From 6ab0c635dc4874ac05142093f3956e15438eae1e Mon Sep 17 00:00:00 2001 From: Ramses Tech User <94632088+ramses-tech-user@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:42:42 +0200 Subject: [PATCH 1/2] Oss release 28.0.0-rc2 created 2023-10-16-11-42 see CHANGELOG.md for details Original commit sha: d77bba2a656f89a78a2fa0c1a88fcea91cb0b19b Co-authored-by: Askanaz Torosyan <46795157+nVxx@users.noreply.github.com> Co-authored-by: Daniel Haas <25718295+bojackHaasman@users.noreply.github.com> Co-authored-by: Mirko Sova <64351017+smirko-dev@users.noreply.github.com> Co-authored-by: Violin Yanev Co-authored-by: Carsten Rohn <710234+delyas@users.noreply.github.com> Co-authored-by: Tobias Hammer Co-authored-by: Bernhard Kisslinger <65217745+bkisslinger@users.noreply.github.com> Co-authored-by: Martin Veith <3490591+veithm@users.noreply.github.com> Co-authored-by: Jonathan Conrad <833168+jcsneaker@users.noreply.github.com> Co-authored-by: Mohamed Sharaf-El-Deen <769940+mohhsharaf@users.noreply.github.com> Co-authored-by: Markus Keppler <92277233+markuskeppler@users.noreply.github.com> Co-authored-by: Chan Tong Yan <4199832+imyumichan@users.noreply.github.com> --- .clang-format | 2 +- .clang-tidy | 312 +- .gitmodules | 48 +- .readthedocs.yml | 16 +- .yamllint | 1 + CHANGELOG.md | 263 +- CMakeLists.txt | 98 +- LICENSE.txt | 1406 +++++ README.md | 16 +- benchmarks/CMakeLists.txt | 30 - client/CMakeLists.txt | 215 - client/logic/.clang-tidy | 506 -- client/logic/cmake/platformConfig.cmake | 209 - client/logic/include/ramses-logic/ErrorData.h | 52 - client/logic/include/ramses-logic/Logger.h | 73 - .../logic/include/ramses-logic/LogicObject.h | 148 - .../include/ramses-logic/RamsesLogicVersion.h | 38 - .../logic/include/ramses-logic/WarningData.h | 78 - .../flatbuffers/generated/LogicEngineGen.h | 433 -- .../lib/flatbuffers/schemas/LogicEngine.fbs | 46 - client/logic/lib/impl/Logger.cpp | 33 - client/logic/lib/impl/LoggerImpl.cpp | 38 - client/logic/lib/impl/LoggerImpl.h | 184 - client/logic/lib/impl/LogicEngine.cpp | 368 -- client/logic/lib/impl/LogicEngineImpl.cpp | 857 --- client/logic/lib/impl/LogicEngineImpl.h | 187 - client/logic/lib/impl/LogicObject.cpp | 107 - client/logic/lib/impl/RamsesLogicVersion.cpp | 24 - .../impl/RamsesRenderGroupBindingElements.cpp | 46 - .../RamsesRenderGroupBindingElementsImpl.cpp | 42 - client/logic/lib/impl/SaveFileConfigImpl.h | 47 - client/logic/lib/internals/ErrorReporting.cpp | 40 - .../logic/lib/internals/ValidationResults.cpp | 39 - .../ramses-logic-viewer-test/CMakeLists.txt | 68 - client/logic/unittests/api/LoggerTest.cpp | 102 - .../api/LogicEngineTest_Compatibility.cpp | 450 -- .../api/LogicEngineTest_Dirtiness.cpp | 569 -- .../api/LogicEngineTest_ErrorHandling.cpp | 113 - .../unittests/api/LogicEngineTest_Factory.cpp | 683 --- ...icEngineTest_LogicNodeUpdateStatistics.cpp | 143 - .../unittests/api/LogicEngineTest_Lookup.cpp | 550 -- .../api/LogicEngineTest_Serialization.cpp | 1216 ---- .../api/LogicEngineTest_SerializedSize.cpp | 397 -- client/logic/unittests/api/LogicNodeTest.cpp | 138 - .../unittests/api/RamsesCameraBindingTest.cpp | 1573 ----- .../api/RamsesMeshNodeBindingTest.cpp | 408 -- .../RamsesRenderGroupBindingElementsTest.cpp | 81 - .../api/RamsesRenderPassBindingTest.cpp | 590 -- .../unittests/internal/ErrorReportingTest.cpp | 107 - .../logic/unittests/res/testScene_01.ramses | Bin 5852 -> 0 bytes client/logic/unittests/shared/LogTestUtils.h | 76 - .../unittests/shared/LogicEngineTest_Base.h | 167 - client/ramses-client-api/Appearance.cpp | 325 - client/ramses-client-api/ArrayBuffer.cpp | 73 - client/ramses-client-api/AttributeInput.cpp | 49 - client/ramses-client-api/DataObject.cpp | 91 - client/ramses-client-api/Effect.cpp | 74 - .../ramses-client-api/EffectDescription.cpp | 138 - client/ramses-client-api/EffectInput.cpp | 34 - client/ramses-client-api/GeometryBinding.cpp | 72 - client/ramses-client-api/RamsesObject.cpp | 45 - client/ramses-client-api/RenderGroup.cpp | 85 - .../RenderTargetDescription.cpp | 58 - client/ramses-client-api/SceneConfig.cpp | 58 - client/ramses-client-api/UniformInput.cpp | 54 - .../ramses-client-api/AttributeInput.h | 69 - .../include/ramses-client-api/EffectInput.h | 63 - .../include/ramses-client-api/RamsesObject.h | 74 - .../include/ramses-client-api/SceneConfig.h | 83 - .../include/ramses-client-api/UniformInput.h | 76 - .../ramses-client-api/include/ramses-client.h | 62 - client/ramses-client/impl/AppearanceImpl.cpp | 740 --- client/ramses-client/impl/AppearanceImpl.h | 133 - client/ramses-client/impl/ArrayBufferImpl.cpp | 173 - client/ramses-client/impl/ArrayBufferImpl.h | 48 - client/ramses-client/impl/BlitPassImpl.h | 56 - client/ramses-client/impl/CameraNodeImpl.cpp | 406 -- client/ramses-client/impl/CameraNodeImpl.h | 104 - .../impl/ClientCommands/DumpSceneToFile.cpp | 35 - .../impl/ClientCommands/DumpSceneToFile.h | 35 - .../impl/ClientCommands/FlushSceneVersion.h | 33 - .../ClientCommands/SceneCommandVisitor.cpp | 90 - client/ramses-client/impl/DataObjectImpl.cpp | 128 - .../impl/EffectDescriptionImpl.cpp | 187 - .../impl/EffectDescriptionImpl.h | 69 - client/ramses-client/impl/EffectImpl.cpp | 269 - client/ramses-client/impl/EffectImpl.h | 70 - client/ramses-client/impl/EffectInputImpl.cpp | 149 - client/ramses-client/impl/EffectInputImpl.h | 72 - .../impl/GeometryBindingImpl.cpp | 398 -- .../ramses-client/impl/GeometryBindingImpl.h | 79 - client/ramses-client/impl/MeshNodeImpl.h | 76 - client/ramses-client/impl/NodeImpl.h | 108 - .../ramses-client/impl/PickableObjectImpl.cpp | 166 - .../ramses-client/impl/PickableObjectImpl.h | 58 - .../ramses-client/impl/RamsesClientImpl.cpp | 836 --- client/ramses-client/impl/RamsesClientImpl.h | 234 - .../impl/RamsesClientTypesImpl.h | 47 - .../ramses-client/impl/RamsesObjectHandle.h | 21 - .../ramses-client/impl/RamsesObjectImpl.cpp | 127 - client/ramses-client/impl/RamsesObjectImpl.h | 74 - .../impl/RamsesObjectRegistry.cpp | 202 - .../ramses-client/impl/RamsesObjectRegistry.h | 100 - .../impl/RamsesObjectTypeUtils.cpp | 81 - .../impl/RamsesObjectTypeUtils.h | 42 - client/ramses-client/impl/RenderBufferImpl.h | 51 - client/ramses-client/impl/RenderGroupImpl.cpp | 316 - client/ramses-client/impl/RenderGroupImpl.h | 69 - client/ramses-client/impl/RenderPassImpl.cpp | 384 -- client/ramses-client/impl/RenderPassImpl.h | 93 - .../impl/RenderTargetDescriptionImpl.cpp | 100 - client/ramses-client/impl/ResourceImpl.h | 59 - client/ramses-client/impl/RotationTypeUtils.h | 66 - client/ramses-client/impl/SceneConfigImpl.h | 30 - client/ramses-client/impl/SceneObjectImpl.cpp | 85 - client/ramses-client/impl/SceneObjectImpl.h | 50 - .../ramses-client/impl/SceneReferenceImpl.cpp | 123 - .../ramses-client/impl/SceneReferenceImpl.h | 57 - client/ramses-client/impl/SceneUtils.h | 37 - .../ramses-client/impl/Texture2DBufferImpl.h | 48 - .../ramses-client/impl/TextureSamplerImpl.cpp | 334 -- .../ramses-client/impl/TextureSamplerImpl.h | 85 - client/ramses-client/impl/TextureUtils.h | 746 --- .../impl/VisibilityModeUtils.cpp | 56 - .../ramses-client/impl/VisibilityModeUtils.h | 26 - .../impl/glslEffectBlock/GlslEffect.cpp | 298 - client/test/AppearanceTest.cpp | 1315 ----- client/test/ClientApplicationLogicTest.cpp | 274 - client/test/CreationHelper.h | 110 - client/test/EffectInputTest.cpp | 369 -- client/test/GeometryBindingTest.cpp | 1063 ---- client/test/GlslEffectTest.cpp | 1050 ---- client/test/MockActionCollector.h | 90 - client/test/RamsesObjectOwnershipTest.cpp | 273 - client/test/RamsesObjectTest.cpp | 171 - client/test/RenderBufferTest.cpp | 156 - client/test/RenderGroupTest.cpp | 523 -- client/test/RenderTargetDescriptionTest.cpp | 206 - client/test/SceneFactoryTest.cpp | 50 - client/test/ScenePersistationTest.cpp | 1615 ----- .../test/text/Freetype2FontInstanceTest.cpp | 205 - client/test/text/HarfbuzzFontInstanceTest.cpp | 219 - cmake/modules/FindBoost.cmake | 155 - cmake/modules/FindLuaJIT.cmake | 40 + cmake/modules/FindQuartzCore.cmake | 26 + cmake/ramses/addSubdirectory.cmake | 29 - cmake/ramses/createTarget.cmake | 61 +- .../ramses}/flatbuffersGeneration.cmake | 4 +- cmake/ramses/folderize.cmake | 31 +- cmake/ramses/makeTestFromTarget.cmake | 9 +- cmake/ramses/platformConfig.cmake | 6 +- ...amses-shared-lib-headlessTemplate.cmake.in | 6 +- .../ramses-shared-libTemplate.cmake.in | 12 +- demo/CMakeLists.txt | 6 +- .../DemoJNIInterface/include/RendererBundle.h | 7 +- .../include/SceneViewerBundle.h | 5 +- .../include/UniformInputWrapper.h | 9 +- .../DemoJNIInterface/src/JNIInterface.cpp | 2 +- .../DemoJNIInterface/src/RendererBundle.cpp | 8 +- .../src/SceneViewerBundle.cpp | 21 +- .../src/UniformInputWrapper.cpp | 11 +- demo/iOS/CMakeLists.txt | 67 + demo/iOS/Info.plist | 53 + demo/iOS/README.md | 30 + .../iOS/include/RendererBundle.h | 25 +- .../ramses-renderer-ios-app-Bridging-Header.h | 6 +- demo/iOS/res/LaunchScreen.storyboard | 25 + demo/iOS/res/Main.storyboard | 25 + demo/iOS/src/AppDelegate.swift | 24 + demo/iOS/src/RendererBundle.mm | 107 + demo/iOS/src/SceneDelegate.swift | 33 + demo/iOS/src/ViewController.swift | 55 + doc/CMakeLists.txt | 6 +- doc/old_ramses/developer/00_MainPage.dox | 26 - .../developer/10_GettingStarted.dox | 169 - doc/old_ramses/developer/20_Contributing.dox | 54 - .../developer/30_CodeStyleGuide.dox | 664 --- doc/old_ramses/developer/40_ClientAPI.dox | 62 - doc/old_ramses/developer/50_Testing.dox | 163 - .../developer/images/guide-which-tests.png | Bin 60278 -> 0 bytes doc/old_ramses/developer/images/initiator.dot | 52 - .../images/ramses_logo_with_alpha2.png | Bin 182174 -> 0 bytes doc/old_ramses/developer/images/responder.dot | 61 - .../developer/images/responder_with_old.dot | 78 - doc/old_ramses/general/40_Examples.dox | 195 - .../general/50_ContentExpiration.dox | 75 - .../general/70_SceneReferencing.dox | 121 - doc/old_ramses/tools/00_MainPage.dox | 43 - .../dlt-logging/10_GeneralIntroduction.dox | 60 - .../tools/dlt-logging/20_UsingDLT.dox | 85 - .../dlt-logging/images/dltviewer_connect.png | Bin 43415 -> 0 bytes .../images/dltviewer_connect_tcp.png | Bin 30396 -> 0 bytes .../images/dltviewer_context_inject.png | Bin 54513 -> 0 bytes .../dlt-logging/images/dltviewer_contexts.png | Bin 73298 -> 0 bytes .../images/dltviewer_injection.png | Bin 23647 -> 0 bytes .../dlt-logging/images/dltviewer_messages.png | Bin 115852 -> 0 bytes .../dlt-logging/images/dltviewer_plugin.png | Bin 21184 -> 0 bytes .../tools/profiling/10_Profiling.dox | 174 - .../tools/scene-viewer/10_SceneViewer.dox | 141 - doc/sphinx/build.rst | 51 +- doc/sphinx/classes/generate_classes.py | 25 +- doc/sphinx/conf.py | 3 - doc/sphinx/core.rst | 35 +- doc/sphinx/examples/core/00_minimal.rst | 2 +- doc/sphinx/examples/logic/00_minimal.rst | 2 +- doc/sphinx/logic.rst | 236 +- doc/sphinx/requirements.txt | 37 +- doc/sphinx/viewer.rst | 22 +- examples/CMakeLists.txt | 30 +- examples/logic/00_minimal/main.cpp | 15 +- .../logic/01a_primitive_properties/main.cpp | 12 +- examples/logic/01b_struct_properties/main.cpp | 12 +- examples/logic/01c_array_properties/main.cpp | 12 +- .../logic/02_errors_compile_time/main.cpp | 30 +- examples/logic/03_errors_runtime/main.cpp | 24 +- examples/logic/04_ramses_scene/main.cpp | 34 +- examples/logic/05_serialization/main.cpp | 65 +- examples/logic/07_links/main.cpp | 12 +- examples/logic/08a_static_animation/main.cpp | 52 +- examples/logic/08b_dynamic_animation/main.cpp | 48 +- examples/logic/09_modules/main.cpp | 14 +- examples/logic/10_globals/main.cpp | 12 +- examples/logic/11_interfaces/main.cpp | 14 +- examples/logic/12_anchor_point/main.cpp | 66 +- examples/logic/13_render_order/main.cpp | 44 +- examples/logic/14_skinbinding/main.cpp | 72 +- examples/logic/15_meshnodebinding/main.cpp | 42 +- examples/logic/CMakeLists.txt | 20 +- examples/logic/shared/SimpleRenderer.h | 12 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 35 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 33 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 83 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 27 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 35 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 35 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 56 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 67 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 34 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 64 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 34 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 66 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 28 +- .../ramses-example-local-client/src/main.cpp | 32 +- .../src/main.cpp | 58 +- .../src/main.cpp | 125 +- .../src/main.cpp | 59 +- .../CMakeLists.txt | 23 +- .../src/main.cpp | 128 +- .../src/main.cpp | 38 +- .../src/main.cpp | 111 +- .../src/main.cpp | 62 +- .../src/main.cpp | 60 +- .../src/main.cpp | 65 +- .../ramses-example-minimal/CMakeLists.txt | 2 +- examples/ramses-example-minimal/src/main.cpp | 9 +- .../ramses-example-renderonce/CMakeLists.txt | 2 +- .../ramses-example-renderonce/src/main.cpp | 60 +- .../ramses-example-text-basic/CMakeLists.txt | 2 +- .../ramses-example-text-basic/src/main.cpp | 15 +- .../CMakeLists.txt | 2 +- .../src/main.cpp | 15 +- external/CMakeLists.txt | 201 +- external/cityhash/src/city.cc | 6 - external/fmt | 2 +- framework/CMakeLists.txt | 270 - .../SceneUpdateSerializationHelper.h | 46 - .../include/Common/StronglyTypedValue.h | 101 - .../Common/test/StronglyTypedValueTest.cpp | 181 - .../Math3d/test/CameraMatrixHelperTest.cpp | 87 - .../Core/Utils/include/Utils/LoggingUtils.h | 109 - .../Core/Utils/include/Utils/MessagePool.h | 102 - framework/Core/Utils/src/UserLogAppender.cpp | 50 - framework/Core/Utils/test/ImageTest.cpp | 374 -- .../include/DltLogAppender/DltAdapter.h | 20 - .../DltLogAppender/test/DltAdapterTest.cpp | 132 - .../include/ScopedLogContextLevel.h | 37 - .../include/framework_common_gmock_header.h | 44 - .../FrameworkTestUtils/src/TestPngHeader.cpp | 42 - .../src/framework_common_gmock_header.cpp | 63 - .../PlatformAbstraction/PlatformTypes.h | 22 - .../PlatformAbstraction/test/HashSetTest.cpp | 395 -- .../test/NumericLimitsTest.cpp | 52 - .../test/PlatformStringUtilsTest.cpp | 34 - .../Resource/EResourceCompressionStatus.h | 21 - .../Resource/include/Resource/ResourceTypes.h | 52 - .../Resource/test/ArrayResourceTest.cpp | 44 - .../include/Scene/EScenePublicationMode.h | 46 - .../test/SceneTest_IteratableMemoryPools.cpp | 216 - .../SceneAPI/include/SceneAPI/EDataSlotType.h | 43 - .../SceneAPI/include/SceneAPI/IScene.h | 317 - .../SceneAPI/include/SceneAPI/TextureEnums.h | 351 -- .../test/TextureSamplerStatesHashTest.cpp | 82 - .../Watchdog/test/PlatformWatchDogMock.h | 26 - .../ramses-framework-api/StatusObject.h | 104 - .../ramses-framework/include/DataTypeUtils.h | 173 - .../include/RamsesFrameworkConfigImpl.h | 87 - .../include/RamsesFrameworkImpl.h | 106 - .../include/StatusObjectImpl.h | 70 - .../include/ThreadWatchdogConfig.h | 71 - .../ramses-framework/src/RamsesFramework.cpp | 99 - .../src/RamsesFrameworkConfig.cpp | 160 - .../src/RamsesFrameworkTypesImpl.cpp | 37 - .../ramses-framework/src/StatusObject.cpp | 39 - .../ramses-framework/src/StatusObjectImpl.cpp | 134 - .../ramses-framework/test/DataTypeTest.cpp | 281 - .../test/RamsesFrameworkConfigTest.cpp | 213 - .../test/StatusObjectTest.cpp | 197 - include/CMakeLists.txt | 76 + .../ramses/client}/Appearance.h | 269 +- .../ramses/client}/ArrayBuffer.h | 66 +- .../ramses/client}/ArrayResource.h | 46 +- include/ramses/client/AttributeInput.h | 47 + .../ramses/client}/BlitPass.h | 59 +- .../ramses/client}/Camera.h | 105 +- .../ramses/client}/ClientObject.h | 32 +- .../ramses/client}/DataObject.h | 62 +- .../ramses/client}/Effect.h | 88 +- .../ramses/client}/EffectDescription.h | 97 +- include/ramses/client/EffectInput.h | 98 + .../ramses/client}/EffectInputSemantic.h | 5 +- .../ramses/client/Geometry.h | 72 +- .../ramses/client}/IClientEventHandler.h | 11 +- .../ramses/client}/MeshNode.h | 103 +- .../ramses/client}/MipLevelData.h | 36 +- .../ramses/client}/Node.h | 154 +- .../ramses/client}/OrthographicCamera.h | 13 +- .../ramses/client}/PerspectiveCamera.h | 22 +- .../ramses/client}/PickableObject.h | 57 +- .../ramses/client}/RamsesClient.h | 134 +- .../ramses/client}/RenderBuffer.h | 53 +- .../ramses/client}/RenderGroup.h | 78 +- .../ramses/client}/RenderGroupMeshIterator.h | 27 +- .../ramses/client}/RenderPass.h | 120 +- .../ramses/client}/RenderPassGroupIterator.h | 27 +- .../ramses/client}/RenderTarget.h | 38 +- .../ramses/client}/RenderTargetDescription.h | 54 +- .../ramses/client}/Resource.h | 34 +- .../ramses/client}/SaveFileConfig.h | 51 +- .../ramses/client}/Scene.h | 334 +- include/ramses/client/SceneConfig.h | 119 + .../ramses/client}/SceneGraphIterator.h | 25 +- .../ramses/client}/SceneIterator.h | 23 +- .../ramses/client}/SceneObject.h | 60 +- .../ramses/client}/SceneObjectIterator.h | 25 +- .../ramses/client}/SceneReference.h | 55 +- .../ramses/client}/Texture2D.h | 46 +- .../ramses/client}/Texture2DBuffer.h | 57 +- .../ramses/client}/Texture3D.h | 46 +- .../ramses/client}/TextureCube.h | 46 +- .../ramses/client}/TextureSampler.h | 63 +- .../ramses/client}/TextureSamplerExternal.h | 34 +- .../ramses/client}/TextureSamplerMS.h | 34 +- .../ramses/client}/TextureSwizzle.h | 7 +- include/ramses/client/UniformInput.h | 53 + .../ramses/client/logic}/AnchorPoint.h | 26 +- .../ramses/client/logic}/AnimationNode.h | 26 +- .../client/logic}/AnimationNodeConfig.h | 37 +- .../ramses/client/logic}/AnimationTypes.h | 2 +- .../ramses/client/logic/AppearanceBinding.h | 40 +- .../ramses/client/logic/CameraBinding.h | 48 +- .../ramses/client/logic}/Collection.h | 4 +- .../ramses/client/logic}/DataArray.h | 28 +- .../ramses/client/logic}/ELuaSavingMode.h | 0 .../ramses/client/logic}/EPropertyType.h | 4 +- .../ramses/client/logic}/EStandardModule.h | 0 .../ramses/client/logic}/Iterator.h | 2 +- .../ramses/client/logic}/LogicEngine.h | 565 +- .../ramses/client/logic}/LogicEngineReport.h | 24 +- .../ramses/client/logic}/LogicNode.h | 31 +- include/ramses/client/logic/LogicObject.h | 52 + .../ramses/client/logic}/LuaConfig.h | 37 +- .../ramses/client/logic}/LuaInterface.h | 23 +- .../ramses/client/logic}/LuaModule.h | 20 +- .../ramses/client/logic}/LuaScript.h | 23 +- .../ramses/client/logic/MeshNodeBinding.h | 42 +- .../ramses/client/logic/NodeBinding.h | 46 +- .../ramses/client/logic}/Property.h | 90 +- .../ramses/client/logic}/PropertyLink.h | 11 + .../ramses/client/logic}/RamsesBinding.h | 4 +- .../ramses/client/logic/RenderGroupBinding.h | 54 +- .../client/logic/RenderGroupBindingElements.h | 55 +- .../ramses/client/logic/RenderPassBinding.h | 46 +- .../ramses/client/logic}/SkinBinding.h | 40 +- .../ramses/client/logic}/TimerNode.h | 20 +- include/ramses/client/ramses-client.h | 59 + .../ramses/client}/ramses-utils.h | 72 +- .../ramses/client/text}/FontCascade.h | 9 +- .../ramses/client/text}/FontInstanceId.h | 7 +- .../ramses/client/text}/FontInstanceOffsets.h | 7 +- .../ramses/client/text}/FontRegistry.h | 16 +- .../ramses/client/text}/Glyph.h | 9 +- .../ramses/client/text}/GlyphMetrics.h | 17 +- .../ramses/client/text}/IFontAccessor.h | 7 +- .../ramses/client/text}/IFontInstance.h | 9 +- .../ramses/client/text}/LayoutUtils.h | 17 +- .../ramses/client/text}/TextCache.h | 21 +- .../ramses/client/text}/TextLine.h | 7 +- .../ramses/client/text}/UtfUtils.h | 7 +- .../ramses/client/text}/ramses-text.h | 27 +- .../ramses/framework}/APIExport.h | 5 +- .../ramses/framework}/AppearanceEnums.h | 7 +- .../ramses/framework}/DataTypes.h | 17 +- .../ramses/framework}/EDataType.h | 11 +- .../ramses/framework}/EFeatureLevel.h | 10 +- .../ramses/framework}/ERotationType.h | 4 +- .../ramses/framework}/EScenePublicationMode.h | 5 +- .../ramses/framework}/EVisibilityMode.h | 7 +- include/ramses/framework/Flags.h | 131 + .../ramses/framework}/IRamshCommand.h | 7 +- .../framework}/IThreadWatchdogNotification.h | 7 +- include/ramses/framework/Issue.h | 55 + .../ramses/framework}/RamsesFramework.h | 98 +- .../ramses/framework}/RamsesFrameworkConfig.h | 120 +- .../ramses/framework}/RamsesFrameworkTypes.h | 79 +- include/ramses/framework/RamsesObject.h | 206 + .../ramses/framework}/RamsesObjectTypes.h | 20 +- .../ramses/framework}/RamsesVersion.h | 7 +- .../ramses/framework}/RendererSceneState.h | 5 +- .../ramses/framework}/StronglyTypedValue.h | 16 +- .../ramses/framework}/TextureEnums.h | 29 +- include/ramses/framework/ValidationReport.h | 101 + .../ramses/renderer}/BinaryShaderCache.h | 29 +- .../ramses/renderer}/DisplayConfig.h | 249 +- .../ramses/renderer}/IBinaryShaderCache.h | 13 +- .../ramses/renderer}/IRendererEventHandler.h | 11 +- .../IRendererSceneControlEventHandler.h | 11 +- .../ramses/renderer}/RamsesRenderer.h | 240 +- .../ramses/renderer}/RendererConfig.h | 81 +- .../ramses/renderer}/RendererSceneControl.h | 143 +- .../ramses/renderer}/Types.h | 27 +- .../DmaOffscreenBufferRenderingTests.h | 44 - .../DmaOffscreenBufferTests.cpp | 555 -- .../DmaOffscreenBufferTests.h | 106 - .../ExternalWindowTests.cpp | 73 - .../RendererTestsFramework.cpp | 623 -- .../RendererTestsFramework.h | 158 - .../RendererTestFramework/RenderingTestCase.h | 39 - .../RendererTestFramework/TestRenderer.h | 104 - .../RendererTestFramework/TestScenes.cpp | 64 - .../RendererTestFramework/TestScenes.h | 105 - .../RenderingTests/DataLinkingTests.cpp | 401 -- .../RenderingTests/DataLinkingTests.h | 71 - .../RenderingTests/DisplayRenderingTests.cpp | 392 -- .../RenderingTests/DisplayRenderingTests.h | 61 - .../RenderingTests/EffectRenderingTests.cpp | 64 - .../RenderingTests/EffectRenderingTests.h | 38 - .../InterruptibleOffscreenBufferLinkTests.cpp | 296 - .../InterruptibleOffscreenBufferLinkTests.h | 46 - .../OffscreenBufferLinkTests.cpp | 318 - .../RenderingTests/OffscreenBufferLinkTests.h | 63 - .../RenderPassRenderingTests.cpp | 111 - .../RenderingTests/RenderPassRenderingTests.h | 35 - .../RenderTargetRenderingTests.cpp | 123 - .../RenderTargetRenderingTests.h | 61 - .../RenderingTests/RenderingTests.h | 75 - .../RenderingTests/SceneRenderingTests.cpp | 414 -- .../RenderingTests/SceneRenderingTests.h | 158 - .../RenderingTests/TextureRenderingTests.cpp | 297 - .../RenderingTests/TextureRenderingTests.h | 94 - .../src/StressTestRenderer.h | 52 - .../TestContent/src/RenderBufferScene.cpp | 119 - .../TestContent/src/StreamTextureScene.cpp | 162 - .../TestContent/src/TriangleAppearance.cpp | 80 - ramses-cli/test/ramses-cli-test.cpp | 348 -- ramses-shared-lib/CMakeLists.txt | 35 - renderer/CMakeLists.txt | 58 - renderer/Platform/Context_WGL/CMakeLists.txt | 21 - .../Device_EGL_Extension/CMakeLists.txt | 25 - .../EmbeddedCompositor_Wayland/CMakeLists.txt | 45 - .../src/WaylandBuffer.cpp | 82 - renderer/Platform/Platform_EGL/CMakeLists.txt | 27 - .../Platform_Wayland_EGL/CMakeLists.txt | 21 - .../Platform_Wayland_IVI_EGL/CMakeLists.txt | 19 - .../Platform_Windows_WGL/CMakeLists.txt | 22 - .../Platform_Windows_WGL.h | 34 - .../Platform/Platform_X11_EGL/CMakeLists.txt | 35 - .../CMakeLists.txt | 38 - .../WaylandEGLExtensionProcs/CMakeLists.txt | 28 - .../Platform/WaylandUtilities/CMakeLists.txt | 33 - .../Platform/Window_Wayland/CMakeLists.txt | 22 - .../Window_Wayland_IVI/CMakeLists.txt | 38 - .../Window_Wayland_Shell/CMakeLists.txt | 33 - .../Window_Wayland_Test/CMakeLists.txt | 19 - .../Platform/Window_Windows/CMakeLists.txt | 35 - renderer/PlatformFactory/CMakeLists.txt | 37 - renderer/RendererLib/CMakeLists.txt | 87 - .../include/RendererAPI/ELoopMode.h | 21 - .../include/RendererAPI/EWindowType.h | 46 - .../RendererAPI/IRendererResourceCache.h | 32 - .../RendererLib/RendererAPI/src/Types.cpp | 29 - .../RendererCommands/src/SetClearColor.cpp | 32 - .../RendererCommands/test/ScreenshotTest.cpp | 73 - .../include/RendererLib/EKeyModifier.h | 65 - .../include/RendererLib/EMouseEventType.h | 57 - .../RendererResourceManagerUtils.h | 29 - .../include/RendererLib/SceneDisplayTracker.h | 88 - .../src/RendererResourceManagerUtils.cpp | 52 - .../RendererLib/test/BufferLinksTest.cpp | 167 - .../test/DataReferenceLinkCachedSceneTest.cpp | 79 - .../test/DataReferenceLinkManagerTest.cpp | 466 -- .../RendererLib/test/DisplayConfigTest.cpp | 141 - .../test/DisplayEventHandlerTest.cpp | 148 - .../RendererLib/test/DisplaySetupTest.cpp | 545 -- .../test/EmbeddedCompositingManagerTest.cpp | 274 - .../test/IntersectionUtilsTest.cpp | 580 -- .../RendererLib/test/LinkManagerBaseTest.cpp | 180 - .../test/PendingSceneResourcesUtilsTest.cpp | 312 - .../RendererLib/test/RenderExecutorTest.cpp | 1773 ------ .../test/RendererCommandBufferTest.cpp | 325 - .../test/RendererCommandExecutorTest.cpp | 498 -- .../RendererLib/test/RendererConfigTest.cpp | 43 - .../test/RendererLogContextTest.cpp | 87 - .../test/RendererResourceManagerTest.cpp | 1448 ----- .../test/RendererResourceRegistryTest.cpp | 434 -- .../RendererSceneResourceRegistryTest.cpp | 174 - .../test/RendererSceneUpdaterTest.cpp | 5219 ---------------- .../test/RendererSceneUpdaterTest.h | 1031 ---- .../RendererSceneUpdaterTest_Resources.cpp | 2450 -------- .../RendererLib/test/RendererScenesTest.cpp | 103 - .../test/RendererStatisticsTest.cpp | 422 -- .../RendererLib/test/RendererTest.cpp | 2664 --------- .../RendererLib/test/ResourceUploaderTest.cpp | 584 -- .../test/ResourceUploadingManagerTest.cpp | 890 --- .../test/SceneDependencyCheckerTest.cpp | 407 -- .../test/SceneExpirationMonitorTest.cpp | 943 --- .../test/SceneLinksManagerTest.cpp | 354 -- .../RendererLib/test/SceneLinksTestUtils.h | 80 - .../test/SceneResourceUploaderTest.cpp | 104 - .../test/TextureLinkManagerTest.cpp | 1141 ---- .../test/TransformationLinkManagerTest.cpp | 364 -- .../DisplayControllerMock.cpp | 28 - .../DisplayControllerMock.h | 48 - .../RendererTestCommon/MockResourceHash.h | 53 - .../RendererTestCommon/RendererMock.cpp | 91 - .../RendererResourceCacheFake.h | 60 - .../RendererResourceCacheMock.h | 29 - .../RendererResourceManagerMock.cpp | 149 - .../RendererResourceManagerMock.h | 104 - .../ResourceDeviceHandleAccessorMock.h | 41 - .../renderer_common_gmock_header.cpp | 35 - .../renderer_common_gmock_header.h | 27 - .../include/ReadPixelCallbackHandler.h | 33 - .../include/RendererTestUtils.h | 90 - .../src/RendererTestUtils.cpp | 223 - renderer/ramses-renderer-api/CMakeLists.txt | 23 - .../DefaultRendererResourceCache.h | 122 - .../IRendererResourceCache.h | 82 - renderer/ramses-renderer-impl/CMakeLists.txt | 47 - .../include/BinaryShaderCacheImpl.h | 71 - .../include/BinaryShaderCacheProxy.h | 44 - .../DefaultRendererResourceCacheImpl.h | 71 - .../include/DisplayConfigImpl.h | 95 - .../include/RamsesRendererImpl.h | 172 - .../include/RamsesRendererUtils.h | 29 - .../include/RendererConfigImpl.h | 54 - .../include/RendererResourceCacheProxy.h | 41 - .../include/RendererSceneControlImpl.h | 85 - .../src/BinaryShaderCache.cpp | 81 - .../src/DefaultRendererResourceCache.cpp | 52 - .../src/DefaultRendererResourceCacheImpl.cpp | 349 -- .../src/DisplayConfig.cpp | 276 - .../src/DisplayConfigImpl.cpp | 388 -- .../src/RamsesRenderer.cpp | 260 - .../src/RamsesRendererImpl.cpp | 664 --- .../src/RamsesRendererUtils.cpp | 293 - .../src/RendererConfig.cpp | 104 - .../src/RendererResourceCacheProxy.cpp | 63 - .../src/RendererSceneControl.cpp | 96 - .../src/RendererSceneControlImpl.cpp | 253 - .../test/BinaryShaderCacheTest.cpp | 291 - .../test/DefaultRendererResourceCacheTest.cpp | 315 - .../test/DisplayConfigTest.cpp | 308 - .../test/RamsesRendererTest.cpp | 937 --- .../test/RendererConfigTest.cpp | 109 - .../test/RendererEventTestHandler.h | 360 -- .../test/RendererMateTest.cpp | 195 - scripts/ci/build/build.py | 20 +- scripts/ci/build/test-cmake-configurations.py | 190 +- scripts/ci/clang-tidy-wrapper.py | 9 +- scripts/ci/collect-coverage.py | 12 +- scripts/ci/common/clangtidy.py | 4 +- scripts/ci/config/clang-tidy-wrapper.yaml | 34 +- .../ci/config/sanitizer/lsan_suppressions.txt | 17 +- .../ci/config/sanitizer/tsan_blacklist.txt | 3 + .../ci/config/sanitizer/ubsan_blacklist.txt | 2 + scripts/ci/config/valgrind/suppressions | 34 +- .../installation-check/check-installation.py | 36 +- .../check-shared-lib-symbols.py | 75 + .../shared-lib-check/CMakeLists.txt | 24 +- .../ramses-shared-lib-check.cpp | 27 +- .../shared-lib-headless-check/CMakeLists.txt | 22 +- .../ramses-shared-lib-check.cpp | 10 +- .../static-lib-check/CMakeLists.txt | 2 +- .../ramses-static-lib-check.cpp | 12 +- scripts/ci/test/test_clangtidy.py | 6 +- scripts/ci/test/test_compilationdb.py | 14 +- .../code_style_checker/check_all_styles.py | 9 +- .../check_api_export_symbols.py | 69 - .../code_style_checker/check_deprecated.py | 2 +- scripts/docker/ramses-basic/Dockerfile | 58 +- .../docker/ramses-basic/requirements.txt | 8 +- scripts/docker/runtime-files/build-ramses.sh | 2 +- scripts/migrate_to_28_0_0.py | 96 + {integration => src}/CMakeLists.txt | 16 +- src/client/CMakeLists.txt | 56 + src/client/impl/Appearance.cpp | 341 ++ src/client/impl/AppearanceImpl.cpp | 767 +++ src/client/impl/AppearanceImpl.h | 126 + .../client}/impl/AppearanceUtils.h | 11 +- src/client/impl/ArrayBuffer.cpp | 90 + src/client/impl/ArrayBufferImpl.cpp | 173 + src/client/impl/ArrayBufferImpl.h | 45 + .../client/impl}/ArrayResource.cpp | 17 +- .../client}/impl/ArrayResourceImpl.cpp | 32 +- .../client}/impl/ArrayResourceImpl.h | 27 +- .../client/impl/AttributeInput.cpp | 17 +- .../client/impl}/BlitPass.cpp | 30 +- .../client}/impl/BlitPassImpl.cpp | 106 +- src/client/impl/BlitPassImpl.h | 57 + .../client/impl}/Camera.cpp | 54 +- src/client/impl/CameraNodeImpl.cpp | 431 ++ src/client/impl/CameraNodeImpl.h | 104 + .../client}/impl/ClientFactory.cpp | 6 +- .../client}/impl/ClientFactory.h | 11 +- src/client/impl/ClientObject.cpp | 32 + .../client}/impl/ClientObjectImpl.cpp | 19 +- .../client}/impl/ClientObjectImpl.h | 18 +- src/client/impl/DataObject.cpp | 105 + src/client/impl/DataObjectImpl.cpp | 139 + .../client}/impl/DataObjectImpl.h | 36 +- src/client/impl/Effect.cpp | 84 + src/client/impl/EffectDescription.cpp | 137 + src/client/impl/EffectDescriptionImpl.cpp | 185 + src/client/impl/EffectDescriptionImpl.h | 63 + src/client/impl/EffectImpl.cpp | 342 ++ src/client/impl/EffectImpl.h | 75 + src/client/impl/EffectInput.cpp | 56 + src/client/impl/EffectInputImpl.cpp | 76 + src/client/impl/EffectInputImpl.h | 58 + .../client}/impl/EffectInputSemanticUtils.h | 77 +- src/client/impl/Geometry.cpp | 82 + src/client/impl/GeometryImpl.cpp | 406 ++ src/client/impl/GeometryImpl.h | 77 + .../client}/impl/IteratorImpl.h | 11 +- .../client/impl}/MeshNode.cpp | 58 +- .../client}/impl/MeshNodeImpl.cpp | 163 +- src/client/impl/MeshNodeImpl.h | 77 + .../client/impl}/Node.cpp | 82 +- .../client}/impl/NodeImpl.cpp | 212 +- src/client/impl/NodeImpl.h | 109 + .../client}/impl/ObjectIteratorImpl.h | 17 +- .../client/impl}/OrthographicCamera.cpp | 6 +- .../client/impl}/PerspectiveCamera.cpp | 10 +- .../client/impl}/PickableObject.cpp | 34 +- src/client/impl/PickableObjectImpl.cpp | 180 + src/client/impl/PickableObjectImpl.h | 59 + .../client/impl}/RamsesClient.cpp | 87 +- src/client/impl/RamsesClientImpl.cpp | 818 +++ src/client/impl/RamsesClientImpl.h | 228 + .../client}/impl/RamsesClientTypesImpl.cpp | 0 src/client/impl/RamsesClientTypesImpl.h | 66 + .../client}/impl/RamsesObjectVector.h | 17 +- .../client/impl}/RenderBuffer.cpp | 23 +- .../client}/impl/RenderBufferImpl.cpp | 78 +- src/client/impl/RenderBufferImpl.h | 47 + src/client/impl/RenderGroup.cpp | 95 + src/client/impl/RenderGroupImpl.cpp | 320 + src/client/impl/RenderGroupImpl.h | 66 + .../client}/impl/RenderGroupMeshIterator.cpp | 20 +- .../client/impl}/RenderPass.cpp | 77 +- .../client}/impl/RenderPassGroupIterator.cpp | 20 +- src/client/impl/RenderPassImpl.cpp | 397 ++ src/client/impl/RenderPassImpl.h | 90 + .../client/impl}/RenderTarget.cpp | 18 +- src/client/impl/RenderTargetDescription.cpp | 63 + .../impl/RenderTargetDescriptionImpl.cpp | 92 + .../impl/RenderTargetDescriptionImpl.h | 25 +- .../client}/impl/RenderTargetImpl.cpp | 40 +- .../client}/impl/RenderTargetImpl.h | 25 +- .../client/impl}/Resource.cpp | 18 +- .../client}/impl/ResourceImpl.cpp | 60 +- src/client/impl/ResourceImpl.h | 55 + .../client}/impl/ResourceObjects.h | 7 +- .../client}/impl/SaveFileConfig.cpp | 17 +- .../client}/impl/SaveFileConfigImpl.cpp | 41 +- src/client/impl/SaveFileConfigImpl.h | 85 + .../client/impl}/Scene.cpp | 278 +- src/client/impl/SceneConfig.cpp | 74 + src/client/impl/SceneConfigImpl.cpp | 42 + src/client/impl/SceneConfigImpl.h | 32 + .../client}/impl/SceneDumper.cpp | 274 +- .../client}/impl/SceneDumper.h | 96 +- .../client}/impl/SceneFactory.cpp | 10 +- .../client}/impl/SceneFactory.h | 9 +- .../client/impl}/SceneGraphIterator.cpp | 4 +- .../client}/impl/SceneGraphIteratorImpl.cpp | 15 +- .../client}/impl/SceneGraphIteratorImpl.h | 13 +- .../client}/impl/SceneImpl.cpp | 1201 ++-- .../client}/impl/SceneImpl.h | 257 +- .../client}/impl/SceneIterator.cpp | 10 +- .../client}/impl/SceneIteratorImpl.h | 16 +- .../client/impl}/SceneObject.cpp | 25 +- src/client/impl/SceneObjectImpl.cpp | 113 + src/client/impl/SceneObjectImpl.h | 58 + .../client}/impl/SceneObjectIterator.cpp | 8 +- src/client/impl/SceneObjectRegistry.cpp | 167 + src/client/impl/SceneObjectRegistry.h | 111 + .../client/impl/SceneObjectRegistryIterator.h | 30 +- .../client/impl}/SceneReference.cpp | 28 +- src/client/impl/SceneReferenceImpl.cpp | 105 + src/client/impl/SceneReferenceImpl.h | 51 + .../client}/impl/SerializationHelper.h | 56 +- .../client/impl}/Texture2D.cpp | 18 +- .../client/impl}/Texture2DBuffer.cpp | 25 +- .../client}/impl/Texture2DBufferImpl.cpp | 82 +- src/client/impl/Texture2DBufferImpl.h | 45 + .../client}/impl/Texture2DImpl.cpp | 34 +- .../client}/impl/Texture2DImpl.h | 29 +- .../client/impl}/Texture3D.cpp | 18 +- .../client}/impl/Texture3DImpl.cpp | 32 +- .../client}/impl/Texture3DImpl.h | 27 +- .../client/impl}/TextureCube.cpp | 18 +- .../client}/impl/TextureCubeImpl.cpp | 32 +- .../client}/impl/TextureCubeImpl.h | 27 +- .../client/impl}/TextureSampler.cpp | 50 +- .../client/impl}/TextureSamplerExternal.cpp | 18 +- src/client/impl/TextureSamplerImpl.cpp | 328 ++ src/client/impl/TextureSamplerImpl.h | 85 + .../client/impl}/TextureSamplerMS.cpp | 18 +- .../client}/impl/TextureUtils.cpp | 76 +- src/client/impl/TextureUtils.h | 503 ++ src/client/impl/UniformInput.cpp | 28 + .../client/impl/logic}/AnchorPoint.cpp | 26 +- .../client/impl/logic}/AnchorPointImpl.cpp | 60 +- .../client/impl/logic}/AnchorPointImpl.h | 16 +- .../client/impl/logic}/AnimationNode.cpp | 14 +- .../impl/logic}/AnimationNodeConfig.cpp | 14 +- .../impl/logic}/AnimationNodeConfigImpl.cpp | 40 +- .../impl/logic}/AnimationNodeConfigImpl.h | 2 +- .../client/impl/logic}/AnimationNodeImpl.cpp | 60 +- .../client/impl/logic}/AnimationNodeImpl.h | 8 +- .../client/impl/logic/AppearanceBinding.cpp | 20 +- .../impl/logic/AppearanceBindingImpl.cpp | 147 +- .../client/impl/logic/AppearanceBindingImpl.h | 26 +- .../client/impl/logic/CameraBinding.cpp | 20 +- .../client/impl/logic/CameraBindingImpl.cpp | 164 +- .../client/impl/logic/CameraBindingImpl.h | 24 +- .../client/impl/logic}/Collection.cpp | 4 +- .../client/impl/logic}/DataArray.cpp | 16 +- .../client/impl/logic}/DataArrayImpl.cpp | 65 +- .../client/impl/logic}/DataArrayImpl.h | 10 +- .../client/impl/logic}/Iterator.cpp | 4 +- src/client/impl/logic/LogicEngine.cpp | 348 ++ src/client/impl/logic/LogicEngineImpl.cpp | 706 +++ src/client/impl/logic/LogicEngineImpl.h | 160 + .../client/impl/logic}/LogicEngineReport.cpp | 4 +- .../impl/logic}/LogicEngineReportImpl.cpp | 4 +- .../impl/logic}/LogicEngineReportImpl.h | 4 +- .../client/impl/logic}/LogicNode.cpp | 21 +- .../client/impl/logic}/LogicNodeImpl.cpp | 14 +- .../client/impl/logic}/LogicNodeImpl.h | 8 +- src/client/impl/logic/LogicObject.cpp | 44 + .../client/impl/logic}/LogicObjectImpl.cpp | 72 +- .../client/impl/logic}/LogicObjectImpl.h | 28 +- .../client/impl/logic}/LuaConfig.cpp | 14 +- .../client/impl/logic}/LuaConfigImpl.cpp | 16 +- .../client/impl/logic}/LuaConfigImpl.h | 2 +- .../client/impl/logic}/LuaInterface.cpp | 14 +- .../client/impl/logic}/LuaInterfaceImpl.cpp | 27 +- .../client/impl/logic}/LuaInterfaceImpl.h | 16 +- .../client/impl/logic}/LuaModule.cpp | 16 +- .../client/impl/logic}/LuaModuleImpl.cpp | 45 +- .../client/impl/logic}/LuaModuleImpl.h | 10 +- .../client/impl/logic}/LuaScript.cpp | 14 +- .../client/impl/logic}/LuaScriptImpl.cpp | 62 +- .../client/impl/logic}/LuaScriptImpl.h | 16 +- .../client/impl/logic/MeshNodeBinding.cpp | 22 +- .../client/impl/logic/MeshNodeBindingImpl.cpp | 114 +- .../client/impl/logic/MeshNodeBindingImpl.h | 22 +- .../client/impl/logic/NodeBinding.cpp | 22 +- .../client/impl/logic/NodeBindingImpl.cpp | 133 +- .../client/impl/logic/NodeBindingImpl.h | 30 +- .../client/impl/logic}/Property.cpp | 47 +- .../client/impl/logic}/PropertyImpl.cpp | 66 +- .../client/impl/logic}/PropertyImpl.h | 10 +- .../client/impl/logic}/RamsesBinding.cpp | 4 +- .../client/impl/logic}/RamsesBindingImpl.cpp | 12 +- .../client/impl/logic}/RamsesBindingImpl.h | 4 +- .../client/impl/logic/RenderGroupBinding.cpp | 22 +- .../impl/logic/RenderGroupBindingElements.cpp | 56 + .../logic/RenderGroupBindingElementsImpl.cpp | 50 + .../logic/RenderGroupBindingElementsImpl.h | 2 +- .../impl/logic/RenderGroupBindingImpl.cpp | 95 +- .../impl/logic/RenderGroupBindingImpl.h | 26 +- .../client/impl/logic/RenderPassBinding.cpp | 22 +- .../impl/logic/RenderPassBindingImpl.cpp | 96 +- .../client/impl/logic/RenderPassBindingImpl.h | 22 +- .../client/impl/logic}/SkinBinding.cpp | 22 +- .../client/impl/logic}/SkinBindingImpl.cpp | 73 +- .../client/impl/logic}/SkinBindingImpl.h | 27 +- .../client/impl/logic}/TimerNode.cpp | 14 +- .../client/impl/logic}/TimerNodeImpl.cpp | 38 +- .../client/impl/logic}/TimerNodeImpl.h | 4 +- .../client}/impl/ramses-utils.cpp | 253 +- .../client/impl/text}/FontCascade.cpp | 6 +- .../client/impl/text}/FontRegistry.cpp | 6 +- .../client/impl/text}/FontRegistryImpl.cpp | 28 +- .../client/impl/text}/FontRegistryImpl.h | 19 +- .../impl/text}/Freetype2FontInstance.cpp | 35 +- .../client/impl/text}/Freetype2FontInstance.h | 23 +- .../client/impl/text}/Freetype2Wrapper.h | 7 +- .../client/impl/text}/FreetypeFontFace.cpp | 13 +- .../client/impl/text}/FreetypeFontFace.h | 13 +- .../client/impl/text}/GlyphGeometry.h | 9 +- .../client/impl/text}/GlyphMapping.h | 7 +- .../client/impl/text}/GlyphTextureAtlas.cpp | 46 +- .../client/impl/text}/GlyphTextureAtlas.h | 16 +- .../client/impl/text}/GlyphTexturePage.cpp | 31 +- .../client/impl/text}/GlyphTexturePage.h | 14 +- .../impl/text}/HarfbuzzFontInstance.cpp | 59 +- .../client/impl/text}/HarfbuzzFontInstance.h | 9 +- .../client/impl/text}/LayoutUtils.cpp | 2 +- .../client/impl/text}/Quad.h | 5 +- .../client/impl/text}/TextCache.cpp | 6 +- .../client/impl/text}/TextCacheImpl.cpp | 74 +- .../client/impl/text}/TextCacheImpl.h | 18 +- .../client/impl/text}/TextTypesImpl.h | 13 +- .../client/impl/text}/UtfUtils.cpp | 66 +- .../internal}/ClientApplicationLogic.cpp | 34 +- .../client/internal}/ClientApplicationLogic.h | 45 +- .../ClientCommands/DumpSceneToFile.cpp | 83 + .../internal/ClientCommands/DumpSceneToFile.h | 17 +- .../ClientCommands/FlushSceneVersion.cpp | 10 +- .../ClientCommands/FlushSceneVersion.h | 27 + .../ClientCommands/LogMemoryUtils.cpp | 68 +- .../internal}/ClientCommands/LogMemoryUtils.h | 13 +- .../ClientCommands/LogResourceMemoryUsage.cpp | 10 +- .../ClientCommands/LogResourceMemoryUsage.h | 18 +- .../ClientCommands/PrintSceneList.cpp | 14 +- .../internal}/ClientCommands/PrintSceneList.h | 16 +- .../ClientCommands/SceneCommandBuffer.cpp | 0 .../ClientCommands/SceneCommandBuffer.h | 19 +- .../ClientCommands/SceneCommandVisitor.cpp | 111 + .../ClientCommands/SceneCommandVisitor.h | 17 +- .../ClientCommands/ValidateCommand.cpp | 27 +- .../ClientCommands/ValidateCommand.h | 18 +- .../client/internal}/DataSlotUtils.cpp | 6 +- .../client/internal}/DataSlotUtils.h | 13 +- .../client/internal}/RamsesVersion.cpp | 30 +- .../client/internal}/RamsesVersion.h | 19 +- .../client/internal/VisibilityModeUtils.h | 20 +- .../internal}/glslEffectBlock/GLSlang.h | 11 +- .../internal/glslEffectBlock/GlslEffect.cpp | 188 + .../internal}/glslEffectBlock/GlslEffect.h | 44 +- .../internal}/glslEffectBlock/GlslLimits.h | 20 +- .../internal/glslEffectBlock/GlslParser.cpp | 390 ++ .../internal/glslEffectBlock/GlslParser.h | 72 + .../glslEffectBlock/GlslToEffectConverter.cpp | 48 +- .../glslEffectBlock/GlslToEffectConverter.h | 43 +- .../client/internal/logic}/ApiObjects.cpp | 821 ++- .../client/internal/logic}/ApiObjects.h | 122 +- .../logic}/ApiObjectsSerializedSize.cpp | 106 +- .../logic}/ApiObjectsSerializedSize.h | 2 +- .../internal/logic}/DeserializationMap.h | 19 +- .../internal/logic}/DirectedAcyclicGraph.cpp | 2 +- .../internal/logic}/DirectedAcyclicGraph.h | 0 .../internal/logic}/EPropertySemantics.h | 0 .../internal/logic}/EnvironmentProtection.cpp | 6 +- .../internal/logic}/EnvironmentProtection.h | 2 +- .../client/internal/logic}/FileUtils.cpp | 0 .../client/internal/logic}/FileUtils.h | 0 .../logic}/InterfaceTypeFunctions.cpp | 4 +- .../internal/logic}/InterfaceTypeFunctions.h | 4 +- .../internal/logic}/InterfaceTypeInfo.h | 4 +- .../internal/logic}/LogicNodeDependencies.cpp | 48 +- .../internal/logic}/LogicNodeDependencies.h | 4 +- .../logic}/LogicNodeUpdateStatistics.cpp | 79 +- .../logic}/LogicNodeUpdateStatistics.h | 17 +- .../internal/logic}/LuaCompilationUtils.cpp | 87 +- .../internal/logic}/LuaCompilationUtils.h | 5 +- .../internal/logic}/LuaCustomizations.cpp | 24 +- .../internal/logic}/LuaCustomizations.h | 2 +- .../internal/logic}/LuaTypeConversions.cpp | 6 +- .../internal/logic}/LuaTypeConversions.h | 6 +- .../internal/logic}/PropertyTypeExtractor.cpp | 12 +- .../internal/logic}/PropertyTypeExtractor.h | 6 +- .../client/internal/logic}/RamsesHelper.h | 6 +- .../internal/logic}/RamsesObjectResolver.cpp | 30 +- .../internal/logic}/RamsesObjectResolver.h | 8 +- .../internal/logic}/SerializationHelper.h | 4 +- .../client/internal/logic}/SerializationMap.h | 12 +- .../client/internal/logic}/SolHelper.h | 4 +- .../client/internal/logic}/SolState.cpp | 20 +- .../client/internal/logic}/SolState.h | 18 +- .../client/internal/logic}/SolWrapper.h | 6 - .../internal/logic}/StdFilesystemWrapper.h | 0 .../client/internal/logic}/TypeData.cpp | 2 +- .../client/internal/logic}/TypeData.h | 2 +- .../client/internal/logic}/TypeUtils.h | 15 +- .../client/internal/logic}/UpdateReport.cpp | 3 +- .../client/internal/logic}/UpdateReport.h | 3 +- .../internal/logic}/WrappedLuaProperty.cpp | 12 +- .../internal/logic}/WrappedLuaProperty.h | 6 +- .../flatbuffers/generated/AnchorPointGen.h | 0 .../flatbuffers/generated/AnimationNodeGen.h | 0 .../flatbuffers/generated/ApiObjectsGen.h | 122 +- .../generated/AppearanceBindingGen.h | 46 +- .../flatbuffers/generated/CameraBindingGen.h | 44 +- .../flatbuffers/generated/DataArrayGen.h | 0 .../logic}/flatbuffers/generated/LinkGen.h | 0 .../flatbuffers/generated/LogicEngineGen.h | 137 + .../flatbuffers/generated/LogicObjectGen.h | 0 .../flatbuffers/generated/LuaInterfaceGen.h | 0 .../flatbuffers/generated/LuaModuleGen.h | 0 .../flatbuffers/generated/LuaScriptGen.h | 0 .../generated/MeshNodeBindingGen.h | 44 +- .../flatbuffers/generated/NodeBindingGen.h | 46 +- .../flatbuffers/generated/PropertyGen.h | 0 .../flatbuffers/generated/RamsesBindingGen.h | 0 .../generated/RamsesReferenceGen.h | 0 .../generated/RenderGroupBindingGen.h | 50 +- .../generated/RenderPassBindingGen.h | 44 +- .../flatbuffers/generated/SkinBindingGen.h | 0 .../flatbuffers/generated/TimerNodeGen.h | 0 .../flatbuffers/schemas/AnchorPoint.fbs | 0 .../flatbuffers/schemas/AnimationNode.fbs | 0 .../logic}/flatbuffers/schemas/ApiObjects.fbs | 25 +- .../flatbuffers/schemas/AppearanceBinding.fbs | 2 +- .../flatbuffers/schemas/CameraBinding.fbs | 2 +- .../logic}/flatbuffers/schemas/DataArray.fbs | 0 .../logic}/flatbuffers/schemas/Link.fbs | 0 .../logic/flatbuffers/schemas/LogicEngine.fbs | 19 + .../flatbuffers/schemas/LogicObject.fbs | 0 .../flatbuffers/schemas/LuaInterface.fbs | 0 .../logic}/flatbuffers/schemas/LuaModule.fbs | 0 .../logic}/flatbuffers/schemas/LuaScript.fbs | 0 .../flatbuffers/schemas/MeshNodeBinding.fbs | 2 +- .../logic/flatbuffers/schemas/NodeBinding.fbs | 2 +- .../logic}/flatbuffers/schemas/Property.fbs | 0 .../flatbuffers/schemas/RamsesBinding.fbs | 0 .../flatbuffers/schemas/RamsesReference.fbs | 0 .../schemas/RenderGroupBinding.fbs | 2 +- .../flatbuffers/schemas/RenderPassBinding.fbs | 2 +- .../flatbuffers/schemas/SkinBinding.fbs | 0 .../logic}/flatbuffers/schemas/TimerNode.fbs | 0 src/framework/CMakeLists.txt | 119 + .../framework/impl}/APILoggingHelper.h | 9 +- .../framework/impl}/APILoggingMacros.h | 15 +- .../framework/impl}/AppearanceEnums.cpp | 2 +- .../framework/impl}/AppearanceEnumsImpl.h | 22 +- .../include => src/framework/impl}/CommandT.h | 10 +- src/framework/impl/DataTypeUtils.h | 174 + .../framework/impl}/DataTypesImpl.h | 16 +- src/framework/impl/EFeatureLevelImpl.h | 27 + src/framework/impl/ErrorReporting.cpp | 47 + .../framework/impl/ErrorReporting.h | 29 +- .../impl}/FrameworkFactoryRegistry.cpp | 10 +- .../impl}/FrameworkFactoryRegistry.h | 9 +- .../framework/impl}/PublicRamshCommand.cpp | 4 +- .../framework/impl}/PublicRamshCommand.h | 13 +- src/framework/impl/RamsesFramework.cpp | 120 + src/framework/impl/RamsesFrameworkConfig.cpp | 160 + .../impl}/RamsesFrameworkConfigImpl.cpp | 76 +- .../impl/RamsesFrameworkConfigImpl.h | 83 + .../framework/impl}/RamsesFrameworkImpl.cpp | 198 +- src/framework/impl/RamsesFrameworkImpl.h | 115 + .../impl/RamsesFrameworkTypesImpl.cpp | 34 + .../impl}/RamsesFrameworkTypesImpl.h | 31 +- src/framework/impl/RamsesObject.cpp | 232 + .../impl}/RamsesObjectFactoryInterfaces.h | 15 +- src/framework/impl/RamsesObjectImpl.cpp | 121 + src/framework/impl/RamsesObjectImpl.h | 68 + .../framework}/impl/RamsesObjectTypeTraits.h | 35 +- src/framework/impl/RamsesObjectTypeUtils.cpp | 58 + src/framework/impl/RamsesObjectTypeUtils.h | 60 + .../framework/impl}/RamsesVersion.cpp | 2 +- .../framework}/impl/SerializationContext.cpp | 33 +- .../framework}/impl/SerializationContext.h | 47 +- .../src => src/framework/impl}/TCPConfig.cpp | 5 +- .../framework/impl}/TCPConfig.h | 11 +- src/framework/impl/TextureEnums.cpp | 32 + .../framework/impl/TextureEnumsImpl.h | 45 +- src/framework/impl/ThreadWatchdogConfig.h | 64 + src/framework/impl/ValidationReport.cpp | 68 + src/framework/impl/ValidationReportImpl.cpp | 53 + src/framework/impl/ValidationReportImpl.h | 78 + .../CommunicationSystemFactory.cpp | 46 +- .../CommunicationSystemFactory.h | 22 +- .../ConnectionStatusUpdateNotifier.cpp | 24 +- .../ConnectionStatusUpdateNotifier.h | 23 +- .../TransportCommon/EConnectionProtocol.h | 15 +- .../TransportCommon/EConnectionStatus.h | 9 +- .../FakeConnectionStatusUpdateNotifier.h | 17 +- .../TransportCommon/FakeConnectionSystem.h | 17 +- .../TransportCommon/FakeDiscoveryDaemon.h | 14 +- .../TransportCommon/ICommunicationSystem.h | 27 +- .../IConnectionStatusListener.h | 10 +- .../IConnectionStatusUpdateNotifier.h | 9 +- .../TransportCommon/IDiscoveryDaemon.h | 9 +- .../TransportCommon/ISceneUpdateSerializer.h | 12 +- .../TransportCommon}/LogConnectionInfo.cpp | 6 +- .../TransportCommon/LogConnectionInfo.h | 9 +- .../RamsesTransportProtocolVersion.h | 7 +- .../SceneUpdateSerializationHelper.cpp | 58 +- .../SceneUpdateSerializationHelper.h | 44 + .../SceneUpdateSerializer.cpp | 8 +- .../TransportCommon/SceneUpdateSerializer.h | 11 +- .../SceneUpdateStreamDeserializer.cpp | 34 +- .../SceneUpdateStreamDeserializer.h | 17 +- .../ServiceHandlerInterfaces.h | 25 +- .../SingleSceneUpdateWriter.cpp | 28 +- .../TransportCommon/SingleSceneUpdateWriter.h | 21 +- .../Communication}/TransportTCP/AsioWrapper.h | 7 +- .../Communication}/TransportTCP/EMessageId.h | 29 +- .../TransportTCP/NetworkParticipantAddress.h | 20 +- .../TransportTCP}/TCPConnectionSystem.cpp | 91 +- .../TransportTCP/TCPConnectionSystem.h | 46 +- .../TransportTCP}/TcpDiscoveryDaemon.cpp | 17 +- .../TransportTCP/TcpDiscoveryDaemon.h | 22 +- .../Components}/ClientSceneLogicBase.cpp | 99 +- .../Components/ClientSceneLogicBase.h | 25 +- .../Components}/ClientSceneLogicDirect.cpp | 44 +- .../Components/ClientSceneLogicDirect.h | 11 +- .../ClientSceneLogicShadowCopy.cpp | 42 +- .../Components/ClientSceneLogicShadowCopy.h | 14 +- .../Components/ERendererToClientEventType.h | 11 +- .../Components}/EffectUniformTime.cpp | 4 +- .../internal}/Components/EffectUniformTime.h | 7 +- .../Components/FileInputStreamContainer.h | 13 +- .../internal}/Components/FlushInformation.h | 28 +- .../Components/FlushTimeInformation.h | 11 +- .../IManagedResourceDeleterCallback.h | 13 +- .../Components/IResourceHashUsageCallback.h | 11 +- .../Components/IResourceProviderComponent.h | 19 +- .../Components/ISceneGraphConsumerComponent.h | 15 +- .../Components/ISceneGraphProviderComponent.h | 19 +- .../internal}/Components/ISceneGraphSender.h | 11 +- .../Components/ISceneProviderEventConsumer.h | 7 +- .../Components/ISceneRendererHandler.h | 9 +- .../Components/InputStreamContainer.h | 9 +- .../internal}/Components/ManagedResource.h | 11 +- .../Components/MemoryInputStreamContainer.h | 15 +- .../OffsetFileInputStreamContainer.h | 11 +- .../Components}/ResourceAvailabilityEvent.cpp | 21 +- .../Components/ResourceAvailabilityEvent.h | 15 +- .../Components}/ResourceComponent.cpp | 37 +- .../internal}/Components/ResourceComponent.h | 16 +- .../ResourceDeleterCallingCallback.h | 7 +- .../Components/ResourceFilesRegistry.h | 21 +- .../internal}/Components/ResourceHashUsage.h | 13 +- .../Components/ResourceHashUsageCallback.h | 7 +- .../Components}/ResourcePersistation.cpp | 34 +- .../Components/ResourcePersistation.h | 13 +- .../ResourceSerializationHelper.cpp | 42 +- .../Components/ResourceSerializationHelper.h | 19 +- .../internal/Components}/ResourceStorage.cpp | 14 +- .../internal}/Components/ResourceStorage.h | 37 +- .../Components}/ResourceTableOfContents.cpp | 12 +- .../Components/ResourceTableOfContents.h | 21 +- .../internal}/Components/SceneFileHandle.h | 11 +- .../Components}/SceneGraphComponent.cpp | 97 +- .../Components/SceneGraphComponent.h | 37 +- .../internal}/Components/SceneUpdate.h | 13 +- .../SingleResourceSerialization.cpp | 18 +- .../Components/SingleResourceSerialization.h | 12 +- .../internal/Core}/Common/BitForgeMacro.h | 5 +- .../internal/Core}/Common/MemoryHandle.h | 9 +- .../Core/Common}/ParticipantIdentifier.cpp | 8 +- .../Core}/Common/ParticipantIdentifier.h | 9 +- .../internal/Core/Common/StronglyTypedValue.h | 31 + .../internal/Core}/Common/TypedMemoryHandle.h | 50 +- .../Core}/Math3d/CameraMatrixHelper.h | 7 +- .../Core/Math3d}/ProjectionParams.cpp | 12 +- .../internal/Core}/Math3d/ProjectionParams.h | 27 +- .../framework/internal/Core/Math3d}/Quad.cpp | 8 +- .../framework/internal/Core}/Math3d/Quad.h | 9 +- .../internal/Core/Math3d}/Rotation.cpp | 9 +- .../internal/Core}/Math3d/Rotation.h | 6 +- .../EnqueueOnlyOneAtATimeQueue.cpp | 19 +- .../EnqueueOnlyOneAtATimeQueue.h | 9 +- .../internal/Core}/TaskFramework/ITask.h | 9 +- .../Core}/TaskFramework/ITaskFinishHandler.h | 7 +- .../internal/Core}/TaskFramework/ITaskQueue.h | 11 +- .../Core}/TaskFramework/ProcessingTaskQueue.h | 9 +- .../internal/Core}/TaskFramework/RefCounted.h | 7 +- .../TaskFramework}/TaskExecutingThread.cpp | 10 +- .../Core}/TaskFramework/TaskExecutingThread.h | 11 +- .../TaskExecutingThreadPool.cpp | 15 +- .../TaskFramework/TaskExecutingThreadPool.h | 18 +- .../TaskFinishHandlerDecorator.cpp | 6 +- .../TaskFinishHandlerDecorator.h | 9 +- .../TaskFramework}/TaskForwardingQueue.cpp | 11 +- .../Core}/TaskFramework/TaskForwardingQueue.h | 9 +- .../TaskFramework}/ThreadedTaskExecutor.cpp | 8 +- .../TaskFramework/ThreadedTaskExecutor.h | 17 +- .../internal/Core}/Utils/Adler32Checksum.h | 13 +- .../AndroidLogger}/AndroidLogAppender.cpp | 12 +- .../Utils}/AndroidLogger/AndroidLogAppender.h | 9 +- .../internal/Core}/Utils/AssertMovable.h | 5 +- .../Core}/Utils/BinaryFileInputStream.h | 30 +- .../Core}/Utils/BinaryFileOutputStream.h | 17 +- .../internal/Core}/Utils/BinaryInputStream.h | 31 +- .../Utils}/BinaryOffsetFileInputStream.cpp | 23 +- .../Core}/Utils/BinaryOffsetFileInputStream.h | 13 +- .../internal/Core}/Utils/BinaryOutputStream.h | 34 +- .../Core/Utils}/ConsoleLogAppender.cpp | 25 +- .../internal/Core}/Utils/ConsoleLogAppender.h | 13 +- .../internal/Core}/Utils/DataTypeUtils.h | 12 +- .../internal/Core/Utils/EnumTraits.h | 123 + .../framework/internal/Core/Utils}/File.cpp | 232 +- .../framework/internal/Core}/Utils/File.h | 54 +- .../internal/Core}/Utils/HandlePool.h | 21 +- .../Core}/Utils/IPeriodicLogSupplier.h | 9 +- .../framework/internal/Core/Utils}/Image.cpp | 29 +- .../framework/internal/Core}/Utils/Image.h | 17 +- .../Core}/Utils/InplaceStringTokenizer.h | 12 +- .../internal/Core}/Utils/LogAppenderBase.h | 7 +- .../internal/Core}/Utils/LogContext.h | 9 +- .../internal/Core/Utils}/LogHelper.cpp | 28 +- .../internal/Core}/Utils/LogHelper.h | 11 +- .../framework/internal/Core}/Utils/LogLevel.h | 9 +- .../internal/Core/Utils}/LogMacros.cpp | 6 +- .../internal/Core}/Utils/LogMacros.h | 99 +- .../internal/Core}/Utils/LogMessage.h | 11 +- .../internal/Core/Utils/LoggingUtils.h | 65 + .../internal/Core}/Utils/MemoryPool.h | 11 +- .../internal/Core}/Utils/MemoryPoolExplicit.h | 13 +- .../internal/Core}/Utils/MemoryPoolIterator.h | 12 +- .../internal/Core}/Utils/MemoryUtils.h | 15 +- .../internal/Core/Utils}/PeriodicLogger.cpp | 18 +- .../internal/Core}/Utils/PeriodicLogger.h | 30 +- .../Core}/Utils/PeriodicLoggerHelper.h | 18 +- .../internal/Core/Utils}/RamsesLogger.cpp | 59 +- .../internal/Core}/Utils/RamsesLogger.h | 30 +- .../Core}/Utils/RawBinaryOutputStream.h | 33 +- .../Core/Utils}/StatisticCollection.cpp | 12 +- .../Core}/Utils/StatisticCollection.h | 18 +- .../internal/Core/Utils}/StringUtils.cpp | 14 +- .../internal/Core}/Utils/StringUtils.h | 11 +- .../internal/Core}/Utils/TextureMathUtils.h | 9 +- .../internal/Core}/Utils/ThreadBarrier.h | 19 +- .../internal/Core/Utils}/ThreadLocalLog.cpp | 4 +- .../internal/Core}/Utils/ThreadLocalLog.h | 71 +- .../Core}/Utils/ThreadLocalLogForced.h | 7 +- .../internal/Core/Utils/UserLogAppender.cpp | 26 + .../internal/Core}/Utils/UserLogAppender.h | 12 +- .../Core}/Utils/VectorBinaryOutputStream.h | 31 +- .../internal/Core}/Utils/VoidOutputStream.h | 30 +- .../framework/internal/Core}/Utils/Warnings.h | 8 +- .../internal/DltLogAppender/DltAdapter.h | 22 +- .../DltLogAppender}/DltAdapterDummy.cpp | 2 +- .../DltLogAppender}/DltAdapterDummy.h | 23 +- .../DltAdapterImpl/DltAdapterImpl.cpp | 227 +- .../DltAdapterImpl/DltAdapterImpl.h | 29 +- .../DltLogAppender}/DltLogAppender.cpp | 4 +- .../internal}/DltLogAppender/DltLogAppender.h | 11 +- .../internal}/DltLogAppender/IDltAdapter.h | 18 +- .../Collections/BlockingQueue.h | 9 +- .../PlatformAbstraction}/Collections/Guid.h | 32 +- .../Collections/HashMap.h | 63 +- .../Collections/HashSet.h | 20 +- .../Collections/HeapArray.h | 23 +- .../Collections/IInputStream.h | 14 +- .../Collections/IOutputStream.h | 11 +- .../PlatformAbstraction}/Collections/Pair.h | 9 +- .../Collections/StringOutputStream.h | 22 +- .../PlatformAbstraction}/Collections/Vector.h | 7 +- .../PlatformAbstraction}/ConsoleInput.cpp | 34 +- .../PlatformAbstraction/ConsoleInput.h | 7 +- .../internal}/PlatformAbstraction/FmtBase.h | 13 +- .../internal/PlatformAbstraction}/Guid.cpp | 7 +- .../internal}/PlatformAbstraction/Hash.h | 18 +- .../internal}/PlatformAbstraction/Macros.h | 5 +- .../PlatformAbstraction/MinimalWindowsH.h | 5 +- .../PlatformAbstraction}/PlatformConsole.cpp | 8 +- .../PlatformAbstraction/PlatformConsole.h | 7 +- .../PlatformEnvironmentVariables.h | 9 +- .../PlatformAbstraction/PlatformError.h | 11 +- .../PlatformAbstraction/PlatformEvent.h | 13 +- .../PlatformAbstraction/PlatformLock.h | 7 +- .../PlatformAbstraction/PlatformMath.h | 7 +- .../PlatformAbstraction/PlatformMemory.h | 9 +- .../PlatformAbstraction}/PlatformSignal.cpp | 16 +- .../PlatformAbstraction/PlatformSignal.h | 7 +- .../PlatformAbstraction/PlatformStringUtils.h | 7 +- .../PlatformAbstraction/PlatformThread.h | 13 +- .../PlatformAbstraction/PlatformTime.h | 10 +- .../PlatformAbstraction}/PlatformTypes.cpp | 9 +- .../internal}/PlatformAbstraction/Runnable.h | 7 +- .../StringOutputStream.cpp | 2 +- .../PlatformAbstraction/VariantWrapper.h | 7 +- .../internal/Thread_Posix.h | 19 +- .../PlatformAbstraction/internal/Thread_std.h | 11 +- .../synchronized_clock.cpp | 6 +- .../PlatformAbstraction/synchronized_clock.h | 11 +- .../framework/internal/Ramsh}/Ramsh.cpp | 26 +- .../framework/internal}/Ramsh/Ramsh.h | 7 +- .../internal/Ramsh}/RamshCommand.cpp | 4 +- .../framework/internal}/Ramsh/RamshCommand.h | 9 +- .../internal}/Ramsh/RamshCommandArguments.h | 31 +- .../Ramsh/RamshCommandArgumentsConverter.h | 88 +- .../Ramsh/RamshCommandArgumentsDataProvider.h | 57 +- .../Ramsh/RamshCommandArgumentsUtils.h | 7 +- .../internal/Ramsh}/RamshCommandExit.cpp | 11 +- .../internal}/Ramsh/RamshCommandExit.h | 11 +- .../Ramsh}/RamshCommandPrintBuildConfig.cpp | 10 +- .../Ramsh/RamshCommandPrintBuildConfig.h | 11 +- .../internal/Ramsh}/RamshCommandPrintHelp.cpp | 10 +- .../internal}/Ramsh/RamshCommandPrintHelp.h | 11 +- .../Ramsh}/RamshCommandPrintLogLevels.cpp | 14 +- .../Ramsh/RamshCommandPrintLogLevels.h | 11 +- .../Ramsh}/RamshCommandPrintRamsesVersion.cpp | 8 +- .../Ramsh/RamshCommandPrintRamsesVersion.h | 11 +- .../Ramsh}/RamshCommandSetConsoleLogLevel.cpp | 12 +- .../Ramsh/RamshCommandSetConsoleLogLevel.h | 11 +- .../Ramsh}/RamshCommandSetContextLogLevel.cpp | 12 +- .../Ramsh/RamshCommandSetContextLogLevel.h | 11 +- .../RamshCommandSetContextLogLevelFilter.cpp | 10 +- .../RamshCommandSetContextLogLevelFilter.h | 11 +- .../RamshCommunicationChannelConsole.cpp | 29 +- .../Ramsh/RamshCommunicationChannelConsole.h | 15 +- ...mmunicationChannelConsoleSignalHandler.cpp | 14 +- ...CommunicationChannelConsoleSignalHandler.h | 9 +- .../Ramsh}/RamshCommunicationChannelDLT.cpp | 45 +- .../Ramsh/RamshCommunicationChannelDLT.h | 28 +- .../internal/Ramsh}/RamshStandardSetup.cpp | 23 +- .../internal}/Ramsh/RamshStandardSetup.h | 17 +- .../framework/internal/Ramsh}/RamshTools.cpp | 15 +- .../framework/internal}/Ramsh/RamshTools.h | 7 +- .../framework/internal}/Ramsh/RamshTypeInfo.h | 19 +- .../SceneGraph}/Resource/ArrayResource.h | 21 +- .../SceneGraph}/Resource/BufferResource.h | 17 +- .../Resource/EResourceCompressionStatus.h | 31 + .../Resource/EffectInputInformation.h | 29 +- .../SceneGraph/Resource}/EffectResource.cpp | 48 +- .../SceneGraph}/Resource/EffectResource.h | 21 +- .../internal/SceneGraph}/Resource/IResource.h | 16 +- .../Resource}/LZ4CompressionUtils.cpp | 8 +- .../Resource/LZ4CompressionUtils.h | 11 +- .../SceneGraph/Resource}/ResourceBase.cpp | 8 +- .../SceneGraph}/Resource/ResourceBase.h | 20 +- .../SceneGraph}/Resource/ResourceInfo.h | 24 +- .../SceneGraph/Resource/ResourceTypes.h | 51 + .../SceneGraph}/Resource/TextureMetaInfo.h | 28 +- .../SceneGraph}/Resource/TextureResource.h | 49 +- .../Scene}/ActionCollectingScene.cpp | 20 +- .../SceneGraph}/Scene/ActionCollectingScene.h | 60 +- .../internal/SceneGraph}/Scene/ClientScene.h | 13 +- .../internal/SceneGraph}/Scene/DataInstance.h | 17 +- .../internal/SceneGraph}/Scene/DataLayout.h | 13 +- .../Scene}/DataLayoutCachedScene.cpp | 4 +- .../SceneGraph}/Scene/DataLayoutCachedScene.h | 17 +- .../SceneGraph}/Scene/ESceneActionId.h | 15 +- .../SceneGraph/Scene/EScenePublicationMode.h | 31 + .../SceneGraph}/Scene/ETransformMatrixType.h | 7 +- .../SceneGraph}/Scene/MatrixCacheEntry.h | 18 +- .../Scene}/ResourceChangeCollectingScene.cpp | 16 +- .../Scene/ResourceChangeCollectingScene.h | 29 +- .../SceneGraph/Scene}/ResourceChanges.cpp | 12 +- .../SceneGraph}/Scene/ResourceChanges.h | 21 +- .../internal/SceneGraph/Scene}/Scene.cpp | 69 +- .../internal/SceneGraph}/Scene/Scene.h | 216 +- .../SceneGraph/Scene}/SceneActionApplier.cpp | 238 +- .../SceneGraph}/Scene/SceneActionApplier.h | 13 +- .../Scene}/SceneActionCollection.cpp | 4 +- .../SceneGraph}/Scene/SceneActionCollection.h | 81 +- .../Scene}/SceneActionCollectionCreator.cpp | 55 +- .../Scene/SceneActionCollectionCreator.h | 66 +- .../SceneGraph/Scene}/SceneDescriber.cpp | 42 +- .../SceneGraph}/Scene/SceneDescriber.h | 9 +- .../SceneGraph/Scene}/ScenePersistation.cpp | 28 +- .../SceneGraph}/Scene/ScenePersistation.h | 11 +- .../internal/SceneGraph}/Scene/TopologyNode.h | 14 +- .../SceneGraph}/Scene/TopologyTransform.h | 15 +- .../Scene}/TransformationCachedScene.cpp | 19 +- .../Scene/TransformationCachedScene.h | 22 +- .../internal/SceneGraph}/SceneAPI/BlitPass.h | 15 +- .../internal/SceneGraph}/SceneAPI/Camera.h | 13 +- .../SceneGraph}/SceneAPI/DataFieldInfo.h | 13 +- .../internal/SceneGraph}/SceneAPI/DataSlot.h | 19 +- .../SceneAPI/ECameraProjectionType.h | 16 +- .../SceneGraph}/SceneAPI/EDataBufferType.h | 16 +- .../SceneGraph/SceneAPI/EDataSlotType.h | 40 + .../internal/SceneGraph}/SceneAPI/EDataType.h | 36 +- .../SceneGraph}/SceneAPI/EFixedSemantics.h | 20 +- .../SceneAPI/ERenderableDataSlotType.h | 16 +- .../SceneGraph}/SceneAPI/ERotationType.h | 31 +- .../SceneGraph/SceneAPI/EShaderStage.h | 37 + .../SceneAPI/EShaderWarningCategory.h | 39 + .../SceneGraph}/SceneAPI/GeometryDataBuffer.h | 17 +- .../internal/SceneGraph}/SceneAPI/Handles.h | 9 +- .../internal/SceneGraph/SceneAPI/IScene.h | 317 + .../SceneGraph}/SceneAPI/MipMapSize.h | 9 +- .../SceneGraph}/SceneAPI/PickableObject.h | 11 +- .../SceneGraph}/SceneAPI/PixelRectangle.h | 15 +- .../SceneGraph}/SceneAPI/RenderBuffer.h | 26 +- .../SceneGraph}/SceneAPI/RenderGroup.h | 15 +- .../SceneGraph}/SceneAPI/RenderGroupUtils.h | 11 +- .../SceneGraph}/SceneAPI/RenderPass.h | 17 +- .../SceneGraph}/SceneAPI/RenderState.h | 40 +- .../SceneGraph}/SceneAPI/RenderTarget.h | 9 +- .../SceneGraph}/SceneAPI/Renderable.h | 21 +- .../SceneGraph}/SceneAPI/RendererSceneState.h | 22 +- .../SceneAPI/ResourceContentHash.h | 60 +- .../SceneGraph}/SceneAPI/ResourceField.h | 20 +- .../SceneAPI/SceneCreationInformation.h | 11 +- .../internal/SceneGraph}/SceneAPI/SceneId.h | 26 +- .../SceneGraph}/SceneAPI/SceneReference.h | 11 +- .../SceneAPI/SceneSizeInformation.h | 16 +- .../SceneGraph}/SceneAPI/SceneTypes.h | 15 +- .../SceneGraph}/SceneAPI/SceneVersionTag.h | 14 +- .../SceneGraph}/SceneAPI/TextureBuffer.h | 21 +- .../SceneGraph/SceneAPI/TextureEnums.h | 256 + .../SceneGraph}/SceneAPI/TextureSampler.h | 29 +- .../SceneAPI/TextureSamplerStates.h | 37 +- .../internal/SceneGraph}/SceneAPI/Viewport.h | 9 +- .../SceneUtils}/DataInstanceHelper.cpp | 72 +- .../SceneUtils/DataInstanceHelper.h | 14 +- .../SceneUtils}/DataLayoutCreationHelper.cpp | 8 +- .../SceneUtils/DataLayoutCreationHelper.h | 15 +- .../SceneUtils/ISceneDataArrayAccessor.h | 21 +- .../SceneGraph/SceneUtils}/ResourceUtils.cpp | 18 +- .../SceneGraph}/SceneUtils/ResourceUtils.h | 19 +- .../SceneReferenceAction.cpp | 4 +- .../SceneReferencing/SceneReferenceAction.h | 31 +- .../SceneReferencing}/SceneReferenceEvent.cpp | 17 +- .../SceneReferencing/SceneReferenceEvent.h | 23 +- .../internal}/Watchdog/IThreadAliveNotifier.h | 9 +- .../internal/Watchdog}/PlatformWatchdog.cpp | 10 +- .../internal}/Watchdog/PlatformWatchdog.h | 23 +- .../Watchdog/ThreadAliveNotifierMock.h | 10 +- .../internal/Watchdog}/ThreadWatchdog.cpp | 6 +- .../internal}/Watchdog/ThreadWatchdog.h | 13 +- {ramses-cli => src/ramses-cli}/CMakeLists.txt | 22 +- .../ramses-cli}/include/ramses-cli.h | 8 +- .../include/ramses-framework-cli.h | 2 +- .../renderer}/CMakeLists.txt | 17 +- src/renderer/impl/BinaryShaderCache.cpp | 91 + .../renderer/impl}/BinaryShaderCacheImpl.cpp | 108 +- src/renderer/impl/BinaryShaderCacheImpl.h | 65 + .../renderer/impl}/BinaryShaderCacheProxy.cpp | 26 +- src/renderer/impl/BinaryShaderCacheProxy.h | 45 + .../impl}/CommandDispatchingThread.cpp | 11 +- .../renderer/impl}/CommandDispatchingThread.h | 15 +- src/renderer/impl/DisplayConfig.cpp | 277 + src/renderer/impl/DisplayConfigImpl.cpp | 334 ++ src/renderer/impl/DisplayConfigImpl.h | 92 + src/renderer/impl/RamsesRenderer.cpp | 272 + src/renderer/impl/RamsesRendererImpl.cpp | 717 +++ src/renderer/impl/RamsesRendererImpl.h | 173 + src/renderer/impl/RendererConfig.cpp | 93 + .../renderer/impl}/RendererConfigImpl.cpp | 46 +- src/renderer/impl/RendererConfigImpl.h | 45 + .../renderer/impl}/RendererEventChainer.h | 13 +- .../renderer/impl}/RendererFactory.cpp | 8 +- .../renderer/impl}/RendererFactory.h | 13 +- .../renderer/impl}/RendererMate.cpp | 45 +- .../renderer/impl}/RendererMate.h | 25 +- .../impl}/RendererMateRamshCommands.cpp | 47 +- .../impl}/RendererMateRamshCommands.h | 28 +- src/renderer/impl/RendererSceneControl.cpp | 107 + .../impl/RendererSceneControlImpl.cpp | 273 + src/renderer/impl/RendererSceneControlImpl.h | 73 + .../Android}/Platform_Android_EGL.cpp | 10 +- .../Platform/Android}/Platform_Android_EGL.h | 13 +- .../Platform/Android}/Window_Android.cpp | 15 +- .../Platform/Android}/Window_Android.h | 11 +- src/renderer/internal/Platform/CMakeLists.txt | 107 + .../Device_EGL_Extension.cpp | 11 +- .../Device_EGL_Extension.h | 17 +- .../internal/Platform/EGL}/Context_EGL.cpp | 31 +- .../internal/Platform/EGL}/Context_EGL.h | 35 +- .../internal/Platform/EGL}/Platform_EGL.h | 75 +- .../internal/Platform/OpenGL}/DebugOutput.cpp | 12 +- .../internal/Platform/OpenGL}/DebugOutput.h | 14 +- .../internal/Platform/OpenGL}/Device_GL.cpp | 647 +- .../internal/Platform/OpenGL}/Device_GL.h | 88 +- .../Platform/OpenGL}/Device_GL_platform.cpp | 4 +- .../Platform/OpenGL}/Device_GL_platform.h | 23 +- .../OpenGL/Device_GL_platform_apple.h | 17 + .../OpenGL}/Device_GL_platform_linux.h | 5 +- .../OpenGL}/Device_GL_platform_windows.h | 5 +- .../Platform/OpenGL}/ShaderGPUResource_GL.cpp | 21 +- .../Platform/OpenGL}/ShaderGPUResource_GL.h | 26 +- .../Platform/OpenGL}/ShaderProgramInfo.h | 12 +- .../Platform/OpenGL}/ShaderUploader_GL.cpp | 103 +- .../Platform/OpenGL}/ShaderUploader_GL.h | 11 +- .../Platform/OpenGL}/TypesConversion_GL.cpp | 373 +- .../Platform/OpenGL}/TypesConversion_GL.h | 29 +- .../internal/Platform/OpenGL}/Types_GL.h | 13 +- .../internal/Platform}/PlatformFactory.cpp | 27 +- .../internal/Platform}/PlatformFactory.h | 9 +- .../EmbeddedCompositor_Wayland.cpp | 110 +- .../EmbeddedCompositor_Wayland.h | 38 +- .../IEmbeddedCompositor_Wayland.h | 9 +- .../INativeWaylandResource.h | 9 +- .../EmbeddedCompositor}/IWaylandBuffer.h | 12 +- .../EmbeddedCompositor}/IWaylandClient.h | 11 +- .../IWaylandCompositorConnection.h | 9 +- .../IWaylandCompositorGlobal.h | 11 +- .../EmbeddedCompositor}/IWaylandDisplay.h | 9 +- .../EmbeddedCompositor}/IWaylandGlobal.h | 9 +- .../IWaylandIVIApplicationConnection.h | 12 +- .../IWaylandIVIApplicationGlobal.h | 9 +- .../EmbeddedCompositor}/IWaylandIVISurface.h | 11 +- .../EmbeddedCompositor}/IWaylandRegion.h | 9 +- .../IWaylandShellConnection.h | 9 +- .../EmbeddedCompositor}/IWaylandShellGlobal.h | 9 +- .../IWaylandShellSurface.h | 9 +- .../EmbeddedCompositor}/IWaylandSurface.h | 16 +- .../EmbeddedCompositor}/LinuxDmabuf.cpp | 25 +- .../Wayland/EmbeddedCompositor}/LinuxDmabuf.h | 25 +- .../EmbeddedCompositor}/LinuxDmabufBuffer.h | 7 +- .../LinuxDmabufConnection.cpp | 30 +- .../LinuxDmabufConnection.h | 9 +- .../EmbeddedCompositor}/LinuxDmabufGlobal.cpp | 43 +- .../EmbeddedCompositor}/LinuxDmabufGlobal.h | 11 +- .../EmbeddedCompositor}/LinuxDmabufParams.cpp | 61 +- .../EmbeddedCompositor}/LinuxDmabufParams.h | 9 +- .../NativeWaylandResource.cpp | 9 +- .../NativeWaylandResource.h | 11 +- .../TextureUploadingAdapter_Wayland.cpp | 85 +- .../TextureUploadingAdapter_Wayland.h | 24 +- .../EmbeddedCompositor/WaylandBuffer.cpp | 133 + .../EmbeddedCompositor}/WaylandBuffer.h | 19 +- .../WaylandBufferResource.cpp | 31 +- .../WaylandBufferResource.h | 13 +- .../WaylandCallbackResource.cpp | 8 +- .../WaylandCallbackResource.h | 9 +- .../EmbeddedCompositor}/WaylandClient.cpp | 14 +- .../EmbeddedCompositor}/WaylandClient.h | 9 +- .../WaylandClientCredentials.cpp | 4 +- .../WaylandClientCredentials.h | 13 +- .../WaylandCompositorConnection.cpp | 26 +- .../WaylandCompositorConnection.h | 12 +- .../WaylandCompositorGlobal.cpp | 22 +- .../WaylandCompositorGlobal.h | 9 +- .../EmbeddedCompositor}/WaylandDisplay.cpp | 72 +- .../EmbeddedCompositor}/WaylandDisplay.h | 15 +- .../EmbeddedCompositor}/WaylandGlobal.cpp | 4 +- .../EmbeddedCompositor}/WaylandGlobal.h | 9 +- .../WaylandIVIApplicationConnection.cpp | 35 +- .../WaylandIVIApplicationConnection.h | 14 +- .../WaylandIVIApplicationGlobal.cpp | 23 +- .../WaylandIVIApplicationGlobal.h | 9 +- .../EmbeddedCompositor}/WaylandIVISurface.cpp | 45 +- .../EmbeddedCompositor}/WaylandIVISurface.h | 12 +- .../WaylandOutputConnection.cpp | 19 +- .../WaylandOutputConnection.h | 12 +- .../WaylandOutputGlobal.cpp | 16 +- .../EmbeddedCompositor}/WaylandOutputGlobal.h | 9 +- .../EmbeddedCompositor}/WaylandOutputParams.h | 7 +- .../EmbeddedCompositor}/WaylandRegion.cpp | 33 +- .../EmbeddedCompositor}/WaylandRegion.h | 14 +- .../WaylandShellConnection.cpp | 24 +- .../WaylandShellConnection.h | 12 +- .../WaylandShellGlobal.cpp | 27 +- .../EmbeddedCompositor}/WaylandShellGlobal.h | 9 +- .../WaylandShellSurface.cpp | 103 +- .../EmbeddedCompositor}/WaylandShellSurface.h | 14 +- .../EmbeddedCompositor}/WaylandSurface.cpp | 189 +- .../EmbeddedCompositor}/WaylandSurface.h | 22 +- .../IVI}/Platform_Wayland_IVI_EGL_ES_3_0.cpp | 20 +- .../IVI}/Platform_Wayland_IVI_EGL_ES_3_0.h | 9 +- .../IVIControllerScreen.cpp | 8 +- .../IVIControllerScreen.h | 10 +- .../IVIControllerSurface.cpp | 109 +- .../IVIControllerSurface.h | 18 +- ...SystemCompositorController_Wayland_IVI.cpp | 129 +- .../SystemCompositorController_Wayland_IVI.h | 15 +- .../WaylandOutput.cpp | 41 +- .../WaylandOutput.h | 12 +- .../Wayland/IVI}/Window_Wayland_IVI.cpp | 8 +- .../Wayland/IVI}/Window_Wayland_IVI.h | 11 +- .../Wayland}/InputHandling_Wayland.cpp | 142 +- .../Platform/Wayland}/InputHandling_Wayland.h | 25 +- .../Platform/Wayland}/Logger_Wayland.cpp | 6 +- .../Platform/Wayland}/Logger_Wayland.h | 7 +- .../Wayland}/Platform_Wayland_EGL.cpp | 43 +- .../Platform/Wayland}/Platform_Wayland_EGL.h | 13 +- .../Platform/Wayland}/UnixDomainSocket.cpp | 26 +- .../Platform/Wayland}/UnixDomainSocket.h | 12 +- .../Wayland}/WaylandEGLExtensionProcs.cpp | 96 +- .../Wayland}/WaylandEGLExtensionProcs.h | 19 +- .../Wayland}/WaylandEnvironmentUtils.cpp | 44 +- .../Wayland}/WaylandEnvironmentUtils.h | 11 +- .../Platform/Wayland}/Window_Wayland.cpp | 32 +- .../Platform/Wayland}/Window_Wayland.h | 15 +- .../internal/Platform/Wayland}/WlContext.h | 15 +- .../Platform_Wayland_Shell_EGL_ES_3_0.cpp | 10 +- .../Platform_Wayland_Shell_EGL_ES_3_0.h | 9 +- .../Wayland/WlShell}/Window_Wayland_Shell.cpp | 19 +- .../Wayland/WlShell}/Window_Wayland_Shell.h | 11 +- .../Platform/Windows}/Context_WGL.cpp | 66 +- .../internal/Platform/Windows}/Context_WGL.h | 31 +- .../Platform/Windows}/HiddenWindow.cpp | 6 +- .../internal/Platform/Windows}/HiddenWindow.h | 12 +- .../Windows}/Platform_Windows_WGL.cpp | 91 +- .../Platform/Windows/Platform_Windows_WGL.h | 38 + .../Platform/Windows}/WglExtensions.cpp | 10 +- .../Platform/Windows}/WglExtensions.h | 13 +- .../Platform/Windows}/Window_Windows.cpp | 71 +- .../Platform/Windows}/Window_Windows.h | 15 +- .../Platform/X11}/Platform_X11_EGL.cpp | 8 +- .../internal/Platform/X11}/Platform_X11_EGL.h | 11 +- .../internal/Platform/X11}/Window_X11.cpp | 108 +- .../internal/Platform/X11}/Window_X11.h | 33 +- .../internal/Platform/iOS/Platform_iOS_EGL.h | 25 + .../internal/Platform/iOS/Platform_iOS_EGL.mm | 34 + .../internal/Platform/iOS/Window_iOS.h | 39 + .../internal/Platform/iOS/Window_iOS.mm | 62 + .../RendererLib}/AsyncEffectUploader.cpp | 27 +- .../RendererLib/AsyncEffectUploader.h | 13 +- .../internal/RendererLib}/BufferLinks.cpp | 4 +- .../internal}/RendererLib/BufferLinks.h | 15 +- .../internal/RendererLib/CMakeLists.txt | 47 + .../internal/RendererLib}/ConstantLogger.cpp | 10 +- .../internal}/RendererLib/ConstantLogger.h | 12 +- .../internal}/RendererLib/DataLinkUtils.h | 13 +- .../DataReferenceLinkCachedScene.cpp | 14 +- .../DataReferenceLinkCachedScene.h | 14 +- .../RendererLib}/DataReferenceLinkManager.cpp | 18 +- .../RendererLib/DataReferenceLinkManager.h | 9 +- .../internal/RendererLib}/DisplayBundle.cpp | 16 +- .../internal}/RendererLib/DisplayBundle.h | 39 +- .../internal/RendererLib}/DisplayConfig.cpp | 41 +- .../internal}/RendererLib/DisplayConfig.h | 61 +- .../RendererLib}/DisplayController.cpp | 34 +- .../internal}/RendererLib/DisplayController.h | 17 +- .../RendererLib}/DisplayDispatcher.cpp | 39 +- .../internal}/RendererLib/DisplayDispatcher.h | 25 +- .../RendererLib}/DisplayEventHandler.cpp | 22 +- .../RendererLib/DisplayEventHandler.h | 15 +- .../internal/RendererLib}/DisplaySetup.cpp | 10 +- .../internal}/RendererLib/DisplaySetup.h | 36 +- .../internal/RendererLib}/DisplayThread.cpp | 12 +- .../internal}/RendererLib/DisplayThread.h | 17 +- .../EmbeddedCompositingManager.cpp | 18 +- .../RendererLib/EmbeddedCompositingManager.h | 11 +- .../internal/RendererLib/Enums}/EKeyCode.h | 133 +- .../internal/RendererLib/Enums/EKeyEvent.h | 24 +- .../internal/RendererLib/Enums/EKeyModifier.h | 57 + .../internal/RendererLib/Enums/ELoopMode.h | 9 +- .../internal/RendererLib/Enums/EMouseEvent.h | 39 +- .../RendererLib/Enums}/EResourceStatus.h | 15 +- .../internal/RendererLib/Enums}/ESceneState.h | 15 +- .../RendererLib}/FrameProfilerStatistics.cpp | 17 +- .../RendererLib/FrameProfilerStatistics.h | 57 +- .../internal}/RendererLib/FrameTimer.h | 11 +- .../RendererLib/IRendererResourceManager.h | 31 +- .../RendererLib}/IRendererSceneEventSender.h | 17 +- .../RendererLib/IRendererSceneStateControl.h | 13 +- .../RendererLib/IRendererSceneUpdater.h | 23 +- .../IResourceDeviceHandleAccessor.h | 17 +- .../internal}/RendererLib/IResourceUploader.h | 19 +- .../RendererLib}/IntersectionUtils.cpp | 16 +- .../internal}/RendererLib/IntersectionUtils.h | 10 +- .../internal/RendererLib}/LinkManagerBase.cpp | 28 +- .../internal}/RendererLib/LinkManagerBase.h | 11 +- .../internal/RendererLib}/LoggingDevice.cpp | 99 +- .../internal}/RendererLib/LoggingDevice.h | 56 +- .../PendingSceneResourcesUtils.cpp | 19 +- .../RendererLib/PendingSceneResourcesUtils.h | 13 +- .../PlatformBase}/Context_Base.cpp | 10 +- .../RendererLib/PlatformBase}/Context_Base.h | 13 +- .../PlatformBase}/DeviceResourceMapper.cpp | 6 +- .../PlatformBase}/DeviceResourceMapper.h | 16 +- .../RendererLib/PlatformBase}/Device_Base.cpp | 15 +- .../RendererLib/PlatformBase}/Device_Base.h | 9 +- .../EmbeddedCompositor_Dummy.cpp | 28 +- .../PlatformBase}/EmbeddedCompositor_Dummy.h | 12 +- .../RendererLib/PlatformBase}/GpuResource.h | 11 +- .../PlatformBase}/IndexBufferGPUResource.h | 9 +- .../PlatformBase}/Platform_Base.cpp | 45 +- .../RendererLib/PlatformBase}/Platform_Base.h | 15 +- .../PlatformBase}/RenderBufferGPUResource.h | 32 +- .../PlatformBase}/RenderTargetGpuResource.cpp | 4 +- .../PlatformBase}/RenderTargetGpuResource.h | 9 +- .../PlatformBase}/RendererLimits.cpp | 10 +- .../PlatformBase}/RendererLimits.h | 19 +- .../PlatformBase}/ShaderGPUResource.h | 9 +- .../TextureUploadingAdapter_Base.cpp | 8 +- .../TextureUploadingAdapter_Base.h | 13 +- .../PlatformBase}/VertexArrayGPUResource.h | 9 +- .../RendererLib/PlatformBase}/Window_Base.cpp | 19 +- .../RendererLib/PlatformBase}/Window_Base.h | 10 +- .../PlatformInterface}/IBinaryShaderCache.h | 19 +- .../RendererLib/PlatformInterface}/IContext.h | 11 +- .../RendererLib/PlatformInterface}/IDevice.h | 70 +- .../PlatformInterface}/IDeviceExtension.h | 9 +- .../PlatformInterface}/IDisplayController.h | 22 +- .../IEmbeddedCompositingManager.h | 11 +- .../PlatformInterface}/IEmbeddedCompositor.h | 11 +- .../PlatformInterface}/IPlatform.h | 7 +- .../PlatformInterface}/IPlatformFactory.h | 9 +- .../PlatformInterface}/IRenderBackend.h | 7 +- .../IResourceUploadRenderBackend.h | 9 +- .../ISystemCompositorController.h | 15 +- .../ITextureUploadingAdapter.h | 20 +- .../RendererLib/PlatformInterface}/IWindow.h | 11 +- .../PlatformInterface}/IWindowEventHandler.h | 20 +- .../RendererLib/RamshCommands/AssignScene.cpp | 100 + .../RendererLib/RamshCommands/AssignScene.h | 20 +- .../RamshCommands/CreateOffscreenBuffer.cpp | 59 + .../RamshCommands/CreateOffscreenBuffer.h | 27 + .../RendererLib/RamshCommands/LinkUnlink.cpp | 48 + .../RendererLib/RamshCommands/LinkUnlink.h | 37 + .../RamshCommands}/LogRendererInfo.cpp | 16 +- .../RamshCommands}/LogRendererInfo.h | 18 +- .../RamshCommands}/PrintStatistics.cpp | 8 +- .../RamshCommands}/PrintStatistics.h | 11 +- .../RendererLib/RamshCommands}/Screenshot.cpp | 32 +- .../RendererLib/RamshCommands}/Screenshot.h | 9 +- .../RamshCommands/SetClearColor.cpp | 73 + .../RamshCommands}/SetClearColor.h | 13 +- .../RamshCommands}/SetFrameTimeLimits.cpp | 6 +- .../RamshCommands}/SetFrameTimeLimits.h | 9 +- .../RamshCommands/SetSceneState.cpp | 34 + .../RendererLib/RamshCommands/SetSceneState.h | 26 + .../SetSkippingOfUnmodifiedBuffers.cpp | 8 +- .../SetSkippingOfUnmodifiedBuffers.h | 9 +- ...mCompositorControllerAddSurfaceToLayer.cpp | 8 +- ...temCompositorControllerAddSurfaceToLayer.h | 11 +- ...stemCompositorControllerDestroySurface.cpp | 8 +- ...SystemCompositorControllerDestroySurface.h | 11 +- ...temCompositorControllerListIviSurfaces.cpp | 9 +- ...ystemCompositorControllerListIviSurfaces.h | 11 +- ...ositorControllerRemoveSurfaceFromLayer.cpp | 8 +- ...mpositorControllerRemoveSurfaceFromLayer.h | 11 +- .../SystemCompositorControllerScreenshot.cpp | 6 +- .../SystemCompositorControllerScreenshot.h | 11 +- ...CompositorControllerSetLayerVisibility.cpp | 6 +- ...emCompositorControllerSetLayerVisibility.h | 11 +- ...sitorControllerSetSurfaceDestRectangle.cpp | 6 +- ...positorControllerSetSurfaceDestRectangle.h | 11 +- ...mCompositorControllerSetSurfaceOpacity.cpp | 6 +- ...temCompositorControllerSetSurfaceOpacity.h | 11 +- ...mpositorControllerSetSurfaceVisibility.cpp | 6 +- ...CompositorControllerSetSurfaceVisibility.h | 11 +- .../RamshCommands}/TriggerPickEvent.cpp | 8 +- .../RamshCommands}/TriggerPickEvent.h | 9 +- .../internal/RendererLib}/RenderBackend.cpp | 8 +- .../internal}/RendererLib/RenderBackend.h | 9 +- .../internal/RendererLib}/RenderExecutor.cpp | 44 +- .../internal/RendererLib}/RenderExecutor.h | 13 +- .../RenderExecutorInternalRenderStates.h | 12 +- .../RenderExecutorInternalState.cpp | 14 +- .../RenderExecutorInternalState.h | 37 +- .../RendererLib}/RenderExecutorLogger.cpp | 30 +- .../RendererLib}/RenderExecutorLogger.h | 9 +- .../RendererLib/RenderableComparator.h | 9 +- .../internal/RendererLib}/Renderer.cpp | 99 +- .../renderer/internal}/RendererLib/Renderer.h | 29 +- .../RendererLib}/RendererCachedScene.cpp | 31 +- .../RendererLib/RendererCachedScene.h | 31 +- .../RendererLib}/RendererCommandBuffer.cpp | 4 +- .../RendererLib/RendererCommandBuffer.h | 9 +- .../RendererLib}/RendererCommandExecutor.cpp | 32 +- .../RendererLib/RendererCommandExecutor.h | 11 +- .../RendererLib/RendererCommandUtils.h | 26 +- .../internal}/RendererLib/RendererCommands.h | 83 +- .../internal/RendererLib}/RendererConfig.cpp | 6 +- .../internal}/RendererLib/RendererConfig.h | 11 +- .../internal}/RendererLib/RendererEvent.h | 61 +- .../RendererLib}/RendererEventCollector.cpp | 10 +- .../RendererLib}/RendererEventCollector.h | 11 +- .../RendererLib}/RendererFrameworkLogic.cpp | 24 +- .../RendererLib}/RendererFrameworkLogic.h | 21 +- .../RendererLib/RendererInterruptState.h | 13 +- .../RendererLib}/RendererLogContext.cpp | 11 +- .../RendererLib/RendererLogContext.h | 18 +- .../internal/RendererLib}/RendererLogger.cpp | 132 +- .../internal}/RendererLib/RendererLogger.h | 27 +- .../RendererPeriodicLogSupplier.cpp | 8 +- .../RendererLib/RendererPeriodicLogSupplier.h | 9 +- .../RendererLib}/RendererResourceManager.cpp | 96 +- .../RendererLib/RendererResourceManager.h | 39 +- .../RendererLib}/RendererResourceRegistry.cpp | 8 +- .../RendererLib/RendererResourceRegistry.h | 9 +- .../RendererSceneControlLogic.cpp | 54 +- .../RendererLib/RendererSceneControlLogic.h | 13 +- .../RendererSceneResourceRegistry.cpp | 12 +- .../RendererSceneResourceRegistry.h | 44 +- .../RendererLib}/RendererSceneUpdater.cpp | 151 +- .../RendererLib/RendererSceneUpdater.h | 44 +- .../internal/RendererLib}/RendererScenes.cpp | 4 +- .../internal}/RendererLib/RendererScenes.h | 17 +- .../RendererLib}/RendererStatistics.cpp | 40 +- .../RendererLib/RendererStatistics.h | 27 +- .../internal/RendererLib}/RenderingContext.h | 19 +- .../internal}/RendererLib/RenderingPassInfo.h | 9 +- .../RenderingPassOrderComparator.h | 11 +- .../RendererLib}/ResourceCachedScene.cpp | 29 +- .../RendererLib/ResourceCachedScene.h | 19 +- .../RendererLib/ResourceDescriptor.h | 23 +- .../ResourceUploadRenderBackend.cpp | 8 +- .../RendererLib/ResourceUploadRenderBackend.h | 9 +- .../RendererLib}/ResourceUploader.cpp | 101 +- .../internal}/RendererLib/ResourceUploader.h | 13 +- .../RendererLib}/ResourceUploadingManager.cpp | 64 +- .../RendererLib/ResourceUploadingManager.h | 18 +- .../RendererLib}/SceneDependencyChecker.cpp | 11 +- .../RendererLib}/SceneDependencyChecker.h | 15 +- .../RendererLib}/SceneDisplayTracker.cpp | 8 +- .../RendererLib/SceneDisplayTracker.h | 86 + .../RendererLib}/SceneExpirationMonitor.cpp | 18 +- .../RendererLib/SceneExpirationMonitor.h | 15 +- .../internal/RendererLib}/SceneLinkScene.cpp | 6 +- .../internal}/RendererLib/SceneLinkScene.h | 11 +- .../internal/RendererLib}/SceneLinks.cpp | 4 +- .../internal}/RendererLib/SceneLinks.h | 13 +- .../RendererLib}/SceneLinksManager.cpp | 50 +- .../internal}/RendererLib/SceneLinksManager.h | 15 +- .../RendererLib}/SceneReferenceLogic.cpp | 31 +- .../RendererLib/SceneReferenceLogic.h | 15 +- .../RendererLib/SceneReferenceOwnership.h | 11 +- .../SceneRenderExecutionIterator.h | 9 +- .../RendererLib}/SceneResourceUploader.cpp | 45 +- .../RendererLib/SceneResourceUploader.h | 12 +- .../RendererLib}/SceneStateExecutor.cpp | 39 +- .../RendererLib/SceneStateExecutor.h | 13 +- .../internal/RendererLib}/SceneStateInfo.cpp | 8 +- .../internal}/RendererLib/SceneStateInfo.h | 20 +- .../internal}/RendererLib/StagingInfo.h | 21 +- .../RendererLib}/TextureLinkCachedScene.cpp | 12 +- .../RendererLib/TextureLinkCachedScene.h | 13 +- .../RendererLib}/TextureLinkManager.cpp | 14 +- .../RendererLib/TextureLinkManager.h | 13 +- .../TransformationLinkCachedScene.cpp | 8 +- .../TransformationLinkCachedScene.h | 9 +- .../TransformationLinkManager.cpp | 16 +- .../RendererLib/TransformationLinkManager.h | 13 +- src/renderer/internal/RendererLib/Types.cpp | 29 + .../renderer/internal/RendererLib}/Types.h | 102 +- src/shared-lib/CMakeLists.txt | 76 + {client/logic/tools => tests}/CMakeLists.txt | 12 +- .../benchmarks}/CMakeLists.txt | 4 +- tests/benchmarks/logic/CMakeLists.txt | 19 + .../benchmarks/logic}/animation.cpp | 30 +- tests/benchmarks/logic/benchmarksetup.h | 25 + .../benchmarks/logic}/compile_lua.cpp | 9 +- .../benchmarks/logic}/getproperty.cpp | 17 +- .../benchmarks/logic}/links.cpp | 28 +- .../benchmarks/logic}/property.cpp | 13 +- .../benchmarks/logic}/serialization.cpp | 52 +- .../benchmarks/logic}/update.cpp | 30 +- tests/integration/CMakeLists.txt | 18 + .../logic-viewer-tests/CMakeLists.txt | 34 + .../LogicViewerAppTest.cpp | 123 +- .../res/ALogicViewerApp_clearColor.png | Bin .../res/ALogicViewerApp_clearColorCmdLine.png | Bin .../res/ALogicViewerApp_red.png | Bin .../res/ALogicViewerApp_red_500x700.png | Bin .../res/ALogicViewerApp_white.png | Bin .../res/ALogicViewerApp_yellow.png | Bin .../render-backend-tests}/CMakeLists.txt | 12 +- .../render-backend-tests}/PlatformTest.cpp | 48 +- .../ShaderUploadTest.cpp | 263 +- .../render-backend-tests}/main.cpp | 8 +- .../renderer-test-utils}/CMakeLists.txt | 11 +- .../ReadPixelCallbackHandler.h | 32 + .../RendererAndSceneTestEventHandler.h | 43 +- .../renderer-test-utils/RendererTestUtils.cpp | 223 + .../renderer-test-utils/RendererTestUtils.h | 90 + .../renderer-tests}/CMakeLists.txt | 144 +- .../DmaOffscreenBufferRenderingTests.h | 44 + .../DmaOffscreenBufferTests.cpp | 574 ++ .../DmaOffscreenBufferTests.h | 106 + .../main.cpp | 8 +- .../EmbeddedCompositingTestMessages.h | 8 +- .../EmbeddedCompositingTestsFramework.cpp | 95 +- .../EmbeddedCompositingTestsFramework.h | 36 +- .../IEmbeddedCompositingTest.h | 9 +- .../NamedPipe.h | 39 +- .../TestForkerApplication.cpp | 4 +- .../TestForkerApplication.h | 7 +- .../TestForkingController.cpp | 13 +- .../TestForkingController.h | 13 +- .../TestSignalHandler.cpp | 10 +- .../TestSignalHandler.h | 7 +- .../WaylandOutputTestParams.h | 7 +- .../TestCases/EmbeddedCompositingTests.h | 10 +- .../EmbeddedCompositingTestsWithFD.cpp | 14 +- .../EmbeddedCompositingTestsWithFD.h | 11 +- .../MultiDisplayStreamTextureTests.cpp | 18 +- .../MultiDisplayStreamTextureTests.h | 11 +- .../MultiSceneStreamTextureTests.cpp | 11 +- .../TestCases/MultiSceneStreamTextureTests.h | 7 +- .../TestCases/MultiStreamTextureTests.cpp | 13 +- .../TestCases/MultiStreamTextureTests.h | 7 +- ...ffscreenBuffersWithStreamTexturesTests.cpp | 6 +- .../OffscreenBuffersWithStreamTexturesTests.h | 7 +- .../TestCases/SharedMemoryBufferTests.cpp | 97 +- .../TestCases/SharedMemoryBufferTests.h | 12 +- .../TestCases/SingleStreamTextureTests.cpp | 48 +- .../TestCases/SingleStreamTextureTests.h | 13 +- .../TestCases/StreamBufferTests.cpp | 24 +- .../TestCases/StreamBufferTests.h | 7 +- .../StreamTextureRendererEventTests.cpp | 16 +- .../StreamTextureRendererEventTests.h | 7 +- ...landApplicationWithRamsesRendererTests.cpp | 6 +- ...aylandApplicationWithRamsesRendererTests.h | 7 +- .../TestCases/WaylandOutputTests.cpp | 46 +- .../TestCases/WaylandOutputTests.h | 15 +- .../TestCases/main.cpp | 22 +- .../TestWaylandApplication/ETriangleColor.h | 7 +- .../OpenGLTriangleDrawer.cpp | 10 +- .../OpenGLTriangleDrawer.h | 13 +- .../TestWaylandApplication/SHMBuffer.cpp | 11 +- .../TestWaylandApplication/SHMBuffer.h | 9 +- .../SHMTriangleDrawer.cpp | 6 +- .../SHMTriangleDrawer.h | 11 +- .../TestApplicationShellSurfaceId.h | 9 +- .../TestApplicationSurfaceId.h | 9 +- .../TestWaylandApplication.cpp | 130 +- .../TestWaylandApplication.h | 22 +- .../TestWaylandApplication/WaylandHandler.cpp | 120 +- .../TestWaylandApplication/WaylandHandler.h | 29 +- .../ExternalWindowTests.cpp | 103 + .../RendererLifecycleTests.cpp | 761 ++- .../RendererLifecycleTests.h | 17 +- .../renderer-lifecycle-tests}/main.cpp | 4 +- .../renderer-test-framework}/IRendererTest.h | 20 +- .../RendererTestsFramework.cpp | 626 ++ .../RendererTestsFramework.h | 158 + .../RenderingTestCase.h | 39 + .../renderer-test-framework}/TestRenderer.cpp | 119 +- .../renderer-test-framework/TestRenderer.h | 103 + .../renderer-test-framework/TestScenes.cpp | 67 + .../renderer-test-framework/TestScenes.h | 108 + .../TestScenesAndRenderer.cpp | 27 +- .../TestScenesAndRenderer.h | 31 +- .../rendering-tests/DataLinkingTests.cpp | 400 ++ .../rendering-tests/DataLinkingTests.h | 71 + .../rendering-tests/DisplayRenderingTests.cpp | 393 ++ .../rendering-tests/DisplayRenderingTests.h | 61 + .../rendering-tests/EffectRenderingTests.cpp | 65 + .../rendering-tests/EffectRenderingTests.h | 38 + .../InterruptibleOffscreenBufferLinkTests.cpp | 297 + .../InterruptibleOffscreenBufferLinkTests.h | 46 + .../OffscreenBufferLinkTests.cpp | 319 + .../OffscreenBufferLinkTests.h | 63 + .../RenderPassRenderingTests.cpp | 112 + .../RenderPassRenderingTests.h | 35 + .../RenderTargetRenderingTests.cpp | 142 + .../RenderTargetRenderingTests.h | 67 + .../rendering-tests/RenderingTests.h | 74 + .../rendering-tests/SceneRenderingTests.cpp | 418 ++ .../rendering-tests/SceneRenderingTests.h | 159 + .../rendering-tests/TextureRenderingTests.cpp | 310 + .../rendering-tests/TextureRenderingTests.h | 95 + .../renderer-tests/rendering-tests}/main.cpp | 8 +- .../res/ARendererDisplays_Black.PNG | Bin .../res/ARendererDisplays_HiddenScene.PNG | Bin .../res/ARendererDisplays_ModifiedScenes1.PNG | Bin .../res/ARendererDisplays_ModifiedScenes2.PNG | Bin .../res/ARendererDisplays_RenderTarget.PNG | Bin .../res/ARendererDisplays_TwoScenes.PNG | Bin ...ndererDisplays_TwoScenesInverseOrdered.PNG | Bin .../ARendererDisplays_TwoScenesOrdered.PNG | Bin .../ARendererDisplays_UnpublishedScene.PNG | Bin .../res/ARendererInstance_AfterLoadSave.PNG | Bin .../ARendererInstance_DynamicResources.PNG | Bin .../res/ARendererInstance_SimpleText.PNG | Bin .../res/ARendererInstance_Three_Triangles.PNG | Bin .../ARendererInstance_Triangles_reordered.PNG | Bin .../res/ARendererInstance_YellowText.PNG | Bin .../AnimatedTriangleScene_AnimatedScene.PNG | Bin .../res/AntiAliasingScene_MSAAx4.PNG | Bin .../res/ArrayInputScene_ArrayInputInt32.PNG | Bin ...InputScene_ArrayInputInt32DynamicIndex.PNG | Bin .../res/ArrayInputScene_ArrayInputVec4.PNG | Bin .../res/BlitPassTest_BlitsColorBuffer.PNG | Bin .../res/BlitPassTest_BlitsDepthBuffer.PNG | Bin .../BlitPassTest_BlitsDepthStencilBuffer.PNG | Bin .../res/BlitPassTest_BlitsSubregion.PNG | Bin .../res/CameraData_FrustumLinked.PNG | Bin .../res/CameraData_NoLinks.PNG | Bin .../res/CameraData_ViewportLinked.PNG | Bin .../res/CubeTextureScene_CubeMap.PNG | Bin .../res/CubeTextureScene_CubeMapFloat.PNG | Bin .../res/CubeTextureScene_CubeMapSwizzled.PNG | Bin .../DataBufferScene_EquilateralTriangle.PNG | Bin .../res/DataBufferScene_RedTriangle.PNG | Bin .../DataBufferScene_RedTriangleInverted.PNG | Bin .../res/DataLinkTest_AllLinksDisabled.PNG | Bin .../res/DataLinkTest_ConfidenceMultiLink.PNG | Bin .../DataLinkTest_ConsumerLinkedToProvider.PNG | Bin ...inkTest_ConsumerLinkedToProviderNested.PNG | Bin ...inkTest_LinkOverridesComsumerTransform.PNG | Bin .../res/DataLinkTest_LinkRemoved.PNG | Bin .../res/DataLinkTest_Linked.PNG | Bin .../res/DataLinkTest_NoLinks.PNG | Bin .../res/DataLinkTest_RemovedConsumer.PNG | Bin .../res/DataLinkTest_RemovedProvider.PNG | Bin .../res/Display_SetClearColor.PNG | Bin .../res/DistributedScene_UnpublishedScene.PNG | Bin ...DmaOffscreenBufferTest_BufferReadWrite.PNG | Bin .../DmaOffscreenBufferTest_BufferWrite.PNG | Bin ...ffscreenBufferTest_BufferWrite_128x256.PNG | Bin ...OffscreenBufferTest_BufferWrite_128x64.PNG | Bin ...ffscreenBufferTest_BufferWrite_256x256.PNG | Bin .../res/EC_BlueTriangleStreamTexture.PNG | Bin .../EC_BlueTriangleStreamTexture_SmallRes.PNG | Bin ...angleStreamTexture_WithTexCoordsOffset.PNG | Bin ...TextureInASceneMappedToOffscreenBuffer.PNG | Bin .../res/EC_Fallback1_Left_Fallback1_Right.PNG | Bin ...Left_Fallback1_Swizzled_Right_Swizzled.PNG | Bin .../res/EC_Fallback1_Left_Fallback2_Right.PNG | Bin .../EC_Fallback1_Left_RedTriangle_Right.PNG | Bin .../EC_Fallback1_Left_WhiteTriangle_Right.PNG | Bin .../res/EC_FallbackTexture_1.PNG | Bin ...EC_FallbackTexture_WithTexCoordsOffset.PNG | Bin .../res/EC_GrayTriangleStreamTexture.PNG | Bin .../res/EC_RedTriangleStreamTexture.PNG | Bin .../EC_RedTriangleStreamTexture_SmallRes.PNG | Bin ...EC_RedTriangle_Left_BlueTriangle_Right.PNG | Bin .../EC_RedTriangle_Left_Fallback1_Right.PNG | Bin ...Triangle_Left_Fallback1_Swizzled_Right.PNG | Bin .../EC_RedTriangle_Left_RedTriangle_Right.PNG | Bin ...edTriangle_Left_ShMemRedTriangle_Right.PNG | Bin .../res/EC_ShMemBlueTriangle.PNG | Bin ...ShMemBlueTriangle_Left_Fallback1_Right.PNG | Bin ...Triangle_Left_Fallback1_Swizzled_Right.PNG | Bin ...MemBlueTriangle_Left_RedTriangle_Right.PNG | Bin ...ueTriangle_Left_ShMemRedTriangle_Right.PNG | Bin ...Triangle_Left_ShMemWhiteTriangle_Right.PNG | Bin ...Triangle_Left_ShMemWhiteTriangle_Right.PNG | Bin .../res/EC_ShMemRedTriangle.PNG | Bin ...Triangle_Left_Fallback1_Swizzled_Right.PNG | Bin ...hMemRedTriangle_Left_RedTriangle_Right.PNG | Bin ...edTriangle_Left_ShMemRedTriangle_Right.PNG | Bin .../res/EC_ShMemWhiteTriangle.PNG | Bin .../res/EC_WhiteTriangleStreamTexture.PNG | Bin .../res/GeometryInstanceScene_Instancing.PNG | Bin ...stanceScene_InstancingAndNotInstancing.PNG | Bin ...ometryShaderScene_PointsInLineStripOut.PNG | Bin .../GeometryShaderScene_PointsInPointsOut.PNG | Bin ...ryShaderScene_PointsInTriangleStripOut.PNG | Bin ...ometryShaderScene_TrianglesInPointsOut.PNG | Bin ...haderScene_TrianglesInTriangleStripOut.PNG | Bin ...edTrianglesScene_AllTrianglesInvisible.PNG | Bin ...chicalRedTrianglesScene_DeleteMeshNode.PNG | Bin ...chicalRedTrianglesScene_RotateAndScale.PNG | Bin ...dTrianglesScene_SomeTrianglesInvisible.PNG | Bin ...exArray32BitScene_NoOffset16BitIndices.PNG | Bin ...exArray32BitScene_NoOffset32BitIndices.PNG | Bin ...ndexArray32BitScene_Offset16BitIndices.PNG | Bin ...ndexArray32BitScene_Offset32BitIndices.PNG | Bin ...ptibleOffscreenBufferLinkTest_FBState0.PNG | Bin ...ptibleOffscreenBufferLinkTest_FBState1.PNG | Bin ...ptibleOffscreenBufferLinkTest_FBState2.PNG | Bin ...OffscreenBufferLinkTest_FBState3Linked.PNG | Bin ...creenBufferLinkTest_OneOBWithTwoScenes.PNG | Bin ...OffscreenBufferLinkTest_ThreeTriangles.PNG | Bin ...ffscreenBufferLinkTest_ThreeTriangles2.PNG | Bin ...ffscreenBufferLinkTest_ThreeTriangles3.PNG | Bin ...enBufferLinkTest_ThreeTriangles_SameOB.PNG | Bin ...creenBufferLinkTest_TrianglesReordered.PNG | Bin ...reenBufferLinkTest_TrianglesReordered2.PNG | Bin ...fferLinkTest_TrianglesReordered_SameOB.PNG | Bin ...nBufferLinkTest_TwoOBsEachWithOneScene.PNG | Bin .../MultiLanguageScene_MultiLanguageText.PNG | Bin ...MultiRenderTarget_ClearTwoColorBuffers.PNG | Bin ...erTarget_ColorBufferWrittenByTwoPasses.PNG | Bin ...enderTarget_DepthBufferUsedByTwoPasses.PNG | Bin .../res/MultiRenderTarget_DepthRead.PNG | Bin ...ultiRenderTarget_OneColorBufferWritten.PNG | Bin .../res/MultiRenderTarget_TwoColorBuffers.PNG | Bin .../res/MultiTypeLinkTest_Linked.PNG | Bin .../res/MultiTypeLinkTest_NoLinks.PNG | Bin ...MultipleGeometryScene_MultipleGeometry.PNG | Bin ...TriangleScene_EulerRotationConventions.PNG | Bin ...ultipleTrianglesScene_AdditiveBlending.PNG | Bin .../MultipleTrianglesScene_AlphaBlending.PNG | Bin ...ultipleTrianglesScene_BlendingConstant.PNG | Bin ...rianglesScene_BlendingDstColorAndAlpha.PNG | Bin ...pleTrianglesScene_CameraTransformation.PNG | Bin .../res/MultipleTrianglesScene_ColorMask.PNG | Bin .../res/MultipleTrianglesScene_DepthFunc.PNG | Bin ...TrianglesScene_DepthFunc_NoDepthBuffer.PNG | Bin .../res/MultipleTrianglesScene_DrawMode.PNG | Bin .../MultipleTrianglesScene_FaceCulling.PNG | Bin ...tipleTrianglesScene_OrthographicCamera.PNG | Bin ...ltipleTrianglesScene_PerspectiveCamera.PNG | Bin ...leTrianglesScene_RenderingOrderChanged.PNG | Bin .../MultipleTrianglesScene_ScissorTest.PNG | Bin .../MultipleTrianglesScene_StencilTest_1.PNG | Bin ...e_StencilTest_1_NoDepthOrStencilBuffer.PNG | Bin ...lesScene_StencilTest_1_NoStencilBuffer.PNG | Bin .../MultipleTrianglesScene_StencilTest_2.PNG | Bin .../MultipleTrianglesScene_StencilTest_3.PNG | Bin ...ultipleTrianglesScene_Subimages_middle.PNG | Bin ...ipleTrianglesScene_SubtractiveBlending.PNG | Bin .../MultipleTrianglesScene_ThreeTriangles.PNG | Bin ...ltipleTrianglesScene_ThreeTrianglesRed.PNG | Bin .../res/OffscreenBufferLinkTest_Black.PNG | Bin .../res/OffscreenBufferLinkTest_BlackOB.PNG | Bin ...ffscreenBufferLinkTest_BufferDestroyed.PNG | Bin .../res/OffscreenBufferLinkTest_DepthTest.PNG | Bin .../res/OffscreenBufferLinkTest_Linked.PNG | Bin .../res/OffscreenBufferLinkTest_Linked2.PNG | Bin ...creenBufferLinkTest_Linked2_OB_Content.PNG | Bin ...nBufferLinkTest_LinkedCustomClearColor.PNG | Bin ...OffscreenBufferLinkTest_LinkedFinished.PNG | Bin ...ffscreenBufferLinkTest_LinkedFinished2.PNG | Bin ...screenBufferLinkTest_LinkedInterrupted.PNG | Bin ...creenBufferLinkTest_LinkedInterrupted2.PNG | Bin ...creenBufferLinkTest_LinkedTwoConsumers.PNG | Bin ...LinkTest_LinkedTwoConsumers_OB_Content.PNG | Bin ...ffscreenBufferLinkTest_LinkedTwoScenes.PNG | Bin ...ferLinkTest_LinkedTwoScenes_OB_Content.PNG | Bin ...screenBufferLinkTest_Linked_OB_Content.PNG | Bin .../OffscreenBufferLinkTest_MSAABuffer.PNG | Bin .../OffscreenBufferLinkTest_StencilTest.PNG | Bin .../res/OffscreenBufferLinkTest_Unlinked.PNG | Bin ...creenBufferLinkTest_UnlinkedMSAABuffer.PNG | Bin .../renderer-tests}/res/ReferencedScenes.PNG | Bin .../res/RenderBuffer_MsaaSampleCount2Blit.PNG | Bin .../res/RenderBuffer_MsaaSampleCount4Blit.PNG | Bin ...enderBuffer_MsaaSampleCount4TexelFetch.PNG | Bin ...rBuffer_OneColorBufferNoDepthOrStencil.PNG | Bin ...OneColorBufferWithWriteOnlyDepthBuffer.PNG | Bin ...rBufferWithWriteOnlyDepthStencilBuffer.PNG | Bin .../res/RenderBuffer_ReadWriteDepthBuffer.PNG | Bin 0 -> 218 bytes .../res/RenderPassClear_Color.PNG | Bin .../res/RenderPassClear_ColorDepth.PNG | Bin .../res/RenderPassClear_ColorStencil.PNG | Bin .../res/RenderPassClear_ColorStencilDepth.PNG | Bin .../res/RenderPassClear_Depth.PNG | Bin .../res/RenderPassClear_None.PNG | Bin .../res/RenderPassClear_Stencil.PNG | Bin .../res/RenderPassClear_StencilDepth.PNG | Bin .../res/RenderPassOnce_Initial.PNG | Bin .../res/RenderPassOnce_Retriggered.PNG | Bin .../res/RenderPassScene_LeftRightViewport.PNG | Bin .../RenderPassScene_MeshInMultiplePasses.PNG | Bin .../res/RenderPassScene_MeshNotInPass.PNG | Bin .../res/RenderPassScene_OneMeshPerPass.PNG | Bin ...ssScene_PassesWithDifferentRenderOrder.PNG | Bin ...nderTargetScene_OrthographicProjection.PNG | Bin ...enderTargetScene_PerspectiveProjection.PNG | Bin .../res/RenderTargetScene_Red.PNG | Bin .../res/RenderTargetScene_RedGreen.PNG | Bin .../res/RenderTargetScene_RedGreenBlue.PNG | Bin ...RenderTargetScene_RedGreenBlue_NoAlpha.PNG | Bin .../res/ShaderTestScene_BoolUniform.PNG | Bin .../res/ShaderTestScene_Discard.PNG | Bin .../res/ShaderTestScene_MultiStageUniform.PNG | Bin .../res/ShaderTestScene_OptimizedInput.PNG | Bin .../res/ShaderTestScene_StructUniform.PNG | Bin .../res/ShaderTestScene_TextureSize.PNG | Bin .../res/ShaderTestScene_TexturedQuad.PNG | Bin ...SingleAppearanceScene_ChangeAppearance.PNG | Bin .../SingleAppearanceScene_GreenTriangles.PNG | Bin .../SingleAppearanceScene_RedTriangles.PNG | Bin .../res/TextScene_ChangedText.PNG | Bin .../res/TextScene_DeletedTextsAndNode.PNG | Bin .../res/TextScene_FontCascade.PNG | Bin .../res/TextScene_ForceAutoHinting.PNG | Bin .../renderer-tests}/res/TextScene_Shaping.PNG | Bin .../res/TextScene_SimpleText.PNG | Bin .../res/TextScene_SmallText.PNG | Bin .../res/TextScene_VerticalOffset.PNG | Bin .../res/TextScene_YellowText.PNG | Bin ...ropicTextureFilteringScene_Anisotropic.PNG | Bin ...CompressedMipMapScene_CompressedMipMap.PNG | Bin .../Texture2DFormatScene_ASTC_RGBA_4x4.PNG | Bin ...xture2DFormatScene_ASTC_SRGB_ALPHA_4x4.PNG | Bin .../res/Texture2DFormatScene_ETC2RGB.PNG | Bin .../res/Texture2DFormatScene_ETC2RGBA.PNG | Bin .../res/Texture2DFormatScene_FloatRG.PNG | Bin .../res/Texture2DFormatScene_FloatRGB.PNG | Bin .../res/Texture2DFormatScene_FloatRed.PNG | Bin .../res/Texture2DFormatScene_R8.PNG | Bin .../res/Texture2DFormatScene_RG8.PNG | Bin .../res/Texture2DFormatScene_RGB565.PNG | Bin .../res/Texture2DFormatScene_RGB8.PNG | Bin .../res/Texture2DFormatScene_RGBA4.PNG | Bin .../res/Texture2DFormatScene_RGBA5551.PNG | Bin .../res/Texture2DFormatScene_RGBA8.PNG | Bin .../res/Texture2DFormatScene_SRGB8.PNG | Bin .../res/Texture2DFormatScene_SRGB8_ALPHA8.PNG | Bin .../Texture2DFormatScene_Swizzled_BGR8.PNG | Bin .../Texture2DFormatScene_Swizzled_BGRA8.PNG | Bin ...2DFormatScene_Swizzled_Luminance_Alpha.PNG | Bin ...ateMipMapScene_GenerateMultipleMipMaps.PNG | Bin ...nerateMipMapScene_GenerateSingleMipMap.PNG | Bin .../res/Texture2DSamplingScene_Bilinear.PNG | Bin ...ure2DSamplingScene_BilinearWithMipMaps.PNG | Bin ...ure2DSamplingScene_MinLinearMagNearest.PNG | Bin ...ure2DSamplingScene_MinNearestMagLinear.PNG | Bin .../res/Texture2DSamplingScene_Nearest.PNG | Bin ...ture2DSamplingScene_NearestWithMipMaps.PNG | Bin .../res/Texture2DSamplingScene_Trilinear.PNG | Bin .../res/Texture3DScene_4Slices.PNG | Bin .../TextureAddressScene_AllAddressModes.PNG | Bin .../res/TextureBuffer_PartialUpdate.PNG | Bin .../res/TextureBuffer_PartialUpdate1.PNG | Bin 0 -> 256 bytes .../res/TextureBuffer_PartialUpdate2.PNG | Bin 0 -> 253 bytes .../res/TextureBuffer_PartialUpdateMipMap.PNG | Bin .../TextureBuffer_PartialUpdateMipMap_RG8.PNG | Bin .../res/TextureBuffer_RGBA8_OneMip.PNG | Bin .../res/TextureBuffer_RGBA8_OneMipGreen.PNG | Bin .../res/TextureBuffer_RGBA8_OneMipRed.PNG | Bin .../res/TextureBuffer_RGBA8_ThreeMips.PNG | Bin ...ropicTextureFilteringScene_Anisotropic.PNG | Bin .../res/TextureLinkTest_Linked.PNG | Bin .../res/TextureLinkTest_NoLinks.PNG | Bin ...TextureLinkTest_ProviderTextureChanged.PNG | Bin .../res/TextureSamplerScene_Changed.PNG | Bin .../res/TextureSamplerScene_ChangedBlue.PNG | Bin .../res/TextureSamplerScene_Initial.PNG | Bin .../res/VisibilityScene_On.PNG | Bin .../res/VisibilityScene_Partial.PNG | Bin .../res/binary-test-shader.frag | 0 .../res/binary-test-shader.vert | 0 ...mses-renderer-test-custom-text-effect.frag | 0 ...mses-renderer-test-custom-text-effect.vert | 0 .../resource-stress-tests}/CMakeLists.txt | 11 +- .../DynamicQuad_Base.cpp | 39 +- .../resource-stress-tests}/DynamicQuad_Base.h | 17 +- .../DynamicQuad_OffscreenRenderTarget.cpp | 44 +- .../DynamicQuad_OffscreenRenderTarget.h | 7 +- .../DynamicQuad_Resources.cpp | 26 +- .../DynamicQuad_Resources.h | 7 +- .../DynamicQuad_SceneResources.cpp | 43 +- .../DynamicQuad_SceneResources.h | 7 +- .../ResourceStressTestScene.cpp | 49 +- .../ResourceStressTestScene.h | 14 +- .../ResourceStressTestSceneArray.cpp | 6 +- .../ResourceStressTestSceneArray.h | 9 +- .../ResourceStressTests.cpp | 18 +- .../ResourceStressTests.h | 39 +- .../resource-stress-tests}/ScreenspaceQuad.h | 13 +- .../StressTestRenderer.cpp | 32 +- .../StressTestRenderer.h | 49 + .../resource-stress-tests}/main.cpp | 7 +- .../integration/smoke-tests}/CMakeLists.txt | 0 .../ramses-local-client-test/CMakeLists.txt | 5 +- .../ramses-local-client-test}/main.cpp | 105 +- .../ramses-test-client/CMakeLists.txt | 7 +- .../smoke-tests/ramses-test-client}/main.cpp | 65 +- .../test-content}/AntiAliasingScene.cpp | 49 +- .../test-content}/ArrayBufferScene.cpp | 111 +- .../test-content}/ArrayInputScene.cpp | 37 +- .../test-content}/ArrayResourceScene.cpp | 71 +- .../test-content}/BlitPassScene.cpp | 42 +- .../integration/test-content}/CMakeLists.txt | 13 +- .../test-content}/CameraDataLinkScene.cpp | 30 +- .../CommonRenderBufferTestScene.cpp | 47 +- .../test-content}/CubeTextureScene.cpp | 102 +- .../test-content}/CustomShaderTestScene.cpp | 41 +- .../test-content}/DataLinkScene.cpp | 20 +- .../test-content}/EmbeddedCompositorScene.cpp | 73 +- .../test-content}/FileLoadingScene.cpp | 95 +- .../test-content}/GeometryInstanceScene.cpp | 65 +- .../test-content}/GeometryShaderScene.cpp | 60 +- .../HierarchicalRedTrianglesScene.cpp | 16 +- .../test-content}/IndexArray32BitScene.cpp | 37 +- .../test-content}/IntegrationScene.cpp | 18 +- .../integration/test-content}/Line.cpp | 51 +- .../test-content}/MsaaRenderBufferScene.cpp | 80 +- .../test-content}/MultiLanguageTextScene.cpp | 31 +- .../MultiTransformationLinkScene.cpp | 18 +- .../test-content}/MultiTriangleGeometry.cpp | 57 +- .../test-content}/MultiTypeLinkScene.cpp | 46 +- .../test-content}/MultipleGeometryScene.cpp | 45 +- .../MultipleRenderTargetScene.cpp | 68 +- .../test-content}/MultipleTrianglesScene.cpp | 118 +- .../test-content/RenderBufferScene.cpp | 178 + .../test-content}/RenderPassClearScene.cpp | 67 +- .../test-content}/RenderPassOnceScene.cpp | 55 +- .../test-content}/RenderPassScene.cpp | 26 +- .../test-content}/RenderTargetScene.cpp | 59 +- .../test-content}/SceneFromPath.cpp | 4 +- .../test-content}/ShaderTestScene.cpp | 119 +- .../test-content}/SingleAppearanceScene.cpp | 42 +- .../test-content/StreamTextureScene.cpp | 156 + .../TestScenes/AntiAliasingScene.h | 7 +- .../TestScenes/ArrayBufferScene.h | 15 +- .../TestScenes/ArrayInputScene.h | 13 +- .../TestScenes/ArrayResourceScene.h | 13 +- .../test-content}/TestScenes/BlitPassScene.h | 7 +- .../TestScenes/CameraDataLinkScene.h | 11 +- .../TestScenes/CommonRenderBufferTestScene.h | 9 +- .../TestScenes/CubeTextureScene.h | 15 +- .../TestScenes/CustomShaderTestScene.h | 13 +- .../test-content}/TestScenes/DataLinkScene.h | 11 +- .../TestScenes/EmbeddedCompositorScene.h | 11 +- .../TestScenes/FileLoadingScene.h | 17 +- .../TestScenes/GeometryInstanceScene.h | 20 +- .../TestScenes/GeometryShaderScene.h | 17 +- .../HierarchicalRedTrianglesScene.h | 9 +- .../TestScenes/IndexArray32BitScene.h | 15 +- .../TestScenes/IntegrationScene.h | 14 +- .../test-content}/TestScenes/Line.h | 20 +- .../TestScenes/MsaaRenderBufferScene.h | 12 +- .../TestScenes/MultiLanguageTextScene.h | 6 +- .../TestScenes/MultiTransformationLinkScene.h | 15 +- .../TestScenes/MultiTriangleGeometry.h | 18 +- .../TestScenes/MultiTypeLinkScene.h | 13 +- .../TestScenes/MultipleGeometryScene.h | 15 +- .../TestScenes/MultipleRenderTargetScene.h | 9 +- .../TestScenes/MultipleTrianglesScene.h | 47 +- .../TestScenes/RenderBufferScene.h | 19 +- .../TestScenes/RenderPassClearScene.h | 19 +- .../TestScenes/RenderPassOnceScene.h | 7 +- .../TestScenes/RenderPassScene.h | 16 +- .../TestScenes/RenderTargetScene.h | 7 +- .../test-content}/TestScenes/SceneFromPath.h | 9 +- .../TestScenes/ShaderTestScene.h | 11 +- .../TestScenes/SingleAppearanceScene.h | 17 +- .../TestScenes/StreamTextureScene.h | 13 +- .../test-content}/TestScenes/TextScene.h | 6 +- .../test-content}/TestScenes/TextScene_Base.h | 14 +- ...exture2DAnisotropicTextureFilteringScene.h | 15 +- .../Texture2DCompressedMipMapScene.h | 25 +- .../TestScenes/Texture2DFormatScene.h | 15 +- .../TestScenes/Texture2DGenerateMipMapScene.h | 21 +- .../TestScenes/Texture2DSamplingScene.h | 25 +- .../test-content}/TestScenes/Texture3DScene.h | 7 +- .../TestScenes/TextureAddressScene.h | 9 +- .../TestScenes/TextureBufferScene.h | 15 +- ...tureCubeAnisotropicTextureFilteringScene.h | 15 +- .../TestScenes/TextureLinkScene.h | 13 +- .../TestScenes/TextureSamplerScene.h | 7 +- .../TestScenes/TransformationLinkScene.h | 13 +- .../test-content}/TestScenes/Triangle.h | 18 +- .../TestScenes/TriangleAppearance.h | 22 +- .../TestScenes/TriangleGeometry.h | 24 +- .../TestScenes/VisibilityScene.h | 7 +- .../test-content}/TestStepCommand.cpp | 6 +- .../test-content}/TestStepCommand.h | 19 +- .../integration/test-content}/TextScene.cpp | 34 +- .../test-content}/TextScene_Base.cpp | 6 +- ...ture2DAnisotropicTextureFilteringScene.cpp | 46 +- .../Texture2DCompressedMipMapScene.cpp | 66 +- .../test-content}/Texture2DFormatScene.cpp | 45 +- .../Texture2DGenerateMipMapScene.cpp | 41 +- .../test-content}/Texture2DSamplingScene.cpp | 51 +- .../test-content}/Texture3DScene.cpp | 39 +- .../test-content}/TextureAddressScene.cpp | 37 +- .../test-content}/TextureBufferScene.cpp | 80 +- ...reCubeAnisotropicTextureFilteringScene.cpp | 64 +- .../test-content}/TextureLinkScene.cpp | 85 +- .../test-content}/TextureSamplerScene.cpp | 53 +- .../test-content}/TransformationLinkScene.cpp | 26 +- .../integration/test-content}/Triangle.cpp | 25 +- .../test-content/TriangleAppearance.cpp | 83 + .../test-content}/TriangleGeometry.cpp | 34 +- .../test-content}/VisibilityScene.cpp | 24 +- .../res/ramses-test-client-3d-textured.frag | 0 .../res/ramses-test-client-3d-textured.vert | 0 .../res/ramses-test-client-Arimo-Regular.ttf | Bin .../ramses-test-client-DroidKufi-Regular.ttf | Bin .../res/ramses-test-client-Roboto-Bold.ttf | Bin .../res/ramses-test-client-Roboto-Light.ttf | Bin .../ramses-test-client-WenQuanYiMicroHei.ttf | Bin .../res/ramses-test-client-arrays.frag | 0 .../res/ramses-test-client-arrays.vert | 0 .../ramses-test-client-basic-extended.frag | 0 .../ramses-test-client-basic-extended.vert | 0 .../res/ramses-test-client-basic.frag | 0 .../res/ramses-test-client-basic.vert | 0 .../res/ramses-test-client-boolUniform.frag | 13 +- .../res/ramses-test-client-boolUniform.vert | 0 .../res/ramses-test-client-cube-nx.png | Bin .../res/ramses-test-client-cube-ny.png | Bin .../res/ramses-test-client-cube-nz.png | Bin .../res/ramses-test-client-cube-px.png | Bin .../res/ramses-test-client-cube-py.png | Bin .../res/ramses-test-client-cube-pz.png | Bin .../res/ramses-test-client-cubeSphere.frag | 0 .../res/ramses-test-client-cubeSphere.vert | 0 ...ramses-test-client-data-buffers-float.frag | 0 ...ramses-test-client-data-buffers-float.vert | 0 ...-test-client-data-buffers-interleaved.frag | 0 ...-test-client-data-buffers-interleaved.vert | 0 .../ramses-test-client-data-buffers-vec2.frag | 0 .../ramses-test-client-data-buffers-vec2.vert | 0 .../ramses-test-client-data-buffers-vec3.frag | 0 .../ramses-test-client-data-buffers-vec3.vert | 0 .../ramses-test-client-data-buffers-vec4.frag | 0 .../ramses-test-client-data-buffers-vec4.vert | 0 .../res/ramses-test-client-data-buffers.frag | 0 .../res/ramses-test-client-data-buffers.vert | 0 .../res/ramses-test-client-discard.frag | 0 .../res/ramses-test-client-discard.vert | 0 ...ses-test-client-embedded-compositing-1.png | Bin ...ses-test-client-embedded-compositing-2.png | Bin ...ses-test-client-embedded-compositing-3.png | Bin ...ses-test-client-embedded-compositing-4.png | Bin ...ses-test-client-embedded-compositing-5.png | Bin ...ses-test-client-embedded-compositing-6.png | Bin .../ramses-test-client-explicitLocation.frag | 0 .../ramses-test-client-explicitLocation.vert | 0 ...s-test-client-explicitLocationSwapped.frag | 0 ...s-test-client-explicitLocationSwapped.vert | 0 ...ramses-test-client-file-loading-basic.vert | 0 .../ramses-test-client-file-loading-red.frag | 0 ...amses-test-client-file-loading-texture.png | Bin ...es-test-client-file-loading-texturing.frag | 0 ...es-test-client-file-loading-texturing.vert | 4 +- .../res/ramses-test-client-input.frag | 0 .../res/ramses-test-client-input.vert | 0 .../res/ramses-test-client-logo-cropped.png | Bin .../ramses-test-client-mplus-1p-regular.ttf | Bin .../ramses-test-client-multiStageUniform.frag | 0 .../ramses-test-client-multiStageUniform.vert | 0 .../ramses-test-client-optimizedInput.frag | 0 .../ramses-test-client-optimizedInput.vert | 0 ...amses-test-client-pixel-accurate-text.frag | 0 ...amses-test-client-pixel-accurate-text.vert | 0 ...mses-test-client-render-one-buffer-ms.frag | 0 ...mses-test-client-render-one-buffer-ms.vert | 0 .../ramses-test-client-render-one-buffer.frag | 0 .../ramses-test-client-render-one-buffer.vert | 0 ...ramses-test-client-render-two-buffers.frag | 0 ...ramses-test-client-render-two-buffers.vert | 0 .../res/ramses-test-client-simple-color.frag | 0 .../res/ramses-test-client-simple-color.vert | 0 .../res/ramses-test-client-structUniform.frag | 0 .../res/ramses-test-client-structUniform.vert | 0 .../res/ramses-test-client-text.frag | 0 .../res/ramses-test-client-text.vert | 0 ...es-test-client-texture-buffer-allmips.frag | 0 ...es-test-client-texture-buffer-allmips.vert | 0 .../ramses-test-client-texture-buffer.frag | 0 .../ramses-test-client-texture-buffer.vert | 0 .../res/ramses-test-client-textureSize.frag | 0 .../res/ramses-test-client-textureSize.vert | 0 .../res/ramses-test-client-textured-cube.frag | 0 .../res/ramses-test-client-textured-cube.vert | 0 ...test-client-textured-with-texel-fetch.frag | 0 ...test-client-textured-with-texel-fetch.vert | 0 .../res/ramses-test-client-textured.frag | 0 .../res/ramses-test-client-textured.vert | 0 .../ramses-test-client-texturedWithColor.frag | 0 .../ramses-test-client-texturedWithColor.vert | 0 .../res/ramses-test-instancing-uniform.frag | 0 .../res/ramses-test-instancing-uniform.vert | 0 .../res/ramses-test-instancing-vertex.frag | 0 .../res/ramses-test-instancing-vertex.vert | 0 .../res/ramses-text-stress-tests.frag | 0 .../res/ramses-text-stress-tests.vert | 0 tests/unittests/CMakeLists.txt | 20 + tests/unittests/client/AppearanceTest.cpp | 1600 +++++ .../unittests/client}/ArrayBufferTest.cpp | 168 +- .../unittests/client}/BlitPassTest.cpp | 106 +- tests/unittests/client/CMakeLists.txt | 45 + .../unittests/client}/CameraTest.cpp | 414 +- .../client/ClientApplicationLogicTest.cpp | 274 + .../client}/ClientEventHandlerMock.cpp | 2 +- .../client}/ClientEventHandlerMock.h | 17 +- tests/unittests/client/ClientTestUtils.cpp | 26 + .../unittests/client}/ClientTestUtils.h | 84 +- .../unittests/client}/CreationHelper.cpp | 113 +- tests/unittests/client/CreationHelper.h | 113 + .../unittests/client}/DataObjectTest.cpp | 43 +- .../client}/EffectDescriptionTest.cpp | 78 +- tests/unittests/client/EffectInputTest.cpp | 252 + .../unittests/client}/EffectTest.cpp | 357 +- tests/unittests/client/GeometryTest.cpp | 1092 ++++ tests/unittests/client/GlslEffectTest.cpp | 1050 ++++ .../unittests/client}/GlslLimitsTest.cpp | 17 +- tests/unittests/client/GlslParserTest.cpp | 526 ++ .../unittests/client}/IteratorTest.cpp | 12 +- .../unittests/client}/MeshNodeTest.cpp | 200 +- tests/unittests/client/MockActionCollector.h | 81 + .../client}/NodeLazyTransformTest.cpp | 50 +- .../unittests/client}/NodeTest.cpp | 246 +- .../client}/NodeTransformationTest.cpp | 112 +- .../unittests/client}/NodeVisibilityTest.cpp | 58 +- .../unittests/client}/PickableObjectTest.cpp | 63 +- .../unittests/client}/QuadTest.cpp | 4 +- .../unittests/client}/RamsesClientTest.cpp | 231 +- .../client/RamsesObjectOwnershipTest.cpp | 312 + tests/unittests/client/RamsesObjectTest.cpp | 226 + .../unittests/client}/RamsesObjectTestTypes.h | 17 +- .../client/RamsesObjectValidationTest.cpp | 152 + .../unittests/client}/RamsesUtilsTest.cpp | 138 +- .../unittests/client}/RamsesVersionTest.cpp | 52 +- tests/unittests/client/RenderBufferTest.cpp | 158 + .../client}/RenderGroupMeshIteratorTest.cpp | 11 +- tests/unittests/client/RenderGroupTest.cpp | 545 ++ .../client}/RenderPassGroupIteratorTest.cpp | 15 +- .../unittests/client}/RenderPassTest.cpp | 415 +- .../client/RenderTargetDescriptionTest.cpp | 239 + .../unittests/client}/RenderTargetTest.cpp | 22 +- .../unittests/client}/ResourceTest.cpp | 656 ++- .../client}/SceneCommandBufferTest.cpp | 16 +- tests/unittests/client/SceneCommandsTest.cpp | 82 + .../client}/SceneDistributionTest.cpp | 77 +- tests/unittests/client/SceneFactoryTest.cpp | 51 + .../client}/SceneGraphIteratorTest.cpp | 16 +- .../unittests/client}/SceneIteratorTest.cpp | 8 +- .../client}/SceneObjectIteratorTest.cpp | 62 +- .../client/SceneObjectRegistryTest.cpp | 96 +- .../client/ScenePersistationTest.cpp | 1715 ++++++ .../unittests/client}/ScenePersistationTest.h | 114 +- .../client}/ScenePersistationThreadedTest.cpp | 66 +- .../unittests/client}/SceneReferenceTest.cpp | 130 +- .../unittests/client}/SceneTest.cpp | 870 +-- .../client}/SerializationHelperTest.cpp | 4 +- .../unittests/client}/SimpleSceneTopology.h | 35 +- .../unittests/client}/TestEffectCreator.h | 30 +- .../unittests/client}/TestEffects.h | 83 +- .../unittests/client}/Texture2DBufferTest.cpp | 102 +- .../unittests/client}/TextureSamplerTest.cpp | 301 +- .../client/logic}/api/AnchorPointTest.cpp | 217 +- .../logic}/api/AnimationNodeConfigTest.cpp | 36 +- .../client/logic}/api/AnimationNodeTest.cpp | 259 +- .../AnimationNodeWithDataPropertiesTest.cpp | 62 +- .../logic/api/AppearanceBindingTest.cpp | 547 +- .../client/logic/api/CameraBindingTest.cpp | 1551 +++++ .../client/logic}/api/CollectionTest.cpp | 4 +- .../client/logic}/api/DataArrayTest.cpp | 144 +- .../client/logic}/api/IteratorTest.cpp | 4 +- .../logic}/api/LogicEngineTest_Animations.cpp | 109 +- .../api/LogicEngineTest_Compatibility.cpp | 252 + .../LogicEngineTest_DependencyExtraction.cpp | 11 +- .../logic/api/LogicEngineTest_Dirtiness.cpp | 577 ++ .../logic/api/LogicEngineTest_Factory.cpp | 676 +++ .../logic}/api/LogicEngineTest_Linking.cpp | 1072 ++-- ...icEngineTest_LogicNodeUpdateStatistics.cpp | 440 ++ .../api/LogicEngineTest_Serialization.cpp | 863 +++ .../api/LogicEngineTest_SerializedSize.cpp | 370 ++ .../logic}/api/LogicEngineTest_Update.cpp | 216 +- .../api/LogicEngineTest_UpdateReport.cpp | 126 +- .../logic}/api/LogicEngineTest_Validation.cpp | 343 +- .../client/logic/api/LogicNodeTest.cpp | 68 + .../client/logic/api/LogicObjectTest.cpp | 291 + .../client/logic}/api/LuaConfigTest.cpp | 56 +- .../client/logic}/api/LuaInterfaceTest.cpp | 248 +- .../client/logic}/api/LuaModuleTest.cpp | 207 +- .../client/logic}/api/LuaScriptTest_Debug.cpp | 91 +- .../client/logic}/api/LuaScriptTest_Init.cpp | 125 +- .../logic}/api/LuaScriptTest_Interface.cpp | 104 +- .../logic}/api/LuaScriptTest_Lifecycle.cpp | 90 +- .../logic}/api/LuaScriptTest_Modules.cpp | 281 +- .../logic}/api/LuaScriptTest_Runtime.cpp | 497 +- .../api/LuaScriptTest_Serialization.cpp | 100 +- .../logic}/api/LuaScriptTest_Syntax.cpp | 152 +- .../client/logic}/api/LuaScriptTest_Types.cpp | 24 +- .../client/logic/api/MeshNodeBindingTest.cpp | 427 ++ .../client/logic/api/NodeBindingTest.cpp | 769 +-- .../client/logic}/api/PropertyTest.cpp | 244 +- .../client/logic}/api/PropertyTypeTest.cpp | 4 +- .../api/RenderGroupBindingElementsTest.cpp | 95 + .../logic/api/RenderGroupBindingTest.cpp | 262 +- .../logic/api/RenderPassBindingTest.cpp | 579 ++ .../client/logic}/api/SaveFileConfigTest.cpp | 33 +- .../client/logic}/api/SkinBindingTest.cpp | 119 +- .../client/logic}/api/TimerNodeTest.cpp | 155 +- .../client/logic}/internal/ApiObjectsTest.cpp | 1557 +++-- .../internal/DirectedAcyclicGraphTest.cpp | 39 +- .../internal/EnvironmentProtectionTest.cpp | 4 +- .../internal/LogicNodeDependenciesTest.cpp | 178 +- .../logic}/internal/LuaCustomizationsTest.cpp | 30 +- .../internal/LuaTypeConversionsTest.cpp | 2 +- .../internal/PropertyTypeExtractorTest.cpp | 2 +- .../logic}/internal/RamsesHelperTest.cpp | 3 +- .../internal/RamsesObjectResolverTest.cpp | 58 +- .../client/logic}/internal/SolHelperTest.cpp | 2 +- .../client/logic}/internal/SolStateTest.cpp | 8 +- .../client/logic}/internal/TypeDataTest.cpp | 2 +- .../client/logic}/internal/TypeUtilsTest.cpp | 15 +- .../internal/WrappedLuaPropertyTest.cpp | 12 +- .../logic}/shared/FeatureLevelTestValues.h | 2 +- .../LogicEngineTestWithCreationHelper.h | 135 + .../logic/shared/LogicEngineTest_Base.h | 243 + .../client/logic}/shared/LogicNodeDummy.h | 16 +- .../client/logic}/shared/LuaScriptTest_Base.h | 2 +- .../logic}/shared/PropertyLinkTestUtils.h | 64 +- .../logic}/shared/RamsesObjectResolverMock.h | 2 +- .../client/logic}/shared/RamsesTestUtils.h | 57 +- .../logic}/shared/SerializationTestUtils.h | 18 +- .../client/logic}/shared/WithTempDirectory.h | 4 +- .../res/ramses-client-test_minimalShader.frag | 0 .../res/ramses-client-test_minimalShader.geom | 0 .../res/ramses-client-test_minimalShader.vert | 0 .../res/ramses-client-test_shader.vert | 0 .../res/ramses-text-DroidKufi-Regular.ttf | Bin .../res/ramses-text-NotoSansThai-Regular.ttf | Bin 0 -> 21720 bytes .../client}/res/ramses-text-Roboto-Bold.ttf | Bin .../res/ramses-text-Roboto-Regular.ttf | Bin .../res/ramses-text-WenQuanYi-Micro-Hei.ttf | Bin 0 -> 4626376 bytes .../client}/res/rgba8_expectedFlipped.png | Bin .../unittests/client}/res/sampleTexture.png | Bin .../client}/res/sampleTexture_invalid.png | 0 .../unittests/client/res/testScene_01.ramses | Bin 19416 -> 25459 bytes .../client}/text/FontCascadeTest.cpp | 14 +- .../client}/text/FontRegistryTest.cpp | 22 +- .../client/text/Freetype2FontInstanceTest.cpp | 287 + .../client}/text/GlyphTextureAtlasTest.cpp | 84 +- .../client}/text/GlyphTexturePageTest.cpp | 53 +- .../client/text/HarfbuzzFontInstanceTest.cpp | 751 +++ .../client}/text/LayoutUtilsHMITest.cpp | 16 +- .../client}/text/LayoutUtilsTest.cpp | 26 +- .../unittests/client}/text/TextCacheTest.cpp | 63 +- .../unittests/client}/text/UtfUtilsTest.cpp | 6 +- .../framework-test-utils}/CMakeLists.txt | 11 +- .../include/CommunicationSystemMock.h | 22 +- .../include/ComponentMocks.h | 36 +- .../include/DummyResource.h | 13 +- .../include/FileDescriptorHelper.h | 7 +- .../include/IOStreamTester.h | 17 +- .../include/InputStreamContainerMock.h | 9 +- .../include/InputStreamMock.h | 13 +- .../include/LogTestUtils.h | 39 + .../MockConnectionStatusUpdateNotifier.h | 10 +- .../include/ResourceMock.h | 12 +- .../include/SceneRendererHandlerMock.h | 17 +- .../include/ScopedConsoleLogDisable.h | 9 +- .../include/ScopedLogContextLevel.h | 57 + .../include/ServiceHandlerMocks.h | 22 +- .../include/TestEqualHelper.h | 7 +- .../include/TestRandom.h | 9 +- .../include/UnsafeTestMemoryHelpers.h | 20 +- .../WindowsInvalidParameterCheckSuppression.h | 11 +- .../src/CommunicationSystemMock.cpp | 6 +- .../src/ComponentMocks.cpp | 34 +- .../src/InputStreamContainerMock.cpp | 2 +- .../src/InputStreamMock.cpp | 2 +- .../MockConnectionStatusUpdateNotifier.cpp | 10 +- .../src/ResourceMock.cpp | 6 +- .../src/SceneRendererHandlerMock.cpp | 2 +- .../src/ServiceHandlerMocks.cpp | 18 +- ...indowsInvalidParameterCheckSuppression.cpp | 2 +- tests/unittests/framework/CMakeLists.txt | 81 + .../CommunicationSystemTest.cpp | 14 +- .../CommunicationSystemTest.h | 10 +- .../CommunicationSystemTestWrapper.cpp | 12 +- .../CommunicationSystemTestWrapper.h | 15 +- .../ConnectionStatusUpdateNotifierTest.cpp | 7 +- .../ConnectionSystemTestHelper.cpp | 26 +- .../ConnectionSystemTestHelper.h | 19 +- ...lushInformationSerializationHelperTest.cpp | 12 +- .../MockConnectionStatusListener.cpp | 10 +- .../MockConnectionStatusListener.h | 10 +- .../SceneUpdateSerializationHelperTest.cpp | 22 +- .../SceneUpdateSerializationTest.cpp | 35 +- .../SceneUpdateSerializerTestHelper.h | 25 +- .../TransportTCP}/TCPConnectionSystemTest.cpp | 8 +- .../AbstractSenderAndReceiverTest.h | 14 +- .../Components}/ClientSceneLogicTest.cpp | 445 +- .../Components}/InputStreamContainerTest.cpp | 32 +- .../ResourceAvailabilityEventTest.cpp | 8 +- .../Components}/ResourceComponentTest.cpp | 58 +- .../Components}/ResourceFileRegistryTest.cpp | 41 +- .../Components}/ResourceHashUsageTest.cpp | 7 +- .../Components}/ResourcePersistationTest.cpp | 113 +- .../ResourceSerializationTestHelper.h | 32 +- .../Components}/ResourceStorageTest.cpp | 73 +- .../ResourceTableOfContentsTest.cpp | 24 +- .../Components}/SceneGraphComponentTest.cpp | 308 +- ...ceneGraphProtocolSenderAndReceiverTest.cpp | 34 +- .../SingleResourceSerializationTest.cpp | 8 +- .../Common}/ParticipantIdentifierTest.cpp | 5 +- .../Core/Math3d/CameraMatrixHelperTest.cpp | 87 + .../Core/Math3d}/ProjectionParamsTest.cpp | 6 +- .../framework/Core/Math3d}/QuadTest.cpp | 5 +- .../framework/Core/Math3d}/RotationTest.cpp | 17 +- .../Core/TaskFramework/test/MockITask.h | 18 +- .../test/MockTaskFinishHandler.h | 10 +- .../Core/TaskFramework/test/MockTaskQueue.h | 12 +- .../test/ProcessingTaskQueueTest.cpp | 7 +- .../test/TaskExecutingThreadTest.cpp | 11 +- .../test/TaskFinishHandlerDecoratorTest.cpp | 6 +- .../test/TaskFinishHandlerDecoratorTest.h | 8 +- .../test/TaskForwardingQueueTest.cpp | 11 +- .../test/ThreadedTaskExecutorTest.cpp | 37 +- .../Utils/test/BinaryFileInputStreamTest.cpp | 9 +- .../Utils/test/BinaryFileOutputStreamTest.cpp | 5 +- .../Core/Utils/test/BinaryInputStreamTest.cpp | 55 +- .../test/BinaryOffsetFileInputStreamTest.cpp | 44 +- .../Utils/test/BinaryOutputStreamTest.cpp | 81 +- .../Core/Utils/test/EnumTraitsTest.cpp | 192 + .../framework}/Core/Utils/test/FileTest.cpp | 10 +- .../framework/Core/Utils/test/ImageTest.cpp | 374 ++ .../Utils/test/InplaceStringTokenizerTest.cpp | 10 +- .../Core/Utils/test/LogHelperTest.cpp | 4 +- .../Core/Utils/test/LogMacrosTest.cpp | 6 +- .../Core/Utils/test/LoggingUtilsTest.cpp | 39 +- .../Utils/test/MemoryPoolExplicitTest.cpp | 11 +- .../Utils/test/MemoryPoolIteratorTest.cpp | 23 +- .../Core/Utils/test/MemoryPoolTest.cpp | 9 +- .../Core/Utils/test/MessagePoolTest.cpp | 5 +- .../Utils/test/PeriodicLoggerHelperTest.cpp | 4 +- .../Utils/test/RawBinaryOutputStreamTest.cpp | 43 +- .../Utils/test/StatisticCollectionTest.cpp | 9 +- .../Core/Utils/test/StringUtilsTest.cpp | 7 +- .../Core/Utils/test/TextureMathUtilsTest.cpp | 4 +- .../Core/Utils/test/ThreadBarrierTest.cpp | 6 +- .../Core/Utils/test/ThreadLocalLogTest.cpp | 8 +- .../test/VectorBinaryOutputStreamTest.cpp | 48 +- .../Core/Utils/test/VoidOutputStreamTest.cpp | 60 +- .../DltLogAppender/DltAdapterTest.cpp | 133 + .../PlatformAbstraction}/AbseilTest.cpp | 2 +- .../BlockingQueueTest.cpp | 10 +- .../PlatformAbstraction}/ComplexTestType.h | 11 +- .../PlatformAbstraction}/FmtlibTest.cpp | 2 +- .../PlatformAbstraction}/GuidTest.cpp | 12 +- .../PlatformAbstraction}/HashMapTest.cpp | 80 +- .../PlatformAbstraction/HashSetTest.cpp | 393 ++ .../PlatformAbstraction}/HashTest.cpp | 34 +- .../PlatformAbstraction}/HeapArrayTest.cpp | 4 +- .../PlatformAbstraction}/IOStreamTest.cpp | 2 +- .../PlatformAbstraction}/MathTest.cpp | 4 +- .../PlatformAbstraction/NumericLimitsTest.cpp | 56 + .../PlatformEndianess.cpp | 4 +- .../PlatformEnvironmentVariablesTest.cpp | 4 +- .../PlatformSignalTest.cpp | 6 +- .../PlatformStringUtilsTest.cpp | 34 + .../PlatformThreadTest.cpp | 5 +- .../PlatformAbstraction}/PlatformTimeTest.cpp | 6 +- .../StringOutputStreamTest.cpp | 30 +- .../PlatformAbstraction}/VectorTest.cpp | 4 +- .../unittests/framework/Ramsh}/RamshTest.cpp | 205 +- .../SceneGraph/Resource/ArrayResourceTest.cpp | 36 + .../SceneGraph/Resource}/CityhashTest.cpp | 3 +- .../Resource}/EffectResourceTest.cpp | 69 +- .../Resource}/LZ4CompressionUtilsTest.cpp | 14 +- .../SceneGraph/Resource}/ResourceTest.cpp | 108 +- .../Resource}/TextureResourceTest.cpp | 19 +- .../SceneGraph/Scene}/ActionTestScene.cpp | 61 +- .../SceneGraph/Scene}/ActionTestScene.h | 170 +- .../Scene}/DataLayoutCachedSceneTest.cpp | 43 +- .../ResourceChangeCollectingSceneTest.cpp | 71 +- .../SceneGraph/Scene}/ResourceUtilsTest.cpp | 17 +- .../SceneActionCollectionBasicTypesTest.cpp | 8 +- .../SceneActionCollectionComplexTypesTest.cpp | 40 +- ...eActionCollectionCreatorAndApplierTest.cpp | 9 +- .../Scene}/SceneActionCollectionTest.cpp | 30 +- .../SceneActionHelperAndApplierTest.cpp | 15 +- .../SceneGraph/Scene}/SceneActionUtils.cpp | 2 +- .../SceneGraph/Scene}/SceneActionUtils.h | 9 +- .../Scene}/SceneActionUtilsTest.cpp | 10 +- .../SceneGraph/Scene}/SceneDescriberTest.cpp | 164 +- .../Scene}/ScenePersistationTest.cpp | 11 +- .../framework/SceneGraph/Scene}/SceneTest.h | 16 +- .../Scene}/SceneTest_BlitPasses.cpp | 18 +- .../SceneGraph/Scene}/SceneTest_Camera.cpp | 14 +- .../Scene}/SceneTest_DataBuffers.cpp | 44 +- .../Scene}/SceneTest_DataInstances.cpp | 64 +- .../Scene}/SceneTest_DataLayouts.cpp | 16 +- .../SceneGraph/Scene}/SceneTest_DataSlot.cpp | 8 +- .../SceneGraph/Scene}/SceneTest_Generic.cpp | 2 +- .../Scene/SceneTest_IteratableMemoryPools.cpp | 215 + .../SceneGraph/Scene}/SceneTest_Nodes.cpp | 8 +- .../Scene}/SceneTest_PickableObjects.cpp | 20 +- .../Scene}/SceneTest_RenderBuffer.cpp | 36 +- .../Scene}/SceneTest_RenderGroups.cpp | 36 +- .../Scene}/SceneTest_RenderPasses.cpp | 52 +- .../Scene}/SceneTest_RenderTarget.cpp | 12 +- .../Scene}/SceneTest_Renderables.cpp | 36 +- .../Scene}/SceneTest_SceneReference.cpp | 2 +- .../SceneGraph/Scene}/SceneTest_States.cpp | 32 +- .../Scene}/SceneTest_TextureBuffers.cpp | 50 +- .../Scene}/SceneTest_TextureSamplers.cpp | 16 +- .../Scene}/SceneTest_Transforms.cpp | 18 +- .../SceneGraph/Scene}/TestingScene.h | 102 +- .../Scene}/TransformationCachedSceneTest.cpp | 63 +- .../SceneAPI}/ResourceContentHashTest.cpp | 12 +- .../SceneAPI/TextureSamplerStatesHashTest.cpp | 82 + .../SceneReferenceEventTest.cpp | 6 +- .../framework/Watchdog/PlatformWatchDogMock.h | 23 + .../Watchdog}/PlatformWatchDogTest.cpp | 54 +- .../Watchdog}/ThreadWatchdogTest.cpp | 24 +- .../unittests/framework}/main.cpp | 0 .../APILoggingHelperTest.cpp | 4 +- .../ramses-framework}/ApiRamshCommandMock.cpp | 2 +- .../ramses-framework}/ApiRamshCommandMock.h | 11 +- .../ramses-framework}/CommandTest.cpp | 29 +- .../framework/ramses-framework}/CommandTest.h | 12 +- .../ramses-framework/DataTypeTest.cpp | 273 + .../ramses-framework/ErrorReportingTest.cpp | 145 + .../framework/ramses-framework/FlagsTest.cpp | 131 + .../PublicRamshCommandTest.cpp | 8 +- .../RamsesFrameworkConfigTest.cpp | 212 + .../ramses-framework}/RamsesFrameworkTest.cpp | 100 +- .../ramses-framework}/RamsesVersionTest.cpp | 4 +- .../StronglyTypedValueTest.cpp | 12 +- .../ramses-framework/ValidationReportTest.cpp | 153 + .../unittests/framework}/res/sampleImage.png | Bin tests/unittests/ramses-cli/CMakeLists.txt | 21 + .../unittests/ramses-cli/ramses-cli-test.cpp | 335 ++ tests/unittests/renderer/CMakeLists.txt | 34 + .../CMakeLists.txt | 23 + .../EmbeddedCompositor_WaylandMock.h | 19 +- .../EmbeddedCompositor_Wayland_Test.cpp | 140 +- .../NativeWaylandResourceMock.h | 9 +- .../WaylandBufferMock.h | 10 +- .../WaylandBufferResourceMock.h | 13 +- .../WaylandBufferResource_Test.cpp | 78 +- .../WaylandCallbackResourceMock.h | 9 +- .../WaylandCallbackResource_Test.cpp | 56 +- .../WaylandClientMock.h | 9 +- .../WaylandClient_Test.cpp | 60 +- .../WaylandCompositorConnectionMock.h | 11 +- .../WaylandCompositorConnection_Test.cpp | 16 +- .../WaylandGlobal_Test.cpp | 4 +- .../WaylandIVISurfaceMock.h | 13 +- .../WaylandRegionMock.h | 11 +- .../WaylandResourceLifecycleTest.cpp | 41 +- .../WaylandResource_Test.cpp | 22 +- .../WaylandShellSurfaceMock.h | 11 +- .../WaylandShellSurface_Test.cpp | 16 +- .../WaylandSurfaceMock.h | 19 +- .../WaylandSurface_Test.cpp | 40 +- .../embedded-compositor-wayland}/main.cpp | 6 +- .../BinaryShaderCacheProxyTest.cpp | 59 +- .../ramses-renderer/BinaryShaderCacheTest.cpp | 296 + .../renderer/ramses-renderer/CMakeLists.txt | 22 + .../ramses-renderer/DisplayConfigTest.cpp | 428 ++ .../RamsesRendererDispatchTest.cpp | 86 +- .../RamsesRendererPickingTest.cpp | 44 +- .../ramses-renderer/RamsesRendererTest.cpp | 935 +++ .../ramses-renderer/RendererConfigTest.cpp | 105 + .../RendererEventTestHandler.h | 358 ++ .../ramses-renderer/RendererMateTest.cpp | 197 + .../RendererSceneControlEventHandlerMock.cpp | 2 +- .../RendererSceneControlEventHandlerMock.h | 9 +- .../RendererSceneControlTest.cpp | 310 +- .../RendererSceneControlWithRendererTest.cpp | 70 +- .../renderer/renderer-lib/CMakeLists.txt | 24 + .../RendererCommands/AssignSceneTest.cpp | 86 + .../CreateOffscreenBufferTest.cpp | 71 + .../RendererCommands/LinkUnlinkTest.cpp | 81 + .../RendererCommands/ScreenshotTest.cpp | 81 + .../RendererCommands/SetClearColorTest.cpp | 60 + .../RendererCommands/SetSceneStateTest.cpp | 72 + .../RendererFrameworkLogicTest.cpp | 71 +- .../RendererLib}/AsyncEffectUploaderTest.cpp | 28 +- .../RendererLib/BufferLinksTest.cpp | 168 + .../DataReferenceLinkCachedSceneTest.cpp | 80 + .../DataReferenceLinkManagerTest.cpp | 483 ++ .../RendererLib}/DisplayBundleMock.cpp | 2 +- .../RendererLib}/DisplayBundleMock.h | 13 +- .../RendererLib/DisplayConfigTest.cpp | 135 + .../RendererLib/DisplayControllerMock.cpp | 26 + .../RendererLib/DisplayControllerMock.h | 41 + .../RendererLib}/DisplayControllerTest.cpp | 38 +- .../RendererLib}/DisplayControllerTestBase.h | 16 +- .../RendererLib}/DisplayDispatcherMock.cpp | 2 +- .../RendererLib}/DisplayDispatcherMock.h | 9 +- .../RendererLib}/DisplayDispatcherTest.cpp | 16 +- .../DisplayDispatcherThreadedTest.cpp | 6 +- .../RendererLib/DisplayEventHandlerTest.cpp | 150 + .../RendererLib/DisplaySetupTest.cpp | 546 ++ .../RendererLib}/DisplayThreadMock.cpp | 2 +- .../RendererLib}/DisplayThreadMock.h | 9 +- .../RendererLib}/DisplayThreadTest.cpp | 21 +- .../EmbeddedCompositingManagerMock.cpp | 2 +- .../EmbeddedCompositingManagerMock.h | 10 +- .../EmbeddedCompositingManagerTest.cpp | 274 + .../RendererLib/IntersectionUtilsTest.cpp | 581 ++ .../RendererLib/LinkManagerBaseTest.cpp | 181 + .../PendingSceneResourcesUtilsTest.cpp | 365 ++ .../RendererLib}/PlatformTest.cpp | 49 +- .../RendererLib}/Platform_BaseMock.cpp | 10 +- .../RendererLib}/Platform_BaseMock.h | 16 +- .../RenderExecutorInternalStateTest.cpp | 17 +- .../RendererLib/RenderExecutorTest.cpp | 1783 ++++++ .../RendererLib}/RendererCachedSceneTest.cpp | 9 +- .../RendererLib/RendererCommandBufferTest.cpp | 325 + .../RendererCommandExecutorTest.cpp | 497 ++ .../RendererLib/RendererConfigTest.cpp | 44 + .../RendererEventCollectorTest.cpp | 17 +- .../RendererLib/RendererLogContextTest.cpp | 86 + .../renderer-lib/RendererLib/RendererMock.cpp | 92 + .../renderer-lib/RendererLib}/RendererMock.h | 24 +- .../RendererResourceManagerMock.cpp | 149 + .../RendererLib/RendererResourceManagerMock.h | 97 + .../RendererResourceManagerTest.cpp | 1380 +++++ .../RendererResourceRegistryTest.cpp | 435 ++ .../RendererSceneControlLogicMock.cpp | 4 +- .../RendererSceneControlLogicMock.h | 9 +- .../RendererSceneControlLogicTest.cpp | 6 +- .../RendererSceneEventSenderMock.h | 9 +- .../RendererSceneResourceRegistryTest.cpp | 175 + .../RendererSceneStateControlMock.cpp | 2 +- .../RendererSceneStateControlMock.h | 9 +- .../RendererSceneUpdaterFacade.cpp | 27 +- .../RendererLib}/RendererSceneUpdaterFacade.h | 25 +- .../RendererLib}/RendererSceneUpdaterMock.cpp | 2 +- .../RendererLib}/RendererSceneUpdaterMock.h | 19 +- .../RendererLib/RendererSceneUpdaterTest.cpp | 5245 +++++++++++++++++ .../RendererLib/RendererSceneUpdaterTest.h | 1033 ++++ .../RendererSceneUpdaterTest_Resources.cpp | 2423 ++++++++ .../RendererLib/RendererScenesTest.cpp | 103 + .../RendererLib/RendererStatisticsTest.cpp | 423 ++ .../renderer-lib/RendererLib/RendererTest.cpp | 2665 +++++++++ .../RendererLib}/ResourceCachedSceneTest.cpp | 20 +- .../RendererLib}/ResourceUploaderMock.cpp | 3 +- .../RendererLib}/ResourceUploaderMock.h | 12 +- .../RendererLib/ResourceUploaderTest.cpp | 580 ++ .../ResourceUploadingManagerTest.cpp | 879 +++ .../RendererLib}/SceneAllocateHelper.cpp | 8 +- .../RendererLib}/SceneAllocateHelper.h | 29 +- .../SceneDependencyCheckerTest.cpp | 407 ++ .../RendererLib}/SceneDisplayTrackerTest.cpp | 10 +- .../SceneExpirationMonitorTest.cpp | 944 +++ .../RendererLib/SceneLinksManagerTest.cpp | 355 ++ .../RendererLib}/SceneLinksTest.cpp | 9 +- .../RendererLib/SceneLinksTestUtils.h | 79 + .../RendererLib}/SceneReferenceLogicMock.cpp | 2 +- .../RendererLib}/SceneReferenceLogicMock.h | 9 +- .../RendererLib}/SceneReferenceLogicTest.cpp | 146 +- ...ceneReferenceLogicWithSceneUpdaterTest.cpp | 36 +- .../RendererLib/SceneResourceUploaderTest.cpp | 103 + .../RendererLib}/SceneStateExecutorTest.cpp | 21 +- .../RendererLib}/SceneStateInfoTest.cpp | 29 +- .../RendererLib}/TestSceneHelper.h | 35 +- .../RendererLib/TextureLinkManagerTest.cpp | 1144 ++++ .../TransformationLinkCachedSceneTest.cpp | 83 +- .../TransformationLinkManagerTest.cpp | 365 ++ .../renderer-test-common}/CMakeLists.txt | 16 +- .../renderer-test-common}/ContextMock.cpp | 10 +- .../renderer-test-common}/ContextMock.h | 11 +- .../renderer-test-common}/DeviceMock.cpp | 6 +- .../renderer-test-common}/DeviceMock.h | 66 +- .../EmbeddedCompositorMock.cpp | 2 +- .../EmbeddedCompositorMock.h | 13 +- .../renderer-test-common/MockResourceHash.h | 60 + .../PlatformFactoryMock.h | 18 +- .../renderer-test-common}/PlatformMock.cpp | 2 +- .../renderer-test-common}/PlatformMock.h | 17 +- .../RenderBackendMock.cpp | 6 +- .../renderer-test-common}/RenderBackendMock.h | 24 +- .../RendererCommandVisitorMock.cpp | 4 +- .../RendererCommandVisitorMock.h | 25 +- .../ResourceDeviceHandleAccessorMock.h | 37 + .../ResourceUploadRenderBackendMock.h | 16 +- .../SystemCompositorControllerMock.cpp | 6 +- .../SystemCompositorControllerMock.h | 11 +- .../WindowEventHandlerMock.cpp | 12 +- .../WindowEventHandlerMock.h | 16 +- .../renderer-test-common}/WindowMock.cpp | 6 +- .../renderer-test-common}/WindowMock.h | 11 +- .../CMakeLists.txt | 22 + ...mCompositorController_Wayland_IVI_Test.cpp | 10 +- .../main.cpp | 2 +- .../wayland-test-utils/CMakeLists.txt | 17 + .../TestWithWaylandEnvironment.h | 9 +- .../window-wayland-common}/AWindowWayland.h | 15 +- .../AWindowWaylandWithEventHandling.h | 39 +- .../window-wayland-common}/CMakeLists.txt | 12 +- .../window-wayland-common}/TestCases.h | 47 +- .../window-wayland-ivi/CMakeLists.txt | 21 + .../Window_Wayland_IVI_Test.cpp | 10 +- .../renderer/window-wayland-ivi}/main.cpp | 4 +- .../window-wayland-wl-shell/CMakeLists.txt | 21 + .../Window_Wayland_Shell_Test.cpp | 8 +- .../window-wayland-wl-shell}/main.cpp | 0 .../renderer/window-windows/CMakeLists.txt | 22 + .../window-windows}/Window_Window_Test.cpp | 54 +- .../renderer/window-x11/CMakeLists.txt | 22 + .../renderer/window-x11}/Window_X11_Test.cpp | 40 +- .../unittests/tools}/CMakeLists.txt | 6 +- .../tools/ramses-logic-viewer/CMakeLists.txt | 27 + .../LogicViewerLuaTest.cpp | 141 +- .../ramses-logic-viewer}/LogicViewerTest.cpp | 29 +- .../LogicViewerTestBase.cpp | 0 .../LogicViewerTestBase.h | 31 +- tools/CMakeLists.txt | 23 + .../ramses-daemon}/CMakeLists.txt | 0 .../ramses-daemon}/main.cpp | 23 +- {utils => tools}/ramses-imgui/CMakeLists.txt | 1 - .../ramses-imgui/include/ImguiClientHelper.h | 68 +- .../ramses-imgui/include/ImguiImageCache.h | 6 +- .../ramses-imgui/include/ImguiWidgets.h | 14 +- .../ramses-imgui/src/ImguiClientHelper.cpp | 43 +- .../ramses-imgui/src/ImguiImageCache.cpp | 46 +- .../ramses-imgui/src/ImguiWidgets.cpp | 72 +- .../ramses-logic-viewer/Arguments.h | 19 +- .../ramses-logic-viewer/CMakeLists.txt | 22 +- .../ramses-logic-viewer/ImguiClientHelper.cpp | 41 +- .../ramses-logic-viewer/ImguiClientHelper.h | 66 +- .../ramses-logic-viewer/ImguiWrapper.h | 0 .../ramses-logic-viewer/LogicViewer.cpp | 87 +- .../ramses-logic-viewer/LogicViewer.h | 32 +- .../ramses-logic-viewer/LogicViewerApp.cpp | 35 +- .../ramses-logic-viewer/LogicViewerApp.h | 9 +- .../ramses-logic-viewer/LogicViewerGui.cpp | 86 +- .../ramses-logic-viewer/LogicViewerGui.h | 2 +- .../ramses-logic-viewer/LogicViewerGuiApp.cpp | 7 +- .../ramses-logic-viewer/LogicViewerGuiApp.h | 0 .../LogicViewerHeadlessApp.cpp | 5 +- .../LogicViewerHeadlessApp.h | 0 .../ramses-logic-viewer/LogicViewerLog.cpp | 10 +- .../ramses-logic-viewer/LogicViewerLog.h | 4 +- .../LogicViewerLuaTypes.cpp | 79 +- .../ramses-logic-viewer/LogicViewerLuaTypes.h | 17 +- .../LogicViewerSettings.cpp | 0 .../ramses-logic-viewer/LogicViewerSettings.h | 0 .../ramses-logic-viewer/Result.h | 0 .../ramses-logic-viewer/SceneSetup.h | 20 +- .../ramses-logic-viewer/UpdateReportSummary.h | 2 +- .../ramses-logic-viewer/main.cpp | 0 .../ramses-logic-viewer/main_headless.cpp | 0 .../CMakeLists.txt | 2 +- .../ramses-renderer-standalone}/src/main.cpp | 38 +- .../ramses-scene-viewer/CMakeLists.txt | 0 .../ramses-scene-viewer/src/ProgressMonitor.h | 13 +- .../ramses-scene-viewer/src/ResourceList.h | 51 +- .../ramses-scene-viewer/src/SceneSetup.h | 33 +- .../ramses-scene-viewer/src/SceneViewer.cpp | 52 +- .../ramses-scene-viewer/src/SceneViewer.h | 16 +- .../src/SceneViewerGui.cpp | 950 +-- .../ramses-scene-viewer/src/SceneViewerGui.h | 144 +- .../ramses-scene-viewer/src/main.cpp | 2 +- .../ramses-stream-viewer/CMakeLists.txt | 0 .../ramses-stream-viewer/src/main.cpp | 56 +- .../test-asset-producer}/CMakeLists.txt | 5 +- .../test-asset-producer}/main.cpp | 95 +- utils/CMakeLists.txt | 11 - 2931 files changed, 108024 insertions(+), 110280 deletions(-) delete mode 100644 benchmarks/CMakeLists.txt delete mode 100644 client/CMakeLists.txt delete mode 100644 client/logic/.clang-tidy delete mode 100644 client/logic/cmake/platformConfig.cmake delete mode 100644 client/logic/include/ramses-logic/ErrorData.h delete mode 100644 client/logic/include/ramses-logic/Logger.h delete mode 100644 client/logic/include/ramses-logic/LogicObject.h delete mode 100644 client/logic/include/ramses-logic/RamsesLogicVersion.h delete mode 100644 client/logic/include/ramses-logic/WarningData.h delete mode 100644 client/logic/lib/flatbuffers/generated/LogicEngineGen.h delete mode 100644 client/logic/lib/flatbuffers/schemas/LogicEngine.fbs delete mode 100644 client/logic/lib/impl/Logger.cpp delete mode 100644 client/logic/lib/impl/LoggerImpl.cpp delete mode 100644 client/logic/lib/impl/LoggerImpl.h delete mode 100644 client/logic/lib/impl/LogicEngine.cpp delete mode 100644 client/logic/lib/impl/LogicEngineImpl.cpp delete mode 100644 client/logic/lib/impl/LogicEngineImpl.h delete mode 100644 client/logic/lib/impl/LogicObject.cpp delete mode 100644 client/logic/lib/impl/RamsesLogicVersion.cpp delete mode 100644 client/logic/lib/impl/RamsesRenderGroupBindingElements.cpp delete mode 100644 client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.cpp delete mode 100644 client/logic/lib/impl/SaveFileConfigImpl.h delete mode 100644 client/logic/lib/internals/ErrorReporting.cpp delete mode 100644 client/logic/lib/internals/ValidationResults.cpp delete mode 100644 client/logic/tools/ramses-logic-viewer-test/CMakeLists.txt delete mode 100644 client/logic/unittests/api/LoggerTest.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_Compatibility.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_Dirtiness.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_ErrorHandling.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_Factory.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_Lookup.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_Serialization.cpp delete mode 100644 client/logic/unittests/api/LogicEngineTest_SerializedSize.cpp delete mode 100644 client/logic/unittests/api/LogicNodeTest.cpp delete mode 100644 client/logic/unittests/api/RamsesCameraBindingTest.cpp delete mode 100644 client/logic/unittests/api/RamsesMeshNodeBindingTest.cpp delete mode 100644 client/logic/unittests/api/RamsesRenderGroupBindingElementsTest.cpp delete mode 100644 client/logic/unittests/api/RamsesRenderPassBindingTest.cpp delete mode 100644 client/logic/unittests/internal/ErrorReportingTest.cpp delete mode 100644 client/logic/unittests/res/testScene_01.ramses delete mode 100644 client/logic/unittests/shared/LogTestUtils.h delete mode 100644 client/logic/unittests/shared/LogicEngineTest_Base.h delete mode 100644 client/ramses-client-api/Appearance.cpp delete mode 100644 client/ramses-client-api/ArrayBuffer.cpp delete mode 100644 client/ramses-client-api/AttributeInput.cpp delete mode 100644 client/ramses-client-api/DataObject.cpp delete mode 100644 client/ramses-client-api/Effect.cpp delete mode 100644 client/ramses-client-api/EffectDescription.cpp delete mode 100644 client/ramses-client-api/EffectInput.cpp delete mode 100644 client/ramses-client-api/GeometryBinding.cpp delete mode 100644 client/ramses-client-api/RamsesObject.cpp delete mode 100644 client/ramses-client-api/RenderGroup.cpp delete mode 100644 client/ramses-client-api/RenderTargetDescription.cpp delete mode 100644 client/ramses-client-api/SceneConfig.cpp delete mode 100644 client/ramses-client-api/UniformInput.cpp delete mode 100644 client/ramses-client-api/include/ramses-client-api/AttributeInput.h delete mode 100644 client/ramses-client-api/include/ramses-client-api/EffectInput.h delete mode 100644 client/ramses-client-api/include/ramses-client-api/RamsesObject.h delete mode 100644 client/ramses-client-api/include/ramses-client-api/SceneConfig.h delete mode 100644 client/ramses-client-api/include/ramses-client-api/UniformInput.h delete mode 100644 client/ramses-client-api/include/ramses-client.h delete mode 100644 client/ramses-client/impl/AppearanceImpl.cpp delete mode 100644 client/ramses-client/impl/AppearanceImpl.h delete mode 100644 client/ramses-client/impl/ArrayBufferImpl.cpp delete mode 100644 client/ramses-client/impl/ArrayBufferImpl.h delete mode 100644 client/ramses-client/impl/BlitPassImpl.h delete mode 100644 client/ramses-client/impl/CameraNodeImpl.cpp delete mode 100644 client/ramses-client/impl/CameraNodeImpl.h delete mode 100644 client/ramses-client/impl/ClientCommands/DumpSceneToFile.cpp delete mode 100644 client/ramses-client/impl/ClientCommands/DumpSceneToFile.h delete mode 100644 client/ramses-client/impl/ClientCommands/FlushSceneVersion.h delete mode 100644 client/ramses-client/impl/ClientCommands/SceneCommandVisitor.cpp delete mode 100644 client/ramses-client/impl/DataObjectImpl.cpp delete mode 100644 client/ramses-client/impl/EffectDescriptionImpl.cpp delete mode 100644 client/ramses-client/impl/EffectDescriptionImpl.h delete mode 100644 client/ramses-client/impl/EffectImpl.cpp delete mode 100644 client/ramses-client/impl/EffectImpl.h delete mode 100644 client/ramses-client/impl/EffectInputImpl.cpp delete mode 100644 client/ramses-client/impl/EffectInputImpl.h delete mode 100644 client/ramses-client/impl/GeometryBindingImpl.cpp delete mode 100644 client/ramses-client/impl/GeometryBindingImpl.h delete mode 100644 client/ramses-client/impl/MeshNodeImpl.h delete mode 100644 client/ramses-client/impl/NodeImpl.h delete mode 100644 client/ramses-client/impl/PickableObjectImpl.cpp delete mode 100644 client/ramses-client/impl/PickableObjectImpl.h delete mode 100644 client/ramses-client/impl/RamsesClientImpl.cpp delete mode 100644 client/ramses-client/impl/RamsesClientImpl.h delete mode 100644 client/ramses-client/impl/RamsesClientTypesImpl.h delete mode 100644 client/ramses-client/impl/RamsesObjectHandle.h delete mode 100644 client/ramses-client/impl/RamsesObjectImpl.cpp delete mode 100644 client/ramses-client/impl/RamsesObjectImpl.h delete mode 100644 client/ramses-client/impl/RamsesObjectRegistry.cpp delete mode 100644 client/ramses-client/impl/RamsesObjectRegistry.h delete mode 100644 client/ramses-client/impl/RamsesObjectTypeUtils.cpp delete mode 100644 client/ramses-client/impl/RamsesObjectTypeUtils.h delete mode 100644 client/ramses-client/impl/RenderBufferImpl.h delete mode 100644 client/ramses-client/impl/RenderGroupImpl.cpp delete mode 100644 client/ramses-client/impl/RenderGroupImpl.h delete mode 100644 client/ramses-client/impl/RenderPassImpl.cpp delete mode 100644 client/ramses-client/impl/RenderPassImpl.h delete mode 100644 client/ramses-client/impl/RenderTargetDescriptionImpl.cpp delete mode 100644 client/ramses-client/impl/ResourceImpl.h delete mode 100644 client/ramses-client/impl/RotationTypeUtils.h delete mode 100644 client/ramses-client/impl/SceneConfigImpl.h delete mode 100644 client/ramses-client/impl/SceneObjectImpl.cpp delete mode 100644 client/ramses-client/impl/SceneObjectImpl.h delete mode 100644 client/ramses-client/impl/SceneReferenceImpl.cpp delete mode 100644 client/ramses-client/impl/SceneReferenceImpl.h delete mode 100644 client/ramses-client/impl/SceneUtils.h delete mode 100644 client/ramses-client/impl/Texture2DBufferImpl.h delete mode 100644 client/ramses-client/impl/TextureSamplerImpl.cpp delete mode 100644 client/ramses-client/impl/TextureSamplerImpl.h delete mode 100644 client/ramses-client/impl/TextureUtils.h delete mode 100644 client/ramses-client/impl/VisibilityModeUtils.cpp delete mode 100644 client/ramses-client/impl/VisibilityModeUtils.h delete mode 100644 client/ramses-client/impl/glslEffectBlock/GlslEffect.cpp delete mode 100644 client/test/AppearanceTest.cpp delete mode 100644 client/test/ClientApplicationLogicTest.cpp delete mode 100644 client/test/CreationHelper.h delete mode 100644 client/test/EffectInputTest.cpp delete mode 100644 client/test/GeometryBindingTest.cpp delete mode 100644 client/test/GlslEffectTest.cpp delete mode 100644 client/test/MockActionCollector.h delete mode 100644 client/test/RamsesObjectOwnershipTest.cpp delete mode 100644 client/test/RamsesObjectTest.cpp delete mode 100644 client/test/RenderBufferTest.cpp delete mode 100644 client/test/RenderGroupTest.cpp delete mode 100644 client/test/RenderTargetDescriptionTest.cpp delete mode 100644 client/test/SceneFactoryTest.cpp delete mode 100644 client/test/ScenePersistationTest.cpp delete mode 100644 client/test/text/Freetype2FontInstanceTest.cpp delete mode 100644 client/test/text/HarfbuzzFontInstanceTest.cpp delete mode 100644 cmake/modules/FindBoost.cmake create mode 100644 cmake/modules/FindLuaJIT.cmake create mode 100644 cmake/modules/FindQuartzCore.cmake delete mode 100644 cmake/ramses/addSubdirectory.cmake rename {client/logic/cmake => cmake/ramses}/flatbuffersGeneration.cmake (93%) create mode 100644 demo/iOS/CMakeLists.txt create mode 100644 demo/iOS/Info.plist create mode 100644 demo/iOS/README.md rename renderer/RendererLib/RendererAPI/include/RendererAPI/EDeviceType.h => demo/iOS/include/RendererBundle.h (57%) rename client/logic/unittests/shared/LogicNodeDummy.cpp => demo/iOS/include/ramses-renderer-ios-app-Bridging-Header.h (86%) create mode 100644 demo/iOS/res/LaunchScreen.storyboard create mode 100644 demo/iOS/res/Main.storyboard create mode 100644 demo/iOS/src/AppDelegate.swift create mode 100644 demo/iOS/src/RendererBundle.mm create mode 100644 demo/iOS/src/SceneDelegate.swift create mode 100644 demo/iOS/src/ViewController.swift delete mode 100644 doc/old_ramses/developer/00_MainPage.dox delete mode 100644 doc/old_ramses/developer/10_GettingStarted.dox delete mode 100644 doc/old_ramses/developer/20_Contributing.dox delete mode 100644 doc/old_ramses/developer/30_CodeStyleGuide.dox delete mode 100644 doc/old_ramses/developer/40_ClientAPI.dox delete mode 100644 doc/old_ramses/developer/50_Testing.dox delete mode 100644 doc/old_ramses/developer/images/guide-which-tests.png delete mode 100644 doc/old_ramses/developer/images/initiator.dot delete mode 100644 doc/old_ramses/developer/images/ramses_logo_with_alpha2.png delete mode 100644 doc/old_ramses/developer/images/responder.dot delete mode 100644 doc/old_ramses/developer/images/responder_with_old.dot delete mode 100644 doc/old_ramses/general/40_Examples.dox delete mode 100644 doc/old_ramses/general/50_ContentExpiration.dox delete mode 100644 doc/old_ramses/general/70_SceneReferencing.dox delete mode 100644 doc/old_ramses/tools/00_MainPage.dox delete mode 100644 doc/old_ramses/tools/dlt-logging/10_GeneralIntroduction.dox delete mode 100644 doc/old_ramses/tools/dlt-logging/20_UsingDLT.dox delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_connect.png delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_connect_tcp.png delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_context_inject.png delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_contexts.png delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_injection.png delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_messages.png delete mode 100644 doc/old_ramses/tools/dlt-logging/images/dltviewer_plugin.png delete mode 100644 doc/old_ramses/tools/profiling/10_Profiling.dox delete mode 100644 doc/old_ramses/tools/scene-viewer/10_SceneViewer.dox delete mode 100644 framework/CMakeLists.txt delete mode 100644 framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializationHelper.h delete mode 100644 framework/Core/Common/include/Common/StronglyTypedValue.h delete mode 100644 framework/Core/Common/test/StronglyTypedValueTest.cpp delete mode 100644 framework/Core/Math3d/test/CameraMatrixHelperTest.cpp delete mode 100644 framework/Core/Utils/include/Utils/LoggingUtils.h delete mode 100644 framework/Core/Utils/include/Utils/MessagePool.h delete mode 100644 framework/Core/Utils/src/UserLogAppender.cpp delete mode 100644 framework/Core/Utils/test/ImageTest.cpp delete mode 100644 framework/DltLogAppender/include/DltLogAppender/DltAdapter.h delete mode 100644 framework/DltLogAppender/test/DltAdapterTest.cpp delete mode 100644 framework/FrameworkTestUtils/include/ScopedLogContextLevel.h delete mode 100644 framework/FrameworkTestUtils/include/framework_common_gmock_header.h delete mode 100644 framework/FrameworkTestUtils/src/TestPngHeader.cpp delete mode 100644 framework/FrameworkTestUtils/src/framework_common_gmock_header.cpp delete mode 100644 framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTypes.h delete mode 100644 framework/PlatformAbstraction/test/HashSetTest.cpp delete mode 100644 framework/PlatformAbstraction/test/NumericLimitsTest.cpp delete mode 100644 framework/PlatformAbstraction/test/PlatformStringUtilsTest.cpp delete mode 100644 framework/SceneGraph/Resource/include/Resource/EResourceCompressionStatus.h delete mode 100644 framework/SceneGraph/Resource/include/Resource/ResourceTypes.h delete mode 100644 framework/SceneGraph/Resource/test/ArrayResourceTest.cpp delete mode 100644 framework/SceneGraph/Scene/include/Scene/EScenePublicationMode.h delete mode 100644 framework/SceneGraph/Scene/test/SceneTest_IteratableMemoryPools.cpp delete mode 100644 framework/SceneGraph/SceneAPI/include/SceneAPI/EDataSlotType.h delete mode 100644 framework/SceneGraph/SceneAPI/include/SceneAPI/IScene.h delete mode 100644 framework/SceneGraph/SceneAPI/include/SceneAPI/TextureEnums.h delete mode 100644 framework/SceneGraph/SceneAPI/test/TextureSamplerStatesHashTest.cpp delete mode 100644 framework/Watchdog/test/PlatformWatchDogMock.h delete mode 100644 framework/ramses-framework-api/include/ramses-framework-api/StatusObject.h delete mode 100644 framework/ramses-framework/include/DataTypeUtils.h delete mode 100644 framework/ramses-framework/include/RamsesFrameworkConfigImpl.h delete mode 100644 framework/ramses-framework/include/RamsesFrameworkImpl.h delete mode 100644 framework/ramses-framework/include/StatusObjectImpl.h delete mode 100644 framework/ramses-framework/include/ThreadWatchdogConfig.h delete mode 100644 framework/ramses-framework/src/RamsesFramework.cpp delete mode 100644 framework/ramses-framework/src/RamsesFrameworkConfig.cpp delete mode 100644 framework/ramses-framework/src/RamsesFrameworkTypesImpl.cpp delete mode 100644 framework/ramses-framework/src/StatusObject.cpp delete mode 100644 framework/ramses-framework/src/StatusObjectImpl.cpp delete mode 100644 framework/ramses-framework/test/DataTypeTest.cpp delete mode 100644 framework/ramses-framework/test/RamsesFrameworkConfigTest.cpp delete mode 100644 framework/ramses-framework/test/StatusObjectTest.cpp create mode 100644 include/CMakeLists.txt rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Appearance.h (56%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/ArrayBuffer.h (71%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/ArrayResource.h (64%) create mode 100644 include/ramses/client/AttributeInput.h rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/BlitPass.h (72%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Camera.h (79%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/ClientObject.h (60%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/DataObject.h (68%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Effect.h (52%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/EffectDescription.h (57%) create mode 100644 include/ramses/client/EffectInput.h rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/EffectInputSemantic.h (96%) rename client/ramses-client-api/include/ramses-client-api/GeometryBinding.h => include/ramses/client/Geometry.h (62%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/IClientEventHandler.h (96%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/MeshNode.h (55%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/MipLevelData.h (80%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Node.h (59%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/OrthographicCamera.h (80%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/PerspectiveCamera.h (85%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/PickableObject.h (77%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RamsesClient.h (58%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderBuffer.h (68%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderGroup.h (66%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderGroupMeshIterator.h (73%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderPass.h (67%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderPassGroupIterator.h (73%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderTarget.h (62%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/RenderTargetDescription.h (58%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Resource.h (61%) rename {client/logic/include/ramses-logic => include/ramses/client}/SaveFileConfig.h (70%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Scene.h (77%) create mode 100644 include/ramses/client/SceneConfig.h rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/SceneGraphIterator.h (77%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/SceneIterator.h (77%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/SceneObject.h (50%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/SceneObjectIterator.h (69%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/SceneReference.h (81%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Texture2D.h (60%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Texture2DBuffer.h (72%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/Texture3D.h (59%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/TextureCube.h (59%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/TextureSampler.h (64%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/TextureSamplerExternal.h (62%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/TextureSamplerMS.h (62%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/client}/TextureSwizzle.h (94%) create mode 100644 include/ramses/client/UniformInput.h rename {client/logic/include/ramses-logic => include/ramses/client/logic}/AnchorPoint.h (88%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/AnimationNode.h (88%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/AnimationNodeConfig.h (79%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/AnimationTypes.h (98%) rename client/logic/include/ramses-logic/RamsesAppearanceBinding.h => include/ramses/client/logic/AppearanceBinding.h (54%) rename client/logic/include/ramses-logic/RamsesCameraBinding.h => include/ramses/client/logic/CameraBinding.h (68%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/Collection.h (98%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/DataArray.h (80%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/ELuaSavingMode.h (100%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/EPropertyType.h (99%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/EStandardModule.h (100%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/Iterator.h (99%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LogicEngine.h (56%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LogicEngineReport.h (81%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LogicNode.h (80%) create mode 100644 include/ramses/client/logic/LogicObject.h rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LuaConfig.h (78%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LuaInterface.h (76%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LuaModule.h (87%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/LuaScript.h (88%) rename client/logic/include/ramses-logic/RamsesMeshNodeBinding.h => include/ramses/client/logic/MeshNodeBinding.h (60%) rename client/logic/include/ramses-logic/RamsesNodeBinding.h => include/ramses/client/logic/NodeBinding.h (67%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/Property.h (80%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/PropertyLink.h (78%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/RamsesBinding.h (91%) rename client/logic/include/ramses-logic/RamsesRenderGroupBinding.h => include/ramses/client/logic/RenderGroupBinding.h (65%) rename client/logic/include/ramses-logic/RamsesRenderGroupBindingElements.h => include/ramses/client/logic/RenderGroupBindingElements.h (50%) rename client/logic/include/ramses-logic/RamsesRenderPassBinding.h => include/ramses/client/logic/RenderPassBinding.h (57%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/SkinBinding.h (77%) rename {client/logic/include/ramses-logic => include/ramses/client/logic}/TimerNode.h (87%) create mode 100644 include/ramses/client/ramses-client.h rename {client/ramses-client-api/include => include/ramses/client}/ramses-utils.h (77%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/FontCascade.h (94%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/FontInstanceId.h (90%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/FontInstanceOffsets.h (90%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/FontRegistry.h (95%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/Glyph.h (95%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/GlyphMetrics.h (87%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/IFontAccessor.h (92%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/IFontInstance.h (93%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/LayoutUtils.h (90%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/TextCache.h (96%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/TextLine.h (94%) rename {client/ramses-text-api/include/ramses-text-api => include/ramses/client/text}/UtfUtils.h (97%) rename {client/ramses-text-api/include => include/ramses/client/text}/ramses-text.h (53%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/APIExport.h (94%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/AppearanceEnums.h (97%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/DataTypes.h (94%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/EDataType.h (94%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/EFeatureLevel.h (91%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/framework}/ERotationType.h (98%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/framework}/EScenePublicationMode.h (90%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/framework}/EVisibilityMode.h (88%) create mode 100644 include/ramses/framework/Flags.h rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/IRamshCommand.h (94%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/IThreadWatchdogNotification.h (95%) create mode 100644 include/ramses/framework/Issue.h rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/RamsesFramework.h (70%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/RamsesFrameworkConfig.h (63%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/RamsesFrameworkTypes.h (82%) create mode 100644 include/ramses/framework/RamsesObject.h rename {client/ramses-client-api/include/ramses-client-api => include/ramses/framework}/RamsesObjectTypes.h (70%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/RamsesVersion.h (90%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/RendererSceneState.h (93%) rename {framework/ramses-framework-api/include/ramses-framework-api => include/ramses/framework}/StronglyTypedValue.h (89%) rename {client/ramses-client-api/include/ramses-client-api => include/ramses/framework}/TextureEnums.h (94%) create mode 100644 include/ramses/framework/ValidationReport.h rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/BinaryShaderCache.h (89%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/DisplayConfig.h (68%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/IBinaryShaderCache.h (96%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/IRendererEventHandler.h (98%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/IRendererSceneControlEventHandler.h (98%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/RamsesRenderer.h (79%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/RendererConfig.h (59%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/RendererSceneControl.h (77%) rename {renderer/ramses-renderer-api/include/ramses-renderer-api => include/ramses/renderer}/Types.h (94%) delete mode 100644 integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferRenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.h delete mode 100644 integration/SandwichTests/RendererTests/RendererLifecycleTests/ExternalWindowTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.cpp delete mode 100644 integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.h delete mode 100644 integration/SandwichTests/RendererTests/RendererTestFramework/RenderingTestCase.h delete mode 100644 integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.h delete mode 100644 integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.cpp delete mode 100644 integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/RenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.h delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.cpp delete mode 100644 integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.h delete mode 100644 integration/StressTests/ResourceStressTests/src/StressTestRenderer.h delete mode 100644 integration/TestContent/src/RenderBufferScene.cpp delete mode 100644 integration/TestContent/src/StreamTextureScene.cpp delete mode 100644 integration/TestContent/src/TriangleAppearance.cpp delete mode 100644 ramses-cli/test/ramses-cli-test.cpp delete mode 100644 ramses-shared-lib/CMakeLists.txt delete mode 100644 renderer/CMakeLists.txt delete mode 100644 renderer/Platform/Context_WGL/CMakeLists.txt delete mode 100644 renderer/Platform/Device_EGL_Extension/CMakeLists.txt delete mode 100644 renderer/Platform/EmbeddedCompositor_Wayland/CMakeLists.txt delete mode 100644 renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBuffer.cpp delete mode 100644 renderer/Platform/Platform_EGL/CMakeLists.txt delete mode 100644 renderer/Platform/Platform_Wayland_EGL/CMakeLists.txt delete mode 100644 renderer/Platform/Platform_Wayland_IVI_EGL/CMakeLists.txt delete mode 100644 renderer/Platform/Platform_Windows_WGL/CMakeLists.txt delete mode 100644 renderer/Platform/Platform_Windows_WGL/include/Platform_Windows_WGL/Platform_Windows_WGL.h delete mode 100644 renderer/Platform/Platform_X11_EGL/CMakeLists.txt delete mode 100644 renderer/Platform/SystemCompositorController_Wayland_IVI/CMakeLists.txt delete mode 100644 renderer/Platform/WaylandEGLExtensionProcs/CMakeLists.txt delete mode 100644 renderer/Platform/WaylandUtilities/CMakeLists.txt delete mode 100644 renderer/Platform/Window_Wayland/CMakeLists.txt delete mode 100644 renderer/Platform/Window_Wayland_IVI/CMakeLists.txt delete mode 100644 renderer/Platform/Window_Wayland_Shell/CMakeLists.txt delete mode 100644 renderer/Platform/Window_Wayland_Test/CMakeLists.txt delete mode 100644 renderer/Platform/Window_Windows/CMakeLists.txt delete mode 100644 renderer/PlatformFactory/CMakeLists.txt delete mode 100644 renderer/RendererLib/CMakeLists.txt delete mode 100644 renderer/RendererLib/RendererAPI/include/RendererAPI/ELoopMode.h delete mode 100644 renderer/RendererLib/RendererAPI/include/RendererAPI/EWindowType.h delete mode 100644 renderer/RendererLib/RendererAPI/include/RendererAPI/IRendererResourceCache.h delete mode 100644 renderer/RendererLib/RendererAPI/src/Types.cpp delete mode 100644 renderer/RendererLib/RendererCommands/src/SetClearColor.cpp delete mode 100644 renderer/RendererLib/RendererCommands/test/ScreenshotTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/include/RendererLib/EKeyModifier.h delete mode 100644 renderer/RendererLib/RendererLib/include/RendererLib/EMouseEventType.h delete mode 100644 renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManagerUtils.h delete mode 100644 renderer/RendererLib/RendererLib/include/RendererLib/SceneDisplayTracker.h delete mode 100644 renderer/RendererLib/RendererLib/src/RendererResourceManagerUtils.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/BufferLinksTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/DataReferenceLinkCachedSceneTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/DataReferenceLinkManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/DisplayConfigTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/DisplayEventHandlerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/DisplaySetupTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/EmbeddedCompositingManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/IntersectionUtilsTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/LinkManagerBaseTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/PendingSceneResourcesUtilsTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RenderExecutorTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererCommandBufferTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererCommandExecutorTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererConfigTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererLogContextTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererResourceManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererResourceRegistryTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererSceneResourceRegistryTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.h delete mode 100644 renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest_Resources.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererScenesTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererStatisticsTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/RendererTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/ResourceUploaderTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/ResourceUploadingManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/SceneDependencyCheckerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/SceneExpirationMonitorTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/SceneLinksManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/SceneLinksTestUtils.h delete mode 100644 renderer/RendererLib/RendererLib/test/SceneResourceUploaderTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/TextureLinkManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererLib/test/TransformationLinkManagerTest.cpp delete mode 100644 renderer/RendererLib/RendererTestCommon/DisplayControllerMock.cpp delete mode 100644 renderer/RendererLib/RendererTestCommon/DisplayControllerMock.h delete mode 100644 renderer/RendererLib/RendererTestCommon/MockResourceHash.h delete mode 100644 renderer/RendererLib/RendererTestCommon/RendererMock.cpp delete mode 100644 renderer/RendererLib/RendererTestCommon/RendererResourceCacheFake.h delete mode 100644 renderer/RendererLib/RendererTestCommon/RendererResourceCacheMock.h delete mode 100644 renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.cpp delete mode 100644 renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.h delete mode 100644 renderer/RendererLib/RendererTestCommon/ResourceDeviceHandleAccessorMock.h delete mode 100644 renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.cpp delete mode 100644 renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.h delete mode 100644 renderer/RendererTestUtils/include/ReadPixelCallbackHandler.h delete mode 100644 renderer/RendererTestUtils/include/RendererTestUtils.h delete mode 100644 renderer/RendererTestUtils/src/RendererTestUtils.cpp delete mode 100644 renderer/ramses-renderer-api/CMakeLists.txt delete mode 100644 renderer/ramses-renderer-api/include/ramses-renderer-api/DefaultRendererResourceCache.h delete mode 100644 renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererResourceCache.h delete mode 100644 renderer/ramses-renderer-impl/CMakeLists.txt delete mode 100644 renderer/ramses-renderer-impl/include/BinaryShaderCacheImpl.h delete mode 100644 renderer/ramses-renderer-impl/include/BinaryShaderCacheProxy.h delete mode 100644 renderer/ramses-renderer-impl/include/DefaultRendererResourceCacheImpl.h delete mode 100644 renderer/ramses-renderer-impl/include/DisplayConfigImpl.h delete mode 100644 renderer/ramses-renderer-impl/include/RamsesRendererImpl.h delete mode 100644 renderer/ramses-renderer-impl/include/RamsesRendererUtils.h delete mode 100644 renderer/ramses-renderer-impl/include/RendererConfigImpl.h delete mode 100644 renderer/ramses-renderer-impl/include/RendererResourceCacheProxy.h delete mode 100644 renderer/ramses-renderer-impl/include/RendererSceneControlImpl.h delete mode 100644 renderer/ramses-renderer-impl/src/BinaryShaderCache.cpp delete mode 100644 renderer/ramses-renderer-impl/src/DefaultRendererResourceCache.cpp delete mode 100644 renderer/ramses-renderer-impl/src/DefaultRendererResourceCacheImpl.cpp delete mode 100644 renderer/ramses-renderer-impl/src/DisplayConfig.cpp delete mode 100644 renderer/ramses-renderer-impl/src/DisplayConfigImpl.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RamsesRenderer.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RamsesRendererImpl.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RamsesRendererUtils.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RendererConfig.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RendererResourceCacheProxy.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RendererSceneControl.cpp delete mode 100644 renderer/ramses-renderer-impl/src/RendererSceneControlImpl.cpp delete mode 100644 renderer/ramses-renderer-impl/test/BinaryShaderCacheTest.cpp delete mode 100644 renderer/ramses-renderer-impl/test/DefaultRendererResourceCacheTest.cpp delete mode 100644 renderer/ramses-renderer-impl/test/DisplayConfigTest.cpp delete mode 100644 renderer/ramses-renderer-impl/test/RamsesRendererTest.cpp delete mode 100644 renderer/ramses-renderer-impl/test/RendererConfigTest.cpp delete mode 100644 renderer/ramses-renderer-impl/test/RendererEventTestHandler.h delete mode 100644 renderer/ramses-renderer-impl/test/RendererMateTest.cpp create mode 100644 scripts/ci/config/sanitizer/ubsan_blacklist.txt create mode 100755 scripts/ci/installation-check/check-shared-lib-symbols.py delete mode 100644 scripts/code_style_checker/check_api_export_symbols.py rename integration/PlatformTests/CMakeLists.txt => scripts/docker/ramses-basic/requirements.txt (80%) create mode 100755 scripts/migrate_to_28_0_0.py rename {integration => src}/CMakeLists.txt (67%) create mode 100644 src/client/CMakeLists.txt create mode 100644 src/client/impl/Appearance.cpp create mode 100644 src/client/impl/AppearanceImpl.cpp create mode 100644 src/client/impl/AppearanceImpl.h rename {client/ramses-client => src/client}/impl/AppearanceUtils.h (89%) create mode 100644 src/client/impl/ArrayBuffer.cpp create mode 100644 src/client/impl/ArrayBufferImpl.cpp create mode 100644 src/client/impl/ArrayBufferImpl.h rename {client/ramses-client-api => src/client/impl}/ArrayResource.cpp (63%) rename {client/ramses-client => src/client}/impl/ArrayResourceImpl.cpp (55%) rename {client/ramses-client => src/client}/impl/ArrayResourceImpl.h (50%) rename client/ramses-client-api/ClientObject.cpp => src/client/impl/AttributeInput.cpp (62%) rename {client/ramses-client-api => src/client/impl}/BlitPass.cpp (64%) rename {client/ramses-client => src/client}/impl/BlitPassImpl.cpp (52%) create mode 100644 src/client/impl/BlitPassImpl.h rename {client/ramses-client-api => src/client/impl}/Camera.cpp (62%) create mode 100644 src/client/impl/CameraNodeImpl.cpp create mode 100644 src/client/impl/CameraNodeImpl.h rename {client/ramses-client => src/client}/impl/ClientFactory.cpp (90%) rename {client/ramses-client => src/client}/impl/ClientFactory.h (81%) create mode 100644 src/client/impl/ClientObject.cpp rename {client/ramses-client => src/client}/impl/ClientObjectImpl.cpp (72%) rename {client/ramses-client => src/client}/impl/ClientObjectImpl.h (63%) create mode 100644 src/client/impl/DataObject.cpp create mode 100644 src/client/impl/DataObjectImpl.cpp rename {client/ramses-client => src/client}/impl/DataObjectImpl.h (51%) create mode 100644 src/client/impl/Effect.cpp create mode 100644 src/client/impl/EffectDescription.cpp create mode 100644 src/client/impl/EffectDescriptionImpl.cpp create mode 100644 src/client/impl/EffectDescriptionImpl.h create mode 100644 src/client/impl/EffectImpl.cpp create mode 100644 src/client/impl/EffectImpl.h create mode 100644 src/client/impl/EffectInput.cpp create mode 100644 src/client/impl/EffectInputImpl.cpp create mode 100644 src/client/impl/EffectInputImpl.h rename {client/ramses-client => src/client}/impl/EffectInputSemanticUtils.h (53%) create mode 100644 src/client/impl/Geometry.cpp create mode 100644 src/client/impl/GeometryImpl.cpp create mode 100644 src/client/impl/GeometryImpl.h rename {client/ramses-client => src/client}/impl/IteratorImpl.h (87%) rename {client/ramses-client-api => src/client/impl}/MeshNode.cpp (56%) rename {client/ramses-client => src/client}/impl/MeshNodeImpl.cpp (53%) create mode 100644 src/client/impl/MeshNodeImpl.h rename {client/ramses-client-api => src/client/impl}/Node.cpp (58%) rename {client/ramses-client => src/client}/impl/NodeImpl.cpp (57%) create mode 100644 src/client/impl/NodeImpl.h rename {client/ramses-client => src/client}/impl/ObjectIteratorImpl.h (64%) rename {client/ramses-client-api => src/client/impl}/OrthographicCamera.cpp (76%) rename {client/ramses-client-api => src/client/impl}/PerspectiveCamera.cpp (71%) rename {client/ramses-client-api => src/client/impl}/PickableObject.cpp (59%) create mode 100644 src/client/impl/PickableObjectImpl.cpp create mode 100644 src/client/impl/PickableObjectImpl.h rename {client/ramses-client-api => src/client/impl}/RamsesClient.cpp (50%) create mode 100644 src/client/impl/RamsesClientImpl.cpp create mode 100644 src/client/impl/RamsesClientImpl.h rename {client/ramses-client => src/client}/impl/RamsesClientTypesImpl.cpp (100%) create mode 100644 src/client/impl/RamsesClientTypesImpl.h rename {client/ramses-client => src/client}/impl/RamsesObjectVector.h (69%) rename {client/ramses-client-api => src/client/impl}/RenderBuffer.cpp (72%) rename {client/ramses-client => src/client}/impl/RenderBufferImpl.cpp (57%) create mode 100644 src/client/impl/RenderBufferImpl.h create mode 100644 src/client/impl/RenderGroup.cpp create mode 100644 src/client/impl/RenderGroupImpl.cpp create mode 100644 src/client/impl/RenderGroupImpl.h rename {client/ramses-client => src/client}/impl/RenderGroupMeshIterator.cpp (58%) rename {client/ramses-client-api => src/client/impl}/RenderPass.cpp (53%) rename {client/ramses-client => src/client}/impl/RenderPassGroupIterator.cpp (58%) create mode 100644 src/client/impl/RenderPassImpl.cpp create mode 100644 src/client/impl/RenderPassImpl.h rename {client/ramses-client-api => src/client/impl}/RenderTarget.cpp (63%) create mode 100644 src/client/impl/RenderTargetDescription.cpp create mode 100644 src/client/impl/RenderTargetDescriptionImpl.cpp rename {client/ramses-client => src/client}/impl/RenderTargetDescriptionImpl.h (51%) rename {client/ramses-client => src/client}/impl/RenderTargetImpl.cpp (65%) rename {client/ramses-client => src/client}/impl/RenderTargetImpl.h (52%) rename {client/ramses-client-api => src/client/impl}/Resource.cpp (63%) rename {client/ramses-client => src/client}/impl/ResourceImpl.cpp (59%) create mode 100644 src/client/impl/ResourceImpl.h rename {client/ramses-client => src/client}/impl/ResourceObjects.h (84%) rename {client/logic/lib => src/client}/impl/SaveFileConfig.cpp (81%) rename {client/logic/lib => src/client}/impl/SaveFileConfigImpl.cpp (65%) create mode 100644 src/client/impl/SaveFileConfigImpl.h rename {client/ramses-client-api => src/client/impl}/Scene.cpp (52%) create mode 100644 src/client/impl/SceneConfig.cpp create mode 100644 src/client/impl/SceneConfigImpl.cpp create mode 100644 src/client/impl/SceneConfigImpl.h rename {client/ramses-client => src/client}/impl/SceneDumper.cpp (66%) rename {client/ramses-client => src/client}/impl/SceneDumper.h (57%) rename {client/ramses-client => src/client}/impl/SceneFactory.cpp (78%) rename {client/ramses-client => src/client}/impl/SceneFactory.h (87%) rename {client/ramses-client-api => src/client/impl}/SceneGraphIterator.cpp (84%) rename {client/ramses-client => src/client}/impl/SceneGraphIteratorImpl.cpp (88%) rename {client/ramses-client => src/client}/impl/SceneGraphIteratorImpl.h (79%) rename {client/ramses-client => src/client}/impl/SceneImpl.cpp (50%) rename {client/ramses-client => src/client}/impl/SceneImpl.h (52%) rename {client/ramses-client => src/client}/impl/SceneIterator.cpp (77%) rename {client/ramses-client => src/client}/impl/SceneIteratorImpl.h (82%) rename {client/ramses-client-api => src/client/impl}/SceneObject.cpp (57%) create mode 100644 src/client/impl/SceneObjectImpl.cpp create mode 100644 src/client/impl/SceneObjectImpl.h rename {client/ramses-client => src/client}/impl/SceneObjectIterator.cpp (78%) create mode 100644 src/client/impl/SceneObjectRegistry.cpp create mode 100644 src/client/impl/SceneObjectRegistry.h rename client/ramses-client/impl/RamsesObjectRegistryIterator.h => src/client/impl/SceneObjectRegistryIterator.h (65%) rename {client/ramses-client-api => src/client/impl}/SceneReference.cpp (59%) create mode 100644 src/client/impl/SceneReferenceImpl.cpp create mode 100644 src/client/impl/SceneReferenceImpl.h rename {client/ramses-client => src/client}/impl/SerializationHelper.h (59%) rename {client/ramses-client-api => src/client/impl}/Texture2D.cpp (70%) rename {client/ramses-client-api => src/client/impl}/Texture2DBuffer.cpp (58%) rename {client/ramses-client => src/client}/impl/Texture2DBufferImpl.cpp (55%) create mode 100644 src/client/impl/Texture2DBufferImpl.h rename {client/ramses-client => src/client}/impl/Texture2DImpl.cpp (70%) rename {client/ramses-client => src/client}/impl/Texture2DImpl.h (60%) rename {client/ramses-client-api => src/client/impl}/Texture3D.cpp (70%) rename {client/ramses-client => src/client}/impl/Texture3DImpl.cpp (67%) rename {client/ramses-client => src/client}/impl/Texture3DImpl.h (57%) rename {client/ramses-client-api => src/client/impl}/TextureCube.cpp (67%) rename {client/ramses-client => src/client}/impl/TextureCubeImpl.cpp (66%) rename {client/ramses-client => src/client}/impl/TextureCubeImpl.h (57%) rename {client/ramses-client-api => src/client/impl}/TextureSampler.cpp (59%) rename {client/ramses-client-api => src/client/impl}/TextureSamplerExternal.cpp (59%) create mode 100644 src/client/impl/TextureSamplerImpl.cpp create mode 100644 src/client/impl/TextureSamplerImpl.h rename {client/ramses-client-api => src/client/impl}/TextureSamplerMS.cpp (55%) rename {client/ramses-client => src/client}/impl/TextureUtils.cpp (60%) create mode 100644 src/client/impl/TextureUtils.h create mode 100644 src/client/impl/UniformInput.cpp rename {client/logic/lib/impl => src/client/impl/logic}/AnchorPoint.cpp (57%) rename {client/logic/lib/impl => src/client/impl/logic}/AnchorPointImpl.cpp (67%) rename {client/logic/lib/impl => src/client/impl/logic}/AnchorPointImpl.h (77%) rename {client/logic/lib/impl => src/client/impl/logic}/AnimationNode.cpp (75%) rename {client/logic/lib/impl => src/client/impl/logic}/AnimationNodeConfig.cpp (84%) rename {client/logic/lib/impl => src/client/impl/logic}/AnimationNodeConfigImpl.cpp (63%) rename {client/logic/lib/impl => src/client/impl/logic}/AnimationNodeConfigImpl.h (95%) rename {client/logic/lib/impl => src/client/impl/logic}/AnimationNodeImpl.cpp (92%) rename {client/logic/lib/impl => src/client/impl/logic}/AnimationNodeImpl.h (91%) rename client/logic/lib/impl/RamsesAppearanceBinding.cpp => src/client/impl/logic/AppearanceBinding.cpp (53%) rename client/logic/lib/impl/RamsesAppearanceBindingImpl.cpp => src/client/impl/logic/AppearanceBindingImpl.cpp (54%) rename client/logic/lib/impl/RamsesAppearanceBindingImpl.h => src/client/impl/logic/AppearanceBindingImpl.h (68%) rename client/logic/lib/impl/RamsesCameraBinding.cpp => src/client/impl/logic/CameraBinding.cpp (55%) rename client/logic/lib/impl/RamsesCameraBindingImpl.cpp => src/client/impl/logic/CameraBindingImpl.cpp (52%) rename client/logic/lib/impl/RamsesCameraBindingImpl.h => src/client/impl/logic/CameraBindingImpl.h (74%) rename {client/logic/lib/impl => src/client/impl/logic}/Collection.cpp (92%) rename {client/logic/lib/impl => src/client/impl/logic}/DataArray.cpp (84%) rename {client/logic/lib/impl => src/client/impl/logic}/DataArrayImpl.cpp (81%) rename {client/logic/lib/impl => src/client/impl/logic}/DataArrayImpl.h (86%) rename {client/logic/lib/impl => src/client/impl/logic}/Iterator.cpp (95%) create mode 100644 src/client/impl/logic/LogicEngine.cpp create mode 100644 src/client/impl/logic/LogicEngineImpl.cpp create mode 100644 src/client/impl/logic/LogicEngineImpl.h rename {client/logic/lib/impl => src/client/impl/logic}/LogicEngineReport.cpp (94%) rename {client/logic/lib/impl => src/client/impl/logic}/LogicEngineReportImpl.cpp (96%) rename {client/logic/lib/impl => src/client/impl/logic}/LogicEngineReportImpl.h (94%) rename {client/logic/lib/impl => src/client/impl/logic}/LogicNode.cpp (69%) rename {client/logic/lib/impl => src/client/impl/logic}/LogicNodeImpl.cpp (80%) rename {client/logic/lib/impl => src/client/impl/logic}/LogicNodeImpl.h (85%) create mode 100644 src/client/impl/logic/LogicObject.cpp rename {client/logic/lib/impl => src/client/impl/logic}/LogicObjectImpl.cpp (50%) rename {client/logic/lib/impl => src/client/impl/logic}/LogicObjectImpl.h (65%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaConfig.cpp (84%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaConfigImpl.cpp (76%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaConfigImpl.h (96%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaInterface.cpp (73%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaInterfaceImpl.cpp (79%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaInterfaceImpl.h (80%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaModule.cpp (67%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaModuleImpl.cpp (81%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaModuleImpl.h (86%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaScript.cpp (74%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaScriptImpl.cpp (79%) rename {client/logic/lib/impl => src/client/impl/logic}/LuaScriptImpl.h (83%) rename client/logic/lib/impl/RamsesMeshNodeBinding.cpp => src/client/impl/logic/MeshNodeBinding.cpp (54%) rename client/logic/lib/impl/RamsesMeshNodeBindingImpl.cpp => src/client/impl/logic/MeshNodeBindingImpl.cpp (51%) rename client/logic/lib/impl/RamsesMeshNodeBindingImpl.h => src/client/impl/logic/MeshNodeBindingImpl.h (66%) rename client/logic/lib/impl/RamsesNodeBinding.cpp => src/client/impl/logic/NodeBinding.cpp (56%) rename client/logic/lib/impl/RamsesNodeBindingImpl.cpp => src/client/impl/logic/NodeBindingImpl.cpp (59%) rename client/logic/lib/impl/RamsesNodeBindingImpl.h => src/client/impl/logic/NodeBindingImpl.h (62%) rename {client/logic/lib/impl => src/client/impl/logic}/Property.cpp (78%) rename {client/logic/lib/impl => src/client/impl/logic}/PropertyImpl.cpp (87%) rename {client/logic/lib/impl => src/client/impl/logic}/PropertyImpl.h (96%) rename {client/logic/lib/impl => src/client/impl/logic}/RamsesBinding.cpp (87%) rename {client/logic/lib/impl => src/client/impl/logic}/RamsesBindingImpl.cpp (80%) rename {client/logic/lib/impl => src/client/impl/logic}/RamsesBindingImpl.h (89%) rename client/logic/lib/impl/RamsesRenderGroupBinding.cpp => src/client/impl/logic/RenderGroupBinding.cpp (52%) create mode 100644 src/client/impl/logic/RenderGroupBindingElements.cpp create mode 100644 src/client/impl/logic/RenderGroupBindingElementsImpl.cpp rename client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.h => src/client/impl/logic/RenderGroupBindingElementsImpl.h (95%) rename client/logic/lib/impl/RamsesRenderGroupBindingImpl.cpp => src/client/impl/logic/RenderGroupBindingImpl.cpp (64%) rename client/logic/lib/impl/RamsesRenderGroupBindingImpl.h => src/client/impl/logic/RenderGroupBindingImpl.h (58%) rename client/logic/lib/impl/RamsesRenderPassBinding.cpp => src/client/impl/logic/RenderPassBinding.cpp (52%) rename client/logic/lib/impl/RamsesRenderPassBindingImpl.cpp => src/client/impl/logic/RenderPassBindingImpl.cpp (50%) rename client/logic/lib/impl/RamsesRenderPassBindingImpl.h => src/client/impl/logic/RenderPassBindingImpl.h (66%) rename {client/logic/lib/impl => src/client/impl/logic}/SkinBinding.cpp (67%) rename {client/logic/lib/impl => src/client/impl/logic}/SkinBindingImpl.cpp (67%) rename {client/logic/lib/impl => src/client/impl/logic}/SkinBindingImpl.h (76%) rename {client/logic/lib/impl => src/client/impl/logic}/TimerNode.cpp (73%) rename {client/logic/lib/impl => src/client/impl/logic}/TimerNodeImpl.cpp (77%) rename {client/logic/lib/impl => src/client/impl/logic}/TimerNodeImpl.h (91%) rename {client/ramses-client => src/client}/impl/ramses-utils.cpp (53%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/FontCascade.cpp (93%) rename {client/ramses-text-api => src/client/impl/text}/FontRegistry.cpp (93%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/FontRegistryImpl.cpp (89%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/FontRegistryImpl.h (85%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/Freetype2FontInstance.cpp (90%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/Freetype2FontInstance.h (85%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/Freetype2Wrapper.h (85%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/FreetypeFontFace.cpp (90%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/FreetypeFontFace.h (88%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/GlyphGeometry.h (84%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/GlyphMapping.h (89%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/GlyphTextureAtlas.cpp (82%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/GlyphTextureAtlas.h (90%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/GlyphTexturePage.cpp (91%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/GlyphTexturePage.h (85%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/HarfbuzzFontInstance.cpp (71%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/HarfbuzzFontInstance.h (90%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/LayoutUtils.cpp (98%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/Quad.h (98%) rename {client/ramses-text-api => src/client/impl/text}/TextCache.cpp (92%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/TextCacheImpl.cpp (77%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/TextCacheImpl.h (83%) rename {client/ramses-client/impl/ramses-text => src/client/impl/text}/TextTypesImpl.h (74%) rename {client/ramses-text-api => src/client/impl/text}/UtfUtils.cpp (85%) rename {client/ramses-client/impl => src/client/internal}/ClientApplicationLogic.cpp (85%) rename {client/ramses-client/impl => src/client/internal}/ClientApplicationLogic.h (78%) create mode 100644 src/client/internal/ClientCommands/DumpSceneToFile.cpp rename client/logic/lib/internals/ValidationResults.h => src/client/internal/ClientCommands/DumpSceneToFile.h (58%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/FlushSceneVersion.cpp (74%) create mode 100644 src/client/internal/ClientCommands/FlushSceneVersion.h rename {client/ramses-client/impl => src/client/internal}/ClientCommands/LogMemoryUtils.cpp (72%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/LogMemoryUtils.h (74%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/LogResourceMemoryUsage.cpp (78%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/LogResourceMemoryUsage.h (65%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/PrintSceneList.cpp (78%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/PrintSceneList.h (69%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/SceneCommandBuffer.cpp (100%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/SceneCommandBuffer.h (83%) create mode 100644 src/client/internal/ClientCommands/SceneCommandVisitor.cpp rename {client/ramses-client/impl => src/client/internal}/ClientCommands/SceneCommandVisitor.h (76%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/ValidateCommand.cpp (66%) rename {client/ramses-client/impl => src/client/internal}/ClientCommands/ValidateCommand.h (69%) rename {client/ramses-client/impl => src/client/internal}/DataSlotUtils.cpp (96%) rename {client/ramses-client/impl => src/client/internal}/DataSlotUtils.h (83%) rename {client/ramses-client/impl => src/client/internal}/RamsesVersion.cpp (87%) rename {client/ramses-client/impl => src/client/internal}/RamsesVersion.h (70%) rename framework/FrameworkTestUtils/include/TestPngHeader.h => src/client/internal/VisibilityModeUtils.h (55%) rename {client/ramses-client/impl => src/client/internal}/glslEffectBlock/GLSlang.h (88%) create mode 100644 src/client/internal/glslEffectBlock/GlslEffect.cpp rename {client/ramses-client/impl => src/client/internal}/glslEffectBlock/GlslEffect.h (51%) rename {client/ramses-client/impl => src/client/internal}/glslEffectBlock/GlslLimits.h (96%) create mode 100644 src/client/internal/glslEffectBlock/GlslParser.cpp create mode 100644 src/client/internal/glslEffectBlock/GlslParser.h rename {client/ramses-client/impl => src/client/internal}/glslEffectBlock/GlslToEffectConverter.cpp (92%) rename {client/ramses-client/impl => src/client/internal}/glslEffectBlock/GlslToEffectConverter.h (67%) rename {client/logic/lib/internals => src/client/internal/logic}/ApiObjects.cpp (52%) rename {client/logic/lib/internals => src/client/internal/logic}/ApiObjects.h (64%) rename {client/logic/lib/internals => src/client/internal/logic}/ApiObjectsSerializedSize.cpp (63%) rename {client/logic/lib/internals => src/client/internal/logic}/ApiObjectsSerializedSize.h (95%) rename {client/logic/lib/internals => src/client/internal/logic}/DeserializationMap.h (86%) rename {client/logic/lib/internals => src/client/internal/logic}/DirectedAcyclicGraph.cpp (99%) rename {client/logic/lib/internals => src/client/internal/logic}/DirectedAcyclicGraph.h (100%) rename {client/logic/lib/internals => src/client/internal/logic}/EPropertySemantics.h (100%) rename {client/logic/lib/internals => src/client/internal/logic}/EnvironmentProtection.cpp (99%) rename {client/logic/lib/internals => src/client/internal/logic}/EnvironmentProtection.h (98%) rename {client/logic/lib/internals => src/client/internal/logic}/FileUtils.cpp (100%) rename {client/logic/lib/internals => src/client/internal/logic}/FileUtils.h (100%) rename {client/logic/lib/internals => src/client/internal/logic}/InterfaceTypeFunctions.cpp (96%) rename {client/logic/lib/internals => src/client/internal/logic}/InterfaceTypeFunctions.h (94%) rename {client/logic/lib/internals => src/client/internal/logic}/InterfaceTypeInfo.h (89%) rename {client/logic/lib/internals => src/client/internal/logic}/LogicNodeDependencies.cpp (80%) rename {client/logic/lib/internals => src/client/internal/logic}/LogicNodeDependencies.h (94%) rename {client/logic/lib/internals => src/client/internal/logic}/LogicNodeUpdateStatistics.cpp (58%) rename {client/logic/lib/internals => src/client/internal/logic}/LogicNodeUpdateStatistics.h (87%) rename {client/logic/lib/internals => src/client/internal/logic}/LuaCompilationUtils.cpp (84%) rename {client/logic/lib/internals => src/client/internal/logic}/LuaCompilationUtils.h (97%) rename {client/logic/lib/internals => src/client/internal/logic}/LuaCustomizations.cpp (96%) rename {client/logic/lib/internals => src/client/internal/logic}/LuaCustomizations.h (98%) rename {client/logic/lib/internals => src/client/internal/logic}/LuaTypeConversions.cpp (98%) rename {client/logic/lib/internals => src/client/internal/logic}/LuaTypeConversions.h (95%) rename {client/logic/lib/internals => src/client/internal/logic}/PropertyTypeExtractor.cpp (97%) rename {client/logic/lib/internals => src/client/internal/logic}/PropertyTypeExtractor.h (93%) rename {client/logic/lib/internals => src/client/internal/logic}/RamsesHelper.h (92%) rename {client/logic/lib/internals => src/client/internal/logic}/RamsesObjectResolver.cpp (84%) rename {client/logic/lib/internals => src/client/internal/logic}/RamsesObjectResolver.h (95%) rename {client/logic/lib/internals => src/client/internal/logic}/SerializationHelper.h (95%) rename {client/logic/lib/internals => src/client/internal/logic}/SerializationMap.h (84%) rename {client/logic/lib/internals => src/client/internal/logic}/SolHelper.h (94%) rename {client/logic/lib/internals => src/client/internal/logic}/SolState.cpp (92%) rename {client/logic/lib/internals => src/client/internal/logic}/SolState.h (86%) rename {client/logic/lib/internals => src/client/internal/logic}/SolWrapper.h (98%) rename {client/logic/lib/internals => src/client/internal/logic}/StdFilesystemWrapper.h (100%) rename {client/logic/lib/internals => src/client/internal/logic}/TypeData.cpp (97%) rename {client/logic/lib/internals => src/client/internal/logic}/TypeData.h (98%) rename {client/logic/lib/internals => src/client/internal/logic}/TypeUtils.h (84%) rename {client/logic/lib/internals => src/client/internal/logic}/UpdateReport.cpp (98%) rename {client/logic/lib/internals => src/client/internal/logic}/UpdateReport.h (93%) rename {client/logic/lib/internals => src/client/internal/logic}/WrappedLuaProperty.cpp (98%) rename {client/logic/lib/internals => src/client/internal/logic}/WrappedLuaProperty.h (96%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/AnchorPointGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/AnimationNodeGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/ApiObjectsGen.h (81%) rename client/logic/lib/flatbuffers/generated/RamsesAppearanceBindingGen.h => src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h (71%) rename client/logic/lib/flatbuffers/generated/RamsesMeshNodeBindingGen.h => src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h (57%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/DataArrayGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/LinkGen.h (100%) create mode 100644 src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/LogicObjectGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/LuaInterfaceGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/LuaModuleGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/LuaScriptGen.h (100%) rename client/logic/lib/flatbuffers/generated/RamsesCameraBindingGen.h => src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h (57%) rename client/logic/lib/flatbuffers/generated/RamsesNodeBindingGen.h => src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h (61%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/PropertyGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/RamsesBindingGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/RamsesReferenceGen.h (100%) rename client/logic/lib/flatbuffers/generated/RamsesRenderGroupBindingGen.h => src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h (78%) rename client/logic/lib/flatbuffers/generated/RamsesRenderPassBindingGen.h => src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h (55%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/SkinBindingGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/generated/TimerNodeGen.h (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/AnchorPoint.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/AnimationNode.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/ApiObjects.fbs (65%) rename client/logic/lib/flatbuffers/schemas/RamsesAppearanceBinding.fbs => src/client/internal/logic/flatbuffers/schemas/AppearanceBinding.fbs (95%) rename client/logic/lib/flatbuffers/schemas/RamsesCameraBinding.fbs => src/client/internal/logic/flatbuffers/schemas/CameraBinding.fbs (95%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/DataArray.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/Link.fbs (100%) create mode 100644 src/client/internal/logic/flatbuffers/schemas/LogicEngine.fbs rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/LogicObject.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/LuaInterface.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/LuaModule.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/LuaScript.fbs (100%) rename client/logic/lib/flatbuffers/schemas/RamsesMeshNodeBinding.fbs => src/client/internal/logic/flatbuffers/schemas/MeshNodeBinding.fbs (95%) rename client/logic/lib/flatbuffers/schemas/RamsesNodeBinding.fbs => src/client/internal/logic/flatbuffers/schemas/NodeBinding.fbs (96%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/Property.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/RamsesBinding.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/RamsesReference.fbs (100%) rename client/logic/lib/flatbuffers/schemas/RamsesRenderGroupBinding.fbs => src/client/internal/logic/flatbuffers/schemas/RenderGroupBinding.fbs (95%) rename client/logic/lib/flatbuffers/schemas/RamsesRenderPassBinding.fbs => src/client/internal/logic/flatbuffers/schemas/RenderPassBinding.fbs (94%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/SkinBinding.fbs (100%) rename {client/logic/lib => src/client/internal/logic}/flatbuffers/schemas/TimerNode.fbs (100%) create mode 100644 src/framework/CMakeLists.txt rename {framework/ramses-framework/include => src/framework/impl}/APILoggingHelper.h (87%) rename {framework/ramses-framework/include => src/framework/impl}/APILoggingMacros.h (95%) rename {framework/ramses-framework/src => src/framework/impl}/AppearanceEnums.cpp (97%) rename {framework/ramses-framework/include => src/framework/impl}/AppearanceEnumsImpl.h (71%) rename {framework/ramses-framework/include => src/framework/impl}/CommandT.h (90%) create mode 100644 src/framework/impl/DataTypeUtils.h rename {framework/ramses-framework/include => src/framework/impl}/DataTypesImpl.h (75%) create mode 100644 src/framework/impl/EFeatureLevelImpl.h create mode 100644 src/framework/impl/ErrorReporting.cpp rename client/ramses-client/impl/IRamsesObjectRegistry.h => src/framework/impl/ErrorReporting.h (50%) rename {framework/ramses-framework/src => src/framework/impl}/FrameworkFactoryRegistry.cpp (78%) rename {framework/ramses-framework/include => src/framework/impl}/FrameworkFactoryRegistry.h (88%) rename {framework/ramses-framework/src => src/framework/impl}/PublicRamshCommand.cpp (92%) rename {framework/ramses-framework/include => src/framework/impl}/PublicRamshCommand.h (72%) create mode 100644 src/framework/impl/RamsesFramework.cpp create mode 100644 src/framework/impl/RamsesFrameworkConfig.cpp rename {framework/ramses-framework/src => src/framework/impl}/RamsesFrameworkConfigImpl.cpp (66%) create mode 100644 src/framework/impl/RamsesFrameworkConfigImpl.h rename {framework/ramses-framework/src => src/framework/impl}/RamsesFrameworkImpl.cpp (66%) create mode 100644 src/framework/impl/RamsesFrameworkImpl.h create mode 100644 src/framework/impl/RamsesFrameworkTypesImpl.cpp rename {framework/ramses-framework/include => src/framework/impl}/RamsesFrameworkTypesImpl.h (65%) create mode 100644 src/framework/impl/RamsesObject.cpp rename {framework/ramses-framework/include => src/framework/impl}/RamsesObjectFactoryInterfaces.h (74%) create mode 100644 src/framework/impl/RamsesObjectImpl.cpp create mode 100644 src/framework/impl/RamsesObjectImpl.h rename {client/ramses-client => src/framework}/impl/RamsesObjectTypeTraits.h (86%) create mode 100644 src/framework/impl/RamsesObjectTypeUtils.cpp create mode 100644 src/framework/impl/RamsesObjectTypeUtils.h rename {framework/ramses-framework/src => src/framework/impl}/RamsesVersion.cpp (94%) rename {client/ramses-client => src/framework}/impl/SerializationContext.cpp (72%) rename {client/ramses-client => src/framework}/impl/SerializationContext.h (63%) rename {framework/ramses-framework/src => src/framework/impl}/TCPConfig.cpp (96%) rename {framework/ramses-framework/include => src/framework/impl}/TCPConfig.h (90%) create mode 100644 src/framework/impl/TextureEnums.cpp rename client/ramses-client-api/TextureEnums.cpp => src/framework/impl/TextureEnumsImpl.h (76%) create mode 100644 src/framework/impl/ThreadWatchdogConfig.h create mode 100644 src/framework/impl/ValidationReport.cpp create mode 100644 src/framework/impl/ValidationReportImpl.cpp create mode 100644 src/framework/impl/ValidationReportImpl.h rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/CommunicationSystemFactory.cpp (65%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/CommunicationSystemFactory.h (75%) rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/ConnectionStatusUpdateNotifier.cpp (80%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/ConnectionStatusUpdateNotifier.h (65%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/EConnectionProtocol.h (66%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/EConnectionStatus.h (87%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/FakeConnectionStatusUpdateNotifier.h (72%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/FakeConnectionSystem.h (86%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/FakeDiscoveryDaemon.h (82%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/ICommunicationSystem.h (76%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/IConnectionStatusListener.h (80%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/IConnectionStatusUpdateNotifier.h (80%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/IDiscoveryDaemon.h (81%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/ISceneUpdateSerializer.h (68%) rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/LogConnectionInfo.cpp (84%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/LogConnectionInfo.h (85%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/RamsesTransportProtocolVersion.h (75%) rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/SceneUpdateSerializationHelper.cpp (85%) create mode 100644 src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/SceneUpdateSerializer.cpp (77%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/SceneUpdateSerializer.h (77%) rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/SceneUpdateStreamDeserializer.cpp (88%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/SceneUpdateStreamDeserializer.h (82%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/ServiceHandlerInterfaces.h (71%) rename {framework/Communication/TransportCommon/src => src/framework/internal/Communication/TransportCommon}/SingleSceneUpdateWriter.cpp (89%) rename {framework/Communication/TransportCommon/include => src/framework/internal/Communication}/TransportCommon/SingleSceneUpdateWriter.h (75%) rename {framework/Communication/TransportTCP/include => src/framework/internal/Communication}/TransportTCP/AsioWrapper.h (96%) rename {framework/Communication/TransportTCP/include => src/framework/internal/Communication}/TransportTCP/EMessageId.h (74%) rename {framework/Communication/TransportTCP/include => src/framework/internal/Communication}/TransportTCP/NetworkParticipantAddress.h (79%) rename {framework/Communication/TransportTCP/src => src/framework/internal/Communication/TransportTCP}/TCPConnectionSystem.cpp (95%) rename {framework/Communication/TransportTCP/include => src/framework/internal/Communication}/TransportTCP/TCPConnectionSystem.h (85%) rename {framework/Communication/TransportTCP/src => src/framework/internal/Communication/TransportTCP}/TcpDiscoveryDaemon.cpp (79%) rename {framework/Communication/TransportTCP/include => src/framework/internal/Communication}/TransportTCP/TcpDiscoveryDaemon.h (66%) rename {framework/Components/src => src/framework/internal/Components}/ClientSceneLogicBase.cpp (78%) rename {framework/Components/include => src/framework/internal}/Components/ClientSceneLogicBase.h (80%) rename {framework/Components/src => src/framework/internal/Components}/ClientSceneLogicDirect.cpp (72%) rename {framework/Components/include => src/framework/internal}/Components/ClientSceneLogicDirect.h (83%) rename {framework/Components/src => src/framework/internal/Components}/ClientSceneLogicShadowCopy.cpp (81%) rename {framework/Components/include => src/framework/internal}/Components/ClientSceneLogicShadowCopy.h (76%) rename {framework/Components/include => src/framework/internal}/Components/ERendererToClientEventType.h (82%) rename {framework/Components/src => src/framework/internal/Components}/EffectUniformTime.cpp (92%) rename {framework/Components/include => src/framework/internal}/Components/EffectUniformTime.h (84%) rename {framework/Components/include => src/framework/internal}/Components/FileInputStreamContainer.h (78%) rename {framework/Components/include => src/framework/internal}/Components/FlushInformation.h (87%) rename {framework/Components/include => src/framework/internal}/Components/FlushTimeInformation.h (89%) rename {framework/Components/include => src/framework/internal}/Components/IManagedResourceDeleterCallback.h (80%) rename {framework/Components/include => src/framework/internal}/Components/IResourceHashUsageCallback.h (74%) rename {framework/Components/include => src/framework/internal}/Components/IResourceProviderComponent.h (81%) rename {framework/Components/include => src/framework/internal}/Components/ISceneGraphConsumerComponent.h (80%) rename {framework/Components/include => src/framework/internal}/Components/ISceneGraphProviderComponent.h (76%) rename {framework/Components/include => src/framework/internal}/Components/ISceneGraphSender.h (87%) rename {framework/Components/include => src/framework/internal}/Components/ISceneProviderEventConsumer.h (88%) rename {framework/Components/include => src/framework/internal}/Components/ISceneRendererHandler.h (88%) rename {framework/Components/include => src/framework/internal}/Components/InputStreamContainer.h (84%) rename {framework/Components/include => src/framework/internal}/Components/ManagedResource.h (79%) rename {framework/Components/include => src/framework/internal}/Components/MemoryInputStreamContainer.h (68%) rename {framework/Components/include => src/framework/internal}/Components/OffsetFileInputStreamContainer.h (78%) rename {framework/Components/src => src/framework/internal/Components}/ResourceAvailabilityEvent.cpp (71%) rename {framework/Components/include => src/framework/internal}/Components/ResourceAvailabilityEvent.h (67%) rename {framework/Components/src => src/framework/internal/Components}/ResourceComponent.cpp (84%) rename {framework/Components/include => src/framework/internal}/Components/ResourceComponent.h (86%) rename {framework/Components/include => src/framework/internal}/Components/ResourceDeleterCallingCallback.h (88%) rename {framework/Components/include => src/framework/internal}/Components/ResourceFilesRegistry.h (90%) rename {framework/Components/include => src/framework/internal}/Components/ResourceHashUsage.h (88%) rename {framework/Components/include => src/framework/internal}/Components/ResourceHashUsageCallback.h (88%) rename {framework/Components/src => src/framework/internal/Components}/ResourcePersistation.cpp (85%) rename {framework/Components/include => src/framework/internal}/Components/ResourcePersistation.h (76%) rename {framework/Components/src => src/framework/internal/Components}/ResourceSerializationHelper.cpp (69%) rename {framework/Components/include => src/framework/internal}/Components/ResourceSerializationHelper.h (72%) rename {framework/Components/src => src/framework/internal/Components}/ResourceStorage.cpp (95%) rename {framework/Components/include => src/framework/internal}/Components/ResourceStorage.h (72%) rename {framework/Components/src => src/framework/internal/Components}/ResourceTableOfContents.cpp (92%) rename {framework/Components/include => src/framework/internal}/Components/ResourceTableOfContents.h (74%) rename {framework/Components/include => src/framework/internal}/Components/SceneFileHandle.h (72%) rename {framework/Components/src => src/framework/internal/Components}/SceneGraphComponent.cpp (91%) rename {framework/Components/include => src/framework/internal}/Components/SceneGraphComponent.h (85%) rename {framework/Components/include => src/framework/internal}/Components/SceneUpdate.h (74%) rename {framework/Components/src => src/framework/internal/Components}/SingleResourceSerialization.cpp (85%) rename {framework/Components/include => src/framework/internal}/Components/SingleResourceSerialization.h (81%) rename {framework/Core/Common/include => src/framework/internal/Core}/Common/BitForgeMacro.h (85%) rename {framework/Core/Common/include => src/framework/internal/Core}/Common/MemoryHandle.h (81%) rename {framework/Core/Common/src => src/framework/internal/Core/Common}/ParticipantIdentifier.cpp (84%) rename {framework/Core/Common/include => src/framework/internal/Core}/Common/ParticipantIdentifier.h (91%) create mode 100644 src/framework/internal/Core/Common/StronglyTypedValue.h rename {framework/Core/Common/include => src/framework/internal/Core}/Common/TypedMemoryHandle.h (81%) rename {framework/Core/Math3d/include => src/framework/internal/Core}/Math3d/CameraMatrixHelper.h (95%) rename {framework/Core/Math3d/src => src/framework/internal/Core/Math3d}/ProjectionParams.cpp (94%) rename {framework/Core/Math3d/include => src/framework/internal/Core}/Math3d/ProjectionParams.h (87%) rename {framework/Core/Math3d/src => src/framework/internal/Core/Math3d}/Quad.cpp (93%) rename {framework/Core/Math3d/include => src/framework/internal/Core}/Math3d/Quad.h (88%) rename {framework/Core/Math3d/src => src/framework/internal/Core/Math3d}/Rotation.cpp (94%) rename {framework/Core/Math3d/include => src/framework/internal/Core}/Math3d/Rotation.h (84%) rename {framework/Core/TaskFramework/src => src/framework/internal/Core/TaskFramework}/EnqueueOnlyOneAtATimeQueue.cpp (81%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/EnqueueOnlyOneAtATimeQueue.h (89%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/ITask.h (86%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/ITaskFinishHandler.h (86%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/ITaskQueue.h (80%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/ProcessingTaskQueue.h (90%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/RefCounted.h (93%) rename {framework/Core/TaskFramework/src => src/framework/internal/Core/TaskFramework}/TaskExecutingThread.cpp (93%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/TaskExecutingThread.h (92%) rename {framework/Core/TaskFramework/src => src/framework/internal/Core/TaskFramework}/TaskExecutingThreadPool.cpp (89%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/TaskExecutingThreadPool.h (89%) rename {framework/Core/TaskFramework/src => src/framework/internal/Core/TaskFramework}/TaskFinishHandlerDecorator.cpp (86%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/TaskFinishHandlerDecorator.h (84%) rename {framework/Core/TaskFramework/src => src/framework/internal/Core/TaskFramework}/TaskForwardingQueue.cpp (87%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/TaskForwardingQueue.h (90%) rename {framework/Core/TaskFramework/src => src/framework/internal/Core/TaskFramework}/ThreadedTaskExecutor.cpp (91%) rename {framework/Core/TaskFramework/include => src/framework/internal/Core}/TaskFramework/ThreadedTaskExecutor.h (88%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/Adler32Checksum.h (78%) rename {framework/Core/Utils/AndroidLogger/src => src/framework/internal/Core/Utils/AndroidLogger}/AndroidLogAppender.cpp (87%) rename {framework/Core/Utils/AndroidLogger/include => src/framework/internal/Core/Utils}/AndroidLogger/AndroidLogAppender.h (81%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/AssertMovable.h (92%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/BinaryFileInputStream.h (82%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/BinaryFileOutputStream.h (82%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/BinaryInputStream.h (73%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/BinaryOffsetFileInputStream.cpp (78%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/BinaryOffsetFileInputStream.h (82%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/BinaryOutputStream.h (67%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/ConsoleLogAppender.cpp (84%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/ConsoleLogAppender.h (85%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/DataTypeUtils.h (96%) create mode 100644 src/framework/internal/Core/Utils/EnumTraits.h rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/File.cpp (51%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/File.h (58%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/HandlePool.h (89%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/IPeriodicLogSupplier.h (79%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/Image.cpp (88%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/Image.h (91%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/InplaceStringTokenizer.h (93%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/LogAppenderBase.h (86%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/LogContext.h (93%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/LogHelper.cpp (84%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/LogHelper.h (82%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/LogLevel.h (78%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/LogMacros.cpp (93%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/LogMacros.h (58%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/LogMessage.h (90%) create mode 100644 src/framework/internal/Core/Utils/LoggingUtils.h rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/MemoryPool.h (97%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/MemoryPoolExplicit.h (96%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/MemoryPoolIterator.h (95%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/MemoryUtils.h (70%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/PeriodicLogger.cpp (95%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/PeriodicLogger.h (78%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/PeriodicLoggerHelper.h (88%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/RamsesLogger.cpp (89%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/RamsesLogger.h (80%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/RawBinaryOutputStream.h (67%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/StatisticCollection.cpp (96%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/StatisticCollection.h (94%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/StringUtils.cpp (87%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/StringUtils.h (91%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/TextureMathUtils.h (94%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/ThreadBarrier.h (80%) rename {framework/Core/Utils/src => src/framework/internal/Core/Utils}/ThreadLocalLog.cpp (93%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/ThreadLocalLog.h (51%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/ThreadLocalLogForced.h (94%) create mode 100644 src/framework/internal/Core/Utils/UserLogAppender.cpp rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/UserLogAppender.h (71%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/VectorBinaryOutputStream.h (63%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/VoidOutputStream.h (67%) rename {framework/Core/Utils/include => src/framework/internal/Core}/Utils/Warnings.h (97%) rename framework/ramses-framework-api/include/ramses-framework-api/EValidationSeverity.h => src/framework/internal/DltLogAppender/DltAdapter.h (63%) rename {framework/DltLogAppender/src/DltAdapterDummy => src/framework/internal/DltLogAppender}/DltAdapterDummy.cpp (88%) rename {framework/DltLogAppender/include/DltLogAppender/DltAdapterDummy => src/framework/internal/DltLogAppender}/DltAdapterDummy.h (71%) rename {framework/DltLogAppender/src => src/framework/internal/DltLogAppender}/DltAdapterImpl/DltAdapterImpl.cpp (62%) rename {framework/DltLogAppender/include => src/framework/internal}/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h (77%) rename {framework/DltLogAppender/src => src/framework/internal/DltLogAppender}/DltLogAppender.cpp (89%) rename {framework/DltLogAppender/include => src/framework/internal}/DltLogAppender/DltLogAppender.h (80%) rename {framework/DltLogAppender/include => src/framework/internal}/DltLogAppender/IDltAdapter.h (86%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/BlockingQueue.h (94%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/Guid.h (81%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/HashMap.h (94%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/HashSet.h (96%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/HeapArray.h (87%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/IInputStream.h (87%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/IOutputStream.h (92%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/Pair.h (80%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/StringOutputStream.h (78%) rename {framework/PlatformAbstraction/include => src/framework/internal/PlatformAbstraction}/Collections/Vector.h (92%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/ConsoleInput.cpp (93%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/ConsoleInput.h (84%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/FmtBase.h (74%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/Guid.cpp (86%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/Hash.h (85%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/Macros.h (90%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/MinimalWindowsH.h (94%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/PlatformConsole.cpp (80%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformConsole.h (96%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformEnvironmentVariables.h (90%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformError.h (73%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformEvent.h (91%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformLock.h (86%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformMath.h (92%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformMemory.h (88%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/PlatformSignal.cpp (89%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformSignal.h (88%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformStringUtils.h (91%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformThread.h (91%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/PlatformTime.h (96%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/PlatformTypes.cpp (86%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/Runnable.h (90%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/StringOutputStream.cpp (86%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/VariantWrapper.h (77%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/internal/Thread_Posix.h (91%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/internal/Thread_std.h (86%) rename {framework/PlatformAbstraction/src => src/framework/internal/PlatformAbstraction}/synchronized_clock.cpp (91%) rename {framework/PlatformAbstraction/include => src/framework/internal}/PlatformAbstraction/synchronized_clock.h (94%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/Ramsh.cpp (86%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/Ramsh.h (96%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommand.cpp (93%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommand.h (89%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandArguments.h (92%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandArgumentsConverter.h (68%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandArgumentsDataProvider.h (93%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandArgumentsUtils.h (94%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandExit.cpp (80%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandExit.h (83%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandPrintBuildConfig.cpp (91%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandPrintBuildConfig.h (78%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandPrintHelp.cpp (80%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandPrintHelp.h (81%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandPrintLogLevels.cpp (85%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandPrintLogLevels.h (80%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandPrintRamsesVersion.cpp (84%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandPrintRamsesVersion.h (78%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandSetConsoleLogLevel.cpp (84%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandSetConsoleLogLevel.h (80%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandSetContextLogLevel.cpp (86%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandSetContextLogLevel.h (80%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommandSetContextLogLevelFilter.cpp (86%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommandSetContextLogLevelFilter.h (79%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommunicationChannelConsole.cpp (91%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommunicationChannelConsole.h (79%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommunicationChannelConsoleSignalHandler.cpp (84%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h (91%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshCommunicationChannelDLT.cpp (54%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshCommunicationChannelDLT.h (66%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshStandardSetup.cpp (58%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshStandardSetup.h (66%) rename {framework/Ramsh/src => src/framework/internal/Ramsh}/RamshTools.cpp (93%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshTools.h (91%) rename {framework/Ramsh/include => src/framework/internal}/Ramsh/RamshTypeInfo.h (92%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/ArrayResource.h (73%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/BufferResource.h (73%) create mode 100644 src/framework/internal/SceneGraph/Resource/EResourceCompressionStatus.h rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/EffectInputInformation.h (83%) rename {framework/SceneGraph/Resource/src => src/framework/internal/SceneGraph/Resource}/EffectResource.cpp (80%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/EffectResource.h (73%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/IResource.h (89%) rename {framework/SceneGraph/Resource/src => src/framework/internal/SceneGraph/Resource}/LZ4CompressionUtils.cpp (94%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/LZ4CompressionUtils.h (82%) rename {framework/SceneGraph/Resource/src => src/framework/internal/SceneGraph/Resource}/ResourceBase.cpp (94%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/ResourceBase.h (90%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/ResourceInfo.h (80%) create mode 100644 src/framework/internal/SceneGraph/Resource/ResourceTypes.h rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/TextureMetaInfo.h (65%) rename {framework/SceneGraph/Resource/include => src/framework/internal/SceneGraph}/Resource/TextureResource.h (79%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/ActionCollectingScene.cpp (97%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ActionCollectingScene.h (86%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ClientScene.h (82%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/DataInstance.h (89%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/DataLayout.h (89%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/DataLayoutCachedScene.cpp (97%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/DataLayoutCachedScene.h (80%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ESceneActionId.h (98%) create mode 100644 src/framework/internal/SceneGraph/Scene/EScenePublicationMode.h rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ETransformMatrixType.h (85%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/MatrixCacheEntry.h (79%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/ResourceChangeCollectingScene.cpp (92%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ResourceChangeCollectingScene.h (78%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/ResourceChanges.cpp (88%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ResourceChanges.h (89%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/Scene.cpp (96%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/Scene.h (77%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/SceneActionApplier.cpp (87%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/SceneActionApplier.h (83%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/SceneActionCollection.cpp (86%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/SceneActionCollection.h (90%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/SceneActionCollectionCreator.cpp (96%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/SceneActionCollectionCreator.h (87%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/SceneDescriber.cpp (95%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/SceneDescriber.h (95%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/ScenePersistation.cpp (88%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/ScenePersistation.h (87%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/TopologyNode.h (73%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/TopologyTransform.h (77%) rename {framework/SceneGraph/Scene/src => src/framework/internal/SceneGraph/Scene}/TransformationCachedScene.cpp (95%) rename {framework/SceneGraph/Scene/include => src/framework/internal/SceneGraph}/Scene/TransformationCachedScene.h (90%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/BlitPass.h (77%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/Camera.h (82%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/DataFieldInfo.h (86%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/DataSlot.h (68%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/ECameraProjectionType.h (62%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/EDataBufferType.h (76%) create mode 100644 src/framework/internal/SceneGraph/SceneAPI/EDataSlotType.h rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/EDataType.h (94%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/EFixedSemantics.h (86%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/ERenderableDataSlotType.h (71%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/ERotationType.h (57%) create mode 100644 src/framework/internal/SceneGraph/SceneAPI/EShaderStage.h create mode 100644 src/framework/internal/SceneGraph/SceneAPI/EShaderWarningCategory.h rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/GeometryDataBuffer.h (70%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/Handles.h (94%) create mode 100644 src/framework/internal/SceneGraph/SceneAPI/IScene.h rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/MipMapSize.h (83%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/PickableObject.h (79%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/PixelRectangle.h (74%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RenderBuffer.h (62%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RenderGroup.h (76%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RenderGroupUtils.h (94%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RenderPass.h (73%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RenderState.h (76%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RenderTarget.h (79%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/Renderable.h (73%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/RendererSceneState.h (66%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/ResourceContentHash.h (63%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/ResourceField.h (64%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/SceneCreationInformation.h (83%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/SceneId.h (76%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/SceneReference.h (78%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/SceneSizeInformation.h (94%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/SceneTypes.h (87%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/SceneVersionTag.h (70%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/TextureBuffer.h (72%) create mode 100644 src/framework/internal/SceneGraph/SceneAPI/TextureEnums.h rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/TextureSampler.h (71%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/TextureSamplerStates.h (56%) rename {framework/SceneGraph/SceneAPI/include => src/framework/internal/SceneGraph}/SceneAPI/Viewport.h (88%) rename {framework/SceneGraph/SceneUtils/src => src/framework/internal/SceneGraph/SceneUtils}/DataInstanceHelper.cpp (64%) rename {framework/SceneGraph/SceneUtils/include => src/framework/internal/SceneGraph}/SceneUtils/DataInstanceHelper.h (84%) rename {framework/SceneGraph/SceneUtils/src => src/framework/internal/SceneGraph/SceneUtils}/DataLayoutCreationHelper.cpp (93%) rename {framework/SceneGraph/SceneUtils/include => src/framework/internal/SceneGraph}/SceneUtils/DataLayoutCreationHelper.h (82%) rename {framework/SceneGraph/SceneUtils/include => src/framework/internal/SceneGraph}/SceneUtils/ISceneDataArrayAccessor.h (91%) rename {framework/SceneGraph/SceneUtils/src => src/framework/internal/SceneGraph/SceneUtils}/ResourceUtils.cpp (69%) rename {framework/SceneGraph/SceneUtils/include => src/framework/internal/SceneGraph}/SceneUtils/ResourceUtils.h (94%) rename {framework/SceneReferencing/src => src/framework/internal/SceneReferencing}/SceneReferenceAction.cpp (95%) rename {framework/SceneReferencing/include => src/framework/internal}/SceneReferencing/SceneReferenceAction.h (64%) rename {framework/SceneReferencing/src => src/framework/internal/SceneReferencing}/SceneReferenceEvent.cpp (73%) rename {framework/SceneReferencing/include => src/framework/internal}/SceneReferencing/SceneReferenceEvent.h (78%) rename {framework/Watchdog/include => src/framework/internal}/Watchdog/IThreadAliveNotifier.h (84%) rename {framework/Watchdog/src => src/framework/internal/Watchdog}/PlatformWatchdog.cpp (88%) rename {framework/Watchdog/include => src/framework/internal}/Watchdog/PlatformWatchdog.h (56%) rename {framework/Watchdog/include => src/framework/internal}/Watchdog/ThreadAliveNotifierMock.h (85%) rename {framework/Watchdog/src => src/framework/internal/Watchdog}/ThreadWatchdog.cpp (94%) rename {framework/Watchdog/include => src/framework/internal}/Watchdog/ThreadWatchdog.h (85%) rename {ramses-cli => src/ramses-cli}/CMakeLists.txt (58%) rename {ramses-cli => src/ramses-cli}/include/ramses-cli.h (94%) rename {ramses-cli => src/ramses-cli}/include/ramses-framework-cli.h (99%) rename {renderer/Platform/Platform_Wayland_Shell_EGL => src/renderer}/CMakeLists.txt (61%) create mode 100644 src/renderer/impl/BinaryShaderCache.cpp rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/BinaryShaderCacheImpl.cpp (60%) create mode 100644 src/renderer/impl/BinaryShaderCacheImpl.h rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/BinaryShaderCacheProxy.cpp (61%) create mode 100644 src/renderer/impl/BinaryShaderCacheProxy.h rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/CommandDispatchingThread.cpp (89%) rename {renderer/ramses-renderer-impl/include => src/renderer/impl}/CommandDispatchingThread.h (80%) create mode 100644 src/renderer/impl/DisplayConfig.cpp create mode 100644 src/renderer/impl/DisplayConfigImpl.cpp create mode 100644 src/renderer/impl/DisplayConfigImpl.h create mode 100644 src/renderer/impl/RamsesRenderer.cpp create mode 100644 src/renderer/impl/RamsesRendererImpl.cpp create mode 100644 src/renderer/impl/RamsesRendererImpl.h create mode 100644 src/renderer/impl/RendererConfig.cpp rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/RendererConfigImpl.cpp (52%) create mode 100644 src/renderer/impl/RendererConfigImpl.h rename {renderer/ramses-renderer-impl/include => src/renderer/impl}/RendererEventChainer.h (97%) rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/RendererFactory.cpp (85%) rename {renderer/ramses-renderer-impl/include => src/renderer/impl}/RendererFactory.h (73%) rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/RendererMate.cpp (80%) rename {renderer/ramses-renderer-impl/include => src/renderer/impl}/RendererMate.h (88%) rename {renderer/ramses-renderer-impl/src => src/renderer/impl}/RendererMateRamshCommands.cpp (71%) rename {renderer/ramses-renderer-impl/include => src/renderer/impl}/RendererMateRamshCommands.h (71%) create mode 100644 src/renderer/impl/RendererSceneControl.cpp create mode 100644 src/renderer/impl/RendererSceneControlImpl.cpp create mode 100644 src/renderer/impl/RendererSceneControlImpl.h rename {renderer/Platform/Platform_Android_EGL/src => src/renderer/internal/Platform/Android}/Platform_Android_EGL.cpp (81%) rename {renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL => src/renderer/internal/Platform/Android}/Platform_Android_EGL.h (78%) rename {renderer/Platform/Platform_Android_EGL/src => src/renderer/internal/Platform/Android}/Window_Android.cpp (80%) rename {renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL => src/renderer/internal/Platform/Android}/Window_Android.h (87%) create mode 100644 src/renderer/internal/Platform/CMakeLists.txt rename {renderer/Platform/Device_EGL_Extension/src => src/renderer/internal/Platform/Device_EGL_Extension}/Device_EGL_Extension.cpp (94%) rename {renderer/Platform/Device_EGL_Extension/include => src/renderer/internal/Platform}/Device_EGL_Extension/Device_EGL_Extension.h (90%) rename {renderer/Platform/Context_EGL/src => src/renderer/internal/Platform/EGL}/Context_EGL.cpp (95%) rename {renderer/Platform/Context_EGL/include/Context_EGL => src/renderer/internal/Platform/EGL}/Context_EGL.h (83%) rename {renderer/Platform/Platform_EGL/include/Platform_EGL => src/renderer/internal/Platform/EGL}/Platform_EGL.h (70%) rename {renderer/Platform/Device_GL/src => src/renderer/internal/Platform/OpenGL}/DebugOutput.cpp (95%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/DebugOutput.h (84%) rename {renderer/Platform/Device_GL/src => src/renderer/internal/Platform/OpenGL}/Device_GL.cpp (68%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/Device_GL.h (69%) rename {renderer/Platform/Device_GL/src => src/renderer/internal/Platform/OpenGL}/Device_GL_platform.cpp (85%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/Device_GL_platform.h (69%) create mode 100644 src/renderer/internal/Platform/OpenGL/Device_GL_platform_apple.h rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/Device_GL_platform_linux.h (91%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/Device_GL_platform_windows.h (99%) rename {renderer/Platform/Device_GL/src => src/renderer/internal/Platform/OpenGL}/ShaderGPUResource_GL.cpp (90%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/ShaderGPUResource_GL.h (75%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/ShaderProgramInfo.h (83%) rename {renderer/Platform/Device_GL/src => src/renderer/internal/Platform/OpenGL}/ShaderUploader_GL.cpp (69%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/ShaderUploader_GL.h (75%) rename {renderer/Platform/Device_GL/src => src/renderer/internal/Platform/OpenGL}/TypesConversion_GL.cpp (62%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/TypesConversion_GL.h (64%) rename {renderer/Platform/Device_GL/include/Device_GL => src/renderer/internal/Platform/OpenGL}/Types_GL.h (81%) rename {renderer/PlatformFactory/src => src/renderer/internal/Platform}/PlatformFactory.cpp (71%) rename {renderer/PlatformFactory/include/PlatformFactory => src/renderer/internal/Platform}/PlatformFactory.h (82%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/EmbeddedCompositor_Wayland.cpp (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/EmbeddedCompositor_Wayland.h (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IEmbeddedCompositor_Wayland.h (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/INativeWaylandResource.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandBuffer.h (75%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandClient.h (84%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandCompositorConnection.h (82%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandCompositorGlobal.h (80%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandDisplay.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandGlobal.h (79%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandIVIApplicationConnection.h (72%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandIVIApplicationGlobal.h (80%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandIVISurface.h (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandRegion.h (86%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandShellConnection.h (83%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandShellGlobal.h (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandShellSurface.h (92%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/IWaylandSurface.h (88%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabuf.cpp (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabuf.h (85%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufBuffer.h (90%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufConnection.cpp (83%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufConnection.h (90%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufGlobal.cpp (66%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufGlobal.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufParams.cpp (84%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/LinuxDmabufParams.h (93%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/NativeWaylandResource.cpp (88%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/NativeWaylandResource.h (94%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/TextureUploadingAdapter_Wayland.cpp (73%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/TextureUploadingAdapter_Wayland.h (75%) create mode 100644 src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.cpp rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandBuffer.h (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandBufferResource.cpp (58%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandBufferResource.h (72%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandCallbackResource.cpp (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandCallbackResource.h (80%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandClient.cpp (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandClient.h (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandClientCredentials.cpp (90%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandClientCredentials.h (77%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandCompositorConnection.cpp (80%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandCompositorConnection.h (88%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandCompositorGlobal.cpp (76%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandCompositorGlobal.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandDisplay.cpp (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandDisplay.h (76%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandGlobal.cpp (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandGlobal.h (82%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandIVIApplicationConnection.cpp (79%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandIVIApplicationConnection.h (84%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandIVIApplicationGlobal.cpp (76%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandIVIApplicationGlobal.h (86%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandIVISurface.cpp (76%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandIVISurface.h (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandOutputConnection.cpp (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandOutputConnection.h (86%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandOutputGlobal.cpp (79%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandOutputGlobal.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandOutputParams.h (84%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandRegion.cpp (72%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandRegion.h (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandShellConnection.cpp (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandShellConnection.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandShellGlobal.cpp (74%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandShellGlobal.h (86%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandShellSurface.cpp (70%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandShellSurface.h (93%) rename {renderer/Platform/EmbeddedCompositor_Wayland/src => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandSurface.cpp (68%) rename {renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland => src/renderer/internal/Platform/Wayland/EmbeddedCompositor}/WaylandSurface.h (89%) rename {renderer/Platform/Platform_Wayland_IVI_EGL/src => src/renderer/internal/Platform/Wayland/IVI}/Platform_Wayland_IVI_EGL_ES_3_0.cpp (82%) rename {renderer/Platform/Platform_Wayland_IVI_EGL/include/Platform_Wayland_IVI_EGL => src/renderer/internal/Platform/Wayland/IVI}/Platform_Wayland_IVI_EGL_ES_3_0.h (82%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/src => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/IVIControllerScreen.cpp (86%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/IVIControllerScreen.h (85%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/src => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/IVIControllerSurface.cpp (66%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/IVIControllerSurface.h (94%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/src => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/SystemCompositorController_Wayland_IVI.cpp (83%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/SystemCompositorController_Wayland_IVI.h (94%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/src => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/WaylandOutput.cpp (62%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI => src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController}/WaylandOutput.h (91%) rename {renderer/Platform/Window_Wayland_IVI/src => src/renderer/internal/Platform/Wayland/IVI}/Window_Wayland_IVI.cpp (93%) rename {renderer/Platform/Window_Wayland_IVI/include/Window_Wayland_IVI => src/renderer/internal/Platform/Wayland/IVI}/Window_Wayland_IVI.h (89%) rename {renderer/Platform/Window_Wayland/src => src/renderer/internal/Platform/Wayland}/InputHandling_Wayland.cpp (68%) rename {renderer/Platform/Window_Wayland/include/Window_Wayland => src/renderer/internal/Platform/Wayland}/InputHandling_Wayland.h (87%) rename {renderer/Platform/Platform_Wayland_EGL/src => src/renderer/internal/Platform/Wayland}/Logger_Wayland.cpp (90%) rename {renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL => src/renderer/internal/Platform/Wayland}/Logger_Wayland.h (85%) rename {renderer/Platform/Platform_Wayland_EGL/src => src/renderer/internal/Platform/Wayland}/Platform_Wayland_EGL.cpp (64%) rename {renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL => src/renderer/internal/Platform/Wayland}/Platform_Wayland_EGL.h (78%) rename {renderer/Platform/WaylandUtilities/src => src/renderer/internal/Platform/Wayland}/UnixDomainSocket.cpp (91%) rename {renderer/Platform/WaylandUtilities/include/WaylandUtilities => src/renderer/internal/Platform/Wayland}/UnixDomainSocket.h (86%) rename {renderer/Platform/WaylandEGLExtensionProcs/src => src/renderer/internal/Platform/Wayland}/WaylandEGLExtensionProcs.cpp (66%) rename {renderer/Platform/WaylandEGLExtensionProcs/include/WaylandEGLExtensionProcs => src/renderer/internal/Platform/Wayland}/WaylandEGLExtensionProcs.h (78%) rename {renderer/Platform/WaylandUtilities/src => src/renderer/internal/Platform/Wayland}/WaylandEnvironmentUtils.cpp (86%) rename {renderer/Platform/WaylandUtilities/include/WaylandUtilities => src/renderer/internal/Platform/Wayland}/WaylandEnvironmentUtils.h (84%) rename {renderer/Platform/Window_Wayland/src => src/renderer/internal/Platform/Wayland}/Window_Wayland.cpp (90%) rename {renderer/Platform/Window_Wayland/include/Window_Wayland => src/renderer/internal/Platform/Wayland}/Window_Wayland.h (90%) rename {renderer/Platform/Window_Wayland/include/Window_Wayland => src/renderer/internal/Platform/Wayland}/WlContext.h (86%) rename {renderer/Platform/Platform_Wayland_Shell_EGL/src => src/renderer/internal/Platform/Wayland/WlShell}/Platform_Wayland_Shell_EGL_ES_3_0.cpp (79%) rename {renderer/Platform/Platform_Wayland_Shell_EGL/include/Platform_Wayland_Shell_EGL => src/renderer/internal/Platform/Wayland/WlShell}/Platform_Wayland_Shell_EGL_ES_3_0.h (81%) rename {renderer/Platform/Window_Wayland_Shell/src => src/renderer/internal/Platform/Wayland/WlShell}/Window_Wayland_Shell.cpp (84%) rename {renderer/Platform/Window_Wayland_Shell/include/Window_Wayland_Shell => src/renderer/internal/Platform/Wayland/WlShell}/Window_Wayland_Shell.h (90%) rename {renderer/Platform/Context_WGL/src => src/renderer/internal/Platform/Windows}/Context_WGL.cpp (81%) rename {renderer/Platform/Context_WGL/include/Context_WGL => src/renderer/internal/Platform/Windows}/Context_WGL.h (65%) rename {renderer/Platform/Window_Windows/src => src/renderer/internal/Platform/Windows}/HiddenWindow.cpp (96%) rename {renderer/Platform/Window_Windows/include/Window_Windows => src/renderer/internal/Platform/Windows}/HiddenWindow.h (82%) rename {renderer/Platform/Platform_Windows_WGL/src => src/renderer/internal/Platform/Windows}/Platform_Windows_WGL.cpp (52%) create mode 100644 src/renderer/internal/Platform/Windows/Platform_Windows_WGL.h rename {renderer/Platform/Context_WGL/src => src/renderer/internal/Platform/Windows}/WglExtensions.cpp (95%) rename {renderer/Platform/Context_WGL/include/Context_WGL => src/renderer/internal/Platform/Windows}/WglExtensions.h (85%) rename {renderer/Platform/Window_Windows/src => src/renderer/internal/Platform/Windows}/Window_Windows.cpp (89%) rename {renderer/Platform/Window_Windows/include/Window_Windows => src/renderer/internal/Platform/Windows}/Window_Windows.h (89%) rename {renderer/Platform/Platform_X11_EGL/src => src/renderer/internal/Platform/X11}/Platform_X11_EGL.cpp (84%) rename {renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL => src/renderer/internal/Platform/X11}/Platform_X11_EGL.h (82%) rename {renderer/Platform/Platform_X11_EGL/src => src/renderer/internal/Platform/X11}/Window_X11.cpp (84%) rename {renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL => src/renderer/internal/Platform/X11}/Window_X11.h (78%) create mode 100644 src/renderer/internal/Platform/iOS/Platform_iOS_EGL.h create mode 100644 src/renderer/internal/Platform/iOS/Platform_iOS_EGL.mm create mode 100644 src/renderer/internal/Platform/iOS/Window_iOS.h create mode 100644 src/renderer/internal/Platform/iOS/Window_iOS.mm rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/AsyncEffectUploader.cpp (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/AsyncEffectUploader.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/BufferLinks.cpp (98%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/BufferLinks.h (91%) create mode 100644 src/renderer/internal/RendererLib/CMakeLists.txt rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/ConstantLogger.cpp (93%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/ConstantLogger.h (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DataLinkUtils.h (92%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DataReferenceLinkCachedScene.cpp (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DataReferenceLinkCachedScene.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DataReferenceLinkManager.cpp (84%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DataReferenceLinkManager.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplayBundle.cpp (94%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplayBundle.h (83%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplayConfig.cpp (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplayConfig.h (79%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplayController.cpp (80%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplayController.h (81%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplayDispatcher.cpp (94%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplayDispatcher.h (91%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplayEventHandler.cpp (79%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplayEventHandler.h (76%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplaySetup.cpp (97%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplaySetup.h (79%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/DisplayThread.cpp (94%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/DisplayThread.h (88%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/EmbeddedCompositingManager.cpp (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/EmbeddedCompositingManager.h (91%) rename {renderer/RendererLib/RendererLib/include/RendererLib => src/renderer/internal/RendererLib/Enums}/EKeyCode.h (54%) rename client/ramses-client/impl/SceneConfigImpl.cpp => src/renderer/internal/RendererLib/Enums/EKeyEvent.h (60%) create mode 100644 src/renderer/internal/RendererLib/Enums/EKeyModifier.h rename renderer/Platform/Platform_EGL/src/dummy.cpp => src/renderer/internal/RendererLib/Enums/ELoopMode.h (75%) rename renderer/RendererLib/RendererLib/include/RendererLib/EKeyEventType.h => src/renderer/internal/RendererLib/Enums/EMouseEvent.h (50%) rename {renderer/RendererLib/RendererLib/include/RendererLib => src/renderer/internal/RendererLib/Enums}/EResourceStatus.h (77%) rename {renderer/RendererLib/RendererLib/include/RendererLib => src/renderer/internal/RendererLib/Enums}/ESceneState.h (87%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/FrameProfilerStatistics.cpp (88%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/FrameProfilerStatistics.h (88%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/FrameTimer.h (93%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/IRendererResourceManager.h (85%) rename {renderer/RendererLib/RendererFramework/include/RendererFramework => src/renderer/internal/RendererLib}/IRendererSceneEventSender.h (78%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/IRendererSceneStateControl.h (84%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/IRendererSceneUpdater.h (88%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/IResourceDeviceHandleAccessor.h (89%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/IResourceUploader.h (77%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/IntersectionUtils.cpp (95%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/IntersectionUtils.h (92%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/LinkManagerBase.cpp (69%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/LinkManagerBase.h (88%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/LoggingDevice.cpp (84%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/LoggingDevice.h (76%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/PendingSceneResourcesUtils.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/PendingSceneResourcesUtils.h (78%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/Context_Base.cpp (88%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/Context_Base.h (85%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/DeviceResourceMapper.cpp (92%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/DeviceResourceMapper.h (88%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/Device_Base.cpp (69%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/Device_Base.h (91%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/EmbeddedCompositor_Dummy.cpp (85%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/EmbeddedCompositor_Dummy.h (91%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/GpuResource.h (86%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/IndexBufferGPUResource.h (86%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/Platform_Base.cpp (85%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/Platform_Base.h (90%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/RenderBufferGPUResource.h (65%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/RenderTargetGpuResource.cpp (86%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/RenderTargetGpuResource.h (80%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/RendererLimits.cpp (91%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/RendererLimits.h (81%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/ShaderGPUResource.h (85%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/TextureUploadingAdapter_Base.cpp (71%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/TextureUploadingAdapter_Base.h (69%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/VertexArrayGPUResource.h (86%) rename {renderer/RendererLib/Platform_Base/src => src/renderer/internal/RendererLib/PlatformBase}/Window_Base.cpp (81%) rename {renderer/RendererLib/Platform_Base/include/Platform_Base => src/renderer/internal/RendererLib/PlatformBase}/Window_Base.h (92%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IBinaryShaderCache.h (76%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IContext.h (86%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IDevice.h (75%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IDeviceExtension.h (89%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IDisplayController.h (75%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IEmbeddedCompositingManager.h (88%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IEmbeddedCompositor.h (92%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IPlatform.h (93%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IPlatformFactory.h (85%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IRenderBackend.h (92%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IResourceUploadRenderBackend.h (82%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/ISystemCompositorController.h (93%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/ITextureUploadingAdapter.h (57%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IWindow.h (91%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib/PlatformInterface}/IWindowEventHandler.h (60%) create mode 100644 src/renderer/internal/RendererLib/RamshCommands/AssignScene.cpp rename client/logic/lib/internals/ErrorReporting.h => src/renderer/internal/RendererLib/RamshCommands/AssignScene.h (58%) create mode 100644 src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.cpp create mode 100644 src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h create mode 100644 src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.cpp create mode 100644 src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.h rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/LogRendererInfo.cpp (80%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/LogRendererInfo.h (72%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/PrintStatistics.cpp (78%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/PrintStatistics.h (82%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/Screenshot.cpp (66%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/Screenshot.h (86%) create mode 100644 src/renderer/internal/RendererLib/RamshCommands/SetClearColor.cpp rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SetClearColor.h (67%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SetFrameTimeLimits.cpp (89%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SetFrameTimeLimits.h (86%) create mode 100644 src/renderer/internal/RendererLib/RamshCommands/SetSceneState.cpp create mode 100644 src/renderer/internal/RendererLib/RamshCommands/SetSceneState.h rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SetSkippingOfUnmodifiedBuffers.cpp (75%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SetSkippingOfUnmodifiedBuffers.h (83%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerAddSurfaceToLayer.cpp (74%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerAddSurfaceToLayer.h (78%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerDestroySurface.cpp (75%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerDestroySurface.h (77%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerListIviSurfaces.cpp (77%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerListIviSurfaces.h (77%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerRemoveSurfaceFromLayer.cpp (74%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerRemoveSurfaceFromLayer.h (77%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerScreenshot.cpp (82%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerScreenshot.h (79%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetLayerVisibility.cpp (80%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetLayerVisibility.h (77%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetSurfaceDestRectangle.cpp (81%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetSurfaceDestRectangle.h (78%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetSurfaceOpacity.cpp (80%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetSurfaceOpacity.h (77%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetSurfaceVisibility.cpp (79%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/SystemCompositorControllerSetSurfaceVisibility.h (77%) rename {renderer/RendererLib/RendererCommands/src => src/renderer/internal/RendererLib/RamshCommands}/TriggerPickEvent.cpp (79%) rename {renderer/RendererLib/RendererCommands/include/RendererCommands => src/renderer/internal/RendererLib/RamshCommands}/TriggerPickEvent.h (85%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RenderBackend.cpp (86%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RenderBackend.h (90%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RenderExecutor.cpp (95%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/RenderExecutor.h (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/RenderExecutorInternalRenderStates.h (92%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RenderExecutorInternalState.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/RenderExecutorInternalState.h (87%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RenderExecutorLogger.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/RenderExecutorLogger.h (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RenderableComparator.h (94%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/Renderer.cpp (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/Renderer.h (91%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererCachedScene.cpp (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererCachedScene.h (82%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererCommandBuffer.cpp (94%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererCommandBuffer.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererCommandExecutor.cpp (95%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererCommandExecutor.h (96%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererCommandUtils.h (96%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererCommands.h (81%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererConfig.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererConfig.h (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererEvent.h (81%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererEventCollector.cpp (97%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/RendererEventCollector.h (95%) rename {renderer/RendererLib/RendererFramework/src => src/renderer/internal/RendererLib}/RendererFrameworkLogic.cpp (93%) rename {renderer/RendererLib/RendererFramework/include/RendererFramework => src/renderer/internal/RendererLib}/RendererFrameworkLogic.h (84%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererInterruptState.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererLogContext.cpp (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererLogContext.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererLogger.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererLogger.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererPeriodicLogSupplier.cpp (85%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererPeriodicLogSupplier.h (85%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererResourceManager.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererResourceManager.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererResourceRegistry.cpp (98%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererResourceRegistry.h (95%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererSceneControlLogic.cpp (83%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererSceneControlLogic.h (95%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererSceneResourceRegistry.cpp (96%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererSceneResourceRegistry.h (79%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererSceneUpdater.cpp (95%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererSceneUpdater.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererScenes.cpp (97%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererScenes.h (86%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/RendererStatistics.cpp (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RendererStatistics.h (89%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib}/RenderingContext.h (67%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/RenderingPassInfo.h (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/RenderingPassOrderComparator.h (89%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/ResourceCachedScene.cpp (97%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/ResourceCachedScene.h (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/ResourceDescriptor.h (66%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/ResourceUploadRenderBackend.cpp (80%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/ResourceUploadRenderBackend.h (84%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/ResourceUploader.cpp (76%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/ResourceUploader.h (85%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/ResourceUploadingManager.cpp (87%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/ResourceUploadingManager.h (88%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneDependencyChecker.cpp (96%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal/RendererLib}/SceneDependencyChecker.h (84%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneDisplayTracker.cpp (89%) create mode 100644 src/renderer/internal/RendererLib/SceneDisplayTracker.h rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneExpirationMonitor.cpp (96%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneExpirationMonitor.h (88%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneLinkScene.cpp (91%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneLinkScene.h (81%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneLinks.cpp (98%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneLinks.h (91%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneLinksManager.cpp (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneLinksManager.h (90%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneReferenceLogic.cpp (97%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneReferenceLogic.h (92%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneReferenceOwnership.h (87%) rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib}/SceneRenderExecutionIterator.h (90%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneResourceUploader.cpp (73%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneResourceUploader.h (82%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneStateExecutor.cpp (94%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneStateExecutor.h (93%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/SceneStateInfo.cpp (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/SceneStateInfo.h (71%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/StagingInfo.h (81%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/TextureLinkCachedScene.cpp (93%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/TextureLinkCachedScene.h (86%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/TextureLinkManager.cpp (95%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/TextureLinkManager.h (94%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/TransformationLinkCachedScene.cpp (95%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/TransformationLinkCachedScene.h (92%) rename {renderer/RendererLib/RendererLib/src => src/renderer/internal/RendererLib}/TransformationLinkManager.cpp (90%) rename {renderer/RendererLib/RendererLib/include => src/renderer/internal}/RendererLib/TransformationLinkManager.h (89%) create mode 100644 src/renderer/internal/RendererLib/Types.cpp rename {renderer/RendererLib/RendererAPI/include/RendererAPI => src/renderer/internal/RendererLib}/Types.h (59%) create mode 100644 src/shared-lib/CMakeLists.txt rename {client/logic/tools => tests}/CMakeLists.txt (70%) rename {integration/SandwichTests => tests/benchmarks}/CMakeLists.txt (86%) create mode 100644 tests/benchmarks/logic/CMakeLists.txt rename {benchmarks => tests/benchmarks/logic}/animation.cpp (91%) create mode 100644 tests/benchmarks/logic/benchmarksetup.h rename {benchmarks => tests/benchmarks/logic}/compile_lua.cpp (91%) rename {benchmarks => tests/benchmarks/logic}/getproperty.cpp (97%) rename {benchmarks => tests/benchmarks/logic}/links.cpp (83%) rename {benchmarks => tests/benchmarks/logic}/property.cpp (88%) rename {benchmarks => tests/benchmarks/logic}/serialization.cpp (59%) rename {benchmarks => tests/benchmarks/logic}/update.cpp (90%) create mode 100644 tests/integration/CMakeLists.txt create mode 100644 tests/integration/logic-viewer-tests/CMakeLists.txt rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/LogicViewerAppTest.cpp (91%) rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/res/ALogicViewerApp_clearColor.png (100%) rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/res/ALogicViewerApp_clearColorCmdLine.png (100%) rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/res/ALogicViewerApp_red.png (100%) rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/res/ALogicViewerApp_red_500x700.png (100%) rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/res/ALogicViewerApp_white.png (100%) rename {client/logic/tools/ramses-logic-viewer-test => tests/integration/logic-viewer-tests}/res/ALogicViewerApp_yellow.png (100%) rename {integration/PlatformTests/RenderBackendTests => tests/integration/render-backend-tests}/CMakeLists.txt (86%) rename {integration/PlatformTests/RenderBackendTests/src => tests/integration/render-backend-tests}/PlatformTest.cpp (92%) rename {integration/PlatformTests/RenderBackendTests/src => tests/integration/render-backend-tests}/ShaderUploadTest.cpp (67%) rename {integration/PlatformTests/RenderBackendTests/src => tests/integration/render-backend-tests}/main.cpp (83%) rename {renderer/RendererTestUtils => tests/integration/renderer-test-utils}/CMakeLists.txt (71%) create mode 100644 tests/integration/renderer-test-utils/ReadPixelCallbackHandler.h rename {renderer/RendererTestUtils/include => tests/integration/renderer-test-utils}/RendererAndSceneTestEventHandler.h (84%) create mode 100644 tests/integration/renderer-test-utils/RendererTestUtils.cpp create mode 100644 tests/integration/renderer-test-utils/RendererTestUtils.h rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/CMakeLists.txt (52%) create mode 100644 tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferRenderingTests.h create mode 100644 tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.cpp create mode 100644 tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.h rename {integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests => tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests}/main.cpp (90%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h (91%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp (87%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h (86%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h (89%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/NamedPipe.h (85%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/TestForkerApplication.cpp (98%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/TestForkerApplication.h (92%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/TestForkingController.cpp (95%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/TestForkingController.h (94%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/TestSignalHandler.cpp (89%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/TestSignalHandler.h (88%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h (91%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/EmbeddedCompositingTests.h (94%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/EmbeddedCompositingTestsWithFD.cpp (93%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/EmbeddedCompositingTestsWithFD.h (87%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/MultiDisplayStreamTextureTests.cpp (95%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/MultiDisplayStreamTextureTests.h (76%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/MultiSceneStreamTextureTests.cpp (96%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/MultiSceneStreamTextureTests.h (91%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/MultiStreamTextureTests.cpp (96%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/MultiStreamTextureTests.h (92%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp (96%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/OffscreenBuffersWithStreamTexturesTests.h (86%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/SharedMemoryBufferTests.cpp (90%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/SharedMemoryBufferTests.h (81%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/SingleStreamTextureTests.cpp (97%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/SingleStreamTextureTests.h (71%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/StreamBufferTests.cpp (94%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/StreamBufferTests.h (93%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/StreamTextureRendererEventTests.cpp (97%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/StreamTextureRendererEventTests.h (93%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/WaylandApplicationWithRamsesRendererTests.cpp (96%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/WaylandApplicationWithRamsesRendererTests.h (86%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/WaylandOutputTests.cpp (84%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/WaylandOutputTests.h (62%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestCases/main.cpp (70%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/ETriangleColor.h (86%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/OpenGLTriangleDrawer.cpp (94%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/OpenGLTriangleDrawer.h (78%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/SHMBuffer.cpp (93%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/SHMBuffer.h (93%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/SHMTriangleDrawer.cpp (97%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/SHMTriangleDrawer.h (80%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/TestApplicationShellSurfaceId.h (79%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/TestApplicationSurfaceId.h (80%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/TestWaylandApplication.cpp (84%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/TestWaylandApplication.h (80%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/WaylandHandler.cpp (89%) rename {integration/SandwichTests/RendererTests/EmbeddedCompositingTests => tests/integration/renderer-tests/embedded-compositing-rendering-tests}/TestWaylandApplication/WaylandHandler.h (92%) create mode 100644 tests/integration/renderer-tests/renderer-lifecycle-tests/ExternalWindowTests.cpp rename {integration/SandwichTests/RendererTests/RendererLifecycleTests => tests/integration/renderer-tests/renderer-lifecycle-tests}/RendererLifecycleTests.cpp (64%) rename {integration/SandwichTests/RendererTests/RendererLifecycleTests => tests/integration/renderer-tests/renderer-lifecycle-tests}/RendererLifecycleTests.h (74%) rename {integration/SandwichTests/RendererTests/RendererLifecycleTests => tests/integration/renderer-tests/renderer-lifecycle-tests}/main.cpp (88%) rename {integration/SandwichTests/RendererTests/RendererTestFramework => tests/integration/renderer-tests/renderer-test-framework}/IRendererTest.h (62%) create mode 100644 tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.cpp create mode 100644 tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.h create mode 100644 tests/integration/renderer-tests/renderer-test-framework/RenderingTestCase.h rename {integration/SandwichTests/RendererTests/RendererTestFramework => tests/integration/renderer-tests/renderer-test-framework}/TestRenderer.cpp (57%) create mode 100644 tests/integration/renderer-tests/renderer-test-framework/TestRenderer.h create mode 100644 tests/integration/renderer-tests/renderer-test-framework/TestScenes.cpp create mode 100644 tests/integration/renderer-tests/renderer-test-framework/TestScenes.h rename {integration/SandwichTests/RendererTests/RendererTestFramework => tests/integration/renderer-tests/renderer-test-framework}/TestScenesAndRenderer.cpp (77%) rename {integration/SandwichTests/RendererTests/RendererTestFramework => tests/integration/renderer-tests/renderer-test-framework}/TestScenesAndRenderer.h (61%) create mode 100644 tests/integration/renderer-tests/rendering-tests/DataLinkingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/DataLinkingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/RenderingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.h create mode 100644 tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.cpp create mode 100644 tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.h rename {integration/SandwichTests/RendererTests/RenderingTests => tests/integration/renderer-tests/rendering-tests}/main.cpp (92%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_Black.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_HiddenScene.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_ModifiedScenes1.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_ModifiedScenes2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_RenderTarget.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_TwoScenes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_TwoScenesInverseOrdered.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_TwoScenesOrdered.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererDisplays_UnpublishedScene.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererInstance_AfterLoadSave.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererInstance_DynamicResources.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererInstance_SimpleText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererInstance_Three_Triangles.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererInstance_Triangles_reordered.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ARendererInstance_YellowText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/AnimatedTriangleScene_AnimatedScene.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/AntiAliasingScene_MSAAx4.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ArrayInputScene_ArrayInputInt32.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ArrayInputScene_ArrayInputInt32DynamicIndex.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ArrayInputScene_ArrayInputVec4.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/BlitPassTest_BlitsColorBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/BlitPassTest_BlitsDepthBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/BlitPassTest_BlitsDepthStencilBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/BlitPassTest_BlitsSubregion.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/CameraData_FrustumLinked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/CameraData_NoLinks.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/CameraData_ViewportLinked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/CubeTextureScene_CubeMap.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/CubeTextureScene_CubeMapFloat.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/CubeTextureScene_CubeMapSwizzled.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataBufferScene_EquilateralTriangle.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataBufferScene_RedTriangle.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataBufferScene_RedTriangleInverted.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_AllLinksDisabled.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_ConfidenceMultiLink.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_ConsumerLinkedToProvider.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_ConsumerLinkedToProviderNested.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_LinkOverridesComsumerTransform.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_LinkRemoved.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_Linked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_NoLinks.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_RemovedConsumer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DataLinkTest_RemovedProvider.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Display_SetClearColor.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DistributedScene_UnpublishedScene.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DmaOffscreenBufferTest_BufferReadWrite.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DmaOffscreenBufferTest_BufferWrite.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DmaOffscreenBufferTest_BufferWrite_128x256.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DmaOffscreenBufferTest_BufferWrite_128x64.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/DmaOffscreenBufferTest_BufferWrite_256x256.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_BlueTriangleStreamTexture.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_BlueTriangleStreamTexture_SmallRes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_BlueTriangleStreamTexture_WithTexCoordsOffset.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_CanUseStreamTextureInASceneMappedToOffscreenBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_Fallback1_Left_Fallback1_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_Fallback1_Left_Fallback1_Swizzled_Right_Swizzled.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_Fallback1_Left_Fallback2_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_Fallback1_Left_RedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_Fallback1_Left_WhiteTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_FallbackTexture_1.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_FallbackTexture_WithTexCoordsOffset.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_GrayTriangleStreamTexture.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangleStreamTexture.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangleStreamTexture_SmallRes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangle_Left_BlueTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangle_Left_Fallback1_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangle_Left_Fallback1_Swizzled_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangle_Left_RedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_RedTriangle_Left_ShMemRedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemBlueTriangle.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemBlueTriangle_Left_Fallback1_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemBlueTriangle_Left_Fallback1_Swizzled_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemBlueTriangle_Left_RedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemBlueTriangle_Left_ShMemRedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemBlueTriangle_Left_ShMemWhiteTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemGreyTriangle_Left_ShMemWhiteTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemRedTriangle.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemRedTriangle_Left_Fallback1_Swizzled_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemRedTriangle_Left_RedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemRedTriangle_Left_ShMemRedTriangle_Right.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_ShMemWhiteTriangle.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/EC_WhiteTriangleStreamTexture.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryInstanceScene_Instancing.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryInstanceScene_InstancingAndNotInstancing.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryShaderScene_PointsInLineStripOut.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryShaderScene_PointsInPointsOut.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryShaderScene_PointsInTriangleStripOut.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryShaderScene_TrianglesInPointsOut.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/GeometryShaderScene_TrianglesInTriangleStripOut.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/HierarchicalRedTrianglesScene_AllTrianglesInvisible.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/HierarchicalRedTrianglesScene_DeleteMeshNode.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/HierarchicalRedTrianglesScene_RotateAndScale.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/HierarchicalRedTrianglesScene_SomeTrianglesInvisible.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/IndexArray32BitScene_NoOffset16BitIndices.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/IndexArray32BitScene_NoOffset32BitIndices.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/IndexArray32BitScene_Offset16BitIndices.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/IndexArray32BitScene_Offset32BitIndices.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_FBState0.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_FBState1.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_FBState2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_FBState3Linked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles3.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiLanguageScene_MultiLanguageText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiRenderTarget_ClearTwoColorBuffers.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiRenderTarget_ColorBufferWrittenByTwoPasses.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiRenderTarget_DepthBufferUsedByTwoPasses.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiRenderTarget_DepthRead.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiRenderTarget_OneColorBufferWritten.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiRenderTarget_TwoColorBuffers.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiTypeLinkTest_Linked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultiTypeLinkTest_NoLinks.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleGeometryScene_MultipleGeometry.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTriangleScene_EulerRotationConventions.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_AdditiveBlending.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_AlphaBlending.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_BlendingConstant.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_BlendingDstColorAndAlpha.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_CameraTransformation.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_ColorMask.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_DepthFunc.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_DepthFunc_NoDepthBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_DrawMode.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_FaceCulling.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_OrthographicCamera.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_PerspectiveCamera.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_RenderingOrderChanged.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_ScissorTest.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_StencilTest_1.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_StencilTest_1_NoStencilBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_StencilTest_2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_StencilTest_3.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_Subimages_middle.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_SubtractiveBlending.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_ThreeTriangles.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/MultipleTrianglesScene_ThreeTrianglesRed.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_Black.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_BlackOB.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_BufferDestroyed.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_DepthTest.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_Linked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_Linked2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_Linked2_OB_Content.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedCustomClearColor.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedFinished.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedFinished2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedInterrupted.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedInterrupted2.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedTwoConsumers.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedTwoScenes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_Linked_OB_Content.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_MSAABuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_StencilTest.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_Unlinked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/OffscreenBufferLinkTest_UnlinkedMSAABuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ReferencedScenes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderBuffer_MsaaSampleCount2Blit.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderBuffer_MsaaSampleCount4Blit.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderBuffer_MsaaSampleCount4TexelFetch.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderBuffer_OneColorBufferNoDepthOrStencil.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer.PNG (100%) create mode 100644 tests/integration/renderer-tests/res/RenderBuffer_ReadWriteDepthBuffer.PNG rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_Color.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_ColorDepth.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_ColorStencil.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_ColorStencilDepth.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_Depth.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_None.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_Stencil.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassClear_StencilDepth.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassOnce_Initial.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassOnce_Retriggered.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassScene_LeftRightViewport.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassScene_MeshInMultiplePasses.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassScene_MeshNotInPass.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassScene_OneMeshPerPass.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderPassScene_PassesWithDifferentRenderOrder.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderTargetScene_OrthographicProjection.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderTargetScene_PerspectiveProjection.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderTargetScene_Red.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderTargetScene_RedGreen.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderTargetScene_RedGreenBlue.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/RenderTargetScene_RedGreenBlue_NoAlpha.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_BoolUniform.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_Discard.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_MultiStageUniform.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_OptimizedInput.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_StructUniform.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_TextureSize.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ShaderTestScene_TexturedQuad.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/SingleAppearanceScene_ChangeAppearance.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/SingleAppearanceScene_GreenTriangles.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/SingleAppearanceScene_RedTriangles.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_ChangedText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_DeletedTextsAndNode.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_FontCascade.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_ForceAutoHinting.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_Shaping.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_SimpleText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_SmallText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_VerticalOffset.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextScene_YellowText.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DAnisotropicTextureFilteringScene_Anisotropic.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DCompressedMipMapScene_CompressedMipMap.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_ASTC_RGBA_4x4.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_ETC2RGB.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_ETC2RGBA.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_FloatRG.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_FloatRGB.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_FloatRed.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_R8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_RG8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_RGB565.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_RGB8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_RGBA4.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_RGBA5551.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_RGBA8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_SRGB8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_SRGB8_ALPHA8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_Swizzled_BGR8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_Swizzled_BGRA8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DFormatScene_Swizzled_Luminance_Alpha.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DGenerateMipMapScene_GenerateMultipleMipMaps.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DGenerateMipMapScene_GenerateSingleMipMap.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_Bilinear.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_BilinearWithMipMaps.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_MinLinearMagNearest.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_MinNearestMagLinear.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_Nearest.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_NearestWithMipMaps.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture2DSamplingScene_Trilinear.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/Texture3DScene_4Slices.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureAddressScene_AllAddressModes.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_PartialUpdate.PNG (100%) create mode 100644 tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate1.PNG create mode 100644 tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate2.PNG rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_PartialUpdateMipMap.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_PartialUpdateMipMap_RG8.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_RGBA8_OneMip.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_RGBA8_OneMipGreen.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_RGBA8_OneMipRed.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureBuffer_RGBA8_ThreeMips.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureCubeAnisotropicTextureFilteringScene_Anisotropic.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureLinkTest_Linked.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureLinkTest_NoLinks.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureLinkTest_ProviderTextureChanged.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureSamplerScene_Changed.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureSamplerScene_ChangedBlue.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/TextureSamplerScene_Initial.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/VisibilityScene_On.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/VisibilityScene_Partial.PNG (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/binary-test-shader.frag (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/binary-test-shader.vert (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ramses-renderer-test-custom-text-effect.frag (100%) rename {integration/SandwichTests/RendererTests => tests/integration/renderer-tests}/res/ramses-renderer-test-custom-text-effect.vert (100%) rename {integration/StressTests/ResourceStressTests => tests/integration/resource-stress-tests}/CMakeLists.txt (69%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_Base.cpp (74%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_Base.h (87%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_OffscreenRenderTarget.cpp (77%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_OffscreenRenderTarget.h (92%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_Resources.cpp (88%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_Resources.h (92%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_SceneResources.cpp (77%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/DynamicQuad_SceneResources.h (92%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ResourceStressTestScene.cpp (82%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ResourceStressTestScene.h (90%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ResourceStressTestSceneArray.cpp (95%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ResourceStressTestSceneArray.h (89%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ResourceStressTests.cpp (95%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ResourceStressTests.h (83%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/ScreenspaceQuad.h (95%) rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/StressTestRenderer.cpp (65%) create mode 100644 tests/integration/resource-stress-tests/StressTestRenderer.h rename {integration/StressTests/ResourceStressTests/src => tests/integration/resource-stress-tests}/main.cpp (93%) rename {integration/SmokeTests => tests/integration/smoke-tests}/CMakeLists.txt (100%) rename {integration/SmokeTests => tests/integration/smoke-tests}/ramses-local-client-test/CMakeLists.txt (85%) rename {integration/SmokeTests/ramses-local-client-test/src => tests/integration/smoke-tests/ramses-local-client-test}/main.cpp (66%) rename {integration/SmokeTests => tests/integration/smoke-tests}/ramses-test-client/CMakeLists.txt (80%) rename {integration/SmokeTests/ramses-test-client/src => tests/integration/smoke-tests/ramses-test-client}/main.cpp (62%) rename {integration/TestContent/src => tests/integration/test-content}/AntiAliasingScene.cpp (61%) rename {integration/TestContent/src => tests/integration/test-content}/ArrayBufferScene.cpp (77%) rename {integration/TestContent/src => tests/integration/test-content}/ArrayInputScene.cpp (63%) rename {integration/TestContent/src => tests/integration/test-content}/ArrayResourceScene.cpp (65%) rename {integration/TestContent/src => tests/integration/test-content}/BlitPassScene.cpp (83%) rename {integration/TestContent => tests/integration/test-content}/CMakeLists.txt (66%) rename {integration/TestContent/src => tests/integration/test-content}/CameraDataLinkScene.cpp (79%) rename {integration/TestContent/src => tests/integration/test-content}/CommonRenderBufferTestScene.cpp (75%) rename {integration/TestContent/src => tests/integration/test-content}/CubeTextureScene.cpp (67%) rename {integration/TestContent/src => tests/integration/test-content}/CustomShaderTestScene.cpp (73%) rename {integration/TestContent/src => tests/integration/test-content}/DataLinkScene.cpp (84%) rename {integration/TestContent/src => tests/integration/test-content}/EmbeddedCompositorScene.cpp (83%) rename {integration/TestContent/src => tests/integration/test-content}/FileLoadingScene.cpp (75%) rename {integration/TestContent/src => tests/integration/test-content}/GeometryInstanceScene.cpp (70%) rename {integration/TestContent/src => tests/integration/test-content}/GeometryShaderScene.cpp (85%) rename {integration/TestContent/src => tests/integration/test-content}/HierarchicalRedTrianglesScene.cpp (92%) rename {integration/TestContent/src => tests/integration/test-content}/IndexArray32BitScene.cpp (79%) rename {integration/TestContent/src => tests/integration/test-content}/IntegrationScene.cpp (90%) rename {integration/TestContent/src => tests/integration/test-content}/Line.cpp (74%) rename {integration/TestContent/src => tests/integration/test-content}/MsaaRenderBufferScene.cpp (70%) rename {integration/TestContent/src => tests/integration/test-content}/MultiLanguageTextScene.cpp (86%) rename {integration/TestContent/src => tests/integration/test-content}/MultiTransformationLinkScene.cpp (91%) rename {integration/TestContent/src => tests/integration/test-content}/MultiTriangleGeometry.cpp (72%) rename {integration/TestContent/src => tests/integration/test-content}/MultiTypeLinkScene.cpp (77%) rename {integration/TestContent/src => tests/integration/test-content}/MultipleGeometryScene.cpp (85%) rename {integration/TestContent/src => tests/integration/test-content}/MultipleRenderTargetScene.cpp (80%) rename {integration/TestContent/src => tests/integration/test-content}/MultipleTrianglesScene.cpp (83%) create mode 100644 tests/integration/test-content/RenderBufferScene.cpp rename {integration/TestContent/src => tests/integration/test-content}/RenderPassClearScene.cpp (76%) rename {integration/TestContent/src => tests/integration/test-content}/RenderPassOnceScene.cpp (73%) rename {integration/TestContent/src => tests/integration/test-content}/RenderPassScene.cpp (88%) rename {integration/TestContent/src => tests/integration/test-content}/RenderTargetScene.cpp (79%) rename {integration/TestContent/src => tests/integration/test-content}/SceneFromPath.cpp (93%) rename {integration/TestContent/src => tests/integration/test-content}/ShaderTestScene.cpp (52%) rename {integration/TestContent/src => tests/integration/test-content}/SingleAppearanceScene.cpp (67%) create mode 100644 tests/integration/test-content/StreamTextureScene.cpp rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/AntiAliasingScene.h (88%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/ArrayBufferScene.h (90%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/ArrayInputScene.h (80%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/ArrayResourceScene.h (86%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/BlitPassScene.h (94%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/CameraDataLinkScene.h (88%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/CommonRenderBufferTestScene.h (87%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/CubeTextureScene.h (86%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/CustomShaderTestScene.h (79%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/DataLinkScene.h (84%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/EmbeddedCompositorScene.h (92%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/FileLoadingScene.h (79%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/GeometryInstanceScene.h (68%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/GeometryShaderScene.h (76%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/HierarchicalRedTrianglesScene.h (90%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/IndexArray32BitScene.h (75%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/IntegrationScene.h (90%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Line.h (79%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MsaaRenderBufferScene.h (85%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultiLanguageTextScene.h (87%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultiTransformationLinkScene.h (82%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultiTriangleGeometry.h (82%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultiTypeLinkScene.h (87%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultipleGeometryScene.h (82%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultipleRenderTargetScene.h (90%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/MultipleTrianglesScene.h (67%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/RenderBufferScene.h (72%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/RenderPassClearScene.h (73%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/RenderPassOnceScene.h (91%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/RenderPassScene.h (79%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/RenderTargetScene.h (94%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/SceneFromPath.h (85%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/ShaderTestScene.h (82%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/SingleAppearanceScene.h (71%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/StreamTextureScene.h (93%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextScene.h (97%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextScene_Base.h (80%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Texture2DAnisotropicTextureFilteringScene.h (79%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Texture2DCompressedMipMapScene.h (67%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Texture2DFormatScene.h (80%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Texture2DGenerateMipMapScene.h (73%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Texture2DSamplingScene.h (71%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Texture3DScene.h (92%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextureAddressScene.h (89%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextureBufferScene.h (88%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h (81%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextureLinkScene.h (85%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TextureSamplerScene.h (92%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TransformationLinkScene.h (83%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/Triangle.h (73%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TriangleAppearance.h (75%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/TriangleGeometry.h (59%) rename {integration/TestContent/include => tests/integration/test-content}/TestScenes/VisibilityScene.h (91%) rename {integration/TestContent/src => tests/integration/test-content}/TestStepCommand.cpp (88%) rename {integration/TestContent/include => tests/integration/test-content}/TestStepCommand.h (64%) rename {integration/TestContent/src => tests/integration/test-content}/TextScene.cpp (94%) rename {integration/TestContent/src => tests/integration/test-content}/TextScene_Base.cpp (91%) rename {integration/TestContent/src => tests/integration/test-content}/Texture2DAnisotropicTextureFilteringScene.cpp (82%) rename {integration/TestContent/src => tests/integration/test-content}/Texture2DCompressedMipMapScene.cpp (67%) rename {integration/TestContent/src => tests/integration/test-content}/Texture2DFormatScene.cpp (91%) rename {integration/TestContent/src => tests/integration/test-content}/Texture2DGenerateMipMapScene.cpp (85%) rename {integration/TestContent/src => tests/integration/test-content}/Texture2DSamplingScene.cpp (90%) rename {integration/TestContent/src => tests/integration/test-content}/Texture3DScene.cpp (79%) rename {integration/TestContent/src => tests/integration/test-content}/TextureAddressScene.cpp (74%) rename {integration/TestContent/src => tests/integration/test-content}/TextureBufferScene.cpp (80%) rename {integration/TestContent/src => tests/integration/test-content}/TextureCubeAnisotropicTextureFilteringScene.cpp (82%) rename {integration/TestContent/src => tests/integration/test-content}/TextureLinkScene.cpp (69%) rename {integration/TestContent/src => tests/integration/test-content}/TextureSamplerScene.cpp (76%) rename {integration/TestContent/src => tests/integration/test-content}/TransformationLinkScene.cpp (86%) rename {integration/TestContent/src => tests/integration/test-content}/Triangle.cpp (62%) create mode 100644 tests/integration/test-content/TriangleAppearance.cpp rename {integration/TestContent/src => tests/integration/test-content}/TriangleGeometry.cpp (59%) rename {integration/TestContent/src => tests/integration/test-content}/VisibilityScene.cpp (72%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-3d-textured.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-3d-textured.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-Arimo-Regular.ttf (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-DroidKufi-Regular.ttf (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-Roboto-Bold.ttf (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-Roboto-Light.ttf (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-WenQuanYiMicroHei.ttf (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-arrays.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-arrays.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-basic-extended.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-basic-extended.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-basic.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-basic.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-boolUniform.frag (75%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-boolUniform.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cube-nx.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cube-ny.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cube-nz.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cube-px.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cube-py.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cube-pz.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cubeSphere.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-cubeSphere.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-float.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-float.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-interleaved.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-interleaved.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-vec2.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-vec2.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-vec3.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-vec3.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-vec4.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers-vec4.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-data-buffers.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-discard.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-discard.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-embedded-compositing-1.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-embedded-compositing-2.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-embedded-compositing-3.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-embedded-compositing-4.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-embedded-compositing-5.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-embedded-compositing-6.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-explicitLocation.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-explicitLocation.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-explicitLocationSwapped.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-explicitLocationSwapped.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-file-loading-basic.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-file-loading-red.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-file-loading-texture.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-file-loading-texturing.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-file-loading-texturing.vert (91%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-input.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-input.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-logo-cropped.png (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-mplus-1p-regular.ttf (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-multiStageUniform.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-multiStageUniform.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-optimizedInput.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-optimizedInput.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-pixel-accurate-text.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-pixel-accurate-text.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-render-one-buffer-ms.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-render-one-buffer-ms.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-render-one-buffer.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-render-one-buffer.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-render-two-buffers.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-render-two-buffers.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-simple-color.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-simple-color.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-structUniform.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-structUniform.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-text.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-text.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-texture-buffer-allmips.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-texture-buffer-allmips.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-texture-buffer.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-texture-buffer.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textureSize.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textureSize.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textured-cube.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textured-cube.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textured-with-texel-fetch.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textured-with-texel-fetch.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textured.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-textured.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-texturedWithColor.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-client-texturedWithColor.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-instancing-uniform.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-instancing-uniform.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-instancing-vertex.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-test-instancing-vertex.vert (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-text-stress-tests.frag (100%) rename {integration/TestContent => tests/integration/test-content}/res/ramses-text-stress-tests.vert (100%) create mode 100644 tests/unittests/CMakeLists.txt create mode 100644 tests/unittests/client/AppearanceTest.cpp rename {client/test => tests/unittests/client}/ArrayBufferTest.cpp (55%) rename {client/test => tests/unittests/client}/BlitPassTest.cpp (51%) create mode 100644 tests/unittests/client/CMakeLists.txt rename {client/test => tests/unittests/client}/CameraTest.cpp (57%) create mode 100644 tests/unittests/client/ClientApplicationLogicTest.cpp rename {client/test => tests/unittests/client}/ClientEventHandlerMock.cpp (95%) rename {client/test => tests/unittests/client}/ClientEventHandlerMock.h (70%) create mode 100644 tests/unittests/client/ClientTestUtils.cpp rename {client/test => tests/unittests/client}/ClientTestUtils.h (71%) rename {client/test => tests/unittests/client}/CreationHelper.cpp (61%) create mode 100644 tests/unittests/client/CreationHelper.h rename {client/test => tests/unittests/client}/DataObjectTest.cpp (70%) rename {client/test => tests/unittests/client}/EffectDescriptionTest.cpp (69%) create mode 100644 tests/unittests/client/EffectInputTest.cpp rename {client/test => tests/unittests/client}/EffectTest.cpp (55%) create mode 100644 tests/unittests/client/GeometryTest.cpp create mode 100644 tests/unittests/client/GlslEffectTest.cpp rename {client/test => tests/unittests/client}/GlslLimitsTest.cpp (87%) create mode 100644 tests/unittests/client/GlslParserTest.cpp rename {client/test => tests/unittests/client}/IteratorTest.cpp (90%) rename {client/test => tests/unittests/client}/MeshNodeTest.cpp (62%) create mode 100644 tests/unittests/client/MockActionCollector.h rename {client/test => tests/unittests/client}/NodeLazyTransformTest.cpp (63%) rename {client/test => tests/unittests/client}/NodeTest.cpp (70%) rename {client/test => tests/unittests/client}/NodeTransformationTest.cpp (61%) rename {client/test => tests/unittests/client}/NodeVisibilityTest.cpp (76%) rename {client/test => tests/unittests/client}/PickableObjectTest.cpp (69%) rename {client/test => tests/unittests/client}/QuadTest.cpp (99%) rename {client/test => tests/unittests/client}/RamsesClientTest.cpp (67%) create mode 100644 tests/unittests/client/RamsesObjectOwnershipTest.cpp create mode 100644 tests/unittests/client/RamsesObjectTest.cpp rename {client/test => tests/unittests/client}/RamsesObjectTestTypes.h (92%) create mode 100644 tests/unittests/client/RamsesObjectValidationTest.cpp rename {client/test => tests/unittests/client}/RamsesUtilsTest.cpp (80%) rename {client/test => tests/unittests/client}/RamsesVersionTest.cpp (90%) create mode 100644 tests/unittests/client/RenderBufferTest.cpp rename {client/test => tests/unittests/client}/RenderGroupMeshIteratorTest.cpp (85%) create mode 100644 tests/unittests/client/RenderGroupTest.cpp rename {client/test => tests/unittests/client}/RenderPassGroupIteratorTest.cpp (81%) rename {client/test => tests/unittests/client}/RenderPassTest.cpp (51%) create mode 100644 tests/unittests/client/RenderTargetDescriptionTest.cpp rename {client/test => tests/unittests/client}/RenderTargetTest.cpp (64%) rename {client/test => tests/unittests/client}/ResourceTest.cpp (51%) rename {client/test/ClientCommands => tests/unittests/client}/SceneCommandBufferTest.cpp (92%) create mode 100644 tests/unittests/client/SceneCommandsTest.cpp rename {client/test => tests/unittests/client}/SceneDistributionTest.cpp (60%) create mode 100644 tests/unittests/client/SceneFactoryTest.cpp rename {client/test => tests/unittests/client}/SceneGraphIteratorTest.cpp (86%) rename {client/test => tests/unittests/client}/SceneIteratorTest.cpp (94%) rename {client/test => tests/unittests/client}/SceneObjectIteratorTest.cpp (56%) rename client/test/RamsesObjectRegistryTest.cpp => tests/unittests/client/SceneObjectRegistryTest.cpp (64%) create mode 100644 tests/unittests/client/ScenePersistationTest.cpp rename {client/test => tests/unittests/client}/ScenePersistationTest.h (55%) rename {client/test => tests/unittests/client}/ScenePersistationThreadedTest.cpp (71%) rename {client/test => tests/unittests/client}/SceneReferenceTest.cpp (55%) rename {client/test => tests/unittests/client}/SceneTest.cpp (55%) rename {client/test => tests/unittests/client}/SerializationHelperTest.cpp (89%) rename {client/test => tests/unittests/client}/SimpleSceneTopology.h (66%) rename {client/test => tests/unittests/client}/TestEffectCreator.h (88%) rename {client/test => tests/unittests/client}/TestEffects.h (58%) rename {client/test => tests/unittests/client}/Texture2DBufferTest.cpp (65%) rename {client/test => tests/unittests/client}/TextureSamplerTest.cpp (53%) rename {client/logic/unittests => tests/unittests/client/logic}/api/AnchorPointTest.cpp (64%) rename {client/logic/unittests => tests/unittests/client/logic}/api/AnimationNodeConfigTest.cpp (87%) rename {client/logic/unittests => tests/unittests/client/logic}/api/AnimationNodeTest.cpp (79%) rename {client/logic/unittests => tests/unittests/client/logic}/api/AnimationNodeWithDataPropertiesTest.cpp (88%) rename client/logic/unittests/api/RamsesAppearanceBindingTest.cpp => tests/unittests/client/logic/api/AppearanceBindingTest.cpp (57%) create mode 100644 tests/unittests/client/logic/api/CameraBindingTest.cpp rename {client/logic/unittests => tests/unittests/client/logic}/api/CollectionTest.cpp (98%) rename {client/logic/unittests => tests/unittests/client/logic}/api/DataArrayTest.cpp (75%) rename {client/logic/unittests => tests/unittests/client/logic}/api/IteratorTest.cpp (98%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LogicEngineTest_Animations.cpp (63%) create mode 100644 tests/unittests/client/logic/api/LogicEngineTest_Compatibility.cpp rename {client/logic/unittests => tests/unittests/client/logic}/api/LogicEngineTest_DependencyExtraction.cpp (94%) create mode 100644 tests/unittests/client/logic/api/LogicEngineTest_Dirtiness.cpp create mode 100644 tests/unittests/client/logic/api/LogicEngineTest_Factory.cpp rename {client/logic/unittests => tests/unittests/client/logic}/api/LogicEngineTest_Linking.cpp (67%) create mode 100644 tests/unittests/client/logic/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp create mode 100644 tests/unittests/client/logic/api/LogicEngineTest_Serialization.cpp create mode 100644 tests/unittests/client/logic/api/LogicEngineTest_SerializedSize.cpp rename {client/logic/unittests => tests/unittests/client/logic}/api/LogicEngineTest_Update.cpp (63%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LogicEngineTest_UpdateReport.cpp (66%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LogicEngineTest_Validation.cpp (52%) create mode 100644 tests/unittests/client/logic/api/LogicNodeTest.cpp create mode 100644 tests/unittests/client/logic/api/LogicObjectTest.cpp rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaConfigTest.cpp (69%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaInterfaceTest.cpp (79%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaModuleTest.cpp (72%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Debug.cpp (60%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Init.cpp (80%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Interface.cpp (87%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Lifecycle.cpp (82%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Modules.cpp (78%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Runtime.cpp (82%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Serialization.cpp (88%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Syntax.cpp (70%) rename {client/logic/unittests => tests/unittests/client/logic}/api/LuaScriptTest_Types.cpp (92%) create mode 100644 tests/unittests/client/logic/api/MeshNodeBindingTest.cpp rename client/logic/unittests/api/RamsesNodeBindingTest.cpp => tests/unittests/client/logic/api/NodeBindingTest.cpp (55%) rename {client/logic/unittests => tests/unittests/client/logic}/api/PropertyTest.cpp (84%) rename {client/logic/unittests => tests/unittests/client/logic}/api/PropertyTypeTest.cpp (99%) create mode 100644 tests/unittests/client/logic/api/RenderGroupBindingElementsTest.cpp rename client/logic/unittests/api/RamsesRenderGroupBindingTest.cpp => tests/unittests/client/logic/api/RenderGroupBindingTest.cpp (52%) create mode 100644 tests/unittests/client/logic/api/RenderPassBindingTest.cpp rename {client/logic/unittests => tests/unittests/client/logic}/api/SaveFileConfigTest.cpp (62%) rename {client/logic/unittests => tests/unittests/client/logic}/api/SkinBindingTest.cpp (63%) rename {client/logic/unittests => tests/unittests/client/logic}/api/TimerNodeTest.cpp (63%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/ApiObjectsTest.cpp (58%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/DirectedAcyclicGraphTest.cpp (93%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/EnvironmentProtectionTest.cpp (99%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/LogicNodeDependenciesTest.cpp (69%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/LuaCustomizationsTest.cpp (98%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/LuaTypeConversionsTest.cpp (99%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/PropertyTypeExtractorTest.cpp (99%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/RamsesHelperTest.cpp (94%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/RamsesObjectResolverTest.cpp (79%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/SolHelperTest.cpp (97%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/SolStateTest.cpp (98%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/TypeDataTest.cpp (99%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/TypeUtilsTest.cpp (89%) rename {client/logic/unittests => tests/unittests/client/logic}/internal/WrappedLuaPropertyTest.cpp (98%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/FeatureLevelTestValues.h (93%) create mode 100644 tests/unittests/client/logic/shared/LogicEngineTestWithCreationHelper.h create mode 100644 tests/unittests/client/logic/shared/LogicEngineTest_Base.h rename {client/logic/unittests => tests/unittests/client/logic}/shared/LogicNodeDummy.h (83%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/LuaScriptTest_Base.h (98%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/PropertyLinkTestUtils.h (54%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/RamsesObjectResolverMock.h (96%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/RamsesTestUtils.h (80%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/SerializationTestUtils.h (84%) rename {client/logic/unittests => tests/unittests/client/logic}/shared/WithTempDirectory.h (93%) rename {client/test => tests/unittests/client}/res/ramses-client-test_minimalShader.frag (100%) rename {client/test => tests/unittests/client}/res/ramses-client-test_minimalShader.geom (100%) rename {client/test => tests/unittests/client}/res/ramses-client-test_minimalShader.vert (100%) rename {client/test => tests/unittests/client}/res/ramses-client-test_shader.vert (100%) rename {client/test => tests/unittests/client}/res/ramses-text-DroidKufi-Regular.ttf (100%) create mode 100644 tests/unittests/client/res/ramses-text-NotoSansThai-Regular.ttf rename {client/test => tests/unittests/client}/res/ramses-text-Roboto-Bold.ttf (100%) rename {client/test => tests/unittests/client}/res/ramses-text-Roboto-Regular.ttf (100%) create mode 100644 tests/unittests/client/res/ramses-text-WenQuanYi-Micro-Hei.ttf rename {client/test => tests/unittests/client}/res/rgba8_expectedFlipped.png (100%) rename {client/test => tests/unittests/client}/res/sampleTexture.png (100%) rename {client/test => tests/unittests/client}/res/sampleTexture_invalid.png (100%) rename client/logic/unittests/res/testLogic_01.rlogic => tests/unittests/client/res/testScene_01.ramses (64%) rename {client/test => tests/unittests/client}/text/FontCascadeTest.cpp (94%) rename {client/test => tests/unittests/client}/text/FontRegistryTest.cpp (92%) create mode 100644 tests/unittests/client/text/Freetype2FontInstanceTest.cpp rename {client/test => tests/unittests/client}/text/GlyphTextureAtlasTest.cpp (86%) rename {client/test => tests/unittests/client}/text/GlyphTexturePageTest.cpp (92%) create mode 100644 tests/unittests/client/text/HarfbuzzFontInstanceTest.cpp rename {client/test => tests/unittests/client}/text/LayoutUtilsHMITest.cpp (89%) rename {client/test => tests/unittests/client}/text/LayoutUtilsTest.cpp (82%) rename {client/test => tests/unittests/client}/text/TextCacheTest.cpp (95%) rename {client/test => tests/unittests/client}/text/UtfUtilsTest.cpp (99%) rename {renderer/Platform/Context_EGL => tests/unittests/framework-test-utils}/CMakeLists.txt (70%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/CommunicationSystemMock.h (77%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/ComponentMocks.h (80%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/DummyResource.h (80%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/FileDescriptorHelper.h (87%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/IOStreamTester.h (81%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/InputStreamContainerMock.h (81%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/InputStreamMock.h (76%) create mode 100644 tests/unittests/framework-test-utils/include/LogTestUtils.h rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/MockConnectionStatusUpdateNotifier.h (79%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/ResourceMock.h (91%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/SceneRendererHandlerMock.h (76%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/ScopedConsoleLogDisable.h (82%) create mode 100644 tests/unittests/framework-test-utils/include/ScopedLogContextLevel.h rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/ServiceHandlerMocks.h (73%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/TestEqualHelper.h (90%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/TestRandom.h (85%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/UnsafeTestMemoryHelpers.h (76%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/include/WindowsInvalidParameterCheckSuppression.h (80%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/CommunicationSystemMock.cpp (87%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/ComponentMocks.cpp (85%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/InputStreamContainerMock.cpp (96%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/InputStreamMock.cpp (95%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/MockConnectionStatusUpdateNotifier.cpp (89%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/ResourceMock.cpp (91%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/SceneRendererHandlerMock.cpp (96%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/ServiceHandlerMocks.cpp (88%) rename {framework/FrameworkTestUtils => tests/unittests/framework-test-utils}/src/WindowsInvalidParameterCheckSuppression.cpp (98%) create mode 100644 tests/unittests/framework/CMakeLists.txt rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/CommunicationSystemTest.cpp (97%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/CommunicationSystemTest.h (87%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/CommunicationSystemTestWrapper.cpp (93%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/CommunicationSystemTestWrapper.h (88%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/ConnectionStatusUpdateNotifierTest.cpp (96%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/ConnectionSystemTestHelper.cpp (73%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/ConnectionSystemTestHelper.h (80%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/FlushInformationSerializationHelperTest.cpp (93%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/MockConnectionStatusListener.cpp (92%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/MockConnectionStatusListener.h (79%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/SceneUpdateSerializationHelperTest.cpp (82%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/SceneUpdateSerializationTest.cpp (93%) rename {framework/Communication/TransportCommon/test => tests/unittests/framework/Communication/TransportCommon}/SceneUpdateSerializerTestHelper.h (63%) rename {framework/Communication/TransportTCP/test => tests/unittests/framework/Communication/TransportTCP}/TCPConnectionSystemTest.cpp (96%) rename {framework/Components/test => tests/unittests/framework/Components}/AbstractSenderAndReceiverTest.h (92%) rename {framework/Components/test => tests/unittests/framework/Components}/ClientSceneLogicTest.cpp (82%) rename {framework/Components/test => tests/unittests/framework/Components}/InputStreamContainerTest.cpp (70%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceAvailabilityEventTest.cpp (87%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceComponentTest.cpp (92%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceFileRegistryTest.cpp (81%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceHashUsageTest.cpp (94%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourcePersistationTest.cpp (78%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceSerializationTestHelper.h (79%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceStorageTest.cpp (77%) rename {framework/Components/test => tests/unittests/framework/Components}/ResourceTableOfContentsTest.cpp (82%) rename {framework/Components/test => tests/unittests/framework/Components}/SceneGraphComponentTest.cpp (83%) rename {framework/Components/test => tests/unittests/framework/Components}/SceneGraphProtocolSenderAndReceiverTest.cpp (85%) rename {framework/Components/test => tests/unittests/framework/Components}/SingleResourceSerializationTest.cpp (94%) rename {framework/Core/Common/test => tests/unittests/framework/Core/Common}/ParticipantIdentifierTest.cpp (92%) create mode 100644 tests/unittests/framework/Core/Math3d/CameraMatrixHelperTest.cpp rename {framework/Core/Math3d/test => tests/unittests/framework/Core/Math3d}/ProjectionParamsTest.cpp (98%) rename {framework/Core/Math3d/test => tests/unittests/framework/Core/Math3d}/QuadTest.cpp (97%) rename {framework/Core/Math3d/test => tests/unittests/framework/Core/Math3d}/RotationTest.cpp (94%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/MockITask.h (80%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/MockTaskFinishHandler.h (76%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/MockTaskQueue.h (78%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp (94%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/TaskExecutingThreadTest.cpp (93%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp (89%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h (77%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/TaskForwardingQueueTest.cpp (93%) rename {framework => tests/unittests/framework}/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp (82%) rename {framework => tests/unittests/framework}/Core/Utils/test/BinaryFileInputStreamTest.cpp (92%) rename {framework => tests/unittests/framework}/Core/Utils/test/BinaryFileOutputStreamTest.cpp (95%) rename {framework => tests/unittests/framework}/Core/Utils/test/BinaryInputStreamTest.cpp (80%) rename {framework => tests/unittests/framework}/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp (80%) rename {framework => tests/unittests/framework}/Core/Utils/test/BinaryOutputStreamTest.cpp (72%) create mode 100644 tests/unittests/framework/Core/Utils/test/EnumTraitsTest.cpp rename {framework => tests/unittests/framework}/Core/Utils/test/FileTest.cpp (98%) create mode 100644 tests/unittests/framework/Core/Utils/test/ImageTest.cpp rename {framework => tests/unittests/framework}/Core/Utils/test/InplaceStringTokenizerTest.cpp (95%) rename {framework => tests/unittests/framework}/Core/Utils/test/LogHelperTest.cpp (98%) rename {framework => tests/unittests/framework}/Core/Utils/test/LogMacrosTest.cpp (91%) rename {framework => tests/unittests/framework}/Core/Utils/test/LoggingUtilsTest.cpp (51%) rename {framework => tests/unittests/framework}/Core/Utils/test/MemoryPoolExplicitTest.cpp (91%) rename {framework => tests/unittests/framework}/Core/Utils/test/MemoryPoolIteratorTest.cpp (97%) rename {framework => tests/unittests/framework}/Core/Utils/test/MemoryPoolTest.cpp (94%) rename {framework => tests/unittests/framework}/Core/Utils/test/MessagePoolTest.cpp (97%) rename {framework => tests/unittests/framework}/Core/Utils/test/PeriodicLoggerHelperTest.cpp (98%) rename {framework => tests/unittests/framework}/Core/Utils/test/RawBinaryOutputStreamTest.cpp (63%) rename {framework => tests/unittests/framework}/Core/Utils/test/StatisticCollectionTest.cpp (98%) rename {framework => tests/unittests/framework}/Core/Utils/test/StringUtilsTest.cpp (96%) rename {framework => tests/unittests/framework}/Core/Utils/test/TextureMathUtilsTest.cpp (98%) rename {framework => tests/unittests/framework}/Core/Utils/test/ThreadBarrierTest.cpp (93%) rename {framework => tests/unittests/framework}/Core/Utils/test/ThreadLocalLogTest.cpp (89%) rename {framework => tests/unittests/framework}/Core/Utils/test/VectorBinaryOutputStreamTest.cpp (50%) rename {framework => tests/unittests/framework}/Core/Utils/test/VoidOutputStreamTest.cpp (58%) create mode 100644 tests/unittests/framework/DltLogAppender/DltAdapterTest.cpp rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/AbseilTest.cpp (99%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/BlockingQueueTest.cpp (96%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/ComplexTestType.h (89%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/FmtlibTest.cpp (96%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/GuidTest.cpp (95%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/HashMapTest.cpp (94%) create mode 100644 tests/unittests/framework/PlatformAbstraction/HashSetTest.cpp rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/HashTest.cpp (67%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/HeapArrayTest.cpp (97%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/IOStreamTest.cpp (99%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/MathTest.cpp (92%) create mode 100644 tests/unittests/framework/PlatformAbstraction/NumericLimitsTest.cpp rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/PlatformEndianess.cpp (94%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/PlatformEnvironmentVariablesTest.cpp (93%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/PlatformSignalTest.cpp (87%) create mode 100644 tests/unittests/framework/PlatformAbstraction/PlatformStringUtilsTest.cpp rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/PlatformThreadTest.cpp (97%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/PlatformTimeTest.cpp (97%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/StringOutputStreamTest.cpp (83%) rename {framework/PlatformAbstraction/test => tests/unittests/framework/PlatformAbstraction}/VectorTest.cpp (96%) rename {framework/Ramsh/test => tests/unittests/framework/Ramsh}/RamshTest.cpp (70%) create mode 100644 tests/unittests/framework/SceneGraph/Resource/ArrayResourceTest.cpp rename {framework/SceneGraph/Resource/test => tests/unittests/framework/SceneGraph/Resource}/CityhashTest.cpp (97%) rename {framework/SceneGraph/Resource/test => tests/unittests/framework/SceneGraph/Resource}/EffectResourceTest.cpp (76%) rename {framework/SceneGraph/Resource/test => tests/unittests/framework/SceneGraph/Resource}/LZ4CompressionUtilsTest.cpp (82%) rename {framework/SceneGraph/Resource/test => tests/unittests/framework/SceneGraph/Resource}/ResourceTest.cpp (81%) rename {framework/SceneGraph/Resource/test => tests/unittests/framework/SceneGraph/Resource}/TextureResourceTest.cpp (80%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/ActionTestScene.cpp (96%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/ActionTestScene.h (64%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/DataLayoutCachedSceneTest.cpp (85%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/ResourceChangeCollectingSceneTest.cpp (88%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/ResourceUtilsTest.cpp (95%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionCollectionBasicTypesTest.cpp (97%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionCollectionComplexTypesTest.cpp (93%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionCollectionCreatorAndApplierTest.cpp (85%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionCollectionTest.cpp (95%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionHelperAndApplierTest.cpp (97%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionUtils.cpp (98%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionUtils.h (85%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneActionUtilsTest.cpp (96%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneDescriberTest.cpp (70%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/ScenePersistationTest.cpp (83%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest.h (75%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_BlitPasses.cpp (86%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_Camera.cpp (83%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_DataBuffers.cpp (66%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_DataInstances.cpp (83%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_DataLayouts.cpp (85%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_DataSlot.cpp (85%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_Generic.cpp (99%) create mode 100644 tests/unittests/framework/SceneGraph/Scene/SceneTest_IteratableMemoryPools.cpp rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_Nodes.cpp (84%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_PickableObjects.cpp (93%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_RenderBuffer.cpp (67%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_RenderGroups.cpp (89%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_RenderPasses.cpp (87%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_RenderTarget.cpp (95%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_Renderables.cpp (84%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_SceneReference.cpp (99%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_States.cpp (95%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_TextureBuffers.cpp (67%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_TextureSamplers.cpp (86%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/SceneTest_Transforms.cpp (87%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/TestingScene.h (91%) rename {framework/SceneGraph/Scene/test => tests/unittests/framework/SceneGraph/Scene}/TransformationCachedSceneTest.cpp (93%) rename {framework/SceneGraph/SceneAPI/test => tests/unittests/framework/SceneGraph/SceneAPI}/ResourceContentHashTest.cpp (94%) create mode 100644 tests/unittests/framework/SceneGraph/SceneAPI/TextureSamplerStatesHashTest.cpp rename {framework/SceneReferencing/test => tests/unittests/framework/SceneReferencing}/SceneReferenceEventTest.cpp (93%) create mode 100644 tests/unittests/framework/Watchdog/PlatformWatchDogMock.h rename {framework/Watchdog/test => tests/unittests/framework/Watchdog}/PlatformWatchDogTest.cpp (54%) rename {framework/Watchdog/test => tests/unittests/framework/Watchdog}/ThreadWatchdogTest.cpp (89%) rename {framework/test => tests/unittests/framework}/main.cpp (100%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/APILoggingHelperTest.cpp (95%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/ApiRamshCommandMock.cpp (95%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/ApiRamshCommandMock.h (78%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/CommandTest.cpp (52%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/CommandTest.h (83%) create mode 100644 tests/unittests/framework/ramses-framework/DataTypeTest.cpp create mode 100644 tests/unittests/framework/ramses-framework/ErrorReportingTest.cpp create mode 100644 tests/unittests/framework/ramses-framework/FlagsTest.cpp rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/PublicRamshCommandTest.cpp (95%) create mode 100644 tests/unittests/framework/ramses-framework/RamsesFrameworkConfigTest.cpp rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/RamsesFrameworkTest.cpp (60%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/RamsesVersionTest.cpp (93%) rename {framework/ramses-framework/test => tests/unittests/framework/ramses-framework}/StronglyTypedValueTest.cpp (91%) create mode 100644 tests/unittests/framework/ramses-framework/ValidationReportTest.cpp rename {framework/test => tests/unittests/framework}/res/sampleImage.png (100%) create mode 100644 tests/unittests/ramses-cli/CMakeLists.txt create mode 100644 tests/unittests/ramses-cli/ramses-cli-test.cpp create mode 100644 tests/unittests/renderer/CMakeLists.txt create mode 100644 tests/unittests/renderer/embedded-compositor-wayland/CMakeLists.txt rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/EmbeddedCompositor_WaylandMock.h (73%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/EmbeddedCompositor_Wayland_Test.cpp (80%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/NativeWaylandResourceMock.h (88%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandBufferMock.h (79%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandBufferResourceMock.h (80%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandBufferResource_Test.cpp (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandCallbackResourceMock.h (86%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandCallbackResource_Test.cpp (79%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandClientMock.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandClient_Test.cpp (82%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandCompositorConnectionMock.h (77%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandCompositorConnection_Test.cpp (91%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandGlobal_Test.cpp (91%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandIVISurfaceMock.h (75%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandRegionMock.h (81%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandResourceLifecycleTest.cpp (88%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandResource_Test.cpp (89%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandShellSurfaceMock.h (90%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandShellSurface_Test.cpp (92%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandSurfaceMock.h (87%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/WaylandSurface_Test.cpp (94%) rename {renderer/Platform/EmbeddedCompositor_Wayland/test => tests/unittests/renderer/embedded-compositor-wayland}/main.cpp (81%) rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/BinaryShaderCacheProxyTest.cpp (73%) create mode 100644 tests/unittests/renderer/ramses-renderer/BinaryShaderCacheTest.cpp create mode 100644 tests/unittests/renderer/ramses-renderer/CMakeLists.txt create mode 100644 tests/unittests/renderer/ramses-renderer/DisplayConfigTest.cpp rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/RamsesRendererDispatchTest.cpp (74%) rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/RamsesRendererPickingTest.cpp (82%) create mode 100644 tests/unittests/renderer/ramses-renderer/RamsesRendererTest.cpp create mode 100644 tests/unittests/renderer/ramses-renderer/RendererConfigTest.cpp create mode 100644 tests/unittests/renderer/ramses-renderer/RendererEventTestHandler.h create mode 100644 tests/unittests/renderer/ramses-renderer/RendererMateTest.cpp rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/RendererSceneControlEventHandlerMock.cpp (96%) rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/RendererSceneControlEventHandlerMock.h (92%) rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/RendererSceneControlTest.cpp (52%) rename {renderer/ramses-renderer-impl/test => tests/unittests/renderer/ramses-renderer}/RendererSceneControlWithRendererTest.cpp (81%) create mode 100644 tests/unittests/renderer/renderer-lib/CMakeLists.txt create mode 100644 tests/unittests/renderer/renderer-lib/RendererCommands/AssignSceneTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererCommands/CreateOffscreenBufferTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererCommands/LinkUnlinkTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererCommands/ScreenshotTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererCommands/SetClearColorTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererCommands/SetSceneStateTest.cpp rename {renderer/RendererLib/RendererFramework/test => tests/unittests/renderer/renderer-lib/RendererFramework}/RendererFrameworkLogicTest.cpp (81%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/AsyncEffectUploaderTest.cpp (95%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/BufferLinksTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkCachedSceneTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkManagerTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayBundleMock.cpp (96%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayBundleMock.h (86%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DisplayConfigTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.h rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayControllerTest.cpp (85%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayControllerTestBase.h (82%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayDispatcherMock.cpp (99%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayDispatcherMock.h (93%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayDispatcherTest.cpp (98%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayDispatcherThreadedTest.cpp (98%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DisplayEventHandlerTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/DisplaySetupTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayThreadMock.cpp (96%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayThreadMock.h (87%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/DisplayThreadTest.cpp (93%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/EmbeddedCompositingManagerMock.cpp (97%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/EmbeddedCompositingManagerMock.h (87%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/IntersectionUtilsTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/LinkManagerBaseTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/PendingSceneResourcesUtilsTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/PlatformTest.cpp (84%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/Platform_BaseMock.cpp (93%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/Platform_BaseMock.h (91%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RenderExecutorInternalStateTest.cpp (98%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererCachedSceneTest.cpp (99%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandBufferTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandExecutorTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererConfigTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererEventCollectorTest.cpp (97%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererLogContextTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.cpp rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/RendererMock.h (78%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.h create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceRegistryTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneControlLogicMock.cpp (90%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneControlLogicMock.h (85%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneControlLogicTest.cpp (99%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneEventSenderMock.h (88%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneResourceRegistryTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneStateControlMock.cpp (96%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneStateControlMock.h (88%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneUpdaterFacade.cpp (79%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneUpdaterFacade.h (81%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneUpdaterMock.cpp (96%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/RendererSceneUpdaterMock.h (90%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.h create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest_Resources.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererScenesTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererStatisticsTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/RendererTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/ResourceCachedSceneTest.cpp (98%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/ResourceUploaderMock.cpp (91%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-lib/RendererLib}/ResourceUploaderMock.h (80%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploadingManagerTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneAllocateHelper.cpp (96%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneAllocateHelper.h (83%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/SceneDependencyCheckerTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneDisplayTrackerTest.cpp (97%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/SceneExpirationMonitorTest.cpp create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksManagerTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneLinksTest.cpp (97%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTestUtils.h rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneReferenceLogicMock.cpp (96%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneReferenceLogicMock.h (83%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneReferenceLogicTest.cpp (89%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneReferenceLogicWithSceneUpdaterTest.cpp (94%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/SceneResourceUploaderTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneStateExecutorTest.cpp (99%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/SceneStateInfoTest.cpp (76%) rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/TestSceneHelper.h (90%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/TextureLinkManagerTest.cpp rename {renderer/RendererLib/RendererLib/test => tests/unittests/renderer/renderer-lib/RendererLib}/TransformationLinkCachedSceneTest.cpp (87%) create mode 100644 tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkManagerTest.cpp rename {renderer/Platform/Device_GL => tests/unittests/renderer/renderer-test-common}/CMakeLists.txt (59%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/ContextMock.cpp (82%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/ContextMock.h (82%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/DeviceMock.cpp (94%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/DeviceMock.h (78%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/EmbeddedCompositorMock.cpp (97%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/EmbeddedCompositorMock.h (88%) create mode 100644 tests/unittests/renderer/renderer-test-common/MockResourceHash.h rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/PlatformFactoryMock.h (63%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/PlatformMock.cpp (97%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/PlatformMock.h (80%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/RenderBackendMock.cpp (94%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/RenderBackendMock.h (65%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/RendererCommandVisitorMock.cpp (92%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/RendererCommandVisitorMock.h (96%) create mode 100644 tests/unittests/renderer/renderer-test-common/ResourceDeviceHandleAccessorMock.h rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/ResourceUploadRenderBackendMock.h (78%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/SystemCompositorControllerMock.cpp (94%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/SystemCompositorControllerMock.h (87%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/WindowEventHandlerMock.cpp (76%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/WindowEventHandlerMock.h (66%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/WindowMock.cpp (94%) rename {renderer/RendererLib/RendererTestCommon => tests/unittests/renderer/renderer-test-common}/WindowMock.h (91%) create mode 100644 tests/unittests/renderer/system-compositor-controller-wayland/CMakeLists.txt rename {renderer/Platform/SystemCompositorController_Wayland_IVI/test => tests/unittests/renderer/system-compositor-controller-wayland}/SystemCompositorController_Wayland_IVI_Test.cpp (88%) rename {renderer/Platform/SystemCompositorController_Wayland_IVI/test => tests/unittests/renderer/system-compositor-controller-wayland}/main.cpp (91%) create mode 100644 tests/unittests/renderer/wayland-test-utils/CMakeLists.txt rename {renderer/Platform/WaylandUtilities/WaylandTestUtils => tests/unittests/renderer/wayland-test-utils}/TestWithWaylandEnvironment.h (92%) rename {renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test => tests/unittests/renderer/window-wayland-common}/AWindowWayland.h (84%) rename {renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test => tests/unittests/renderer/window-wayland-common}/AWindowWaylandWithEventHandling.h (88%) rename {renderer/Platform/Platform_Android_EGL => tests/unittests/renderer/window-wayland-common}/CMakeLists.txt (68%) rename {renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test => tests/unittests/renderer/window-wayland-common}/TestCases.h (89%) create mode 100644 tests/unittests/renderer/window-wayland-ivi/CMakeLists.txt rename {renderer/Platform/Window_Wayland_IVI/test => tests/unittests/renderer/window-wayland-ivi}/Window_Wayland_IVI_Test.cpp (83%) rename {renderer/Platform/Window_Wayland_IVI/test => tests/unittests/renderer/window-wayland-ivi}/main.cpp (85%) create mode 100644 tests/unittests/renderer/window-wayland-wl-shell/CMakeLists.txt rename {renderer/Platform/Window_Wayland_Shell/test => tests/unittests/renderer/window-wayland-wl-shell}/Window_Wayland_Shell_Test.cpp (76%) rename {renderer/Platform/Window_Wayland_Shell/test => tests/unittests/renderer/window-wayland-wl-shell}/main.cpp (100%) create mode 100644 tests/unittests/renderer/window-windows/CMakeLists.txt rename {renderer/Platform/Window_Windows/test => tests/unittests/renderer/window-windows}/Window_Window_Test.cpp (77%) create mode 100644 tests/unittests/renderer/window-x11/CMakeLists.txt rename {renderer/Platform/Platform_X11_EGL/test => tests/unittests/renderer/window-x11}/Window_X11_Test.cpp (86%) rename {integration/StressTests => tests/unittests/tools}/CMakeLists.txt (80%) create mode 100644 tests/unittests/tools/ramses-logic-viewer/CMakeLists.txt rename {client/logic/tools/ramses-logic-viewer-test => tests/unittests/tools/ramses-logic-viewer}/LogicViewerLuaTest.cpp (89%) rename {client/logic/tools/ramses-logic-viewer-test => tests/unittests/tools/ramses-logic-viewer}/LogicViewerTest.cpp (56%) rename {client/logic/tools/ramses-logic-viewer-test => tests/unittests/tools/ramses-logic-viewer}/LogicViewerTestBase.cpp (100%) rename {client/logic/tools/ramses-logic-viewer-test => tests/unittests/tools/ramses-logic-viewer}/LogicViewerTestBase.h (85%) create mode 100644 tools/CMakeLists.txt rename {ramses-daemon => tools/ramses-daemon}/CMakeLists.txt (100%) rename {ramses-daemon => tools/ramses-daemon}/main.cpp (73%) rename {utils => tools}/ramses-imgui/CMakeLists.txt (94%) rename {utils => tools}/ramses-imgui/include/ImguiClientHelper.h (70%) rename {utils => tools}/ramses-imgui/include/ImguiImageCache.h (80%) rename {utils => tools}/ramses-imgui/include/ImguiWidgets.h (84%) rename {utils => tools}/ramses-imgui/src/ImguiClientHelper.cpp (93%) rename {utils => tools}/ramses-imgui/src/ImguiImageCache.cpp (59%) rename {utils => tools}/ramses-imgui/src/ImguiWidgets.cpp (87%) rename {client/logic/tools => tools}/ramses-logic-viewer/Arguments.h (85%) rename {client/logic/tools => tools}/ramses-logic-viewer/CMakeLists.txt (83%) rename {client/logic/tools => tools}/ramses-logic-viewer/ImguiClientHelper.cpp (93%) rename {client/logic/tools => tools}/ramses-logic-viewer/ImguiClientHelper.h (69%) rename {client/logic/tools => tools}/ramses-logic-viewer/ImguiWrapper.h (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewer.cpp (79%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewer.h (87%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerApp.cpp (74%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerApp.h (87%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerGui.cpp (94%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerGui.h (98%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerGuiApp.cpp (95%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerGuiApp.h (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerHeadlessApp.cpp (91%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerHeadlessApp.h (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerLog.cpp (94%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerLog.h (95%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerLuaTypes.cpp (81%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerLuaTypes.h (84%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerSettings.cpp (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/LogicViewerSettings.h (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/Result.h (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/SceneSetup.h (89%) rename {client/logic/tools => tools}/ramses-logic-viewer/UpdateReportSummary.h (98%) rename {client/logic/tools => tools}/ramses-logic-viewer/main.cpp (100%) rename {client/logic/tools => tools}/ramses-logic-viewer/main_headless.cpp (100%) rename {renderer/ramses-renderer-main => tools/ramses-renderer-standalone}/CMakeLists.txt (92%) rename {renderer/ramses-renderer-main => tools/ramses-renderer-standalone}/src/main.cpp (82%) rename {utils => tools}/ramses-scene-viewer/CMakeLists.txt (100%) rename {utils => tools}/ramses-scene-viewer/src/ProgressMonitor.h (90%) rename {utils => tools}/ramses-scene-viewer/src/ResourceList.h (79%) rename {utils => tools}/ramses-scene-viewer/src/SceneSetup.h (81%) rename {utils => tools}/ramses-scene-viewer/src/SceneViewer.cpp (84%) rename {utils => tools}/ramses-scene-viewer/src/SceneViewer.h (83%) rename {utils => tools}/ramses-scene-viewer/src/SceneViewerGui.cpp (70%) rename {utils => tools}/ramses-scene-viewer/src/SceneViewerGui.h (53%) rename {utils => tools}/ramses-scene-viewer/src/main.cpp (93%) rename {utils => tools}/ramses-stream-viewer/CMakeLists.txt (100%) rename {utils => tools}/ramses-stream-viewer/src/main.cpp (89%) rename {client/logic/unittests/testAssetProducer => tools/test-asset-producer}/CMakeLists.txt (71%) rename {client/logic/unittests/testAssetProducer => tools/test-asset-producer}/main.cpp (80%) delete mode 100644 utils/CMakeLists.txt diff --git a/.clang-format b/.clang-format index 44119d0f8..f88676157 100644 --- a/.clang-format +++ b/.clang-format @@ -47,7 +47,7 @@ PointerAlignment: Left ReflowComments: true SpacesBeforeTrailingComments: 1 Cpp11BracedListStyle: true -Standard: Cpp11 +Standard: Cpp17 IndentWidth: 4 ContinuationIndentWidth: 4 UseTab: Never diff --git a/.clang-tidy b/.clang-tidy index 76673a577..4bb35640b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -8,237 +8,94 @@ --- Checks: - # disable all by default and enable selected checks '-*, - - bugprone-argument-comment, - bugprone-assert-side-effect, - bugprone-bool-pointer-implicit-conversion, - # TODO bugprone-branch-clone, - bugprone-copy-constructor-init, - bugprone-dangling-handle, - bugprone-exception-escape, - bugprone-fold-init-type, - bugprone-forward-declaration-namespace, - bugprone-forwarding-reference-overload, - bugprone-inaccurate-erase, - bugprone-incorrect-roundings, - bugprone-integer-division, - bugprone-lambda-function-name, - bugprone-macro-repeated-side-effects, - bugprone-misplaced-operator-in-strlen-in-alloc, - bugprone-misplaced-widening-cast, - bugprone-move-forwarding-reference, - bugprone-multiple-statement-macro, - bugprone-parent-virtual-call, - bugprone-posix-return, - bugprone-sizeof-container, - bugprone-sizeof-expression, - bugprone-string-constructor, - bugprone-string-integer-assignment, - bugprone-string-literal-with-embedded-nul, - bugprone-suspicious-enum-usage, - bugprone-suspicious-memset-usage, - bugprone-suspicious-missing-comma, - bugprone-suspicious-semicolon, - bugprone-suspicious-string-compare, - bugprone-swapped-arguments, - bugprone-terminating-continue, - bugprone-throw-keyword-missing, - bugprone-too-small-loop-variable, - bugprone-undefined-memory-manipulation, - bugprone-undelegated-constructor, - # TODO(tobias) false positives: bugprone-unhandled-self-assignment, - bugprone-unused-raii, - bugprone-unused-return-value, - bugprone-use-after-move, - bugprone-virtual-near-miss, - bugprone-spuriously-wake-up-functions, - bugprone-suspicious-include, - bugprone-misplaced-pointer-arithmetic-in-alloc, - bugprone-reserved-identifier, + bugprone-*, + -bugprone-argument-comment, + -bugprone-branch-clone, + -bugprone-no-escape, + -bugprone-narrowing-conversions, + -bugprone-redundant-branch-condition, + -bugprone-signal-handler, cert-dcl16-c, cert-dcl21-cpp, - cert-dcl54-cpp, + cert-dcl50-cpp, cert-dcl58-cpp, cert-dcl59-cpp, cert-env33-c, - cert-err09-cpp, + cert-err34-c, cert-err52-cpp, cert-err60-cpp, - cert-err61-cpp, - cert-fio38-c, - cert-msc32-c, + cert-mem57-cpp, + cert-msc50-cpp, cert-msc51-cpp, - cert-oop11-cpp, - cert-dcl50-cpp, - cert-flp30-c, - # TODO(tobias) false positives: cert-oop54-cpp, - cert-oop57-cpp, + cert-oop58-cpp, clang-analyzer-apiModeling.StdCLibraryFunctions, - clang-analyzer-apiModeling.TrustNonnull, - clang-analyzer-apiModeling.google.GTest, - clang-analyzer-apiModeling.llvm.CastValue, - clang-analyzer-apiModeling.llvm.ReturnValue, - clang-analyzer-core.CallAndMessage, - clang-analyzer-core.DivideZero, - clang-analyzer-core.DynamicTypePropagation, - clang-analyzer-core.NonNullParamChecker, - clang-analyzer-core.NonnilStringConstants, - clang-analyzer-core.NullDereference, - clang-analyzer-core.StackAddrEscapeBase, - clang-analyzer-core.StackAddressEscape, - clang-analyzer-core.UndefinedBinaryOperatorResult, - clang-analyzer-core.VLASize, - clang-analyzer-core.builtin.BuiltinFunctions, - clang-analyzer-core.builtin.NoReturnFunctions, - clang-analyzer-core.uninitialized.ArraySubscript, - clang-analyzer-core.uninitialized.Assign, - clang-analyzer-core.uninitialized.Branch, - clang-analyzer-core.uninitialized.CapturedBlockVariable, - clang-analyzer-core.uninitialized.UndefReturn, + clang-analyzer-core*, + clang-analyzer-cplusplus.*, clang-analyzer-deadcode.DeadStores, - clang-analyzer-nullability.NullPassedToNonnull, - clang-analyzer-nullability.NullReturnedFromNonnull, - clang-analyzer-nullability.NullabilityBase, - clang-analyzer-nullability.NullableDereferenced, - clang-analyzer-nullability.NullablePassedToNonnull, - clang-analyzer-nullability.NullableReturnedFromNonnull, clang-analyzer-optin.cplusplus.UninitializedObject, - clang-analyzer-optin.mpi.MPI-Checker, - clang-analyzer-optin.performance.GCDAntipattern, + clang-analyzer-optin.cplusplus.VirtualCall, clang-analyzer-optin.performance.Padding, clang-analyzer-optin.portability.UnixAPI, - clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, - clang-analyzer-security.insecureAPI.SecuritySyntaxChecker, - clang-analyzer-security.insecureAPI.UncheckedReturn, - clang-analyzer-security.insecureAPI.bcmp, - clang-analyzer-security.insecureAPI.bcopy, - clang-analyzer-security.insecureAPI.bzero, - clang-analyzer-security.insecureAPI.getpw, - clang-analyzer-security.insecureAPI.gets, - clang-analyzer-security.insecureAPI.mkstemp, - clang-analyzer-security.insecureAPI.mktemp, - clang-analyzer-security.insecureAPI.rand, - clang-analyzer-security.insecureAPI.strcpy, - clang-analyzer-security.insecureAPI.vfork, - clang-analyzer-unix.API, - clang-analyzer-unix.DynamicMemoryModeling, - clang-analyzer-unix.Malloc, - clang-analyzer-unix.MallocSizeof, - clang-analyzer-unix.MismatchedDeallocator, - clang-analyzer-unix.Vfork, - clang-analyzer-unix.cstring.BadSizeArg, - clang-analyzer-unix.cstring.CStringModeling, - clang-analyzer-unix.cstring.NullArg, - clang-analyzer-valist.CopyToSelf, - clang-analyzer-valist.Uninitialized, - clang-analyzer-valist.Unterminated, - clang-analyzer-valist.ValistBase, + clang-analyzer-security.*, + -clang-analyzer-security.insecureAPI.decodeValueOfObjCType, + clang-analyzer-unix.*, + clang-analyzer-valist.*, - clang-analyzer-cplusplus.InnerPointer, - clang-analyzer-cplusplus.Move, - clang-analyzer-cplusplus.NewDelete, - clang-analyzer-cplusplus.NewDeleteLeaks, - clang-analyzer-cplusplus.SelfAssignment, - clang-analyzer-cplusplus.SmartPtr, - # TODO clang-analyzer-optin.cplusplus.VirtualCall, - clang-analyzer-security.FloatLoopCounter, + cppcoreguidelines-avoid-goto, + cppcoreguidelines-c-copy-assignment-signature, + cppcoreguidelines-init-variables, + cppcoreguidelines-interfaces-global-init, + cppcoreguidelines-narrowing-conversions, + cppcoreguidelines-no-malloc, + cppcoreguidelines-pro-type-cstyle-cast, + cppcoreguidelines-pro-type-reinterpret-cast, + cppcoreguidelines-pro-type-vararg, + cppcoreguidelines-slicing, + google-build-explicit-make-pair, + google-default-arguments, google-explicit-constructor, google-global-names-in-headers, google-readability-casting, - # TODO google-readability-todo, + google-runtime-int, google-runtime-operator, - google-build-explicit-make-pair, + google-upgrade-googletest-case, - # alias: hicpp-avoid-goto, - # TODO hicpp-multiway-paths-covered, - # TODO hicpp-named-parameter, + hicpp-exception-baseclass, + hicpp-multiway-paths-covered, hicpp-no-assembler, - hicpp-noexcept-move, hicpp-signed-bitwise, - hicpp-exception-baseclass, - - misc-redundant-expression, - misc-unused-alias-decls, - misc-unused-parameters, - misc-unused-using-decls, - misc-definitions-in-headers, - # TODO misc-misplaced-const, - misc-redundant-expression, - misc-unused-alias-decls, - misc-unused-parameters, - misc-unused-using-decls, - misc-unconventional-assign-operator, - - modernize-avoid-c-arrays, - modernize-use-nullptr, - modernize-use-bool-literals, - modernize-raw-string-literal, - modernize-use-nodiscard, - modernize-use-using, - modernize-deprecated-headers, - modernize-make-unique, - modernize-make-shared, - modernize-use-override, - - performance-faster-string-find, - performance-for-range-copy, - performance-implicit-conversion-in-loop, - performance-inefficient-algorithm, - performance-inefficient-string-concatenation, - performance-inefficient-vector-operation, - performance-move-constructor-init, - performance-noexcept-move-constructor, - performance-type-promotion-in-math-fn, - performance-unnecessary-copy-initialization, - performance-unnecessary-value-param, - performance-no-automatic-move, - - portability-simd-intrinsics, - - readability-redundant-control-flow, - readability-redundant-declaration, - readability-redundant-function-ptr-dereference, - readability-redundant-preprocessor, - readability-redundant-smartptr-get, - readability-redundant-string-cstr - readability-braces-around-statements, - # TODO readability-container-size-empty, - # TODO readability-convert-member-functions-to-static, - readability-deleted-default, - readability-isolate-declaration, - readability-delete-null-pointer, - readability-inconsistent-declaration-parameter-name, - readability-non-const-parameter, - readability-redundant-control-flow, - readability-redundant-declaration, - readability-redundant-function-ptr-dereference, - readability-redundant-smartptr-get, - readability-redundant-string-cstr, - readability-redundant-string-init, - readability-simplify-subscript-expr, - # TODO readability-static-accessed-through-instance, - readability-static-definition-in-anonymous-namespace, - readability-string-compare, - readability-uniqueptr-delete-release, - readability-misleading-indentation, - readability-misplaced-array-index, - readability-implicit-bool-conversion, - cppcoreguidelines-avoid-goto, - cppcoreguidelines-c-copy-assignment-signature, - cppcoreguidelines-interfaces-global-init, - # TODO cppcoreguidelines-narrowing-conversions, - cppcoreguidelines-no-malloc, - # TODO cppcoreguidelines-pro-type-cstyle-cast, - cppcoreguidelines-slicing, - cppcoreguidelines-avoid-non-const-global-variable, - cppcoreguidelines-pro-type-reinterpret-cast, + misc-*, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + + modernize-*, + -modernize-concat-nested-namespaces, + -modernize-replace-disallow-copy-and-assign-macro, + -modernize-use-trailing-return-type, + -modernize-use-transparent-functors, + + performance-*, + -performance-no-int-to-ptr, + + readability-*, + -readability-avoid-const-params-in-decls, + -readability-function-cognitive-complexity, + -readability-function-size, + -readability-identifier-naming, + -readability-magic-numbers, + -readability-make-member-function-const, + -readability-qualified-auto, + -readability-redundant-access-specifiers, + -readability-simplify-boolean-expr, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof, ' + # everything that is enabled is also an error WarningsAsErrors: '*' @@ -246,24 +103,43 @@ WarningsAsErrors: '*' HeaderFilterRegex: '.*' AnalyzeTemporaryDtors: false -FormatStyle: none +FormatStyle: File CheckOptions: - - key: modernize-use-nullptr.NullMacros - value: 'NULL' + - key: bugprone-assert-side-effect.AssertMacros + value: 'Q_ASSERT,Q_ASSERT_X,Q_CHECK_PTR' + - key: bugprone-assert-side-effect.CheckFunctionCalls + value: '1' + - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression + value: '1' + - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: '1' + + - key: cppcoreguidelines-narrowing-conversions.PedanticMode + value: '1' + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions + value: '1' + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: '1' - - key: cert-oop57-cpp.MemSetNames - value: ramses_internal::PlatformMemory::Set - - key: cert-oop57-cpp.MemCpyNames - value: ramses_internal::PlatformMemory::Copy - - key: cert-oop57-cpp.MemCmpNames - value: ramses_internal::PlatformMemory::Compare - - - key: readability-implicit-bool-conversion.AllowIntegerConditions - value: true - - key: readability-implicit-bool-conversion.AllowPointerConditions + - key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals value: true + - key: hicpp-multiway-paths-covered.WarnOnMissingElse + value: '0' + + - key: misc-throw-by-value-catch-by-reference.WarnOnLargeObject + value: '1' + - key: modernize-use-override.AllowOverrideAndFinal value: true - - key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals + + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: false + + - key: readability-braces-around-statements.ShortStatementLines + value: '2' + - key: readability-implicit-bool-conversion.AllowPointerConditions value: true + + +... diff --git a/.gitmodules b/.gitmodules index 57df9d4d3..90013a265 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,36 +1,36 @@ [submodule "external/harfbuzz"] - url = https://github.com/harfbuzz/harfbuzz.git - path = external/harfbuzz + url = https://github.com/harfbuzz/harfbuzz.git + path = external/harfbuzz [submodule "external/googletest"] - url = https://github.com/google/googletest.git - path = external/googletest + url = https://github.com/google/googletest.git + path = external/googletest [submodule "external/glm"] - url = https://github.com/g-truc/glm.git - path = external/glm + url = https://github.com/g-truc/glm.git + path = external/glm [submodule "external/glslang"] - url = https://github.com/KhronosGroup/glslang.git - path = external/glslang + url = https://github.com/KhronosGroup/glslang.git + path = external/glslang [submodule "external/freetype"] - url = https://github.com/freetype/freetype.git - path = external/freetype + url = https://github.com/freetype/freetype.git + path = external/freetype [submodule "external/asio"] - url = https://github.com/chriskohlhoff/asio.git - path = external/asio + url = https://github.com/chriskohlhoff/asio.git + path = external/asio [submodule "external/lz4"] - url = https://github.com/lz4/lz4.git - path = external/lz4 + url = https://github.com/lz4/lz4.git + path = external/lz4 [submodule "external/fmt"] - url = https://github.com/fmtlib/fmt.git - path = external/fmt + url = https://github.com/fmtlib/fmt.git + path = external/fmt [submodule "external/abseil"] - url = https://github.com/abseil/abseil-cpp.git - path = external/abseil + url = https://github.com/abseil/abseil-cpp.git + path = external/abseil [submodule "external/imgui"] - url = https://github.com/ocornut/imgui.git - path = external/imgui + url = https://github.com/ocornut/imgui.git + path = external/imgui [submodule "external/cli11"] - url = https://github.com/CLIUtils/CLI11.git - path = external/cli11 + url = https://github.com/CLIUtils/CLI11.git + path = external/cli11 [submodule "external/google-benchmark"] path = external/google-benchmark url = https://github.com/google/benchmark.git @@ -44,5 +44,5 @@ path = external/sol url = https://github.com/ThePhD/sol2.git [submodule "external/ANGLE"] - path = external/ANGLE - url = https://github.com/bmwcarit/ANGLE.git + path = external/ANGLE + url = https://github.com/bmwcarit/ANGLE.git diff --git a/.readthedocs.yml b/.readthedocs.yml index e2eecca32..2197e6287 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2020 BMW AG +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -13,12 +13,16 @@ --- version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.10" + sphinx: - configuration: doc/sphinx/conf.py - fail_on_warning: true + configuration: doc/sphinx/conf.py + fail_on_warning: true python: - version: 3.7 - install: - - requirements: doc/sphinx/requirements.txt + install: + - requirements: doc/sphinx/requirements.txt ... diff --git a/.yamllint b/.yamllint index 718bcb8a8..ef7ebab06 100644 --- a/.yamllint +++ b/.yamllint @@ -23,6 +23,7 @@ ignore: | /external/google-benchmark/ /external/glslang/ /external/lua/ + /external/ANGLE/ rules: line-length: disable diff --git a/CHANGELOG.md b/CHANGELOG.md index 123f44e07..7e0ca7cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,150 @@ # Ramses Changelog +28.0.0-rc2 +------------------- +### Added + +- Added periodic statistics logs with more details and `EStatisticsLogMode` to select the log mode + - If detailed statistics is selected, the slowest executed nodes are shown +- Added support for creating renderer with iOS window +- Added native support for bool uniforms, no need to use int32_t anymore to set them + - existing application code using `Appearance::setInputValue` with `int32_t` type to set boolean shader uniforms must change to use `bool` type now! +- Supported bool for the data container `ramses::DataObject` +- Added `RamsesFramework::getFeatureLevel()` to easily guery EFeatureLevel +- Added `RamsesClient::getRamsesFramework()` to be able to get reference to `RamsesFramework` from `RamsesClient` + - this can be used for example to get feature level just by having a `Scene` instance: myScene.getRamsesClient().getRamsesFramework().getFeatureLevel() +- Added `SceneObject::getScene()` to get reference to owning `Scene` from any object created from that Scene +- Added a non-const version of `LogicNode::getOutputs()` +- Added a non-const version of `Property::getIncomingLink()` and `Property::getOutgoingLink()` +- Added a non-const version of `Property::getIncomingLink()` and `Property::getOutgoingLink()` +- Added support for 16bit and 32bit depth render buffers: `ERenderBufferFormat::Depth16`, `ERenderBufferFormat::Depth32` +- Added common cast operators for ramses and logic objects: `T* RamsesObject::as()` `T ramses::object_cast(RamsesObject*)` + +### Changed + +- Switched from fmt 7.0.1 to 10.1.1 +- LogicEngine is now part of Scene and can only be created using Scene::createLogicEngine + - LogicEngine is now a SceneObject of type ERamsesObjectType::LogicEngine and its lifecycle is managed by Scene as for any other SceneObject. LogicEngine is no longer a movable type. + - removed scene argument from all loading functions in LogicEngine: loadFromFile, loadFromFileDescriptor, loadFromBuffer +- LogicObject now derives from SceneObject and therefore RamsesObject + - LogicObject::getId is renamed to LogicObject::getLogicObjectId + - LogicObject::setName/getName are removed, methods with same name and functionality (but different return type!) are in base class RamsesObject + - LogicObject::set/getUserId moved to its base RamsesObject so any RamsesObject can use it now +- RamsesObject.h and RamsesObjectTypes.h moved from `client` include folder to `framework`, update include paths accordingly +- RamsesObject::getName returns std::string_view instead of const char* +- Reworked API error handling for all public Ramses classes: + - removed `status_t`, `StatusOK`, `StatusObject::getStatusMessage`, `LogicEngine::getErrors`, `ErrorData` and the whole `StatusObject` class + - all API methods that can fail (at API call time) return `bool` (true for success) + - errors are now collected centrally in `RamsesFramework` + - `RamsesFramework::getLastError` can be used to get the last error that occurred (as optional), calling it will clear the error + - configs and other not managed objects (`RamsesFrameworkConfig`, `RendererConfig`, `DisplayConfig`,`SceneConfig`, `RenderTargetDescription`, `EffectDescription`, `EffectInput`) + no longer inherit from StatusObject. Their methods also return `bool` but error messages are only logged (not tracked with `getLastError`). + - `RenderTargetDescription::addRenderBuffer` has optional argument to get human readable description of error if failed +- Export whole API classes in shared libs, instead of exporting only public functions +- Changed default publishing mode of scenes from EScenePublicationMode::LocalAndRemote to EScenePublicationMode::LocalOnly + - local-only scenes cannot be sent over network, as the name suggests + - local-only scenes must be periodically flushed in order to get to Ready/Rendered state if using RendererSceneControl + - local-only scenes will not keep static resources in system memory after they are uploaded to VRAM + - local-only scenes benefit from slight performance gain when flushing and slightly smaller system memory footprint +- Changed `LogicEngine::link()`, `LogicEngine::linkWeak()` and `LogicEngine::unlink()` to take non-const Property objects as inputs +- RamsesUtils changed to a namespace instead of class of static functions +- Changed include directory structure, and added a migrate script + - Frameowrk API headers are found under "ramses/framework" + - Client API headers are found under "ramses/client" + - Text API headers are found under "ramses/client/text" + - Logic API headers are found under "ramses/client/logic" + - Renderer API headers are found under "ramses/renderer" + - Added a script scripts/migrate_to_28_0_0.py that can migrate all includes in a codebase to new format +- Forbade to use the default constructors of `UniformInput` and `AttributeInput` to create objects but objects can be + - created via copy and move constructors, or + - obtained via `getUniformInput`, `getAttributeInput`, `findUniformInput`, and `findAttributeInput` +- Changed to return `optional` or `optional` objects in `getUniformInput`, `getAttributeInput`, `findUniformInput`, and `findAttributeInput` + - Removed `EffectInput::isValid` because `UniformInput` and `AttributeInput` objects are always valid now + - Changed `EffectInput::getDataType` to return `EDataType` instead of `optional` +- DisplayConfig::validate produces warnings or errors if suboptimal or wrong settings are set, e.g., incompatible window and device types are set +- Remove the 4096 upper limit of offscreen buffer size in RamsesRenderer. The buffer size is limited by the driver. +- Changed LogHandlerFunc to use `std::string_view` instead of `const std::string&` +- ramses-logic logs are output to DLT (context 'RCLI') +- Changed clear flags parameters from `uint32_t` to `ramses::ClearFlags` (`ramses::Flags`) +- Remove cmake option `ramses-sdk_BUILD_IVI_TEST_APPS` + - ivi-gears and ivi-simple-dmabuf-egl are now controlled with option for building tools +- Remove check for cmake variable `ramses-sdk_DISABLE_WAYLAND_IVI_EXTENSION`. The extensions get built if the required + dependencies are available +- Changed key modifier flags parameters from `uint32_t`to `ramses::KeyModifiers` (`ramses::Flags`) +- Moved `ramses-client-api/TextureEnums.h` to `ramses/framework/TextureEnums.h` +- Moved `ramses-client-api/ERotationType.h` to `ramses/framework/ERotationType.h` +- Moved `ramses-client-api/EVisibilityMode.h` to `ramses/framework/EVisibilityMode.h` +- Moved `ramses-client-api/EScenePublicationMode.h` to `ramses/framework/EScenePublicationMode.h` +- MeshNode instance count can be zero (nothing drawn) to match OpenGL behavior +- OpenGL drawcalls are always using glDrawElementsInstanced even for instance count 1 +- `ETextureAddressMode` enum is now explicitly typed to uint8_t +- `ETextureSamplingMethod` enum is now explicitly typed to uint8_t +- `ETextureFormat` enum is now explicitly typed to uint8_t +- The scene validation will not return `StatusOK` if effects have shader warnings, and these warnings will appear in the validation report with a severity of `EValidationSeverity::Warning` +- `DisplayConfig::setX11WindowHandle`: changed parameter from `unsigned long` to `X11WindowHandle` +- `IFontInstance::getAllSupportedCharacters()`: changed return type set from `unsigned long` to `char32_t` +- Replaced `Scene::findObjectByName(..)`/`Scene::findObjectById(..)` by `T* Scene::findObject(..)` +- Replaced `LogicEngine::findByName(..)`/`LogicEngine::findLogicObjectById(..)` by `T* LogicEngine::findObject(..)` +- Changed scene validation interface to report a list of messages (see `ramses::RamsesObject::validate(ValidationReport&)`) +- Replaced `const std::vector& LogicEngine::validate()` by common Ramses validation (`ramses::RamsesObject::validate(ValidationReport&)`) +- Added SaveFileConfig parameter to `ramses::Scene::saveToFile()`, removed compression flag + - compression flag is part of SaveFileConfig and is disabled by default + - by default `saveToFile()` will fail, if the scene has validation errors (warnings will be ignored) +- Changed interfaces to load/create scenes: Added sceneId and verification flag to `ramses::SceneConfig` +- Renamed ramses::EClearFlags to ramses::EClearFlag +- Renamed standalone renderer executable to `ramses-renderer-standalone` +- Renamed `ERamsesObjectType::ArrayBufferObject` to `ERamsesObjectType::ArrayBuffer` to match the class name +- Renamed the classes for bindings to Ramses objects + - Renamed `RamsesAppearanceBinding` to `AppearanceBinding` + - Renamed `RamsesCameraBinding` to `CameraBinding` + - Renamed `RamsesNodeBinding` to `NodeBinding` + - Renamed `RamsesRenderPassBinding` to `RenderPassBinding` + - Renamed `RamsesRenderGroupBinding` to `RenderGroupBinding` and `RamsesRenderGroupBindingElements` to `RenderGroupBindingElements` + - Renamed `RamsesMeshNodeBinding` to `MeshNodeBinding` +- Renamed `GeometryBinding` to `Geometry` + +### Removed + +- Removed `IRendererResourceCache`, `DefaultRendererResourceCache` and `RendererConfig::setRendererResourceCache()` +- Removed `resourceCacheFlag_t` type and updated relavant classes and implementations + - Removed `ResourceCacheFlag_DoNotCache` + - Removed resourceCacheFlag_t optional argument from all Scene::create*Resource + - Removed `resourceCacheFlagTag` +- Removed default rotation convention from Node::setRotation(vec3f,rotationType) +- Removed support for 32-bit architectures. +- Removed `LogicEngine::setStatisticsLogLevel`: periodic log level is always set to Info +- Removed obsolete `RamsesLogicVersion`, use `RamsesVersion` instead +- Removed `SceneObject::getSceneId()`, use `SceneObject::getScene().getSceneId()` instead +- Removed `DisplayConfig::keepEffectsUploaded()` and `--delete-effects` cli option. Effects are always kept uploaded now. +- Removed `DisplayConfig::setWindowBorderless()` and `--borderless` cli option. +- Removed ERamsesObjectType::NUMBER_OF_TYPES +- Removed ETextureFormat::Invalid +- Removed ramses::AllFeatureLevels array +- Removed ramses-logic logging API: + - `Logger::SetVerbosityLimit()`: Use RamsesFrameworkConfig to set loglevel for context 'RCLI' (Info by default) + - `Logger::SetLogHandler()`: Use RamsesFramework::SetLogHandler() + - `Logger::SetDefaultLogging()`: Use RamsesFrameworkConfig::setLogLevelConsole(ELogLevel::Off) to disable console output +- Removed cmake option `ramses-sdk_BUILD_DAEMON`. Now `ramses-deamon` is controlled by cmake option for tools `ramses-sdk_BUILD_TOOLS` +- Removed `ERenderBufferType` from all API (removed RenderBuffer::getBufferType, changed Scene::createRenderBuffer) +- Removed `ramses::RamsesUtils::TryConvert`. Use `ramses::object_cast` instead. +- Removed deprecated method overload `LogicEngine::createLuaInterface` without LuaConfig parameter in favor of providing an empty `LuaConfig` as the default argument. + Now modules declaration will always be verified (also when not explicitly providing `LuaConfig`). +- Removed `LogicEngine::saveToFile()`, `LogicEngine::load*` and `LogicEngine::GetFeatureLevelFrom*` methods. Logic is stored in the ramses scene. +- Removed logic object ID and related API: `LogicObject::getLogicObjectId`, `LogicEngine::findObjectById` + - use sceneObjectId_t instead as a unique numerical Id for objects +- Removed `logicfile` command line parameter from ramses-logic-viewer + +### Fixed + +- RamsesMeshNodeBinding now fails update if it receives invalid value (e.g. negative index count) +- Fixed RamsesNodeBinding visibility property inconsistency if saved with Ramses node 3-state visibility as 'Off' + but properties 'enable'=false and 'visible'=true +- Added checks to prevent crash when logging due to Fmt issue (triggered by some string_views on Ubuntu 20) 28.0.0-rc1 ------------------- -### Added +### Added - Added Ramses logic to the Ramses repository - If you want to exclude logic, disable it over CMake (ramses-sdk_ENABLE_LOGIC=OFF) @@ -25,7 +165,7 @@ - Added DisplayConfig::setDeviceType() and DisplayConfig::getDeviceType() to select device type for display creation - Added DisplayConfig::setWindowType() and DisplayConfig::getWindowType() to select window type for display creation -### Changed +### Changed - RamsesFrameworkConfig constructor needs a mandatory argument to specify feature level (see EFeatureLevel for more information) - Replaced uint32_t with size_t throughout the API where applicable: `Appearance`, `Effect`, `EffectDescription`, `Node`, `RamsesUtils`, `Scene`, `Texture2DBuffer`, `UniformInput`, `IRendererSceneControlEventHandler::objectsPicked()`. @@ -105,17 +245,16 @@ - RamsesFrameworkConfig, RendererConfig, DisplayConfig, UniformInput, AttributeInput, EffectDescription, RenderTargetDescription ETextureCubeFace, ERenderTargetDepthBufferType, ERenderBufferType, ERenderBufferFormat, ERenderBufferAccessMode - The const char* parameters of the public API are replaced by std::string_view. +- The pointers to data memory (std::uint8_t*, unsigned char*) of the public API are replaced by std::byte*. -### Removed +### Removed - Removed cmake option ramses-sdk_CONSOLE_LOGLEVEL (use RamsesFrameworkConfig to configure log levels) - Removed environment variables CONSOLE_LOGLEVEL and RAMSES_LOGLEVEL (use RamsesFrameworkConfig to configure log levels) - Removed RamsesFramework::SetConsoleLogLevel (use RamsesFrameworkConfig::setLogLevelConsole) -- Removed ramses-ptx-export utility (Use RamsesComposer to create scene files). - Removed AnimationSystem and all related classes, animations moved from Ramses to Ramses Logic. - Removed text generator. - Removed visual frame profiler ('fp' Ramsh command). -- Removed SomeIP support. - Removed tools for generating and packing effects from shaders (ramses-utils, ramses-resource-tools, ramses-shader-tools). - Removed ramses-packet-player. - Removed DCSM support and all related API. @@ -141,7 +280,7 @@ - Removed rlogic::ELogMessageType (replaced by ramses::ELogLevel) - Removed deprecated enum ESceneResourceStatus -### Fixed +### Fixed - Fixed potential renderer crash when having more than one displays in non-threaded mode and destroying all but one due to missing context enable - Fixed race condition when instantiating multiple RamsesFramework instances in different threads @@ -149,30 +288,88 @@ ## Older releases -27.0.132 +27.0.137 ------------------- + General changes + ------------------------------------------------------------------------ + - Unified logging for ivi surfaces and layers (ids are prefixed by "ivi-surface:"/"ivi-layer:") + - Dcsm periodic logs resolve technical content to scene id or ivi surface + Bugfixes ------------------------------------------------------------------------ - - Fixed wrong events given by DcsmContentControlEventHandlerMock::contentLinkedToTextureConsumer - Previously could report linked events for non-linked contents (also with wrong datalink id) when the - contents shared technical id (like same wayland surface) internally + - Fixed stream surface availability if a wayland client detaches/re-attaches ivi-surfaces in a single frame -27.0.131 +27.0.136 ------------------- General changes ------------------------------------------------------------------------ - - ramses-packet-player: - - enable remote rendering for replayed scenes - - added "--dcsm" command line parameter to replay on DcsmConsumers - - added "--someip" command line parameter to connect to SomeIP remotes - - added "--play" option to automatically replay after start - - show details for array resources - - added "--ivi-surface", "--ivi-layer" options to run packet-player as wayland-ivi client - - added inspection gui for the current scene state + - Optimized texture buffer uploads when frequently updating subregions - API changes + Bugfixes ------------------------------------------------------------------------ - - Added predefined IDs for FocusRequest handling + - Ramses explicitly requests OpenGL ES 3.2 context instead of ES 3.x. + If 3.2 is not available it will automatically downgrade to 3.1 or 3.0 and log and error message + +27.0.135 +------------------- + General changes + ------------------------------------------------------------------------ + - ramses-scene-viewer reports warnings for non-optimal shaders: + - interface mismatch between shader stages (unused in and out varyings) + - precision mismatch between shader stages + - unused varyings + - unused uniforms + - Added documentation about ramses::Appearance default values + - Disable ramses-sdk_ENABLE_HARFBUZZ_LEGACY_SHAPING for Integrity. + It's now disabled by default for all platforms. + +27.0.134 +------------------- + Bugfixes + ------------------------------------------------------------------------ + - Fixed race condition when instantiating multiple RamsesFramework instances in different threads + +27.0.133 +------------------- + General changes + ------------------------------------------------------------------------ + - improved scene dumps by ramsh / DLT injection + - Added alias for 'dumpSceneToFile': 'dumpScene' + - Filename is optional if -sendViaDLT is enabled + - Added '-flush' option for ramsh command: dumpSceneToFile + - ramses-scene-viewer: + - show contents of Texture2DBuffer objects + - added/improved ramsh commands: + - fixed parsing large integer values + - 'p,screenshot' can capture offscreen buffers + - 'clc' can clear offscreen buffers + - 'scenestate' modifies the scene state + - 'obCreate' creates an offscreen buffer + - 'assign' assigns a scene to a display / offscreenbuffer + - 'link'/'unlink' connects offscreenbuffer to a texture consumer + + Bugfixes + ------------------------------------------------------------------------ + - Fixed HarfBuzz shaping for text blocks with special characters (punctuation, + diacritics, ligature modifiers, private use). This affects positioning (e.g. + kerning) and ligature creation for Arabic, Thai, Vietnamese and Japanese, + as well as characters common to all languages. + Previous behavior can be enabled using ramses-sdk_ENABLE_HARFBUZZ_LEGACY_SHAPING. + - Fixed possible crash when shader compilation fails with an empty error message. + - Fixed renderer crash if StreamBuffer with same ID is created or non-existing StreamBuffer is destroyed + - Fixed potential renderer crash when having more than one displays in non-threaded mode + and destroying all but one due to missing context enable + - Fixed missing streambuffer deletion towards renderer from + DcsmContentControl in cases when stream became unavailable while contents using + it were in state Ready or Shown + +27.0.132 +------------------- + Bugfixes + ------------------------------------------------------------------------ + - Fixed wrong events given by DcsmContentControlEventHandlerMock::contentLinkedToTextureConsumer + Previously could report linked events for non-linked contents (also with wrong datalink id) when the + contents shared technical id (like same wayland surface) internally 27.0.130 ------------------- @@ -496,30 +693,6 @@ - Fix segfault caused by activating VAO that was not uploaded due to wrong handling of dirtiness in resource cached scene. -27.0.106 -------------------- - General changes - ------------------------------------------------------------------------ - - Display creation DOES fail if EC creation fails - - A change was introduced as a temporary measure in 27.0.105 that lets display - creation not fail if EC creation fails. This change is reverted and a proper - fix is introduced. See API changes of RendererConfig and DisplayConfig. - - API changes - ------------------------------------------------------------------------ - - API for configuring EC is moved from RendererConfig to DisplayConfig - - Allows configuring EC per display - - API in RendererConfig is not removed. It is marked as deprecated and always fails - - Add layoutAvailability field to DCSM metadata - - Bugfixes - ------------------------------------------------------------------------ - - Failing to create EGL image from DMA buffer does not lead to a segfault - in RAMSES renderer. An error code ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER - is sent to wayland client. - - Fix segfault caused by activating VAO that was not uploaded due to wrong handling of dirtiness - in resource cached scene. - 27.0.105 ------------------- General changes diff --git a/CMakeLists.txt b/CMakeLists.txt index 45dfb565f..ee4851b18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,17 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -CMAKE_MINIMUM_REQUIRED(VERSION 3.10) +CMAKE_MINIMUM_REQUIRED(VERSION 3.13) set(RAMSES_VERSION_MAJOR 28) set(RAMSES_VERSION_MINOR 0) set(RAMSES_VERSION_PATCH 0) -set(RAMSES_VERSION_POSTFIX "-rc1") +set(RAMSES_VERSION_POSTFIX "-rc2") set(RAMSES_VERSION "${RAMSES_VERSION_MAJOR}.${RAMSES_VERSION_MINOR}.${RAMSES_VERSION_PATCH}${RAMSES_VERSION_POSTFIX}") project(ramses-sdk @@ -40,6 +40,7 @@ option(ramses-sdk_ENABLE_DEFAULT_WINDOW_TYPE "Enable a default window option(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS "Enable building for Windows window" OFF) option(ramses-sdk_ENABLE_WINDOW_TYPE_X11 "Enable building for X11 window" OFF) option(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID "Enable building for Android window" OFF) +option(ramses-sdk_ENABLE_WINDOW_TYPE_IOS "Enable building for iOS window" OFF) option(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI "Enable building for Wayland ivi window" OFF) option(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL "Enable building for Wayland wl_shell window" OFF) @@ -49,7 +50,6 @@ option(ramses-sdk_BUILD_HEADLESS_SHARED_LIB "Enable building headles # optional components option(ramses-sdk_ENABLE_LOGIC "Enable ramses logic - a component for scripting and animation." ON) -option(ramses-sdk_BUILD_DAEMON "Build the ramses daemon." ON) option(ramses-sdk_TEXT_SUPPORT "Enable/disable the ramses text API." ON) option(ramses-sdk_ENABLE_TCP_SUPPORT "Enable use of TCP communication." ON) option(ramses-sdk_ENABLE_DLT "Enable DLT logging support." ON) @@ -70,6 +70,8 @@ option(ramses-sdk_USE_IMAGEMAGICK "Enable tests depending option(ramses-sdk_ALLOW_PLATFORM_GLM "Enable to search for platform provided OpenGL Math libary (glm)." ON) option(ramses-sdk_ALLOW_PLATFORM_FREETYPE "Enable to search for platform provided freetype and harfbuzz." ON) option(ramses-sdk_ALLOW_CUSTOM_FREETYPE "Allow usage of custom freetype and harfbuzz if platform provided one was not found." ON) +option(ramses-sdk_ALLOW_PLATFORM_LZ4 "Enable to search for platform provided lz4" ON) +option(ramses-sdk_USE_PLATFORM_LUAJIT "Uses platform provided luajit instead of internal lua" OFF) # other options set(ramses-sdk_FOLDER_PREFIX "" CACHE STRING "Optional folder prefix for targets in visual studio.") @@ -95,21 +97,29 @@ include(cmake/ramses/folderize.cmake) include(cmake/ramses/testConfig.cmake) include(cmake/ramses/buildConfig.cmake) include(cmake/ramses/createTarget.cmake) -include(cmake/ramses/addSubdirectory.cmake) + +if(ramses-sdk_ENABLE_FLATBUFFERS_GENERATION) + include(cmake/ramses/flatbuffersGeneration.cmake) +endif() set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ) -if (CMAKE_BUILD_TYPE) +# 32-bit builds are not supported +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + message(FATAL_ERROR "32-bit architectures not supported.") +endif() + +if(CMAKE_BUILD_TYPE) # Case-insensitive comparison required, build type is not case-sensitive string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER_CASE) if(NOT("${CMAKE_BUILD_TYPE_LOWER_CASE}" STREQUAL "debug" OR "${CMAKE_BUILD_TYPE_LOWER_CASE}" STREQUAL "release" OR "${CMAKE_BUILD_TYPE_LOWER_CASE}" STREQUAL "relwithdebinfo" OR "${CMAKE_BUILD_TYPE_LOWER_CASE}" STREQUAL "minsizerel")) message(FATAL_ERROR "Build type set to unsupported type ${CMAKE_BUILD_TYPE}.") - endif () -endif () + endif() +endif() message(STATUS "Ramses Build Config: SystemName ${CMAKE_SYSTEM_NAME}, SystemVersion ${CMAKE_SYSTEM_VERSION}, CompilerID ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}, BuildType ${CMAKE_BUILD_TYPE}, TargetBitness ${TARGET_BITNESS} c++${ramses-sdk_CPP_VERSION}") @@ -118,12 +128,19 @@ if((${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OR (${CMAKE_SYSTEM_NAME} MATCHES "iOS set(ramses-sdk_TEXT_SUPPORT OFF CACHE STRING "Enable/disable the ramses text API." FORCE) endif() +IF (NOT DEFINED ramses-sdk_ENABLE_HARFBUZZ_LEGACY_SHAPING) + IF ("${TARGET_OS}" STREQUAL "Integrity") + SET(ramses-sdk_ENABLE_HARFBUZZ_LEGACY_SHAPING ON CACHE BOOL "Enable old shaping behavior for text blocks with special characters, e.g. for compatibility reasons") + ENDIF() +ENDIF() + # check if valid configuration of window types in relation with shared libs set(ANY_WINDOW_TYPE_ENABLED OFF) if( ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS OR ramses-sdk_ENABLE_WINDOW_TYPE_X11 OR ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID + OR ramses-sdk_ENABLE_WINDOW_TYPE_IOS OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) set(ANY_WINDOW_TYPE_ENABLED ON) @@ -152,6 +169,8 @@ if(NOT ANY_WINDOW_TYPE_ENABLED) setDefaultWindowType(ramses-sdk_ENABLE_WINDOW_TYPE_X11 "X11") elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") setDefaultWindowType(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID "Android") + elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") + setDefaultWindowType(ramses-sdk_ENABLE_WINDOW_TYPE_IOS "iOS") else() message(WARNING "Incorrect configuration. No default window type is known for the build operating system '${CMAKE_SYSTEM_NAME}':\n" "* Either disable default window type by setting ramses-sdk_ENABLE_DEFAULT_WINDOW_TYPE=OFF\n" @@ -193,9 +212,6 @@ if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) endif() set(CMAKE_DEBUG_POSTFIX "") # no debug suffix in this project -set(ramses-shared-lib-MIXIN CACHE INTERNAL "") -set(ramses-shared-lib-renderer-MIXIN CACHE INTERNAL "") - include(cmake/ramses/platformTargets.cmake) include(cmake/ramses/makeTestFromTarget.cmake) include(cmake/ramses/resourceCopy.cmake) @@ -219,57 +235,37 @@ if (ramses-sdk_ALLOW_PLATFORM_GLM) add_library(glm::glm ALIAS glm) endif() endif() +if (ramses-sdk_USE_PLATFORM_LUAJIT) + if (TARGET lua::lua) + message(WARNING "ramses-sdk_USE_PLATFORM_LUAJIT is ON, but `lua::lua` target is already defined. Existing `lua::lua` target will be used.") + endif() + find_package(LuaJIT REQUIRED) +endif() createBuildConfig() -addSubdirectory(MODE AUTO PATH external) -addSubdirectory(MODE ON PATH framework) - -addSubdirectory(MODE ON PATH client) - -if(ANY_WINDOW_TYPE_ENABLED) - addSubdirectory(MODE ON PATH renderer) -endif() +add_subdirectory(external) -addSubdirectory(MODE AUTO PATH ramses-shared-lib) -addSubdirectory(MODE AUTO PATH ramses-cli) +add_subdirectory(include) +add_subdirectory(src) if(ramses-sdk_BUILD_TOOLS) - addSubdirectory(MODE AUTO PATH utils) -endif() - -if(ramses-sdk_BUILD_DAEMON) - addSubdirectory(MODE ON PATH ramses-daemon) + add_subdirectory(tools) endif() -if(ramses-sdk_BUILD_TOOLS) - addSubdirectory(MODE AUTO PATH renderer/ramses-renderer-main) -endif() -if(ramses-sdk_BUILD_EXAMPLES) - addSubdirectory(MODE AUTO PATH examples) -endif() -if(ramses-sdk_BUILD_TESTS) - addSubdirectory(MODE AUTO PATH integration) -endif() -if(ramses-sdk_BUILD_DEMOS) - addSubdirectory(MODE AUTO PATH demo) +if(ramses-sdk_BUILD_FULL_SHARED_LIB OR ramses-sdk_BUILD_HEADLESS_SHARED_LIB) + if(ramses-sdk_BUILD_EXAMPLES) + add_subdirectory(examples) + endif() endif() - -if(RAMSES_TOPLEVEL AND ramses-sdk_BUILD_TOOLS AND ramses-sdk_ENABLE_LOGIC) - addSubdirectory(MODE AUTO PATH client/logic/tools) - # TODO Fix the benchmarks - they need to link statically, ramses doesn't allow that currently - # OR rework the benchmarks to only benchmark public API - #addSubdirectory(MODE ON PATH benchmarks) +if(ramses-sdk_BUILD_FULL_SHARED_LIB) + if(ramses-sdk_BUILD_DEMOS) + add_subdirectory(demo) + endif() endif() -# Only enable testAssetProducer if built as top-level project -if(RAMSES_TOPLEVEL AND ramses-sdk_BUILD_TESTS AND ramses-sdk_ENABLE_LOGIC) - addSubdirectory(MODE AUTO PATH client/logic/unittests/testAssetProducer) - if(TARGET testAssetProducer) - add_custom_target(RL_REGEN_TEST_ASSETS - COMMAND testAssetProducer ${PROJECT_SOURCE_DIR}/client/logic/unittests/res) - set_property(TARGET RL_REGEN_TEST_ASSETS PROPERTY FOLDER "CMakePredefinedTargets") - endif() +if(ramses-sdk_BUILD_TESTS) + add_subdirectory(tests) endif() include(cmake/ramses/createPackage.cmake) diff --git a/LICENSE.txt b/LICENSE.txt index a612ad981..d3fe6c091 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -371,3 +371,1409 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene "cityhash" Bibliothek +ist nach der "MIT Lizenz" lizenziert. +Der Source Code dieser Software, steht auf +https://github.com/google/cityhash zum Download zur Verfügung. + +The "cityhash" library included in some products is +licensed under the "MIT License". +To obtain a copy of the source code for this component, +visit https://github.com/google/cityhash + +Copyright (c) 2011 Google, Inc. + +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. + +------------------------------------------------------------------------ + +Der in einigen Produkten enthaltene "OpenGL Header" +ist nach der "Khronos Group-License" lizenziert. Den +Lizenztext in der englischen Original-Fassung +inklusive Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "OpenGL Header" included in some products is +licensed under the "Khronos Group- License". A copy of +that license in the English original version with a +copyright notice, a disclaimer of warranty, and an +exclusion of liability is included in this document +below. + +Copyright (c) 2013 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are 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 Materials. + +THE MATERIALS ARE 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 +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"lodepng" ist nach der "zlib License" lizenziert. +Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "lodepng" software included in some +products is licensed under the "zlib License". +A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + +LodePNG version 20180114 + +Copyright (c) 2005-2018 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + +------------------------------------------------------------------------ + +Der in einigen Produkten enthaltene "Wayland IVI Extension" +ist nach der "MIT/X11-Lizenz" lizenziert. Den +Lizenztext in der englischen Original-Fassung +inklusive Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "Wayland IVI Extension" included in some products is +licensed under the "MIT/X11 license". A copy of that +license in the English original version with a +copyright notice, a disclaimer of warranty, and an +exclusion of liability is included in this document below. + +Copyright (C) 2013 DENSO CORPORATION +Copyright (c) 2013 BMW Car IT GmbH + +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. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"Roboto Font" ist nach der "Apache Lizenz, +Version 2.0" lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "Roboto Font" software included in some +products is licensed under the "Apache License, +Version 2.0". A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"M+ Font" ist nach der "Public Domain" +lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "M+ Font" software included in some +products is licensed under the "Public Domain". +A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + +M+ FONTS Copyright (C) 2002-2017 M+ FONTS PROJECT + +- + +LICENSE_E + + + + +These fonts are free software. +Unlimited permission is granted to use, copy, and distribute them, with +or without modification, either commercially or noncommercially. +THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY. + + +http://mplus-fonts.osdn.jp + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"WenQuanYi MicroHei" ist nach der "Apache Lizenz, +Version 2.0" lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "WenQuanYi MicroHei" software included in some +products is licensed under the "Apache License, +Version 2.0". A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this +document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, +documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not +limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which +the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. +For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to +that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including +but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are +managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is +conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor +and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a +perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works +of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with +the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim +or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or +contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the +date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and + + 2. You must cause any modified files to carry prominent notices stating that You changed the files; and + + 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative +Works; and + + 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to +any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display +generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for +informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You +distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot +be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the +Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the +Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of +the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume +any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as +a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment +syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party archives. +Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless +required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing +permissions and limitations under the License. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"Arimo Font" ist nach der "Apache Lizenz, +Version 2.0" lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "Arimo Font" software included in some +products is licensed under the "Apache License, +Version 2.0". A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"Droid Kufi Font" ist nach der "Apache Lizenz, +Version 2.0" lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "Droid Kufi Font" software included in some +products is licensed under the "Apache License, +Version 2.0". A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"Droid Naskh Font" ist nach der "Apache Lizenz, +Version 2.0" lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "Droid Naskh Font" software included in some +products is licensed under the "Apache License, +Version 2.0". A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------------ + +Die in einigen Produkten enthaltene Software +"Satisfy Font" ist nach der "Apache Lizenz, +Version 2.0" lizenziert. Eine Kopie dieser Lizenz +in der englischen Original-Fassung inklusive +Copyright-Hinweis, Gewährleistungs- und +Haftungsausschlüssen finden Sie nachfolgend. + +The "Satisfy Font" software included in some +products is licensed under the "Apache License, +Version 2.0". A copy of that license in the +English original version with a copyright notice, +a disclaimer of warranty, and an exclusion of +liability is included in this document below. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index d738e479b..588b5b7ae 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -## Table of Contents +# Table of Contents * [What is RAMSES](#what-is-ramses) * [Obtaining the source code](#obtaining-the-source-code) * [Building and testing](#building-and-testing) * [License](#license) -## What is RAMSES +# What is RAMSES RAMSES is 3D rendering engine with focus on bandwidth and resource efficiency. For a broader overview and introduction to the ecosystem and tools, visit our @@ -14,14 +14,14 @@ For a broader overview and introduction to the ecosystem and tools, visit our Have a look at our showcase video: [![RAMSES Distributed Rendering Engine Showcase Video](https://img.youtube.com/vi/tyzvEI25BMg/0.jpg)](https://www.youtube.com/watch?v=tyzvEI25BMg) -## Obtaining the source code +# Obtaining the source code RAMSES can be cloned from its BMW Car IT repository using git: ``` git clone --recurse-submodules https://github.com/bmwcarit/ramses ``` -## Building and testing +# Build instructions General building tips: RAMSES's build system is based on CMake. It has mandatory components and optional components which are built only if required dependencies and/or CMake flags are present. The CMake log will @@ -83,7 +83,7 @@ You can also check the docker container setup scripts for a reference how to bui /scripts/docker/runtime-files/build-ramses.sh -> contains CMake command for building ``` -## License +# License RAMSES original code is copyright BMW Car IT or BMW AG ```Copyright (C) 20xx BMW Car IT GmbH``` or ```Copyright (C) 20xx BMW AG``` @@ -108,6 +108,7 @@ Submodule reference: - GLSLang (Licensed under BSD-3 and Khronos Group License) - OpenGL Mathematics (Licensed under MIT License) - Googletest (Licensed under BSD-3) +- Google Benchmark (Licensed under Apache-2.0) - Harfbuzz (Licensed under MIT and ISC; see external/harfbuzz/COPYING) - Asio (Boost Software License - Version 1.0) - LZ4 (Licensed under BSD-2; see also external/lz4/LICENSE for more details) @@ -115,6 +116,10 @@ Submodule reference: - ImGui (Licensed under MIT License) - Abseil (Licensed under Apache 2.0; see also external/abseil/LICENSE for more details) - CLI11 (Licensed under BSD-3) +- Lua (Licensed under MIT) +- Sol (Licensed under MIT) +- Flatbuffers (Licensed under Apache-2.0) +- Webkit ANGLE (Licensed under BSD-3) Included Assets: - Roboto Font (Licensed under Apache 2.0) @@ -124,3 +129,4 @@ Included Assets: - Droid Kufi Font (Licensed under Apache 2.0) - Droid Naskh Font (Licensed under Apache 2.0) - Satisfy Font (Licensed under Apache 2.0) +- Noto Sans Thai Font (Licensed under SIL Open Font License 1.1) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt deleted file mode 100644 index a12dc1023..000000000 --- a/benchmarks/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2020 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -file(GLOB src *.cpp) - -add_executable(benchmarks - ${src} -) - -target_link_libraries(benchmarks - PRIVATE - rlogic::ramses-logic-static - ramses::google-benchmark-main - fmt::fmt - sol2::sol2 - lua::lua -) - -target_include_directories(benchmarks - PRIVATE - $ - $ -) - -folderizeTarget(benchmarks) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt deleted file mode 100644 index ebd0bf451..000000000 --- a/client/CMakeLists.txt +++ /dev/null @@ -1,215 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -file(GLOB - RAMSES_CLIENT_FILES_SOURCE - ramses-client/impl/*.cpp - ramses-client/impl/glslEffectBlock/*.cpp - ramses-client/impl/ClientCommands/*.cpp - ramses-client-api/*.cpp) - -file(GLOB - RAMSES_CLIENT_API_INCLUDE_BASE - ramses-client-api/include) - -add_library(ramses-client-api INTERFACE) - -if(ramses-sdk_TEXT_SUPPORT) - set(client_text_dependencies - freetype - harfbuzz) - set(client_text_includes - ramses-client/impl/ramses-text/*.h - ramses-text-api/include/*.h - ramses-text-api/include/ramses-text-api/*.h - ) - - file(GLOB - RAMSES_CLIENT_TEXT_FILES_SOURCE - ramses-client/impl/ramses-text/*.cpp - ramses-text-api/*.cpp) - file(GLOB RAMSES_CLIENT_TEXT_API_INCLUDE_BASE - ramses-text-api/include) - file(GLOB - RAMSES_CLIENT_TEXT_TEST_FILES_SOURCE - test/text/*.cpp) - file(GLOB - RAMSES_CLIENT_TEXT_TEST_FILES_INCLUDES - test/text/*.h) - - target_compile_definitions(ramses-client-api INTERFACE RAMSES_TEXT_ENABLED) -else() - set(client_text_dependencies "") - set(client_text_includes "") - - set(RAMSES_CLIENT_TEXT_FILES_SOURCE "") - set(RAMSES_CLIENT_TEXT_API_INCLUDE_BASE "") - set(RAMSES_CLIENT_TEXT_TEST_FILES_SOURCE "") - set(RAMSES_CLIENT_TEXT_TEST_FILES_INCLUDES "") -endif() - - -# Logic hacky integration TODO fix and improve - -if(NOT ramses-sdk_ENABLE_LOGIC) - set(ramses_logic_api_include_base "") - set(ramses_logic_internal_includes "") - set(ramses_logic_src_files "") - set(ramses_logic_deps "") -else() - if(ramses-sdk_ENABLE_FLATBUFFERS_GENERATION) - include(${CMAKE_CURRENT_SOURCE_DIR}/logic/cmake/flatbuffersGeneration.cmake) - endif() - - file(GLOB - ramses_logic_api_include_base - logic/include) - - set(ramses_logic_internal_includes - logic/include - logic/lib - logic/lib/flatbuffers - ) - - file(GLOB logic_public_headers logic/include/ramses-logic/*.h) - file(GLOB logic_impl_headers logic/lib/impl/*.h) - file(GLOB logic_internal_headers logic/lib/internals/*.h) - file(GLOB logic_flatbuf_gen_headers logic/lib/flatbuffers/generated/*.h) - file(GLOB logic_flatbuf_schemas logic/lib/flatbuffers/schemas/*.fbs) - - file(GLOB logic_impl_src logic/lib/impl/*.cpp) - file(GLOB logic_internals_src logic/lib/internals/*.cpp) - - # Attach sol headers to ramses-logic-obj to be able to navigate/debug with them - # Workaround for MSVC (ignores interface targets otherwise if not attached to a non-interface target) - file(GLOB logic_sol_headers - ${PROJECT_SOURCE_DIR}/external/sol/include/sol/*.hpp - ${PROJECT_SOURCE_DIR}/external/sol/include/sol/compatibility/* - ) - - set(ramses_logic_src_files - ${logic_public_headers} - ${logic_impl_headers} - ${logic_internal_headers} - ${logic_flatbuf_gen_headers} - ${logic_flatbuf_schemas} - ${logic_impl_src} - ${logic_internals_src} - ) - - # This is the only robust way to add files to a VS project with CMake - # without having them treated as source files - set_source_files_properties(${logic_flatbuf_gen_headers} PROPERTIES HEADER_FILE_ONLY TRUE) - set_source_files_properties(${logic_flatbuf_schemas} PROPERTIES HEADER_FILE_ONLY TRUE) - set_source_files_properties(${logic_sol_headers} PROPERTIES HEADER_FILE_ONLY TRUE) - - set(ramses_logic_deps - sol2::sol2 - lua::lua - ramses::flatbuffers - fmt::fmt - ) - - - file(GLOB logic_tests_src - logic/unittests/api/*.cpp - logic/unittests/api/*.h - logic/unittests/internal/*.cpp - logic/unittests/internal/*.h - logic/unittests/shared/*.cpp - logic/unittests/shared/*.h - ) - - set(logic_tests_include_dirs - logic/lib/flatbuffers - logic/lib - logic/unittests/shared - ) - - set(logic_tests_res - logic/unittests/res - ) - - # TODO fix this, doesn't work with the ramses build system... - #if(TARGET FlatbufGen) - # set(ramses_logic_deps ${ramses_logic_deps} FlatbufGen) - #endif() -endif() - - -target_include_directories(ramses-client-api INTERFACE ${RAMSES_CLIENT_API_INCLUDE_BASE} ${RAMSES_CLIENT_TEXT_API_INCLUDE_BASE} ${ramses_logic_api_include_base}) - -createModule( - NAME ramses-client - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS ramses-client/impl - ${ramses_logic_internal_includes} - SRC_FILES ramses-client/impl/*.h - ramses-client/impl/glslEffectBlock/*.h - ramses-client/impl/ClientCommands/*.h - ramses-client-api/include/*.h - ramses-client-api/include/ramses-client-api/*.h - ${RAMSES_CLIENT_FILES_SOURCE} - ${RAMSES_CLIENT_TEXT_FILES_SOURCE} - ${ramses_logic_src_files} - ${client_text_includes} - DEPENDENCIES ramses-client-api - ramses-framework - ramses-glslang - ${client_text_dependencies} - ${ramses_logic_deps} -) - -if(ramses-sdk_ENABLE_INSTALL) - install(DIRECTORY ramses-client-api/include/ DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}" COMPONENT ramses-sdk-devel) - if(ramses-sdk_TEXT_SUPPORT) - install(DIRECTORY ramses-text-api/include/ DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}" COMPONENT ramses-sdk-devel) - endif() - if(ramses-sdk_ENABLE_LOGIC) - install(DIRECTORY logic/include/ DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}" COMPONENT ramses-sdk-devel) - endif() -endif() - -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - target_compile_options(ramses-client PRIVATE "-Wsuggest-override") -endif() - - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME ramses-client-test - TYPE BINARY - INCLUDE_PATHS test - ${logic_tests_include_dirs} - SRC_FILES test/*.h - test/ClientCommands/*.h - ${RAMSES_CLIENT_TEXT_TEST_FILES_INCLUDES} - test/*.cpp - ${RAMSES_CLIENT_TEXT_TEST_FILES_SOURCE} - test/ClientCommands/*.cpp - ${logic_tests_src} - RESOURCE_FOLDERS test/res - ${logic_tests_res} - DEPENDENCIES ramses-client - FrameworkTestUtils - ramses-gmock-main - ) - - makeTestFromTarget( - TARGET ramses-client-test - SUFFIX UNITTEST) - -endif() - -set(ramses-shared-lib-MIXIN - ${ramses-shared-lib-MIXIN} - INCLUDE_PATHS ${RAMSES_CLIENT_API_INCLUDE_BASE} ${RAMSES_CLIENT_TEXT_API_INCLUDE_BASE} ${ramses_logic_api_include_base} - SRC_FILES ${RAMSES_CLIENT_FILES_SOURCE} ${RAMSES_CLIENT_TEXT_FILES_SOURCE} ${ramses_logic_src_files} - DEPENDENCIES ramses-client - CACHE INTERNAL "") diff --git a/client/logic/.clang-tidy b/client/logic/.clang-tidy deleted file mode 100644 index c8130c25f..000000000 --- a/client/logic/.clang-tidy +++ /dev/null @@ -1,506 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2020 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - ---- -Checks: - '-*, - - boost-use-to-string, - - # We are not using abseil - #abseil-*, - - # Android specific checks - #android-*, - - # Coding style, projects can optionally enable this. - #bugprone-argument-comment, - bugprone-assert-side-effect, - bugprone-bad-signal-to-kill-thread, - bugprone-bool-pointer-implicit-conversion, - # Incompatible with C++ attribute fallthrough in switch/case statements. - #bugprone-branch-clone, - bugprone-copy-constructor-init, - bugprone-dangling-handle, - bugprone-dynamic-static-initializers, - bugprone-exception-escape, - bugprone-fold-init-type, - bugprone-forward-declaration-namespace, - bugprone-forwarding-reference-overload, - bugprone-inaccurate-erase, - bugprone-incorrect-roundings, - bugprone-infinite-loop, - bugprone-integer-division, - bugprone-lambda-function-name, - bugprone-macro-parentheses, - bugprone-macro-repeated-side-effects, - bugprone-misplaced-operator-in-strlen-in-alloc, - bugprone-misplaced-widening-cast, - bugprone-move-forwarding-reference, - bugprone-multiple-statement-macro, - # Alias: cppcoreguidelines-narrowing-conversions - #bugprone-narrowing-conversions, - bugprone-not-null-terminated-result, - bugprone-parent-virtual-call, - bugprone-posix-return, - bugprone-signed-char-misuse, - bugprone-sizeof-container, - bugprone-sizeof-expression, - bugprone-string-constructor, - bugprone-string-integer-assignment, - bugprone-string-literal-with-embedded-nul, - bugprone-suspicious-enum-usage, - bugprone-suspicious-memset-usage, - bugprone-suspicious-missing-comma, - bugprone-suspicious-semicolon, - bugprone-suspicious-string-compare, - bugprone-swapped-arguments, - bugprone-terminating-continue, - bugprone-throw-keyword-missing, - bugprone-too-small-loop-variable, - bugprone-undefined-memory-manipulation, - bugprone-undelegated-constructor, - bugprone-unhandled-self-assignment, - bugprone-unused-raii, - bugprone-unused-return-value, - bugprone-use-after-move, - bugprone-virtual-near-miss, - - # Alias: misc-static-assert - #cert-dcl03-c, - cert-dcl16-c, - cert-dcl21-cpp, - cert-dcl50-cpp, - # Alias: misc-new-delete-overloads - #cert-dcl54-cpp, - cert-dcl58-cpp, - cert-dcl59-cpp, - cert-env33-c, - # Alias: misc-throw-by-value-catch-by-reference - #cert-err09-cpp, - cert-err34-c, - cert-err52-cpp, - # This prevents defining any global const that is not a POD (std::string/vectors/...) - #cert-err58-cpp, - cert-err60-cpp, - # Alias: misc-throw-by-value-catch-by-reference - #cert-err61-cpp, - # Alias: misc-non-copyable-objects - #cert-fio38-c, - #cert-flp30-c, - cert-mem57-cpp, - # Alias: cert-msc50-cpp - #cert-msc30-c, - # Alias: cert-msc51-cpp - #cert-msc32-c, - cert-msc50-cpp, - cert-msc51-cpp, - # Alias: performance-move-constructor-init - #cert-oop11-cpp, - # Alias: bugprone-unhandled-self-assignment - #cert-oop54-cpp, - cert-oop58-cpp, - # Alias: bugprone-bad-signal-to-kill-thread - #cert-pos44-c - - # No documentation available - #clang-analyzer-apiModeling.google.GTest, - # No documentation available - #clang-analyzer-apiModeling.llvm.CastValue, - # No documentation available - #clang-analyzer-apiModeling.llvm.ReturnValue, - clang-analyzer-apiModeling.StdCLibraryFunctions, - # No documentation available - #clang-analyzer-apiModeling.TrustNonnull, - clang-analyzer-core.CallAndMessage, - clang-analyzer-core.DivideZero, - clang-analyzer-core.DynamicTypePropagation, - clang-analyzer-core.NonNullParamChecker, - clang-analyzer-core.NonnilStringConstants, - clang-analyzer-core.NullDereference, - clang-analyzer-core.StackAddrEscapeBase, - clang-analyzer-core.StackAddressEscape, - clang-analyzer-core.UndefinedBinaryOperatorResult, - clang-analyzer-core.VLASize, - clang-analyzer-core.builtin.BuiltinFunctions, - clang-analyzer-core.builtin.NoReturnFunctions, - clang-analyzer-core.uninitialized.ArraySubscript, - clang-analyzer-core.uninitialized.Assign, - clang-analyzer-core.uninitialized.Branch, - clang-analyzer-core.uninitialized.CapturedBlockVariable, - clang-analyzer-core.uninitialized.UndefReturn, - clang-analyzer-cplusplus.InnerPointer, - clang-analyzer-cplusplus.Move, - clang-analyzer-cplusplus.NewDelete, - clang-analyzer-cplusplus.NewDeleteLeaks, - clang-analyzer-cplusplus.PureVirtualCall, - clang-analyzer-cplusplus.SelfAssignment, - clang-analyzer-cplusplus.SmartPtr, - clang-analyzer-cplusplus.VirtualCallModeling, - clang-analyzer-deadcode.DeadStores, - # We are not using Fuchsia - #clang-analyzer-fuchsia.HandleChecker, - # ObjectiveC only - #clang-analyzer-nullability.*, - clang-analyzer-optin.cplusplus.UninitializedObject, - clang-analyzer-optin.cplusplus.VirtualCall, - # We are not using the MPI library - #clang-analyzer-optin.mpi.*, - # We are not developing for osx - #clang-analyzer-optin.osx.*, - # We are not using GCD - #clang-analyzer-optin.performance.GCDAntipattern, - clang-analyzer-optin.performance.Padding, - clang-analyzer-optin.portability.UnixAPI, - # We are not using std::experimental::simd - #clang-analyzer-optin.portability-simd-intrinsics, - # We are not developing for osx - #clang-analyzer-osx.*, - clang-analyzer-security.FloatLoopCounter, - clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, - clang-analyzer-security.insecureAPI.SecuritySyntaxChecker, - clang-analyzer-security.insecureAPI.UncheckedReturn, - clang-analyzer-security.insecureAPI.bcmp, - clang-analyzer-security.insecureAPI.bcopy, - clang-analyzer-security.insecureAPI.bzero, - # We are not using ObjectiveC - #clang-analyzer-security.insecureAPI.decodeValueOfObjCType, - clang-analyzer-security.insecureAPI.getpw, - clang-analyzer-security.insecureAPI.gets, - clang-analyzer-security.insecureAPI.mkstemp, - clang-analyzer-security.insecureAPI.mktemp, - clang-analyzer-security.insecureAPI.rand, - clang-analyzer-security.insecureAPI.strcpy, - clang-analyzer-security.insecureAPI.vfork, - clang-analyzer-unix.API, - clang-analyzer-unix.DynamicMemoryModeling, - clang-analyzer-unix.Malloc, - clang-analyzer-unix.MallocSizeof, - clang-analyzer-unix.MismatchedDeallocator, - clang-analyzer-unix.Vfork, - clang-analyzer-unix.cstring.BadSizeArg, - clang-analyzer-unix.cstring.CStringModeling, - clang-analyzer-unix.cstring.NullArg, - clang-analyzer-valist.CopyToSelf, - clang-analyzer-valist.Uninitialized, - clang-analyzer-valist.Unterminated, - clang-analyzer-valist.ValistBase, - - # Alias: modernize-avoid-c-arrays - #cppcoreguidelines-avoid-c-arrays, - cppcoreguidelines-avoid-goto, - # Alias: readability-magic-numbers - #cppcoreguidelines-avoid-magic-numbers, - cppcoreguidelines-c-copy-assignment-signature, - # Alias: modernize-use-override - #cppcoreguidelines-explicit-virtual-functions, - cppcoreguidelines-init-variables, - cppcoreguidelines-interfaces-global-init, - # This check does not work well with exporting API symbols - #cppcoreguidelines-macro-usage, - cppcoreguidelines-narrowing-conversions, - cppcoreguidelines-no-malloc, - # Alias: misc-non-private-member-variables-in-classes - #cppcoreguidelines-non-private-member-variables-in-classes, - # We are not using GSL - #cppcoreguidelines-owning-memory, - # Many false positives (for example in MGU NaRE::logInfo) - #cppcoreguidelines-pro-bounds-array-to-pointer-decay, - # Many false positives - #cppcoreguidelines-pro-bounds-constant-array-index, - cppcoreguidelines-pro-bounds-pointer-arithmetic, - cppcoreguidelines-pro-type-const-cast, - cppcoreguidelines-pro-type-cstyle-cast, - cppcoreguidelines-pro-type-member-init, - cppcoreguidelines-pro-type-reinterpret-cast, - cppcoreguidelines-pro-type-static-cast-downcast, - cppcoreguidelines-pro-type-union-access, - cppcoreguidelines-pro-type-vararg, - cppcoreguidelines-slicing, - # TODO Violin check if we can workaround these issues with our clang-tidy wrapper - # Google test templated tests TEST_P macros break this rule - #cppcoreguidelines-special-member-functions, - # Google test does not comply to this rule - #cppcoreguidelines-avoid-non-const-global-variables, - - # We are not using Darwin - #darwin-*, - - # We are not using Fuchsia - #fuchsia-*, - - google-build-explicit-make-pair, - # Alias: cert-dcl59-cpp - #google-build-namespaces, - # Coding style, projects can optionally enable this. - #google-build-using-namespace, - google-default-arguments, - google-explicit-constructor, - google-global-names-in-headers, - # We are not using ObjectiveC - #google-objc-*, - # Coding style, projects can optionally enable this. - #google-readability-avoid-underscore-in-googletest-name, - # Alias: readability-braces-around-statements - #google-readability-braces-around-statements, - google-readability-casting, - # Coding style, projects can optionally enable this. - #google-readability-function-size, - # Alias: llvm-namespace-comment - #google-readability-namespace-comments, - # Coding style, projects can optionally enable this. - #google-readability-todo, - google-runtime-int, - google-runtime-operator, - # Coding style, projects can optionally enable this. - #google-runtime-references, - # Coding style, projects can optionally enable this. - google-upgrade-googletest-case, - - # Alias: modernize-avoid-c-arrays - #hicpp-avoid-c-arrays, - hicpp-avoid-goto, - # Alias: readability-braces-around-statements - #hicpp-braces-around-statements, - # Alias: modernize-deprecated-headers - #hicpp-deprecated-headers, - # Coding style, projects can optionally enable this. - #hicpp-exception-baseclass, - # Alias: google-explicit-constructor - #hicpp-explicit-conversions, - # Alias: readability-function-size - #hicpp-function-size, - # Alias: bugprone-use-after-move - #hicpp-invalid-access-moved, - # Alias: cppcoreguidelines-pro-type-member-init - #hicpp-member-init, - hicpp-move-const-arg, - hicpp-multiway-paths-covered, - hicpp-named-parameter, - # Alias: misc-new-delete-overloads - #hicpp-new-delete-operators, - # Alias: cppcoreguidelines-pro-bounds-array-to-pointer-decay - #hicpp-no-array-decay, - hicpp-no-assembler, - hicpp-noexcept-move, - # Alias: cppcoreguidelines-no-malloc - #hicpp-no-malloc, - hicpp-signed-bitwise, - # Alias: cppcoreguidelines-special-member-functions - #hicpp-special-member-functions, - # Alias: misc-static-assert - #hicpp-static-assert, - # Alias: bugprone-undelegated-constructor - #hicpp-undelegated-constructor, - # Alias: readability-uppercase-literal-suffix - #hicpp-uppercase-literal-suffix, - # Alias: modernize-use-auto - #hicpp-use-auto, - # Alias: modernize-use-emplace - #hicpp-use-emplace, - # Alias: modernize-use-equals-default - #hicpp-use-equals-default, - # Alias: modernize-use-equals-delete - #hicpp-use-equals-delete, - # Alias: modernize-use-noexcept - #hicpp-use-noexcept, - # Alias: modernize-use-nullptr - #hicpp-use-nullptr, - # Alias: modernize-use-override - #hicpp-use-override, - # Alias: cppcoreguidelines-pro-type-vararg - #hicpp-vararg, - - # We are not working on the Linux kernel - #linuxkernel-*, - - # Not relevant for us, too llvm specific - #llvm-header-guard, - #llvm-include-order, - #llvm-namespace-comment, - #llvm-prefer-isa-or-dyn-cast-in-conditionals, - - # We are not using the LLVM::Register class - #llvm-prefer-register-over-unsigned, - # Alias: readability-qualified-auto - #llvm-qualified-auto, - # We are not using the LLVM::Twine class - #llvm-twine-local, - - misc-definitions-in-headers, - misc-misplaced-const, - misc-new-delete-overloads, - misc-non-copyable-objects, - # Coding style, projects can optionally enable this. - #misc-non-private-member-variables-in-classes, - misc-redundant-expression, - misc-static-assert, - misc-throw-by-value-catch-by-reference, - misc-unconventional-assign-operator, - misc-uniqueptr-reset-release, - misc-unused-alias-decls, - misc-unused-parameters, - misc-unused-using-decls, - - # Coding style, projects can optionally enable this. - #modernize-avoid-bind, - modernize-avoid-c-arrays, - # MGU code that uses Qt MOC cannot use nested namespaces. - #modernize-concat-nested-namespaces, - modernize-deprecated-ios-base-aliases, - modernize-deprecated-headers, - modernize-loop-convert, - modernize-make-shared, - modernize-make-unique, - modernize-pass-by-value, - modernize-raw-string-literal, - modernize-redundant-void-arg, - modernize-replace-auto-ptr, - modernize-replace-random-shuffle, - modernize-return-braced-init-list, - modernize-shrink-to-fit, - modernize-unary-static-assert, - modernize-use-auto, - modernize-use-bool-literals, - modernize-use-default-member-init, - modernize-use-emplace, - modernize-use-equals-default, - modernize-use-equals-delete, - modernize-use-nodiscard, - modernize-use-noexcept, - modernize-use-nullptr, - modernize-use-override, - # Coding style, projects can optionally enable this. - #modernize-use-trailing-return-type, - # Coding style, projects can optionally enable this. - #modernize-use-transparent-functors, - modernize-use-uncaught-exceptions, - modernize-use-using, - - # We are not using MPI - #mpi-*, - - # We are not using ObjectiveC - #objc-*, - - # We are not using OpenMP - #openmp-*, - - performance-faster-string-find, - performance-for-range-copy, - performance-implicit-conversion-in-loop, - performance-inefficient-algorithm, - performance-inefficient-string-concatenation, - performance-inefficient-vector-operation, - performance-move-const-arg, - performance-move-constructor-init, - performance-no-automatic-move, - performance-noexcept-move-constructor, - performance-trivially-destructible, - performance-type-promotion-in-math-fn, - performance-unnecessary-copy-initialization, - performance-unnecessary-value-param, - - # We are not using std::experimental::simd - #portability-simd-*, - - # Coding style, projects can optionally enable this. - #readability-avoid-const-params-in-decls, - readability-braces-around-statements, - readability-const-return-type, - readability-container-size-empty, - readability-convert-member-functions-to-static, - readability-delete-null-pointer, - readability-deleted-default, - readability-else-after-return, - # Coding style, projects can optionally enable this. - #readability-function-size, - # Coding style, projects can optionally enable this. - #readability-identifier-naming, - readability-implicit-bool-conversion, - readability-inconsistent-declaration-parameter-name, - readability-isolate-declaration, - # Coding style, projects can optionally enable this. - #readability-magic-numbers, - # Disabled because not working properly with public pimpl members - #readability-make-member-function-const, - readability-misleading-indentation, - readability-misplaced-array-index, - readability-named-parameter, - readability-non-const-parameter, - # Coding style, projects can optionally enable this. - #readability-qualified-auto, - # Coding style, projects can optionally enable this. - #readability-redundant-access-specifiers, - readability-redundant-control-flow, - readability-redundant-declaration, - readability-redundant-function-ptr-dereference, - readability-redundant-member-init, - readability-redundant-preprocessor, - readability-redundant-smartptr-get, - readability-redundant-string-cstr, - readability-redundant-string-init, - # Coding style, projects can optionally enable this. - #readability-simplify-boolean-expr, - readability-simplify-subscript-expr, - readability-static-accessed-through-instance, - readability-static-definition-in-anonymous-namespace, - readability-string-compare, - readability-uniqueptr-delete-release, - # Coding style, projects can optionally enable this. - #readability-uppercase-literal-suffix - - # We are not using the Zircon kernel - #zircon-temporary-objects -' - -# everything that is enabled is also an error -WarningsAsErrors: '*' - -HeaderFilterRegex: '.*' - -AnalyzeTemporaryDtors: false -FormatStyle: File - -CheckOptions: - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/bugprone-assert-side-effect.html#cmdoption-arg-assertmacros - - key: bugprone-assert-side-effect.AssertMacros - value: 'Q_ASSERT,Q_ASSERT_X,Q_CHECK_PTR' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/bugprone-assert-side-effect.html#cmdoption-arg-checkfunctioncalls - - key: bugprone-assert-side-effect.CheckFunctionCalls - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/bugprone-sizeof-expression.html#cmdoption-arg-warnonsizeofintegerexpression - - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/bugprone-suspicious-string-compare.html#cmdoption-arg-warnonlogicalnotcomparison - - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/misc-throw-by-value-catch-by-reference.html#cmdoption-arg-warnonlargeobject - - key: misc-throw-by-value-catch-by-reference.WarnOnLargeObject - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.html#cmdoption-arg-pedanticmode - - key: cppcoreguidelines-narrowing-conversions.PedanticMode - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.html#cmdoption-arg-allowsoledefaultdtor - - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.html#cmdoption-arg-allowsoledefaultdtor - - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor - value: '1' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/hicpp-multiway-paths-covered.html#cmdoption-arg-warnonmissingelse - - key: hicpp-multiway-paths-covered.WarnOnMissingElse - value: '0' - # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/readability-implicit-bool-conversion.html#cmdoption-arg-allowpointerconditions - - key: readability-implicit-bool-conversion.AllowPointerConditions - value: '1' - # https://clang.llvm.org/extra/clang-tidy/checks/readability-braces-around-statements.html - - key: readability-braces-around-statements.ShortStatementLines - value: '2' - - key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals - value: true -... diff --git a/client/logic/cmake/platformConfig.cmake b/client/logic/cmake/platformConfig.cmake deleted file mode 100644 index b3d1a47ce..000000000 --- a/client/logic/cmake/platformConfig.cmake +++ /dev/null @@ -1,209 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2020 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -# let cmake know c++ version -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - - -# set additional flags dependent on used compiler and version - -# TODO Violin/Tobias which of this stuff can be modernized? - -# helper function to add flags -FUNCTION(ADD_FLAGS VAR) - SET(TMP "${${VAR}}") - FOREACH(flags ${ARGN}) - SET(TMP "${TMP} ${flags}") - ENDFOREACH() - SET(${VAR} ${TMP} PARENT_SCOPE) -ENDFUNCTION() - -FUNCTION(REMOVE_FROM_FLAGS flags toRemoveList outVar) - string(REGEX REPLACE " +" ";" flags_LIST "${flags}") # to list - list(REMOVE_ITEM flags_LIST ${toRemoveList}) # filter list - string(REPLACE ";" " " flags_filtered "${flags_LIST}") # to string - set(${outVar} "${flags_filtered}" PARENT_SCOPE) -ENDFUNCTION() - -# variables to fill -SET(RLOGIC_C_CXX_FLAGS) -SET(RLOGIC_C_FLAGS) -SET(RLOGIC_CXX_FLAGS) -SET(RLOGIC_DEBUG_FLAGS) -SET(RLOGIC_DEBUG_INFO_FLAGS) -SET(RLOGIC_RELEASE_FLAGS) - -# gcc OR clang (they share a lot) -IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-fPIC -pthread") - if (NOT ramses-logic_DISABLE_SYMBOL_VISIBILITY) - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-fvisibility=hidden") - endif() - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wall -Wextra -Wcast-align -Wshadow -Wformat -Wformat-security -Wvla -Wmissing-include-dirs") - ADD_FLAGS(RLOGIC_CXX_FLAGS "-std=c++17 -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast") - ADD_FLAGS(RLOGIC_C_FLAGS "-std=c11") - ADD_FLAGS(RLOGIC_DEBUG_FLAGS "-ggdb -D_DEBUG -fno-omit-frame-pointer") - ADD_FLAGS(RLOGIC_RELEASE_FLAGS "-O2 -DNDEBUG -fstack-protector-strong -D_FORTIFY_SOURCE=2") - ADD_FLAGS(RLOGIC_DEBUG_INFO_FLAGS "-ggdb -fno-omit-frame-pointer") - - if (ramses-logic_WARNINGS_AS_ERRORS) - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Werror") - endif() -ENDIF() - -# gcc specific -IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # optimize for debuggability - ADD_FLAGS(RLOGIC_DEBUG_FLAGS "-Og") - - # remap GOT readonly after resolving - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wl,-z,relro,-z,now") - - if (ramses-sdk_BUILD_WITH_LTO) - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-flto -Wodr -Wlto-type-mismatch") - endif() - - # gcc specific warnings - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wformat-signedness") - # disable too crazy optimizations causing problems - ADD_FLAGS(RLOGIC_RELEASE_FLAGS "-fno-ipa-cp-clone") - - IF(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) - # disable unfixed warnings from gcc 7 - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wno-stringop-overflow -Wno-implicit-fallthrough") - - # enable more warnings on newer gcc - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wformat-overflow -Wfree-nonheap-object") - ENDIF() - - # disable unfixed warnings from gcc 8 - IF(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wno-cast-function-type") - ENDIF() -ENDIF() - -# clang specific -IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) - message(FATAL_ERROR "Clang versions prior 10.0 are not supported") - endif() - - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-Wimplicit-fallthrough") - - # do not optimize debug build at all (-Og is wrong on clang) - ADD_FLAGS(RLOGIC_DEBUG_FLAGS "-O0") - - IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.5) - # suppress missing override keyword warning - ADD_FLAGS(RLOGIC_CXX_FLAGS "-Winconsistent-missing-override -Wmove") - ENDIF() - - # handle enable coverage - if (ramses-logic_ENABLE_TEST_COVERAGE) - message(STATUS "+ Test coverage (clang)") - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-fprofile-instr-generate") - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "-fcoverage-mapping") - endif() -ENDIF() - -IF(ramses-logic_ENABLE_TEST_COVERAGE AND NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - message(FATAL_ERROR "Can't enable test coverage for compilers different than clang!") -ENDIF() - -# flags for windows -IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - REMOVE_FROM_FLAGS("${CMAKE_CXX_FLAGS}" "/W1;/W2;/W3;/W4" CMAKE_CXX_FLAGS) - - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "/MP /DNOMINMAX") - ADD_FLAGS(RLOGIC_CXX_FLAGS "/std:c++17 /W4 /wd4503 /wd4265 /wd4201 /wd4127 /wd4996 /bigobj") - ADD_FLAGS(RLOGIC_RELEASE_FLAGS "/MD /O2 /Ob2 /DNDEBUG") - ADD_FLAGS(RLOGIC_DEBUG_FLAGS "/MDd /Zi /Od /RTC1 /D_DEBUG") - ADD_FLAGS(RLOGIC_DEBUG_INFO_FLAGS "/Zi") - - if (ramses-logic_WARNINGS_AS_ERRORS) - ADD_FLAGS(RLOGIC_C_CXX_FLAGS "/WX") - endif() - ADD_DEFINITIONS("-D_WIN32_WINNT=0x0600" "-DWINVER=0x0600") # enable 'modern' windows APIs -ENDIF() - -IF(${CMAKE_SYSTEM_NAME} MATCHES "Android") - SET(ENV{PKG_CONFIG_PATH} "") - SET(ENV{PKG_CONFIG_LIBDIR} "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig") - SET(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) -ENDIF() - -# Special handling for C++17 filesystem -# Unfortunately not trivial, please keep this CMake config in one place! - -function(check_handle_special_filesystem_lib libname) - include(CheckCXXSourceCompiles) - set(CMAKE_REQUIRED_LIBRARIES ${libname}) - check_cxx_source_compiles("int main() {}" RLOGIC_HAS_STD_FS) - - if (RLOGIC_HAS_STD_FS) - link_libraries(${libname}) - else() - if (ramses-sdk_BUILD_TESTS) - message(FATAL_ERROR "std::filesystem libary not found. Cannot use emulation with tests enabled") - endif() - if (NOT CMAKE_SYSTEM_NAME STREQUAL Linux) - message(FATAL_ERROR "std::filesystem libary not found. Can use emulation only on Linux") - endif() - - message(STATUS "std::filesystem libary not found, enable emulation") - add_definitions("-DRLOGIC_STD_FILESYSTEM_EMULATION") - endif() -endfunction() - -# GCC prior version 9.1 puts filesystem in a separate static lib (stdc++fs) and potentially in the experimental namespace -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) - check_handle_special_filesystem_lib(stdc++fs) - # gcc prior version 8 puts symbols in the experimental namespace - if(RLOGIC_HAS_STD_FS AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8) - add_definitions("-DRLOGIC_STD_FILESYSTEM_EXPERIMENTAL") - endif() -endif() - -# llvm prior version 9 puts filesystem in a separate static lib, similar but not exactly the same as GCC above -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) - # libc++ requires different settings than stdlibc++ - # check if libc++ is used by inspecting global flags - string(FIND "${CMAKE_CXX_FLAGS}" "-stdlib=libc++" USES_LIBCXX) - if(USES_LIBCXX EQUAL -1) - # Link stdc++fs from the std lib - check_handle_special_filesystem_lib(stdc++fs) - # llvm prior version 7 puts symbols in the experimental namespace - if(RLOGIC_HAS_STD_FS AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7) - add_definitions("-DRLOGIC_STD_FILESYSTEM_EXPERIMENTAL") - endif() - else() - message(STATUS "Detected usage of libc++, using libc++ specific compiler flags") - # See docs for more details https://libcxx.llvm.org/docs/UsingLibcxx.html#using-libc-experimental-and-experimental - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7) - check_handle_special_filesystem_lib(c++experimental) - if (RLOGIC_HAS_STD_FS) - add_definitions("-DRLOGIC_STD_FILESYSTEM_EXPERIMENTAL") - endif() - else() - link_libraries(libc++fs) - endif() - endif() -endif() - -# distribute to the correct cmake variables -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RLOGIC_CXX_FLAGS} ${RLOGIC_C_CXX_FLAGS} ") -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${RLOGIC_C_FLAGS} ${RLOGIC_C_CXX_FLAGS} ") - -SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${RLOGIC_DEBUG_FLAGS}") -SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${RLOGIC_DEBUG_FLAGS}") -SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${RLOGIC_RELEASE_FLAGS}") -SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${RLOGIC_RELEASE_FLAGS}") -SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${RLOGIC_RELEASE_FLAGS} ${RLOGIC_DEBUG_INFO_FLAGS}") -SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${RLOGIC_RELEASE_FLAGS} ${RLOGIC_DEBUG_INFO_FLAGS}") diff --git a/client/logic/include/ramses-logic/ErrorData.h b/client/logic/include/ramses-logic/ErrorData.h deleted file mode 100644 index 5c5ae836b..000000000 --- a/client/logic/include/ramses-logic/ErrorData.h +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include - -namespace ramses -{ - class LogicObject; - - /** - * #EErrorType helps distinguish between different types of errors in #ramses::ErrorData - */ - enum class EErrorType : int - { - BinaryDataAccessError, ///< Error during attempts to read or write files, non-existing paths, or data corruption (truncation, bit flips etc) - BinaryVersionMismatch, ///< The binary data was created with an incompatible version of the runtime(s) - either logic or ramses - ContentStateError, ///< The logic engine content is in an invalid state - RuntimeError, ///< There was an error during update(), e.g. a RamsesBinding failed to pass its values to Ramses, or Lua script's run() failed - IllegalArgument, ///< A call to the Ramses Logic API with missing arguments or incorrect values provided by user code - LuaSyntaxError, ///< Lua syntax error, e.g. when creating scripts from syntactically incorrect Lua source code - Other, ///< Error does not fit in any of the above clusters - }; - - /** - * Holds information about an error which occured during #ramses::LogicEngine API calls - */ - struct ErrorData - { - /** - * Error description as human-readable text. For Lua errors, an extra stack - * trace is contained in the error string with new-line separators. - */ - std::string message; - - /** - * Semantic type of the error - */ - EErrorType type; - - /** - * The #ramses::LogicObject which caused the issue. Can be nullptr if the issue was not originating from a specific object. - */ - const LogicObject* object; - }; -} diff --git a/client/logic/include/ramses-logic/Logger.h b/client/logic/include/ramses-logic/Logger.h deleted file mode 100644 index 2aedc1cd7..000000000 --- a/client/logic/include/ramses-logic/Logger.h +++ /dev/null @@ -1,73 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-framework-api/APIExport.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -#include -#include - -/** - * @ingroup LogicAPI - * Interface to interact with the internal logger. If you want to handle log messages by yourself, you can - * register your own log handler function with #ramses::Logger::SetLogHandler, which is called each time - * a log message is logged. In addition you can silence the standard output of the log messages - */ -namespace ramses::Logger -{ - /** - * The #LogHandlerFunc can be used to implement a custom log handler. The - * function is called for each log message separately. After the call to the function - * the string data behind std::string_view is deleted. If you want to keep it, you must - * copy it e.g. to a std::string. - * E.g. - * \code{.cpp} - * ramses::Logger::SetLogHandler([](ElogMessageType msgType, std::string_view message){ - * std::cout << message std::endl; - * }); - * \endcode - */ - using LogHandlerFunc = std::function; - - /** - * Controls how verbose the logging is. \p verbosityLimit has the following semantics: - * - if log message has message type with higher or equal priority as verbosityLimit, then it is logged - * - log priority is as documented by #ramses::ELogLevel (Errors are more important than Warnings, etc) - * - the default value is #ramses::ELogLevel::Info, meaning that log messages are processed if they - * have INFO priority or higher. - * - * @param verbosityLimit least priority a log message must have in order to be processed - */ - RAMSES_API void SetLogVerbosityLimit(ELogLevel verbosityLimit); - - /** - * Returns the current log verbosity limit of the logger. See #ramses::Logger::SetLogVerbosityLimit for - * more info on semantics. - * - * @return current log verbosity limit - */ - RAMSES_API ELogLevel GetLogVerbosityLimit(); - - /** - * Sets a custom log handler function, which is called each time a log message occurs. - * Note: setting a custom logger incurs a slight performance cost because log messages - * will be assembled and reported, even if default logging is disabled (#SetDefaultLogging). - * - * @ param logHandlerFunc function which is called for each log message - */ - RAMSES_API void SetLogHandler(const LogHandlerFunc& logHandlerFunc); - - /** - * Sets the default logging to std::out to enabled or disabled. Enabled by default. - * - * @param loggingEnabled true if you want to enable logging to std::out, false otherwise - */ - RAMSES_API void SetDefaultLogging(bool loggingEnabled); -} diff --git a/client/logic/include/ramses-logic/LogicObject.h b/client/logic/include/ramses-logic/LogicObject.h deleted file mode 100644 index 61107d420..000000000 --- a/client/logic/include/ramses-logic/LogicObject.h +++ /dev/null @@ -1,148 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-framework-api/APIExport.h" -#include -#include - -namespace ramses::internal -{ - class LogicObjectImpl; - class ApiObjects; -} - -namespace ramses -{ - /** - * A base class for all rlogic API objects - */ - class LogicObject - { - public: - /** - * Returns the name of this object. - * - * @return the name of this object - */ - [[nodiscard]] RAMSES_API std::string_view getName() const; - - /** - * Sets the name of this object. - * - * @param name new name of the object - * @return true if setting the name was successful, false if the object name can't be changed (e.g. interface objects) - */ - RAMSES_API bool setName(std::string_view name); - - /** - * Returns the id of this object. Every object gets a unique, immutable id assigned on object creation. - * The id is serialized and thus persisted on load. - * - * @return the id of this object - */ - [[nodiscard]] RAMSES_API uint64_t getId() const; - - /** - * Set user ID for this object. - * User IDs are optional identifiers of logic objects stored as number of up to 128 bits. - * User IDs will be logged together with name (#getName) and unique ID (#getId) and are serialized, thus persistent. - * Note that user IDs do not have to be unique, it is user's choice and responsibility if uniqueness is desired. - * User ID is logged only if other than [0,0] was set and the format is hexadecimal 'highId|lowId' with all digits printed. - * - * @param highId high 64 bits of user ID to set - * @param lowId low 64 bits of user ID to set - * @return true if successful, false if failed - */ - RAMSES_API bool setUserId(uint64_t highId, uint64_t lowId); - - /** - * Returns the user ID set using #setUserId. - * - * @return the user ID [highId, lowId] or [0, 0] if no user ID was set - */ - [[nodiscard]] RAMSES_API std::pair getUserId() const; - - /** - * Casts this object to given type. - * Has same behavior as \c dynamic_cast, will return nullptr (without error) if given type does not match this object. - * - * @return logic object cast to given type or nullptr if wrong type provided - */ - template - [[nodiscard]] const T* as() const; - - /** - * @copydoc as() const - */ - template - [[nodiscard]] T* as(); - - /** - * Deleted copy constructor - */ - LogicObject(const LogicObject&) = delete; - - /** - * Deleted move constructor - */ - LogicObject(LogicObject&&) = delete; - - /** - * Deleted assignment operator - */ - LogicObject& operator=(const LogicObject&) = delete; - - /** - * Deleted move assignment operator - */ - LogicObject& operator=(LogicObject&&) = delete; - - std::unique_ptr m_impl; - - protected: - /** - * Constructor of #LogicObject. User is not supposed to call this - LogcNodes are created by subclasses - * - * @param impl implementation details of the #LogicObject - */ - explicit LogicObject(std::unique_ptr impl) noexcept; - - /** - * Destructor of #LogicObject - */ - virtual ~LogicObject() noexcept; - - /** - * Internal implementation of #as with check that T is the correct type - */ - template - [[nodiscard]] RAMSES_API const T* internalCast() const; - - /** - * @copydoc internalCast() const - */ - template - [[nodiscard]] RAMSES_API T* internalCast(); - - friend class internal::ApiObjects; - }; - - template const T* LogicObject::as() const - { - static_assert(std::is_base_of::value, "T in as must be a subclass of LogicObject!"); - return internalCast(); - } - - template T* LogicObject::as() - { - static_assert(std::is_base_of::value, "T in as must be a subclass of LogicObject!"); - return internalCast(); - } -} diff --git a/client/logic/include/ramses-logic/RamsesLogicVersion.h b/client/logic/include/ramses-logic/RamsesLogicVersion.h deleted file mode 100644 index b0ec0ba30..000000000 --- a/client/logic/include/ramses-logic/RamsesLogicVersion.h +++ /dev/null @@ -1,38 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-framework-api/APIExport.h" -#include -#include - -namespace ramses -{ - /** - * @brief Ramses Logic version information - */ - struct RamsesLogicVersion - { - /// Version information as string in format major.minor.patch with an optional arbitrary suffix - const std::string_view string; - - /// Major version - uint32_t major; - /// Minor version - uint32_t minor; - /// Patch version - uint32_t patch; - }; - - /** - * @brief Retrieve currently used Ramses Logic version information - * @returns the Ramses Logic version of the currently used build - */ - [[nodiscard]] RAMSES_API RamsesLogicVersion GetRamsesLogicVersion(); -} diff --git a/client/logic/include/ramses-logic/WarningData.h b/client/logic/include/ramses-logic/WarningData.h deleted file mode 100644 index 4b52a72b0..000000000 --- a/client/logic/include/ramses-logic/WarningData.h +++ /dev/null @@ -1,78 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include - -namespace ramses -{ - class LogicObject; - - /** - * #EWarningType lists the types of content warnings issued by #ramses::LogicEngine::validate - */ - enum class EWarningType : int - { - Performance, ///< Warns about possibly optimize-able performance overhead - UnsafeDataState, ///< Warns about possible data races, potential data loss or otherwise unsafe data - UninitializedData, ///< Warns about uninitialized data which may result in unexpected behavior - PrecisionLoss, ///< Warns about possible precision issues, e.g. casting large types to smaller types - UnusedContent, ///< Warns about unused content which might be removed altogether - DuplicateContent, ///< Warns about duplicate content which might be possible to merge/optimize - Other, ///< Warning does not match any of the existing categories (this is used for new warnings for API compatibility) - }; - - /** - * Holds information about a warning returned by #ramses::LogicEngine::validate() - */ - struct WarningData - { - /** - * Error description as human-readable text. - */ - std::string message; - - /** - * Semantic type of the warning. - */ - EWarningType type; - - /** - * The #ramses::LogicObject which caused the warning. Can be nullptr if the warning was not originating from a specific object. - */ - const LogicObject* object; - }; - - /** - * Returns the string representation of a given warning type. Use this to display a human-readible text - * to content creators. - */ - constexpr const char* GetVerboseDescription(EWarningType warningType) - { - switch (warningType) - { - case EWarningType::Performance: - return "Performance"; - case EWarningType::UnsafeDataState: - return "Unsafe Data State"; - case EWarningType::UninitializedData: - return "Uninitialized Data"; - case EWarningType::PrecisionLoss: - return "Precision Loss"; - case EWarningType::UnusedContent: - return "Unused Content"; - case EWarningType::DuplicateContent: - return "Duplicate Content"; - case EWarningType::Other: - return "Other"; - } - return ""; - } - -} diff --git a/client/logic/lib/flatbuffers/generated/LogicEngineGen.h b/client/logic/lib/flatbuffers/generated/LogicEngineGen.h deleted file mode 100644 index d55078d54..000000000 --- a/client/logic/lib/flatbuffers/generated/LogicEngineGen.h +++ /dev/null @@ -1,433 +0,0 @@ -// automatically generated by the FlatBuffers compiler, do not modify - - -#ifndef FLATBUFFERS_GENERATED_LOGICENGINE_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_LOGICENGINE_RLOGIC_SERIALIZATION_H_ - -#include "flatbuffers/flatbuffers.h" - -#include "AnchorPointGen.h" -#include "AnimationNodeGen.h" -#include "ApiObjectsGen.h" -#include "DataArrayGen.h" -#include "LinkGen.h" -#include "LogicObjectGen.h" -#include "LuaInterfaceGen.h" -#include "LuaModuleGen.h" -#include "LuaScriptGen.h" -#include "PropertyGen.h" -#include "RamsesAppearanceBindingGen.h" -#include "RamsesBindingGen.h" -#include "RamsesCameraBindingGen.h" -#include "RamsesMeshNodeBindingGen.h" -#include "RamsesNodeBindingGen.h" -#include "RamsesReferenceGen.h" -#include "RamsesRenderGroupBindingGen.h" -#include "RamsesRenderPassBindingGen.h" -#include "SkinBindingGen.h" -#include "TimerNodeGen.h" - -namespace rlogic_serialization { - -struct Version; -struct VersionBuilder; - -struct Metadata; -struct MetadataBuilder; - -struct LogicEngine; -struct LogicEngineBuilder; - -inline const flatbuffers::TypeTable *VersionTypeTable(); - -inline const flatbuffers::TypeTable *MetadataTypeTable(); - -inline const flatbuffers::TypeTable *LogicEngineTypeTable(); - -struct Version FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef VersionBuilder Builder; - struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return VersionTypeTable(); - } - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_V_MAJOR = 4, - VT_V_MINOR = 6, - VT_V_PATCH = 8, - VT_V_STRING = 10 - }; - uint32_t v_major() const { - return GetField(VT_V_MAJOR, 0); - } - uint32_t v_minor() const { - return GetField(VT_V_MINOR, 0); - } - uint32_t v_patch() const { - return GetField(VT_V_PATCH, 0); - } - const flatbuffers::String *v_string() const { - return GetPointer(VT_V_STRING); - } - bool Verify(flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyField(verifier, VT_V_MAJOR) && - VerifyField(verifier, VT_V_MINOR) && - VerifyField(verifier, VT_V_PATCH) && - VerifyOffset(verifier, VT_V_STRING) && - verifier.VerifyString(v_string()) && - verifier.EndTable(); - } -}; - -struct VersionBuilder { - typedef Version Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_v_major(uint32_t v_major) { - fbb_.AddElement(Version::VT_V_MAJOR, v_major, 0); - } - void add_v_minor(uint32_t v_minor) { - fbb_.AddElement(Version::VT_V_MINOR, v_minor, 0); - } - void add_v_patch(uint32_t v_patch) { - fbb_.AddElement(Version::VT_V_PATCH, v_patch, 0); - } - void add_v_string(flatbuffers::Offset v_string) { - fbb_.AddOffset(Version::VT_V_STRING, v_string); - } - explicit VersionBuilder(flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - VersionBuilder &operator=(const VersionBuilder &); - flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); - return o; - } -}; - -inline flatbuffers::Offset CreateVersion( - flatbuffers::FlatBufferBuilder &_fbb, - uint32_t v_major = 0, - uint32_t v_minor = 0, - uint32_t v_patch = 0, - flatbuffers::Offset v_string = 0) { - VersionBuilder builder_(_fbb); - builder_.add_v_string(v_string); - builder_.add_v_patch(v_patch); - builder_.add_v_minor(v_minor); - builder_.add_v_major(v_major); - return builder_.Finish(); -} - -struct Version::Traits { - using type = Version; - static auto constexpr Create = CreateVersion; -}; - -inline flatbuffers::Offset CreateVersionDirect( - flatbuffers::FlatBufferBuilder &_fbb, - uint32_t v_major = 0, - uint32_t v_minor = 0, - uint32_t v_patch = 0, - const char *v_string = nullptr) { - auto v_string__ = v_string ? _fbb.CreateString(v_string) : 0; - return rlogic_serialization::CreateVersion( - _fbb, - v_major, - v_minor, - v_patch, - v_string__); -} - -struct Metadata FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef MetadataBuilder Builder; - struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return MetadataTypeTable(); - } - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_METADATASTRING = 4, - VT_EXPORTERVERSION = 6, - VT_EXPORTERFILEVERSION = 8 - }; - const flatbuffers::String *metadataString() const { - return GetPointer(VT_METADATASTRING); - } - const rlogic_serialization::Version *exporterVersion() const { - return GetPointer(VT_EXPORTERVERSION); - } - uint32_t exporterFileVersion() const { - return GetField(VT_EXPORTERFILEVERSION, 0); - } - bool Verify(flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffset(verifier, VT_METADATASTRING) && - verifier.VerifyString(metadataString()) && - VerifyOffset(verifier, VT_EXPORTERVERSION) && - verifier.VerifyTable(exporterVersion()) && - VerifyField(verifier, VT_EXPORTERFILEVERSION) && - verifier.EndTable(); - } -}; - -struct MetadataBuilder { - typedef Metadata Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_metadataString(flatbuffers::Offset metadataString) { - fbb_.AddOffset(Metadata::VT_METADATASTRING, metadataString); - } - void add_exporterVersion(flatbuffers::Offset exporterVersion) { - fbb_.AddOffset(Metadata::VT_EXPORTERVERSION, exporterVersion); - } - void add_exporterFileVersion(uint32_t exporterFileVersion) { - fbb_.AddElement(Metadata::VT_EXPORTERFILEVERSION, exporterFileVersion, 0); - } - explicit MetadataBuilder(flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - MetadataBuilder &operator=(const MetadataBuilder &); - flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); - return o; - } -}; - -inline flatbuffers::Offset CreateMetadata( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset metadataString = 0, - flatbuffers::Offset exporterVersion = 0, - uint32_t exporterFileVersion = 0) { - MetadataBuilder builder_(_fbb); - builder_.add_exporterFileVersion(exporterFileVersion); - builder_.add_exporterVersion(exporterVersion); - builder_.add_metadataString(metadataString); - return builder_.Finish(); -} - -struct Metadata::Traits { - using type = Metadata; - static auto constexpr Create = CreateMetadata; -}; - -inline flatbuffers::Offset CreateMetadataDirect( - flatbuffers::FlatBufferBuilder &_fbb, - const char *metadataString = nullptr, - flatbuffers::Offset exporterVersion = 0, - uint32_t exporterFileVersion = 0) { - auto metadataString__ = metadataString ? _fbb.CreateString(metadataString) : 0; - return rlogic_serialization::CreateMetadata( - _fbb, - metadataString__, - exporterVersion, - exporterFileVersion); -} - -struct LogicEngine FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef LogicEngineBuilder Builder; - struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return LogicEngineTypeTable(); - } - enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { - VT_RAMSESVERSION = 4, - VT_RLOGICVERSION = 6, - VT_APIOBJECTS = 8, - VT_ASSETMETADATA = 10, - VT_FEATURELEVEL = 12 - }; - const rlogic_serialization::Version *ramsesVersion() const { - return GetPointer(VT_RAMSESVERSION); - } - const rlogic_serialization::Version *rlogicVersion() const { - return GetPointer(VT_RLOGICVERSION); - } - const rlogic_serialization::ApiObjects *apiObjects() const { - return GetPointer(VT_APIOBJECTS); - } - const rlogic_serialization::Metadata *assetMetadata() const { - return GetPointer(VT_ASSETMETADATA); - } - uint32_t featureLevel() const { - return GetField(VT_FEATURELEVEL, 1); - } - bool Verify(flatbuffers::Verifier &verifier) const { - return VerifyTableStart(verifier) && - VerifyOffsetRequired(verifier, VT_RAMSESVERSION) && - verifier.VerifyTable(ramsesVersion()) && - VerifyOffsetRequired(verifier, VT_RLOGICVERSION) && - verifier.VerifyTable(rlogicVersion()) && - VerifyOffset(verifier, VT_APIOBJECTS) && - verifier.VerifyTable(apiObjects()) && - VerifyOffset(verifier, VT_ASSETMETADATA) && - verifier.VerifyTable(assetMetadata()) && - VerifyField(verifier, VT_FEATURELEVEL) && - verifier.EndTable(); - } -}; - -struct LogicEngineBuilder { - typedef LogicEngine Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_ramsesVersion(flatbuffers::Offset ramsesVersion) { - fbb_.AddOffset(LogicEngine::VT_RAMSESVERSION, ramsesVersion); - } - void add_rlogicVersion(flatbuffers::Offset rlogicVersion) { - fbb_.AddOffset(LogicEngine::VT_RLOGICVERSION, rlogicVersion); - } - void add_apiObjects(flatbuffers::Offset apiObjects) { - fbb_.AddOffset(LogicEngine::VT_APIOBJECTS, apiObjects); - } - void add_assetMetadata(flatbuffers::Offset assetMetadata) { - fbb_.AddOffset(LogicEngine::VT_ASSETMETADATA, assetMetadata); - } - void add_featureLevel(uint32_t featureLevel) { - fbb_.AddElement(LogicEngine::VT_FEATURELEVEL, featureLevel, 1); - } - explicit LogicEngineBuilder(flatbuffers::FlatBufferBuilder &_fbb) - : fbb_(_fbb) { - start_ = fbb_.StartTable(); - } - LogicEngineBuilder &operator=(const LogicEngineBuilder &); - flatbuffers::Offset Finish() { - const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); - fbb_.Required(o, LogicEngine::VT_RAMSESVERSION); - fbb_.Required(o, LogicEngine::VT_RLOGICVERSION); - return o; - } -}; - -inline flatbuffers::Offset CreateLogicEngine( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset ramsesVersion = 0, - flatbuffers::Offset rlogicVersion = 0, - flatbuffers::Offset apiObjects = 0, - flatbuffers::Offset assetMetadata = 0, - uint32_t featureLevel = 1) { - LogicEngineBuilder builder_(_fbb); - builder_.add_featureLevel(featureLevel); - builder_.add_assetMetadata(assetMetadata); - builder_.add_apiObjects(apiObjects); - builder_.add_rlogicVersion(rlogicVersion); - builder_.add_ramsesVersion(ramsesVersion); - return builder_.Finish(); -} - -struct LogicEngine::Traits { - using type = LogicEngine; - static auto constexpr Create = CreateLogicEngine; -}; - -inline const flatbuffers::TypeTable *VersionTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_UINT, 0, -1 }, - { flatbuffers::ET_UINT, 0, -1 }, - { flatbuffers::ET_UINT, 0, -1 }, - { flatbuffers::ET_STRING, 0, -1 } - }; - static const char * const names[] = { - "v_major", - "v_minor", - "v_patch", - "v_string" - }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 4, type_codes, nullptr, nullptr, names - }; - return &tt; -} - -inline const flatbuffers::TypeTable *MetadataTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_UINT, 0, -1 } - }; - static const flatbuffers::TypeFunction type_refs[] = { - rlogic_serialization::VersionTypeTable - }; - static const char * const names[] = { - "metadataString", - "exporterVersion", - "exporterFileVersion" - }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, names - }; - return &tt; -} - -inline const flatbuffers::TypeTable *LogicEngineTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 }, - { flatbuffers::ET_SEQUENCE, 0, 2 }, - { flatbuffers::ET_UINT, 0, -1 } - }; - static const flatbuffers::TypeFunction type_refs[] = { - rlogic_serialization::VersionTypeTable, - rlogic_serialization::ApiObjectsTypeTable, - rlogic_serialization::MetadataTypeTable - }; - static const char * const names[] = { - "ramsesVersion", - "rlogicVersion", - "apiObjects", - "assetMetadata", - "featureLevel" - }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names - }; - return &tt; -} - -inline const rlogic_serialization::LogicEngine *GetLogicEngine(const void *buf) { - return flatbuffers::GetRoot(buf); -} - -inline const rlogic_serialization::LogicEngine *GetSizePrefixedLogicEngine(const void *buf) { - return flatbuffers::GetSizePrefixedRoot(buf); -} - -inline const char *LogicEngineIdentifier() { - return "rl28"; -} - -inline bool LogicEngineBufferHasIdentifier(const void *buf) { - return flatbuffers::BufferHasIdentifier( - buf, LogicEngineIdentifier()); -} - -inline bool VerifyLogicEngineBuffer( - flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(LogicEngineIdentifier()); -} - -inline bool VerifySizePrefixedLogicEngineBuffer( - flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(LogicEngineIdentifier()); -} - -inline void FinishLogicEngineBuffer( - flatbuffers::FlatBufferBuilder &fbb, - flatbuffers::Offset root) { - fbb.Finish(root, LogicEngineIdentifier()); -} - -inline void FinishSizePrefixedLogicEngineBuffer( - flatbuffers::FlatBufferBuilder &fbb, - flatbuffers::Offset root) { - fbb.FinishSizePrefixed(root, LogicEngineIdentifier()); -} - -} // namespace rlogic_serialization - -#endif // FLATBUFFERS_GENERATED_LOGICENGINE_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/schemas/LogicEngine.fbs b/client/logic/lib/flatbuffers/schemas/LogicEngine.fbs deleted file mode 100644 index cb440e09b..000000000 --- a/client/logic/lib/flatbuffers/schemas/LogicEngine.fbs +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -include "ApiObjects.fbs"; - -namespace rlogic_serialization; - -table Version -{ - // Uses v_ prefix because major/minor collides with system header symbols - v_major:uint32; - v_minor:uint32; - v_patch:uint32; - v_string:string; -} - -table Metadata -{ - metadataString:string; - exporterVersion:Version; - exporterFileVersion:uint32; -} - -table LogicEngine -{ - // Meta info - ramsesVersion:Version (required); - rlogicVersion:Version (required); - // Data objects - apiObjects:ApiObjects; - assetMetadata:Metadata; - featureLevel:uint32 = 1; -} - -root_type LogicEngine; - -// The identifier ensures a binary file/buffer can be identified as a ramses logic -// binary file; also, it is versioned so that breaking changes -// can be handled gracefully by a runtime. -// If we ever reach 99, can use the letters too (e.g. rl99 -> r100) -file_identifier "rl28"; diff --git a/client/logic/lib/impl/Logger.cpp b/client/logic/lib/impl/Logger.cpp deleted file mode 100644 index 5074c7120..000000000 --- a/client/logic/lib/impl/Logger.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-logic/Logger.h" -#include "impl/LoggerImpl.h" - -namespace ramses::Logger -{ - void SetLogVerbosityLimit(ELogLevel verbosityLimit) - { - internal::LoggerImpl::GetInstance().setLogVerbosityLimit(verbosityLimit); - } - - ELogLevel GetLogVerbosityLimit() - { - return internal::LoggerImpl::GetInstance().getLogVerbosityLimit(); - } - - void SetLogHandler(const LogHandlerFunc& logHandlerFunc) - { - internal::LoggerImpl::GetInstance().setLogHandler(logHandlerFunc); - } - - void SetDefaultLogging(bool loggingEnabled) - { - internal::LoggerImpl::GetInstance().setDefaultLogging(loggingEnabled); - } -} diff --git a/client/logic/lib/impl/LoggerImpl.cpp b/client/logic/lib/impl/LoggerImpl.cpp deleted file mode 100644 index 485ccb08c..000000000 --- a/client/logic/lib/impl/LoggerImpl.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "impl/LoggerImpl.h" - -namespace ramses::internal -{ - LoggerImpl::LoggerImpl() noexcept - : m_logHandler(nullptr) - { - } - - void LoggerImpl::setLogHandler(Logger::LogHandlerFunc logHandlerFunc) - { - m_logHandler = std::move(logHandlerFunc); - } - - void LoggerImpl::setDefaultLogging(bool loggingEnabled) - { - m_defaultLogging = loggingEnabled; - } - - void LoggerImpl::setLogVerbosityLimit(ELogLevel verbosityLimit) - { - m_logVerbosityLimit = verbosityLimit; - } - - ELogLevel LoggerImpl::getLogVerbosityLimit() const - { - return m_logVerbosityLimit; - } - -} diff --git a/client/logic/lib/impl/LoggerImpl.h b/client/logic/lib/impl/LoggerImpl.h deleted file mode 100644 index 09d5a1cc7..000000000 --- a/client/logic/lib/impl/LoggerImpl.h +++ /dev/null @@ -1,184 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-logic/Logger.h" - -#include "fmt/format.h" -#include - -#ifdef __ANDROID__ -#include -#else -#include -#endif - -#define LOG_FATAL(...) \ -internal::LoggerImpl::GetInstance().log(ramses::ELogLevel::Fatal, __VA_ARGS__) - -#define LOG_ERROR(...) \ -internal::LoggerImpl::GetInstance().log(ramses::ELogLevel::Error, __VA_ARGS__) - -#define LOG_WARN(...) \ -internal::LoggerImpl::GetInstance().log(ramses::ELogLevel::Warn, __VA_ARGS__) - -#define LOG_INFO(...) \ -internal::LoggerImpl::GetInstance().log(ramses::ELogLevel::Info, __VA_ARGS__) - -#define LOG_DEBUG(...) \ -internal::LoggerImpl::GetInstance().log(ramses::ELogLevel::Debug, __VA_ARGS__) - -#define LOG_TRACE(...) \ -internal::LoggerImpl::GetInstance().log(ramses::ELogLevel::Trace, __VA_ARGS__) - -namespace ramses::internal -{ - static inline const char* GetLogMessageTypeString(ELogLevel type) - { - switch (type) - { - case ELogLevel::Off: - assert(false && "Should never call this!"); - return ""; - case ELogLevel::Fatal: - return "FATAL "; - case ELogLevel::Error: - return "ERROR"; - case ELogLevel::Warn: - return "WARN "; - case ELogLevel::Info: - return "INFO "; - case ELogLevel::Debug: - return "DEBUG"; - case ELogLevel::Trace: - return "TRACE"; - } - assert(false); - return "INFO "; - } - - class LoggerImpl - { - public: - ~LoggerImpl() noexcept = default; - LoggerImpl(const LoggerImpl& other) = delete; - LoggerImpl(LoggerImpl&& other) = delete; - LoggerImpl& operator=(const LoggerImpl& other) = delete; - LoggerImpl& operator=(LoggerImpl&& other) = delete; - - template - // NOLINTNEXTLINE(modernize-avoid-c-arrays) need type representing string literals - void log(ELogLevel messageType, const char(&fmtString)[N], const ARGS&... args); - template - void log(ELogLevel messageType, std::string_view fmtString, const ARGS&... args); - - void setLogVerbosityLimit(ELogLevel verbosityLimit); - [[nodiscard]] ELogLevel getLogVerbosityLimit() const; - void setLogHandler(Logger::LogHandlerFunc logHandlerFunc); - void setDefaultLogging(bool loggingEnabled); - - static LoggerImpl& GetInstance(); - - private: - LoggerImpl() noexcept; - - [[nodiscard]] bool logMessageExceedsVerbosityLimit(ELogLevel messageType) const - { - return (messageType > m_logVerbosityLimit); - } - - static void PrintLogMessage(ELogLevel messageType, const std::string& message); - - Logger::LogHandlerFunc m_logHandler; - bool m_defaultLogging = true; - - ELogLevel m_logVerbosityLimit = ELogLevel::Info; - - }; - - // Note: we are forcing here format string to be literal to avoid issues. - // Otherwise a generated string could potentially contain content from user application - // (e.g. name, script code, malicious or invalid file) which if passed - // to Fmt directly as format string could cause undesired behavior (e.g. if contains curly brackets). - template - // NOLINTNEXTLINE(modernize-avoid-c-arrays) need type representing string literals - inline void LoggerImpl::log(ELogLevel messageType, const char(&fmtString)[N], const ARGS&... args) - { - // Early exit if log level exceeded, or no logger configured - if (logMessageExceedsVerbosityLimit(messageType) || (!m_defaultLogging && !m_logHandler)) - { - return; - } - - const std::string formattedMessage = fmt::format(fmtString, args...); - if (m_defaultLogging) - { - PrintLogMessage(messageType, formattedMessage); - } - if (nullptr != m_logHandler) - { - m_logHandler(messageType, formattedMessage); - } - } - - // workaround to make static assert below dependent on template argument - template - struct TemplatedFalse : std::false_type {}; - - template - void LoggerImpl::log(ELogLevel /*messageType*/, std::string_view /*fmtString*/, const ARGS&... /*args*/) - { - // See comment above for reasons - static_assert(TemplatedFalse::value, "Always use literal as format string when logging, e.g. 'LOG_ERROR(\"{}\", errorMsg)'"); - } - - inline void LoggerImpl::PrintLogMessage(ELogLevel messageType, const std::string& message) - { -#ifdef __ANDROID__ - - android_LogPriority logLevel; - - switch (messageType) - { - case ELogLevel::Trace: - logLevel = ANDROID_LOG_VERBOSE; - break; - case ELogLevel::Debug: - logLevel = ANDROID_LOG_DEBUG; - break; - case ELogLevel::Info: - logLevel = ANDROID_LOG_INFO; - break; - case ELogLevel::Warn: - logLevel = ANDROID_LOG_WARN; - break; - case ELogLevel::Error: - logLevel = ANDROID_LOG_ERROR; - break; - case ELogLevel::Fatal: - logLevel = ANDROID_LOG_FATAL; - break; - default: - logLevel = ANDROID_LOG_UNKNOWN; - break; - } - - __android_log_print(logLevel, "Ramses.Logic", "%s", message.c_str()); -#else - std::cout << "[ " << GetLogMessageTypeString(messageType) << " ] " << message << std::endl; -#endif - } - - inline LoggerImpl& LoggerImpl::GetInstance() - { - static LoggerImpl logger; - return logger; - } -} diff --git a/client/logic/lib/impl/LogicEngine.cpp b/client/logic/lib/impl/LogicEngine.cpp deleted file mode 100644 index b4dfe0fa9..000000000 --- a/client/logic/lib/impl/LogicEngine.cpp +++ /dev/null @@ -1,368 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-logic/LogicEngine.h" - -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" - -#include "impl/LogicEngineImpl.h" -#include "impl/LuaConfigImpl.h" -#include "internals/ApiObjects.h" - -#include - -namespace ramses -{ - LogicEngine::LogicEngine(ramses::EFeatureLevel featureLevel) noexcept - : m_impl(std::make_unique(featureLevel)) - { - } - - LogicEngine::~LogicEngine() noexcept = default; - - ramses::EFeatureLevel LogicEngine::getFeatureLevel() const - { - return m_impl->getFeatureLevel(); - } - - LogicEngine::LogicEngine(LogicEngine&& other) noexcept = default; - - LogicEngine& LogicEngine::operator=(LogicEngine&& other) noexcept = default; - - template - Collection LogicEngine::getLogicObjectsInternal() const - { - return Collection(m_impl->getApiObjects().getApiObjectContainer()); - } - - template - const T* findObject(const internal::ApiObjects& apiObjects, std::string_view name) - { - const auto& container = apiObjects.getApiObjectContainer(); - const auto it = std::find_if(container.cbegin(), container.cend(), [name](const auto& o) { - return o->getName() == name; }); - - return (it == container.cend() ? nullptr : *it); - } - - template - const T* LogicEngine::findLogicObjectInternal(std::string_view name) const - { - auto& container = m_impl->getApiObjects().getApiObjectContainer(); - const auto it = std::find_if(container.begin(), container.end(), [name](const auto& o) { - return o->getName() == name; }); - - return (it == container.end() ? nullptr : *it); - } - - template - T* LogicEngine::findLogicObjectInternal(std::string_view name) - { - auto& container = m_impl->getApiObjects().getApiObjectContainer(); - const auto it = std::find_if(container.begin(), container.end(), [name](const auto& o) { - return o->getName() == name; }); - - return (it == container.end() ? nullptr : *it); - } - - const LogicObject* LogicEngine::findLogicObjectById(uint64_t id) const - { - return m_impl->getApiObjects().getApiObjectById(id); - } - - LogicObject* LogicEngine::findLogicObjectById(uint64_t id) - { - return m_impl->getApiObjects().getApiObjectById(id); - } - - LuaScript* LogicEngine::createLuaScript(std::string_view source, const LuaConfig& config, std::string_view scriptName) - { - return m_impl->createLuaScript(source, *config.m_impl, scriptName); - } - - LuaInterface* LogicEngine::createLuaInterface(std::string_view source, std::string_view interfaceName, const LuaConfig& config) - { - return m_impl->createLuaInterface(source, *config.m_impl, interfaceName, true); - } - - LuaInterface* LogicEngine::createLuaInterface(std::string_view source, std::string_view interfaceName) - { - // deprecated version of interface creation does not verify modules declared in script - return m_impl->createLuaInterface(source, {}, interfaceName, false); - } - - LuaModule* LogicEngine::createLuaModule(std::string_view source, const LuaConfig& config, std::string_view moduleName) - { - return m_impl->createLuaModule(source, *config.m_impl, moduleName); - } - - bool LogicEngine::extractLuaDependencies(std::string_view source, const std::function& callbackFunc) - { - return m_impl->extractLuaDependencies(source, callbackFunc); - } - - RamsesNodeBinding* LogicEngine::createRamsesNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType /* = ramses::ERotationType::Euler_XYZ*/, std::string_view name) - { - return m_impl->createRamsesNodeBinding(ramsesNode, rotationType, name); - } - - bool LogicEngine::destroy(LogicObject& object) - { - return m_impl->destroy(object); - } - - RamsesAppearanceBinding* LogicEngine::createRamsesAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name) - { - return m_impl->createRamsesAppearanceBinding(ramsesAppearance, name); - } - - RamsesCameraBinding* LogicEngine::createRamsesCameraBinding(ramses::Camera& ramsesCamera, std::string_view name) - { - return m_impl->createRamsesCameraBinding(ramsesCamera, name); - } - - RamsesCameraBinding* LogicEngine::createRamsesCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name) - { - return m_impl->createRamsesCameraBindingWithFrustumPlanes(ramsesCamera, name); - } - - RamsesRenderPassBinding* LogicEngine::createRamsesRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name) - { - return m_impl->createRamsesRenderPassBinding(ramsesRenderPass, name); - } - - RamsesRenderGroupBinding* LogicEngine::createRamsesRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElements& elements, std::string_view name) - { - return m_impl->createRamsesRenderGroupBinding(ramsesRenderGroup, elements, name); - } - - RamsesMeshNodeBinding* LogicEngine::createRamsesMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name) - { - return m_impl->createRamsesMeshNodeBinding(ramsesMeshNode, name); - } - - SkinBinding* LogicEngine::createSkinBinding( - const std::vector& joints, - const std::vector& inverseBindMatrices, - RamsesAppearanceBinding& appearanceBinding, - const ramses::UniformInput& jointMatInput, - std::string_view name) - { - return m_impl->createSkinBinding(joints, inverseBindMatrices, appearanceBinding, jointMatInput, name); - } - - template - DataArray* LogicEngine::createDataArrayInternal(const std::vector& data, std::string_view name) - { - static_assert(CanPropertyTypeBeStoredInDataArray(PropertyTypeToEnum::TYPE)); - return m_impl->createDataArray(data, name); - } - - AnimationNode* LogicEngine::createAnimationNode(const AnimationNodeConfig& config, std::string_view name) - { - return m_impl->createAnimationNode(config, name); - } - - TimerNode* LogicEngine::createTimerNode(std::string_view name) - { - return m_impl->createTimerNode(name); - } - - AnchorPoint* LogicEngine::createAnchorPoint(RamsesNodeBinding& nodeBinding, RamsesCameraBinding& cameraBinding, std::string_view name) - { - return m_impl->createAnchorPoint(nodeBinding, cameraBinding, name); - } - - const std::vector& LogicEngine::getErrors() const - { - return m_impl->getErrors(); - } - - const std::vector& LogicEngine::validate() const - { - return m_impl->validate(); - } - - bool LogicEngine::update() - { - return m_impl->update(); - } - - void LogicEngine::enableUpdateReport(bool enable) - { - m_impl->enableUpdateReport(enable); - } - - LogicEngineReport LogicEngine::getLastUpdateReport() const - { - return m_impl->getLastUpdateReport(); - } - - void LogicEngine::setStatisticsLoggingRate(size_t loggingRate) - { - m_impl->setStatisticsLoggingRate(loggingRate); - } - - void LogicEngine::setStatisticsLogLevel(ELogLevel logLevel) - { - m_impl->setStatisticsLogLevel(logLevel); - } - - bool LogicEngine::loadFromFile(std::string_view filename, ramses::Scene* ramsesScene /* = nullptr*/, bool enableMemoryVerification /* = true */) - { - return m_impl->loadFromFile(filename, ramsesScene, enableMemoryVerification); - } - - bool LogicEngine::loadFromFileDescriptor(int fd, size_t offset, size_t length, ramses::Scene* ramsesScene /* = nullptr*/, bool enableMemoryVerification /* = true */) - { - return m_impl->loadFromFileDescriptor(fd, offset, length, ramsesScene, enableMemoryVerification); - } - - bool LogicEngine::loadFromBuffer(const void* rawBuffer, size_t bufferSize, ramses::Scene* ramsesScene /* = nullptr*/, bool enableMemoryVerification /* = true */) - { - return m_impl->loadFromBuffer(rawBuffer, bufferSize, ramsesScene, enableMemoryVerification); - } - - bool LogicEngine::GetFeatureLevelFromFile(std::string_view filename, ramses::EFeatureLevel& detectedFeatureLevel) - { - return internal::LogicEngineImpl::GetFeatureLevelFromFile(filename, detectedFeatureLevel); - } - - bool LogicEngine::GetFeatureLevelFromBuffer(std::string_view logname, const void* buffer, size_t bufferSize, ramses::EFeatureLevel& detectedFeatureLevel) - { - return internal::LogicEngineImpl::GetFeatureLevelFromBuffer(logname, buffer, bufferSize, detectedFeatureLevel); - } - - bool LogicEngine::saveToFile(std::string_view filename, const SaveFileConfig& config) - { - return m_impl->saveToFile(filename, *config.m_impl); - } - - bool LogicEngine::link(const Property& sourceProperty, const Property& targetProperty) - { - return m_impl->link(sourceProperty, targetProperty); - } - - bool LogicEngine::linkWeak(const Property& sourceProperty, const Property& targetProperty) - { - return m_impl->linkWeak(sourceProperty, targetProperty); - } - - bool LogicEngine::unlink(const Property& sourceProperty, const Property& targetProperty) - { - return m_impl->unlink(sourceProperty, targetProperty); - } - - bool LogicEngine::isLinked(const LogicNode& logicNode) const - { - return m_impl->isLinked(logicNode); - } - - size_t LogicEngine::getTotalSerializedSize(ELuaSavingMode luaSavingMode) const - { - return m_impl->getTotalSerializedSize(luaSavingMode); - } - - template - size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode luaSavingMode) const - { - return m_impl->getSerializedSize(luaSavingMode); - } - - const std::vector& LogicEngine::getPropertyLinks() const - { - return m_impl->getApiObjects().getAllPropertyLinks(); - } - - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; - - template RAMSES_API const LogicObject* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const LuaScript* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const LuaModule* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const LuaInterface* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const RamsesNodeBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const RamsesAppearanceBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const RamsesCameraBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const RamsesRenderPassBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const RamsesRenderGroupBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const RamsesMeshNodeBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const SkinBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const DataArray* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const AnimationNode* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const TimerNode* LogicEngine::findLogicObjectInternal(std::string_view) const; - template RAMSES_API const AnchorPoint* LogicEngine::findLogicObjectInternal(std::string_view) const; - - template RAMSES_API LogicObject* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API LuaScript* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API LuaModule* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API LuaInterface* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API RamsesNodeBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API RamsesAppearanceBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API RamsesCameraBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API RamsesRenderPassBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API RamsesRenderGroupBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API RamsesMeshNodeBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API SkinBinding* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API DataArray* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API AnimationNode* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API TimerNode* LogicEngine::findLogicObjectInternal(std::string_view); - template RAMSES_API AnchorPoint* LogicEngine::findLogicObjectInternal(std::string_view); - - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); - template RAMSES_API DataArray* LogicEngine::createDataArrayInternal>(const std::vector>&, std::string_view); - - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; - template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; -} diff --git a/client/logic/lib/impl/LogicEngineImpl.cpp b/client/logic/lib/impl/LogicEngineImpl.cpp deleted file mode 100644 index 4c89ed862..000000000 --- a/client/logic/lib/impl/LogicEngineImpl.cpp +++ /dev/null @@ -1,857 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "impl/LogicEngineImpl.h" - -#include "ramses-framework-api/RamsesVersion.h" -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/SkinBinding.h" - -#include "impl/LogicNodeImpl.h" -#include "impl/LoggerImpl.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LuaModuleImpl.h" -#include "impl/LuaConfigImpl.h" -#include "impl/SaveFileConfigImpl.h" -#include "impl/LogicEngineReportImpl.h" -#include "impl/RamsesRenderGroupBindingElementsImpl.h" - -#include "internals/FileUtils.h" -#include "internals/TypeUtils.h" -#include "internals/RamsesObjectResolver.h" - -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-utils.h" - -#include "generated/LogicEngineGen.h" -#include "ramses-sdk-build-config.h" - -#include "fmt/format.h" - -#include -#include -#include - -namespace ramses::internal -{ - LogicEngineImpl::LogicEngineImpl(ramses::EFeatureLevel featureLevel) - : m_apiObjects{ std::make_unique(featureLevel) } - , m_featureLevel{ featureLevel } - { - if (std::find(ramses::AllFeatureLevels.cbegin(), ramses::AllFeatureLevels.cend(), m_featureLevel) == ramses::AllFeatureLevels.cend()) - { - LOG_ERROR("Unrecognized feature level '0{}' provided, falling back to feature level 01", m_featureLevel); - m_featureLevel = ramses::EFeatureLevel_01; - } - } - - LogicEngineImpl::~LogicEngineImpl() noexcept = default; - - LuaScript* LogicEngineImpl::createLuaScript(std::string_view source, const LuaConfigImpl& config, std::string_view scriptName) - { - m_errors.clear(); - return m_apiObjects->createLuaScript(source, config, scriptName, m_errors); - } - - LuaInterface* LogicEngineImpl::createLuaInterface(std::string_view source, const LuaConfigImpl& config, std::string_view interfaceName, bool verifyModules) - { - m_errors.clear(); - return m_apiObjects->createLuaInterface(source, config, interfaceName, m_errors, verifyModules); - } - - LuaModule* LogicEngineImpl::createLuaModule(std::string_view source, const LuaConfigImpl& config, std::string_view moduleName) - { - m_errors.clear(); - return m_apiObjects->createLuaModule(source, config, moduleName, m_errors); - } - - bool LogicEngineImpl::extractLuaDependencies(std::string_view source, const std::function& callbackFunc) - { - m_errors.clear(); - const std::optional> extractedDependencies = LuaCompilationUtils::ExtractModuleDependencies(source, m_errors); - if (!extractedDependencies) - return false; - - for (const auto& dep : *extractedDependencies) - callbackFunc(dep); - - return true; - } - - RamsesNodeBinding* LogicEngineImpl::createRamsesNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createRamsesNodeBinding(ramsesNode, rotationType, name); - } - - RamsesAppearanceBinding* LogicEngineImpl::createRamsesAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createRamsesAppearanceBinding(ramsesAppearance, name); - } - - RamsesCameraBinding* LogicEngineImpl::createRamsesCameraBinding(ramses::Camera& ramsesCamera, std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createRamsesCameraBinding(ramsesCamera, false, name); - } - - RamsesCameraBinding* LogicEngineImpl::createRamsesCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createRamsesCameraBinding(ramsesCamera, true, name); - } - - RamsesRenderPassBinding* LogicEngineImpl::createRamsesRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createRamsesRenderPassBinding(ramsesRenderPass, name); - } - - RamsesRenderGroupBinding* LogicEngineImpl::createRamsesRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElements& elements, std::string_view name) - { - m_errors.clear(); - - if (elements.m_impl->getElements().empty()) - { - m_errors.add("Cannot create RamsesRenderGroupBinding, there were no elements provided.", nullptr, EErrorType::Other); - return nullptr; - } - - for (const auto& element : elements.m_impl->getElements()) - { - bool isContained = false; - if (element.second->isOfType(ramses::ERamsesObjectType::MeshNode)) - { - isContained = ramsesRenderGroup.containsMeshNode(*ramses::RamsesUtils::TryConvert(*element.second)); - } - else if (element.second->isOfType(ramses::ERamsesObjectType::RenderGroup)) - { - isContained = ramsesRenderGroup.containsRenderGroup(*ramses::RamsesUtils::TryConvert(*element.second)); - } - - if (!isContained) - { - m_errors.add("Cannot create RamsesRenderGroupBinding, one or more of the provided elements is not contained in the RenderGroup to bind.", nullptr, EErrorType::Other); - return nullptr; - } - } - - return m_apiObjects->createRamsesRenderGroupBinding(ramsesRenderGroup, elements, name); - } - - RamsesMeshNodeBinding* LogicEngineImpl::createRamsesMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createRamsesMeshNodeBinding(ramsesMeshNode, name); - } - - SkinBinding* LogicEngineImpl::createSkinBinding( - const std::vector& joints, - const std::vector& inverseBindMatrices, - RamsesAppearanceBinding& appearanceBinding, - const ramses::UniformInput& jointMatInput, - std::string_view name) - { - m_errors.clear(); - - if (joints.empty() || std::find(joints.cbegin(), joints.cend(), nullptr) != joints.cend()) - { - m_errors.add("Cannot create SkinBinding, no or null joint node bindings provided.", nullptr, EErrorType::Other); - return nullptr; - } - - if (joints.size() != inverseBindMatrices.size()) - { - m_errors.add("Cannot create SkinBinding, number of inverse matrices must match the number of joints.", nullptr, EErrorType::Other); - return nullptr; - } - - for (const auto nodeBinding : joints) - { - const auto& nodeBindings = m_apiObjects->getApiObjectContainer(); - if (std::find(nodeBindings.cbegin(), nodeBindings.cend(), nodeBinding) == nodeBindings.cend()) - { - m_errors.add(fmt::format("Failed to create SkinBinding '{}': one or more of the provided Ramses node bindings was not found in this logic instance.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - } - - const auto& appearanceBindings = m_apiObjects->getApiObjectContainer(); - if (std::find(appearanceBindings.cbegin(), appearanceBindings.cend(), &appearanceBinding) == appearanceBindings.cend()) - { - m_errors.add(fmt::format("Failed to create SkinBinding '{}': provided Ramses appearance binding was not found in this logic instance.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - - ramses::UniformInput actualUniformInput; - appearanceBinding.getRamsesAppearance().getEffect().findUniformInput(jointMatInput.getName(), actualUniformInput); - if (!actualUniformInput.isValid() || appearanceBinding.getRamsesAppearance().isInputBound(actualUniformInput)) - { - m_errors.add("Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound.", - nullptr, EErrorType::Other); - return nullptr; - } - - if (*actualUniformInput.getDataType() != ramses::EDataType::Matrix44F - || actualUniformInput.getElementCount() != joints.size()) - { - m_errors.add("Cannot create SkinBinding, provided uniform input must be of type array of Matrix4x4 with element count matching number of joints.", nullptr, EErrorType::Other); - return nullptr; - } - - std::vector jointsAsImpls; - jointsAsImpls.reserve(joints.size()); - for (const auto j : joints) - jointsAsImpls.push_back(&j->m_nodeBinding); - - return m_apiObjects->createSkinBinding(std::move(jointsAsImpls), inverseBindMatrices, appearanceBinding.m_appearanceBinding, actualUniformInput, name); - } - - template - DataArray* LogicEngineImpl::createDataArray(const std::vector& data, std::string_view name) - { - static_assert(CanPropertyTypeBeStoredInDataArray(PropertyTypeToEnum::TYPE)); - m_errors.clear(); - - if (data.empty()) - { - m_errors.add(fmt::format("Cannot create DataArray '{}' with empty data.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - - if constexpr (std::is_same_v>) - { - for (const auto& vec : data) - { - if (vec.size() != data.front().size()) - { - m_errors.add("Failed to create DataArray of float arrays: all arrays must be of same size.", nullptr, EErrorType::Other); - return nullptr; - } - } - } - - // NOLINTNEXTLINE(readability-misleading-indentation) for some reason clang is confused about constexpr branch above - return m_apiObjects->createDataArray(data, name); - } - - ramses::AnimationNode* LogicEngineImpl::createAnimationNode(const AnimationNodeConfig& config, std::string_view name) - { - m_errors.clear(); - - auto containsDataArray = [this](const DataArray* da) { - const auto& dataArrays = m_apiObjects->getApiObjectContainer(); - const auto it = std::find_if(dataArrays.cbegin(), dataArrays.cend(), - [da](const auto& d) { return d == da; }); - return it != dataArrays.cend(); - }; - - if (config.getChannels().empty()) - { - m_errors.add(fmt::format("Failed to create AnimationNode '{}': must provide at least one channel.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - - for (const auto& channel : config.getChannels()) - { - if (!containsDataArray(channel.timeStamps) || - !containsDataArray(channel.keyframes)) - { - m_errors.add(fmt::format("Failed to create AnimationNode '{}': timestamps or keyframes were not found in this logic instance.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - - if ((channel.tangentsIn && !containsDataArray(channel.tangentsIn)) || - (channel.tangentsOut && !containsDataArray(channel.tangentsOut))) - { - m_errors.add(fmt::format("Failed to create AnimationNode '{}': tangents were not found in this logic instance.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - } - - return m_apiObjects->createAnimationNode(*config.m_impl, name); - } - - TimerNode* LogicEngineImpl::createTimerNode(std::string_view name) - { - m_errors.clear(); - return m_apiObjects->createTimerNode(name); - } - - AnchorPoint* LogicEngineImpl::createAnchorPoint(RamsesNodeBinding& nodeBinding, RamsesCameraBinding& cameraBinding, std::string_view name) - { - m_errors.clear(); - - const auto& nodeBindings = m_apiObjects->getApiObjectContainer(); - const auto& cameraBindings = m_apiObjects->getApiObjectContainer(); - if (std::find(nodeBindings.cbegin(), nodeBindings.cend(), &nodeBinding) == nodeBindings.cend() || - std::find(cameraBindings.cbegin(), cameraBindings.cend(), &cameraBinding) == cameraBindings.cend()) - { - m_errors.add(fmt::format("Failed to create AnchorPoint '{}': provided Ramses node binding and/or camera binding were not found in this logic instance.", name), nullptr, EErrorType::IllegalArgument); - return nullptr; - } - - return m_apiObjects->createAnchorPoint(nodeBinding.m_nodeBinding, cameraBinding.m_cameraBinding, name); - } - - bool LogicEngineImpl::destroy(LogicObject& object) - { - m_errors.clear(); - return m_apiObjects->destroy(object, m_errors); - } - - bool LogicEngineImpl::isLinked(const LogicNode& logicNode) const - { - return m_apiObjects->getLogicNodeDependencies().isLinked(logicNode.m_impl); - } - - ramses::EFeatureLevel LogicEngineImpl::getFeatureLevel() const - { - return m_featureLevel; - } - - size_t LogicEngineImpl::activateLinksRecursive(PropertyImpl& output) - { - size_t activatedLinks = 0u; - - const auto childCount = output.getChildCount(); - for (size_t i = 0; i < childCount; ++i) - { - PropertyImpl& child = *output.getChild(i)->m_impl; - - if (TypeUtils::CanHaveChildren(child.getType())) - { - activatedLinks += activateLinksRecursive(child); - } - else - { - const auto& outgoingLinks = child.getOutgoingLinks(); - for (const auto& outLink : outgoingLinks) - { - PropertyImpl* linkedProp = outLink.property; - const bool valueChanged = linkedProp->setValue(child.getValue()); - if (valueChanged || linkedProp->getPropertySemantics() == EPropertySemantics::AnimationInput) - { - linkedProp->getLogicNode().setDirty(true); - ++activatedLinks; - } - } - } - } - - return activatedLinks; - } - - bool LogicEngineImpl::update() - { - m_errors.clear(); - - if (m_statisticsEnabled || m_updateReportEnabled) - { - m_updateReport.clear(); - m_updateReport.sectionStarted(UpdateReport::ETimingSection::TotalUpdate); - } - if (m_updateReportEnabled) - { - m_updateReport.sectionStarted(UpdateReport::ETimingSection::TopologySort); - } - - const std::optional& sortedNodes = m_apiObjects->getLogicNodeDependencies().getTopologicallySortedNodes(); - if (!sortedNodes) - { - m_errors.add("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling update()!", nullptr, EErrorType::ContentStateError); - return false; - } - - if (m_updateReportEnabled) - m_updateReport.sectionFinished(UpdateReport::ETimingSection::TopologySort); - - // force dirty all timer nodes, anchor points and skinbindings - setNodeToBeAlwaysUpdatedDirty(); - - const bool success = updateNodes(*sortedNodes); - - if (m_statisticsEnabled || m_updateReportEnabled) - { - m_updateReport.sectionFinished(UpdateReport::ETimingSection::TotalUpdate); - m_statistics.collect(m_updateReport, sortedNodes->size()); - if (m_statistics.checkUpdateFrameFinished()) - m_statistics.calculateAndLog(); - } - - return success; - } - - bool LogicEngineImpl::updateNodes(const NodeVector& sortedNodes) - { - for (LogicNodeImpl* nodeIter : sortedNodes) - { - LogicNodeImpl& node = *nodeIter; - - if (!node.isDirty()) - { - if (m_updateReportEnabled) - m_updateReport.nodeSkippedExecution(node); - - if(m_nodeDirtyMechanismEnabled) - continue; - } - - if (m_updateReportEnabled) - m_updateReport.nodeExecutionStarted(node); - if (m_statisticsEnabled) - m_statistics.nodeExecuted(); - - const std::optional potentialError = node.update(); - if (potentialError) - { - m_errors.add(potentialError->message, &node.getLogicObject(), EErrorType::RuntimeError); - return false; - } - - Property* outputs = node.getOutputs(); - if (outputs != nullptr) - { - const size_t activatedLinks = activateLinksRecursive(*outputs->m_impl); - - if (m_statisticsEnabled || m_updateReportEnabled) - m_updateReport.linksActivated(activatedLinks); - } - - if (m_updateReportEnabled) - m_updateReport.nodeExecutionFinished(); - - node.setDirty(false); - } - - return true; - } - - void LogicEngineImpl::setNodeToBeAlwaysUpdatedDirty() - { - // force timer nodes dirty so they can update their ticker - for (TimerNode* timerNode : m_apiObjects->getApiObjectContainer()) - timerNode->m_impl.setDirty(true); - // force anchor points dirty because they depend on set of ramses states which cannot be monitored - for (AnchorPoint* anchorPoint : m_apiObjects->getApiObjectContainer()) - anchorPoint->m_impl.setDirty(true); - // force skinbindings dirty because they depend on set of ramses states which cannot be monitored - for (SkinBinding* skinBinding : m_apiObjects->getApiObjectContainer()) - skinBinding->m_impl.setDirty(true); - } - - const std::vector& LogicEngineImpl::getErrors() const - { - return m_errors.getErrors(); - } - - const std::vector& LogicEngineImpl::validate() const - { - m_validationResults.clear(); - - if (m_apiObjects->bindingsDirty()) - m_validationResults.add("Saving logic engine content with manually updated binding values without calling update() will result in those values being lost!", nullptr, EWarningType::UnsafeDataState); - - m_apiObjects->validateInterfaces(m_validationResults); - m_apiObjects->validateDanglingNodes(m_validationResults); - - return m_validationResults.getWarnings(); - } - - bool LogicEngineImpl::CheckRamsesVersionFromFile(const rlogic_serialization::Version& ramsesVersion) - { - // Only major version changes result in file incompatibilities - return static_cast(ramsesVersion.v_major()) == ramses::GetRamsesVersion().major; - } - - bool LogicEngineImpl::loadFromBuffer(const void* rawBuffer, size_t bufferSize, ramses::Scene* scene, bool enableMemoryVerification) - { - return loadFromByteData(rawBuffer, bufferSize, scene, enableMemoryVerification, fmt::format("data buffer '{}' (size: {})", rawBuffer, bufferSize)); - } - - bool LogicEngineImpl::loadFromFile(std::string_view filename, ramses::Scene* scene, bool enableMemoryVerification) - { - std::optional> maybeBytesFromFile = FileUtils::LoadBinary(std::string(filename)); - if (!maybeBytesFromFile) - { - m_errors.add(fmt::format("Failed to load file '{}'", filename), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - - const size_t fileSize = (*maybeBytesFromFile).size(); - return loadFromByteData((*maybeBytesFromFile).data(), fileSize, scene, enableMemoryVerification, fmt::format("file '{}' (size: {})", filename, fileSize)); - } - - bool LogicEngineImpl::loadFromFileDescriptor(int fd, size_t offset, size_t size, ramses::Scene* scene, bool enableMemoryVerification) - { - if (fd <= 0) - { - m_errors.add(fmt::format("Invalid file descriptor: {}", fd), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - if (size == 0) - { - m_errors.add("Failed to load from file descriptor: size may not be 0", nullptr, EErrorType::BinaryDataAccessError); - return false; - } - std::optional> maybeBytesFromFile = FileUtils::LoadBinary(fd, offset, size); - if (!maybeBytesFromFile) - { - m_errors.add(fmt::format("Failed to load from file descriptor: fd: {} offset: {} size: {}", fd, offset, size), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - return loadFromByteData((*maybeBytesFromFile).data(), size, scene, enableMemoryVerification, fmt::format("fd: {} (offset: {}, size: {})", fd, offset, size)); - } - - bool LogicEngineImpl::checkFileIdentifierBytes(const std::string& dataSourceDescription, const std::string& fileIdBytes) - { - const std::string expected = rlogic_serialization::LogicEngineIdentifier(); - if (expected.substr(0, 2) != fileIdBytes.substr(0, 2)) - { - m_errors.add(fmt::format("{}: Tried loading a binary data which doesn't store Ramses Logic content! Expected file bytes 4-5 to be '{}', but found '{}' instead", - dataSourceDescription, - expected.substr(0, 2), - fileIdBytes.substr(0, 2) - ), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - - if (expected.substr(2, 2) != fileIdBytes.substr(2, 2)) - { - m_errors.add(fmt::format("{}: Version mismatch while loading binary data! Expected version '{}', but found '{}'", - dataSourceDescription, - expected.substr(2, 2), - fileIdBytes.substr(2, 2) - ),nullptr, EErrorType::BinaryVersionMismatch); - return false; - } - - return true; - } - - bool LogicEngineImpl::loadFromByteData(const void* byteData, size_t byteSize, ramses::Scene* scene, bool enableMemoryVerification, const std::string& dataSourceDescription) - { - m_errors.clear(); - - if (byteSize < 8) - { - m_errors.add(fmt::format("{} contains corrupted data! Data should be at least 8 bytes", dataSourceDescription), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - - auto* char8Data(static_cast(byteData)); - // file identifier bytes are always placed at bytes 4-7 in the buffer - const std::string fileIdBytes(&char8Data[4], 4); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) No better options - if (!checkFileIdentifierBytes(dataSourceDescription, fileIdBytes)) - { - return false; - } - - auto* uint8Data(static_cast(byteData)); - if (enableMemoryVerification) - { - flatbuffers::Verifier bufferVerifier(uint8Data, byteSize); - const bool bufferOK = bufferVerifier.VerifyBuffer(rlogic_serialization::LogicEngineIdentifier()); - - if (!bufferOK) - { - m_errors.add(fmt::format("{} contains corrupted data!", dataSourceDescription), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - } - - const auto* logicEngine = rlogic_serialization::GetLogicEngine(byteData); - - if (nullptr == logicEngine) - { - m_errors.add(fmt::format("{} doesn't contain logic engine data with readable version specifiers", dataSourceDescription), nullptr, EErrorType::BinaryVersionMismatch); - return false; - } - - const auto& ramsesVersion = *logicEngine->ramsesVersion(); - const auto& rlogicVersion = *logicEngine->rlogicVersion(); - - LOG_INFO("Loading logic engine content from '{}' which was exported with Ramses {} and Logic Engine {}", dataSourceDescription, ramsesVersion.v_string()->string_view(), rlogicVersion.v_string()->string_view()); - - if (!CheckRamsesVersionFromFile(ramsesVersion)) - { - m_errors.add(fmt::format("Version mismatch while loading {}! Expected Ramses version {}.x.x but found {}", - dataSourceDescription, ramses::GetRamsesVersion().major, - ramsesVersion.v_string()->string_view()), nullptr, EErrorType::BinaryVersionMismatch); - return false; - } - - const auto featureLevel = static_cast(logicEngine->featureLevel()); - if (featureLevel != m_featureLevel) - { - m_errors.add(fmt::format("Feature level mismatch while loading {}! Loaded file with feature level {} but LogicEngine was instantiated with feature level {}", - dataSourceDescription, featureLevel, m_featureLevel), nullptr, EErrorType::BinaryVersionMismatch); - return false; - } - - if (nullptr == logicEngine->apiObjects()) - { - m_errors.add(fmt::format("Fatal error while loading {}: doesn't contain API objects!", dataSourceDescription), nullptr, EErrorType::BinaryVersionMismatch); - return false; - } - - if (logicEngine->assetMetadata()) - { - LogAssetMetadata(*logicEngine->assetMetadata()); - } - - std::unique_ptr ramsesResolver; - if (scene != nullptr) - ramsesResolver = std::make_unique(m_errors, *scene); - - std::unique_ptr deserializedObjects = ApiObjects::Deserialize(*logicEngine->apiObjects(), ramsesResolver.get(), dataSourceDescription, m_errors, m_featureLevel); - - if (!deserializedObjects) - { - return false; - } - - // No errors -> move data into member - m_apiObjects = std::move(deserializedObjects); - - return true; - } - - bool LogicEngineImpl::GetFeatureLevelFromFile(std::string_view filename, ramses::EFeatureLevel& detectedFeatureLevel) - { - std::optional> maybeBytesFromFile = FileUtils::LoadBinary(std::string(filename)); - if (!maybeBytesFromFile) - { - LOG_ERROR("Failed to load file '{}'", filename); - return false; - } - - return GetFeatureLevelFromBuffer(filename, maybeBytesFromFile->data(), maybeBytesFromFile->size(), detectedFeatureLevel); - } - - bool LogicEngineImpl::GetFeatureLevelFromBuffer(std::string_view logname, const void* buffer, size_t bufferSize, ramses::EFeatureLevel& detectedFeatureLevel) - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) Safe here, not worth transforming whole vector - flatbuffers::Verifier bufferVerifier(reinterpret_cast(buffer), bufferSize); - if (!bufferVerifier.VerifyBuffer()) - { - LOG_ERROR("'{}' contains corrupted data", logname); - return false; - } - - const auto* logicEngine = rlogic_serialization::GetLogicEngine(buffer); - if (!logicEngine) - { - LOG_ERROR("File '{}' could not be parsed", logname); - return false; - } - - const uint32_t featureLevelInt = logicEngine->featureLevel(); - if (std::find(ramses::AllFeatureLevels.cbegin(), ramses::AllFeatureLevels.cend(), featureLevelInt) == ramses::AllFeatureLevels.cend()) - { - LOG_ERROR("Could not recognize feature level in file '{}'", logname); - return false; - } - - detectedFeatureLevel = static_cast(featureLevelInt); - return true; - } - - bool LogicEngineImpl::saveToFile(std::string_view filename, const SaveFileConfigImpl& config) - { - m_errors.clear(); - - if (!m_apiObjects->checkBindingsReferToSameRamsesScene(m_errors)) - { - m_errors.add("Can't save a logic engine to file while it has references to more than one Ramses scene!", nullptr, EErrorType::ContentStateError); - return false; - } - - // Refuse save() if logic graph has loops - if (!m_apiObjects->getLogicNodeDependencies().getTopologicallySortedNodes()) - { - m_errors.add("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling saveToFile()!", nullptr, EErrorType::ContentStateError); - return false; - } - - if (config.getValidationEnabled()) - { - const std::vector& warnings = validate(); - - if (!warnings.empty()) - { - m_errors.add( - "Failed to saveToFile() because validation warnings were encountered! " - "Refer to the documentation of saveToFile() for details how to address these gracefully.", nullptr, EErrorType::ContentStateError); - return false; - } - } - - const auto& scripts = m_apiObjects->getApiObjectContainer(); - const auto sIt = std::find_if(scripts.cbegin(), scripts.cend(), [](const LuaScript* s) { return s->m_script.hasDebugLogFunctions(); }); - if (sIt != scripts.cend()) - { - m_errors.add(fmt::format("Cannot save to file, Lua script '{}' has enabled debug log functions, remove this script before saving.", (*sIt)->m_impl.getIdentificationString()), *sIt, EErrorType::ContentStateError); - return false; - } - const auto& modules = m_apiObjects->getApiObjectContainer(); - const auto mIt = std::find_if(modules.cbegin(), modules.cend(), [](const LuaModule* m) { return m->m_impl.hasDebugLogFunctions(); }); - if (mIt != modules.cend()) - { - m_errors.add(fmt::format("Cannot save to file, Lua module '{}' has enabled debug log functions, remove this module before saving.", (*mIt)->m_impl.getIdentificationString()), *mIt, EErrorType::ContentStateError); - return false; - } - - flatbuffers::FlatBufferBuilder builder; - ramses::RamsesVersion ramsesVersion = ramses::GetRamsesVersion(); - - const auto ramsesVersionOffset = rlogic_serialization::CreateVersion(builder, - ramsesVersion.major, - ramsesVersion.minor, - ramsesVersion.patch, - builder.CreateString(ramsesVersion.string)); - builder.Finish(ramsesVersionOffset); - - const auto ramsesLogicVersionOffset = rlogic_serialization::CreateVersion(builder, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR_INT, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR_INT, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_PATCH_INT, - builder.CreateString(ramses_sdk::RAMSES_SDK_RAMSES_VERSION)); - builder.Finish(ramsesLogicVersionOffset); - - const auto exporterVersionOffset = rlogic_serialization::CreateVersion(builder, - config.getExporterMajorVersion(), - config.getExporterMinorVersion(), - config.getExporterPatchVersion(), - builder.CreateString("")); - builder.Finish(exporterVersionOffset); - - const auto assetMetadataOffset = rlogic_serialization::CreateMetadata(builder, - builder.CreateString(config.getMetadataString()), - exporterVersionOffset, - config.getExporterFileFormatVersion()); - builder.Finish(assetMetadataOffset); - - const auto logicEngine = rlogic_serialization::CreateLogicEngine(builder, - ramsesVersionOffset, - ramsesLogicVersionOffset, - ApiObjects::Serialize(*m_apiObjects, builder, config.getLuaSavingMode()), - assetMetadataOffset, - m_featureLevel); - - builder.Finish(logicEngine, rlogic_serialization::LogicEngineIdentifier()); - - if (!FileUtils::SaveBinary(std::string(filename), builder.GetBufferPointer(), builder.GetSize())) - { - m_errors.add(fmt::format("Failed to save content to path '{}'!", filename), nullptr, EErrorType::BinaryDataAccessError); - return false; - } - - LOG_INFO("Saved logic engine to file: '{}'.", filename); - - return true; - } - - void LogicEngineImpl::LogAssetMetadata(const rlogic_serialization::Metadata& assetMetadata) - { - const std::string_view metadataString = assetMetadata.metadataString() ? assetMetadata.metadataString()->string_view() : "none"; - LOG_INFO("Logic Engine content metadata: '{}'", metadataString); - const std::string exporterVersion = assetMetadata.exporterVersion() ? - fmt::format("{}.{}.{} (file format version {})", - assetMetadata.exporterVersion()->v_major(), - assetMetadata.exporterVersion()->v_minor(), - assetMetadata.exporterVersion()->v_patch(), - assetMetadata.exporterFileVersion()) : "undefined"; - LOG_INFO("Exporter version: {}", exporterVersion); - } - - bool LogicEngineImpl::link(const Property& sourceProperty, const Property& targetProperty) - { - m_errors.clear(); - - return m_apiObjects->getLogicNodeDependencies().link(*sourceProperty.m_impl, *targetProperty.m_impl, false, m_errors); - } - - bool LogicEngineImpl::linkWeak(const Property& sourceProperty, const Property& targetProperty) - { - m_errors.clear(); - - return m_apiObjects->getLogicNodeDependencies().link(*sourceProperty.m_impl, *targetProperty.m_impl, true, m_errors); - } - - bool LogicEngineImpl::unlink(const Property& sourceProperty, const Property& targetProperty) - { - m_errors.clear(); - - return m_apiObjects->getLogicNodeDependencies().unlink(*sourceProperty.m_impl, *targetProperty.m_impl, m_errors); - } - - ApiObjects& LogicEngineImpl::getApiObjects() - { - return *m_apiObjects; - } - - void LogicEngineImpl::disableTrackingDirtyNodes() - { - m_nodeDirtyMechanismEnabled = false; - } - - void LogicEngineImpl::enableUpdateReport(bool enable) - { - m_updateReportEnabled = enable; - if (!m_updateReportEnabled) - m_updateReport.clear(); - } - - LogicEngineReport LogicEngineImpl::getLastUpdateReport() const - { - return LogicEngineReport{ std::make_unique(m_updateReport) }; - } - - void LogicEngineImpl::setStatisticsLoggingRate(size_t loggingRate) - { - m_statistics.setLoggingRate(loggingRate); - m_statisticsEnabled = (loggingRate != 0u); - } - - void LogicEngineImpl::setStatisticsLogLevel(ELogLevel logLevel) - { - m_statistics.setLogLevel(logLevel); - } - - size_t LogicEngineImpl::getTotalSerializedSize(ELuaSavingMode luaSavingMode) const - { - return ApiObjectsSerializedSize::GetTotalSerializedSize(*m_apiObjects, luaSavingMode); - } - - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); - template DataArray* LogicEngineImpl::createDataArray>(const std::vector>&, std::string_view name); -} diff --git a/client/logic/lib/impl/LogicEngineImpl.h b/client/logic/lib/impl/LogicEngineImpl.h deleted file mode 100644 index b61e165f9..000000000 --- a/client/logic/lib/impl/LogicEngineImpl.h +++ /dev/null @@ -1,187 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/LogicEngineReport.h" -#include "ramses-framework-api/DataTypes.h" -#include "ramses-framework-api/EFeatureLevel.h" -#include "internals/ApiObjects.h" -#include "internals/LogicNodeDependencies.h" -#include "internals/ErrorReporting.h" -#include "internals/ValidationResults.h" -#include "internals/UpdateReport.h" -#include "internals/LogicNodeUpdateStatistics.h" -#include "internals/ApiObjectsSerializedSize.h" - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/ERotationType.h" - -#include -#include -#include -#include - -namespace ramses -{ - class Scene; - class SceneObject; - class Node; - class Appearance; - class Camera; - class RenderPass; - class RenderGroup; - class UniformInput; - class MeshNode; -} - -namespace ramses -{ - class RamsesNodeBinding; - class RamsesAppearanceBinding; - class RamsesCameraBinding; - class RamsesRenderPassBinding; - class RamsesRenderGroupBinding; - class RamsesRenderGroupBindingElements; - class RamsesMeshNodeBinding; - class SkinBinding; - class DataArray; - class AnimationNode; - class AnimationNodeConfig; - class TimerNode; - class AnchorPoint; - class LuaScript; - class LuaInterface; - class LuaModule; - class LogicNode; - class Property; - enum class ELogLevel; -} - -namespace rlogic_serialization -{ - struct Version; - struct Metadata; -} - -namespace ramses::internal -{ - class LuaConfigImpl; - class SaveFileConfigImpl; - class LogicNodeImpl; - class RamsesBindingImpl; - class ApiObjects; - - class LogicEngineImpl - { - public: - // Move-able (noexcept); Not copy-able - - // can't be noexcept anymore because constructor of std::unordered_map can throw - explicit LogicEngineImpl(ramses::EFeatureLevel featureLevel); - ~LogicEngineImpl() noexcept; - - LogicEngineImpl(LogicEngineImpl&& other) noexcept = default; - LogicEngineImpl& operator=(LogicEngineImpl&& other) noexcept = default; - LogicEngineImpl(const LogicEngineImpl& other) = delete; - LogicEngineImpl& operator=(const LogicEngineImpl& other) = delete; - - // Public API - LuaScript* createLuaScript(std::string_view source, const LuaConfigImpl& config, std::string_view scriptName); - LuaInterface* createLuaInterface(std::string_view source, const LuaConfigImpl& config, std::string_view interfaceName, bool verifyModules); - LuaModule* createLuaModule(std::string_view source, const LuaConfigImpl& config, std::string_view moduleName); - bool extractLuaDependencies(std::string_view source, const std::function& callbackFunc); - RamsesNodeBinding* createRamsesNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name); - RamsesAppearanceBinding* createRamsesAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name); - RamsesCameraBinding* createRamsesCameraBinding(ramses::Camera& ramsesCamera, std::string_view name); - RamsesCameraBinding* createRamsesCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name); - RamsesRenderPassBinding* createRamsesRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name); - RamsesRenderGroupBinding* createRamsesRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElements& elements, std::string_view name); - RamsesMeshNodeBinding* createRamsesMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name); - SkinBinding* createSkinBinding( - const std::vector& joints, - const std::vector& inverseBindMatrices, - RamsesAppearanceBinding& appearanceBinding, - const ramses::UniformInput& jointMatInput, - std::string_view name); - template - DataArray* createDataArray(const std::vector& data, std::string_view name); - AnimationNode* createAnimationNode(const AnimationNodeConfig& config, std::string_view name); - TimerNode* createTimerNode(std::string_view name); - AnchorPoint* createAnchorPoint(RamsesNodeBinding& nodeBinding, RamsesCameraBinding& cameraBinding, std::string_view name); - - bool destroy(LogicObject& object); - - bool update(); - - [[nodiscard]] const std::vector& getErrors() const; - const std::vector& validate() const; - - bool loadFromFile(std::string_view filename, ramses::Scene* scene, bool enableMemoryVerification); - bool loadFromFileDescriptor(int fd, size_t offset, size_t size, ramses::Scene* scene, bool enableMemoryVerification); - bool loadFromBuffer(const void* rawBuffer, size_t bufferSize, ramses::Scene* scene, bool enableMemoryVerification); - bool saveToFile(std::string_view filename, const SaveFileConfigImpl& config); - [[nodiscard]] static bool GetFeatureLevelFromFile(std::string_view filename, ramses::EFeatureLevel& detectedFeatureLevel); - [[nodiscard]] static bool GetFeatureLevelFromBuffer(std::string_view logname, const void* buffer, size_t bufferSize, ramses::EFeatureLevel& detectedFeatureLevel); - - bool link(const Property& sourceProperty, const Property& targetProperty); - bool linkWeak(const Property& sourceProperty, const Property& targetProperty); - bool unlink(const Property& sourceProperty, const Property& targetProperty); - - [[nodiscard]] bool isLinked(const LogicNode& logicNode) const; - - [[nodiscard]] ramses::EFeatureLevel getFeatureLevel() const; - - [[nodiscard]] ApiObjects& getApiObjects(); - - // for benchmarking purposes only - void disableTrackingDirtyNodes(); - - void enableUpdateReport(bool enable); - [[nodiscard]] LogicEngineReport getLastUpdateReport() const; - - void setStatisticsLoggingRate(size_t loggingRate); - void setStatisticsLogLevel(ELogLevel logLevel); - - [[nodiscard]] size_t getTotalSerializedSize(ELuaSavingMode luaSavingMode) const; - template - [[nodiscard]] size_t getSerializedSize(ELuaSavingMode luaSavingMode) const; - - private: - size_t activateLinksRecursive(PropertyImpl& output); - void setNodeToBeAlwaysUpdatedDirty(); - - static bool CheckRamsesVersionFromFile(const rlogic_serialization::Version& ramsesVersion); - - static void LogAssetMetadata(const rlogic_serialization::Metadata& assetMetadata); - - [[nodiscard]] bool updateNodes(const NodeVector& nodes); - - [[nodiscard]] bool loadFromByteData(const void* byteData, size_t byteSize, ramses::Scene* scene, bool enableMemoryVerification, const std::string& dataSourceDescription); - [[nodiscard]] bool checkFileIdentifierBytes(const std::string& dataSourceDescription, const std::string& fileIdBytes); - - std::unique_ptr m_apiObjects; - ErrorReporting m_errors; - mutable ValidationResults m_validationResults; - bool m_nodeDirtyMechanismEnabled = true; - - bool m_updateReportEnabled = false; - bool m_statisticsEnabled = true; - UpdateReport m_updateReport; - LogicNodeUpdateStatistics m_statistics; - - ramses::EFeatureLevel m_featureLevel; - }; - - template - size_t LogicEngineImpl::getSerializedSize(ELuaSavingMode luaSavingMode) const - { - return ApiObjectsSerializedSize::GetSerializedSize(*m_apiObjects, luaSavingMode); - } -} diff --git a/client/logic/lib/impl/LogicObject.cpp b/client/logic/lib/impl/LogicObject.cpp deleted file mode 100644 index 827383017..000000000 --- a/client/logic/lib/impl/LogicObject.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-logic/LogicObject.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" -#include "impl/LogicObjectImpl.h" - -namespace ramses -{ - LogicObject::LogicObject(std::unique_ptr impl) noexcept - : m_impl{ std::move(impl) } - { - } - - LogicObject::~LogicObject() noexcept = default; - - std::string_view LogicObject::getName() const - { - return m_impl->getName(); - } - - uint64_t LogicObject::getId() const - { - return m_impl->getId(); - } - - bool LogicObject::setUserId(uint64_t highId, uint64_t lowId) - { - return m_impl->setUserId(highId, lowId); - } - - std::pair LogicObject::getUserId() const - { - return m_impl->getUserId(); - } - - template - const T* LogicObject::internalCast() const - { - return dynamic_cast(this); - } - - template - T* LogicObject::internalCast() - { - return dynamic_cast(this); - } - - bool LogicObject::setName(std::string_view name) - { - return m_impl->setName(name); - } - - template RAMSES_API const LogicObject* LogicObject::internalCast() const; - template RAMSES_API const LogicNode* LogicObject::internalCast() const; - template RAMSES_API const RamsesBinding* LogicObject::internalCast() const; - template RAMSES_API const LuaModule* LogicObject::internalCast() const; - template RAMSES_API const LuaScript* LogicObject::internalCast() const; - template RAMSES_API const LuaInterface* LogicObject::internalCast() const; - template RAMSES_API const RamsesNodeBinding* LogicObject::internalCast() const; - template RAMSES_API const RamsesAppearanceBinding* LogicObject::internalCast() const; - template RAMSES_API const RamsesCameraBinding* LogicObject::internalCast() const; - template RAMSES_API const RamsesRenderPassBinding* LogicObject::internalCast() const; - template RAMSES_API const RamsesRenderGroupBinding* LogicObject::internalCast() const; - template RAMSES_API const RamsesMeshNodeBinding* LogicObject::internalCast() const; - template RAMSES_API const SkinBinding* LogicObject::internalCast() const; - template RAMSES_API const DataArray* LogicObject::internalCast() const; - template RAMSES_API const AnimationNode* LogicObject::internalCast() const; - template RAMSES_API const TimerNode* LogicObject::internalCast() const; - template RAMSES_API const AnchorPoint* LogicObject::internalCast() const; - - template RAMSES_API LogicObject* LogicObject::internalCast(); - template RAMSES_API LogicNode* LogicObject::internalCast(); - template RAMSES_API RamsesBinding* LogicObject::internalCast(); - template RAMSES_API LuaModule* LogicObject::internalCast(); - template RAMSES_API LuaScript* LogicObject::internalCast(); - template RAMSES_API LuaInterface* LogicObject::internalCast(); - template RAMSES_API RamsesNodeBinding* LogicObject::internalCast(); - template RAMSES_API RamsesAppearanceBinding* LogicObject::internalCast(); - template RAMSES_API RamsesCameraBinding* LogicObject::internalCast(); - template RAMSES_API RamsesRenderPassBinding* LogicObject::internalCast(); - template RAMSES_API RamsesRenderGroupBinding* LogicObject::internalCast(); - template RAMSES_API RamsesMeshNodeBinding* LogicObject::internalCast(); - template RAMSES_API SkinBinding* LogicObject::internalCast(); - template RAMSES_API DataArray* LogicObject::internalCast(); - template RAMSES_API AnimationNode* LogicObject::internalCast(); - template RAMSES_API TimerNode* LogicObject::internalCast(); - template RAMSES_API AnchorPoint* LogicObject::internalCast(); -} diff --git a/client/logic/lib/impl/RamsesLogicVersion.cpp b/client/logic/lib/impl/RamsesLogicVersion.cpp deleted file mode 100644 index 5c109cbed..000000000 --- a/client/logic/lib/impl/RamsesLogicVersion.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-logic/RamsesLogicVersion.h" -#include "ramses-sdk-build-config.h" - -namespace ramses -{ - RamsesLogicVersion GetRamsesLogicVersion() - { - return RamsesLogicVersion - { - ramses_sdk::RAMSES_SDK_RAMSES_VERSION, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR_INT, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR_INT, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_PATCH_INT, - }; - } -} diff --git a/client/logic/lib/impl/RamsesRenderGroupBindingElements.cpp b/client/logic/lib/impl/RamsesRenderGroupBindingElements.cpp deleted file mode 100644 index 0264cac35..000000000 --- a/client/logic/lib/impl/RamsesRenderGroupBindingElements.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "impl/RamsesRenderGroupBindingElementsImpl.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/RenderGroup.h" - -namespace ramses -{ - RamsesRenderGroupBindingElements::RamsesRenderGroupBindingElements() noexcept - : m_impl{ std::make_unique() } - { - } - RamsesRenderGroupBindingElements::~RamsesRenderGroupBindingElements() noexcept = default; - - RamsesRenderGroupBindingElements::RamsesRenderGroupBindingElements(const RamsesRenderGroupBindingElements& other) - : m_impl{ std::make_unique(*other.m_impl) } - { - } - - RamsesRenderGroupBindingElements::RamsesRenderGroupBindingElements(RamsesRenderGroupBindingElements&& other) noexcept = default; - - RamsesRenderGroupBindingElements& RamsesRenderGroupBindingElements::operator=(const RamsesRenderGroupBindingElements& other) - { - m_impl = std::make_unique(*other.m_impl); - return *this; - } - - RamsesRenderGroupBindingElements& RamsesRenderGroupBindingElements::operator=(RamsesRenderGroupBindingElements&& other) noexcept = default; - - bool RamsesRenderGroupBindingElements::addElement(const ramses::MeshNode& meshNode, std::string_view elementName) - { - return m_impl->addElement(meshNode, elementName); - } - - bool RamsesRenderGroupBindingElements::addElement(const ramses::RenderGroup& nestedRenderGroup, std::string_view elementName) - { - return m_impl->addElement(nestedRenderGroup, elementName); - } -} diff --git a/client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.cpp b/client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.cpp deleted file mode 100644 index 30a8f0cd6..000000000 --- a/client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "impl/RamsesRenderGroupBindingElementsImpl.h" -#include "impl/LoggerImpl.h" -#include "ramses-client-api/SceneObject.h" - -namespace ramses::internal -{ - bool RamsesRenderGroupBindingElementsImpl::addElement(const ramses::SceneObject& ramsesObject, std::string_view elementName) - { - assert(ramsesObject.isOfType(ramses::ERamsesObjectType::MeshNode) || ramsesObject.isOfType(ramses::ERamsesObjectType::RenderGroup)); - - const std::string name = (elementName.empty() ? ramsesObject.getName() : std::string{ elementName }); - if (name.empty()) - { - LOG_ERROR("RamsesRenderGroupBindingElements: Failed to add element, object has no name and provided element name is empty."); - return false; - } - - const auto it = std::find_if(m_elements.cbegin(), m_elements.cend(), [&ramsesObject](const auto& e) { return e.second == &ramsesObject; }); - if (it != m_elements.cend()) - { - LOG_ERROR("RamsesRenderGroupBindingElements: Failed to add element '{}', it is already contained under name '{}'.", name, it->first); - return false; - } - - m_elements.push_back({ name, &ramsesObject }); - - return true; - } - - const RamsesRenderGroupBindingElementsImpl::Elements& RamsesRenderGroupBindingElementsImpl::getElements() const - { - return m_elements; - } -} diff --git a/client/logic/lib/impl/SaveFileConfigImpl.h b/client/logic/lib/impl/SaveFileConfigImpl.h deleted file mode 100644 index 057ac2991..000000000 --- a/client/logic/lib/impl/SaveFileConfigImpl.h +++ /dev/null @@ -1,47 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include -#include -#include "ramses-logic/ELuaSavingMode.h" - -namespace ramses -{ - class LuaModule; -} - -namespace ramses::internal -{ - class SaveFileConfigImpl - { - public: - void setMetadataString(std::string_view metadata); - void setExporterVersion(uint32_t major, uint32_t minor, uint32_t patch, uint32_t fileFormatVersion); - void setValidationEnabled(bool validationEnabled); - void setLuaSavingMode(ELuaSavingMode mode); - - [[nodiscard]] const std::string& getMetadataString() const; - [[nodiscard]] uint32_t getExporterMajorVersion() const; - [[nodiscard]] uint32_t getExporterMinorVersion() const; - [[nodiscard]] uint32_t getExporterPatchVersion() const; - [[nodiscard]] uint32_t getExporterFileFormatVersion() const; - [[nodiscard]] bool getValidationEnabled() const; - [[nodiscard]] ELuaSavingMode getLuaSavingMode() const; - - private: - std::string m_metadata; - uint32_t m_exporterMajorVersion = 0u; - uint32_t m_exporterMinorVersion = 0u; - uint32_t m_exporterPatchVersion = 0u; - uint32_t m_exporterFileFormatVersion = 0u; - bool m_validationEnabled = true; - ELuaSavingMode m_luaSavingMode = ELuaSavingMode::SourceAndByteCode; - }; -} diff --git a/client/logic/lib/internals/ErrorReporting.cpp b/client/logic/lib/internals/ErrorReporting.cpp deleted file mode 100644 index dd6bdd82a..000000000 --- a/client/logic/lib/internals/ErrorReporting.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "internals/ErrorReporting.h" -#include "ramses-logic/LogicNode.h" -#include "impl/LoggerImpl.h" -#include "impl/LogicObjectImpl.h" - -namespace ramses::internal -{ - void ErrorReporting::add(std::string errorMessage, const LogicObject* logicObject, EErrorType type) - { - if (logicObject) - { - LOG_ERROR("[{}] {}", logicObject->m_impl->getIdentificationString(), errorMessage); - } - else - { - LOG_ERROR("{}", errorMessage); - } - - m_errors.emplace_back(ErrorData{ std::move(errorMessage), type, logicObject }); - } - - void ErrorReporting::clear() - { - m_errors.clear(); - } - - const std::vector& ErrorReporting::getErrors() const - { - return m_errors; - } - -} diff --git a/client/logic/lib/internals/ValidationResults.cpp b/client/logic/lib/internals/ValidationResults.cpp deleted file mode 100644 index a7513274d..000000000 --- a/client/logic/lib/internals/ValidationResults.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "internals/ValidationResults.h" -#include "ramses-logic/LogicNode.h" -#include "impl/LoggerImpl.h" -#include "impl/LogicObjectImpl.h" - -namespace ramses::internal -{ - void ValidationResults::add(std::string warningMessage, const LogicObject* logicObject, EWarningType type) - { - if (logicObject) - { - LOG_WARN("[{}] {}", logicObject->m_impl->getIdentificationString(), warningMessage); - } - else - { - LOG_WARN("{}", warningMessage); - } - - m_warnings.emplace_back(WarningData{ std::move(warningMessage), type, logicObject }); - } - - void ValidationResults::clear() - { - m_warnings.clear(); - } - - const std::vector& ValidationResults::getWarnings() const - { - return m_warnings; - } -} diff --git a/client/logic/tools/ramses-logic-viewer-test/CMakeLists.txt b/client/logic/tools/ramses-logic-viewer-test/CMakeLists.txt deleted file mode 100644 index 3c46d05e7..000000000 --- a/client/logic/tools/ramses-logic-viewer-test/CMakeLists.txt +++ /dev/null @@ -1,68 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2022 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -find_package(ImageMagick COMPONENTS compare) - -if(ramses-sdk_USE_IMAGEMAGICK AND NOT ImageMagick_FOUND) - message(FATAL_ERROR "ImageMagick compare not found, but required (ramses-sdk_USE_IMAGEMAGICK=ON)") -endif() - -add_executable(ramses-logic-viewer-unittests - LogicViewerTestBase.h - LogicViewerTestBase.cpp - LogicViewerTest.cpp - LogicViewerLuaTest.cpp -) - -target_link_libraries(ramses-logic-viewer-unittests - PRIVATE - ramses-logic-viewer-lib - ramses-client - ramses-gmock - ramses-gmock-main - ) - -target_include_directories(ramses-logic-viewer-unittests - PRIVATE - $ - $ -) - -MakeTestFromTarget( - TARGET ramses-logic-viewer-unittests - SUFFIX UNITTEST) - -folderizeTarget(ramses-logic-viewer-unittests) - -if(ramses-sdk_USE_IMAGEMAGICK AND ImageMagick_FOUND) - createModule( - NAME ramses-logic-viewer-swrast-tests - TYPE BINARY - ENABLE_INSTALL ${ramses-sdk_ENABLE_INSTALL} - INCLUDE_PATHS ${PROJECT_SOURCE_DIR}/client/logic/tools/ramses-logic-viewer - ${PROJECT_SOURCE_DIR}/client/logic/unittests/shared - SRC_FILES LogicViewerAppTest.cpp - DEPENDENCIES ramses-logic-viewer-gui-lib - ramses-shared-lib - ramses-gmock - ramses-gmock-main - ) - - if(TARGET ramses-logic-viewer-swrast-tests) - target_compile_definitions(ramses-logic-viewer-swrast-tests - PRIVATE MAGICK_COMPARE="${ImageMagick_compare_EXECUTABLE}" - ) - - MakeTestFromTarget( - TARGET ramses-logic-viewer-swrast-tests - SUFFIX RNDSANDWICHTEST_SWRAST) - - add_custom_command(TARGET ramses-logic-viewer-swrast-tests PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/res/ ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/unittests) - endif() -endif() diff --git a/client/logic/unittests/api/LoggerTest.cpp b/client/logic/unittests/api/LoggerTest.cpp deleted file mode 100644 index d95b84399..000000000 --- a/client/logic/unittests/api/LoggerTest.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gmock/gmock.h" - -#include "ramses-logic/Logger.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -#include "impl/LoggerImpl.h" - -#include "LogTestUtils.h" - -namespace ramses -{ - // Test default state without fixture - TEST(ALogger_ByDefault, HasInfoAsVerbosityLimit) - { - EXPECT_EQ(ELogLevel::Info, Logger::GetLogVerbosityLimit()); - } - - class ALogger : public ::testing::Test - { - protected: - std::vector m_logTypes; - std::vector m_logMessages; - ScopedLogContextLevel m_logCollector{ ELogLevel::Trace, [this](ELogLevel type, std::string_view message) - { - m_logTypes.emplace_back(type); - m_logMessages.emplace_back(message); - } - }; - }; - - TEST_F(ALogger, LogsDifferentLogLevelsSequentially) - { - LOG_FATAL("Fatal"); - LOG_ERROR("Error"); - LOG_WARN("Warn"); - LOG_INFO("Info"); - LOG_DEBUG("Debug"); - LOG_TRACE("Trace"); - - EXPECT_THAT(m_logTypes, ::testing::ElementsAre(ELogLevel::Fatal, ELogLevel::Error, ELogLevel::Warn, ELogLevel::Info, ELogLevel::Debug, ELogLevel::Trace)); - EXPECT_THAT(m_logMessages, ::testing::ElementsAre("Fatal", "Error", "Warn", "Info", "Debug", "Trace")); - } - - TEST_F(ALogger, LogsFormattedMessage) - { - LOG_INFO("Info Message {}", 42); - - EXPECT_EQ(m_logMessages[0], "Info Message 42"); - } - - TEST_F(ALogger, LogsFormattedMessageWithMultipleArgumentsAndTypes) - { - LOG_INFO("Info Message {} {} {} {}", 42, 0.5f, "bool:", true); - - EXPECT_EQ(m_logMessages[0], "Info Message 42 0.5 bool: true"); - } - - TEST_F(ALogger, SetsDefaultLoggingOffAndOnAgain) - { - Logger::SetDefaultLogging(false); - LOG_INFO("Info Message {} {} {}", 42, 42.0f, "42"); - Logger::SetDefaultLogging(true); - LOG_INFO("Info Message {} {} {}", 42, 42.0f, "43"); - - // Can't expect anything because default logging goes to stdout - } - - TEST_F(ALogger, SetsDefaultLoggingOff_DoesNotAffectCustomLogHandler) - { - Logger::SetDefaultLogging(false); - - LOG_INFO("info"); - EXPECT_EQ(m_logMessages[0], "info"); - - // Reset to not affect other tests - Logger::SetDefaultLogging(true); - } - - TEST_F(ALogger, ChangesLogVerbosityAffectsWhichMessagesAreProcessed) - { - Logger::SetLogVerbosityLimit(ELogLevel::Error); - - // Simulate logs of all types. Only error and fatal error should be logged - LOG_TRACE("trace"); - LOG_DEBUG("debug"); - LOG_FATAL("fatal"); - LOG_WARN("warn"); - LOG_INFO("info"); - LOG_DEBUG("debug"); - LOG_ERROR("error"); - - EXPECT_THAT(m_logTypes, ::testing::ElementsAre(ELogLevel::Fatal, ELogLevel::Error)); - } -} diff --git a/client/logic/unittests/api/LogicEngineTest_Compatibility.cpp b/client/logic/unittests/api/LogicEngineTest_Compatibility.cpp deleted file mode 100644 index 84044ca47..000000000 --- a/client/logic/unittests/api/LogicEngineTest_Compatibility.cpp +++ /dev/null @@ -1,450 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "RamsesTestUtils.h" -#include "WithTempDirectory.h" -#include "FeatureLevelTestValues.h" -#include "PropertyLinkTestUtils.h" - -#include "ramses-logic/RamsesLogicVersion.h" -#include "ramses-logic/Property.h" - -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-framework-api/RamsesVersion.h" - -#include "internals/ApiObjects.h" -#include "internals/FileUtils.h" - -#include "generated/LogicEngineGen.h" -#include "fmt/format.h" - -#include - -namespace ramses::internal -{ - class ALogicEngine_Compatibility : public ALogicEngineBase, public ::testing::TestWithParam - { - public: - ALogicEngine_Compatibility() : ALogicEngineBase{ GetParam() } - { - } - - protected: - static const char* GetFileIdentifier() - { - return rlogic_serialization::LogicEngineIdentifier(); - } - - void createFlatLogicEngineData( - ramses::RamsesVersion ramsesVersion, - ramses::RamsesLogicVersion logicVersion, - const char* fileId = GetFileIdentifier(), - ramses::EFeatureLevel featureLevel = GetParam()) - { - ApiObjects emptyApiObjects{ featureLevel }; - - auto logicEngine = rlogic_serialization::CreateLogicEngine( - m_fbBuilder, - rlogic_serialization::CreateVersion(m_fbBuilder, - ramsesVersion.major, ramsesVersion.minor, ramsesVersion.patch, m_fbBuilder.CreateString(ramsesVersion.string)), - rlogic_serialization::CreateVersion(m_fbBuilder, - logicVersion.major, logicVersion.minor, logicVersion.patch, m_fbBuilder.CreateString(logicVersion.string)), - ApiObjects::Serialize(emptyApiObjects, m_fbBuilder, ELuaSavingMode::ByteCodeOnly), - 0, - featureLevel - ); - - m_fbBuilder.Finish(logicEngine, fileId); - } - - static ramses::RamsesVersion FakeRamsesVersion() - { - ramses::RamsesVersion version{ - "10.20.900-suffix", - 10, - 20, - 900 - }; - return version; - } - - flatbuffers::FlatBufferBuilder m_fbBuilder; - WithTempDirectory m_tempDir; - }; - - INSTANTIATE_TEST_SUITE_P( - ALogicEngine_CompatibilityTests, - ALogicEngine_Compatibility, - GetFeatureLevelTestValues()); - - TEST_P(ALogicEngine_Compatibility, CreatesLogicEngineWithFeatureLevel) - { - LogicEngine logicEngine{ GetParam() }; - EXPECT_EQ(GetParam(), logicEngine.getFeatureLevel()); - } - - TEST_P(ALogicEngine_Compatibility, FallsBackToFeatureLevel01IfUnknownFeatureLevelRequested) - { - LogicEngine logicEngine{ static_cast(999) }; - EXPECT_EQ(ramses::EFeatureLevel_01, logicEngine.getFeatureLevel()); - } - - TEST_P(ALogicEngine_Compatibility, ProducesErrorIfDeserilizedFromFileReferencingIncompatibleRamsesVersion) - { - createFlatLogicEngineData(FakeRamsesVersion(), GetRamsesLogicVersion()); - - ASSERT_TRUE(FileUtils::SaveBinary("wrong_ramses_version.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - EXPECT_FALSE(m_logicEngine.loadFromFile("wrong_ramses_version.bin")); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr("Version mismatch while loading file 'wrong_ramses_version.bin' (size: ")); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr(fmt::format("Expected Ramses version {}.x.x but found 10.20.900-suffix", ramses::GetRamsesVersion().major))); - - //Also test with buffer version of the API - EXPECT_FALSE(m_logicEngine.loadFromBuffer(m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr("Version mismatch while loading data buffer")); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr(fmt::format("Expected Ramses version {}.x.x but found 10.20.900-suffix", ramses::GetRamsesVersion().major))); - } - - TEST_P(ALogicEngine_Compatibility, ProducesErrorIfDeserilizedFromDifferentTypeOfFile) - { - const char* badFileIdentifier = "xyWW"; - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), badFileIdentifier); - - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - EXPECT_FALSE(m_logicEngine.loadFromFile("temp.bin")); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr(fmt::format("Tried loading a binary data which doesn't store Ramses Logic content! Expected file bytes 4-5 to be 'rl', but found 'xy' instead"))); - } - - TEST_P(ALogicEngine_Compatibility, ProducesErrorIfDeserilizedFromIncompatibleFileVersion) - { - // Format was changed - const char* versionFromFuture = "rl99"; - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), versionFromFuture); - - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - EXPECT_FALSE(m_logicEngine.loadFromFile("temp.bin")); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr(fmt::format("Version mismatch while loading binary data! Expected version '28', but found '99'"))); - } - - TEST_P(ALogicEngine_Compatibility, CanDeserializeSameFeatureLevelVersion) - { - const ramses::EFeatureLevel featureLevel = GetParam(); - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), GetFileIdentifier(), featureLevel); - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - LogicEngine logicEngine(featureLevel); - EXPECT_TRUE(logicEngine.loadFromFile("temp.bin")); - EXPECT_TRUE(logicEngine.getErrors().empty()); - } - - TEST_P(ALogicEngine_Compatibility, ProducesErrorIfDeserializedFromIncompatibleFeatureLevelVersion) - { - // this test is not parametrized - if (GetParam() != ramses::EFeatureLevel_01) - GTEST_SKIP(); - - // test all mismatching combinations - for (ramses::EFeatureLevel fileFeatureLevel : ramses::AllFeatureLevels) - { - for (ramses::EFeatureLevel engineFeatureLevel : ramses::AllFeatureLevels) - { - if (fileFeatureLevel == engineFeatureLevel) - continue; - - // note - use file identifier matching engine otherwise load fails already when checking file identifier (tested above) - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), GetFileIdentifier(), fileFeatureLevel); - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - LogicEngine logicEngine{ engineFeatureLevel }; - EXPECT_FALSE(logicEngine.loadFromFile("temp.bin")); - const auto& errors = logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr(fmt::format("Feature level mismatch while loading file 'temp.bin' (size:"))); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr(fmt::format("Loaded file with feature level {} but LogicEngine was instantiated with feature level {}", fileFeatureLevel, engineFeatureLevel))); - } - } - } - - TEST_P(ALogicEngine_Compatibility, CanParseFeatureLevelFromFile) - { - const ramses::EFeatureLevel featureLevel = GetParam(); - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), GetFileIdentifier(), featureLevel); - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - ramses::EFeatureLevel detectedFeatureLevel = ramses::EFeatureLevel_01; - ASSERT_TRUE(LogicEngine::GetFeatureLevelFromFile("temp.bin", detectedFeatureLevel)); - EXPECT_EQ(featureLevel, detectedFeatureLevel); - } - - TEST_P(ALogicEngine_Compatibility, FailsToParseFeatureLevelFromNotExistingFile) - { - ramses::EFeatureLevel detectedFeatureLevel = ramses::EFeatureLevel_01; - EXPECT_FALSE(LogicEngine::GetFeatureLevelFromFile("doesntexist", detectedFeatureLevel)); - } - - TEST_P(ALogicEngine_Compatibility, FailsToParseFeatureLevelFromCorruptedFile) - { - const std::string invalidData{"invaliddata"}; - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", invalidData.data(), invalidData.size())); - - ramses::EFeatureLevel detectedFeatureLevel = ramses::EFeatureLevel_01; - EXPECT_FALSE(LogicEngine::GetFeatureLevelFromFile("temp.bin", detectedFeatureLevel)); - } - - TEST_P(ALogicEngine_Compatibility, FailsToParseFeatureLevelFromValidFileButUnknownFeatureLevel) - { - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), GetFileIdentifier(), static_cast(999)); - ASSERT_TRUE(FileUtils::SaveBinary("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize())); - - ramses::EFeatureLevel detectedFeatureLevel = ramses::EFeatureLevel_01; - EXPECT_FALSE(LogicEngine::GetFeatureLevelFromFile("temp.bin", detectedFeatureLevel)); - } - - TEST_P(ALogicEngine_Compatibility, CanParseFeatureLevelFromBuffer) - { - const ramses::EFeatureLevel featureLevel = GetParam(); - createFlatLogicEngineData(ramses::GetRamsesVersion(), GetRamsesLogicVersion(), GetFileIdentifier(), featureLevel); - ramses::EFeatureLevel detectedFeatureLevel = ramses::EFeatureLevel_01; - ASSERT_TRUE(LogicEngine::GetFeatureLevelFromBuffer("temp.bin", m_fbBuilder.GetBufferPointer(), m_fbBuilder.GetSize(), detectedFeatureLevel)); - EXPECT_EQ(featureLevel, detectedFeatureLevel); - } - - // These tests will always break on incompatible file format changes. - class ALogicEngine_Binary_Compatibility : public ::testing::Test - { - protected: - static void checkBaseContents(LogicEngine& logicEngine, ramses::Scene& ramsesScene) - { - ASSERT_NE(nullptr, logicEngine.findByName("nestedModuleMath")); - ASSERT_NE(nullptr, logicEngine.findByName("moduleMath")); - ASSERT_NE(nullptr, logicEngine.findByName("moduleTypes")); - const auto script1 = logicEngine.findByName("script1"); - ASSERT_NE(nullptr, script1); - EXPECT_NE(nullptr, script1->getInputs()->getChild("intInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("int64Input")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("vec2iInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("vec3iInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("vec4iInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("floatInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("vec2fInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("vec3fInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("vec4fInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("boolInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("stringInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("structInput")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("arrayInput")); - EXPECT_NE(nullptr, script1->getOutputs()->getChild("floatOutput")); - EXPECT_NE(nullptr, script1->getOutputs()->getChild("nodeTranslation")); - EXPECT_NE(nullptr, script1->getInputs()->getChild("floatInput")); - const auto script2 = logicEngine.findByName("script2"); - ASSERT_NE(nullptr, script2); - EXPECT_NE(nullptr, script2->getInputs()->getChild("floatInput")); - EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("offsetX")); - EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("offsetY")); - EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("width")); - EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("height")); - EXPECT_NE(nullptr, script2->getOutputs()->getChild("floatUniform")); - const auto animNode = logicEngine.findByName("animNode"); - ASSERT_NE(nullptr, animNode); - EXPECT_EQ(1u, animNode->getInputs()->getChildCount()); - ASSERT_EQ(2u, animNode->getOutputs()->getChildCount()); - EXPECT_NE(nullptr, animNode->getOutputs()->getChild("channel")); - ASSERT_NE(nullptr, logicEngine.findByName("animNodeWithDataProperties")); - EXPECT_EQ(2u, logicEngine.findByName("animNodeWithDataProperties")->getInputs()->getChildCount()); - EXPECT_NE(nullptr, logicEngine.findByName("timerNode")); - - const auto nodeBinding = logicEngine.findByName("nodebinding"); - ASSERT_NE(nullptr, nodeBinding); - EXPECT_NE(nullptr, nodeBinding->getInputs()->getChild("enabled")); - EXPECT_TRUE(logicEngine.isLinked(*nodeBinding)); - const auto cameraBinding = logicEngine.findByName("camerabinding"); - ASSERT_NE(nullptr, cameraBinding); - const auto appearanceBinding = logicEngine.findByName("appearancebinding"); - ASSERT_NE(nullptr, appearanceBinding); - EXPECT_NE(nullptr, logicEngine.findByName("dataarray")); - - std::vector expectedLinks; - - const auto intf = logicEngine.findByName("intf"); - ASSERT_NE(nullptr, intf); - intf->getInputs()->getChild("struct")->getChild("floatInput")->set(42.5f); - expectedLinks.push_back({ intf->getOutputs()->getChild("struct")->getChild("floatInput"), script1->getInputs()->getChild("floatInput"), false }); - expectedLinks.push_back({ script1->getOutputs()->getChild("boolOutput"), nodeBinding->getInputs()->getChild("enabled"), false }); - expectedLinks.push_back({ script1->getOutputs()->getChild("floatOutput"), script2->getInputs()->getChild("floatInput"), false }); - expectedLinks.push_back({ script1->getOutputs()->getChild("nodeTranslation"), nodeBinding->getInputs()->getChild("translation"), false }); - expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("offsetX"), cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"), false }); - expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("offsetY"), cameraBinding->getInputs()->getChild("viewport")->getChild("offsetY"), false }); - expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("width"), cameraBinding->getInputs()->getChild("viewport")->getChild("width"), false }); - expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("height"), cameraBinding->getInputs()->getChild("viewport")->getChild("height"), false }); - expectedLinks.push_back({ script2->getOutputs()->getChild("floatUniform"), appearanceBinding->getInputs()->getChild("floatUniform"), false }); - expectedLinks.push_back({ animNode->getOutputs()->getChild("channel"), appearanceBinding->getInputs()->getChild("animatedFloatUniform"), false }); - - const auto triLogicIntf = logicEngine.findByName("Interface_CameraCrane"); - ASSERT_TRUE(triLogicIntf); - const auto triLogicScript = logicEngine.findByName("CameraCrane"); - ASSERT_TRUE(triLogicScript); - const auto triCamNode = logicEngine.findByName("triangleCamNodeBinding"); - ASSERT_TRUE(triCamNode); - const auto triCamBinding = logicEngine.findByName("triangleCamBinding"); - ASSERT_TRUE(triCamBinding); - expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("CraneGimbal")->getChild("Yaw"), triLogicScript->getInputs()->getChild("yaw"), false }); - expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("CraneGimbal")->getChild("Pitch"), triLogicScript->getInputs()->getChild("pitch"), false }); - expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("Viewport")->getChild("Width"), triCamBinding->getInputs()->getChild("viewport")->getChild("width"), false }); - expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("Viewport")->getChild("Height"), triCamBinding->getInputs()->getChild("viewport")->getChild("height"), false }); - expectedLinks.push_back({ triLogicScript->getOutputs()->getChild("translation"), triCamNode->getInputs()->getChild("translation"), false }); - - PropertyLinkTestUtils::ExpectLinks(logicEngine, expectedLinks); - - EXPECT_TRUE(logicEngine.update()); - - // Values on Ramses are updated according to expectations - vec3f translation; - auto node = ramses::RamsesUtils::TryConvert(*ramsesScene.findObjectByName("test node")); - auto camera = ramses::RamsesUtils::TryConvert(*ramsesScene.findObjectByName("test camera")); - node->getTranslation(translation); - EXPECT_EQ(translation, vec3f(42.5f, 2.f, 3.f)); - // test that linked value from script propagated to ramses scene - EXPECT_EQ(ramses::EVisibilityMode::Off, node->getVisibility()); - - EXPECT_EQ(camera->getViewportX(), 45); - EXPECT_EQ(camera->getViewportY(), 47); - EXPECT_EQ(camera->getViewportWidth(), 143u); - EXPECT_EQ(camera->getViewportHeight(), 243u); - - // Animation node is linked and can be animated - EXPECT_FLOAT_EQ(2.f, *animNode->getOutputs()->getChild("duration")->get()); - animNode->getInputs()->getChild("progress")->set(0.75f); - EXPECT_TRUE(logicEngine.update()); - - ramses::UniformInput uniform; - auto appearance = ramses::RamsesUtils::TryConvert(*ramsesScene.findObjectByName("test appearance")); - appearance->getEffect().getUniformInput(1, uniform); - float floatValue = 0.f; - appearance->getInputValue(uniform, floatValue); - EXPECT_FLOAT_EQ(1.5f, floatValue); - - EXPECT_EQ(957, *logicEngine.findByName("script2")->getOutputs()->getChild("nestedModulesResult")->get()); - - EXPECT_TRUE(logicEngine.findByName("renderpassbinding")); - EXPECT_TRUE(logicEngine.findByName("anchorpoint")); - - const auto cameraBindingPersp = logicEngine.findByName("camerabindingPersp"); - const auto cameraBindingPerspWithFrustumPlanes = logicEngine.findByName("camerabindingPerspWithFrustumPlanes"); - ASSERT_TRUE(cameraBindingPersp && cameraBindingPerspWithFrustumPlanes); - EXPECT_EQ(4u, cameraBindingPersp->getInputs()->getChild("frustum")->getChildCount()); - EXPECT_EQ(6u, cameraBindingPerspWithFrustumPlanes->getInputs()->getChild("frustum")->getChildCount()); - - EXPECT_TRUE(logicEngine.findByName("rendergroupbinding")); - EXPECT_TRUE(logicEngine.findByName("skin")); - const auto dataArray = logicEngine.findByName("dataarrayOfArrays"); - ASSERT_TRUE(dataArray); - EXPECT_EQ(EPropertyType::Array, dataArray->getDataType()); - EXPECT_EQ(2u, dataArray->getNumElements()); - const auto data = dataArray->getData>(); - ASSERT_TRUE(data); - const std::vector> expectedData{ { 1.f, 2.f, 3.f, 4.f, 5.f }, { 6.f, 7.f, 8.f, 9.f, 10.f } }; - EXPECT_EQ(expectedData, *data); - EXPECT_TRUE(logicEngine.findByName("meshnodebinding")); - } - - static void expectFeatureLevel02Content(const LogicEngine& /*logicEngine*/, ramses::Scene& /*ramsesScene*/) - { - // features added in future feature level expected to be present - } - - static void expectFeatureLevel02ContentNotPresent(const LogicEngine& /*logicEngine*/) - { - // features added in future feature level expected NOT to be present in previous feature levels - } - - static void checkContents(LogicEngine& logicEngine, ramses::Scene& scene) - { - // check for content expected to exist - // higher feature level always contains content supported by lower level - switch (logicEngine.getFeatureLevel()) - { - //case ramses::EFeatureLevel_02: - // expectFeatureLevel02Content(logicEngine, scene); - // [[fallthrough]]; - case ramses::EFeatureLevel_01: - checkBaseContents(logicEngine, scene); - break; - } - - // check for content expected to not exist - // lower feature level never contains content supported only in higher level - switch (logicEngine.getFeatureLevel()) - { - case ramses::EFeatureLevel_01: - expectFeatureLevel02ContentNotPresent(logicEngine); - // [[fallthrough]]; - //case EFeatureLevel_02: - // break; - } - } - - static void saveAndReloadAndCheckContents(LogicEngine& logicEngine, ramses::Scene& scene) - { - WithTempDirectory tempDir; - - ramses::SaveFileConfig noValidationConfig; - noValidationConfig.setValidationEnabled(false); - logicEngine.saveToFile("temp.rlogic", noValidationConfig); - - LogicEngine logicEngineAnother{ logicEngine.getFeatureLevel() }; - ASSERT_TRUE(logicEngineAnother.loadFromFile("temp.rlogic", &scene)); - EXPECT_TRUE(logicEngineAnother.update()); - - checkContents(logicEngineAnother, scene); - } - - ramses::Scene* loadRamsesScene(ramses::EFeatureLevel featureLevel) - { - switch(featureLevel) - { - case ramses::EFeatureLevel_01: - return &m_ramses.loadSceneFromFile("res/testScene_01.ramses"); - } - return nullptr; - } - - RamsesTestSetup m_ramses; - }; - - TEST_F(ALogicEngine_Binary_Compatibility, CanLoadAndUpdateABinaryFileExportedWithLastCompatibleVersionOfEngine_FeatureLevel01) - { - ramses::EFeatureLevel featureLevel = ramses::EFeatureLevel_Latest; - EXPECT_TRUE(LogicEngine::GetFeatureLevelFromFile("res/testLogic_01.rlogic", featureLevel)); - EXPECT_EQ(ramses::EFeatureLevel_01, featureLevel); - - ramses::Scene* scene = loadRamsesScene(ramses::EFeatureLevel_01); - ASSERT_TRUE(scene); - LogicEngine logicEngine{ featureLevel }; - ASSERT_TRUE(logicEngine.loadFromFile("res/testLogic_01.rlogic", scene)); - EXPECT_TRUE(logicEngine.update()); - - checkContents(logicEngine, *scene); - saveAndReloadAndCheckContents(logicEngine, *scene); - } -} diff --git a/client/logic/unittests/api/LogicEngineTest_Dirtiness.cpp b/client/logic/unittests/api/LogicEngineTest_Dirtiness.cpp deleted file mode 100644 index 9f9da9a43..000000000 --- a/client/logic/unittests/api/LogicEngineTest_Dirtiness.cpp +++ /dev/null @@ -1,569 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/Property.h" - -#include "LogicEngineTest_Base.h" -#include "impl/LogicEngineImpl.h" -#include "impl/LogicNodeImpl.h" -#include "internals/ApiObjects.h" - -#include "RamsesTestUtils.h" - -namespace ramses::internal -{ - class ALogicEngine_DirtinessBase : public ALogicEngineBase - { - protected: - ApiObjects& m_apiObjects = { m_logicEngine.m_impl->getApiObjects() }; - - const std::string_view m_minimal_script = R"( - function interface(IN,OUT) - IN.data = Type:Int32() - OUT.data = Type:Int32() - end - function run(IN,OUT) - OUT.data = IN.data - end - )"; - - const std::string_view m_nested_properties_script = R"( - function interface(IN,OUT) - IN.data = { - nested = Type:Int32() - } - OUT.data = { - nested = Type:Int32() - } - end - function run(IN,OUT) - OUT.data.nested = IN.data.nested - end - )"; - - const std::string_view m_valid_empty_interface = R"( - function interface(IN,OUT) - end - )"; - - const std::string_view m_minimal_interface = R"( - function interface(IN) - IN.data = Type:Int32() - end - )"; - }; - - class ALogicEngine_Dirtiness : public ALogicEngine_DirtinessBase, public ::testing::Test - { - }; - - TEST_F(ALogicEngine_Dirtiness, CreatedObjectsAreDirtyAfterCreating) - { - const auto obj1 = m_logicEngine.createLuaScript(m_valid_empty_script); - EXPECT_TRUE(obj1->m_impl.isDirty()); - const auto obj2 = m_logicEngine.createLuaInterface(m_valid_empty_interface, "iface name"); - EXPECT_TRUE(obj2->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, CreatedBindingsAreNotDirtyAfterCreating) - { - const auto obj3 = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - EXPECT_FALSE(obj3->m_impl.isDirty()); - const auto obj4 = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, ""); - EXPECT_FALSE(obj4->m_impl.isDirty()); - const auto obj5 = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, ""); - EXPECT_FALSE(obj5->m_impl.isDirty()); - const auto obj6 = createRenderGroupBinding(); - EXPECT_FALSE(obj6->m_impl.isDirty()); - const auto obj = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode); - EXPECT_FALSE(obj->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingNodeBinding_AndChangingInput) - { - RamsesNodeBinding* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - nodeBinding->getInputs()->getChild("scaling")->set(vec3f{1.5f, 1.f, 1.f}); - EXPECT_TRUE(nodeBinding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingAppearanceBinding_AndChangingInput) - { - RamsesAppearanceBinding* appBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, ""); - appBinding->getInputs()->getChild("floatUniform")->set(15.f); - EXPECT_TRUE(appBinding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingCameraBinding_AndChangingInput) - { - RamsesCameraBinding* camBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, ""); - camBinding->getInputs()->getChild("viewport")->getChild("width")->set(15); - EXPECT_TRUE(camBinding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingRenderPassBinding_AndChangingInput) - { - RamsesRenderPassBinding* binding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, ""); - binding->getInputs()->getChild("enabled")->set(false); - EXPECT_TRUE(binding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingRenderGroupBinding_AndChangingInput) - { - auto binding = createRenderGroupBinding(); - binding->getInputs()->getChild("renderOrders")->getChild("mesh")->set(42); - EXPECT_TRUE(binding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingMeshNodeBinding_AndChangingInput) - { - auto binding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode); - binding->getInputs()->getChild("vertexOffset")->set(42); - EXPECT_TRUE(binding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, NotDirty_AfterCreatingObjectsAndCallingUpdate) - { - const auto obj1 = m_logicEngine.createLuaScript(m_valid_empty_script); - const auto obj2 = m_logicEngine.createLuaInterface(m_valid_empty_interface, "iface name"); - const auto obj3 = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - const auto obj4 = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, ""); - const auto obj5 = m_logicEngine.createRamsesCameraBinding(*m_camera, ""); - const auto obj6 = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, ""); - const auto obj7 = createRenderGroupBinding(); - const auto obj8 = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode); - - m_logicEngine.update(); - EXPECT_FALSE(obj1->m_impl.isDirty()); - EXPECT_FALSE(obj2->m_impl.isDirty()); - EXPECT_FALSE(obj3->m_impl.isDirty()); - EXPECT_FALSE(obj4->m_impl.isDirty()); - EXPECT_FALSE(obj5->m_impl.isDirty()); - EXPECT_FALSE(obj6->m_impl.isDirty()); - EXPECT_FALSE(obj7->m_impl.isDirty()); - EXPECT_FALSE(obj8->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, Dirty_AfterSettingScriptInput) - { - LuaScript* script = m_logicEngine.createLuaScript(m_minimal_script); - m_logicEngine.update(); - - script->getInputs()->getChild("data")->set(5); - - EXPECT_TRUE(script->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, Dirty_AfterSettingNestedScriptInput) - { - LuaScript* script = m_logicEngine.createLuaScript(m_nested_properties_script); - m_logicEngine.update(); - - script->getInputs()->getChild("data")->getChild("nested")->set(5); - - EXPECT_TRUE(script->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, Dirty_AfterSettingInterfaceInput) - { - LuaInterface* intf = m_logicEngine.createLuaInterface(m_minimal_interface, "iface name"); - m_logicEngine.update(); - - intf->getInputs()->getChild("data")->set(5); - - EXPECT_TRUE(intf->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(intf->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, Dirty_WhenSettingBindingInputToDefaultValue) - { - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - m_logicEngine.update(); - - // zeroes is the default value - binding->getInputs()->getChild("translation")->set({0, 0, 0}); - EXPECT_TRUE(binding->m_impl.isDirty()); - m_logicEngine.update(); - - // Set different value, and then set again - binding->getInputs()->getChild("translation")->set({1, 2, 3}); - m_logicEngine.update(); - binding->getInputs()->getChild("translation")->set({1, 2, 3}); - EXPECT_TRUE(binding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, Dirty_WhenSettingBindingInputToDifferentValue) - { - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - m_logicEngine.update(); - - // Set non-default value, and then set again to different value - binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); - m_logicEngine.update(); - EXPECT_FALSE(binding->m_impl.isDirty()); - binding->getInputs()->getChild("translation")->set({ 11, 12, 13 }); - EXPECT_TRUE(binding->m_impl.isDirty()); - } - - class ALogicEngine_DirtinessViaLink : public ALogicEngine_DirtinessBase, public ::testing::TestWithParam - { - protected: - void link(const Property& src, const Property& dst) - { - if (GetParam()) - { - EXPECT_TRUE(m_logicEngine.linkWeak(src, dst)); - } - else - { - EXPECT_TRUE(m_logicEngine.link(src, dst)); - } - } - }; - - INSTANTIATE_TEST_SUITE_P( - ALogicEngine_DirtinessViaLink_TestInstances, - ALogicEngine_DirtinessViaLink, - ::testing::Values(false, true)); - - TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenAddingLink) - { - LuaScript* script1 = m_logicEngine.createLuaScript(m_minimal_script); - LuaScript* script2 = m_logicEngine.createLuaScript(m_minimal_script); - m_logicEngine.update(); - - link(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); - EXPECT_TRUE(script1->m_impl.isDirty()); - EXPECT_TRUE(script2->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - } - - TEST_P(ALogicEngine_DirtinessViaLink, NotDirty_WhenRemovingLink) - { - LuaScript* script1 = m_logicEngine.createLuaScript(m_minimal_script); - LuaScript* script2 = m_logicEngine.createLuaScript(m_minimal_script); - link(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); - m_logicEngine.update(); - - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - m_logicEngine.unlink(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); - - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - } - - TEST_P(ALogicEngine_DirtinessViaLink, NotDirty_WhenRemovingNestedLink) - { - LuaScript* script1 = m_logicEngine.createLuaScript(m_nested_properties_script); - LuaScript* script2 = m_logicEngine.createLuaScript(m_nested_properties_script); - link(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); - m_logicEngine.update(); - - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - m_logicEngine.unlink(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); - - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - } - - // Removing link does not mark things dirty, but setting value does - TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenRemovingLink_AndSettingValueByCallingSetAfterwards) - { - LuaScript* script1 = m_logicEngine.createLuaScript(m_nested_properties_script); - LuaScript* script2 = m_logicEngine.createLuaScript(m_nested_properties_script); - m_logicEngine.update(); - - link(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); - m_logicEngine.update(); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - - m_logicEngine.unlink(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); - script2->getInputs()->getChild("data")->getChild("nested")->set(5); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_TRUE(script2->m_impl.isDirty()); - } - - TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenAddingLinkToInterfaceInput) - { - LuaScript* script = m_logicEngine.createLuaScript(m_minimal_script); - LuaInterface* intf = m_logicEngine.createLuaInterface(m_minimal_interface, "iface name"); - m_logicEngine.update(); - - link(*script->getOutputs()->getChild("data"), *intf->getInputs()->getChild("data")); - EXPECT_TRUE(script->m_impl.isDirty()); - EXPECT_TRUE(intf->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(intf->m_impl.isDirty()); - } - - TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenAddingLinkToInterfaceOutput) - { - LuaScript* script = m_logicEngine.createLuaScript(m_minimal_script); - LuaInterface* intf = m_logicEngine.createLuaInterface(m_minimal_interface, "iface name"); - m_logicEngine.update(); - - link(*intf->getOutputs()->getChild("data"), *script->getInputs()->getChild("data")); - EXPECT_TRUE(script->m_impl.isDirty()); - EXPECT_TRUE(intf->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(intf->m_impl.isDirty()); - } - - TEST_P(ALogicEngine_DirtinessViaLink, NotDirty_WhenRemovingLinkToInterface) - { - LuaScript* script = m_logicEngine.createLuaScript(m_minimal_script); - LuaInterface* intf = m_logicEngine.createLuaInterface(m_minimal_interface, "iface name"); - link(*script->getOutputs()->getChild("data"), *intf->getInputs()->getChild("data")); - m_logicEngine.update(); - - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(intf->m_impl.isDirty()); - m_logicEngine.unlink(*script->getOutputs()->getChild("data"), *intf->getInputs()->getChild("data")); - - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(intf->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(intf->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_Dirtiness, Dirty_WhenScriptHadRuntimeError) - { - const std::string_view scriptWithError = R"( - function interface(IN,OUT) - end - function run(IN,OUT) - error("Snag!") - end - )"; - - const auto script = m_logicEngine.createLuaScript(scriptWithError); - EXPECT_FALSE(m_logicEngine.update()); - - EXPECT_TRUE(script->m_impl.isDirty()); - } - - // This is a bit of a special case, but an important one. If script A provides a value for script B and script A has an error - // AFTER it set a new value to B, then script B will be dirty (and not executed!) until the error in script A was fixed - TEST_F(ALogicEngine_Dirtiness, KeepsDirtynessStateOfDependentScript_UntilErrorInSourceScriptIsFixed) - { - const std::string_view scriptWithFixableError = R"( - function interface(IN,OUT) - IN.triggerError = Type:Bool() - IN.data = Type:Int32() - OUT.data = Type:Int32() - end - function run(IN,OUT) - OUT.data = IN.data - if IN.triggerError then - error("Snag!") - end - end - )"; - - LuaScript* script1 = m_logicEngine.createLuaScript(scriptWithFixableError); - LuaScript* script2 = m_logicEngine.createLuaScript(m_minimal_script); - - // No error -> have normal run -> nothing is dirty - script1->getInputs()->getChild("triggerError")->set(false); - m_logicEngine.link(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); - m_logicEngine.update(); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - - // Trigger error -> keep in dirty state - script1->getInputs()->getChild("triggerError")->set(true); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_TRUE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - // "fix" the error and set a value -> expect nothing is dirty and value was propagated - script1->getInputs()->getChild("triggerError")->set(false); - script1->getInputs()->getChild("data")->set(15); - - m_logicEngine.update(); - EXPECT_FALSE(script1->m_impl.isDirty()); - EXPECT_FALSE(script2->m_impl.isDirty()); - EXPECT_EQ(15, *script2->getOutputs()->getChild("data")->get()); - } - - class ALogicEngine_BindingDirtiness : public ALogicEngine_Dirtiness - { - protected: - const std::string_view m_bindningDataScript = R"( - function interface(IN,OUT) - OUT.vec3f = Type:Vec3f() - end - function run(IN,OUT) - OUT.vec3f = {1, 2, 3} - end - )"; - }; - - TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterConstruction) - { - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterCreatingScript) - { - m_logicEngine.createLuaScript(m_valid_empty_script); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterCreatingNodeBinding) - { - m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterCreatingAppearanceBinding) - { - m_logicEngine.createRamsesAppearanceBinding(*m_appearance, ""); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenSettingBindingInputToDefaultValue) - { - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - m_logicEngine.update(); - - // zeroes is the default value - binding->getInputs()->getChild("translation")->set({ 0, 0, 0 }); - EXPECT_TRUE(m_apiObjects.bindingsDirty()); - m_logicEngine.update(); - - // Set different value, and then set again - binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); - m_logicEngine.update(); - binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); - EXPECT_TRUE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenSettingBindingInputToDifferentValue) - { - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - m_logicEngine.update(); - - // Set non-default value, and then set again to different value - binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); - m_logicEngine.update(); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - binding->getInputs()->getChild("translation")->set({ 11, 12, 13 }); - EXPECT_TRUE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenAddingLink) - { - LuaScript* script = m_logicEngine.createLuaScript(m_bindningDataScript); - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - m_logicEngine.update(); - - m_logicEngine.link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); - EXPECT_TRUE(m_apiObjects.bindingsDirty()); - - // After update - not dirty - m_logicEngine.update(); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, NotDirty_WhenRemovingLink) - { - LuaScript* script = m_logicEngine.createLuaScript(m_bindningDataScript); - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - m_logicEngine.link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); - m_logicEngine.update(); - - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(binding->m_impl.isDirty()); - m_logicEngine.unlink(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); - - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(binding->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(binding->m_impl.isDirty()); - } - - // Special case, but worth testing as we want that bindings are always - // executed when adding link, even if the link was just "re-added" - TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenReAddingLink) - { - LuaScript* script = m_logicEngine.createLuaScript(m_bindningDataScript); - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation"))); - m_logicEngine.update(); - - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(binding->m_impl.isDirty()); - m_logicEngine.unlink(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); - m_logicEngine.link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); - - EXPECT_TRUE(script->m_impl.isDirty()); - EXPECT_TRUE(binding->m_impl.isDirty()); - m_logicEngine.update(); - EXPECT_FALSE(script->m_impl.isDirty()); - EXPECT_FALSE(binding->m_impl.isDirty()); - } - - TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenSettingDataToNestedAppearanceBindingInputs) - { - // Vertex shader with array -> results in nested binding inputs - const std::string_view vertShader_array = R"( - #version 300 es - - uniform highp vec4 vec4Array[2]; - - void main() - { - gl_Position = vec4Array[1]; - })"; - - const std::string_view fragShader_trivial = R"( - #version 300 es - - out lowp vec4 color; - void main(void) - { - color = vec4(1.0, 0.0, 0.0, 1.0); - })"; - - RamsesAppearanceBinding* binding = m_logicEngine.createRamsesAppearanceBinding(RamsesTestSetup::CreateTestAppearance(*m_scene, vertShader_array, fragShader_trivial), ""); - - m_logicEngine.update(); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - - EXPECT_TRUE(binding->getInputs()->getChild("vec4Array")->getChild(0)->set({ .1f, .2f, .3f, .4f })); - EXPECT_TRUE(m_apiObjects.bindingsDirty()); - - m_logicEngine.update(); - EXPECT_FALSE(m_apiObjects.bindingsDirty()); - } -} - diff --git a/client/logic/unittests/api/LogicEngineTest_ErrorHandling.cpp b/client/logic/unittests/api/LogicEngineTest_ErrorHandling.cpp deleted file mode 100644 index bffa67e98..000000000 --- a/client/logic/unittests/api/LogicEngineTest_ErrorHandling.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" - -#include "WithTempDirectory.h" - -namespace ramses -{ - class ALogicEngine_ErrorHandling : public ALogicEngine - { - protected: - const std::string_view m_linkable_script = R"( - function interface(IN,OUT) - IN.input = Type:Bool() - OUT.output = Type:Bool() - end - function run(IN,OUT) - end - )"; - }; - - TEST_F(ALogicEngine_ErrorHandling, ClearsErrorsOnCreateNewLuaScript) - { - auto script = m_logicEngine.createLuaScript("somefile.txt"); - ASSERT_EQ(nullptr, script); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); - - script = m_logicEngine.createLuaScript(m_valid_empty_script); - ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - } - - TEST_F(ALogicEngine_ErrorHandling, ReturnsOnFirstError) - { - auto script = m_logicEngine.createLuaScript(m_invalid_empty_script); - ASSERT_EQ(nullptr, script); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - } - - TEST_F(ALogicEngine_ErrorHandling, ClearsErrorsOnUpdate) - { - auto script = m_logicEngine.createLuaScript(m_invalid_empty_script); - ASSERT_EQ(nullptr, script); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); - } - - TEST_F(ALogicEngine_ErrorHandling, ClearsErrorsOnCreateNewRamsesNodeBinding) - { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; - auto ramsesNodeBinding = otherLogicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - - ASSERT_FALSE(m_logicEngine.destroy(*ramsesNodeBinding)); - - EXPECT_FALSE(m_logicEngine.getErrors().empty()); - auto anotherNodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_NE(nullptr, anotherNodeBinding); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - } - - TEST_F(ALogicEngine_ErrorHandling, ClearsErrorsOnSaveAndLoadFromFile) - { - WithTempDirectory tempFolder; - - m_logicEngine.createLuaScript(m_valid_empty_script); - - // Generate error, so that we can test it's cleared by saveToFile() - m_logicEngine.createLuaScript(m_invalid_empty_script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "logic.bin")); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); - - // Generate error, so that we can test it's cleared by loadFromFile() - m_logicEngine.createLuaScript(m_invalid_empty_script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - - EXPECT_TRUE(m_logicEngine.loadFromFile("logic.bin")); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); - } - - TEST_F(ALogicEngine_ErrorHandling, ClearsErrorsOnLinkAndUnlink) - { - LuaScript* script1 = m_logicEngine.createLuaScript(m_linkable_script); - LuaScript* script2 = m_logicEngine.createLuaScript(m_linkable_script); - - // Generate error, so that we can test it's cleared by link() - m_logicEngine.createLuaScript(m_invalid_empty_script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - - EXPECT_TRUE(m_logicEngine.link(*script1->getOutputs()->getChild("output"), *script2->getInputs()->getChild("input"))); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); - - // Generate error, so that we can test it's cleared by unlink() - m_logicEngine.createLuaScript(m_invalid_empty_script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - - EXPECT_TRUE(m_logicEngine.unlink(*script1->getOutputs()->getChild("output"), *script2->getInputs()->getChild("input"))); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); - } -} - diff --git a/client/logic/unittests/api/LogicEngineTest_Factory.cpp b/client/logic/unittests/api/LogicEngineTest_Factory.cpp deleted file mode 100644 index e7b303a95..000000000 --- a/client/logic/unittests/api/LogicEngineTest_Factory.cpp +++ /dev/null @@ -1,683 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/AnimationNodeConfig.h" - -#include "ramses-client-api/DataObject.h" - -#include "impl/LogicNodeImpl.h" - -#include "FeatureLevelTestValues.h" -#include "WithTempDirectory.h" - -#include -#include - -namespace ramses -{ - // There are more specific "create/destroy" tests in ApiObjects unit tests! - class ALogicEngine_Factory : public ALogicEngineBase, public ::testing::TestWithParam - { - public: - ALogicEngine_Factory() : ALogicEngineBase{ GetParam() } - { - } - - protected: - WithTempDirectory tempFolder; - }; - - INSTANTIATE_TEST_SUITE_P( - ALogicEngine_FactoryTests, - ALogicEngine_Factory, - ramses::internal::GetFeatureLevelTestValues()); - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingEmptyScript) - { - const LuaScript* script = m_logicEngine.createLuaScript(""); - ASSERT_EQ(nullptr, script); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); - } - - TEST_P(ALogicEngine_Factory, CreatesScriptFromValidLuaWithoutErrors) - { - const LuaScript* script = m_logicEngine.createLuaScript(m_valid_empty_script); - ASSERT_TRUE(nullptr != script); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - } - - TEST_P(ALogicEngine_Factory, DestroysScriptWithoutErrors) - { - LuaScript* script = m_logicEngine.createLuaScript(m_valid_empty_script); - ASSERT_TRUE(script); - ASSERT_TRUE(m_logicEngine.destroy(*script)); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingScriptFromAnotherEngineInstance) - { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; - auto script = otherLogicEngine.createLuaScript(m_valid_empty_script); - ASSERT_TRUE(script); - ASSERT_FALSE(m_logicEngine.destroy(*script)); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to destroy object ' [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, CreatesLuaModule) - { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); - ASSERT_NE(nullptr, module); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - EXPECT_EQ(module, m_logicEngine.findByName("mymodule")); - ASSERT_EQ(1u, m_logicEngine.getCollection().size()); - EXPECT_EQ(module, *m_logicEngine.getCollection().cbegin()); - - const auto& constLogicEngine = m_logicEngine; - EXPECT_EQ(module, constLogicEngine.findByName("mymodule")); - } - - TEST_P(ALogicEngine_Factory, AllowsCreatingLuaModuleWithEmptyName) - { - EXPECT_NE(nullptr, m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - } - - TEST_P(ALogicEngine_Factory, AllowsCreatingLuaModuleWithNameContainingNonAlphanumericChars) - { - EXPECT_NE(nullptr, m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "!@#$")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - } - - TEST_P(ALogicEngine_Factory, AllowsCreatingLuaModuleWithDupliciteNameEvenIfSourceDiffers) - { - ASSERT_TRUE(m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule")); - // same name and same source is OK - EXPECT_TRUE(m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule")); - - // same name and different source is also OK - EXPECT_TRUE(m_logicEngine.createLuaModule("return {}", {}, "mymodule")); - } - - TEST_P(ALogicEngine_Factory, CanDestroyLuaModule) - { - LuaModule* module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); - ASSERT_NE(nullptr, module); - EXPECT_TRUE(m_logicEngine.destroy(*module)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - EXPECT_FALSE(m_logicEngine.findByName("mymodule")); - } - - TEST_P(ALogicEngine_Factory, FailsToDestroyLuaModuleIfFromOtherLogicInstance) - { - LogicEngine otherLogic{ m_logicEngine.getFeatureLevel() }; - LuaModule* module = otherLogic.createLuaModule(m_moduleSourceCode); - ASSERT_NE(nullptr, module); - - EXPECT_FALSE(m_logicEngine.destroy(*module)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, "Failed to destroy object ' [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, FailsToDestroyLuaModuleIfUsedInLuaScript) - { - LuaModule* module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); - ASSERT_NE(nullptr, module); - - constexpr std::string_view valid_empty_script = R"( - modules("mymodule") - function interface(IN,OUT) - end - function run(IN,OUT) - end - )"; - EXPECT_TRUE(m_logicEngine.createLuaScript(valid_empty_script, CreateDeps({ { "mymodule", module } }), "script")); - - EXPECT_FALSE(m_logicEngine.destroy(*module)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, "Failed to destroy LuaModule 'mymodule', it is used in LuaScript 'script'"); - } - - TEST_P(ALogicEngine_Factory, CanDestroyModuleAfterItIsNotUsedAnymore) - { - LuaModule* module = m_logicEngine.createLuaModule(m_moduleSourceCode); - ASSERT_NE(nullptr, module); - - constexpr std::string_view valid_empty_script = R"( - modules("mymodule") - function interface(IN,OUT) - end - function run(IN,OUT) - end - )"; - auto script = m_logicEngine.createLuaScript(valid_empty_script, CreateDeps({ { "mymodule", module } })); - ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.destroy(*module)); - - EXPECT_TRUE(m_logicEngine.destroy(*script)); - EXPECT_TRUE(m_logicEngine.destroy(*module)); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingLuaScriptUsingModuleFromAnotherLogicInstance) - { - LogicEngine other{ m_logicEngine.getFeatureLevel() }; - const auto module = other.createLuaModule(m_moduleSourceCode); - ASSERT_NE(nullptr, module); - - EXPECT_EQ(nullptr, m_logicEngine.createLuaScript(m_valid_empty_script, CreateDeps({ { "name", module } }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, - "Failed to map Lua module 'name'! It was created on a different instance of LogicEngine."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingLuaModuleUsingModuleFromAnotherLogicInstance) - { - LogicEngine other{ m_logicEngine.getFeatureLevel() }; - const auto module = other.createLuaModule(m_moduleSourceCode); - ASSERT_NE(nullptr, module); - - LuaConfig config; - config.addDependency("name", *module); - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule(m_valid_empty_script, config)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, - "Failed to map Lua module 'name'! It was created on a different instance of LogicEngine."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRamsesNodeBindingFromAnotherEngineInstance) - { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; - - auto ramsesNodeBinding = otherLogicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - ASSERT_TRUE(ramsesNodeBinding); - ASSERT_FALSE(m_logicEngine.destroy(*ramsesNodeBinding)); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to destroy object 'NodeBinding [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRamsesAppearanceBindingFromAnotherEngineInstance) - { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; - auto binding = otherLogicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - ASSERT_TRUE(binding); - ASSERT_FALSE(m_logicEngine.destroy(*binding)); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to destroy object 'AppearanceBinding [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, DestroysRamsesCameraBindingWithoutErrors) - { - auto binding = m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - ASSERT_TRUE(binding); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRamsesCameraBindingFromAnotherEngineInstance) - { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; - auto binding = otherLogicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - ASSERT_TRUE(binding); - ASSERT_FALSE(m_logicEngine.destroy(*binding)); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to destroy object 'CameraBinding [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, DestroysRamsesRenderPassBindingWithoutErrors) - { - auto binding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rp"); - ASSERT_TRUE(binding); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRamsesRenderPassBindingFromAnotherEngineInstance) - { - LogicEngine otherLogicEngine{ GetParam() }; - auto binding = otherLogicEngine.createRamsesRenderPassBinding(*m_renderPass, "rp"); - ASSERT_TRUE(binding); - ASSERT_FALSE(m_logicEngine.destroy(*binding)); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to destroy object 'rp [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, DestroysRamsesRenderGroupBindingWithoutErrors) - { - auto binding = createRenderGroupBinding(); - ASSERT_TRUE(binding); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRamsesRenderGroupBindingFromAnotherEngineInstance) - { - auto binding = createRenderGroupBinding(); - ASSERT_TRUE(binding); - - LogicEngine otherLogicEngine{ GetParam() }; - ASSERT_FALSE(otherLogicEngine.destroy(*binding)); - EXPECT_EQ(otherLogicEngine.getErrors().size(), 1u); - EXPECT_EQ(otherLogicEngine.getErrors()[0].message, "Failed to destroy object 'renderGroupBinding [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, DestroysRamsesMeshNodeBindingWithoutErrors) - { - auto binding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode); - ASSERT_TRUE(binding); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRamsesMeshNodeBindingFromAnotherEngineInstance) - { - auto binding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode); - ASSERT_TRUE(binding); - - LogicEngine otherLogicEngine{ GetParam() }; - ASSERT_FALSE(otherLogicEngine.destroy(*binding)); - EXPECT_EQ(otherLogicEngine.getErrors().size(), 1u); - EXPECT_EQ(otherLogicEngine.getErrors()[0].message, "Failed to destroy object ' [Id=1]', cannot find it in this LogicEngine instance."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingAnchorPointAndNodeOrCameraFromAnotherInstance) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera); - - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; - const auto nodeBindingOther = otherEngine.createRamsesNodeBinding(*m_node); - const auto cameraBindingOther = otherEngine.createRamsesCameraBinding(*m_camera); - - EXPECT_EQ(nullptr, m_logicEngine.createAnchorPoint(*nodeBindingOther, *cameraBinding, "anchor")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to create AnchorPoint 'anchor': provided Ramses node binding and/or camera binding were not found in this logic instance."); - - EXPECT_EQ(nullptr, m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBindingOther, "anchor")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to create AnchorPoint 'anchor': provided Ramses node binding and/or camera binding were not found in this logic instance."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndNodeOrAppearanceFromAnotherInstance) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance); - - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; - const auto nodeBindingOther = otherEngine.createRamsesNodeBinding(*m_node); - const auto appearanceBindingOther = otherEngine.createRamsesAppearanceBinding(*m_appearance); - - EXPECT_EQ(nullptr, createSkinBinding(*nodeBindingOther, *appearanceBinding, m_logicEngine)); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to create SkinBinding 'skin': one or more of the provided Ramses node bindings was not found in this logic instance."); - - EXPECT_EQ(nullptr, createSkinBinding(*nodeBinding, *appearanceBindingOther, m_logicEngine)); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to create SkinBinding 'skin': provided Ramses appearance binding was not found in this logic instance."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndNodesEmptyOrNull) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance); - ramses::UniformInput uniform; - m_appearance->getEffect().findUniformInput("jointMat", uniform); - EXPECT_TRUE(uniform.isValid()); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({}, {}, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, no or null joint node bindings provided."); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding, nullptr }, { matrix44f{ 0.f }, matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, no or null joint node bindings provided."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndNodesCountDifferentFromMatricesCount) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance); - ramses::UniformInput uniform; - m_appearance->getEffect().findUniformInput("jointMat", uniform); - EXPECT_TRUE(uniform.isValid()); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding }, { matrix44f{ 0.f }, matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, number of inverse matrices must match the number of joints."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformInvalidOrFromAnotherEffect) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance); - - // invalid uniform - ramses::UniformInput uniform; - EXPECT_FALSE(uniform.isValid()); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound."); - - // valid uniform but from other effect - const std::string_view vertShader = R"( - #version 100 - uniform highp float someUniform; - attribute vec3 a_position; - void main() - { - gl_Position = someUniform * vec4(a_position, 1.0); - })"; - const std::string_view fragShader = R"( - #version 100 - void main(void) - { - gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); - })"; - ramses::EffectDescription effectDesc; - effectDesc.setVertexShader(vertShader.data()); - effectDesc.setFragmentShader(fragShader.data()); - const auto otherEffect = m_scene->createEffect(effectDesc); - otherEffect->findUniformInput("someUniform", uniform); - EXPECT_TRUE(uniform.isValid()); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformIsBoundInRamses) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance); - - ramses::UniformInput uniform; - m_appearance->getEffect().findUniformInput("floatUniform", uniform); - EXPECT_TRUE(uniform.isValid()); - EXPECT_EQ(ramses::StatusOK, m_appearance->bindInput(uniform, *m_scene->createDataObject(ramses::EDataType::Float))); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformDataTypeWrong) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance); - - ramses::UniformInput uniform; - m_appearance->getEffect().findUniformInput("floatUniform", uniform); - EXPECT_TRUE(uniform.isValid()); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, provided uniform input must be of type array of Matrix4x4 with element count matching number of joints."); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformPointsToArrayWithMismatchedSize) - { - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); - - const std::string_view vertShader = R"( - #version 100 - uniform highp mat4 someArray[3]; - attribute vec3 a_position; - void main() - { - gl_Position = someArray[0] * vec4(a_position, 1.0); - })"; - const std::string_view fragShader = R"( - #version 100 - void main(void) - { - gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); - })"; - ramses::EffectDescription effectDesc; - effectDesc.setVertexShader(vertShader.data()); - effectDesc.setFragmentShader(fragShader.data()); - const auto otherEffect = m_scene->createEffect(effectDesc); - ramses::UniformInput uniform; - otherEffect->findUniformInput("someArray", uniform); - EXPECT_TRUE(uniform.isValid()); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_scene->createAppearance(*otherEffect)); - - EXPECT_FALSE(m_logicEngine.createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, uniform, "skin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create SkinBinding, provided uniform input must be of type array of Matrix4x4 with element count matching number of joints."); - } - - TEST_P(ALogicEngine_Factory, RenamesObjectsAfterCreation) - { - auto script = m_logicEngine.createLuaScript(m_valid_empty_script); - auto ramsesNodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - auto ramsesAppearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - auto ramsesCameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - - script->setName("same name twice"); - ramsesNodeBinding->setName("same name twice"); - ramsesAppearanceBinding->setName(""); - ramsesCameraBinding->setName(""); - - EXPECT_EQ("same name twice", script->getName()); - EXPECT_EQ("same name twice", ramsesNodeBinding->getName()); - EXPECT_EQ("", ramsesAppearanceBinding->getName()); - EXPECT_EQ("", ramsesCameraBinding->getName()); - - auto ramsesRenderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rp"); - ramsesRenderPassBinding->setName(""); - EXPECT_EQ("", ramsesRenderPassBinding->getName()); - - auto ramsesRenderGroupBinding = createRenderGroupBinding(); - ramsesRenderGroupBinding->setName(""); - EXPECT_EQ("", ramsesRenderGroupBinding->getName()); - } - - TEST_P(ALogicEngine_Factory, CanCastObjectsToValidTypes) - { - LogicObject* luaModule = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - LogicObject* luaScript = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - LogicObject* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - LogicObject* appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - LogicObject* cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - LogicObject* dataArray = m_logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray->as(), dataArray->as(), EInterpolationType::Linear }); - LogicObject* animNode = m_logicEngine.createAnimationNode(config, "animNode"); - LogicObject* renderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rp"); - LogicObject* renderGroupBinding = createRenderGroupBinding(); - LogicObject* skin = createSkinBinding(m_logicEngine); - LogicObject* meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode); - - EXPECT_TRUE(luaModule->as()); - EXPECT_TRUE(luaScript->as()); - EXPECT_TRUE(nodeBinding->as()); - EXPECT_TRUE(appearanceBinding->as()); - EXPECT_TRUE(cameraBinding->as()); - EXPECT_TRUE(dataArray->as()); - EXPECT_TRUE(animNode->as()); - EXPECT_TRUE(renderPassBinding->as()); - EXPECT_TRUE(renderGroupBinding->as()); - EXPECT_TRUE(skin->as()); - EXPECT_TRUE(meshBinding->as()); - - EXPECT_FALSE(luaModule->as()); - EXPECT_FALSE(luaScript->as()); - EXPECT_FALSE(nodeBinding->as()); - EXPECT_FALSE(appearanceBinding->as()); - EXPECT_FALSE(cameraBinding->as()); - EXPECT_FALSE(dataArray->as()); - EXPECT_FALSE(animNode->as()); - EXPECT_FALSE(renderPassBinding->as()); - EXPECT_FALSE(renderGroupBinding->as()); - EXPECT_FALSE(skin->as()); - EXPECT_FALSE(meshBinding->as()); - - //cast obj -> node -> binding -> appearanceBinding - auto* nodeCastFromObject = appearanceBinding->as(); - EXPECT_TRUE(nodeCastFromObject); - auto* bindingCastFromNode = nodeCastFromObject->as(); - EXPECT_TRUE(bindingCastFromNode); - auto* appearanceBindingCastFromBinding = bindingCastFromNode->as(); - EXPECT_TRUE(appearanceBindingCastFromBinding); - - //cast appearanceBinding -> binding -> node -> obj - EXPECT_TRUE(appearanceBindingCastFromBinding->as()); - EXPECT_TRUE(bindingCastFromNode->as()); - EXPECT_TRUE(nodeCastFromObject->as()); - - //cast obj -> node -> animationnode - auto* anNodeCastFromObject = animNode->as(); - EXPECT_TRUE(anNodeCastFromObject); - auto* animationCastFromNode = anNodeCastFromObject->as(); - EXPECT_TRUE(animationCastFromNode); - - //cast animationnode -> node -> obj - EXPECT_TRUE(animationCastFromNode->as()); - EXPECT_TRUE(anNodeCastFromObject->as()); - } - - TEST_P(ALogicEngine_Factory, CanCastObjectsToValidTypes_Const) - { - m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const LogicObject* dataArray = m_logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray->as(), dataArray->as(), EInterpolationType::Linear }); - m_logicEngine.createAnimationNode(config, "animNode"); - m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - createRenderGroupBinding(); - createSkinBinding(m_logicEngine); - m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "meshBinding"); - - const auto& immutableLogicEngine = m_logicEngine; - const auto* luaModuleConst = immutableLogicEngine.findByName("luaModule"); - const auto* luaScriptConst = immutableLogicEngine.findByName("script"); - const auto* nodeBindingConst = immutableLogicEngine.findByName("nodebinding"); - const auto* appearanceBindingConst = immutableLogicEngine.findByName("appbinding"); - const auto* cameraBindingConst = immutableLogicEngine.findByName("camerabinding"); - const auto* dataArrayConst = immutableLogicEngine.findByName("dataarray"); - const auto* animNodeConst = immutableLogicEngine.findByName("animNode"); - const auto* renderPassBindingConst = immutableLogicEngine.findByName("renderPass"); - const auto* renderGroupBindingConst = immutableLogicEngine.findByName("renderGroupBinding"); - const auto* skinConst = immutableLogicEngine.findByName("skin"); - const auto* meshBindingConst = immutableLogicEngine.findByName("meshBinding"); - - EXPECT_TRUE(luaModuleConst->as()); - EXPECT_TRUE(luaScriptConst->as()); - EXPECT_TRUE(nodeBindingConst->as()); - EXPECT_TRUE(appearanceBindingConst->as()); - EXPECT_TRUE(cameraBindingConst->as()); - EXPECT_TRUE(dataArrayConst->as()); - EXPECT_TRUE(animNodeConst->as()); - EXPECT_TRUE(renderPassBindingConst->as()); - EXPECT_TRUE(renderGroupBindingConst->as()); - EXPECT_TRUE(skinConst->as()); - EXPECT_TRUE(meshBindingConst->as()); - - EXPECT_FALSE(luaModuleConst->as()); - EXPECT_FALSE(luaScriptConst->as()); - EXPECT_FALSE(nodeBindingConst->as()); - EXPECT_FALSE(appearanceBindingConst->as()); - EXPECT_FALSE(cameraBindingConst->as()); - EXPECT_FALSE(dataArrayConst->as()); - EXPECT_FALSE(animNodeConst->as()); - EXPECT_FALSE(renderPassBindingConst->as()); - EXPECT_FALSE(renderGroupBindingConst->as()); - EXPECT_FALSE(skinConst->as()); - EXPECT_FALSE(meshBindingConst->as()); - - // cast obj -> node -> binding -> appearanceBinding - const auto* nodeCastFromObject = appearanceBindingConst->as(); - EXPECT_TRUE(nodeCastFromObject); - const auto* bindingCastFromNode = nodeCastFromObject->as(); - EXPECT_TRUE(bindingCastFromNode); - const auto* appearanceBindingCastFromBinding = bindingCastFromNode->as(); - EXPECT_TRUE(appearanceBindingCastFromBinding); - - // cast appearanceBinding -> binding -> node -> obj - EXPECT_TRUE(appearanceBindingCastFromBinding->as()); - EXPECT_TRUE(bindingCastFromNode->as()); - EXPECT_TRUE(nodeCastFromObject->as()); - - // cast obj -> node -> animationnode - const auto* anNodeCastFromObject = animNodeConst->as(); - EXPECT_TRUE(anNodeCastFromObject); - const auto* animationCastFromNode = anNodeCastFromObject->as(); - EXPECT_TRUE(animationCastFromNode); - - // cast animationnode -> node -> obj - EXPECT_TRUE(animationCastFromNode->as()); - EXPECT_TRUE(anNodeCastFromObject->as()); - } - - TEST_P(ALogicEngine_Factory, ProducesErrorIfWrongObjectTypeIsDestroyed) - { - struct UnknownObjectImpl: internal::LogicNodeImpl - { - UnknownObjectImpl() - : LogicNodeImpl("name", 1u) - { - } - - std::optional update() override { return std::nullopt; } - void createRootProperties() final {} - }; - - struct UnknownObject : LogicNode - { - explicit UnknownObject(std::unique_ptr impl) - : LogicNode(std::move(impl)) - { - } - }; - - UnknownObject unknownObject(std::make_unique()); - EXPECT_FALSE(m_logicEngine.destroy(unknownObject)); - const auto& errors = m_logicEngine.getErrors(); - EXPECT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "Tried to destroy object 'name' with unknown type"); - } - - TEST_P(ALogicEngine_Factory, CanBeMoved) - { - LuaScript* script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "Script"); - RamsesNodeBinding* ramsesNodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - RamsesAppearanceBinding* appBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - RamsesCameraBinding* camBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - const RamsesRenderPassBinding* rpBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "RenderPass"); - const RamsesRenderGroupBinding* rgBinding = createRenderGroupBinding(); - - LogicEngine movedLogicEngine(std::move(m_logicEngine)); - EXPECT_EQ(script, movedLogicEngine.findByName("Script")); - EXPECT_EQ(ramsesNodeBinding, movedLogicEngine.findByName("NodeBinding")); - EXPECT_EQ(appBinding, movedLogicEngine.findByName("AppearanceBinding")); - EXPECT_EQ(camBinding, movedLogicEngine.findByName("CameraBinding")); - EXPECT_EQ(rpBinding, movedLogicEngine.findByName("RenderPass")); - EXPECT_EQ(rgBinding, movedLogicEngine.findByName("renderGroupBinding")); - - movedLogicEngine.update(); - - LogicEngine moveAssignedLogicEngine{ GetParam() }; - moveAssignedLogicEngine = std::move(movedLogicEngine); - - EXPECT_EQ(script, moveAssignedLogicEngine.findByName("Script")); - EXPECT_EQ(ramsesNodeBinding, moveAssignedLogicEngine.findByName("NodeBinding")); - EXPECT_EQ(appBinding, moveAssignedLogicEngine.findByName("AppearanceBinding")); - EXPECT_EQ(camBinding, moveAssignedLogicEngine.findByName("CameraBinding")); - EXPECT_EQ(rpBinding, moveAssignedLogicEngine.findByName("RenderPass")); - EXPECT_EQ(rgBinding, moveAssignedLogicEngine.findByName("renderGroupBinding")); - - moveAssignedLogicEngine.update(); - } -} diff --git a/client/logic/unittests/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp b/client/logic/unittests/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp deleted file mode 100644 index 22a1e01fa..000000000 --- a/client/logic/unittests/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gtest/gtest.h" -#include "LogicEngineTest_Base.h" -#include "ramses-logic/Property.h" - -#include "LogTestUtils.h" - -namespace ramses -{ - class ALogicEngine_LogicObjectStatistics : public ALogicEngine - { - protected: - std::vector m_logTypes; - std::vector m_logMessages; - ScopedLogContextLevel m_logCollector{ELogLevel::Debug, [this](ELogLevel type, std::string_view message) - { - m_logTypes.emplace_back(type); - m_logMessages.emplace_back(message); - }}; - }; - - TEST_F(ALogicEngine_LogicObjectStatistics, LogsAllMessages) - { - m_logicEngine.setStatisticsLoggingRate(2u); - - constexpr auto scriptSource = R"( - function interface(IN,OUT) - IN.param = Type:Int32() - OUT.param = Type:Int32() - end - function run(IN,OUT) - OUT.param = IN.param - end - )"; - - auto node1 = m_logicEngine.createLuaScript(scriptSource); - auto node2 = m_logicEngine.createLuaScript(scriptSource); - auto node3 = m_logicEngine.createLuaScript(scriptSource); - auto node4 = m_logicEngine.createLuaScript(scriptSource); - auto node5 = m_logicEngine.createLuaScript(scriptSource); - - m_logicEngine.link(*node1->getOutputs()->getChild(0u), *node2->getInputs()->getChild(0u)); - m_logicEngine.link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); - m_logicEngine.link(*node3->getOutputs()->getChild(0u), *node4->getInputs()->getChild(0u)); - m_logicEngine.link(*node4->getOutputs()->getChild(0u), *node5->getInputs()->getChild(0u)); - - for (int32_t i = 0; i < 4; ++i) - { - node1->getInputs()->getChild(0u)->set(i); - EXPECT_TRUE(m_logicEngine.update()); - } - - //5 log lines per frame and 2 frames - EXPECT_EQ(10u, m_logMessages.size()); - - EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); - EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[2].find("Time between Update calls (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[5].find("Time since last log:") != std::string::npos); - EXPECT_TRUE(m_logMessages[6].find("Update Execution time (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[7].find("Time between Update calls (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[8].find("Nodes Executed (min/max/avg):") != std::string::npos); - EXPECT_TRUE(m_logMessages[9].find("Activated links (min/max/avg):") != std::string::npos); - } - - TEST_F(ALogicEngine_LogicObjectStatistics, NoLogsWhenLoggingRateZero) - { - m_logicEngine.setStatisticsLoggingRate(0u); - - for (int32_t i = 0; i < 4; ++i) - { - EXPECT_TRUE(m_logicEngine.update()); - } - - EXPECT_EQ(0u, m_logMessages.size()); - } - - TEST_F(ALogicEngine_LogicObjectStatistics, OnlyLogWhenLogLevelSmallerOrEqualToLogVerbosityLimit) - { - m_logicEngine.setStatisticsLoggingRate(2u); - m_logicEngine.setStatisticsLogLevel(ELogLevel::Trace); - - for (int32_t i = 0; i < 4; ++i) - { - EXPECT_TRUE(m_logicEngine.update()); - } - EXPECT_TRUE(m_logMessages.empty()); - - m_logicEngine.setStatisticsLogLevel(ELogLevel::Info); - for (int32_t i = 0; i < 4; ++i) - { - EXPECT_TRUE(m_logicEngine.update()); - } - EXPECT_FALSE(m_logMessages.empty()); - - m_logMessages.clear(); - EXPECT_TRUE(m_logMessages.empty()); - - m_logicEngine.setStatisticsLogLevel(ELogLevel::Warn); - for (int32_t i = 0; i < 4; ++i) - { - EXPECT_TRUE(m_logicEngine.update()); - } - EXPECT_FALSE(m_logMessages.empty()); - } - - TEST_F(ALogicEngine_LogicObjectStatistics, LogsAccordingToLoggingRate) - { - m_logicEngine.setStatisticsLoggingRate(2u); - - m_logicEngine.update(); - EXPECT_TRUE(m_logMessages.empty()); - - m_logicEngine.update(); - EXPECT_EQ(5u ,m_logMessages.size()); - - m_logicEngine.update(); - EXPECT_EQ(5u, m_logMessages.size()); - - m_logicEngine.update(); - EXPECT_EQ(10u, m_logMessages.size()); - } - - TEST_F(ALogicEngine_LogicObjectStatistics, NoTimeBetweenUpdateCallsCalculationWithLoggingRateOne) - { - m_logicEngine.setStatisticsLoggingRate(1u); - - m_logicEngine.update(); - EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); - - m_logicEngine.update(); - EXPECT_TRUE(m_logMessages[7].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); - } -} diff --git a/client/logic/unittests/api/LogicEngineTest_Lookup.cpp b/client/logic/unittests/api/LogicEngineTest_Lookup.cpp deleted file mode 100644 index 11b9adec7..000000000 --- a/client/logic/unittests/api/LogicEngineTest_Lookup.cpp +++ /dev/null @@ -1,550 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" -#include "ramses-logic/AnimationNodeConfig.h" - -#include "impl/LuaModuleImpl.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LuaInterfaceImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" -#include "impl/RamsesRenderPassBindingImpl.h" -#include "impl/RamsesRenderGroupBindingImpl.h" -#include "impl/RamsesMeshNodeBindingImpl.h" -#include "impl/DataArrayImpl.h" -#include "impl/AnimationNodeImpl.h" -#include "impl/TimerNodeImpl.h" -#include "impl/AnchorPointImpl.h" -#include "impl/SkinBindingImpl.h" - -namespace ramses -{ - class ALogicEngine_Lookup : public ALogicEngine - { - public: - ALogicEngine_Lookup() : ALogicEngine{ ramses::EFeatureLevel_Latest } // test with latest feature level so all possible API objects are available - { - } - - protected: - AnimationNode* createAnimationNode(const DataArray* dataArray) - { - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray, dataArray }); - return m_logicEngine.createAnimationNode(config, "animNode"); - } - }; - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirName) - { - const auto luaModule = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - const auto script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const auto renderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - const auto renderGroupBinding = createRenderGroupBinding(); - const auto meshNodeBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "dataarray"); - const auto animNode = createAnimationNode(dataArray); - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); - const auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - const auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - const auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - EXPECT_EQ(luaModule, m_logicEngine.findByName("luaModule")); - EXPECT_EQ(script, m_logicEngine.findByName("script")); - EXPECT_EQ(nodeBinding, m_logicEngine.findByName("nodebinding")); - EXPECT_EQ(appearanceBinding, m_logicEngine.findByName("appbinding")); - EXPECT_EQ(cameraBinding, m_logicEngine.findByName("camerabinding")); - EXPECT_EQ(renderPassBinding, m_logicEngine.findByName("rpbinding")); - EXPECT_EQ(renderGroupBinding, m_logicEngine.findByName("renderGroupBinding")); - EXPECT_EQ(meshNodeBinding, m_logicEngine.findByName("mb")); - EXPECT_EQ(dataArray, m_logicEngine.findByName("dataarray")); - EXPECT_EQ(animNode, m_logicEngine.findByName("animNode")); - EXPECT_EQ(timerNode, m_logicEngine.findByName("timerNode")); - EXPECT_EQ(intf, m_logicEngine.findByName("intf")); - EXPECT_EQ(anchor, m_logicEngine.findByName("anchor")); - EXPECT_EQ(skin, m_logicEngine.findByName("skin")); - - EXPECT_EQ(luaModule, m_logicEngine.findByName("luaModule")); - EXPECT_EQ(script, m_logicEngine.findByName("script")); - EXPECT_EQ(nodeBinding, m_logicEngine.findByName("nodebinding")); - EXPECT_EQ(appearanceBinding, m_logicEngine.findByName("appbinding")); - EXPECT_EQ(cameraBinding, m_logicEngine.findByName("camerabinding")); - EXPECT_EQ(renderPassBinding, m_logicEngine.findByName("rpbinding")); - EXPECT_EQ(renderGroupBinding, m_logicEngine.findByName("renderGroupBinding")); - EXPECT_EQ(meshNodeBinding, m_logicEngine.findByName("mb")); - EXPECT_EQ(dataArray, m_logicEngine.findByName("dataarray")); - EXPECT_EQ(animNode, m_logicEngine.findByName("animNode")); - EXPECT_EQ(timerNode, m_logicEngine.findByName("timerNode")); - EXPECT_EQ(intf, m_logicEngine.findByName("intf")); - EXPECT_EQ(anchor, m_logicEngine.findByName("anchor")); - EXPECT_EQ(skin, m_logicEngine.findByName("skin")); - - auto it = m_logicEngine.getCollection().cbegin(); - EXPECT_EQ(*it++, luaModule); - EXPECT_EQ(*it++, script); - EXPECT_EQ(*it++, nodeBinding); - EXPECT_EQ(*it++, appearanceBinding); - EXPECT_EQ(*it++, cameraBinding); - EXPECT_EQ(*it++, renderPassBinding); - EXPECT_EQ(*it++, renderGroupBinding); - EXPECT_EQ(*it++, meshNodeBinding); - EXPECT_EQ(*it++, dataArray); - EXPECT_EQ(*it++, animNode); - EXPECT_EQ(*it++, timerNode); - EXPECT_EQ(*it++, intf); - EXPECT_EQ(*it++, anchor); - EXPECT_EQ(*it++, skin); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirName_Const) - { - const auto luaModule = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - const auto script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const auto renderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - const auto renderGroupBinding = createRenderGroupBinding(); - const auto meshNodeBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "dataarray"); - const auto animNode = createAnimationNode(dataArray); - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); - const auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - const auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - const auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - const LogicEngine& immutableLogicEngine = m_logicEngine; - EXPECT_EQ(luaModule, immutableLogicEngine.findByName("luaModule")); - EXPECT_EQ(script, immutableLogicEngine.findByName("script")); - EXPECT_EQ(nodeBinding, immutableLogicEngine.findByName("nodebinding")); - EXPECT_EQ(appearanceBinding, immutableLogicEngine.findByName("appbinding")); - EXPECT_EQ(cameraBinding, immutableLogicEngine.findByName("camerabinding")); - EXPECT_EQ(renderPassBinding, immutableLogicEngine.findByName("rpbinding")); - EXPECT_EQ(renderGroupBinding, immutableLogicEngine.findByName("renderGroupBinding")); - EXPECT_EQ(meshNodeBinding, immutableLogicEngine.findByName("mb")); - EXPECT_EQ(dataArray, immutableLogicEngine.findByName("dataarray")); - EXPECT_EQ(animNode, immutableLogicEngine.findByName("animNode")); - EXPECT_EQ(timerNode, immutableLogicEngine.findByName("timerNode")); - EXPECT_EQ(intf, immutableLogicEngine.findByName("intf")); - EXPECT_EQ(anchor, immutableLogicEngine.findByName("anchor")); - EXPECT_EQ(skin, immutableLogicEngine.findByName("skin")); - - EXPECT_EQ(luaModule, immutableLogicEngine.findByName("luaModule")); - EXPECT_EQ(script, immutableLogicEngine.findByName("script")); - EXPECT_EQ(nodeBinding, immutableLogicEngine.findByName("nodebinding")); - EXPECT_EQ(appearanceBinding, immutableLogicEngine.findByName("appbinding")); - EXPECT_EQ(cameraBinding, immutableLogicEngine.findByName("camerabinding")); - EXPECT_EQ(renderPassBinding, immutableLogicEngine.findByName("rpbinding")); - EXPECT_EQ(renderGroupBinding, immutableLogicEngine.findByName("renderGroupBinding")); - EXPECT_EQ(meshNodeBinding, immutableLogicEngine.findByName("mb")); - EXPECT_EQ(dataArray, immutableLogicEngine.findByName("dataarray")); - EXPECT_EQ(animNode, immutableLogicEngine.findByName("animNode")); - EXPECT_EQ(timerNode, immutableLogicEngine.findByName("timerNode")); - EXPECT_EQ(intf, immutableLogicEngine.findByName("intf")); - EXPECT_EQ(anchor, immutableLogicEngine.findByName("anchor")); - EXPECT_EQ(skin, immutableLogicEngine.findByName("skin")); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirName_CanBeUsedWithRealType) - { - m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - createRenderGroupBinding(); - m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); - createAnimationNode(dataArray); - m_logicEngine.createTimerNode("timerNode"); - m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - const auto* luaModuleFound = m_logicEngine.findByName("luaModule")->as(); - const auto* luaScriptFound = m_logicEngine.findByName("script")->as(); - const auto* nodeBindingFound = m_logicEngine.findByName("nodebinding")->as(); - const auto* appearanceBindingFound = m_logicEngine.findByName("appbinding")->as(); - const auto* cameraBindingFound = m_logicEngine.findByName("camerabinding")->as(); - const auto* renderPassBindingFound = m_logicEngine.findByName("rpbinding")->as(); - const auto* renderGroupBindingFound = m_logicEngine.findByName("renderGroupBinding")->as(); - const auto* meshNodeBindingFound = m_logicEngine.findByName("mb")->as(); - const auto* dataArrayFound = m_logicEngine.findByName("dataarray")->as(); - const auto* animNodeFound = m_logicEngine.findByName("animNode")->as(); - const auto* timerNodeFound = m_logicEngine.findByName("timerNode")->as(); - const auto* intfFound = m_logicEngine.findByName("intf")->as(); - const auto* anchorFound = m_logicEngine.findByName("anchor")->as(); - const auto* skinFound = m_logicEngine.findByName("skin")->as(); - - ASSERT_NE(nullptr, luaModuleFound); - ASSERT_NE(nullptr, luaScriptFound); - ASSERT_NE(nullptr, nodeBindingFound); - ASSERT_NE(nullptr, appearanceBindingFound); - ASSERT_NE(nullptr, cameraBindingFound); - ASSERT_NE(nullptr, renderPassBindingFound); - ASSERT_NE(nullptr, renderGroupBindingFound); - ASSERT_NE(nullptr, meshNodeBindingFound); - ASSERT_NE(nullptr, dataArrayFound); - ASSERT_NE(nullptr, animNodeFound); - ASSERT_NE(nullptr, timerNodeFound); - ASSERT_NE(nullptr, intfFound); - ASSERT_NE(nullptr, anchorFound); - ASSERT_NE(nullptr, skinFound); - - EXPECT_EQ(luaModuleFound->getName(), "luaModule"); - EXPECT_EQ(luaScriptFound->getName(), "script"); - EXPECT_EQ(nodeBindingFound->getName(), "nodebinding"); - EXPECT_EQ(appearanceBindingFound->getName(), "appbinding"); - EXPECT_EQ(cameraBindingFound->getName(), "camerabinding"); - EXPECT_EQ(renderPassBindingFound->getName(), "rpbinding"); - EXPECT_EQ(renderGroupBindingFound->getName(), "renderGroupBinding"); - EXPECT_EQ(meshNodeBindingFound->getName(), "mb"); - EXPECT_EQ(dataArrayFound->getName(), "dataarray"); - EXPECT_EQ(animNodeFound->getName(), "animNode"); - EXPECT_EQ(timerNodeFound->getName(), "timerNode"); - EXPECT_EQ(intfFound->getName(), "intf"); - EXPECT_EQ(anchorFound->getName(), "anchor"); - EXPECT_EQ(skinFound->getName(), "skin"); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirName_CanBeUsedAsRealType_Const) - { - m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - createRenderGroupBinding(); - m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); - createAnimationNode(dataArray); - m_logicEngine.createTimerNode("timerNode"); - m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - const LogicEngine& immutableLogicEngine = m_logicEngine; - const auto* luaModuleFound = immutableLogicEngine.findByName("luaModule")->as(); - const auto* luaScriptFound = immutableLogicEngine.findByName("script")->as(); - const auto* nodeBindingFound = immutableLogicEngine.findByName("nodebinding")->as(); - const auto* appearanceBindingFound = immutableLogicEngine.findByName("appbinding")->as(); - const auto* cameraBindingFound = immutableLogicEngine.findByName("camerabinding")->as(); - const auto* renderPassBindingFound = immutableLogicEngine.findByName("rpbinding")->as(); - const auto* renderGroupBindingFound = immutableLogicEngine.findByName("renderGroupBinding")->as(); - const auto* meshNodeBindingFound = immutableLogicEngine.findByName("mb")->as(); - const auto* dataArrayFound = immutableLogicEngine.findByName("dataarray")->as(); - const auto* animNodeFound = immutableLogicEngine.findByName("animNode")->as(); - const auto* timerNodeFound = immutableLogicEngine.findByName("timerNode")->as(); - const auto* intfFound = immutableLogicEngine.findByName("intf")->as(); - const auto* anchorFound = immutableLogicEngine.findByName("anchor")->as(); - const auto* skinFound = immutableLogicEngine.findByName("skin")->as(); - - ASSERT_NE(nullptr, luaModuleFound); - ASSERT_NE(nullptr, luaScriptFound); - ASSERT_NE(nullptr, nodeBindingFound); - ASSERT_NE(nullptr, appearanceBindingFound); - ASSERT_NE(nullptr, cameraBindingFound); - ASSERT_NE(nullptr, renderPassBindingFound); - ASSERT_NE(nullptr, renderGroupBindingFound); - ASSERT_NE(nullptr, meshNodeBindingFound); - ASSERT_NE(nullptr, dataArrayFound); - ASSERT_NE(nullptr, animNodeFound); - ASSERT_NE(nullptr, timerNodeFound); - ASSERT_NE(nullptr, intfFound); - ASSERT_NE(nullptr, anchorFound); - ASSERT_NE(nullptr, skinFound); - - EXPECT_EQ(luaModuleFound->getName(), "luaModule"); - EXPECT_EQ(luaScriptFound->getName(), "script"); - EXPECT_EQ(nodeBindingFound->getName(), "nodebinding"); - EXPECT_EQ(appearanceBindingFound->getName(), "appbinding"); - EXPECT_EQ(cameraBindingFound->getName(), "camerabinding"); - EXPECT_EQ(renderPassBindingFound->getName(), "rpbinding"); - EXPECT_EQ(renderGroupBindingFound->getName(), "renderGroupBinding"); - EXPECT_EQ(meshNodeBindingFound->getName(), "mb"); - EXPECT_EQ(dataArrayFound->getName(), "dataarray"); - EXPECT_EQ(animNodeFound->getName(), "animNode"); - EXPECT_EQ(timerNodeFound->getName(), "timerNode"); - EXPECT_EQ(intfFound->getName(), "intf"); - EXPECT_EQ(anchorFound->getName(), "anchor"); - EXPECT_EQ(skinFound->getName(), "skin"); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirId) - { - const auto luaModule = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - const auto script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const auto renderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - const auto renderGroupBinding = createRenderGroupBinding(); - const auto meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); - const auto animNode = createAnimationNode(dataArray); - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); - const auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - const auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - const auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - EXPECT_EQ(luaModule, m_logicEngine.findLogicObjectById(1u)); - EXPECT_EQ(script, m_logicEngine.findLogicObjectById(2u)); - EXPECT_EQ(nodeBinding, m_logicEngine.findLogicObjectById(3u)); - EXPECT_EQ(appearanceBinding, m_logicEngine.findLogicObjectById(4u)); - EXPECT_EQ(cameraBinding, m_logicEngine.findLogicObjectById(5u)); - EXPECT_EQ(renderPassBinding, m_logicEngine.findLogicObjectById(6u)); - EXPECT_EQ(renderGroupBinding, m_logicEngine.findLogicObjectById(7u)); - EXPECT_EQ(meshBinding, m_logicEngine.findLogicObjectById(8u)); - EXPECT_EQ(dataArray, m_logicEngine.findLogicObjectById(9u)); - EXPECT_EQ(animNode, m_logicEngine.findLogicObjectById(10u)); - EXPECT_EQ(timerNode, m_logicEngine.findLogicObjectById(11u)); - EXPECT_EQ(intf, m_logicEngine.findLogicObjectById(12u)); - EXPECT_EQ(anchor, m_logicEngine.findLogicObjectById(13u)); - EXPECT_EQ(skin, m_logicEngine.findLogicObjectById(14u)); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirId_Const) - { - const auto luaModule = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - const auto script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const auto renderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - const auto renderGroupBinding = createRenderGroupBinding(); - const auto meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); - const auto animNode = createAnimationNode(dataArray); - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); - const auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - const auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - const auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - const LogicEngine& immutableLogicEngine = m_logicEngine; - EXPECT_EQ(luaModule, immutableLogicEngine.findLogicObjectById(1u)); - EXPECT_EQ(script, immutableLogicEngine.findLogicObjectById(2u)); - EXPECT_EQ(nodeBinding, immutableLogicEngine.findLogicObjectById(3u)); - EXPECT_EQ(appearanceBinding, immutableLogicEngine.findLogicObjectById(4u)); - EXPECT_EQ(cameraBinding, immutableLogicEngine.findLogicObjectById(5u)); - EXPECT_EQ(renderPassBinding, immutableLogicEngine.findLogicObjectById(6u)); - EXPECT_EQ(renderGroupBinding, immutableLogicEngine.findLogicObjectById(7u)); - EXPECT_EQ(meshBinding, immutableLogicEngine.findLogicObjectById(8u)); - EXPECT_EQ(dataArray, immutableLogicEngine.findLogicObjectById(9u)); - EXPECT_EQ(animNode, immutableLogicEngine.findLogicObjectById(10u)); - EXPECT_EQ(timerNode, immutableLogicEngine.findLogicObjectById(11u)); - EXPECT_EQ(intf, immutableLogicEngine.findLogicObjectById(12u)); - EXPECT_EQ(anchor, immutableLogicEngine.findLogicObjectById(13u)); - EXPECT_EQ(skin, immutableLogicEngine.findLogicObjectById(14u)); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsByTheirName_CutsNameAtNullTermination) - { - RamsesAppearanceBinding* appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - EXPECT_EQ(appearanceBinding, m_logicEngine.findByName("appbinding\0withsurprise")); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectsAfterRenaming_ByNewNameOnly) - { - LuaModule* luaModule = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - LuaScript* script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - RamsesNodeBinding* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - RamsesAppearanceBinding* appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - RamsesCameraBinding* cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - RamsesRenderPassBinding* renderPassBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - auto renderGroupBinding = createRenderGroupBinding(); - auto meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "meshbinding"); - auto dataArray = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "dataarray"); - auto animNode = createAnimationNode(dataArray); - auto timerNode = m_logicEngine.createTimerNode("timerNode"); - auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - // Rename - luaModule->setName("L"); - script->setName("S"); - nodeBinding->setName("NB"); - appearanceBinding->setName("AB"); - cameraBinding->setName("CB"); - renderPassBinding->setName("RPB"); - renderGroupBinding->setName("RGB"); - meshBinding->setName("MB"); - dataArray->setName("DA"); - animNode->setName("AN"); - timerNode->setName("TN"); - intf->setName("I"); - anchor->setName("A"); - skin->setName("SB"); - - // Can't find by old name - EXPECT_EQ(nullptr, m_logicEngine.findByName("luaModule")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("script")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("nodebinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("appbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("camerabinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("rpbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("renderGroupBinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("meshbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("dataarray")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("animNode")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("timerNode")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("intf")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("anchor")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("skin")); - - // Found by new name - EXPECT_EQ(luaModule, m_logicEngine.findByName("L")); - EXPECT_EQ(script, m_logicEngine.findByName("S")); - EXPECT_EQ(nodeBinding, m_logicEngine.findByName("NB")); - EXPECT_EQ(appearanceBinding, m_logicEngine.findByName("AB")); - EXPECT_EQ(cameraBinding, m_logicEngine.findByName("CB")); - EXPECT_EQ(renderPassBinding, m_logicEngine.findByName("RPB")); - EXPECT_EQ(renderGroupBinding, m_logicEngine.findByName("RGB")); - EXPECT_EQ(meshBinding, m_logicEngine.findByName("MB")); - EXPECT_EQ(dataArray, m_logicEngine.findByName("DA")); - EXPECT_EQ(animNode, m_logicEngine.findByName("AN")); - EXPECT_EQ(timerNode, m_logicEngine.findByName("TN")); - EXPECT_EQ(intf, m_logicEngine.findByName("I")); - EXPECT_EQ(anchor, m_logicEngine.findByName("A")); - EXPECT_EQ(skin, m_logicEngine.findByName("SB")); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectByNameOnlyIfTypeMatches) - { - m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - createRenderGroupBinding(); - m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "meshbinding"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "dataarray"); - createAnimationNode(dataArray); - m_logicEngine.createTimerNode("timerNode"); - m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - EXPECT_EQ(nullptr, m_logicEngine.findByName("dataarray")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("nodebinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("appbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("camerabinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("animNode")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("script")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("luaModule")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("appbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("meshbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("renderGroupBinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("anchor")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("timerNode")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("intf")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("rpbinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("skin")); - } - - TEST_F(ALogicEngine_Lookup, FindsObjectByNameOnlyStringMatchesExactly) - { - m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - - EXPECT_EQ(nullptr, m_logicEngine.findByName("Nodebinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("node")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("binding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("Xnodebinding")); - EXPECT_EQ(nullptr, m_logicEngine.findByName("nodebindinY")); - } - - TEST_F(ALogicEngine_Lookup, GetHLObjectFromImpl) - { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - const auto script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const auto rpBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - const auto rgBinding = createRenderGroupBinding(); - const auto meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "meshbinding"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "dataarray"); - const auto animNode = createAnimationNode(dataArray); - const auto timer = m_logicEngine.createTimerNode("timerNode"); - const auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - const auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - const auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - EXPECT_EQ(module, &module->m_impl.getLogicObject()); - EXPECT_EQ(script, &script->m_script.getLogicObject()); - EXPECT_EQ(nodeBinding, &nodeBinding->m_nodeBinding.getLogicObject()); - EXPECT_EQ(appearanceBinding, &appearanceBinding->m_appearanceBinding.getLogicObject()); - EXPECT_EQ(cameraBinding, &cameraBinding->m_cameraBinding.getLogicObject()); - EXPECT_EQ(rpBinding, &rpBinding->m_renderPassBinding.getLogicObject()); - EXPECT_EQ(rgBinding, &rgBinding->m_renderGroupBinding.getLogicObject()); - EXPECT_EQ(meshBinding, &meshBinding->m_meshNodeBinding.getLogicObject()); - EXPECT_EQ(dataArray, &dataArray->m_impl.getLogicObject()); - EXPECT_EQ(animNode, &animNode->m_animationNodeImpl.getLogicObject()); - EXPECT_EQ(timer, &timer->m_timerNodeImpl.getLogicObject()); - EXPECT_EQ(intf, &intf->m_interface.getLogicObject()); - EXPECT_EQ(anchor, &anchor->m_anchorPointImpl.getLogicObject()); - EXPECT_EQ(skin, &skin->m_skinBinding.getLogicObject()); - } - - TEST_F(ALogicEngine_Lookup, GetHLObjectFromImpl_const) - { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "luaModule"); - const auto script = m_logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - const auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appbinding"); - const auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "camerabinding"); - const auto rpBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpbinding"); - const auto rgBinding = createRenderGroupBinding(); - const auto meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "meshbinding"); - const auto dataArray = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "dataarray"); - const auto animNode = createAnimationNode(dataArray); - const auto timer = m_logicEngine.createTimerNode("timerNode"); - const auto intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - const auto anchor = m_logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - const auto skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_logicEngine); - - const auto& moduleImpl = module->m_impl; - const auto& scriptImpl = script->m_script; - const auto& nodeBindingImpl = nodeBinding->m_nodeBinding; - const auto& appearanceBindingImpl = appearanceBinding->m_appearanceBinding; - const auto& cameraBindingImpl = cameraBinding->m_cameraBinding; - const auto& rpBindingImpl = rpBinding->m_renderPassBinding; - const auto& rgBindingImpl = rgBinding->m_renderGroupBinding; - const auto& meshBindingImpl = meshBinding->m_meshNodeBinding; - const auto& dataArrayImpl = dataArray->m_impl; - const auto& animNodeImpl = animNode->m_animationNodeImpl; - const auto& timerImpl = timer->m_timerNodeImpl; - const auto& intfImpl = intf->m_interface; - const auto& anchorImpl = anchor->m_anchorPointImpl; - const auto& skinImpl = skin->m_skinBinding; - - EXPECT_EQ(module, &moduleImpl.getLogicObject()); - EXPECT_EQ(script, &scriptImpl.getLogicObject()); - EXPECT_EQ(nodeBinding, &nodeBindingImpl.getLogicObject()); - EXPECT_EQ(appearanceBinding, &appearanceBindingImpl.getLogicObject()); - EXPECT_EQ(cameraBinding, &cameraBindingImpl.getLogicObject()); - EXPECT_EQ(rpBinding, &rpBindingImpl.getLogicObject()); - EXPECT_EQ(rgBinding, &rgBindingImpl.getLogicObject()); - EXPECT_EQ(meshBinding, &meshBindingImpl.getLogicObject()); - EXPECT_EQ(dataArray, &dataArrayImpl.getLogicObject()); - EXPECT_EQ(animNode, &animNodeImpl.getLogicObject()); - EXPECT_EQ(timer, &timerImpl.getLogicObject()); - EXPECT_EQ(intf, &intfImpl.getLogicObject()); - EXPECT_EQ(anchor, &anchorImpl.getLogicObject()); - EXPECT_EQ(skin, &skinImpl.getLogicObject()); - } -} - diff --git a/client/logic/unittests/api/LogicEngineTest_Serialization.cpp b/client/logic/unittests/api/LogicEngineTest_Serialization.cpp deleted file mode 100644 index 9b4c40143..000000000 --- a/client/logic/unittests/api/LogicEngineTest_Serialization.cpp +++ /dev/null @@ -1,1216 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "RamsesTestUtils.h" -#include "WithTempDirectory.h" -#include "FeatureLevelTestValues.h" -#include "PropertyLinkTestUtils.h" - -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/Logger.h" -#include "ramses-logic/RamsesLogicVersion.h" - -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-framework-api/RamsesVersion.h" - -#include "impl/LogicNodeImpl.h" -#include "impl/LogicEngineImpl.h" -#include "impl/DataArrayImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/ApiObjects.h" -#include "internals/FileUtils.h" -#include "LogTestUtils.h" -#include "FileDescriptorHelper.h" - -#include "generated/LogicEngineGen.h" -#include "ramses-sdk-build-config.h" -#include "fmt/format.h" - -#include -#include - -namespace ramses::internal -{ - class ALogicEngine_Serialization : public ALogicEngineBase, public ::testing::TestWithParam - { - public: - ALogicEngine_Serialization() : ALogicEngineBase{ GetParam() } - { - } - - protected: - static std::vector CreateTestBuffer(const SaveFileConfig& config = {}) - { - LogicEngine logicEngineForSaving{ GetParam() }; - - const std::string src = R"( - function interface(IN,OUT) - IN.param = Type:Int32() - OUT.param2 = Type:Int32() - end - function run(IN,OUT) - end - )"; - - // Create simple (and compact) valid setup, where two (identical) scripts are created - // and their inputs and outputs are cross linked, so that none of the scripts - // generate a warning for having unlinked inputs or outputs - - auto* script1 = logicEngineForSaving.createLuaScript(src, {}, "luascript"); - auto* script2 = logicEngineForSaving.createLuaScript(src, {}, "luascript2"); - - //link output of 1st script to input of 2nd script - logicEngineForSaving.link(*script1->getOutputs()->getChild("param2"), *script2->getInputs()->getChild("param")); - //link output of 2nd script to input of 1st script, use weak link to avoid circular dependancy - logicEngineForSaving.linkWeak(*script2->getOutputs()->getChild("param2"), *script1->getInputs()->getChild("param")); - - EXPECT_TRUE(logicEngineForSaving.saveToFile("tempfile.bin", config)); - - return *FileUtils::LoadBinary("tempfile.bin"); - } - - static void SaveBufferToFile(const std::vector& bufferData, const std::string& file) - { - FileUtils::SaveBinary(file, static_cast(bufferData.data()), bufferData.size()); - } - - std::vector saveAndLoadAllTypesOfObjects() - { - LogicEngine logicEngine{ GetParam() }; - logicEngine.createLuaModule(m_moduleSourceCode, {}, "module"); - logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - auto* nodeBinding = logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); - auto* appearanceBinding = logicEngine.createRamsesAppearanceBinding(*m_appearance, "appearanceBinding"); - auto* cameraBinding = logicEngine.createRamsesCameraBinding(*m_camera, "cameraBinding"); - const auto* dataArray = logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataArray"); - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - logicEngine.createAnimationNode(config, "animNode"); - logicEngine.createTimerNode("timerNode"); - logicEngine.createLuaInterface(R"( - function interface(IN, OUT) - end - )", "intf"); - - logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpBinding"); - logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - createRenderGroupBinding(logicEngine); - createSkinBinding(*nodeBinding, *appearanceBinding, logicEngine); - logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - - EXPECT_TRUE(logicEngine.update()); - EXPECT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - const std::vector names{ "module", "script", "nodeBinding", "appearanceBinding", "cameraBinding", "dataArray", "animNode", - "timerNode", "intf", "rpBinding", "anchor", "renderGroupBinding", "skin", "mb" }; - - std::vector objects; - for (const auto& name : names) - { - auto obj = m_logicEngine.findByName(name); - EXPECT_NE(nullptr, obj); - objects.push_back(obj); - } - - return objects; - } - - WithTempDirectory m_tempDirectory; - }; - - INSTANTIATE_TEST_SUITE_P( - ALogicEngine_SerializationTests, - ALogicEngine_Serialization, - ramses::internal::GetFeatureLevelTestValues()); - - TEST_P(ALogicEngine_Serialization, ProducesErrorIfDeserilizedFromInvalidFile) - { - EXPECT_FALSE(m_logicEngine.loadFromFile("invalid")); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr("Failed to load file 'invalid'")); - } - - TEST_P(ALogicEngine_Serialization, ProducesErrorIfDeserilizedFromFileWithoutApiObjects) - { - { - ramses::RamsesVersion ramsesVersion = ramses::GetRamsesVersion(); - flatbuffers::FlatBufferBuilder builder; - auto logicEngine = rlogic_serialization::CreateLogicEngine( - builder, - rlogic_serialization::CreateVersion(builder, - ramsesVersion.major, - ramsesVersion.minor, - ramsesVersion.patch, - builder.CreateString(ramsesVersion.string)), - rlogic_serialization::CreateVersion(builder, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR_INT, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR_INT, - ramses_sdk::RAMSES_SDK_PROJECT_VERSION_PATCH_INT, - builder.CreateString(ramses_sdk::RAMSES_SDK_RAMSES_VERSION)), - 0, // missing api objects - 0, - GetParam() - ); - - builder.Finish(logicEngine, rlogic_serialization::LogicEngineIdentifier()); - - ASSERT_TRUE(FileUtils::SaveBinary("no_api_objects.bin", builder.GetBufferPointer(), builder.GetSize())); - } - - EXPECT_FALSE(m_logicEngine.loadFromFile("no_api_objects.bin")); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_THAT(errors[0].message, ::testing::HasSubstr("doesn't contain API objects")); - } - - TEST_P(ALogicEngine_Serialization, ProducesErrorWhenProvidingAFolderAsTargetForSaving) - { - fs::create_directories("folder"); - EXPECT_FALSE(m_logicEngine.saveToFile("folder")); - EXPECT_EQ("Failed to save content to path 'folder'!", m_logicEngine.getErrors()[0].message); - } - - TEST_P(ALogicEngine_Serialization, ProducesErrorIfDeserilizedFromFolder) - { - fs::create_directories("folder"); - EXPECT_FALSE(m_logicEngine.loadFromFile("folder")); - EXPECT_EQ("Failed to load file 'folder'", m_logicEngine.getErrors()[0].message); - } - - TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor) - { - std::vector bufferData = CreateTestBuffer(); - SaveBufferToFile(bufferData, "LogicEngine.bin"); - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin"); - EXPECT_LT(0, fd); - EXPECT_TRUE(m_logicEngine.loadFromFileDescriptor(fd, 0, bufferData.size())); - } - - TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptorWithOffset) - { - const size_t offset = 10; - std::vector bufferData = CreateTestBuffer(); - bufferData.insert(bufferData.begin(), offset, 'x'); - SaveBufferToFile(bufferData, "LogicEngine.bin"); - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin"); - EXPECT_LT(0, fd); - EXPECT_TRUE(m_logicEngine.loadFromFileDescriptor(fd, offset, bufferData.size()- offset)); - } - - TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_InvalidFileDescriptor) - { - EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(0, 0, 1000)); - EXPECT_EQ("Invalid file descriptor: 0", m_logicEngine.getErrors()[0].message); - } - - TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_ZeroSize) - { - EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(42, 0, 0)); - EXPECT_EQ("Failed to load from file descriptor: size may not be 0", m_logicEngine.getErrors()[0].message); - } - - TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_InvalidOffset) - { - std::vector bufferData = CreateTestBuffer(); - SaveBufferToFile(bufferData, "LogicEngine.bin"); - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin"); - EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(fd, bufferData.size(), bufferData.size())); - EXPECT_EQ(fmt::format("Failed to load from file descriptor: fd: {} offset: {} size: {}", fd, bufferData.size(), bufferData.size()), - m_logicEngine.getErrors()[0].message); - } - - TEST_P(ALogicEngine_Serialization, LoadFromFileDescriptor_InvalidSize) - { - std::vector bufferData = CreateTestBuffer(); - SaveBufferToFile(bufferData, "LogicEngine.bin"); - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary("LogicEngine.bin"); - EXPECT_FALSE(m_logicEngine.loadFromFileDescriptor(fd, 0, bufferData.size() + 1)); - EXPECT_EQ(fmt::format("Failed to load from file descriptor: fd: {} offset: {} size: {}", fd, 0, bufferData.size() + 1), - m_logicEngine.getErrors()[0].message); - } - - TEST_P(ALogicEngine_Serialization, DeserializesFromMemoryBuffer) - { - const std::vector bufferData = CreateTestBuffer(); - - EXPECT_TRUE(m_logicEngine.loadFromBuffer(bufferData.data(), bufferData.size())); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - { - auto script = m_logicEngine.findByName("luascript"); - ASSERT_NE(nullptr, script); - const auto inputs = script->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(1u, inputs->getChildCount()); - } - } - - TEST_P(ALogicEngine_Serialization, ProducesErrorIfDeserializedFromCorruptedData) - { - // Emulate data corruption - { - std::vector bufferData = CreateTestBuffer(); - ASSERT_GT(bufferData.size(), 62u); - // Do a random byte corruption - // byte 62 happens to break the format - found out by trial and error - bufferData[62] = 42; - SaveBufferToFile(bufferData, "LogicEngine.bin"); - } - - // Test with file API - { - ASSERT_FALSE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("contains corrupted data!")); - } - - // Test with buffer API - { - std::vector corruptedMemory = *FileUtils::LoadBinary("LogicEngine.bin"); - ASSERT_FALSE(m_logicEngine.loadFromBuffer(corruptedMemory.data(), corruptedMemory.size())); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("contains corrupted data!")); - } - } - - TEST_P(ALogicEngine_Serialization, PrintsMetadataInfoOnLoad) - { - SaveFileConfig config; - config.setMetadataString("This is a scene exported for tests"); - config.setExporterVersion(3, 1, 2, 42); - - // Test different constructor variations - SaveFileConfig config2(config); - config2 = config; - config2 = std::move(config); - - SaveBufferToFile(CreateTestBuffer(config2), "LogicEngine.bin"); - - TestLogCollector logCollector(ELogLevel::Info); - - // Test with file API - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - - ASSERT_EQ(logCollector.logs.size(), 3u); - EXPECT_THAT(logCollector.logs[0].message, ::testing::HasSubstr("Loading logic engine content from 'file 'LogicEngine.bin'")); - EXPECT_THAT(logCollector.logs[1].message, ::testing::HasSubstr("Logic Engine content metadata: 'This is a scene exported for tests'")); - EXPECT_THAT(logCollector.logs[2].message, ::testing::HasSubstr("Exporter version: 3.1.2 (file format version 42)")); - } - - logCollector.logs.clear(); - - // Test with buffer API - { - const std::vector byteBuffer = *FileUtils::LoadBinary("LogicEngine.bin"); - EXPECT_TRUE(m_logicEngine.loadFromBuffer(byteBuffer.data(), byteBuffer.size())); - - ASSERT_EQ(logCollector.logs.size(), 3u); - EXPECT_THAT(logCollector.logs[0].message, ::testing::HasSubstr("Loading logic engine content from 'data buffer")); - EXPECT_THAT(logCollector.logs[1].message, ::testing::HasSubstr("Logic Engine content metadata: 'This is a scene exported for tests'")); - EXPECT_THAT(logCollector.logs[2].message, ::testing::HasSubstr("Exporter version: 3.1.2 (file format version 42)")); - } - } - - TEST_P(ALogicEngine_Serialization, PrintsMetadataInfoOnLoad_NoVersionInfoProvided) - { - SaveFileConfig config; - SaveBufferToFile(CreateTestBuffer(config), "LogicEngine.bin"); - - TestLogCollector logCollector(ELogLevel::Info); - - // Test with file API - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - - ASSERT_EQ(logCollector.logs.size(), 3u); - EXPECT_THAT(logCollector.logs[0].message, ::testing::HasSubstr("Loading logic engine content from 'file 'LogicEngine.bin'")); - EXPECT_THAT(logCollector.logs[1].message, ::testing::HasSubstr("Logic Engine content metadata: ''")); - EXPECT_THAT(logCollector.logs[2].message, ::testing::HasSubstr("Exporter version: 0.0.0 (file format version 0)")); - } - - logCollector.logs.clear(); - - // Test with buffer API - { - const std::vector byteBuffer = *FileUtils::LoadBinary("LogicEngine.bin"); - EXPECT_TRUE(m_logicEngine.loadFromBuffer(byteBuffer.data(), byteBuffer.size())); - - ASSERT_EQ(logCollector.logs.size(), 3u); - EXPECT_THAT(logCollector.logs[0].message, ::testing::HasSubstr("Loading logic engine content from 'data buffer")); - EXPECT_THAT(logCollector.logs[1].message, ::testing::HasSubstr("Logic Engine content metadata: ''")); - EXPECT_THAT(logCollector.logs[2].message, ::testing::HasSubstr("Exporter version: 0.0.0 (file format version 0)")); - } - } - - // the special file identifiers in flatbuffers are at bytes 4-7, so a file smaller than 8 bytes is a special case of broken - TEST_P(ALogicEngine_Serialization, ProducesErrorIfDeserializedFromFileSmallerThan8Bytes) - { - // Emulate data truncation - { - std::vector bufferData = CreateTestBuffer(); - ASSERT_GT(bufferData.size(), 60u); - std::vector truncated(bufferData.begin(), bufferData.begin() + 7); - SaveBufferToFile(truncated, "LogicEngine.bin"); - } - - // Test with file API - { - EXPECT_FALSE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("(size: 7) contains corrupted data! Data should be at least 8 bytes")); - } - - // Test with buffer API - { - std::vector truncatedMemory = *FileUtils::LoadBinary("LogicEngine.bin"); - EXPECT_FALSE(m_logicEngine.loadFromBuffer(truncatedMemory.data(), truncatedMemory.size())); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("(size: 7) contains corrupted data! Data should be at least 8 bytes")); - } - } - - TEST_P(ALogicEngine_Serialization, ProducesErrorIfDeserializedFromTruncatedData) - { - // Emulate data truncation - { - std::vector bufferData = CreateTestBuffer(); - ASSERT_GT(bufferData.size(), 60u); - - // Cutting off the data at byte 60 breaks deserialization (found by trial and error) - std::vector truncated(bufferData.begin(), bufferData.begin() + 60); - SaveBufferToFile(truncated, "LogicEngine.bin"); - } - - // Test with file API - { - EXPECT_FALSE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("(size: 60) contains corrupted data!")); - } - - // Test with buffer API - { - std::vector truncatedMemory = *FileUtils::LoadBinary("LogicEngine.bin"); - EXPECT_FALSE(m_logicEngine.loadFromBuffer(truncatedMemory.data(), truncatedMemory.size())); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("(size: 60) contains corrupted data!")); - } - } - -// The Windows API doesn't allow non-admin access to symlinks, this breaks on dev machines -#ifndef _WIN32 - TEST_P(ALogicEngine_Serialization, CanBeDeserializedFromHardLink) - { - EXPECT_TRUE(m_logicEngine.saveToFile("testfile.bin")); - fs::create_hard_link("testfile.bin", "hardlink"); - EXPECT_TRUE(m_logicEngine.loadFromFile("hardlink")); - } - - TEST_P(ALogicEngine_Serialization, CanBeDeserializedFromSymLink) - { - EXPECT_TRUE(m_logicEngine.saveToFile("testfile.bin")); - fs::create_symlink("testfile.bin", "symlink"); - EXPECT_TRUE(m_logicEngine.loadFromFile("symlink")); - } - - TEST_P(ALogicEngine_Serialization, FailsGracefullyWhenTryingToOpenFromDanglingSymLink) - { - EXPECT_TRUE(m_logicEngine.saveToFile("testfile.bin")); - fs::create_symlink("testfile.bin", "dangling_symlink"); - fs::remove("testfile.bin"); - EXPECT_FALSE(m_logicEngine.loadFromFile("dangling_symlink")); - EXPECT_EQ("Failed to load file 'dangling_symlink'", m_logicEngine.getErrors()[0].message); - } -#endif - - TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserializedWithNoScriptsAndNoNodeBindings) - { - { - LogicEngine logicEngine{ GetParam() }; - ASSERT_TRUE(logicEngine.saveToFile("LogicEngine.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - } - } - - TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserializedWithNoScripts) - { - { - LogicEngine logicEngine{ GetParam() }; - logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); - - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - { - auto rNodeBinding = m_logicEngine.findByName("binding"); - ASSERT_NE(nullptr, rNodeBinding); - } - } - } - - TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserializedWithoutNodeBindings) - { - { - LogicEngine logicEngine{ GetParam() }; - logicEngine.createLuaScript(R"( - function interface(IN,OUT) - IN.param = Type:Int32() - end - function run(IN,OUT) - end - )", {}, "luascript"); - - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - { - auto script = m_logicEngine.findByName("luascript"); - ASSERT_NE(nullptr, script); - const auto inputs = script->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(1u, inputs->getChildCount()); - } - } - } - - TEST_P(ALogicEngine_Serialization, ProducesErrorIfSavedWithValidationWarning) - { - // Put logic engine to a dirty state (create new object and don't call update) - RamsesNodeBinding* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); - - std::vector messages; - std::vector messageTypes; - ScopedLogContextLevel scopedLogs(ELogLevel::Warn, [&](ELogLevel msgType, std::string_view message) { - messages.emplace_back(message); - messageTypes.emplace_back(msgType); - }); - - // Set a value and save -> causes warning - nodeBinding->getInputs()->getChild("visibility")->set(false); - ASSERT_TRUE(nodeBinding->m_impl.isDirty()); - ASSERT_FALSE(m_logicEngine.saveToFile("LogicEngine.bin")); - - ASSERT_EQ(3u, messages.size()); - EXPECT_EQ("Saving logic engine content with manually updated binding values without calling update() will result in those values being lost!", messages[0]); - EXPECT_EQ("[binding [Id=1]] Node [binding] has no ingoing links! Node should be deleted or properly linked!", messages[1]); - EXPECT_EQ("Failed to saveToFile() because validation warnings were encountered! Refer to the documentation of saveToFile() for details how to address these gracefully.", messages[2]); - EXPECT_EQ(ELogLevel::Warn, messageTypes[0]); - EXPECT_EQ(ELogLevel::Warn, messageTypes[1]); - EXPECT_EQ(ELogLevel::Error, messageTypes[2]); - - // Unset custom log handler - Logger::SetLogHandler([](ELogLevel msgType, std::string_view message) { - (void)message; - (void)msgType; - }); - } - - TEST_P(ALogicEngine_Serialization, RefusesToSaveTwoNodeBindingsWhichPointToDifferentScenes) - { - RamsesTestSetup testSetup; - ramses::Scene* scene1 = testSetup.createScene(ramses::sceneId_t(1)); - ramses::Scene* scene2 = testSetup.createScene(ramses::sceneId_t(2)); - - ramses::Node* node1 = scene1->createNode("node1"); - ramses::Node* node2 = scene2->createNode("node2"); - - m_logicEngine.createRamsesNodeBinding(*node1, ramses::ERotationType::Euler_XYZ, "binding1"); - ramses::RamsesNodeBinding* binding2 = m_logicEngine.createRamsesNodeBinding(*node2, ramses::ERotationType::Euler_XYZ, "binding2"); - - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_be_written.logic")); - ASSERT_EQ(2u, m_logicEngine.getErrors().size()); - EXPECT_EQ("Ramses node 'node2' is from scene with id:2 but other objects are from scene with id:1!", m_logicEngine.getErrors()[0].message); - EXPECT_EQ(binding2, m_logicEngine.getErrors()[0].object); - EXPECT_EQ("Can't save a logic engine to file while it has references to more than one Ramses scene!", m_logicEngine.getErrors()[1].message); - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[1].object); - } - - TEST_P(ALogicEngine_Serialization, RefusesToSaveTwoCameraBindingsWhichPointToDifferentScenes) - { - RamsesTestSetup testSetup; - ramses::Scene* scene1 = testSetup.createScene(ramses::sceneId_t(1)); - ramses::Scene* scene2 = testSetup.createScene(ramses::sceneId_t(2)); - - ramses::PerspectiveCamera* camera1 = scene1->createPerspectiveCamera("camera1"); - ramses::PerspectiveCamera* camera2 = scene2->createPerspectiveCamera("camera2"); - - m_logicEngine.createRamsesCameraBinding(*camera1, "binding1"); - ramses::RamsesCameraBinding* binding2 = m_logicEngine.createRamsesCameraBinding(*camera2, "binding2"); - - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_be_written.logic")); - ASSERT_EQ(2u, m_logicEngine.getErrors().size()); - EXPECT_EQ("Ramses camera 'camera2' is from scene with id:2 but other objects are from scene with id:1!", m_logicEngine.getErrors()[0].message); - EXPECT_EQ(binding2, m_logicEngine.getErrors()[0].object); - EXPECT_EQ("Can't save a logic engine to file while it has references to more than one Ramses scene!", m_logicEngine.getErrors()[1].message); - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[1].object); - } - - TEST_P(ALogicEngine_Serialization, RefusesToSaveTwoRenderPassBindingsWhichPointToDifferentScenes) - { - RamsesTestSetup testSetup; - ramses::Scene* scene1 = testSetup.createScene(ramses::sceneId_t(1)); - ramses::Scene* scene2 = testSetup.createScene(ramses::sceneId_t(2)); - - auto* rp1 = scene1->createRenderPass("rp1"); - auto* rp2 = scene2->createRenderPass("rp2"); - - m_logicEngine.createRamsesRenderPassBinding(*rp1, "binding1"); - auto* binding2 = m_logicEngine.createRamsesRenderPassBinding(*rp2, "binding2"); - - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_be_written.logic")); - ASSERT_EQ(2u, m_logicEngine.getErrors().size()); - EXPECT_EQ("Ramses render pass 'rp2' is from scene with id:2 but other objects are from scene with id:1!", m_logicEngine.getErrors()[0].message); - EXPECT_EQ(binding2, m_logicEngine.getErrors()[0].object); - EXPECT_EQ("Can't save a logic engine to file while it has references to more than one Ramses scene!", m_logicEngine.getErrors()[1].message); - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[1].object); - } - - TEST_P(ALogicEngine_Serialization, RefusesToSaveTwoRenderGroupBindingsWhichPointToDifferentScenes) - { - RamsesTestSetup testSetup; - ramses::Scene* scene1 = testSetup.createScene(ramses::sceneId_t(1)); - ramses::Scene* scene2 = testSetup.createScene(ramses::sceneId_t(2)); - - const auto meshNode1 = scene1->createMeshNode(); - auto* rg1 = scene1->createRenderGroup("rg1"); - rg1->addMeshNode(*meshNode1); - - const auto meshNode2 = scene2->createMeshNode(); - auto* rg2 = scene2->createRenderGroup("rg2"); - rg2->addMeshNode(*meshNode2); - - RamsesRenderGroupBindingElements elements1; - EXPECT_TRUE(elements1.addElement(*meshNode1, "mesh")); - m_logicEngine.createRamsesRenderGroupBinding(*rg1, elements1, "binding1"); - - RamsesRenderGroupBindingElements elements2; - EXPECT_TRUE(elements2.addElement(*meshNode2, "mesh")); - const auto binding2 = m_logicEngine.createRamsesRenderGroupBinding(*rg2, elements2, "binding2"); - - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_be_written.logic")); - ASSERT_EQ(2u, m_logicEngine.getErrors().size()); - EXPECT_EQ("Ramses render group 'rg2' is from scene with id:2 but other objects are from scene with id:1!", m_logicEngine.getErrors()[0].message); - EXPECT_EQ(binding2, m_logicEngine.getErrors()[0].object); - EXPECT_EQ("Can't save a logic engine to file while it has references to more than one Ramses scene!", m_logicEngine.getErrors()[1].message); - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[1].object); - } - - TEST_P(ALogicEngine_Serialization, RefusesToSaveTwoMeshNodeBindingsWhichPointToDifferentScenes) - { - RamsesTestSetup testSetup; - ramses::Scene* scene1 = testSetup.createScene(ramses::sceneId_t(1)); - ramses::Scene* scene2 = testSetup.createScene(ramses::sceneId_t(2)); - - const auto meshNode1 = scene1->createMeshNode("mesh1"); - const auto meshNode2 = scene2->createMeshNode("mesh2"); - - m_logicEngine.createRamsesMeshNodeBinding(*meshNode1, "binding1"); - const auto binding2 = m_logicEngine.createRamsesMeshNodeBinding(*meshNode2, "binding2"); - - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_be_written.logic")); - ASSERT_EQ(2u, m_logicEngine.getErrors().size()); - EXPECT_EQ("Ramses mesh node 'mesh2' is from scene with id:2 but other objects are from scene with id:1!", m_logicEngine.getErrors()[0].message); - EXPECT_EQ(binding2, m_logicEngine.getErrors()[0].object); - EXPECT_EQ("Can't save a logic engine to file while it has references to more than one Ramses scene!", m_logicEngine.getErrors()[1].message); - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[1].object); - } - - TEST_P(ALogicEngine_Serialization, RefusesToSaveAppearanceBindingWhichIsFromDifferentSceneThanNodeBinding) - { - ramses::Scene* scene2 = m_ramses.createScene(ramses::sceneId_t(2)); - - m_logicEngine.createRamsesNodeBinding(*scene2->createNode(), ramses::ERotationType::Euler_XYZ, "node binding"); - ramses::RamsesAppearanceBinding* appBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "app binding"); - - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_be_written.logic")); - EXPECT_EQ(2u, m_logicEngine.getErrors().size()); - EXPECT_EQ("Ramses appearance 'test appearance' is from scene with id:1 but other objects are from scene with id:2!", m_logicEngine.getErrors()[0].message); - EXPECT_EQ(appBinding, m_logicEngine.getErrors()[0].object); - EXPECT_EQ("Can't save a logic engine to file while it has references to more than one Ramses scene!", m_logicEngine.getErrors()[1].message); - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[1].object); - } - - TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserilizedSuccessfully) - { - saveAndLoadAllTypesOfObjects(); - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - { - auto moduleByName = m_logicEngine.findByName("module"); - auto moduleById = m_logicEngine.findLogicObjectById(1u); - ASSERT_NE(nullptr, moduleByName); - ASSERT_EQ(moduleById, moduleByName); - } - { - auto scriptByName = m_logicEngine.findByName("script"); - auto scriptById = m_logicEngine.findLogicObjectById(2u); - ASSERT_NE(nullptr, scriptByName); - ASSERT_EQ(scriptById, scriptByName); - const auto inputs = scriptByName->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(0u, inputs->getChildCount()); - EXPECT_TRUE(scriptByName->m_impl.isDirty()); - } - { - auto rNodeBindingByName = m_logicEngine.findByName("nodeBinding"); - auto rNodeBindingById = m_logicEngine.findLogicObjectById(3u); - ASSERT_NE(nullptr, rNodeBindingByName); - ASSERT_EQ(rNodeBindingById, rNodeBindingByName); - const auto inputs = rNodeBindingByName->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(5u, inputs->getChildCount()); - EXPECT_FALSE(rNodeBindingByName->m_impl.isDirty()); - } - { - auto rCameraBindingByName = m_logicEngine.findByName("cameraBinding"); - auto rCameraBindingById = m_logicEngine.findLogicObjectById(5u); - ASSERT_NE(nullptr, rCameraBindingByName); - ASSERT_EQ(rCameraBindingById, rCameraBindingByName); - const auto inputs = rCameraBindingByName->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(2u, inputs->getChildCount()); - EXPECT_FALSE(rCameraBindingByName->m_impl.isDirty()); - } - { - auto rAppearanceBindingByName = m_logicEngine.findByName("appearanceBinding"); - auto rAppearanceBindingById = m_logicEngine.findLogicObjectById(4u); - ASSERT_NE(nullptr, rAppearanceBindingByName); - ASSERT_EQ(rAppearanceBindingById, rAppearanceBindingByName); - const auto inputs = rAppearanceBindingByName->getInputs(); - ASSERT_NE(nullptr, inputs); - - ASSERT_EQ(1u, inputs->getChildCount()); - auto floatUniform = inputs->getChild(0); - ASSERT_NE(nullptr, floatUniform); - EXPECT_EQ("floatUniform", floatUniform->getName()); - EXPECT_EQ(EPropertyType::Float, floatUniform->getType()); - EXPECT_FALSE(rAppearanceBindingByName->m_impl.isDirty()); - } - { - const auto dataArrayByName = m_logicEngine.findByName("dataArray"); - const auto dataArrayById = m_logicEngine.findLogicObjectById(6u); - ASSERT_NE(nullptr, dataArrayByName); - ASSERT_EQ(dataArrayById, dataArrayByName); - EXPECT_EQ(EPropertyType::Float, dataArrayByName->getDataType()); - ASSERT_NE(nullptr, dataArrayByName->getData()); - const std::vector expectedData{ 1.f, 2.f, 3.f }; - EXPECT_EQ(expectedData, *m_logicEngine.findByName("dataArray")->getData()); - - const auto animNodeByName = m_logicEngine.findByName("animNode"); - const auto animNodeById = m_logicEngine.findLogicObjectById(7u); - ASSERT_NE(nullptr, animNodeByName); - ASSERT_EQ(animNodeById, animNodeByName); - ASSERT_EQ(1u, animNodeByName->getChannels().size()); - EXPECT_EQ(dataArrayByName, animNodeByName->getChannels().front().timeStamps); - EXPECT_EQ(dataArrayByName, animNodeByName->getChannels().front().keyframes); - } - { - auto rpBindingByName = m_logicEngine.findByName("rpBinding"); - auto rpBindingById = m_logicEngine.findLogicObjectById(10u); - ASSERT_NE(nullptr, rpBindingByName); - ASSERT_EQ(rpBindingById, rpBindingByName); - const auto inputs = rpBindingByName->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(4u, inputs->getChildCount()); - EXPECT_FALSE(rpBindingByName->m_impl.isDirty()); - - auto anchorByName = m_logicEngine.findByName("anchor"); - auto anchorById = m_logicEngine.findLogicObjectById(11u); - ASSERT_NE(nullptr, anchorByName); - ASSERT_EQ(anchorById, anchorByName); - const auto outputs = anchorByName->getOutputs(); - ASSERT_NE(nullptr, outputs); - EXPECT_EQ(2u, outputs->getChildCount()); - } - { - auto rgBindingByName = m_logicEngine.findByName("renderGroupBinding"); - auto rgBindingById = m_logicEngine.findLogicObjectById(12u); - ASSERT_NE(nullptr, rgBindingByName); - ASSERT_EQ(rgBindingById, rgBindingByName); - const auto inputs = rgBindingByName->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(1u, inputs->getChildCount()); - EXPECT_EQ(1u, inputs->getChild("renderOrders")->getChildCount()); - EXPECT_FALSE(rgBindingByName->m_impl.isDirty()); - } - { - auto skinByName = m_logicEngine.findByName("skin"); - auto skinById = m_logicEngine.findLogicObjectById(13u); - ASSERT_NE(nullptr, skinByName); - ASSERT_EQ(skinById, skinByName); - } - { - auto meshBindingByName = m_logicEngine.findByName("mb"); - auto meshBindingById = m_logicEngine.findLogicObjectById(14u); - ASSERT_NE(nullptr, meshBindingByName); - ASSERT_EQ(meshBindingById, meshBindingByName); - const auto inputs = meshBindingByName->getInputs(); - ASSERT_NE(nullptr, inputs); - EXPECT_EQ(4u, inputs->getChildCount()); - EXPECT_FALSE(meshBindingByName->m_impl.isDirty()); - } - } - } - - TEST_P(ALogicEngine_Serialization, ReplacesCurrentStateWithStateFromFile) - { - { - LogicEngine logicEngine{ GetParam() }; - logicEngine.createLuaScript(R"( - function interface(IN,OUT) - IN.param = Type:Int32() - end - function run(IN,OUT) - end - )", {}, "luascript"); - - logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - } - { - m_logicEngine.createLuaScript(R"( - function interface(IN,OUT) - IN.param2 = Type:Float() - end - function run(IN,OUT) - end - )", {}, "luascript2"); - - m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding2"); - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - { - ASSERT_EQ(nullptr, m_logicEngine.findByName("luascript2")); - ASSERT_EQ(nullptr, m_logicEngine.findByName("binding2")); - - auto script = m_logicEngine.findByName("luascript"); - ASSERT_NE(nullptr, script); - auto rNodeBinding = m_logicEngine.findByName("binding"); - ASSERT_NE(nullptr, rNodeBinding); - EXPECT_EQ(m_node, &rNodeBinding->getRamsesNode()); - } - } - } - - TEST_P(ALogicEngine_Serialization, DeserializesLinks) - { - { - std::string_view scriptSource = R"( - function interface(IN,OUT) - IN.input = Type:Int32() - OUT.output = Type:Int32() - end - function run(IN,OUT) - end - )"; - - LogicEngine logicEngine{ GetParam() }; - auto sourceScript1 = logicEngine.createLuaScript(scriptSource, {}, "SourceScript1"); - auto targetScript1 = logicEngine.createLuaScript(scriptSource, {}, "TargetScript1"); - auto sourceScript2 = logicEngine.createLuaScript(scriptSource, {}, "SourceScript2"); - auto targetScript2 = logicEngine.createLuaScript(scriptSource, {}, "TargetScript2"); - logicEngine.createLuaScript(scriptSource, {}, "NotLinkedScript"); - - auto srcOutput1 = sourceScript1->getOutputs()->getChild("output"); - auto tgtInput1 = targetScript1->getInputs()->getChild("input"); - auto srcOutput2 = sourceScript2->getOutputs()->getChild("output"); - auto tgtInput2 = targetScript2->getInputs()->getChild("input"); - - EXPECT_TRUE(logicEngine.link(*srcOutput1, *tgtInput1)); - EXPECT_TRUE(logicEngine.linkWeak(*srcOutput2, *tgtInput2)); - - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - auto sourceScript1 = m_logicEngine.findByName("SourceScript1"); - auto targetScript1 = m_logicEngine.findByName("TargetScript1"); - auto sourceScript2 = m_logicEngine.findByName("SourceScript2"); - auto targetScript2 = m_logicEngine.findByName("TargetScript2"); - auto notLinkedScript = m_logicEngine.findByName("NotLinkedScript"); - - EXPECT_TRUE(m_logicEngine.isLinked(*sourceScript1)); - EXPECT_TRUE(m_logicEngine.isLinked(*targetScript1)); - EXPECT_TRUE(m_logicEngine.isLinked(*sourceScript2)); - EXPECT_TRUE(m_logicEngine.isLinked(*targetScript2)); - EXPECT_FALSE(m_logicEngine.isLinked(*notLinkedScript)); - - const internal::LogicNodeDependencies& internalNodeDependencies = m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies(); - EXPECT_TRUE(internalNodeDependencies.isLinked(sourceScript1->m_impl)); - EXPECT_TRUE(internalNodeDependencies.isLinked(targetScript1->m_impl)); - EXPECT_TRUE(internalNodeDependencies.isLinked(sourceScript2->m_impl)); - EXPECT_TRUE(internalNodeDependencies.isLinked(targetScript2->m_impl)); - - const auto srcOutput1 = sourceScript1->getOutputs()->getChild("output"); - const auto tgtInput1 = targetScript1->getInputs()->getChild("input"); - const auto srcOutput2 = sourceScript2->getOutputs()->getChild("output"); - const auto tgtInput2 = targetScript2->getInputs()->getChild("input"); - PropertyLinkTestUtils::ExpectLinks(m_logicEngine, { - { srcOutput1, tgtInput1, false }, - { srcOutput2, tgtInput2, true } - }); - - const auto& srcOutLinks1 = srcOutput1->m_impl->getOutgoingLinks(); - const auto& srcOutLinks2 = srcOutput2->m_impl->getOutgoingLinks(); - ASSERT_EQ(1u, srcOutLinks1.size()); - ASSERT_EQ(1u, srcOutLinks2.size()); - EXPECT_EQ(tgtInput1->m_impl.get(), srcOutLinks1[0].property); - EXPECT_EQ(tgtInput2->m_impl.get(), srcOutLinks2[0].property); - EXPECT_FALSE(srcOutLinks1[0].isWeakLink); - EXPECT_TRUE(srcOutLinks2[0].isWeakLink); - EXPECT_EQ(srcOutput1->m_impl.get(), tgtInput1->m_impl->getIncomingLink().property); - EXPECT_EQ(srcOutput2->m_impl.get(), tgtInput2->m_impl->getIncomingLink().property); - EXPECT_FALSE(tgtInput1->m_impl->getIncomingLink().isWeakLink); - EXPECT_TRUE(tgtInput2->m_impl->getIncomingLink().isWeakLink); - } - } - - TEST_P(ALogicEngine_Serialization, InternalLinkDataIsDeletedAfterDeserialization) - { - std::string_view scriptSource = R"( - function interface(IN,OUT) - IN.input = Type:Int32() - OUT.output = Type:Int32() - end - function run(IN,OUT) - end - )"; - - auto sourceScript = m_logicEngine.createLuaScript(scriptSource, {}, "SourceScript"); - auto targetScript = m_logicEngine.createLuaScript(scriptSource, {}, "TargetScript"); - - // Save logic engine state without links to file - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "LogicEngine.bin")); - - // Create link (should be wiped after loading from file) - auto output = sourceScript->getOutputs()->getChild("output"); - auto input = targetScript->getInputs()->getChild("input"); - m_logicEngine.link(*output, *input); - - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - - auto sourceScriptAfterLoading = m_logicEngine.findByName("SourceScript"); - auto targetScriptAfterLoading = m_logicEngine.findByName("TargetScript"); - - // Make a copy of the object so that we can call non-const methods on it too (getTopologicallySortedNodes()) - // This can't happen in user code, we only do this to test internal data - internal::LogicNodeDependencies internalNodeDependencies = m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies(); - ASSERT_TRUE(internalNodeDependencies.getTopologicallySortedNodes().has_value()); - - // New objects are not linked (because they weren't before saving) - EXPECT_FALSE(m_logicEngine.isLinked(*sourceScriptAfterLoading)); - EXPECT_FALSE(m_logicEngine.isLinked(*targetScriptAfterLoading)); - EXPECT_FALSE(internalNodeDependencies.isLinked(sourceScriptAfterLoading->m_impl)); - EXPECT_FALSE(internalNodeDependencies.isLinked(sourceScriptAfterLoading->m_impl)); - - // Internal topological graph has two unsorted nodes, before and after update() - EXPECT_EQ(2u, (*internalNodeDependencies.getTopologicallySortedNodes()).size()); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(2u, (*internalNodeDependencies.getTopologicallySortedNodes()).size()); - } - - TEST_P(ALogicEngine_Serialization, PreviouslyCreatedModulesAreDeletedInSolStateAfterDeserialization) - { - { - LogicEngine logicEngineForSaving{ GetParam() }; - const std::string_view moduleSrc = R"( - local mymath = {} - mymath.PI=3.1415 - return mymath - )"; - - std::string_view script = R"( - modules("mymath") - function interface(IN,OUT) - OUT.pi = Type:Float() - end - function run(IN,OUT) - OUT.pi = mymath.PI - end - )"; - - LuaModule* mymath = logicEngineForSaving.createLuaModule(moduleSrc, {}, "mymath"); - LuaConfig config; - config.addDependency("mymath", *mymath); - logicEngineForSaving.createLuaScript(script, config, "script"); - - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngineForSaving, "LogicEngine.bin")); - } - - // Create a module with name colliding with the one from file - it should be deleted - const std::string_view moduleToBeWipedSrc = R"( - local mymath = {} - mymath.PI=4 - return mymath - )"; - - // This module will be overwritten when loading the file below. The logic engine should not - // keep any leftovers from modules or scripts when loading from file - all content should be - // taken from the file! - LuaModule* moduleToBeWiped = m_logicEngine.createLuaModule(moduleToBeWipedSrc, {}, "mymath"); - EXPECT_NE(nullptr, moduleToBeWiped); - - ASSERT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - - m_logicEngine.update(); - - auto script = m_logicEngine.findByName("script"); - - // This is the PI from the loaded module, not from 'moduleToBeWiped' - EXPECT_FLOAT_EQ(3.1415f, *script->getOutputs()->getChild("pi")->get()); - } - - TEST_P(ALogicEngine_Serialization, IDsAfterDeserializationAreUnique) - { - uint64_t serializedId = 0u; - { - LogicEngine logicEngine{ GetParam() }; - const auto script = logicEngine.createLuaScript(R"( - function interface(IN,OUT) - IN.param = Type:Int32() - end - function run(IN,OUT) - end - )", {}, "luascript"); - serializedId = script->getId(); - - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - } - - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - auto script = m_logicEngine.findLogicObjectById(serializedId); - ASSERT_NE(nullptr, script); - EXPECT_EQ("luascript", script->getName()); - - const auto anotherScript = m_logicEngine.createLuaScript(R"( - function interface(IN,OUT) - end - function run(IN,OUT) - end - )"); - EXPECT_GT(anotherScript->getId(), script->getId()); - } - - TEST_P(ALogicEngine_Serialization, persistsUserIds) - { - { - LogicEngine logicEngine{ GetParam() }; - auto* luaModule = logicEngine.createLuaModule(m_moduleSourceCode, {}, "module"); - auto* luaScript = logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); - auto* nodeBinding = logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); - auto* appearanceBinding = logicEngine.createRamsesAppearanceBinding(*m_appearance, "appearanceBinding"); - auto* cameraBinding = logicEngine.createRamsesCameraBinding(*m_camera, "cameraBinding"); - auto* dataArray = logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataArray"); - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - auto* animationNode = logicEngine.createAnimationNode(config, "animNode"); - auto* timerNode = logicEngine.createTimerNode("timerNode"); - auto* luaInterface = logicEngine.createLuaInterface(R"( - function interface(IN, OUT) - end - )", "intf"); - auto* rpBinding = logicEngine.createRamsesRenderPassBinding(*m_renderPass, "rpBinding"); - auto* anchor = logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); - auto* rgBinding = createRenderGroupBinding(logicEngine); - auto* skin = createSkinBinding(logicEngine); - auto* meshBinding = logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - - EXPECT_TRUE(luaModule->setUserId(1u, 2u)); - EXPECT_TRUE(luaScript->setUserId(3u, 4u)); - EXPECT_TRUE(nodeBinding->setUserId(5u, 6u)); - EXPECT_TRUE(appearanceBinding->setUserId(7u, 8u)); - EXPECT_TRUE(cameraBinding->setUserId(9u, 10u)); - EXPECT_TRUE(dataArray->setUserId(11u, 12u)); - EXPECT_TRUE(animationNode->setUserId(13u, 14u)); - EXPECT_TRUE(timerNode->setUserId(15u, 16u)); - EXPECT_TRUE(luaInterface->setUserId(17u, 18u)); - EXPECT_TRUE(rpBinding->setUserId(19u, 20u)); - EXPECT_TRUE(anchor->setUserId(21u, 22u)); - EXPECT_TRUE(rgBinding->setUserId(23u, 24u)); - EXPECT_TRUE(skin->setUserId(25u, 26u)); - EXPECT_TRUE(meshBinding->setUserId(27u, 28u)); - - ASSERT_TRUE(SaveToFileWithoutValidation(logicEngine, "LogicEngine.bin")); - } - - EXPECT_TRUE(m_logicEngine.loadFromFile("LogicEngine.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - auto obj = m_logicEngine.findByName("module"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(1u, obj->getUserId().first); - EXPECT_EQ(2u, obj->getUserId().second); - - obj = m_logicEngine.findByName("script"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(3u, obj->getUserId().first); - EXPECT_EQ(4u, obj->getUserId().second); - - obj = m_logicEngine.findByName("nodeBinding"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(5u, obj->getUserId().first); - EXPECT_EQ(6u, obj->getUserId().second); - - obj = m_logicEngine.findByName("appearanceBinding"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(7u, obj->getUserId().first); - EXPECT_EQ(8u, obj->getUserId().second); - - obj = m_logicEngine.findByName("cameraBinding"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(9u, obj->getUserId().first); - EXPECT_EQ(10u, obj->getUserId().second); - - obj = m_logicEngine.findByName("dataArray"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(11u, obj->getUserId().first); - EXPECT_EQ(12u, obj->getUserId().second); - - obj = m_logicEngine.findByName("animNode"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(13u, obj->getUserId().first); - EXPECT_EQ(14u, obj->getUserId().second); - - obj = m_logicEngine.findByName("timerNode"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(15u, obj->getUserId().first); - EXPECT_EQ(16u, obj->getUserId().second); - - obj = m_logicEngine.findByName("intf"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(17u, obj->getUserId().first); - EXPECT_EQ(18u, obj->getUserId().second); - - obj = m_logicEngine.findByName("rpBinding"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(19u, obj->getUserId().first); - EXPECT_EQ(20u, obj->getUserId().second); - - obj = m_logicEngine.findByName("anchor"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(21u, obj->getUserId().first); - EXPECT_EQ(22u, obj->getUserId().second); - - obj = m_logicEngine.findByName("renderGroupBinding"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(23u, obj->getUserId().first); - EXPECT_EQ(24u, obj->getUserId().second); - - obj = m_logicEngine.findByName("skin"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(25u, obj->getUserId().first); - EXPECT_EQ(26u, obj->getUserId().second); - - obj = m_logicEngine.findByName("mb"); - ASSERT_NE(nullptr, obj); - EXPECT_EQ(27u, obj->getUserId().first); - EXPECT_EQ(28u, obj->getUserId().second); - } - - TEST_P(ALogicEngine_Serialization, persistsLogicObjectImplToHLObjectMapping) - { - const auto objects = saveAndLoadAllTypesOfObjects(); - for (const auto& obj : objects) - { - EXPECT_EQ(obj, &obj->m_impl->getLogicObject()); - } - } - - TEST_P(ALogicEngine_Serialization, persistsOwnershipOfAllPropertiesByTheirLogicNode) - { - const auto objects = saveAndLoadAllTypesOfObjects(); - - int propsCount = 0; - for (const auto& obj : objects) - { - const auto logicNode = obj->as(); - if (!logicNode) - continue; - - std::deque props{ logicNode->getInputs(), logicNode->getOutputs() }; - while (!props.empty()) - { - const auto prop = props.back(); - props.pop_back(); - if (prop == nullptr) - continue; - - propsCount++; - EXPECT_EQ(obj, &prop->getOwningLogicNode()); - - for (size_t i = 0u; i < prop->getChildCount(); ++i) - props.push_back(prop->getChild(i)); - } - } - - // just check that the iterating over all props works - EXPECT_EQ(50, propsCount); - } - - TEST_P(ALogicEngine_Serialization, persistsPropertyImplToHLObjectMapping) - { - const auto objects = saveAndLoadAllTypesOfObjects(); - - int propsCount = 0; - for (const auto& obj : objects) - { - const auto logicNode = obj->as(); - if (!logicNode) - continue; - - std::deque props{ logicNode->getInputs(), logicNode->getOutputs() }; - while (!props.empty()) - { - const auto prop = props.back(); - props.pop_back(); - if (prop == nullptr) - continue; - - propsCount++; - EXPECT_EQ(prop, &prop->m_impl->getPropertyInstance()); - - for (size_t i = 0u; i < prop->getChildCount(); ++i) - props.push_back(prop->getChild(i)); - } - } - - // just check that the iterating over all props works - EXPECT_EQ(50, propsCount); - } -} diff --git a/client/logic/unittests/api/LogicEngineTest_SerializedSize.cpp b/client/logic/unittests/api/LogicEngineTest_SerializedSize.cpp deleted file mode 100644 index abf2a9b60..000000000 --- a/client/logic/unittests/api/LogicEngineTest_SerializedSize.cpp +++ /dev/null @@ -1,397 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2023 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/UniformInput.h" - -#include "RamsesTestUtils.h" -#include "FeatureLevelTestValues.h" - -namespace ramses::internal -{ - class ALogicEngine_SerializedSize : public ALogicEngineBase, public ::testing::TestWithParam - { - protected: - ALogicEngine_SerializedSize() - { - m_renderGroup->addMeshNode(*m_meshNode); - } - - LogicEngine m_logicEngine{ GetParam() }; - - RamsesTestSetup m_ramses; - ramses::Scene* m_scene = { m_ramses.createScene() }; - ramses::Node* m_node = { m_scene->createNode() }; - ramses::PerspectiveCamera* m_camera = { m_scene->createPerspectiveCamera() }; - ramses::Appearance* m_appearance = { &RamsesTestSetup::CreateTrivialTestAppearance(*m_scene) }; - ramses::RenderPass* m_renderPass = { m_scene->createRenderPass() }; - ramses::RenderGroup* m_renderGroup = { m_scene->createRenderGroup() }; - ramses::MeshNode* m_meshNode = { m_scene->createMeshNode("meshNode") }; - - const std::string_view m_valid_empty_script = R"( - function interface(IN,OUT) - end - function run(IN,OUT) - end - )"; - - const std::string_view m_valid_empty_interface = R"( - function interface(IN,OUT) - end - )"; - - const std::string_view m_module_source_code = R"( - local mymath = {} - function mymath.add(a,b) - return a+b - end - mymath.PI=3.1415 - return mymath - )"; - - const std::string_view m_script_source_code = R"( - function interface(IN,OUT) - IN.a = Type:Float() - IN.b = Type:Float() - OUT.value = Type:Float() - end - - function run(IN,OUT) - OUT.value = IN.a + IN.b - end - )"; - - const std::string_view m_script_with_module_source_code = R"( - modules("mymath") - - function interface(IN,OUT) - OUT.v = Type:Int32() - OUT.pi = Type:Float() - end - - function run(IN,OUT) - OUT.v = mymath.add(1,2) - OUT.pi = mymath.PI - end - )"; - - LuaScript* createScript(std::string_view source) - { - auto script = m_logicEngine.createLuaScript(source, {}, "script"); - EXPECT_NE(nullptr, script); - return script; - } - - LuaInterface* createInterface() - { - auto intf = m_logicEngine.createLuaInterface(m_valid_empty_interface, "intf"); - EXPECT_NE(nullptr, intf); - return intf; - } - - SkinBinding* createSkinBinding() - { - const auto node = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeForSkin"); - const auto appearance = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appearanceForSkin"); - ramses::UniformInput uniform; - appearance->getRamsesAppearance().getEffect().findUniformInput("jointMat", uniform); - EXPECT_TRUE(uniform.isValid()); - return m_logicEngine.createSkinBinding({ node }, { matrix44f{ 0.f } }, *appearance, uniform, "skin"); - } - }; - - static constexpr size_t EmptySerializedSizeTotal{ 164u }; - - INSTANTIATE_TEST_SUITE_P( - ALogicEngine_SerializedSizeTests, - ALogicEngine_SerializedSize, - ramses::internal::GetFeatureLevelTestValues()); - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithoutContent) - { - EXPECT_EQ(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), EmptySerializedSizeTotal); - - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 0u); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithInterface) - { - createInterface(); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 112u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithModule) - { - this->m_logicEngine.createLuaModule(m_module_source_code, {}, "module"); - const auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceCodeOnly); - EXPECT_EQ(result, 260u); - } - - // TODO figure out how to deal with difference in exported size on different platforms - // byte code has different size on 32bit arch (currently known) - TEST_P(ALogicEngine_SerializedSize, DISABLED_ChecksSerializedSizeWithModule_withByteCode) - { - this->m_logicEngine.createLuaModule(m_module_source_code, {}, "module"); - - auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::ByteCodeOnly); - EXPECT_EQ(result, 366u); - - result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceAndByteCode); - EXPECT_EQ(result, 550u); - - // default is source and bytecode - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), - this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceAndByteCode)); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithEmptyScript) - { - createScript(m_valid_empty_script); - const auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceCodeOnly); - EXPECT_EQ(result, 312u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(ELuaSavingMode::SourceCodeOnly), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithScript) - { - this->m_logicEngine.createLuaScript(m_script_source_code, {}, "script"); - const auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceCodeOnly); - EXPECT_EQ(result, 616u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(ELuaSavingMode::SourceCodeOnly), EmptySerializedSizeTotal); - } - - // TODO figure out how to deal with difference in exported size on different platforms - // byte code has different size on 32bit arch (currently known) - TEST_P(ALogicEngine_SerializedSize, DISABLED_ChecksSerializedSizeWithScript_withByteCode) - { - this->m_logicEngine.createLuaScript(m_script_source_code, {}, "script"); - - auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::ByteCodeOnly); - EXPECT_EQ(result, 912u); - - result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceAndByteCode); - EXPECT_EQ(result, 1184u); - - // default is source and bytecode - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), - this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceAndByteCode)); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithScriptAndModuleDependency) - { - LuaConfig config; - const auto module = this->m_logicEngine.createLuaModule(m_module_source_code, config, "module"); - config.addDependency("mymath", *module); - this->m_logicEngine.createLuaScript(m_script_with_module_source_code, config, "script"); - - const auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceCodeOnly); - EXPECT_EQ(result, 624u); - } - - // TODO figure out how to deal with difference in exported size on different platforms - // byte code has different size on 32bit arch (currently known) - TEST_P(ALogicEngine_SerializedSize, DISABLED_ChecksSerializedSizeWithScriptAndModuleDependency_withByteCode) - { - LuaConfig config; - const auto module = this->m_logicEngine.createLuaModule(m_module_source_code, config, "module"); - config.addDependency("mymath", *module); - this->m_logicEngine.createLuaScript(m_script_with_module_source_code, config, "script"); - - auto result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::ByteCodeOnly); - EXPECT_EQ(result, 1000u); - - result = this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceAndByteCode); - EXPECT_EQ(result, 1304u); - - // default is source and bytecode - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), - this->m_logicEngine.getSerializedSize(ELuaSavingMode::SourceAndByteCode)); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithNodeBinding) - { - this->m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto result = this->m_logicEngine.getSerializedSize(); - EXPECT_EQ(result, 440u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithAppearanceBinding) - { - this->m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "appearance"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 256u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCameraBinding) - { - this->m_logicEngine.createRamsesCameraBinding(*m_camera, "camera"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 632u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCameraBindingWithFrustumPlanes) - { - this->m_logicEngine.createRamsesCameraBindingWithFrustumPlanes(*m_camera, "camera"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 728u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithRenderPassBinding) - { - this->m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderpass"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 376u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithRenderGroupBinding) - { - RamsesRenderGroupBindingElements elements; - EXPECT_TRUE(elements.addElement(*m_meshNode)); - this->m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "rg"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 336u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithMeshNodeBinding) - { - this->m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 376u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - // Since there is a template specialization for animations collecting data arrays - // we test with different data arrays and reusing one single data array. - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithLinearAnimation_differentArrays) - { - // Test animation channel with different data arrays - auto dataArray1 = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data1"); - auto dataArray2 = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data2"); - ASSERT_NE(nullptr, dataArray1); - ASSERT_NE(nullptr, dataArray2); - - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray1, dataArray2, EInterpolationType::Linear }); - this->m_logicEngine.createAnimationNode(config, "animation"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 378u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithLinearAnimation_sameArray) - { - // Test animation channel with the same data arrays - auto data = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); - ASSERT_NE(nullptr, data); - - AnimationNodeConfig config; - config.addChannel({ "channel", data, data, EInterpolationType::Linear }); - this->m_logicEngine.createAnimationNode(config, "animation"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 378u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - // Since there is a template specialization for animations collecting data arrays - // we test with different data arrays and reusing one single data array. - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCubicAnimation_differentArrays) - { - // Test animation channel with different data arrays - auto dataArray1 = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data1"); - auto dataArray2 = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data2"); - auto dataArray3 = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data3"); - auto dataArray4 = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data4"); - ASSERT_NE(nullptr, dataArray1); - ASSERT_NE(nullptr, dataArray2); - ASSERT_NE(nullptr, dataArray3); - ASSERT_NE(nullptr, dataArray4); - - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray1, dataArray2, EInterpolationType::Cubic, dataArray3, dataArray4 }); - this->m_logicEngine.createAnimationNode(config, "animation"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 378u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCubicAnimation_sameArray) - { - // Test animation channel with the same data arrays - auto data = this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); - ASSERT_NE(nullptr, data); - - AnimationNodeConfig config; - config.addChannel({ "channel", data, data, EInterpolationType::Cubic, data, data }); - this->m_logicEngine.createAnimationNode(config, "animation"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 378u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithDataArray) - { - this->m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 98u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithTimer) - { - this->m_logicEngine.createTimerNode("timer"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 290u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithAnchorPoint) - { - const auto node = this->m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto camera = this->m_logicEngine.createRamsesCameraBinding(*m_camera, "camera"); - ASSERT_TRUE(node && camera); - this->m_logicEngine.createAnchorPoint(*node, *camera, "timer"); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 248u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } - - TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithSkinBinding) - { - createSkinBinding(); - EXPECT_EQ(this->m_logicEngine.getSerializedSize(), 184u); - EXPECT_GT(this->m_logicEngine.getTotalSerializedSize(), EmptySerializedSizeTotal); - } -} diff --git a/client/logic/unittests/api/LogicNodeTest.cpp b/client/logic/unittests/api/LogicNodeTest.cpp deleted file mode 100644 index f6c0aea7f..000000000 --- a/client/logic/unittests/api/LogicNodeTest.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gmock/gmock.h" - -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/Property.h" -#include "impl/LogicNodeImpl.h" -#include "impl/PropertyImpl.h" - -namespace ramses::internal -{ - class LogicNodeDummy : public LogicNode - { - public: - explicit LogicNodeDummy(std::unique_ptr impl) - : LogicNode(std::move(impl)) - { - m_impl.setLogicObject(*this); - } - }; - - class LogicNodeImplMock : public LogicNodeImpl - { - public: - // Forwarding constructor - explicit LogicNodeImplMock(std::string_view name) - : LogicNodeImpl(name, 1u) - { - } - - // Expose protected method for testing - using LogicNodeImpl::setRootProperties; - - MOCK_METHOD(std::optional, update, (), (override, final)); - MOCK_METHOD(void, createRootProperties, (), (override, final)); - }; - - class ALogicNodeImpl : public ::testing::Test - { - }; - - TEST_F(ALogicNodeImpl, RemembersNameGivenInConstructor) - { - const LogicNodeImplMock logicNode("name"); - EXPECT_EQ(logicNode.getName(), "name"); - } - - TEST_F(ALogicNodeImpl, CanReceiveNewName) - { - LogicNodeImplMock logicNode("name"); - logicNode.setName("newName"); - EXPECT_EQ(logicNode.getName(), "newName"); - } - - TEST_F(ALogicNodeImpl, DirtyByDefault) - { - const LogicNodeImplMock logicNode(""); - EXPECT_TRUE(logicNode.isDirty()); - } - - TEST_F(ALogicNodeImpl, DirtyWhenSetDirty) - { - LogicNodeImplMock logicNode(""); - logicNode.setDirty(false); - EXPECT_FALSE(logicNode.isDirty()); - logicNode.setDirty(true); - EXPECT_TRUE(logicNode.isDirty()); - } - - TEST_F(ALogicNodeImpl, TakesOwnershipOfGivenProperties) - { - const HierarchicalTypeData nestedTypeData( - TypeData{ "root", EPropertyType::Struct }, { - HierarchicalTypeData(TypeData{"nested", EPropertyType::Struct}, { - MakeType("float", EPropertyType::Float), - MakeStruct("nested", { TypeData("float", EPropertyType::Float) }) - }) - }); - - auto inputType = nestedTypeData; - auto outputType = nestedTypeData; - // These usually come from subclasses deserialization code - auto inputs = std::make_unique(inputType, EPropertySemantics::ScriptInput); - auto outputs = std::make_unique(outputType, EPropertySemantics::ScriptOutput); - - LogicNodeDummy logicNode{ std::make_unique("name") }; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) known test type - (static_cast(logicNode.m_impl)).setRootProperties(std::move(inputs), std::move(outputs)); - - // const outputs - { - const Property* root = logicNode.getOutputs(); - EXPECT_EQ(&logicNode, &root->getOwningLogicNode()); - ASSERT_EQ(1u, root->getChildCount()); - - const auto nested = root->getChild(0); - EXPECT_EQ(&logicNode, &nested->getOwningLogicNode()); - ASSERT_EQ(2u, nested->getChildCount()); - - const auto nestedFloat = nested->getChild(0); - EXPECT_EQ(&logicNode, &nestedFloat->getOwningLogicNode()); - - const auto nestedNested = nested->getChild(1); - EXPECT_EQ(&logicNode, &nestedNested->getOwningLogicNode()); - ASSERT_EQ(1u, nestedNested->getChildCount()); - - const auto nestedNestedFloat = nestedNested->getChild(0); - EXPECT_EQ(&logicNode, &nestedNestedFloat->getOwningLogicNode()); - } - - // non-const inputs - { - Property* root = logicNode.getInputs(); - EXPECT_EQ(&logicNode, &root->getOwningLogicNode()); - ASSERT_EQ(1u, root->getChildCount()); - - const auto nested = root->getChild(0); - EXPECT_EQ(&logicNode, &nested->getOwningLogicNode()); - ASSERT_EQ(2u, nested->getChildCount()); - - const auto nestedFloat = nested->getChild(0); - EXPECT_EQ(&logicNode, &nestedFloat->getOwningLogicNode()); - - const auto nestedNested = nested->getChild(1); - EXPECT_EQ(&logicNode, &nestedNested->getOwningLogicNode()); - ASSERT_EQ(1u, nestedNested->getChildCount()); - - const auto nestedNestedFloat = nestedNested->getChild(0); - EXPECT_EQ(&logicNode, &nestedNestedFloat->getOwningLogicNode()); - } - } -} diff --git a/client/logic/unittests/api/RamsesCameraBindingTest.cpp b/client/logic/unittests/api/RamsesCameraBindingTest.cpp deleted file mode 100644 index 0ff96f40c..000000000 --- a/client/logic/unittests/api/RamsesCameraBindingTest.cpp +++ /dev/null @@ -1,1573 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "RamsesObjectResolverMock.h" -#include "RamsesTestUtils.h" -#include "SerializationTestUtils.h" -#include "WithTempDirectory.h" - -#include "impl/LogicEngineImpl.h" -#include "impl/RamsesCameraBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/RamsesHelper.h" -#include "generated/RamsesCameraBindingGen.h" - -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/RamsesCameraBinding.h" - -#include "ramses-utils.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" - -namespace ramses::internal -{ - constexpr int32_t DefaultViewportOffsetX = 0; - constexpr int32_t DefaultViewportOffsetY = 0; - constexpr uint32_t DefaultViewportWidth = 16u; - constexpr uint32_t DefaultViewportHeight = 16u; - - constexpr float NearPlaneDefault = 0.1f; - constexpr float FarPlaneDefault = 1.0f; - - constexpr float PerspectiveFrustumFOVdefault = 168.579f; - constexpr float PerspectiveFrustumARdefault = 1.f; - - constexpr float OrthoFrustumLPdefault = -1.f; - constexpr float OrthoFrustumRPdefault = 1.f; - constexpr float OrthoFrustumBPdefault = -1.f; - constexpr float OrthoFrustumTPdefault = 1.0f; - - class ARamsesCameraBinding : public ALogicEngine - { - protected: - ARamsesCameraBinding() - : m_testScene(*m_ramsesTestSetup.createScene(ramses::sceneId_t(1))) - { - } - - static void ExpectPropertyTypeAndChildCount(const Property* prop, EPropertyType type, uint32_t childCount) - { - ASSERT_NE(nullptr, prop); - EXPECT_EQ(type, prop->getType()); - EXPECT_EQ(childCount, prop->getChildCount()); - } - - static void ExpectDefaultViewportValues(const ramses::Camera& camera) - { - EXPECT_EQ(camera.getViewportX(), DefaultViewportOffsetX); - EXPECT_EQ(camera.getViewportY(), DefaultViewportOffsetY); - EXPECT_EQ(camera.getViewportWidth(), DefaultViewportWidth); - EXPECT_EQ(camera.getViewportHeight(), DefaultViewportHeight); - } - - static void ExpectDefaultPerspectiveCameraFrustumValues(const ramses::PerspectiveCamera& camera) - { - EXPECT_NEAR(camera.getVerticalFieldOfView(), PerspectiveFrustumFOVdefault, 0.001f); - EXPECT_EQ(camera.getAspectRatio(), PerspectiveFrustumARdefault); - EXPECT_EQ(camera.getNearPlane(), NearPlaneDefault); - EXPECT_EQ(camera.getFarPlane(), FarPlaneDefault); - } - - static void ExpectDefaultCameraFrustumPlanes(const ramses::Camera& camera) - { - EXPECT_EQ(camera.getLeftPlane(), OrthoFrustumLPdefault); - EXPECT_EQ(camera.getRightPlane(), OrthoFrustumRPdefault); - EXPECT_EQ(camera.getBottomPlane(), OrthoFrustumBPdefault); - EXPECT_EQ(camera.getTopPlane(), OrthoFrustumTPdefault); - EXPECT_EQ(camera.getNearPlane(), NearPlaneDefault); - EXPECT_EQ(camera.getFarPlane(), FarPlaneDefault); - } - - static void ExpectDefaultValues(const ramses::RamsesCameraBinding& cameraBinding) - { - ExpectDefaultViewportValues(cameraBinding.getRamsesCamera()); - if (cameraBinding.m_cameraBinding.hasFrustumPlanesProperties()) - { - ExpectDefaultCameraFrustumPlanes(cameraBinding.getRamsesCamera()); - } - else - { - ExpectDefaultPerspectiveCameraFrustumValues(*ramses::RamsesUtils::TryConvert(cameraBinding.getRamsesCamera())); - } - } - - static void ExpectInputPropertiesWithFrustumPlanes(const RamsesCameraBindingImpl& cameraBinding) - { - EXPECT_TRUE(cameraBinding.hasFrustumPlanesProperties()); - - const auto inputs = cameraBinding.getInputs(); - ASSERT_EQ(2u, inputs->getChildCount()); - - const auto vpProperties = inputs->getChild("viewport"); - const auto frustum = inputs->getChild("frustum"); - ASSERT_EQ(4u, vpProperties->getChildCount()); - ASSERT_EQ(6u, frustum->getChildCount()); - - const auto vpOffsetX = vpProperties->getChild("offsetX"); - const auto vpOffsety = vpProperties->getChild("offsetY"); - const auto vpWidth = vpProperties->getChild("width"); - const auto vpHeight = vpProperties->getChild("height"); - const auto nP = frustum->getChild("nearPlane"); - const auto fP = frustum->getChild("farPlane"); - const auto lp = frustum->getChild("leftPlane"); - const auto rP = frustum->getChild("rightPlane"); - const auto bP = frustum->getChild("bottomPlane"); - const auto tP = frustum->getChild("topPlane"); - - // Test that internal indices match properties resolved by name - EXPECT_EQ(vpProperties, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))); - EXPECT_EQ(frustum, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))); - - EXPECT_EQ(vpOffsetX, vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))); - EXPECT_EQ(vpOffsety, vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))); - EXPECT_EQ(vpWidth, vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))); - EXPECT_EQ(vpHeight, vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))); - EXPECT_EQ(nP, frustum->m_impl->getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::NearPlane))); - EXPECT_EQ(fP, frustum->m_impl->getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::FarPlane))); - EXPECT_EQ(lp, frustum->m_impl->getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::LeftPlane))); - EXPECT_EQ(rP, frustum->m_impl->getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::RightPlane))); - EXPECT_EQ(bP, frustum->m_impl->getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::BottomPlane))); - EXPECT_EQ(tP, frustum->m_impl->getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::TopPlane))); - - ExpectPropertyTypeAndChildCount(inputs->getChild("viewport"), EPropertyType::Struct, 4); - ExpectPropertyTypeAndChildCount(inputs->getChild("frustum"), EPropertyType::Struct, 6); - - ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetX"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetY"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(vpProperties->getChild("width"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(vpProperties->getChild("height"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("nearPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("farPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("leftPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("rightPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("bottomPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("topPlane"), EPropertyType::Float, 0); - } - - static void ExpectInputPropertiesWithoutFrustumPlanes(const RamsesCameraBindingImpl& cameraBinding) - { - EXPECT_FALSE(cameraBinding.hasFrustumPlanesProperties()); - - const auto inputs = cameraBinding.getInputs(); - ASSERT_EQ(2u, inputs->getChildCount()); - - const auto vpProperties = inputs->getChild("viewport"); - const auto frustum = inputs->getChild("frustum"); - ASSERT_EQ(4u, vpProperties->getChildCount()); - ASSERT_EQ(4u, frustum->getChildCount()); - - const auto vpOffsetX = vpProperties->getChild("offsetX"); - const auto vpOffsety = vpProperties->getChild("offsetY"); - const auto vpWidth = vpProperties->getChild("width"); - const auto vpHeight = vpProperties->getChild("height"); - - const auto nP = frustum->getChild("nearPlane"); - const auto fP = frustum->getChild("farPlane"); - const auto fov = frustum->getChild("fieldOfView"); - const auto aR = frustum->getChild("aspectRatio"); - - // Test that internal indices match properties resolved by name - EXPECT_EQ(vpProperties, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))); - EXPECT_EQ(frustum, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))); - - EXPECT_EQ(vpOffsetX, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))); - EXPECT_EQ(vpOffsety, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))); - EXPECT_EQ(vpWidth, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))); - EXPECT_EQ(vpHeight, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))); - EXPECT_EQ(nP, frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::NearPlane))); - EXPECT_EQ(fP, frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FarPlane))); - EXPECT_EQ(fov, frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))); - EXPECT_EQ(aR, frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))); - - ExpectPropertyTypeAndChildCount(inputs->getChild("viewport"), EPropertyType::Struct, 4); - ExpectPropertyTypeAndChildCount(inputs->getChild("frustum"), EPropertyType::Struct, 4); - - ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetX"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetY"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(vpProperties->getChild("width"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(vpProperties->getChild("height"), EPropertyType::Int32, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("nearPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("farPlane"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("fieldOfView"), EPropertyType::Float, 0); - ExpectPropertyTypeAndChildCount(frustum->getChild("aspectRatio"), EPropertyType::Float, 0); - } - - RamsesTestSetup m_ramsesTestSetup; - ramses::Scene& m_testScene; - ramses::OrthographicCamera& m_orthoCam = { *m_testScene.createOrthographicCamera() }; - ramses::PerspectiveCamera& m_perspectiveCam = { *m_testScene.createPerspectiveCamera() }; - }; - - TEST_F(ARamsesCameraBinding, HasANameAfterCreation) - { - auto& cameraBinding = *m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - EXPECT_EQ("CameraBinding", cameraBinding.getName()); - } - - TEST_F(ARamsesCameraBinding, HasAIdAfterCreation) - { - auto& cameraBinding = *m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - EXPECT_EQ(cameraBinding.getId(), 1u); - } - - TEST_F(ARamsesCameraBinding, HasNoOutputsAfterCreation) - { - auto& cameraBinding = *m_logicEngine.createRamsesCameraBinding(*m_camera, ""); - EXPECT_EQ(nullptr, cameraBinding.getOutputs()); - } - - TEST_F(ARamsesCameraBinding, ProducesNoErrorsDuringUpdate_IfNoRamsesCameraIsAssigned) - { - auto& cameraBinding = *m_logicEngine.createRamsesCameraBinding(*m_camera, ""); - EXPECT_EQ(std::nullopt, cameraBinding.m_impl.update()); - } - - TEST_F(ARamsesCameraBinding, ReturnsReferenceToRamsesCamera) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - EXPECT_EQ(&m_perspectiveCam, &cameraBinding.getRamsesCamera()); - } - - TEST_F(ARamsesCameraBinding, HasInputsAfterInitializingWithPerspectiveCamera) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - ExpectInputPropertiesWithoutFrustumPlanes(cameraBinding.m_cameraBinding); - } - - TEST_F(ARamsesCameraBinding, HasInputsAfterInitializingFromOrthoCamera) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - ExpectInputPropertiesWithFrustumPlanes(cameraBinding.m_cameraBinding); - } - - TEST_F(ARamsesCameraBinding, HasInputsAfterInitializingFromOrthoCamera_createdWithFrustumPlanes) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBindingWithFrustumPlanes(m_orthoCam); - ExpectInputPropertiesWithFrustumPlanes(cameraBinding.m_cameraBinding); - } - - TEST_F(ARamsesCameraBinding, HasInputsAfterInitializingFromPerspectiveCamera_createdWithFrustumPlanes) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBindingWithFrustumPlanes(m_perspectiveCam); - ExpectInputPropertiesWithFrustumPlanes(cameraBinding.m_cameraBinding); - } - - TEST_F(ARamsesCameraBinding, DoesNotOverwriteDefaultValues_WhenCreatedFromOrthoCamera) - { - m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - m_logicEngine.update(); - - //Expect default values on the camera, because nothing was set so far - ExpectDefaultViewportValues(m_orthoCam); - ExpectDefaultCameraFrustumPlanes(m_orthoCam); - } - - TEST_F(ARamsesCameraBinding, DoesNotOverwriteDefaultValues_WhenCreatedFromPerspectiveCamera) - { - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - m_logicEngine.update(); - - //Expect default values on the camera, because nothing was set so far - ExpectDefaultViewportValues(m_perspectiveCam); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - } - - TEST_F(ARamsesCameraBinding, ReportsErrorOnUpdate_WhenSettingZeroToViewportSize) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - const auto inputs = cameraBinding.getInputs(); - auto vpProperties = inputs->getChild("viewport"); - // Setting illegal viewport values: width and height cannot be 0 so an error will be produced on ramses camera - vpProperties->getChild("width")->set(0); - - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Camera viewport size must be positive! (width: 0; height: 16)"); - - // Fix width, break height -> still generates error - vpProperties->getChild("width")->set(8); - vpProperties->getChild("height")->set(0); - - // Expect default values on the camera, because setting values failed - ExpectDefaultViewportValues(m_orthoCam); - - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Camera viewport size must be positive! (width: 8; height: 0)"); - - // Fix height and update recovers the errors - vpProperties->getChild("height")->set(32); - EXPECT_TRUE(m_logicEngine.update()); - - EXPECT_EQ(m_orthoCam.getViewportWidth(), 8u); - EXPECT_EQ(m_orthoCam.getViewportHeight(), 32u); - } - - TEST_F(ARamsesCameraBinding, ReportsErrorOnUpdate_WhenSettingNegativeViewportSize) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - const auto inputs = cameraBinding.getInputs(); - auto vpProperties = inputs->getChild("viewport"); - // Setting illegal viewport values: width and height cannot be 0 so an error will be produced on ramses camera - vpProperties->getChild("width")->set(-1); - vpProperties->getChild("height")->set(-1); - - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Camera viewport size must be positive! (width: -1; height: -1)"); - - // Setting positive values recovers from the error - vpProperties->getChild("width")->set(10); - vpProperties->getChild("height")->set(12); - EXPECT_TRUE(m_logicEngine.update()); - - EXPECT_EQ(m_orthoCam.getViewportWidth(), 10u); - EXPECT_EQ(m_orthoCam.getViewportHeight(), 12u); - } - - TEST_F(ARamsesCameraBinding, ReportsErrorOnUpdate_WhenSettingInvalidFrustumValuesOnOrthoCamera) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - - auto frustum = cameraBinding.getInputs()->getChild("frustum"); - // Setting illegal frustum values: left plane cannot be smaller than right plane so an error will be produced on ramses camera - frustum->getChild("leftPlane")->set(2.f); - frustum->getChild("rightPlane")->set(1.f); - - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Camera::setFrustum failed - check validity of given frustum planes"); - - //Still expect default values on the camera, because setting values failed - ExpectDefaultCameraFrustumPlanes(m_orthoCam); - - // Recovers from the error once values are ok - frustum->getChild("rightPlane")->set(3.f); - EXPECT_TRUE(m_logicEngine.update()); - } - - TEST_F(ARamsesCameraBinding, ReportsErrorOnUpdate_WhenSettingInvalidFrustumValuesOnPerspectiveCamera) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - auto frustum = cameraBinding.getInputs()->getChild("frustum"); - // Setting illegal frustum values: fov and aspect ratio cannot be 0 so an error will be produced on ramses camera - frustum->getChild("fieldOfView")->set(0.f); - frustum->getChild("aspectRatio")->set(0.f); - - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "PerspectiveCamera::setFrustum failed - check validity of given frustum planes"); - - // Fixing just the FOV does not fix the issue, need to also fix aspect ratio - frustum->getChild("fieldOfView")->set(15.f); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "PerspectiveCamera::setFrustum failed - check validity of given frustum planes"); - - //Still expect default values on the camera, because setting values failed - ExpectDefaultViewportValues(m_perspectiveCam); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - frustum->getChild("aspectRatio")->set(1.f); - EXPECT_TRUE(m_logicEngine.update()); - } - - TEST_F(ARamsesCameraBinding, InitializesInputPropertiesOfPerpespectiveCameraToMatchRamsesDefaultValues) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - const auto inputs = cameraBinding.getInputs(); - ASSERT_NE(nullptr, inputs); - - const auto vpProperties = inputs->getChild("viewport"); - const auto frustum = inputs->getChild("frustum"); - ASSERT_EQ(4u, vpProperties->getChildCount()); - ASSERT_EQ(4u, frustum->getChildCount()); - - EXPECT_EQ(*vpProperties->getChild("offsetX")->get(), m_perspectiveCam.getViewportX()); - EXPECT_EQ(*vpProperties->getChild("offsetY")->get(), m_perspectiveCam.getViewportY()); - EXPECT_EQ(static_cast(*vpProperties->getChild("width")->get()), m_perspectiveCam.getViewportWidth()); - EXPECT_EQ(static_cast(*vpProperties->getChild("height")->get()), m_perspectiveCam.getViewportHeight()); - - EXPECT_EQ(*frustum->getChild("nearPlane")->get(), m_perspectiveCam.getNearPlane()); - EXPECT_EQ(*frustum->getChild("farPlane")->get(), m_perspectiveCam.getFarPlane()); - EXPECT_NEAR(*frustum->getChild("fieldOfView")->get(), m_perspectiveCam.getVerticalFieldOfView(), 0.001f); - EXPECT_EQ(*frustum->getChild("aspectRatio")->get(), m_perspectiveCam.getAspectRatio()); - } - - TEST_F(ARamsesCameraBinding, InitializesInputPropertiesOfOrthographicCameraToMatchRamsesDefaultValues) - { - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - - const auto inputs = cameraBinding.getInputs(); - ASSERT_NE(nullptr, inputs); - - const auto vpProperties = inputs->getChild("viewport"); - const auto frustum = inputs->getChild("frustum"); - ASSERT_EQ(4u, vpProperties->getChildCount()); - ASSERT_EQ(6u, frustum->getChildCount()); - - EXPECT_EQ(*vpProperties->getChild("offsetX")->get(), m_orthoCam.getViewportX()); - EXPECT_EQ(*vpProperties->getChild("offsetY")->get(), m_orthoCam.getViewportY()); - EXPECT_EQ(static_cast(*vpProperties->getChild("width")->get()), m_orthoCam.getViewportWidth()); - EXPECT_EQ(static_cast(*vpProperties->getChild("height")->get()), m_orthoCam.getViewportHeight()); - - EXPECT_EQ(*frustum->getChild("nearPlane")->get(), m_orthoCam.getNearPlane()); - EXPECT_EQ(*frustum->getChild("farPlane")->get(), m_orthoCam.getFarPlane()); - EXPECT_EQ(*frustum->getChild("leftPlane")->get(), m_orthoCam.getLeftPlane()); - EXPECT_EQ(*frustum->getChild("rightPlane")->get(), m_orthoCam.getRightPlane()); - EXPECT_EQ(*frustum->getChild("bottomPlane")->get(), m_orthoCam.getBottomPlane()); - EXPECT_EQ(*frustum->getChild("topPlane")->get(), m_orthoCam.getTopPlane()); - } - - TEST_F(ARamsesCameraBinding, MarksInputsAsBindingInputsForPerspectiveCameraBinding) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - const auto inputs = cameraBinding->getInputs(); - for (size_t i = 0; i < inputs->getChildCount(); ++i) - { - const auto inputStruct = inputs->getChild(i); - EXPECT_EQ(EPropertySemantics::BindingInput, inputStruct->m_impl->getPropertySemantics()); - - for (size_t j = 0; j < inputs->getChild(i)->getChildCount(); ++j) - { - const auto inputProperty = inputStruct->m_impl->getChild(j); - EXPECT_EQ(EPropertySemantics::BindingInput, inputProperty->m_impl->getPropertySemantics()); - } - } - } - - TEST_F(ARamsesCameraBinding, MarksInputsAsBindingInputsForOrthoCameraBinding) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - - const auto inputs = cameraBinding->getInputs(); - for (size_t i = 0; i < inputs->getChildCount(); ++i) - { - const auto inputStruct = inputs->getChild(i); - EXPECT_EQ(EPropertySemantics::BindingInput, inputStruct->m_impl->getPropertySemantics()); - - for (size_t j = 0; j < inputs->getChild(i)->getChildCount(); ++j) - { - const auto inputProperty = inputStruct->m_impl->getChild(j); - EXPECT_EQ(EPropertySemantics::BindingInput, inputProperty->m_impl->getPropertySemantics()); - } - } - } - - TEST_F(ARamsesCameraBinding, ReturnsBoundRamsesCamera) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - EXPECT_EQ(&m_perspectiveCam, &cameraBinding->getRamsesCamera()); - } - - TEST_F(ARamsesCameraBinding, DoesNotModifyRamsesWithoutUpdateBeingCalledWithPerspectiveCamera) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - auto inputs = cameraBinding->getInputs(); - auto vpProperties = inputs->getChild("viewport"); - auto frustum = inputs->getChild("frustum"); - - vpProperties->getChild("offsetX")->set(4); - vpProperties->getChild("offsetY")->set(7); - vpProperties->getChild("width")->set(11); - vpProperties->getChild("height")->set(19); - - frustum->getChild("nearPlane")->set(3.1f); - frustum->getChild("farPlane")->set(.2f); - frustum->getChild("fieldOfView")->set(4.2f); - frustum->getChild("aspectRatio")->set(2.1f); - - ExpectDefaultValues(*cameraBinding); - } - - TEST_F(ARamsesCameraBinding, DoesNotModifyRamsesWithoutUpdateBeingCalledWithOrthoCamera) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - - auto inputs = cameraBinding->getInputs(); - auto vpProperties = inputs->getChild("viewport"); - auto frustum = inputs->getChild("frustum"); - - vpProperties->getChild("offsetX")->set(4); - vpProperties->getChild("offsetY")->set(7); - vpProperties->getChild("width")->set(11); - vpProperties->getChild("height")->set(19); - - frustum->getChild("nearPlane")->set(3.1f); - frustum->getChild("farPlane")->set(.2f); - frustum->getChild("leftPlane")->set(6.2f); - frustum->getChild("rightPlane")->set(2.8f); - frustum->getChild("bottomPlane")->set(1.9f); - frustum->getChild("topPlane")->set(7.1f); - - ExpectDefaultValues(*cameraBinding); - } - - TEST_F(ARamsesCameraBinding, ModifiesRamsesPerspectiveCamOnUpdate_OnlyAfterExplicitlyAssignedToInputs) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - auto inputs = cameraBinding->getInputs(); - auto vpProperties = inputs->getChild("viewport"); - auto frustum = inputs->getChild("frustum"); - - const int32_t newVpOffsetX = 23; - vpProperties->getChild("offsetX")->set(newVpOffsetX); - - // Update not called yet -> still default values - ExpectDefaultValues(*cameraBinding); - - cameraBinding->m_cameraBinding.update(); - // Only propagated vpOffsetX, the others have default values - EXPECT_EQ(m_perspectiveCam.getViewportX(), newVpOffsetX); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 0); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 16u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 16u); - - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - // Set and test all properties - const int32_t newVpOffsetY = 13; - const int32_t newVpWidth = 56; - const int32_t newVpHeight = 45; - - const float newFov = 30.f; - const float newAR = 640.f / 480.f; - const float newNearPlane = 4.4f; - const float newFarPlane = 5.1f; - - vpProperties->getChild("offsetY")->set(newVpOffsetY); - vpProperties->getChild("width")->set(newVpWidth); - vpProperties->getChild("height")->set(newVpHeight); - - frustum->getChild("fieldOfView")->set(newFov); - frustum->getChild("aspectRatio")->set(newAR); - frustum->getChild("nearPlane")->set(newNearPlane); - frustum->getChild("farPlane")->set(newFarPlane); - cameraBinding->m_cameraBinding.update(); - - EXPECT_EQ(m_perspectiveCam.getViewportX(), newVpOffsetX); - EXPECT_EQ(m_perspectiveCam.getViewportY(), newVpOffsetY); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), static_cast(newVpWidth)); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), static_cast(newVpHeight)); - - EXPECT_NEAR(m_perspectiveCam.getVerticalFieldOfView(), newFov, 0.001f); - EXPECT_EQ(m_perspectiveCam.getAspectRatio(), newAR); - EXPECT_EQ(m_perspectiveCam.getNearPlane(), newNearPlane); - EXPECT_EQ(m_perspectiveCam.getFarPlane(), newFarPlane); - } - - TEST_F(ARamsesCameraBinding, ModifiesRamsesOrthoCamOnUpdate_OnlyAfterExplicitlyAssignedToInputs) - { - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - - auto inputs = cameraBinding->getInputs(); - auto vpProperties = inputs->getChild("viewport"); - auto frustum = inputs->getChild("frustum"); - - const int32_t newVpOffsetX = 23; - vpProperties->getChild("offsetX")->set(newVpOffsetX); - - // Update not called yet -> still default values - ExpectDefaultValues(*cameraBinding); - - cameraBinding->m_cameraBinding.update(); - // Only propagated vpOffsetX, the others have default values - EXPECT_EQ(m_orthoCam.getViewportX(), newVpOffsetX); - EXPECT_EQ(m_orthoCam.getViewportY(), 0); - EXPECT_EQ(m_orthoCam.getViewportWidth(), 16u); - EXPECT_EQ(m_orthoCam.getViewportHeight(), 16u); - - ExpectDefaultCameraFrustumPlanes(m_orthoCam); - - // Set and test all properties - const int32_t newVpOffsetY = 13; - const int32_t newVpWidth = 56; - const int32_t newVpHeight = 45; - - const float newLeftPlane = 0.2f; - const float newRightPlane = 0.3f; - const float newBottomPlane = 0.4f; - const float newTopPlane = 0.5f; - const float newNearPlane = 4.f; - const float newFarPlane = 5.1f; - - vpProperties->getChild("offsetY")->set(newVpOffsetY); - vpProperties->getChild("width")->set(newVpWidth); - vpProperties->getChild("height")->set(newVpHeight); - - frustum->getChild("leftPlane")->set(newLeftPlane); - frustum->getChild("rightPlane")->set(newRightPlane); - frustum->getChild("bottomPlane")->set(newBottomPlane); - frustum->getChild("topPlane")->set(newTopPlane); - frustum->getChild("nearPlane")->set(newNearPlane); - frustum->getChild("farPlane")->set(newFarPlane); - cameraBinding->m_cameraBinding.update(); - - EXPECT_EQ(m_orthoCam.getViewportX(), newVpOffsetX); - EXPECT_EQ(m_orthoCam.getViewportY(), newVpOffsetY); - EXPECT_EQ(m_orthoCam.getViewportWidth(), static_cast(newVpWidth)); - EXPECT_EQ(m_orthoCam.getViewportHeight(), static_cast(newVpHeight)); - - EXPECT_EQ(m_orthoCam.getLeftPlane(), newLeftPlane); - EXPECT_EQ(m_orthoCam.getRightPlane(), newRightPlane); - EXPECT_EQ(m_orthoCam.getBottomPlane(), newBottomPlane); - EXPECT_EQ(m_orthoCam.getTopPlane(), newTopPlane); - EXPECT_EQ(m_orthoCam.getNearPlane(), newNearPlane); - EXPECT_EQ(m_orthoCam.getFarPlane(), newFarPlane); - } - - TEST_F(ARamsesCameraBinding, PropagatesItsInputsToRamsesPerspectiveCameraOnUpdate_WithLinksInsteadOfSetCall) - { - const std::string_view scriptSrc = R"( - function interface(IN,OUT) - OUT.vpProps = { - vpX = Type:Int32(), - vpY = Type:Int32(), - vpW = Type:Int32(), - vpH = Type:Int32() - } - end - function run(IN,OUT) - OUT.vpProps = { - vpX = 5, - vpY = 10, - vpW = 35, - vpH = 19 - } - end - )"; - - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vpProps")->getChild("vpX"), *cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vpProps")->getChild("vpY"), *cameraBinding->getInputs()->getChild("viewport")->getChild("offsetY"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vpProps")->getChild("vpW"), *cameraBinding->getInputs()->getChild("viewport")->getChild("width"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vpProps")->getChild("vpH"), *cameraBinding->getInputs()->getChild("viewport")->getChild("height"))); - - // Links have no effect before update() explicitly called - ExpectDefaultValues(*cameraBinding); - - m_logicEngine.update(); - - // Linked values got updates, not-linked values were not modified - EXPECT_EQ(m_perspectiveCam.getViewportX(), 5); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 10); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 35u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 19u); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - } - - TEST_F(ARamsesCameraBinding, PropagatesItsInputsToRamsesOrthoCameraOnUpdate_WithLinksInsteadOfSetCall) - { - const std::string_view scriptSrc = R"( - function interface(IN,OUT) - OUT.frustProps = { - lP = Type:Float(), - rP = Type:Float(), - bP = Type:Float(), - tP = Type:Float(), - nP = Type:Float(), - fP = Type:Float() - } - end - function run(IN,OUT) - OUT.frustProps = { - lP = 0.2, - rP = 0.3, - bP = 0.4, - tP = 0.5, - nP = 0.6, - fP = 0.7 - } - end - )"; - - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - - auto* cameraBinding = m_logicEngine.createRamsesCameraBinding(m_orthoCam, ""); - - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("lP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("leftPlane"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("rP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("rightPlane"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("bP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("bottomPlane"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("tP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("topPlane"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("nP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("nearPlane"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("fP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("farPlane"))); - - // Links have no effect before update() explicitly called - ExpectDefaultValues(*cameraBinding); - - m_logicEngine.update(); - - // Linked values got updates, not-linked values were not modified - EXPECT_EQ(m_orthoCam.getLeftPlane(), 0.2f); - EXPECT_EQ(m_orthoCam.getRightPlane(), 0.3f); - EXPECT_EQ(m_orthoCam.getBottomPlane(), 0.4f); - EXPECT_EQ(m_orthoCam.getTopPlane(), 0.5f); - EXPECT_EQ(m_orthoCam.getNearPlane(), 0.6f); - EXPECT_EQ(m_orthoCam.getFarPlane(), 0.7f); - ExpectDefaultViewportValues(m_orthoCam); - } - - TEST_F(ARamsesCameraBinding, DoesNotOverrideExistingValuesAfterRamsesCameraIsAssignedToBinding) - { - m_perspectiveCam.setViewport(3, 4, 10u, 11u); - m_perspectiveCam.setFrustum(30.f, 640.f / 480.f, 2.3f, 5.6f); - - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - - EXPECT_EQ(m_perspectiveCam.getViewportX(), 3); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 4); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 10u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 11u); - - EXPECT_NEAR(m_perspectiveCam.getVerticalFieldOfView(), 30.f, 0.001f); - EXPECT_EQ(m_perspectiveCam.getAspectRatio(), 640.f / 480.f); - EXPECT_EQ(m_perspectiveCam.getNearPlane(), 2.3f); - EXPECT_EQ(m_perspectiveCam.getFarPlane(), 5.6f); - } - - // This fixture only contains serialization unit tests, for higher order tests see `ARamsesCameraBinding_SerializationWithFile` - class ARamsesCameraBinding_SerializationLifecycle : public ARamsesCameraBinding - { - protected: - flatbuffers::Offset serializeRootInput(bool withFrustumPlanes, bool withError = false) - { - std::vector frustumPlanes = { - TypeData{ "nearPlane", EPropertyType::Float }, - TypeData{ "farPlane", EPropertyType::Float }, - }; - - if (withFrustumPlanes) - { - frustumPlanes.emplace_back("leftPlane", EPropertyType::Float); - frustumPlanes.emplace_back("rightPlane", EPropertyType::Float); - frustumPlanes.emplace_back("bottomPlane", EPropertyType::Float); - if (!withError) - frustumPlanes.emplace_back("topPlane", EPropertyType::Float); - } - else - { - frustumPlanes.emplace_back("fieldOfView", EPropertyType::Float); - frustumPlanes.emplace_back("aspectRatio", EPropertyType::Float); - } - - HierarchicalTypeData cameraBindingInputs( - TypeData{ "", EPropertyType::Struct }, - { - MakeStruct("viewport", - { - TypeData{"offsetX", EPropertyType::Int32}, - TypeData{"offsetY", EPropertyType::Int32}, - TypeData{"width", EPropertyType::Int32}, - TypeData{"height", EPropertyType::Int32} - } - ), - MakeStruct("frustum", frustumPlanes), - } - ); - - return PropertyImpl::Serialize(PropertyImpl{ cameraBindingInputs, EPropertySemantics::BindingInput }, m_flatBufferBuilder, m_serializationMap); - } - - flatbuffers::FlatBufferBuilder m_flatBufferBuilder; - SerializationTestUtils m_testUtils{ m_flatBufferBuilder }; - ::testing::StrictMock m_resolverMock; - ErrorReporting m_errorReporting; - SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; - }; - - // More unit tests with inputs/outputs declared in LogicNode (base class) serialization tests - TEST_F(ARamsesCameraBinding_SerializationLifecycle, RemembersBaseClassData) - { - // Serialize - { - RamsesCameraBindingImpl binding(*m_camera, true, "name", 1u); - binding.createRootProperties(); - (void)RamsesCameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); - } - - // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - - ASSERT_TRUE(serializedBinding.base()); - ASSERT_TRUE(serializedBinding.base()->base()); - ASSERT_TRUE(serializedBinding.base()->base()->name()); - EXPECT_EQ(serializedBinding.base()->base()->name()->string_view(), "name"); - EXPECT_EQ(serializedBinding.base()->base()->id(), 1u); - - ASSERT_TRUE(serializedBinding.base()->rootInput()); - EXPECT_EQ(serializedBinding.base()->rootInput()->rootType(), rlogic_serialization::EPropertyRootType::Struct); - ASSERT_TRUE(serializedBinding.base()->rootInput()->children()); - EXPECT_EQ(serializedBinding.base()->rootInput()->children()->size(), 2u); - - // Deserialize - { - EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_camera->getSceneObjectId())).WillOnce(::testing::Return(m_camera)); - std::unique_ptr deserializedBinding = RamsesCameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); - - ASSERT_TRUE(deserializedBinding); - EXPECT_EQ(deserializedBinding->getName(), "name"); - EXPECT_EQ(deserializedBinding->getId(), 1u); - EXPECT_EQ(deserializedBinding->getInputs()->getType(), EPropertyType::Struct); - EXPECT_EQ(deserializedBinding->getInputs()->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_EQ(deserializedBinding->getInputs()->getName(), ""); - EXPECT_EQ(deserializedBinding->getInputs()->getChildCount(), 2u); - } - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, RemembersRamsesCameraId) - { - // Serialize - { - RamsesCameraBindingImpl binding(*m_camera, true, "name", 1u); - binding.createRootProperties(); - (void)RamsesCameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); - } - - // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - - EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectId(), m_camera->getSceneObjectId().getValue()); - EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectType(), static_cast(ramses::ERamsesObjectType::OrthographicCamera)); - - // Deserialize - { - EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_camera->getSceneObjectId())).WillOnce(::testing::Return(m_camera)); - std::unique_ptr deserializedBinding = RamsesCameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); - - ASSERT_TRUE(deserializedBinding); - EXPECT_EQ(&deserializedBinding->getRamsesCamera(), m_camera); - } - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, SerializesInputProperties_withFrustumPlanes) - { - // Serialize - { - RamsesCameraBindingImpl binding(*m_camera, true, "name", 1u); - binding.createRootProperties(); - (void)RamsesCameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); - } - - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - - // Deserialize - { - EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_camera->getSceneObjectId())).WillOnce(::testing::Return(m_camera)); - std::unique_ptr deserializedBinding = RamsesCameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); - ASSERT_TRUE(deserializedBinding); - - ExpectInputPropertiesWithFrustumPlanes(*deserializedBinding); - } - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, SerializesInputProperties_withoutFrustumPlanes) - { - auto perspCamera = m_scene->createPerspectiveCamera(); - - // Serialize - { - RamsesCameraBindingImpl binding(*perspCamera, false, "name", 1u); - binding.createRootProperties(); - (void)RamsesCameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); - } - - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - - // Deserialize - { - EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), perspCamera->getSceneObjectId())).WillOnce(::testing::Return(perspCamera)); - std::unique_ptr deserializedBinding = RamsesCameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); - ASSERT_TRUE(deserializedBinding); - - ExpectInputPropertiesWithoutFrustumPlanes(*deserializedBinding); - } - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ProducesErrorIfInputPropertiesInvalid) - { - { - auto ramsesRef = rlogic_serialization::CreateRamsesReference( - m_flatBufferBuilder, - 12u, - static_cast(ramses::ERamsesObjectType::OrthographicCamera) - ); - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - ramsesRef, - serializeRootInput(true, true) - ); - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesCameraBinding from serialized data: missing or invalid input properties!"); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) - { - { - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - 0 // no base binding info - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesCameraBinding from serialized data: missing base class info!"); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenNoBindingName) - { - { - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - 0, // no name! - 1u) - ); - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesCameraBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenNoBindingId) - { - { - auto base = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 0)); - auto binding = rlogic_serialization::CreateRamsesCameraBinding(m_flatBufferBuilder, base); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesCameraBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenNoRootInput) - { - { - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - 0 // no root input - ); - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesCameraBinding from serialized data: missing root input!"); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) - { - { - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - 0, - m_testUtils.serializeTestProperty("", rlogic_serialization::EPropertyRootType::Struct, false, true) // rootInput with errors - ); - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenBoundCameraCannotBeResolved) - { - const ramses::sceneObjectId_t mockObjectId{ 12 }; - { - auto ramsesRef = rlogic_serialization::CreateRamsesReference( - m_flatBufferBuilder, - mockObjectId.getValue() - ); - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - ramsesRef, - serializeRootInput(true) - ); - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(nullptr)); - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - } - - TEST_F(ARamsesCameraBinding_SerializationLifecycle, ErrorWhenSavedCameraTypeDoesNotMatchResolvedCameraType) - { - RamsesTestSetup ramses; - ramses::Scene* scene = ramses.createScene(); - auto* perspCamera = scene->createPerspectiveCamera(); - - const ramses::sceneObjectId_t mockObjectId{ 12 }; - { - auto ramsesRef = rlogic_serialization::CreateRamsesReference( - m_flatBufferBuilder, - mockObjectId.getValue(), - uint32_t(ramses::ERamsesObjectType::OrthographicCamera) // save ortho camera - ); - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - ramsesRef, - serializeRootInput(false) - ); - auto binding = rlogic_serialization::CreateRamsesCameraBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - // resolver returns perspective camera, but orthographic camera is expected -> error - EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(perspCamera)); - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesCameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesCameraBinding from serialized data: loaded type does not match referenced camera type!"); - } - - class ARamsesCameraBinding_SerializationWithFile : public ARamsesCameraBinding - { - protected: - WithTempDirectory tempFolder; - }; - - TEST_F(ARamsesCameraBinding_SerializationWithFile, ContainsItsDataAfterLoading) - { - const int32_t newVpOffsetX = 10; - const int32_t newVpOffsetY = 13; - const int32_t newVpWidth = 56; - const int32_t newVpHeight = 45; - - const float newFov = 30.f; - const float newAR = 640.f / 480.f; - const float newNearPlane = 4.4f; - const float newFarPlane = 5.1f; - { - auto& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - - auto inputs = cameraBinding.getInputs(); - auto vpProperties = inputs->getChild("viewport"); - auto frustum = inputs->getChild("frustum"); - - vpProperties->getChild("offsetX")->set(newVpOffsetX); - vpProperties->getChild("offsetY")->set(newVpOffsetY); - vpProperties->getChild("width")->set(newVpWidth); - vpProperties->getChild("height")->set(newVpHeight); - - frustum->getChild("fieldOfView")->set(newFov); - frustum->getChild("aspectRatio")->set(newAR); - frustum->getChild("nearPlane")->set(newNearPlane); - frustum->getChild("farPlane")->set(newFarPlane); - m_logicEngine.update(); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("camerabinding.bin", &m_testScene)); - const auto& loadedCameraBinding = *m_logicEngine.findByName("CameraBinding"); - EXPECT_EQ("CameraBinding", loadedCameraBinding.getName()); - EXPECT_EQ(loadedCameraBinding.getId(), 1u); - EXPECT_EQ(loadedCameraBinding.getRamsesCamera().getSceneObjectId(), m_perspectiveCam.getSceneObjectId()); - - const auto& inputs = loadedCameraBinding.getInputs(); - ASSERT_EQ(inputs->getChildCount(), 2u); - auto vpProperties = inputs->getChild("viewport"); - auto frustum = inputs->getChild("frustum"); - ASSERT_EQ(vpProperties->getChildCount(), 4u); - ASSERT_EQ(vpProperties->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - ASSERT_EQ(frustum->getChildCount(), 4u); - ASSERT_EQ(frustum->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - - EXPECT_EQ(*vpProperties->getChild("offsetX")->get(), newVpOffsetX); - EXPECT_EQ(vpProperties->getChild("offsetX")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_EQ(*vpProperties->getChild("offsetY")->get(), newVpOffsetY); - EXPECT_EQ(vpProperties->getChild("offsetY")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_EQ(*vpProperties->getChild("width")->get(), newVpWidth); - EXPECT_EQ(vpProperties->getChild("width")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_EQ(*vpProperties->getChild("height")->get(), newVpHeight); - EXPECT_EQ(vpProperties->getChild("height")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - - EXPECT_EQ(*frustum->getChild("nearPlane")->get(), newNearPlane); - EXPECT_EQ(frustum->getChild("nearPlane")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_EQ(*frustum->getChild("farPlane")->get(), newFarPlane); - EXPECT_EQ(frustum->getChild("farPlane")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_NEAR(*frustum->getChild("fieldOfView")->get(), newFov, 0.001f); - EXPECT_EQ(frustum->getChild("fieldOfView")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - EXPECT_EQ(*frustum->getChild("aspectRatio")->get(), newAR); - EXPECT_EQ(frustum->getChild("aspectRatio")->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); - - // Test that internal indices match properties resolved by name - EXPECT_EQ(vpProperties->getChild("offsetX"), vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))); - EXPECT_EQ(vpProperties->getChild("offsetY"), vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))); - EXPECT_EQ(vpProperties->getChild("width"), vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))); - EXPECT_EQ(vpProperties->getChild("height"), vpProperties->m_impl->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))); - - EXPECT_EQ(frustum->getChild("nearPlane"), frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::NearPlane))); - EXPECT_EQ(frustum->getChild("farPlane"), frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FarPlane))); - EXPECT_EQ(frustum->getChild("fieldOfView"), frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))); - EXPECT_EQ(frustum->getChild("aspectRatio"), frustum->m_impl->getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))); - } - } - - TEST_F(ARamsesCameraBinding_SerializationWithFile, KeepsItsProperties_WhenNoRamsesLinksAndSceneProvided) - { - { - m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - - { - ASSERT_TRUE(m_logicEngine.loadFromFile("camerabinding.bin", m_scene)); - auto loadedCameraBinding = m_logicEngine.findByName("CameraBinding"); - EXPECT_EQ(&loadedCameraBinding->getRamsesCamera(), m_camera); - EXPECT_EQ(loadedCameraBinding->getInputs()->getChildCount(), 2u); - EXPECT_EQ(loadedCameraBinding->getOutputs(), nullptr); - EXPECT_EQ(loadedCameraBinding->getName(), "CameraBinding"); - } - } - - TEST_F(ARamsesCameraBinding_SerializationWithFile, RestoresLinkToRamsesCamera) - { - { - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("camerabinding.bin", &m_testScene)); - const auto& cameraBinding = *m_logicEngine.findByName("CameraBinding"); - EXPECT_EQ(&cameraBinding.getRamsesCamera(), &m_perspectiveCam); - } - } - - TEST_F(ARamsesCameraBinding_SerializationWithFile, ProducesError_WhenHavingLinkToRamsesCamera_ButNoSceneWasProvided) - { - { - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - { - EXPECT_FALSE(m_logicEngine.loadFromFile("camerabinding.bin")); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(errors.size(), 1u); - EXPECT_EQ(errors[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); - } - } - - TEST_F(ARamsesCameraBinding_SerializationWithFile, HandlesErrorWhenRamsesSceneWasSerializedWithOneTypeOfCamera_ButLoadedWithADifferentOne) - { - { - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - - // Fake that the ramses scene is exactly the same, just camera type changed (perspective -> ortho) - // This is a bit evil, but quite possible e.g. if camera was switched from perspective -> ortho and the ramses id didn't change because no other change in the scene and - // the camera is exported at the exact same time -> receives the same ID - ramses::Scene& slightlyModifiedScene = *m_ramsesTestSetup.createScene(ramses::sceneId_t(2)); - slightlyModifiedScene.createOrthographicCamera(""); - { - // loadFromFile catches the error -> content is not modified - EXPECT_FALSE(m_logicEngine.loadFromFile("camerabinding.bin", &slightlyModifiedScene)); - // Update still works with the old state of the logic engine - EXPECT_TRUE(m_logicEngine.update()); - } - } - - TEST_F(ARamsesCameraBinding_SerializationWithFile, ProducesError_WhenHavingLinkToRamsesCamera_WhichWasDeleted) - { - { - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - - m_testScene.destroy(m_perspectiveCam); - - { - EXPECT_FALSE(m_logicEngine.loadFromFile("camerabinding.bin", &m_testScene)); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(errors.size(), 1u); - EXPECT_EQ(errors[0].message, "Fatal error during loading from file! Serialized Ramses Logic object 'CameraBinding' points to a Ramses object (id: 2) which couldn't be found in the provided scene!"); - } - } - - TEST_F(ARamsesCameraBinding_SerializationWithFile, DoesNotModifyRamsesCameraProperties_WhenNoValuesWereExplicitlySetBeforeSaving) - { - { - m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("camerabinding.bin", &m_testScene)); - EXPECT_TRUE(m_logicEngine.update()); - - ExpectDefaultValues(*m_logicEngine.findByName("CameraBinding")); - } - } - - // Tests that the camera properties don't overwrite ramses' values after loading from file, until - // set() is called again explicitly after loadFromFile() - TEST_F(ARamsesCameraBinding_SerializationWithFile, ReappliesViewportPropertiesToRamsesCamera_OnlyAfterExplicitlySetAgain) - { - { - auto& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - // Set some values to the binding's inputs. Those should be lost/discarded on save() because we dont' call update() below - auto vpProperties = cameraBinding.getInputs()->getChild("viewport"); - vpProperties->getChild("offsetX")->set(4); - vpProperties->getChild("offsetY")->set(5); - vpProperties->getChild("width")->set(6); - vpProperties->getChild("height")->set(7); - m_logicEngine.update(); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - - // These values will be used to fill the cache of camera bindings on load() - m_perspectiveCam.setViewport(11, 12, 13u, 14u); - - { - EXPECT_TRUE(m_logicEngine.loadFromFile("camerabinding.bin", &m_testScene)); - - // Artificially set to other values so that we can verify update() didn't change them - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - - EXPECT_TRUE(m_logicEngine.update()); - - // Camera binding does not re-apply its cached values to ramses camera viewport - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - - // Set only one value of viewport struct. Use the same value as the one in cache on purpose! - // Calling set forces set on ramses regardless of the value used - m_logicEngine.findByName("CameraBinding")->getInputs()->getChild("viewport")->getChild("offsetX")->set(11); - m_logicEngine.update(); - EXPECT_TRUE(m_logicEngine.update()); - - // vpOffsetX changed, the rest is taken from the initially saved inputs, not what was set on the camera! - EXPECT_EQ(m_perspectiveCam.getViewportX(), 11); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 12); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 13u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 14u); - } - } - - // This is sort of a confidence test, testing a combination of: - // - bindings only propagating their values to ramses camera if the value was set by an incoming link - // - saving and loading files - // The general expectation is that after loading + update(), the logic scene would overwrite only ramses - // properties wrapped by a LogicBinding which is linked to a script - TEST_F(ARamsesCameraBinding_SerializationWithFile, SetsOnlyRamsesCameraPropertiesForWhichTheBindingInputIsLinked_WhenCallingUpdateAfterLoading) - { - // These values should not be overwritten by logic on update() - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - - { - const std::string_view scriptSrc = R"( - function interface(IN,OUT) - OUT.frustProps = { - fov = Type:Float(), - aR = Type:Float(), - nP = Type:Float(), - fP = Type:Float() - } - end - function run(IN,OUT) - OUT.frustProps = { - fov = 30.0, - aR = 640.0 / 480.0, - nP = 2.3, - fP = 5.6 - } - end - )"; - - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("fov"), *cameraBinding.getInputs()->getChild("frustum")->getChild("fieldOfView"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("aR"), *cameraBinding.getInputs()->getChild("frustum")->getChild("aspectRatio"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("nP"), *cameraBinding.getInputs()->getChild("frustum")->getChild("nearPlane"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("frustProps")->getChild("fP"), *cameraBinding.getInputs()->getChild("frustum")->getChild("farPlane"))); - - m_logicEngine.update(); - EXPECT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "camerabinding.bin")); - } - - // Modify 'linked' properties before loading to check if logic will overwrite them after load + update - m_perspectiveCam.setFrustum(15.f, 320.f / 240.f, 4.1f, 7.9f); - - { - EXPECT_TRUE(m_logicEngine.loadFromFile("camerabinding.bin", &m_testScene)); - - EXPECT_TRUE(m_logicEngine.update()); - - // Viewport properties were not linked -> their values are not modified - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - // Frustum properties are linked -> values were updated - EXPECT_NEAR(m_perspectiveCam.getVerticalFieldOfView(), 30.f, 0.001f); - EXPECT_EQ(m_perspectiveCam.getAspectRatio(), 640.f / 480.f); - EXPECT_EQ(m_perspectiveCam.getNearPlane(), 2.3f); - EXPECT_EQ(m_perspectiveCam.getFarPlane(), 5.6f); - - // Manually setting values on ramses followed by a logic update has no effect - // Logic is not "dirty" and it doesn't know it needs to update ramses - m_perspectiveCam.setViewport(43, 34, 84u, 62u); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 43); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 34); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 84u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 62u); - } - } - - // Larger confidence tests which verify and document the entire data flow cycle of bindings - // There are smaller tests which test only properties and their data propagation rules (see property unit tests) - // There are also "dirtiness" tests which test when a camera is being re-updated (see logic engine dirtiness tests) - // These tests test everything in combination - - class ARamsesCameraBinding_DataFlow : public ARamsesCameraBinding - { - }; - - TEST_F(ARamsesCameraBinding_DataFlow, WithExplicitSet) - { - // Create camera and preset values - m_perspectiveCam.setViewport(11, 12, 13u, 14u); - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, ""); - // set other values to artificially check that the binding won't override them - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - - // Nothing happens here - binding does not overwrite ramses values because no user value set() was called and no link exists - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - - // Set only two view port properties - auto vpProperties = cameraBinding.getInputs()->getChild("viewport"); - vpProperties->getChild("offsetX")->set(4); - vpProperties->getChild("width")->set(21); - - // Update not called yet -> still has preset values for vpOffsetX and vpWidth in ramses camera, and default frustum values - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - // Update() triggers all viewport to be set on ramses to the two values that were explicitly set - // and the other two previous values of the binding input - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 4); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 12); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 21u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 14u); - // Frustum is not modified - only viewport was explicitly set - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - // Set two properties of each viewPort and frustum property struct - vpProperties->getChild("offsetY")->set(13); - vpProperties->getChild("height")->set(63); - auto frustum = cameraBinding.getInputs()->getChild("frustum"); - frustum->getChild("nearPlane")->set(2.3f); - frustum->getChild("farPlane")->set(5.6f); - - // On update all values of both structs are set - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 4); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 13); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 21u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 63u); - - EXPECT_NEAR(m_perspectiveCam.getVerticalFieldOfView(), PerspectiveFrustumFOVdefault, 0.001f); - EXPECT_EQ(m_perspectiveCam.getAspectRatio(), PerspectiveFrustumARdefault); - EXPECT_EQ(m_perspectiveCam.getNearPlane(), 2.3f); - EXPECT_EQ(m_perspectiveCam.getFarPlane(), 5.6f); - - // Calling update again does not "rewrite" the data to ramses. Check this by setting a value manually and call update() again - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - - // Set all properties manually this time - vpProperties->getChild("offsetX")->set(4); - vpProperties->getChild("offsetY")->set(5); - vpProperties->getChild("width")->set(6); - vpProperties->getChild("height")->set(7); - - frustum->getChild("fieldOfView")->set(30.f); - frustum->getChild("aspectRatio")->set(640.f / 480.f); - frustum->getChild("nearPlane")->set(1.3f); - frustum->getChild("farPlane")->set(7.6f); - m_logicEngine.update(); - - // All of the property values were passed to ramses - EXPECT_EQ(m_perspectiveCam.getViewportX(), 4); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 5); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 6u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 7u); - - EXPECT_NEAR(m_perspectiveCam.getVerticalFieldOfView(), 30.f, 0.001f); - EXPECT_EQ(m_perspectiveCam.getAspectRatio(), 640.f / 480.f); - EXPECT_EQ(m_perspectiveCam.getNearPlane(), 1.3f); - EXPECT_EQ(m_perspectiveCam.getFarPlane(), 7.6f); - } - - TEST_F(ARamsesCameraBinding_DataFlow, WithLinks) - { - const std::string_view scriptSrc = R"( - function interface(IN,OUT) - OUT.vpOffsetX = Type:Int32() - end - function run(IN,OUT) - OUT.vpOffsetX = 15 - end - )"; - - // Create camera and preset values - m_perspectiveCam.setViewport(11, 12, 13u, 14u); - - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - RamsesCameraBinding& cameraBinding = *m_logicEngine.createRamsesCameraBinding(m_perspectiveCam, "CameraBinding"); - // set other values to artificially check that the binding won't override them - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - - // Adding and removing link does not set anything in ramses - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); - ASSERT_TRUE(m_logicEngine.unlink(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - // Create link and calling update -> sets values to ramses set by the link (vpOffsetX) - // and uses cached values in the binding for the other vp properties - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 15); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 12); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 13u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 14u); - // Does not touch the frustum because not linked or set at all - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - // Link does not overwrite manually set values as long as the actual value didnt change to avoid causing unnecessary sets on ramses - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - - // Remove link -> value is not overwritten any more - ASSERT_TRUE(m_logicEngine.unlink(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); - m_perspectiveCam.setViewport(9, 8, 1u, 2u); - m_logicEngine.update(); - EXPECT_EQ(m_perspectiveCam.getViewportX(), 9); - EXPECT_EQ(m_perspectiveCam.getViewportY(), 8); - EXPECT_EQ(m_perspectiveCam.getViewportWidth(), 1u); - EXPECT_EQ(m_perspectiveCam.getViewportHeight(), 2u); - ExpectDefaultPerspectiveCameraFrustumValues(m_perspectiveCam); - } -} diff --git a/client/logic/unittests/api/RamsesMeshNodeBindingTest.cpp b/client/logic/unittests/api/RamsesMeshNodeBindingTest.cpp deleted file mode 100644 index 9e781e644..000000000 --- a/client/logic/unittests/api/RamsesMeshNodeBindingTest.cpp +++ /dev/null @@ -1,408 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" -#include "RamsesTestUtils.h" -#include "SerializationTestUtils.h" -#include "RamsesObjectResolverMock.h" -#include "WithTempDirectory.h" - -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/Property.h" -#include "impl/RamsesMeshNodeBindingImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/TypeData.h" - -#include "generated/RamsesMeshNodeBindingGen.h" - -namespace ramses::internal -{ - class ARamsesMeshNodeBinding : public ALogicEngine - { - public: - ARamsesMeshNodeBinding() - { - // in order for the tests to be closer to reality, use ramses MeshNode with actual geometry and appearance, - // the geometry affects some of the values exposed in the binding (namely instanceCount) - const std::array vertexPositionsArray = { ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{1.f, 0.f, -1.f}, ramses::vec3f{0.f, 1.f, -1.f} }; - const ramses::ArrayResource* vertexPositions = m_scene->createArrayResource(3u, vertexPositionsArray.data()); - const std::array indexArray = { 0, 1, 2 }; - const ramses::ArrayResource* indices = m_scene->createArrayResource(3u, indexArray.data()); - - ramses::EffectDescription effectDesc; - effectDesc.setVertexShader(R"( - #version 100 - attribute vec3 a_position; - void main() - { - gl_Position = vec4(a_position, 1.0); - } - )"); - effectDesc.setFragmentShader(R"( - #version 100 - void main(void) - { - gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); - } - )"); - const ramses::Effect* effect = m_scene->createEffect(effectDesc); - ramses::Appearance* appearance = m_scene->createAppearance(*effect); - - m_geometry = m_scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - m_geometry->setInputBuffer(positionsInput, *vertexPositions); - m_geometry->setIndices(*indices); - - m_meshNodeWithGeometry = m_scene->createMeshNode("meshNode"); - m_meshNodeWithGeometry->setAppearance(*appearance); - m_meshNodeWithGeometry->setGeometryBinding(*m_geometry); - EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); - - m_meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNodeWithGeometry, "meshBinding"); - EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); - } - - protected: - ramses::GeometryBinding* m_geometry = nullptr; - ramses::MeshNode* m_meshNodeWithGeometry = nullptr; - RamsesMeshNodeBinding* m_meshBinding = nullptr; - }; - - TEST_F(ARamsesMeshNodeBinding, RefersToGivenRamsesObject) - { - EXPECT_EQ(m_meshNodeWithGeometry, &m_meshBinding->getRamsesMeshNode()); - const auto& mbConst = *m_meshBinding; - EXPECT_EQ(m_meshNodeWithGeometry, &mbConst.getRamsesMeshNode()); - const auto& mbImplConst = m_meshBinding->m_meshNodeBinding; - EXPECT_EQ(m_meshNodeWithGeometry, &mbImplConst.getRamsesMeshNode()); - } - - TEST_F(ARamsesMeshNodeBinding, HasInputPropertiesAndNoOutputs) - { - ASSERT_NE(nullptr, m_meshBinding->getInputs()); - ASSERT_EQ(size_t(RamsesMeshNodeBindingImpl::EInputProperty::COUNT), m_meshBinding->getInputs()->getChildCount()); - EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::VertexOffset)), m_meshBinding->getInputs()->getChild("vertexOffset")); - EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset)), m_meshBinding->getInputs()->getChild("indexOffset")); - EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount)), m_meshBinding->getInputs()->getChild("indexCount")); - EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount)), m_meshBinding->getInputs()->getChild("instanceCount")); - EXPECT_EQ(nullptr, m_meshBinding->getOutputs()); - } - - TEST_F(ARamsesMeshNodeBinding, InputPropertiesAreInitializedFromBoundMeshNode) - { - m_meshNodeWithGeometry->setStartVertex(42); - m_meshNodeWithGeometry->setStartIndex(43); - m_meshNodeWithGeometry->setIndexCount(44); - m_meshNodeWithGeometry->setInstanceCount(45); - // create new binding - const auto binding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNodeWithGeometry); - - EXPECT_EQ(42, *binding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::VertexOffset))->get()); - EXPECT_EQ(43, *binding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset))->get()); - EXPECT_EQ(44, *binding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount))->get()); - EXPECT_EQ(45, *binding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount))->get()); - } - - TEST_F(ARamsesMeshNodeBinding, SetsModifiedBoundValuesOnUpdate) - { - // initial values - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartVertex()); - EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartIndex()); - EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); - EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); - - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::VertexOffset))->set(42)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(42u, m_meshNodeWithGeometry->getStartVertex()); - EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartIndex()); - EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); - EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); - - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset))->set(43)); - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount))->set(44)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(42u, m_meshNodeWithGeometry->getStartVertex()); - EXPECT_EQ(43u, m_meshNodeWithGeometry->getStartIndex()); - EXPECT_EQ(44u, m_meshNodeWithGeometry->getIndexCount()); - EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); - - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount))->set(45)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(42u, m_meshNodeWithGeometry->getStartVertex()); - EXPECT_EQ(43u, m_meshNodeWithGeometry->getStartIndex()); - EXPECT_EQ(44u, m_meshNodeWithGeometry->getIndexCount()); - EXPECT_EQ(45u, m_meshNodeWithGeometry->getInstanceCount()); - } - - TEST_F(ARamsesMeshNodeBinding, FailsUpdateIfTryingToSetInvalidValue) - { - // mesh instance count cannot be 0 - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount))->set(0)); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_meshBinding, m_logicEngine.getErrors()[0].object); - EXPECT_EQ(EErrorType::RuntimeError, m_logicEngine.getErrors()[0].type); - EXPECT_EQ("MeshNode::setInstanceCount failed: instance count must not be 0!", m_logicEngine.getErrors()[0].message); - } - - class ARamsesMeshNodeBinding_SerializationLifecycle : public ARamsesMeshNodeBinding - { - protected: - enum class ESerializationIssue - { - AllValid, - MissingBase, - MissingName, - MissingRoot, - CorruptedInputProperties, - MissingBoundObject, - UnresolvedMeshNode, - InvalidBoundObjectType - }; - - std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) - { - { - auto inputsType = MakeStruct("", { - TypeData{"vertexOffset", EPropertyType::Int32}, - TypeData{"indexOffset", EPropertyType::Int32}, - (issue == ESerializationIssue::CorruptedInputProperties ? TypeData{"wrong", EPropertyType::Int32} : TypeData{"indexCount", EPropertyType::Int32}), - TypeData{"instanceCount", EPropertyType::Int32} - }); - auto inputs = std::make_unique(std::move(inputsType), EPropertySemantics::BindingInput); - - SerializationMap serializationMap; - const auto logicObject = rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u, 0u, 0u); - auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, - logicObject, - (issue == ESerializationIssue::MissingBoundObject ? 0 : rlogic_serialization::CreateRamsesReference(m_flatBufferBuilder, - 1u, (issue == ESerializationIssue::InvalidBoundObjectType ? 0 : static_cast(ramses::ERamsesObjectType::MeshNode)))), - (issue == ESerializationIssue::MissingRoot ? 0 : PropertyImpl::Serialize(*inputs, m_flatBufferBuilder, serializationMap))); - - auto fbMeshNodeBinding = rlogic_serialization::CreateRamsesMeshNodeBinding(m_flatBufferBuilder, (issue == ESerializationIssue::MissingBase ? 0 : fbRamsesBinding)); - m_flatBufferBuilder.Finish(fbMeshNodeBinding); - } - - switch (issue) - { - case ESerializationIssue::AllValid: - case ESerializationIssue::InvalidBoundObjectType: - EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(m_meshNodeWithGeometry)); - break; - case ESerializationIssue::UnresolvedMeshNode: - EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(nullptr)); - break; - default: - break; - } - - DeserializationMap deserializationMap; - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - return RamsesMeshNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, deserializationMap); - } - - flatbuffers::FlatBufferBuilder m_flatBufferBuilder; - ::testing::StrictMock m_resolverMock; - ErrorReporting m_errorReporting; - }; - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, CanSerializeWithNoIssue) - { - EXPECT_TRUE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingBase) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingBase)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing base class info!"); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingName) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingName)); - ASSERT_EQ(2u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LogicObject base from serialized data: missing name!"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing name and/or ID!"); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingRoot) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingRoot)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing root input!"); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_CorruptedInputProperties) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::CorruptedInputProperties)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesMeshNodeBinding from serialized data: corrupted root input!"); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingBoundObject) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingBoundObject)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing ramses object reference!"); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_UnresolvedMeshNode) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::UnresolvedMeshNode)); - // error message is generated in resolver which is mocked here - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_InvalidBoundObjectType) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesMeshNodeBinding_SerializationLifecycle::ESerializationIssue::InvalidBoundObjectType)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesMeshNodeBinding from serialized data: loaded object type does not match referenced object type!"); - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, FailsToLoadWhenNoSceneProvided) - { - { - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - { - EXPECT_FALSE(m_logicEngine.loadFromFile("binding.bin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); - } - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, KeepsItsPropertiesAfterDeserialization) - { - { - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset))->set(41)); - EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount))->set(42)); - EXPECT_TRUE(m_logicEngine.update()); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding.bin", m_scene)); - const auto loadedBinding = m_logicEngine.findByName("meshBinding"); - ASSERT_TRUE(loadedBinding); - EXPECT_EQ(m_meshNodeWithGeometry, &loadedBinding->getRamsesMeshNode()); - - ASSERT_NE(nullptr, loadedBinding->getInputs()); - ASSERT_EQ(size_t(RamsesMeshNodeBindingImpl::EInputProperty::COUNT), loadedBinding->getInputs()->getChildCount()); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::VertexOffset)), loadedBinding->getInputs()->getChild("vertexOffset")); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset)), loadedBinding->getInputs()->getChild("indexOffset")); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount)), loadedBinding->getInputs()->getChild("indexCount")); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount)), loadedBinding->getInputs()->getChild("instanceCount")); - EXPECT_EQ(nullptr, loadedBinding->getOutputs()); - - EXPECT_EQ(41, *loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset))->get()); - EXPECT_EQ(42, *loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount))->get()); - - // confidence test - can set new values - EXPECT_TRUE(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset))->set(43)); - EXPECT_TRUE(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount))->set(44)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartVertex()); // did not change - EXPECT_EQ(43u, m_meshNodeWithGeometry->getStartIndex()); // changed before saving and again after loading - EXPECT_EQ(44u, m_meshNodeWithGeometry->getIndexCount()); // changed after loading - EXPECT_EQ(42u, m_meshNodeWithGeometry->getInstanceCount()); // changed before saving - } - } - - TEST_F(ARamsesMeshNodeBinding_SerializationLifecycle, DoesNotModifyAnyValueIfNotSetDuringSerializationAndDeserialization) - { - { - EXPECT_TRUE(m_logicEngine.update()); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding.bin", m_scene)); - const auto loadedBinding = m_logicEngine.findByName("meshBinding"); - ASSERT_TRUE(loadedBinding); - EXPECT_EQ(m_meshNodeWithGeometry, &loadedBinding->getRamsesMeshNode()); - - ASSERT_NE(nullptr, loadedBinding->getInputs()); - ASSERT_EQ(size_t(RamsesMeshNodeBindingImpl::EInputProperty::COUNT), loadedBinding->getInputs()->getChildCount()); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::VertexOffset)), loadedBinding->getInputs()->getChild("vertexOffset")); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexOffset)), loadedBinding->getInputs()->getChild("indexOffset")); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount)), loadedBinding->getInputs()->getChild("indexCount")); - EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::InstanceCount)), loadedBinding->getInputs()->getChild("instanceCount")); - EXPECT_EQ(nullptr, loadedBinding->getOutputs()); - - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartVertex()); - EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartIndex()); - EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); - EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); - } - } - - // Note that this is not recommended usage of Ramses binding, modifying the bound object after it is bound - TEST_F(ARamsesMeshNodeBinding, DoesNotModifyIndexCountEvenIfGeometryAssignedAfterBindingIsCreated) - { - auto meshNode = m_scene->createMeshNode(); - auto meshBinding = m_logicEngine.createRamsesMeshNodeBinding(*meshNode); - EXPECT_EQ(0u, meshNode->getIndexCount()); - - // assign geometry which in ramses automatically updates index count based on its indices - meshNode->setGeometryBinding(*m_geometry); - EXPECT_EQ(3u, meshNode->getIndexCount()); - - // mesh binding update must not affect that value even though binding's property holds indexCount=0 because it was initialized with it - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(3u, meshNode->getIndexCount()); - - // the index count value will be overridden only when explicitly set from binding (or link) - EXPECT_TRUE(meshBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount))->set(2)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(2u, meshNode->getIndexCount()); - } - - // Note that this is not recommended usage of Ramses binding, modifying the bound object after it is bound - TEST_F(ARamsesMeshNodeBinding, DoesNotModifyIndexCountEvenIfGeometryAssignedAfterBindingIsCreated_SerializedAndDeserialized) - { - auto meshNode = m_scene->createMeshNode(); - - { - m_logicEngine.createRamsesMeshNodeBinding(*meshNode, "bindingWithLateGeometry"); - EXPECT_EQ(0u, meshNode->getIndexCount()); - - // assign geometry which in ramses automatically updates index count based on its indices - meshNode->setGeometryBinding(*m_geometry); - EXPECT_EQ(3u, meshNode->getIndexCount()); - - // mesh binding update must not affect that value even though binding's property holds indexCount=0 because it was initialized with it - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(3u, meshNode->getIndexCount()); - - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - ASSERT_TRUE(m_logicEngine.loadFromFile("binding.bin", m_scene)); - const auto loadedBinding = m_logicEngine.findByName("bindingWithLateGeometry"); - ASSERT_TRUE(loadedBinding); - EXPECT_EQ(meshNode, &loadedBinding->getRamsesMeshNode()); - - // value still unchanged - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(3u, meshNode->getIndexCount()); - - // the index count value will be overridden only when explicitly set from binding (or link) - EXPECT_TRUE(loadedBinding->getInputs()->getChild(size_t(RamsesMeshNodeBindingImpl::EInputProperty::IndexCount))->set(2)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(2u, meshNode->getIndexCount()); - } -} diff --git a/client/logic/unittests/api/RamsesRenderGroupBindingElementsTest.cpp b/client/logic/unittests/api/RamsesRenderGroupBindingElementsTest.cpp deleted file mode 100644 index 33f2bc467..000000000 --- a/client/logic/unittests/api/RamsesRenderGroupBindingElementsTest.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" -#include "ramses-client-api/RenderGroup.h" -#include "impl/RamsesRenderGroupBindingElementsImpl.h" - -namespace ramses::internal -{ - class ARamsesRenderGroupBindingElements : public ALogicEngine - { - protected: - void expectElements(const RamsesRenderGroupBindingElementsImpl::Elements& elements) const - { - EXPECT_EQ(m_elements.m_impl->getElements(), elements); - } - - RamsesRenderGroupBindingElements m_elements; - }; - - TEST_F(ARamsesRenderGroupBindingElements, CanAddMeshAndRenderGroupElement) - { - EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); - EXPECT_TRUE(m_elements.addElement(*m_renderGroup, "rg")); - expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); - } - - TEST_F(ARamsesRenderGroupBindingElements, CanBeCopyAndMoveConstructed) - { - EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); - const RamsesRenderGroupBindingElementsImpl::Elements expectedElements{ { "mesh", m_meshNode } }; - - RamsesRenderGroupBindingElements elementsCopy{ m_elements }; - EXPECT_EQ(elementsCopy.m_impl->getElements(), expectedElements); - - RamsesRenderGroupBindingElements elementsMove{ std::move(elementsCopy) }; - EXPECT_EQ(elementsMove.m_impl->getElements(), expectedElements); - } - - TEST_F(ARamsesRenderGroupBindingElements, CanBeCopyAndMoveAssigned) - { - EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); - const RamsesRenderGroupBindingElementsImpl::Elements expectedElements{ { "mesh", m_meshNode } }; - - RamsesRenderGroupBindingElements elementsCopy; - elementsCopy = m_elements; - EXPECT_EQ(elementsCopy.m_impl->getElements(), expectedElements); - - RamsesRenderGroupBindingElements elementsMove; - elementsMove = std::move(elementsCopy); - EXPECT_EQ(elementsMove.m_impl->getElements(), expectedElements); - } - - TEST_F(ARamsesRenderGroupBindingElements, AddsElementUnderItsObjectNameIfNoElementNameProvided) - { - EXPECT_TRUE(m_elements.addElement(*m_meshNode)); - expectElements({ { "meshNode", m_meshNode } }); - } - - TEST_F(ARamsesRenderGroupBindingElements, FailsToAddElementIfNoElementNameProvidedAndObjectNameEmpty) - { - EXPECT_STREQ("", m_renderGroup->getName()); - EXPECT_FALSE(m_elements.addElement(*m_renderGroup)); - expectElements({}); - } - - TEST_F(ARamsesRenderGroupBindingElements, FailsToAddElementMoreThanOnce) - { - EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); - EXPECT_TRUE(m_elements.addElement(*m_renderGroup, "rg")); - expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); - EXPECT_FALSE(m_elements.addElement(*m_meshNode, "mesh")); - EXPECT_FALSE(m_elements.addElement(*m_renderGroup, "rg")); - expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); - } -} diff --git a/client/logic/unittests/api/RamsesRenderPassBindingTest.cpp b/client/logic/unittests/api/RamsesRenderPassBindingTest.cpp deleted file mode 100644 index 5838e5759..000000000 --- a/client/logic/unittests/api/RamsesRenderPassBindingTest.cpp +++ /dev/null @@ -1,590 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "LogicEngineTest_Base.h" - -#include "RamsesObjectResolverMock.h" -#include "RamsesTestUtils.h" -#include "SerializationTestUtils.h" -#include "WithTempDirectory.h" - -#include "impl/LogicEngineImpl.h" -#include "impl/RamsesRenderPassBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/RamsesHelper.h" -#include "generated/RamsesRenderPassBindingGen.h" - -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" - -#include "ramses-client-api/RenderPass.h" - -#include "ramses-utils.h" - -namespace ramses::internal -{ - class ARamsesRenderPassBinding : public ALogicEngine - { - }; - - TEST_F(ARamsesRenderPassBinding, HasANameAfterCreation) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - EXPECT_EQ("renderPass", renderPassBinding.getName()); - } - - TEST_F(ARamsesRenderPassBinding, RefersToGivenRenderPass) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - EXPECT_EQ(m_renderPass, &renderPassBinding.getRamsesRenderPass()); - const auto& rpConst = renderPassBinding; - EXPECT_EQ(m_renderPass, &rpConst.getRamsesRenderPass()); - const auto& rpImplConst = renderPassBinding.m_renderPassBinding; - EXPECT_EQ(m_renderPass, &rpImplConst.getRamsesRenderPass()); - } - - TEST_F(ARamsesRenderPassBinding, HasInputsAfterCreation) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - ASSERT_NE(nullptr, renderPassBinding.getInputs()); - ASSERT_EQ(std::size_t(RamsesRenderPassBindingImpl::EPropertyIndex_COUNT), renderPassBinding.getInputs()->getChildCount()); - EXPECT_EQ(renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_Enabled), renderPassBinding.getInputs()->getChild("enabled")); - EXPECT_EQ(EPropertyType::Bool, renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_Enabled)->getType()); - EXPECT_EQ(renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOrder), renderPassBinding.getInputs()->getChild("renderOrder")); - EXPECT_EQ(EPropertyType::Int32, renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOrder)->getType()); - EXPECT_EQ(renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_ClearColor), renderPassBinding.getInputs()->getChild("clearColor")); - EXPECT_EQ(EPropertyType::Vec4f, renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_ClearColor)->getType()); - EXPECT_EQ(renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOnce), renderPassBinding.getInputs()->getChild("renderOnce")); - EXPECT_EQ(EPropertyType::Bool, renderPassBinding.getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOnce)->getType()); - } - - TEST_F(ARamsesRenderPassBinding, HasNoOutputsAfterCreation) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - EXPECT_EQ(nullptr, renderPassBinding.getOutputs()); - } - - // This fixture only contains serialization unit tests, for higher order tests see `ARamsesRenderPassBinding_WithRamses_AndFiles` - class ARamsesRenderPassBinding_SerializationLifecycle : public ARamsesRenderPassBinding - { - protected: - enum class ESerializationIssue - { - AllValid, - RootNotStruct, - BoundObjectReferenceMissing, - BoundObjectTypeMismatch - }; - - std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) - { - { - auto inputsType = MakeStruct("", { - TypeData{"enabled", EPropertyType::Bool}, - TypeData{"renderOrder", EPropertyType::Int32}, - TypeData{"clearColor", EPropertyType::Vec4f}, - TypeData{"renderOnce", EPropertyType::Bool} - }); - - HierarchicalTypeData inputs = (issue == ESerializationIssue::RootNotStruct ? MakeType("", EPropertyType::Bool) : inputsType); - auto inputsImpl = std::make_unique(std::move(inputs), EPropertySemantics::BindingInput); - - auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, m_flatBufferBuilder.CreateString("name"), 1u, 0u, 0u), - (issue == ESerializationIssue::BoundObjectReferenceMissing ? 0 : rlogic_serialization::CreateRamsesReference(m_flatBufferBuilder, - 1u, (issue == ESerializationIssue::BoundObjectTypeMismatch ? 0 : static_cast(ramses::ERamsesObjectType::RenderPass)))), - PropertyImpl::Serialize(*inputsImpl, m_flatBufferBuilder, m_serializationMap)); - - auto fbRenderPassBinding = rlogic_serialization::CreateRamsesRenderPassBinding(m_flatBufferBuilder, fbRamsesBinding); - m_flatBufferBuilder.Finish(fbRenderPassBinding); - } - - switch (issue) - { - case ESerializationIssue::AllValid: - case ESerializationIssue::BoundObjectTypeMismatch: - EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(m_renderPass)); - break; - case ESerializationIssue::RootNotStruct: - case ESerializationIssue::BoundObjectReferenceMissing: - break; - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - return RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - } - - flatbuffers::FlatBufferBuilder m_flatBufferBuilder; - SerializationTestUtils m_testUtils {m_flatBufferBuilder}; - ::testing::StrictMock m_resolverMock; - ErrorReporting m_errorReporting; - SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; - }; - - // More unit tests with inputs/outputs declared in LogicNode (base class) serialization tests - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, StoresBaseClassData) - { - // Serialize - { - RamsesRenderPassBindingImpl binding(*m_renderPass, "name", 1u); - binding.createRootProperties(); - (void)RamsesRenderPassBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); - } - - // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - - ASSERT_TRUE(serializedBinding.base()); - ASSERT_TRUE(serializedBinding.base()->base()); - ASSERT_TRUE(serializedBinding.base()->base()->name()); - EXPECT_EQ(serializedBinding.base()->base()->name()->string_view(), "name"); - EXPECT_EQ(serializedBinding.base()->base()->id(), 1u); - - ASSERT_TRUE(serializedBinding.base()->rootInput()); - EXPECT_EQ(serializedBinding.base()->rootInput()->rootType(), rlogic_serialization::EPropertyRootType::Struct); - ASSERT_TRUE(serializedBinding.base()->rootInput()->children()); - EXPECT_EQ(serializedBinding.base()->rootInput()->children()->size(), size_t(RamsesRenderPassBindingImpl::EPropertyIndex_COUNT)); - - // Deserialize - { - EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), m_renderPass->getSceneObjectId())).WillOnce(::testing::Return(m_renderPass)); - std::unique_ptr deserializedBinding = RamsesRenderPassBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); - - ASSERT_TRUE(deserializedBinding); - EXPECT_EQ(deserializedBinding->getName(), "name"); - EXPECT_EQ(deserializedBinding->getId(), 1u); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); - } - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, StoresIdAndTypeAndRenderPassRef) - { - // Serialize - { - RamsesRenderPassBindingImpl binding(*m_renderPass, "name", 1u); - binding.createRootProperties(); - (void)RamsesRenderPassBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); - } - - // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - - ASSERT_TRUE(serializedBinding.base()->boundRamsesObject()); - EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectId(), m_renderPass->getSceneObjectId().getValue()); - EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectType(), static_cast(ramses::ERamsesObjectType::RenderPass)); - - // Deserialize - { - EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), m_renderPass->getSceneObjectId())).WillOnce(::testing::Return(m_renderPass)); - std::unique_ptr deserializedBinding = RamsesRenderPassBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); - - ASSERT_TRUE(deserializedBinding); - EXPECT_EQ(&deserializedBinding->getRamsesRenderPass(), m_renderPass); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); - - // Check that input was deserialized too - EXPECT_EQ(deserializedBinding->getInputs()->getChild("enabled")->getType(), EPropertyType::Bool); - } - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) - { - { - auto binding = rlogic_serialization::CreateRamsesRenderPassBinding( - m_flatBufferBuilder, - 0 // no base binding info - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderPassBinding from serialized data: missing base class info!"); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ErrorWhenNoBindingName) - { - { - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - 0, // no name! - 1u) - ); - auto binding = rlogic_serialization::CreateRamsesRenderPassBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesRenderPassBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ErrorWhenNoBindingId) - { - { - auto base = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 0) // no id - ); - auto binding = rlogic_serialization::CreateRamsesRenderPassBinding(m_flatBufferBuilder, base); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesRenderPassBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ErrorWhenNoRootInput) - { - { - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - 0 // no root input - ); - auto binding = rlogic_serialization::CreateRamsesRenderPassBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderPassBinding from serialized data: missing root input!"); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ErrorWhenBoundRenderPassCannotBeResolved) - { - const ramses::sceneObjectId_t mockObjectId {12}; - { - auto ramsesRef = rlogic_serialization::CreateRamsesReference( - m_flatBufferBuilder, - mockObjectId.getValue() - ); - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - ramsesRef, - m_testUtils.serializeTestProperty("") - ); - auto binding = rlogic_serialization::CreateRamsesRenderPassBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(nullptr)); - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) - { - { - auto base = rlogic_serialization::CreateRamsesBinding( - m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - m_flatBufferBuilder.CreateString("name"), - 1u), - 0, - m_testUtils.serializeTestProperty("", rlogic_serialization::EPropertyRootType::Struct, false, true) // rootInput with errors - ); - auto binding = rlogic_serialization::CreateRamsesRenderPassBinding( - m_flatBufferBuilder, - base - ); - m_flatBufferBuilder.Finish(binding); - } - - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesRenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); - - EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, CanSerializeWithNoIssue) - { - EXPECT_TRUE(deserializeSerializedDataWithIssue(ARamsesRenderPassBinding_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ReportsSerializationError_RootNotStructType) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderPassBinding_SerializationLifecycle::ESerializationIssue::RootNotStruct)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderPassBinding from serialized data: root input has unexpected type!"); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ReportsSerializationError_BoundObjectReferenceMissing) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderPassBinding_SerializationLifecycle::ESerializationIssue::BoundObjectReferenceMissing)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderPassBinding from serialized data: missing ramses object reference!"); - } - - TEST_F(ARamsesRenderPassBinding_SerializationLifecycle, ReportsSerializationError_BoundObjectTypeMismatch) - { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderPassBinding_SerializationLifecycle::ESerializationIssue::BoundObjectTypeMismatch)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderPassBinding from serialized data: loaded object type does not match referenced object type!"); - } - - class ARamsesRenderPassBinding_WithRamses : public ARamsesRenderPassBinding - { - }; - - TEST_F(ARamsesRenderPassBinding_WithRamses, ReturnsReferenceToRamsesRenderPass) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - EXPECT_EQ(m_renderPass, &renderPassBinding.getRamsesRenderPass()); - } - - TEST_F(ARamsesRenderPassBinding_WithRamses, GivesInputs_BindingInputSemantics) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - for (size_t i = 0; i < renderPassBinding.getInputs()->getChildCount(); ++i) - { - EXPECT_EQ(EPropertySemantics::BindingInput, renderPassBinding.getInputs()->getChild(i)->m_impl->getPropertySemantics()); - } - } - - TEST_F(ARamsesRenderPassBinding_WithRamses, TakesInitialValuesFromRamsesRenderPass) - { - m_renderPass->setEnabled(false); - m_renderPass->setRenderOrder(42); - m_renderPass->setClearColor({0.1f, 0.2f, 0.3f, 0.4f}); - m_renderPass->setRenderOnce(true); - - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - auto inputs = renderPassBinding.getInputs(); - EXPECT_FALSE(*inputs->getChild("enabled")->get()); - EXPECT_EQ(42, *inputs->getChild("renderOrder")->get()); - EXPECT_EQ(*inputs->getChild("clearColor")->get(), vec4f(0.1f, 0.2f, 0.3f, 0.4f)); - EXPECT_TRUE(inputs->getChild("renderOnce")->set(true)); - } - - TEST_F(ARamsesRenderPassBinding_WithRamses, UpdatesRenderPassIfInputValuesWereSet) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - auto inputs = renderPassBinding.getInputs(); - ASSERT_EQ(std::size_t(RamsesRenderPassBindingImpl::EPropertyIndex_COUNT), inputs->getChildCount()); - EXPECT_TRUE(inputs->getChild("enabled")->set(false)); - EXPECT_TRUE(inputs->getChild("renderOrder")->set(42)); - EXPECT_TRUE(inputs->getChild("clearColor")->set({ 0.1f, 0.2f, 0.3f, 0.4f })); - EXPECT_TRUE(inputs->getChild("renderOnce")->set(true)); - - EXPECT_TRUE(m_logicEngine.update()); - - EXPECT_FALSE(m_renderPass->isEnabled()); - EXPECT_EQ(42, m_renderPass->getRenderOrder()); - vec4f clearColor = m_renderPass->getClearColor(); - EXPECT_EQ(clearColor, vec4f(0.1f, 0.2f, 0.3f, 0.4f)); - EXPECT_TRUE(m_renderPass->isRenderOnce()); - } - - TEST_F(ARamsesRenderPassBinding_WithRamses, PropagateItsInputsToRamsesRenderPassOnUpdate_OnlyWhenExplicitlySet) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - - // Set values directly - m_renderPass->setEnabled(false); - m_renderPass->setRenderOrder(3); - - // Set only one of the inputs to the binding object, the other one (enabled) not - EXPECT_TRUE(renderPassBinding.getInputs()->getChild("renderOrder")->set(99)); - - EXPECT_TRUE(m_logicEngine.update()); - - // Only propagate the value which was also set in the binding object - EXPECT_FALSE(m_renderPass->isEnabled()); - EXPECT_EQ(99, m_renderPass->getRenderOrder()); - } - - TEST_F(ARamsesRenderPassBinding_WithRamses, PropagatesItsInputsToRamsesRenderPassOnUpdate_WithLinksInsteadOfSetCall) - { - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - - // Set values directly - m_renderPass->setRenderOrder(13); - m_renderPass->setEnabled(false); - - // Link binding input to a script (binding is not set directly, but is linked) - const std::string_view scriptSrc = R"( - function interface(IN,OUT) - OUT.val = Type:Int32() - end - function run(IN,OUT) - OUT.val = 42 - end - )"; - const LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("val"), *renderPassBinding.getInputs()->getChild("renderOrder"))); - - EXPECT_TRUE(m_logicEngine.update()); - - // Only propagate the value which was also linked over the binding object's input to a script - EXPECT_EQ(42, m_renderPass->getRenderOrder()); - EXPECT_FALSE(m_renderPass->isEnabled()); - } - - class ARamsesRenderPassBinding_WithRamses_AndFiles : public ARamsesRenderPassBinding_WithRamses - { - protected: - WithTempDirectory tempFolder; - }; - - TEST_F(ARamsesRenderPassBinding_WithRamses_AndFiles, KeepsItsPropertiesAfterDeserialization_WhenNoRamsesLinksAndSceneProvided) - { - { - m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding.bin", m_scene)); - auto loadedBinding = m_logicEngine.findByName("renderPass"); - EXPECT_EQ(&loadedBinding->getRamsesRenderPass(), m_renderPass); - EXPECT_EQ(loadedBinding->getName(), "renderPass"); - - ASSERT_NE(nullptr, loadedBinding->getInputs()); - ASSERT_EQ(std::size_t(RamsesRenderPassBindingImpl::EPropertyIndex_COUNT), loadedBinding->getInputs()->getChildCount()); - EXPECT_EQ(loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_Enabled), loadedBinding->getInputs()->getChild("enabled")); - EXPECT_EQ(EPropertyType::Bool, loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_Enabled)->getType()); - EXPECT_EQ(loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOrder), loadedBinding->getInputs()->getChild("renderOrder")); - EXPECT_EQ(EPropertyType::Int32, loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOrder)->getType()); - EXPECT_EQ(loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_ClearColor), loadedBinding->getInputs()->getChild("clearColor")); - EXPECT_EQ(EPropertyType::Vec4f, loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_ClearColor)->getType()); - EXPECT_EQ(loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOnce), loadedBinding->getInputs()->getChild("renderOnce")); - EXPECT_EQ(EPropertyType::Bool, loadedBinding->getInputs()->getChild(RamsesRenderPassBindingImpl::EPropertyIndex_RenderOnce)->getType()); - } - } - - TEST_F(ARamsesRenderPassBinding_WithRamses_AndFiles, KeepsPropertyValueAfterDeserializationWithScene) - { - const ramses::sceneObjectId_t bindingIdBeforeReload = m_renderPass->getSceneObjectId(); - { - auto& binding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - binding.getInputs()->getChild("renderOrder")->set(42); - m_logicEngine.update(); - EXPECT_EQ(42, m_renderPass->getRenderOrder()); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "logic.bin")); - } - - { - ASSERT_TRUE(m_logicEngine.loadFromFile("logic.bin", m_scene)); - auto loadedBinding = m_logicEngine.findByName("renderPass"); - EXPECT_EQ(loadedBinding->getRamsesRenderPass().getSceneObjectId(), bindingIdBeforeReload); - - ASSERT_NE(nullptr, loadedBinding->getInputs()); - ASSERT_EQ(std::size_t(RamsesRenderPassBindingImpl::EPropertyIndex_COUNT), loadedBinding->getInputs()->getChildCount()); - EXPECT_EQ(42, *loadedBinding->getInputs()->getChild("renderOrder")->get()); - EXPECT_EQ(42, m_renderPass->getRenderOrder()); - } - } - - TEST_F(ARamsesRenderPassBinding_WithRamses_AndFiles, ProducesError_WhenHavingLinkToRenderPass_ButNoSceneWasProvided) - { - { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - tempEngineForSaving.createRamsesRenderPassBinding(*m_renderPass, "AppBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "WithRamsesRenderPass.bin")); - } - { - EXPECT_FALSE(m_logicEngine.loadFromFile("WithRamsesRenderPass.bin")); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(errors.size(), 1u); - EXPECT_EQ(errors[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); - } - } - - // This is sort of a confidence test, testing a combination of: - // - bindings only propagating their values to ramses if the value was set by an incoming link - // - saving and loading files - // - value only re-applied to ramses if changed. Otherwise not. - // The general expectation is that after loading + update(), the logic scene would overwrite ramses - // properties wrapped by a LogicBinding if they are linked to a script - TEST_F(ARamsesRenderPassBinding_WithRamses_AndFiles, SetsOnlyRenderPassValuesForWhichTheBindingInputIsLinked_AfterLoadingFromFile_AndCallingUpdate) - { - { - const std::string_view scriptSrc = R"( - function interface(IN,OUT) - OUT.val = Type:Int32() - end - function run(IN,OUT) - OUT.val = 42 - end - )"; - - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - auto& renderPassBinding = *m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "renderPass"); - - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("val"), *renderPassBinding.getInputs()->getChild("renderOrder"))); - ASSERT_TRUE(m_logicEngine.update()); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "SomeValuesLinked.bin")); - } - - // Set renderOrder to a different value than the one set by the link - m_renderPass->setRenderOrder(13); - // Set renderOnce to custom value - it should not be overwritten by logic at all, because there is no link - // or any set() calls to the corresponding RamsesRenderPassBinding input - m_renderPass->setRenderOnce(true); - - { - EXPECT_TRUE(m_logicEngine.loadFromFile("SomeValuesLinked.bin", m_scene)); - - // nothing happens before update() - EXPECT_EQ(13, m_renderPass->getRenderOrder()); - EXPECT_TRUE(m_renderPass->isRenderOnce()); - - EXPECT_TRUE(m_logicEngine.update()); - - // Script is executed -> link is activated -> binding is updated, only for the linked property - EXPECT_EQ(42, m_renderPass->getRenderOrder()); - EXPECT_TRUE(m_renderPass->isRenderOnce()); - - // Reset uniform manually and call update does nothing (must set binding input explicitly to cause overwrite in ramses) - m_renderPass->setRenderOrder(13); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(13, m_renderPass->getRenderOrder()); - EXPECT_TRUE(m_renderPass->isRenderOnce()); - } - } -} diff --git a/client/logic/unittests/internal/ErrorReportingTest.cpp b/client/logic/unittests/internal/ErrorReportingTest.cpp deleted file mode 100644 index 7eff953ff..000000000 --- a/client/logic/unittests/internal/ErrorReportingTest.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gmock/gmock.h" - -#include "internals/ErrorReporting.h" -#include "ramses-logic/Logger.h" -#include "LogicNodeDummy.h" - -namespace ramses::internal -{ - class AErrorReporting : public ::testing::Test - { - protected: - AErrorReporting() - { - // Explicitly check that default logging does not affect custom error logs - Logger::SetDefaultLogging(false); - - Logger::SetLogHandler([this](ELogLevel type, std::string_view message) { - EXPECT_EQ(ELogLevel::Error, type); - m_loggedErrors.emplace_back(std::string(message)); - }); - } - - void TearDown() override - { - // Unset custom logger to avoid interference with other tests which use logs - Logger::SetLogHandler({}); - } - - ErrorReporting m_errorReporting; - - std::vector m_loggedErrors; - }; - - TEST_F(AErrorReporting, ProducesNoErrorsDuringConstruction) - { - EXPECT_EQ(0u, m_errorReporting.getErrors().size()); - } - - TEST_F(AErrorReporting, ProducesNoLogsDuringConstruction) - { - EXPECT_EQ(0u, m_loggedErrors.size()); - } - - TEST_F(AErrorReporting, StoresSourceLogicObjectWhenProvided) - { - class TestObject : public LogicObject - { - public: - TestObject() : LogicObject(std::make_unique("", 0)) - { - } - }; - TestObject object1; - TestObject object2; - - m_errorReporting.add("error 1", &object1, EErrorType::ContentStateError); - m_errorReporting.add("error 2", &object2, EErrorType::BinaryVersionMismatch); - - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "error 1"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, &object1); - EXPECT_EQ(m_errorReporting.getErrors()[0].type, EErrorType::ContentStateError); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "error 2"); - EXPECT_EQ(m_errorReporting.getErrors()[1].object, &object2); - EXPECT_EQ(m_errorReporting.getErrors()[1].type, EErrorType::BinaryVersionMismatch); - } - - TEST_F(AErrorReporting, StoresErrorsInTheOrderAdded) - { - m_errorReporting.add("error 1", nullptr, EErrorType::IllegalArgument); - m_errorReporting.add("error 2", nullptr, EErrorType::IllegalArgument); - - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "error 1"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "error 2"); - } - - TEST_F(AErrorReporting, LogsErrorsInTheOrderAdded) - { - m_errorReporting.add("error 1", nullptr, EErrorType::IllegalArgument); - m_errorReporting.add("error 2", nullptr, EErrorType::IllegalArgument); - - ASSERT_EQ(m_loggedErrors.size(), 2u); - EXPECT_EQ(m_loggedErrors[0], "error 1"); - EXPECT_EQ(m_loggedErrors[1], "error 2"); - - } - - TEST_F(AErrorReporting, ClearsErrors) - { - m_errorReporting.add("error 1", nullptr, EErrorType::IllegalArgument); - - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - - m_errorReporting.clear(); - - ASSERT_TRUE(m_errorReporting.getErrors().empty()); - } -} diff --git a/client/logic/unittests/res/testScene_01.ramses b/client/logic/unittests/res/testScene_01.ramses deleted file mode 100644 index aa618b70a8ba14fa234e7f2ae9bb2058bb0a74f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5852 zcmd6reP~s67{~86D_t+@W|{pVClXT8vDF$=w>ICVSOe2E!-gHV<92Pk_j2!bC^{Te zh$TTpp<$q9e^fM4Op3}8Az7HxpJIjd@`o>#&_89wdOqho&)K;z(?t?K@VnpN^ZcH- z-}8HZ=bUZr3b!ZYWmbFXOgx#G`#^nFbyam+`PwD%>{2(=HFraGZGCM`O?!L02wOZi zyD9BG>h*fvb8FhlTVnz3pBUg)OI& zuq4VbC)*ri3pq3wv0v1S=Tl|X1nsMM5TAb1N)KJcN^cwz34 z5v9_y67vxeixe+IGR8-judR&}Oo?NbqnTruPdAbu_QDven4c^B_0plU@O6u#KmlDZ z9cvQ0k(kBkdg*YU_!><=w2z%I^@qs^@7YtgW9yW%z0X*F_!ueA(dh7u!2b5D__4a1 z4mQgMue|=*mu(kjoIn27oK+PreZU!1s-JSf$vPJ(&_`C3Y%?GZA&^2rpl#Z8{RLyp z(3H%HuVf5L9WqYHhu z6{^ad@KldE>meBvq_M9V1&l&6M)PdK08cr@iz6gsI34C#-l}u*xJ~Em(oP%Qkjx+y zU=6V_L5F!l?h|eyO|+n(gbK+Rl!JoyH?h;4CpFvT@f{Kj7RN3h9ymDrplmj!uXDiF&y^Z-()h-(!dkE@5vI~p;pv&sSNN*#f^%5cOl=U z_=X_m8&Etd2ptO`c1m!kjPDi8B8q>+DBzEp|wp zyx>B?J%Wn`_X{o&d`GZZa6l0KK|%Dt5Jdm1;3B~v1+lv*h~4jk%LM-rlork*_Q#t+ zO6ISX3R07-m&rPbWQUh;05ALWZ>#jHPZ&V(IIw2{GD!N*FKjj)cN3l6p3~!Hx_s~U zGC;#Di6EWhr-WzZL?l%|t(5$gmU7zN9xv^ZH-u8u0Wyqn#{RfZtn&&Z&EV}M1@TWb zOe}E9Awy(=5oBoZ7qX#Qkn!cXnFmz=sXX zC%@Nl8gG=Xr#B^W((%m}%Gz_}P;8Krft;UTbvKpr+_al$_t-CFnZR?r)W%MQJ(2-L z!x<4AU5+{IlwsGlNf2{Pt*@i``~7P-pwmnCc-iz8(uFX#ZiFWm&VoK2#{BQ1mzGwp zx$xWJpD%tsbYkI|Q-`cuI8)}9NOV8=WeucM*6zfIkQ>qN5XnQ-igl|e&s$`+PMwMo zSB0Fy`lY=@hnIFzZYE>|_4l5r)C1P(&u5M*V|bh`+s(?UtlyONoL;Z} zL1*|X&nxuYn~Zme!+2u)i~>Jf3nZk~*}ZPLJ{p|H;WV6?M5~=X9Br0UQ&l~qFulIm z)i*nw60?7iR!iFLT$t=mrX?>w7l}X1k$?Q@&q(QL1ISS*(LB~1`43u7&o0YI589{> z7r-90$n62ZeB{1(R+cNs;_9W!?F;{X;6&dFg~cysQS4oDEla-#ZC`6^+6wjm&n}Ga XSsJm}hDPd7k%!x;F0zdquUr2CecOqV diff --git a/client/logic/unittests/shared/LogTestUtils.h b/client/logic/unittests/shared/LogTestUtils.h deleted file mode 100644 index 59dca7630..000000000 --- a/client/logic/unittests/shared/LogTestUtils.h +++ /dev/null @@ -1,76 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-logic/Logger.h" - -namespace ramses -{ - class ScopedLogContextLevel - { - public: - explicit ScopedLogContextLevel(ELogLevel verbosityLimit) - : m_savedLogVerbosityLimit(Logger::GetLogVerbosityLimit()) - { - Logger::SetLogVerbosityLimit(verbosityLimit); - } - - ScopedLogContextLevel(ELogLevel logPriority, const ramses::Logger::LogHandlerFunc& handler) - : ScopedLogContextLevel(logPriority) - { - Logger::SetLogHandler(handler); - m_unsetCustomHandler = true; - } - - ~ScopedLogContextLevel() - { - Logger::SetLogVerbosityLimit(m_savedLogVerbosityLimit); - - if (m_unsetCustomHandler) - { - // Set an empty lambda to avoid side effects - Logger::SetLogHandler([](ELogLevel type, std::string_view message){ - (void)type; - (void)message; - }); - } - } - - ScopedLogContextLevel(const ScopedLogContextLevel&) = default; - ScopedLogContextLevel& operator=(const ScopedLogContextLevel&) = default; - - private: - ELogLevel m_savedLogVerbosityLimit; - bool m_unsetCustomHandler = false; - }; - - struct TestLog - { - ELogLevel type; - std::string message; - }; - - class TestLogCollector - { - public: - explicit TestLogCollector(ELogLevel verbosityLimit) - : m_logCollector(verbosityLimit, [this](ELogLevel type, std::string_view message) - { - logs.emplace_back(TestLog{type, std::string{message}}); - }) - { - } - - std::vector logs; - - private: - ScopedLogContextLevel m_logCollector; - }; -} diff --git a/client/logic/unittests/shared/LogicEngineTest_Base.h b/client/logic/unittests/shared/LogicEngineTest_Base.h deleted file mode 100644 index 1770a2b31..000000000 --- a/client/logic/unittests/shared/LogicEngineTest_Base.h +++ /dev/null @@ -1,167 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "gtest/gtest.h" -#include "gmock/gmock.h" - -#include "RamsesTestUtils.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-utils.h" - -namespace ramses -{ - class ALogicEngineBase - { - public: - explicit ALogicEngineBase(ramses::EFeatureLevel featureLevel = ramses::EFeatureLevel_Latest) - : m_logicEngine{ featureLevel } - { - // make ramses camera valid, needed for anchor points - m_camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f); - m_renderGroup->addMeshNode(*m_meshNode); - m_saveFileConfigNoValidation.setValidationEnabled(false); - } - - static LuaConfig CreateDeps(const std::vector>& dependencies) - { - LuaConfig config; - for (const auto& [alias, module] : dependencies) - { - config.addDependency(alias, *module); - } - - return config; - } - - static LuaConfig WithStdModules(std::initializer_list modules) - { - LuaConfig config; - for (auto m : modules) - { - config.addStandardModuleDependency(m); - } - return config; - } - - static bool SaveToFileWithoutValidation(LogicEngine& logicEngine, std::string_view filename) - { - SaveFileConfig configNoValidation; - configNoValidation.setValidationEnabled(false); - return logicEngine.saveToFile(filename, configNoValidation); - } - - protected: - LogicEngine m_logicEngine; - SaveFileConfig m_saveFileConfigNoValidation; - RamsesTestSetup m_ramses; - ramses::Scene* m_scene = { m_ramses.createScene() }; - ramses::Node* m_node = { m_scene->createNode() }; - ramses::OrthographicCamera* m_camera = { m_scene->createOrthographicCamera() }; - ramses::Appearance* m_appearance = { &RamsesTestSetup::CreateTrivialTestAppearance(*m_scene) }; - ramses::RenderPass* m_renderPass = { m_scene->createRenderPass() }; - ramses::RenderGroup* m_renderGroup = { m_scene->createRenderGroup() }; - ramses::MeshNode* m_meshNode = { m_scene->createMeshNode("meshNode") }; - - const std::string_view m_valid_empty_script = R"( - function interface(IN,OUT) - end - function run(IN,OUT) - end - )"; - - const std::string_view m_invalid_empty_script = R"( - )"; - - const std::string_view m_moduleSourceCode = R"( - local mymath = {} - function mymath.add(a,b) - print(a+b) - end - return mymath - )"; - - const std::string_view m_interfaceSourceCode = R"( - function interface(inout_params) - inout_params.param_vec3f = Type:Vec3f() - end - )"; - - void recreate() - { - const ramses::sceneId_t sceneId = m_scene->getSceneId(); - - m_ramses.destroyScene(*m_scene); - m_scene = m_ramses.createScene(sceneId); - m_node = m_scene->createNode(); - m_camera = m_scene->createOrthographicCamera(); - m_appearance = &RamsesTestSetup::CreateTrivialTestAppearance(*m_scene); - m_renderPass = m_scene->createRenderPass(); - m_renderGroup = m_scene->createRenderGroup(); - m_meshNode = m_scene->createMeshNode(); - } - - RamsesRenderGroupBinding* createRenderGroupBinding(LogicEngine& logicEngine) - { - RamsesRenderGroupBindingElements elements; - EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); - return logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "renderGroupBinding"); - } - - RamsesRenderGroupBinding* createRenderGroupBinding() - { - return createRenderGroupBinding(m_logicEngine); - } - - static SkinBinding* createSkinBinding(const RamsesNodeBinding& nodeBinding, RamsesAppearanceBinding& appearanceBinding, LogicEngine& logicEngine) - { - ramses::UniformInput uniform; - appearanceBinding.getRamsesAppearance().getEffect().findUniformInput("jointMat", uniform); - EXPECT_TRUE(uniform.isValid()); - return logicEngine.createSkinBinding({ &nodeBinding }, { matrix44f{ 0.f } }, appearanceBinding, uniform, "skin"); - } - - SkinBinding* createSkinBinding(LogicEngine& logicEngine) - { - const auto nodeBinding = logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeForSkin"); - auto appearanceBinding = logicEngine.createRamsesAppearanceBinding(*m_appearance, "appearanceForSkin"); - return createSkinBinding(*nodeBinding, *appearanceBinding, logicEngine); - } - - size_t m_emptySerializedSizeTotal{164u}; - }; - - class ALogicEngine : public ALogicEngineBase, public ::testing::Test - { - public: - explicit ALogicEngine(ramses::EFeatureLevel featureLevel = ramses::EFeatureLevel_Latest) - : ALogicEngineBase{ featureLevel } - { - } - }; -} diff --git a/client/ramses-client-api/Appearance.cpp b/client/ramses-client-api/Appearance.cpp deleted file mode 100644 index dfee20513..000000000 --- a/client/ramses-client-api/Appearance.cpp +++ /dev/null @@ -1,325 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/DataObject.h" - -// internal -#include "AppearanceImpl.h" - -namespace ramses -{ - Appearance::Appearance(std::unique_ptr impl) - : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } - { - } - - status_t Appearance::setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) - { - const status_t status = m_impl.setBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); - LOG_HL_CLIENT_API4(status, srcColor, destColor, srcAlpha, destAlpha); - return status; - } - - status_t Appearance::getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const - { - return m_impl.getBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); - } - - status_t Appearance::setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha) - { - const status_t status = m_impl.setBlendingOperations(operationColor, operationAlpha); - LOG_HL_CLIENT_API2(status, operationColor, operationAlpha); - return status; - } - - status_t Appearance::getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const - { - return m_impl.getBlendingOperations(operationColor, operationAlpha); - } - - ramses::status_t Appearance::setBlendingColor(const vec4f& color) - { - const status_t status = m_impl.setBlendingColor(color); - LOG_HL_CLIENT_API4(status, color[0], color[1], color[2], color[3]); - return status; - } - - ramses::status_t Appearance::getBlendingColor(vec4f& color) const - { - return m_impl.getBlendingColor(color); - } - - status_t Appearance::setDepthWrite(EDepthWrite mode) - { - const status_t status = m_impl.setDepthWrite(mode); - LOG_HL_CLIENT_API1(status, mode); - return status; - } - - status_t Appearance::getDepthWriteMode(EDepthWrite& mode) const - { - return m_impl.getDepthWriteMode(mode); - } - - status_t Appearance::setDepthFunction(EDepthFunc func) - { - const status_t status = m_impl.setDepthFunction(func); - LOG_HL_CLIENT_API1(status, func); - return status; - } - - status_t Appearance::getDepthFunction(EDepthFunc& func) const - { - return m_impl.getDepthFunction(func); - } - - status_t Appearance::setScissorTest(EScissorTest state, int16_t x, int16_t y, uint16_t width, uint16_t height) - { - const status_t status = m_impl.setScissorTest(state, x, y, width, height); - LOG_HL_CLIENT_API5(status, state, x, y, width, height); - return status; - } - - status_t Appearance::getScissorTestState(EScissorTest& state) const - { - return m_impl.getScissorTestState(state); - } - - status_t Appearance::getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const - { - return m_impl.getScissorRegion(x, y, width, height); - } - - status_t Appearance::setStencilFunction(EStencilFunc func, uint8_t ref, uint8_t mask) - { - const status_t status = m_impl.setStencilFunc(func, ref, mask); - LOG_HL_CLIENT_API3(status, func, ref, mask); - return status; - } - - status_t Appearance::getStencilFunction(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const - { - return m_impl.getStencilFunc(func, ref, mask); - } - - status_t Appearance::setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass) - { - const status_t status = m_impl.setStencilOperation(sfail, dpfail, dppass); - LOG_HL_CLIENT_API3(status, sfail, dpfail, dppass); - return status; - } - - status_t Appearance::getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const - { - return m_impl.getStencilOperation(sfail, dpfail, dppass); - } - - status_t Appearance::setCullingMode(ECullMode mode) - { - const status_t status = m_impl.setCullingMode(mode); - LOG_HL_CLIENT_API1(status, mode); - return status; - } - - status_t Appearance::getCullingMode(ECullMode& mode) const - { - return m_impl.getCullingMode(mode); - } - - status_t Appearance::setDrawMode(EDrawMode mode) - { - const status_t status = m_impl.setDrawMode(mode); - LOG_HL_CLIENT_API1(status, mode); - return status; - } - - status_t Appearance::getDrawMode(EDrawMode& mode) const - { - return m_impl.getDrawMode(mode); - } - - status_t Appearance::setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha) - { - const status_t status = m_impl.setColorWriteMask(writeRed, writeGreen, writeBlue, writeAlpha); - LOG_HL_CLIENT_API4(status, writeRed, writeGreen, writeBlue, writeAlpha); - return status; - } - - status_t Appearance::getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const - { - return m_impl.getColorWriteMask(writeRed, writeGreen, writeBlue, writeAlpha); - } - - status_t Appearance::setInputTexture(const UniformInput& input, const TextureSampler& textureSampler) - { - const status_t status = m_impl.setInputTexture(input.m_impl, textureSampler.m_impl); - LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(textureSampler)); - return status; - } - - status_t Appearance::getInputTexture(const UniformInput& input, const TextureSampler*& textureSampler) const - { - return m_impl.getInputTexture(input.m_impl, textureSampler); - } - - status_t Appearance::setInputTexture(const UniformInput& input, const TextureSamplerMS& textureSampler) - { - const status_t status = m_impl.setInputTexture(input.m_impl, textureSampler.m_impl); - LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(textureSampler)); - return status; - } - - status_t Appearance::getInputTextureMS(const UniformInput& input, const TextureSamplerMS*& textureSampler) const - { - return m_impl.getInputTextureMS(input.m_impl, textureSampler); - } - - status_t Appearance::setInputTexture(const UniformInput& input, const TextureSamplerExternal& textureSampler) - { - const status_t status = m_impl.setInputTexture(input.m_impl, textureSampler.m_impl); - LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(textureSampler)); - return status; - } - - status_t Appearance::getInputTextureExternal(const UniformInput& input, const TextureSamplerExternal*& textureSampler) const - { - return m_impl.getInputTextureExternal(input.m_impl, textureSampler); - } - - status_t Appearance::bindInput(const UniformInput& input, const DataObject& dataObject) - { - const status_t status = m_impl.bindInput(input.m_impl, dataObject.m_impl); - LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(dataObject)); - return status; - } - - status_t Appearance::unbindInput(const UniformInput& input) - { - const status_t status = m_impl.unbindInput(input.m_impl); - LOG_HL_CLIENT_API1(status, LOG_API_GENERIC_OBJECT_STRING(input)); - return status; - } - - bool Appearance::isInputBound(const UniformInput& input) const - { - return m_impl.isInputBound(input.m_impl); - } - - const DataObject* Appearance::getDataObjectBoundToInput(const UniformInput& input) const - { - return m_impl.getBoundDataObject(input.m_impl); - } - - const Effect& Appearance::getEffect() const - { - return m_impl.getEffect(); - } - - template status_t Appearance::setInputValueInternal(const UniformInput& input, T&& value) - { - // API uses ref/move forwarding for possibility to move but current implementation does not make use of it - return setInputValueInternal(input, 1u, &value); - } - - template status_t Appearance::setInputValueInternal(const UniformInput& input, size_t elementCount, const T* values) - { - return m_impl.setInputValue(input.m_impl, elementCount, values); - } - - template status_t Appearance::getInputValueInternal(const UniformInput& input, T& value) const - { - return getInputValueInternal(input, 1u, &value); - } - - template status_t Appearance::getInputValueInternal(const UniformInput& input, size_t elementCount, T* valuesOut) const - { - return m_impl.getInputValue(input.m_impl, elementCount, valuesOut); - } - - // const l-value instances - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const int32_t&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const float&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const vec2i&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const vec3i&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const vec4i&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const vec2f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const vec3f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const vec4f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const matrix22f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const matrix33f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, const matrix44f&); - - // l-value instances - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, int32_t&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, float&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec2i&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec3i&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec4i&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec2f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec3f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec4f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, matrix22f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, matrix33f&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, matrix44f&); - - // r-value instances - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, int32_t&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, float&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec2i&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec3i&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec4i&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec2f&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec3f&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, vec4f&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, matrix22f&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, matrix33f&&); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, matrix44f&&); - - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const int32_t*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const float*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const vec2i*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const vec3i*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const vec4i*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const vec2f*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const vec3f*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const vec4f*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const matrix22f*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const matrix33f*); - template RAMSES_API status_t Appearance::setInputValueInternal(const UniformInput&, size_t, const matrix44f*); - - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, int32_t&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, float&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, vec2i&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, vec3i&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, vec4i&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, vec2f&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, vec3f&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, vec4f&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, matrix22f&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, matrix33f&) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, matrix44f&) const; - - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, int32_t*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, float*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, vec2i*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, vec3i*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, vec4i*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, vec2f*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, vec3f*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, vec4f*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, matrix22f*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, matrix33f*) const; - template RAMSES_API status_t Appearance::getInputValueInternal(const UniformInput&, size_t, matrix44f*) const; -} diff --git a/client/ramses-client-api/ArrayBuffer.cpp b/client/ramses-client-api/ArrayBuffer.cpp deleted file mode 100644 index 3e1c62b4b..000000000 --- a/client/ramses-client-api/ArrayBuffer.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/ArrayBuffer.h" - -// Internal -#include "ArrayBufferImpl.h" - -namespace ramses -{ - ArrayBuffer::ArrayBuffer(std::unique_ptr impl) - : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } - { - } - - template status_t ArrayBuffer::updateDataInternal(uint32_t firstElement, uint32_t numElements, const T* bufferData) - { - if (GetEDataType() != m_impl.getDataType()) - return m_impl.addErrorEntry("ArrayBuffer::updateData: Wrong data type used to update buffer!"); - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) we store all data types as bytes internally - const status_t status = m_impl.updateData(firstElement, numElements, reinterpret_cast(bufferData)); - LOG_HL_CLIENT_API3(status, firstElement, numElements, LOG_API_GENERIC_PTR_STRING(bufferData)); - return status; - } - - uint32_t ArrayBuffer::getMaximumNumberOfElements() const - { - return m_impl.getMaximumNumberOfElements(); - } - - uint32_t ArrayBuffer::getUsedNumberOfElements() const - { - return m_impl.getUsedNumberOfElements(); - } - - EDataType ArrayBuffer::getDataType() const - { - return m_impl.getDataType(); - } - - template status_t ArrayBuffer::getDataInternal(T* buffer, uint32_t numElements) const - { - if (GetEDataType() != m_impl.getDataType()) - return m_impl.addErrorEntry("ArrayBuffer::getData: Wrong data type used to get data!"); - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) we store all data types as bytes internally - return m_impl.getData(reinterpret_cast(buffer), numElements); - } - - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const uint16_t*); - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const uint32_t*); - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const float*); - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const vec2f*); - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const vec3f*); - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const vec4f*); - template RAMSES_API status_t ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const Byte*); - - template RAMSES_API status_t ArrayBuffer::getDataInternal(uint16_t*, uint32_t) const; - template RAMSES_API status_t ArrayBuffer::getDataInternal(uint32_t*, uint32_t) const; - template RAMSES_API status_t ArrayBuffer::getDataInternal(float*, uint32_t) const; - template RAMSES_API status_t ArrayBuffer::getDataInternal(vec2f*, uint32_t) const; - template RAMSES_API status_t ArrayBuffer::getDataInternal(vec3f*, uint32_t) const; - template RAMSES_API status_t ArrayBuffer::getDataInternal(vec4f*, uint32_t) const; - template RAMSES_API status_t ArrayBuffer::getDataInternal(Byte*, uint32_t) const; -} diff --git a/client/ramses-client-api/AttributeInput.cpp b/client/ramses-client-api/AttributeInput.cpp deleted file mode 100644 index dc55e2f21..000000000 --- a/client/ramses-client-api/AttributeInput.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/AttributeInput.h" -#include "EffectInputImpl.h" - -namespace ramses -{ - AttributeInput::AttributeInput() - : EffectInput{ std::make_unique() } - { - } - - AttributeInput::~AttributeInput() = default; - - AttributeInput::AttributeInput(const AttributeInput& other) - : EffectInput{ std::make_unique(other.m_impl) } - { - } - - AttributeInput::AttributeInput(AttributeInput&& other) noexcept - : EffectInput{ std::unique_ptr(static_cast(other.StatusObject::m_impl.release())) } - { - } - - AttributeInput& AttributeInput::operator=(const AttributeInput& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - AttributeInput& AttributeInput::operator=(AttributeInput&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - EEffectAttributeSemantic AttributeInput::getSemantics() const - { - return m_impl.get().getAttributeSemantics(); - } -} diff --git a/client/ramses-client-api/DataObject.cpp b/client/ramses-client-api/DataObject.cpp deleted file mode 100644 index 202351d6b..000000000 --- a/client/ramses-client-api/DataObject.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/DataObject.h" - -//internal -#include "DataObjectImpl.h" - -namespace ramses -{ - DataObject::DataObject(std::unique_ptr impl) - : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } - { - } - - EDataType DataObject::getDataType() const - { - return m_impl.getDataType(); - } - - template - status_t DataObject::setValueInternal(T&& value) - { - return m_impl.setValue(value); - - } - - template - status_t DataObject::getValueInternal(T& value) const - { - return m_impl.getValue(value); - } - - // const l-value instances - template RAMSES_API status_t DataObject::setValueInternal(const int32_t&); - template RAMSES_API status_t DataObject::setValueInternal(const float&); - template RAMSES_API status_t DataObject::setValueInternal(const vec2i&); - template RAMSES_API status_t DataObject::setValueInternal(const vec3i&); - template RAMSES_API status_t DataObject::setValueInternal(const vec4i&); - template RAMSES_API status_t DataObject::setValueInternal(const vec2f&); - template RAMSES_API status_t DataObject::setValueInternal(const vec3f&); - template RAMSES_API status_t DataObject::setValueInternal(const vec4f&); - template RAMSES_API status_t DataObject::setValueInternal(const matrix22f&); - template RAMSES_API status_t DataObject::setValueInternal(const matrix33f&); - template RAMSES_API status_t DataObject::setValueInternal(const matrix44f&); - - // l-value instances - template RAMSES_API status_t DataObject::setValueInternal(int32_t&); - template RAMSES_API status_t DataObject::setValueInternal(float&); - template RAMSES_API status_t DataObject::setValueInternal(vec2i&); - template RAMSES_API status_t DataObject::setValueInternal(vec3i&); - template RAMSES_API status_t DataObject::setValueInternal(vec4i&); - template RAMSES_API status_t DataObject::setValueInternal(vec2f&); - template RAMSES_API status_t DataObject::setValueInternal(vec3f&); - template RAMSES_API status_t DataObject::setValueInternal(vec4f&); - template RAMSES_API status_t DataObject::setValueInternal(matrix22f&); - template RAMSES_API status_t DataObject::setValueInternal(matrix33f&); - template RAMSES_API status_t DataObject::setValueInternal(matrix44f&); - - // r-value instances - template RAMSES_API status_t DataObject::setValueInternal(int32_t&&); - template RAMSES_API status_t DataObject::setValueInternal(float&&); - template RAMSES_API status_t DataObject::setValueInternal(vec2i&&); - template RAMSES_API status_t DataObject::setValueInternal(vec3i&&); - template RAMSES_API status_t DataObject::setValueInternal(vec4i&&); - template RAMSES_API status_t DataObject::setValueInternal(vec2f&&); - template RAMSES_API status_t DataObject::setValueInternal(vec3f&&); - template RAMSES_API status_t DataObject::setValueInternal(vec4f&&); - template RAMSES_API status_t DataObject::setValueInternal(matrix22f&&); - template RAMSES_API status_t DataObject::setValueInternal(matrix33f&&); - template RAMSES_API status_t DataObject::setValueInternal(matrix44f&&); - - template RAMSES_API status_t DataObject::getValueInternal(int32_t&) const; - template RAMSES_API status_t DataObject::getValueInternal(float&) const; - template RAMSES_API status_t DataObject::getValueInternal(vec2i&) const; - template RAMSES_API status_t DataObject::getValueInternal(vec3i&) const; - template RAMSES_API status_t DataObject::getValueInternal(vec4i&) const; - template RAMSES_API status_t DataObject::getValueInternal(vec2f&) const; - template RAMSES_API status_t DataObject::getValueInternal(vec3f&) const; - template RAMSES_API status_t DataObject::getValueInternal(vec4f&) const; - template RAMSES_API status_t DataObject::getValueInternal(matrix22f&) const; - template RAMSES_API status_t DataObject::getValueInternal(matrix33f&) const; - template RAMSES_API status_t DataObject::getValueInternal(matrix44f&) const; -} diff --git a/client/ramses-client-api/Effect.cpp b/client/ramses-client-api/Effect.cpp deleted file mode 100644 index 5e332d21e..000000000 --- a/client/ramses-client-api/Effect.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" - -// internal -#include "EffectImpl.h" - -namespace ramses -{ - Effect::Effect(std::unique_ptr impl) - : Resource{ std::move(impl) } - , m_impl{ static_cast(Resource::m_impl) } - { - } - - size_t Effect::getUniformInputCount() const - { - return m_impl.getUniformInputCount(); - } - - size_t Effect::getAttributeInputCount() const - { - return m_impl.getAttributeInputCount(); - } - - status_t Effect::getUniformInput(size_t index, UniformInput& uniformInput) const - { - return m_impl.getUniformInput(index, uniformInput.m_impl); - } - - status_t Effect::findUniformInput(EEffectUniformSemantic uniformSemantic, UniformInput& uniformInput) const - { - return m_impl.findUniformInput(uniformSemantic, uniformInput.m_impl); - } - - status_t Effect::getAttributeInput(size_t index, AttributeInput& attributeInput) const - { - return m_impl.getAttributeInput(index, attributeInput.m_impl); - } - - status_t Effect::findAttributeInput(EEffectAttributeSemantic attributeSemantic, AttributeInput& attributeInput) const - { - return m_impl.findAttributeInput(attributeSemantic, attributeInput.m_impl); - } - - bool Effect::hasGeometryShader() const - { - return m_impl.hasGeometryShader(); - } - - status_t Effect::getGeometryShaderInputType(EDrawMode& expectedGeometryInputType) const - { - return m_impl.getGeometryShaderInputType(expectedGeometryInputType); - } - - status_t Effect::findUniformInput(std::string_view inputName, UniformInput& uniformInput) const - { - return m_impl.findUniformInput(inputName, uniformInput.m_impl); - } - - status_t Effect::findAttributeInput(std::string_view inputName, AttributeInput& attributeInput) const - { - return m_impl.findAttributeInput(inputName, attributeInput.m_impl); - } -} diff --git a/client/ramses-client-api/EffectDescription.cpp b/client/ramses-client-api/EffectDescription.cpp deleted file mode 100644 index 5af7574a6..000000000 --- a/client/ramses-client-api/EffectDescription.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/EffectDescription.h" - -// internal -#include "EffectDescriptionImpl.h" - -namespace ramses -{ - EffectDescription::EffectDescription() - : StatusObject{ std::make_unique() } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - EffectDescription::~EffectDescription() = default; - - EffectDescription::EffectDescription(const EffectDescription& other) - : StatusObject{ std::make_unique(other.m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - EffectDescription::EffectDescription(EffectDescription&& other) noexcept - : StatusObject{ std::move(other.StatusObject::m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - EffectDescription& EffectDescription::operator=(const EffectDescription& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - EffectDescription& EffectDescription::operator=(EffectDescription&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - status_t EffectDescription::setVertexShader(std::string_view shaderSource) - { - const status_t status = m_impl.get().setVertexShader(shaderSource); - LOG_HL_CLIENT_API1(status, shaderSource); - return status; - } - - status_t EffectDescription::setFragmentShader(std::string_view shaderSource) - { - const status_t status = m_impl.get().setFragmentShader(shaderSource); - LOG_HL_CLIENT_API1(status, shaderSource); - return status; - } - - status_t EffectDescription::setGeometryShader(std::string_view shaderSource) - { - const status_t status = m_impl.get().setGeometryShader(shaderSource); - LOG_HL_CLIENT_API1(status, shaderSource); - return status; - } - - status_t EffectDescription::setVertexShaderFromFile(std::string_view shaderSourceFileName) - { - const status_t status = m_impl.get().setVertexShaderFromFile(shaderSourceFileName); - LOG_HL_CLIENT_API1(status, shaderSourceFileName); - return status; - } - - status_t EffectDescription::setFragmentShaderFromFile(std::string_view shaderSourceFileName) - { - const status_t status = m_impl.get().setFragmentShaderFromFile(shaderSourceFileName); - LOG_HL_CLIENT_API1(status, shaderSourceFileName); - return status; - } - - status_t EffectDescription::setGeometryShaderFromFile(std::string_view shaderSourceFileName) - { - const status_t status = m_impl.get().setGeometryShaderFromFile(shaderSourceFileName); - LOG_HL_CLIENT_API1(status, shaderSourceFileName); - return status; - } - - status_t EffectDescription::addCompilerDefine(std::string_view define) - { - const status_t status = m_impl.get().addCompilerDefine(define); - LOG_HL_CLIENT_API1(status, define); - return status; - } - - status_t EffectDescription::setUniformSemantic(std::string_view inputName, EEffectUniformSemantic semanticType) - { - const status_t status = m_impl.get().setUniformSemantic(inputName, semanticType); - LOG_HL_CLIENT_API2(status, inputName, semanticType); - return status; - } - - status_t EffectDescription::setAttributeSemantic(std::string_view inputName, EEffectAttributeSemantic semanticType) - { - const status_t status = m_impl.get().setAttributeSemantic(inputName, semanticType); - LOG_HL_CLIENT_API2(status, inputName, semanticType); - return status; - } - - const char* EffectDescription::getVertexShader() const - { - return m_impl.get().getVertexShader(); - } - - const char* EffectDescription::getFragmentShader() const - { - return m_impl.get().getFragmentShader(); - } - - const char* EffectDescription::getGeometryShader() const - { - return m_impl.get().getGeometryShader(); - } - - size_t EffectDescription::getNumberOfCompilerDefines() const - { - return m_impl.get().getNumberOfCompilerDefines(); - } - - const char* EffectDescription::getCompilerDefine(size_t index) const - { - return m_impl.get().getCompilerDefine(index); - } -} diff --git a/client/ramses-client-api/EffectInput.cpp b/client/ramses-client-api/EffectInput.cpp deleted file mode 100644 index 90b534eb4..000000000 --- a/client/ramses-client-api/EffectInput.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/EffectInput.h" -#include "EffectInputImpl.h" - -namespace ramses -{ - EffectInput::EffectInput(std::unique_ptr effectInputImpl) - : StatusObject{ std::move(effectInputImpl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - const char* EffectInput::getName() const - { - return m_impl.get().getName().c_str(); - } - - bool EffectInput::isValid() const - { - return m_impl.get().isValid(); - } - - std::optional EffectInput::getDataType() const - { - return m_impl.get().getDataType(); - } -} diff --git a/client/ramses-client-api/GeometryBinding.cpp b/client/ramses-client-api/GeometryBinding.cpp deleted file mode 100644 index bf40a2324..000000000 --- a/client/ramses-client-api/GeometryBinding.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/ArrayBuffer.h" - -// internal -#include "GeometryBindingImpl.h" - -namespace ramses -{ - GeometryBinding::GeometryBinding(std::unique_ptr impl) - : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } - { - } - - status_t GeometryBinding::setIndices(const ArrayResource& indicesResource) - { - const status_t status = m_impl.setIndices(indicesResource.m_impl); - LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(indicesResource)); - return status; - } - - status_t GeometryBinding::setIndices(const ArrayBuffer& arrayBuffer) - { - const status_t status = m_impl.setIndices(arrayBuffer.m_impl); - LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(arrayBuffer)); - return status; - } - - status_t GeometryBinding::setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint32_t instancingDivisor) - { - const status_t status = m_impl.setInputBuffer(attributeInput.m_impl, arrayResource.m_impl, instancingDivisor, 0u, 0u); - LOG_HL_CLIENT_API3(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayResource), instancingDivisor); - return status; - } - - status_t GeometryBinding::setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint16_t offset, uint16_t stride) - { - const status_t status = m_impl.setInputBuffer(attributeInput.m_impl, arrayResource.m_impl, 0u, offset, stride); - LOG_HL_CLIENT_API4(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayResource), offset, stride); - return status; - } - - status_t GeometryBinding::setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint32_t instancingDivisor /*= 0*/) - { - const status_t status = m_impl.setInputBuffer(attributeInput.m_impl, arrayBuffer.m_impl, instancingDivisor, 0u, 0u); - LOG_HL_CLIENT_API3(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayBuffer), instancingDivisor); - return status; - } - - status_t GeometryBinding::setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint16_t offset, uint16_t stride) - { - const status_t status = m_impl.setInputBuffer(attributeInput.m_impl, arrayBuffer.m_impl, 0u, offset, stride); - LOG_HL_CLIENT_API4(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayBuffer), offset, stride); - return status; - } - - const Effect& GeometryBinding::getEffect() const - { - return m_impl.getEffect(); - } -} diff --git a/client/ramses-client-api/RamsesObject.cpp b/client/ramses-client-api/RamsesObject.cpp deleted file mode 100644 index 19b6e052b..000000000 --- a/client/ramses-client-api/RamsesObject.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/RamsesObject.h" - -// internal -#include "RamsesObjectImpl.h" - -namespace ramses -{ - RamsesObject::RamsesObject(std::unique_ptr impl) - : StatusObject{ std::move(impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - m_impl.setRamsesObject(*this); - } - - const char* RamsesObject::getName() const - { - return m_impl.getName().c_str(); - } - - status_t RamsesObject::setName(std::string_view name) - { - const status_t status =m_impl.setName(*this, name); - LOG_HL_CLIENT_API1(status, name); - return status; - } - - ramses::ERamsesObjectType RamsesObject::getType() const - { - return m_impl.getType(); - } - - bool RamsesObject::isOfType(ERamsesObjectType type) const - { - return m_impl.isOfType(type); - } -} diff --git a/client/ramses-client-api/RenderGroup.cpp b/client/ramses-client-api/RenderGroup.cpp deleted file mode 100644 index 14f41b314..000000000 --- a/client/ramses-client-api/RenderGroup.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/MeshNode.h" - -// internal -#include "RenderGroupImpl.h" - -namespace ramses -{ - RenderGroup::RenderGroup(std::unique_ptr impl) - : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } - { - } - - status_t RenderGroup::addMeshNode(const MeshNode& mesh, int32_t orderWithinGroup) - { - const status_t status = m_impl.addMeshNode(mesh.m_impl, orderWithinGroup); - LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(mesh), orderWithinGroup); - return status; - } - - status_t RenderGroup::removeMeshNode(const MeshNode& mesh) - { - const status_t status = m_impl.remove(mesh.m_impl); - LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(mesh)); - return status; - } - - bool RenderGroup::containsMeshNode(const MeshNode& mesh) const - { - return m_impl.contains(mesh.m_impl); - } - - status_t RenderGroup::getMeshNodeOrder(const MeshNode& mesh, int32_t& orderWithinGroup) const - { - return m_impl.getMeshNodeOrder(mesh.m_impl, orderWithinGroup); - } - - status_t RenderGroup::addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinGroup) - { - const status_t status = m_impl.addRenderGroup(renderGroup.m_impl, orderWithinGroup); - LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(renderGroup), orderWithinGroup); - return status; - } - - status_t RenderGroup::removeRenderGroup(const RenderGroup& renderGroup) - { - const status_t status = m_impl.remove(renderGroup.m_impl); - LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(renderGroup)); - return status; - } - - bool RenderGroup::containsRenderGroup(const RenderGroup& renderGroup) const - { - return m_impl.contains(renderGroup.m_impl); - } - - status_t RenderGroup::getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinGroup) const - { - return m_impl.getRenderGroupOrder(renderGroup.m_impl, orderWithinGroup); - } - - status_t RenderGroup::removeAllRenderables() - { - const status_t status = m_impl.removeAllRenderables(); - LOG_HL_CLIENT_API_NOARG(status); - return status; - } - - ramses::status_t RenderGroup::removeAllRenderGroups() - { - const status_t status = m_impl.removeAllRenderGroups(); - LOG_HL_CLIENT_API_NOARG(status); - return status; - } -} diff --git a/client/ramses-client-api/RenderTargetDescription.cpp b/client/ramses-client-api/RenderTargetDescription.cpp deleted file mode 100644 index a5dc0dbda..000000000 --- a/client/ramses-client-api/RenderTargetDescription.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/RenderBuffer.h" - -// internal -#include "RenderTargetDescriptionImpl.h" - -namespace ramses -{ - RenderTargetDescription::RenderTargetDescription() - : StatusObject{ std::make_unique() } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RenderTargetDescription::~RenderTargetDescription() = default; - - RenderTargetDescription::RenderTargetDescription(const RenderTargetDescription& other) - : StatusObject{ std::make_unique(other.m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RenderTargetDescription::RenderTargetDescription(RenderTargetDescription&& other) noexcept - : StatusObject{ std::move(other.StatusObject::m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RenderTargetDescription& RenderTargetDescription::operator=(const RenderTargetDescription& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - RenderTargetDescription& RenderTargetDescription::operator=(RenderTargetDescription&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - status_t RenderTargetDescription::addRenderBuffer(const RenderBuffer& renderBuffer) - { - const status_t status = m_impl.get().addRenderBuffer(renderBuffer.m_impl); - LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(renderBuffer)); - return status; - } -} diff --git a/client/ramses-client-api/SceneConfig.cpp b/client/ramses-client-api/SceneConfig.cpp deleted file mode 100644 index 299a55076..000000000 --- a/client/ramses-client-api/SceneConfig.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-client-api/SceneConfig.h" - -// internal -#include "SceneConfigImpl.h" -#include "APILoggingMacros.h" - -namespace ramses -{ - SceneConfig::SceneConfig() - : StatusObject{ std::make_unique() } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - SceneConfig::~SceneConfig() = default; - - SceneConfig::SceneConfig(const SceneConfig& other) - : StatusObject{ std::make_unique(other.m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - SceneConfig::SceneConfig(SceneConfig&& other) noexcept - : StatusObject{ std::move(other.StatusObject::m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - SceneConfig& SceneConfig::operator=(const SceneConfig& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - SceneConfig& SceneConfig::operator=(SceneConfig&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - status_t SceneConfig::setPublicationMode(EScenePublicationMode publicationMode) - { - const status_t status = m_impl.get().setPublicationMode(publicationMode); - LOG_HL_CLIENT_API1(status, publicationMode); - return status; - } -} diff --git a/client/ramses-client-api/UniformInput.cpp b/client/ramses-client-api/UniformInput.cpp deleted file mode 100644 index 139a64f79..000000000 --- a/client/ramses-client-api/UniformInput.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/UniformInput.h" -#include "EffectInputImpl.h" - -namespace ramses -{ - UniformInput::UniformInput() - : EffectInput{ std::make_unique() } - { - } - - UniformInput::~UniformInput() = default; - - UniformInput::UniformInput(const UniformInput& other) - : EffectInput{ std::make_unique(other.m_impl) } - { - } - - UniformInput::UniformInput(UniformInput&& other) noexcept - : EffectInput{ std::unique_ptr(static_cast(other.StatusObject::m_impl.release())) } - { - } - - UniformInput& UniformInput::operator=(const UniformInput& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - UniformInput& UniformInput::operator=(UniformInput&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - EEffectUniformSemantic UniformInput::getSemantics() const - { - return m_impl.get().getUniformSemantics(); - } - - size_t UniformInput::getElementCount() const - { - return m_impl.get().getElementCount(); - } -} diff --git a/client/ramses-client-api/include/ramses-client-api/AttributeInput.h b/client/ramses-client-api/include/ramses-client-api/AttributeInput.h deleted file mode 100644 index 6b31ab78d..000000000 --- a/client/ramses-client-api/include/ramses-client-api/AttributeInput.h +++ /dev/null @@ -1,69 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_ATTRIBUTEINPUT_H -#define RAMSES_ATTRIBUTEINPUT_H - -#include "ramses-client-api/EffectInput.h" -#include "ramses-client-api/EffectInputSemantic.h" - -namespace ramses -{ - /** - * @ingroup CoreAPI - * @brief The AttributeInput is a description of an attribute effect input - */ - class AttributeInput : public EffectInput - { - public: - /** - * @brief Constructor of AttributeInput. - */ - RAMSES_API AttributeInput(); - - /** - * @brief Destructor of AttributeInput. - */ - RAMSES_API ~AttributeInput() override; - - /** - * @brief Returns the effect input semantics. - * - * @return Effect input semantics - */ - [[nodiscard]] RAMSES_API EEffectAttributeSemantic getSemantics() const; - - /** - * @brief Copy constructor - * @param other source to copy from - */ - RAMSES_API AttributeInput(const AttributeInput& other); - - /** - * @brief Move constructor - * @param other source to move from - */ - RAMSES_API AttributeInput(AttributeInput&& other) noexcept; - - /** - * @brief Copy assignment - * @param other source to copy from - * @return this instance - */ - RAMSES_API AttributeInput& operator=(const AttributeInput& other); - - /** - * @brief Move assignment - * @param other source to move from - * @return this instance - */ - RAMSES_API AttributeInput& operator=(AttributeInput&& other) noexcept; - }; -} - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/EffectInput.h b/client/ramses-client-api/include/ramses-client-api/EffectInput.h deleted file mode 100644 index 576ef7bec..000000000 --- a/client/ramses-client-api/include/ramses-client-api/EffectInput.h +++ /dev/null @@ -1,63 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EFFECTINPUT_H -#define RAMSES_EFFECTINPUT_H - -#include "ramses-framework-api/StatusObject.h" -#include "ramses-framework-api/EDataType.h" -#include - -namespace ramses -{ - class EffectInputImpl; - - /** - * @ingroup CoreAPI - * @brief The EffectInput is a description of an effect input - */ - class EffectInput : public StatusObject - { - public: - /** - * @brief Returns the name of the effect input. - * - * @return Name of the effect input - */ - [[nodiscard]] RAMSES_API const char* getName() const; - - /** - * @brief Returns the state of the EffectInput object. - * - * @return Returns true if this EffectInput object is initialized and refers to an existing effect input - */ - [[nodiscard]] RAMSES_API bool isValid() const; - - /** - * @brief Returns the effect input data type. - * - * @return Effect input data type if #isValid, std::nullopt otherwise - */ - [[nodiscard]] RAMSES_API std::optional getDataType() const; - - /** - * Stores internal data for implementation specifics of EffectInput. - */ - std::reference_wrapper m_impl; - - protected: - /** - * @brief Constructor of EffectInput. - * - * @param[in] effectInputImpl Internal data for implementation specifics of EffectInput (sink - instance becomes owner) - */ - explicit EffectInput(std::unique_ptr effectInputImpl); - }; -} - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RamsesObject.h b/client/ramses-client-api/include/ramses-client-api/RamsesObject.h deleted file mode 100644 index c6ba5e5fc..000000000 --- a/client/ramses-client-api/include/ramses-client-api/RamsesObject.h +++ /dev/null @@ -1,74 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESOBJECT_H -#define RAMSES_RAMSESOBJECT_H - -#include "ramses-framework-api/StatusObject.h" -#include "ramses-client-api/RamsesObjectTypes.h" - -#include - -namespace ramses -{ - /** - * @ingroup CoreAPI - * @brief The RamsesObject is a base class for all client API objects owned by the framework. - */ - class RamsesObject : public StatusObject - { - public: - /** - * @brief Returns the name of the object. - * - * @return Name of the object - */ - [[nodiscard]] RAMSES_API const char* getName() const; - - /** - * @brief Changes the name of the object. - * - * @param name New name of the object - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - */ - RAMSES_API status_t setName(std::string_view name); - - /** - * @brief Gets type of the object. - * - * @return Type of the object, see ERamsesObjectType enum for possible values. - */ - [[nodiscard]] RAMSES_API ERamsesObjectType getType() const; - - /** - * @brief Checks if the object is of given type. - * - * @param[in] type Type to check against. - * @return True if object is of given type, ie. it can be converted to given type. - */ - [[nodiscard]] RAMSES_API bool isOfType(ERamsesObjectType type) const; - - /** - * Stores internal data for implementation specifics of RamsesObject. - */ - class RamsesObjectImpl& m_impl; - - protected: - /** - * @brief Constructor for RamsesObject. - * - * @param[in] impl Internal data for implementation specifics of RamsesObject (sink - instance becomes owner) - */ - explicit RamsesObject(std::unique_ptr impl); - - friend class RamsesObjectRegistry; - }; -} - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/SceneConfig.h b/client/ramses-client-api/include/ramses-client-api/SceneConfig.h deleted file mode 100644 index 6105c2d1b..000000000 --- a/client/ramses-client-api/include/ramses-client-api/SceneConfig.h +++ /dev/null @@ -1,83 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENECONFIG_H -#define RAMSES_SCENECONFIG_H - -#include "ramses-framework-api/StatusObject.h" -#include "ramses-client-api/EScenePublicationMode.h" -#include "ramses-client-api/EVisibilityMode.h" - -namespace ramses -{ - class SceneConfigImpl; - - /** - * @ingroup CoreAPI - * @brief The SceneConfig holds a set of parameters to be used when creating a scene. - */ - class SceneConfig : public StatusObject - { - public: - /** - * @brief Empty constructor of SceneConfig - has default values - */ - RAMSES_API SceneConfig(); - - /** - * @brief Destructor of SceneConfig - */ - RAMSES_API ~SceneConfig() override; - - /** - * @brief Set the publication mode that will be used for this scene. - * - * Later calls to publish must use the same value as given here. - * Setting this to EScenePublicationMode_LocalOnly for scenes that will never be - * published remotely enables optimization possibilities. - * - * @param[in] publicationMode Publication mode to use with scene. - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - */ - RAMSES_API status_t setPublicationMode(EScenePublicationMode publicationMode); - - /** - * @brief Copy constructor - * @param other source to copy from - */ - RAMSES_API SceneConfig(const SceneConfig& other); - - /** - * @brief Move constructor - * @param other source to move from - */ - RAMSES_API SceneConfig(SceneConfig&& other) noexcept; - - /** - * @brief Copy assignment - * @param other source to copy from - * @return this instance - */ - RAMSES_API SceneConfig& operator=(const SceneConfig& other); - - /** - * @brief Move assignment - * @param other source to move from - * @return this instance - */ - RAMSES_API SceneConfig& operator=(SceneConfig&& other) noexcept; - - /** - * Stores internal data for implementation specifics of SceneConfig. - */ - std::reference_wrapper m_impl; - }; -} - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/UniformInput.h b/client/ramses-client-api/include/ramses-client-api/UniformInput.h deleted file mode 100644 index 1dc67533d..000000000 --- a/client/ramses-client-api/include/ramses-client-api/UniformInput.h +++ /dev/null @@ -1,76 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_UNIFORMINPUT_H -#define RAMSES_UNIFORMINPUT_H - -#include "ramses-client-api/EffectInput.h" -#include "ramses-client-api/EffectInputSemantic.h" -#include "ramses-framework-api/EDataType.h" - -namespace ramses -{ - /** - * @ingroup CoreAPI - * @brief The UniformInput is a description of an uniform effect input - */ - class UniformInput : public EffectInput - { - public: - /** - * @brief Constructor of UniformInput. - */ - RAMSES_API UniformInput(); - - /** - * @brief Destructor of UniformInput - */ - RAMSES_API ~UniformInput() override; - - /** - * @brief Returns the effect input semantics. - * - * @return Effect input semantics - */ - [[nodiscard]] RAMSES_API EEffectUniformSemantic getSemantics() const; - - /** - * @brief Returns the number of elements that are assigned to this effect input - * @return the element count or 0 if not initialized - */ - [[nodiscard]] RAMSES_API size_t getElementCount() const; - - /** - * @brief Copy constructor - * @param other source to copy from - */ - RAMSES_API UniformInput(const UniformInput& other); - - /** - * @brief Move constructor - * @param other source to move from - */ - RAMSES_API UniformInput(UniformInput&& other) noexcept; - - /** - * @brief Copy assignment - * @param other source to copy from - * @return this instance - */ - RAMSES_API UniformInput& operator=(const UniformInput& other); - - /** - * @brief Move assignment - * @param other source to move from - * @return this instance - */ - RAMSES_API UniformInput& operator=(UniformInput&& other) noexcept; - }; -} - -#endif diff --git a/client/ramses-client-api/include/ramses-client.h b/client/ramses-client-api/include/ramses-client.h deleted file mode 100644 index 326621f3c..000000000 --- a/client/ramses-client-api/include/ramses-client.h +++ /dev/null @@ -1,62 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSES_CLIENT_H -#define RAMSES_RAMSES_CLIENT_H - -/** - * @defgroup CoreAPI The Ramses Core API - * This group contains all of the Ramses Core API types. - */ - -#include "ramses-client-api/RamsesClient.h" - -// Scene -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/IClientEventHandler.h" -#include "ramses-client-api/DataObject.h" - -// Effect -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" - -// Resources -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" - -// Data Buffers -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" - -// Iterators -#include "ramses-client-api/SceneIterator.h" -#include "ramses-client-api/RenderPassGroupIterator.h" -#include "ramses-client-api/RenderGroupMeshIterator.h" -#include "ramses-client-api/SceneGraphIterator.h" -#include "ramses-client-api/SceneObjectIterator.h" - -#endif diff --git a/client/ramses-client/impl/AppearanceImpl.cpp b/client/ramses-client/impl/AppearanceImpl.cpp deleted file mode 100644 index e04439f4b..000000000 --- a/client/ramses-client/impl/AppearanceImpl.cpp +++ /dev/null @@ -1,740 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// client API -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/DataObject.h" - -// internal -#include "AppearanceImpl.h" -#include "EffectImpl.h" -#include "EffectInputImpl.h" -#include "ObjectIteratorImpl.h" -#include "TextureSamplerImpl.h" -#include "DataObjectImpl.h" -#include "AppearanceUtils.h" -#include "SerializationContext.h" -#include "SceneImpl.h" -#include "RamsesObjectRegistryIterator.h" -#include "Scene/ClientScene.h" -#include "SceneUtils/DataLayoutCreationHelper.h" -#include "SceneUtils/ISceneDataArrayAccessor.h" -#include "SceneUtils/DataInstanceHelper.h" -#include "SceneAPI/EDataType.h" -#include "ObjectIteratorImpl.h" -#include "DataTypeUtils.h" -#include - -namespace ramses -{ - AppearanceImpl::AppearanceImpl(SceneImpl& scene, std::string_view appearancename) - : SceneObjectImpl(scene, ERamsesObjectType::Appearance, appearancename) - , m_effectImpl(nullptr) - { - } - - AppearanceImpl::~AppearanceImpl() - { - } - - status_t AppearanceImpl::setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) - { - getIScene().setRenderStateBlendFactors(m_renderStateHandle, srcColor, destColor, srcAlpha, destAlpha); - return StatusOK; - } - - ramses::status_t AppearanceImpl::getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const - { - const ramses_internal::RenderState& rs = getIScene().getRenderState(m_renderStateHandle); - srcColor = rs.blendFactorSrcColor; - destColor = rs.blendFactorDstColor; - srcAlpha = rs.blendFactorSrcAlpha; - destAlpha = rs.blendFactorDstAlpha; - return StatusOK; - } - - status_t AppearanceImpl::setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha) - { - if ((operationColor == EBlendOperation::Disabled) != (operationAlpha == EBlendOperation::Disabled)) - { - return addErrorEntry("Appearance::setBlendingOperations: invalid combination - one of operationColor or operationAlpha disabled and the other not!"); - } - - getIScene().setRenderStateBlendOperations(m_renderStateHandle, operationColor, operationAlpha); - - return StatusOK; - } - - status_t AppearanceImpl::getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const - { - const ramses_internal::RenderState& rs = getIScene().getRenderState(m_renderStateHandle); - operationColor = rs.blendOperationColor; - operationAlpha = rs.blendOperationAlpha; - return StatusOK; - } - - ramses::status_t AppearanceImpl::setBlendingColor(const vec4f& color) - { - getIScene().setRenderStateBlendColor(m_renderStateHandle, color); - return StatusOK; - } - - ramses::status_t AppearanceImpl::getBlendingColor(vec4f& color) const - { - color = getIScene().getRenderState(m_renderStateHandle).blendColor; - return StatusOK; - } - - status_t AppearanceImpl::setDepthFunction(EDepthFunc func) - { - getIScene().setRenderStateDepthFunc(m_renderStateHandle, func); - return StatusOK; - } - - status_t AppearanceImpl::getDepthFunction(EDepthFunc& func) const - { - func = getIScene().getRenderState(m_renderStateHandle).depthFunc; - return StatusOK; - } - - status_t AppearanceImpl::setDepthWrite(EDepthWrite flag) - { - getIScene().setRenderStateDepthWrite(m_renderStateHandle, flag); - return StatusOK; - } - - ramses::status_t AppearanceImpl::getDepthWriteMode(EDepthWrite& mode) const - { - mode = getIScene().getRenderState(m_renderStateHandle).depthWrite; - return StatusOK; - } - - status_t AppearanceImpl::setScissorTest(EScissorTest flag, int16_t x, int16_t y, uint16_t width, uint16_t height) - { - getIScene().setRenderStateScissorTest(m_renderStateHandle, flag, { x, y, width, height }); - return StatusOK; - } - - status_t AppearanceImpl::getScissorTestState(EScissorTest& mode) const - { - mode = getIScene().getRenderState(m_renderStateHandle).scissorTest; - return StatusOK; - } - - status_t AppearanceImpl::getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const - { - const auto& scissorRegion = getIScene().getRenderState(m_renderStateHandle).scissorRegion; - x = scissorRegion.x; - y = scissorRegion.y; - width = scissorRegion.width; - height = scissorRegion.height; - - return StatusOK; - } - - status_t AppearanceImpl::setStencilFunc(EStencilFunc func, uint8_t ref, uint8_t mask) - { - getIScene().setRenderStateStencilFunc(m_renderStateHandle, func, ref, mask); - return StatusOK; - } - - status_t AppearanceImpl::getStencilFunc(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const - { - const ramses_internal::RenderState& rs = getIScene().getRenderState(m_renderStateHandle); - func = rs.stencilFunc; - ref = rs.stencilRefValue; - mask = rs.stencilMask; - return StatusOK; - } - - status_t AppearanceImpl::setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass) - { - getIScene().setRenderStateStencilOps( m_renderStateHandle, sfail, dpfail, dppass); - return StatusOK; - } - - status_t AppearanceImpl::getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const - { - const ramses_internal::RenderState& rs = getIScene().getRenderState(m_renderStateHandle); - sfail = rs.stencilOpFail; - dpfail = rs.stencilOpDepthFail; - dppass = rs.stencilOpDepthPass; - return StatusOK; - } - - status_t AppearanceImpl::setCullingMode(ECullMode mode) - { - getIScene().setRenderStateCullMode(m_renderStateHandle, mode); - return StatusOK; - } - - status_t AppearanceImpl::getCullingMode(ECullMode& mode) const - { - mode = getIScene().getRenderState(m_renderStateHandle).cullMode; - return StatusOK; - } - - status_t AppearanceImpl::setDrawMode(EDrawMode mode) - { - if (m_effectImpl->hasGeometryShader()) - { - EDrawMode geometryShaderInputType; - m_effectImpl->getGeometryShaderInputType(geometryShaderInputType); - if (!AppearanceUtils::GeometryShaderCompatibleWithDrawMode(geometryShaderInputType, mode)) - { - return addErrorEntry("Appearance::setDrawMode failed, source Effect has a geometry shader which expects a different draw mode."); - } - } - - getIScene().setRenderStateDrawMode(m_renderStateHandle, mode); - return StatusOK; - } - - status_t AppearanceImpl::getDrawMode(EDrawMode& mode) const - { - mode = getIScene().getRenderState(m_renderStateHandle).drawMode; - return StatusOK; - } - - status_t AppearanceImpl::setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha) - { - const ramses_internal::ColorWriteMask colorMask = - (writeRed ? static_cast(ramses_internal::EColorWriteFlag_Red ) : 0u) | - (writeGreen ? static_cast(ramses_internal::EColorWriteFlag_Green) : 0u) | - (writeBlue ? static_cast(ramses_internal::EColorWriteFlag_Blue ) : 0u) | - (writeAlpha ? static_cast(ramses_internal::EColorWriteFlag_Alpha) : 0u); - getIScene().setRenderStateColorWriteMask(m_renderStateHandle, colorMask); - return StatusOK; - } - - status_t AppearanceImpl::getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const - { - const ramses_internal::ColorWriteMask mask = getIScene().getRenderState(m_renderStateHandle).colorWriteMask; - writeRed = (mask & ramses_internal::EColorWriteFlag_Red ) != 0; - writeGreen = (mask & ramses_internal::EColorWriteFlag_Green) != 0; - writeBlue = (mask & ramses_internal::EColorWriteFlag_Blue ) != 0; - writeAlpha = (mask & ramses_internal::EColorWriteFlag_Alpha) != 0; - return StatusOK; - } - - status_t AppearanceImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << (m_effectImpl ? serializationContext.getIDForObject(m_effectImpl) : serializationContext.GetObjectIDNull()); - - outStream << m_renderStateHandle; - outStream << m_uniformLayout; - outStream << m_uniformInstance; - - outStream << static_cast(m_bindableInputs.size()); - for (const auto& bindableInput : m_bindableInputs) - { - outStream << bindableInput.key; - outStream << (bindableInput.value.externallyBoundDataObject ? serializationContext.getIDForObject(bindableInput.value.externallyBoundDataObject) : serializationContext.GetObjectIDNull()); - outStream << bindableInput.value.dataReference; - } - - return StatusOK; - } - - status_t AppearanceImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_effectImpl); - - inStream >> m_renderStateHandle; - inStream >> m_uniformLayout; - inStream >> m_uniformInstance; - - uint32_t bindableInputCount = 0u; - inStream >> bindableInputCount; - - assert(m_bindableInputs.size() == 0u); - m_bindableInputs.reserve(bindableInputCount); - for (uint32_t i = 0u; i < bindableInputCount; ++i) - { - uint32_t inputIndex = 0u; - inStream >> inputIndex; - - BindableInput bindableInput; - serializationContext.ReadDependentPointerAndStoreAsID(inStream, bindableInput.externallyBoundDataObject); - inStream >> bindableInput.dataReference; - - m_bindableInputs.put(inputIndex, bindableInput); - } - - serializationContext.addForDependencyResolve(this); - - return StatusOK; - } - - status_t AppearanceImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::resolveDeserializationDependencies(serializationContext)); - - serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_effectImpl); - - for (auto& bindableInput : m_bindableInputs) - serializationContext.resolveDependencyIDImplAndStoreAsPointer(bindableInput.value.externallyBoundDataObject); - - return StatusOK; - } - - status_t AppearanceImpl::validate() const - { - const auto status = SceneObjectImpl::validate(); - return std::max(status, std::max(validateEffect(), validateUniforms())); - } - - status_t AppearanceImpl::validateEffect() const - { - ObjectIteratorImpl iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::Effect); - RamsesObject* ramsesObject = iter.getNext(); - while (nullptr != ramsesObject) - { - const Effect& effect = RamsesObjectTypeUtils::ConvertTo(*ramsesObject); - if (&effect.m_impl == m_effectImpl) - return addValidationOfDependentObject(*m_effectImpl); - - ramsesObject = iter.getNext(); - } - - return addValidationMessage(EValidationSeverity::Error, "Appearance is referring to an invalid Effect"); - } - - status_t AppearanceImpl::validateUniforms() const - { - status_t status = StatusOK; - const ramses_internal::DataLayout& layout = getIScene().getDataLayout(m_uniformLayout); - const uint32_t numFields = layout.getFieldCount(); - for (ramses_internal::DataFieldHandle fieldHandle(0u); fieldHandle < numFields; ++fieldHandle) - { - const ramses_internal::EDataType dataType = layout.getField(fieldHandle).dataType; - if (!IsTextureSamplerType(dataType)) - continue; - - const ramses_internal::TextureSamplerHandle samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, fieldHandle); - if (!getIScene().isTextureSamplerAllocated(samplerHandle)) - return addValidationMessage(EValidationSeverity::Error, "Appearance is using a Texture Sampler that does not exist"); - - RamsesObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSampler); - while (const TextureSampler* sampler = iter.getNext()) - { - if (samplerHandle == sampler->m_impl.getTextureSamplerHandle()) - { - status = std::max(status, addValidationOfDependentObject(sampler->m_impl)); - break; - } - } - } - - for (const auto& bindableInput : m_bindableInputs) - { - if (bindableInput.value.externallyBoundDataObject) - { - const ramses_internal::DataInstanceHandle boundInstance = getIScene().getDataReference(m_uniformInstance, ramses_internal::DataFieldHandle(bindableInput.key)); - if (!getIScene().isDataInstanceAllocated(boundInstance)) - return addValidationMessage(EValidationSeverity::Error, "Appearance's input is bound to a DataObject that does not exist"); - - ObjectIteratorImpl iterator(getSceneImpl().getObjectRegistry(), ERamsesObjectType::DataObject); - RamsesObject* ramsesObject = nullptr; - while (nullptr != (ramsesObject = iterator.getNext())) - { - const DataObject& dataObject = RamsesObjectTypeUtils::ConvertTo(*ramsesObject); - if (boundInstance == dataObject.m_impl.getDataReference()) - { - status = std::max(status, addValidationOfDependentObject(dataObject.m_impl)); - break; - } - } - } - } - - return status; - } - - void AppearanceImpl::initializeFrameworkData(const EffectImpl& effect) - { - m_effectImpl = &effect; - - m_renderStateHandle = getIScene().allocateRenderState(ramses_internal::RenderStateHandle::Invalid()); - createUniformDataInstance(effect); - - // Set draw mode to geometry shader's expected mode, if effect has such - if (effect.hasGeometryShader()) - { - EDrawMode geometryShaderInputType; - effect.getGeometryShaderInputType(geometryShaderInputType); - setDrawMode(geometryShaderInputType); - } - } - - void AppearanceImpl::deinitializeFrameworkData() - { - getIScene().releaseDataInstance(m_uniformInstance); - m_uniformInstance = ramses_internal::DataInstanceHandle::Invalid(); - - for(const auto& bindableInput : m_bindableInputs) - { - const ramses_internal::DataInstanceHandle dataRef = bindableInput.value.dataReference; - const ramses_internal::DataLayoutHandle dataRefLayout = getIScene().getLayoutOfDataInstance(dataRef); - getIScene().releaseDataInstance(dataRef); - getIScene().releaseDataLayout(dataRefLayout); - } - m_bindableInputs.clear(); - - getIScene().releaseRenderState(m_renderStateHandle); - m_renderStateHandle = ramses_internal::RenderStateHandle::Invalid(); - - getIScene().releaseDataLayout(m_uniformLayout); - m_uniformLayout = ramses_internal::DataLayoutHandle::Invalid(); - } - - const EffectImpl* AppearanceImpl::getEffectImpl() const - { - return m_effectImpl; - } - - const Effect& AppearanceImpl::getEffect() const - { - return RamsesObjectTypeUtils::ConvertTo(m_effectImpl->getRamsesObject()); - } - - ramses_internal::RenderStateHandle AppearanceImpl::getRenderStateHandle() const - { - return m_renderStateHandle; - } - - ramses_internal::DataInstanceHandle AppearanceImpl::getUniformDataInstance() const - { - return m_uniformInstance; - } - - void AppearanceImpl::createUniformDataInstance(const EffectImpl& effect) - { - ramses_internal::InputIndexVector referencedInputs; - const ramses_internal::EffectInputInformationVector& uniformsInputInfo = effect.getUniformInputInformation(); - m_uniformLayout = ramses_internal::DataLayoutCreationHelper::CreateUniformDataLayoutMatchingEffectInputs(getIScene(), uniformsInputInfo, referencedInputs, effect.getLowlevelResourceHash()); - - m_uniformInstance = getIScene().allocateDataInstance(m_uniformLayout); - - m_bindableInputs.reserve(m_bindableInputs.size() + referencedInputs.size()); - for (const auto& refInput : referencedInputs) - { - const ramses_internal::DataFieldHandle dataField(refInput); - - BindableInput bindableInput; - bindableInput.externallyBoundDataObject = nullptr; - bindableInput.dataReference = ramses_internal::DataLayoutCreationHelper::CreateAndBindDataReference(getIScene(), m_uniformInstance, dataField, uniformsInputInfo[refInput].dataType); - m_bindableInputs.put(refInput, bindableInput); - } - } - - status_t AppearanceImpl::checkEffectInputValidityAndValueCompatibility(const EffectInputImpl& input, size_t valueElementCount, std::initializer_list valueDataType) const - { - if (input.getEffectHash() != m_effectImpl->getLowlevelResourceHash()) - { - return addErrorEntry("Appearance::set failed, input is not properly initialized or cannot be used with this appearance."); - } - - const auto result = std::find(valueDataType.begin(), valueDataType.end(), input.getInternalDataType()); - if (result == valueDataType.end()) - { - return addErrorEntry(::fmt::format("Appearance::set failed, value type does not match input data type {}", EnumToString(input.getInternalDataType()))); - } - - if (input.getElementCount() != valueElementCount) - { - return addErrorEntry("Appearance::set failed, element count does not match"); - } - return StatusOK; - } - - ramses_internal::DataInstanceHandle AppearanceImpl::getDataReference(ramses_internal::DataFieldHandle dataField, ramses_internal::EDataType expectedDataType) const - { - const ramses_internal::DataInstanceHandle dataReference = getIScene().getDataReference(m_uniformInstance, dataField); - assert(getIScene().getDataLayout(m_uniformLayout).getField(dataField).elementCount == 1u); - assert(getIScene().getDataLayout(getIScene().getLayoutOfDataInstance(dataReference)).getField(ramses_internal::DataFieldHandle(0u)).dataType == expectedDataType); - UNUSED(expectedDataType); - - return dataReference; - } - - template - status_t AppearanceImpl::setInputValue(const EffectInputImpl& input, size_t elementCount, const T* valuesIn) - { - return setDataArrayChecked(elementCount, valuesIn, input); - } - template - status_t AppearanceImpl::getInputValue(const EffectInputImpl& input, size_t elementCount, T* valuesOut) const - { - return getDataArrayChecked(elementCount, valuesOut, input); - } - - template - status_t AppearanceImpl::setDataArrayChecked(size_t elementCount, const T* values, const EffectInputImpl& input) - { - if (input.getSemantics() != ramses_internal::EFixedSemantics::Invalid) - { - return addErrorEntry("Appearance::set failed, can't access value of semantic uniform"); - } - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, elementCount, {ramses_internal::TypeToEDataTypeTraits::DataType})); - - const uint32_t inputIndex = static_cast(input.getInputIndex()); - const BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - const bool isBindable = (bindableInput != nullptr); - if (isBindable && bindableInput->externallyBoundDataObject) - { - return addErrorEntry("Appearance::set failed, given uniform input is currently bound to a DataObject. Either unbind it from input first or set value on the DataObject itself."); - } - - const ramses_internal::DataFieldHandle dataField(inputIndex); - if (isBindable) - { - const ramses_internal::DataInstanceHandle dataReference = getDataReference(dataField, input.getInternalDataType()); - const T* currentValues = ramses_internal::ISceneDataArrayAccessor::GetDataArray(&getIScene(), dataReference, ramses_internal::DataFieldHandle(0u)); - if (ramses_internal::PlatformMemory::Compare(currentValues, values, 1u * sizeof(T)) != 0) - { - ramses_internal::ISceneDataArrayAccessor::SetDataArray(&getIScene(), dataReference, ramses_internal::DataFieldHandle(0u), 1u, values); - } - } - else - { - static_assert( std::is_same_v == true ); - assert(getIScene().getDataLayout(m_uniformLayout).getField(dataField).elementCount == static_cast(elementCount)); - const T* currentValues = ramses_internal::ISceneDataArrayAccessor::GetDataArray(&getIScene(), m_uniformInstance, dataField); - if (ramses_internal::PlatformMemory::Compare(currentValues, values, elementCount * sizeof(T)) != 0) - { - ramses_internal::ISceneDataArrayAccessor::SetDataArray(&getIScene(), m_uniformInstance, dataField, static_cast(elementCount), values); - } - } - - return StatusOK; - } - - template - status_t AppearanceImpl::getDataArrayChecked(size_t elementCount, T* values, const EffectInputImpl& input) const - { - if (input.getSemantics() != ramses_internal::EFixedSemantics::Invalid) - { - return addErrorEntry("Appearance::set failed, can't access value of semantic uniform"); - } - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, elementCount, {ramses_internal::TypeToEDataTypeTraits::DataType})); - - const BindableInput* bindableInput = m_bindableInputs.get(static_cast(input.getInputIndex())); - const bool isBindable = (bindableInput != nullptr); - if (isBindable && bindableInput->externallyBoundDataObject) - { - return addErrorEntry("Appearance::get failed, given uniform input is currently bound to a DataObject. Either unbind it from input first or get value from the DataObject itself."); - } - - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex())); - if (isBindable) - { - const ramses_internal::DataInstanceHandle dataReference = getDataReference(dataField, input.getInternalDataType()); - ramses_internal::PlatformMemory::Copy(values, ramses_internal::ISceneDataArrayAccessor::GetDataArray(&getIScene(), dataReference, ramses_internal::DataFieldHandle(0u)), EnumToSize(input.getInternalDataType())); - } - else - { - ramses_internal::PlatformMemory::Copy(values, ramses_internal::ISceneDataArrayAccessor::GetDataArray(&getIScene(), m_uniformInstance, dataField), elementCount * EnumToSize(input.getInternalDataType())); - } - - return StatusOK; - } - - status_t AppearanceImpl::setInputTexture(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler) - { - if (!isFromTheSameSceneAs(textureSampler)) - { - return addErrorEntry("Appearance::setInputTexture failed, textureSampler is not from the same scene as this appearance"); - } - - return setInputTextureInternal(input, textureSampler); - } - - status_t AppearanceImpl::getInputTexture(const EffectInputImpl& input, const TextureSampler*& textureSampler) - { - textureSampler = nullptr; - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, 1u, - {ramses_internal::EDataType::TextureSampler2D, ramses_internal::EDataType::TextureSampler3D, ramses_internal::EDataType::TextureSamplerCube})); - - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex())); - const auto samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, dataField); - if (samplerHandle.isValid()) - { - RamsesObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSampler); - while (const TextureSampler* sampler = iter.getNext()) - { - if (samplerHandle == sampler->m_impl.getTextureSamplerHandle()) - { - textureSampler = sampler; - break; - } - } - } - return StatusOK; - } - - status_t AppearanceImpl::getInputTextureMS(const EffectInputImpl& input, const TextureSamplerMS*& textureSampler) - { - textureSampler = nullptr; - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, 1u, - {ramses_internal::EDataType::TextureSampler2DMS})); - - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex())); - const auto samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, dataField); - if (samplerHandle.isValid()) - { - RamsesObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSamplerMS); - while (const TextureSamplerMS* sampler = iter.getNext()) - { - if (samplerHandle == sampler->m_impl.getTextureSamplerHandle()) - { - textureSampler = sampler; - break; - } - } - } - return StatusOK; - } - - status_t AppearanceImpl::getInputTextureExternal(const EffectInputImpl& input, const TextureSamplerExternal*& textureSampler) - { - textureSampler = nullptr; - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, 1u, - {ramses_internal::EDataType::TextureSamplerExternal})); - - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex())); - const auto samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, dataField); - if (samplerHandle.isValid()) - { - RamsesObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSamplerExternal); - while (const TextureSamplerExternal* sampler = iter.getNext()) - { - if (samplerHandle == sampler->m_impl.getTextureSamplerHandle()) - { - textureSampler = sampler; - break; - } - } - } - return StatusOK; - } - - status_t AppearanceImpl::bindInput(const EffectInputImpl& input, const DataObjectImpl& dataObject) - { - if (!isFromTheSameSceneAs(dataObject)) - return addErrorEntry("Appearance::bindInput failed, dataObject is not from the same scene as this appearance"); - - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, 1u, {DataTypeUtils::ConvertDataTypeToInternal(dataObject.getDataType())})); - - const uint32_t inputIndex = static_cast(input.getInputIndex()); - BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - if (bindableInput == nullptr) - return addErrorEntry("Appearance::bindInput failed, given uniform input cannot be bound to a DataObject."); - - return bindInputInternal(input, dataObject); - } - - status_t AppearanceImpl::unbindInput(const EffectInputImpl& input) - { - const uint32_t inputIndex = static_cast(input.getInputIndex()); - BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - if (bindableInput == nullptr || !bindableInput->externallyBoundDataObject) - { - return addErrorEntry("Appearance::unbindInput failed, given uniform input is not bound to a DataObject."); - } - - return unbindInputInternal(input); - } - - bool AppearanceImpl::isInputBound(const EffectInputImpl& input) const - { - const uint32_t inputIndex = static_cast(input.getInputIndex()); - const BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - return (bindableInput != nullptr) && bindableInput->externallyBoundDataObject != nullptr; - } - - status_t AppearanceImpl::setInputTextureInternal(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler) - { - CHECK_RETURN_ERR(checkEffectInputValidityAndValueCompatibility(input, 1u, {textureSampler.getTextureDataType()})); - - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex())); - const ramses_internal::TextureSamplerHandle samplerHandle = textureSampler.getTextureSamplerHandle(); - getIScene().setDataTextureSamplerHandle(m_uniformInstance, dataField, samplerHandle); - return StatusOK; - } - - status_t AppearanceImpl::bindInputInternal(const EffectInputImpl& input, const DataObjectImpl& dataObject) - { - const uint32_t inputIndex = static_cast(input.getInputIndex()); - const ramses_internal::DataFieldHandle dataField(inputIndex); - getIScene().setDataReference(m_uniformInstance, dataField, dataObject.getDataReference()); - - BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - assert(bindableInput != nullptr); - bindableInput->externallyBoundDataObject = &dataObject; - - return StatusOK; - } - - status_t AppearanceImpl::unbindInputInternal(const EffectInputImpl& input) - { - const uint32_t inputIndex = static_cast(input.getInputIndex()); - BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - const ramses_internal::DataFieldHandle dataField(inputIndex); - getIScene().setDataReference(m_uniformInstance, dataField, bindableInput->dataReference); - bindableInput->externallyBoundDataObject = nullptr; - - return StatusOK; - } - - ramses_internal::DataLayoutHandle AppearanceImpl::getUniformDataLayout() const - { - return m_uniformLayout; - } - - const ramses::DataObject* AppearanceImpl::getBoundDataObject(const EffectInputImpl& input) const - { - const uint32_t inputIndex = static_cast(input.getInputIndex()); - const BindableInput* bindableInput = m_bindableInputs.get(inputIndex); - if (bindableInput && bindableInput->externallyBoundDataObject) - return &RamsesObjectTypeUtils::ConvertTo(bindableInput->externallyBoundDataObject->getRamsesObject()); - - return nullptr; - } - - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const int32_t*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, int32_t*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const float*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, float*) const; - - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec2i*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec2i*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec3i*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec3i*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec4i*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec4i*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec2f*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec2f*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec3f*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec3f*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec4f*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec4f*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const matrix22f*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, matrix22f*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const matrix33f*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, matrix33f*) const; - template status_t AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const matrix44f*); - template status_t AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, matrix44f*) const; -} diff --git a/client/ramses-client/impl/AppearanceImpl.h b/client/ramses-client/impl/AppearanceImpl.h deleted file mode 100644 index ed2c0194a..000000000 --- a/client/ramses-client/impl/AppearanceImpl.h +++ /dev/null @@ -1,133 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_APPEARANCEIMPL_H -#define RAMSES_APPEARANCEIMPL_H - -// client api -#include "ramses-client-api/Appearance.h" -#include "ramses-framework-api/AppearanceEnums.h" - -// internal -#include "SceneObjectImpl.h" - -// ramses framework -#include "SceneAPI/Handles.h" -#include "SceneAPI/EDataType.h" -#include "Collections/HashMap.h" - -#include -#include - -namespace ramses_internal -{ - struct DataReference; - class IScene; -} - -namespace ramses -{ - class EffectImpl; - class EffectInputImpl; - class TextureSamplerImpl; - class DataObjectImpl; - - class AppearanceImpl final : public SceneObjectImpl - { - public: - AppearanceImpl(SceneImpl& scene, std::string_view appearancename); - ~AppearanceImpl() override; - - void initializeFrameworkData(const EffectImpl& effect); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - const EffectImpl* getEffectImpl() const; - const Effect& getEffect() const; - - status_t setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha); - status_t getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const; - status_t setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha); - status_t getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const; - status_t setBlendingColor(const vec4f& color); - status_t getBlendingColor(vec4f& color) const; - status_t setDepthFunction(EDepthFunc func); - status_t getDepthFunction(EDepthFunc& func) const; - status_t setDepthWrite(EDepthWrite flag); - status_t getDepthWriteMode(EDepthWrite& mode) const; - status_t setScissorTest(EScissorTest flag, int16_t x, int16_t y, uint16_t width, uint16_t height); - status_t getScissorTestState(EScissorTest& mode) const; - status_t getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const; - status_t setStencilFunc(EStencilFunc func, uint8_t ref, uint8_t mask); - status_t getStencilFunc(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const; - status_t setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass); - status_t getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const; - status_t setCullingMode(ECullMode mode); - status_t getCullingMode(ECullMode& mode) const; - status_t setDrawMode(EDrawMode mode); - status_t getDrawMode(EDrawMode& mode) const; - status_t setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha); - status_t getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const; - - template - status_t setInputValue(const EffectInputImpl& input, size_t elementCount, const T* valuesIn); - template - status_t getInputValue(const EffectInputImpl& input, size_t elementCount, T* valuesOut) const; - - status_t setInputTexture(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler); - status_t getInputTexture(const EffectInputImpl& input, const TextureSampler*& textureSampler); - status_t getInputTextureMS(const EffectInputImpl& input, const TextureSamplerMS*& textureSampler); - status_t getInputTextureExternal(const EffectInputImpl& input, const TextureSamplerExternal*& textureSampler); - - status_t bindInput(const EffectInputImpl& input, const DataObjectImpl& dataObject); - status_t unbindInput(const EffectInputImpl& input); - bool isInputBound(const EffectInputImpl& input) const; - const DataObject* getBoundDataObject(const EffectInputImpl& input) const; - - ramses_internal::RenderStateHandle getRenderStateHandle() const; - ramses_internal::DataInstanceHandle getUniformDataInstance() const; - ramses_internal::DataLayoutHandle getUniformDataLayout() const; - - private: - void createUniformDataInstance(const EffectImpl& effect); - - status_t checkEffectInputValidityAndValueCompatibility(const EffectInputImpl& input, size_t valueElementCount, std::initializer_list valueDataType) const; - ramses_internal::DataInstanceHandle getDataReference(ramses_internal::DataFieldHandle dataField, ramses_internal::EDataType expectedDataType) const; - - template - status_t setDataArrayChecked(size_t elementCount, const T* values, const EffectInputImpl& input); - template - status_t getDataArrayChecked(size_t elementCount, T* values, const EffectInputImpl& input) const; - - status_t setInputTextureInternal(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler); - status_t bindInputInternal(const EffectInputImpl& input, const DataObjectImpl& dataObject); - status_t unbindInputInternal(const EffectInputImpl& input); - - status_t validateEffect() const; - status_t validateUniforms() const; - - const EffectImpl* m_effectImpl; - ramses_internal::RenderStateHandle m_renderStateHandle; - ramses_internal::DataLayoutHandle m_uniformLayout; - ramses_internal::DataInstanceHandle m_uniformInstance; - - struct BindableInput - { - const DataObjectImpl* externallyBoundDataObject = nullptr; - ramses_internal::DataInstanceHandle dataReference; - }; - - using BindableInputMap = ramses_internal::HashMap; - BindableInputMap m_bindableInputs; - }; -} - -#endif diff --git a/client/ramses-client/impl/ArrayBufferImpl.cpp b/client/ramses-client/impl/ArrayBufferImpl.cpp deleted file mode 100644 index e30fa3346..000000000 --- a/client/ramses-client/impl/ArrayBufferImpl.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ArrayBufferImpl.h" -#include "SerializationContext.h" -#include "DataTypeUtils.h" -#include "Scene/ClientScene.h" -#include "SceneAPI/EDataType.h" - -namespace ramses -{ - ArrayBufferImpl::ArrayBufferImpl(SceneImpl& scene, std::string_view databufferName) - : SceneObjectImpl(scene, ERamsesObjectType::ArrayBufferObject, databufferName) - { - } - - ArrayBufferImpl::~ArrayBufferImpl() - { - } - - void ArrayBufferImpl::initializeFrameworkData(EDataType dataType, uint32_t numElements) - { - assert(!m_dataBufferHandle.isValid()); - const ramses_internal::EDataBufferType dataBufferType = DataTypeUtils::DeductBufferTypeFromDataType(dataType); - const ramses_internal::EDataType dataTypeInternal = DataTypeUtils::ConvertDataTypeToInternal(dataType); - const uint32_t maximumSizeInBytes = EnumToSize(dataTypeInternal) * numElements; - m_dataBufferHandle = getIScene().allocateDataBuffer(dataBufferType, dataTypeInternal, maximumSizeInBytes); - } - - void ArrayBufferImpl::deinitializeFrameworkData() - { - assert(m_dataBufferHandle.isValid()); - getIScene().releaseDataBuffer(m_dataBufferHandle); - m_dataBufferHandle = ramses_internal::DataBufferHandle::Invalid(); - } - - - ramses_internal::DataBufferHandle ArrayBufferImpl::getDataBufferHandle() const - { - return m_dataBufferHandle; - } - - uint32_t ArrayBufferImpl::getElementCount() const - { - const ramses_internal::GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - return static_cast(dataBuffer.data.size()) / EnumToSize(dataBuffer.dataType); - } - - uint32_t ArrayBufferImpl::getUsedElementCount() const - { - const ramses_internal::GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - return static_cast(dataBuffer.usedSize) / EnumToSize(dataBuffer.dataType); - } - - EDataType ArrayBufferImpl::getDataType() const - { - const ramses_internal::GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - return DataTypeUtils::ConvertDataTypeFromInternal(dataBuffer.dataType); - } - - status_t ArrayBufferImpl::getData(ramses_internal::Byte* buffer, uint32_t numElements) const - { - const auto& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - const uint32_t dataSizeToCopy = std::min(numElements * EnumToSize(dataBuffer.dataType), static_cast(dataBuffer.data.size())); - ramses_internal::PlatformMemory::Copy(buffer, dataBuffer.data.data(), dataSizeToCopy); - - return StatusOK; - } - - status_t ArrayBufferImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << m_dataBufferHandle; - - return StatusOK; - } - - status_t ArrayBufferImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - inStream >> m_dataBufferHandle; - - return StatusOK; - } - - status_t ArrayBufferImpl::validate() const - { - status_t status = SceneObjectImpl::validate(); - - const auto& iscene = getIScene(); - - const bool isInitialized = (iscene.getDataBuffer(getDataBufferHandle()).usedSize > 0u); - - bool usedAsInput = false; - for (ramses_internal::DataInstanceHandle di(0u); di < iscene.getDataInstanceCount() && !usedAsInput; ++di) - { - if (!iscene.isDataInstanceAllocated(di)) - continue; - - const auto dlh = iscene.getLayoutOfDataInstance(di); - const ramses_internal::DataLayout& dl = iscene.getDataLayout(dlh); - for (ramses_internal::DataFieldHandle df(0u); df < dl.getFieldCount(); ++df) - { - switch (dl.getField(df).dataType) - { - case ramses_internal::EDataType::Indices: - case ramses_internal::EDataType::UInt16Buffer: - case ramses_internal::EDataType::FloatBuffer: - case ramses_internal::EDataType::Vector2Buffer: - case ramses_internal::EDataType::Vector3Buffer: - case ramses_internal::EDataType::Vector4Buffer: - { - const auto& resource = iscene.getDataResource(di, df); - if (resource.dataBuffer == getDataBufferHandle()) - usedAsInput = true; - break; - } - default: - break; - } - } - } - - for (ramses_internal::PickableObjectHandle po(0u); po < iscene.getPickableObjectCount() && !usedAsInput; ++po) - { - if (iscene.isPickableObjectAllocated(po) && iscene.getPickableObject(po).geometryHandle == getDataBufferHandle()) - usedAsInput = true; - } - - if (usedAsInput && !isInitialized) - return addValidationMessage(EValidationSeverity::Warning, "DataBuffer is used as geometry input but there is no data set, this could lead to graphical glitches if actually rendered."); - - if (!usedAsInput) - return addValidationMessage(EValidationSeverity::Warning, "DataBuffer is not used anywhere, destroy it if not needed."); - - return status; - } - - status_t ArrayBufferImpl::updateData(uint32_t firstElement, uint32_t numElements, const ramses_internal::Byte* bufferData) - { - const ramses_internal::GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - const size_t maximumSizeInBytes = dataBuffer.data.size(); - const uint32_t offsetInBytes = firstElement * EnumToSize(dataBuffer.dataType); - const uint32_t dataSizeInBytes = numElements * EnumToSize(dataBuffer.dataType); - if (offsetInBytes + dataSizeInBytes > maximumSizeInBytes) - { - return addErrorEntry("DataBuffer::update failed - trying to write data beyond maximum size"); - } - - getIScene().updateDataBuffer(m_dataBufferHandle, offsetInBytes, dataSizeInBytes, bufferData); - - return StatusOK; - } - - uint32_t ArrayBufferImpl::getMaximumNumberOfElements() const - { - const ramses_internal::GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - return static_cast(dataBuffer.data.size()) / EnumToSize(dataBuffer.dataType); - } - - uint32_t ArrayBufferImpl::getUsedNumberOfElements() const - { - const ramses_internal::GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); - return dataBuffer.usedSize / EnumToSize(dataBuffer.dataType); - } -} diff --git a/client/ramses-client/impl/ArrayBufferImpl.h b/client/ramses-client/impl/ArrayBufferImpl.h deleted file mode 100644 index 4229e2445..000000000 --- a/client/ramses-client/impl/ArrayBufferImpl.h +++ /dev/null @@ -1,48 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_ARRAYBUFFERIMPL_H -#define RAMSES_ARRAYBUFFERIMPL_H - -#include "SceneObjectImpl.h" -#include "SceneAPI/Handles.h" -#include "ramses-framework-api/EDataType.h" - -#include - -namespace ramses -{ - class ArrayBufferImpl : public SceneObjectImpl - { - public: - ArrayBufferImpl(SceneImpl& scene, std::string_view databufferName); - ~ArrayBufferImpl() override; - - void initializeFrameworkData(EDataType dataType, uint32_t numElements); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - status_t validate() const override; - - status_t updateData(uint32_t firstElement, uint32_t numElements, const ramses_internal::Byte* bufferData); - - ramses_internal::DataBufferHandle getDataBufferHandle() const; - uint32_t getMaximumNumberOfElements() const; - uint32_t getElementCount() const; - uint32_t getUsedNumberOfElements() const; - uint32_t getUsedElementCount() const; - EDataType getDataType() const; - status_t getData(ramses_internal::Byte* buffer, uint32_t numElements) const; - - private: - ramses_internal::DataBufferHandle m_dataBufferHandle; - }; -} - -#endif diff --git a/client/ramses-client/impl/BlitPassImpl.h b/client/ramses-client/impl/BlitPassImpl.h deleted file mode 100644 index ed8c83ac4..000000000 --- a/client/ramses-client/impl/BlitPassImpl.h +++ /dev/null @@ -1,56 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_BLITPASSIMPL_H -#define RAMSES_BLITPASSIMPL_H - -#include "SceneObjectImpl.h" -#include "SceneAPI/Handles.h" - -#include - -namespace ramses -{ - class RenderBufferImpl; - class RenderBuffer; - - class BlitPassImpl final : public SceneObjectImpl - { - public: - BlitPassImpl(SceneImpl& scene, std::string_view blitpassName); - ~BlitPassImpl() override; - - void initializeFrameworkData(const RenderBufferImpl& sourceRenderBuffer, const RenderBufferImpl& destinationRenderBuffer); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - const RenderBuffer& getSourceRenderBuffer() const; - const RenderBuffer& getDestinationRenderBuffer() const; - - status_t setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height); - void getBlittingRegion(uint32_t& sourceX, uint32_t& sourceY, uint32_t& destinationX, uint32_t& destinationY, uint32_t& width, uint32_t& height) const; - - status_t setRenderOrder(int32_t renderOrder); - int32_t getRenderOrder() const; - - status_t setEnabled(bool isEnabeld); - bool isEnabled() const; - - ramses_internal::BlitPassHandle getBlitPassHandle() const; - - private: - ramses_internal::BlitPassHandle m_blitPassHandle; - const RenderBufferImpl* m_sourceRenderBufferImpl = nullptr; - const RenderBufferImpl* m_destinationRenderBufferImpl = nullptr; - }; -} - -#endif diff --git a/client/ramses-client/impl/CameraNodeImpl.cpp b/client/ramses-client/impl/CameraNodeImpl.cpp deleted file mode 100644 index 33868bf18..000000000 --- a/client/ramses-client/impl/CameraNodeImpl.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/DataObject.h" -#include "CameraNodeImpl.h" -#include "DataObjectImpl.h" -#include "SerializationContext.h" -#include "Scene/ClientScene.h" -#include "Math3d/CameraMatrixHelper.h" -#include - -namespace ramses -{ - CameraNodeImpl::CameraNodeImpl(SceneImpl& scene, ERamsesObjectType cameraType, std::string_view cameraName) - : NodeImpl(scene, cameraType, cameraName) - { - } - - CameraNodeImpl::~CameraNodeImpl() - { - } - - status_t CameraNodeImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(NodeImpl::serialize(outStream, serializationContext)); - - outStream << m_cameraHandle; - outStream << m_dataLayout; - outStream << m_dataInstance; - outStream << m_viewportDataReferenceLayout; - outStream << m_viewportOffsetDataReference; - outStream << m_viewportSizeDataReference; - outStream << m_frustumPlanesDataReferenceLayout; - outStream << m_frustumPlanesDataReference; - outStream << m_frustumNearFarDataReferenceLayout; - outStream << m_frustumNearFarDataReference; - outStream << m_frustumInitialized; - outStream << m_viewportInitialized; - - return StatusOK; - } - - status_t CameraNodeImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(NodeImpl::deserialize(inStream, serializationContext)); - - inStream >> m_cameraHandle; - inStream >> m_dataLayout; - inStream >> m_dataInstance; - inStream >> m_viewportDataReferenceLayout; - inStream >> m_viewportOffsetDataReference; - inStream >> m_viewportSizeDataReference; - inStream >> m_frustumPlanesDataReferenceLayout; - inStream >> m_frustumPlanesDataReference; - inStream >> m_frustumNearFarDataReferenceLayout; - inStream >> m_frustumNearFarDataReference; - inStream >> m_frustumInitialized; - inStream >> m_viewportInitialized; - - return StatusOK; - } - - void CameraNodeImpl::initializeFrameworkData() - { - NodeImpl::initializeFrameworkData(); - - // main data instance with all references - const ramses_internal::DataFieldInfoVector dataRefFiels(4u, ramses_internal::DataFieldInfo{ ramses_internal::EDataType::DataReference }); - m_dataLayout = getIScene().allocateDataLayout(dataRefFiels, {}); - m_dataInstance = getIScene().allocateDataInstance(m_dataLayout); - - // VP offset and size - m_viewportDataReferenceLayout = getIScene().allocateDataLayout({ ramses_internal::DataFieldInfo{ramses_internal::EDataType::Vector2I} }, {}); - m_viewportOffsetDataReference = getIScene().allocateDataInstance(m_viewportDataReferenceLayout); - m_viewportSizeDataReference = getIScene().allocateDataInstance(m_viewportDataReferenceLayout); - - // frustum planes - m_frustumPlanesDataReferenceLayout = getIScene().allocateDataLayout({ ramses_internal::DataFieldInfo{ramses_internal::EDataType::Vector4F} }, {}); - m_frustumPlanesDataReference = getIScene().allocateDataInstance(m_frustumPlanesDataReferenceLayout); - - // frustum near/far planes - m_frustumNearFarDataReferenceLayout = getIScene().allocateDataLayout({ ramses_internal::DataFieldInfo{ramses_internal::EDataType::Vector2F} }, {}); - m_frustumNearFarDataReference = getIScene().allocateDataInstance(m_frustumNearFarDataReferenceLayout); - - // link data references to data instances - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField, m_viewportOffsetDataReference); - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField, m_viewportSizeDataReference); - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField, m_frustumPlanesDataReference); - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField, m_frustumNearFarDataReference); - - // set default values, even though camera is considered invalid if all these are not set explicitly by user - getIScene().setDataSingleVector2i(m_viewportOffsetDataReference, ramses_internal::DataFieldHandle{ 0 }, { 0, 0 }); - getIScene().setDataSingleVector2i(m_viewportSizeDataReference, ramses_internal::DataFieldHandle{ 0 }, { 16, 16 }); - getIScene().setDataSingleVector4f(m_frustumPlanesDataReference, ramses_internal::DataFieldHandle{ 0 }, { -1.f, 1.f, -1.f, 1.f }); - getIScene().setDataSingleVector2f(m_frustumNearFarDataReference, ramses_internal::DataFieldHandle{ 0 }, { 0.1f, 1.f }); - - const auto projType = (getType() == ERamsesObjectType::PerspectiveCamera ? ramses_internal::ECameraProjectionType::Perspective : ramses_internal::ECameraProjectionType::Orthographic); - - m_cameraHandle = getIScene().allocateCamera(projType, getNodeHandle(), m_dataInstance, ramses_internal::CameraHandle::Invalid()); - } - - void CameraNodeImpl::deinitializeFrameworkData() - { - getIScene().releaseDataInstance(m_dataInstance); - m_dataInstance = ramses_internal::DataInstanceHandle::Invalid(); - getIScene().releaseDataLayout(m_dataLayout); - m_dataLayout = ramses_internal::DataLayoutHandle::Invalid(); - getIScene().releaseDataInstance(m_viewportOffsetDataReference); - m_viewportOffsetDataReference = ramses_internal::DataInstanceHandle::Invalid(); - getIScene().releaseDataInstance(m_viewportSizeDataReference); - m_viewportSizeDataReference = ramses_internal::DataInstanceHandle::Invalid(); - getIScene().releaseDataLayout(m_viewportDataReferenceLayout); - m_viewportDataReferenceLayout = ramses_internal::DataLayoutHandle::Invalid(); - getIScene().releaseDataInstance(m_frustumPlanesDataReference); - m_frustumPlanesDataReference = ramses_internal::DataInstanceHandle::Invalid(); - getIScene().releaseDataLayout(m_frustumPlanesDataReferenceLayout); - m_frustumPlanesDataReferenceLayout = ramses_internal::DataLayoutHandle::Invalid(); - getIScene().releaseDataInstance(m_frustumNearFarDataReference); - m_frustumNearFarDataReference = ramses_internal::DataInstanceHandle::Invalid(); - getIScene().releaseDataLayout(m_frustumNearFarDataReferenceLayout); - m_frustumNearFarDataReferenceLayout = ramses_internal::DataLayoutHandle::Invalid(); - getIScene().releaseCamera(m_cameraHandle); - m_cameraHandle = ramses_internal::CameraHandle::Invalid(); - - NodeImpl::deinitializeFrameworkData(); - } - - status_t CameraNodeImpl::validate() const - { - status_t status = NodeImpl::validate(); - - if (!m_frustumInitialized && !isFrustumPlanesBound()) - status = addValidationMessage(EValidationSeverity::Error, "Camera frustum is not initialized!"); - - if (!getProjectionParams().isValid()) - status = addValidationMessage(EValidationSeverity::Error, "Camera frustum invalid!"); - - if (!m_viewportInitialized && !(isViewportOffsetBound() && isViewportSizeBound())) - status = addValidationMessage(EValidationSeverity::Error, "Camera viewport is not initialized!"); - - if (getViewportWidth() == 0 || getViewportHeight() == 0) - status = addValidationMessage(EValidationSeverity::Error, "Camera viewport invalid!"); - - return status; - } - - ramses_internal::ECameraProjectionType CameraNodeImpl::getProjectionType() const - { - return getIScene().getCamera(m_cameraHandle).projectionType; - } - - status_t CameraNodeImpl::setPerspectiveFrustum(float fovY, float aspectRatio, float nearPlane, float farPlane) - { - assert(isOfType(ERamsesObjectType::PerspectiveCamera)); - - const auto params = ramses_internal::ProjectionParams::Perspective(fovY, aspectRatio, nearPlane, farPlane); - if (!params.isValid()) - return addErrorEntry("PerspectiveCamera::setFrustum failed - check validity of given frustum planes"); - - updateProjectionParamsOnScene(params); - m_frustumInitialized = true; - - return StatusOK; - } - - float CameraNodeImpl::getVerticalFieldOfView() const - { - assert(isOfType(ERamsesObjectType::PerspectiveCamera)); - return ramses_internal::ProjectionParams::GetPerspectiveFovY(getProjectionParams()); - } - - float CameraNodeImpl::getAspectRatio() const - { - assert(isOfType(ERamsesObjectType::PerspectiveCamera)); - return ramses_internal::ProjectionParams::GetAspectRatio(getProjectionParams()); - } - - status_t CameraNodeImpl::setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane) - { - const auto params = ramses_internal::ProjectionParams::Frustum( - getIScene().getCamera(m_cameraHandle).projectionType, leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane); - - if (!params.isValid()) - return addErrorEntry("Camera::setFrustum failed - check validity of given frustum planes"); - - updateProjectionParamsOnScene(params); - m_frustumInitialized = true; - - return StatusOK; - } - - float CameraNodeImpl::getNearPlane() const - { - const auto nearFarData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField); - return getIScene().getDataSingleVector2f(nearFarData, ramses_internal::DataFieldHandle{ 0 }).x; - } - - float CameraNodeImpl::getFarPlane() const - { - const auto nearFarData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField); - return getIScene().getDataSingleVector2f(nearFarData, ramses_internal::DataFieldHandle{ 0 }).y; - } - - status_t CameraNodeImpl::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) - { - // Set a sane upper limit for viewport to avoid GL_INVALID_VALUE in glViewport() - if (width > 0 && width <= 32768u && height > 0 && height <= 32768u ) - { - getIScene().setDataSingleVector2i(m_viewportOffsetDataReference, ramses_internal::DataFieldHandle{ 0 }, { x, y }); - getIScene().setDataSingleVector2i(m_viewportSizeDataReference, ramses_internal::DataFieldHandle{ 0 }, { int32_t(width), int32_t(height) }); - m_viewportInitialized = true; - } - else - { - return addErrorEntry("Camera::setViewport failed - width and height must be within [1, 32768]!"); - } - - return StatusOK; - } - - int32_t CameraNodeImpl::getViewportX() const - { - const auto vpOffsetData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField); - return getIScene().getDataSingleVector2i(vpOffsetData, ramses_internal::DataFieldHandle{ 0 }).x; - } - - int32_t CameraNodeImpl::getViewportY() const - { - const auto vpOffsetData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField); - return getIScene().getDataSingleVector2i(vpOffsetData, ramses_internal::DataFieldHandle{ 0 }).y; - } - - uint32_t CameraNodeImpl::getViewportWidth() const - { - const auto vpSizeData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField); - return getIScene().getDataSingleVector2i(vpSizeData, ramses_internal::DataFieldHandle{ 0 }).x; - } - - uint32_t CameraNodeImpl::getViewportHeight() const - { - const auto vpSizeData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField); - return getIScene().getDataSingleVector2i(vpSizeData, ramses_internal::DataFieldHandle{ 0 }).y; - } - - float CameraNodeImpl::getLeftPlane() const - { - const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField); - return getIScene().getDataSingleVector4f(frustumData, ramses_internal::DataFieldHandle{ 0 }).x; - } - - float CameraNodeImpl::getRightPlane() const - { - const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField); - return getIScene().getDataSingleVector4f(frustumData, ramses_internal::DataFieldHandle{ 0 }).y; - } - - float CameraNodeImpl::getBottomPlane() const - { - const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField); - return getIScene().getDataSingleVector4f(frustumData, ramses_internal::DataFieldHandle{ 0 }).z; - } - - float CameraNodeImpl::getTopPlane() const - { - const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField); - return getIScene().getDataSingleVector4f(frustumData, ramses_internal::DataFieldHandle{ 0 }).w; - } - - ramses_internal::CameraHandle CameraNodeImpl::getCameraHandle() const - { - return m_cameraHandle; - } - - status_t CameraNodeImpl::getProjectionMatrix(matrix44f& projectionMatrix) const - { - if (!m_frustumInitialized) - { - return addErrorEntry("CameraImpl::getProjectionMatrix failed - Camera frustum is not initialized!"); - } - - projectionMatrix = ramses_internal::CameraMatrixHelper::ProjectionMatrix(getProjectionParams()); - - return StatusOK; - } - - ramses_internal::ProjectionParams CameraNodeImpl::getProjectionParams() const - { - const auto frustumDataInstance = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField); - const auto& frustumData = getIScene().getDataSingleVector4f(frustumDataInstance, ramses_internal::DataFieldHandle{ 0 }); - const auto nearFarDataInstance = getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField); - const auto& nearFarData = getIScene().getDataSingleVector2f(nearFarDataInstance, ramses_internal::DataFieldHandle{ 0 }); - - return ramses_internal::ProjectionParams::Frustum( - getIScene().getCamera(m_cameraHandle).projectionType, - frustumData.x, - frustumData.y, - frustumData.z, - frustumData.w, - nearFarData.x, - nearFarData.y); - } - - void CameraNodeImpl::updateProjectionParamsOnScene(const ramses_internal::ProjectionParams& params) - { - getIScene().setDataSingleVector4f(m_frustumPlanesDataReference, ramses_internal::DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); - getIScene().setDataSingleVector2f(m_frustumNearFarDataReference, ramses_internal::DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); - } - - status_t CameraNodeImpl::bindViewportOffset(const DataObject& offsetData) - { - if (offsetData.getDataType() != EDataType::Vector2I) - return addErrorEntry("Camera::bindViewportOffset failed, data object must be of type EDataType::Vector2I"); - if (!isFromTheSameSceneAs(offsetData.m_impl)) - return addErrorEntry("Camera::bindViewportOffset failed, viewport offset data object is not from the same scene as this camera"); - - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField, offsetData.m_impl.getDataReference()); - return StatusOK; - } - - status_t CameraNodeImpl::bindViewportSize(const DataObject& sizeData) - { - if (sizeData.getDataType() != EDataType::Vector2I) - return addErrorEntry("Camera::bindViewportSize failed, data object must be of type EDataType::Vector2I"); - if (!isFromTheSameSceneAs(sizeData.m_impl)) - return addErrorEntry("Camera::bindViewportSize failed, viewport size data object is not from the same scene as this camera"); - - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField, sizeData.m_impl.getDataReference()); - return StatusOK; - } - - status_t CameraNodeImpl::bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarData) - { - if (frustumPlanesData.getDataType() != EDataType::Vector4F || nearFarData.getDataType() != EDataType::Vector2F) - return addErrorEntry("Camera::bindFrustumPlanes failed, data objects must be of type EDataType::Vector4F and EDataType::Vector2F"); - if (!isFromTheSameSceneAs(frustumPlanesData.m_impl) || !isFromTheSameSceneAs(nearFarData.m_impl)) - return addErrorEntry("Camera::bindFrustumPlanes failed, one of the frustum planes data object is not from the same scene as this camera"); - - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField, frustumPlanesData.m_impl.getDataReference()); - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField, nearFarData.m_impl.getDataReference()); - return StatusOK; - } - - status_t CameraNodeImpl::unbindViewportOffset() - { - if (isViewportOffsetBound()) - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField, m_viewportOffsetDataReference); - return StatusOK; - } - - status_t CameraNodeImpl::unbindViewportSize() - { - if (isViewportSizeBound()) - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField, m_viewportSizeDataReference); - return StatusOK; - } - - status_t CameraNodeImpl::unbindFrustumPlanes() - { - if (isFrustumPlanesBound()) - { - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField, m_frustumPlanesDataReference); - getIScene().setDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField, m_frustumNearFarDataReference); - } - return StatusOK; - } - - bool CameraNodeImpl::isViewportOffsetBound() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField) != m_viewportOffsetDataReference; - } - - bool CameraNodeImpl::isViewportSizeBound() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField) != m_viewportSizeDataReference; - } - - bool CameraNodeImpl::isFrustumPlanesBound() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField) != m_frustumPlanesDataReference; - } - - ramses_internal::DataInstanceHandle CameraNodeImpl::getViewportOffsetHandle() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportOffsetField); - } - - ramses_internal::DataInstanceHandle CameraNodeImpl::getViewportSizeHandle() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::ViewportSizeField); - } - - ramses_internal::DataInstanceHandle CameraNodeImpl::getFrustrumPlanesHandle() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumPlanesField); - } - - ramses_internal::DataInstanceHandle CameraNodeImpl::getFrustrumNearFarPlanesHandle() const - { - return getIScene().getDataReference(m_dataInstance, ramses_internal::Camera::FrustumNearFarPlanesField); - } -} diff --git a/client/ramses-client/impl/CameraNodeImpl.h b/client/ramses-client/impl/CameraNodeImpl.h deleted file mode 100644 index e90d6ac82..000000000 --- a/client/ramses-client/impl/CameraNodeImpl.h +++ /dev/null @@ -1,104 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_CAMERANODEIMPL_H -#define RAMSES_CAMERANODEIMPL_H - -// API -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -// internal -#include "NodeImpl.h" -#include "SceneAPI/ECameraProjectionType.h" -#include "Math3d/ProjectionParams.h" - -#include - -namespace ramses -{ - class DataObject; - - class CameraNodeImpl final : public NodeImpl - { - public: - CameraNodeImpl(SceneImpl& scene, ERamsesObjectType cameraType, std::string_view cameraName); - ~CameraNodeImpl() override; - - // Common for all camera types - ramses_internal::ECameraProjectionType getProjectionType() const; - - status_t setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); - int32_t getViewportX() const; - int32_t getViewportY() const; - uint32_t getViewportWidth() const; - uint32_t getViewportHeight() const; - - void initializeFrameworkData(); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - status_t validate() const override; - ramses_internal::CameraHandle getCameraHandle() const; - - status_t setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane); - float getLeftPlane() const; - float getRightPlane() const; - float getBottomPlane() const; - float getTopPlane() const; - float getNearPlane() const; - float getFarPlane() const; - - status_t setPerspectiveFrustum(float fovY, float aspectRatio, float nearPlane, float farPlane); - float getVerticalFieldOfView() const; - float getAspectRatio() const; - - status_t getProjectionMatrix(matrix44f& projectionMatrix) const; - - status_t bindViewportOffset(const DataObject& offsetData); - status_t bindViewportSize(const DataObject& sizeData); - status_t bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarData); - status_t unbindViewportOffset(); - status_t unbindViewportSize(); - status_t unbindFrustumPlanes(); - bool isViewportOffsetBound() const; - bool isViewportSizeBound() const; - bool isFrustumPlanesBound() const; - - ramses_internal::DataInstanceHandle getViewportOffsetHandle() const; - ramses_internal::DataInstanceHandle getViewportSizeHandle() const; - ramses_internal::DataInstanceHandle getFrustrumPlanesHandle() const; - ramses_internal::DataInstanceHandle getFrustrumNearFarPlanesHandle() const; - - private: - ramses_internal::ProjectionParams getProjectionParams() const; - void updateProjectionParamsOnScene(const ramses_internal::ProjectionParams& params); - - ramses_internal::CameraHandle m_cameraHandle; - // Data layout/instance for data references to VP offset, VP size, frustum planes, frustum near/far planes. - // By default each reference points to a data instance with values settable/gettable via API (declared below), - // each can be however bound to external data reference (data object on HL). - ramses_internal::DataLayoutHandle m_dataLayout; - ramses_internal::DataInstanceHandle m_dataInstance; - // VP offset and size data instances holding values - ramses_internal::DataLayoutHandle m_viewportDataReferenceLayout; - ramses_internal::DataInstanceHandle m_viewportOffsetDataReference; - ramses_internal::DataInstanceHandle m_viewportSizeDataReference; - // Frustum planes data instance holding values (left, right, bottom, top) - ramses_internal::DataLayoutHandle m_frustumPlanesDataReferenceLayout; - ramses_internal::DataInstanceHandle m_frustumPlanesDataReference; - // Frustum near/far planes data instance holding values (near, far) - ramses_internal::DataLayoutHandle m_frustumNearFarDataReferenceLayout; - ramses_internal::DataInstanceHandle m_frustumNearFarDataReference; - - bool m_frustumInitialized = false; - bool m_viewportInitialized = false; - }; -} - -#endif diff --git a/client/ramses-client/impl/ClientCommands/DumpSceneToFile.cpp b/client/ramses-client/impl/ClientCommands/DumpSceneToFile.cpp deleted file mode 100644 index 34fec6bcb..000000000 --- a/client/ramses-client/impl/ClientCommands/DumpSceneToFile.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DumpSceneToFile.h" -#include "RamsesClientImpl.h" -#include "SceneCommandBuffer.h" - -namespace ramses_internal -{ - DumpSceneToFile::DumpSceneToFile(ramses::RamsesClientImpl& client) - : m_client(client) - { - description = "dump scene to file"; - registerKeyword("dumpSceneToFile"); - getArgument<0>().setDescription("scene id"); - getArgument<1>().setDescription("file name"); - getArgument<2>().setDescription("send dumped scene file and related resource files via dlt (-sendViaDLT)"); - getArgument<2>().setDefaultValue(""); - } - - bool DumpSceneToFile::execute(uint64_t& sceneId, std::string& fileName, std::string& sendViaDLT) const - { - SceneCommandDumpSceneToFile command; - command.fileName = fileName; - command.sendViaDLT = sendViaDLT == "-sendViaDLT"; - - m_client.enqueueSceneCommand(ramses::sceneId_t(sceneId), std::move(command)); - return true; - } -} diff --git a/client/ramses-client/impl/ClientCommands/DumpSceneToFile.h b/client/ramses-client/impl/ClientCommands/DumpSceneToFile.h deleted file mode 100644 index 6f234dcaf..000000000 --- a/client/ramses-client/impl/ClientCommands/DumpSceneToFile.h +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DUMPSCENETOFILE_H -#define RAMSES_DUMPSCENETOFILE_H - -#include "Ramsh/RamshCommandArguments.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -#include - -namespace ramses -{ - class RamsesClientImpl; -} - -namespace ramses_internal -{ - class DumpSceneToFile : public RamshCommandArgs - { - public: - explicit DumpSceneToFile(ramses::RamsesClientImpl& client); - bool execute(uint64_t& sceneId, std::string& fileName, std::string& sendViaDLT) const override; - - private: - ramses::RamsesClientImpl& m_client; - }; -} - -#endif diff --git a/client/ramses-client/impl/ClientCommands/FlushSceneVersion.h b/client/ramses-client/impl/ClientCommands/FlushSceneVersion.h deleted file mode 100644 index f2178001f..000000000 --- a/client/ramses-client/impl/ClientCommands/FlushSceneVersion.h +++ /dev/null @@ -1,33 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_FLUSHSCENEVERSION_H -#define RAMSES_FLUSHSCENEVERSION_H - -#include "Ramsh/RamshCommandArguments.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -namespace ramses -{ - class RamsesClientImpl; -} - -namespace ramses_internal -{ - class FlushSceneVersion : public RamshCommandArgs - { - public: - explicit FlushSceneVersion(ramses::RamsesClientImpl& client); - bool execute(ramses::sceneVersionTag_t& sceneVersion, uint64_t& sceneId) const override; - - private: - ramses::RamsesClientImpl& m_client; - }; -} - -#endif //RAMSES_FLUSHSCENEVERSION_H diff --git a/client/ramses-client/impl/ClientCommands/SceneCommandVisitor.cpp b/client/ramses-client/impl/ClientCommands/SceneCommandVisitor.cpp deleted file mode 100644 index 9f6346d91..000000000 --- a/client/ramses-client/impl/ClientCommands/SceneCommandVisitor.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "SceneCommandVisitor.h" -#include "SceneCommandBuffer.h" -#include "RamsesObjectTypeUtils.h" -#include "SceneImpl.h" -#include "RamsesClientImpl.h" -#include "LogMemoryUtils.h" -#include "Utils/LogMacros.h" -#include - -namespace ramses_internal -{ - - void SceneCommandVisitor::operator()(const SceneCommandFlushSceneVersion& cmd) - { - LOG_INFO(CONTEXT_CLIENT, "SceneCommandVisitor::execute: set scene version in next flush to \"" << cmd.sceneVersion << "\""); - m_scene.setSceneVersionForNextFlush(cmd.sceneVersion); - } - - void SceneCommandVisitor::operator()(const SceneCommandValidationRequest& cmd) - { - if (cmd.optionalObjectName.empty()) - { - // no object, validate whole scene - m_scene.validate(); - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "Validation: " << m_scene.getValidationReport(cmd.severity)); - } - else - { - if (const ramses::RamsesObject* ro = m_scene.findObjectByName(cmd.optionalObjectName.c_str())) - { - std::ignore = ro->validate(); - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "Validation: " << ro->getValidationReport(cmd.severity)); - } - else - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Validation could not find requested object with name: " << cmd.optionalObjectName); - } - } - - void SceneCommandVisitor::operator()(const SceneCommandDumpSceneToFile& cmd) const - { - const std::string sceneDumpFileWithExtension = cmd.fileName + ".ramses"; - const ramses::status_t status = m_scene.saveToFile(sceneDumpFileWithExtension.c_str(), false); - if (status == ramses::StatusOK) - { - if (cmd.sendViaDLT) - SceneCommandVisitor::SendSceneViaDLT(sceneDumpFileWithExtension); - } - else - LOG_WARN(CONTEXT_CLIENT, "SceneCommandVisitor::execute: failed to dump scene to file: " << m_scene.getStatusMessage(status)); - } - - void SceneCommandVisitor::operator()(const SceneCommandLogResourceMemoryUsage& /*cmd*/) const - { - MemoryInfoVector memoryInfos = GetMemoryInfoFromScene(m_scene); - std::sort(memoryInfos.begin(), memoryInfos.end(),[](const MemoryInfo& lhs, const MemoryInfo& rhs){return lhs.memoryUsage > rhs.memoryUsage;}); - - LOG_INFO_F(ramses_internal::CONTEXT_CLIENT,([&](ramses_internal::StringOutputStream& out){ - const uint32_t memTotal = std::accumulate(memoryInfos.begin(), memoryInfos.end(), 0u, - [](uint32_t v, const MemoryInfo& info){return v + info.memoryUsage;}); - out << "\n\rTotal memory usage: " << memTotal; - out << "\n\rDetailed memory usage:"; - for (const auto info : memoryInfos) - { - out << "\n\r"; - out << info.memoryUsage << "\t"; - out << info.logInfoMesage; - } - })); - } - - void SceneCommandVisitor::SendSceneViaDLT(const std::string& sceneDumpFileName) - { - if (GetRamsesLogger().transmitFile(sceneDumpFileName, false)) - { - LOG_INFO(CONTEXT_CLIENT, "SceneCommandVisitor::sendSceneAndResourceFilesViaDLT: started dlt file transfer: " << sceneDumpFileName); - } - else - { - LOG_INFO(CONTEXT_RENDERER, "SceneCommandVisitor::sendSceneAndResourceFilesViaDLT: failed to send scene dump file via dlt: " << sceneDumpFileName); - } - } -} diff --git a/client/ramses-client/impl/DataObjectImpl.cpp b/client/ramses-client/impl/DataObjectImpl.cpp deleted file mode 100644 index d4cf32393..000000000 --- a/client/ramses-client/impl/DataObjectImpl.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DataObjectImpl.h" -#include "SceneObjectImpl.h" -#include "Scene/ClientScene.h" -#include "SceneUtils/ISceneDataArrayAccessor.h" -#include "SceneAPI/ResourceContentHash.h" -#include "DataTypeUtils.h" - -namespace ramses -{ - DataObjectImpl::DataObjectImpl(SceneImpl& scene, ERamsesObjectType ramsesType, EDataType dataType, std::string_view name) - : SceneObjectImpl{ scene, ramsesType, name } - , m_dataType{ dataType } - { - } - - DataObjectImpl::~DataObjectImpl() = default; - - void DataObjectImpl::initializeFrameworkData() - { - ramses_internal::ClientScene& scene = getIScene(); - - // create data layout on scene - m_layoutHandle = scene.allocateDataLayout({ ramses_internal::DataFieldInfo(DataTypeUtils::ConvertDataTypeToInternal(m_dataType)) }, ramses_internal::ResourceContentHash::Invalid()); - - // allocate data instance based on created layout - m_dataReference = scene.allocateDataInstance(m_layoutHandle); - } - - void DataObjectImpl::deinitializeFrameworkData() - { - ramses_internal::ClientScene& scene = getIScene(); - - scene.releaseDataInstance(m_dataReference); - scene.releaseDataLayout(m_layoutHandle); - - m_dataReference = ramses_internal::DataInstanceHandle::Invalid(); - m_layoutHandle = ramses_internal::DataLayoutHandle::Invalid(); - } - - status_t DataObjectImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << static_cast(m_dataType); - outStream << m_layoutHandle; - outStream << m_dataReference; - - return StatusOK; - } - - status_t DataObjectImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - uint32_t enumType = 0u; - inStream >> enumType; - m_dataType = static_cast(enumType); - inStream >> m_layoutHandle; - inStream >> m_dataReference; - - return StatusOK; - } - - template - status_t DataObjectImpl::setValue(const T& value) - { - if (ramses_internal::TypeToEDataTypeTraits::DataType != DataTypeUtils::ConvertDataTypeToInternal(m_dataType)) - return addErrorEntry("DataObject::setValue failed, value type does not match DataObject data type"); - - ramses_internal::ISceneDataArrayAccessor::SetDataArray(&getIScene(), m_dataReference, ramses_internal::DataFieldHandle(0u), 1u, &value); - return StatusOK; - } - - template - status_t DataObjectImpl::getValue(T& value) const - { - if (ramses_internal::TypeToEDataTypeTraits::DataType != DataTypeUtils::ConvertDataTypeToInternal(m_dataType)) - return addErrorEntry("DataObject::getValue failed, value type does not match DataObject data type"); - - const T* data = ramses_internal::ISceneDataArrayAccessor::GetDataArray(&getIScene(), m_dataReference, ramses_internal::DataFieldHandle(0u)); - assert(data != nullptr); - value = data[0]; - - return StatusOK; - } - - EDataType DataObjectImpl::getDataType() const - { - return m_dataType; - } - - ramses_internal::DataInstanceHandle DataObjectImpl::getDataReference() const - { - return m_dataReference; - } - - template status_t DataObjectImpl::setValue(const int32_t&); - template status_t DataObjectImpl::setValue(const float&); - template status_t DataObjectImpl::setValue(const glm::vec2&); - template status_t DataObjectImpl::setValue(const glm::vec3&); - template status_t DataObjectImpl::setValue(const glm::vec4&); - template status_t DataObjectImpl::setValue(const glm::ivec2&); - template status_t DataObjectImpl::setValue(const glm::ivec3&); - template status_t DataObjectImpl::setValue(const glm::ivec4&); - template status_t DataObjectImpl::setValue(const glm::mat2&); - template status_t DataObjectImpl::setValue(const glm::mat3&); - template status_t DataObjectImpl::setValue(const glm::mat4&); - - template status_t DataObjectImpl::getValue(int32_t&) const; - template status_t DataObjectImpl::getValue(float&) const; - template status_t DataObjectImpl::getValue(glm::vec2&) const; - template status_t DataObjectImpl::getValue(glm::vec3&) const; - template status_t DataObjectImpl::getValue(glm::vec4&) const; - template status_t DataObjectImpl::getValue(glm::ivec2&) const; - template status_t DataObjectImpl::getValue(glm::ivec3&) const; - template status_t DataObjectImpl::getValue(glm::ivec4&) const; - template status_t DataObjectImpl::getValue(glm::mat2&) const; - template status_t DataObjectImpl::getValue(glm::mat3&) const; - template status_t DataObjectImpl::getValue(glm::mat4&) const; -} diff --git a/client/ramses-client/impl/EffectDescriptionImpl.cpp b/client/ramses-client/impl/EffectDescriptionImpl.cpp deleted file mode 100644 index d835e4f5e..000000000 --- a/client/ramses-client/impl/EffectDescriptionImpl.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// internal -#include "EffectDescriptionImpl.h" -#include "EffectInputSemanticUtils.h" - -// framework -#include "Utils/File.h" -#include "Utils/LogMacros.h" - - -namespace ramses -{ - EffectDescriptionImpl::EffectDescriptionImpl() - { - } - - EffectDescriptionImpl::~EffectDescriptionImpl() - { - } - - status_t EffectDescriptionImpl::setVertexShader(std::string_view shaderSource) - { - m_vertexShaderSource = shaderSource; - return StatusOK; - } - - status_t EffectDescriptionImpl::setFragmentShader(std::string_view shaderSource) - { - m_fragmentShaderSource = shaderSource; - return StatusOK; - } - - status_t EffectDescriptionImpl::setGeometryShader(std::string_view shaderSource) - { - m_geometryShaderSource = shaderSource; - return StatusOK; - } - - status_t EffectDescriptionImpl::setVertexShaderFromFile(std::string_view shaderSourceFileName) - { - if (!ReadFileContentsToString(shaderSourceFileName, m_vertexShaderSource)) - { - return addErrorEntry("EffectDescription::setVertexShaderFromFile could not read file!"); - } - - return StatusOK; - } - - status_t EffectDescriptionImpl::setFragmentShaderFromFile(std::string_view shaderSourceFileName) - { - if (!ReadFileContentsToString(shaderSourceFileName, m_fragmentShaderSource)) - { - return addErrorEntry("EffectDescription::setFragmentShaderFromFile could not read file!"); - } - - return StatusOK; - } - - ramses::status_t EffectDescriptionImpl::setGeometryShaderFromFile(std::string_view shaderSourceFileName) - { - if (!ReadFileContentsToString(shaderSourceFileName, m_geometryShaderSource)) - { - return addErrorEntry("EffectDescription::setGeometryShaderFromFile could not read file!"); - } - - return StatusOK; - } - - status_t EffectDescriptionImpl::addCompilerDefine(std::string_view define) - { - if (define.empty()) - { - return addErrorEntry("EffectDescription::addCompilerDefine cannot add empty define!"); - } - - m_compilerDefines.emplace_back(define); - return StatusOK; - } - - status_t EffectDescriptionImpl::setSemantic(std::string_view semanticName, ramses_internal::EFixedSemantics semanticType) - { - if (semanticName.empty()) - { - return addErrorEntry("EffectDescription::setSemantic cannot set empty semantic name!"); - } - - m_inputSemantics.put(std::string{semanticName}, semanticType); - return StatusOK; - } - - status_t EffectDescriptionImpl::setUniformSemantic(std::string_view semanticName, EEffectUniformSemantic semanticType) - { - const ramses_internal::EFixedSemantics semanticTypeInternal = EffectInputSemanticUtils::GetEffectInputSemanticInternal(semanticType); - return setSemantic(semanticName, semanticTypeInternal); - } - - status_t EffectDescriptionImpl::setAttributeSemantic(std::string_view semanticName, EEffectAttributeSemantic semanticType) - { - const ramses_internal::EFixedSemantics semanticTypeInternal = EffectInputSemanticUtils::GetEffectInputSemanticInternal(semanticType); - return setSemantic(semanticName, semanticTypeInternal); - } - - const char* EffectDescriptionImpl::getVertexShader() const - { - return m_vertexShaderSource.c_str(); - } - - const char* EffectDescriptionImpl::getFragmentShader() const - { - return m_fragmentShaderSource.c_str(); - } - - const char* EffectDescriptionImpl::getGeometryShader() const - { - return m_geometryShaderSource.c_str(); - } - - size_t EffectDescriptionImpl::getNumberOfCompilerDefines() const - { - return m_compilerDefines.size(); - } - - const std::vector& EffectDescriptionImpl::getCompilerDefines() const - { - return m_compilerDefines; - } - - const char* EffectDescriptionImpl::getCompilerDefine(size_t index) const - { - if (index < getNumberOfCompilerDefines()) - { - return m_compilerDefines[index].c_str(); - } - - return nullptr; - } - - const EffectDescriptionImpl::SemanticsMap& EffectDescriptionImpl::getSemanticsMap() const - { - return m_inputSemantics; - } - - bool EffectDescriptionImpl::ReadFileContentsToString(std::string_view fileName, std::string& fileContents) - { - ramses_internal::File inFile(fileName); - if (!inFile.exists()) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: could not find file: " << fileName); - return false; - } - - if (!inFile.open(ramses_internal::File::Mode::ReadOnlyBinary)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: could not open file: " << fileName); - return false; - } - - size_t fileSize = 0; - size_t readBytes = 0; - if (!inFile.getSizeInBytes(fileSize)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: error reading file info: " << fileName); - return false; - } - - std::vector charVector(fileSize + 1u); - const ramses_internal::EStatus stat = inFile.read(&charVector[0], fileSize, readBytes); - if (stat == ramses_internal::EStatus::Ok || stat == ramses_internal::EStatus::Eof) - { - charVector[readBytes] = '\0'; - fileContents = &charVector[0]; - return true; - } - else - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: error reading file contents: " << fileName); - return false; - } - } -} diff --git a/client/ramses-client/impl/EffectDescriptionImpl.h b/client/ramses-client/impl/EffectDescriptionImpl.h deleted file mode 100644 index 4479ab667..000000000 --- a/client/ramses-client/impl/EffectDescriptionImpl.h +++ /dev/null @@ -1,69 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EFFECTDESCRIPTIONIMPL_H -#define RAMSES_EFFECTDESCRIPTIONIMPL_H - -// API -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/EffectInputSemantic.h" - -// internal -#include "StatusObjectImpl.h" - -// ramses framework -#include "Collections/HashMap.h" -#include "SceneAPI/EFixedSemantics.h" - -#include -#include -#include - -namespace ramses -{ - class EffectDescriptionImpl : public StatusObjectImpl - { - public: - using SemanticsMap = ramses_internal::HashMap; - - EffectDescriptionImpl(); - ~EffectDescriptionImpl() override; - - status_t setVertexShader(std::string_view shaderSource); - status_t setFragmentShader(std::string_view shaderSource); - status_t setGeometryShader(std::string_view shaderSource); - status_t setVertexShaderFromFile(std::string_view shaderSourceFileName); - status_t setFragmentShaderFromFile(std::string_view shaderSourceFileName); - status_t setGeometryShaderFromFile(std::string_view shaderSourceFileName); - status_t addCompilerDefine(std::string_view define); - status_t setUniformSemantic(std::string_view semanticName, EEffectUniformSemantic semanticType); - status_t setAttributeSemantic(std::string_view semanticName, EEffectAttributeSemantic semanticType); - - const char* getVertexShader() const; - const char* getFragmentShader() const; - const char* getGeometryShader() const; - size_t getNumberOfCompilerDefines() const; - const std::vector& getCompilerDefines() const; - const char* getCompilerDefine(size_t index) const; - const SemanticsMap& getSemanticsMap() const; - - static bool ReadFileContentsToString(std::string_view fileName, std::string& fileContents); - - private: - status_t setSemantic(std::string_view semanticName, ramses_internal::EFixedSemantics semanticType); - - std::string m_vertexShaderSource; - std::string m_fragmentShaderSource; - std::string m_geometryShaderSource; - - std::vector m_compilerDefines; - SemanticsMap m_inputSemantics; - }; -} - -#endif diff --git a/client/ramses-client/impl/EffectImpl.cpp b/client/ramses-client/impl/EffectImpl.cpp deleted file mode 100644 index 8331c4711..000000000 --- a/client/ramses-client/impl/EffectImpl.cpp +++ /dev/null @@ -1,269 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "EffectImpl.h" -#include "EffectInputImpl.h" -#include "SerializationContext.h" -#include "RamsesClientImpl.h" -#include "Components/ManagedResource.h" -#include "Resource/IResource.h" -#include "Components/ResourceHashUsage.h" -#include "Resource/EffectResource.h" -#include "Collections/StringOutputStream.h" -#include "EffectInputSemanticUtils.h" -#include "AppearanceUtils.h" - -namespace ramses -{ - EffectImpl::EffectImpl(ramses_internal::ResourceHashUsage hashUsage, SceneImpl& scene, std::string_view effectname) - : ResourceImpl(ERamsesObjectType::Effect, std::move(hashUsage), scene, effectname) - { - } - - EffectImpl::~EffectImpl() - { - } - - void EffectImpl::initializeFromFrameworkData(const ramses_internal::EffectInputInformationVector& uniformInputs, const ramses_internal::EffectInputInformationVector& attributeInputs, std::optional geometryShaderInputType) - { - m_effectUniformInputs = uniformInputs; - m_effectAttributeInputs = attributeInputs; - m_geometryShaderInputType = geometryShaderInputType; - } - - status_t EffectImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(ResourceImpl::serialize(outStream, serializationContext)); - - outStream << static_cast(m_effectUniformInputs.size()); - for(const auto& input : m_effectUniformInputs) - { - outStream << input.inputName; - outStream << static_cast(input.dataType); - outStream << static_cast(input.elementCount); - outStream << static_cast(input.semantics); - } - - outStream << static_cast(m_effectAttributeInputs.size()); - for (const auto& input : m_effectAttributeInputs) - { - outStream << input.inputName; - outStream << static_cast(input.dataType); - outStream << static_cast(input.semantics); - } - - const int32_t gsInputType = (m_geometryShaderInputType ? static_cast(*m_geometryShaderInputType) : -1); - outStream << gsInputType; - - return StatusOK; - } - - status_t EffectImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(ResourceImpl::deserialize(inStream, serializationContext)); - - uint32_t count = 0u; - inStream >> count; - m_effectUniformInputs.resize(count); - for (uint32_t i = 0u; i < count; ++i) - { - inStream >> m_effectUniformInputs[i].inputName; - - uint32_t dataTypeAsUInt = 0; - inStream >> dataTypeAsUInt; - uint32_t elementCount = 0; - inStream >> elementCount; - uint32_t semanticAsUInt = 0; - inStream >> semanticAsUInt; - - m_effectUniformInputs[i].dataType = static_cast(dataTypeAsUInt); - m_effectUniformInputs[i].elementCount = elementCount; - m_effectUniformInputs[i].semantics = static_cast(semanticAsUInt); - } - - inStream >> count; - m_effectAttributeInputs.resize(count); - for (uint32_t i = 0u; i < count; ++i) - { - inStream >> m_effectAttributeInputs[i].inputName; - - uint32_t dataTypeAsUInt = 0; - inStream >> dataTypeAsUInt; - uint32_t semanticAsUInt = 0; - inStream >> semanticAsUInt; - - m_effectAttributeInputs[i].dataType = static_cast(dataTypeAsUInt); - m_effectAttributeInputs[i].elementCount = 1u; - m_effectAttributeInputs[i].semantics = static_cast(semanticAsUInt); - } - - int32_t gsInputType = -1; - inStream >> gsInputType; - if (gsInputType >= 0) - m_geometryShaderInputType = static_cast(gsInputType); - - return StatusOK; - } - - size_t EffectImpl::getUniformInputCount() const - { - return m_effectUniformInputs.size(); - } - - size_t EffectImpl::getAttributeInputCount() const - { - return m_effectAttributeInputs.size(); - } - - status_t EffectImpl::getUniformInput(size_t index, EffectInputImpl& inputImpl) const - { - if (index >= getUniformInputCount()) - { - return addErrorEntry("Effect: getUniformInput failed, index out of range!"); - } - - initializeEffectInputData(inputImpl, m_effectUniformInputs[index], index); - - return StatusOK; - } - - status_t EffectImpl::findUniformInput(EEffectUniformSemantic uniformSemantic, EffectInputImpl& inputImpl) const - { - const size_t index = findEffectInputIndex(m_effectUniformInputs, EffectInputSemanticUtils::GetEffectInputSemanticInternal(uniformSemantic)); - if (index == InvalidInputIndex) - { - return addErrorEntry("Effect: getUniformInput failed, semantic is not defined in effect!"); - } - - const ramses_internal::EffectInputInformation& effectInputInfo = m_effectUniformInputs[index]; - initializeEffectInputData(inputImpl, effectInputInfo, index); - - return StatusOK; - } - - status_t EffectImpl::getAttributeInput(size_t index, EffectInputImpl& inputImpl) const - { - if (index >= getAttributeInputCount()) - { - return addErrorEntry("Effect: getAttributeInput failed, index out of range!"); - } - - initializeEffectInputData(inputImpl, m_effectAttributeInputs[index], index); - - return StatusOK; - } - - status_t EffectImpl::findAttributeInput(EEffectAttributeSemantic attributeSemantic, EffectInputImpl& inputImpl) const - { - const size_t index = findEffectInputIndex(m_effectAttributeInputs, EffectInputSemanticUtils::GetEffectInputSemanticInternal(attributeSemantic)); - if (index == InvalidInputIndex) - { - return addErrorEntry("Effect: getAttributeInput failed, semantic not defined in effect!"); - } - - const ramses_internal::EffectInputInformation& effectInputInfo = m_effectAttributeInputs[index]; - initializeEffectInputData(inputImpl, effectInputInfo, index); - - return StatusOK; - } - - status_t EffectImpl::findUniformInput(std::string_view inputName, EffectInputImpl& inputImpl) const - { - const size_t index = getEffectInputIndex(m_effectUniformInputs, inputName); - if (index == InvalidInputIndex) - return addErrorEntry((ramses_internal::StringOutputStream() << "Effect::findUniformInput: failed, uniform input '" << inputName << "' could not be found in effect '" << getName()).c_str()); - - const ramses_internal::EffectInputInformation& effectInputInfo = m_effectUniformInputs[index]; - initializeEffectInputData(inputImpl, effectInputInfo, index); - - return StatusOK; - } - - status_t EffectImpl::findAttributeInput(std::string_view inputName, EffectInputImpl& inputImpl) const - { - const size_t index = getEffectInputIndex(m_effectAttributeInputs, inputName); - if (index == InvalidInputIndex) - return addErrorEntry((ramses_internal::StringOutputStream() << "Effect::findAttributeInput: failed, attribute input '" << inputName << "' could not be found in effect '" << getName()).c_str()); - - const ramses_internal::EffectInputInformation& effectInputInfo = m_effectAttributeInputs[index]; - initializeEffectInputData(inputImpl, effectInputInfo, index); - - return StatusOK; - } - - const ramses_internal::EffectInputInformationVector& EffectImpl::getUniformInputInformation() const - { - return m_effectUniformInputs; - } - - const ramses_internal::EffectInputInformationVector& EffectImpl::getAttributeInputInformation() const - { - return m_effectAttributeInputs; - } - - size_t EffectImpl::getEffectInputIndex(const ramses_internal::EffectInputInformationVector& effectInputVector, std::string_view inputName) const - { - const size_t numInputs = effectInputVector.size(); - for (size_t i = 0u; i < numInputs; ++i) - { - const ramses_internal::EffectInputInformation& effectInputInfo = effectInputVector[i]; - if (effectInputInfo.inputName == inputName) - { - return i; - } - } - - return InvalidInputIndex; - } - - size_t EffectImpl::findEffectInputIndex(const ramses_internal::EffectInputInformationVector& effectInputVector, ramses_internal::EFixedSemantics inputSemantics) const - { - if (ramses_internal::EFixedSemantics::Invalid == inputSemantics) - return InvalidInputIndex; - - const size_t numInputs = effectInputVector.size(); - for (size_t i = 0u; i < numInputs; ++i) - { - const ramses_internal::EffectInputInformation& effectInputInfo = effectInputVector[i]; - if (effectInputInfo.semantics == inputSemantics) - { - return i; - } - } - - return InvalidInputIndex; - } - - void EffectImpl::initializeEffectInputData(EffectInputImpl& effectInputImpl, const ramses_internal::EffectInputInformation& effectInputInfo, size_t index) const - { - effectInputImpl.initialize( - getLowlevelResourceHash(), - effectInputInfo.inputName, - effectInputInfo.dataType, - effectInputInfo.semantics, - effectInputInfo.elementCount, - index - ); - } - - bool EffectImpl::hasGeometryShader() const - { - return m_geometryShaderInputType.has_value(); - } - - status_t EffectImpl::getGeometryShaderInputType(EDrawMode& inputType) const - { - if (!hasGeometryShader()) - { - return addErrorEntry((ramses_internal::StringOutputStream() << "Effect::getGeometryShaderInputType: failed, effect '" << getName() << "' has no geometry shader attached to it!").c_str()); - } - - inputType = *m_geometryShaderInputType; - return StatusOK; - } -} diff --git a/client/ramses-client/impl/EffectImpl.h b/client/ramses-client/impl/EffectImpl.h deleted file mode 100644 index 9da9cac14..000000000 --- a/client/ramses-client/impl/EffectImpl.h +++ /dev/null @@ -1,70 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EFFECTIMPL_H -#define RAMSES_EFFECTIMPL_H - -// client api -#include "ramses-client-api/EffectInputSemantic.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -// internal -#include "ResourceImpl.h" - -// ramses framework -#include "SceneUtils/DataLayoutCreationHelper.h" -#include "SceneAPI/RenderState.h" - -#include -#include - -namespace ramses -{ - class EffectInputImpl; - - class EffectImpl final : public ResourceImpl - { - public: - EffectImpl(ramses_internal::ResourceHashUsage hashUsage, SceneImpl& scene, std::string_view effectname); - ~EffectImpl() override; - - void initializeFromFrameworkData(const ramses_internal::EffectInputInformationVector& uniformInputs, const ramses_internal::EffectInputInformationVector& attributeInputs, std::optional geometryShaderInputType); - - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - size_t getUniformInputCount() const; - size_t getAttributeInputCount() const; - - bool hasGeometryShader() const; - status_t getGeometryShaderInputType(EDrawMode& inputType) const; - - status_t getUniformInput(size_t index, EffectInputImpl& inputImpl) const; - status_t findUniformInput(EEffectUniformSemantic uniformSemantic, EffectInputImpl& inputImpl) const; - status_t getAttributeInput(size_t index, EffectInputImpl& inputImpl) const; - status_t findAttributeInput(EEffectAttributeSemantic attributeSemantic, EffectInputImpl& inputImpl) const; - status_t findUniformInput(std::string_view inputName, EffectInputImpl& inputImpl) const; - status_t findAttributeInput(std::string_view inputName, EffectInputImpl& inputImpl) const; - - const ramses_internal::EffectInputInformationVector& getUniformInputInformation() const; - const ramses_internal::EffectInputInformationVector& getAttributeInputInformation() const; - - private: - static const size_t InvalidInputIndex = 0xffff; - - size_t getEffectInputIndex(const ramses_internal::EffectInputInformationVector& effectInputVector, std::string_view inputName) const; - size_t findEffectInputIndex(const ramses_internal::EffectInputInformationVector& effectInputVector, ramses_internal::EFixedSemantics inputSemantics) const; - void initializeEffectInputData(EffectInputImpl& effectInputImpl, const ramses_internal::EffectInputInformation& effectInputInfo, size_t index) const; - - ramses_internal::EffectInputInformationVector m_effectUniformInputs; - ramses_internal::EffectInputInformationVector m_effectAttributeInputs; - std::optional m_geometryShaderInputType; - }; -} - -#endif diff --git a/client/ramses-client/impl/EffectInputImpl.cpp b/client/ramses-client/impl/EffectInputImpl.cpp deleted file mode 100644 index 499452c50..000000000 --- a/client/ramses-client/impl/EffectInputImpl.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "EffectInputImpl.h" -#include "DataTypeUtils.h" -#include "EffectInputSemanticUtils.h" -#include "SceneAPI/IScene.h" - -namespace ramses -{ - EffectInputImpl::EffectInputImpl() - : StatusObjectImpl() - , m_effectHash(ramses_internal::ResourceContentHash::Invalid()) - , m_dataType(ramses_internal::EDataType::Invalid) - , m_semantics(ramses_internal::EFixedSemantics::Invalid) - , m_elementCount(0u) - , m_inputIndex(static_cast(-1)) - { - } - - EffectInputImpl::~EffectInputImpl() - { - } - - void EffectInputImpl::initialize( - const ramses_internal::ResourceContentHash& effectHash, - std::string_view name, - ramses_internal::EDataType dataType, - ramses_internal::EFixedSemantics semantics, - size_t elementCount, - size_t index) - { - m_effectHash = effectHash; - m_name = name; - m_dataType = dataType; - m_semantics = semantics; - m_elementCount = elementCount; - m_inputIndex = index; - } - - bool EffectInputImpl::isValid() const - { - return m_effectHash.isValid(); - } - - ramses_internal::ResourceContentHash EffectInputImpl::getEffectHash() const - { - return m_effectHash; - } - - const std::string& EffectInputImpl::getName() const - { - return m_name; - } - - std::optional EffectInputImpl::getDataType() const - { - if (!isValid()) - return std::nullopt; - - return DataTypeUtils::ConvertDataTypeFromInternal(m_dataType); - } - - ramses_internal::EDataType EffectInputImpl::getInternalDataType() const - { - return m_dataType; - } - - ramses_internal::EFixedSemantics EffectInputImpl::getSemantics() const - { - return m_semantics; - } - - size_t EffectInputImpl::getElementCount() const - { - return m_elementCount; - } - - size_t EffectInputImpl::getInputIndex() const - { - return m_inputIndex; - } - - EEffectUniformSemantic EffectInputImpl::getUniformSemantics() const - { - return EffectInputSemanticUtils::GetEffectUniformSemanticFromInternal(m_semantics); - } - - EEffectAttributeSemantic EffectInputImpl::getAttributeSemantics() const - { - return EffectInputSemanticUtils::GetEffectAttributeSemanticFromInternal(m_semantics); - } - - ramses::status_t EffectInputImpl::serialize(ramses_internal::IOutputStream& outStream) const - { - outStream << m_effectHash; - outStream << m_name; - outStream << static_cast(m_dataType); - outStream << static_cast(m_semantics); - outStream << static_cast(m_elementCount); - outStream << static_cast(m_inputIndex); - - return StatusOK; - } - - ramses::status_t EffectInputImpl::deserialize(ramses_internal::IInputStream& inStream) - { - inStream >> m_effectHash; - inStream >> m_name; - - uint32_t dataType; - inStream >> dataType; - m_dataType = ramses_internal::EDataType(dataType); - - uint32_t semantics; - inStream >> semantics; - m_semantics = ramses_internal::EFixedSemantics(semantics); - - uint32_t elementCount; - inStream >> elementCount; - m_elementCount = elementCount; - - uint32_t inputIndex; - inStream >> inputIndex; - m_inputIndex = inputIndex; - - return StatusOK; - } - - bool EffectInputImpl::operator==(const EffectInputImpl& other) const - { - return m_effectHash == other.m_effectHash && - m_name == other.m_name && - m_dataType == other.m_dataType && - m_semantics == other.m_semantics && - m_elementCount == other.m_elementCount && - m_inputIndex == other.m_inputIndex; - } - - bool EffectInputImpl::operator!=(const EffectInputImpl& other) const - { - return !operator==(other); - } -} diff --git a/client/ramses-client/impl/EffectInputImpl.h b/client/ramses-client/impl/EffectInputImpl.h deleted file mode 100644 index ff484137f..000000000 --- a/client/ramses-client/impl/EffectInputImpl.h +++ /dev/null @@ -1,72 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EFFECTINPUTIMPL_H -#define RAMSES_EFFECTINPUTIMPL_H - -// client api -#include "ramses-framework-api/EDataType.h" -#include "ramses-client-api/EffectInputSemantic.h" - -// internal -#include "StatusObjectImpl.h" - -// framework -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EFixedSemantics.h" - -#include -#include - -namespace ramses -{ - class EffectInputImpl final : public StatusObjectImpl - { - public: - EffectInputImpl(); - ~EffectInputImpl() override; - - void initialize( - const ramses_internal::ResourceContentHash& effectHash, - std::string_view name, - ramses_internal::EDataType dataType, - ramses_internal::EFixedSemantics semantics, - size_t elementCount, - size_t index); - - bool isValid() const; - - ramses_internal::ResourceContentHash getEffectHash() const; - const std::string& getName() const; - ramses_internal::EDataType getInternalDataType() const; - ramses_internal::EFixedSemantics getSemantics() const; - size_t getElementCount() const; - size_t getInputIndex() const; - - std::optional getDataType() const; - EEffectUniformSemantic getUniformSemantics() const; - EEffectAttributeSemantic getAttributeSemantics() const; - - status_t serialize(ramses_internal::IOutputStream& outStream) const; - status_t deserialize(ramses_internal::IInputStream& inStream); - - bool operator==(const EffectInputImpl& other)const; - bool operator!=(const EffectInputImpl& other)const; - - private: - ramses_internal::ResourceContentHash m_effectHash; - std::string m_name; - ramses_internal::EDataType m_dataType; - ramses_internal::EFixedSemantics m_semantics; - size_t m_elementCount; - size_t m_inputIndex; - }; -} - -#endif diff --git a/client/ramses-client/impl/GeometryBindingImpl.cpp b/client/ramses-client/impl/GeometryBindingImpl.cpp deleted file mode 100644 index 87c151d8d..000000000 --- a/client/ramses-client/impl/GeometryBindingImpl.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - - -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/ArrayBuffer.h" - -#include "GeometryBindingImpl.h" -#include "EffectInputImpl.h" -#include "ArrayResourceImpl.h" -#include "EffectImpl.h" -#include "RamsesClientImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "Scene/ClientScene.h" -#include "SceneObjectImpl.h" -#include "SceneImpl.h" -#include "ArrayBufferImpl.h" -#include "RamsesObjectRegistryIterator.h" -#include "DataTypeUtils.h" -#include "ObjectIteratorImpl.h" -#include "SerializationContext.h" - -namespace ramses -{ - GeometryBindingImpl::GeometryBindingImpl(SceneImpl& scene, std::string_view name) - : SceneObjectImpl(scene, ERamsesObjectType::GeometryBinding, name) - , m_effectImpl(nullptr) - , m_indicesCount(0u) - { - } - - GeometryBindingImpl::~GeometryBindingImpl() - { - } - - void GeometryBindingImpl::initializeFrameworkData(const EffectImpl& effect) - { - m_effectImpl = &effect; - createDataLayout(); - } - - void GeometryBindingImpl::deinitializeFrameworkData() - { - if (m_attributeInstance.isValid()) - { - getIScene().releaseDataInstance(m_attributeInstance); - m_attributeInstance = ramses_internal::DataInstanceHandle::Invalid(); - } - - if (m_attributeLayout.isValid()) - { - getIScene().releaseDataLayout(m_attributeLayout); - m_attributeLayout = ramses_internal::DataLayoutHandle::Invalid(); - } - } - - status_t GeometryBindingImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << (m_effectImpl ? serializationContext.getIDForObject(m_effectImpl) : serializationContext.GetObjectIDNull()); - - outStream << m_attributeLayout; - outStream << m_attributeInstance; - outStream << m_indicesCount; - - return StatusOK; - } - - status_t GeometryBindingImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_effectImpl); - - inStream >> m_attributeLayout; - inStream >> m_attributeInstance; - inStream >> m_indicesCount; - - serializationContext.addForDependencyResolve(this); - - return StatusOK; - } - - status_t GeometryBindingImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::resolveDeserializationDependencies(serializationContext)); - - serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_effectImpl); - - return StatusOK; - } - - status_t GeometryBindingImpl::validate() const - { - status_t status = SceneObjectImpl::validate(); - - const status_t effectStatus = validateEffect(); - if (StatusOK != effectStatus) - status = effectStatus; - - const status_t attributeStatus = validateAttribute(); - if (StatusOK != attributeStatus) - status = attributeStatus; - - return status; - } - - status_t GeometryBindingImpl::validateEffect() const - { - ObjectIteratorImpl iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::Effect); - RamsesObject* ramsesObject = iter.getNext(); - while (nullptr != ramsesObject) - { - const Effect& effect = RamsesObjectTypeUtils::ConvertTo(*ramsesObject); - if (&effect.m_impl == m_effectImpl) - return addValidationOfDependentObject(*m_effectImpl); - - ramsesObject = iter.getNext(); - } - - return addValidationMessage(EValidationSeverity::Error, "GeometryBinding is referring to an invalid Effect"); - } - - status_t GeometryBindingImpl::validateAttribute() const - { - status_t status = StatusOK; - const ramses_internal::DataLayout& layout = getIScene().getDataLayout(m_attributeLayout); - const uint32_t dataLayoutFieldCount = layout.getFieldCount(); - for (ramses_internal::DataFieldHandle fieldIndex(0u); fieldIndex < dataLayoutFieldCount; ++fieldIndex) - { - const ramses_internal::ResourceField& dataResource = - getIScene().getDataResource(m_attributeInstance, fieldIndex); - - if (dataResource.dataBuffer.isValid()) - { - const ramses_internal::EDataType fieldDataType = layout.getField(fieldIndex).dataType; - const status_t dataBufferStatus = - validateDataBuffer(dataResource.dataBuffer, fieldDataType); - if (StatusOK != dataBufferStatus) - status = dataBufferStatus; - } - else - { - if (dataResource.hash.isValid()) - { - const status_t resourceStatus = validateResource(dataResource.hash); - if (StatusOK != resourceStatus) - status = resourceStatus; - } - } - } - - return status; - } - - status_t GeometryBindingImpl::validateResource(ramses_internal::ResourceContentHash resourceHash) const - { - const Resource* resource = getSceneImpl().scanForResourceWithHash(resourceHash); - if (nullptr == resource) - return addValidationMessage(EValidationSeverity::Error, "GeometryBinding is referring to resource that does not exist"); - - return addValidationOfDependentObject(resource->m_impl); - } - - status_t GeometryBindingImpl::validateDataBuffer(ramses_internal::DataBufferHandle dataBuffer, ramses_internal::EDataType fieldDataType) const - { - if (!getIScene().isDataBufferAllocated(dataBuffer)) - return addValidationMessage(EValidationSeverity::Error, "GeometryBinding is referring to data buffer that does not exist"); - - const auto dataBufferType = getIScene().getDataBuffer(dataBuffer).dataType; - if (!dataTypeMatchesInputType(dataBufferType, fieldDataType)) - return addValidationMessage(EValidationSeverity::Error, "GeometryBinding is referring to data buffer with type that does not match data layout field type"); - - const ArrayBufferImpl* dataBufferImpl = findDataBuffer(dataBuffer); - assert(nullptr != dataBufferImpl); - - return addValidationOfDependentObject(*dataBufferImpl); - } - - ramses::ArrayBufferImpl* GeometryBindingImpl::findDataBuffer(ramses_internal::DataBufferHandle dataBufferHandle) const - { - RamsesObjectRegistryIterator arrayBufferIter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::ArrayBufferObject); - while (const ArrayBuffer* dataBuffer = arrayBufferIter.getNext()) - { - if (dataBuffer->m_impl.getDataBufferHandle() == dataBufferHandle) - { - return &dataBuffer->m_impl; - } - } - - return nullptr; - } - - void GeometryBindingImpl::createDataLayout() - { - assert(!m_attributeLayout.isValid()); - - const ramses_internal::EffectInputInformationVector& attributesList = m_effectImpl->getAttributeInputInformation(); - ramses_internal::DataFieldInfoVector dataFields; - dataFields.reserve(attributesList.size()); - - // Indices are always stored at fixed data slot with index IndicesDataFieldIndex - dataFields.push_back(ramses_internal::DataFieldInfo{ ramses_internal::EDataType::Indices, 1u, ramses_internal::EFixedSemantics::Indices }); - for (const auto& attribInfo : attributesList) - { - dataFields.push_back(ramses_internal::DataFieldInfo{ attribInfo.dataType, attribInfo.elementCount, attribInfo.semantics }); - } - - m_attributeLayout = getIScene().allocateDataLayout(dataFields, m_effectImpl->getLowlevelResourceHash()); - m_attributeInstance = getIScene().allocateDataInstance(m_attributeLayout); - } - - ramses_internal::ResourceContentHash GeometryBindingImpl::getEffectHash() const - { - return m_effectImpl->getLowlevelResourceHash(); - } - - ramses_internal::DataLayoutHandle GeometryBindingImpl::getAttributeDataLayout() const - { - return m_attributeLayout; - } - - ramses_internal::DataInstanceHandle GeometryBindingImpl::getAttributeDataInstance() const - { - return m_attributeInstance; - } - - uint32_t GeometryBindingImpl::getIndicesCount() const - { - return m_indicesCount; - } - - status_t GeometryBindingImpl::setIndices(const ArrayResourceImpl& arrayResource) - { - if (!isFromTheSameSceneAs(arrayResource)) - { - return addErrorEntry("GeometryBinding::setIndices failed, indicesResource is not from the same client as this GeometryBinding."); - } - - if (!DataTypeUtils::IsValidIndicesType(arrayResource.getElementType())) - { - return addErrorEntry("GeometryBinding::setIndices failed, indicesResource is not of valid data type."); - } - - const ramses_internal::DataLayout& layout = getIScene().getDataLayout(m_attributeLayout); - const uint32_t fieldCount = layout.getFieldCount(); - ramses_internal::EFixedSemantics indicesFieldSemantics = ramses_internal::EFixedSemantics::Invalid; - const ramses_internal::DataFieldHandle field(IndicesDataFieldIndex); - - if (IndicesDataFieldIndex < fieldCount) - { - indicesFieldSemantics = layout.getField(field).semantics; - } - - if (indicesFieldSemantics == ramses_internal::EFixedSemantics::Indices) - { - getIScene().setDataResource(m_attributeInstance, field, arrayResource.getLowlevelResourceHash(), ramses_internal::DataBufferHandle::Invalid(), 0u, 0u, 0u); - m_indicesCount = arrayResource.getElementCount(); - - return StatusOK; - } - - return addErrorEntry("GeometryBinding::setIndices failed - indices slot was not enabled in this geometry."); - } - - status_t GeometryBindingImpl::setIndices(const ArrayBufferImpl& dataBuffer) - { - if (!isFromTheSameSceneAs(dataBuffer)) - { - return addErrorEntry("GeometryBinding::setIndices failed, dataBuffer is not from the same scene as this GeometryBinding."); - } - - if (!DataTypeUtils::IsValidIndicesType(dataBuffer.getDataType())) - { - return addErrorEntry("GeometryBinding::setIndices failed, arrayBuffer is not of valid data type."); - } - - const ramses_internal::DataLayout& layout = getIScene().getDataLayout(m_attributeLayout); - const uint32_t fieldCount = layout.getFieldCount(); - ramses_internal::EFixedSemantics indicesFieldSemantics = ramses_internal::EFixedSemantics::Invalid; - const ramses_internal::DataFieldHandle field(IndicesDataFieldIndex); - - if (IndicesDataFieldIndex < fieldCount) - { - indicesFieldSemantics = layout.getField(field).semantics; - } - - if (indicesFieldSemantics == ramses_internal::EFixedSemantics::Indices) - { - const ramses_internal::DataBufferHandle dataBufferHandle = dataBuffer.getDataBufferHandle(); - - getIScene().setDataResource(m_attributeInstance, field, ramses_internal::ResourceContentHash::Invalid(), dataBufferHandle, 0u, 0u, 0u); - - m_indicesCount = dataBuffer.getElementCount(); - - return StatusOK; - } - - return addErrorEntry("GeometryBinding::setIndices failed - indices slot was not enabled in this geometry."); - } - - status_t GeometryBindingImpl::setInputBuffer(const EffectInputImpl& input, const ArrayResourceImpl& bufferResource, uint32_t instancingDivisor, uint16_t offset, uint16_t stride) - { - if (!isFromTheSameSceneAs(bufferResource)) - { - return addErrorEntry("GeometryBinding::setInputBuffer failed, array resource is not from the same client as the GeometryBinding."); - } - - if (!DataTypeUtils::IsValidVerticesType(bufferResource.getElementType())) - { - return addErrorEntry("GeometryBinding::setInputBuffer failed, array resource is not of valid data type."); - } - - if (input.getEffectHash() != m_effectImpl->getLowlevelResourceHash()) - { - return addErrorEntry("GeometryBinding::setInputBuffer failed, input is not properly initialized or cannot be used with this geometry binding."); - } - - if ((offset > 0 || stride > 0) && bufferResource.getElementType() != EDataType::ByteBlob) - return addErrorEntry("GeometryBinding::setInputBuffer failed, custom stride/offset can be used only with array resources of type byte blob"); - - if (!dataTypeMatchesInputType(DataTypeUtils::ConvertDataTypeToInternal(bufferResource.getElementType()), input.getInternalDataType())) - return addErrorEntry("GeometryBinding::setInputBuffer failed, array resource type does not match input data type"); - - // data field index on low level scene is indexed starting after reserved slot for indices - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex()) + IndicesDataFieldIndex + 1u); - getIScene().setDataResource(m_attributeInstance, dataField, bufferResource.getLowlevelResourceHash(), ramses_internal::DataBufferHandle::Invalid(), instancingDivisor, offset, stride); - - return StatusOK; - } - - status_t GeometryBindingImpl::setInputBuffer(const EffectInputImpl& input, const ArrayBufferImpl& dataBuffer, uint32_t instancingDivisor, uint16_t offset, uint16_t stride) - { - if (!isFromTheSameSceneAs(dataBuffer)) - { - return addErrorEntry("GeometryBinding::setInputBuffer failed, dataBuffer is not from the same scene as the GeometryBinding."); - } - - if (!DataTypeUtils::IsValidVerticesType(dataBuffer.getDataType())) - { - return addErrorEntry("GeometryBinding::setInputBuffer failed, arrayBuffer is not of valid data type."); - } - - if (input.getEffectHash() != m_effectImpl->getLowlevelResourceHash()) - { - return addErrorEntry("GeometryBinding::setInputBuffer failed, input is not properly initialized or cannot be used with this geometry binding."); - } - - const ramses_internal::DataBufferHandle dataBufferHandle = dataBuffer.getDataBufferHandle(); - const ramses_internal::EDataType dataBufferDataType = getIScene().getDataBuffer(dataBufferHandle).dataType; - - if ((offset > 0 || stride > 0) && dataBuffer.getDataType() != EDataType::ByteBlob) - return addErrorEntry("GeometryBinding::setInputBuffer failed, custom stride/offset can be used only with data buffers of type byte blob"); - - if (!dataTypeMatchesInputType(dataBufferDataType, input.getInternalDataType())) - return addErrorEntry("GeometryBinding::setInputBuffer failed, vertex data buffer type does not match input data type"); - - // data field index on low level scene is indexed starting after reserved slot for indices - const ramses_internal::DataFieldHandle dataField(static_cast(input.getInputIndex()) + IndicesDataFieldIndex + 1u); - getIScene().setDataResource(m_attributeInstance, dataField, ramses_internal::ResourceContentHash::Invalid(), dataBufferHandle, instancingDivisor, offset, stride); - - return StatusOK; - } - - bool GeometryBindingImpl::dataTypeMatchesInputType(ramses_internal::EDataType resourceType, ramses_internal::EDataType inputDataType) - { - switch (resourceType) - { - case ramses_internal::EDataType::UInt16: - case ramses_internal::EDataType::UInt32: - return inputDataType == ramses_internal::EDataType::Indices; - case ramses_internal::EDataType::ByteBlob: - return ramses_internal::IsBufferDataType(inputDataType); - case ramses_internal::EDataType::Float: - return inputDataType == ramses_internal::EDataType::FloatBuffer; - case ramses_internal::EDataType::Vector2F: - return inputDataType == ramses_internal::EDataType::Vector2Buffer; - case ramses_internal::EDataType::Vector3F: - return inputDataType == ramses_internal::EDataType::Vector3Buffer; - case ramses_internal::EDataType::Vector4F: - return inputDataType == ramses_internal::EDataType::Vector4Buffer; - default: - return false; - } - } - - const Effect& GeometryBindingImpl::getEffect() const - { - return RamsesObjectTypeUtils::ConvertTo(m_effectImpl->getRamsesObject()); - } -} diff --git a/client/ramses-client/impl/GeometryBindingImpl.h b/client/ramses-client/impl/GeometryBindingImpl.h deleted file mode 100644 index 400fadf4f..000000000 --- a/client/ramses-client/impl/GeometryBindingImpl.h +++ /dev/null @@ -1,79 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_GEOMETRYBINDINGIMPL_H -#define RAMSES_GEOMETRYBINDINGIMPL_H - -// internal -#include "SceneObjectImpl.h" -#include "ArrayResourceImpl.h" - -// ramses framework -#include "SceneAPI/EDataType.h" - -#include - -namespace ramses_internal -{ - class IScene; -} - -namespace ramses -{ - class ResourceImpl; - class EffectImpl; - class AttributeInput; - class ArrayBufferImpl; - class ArrayResourceImpl; - - class GeometryBindingImpl final : public SceneObjectImpl - { - public: - GeometryBindingImpl(SceneImpl& scene, std::string_view name); - ~GeometryBindingImpl() override; - - void initializeFrameworkData(const EffectImpl& effect); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - status_t setInputBuffer(const EffectInputImpl& input, const ArrayResourceImpl& bufferResource, uint32_t instancingDivisor, uint16_t offset, uint16_t stride); - status_t setInputBuffer(const EffectInputImpl& input, const ArrayBufferImpl& dataBuffer, uint32_t instancingDivisor, uint16_t offset, uint16_t stride); - status_t setIndices(const ArrayResourceImpl& arrayResource); - status_t setIndices(const ArrayBufferImpl& dataBuffer); - - ramses_internal::ResourceContentHash getEffectHash() const; - ramses_internal::DataLayoutHandle getAttributeDataLayout() const; - ramses_internal::DataInstanceHandle getAttributeDataInstance() const; - uint32_t getIndicesCount() const; - - const Effect& getEffect() const; - - static const uint32_t IndicesDataFieldIndex = 0u; - - private: - void createDataLayout(); - - status_t validateEffect() const; - status_t validateAttribute() const; - status_t validateResource(ramses_internal::ResourceContentHash resourceHash) const; - status_t validateDataBuffer(ramses_internal::DataBufferHandle dataBuffer, ramses_internal::EDataType fieldDataType) const; - ArrayBufferImpl* findDataBuffer(ramses_internal::DataBufferHandle dataBufferHandle) const; - - static bool dataTypeMatchesInputType(ramses_internal::EDataType resourceType, ramses_internal::EDataType inputDataType); - - const EffectImpl* m_effectImpl; - ramses_internal::DataLayoutHandle m_attributeLayout; - ramses_internal::DataInstanceHandle m_attributeInstance; - uint32_t m_indicesCount; - }; -} - -#endif diff --git a/client/ramses-client/impl/MeshNodeImpl.h b/client/ramses-client/impl/MeshNodeImpl.h deleted file mode 100644 index 4138a63d9..000000000 --- a/client/ramses-client/impl/MeshNodeImpl.h +++ /dev/null @@ -1,76 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_MESHNODEIMPL_H -#define RAMSES_MESHNODEIMPL_H - -// internal -#include "NodeImpl.h" - -#include - -namespace ramses_internal -{ - class ClientApplicationLogic; -} - -namespace ramses -{ - class Appearance; - class GeometryBinding; - class GeometryBindingImpl; - class AppearanceImpl; - - class MeshNodeImpl final : public NodeImpl - { - public: - MeshNodeImpl(SceneImpl& scene, std::string_view nodeName); - ~MeshNodeImpl() override; - - void initializeFrameworkData(); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - status_t setAppearance(AppearanceImpl& appearanceImpl); - status_t setGeometryBinding(GeometryBindingImpl& geometryImpl); - status_t removeAppearanceAndGeometry(); - status_t setStartIndex(uint32_t startIndex); - uint32_t getStartIndex() const; - status_t setIndexCount(uint32_t indexCount); - uint32_t getIndexCount() const; - status_t setFlattenedVisibility(EVisibilityMode mode); - EVisibilityMode getFlattenedVisibility() const; - status_t setInstanceCount(uint32_t instanceCount); - uint32_t getInstanceCount() const; - status_t setStartVertex(uint32_t startVertex); - uint32_t getStartVertex() const; - - ramses_internal::RenderableHandle getRenderableHandle() const; - - const AppearanceImpl* getAppearanceImpl() const; - const Appearance* getAppearance() const; - Appearance* getAppearance(); - - const GeometryBindingImpl* getGeometryBindingImpl() const; - const GeometryBinding* getGeometryBinding() const; - GeometryBinding* getGeometryBinding(); - - private: - static bool AreGeometryAndAppearanceCompatible(const GeometryBindingImpl& geometry, const AppearanceImpl& appearance); - - ramses_internal::RenderableHandle m_renderableHandle; - - const AppearanceImpl* m_appearanceImpl; - const GeometryBindingImpl* m_geometryImpl; - }; -} - -#endif diff --git a/client/ramses-client/impl/NodeImpl.h b/client/ramses-client/impl/NodeImpl.h deleted file mode 100644 index 7af4f3672..000000000 --- a/client/ramses-client/impl/NodeImpl.h +++ /dev/null @@ -1,108 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_NODEIMPL_H -#define RAMSES_NODEIMPL_H - -// internal -#include "SceneObjectImpl.h" -#include "ramses-client-api/EVisibilityMode.h" -#include "ramses-client-api/ERotationType.h" -#include "DataTypesImpl.h" - -// ramses framework -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/ERotationType.h" -#include "Collections/Vector.h" - -#include - -namespace ramses -{ - class SceneImpl; - class Node; - - class NodeImpl : public SceneObjectImpl - { - public: - NodeImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view nodeName); - ~NodeImpl() override; - - void initializeFrameworkData(); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - - status_t addChild(NodeImpl& childNode); - status_t removeChild(NodeImpl& node); - status_t removeParent(); - status_t setParent(NodeImpl& parentNode); - status_t removeAllChildren(); - - bool hasChild() const; - size_t getChildCount() const; - Node* getChild(size_t index); - const Node* getChild(size_t index) const; - - NodeImpl& getChildImpl(size_t index); - const NodeImpl& getChildImpl(size_t index) const; - - bool hasParent() const; - Node* getParent(); - const Node* getParent() const; - NodeImpl* getParentImpl(); - const NodeImpl* getParentImpl() const; - - status_t getModelMatrix(glm::mat4x4& modelMatrix) const; - status_t getInverseModelMatrix(glm::mat4x4& inverseModelMatrix) const; - - status_t translate(const vec3f& translation); - status_t setTranslation(const vec3f& translation); - status_t getTranslation(vec3f& translation) const; - status_t setRotation(const vec3f& rotation, ERotationType rotationType); - ERotationType getRotationType() const; - status_t getRotation(vec3f& rotation) const; - status_t setRotation(const quat& rotation); - status_t getRotation(quat& rotation) const; - status_t scale(const vec3f& scaling); - status_t setScaling(const vec3f& scaling); - status_t getScaling(vec3f& scaling) const; - - status_t setVisibility(EVisibilityMode mode); - EVisibilityMode getVisibility() const; - - void initializeTransform(); - ramses_internal::TransformHandle getTransformHandle() const; - - ramses_internal::SceneId getSceneId() const; - ramses_internal::NodeHandle getNodeHandle() const; - - void markDirty(); - bool isDirty() const; - - private: - using NodeVector = std::vector; - - void removeChildInternally(NodeVector::iterator childIt); - status_t setRotationInternal(glm::vec4&& rotation, ramses_internal::ERotationType rotationType); - - ramses_internal::NodeHandle m_nodeHandle; - - NodeVector m_children; - NodeImpl* m_parent; - - ramses_internal::TransformHandle m_transformHandle; - - //The actual visibility - EVisibilityMode m_visibilityMode; - }; -} - -#endif diff --git a/client/ramses-client/impl/PickableObjectImpl.cpp b/client/ramses-client/impl/PickableObjectImpl.cpp deleted file mode 100644 index ec317e2ed..000000000 --- a/client/ramses-client/impl/PickableObjectImpl.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "PickableObjectImpl.h" -#include "ArrayBufferImpl.h" -#include "CameraNodeImpl.h" -#include "SceneAPI/PickableObject.h" -#include "SceneAPI/SceneTypes.h" -#include "ramses-client-api/Camera.h" -#include "Scene/ClientScene.h" -#include "SerializationContext.h" -#include "RamsesObjectTypeUtils.h" -#include "Scene/Scene.h" -#include "ramses-client-api/ArrayBuffer.h" - -namespace ramses -{ - PickableObjectImpl::PickableObjectImpl(SceneImpl& scene, std::string_view pickableObjectName) - : NodeImpl(scene, ERamsesObjectType::PickableObject, pickableObjectName) - { - } - - void PickableObjectImpl::initializeFrameworkData(const ArrayBufferImpl& geometryBuffer, pickableObjectId_t id) - { - NodeImpl::initializeFrameworkData(); - - assert(!m_pickableObjectHandle.isValid()); - m_geometryBufferImpl = &geometryBuffer; - - const ramses_internal::DataBufferHandle geometryBufferHandle = geometryBuffer.getDataBufferHandle(); - m_pickableObjectHandle = getIScene().allocatePickableObject(geometryBufferHandle, this->getNodeHandle(), ramses_internal::PickableObjectId{ id.getValue() }); - } - - void PickableObjectImpl::deinitializeFrameworkData() - { - assert(m_pickableObjectHandle.isValid()); - getIScene().releasePickableObject(m_pickableObjectHandle); - m_pickableObjectHandle = ramses_internal::PickableObjectHandle::Invalid(); - - NodeImpl::deinitializeFrameworkData(); - } - - status_t PickableObjectImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(NodeImpl::serialize(outStream, serializationContext)); - - outStream << m_pickableObjectHandle; - assert(m_geometryBufferImpl != nullptr); - - outStream << serializationContext.getIDForObject(m_geometryBufferImpl); - if (m_cameraImpl != nullptr) - outStream << serializationContext.getIDForObject(m_cameraImpl); - else - outStream << SerializationContext::GetObjectIDNull(); - return StatusOK; - } - - status_t PickableObjectImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(NodeImpl::deserialize(inStream, serializationContext)); - - inStream >> m_pickableObjectHandle; - - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_geometryBufferImpl); - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_cameraImpl); - serializationContext.addForDependencyResolve(this); - return StatusOK; - } - - status_t PickableObjectImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(NodeImpl::resolveDeserializationDependencies(serializationContext)); - - serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_geometryBufferImpl); - serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_cameraImpl); - - return StatusOK; - } - - status_t PickableObjectImpl::validate() const - { - status_t status = NodeImpl::validate(); - - const ramses_internal::PickableObject& pickableObject = getIScene().getPickableObject(m_pickableObjectHandle); - - if (!getIScene().isDataBufferAllocated(pickableObject.geometryHandle)) - status = addValidationMessage(EValidationSeverity::Error, "pickable object references a deleted geometry buffer"); - else - status = std::max(status, addValidationOfDependentObject(*m_geometryBufferImpl)); - - if (!pickableObject.cameraHandle.isValid()) - status = std::max(status, addValidationMessage(EValidationSeverity::Warning, "pickable object references no camera, a valid camera must be set")); - else if (!getIScene().isCameraAllocated(pickableObject.cameraHandle)) - status = addValidationMessage(EValidationSeverity::Error, "pickable object references a deleted camera"); - - return status; - } - - const ArrayBuffer& PickableObjectImpl::getGeometryBuffer() const - { - assert(nullptr != m_geometryBufferImpl); - return RamsesObjectTypeUtils::ConvertTo(m_geometryBufferImpl->getRamsesObject()); - } - - status_t PickableObjectImpl::setCamera(const CameraNodeImpl& cameraImpl) - { - if (!isFromTheSameSceneAs(cameraImpl)) - { - return addErrorEntry("PickableObject::setCamera failed - camera is not from the same scene as this PickableObject"); - } - - const status_t cameraValidity = cameraImpl.validate(); - if (StatusOK == cameraValidity) - { - m_cameraImpl = &cameraImpl; - getIScene().setPickableObjectCamera(m_pickableObjectHandle, cameraImpl.getCameraHandle()); - } - else - { - std::string str = - "PickableObject::setCamera failed - camera is not valid, maybe camera was not initialized:\n"; - str += cameraImpl.getValidationReport(EValidationSeverity::Warning); - return addErrorEntry(str); - } - - return cameraValidity; - } - - const Camera* PickableObjectImpl::getCamera() const - { - return (m_cameraImpl ? &RamsesObjectTypeUtils::ConvertTo(m_cameraImpl->getRamsesObject()) : nullptr); - } - - status_t PickableObjectImpl::setPickableObjectId(pickableObjectId_t id) - { - getIScene().setPickableObjectId(m_pickableObjectHandle, ramses_internal::PickableObjectId(id.getValue())); - return StatusOK; - } - - pickableObjectId_t PickableObjectImpl::getPickableObjectId() const - { - return pickableObjectId_t(getIScene().getPickableObject(m_pickableObjectHandle).id.getValue()); - } - - status_t PickableObjectImpl::setEnabled(bool enable) - { - getIScene().setPickableObjectEnabled(m_pickableObjectHandle, enable); - return StatusOK; - } - - bool PickableObjectImpl::isEnabled() const - { - return getIScene().getPickableObject(m_pickableObjectHandle).isEnabled; - } - - ramses_internal::PickableObjectHandle PickableObjectImpl::getPickableObjectHandle() const - { - return m_pickableObjectHandle; - } -} - diff --git a/client/ramses-client/impl/PickableObjectImpl.h b/client/ramses-client/impl/PickableObjectImpl.h deleted file mode 100644 index 9d6736d14..000000000 --- a/client/ramses-client/impl/PickableObjectImpl.h +++ /dev/null @@ -1,58 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_PICKABLEOBJECTIMPL_H -#define RAMSES_PICKABLEOBJECTIMPL_H - -#include "SceneObjectImpl.h" -#include "SceneAPI/Handles.h" -#include "NodeImpl.h" - -#include - -namespace ramses -{ - class ArrayBufferImpl; - class ArrayBuffer; - class CameraNodeImpl; - class Camera; - - class PickableObjectImpl final : public NodeImpl - { - public: - PickableObjectImpl(SceneImpl& scene, std::string_view pickableObjectName); - ~PickableObjectImpl() override = default; - - void initializeFrameworkData(const ArrayBufferImpl& geometryBuffer, pickableObjectId_t id); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - const ArrayBuffer& getGeometryBuffer() const; - - status_t setCamera(const CameraNodeImpl& cameraImpl); - const Camera* getCamera() const; - - status_t setPickableObjectId(pickableObjectId_t id); - pickableObjectId_t getPickableObjectId() const; - - status_t setEnabled(bool enable); - bool isEnabled() const; - - ramses_internal::PickableObjectHandle getPickableObjectHandle() const; - - private: - ramses_internal::PickableObjectHandle m_pickableObjectHandle; - const ArrayBufferImpl* m_geometryBufferImpl = nullptr; - const CameraNodeImpl* m_cameraImpl = nullptr; - }; -} - -#endif diff --git a/client/ramses-client/impl/RamsesClientImpl.cpp b/client/ramses-client/impl/RamsesClientImpl.cpp deleted file mode 100644 index 443ebabb0..000000000 --- a/client/ramses-client/impl/RamsesClientImpl.cpp +++ /dev/null @@ -1,836 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Resource.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-sdk-build-config.h" - -// internal -#include "SceneImpl.h" -#include "SceneConfigImpl.h" -#include "ResourceImpl.h" -#include "RamsesFrameworkImpl.h" -#include "RamsesClientImpl.h" -#include "RamsesObjectRegistryIterator.h" -#include "SerializationHelper.h" -#include "RamsesVersion.h" -#include "SceneReferenceImpl.h" - -// framework -#include "SceneAPI/SceneCreationInformation.h" -#include "Scene/ScenePersistation.h" -#include "Scene/ClientScene.h" -#include "Components/ResourcePersistation.h" -#include "Components/ManagedResource.h" -#include "Components/ResourceTableOfContents.h" -#include "Components/FileInputStreamContainer.h" -#include "Components/MemoryInputStreamContainer.h" -#include "Components/OffsetFileInputStreamContainer.h" -#include "Resource/IResource.h" -#include "ClientCommands/PrintSceneList.h" -#include "ClientCommands/FlushSceneVersion.h" -#include "SerializationContext.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/LogContext.h" -#include "Utils/File.h" -#include "Collections/IInputStream.h" -#include "Collections/HashMap.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" -#include "ClientFactory.h" -#include "FrameworkFactoryRegistry.h" -#include "Ramsh/Ramsh.h" -#include "DataTypeUtils.h" -#include "Resource/ArrayResource.h" -#include "Resource/EffectResource.h" -#include "Resource/TextureResource.h" -#include "glslEffectBlock/GlslEffect.h" -#include "EffectDescriptionImpl.h" -#include "TextureUtils.h" - -#include "PlatformAbstraction/PlatformTypes.h" -#include - -namespace ramses -{ - static const bool clientRegisterSuccess = ClientFactory::RegisterClientFactory(); - - RamsesClientImpl::RamsesClientImpl(RamsesFrameworkImpl& framework, std::string_view applicationName) - : RamsesObjectImpl(ERamsesObjectType::Client, applicationName) - , m_appLogic(framework.getParticipantAddress().getParticipantId(), framework.getFrameworkLock()) - , m_sceneFactory() - , m_framework(framework) - , m_loadFromFileTaskQueue(framework.getTaskQueue()) - , m_deleteSceneQueue(framework.getTaskQueue()) - { - assert(!framework.isConnected()); - - m_appLogic.init(framework.getResourceComponent(), framework.getScenegraphComponent()); - m_cmdPrintSceneList = std::make_shared(*this); - m_cmdPrintValidation = std::make_shared(*this); - m_cmdFlushSceneVersion = std::make_shared(*this); - m_cmdDumpSceneToFile = std::make_shared(*this); - m_cmdLogResourceMemoryUsage = std::make_shared(*this); - framework.getRamsh().add(m_cmdPrintSceneList); - framework.getRamsh().add(m_cmdPrintValidation); - framework.getRamsh().add(m_cmdFlushSceneVersion); - framework.getRamsh().add(m_cmdDumpSceneToFile); - framework.getRamsh().add(m_cmdLogResourceMemoryUsage); - m_framework.getPeriodicLogger().registerPeriodicLogSupplier(&m_framework.getScenegraphComponent()); - } - - RamsesClientImpl::~RamsesClientImpl() - { - LOG_INFO(CONTEXT_CLIENT, "RamsesClientImpl::~RamsesClientImpl"); - m_deleteSceneQueue.disableAcceptingTasksAfterExecutingCurrentQueue(); - m_loadFromFileTaskQueue.disableAcceptingTasksAfterExecutingCurrentQueue(); - - // delete async loaded scenes that were never collected via calling dispatchEvents - ramses_internal::PlatformGuard g(m_clientLock); - m_asyncSceneLoadStatusVec.clear(); - - LOG_INFO(CONTEXT_CLIENT, "RamsesClientImpl::~RamsesClientImpl deleting scenes"); - m_scenes.clear(); - - m_framework.getPeriodicLogger().removePeriodicLogSupplier(&m_framework.getScenegraphComponent()); - } - - void RamsesClientImpl::setHLObject(RamsesClient* hlClient) - { - assert(hlClient); - m_hlClient = hlClient; - } - - - void RamsesClientImpl::deinitializeFrameworkData() - { - } - - const ramses_internal::ClientApplicationLogic& RamsesClientImpl::getClientApplication() const - { - return m_appLogic; - } - - ramses_internal::ClientApplicationLogic& RamsesClientImpl::getClientApplication() - { - return m_appLogic; - } - - Scene* RamsesClientImpl::createScene(sceneId_t sceneId, const SceneConfigImpl& sceneConfig, std::string_view name) - { - if (!sceneId.isValid()) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::createScene: invalid sceneId"); - return nullptr; - } - - ramses_internal::PlatformGuard g(m_clientLock); - ramses_internal::SceneInfo sceneInfo; - sceneInfo.friendlyName = name; - sceneInfo.sceneID = ramses_internal::SceneId(sceneId.getValue()); - - ramses_internal::ClientScene* internalScene = m_sceneFactory.createScene(sceneInfo); - if (nullptr == internalScene) - { - return nullptr; - } - - auto impl = std::make_unique(*internalScene, sceneConfig, *m_hlClient); - impl->initializeFrameworkData(); - m_scenes.push_back(SceneOwningPtr{ new Scene{ std::move(impl) }, [](Scene* s) { delete s; } }); - - return m_scenes.back().get(); - } - - RamsesClientImpl::DeleteSceneRunnable::DeleteSceneRunnable(SceneOwningPtr scene, InternalSceneOwningPtr llscene) - : m_scene{ std::move(scene) } - , m_lowLevelScene{ std::move(llscene) } - { - } - - void RamsesClientImpl::DeleteSceneRunnable::execute() - { - m_scene.reset(); - m_lowLevelScene.reset(); - } - - status_t RamsesClientImpl::destroy(Scene& scene) - { - ramses_internal::PlatformGuard g(m_clientLock); - auto iter = std::find_if(m_scenes.begin(), m_scenes.end(), [&scene](auto& s) { return s.get() == &scene; }); - if (iter != m_scenes.end()) - { - auto sceneOwnPtr = std::move(*iter); - m_scenes.erase(iter); - - const ramses_internal::SceneId sceneID(scene.getSceneId().getValue()); - auto llscene = m_sceneFactory.releaseScene(sceneID); - - getClientApplication().removeScene(sceneID); - - scene.m_impl.closeSceneFile(); - auto task = new DeleteSceneRunnable(std::move(sceneOwnPtr), std::move(llscene)); - m_deleteSceneQueue.enqueue(*task); - task->release(); - - return StatusOK; - } - - return addErrorEntry("RamsesClient::destroy failed, scene is not in this client."); - } - - void RamsesClientImpl::writeLowLevelResourcesToStream(const ResourceObjects& resources, ramses_internal::BinaryFileOutputStream& resourceOutputStream, bool compress) const - { - //getting names for resources (names are transmitted only for debugging purposes) - ramses_internal::ManagedResourceVector managedResources; - managedResources.reserve(resources.size()); - for (const auto res : resources) - { - assert(res != nullptr); - const ramses_internal::ResourceContentHash& hash = res->m_impl.getLowlevelResourceHash(); - const ramses_internal::ManagedResource managedRes = getClientApplication().getResource(hash); - if (managedRes) - { - managedResources.push_back(managedRes); - } - else - { - const ramses_internal::ManagedResource loadedResource = getClientApplication().loadResource(hash); - assert(loadedResource); - managedResources.push_back(loadedResource); - } - } - - // sort resources by hash to maintain a deterministic order in which we write them to file, remove duplicates - std::sort(managedResources.begin(), managedResources.end(), [](auto const& a, auto const& b) { return a->getHash() < b->getHash(); }); - managedResources.erase(std::unique(managedResources.begin(), managedResources.end()), managedResources.end()); - - // write LL-TOC and LL resources - ramses_internal::ResourcePersistation::WriteNamedResourcesWithTOCToStream(resourceOutputStream, managedResources, compress); - } - - ramses_internal::ManagedResource RamsesClientImpl::getResource(ramses_internal::ResourceContentHash hash) const - { - return m_appLogic.getResource(hash); - } - - SceneOwningPtr RamsesClientImpl::loadSceneObjectFromStream(const std::string& caller, - std::string const& filename, - ramses_internal::IInputStream& inputStream, - bool localOnly, - sceneId_t sceneId) - { - LOG_TRACE(ramses_internal::CONTEXT_CLIENT, "RamsesClient::prepareSceneFromInputStream: start loading scene from input stream"); - - ramses_internal::SceneCreationInformation createInfo; - ramses_internal::ScenePersistation::ReadSceneMetadataFromStream(inputStream, createInfo); - if (sceneId.isValid()) - { - const auto newSceneId = ramses_internal::SceneId(sceneId.getValue()); - LOG_INFO_P(ramses_internal::CONTEXT_CLIENT, "RamsesClient::{}: Override stored scene id: {} with user provided scene id: {}", caller, createInfo.m_id, newSceneId); - createInfo.m_id = newSceneId; - } - const ramses_internal::SceneSizeInformation& sizeInformation = createInfo.m_sizeInfo; - const ramses_internal::SceneInfo sceneInfo(createInfo.m_id, createInfo.m_name); - - LOG_DEBUG(ramses_internal::CONTEXT_CLIENT, "RamsesClient::prepareSceneFromInputStream: scene to be loaded has " << sizeInformation); - - ramses_internal::ClientScene* internalScene = nullptr; - { - ramses_internal::PlatformGuard g(m_clientLock); - internalScene = m_sceneFactory.createScene(sceneInfo); - } - if (nullptr == internalScene) - { - return nullptr; - } - internalScene->preallocateSceneSize(sizeInformation); - - // need first to create the pimpl, so that internal framework components know the new scene - SceneConfigImpl sceneConfig; - if (localOnly) - { - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RamsesClient::" << caller << ": Mark file loaded from " << filename << " with sceneId " << createInfo.m_id << " as local only"); - sceneConfig.setPublicationMode(EScenePublicationMode::LocalOnly); - } - - auto impl = std::make_unique(*internalScene, sceneConfig, *m_hlClient); - - // now the scene is registered, so it's possible to load the low level content into the scene - LOG_TRACE(ramses_internal::CONTEXT_CLIENT, " Reading low level scene from stream"); - ramses_internal::ScenePersistation::ReadSceneFromStream(inputStream, *internalScene); - - LOG_TRACE(ramses_internal::CONTEXT_CLIENT, " Deserializing high level scene objects from stream"); - DeserializationContext deserializationContext; - SerializationHelper::DeserializeObjectID(inputStream); - const auto stat = impl->deserialize(inputStream, deserializationContext); - if (stat != StatusOK) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, " Failed to deserialize high level scene:"); - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, getStatusMessage(stat)); - return nullptr; - } - - LOG_TRACE(ramses_internal::CONTEXT_CLIENT, " Done with preparing scene from input stream."); - - return SceneOwningPtr{ new Scene{ std::move(impl) }, [](Scene* s) { delete s; } }; - } - - SceneOwningPtr RamsesClientImpl::loadSceneFromCreationConfig(const SceneCreationConfig& cconfig) - { - // this stream contains scene data AND resource data and will be handed over to and held open by resource component as resource stream - ramses_internal::IInputStream& inputStream = cconfig.streamContainer->getStream(); - if (inputStream.getState() != ramses_internal::EStatus::Ok) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::" << cconfig.caller << ": failed to open scene source " << cconfig.dataSource); - return nullptr; - } - - if (!ReadRamsesVersionAndPrintWarningOnMismatch(inputStream, "scene file", getFramework().getFeatureLevel())) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::" << cconfig.caller << ": failed to read from scene source " << cconfig.dataSource); - return nullptr; - } - - uint64_t sceneObjectStart = 0; - uint64_t llResourceStart = 0; - inputStream >> sceneObjectStart; - inputStream >> llResourceStart; - - SceneOwningPtr scene; - if (cconfig.prefetchData) - { - std::vector sceneData(static_cast(llResourceStart - sceneObjectStart)); - inputStream.read(sceneData.data(), sceneData.size()); - - if (inputStream.getState() != ramses_internal::EStatus::Ok) - { - LOG_ERROR_P(ramses_internal::CONTEXT_CLIENT, "RamsesClient::{}: Failed reading scene from file: {} ", cconfig.caller, inputStream.getState()); - return nullptr; - } - - ramses_internal::BinaryInputStream sceneDataStream(sceneData.data()); - scene = loadSceneObjectFromStream(cconfig.caller, cconfig.dataSource, sceneDataStream, cconfig.localOnly, cconfig.sceneId); - } - else - { - // this path will be used in the future when creating scene from user provided stream - scene = loadSceneObjectFromStream(cconfig.caller, cconfig.dataSource, inputStream, cconfig.localOnly, cconfig.sceneId); - } - if (!scene) - { - LOG_ERROR_P(ramses_internal::CONTEXT_CLIENT, "RamsesClient::{}: scene creation for '{}' failed", cconfig.caller, cconfig.dataSource); - return nullptr; - } - - // calls on m_appLogic are thread safe - // register stream for on-demand resource loading (LL-Resources) - ramses_internal::ResourceTableOfContents loadedTOC; - loadedTOC.readTOCPosAndTOCFromStream(inputStream); - const ramses_internal::SceneFileHandle fileHandle = m_appLogic.addResourceFile(cconfig.streamContainer, loadedTOC); - scene->m_impl.setSceneFileHandle(fileHandle); - - LOG_INFO_P(CONTEXT_CLIENT, "RamsesClient::{}: Source '{}' has handle {}", cconfig.caller, cconfig.dataSource, fileHandle); - - return scene; - } - - Scene* RamsesClientImpl::loadSceneFromFile(std::string_view fileName, bool localOnly) - { - if (fileName.empty()) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFile: filename may not be empty"); - return nullptr; - } - - return loadSceneSynchonousCommon({ - "loadSceneFromFile", - std::string{fileName}, - std::make_shared(fileName), - true, - localOnly, - sceneId_t(), - }); - } - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Scene* RamsesClientImpl::loadSceneFromMemory(std::unique_ptr data, size_t size, bool localOnly) - { - if (!data) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromMemory: data may not be null"); - return nullptr; - } - if (size == 0) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromMemory: size may not be 0"); - return nullptr; - } - - return loadSceneSynchonousCommon({ - "loadSceneFromMemory", - fmt::format("", size), - std::make_shared(std::move(data)), - false, - localOnly, - sceneId_t(), - }); - } - - Scene* RamsesClientImpl::loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, bool localOnly) - { - if (fd <= 0) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: filedescriptor must be valid " << fd); - return nullptr; - } - if (length == 0) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: length may not be 0"); - return nullptr; - } - - return loadSceneSynchonousCommon(SceneCreationConfig{ - "loadSceneFromFileDescriptor", - fmt::format("", fd, offset, length), - std::make_shared(fd, offset, length), - true, - localOnly, - sceneId_t() - }); - } - - Scene* RamsesClientImpl::loadSceneFromFileDescriptor(sceneId_t sceneId, int fd, size_t offset, size_t length, bool localOnly) - { - if (fd <= 0) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: filedescriptor must be valid " << fd); - return nullptr; - } - if (length == 0) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: length may not be 0"); - return nullptr; - } - if (!sceneId.isValid()) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: invalid sceneId"); - return nullptr; - } - - return loadSceneSynchonousCommon(SceneCreationConfig{ - fmt::format("loadSceneFromFileDescriptor", sceneId), - fmt::format("", fd, offset, length), - std::make_shared(fd, offset, length), - true, - localOnly, - sceneId, - }); - } - - Scene* RamsesClientImpl::loadSceneSynchonousCommon(const SceneCreationConfig& cconfig) - { - const uint64_t start = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - auto scene = loadSceneFromCreationConfig(cconfig); - if (!scene) - return nullptr; - - auto* scenePtr = scene.get(); - finalizeLoadedScene(std::move(scene)); - - const uint64_t end = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RamsesClient::" << cconfig.caller << " Scene loaded from '" << cconfig.dataSource << "' in " << (end - start) << " ms"); - - return scenePtr; - } - - void RamsesClientImpl::finalizeLoadedScene(SceneOwningPtr scene) - { - // add to the known list of scenes - ramses_internal::PlatformGuard g(m_clientLock); - m_scenes.push_back(std::move(scene)); - } - - void RamsesClientImpl::WriteCurrentBuildVersionToStream(ramses_internal::IOutputStream& stream, EFeatureLevel featureLevel) - { - ramses_internal::RamsesVersion::WriteToStream(stream, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, featureLevel); - } - - bool RamsesClientImpl::GetFeatureLevelFromStream(ramses_internal::IInputStreamContainer& streamContainer, const std::string& desc, EFeatureLevel& detectedFeatureLevel) - { - ramses_internal::RamsesVersion::VersionInfo readVersion; - if (!ramses_internal::RamsesVersion::ReadFromStream(streamContainer.getStream(), readVersion, detectedFeatureLevel)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::GetFeatureLevelFromSceneFile: failed to read RAMSES version and feature level from '" << desc << "'."); - return false; - } - - return true; - } - - bool RamsesClientImpl::GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel) - { - ramses_internal::FileInputStreamContainer streamContainer{ fileName }; - return GetFeatureLevelFromStream(streamContainer, std::string{fileName}, detectedFeatureLevel); - } - - bool RamsesClientImpl::GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel) - { - if (fd <= 0) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::GetFeatureLevelFromFile: filedescriptor must be valid " << fd); - return false; - } - if (length == 0u) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::GetFeatureLevelFromFile: length may not be 0"); - return false; - } - - ramses_internal::OffsetFileInputStreamContainer streamContainer{ fd, offset, length }; - return GetFeatureLevelFromStream(streamContainer, fmt::format("fileDescriptor fd:{} offset:{} length:{}", fd, offset, length), detectedFeatureLevel); - } - - bool RamsesClientImpl::ReadRamsesVersionAndPrintWarningOnMismatch(ramses_internal::IInputStream& inputStream, std::string_view verboseFileName, EFeatureLevel featureLevel) - { - // return false on read error only, not version mismatch - ramses_internal::RamsesVersion::VersionInfo readVersion; - EFeatureLevel featureLevelFromFile = EFeatureLevel_01; - if (!ramses_internal::RamsesVersion::ReadFromStream(inputStream, readVersion, featureLevelFromFile)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::ReadRamsesVersionAndPrintWarningOnMismatch: failed to read RAMSES version for " << verboseFileName << ", file probably corrupt. Loading aborted."); - return false; - } - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RAMSES version in file '" << verboseFileName << "': [" << readVersion.versionString << "]; GitHash: [" << readVersion.gitHash << "]; FeatureLevel: [" << featureLevelFromFile << "];"); - - if (!ramses_internal::RamsesVersion::MatchesMajorMinor(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR_INT, ::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR_INT, readVersion)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::ReadRamsesVersionAndPrintWarningOnMismatch: Version of file " << verboseFileName << " does not match MAJOR.MINOR of this build. Cannot load the file."); - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "SDK version of loader: [" << ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION << "]; GitHash: [" << ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH << "]"); - return false; - } - - if (featureLevelFromFile != featureLevel) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::ReadRamsesVersionAndPrintWarningOnMismatch: Feature level of file '" << verboseFileName - << "' is " << featureLevelFromFile << " which does not match feature level of this Ramses instance (" << featureLevel << "). Cannot load the file."); - return false; - } - - return true; - } - - status_t RamsesClientImpl::loadSceneFromFileAsync(std::string_view fileName, bool localOnly) - { - const std::string stdFilename(fileName); - if (stdFilename.empty()) - return addErrorEntry("RamsesClient::loadSceneFromFileAsync: filename may not be empty"); - - LoadSceneRunnable* task = - new LoadSceneRunnable(*this, SceneCreationConfig{ - "loadSceneFromFileAsync", - stdFilename, - std::make_shared(stdFilename), - true, - localOnly, - sceneId_t() - }); - m_loadFromFileTaskQueue.enqueue(*task); - task->release(); - return StatusOK; - } - - SceneReference* RamsesClientImpl::findSceneReference(sceneId_t masterSceneId, sceneId_t referencedSceneId) - { - for (auto const& scene : getListOfScenes()) - { - if (masterSceneId == scene->getSceneId()) - return scene->m_impl.getSceneReference(referencedSceneId); - } - - return nullptr; - } - - status_t RamsesClientImpl::dispatchEvents(IClientEventHandler& clientEventHandler) - { - std::vector localAsyncSceneLoadStatus; - { - ramses_internal::PlatformGuard g(m_clientLock); - localAsyncSceneLoadStatus.swap(m_asyncSceneLoadStatusVec); - } - - for (auto& sceneStatus : localAsyncSceneLoadStatus) - { - if (sceneStatus.scene) - { - // finalize scene - Scene* scene = sceneStatus.scene.get(); - const uint64_t start = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - finalizeLoadedScene(std::move(sceneStatus.scene)); - const uint64_t end = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RamsesClient::dispatchEvents(sceneFileLoadSucceeded): Synchronous postprocessing of scene loaded from '" << - sceneStatus.sceneFilename << "' (sceneName: " << scene->getName() << ", sceneId " << scene->getSceneId() << ") in " << (end - start) << " ms"); - - clientEventHandler.sceneFileLoadSucceeded(sceneStatus.sceneFilename.c_str(), scene); - } - else - { - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RamsesClient::dispatchEvents(sceneFileLoadFailed): " << sceneStatus.sceneFilename); - clientEventHandler.sceneFileLoadFailed(sceneStatus.sceneFilename.c_str()); - } - } - - const auto clientRendererEvents = getClientApplication().popSceneReferenceEvents(); - for (const auto& rendererEvent : clientRendererEvents) - { - switch (rendererEvent.type) - { - case ramses_internal::SceneReferenceEventType::SceneStateChanged: - { - auto sr = findSceneReference(sceneId_t{ rendererEvent.masterSceneId.getValue() }, sceneId_t{ rendererEvent.referencedScene.getValue() }); - if (sr) - { - LOG_INFO(CONTEXT_CLIENT, "RamsesClient::dispatchEvents master:reference scene state changed: " - << rendererEvent.masterSceneId << ":" << rendererEvent.referencedScene << " " << EnumToString(rendererEvent.sceneState)); - - sr->m_impl.setReportedState(SceneReferenceImpl::GetSceneReferenceState(rendererEvent.sceneState)); - clientEventHandler.sceneReferenceStateChanged(*sr, SceneReferenceImpl::GetSceneReferenceState(rendererEvent.sceneState)); - } - else - LOG_WARN(CONTEXT_CLIENT, "RamsesClientImpl::dispatchEvents: did not find SceneReference for a SceneStateChanged event: " - << rendererEvent.masterSceneId << " " << rendererEvent.referencedScene << " " << EnumToString(rendererEvent.sceneState)); - break; - } - case ramses_internal::SceneReferenceEventType::SceneFlushed: - { - auto sr = findSceneReference(sceneId_t{ rendererEvent.masterSceneId.getValue() }, sceneId_t{ rendererEvent.referencedScene.getValue() }); - if (sr) - clientEventHandler.sceneReferenceFlushed(*sr, sceneVersionTag_t{ rendererEvent.tag.getValue() }); - else - LOG_WARN(CONTEXT_CLIENT, "RamsesClientImpl::dispatchEvents: did not find SceneReference for a SceneFlushed event: " - << rendererEvent.masterSceneId << " " << rendererEvent.referencedScene << " " << rendererEvent.tag); - break; - } - case ramses_internal::SceneReferenceEventType::DataLinked: - clientEventHandler.dataLinked(sceneId_t{ rendererEvent.providerScene.getValue() }, dataProviderId_t{ rendererEvent.dataProvider.getValue() }, - sceneId_t{ rendererEvent.consumerScene.getValue() }, dataConsumerId_t{ rendererEvent.dataConsumer.getValue() }, rendererEvent.status); - break; - case ramses_internal::SceneReferenceEventType::DataUnlinked: - clientEventHandler.dataUnlinked(sceneId_t{ rendererEvent.consumerScene.getValue() }, dataConsumerId_t{ rendererEvent.dataConsumer.getValue() }, rendererEvent.status); - break; - } - } - - return StatusOK; - } - - RamsesClientImpl::LoadSceneRunnable::LoadSceneRunnable(RamsesClientImpl& client, SceneCreationConfig&& cconfig) - : m_client(client) - , m_cconfig(std::move(cconfig)) - { - } - - void RamsesClientImpl::LoadSceneRunnable::execute() - { - const uint64_t start = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - auto scene = m_client.loadSceneFromCreationConfig(m_cconfig); - const uint64_t end = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - - if (scene) - { - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileAsync: Scene loaded from '" << m_cconfig.dataSource << - "' (sceneName: " << scene->getName() << ", sceneId " << scene->getSceneId() << ") in " << (end - start) << " ms"); - } - - ramses_internal::PlatformGuard g(m_client.m_clientLock); - // NOTE: only used for real files by name for now, not sure how to report other cases to user. - // therefore can assume dataSource is the filename - m_client.m_asyncSceneLoadStatusVec.push_back({std::move(scene), m_cconfig.dataSource}); - } - - const SceneVector& RamsesClientImpl::getListOfScenes() const - { - ramses_internal::PlatformGuard g(m_clientLock); - return m_scenes; - } - - ramses_internal::ResourceHashUsage RamsesClientImpl::getHashUsage_ThreadSafe(const ramses_internal::ResourceContentHash& hash) const - { - return m_appLogic.getHashUsage(hash); - } - - ramses_internal::ManagedResource RamsesClientImpl::getResource_ThreadSafe(ramses_internal::ResourceContentHash hash) const - { - return m_appLogic.getResource(hash); - } - - ramses_internal::ManagedResource RamsesClientImpl::loadResource_ThreadSafe(const ramses_internal::ResourceContentHash& hash) const - { - return m_appLogic.loadResource(hash); - } - - const Scene* RamsesClientImpl::findSceneByName(std::string_view name) const - { - ramses_internal::PlatformGuard g(m_clientLock); - for (const auto& scene : m_scenes) - if (scene->getName() == name) - return scene.get(); - - return nullptr; - } - - Scene* RamsesClientImpl::findSceneByName(std::string_view name) - { - // Non-const version of findObjectByName cast to its const version to avoid duplicating code - return const_cast((const_cast(*this)).findSceneByName(name)); - } - - const Scene* RamsesClientImpl::getScene(sceneId_t sceneId) const - { - ramses_internal::PlatformGuard g(m_clientLock); - for (const auto& scene : m_scenes) - if (scene->getSceneId() == sceneId) - return scene.get(); - - return nullptr; - } - - Scene* RamsesClientImpl::getScene(sceneId_t sceneId) - { - // Non-const version of findObjectByName cast to its const version to avoid duplicating code - return const_cast((const_cast(*this)).getScene(sceneId)); - } - - - ramses_internal::ManagedResource RamsesClientImpl::manageResource(const ramses_internal::IResource* res) - { - ramses_internal::ManagedResource managedRes = m_appLogic.addResource(res); - LOG_HL_CLIENT_API_STR("Created resource with internal hash " << managedRes->getHash() << ", name: " << managedRes->getName()); - - return managedRes; - } - - RamsesClientImpl& RamsesClientImpl::createImpl(std::string_view name, RamsesFrameworkImpl& components) - { - return *new RamsesClientImpl(components, name); - } - - RamsesFrameworkImpl& RamsesClientImpl::getFramework() - { - return m_framework; - } - - status_t RamsesClientImpl::validate() const - { - status_t status = RamsesObjectImpl::validate(); - - const status_t scenesStatus = validateScenes(); - if (StatusOK != scenesStatus) - status = scenesStatus; - - return status; - } - - status_t RamsesClientImpl::validateScenes() const - { - ramses_internal::PlatformGuard g(m_clientLock); - - status_t status = StatusOK; - for(const auto& scene : m_scenes) - { - const status_t sceneStatus = addValidationOfDependentObject(scene->m_impl); - if (StatusOK != sceneStatus) - status = sceneStatus; - } - - ramses_internal::StringOutputStream msg; - msg << "Contains " << m_scenes.size() << " scenes"; - addValidationMessage(EValidationSeverity::Info, msg.release()); - - return status; - } - - ramses_internal::ManagedResource RamsesClientImpl::createManagedArrayResource(uint32_t numElements, EDataType type, const void* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name) - { - if (0u == numElements || nullptr == arrayData) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClientImpl::createManagedArrayResource Array resource must have element count > 0 and data must not be nullptr!"); - return {}; - } - - ramses_internal::EDataType elementType = DataTypeUtils::ConvertDataTypeToInternal(type); - ramses_internal::EResourceType resourceType = DataTypeUtils::DeductResourceTypeFromDataType(type); - - auto resource = new ramses_internal::ArrayResource(resourceType, numElements, elementType, arrayData, ramses_internal::ResourceCacheFlag(cacheFlag.getValue()), name); - return manageResource(resource); - } - - template - ramses_internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses_internal::EResourceType textureType, - uint32_t width, uint32_t height, uint32_t depth, - ETextureFormat format, - uint32_t mipMapCount, const MipDataStorageType mipLevelData[], bool generateMipChain, // NOLINT(modernize-avoid-c-arrays) - const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name) - { - if (!TextureUtils::TextureParametersValid(width, height, depth, mipMapCount) || !TextureUtils::MipDataValid(width, height, depth, mipMapCount, mipLevelData, format)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::createTexture: invalid parameters"); - return {}; - } - - if (generateMipChain && (!FormatSupportsMipChainGeneration(format) || (mipMapCount > 1))) - { - LOG_WARN(ramses_internal::CONTEXT_CLIENT, "RamsesClient::createTexture: cannot auto generate mipmaps when custom mipmap data provided or unsupported format used"); - generateMipChain = false; - } - - ramses_internal::TextureMetaInfo texDesc; - texDesc.m_width = width; - texDesc.m_height = height; - texDesc.m_depth = depth; - texDesc.m_format = TextureUtils::GetTextureFormatInternal(format); - texDesc.m_generateMipChain = generateMipChain; - texDesc.m_swizzle = TextureUtils::GetTextureSwizzleInternal(swizzle); - TextureUtils::FillMipDataSizes(texDesc.m_dataSizes, mipMapCount, mipLevelData); - - ramses_internal::TextureResource* resource = new ramses_internal::TextureResource(textureType, texDesc, ramses_internal::ResourceCacheFlag(cacheFlag.getValue()), name); - TextureUtils::FillMipData(const_cast(resource->getResourceData().data()), mipMapCount, mipLevelData); - - return manageResource(resource); - } - template ramses_internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses_internal::EResourceType textureType, - uint32_t width, uint32_t height, uint32_t depth, - ETextureFormat format, - uint32_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, - const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name); - template ramses_internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses_internal::EResourceType textureType, - uint32_t width, uint32_t height, uint32_t depth, - ETextureFormat format, - uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, - const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name); - - ramses_internal::ManagedResource RamsesClientImpl::createManagedEffect(const EffectDescription& effectDesc, resourceCacheFlag_t cacheFlag, std::string_view name, std::string& errorMessages) - { - //create effect using vertex and fragment shaders - ramses_internal::GlslEffect effectBlock(effectDesc.getVertexShader(), effectDesc.getFragmentShader(), effectDesc.getGeometryShader(), effectDesc.m_impl.get().getCompilerDefines(), - effectDesc.m_impl.get().getSemanticsMap(), name); - errorMessages.clear(); - ramses_internal::EffectResource* effectResource = effectBlock.createEffectResource(ramses_internal::ResourceCacheFlag(cacheFlag.getValue())); - if (!effectResource) - { - errorMessages = effectBlock.getEffectErrorMessages(); - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesClient::createEffect Failed to create effect resource (name: '" << name << "') :\n " << effectBlock.getEffectErrorMessages()); - return {}; - } - return manageResource(effectResource); - } -} diff --git a/client/ramses-client/impl/RamsesClientImpl.h b/client/ramses-client/impl/RamsesClientImpl.h deleted file mode 100644 index dfb67e1a8..000000000 --- a/client/ramses-client/impl/RamsesClientImpl.h +++ /dev/null @@ -1,234 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESCLIENTIMPL_H -#define RAMSES_RAMSESCLIENTIMPL_H - -#include - -// client api -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/IClientEventHandler.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-client-api/Scene.h" - -// RAMSES framework -#include "ramses-framework-api/EFeatureLevel.h" -#include "Utils/LogContext.h" -#include "SceneFactory.h" -#include "ClientApplicationLogic.h" -#include "RamsesObjectImpl.h" -#include "RamsesObjectRegistry.h" -#include "ResourceObjects.h" -#include "Collections/Vector.h" -#include "RamsesObjectVector.h" -#include "ClientCommands/ValidateCommand.h" -#include "ClientCommands/DumpSceneToFile.h" -#include "ClientCommands/LogResourceMemoryUsage.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "TaskFramework/ITask.h" -#include "TaskFramework/EnqueueOnlyOneAtATimeQueue.h" -#include "TaskFramework/TaskForwardingQueue.h" -#include "Collections/HashMap.h" -#include "RamsesFrameworkTypesImpl.h" -#include "SceneImpl.h" - -#include -#include - -namespace ramses_internal -{ - class IInputStream; - class PrintSceneList; - class FlushSceneVersion; - class BinaryFileOutputStream; - class BinaryFileInputStream; - class ClientScene; -} - -namespace ramses -{ - class Effect; - class Texture3D; - class Texture2D; - class ArrayResource; - class Texture; - class TextureCube; - class EffectDescription; - class ArrayResourceImpl; - class Texture2DImpl; - class ResourceFileDescription; - class ResourceFileDescriptionSet; - class RamsesFrameworkImpl; - class ClientObjectImpl; - class SceneImpl; - class SceneConfigImpl; - class ResourceImpl; - class RamsesClient; - - using SceneOwningPtr = std::unique_ptr>; - using SceneVector = std::vector; - using InternalSceneOwningPtr = std::unique_ptr; - using ResourceVector = std::vector; // resources are owned by Scene's object registry - - class RamsesClientImpl final : public RamsesObjectImpl - { - public: - RamsesClientImpl(RamsesFrameworkImpl& ramsesFramework, std::string_view applicationName); - ~RamsesClientImpl() override; - - void setHLObject(RamsesClient* hlClient); - - void deinitializeFrameworkData() override; - - virtual ramses_internal::ManagedResource getResource(ramses_internal::ResourceContentHash hash) const; - template - const T* getResourceData(const ramses_internal::ResourceContentHash& hash) const - { - ramses_internal::ManagedResource managedResource = getResource(hash); - const ramses_internal::IResource* untypedResource = managedResource.get(); - return untypedResource->convertTo(); - } - const ramses_internal::ClientApplicationLogic& getClientApplication() const; - ramses_internal::ClientApplicationLogic& getClientApplication(); - - Scene* createScene(sceneId_t sceneId, const SceneConfigImpl& sceneConfig, std::string_view name); - status_t destroy(Scene& scene); - - Scene* loadSceneFromFile(std::string_view fileName, bool localOnly); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Scene* loadSceneFromMemory(std::unique_ptr data, size_t size, bool localOnly); - Scene* loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, bool localOnly); - Scene* loadSceneFromFileDescriptor(sceneId_t sceneId, int fd, size_t offset, size_t length, bool localOnly); - - status_t loadSceneFromFileAsync(std::string_view fileName, bool localOnly); - - status_t dispatchEvents(IClientEventHandler& clientEventHandler); - - const SceneVector& getListOfScenes() const; - const Scene* findSceneByName(std::string_view name) const; - Scene* findSceneByName(std::string_view name); - const Scene* getScene(sceneId_t sceneId) const; - Scene* getScene(sceneId_t sceneId); - - RamsesFrameworkImpl& getFramework(); - static RamsesClientImpl& createImpl(std::string_view name, RamsesFrameworkImpl& components); - - status_t validate() const override; - - template - void enqueueSceneCommand(sceneId_t sceneId, T cmd); - - // special wrappers for known thread safe function - ramses_internal::ResourceHashUsage getHashUsage_ThreadSafe(const ramses_internal::ResourceContentHash& hash) const; - ramses_internal::ManagedResource getResource_ThreadSafe(ramses_internal::ResourceContentHash hash) const; - ramses_internal::ManagedResource loadResource_ThreadSafe(const ramses_internal::ResourceContentHash& hash) const; - - SceneReference* findSceneReference(sceneId_t masterSceneId, sceneId_t referencedSceneId); - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - ramses_internal::ManagedResource createManagedArrayResource(uint32_t numElements, EDataType type, const void* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name); - template // NOLINTNEXTLINE(modernize-avoid-c-arrays) - ramses_internal::ManagedResource createManagedTexture(ramses_internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, uint32_t mipMapCount, const MipDataStorageType mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name); - ramses_internal::ManagedResource createManagedEffect(const EffectDescription& effectDesc, resourceCacheFlag_t cacheFlag, std::string_view name, std::string& errorMessages); - - void writeLowLevelResourcesToStream(const ResourceObjects& resources, ramses_internal::BinaryFileOutputStream& resourceOutputStream, bool compress) const; - static bool ReadRamsesVersionAndPrintWarningOnMismatch(ramses_internal::IInputStream& inputStream, std::string_view verboseFileName, EFeatureLevel featureLevel); - static void WriteCurrentBuildVersionToStream(ramses_internal::IOutputStream& stream, EFeatureLevel featureLevel); - static bool GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel); - static bool GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel); - - private: - struct SceneCreationConfig - { - std::string caller; - std::string dataSource; - ramses_internal::InputStreamContainerSPtr streamContainer; - bool prefetchData; - bool localOnly; - sceneId_t sceneId; - }; - - class LoadSceneRunnable : public ramses_internal::ITask - { - public: - LoadSceneRunnable(RamsesClientImpl& client, SceneCreationConfig&& cconfig); - void execute() override; - - private: - RamsesClientImpl& m_client; - SceneCreationConfig m_cconfig; - }; - - class DeleteSceneRunnable : public ramses_internal::ITask - { - public: - DeleteSceneRunnable(SceneOwningPtr scene, InternalSceneOwningPtr llscene); - void execute() override; - - private: - SceneOwningPtr m_scene; - InternalSceneOwningPtr m_lowLevelScene; - }; - - struct SceneLoadStatus - { - SceneOwningPtr scene; - std::string sceneFilename; - }; - - friend class LoadSceneRunnable; - - ramses_internal::ManagedResource manageResource(const ramses_internal::IResource* res); - - Scene* loadSceneSynchonousCommon(const SceneCreationConfig& cconf); - SceneOwningPtr loadSceneFromCreationConfig(const SceneCreationConfig& cconf); - SceneOwningPtr loadSceneObjectFromStream(const std::string& caller, - std::string const& filename, - ramses_internal::IInputStream& inputStream, - bool localOnly, - sceneId_t sceneId); - void finalizeLoadedScene(SceneOwningPtr scene); - - status_t validateScenes() const; - - static bool GetFeatureLevelFromStream(ramses_internal::IInputStreamContainer& streamContainer, const std::string& desc, EFeatureLevel& detectedFeatureLevel); - - RamsesClient* m_hlClient = nullptr; - ramses_internal::ClientApplicationLogic m_appLogic; - ramses_internal::SceneFactory m_sceneFactory; - - SceneVector m_scenes; - - std::shared_ptr m_cmdPrintSceneList; - std::shared_ptr m_cmdPrintValidation; - std::shared_ptr m_cmdFlushSceneVersion; - std::shared_ptr m_cmdDumpSceneToFile; - std::shared_ptr m_cmdLogResourceMemoryUsage; - - RamsesFrameworkImpl& m_framework; - mutable ramses_internal::PlatformLock m_clientLock; - - ramses_internal::TaskForwardingQueue m_loadFromFileTaskQueue; - ramses_internal::EnqueueOnlyOneAtATimeQueue m_deleteSceneQueue; - - std::vector m_asyncSceneLoadStatusVec; - }; - - template - void RamsesClientImpl::enqueueSceneCommand(sceneId_t sceneId, T cmd) - { - ramses_internal::PlatformGuard guard(m_clientLock); - auto it = std::find_if(m_scenes.begin(), m_scenes.end(), [&](const auto& scene) { return scene->getSceneId() == sceneId; }); - if (it != m_scenes.end()) - (*it)->m_impl.enqueueSceneCommand(std::move(cmd)); - } -} - -#endif diff --git a/client/ramses-client/impl/RamsesClientTypesImpl.h b/client/ramses-client/impl/RamsesClientTypesImpl.h deleted file mode 100644 index 86cbce6da..000000000 --- a/client/ramses-client/impl/RamsesClientTypesImpl.h +++ /dev/null @@ -1,47 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESCLIENTTYPESIMPL_H -#define RAMSES_RAMSESCLIENTTYPESIMPL_H - -#include "Collections/StringOutputStream.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "SceneAPI/TextureEnums.h" -#include "TextureUtils.h" -#include "ramses-framework-api/EDataType.h" - -template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase -{ - template - constexpr auto format(const ramses::TextureSwizzle& swizzle, FormatContext& ctx) - { - return fmt::format_to(ctx.out(), "TextureSwizzling:[{};{};{};{}]", - ramses_internal::EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelRed)), - ramses_internal::EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelGreen)), - ramses_internal::EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelBlue)), - ramses_internal::EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelAlpha))); - } -}; - -namespace ramses -{ - const std::array DataTypeNames = - { - "UINT16", - "UINT32", - "FLOAT", - "VECTOR2F", - "VECTOR3F", - "VECTOR4F", - }; -} - -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses::EDataType, "EDataType", ramses::DataTypeNames, ramses::EDataType::Vector4F) - -#endif diff --git a/client/ramses-client/impl/RamsesObjectHandle.h b/client/ramses-client/impl/RamsesObjectHandle.h deleted file mode 100644 index 4657243e8..000000000 --- a/client/ramses-client/impl/RamsesObjectHandle.h +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESOBJECTHANDLE_H -#define RAMSES_RAMSESOBJECTHANDLE_H - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "Common/TypedMemoryHandle.h" - -namespace ramses -{ - struct RamsesObjectHandleTag {}; - using RamsesObjectHandle = ramses_internal::TypedMemoryHandle; -} - -#endif diff --git a/client/ramses-client/impl/RamsesObjectImpl.cpp b/client/ramses-client/impl/RamsesObjectImpl.cpp deleted file mode 100644 index 9293a09a7..000000000 --- a/client/ramses-client/impl/RamsesObjectImpl.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RamsesObjectImpl.h" -#include "NodeImpl.h" -#include "IRamsesObjectRegistry.h" -#include "RamsesObjectTypeUtils.h" -#include "SerializationContext.h" -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "Collections/StringOutputStream.h" - -namespace ramses -{ - RamsesObjectImpl::RamsesObjectImpl(ERamsesObjectType type, std::string_view name) - : m_type(type) - , m_name(name) - { - } - - RamsesObjectImpl::~RamsesObjectImpl() - { - } - - void RamsesObjectImpl::setObjectRegistry(IRamsesObjectRegistry& objectRegistry) - { - m_objectRegistry = &objectRegistry; - } - - void RamsesObjectImpl::setObjectRegistryHandle(RamsesObjectHandle handle) - { - m_objectRegistryHandle = handle; - } - - RamsesObjectHandle RamsesObjectImpl::getObjectRegistryHandle() const - { - return m_objectRegistryHandle; - } - - ERamsesObjectType RamsesObjectImpl::getType() const - { - return m_type; - } - - bool RamsesObjectImpl::isOfType(ERamsesObjectType type) const - { - return RamsesObjectTypeUtils::IsTypeMatchingBaseType(m_type, type); - } - - const std::string& RamsesObjectImpl::getName() const - { - return m_name; - } - - status_t RamsesObjectImpl::setName(RamsesObject& object, std::string_view name) - { - std::string newName{name}; - if (m_objectRegistry) - { - // updateName must be called before m_name is changed - m_objectRegistry->updateName(object, newName); - } - std::swap(m_name, newName); - - return StatusOK; - } - - const RamsesObject& RamsesObjectImpl::getRamsesObject() const - { - assert(m_ramsesObject != nullptr); - return *m_ramsesObject; - } - - RamsesObject& RamsesObjectImpl::getRamsesObject() - { - // non-const version of getRamsesObject cast to its const version to avoid duplicating code - return const_cast((const_cast(*this)).getRamsesObject()); - } - - void RamsesObjectImpl::setRamsesObject(RamsesObject& ramsesObject) - { - assert(m_ramsesObject == nullptr); - m_ramsesObject = &ramsesObject; - } - - ramses::status_t RamsesObjectImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - outStream << serializationContext.getIDForObject(this); - outStream << m_name; - - return StatusOK; - } - - status_t RamsesObjectImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - UNUSED(serializationContext); - inStream >> m_name; - - return StatusOK; - } - - status_t RamsesObjectImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) - { - UNUSED(serializationContext); - return StatusOK; - } - - status_t RamsesObjectImpl::validate() const - { - const status_t status = StatusObjectImpl::validate(); - std::string message{ fmt::format("{} '{}'", RamsesObjectTypeUtils::GetRamsesObjectTypeName(getType()), getName()) }; - StatusObjectImpl::addValidationMessage(EValidationSeverity::Info, std::move(message)); - - return status; - } - - status_t RamsesObjectImpl::addValidationMessage(EValidationSeverity severity, std::string message) const - { - message = fmt::format("{} '{}': {}", RamsesObjectTypeUtils::GetRamsesObjectTypeName(getType()), getName(), message); - return StatusObjectImpl::addValidationMessage(severity, std::move(message)); - } -} diff --git a/client/ramses-client/impl/RamsesObjectImpl.h b/client/ramses-client/impl/RamsesObjectImpl.h deleted file mode 100644 index e4325cef8..000000000 --- a/client/ramses-client/impl/RamsesObjectImpl.h +++ /dev/null @@ -1,74 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESOBJECTIMPL_H -#define RAMSES_RAMSESOBJECTIMPL_H - -// API -#include "ramses-client-api/RamsesObjectTypes.h" - -// internal -#include "StatusObjectImpl.h" -#include "RamsesObjectHandle.h" - -#include -#include - -namespace ramses_internal -{ - class IOutputStream; - class IInputStream; -} - -namespace ramses -{ - class SerializationContext; - class DeserializationContext; - class RamsesObject; - class IRamsesObjectRegistry; - - class RamsesObjectImpl : public StatusObjectImpl - { - public: - explicit RamsesObjectImpl(ERamsesObjectType type, std::string_view name); - ~RamsesObjectImpl() override; - - void setObjectRegistry(IRamsesObjectRegistry& objectRegistry); - void setObjectRegistryHandle(RamsesObjectHandle handle); - RamsesObjectHandle getObjectRegistryHandle() const; - - ERamsesObjectType getType() const; - bool isOfType(ERamsesObjectType type) const; - const std::string& getName() const; - virtual status_t setName(RamsesObject& object, std::string_view name); - const RamsesObject& getRamsesObject() const; - RamsesObject& getRamsesObject(); - void setRamsesObject(RamsesObject& ramsesObject); - - virtual status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const; - virtual status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext); - virtual status_t resolveDeserializationDependencies(DeserializationContext& serializationContext); - - virtual void deinitializeFrameworkData() = 0; - - status_t validate() const override; - - protected: - status_t addValidationMessage(EValidationSeverity severity, std::string message) const override; - - private: - ERamsesObjectType m_type; - std::string m_name; - - RamsesObject* m_ramsesObject = nullptr; - IRamsesObjectRegistry* m_objectRegistry = nullptr; - RamsesObjectHandle m_objectRegistryHandle; - }; -} - -#endif diff --git a/client/ramses-client/impl/RamsesObjectRegistry.cpp b/client/ramses-client/impl/RamsesObjectRegistry.cpp deleted file mode 100644 index f5205b53d..000000000 --- a/client/ramses-client/impl/RamsesObjectRegistry.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RamsesObjectRegistry.h" -#include "ramses-client-api/RamsesObject.h" -#include "ramses-client-api/Node.h" -#include "RamsesObjectImpl.h" -#include "ObjectIteratorImpl.h" -#include "NodeImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "PlatformAbstraction/PlatformStringUtils.h" - -namespace ramses -{ - RamsesObjectRegistry::~RamsesObjectRegistry() = default; - - void RamsesObjectRegistry::registerObjectInternal(RamsesObject& object) - { - assert(!containsObject(object)); - - const ERamsesObjectType type = object.m_impl.getType(); - const RamsesObjectHandle handle = m_objects[static_cast(type)].allocate(); - *m_objects[static_cast(type)].getMemory(handle) = &object; - object.m_impl.setObjectRegistry(*this); - object.m_impl.setObjectRegistryHandle(handle); - - updateName(object, object.m_impl.getName()); - trackSceneObjectById(object); - } - - void RamsesObjectRegistry::destroyAndUnregisterObject(RamsesObject& object) - { - assert(containsObject(object)); - - if (object.isOfType(ERamsesObjectType::Node)) - setNodeDirty(RamsesObjectTypeUtils::ConvertTo(object).m_impl, false); - - if (object.isOfType(ERamsesObjectType::SceneObject)) - { - const sceneObjectId_t sceneObjectId = RamsesObjectTypeUtils::ConvertTo(object).getSceneObjectId(); - assert(m_objectsById.contains(sceneObjectId)); - m_objectsById.remove(sceneObjectId); - } - - m_objectsByName.remove(object.m_impl.getName()); - - const RamsesObjectHandle handle = object.m_impl.getObjectRegistryHandle(); - const auto type = static_cast(object.m_impl.getType()); - m_objects[type].release(handle); - - auto it = std::find_if(m_objectsOwningContainer.begin(), m_objectsOwningContainer.end(), [&object](auto& ro) { return ro.get() == &object; }); - assert(it != m_objectsOwningContainer.end()); - m_objectsOwningContainer.erase(it); - } - - void RamsesObjectRegistry::reserveAdditionalGeneralCapacity(uint32_t additionalCount) - { - // not every object has a name. this might reserve more than needed but never more than - // num of current objects with name + additionalCapacity - m_objectsByName.reserve(m_objectsByName.size() + additionalCount); - m_objectsOwningContainer.reserve(m_objectsOwningContainer.size() + additionalCount); - } - - void RamsesObjectRegistry::reserveAdditionalObjectCapacity(ERamsesObjectType type, uint32_t additionalCount) - { - assert(RamsesObjectTypeUtils::IsConcreteType(type)); - const auto index = static_cast(type); - m_objects[index].preallocateSize(m_objects[index].getActualCount() + additionalCount); - } - - uint32_t RamsesObjectRegistry::getNumberOfObjects(ERamsesObjectType type) const - { - assert(RamsesObjectTypeUtils::IsConcreteType(type)); - return m_objects[static_cast(type)].getActualCount(); - } - - bool RamsesObjectRegistry::containsObject(const RamsesObject& object) const - { - const RamsesObjectHandle handle = object.m_impl.getObjectRegistryHandle(); - const ERamsesObjectType type = object.m_impl.getType(); - const RamsesObjectsPool& objectsPool = m_objects[static_cast(type)]; - return objectsPool.isAllocated(handle) && (*objectsPool.getMemory(handle) == &object); - } - - void RamsesObjectRegistry::updateName(RamsesObject& object, const std::string& name) - { - assert(containsObject(object)); - const std::string& oldName = object.m_impl.getName(); - if (!oldName.empty()) - { - m_objectsByName.remove(oldName); - } - if (name.size()> 0) - { - m_objectsByName.put(name, &object); - } - } - - void RamsesObjectRegistry::trackSceneObjectById(RamsesObject& object) - { - if (object.isOfType(ERamsesObjectType::SceneObject)) - { - SceneObject& sceneObject = RamsesObjectTypeUtils::ConvertTo(object); - const sceneObjectId_t sceneObjectId = RamsesObjectTypeUtils::ConvertTo(object).getSceneObjectId(); - assert(!m_objectsById.contains(sceneObjectId)); - m_objectsById.put(sceneObjectId, &sceneObject); - } - } - - RamsesObject* RamsesObjectRegistry::findObjectByName(std::string_view name) - { - RamsesObject* object(nullptr); - m_objectsByName.get(std::string{name}, object); - return object; - } - - const RamsesObject* RamsesObjectRegistry::findObjectByName(std::string_view name) const - { - // const version of findObjectByName cast to its non-const version to avoid duplicating code - return const_cast((const_cast(*this)).findObjectByName(name)); - } - - SceneObject* RamsesObjectRegistry::findObjectById(sceneObjectId_t id) - { - SceneObject* object(nullptr); - m_objectsById.get(id, object); - - return object; - } - - const SceneObject* RamsesObjectRegistry::findObjectById(sceneObjectId_t id) const - { - // const version of findObjectById cast to its non-const version to avoid duplicating code - return const_cast((const_cast(*this)).findObjectById(id)); - } - - void RamsesObjectRegistry::getObjectsOfType(RamsesObjectVector& objects, ERamsesObjectType ofType) const - { - assert(objects.empty()); - - // preallocate memory in container - uint32_t objectCount = 0u; - for (uint32_t i = 0u; i < static_cast(ERamsesObjectType::NUMBER_OF_TYPES); ++i) - { - const ERamsesObjectType type = ERamsesObjectType(i); - if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ofType)) - { - objectCount += m_objects[i].getActualCount(); - } - } - objects.reserve(objectCount); - - for (uint32_t i = 0u; i < static_cast(ERamsesObjectType::NUMBER_OF_TYPES); ++i) - { - const ERamsesObjectType type = ERamsesObjectType(i); - if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ofType)) - { - const RamsesObjectsPool& objectsPool = m_objects[i]; - for (RamsesObjectHandle handle(0u); handle < objectsPool.getTotalCount(); ++handle) - { - if (objectsPool.isAllocated(handle)) - { - objects.push_back(*objectsPool.getMemory(handle)); - } - } - } - } - } - - void RamsesObjectRegistry::setNodeDirty(NodeImpl& node, bool dirty) - { - if (dirty) - { - m_dirtyNodes.put(&node); - } - else - { - m_dirtyNodes.remove(&node); - } - } - - bool RamsesObjectRegistry::isNodeDirty(const NodeImpl& node) const - { - NodeImpl* nodeImplPtr = &const_cast(node); - return m_dirtyNodes.contains(nodeImplPtr); - } - - const NodeImplSet& RamsesObjectRegistry::getDirtyNodes() const - { - return m_dirtyNodes; - } - - void RamsesObjectRegistry::clearDirtyNodes() - { - m_dirtyNodes.clear(); - } -} diff --git a/client/ramses-client/impl/RamsesObjectRegistry.h b/client/ramses-client/impl/RamsesObjectRegistry.h deleted file mode 100644 index e4dd47c60..000000000 --- a/client/ramses-client/impl/RamsesObjectRegistry.h +++ /dev/null @@ -1,100 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESOBJECTREGISTRY_H -#define RAMSES_RAMSESOBJECTREGISTRY_H - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/RamsesObject.h" - -#include "RamsesObjectHandle.h" -#include "RamsesObjectVector.h" -#include "IRamsesObjectRegistry.h" - -#include "Collections/HashMap.h" -#include "Utils/MemoryPool.h" - -#include -#include -#include - -namespace ramses -{ - class RamsesObject; - class RamsesObjectImpl; - class SceneObject; - - class RamsesObjectRegistry final : public IRamsesObjectRegistry - { - public: - ~RamsesObjectRegistry() override; - - template - T& createAndRegisterObject(std::unique_ptr impl); - void destroyAndUnregisterObject(RamsesObject& object); - - void reserveAdditionalGeneralCapacity(uint32_t additionalCount); - void reserveAdditionalObjectCapacity(ERamsesObjectType type, uint32_t additionalCount); - [[nodiscard]] uint32_t getNumberOfObjects(ERamsesObjectType type) const; - - void getObjectsOfType(RamsesObjectVector& objects, ERamsesObjectType ofType) const; - - // IRamsesObjectRegistry - void updateName(RamsesObject& object, const std::string& name) override; - - [[nodiscard]] const RamsesObject* findObjectByName(std::string_view name) const; - RamsesObject* findObjectByName(std::string_view name); - - [[nodiscard]] const SceneObject* findObjectById(sceneObjectId_t id) const; - SceneObject* findObjectById(sceneObjectId_t id); - - - void setNodeDirty(NodeImpl& node, bool dirty); - [[nodiscard]] bool isNodeDirty(const NodeImpl& node) const; - - [[nodiscard]] const NodeImplSet& getDirtyNodes() const; - void clearDirtyNodes(); - - private: - void registerObjectInternal(RamsesObject& object); - [[nodiscard]] bool containsObject(const RamsesObject& object) const; - void trackSceneObjectById(RamsesObject& object); - - using ObjectNameMap = ramses_internal::HashMap; - ObjectNameMap m_objectsByName; - - using ObjectIdMap = ramses_internal::HashMap; - ObjectIdMap m_objectsById; - - using RamsesObjectsPool = ramses_internal::MemoryPool; - std::array(ERamsesObjectType::NUMBER_OF_TYPES)> m_objects; - - using RamsesObjectUniquePtr = std::unique_ptr>; - std::vector m_objectsOwningContainer; - - NodeImplSet m_dirtyNodes; - - friend class RamsesObjectRegistryIterator; - }; - - template - T& RamsesObjectRegistry::createAndRegisterObject(std::unique_ptr impl) - { - static_assert(std::is_base_of_v, "Meant for RamsesObject instances only"); - - std::unique_ptr> object{ new T{ std::move(impl) }, [](RamsesObject* o) { delete o; } }; - T* objectRawPtr = object.get(); - this->m_objectsOwningContainer.push_back(std::move(object)); - - this->registerObjectInternal(*objectRawPtr); - - return *objectRawPtr; - } -} - -#endif diff --git a/client/ramses-client/impl/RamsesObjectTypeUtils.cpp b/client/ramses-client/impl/RamsesObjectTypeUtils.cpp deleted file mode 100644 index 182a35583..000000000 --- a/client/ramses-client/impl/RamsesObjectTypeUtils.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RamsesObjectTypeUtils.h" -#include "Utils/LoggingUtils.h" - -namespace ramses -{ - const std::array RamsesObjectTypeNames = - { - "ERamsesObjectType_Invalid", - "ERamsesObjectType_RamsesObject", - "ERamsesObjectType_ClientObject", - "ERamsesObjectType_SceneObject", - "ERamsesObjectType_Client", - "ERamsesObjectType_Scene", - "ERamsesObjectType_Node", - "ERamsesObjectType_MeshNode", - "ERamsesObjectType_Camera", - "ERamsesObjectType_PerspectiveCamera", - "ERamsesObjectType_OrthographicCamera", - "ERamsesObjectType_Effect", - "ERamsesObjectType_Appearance", - "ERamsesObjectType_Geometry", - "ERamsesObjectType_PickableObject", - "ERamsesObjectType_Resource", - "ERamsesObjectType_Texture2D", - "ERamsesObjectType_Texture3D", - "ERamsesObjectType_TextureCube", - "ERamsesObjectType_ArrayResource", - "ERamsesObjectType_RenderGroup", - "ERamsesObjectType_RenderPass", - "ERamsesObjectType_BlitPass", - "ERamsesObjectType_TextureSampler", - "ERamsesObjectType_TextureSamplerMS", - "ERamsesObjectType_RenderBuffer", - "ERamsesObjectType_RenderTarget", - "ERamsesObjectType_ArrayBufferObject", - "ERamsesObjectType_Texture2DBuffer", - "ERamsesObjectType_DataObject", - "ERamsesObjectType_SceneReference", - "ERamsesObjectType_TextureSamplerExternal" - }; - - ENUM_TO_STRING(ERamsesObjectType, RamsesObjectTypeNames, ERamsesObjectType::NUMBER_OF_TYPES); - - static_assert(static_cast(ERamsesObjectType::NUMBER_OF_TYPES) == RamsesObjectTraits.size(), "Every RamsesObject type must register its traits!"); - - const char* RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType type) - { - return EnumToString(type); - } - - bool RamsesObjectTypeUtils::IsTypeMatchingBaseType(ERamsesObjectType type, ERamsesObjectType baseType) - { - while (type != ERamsesObjectType::Invalid) - { - if (type == baseType) - { - return true; - } - const auto index = static_cast(type); - assert(RamsesObjectTraits[index].typeID == type && "Wrong order of RamsesObject traits!"); - type = RamsesObjectTraits[index].baseClassTypeID; - } - - return false; - } - - bool RamsesObjectTypeUtils::IsConcreteType(ERamsesObjectType type) - { - const auto index = static_cast(type); - assert(RamsesObjectTraits[index].typeID == type && "Wrong order of RamsesObject traits!"); - return RamsesObjectTraits[index].isConcreteType; - } -} diff --git a/client/ramses-client/impl/RamsesObjectTypeUtils.h b/client/ramses-client/impl/RamsesObjectTypeUtils.h deleted file mode 100644 index 60f9f2cfd..000000000 --- a/client/ramses-client/impl/RamsesObjectTypeUtils.h +++ /dev/null @@ -1,42 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESOBJECTTYPEUTILS_H -#define RAMSES_RAMSESOBJECTTYPEUTILS_H - -#include "ramses-client-api/RamsesObjectTypes.h" -#include "ramses-client-api/RamsesObject.h" -#include "RamsesObjectTypeTraits.h" -#include - -namespace ramses -{ - class RamsesObjectTypeUtils final - { - public: - static const char* GetRamsesObjectTypeName( ERamsesObjectType type ); - static bool IsTypeMatchingBaseType(ERamsesObjectType type, ERamsesObjectType baseType); - static bool IsConcreteType(ERamsesObjectType type); - - template - static const T& ConvertTo(const RamsesObject& obj) - { - assert(IsTypeMatchingBaseType(obj.getType(), TYPE_ID_OF_RAMSES_OBJECT::ID)); - return static_cast(obj); - } - - template - static T& ConvertTo(RamsesObject& obj) - { - assert(IsTypeMatchingBaseType(obj.getType(), TYPE_ID_OF_RAMSES_OBJECT::ID)); - return static_cast(obj); - } - }; -} - -#endif diff --git a/client/ramses-client/impl/RenderBufferImpl.h b/client/ramses-client/impl/RenderBufferImpl.h deleted file mode 100644 index b2b613748..000000000 --- a/client/ramses-client/impl/RenderBufferImpl.h +++ /dev/null @@ -1,51 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERBUFFERIMPL_H -#define RAMSES_RENDERBUFFERIMPL_H - -#include "ramses-client-api/TextureEnums.h" - -// internal -#include "SceneObjectImpl.h" - -// ramses framework -#include "SceneAPI/Handles.h" - -#include - -namespace ramses -{ - class RenderBufferImpl final : public SceneObjectImpl - { - public: - RenderBufferImpl(SceneImpl& scene, std::string_view name); - ~RenderBufferImpl() override; - - void initializeFrameworkData(uint32_t width, uint32_t height, ERenderBufferType bufferType, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - status_t validate() const override; - - uint32_t getWidth() const; - uint32_t getHeight() const; - ERenderBufferType getBufferType() const; - ERenderBufferFormat getBufferFormat() const; - ERenderBufferAccessMode getAccessMode() const; - uint32_t getSampleCount() const; - - ramses_internal::RenderBufferHandle getRenderBufferHandle() const; - - private: - ramses_internal::RenderBufferHandle m_renderBufferHandle; - }; -} - -#endif diff --git a/client/ramses-client/impl/RenderGroupImpl.cpp b/client/ramses-client/impl/RenderGroupImpl.cpp deleted file mode 100644 index 78540ff2b..000000000 --- a/client/ramses-client/impl/RenderGroupImpl.cpp +++ /dev/null @@ -1,316 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RenderGroupImpl.h" - -#include "ramses-client-api/MeshNode.h" - -#include "SerializationContext.h" -#include "MeshNodeImpl.h" - -#include "SceneObjectImpl.h" -#include "SceneImpl.h" -#include "Scene/ClientScene.h" -#include "SceneAPI/RenderGroup.h" -#include "SceneAPI/RenderGroupUtils.h" - - -namespace ramses -{ - RenderGroupImpl::RenderGroupImpl(SceneImpl& scene, std::string_view name) - : SceneObjectImpl(scene, ERamsesObjectType::RenderGroup, name) - { - } - - RenderGroupImpl::~RenderGroupImpl() - { - } - - template - void serializeObjects(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext, const std::vector& objects) - { - outStream << static_cast(objects.size()); - for(const auto& object : objects) - { - assert(nullptr != object); - outStream << serializationContext.getIDForObject(object); - } - } - - status_t RenderGroupImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << m_renderGroupHandle; - - serializeObjects(outStream, serializationContext, m_meshes); - serializeObjects(outStream, serializationContext, m_renderGroups); - - return StatusOK; - } - - template - void deserializeObjects(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext, std::vector& objects) - { - UNUSED(serializationContext); - uint32_t numberOfObjects = 0; - inStream >> numberOfObjects; - assert(objects.empty()); - objects.reserve(numberOfObjects); - - for (uint32_t i = 0; i < numberOfObjects; ++i) - { - OBJECT* object = nullptr; - serializationContext.ReadDependentPointerAndStoreAsID(inStream, object); - objects.push_back(object); - } - } - - status_t RenderGroupImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - inStream >> m_renderGroupHandle; - - deserializeObjects(inStream, serializationContext, m_meshes); - deserializeObjects(inStream, serializationContext, m_renderGroups); - - serializationContext.addForDependencyResolve(this); - - return StatusOK; - } - - status_t RenderGroupImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::resolveDeserializationDependencies(serializationContext)); - - for (size_t i = 0; i < m_meshes.size(); ++i) - { - MeshNodeImpl* mesh = const_cast(m_meshes[i]); - serializationContext.resolveDependencyIDImplAndStoreAsPointer(mesh); - m_meshes[i] = mesh; - } - - for (size_t i = 0; i < m_renderGroups.size(); ++i) - { - RenderGroupImpl* renderGroup = const_cast(m_renderGroups[i]); - serializationContext.resolveDependencyIDImplAndStoreAsPointer(renderGroup); - m_renderGroups[i] = renderGroup; - } - - return StatusOK; - } - - status_t RenderGroupImpl::validate() const - { - status_t status = SceneObjectImpl::validate(); - - if (m_meshes.empty() && m_renderGroups.empty()) - status = addValidationMessage(EValidationSeverity::Warning, "rendergroup does not contain any meshes"); - - for (const auto& element : m_meshes) - status = std::max(status, addValidationOfDependentObject(*element)); - - for (const auto& element : m_renderGroups) - status = std::max(status, addValidationOfDependentObject(*element)); - - return status; - } - - void RenderGroupImpl::initializeFrameworkData() - { - m_renderGroupHandle = getIScene().allocateRenderGroup(); - } - - void RenderGroupImpl::deinitializeFrameworkData() - { - assert(m_renderGroupHandle.isValid()); - getIScene().releaseRenderGroup(m_renderGroupHandle); - m_renderGroupHandle = ramses_internal::RenderGroupHandle::Invalid(); - } - - bool RenderGroupImpl::contains(const MeshNodeImpl& meshImpl) const - { - // const cast just to get ptr for query - return ramses_internal::contains_c(m_meshes, &meshImpl); - } - - const MeshNodeImplVector& RenderGroupImpl::getAllMeshes() const - { - return m_meshes; - } - - status_t RenderGroupImpl::getMeshNodeOrder(const MeshNodeImpl& mesh, int32_t& orderWithinGroup) const - { - if (!contains(mesh)) - { - return addErrorEntry("RenderGroup::getMeshNodeOrder failed - mesh not contained in RenderGroup"); - } - - const auto& internalRenderGroup = getIScene().getRenderGroup(m_renderGroupHandle); - const auto renderableEntryIt = ramses_internal::RenderGroupUtils::FindRenderableEntry(mesh.getRenderableHandle(), internalRenderGroup); - if (renderableEntryIt == internalRenderGroup.renderables.cend()) - { - assert(false); - return addErrorEntry("RenderGroup::getMeshNodeOrder failed - fatal, mesh not found in internal render group"); - } - - orderWithinGroup = renderableEntryIt->order; - return StatusOK; - } - - status_t RenderGroupImpl::addMeshNode(const MeshNodeImpl& meshImpl, int32_t orderWithinGroup) - { - if (!isFromTheSameSceneAs(meshImpl)) - { - return addErrorEntry("RenderGroup::addMeshNode failed - meshNode is not from the same scene as this RenderGroup."); - } - - if (ramses_internal::contains_c(m_meshes, &meshImpl)) - { - remove(meshImpl); - } - - const ramses_internal::RenderableHandle renderableToAdd = meshImpl.getRenderableHandle(); - getIScene().addRenderableToRenderGroup(m_renderGroupHandle, renderableToAdd, orderWithinGroup); - m_meshes.push_back(&meshImpl); - - return StatusOK; - } - - status_t RenderGroupImpl::remove(const MeshNodeImpl& mesh) - { - MeshNodeImplVector::iterator iter = ramses_internal::find_c(m_meshes, &mesh); - if (iter == m_meshes.end()) - { - return addErrorEntry("RenderGroup::remove failed - could not remove MeshNode from RenderGroup because it was not contained"); - } - - removeInternal(iter); - - return StatusOK; - } - - void RenderGroupImpl::removeIfContained(const MeshNodeImpl& mesh) - { - MeshNodeImplVector::iterator iter = ramses_internal::find_c(m_meshes, &mesh); - if (iter != m_meshes.end()) - { - removeInternal(iter); - } - } - - status_t RenderGroupImpl::addRenderGroup(const RenderGroupImpl& renderGroupImpl, int32_t orderWithinGroup) - { - if (!isFromTheSameSceneAs(renderGroupImpl)) - { - return addErrorEntry("RenderGroup::addRenderGroup failed - renderGroup is not from the same scene as this RenderGroup."); - } - - if (ramses_internal::contains_c(m_renderGroups, &renderGroupImpl)) - { - remove(renderGroupImpl); - } - - const ramses_internal::RenderGroupHandle renderGroupToAdd = renderGroupImpl.getRenderGroupHandle(); - getIScene().addRenderGroupToRenderGroup(m_renderGroupHandle, renderGroupToAdd, orderWithinGroup); - m_renderGroups.push_back(&renderGroupImpl); - - return StatusOK; - } - - status_t RenderGroupImpl::remove(const RenderGroupImpl& renderGroup) - { - RenderGroupImplVector::iterator iter = ramses_internal::find_c(m_renderGroups, &renderGroup); - if (iter == m_renderGroups.end()) - { - return addErrorEntry("RenderGroup::removeRenderGroup failed - could not remove render group from RenderGroup because it was not contained"); - } - - removeInternal(iter); - - return StatusOK; - } - - void RenderGroupImpl::removeIfContained(const RenderGroupImpl& renderGroup) - { - RenderGroupImplVector::iterator iter = ramses_internal::find_c(m_renderGroups, &renderGroup); - if (iter != m_renderGroups.end()) - { - removeInternal(iter); - } - } - - bool RenderGroupImpl::contains(const RenderGroupImpl& renderGroup) const - { - return ramses_internal::contains_c(m_renderGroups, &renderGroup); - } - - status_t RenderGroupImpl::getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinGroup) const - { - if (!contains(renderGroup)) - { - return addErrorEntry("RenderGroup::getRenderGroupOrder failed - render group not contained in RenderGroup"); - } - - const auto& internalRenderGroup = getIScene().getRenderGroup(m_renderGroupHandle); - const auto renderGroupEntryIt = ramses_internal::RenderGroupUtils::FindRenderGroupEntry(renderGroup.getRenderGroupHandle(), internalRenderGroup); - if (renderGroupEntryIt == internalRenderGroup.renderGroups.cend()) - { - assert(false); - return addErrorEntry("RenderGroup::getRenderGroupOrder failed - fatal, render group not found in internal render group"); - } - - orderWithinGroup = renderGroupEntryIt->order; - return StatusOK; - } - - const RenderGroupImplVector& RenderGroupImpl::getAllRenderGroups() const - { - return m_renderGroups; - } - - status_t RenderGroupImpl::removeAllRenderables() - { - while (!m_meshes.empty()) - { - CHECK_RETURN_ERR(remove(*m_meshes.front())); - } - - return StatusOK; - } - - status_t RenderGroupImpl::removeAllRenderGroups() - { - while (!m_renderGroups.empty()) - { - CHECK_RETURN_ERR(remove(*m_renderGroups.front())); - } - - return StatusOK; - } - - ramses_internal::RenderGroupHandle RenderGroupImpl::getRenderGroupHandle() const - { - return m_renderGroupHandle; - } - - void RenderGroupImpl::removeInternal(MeshNodeImplVector::iterator iter) - { - const ramses_internal::RenderableHandle renderableToRemove = (*iter)->getRenderableHandle(); - getIScene().removeRenderableFromRenderGroup(m_renderGroupHandle, renderableToRemove); - m_meshes.erase(iter); - } - - void RenderGroupImpl::removeInternal(RenderGroupImplVector::iterator iter) - { - const ramses_internal::RenderGroupHandle renderGroupToRemove = (*iter)->getRenderGroupHandle(); - getIScene().removeRenderGroupFromRenderGroup(m_renderGroupHandle, renderGroupToRemove); - m_renderGroups.erase(iter); - } -} diff --git a/client/ramses-client/impl/RenderGroupImpl.h b/client/ramses-client/impl/RenderGroupImpl.h deleted file mode 100644 index 968d3add1..000000000 --- a/client/ramses-client/impl/RenderGroupImpl.h +++ /dev/null @@ -1,69 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERGROUPIMPL_H -#define RAMSES_RENDERGROUPIMPL_H - -#include "SceneObjectImpl.h" -#include "SceneAPI/Handles.h" - -#include -#include - -namespace ramses -{ - class MeshNodeImpl; - class RenderGroupImpl; - - using MeshNodeImplVector = std::vector; - using RenderGroupImplVector = std::vector; - - class RenderGroupImpl final : public SceneObjectImpl - { - public: - RenderGroupImpl(SceneImpl& scene, std::string_view name); - ~RenderGroupImpl() override; - - void initializeFrameworkData(); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - status_t addMeshNode(const MeshNodeImpl& mesh, int32_t orderWithinGroup); - status_t remove(const MeshNodeImpl& mesh); - void removeIfContained(const MeshNodeImpl& mesh); - bool contains(const MeshNodeImpl& mesh) const; - status_t getMeshNodeOrder(const MeshNodeImpl& mesh, int32_t& orderWithinGroup) const; - const MeshNodeImplVector& getAllMeshes() const; - - status_t addRenderGroup(const RenderGroupImpl& renderGroup, int32_t orderWithinGroup); - status_t remove(const RenderGroupImpl& renderGroup); - void removeIfContained(const RenderGroupImpl& renderGroup); - bool contains(const RenderGroupImpl& renderGroup) const; - status_t getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinGroup) const; - const RenderGroupImplVector& getAllRenderGroups() const; - - status_t removeAllRenderables(); - status_t removeAllRenderGroups(); - - ramses_internal::RenderGroupHandle getRenderGroupHandle() const; - - private: - void removeInternal(MeshNodeImplVector::iterator iter); - void removeInternal(RenderGroupImplVector::iterator iter); - - ramses_internal::RenderGroupHandle m_renderGroupHandle; - - MeshNodeImplVector m_meshes; - RenderGroupImplVector m_renderGroups; - }; -} - -#endif diff --git a/client/ramses-client/impl/RenderPassImpl.cpp b/client/ramses-client/impl/RenderPassImpl.cpp deleted file mode 100644 index 4cf1f2b72..000000000 --- a/client/ramses-client/impl/RenderPassImpl.cpp +++ /dev/null @@ -1,384 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RenderPassImpl.h" - -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/RenderTarget.h" - -#include "SerializationContext.h" -#include "CameraNodeImpl.h" -#include "RenderTargetImpl.h" -#include "RenderGroupImpl.h" -#include "RamsesObjectTypeUtils.h" - -#include "Scene/ClientScene.h" - -#include - -namespace ramses -{ - RenderPassImpl::RenderPassImpl(SceneImpl& scene, std::string_view renderpassName) - : SceneObjectImpl(scene, ERamsesObjectType::RenderPass, renderpassName) - , m_cameraImpl(nullptr) - , m_renderTargetImpl(nullptr) - { - } - - RenderPassImpl::~RenderPassImpl() - { - } - - status_t RenderPassImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << m_renderPassHandle; - - if (m_cameraImpl != nullptr) - { - outStream << serializationContext.getIDForObject(m_cameraImpl); - } - else - { - outStream << SerializationContext::GetObjectIDNull(); - } - - if (m_renderTargetImpl != nullptr) - { - outStream << serializationContext.getIDForObject(m_renderTargetImpl); - } - else - { - outStream << SerializationContext::GetObjectIDNull(); - } - - outStream << static_cast(m_renderGroups.size()); - for (const auto groupImpl : m_renderGroups) - { - assert(nullptr != groupImpl); - outStream << serializationContext.getIDForObject(groupImpl); - } - - return StatusOK; - } - - status_t RenderPassImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - inStream >> m_renderPassHandle; - - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_cameraImpl); - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_renderTargetImpl); - - uint32_t numberOfGroups = 0; - inStream >> numberOfGroups; - assert(m_renderGroups.empty()); - m_renderGroups.reserve(numberOfGroups); - - for (uint32_t i = 0; i < numberOfGroups; ++i) - { - RenderGroupImpl* groupImpl = nullptr; - serializationContext.ReadDependentPointerAndStoreAsID(inStream, groupImpl); - m_renderGroups.push_back(groupImpl); - } - - serializationContext.addForDependencyResolve(this); - - return StatusOK; - } - - status_t RenderPassImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::resolveDeserializationDependencies(serializationContext)); - - serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_cameraImpl); - serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_renderTargetImpl); - - for (size_t i = 0; i < m_renderGroups.size(); ++i) - { - RenderGroupImpl* group = const_cast(m_renderGroups[i]); - serializationContext.resolveDependencyIDImplAndStoreAsPointer(group); - m_renderGroups[i] = group; - } - - return StatusOK; - } - - status_t RenderPassImpl::validate() const - { - status_t status = SceneObjectImpl::validate(); - - if (nullptr == m_cameraImpl) - status = addValidationMessage(EValidationSeverity::Warning, "renderpass does not have a camera set"); - - if (0 == m_renderGroups.size()) - status = addValidationMessage(EValidationSeverity::Warning, "renderpass does not contain any rendergroups"); - else - { - for (const auto& renderGroup : m_renderGroups) - status = std::max(status, addValidationOfDependentObject(*renderGroup)); - } - - if (nullptr != m_renderTargetImpl) - status = std::max(status, addValidationOfDependentObject(*m_renderTargetImpl)); - else if (getClearFlags() != ramses_internal::EClearFlags_None) - status = std::max(status, addValidationMessage(EValidationSeverity::Warning, "renderpass has clear flags enabled whithout any rendertarget, clear flags will have no effect")); - - return status; - } - - void RenderPassImpl::initializeFrameworkData() - { - m_renderPassHandle = getIScene().allocateRenderPass(); - } - - void RenderPassImpl::deinitializeFrameworkData() - { - assert(m_renderPassHandle.isValid()); - getIScene().releaseRenderPass(m_renderPassHandle); - m_renderPassHandle = ramses_internal::RenderPassHandle::Invalid(); - } - - status_t RenderPassImpl::setCamera(const CameraNodeImpl& cameraImpl) - { - if (!isFromTheSameSceneAs(cameraImpl)) - return addErrorEntry("RenderPass::setCamera failed - camera is not from the same scene as this RenderPass"); - - const status_t cameraValidity = cameraImpl.validate(); - if (StatusOK == cameraValidity) - { - m_cameraImpl = &cameraImpl; - getIScene().setRenderPassCamera(m_renderPassHandle, cameraImpl.getCameraHandle()); - } - else - { - std::string str = "RenderPass::setCamera failed - camera is not valid, maybe camera was not initialized:\n"; - str += cameraImpl.getValidationReport(EValidationSeverity::Warning); - return addErrorEntry(str); - } - - return cameraValidity; - } - - const Camera* RenderPassImpl::getCamera() const - { - if (m_cameraImpl != nullptr) - { - return &RamsesObjectTypeUtils::ConvertTo(m_cameraImpl->getRamsesObject()); - } - - return nullptr; - } - - Camera* RenderPassImpl::getCamera() - { - // non-const version of getCamera cast to its const version to avoid duplicating code - return const_cast((const_cast(*this)).getCamera()); - } - - status_t RenderPassImpl::setClearColor(const glm::vec4& clearColor) - { - getIScene().setRenderPassClearColor(m_renderPassHandle, clearColor); - return StatusOK; - } - - const glm::vec4& RenderPassImpl::getClearColor() const - { - return getIScene().getRenderPass(m_renderPassHandle).clearColor; - } - - status_t RenderPassImpl::setClearFlags(uint32_t clearFlags) - { - static_assert( - static_cast(ramses::EClearFlags_None) == static_cast(ramses_internal::EClearFlags_None) && - static_cast(ramses::EClearFlags_Color) == static_cast(ramses_internal::EClearFlags_Color) && - static_cast(ramses::EClearFlags_Depth) == static_cast(ramses_internal::EClearFlags_Depth) && - static_cast(ramses::EClearFlags_Stencil) == static_cast(ramses_internal::EClearFlags_Stencil) && - static_cast(ramses::EClearFlags_All) == static_cast(ramses_internal::EClearFlags_All), "Type conversion mismatch"); - - getIScene().setRenderPassClearFlag(m_renderPassHandle, clearFlags); - return StatusOK; - } - - uint32_t RenderPassImpl::getClearFlags() const - { - return getIScene().getRenderPass(m_renderPassHandle).clearFlags; - } - - status_t RenderPassImpl::addRenderGroup(const RenderGroupImpl& renderGroup, int32_t orderWithinPass) - { - if (!isFromTheSameSceneAs(renderGroup)) - { - return addErrorEntry("RenderPass::addRenderGroup failed - renderGroup is not from the same scene as this RenderPass"); - } - - if (!ramses_internal::contains_c(m_renderGroups, &renderGroup)) - { - const ramses_internal::RenderGroupHandle renderGroupHandle = renderGroup.getRenderGroupHandle(); - getIScene().addRenderGroupToRenderPass(m_renderPassHandle, renderGroupHandle, orderWithinPass); - m_renderGroups.push_back(&renderGroup); - } - - return StatusOK; - } - - status_t RenderPassImpl::remove(const RenderGroupImpl& renderGroup) - { - RenderGroupVector::iterator iter = ramses_internal::find_c(m_renderGroups, &renderGroup); - if (iter == m_renderGroups.end()) - { - return addErrorEntry("RenderPass::removeRenderGroup failed - could not remove RenderGroup from Renderpass because it was not contained"); - } - - removeInternal(iter); - - return StatusOK; - } - - void RenderPassImpl::removeIfContained(const RenderGroupImpl& renderGroup) - { - RenderGroupVector::iterator iter = ramses_internal::find_c(m_renderGroups, &renderGroup); - if (iter != m_renderGroups.end()) - { - removeInternal(iter); - } - } - - bool RenderPassImpl::contains(const RenderGroupImpl& renderGroup) const - { - return ramses_internal::contains_c(m_renderGroups, &renderGroup); - } - - ramses::status_t RenderPassImpl::getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinPass) const - { - if (!contains(renderGroup)) - { - return addErrorEntry("RenderPass::getRenderGroupOrder failed - render group not contained in RenderPass"); - } - - const ramses_internal::RenderGroupHandle renderGroupHandle = renderGroup.getRenderGroupHandle(); - const ramses_internal::RenderPass& internalRP = getIScene().getRenderPass(m_renderPassHandle); - for (const auto& rgEntry : internalRP.renderGroups) - { - if (rgEntry.renderGroup == renderGroupHandle) - { - orderWithinPass = rgEntry.order; - return StatusOK; - } - } - - assert(false); - return addErrorEntry("RenderPass::getRenderGroupOrder failed - fatal, render group not found in internal render pass"); - } - - const RenderGroupVector& RenderPassImpl::getAllRenderGroups() const - { - return m_renderGroups; - } - - status_t RenderPassImpl::removeAllRenderGroups() - { - while (!m_renderGroups.empty()) - { - CHECK_RETURN_ERR(remove(*m_renderGroups.front())); - } - - return StatusOK; - } - - ramses_internal::RenderPassHandle RenderPassImpl::getRenderPassHandle() const - { - return m_renderPassHandle; - } - - status_t RenderPassImpl::setRenderTarget(RenderTargetImpl* renderTargetImpl) - { - if (renderTargetImpl == m_renderTargetImpl) - return StatusOK; - - ramses_internal::RenderTargetHandle rtHandle(ramses_internal::RenderTargetHandle::Invalid()); - if (nullptr != renderTargetImpl) - { - if (nullptr == m_cameraImpl) - return addErrorEntry("RenderPass::setRenderTarget failed - must explicitly assign a custom camera (perspective or orthographic) before rendering to render terget."); - - if (!isFromTheSameSceneAs(*renderTargetImpl)) - return addErrorEntry("RenderPass::setRenderTarget failed - renderTarget is not from the same scene as this RenderPass."); - - rtHandle = renderTargetImpl->getRenderTargetHandle(); - } - - m_renderTargetImpl = renderTargetImpl; - getIScene().setRenderPassRenderTarget(m_renderPassHandle, rtHandle); - - return StatusOK; - } - - const RenderTarget* RenderPassImpl::getRenderTarget() const - { - if (m_renderTargetImpl != nullptr) - { - return &RamsesObjectTypeUtils::ConvertTo(m_renderTargetImpl->getRamsesObject()); - } - - return nullptr; - } - - status_t RenderPassImpl::setRenderOrder(int32_t renderOrder) - { - getIScene().setRenderPassRenderOrder(m_renderPassHandle, renderOrder); - return StatusOK; - } - - int32_t RenderPassImpl::getRenderOrder() const - { - return getIScene().getRenderPass(m_renderPassHandle).renderOrder; - } - - status_t RenderPassImpl::setEnabled(bool isEnabeld) - { - getIScene().setRenderPassEnabled(m_renderPassHandle, isEnabeld); - return StatusOK; - } - - bool RenderPassImpl::isEnabled() const - { - return getIScene().getRenderPass(m_renderPassHandle).isEnabled; - } - - void RenderPassImpl::removeInternal(RenderGroupVector::iterator iter) - { - const ramses_internal::RenderGroupHandle renderGroupHandle = (*iter)->getRenderGroupHandle(); - getIScene().removeRenderGroupFromRenderPass(m_renderPassHandle, renderGroupHandle); - m_renderGroups.erase(iter); - } - - status_t RenderPassImpl::setRenderOnce(bool enable) - { - getIScene().setRenderPassRenderOnce(m_renderPassHandle, enable); - return StatusOK; - } - - bool RenderPassImpl::isRenderOnce() const - { - return getIScene().getRenderPass(m_renderPassHandle).isRenderOnce; - } - - status_t RenderPassImpl::retriggerRenderOnce() - { - if (!isRenderOnce()) - { - return addErrorEntry("RenderPass::retriggerRenderOnce - cannot retrigger rendering of render pass that does not have render once flag set"); - } - - getIScene().retriggerRenderPassRenderOnce(m_renderPassHandle); - return StatusOK; - } -} diff --git a/client/ramses-client/impl/RenderPassImpl.h b/client/ramses-client/impl/RenderPassImpl.h deleted file mode 100644 index 8b5af6196..000000000 --- a/client/ramses-client/impl/RenderPassImpl.h +++ /dev/null @@ -1,93 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERPASSIMPL_H -#define RAMSES_RENDERPASSIMPL_H - -#include "SceneObjectImpl.h" -#include "SceneAPI/Handles.h" -#include "DataTypesImpl.h" - -#include -#include - -namespace ramses_internal -{ - class IScene; -} - -namespace ramses -{ - class Camera; - class CameraImpl; - class CameraNodeImpl; - class RenderGroupImpl; - class RenderPass; - class RenderTarget; - class RenderTargetImpl; - - using RenderGroupVector = std::vector; - - class RenderPassImpl final : public SceneObjectImpl - { - public: - RenderPassImpl(SceneImpl& scene, std::string_view renderpassName); - ~RenderPassImpl() override; - - void initializeFrameworkData(); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t resolveDeserializationDependencies(DeserializationContext& serializationContext) override; - status_t validate() const override; - - status_t setCamera(const CameraNodeImpl& cameraImpl); - const Camera* getCamera()const; - Camera* getCamera(); - - status_t setClearColor(const glm::vec4& clearColor); - const glm::vec4& getClearColor() const; - - status_t setClearFlags(uint32_t clearFlags); - uint32_t getClearFlags() const; - - status_t addRenderGroup(const RenderGroupImpl& renderGroup, int32_t orderWithinPass); - status_t remove(const RenderGroupImpl& renderGroup); - void removeIfContained(const RenderGroupImpl& renderGroup); - bool contains(const RenderGroupImpl& renderGroup) const; - status_t getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinPass) const; - const RenderGroupVector& getAllRenderGroups() const; - status_t removeAllRenderGroups(); - - status_t setRenderTarget(RenderTargetImpl* renderTarget); - const RenderTarget* getRenderTarget() const; - - status_t setRenderOrder(int32_t renderOrder); - int32_t getRenderOrder() const; - - status_t setEnabled(bool isEnabeld); - bool isEnabled() const; - - status_t setRenderOnce(bool enable); - bool isRenderOnce() const; - status_t retriggerRenderOnce(); - - ramses_internal::RenderPassHandle getRenderPassHandle() const; - - private: - void removeInternal(RenderGroupVector::iterator iter); - - ramses_internal::RenderPassHandle m_renderPassHandle; - const CameraNodeImpl* m_cameraImpl; - const RenderTargetImpl* m_renderTargetImpl; - - RenderGroupVector m_renderGroups; - }; -} - -#endif diff --git a/client/ramses-client/impl/RenderTargetDescriptionImpl.cpp b/client/ramses-client/impl/RenderTargetDescriptionImpl.cpp deleted file mode 100644 index 78b93530d..000000000 --- a/client/ramses-client/impl/RenderTargetDescriptionImpl.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RenderTargetDescriptionImpl.h" -#include "ramses-client-api/SceneObjectIterator.h" -#include "SceneImpl.h" -#include "RenderBufferImpl.h" -#include "Scene/ClientScene.h" - -namespace ramses -{ - RenderTargetDescriptionImpl::RenderTargetDescriptionImpl() - : m_scene(nullptr) - { - } - - RenderTargetDescriptionImpl::~RenderTargetDescriptionImpl() - { - } - - status_t RenderTargetDescriptionImpl::validate() const - { - status_t status = StatusObjectImpl::validate(); - - if (m_renderBuffers.empty()) - status = addValidationMessage(EValidationSeverity::Warning, "there is no RenderBuffer added"); - else - { - assert(m_scene != nullptr); - for(const auto& rb : m_renderBuffers) - { - if (!m_scene->getIScene().isRenderBufferAllocated(rb)) - status = addValidationMessage(EValidationSeverity::Error, "referencing one or more RenderBuffers that do not exist in scene anymore"); - } - } - - return status; - } - - status_t RenderTargetDescriptionImpl::addRenderBuffer(const RenderBufferImpl& renderBuffer) - { - const ramses_internal::RenderBufferHandle bufferHandle = renderBuffer.getRenderBufferHandle(); - if (contains_c(m_renderBuffers, bufferHandle)) - { - return addErrorEntry("RenderTargetDescription::addRenderBuffer failed: trying to add a render buffer that is already contained!"); - } - - const SceneImpl& scene = renderBuffer.getSceneImpl(); - if (m_scene != nullptr) - { - assert(!m_renderBuffers.empty()); - - if (&scene != m_scene) - { - return addErrorEntry("RenderTargetDescription::addRenderBuffer failed: all render buffers must be from the same scene!"); - } - - const ramses_internal::IScene& iscene = m_scene->getIScene(); - const ramses_internal::RenderBuffer& renderBufferData = iscene.getRenderBuffer(bufferHandle); - const ramses_internal::RenderBuffer& existingRenderBuffer = iscene.getRenderBuffer(m_renderBuffers.front()); - if (renderBufferData.width != existingRenderBuffer.width || renderBufferData.height != existingRenderBuffer.height) - { - return addErrorEntry("RenderTargetDescription::addRenderBuffer failed: all render buffers must have the same resolution!"); - } - - if (renderBufferData.type == ramses_internal::ERenderBufferType_DepthBuffer || renderBufferData.type == ramses_internal::ERenderBufferType_DepthStencilBuffer) - { - for(const auto& rb : m_renderBuffers) - { - const ramses_internal::ERenderBufferType rbType = iscene.getRenderBuffer(rb).type; - if (rbType == ramses_internal::ERenderBufferType_DepthBuffer || rbType == ramses_internal::ERenderBufferType_DepthStencilBuffer) - { - return addErrorEntry("RenderTargetDescription::addRenderBuffer failed: cannot add more than one depth/stencil buffer!"); - } - } - } - - if (existingRenderBuffer.sampleCount != renderBufferData.sampleCount) - { - return addErrorEntry("RenderTargetDescription::addRenderBuffer failed: all render buffers must have same MSAA sample count!"); - } - } - - m_renderBuffers.push_back(bufferHandle); - m_scene = &scene; - - return StatusOK; - } - - const ramses_internal::RenderBufferHandleVector& RenderTargetDescriptionImpl::getRenderBuffers() const - { - return m_renderBuffers; - } - -} diff --git a/client/ramses-client/impl/ResourceImpl.h b/client/ramses-client/impl/ResourceImpl.h deleted file mode 100644 index 57f0fd66e..000000000 --- a/client/ramses-client/impl/ResourceImpl.h +++ /dev/null @@ -1,59 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RESOURCEIMPL_H -#define RAMSES_RESOURCEIMPL_H - -// internal -#include "SceneImpl.h" - -// ramses framework -#include "SceneAPI/ResourceContentHash.h" -#include "Components/ManagedResource.h" -#include "Components/ResourceHashUsage.h" - -#include -#include - -namespace ramses_internal -{ - class ClientApplicationLogic; -} - -namespace ramses -{ - class ResourceImpl : public SceneObjectImpl - { - public: - ResourceImpl(ERamsesObjectType type, - ramses_internal::ResourceHashUsage hashUsage, - SceneImpl& scene, - std::string_view name); - ~ResourceImpl() override; - - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - resourceId_t getResourceId() const; - ramses_internal::ResourceContentHash getLowlevelResourceHash() const; - - status_t validate() const override; - status_t setName(RamsesObject& object, std::string_view name) override; - - static resourceId_t CreateResourceHash(ramses_internal::ResourceContentHash llhash, const std::string& name, ERamsesObjectType type); - - private: - void updateResourceHash(); - - ramses_internal::ResourceHashUsage m_hashUsage; - resourceId_t m_resourceId; - }; -} - -#endif diff --git a/client/ramses-client/impl/RotationTypeUtils.h b/client/ramses-client/impl/RotationTypeUtils.h deleted file mode 100644 index 6d1387c1a..000000000 --- a/client/ramses-client/impl/RotationTypeUtils.h +++ /dev/null @@ -1,66 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#pragma once - -#include "ramses-client-api/ERotationType.h" -#include "SceneAPI/ERotationType.h" -#include - -namespace ramses -{ - namespace RotationTypeUtils - { - inline ERotationType ConvertRotationTypeFromInternal(ramses_internal::ERotationType rotationType) - { - switch (rotationType) - { - case ramses_internal::ERotationType::Euler_ZYX: return ERotationType::Euler_ZYX; - case ramses_internal::ERotationType::Euler_YZX: return ERotationType::Euler_YZX; - case ramses_internal::ERotationType::Euler_ZXY: return ERotationType::Euler_ZXY; - case ramses_internal::ERotationType::Euler_XZY: return ERotationType::Euler_XZY; - case ramses_internal::ERotationType::Euler_YXZ: return ERotationType::Euler_YXZ; - case ramses_internal::ERotationType::Euler_XYZ: return ERotationType::Euler_XYZ; - case ramses_internal::ERotationType::Euler_XYX: return ERotationType::Euler_XYX; - case ramses_internal::ERotationType::Euler_XZX: return ERotationType::Euler_XZX; - case ramses_internal::ERotationType::Euler_YXY: return ERotationType::Euler_YXY; - case ramses_internal::ERotationType::Euler_YZY: return ERotationType::Euler_YZY; - case ramses_internal::ERotationType::Euler_ZXZ: return ERotationType::Euler_ZXZ; - case ramses_internal::ERotationType::Euler_ZYZ: return ERotationType::Euler_ZYZ; - case ramses_internal::ERotationType::Quaternion: return ERotationType::Quaternion; - } - - assert(false); - return ERotationType::Euler_XYZ; - } - - inline ramses_internal::ERotationType ConvertRotationTypeToInternal(ERotationType rotationType) - { - switch (rotationType) - { - case ERotationType::Euler_ZYX: return ramses_internal::ERotationType::Euler_ZYX; - case ERotationType::Euler_YZX: return ramses_internal::ERotationType::Euler_YZX; - case ERotationType::Euler_ZXY: return ramses_internal::ERotationType::Euler_ZXY; - case ERotationType::Euler_XZY: return ramses_internal::ERotationType::Euler_XZY; - case ERotationType::Euler_YXZ: return ramses_internal::ERotationType::Euler_YXZ; - case ERotationType::Euler_XYZ: return ramses_internal::ERotationType::Euler_XYZ; - case ERotationType::Euler_XYX: return ramses_internal::ERotationType::Euler_XYX; - case ERotationType::Euler_XZX: return ramses_internal::ERotationType::Euler_XZX; - case ERotationType::Euler_YXY: return ramses_internal::ERotationType::Euler_YXY; - case ERotationType::Euler_YZY: return ramses_internal::ERotationType::Euler_YZY; - case ERotationType::Euler_ZXZ: return ramses_internal::ERotationType::Euler_ZXZ; - case ERotationType::Euler_ZYZ: return ramses_internal::ERotationType::Euler_ZYZ; - case ERotationType::Quaternion: return ramses_internal::ERotationType::Quaternion; - } - - assert(false); - return ramses_internal::ERotationType::Euler_ZYZ; - } - }; -} - diff --git a/client/ramses-client/impl/SceneConfigImpl.h b/client/ramses-client/impl/SceneConfigImpl.h deleted file mode 100644 index 8f552c5d5..000000000 --- a/client/ramses-client/impl/SceneConfigImpl.h +++ /dev/null @@ -1,30 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENECONFIGIMPL_H -#define RAMSES_SCENECONFIGIMPL_H - -#include "StatusObjectImpl.h" -#include "ramses-client-api/EScenePublicationMode.h" -#include - -namespace ramses -{ - class IBinaryShaderCache; - class SceneConfigImpl : public StatusObjectImpl - { - public: - status_t setPublicationMode(EScenePublicationMode publicationMode); - EScenePublicationMode getPublicationMode() const; - - private: - EScenePublicationMode m_publicationMode = EScenePublicationMode::LocalAndRemote; - }; -} - -#endif diff --git a/client/ramses-client/impl/SceneObjectImpl.cpp b/client/ramses-client/impl/SceneObjectImpl.cpp deleted file mode 100644 index fb43a61f6..000000000 --- a/client/ramses-client/impl/SceneObjectImpl.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "SceneObjectImpl.h" -#include "SceneImpl.h" - -#include "SerializationContext.h" - -namespace ramses -{ - SceneObjectImpl::SceneObjectImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name) - : ClientObjectImpl(scene.getClientImpl(), type, name) - , m_scene(scene) - , m_sceneObjectId(scene.getNextSceneObjectId()) - { - m_scene.getStatisticCollection().statObjectsCreated.incCounter(1); - } - - SceneObjectImpl::~SceneObjectImpl() - { - m_scene.getStatisticCollection().statObjectsDestroyed.incCounter(1); - } - - const SceneImpl& SceneObjectImpl::getSceneImpl() const - { - return m_scene; - } - - SceneImpl& SceneObjectImpl::getSceneImpl() - { - return m_scene; - } - - const ramses_internal::ClientScene& SceneObjectImpl::getIScene() const - { - return m_scene.getIScene(); - } - - ramses_internal::ClientScene& SceneObjectImpl::getIScene() - { - return m_scene.getIScene(); - } - - bool SceneObjectImpl::isFromTheSameSceneAs(const SceneObjectImpl& otherObject) const - { - return &getIScene() == &(otherObject.getIScene()); - } - - status_t SceneObjectImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(RamsesObjectImpl::serialize(outStream, serializationContext)); - assert(m_sceneObjectId.isValid()); - outStream << (serializationContext.getSerializeSceneObjectIds() ? m_sceneObjectId.getValue() : sceneObjectId_t::Invalid().getValue()); - - return StatusOK; - } - - status_t SceneObjectImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(RamsesObjectImpl::deserialize(inStream, serializationContext)); - sceneObjectId_t id; - inStream >> id.getReference(); - - if (id.isValid()) - m_sceneObjectId = std::move(id); - - return StatusOK; - } - - sceneObjectId_t SceneObjectImpl::getSceneObjectId() const - { - return m_sceneObjectId; - } - - ramses::sceneId_t SceneObjectImpl::getSceneId() const - { - return m_scene.getSceneId(); - } - -} diff --git a/client/ramses-client/impl/SceneObjectImpl.h b/client/ramses-client/impl/SceneObjectImpl.h deleted file mode 100644 index 0096f8fbc..000000000 --- a/client/ramses-client/impl/SceneObjectImpl.h +++ /dev/null @@ -1,50 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENEOBJECTIMPL_H -#define RAMSES_SCENEOBJECTIMPL_H - -#include "ClientObjectImpl.h" - -#include - -namespace ramses_internal -{ - class ClientScene; -} - -namespace ramses -{ - class SceneImpl; - - class SceneObjectImpl : public ClientObjectImpl - { - public: - explicit SceneObjectImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name); - ~SceneObjectImpl() override; - - // impl methods - const SceneImpl& getSceneImpl() const; - SceneImpl& getSceneImpl(); - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - const ramses_internal::ClientScene& getIScene() const; - ramses_internal::ClientScene& getIScene(); - sceneObjectId_t getSceneObjectId() const; - sceneId_t getSceneId() const; - - bool isFromTheSameSceneAs(const SceneObjectImpl& otherObject) const; - - private: - SceneImpl& m_scene; - sceneObjectId_t m_sceneObjectId; - }; -} - -#endif diff --git a/client/ramses-client/impl/SceneReferenceImpl.cpp b/client/ramses-client/impl/SceneReferenceImpl.cpp deleted file mode 100644 index 71b35a87b..000000000 --- a/client/ramses-client/impl/SceneReferenceImpl.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "SceneReferenceImpl.h" -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "Scene/ClientScene.h" -#include "SceneAPI/RendererSceneState.h" -#include "SceneObjectImpl.h" -#include "SceneImpl.h" - -namespace ramses -{ - SceneReferenceImpl::SceneReferenceImpl(SceneImpl& scene, std::string_view name) - : SceneObjectImpl(scene, ERamsesObjectType::SceneReference, name) - { - } - - void SceneReferenceImpl::initializeFrameworkData(sceneId_t referencedScene) - { - m_sceneReferenceHandle = getIScene().allocateSceneReference(ramses_internal::SceneId{ referencedScene.getValue() }); - } - - void SceneReferenceImpl::deinitializeFrameworkData() - { - getIScene().releaseSceneReference(m_sceneReferenceHandle); - } - - ramses::status_t SceneReferenceImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << m_sceneReferenceHandle; - - return StatusOK; - } - - ramses::status_t SceneReferenceImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - inStream >> m_sceneReferenceHandle; - - return StatusOK; - } - - sceneId_t SceneReferenceImpl::getReferencedSceneId() const - { - return sceneId_t{ getIScene().getSceneReference(m_sceneReferenceHandle).sceneId.getValue() }; - } - - status_t SceneReferenceImpl::requestState(RendererSceneState requestedState) - { - if (requestedState == RendererSceneState::Unavailable) - return addErrorEntry("SceneReference::requestState: Can not request scene reference state Unavailable. In order to release the scene from renderer request Available state"); - - getIScene().requestSceneReferenceState(m_sceneReferenceHandle, GetInternalSceneReferenceState(requestedState)); - return StatusOK; - } - - - RendererSceneState SceneReferenceImpl::getRequestedState() const - { - return GetSceneReferenceState(getIScene().getSceneReference(m_sceneReferenceHandle).requestedState); - } - - status_t SceneReferenceImpl::requestNotificationsForSceneVersionTags(bool flag) - { - getIScene().requestSceneReferenceFlushNotifications(m_sceneReferenceHandle, flag); - return StatusOK; - } - - status_t SceneReferenceImpl::setRenderOrder(int32_t renderOrder) - { - getIScene().setSceneReferenceRenderOrder(m_sceneReferenceHandle, renderOrder); - return StatusOK; - } - - ramses_internal::RendererSceneState SceneReferenceImpl::GetInternalSceneReferenceState(ramses::RendererSceneState state) - { - switch (state) - { - case RendererSceneState::Unavailable: return ramses_internal::RendererSceneState::Unavailable; - case RendererSceneState::Available: return ramses_internal::RendererSceneState::Available; - case RendererSceneState::Ready: return ramses_internal::RendererSceneState::Ready; - case RendererSceneState::Rendered: return ramses_internal::RendererSceneState::Rendered; - } - return ramses_internal::RendererSceneState::Unavailable; - } - - RendererSceneState SceneReferenceImpl::GetSceneReferenceState(ramses_internal::RendererSceneState state) - { - switch (state) - { - case ramses_internal::RendererSceneState::Unavailable: return RendererSceneState::Unavailable; - case ramses_internal::RendererSceneState::Available: return RendererSceneState::Available; - case ramses_internal::RendererSceneState::Ready: return RendererSceneState::Ready; - case ramses_internal::RendererSceneState::Rendered: return RendererSceneState::Rendered; - } - return RendererSceneState::Unavailable; - } - - ramses_internal::SceneReferenceHandle SceneReferenceImpl::getSceneReferenceHandle() const - { - return m_sceneReferenceHandle; - } - - RendererSceneState SceneReferenceImpl::getReportedState() const - { - return m_reportedState; - } - - void SceneReferenceImpl::setReportedState(RendererSceneState state) - { - m_reportedState = state; - } - -} diff --git a/client/ramses-client/impl/SceneReferenceImpl.h b/client/ramses-client/impl/SceneReferenceImpl.h deleted file mode 100644 index 41b967b3e..000000000 --- a/client/ramses-client/impl/SceneReferenceImpl.h +++ /dev/null @@ -1,57 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENEREFERENCEIMPL_H -#define RAMSES_SCENEREFERENCEIMPL_H - -#include "ramses-framework-api/RendererSceneState.h" - -// internal -#include "SceneObjectImpl.h" - -// ramses framework -#include "SceneAPI/Handles.h" -#include "SceneAPI/RendererSceneState.h" - -#include - -namespace ramses -{ - class SceneReferenceImpl final : public SceneObjectImpl - { - public: - SceneReferenceImpl(SceneImpl& scene, std::string_view name); - - void initializeFrameworkData(sceneId_t referencedScene); - - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - - status_t requestState(RendererSceneState requestedState); - sceneId_t getReferencedSceneId() const; - RendererSceneState getRequestedState() const; - status_t requestNotificationsForSceneVersionTags(bool flag); - status_t setRenderOrder(int32_t renderOrder); - - ramses_internal::SceneReferenceHandle getSceneReferenceHandle() const; - - static ramses_internal::RendererSceneState GetInternalSceneReferenceState(ramses::RendererSceneState state); - static RendererSceneState GetSceneReferenceState(ramses_internal::RendererSceneState state); - - RendererSceneState getReportedState() const; - void setReportedState(RendererSceneState state); - - private: - RendererSceneState m_reportedState = RendererSceneState::Unavailable; - - ramses_internal::SceneReferenceHandle m_sceneReferenceHandle; - }; -} - -#endif diff --git a/client/ramses-client/impl/SceneUtils.h b/client/ramses-client/impl/SceneUtils.h deleted file mode 100644 index 74db7beeb..000000000 --- a/client/ramses-client/impl/SceneUtils.h +++ /dev/null @@ -1,37 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENEUTILS_H -#define RAMSES_SCENEUTILS_H - -#include - -#include "Scene/EScenePublicationMode.h" -#include "ramses-client-api/EScenePublicationMode.h" - -namespace ramses -{ - class SceneUtils - { - public: - static ramses_internal::EScenePublicationMode GetScenePublicationModeInternal(ramses::EScenePublicationMode publicationMode) - { - switch (publicationMode) - { - case ramses::EScenePublicationMode::LocalAndRemote: - return ramses_internal::EScenePublicationMode_LocalAndRemote; - case ramses::EScenePublicationMode::LocalOnly: - return ramses_internal::EScenePublicationMode_LocalOnly; - } - assert(false); - return ramses_internal::EScenePublicationMode_LocalAndRemote; - } - }; -} - -#endif diff --git a/client/ramses-client/impl/Texture2DBufferImpl.h b/client/ramses-client/impl/Texture2DBufferImpl.h deleted file mode 100644 index 90d8194eb..000000000 --- a/client/ramses-client/impl/Texture2DBufferImpl.h +++ /dev/null @@ -1,48 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_TEXTURE2DBUFFERIMPL_H -#define RAMSES_TEXTURE2DBUFFERIMPL_H - -#include "SceneObjectImpl.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/MipMapSize.h" -#include "ramses-framework-api/EDataType.h" -#include "ramses-client-api/TextureEnums.h" - -#include - -namespace ramses -{ - class Texture2DBufferImpl : public SceneObjectImpl - { - public: - Texture2DBufferImpl(SceneImpl& scene, std::string_view textureBufferName); - ~Texture2DBufferImpl() override; - - void initializeFrameworkData(const ramses_internal::MipMapDimensions& mipDimensions, ETextureFormat textureFormat); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t validate() const override; - - status_t setData(const ramses_internal::Byte* data, size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height); - size_t getMipLevelCount() const; - ETextureFormat getTexelFormat() const; - status_t getMipLevelData(size_t mipLevel, char* buffer, size_t bufferSize) const; - status_t getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const; - size_t getMipLevelDataSizeInBytes(size_t mipLevel) const; - - ramses_internal::TextureBufferHandle getTextureBufferHandle() const; - - private: - ramses_internal::TextureBufferHandle m_textureBufferHandle; - }; -} - -#endif diff --git a/client/ramses-client/impl/TextureSamplerImpl.cpp b/client/ramses-client/impl/TextureSamplerImpl.cpp deleted file mode 100644 index e8e9e33e8..000000000 --- a/client/ramses-client/impl/TextureSamplerImpl.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "Texture2DImpl.h" -#include "Texture3DImpl.h" -#include "TextureCubeImpl.h" -#include "TextureSamplerImpl.h" -#include "RamsesClientImpl.h" -#include "RenderBufferImpl.h" -#include "Texture2DBufferImpl.h" -#include "RenderTargetImpl.h" -#include "SceneImpl.h" -#include "Scene/ClientScene.h" -#include "TextureUtils.h" -#include "RamsesObjectRegistryIterator.h" -#include "DataSlotUtils.h" - -namespace ramses -{ - TextureSamplerImpl::TextureSamplerImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name) - : SceneObjectImpl(scene, type, name) - , m_textureType(ERamsesObjectType::Invalid) - { - } - - TextureSamplerImpl::~TextureSamplerImpl() - { - } - - void TextureSamplerImpl::initializeFrameworkData( - const ramses_internal::TextureSamplerStates& samplerStates, - ERamsesObjectType textureType, - ramses_internal::TextureSampler::ContentType contentType, - ramses_internal::ResourceContentHash textureHash, - ramses_internal::MemoryHandle contentHandle) - { - m_textureType = textureType; - m_textureSamplerHandle = getIScene().allocateTextureSampler({ samplerStates, contentType, textureHash, contentHandle }); - } - - void TextureSamplerImpl::deinitializeFrameworkData() - { - assert(m_textureSamplerHandle.isValid()); - getIScene().releaseTextureSampler(m_textureSamplerHandle); - m_textureSamplerHandle = ramses_internal::TextureSamplerHandle::Invalid(); - } - - status_t TextureSamplerImpl::setTextureData(const Texture2D& texture) - { - if (!isFromTheSameSceneAs(texture.m_impl)) - return addErrorEntry("TextureSampler::setTextureData failed, client texture is not from the same client as this sampler."); - - return setTextureDataInternal(ERamsesObjectType::Texture2D, ramses_internal::TextureSampler::ContentType::ClientTexture, texture.m_impl.getLowlevelResourceHash(), ramses_internal::InvalidMemoryHandle); - } - - status_t TextureSamplerImpl::setTextureData(const Texture3D& texture) - { - if (m_textureType != ERamsesObjectType::Texture3D) - return addErrorEntry("TextureSampler::setTextureData failed, changing data from non 3D texture to 3D texture is not supported. Create a new TextureSampler instead."); - - if (!isFromTheSameSceneAs(texture.m_impl)) - return addErrorEntry("TextureSampler::setTextureData failed, client texture is not from the same client as this sampler."); - - return setTextureDataInternal(ERamsesObjectType::Texture3D, ramses_internal::TextureSampler::ContentType::ClientTexture, texture.m_impl.getLowlevelResourceHash(), ramses_internal::InvalidMemoryHandle); - } - - status_t TextureSamplerImpl::setTextureData(const TextureCube& texture) - { - if (!isFromTheSameSceneAs(texture.m_impl)) - return addErrorEntry("TextureSampler::setTextureData failed, client texture is not from the same client as this sampler."); - - return setTextureDataInternal(ERamsesObjectType::TextureCube, ramses_internal::TextureSampler::ContentType::ClientTexture, texture.m_impl.getLowlevelResourceHash(), ramses_internal::InvalidMemoryHandle); - } - - status_t TextureSamplerImpl::setTextureData(const Texture2DBuffer& texture) - { - if (!getSceneImpl().containsSceneObject(texture.m_impl)) - return addErrorEntry("TextureSampler::setTextureData failed, texture2D buffer is not from the same scene as this sampler."); - - return setTextureDataInternal(ERamsesObjectType::Texture2DBuffer, ramses_internal::TextureSampler::ContentType::TextureBuffer, {}, texture.m_impl.getTextureBufferHandle().asMemoryHandle()); - } - - status_t TextureSamplerImpl::setTextureData(const RenderBuffer& texture) - { - if (!getSceneImpl().containsSceneObject(texture.m_impl)) - return addErrorEntry("TextureSampler::setTextureData failed, render buffer is not from the same scene as this sampler."); - - if (ERenderBufferAccessMode::WriteOnly == texture.m_impl.getAccessMode()) - return addErrorEntry("TextureSampler::setTextureData failed, render buffer has access mode write only."); - - return setTextureDataInternal(ERamsesObjectType::RenderBuffer, ramses_internal::TextureSampler::ContentType::RenderBuffer, {}, texture.m_impl.getRenderBufferHandle().asMemoryHandle()); - } - - status_t TextureSamplerImpl::setTextureDataInternal(ERamsesObjectType textureType, - ramses_internal::TextureSampler::ContentType contentType, - ramses_internal::ResourceContentHash textureHash, - ramses_internal::MemoryHandle contentHandle) - { - if (ramses_internal::DataSlotUtils::HasDataSlotIdForTextureSampler(getIScene(), m_textureSamplerHandle)) - // Additional logic would have to be added on renderer side to support change of content to update consumer fallback sampler state. - // Also more checks would be required here to make sure only 2D textures are involved. - return addErrorEntry("TextureSampler::setTextureData failed, changing texture sampler data for a sampler marked as texture link consumer is not supported. Create a new TextureSampler instead."); - - // With current internal logic re-creating the texture sampler instance adds minimal overhead - // and thus extra support for change of data source on internal scene level is not worth the additional complexity. - // This should be revisited whenever internal logic changes. - - const ramses_internal::TextureSamplerStates samplerStates = getIScene().getTextureSampler(m_textureSamplerHandle).states; - getIScene().releaseTextureSampler(m_textureSamplerHandle); - - // re-allocate with same handle - const auto handle = getIScene().allocateTextureSampler({ samplerStates, contentType, textureHash, contentHandle }, m_textureSamplerHandle); - UNUSED(handle); - assert(m_textureSamplerHandle == handle); - - m_textureType = textureType; - - return StatusOK; - } - - ETextureAddressMode TextureSamplerImpl::getWrapUMode() const - { - return TextureUtils::GetTextureAddressModeFromInternal(getIScene().getTextureSampler(m_textureSamplerHandle).states.m_addressModeU); - } - - ETextureAddressMode TextureSamplerImpl::getWrapVMode() const - { - return TextureUtils::GetTextureAddressModeFromInternal(getIScene().getTextureSampler(m_textureSamplerHandle).states.m_addressModeV); - } - - ETextureAddressMode TextureSamplerImpl::getWrapRMode() const - { - return TextureUtils::GetTextureAddressModeFromInternal(getIScene().getTextureSampler(m_textureSamplerHandle).states.m_addressModeR); - } - - ETextureSamplingMethod TextureSamplerImpl::getMinSamplingMethod() const - { - return TextureUtils::GetTextureSamplingFromInternal(getIScene().getTextureSampler(m_textureSamplerHandle).states.m_minSamplingMode); - } - - ETextureSamplingMethod TextureSamplerImpl::getMagSamplingMethod() const - { - return TextureUtils::GetTextureSamplingFromInternal(getIScene().getTextureSampler(m_textureSamplerHandle).states.m_magSamplingMode); - } - - uint32_t TextureSamplerImpl::getAnisotropyLevel() const - { - return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_anisotropyLevel; - } - - ramses_internal::TextureSamplerHandle TextureSamplerImpl::getTextureSamplerHandle() const - { - return m_textureSamplerHandle; - } - - ERamsesObjectType TextureSamplerImpl::getTextureType() const - { - return m_textureType; - } - - ramses_internal::EDataType TextureSamplerImpl::getTextureDataType() const - { - if (getIScene().getTextureSampler(m_textureSamplerHandle).contentType == ramses_internal::TextureSampler::ContentType::RenderBufferMS) - return ramses_internal::EDataType::TextureSampler2DMS; - - switch (m_textureType) - { - case ERamsesObjectType::Texture2D: - case ERamsesObjectType::RenderBuffer: - case ERamsesObjectType::Texture2DBuffer: return ramses_internal::EDataType::TextureSampler2D; - case ERamsesObjectType::Texture3D: return ramses_internal::EDataType::TextureSampler3D; - case ERamsesObjectType::TextureCube: return ramses_internal::EDataType::TextureSamplerCube; - case ERamsesObjectType::TextureSamplerExternal: return ramses_internal::EDataType::TextureSamplerExternal; - default: break; - } - return ramses_internal::EDataType::Invalid; - } - - status_t TextureSamplerImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const - { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); - - outStream << static_cast(m_textureType); - outStream << m_textureSamplerHandle; - - return StatusOK; - } - - status_t TextureSamplerImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) - { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); - - uint32_t value; - - inStream >> value; - m_textureType = static_cast(value); - - inStream >> m_textureSamplerHandle; - - return StatusOK; - } - - status_t TextureSamplerImpl::validate() const - { - status_t status = SceneObjectImpl::validate(); - - status_t contentStatus = StatusOK; - const Resource* resource = nullptr; - const ramses_internal::TextureSampler& sampler = getIScene().getTextureSampler(m_textureSamplerHandle); - switch (sampler.contentType) - { - case ramses_internal::TextureSampler::ContentType::ClientTexture: - resource = getSceneImpl().scanForResourceWithHash(sampler.textureResource); - if (resource == nullptr) - return addValidationMessage(EValidationSeverity::Error, "Client texture set in TextureSampler does not exist"); - contentStatus = validateResource(resource); - break; - case ramses_internal::TextureSampler::ContentType::RenderBuffer: - case ramses_internal::TextureSampler::ContentType::RenderBufferMS: - contentStatus = validateRenderBuffer(ramses_internal::RenderBufferHandle(sampler.contentHandle)); - break; - case ramses_internal::TextureSampler::ContentType::TextureBuffer: - contentStatus = validateTextureBuffer(ramses_internal::TextureBufferHandle(sampler.contentHandle)); - break; - case ramses_internal::TextureSampler::ContentType::ExternalTexture: - contentStatus = StatusOK; - break; - case ramses_internal::TextureSampler::ContentType::OffscreenBuffer: - case ramses_internal::TextureSampler::ContentType::StreamBuffer: - case ramses_internal::TextureSampler::ContentType::None: - return addValidationMessage(EValidationSeverity::Error, "There is no valid content source set in TextureSampler"); - } - - return std::max(status, contentStatus); - } - - ramses::status_t TextureSamplerImpl::validateRenderBuffer(ramses_internal::RenderBufferHandle renderBufferHandle) const - { - status_t status = StatusOK; - bool foundRenderBuffer = false; - - const bool isRenderBufferValid = getIScene().isRenderBufferAllocated(renderBufferHandle); - if (isRenderBufferValid) - { - RamsesObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::RenderBuffer); - while (const RenderBuffer* renderBuffer = iter.getNext()) - { - if (renderBufferHandle == renderBuffer->m_impl.getRenderBufferHandle()) - { - foundRenderBuffer = true; - status = addValidationOfDependentObject(renderBuffer->m_impl); - break; - } - } - } - - if (!foundRenderBuffer) - return addValidationMessage(EValidationSeverity::Error, "Texture Sampler is using a RenderBuffer which does not exist"); - - return status; - } - - ramses::status_t TextureSamplerImpl::validateTextureBuffer(ramses_internal::TextureBufferHandle textureBufferHandle) const - { - status_t status = StatusOK; - bool foundTextureBuffer = false; - - const bool isTextureBufferValid = getIScene().isTextureBufferAllocated(textureBufferHandle); - if (isTextureBufferValid) - { - RamsesObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::Texture2DBuffer); - while (const Texture2DBuffer* textureBuffer = iter.getNext()) - { - if (textureBufferHandle == textureBuffer->m_impl.getTextureBufferHandle()) - { - foundTextureBuffer = true; - status = addValidationOfDependentObject(textureBuffer->m_impl); - break; - } - } - } - - if (!foundTextureBuffer) - return addValidationMessage(EValidationSeverity::Error, "Texture Sampler is using a TextureBuffer which does not exist"); - - return status; - } - - ramses::status_t TextureSamplerImpl::validateResource(const Resource* resource) const - { - if (!resource) - return StatusOK; - - status_t textureStatus = StatusOK; - const ERamsesObjectType resourceType = resource->getType(); - switch (resourceType) - { - case ERamsesObjectType::Texture2D: - { - const Texture2D& texture = RamsesObjectTypeUtils::ConvertTo(*resource); - textureStatus = addValidationOfDependentObject(texture.m_impl); - break; - } - case ERamsesObjectType::Texture3D: - { - const Texture3D& texture = RamsesObjectTypeUtils::ConvertTo(*resource); - textureStatus = addValidationOfDependentObject(texture.m_impl); - break; - } - case ERamsesObjectType::TextureCube: - { - const TextureCube& texture = RamsesObjectTypeUtils::ConvertTo(*resource); - textureStatus = addValidationOfDependentObject(texture.m_impl); - break; - } - default: - assert(false); - break; - } - - return textureStatus; - } -} diff --git a/client/ramses-client/impl/TextureSamplerImpl.h b/client/ramses-client/impl/TextureSamplerImpl.h deleted file mode 100644 index 456d3eabf..000000000 --- a/client/ramses-client/impl/TextureSamplerImpl.h +++ /dev/null @@ -1,85 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_TEXTURESAMPLERIMPL_H -#define RAMSES_TEXTURESAMPLERIMPL_H - -#include "ramses-client-api/TextureEnums.h" - -// internal -#include "SceneObjectImpl.h" - -// ramses framework -#include "SceneAPI/Handles.h" -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/EDataType.h" - -#include - -namespace ramses -{ - class Texture2D; - class Texture3D; - class TextureCube; - class Texture2DBuffer; - class RenderBuffer; - class Resource; - - class TextureSamplerImpl final : public SceneObjectImpl - { - public: - TextureSamplerImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name); - ~TextureSamplerImpl() override; - - void initializeFrameworkData( - const ramses_internal::TextureSamplerStates& samplerStates, - ERamsesObjectType textureType, - ramses_internal::TextureSampler::ContentType contentType, - ramses_internal::ResourceContentHash textureHash, - ramses_internal::MemoryHandle contentHandle); - - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t validate() const override; - - ETextureAddressMode getWrapUMode() const; - ETextureAddressMode getWrapVMode() const; - ETextureAddressMode getWrapRMode() const; - ETextureSamplingMethod getMinSamplingMethod() const; - ETextureSamplingMethod getMagSamplingMethod() const; - uint32_t getAnisotropyLevel() const; - - status_t setTextureData(const Texture2D& texture); - status_t setTextureData(const Texture3D& texture); - status_t setTextureData(const TextureCube& texture); - status_t setTextureData(const Texture2DBuffer& texture); - status_t setTextureData(const RenderBuffer& texture); - - ramses_internal::TextureSamplerHandle getTextureSamplerHandle() const; - ramses_internal::EDataType getTextureDataType() const; - ERamsesObjectType getTextureType() const; - - private: - status_t setTextureDataInternal(ERamsesObjectType textureType, - ramses_internal::TextureSampler::ContentType contentType, - ramses_internal::ResourceContentHash textureHash, - ramses_internal::MemoryHandle contentHandle); - - status_t validateRenderBuffer(ramses_internal::RenderBufferHandle renderBufferHandle) const; - status_t validateTextureBuffer(ramses_internal::TextureBufferHandle textureBufferHandle) const; - status_t validateResource(const Resource* resource) const; - - ERamsesObjectType m_textureType; - - ramses_internal::TextureSamplerHandle m_textureSamplerHandle; - }; -} - -#endif diff --git a/client/ramses-client/impl/TextureUtils.h b/client/ramses-client/impl/TextureUtils.h deleted file mode 100644 index accfabe5c..000000000 --- a/client/ramses-client/impl/TextureUtils.h +++ /dev/null @@ -1,746 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_TEXTUREUTILS_H -#define RAMSES_TEXTUREUTILS_H - -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "SceneAPI/TextureEnums.h" -#include "Resource/TextureMetaInfo.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "Utils/LogMacros.h" - -namespace ramses -{ - class Texture2D; - - class TextureUtils - { - public: - static ramses_internal::ETextureFormat GetTextureFormatInternal(ETextureFormat textureformat) - { - switch (textureformat) - { - case ETextureFormat::R8: - return ramses_internal::ETextureFormat::R8; - - case ETextureFormat::RG8: - return ramses_internal::ETextureFormat::RG8; - - case ETextureFormat::RGB8: - return ramses_internal::ETextureFormat::RGB8; - case ETextureFormat::RGB565: - return ramses_internal::ETextureFormat::RGB565; - - case ETextureFormat::RGBA8: - return ramses_internal::ETextureFormat::RGBA8; - case ETextureFormat::RGBA4: - return ramses_internal::ETextureFormat::RGBA4; - case ETextureFormat::RGBA5551: - return ramses_internal::ETextureFormat::RGBA5551; - - case ETextureFormat::ETC2RGB: - return ramses_internal::ETextureFormat::ETC2RGB; - case ETextureFormat::ETC2RGBA: - return ramses_internal::ETextureFormat::ETC2RGBA; - case ETextureFormat::ASTC_RGBA_4x4: - return ramses_internal::ETextureFormat::ASTC_RGBA_4x4; - case ETextureFormat::ASTC_RGBA_5x4: - return ramses_internal::ETextureFormat::ASTC_RGBA_5x4; - case ETextureFormat::ASTC_RGBA_5x5: - return ramses_internal::ETextureFormat::ASTC_RGBA_5x5; - case ETextureFormat::ASTC_RGBA_6x5: - return ramses_internal::ETextureFormat::ASTC_RGBA_6x5; - case ETextureFormat::ASTC_RGBA_6x6: - return ramses_internal::ETextureFormat::ASTC_RGBA_6x6; - case ETextureFormat::ASTC_RGBA_8x5: - return ramses_internal::ETextureFormat::ASTC_RGBA_8x5; - case ETextureFormat::ASTC_RGBA_8x6: - return ramses_internal::ETextureFormat::ASTC_RGBA_8x6; - case ETextureFormat::ASTC_RGBA_8x8: - return ramses_internal::ETextureFormat::ASTC_RGBA_8x8; - case ETextureFormat::ASTC_RGBA_10x5: - return ramses_internal::ETextureFormat::ASTC_RGBA_10x5; - case ETextureFormat::ASTC_RGBA_10x6: - return ramses_internal::ETextureFormat::ASTC_RGBA_10x6; - case ETextureFormat::ASTC_RGBA_10x8: - return ramses_internal::ETextureFormat::ASTC_RGBA_10x8; - case ETextureFormat::ASTC_RGBA_10x10: - return ramses_internal::ETextureFormat::ASTC_RGBA_10x10; - case ETextureFormat::ASTC_RGBA_12x10: - return ramses_internal::ETextureFormat::ASTC_RGBA_12x10; - case ETextureFormat::ASTC_RGBA_12x12: - return ramses_internal::ETextureFormat::ASTC_RGBA_12x12; - case ETextureFormat::ASTC_SRGBA_4x4: - return ramses_internal::ETextureFormat::ASTC_SRGBA_4x4; - case ETextureFormat::ASTC_SRGBA_5x4: - return ramses_internal::ETextureFormat::ASTC_SRGBA_5x4; - case ETextureFormat::ASTC_SRGBA_5x5: - return ramses_internal::ETextureFormat::ASTC_SRGBA_5x5; - case ETextureFormat::ASTC_SRGBA_6x5: - return ramses_internal::ETextureFormat::ASTC_SRGBA_6x5; - case ETextureFormat::ASTC_SRGBA_6x6: - return ramses_internal::ETextureFormat::ASTC_SRGBA_6x6; - case ETextureFormat::ASTC_SRGBA_8x5: - return ramses_internal::ETextureFormat::ASTC_SRGBA_8x5; - case ETextureFormat::ASTC_SRGBA_8x6: - return ramses_internal::ETextureFormat::ASTC_SRGBA_8x6; - case ETextureFormat::ASTC_SRGBA_8x8: - return ramses_internal::ETextureFormat::ASTC_SRGBA_8x8; - case ETextureFormat::ASTC_SRGBA_10x5: - return ramses_internal::ETextureFormat::ASTC_SRGBA_10x5; - case ETextureFormat::ASTC_SRGBA_10x6: - return ramses_internal::ETextureFormat::ASTC_SRGBA_10x6; - case ETextureFormat::ASTC_SRGBA_10x8: - return ramses_internal::ETextureFormat::ASTC_SRGBA_10x8; - case ETextureFormat::ASTC_SRGBA_10x10: - return ramses_internal::ETextureFormat::ASTC_SRGBA_10x10; - case ETextureFormat::ASTC_SRGBA_12x10: - return ramses_internal::ETextureFormat::ASTC_SRGBA_12x10; - case ETextureFormat::ASTC_SRGBA_12x12: - return ramses_internal::ETextureFormat::ASTC_SRGBA_12x12; - case ETextureFormat::R16F: - return ramses_internal::ETextureFormat::R16F; - case ETextureFormat::R32F: - return ramses_internal::ETextureFormat::R32F; - case ETextureFormat::RG16F: - return ramses_internal::ETextureFormat::RG16F; - case ETextureFormat::RG32F: - return ramses_internal::ETextureFormat::RG32F; - case ETextureFormat::RGB16F: - return ramses_internal::ETextureFormat::RGB16F; - case ETextureFormat::RGB32F: - return ramses_internal::ETextureFormat::RGB32F; - case ETextureFormat::RGBA16F: - return ramses_internal::ETextureFormat::RGBA16F; - case ETextureFormat::RGBA32F: - return ramses_internal::ETextureFormat::RGBA32F; - - case ETextureFormat::SRGB8: - return ramses_internal::ETextureFormat::SRGB8; - case ETextureFormat::SRGB8_ALPHA8: - return ramses_internal::ETextureFormat::SRGB8_ALPHA8; - case ETextureFormat::Invalid: - break; - } - - assert(false); - return ramses_internal::ETextureFormat::RGBA8; - } - - static ETextureFormat GetTextureFormatFromInternal(ramses_internal::ETextureFormat textureformat) - { - switch (textureformat) - { - case ramses_internal::ETextureFormat::R8: - return ETextureFormat::R8; - - case ramses_internal::ETextureFormat::RG8: - return ETextureFormat::RG8; - - case ramses_internal::ETextureFormat::RGB8: - return ETextureFormat::RGB8; - case ramses_internal::ETextureFormat::RGB565: - return ETextureFormat::RGB565; - - case ramses_internal::ETextureFormat::RGBA8: - return ETextureFormat::RGBA8; - case ramses_internal::ETextureFormat::RGBA4: - return ETextureFormat::RGBA4; - case ramses_internal::ETextureFormat::RGBA5551: - return ETextureFormat::RGBA5551; - - case ramses_internal::ETextureFormat::ETC2RGB: - return ETextureFormat::ETC2RGB; - case ramses_internal::ETextureFormat::ETC2RGBA: - return ETextureFormat::ETC2RGBA; - - case ramses_internal::ETextureFormat::ASTC_RGBA_4x4: - return ETextureFormat::ASTC_RGBA_4x4; - case ramses_internal::ETextureFormat::ASTC_RGBA_5x4: - return ETextureFormat::ASTC_RGBA_5x4; - case ramses_internal::ETextureFormat::ASTC_RGBA_5x5: - return ETextureFormat::ASTC_RGBA_5x5; - case ramses_internal::ETextureFormat::ASTC_RGBA_6x5: - return ETextureFormat::ASTC_RGBA_6x5; - case ramses_internal::ETextureFormat::ASTC_RGBA_6x6: - return ETextureFormat::ASTC_RGBA_6x6; - case ramses_internal::ETextureFormat::ASTC_RGBA_8x5: - return ETextureFormat::ASTC_RGBA_8x5; - case ramses_internal::ETextureFormat::ASTC_RGBA_8x6: - return ETextureFormat::ASTC_RGBA_8x6; - case ramses_internal::ETextureFormat::ASTC_RGBA_8x8: - return ETextureFormat::ASTC_RGBA_8x8; - case ramses_internal::ETextureFormat::ASTC_RGBA_10x5: - return ETextureFormat::ASTC_RGBA_10x5; - case ramses_internal::ETextureFormat::ASTC_RGBA_10x6: - return ETextureFormat::ASTC_RGBA_10x6; - case ramses_internal::ETextureFormat::ASTC_RGBA_10x8: - return ETextureFormat::ASTC_RGBA_10x8; - case ramses_internal::ETextureFormat::ASTC_RGBA_10x10: - return ETextureFormat::ASTC_RGBA_10x10; - case ramses_internal::ETextureFormat::ASTC_RGBA_12x10: - return ETextureFormat::ASTC_RGBA_12x10; - case ramses_internal::ETextureFormat::ASTC_RGBA_12x12: - return ETextureFormat::ASTC_RGBA_12x12; - case ramses_internal::ETextureFormat::ASTC_SRGBA_4x4: - return ETextureFormat::ASTC_SRGBA_4x4; - case ramses_internal::ETextureFormat::ASTC_SRGBA_5x4: - return ETextureFormat::ASTC_SRGBA_5x4; - case ramses_internal::ETextureFormat::ASTC_SRGBA_5x5: - return ETextureFormat::ASTC_SRGBA_5x5; - case ramses_internal::ETextureFormat::ASTC_SRGBA_6x5: - return ETextureFormat::ASTC_SRGBA_6x5; - case ramses_internal::ETextureFormat::ASTC_SRGBA_6x6: - return ETextureFormat::ASTC_SRGBA_6x6; - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x5: - return ETextureFormat::ASTC_SRGBA_8x5; - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x6: - return ETextureFormat::ASTC_SRGBA_8x6; - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x8: - return ETextureFormat::ASTC_SRGBA_8x8; - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x5: - return ETextureFormat::ASTC_SRGBA_10x5; - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x6: - return ETextureFormat::ASTC_SRGBA_10x6; - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x8: - return ETextureFormat::ASTC_SRGBA_10x8; - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x10: - return ETextureFormat::ASTC_SRGBA_10x10; - case ramses_internal::ETextureFormat::ASTC_SRGBA_12x10: - return ETextureFormat::ASTC_SRGBA_12x10; - case ramses_internal::ETextureFormat::ASTC_SRGBA_12x12: - return ETextureFormat::ASTC_SRGBA_12x12; - - case ramses_internal::ETextureFormat::R16F: - return ETextureFormat::R16F; - case ramses_internal::ETextureFormat::R32F: - return ETextureFormat::R32F; - case ramses_internal::ETextureFormat::RG16F: - return ETextureFormat::RG16F; - case ramses_internal::ETextureFormat::RG32F: - return ETextureFormat::RG32F; - case ramses_internal::ETextureFormat::RGB16F: - return ETextureFormat::RGB16F; - case ramses_internal::ETextureFormat::RGB32F: - return ETextureFormat::RGB32F; - case ramses_internal::ETextureFormat::RGBA16F: - return ETextureFormat::RGBA16F; - case ramses_internal::ETextureFormat::RGBA32F: - return ETextureFormat::RGBA32F; - - case ramses_internal::ETextureFormat::SRGB8: - return ETextureFormat::SRGB8; - case ramses_internal::ETextureFormat::SRGB8_ALPHA8: - return ETextureFormat::SRGB8_ALPHA8; - case ramses_internal::ETextureFormat::Invalid: - case ramses_internal::ETextureFormat::R16: - case ramses_internal::ETextureFormat::RG16: - case ramses_internal::ETextureFormat::RGB16: - case ramses_internal::ETextureFormat::RGBA16: - case ramses_internal::ETextureFormat::DXT1RGB: - case ramses_internal::ETextureFormat::DXT3RGBA: - case ramses_internal::ETextureFormat::DXT5RGBA: - case ramses_internal::ETextureFormat::Depth16: - case ramses_internal::ETextureFormat::Depth24: - case ramses_internal::ETextureFormat::Depth24_Stencil8: - case ramses_internal::ETextureFormat::NUMBER_OF_TYPES: - break; - } - assert(false); - return ETextureFormat::RGBA8; - } - - static ETextureChannelColor GetTextureChannelColorFromInternal(ramses_internal::ETextureChannelColor textureChannelColor) - { - switch (textureChannelColor) - { - case ramses_internal::ETextureChannelColor::Red: - return ETextureChannelColor::Red; - case ramses_internal::ETextureChannelColor::Green: - return ETextureChannelColor::Green; - case ramses_internal::ETextureChannelColor::Blue: - return ETextureChannelColor::Blue; - case ramses_internal::ETextureChannelColor::Alpha: - return ETextureChannelColor::Alpha; - case ramses_internal::ETextureChannelColor::One: - return ETextureChannelColor::One; - case ramses_internal::ETextureChannelColor::Zero: - return ETextureChannelColor::Zero; - case ramses_internal::ETextureChannelColor::NUMBER_OF_ELEMENTS: - break; - } - assert(false); - return ETextureChannelColor::Red; - } - - static ramses_internal::ETextureChannelColor GetTextureChannelColorInternal(ETextureChannelColor textureChannelColor) - { - switch (textureChannelColor) - { - case ETextureChannelColor::Red: - return ramses_internal::ETextureChannelColor::Red; - case ETextureChannelColor::Green: - return ramses_internal::ETextureChannelColor::Green; - case ETextureChannelColor::Blue: - return ramses_internal::ETextureChannelColor::Blue; - case ETextureChannelColor::Alpha: - return ramses_internal::ETextureChannelColor::Alpha; - case ETextureChannelColor::One: - return ramses_internal::ETextureChannelColor::One; - case ETextureChannelColor::Zero: - return ramses_internal::ETextureChannelColor::Zero; - } - assert(false); - return ramses_internal::ETextureChannelColor::Red; - } - - static ramses_internal::TextureSwizzleArray GetTextureSwizzleInternal(const TextureSwizzle& swizzle) - { - return ramses_internal::TextureSwizzleArray{ - GetTextureChannelColorInternal(swizzle.channelRed), - GetTextureChannelColorInternal(swizzle.channelGreen), - GetTextureChannelColorInternal(swizzle.channelBlue), - GetTextureChannelColorInternal(swizzle.channelAlpha) - }; - } - - static TextureSwizzle GetTextureSwizzleFromInternal(const ramses_internal::TextureSwizzleArray& swizzle) - { - return TextureSwizzle{ - GetTextureChannelColorFromInternal(swizzle[0]), - GetTextureChannelColorFromInternal(swizzle[1]), - GetTextureChannelColorFromInternal(swizzle[2]), - GetTextureChannelColorFromInternal(swizzle[3]) - }; - } - - static bool IsTextureSizeSupportedByFormat(uint32_t width, uint32_t height, ETextureFormat textureformat) - { - switch (textureformat) - { - case ETextureFormat::R8: - case ETextureFormat::RG8: - case ETextureFormat::RGB8: - case ETextureFormat::RGB565: - case ETextureFormat::RGBA8: - case ETextureFormat::RGBA4: - case ETextureFormat::RGBA5551: - case ETextureFormat::R16F: - case ETextureFormat::R32F: - case ETextureFormat::RG16F: - case ETextureFormat::RG32F: - case ETextureFormat::RGB16F: - case ETextureFormat::RGB32F: - case ETextureFormat::RGBA16F: - case ETextureFormat::RGBA32F: - case ETextureFormat::SRGB8: - case ETextureFormat::SRGB8_ALPHA8: - // no special requirements - return true; - - case ETextureFormat::ETC2RGB: - case ETextureFormat::ETC2RGBA: - case ETextureFormat::ASTC_RGBA_4x4: - case ETextureFormat::ASTC_SRGBA_4x4: - return (width % 4 == 0) && (height % 4 == 0); - - case ETextureFormat::ASTC_RGBA_5x4: - case ETextureFormat::ASTC_SRGBA_5x4: - return (width % 5 == 0) && (height % 4 == 0); - - case ETextureFormat::ASTC_RGBA_5x5: - case ETextureFormat::ASTC_SRGBA_5x5: - return (width % 5 == 0) && (height % 5 == 0); - - case ETextureFormat::ASTC_RGBA_6x5: - case ETextureFormat::ASTC_SRGBA_6x5: - return (width % 6 == 0) && (height % 5 == 0); - - case ETextureFormat::ASTC_RGBA_6x6: - case ETextureFormat::ASTC_SRGBA_6x6: - return (width % 6 == 0) && (height % 6 == 0); - - case ETextureFormat::ASTC_RGBA_8x5: - case ETextureFormat::ASTC_SRGBA_8x5: - return (width % 8 == 0) && (height % 5 == 0); - - case ETextureFormat::ASTC_RGBA_8x6: - case ETextureFormat::ASTC_SRGBA_8x6: - return (width % 8 == 0) && (height % 6 == 0); - - case ETextureFormat::ASTC_RGBA_8x8: - case ETextureFormat::ASTC_SRGBA_8x8: - return (width % 8 == 0) && (height % 8 == 0); - - case ETextureFormat::ASTC_RGBA_10x5: - case ETextureFormat::ASTC_SRGBA_10x5: - return (width % 10 == 0) && (height % 5 == 0); - - case ETextureFormat::ASTC_RGBA_10x6: - case ETextureFormat::ASTC_SRGBA_10x6: - return (width % 10 == 0) && (height % 6 == 0); - - case ETextureFormat::ASTC_RGBA_10x8: - case ETextureFormat::ASTC_SRGBA_10x8: - return (width % 10 == 0) && (height % 8 == 0); - - case ETextureFormat::ASTC_RGBA_10x10: - case ETextureFormat::ASTC_SRGBA_10x10: - return (width % 10 == 0) && (height % 10 == 0); - - case ETextureFormat::ASTC_RGBA_12x10: - case ETextureFormat::ASTC_SRGBA_12x10: - return (width % 12 == 0) && (height % 10 == 0); - - case ETextureFormat::ASTC_RGBA_12x12: - case ETextureFormat::ASTC_SRGBA_12x12: - return (width % 12 == 0) && (height % 12 == 0); - - case ETextureFormat::Invalid: - assert(false); - return false; - } - - assert(false); - return false; - } - - static ramses_internal::EWrapMethod GetTextureAddressModeInternal(ETextureAddressMode addressMode) - { - switch (addressMode) - { - case ETextureAddressMode::Clamp: - return ramses_internal::EWrapMethod::Clamp; - case ETextureAddressMode::Repeat: - return ramses_internal::EWrapMethod::Repeat; - case ETextureAddressMode::Mirror: - return ramses_internal::EWrapMethod::RepeatMirrored; - } - assert(false); - return ramses_internal::EWrapMethod::Clamp; - } - - static ETextureAddressMode GetTextureAddressModeFromInternal(ramses_internal::EWrapMethod addressMode) - { - switch (addressMode) - { - case ramses_internal::EWrapMethod::Clamp: - return ETextureAddressMode::Clamp; - case ramses_internal::EWrapMethod::Repeat: - return ETextureAddressMode::Repeat; - case ramses_internal::EWrapMethod::RepeatMirrored: - return ETextureAddressMode::Mirror; - case ramses_internal::EWrapMethod::NUMBER_OF_ELEMENTS: - break; - } - assert(false); - return ETextureAddressMode::Clamp; - } - - static ramses_internal::ETextureCubeFace GetTextureCubeFaceInternal(ETextureCubeFace face) - { - switch (face) - { - case ETextureCubeFace::PositiveX: - return ramses_internal::ETextureCubeFace_PositiveX; - case ETextureCubeFace::NegativeX: - return ramses_internal::ETextureCubeFace_NegativeX; - case ETextureCubeFace::PositiveY: - return ramses_internal::ETextureCubeFace_PositiveY; - case ETextureCubeFace::NegativeY: - return ramses_internal::ETextureCubeFace_NegativeY; - case ETextureCubeFace::PositiveZ: - return ramses_internal::ETextureCubeFace_PositiveZ; - case ETextureCubeFace::NegativeZ: - return ramses_internal::ETextureCubeFace_NegativeZ; - } - assert(false); - return ramses_internal::ETextureCubeFace_PositiveX; - } - - static ramses_internal::ESamplingMethod GetTextureSamplingInternal(ETextureSamplingMethod sampling) - { - switch (sampling) - { - case ETextureSamplingMethod::Nearest: - return ramses_internal::ESamplingMethod::Nearest; - case ETextureSamplingMethod::Nearest_MipMapNearest: - return ramses_internal::ESamplingMethod::Nearest_MipMapNearest; - case ETextureSamplingMethod::Nearest_MipMapLinear: - return ramses_internal::ESamplingMethod::Nearest_MipMapLinear; - case ETextureSamplingMethod::Linear: - return ramses_internal::ESamplingMethod::Linear; - case ETextureSamplingMethod::Linear_MipMapNearest: - return ramses_internal::ESamplingMethod::Linear_MipMapNearest; - case ETextureSamplingMethod::Linear_MipMapLinear: - return ramses_internal::ESamplingMethod::Linear_MipMapLinear; - } - assert(false); - return ramses_internal::ESamplingMethod::Linear; - } - - static ETextureSamplingMethod GetTextureSamplingFromInternal(ramses_internal::ESamplingMethod sampling) - { - switch (sampling) - { - case ramses_internal::ESamplingMethod::Nearest: - return ETextureSamplingMethod::Nearest; - case ramses_internal::ESamplingMethod::Nearest_MipMapNearest: - return ETextureSamplingMethod::Nearest_MipMapNearest; - case ramses_internal::ESamplingMethod::Nearest_MipMapLinear: - return ETextureSamplingMethod::Nearest_MipMapLinear; - case ramses_internal::ESamplingMethod::Linear: - return ETextureSamplingMethod::Linear; - case ramses_internal::ESamplingMethod::Linear_MipMapNearest: - return ETextureSamplingMethod::Linear_MipMapNearest; - case ramses_internal::ESamplingMethod::Linear_MipMapLinear: - return ETextureSamplingMethod::Linear_MipMapLinear; - case ramses_internal::ESamplingMethod::NUMBER_OF_ELEMENTS: - break; - } - assert(false); - return ETextureSamplingMethod::Linear; - } - - static ramses_internal::ERenderBufferType GetRenderBufferTypeInternal(ERenderBufferType bufferType) - { - switch (bufferType) - { - case ERenderBufferType::Color: - return ramses_internal::ERenderBufferType_ColorBuffer; - case ERenderBufferType::Depth: - return ramses_internal::ERenderBufferType_DepthBuffer; - case ERenderBufferType::DepthStencil: - return ramses_internal::ERenderBufferType_DepthStencilBuffer; - } - assert(false); - return ramses_internal::ERenderBufferType_InvalidBuffer; - } - - static ERenderBufferType GetRenderBufferTypeFromInternal(ramses_internal::ERenderBufferType bufferType) - { - switch (bufferType) - { - case ramses_internal::ERenderBufferType_ColorBuffer: - return ERenderBufferType::Color; - case ramses_internal::ERenderBufferType_DepthBuffer: - return ERenderBufferType::Depth; - case ramses_internal::ERenderBufferType_DepthStencilBuffer: - return ERenderBufferType::DepthStencil; - case ramses_internal::ERenderBufferType_InvalidBuffer: - case ramses_internal::ERenderBufferType_NUMBER_OF_ELEMENTS: - break; - } - assert(false); - return ERenderBufferType::Color; - } - - static ramses_internal::ETextureFormat GetRenderBufferFormatInternal(ERenderBufferFormat bufferFormat) - { - switch (bufferFormat) - { - case ramses::ERenderBufferFormat::RGBA4: - return ramses_internal::ETextureFormat::RGBA4; - case ramses::ERenderBufferFormat::R8: - return ramses_internal::ETextureFormat::R8; - case ramses::ERenderBufferFormat::RG8: - return ramses_internal::ETextureFormat::RG8; - case ramses::ERenderBufferFormat::RGB8: - return ramses_internal::ETextureFormat::RGB8; - case ramses::ERenderBufferFormat::RGBA8: - return ramses_internal::ETextureFormat::RGBA8; - case ramses::ERenderBufferFormat::R16F: - return ramses_internal::ETextureFormat::R16F; - case ramses::ERenderBufferFormat::R32F: - return ramses_internal::ETextureFormat::R32F; - case ramses::ERenderBufferFormat::RG16F: - return ramses_internal::ETextureFormat::RG16F; - case ramses::ERenderBufferFormat::RG32F: - return ramses_internal::ETextureFormat::RG32F; - case ramses::ERenderBufferFormat::RGB16F: - return ramses_internal::ETextureFormat::RGB16F; - case ramses::ERenderBufferFormat::RGB32F: - return ramses_internal::ETextureFormat::RGB32F; - case ramses::ERenderBufferFormat::RGBA16F: - return ramses_internal::ETextureFormat::RGBA16F; - case ramses::ERenderBufferFormat::RGBA32F: - return ramses_internal::ETextureFormat::RGBA32F; - case ramses::ERenderBufferFormat::Depth24: - return ramses_internal::ETextureFormat::Depth24; - case ramses::ERenderBufferFormat::Depth24_Stencil8: - return ramses_internal::ETextureFormat::Depth24_Stencil8; - } - assert(false); - return ramses_internal::ETextureFormat::Invalid; - } - - static ramses_internal::ERenderBufferAccessMode GetRenderBufferAccessModeInternal(ERenderBufferAccessMode accessMode) - { - switch (accessMode) - { - case ramses::ERenderBufferAccessMode::WriteOnly: - return ramses_internal::ERenderBufferAccessMode_WriteOnly; - case ramses::ERenderBufferAccessMode::ReadWrite: - return ramses_internal::ERenderBufferAccessMode_ReadWrite; - } - assert(false); - return ramses_internal::ERenderBufferAccessMode_Invalid; - } - - static ERenderBufferFormat GetRenderBufferFormatFromInternal(ramses_internal::ETextureFormat bufferFormat) - { - switch (bufferFormat) - { - case ramses_internal::ETextureFormat::RGBA4: - return ERenderBufferFormat::RGBA4; - case ramses_internal::ETextureFormat::R8: - return ERenderBufferFormat::R8; - case ramses_internal::ETextureFormat::RG8: - return ERenderBufferFormat::RG8; - case ramses_internal::ETextureFormat::RGB8: - return ERenderBufferFormat::RGB8; - case ramses_internal::ETextureFormat::RGBA8: - return ERenderBufferFormat::RGBA8; - case ramses_internal::ETextureFormat::R16F: - return ERenderBufferFormat::R16F; - case ramses_internal::ETextureFormat::R32F: - return ERenderBufferFormat::R32F; - case ramses_internal::ETextureFormat::RG16F: - return ERenderBufferFormat::RG16F; - case ramses_internal::ETextureFormat::RG32F: - return ERenderBufferFormat::RG32F; - case ramses_internal::ETextureFormat::RGB16F: - return ERenderBufferFormat::RGB16F; - case ramses_internal::ETextureFormat::RGB32F: - return ERenderBufferFormat::RGB32F; - case ramses_internal::ETextureFormat::RGBA16F: - return ERenderBufferFormat::RGBA16F; - case ramses_internal::ETextureFormat::RGBA32F: - return ERenderBufferFormat::RGBA32F; - - case ramses_internal::ETextureFormat::Depth24: - return ERenderBufferFormat::Depth24; - case ramses_internal::ETextureFormat::Depth24_Stencil8: - return ERenderBufferFormat::Depth24_Stencil8; - case ramses_internal::ETextureFormat::Invalid: - case ramses_internal::ETextureFormat::RGB565: - case ramses_internal::ETextureFormat::RGBA5551: - case ramses_internal::ETextureFormat::ETC2RGB: - case ramses_internal::ETextureFormat::ETC2RGBA: - case ramses_internal::ETextureFormat::ASTC_RGBA_4x4: - case ramses_internal::ETextureFormat::ASTC_RGBA_5x4: - case ramses_internal::ETextureFormat::ASTC_RGBA_5x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_6x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_6x6: - case ramses_internal::ETextureFormat::ASTC_RGBA_8x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_8x6: - case ramses_internal::ETextureFormat::ASTC_RGBA_8x8: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x6: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x8: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x10: - case ramses_internal::ETextureFormat::ASTC_RGBA_12x10: - case ramses_internal::ETextureFormat::ASTC_RGBA_12x12: - case ramses_internal::ETextureFormat::ASTC_SRGBA_4x4: - case ramses_internal::ETextureFormat::ASTC_SRGBA_5x4: - case ramses_internal::ETextureFormat::ASTC_SRGBA_5x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_6x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_6x6: - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x6: - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x8: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x6: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x8: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x10: - case ramses_internal::ETextureFormat::ASTC_SRGBA_12x10: - case ramses_internal::ETextureFormat::ASTC_SRGBA_12x12: - case ramses_internal::ETextureFormat::SRGB8: - case ramses_internal::ETextureFormat::SRGB8_ALPHA8: - case ramses_internal::ETextureFormat::R16: - case ramses_internal::ETextureFormat::RG16: - case ramses_internal::ETextureFormat::RGB16: - case ramses_internal::ETextureFormat::RGBA16: - case ramses_internal::ETextureFormat::DXT1RGB: - case ramses_internal::ETextureFormat::DXT3RGBA: - case ramses_internal::ETextureFormat::DXT5RGBA: - case ramses_internal::ETextureFormat::Depth16: - case ramses_internal::ETextureFormat::NUMBER_OF_TYPES: - break; - } - assert(false); - return ERenderBufferFormat::RGBA8; - } - - static ERenderBufferAccessMode GetRenderBufferAccessModeFromInternal(ramses_internal::ERenderBufferAccessMode accessMode) - { - switch (accessMode) - { - case ramses_internal::ERenderBufferAccessMode_WriteOnly: - return ERenderBufferAccessMode::WriteOnly; - case ramses_internal::ERenderBufferAccessMode_ReadWrite: - return ERenderBufferAccessMode::ReadWrite; - case ramses_internal::ERenderBufferAccessMode_Invalid: - case ramses_internal::ERenderBufferAccessMode_NUMBER_OF_ELEMENTS: - break; - } - assert(false); - return ERenderBufferAccessMode::ReadWrite; - } - - static bool IsRenderBufferTypeCompatibleWithFormat(ERenderBufferType bufferType, ERenderBufferFormat bufferFormat) - { - switch (bufferType) - { - case ERenderBufferType::Color: - return - bufferFormat == ERenderBufferFormat::RGBA4 || - bufferFormat == ERenderBufferFormat::R8 || - bufferFormat == ERenderBufferFormat::RG8 || - bufferFormat == ERenderBufferFormat::RGB8 || - bufferFormat == ERenderBufferFormat::RGBA8 || - bufferFormat == ERenderBufferFormat::R16F || - bufferFormat == ERenderBufferFormat::R32F || - bufferFormat == ERenderBufferFormat::RG16F || - bufferFormat == ERenderBufferFormat::RG32F || - bufferFormat == ERenderBufferFormat::RGB16F || - bufferFormat == ERenderBufferFormat::RGB32F || - bufferFormat == ERenderBufferFormat::RGBA16F || - bufferFormat == ERenderBufferFormat::RGBA32F; - case ERenderBufferType::Depth: - return bufferFormat == ERenderBufferFormat::Depth24; - case ERenderBufferType::DepthStencil: - return bufferFormat == ERenderBufferFormat::Depth24_Stencil8; - } - assert(false); - return false; - } - - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipDataSizes(ramses_internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const MipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipDataSizes(ramses_internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipData(uint8_t* dest, uint32_t mipMapCount, const MipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipData(uint8_t* dest, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const MipLevelData mipLevelData[], ETextureFormat format); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static bool MipDataValid(uint32_t size, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format); - static bool TextureParametersValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount); - }; -} - -#endif diff --git a/client/ramses-client/impl/VisibilityModeUtils.cpp b/client/ramses-client/impl/VisibilityModeUtils.cpp deleted file mode 100644 index 99aeb9e5d..000000000 --- a/client/ramses-client/impl/VisibilityModeUtils.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "VisibilityModeUtils.h" -#include "Utils/LoggingUtils.h" - -namespace ramses -{ - ramses::EVisibilityMode VisibilityModeUtils::ConvertToHL(ramses_internal::EVisibilityMode mode) - { - switch (mode) - { - case ramses_internal::EVisibilityMode::Off: - return EVisibilityMode::Off; - case ramses_internal::EVisibilityMode::Invisible: - return EVisibilityMode::Invisible; - case ramses_internal::EVisibilityMode::Visible: - return EVisibilityMode::Visible; - } - assert(!"unreachable code"); - return EVisibilityMode::Visible; - } - - ramses_internal::EVisibilityMode VisibilityModeUtils::ConvertToLL(EVisibilityMode mode) - { - switch (mode) - { - case EVisibilityMode::Off: - return ramses_internal::EVisibilityMode::Off; - case EVisibilityMode::Invisible: - return ramses_internal::EVisibilityMode::Invisible; - case EVisibilityMode::Visible: - return ramses_internal::EVisibilityMode::Visible; - } - assert(!"unreachable code"); - return ramses_internal::EVisibilityMode::Visible; - } - - const std::array VisibilityModeNames = - { - "Off", - "Invisible", - "Visible" - }; - ENUM_TO_STRING(EVisibilityMode, VisibilityModeNames, 3); - - const char* VisibilityModeUtils::ToString(EVisibilityMode mode) - { - return EnumToString(mode); - } -} diff --git a/client/ramses-client/impl/VisibilityModeUtils.h b/client/ramses-client/impl/VisibilityModeUtils.h deleted file mode 100644 index 4bdf6fd68..000000000 --- a/client/ramses-client/impl/VisibilityModeUtils.h +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_VISIBILITYMODEUTILS_H -#define RAMSES_VISIBILITYMODEUTILS_H - -#include "ramses-client-api/EVisibilityMode.h" -#include "SceneAPI/Renderable.h" - -namespace ramses -{ - class VisibilityModeUtils - { - public: - static ramses::EVisibilityMode ConvertToHL(ramses_internal::EVisibilityMode mode); - static ramses_internal::EVisibilityMode ConvertToLL(EVisibilityMode mode); - static const char* ToString(EVisibilityMode mode); - }; -} - -#endif diff --git a/client/ramses-client/impl/glslEffectBlock/GlslEffect.cpp b/client/ramses-client/impl/glslEffectBlock/GlslEffect.cpp deleted file mode 100644 index f468b9b94..000000000 --- a/client/ramses-client/impl/glslEffectBlock/GlslEffect.cpp +++ /dev/null @@ -1,298 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "GlslEffect.h" -#include "Resource/EffectResource.h" -#include "glslEffectBlock/GlslToEffectConverter.h" -#include "GlslLimits.h" -#include - -namespace ramses_internal -{ - /* - wrapper for glslang process wide initializer and finalizer. - constructed once in this file. it is global and lives longer - than main. will not work if global static objects start using - effects but this should never happen. - */ - class GlslangInitAndFinalizeOnceHelper - { - public: - GlslangInitAndFinalizeOnceHelper() - { - glslang::InitializeProcess(); - glslang::InitProcess(); - } - - ~GlslangInitAndFinalizeOnceHelper() - { - glslang::DetachProcess(); - glslang::FinalizeProcess(); - } - }; - static GlslangInitAndFinalizeOnceHelper glslangInitializer; - - - GlslEffect::GlslEffect(std::string_view vertexShader, - std::string_view fragmentShader, - std::string_view geometryShader, - const std::vector& compilerDefines, - const HashMap& semanticInputs, - std::string_view name) - : m_vertexShader(vertexShader) - , m_fragmentShader(fragmentShader) - , m_geometryShader(geometryShader) - , m_compilerDefines(compilerDefines) - , m_semanticInputs(semanticInputs) - , m_name(name) - , m_effectResource(nullptr) - , m_shadingLanguageVersion(0) - { - } - - GlslEffect::~GlslEffect() - { - } - - EffectResource* GlslEffect::createEffectResource(ResourceCacheFlag cacheFlag) - { - if (m_effectResource) - { - return m_effectResource; - } - - std::string defineString = createDefineString(); - - ShaderParts vertexShaderParts; - ShaderParts fragmentShaderParts; - ShaderParts geometryShaderParts; - std::unique_ptr glslVertexShader(new glslang::TShader(EShLangVertex)); - std::unique_ptr glslFragmentShader(new glslang::TShader(EShLangFragment)); - - const bool hasGeometryShader = !m_geometryShader.empty(); - std::unique_ptr glslGeometryShader(hasGeometryShader ? new glslang::TShader(EShLangGeometry) : nullptr); - - if (!createShaderParts(vertexShaderParts, defineString, m_vertexShader) || - !createShaderParts(fragmentShaderParts, defineString, m_fragmentShader) || - (hasGeometryShader && !createShaderParts(geometryShaderParts, defineString, m_geometryShader))) - { - return nullptr; - } - - TBuiltInResource glslCompilationResources; - // Use the GLSL version to determine the resource limits in the shader. The version used in vertex - // and fragment shader must be the same (checked later), so we just pass one of them for now. - GlslLimits::InitCompilationResources(glslCompilationResources, vertexShaderParts.version); - if (!parseShader(*glslVertexShader, glslCompilationResources, vertexShaderParts, "vertex shader") || - !parseShader(*glslFragmentShader, glslCompilationResources, fragmentShaderParts, "fragment shader") || - (hasGeometryShader && !parseShader(*glslGeometryShader, glslCompilationResources, geometryShaderParts, "geometry shader"))) - { - return nullptr; - } - - std::unique_ptr program(linkProgram(glslVertexShader.get(), - glslFragmentShader.get(), - hasGeometryShader ? glslGeometryShader.get() : nullptr)); - if (!program) - { - return nullptr; - } - - GlslToEffectConverter glslToEffectConverter(m_semanticInputs); - if (!glslToEffectConverter.parseShaderProgram(program.get())) - { - m_errorMessages << "[GLSL Input Parser] " << glslToEffectConverter.getStatusMessage(); - return nullptr; - } - - if (!extractAndCheckShaderVersions(program.get())) - { - return nullptr; - } - - if (!extractAndCheckExtensions(program.get())) - { - return nullptr; - } - - const EffectInputInformationVector& uniformInputs = glslToEffectConverter.getUniformInputs(); - const EffectInputInformationVector& attributeInputs = glslToEffectConverter.getAttributeInputs(); - const auto geomInputType = glslToEffectConverter.getGeometryShaderInputType(); - - m_effectResource = new EffectResource(mergeShaderParts(vertexShaderParts), mergeShaderParts(fragmentShaderParts), mergeShaderParts(geometryShaderParts), geomInputType, uniformInputs, attributeInputs, m_name, cacheFlag); - return m_effectResource; - } - - std::string GlslEffect::createDefineString() const - { - StringOutputStream result; - for(const auto& defineString : m_compilerDefines) - { - result << "#define " << defineString << "\n"; - } - return result.release(); - } - - bool GlslEffect::createShaderParts(ShaderParts& outParts, const std::string& defineString, const std::string& userShader) const - { - size_t versionStringStart; - std::string versionString; - if ((versionStringStart = userShader.find("#version")) != std::string::npos) - { - size_t versionStringEnd = userShader.find('\n', versionStringStart); - if (versionStringEnd == std::string::npos) - { - m_errorMessages << "[GLSL Compiler] " << m_name << " Shader contains #version without newline \n"; - return false; - } - - outParts.version = userShader.substr(versionStringStart, versionStringEnd + 1 - versionStringStart); - outParts.userCode = userShader.substr(versionStringEnd + 1, userShader.size() - versionStringEnd - 1); - } - else - { - outParts.version = "#version 100\n"; - outParts.userCode = userShader; - } - - outParts.defines = defineString; - return true; - } - - std::string GlslEffect::mergeShaderParts(const ShaderParts& shaderParts) const - { - StringOutputStream str; - str << shaderParts.version << shaderParts.defines << shaderParts.userCode; - return str.release(); - } - - bool GlslEffect::parseShader(glslang::TShader& tShader, const TBuiltInResource& glslCompilationResources, const ShaderParts& shaderParts, const std::string& shaderName) - { - const std::array fragmentShaderCodeCString = { shaderParts.version.c_str(), shaderParts.defines.c_str(), shaderParts.userCode.c_str() }; - tShader.setStrings(fragmentShaderCodeCString.data(), 3); - bool parsingSuccessful = tShader.parse(&glslCompilationResources, 100, false, EShMsgDefault); - if (!parsingSuccessful) - { - m_errorMessages << "[GLSL Compiler] " << shaderName << " Shader Parsing Error:\n" << tShader.getInfoLog() << "\n"; - return false; - } - return true; - } - - glslang::TProgram* GlslEffect::linkProgram(glslang::TShader* vertexShader, glslang::TShader* fragmentShader, glslang::TShader* geometryShader) const - { - std::unique_ptr program(new glslang::TProgram()); - program->addShader(vertexShader); - program->addShader(fragmentShader); - if (geometryShader) - program->addShader(geometryShader); - - if (!program->link(EShMsgDefault)) - { - m_errorMessages << "[GLSL Compiler] Shader Program Linker Error:\n" << program->getInfoLog() << "\n"; - return nullptr; - } - - if (!program->buildReflection()) - { - m_errorMessages << "[GLSL Compiler] Shader program error in buildReflection\n"; - return nullptr; - } - return program.release(); - } - - bool GlslEffect::extractAndCheckShaderVersions(const glslang::TProgram* program) - { - // profile check - const EProfile vertexShaderProfile = program->getIntermediate(EShLangVertex)->getProfile(); - const EProfile fragmentShaderProfile = program->getIntermediate(EShLangFragment)->getProfile(); - const auto geometryShaderIntermediate = program->getIntermediate(EShLangGeometry); - const bool hasGeometryShader = (nullptr != geometryShaderIntermediate); - const EProfile geometryShaderProfile = hasGeometryShader ? geometryShaderIntermediate->getProfile() : ENoProfile; - - if (vertexShaderProfile != EEsProfile || fragmentShaderProfile != EEsProfile || (hasGeometryShader && geometryShaderProfile != EEsProfile)) - { - m_errorMessages << "[GLSL Compiler] " << m_name << " unsupported profile (supported profile: es)\n"; - return false; - } - - const uint32_t vertexShaderVersion = program->getIntermediate(EShLangVertex)->getVersion(); - const uint32_t fragmentShaderVersion = program->getIntermediate(EShLangFragment)->getVersion(); - const uint32_t geometryShaderVersion = hasGeometryShader ? geometryShaderIntermediate->getVersion() : 0u; - - const uint32_t maximumSupportedVersion = 320u; - - if (vertexShaderVersion > maximumSupportedVersion || - fragmentShaderVersion > maximumSupportedVersion || - (hasGeometryShader && geometryShaderVersion > maximumSupportedVersion)) - { - m_errorMessages << "[GLSL Compiler] " << m_name << " unsupported version (maximum supported version: 320 es)\n"; - return false; - } - - if (vertexShaderVersion != fragmentShaderVersion || - (hasGeometryShader && vertexShaderVersion != geometryShaderVersion)) - { - m_errorMessages << "[GLSL Compiler] " << m_name << " version of vertex, fragment and geometry shaders must be same\n"; - return false; - } - - m_shadingLanguageVersion = vertexShaderVersion; - return true; - } - - bool GlslEffect::extractAndCheckExtensions(const glslang::TProgram* program) - { - bool success = true; - const std::set& vertexExtensions = program->getIntermediate(EShLangVertex)->getRequestedExtensions(); - for (std::set::const_iterator it = vertexExtensions.begin(); it != vertexExtensions.end(); it++) - { - const char* extensionName = it->c_str(); - if (!isSupportedExtension(extensionName)) - { - m_errorMessages << "[GLSL Compiler] " << m_name << " extension not supported in vertex shader: " << extensionName << "\n"; - success = false; - } - } - - const std::set& fragmentExtensions = program->getIntermediate(EShLangFragment)->getRequestedExtensions(); - for (std::set::const_iterator it = fragmentExtensions.begin(); it != fragmentExtensions.end(); it++) - { - const char* extensionName = it->c_str(); - if (!isSupportedExtension(extensionName)) - { - m_errorMessages << "[GLSL Compiler] " << m_name << " extension not supported in fragment shader: " << extensionName << "\n"; - success = false; - } - } - - return success; - } - - uint32_t GlslEffect::getShadingLanguageVersion() const - { - assert(m_effectResource); - return m_shadingLanguageVersion; - } - - std::string GlslEffect::getEffectErrorMessages() const - { - return m_errorMessages.data(); - } - - bool GlslEffect::isSupportedExtension(const std::string& extension) const - { - if (extension == "GL_OES_EGL_image_external" - || extension == "GL_OES_EGL_image_external_essl3") - { - return true; - } - - return false; - } -} diff --git a/client/test/AppearanceTest.cpp b/client/test/AppearanceTest.cpp deleted file mode 100644 index 001da9a78..000000000 --- a/client/test/AppearanceTest.cpp +++ /dev/null @@ -1,1315 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gtest/gtest.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "TestEffectCreator.h" -#include "ClientTestUtils.h" -#include "EffectImpl.h" -#include "DataObjectImpl.h" -#include "TextureSamplerImpl.h" -#include "AppearanceImpl.h" -#include "AppearanceUtils.h" - -namespace ramses -{ - class AAppearanceTest : public ::testing::Test - { - public: - - static void SetUpTestCase() - { - sharedTestState = std::make_unique(false); - } - - static void TearDownTestCase() - { - sharedTestState = nullptr; - } - - void SetUp() override - { - EXPECT_TRUE(sharedTestState != nullptr); - sharedTestState->recreateAppearence(); - appearance = sharedTestState->appearance; - } - - protected: - struct TextureInputInfo - { - UniformInput input; - TextureSampler* sampler = nullptr; - TextureSamplerMS* samplerMS = nullptr; - TextureSamplerExternal* samplerExternal = nullptr; - RenderBuffer* renderBuffer = nullptr; - Texture2D* texture2D = nullptr; - Texture3D* texture3D = nullptr; - TextureCube* textureCube = nullptr; - }; - - void getTexture2DInputInfo(TextureInputInfo& info) - { - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", info.input)); - - const uint8_t data[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, data); - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); - EXPECT_TRUE(texture != nullptr); - info.texture2D = texture; - - TextureSampler* sampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - EXPECT_TRUE(sampler != nullptr); - info.sampler = sampler; - } - - void getTexture2DMSInputInfo(TextureInputInfo& info) - { - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dMSInput", info.input)); - - RenderBuffer* renderBuffer = sharedTestState->getScene().createRenderBuffer(2u, 2u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); - EXPECT_TRUE(renderBuffer != nullptr); - info.renderBuffer = renderBuffer; - - TextureSamplerMS* samplerMS = sharedTestState->getScene().createTextureSamplerMS(*renderBuffer, "renderBuffer"); - EXPECT_TRUE(samplerMS != nullptr); - info.samplerMS = samplerMS; - } - - void getTexture3DInputInfo(TextureInputInfo& info) - { - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture3dInput", info.input)); - - const uint8_t data[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, data); - Texture3D* texture = sharedTestState->getScene().createTexture3D(ETextureFormat::RGB8, 1u, 1u, 1u, 1u, &mipData, false); - EXPECT_TRUE(texture != nullptr); - info.texture3D = texture; - - TextureSampler* sampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - EXPECT_TRUE(sampler != nullptr); - info.sampler = sampler; - } - - void getTextureCubeInputInfo(TextureInputInfo& info) - { - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("textureCubeInput", info.input)); - - const uint8_t data[] = { 1, 2, 3 }; - const CubeMipLevelData mipData(3u, data, data, data, data, data, data); - TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, 1u, &mipData, false); - EXPECT_TRUE(texture != nullptr); - info.textureCube = texture; - - TextureSampler* sampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - EXPECT_TRUE(sampler != nullptr); - info.sampler = sampler; - } - - void getTextureExternalInputInfo(TextureInputInfo& info) - { - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("textureExternalInput", info.input)); - - TextureSamplerExternal* sampler = sharedTestState->getScene().createTextureSamplerExternal(ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear); - EXPECT_TRUE(sampler != nullptr); - info.samplerExternal = sampler; - } - - static std::unique_ptr sharedTestState; - Appearance* appearance; - }; - - std::unique_ptr AAppearanceTest::sharedTestState; - - class AAppearanceTestWithSemanticUniforms : public AAppearanceTest - { - public: - static void SetUpTestCase() - { - sharedTestState = std::make_unique(true); - } - }; - - TEST_F(AAppearanceTest, getsTheSameEffectUsedToCreateIt) - { - const Effect& effect = appearance->getEffect(); - EXPECT_EQ(&effect, sharedTestState->effect); - - const ramses_internal::DataLayout uniformLayout = sharedTestState->getInternalScene().getDataLayout(appearance->m_impl.getUniformDataLayout()); - const ramses_internal::ResourceContentHash& effectHashFromUniformLayout = uniformLayout.getEffectHash(); - EXPECT_EQ(appearance->getEffect().m_impl.getLowlevelResourceHash(), effectHashFromUniformLayout); - } - - TEST_F(AAppearanceTest, setGetBlendingFactors) - { - status_t stat = appearance->setBlendingFactors(EBlendFactor::One, EBlendFactor::SrcAlpha, EBlendFactor::OneMinusSrcAlpha, EBlendFactor::DstAlpha); - EXPECT_EQ(StatusOK, stat); - EBlendFactor srcColor = EBlendFactor::Zero; - EBlendFactor destColor = EBlendFactor::Zero; - EBlendFactor srcAlpha = EBlendFactor::Zero; - EBlendFactor destAlpha = EBlendFactor::Zero; - stat = appearance->getBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); - EXPECT_EQ(StatusOK, stat); - EXPECT_EQ(EBlendFactor::One, srcColor); - EXPECT_EQ(EBlendFactor::SrcAlpha, destColor); - EXPECT_EQ(EBlendFactor::OneMinusSrcAlpha, srcAlpha); - EXPECT_EQ(EBlendFactor::DstAlpha, destAlpha); - } - - TEST_F(AAppearanceTest, setGetBlendingColor) - { - vec4f color{ std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; - //default values - status_t stat = appearance->getBlendingColor(color); - EXPECT_EQ(StatusOK, stat); - EXPECT_EQ(color, vec4f(0.f, 0.f, 0.f, 0.f)); - - const vec4f colorToSet{ 0.1f, 0.2f, 0.3f, 0.4f }; - stat = appearance->setBlendingColor(colorToSet); - EXPECT_EQ(StatusOK, stat); - - stat = appearance->getBlendingColor(color); - EXPECT_EQ(StatusOK, stat); - EXPECT_EQ(colorToSet, color); - } - - TEST_F(AAppearanceTest, setGetDepthWrite) - { - EDepthWrite depthWriteMode = EDepthWrite::Disabled; - EXPECT_EQ(StatusOK, appearance->setDepthWrite(EDepthWrite::Enabled)); - EXPECT_EQ(StatusOK, appearance->getDepthWriteMode(depthWriteMode)); - EXPECT_EQ(EDepthWrite::Enabled, depthWriteMode); - - EXPECT_EQ(StatusOK, appearance->setDepthWrite(EDepthWrite::Disabled)); - EXPECT_EQ(StatusOK, appearance->getDepthWriteMode(depthWriteMode)); - EXPECT_EQ(EDepthWrite::Disabled, depthWriteMode); - - EXPECT_EQ(StatusOK, appearance->setDepthWrite(EDepthWrite::Enabled)); - EXPECT_EQ(StatusOK, appearance->getDepthWriteMode(depthWriteMode)); - EXPECT_EQ(EDepthWrite::Enabled, depthWriteMode); - } - - TEST_F(AAppearanceTest, setGetDepthFunction) - { - EDepthFunc depthFunc = EDepthFunc::Disabled; - EXPECT_EQ(StatusOK, appearance->getDepthFunction(depthFunc)); - EXPECT_EQ(EDepthFunc::LessEqual, depthFunc); - - EXPECT_EQ(StatusOK, appearance->setDepthFunction(EDepthFunc::GreaterEqual)); - EXPECT_EQ(StatusOK, appearance->getDepthFunction(depthFunc)); - EXPECT_EQ(EDepthFunc::GreaterEqual, depthFunc); - } - - TEST_F(AAppearanceTest, setGetScissorTest) - { - EScissorTest mode = EScissorTest::Disabled; - EXPECT_EQ(StatusOK, appearance->setScissorTest(EScissorTest::Enabled, 1, 2, 3u, 4u)); - EXPECT_EQ(StatusOK, appearance->getScissorTestState(mode)); - EXPECT_EQ(EScissorTest::Enabled, mode); - - int16_t x = 0; - int16_t y = 0; - uint16_t width = 0u; - uint16_t height = 0; - EXPECT_EQ(StatusOK, appearance->getScissorRegion(x, y, width, height)); - EXPECT_EQ(1, x); - EXPECT_EQ(2, y); - EXPECT_EQ(3u, width); - EXPECT_EQ(4u, height); - } - - TEST_F(AAppearanceTest, setGetStencilFunc) - { - status_t stat = appearance->setStencilFunction(EStencilFunc::Equal, 2u, 0xef); - EXPECT_EQ(StatusOK, stat); - EStencilFunc func = EStencilFunc::Disabled; - uint8_t ref = 0; - uint8_t mask = 0; - stat = appearance->getStencilFunction(func, ref, mask); - EXPECT_EQ(StatusOK, stat); - EXPECT_EQ(EStencilFunc::Equal, func); - EXPECT_EQ(2u, ref); - EXPECT_EQ(0xef, mask); - } - - TEST_F(AAppearanceTest, setGetStencilOperation) - { - status_t stat = appearance->setStencilOperation(EStencilOperation::Decrement, EStencilOperation::Increment, EStencilOperation::DecrementWrap); - EXPECT_EQ(StatusOK, stat); - EStencilOperation sfail = EStencilOperation::Zero; - EStencilOperation dpfail = EStencilOperation::Zero; - EStencilOperation dppass = EStencilOperation::Zero; - stat = appearance->getStencilOperation(sfail, dpfail, dppass); - EXPECT_EQ(StatusOK, stat); - EXPECT_EQ(EStencilOperation::Decrement, sfail); - EXPECT_EQ(EStencilOperation::Increment, dpfail); - EXPECT_EQ(EStencilOperation::DecrementWrap, dppass); - } - - TEST_F(AAppearanceTest, setGetBlendOperations) - { - EXPECT_EQ(StatusOK, appearance->setBlendingOperations(EBlendOperation::Subtract, EBlendOperation::Max)); - EBlendOperation opColor = EBlendOperation::Disabled; - EBlendOperation opAlpha = EBlendOperation::Disabled; - EXPECT_EQ(StatusOK, appearance->getBlendingOperations(opColor, opAlpha)); - EXPECT_EQ(EBlendOperation::Subtract, opColor); - EXPECT_EQ(EBlendOperation::Max, opAlpha); - } - - TEST_F(AAppearanceTest, setGetCullMode) - { - ECullMode mode = ECullMode::Disabled; - EXPECT_EQ(StatusOK, appearance->setCullingMode(ECullMode::FrontFacing)); - EXPECT_EQ(StatusOK, appearance->getCullingMode(mode)); - EXPECT_EQ(ECullMode::FrontFacing, mode); - EXPECT_EQ(StatusOK, appearance->setCullingMode(ECullMode::Disabled)); - EXPECT_EQ(StatusOK, appearance->getCullingMode(mode)); - EXPECT_EQ(ECullMode::Disabled, mode); - } - - TEST_F(AAppearanceTest, hasDrawModeTrianglesByDefault) - { - EDrawMode mode; - EXPECT_EQ(StatusOK, appearance->getDrawMode(mode)); - EXPECT_EQ(EDrawMode::Triangles, mode); - } - - TEST_F(AAppearanceTest, setGetDrawMode) - { - EDrawMode mode = EDrawMode::Lines; - EXPECT_EQ(StatusOK, appearance->setDrawMode(EDrawMode::Points)); - EXPECT_EQ(StatusOK, appearance->getDrawMode(mode)); - EXPECT_EQ(EDrawMode::Points, mode); - } - - TEST_F(AAppearanceTest, setGetColorWriteMask) - { - bool writeR = false; - bool writeG = false; - bool writeB = false; - bool writeA = false; - EXPECT_EQ(StatusOK, appearance->setColorWriteMask(true, false, true, false)); - EXPECT_EQ(StatusOK, appearance->getColorWriteMask(writeR, writeG, writeB, writeA)); - EXPECT_TRUE(writeR); - EXPECT_FALSE(writeG); - EXPECT_TRUE(writeB); - EXPECT_FALSE(writeA); - EXPECT_EQ(StatusOK, appearance->setColorWriteMask(false, true, false, true)); - EXPECT_EQ(StatusOK, appearance->getColorWriteMask(writeR, writeG, writeB, writeA)); - EXPECT_FALSE(writeR); - EXPECT_TRUE(writeG); - EXPECT_FALSE(writeB); - EXPECT_TRUE(writeA); - } - - TEST_F(AAppearanceTest, reportsErrorWhenGetSetMismatchingInputTypeScalar) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("integerInput", inputObject)); - - const float value = 42; - float getValue = 0; - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, getValue)); - } - - TEST_F(AAppearanceTest, reportsErrorWhenGetSetMismatchingInputTypeArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("integerInput", inputObject)); - - const float values[] = { 42, 43, 44, 45, 46, 47 }; - float getValues[6]; - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 6u, values)); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 6u, getValues)); - } - - TEST_F(AAppearanceTest, reportsErrorWhenGetSetMismatchingInputTypeTexture) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("integerInput", inputObject)); - - const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); - - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); - ASSERT_TRUE(texture != nullptr); - TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_NE(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureSampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture)); - } - - TEST_F(AAppearanceTest, reportsErrorWhenSetInputTextureFromADifferentScene) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", inputObject)); - - const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); - - ramses::Scene& anotherScene = *sharedTestState->getClient().createScene(sceneId_t(1u)); - Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); - ASSERT_TRUE(texture != nullptr); - TextureSampler* textureSampler = anotherScene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_NE(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - EXPECT_EQ(StatusOK, anotherScene.destroy(*texture)); - EXPECT_EQ(StatusOK, sharedTestState->getClient().destroy(anotherScene)); - } - - TEST_F(AAppearanceTest, getsSamplerSetToUniformInput) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", inputObject)); - - const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); - ASSERT_TRUE(texture != nullptr); - TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_EQ(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - const TextureSampler* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTexture(inputObject, actualSampler)); - EXPECT_EQ(textureSampler, actualSampler); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureSampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture)); - } - - TEST_F(AAppearanceTest, getsNullSamplerIfNoneSetToUniformInput) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", inputObject)); - - const TextureSampler* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTexture(inputObject, actualSampler)); - EXPECT_EQ(nullptr, actualSampler); - } - - TEST_F(AAppearanceTest, getsNullSamplerMSIfNoneSetToUniformInput) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dMSInput", inputObject)); - - const TextureSamplerMS* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTextureMS(inputObject, actualSampler)); - EXPECT_EQ(nullptr, actualSampler); - } - - TEST_F(AAppearanceTest, getsNullSamplerExternalIfNoneSetToUniformInput) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("textureExternalInput", inputObject)); - - const TextureSamplerExternal* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTextureExternal(inputObject, actualSampler)); - EXPECT_EQ(nullptr, actualSampler); - } - - TEST_F(AAppearanceTest, failsToGetSamplerSetToUniformIfInputHasWrongType) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("integerInput", inputObject)); - - const TextureSampler* actualSampler = nullptr; - EXPECT_NE(StatusOK, appearance->getInputTexture(inputObject, actualSampler)); - EXPECT_EQ(nullptr, actualSampler); - } - - TEST_F(AAppearanceTest, failsToGetSamplerMSIfInputHasWrongType) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", inputObject)); - - const TextureSamplerMS* actualSampler = nullptr; - EXPECT_NE(StatusOK, appearance->getInputTextureMS(inputObject, actualSampler)); - EXPECT_EQ(nullptr, actualSampler); - } - - TEST_F(AAppearanceTest, failsToGetSamplerExternalIfInputHasWrongType) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", inputObject)); - - const TextureSamplerExternal* actualSampler = nullptr; - EXPECT_NE(StatusOK, appearance->getInputTextureExternal(inputObject, actualSampler)); - EXPECT_EQ(nullptr, actualSampler); - } - - /// Int32 - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeInt32) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("integerInput", inputObject)); - - int32_t value = 42; - int32_t& valueR = value; - const int32_t& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - int32_t getValue = 0; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeInt32Array) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("integerInputArray", inputObject)); - - const int32_t value = 42; - int32_t values[] = { value, value * 2, value * 3 }; - int32_t getValues[3] = { 0 }; - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values)); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues)); - EXPECT_EQ(values, absl::MakeSpan(getValues)); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values)); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values)); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, values)); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, values)); - } - - /// Float - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeFloat) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - - float value = 42; - float& valueR = value; - const float& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - float getValue = 0; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeFloatArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInputArray", inputObject)); - - const float value = 42; - const float values[] = { value, value * 2, value * 3 }; - float getValues[3] = { 0 }; - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values)); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues)); - EXPECT_EQ(values, absl::MakeSpan(getValues)); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values)); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values)); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues)); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues)); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2i) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec2iInput", inputObject)); - - vec2i value{ 42, 24 }; - vec2i& valueR = value; - const vec2i& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - vec2i getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2iArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec2iInputArray", inputObject)); - - const std::vector values = { vec2i{42, 43}, vec2i{44, 45}, vec2i{46, 47} }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3i) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec3iInput", inputObject)); - - vec3i value{ 42, 24, 4422 }; - vec3i& valueR = value; - const vec3i& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - vec3i getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3iArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec3iInputArray", inputObject)); - - const std::vector values = { vec3i{42, 43, 444}, vec3i{44, 45, 555}, vec3i{46, 47, 666} }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4i) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec4iInput", inputObject)); - - vec4i value{ 42, 24, 44, 22 }; - vec4i& valueR = value; - const vec4i& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - vec4i getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4iArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec4iInputArray", inputObject)); - - const std::vector values = { vec4i{42, 43, 444, 555}, vec4i{44, 45, 666, 777}, vec4i{46, 47, 888, 999} }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2f) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec2fInput", inputObject)); - - vec2f value{ 42.f, 24.f }; - vec2f& valueR = value; - const vec2f& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - vec2f getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2fArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec2fInputArray", inputObject)); - - const std::vector values = { vec2f{42.f, 43.f}, vec2f{44.f, 45.f}, vec2f{46.f, 47.f} }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3f) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec3fInput", inputObject)); - - vec3f value{ 42.f, 24.f, 44.f }; - vec3f& valueR = value; - const vec3f& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - vec3f getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3fArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec3fInputArray", inputObject)); - - const std::vector values = { vec3f{42.f, 43.f, 444.f}, vec3f{44.f, 45.f, 666.f}, vec3f{46.f, 47.f, 888.f} }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4f) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec4fInput", inputObject)); - - vec4f value{ 42.f, 24.f, 44.f, 55.f }; - vec4f& valueR = value; - const vec4f& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - vec4f getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4fArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec4fInputArray", inputObject)); - - const std::vector values = { vec4f{42.f, 43.f, 444.f, 555.f}, vec4f{44.f, 45.f, 666.f, 777.f}, vec4f{46.f, 47.f, 888.f, 999.f} }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - /// matrix22f - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix22f) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix22fInput", inputObject)); - - matrix22f value{ 42.f, 43.f, 44.f, 45.f }; - matrix22f& valueR = value; - const matrix22f& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - matrix22f getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix22fArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix22fInputArray", inputObject)); - - const std::vector values = - { - matrix22f{42.f, 43.f, 44.f, 45.f}, - matrix22f{46.f, 47.f, 48.f, 49.f}, - matrix22f{50.f, 51.f, 52.f, 53.f} - }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - /// Matrix33f - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix33f) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix33fInput", inputObject)); - - matrix33f value{ 42.f, 43.f, 44.f, 45.f, 46.f, 47.f, 48.f, 49.f, 50.f }; - matrix33f& valueR = value; - const matrix33f& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - matrix33f getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix33fArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix33fInputArray", inputObject)); - - const std::vector values = - { - matrix33f{42.f, 43.f, 44.f, 45.f, 11.f, 22.f, 33.f, 44.f, 55.f}, - matrix33f{46.f, 47.f, 48.f, 49.f, 66.f, 77.f, 88.f, 99.f, 10.f}, - matrix33f{50.f, 51.f, 52.f, 53.f, 20.f, 30.f, 40.f, 50.f, 60.f} - }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - /// Matrix44f - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix44f) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix44fInput", inputObject)); - - matrix44f value{ 42.f, 43.f, 44.f, 45.f, 46.f, 47.f, 48.f, 49.f, 50.f, 51.f, 52.f, 53.f, 54.f, 55.f, 56.f, 57.f }; - matrix44f& valueR = value; - const matrix44f& valueCR = value; - auto valueM = value; - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, value)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, valueCR)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, std::move(valueM))); - - matrix44f getValue; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, getValue)); - EXPECT_EQ(value, getValue); - } - - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix44fArray) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix44fInputArray", inputObject)); - - const std::vector values = - { - matrix44f{42.f, 43.f, 44.f, 45.f, 11.f, 22.f, 33.f, 44.f, 55.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f}, - matrix44f{46.f, 47.f, 48.f, 49.f, 66.f, 77.f, 88.f, 99.f, 10.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f}, - matrix44f{50.f, 51.f, 52.f, 53.f, 20.f, 30.f, 40.f, 50.f, 60.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f} - }; - std::vector getValues(values.size()); - - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 3u, values.data())); - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, 3u, getValues.data())); - EXPECT_EQ(values, getValues); - - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 0u, values.data())); - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, 11u, values.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 0u, getValues.data())); - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, 11u, getValues.data())); - } - - /// Texture2D - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTexture2D) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dInput", inputObject)); - - const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); - - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); - ASSERT_TRUE(texture != nullptr); - TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_EQ(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - const TextureSampler* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTexture(inputObject, actualSampler)); - EXPECT_EQ(textureSampler, actualSampler); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureSampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture)); - } - - /// Texture2DMS - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTexture2DMS) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("texture2dMSInput", inputObject)); - - RenderBuffer* renderBuffer = sharedTestState->getScene().createRenderBuffer(2u, 2u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); - TextureSamplerMS* textureSampler = sharedTestState->getScene().createTextureSamplerMS(*renderBuffer, "renderBuffer"); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_EQ(textureSampler->m_impl.getTextureDataType(), ramses_internal::EDataType::TextureSampler2DMS); - EXPECT_EQ(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - const TextureSamplerMS* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTextureMS(inputObject, actualSampler)); - EXPECT_EQ(textureSampler, actualSampler); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureSampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*renderBuffer)); - } - - /// TextureCube - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTextureCube) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("textureCubeInput", inputObject)); - - const uint8_t texData[] = { 1, 2, 3 }; - const CubeMipLevelData mipData(3u, texData, texData, texData, texData, texData, texData); - - TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, 1u, &mipData, false); - ASSERT_TRUE(texture != nullptr); - TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, *texture); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_EQ(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - const TextureSampler* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTexture(inputObject, actualSampler)); - EXPECT_EQ(textureSampler, actualSampler); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureSampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture)); - } - - /// TextureExternal - TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTextureExternal) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("textureExternalInput", inputObject)); - - TextureSamplerExternal* textureSampler = sharedTestState->getScene().createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear); - ASSERT_TRUE(textureSampler != nullptr); - - EXPECT_EQ(textureSampler->m_impl.getTextureDataType(), ramses_internal::EDataType::TextureSamplerExternal); - EXPECT_EQ(StatusOK, appearance->setInputTexture(inputObject, *textureSampler)); - - const TextureSamplerExternal* actualSampler = nullptr; - EXPECT_EQ(StatusOK, appearance->getInputTextureExternal(inputObject, actualSampler)); - EXPECT_EQ(textureSampler, actualSampler); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureSampler)); - } - - /// Binding data objects - TEST_F(AAppearanceTest, uniformInputIsNotBoundInitially) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - EXPECT_FALSE(appearance->isInputBound(inputObject)); - } - - TEST_F(AAppearanceTest, canBindDataObjectToUniformInput) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_EQ(StatusOK, appearance->bindInput(inputObject, *dataObject)); - EXPECT_TRUE(appearance->isInputBound(inputObject)); - EXPECT_EQ(dataObject->m_impl.getDataReference(), appearance->getDataObjectBoundToInput(inputObject)->m_impl.getDataReference()); - - EXPECT_EQ(StatusOK, appearance->unbindInput(inputObject)); - EXPECT_FALSE(appearance->isInputBound(inputObject)); - EXPECT_EQ(nullptr, appearance->getDataObjectBoundToInput(inputObject)); - } - - TEST_F(AAppearanceTest, failsToSetOrGetValueIfInputBound) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 666.f)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - dataObject->setValue(333.f); - - EXPECT_EQ(StatusOK, appearance->bindInput(inputObject, *dataObject)); - - const float setValue = 0.111f; - float value = 0.f; - EXPECT_NE(StatusOK, appearance->setInputValue(inputObject, setValue)); - EXPECT_EQ(StatusOK, dataObject->getValue(value)); - EXPECT_FLOAT_EQ(333.f, value); // failed setter does not modify data object - value = 0.f; - EXPECT_NE(StatusOK, appearance->getInputValue(inputObject, value)); - EXPECT_FLOAT_EQ(0.f, value); // failed getter does not modify out parameter - - EXPECT_EQ(StatusOK, appearance->unbindInput(inputObject)); - - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, value)); - EXPECT_EQ(666.f, value); // failed setter did not modify previously set value - } - - TEST_F(AAppearanceTest, failsToBindDataObjectToArrayUniformInput) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInputArray", inputObject)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_NE(StatusOK, appearance->bindInput(inputObject, *dataObject)); - } - - TEST_F(AAppearanceTest, failsToBindDataObjectFromADifferentScene) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - - ramses::Scene& anotherScene = *sharedTestState->getClient().createScene(sceneId_t(1u)); - auto dataObject = anotherScene.createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_NE(StatusOK, appearance->bindInput(inputObject, *dataObject)); - - EXPECT_EQ(StatusOK, sharedTestState->getClient().destroy(anotherScene)); - } - - TEST_F(AAppearanceTest, failsToBindDataObjectOfMismatchingType) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("vec4fInput", inputObject)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_NE(StatusOK, appearance->bindInput(inputObject, *dataObject)); - EXPECT_FALSE(appearance->isInputBound(inputObject)); - } - - TEST_F(AAppearanceTestWithSemanticUniforms, failsToBindDataObjectIfInputHasSemantics) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("matrix44fInput", inputObject)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Matrix44F); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_NE(StatusOK, appearance->bindInput(inputObject, *dataObject)); - } - - TEST_F(AAppearanceTest, unbindingDataObjectFallsBackToPreviouslySetValue) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - EXPECT_EQ(StatusOK, appearance->setInputValue(inputObject, 666.f)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_EQ(StatusOK, appearance->bindInput(inputObject, *dataObject)); - dataObject->setValue(13.f); - - EXPECT_EQ(StatusOK, appearance->unbindInput(inputObject)); - float value = 0.f; - EXPECT_EQ(StatusOK, appearance->getInputValue(inputObject, value)); - EXPECT_FLOAT_EQ(666.f, value); - } - - TEST_F(AAppearanceTest, failsToUnbindIfInputIsNotBound) - { - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - - EXPECT_NE(StatusOK, appearance->unbindInput(inputObject)); - } - - /// Validation - TEST_F(AAppearanceTest, reportsErrorWhenValidatedWithInvalidTextureSampler) - { - TextureInputInfo texture2DInputInfo; - getTexture2DInputInfo(texture2DInputInfo); - - TextureInputInfo texture2DMSInputInfo; - getTexture2DMSInputInfo(texture2DMSInputInfo); - - TextureInputInfo texture3DInputInfo; - getTexture3DInputInfo(texture3DInputInfo); - - TextureInputInfo textureCubeInputInfo; - getTextureCubeInputInfo(textureCubeInputInfo); - - TextureInputInfo textureExternalInfo; - getTextureExternalInputInfo(textureExternalInfo); - - Appearance* newAppearance = sharedTestState->getScene().createAppearance(*sharedTestState->effect, "New Appearance"); - ASSERT_TRUE(nullptr != newAppearance); - - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture2DInputInfo.input, *texture2DInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture2DMSInputInfo.input, *texture2DMSInputInfo.samplerMS)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture3DInputInfo.input, *texture3DInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(textureCubeInputInfo.input, *textureCubeInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(textureExternalInfo.input, *textureExternalInfo.samplerExternal)); - EXPECT_EQ(StatusOK, newAppearance->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DInputInfo.sampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DMSInputInfo.samplerMS)); - EXPECT_NE(StatusOK, newAppearance->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*newAppearance)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DInputInfo.texture2D)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DMSInputInfo.renderBuffer)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureCubeInputInfo.sampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureCubeInputInfo.textureCube)); - } - - TEST_F(AAppearanceTest, reportsErrorWhenValidatedWithInvalidTexture) - { - TextureInputInfo texture2DInputInfo; - getTexture2DInputInfo(texture2DInputInfo); - - TextureInputInfo texture2DMSInputInfo; - getTexture2DMSInputInfo(texture2DMSInputInfo); - - TextureInputInfo texture3DInputInfo; - getTexture3DInputInfo(texture3DInputInfo); - - TextureInputInfo textureCubeInputInfo; - getTextureCubeInputInfo(textureCubeInputInfo); - - TextureInputInfo textureExternalInfo; - getTextureExternalInputInfo(textureExternalInfo); - - Appearance* newAppearance = sharedTestState->getScene().createAppearance(*sharedTestState->effect, "New Appearance"); - ASSERT_TRUE(nullptr != newAppearance); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture2DInputInfo.input, *texture2DInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture2DMSInputInfo.input, *texture2DMSInputInfo.samplerMS)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture3DInputInfo.input, *texture3DInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(textureCubeInputInfo.input, *textureCubeInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(textureExternalInfo.input, *textureExternalInfo.samplerExternal)); - EXPECT_EQ(StatusOK, newAppearance->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DInputInfo.texture2D)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DMSInputInfo.renderBuffer)); - EXPECT_NE(StatusOK, appearance->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*newAppearance)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DInputInfo.sampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DMSInputInfo.samplerMS)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureCubeInputInfo.sampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureCubeInputInfo.textureCube)); - } - - TEST_F(AAppearanceTest, reportsErrorWhenValidatedWithBoundDataObjectThatWasDestroyed) - { - TextureInputInfo texture2DInputInfo; - getTexture2DInputInfo(texture2DInputInfo); - - TextureInputInfo texture2DMSInputInfo; - getTexture2DMSInputInfo(texture2DMSInputInfo); - - TextureInputInfo texture3DInputInfo; - getTexture3DInputInfo(texture3DInputInfo); - - TextureInputInfo textureCubeInputInfo; - getTextureCubeInputInfo(textureCubeInputInfo); - - TextureInputInfo textureExternalInfo; - getTextureExternalInputInfo(textureExternalInfo); - - Appearance* newAppearance = sharedTestState->getScene().createAppearance(*sharedTestState->effect, "New Appearance"); - ASSERT_TRUE(nullptr != newAppearance); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture2DInputInfo.input, *texture2DInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture2DMSInputInfo.input, *texture2DMSInputInfo.samplerMS)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(texture3DInputInfo.input, *texture3DInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(textureCubeInputInfo.input, *textureCubeInputInfo.sampler)); - EXPECT_EQ(StatusOK, newAppearance->setInputTexture(textureExternalInfo.input, *textureExternalInfo.samplerExternal)); - EXPECT_EQ(StatusOK, newAppearance->validate()); - - UniformInput inputObject; - EXPECT_EQ(StatusOK, sharedTestState->effect->findUniformInput("floatInput", inputObject)); - - auto dataObject = sharedTestState->getScene().createDataObject(EDataType::Float); - ASSERT_TRUE(dataObject != nullptr); - - EXPECT_EQ(StatusOK, newAppearance->bindInput(inputObject, *dataObject)); - EXPECT_EQ(StatusOK, newAppearance->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*dataObject)); - EXPECT_NE(StatusOK, newAppearance->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*newAppearance)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DInputInfo.sampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DInputInfo.texture2D)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DMSInputInfo.samplerMS)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*texture2DMSInputInfo.renderBuffer)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureCubeInputInfo.sampler)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*textureCubeInputInfo.textureCube)); - } - - TEST_F(AAppearanceTest, failsWhenWrongBlendingOperationsSet) - { - status_t stat = appearance->setBlendingOperations(EBlendOperation::Subtract, EBlendOperation::ReverseSubtract); - EXPECT_EQ(StatusOK, stat); - - stat = appearance->setBlendingOperations(EBlendOperation::Add, EBlendOperation::Disabled); - EXPECT_NE(StatusOK, stat); - - stat = appearance->setBlendingOperations(EBlendOperation::Disabled, EBlendOperation::Add); - EXPECT_NE(StatusOK, stat); - - EBlendOperation opColor = EBlendOperation::Disabled; - EBlendOperation opAlpha = EBlendOperation::Disabled; - EXPECT_EQ(StatusOK, appearance->getBlendingOperations(opColor, opAlpha)); - EXPECT_EQ(EBlendOperation::Subtract, opColor); - EXPECT_EQ(EBlendOperation::ReverseSubtract, opAlpha); - } - - class AnAppearanceWithGeometryShader : public AAppearanceTest - { - public: - - static void SetUpTestCase() - { - sharedTestState = std::make_unique(false, true); - } - }; - - TEST_F(AnAppearanceWithGeometryShader, HasInitialDrawModeOfGeometryShadersRequirement) - { - EDrawMode mode; - EXPECT_EQ(StatusOK, appearance->getDrawMode(mode)); - EXPECT_EQ(EDrawMode::Lines, mode); - } - - TEST_F(AnAppearanceWithGeometryShader, RefusesToChangeDrawingMode_WhenIncompatibleToGeometryShader) - { - // Shader uses lines, can't change to incompatible types - EXPECT_NE(StatusOK, appearance->setDrawMode(EDrawMode::Points)); - EXPECT_NE(StatusOK, appearance->setDrawMode(EDrawMode::Triangles)); - EXPECT_NE(StatusOK, appearance->setDrawMode(EDrawMode::TriangleFan)); - EDrawMode mode; - EXPECT_EQ(StatusOK, appearance->getDrawMode(mode)); - EXPECT_EQ(EDrawMode::Lines, mode); - } - - TEST_F(AnAppearanceWithGeometryShader, AllowsChangingDrawMode_IfNewModeIsStillCompatible) - { - // Shader uses lines, change to line strip is ok - still produces lines for the geometry stage - EXPECT_EQ(StatusOK, appearance->setDrawMode(EDrawMode::LineStrip)); - EDrawMode mode; - EXPECT_EQ(StatusOK, appearance->getDrawMode(mode)); - EXPECT_EQ(EDrawMode::LineStrip, mode); - } - - TEST(AnAppearanceUtils, ChecksValidGeometryShaderModes) - { - // valid combinations of appearance draw mode (first) and GS input type (second) - const std::initializer_list> validDrawModeToGSModeCombinations = { - { EDrawMode::Points, EDrawMode::Points }, - { EDrawMode::Lines, EDrawMode::Lines }, - { EDrawMode::LineStrip, EDrawMode::Lines }, - { EDrawMode::LineLoop, EDrawMode::Lines }, - { EDrawMode::Triangles, EDrawMode::Triangles }, - { EDrawMode::TriangleStrip, EDrawMode::Triangles }, - { EDrawMode::TriangleFan, EDrawMode::Triangles } - }; - - for (int drawModeVal = 0; drawModeVal <= static_cast(EDrawMode::LineStrip); ++drawModeVal) - { - for (int gsInputTypeVal = 0; gsInputTypeVal <= static_cast(EDrawMode::LineStrip); ++gsInputTypeVal) - { - const auto drawMode = static_cast(drawModeVal); - const auto gsInputType = static_cast(gsInputTypeVal); - - // gs input mode restricted to only these types - if (gsInputType != EDrawMode::Points && gsInputType != EDrawMode::Lines && gsInputType != EDrawMode::Triangles) - continue; - - const auto it = std::find_if(validDrawModeToGSModeCombinations.begin(), validDrawModeToGSModeCombinations.end(), [&](const auto& modePair) { - return modePair.first == drawMode && modePair.second == gsInputType; - }); - const bool isValid = (it != validDrawModeToGSModeCombinations.end()); - - EXPECT_EQ(isValid, AppearanceUtils::GeometryShaderCompatibleWithDrawMode(gsInputType, drawMode)); - } - } - } -} diff --git a/client/test/ClientApplicationLogicTest.cpp b/client/test/ClientApplicationLogicTest.cpp deleted file mode 100644 index 8b2378c1c..000000000 --- a/client/test/ClientApplicationLogicTest.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gmock/gmock.h" -#include "framework_common_gmock_header.h" -#include "ClientApplicationLogic.h" -#include "ComponentMocks.h" -#include "ResourceMock.h" -#include "DummyResource.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "Scene/ClientScene.h" -#include "Components/ResourceComponent.h" -#include "Components/SceneGraphComponent.h" -#include "Resource/TextureResource.h" -#include "TransportCommon/FakeConnectionSystem.h" -#include "TransportCommon/FakeConnectionStatusUpdateNotifier.h" -#include "Components/FileInputStreamContainer.h" - -using namespace ramses_internal; - -class AClientApplicationLogic : public ::testing::Test -{ -public: - AClientApplicationLogic() - : dummyGuid(555) - , logic(dummyGuid, frameworkLock) - , sceneId(44u) - , dummyScene(SceneInfo(sceneId)) - { - logic.init(resourceComponent, scenegraphProviderComponent); - } - -protected: - Guid dummyGuid; - StrictMock resourceComponent; - StrictMock scenegraphProviderComponent; - PlatformLock frameworkLock; - ClientApplicationLogic logic; - - SceneId sceneId; - ClientScene dummyScene; - - void createDummyScene() - { - EXPECT_CALL(scenegraphProviderComponent, handleCreateScene(Ref(dummyScene), false, Ref(logic))); - logic.createScene(dummyScene, false); - } -}; - -TEST_F(AClientApplicationLogic, sceneDistributionIsDisabledInitially) -{ - createDummyScene(); - EXPECT_FALSE(logic.isScenePublished(sceneId)); -} - -TEST_F(AClientApplicationLogic, canEnableLocalOnlySceneDistribution) -{ - createDummyScene(); - - EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode_LocalOnly)); - logic.publishScene(sceneId, EScenePublicationMode_LocalOnly); - - EXPECT_TRUE(logic.isScenePublished(sceneId)); -} - -TEST_F(AClientApplicationLogic, canEnableSceneDistribution) -{ - createDummyScene(); - EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode_LocalAndRemote)); - logic.publishScene(sceneId, EScenePublicationMode_LocalAndRemote); - - EXPECT_TRUE(logic.isScenePublished(sceneId)); -} - -TEST_F(AClientApplicationLogic, canDisableLocalOnlySceneDistribution) -{ - createDummyScene(); - EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode_LocalOnly)); - logic.publishScene(sceneId, EScenePublicationMode_LocalOnly); - - EXPECT_CALL(scenegraphProviderComponent, handleUnpublishScene(sceneId)); - logic.unpublishScene(sceneId); - - EXPECT_FALSE(logic.isScenePublished(sceneId)); -} - -TEST_F(AClientApplicationLogic, canDisableSceneDistribution) -{ - createDummyScene(); - EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode_LocalAndRemote)); - logic.publishScene(sceneId, EScenePublicationMode_LocalAndRemote); - - EXPECT_CALL(scenegraphProviderComponent, handleUnpublishScene(sceneId)); - logic.unpublishScene(sceneId); - - EXPECT_FALSE(logic.isScenePublished(sceneId)); -} - -TEST_F(AClientApplicationLogic, forwardsAddingOfResourceFileToResourceComponent) -{ - const ResourceInfo resourceInfo(EResourceType_VertexArray, ResourceContentHash(44u, 0), 2u, 1u); - ResourceTableOfContents resourceToc; - resourceToc.registerContents(resourceInfo, 0u, 1u); - const std::string fileName("resourceFile"); - InputStreamContainerSPtr resourceFileStream(std::make_shared(fileName)); - - EXPECT_CALL(resourceComponent, addResourceFile(resourceFileStream, Ref(resourceToc))); - logic.addResourceFile(resourceFileStream, resourceToc); -} - -TEST_F(AClientApplicationLogic, triesToGetRequestedResourceFromResourceComponent) -{ - const ResourceContentHash dummyResourceHash(44u, 0); - ON_CALL(resourceComponent, getResource(_)).WillByDefault(Return(ManagedResource())); - EXPECT_CALL(resourceComponent, getResource(dummyResourceHash)); - EXPECT_EQ(ManagedResource(), logic.getResource(dummyResourceHash)); -} - -TEST_F(AClientApplicationLogic, triesToGetHashUsageFromResourceComponent) -{ - const ResourceContentHash dummyResourceHash(44u, 0); - ON_CALL(resourceComponent, getResourceHashUsage(_)).WillByDefault(Return(ResourceHashUsage())); - EXPECT_CALL(resourceComponent, getResourceHashUsage(dummyResourceHash)); - EXPECT_FALSE(logic.getHashUsage(dummyResourceHash).isValid()); -} - -TEST_F(AClientApplicationLogic, addsAndRemovesResourceFilesFromComponent) -{ - InputStreamContainerSPtr resourceFileInputStream; - ResourceTableOfContents toc; - - EXPECT_CALL(resourceComponent, addResourceFile(_,_)); - const auto handle = logic.addResourceFile(resourceFileInputStream, toc); - - EXPECT_CALL(resourceComponent, removeResourceFile(handle)); - logic.removeResourceFile(handle); -} - -TEST_F(AClientApplicationLogic, gathersSceneReferenceEventsInAContainer) -{ - SceneReferenceEvent event(SceneId { 123 }); - event.referencedScene = SceneId{ 123456789 }; - - EXPECT_TRUE(logic.popSceneReferenceEvents().empty()); - logic.handleSceneReferenceEvent(event, Guid{}); - const auto result = logic.popSceneReferenceEvents(); - EXPECT_EQ(result.size(), 1u); - const auto sre = result.front(); - EXPECT_EQ(sre.referencedScene, event.referencedScene); - - logic.handleSceneReferenceEvent(event, Guid{}); - logic.handleSceneReferenceEvent(event, Guid{}); - logic.handleSceneReferenceEvent(event, Guid{}); - logic.handleSceneReferenceEvent(event, Guid{}); - EXPECT_EQ(logic.popSceneReferenceEvents().size(), 4u); -} - - -TEST_F(AClientApplicationLogic, incomingResourceAvailabilityEventDoesNoHarm) -{ - ResourceAvailabilityEvent event; - event.sceneid = SceneId { 123 }; - - logic.handleResourceAvailabilityEvent(event, Guid {}); -} - -TEST_F(AClientApplicationLogic, returnsReturnValueFromComponentOnFlush) -{ - EXPECT_CALL(scenegraphProviderComponent, handleFlush(_, _, _)).WillOnce(Return(true)); - EXPECT_TRUE(logic.flush(sceneId, {}, {})); - - EXPECT_CALL(scenegraphProviderComponent, handleFlush(_, _, _)).WillOnce(Return(false)); - EXPECT_FALSE(logic.flush(sceneId, {}, {})); -} - -class AClientApplicationLogicWithRealComponents : public ::testing::Test -{ -public: - AClientApplicationLogicWithRealComponents() - : resComp(stats, fwlock) - , sceneComp(clientId, commSystem, connStatusUpdateNotifier, resComp, fwlock, ramses::EFeatureLevel_Latest) - , logic(clientId, fwlock) - { - logic.init(resComp, sceneComp); - } - -protected: - const SceneId sceneId = SceneId(1u); - const Guid clientId = Guid(1); - const Guid renderer1 = Guid(2); - const Guid renderer2 = Guid(3); - - PlatformLock fwlock; - StatisticCollectionFramework stats; - ResourceComponent resComp; - FakeConnectionSystem commSystem; - FakeConnectionStatusUpdateNotifier connStatusUpdateNotifier; - SceneGraphComponent sceneComp; - - ClientApplicationLogic logic; -}; - -TEST_F(AClientApplicationLogicWithRealComponents, keepsResourcesAliveForNewSubscriberForShadowCopyScene) -{ - ClientScene clientScene{ SceneInfo(sceneId) }; - logic.createScene(clientScene, false); - logic.publishScene(sceneId, EScenePublicationMode_LocalAndRemote); - auto res = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {}); - res->setResourceData(ResourceBlob{ 1 }, { 1u, 1u }); - auto hash = res->getHash(); - { - // simulate creation of texture2d - auto manRes = logic.addResource(res); - - auto hashUsage = logic.getHashUsage(hash); - - // use texture2d - const auto dataSlotHandle = clientScene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, hash, {} }); - - sceneComp.handleSubscribeScene(sceneId, renderer1); - EXPECT_TRUE(logic.flush(sceneId, {}, {})); - - // release data slot along with its texture2d (by leaving scope) - clientScene.releaseDataSlot(dataSlotHandle); - } - - EXPECT_EQ(resComp.getResource(hash).get(), res); - sceneComp.handleSubscribeScene(sceneId, renderer2); -} - -TEST_F(AClientApplicationLogicWithRealComponents, keepsAlsoOldResourcesAliveForNewSubscriberForShadowCopyScene) -{ - ClientScene clientScene{ SceneInfo(sceneId) }; - logic.createScene(clientScene, false); - logic.publishScene(sceneId, EScenePublicationMode_LocalAndRemote); - auto res = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {}); - res->setResourceData(ResourceBlob{ 1 }, { 1u, 1u }); - auto res2 = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(2u, 2u, 1u, ETextureFormat::R8, true, {}, { 4u }), ResourceCacheFlag_DoNotCache, {}); - res2->setResourceData(ResourceBlob{ 2 }, { 2u, 2u }); - auto hash = res->getHash(); - auto hash2 = res2->getHash(); - { - // simulate creation of texture2d - auto manRes = logic.addResource(res); - auto hashUsage = logic.getHashUsage(hash); - - // use texture2d - const auto dataSlotHandle = clientScene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, hash, {} }); - - sceneComp.handleSubscribeScene(sceneId, renderer1); - EXPECT_TRUE(logic.flush(sceneId, {}, {})); - - // another stream texture - auto manRes2 = logic.addResource(res2); - auto hashUsage2 = logic.getHashUsage(hash2); - - // use texture2d - clientScene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(1u), {}, {}, hash2, {} }); - - EXPECT_TRUE(logic.flush(sceneId, {}, {})); - - // release first data slot along with its texture2d (by leaving scope) - clientScene.releaseDataSlot(dataSlotHandle); - } - - EXPECT_EQ(resComp.getResource(hash).get(), res); - EXPECT_EQ(resComp.getResource(hash2).get(), res2); - sceneComp.handleSubscribeScene(sceneId, renderer2); -} diff --git a/client/test/CreationHelper.h b/client/test/CreationHelper.h deleted file mode 100644 index 8edd9d3f7..000000000 --- a/client/test/CreationHelper.h +++ /dev/null @@ -1,110 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_CREATIONHELPER_H -#define RAMSES_CREATIONHELPER_H - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Collections/Vector.h" -#include "Collections/Pair.h" - -#include -#include - -namespace ramses -{ - class RamsesObject; - class SceneObject; - class RamsesClient; - class RamsesFramework; - class TextureCubeInput; - class Scene; - class Node; - class MeshNode; - class PerspectiveCamera; - class OrthographicCamera; - class Effect; - class Appearance; - class Texture2D; - class Texture3D; - class TextureCube; - class ArrayResource; - class RenderGroup; - class RenderPass; - class BlitPass; - class TextureSampler; - class TextureSamplerMS; - class RenderBuffer; - class RenderTarget; - class RenderGroup; - class ArrayBuffer; - class Texture2DBuffer; - class GeometryBinding; - class DataObject; - class PickableObject; - class SceneReference; - - class CreationHelper - { - public: - CreationHelper(Scene* scene, RamsesClient* ramsesClient); - ~CreationHelper(); - - void setScene(Scene* scene); - - template - ObjectType* createObjectOfType(std::string_view name) - { - UNUSED(name); - assert(false); - return NULL; - } - - void destroyAdditionalAllocatedSceneObjects(); - [[nodiscard]] size_t getAdditionalAllocatedNodeCount() const; - - private: - Scene* m_scene; - RamsesClient* m_ramsesClient; - - using ClientAndFramework = std::pair; - using RamsesClientAndFrameworkComponentVector = std::vector; - RamsesClientAndFrameworkComponentVector m_allocatedClientAndFrameworkComponents; - std::vector m_additionalAllocatedSceneObjects; - sceneId_t m_lastReferencedSceneId{ 123u }; - }; - - template <> RamsesClient* CreationHelper::createObjectOfType(std::string_view name); - template <> Scene* CreationHelper::createObjectOfType(std::string_view name); - template <> Node* CreationHelper::createObjectOfType(std::string_view name); - template <> MeshNode* CreationHelper::createObjectOfType(std::string_view name); - template <> PerspectiveCamera* CreationHelper::createObjectOfType(std::string_view name); - template <> OrthographicCamera* CreationHelper::createObjectOfType(std::string_view name); - template <> Effect* CreationHelper::createObjectOfType(std::string_view name); - template <> Appearance* CreationHelper::createObjectOfType(std::string_view name); - template <> Texture2D* CreationHelper::createObjectOfType(std::string_view name); - template <> Texture3D* CreationHelper::createObjectOfType(std::string_view name); - template <> TextureCube* CreationHelper::createObjectOfType(std::string_view name); - template <> ArrayResource* CreationHelper::createObjectOfType(std::string_view name); - template <> RenderGroup* CreationHelper::createObjectOfType(std::string_view name); - template <> RenderPass* CreationHelper::createObjectOfType(std::string_view name); - template <> BlitPass* CreationHelper::createObjectOfType(std::string_view name); - template <> TextureSampler* CreationHelper::createObjectOfType(std::string_view name); - template <> TextureSamplerMS* CreationHelper::createObjectOfType(std::string_view name); - template <> RenderBuffer* CreationHelper::createObjectOfType(std::string_view name); - template <> RenderTarget* CreationHelper::createObjectOfType(std::string_view name); - template <> GeometryBinding* CreationHelper::createObjectOfType(std::string_view name); - template <> DataObject* CreationHelper::createObjectOfType(std::string_view name); - template <> ArrayBuffer* CreationHelper::createObjectOfType(std::string_view name); - template <> Texture2DBuffer* CreationHelper::createObjectOfType(std::string_view name); - template <> PickableObject* CreationHelper::createObjectOfType(std::string_view name); - template <> SceneReference* CreationHelper::createObjectOfType(std::string_view name); -} - -#endif diff --git a/client/test/EffectInputTest.cpp b/client/test/EffectInputTest.cpp deleted file mode 100644 index b5c5878fa..000000000 --- a/client/test/EffectInputTest.cpp +++ /dev/null @@ -1,369 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" -#include "EffectInputImpl.h" -#include "SceneAPI/IScene.h" -#include "Utils/File.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" - -using namespace testing; - -namespace ramses -{ - class AnEffectInput : public ::testing::Test - { - protected: - void initializeInput(EffectInput& input) - { - constexpr ramses_internal::ResourceContentHash effectHash{ 1u, 0u }; - constexpr ramses_internal::EFixedSemantics semantics = ramses_internal::EFixedSemantics::ModelMatrix; - input.m_impl.get().initialize(effectHash, "test", ramses_internal::EDataType::Matrix44F, semantics, 1u, 66u); - } - - void checkInput(const EffectInput& input) - { - AttributeInput compInput; - initializeInput(compInput); - - EXPECT_STREQ(compInput.getName(), input.getName()); - EXPECT_EQ(compInput.getDataType(), input.getDataType()); - EXPECT_EQ(compInput.m_impl.get().getSemantics(), input.m_impl.get().getSemantics()); - EXPECT_EQ(compInput.m_impl.get().getElementCount(), input.m_impl.get().getElementCount()); - EXPECT_EQ(compInput.m_impl.get().getInputIndex(), input.m_impl.get().getInputIndex()); - EXPECT_EQ(compInput.m_impl.get().getEffectHash(), input.m_impl.get().getEffectHash()); - } - }; - - TEST_F(AnEffectInput, UniformInputIsInitializedToDefaultUponCreation) - { - UniformInput input; - EXPECT_STREQ("", input.getName()); - EXPECT_EQ(0u, input.getElementCount()); - EXPECT_EQ(ramses_internal::ResourceContentHash::Invalid(), input.m_impl.get().getEffectHash()); - EXPECT_FALSE(input.getDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::Invalid, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectUniformSemantic::Invalid, input.getSemantics()); - EXPECT_EQ(static_cast(-1), input.m_impl.get().getInputIndex()); - EXPECT_FALSE(input.isValid()); - } - - TEST_F(AnEffectInput, AttributeInputIsInitializedToDefaultUponCreation) - { - AttributeInput input; - EXPECT_STREQ("", input.getName()); - EXPECT_EQ(ramses_internal::ResourceContentHash::Invalid(), input.m_impl.get().getEffectHash()); - EXPECT_FALSE(input.getDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::Invalid, input.m_impl.get().getSemantics()); - EXPECT_EQ(static_cast(-1), input.m_impl.get().getInputIndex()); - EXPECT_FALSE(input.isValid()); - } - - TEST_F(AnEffectInput, UniformInputIsInitializedToGivenValues) - { - const ramses_internal::ResourceContentHash effectHash(1u, 0); - const std::string inputName("test"); - const ramses_internal::EDataType dataType = ramses_internal::EDataType::Int32; - const ramses_internal::EFixedSemantics semantics = ramses_internal::EFixedSemantics::ModelMatrix; - const size_t elementCount = 9u; - const size_t index = 66u; - - UniformInput input; - input.m_impl.get().initialize(effectHash, inputName, dataType, semantics, elementCount, index); - - EXPECT_STREQ(inputName.c_str(), input.getName()); - EXPECT_EQ(elementCount, input.getElementCount()); - EXPECT_EQ(EDataType::Int32, *input.getDataType()); - EXPECT_EQ(effectHash, input.m_impl.get().getEffectHash()); - EXPECT_EQ(dataType, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(semantics, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectUniformSemantic::ModelMatrix, input.getSemantics()); - EXPECT_EQ(index, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); - } - - TEST_F(AnEffectInput, AttributeInputIsInitializedToGivenValues) - { - const ramses_internal::ResourceContentHash effectHash(1u, 0); - const std::string inputName("test"); - const ramses_internal::EDataType dataType = ramses_internal::EDataType::Vector2Buffer; - const ramses_internal::EFixedSemantics semantics = ramses_internal::EFixedSemantics::TextPositionsAttribute; - const size_t index = 66u; - - AttributeInput input; - input.m_impl.get().initialize(effectHash, inputName, dataType, semantics, 1u, index); - - EXPECT_STREQ(inputName.c_str(), input.getName()); - EXPECT_EQ(EDataType::Vector2F, *input.getDataType()); - EXPECT_EQ(effectHash, input.m_impl.get().getEffectHash()); - EXPECT_EQ(dataType, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(semantics, input.m_impl.get().getSemantics()); - EXPECT_EQ(index, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); - } - - TEST_F(AnEffectInput, CanBeSerialized) - { - const ramses_internal::ResourceContentHash effectHash(1u, 0); - const std::string inputName("test"); - const ramses_internal::EDataType dataType = ramses_internal::EDataType::Vector2Buffer; - const ramses_internal::EFixedSemantics semantics = ramses_internal::EFixedSemantics::TextTextureCoordinatesAttribute; - const size_t index = 66u; - - EffectInputImpl input; - input.initialize(effectHash, inputName, dataType, semantics, 1u, index); - - const std::string fileName("someTemporaryFile.ram"); - - ramses_internal::File outputFile(fileName); - ramses_internal::BinaryFileOutputStream outputStream(outputFile); - assert(outputFile.isOpen()); - - input.serialize(outputStream); - - outputFile.close(); - - ramses_internal::File inputFile(fileName); - ramses_internal::BinaryFileInputStream inputStream(inputFile); - assert(inputFile.isOpen()); - - EffectInputImpl inputRead; - - inputRead.deserialize(inputStream); - - inputFile.close(); - - EXPECT_EQ(inputName, inputRead.getName()); - EXPECT_EQ(input.getDataType(), inputRead.getDataType()); - EXPECT_EQ(effectHash, inputRead.getEffectHash()); - EXPECT_EQ(dataType, inputRead.getInternalDataType()); - EXPECT_EQ(semantics, inputRead.getSemantics()); - EXPECT_EQ(index, inputRead.getInputIndex()); - EXPECT_TRUE(inputRead.isValid()); - } - - TEST_F(AnEffectInput, CanBeComparedForEquality) - { - const ramses_internal::ResourceContentHash effectHash(1u, 0); - const std::string inputName("test"); - const ramses_internal::EDataType dataType = ramses_internal::EDataType::Vector2Buffer; - const ramses_internal::EFixedSemantics semantics = ramses_internal::EFixedSemantics::TextPositionsAttribute; - const size_t index = 66u; - - EffectInputImpl input1; - input1.initialize(effectHash, inputName, dataType, semantics, 1u, index); - - { - EffectInputImpl input2; - input2.initialize(effectHash, inputName, dataType, semantics, 1u, index); - EXPECT_TRUE(input1 == input2); - EXPECT_FALSE(input1 != input2); - } - - { - EffectInputImpl input2; - input2.initialize(ramses_internal::ResourceContentHash(2u, 0), inputName, dataType, semantics, 1u, index); - EXPECT_FALSE(input1 == input2); - EXPECT_TRUE(input1 != input2); - } - - { - EffectInputImpl input2; - input2.initialize(effectHash, "test2", dataType, semantics, 1u, index); - EXPECT_FALSE(input1 == input2); - EXPECT_TRUE(input1 != input2); - } - - { - EffectInputImpl input2; - input2.initialize(effectHash, inputName, ramses_internal::EDataType::Vector3Buffer, semantics, 1u, index); - EXPECT_FALSE(input1 == input2); - EXPECT_TRUE(input1 != input2); - } - - { - EffectInputImpl input2; - input2.initialize(effectHash, inputName, dataType, ramses_internal::EFixedSemantics::TextTextureCoordinatesAttribute, 1u, index); - EXPECT_FALSE(input1 == input2); - EXPECT_TRUE(input1 != input2); - } - - { - EffectInputImpl input2; - input2.initialize(effectHash, inputName, dataType, semantics, 2u, index); - EXPECT_FALSE(input1 == input2); - EXPECT_TRUE(input1 != input2); - } - - { - EffectInputImpl input2; - input2.initialize(effectHash, inputName, dataType, semantics, 1u, 67u); - EXPECT_FALSE(input1 == input2); - EXPECT_TRUE(input1 != input2); - } - } - - TEST_F(AnEffectInput, ReturnsCorrectDataType) - { - const ramses_internal::ResourceContentHash effectHash(1u, 0); - const std::string inputName("test"); - const ramses_internal::EFixedSemantics semantics = ramses_internal::EFixedSemantics::ModelMatrix; - const size_t index = 66u; - UniformInput input; - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::UInt16, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::UInt16); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::UInt32, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::UInt32); - - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Int32, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Int32); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Vector2I, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Vector2I); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Vector3I, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Vector3I); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Vector4I, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Vector4I); - - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Float, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Float); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Vector2F, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Vector2F); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Vector3F, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Vector3F); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Vector4F, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Vector4F); - - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Matrix22F, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Matrix22F); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Matrix33F, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Matrix33F); - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::Matrix44F, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::Matrix44F); - - - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::TextureSampler2D, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::TextureSampler2D); - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::TextureSampler2DMS, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::TextureSampler2DMS); - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::TextureSampler3D, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::TextureSampler3D); - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::TextureSamplerCube, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::TextureSamplerCube); - input.m_impl.get().initialize(effectHash, inputName, ramses_internal::EDataType::TextureSamplerExternal, semantics, 1u, index); - EXPECT_EQ(*input.getDataType(), EDataType::TextureSamplerExternal); - } - - TEST_F(AnEffectInput, CanBeCopyAndMoveConstructed_Uniform) - { - UniformInput input; - initializeInput(input); - - UniformInput inputCopy{ input }; - checkInput(inputCopy); - - UniformInput inputMove{ std::move(input) }; - checkInput(inputMove); - } - - TEST_F(AnEffectInput, CanBeCopyAndMoveAssigned_Uniform) - { - UniformInput input; - initializeInput(input); - - UniformInput inputCopy; - inputCopy = input; - checkInput(inputCopy); - - UniformInput inputMove; - inputMove = std::move(input); - checkInput(inputMove); - } - - TEST_F(AnEffectInput, CanBeSelfAssigned_Uniform) - { - UniformInput input; - initializeInput(input); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - input = input; - checkInput(input); - input = std::move(input); - // NOLINTNEXTLINE(bugprone-use-after-move) - checkInput(input); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } - - TEST_F(AnEffectInput, CanBeCopyAndMoveConstructed_Attribute) - { - AttributeInput input; - initializeInput(input); - - AttributeInput inputCopy{ input }; - checkInput(inputCopy); - - AttributeInput inputMove{ std::move(input) }; - checkInput(inputMove); - } - - TEST_F(AnEffectInput, CanBeCopyAndMoveAssigned_Attribute) - { - AttributeInput input; - initializeInput(input); - - AttributeInput inputCopy; - inputCopy = input; - checkInput(inputCopy); - - AttributeInput inputMove; - inputMove = std::move(input); - checkInput(inputMove); - } - - TEST_F(AnEffectInput, CanBeSelfAssigned_Attribute) - { - AttributeInput input; - initializeInput(input); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - input = input; - checkInput(input); - input = std::move(input); - // NOLINTNEXTLINE(bugprone-use-after-move) - checkInput(input); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } -} diff --git a/client/test/GeometryBindingTest.cpp b/client/test/GeometryBindingTest.cpp deleted file mode 100644 index df1ca551c..000000000 --- a/client/test/GeometryBindingTest.cpp +++ /dev/null @@ -1,1063 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "ClientTestUtils.h" -#include "TestEffectCreator.h" - -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/ArrayBuffer.h" - -#include "ResourceImpl.h" -#include "EffectImpl.h" -#include "ArrayResourceImpl.h" -#include "GeometryBindingImpl.h" -#include "ArrayBufferImpl.h" -#include "Resource/EffectResource.h" -#include "ramses-framework-api/EDataType.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/ResourceContentHash.h" - -using namespace testing; - -namespace ramses -{ - class GeometryBindingTest : public ::testing::Test - { - public: - static void SetUpTestCase() - { - sharedTestState = new TestEffectCreator; - } - - static void TearDownTestCase() - { - delete sharedTestState; - sharedTestState = nullptr; - } - - void SetUp() override - { - EXPECT_TRUE(sharedTestState != nullptr); - } - - protected: - void checkHashSetToInternalScene(const GeometryBinding& geometryBinding, ramses_internal::DataFieldHandle field, const Resource& resource, uint32_t expectedInstancingDivisor) const - { - const ramses_internal::ResourceContentHash expectedHash = resource.m_impl.getLowlevelResourceHash(); - const ramses_internal::ResourceField& actualDataResource = sharedTestState->getInternalScene().getDataResource(geometryBinding.m_impl.getAttributeDataInstance(), field); - EXPECT_EQ(expectedHash, actualDataResource.hash); - EXPECT_EQ(expectedInstancingDivisor, actualDataResource.instancingDivisor); - } - - void checkDataBufferSetToInternalScene(const GeometryBinding& geometryBinding, ramses_internal::DataFieldHandle field, const ArrayBufferImpl& dataBuffer, uint32_t expectedInstancingDivisor) const - { - const ramses_internal::DataBufferHandle dataBufferHandle = dataBuffer.getDataBufferHandle(); - const ramses_internal::ResourceField& actualDataResource = sharedTestState->getInternalScene().getDataResource(geometryBinding.m_impl.getAttributeDataInstance(), field); - EXPECT_EQ(dataBufferHandle, actualDataResource.dataBuffer); - EXPECT_EQ(expectedInstancingDivisor, actualDataResource.instancingDivisor); - } - - ArrayResource* setVec3fArrayInput(GeometryBinding& geometry) - { - const vec3f vert{ 0.f, 1.f, 2.f }; - auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vec3Vertices"); - EXPECT_TRUE(vertices != nullptr); - assert(vertices); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", input)); - EXPECT_EQ(StatusOK, geometry.setInputBuffer(input, *vertices)); - - return vertices; - } - - ArrayResource* setVec2fArrayInput(GeometryBinding& geometry) - { - const vec2f vert{ 0.f, 1.f }; - auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vec2Vertices"); - EXPECT_TRUE(vertices != nullptr); - assert(vertices); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - EXPECT_EQ(StatusOK, geometry.setInputBuffer(input, *vertices)); - - return vertices; - } - - ArrayResource* setVec4fArrayInput(GeometryBinding& geometry) - { - const vec4f vert{ 0.f, 1.f, 2.f, 3.f }; - auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vec4Vertices"); - EXPECT_TRUE(vertices != nullptr); - assert(vertices); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec4fArrayInput", input)); - EXPECT_EQ(StatusOK, geometry.setInputBuffer(input, *vertices)); - - return vertices; - } - - ArrayResource* setFloatArrayInput(GeometryBinding& geometry) - { - float verts[8] = { 0.1f }; - auto vertices = sharedTestState->getScene().createArrayResource(8u, verts, ResourceCacheFlag_DoNotCache, "floatVertices"); - EXPECT_TRUE(vertices != nullptr); - assert(vertices); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("floatArrayInput", input)); - EXPECT_EQ(StatusOK, geometry.setInputBuffer(input, *vertices)); - - return vertices; - } - - ArrayResource* setIndicesInput(GeometryBinding& geometry) - { - uint32_t inds[3] = { 0u }; - auto indices = sharedTestState->getScene().createArrayResource(3u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - EXPECT_TRUE(indices != nullptr); - assert(indices); - - EXPECT_EQ(0u, geometry.m_impl.getIndicesCount()); - EXPECT_EQ(StatusOK, geometry.setIndices(*indices)); - - return indices; - } - - static TestEffectCreator* sharedTestState; - }; - - TestEffectCreator* GeometryBindingTest::sharedTestState = nullptr; - - TEST_F(GeometryBindingTest, CanGetEffect) - { - Effect* emptyEffect = TestEffects::CreateTestEffect(sharedTestState->getScene()); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*emptyEffect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const Effect& resultEffect = geometry->getEffect(); - EXPECT_EQ(resultEffect.getResourceId(), emptyEffect->getResourceId()); - EXPECT_EQ(resultEffect.m_impl.getLowlevelResourceHash(), emptyEffect->m_impl.getLowlevelResourceHash()); - - const uint32_t fieldCount = sharedTestState->getInternalScene().getDataLayout(geometry->m_impl.getAttributeDataLayout()).getFieldCount(); - EXPECT_EQ(1u, fieldCount); - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getScene().destroy(*emptyEffect); - } - - TEST_F(GeometryBindingTest, dataLayoutHasOnlyIndicesForEmptyEffect) - { - Effect* emptyEffect = TestEffects::CreateTestEffect(sharedTestState->getScene()); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*emptyEffect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const uint32_t fieldCount = sharedTestState->getInternalScene().getDataLayout(geometry->m_impl.getAttributeDataLayout()).getFieldCount(); - EXPECT_EQ(1u, fieldCount); - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getScene().destroy(*emptyEffect); - } - - TEST_F(GeometryBindingTest, dataLayoutHasRightEffectHash) - { - Effect* emptyEffect = TestEffects::CreateTestEffect(sharedTestState->getScene()); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*emptyEffect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const ramses_internal::DataLayout geometryLayout = sharedTestState->getInternalScene().getDataLayout(geometry->m_impl.getAttributeDataLayout()); - const ramses_internal::ResourceContentHash& effectHashFromGeometryLayout = geometryLayout.getEffectHash(); - - EXPECT_EQ(emptyEffect->m_impl.getLowlevelResourceHash(), effectHashFromGeometryLayout); - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getScene().destroy(*emptyEffect); - } - - TEST_F(GeometryBindingTest, indicesFieldIsCreatedAtFixedSlot) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const ramses_internal::DataFieldHandle indicesField(GeometryBindingImpl::IndicesDataFieldIndex); - const ramses_internal::EFixedSemantics semantics = sharedTestState->getInternalScene().getDataLayout(geometry->m_impl.getAttributeDataLayout()).getField(indicesField).semantics; - EXPECT_EQ(ramses_internal::EFixedSemantics::Indices, semantics); - - sharedTestState->getScene().destroy(*geometry); - } - - TEST_F(GeometryBindingTest, canSetResource) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const vec3f vert{ 0.f, 1.f, 2.f }; - ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vertices"); - ASSERT_TRUE(vertices != nullptr); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", input)); - EXPECT_EQ(StatusOK, geometry->setInputBuffer(input, *vertices, 13u)); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(3u), *vertices, 13u); // first field is indices - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getScene().destroy(*vertices); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingResourceToInvalidInput) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const vec3f vert{ 0.f, 1.f, 2.f }; - ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vertices"); - ASSERT_TRUE(vertices != nullptr); - - AttributeInput input; - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getScene().destroy(*vertices); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingResourceWithMismatchingTypeInEffect) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const vec2f vert{ 1.f, 2.f }; - ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vertices"); - ASSERT_TRUE(vertices != nullptr); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", input)); - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getScene().destroy(*vertices); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingVec2ArrayResourceFromAnotherScene) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const vec2f vert{ 1.f, 2.f }; - Scene& anotherScene(*sharedTestState->getClient().createScene(sceneId_t{ 0xf00 })); - ArrayResource* const vertices = anotherScene.createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vec2Vertices"); - ASSERT_TRUE(vertices != nullptr); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getClient().destroy(anotherScene); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingIndexDataBufferFromAnotherScene) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); - ASSERT_NE(nullptr, otherScene); - - ArrayBuffer* const indices = otherScene->createArrayBuffer(EDataType::UInt32, 3u, "indices"); - ASSERT_NE(nullptr, indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - sharedTestState->getScene().destroy(*indices); - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getClient().destroy(*otherScene); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingVertexDataBufferFromAnotherScene) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); - ASSERT_NE(nullptr, otherScene); - - ArrayBuffer* const vertices = otherScene->createArrayBuffer(EDataType::Float, 3u, "vertices"); - ASSERT_NE(nullptr, vertices); - - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("floatArrayInput", input)); - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - sharedTestState->getScene().destroy(*vertices); - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getClient().destroy(*otherScene); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingArrayBufferByteBlobFromAnotherScene) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); - ASSERT_NE(nullptr, otherScene); - - ArrayBuffer* const vertices = otherScene->createArrayBuffer(EDataType::ByteBlob, 3u, "vertices"); - ASSERT_NE(nullptr, vertices); - - AttributeInput inputVec2; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", inputVec2)); - AttributeInput inputVec3; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", inputVec3)); - - constexpr uint16_t nonZeroStride = 13u; - EXPECT_NE(StatusOK, geometry->setInputBuffer(inputVec2, *vertices, 0u, nonZeroStride)); - EXPECT_NE(StatusOK, geometry->setInputBuffer(inputVec3, *vertices, 2 * sizeof(float), nonZeroStride)); - - sharedTestState->getScene().destroy(*vertices); - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getClient().destroy(*otherScene); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingArrayResourceByteBlobFromAnotherScene) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); - ASSERT_NE(nullptr, otherScene); - - const ramses::Byte data[4] = { 0 }; - ArrayResource* const vertices = otherScene->createArrayResource(sizeof(data), data); - ASSERT_NE(nullptr, vertices); - - AttributeInput inputVec2; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", inputVec2)); - AttributeInput inputVec3; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", inputVec3)); - - constexpr uint16_t nonZeroStride = 13u; - EXPECT_NE(StatusOK, geometry->setInputBuffer(inputVec2, *vertices, 0u, nonZeroStride)); - EXPECT_NE(StatusOK, geometry->setInputBuffer(inputVec3, *vertices, 2 * sizeof(float), nonZeroStride)); - - sharedTestState->getScene().destroy(*vertices); - sharedTestState->getScene().destroy(*geometry); - sharedTestState->getClient().destroy(*otherScene); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingUint16ArrayIndicesFromAnotherScene) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - uint16_t inds[3] = { 0u }; - Scene& anotherScene(*sharedTestState->getClient().createScene(sceneId_t{ 0xf00 })); - ArrayResource* const indices = anotherScene.createArrayResource(3u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices != nullptr); - - EXPECT_EQ(0u, geometry->m_impl.getIndicesCount()); - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - sharedTestState->getClient().destroy(anotherScene); - } - - TEST_F(GeometryBindingTest, canSetIndicesResource16) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const uint16_t inds[3] = { 0u }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(3u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices != nullptr); - - EXPECT_EQ(0u, geometry->m_impl.getIndicesCount()); - EXPECT_EQ(StatusOK, geometry->setIndices(*indices)); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(0u), *indices, 0u); - EXPECT_EQ(indices->m_impl.getElementCount(), geometry->m_impl.getIndicesCount()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, canSetIndicesResource32) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const uint32_t inds[3] = { 0u }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(3u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices != nullptr); - - EXPECT_EQ(0u, geometry->m_impl.getIndicesCount()); - EXPECT_EQ(StatusOK, geometry->setIndices(*indices)); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(0u), *indices, 0u); - EXPECT_EQ(indices->m_impl.getElementCount(), geometry->m_impl.getIndicesCount()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, canSetIndicesDataBuffer16) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - ArrayBuffer* const indices = sharedTestState->getScene().createArrayBuffer(EDataType::UInt16, 1u, "index data buffer"); - ASSERT_TRUE(indices != nullptr); - - EXPECT_EQ(0u, geometry->m_impl.getIndicesCount()); - EXPECT_EQ(StatusOK, geometry->setIndices(*indices)); - checkDataBufferSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(0u), indices->m_impl, 0u); - EXPECT_EQ(indices->m_impl.getElementCount(), geometry->m_impl.getIndicesCount()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, canSetIndicesDataBuffer32) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - ArrayBuffer* const indices = sharedTestState->getScene().createArrayBuffer(EDataType::UInt32, 1u, "index data buffer"); - ASSERT_TRUE(indices != nullptr); - - EXPECT_EQ(0u, geometry->m_impl.getIndicesCount()); - EXPECT_EQ(StatusOK, geometry->setIndices(*indices)); - checkDataBufferSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(0u), indices->m_impl, 0u); - EXPECT_EQ(indices->m_impl.getElementCount(), geometry->m_impl.getIndicesCount()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceFLoat) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const float inds[4] = { .0f, .0f, .0f, .0f }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(4u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceVec2F) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const vec2f indice{ 1.f, 2.f }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(1u, &indice, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceVec3F) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const vec3f indice{ 1.f, 2.f, 3.f }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(1u, &indice, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceVec4F) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const vec4f indice{ 1.f, 2.f, 3.f, 4.f }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(1u, &indice, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferFloat) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const float inds[4] = { .0f, .0f, .0f, .0f }; - ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(EDataType::Float, 1u, "indices"); - ASSERT_TRUE(indices); - indices->updateData(0u, 1u, inds); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferVec2F) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const float inds[4] = { .0f, .0f, .0f, .0f }; - ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(EDataType::Vector2F, 1u, "indices"); - ASSERT_TRUE(indices); - indices->updateData(0u, 1u, inds); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferVec3F) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const float inds[4] = { .0f, .0f, .0f, .0f }; - ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(EDataType::Vector3F, 1u, "indices"); - ASSERT_TRUE(indices); - indices->updateData(0u, 1u, inds); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferVec4F) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const float inds[4] = { .0f, .0f, .0f, .0f }; - ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(EDataType::Vector4F, 1u, "indices"); - ASSERT_TRUE(indices); - indices->updateData(0u, 1u, inds); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferByteBlob) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - ArrayBuffer* const indices = sharedTestState->getScene().createArrayBuffer(EDataType::ByteBlob, 1u, "indices"); - ASSERT_TRUE(indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceByteBlob) - { - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const ramses::Byte inds[4] = { 0, 1, 2, 3 }; - ArrayResource* const indices = sharedTestState->getScene().createArrayResource(sizeof(inds), inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - ASSERT_TRUE(indices); - - EXPECT_NE(StatusOK, geometry->setIndices(*indices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indices)); - } - - TEST_F(GeometryBindingTest, canSetAttributeResourceInput) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const vec3f vert{ 0.f, 1.f, 2.f }; - ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vertices"); - ASSERT_TRUE(vertices != nullptr); - - EXPECT_EQ(StatusOK, geometry->setInputBuffer(input, *vertices)); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(3u), *vertices, 0u); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, canSetAttributeVertexDataBufferInput) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(EDataType::Vector3F, 3u, "vertices"); - ASSERT_TRUE(vertices != nullptr); - - EXPECT_EQ(StatusOK, geometry->setInputBuffer(input, *vertices, 16u)); - checkDataBufferSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(3u), vertices->m_impl, 16u); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, canVertexDataBufferInput_ArrayBufferByteBlob) - { - AttributeInput inputVec2; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", inputVec2)); - AttributeInput inputVec3; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", inputVec3)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - ArrayBuffer* const interleavedVertices = sharedTestState->getScene().createArrayBuffer(EDataType::ByteBlob, 5 * sizeof(float) *3u, "vertices"); - ASSERT_TRUE(interleavedVertices != nullptr); - - constexpr uint16_t nonZeroStride = 17u; - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec2, *interleavedVertices, 0u, nonZeroStride)); - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); - checkDataBufferSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(2u), interleavedVertices->m_impl, 0u); - checkDataBufferSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(3u), interleavedVertices->m_impl, 0u); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*interleavedVertices)); - } - - TEST_F(GeometryBindingTest, canSetVertexDataBufferInput_ArrayResourceByteBlob) - { - AttributeInput inputVec2; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", inputVec2)); - AttributeInput inputVec3; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", inputVec3)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const float data[10] = { 1.f }; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) interleaved vertices passed as byte blob - ArrayResource* const interleavedVertices = sharedTestState->getScene().createArrayResource(sizeof(data), reinterpret_cast(data)); - ASSERT_TRUE(interleavedVertices != nullptr); - - constexpr uint16_t nonZeroStride = 17u; - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec2, *interleavedVertices, 0u, nonZeroStride)); - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(2u), *interleavedVertices, 0u); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(3u), *interleavedVertices, 0u); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*interleavedVertices)); - } - - TEST_F(GeometryBindingTest, canSetArrayBufferByteBlobToSingleAttribute) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(EDataType::ByteBlob, 1u, "vertices"); - ASSERT_TRUE(vertices); - - EXPECT_EQ(StatusOK, geometry->setInputBuffer(input, *vertices)); - checkDataBufferSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(2u), vertices->m_impl, 0u); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, canSetArrayResourceByteBlobToSingleAttribute) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const float data[10] = { 1.f }; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) interleaved vertices passed as byte blob - ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(sizeof(data), reinterpret_cast(data)); - ASSERT_TRUE(vertices); - - EXPECT_EQ(StatusOK, geometry->setInputBuffer(input, *vertices)); - checkHashSetToInternalScene(*geometry, ramses_internal::DataFieldHandle(2u), *vertices, 0u); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, cannotSetStrideAndOffsetToNonByteBlobVertexDataBuffers) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(EDataType::Vector2F, 1u, "vertices"); - ASSERT_TRUE(vertices); - - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices, 1u, 2u)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, cannotSetStrideAndOffsetToNonByteBlobVertexArrayResource) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - const vec2f vert{ 0.f, 1.f }; - const auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vertices"); - ASSERT_TRUE(vertices); - - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices, 1u, 2u)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingAttributeResourceInputWithWrongType) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - const vec3f vert{ 0.f, 1.f, 2.f }; - ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, ramses::ResourceCacheFlag_DoNotCache, "vertices"); - ASSERT_TRUE(vertices != nullptr); - - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingAttributeVertexDataBufferInputWithWrongTypeUInt16) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(EDataType::UInt16, 1u, "vertices"); - ASSERT_TRUE(vertices); - - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenSettingAttributeVertexDataBufferInputWithWrongTypeUInt32) - { - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", input)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry); - - ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(EDataType::UInt32, 1u, "vertices"); - ASSERT_TRUE(vertices); - - EXPECT_NE(StatusOK, geometry->setInputBuffer(input, *vertices)); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertices)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedEffectResource) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - auto floatArray = setFloatArrayInput(*geometry); - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*sharedTestState->effect)); - sharedTestState->effect = nullptr; - EXPECT_NE(StatusOK, geometry->validate()); - - // restore the effect in sharedTestState after this test case - sharedTestState->effect = sharedTestState->createEffect(sharedTestState->getScene(), false); - ASSERT_TRUE(nullptr != sharedTestState->effect); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedIndicesResource) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - auto floatArray = setFloatArrayInput(*geometry); - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedInputFloatArrayResource) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - auto floatArray = setFloatArrayInput(*geometry); - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedInputVec2ArrayResource) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - auto floatArray = setFloatArrayInput(*geometry); - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedInputVec3ArrayResource) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - auto floatArray = setFloatArrayInput(*geometry); - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedInputVec4ArrayResource) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - auto floatArray = setFloatArrayInput(*geometry); - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedArrayBufferByteBlob) - { - AttributeInput inputVec2; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", inputVec2)); - AttributeInput inputVec3; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", inputVec3)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - ArrayBuffer* const interleavedVertices = sharedTestState->getScene().createArrayBuffer(EDataType::ByteBlob, 5 * sizeof(float) * 3u, "vertices"); - ASSERT_TRUE(interleavedVertices != nullptr); - std::vector dummyData(interleavedVertices->getMaximumNumberOfElements(), 0x00); - interleavedVertices->updateData(0u, 1u, dummyData.data()); - - auto floatArray = setFloatArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - constexpr uint16_t nonZeroStride = 12u; - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec2, *interleavedVertices, 0u, nonZeroStride)); - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*interleavedVertices)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedArrayResourceByteBlobl) - { - AttributeInput inputVec2; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec2fArrayInput", inputVec2)); - AttributeInput inputVec3; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("vec3fArrayInput", inputVec3)); - - GeometryBinding* const geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - - uint32_t data[4] = { 0u }; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) interleaved vertices passed as byte blob - ArrayResource* const interleavedVertices = sharedTestState->getScene().createArrayResource(sizeof(data), reinterpret_cast(data)); - ASSERT_TRUE(interleavedVertices != nullptr); - - auto floatArray = setFloatArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - constexpr uint16_t nonZeroStride = 12u; - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec2, *interleavedVertices, 0u, nonZeroStride)); - EXPECT_EQ(StatusOK, geometry->setInputBuffer(inputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*interleavedVertices)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*floatArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithDestroyedVertexDataBuffer) - { - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - - ArrayBuffer* const vertexDataBuffer = sharedTestState->getScene().createArrayBuffer(EDataType::Float, 3, "vertices"); - ASSERT_TRUE(vertexDataBuffer != nullptr); - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("floatArrayInput", input)); - geometry->setInputBuffer(input, *vertexDataBuffer); - const float data[] = { 0 }; - vertexDataBuffer->updateData(0u, 1u, data); - - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertexDataBuffer)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } - - TEST_F(GeometryBindingTest, reportsErrorWhenValidatedWithVertexDataBufferThatHasWrongType) - { - //It is possible that a data buffer gets deleted, and new data buffer gets created with same - //handle. Unfortunately validate can not check if a data buffer was destroyed and re-created - //but it can at least check that the assigned data buffer is of a correct type - GeometryBinding* geometry = sharedTestState->getScene().createGeometryBinding(*sharedTestState->effect, "geometry"); - - ArrayBuffer* const vertexDataBuffer = sharedTestState->getScene().createArrayBuffer(EDataType::Float, 3, "vertices"); - ASSERT_TRUE(vertexDataBuffer != nullptr); - AttributeInput input; - EXPECT_EQ(StatusOK, sharedTestState->effect->findAttributeInput("floatArrayInput", input)); - geometry->setInputBuffer(input, *vertexDataBuffer); - const float data[] = { 0 }; - vertexDataBuffer->updateData(0u, 1u, data); - - auto vec2fArray = setVec2fArrayInput(*geometry); - auto vec3fArray = setVec3fArrayInput(*geometry); - auto vec4fArray = setVec4fArrayInput(*geometry); - auto indicesArray = setIndicesInput(*geometry); - EXPECT_EQ(StatusOK, geometry->validate()); - - //delete data buffer and create new one with same handle - ramses_internal::DataBufferHandle dataBufferHandle = vertexDataBuffer->m_impl.getDataBufferHandle(); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vertexDataBuffer)); - ASSERT_FALSE(sharedTestState->getScene().m_impl.getIScene().isDataBufferAllocated(dataBufferHandle)); - sharedTestState->getScene().m_impl.getIScene().allocateDataBuffer(ramses_internal::EDataBufferType::VertexBuffer, ramses_internal::EDataType::Vector2F, 10 * sizeof(float), dataBufferHandle); - ASSERT_TRUE(sharedTestState->getScene().m_impl.getIScene().isDataBufferAllocated(dataBufferHandle)); - EXPECT_NE(StatusOK, geometry->validate()); - - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*geometry)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec2fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec3fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*vec4fArray)); - EXPECT_EQ(StatusOK, sharedTestState->getScene().destroy(*indicesArray)); - } -} diff --git a/client/test/GlslEffectTest.cpp b/client/test/GlslEffectTest.cpp deleted file mode 100644 index 5a012b9e9..000000000 --- a/client/test/GlslEffectTest.cpp +++ /dev/null @@ -1,1050 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "glslEffectBlock/GlslEffect.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Resource/EffectResource.h" -#include "gmock/gmock.h" - -#include -#include -#include - -using namespace ramses_internal; - -class AGlslEffect : public ::testing::Test -{ -public: - AGlslEffect() - { - } - - const std::string basicVertexShader = R"SHADER( - #version 320 es - void main(void) - { - gl_Position = vec4(0.0); - } - )SHADER"; - const std::string basicFragmentShader = R"SHADER( - #version 320 es - out lowp vec4 colorOut; - void main(void) - { - colorOut = vec4(0.0); - })SHADER"; - const std::string basicGeometryShader = R"SHADER( - #version 320 es - layout(points) in; - layout(points, max_vertices = 1) out; - void main() { - gl_Position = vec4(0.0); - EmitVertex(); - } - )SHADER"; - const std::vector emptyCompilerDefines; - const HashMap emptySemanticInputs; - -protected: - static void VerifyUniformInputExists(const EffectResource& effect, std::string_view uniformName) - { - const DataFieldHandle effecthandle = effect.getUniformDataFieldHandleByName(std::string(uniformName)); - EXPECT_TRUE(effecthandle.isValid()); - }; -}; - -TEST_F(AGlslEffect, canParseBasicShaders) -{ - GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getUniformInputs().size()); - EXPECT_EQ(0u, res->getAttributeInputs().size()); - EXPECT_FALSE(res->getGeometryShaderInputType().has_value()); - EXPECT_EQ(std::string(), res->getName()); -} - -TEST_F(AGlslEffect, canParseBasicShaders_WithGeometryShader) -{ - GlslEffect ge(basicVertexShader, basicFragmentShader, basicGeometryShader, emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getUniformInputs().size()); - EXPECT_EQ(0u, res->getAttributeInputs().size()); - EXPECT_EQ(EDrawMode::Points, res->getGeometryShaderInputType()); - EXPECT_EQ(std::string(), res->getName()); -} - -TEST_F(AGlslEffect, canParseGeometryShaderWithTriangles) -{ - const std::string geometryShaderTriangles = R"SHADER( - #version 320 es - layout(triangles) in; - layout(points, max_vertices = 1) out; - void main() { - gl_Position = vec4(0.0); - } - )SHADER"; - - GlslEffect ge(basicVertexShader, basicFragmentShader, geometryShaderTriangles, emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getUniformInputs().size()); - EXPECT_EQ(0u, res->getAttributeInputs().size()); - EXPECT_EQ(EDrawMode::Triangles, res->getGeometryShaderInputType()); - EXPECT_EQ(std::string(), res->getName()); -} - -TEST_F(AGlslEffect, usesPassedName) -{ - GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, "someName"); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(std::string("someName"), res->getName()); -} - -TEST_F(AGlslEffect, rejectsEmptyVertexShader) -{ - GlslEffect ge("", basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, rejectsBrokenVertexShader) -{ - GlslEffect ge("foo", basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, rejectsEmptyFragmentShader) -{ - GlslEffect ge(basicVertexShader, "", "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, rejectsBrokenFragmentShader) -{ - GlslEffect ge(basicVertexShader, "bar", "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, acceptsEmptyGeometryShader) -{ - GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, rejectsBrokenGeometryShader) -{ - GlslEffect ge(basicVertexShader, basicFragmentShader, "bar", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, usesProvidedDefines) -{ - const char* vertexShader = - "void main(void)\n" - "{\n" - " gl_Position = DEFINE_ZERO;\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = DEFINE_ONE;\n" - "}\n"; - std::vector compilerDefines; - compilerDefines.push_back("DEFINE_ZERO vec4(0.0)"); - compilerDefines.push_back("DEFINE_ONE vec4(1.0)"); - GlslEffect ge(vertexShader, fragmentShader, "", compilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_THAT(res->getVertexShader(), ::testing::HasSubstr(compilerDefines[0])); - EXPECT_THAT(res->getVertexShader(), ::testing::HasSubstr(compilerDefines[1])); - EXPECT_THAT(res->getFragmentShader(), ::testing::HasSubstr(compilerDefines[0])); - EXPECT_THAT(res->getFragmentShader(), ::testing::HasSubstr(compilerDefines[1])); -} - -TEST_F(AGlslEffect, generatedShaderWithDefaultVersionAndDefinesEmbedded) -{ - // this test will break if anything is changed in shader generation (even if no semantic change like adding newline!) - const char* vertexShader = - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - std::vector compilerDefines; - compilerDefines.push_back("FIRST_DEFINE foo"); - compilerDefines.push_back("OTHER_DEFINE bar"); - GlslEffect ge(vertexShader, fragmentShader, "", compilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - const char* expectedVertexShader = - "#version 100\n" - "#define FIRST_DEFINE foo\n" - "#define OTHER_DEFINE bar\n" - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* expectedFragmentShader = - "#version 100\n" - "#define FIRST_DEFINE foo\n" - "#define OTHER_DEFINE bar\n" - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - - EXPECT_STREQ(expectedVertexShader, res->getVertexShader()); - EXPECT_STREQ(expectedFragmentShader, res->getFragmentShader()); -} - -TEST_F(AGlslEffect, acceptsGLSLESShaders_Version300es) -{ - const char* vertexShader = R"SHADER( - #version 300 es - in lowp vec3 a_position; - out lowp vec3 v_position; - void main(void) - { - v_position = a_position; - gl_Position = vec4(a_position, 1.0); - })SHADER"; - const char* fragmentShader = R"SHADER( - #version 300 es - in lowp vec3 v_position; - out lowp vec4 color; - void main(void) - { - color = vec4(v_position, 1.0); - })SHADER"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, acceptsGLSLESShaders_Version310es) -{ - const char* vertexShader = R"SHADER( - #version 310 es - in lowp vec3 a_position; - out lowp vec3 v_position; - void main(void) - { - v_position = a_position; - gl_Position = vec4(a_position, 1.0); - })SHADER"; - const char* fragmentShader = R"SHADER( - #version 310 es - in lowp vec3 v_position; - out lowp vec4 color; - void main(void) - { - color = vec4(v_position, 1.0); - })SHADER"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, acceptsGLSLESShaders_Version310esWithGeometryShaderExtension) -{ - const char* vertexShader = R"SHADER( - #version 310 es - in lowp vec3 a_position; - out lowp vec3 v_position; - void main(void) - { - v_position = a_position; - gl_Position = vec4(a_position, 1.0); - })SHADER"; - const char* fragmentShader = R"SHADER( - #version 310 es - in lowp vec3 v_position; - out lowp vec4 color; - void main(void) - { - color = vec4(v_position, 1.0); - })SHADER"; - - const std::string geometryShader = R"SHADER( - #version 310 es - #extension GL_EXT_geometry_shader : enable - layout(points) in; - layout(points, max_vertices = 1) out; - void main() { - gl_Position = vec4(0.0); - EmitVertex(); - } - )SHADER"; - - GlslEffect ge(vertexShader, fragmentShader, geometryShader, emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - EXPECT_EQ(EDrawMode::Points, res->getGeometryShaderInputType()); - - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, doesNotAcceptMixedES2VertexAndES3FragmentShaders) -{ - const char* vertexShader = - "#version 100\n" - "attribute lowp vec3 a_position;\n" - "varying lowp vec3 v_position;\n" - "void main(void)\n" - "{\n" - " v_position = a_position;\n" - " gl_Position = vec4(a_position, 1.0);\n" - "}\n"; - const char* fragmentShader = - "#version 300 es\n" - "in lowp vec3 v_position;\n" - "out lowp vec4 color;\n" - "void main(void)\n" - "{\n" - " color = vec4(v_position, 1.0);\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, doesNotAcceptMixedES3VertexAndES2FragmentShaders) -{ - const char* vertexShader = - "#version 300 es\n" - "in lowp vec3 a_position;\n" - "out lowp vec3 v_position;\n" - "void main(void)\n" - "{\n" - " v_position = a_position;\n" - " gl_Position = vec4(a_position, 1.0);\n" - "}\n"; - const char* fragmentShader = - "#version 100\n" - "varying lowp vec3 v_position;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(v_position, 1.0);\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, canParseShaderInputs) -{ - const char* vertexShader = R"SHADER( - #version 320 es - precision highp float; - uniform mat4 uniformWithSemantic; - uniform mat3 matrix3x3; - uniform mat2 matrix2x2; - in vec3 attributeWithSemantic; - in float attributeFloat; - void main(void) - { - gl_Position = vec4(0.0); - })SHADER"; - const char* fragmentShader = R"SHADER( - #version 320 es - precision highp float; - uniform sampler2D uniformSampler; - uniform vec4 uniformVec; - out vec4 colorOut; - void main(void) - { - colorOut = vec4(0.0); - })SHADER"; - const std::string geometryShader = R"SHADER( - #version 320 es - precision highp float; - layout(points) in; - layout(points, max_vertices = 1) out; - out vec4 g_colorOut; - uniform float uniformGeomFloat; - uniform vec4 uniformGeomVec; - uniform sampler2D uniformGeomSampler; - void main() { - gl_Position = uniformGeomVec + vec4(uniformGeomFloat); - g_colorOut = texture(uniformGeomSampler, vec2(0.0)); - EmitVertex(); - } - )SHADER"; - - HashMap semantics; - semantics.put("uniformWithSemantic", EFixedSemantics::ModelViewProjectionMatrix); - semantics.put("attributeWithSemantic", EFixedSemantics::CameraWorldPosition); - - GlslEffect ge(vertexShader, fragmentShader, geometryShader, emptyCompilerDefines, semantics, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - const EffectInputInformationVector& uniforms = res->getUniformInputs(); - const EffectInputInformationVector& attributes = res->getAttributeInputs(); - - ASSERT_EQ(8u, uniforms.size()); - EXPECT_EQ(EffectInputInformation("uniformWithSemantic", 1, EDataType::Matrix44F, EFixedSemantics::ModelViewProjectionMatrix), uniforms[0]); - EXPECT_EQ(EffectInputInformation("matrix3x3", 1, EDataType::Matrix33F, EFixedSemantics::Invalid), uniforms[1]); - EXPECT_EQ(EffectInputInformation("matrix2x2", 1, EDataType::Matrix22F, EFixedSemantics::Invalid), uniforms[2]); - EXPECT_EQ(EffectInputInformation("uniformSampler", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[3]); - EXPECT_EQ(EffectInputInformation("uniformVec", 1, EDataType::Vector4F, EFixedSemantics::Invalid), uniforms[4]); - EXPECT_EQ(EffectInputInformation("uniformGeomFloat", 1, EDataType::Float, EFixedSemantics::Invalid), uniforms[5]); - EXPECT_EQ(EffectInputInformation("uniformGeomVec", 1, EDataType::Vector4F, EFixedSemantics::Invalid), uniforms[6]); - EXPECT_EQ(EffectInputInformation("uniformGeomSampler", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[7]); - - ASSERT_EQ(2u, attributes.size()); - EXPECT_EQ(EffectInputInformation("attributeWithSemantic", 1, EDataType::Vector3Buffer, EFixedSemantics::CameraWorldPosition), attributes[0]); - EXPECT_EQ(EffectInputInformation("attributeFloat", 1, EDataType::FloatBuffer, EFixedSemantics::Invalid), attributes[1]); -} - -TEST_F(AGlslEffect, canParseSamplerInputsGLSLES2) -{ - const char* vertexShader = - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "precision highp float;\n" - "uniform sampler2D s2d;\n" - "uniform samplerCube sc;\n" - "#extension GL_OES_EGL_image_external : require\n" - "uniform samplerExternalOES texExternal;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getAttributeInputs().size()); - - const EffectInputInformationVector& uniforms = res->getUniformInputs(); - - ASSERT_EQ(3u, uniforms.size()); - EXPECT_EQ(EffectInputInformation("s2d", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[0]); - EXPECT_EQ(EffectInputInformation("sc", 1, EDataType::TextureSamplerCube, EFixedSemantics::Invalid), uniforms[1]); - EXPECT_EQ(EffectInputInformation("texExternal", 1, EDataType::TextureSamplerExternal, EFixedSemantics::Invalid), uniforms[2]); -} - -TEST_F(AGlslEffect, canParseSamplerInputsGLSLES3) -{ - const char* vertexShader = - "#version 300 es\n" - "precision highp float;" - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "#version 300 es\n" - "precision highp float;" - "uniform sampler2D s2d;\n" - "uniform highp sampler3D s3d;\n" - "uniform samplerCube sc;\n" - "#extension GL_OES_EGL_image_external_essl3 : require\n" - "uniform samplerExternalOES texExternal;\n" - - "out vec4 color;\n" - "void main(void)\n" - "{\n" - " color = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getAttributeInputs().size()); - - const EffectInputInformationVector& uniforms = res->getUniformInputs(); - - ASSERT_EQ(4u, uniforms.size()); - EXPECT_EQ(EffectInputInformation("s2d", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[0]); - EXPECT_EQ(EffectInputInformation("s3d", 1, EDataType::TextureSampler3D, EFixedSemantics::Invalid), uniforms[1]); - EXPECT_EQ(EffectInputInformation("sc", 1, EDataType::TextureSamplerCube, EFixedSemantics::Invalid), uniforms[2]); - EXPECT_EQ(EffectInputInformation("texExternal", 1, EDataType::TextureSamplerExternal, EFixedSemantics::Invalid), uniforms[3]); -} - -TEST_F(AGlslEffect, canParseSamplerInputsGLSLES31) -{ - const char* vertexShader = - "#version 310 es\n" - "precision highp float;" - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "#version 310 es\n" - "precision highp float;" - "uniform sampler2D s2d;\n" - "uniform highp sampler2DMS s2dMS;" - "uniform highp sampler3D s3d;\n" - "uniform samplerCube sc;\n" - "#extension GL_OES_EGL_image_external_essl3 : require\n" - "uniform samplerExternalOES texExternal;\n" - "out vec4 color;\n" - "void main(void)\n" - "{\n" - " color = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getAttributeInputs().size()); - - const EffectInputInformationVector& uniforms = res->getUniformInputs(); - - ASSERT_EQ(5u, uniforms.size()); - EXPECT_EQ(EffectInputInformation("s2d", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[0]); - EXPECT_EQ(EffectInputInformation("s2dMS", 1, EDataType::TextureSampler2DMS, EFixedSemantics::Invalid), uniforms[1]); - EXPECT_EQ(EffectInputInformation("s3d", 1, EDataType::TextureSampler3D, EFixedSemantics::Invalid), uniforms[2]); - EXPECT_EQ(EffectInputInformation("sc", 1, EDataType::TextureSamplerCube, EFixedSemantics::Invalid), uniforms[3]); - EXPECT_EQ(EffectInputInformation("texExternal", 1, EDataType::TextureSamplerExternal, EFixedSemantics::Invalid), uniforms[4]); -} - - -TEST_F(AGlslEffect, canParseArrayInputs) -{ - const char* vertexShader = - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "precision highp float;\n" - "uniform vec3 v[4];\n" - "uniform mat4 m[2];\n" - "uniform int i[2];\n" - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getAttributeInputs().size()); - - const EffectInputInformationVector& uniforms = res->getUniformInputs(); - - ASSERT_EQ(3u, uniforms.size()); - EXPECT_EQ(EffectInputInformation("v", 4, EDataType::Vector3F, EFixedSemantics::Invalid), uniforms[0]); - EXPECT_EQ(EffectInputInformation("m", 2, EDataType::Matrix44F, EFixedSemantics::Invalid), uniforms[1]); - EXPECT_EQ(EffectInputInformation("i", 2, EDataType::Int32, EFixedSemantics::Invalid), uniforms[2]); -} - -TEST_F(AGlslEffect, failsWithWrongSemanticForType) -{ - const char* vertexShader = - "precision highp float;\n" - "uniform vec3 uniformWithWrongSemantic;\n" - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - - HashMap semantics; - semantics.put("uniformWithWrongSemantic", EFixedSemantics::ModelViewProjectionMatrix); - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, semantics, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, ignoresNormalGlobalsAndVaryings) -{ - const char* vertexShader = - "precision highp float;\n" - "mat4 foobar;\n" - "const float fl = float(0.1);\n" - "varying vec3 v3;\n" - "void main(void)\n" - "{\n" - " gl_Position = foobar * vec4(v3, fl);\n" - "}\n"; - const char* fragmentShader = - "precision highp float;\n" - "varying vec3 v3;\n" - "bool b;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(0u, res->getUniformInputs().size()); - EXPECT_EQ(0u, res->getAttributeInputs().size()); -} - -TEST_F(AGlslEffect, canParseStructUniform) -{ - const char* vertexShader = - "precision highp float;\n" - "struct S {\n" - " vec3 a;\n" - " vec3 b;\n" - "};\n" - "uniform S s;\n" - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(2u, res->getUniformInputs().size()); - VerifyUniformInputExists(*res, "s.a"); - VerifyUniformInputExists(*res, "s.b"); -} - -TEST_F(AGlslEffect, canParseArrayOfStructUniform) -{ - const char* vertexShader = - "precision highp float;\n" - "struct S {\n" - " vec3 a;\n" - " vec3 b;\n" - "};\n" - "uniform S s[4];\n" // Array declaration - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(8u, res->getUniformInputs().size()); - VerifyUniformInputExists(*res, "s[0].a"); - VerifyUniformInputExists(*res, "s[0].b"); - - VerifyUniformInputExists(*res, "s[1].a"); - VerifyUniformInputExists(*res, "s[1].b"); - - VerifyUniformInputExists(*res, "s[2].a"); - VerifyUniformInputExists(*res, "s[2].b"); - - VerifyUniformInputExists(*res, "s[3].a"); - VerifyUniformInputExists(*res, "s[3].b"); -} - -TEST_F(AGlslEffect, canParseNestedStructUniform) -{ - const char* vertexShader = - "precision highp float;\n" - "struct simpleStruct {\n" - " vec3 a;\n" - " vec3 b;\n" - "};\n" - "struct nestedStruct {\n" // Include previous struct as a member - " vec3 c;\n" - " simpleStruct d;\n" - "};\n" - "uniform nestedStruct ns;\n" - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(3u, res->getUniformInputs().size()); - VerifyUniformInputExists(*res, "ns.c"); - VerifyUniformInputExists(*res, "ns.d.a"); - VerifyUniformInputExists(*res, "ns.d.b"); -} - -TEST_F(AGlslEffect, canParseArrayOfNestedStructUniforms) -{ - const char* vertexShader = - "precision highp float;\n" - "struct simpleStruct {\n" - " vec3 a;\n" - " vec3 b;\n" - "};\n" - "struct nestedStruct {\n" // Include previous struct as a member - " simpleStruct c;\n" - "};\n" - "struct doubleNestedStruct {\n" // Add another level; this time as an array - " nestedStruct d[2];\n" - "};\n" - "uniform doubleNestedStruct ans[5];\n" // Array of those guys - "void main(void)\n" - "{\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0);\n" - "}\n"; - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(20u, res->getUniformInputs().size()); - - VerifyUniformInputExists(*res, "ans[0].d[0].c.a"); - VerifyUniformInputExists(*res, "ans[0].d[0].c.b"); - VerifyUniformInputExists(*res, "ans[0].d[1].c.a"); - VerifyUniformInputExists(*res, "ans[0].d[1].c.b"); - - VerifyUniformInputExists(*res, "ans[1].d[0].c.a"); - VerifyUniformInputExists(*res, "ans[1].d[0].c.b"); - VerifyUniformInputExists(*res, "ans[1].d[1].c.a"); - VerifyUniformInputExists(*res, "ans[1].d[1].c.b"); - - VerifyUniformInputExists(*res, "ans[2].d[0].c.a"); - VerifyUniformInputExists(*res, "ans[2].d[0].c.b"); - VerifyUniformInputExists(*res, "ans[2].d[1].c.a"); - VerifyUniformInputExists(*res, "ans[2].d[1].c.b"); - - VerifyUniformInputExists(*res, "ans[3].d[0].c.a"); - VerifyUniformInputExists(*res, "ans[3].d[0].c.b"); - VerifyUniformInputExists(*res, "ans[3].d[1].c.a"); - VerifyUniformInputExists(*res, "ans[3].d[1].c.b"); - - VerifyUniformInputExists(*res, "ans[4].d[0].c.a"); - VerifyUniformInputExists(*res, "ans[4].d[0].c.b"); - VerifyUniformInputExists(*res, "ans[4].d[1].c.a"); - VerifyUniformInputExists(*res, "ans[4].d[1].c.b"); -} - -class GlslEffectTestRunnable : public Runnable -{ -public: - GlslEffectTestRunnable() - : createdSuccessfully(false) - { - } - - void run() override - { - const std::vector defs; - const ramses_internal::HashMap sems; - const char* v = "void main(){gl_Position=vec4(0);}"; - const char* f = "void main(){gl_FragColor=vec4(0);}"; - - GlslEffect eff(v, f, "", defs, sems, "myname"); - std::unique_ptr resource(eff.createEffectResource(ResourceCacheFlag(0u))); - createdSuccessfully = static_cast(resource); - } - - bool createdSuccessfully; -}; - -TEST_F(AGlslEffect, UseGlslEffectFromDifferentThread) -{ - PlatformThread th("GlslEffectThrd"); - GlslEffectTestRunnable r; - th.start(r); - th.join(); - EXPECT_TRUE(r.createdSuccessfully); -} - -TEST_F(AGlslEffect, UseGlslEffectFromMainThread) -{ - GlslEffectTestRunnable r; - r.run(); - EXPECT_TRUE(r.createdSuccessfully); -} - -TEST_F(AGlslEffect, hasDefaultShaderVersion100) -{ - const std::string basicVertexShader_v100 = R"SHADER( - void main(void) - { - gl_Position = vec4(0.0); - } - )SHADER"; - const std::string basicFragmentShader_v100 = R"SHADER( - void main(void) - { - gl_FragColor = vec4(0.0); - })SHADER"; - - GlslEffect ge(basicVertexShader_v100, basicFragmentShader_v100, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - EXPECT_EQ(100u, ge.getShadingLanguageVersion()); -} - -TEST_F(AGlslEffect, isAbleToParseShadersWithVersionString) -{ - GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); -} - -TEST_F(AGlslEffect, acceptsReturnStatementsEveryWhere) -{ - const char* vertexShader = R"SHADER( - #version 320 es - precision highp float; - out float attr; - void fun() { - if (attr < 1.0) return; - return; - } - void main(void) - { - if (attr > 1.0) return; - gl_Position = vec4(0.0); - return; - })SHADER"; - - GlslEffect ge(vertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, acceptsLoopsInShaders) -{ - const char* vertexShader = R"SHADER( - #version 320 es - precision highp float; - uniform int value; - void main(void) - { - int i = 0; - for (i = 0; i < value; ++i) continue; - while (value < 10) --i; - do { break; } while (false); - gl_Position = vec4(0.0); - })SHADER"; - - GlslEffect ge(vertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, supportsExternalTextureExtension) -{ - const char* vertexShader = - "#version 100\n" - "precision highp float;\n" - "attribute vec2 texCoords;\n" - "varying vec2 texCoord;\n" - "void main(void)\n" - "{\n" - " texCoord = texCoords;\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "#version 100\n" - "precision highp float;\n" - "#extension GL_OES_EGL_image_external : require\n" - "uniform samplerExternalOES tex;\n" - "varying vec2 texCoord;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = texture2D(tex, texCoord);\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_TRUE(res); -} - -TEST_F(AGlslEffect, doesNotSupportFloatTextureExtension) -{ - const char* vertexShader = - "#version 100\n" - "precision highp float;\n" - "attribute vec2 texCoords;\n" - "varying vec2 texCoord;\n" - "void main(void)\n" - "{\n" - " texCoord = texCoords;\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "#version 100\n" - "precision highp float;\n" - "#extension GL_OES_texture_float : require\n" - "uniform sampler2D tex;\n" - "varying vec2 texCoord;\n" - "void main(void)\n" - "{\n" - " float color = texture2D(tex, texCoord);\n" - " gl_FragColor = vec4(vec3(color*255), 1.0);\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, doesNotSupport3DTextureExtension) -{ - const char* vertexShader = - "#version 100\n" - "precision highp float;\n" - "attribute vec3 texCoords;\n" - "varying vec3 texCoord;\n" - "void main(void)\n" - "{\n" - " texCoord = texCoords;\n" - " gl_Position = vec4(0.0);\n" - "}\n"; - const char* fragmentShader = - "#version 100\n" - "precision highp float;\n" - "#extension GL_OES_texture_3D : require\n" - "uniform mediump sampler3D tex;\n" - "varying vec3 texCoord;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = texture3D(tex, texCoord);\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, treatsUniformDeclaredInBothStagesWithSameNameAsSingleUniform) -{ - const char* vertexShader = - "uniform lowp vec4 dummy;\n" - "void main(void)\n" - "{\n" - " gl_Position = dummy;\n" - "}\n"; - const char* fragmentShader = - "uniform lowp vec4 dummy;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = dummy;\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - const std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_TRUE(res); - - EXPECT_EQ(1u, res->getUniformInputs().size()); -} - -TEST_F(AGlslEffect, rejectsEffectWithUniformDeclaredInBothStagesWithSameNameButDifferentDataType) -{ - const char* vertexShader = - "uniform lowp vec4 dummy;\n" - "void main(void)\n" - "{\n" - " gl_Position = dummy;\n" - "}\n"; - const char* fragmentShader = - "uniform lowp float dummy;\n" - "void main(void)\n" - "{\n" - " gl_FragColor = vec4(0.0, 0.0, 0.0, dummy);\n" - "}\n"; - - GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - const std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -// This is a bug in glslang, or maybe wrong configuration in ramses. In gles 100, active should be allowed -TEST_F(AGlslEffect, doesNotAcceptActiveAsKeywordInVersion100) -{ - const std::string basicVertexShader_v100 = R"SHADER( - attribute float active; - void main(void) - { - gl_Position = vec4(0.0); - } - )SHADER"; - const std::string basicFragmentShader_v100 = R"SHADER( - void main(void) - { - gl_FragColor = vec4(0.0); - })SHADER"; - GlslEffect ge(basicVertexShader_v100, basicFragmentShader_v100, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - EXPECT_FALSE(res); -} - -TEST_F(AGlslEffect, canRetrieveGLSLErrorMessage) -{ - const char* vertexShader = R"SHADER( - #version 320 es - out float inp; - void main(void) - { - gl_Position = vec4(0.0) - })SHADER"; - GlslEffect ge(vertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); - std::unique_ptr res(ge.createEffectResource(ResourceCacheFlag(0u))); - ASSERT_FALSE(res); - using namespace ::testing; - EXPECT_THAT(ge.getEffectErrorMessages(), - AnyOf(Eq("[GLSL Compiler] vertex shader Shader Parsing Error:\n" - "ERROR: 2:5: '' : syntax error\n" - "ERROR: 1 compilation errors. No code generated.\n\n\n"), - Eq("[GLSL Compiler] vertex shader Shader Parsing Error:\n" - "ERROR: 2:5: '' : syntax error, unexpected RIGHT_BRACE, expecting COMMA or SEMICOLON\n" - "ERROR: 1 compilation errors. No code generated.\n\n\n"))); -} diff --git a/client/test/MockActionCollector.h b/client/test/MockActionCollector.h deleted file mode 100644 index 052191305..000000000 --- a/client/test/MockActionCollector.h +++ /dev/null @@ -1,90 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_MOCKACTIONCOLLECTOR_H -#define RAMSES_MOCKACTIONCOLLECTOR_H - -#include "gmock/gmock.h" -#include "Components/ISceneGraphConsumerComponent.h" -#include "SceneAPI/SceneId.h" -#include "Scene/SceneActionCollection.h" -#include "SceneRendererHandlerMock.h" -#include "Components/SceneUpdate.h" - -namespace ramses -{ - class MockActionCollector : public ramses_internal::SceneRendererHandlerMock - { - public: - MockActionCollector() - : m_numReceivedActionLists(0) - , m_sceneGraphConsumer(nullptr) - { - } - - void init(ramses_internal::ISceneGraphConsumerComponent& sceneGraphConsumer) - { - m_sceneGraphConsumer = &sceneGraphConsumer; - } - - void resetCollecting() - { - m_collectedActions.clear(); - m_numReceivedActionLists = 0u; - } - - uint32_t getNumberOfActions() const - { - return m_collectedActions.numberOfActions(); - } - - ramses_internal::SceneActionCollection getCopyOfCollectedActions() - { - return m_collectedActions.copy(); - } - - uint32_t getNumReceivedActionLists() const - { - return m_numReceivedActionLists; - } - - private: - ramses_internal::SceneActionCollection m_collectedActions; - uint32_t m_numReceivedActionLists; - - ramses_internal::ISceneGraphConsumerComponent* m_sceneGraphConsumer; - - void handleInitializeScene(const ramses_internal::SceneInfo& sceneInfo, const ramses_internal::Guid& providerID) override - { - ramses_internal::SceneRendererHandlerMock::handleInitializeScene(sceneInfo, providerID); - } - - void handleSceneUpdate(const ramses_internal::SceneId& sceneId, ramses_internal::SceneUpdate&& sceneUpdate, const ramses_internal::Guid& providerID) override - { - m_collectedActions.append(sceneUpdate.actions); - ramses_internal::SceneRendererHandlerMock::handleSceneUpdate(sceneId, std::move(sceneUpdate), providerID); - ++m_numReceivedActionLists; - } - - void handleNewSceneAvailable(const ramses_internal::SceneInfo& newScene, const ramses_internal::Guid& providerID) override - { - ramses_internal::SceneRendererHandlerMock::handleNewSceneAvailable(newScene, providerID); - if (m_sceneGraphConsumer != nullptr) - { - m_sceneGraphConsumer->subscribeScene(providerID, newScene.sceneID); - } - } - - void handleSceneBecameUnavailable(const ramses_internal::SceneId& unavailableScene, const ramses_internal::Guid& providerID) override - { - ramses_internal::SceneRendererHandlerMock::handleSceneBecameUnavailable(unavailableScene, providerID); - } - }; -} - -#endif diff --git a/client/test/RamsesObjectOwnershipTest.cpp b/client/test/RamsesObjectOwnershipTest.cpp deleted file mode 100644 index 255b9c15e..000000000 --- a/client/test/RamsesObjectOwnershipTest.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/PickableObject.h" - -#include "RamsesClientImpl.h" -#include "SceneImpl.h" -#include "ClientTestUtils.h" -#include "RamsesObjectTestTypes.h" -#include "NodeImpl.h" -#include "MeshNodeImpl.h" -#include "CameraNodeImpl.h" -#include "GeometryBindingImpl.h" -#include "RenderGroupImpl.h" -#include "RenderPassImpl.h" -#include "BlitPassImpl.h" -#include "RenderBufferImpl.h" -#include "RenderTargetImpl.h" -#include "TextureSamplerImpl.h" -#include "DataObjectImpl.h" -#include "ArrayBufferImpl.h" -#include "Texture2DBufferImpl.h" -#include "PickableObjectImpl.h" -#include "SceneReferenceImpl.h" -#include "Texture2DImpl.h" -#include "Texture3DImpl.h" -#include "TextureCubeImpl.h" -#include "EffectImpl.h" - -#include "RamsesObjectTypeTraits.h" -#include "RamsesObjectTypeUtils.h" - -namespace ramses -{ - using namespace testing; - - template - class SceneOwnershipTest : public LocalTestClientWithScene, public testing::Test - { - public: - SceneOwnershipTest() : LocalTestClientWithScene() - { - } - - void expectNoFrameworkObjectsAllocated() - { - m_creationHelper.destroyAdditionalAllocatedSceneObjects(); - const ramses_internal::IScene& scene = this->m_scene.m_impl.getIScene(); - - for (ramses_internal::NodeHandle i(0); i < scene.getNodeCount(); ++i) - { - EXPECT_FALSE(scene.isNodeAllocated(i)); - } - - for (ramses_internal::CameraHandle i(0); i < scene.getCameraCount(); ++i) - { - EXPECT_FALSE(scene.isCameraAllocated(i)); - } - - for (ramses_internal::TransformHandle i(0); i < scene.getTransformCount(); ++i) - { - EXPECT_FALSE(scene.isTransformAllocated(i)); - } - - for (ramses_internal::RenderableHandle i(0); i < scene.getRenderableCount(); ++i) - { - EXPECT_FALSE(scene.isRenderableAllocated(i)); - } - - for (ramses_internal::RenderStateHandle i(0); i < scene.getRenderStateCount(); ++i) - { - EXPECT_FALSE(scene.isRenderStateAllocated(i)); - } - - for (ramses_internal::DataLayoutHandle i(0); i < scene.getDataLayoutCount(); ++i) - { - EXPECT_FALSE(scene.isDataLayoutAllocated(i)); - } - - for (ramses_internal::DataInstanceHandle i(0); i < scene.getDataInstanceCount(); ++i) - { - EXPECT_FALSE(scene.isDataInstanceAllocated(i)); - } - - for (ramses_internal::RenderPassHandle i(0); i < scene.getRenderPassCount(); ++i) - { - EXPECT_FALSE(scene.isRenderPassAllocated(i)); - } - - for (ramses_internal::RenderTargetHandle i(0); i < scene.getRenderTargetCount(); ++i) - { - EXPECT_FALSE(scene.isRenderTargetAllocated(i)); - } - - for (ramses_internal::TextureSamplerHandle i(0); i < scene.getTextureSamplerCount(); ++i) - { - EXPECT_FALSE(scene.isTextureSamplerAllocated(i)); - } - } - }; - - template - class ClientOwnershipTest : public LocalTestClientWithScene, public testing::Test{}; - - TYPED_TEST_SUITE(SceneOwnershipTest, SceneObjectTypes); - TYPED_TEST_SUITE(ClientOwnershipTest, ClientObjectTypes); - - TYPED_TEST(SceneOwnershipTest, sceneObjectsAreOfTypeSceneObject) - { - auto obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - EXPECT_TRUE(obj->isOfType(ERamsesObjectType::SceneObject)); - EXPECT_TRUE(obj->isOfType(ERamsesObjectType::ClientObject)); - } - - TYPED_TEST(SceneOwnershipTest, sceneObjectsHaveReferenceToTheirScene) - { - auto obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - EXPECT_EQ(&this->m_scene.m_impl, &obj->m_impl.getSceneImpl()); - EXPECT_EQ(&this->m_scene.m_impl.getIScene(), &obj->m_impl.getIScene()); - EXPECT_EQ(&this->client.m_impl, &obj->m_impl.getClientImpl()); - } - - TYPED_TEST(SceneOwnershipTest, sceneContainsCreatedObject) - { - const RamsesObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - const RamsesObject* sn = this->m_scene.findObjectByName("objectName"); - ASSERT_TRUE(nullptr != sn); - EXPECT_EQ(obj, sn); - EXPECT_EQ(obj, this->m_constRefToScene.findObjectByName("objectName")); - } - - // TODO Violin improve this test - // The test is very ugly, because it checks that "a scene does not contain ANY LL object after - // a specific HL object was destroyed. Instead, it should be testing that a scene does not - // contain the LL components of the HL object which is being deleted - TYPED_TEST(SceneOwnershipTest, sceneDoesNotContainDestroyedObject) - { - auto obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - EXPECT_EQ(StatusOK, this->m_scene.destroy(*obj)); - const RamsesObject* sn = this->m_scene.findObjectByName("objectName"); - ASSERT_TRUE(nullptr == sn); - - this->expectNoFrameworkObjectsAllocated(); - } - - TYPED_TEST(SceneOwnershipTest, creatingAndDestroyingObjectsUpdatesStatisticCounter) - { - this->m_scene.m_impl.getStatisticCollection().nextTimeInterval(); //object number is updated by nextTimeInterval() - uint32_t initialNumber = this->m_scene.m_impl.getStatisticCollection().statObjectsCount.getCounterValue(); - EXPECT_EQ(0u, this->m_scene.m_impl.getStatisticCollection().statObjectsCreated.getCounterValue()); - EXPECT_EQ(0u, this->m_scene.m_impl.getStatisticCollection().statObjectsDestroyed.getCounterValue()); - - auto obj = &this->template createObject("objectName"); - uint32_t numberCreated = this->m_scene.m_impl.getStatisticCollection().statObjectsCreated.getCounterValue(); - EXPECT_LE(1u, numberCreated); //some types create multiple scene objects (e.g. RenderTarget) - EXPECT_EQ(0u, this->m_scene.m_impl.getStatisticCollection().statObjectsDestroyed.getCounterValue()); - - this->m_scene.m_impl.getStatisticCollection().nextTimeInterval(); - EXPECT_EQ(initialNumber + numberCreated, this->m_scene.m_impl.getStatisticCollection().statObjectsCount.getCounterValue()); - - this->m_scene.destroy(*obj); - - EXPECT_LE(1u, this->m_scene.m_impl.getStatisticCollection().statObjectsDestroyed.getCounterValue()); - - this->m_scene.m_impl.getStatisticCollection().nextTimeInterval(); - EXPECT_GT(initialNumber + numberCreated, this->m_scene.m_impl.getStatisticCollection().statObjectsCount.getCounterValue()); - EXPECT_LE(initialNumber, this->m_scene.m_impl.getStatisticCollection().statObjectsCount.getCounterValue()); - } - - TYPED_TEST(ClientOwnershipTest, clientContainsCreatedObject) - { - const RamsesObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - const RamsesObject* sn = this->client.findSceneByName("objectName"); - ASSERT_TRUE(nullptr != sn); - EXPECT_EQ(obj, sn); - } - - TYPED_TEST(ClientOwnershipTest, clientDoesNotContainDestroyedObject) - { - RamsesObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - this->client.destroy(static_cast(*obj)); - const RamsesObject* sn = this->client.findSceneByName("objectName"); - ASSERT_TRUE(nullptr == sn); - } - - TYPED_TEST(SceneOwnershipTest, sceneObjectNameChanged) - { - RamsesObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - obj->setName("otherObjectName"); - RamsesObject* sn = this->m_scene.findObjectByName("otherObjectName"); - ASSERT_TRUE(nullptr != sn); - EXPECT_EQ(obj, sn); - } - - TYPED_TEST(ClientOwnershipTest, clientObjectsAreOfTypeClientObject) - { - const ClientObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - EXPECT_TRUE(obj->isOfType(ERamsesObjectType::ClientObject)); - } - - TYPED_TEST(ClientOwnershipTest, clientObjectsHaveReferenceToTheirClient) - { - const ClientObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - EXPECT_EQ(&this->client.m_impl, &obj->m_impl.getClientImpl()); - } - - TYPED_TEST(ClientOwnershipTest, clientObjectNameChanged) - { - RamsesObject* obj = &this->template createObject("objectName"); - ASSERT_TRUE(nullptr != obj); - obj->setName("otherObjectName"); - RamsesObject* sn = this->client.findSceneByName("otherObjectName"); - ASSERT_TRUE(nullptr != sn); - EXPECT_EQ(obj, sn); - } - - TYPED_TEST(SceneOwnershipTest, sceneNotContainsDestroyedObject) - { - RamsesObject& obj = this->template createObject("objectName"); - TypeParam& objTyped = static_cast(obj); - - this->m_scene.destroy(objTyped); - const RamsesObject* sn = this->m_scene.findObjectByName("objectName"); - EXPECT_EQ(nullptr, sn); - } - - TYPED_TEST(ClientOwnershipTest, clientDoesNotFindDestroyedObject) - { - RamsesObject* obj = &this->template createObject("objectName"); - TypeParam& objTyped = static_cast(*obj); - ASSERT_TRUE(obj); - - this->client.destroy(objTyped); - RamsesObject* sn = this->client.findSceneByName("objectName"); - ASSERT_FALSE(sn); - } -} diff --git a/client/test/RamsesObjectTest.cpp b/client/test/RamsesObjectTest.cpp deleted file mode 100644 index 940012571..000000000 --- a/client/test/RamsesObjectTest.cpp +++ /dev/null @@ -1,171 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/SceneReference.h" - -#include "ramses-utils.h" -#include "RamsesObjectTypeUtils.h" -#include "RamsesObjectTestTypes.h" -#include "ClientTestUtils.h" - -using namespace testing; - -namespace ramses -{ - template - class RamsesObjectTest : public LocalTestClientWithScene, public testing::Test - { - }; - - TYPED_TEST_SUITE(RamsesObjectTest, RamsesObjectTypes); - - TYPED_TEST(RamsesObjectTest, getType) - { - const RamsesObject& obj = this->template createObject("object"); - const ERamsesObjectType type = TYPE_ID_OF_RAMSES_OBJECT::ID; - EXPECT_EQ(type, obj.getType()); - EXPECT_TRUE(obj.isOfType(ERamsesObjectType::RamsesObject)); - } - - TYPED_TEST(RamsesObjectTest, isOfTypeForAllDefinedBaseClasses) - { - const RamsesObject& obj = this->template createObject("object"); - ERamsesObjectType type = TYPE_ID_OF_RAMSES_OBJECT::ID; - - while (type != ERamsesObjectType::Invalid) - { - EXPECT_TRUE(obj.isOfType(type)); - type = RamsesObjectTraits[static_cast(type)].baseClassTypeID; - } - } - - TYPED_TEST(RamsesObjectTest, getSetName) - { - RamsesObject& obj = this->template createObject("object"); - ASSERT_STREQ("object", obj.getName()); - EXPECT_EQ(StatusOK, obj.setName("newName")); - ASSERT_STREQ("newName", obj.getName()); - - if (obj.isOfType(ERamsesObjectType::SceneObject)) - { - EXPECT_EQ(nullptr, this->m_scene.findObjectByName("object")); - EXPECT_EQ(&obj, this->m_scene.findObjectByName("newName")); - } - } - - TYPED_TEST(RamsesObjectTest, convertToTypes) - { - RamsesObject& obj = this->template createObject("object"); - const RamsesObject& constObj = obj; - - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(obj)); - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(constObj)); - - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(obj)); - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(constObj)); - } - - TYPED_TEST(RamsesObjectTest, convertToItsClosestBaseClass) - { - using BaseClassType = typename CLASS_OF_RAMSES_OBJECT_TYPE::ID>::BaseTypeID>::ClassType; - - RamsesObject& obj = this->template createObject("object"); - const RamsesObject& constObj = obj; - - if (TYPE_ID_OF_RAMSES_OBJECT::ID != ERamsesObjectType::Invalid) - { - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(obj)); - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(constObj)); - } - } - - TYPED_TEST(RamsesObjectTest, getRamsesObjectFromImpl) - { - RamsesObject& obj = this->template createObject("object"); - const RamsesObject& constObj = obj; - - RamsesObjectImpl& objImpl = obj.m_impl; - const RamsesObjectImpl& constObjImpl = constObj.m_impl; - - EXPECT_EQ(&obj, &objImpl.getRamsesObject()); - EXPECT_EQ(&constObj, &constObjImpl.getRamsesObject()); - } - - TYPED_TEST(RamsesObjectTest, validationStringIsNonEmptyAfterCall) - { - const RamsesObject& obj = this->template createObject("object"); - - EXPECT_STREQ("", obj.getValidationReport()); - std::ignore = obj.validate(); - EXPECT_STRNE("", obj.getValidationReport()); - } - - TYPED_TEST(RamsesObjectTest, validationStringContainsObjectTypeAndName) - { - const RamsesObject& obj = this->template createObject("object"); - - EXPECT_NE(StatusOK, obj.m_impl.addErrorEntry("dummy")); - std::ignore = obj.validate(); - const std::string validationReport = obj.getValidationReport(EValidationSeverity::Info); - - EXPECT_THAT(validationReport, ::testing::HasSubstr(RamsesObjectTypeUtils::GetRamsesObjectTypeName(obj.getType()))); - EXPECT_THAT(validationReport, ::testing::HasSubstr(obj.getName())); - } - - TYPED_TEST(RamsesObjectTest, validationIgnoresWrongAPIUsage) - { - RamsesObject& obj = this->template createObject("object"); - - // simulate some API usage error - EXPECT_NE(StatusOK, obj.m_impl.addErrorEntry("dummy error msg")); - - std::ignore = obj.validate(); - EXPECT_THAT(obj.getValidationReport(EValidationSeverity::Warning), ::testing::Not(::testing::HasSubstr("dummy error msg"))); - - // will not appear in validation of scene either - std::ignore = this->m_scene.validate(); - EXPECT_THAT(this->m_scene.getValidationReport(EValidationSeverity::Warning), ::testing::Not(::testing::HasSubstr("dummy error msg"))); - } - - TYPED_TEST(RamsesObjectTest, convertToWrongType) - { - RamsesObject& obj = this->template createObject("object"); - const RamsesObject& constObj = obj; - - if (constObj.getType() != ERamsesObjectType::PerspectiveCamera) - { - EXPECT_TRUE(nullptr == RamsesUtils::TryConvert(obj)); - EXPECT_TRUE(nullptr == RamsesUtils::TryConvert(constObj)); - } - else - { - EXPECT_TRUE(nullptr == RamsesUtils::TryConvert(obj)); - EXPECT_TRUE(nullptr == RamsesUtils::TryConvert(constObj)); - } - } -} diff --git a/client/test/RenderBufferTest.cpp b/client/test/RenderBufferTest.cpp deleted file mode 100644 index fea615deb..000000000 --- a/client/test/RenderBufferTest.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "RenderBufferImpl.h" -#include "ClientTestUtils.h" - -using namespace testing; - -namespace ramses -{ - class RenderBufferTest : public LocalTestClientWithScene, public testing::Test - { - protected: - void useInRenderPass(const RenderBuffer& rb) - { - RenderTargetDescription rtDesc; - rtDesc.addRenderBuffer(rb); - RenderTarget* rt = this->m_scene.createRenderTarget(rtDesc); - RenderPass* rp = this->m_scene.createRenderPass(); - OrthographicCamera* orthoCam = this->m_scene.createOrthographicCamera("camera"); - orthoCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - orthoCam->setViewport(0, 0, 100, 200); - rp->setCamera(*orthoCam); - rp->setRenderTarget(rt); - } - - void useInBlitPassAsSource(const RenderBuffer& rb) - { - const RenderBuffer* dummyRb = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - this->m_scene.createBlitPass(rb, *dummyRb); - } - - void useInBlitPassAsDestination(const RenderBuffer& rb) - { - const RenderBuffer* dummyRb = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - this->m_scene.createBlitPass(*dummyRb, rb); - } - }; - - TEST_F(RenderBufferTest, canCreateRenderBuffer) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(600u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 4u, "RenderBuffer"); - ASSERT_TRUE(renderBuffer != nullptr); - EXPECT_EQ(600u, renderBuffer->getWidth()); - EXPECT_EQ(400u, renderBuffer->getHeight()); - EXPECT_EQ(ERenderBufferType::Color, renderBuffer->getBufferType()); - EXPECT_EQ(ERenderBufferFormat::RGBA8, renderBuffer->getBufferFormat()); - EXPECT_EQ(ERenderBufferAccessMode::WriteOnly, renderBuffer->getAccessMode()); - EXPECT_EQ(4u, renderBuffer->getSampleCount()); - const ramses_internal::RenderBufferHandle rbHandle = renderBuffer->m_impl.getRenderBufferHandle(); - EXPECT_TRUE(m_internalScene.isRenderBufferAllocated(rbHandle)); - } - - TEST_F(RenderBufferTest, failsToCreateRenderBufferWithZeroWidthOrHeight) - { - RenderBuffer* rbWithZeroWidth = m_scene.createRenderBuffer(0u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(nullptr, rbWithZeroWidth); - - RenderBuffer* rbWithZeroHeight = m_scene.createRenderBuffer(400u, 0u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(nullptr, rbWithZeroHeight); - } - - TEST_F(RenderBufferTest, failsToCreateRenderBufferOfIncompatibleTypeAndFormat) - { - EXPECT_TRUE(nullptr == m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite)); - EXPECT_TRUE(nullptr == m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::Depth24_Stencil8, ERenderBufferAccessMode::ReadWrite)); - EXPECT_TRUE(nullptr == m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24_Stencil8, ERenderBufferAccessMode::ReadWrite)); - EXPECT_TRUE(nullptr == m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Depth, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite)); - EXPECT_TRUE(nullptr == m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::DepthStencil, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite)); - EXPECT_TRUE(nullptr == m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::DepthStencil, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite)); - } - - TEST_F(RenderBufferTest, canCreateReadWriteMSAARenderBuffer) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(600u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u, "RenderBuffer"); - ASSERT_TRUE(renderBuffer != nullptr); - EXPECT_EQ(600u, renderBuffer->getWidth()); - EXPECT_EQ(400u, renderBuffer->getHeight()); - EXPECT_EQ(ERenderBufferType::Color, renderBuffer->getBufferType()); - EXPECT_EQ(ERenderBufferFormat::RGBA8, renderBuffer->getBufferFormat()); - EXPECT_EQ(ERenderBufferAccessMode::ReadWrite, renderBuffer->getAccessMode()); - EXPECT_EQ(4u, renderBuffer->getSampleCount()); - const ramses_internal::RenderBufferHandle rbHandle = renderBuffer->m_impl.getRenderBufferHandle(); - EXPECT_TRUE(m_internalScene.isRenderBufferAllocated(rbHandle)); - } - - TEST_F(RenderBufferTest, reportsErrorIfNotUsedInAnyRenderPassNorBlitPass) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - EXPECT_NE(StatusOK, renderBuffer->validate()); - } - - TEST_F(RenderBufferTest, reportsErrorIfUsedInRenderPassButNotReferencedByAnySamplerNorUsedAsBlitPassSource) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - useInRenderPass(*renderBuffer); - EXPECT_NE(StatusOK, renderBuffer->validate()); - } - - TEST_F(RenderBufferTest, reportsErrorIfReferencedBySamplerButNotUsedInAnyRenderPassNorUsedAsBlitPassDestination) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Nearest, *renderBuffer); - EXPECT_NE(StatusOK, renderBuffer->validate()); - } - - TEST_F(RenderBufferTest, validatesWhenUsedInRenderPassAndReferencedBySampler) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - useInRenderPass(*renderBuffer); - m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Nearest, *renderBuffer); - EXPECT_EQ(StatusOK, renderBuffer->validate()); - } - - TEST_F(RenderBufferTest, validatesWhenUsedAsBlitPassDestinationAndReferencedBySampler) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - useInBlitPassAsDestination(*renderBuffer); - m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Nearest, *renderBuffer); - EXPECT_EQ(StatusOK, renderBuffer->validate()); - } - - TEST_F(RenderBufferTest, validatesWhenNOTReferencedBySamplerButUsedAsBlitPassSource) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - useInRenderPass(*renderBuffer); - useInBlitPassAsSource(*renderBuffer); - EXPECT_EQ(StatusOK, renderBuffer->validate()); - } - - TEST_F(RenderBufferTest, doesNotReportsErrorIfUsedInRenderPassButNotReferencedByAnySampler_DepthBufferType) - { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite); - ASSERT_TRUE(renderBuffer != nullptr); - useInRenderPass(*renderBuffer); - EXPECT_EQ(StatusOK, renderBuffer->validate()); - } -} diff --git a/client/test/RenderGroupTest.cpp b/client/test/RenderGroupTest.cpp deleted file mode 100644 index bf2a7eb7d..000000000 --- a/client/test/RenderGroupTest.cpp +++ /dev/null @@ -1,523 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "ClientTestUtils.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/GeometryBinding.h" -#include "RenderGroupImpl.h" -#include "MeshNodeImpl.h" -#include "RamsesClientImpl.h" -#include "SceneAPI/RenderGroup.h" -#include "SceneAPI/RenderGroupUtils.h" - -using namespace testing; -using namespace ramses_internal; - -namespace ramses -{ - class ARenderGroup : public LocalTestClientWithScene, public testing::Test - { - protected: - ARenderGroup() - : LocalTestClientWithScene() - , renderGroup(*m_scene.createRenderGroup("RenderGroup")) - , renderGroup2(*m_scene.createRenderGroup("RenderGroup2")) - { - } - - void expectNumMeshesContained(uint32_t numMeshes, const RenderGroup& group) - { - EXPECT_EQ(numMeshes, static_cast(group.m_impl.getAllMeshes().size())); - } - - void expectNumRenderGroupsContained(uint32_t numRenderGroups, const RenderGroup& group) - { - EXPECT_EQ(numRenderGroups, static_cast(group.m_impl.getAllRenderGroups().size())); - } - - void expectMeshContained(const MeshNode& mesh, int32_t order, const RenderGroup& group) - { - EXPECT_TRUE(group.containsMeshNode(mesh)); - int32_t actualOrder = 0; - EXPECT_EQ(StatusOK, group.getMeshNodeOrder(mesh, actualOrder)); - EXPECT_EQ(order, actualOrder); - - const auto& internalRg = m_internalScene.getRenderGroup(group.m_impl.getRenderGroupHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderable(mesh.m_impl.getRenderableHandle(), internalRg)); - EXPECT_EQ(order, ramses_internal::RenderGroupUtils::FindRenderableEntry(mesh.m_impl.getRenderableHandle(), internalRg)->order); - } - - void expectRenderGroupContained(const RenderGroup& nestedRenderGroup, int32_t order, const RenderGroup& group) - { - EXPECT_TRUE(group.containsRenderGroup(nestedRenderGroup)); - int32_t actualOrder = 0; - EXPECT_EQ(StatusOK, group.getRenderGroupOrder(nestedRenderGroup, actualOrder)); - EXPECT_EQ(order, actualOrder); - - const auto& internalRg = m_internalScene.getRenderGroup(group.m_impl.getRenderGroupHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(nestedRenderGroup.m_impl.getRenderGroupHandle(), internalRg)); - EXPECT_EQ(order, ramses_internal::RenderGroupUtils::FindRenderGroupEntry(nestedRenderGroup.m_impl.getRenderGroupHandle(), internalRg)->order); - } - - void expectMeshNotContained(const MeshNode& mesh, const RenderGroup& group) - { - EXPECT_FALSE(group.containsMeshNode(mesh)); - const auto& internalRg = m_internalScene.getRenderGroup(group.m_impl.getRenderGroupHandle()); - EXPECT_FALSE(ramses_internal::RenderGroupUtils::ContainsRenderable(mesh.m_impl.getRenderableHandle(), internalRg)); - } - - void expectRenderGroupNotContained(const RenderGroup& nestedRenderGroup, const RenderGroup& group) - { - EXPECT_FALSE(group.containsRenderGroup(nestedRenderGroup)); - const auto& internalRg = m_internalScene.getRenderGroup(group.m_impl.getRenderGroupHandle()); - EXPECT_FALSE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(nestedRenderGroup.m_impl.getRenderGroupHandle(), internalRg)); - } - - void addValidMeshToRenderGroup() - { - MeshNode& mesh = createValidMeshNode(); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(mesh, 3)); - } - - void addBrokenMeshToRenderGroup() - { - addBrokenMeshToRenderGroup(renderGroup); - } - - void addBrokenMeshToRenderGroup(RenderGroup& renderGroupParam) - { - MeshNode* mesh = m_scene.createMeshNode(); - Appearance* appearance = m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); - mesh->setAppearance(*appearance); - // missing geometry binding - EXPECT_EQ(StatusOK, renderGroupParam.addMeshNode(*mesh, 3)); - } - - RenderGroup& renderGroup; - RenderGroup& renderGroup2; - }; - - TEST_F(ARenderGroup, canValidate) - { - addValidMeshToRenderGroup(); - - EXPECT_EQ(StatusOK, renderGroup.validate()); - } - - TEST_F(ARenderGroup, validationGivesWarningIfRenderGroupIsEmpty) - { - EXPECT_NE(StatusOK, renderGroup.validate()); - } - - TEST_F(ARenderGroup, validationGivesWarningIfNestedRenderGroupIsEmpty) - { - addValidMeshToRenderGroup(); - ASSERT_EQ(StatusOK, renderGroup.validate()); - - renderGroup.addRenderGroup(renderGroup2); - - EXPECT_NE(StatusOK, renderGroup.validate()); - } - - TEST_F(ARenderGroup, validatesIfEmptyButNestedRenderGroupIsNot) - { - // empty -> invalid - EXPECT_NE(StatusOK, renderGroup.validate()); - - // nested group also empty -> invalid - renderGroup.addRenderGroup(renderGroup2); - EXPECT_NE(StatusOK, renderGroup.validate()); - - // add mesh to nested group -> valid - EXPECT_EQ(StatusOK, renderGroup2.addMeshNode(createValidMeshNode(), 3)); - EXPECT_EQ(StatusOK, renderGroup.validate()); - } - - TEST_F(ARenderGroup, validationGivesWarningIfRenderGroupContainsInvalidMesh) - { - addBrokenMeshToRenderGroup(); - - EXPECT_NE(StatusOK, renderGroup.validate()); - } - - TEST_F(ARenderGroup, validationGivesWarningIfNestedRenderGroupContainsInvalidMesh) - { - addValidMeshToRenderGroup(); - ASSERT_EQ(StatusOK, renderGroup.validate()); - - addBrokenMeshToRenderGroup(renderGroup2); - - ASSERT_EQ(StatusOK, renderGroup.validate()); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(renderGroup2)); - - EXPECT_NE(StatusOK, renderGroup.validate()); - } - - TEST_F(ARenderGroup, canAddMeshNodes) - { - MeshNode* mesh = m_scene.createMeshNode(); - MeshNode* mesh2 = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 3)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh2, -7)); - - expectNumMeshesContained(2u, renderGroup); - expectMeshContained(*mesh, 3, renderGroup); - expectMeshContained(*mesh2, -7, renderGroup); - } - - TEST_F(ARenderGroup, canAddRenderGroups) - { - addValidMeshToRenderGroup(); - MeshNode& mesh2 = createValidMeshNode(); - ASSERT_EQ(StatusOK, renderGroup2.addMeshNode(mesh2, 5)); - - EXPECT_EQ(StatusOK, renderGroup.addRenderGroup(renderGroup2, 2)); - - EXPECT_EQ(StatusOK, renderGroup.validate()); - EXPECT_EQ(StatusOK, renderGroup2.validate()); - } - - TEST_F(ARenderGroup, reportsErrorWhenAddMeshNodeFromAnotherScene) - { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - MeshNode* mesh = anotherScene.createMeshNode(); - - EXPECT_NE(StatusOK, renderGroup.addMeshNode(*mesh)); - } - - TEST_F(ARenderGroup, reportsErrorWhenAddRenderGroupFromAnotherScene) - { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - RenderGroup* nestedRenderGroup = anotherScene.createRenderGroup(); - - EXPECT_NE(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup)); - } - - TEST_F(ARenderGroup, canCheckContainmentOfMeshes) - { - MeshNode* mesh = m_scene.createMeshNode(); - MeshNode* mesh2 = m_scene.createMeshNode(); - MeshNode* mesh3 = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh3, 3)); - - expectNumMeshesContained(2u, renderGroup); - expectMeshContained(*mesh, 1, renderGroup); - expectMeshNotContained(*mesh2, renderGroup); - expectMeshContained(*mesh3, 3, renderGroup); - } - - TEST_F(ARenderGroup, canCheckContainmentOfNestedRenderGroups) - { - expectRenderGroupNotContained(renderGroup2, renderGroup); - - EXPECT_EQ(StatusOK, renderGroup.addRenderGroup(renderGroup2, 2)); - - expectNumRenderGroupsContained(1u, renderGroup); - expectRenderGroupContained(renderGroup2, 2, renderGroup); - } - - TEST_F(ARenderGroup, doesNotDirectlyContainRenderablesOfNestedRenderGroups) - { - MeshNode* mesh = m_scene.createMeshNode(); - - ASSERT_EQ(StatusOK, renderGroup2.addMeshNode(*mesh, 1)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(renderGroup2)); - - expectMeshNotContained(*mesh, renderGroup); - } - - TEST_F(ARenderGroup, canRemoveMesh) - { - MeshNode* mesh = m_scene.createMeshNode(); - ASSERT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.removeMeshNode(*mesh)); - - expectNumMeshesContained(0u, renderGroup); - expectMeshNotContained(*mesh, renderGroup); - } - - TEST_F(ARenderGroup, canRemoveRenderGroup) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 1)); - EXPECT_EQ(StatusOK, renderGroup.removeRenderGroup(*nestedRenderGroup)); - - expectNumRenderGroupsContained(0u, renderGroup); - expectRenderGroupNotContained(*nestedRenderGroup, renderGroup); - } - - TEST_F(ARenderGroup, removingMeshDoesNotAffectOtherMeshes) - { - MeshNode* mesh = m_scene.createMeshNode(); - MeshNode* mesh2 = m_scene.createMeshNode(); - MeshNode* mesh3 = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh2, 2)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh3, 3)); - - EXPECT_EQ(StatusOK, renderGroup.removeMeshNode(*mesh2)); - - expectNumMeshesContained(2u, renderGroup); - expectMeshContained(*mesh, 1, renderGroup); - expectMeshNotContained(*mesh2, renderGroup); - expectMeshContained(*mesh3, 3, renderGroup); - } - - TEST_F(ARenderGroup, removingRenderGroupDoesNotAffectOtherRenderGroups) - { - RenderGroup* nestedRenderGroup1 = m_scene.createRenderGroup(); - RenderGroup* nestedRenderGroup2 = m_scene.createRenderGroup(); - RenderGroup* nestedRenderGroup3 = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup1, 1)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup2, 2)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup3, 3)); - - ASSERT_EQ(StatusOK, renderGroup.removeRenderGroup(*nestedRenderGroup2)); - - expectNumRenderGroupsContained(2u, renderGroup); - expectRenderGroupContained(*nestedRenderGroup1, 1, renderGroup); - expectRenderGroupNotContained(*nestedRenderGroup2, renderGroup); - expectRenderGroupContained(*nestedRenderGroup3, 3, renderGroup); - } - - TEST_F(ARenderGroup, doesNotContainMeshWhichWasDestroyed) - { - MeshNode* mesh = m_scene.createMeshNode(); - ASSERT_EQ(StatusOK, renderGroup.addMeshNode(*mesh)); - ASSERT_EQ(StatusOK, m_scene.destroy(*mesh)); - - expectNumMeshesContained(0u, renderGroup); - } - - TEST_F(ARenderGroup, doesNotContainRenderGroupWhichWasDestroyed) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup)); - ASSERT_EQ(StatusOK, m_scene.destroy(*nestedRenderGroup)); - - expectNumRenderGroupsContained(0u, renderGroup); - } - - TEST_F(ARenderGroup, destroyingMeshDoesNotAffectOtherMeshes) - { - MeshNode* mesh = m_scene.createMeshNode(); - MeshNode* mesh2 = m_scene.createMeshNode(); - MeshNode* mesh3 = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh2, 2)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh3, 3)); - - EXPECT_EQ(StatusOK, m_scene.destroy(*mesh2)); - - expectNumMeshesContained(2u, renderGroup); - expectMeshContained(*mesh, 1, renderGroup); - expectMeshContained(*mesh3, 3, renderGroup); - } - - TEST_F(ARenderGroup, destroyingRenderGroupDoesNotAffectOtherRenderGroups) - { - RenderGroup* nestedRenderGroup1 = m_scene.createRenderGroup(); - RenderGroup* nestedRenderGroup2 = m_scene.createRenderGroup(); - RenderGroup* nestedRenderGroup3 = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup1, 1)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup2, 2)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup3, 3)); - - EXPECT_EQ(StatusOK, m_scene.destroy(*nestedRenderGroup2)); - - expectNumRenderGroupsContained(2u, renderGroup); - expectRenderGroupContained(*nestedRenderGroup1, 1, renderGroup); - expectRenderGroupContained(*nestedRenderGroup3, 3, renderGroup); - } - - TEST_F(ARenderGroup, canRemoveAllMeshes) - { - MeshNode* mesh = m_scene.createMeshNode(); - MeshNode* mesh2 = m_scene.createMeshNode(); - MeshNode* mesh3 = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh2, 2)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh3, 3)); - - EXPECT_EQ(StatusOK, renderGroup.removeAllRenderables()); - - expectNumMeshesContained(0u, renderGroup); - expectMeshNotContained(*mesh, renderGroup); - expectMeshNotContained(*mesh2, renderGroup); - expectMeshNotContained(*mesh3, renderGroup); - } - - TEST_F(ARenderGroup, canRemoveAllRenderGroups) - { - RenderGroup* nestedRenderGroup1 = m_scene.createRenderGroup(); - RenderGroup* nestedRenderGroup2 = m_scene.createRenderGroup(); - RenderGroup* nestedRenderGroup3 = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup1, 1)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup2, 2)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup3, 3)); - - EXPECT_EQ(StatusOK, renderGroup.removeAllRenderGroups()); - - expectNumRenderGroupsContained(0u, renderGroup); - expectRenderGroupNotContained(*nestedRenderGroup1, renderGroup); - expectRenderGroupNotContained(*nestedRenderGroup2, renderGroup); - expectRenderGroupNotContained(*nestedRenderGroup3, renderGroup); - } - - TEST_F(ARenderGroup, reportsErrorWhenTryingToRemoveMeshWhichWasNotAdded) - { - MeshNode* mesh = m_scene.createMeshNode(); - status_t status = renderGroup.removeMeshNode(*mesh); - - EXPECT_NE(StatusOK, status); - } - - TEST_F(ARenderGroup, reportsErrorWhenTryingToRemoveRenderGroupWhichWasNotAdded) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - EXPECT_NE(StatusOK, renderGroup.removeRenderGroup(*nestedRenderGroup)); - } - - TEST_F(ARenderGroup, canChangeMeshOrderByReaddingIt) - { - MeshNode* mesh = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 999)); - - expectMeshContained(*mesh, 999, renderGroup); - } - - TEST_F(ARenderGroup, canChangeRenderGroupOrderByReaddingIt) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 1)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 999)); - - expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup); - } - - TEST_F(ARenderGroup, canChangeMeshOrderByRemovingItAndAddingAgain) - { - MeshNode* mesh = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 1)); - EXPECT_EQ(StatusOK, renderGroup.removeMeshNode(*mesh)); - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 999)); - - expectMeshContained(*mesh, 999, renderGroup); - } - - TEST_F(ARenderGroup, canChangeRenderGroupOrderByRemovingItAndAddingAgain) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 1)); - ASSERT_EQ(StatusOK, renderGroup.removeRenderGroup(*nestedRenderGroup)); - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 999)); - - expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup); - } - - TEST_F(ARenderGroup, meshCanBeAddedToTwoGroupsWithDifferentOrder) - { - MeshNode* mesh = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 3)); - EXPECT_EQ(StatusOK, renderGroup2.addMeshNode(*mesh, 999)); - - expectNumMeshesContained(1u, renderGroup); - expectNumMeshesContained(1u, renderGroup2); - expectMeshContained(*mesh, 3, renderGroup); - expectMeshContained(*mesh, 999, renderGroup2); - } - - TEST_F(ARenderGroup, renderGroupCanBeAddedToTwoGroupsWithDifferentOrder) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - - EXPECT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 3)); - EXPECT_EQ(StatusOK, renderGroup2.addRenderGroup(*nestedRenderGroup, 999)); - - expectNumRenderGroupsContained(1u, renderGroup); - expectNumRenderGroupsContained(1u, renderGroup2); - expectRenderGroupContained(*nestedRenderGroup, 3, renderGroup); - expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup2); - } - - TEST_F(ARenderGroup, removingMeshFromGroupDoesNotAffectItsPlaceInAnotherGroup) - { - MeshNode* mesh = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 3)); - EXPECT_EQ(StatusOK, renderGroup2.addMeshNode(*mesh, 999)); - - EXPECT_EQ(StatusOK, renderGroup.removeMeshNode(*mesh)); - - expectNumMeshesContained(0u, renderGroup); - expectNumMeshesContained(1u, renderGroup2); - expectMeshNotContained(*mesh, renderGroup); - expectMeshContained(*mesh, 999, renderGroup2); - } - - TEST_F(ARenderGroup, removingRenderGroupFromGroupDoesNotAffectItsPlaceInAnotherGroup) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 3)); - ASSERT_EQ(StatusOK, renderGroup2.addRenderGroup(*nestedRenderGroup, 999)); - - EXPECT_EQ(StatusOK, renderGroup.removeRenderGroup(*nestedRenderGroup)); - - expectNumRenderGroupsContained(0u, renderGroup); - expectNumRenderGroupsContained(1u, renderGroup2); - expectRenderGroupNotContained(*nestedRenderGroup, renderGroup); - expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup2); - } - - TEST_F(ARenderGroup, destroyedMeshIsRemovedFromAllItsGroups) - { - MeshNode* mesh = m_scene.createMeshNode(); - - EXPECT_EQ(StatusOK, renderGroup.addMeshNode(*mesh, 3)); - EXPECT_EQ(StatusOK, renderGroup2.addMeshNode(*mesh, 999)); - - EXPECT_EQ(StatusOK, m_scene.destroy(*mesh)); - - expectNumMeshesContained(0u, renderGroup); - expectNumMeshesContained(0u, renderGroup2); - } - - TEST_F(ARenderGroup, destroyedNestedRenderGroupIsRemovedFromAllItsGroups) - { - RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); - - ASSERT_EQ(StatusOK, renderGroup.addRenderGroup(*nestedRenderGroup, 3)); - ASSERT_EQ(StatusOK, renderGroup2.addRenderGroup(*nestedRenderGroup, 999)); - - ASSERT_EQ(StatusOK, m_scene.destroy(*nestedRenderGroup)); - - expectNumRenderGroupsContained(0u, renderGroup); - expectNumRenderGroupsContained(0u, renderGroup2); - } -} diff --git a/client/test/RenderTargetDescriptionTest.cpp b/client/test/RenderTargetDescriptionTest.cpp deleted file mode 100644 index 39128a670..000000000 --- a/client/test/RenderTargetDescriptionTest.cpp +++ /dev/null @@ -1,206 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "RenderBufferImpl.h" -#include "RenderTargetDescriptionImpl.h" -#include "ClientTestUtils.h" - -using namespace testing; - -namespace ramses -{ - class RenderTargetDescriptionTest : public LocalTestClientWithScene, public testing::Test - { - public: - protected: - RenderTargetDescription rtDesc; - }; - - TEST_F(RenderTargetDescriptionTest, initialState) - { - EXPECT_TRUE(rtDesc.m_impl.get().getRenderBuffers().empty()); - } - - TEST_F(RenderTargetDescriptionTest, validatesAsWarningIfEmpty) - { - EXPECT_NE(StatusOK, rtDesc.validate()); - } - - TEST_F(RenderTargetDescriptionTest, canAddRenderBuffer) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - - ASSERT_EQ(1u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - - EXPECT_EQ(StatusOK, rtDesc.validate()); - } - - TEST_F(RenderTargetDescriptionTest, canAddMultipleColorRenderBuffers) - { - const RenderBuffer& colorRb1 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer& colorRb2 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb1)); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb2)); - - ASSERT_EQ(2u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb1.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - EXPECT_EQ(colorRb2.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[1]); - - EXPECT_EQ(StatusOK, rtDesc.validate()); - } - - TEST_F(RenderTargetDescriptionTest, canAddMultipleColorRenderBuffersWithDepthBuffer) - { - const RenderBuffer& colorRb1 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer& colorRb2 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer& depthRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb1)); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb2)); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(depthRb)); - - ASSERT_EQ(3u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb1.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - EXPECT_EQ(colorRb2.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[1]); - EXPECT_EQ(depthRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[2]); - - EXPECT_EQ(StatusOK, rtDesc.validate()); - } - - TEST_F(RenderTargetDescriptionTest, failsToAddSameRenderBufferTwice) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - EXPECT_NE(StatusOK, rtDesc.addRenderBuffer(colorRb)); - - ASSERT_EQ(1u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - } - - TEST_F(RenderTargetDescriptionTest, failsToAddRenderBufferFromAnotherScene) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - - Scene& otherScene = *client.createScene(sceneId_t(666u)); - const RenderBuffer& otherRb = *otherScene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_NE(StatusOK, rtDesc.addRenderBuffer(otherRb)); - - ASSERT_EQ(1u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - } - - TEST_F(RenderTargetDescriptionTest, failsToAddRenderBufferWithDifferentResolution) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - - const RenderBuffer& otherRb = *m_scene.createRenderBuffer(1u, 2u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_NE(StatusOK, rtDesc.addRenderBuffer(otherRb)); - - ASSERT_EQ(1u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - } - - TEST_F(RenderTargetDescriptionTest, failsToAddMoreThanOneDepthBuffer) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer& depthRb1 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer& depthRb2 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::DepthStencil, ERenderBufferFormat::Depth24_Stencil8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(depthRb1)); - EXPECT_NE(StatusOK, rtDesc.addRenderBuffer(depthRb2)); - - ASSERT_EQ(2u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - EXPECT_EQ(depthRb1.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[1]); - } - - TEST_F(RenderTargetDescriptionTest, failsToValidateAfterAddedRenderBufferDestroyed) - { - RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - EXPECT_EQ(StatusOK, rtDesc.validate()); - - m_scene.destroy(colorRb); - EXPECT_NE(StatusOK, rtDesc.validate()); - } - - TEST_F(RenderTargetDescriptionTest, canAddRenderBuffersWithMsaaSampleCountNotZero) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 4u); - const RenderBuffer& depthRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 4u); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(depthRb)); - - ASSERT_EQ(2u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - EXPECT_EQ(depthRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[1]); - } - - TEST_F(RenderTargetDescriptionTest, canNotAddRenderBuffersWithDifferentMsaaSampleCount) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 3u); - const RenderBuffer& depthRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 4u); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - EXPECT_NE(StatusOK, rtDesc.addRenderBuffer(depthRb)); - - ASSERT_EQ(1u, rtDesc.m_impl.get().getRenderBuffers().size()); - EXPECT_EQ(colorRb.m_impl.getRenderBufferHandle(), rtDesc.m_impl.get().getRenderBuffers()[0]); - } - - TEST_F(RenderTargetDescriptionTest, CanBeCopyAndMoveConstructed) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - - RenderTargetDescription rtDescCopy{ rtDesc }; - EXPECT_THAT(rtDescCopy.m_impl.get().getRenderBuffers(), ElementsAre(colorRb.m_impl.getRenderBufferHandle())); - - RenderTargetDescription rtDescMove{ std::move(rtDesc) }; - EXPECT_THAT(rtDescMove.m_impl.get().getRenderBuffers(), ElementsAre(colorRb.m_impl.getRenderBufferHandle())); - } - - TEST_F(RenderTargetDescriptionTest, CanBeCopyAndMoveAssigned) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - - RenderTargetDescription rtDescCopy; - rtDescCopy = rtDesc; - EXPECT_THAT(rtDescCopy.m_impl.get().getRenderBuffers(), ElementsAre(colorRb.m_impl.getRenderBufferHandle())); - - RenderTargetDescription rtDescMove; - rtDescMove = std::move(rtDesc); - EXPECT_THAT(rtDescMove.m_impl.get().getRenderBuffers(), ElementsAre(colorRb.m_impl.getRenderBufferHandle())); - } - - TEST_F(RenderTargetDescriptionTest, CanBeSelfAssigned) - { - const RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - EXPECT_EQ(StatusOK, rtDesc.addRenderBuffer(colorRb)); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - rtDesc = rtDesc; - EXPECT_THAT(rtDesc.m_impl.get().getRenderBuffers(), ElementsAre(colorRb.m_impl.getRenderBufferHandle())); - rtDesc = std::move(rtDesc); - // NOLINTNEXTLINE(bugprone-use-after-move) - EXPECT_THAT(rtDesc.m_impl.get().getRenderBuffers(), ElementsAre(colorRb.m_impl.getRenderBufferHandle())); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } -} diff --git a/client/test/SceneFactoryTest.cpp b/client/test/SceneFactoryTest.cpp deleted file mode 100644 index 1b2e3f4e9..000000000 --- a/client/test/SceneFactoryTest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "framework_common_gmock_header.h" -#include "SceneAPI/IScene.h" -#include "gmock/gmock.h" -#include "SceneFactory.h" -#include "Scene/ClientScene.h" - -using namespace ramses_internal; -using namespace testing; - -class ASceneFactory : public Test -{ -protected: - SceneFactory factory; -}; - -TEST_F(ASceneFactory, createsAndDeletesScene) -{ - IScene* scene = factory.createScene(SceneInfo()); - ASSERT_TRUE(nullptr != scene); - auto sceneOwnPtr = factory.releaseScene(scene->getSceneId()); - EXPECT_EQ(scene, sceneOwnPtr.get()); -} - -TEST_F(ASceneFactory, cannotCreateTwoScenesWithTheSameId) -{ - IScene* scene = factory.createScene(SceneInfo()); - ASSERT_TRUE(nullptr != scene); - EXPECT_TRUE(nullptr == factory.createScene(SceneInfo(scene->getSceneId()))); -} - -TEST_F(ASceneFactory, createsSceneWithProvidedOptions) -{ - const SceneSizeInformation sizeInfo(1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u, 16u, 17u, 18u); - const SceneId sceneId(456u); - const SceneInfo sceneInfo(sceneId, "sceneName"); - Scene* scene = static_cast(factory.createScene(sceneInfo)); - scene->preallocateSceneSize(sizeInfo); - ASSERT_TRUE(scene != nullptr); - EXPECT_EQ(std::string("sceneName"), scene->getName()); - EXPECT_EQ(sizeInfo, scene->getSceneSizeInformation()); - EXPECT_EQ(sceneId, scene->getSceneId()); -} diff --git a/client/test/ScenePersistationTest.cpp b/client/test/ScenePersistationTest.cpp deleted file mode 100644 index 528d82aab..000000000 --- a/client/test/ScenePersistationTest.cpp +++ /dev/null @@ -1,1615 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "PlatformAbstraction/PlatformError.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-utils.h" - -#include "ScenePersistationTest.h" -#include "CameraNodeImpl.h" -#include "AppearanceImpl.h" -#include "DataObjectImpl.h" -#include "GeometryBindingImpl.h" -#include "EffectImpl.h" -#include "SceneImpl.h" -#include "Texture2DImpl.h" -#include "TextureSamplerImpl.h" -#include "RenderGroupImpl.h" -#include "RenderPassImpl.h" -#include "BlitPassImpl.h" -#include "PickableObjectImpl.h" -#include "RenderBufferImpl.h" -#include "RenderTargetImpl.h" -#include "MeshNodeImpl.h" -#include "ArrayBufferImpl.h" -#include "Texture2DBufferImpl.h" - -#include "Utils/File.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/BlitPass.h" -#include "SceneAPI/RenderGroupUtils.h" -#include "SceneAPI/RenderPass.h" -#include "Scene/ESceneActionId.h" -#include "Scene/ResourceChanges.h" -#include "Scene/SceneActionApplier.h" - -#include "TestEffects.h" -#include "FileDescriptorHelper.h" -#include "UnsafeTestMemoryHelpers.h" -#include "RamsesObjectTestTypes.h" - -#include -#include -#include - -namespace ramses -{ - using namespace testing; - - TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromFile) - { - EXPECT_EQ(StatusOK, m_scene.saveToFile("someTemporaryFile.ram", false)); - EXPECT_NE(nullptr, m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", false)); - } - - TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromMemoryWithExplicitDeleter) - { - EXPECT_EQ(StatusOK, m_scene.saveToFile("someTemporaryFile.ram", false)); - - ramses_internal::File f("someTemporaryFile.ram"); - size_t fileSize = 0; - EXPECT_TRUE(f.getSizeInBytes(fileSize)); - - std::unique_ptr data(new unsigned char[fileSize], [](const unsigned char* ptr) { delete[] ptr; }); - size_t numBytesRead = 0; - EXPECT_TRUE(f.open(ramses_internal::File::Mode::ReadOnlyBinary)); - EXPECT_EQ(ramses_internal::EStatus::Ok, f.read(data.get(), fileSize, numBytesRead)); - EXPECT_NE(nullptr, m_clientForLoading.loadSceneFromMemory(std::move(data), fileSize, false)); - } - - TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromMemoryWithImplicitDeleter) - { - EXPECT_EQ(StatusOK, m_scene.saveToFile("someTemporaryFile.ram", false)); - - ramses_internal::File f("someTemporaryFile.ram"); - size_t fileSize = 0; - EXPECT_TRUE(f.getSizeInBytes(fileSize)); - - std::unique_ptr data(new unsigned char[fileSize]); - size_t numBytesRead = 0; - EXPECT_TRUE(f.open(ramses_internal::File::Mode::ReadOnlyBinary)); - EXPECT_EQ(ramses_internal::EStatus::Ok, f.read(data.get(), fileSize, numBytesRead)); - EXPECT_NE(nullptr, RamsesUtils::LoadSceneFromMemory(m_clientForLoading, std::move(data), fileSize, false)); - } - - TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromFileDescriptor) - { - EXPECT_EQ(StatusOK, m_scene.saveToFile("someTemporaryFile.ram", false)); - - size_t fileSize = 0; - { - // write to a file with some offset - ramses_internal::File inFile("someTemporaryFile.ram"); - EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); - std::vector data(fileSize); - size_t numBytesRead = 0; - EXPECT_TRUE(inFile.open(ramses_internal::File::Mode::ReadOnlyBinary)); - EXPECT_EQ(ramses_internal::EStatus::Ok, inFile.read(data.data(), fileSize, numBytesRead)); - - ramses_internal::File outFile("someTemporaryFileWithOffset.ram"); - EXPECT_TRUE(outFile.open(ramses_internal::File::Mode::WriteOverWriteOldBinary)); - - uint32_t zeroData = 0; - EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); - EXPECT_TRUE(outFile.write(data.data(), data.size())); - EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); - } - - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary ("someTemporaryFileWithOffset.ram"); - auto scene = m_clientForLoading.loadSceneFromFileDescriptor(fd, 4, fileSize, false); - ASSERT_NE(nullptr, scene); - EXPECT_EQ(123u, scene->getSceneId().getValue()); - } - - TEST_F(ASceneLoadedFromFile, canReadSceneFromFileDescriptorCustomSceneId) - { - const char* filename = "someTemporaryFile.ram"; - EXPECT_EQ(StatusOK, m_scene.saveToFile(filename, false)); - size_t fileSize = 0; - { - ramses_internal::File inFile(filename); - EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); - } - { - const int fdA = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary(filename); - auto sceneA = m_clientForLoading.loadSceneFromFileDescriptor(ramses::sceneId_t(335), fdA, 0, fileSize, false); - ASSERT_TRUE(sceneA); - EXPECT_EQ(335u, sceneA->getSceneId().getValue()); - } - { - const int fdB = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary (filename); - auto sceneB = m_clientForLoading.loadSceneFromFileDescriptor(ramses::sceneId_t(339), fdB, 0, fileSize, false); - ASSERT_TRUE(sceneB); - EXPECT_EQ(339u, sceneB->getSceneId().getValue()); - } - } - - TEST_F(ASceneLoadedFromFile, errorsReadingSceneFromFileDescriptorCustomSceneId) - { - const char* filename = "someTemporaryFile.ram"; - EXPECT_EQ(StatusOK, m_scene.saveToFile(filename, false)); - size_t fileSize = 0; - { - ramses_internal::File inFile(filename); - EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); - } - - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary(filename); - - static_cast(m_clientForLoading.createScene(m_scene.getSceneId())); - const auto duplicateSceneId = m_clientForLoading.loadSceneFromFileDescriptor(m_scene.getSceneId(), fd, 0, fileSize, false); - const auto invalidSceneId = m_clientForLoading.loadSceneFromFileDescriptor(ramses::sceneId_t(), fd, 0, fileSize, false); - const auto invalidFileDescriptor = m_clientForLoading.loadSceneFromFileDescriptor(ramses::sceneId_t(340), 0, 0, fileSize, false); - const auto invalidFileSize = m_clientForLoading.loadSceneFromFileDescriptor(ramses::sceneId_t(341), fd, 0, 0, false); - - EXPECT_TRUE(duplicateSceneId == nullptr); - EXPECT_TRUE(invalidSceneId == nullptr); - EXPECT_TRUE(invalidFileDescriptor == nullptr); - EXPECT_TRUE(invalidFileSize == nullptr); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAScene) - { - const status_t status = m_scene.saveToFile("someTemporaryFile.ram", false); - EXPECT_EQ(StatusOK, status); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {}); - ASSERT_TRUE(nullptr != m_sceneLoaded); - - ObjectTypeHistogram origSceneNumbers; - ObjectTypeHistogram loadedSceneNumbers; - fillObjectTypeHistogramFromScene(origSceneNumbers, m_scene); - fillObjectTypeHistogramFromScene(loadedSceneNumbers, *m_sceneLoaded); - EXPECT_PRED_FORMAT2(AssertHistogramEqual, origSceneNumbers, loadedSceneNumbers); - - ramses_internal::SceneSizeInformation origSceneSizeInfo = m_scene.m_impl.getIScene().getSceneSizeInformation(); - ramses_internal::SceneSizeInformation loadedSceneSizeInfo = m_sceneLoaded->m_impl.getIScene().getSceneSizeInformation(); - EXPECT_EQ(origSceneSizeInfo, loadedSceneSizeInfo); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAPerspectiveCamera) - { - PerspectiveCamera* camera = this->m_scene.createPerspectiveCamera("my cam"); - camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - camera->setViewport(1, -2, 3, 4); - - doWriteReadCycle(); - - PerspectiveCamera* loadedCamera = this->getObjectForTesting("my cam"); - EXPECT_EQ(camera->m_impl.getCameraHandle(), loadedCamera->m_impl.getCameraHandle()); - EXPECT_EQ(0.1f, loadedCamera->getLeftPlane()); - EXPECT_EQ(0.2f, loadedCamera->getRightPlane()); - EXPECT_EQ(0.3f, loadedCamera->getBottomPlane()); - EXPECT_EQ(0.4f, loadedCamera->getTopPlane()); - EXPECT_EQ(0.5f, loadedCamera->getNearPlane()); - EXPECT_EQ(0.6f, loadedCamera->getFarPlane()); - - EXPECT_EQ(1, loadedCamera->getViewportX()); - EXPECT_EQ(-2, loadedCamera->getViewportY()); - EXPECT_EQ(3u, loadedCamera->getViewportWidth()); - EXPECT_EQ(4u, loadedCamera->getViewportHeight()); - - EXPECT_EQ(StatusOK, loadedCamera->validate()); - - m_sceneLoaded->destroy(*loadedCamera); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAnOrthographicCamera) - { - OrthographicCamera* camera = this->m_scene.createOrthographicCamera("my cam"); - camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - camera->setViewport(1, -2, 3, 4); - - doWriteReadCycle(); - - OrthographicCamera* loadedCamera = this->getObjectForTesting("my cam"); - - EXPECT_EQ(camera->m_impl.getCameraHandle(), loadedCamera->m_impl.getCameraHandle()); - - EXPECT_FLOAT_EQ(0.1f, loadedCamera->getLeftPlane()); - EXPECT_FLOAT_EQ(0.2f, loadedCamera->getRightPlane()); - EXPECT_FLOAT_EQ(0.3f, loadedCamera->getBottomPlane()); - EXPECT_FLOAT_EQ(0.4f, loadedCamera->getTopPlane()); - EXPECT_FLOAT_EQ(0.5f, loadedCamera->getNearPlane()); - EXPECT_FLOAT_EQ(0.6f, loadedCamera->getFarPlane()); - - EXPECT_EQ(1, loadedCamera->getViewportX()); - EXPECT_EQ(-2, loadedCamera->getViewportY()); - EXPECT_EQ(3u, loadedCamera->getViewportWidth()); - EXPECT_EQ(4u, loadedCamera->getViewportHeight()); - - EXPECT_EQ(StatusOK, loadedCamera->validate()); - - m_sceneLoaded->destroy(*loadedCamera); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAnEffect) - { - TestEffects::CreateTestEffectWithAllStages(this->m_scene, "eff"); - - doWriteReadCycle(); - - auto loadedEffect = this->getObjectForTesting("eff"); - ASSERT_TRUE(loadedEffect); - - // check uniforms - EXPECT_EQ(4u, loadedEffect->getUniformInputCount()); - UniformInput uniform; - EXPECT_EQ(StatusOK, loadedEffect->getUniformInput(0u, uniform)); - EXPECT_STREQ("vs_uniform", uniform.getName()); - EXPECT_EQ(EDataType::Float, *uniform.getDataType()); - EXPECT_EQ(1u, uniform.getElementCount()); - - EXPECT_EQ(StatusOK, loadedEffect->getUniformInput(1u, uniform)); - EXPECT_STREQ("colorRG", uniform.getName()); - EXPECT_EQ(EDataType::Vector2F, *uniform.getDataType()); - EXPECT_EQ(1u, uniform.getElementCount()); - - EXPECT_EQ(StatusOK, loadedEffect->getUniformInput(2u, uniform)); - EXPECT_STREQ("colorBA", uniform.getName()); - EXPECT_EQ(EDataType::Float, *uniform.getDataType()); - EXPECT_EQ(2u, uniform.getElementCount()); - - EXPECT_EQ(StatusOK, loadedEffect->getUniformInput(3u, uniform)); - EXPECT_STREQ("gs_uniform", uniform.getName()); - EXPECT_EQ(EDataType::Float, *uniform.getDataType()); - EXPECT_EQ(1u, uniform.getElementCount()); - - // check attributes - EXPECT_EQ(3u, loadedEffect->getAttributeInputCount()); - AttributeInput attrib; - EXPECT_EQ(StatusOK, loadedEffect->getAttributeInput(0u, attrib)); - EXPECT_STREQ("a_position1", attrib.getName()); - EXPECT_EQ(EDataType::Vector3F, *attrib.getDataType()); - - EXPECT_EQ(StatusOK, loadedEffect->getAttributeInput(1u, attrib)); - EXPECT_STREQ("a_position2", attrib.getName()); - EXPECT_EQ(EDataType::Float, *attrib.getDataType()); - - EXPECT_EQ(StatusOK, loadedEffect->getAttributeInput(2u, attrib)); - EXPECT_STREQ("a_position3", attrib.getName()); - EXPECT_EQ(EDataType::Float, *attrib.getDataType()); - - // GS - EXPECT_TRUE(loadedEffect->hasGeometryShader()); - EDrawMode gsInputType = EDrawMode::Points; - EXPECT_EQ(StatusOK, loadedEffect->getGeometryShaderInputType(gsInputType)); - EXPECT_EQ(EDrawMode::Lines, gsInputType); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearance) - { - Effect* effect = TestEffects::CreateTestEffect(this->m_scene); - Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); - - doWriteReadCycle(); - - Appearance* loadedAppearance = this->getObjectForTesting("appearance"); - - EXPECT_EQ(appearance->getEffect().m_impl.getLowlevelResourceHash(), loadedAppearance->getEffect().m_impl.getLowlevelResourceHash()); - EXPECT_EQ(appearance->m_impl.getRenderStateHandle(), loadedAppearance->m_impl.getRenderStateHandle()); - EXPECT_EQ(appearance->m_impl.getUniformDataInstance(), loadedAppearance->m_impl.getUniformDataInstance()); - EXPECT_EQ(appearance->m_impl.getIScene().getSceneId(), loadedAppearance->m_impl.getIScene().getSceneId()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearanceWithGeometryShaderAndRestrictDrawMode) - { - const Effect* effect = TestEffects::CreateTestEffectWithAllStages(this->m_scene); - this->m_scene.createAppearance(*effect, "appearance"); - - doWriteReadCycle(); - - Appearance* loadedAppearance = this->getObjectForTesting("appearance"); - - EDrawMode drawMode = EDrawMode::Points; - EXPECT_EQ(StatusOK, loadedAppearance->getDrawMode(drawMode)); - EXPECT_EQ(EDrawMode::Lines, drawMode); - - EDrawMode gsInputType = EDrawMode::Points; - ASSERT_TRUE(loadedAppearance->getEffect().hasGeometryShader()); - EXPECT_EQ(StatusOK, loadedAppearance->getEffect().getGeometryShaderInputType(gsInputType)); - EXPECT_EQ(EDrawMode::Lines, gsInputType); - - // valid draw mode change - EXPECT_EQ(StatusOK, loadedAppearance->setDrawMode(EDrawMode::LineStrip)); - - // invalid draw mode change - EXPECT_NE(StatusOK, loadedAppearance->setDrawMode(EDrawMode::Points)); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearanceWithGeometryShaderAndRestrictDrawMode_usingSameEffect) - { - const Effect* effect1 = TestEffects::CreateTestEffectWithAllStages(this->m_scene); - const Effect* effect2 = TestEffects::CreateTestEffectWithAllStages(this->m_scene); - this->m_scene.createAppearance(*effect1, "appearance1"); - this->m_scene.createAppearance(*effect2, "appearance2"); - - doWriteReadCycle(); - - Appearance* loadedAppearance1 = this->getObjectForTesting("appearance1"); - Appearance* loadedAppearance2 = this->getObjectForTesting("appearance2"); - - // appearance 1 - EDrawMode drawMode = EDrawMode::Points; - EDrawMode gsInputType = EDrawMode::Points; - EXPECT_EQ(StatusOK, loadedAppearance1->getDrawMode(drawMode)); - EXPECT_EQ(EDrawMode::Lines, drawMode); - ASSERT_TRUE(loadedAppearance1->getEffect().hasGeometryShader()); - EXPECT_EQ(StatusOK, loadedAppearance1->getEffect().getGeometryShaderInputType(gsInputType)); - EXPECT_EQ(EDrawMode::Lines, gsInputType); - - // appearance 2 - EXPECT_EQ(StatusOK, loadedAppearance1->getDrawMode(drawMode)); - EXPECT_EQ(EDrawMode::Lines, drawMode); - ASSERT_TRUE(loadedAppearance1->getEffect().hasGeometryShader()); - EXPECT_EQ(StatusOK, loadedAppearance1->getEffect().getGeometryShaderInputType(gsInputType)); - EXPECT_EQ(EDrawMode::Lines, gsInputType); - - // valid draw mode change - EXPECT_EQ(StatusOK, loadedAppearance1->setDrawMode(EDrawMode::LineStrip)); - EXPECT_EQ(StatusOK, loadedAppearance2->setDrawMode(EDrawMode::LineStrip)); - - // invalid draw mode change - EXPECT_NE(StatusOK, loadedAppearance1->setDrawMode(EDrawMode::Points)); - EXPECT_NE(StatusOK, loadedAppearance2->setDrawMode(EDrawMode::Points)); - } - - TEST_F(ASceneLoadedFromFile, keepingTrackOfsceneObjectIdsAndFindObjectByIdWork) - { - Effect* effect = TestEffects::CreateTestEffect(this->m_scene); - - const Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); - const GeometryBinding* geometry = this->m_scene.createGeometryBinding(*effect, "geometry"); - - const sceneObjectId_t appearanceIdBeforeSaveAndLoad = appearance->getSceneObjectId(); - const sceneObjectId_t geometryIdBeforeSaveAndLoad = geometry->getSceneObjectId(); - EXPECT_NE(appearanceIdBeforeSaveAndLoad, geometryIdBeforeSaveAndLoad); - - doWriteReadCycle(); - - const Appearance& appearanceLoaded = RamsesObjectTypeUtils::ConvertTo(*this->m_scene.findObjectById(appearanceIdBeforeSaveAndLoad)); - EXPECT_EQ(appearanceIdBeforeSaveAndLoad, appearanceLoaded.getSceneObjectId()); - - const GeometryBinding& geometryLoaded = RamsesObjectTypeUtils::ConvertTo(*this->m_scene.findObjectById(geometryIdBeforeSaveAndLoad)); - EXPECT_EQ(geometryIdBeforeSaveAndLoad, geometryLoaded.getSceneObjectId()); - - const Camera* camera = this->m_scene.createOrthographicCamera("camera"); - EXPECT_NE(appearanceIdBeforeSaveAndLoad, camera->getSceneObjectId()); - EXPECT_NE(geometryIdBeforeSaveAndLoad, camera->getSceneObjectId()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearanceWithUniformValuesSetOrBound) - { - Effect* effect = TestEffects::CreateTestEffect(this->m_scene); - Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); - - UniformInput fragColorR; - UniformInput fragColorG; - effect->findUniformInput("u_FragColorR", fragColorR); - effect->findUniformInput("u_FragColorG", fragColorG); - - auto fragColorDataObject = this->m_scene.createDataObject(EDataType::Float); - fragColorDataObject->setValue(123.f); - - appearance->setInputValue(fragColorR, 567.f); - appearance->bindInput(fragColorG, *fragColorDataObject); - - doWriteReadCycle(); - - Appearance* loadedAppearance = this->getObjectForTesting("appearance"); - const Effect& loadedEffect = loadedAppearance->getEffect(); - - UniformInput fragColorROut; - UniformInput fragColorGOut; - loadedEffect.findUniformInput("u_FragColorR", fragColorROut); - loadedEffect.findUniformInput("u_FragColorG", fragColorGOut); - - float resultR = 0.f; - loadedAppearance->getInputValue(fragColorROut, resultR); - EXPECT_FLOAT_EQ(567.f, resultR); - - ASSERT_TRUE(loadedAppearance->isInputBound(fragColorGOut)); - float resultG = 0.f; - const DataObject& fragColorDataObjectOut = *loadedAppearance->getDataObjectBoundToInput(fragColorGOut); - fragColorDataObjectOut.getValue(resultG); - EXPECT_EQ(123.f, resultG); - } - - TEST_F(ASceneLoadedFromFile, multipleAppearancesSharingSameEffectAreCorrectlyWrittenAndLoaded) - { - Effect* effect = TestEffects::CreateTestEffect(this->m_scene); - const Appearance* appearance1 = m_scene.createAppearance(*effect, "appearance1"); - const Appearance* appearance2 = m_scene.createAppearance(*effect, "appearance2"); - - // check data layout ref count on LL - EXPECT_EQ(appearance1->m_impl.getUniformDataLayout(), appearance2->m_impl.getUniformDataLayout()); - const uint32_t numReferences = m_scene.m_impl.getIScene().getNumDataLayoutReferences(appearance1->m_impl.getUniformDataLayout()); - EXPECT_EQ(2u, numReferences); - - doWriteReadCycle(); - - Appearance* loadedAppearance1 = this->getObjectForTesting("appearance1"); - Appearance* loadedAppearance2 = this->getObjectForTesting("appearance2"); - - // check data layout ref count on LL in loaded scene - EXPECT_EQ(loadedAppearance1->m_impl.getUniformDataLayout(), loadedAppearance2->m_impl.getUniformDataLayout()); - const uint32_t numReferencesLoaded = m_scene.m_impl.getIScene().getNumDataLayoutReferences(loadedAppearance1->m_impl.getUniformDataLayout()); - EXPECT_EQ(numReferences, numReferencesLoaded); - - m_sceneLoaded->destroy(*loadedAppearance2); - m_sceneLoaded->destroy(*loadedAppearance1); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteGeometryBinding) - { - static const uint16_t inds[3] = { 0, 1, 2 }; - ArrayResource* const indices = this->m_scene.createArrayResource(3u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); - - Effect* effect = TestEffects::CreateTestEffectWithAttribute(this->m_scene); - - GeometryBinding* geometry = this->m_scene.createGeometryBinding(*effect, "geometry"); - ASSERT_TRUE(geometry != nullptr); - EXPECT_EQ(StatusOK, geometry->setIndices(*indices)); - - doWriteReadCycle(); - - const GeometryBinding* loadedGeometry = this->getObjectForTesting("geometry"); - - EXPECT_EQ(geometry->m_impl.getEffectHash(), loadedGeometry->m_impl.getEffectHash()); - EXPECT_EQ(geometry->m_impl.getAttributeDataLayout(), loadedGeometry->m_impl.getAttributeDataLayout()); - EXPECT_EQ(geometry->m_impl.getAttributeDataInstance(), loadedGeometry->m_impl.getAttributeDataInstance()); - EXPECT_EQ(geometry->m_impl.getIndicesCount(), loadedGeometry->m_impl.getIndicesCount()); - - const Effect& loadedEffect = loadedGeometry->getEffect(); - EXPECT_EQ(effect->getResourceId(), loadedEffect.getResourceId()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), loadedEffect.m_impl.getLowlevelResourceHash()); - EXPECT_EQ(effect->m_impl.getObjectRegistryHandle(), loadedEffect.m_impl.getObjectRegistryHandle()); - EXPECT_EQ(4u, loadedEffect.getAttributeInputCount()); - AttributeInput attributeInputOut; - EXPECT_EQ(StatusOK, loadedEffect.findAttributeInput("a_position", attributeInputOut)); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode) - { - MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); - - doWriteReadCycle(); - - MeshNode* loadedMeshNode = this->getObjectForTesting("a meshnode"); - - EXPECT_EQ(meshNode->getAppearance(), loadedMeshNode->getAppearance()); - EXPECT_EQ(meshNode->getGeometryBinding(), loadedMeshNode->getGeometryBinding()); - EXPECT_EQ(meshNode->getStartIndex(), loadedMeshNode->getStartIndex()); - EXPECT_EQ(meshNode->getIndexCount(), loadedMeshNode->getIndexCount()); - EXPECT_EQ(meshNode->m_impl.getFlattenedVisibility(), loadedMeshNode->m_impl.getFlattenedVisibility()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode_withVisibilityParent) - { - MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); - - Node* visibilityParent = this->m_scene.createNode("vis node"); - visibilityParent->setVisibility(EVisibilityMode::Invisible); - visibilityParent->addChild(*meshNode); - - // Apply visibility state only with flush, not before - EXPECT_EQ(meshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - this->m_scene.flush(); - EXPECT_EQ(meshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - - doWriteReadCycle(); - - MeshNode* loadedMeshNode = this->getObjectForTesting("a meshnode"); - EXPECT_EQ(loadedMeshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode_withVisibilityParentOff) - { - MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); - - Node* visibilityParent = this->m_scene.createNode("vis node"); - visibilityParent->setVisibility(EVisibilityMode::Off); - visibilityParent->addChild(*meshNode); - - // Apply visibility state only with flush, not before - EXPECT_EQ(meshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - this->m_scene.flush(); - EXPECT_EQ(meshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - - doWriteReadCycle(); - - MeshNode* loadedMeshNode = this->getObjectForTesting("a meshnode"); - EXPECT_EQ(loadedMeshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode_withValues) - { - Effect* effect = TestEffects::CreateTestEffect(this->m_scene); - Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); - - GeometryBinding* geometry = this->m_scene.createGeometryBinding(*effect, "geometry"); - const uint16_t data = 0u; - ArrayResource* indices = this->m_scene.createArrayResource(1u, &data, ramses::ResourceCacheFlag_DoNotCache, "indices"); - geometry->setIndices(*indices); - - MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); - - //EXPECT_EQ(StatusOK, meshNode.setIndexArray(indices)); - EXPECT_EQ(StatusOK, meshNode->setAppearance(*appearance)); - EXPECT_EQ(StatusOK, meshNode->setGeometryBinding(*geometry)); - EXPECT_EQ(StatusOK, meshNode->setStartIndex(456)); - EXPECT_EQ(StatusOK, meshNode->setIndexCount(678u)); - EXPECT_EQ(StatusOK, meshNode->m_impl.setFlattenedVisibility(EVisibilityMode::Off)); - doWriteReadCycle(); - - MeshNode* loadedMeshNode = this->getObjectForTesting("a meshnode"); - - EXPECT_STREQ(meshNode->getAppearance()->getName(), loadedMeshNode->getAppearance()->getName()); - EXPECT_EQ(meshNode->getAppearance()->getSceneObjectId(), loadedMeshNode->getAppearance()->getSceneObjectId()); - EXPECT_STREQ(meshNode->getGeometryBinding()->getName(), loadedMeshNode->getGeometryBinding()->getName()); - EXPECT_EQ(meshNode->getGeometryBinding()->getSceneObjectId(), loadedMeshNode->getGeometryBinding()->getSceneObjectId()); - EXPECT_EQ(meshNode->getSceneObjectId(), loadedMeshNode->getSceneObjectId()); - EXPECT_EQ(456u, loadedMeshNode->getStartIndex()); - EXPECT_EQ(678u, loadedMeshNode->getIndexCount()); - EXPECT_EQ(loadedMeshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithVisibility) - { - Node* visibilityNode = this->m_scene.createNode("a visibilitynode"); - - visibilityNode->setVisibility(EVisibilityMode::Invisible); - - doWriteReadCycle(); - - Node* loadedVisibilityNode = this->getObjectForTesting("a visibilitynode"); - - EXPECT_EQ(loadedVisibilityNode->getVisibility(), EVisibilityMode::Invisible); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteARenderGroup) - { - RenderGroup* renderGroup = this->m_scene.createRenderGroup("a rendergroup"); - - MeshNode* meshA = this->m_scene.createMeshNode("meshA"); - MeshNode* meshB = this->m_scene.createMeshNode("meshB"); - - renderGroup->addMeshNode(*meshA, 1); - renderGroup->addMeshNode(*meshB, 2); - - doWriteReadCycle(); - - RenderGroup* loadedRenderGroup = this->getObjectForTesting("a rendergroup"); - const MeshNode* loadedMeshA = this->getObjectForTesting("meshA"); - const MeshNode* loadedMeshB = this->getObjectForTesting("meshB"); - - EXPECT_STREQ(renderGroup->getName(), loadedRenderGroup->getName()); - EXPECT_EQ(renderGroup->getSceneObjectId(), loadedRenderGroup->getSceneObjectId()); - - EXPECT_EQ(2u, loadedRenderGroup->m_impl.getAllMeshes().size()); - EXPECT_EQ(&loadedMeshA->m_impl, loadedRenderGroup->m_impl.getAllMeshes()[0]); - EXPECT_EQ(&loadedMeshB->m_impl, loadedRenderGroup->m_impl.getAllMeshes()[1]); - - const auto& internalRg = m_sceneLoaded->m_impl.getIScene().getRenderGroup(renderGroup->m_impl.getRenderGroupHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderable(meshA->m_impl.getRenderableHandle(), internalRg)); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderable(meshB->m_impl.getRenderableHandle(), internalRg)); - EXPECT_EQ(1, ramses_internal::RenderGroupUtils::FindRenderableEntry(meshA->m_impl.getRenderableHandle(), internalRg)->order); - EXPECT_EQ(2, ramses_internal::RenderGroupUtils::FindRenderableEntry(meshB->m_impl.getRenderableHandle(), internalRg)->order); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteANestedRenderGroup) - { - RenderGroup* renderGroup = this->m_scene.createRenderGroup("a rendergroup"); - RenderGroup* nestedRenderGroup = this->m_scene.createRenderGroup("a nested rendergroup"); - - MeshNode* meshA = this->m_scene.createMeshNode("meshA"); - MeshNode* meshB = this->m_scene.createMeshNode("meshB"); - - ASSERT_EQ(StatusOK, renderGroup->addMeshNode(*meshA, 1)); - ASSERT_EQ(StatusOK, nestedRenderGroup->addMeshNode(*meshB, 2)); - ASSERT_EQ(StatusOK, renderGroup->addRenderGroup(*nestedRenderGroup, 1)); - - doWriteReadCycle(); - - RenderGroup* loadedRenderGroup = this->getObjectForTesting("a rendergroup"); - RenderGroup* loadedNestedRenderGroup = this->getObjectForTesting("a nested rendergroup"); - const MeshNode* loadedMeshA = this->getObjectForTesting("meshA"); - const MeshNode* loadedMeshB = this->getObjectForTesting("meshB"); - - EXPECT_STREQ(nestedRenderGroup->getName(), loadedNestedRenderGroup->getName()); - EXPECT_EQ(nestedRenderGroup->getSceneObjectId(), loadedNestedRenderGroup->getSceneObjectId()); - - EXPECT_EQ(1u, loadedRenderGroup->m_impl.getAllMeshes().size()); - EXPECT_EQ(1u, loadedRenderGroup->m_impl.getAllRenderGroups().size()); - EXPECT_EQ(1u, loadedNestedRenderGroup->m_impl.getAllMeshes().size()); - - EXPECT_EQ(&loadedMeshA->m_impl, loadedRenderGroup->m_impl.getAllMeshes()[0]); - EXPECT_EQ(&loadedMeshB->m_impl, loadedNestedRenderGroup->m_impl.getAllMeshes()[0]); - - EXPECT_EQ(&loadedNestedRenderGroup->m_impl, loadedRenderGroup->m_impl.getAllRenderGroups()[0]); - - const auto& internalRg = m_sceneLoaded->m_impl.getIScene().getRenderGroup(renderGroup->m_impl.getRenderGroupHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderable(meshA->m_impl.getRenderableHandle(), internalRg)); - EXPECT_EQ(1, ramses_internal::RenderGroupUtils::FindRenderableEntry(meshA->m_impl.getRenderableHandle(), internalRg)->order); - - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(nestedRenderGroup->m_impl.getRenderGroupHandle(), internalRg)); - EXPECT_EQ(1, ramses_internal::RenderGroupUtils::FindRenderGroupEntry(nestedRenderGroup->m_impl.getRenderGroupHandle(), internalRg)->order); - - const auto& internalRgNested = m_sceneLoaded->m_impl.getIScene().getRenderGroup(nestedRenderGroup->m_impl.getRenderGroupHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderable(meshB->m_impl.getRenderableHandle(), internalRgNested)); - EXPECT_EQ(2, ramses_internal::RenderGroupUtils::FindRenderableEntry(meshB->m_impl.getRenderableHandle(), internalRgNested)->order); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteABasicRenderPass) - { - const int32_t renderOrder = 1; - - RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); - EXPECT_EQ(StatusOK, renderPass->setRenderOrder(renderOrder)); - EXPECT_EQ(StatusOK, renderPass->setEnabled(false)); - EXPECT_EQ(StatusOK, renderPass->setRenderOnce(true)); - - doWriteReadCycle(); - - RenderPass* loadedRenderPass = this->getObjectForTesting("a renderpass"); - - EXPECT_STREQ(renderPass->getName(), loadedRenderPass->getName()); - EXPECT_EQ(renderPass->getSceneObjectId(), loadedRenderPass->getSceneObjectId()); - EXPECT_EQ(renderOrder, loadedRenderPass->getRenderOrder()); - EXPECT_FALSE(loadedRenderPass->isEnabled()); - EXPECT_TRUE(loadedRenderPass->isRenderOnce()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteARenderPassWithACamera) - { - RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); - - PerspectiveCamera* perspCam = this->m_scene.createPerspectiveCamera("camera"); - perspCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - perspCam->setViewport(0, 0, 100, 200); - renderPass->setCamera(*perspCam); - - doWriteReadCycle(); - - RenderPass* loadedRenderPass = this->getObjectForTesting("a renderpass"); - const Camera* loadedCamera = this->getObjectForTesting("camera"); - - EXPECT_STREQ(renderPass->getName(), loadedRenderPass->getName()); - EXPECT_EQ(renderPass->getSceneObjectId(), loadedRenderPass->getSceneObjectId()); - EXPECT_EQ(loadedCamera, loadedRenderPass->getCamera()); - EXPECT_EQ(StatusOK, loadedCamera->validate()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteARenderPassWhichHasRenderGroups) - { - RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); - RenderGroup* groupA = this->m_scene.createRenderGroup("groupA"); - RenderGroup* groupB = this->m_scene.createRenderGroup("groupB"); - renderPass->addRenderGroup(*groupA, 1); - renderPass->addRenderGroup(*groupB, 2); - - doWriteReadCycle(); - - RenderPass* loadedRenderPass = this->getObjectForTesting("a renderpass"); - const RenderGroup* loadedMeshA = this->getObjectForTesting("groupA"); - const RenderGroup* loadedMeshB = this->getObjectForTesting("groupB"); - - EXPECT_STREQ(renderPass->getName(), loadedRenderPass->getName()); - EXPECT_EQ(renderPass->getSceneObjectId(), loadedRenderPass->getSceneObjectId()); - EXPECT_EQ(2u, loadedRenderPass->m_impl.getAllRenderGroups().size()); - EXPECT_EQ(&loadedMeshA->m_impl, loadedRenderPass->m_impl.getAllRenderGroups()[0]); - EXPECT_EQ(&loadedMeshB->m_impl, loadedRenderPass->m_impl.getAllRenderGroups()[1]); - - const ramses_internal::RenderPass& internalRP = m_sceneLoaded->m_impl.getIScene().getRenderPass(renderPass->m_impl.getRenderPassHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(groupA->m_impl.getRenderGroupHandle(), internalRP)); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(groupB->m_impl.getRenderGroupHandle(), internalRP)); - EXPECT_EQ(groupA->m_impl.getRenderGroupHandle(), ramses_internal::RenderGroupUtils::FindRenderGroupEntry(groupA->m_impl.getRenderGroupHandle(), internalRP)->renderGroup); - EXPECT_EQ(groupB->m_impl.getRenderGroupHandle(), ramses_internal::RenderGroupUtils::FindRenderGroupEntry(groupB->m_impl.getRenderGroupHandle(), internalRP)->renderGroup); - EXPECT_EQ(1, ramses_internal::RenderGroupUtils::FindRenderGroupEntry(groupA->m_impl.getRenderGroupHandle(), internalRP)->order); - EXPECT_EQ(2, ramses_internal::RenderGroupUtils::FindRenderGroupEntry(groupB->m_impl.getRenderGroupHandle(), internalRP)->order); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteBlitPass) - { - const int32_t renderOrder = 1; - const RenderBuffer* srcRenderBuffer = this->m_scene.createRenderBuffer(23, 42, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 0u, "src renderBuffer"); - const RenderBuffer* dstRenderBuffer = this->m_scene.createRenderBuffer(23, 42, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 0u, "dst renderBuffer"); - - BlitPass* blitPass = this->m_scene.createBlitPass(*srcRenderBuffer, *dstRenderBuffer, "a blitpass"); - EXPECT_EQ(StatusOK, blitPass->setRenderOrder(renderOrder)); - EXPECT_EQ(StatusOK, blitPass->setEnabled(false)); - - doWriteReadCycle(); - - const BlitPass* loadedBlitPass = this->getObjectForTesting("a blitpass"); - - EXPECT_STREQ(blitPass->getName(), loadedBlitPass->getName()); - EXPECT_EQ(blitPass->getSceneObjectId(), loadedBlitPass->getSceneObjectId()); - EXPECT_EQ(renderOrder, loadedBlitPass->getRenderOrder()); - EXPECT_FALSE(loadedBlitPass->isEnabled()); - - const ramses_internal::BlitPassHandle loadedBlitPassHandle = loadedBlitPass->m_impl.getBlitPassHandle(); - const ramses_internal::BlitPass& blitPassInternal = m_sceneLoaded->m_impl.getIScene().getBlitPass(loadedBlitPassHandle); - EXPECT_EQ(renderOrder, blitPassInternal.renderOrder); - EXPECT_FALSE(blitPassInternal.isEnabled); - EXPECT_EQ(srcRenderBuffer->m_impl.getRenderBufferHandle(), blitPassInternal.sourceRenderBuffer); - EXPECT_EQ(dstRenderBuffer->m_impl.getRenderBufferHandle(), blitPassInternal.destinationRenderBuffer); - EXPECT_EQ(renderOrder, blitPassInternal.renderOrder); - - const ramses_internal::PixelRectangle& sourceRegion = blitPassInternal.sourceRegion; - EXPECT_EQ(0u, sourceRegion.x); - EXPECT_EQ(0u, sourceRegion.y); - EXPECT_EQ(static_cast(srcRenderBuffer->getWidth()), sourceRegion.width); - EXPECT_EQ(static_cast(srcRenderBuffer->getHeight()), sourceRegion.height); - - const ramses_internal::PixelRectangle& destinationRegion = blitPassInternal.destinationRegion; - EXPECT_EQ(0u, destinationRegion.x); - EXPECT_EQ(0u, destinationRegion.y); - EXPECT_EQ(static_cast(dstRenderBuffer->getWidth()), destinationRegion.width); - EXPECT_EQ(static_cast(dstRenderBuffer->getHeight()), destinationRegion.height); - - //client HL api - { - uint32_t sourceXOut; - uint32_t sourceYOut; - uint32_t destinationXOut; - uint32_t destinationYOut; - uint32_t widthOut; - uint32_t heightOut; - loadedBlitPass->getBlittingRegion(sourceXOut, sourceYOut, destinationXOut, destinationYOut, widthOut, heightOut); - EXPECT_EQ(0u, sourceXOut); - EXPECT_EQ(0u, sourceYOut); - EXPECT_EQ(0u, destinationXOut); - EXPECT_EQ(0u, destinationYOut); - EXPECT_EQ(dstRenderBuffer->getWidth(), widthOut); - EXPECT_EQ(dstRenderBuffer->getHeight(), heightOut); - } - - EXPECT_EQ(srcRenderBuffer->m_impl.getRenderBufferHandle(), loadedBlitPass->getSourceRenderBuffer().m_impl.getRenderBufferHandle()); - EXPECT_EQ(srcRenderBuffer->m_impl.getObjectRegistryHandle(), loadedBlitPass->getSourceRenderBuffer().m_impl.getObjectRegistryHandle()); - - EXPECT_EQ(dstRenderBuffer->m_impl.getRenderBufferHandle(), loadedBlitPass->getDestinationRenderBuffer().m_impl.getRenderBufferHandle()); - EXPECT_EQ(dstRenderBuffer->m_impl.getObjectRegistryHandle(), loadedBlitPass->getDestinationRenderBuffer().m_impl.getObjectRegistryHandle()); - } - - TEST_F(ASceneLoadedFromFile, canReadWritePickableObject) - { - const EDataType geometryBufferDataType = EDataType::Vector3F; - const ArrayBuffer* geometryBuffer = this->m_scene.createArrayBuffer(geometryBufferDataType, 3u, "geometryBuffer"); - - const int32_t viewPort_x = 1; - const int32_t viewPort_y = 2; - const uint32_t viewPort_width = 200; - const uint32_t viewPort_height = 300; - PerspectiveCamera* pickableCamera = m_scene.createPerspectiveCamera("pickableCamera"); - pickableCamera->setFrustum(-1.4f, 1.4f, -1.4f, 1.4f, 1.f, 100.f); - pickableCamera->setViewport(viewPort_x, viewPort_y, viewPort_width, viewPort_height); - - const pickableObjectId_t id(2); - PickableObject* pickableObject = this->m_scene.createPickableObject(*geometryBuffer, id, "PickableObject"); - EXPECT_EQ(StatusOK, pickableObject->setCamera(*pickableCamera)); - EXPECT_EQ(StatusOK, pickableObject->setEnabled(false)); - - doWriteReadCycle(); - - const PickableObject* loadedPickableObject = this->getObjectForTesting("PickableObject"); - - EXPECT_STREQ(pickableObject->getName(), loadedPickableObject->getName()); - EXPECT_EQ(pickableObject->getSceneObjectId(), loadedPickableObject->getSceneObjectId()); - EXPECT_EQ(id, loadedPickableObject->getPickableObjectId()); - EXPECT_FALSE(loadedPickableObject->isEnabled()); - EXPECT_EQ(this->getObjectForTesting("pickableCamera"), loadedPickableObject->getCamera()); - EXPECT_EQ(this->getObjectForTesting("geometryBuffer"), &loadedPickableObject->getGeometryBuffer()); - - const ramses_internal::PickableObjectHandle loadedPickableObjectPassHandle = loadedPickableObject->m_impl.getPickableObjectHandle(); - const ramses_internal::PickableObject& pickableObjectInternal = m_sceneLoaded->m_impl.getIScene().getPickableObject(loadedPickableObjectPassHandle); - EXPECT_EQ(id.getValue(), pickableObjectInternal.id.getValue()); - EXPECT_FALSE(pickableObjectInternal.isEnabled); - EXPECT_EQ(geometryBuffer->m_impl.getDataBufferHandle(), pickableObjectInternal.geometryHandle); - EXPECT_EQ(pickableCamera->m_impl.getCameraHandle(), pickableObjectInternal.cameraHandle); - - EXPECT_EQ(geometryBuffer->m_impl.getDataBufferHandle(), loadedPickableObject->getGeometryBuffer().m_impl.getDataBufferHandle()); - EXPECT_EQ(geometryBuffer->m_impl.getObjectRegistryHandle(), loadedPickableObject->getGeometryBuffer().m_impl.getObjectRegistryHandle()); - - EXPECT_EQ(pickableCamera->m_impl.getCameraHandle(), loadedPickableObject->getCamera()->m_impl.getCameraHandle()); - EXPECT_EQ(pickableCamera->m_impl.getObjectRegistryHandle(), loadedPickableObject->getCamera()->m_impl.getObjectRegistryHandle()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteRenderBuffer) - { - RenderBuffer* renderBuffer = this->m_scene.createRenderBuffer(23, 42, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 4u, "a renderTarget"); - - doWriteReadCycle(); - - RenderBuffer* loadedRenderBuffer = this->getObjectForTesting("a renderTarget"); - - EXPECT_STREQ(renderBuffer->getName(), loadedRenderBuffer->getName()); - EXPECT_EQ(renderBuffer->getSceneObjectId(), loadedRenderBuffer->getSceneObjectId()); - EXPECT_EQ(renderBuffer->getWidth(), loadedRenderBuffer->getWidth()); - EXPECT_EQ(renderBuffer->getHeight(), loadedRenderBuffer->getHeight()); - EXPECT_EQ(renderBuffer->getBufferType(), loadedRenderBuffer->getBufferType()); - EXPECT_EQ(renderBuffer->getBufferFormat(), loadedRenderBuffer->getBufferFormat()); - EXPECT_EQ(renderBuffer->getAccessMode(), loadedRenderBuffer->getAccessMode()); - EXPECT_EQ(renderBuffer->getSampleCount(), loadedRenderBuffer->getSampleCount()); - - EXPECT_EQ(renderBuffer->m_impl.getRenderBufferHandle(), loadedRenderBuffer->m_impl.getRenderBufferHandle()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteARenderPassWithARenderTargetAndCamera) - { - RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); - - RenderBuffer* renderBuffer = this->m_scene.createRenderBuffer(23u, 42u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite, 0u, "a renderBuffer"); - RenderTargetDescription rtDesc; - rtDesc.addRenderBuffer(*renderBuffer); - RenderTarget* renderTarget = this->m_scene.createRenderTarget(rtDesc, "target"); - - OrthographicCamera* orthoCam = this->m_scene.createOrthographicCamera("camera"); - orthoCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - orthoCam->setViewport(0, 0, 100, 200); - - renderPass->setCamera(*orthoCam); - renderPass->setRenderTarget(renderTarget); - - doWriteReadCycle(); - - const RenderPass* loadedRenderPass = this->getObjectForTesting("a renderpass"); - const RenderTarget* loadedRenderTarget = this->getObjectForTesting("target"); - const OrthographicCamera* loadedCamera = this->getObjectForTesting("camera"); - - EXPECT_EQ(loadedRenderTarget, loadedRenderPass->getRenderTarget()); - EXPECT_EQ(loadedCamera, loadedRenderPass->getCamera()); - EXPECT_EQ(StatusOK, loadedRenderTarget->validate()); - EXPECT_EQ(StatusOK, loadedCamera->validate()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteRenderTarget) - { - const RenderBuffer& rb = *m_scene.createRenderBuffer(16u, 8u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - RenderTargetDescription rtDesc; - rtDesc.addRenderBuffer(rb); - - RenderTarget* renderTarget = this->m_scene.createRenderTarget(rtDesc, "a renderTarget"); - - doWriteReadCycle(); - - RenderTarget* loadedRenderTarget = this->getObjectForTesting("a renderTarget"); - - EXPECT_STREQ(renderTarget->getName(), loadedRenderTarget->getName()); - EXPECT_EQ(renderTarget->getSceneObjectId(), loadedRenderTarget->getSceneObjectId()); - EXPECT_EQ(renderTarget->getWidth(), loadedRenderTarget->getWidth()); - EXPECT_EQ(renderTarget->getHeight(), loadedRenderTarget->getHeight()); - - EXPECT_EQ(renderTarget->m_impl.getRenderTargetHandle(), loadedRenderTarget->m_impl.getRenderTargetHandle()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteIndexDataBuffer) - { - ArrayBuffer& buffer = *m_scene.createArrayBuffer(EDataType::UInt32, 6u, "indexDB"); - buffer.updateData(3u, 2u, std::array{ {6, 7} }.data()); - - doWriteReadCycle(); - - const ArrayBuffer* loadedBuffer = this->getObjectForTesting("indexDB"); - - EXPECT_STREQ(buffer.getName(), loadedBuffer->getName()); - EXPECT_EQ(buffer.m_impl.getDataBufferHandle(), loadedBuffer->m_impl.getDataBufferHandle()); - EXPECT_EQ(6 * sizeof(uint32_t), m_scene.m_impl.getIScene().getDataBuffer(loadedBuffer->m_impl.getDataBufferHandle()).data.size()); - EXPECT_EQ(5 * sizeof(uint32_t), m_scene.m_impl.getIScene().getDataBuffer(loadedBuffer->m_impl.getDataBufferHandle()).usedSize); - - const ramses_internal::Byte* loadedDataBufferData = m_scene.m_impl.getIScene().getDataBuffer(loadedBuffer->m_impl.getDataBufferHandle()).data.data(); - EXPECT_EQ(6u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedDataBufferData, 3)); - EXPECT_EQ(7u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedDataBufferData, 4)); - - EXPECT_EQ(6u, loadedBuffer->getMaximumNumberOfElements()); - EXPECT_EQ(5u, loadedBuffer->getUsedNumberOfElements()); - EXPECT_EQ(EDataType::UInt32, loadedBuffer->getDataType()); - std::array bufferDataOut; - EXPECT_EQ(StatusOK, loadedBuffer->getData(bufferDataOut.data(), static_cast(bufferDataOut.size() * sizeof(uint32_t)))); - EXPECT_EQ(6u, bufferDataOut[3]); - EXPECT_EQ(7u, bufferDataOut[4]); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteTexture2DBuffer) - { - Texture2DBuffer& buffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2, "textureBuffer"); - buffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data()); - buffer.updateData(1, 0, 0, 1, 1, std::array{ {78} }.data()); - - doWriteReadCycle(); - - const Texture2DBuffer* loadedBuffer = this->getObjectForTesting("textureBuffer"); - - EXPECT_STREQ(buffer.getName(), loadedBuffer->getName()); - EXPECT_EQ(buffer.getSceneObjectId(), loadedBuffer->getSceneObjectId()); - EXPECT_EQ(buffer.m_impl.getTextureBufferHandle(), loadedBuffer->m_impl.getTextureBufferHandle()); - - //iscene - const ramses_internal::TextureBuffer& loadedInternalBuffer = m_scene.m_impl.getIScene().getTextureBuffer(loadedBuffer->m_impl.getTextureBufferHandle()); - ASSERT_EQ(2u, loadedInternalBuffer.mipMaps.size()); - EXPECT_EQ(3u, loadedInternalBuffer.mipMaps[0].width); - EXPECT_EQ(4u, loadedInternalBuffer.mipMaps[0].height); - EXPECT_EQ(1u, loadedInternalBuffer.mipMaps[1].width); - EXPECT_EQ(2u, loadedInternalBuffer.mipMaps[1].height); - EXPECT_EQ(56u, ramses_internal::TextureBuffer::GetMipMapDataSizeInBytes(loadedInternalBuffer)); - EXPECT_EQ(ramses_internal::ETextureFormat::RGBA8, loadedInternalBuffer.textureFormat); - - const ramses_internal::Byte* loadedBufferDataMip0 = loadedInternalBuffer.mipMaps[0].data.data(); - const ramses_internal::Byte* loadedBufferDataMip1 = loadedInternalBuffer.mipMaps[1].data.data(); - EXPECT_EQ(12u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 0)); - EXPECT_EQ(23u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 1)); - EXPECT_EQ(34u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 3 * 1 + 0)); - EXPECT_EQ(56u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 3 * 1 + 1)); - EXPECT_EQ(78u, ramses_internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip1, 0)); - - //client API - EXPECT_EQ(2u, loadedBuffer->getMipLevelCount()); - uint32_t mipLevelWidthOut; - uint32_t mipLevelHeightOut; - EXPECT_EQ(StatusOK, loadedBuffer->getMipLevelSize(0u, mipLevelWidthOut, mipLevelHeightOut)); - EXPECT_EQ(3u, mipLevelWidthOut); - EXPECT_EQ(4u, mipLevelHeightOut); - EXPECT_EQ(StatusOK, loadedBuffer->getMipLevelSize(1u, mipLevelWidthOut, mipLevelHeightOut)); - EXPECT_EQ(1u, mipLevelWidthOut); - EXPECT_EQ(2u, mipLevelHeightOut); - - EXPECT_EQ(ETextureFormat::RGBA8, loadedBuffer->getTexelFormat()); - std::array bufferForMip0; - std::array bufferForMip1; - EXPECT_EQ(StatusOK, loadedBuffer->getMipLevelData(0u, bufferForMip0.data(), static_cast(bufferForMip0.size() * sizeof(uint32_t)))); - EXPECT_EQ(StatusOK, loadedBuffer->getMipLevelData(1u, bufferForMip1.data(), static_cast(bufferForMip1.size() * sizeof(uint32_t)))); - EXPECT_EQ(12u, bufferForMip0[0]); - EXPECT_EQ(23u, bufferForMip0[1]); - EXPECT_EQ(34u, bufferForMip0[3 * 1 + 0]); - EXPECT_EQ(56u, bufferForMip0[3 * 1 + 1]); - EXPECT_EQ(78u, bufferForMip1[0]); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteANode) - { - //generic node cannot be created, therefore using group node - Node* grandParent = this->m_scene.createNode("node1"); - Node* parent = this->m_scene.createNode("node2"); - Node* child = this->m_scene.createNode("node3"); - - grandParent->addChild(*parent); - child->setParent(*parent); - - child->setTranslation({1, 2, 3}); - child->setVisibility(EVisibilityMode::Invisible); - child->setRotation({1, 2, 3}, ERotationType::Euler_XYX); - child->setScaling({1, 2, 3}); - - doWriteReadCycle(); - - Node* loadedGrandParent = this->getObjectForTesting("node1"); - Node* loadedParent = this->getObjectForTesting("node2"); - Node* loadedChild = this->getObjectForTesting("node3"); - ASSERT_TRUE(nullptr != loadedGrandParent); - ASSERT_TRUE(nullptr != loadedParent); - ASSERT_TRUE(nullptr != loadedChild); - EXPECT_EQ(loadedParent, loadedChild->getParent()); - EXPECT_EQ(loadedGrandParent, loadedParent->getParent()); - EXPECT_EQ(loadedParent, loadedGrandParent->getChild(0u)); - EXPECT_EQ(1u, loadedGrandParent->getChildCount()); - EXPECT_EQ(1u, loadedParent->getChildCount()); - EXPECT_EQ(0u, loadedChild->getChildCount()); - - vec3f translation; - EXPECT_EQ(StatusOK, loadedChild->getTranslation(translation)); - EXPECT_FLOAT_EQ(1, translation.x); - EXPECT_FLOAT_EQ(2, translation.y); - EXPECT_FLOAT_EQ(3, translation.z); - - EXPECT_EQ(loadedChild->getVisibility(), EVisibilityMode::Invisible); - - vec3f rotation; - EXPECT_EQ(StatusOK, loadedChild->getRotation(rotation)); - EXPECT_FLOAT_EQ(1, rotation.x); - EXPECT_FLOAT_EQ(2, rotation.y); - EXPECT_FLOAT_EQ(3, rotation.z); - EXPECT_EQ(ERotationType::Euler_XYX, loadedChild->getRotationType()); - - vec3f scale; - EXPECT_EQ(StatusOK, loadedChild->getScaling(scale)); - EXPECT_FLOAT_EQ(1, scale.x); - EXPECT_FLOAT_EQ(2, scale.y); - EXPECT_FLOAT_EQ(3, scale.z); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithTranslation) - { - Node* node = this->m_scene.createNode("translate node 1"); - Node* child = this->m_scene.createNode("groupnode child"); - - node->setTranslation({1, 2, 3}); - node->addChild(*child); - - doWriteReadCycle(); - - Node* loadedTranslateNode = getObjectForTesting("translate node 1"); - Node* loadedChild = getObjectForTesting("groupnode child"); - - ASSERT_TRUE(nullptr != loadedTranslateNode); - ASSERT_TRUE(nullptr != loadedChild); - - EXPECT_EQ(1u, node->getChildCount()); - EXPECT_EQ(loadedChild, loadedTranslateNode->getChild(0u)); - vec3f translation; - EXPECT_EQ(StatusOK, node->getTranslation(translation)); - EXPECT_FLOAT_EQ(1, translation.x); - EXPECT_FLOAT_EQ(2, translation.y); - EXPECT_FLOAT_EQ(3, translation.z); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithRotation) - { - Node* node = this->m_scene.createNode("rotate node 1"); - Node* child = this->m_scene.createNode("groupnode child"); - - node->setRotation({1, 2, 3}, ERotationType::Euler_ZYX); - child->setParent(*node); - doWriteReadCycle(); - - Node* loadedRotateNode = getObjectForTesting("rotate node 1"); - Node* loadedChild = getObjectForTesting("groupnode child"); - - ASSERT_TRUE(nullptr != loadedRotateNode); - ASSERT_TRUE(nullptr != loadedChild); - - EXPECT_EQ(1u, loadedRotateNode->getChildCount()); - EXPECT_EQ(loadedChild, loadedRotateNode->getChild(0u)); - vec3f rotation; - EXPECT_EQ(StatusOK, loadedRotateNode->getRotation(rotation)); - EXPECT_FLOAT_EQ(1, rotation.x); - EXPECT_FLOAT_EQ(2, rotation.y); - EXPECT_FLOAT_EQ(3, rotation.z); - EXPECT_EQ(ERotationType::Euler_ZYX, loadedRotateNode->getRotationType()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithScaling) - { - Node* node = this->m_scene.createNode("scale node"); - Node* child = this->m_scene.createNode("groupnode child"); - - node->setScaling({1, 2, 3}); - child->setParent(*node); - doWriteReadCycle(); - - Node* loadedScaleNode = getObjectForTesting("scale node"); - Node* loadedChild = getObjectForTesting("groupnode child"); - - ASSERT_TRUE(nullptr != loadedScaleNode); - ASSERT_TRUE(nullptr != loadedChild); - - EXPECT_EQ(1u, loadedScaleNode->getChildCount()); - EXPECT_EQ(loadedChild, loadedScaleNode->getChild(0u)); - vec3f scale; - EXPECT_EQ(StatusOK, loadedScaleNode->getScaling(scale)); - EXPECT_FLOAT_EQ(1, scale.x); - EXPECT_FLOAT_EQ(2, scale.y); - EXPECT_FLOAT_EQ(3, scale.z); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteATextureSampler) - { - const ETextureAddressMode wrapUMode = ETextureAddressMode::Mirror; - const ETextureAddressMode wrapVMode = ETextureAddressMode::Repeat; - const ETextureSamplingMethod minSamplingMethod = ETextureSamplingMethod::Linear_MipMapNearest; - const ETextureSamplingMethod magSamplingMethod = ETextureSamplingMethod::Linear; - const uint8_t data[4] = { 0u }; - const MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = this->m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "texture"); - - TextureSampler* sampler = this->m_scene.createTextureSampler(wrapUMode, wrapVMode, minSamplingMethod, magSamplingMethod, *texture, 8u, "sampler"); - ASSERT_TRUE(nullptr != sampler); - - doWriteReadCycle(); - - TextureSampler* loadedSampler = getObjectForTesting("sampler"); - ASSERT_TRUE(nullptr != loadedSampler); - - EXPECT_EQ(wrapUMode, loadedSampler->getWrapUMode()); - EXPECT_EQ(wrapVMode, loadedSampler->getWrapVMode()); - EXPECT_EQ(minSamplingMethod, loadedSampler->getMinSamplingMethod()); - EXPECT_EQ(magSamplingMethod, loadedSampler->getMagSamplingMethod()); - EXPECT_EQ(8u, loadedSampler->getAnisotropyLevel()); - EXPECT_EQ(texture->m_impl.getLowlevelResourceHash(), this->m_sceneLoaded->m_impl.getIScene().getTextureSampler(loadedSampler->m_impl.getTextureSamplerHandle()).textureResource); - EXPECT_EQ(ERamsesObjectType::Texture2D, loadedSampler->m_impl.getTextureType()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteATextureSamplerMS) - { - RenderBuffer* renderBuffer = m_scene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); - - TextureSamplerMS* sampler = this->m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); - ASSERT_TRUE(nullptr != sampler); - - doWriteReadCycle(); - - TextureSamplerMS* loadedSampler = getObjectForTesting("sampler"); - ASSERT_TRUE(nullptr != loadedSampler); - - EXPECT_EQ(ERamsesObjectType::RenderBuffer, loadedSampler->m_impl.getTextureType()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteATextureSamplerExternal) - { - TextureSamplerExternal* sampler = this->m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, "sampler"); - ASSERT_TRUE(nullptr != sampler); - - doWriteReadCycle(); - - TextureSamplerExternal* loadedSampler = getObjectForTesting("sampler"); - ASSERT_TRUE(nullptr != loadedSampler); - - EXPECT_EQ(ERamsesObjectType::TextureSamplerExternal, loadedSampler->m_impl.getTextureType()); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteSceneId) - { - const sceneId_t sceneId = ramses::sceneId_t(1ULL << 63); - ramses::Scene& mScene(*client.createScene(sceneId)); - - const status_t status = mScene.saveToFile("someTempararyFile.ram", false); - EXPECT_EQ(StatusOK, status); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); - ASSERT_TRUE(nullptr != m_sceneLoaded); - - EXPECT_EQ(sceneId, m_sceneLoaded->getSceneId()); - } - - TEST_F(ASceneLoadedFromFile, defaultsToLocalAndRemotePublicationMode) - { - const sceneId_t sceneId(81); - EXPECT_EQ(StatusOK, client.createScene(sceneId)->saveToFile("someTempararyFile.ram", false)); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); - ASSERT_TRUE(nullptr != m_sceneLoaded); - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, m_sceneLoaded->m_impl.getPublicationModeSetFromSceneConfig()); - } - - // TODO(tobias) add to store this option to file format as soon as changes are allowed - TEST_F(ASceneLoadedFromFile, DISABLED_respectsPublicationModeSetOnCreatingFileBeforeSave) - { - const sceneId_t sceneId(81); - SceneConfig config; - config.setPublicationMode(ramses::EScenePublicationMode::LocalOnly); - EXPECT_EQ(StatusOK, client.createScene(sceneId, config)->saveToFile("someTempararyFile.ram", false)); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); - ASSERT_TRUE(nullptr != m_sceneLoaded); - EXPECT_EQ(EScenePublicationMode::LocalOnly, m_sceneLoaded->m_impl.getPublicationModeSetFromSceneConfig()); - } - - TEST_F(ASceneLoadedFromFile, canOverwritePublicationModeForLoadedFiles) - { - const sceneId_t sceneId(80); - EXPECT_EQ(StatusOK, client.createScene(sceneId)->saveToFile("someTempararyFile.ram", false)); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram", true); - ASSERT_TRUE(nullptr != m_sceneLoaded); - EXPECT_EQ(EScenePublicationMode::LocalOnly, m_sceneLoaded->m_impl.getPublicationModeSetFromSceneConfig()); - } - - TEST_F(ASceneLoadedFromFile, reportsErrorWhenSavingSceneToFileWithInvalidFileName) - { - ramses::status_t status = m_scene.saveToFile("?Euler_ZYX:/dummyFile", false); - EXPECT_NE(ramses::StatusOK, status); - } - - TEST_F(ASceneLoadedFromFile, reportsErrorWhenSavingSceneToFileWithNoFileName) - { - ramses::status_t status = m_scene.saveToFile({}, false); - EXPECT_NE(ramses::StatusOK, status); - } - - TEST_F(ASceneLoadedFromFile, overwritesExistingFileWhenSavingSceneToIt) - { - { - ramses_internal::File existingFile("dummyFile.dat"); - existingFile.createFile(); - } - - ramses::status_t status = m_scene.saveToFile("dummyFile.dat", false); - EXPECT_EQ(ramses::StatusOK, status); - - { - ramses_internal::File fileShouldBeOverwritten("dummyFile.dat"); - EXPECT_TRUE(fileShouldBeOverwritten.open(ramses_internal::File::Mode::ReadOnly)); - size_t fileSize = 0; - EXPECT_TRUE(fileShouldBeOverwritten.getSizeInBytes(fileSize)); - EXPECT_NE(0u, fileSize); - } - - EXPECT_TRUE(ramses_internal::File("dummyFile.dat").remove()); - } - - TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromFileWithInvalidFileName) - { - ramses::Scene* scene = client.loadSceneFromFile("?Euler_ZYX:/dummyFile"); - EXPECT_TRUE(nullptr == scene); - } - - TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromFileWithoutFileName) - { - EXPECT_EQ(nullptr, client.loadSceneFromFile({})); - EXPECT_EQ(nullptr, client.loadSceneFromFile("")); - } - - TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromInvalidMemory) - { - auto deleter = [](const unsigned char* ptr) { delete[] ptr; }; - EXPECT_EQ(nullptr, client.loadSceneFromMemory(std::unique_ptr(nullptr, deleter), 1, false)); - EXPECT_EQ(nullptr, client.loadSceneFromMemory(std::unique_ptr(new unsigned char[1], deleter), 0, false)); - - EXPECT_EQ(nullptr, RamsesUtils::LoadSceneFromMemory(client, std::unique_ptr(nullptr), 1, false)); - EXPECT_EQ(nullptr, RamsesUtils::LoadSceneFromMemory(client, std::unique_ptr(new unsigned char[1]), 0, false)); - } - - TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromInvalidFileDescriptor) - { - EXPECT_EQ(nullptr, client.loadSceneFromFileDescriptor(-1, 0, 1, false)); - EXPECT_EQ(nullptr, client.loadSceneFromFileDescriptor(1, 0, 0, false)); - } - - TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromUnexistingFile) - { - ramses::Scene* scene = client.loadSceneFromFile("ZEGETWTWAGTGSDGEg_thisfilename_in_this_directory_should_not_exist_DSAFDSFSTEZHDXHB"); - EXPECT_TRUE(nullptr == scene); - } - - TEST_F(ASceneLoadedFromFile, canHandleAllZeroFileOnSceneLoad) - { - const char* filename = "allzerofile.dat"; - { - ramses_internal::File file(filename); - EXPECT_TRUE(file.open(ramses_internal::File::Mode::WriteNew)); - std::vector zerovector(4096); - EXPECT_TRUE(file.write(&zerovector[0], zerovector.size())); - file.close(); - } - - ramses::Scene* scene = client.loadSceneFromFile(filename); - EXPECT_TRUE(scene == nullptr); - } - - TEST_F(ASceneLoadedFromFile, cannotLoadSameFileTwice) - { - const sceneId_t sceneId = ramses::sceneId_t(1ULL << 63); - ramses::Scene* scene = client.createScene(sceneId); - const status_t status = scene->saveToFile("someTempararyFile.ram", false); - EXPECT_EQ(StatusOK, status); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); - EXPECT_NE(nullptr, m_sceneLoaded); - - EXPECT_EQ(nullptr, m_clientForLoading.loadSceneFromFile("someTempararyFile.ram")); - } - - TEST_F(ASceneLoadedFromFile, cannotLoadScenesWithSameSceneIdTwice) - { - const sceneId_t sceneId = ramses::sceneId_t(1ULL << 63); - - { - ramses::Scene* scene = client.createScene(sceneId); - const status_t status = scene->saveToFile("someTempararyFile.ram", false); - EXPECT_EQ(StatusOK, status); - client.destroy(*scene); - } - { - ramses::Scene* scene = client.createScene(sceneId); - const status_t status = scene->saveToFile("someTempararyFile_2.ram", false); - EXPECT_EQ(StatusOK, status); - client.destroy(*scene); - } - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); - EXPECT_NE(nullptr, m_sceneLoaded); - - EXPECT_EQ(nullptr, m_clientForLoading.loadSceneFromFile("someTempararyFile_2.ram")); - } - - TEST_F(ASceneLoadedFromFile, cannotLoadSceneWithMismatchingFeatureLevel) - { - saveSceneWithFeatureLevelToFile(EFeatureLevel(99), "someTemporaryFile.ram"); - EXPECT_EQ(nullptr, m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram")); - } - - TEST_F(ASceneLoadedFromFile, canGetFeatureLevelFromSceneFile) - { - saveSceneWithFeatureLevelToFile(EFeatureLevel_Latest, "someTemporaryFile.ram"); - - EFeatureLevel featureLevel = EFeatureLevel_01; - EXPECT_TRUE(RamsesClient::GetFeatureLevelFromFile("someTemporaryFile.ram", featureLevel)); - EXPECT_EQ(EFeatureLevel_Latest, featureLevel); - } - - TEST_F(ASceneLoadedFromFile, failsToGetFeatureLevelFromFileWithUnknownFeatureLevel) - { - saveSceneWithFeatureLevelToFile(EFeatureLevel(99), "someTemporaryFile.ram"); - - EFeatureLevel featureLevel = EFeatureLevel_01; - EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile("someTemporaryFile.ram", featureLevel)); - } - - TEST_F(ASceneLoadedFromFile, failsToGetFeatureLevelFromNonexistingFile) - { - EFeatureLevel featureLevel = EFeatureLevel_01; - EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile("doesnt.Exist", featureLevel)); - } - - TEST_F(ASceneLoadedFromFile, canGetFeatureLevelFromSceneFileViaFileDescriptor) - { - saveSceneWithFeatureLevelToFile(EFeatureLevel_Latest, "someTemporaryFile.ram"); - - size_t fileSize = 0; - { - // write to a file with some offset - ramses_internal::File inFile("someTemporaryFile.ram"); - EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); - std::vector data(fileSize); - size_t numBytesRead = 0; - EXPECT_TRUE(inFile.open(ramses_internal::File::Mode::ReadOnlyBinary)); - EXPECT_EQ(ramses_internal::EStatus::Ok, inFile.read(data.data(), fileSize, numBytesRead)); - - ramses_internal::File outFile("someTemporaryFileWithOffset.ram"); - EXPECT_TRUE(outFile.open(ramses_internal::File::Mode::WriteOverWriteOldBinary)); - - uint32_t zeroData = 0; - EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); - EXPECT_TRUE(outFile.write(data.data(), data.size())); - EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); - } - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary("someTemporaryFileWithOffset.ram"); - - EFeatureLevel featureLevel = EFeatureLevel_01; - EXPECT_TRUE(RamsesClient::GetFeatureLevelFromFile(fd, 4u, fileSize, featureLevel)); - EXPECT_EQ(EFeatureLevel_Latest, featureLevel); - } - - TEST_F(ASceneLoadedFromFile, failsToGetFeatureLevelFromInvalidFileDescriptor) - { - EFeatureLevel featureLevel = EFeatureLevel_01; - EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile(-1, 0u, 10u, featureLevel)); - EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile(1, 0u, 0u, featureLevel)); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteTransformDataSlot) - { - Node* node = this->m_scene.createNode("node"); - - EXPECT_EQ(StatusOK, this->m_scene.m_impl.createTransformationDataConsumer(*node, dataConsumerId_t(2u))); - ASSERT_EQ(1u, this->m_scene.m_impl.getIScene().getDataSlotCount()); - - ramses_internal::DataSlotHandle slotHandle(0u); - EXPECT_TRUE(this->m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - - doWriteReadCycle(); - - const Node* nodeLoaded = this->getObjectForTesting("node"); - ramses_internal::NodeHandle nodeHandle = nodeLoaded->m_impl.getNodeHandle(); - ASSERT_EQ(1u, this->m_sceneLoaded->m_impl.getIScene().getDataSlotCount()); - - EXPECT_TRUE(this->m_sceneLoaded->m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(nodeHandle, this->m_sceneLoaded->m_impl.getIScene().getDataSlot(slotHandle).attachedNode); - EXPECT_EQ(ramses_internal::DataSlotId(2u), this->m_sceneLoaded->m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TransformationConsumer, this->m_sceneLoaded->m_impl.getIScene().getDataSlot(slotHandle).type); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteDataObject) - { - float setValue = 5.0f; - auto data = this->m_scene.createDataObject(EDataType::Float, "floatData"); - - EXPECT_EQ(StatusOK, data->setValue(setValue)); - - doWriteReadCycle(); - - const auto loadedData = this->getObjectForTesting("floatData"); - ASSERT_TRUE(loadedData); - float loadedValue = 0.0f; - EXPECT_EQ(EDataType::Float, loadedData->getDataType()); - EXPECT_EQ(StatusOK, loadedData->getValue(loadedValue)); - EXPECT_EQ(setValue, loadedValue); - } - - TEST_F(ASceneLoadedFromFile, canReadWriteSceneReferences) - { - constexpr ramses::sceneId_t referencedSceneId(444); - auto sr1 = this->m_scene.createSceneReference(referencedSceneId, "scene ref"); - sr1->requestState(RendererSceneState::Ready); - - constexpr ramses::sceneId_t referencedSceneId2(555); - auto sr2 = this->m_scene.createSceneReference(referencedSceneId2, "scene ref2"); - sr2->requestState(RendererSceneState::Rendered); - - doWriteReadCycle(); - - const SceneReference* loadedSceneRef = this->getObjectForTesting("scene ref"); - ASSERT_TRUE(loadedSceneRef); - EXPECT_EQ(referencedSceneId, loadedSceneRef->getReferencedSceneId()); - EXPECT_EQ(ramses::RendererSceneState::Ready, loadedSceneRef->getRequestedState()); - - const SceneReference* loadedSceneRef2 = this->getObjectForTesting("scene ref2"); - ASSERT_TRUE(loadedSceneRef2); - EXPECT_EQ(referencedSceneId2, loadedSceneRef2->getReferencedSceneId()); - EXPECT_EQ(ramses::RendererSceneState::Rendered, loadedSceneRef2->getRequestedState()); - } - - TEST_F(ASceneLoadedFromFile, savesLLResourceOnlyOnceIfTwoHLResourcesReferToIt) - { - std::vector inds(300); - std::iota(inds.begin(), inds.end(), static_cast(0u)); - this->m_scene.createArrayResource(300u, inds.data(), ramses::ResourceCacheFlag_DoNotCache, "indices"); - this->m_scene.createArrayResource(300u, inds.data(), ramses::ResourceCacheFlag_DoNotCache, "indices"); - this->m_scene.createArrayResource(300u, inds.data(), ramses::ResourceCacheFlag_DoNotCache, "indices"); - this->m_scene.createArrayResource(300u, inds.data(), ramses::ResourceCacheFlag_DoNotCache, "indices2"); - this->m_scene.createArrayResource(300u, inds.data(), ramses::ResourceCacheFlag_DoNotCache, "indices2"); - this->m_scene.createArrayResource(300u, inds.data(), ramses::ResourceCacheFlag_DoNotCache, "indices2"); - - doWriteReadCycle(); - std::ifstream in("someTemporaryFile.ram", std::ifstream::ate | std::ifstream::binary); - auto size = in.tellg(); - EXPECT_GT(1600, size) << "scene file size exceeds allowed max size. verify that LL resource is saved only once before adapting this number"; - } - - template - struct TestHelper - { - static T* create(ASceneLoadedFromFileTemplated* fixture, ramses::RamsesClient&, ramses::Scene&) - { - return &fixture->template createObject("a node"); - } - }; - - TYPED_TEST_SUITE(ASceneLoadedFromFileTemplated, NodeTypes); - TYPED_TEST(ASceneLoadedFromFileTemplated, canReadWriteAllNodes) - { - auto node = TestHelper::create(this, this->client, this->m_scene); - - node->setVisibility(EVisibilityMode::Invisible); - - auto child = &this->template createObject("child"); - auto parent = &this->template createObject("parent"); - - node->setTranslation({1, 2, 3}); - node->setRotation({4, 5, 6}, ERotationType::Euler_XZX); - node->setScaling({7, 8, 9}); - node->addChild(*child); - node->setParent(*parent); - - this->m_scene.flush(); - - this->doWriteReadCycle(); - - const auto loadedSuperNode = this->template getObjectForTesting("a node"); - const auto loadedChild = this->template getObjectForTesting("child"); - const auto loadedParent = this->template getObjectForTesting("parent"); - - ASSERT_TRUE(nullptr != loadedSuperNode); - ASSERT_TRUE(nullptr != loadedChild); - ASSERT_TRUE(nullptr != loadedParent); - - ASSERT_EQ(1u, loadedSuperNode->getChildCount()); - EXPECT_EQ(loadedChild, loadedSuperNode->getChild(0u)); - EXPECT_EQ(loadedParent, loadedSuperNode->getParent()); - vec3f value; - EXPECT_EQ(StatusOK, loadedSuperNode->getTranslation(value)); - EXPECT_FLOAT_EQ(1, value.x); - EXPECT_FLOAT_EQ(2, value.y); - EXPECT_FLOAT_EQ(3, value.z); - EXPECT_EQ(StatusOK, loadedSuperNode->getRotation(value)); - EXPECT_FLOAT_EQ(4, value.x); - EXPECT_FLOAT_EQ(5, value.y); - EXPECT_FLOAT_EQ(6, value.z); - EXPECT_EQ(ERotationType::Euler_XZX, loadedSuperNode->getRotationType()); - EXPECT_EQ(StatusOK, loadedSuperNode->getScaling(value)); - EXPECT_FLOAT_EQ(7, value.x); - EXPECT_FLOAT_EQ(8, value.y); - EXPECT_FLOAT_EQ(9, value.z); - - EXPECT_EQ(loadedSuperNode->getVisibility(), EVisibilityMode::Invisible); - } - - TEST_F(ASceneLoadedFromFile, compressedFileIsSmallerThanUncompressedWhenUsingSaveSceneToFile) - { - Scene* scene = client.createScene(sceneId_t(1)); - const std::vector data(1000u, 0u); - EXPECT_TRUE(scene->createArrayResource(static_cast(data.size()), data.data())); - - EXPECT_EQ(StatusOK, scene->saveToFile("testscene.ramscene", false)); - - ramses_internal::File file("testscene.ramscene"); - EXPECT_TRUE(file.exists()); - size_t uncompressedFileSize = 0; - EXPECT_TRUE(file.getSizeInBytes(uncompressedFileSize)); - - EXPECT_EQ(StatusOK, scene->saveToFile("testscene.ramscene", true)); - - ramses_internal::File file2("testscene.ramscene"); - EXPECT_TRUE(file2.exists()); - size_t compressedFileSize = 0; - EXPECT_TRUE(file2.getSizeInBytes(compressedFileSize)); - - EXPECT_GT(uncompressedFileSize, compressedFileSize); - } - - TEST_F(ASceneLoadedFromFile, savedFilesAreConsistent) - { - for (const auto& name : { "ts1.ramscene", "ts2.ramscene", "ts3.ramscene", "ts4.ramscene", "ts5.ramscene", "ts6.ramscene" }) - { - EXPECT_EQ(StatusOK, this->m_scene.saveToFile(name, false)); - } - - for (const auto& name : { "ts2.ramscene", "ts3.ramscene", "ts4.ramscene", "ts5.ramscene", "ts6.ramscene" }) - { - EXPECT_TRUE(ClientTestUtils::CompareBinaryFiles("ts1.ramscene", name)); - } - } - - TEST_F(ASceneLoadedFromFile, closesSceneFileAndLowLevelResourceWhenDestroyed) - { - const status_t status = m_scene.saveToFile("someTemporaryFile.ram", false); - EXPECT_EQ(StatusOK, status); - - m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {}); - ASSERT_TRUE(nullptr != m_sceneLoaded); - - const ramses_internal::SceneFileHandle handle = m_sceneLoaded->m_impl.getSceneFileHandle(); - EXPECT_TRUE(m_clientForLoading.m_impl.getClientApplication().hasResourceFile(handle)); - m_clientForLoading.destroy(*m_sceneLoaded); - - // scene gets destroyed asynchronously, so we can't just test after the destroy - // unfortunately there is no callback, but I don't want to skip the test - // => wait for it to happen in finite time, we don't test for performance here - uint32_t ticks = 60000u; - for (; ticks > 0; --ticks) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - if (!m_clientForLoading.m_impl.getClientApplication().hasResourceFile(handle)) - break; - } - EXPECT_GT(ticks, 0u); - } -} diff --git a/client/test/text/Freetype2FontInstanceTest.cpp b/client/test/text/Freetype2FontInstanceTest.cpp deleted file mode 100644 index dae64eff7..000000000 --- a/client/test/text/Freetype2FontInstanceTest.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-text/Freetype2FontInstance.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text/Quad.h" -#include "gtest/gtest.h" - -namespace ramses -{ - class AFreetype2FontInstance : public testing::Test - { - public: - static void SetUpTestCase() - { - FRegistry = new FontRegistry; - const auto fontId = FRegistry->createFreetype2Font("res/ramses-text-Roboto-Bold.ttf"); - - FontInstanceId4 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 4); - FontInstanceId10 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 10); - - FontInstance4 = static_cast(FRegistry->getFontInstance(FontInstanceId4)); - FontInstance10 = static_cast(FRegistry->getFontInstance(FontInstanceId10)); - } - - static void TearDownTestCase() - { - delete FRegistry; - } - - protected: - void expectGlyphMetricsEq(const GlyphMetrics& expected, const GlyphMetrics& actual) - { - EXPECT_EQ(expected.key.fontInstanceId, actual.key.fontInstanceId); - EXPECT_EQ(expected.key.identifier.getValue(), actual.key.identifier.getValue()); - EXPECT_EQ(expected.posX, actual.posX); - EXPECT_EQ(expected.posY, actual.posY); - EXPECT_EQ(expected.width, actual.width); - EXPECT_EQ(expected.height, actual.height); - EXPECT_EQ(expected.advance, actual.advance); - } - - GlyphMetricsVector getPositionedGlyphs(const std::u32string& str, IFontInstance& fontInstance) - { - GlyphMetricsVector ret; - ret.reserve(str.size()); - fontInstance.loadAndAppendGlyphMetrics(str.cbegin(), str.cend(), ret); - return ret; - } - - static FontRegistry* FRegistry; - static FontInstanceId FontInstanceId4; - static FontInstanceId FontInstanceId10; - static Freetype2FontInstance* FontInstance4; - static Freetype2FontInstance* FontInstance10; - }; - - FontRegistry* AFreetype2FontInstance::FRegistry(nullptr); - FontInstanceId AFreetype2FontInstance::FontInstanceId4(0u); - FontInstanceId AFreetype2FontInstance::FontInstanceId10(0u); - Freetype2FontInstance* AFreetype2FontInstance::FontInstance4(nullptr); - Freetype2FontInstance* AFreetype2FontInstance::FontInstance10(nullptr); - - TEST_F(AFreetype2FontInstance, ComputesHeightFromFontData) - { - EXPECT_EQ(5 , FontInstance4 ->getHeight()); - EXPECT_EQ(12, FontInstance10->getHeight()); - } - - TEST_F(AFreetype2FontInstance, ComputesAscenderFromFontData) - { - EXPECT_EQ(4, FontInstance4->getAscender()); - EXPECT_EQ(10, FontInstance10->getAscender()); - } - - TEST_F(AFreetype2FontInstance, ComputesDescenderFromFontData) - { - EXPECT_EQ(-1, FontInstance4->getDescender()); - EXPECT_EQ(-3, FontInstance10->getDescender()); - } - - TEST_F(AFreetype2FontInstance, ObtainsGlyphMetrics) - { - const GlyphMetricsVector positionedGlyphs = getPositionedGlyphs(U"%", *FontInstance4); - - ASSERT_EQ(1u, positionedGlyphs.size()); - const GlyphKey key{ GlyphId(9u), FontInstanceId4 }; - expectGlyphMetricsEq({ key, 3u, 4u, 0, -1, 3 }, positionedGlyphs.front()); - } - - TEST_F(AFreetype2FontInstance, ObtainsMultipleGlyphMetrics) - { - const std::u32string str = U" abc 123 ._! "; - const GlyphMetricsVector positionedGlyphs = getPositionedGlyphs(str, *FontInstance4); - - ASSERT_EQ(13u, positionedGlyphs.size()); - auto it = positionedGlyphs.cbegin(); - - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - expectGlyphMetricsEq({ { GlyphId( 69u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //a - expectGlyphMetricsEq({ { GlyphId( 70u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //b - expectGlyphMetricsEq({ { GlyphId( 71u), FontInstanceId4 }, 2u, 4u, 0, -1, 2 }, *it++); //c - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - expectGlyphMetricsEq({ { GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }, *it++); //1 - expectGlyphMetricsEq({ { GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }, *it++); //2 - expectGlyphMetricsEq({ { GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //3 - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - expectGlyphMetricsEq({ { GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }, *it++); //. - expectGlyphMetricsEq({ { GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }, *it++); //_ - expectGlyphMetricsEq({ { GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }, *it++); //! - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - EXPECT_EQ(it, positionedGlyphs.cend()); - } - - TEST_F(AFreetype2FontInstance, ObtainsMultipleGlyphMetricsFromInstancesWithDifferentSizes) - { - const std::u32string str1 = U" 123 ._! "; - const std::u32string str2 = U" abc "; - const GlyphMetricsVector positionedGlyphs1 = getPositionedGlyphs(str1, *FontInstance4); - const GlyphMetricsVector positionedGlyphs2 = getPositionedGlyphs(str2, *FontInstance10); - - ASSERT_EQ(9u, positionedGlyphs1.size()); - ASSERT_EQ(5u, positionedGlyphs2.size()); - - auto it1 = positionedGlyphs1.cbegin(); - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' - expectGlyphMetricsEq({ { GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }, *it1++); //1 - expectGlyphMetricsEq({ { GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }, *it1++); //2 - expectGlyphMetricsEq({ { GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it1++); //3 - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' - expectGlyphMetricsEq({ { GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }, *it1++); //. - expectGlyphMetricsEq({ { GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }, *it1++); //_ - expectGlyphMetricsEq({ { GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }, *it1++); //! - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' - EXPECT_EQ(it1, positionedGlyphs1.cend()); - - auto it2 = positionedGlyphs2.cbegin(); - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }, *it2++); //' ' - expectGlyphMetricsEq({ { GlyphId( 69u), FontInstanceId10 }, 6u, 5u, 0, 0, 5 }, *it2++); //a - expectGlyphMetricsEq({ { GlyphId( 70u), FontInstanceId10 }, 6u, 8u, 0, 0, 6 }, *it2++); //b - expectGlyphMetricsEq({ { GlyphId( 71u), FontInstanceId10 }, 5u, 5u, 0, 0, 5 }, *it2++); //c - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }, *it2++); //' ' - EXPECT_EQ(it2, positionedGlyphs2.cend()); - } - - TEST_F(AFreetype2FontInstance, CanLoadACharacterGlyphBitmapData) - { - QuadSize glyphBitmapSize; - const GlyphData bitmapData = FontInstance4->loadGlyphBitmapData(FontInstance4->getGlyphId(U'@'), glyphBitmapSize.x, glyphBitmapSize.y); - EXPECT_EQ(4u, glyphBitmapSize.x); - EXPECT_EQ(4u, glyphBitmapSize.y); - - EXPECT_EQ(bitmapData, std::vector({ - 0x1e, 0x57, 0x54, 0x7, - 0x66, 0x6f, 0x65, 0x51, - 0x5e, 0x8b, 0x96, 0x36, - 0x41, 0x58, 0x28, 0x0 - })); - } - - TEST_F(AFreetype2FontInstance, ReportsSupportedCharCodes) - { - const std::u32string str1 = U" 123 ._! "; - for (auto c : str1) - EXPECT_TRUE(FontInstance4->supportsCharacter(c)); - - const std::u32string str2 = U" abc "; - for (auto c : str2) - EXPECT_TRUE(FontInstance10->supportsCharacter(c)); - } - - TEST_F(AFreetype2FontInstance, ReportsSupportedCharCodesAfterAlreadyLoadedTheirMetrics) - { - const std::u32string str1 = U" 123 ._! "; - const std::u32string str2 = U" abc "; - const GlyphMetricsVector positionedGlyphs1 = getPositionedGlyphs(str1, *FontInstance4); - const GlyphMetricsVector positionedGlyphs2 = getPositionedGlyphs(str2, *FontInstance10); - EXPECT_FALSE(positionedGlyphs1.empty()); - EXPECT_FALSE(positionedGlyphs2.empty()); - - for (auto c : str1) - EXPECT_TRUE(FontInstance4->supportsCharacter(c)); - - for (auto c : str2) - EXPECT_TRUE(FontInstance10->supportsCharacter(c)); - } - - TEST_F(AFreetype2FontInstance, ReportsUnsupportedCharCode) - { - EXPECT_FALSE(FontInstance10->supportsCharacter(0x19aa)); - } - - TEST_F(AFreetype2FontInstance, ReportsAllSupportedChars) - { - const std::unordered_set supportedChars = FontInstance10->getAllSupportedCharacters(); - EXPECT_TRUE(896u == supportedChars.size()); - EXPECT_TRUE(supportedChars.end() != supportedChars.find(165u)); - EXPECT_FALSE(supportedChars.end() != supportedChars.find(127u)); - } -} diff --git a/client/test/text/HarfbuzzFontInstanceTest.cpp b/client/test/text/HarfbuzzFontInstanceTest.cpp deleted file mode 100644 index 37c94a126..000000000 --- a/client/test/text/HarfbuzzFontInstanceTest.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-text/HarfbuzzFontInstance.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text/Quad.h" -#include "gtest/gtest.h" -#include "gmock/gmock.h" - -namespace ramses -{ - class AHarfbuzzFontInstance : public testing::Test - { - public: - static void SetUpTestCase() - { - FRegistry = new FontRegistry; - const auto fontId = FRegistry->createFreetype2Font("res/ramses-text-Roboto-Bold.ttf"); - const auto fontArabicId = FRegistry->createFreetype2Font("res/ramses-text-DroidKufi-Regular.ttf"); - - FontInstanceId4 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 4); - FontInstanceId10 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 10); - FontInstanceArabicId = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontArabicId, 10); - - FontInstance4 = static_cast(FRegistry->getFontInstance(FontInstanceId4)); - FontInstance10 = static_cast(FRegistry->getFontInstance(FontInstanceId10)); - FontInstanceArabic = FRegistry->getFontInstance(FontInstanceArabicId); - } - - static void TearDownTestCase() - { - delete FRegistry; - } - - protected: - void expectGlyphMetricsEq(const GlyphMetrics& expected, const GlyphMetrics& actual) - { - EXPECT_EQ(expected.key.fontInstanceId, actual.key.fontInstanceId); - EXPECT_EQ(expected.key.identifier.getValue(), actual.key.identifier.getValue()); - EXPECT_EQ(expected.posX, actual.posX); - EXPECT_EQ(expected.posY, actual.posY); - EXPECT_EQ(expected.width, actual.width); - EXPECT_EQ(expected.height, actual.height); - EXPECT_EQ(expected.advance, actual.advance); - } - - GlyphMetricsVector getPositionedGlyphs(const std::u32string& str, IFontInstance& fontInstance) - { - GlyphMetricsVector ret; - ret.reserve(str.size()); - fontInstance.loadAndAppendGlyphMetrics(str.cbegin(), str.cend(), ret); - return ret; - } - - static FontRegistry* FRegistry; - static FontInstanceId FontInstanceId4; - static FontInstanceId FontInstanceId10; - static FontInstanceId FontInstanceArabicId; - // intentionally use base pointer - static Freetype2FontInstance* FontInstance4; - static Freetype2FontInstance* FontInstance10; - static IFontInstance* FontInstanceArabic; - }; - - FontRegistry* AHarfbuzzFontInstance::FRegistry(nullptr); - FontInstanceId AHarfbuzzFontInstance::FontInstanceId4(0u); - FontInstanceId AHarfbuzzFontInstance::FontInstanceId10(0u); - FontInstanceId AHarfbuzzFontInstance::FontInstanceArabicId(0u); - Freetype2FontInstance* AHarfbuzzFontInstance::FontInstance4(nullptr); - Freetype2FontInstance* AHarfbuzzFontInstance::FontInstance10(nullptr); - IFontInstance* AHarfbuzzFontInstance::FontInstanceArabic(nullptr); - - ////// - /// These tests test base class functionality with no HB reshaping involved, essentially equivalent to freetype2 font instance - ////// - TEST_F(AHarfbuzzFontInstance, ComputesHeightFromFontData) - { - EXPECT_EQ(5 , FontInstance4 ->getHeight()); - EXPECT_EQ(12, FontInstance10->getHeight()); - } - - TEST_F(AHarfbuzzFontInstance, ComputesAscenderFromFontData) - { - EXPECT_EQ(4, FontInstance4->getAscender()); - EXPECT_EQ(10, FontInstance10->getAscender()); - } - - TEST_F(AHarfbuzzFontInstance, ComputesDescenderFromFontData) - { - EXPECT_EQ(-1, FontInstance4->getDescender()); - EXPECT_EQ(-3, FontInstance10->getDescender()); - } - - TEST_F(AHarfbuzzFontInstance, ObtainsGlyphMetrics) - { - const GlyphMetricsVector positionedGlyphs = getPositionedGlyphs(U"%", *FontInstance4); - - ASSERT_EQ(1u, positionedGlyphs.size()); - const GlyphKey key{ GlyphId(9u), FontInstanceId4 }; - expectGlyphMetricsEq({ key, 3u, 4u, 0, -1, 3 }, positionedGlyphs.front()); - } - - TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetrics) - { - const std::u32string str = U" abc 123 ._! "; - const GlyphMetricsVector positionedGlyphs = getPositionedGlyphs(str, *FontInstance4); - - ASSERT_EQ(13u, positionedGlyphs.size()); - auto it = positionedGlyphs.cbegin(); - - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - expectGlyphMetricsEq({ { GlyphId( 69u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //a - expectGlyphMetricsEq({ { GlyphId( 70u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //b - expectGlyphMetricsEq({ { GlyphId( 71u), FontInstanceId4 }, 2u, 4u, 0, -1, 2 }, *it++); //c - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - expectGlyphMetricsEq({ { GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }, *it++); //1 - expectGlyphMetricsEq({ { GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }, *it++); //2 - expectGlyphMetricsEq({ { GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //3 - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - expectGlyphMetricsEq({ { GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }, *it++); //. - expectGlyphMetricsEq({ { GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }, *it++); //_ - expectGlyphMetricsEq({ { GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }, *it++); //! - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' - EXPECT_EQ(it, positionedGlyphs.cend()); - } - - TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsFromInstancesWithDifferentSizes) - { - const std::u32string str1 = U" 123 ._! "; - const std::u32string str2 = U" abc "; - const GlyphMetricsVector positionedGlyphs1 = getPositionedGlyphs(str1, *FontInstance4); - const GlyphMetricsVector positionedGlyphs2 = getPositionedGlyphs(str2, *FontInstance10); - - ASSERT_EQ(9u, positionedGlyphs1.size()); - ASSERT_EQ(5u, positionedGlyphs2.size()); - - auto it1 = positionedGlyphs1.cbegin(); - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' - expectGlyphMetricsEq({ { GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }, *it1++); //1 - expectGlyphMetricsEq({ { GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }, *it1++); //2 - expectGlyphMetricsEq({ { GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it1++); //3 - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' - expectGlyphMetricsEq({ { GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }, *it1++); //. - expectGlyphMetricsEq({ { GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }, *it1++); //_ - expectGlyphMetricsEq({ { GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }, *it1++); //! - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' - EXPECT_EQ(it1, positionedGlyphs1.cend()); - - auto it2 = positionedGlyphs2.cbegin(); - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }, *it2++); //' ' - expectGlyphMetricsEq({ { GlyphId( 69u), FontInstanceId10 }, 6u, 5u, 0, 0, 5 }, *it2++); //a - expectGlyphMetricsEq({ { GlyphId( 70u), FontInstanceId10 }, 6u, 8u, 0, 0, 6 }, *it2++); //b - expectGlyphMetricsEq({ { GlyphId( 71u), FontInstanceId10 }, 5u, 5u, 0, 0, 5 }, *it2++); //c - expectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }, *it2++); //' ' - EXPECT_EQ(it2, positionedGlyphs2.cend()); - } - - TEST_F(AHarfbuzzFontInstance, CanLoadACharacterGlyphBitmapData) - { - QuadSize glyphBitmapSize; - const GlyphData bitmapData = FontInstance4->loadGlyphBitmapData(FontInstance4->getGlyphId(U'@'), glyphBitmapSize.x, glyphBitmapSize.y); - EXPECT_EQ(4u, glyphBitmapSize.x); - EXPECT_EQ(4u, glyphBitmapSize.y); - - EXPECT_EQ(bitmapData, std::vector({ 0x1e, 0x57, 0x54, 0x7, - 0x66, 0x6f, 0x65, 0x51, - 0x5e, 0x8b, 0x96, 0x36, - 0x41, 0x58, 0x28, 0x0 })); - } - - TEST_F(AHarfbuzzFontInstance, ReportsUnsupportedCharCode) - { - EXPECT_FALSE(FontInstance10->supportsCharacter(0x19aa)); - } - ////// - ////// - - TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsWithReshaping) - { - const std::u32string str = {32, 1577, 1581, 1608, 1604, 32, }; //U" ةحول "; - - const GlyphMetricsVector positionedGlyphs = getPositionedGlyphs(str, *FontInstanceArabic); - - ASSERT_EQ(6u, positionedGlyphs.size()); - auto it = positionedGlyphs.cbegin(); - - expectGlyphMetricsEq({ { GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }, *it++); - expectGlyphMetricsEq({ { GlyphId(154u), FontInstanceArabicId }, 7u, 8u, 0, 0, 6 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 8u), FontInstanceArabicId }, 7u, 5u, -1, 0, 6 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 47u), FontInstanceArabicId }, 6u, 7u, 0, -2, 6 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 35u), FontInstanceArabicId }, 4u, 8u, -1, 0, 3 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }, *it++); - EXPECT_EQ(it, positionedGlyphs.cend()); - } - - TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsWithReshaping2) - { - const std::u32string str = {32, 1575, 1604, 1593, 1585, 1576, 1610, 1577, 32, }; //U" العربية "; - const GlyphMetricsVector positionedGlyphs = getPositionedGlyphs(str, *FontInstanceArabic); - - ASSERT_EQ(8u, positionedGlyphs.size()); - auto it = positionedGlyphs.cbegin(); - - expectGlyphMetricsEq({ { GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 51u), FontInstanceArabicId }, 8u, 8u, 0, 0, 7 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 27u), FontInstanceArabicId }, 6u, 5u, -1, 0, 5 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 13u), FontInstanceArabicId }, 5u, 7u, -1, -2, 4 }, *it++); - expectGlyphMetricsEq({ { GlyphId(135u), FontInstanceArabicId }, 5u, 7u, -1, -2, 3 }, *it++); - expectGlyphMetricsEq({ { GlyphId(216u), FontInstanceArabicId }, 4u, 7u, -1, -2, 4 }, *it++); - expectGlyphMetricsEq({ { GlyphId(153u), FontInstanceArabicId }, 6u, 7u, 0, 0, 6 }, *it++); - expectGlyphMetricsEq({ { GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }, *it++); - EXPECT_EQ(it, positionedGlyphs.cend()); - } -} diff --git a/cmake/modules/FindBoost.cmake b/cmake/modules/FindBoost.cmake deleted file mode 100644 index 0aafa1a95..000000000 --- a/cmake/modules/FindBoost.cmake +++ /dev/null @@ -1,155 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -INCLUDE(FindPackageHandleStandardArgs) - -# try to find system boost (forward all arguments, skip this file) -FIND_PACKAGE(Boost ${Boost_FIND_VERSION} COMPONENTS system log QUIET NO_CMAKE_PATH) - -IF (Boost_FOUND) - SET(Boost_SOURCE_TYPE "system") - -ELSE() - # handle linux and windows separately, not much in common - IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - # boost path in container - SET(Boost_BASE_PATH /opt/boost/ CACHE STRING "Path to boost prebuilt package") - - # try to find libs and includes - FIND_LIBRARY(boost_system_LIBRARY - boost_system - HINTS ${Boost_BASE_PATH}/lib/) - FIND_LIBRARY(boost_thread_LIBRARY - boost_thread - HINTS ${Boost_BASE_PATH}/lib/) - FIND_LIBRARY(boost_filesystem_LIBRARY - boost_filesystem - HINTS ${Boost_BASE_PATH}/lib/) - FIND_LIBRARY(boost_log_LIBRARY - boost_log - HINTS ${Boost_BASE_PATH}/lib/) - FIND_PATH(Boost_INCLUDE_DIR - boost/asio/steady_timer.hpp - HINTS ${Boost_BASE_PATH}/include) - - # do the checking - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Boost - FOUND_VAR Boost_FOUND - REQUIRED_VARS boost_system_LIBRARY boost_thread_LIBRARY boost_filesystem_LIBRARY boost_log_LIBRARY Boost_INCLUDE_DIR) - - # set output vars - SET(Boost_LIBRARIES ${boost_system_LIBRARY} ${boost_thread_LIBRARY} ${boost_filesystem_LIBRARY} ${boost_log_LIBRARY}) - SET(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) - - ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - # do some magic path guessing - SET(Boost_BASE_PATH "$ENV{Boost_BASE_PATH}" CACHE STRING "Path to boost prebuilt package") - - # guess lib and include paths - IF (Boost_BASE_PATH) - MESSAGE(STATUS "Use Boost_BASE_PATH: ${Boost_BASE_PATH}") - - # get msvc version - IF ("${MSVC_VERSION}" EQUAL 1900) # VS2015 - SET(Boost_MSVC_LIB_PATH "14.0") - ELSEIF ("${MSVC_VERSION}" EQUAL 1910 OR "${MSVC_VERSION}" GREATER 1910) # VS2017 is 1910 - 1919 - SET(Boost_MSVC_LIB_PATH "14.1") - ENDIF() - - IF (DEFINED Boost_MSVC_LIB_PATH) - SET(Boost_LIBRARY_PATH_GUESSED "${Boost_BASE_PATH}/lib${TARGET_BITNESS}-msvc-${Boost_MSVC_LIB_PATH}") - SET(Boost_INCLUDE_PATH_GUESSED "${Boost_BASE_PATH}") - ENDIF() - ENDIF() - - # these are the final search paths - SET(Boost_LIBRARY_SEARCH_PATH "" CACHE STRING "Boost library path") - SET(Boost_INCLUDE_SEARCH_PATH "" CACHE STRING "Boost include path") - - IF (NOT Boost_LIBRARY_SEARCH_PATH) - SET(Boost_LIBRARY_SEARCH_PATH "${Boost_LIBRARY_PATH_GUESSED}") - ENDIF() - - IF (NOT Boost_INCLUDE_SEARCH_PATH) - SET(Boost_INCLUDE_SEARCH_PATH "${Boost_INCLUDE_PATH_GUESSED}") - ENDIF() - - # check include path - FIND_PATH(Boost_INCLUDE_DIR - boost/asio/steady_timer.hpp - PATHS ${Boost_INCLUDE_SEARCH_PATH}) - - IF (Boost_LIBRARY_SEARCH_PATH AND EXISTS "${Boost_LIBRARY_SEARCH_PATH}/") - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Boost - FOUND_VAR Boost_FOUND - REQUIRED_VARS Boost_LIBRARY_SEARCH_PATH Boost_INCLUDE_DIR) - - # set output vars - SET(Boost_LIBRARIES ) # uses auto-linking - SET(Boost_LINK_DIRECTORIES ${Boost_LIBRARY_SEARCH_PATH}) - SET(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) - - # add library search directory to global search paths - LINK_DIRECTORIES(${Boost_LINK_DIRECTORIES}) - ENDIF() - ENDIF() - - SET(Boost_SOURCE_TYPE "direct") -ENDIF() - -IF (Boost_FOUND) - # default defines - SET(Boost_DEFINES BOOST_ASIO_HAS_STD_CHRONO BOOST_ASIO_HAS_STD_ARRAY) - IF (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - LIST(APPEND Boost_DEFINES BOOST_ASIO_DISABLE_IOCP) - ENDIF() - - IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - LIST(GET Boost_LIBRARIES 0 Boost_SOME_LIBRARY) - - if (TARGET ${Boost_SOME_LIBRARY}) - # modern target based find script - SET(Boost_HAS_SHARED_LIBS TRUE) - else() - # add defines/libs depending on shared or static library found - GET_FILENAME_COMPONENT(Boost_SOME_LIBRARY_SUFFIX ${Boost_SOME_LIBRARY} EXT) - IF (${Boost_SOME_LIBRARY_SUFFIX} STREQUAL ${CMAKE_SHARED_LIBRARY_SUFFIX}) - SET(Boost_HAS_SHARED_LIBS TRUE) - ELSE() - SET(Boost_HAS_SHARED_LIBS FALSE) - ENDIF() - endif() - ELSE() - SET(Boost_HAS_SHARED_LIBS FALSE) - ENDIF() - - IF (Boost_HAS_SHARED_LIBS) - SET(Boost_LIBRARY_TYPE "shared") - # dynamic linking defines - LIST(APPEND Boost_DEFINES BOOST_ALL_DYN_LINK) - ELSE() - SET(Boost_LIBRARY_TYPE "static") - # extra libs required for static linking on linux - IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") - LIST(APPEND Boost_LIBRARIES rt) - ENDIF() - ENDIF() - - # find version - file(READ "${Boost_INCLUDE_DIR}/boost/version.hpp" BOOST_version_file) - string(REGEX MATCH "BOOST_LIB_VERSION[ ]+\"([^\"]+)" BOOST_version_match_full "${BOOST_version_file}") - string(REPLACE "_" "." BOOST_version "${CMAKE_MATCH_1}") - if (NOT BOOST_version) - message(FATAL_ERROR "boost version could not be extracted from ${Boost_INCLUDE_DIR}/boost/version.hpp (found: ${BOOST_version})") - endif() - - message(STATUS "+ Boost (${Boost_SOURCE_TYPE}, ${Boost_LIBRARY_TYPE}, ${BOOST_version})") -ELSE() - message(STATUS "- Boost") -ENDIF() - diff --git a/cmake/modules/FindLuaJIT.cmake b/cmake/modules/FindLuaJIT.cmake new file mode 100644 index 000000000..20d24a4b4 --- /dev/null +++ b/cmake/modules/FindLuaJIT.cmake @@ -0,0 +1,40 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- +find_path(LUAJIT_INCLUDE_DIR lua.h + HINTS + $ENV{LUAJIT_DIR} + PATH_SUFFIXES luajit-2.1 luajit + PATHS + /usr/local + /usr + /opt + ) + +find_library(LUAJIT_LIBRARY + NAMES luajit-5.1 lua51 + HINTS + $ENV{LUAJIT_DIR} + PATH_SUFFIXES lib64 lib + PATHS + /usr/local + /usr + /opt + ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LuaJIT DEFAULT_MSG LUAJIT_LIBRARY LUAJIT_INCLUDE_DIR) + +if(NOT TARGET lua::lua) + add_library(lua::lua UNKNOWN IMPORTED) + set_target_properties(lua::lua PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${LUAJIT_INCLUDE_DIR}" + IMPORTED_LOCATION "${LUAJIT_LIBRARY}") +endif() + +mark_as_advanced(LUAJIT_INCLUDE_DIR LUAJIT_LIBRARY) + diff --git a/cmake/modules/FindQuartzCore.cmake b/cmake/modules/FindQuartzCore.cmake new file mode 100644 index 000000000..db2457e12 --- /dev/null +++ b/cmake/modules/FindQuartzCore.cmake @@ -0,0 +1,26 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +IF((CMAKE_SYSTEM_NAME MATCHES "Darwin") OR (CMAKE_SYSTEM_NAME MATCHES "iOS")) + + FIND_LIBRARY(QUARTZCORE_LIBRARY QuartzCore) + FIND_PATH(QuartzCore_INCLUDE_DIRS CAMetalLayer.h) + + SET(QuartzCore_LIBRARIES "${QUARTZCORE_LIBRARY}" ) + + IF(QuartzCore_LIBRARIES AND QuartzCore_INCLUDE_DIRS) + SET(QuartzCore_FOUND TRUE) + ENDIF() + + MARK_AS_ADVANCED( + QuartzCore_FOUND + QuartzCore_LIBRARIES + QuartzCore_INCLUDE_DIRS + ) + +ENDIF() diff --git a/cmake/ramses/addSubdirectory.cmake b/cmake/ramses/addSubdirectory.cmake deleted file mode 100644 index f3aaaec00..000000000 --- a/cmake/ramses/addSubdirectory.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2023 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - - -function(addSubdirectory) - cmake_parse_arguments(SUBDIR "" "PATH;MODE" "" ${ARGV}) - - if(SUBDIR_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "Unparsed addSubdirectories properties: '${SUBDIR_UNPARSED_ARGUMENTS}'") - endif() - - # TODO this global variable is not needed, FIND_PACKAGE has its own caching - set(GLOBAL_WILL_NOT_FIND_DEPENDENCY "" CACHE INTERNAL "") - - if(${SUBDIR_MODE} STREQUAL "AUTO") - set(GLOBAL_MODULE_DEPENDENCY_CHECK ON) - elseif(${SUBDIR_MODE} STREQUAL "ON") - set(GLOBAL_MODULE_DEPENDENCY_CHECK OFF) - else() - message(FATAL_ERROR "Unsupported mode passed to addSubdirectories: `${SUBDIR_MODE}`") - endif() - - add_subdirectory(${SUBDIR_PATH}) -endfunction() diff --git a/cmake/ramses/createTarget.cmake b/cmake/ramses/createTarget.cmake index b49020ad1..81a4e63d8 100644 --- a/cmake/ramses/createTarget.cmake +++ b/cmake/ramses/createTarget.cmake @@ -76,6 +76,27 @@ function(setSharedTargetProperties) folderizeTarget(${TARGET_NAME}) endfunction() +# macro to import dependencies and check if any are none available. If a missing dependency is found +# the passed variable as 'MISSING_DEPENDENCY_OUT' is overritten with the missing +# dependency name, otherwise it gets unset +macro(importDependenciesAndCheckMissing MISSING_DEPENDENCY_OUT) + # discard any values in MISSING_DEPENDENCY_OUT if any. + # If all dependencies are found it becomes undefined + unset(${MISSING_DEPENDENCY_OUT}) + + foreach(DEPENDENCY ${ARGN}) + if((NOT TARGET ${DEPENDENCY}) AND (NOT ${DEPENDENCY}_FOUND)) + # since macro's dont create a new scope, the variables declared + # in the find scripts will be visible in the calling scope + find_package(${DEPENDENCY}) + if(NOT ${DEPENDENCY}_FOUND) + set(${MISSING_DEPENDENCY_OUT} ${DEPENDENCY}) + break() + endif() + endif() + endforeach() +endmacro() + function(createModule) cmake_parse_arguments( MODULE # Prefix of parsed args @@ -95,40 +116,9 @@ function(createModule) # resolve file wildcards file(GLOB GLOBBED_MODULE_SRC_FILES LIST_DIRECTORIES false ${MODULE_SRC_FILES}) - # check, if all dependencies can be resolved - # TODO This can be for sure modernized with newer CMake functionality for dependency resolving - set(MSG "") - set(MODULE_BUILD_ENABLED TRUE) - foreach(DEPENDENCY ${MODULE_DEPENDENCIES}) - if(NOT TARGET ${DEPENDENCY}) - list(FIND GLOBAL_WILL_NOT_FIND_DEPENDENCY ${DEPENDENCY} SKIP_FIND) - - if(NOT ${DEPENDENCY}_FOUND AND SKIP_FIND EQUAL -1) - find_package(${DEPENDENCY} QUIET) - endif() - if(NOT ${DEPENDENCY}_FOUND) - set(GLOBAL_WILL_NOT_FIND_DEPENDENCY "${DEPENDENCY};${GLOBAL_WILL_NOT_FIND_DEPENDENCY}" CACHE INTERNAL "") - - list(APPEND MSG "missing ${DEPENDENCY}") - if(GLOBAL_MODULE_DEPENDENCY_CHECK) - set(MODULE_BUILD_ENABLED FALSE) - endif() - endif() - endif() - endforeach() - - if(NOT MODULE_BUILD_ENABLED) - message(STATUS "- ${MODULE_NAME} [${MSG}]") - set(GLOBAL_WILL_NOT_FIND_DEPENDENCY "${MODULE_NAME};${GLOBAL_WILL_NOT_FIND_DEPENDENCY}" CACHE INTERNAL "") - return() - endif() - - if(NOT "${MSG}" STREQUAL "") - message(" build enabled, but") - foreach(M ${MSG}) - message(" - ${M}") - endforeach() - message(FATAL_ERROR "aborting configuration") + importDependenciesAndCheckMissing(MISSING_DEPENDENCY ${MODULE_DEPENDENCIES}) + if(MISSING_DEPENDENCY) + message(FATAL_ERROR "Aborting configuration! Missing dependency: ${MISSING_DEPENDENCY} for module: ${MODULE_NAME}!") endif() message(STATUS "+ ${MODULE_NAME} (${MODULE_TYPE})") @@ -219,7 +209,7 @@ function(createModuleWithRenderer) message(FATAL_ERROR "Unparsed createModuleWithRenderer properties: '${MODULE_UNPARSED_ARGUMENTS}'") endif() - set(MODULE_DEPENDENCIES "ramses-renderer-impl;ramses-build-options-base;${MODULE_DEPENDENCIES}") + set(MODULE_DEPENDENCIES "ramses-renderer;ramses-build-options-base;${MODULE_DEPENDENCIES}") createModule(NAME ${MODULE_NAME} TYPE ${MODULE_TYPE} @@ -230,4 +220,5 @@ function(createModuleWithRenderer) RESOURCE_FOLDERS ${MODULE_RESOURCE_FOLDERS} ) + target_link_libraries(${MODULE_NAME} INTERFACE ramses-api) endfunction() diff --git a/client/logic/cmake/flatbuffersGeneration.cmake b/cmake/ramses/flatbuffersGeneration.cmake similarity index 93% rename from client/logic/cmake/flatbuffersGeneration.cmake rename to cmake/ramses/flatbuffersGeneration.cmake index 951801bab..c74e39ae5 100644 --- a/client/logic/cmake/flatbuffersGeneration.cmake +++ b/cmake/ramses/flatbuffersGeneration.cmake @@ -22,8 +22,8 @@ # make # Uses checked-in *_gen.h files and ignores any changes in *.fbs files # make FlatbufGen # Fails - no such targets because disabled -file(GLOB flatbuffers_schemas "${PROJECT_SOURCE_DIR}/client/logic/lib/flatbuffers/schemas/*.fbs") -set(flatbuffers_output_dir "${PROJECT_SOURCE_DIR}/client/logic/lib/flatbuffers/generated") +file(GLOB flatbuffers_schemas "${PROJECT_SOURCE_DIR}/src/client/internal/logic/flatbuffers/schemas/*.fbs") +set(flatbuffers_output_dir "${PROJECT_SOURCE_DIR}/src/client/internal/logic/flatbuffers/generated") # create list of *_gen.h out of *.fbs file list foreach(schema ${flatbuffers_schemas}) diff --git a/cmake/ramses/folderize.cmake b/cmake/ramses/folderize.cmake index 836104626..77279c5ac 100644 --- a/cmake/ramses/folderize.cmake +++ b/cmake/ramses/folderize.cmake @@ -22,24 +22,21 @@ function(cmakePathToFolderName OUT) set(${OUT} ${folder_path} PARENT_SCOPE) endfunction() -function(folderizeTarget tgt) - # skip interface libs because VS generator ignores INTERFACE_FOLDER property - get_target_property(tgt_type ${tgt} TYPE) - if (tgt_type STREQUAL INTERFACE_LIBRARY) - return() - endif() - +function(folderizeTarget TARGET_NAME) cmakePathToFolderName(folderName) - set_property(TARGET ${tgt} PROPERTY FOLDER "${folderName}") + set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "${folderName}") # sort sources in groups - get_target_property(tgt_content ${tgt} SOURCES) - if (tgt_content) - foreach(file_iter ${tgt_content}) - string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" tmp1 "${file_iter}") - string(REGEX REPLACE "/[^/]*$" "" tmp2 "${tmp1}") - string(REPLACE "/" "\\" module_internal_path "${tmp2}") - source_group(${module_internal_path} FILES ${file_iter}) - endforeach() - endif() + get_target_property(TARGET_SOURCE_DIR ${TARGET_NAME} SOURCE_DIR) + + get_target_property(TARGET_SOURCES ${TARGET_NAME} SOURCES) + + foreach(SOURCE_PATH ${TARGET_SOURCES}) + get_filename_component(SOURCE_FULL_PATH "${SOURCE_PATH}" ABSOLUTE) + get_filename_component(SOURCE_FULL_DIR "${SOURCE_FULL_PATH}" DIRECTORY) + file(RELATIVE_PATH SOURCE_RELATIVE_DIR "${TARGET_SOURCE_DIR}" "${SOURCE_FULL_DIR}") + if(NOT "${SOURCE_RELATIVE_DIR}" STREQUAL "") + source_group(${SOURCE_RELATIVE_DIR} FILES ${SOURCE_FULL_PATH}) + endif() + endforeach() endfunction() diff --git a/cmake/ramses/makeTestFromTarget.cmake b/cmake/ramses/makeTestFromTarget.cmake index d833fab96..2987ee07e 100644 --- a/cmake/ramses/makeTestFromTarget.cmake +++ b/cmake/ramses/makeTestFromTarget.cmake @@ -9,7 +9,7 @@ function(makeTestFromTarget) cmake_parse_arguments( TEST # Prefix of parsed args - "SKIPPABLE" # Options + "" # Options "TARGET;SUFFIX" # Single-value args "EXTRA_ARGS" # Multi-value-args ${ARGN} @@ -22,12 +22,7 @@ function(makeTestFromTarget) set(TEST_NAME "${TEST_TARGET}_${TEST_SUFFIX}") if (NOT TARGET ${TEST_TARGET}) - if(TEST_SKIPPABLE) - message(STATUS "Skipping test '${TEST_NAME}' because it's executable ${TEST_TARGET} is missing") - return() - else() - message(FATAL_ERROR "makeTestFromTarget: Test target '${TEST_TARGET}' not found") - endif() + message(FATAL_ERROR "makeTestFromTarget: Test target '${TEST_TARGET}' not found") endif() # TODO: tests should not have to be installed, this line could be deleted after tests have been refactored to diff --git a/cmake/ramses/platformConfig.cmake b/cmake/ramses/platformConfig.cmake index c1fd4732a..1e3ab2ee9 100644 --- a/cmake/ramses/platformConfig.cmake +++ b/cmake/ramses/platformConfig.cmake @@ -129,7 +129,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if (ramses-sdk_ENABLE_SANITIZER AND NOT ramses-sdk_ENABLE_SANITIZER STREQUAL "") set(ubsan_checks "bool,bounds,enum,float-divide-by-zero,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,vla-bound,float-cast-overflow,vptr,alignment,function") - set(ubsan_options "-fsanitize=undefined -fno-sanitize-recover=${ubsan_checks}") + set(ubsan_options "-fsanitize=undefined -fno-sanitize-recover=${ubsan_checks} -fsanitize-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/scripts/ci/config/sanitizer/ubsan_blacklist.txt") set(tsan_options "-fsanitize=thread") set(asan_options "-fsanitize=address") @@ -158,7 +158,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") addFlags(RAMSES_C_CXX_FLAGS "/MP /DNOMINMAX") addFlags(RAMSES_CXX_FLAGS "/std:c++${ramses-sdk_CPP_VERSION} /bigobj") - target_compile_options(ramses-build-options-base INTERFACE /W4 /wd4503 /wd4265 /wd4201 /wd4127 /wd4996 /wd4702) + target_compile_options(ramses-build-options-base INTERFACE /W4 /wd4503 /wd4265 /wd4201 /wd4127 /wd4996 /wd4702 /wd4251) addFlags(RAMSES_RELEASE_FLAGS "/MD /O2 /Ob2 /DNDEBUG") addFlags(RAMSES_DEBUG_FLAGS "/MDd /Zi /Od /D_DEBUG") target_compile_options(ramses-build-options-base INTERFACE $<$:/RTC1>) @@ -182,7 +182,7 @@ endif() if((${CMAKE_SYSTEM_NAME} MATCHES "Darwin") OR (${CMAKE_SYSTEM_NAME} MATCHES "iOS")) addFlags(RAMSES_C_FLAGS "-x objective-c") - set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++${ramses-sdk_CPP_VERSION}") set(CMAKE_XCODE_GENERATE_SCHEME ON) endif() diff --git a/cmake/templates/ramses-shared-lib-headlessTemplate.cmake.in b/cmake/templates/ramses-shared-lib-headlessTemplate.cmake.in index 10da7f5ea..f0760f1d8 100644 --- a/cmake/templates/ramses-shared-lib-headlessTemplate.cmake.in +++ b/cmake/templates/ramses-shared-lib-headlessTemplate.cmake.in @@ -15,11 +15,7 @@ SET(ramses-shared-lib-headless_VERSION @RAMSES_VERSION@) @PACKAGE_INIT@ - -#find include dir -FIND_PATH(ramses-shared-lib-headless_INCLUDE_DIRS ramses-client.h - HINTS @PACKAGE_RAMSES_INSTALL_HEADERS_PATH@ -) +set(ramses-shared-lib-headless_INCLUDE_DIRS "@PACKAGE_RAMSES_INSTALL_HEADERS_PATH@") if(IS_DIRECTORY "${ramses-shared-lib-headless_INCLUDE_DIRS}/glm") add_library(glm::glm INTERFACE IMPORTED) diff --git a/cmake/templates/ramses-shared-libTemplate.cmake.in b/cmake/templates/ramses-shared-libTemplate.cmake.in index 84da2700e..dfbc28028 100644 --- a/cmake/templates/ramses-shared-libTemplate.cmake.in +++ b/cmake/templates/ramses-shared-libTemplate.cmake.in @@ -24,17 +24,7 @@ if(COMPONENTS_LENGTH GREATER 0) MESSAGE(FATAL_ERROR "Unused components passed: ${ramses-shared-lib_FIND_COMPONENTS}") endif() -# find include dir -IF("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") - FIND_PATH(ramses-shared-lib_INCLUDE_DIRS ramses-client.h - HINTS @PACKAGE_RAMSES_INSTALL_HEADERS_PATH@ - CMAKE_FIND_ROOT_PATH_BOTH - ) -ELSE() - FIND_PATH(ramses-shared-lib_INCLUDE_DIRS ramses-client.h - HINTS @PACKAGE_RAMSES_INSTALL_HEADERS_PATH@ - ) -ENDIF() +set(ramses-shared-lib_INCLUDE_DIRS "@PACKAGE_RAMSES_INSTALL_HEADERS_PATH@") if(IS_DIRECTORY "${ramses-shared-lib_INCLUDE_DIRS}/glm") add_library(glm::glm INTERFACE IMPORTED) diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index fbb4973da..1b89600e9 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -6,4 +6,8 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -add_subdirectory(android) +if(CMAKE_SYSTEM_NAME STREQUAL "Android") + add_subdirectory(android) +elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") + add_subdirectory(iOS) +endif() diff --git a/demo/android/DemoJNIInterface/include/RendererBundle.h b/demo/android/DemoJNIInterface/include/RendererBundle.h index c95d1c844..71837055c 100644 --- a/demo/android/DemoJNIInterface/include/RendererBundle.h +++ b/demo/android/DemoJNIInterface/include/RendererBundle.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERBUNDLE_H -#define RAMSES_RENDERERBUNDLE_H +#pragma once #include #include #include #include -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" namespace ramses { @@ -55,5 +54,3 @@ class RAMSES_API_EXPORT RendererBundle std::unique_ptr m_dispatchEventsThread; std::atomic m_cancelDispatchLoop; }; - -#endif diff --git a/demo/android/DemoJNIInterface/include/SceneViewerBundle.h b/demo/android/DemoJNIInterface/include/SceneViewerBundle.h index 50e2d4365..7c8f171e6 100644 --- a/demo/android/DemoJNIInterface/include/SceneViewerBundle.h +++ b/demo/android/DemoJNIInterface/include/SceneViewerBundle.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEVIEWERBUNDLE_H -#define RAMSES_SCENEVIEWERBUNDLE_H +#pragma once #include "RendererBundle.h" #include "UniformInputWrapper.h" @@ -38,5 +37,3 @@ class SceneViewerBundle : public RendererBundle { ramses::RamsesClient* m_client; ramses::Scene* m_loadedScene; }; - -#endif diff --git a/demo/android/DemoJNIInterface/include/UniformInputWrapper.h b/demo/android/DemoJNIInterface/include/UniformInputWrapper.h index 0ea99da4f..f586ac94a 100644 --- a/demo/android/DemoJNIInterface/include/UniformInputWrapper.h +++ b/demo/android/DemoJNIInterface/include/UniformInputWrapper.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UNIFORMINPUTWRAPPER_H -#define RAMSES_UNIFORMINPUTWRAPPER_H +#pragma once -#include "ramses-client-api/UniformInput.h" +#include "ramses/client/UniformInput.h" namespace ramses { @@ -24,9 +23,7 @@ class UniformInputWrapper void setInputValueFloat(float x); private: - ramses::UniformInput m_input; + std::optional m_input; ramses::Appearance& m_appearance; }; - -#endif diff --git a/demo/android/DemoJNIInterface/src/JNIInterface.cpp b/demo/android/DemoJNIInterface/src/JNIInterface.cpp index 1c4f5564b..42a15e96f 100644 --- a/demo/android/DemoJNIInterface/src/JNIInterface.cpp +++ b/demo/android/DemoJNIInterface/src/JNIInterface.cpp @@ -13,7 +13,7 @@ #include "RendererBundle.h" #include "SceneViewerBundle.h" #include "UniformInputWrapper.h" -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" extern "C" diff --git a/demo/android/DemoJNIInterface/src/RendererBundle.cpp b/demo/android/DemoJNIInterface/src/RendererBundle.cpp index c81c70a36..ccbff3af4 100644 --- a/demo/android/DemoJNIInterface/src/RendererBundle.cpp +++ b/demo/android/DemoJNIInterface/src/RendererBundle.cpp @@ -11,10 +11,10 @@ #include #include -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-framework-api/RamsesFramework.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/framework/RamsesFramework.h" RendererBundle::RendererBundle( diff --git a/demo/android/DemoJNIInterface/src/SceneViewerBundle.cpp b/demo/android/DemoJNIInterface/src/SceneViewerBundle.cpp index c16f12024..e39831754 100644 --- a/demo/android/DemoJNIInterface/src/SceneViewerBundle.cpp +++ b/demo/android/DemoJNIInterface/src/SceneViewerBundle.cpp @@ -10,8 +10,8 @@ #include -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" SceneViewerBundle::SceneViewerBundle(ANativeWindow* nativeWindow, int width, int height, const char* sceneFile) @@ -39,12 +39,7 @@ void SceneViewerBundle::run() ramses::Node* SceneViewerBundle::findNodeByName(const char* name) { - ramses::RamsesObject* nodeObject = m_loadedScene->findObjectByName(name); - if (nodeObject) - { - return ramses::RamsesUtils::TryConvert(*nodeObject); - } - return nullptr; + return m_loadedScene->findObject(name); } void SceneViewerBundle::flushScene() @@ -54,14 +49,10 @@ void SceneViewerBundle::flushScene() UniformInputWrapper* SceneViewerBundle::findUniformInput(const char* appearanceName, const char* inputName) { - ramses::RamsesObject* appearanceObj = m_loadedScene->findObjectByName(appearanceName); - if (appearanceObj) + auto* appearance = m_loadedScene->findObject(appearanceName); + if (appearance) { - ramses::Appearance* appearance = ramses::RamsesUtils::TryConvert(*appearanceObj); - if (appearance) - { - return new UniformInputWrapper(inputName, *appearance); - } + return new UniformInputWrapper(inputName, *appearance); } return nullptr; } diff --git a/demo/android/DemoJNIInterface/src/UniformInputWrapper.cpp b/demo/android/DemoJNIInterface/src/UniformInputWrapper.cpp index 5dd076c91..57d2762d2 100644 --- a/demo/android/DemoJNIInterface/src/UniformInputWrapper.cpp +++ b/demo/android/DemoJNIInterface/src/UniformInputWrapper.cpp @@ -8,18 +8,19 @@ #include "UniformInputWrapper.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" UniformInputWrapper::UniformInputWrapper(const char* inputName, ramses::Appearance& appearance) - :m_appearance(appearance) + : m_input{appearance.getEffect().findUniformInput(inputName)} + , m_appearance{appearance} { - m_appearance.getEffect().findUniformInput(inputName, m_input); + assert(m_input.has_value()); } void UniformInputWrapper::setInputValueFloat(float x) { - m_appearance.setInputValue(m_input, x); + m_appearance.setInputValue(*m_input, x); } diff --git a/demo/iOS/CMakeLists.txt b/demo/iOS/CMakeLists.txt new file mode 100644 index 000000000..4c95cb33f --- /dev/null +++ b/demo/iOS/CMakeLists.txt @@ -0,0 +1,67 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +if(${CMAKE_SYSTEM_NAME} MATCHES "iOS") + enable_language(OBJC OBJCXX Swift) + + set(MACOSX_BUNDLE_EXECUTABLE_NAME "ramses-renderer-ios-app") + set(MACOSX_BUNDLE_INFO_STRING "com.bmw.ramses-renderer-ios-app") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.bmw.ramses-renderer-ios-app") + set(MACOSX_BUNDLE_BUNDLE_NAME "com.bmw.ramses-renderer-ios-app") + set(MACOSX_BUNDLE_ICON_FILE "") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "1.0") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0") + set(MACOSX_BUNDLE_BUNDLE_VERSION "1.0") + set(MACOSX_BUNDLE_COPYRIGHT "Copyright (C) 2023 BMW AG") + set(MACOSX_DEPLOYMENT_TARGET 14.0) + + SET(CMAKE_XCODE_GENERATE_SCHEME ON) + SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY, "./out") + + set(APP_HEADER_FILES + ./include/RendererBundle.h + ./include/ramses-renderer-ios-app-Bridging-Header.h + ) + + set(APP_SOURCE_FILES + ./src/AppDelegate.swift + ./src/RendererBundle.mm + ./src/SceneDelegate.swift + ./src/ViewController.swift + ) + + set(RESOURCES + ./res/Main.storyboard + ./res/LaunchScreen.storyboard + ) + + include_directories(PUBLIC ./include) + + add_executable( + ramses-renderer-ios-app + MACOSX_BUNDLE + ${APP_HEADER_FILES} + ${APP_SOURCE_FILES} + ${RESOURCES} + ) + + SET(EMBED_FRAMEWORKS ramses-shared-lib) + set_target_properties(ramses-renderer-ios-app PROPERTIES + MACOSX_FRAMEWORK_IDENTIFIER com.bmw.ramses-renderer-ios-app + XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer" + XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@loader_path/Frameworks" + XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION + XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/include/ramses-renderer-ios-app-Bridging-Header.h" + XCODE_EMBED_FRAMEWORKS "${EMBED_FRAMEWORKS}" + XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + RESOURCE "${RESOURCES}" + ) + + target_link_libraries(ramses-renderer-ios-app PUBLIC ramses-shared-lib) +endif() diff --git a/demo/iOS/Info.plist b/demo/iOS/Info.plist new file mode 100644 index 000000000..70e607869 --- /dev/null +++ b/demo/iOS/Info.plist @@ -0,0 +1,53 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleGetInfoString + com.bmw.ramses-renderer-ios-app + CFBundleIconFile + + CFBundleIdentifier + com.bmw.ramses-renderer-ios-app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + 1.0 + CFBundleName + com.bmw.ramses-renderer-ios-app + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + + NSHumanReadableCopyright + Copyright (C) 2023 BMW AG + + diff --git a/demo/iOS/README.md b/demo/iOS/README.md new file mode 100644 index 000000000..653fbe573 --- /dev/null +++ b/demo/iOS/README.md @@ -0,0 +1,30 @@ +# RAMSES Renderer demo app + +This is an example how a RAMSES renderer can be integrated into an iOS app. It includes compiling RAMSES as native code for iOS with cmake, and invokes it using ObjectiveC++ from a Swift App. +The app contains an UIView with a CAMetalLayer which is used to instantiate the RAMSES renderer. The renderer then uses the surface as window for rendering. + +The RAMSES renderer app connects to pre-defined IPs set in iOS/src/ViewController.swift. The default IPs can be used to test the app with the iOS simulator. For running the app on a hardware device the values need to be adapted accordingly. + +To show content inside the RAMSES renderer app with the iOS simulator and a macOS host system follow these steps: + +- Generate an Xcode project for iOS using cmake with the ramses repository root folder as input. You can add the ID of your development team using `-DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=...` at the end of this command, otherwise Xcode might ask you to set a development team ID manually. +``` +cmake .. -DCMAKE_SYSTEM_NAME=iOS -GXcode -DCMAKE_OSX_SYSROOT=iphonesimulator +``` +- Open the resulting Xcode project to build and run the ramses-renderer-ios-app on the iOS simulator. +- Build RAMSES for the macOS host system with a C++ cmake build (not an iOS build). Use the same version of RAMSES repository used for building the iOS app. +- Go to the bin directory of the RAMSES macOS build and execute in two separate shells: +```shell +./ramses-daemon +``` +and +```shell +./ramses-example-basic-geometry +``` +to start a RAMSES daemon and a RAMSES client example on the host system. + +The renderer will connect to the client on the host system and render the scene provided by the client. Any of the non-local ramses examples can be started in the same manner. + +# RAMSES Client creation + +Ramse client can be created, in order to create scene and render them on a Ramses renderer in a similar fashion to any of the other supported platforms. diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/EDeviceType.h b/demo/iOS/include/RendererBundle.h similarity index 57% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/EDeviceType.h rename to demo/iOS/include/RendererBundle.h index cbdc49671..0ff149bb1 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/EDeviceType.h +++ b/demo/iOS/include/RendererBundle.h @@ -6,17 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EDEVICETYPE_H -#define RAMSES_EDEVICETYPE_H +#pragma once -namespace ramses_internal -{ - enum class EDeviceType - { - GLES_3_0, - GL_4_2, - GL_4_5 - }; -} +#import -#endif +@interface RendererBundle : NSObject + +- (instancetype)initWithMetalLayer:(CAMetalLayer *)metalLayer width:(int)width height:(int)height interfaceSelectionIP:(NSString*)interfaceSelectionIP daemonIP:(NSString*)daemonIP; + +- (void)dealloc; + +- (void)connect; +- (void)run; +- (CAMetalLayer*) getNativeWindow; + +@end diff --git a/client/logic/unittests/shared/LogicNodeDummy.cpp b/demo/iOS/include/ramses-renderer-ios-app-Bridging-Header.h similarity index 86% rename from client/logic/unittests/shared/LogicNodeDummy.cpp rename to demo/iOS/include/ramses-renderer-ios-app-Bridging-Header.h index 999b98f58..c04537a46 100644 --- a/client/logic/unittests/shared/LogicNodeDummy.cpp +++ b/demo/iOS/include/ramses-renderer-ios-app-Bridging-Header.h @@ -1,9 +1,11 @@ // ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG +// Copyright (C) 2023 BMW AG // ------------------------------------------------------------------------- // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "LogicNodeDummy.h" +#pragma once + +#import "RendererBundle.h" diff --git a/demo/iOS/res/LaunchScreen.storyboard b/demo/iOS/res/LaunchScreen.storyboard new file mode 100644 index 000000000..865e9329f --- /dev/null +++ b/demo/iOS/res/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/iOS/res/Main.storyboard b/demo/iOS/res/Main.storyboard new file mode 100644 index 000000000..322d6341f --- /dev/null +++ b/demo/iOS/res/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/iOS/src/AppDelegate.swift b/demo/iOS/src/AppDelegate.swift new file mode 100644 index 000000000..8fd1c34b5 --- /dev/null +++ b/demo/iOS/src/AppDelegate.swift @@ -0,0 +1,24 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + } +} diff --git a/demo/iOS/src/RendererBundle.mm b/demo/iOS/src/RendererBundle.mm new file mode 100644 index 000000000..5f91cd53d --- /dev/null +++ b/demo/iOS/src/RendererBundle.mm @@ -0,0 +1,107 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererBundle.h" + +#include +#include +#include +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/framework/RamsesFramework.h" + +class SceneStateAutoShowEventHandler : public ramses::RendererSceneControlEventHandlerEmpty +{ +public: + SceneStateAutoShowEventHandler(ramses::RamsesRenderer& renderer, ramses::displayId_t displayId); + + virtual void sceneStateChanged(ramses::sceneId_t sceneId, ramses::RendererSceneState state) override; + +private: + ramses::RendererSceneControl& m_sceneControlAPI; + ramses::displayId_t m_displayId; +}; + +SceneStateAutoShowEventHandler::SceneStateAutoShowEventHandler(ramses::RamsesRenderer& renderer, ramses::displayId_t displayId) + : m_sceneControlAPI(*renderer.getSceneControlAPI()) + , m_displayId(displayId) +{ +} + +void SceneStateAutoShowEventHandler::sceneStateChanged(ramses::sceneId_t sceneId, ramses::RendererSceneState state) +{ + if (state == ramses::RendererSceneState::Available) + { + m_sceneControlAPI.setSceneMapping(sceneId, m_displayId); + m_sceneControlAPI.setSceneState(sceneId, ramses::RendererSceneState::Rendered); + m_sceneControlAPI.flush(); + } +} + +@implementation RendererBundle { + CAMetalLayer* m_nativeWindow; + std::unique_ptr m_framework; + ramses::RamsesRenderer* m_renderer; + std::unique_ptr m_autoShowHandler; + std::unique_ptr m_dispatchEventsThread; + std::atomic m_cancelDispatchLoop; +} + +- (instancetype)initWithMetalLayer:(CAMetalLayer *)metalLayer width:(int)width height:(int)height interfaceSelectionIP:(NSString*)interfaceSelectionIP daemonIP:(NSString*)daemonIP { + self = [super init]; + m_nativeWindow = metalLayer; + + ramses::RamsesFrameworkConfig frameworkConfig(ramses::EFeatureLevel_Latest); + frameworkConfig.setInterfaceSelectionIPForTCPCommunication([interfaceSelectionIP cStringUsingEncoding:kCFStringEncodingUTF8]); + frameworkConfig.setDaemonIPForTCPCommunication([daemonIP cStringUsingEncoding:kCFStringEncodingUTF8]); + m_framework.reset(new ramses::RamsesFramework(frameworkConfig)); + ramses::RendererConfig rendererConfig; + m_renderer = m_framework->createRenderer(rendererConfig); + + ramses::DisplayConfig displayConfig; + displayConfig.setWindowRectangle(0, 0, width, height); + displayConfig.setIOSNativeWindow(ramses::IOSNativeWindowPtr{ m_nativeWindow }); + ramses::displayId_t displayId = m_renderer->createDisplay(displayConfig); + + m_autoShowHandler.reset(new SceneStateAutoShowEventHandler(*m_renderer, displayId)); + + m_renderer->setFramerateLimit(displayId, 60); + m_renderer->flush(); + return self; +} + +-(void)dealloc { + m_cancelDispatchLoop = true; + m_dispatchEventsThread->join(); + m_framework->destroyRenderer(*m_renderer); + [super dealloc]; +} + +-(void)connect { + m_framework->connect(); +} + +-(void) run { + m_renderer->startThread(); + m_dispatchEventsThread.reset(new std::thread([self] + { + while (m_cancelDispatchLoop == false) + { + m_renderer->getSceneControlAPI()->dispatchEvents(*m_autoShowHandler); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + })); +} + +-(CAMetalLayer*) getNativeWindow { + return m_nativeWindow; +} + +@end diff --git a/demo/iOS/src/SceneDelegate.swift b/demo/iOS/src/SceneDelegate.swift new file mode 100644 index 000000000..a2dfe5466 --- /dev/null +++ b/demo/iOS/src/SceneDelegate.swift @@ -0,0 +1,33 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + } + + func sceneDidBecomeActive(_ scene: UIScene) { + } + + func sceneWillResignActive(_ scene: UIScene) { + } + + func sceneWillEnterForeground(_ scene: UIScene) { + } + + func sceneDidEnterBackground(_ scene: UIScene) { + } +} diff --git a/demo/iOS/src/ViewController.swift b/demo/iOS/src/ViewController.swift new file mode 100644 index 000000000..b432c384e --- /dev/null +++ b/demo/iOS/src/ViewController.swift @@ -0,0 +1,55 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +import UIKit + +class ViewController: UIViewController { + private var metalLayer: CAMetalLayer! + private var rendererBundle: RendererBundle? + + override func viewDidLoad() { + super.viewDidLoad() + metalLayer = CAMetalLayer() + metalLayer.frame = view.frame + view.layer.addSublayer(metalLayer) + } + + private func recreateRenderer() { + destroyRenderer() + + metalLayer.contentsScale = view.window!.screen.nativeScale + metalLayer.drawableSize = CGSizeMake(metalLayer.frame.size.width * metalLayer.contentsScale, + metalLayer.frame.size.height * metalLayer.contentsScale) + rendererBundle = RendererBundle(metalLayer: metalLayer, + width: Int32(metalLayer.frame.size.width * metalLayer.contentsScale), + height: Int32(metalLayer.frame.size.height * metalLayer.contentsScale), + interfaceSelectionIP: "127.0.0.1", + daemonIP: "127.0.0.1") + rendererBundle?.connect() + rendererBundle?.run() + } + + private func destroyRenderer() { + rendererBundle = nil + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + coordinator.animate(alongsideTransition: nil) { context in + self.metalLayer.frame = self.view.layer.frame + self.recreateRenderer() + } + } + + override func viewDidAppear(_ animated: Bool) { + recreateRenderer() + } + + override func viewDidDisappear(_ animated: Bool) { + destroyRenderer() + } +} diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 06fcd2bd1..7b3535294 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -34,11 +34,7 @@ endif() # when an API file is changed) file(GLOB_RECURSE API_FILES - # TODO clean this up, it is a mess - ${PROJECT_SOURCE_DIR}/*/ramses-*-api/include/*.h - ${PROJECT_SOURCE_DIR}/*/*/ramses-*-api/include/*.h - ${PROJECT_SOURCE_DIR}/*/*/*/ramses-*-api/include/*.h - ${PROJECT_SOURCE_DIR}/client/logic/include/ramses-logic/*.h + ${PROJECT_SOURCE_DIR}/include/ramses/*.h ) string(REPLACE ";" " " DOXYGEN_INPUT "${API_FILES}") diff --git a/doc/old_ramses/developer/00_MainPage.dox b/doc/old_ramses/developer/00_MainPage.dox deleted file mode 100644 index 8d7ed0428..000000000 --- a/doc/old_ramses/developer/00_MainPage.dox +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page DeveloperMain Developer Documentation - - -# Content - -This document contains important information for people working with the RAMSES code -base. - -- @ref GettingStarted -- @ref Contributing -- @ref CodeStyleGuide -- @ref RAMSESClientAPIDev -- @ref RAMSESTestStrategy - - -*/ diff --git a/doc/old_ramses/developer/10_GettingStarted.dox b/doc/old_ramses/developer/10_GettingStarted.dox deleted file mode 100644 index e140b41fe..000000000 --- a/doc/old_ramses/developer/10_GettingStarted.dox +++ /dev/null @@ -1,169 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page GettingStarted Getting started with RAMSES - -\anchor buildingRamses -# Building RAMSES -In order to build RAMSES, some tools are required: -- git (recent version with proper submodule support) -- CMake (\>= 3.8) - -optional: -- doxygen (for documentation) -- python (for code style checking) - -## Clone Repository -RAMSES is managed in a git repository, which pulls in additional build dependencies using git submodules. -All these external dependencies are contained in \/external/. -Take care to update the submodules to the expected state before building. - -You can either checkout recursively - - $ git clone --recursive repositoryLocation - -or update submodules manually - - $ git clone repositoryLocation - $ cd ramses-sdk - $ git submodule update --init --recursive - -## Configure Build Environment -The RAMSES build scripts are based on CMake, but we avoided writing plain CMake build scripts for each module of the project. -Instead we use a centralized set of CMake macros for project configuration, which are called ACME2 ("Another CMake Extension 2"). -Having centralized scripts greatly improves maintainability of our build scripts and guarantees the same behaviour for each module of RAMSES. -ACME2 is pulled into the build environment as git submodule together with other build dependencies. - -Concerning build environment setup, ACME2 is fully transparent. -RAMSES can be treated as standard CMake project. - -For some platforms RAMSES requires the use of a toolchain file to define some compiler constants. -The RAMSES build environment contains a set of standard CMake toolchain files as a starting point. -These example toolchain files are located in - - /cmake/toolchain - - -### Example: generation of a Makefile build environment on Linux 64 bit host - - $ mkdir - $ cd - $ cmake -DCMAKE_TOOLCHAIN_FILE=/cmake/toolchain/Linux_X86_64.toolchain - & make - - -CMake specific build flags -- `CMAKE_INSTALL_PREFIX=` - - configure target directory for install build target - -- `CMAKE_BUILD_TYPE=` - - set build configuration, if not specified, RAMSES will default to "Release". - -Common build flags (created by ACME2) include -- `ramses-sdk_BUILD_TESTS:[ON|OFF]` - - `ON`: enable build of RAMSES unit tests - - `OFF`: disable build of RAMSES unit tests -- `ramses-sdk__:[ON|OFF|AUTO]` - - `ON`: enable build of module - - `OFF`: disable build of module - - `AUTO`: build enabled, if dependencies are resolved; otherwise build is disabled -- `ramses-sdk_BUILD_EXAMPLES:[ON|OFF]` - -## Build -After CMake has successfully created the build environment, build the RAMSES project as required by your selected build environment, e.g. -- Makefile: make -- Visual Studio: F7 -- KDevelop: F8 -- XCode: Ctrl + B -- QtCreator: Ctrl + B - -## [Optional] Install -CMake creates an install target in most cases, which will copy all build results into the directory defined in CMAKE_INSTALL_PREFIX. -Triggering the install target depends on the selected CMake generator and the build environment, e.g. -- Makefile: make install -- Most IDEs (Visual Studio, KDevelop, XCode, QtCreator, ...): build install target - -## [Optional] Package -Our ACME2 build scripts internally configure CPack to support platform specific packaging. -Triggering the package target depends on the selected CMake generator and the build environment, e.g. -- Makefile: make package -- Most IDEs (Visual Studio, KDevelop, XCode, QtCreator, ...): build package target - -\anchor running -# Running RAMSES - -## Run Unit Tests -The unit tests of RAMSES are based on GTest (Google Test) library, so all unit test binaries inherit all gtest features like shuffling, generating XML reports or filtering of tests to be executed. -Unit tests are internally registered at CTest to support best integration of unit test execution into various IDEs. Depending on the used CMake generator, unit tests can be executed by -- running 'make test' for Makefile based systems -- executing target 'RUN_TESTS' in Visual Studio - -## Run RAMSES Test Scenario -A minimal test scenario for RAMSES includes three applications: -- Daemon: central controller for content distribution -- Renderer: render content to visual representation on display -- Client: provide content to be rendered - -### Run Daemon -The RAMSES Daemon component is simply started without any special arguments. - - $ cd /bin - $ ./ramses-daemon - -It will enable its communication framework (currently Ethernet IPv4) and wait for incoming connections. -A RAMSES setup always requires exactly one daemon to be available within the communication network. -Any RAMSES component (Renderers, Clients) needs to know how to connect to the daemon (IP address in current implementation). - -### Run Renderer -The RAMSES renderer component creates a dedicated renderer binary for each supported platform, e.g. - -Renderer Name | Description ----------------------------------------|--------------------------------------------- -ramses-renderer-x11-egl-es-3-0 | OpenGL ES 3.0 Renderer for X11 backend -ramses-renderer-wayland-ivi-egl-es-3-0 | OpenGL ES 3.0 Renderer for Wayland Backend -ramses-renderer-windows-wgl-4-2-core | OpenGL 4.2 Renderer for Windows -ramses-renderer-windows-wgl-4-5 | OpenGL 4.5 Renderer for Windows - -All renderer binaries share the same set of command line arguments. -Run with '-help' to see available command line parameters. - - -### Run Client Application -All RAMSES client applications need a connection to RAMSES daemon to register their content. -All current client applications interpret the command line flag -- `--daemon-ip ` - -as IP address of the daemon to connect to. -If no IP address is provided, 'localhost' is silently used as daemon address. - -The client applications are located in - - /bin - -Most client applications load resource files (e.g. .RAMSES files), which are located in - - /bin/res - -Clients expect the resources in - - /res - -directory. -Take care to start the clients with a working directory of \/bin, -where res folder can be resolved. Otherwise expect misbehaviour of client applications. - -#### Example: Start a basic example, connect to ramses-daemon at IP 1.2.3.4 - - $ cd /bin - $ ./ramses-example-basic-geometry --daemon-ip 1.2.3.4 - -*/ diff --git a/doc/old_ramses/developer/20_Contributing.dox b/doc/old_ramses/developer/20_Contributing.dox deleted file mode 100644 index b73983070..000000000 --- a/doc/old_ramses/developer/20_Contributing.dox +++ /dev/null @@ -1,54 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page Contributing Contributing Code to RAMSES - -# Patch format - -All patches for RAMSES have to pass our code review system. No code can be -merged into the repository, without passing the review of another RAMSES -developer. In order to enable fast patch reviews, patches are required to -meet some requirements. Patches that do not obey these rules, will not be -considered during review and have no chance of being merged into the code -base (*). - -- has a meaningful patch description - - if reviewer does not understand the content of the patch from the - description, review will be denied - - patch description matches exactly the code change -- targets exactly one issue - - changes exacty one issue in the code - - no mixing of refactoring and other work - - NOT: adds function to API and fixed memory leak -- small - - typically only few lines - - cannot be split -- self-contained - - does not break anything -- includes unit tests - - patch contains at least one test closely related to the change - - see test strategy, how to test features -- works for all platforms - - checked by zuul build jobs - - patches not passing build servers will not be reviewed -- respects licenses and external IP - - patch must not contain any copied code - - patch content is free of external IP - - all mandatory (open source) license restrictions are satisfied -- applies to latest master - - conflicts with master have to be resolved by patch creator - -If one patch depends on another patch in order for a change to be -complete, that is OK and encouraged. Multiple smaller patches are -generally better. A good reference for patch design is the Linux -kernel documentation (parts 2, 3 and 4): -https://www.kernel.org/doc/Documentation/SubmittingPatches - -*/ diff --git a/doc/old_ramses/developer/30_CodeStyleGuide.dox b/doc/old_ramses/developer/30_CodeStyleGuide.dox deleted file mode 100644 index 0a47c8949..000000000 --- a/doc/old_ramses/developer/30_CodeStyleGuide.dox +++ /dev/null @@ -1,664 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page CodeStyleGuide Code Style Guide for RAMSES modules - -# Goals - -- Good readable code -- Simplify debugging -- Enforcing correctness for obvious code issues -- Consistent code style through all of the RAMSES modules -- Have fully automated code style checking for local and build server usage - - -# Automated Testing - -RAMSES ships with a python-based code style checker (in \/scripts/code_style_checker). -You should not use it directly, but trigger the target "CHECK_CODE_STYLE" generated by CMake. - -Environment | Usage ------------------------------------- -Visual Studio | right click on "CHECK_CODE_STYLE" (in folder "CMakePredefinedTargets") and choose "Build" -Makefiles | make CHECK_CODE_STYLE -Ninja | ninja-build CHECK_CODE_STYLE - -On invocation, the style checker will issue *two* warnings per style issue: -- Visual Studio/MSBuild format -- GCC format - -This allows various IDEs to pick up the warnings depending on their preferred flavor. - - -# (Unordered) Rules - -### Each file must contain an accepted license header. -- Rationale: Make sure that proprietary license cannot be missed. -- Wrong: -\code -No source code license in file. -\endcode -- Correct: -\code -Source file starts with license -\endcode -- Style Checker: warning - -### One Class definition per file, filename matches class name. - -- Rationale: Readability, Consistency - -### Types have camel-case name, no prefixes (CClass, SStruct) - -- Rationale: Readability, Consistency -- Wrong: -\code -class C_foobar; -\endcode -- Correct: -\code -class FooBar; -\endcode -- Style Checker: N/A - - - -### Use "I" Prefix for abstract interfaces. - -- Rationale: Readability, Consistency -- Correct: -\code -class INode -{ -public: - virtual void func1() = 0; - virtual void func2() = 0; -} -\endcode -- Style Checker: N/A - - -### If possible use enum class over plain enum for type safety. If plain enum is used, prefix with "E" and each value has is prefixed with a full enum type name (exactly). - -- Rationale: Readability, Consistency -- Correct: -\code -enum ENodeType -{ - ENodeType_Bar, - ENodeType_Foo -}; -\endcode -- Style Checker: warning - - -### When using enum classes, don't use "E" prefix for the type name; value names must NOT be prefixed by type name. - -- Rationale: Readability, Consistency -- Correct: -\code -enum class NodeType -{ - Foo, - Bar -}; -\endcode -- Style Checker: N/A - - -### Use upper case with underscore for macros. - -- Rationale: Readability, Consistency -- Correct: -\code -#define MY_SPECIAL_MACRO -\endcode -- Style Checker: N/A - - -### Header files must include header guards with format RAMSES_[PREFIX_]FILENAME_H. - -- Rationale: Correctness -- Wrong: -\code -in file BadExample.h -#ifndef __BAD_EXAMPLE_H__ -#define __BAD_EXAMPLE_H__ -[...] -#endif // __BAD_EXAMPLE_H__ -\endcode -- Correct: -\code -in file GoodExample.h -#ifndef RAMSES_GOODEXAMPLE_H -#define RAMSES_GOODEXAMPLE_H -[...] -#endif -\endcode -- Style Checker: warning - - -### Never use tabs, indent with 4 spaces. - -- Rationale: Readability, Consistency -- Style Checker: warning - -### Static member methods use camel case naming with capital first letter. - -- Rationale: Readability, Consistency -- Wrong: -\code -class UtilClass -{ -public: - static void doAwesomeStuff(); -} -\endcode -- Correct: -\code -class UtilClass -{ -public: - static void DoAwesomeStuff(); -} -\endcode -- Style Checker: N/A - -### Static const variables use camel case naming with capital first letter. - -- Rationale: Readability, Consistency -- Wrong: -\code -class Constants -{ - public: - static const int some_var = 0; -} -\endcode -- Correct: -\code -class Constants -{ - public: - static const int SomeVar = 0; -} -\endcode -- Style Checker: N/A - - -### All non public API RAMSES code lives in ramses_internal namespace. - -- Rationale: Correctness, Consistency -- Correct: -\code -namespace ramses_internal -{ - [ramses code] -} // namespace ramses_internal -\endcode -- Style Checker: N/A - - -### RAMSES API code lives in ramses namespace. - -- Rationale: Correctness, Consistency -- Correct: -\code -namespace ramses -{ - [ramses code] -} // namespace ramses -\endcode -- Style Checker: N/A - - -### All files end with an empty line. - -- Rationale: Correctness, Consistency -- Style Checker: warning - - -### Member functions use camel case, starting with small first letter. Prefer a name starting with a verb. - -- Rationale: Readability, Consistency -- Wrong: -\code -class MyClass -{ - UInt32 Get_Value(); -}; -\endcode -- Correct: -\code -class MyClass -{ - UInt32 getValue() const; -}; -\endcode -- Style Checker: N/A - -### Structs should have only public members, which don't have a m_ prefix. - -- Rationale: Readability, Consistency, Simplicity -- Wrong: -\code -struct ThisLooksLikeAClass -{ - UInt32 doComplicatedLogic(); - -private: - UInt32 m_foo; -}; - -\endcode -- Correct: -\code -struct MyStruct -{ - UInt32 foo; - UInt32 bar; -}; -\endcode -- Style Checker: N/A - - -### Member variables start with prefix "m_", followed by camel case name starting with small letter. - -- Rationale: Readability, Consistency -- Wrong: -\code -class MyClass -{ - UInt32 myCoolVar; -}; -\endcode -- Correct: -\code -class MyClass -{ - UInt32 m_myCoolVar; -}; -\endcode -- Style Checker: N/A - - -### Always use const wherever possible. -Mutable is allowed in specific cases (caching, locks) but must keep -'logical' constness - changes may not be visible to the outside. - -- Rationale: Correctness, Debugging, Compiler friendly -- Wrong: -\code -class MyClass -{ - Bool isValid(); - String process(String& str); - String& getCurrentMsg(); -}; -\endcode -- Correct: -\code -class MyClass -{ - Bool isValid() const; - String process(const String& str) const; - const String& getCurrentMsg() const; -}; -\endcode -- Style Checker: N/A - - -### Comments have form "// TODO() text". - -- Rationale: Developers should take responsibility for TODOs in code and be able to answer questions on the issue. -- Wrong: -\code -// TODO some text -\endcode -- Correct: -\code -// TODO(Timo) some text -\endcode -- Style Checker: N/A - -### Avoid unused variables where possible, mark with macro UNUSED(x) when needed. - -- Rationale: Correctness -- Wrong: -\code -void func(Int32 value) -{ -} -\endcode -- Correct (use for permanent solutions, comment out or delete): -\code -void func(Int32 /*value*/) -{ -} -\endcode -- Correct (only temporarily or technical necessity) -\code -void func(Int32 value) -{ - UNUSED(value); -} -\endcode -- Style Checker: N/A (is detected by compiler) - - -### Remove any spaces at the of lines. - -- Rationale: Consistency, Correctness -- Style Checker: warning - - -### Only do one definition per line. - -- Rationale: Debugging -- Wrong: -\code -Int32* a = NULL, b, c = 5; -\endcode -- Correct: -\code -Int32* a = NULL; -Int32 b = 0; -Int32 c = 5; -\endcode -- Style Checker: warning - - -### Only one statement per line. - -- Rationale: Debugging -- Wrong: -\code -case 4: doSomething(); a += 5; checkForFoo(); -\endcode -- Correct: -\code -case 4: - doSomething(); - a += 5; - checkForBlub(); - break; -\endcode -- Style Checker: Disabled - - -### Curly braces on dedicated lines. - -- Rationale: Readability, Consistency -- Wrong: -\code -while (running) { - [...] -} -\endcode -- Correct: -\code -while (running) -{ - [...] -} -\endcode -- Style Checker: warning - - -### Always use virtual and override, final keywords where appropriate. - -- Rationale: Correctness, Consistency -- Style Checker: N/A - -### Avoid empty lines at start and end of code blocks. - -- Rationale: Readability -- Wrong: -\code -class MyClass -{ - - MyClass(); - -}; -\endcode -- Correct: -\code -class MyClass -{ - MyClass(); -}; -\endcode -- Style Checker: N/A - -### Don't add extra whitespaces in argument lists, just one space after a comma. - -- Rationale: Readability, Consistency -- Wrong: -\code -void func( Int32 arg1 , Bool arg2 ); -\endcode -- Correct: -\code -void func(Int32 arg1, Bool arg2); -\endcode -- Style Checker: N/A - - -### Initialize primitive types, especially pointers. Prefer init in class declaration. - -- Rationale: Consistency, Safety -- Wrong: -\code -Foo* myFoo; -int i; - -class myClass -{ - Foo* m_foo; - int m_val; -}; -\endcode -- Correct: -\code -Foo* myFoo = nullptr; -int i = 0; - -class myClass -{ - Foo* m_foo = nullptr; - int m_val = 0; - // or in constructor -}; -\endcode -- Style Checker: N/A - - -### Avoid raw pointers. - -- Rationale: Safety -- Wrong: -\code -int *arr = new int[size]; -int arrStatic[3]; -Foo *foo = new Foo; -delete foo; -\endcode -- Correct: -\code -std::vector arr(size); -std::array arrStatic{ x, y, z }; -std::unique_ptr foo{ new Foo }; - -\endcode -- Style Checker: N/A - -### Class initialization list - one member variable per line - -- Rationale: Readability, Consistency, Debugging -- Wrong: -\code -MyClass::MyClass() : BaseClass(param, param2), m_bmw(1), m_merc(2), m_audi(3) {} -\endcode -- Correct: -\code -MyClass::MyClass() - : BaseClass(param, param2) - , m_bmw(1) - , m_merc(2) - , m_audi(3) -{ -} -\endcode -- Style Checker: N/A - - -### Avoid using typedef, prefer using "using" instead - -- Rationale: Readability -- Wrong: -\code -typedef int Index; -\endcode -- Correct: -\code -using Index = int; -\endcode -- Style Checker: N/A - -### Don't use "using namespace MyNamespace;" in global namespace and in headers - -- Rationale: Readability, Consistency -- Style Checker: N/A - -### Prefer the rule of 5 for constructors and assignment operators - -- Rationale: Readability, Consistency -- Style Checker: N/A - -### Constructors have to be explicit when one argument only - -- Rationale: Robustness -- Wrong: -\code -class MyClass -{ - MyClass(); - MyClass(int num); -}; -\endcode -- Correct: -\code -class MyClass -{ - MyClass(); - explicit MyClass(int num); -}; -\endcode -- Style Checker: N/A - -### Don't use exceptions, if you have to call exception-throwing functions make sure they don't throw exceptions in your code for your inputs. - -- Rationale: Compatibility -- Wrong: -\code -try -{ - helperMap.at(key) = 5; -} -catch(...) -[...] -\endcode -- Correct: -\code -auto it = helperMap.find(key); -if (it != helperMap.end()) -{ - *it = 5; -} -\endcode -- Style Checker: N/A - -### Types which are only used in one translation unit (.cpp file) must be put in anonymous namespace - -- Rationale: Robustness -- Style Checker: N/A - - -### Functions which are only used in one translation unit (.cpp file) must be put in anonymous -namespace or declared static - -- Rationale: Robustness -- Style Checker: N/A - - -### Anonymous namespace must not be used in header files - -- Rationale: Performance, Correctness -- Style Checker: N/A - - -### Class definition - public, protected, private in that order. -Generally: Types first, Methods, followed by member variables. - -- Rationale: Readability, Consistency -- Wrong: -\code -class MyClass() -{ -public: - Int32 m_val; -private: - void compute(); -public: - virtual void base0Method(); -}; -\endcode -- Correct: -\code -class MyClass() -{ -public: - virtual void base0Method(); - virtual void base1Method(); - -protected: - void protMethod(); - - Int32 m_val; - -private: - void compute(); - - Data m_data; -}; -\endcode -- Style Checker: N/A - - - -# Good practices - -### Use reserve() for all containers, when possible to estimate element count - -- Rationale: Performance - - -### Use STL algorithms when applicable (e.g. find_if instead of range-based for) - -- Rationale: Performance, Stability - - -### Use modern pointer conventions - avoid shared pointers, use unique_ptr instead of new/delete etc. - -- Rationale: Performance, Stability - - -*/ diff --git a/doc/old_ramses/developer/40_ClientAPI.dox b/doc/old_ramses/developer/40_ClientAPI.dox deleted file mode 100644 index 63b82c34f..000000000 --- a/doc/old_ramses/developer/40_ClientAPI.dox +++ /dev/null @@ -1,62 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page RAMSESClientAPIDev RAMSES Client API - -# RAMSES Client Code Structure - -The Client API and its implementation are separated using the PIMPL(*) pattern, to -support a high degree of API stability, while still being able to change -almost any aspect of the underlying implementation. - -(*) PIMPL = Pointer to IMPLementation - -The RAMSES Renderer API follows the same priniciples as the Client API, just with much smaller number of classes. - -# Object Lifecycle - -The RAMSES Client API was designed with efficient data structures for -distributed use cases. The major impact of that design decision is a strict -memory management strategy implemented in the RAMSES framework. -Another direct result is the mandatory use of factories to create (and -destroy) all framework managed data structures. - -All objects created by one of the client API factories (RamsesClient or Scene) -are owned by the object containing the factory and must be destroyed with the corresponding destroy() function. -Using an object after passing it to a destroy() function is an application bug. -Destroying the factory automatically destroys all its managed objects. - -Any object created by application code is owned by application. RAMSES -takes neither ownership nor longer term reference unless explicitly specified in the API. - - -# Resulting Code Rules - --# Every RAMSES client object derives from RamsesObject, directly or indirectly - - RamsesObject provides functionality common for all RAMSES objects - --# Make its constructor, destructor protected, Copy/Move-constructor deleted - - force applications to use factories - --# Every PIMPL object derives from RamsesObjectImpl, directly or indirectly - - RamsesObjectImpl provides functionality common for all PIMPL objects - --# Define factory as its friend - --# The high level API implementation only forwards calls to the internal PIMPL class, only the PIMPL contains logic and has access to internal framework. - --# Factory owns RAMSES client object and the client object owns its PIMPL - --# PIMPLS refer to internal framework data - - PIMPLS control lifecycle of internal data - --# RAMSES objects are passed as references into all API functions unless nullptr is a valid parameter - -*/ diff --git a/doc/old_ramses/developer/50_Testing.dox b/doc/old_ramses/developer/50_Testing.dox deleted file mode 100644 index f05c90499..000000000 --- a/doc/old_ramses/developer/50_Testing.dox +++ /dev/null @@ -1,163 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page RAMSESTestStrategy RAMSES Test strategy - -# Introduction - -The main focus of this document is not to provide extensive theory on testing, but rather to -suggest a mentality for our testing activities and to provide some helpful guides. - -##Primary goals - -What do we want to achieve with the time we invest into tests: - -- Catch errors as fast as possible -- Catch problems in all different issue categories -- Create understanding and transparency, which code has passed which quality gate. -In order to achieve this we must also -- Value good and maintainable test code -- Provide structures that reduce time and obstacles to add new tests - -## General principles for testing - -Principles that we should follow in our test activities result resulting from the primary goals: - -- Test as "small as possible" i.e. fail fast -- view test code as part of code base -> same quality concerning reusablility, robustness, readability etc. -- do not forget error cases when testing, they are at least as important -- must be automated - -From the "test as small as possible" principle it follows that a feature should always be tested on the smallest possible level. If e.g. the code around is organized in a layered fashion the test should if possible only test one layer and the adjacent layer should be covered by separate tests. -Other kinds of tests that test a user feature in a broader scope should only be used as **additional** security mechanism to ensure that the components work correctly together or to prove that a user feature works from end to end. - -# Types of errors - -Since the goals are distributed in very different areas of the development -process, there's not one magic kind of test that will detect all of the -possible issues. A multi-stage strategy for error detection and handling must -be defined to effectively handle the possible error scenarios. - -This section lists the different types of possible error scenarios and lists which measures we use to reduce -the risk of running into these problems. The types of tests mentioned in these measures are further explained in the next section. - -## Platform issues -Description: platforms behave differently; underlying library or driver was updated, now errors occur. - -Measures: -- Build every patch before mainline -- Run all unit tests (including sandwich tests) on all targets nightly -- Run renderer tests on all targets nightly - -## Code Issues -Description: errors in implementation - -Measures: -- Build/Run unit tests every patch before mainline -- static checking tools nightly for example coverity/teamscale (*todo) -- dynamic checking tools nightly for example valgrind/sanitizers - -## Complex Code Issues -Description: e.g. race conditions, dead-locks, use-after-free, memory leaks - -Measures: -- static & dynamic checking tools nightly for example coverity/teamscale/valgrind/sanitizers - -## Runtime Issues -Description: all kinds of possible runtime behavior - -Measures: -- real world stress tests -- top level smoke tests run nightly on all targets - -## Performance Issues -Description: Performance drops unnoticed / in certain cases - -Measures: -- performance tests in critical areas (renderloop, scene loading, scene transfer, latency) (*todo) - -## Integration Issues -Description: wrong usage of RAMSES in other applications - -Measures: -- Continuous delivery enables early customer integration and testing -- Provide example applications showing usage -- Customer documentation - -## Customer Feature Tests -Description: Customer feature exposes unexpected behavior, graphic artefacts, wrong content, ... - -Measures: -- Top level smoke tests run nightly on all targets - -## Requirement/Specification Issues -Description: Requirements change during development, are understood wrong or are missing - -Measures: -- Agile development allows change of requirements every sprint. Customer can review at early product stage. - - - -# Types of tests -## Unit test -- per class -- google test based - -ideally executed: -- per patch on build server for selected platforms -- nightly for all platforms - -## Sandwich test -- on component level or crossing multiple layers -- google test based - -ideally executed: -- per patch on build server for selected platforms -- nightly for all platforms - -## Rendering test -- sandwich test that executes all steps from scene to rendering of a pixel -- custom test framework - -ideally executed: -- with software renderer per patch on build server -- nightly for all platforms - -## Top level smoke test -- tests end-to-end executing a top-level Ramses feature -- test with binary deployment, real network communication -- custom python-based test framework - -ideally executed: -- nightly for all platforms - -## Stress test -- sandwich or smoke test under extreme load -- python-based test framework - -ideally executed: -- per patch on build server for selected platforms -- nightly for all platforms - -## Performance test -- tests performance-critical parts of Ramses -- TODO - -ideally executed: -- per patch on build server for selected platforms -- nightly for all platforms - - - -# Guide: which kind of test(s) should be used for a patch - -@image html guide-which-tests.png "Guide: which kind of test(s) should be used for a patch" - -*/ diff --git a/doc/old_ramses/developer/images/guide-which-tests.png b/doc/old_ramses/developer/images/guide-which-tests.png deleted file mode 100644 index 70b01e938fb99b6f46d45148cff00b58d52f6232..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60278 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 diff --git a/doc/old_ramses/developer/images/initiator.dot b/doc/old_ramses/developer/images/initiator.dot deleted file mode 100644 index 656aa1850..000000000 --- a/doc/old_ramses/developer/images/initiator.dot +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -digraph Initiator { - { - unavailable [label="Unavailable"] - send_session [label="Init session" shape=diamond] - session_wait [label="Wait session\nreply"] - connected [label="Connected"] - normal_send [label="Normal send" shape=diamond] - update_timeout [label="Update timeout" shape=diamond] - send_keepalive_wait_session [label="Send keepalive\n(Same MID)" shape=diamond] - send_keepalive_connected [label="Send keepalive\n(Same MID)" shape=diamond] - } - - unavailable -> send_session [label="Up"] - unavailable -> unavailable [label="Any message"] - - send_session -> session_wait [label="Ok"] - send_session -> session_wait [label="Fail"] - - session_wait -> unavailable [label="Down"] - session_wait -> session_wait [label="Unrealted SID"] - session_wait -> send_session [label="Timeout"] - session_wait -> send_session [label="MID Mismatch"] - session_wait -> send_session [label="Error valid SID"] - session_wait -> connected [label="Session confirm\n+CONNECT" color=green] - session_wait -> send_keepalive_wait_session [label="On timer"] - - send_keepalive_wait_session -> session_wait [label="Ok/Fail"] - - connected -> unavailable [label="Down\n+DISCONNECT" color=red] - connected -> update_timeout [label="Receive OK"] - connected -> connected [label="Unrealted SID"] // really? - connected -> send_session [label="Timeout\n+DISCONNECT" color=red] - connected -> send_session [label="MID Mismatch\n+DISCONNECT" color=red] - connected -> send_session [label="Error valid SID\n+DISCONNECT" color=red] - connected -> normal_send [label="Send message"] - connected -> send_keepalive_connected [label="On timer"] - - normal_send -> connected [label="Ok"] - normal_send -> send_session [label="Fail\n+DISCONNECT" color=red] - - update_timeout -> connected - - send_keepalive_connected -> connected [label="Ok/Fail"] -} diff --git a/doc/old_ramses/developer/images/ramses_logo_with_alpha2.png b/doc/old_ramses/developer/images/ramses_logo_with_alpha2.png deleted file mode 100644 index fa2b53b44c6d6da0b7454f0e34e0ffaa6b0fd199..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182174 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 z

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

6-TPdB8?-Zui7HI&Do_TQ?g|0}AtIRD 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

LeuXT$7JIVCg&3cAZ|Jw0vpRaB}Z zG0+TZTr`O$0g-=X$u9@q|0?sOlkoJXK~)gB4jn%*rtf3)z*ho)`G0 unavailable [label="Any (other) message"] - unavailable -> session_wait [label="Up"] - - session_wait -> unavailable [label="Down"] - session_wait -> send_sid_error_waitforsession [label="Non-init message"] - session_wait -> session_reply [label="Init session"] - - send_sid_error_waitforsession -> session_wait [label="OK/Fail"] - - session_reply -> connected [label="Ok\n+CONNECT" color=green] - session_reply -> session_wait [label="Fail"] - - connected -> connected [label="Receive OK"] - connected -> send_error [label="Wrong MID\n+DISCONNECT" color=red] - connected -> send_error [label="Timeout\n+DISCONNECT" color=red] - connected -> send_sid_error_connected [label="Wrong SID"] - connected -> session_reply [label="Init session\n+DISCONNECT" color=red] - connected -> unavailable [label="Down\n+DISCONNECT" color=red] - connected -> normal_send [label="Send message"] - connected -> send_keepalive_connected [label="On timer"] - - send_error -> session_wait [label="OK/Fail"] - - send_sid_error_connected -> connected [label="OK/Fail"] - - normal_send -> connected [label="Ok"] - normal_send -> send_error [label="Fail\n+DISCONNECT" color=red] - - send_keepalive_connected -> connected [label="Ok/Fail"] - - // extra stuff for faster ramp-up - unavailable -> wait_for_up [label="Init session" color=darkgrey] - wait_for_up -> unavailable [label="Timeout" color=darkgrey] - wait_for_up -> unavailable [label="Wrong MID or SID" color=darkgrey] - wait_for_up -> unavailable [label="Any other message" color=darkgrey] - wait_for_up -> session_reply [label="Up" color=darkgrey] - wait_for_up -> wait_for_up [label="Init session or keepalive" color=darkgrey] -} diff --git a/doc/old_ramses/developer/images/responder_with_old.dot b/doc/old_ramses/developer/images/responder_with_old.dot deleted file mode 100644 index c6a645fbb..000000000 --- a/doc/old_ramses/developer/images/responder_with_old.dot +++ /dev/null @@ -1,78 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -digraph Responder { - { - unknown [label="Unknown" color=purple] - old_code [label="Old Code" color=purple] - - unavailable [label="Unavailable"] - session_wait [label="Wait for\nsession"] - session_reply [label="Send session\nreply" shape=diamond] - connected [label="Connected"] - send_error [label="Send error with\nlast valid SID" shape=diamond] - send_sid_error_connected [label="Send error with\ninvalid SID" shape=diamond] - send_sid_error_waitforsession [label="Send error with\nreceived SID" shape=diamond] - normal_send [label="Normal send" shape=diamond] - send_keepalive_connected [label="Send keepalive\n(Same MID)" shape=diamond] - - wait_for_up [label="Wait for up"] - } - - unknown -> wait_for_up [label="New pinfo\n+Mark reponsible" color=purple] - unknown -> unknown [label="Any other message" color=purple] - unknown -> unknown [label="Old pinfo" color=purple] - unknown -> old_code [label="Up\n+Inform new" color=purple] - - old_code ->unknown [label="Down\n+Inform new" color=purple] - old_code -> old_code [label="Old pinfo" color=purple] - - // see notes in initiator - old_code -> old_code [label="Send pinfo\nSend keepalive (incr MID)" color=purple] - - // can directly reply -> no delay when arrives without issues - old_code -> session_reply [label="New pinfo\n+Mark resposible" color=purple] - - unavailable -> unavailable [label="Any other message"] - unavailable -> session_wait [label="Up"] - - session_wait -> unavailable [label="Down"] - session_wait -> send_sid_error_waitforsession [label="Non-init message"] - session_wait -> session_reply [label="Init session"] - - send_sid_error_waitforsession -> session_wait [label="OK/Fail"] - - session_reply -> connected [label="Ok\n+CONNECT" color=green] - session_reply -> session_wait [label="Fail"] - - connected -> connected [label="Receive OK"] - connected -> send_error [label="Wrong MID\n+DISCONNECT" color=red] - connected -> send_error [label="Timeout\n+DISCONNECT" color=red] - connected -> send_sid_error_connected [label="Wrong SID"] - connected -> session_reply [label="Init session\n+DISCONNECT" color=red] - connected -> unavailable [label="Down\n+DISCONNECT" color=red] - connected -> normal_send [label="Send message"] - connected -> send_keepalive_connected [label="On timer"] - - send_error -> session_wait [label="OK/Fail"] - - send_sid_error_connected -> connected [label="OK/Fail"] - - normal_send -> connected [label="Ok"] - normal_send -> send_error [label="Fail\n+DISCONNECT" color=red] - - send_keepalive_connected -> connected [label="Ok/Fail"] - - // extra stuff for faster ramp-up - unavailable -> wait_for_up [label="Init session"] - wait_for_up -> unavailable [label="Timeout"] - wait_for_up -> unavailable [label="Wrong MID or SID"] - wait_for_up -> unavailable [label="Any other message"] - wait_for_up -> session_reply [label="Up"] - wait_for_up -> wait_for_up [label="Init session or keepalive"] -} diff --git a/doc/old_ramses/general/40_Examples.dox b/doc/old_ramses/general/40_Examples.dox deleted file mode 100644 index 17470c689..000000000 --- a/doc/old_ramses/general/40_Examples.dox +++ /dev/null @@ -1,195 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page CodeExamples Exemplary API snippets - -# RAMSES API Usage by Example -Note: The following example code snippets -- omit error checking to keep code snippets small -- do not represent full applications - -The full source code of the examples is available in examples section of this document. - -## Minimal -This client implements the minimal requirements for a client to provide -content within the RAMSES distributed system. -@snippet ramses-example-minimal/src/main.cpp Minimal Example - -## Basic Geometry -This code creates a simple geometry. -@snippet ramses-example-basic-geometry/src/main.cpp Basic Geometry Example - -## Basic Scene Graph -This code creates a simple scene graph with multiple nodes. -@snippet ramses-example-basic-scenegraph/src/main.cpp Basic Scene Graph Example - -## Basic Texturing -This code creates a simple scene demonstrating basic usage of textures. -@snippet ramses-example-basic-texturing/src/main.cpp Basic Texturing Example - -## Basic Blending -This code creates a simple scene consisting of three triangles with alpha blending. -@snippet ramses-example-basic-blending/src/main.cpp Basic Blending Example - -## Basic GLSL Import -This code creates an appearance by importing GLSL ES 2.0 -shader code files. The appearance is then used to render a simple geometry. -@snippet ramses-example-basic-effect-from-glsl/src/main.cpp Basic GLSL Import Example - -## Basic Render Groups Handling -This code shows the usage of (nested) render groups. Render groups are containing -renderables which are rendered within the render pass the render group is added to. -The renderables inside one render group have an order in which they are rendered. -A render group can also contain other nested render groups with their renderables. -The example shows a render group containing two elements, a mesh and a nested -render group with two further meshes. -@snippet ramses-example-basic-rendergroups/src/main.cpp Basic Render Groups Example - -## Render Pass and Camera Setup -This code shows the usage of renderpasses and cameras. Each renderpass needs to -have a valid camera assigned. The example creates two renderpasses, each with a camera using -different projection and viewport parameters. In order for a mesh to be rendered -it must be added to a renderpass. -@snippet ramses-example-basic-renderpasses/src/main.cpp Basic Renderpasses Example - -## Basic Render Target Handling -This code shows the usage of a render target in combination with render passes. -A render target can be assigned to a render pass, so the pass renders its meshes into a render target. -The render target can be then used as texture input in a following render pass. -@snippet ramses-example-basic-rendertarget/src/main.cpp Basic Rendertarget Example - -## Local Compositing -This example shows how to replace a texture with an ivi surface that is provided to the embedded wayland compositor by a wayland client. -The example code creates a stream buffer that gets linked to a texture consumer. The renderer replaces the texture consumer -by the content of a wayland surface when the wayland surface is connected to the renderer's embedded compositor. The ivi surface id -is an integer to identify the wayland surface according to ivi application protocol. - -@snippet ramses-example-local-compositing/src/main.cpp Local Compositing Example - -## Data Buffers (vertices) -This example creates a scene with a shape. Some of the shape's vertices -are changed every frame and thus translated around the scene. - -The first part of the example shows the setup of the data buffer objects. -In contrast to ressources, data buffers get created via the scene -(and are owned by the scene). -@snippet ramses-example-data-buffers-vertices/src/main.cpp Data Buffer Example Setup -The second part of the example shows the application logic. A loop cycling -over some of the vertices and changing their positions. These positions -get applied to the data buffer by using \c setData. With \c setData you can -set either the whole data buffer or just do partial updates using the optional -offset parameter. -@snippet ramses-example-data-buffers-vertices/src/main.cpp Data Buffer Example Loop - -## Data Buffers (textures) -This example creates a scene with two quads. Each quad is showing the same -texture. The difference between both quads is, that they are are showing -different mipmap levels of the same texture. The texture's content within each -mipmap layer is changed every frame. - -The first part of the example shows the setup of the data buffer objects. -In contrast to client resources, data buffers get created via the scene -(and are owned by the scene). -@snippet ramses-example-data-buffers-texture/src/main.cpp Data Buffer Texture Example create buffer -To change the data in the two mipmap levels, two data arrays (\c regionDataLevel0 and -\c regionDataLevel1) have been created. The size of these arrays is the size of the area to be -changed (\c regionWidth and \c regionHeight). - -In the following example you see a for-loop that calculates new pixel data for the two arrays. -The array-data is then used with \c setData to update the data buffer. Other parameters in the -\c setData call are the target mipmap level, and the dimensions. - -Finally a \c scene->flush() is called to send the updated data to the renderer. -@snippet ramses-example-data-buffers-texture/src/main.cpp Data Buffer Texture Example Update Loop - -## Basic File/Asset Loading -This code loads an asset (a serialized ramses scene). -@snippet ramses-example-basic-file-loading/src/main.cpp Basic File Loading Example - -## Render Once Render Pass -This code shows the usage of a render pass that is rendered only once into a render target. -A heavy content that is mostly static can benefit from this feature where it is rendered only once -or with low frequency and the result in form of a render buffer is used every frame in the main scene. -@snippet ramses-example-renderonce/src/main.cpp Render Once Example Setup -@snippet ramses-example-renderonce/src/main.cpp Render Once Example Retrigger - -## Geometry Instancing -This code demonstrates usage of geometry instancing via ramses apis. -@snippet ramses-example-geometry-instancing/src/main.cpp Basic Geometry Instancing Example - -## Geometry Shaders -This code demonstrates usage of geometry shaders in ramses. -@snippet ramses-example-local-geometry-shaders/src/main.cpp Geometry Shaders Example - -## Interleaved vertex attributes -This code demonstrates usage of interleaved vertex attributes via ramses apis. -@snippet ramses-example-interleaved-vertex-buffers/src/main.cpp Interleaved Vertex Buffers Example - -## Multiple Displays -This code creates a renderer with two displays and shows how a scene can be mapped to a display. -@snippet ramses-example-local-displays/src/main.cpp Displays Example - -## Data Linking -This code creates two scenes, maps them to a display and shows how transformation data can be linked from one scene to the other. - -Client side configuration: -@snippet ramses-example-local-datalink/src/main.cpp Data Linking Example Client -Renderer side link creation: -@snippet ramses-example-local-datalink/src/main.cpp Data Linking Example Renderer - -## Viewport Linking -This code creates two scenes with content and a master scene. The master scene is only used to control position and size -of the other two scenes (in 2D) using data linking - it links viewport parameters to the other scenes' cameras. The master scene -has no knowledge of the other scenes except for their camera viewport data consumer IDs. - -Content side setup: -@snippet ramses-example-local-viewport-link/src/main.cpp Viewport Link Example Content -Master scene side setup: -@snippet ramses-example-local-viewport-link/src/main.cpp Viewport Link Example Master -Linking together: -@snippet ramses-example-local-viewport-link/src/main.cpp Viewport Link Example - -## Offscreen Buffer -This code creates a consumer scene with two quads next to each other and two texture consumers. Then a provider scene with a quad is created twice while one is rendered into an offscreen buffer and the other one into -a multisampled offscreen buffer. -The two offscreen buffers are then linked to the consumer scene so that they are used as textures on the two quads and the difference between multisampled and regular offscreen buffer becomes visible. -On top the offscreen buffers are periodically linked and unlinked from their consumers so the fallback Texture/RenderBuffer is shown. -@snippet ramses-example-local-offscreenbuffer/src/main.cpp Offscreen Buffer Example - -## Pick Handling -This code creates a scene with two triangles and two pickable objects exactly covering these two triangles. -The pickable objects use the scenes camera. This is needed for the intersection algorithm to unproject the pick ray in the same space as -the object in the scene, that should be picked. -When the user clicks on either of the two triangles in the renderer window the triangles change color. -@snippet ramses-example-local-pick-handling/src/main.cpp Pick Handling Example - -## Basic Text -This code creates a basic text using a text API. -@snippet ramses-example-text-basic/src/main.cpp Basic Text Example - -## Text with multiple languages -This code creates a text using a text API containing several languages. -@snippet ramses-example-text-languages/src/main.cpp Languages Text Example - -## Local rendering of scenes -This code demonstrates creating a scene and rendering it locally via ramses apis. -See example code in examples/ramses-example-local-client/src/main.cpp - -## Scene referencing -This code creates two scenes with content and a master scene. The master scene is only used to control position and size -of the other two scenes (in 2D) using data linking - it links viewport parameters to the other scenes' cameras. The master scene -has no knowledge of the other scenes except for their camera viewport data consumer IDs. The example also shows how a client can control -rendering states of other scenes without access to renderer API. - -Create scene references and get the to a rendered state: -@snippet ramses-example-local-scene-referencing/src/main.cpp Scene referencing setup -Link viewport via scene referencing: -@snippet ramses-example-local-scene-referencing/src/main.cpp Scene referencing data linking -*/ diff --git a/doc/old_ramses/general/50_ContentExpiration.dox b/doc/old_ramses/general/50_ContentExpiration.dox deleted file mode 100644 index 51f7e92e6..000000000 --- a/doc/old_ramses/general/50_ContentExpiration.dox +++ /dev/null @@ -1,75 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page ContentExpiration RAMSES Content Expiration and Time Limited Validity of Content - -# Latency -Typically, a rendering application has a loop which directly renders on a screen. -The latency in this case between the time where content is prepared until the time -the screen shows it is in the range of 1 or 2 frames. However, when showing content -with RAMSES, where multiple clients are commiting content possibly over slow network -and with performance constraints, the latency can be much larger. Other factors influencing -latency can be resource fetching and uploading, blocking due to system compositor. - -# Time limited validity of content -Especially in automotive, there are many cases where validity of content shown on screen -is strictly time limited (e.g. obstacles in parking assistant shown too late or too long -not corresponding to reality). Latency can be one of the causes of displaying outdated -(expired) content but there can be more reasons, for example client not providing -up-to-date content frequently enough due to low performance, faulty logic or general failure. - -# Expiration time stamps -RAMSES provides an API allowing the client as content provider to mark every modified state -of a scene with a 'expiration date' to inform the renderer that past this point in time that -content is not valid anymore. Remember that renderer might be remote and thus not controlled -by the client, therefore in the context of RAMSES framework this is one way communication -from client to renderer. - -# Renderer events -Renderer receives, processes and displays the content provided by client, therefore only -renderer can check the expiration time stamps. It is impossible to define a general rule -for reacting on expiration and therefore it is the responsibility of the application logic -running the renderer to decide what to do with an expired scene content. RAMSES provides -corresponding renderer events for that. First of all there will be an event when a scene -uses expiration timestamp for the first time, notifying the application logic on renderer -side that expiration monitoring is now active for that scene. An expiration event is then -generated when the scene has an expired content, another event will be generated if the scene -content recovers from expiration and is up-to-date again. Client can decide to stop monitoring -the scene for expiration by setting no expiration timestamp, this will again result in -an event on renderer side that the scene is not being monitored anymore. - -## Time stamps -When talking about time and delays, one has to be careful to how the time is measured -and how reliable the clock is. In a distributed system, there is also the factor of -synchronization of clocks. In order for the check mechanism to work, all system clocks -for all participants in the network have to be synchronized - -## API -As mentioned above there is API both on client side and renderer side. -Client sets an 'expiration time' (see documentation of \ref ramses::Scene::setExpirationTimestamp() for more details) -of a scene, this expiration time is commited with the next scene flush together with other changes made to it. -What it means is that the flushed state of scene will expire at the set point in time. - -The renderer has an asynchroneous event callback which reports when scene is enabled -for expiration monitoring (#ramses::IRendererSceneControlEventHandler::sceneExpirationMonitoringEnabled), -when the scene's expiration time is passed (#ramses::IRendererSceneControlEventHandler::sceneExpired), -when it catches up and recovers from expiration (#ramses::IRendererSceneControlEventHandler::sceneRecoveredFromExpiration) -and when scene disables monitoring by setting expiration timestamp to zero -(#ramses::IRendererSceneControlEventHandler::sceneExpirationMonitoringDisabled). - -## Example -A simple example how to use the latency/expiration API of RAMSES is to define -a fixed threshold (e.g. 0.5 second), and add this threshold to the expiration -timestamp assigned to each scene. Then, whenever a renderer reports that a -scene expired, the renderer control logic can temporarily hide the scene and -show a fallback scene, e.g. a loading wheel or just a black image. As soon as -the original scene recovered, it can be shown again. - -*/ diff --git a/doc/old_ramses/general/70_SceneReferencing.dox b/doc/old_ramses/general/70_SceneReferencing.dox deleted file mode 100644 index 164e23bae..000000000 --- a/doc/old_ramses/general/70_SceneReferencing.dox +++ /dev/null @@ -1,121 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page SceneReferencing Scene Referencing - -# The concept - -There are two main roles in the Ramses ecosystem: - - content provider (ramses::RamsesClient) - - create and offer content - - modify content based on application logic - - content consumer (ramses::RamsesRenderer + ramses::RendererSceneControl) - - accept/assign content from providers - - control content rendering state based on application logic - -If one application implements both of these roles, it has full control over the rendered result. If the provider and consumer are two different applications -then each takes care of its own part and thanks to Ramses a large number of use cases can be achieved without any additional communication channel. - -Now imagine a situation where there are 3 participants, each being a separate application: - - content provider A - - provides content not knowing where and how it ends up used (e.g. navigation map) - - content provider/consumer B - - accepts content from A, uses it inside its own content and offers the result as new content (e.g. navigation map extended with additional overlay elements) - - content consumer C - - accepts the single composited content from B and controls its rendering state - -The participant B represents a new role which combines the two basic roles of provider and consumer. The important aspect to realize here is that C does not -know anything about A and does not care about its content, it only takes content from B. Whether content from B nests other contents or not is irrelevant for C. -Using this content nesting approach allows a nice and clearly defined separation of responsibilities between various components. In theory it can also scale -to arbitrary number of nesting layers and arbitrary number of contents nested in each layer. - -# The concept vs Ramses - -Applying this concept on Ramses level reveals a problem - the content to be nested coming from lower layer carries a very limited set of information - -scene ID and maybe some extra metadata. Also there is typically just one ramses::RamsesRenderer for given content -and it is owned by a consumer which in case of nesting is in the top layer, so it is inaccessible from any other layer. - -# Scene Referencing - -To tackle the problem Ramses provides a scene referencing API which allows any scene to 'reference' another scene just by its scene ID, -this establishes a master scene and referenced scene relationship. It is important to realize that this does NOT expose any of the referenced scene's -Ramses objects (meshnodes, appearances, ...) to the master scene. Instead the API allows to change the referenced scenes rendering states similar -to the ramses::RendererSceneControl API, i.e. rendering state, render order, data linking etc., see the ramses::Scene and ramses::SceneReference API docs for details. -Even though limited, this already gives a number of possibilities how to composite/nest scenes together. - -Overview of the example above, now with scene referencing: - - content provider A - - creates and publishes sceneA - - content provider/consumer B - - creates and maintains its own sceneB - - creates scene reference for sceneA in sceneB (sceneB becomes master of sceneA) - - maintains sceneA's states using scene referencing API - - publishes sceneB - - content consumer C - - maintains sceneB's states using scene control API - -Consumer C knows nothing about provider A on application level which is one of the key goals of the initial concept. However down there, -in consumer C's renderer, all the data is available to render sceneA as part of its master sceneB. -Scene referencing is essentially a remote control of renderering states limited to the scene being referenced. - -In order to make all this technically possible there are several rules, guarantees and limitations: - - a scene can be referenced by one master only at a time - - RamsesRenderer guarantees that referenced scene will never have higher rendering state than its master (higher state can however be requested from API and will be just kept back) - - RamsesRenderer guarantees to show/hide master scene with all its references at once (assuming conditions for each scene involved to be ready/showable are met) - - referenced scene inherits display mapping and display buffer assignment from its master - - referenced scene's render order is relative to its master scene's render order - -Check out the scene referencing example in Ramses SDK to get the feeling of how to use the API for a basic use case. - -# Scene Referencing events - -The mechanism of events dispatching is used in several Ramses APIs and it is not different with scene referencing. With scene referencing however we deal -with two remote participants (referenced scene's provider and consumer with renderer), both contributing to the overall state. This makes it even -more important to use the events carefully as means of synchronization and correct sequence of commands. - -One event in particular can be helpful to achieve well defined behavior - a versioned flush applied event (ramses::IClientEventHandler::sceneReferenceFlushed). -As stated above master scene has no access to referenced scene's internals, it can only request state changes for the scene as a whole on renderer's side. -But Ramses scene can be flushed with a version tag, the said event is emitted whenever a referenced scene is flushed with version tag and that flush -is fully applied to the scene on renderer's side. The version tag itself is a user defined value, so there can be an agreement how to interprete the value -by the application logic (see ramses::Scene::flush). - -# Understanding READY state - -The READY state is probably the most important state and should always be used as synchronization point whenever there is need to show a scene/content -'at once' (with minimal delay), Ramses guarantees to show any given READY scene in the very next frame after the show request. This is essential if a collection -of scenes/contents need to appear together, which is probably the case whenever scene referencing is used. Ramses scene in state READY (ramses::RendererSceneControl::setSceneState) -means that it is mapped to display, assigned to display buffer, its resources are uploaded and it is ready to be shown within a single frame. - -Typical example of a desired sequence of state requests/events would then look like this: - - -
Provider Consumer/Provider (master scene) Consumer with renderer -
prepare *sceneA* prepare *sceneB* set up renderer -
publish *sceneA* publish *sceneB* -
create *sceneA* reference -
-
update *sceneA* (optionally flush with version tag) -
(optionally wait for version tag) -
request *sceneA* reference **READY** -
... (*sceneA* gets uploaded by Ramses internal logic) -
request *sceneA* reference **RENDERED** (*see note below*) -
-
set *sceneB* to **RENDERED** -
(both *sceneA* and *sceneB* are shown together) -
- -It is important to prepare and set all the content you are nesting to a state which is desired to be rendered. Good practice is to synchronize using the scene version tag, -that way the consumer can make sure that the scene it is consuming is in the correct state before requesting it to be READY/RENDERED. - -Note regarding requesting the scene reference RENDERED: remember that a referenced scene's rendering state on renderer's side will be kept back to never be higher -than master scene's state so it is safe to request RENDERED right away, it can even replace the READY request (state change events will anyway be reported one by one, -so there will be event that scene reference is READY even when requested RENDERED right away). - -*/ diff --git a/doc/old_ramses/tools/00_MainPage.dox b/doc/old_ramses/tools/00_MainPage.dox deleted file mode 100644 index a1c8bf962..000000000 --- a/doc/old_ramses/tools/00_MainPage.dox +++ /dev/null @@ -1,43 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** -@page ToolsAndDebugging Profiling, debugging, and tools - -Ramses provides logs and various tools to help find issues in your application. -In the sections below you will find hints where to find those and how to use them. - -# Debugging your scene - -The easiest way to find problems in your content/scene is to use the validation layer of the -Ramses client. The validation is an optional layer of Ramses which shows hints of possible -undefined behavior, performance problems, or missing data which can cause your content to render -not as expected. This is similar to how Vulkan works - you can enable the validation to find issues, -and disable it for production to save resources. - -Each ramses::StatusObject derived class has a validate() method. Call it to find issues in that object. -You can do this for an entire scene by calling validate() on a ramses::Scene, ramses::RamsesRenderer or any -other object. Validation works recursively, i.e. a RamsesClient will validate all its scenes, a ramses::Scene will -validate all its scene objects, and so on. - -# Profiling and performance - -For a detailed overview of the various ways to profile your content and optimize performance, refer to the @ref Profiling -section. - -# Scene viewer - -Ramses scenes can be stored to a file @ref ramses::Scene::saveToFile(). -These scene files can be viewed and inspected by the @ref SceneViewer. - -# Logging - -Ramses uses DLT to publish logs. See @ref DltIntroduction for an overview of DLT in general, and @ref UsingDLT for -details on usage of DLT in the Ramses context. - - */ diff --git a/doc/old_ramses/tools/dlt-logging/10_GeneralIntroduction.dox b/doc/old_ramses/tools/dlt-logging/10_GeneralIntroduction.dox deleted file mode 100644 index 75f803a79..000000000 --- a/doc/old_ramses/tools/dlt-logging/10_GeneralIntroduction.dox +++ /dev/null @@ -1,60 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page DltIntroduction General introduction to Diagnostic Log and Trace (DLT) - -DLT is an open source trace and logging framework developed within the COVESA alliance. -It consists of a daemon connected to user applications. A tool called "dlt viewer" is used -to receive, display and sort the incoming messages. It is possible to control the logging of an -application using the DLT API, e.g. limit or expand logging and send custom text commands. - -# dlt-daemon - -dlt-daemon is a system daemon that gathers log messages from connected applications. It is possible -to build and install the dlt-daemon from the following git repository: - -\verbatim -https://github.com/COVESA/dlt-daemon.git -\endverbatim - -# dlt_viewer - -dlt_viewer is a Qt-based application to show and filter log messages collected by dlt-daemon. dlt_viewer -is executed on a host machine and provides a graphical user interface with table-views of the incoming -messages. dlt_viewer is able to handle connections to multiple target systems. -The sources are available in the following git repository: - -\verbatim -https://github.com/COVESA/dlt-viewer.git -\endverbatim - -The following screenshots show the dialogs to establish a connection to an ECU with a running dlt-daemon: - -@image html dltviewer_connect.png "Create a new ECU, general settings" - -@image html dltviewer_connect_tcp.png "Create a new ECU, TCP configuration" - -After successfully connecting to the system where RAMSES is executed dlt_viewer will show applications and -contexts that have been registered at dlt-daemon. The following window is located in the top left of dlt_viewer. - -@image html dltviewer_contexts.png "List of applications and their contexts" - -dlt_viewer arranges incoming messages in a table that can be filtered. Without any filters applied applied all -messages from all contexts are sorted by their timestamp. The log table window is located at the right side in -dlt_viewer. The following screenshot shows the output of some dlt log messages from a RAMSES renderer. - -@image html dltviewer_messages.png "List of messages" - -The messages can be inspected using plugins. The 'Dlt Viewer Plugin' for example shows the message in -different formats (binary, text, mixed). - -@image html dltviewer_plugin.png "Dlt Viewer Plugin" - -*/ diff --git a/doc/old_ramses/tools/dlt-logging/20_UsingDLT.dox b/doc/old_ramses/tools/dlt-logging/20_UsingDLT.dox deleted file mode 100644 index 79af0f8b4..000000000 --- a/doc/old_ramses/tools/dlt-logging/20_UsingDLT.dox +++ /dev/null @@ -1,85 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** - -@page UsingDLT Using DLT with RAMSES - -# What is being logged by RAMSES and where - -RAMSES has a lot of logging information, but most of it is hidden (log level debug or lower) unless explicitly enabled -in order to avoid spam. Two of the things which are always logged are: -- The RAMSES version and some system information the first time a ramses application is instantiated -- Changes to scene states (new scene published, existing scene shown/hidden, etc.) - -Other logs have to be enabled explicitly by turning up the log level of the corresponding log contexts within the application -which is being debugged. - -# Application ID - -DLT allows each application to have an application ID. Since RAMSES is not a standalone application, it -doesn't have its own application ID. It is recommended that every application using RAMSES sets a unique application ID via RamsesFramework::setDLTApplicationID(). -The application ID (visible in DltViewer for every log message) can be used to easily identify which RAMSES instance was the source of that log message. -Be watchful when filtering logs to filter for the correct application ID - this is possible by either enabling the log contexts for that specific application -ID only and disabling the rest, or by specifying it in the log filters. - -# User-related log contexts - -RAPI
-Contains all "non-const" calls to RAMSES Client API methods, such as Scene::createMeshNode(). These log -messages are logged with trace log level for performance reasons. To enable it, the context log level must -be set to "verbose" from the DLT Viewer. - -RAPR
-Same as RAPI, but shows the calls to the RAMSES Renderer API, such as RamsesRenderer::startThread() - -RMSH
-Used to pass DLT injections to RAMSES over the ramses shell (RAMSH) - see below how to do that - -RRND
-Shows state changes of scenes on the renderer. This includes availability and subscription status -as well as whether the scene is mapped (i.e. on the GPU) and shown (being rendered to a buffer, not necessarily visible) - -# Other, less interesting log contexts -RFRA
-Internal logs from the ramses framework. - -RCLI
-Internal logs from the ramses client. - -RCOM
-Logs from the communication/distribution subsystem. - -RPRO
-Profiling logs. - -RSMT
-Used for automated smoke tests. - -# DLT injections - -DLT provides a way to send messages to an application, called "injections". Injections are -typically used for debugging purposes such as enabling specific features temporarily or -controlling the flow of the application. - -In order to send an injection to RAMSES: -- right click the Ramsh context (RMSH) -- select 'Send injection...' -- set service id to 5000 (RAMSES utilizes only one service with a fixed port) -- enter the Ramsh command as plain text in the 'Data' field - -@image html dltviewer_context_inject.png "DLT injection for a given context" - -@image html dltviewer_injection.png "DLT injection service id + payload" - -The simplest command which prints all available commands is 'help'. Useful commands are: -- validate: checks the validity and prints the state of a scene of a ramses client -- rinfo: prints the internal state of the RAMSES renderer -- dumpSceneToFile: saves a scene to file(s) and sends the file optionally over DLT - -*/ diff --git a/doc/old_ramses/tools/dlt-logging/images/dltviewer_connect.png b/doc/old_ramses/tools/dlt-logging/images/dltviewer_connect.png deleted file mode 100644 index a62c9ca8460babae8cec962a6e5db813e6a5faea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43415 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 IpO^&wFFnF4ZvX%Q diff --git a/doc/old_ramses/tools/dlt-logging/images/dltviewer_context_inject.png b/doc/old_ramses/tools/dlt-logging/images/dltviewer_context_inject.png deleted file mode 100644 index 7d56bd400ff60e142aef70289bdf2e08fb3df833..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54513 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 diff --git a/doc/old_ramses/tools/dlt-logging/images/dltviewer_injection.png b/doc/old_ramses/tools/dlt-logging/images/dltviewer_injection.png deleted file mode 100644 index f872b567167124a5686a2090775f9ba0f92c05a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23647 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

^@)V_j#$H@eeXOpJwj1$(yk6jv_ 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 diff --git a/doc/old_ramses/tools/dlt-logging/images/dltviewer_messages.png b/doc/old_ramses/tools/dlt-logging/images/dltviewer_messages.png deleted file mode 100644 index a8c2c9441f4b376d214689286696e98c39218c03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115852 zcmce;2|Si>yEghrp&~2yoX2sTXVCFus!VG(uc1&V zOzLXXlN1WAC51w>j)4wenX13D6aUa%(om&RmdT$71*wtv%4!?6^L7-C=@=5I#uzs)Aylx$J2M3re}(1_nqFrqPUXUPebLLuPYATle_C;y1~Vj7VaFY zuE#!+@D={bsZPyh-5bcc;n9WTA`dpu=o}Jx;L9R>nBgX0_0UXMzP!dm#+wE;Zt@nn zpHoLpO@Hf->t(c#+R)gXqmwy#Xy3*&(kD(?aIvxux8_AV9oVvoi>on|N3QSkkt0W{ z+u9Q9-oNkfYe=3HrBbQ4-@MtI`}XZgH#fJ`rtCLw8h%yWQa^Pn^4hg)diy;ldXAnv zdGeu7wsk{Z1pD&Z>+~;PyqKKpObADXNK~&wwCmL)r%!K)y?_6dwsy1X z&h{!jkJhd2;R32=L(Lh+mX@0LrLUTVDmeZ2bai!AJ9;!ELBXSKB%!56_f=MwkbI(6 zMMcHc&tmOo#avz`R9uq46-B&!DLVY^b#LU5SZHXd{rqIVbYWp`ZmiJJd(@Pal&*n+ zBk$k8Pox$pcq|>*DJF^D*Kj)_gtE;OI85(X?Qc^muug^?*l9m>5>(*NGUv2F` zkHta#x`UfHZx)x7^bQYabzc}$?Qx<~SK56jEnN}Dt3XFN;5y54;>3yS4b}ett2J-G`o8#nMuQV(_axXm-3-tf9Gi_QUv>uU|_)d|3DW`*#~h#{le&uP=1y zDSdGEIB=$rQL0YC*hzA9p(7x(^MxXnLBsy z{grj?{HYmg>7Ac&*#slMRc+lu}5Y%e0zIcSY-F^_SyNh z9HO3O9X~%k*1#@2++OIW5OnRr`SXf=EDzqinToXBu0DO3EsaadWrd@o;{rDN#kYu=meTUAS;pP; zbMxlr=AU)3Qdb2t@goBDwdEBRCAdLsjio2M=I|X&xBR2~+x=*0>G=5hR~$Qbth%nQ zT&!rp`{}7vZ>;0MmuC#CS1X=8$tEc&Def|D`T6TtrDMm|B_t%^>t1nj+-G0v1~nwB zmetoYQyx5j9xU%RZ-2{G*`KkbN#BEsnVI>~qep!E_itKW9QB}~Bzi3EU%7(jl97?^ z@1KhJ)&s3X`H!DJm$&5Foj7&M$JckIZ$Lmm=A^rE-6 zNK~^}-e1wY-ltNJmEgSKgS@t)BF%am7iVXs9UDrUnmEe*nUpVG`sEU>CBk0z<;%to zA3kWl>hf#3dGltBgb`iln>QW({Xtn-`-E=m<=AX}_39NL3j@oB4J#>TSFV)i-7`DnNG^d;hdkJ7Z(>lbnzk&9x0BcfrZ6dIXSrv z92~TiPmPTduCq3l-!oU+_0)N#qzILkmd2RAJpY=`!NEaXT3YqOh40%hZ;*Q>#?#Q? zw1)EX<;&xzPANM(%OYcZ{_^F7zJA!(uV)wjv@iST=I%$%$hh)lmr%n=ZEXfADXEzI z_my#I3JbSTeRrNp;ggf&!b7*QwJm8YaM`r!;lqapSFWtt5*APsxjp&i%XO6F7cQ)! zr>ED}(J5R80o89PO+OSrDff)p}hIwhvqCR zE|rkY6|DRbd;{j$;m0M zs95#*@#Bk8PoF$tz|H#|;_Lf7ThuZm!@chGnKQABZmNsp%F|8RN!SmFeq~;?^fm*Z zcP>meYW<#=P`0oTM)DL#fWS2y&W(2?()&h7H~rx?c>L&gR21F0bLULXB6R>0N!AzoQqn=?f-b_IoOWoCs1&+2M5q_>Be znH!Khj1$c9&aX}De<5EntXpTYBF2M1qOuXec*l+%B$mq9^)SBF&Gjzvp%*xCfE(dg z1yNyqV&a6hcC}y(c9QL6-wE9u8yY$~I^>{L0cDE1y4;#akNzF;Qd5n3>S8t1UtO+c z@9OE{mzL&8Rt>9scUPE(f^)2BX13!)qCyyn2(Gi~jpH)1vcI0C6_x_tiJN`oLt@X0 z|FF0)QJ>?!C`*!!$wEd}mWs8tsQcp7xwwNa>>dka0p&9q5xL2-k>!H}Lw-z8pFS-a zY0b0ktYljFUB=9Et0Ko^Sw8D(dnmcl$^J%?pS-=j#SMxnA{R0l00D1DN2?;{$nmBo zUlY~MVUduK(6ss0(?iF@!xJ>z63%-{SC=1%f8AmUS?@& zY0dllUNkzm>lCF+wOh{q?%*vrXlOXsN4syu$MD*0!!kO=a|3LWItgUB!-o&Y$vLh= zjv3~r^EyO9>WaB{4+%*8yy(S?7ZI6BSVvyYvFTXhSrlbkpnc@*i4!!34_AnCJVKjXxi^cO$hL}=ay^f{=z<1?VbP- z>u*eLNYhs^Iqp$X8N!v}Jawh|-McKiS>V5bjlyRQkVAOnqsuyQ0e`7>-wF$>%5HbR zm8z8}`Qct@=(=Nx@=R;juI&PHC@u9qzs1DbI&^Z<{!wbG^0{-_wUV~Ex_HbFA3UIJ z+qUg$%S<4H<7nIEV9S3$DIGoU?*l+yGGTv{nfc(+BVQmt-P~)d04OvIYHMoT?p#iP z^9Fm!Da{eQBeg#a_cEQ$6*G%$pF~=}$di|wOGEKr$FoK>`W6D*ax&A?M`Nc| zROs*@RRl%}<7yTY|9rfto}S+C>1h=^JE_Jc4l%v*;7w9LdwTp!OO+6dGhLc)Ni1|z zQ7Tk^etHV%&TDAM68Vh#QU)hBBR$d$9@(=cH%1p8cXxL@75}$yWlPJ-BpgR{d*ZN& zK=A$ugvtnNR#sNnpE9nq;$$E3^77&x9wa3x{`w;qasFidV!Ua6!d6+?w~exmpFVm2 z_17+(?guYlh9Z3F7P<38MMd@f$QA>zh%riXu|?d@V^Wesj@jvSx+^QRMEsgn$wo9OLCWMm1VGOSrsij&ng zpu-vTPSY1i5O62y>C@FSBdvpbcHE7QuJWXzwOL+r!$B|p{P|99YO?#cSUq=K<#eQB}n@>U_e3QnhlPB8- z@^U`JO_l{P8mh`1dW7Yy`WhE#{`i;Pf? zBz8L@V*ChS5{{>w z<@@;Ywk-VwgqP1(**!(JAjVs|36hWO)FUPOPolNKXm2FEsbT*b=_5DQC$lf|b8X)IBP$#w z=g~NsvAeB($T(^jFUJ4%*IzM4S_ucXnwpxTq!KVPGMXQFmiE2%wabk4$rC5G>g((C z^CQ>Em|IvhzOiUBYfd*jed2^7R?U9wSH;X*jYmaA3S&+O)-W=U4&&fS0uV6G5yk6rrSGELXhgwOQ(Wx1 zXYXF~#rdhaTomPOLDW4qKM%cn{W=*Ho@6ifLG}0V_Yi+kj~ARMy7MS{)TOBig*|@q z(X(e7va+&*8`kWUl8Our#Eq-*vD_{wI2c6be^*;;ku4RQks)z3;XrJT?g;(2i?#qy z_-XaShy6B5U2T1PI5svGdrK9;=~15JD8aBb;GW7aNg%hfM%O+zEc3?$c=Yk(aen?Q zmX?n^Jt?>}0UaHk3;9mAc^Q)zYctFng|6Aym^Cq@oT(`<_nmc>N5;I6ZOs!@ap^m% z`bK6yrLBbK3Je6sGVlACv|wx!(>gjDyLRoepBgyxsi`Ry82aPvSf}}q zEGynGqQUXsqC>k}TAeOjym%BPoS?9`f8gX|HHaEP1JAv$o1oZ@k8SP3pD@}xL|DiBkJl~`5)Fs zi>(i<5Q zO&UBfI0%d%GBh+~){{@2#?RYU+R9QRUaO|JdZN-ZI(fqSla-?_6PH0AN3^XE4eJa+Lao3nVc0_a>UYhGJhdnw5BM-~(4 zy1v%D83ku98+P`n>{OJ$sGFa>cu|pK+o_i~G4(^-^aJ;X4I2(2o#Z(TUlNY-^Lwn* z@8>xCVi!ApCG4(P;p_^1*RfSPthkCazU|usP>fywF)=;#ZKAU2TCQ%-{pILQd#`-f zNn5E6R$$_NU#*P{H&9~Xbg!Q5B~XB~^Mh&nE}|$eMn*;s&bzZmEt?1O?G!RHZN`=U z{n56*bxz0lNA}gxEK}@^i5HH|BOhj7r>EO9{>j*kUHXrYTGchZm0$SpdAw1m+t2)< z;;E11=7#%aqLIdi|M}4`D%)aT{<~lQekk{`Vb?}huB3yL8|D}5N7;6=XD7XRylPXx zsh@iqcnbbZdfmCR=kMpjtFtbP`?+hPy@COGr8%4FSX`0wk>Z&vS`TD;-$>) z2$3W|{Y=$zf3ti2yDG30rQ_z`+O1bNYkxmCQtR=0JcIn{?z^&|(0}s`4qcc3ET)RJ zSAB|ZQ{4{)puJ>ndpLS@)T}uyD2N7#9%q(i)244DEyxBxfBvN4Ub*EQhT>N?7rM!T znk3*usy3=I^UC`o`lCK>`1*Ca#vzMT zm>_RKS{User6qM-^N$}!bukjNBYC53Pr|ypyGtr7=~j4A&ZX&H4h1pIO+!P&!mtux z=t}R(!WVkeg4C6OUIgL*JT3|TumbxEPR&)=^h;G0y{;&SwY7C(@Wj2u#4yB1g3l6- z<*u9P%U2Xm%3B@+wT8e#f9_n9wFUi+8#f3o^kT%q+}z8@hc-pi(4#rd&#$}Dducgw z9c@1T4WBRViV2!L4t04gad>G<*Ep2)&Ex}w-%ZBJirtKrzRY4xgA zt2EsLTMj3TKFf_wo7ini0bRNmV;kHf;BumTsz-M!cKJeZl(g485IYIeYpSd$jHXa z1w-oKL=a~)EWb(ak(Ty|pdACzZfI^kSJ|WwZh(6oJ$ObX0I~V$;Xmqk8sEKJgKY(r zgzcdIN-Tm@Nr(76)dWjXY`uN-V_ zt>@sN2hGH|<-lz6U&iL<{WD9Q;k0y&haiSv6WorC4bpd?TLmT+b!-W+6Du2=_oGLG z0KWzZiU_WLs2&a+JczHa*uQ`ObKP7vR2Kd~MHCXN-P&xKM%MZ>Zt_kmTH1`Asiv;3 z2qd=z=5hr^etE%;VdaYQ%F16qOMJ+;66M_7POG)7OQJ#(JhDsz2c z15!STVxUuySj1_vQ&YDi!@IhXsAw5L$CV9lP7_jq)ZO! z=nzh}uFedPgk4lO@cDBQlj-%8d-v{%Uu`?U;HT95?Hf+>>a zA;x`dY6|f3qQMm+L(^^vqeqsMZ70Bjo!e13|8#S?IIq$RhpyKpkgnNLhC3gc$*I^%A~krpJn@$TB#*e8L33_Y`84p~@POX@x^#RF7bD)C-5ny;HJ^-nx*WiEnY z@$irAtyskJa_XJvXjFiyI)l&ulee{JAG^%cX(My|%$dtkG7gVCTAxNAR0jbALMnik ze%(({IkWw#i9dUL1HfJQ`udWq2Q^lTOG(1(h>4}mC=?%2--_r5h%_9vS5`)*xV(HN z*8eb0?Y;Z=V+|U?*8(Mhj28y<&j5B53Q9p44KNL$3bvsCZuh`N!{P65J~lLf$S6hG zZU{*b>wy(D81JfSE^v_sogR>$E^hKP7V@k6YzIA6MTK8noCS5p-vCMzlonw=!DlPm z*oa@TuvpG8vP5i6(Mnv0jY%m+u^OI`unsywhEbIgigvJAR45_92Hps&keoUZ=0W=$ za(9;p$HvOb%M41fE?$m9Kqa^sF-yYg`=0Xha{FAd>xY@^>gvSpdNesVZ8H4w*BR`p zKEcBAw10R3zRnMTf+C?|zg^FRw{JJ!-)E(C{5T6T7?|w`&z{|qGW~E!UA=#!3poI- zd5+LWxn?^8cu~@y(9yZ|-~k`t3?m{G7;MXEr=`3PxB=CF-<}^lk9wE=KPOOYcU-@I zJ?Y`Y6{v@uTwFR!{nk>JE>xZYM?^tF^nCd6QuNxBy1K#0?KZi(lJb16=G}zu-Ro%@k}BJqUs@E@|rruUw1TmEVR0Ra<}6B-(8;dyxS{CW9Z;d6i+3=}9|0da9H zs^{qt(SWPL(m2k)07Bgg1+}EZrntlACq%i0g$3)?y8tCJESf|?+Is~)NKFlBcmKl# zy@6c!fddE9Z%mjla?7N*K5tu?n=phhpjY7h=9v)y9;NtWl5$D+HCxo)9z&de-hRu8 z4x5{urBJ|S(qTV>^m+hM8`<;s__%j=wlvPek2gg~=?G<&!J8&3+iw@X6uW+gd5gR& z1AYvLbVe;cH!rUf^(*14ussnE#2tp3kB$D%TxXsGldfp=rc_*}?3dV=g^7NsL#p_^ zAH_UZ4x0wsp$aHTWY(FHi$cj&y%Sr7g>T{V6%QX?1+syV@HhMRJi&uM3xv7UW#!SCkHH95JtTX*fc`Hd6FnO^3`xS$bBJqqB@t$&(_ z42nHznsaOg3c>fB`*8H$p4G@J+OLe&^E!cGb3l|^NJrn89wNFGXKKdw?unV1DugxL z{!iN|o~Ui{c7_%fw@xIhbl}zNu03>Yx=hq*xEr7;6QcI|<~!grjPOQ;Zp2oBX!)*1 zfGsFy^sC<2q7RefMv7Wm%z*P1VEt$)K==e<1+of=J5QQDN=aEkk(82(?P{nR7%Hk^ zI{?s(ge+-fj+e@Is|EcY@PErk?v~J|DHd6LmX@Y^;sopDkfSzQsPh&#{=EmBr2>s_O~#5gZcIfgeF=By!^N(jo+` z{*y(w?X}KL{zRrDf)J68Vq!RjO1{Sv<{4lYrOrNZ8=iYDzUDXhZxf3it|&r4IC1)P zdGjQPn^wdJ`>90~sv^9>gB3*)J4V=dAH03^@Ry^XKYv%j6aNgY@KZy>R%K;nVidvw z2?-1Ym^TA*Gd3|%;SUe|>)ErQz$clRcff_5*U^at_Xbhu zMS5DYtbPzmO6XXe+SJt4q@<#v;-i-@kAY;X%Ycf1`}J!ns8!)W0&Y`Z&JB-zL->c< zcpGIJD=XnQKr;7#e`B!+$mp*pPx!z{ymcDmzpip&LF_XRLdI=1!G~aMBjV$Ecn&zF z>m&%P7b5!-UJ~>Ucq2k#ojG&YeRpF}BL7 z%a^x-wqj*%kZyNX9v>e!wy@B^DuGvta{Nm{eH`SVA5;b^loBe{ere$bRzC@Kek1TK zA%>__M61I3esS{X4l%Lq;cC>kZ{HHq7hD;3e^B5KSQ6mc`1a#R zB$gM5kDuRp_V?EPa&nRXK@nPq4d|#2wd6(uy5fn68}+=<$=(5K5!Yqb{qC+k0?}pw zBJv`*KnSY8uH}-)h-b24^X4N|D(cA%EG&oMULb3N0Awv4RcNt)pVfCNxVzzb5)or# zVptOEw~Iz=phy?U(AEU!b9p`qG$Sf7Hr5kJ-%LERcZ9SOG;#6pGwpz~0YE@l%a?iu zM_?ha@2Y0qtl)kCWb})w%GQ@3W8M1I3j6xZUcGs<4VWJ^WAgX!7x?+dr>9d;FMtd* zHZxQEGd*nd{Tfo^9+NswJmE*H*9#q2S6>bGI}*$-NI`38h)|!tFoaNt=HXv}}F55$Km!cVy%fEp|A2lw&sU`}&F<&#o$t6bO~KI{EU@ zg+N*<%@8mLi=c9~U+<^Q(!8ZtS;qqo9XG_JWh&x@|C1*>CP!MiK=2p{`*s&ZfI0zw zbaHksg-8RS{(MV1`LJKT&%g*Yl6aldSsPmyiqTymCBPo^ds@_#a`Rv95DP-{e zeaZPAe;QaPmzV8*?>y4~VnSH7e|+>S{!RF$ej;q2a|~2ZujVe!wY56^bqapS1|Wvw ztWIY!ze=}{1rvmzkD?t^0Uct{@COiCOgJ-mlVSkgHg!*A%`z}oAfIL@>J{V)^hDT= zY9h9M{Pao5+InRBi5euCVn8v&%a>Pqcr0r<*hT*{8P@whl3_8O|3aR*OZg%yz-@Mj zkA--~>c)EO69xGADBv^@;)hq<%>GiF;2Q{Hj*jTl@Gm49=RpTz1)*@Yd&lhTtY>X^ z>HGJ?k+$48fTZwk!HJHvOx0>A%-;#KNd>qG=W;qk69Ys+6lcR(4sa=W4DzLdw872B zNOpH#c=Z-&bju=7Vd(sBRzut&3Rc?a+QR?^b}#?>YYm7XPB{k=P)uV;na8Xu2b~sX zB^a5QSUEVFUR>U_d$-@KSCZE4g{{MqMXNzZ*f==2_x2*^D+#gzDTG}&&~_Fw$V0A0 zJxY9_w{Nq2Zfc^XfP3qJc#8jSU}yiOzdX-j7ly6mEpAwggYfp!D9=O(kORV;p+bboSSUX z|3{pgxr%axE4CjgKFfpFgREdoIu2*)nmBp4%|3KYr6^)(D1T-~1CTp`zkgTpdKAOG z3N%632nYpWBib&A0%~w8xO3wu5!#hgRS&2MF%Kca`kXa?DGHeIm>9A!B6|5T`=g_x z{NYFwfIsc--MhgOqc=gqf?h*%)`sN?uv?+V%K_OUJu}nwnIJPLsK*-7x9{D%iR#&% ztvmD8EAK;mhag&}nzL+c=1|YKJ}|r*8ze98(6{^>QsABV$L{^N%v(KIm^nGfvQU z&2KEu<+(#7-G|ur3sxhPGNuuR5tfyM@XJ)UJ7WZ+q_L%$~{315xFoPMqQ2r=#f?ocQw zgWyI=)#>7rG-fDVnzw*2gAa;&O-;@0sK@eJ$}9p0T(B%$T(_Wb^5a`>^OMB;*z+O& z7Wijktb=NTOOKJfyas%+fw_4QB2!m)H`M!;V`F0n`P)e?XJFt7UtXMy?Fx{mq$~l) zo%G^G2sryq{NW5JAYvflVUrSeCUUzLp;<^(h(ix%YWeFmb2Bsej7f{wXAY_>6 zDuA5F`Wgg4@4_BSi_8#ZMvKiC4E4OgHvTJSvRJJS z@L(4QqyV-`p#X}zFF1id&(>K@_APkvf`|0bzlqBO5(T`Xeu)2+Vi;RVrUR=ADg)G+ z=!_0hiR;9B>9nbXa9rI~Sem7U-6zMccis9rjaQo@aARJ0C{Qnyl$8~!vrB(QiG9ej zy^sghHLPvmNBitsZr-}(gE)yhZy9lNtZDi9kpjmkp$=huB4m-YJA_`G;is+7%^>zp zPEC<@0A7fRjV-|zK;^mO?uod6Wj&)8 z18?0@2IIcZvYF76DP_(#ZUCmzfLp_&&`poFqdNj7!+)}|zL(pWl04%f5_jMR8+bbS zc3WIrOkq<5iS7q?Bk?UG+^ZcqQkrSr7!w?Z3J;XonBsuo{{68v??zj4U$^FV*G5Z8 zogi}680w>~{QTarPZ#A-CI1}_FAJTDaD@U4zmp<}piW)l+rpM7=EX~wh>0O7Il1x0 z^|KI>5c8GzSnR)7>3=1LL#%_4a(npF^=!TA4LBFXfDB8FC&j$O%T+}m1KZut~Fy34aFwr zu?ff!!VW_641(l^lHyWFso(IcMu5T!z&lui>sE>{Qw2j(aa2k6g|ES;r*0E55|S~F)=ZV4%=GW>!epbnUQ9I)gcgPQvKym<674nv%e z9zPxfCuIct289CIigZ1IK}l=&LC_|~Tw;ZW8J7ZA8)~nWt5$UYj1o6oQj%d_`9%A5 z7`6|R08HK-7A~@V04x!KV9W12d4tpmKm>%pLM8>Qy^^Z0@3A-~EOi_aO~!e02eG?? zBr-xW(^UG1jh~yFD}#a`rY+)Zar|o(%V~%(uUSO#MA-)-l=u-r6hkAvnz~P!4fzac zwYt8(3NAR?g;`sX^RHIIs42d0p9*n=gFl2LFGwmCw27LUS}{^tEY;Pq_5k!o+)+|S z1}maKj(tBHEFiZKlq9nbN=Rf}ybmAIN)T=2$stoibo0lff;grAxi?hC<|l;+SA@og zj}8W#5H;<$Thgr{K!DI_&c9(N_xLo}98~<+JdGa=AxD$E4UB~Z{W|z0*lrY^BsPMD zgqN4{GwrhO-wirQQ$Q(&rwnDCm#fI8Nq3#Q2H{@(+q)TL31xfxJ)m^99CR)>j@R3= z@(*t4Lv;bSZ+3ns^Wt^?f2DuZj87xmGo3&G&0!s=-^KJx6>H#ES9v&ww;nh#_p7sW zY&g697tT}gYw*5_hM?U>kH z^S4ti`8U=hVb|3?gx?eEas2%GAYzULili7=Td&){KR4_ZV^&rc#7#Vwk+IK5nhDhZw{GDKZ7Q)E=hGixxH+Pfp+2@|Pt$kP9f)rP`2$_xFf z4+=n@tb?7Mk>oF2WK){{vMSd}_0Ffrv0u~l!-x=5eS2#i;|(Yuh!H=BheKc-O<5Gv z%MXN7SN#4x6Ywl_c(a+8MfKdo9u9jUqKzNk4$=teK7b*00rEWdGzj{a^D`391^$^E zb}L|LUIJRw1W$@u*eSUjF`pe)Gm;Sz!r<@x31$qrqE4o}qod*Jhn)Y4Ocsqct+=_7 z=qB=ihNSR>QsSA08PJJDnxSI$+X;~{2vJUaIQGt+mFV9gQaK{^6A0q3EJ6IEi=hG( zH9A{rxZ5+$*8^qQ^nT!hQ$lLBx;oCudLd0ugh%I@5het}%6gsGCQJxq$4{OlO$LZC zM_iBD+Df2FhP06J^41FqIV+jK%uK3$#CE^DJUI}VVq?fMHz3Uz0k#rGnQk3-_2lc& zNpx@2#dtyAD}kpWQs^kLp*9ElKDMCwG1Kl51}&M0!9*}{h^g;YK6{oO{zz|Z_K5~l zkll?PkPm{uMpk;BJ9EY>@DP=R2T<~5E7ucQ2%;)L2m0XjY@6p?mxSpCuiH;9an$yr zl*n)#)x%@L>u3GZev?{*=#F;EaI`KU6&`T<{j)%N5|m9k8fkDQ$y0}ip{|m`yybv_ zqy`Ed8JB61$T!Zs_jwqBnGyEN(PcLEE>}HLi0JAXQ6lD!Eub|J|1D+|$7{cpL-VUN zA5xz~_nGaVX)E2g_CdDPe`2!2L$ENAqMay()ecJt-?-m+#@4Pq;k?AXc|70Rh$r?Qi9S z&;I%=-M;^%E==a6*YNjXTG5furs`ldel0JJF5AF738~`}WJpVDDSxcR;d5#m9B)N! zsZMz~Aav!5RWj~?Xqx?feYb&Apd>b2Em~BeQV|aJpeYc*MgXia#0-$q7cLxel}~*B z;zbRJe(cJ=Re;w*s748Cip)t&vUb)mXc}8t9Yq7*BUq`OW1)E>Q>DVt-_wlLSPlK- z^XJdi&CM}ThlGPh75-QL-@^mJnX~#&vI1g;BV(|#f?+0JT@d`l%?w=rFS!2p7SzxUC0*Ru^Oo$KfJR9wa+>SAPC?)zy4nhYmq`5M&EN-e-wOBjE%W78X7N>l5%LT;)mFlo5~+dG=rP zMW=}t_TylNZMLwzvA%5N==DzX>(}@0myv;l@Hg}B;E;mcm9pFPL!3GU(yizny(ezC z;_jV0EUXO?+cb!E8ta2PJp5(ps`i$DcmZCDw&vd;?rJG1;{651T;0?Zol%R(F9@FN z*TfudVCS}Neu06a`h}W!0=l}oX3)$ZJ%6r=+`$u71Yr%jR&`ZX0D=JY8*=f8j2fB5 zA1BDdUqcN8yO>^G zA9Gk$G9W4*EMip%k6aYAnQ9zAV&A%AX4ZGI=peYWsev!Iz?=|cH4a{k3;cBISXo$g z$PUNpOx@Rcowyd4cwKsV*t;7R7e~FHJb}^d#PK^Rd-^SXE}CzqW#E7EWVIH9S@3G_ zhAQje=z{VU{yXlm2(q)BJ$}bPan+qmtGz>>lqYUG!W4D*T>=I!v_(^CDvl(~b%j;C zjn*Xq3V(<*w}89}_Xi&&7odFI#3^5BuP}eR?bLye$%P|v{LrCdi|E)uGek}zU6CVT zZG8-6<)h;}a^7xWSos;gya+YHOoNgYM5pW@9K>LS?S4v%NDbtBZ-KaPO)rrC04jeH zmN^{%AQtlT-$N@M(aNHuIS{tKLnfD4T0AhU&@~luT3aZ!uYH&L+d;>-$DKk&H*Wm& z_O6Dx>d@IEEn75Pei`k&p~_w3V7O)?UR9 z|BGz;fRQ-ovI+(Uu&!TEiz>twy;IMXI2kL~+9C^q=JHu(kyeeEH-`7^L`ezX=IGJl z>7OOEq~=3u)tqa$9nMPq0TXSy&Wj&A-15}JOAN&I=z*pyz}B2UEY2AmeF>oPIh!6W zz^Iehxpc$_e>C%muaamJPKbMBwJvLh{1PW4oe%zLI|Ul(KmNx#x-%GU_(7*iY*EnGO}buOsJO ztVcA{$L$g2K3}BBB4Y&5<>wTS)e>^(NJ=3S20{@ zXiO(XOVQH&voH0+8`c1K6Uq?BZdgejPK?9o#Yd|A2#v>@>j&|&UcDLyPDY~EMHSV3 z2{0?^`A6e}-jk4ckUmg;P~i}SH39Sh66}x&y8u7`L-j*ruN>xtw(`S_R0g}4!s6tq zywCUHc|?rV-&G8cM*uiyn03ZbWjLJp04oqF46?0TuS7dv30vxRdkEGQBuahoCcW+8 zWvY-I&R>?|4EEsIU1f3n%1A3b$Iv0X78YDdJ1bzx-@_;vdF8p$A*Te5SbpKRvglE; zEd&7N5KP9 z)u+A}pTu1<8bWN68OXP=j268)KOMl#OB5p%6DOZ;z$aK@LX}Yrh;hTt&IdJ4OG}G5 zTxF-vog*uaq7@}pC34Bf0vAWH4|YBuK$v&Jh?yp$4*(h$A5W}tn7QIz-)rY(fa}9s zcEI!W^z<4CEnpsXA^TxrX8V%E4pBklD#JRNZr6JZLJ23_sbB`kTno&`(0}?;CxXo4 zzy~;GJZySFw$;dvG-ONwC!hk%(0J)Ulw(P+Ua>(ohkQajR-|hU5R`Z@#SXGa!_p0O z3dJr1e6gsTomFt4_~myS2!O|{!$8{E_BbU)36~FAhAaST-5-|GSJ`02gAt0*+Aw|( zzv9(H%MXZOz1ay^wgHGqn>7q4A3>NqoH&M2Cn&q1vt?@>+}o>v0p@Xb>@KV;03`Ly znJ*U6FY@!bK}W?PD`Q~|uu`Cw$kYJv1*i}z(dF1?zIYzPJAj+7em>^o?ai`z^E*06 zM!HUqoq85a7nZ>hK7`Upv}l;KbM(EIQI2=?_R_;2p?MDz21w0D1P3+2UkU9+9=v#{ zq~^l2d~4sof_ojxGSm4qU}9YS1j1?hs;TJ%U)p*AL!to=|kD+(AY zEdD@xL{ys^Y%2Tul^wM;k!|oiY{t9P355&CFeoi2fpPSMZNe5KL6|h0V@gCPoNyFR z>^`u^q@5kuqzZ3OIzP!68c5EhKm%(Hs~Vh76Q5|uh#pndxm7`dx3aQQcfRMlHj2+J z2b?H8JUk$(I}rAd@7O@`L}7o;VLo&5%@@)3RX9-?qa#IZ&=>{)QAhOW^wg9em7k0W z0of1&<|Q7Oq2DHHFNQxmcMn#GkXmjF>fjgwA~Y)OFPo0w+t6YOHkz;;S?_ccPN zum!C|>W)EwxW%{4`%uup19l0jnhYin!HcsJ#WUeqATdJbLvJ25+e%b2G!!&AQDHYH z_5MPy!g5Km7i|U#Ezx?k2#`@Y@UlR?usU~a;>ObBZ7`Jw!4!k!M++kH_L0Zrh@xx2 zd3q=a>(~7Kdnwugz~NFnQ4_hs07dN9K)~?i((^0bxUru!r4!yBQ!_yRCo59z5TB6<-keJ z^6&lEMi|L(v~n$cON2wLF>aYaMO)U`xE?V9F2Gq3M7io)u^6*|{uqHoA{!4*xeIsQ zunRFx$Rp(_8Wrg=>7oo@#xR(cfg`RO(@k-8K-WG&L8~bS07{s~qKuNnSaOrB<4AB? z;rtrZ@SL*tJDjHm{D2BgrWh%6b8~2W_rt9N^TJ=YHM$nY2rtm9cn*rY?v~cp-U>0^ z#Hcb@RF>s5b_sKalHm5EctY$V93k!)vEX)XcZ(^Qg4MB_{%Gari1!OWDLyWc%NJw3JE zb!o0QE&pQnUc&1@rSQS2hI^wY7K3{3aqhjc0m-nVyW5X67j$1k7a*A+pp=w;RB|KS z1*qTswMDG2ZyYn{>Kt<-9q-u{8TCUr=rOxlvjc9Da2=vI5*ogodKeO#B)6J+~NTkNPz{( zMu8L3c7D>F`+yS*pf>Sq;#}Aw-azKviU@?=gJ9(eDhy&88Sa3#$*u5TEBbBo$|mNDRmhhS=T+PLNHB z!xok-Tf`TkXV2j9kU(#gE#wgFEk2B^BE8bUzeFh@;~=o3KmkKZb4kj2Yf$2CdiFPg z!QhQRmJ+`==7sF;X^dFhyL|!@)VsMl%wb|foj@97AZyA4D522>OM_Ve^w4E|um#T6 zYY82K9t`b(f`f?=cnG1Cv>(Ec0xFNW+W3`_kqFS{imcvOpyV+-$Ou)|QCSyMQzRu} zgC3=0L^1_k2Dd3)SXF1%2?(L2XBvxI zp2v4kG5G-cC#tpFFLzVx_g_b1fd=2wt|b?U94Wmk5!a8AW!`Y|Cxb{k+V%oHJ|N-( z?%lf(Nuv+_MZ)5%h=^`ZdWB-mV9r$LwmHywzlmOen}`#*dTbqP1#pf;bp)A%t8#gI zejzm@BSR5S0emgA?3Q1qiCIt#5Mpy`qI-r$k2ZJT{|WvNl63D`b|ywfGPDPk_fK$h zz+jlG_3ax5=`0qA5K$HR7R}r`2Zdu26YJk_LA-_3NxBB39!MgblIBtnkAV!RgNfA^ zrb2Sfa4s2OJ^O%&BhM6oX(&So=aIEHJ6YA%)<*G!<(jk%f>>U_hOU3znVN`3aaxkF zm_SJ2aR?v{LdhYAUi!Dkg=IVntUCshF)Ymwih;~Pf1+2Q4eXP6ui}yt8r*dK-TuT# zjb-Y&bJ1)6>g?>4K>cqIaqUbz7boWfwCtm;!wh766V9%L?YpAPE7v`T#fYUl+$sX~s&fLZM#?tBt-)^<3)W8=(n>?E4B^;jKV} zFKH(v^JqYYelWS;IDx1rVf%{;Ku_)kK9c&bMS0!RtlgRqTtB%0A({kvq!omc*pJ2XeS-k>@gtb-K-=?G-ua41mf#8+o*Bg_4 z!yo=cU3HrObv){t7K6o_>0D4c#8QT133SujwWQwx8nzhowa82=7+%o0VZ%X71df&{ zd3gR|FN%AXmu>q^5y&e8N%GcVW9}&6yHCvCDVoVHNKpM4*RKP(MuvlH!_7gwZmE+%W z9PNj{Z$w5?g#H0qGItM7kX-gO5ROUo=vwS7=y9cR4^Sw$uri>xkBDA`dxz77F!D7= zw-luLEOM-F8y+4GzjLR2>M@x{^3=|Dw*;&Sq_+zf+>>PhNuC!R3<(?*TKqX(^ zM>=SqIN=3l5~W1xr%!Cy-k5vguPIbU=Eb2*k^Uiyt!jAX%1sQMQAXt>8OsRpZDVim z`chNt15b821jMm2W`&seW0ZFaUFZ96#f4acs! ztjx#$`t_Pmn4D1;+X!tWb^FCa*VsRD2M@-B9qY%j{ESXGm=(7{ODC@NH!4?3*E#bb z|An94^_b8|K+3~y1%KXj{fKbIi3H22uAy-YMIH!ULgg=_w4qW3BWs8qUsF>P1m;JS zmVtq4qS2=Ss8g#TpWm&Wz(L|efjrQZ#tSv>1USq4-I(^Ug&6(83-idk#smfuCYM{h{WHDX%-w|N*M?2*ug@V>Ipj#yak*2xOSD&S_H5K`MNL7nSzmj z|Nb!&O#}Mk)%e42qxl+dbrMB4;V9vrBqno21TC5Wjn0NyB48oQ+sVQ% zaFFy;z&W`CTO7Ji9~of*mJ~G%816pA3_&F0SLx{waLCDC2;r3QM%0Bj{!^I3k{XqY zYbBi>82^B!#ca{cT^8VZu-P{sa1zISEJCk>zQz2O>({rzST{a7Nwf*n+7=RNQA26_ zV}k-orvdOFInbE9fM=TH2>$CcY@55p#Cj9Ua4P3iSP_?{AiPv^PqCa})Sa+O=&pp7`i6(=)=Vn(3z(guQ=oe9ykQfAEzC z-3H@}T;FL3quCa+?6jNaN2GB3`N#VnE-mYu>g%7IY1?lKMmp>pt1!LAjHWyxNi)Tk zf1Gvdt!Muj9sR}dpZM4zV=nSS|HQ`{C)@4?(|SAH{daD3sJ+v??|ri;TzT8ZcQ#vH z`49GWw7q#9`K7VxMb+1t*9IAG)%`mSjDI>0qAQqu)Bgs1;epL(?Fh#V_yG>{t0{=( zX|D9WO+1*Rs)!kbuzaAy-g70{L6IV7QL9~8%vVY}1Q!;u zT46kZ=&oE;b3kK$IH}$~K0k)LMFB6#ufr==DbmYB90eF7!w6%L#nP{o0##$EAo{lkja7#g?9=QSe9fK9oJ&N@0 zN<*CE-?uO8V)hx6!Qh#YukHaazHQ7x^A2G-P>2!Z1`2o@3RqZ9U@G?A7kt6Xy28+= zdk;KN84RXyjgmIN8MG$dbTI|53<}B4rw%<0u(+0#l_jg+Gg)=^WGUc72S_cnE!x(@ zA4rR;9Ze7T1TXiq-~=IdA0{W;VoVd7DT}V%{=Un0u5j;v?u5+SYRo|Xq-kZ?28QoCN|@0yFA7|E5T4lXrF7{Yfw{@ zh88k&;W7O$)LNt-#r;D$EPLF9dAdL-@H4kQYWcV6tvh$F#%NSBE_6l|?hZ(@q?H>> z0fDvEB+oVSx6=U>j|RwVC^xO5dB&iaU?{=wlaU7wY+(l2RfMBL7BEQk9g+$Ib{;rk zho`AM3mO)%!zhWcEAP5Xs5X@2&cm-T{!IX5M2Hm#%M8#$hWp~}N&)S_`Nixp|0MT9 z0V*m%-UY84A$fs5EW9QU90r4tA9~Yp-^3%oZ{{M#9TQ(U^1|O6Z%@;5b|r*{pIu$$ zA)RgPUXro-PD`(Ue}m zL;wO?IY-_@1obUT$=+jYdjDm+-5WmnAKjIGu;2Wh{pR14Oo{n7*P(E$^*c0}W|CHV zv&K}4b&16T1|&d~C5AGFEge8VhQtl|3dKSzE5S#p^+3pw4SjA{AEiiKY@v(%OiT|F z=YYT?e23^t#)JE<^R{FoDeTMCaShL*Ax=`&}rm)hrR{HUthNnIX?ZhgaQI|scimL zL-7NB-5B^5ZSBq!YY(V*6R%iK5L5&AfQWsN$D&(nibYxk(kxnJly~S}*MRp*PpYb# zx#!U^lD3d*Enw~bDo!zz*akMO8VdFrp!1y&GhfrARQT~CGP*+aDrVoiuU@sKkYOp0 zo-*ah1MVt(l8q(>4xtult&$PJ0?V9b2VL>_bxzXHlv%eN z+6Z_BDl7XcWECy`jV-BUE`!yw6Uv6geNKz*bLsyf+WU3bze9UJ4f)?dd-MN+_7-QX zVUECGGXnnKMt;}*1Nn8*;XF?yHCPZMfKs^JjIy&Yt@WLr{@-J~0EE$Wd+c2lv=gUH z`KYm--77OM&jZZ#4sl3Sjn?_)Cd+J|N?<3VLY<^0*`!3!E<^)IP*F(i?ygd>5;aI4 z7Mx@;N;=Eh7K{G{$sKmLazy!VjF)RJtO18{0inR5C`hIB#e4!tFfhK)G#k<~zb}zj z{T|v&K@NVDRm(tG-*uxZZC$wW`Cid6@>$gS z_HB&H1bNWYvJ{p~_FmgJOLH4N@Hw3|WN5c*%|{A3M!@Okw1|<42U<*%>S z0b0rP?bU?fNpz~_Bp0swHqr(IOl)_50XpY|E^P&^D#A5-nU#zCTBWr@&eiHv0l8?8A2ug8}o`2mg5)d;JKc7?I-bebzW^=GZphyo6@*00C z`NfNmi)TdKzD-+SBYfUoP!fbBqkFWxu78H;R)@;s6!@T^y441$ZP|iId=2lr_v!2O z+-q^eut>LJ5Q7UQz`x7<=*K+3{e)3vTNhuUWME8dv&wP&6#e?t?|{ThuO0<*Y5=eW z#n1#)i^C*q+f?u;af5P~<8b0O}%OgM+5Ex13Eh&>?OA`Wz!VC~pBE{dr>Cxqi;GMG+1X6N@kh&M; zrbrx!i1dhZ#?-nRU-nA1tCYUN`MJ7|_gT1dniEXs(=QJ6?Ch`)fs0pT0Fs~>;04?= za0p|N(Q|SqF0{1;3lPK)ecWpN$O45!@<1j&;PXpV39cXz8nu4?F5S;t74$UpMPwVY z&=+pMfQp>F83Am-o`0h$v>Vm~{gG>Q1>@#qe|uvwh~(RaixHIadXadrld3o+&B~!H zC-7K&6&dr8nh%rltJWcKL1Lwmr>m-~pGs~Oa=oZfHgpKQ8+Wb_skQ;mrY^{!xsXg= zw7vsuzZk7N0ZJe_6{7f@eJF_J@dmP-rusq7rUzbW;+(%T-#G5jUV?0DLLE+R35C&$j5S?C>S0(${6 zGPaj@OWscm?Lq&>kh!6ulIl~sod21vRF zFa5kKIDNqisZt1i6{7^o3XN|%tXGQ5QT9I_P7f^^1Rwr^&CHD*fQTu8hm%8sT?B4=dM2xk_D3xv+b!5_k%mTz!sPn9I)#G5zmSsE*CJ;@O)|KH&W_M8O%WdkyhbK zlnA0}u(SI$c<{ZjRp}2nQT_D`f8|>e5xvIw!LXiYoOZ+`?1bvlD!fF#3(zg-x}qwr zUpvA|Fb0vLcV+6c5in+428c1aBI_@VSwh8@fNfR8&b;UFN1vWgJZ-qL_0J{c%bxa2 zZ$q;v5q>B^6^cvqZ@Ge22LSiL9A|Xgw0QBS-YaHo>hRwI?G;w@7M|*aD+~jsg54Wwi+Dsp!c@Ite-A$(&gkj&hO8$ke+iowmB$nf zXGl)E693*wUN#T=cUXS?Okto-^~ZF2G9Z%L%3}hiXTVavwSop6K&kX-;UI}>;p?aN zdu{hR&uNCql0BR_8K~%F6GPr1MFYii*x?7f>XH)$XeWZ_i)K-sVa@>W?jGnlQ!1pd z?{2lB`gXKj@r|3otP>pxkD_*1Y+I&g`(j>o^}Q4^()v68oa^QOoN7a8uHTj^(v|Q@ zhW_frZu2T^l}ek>WN=|6ikw=Rp7g|DCo1-|$*(6D$8uu)M)EZ(zi)$#yO)&D3i%6$$H!ha8^XQ@!2zB zeMVWZ$%+&egT4g!o(~SrP8&CVd~;NgI6lr_x^yPceaDV29CtTCl}o-NPyU#hIfiSO z+!!VzEFq&i$qRAplS*GCzdWvWOr!Wh{mP9 z1LneXa%A7w*-Sk_kq&Mz;3zntK+zBeWAyak>p6lXc$g>-kpl17LD7fqc=e_VI-YQ? zs9*0J4jMKrkW>N2(6kw%sd;T~B#T&LteHvi9NNQuhxNxt`!$IW+_`t}8Tvp^3Q>SZ zazk2n>^Oz}K-2t=LNIsi10J}Y#(*>Yl?gqa*N3ve9VOlpGSGsJochLwQA?L)I5)Mf z_?6EM1x#G^{qJ*->xpJ&{({}|nU^l@MG*INjv{@v{R!y!+;@EDmbPr(d@QY>V8}?w zmOVcdxpX*rQsdB}Onq~gB_djC9yVW_KpY<5DS_kf#`+%nCw`{q)=)b^Z%PbBn?3CY zyMhM~A2Gs^A0?W3C`S$^i9WVQiKn^XUcH^WcRN^Ct~9ZJ8l~CY41NjBcR!o5L^e}R zk?s>8??KCpzTArx$rb65zDZdSPE$A_Rh5*K%23;#y>a6(pa9tV4n9`TmJQkuUEUV4 zuh^O2h1^8tY0<)~b=>P?RmRUtX<+o3o`!f;mnV!V zW4u%#e8Zi*BoHce_o}63Ws)LJc`x}h67#~QBxVaSbe%tUE`ZxjsLm;-Ch!!<&U*9q zld+nPjz4#zac8Z=ppbkD>{!j^TW}X*@%)M+l7jLiq+i%kGY1E)`n7Ay5H+|T(H<)+ zV^kxzN5frjES4)Z>b2j}WychWG2+K?|HtzWj(6j)gLnLQDE%|*QZ5IrNB}DN85}s= zf?d3Q=iswn3z|4==umleHrm-~d3^^L(zN?wF6cd2Qkb!o2^DE ze}lM>Eh|mB>2Ii2fRKjeBh&wt?O1JY*OMYZ}k<5gb!%O5K1*D`H& zEx%bWt?OU^NfsTvYjOWh1J72JKi{t#rCsuTe-i^MwS714=$$OAuIRhJ!MLtpa=+}m zva0{vQ4_|ZWJW|Zxb!fx=UGi=HPJA;wNgobzTJkZlR=v9PmF)ZMAMjvK3Z}6(6l3> zXP@dAtUV1m;7CV=W>VxgGJXo?9cbhYtgrw3Et+qOz zEo>LAVS=CW`CidK%KnJo9L@7dLO|@(z-CbRpj)Z3mt}v4D zD>`8R4htXLyVnKJJ0W9JpKGcF^s)e{kPZrg#Eon)*{yJWFsu9mFa44B`8X${hUophMjc|v=44X=Jj1n_0sUdQr6RR z;aH>gtwr^DkFy2nvj&eG?xWBAn?>HUG>!ikB+wD4&IDOqvErLXmWG-r4$v#X5deYg6Tb^a@=Pie+ci z(i8*d<{wCcYd|AJUBO~_T6*Xun4Qpb2nj{8%^~}q&YXxCHy{=W4xcFw_yW{e+@L0I z6ftNE@+nhWC*AHZeZQt=(Vi@SR6@xAsNEZN7}|(Ze$_{NzrYX(4*{N_O!{j(H`|yIvrwu8(*II6&LQ|#(A$He|x|@i;)UP4NM~^Ri zW;L*Fb<(l8_-;AR(sOd>&&3YYeWPR7QBz@2g#G`WM`+FI0-LV6sWVMPH%d*J+riLH zoA^V0O~#WAM+G7ohA$El&?3+;1jK8X@5aN_pur>9deZ}-1T)8vuUk?=QRMR-6VC<5kO=eBezB%BF4xw6BUWF zGIuu7rGASWn^50n6|h51jT$_}oAjD;pw7=?&9T{Oyp?(9=ME-}6`p z7%M-%F{fZXdnJEWhiX8iC8&-e+6hoDW8Re6>wf8{I!StAWOY@!Mm&(Go|HWA-;d=f z9`Ny*tfxo1=En~obfvVzZU`&KkM|2R-HWsyKK>NMO9Bwg(MRu)%Zt5EczZB|l>YQ9 z`%nqdPVYy1|K=$hQ4dYc16U%626CswIr{l#P?kjY*nWR|gu?$UDHrsf)^z4Fg@sNU z9f4GEs2xzyQo3%fr6iIKx@3JsJW`??UtL&VSs6?rQK~yWy9fawm+~&AujpM})W2`+ z2C8xN1E;}5o^AFk>VSVYaG>P_Ew6_ZtxMQy=5?rFub%9Np@+{5H-64I!SrhB8z`no zpge;MW>)c`!-prbvlgMs2aaxDNh|ku!_y;!-6TwDcc)88J7`9v%qE0HibwP{yW5UI zksy5(LZKokB>r_61Cc-9>v*1JH{{+%*Hd7vwI&JpFIb(Qj zrMIGL`cU@i;JWd8v~xcVzh$A&I|R@covam^>-^+ZTWn~*?<~aQOLhQwt zzFi@fUAdq1icJ(f`t^H&(oFK8I(BRnKJS_1dYCG7>(casRNgpJ^>4s>4gG%x>y`ZA z(S(Okm~*fL@Hzz7v;-^1zYek#z{a=goBe;p>-i%!6oQFzbsG%(|9_|c9=0s~SY^aC zcwDs77ARB55^K{$MSHi5exs3Obg`5JqSzFrF)+`^b@}~fEeBdcWuCln@oV^IUVrhO zz~@Zo5T|`qh?C;_%6ARG?_uI4rdzQ^f^_5$g3fh&Tm5;mKFljy7^fRU`&4>SrmF-FjO zRfT%_37f>GhNb=wr!X7>t&^l;Vm}rb3~8f$cTSOrS(RwXYEgAPASX}|Ss3~*$aZ0K zmd|j)4aN}+bkQE&YTa73oHf!zkzi}|e#eIqiePW&Q9D`w5?M$AT}L6I7vM+_JygUj zV-j?T|BH^-wJ-~={c}=aQ?cfd!6iyi`G^Q6P^HVX2ekS_Q>Vda%E2fjWv~)O!&Mxg z-s8=wke8y!6#I@KIZ?^gHVSL@iTR#qYr$uemWyB)251 zP$^8U>%eLE0Q&^DGkS4hR=!+( zQ<1qzAbCMeCShj_Umx=!KZ8s*Q2LbSMy}hpiw%N~riSueRI>!YOu!!@10g_=FPcWQ zN9nh0ZQTw348P}lZJifv8X;m2wymmVzGue^#1C(%V5YZ5iV z_K!@V=xv4nsf}|hYdEb6n)-i&=dFu}{i{yf=a=)RH>Y`D!&am`eCX!mz(9K?C4vLm z0aGjq-4ph*J125WnG^@VfxH6hFnQ_7A&eJ>_yb>V00akh7;)ekOUBH4*+;sPokS|p zDfk!8?td~nFtz#b7yk2`!u;)D@}hHfY*tS?RqnTSA=oXFeB4Y}1e1J?OofUesg+3UH? zOW)HTEK-6ey$`BCkoVpyP5OS&AjXcYDxeF>lnidr^=~0>SN&ZJP&O|RQ%Z4kh*PlI ze*)j7V??Ji{zy?nXLynYgcZn9$Xs7*zqLF)*x>ID510|qp?tM{aE zn`H*6X3N%>M#>mn6k-C;E4ZW~M8h=Y_;&_A!d$6Pu#3O_m{|V6tHzgIwez6KA-3L^ zJ(q*}Mfi<_A~pdxcY`m$x?A&3x|Jv9pUWgv3bd`Ep+mT_)K|MQ!l0^h(1=@% zX|yRX$(A@Xix^iiO(}jDZmKH@ivN}o;6<>CBXoK<;KJx$-V~%YAtU2QId8<31nd?0 z;)Zm1qCLlCF_1WL;2s$)CDt)IHq%(EcLdQ8vaeI|Bc@?#AE2M9y~Fk%d#Y3Xq}_Ey zue5$G=<7j#+EE23V$^isL1Vuc!0pk?mj}`1@dHu+!E2(iRR&&1Va{@y`g3+FDFVE( zI5)#%!*}sYlREJ+Cnwg|%f9TGho7IR*Qj&?in@^X^gKDan?zd-wwmUpi6U(sQ!Kyd zG0&Fj{}ldtL2AztNI?tfbL@(Jl2uR9%(82Wq#G$y*YJ5&58^L_D@sZ?PD9!Vw6Zcp zvWREfOnLtljjqdvG>?!KWcz$9>RvU#IqZ zvnZXi)tgsdZD^XLo&EZx()Zybt%Grz&xukTp_lXQ00g`Dr-pFt8Orlyv!dYvTLE`J`Xr-61M4u&ZP1_HpgBdiNz z=`uFBMl`~+E-a!Dh=_=I9iXekAAfdq$S(DbgjB&CG_Zd~$p5c7KQAYH((rgsI-S%+ zP;EN##|sCc{emtd(fO&(W$<=G=F3#6$f5> ztjZ~06Hh8Jqxmz^2YR;4H*dZ%KU#M7LKXfH*{KX6AaIp}QY_fGF+8p8!n-!i`IGpq zy0vTnz(L(b|I^2h(*veo=I31t3k&2h2avPx zz>YS3ck|0dTG?LSI~l&zqA2hE`)E{IpS>~Od$C=wTf4UTb1Iqrr_weJ6`|B{Fmvi0 zE@rxh*A-X^v1RbIGiq^*eSLlPJ%b~DwnuMzjz^xAb)Tc`nEG&XlL$9UBAKSa_HDAF zHrqO)3!D{LL0R(wsihry7r5WSefmr}>&6PeegcUz&XWe%iXya*%4{?LZfXSvX)ybvg5pp~GU6 zMsN@vUsZR4Y)yD|N_qG0sz&z15D@(_7(-3zgsWl;-n3}}EinLD%U0z1cl08ej4A-# z+{<<#FHOcmDC0mPV1v2*aXu z-3gJwg$c^Oqn2OwQ+?lae@Pd=SsL<<59;=D>p_Zqr{7YouIqSlG9L5~19#l&@v-f=6Gc4^6eV3m}*SUl{ z$>cJoWF}fxOqCc^5I*_d*zsI)otU#nj$b{_0Bg;gZmJ*<(dp?bv|8w{qKo-(A|~#4 z$dW=jV^r|^&M*4;tE+=(*T=JQ(KvL{^4jqOKyyTRLE>4#yvNmACHM{D|Ky7zIcPVoxQD6JmQ`F?sl<%7uU-Vd6EkNmWu#Sim=nq_$zYhDyW z3Ns`qV1wbAP>KA6B^yWF6#1O}^cc1*ajncSOo zInV3-l@o#jGTx}xNV+Xkhx~D4JPeM)$mwc$$y~}wrs?`8VW#-yGk-fpH6G>NBBuBWEwsH{CYz{;|>s$ZPhXbo< z@2!*0H0CEApLIbhXo@VwmPx0#Vd95)+w&}(?v~o^g zuQJxn5*FXCH0zK|A*6M}LSUJAVjS*87cfI~@kz(6poBYvsI(~)bH?3xt3``I&s*Y% z1*Comie{2mShS~2qkOz|^4%o6RU3L(>C0@>&83Tqz9i=!v9D#?28}zW`^pKgHlUC= zGtIXwtjzwj;kjYo{u(>Xv~WuS_0mYH)1RLNBQFlQIT?70l(bgja6#W(RyD3 zYjK-a=(QE8t-%@ZQN_y>I%{;^YCN2BvP?*%yYn;l4gem@J`)q!eO7$pDg72Wy)8Hi1Tti8T>-h_HIWdT9eeg{2WLg$Kxyqx zJ!b(SWuO;{yj8}FbQrq)4m$&S{++hg_Th{iTRL6VLcAz1PXym<((c@9W3Nrf&o_nS@kozJ=tHyAD6^f|p zs*$AJCIX-h-=r3ix|!Q6h3|O#a*= z&7p8A53Ls8;f{S?gpwzZPv#Loy4K_cgqiw)mM^-?B2I%jx7POs&6Jc74iT9TD^6^! zjaM^Hez9aqJjR|@Wv)_6FWStsoGS#fi98!^*0O#5vVMJiUw?;Af!(+N|Z^wh=-r%9UeeQ z$lh-GSFDEEaiAUKQTFhjgckN;Mu-@yg$8f6ylYfJ<~3?|6zyJa5I{{5$kcmj*vbN_n=ldskT^G$&kLo zr?SbF>B`xKmxBURrrk$W{XuXCLTSXHdQqDz1vCR6%JF`^&#yT2sWB)6I*I39REoqu z&QliwH27l^950YlLMU>-bBZ1RUTe#GBZ3=D{#5iy%ytIT|E2phbhU! zchL}L7Q$q{j&gFEblNbIn1ai$qZ{E5U5^%ftrOA}ZlP?seDXBQz$&0G?^O*GbOtC{prdg~e z_WU+}SIY$p=AeY0<7eXUfFc-DJqt>IIG)_hGJ2Coke5?_Igk zC#ZUUWQQ*OU?eek;ah@!$pOcX#*Th>cQOU(?@$U{Ya}*$Bx8C-DeEfYTjb? zSaP39@U1*oUTe>IT@I?Hp~k$lUhsJpP;7P5*O6S72Au{N$oA6Psr=h8)o(+35_OmW*Ib6^^Dw;}DHc0HtfGBM%l(>=1e>R;vEt>B~~4B;*qYCf;f zwyCgn^>PFX0eC`IaTOMgx}v*23We360P3;m?M93karMTHU+2OOO}IO~x?pxGse2DF z>=^!TrWrUa3pF=HQT>LsueWL28?A}yx?3z+Bc@b zjA4^0gbcZM_u*KcaaSK1Iw_Zd0(INd^>kD zkYifEfKy3ce|=B>Pn)u}j#ZJZ!L1@ngUq5#t(NP0@*&|YvN#!mLF$-EPJ%T5W2@l# zS~{4|FJ>c~!bu$93w}AMSFcIG!nEx*^rQ+GGC45g094^1(> zkfMSxAz)Q$QIf&-?3dkb=5HGMVyOOFC~(Y;E2gL3;F5W}c-!KEdVYWmsK<)jfD+@j zeM!HEdpbw|fG2exd##}ymnlV{If z>i6D1@w6-KKDl1A))u}|Upe>C zy%o7B12)DOx9@QDi6dLJ*byZ+$Tw>#CzR^FyfiyoN?(>0iCHK)Cfe;1;{m$^M;YjL zUD6o6nmzcl!z6BcH%<+mgUJ`!lC@r}{qChUcyNpA5~peMd&d`M(_UQr*sPa{8Q?C<#@_emq$6Cr}5KL_8IW51W~7+8b0F zE!lTss8JfZ{>kUinNiBCVLL$GcIx3wlg>I}U4Mt%&`gq9Oc55dg#YHUp2pX#16m~MklX`IaRR1# z#E8ASch^>2Tb9xUey{p-#CY4+`4K5dtP~3Acd6^d+l(q!l-**I1MFr3!nu&yM*R-O zptV6(AQ_8%YPi?Y&E?!?ynI6{6#^NPdnWPXrUmVy{K*7~qUpUft&h)Qi{x-qMj%=h34d`^$j!M9LdU0xF9aw~l@(3x^n5b+DcRBSfm!ZFa^5(#kmc1-_&y3LTX<=5yz3hc z1$*HMQcxTJ^r3Vgjh0p%I@oI}(8oyCjBZ^xfeb#M_^0H8Io)&)JQD+VsBvS3b$a9| z2|mN+`!;(SSZarBX4U#dcN@<)AN+DdUP!#QE-ECp6M^08U3m}r0WfI&;;b@l#P-8U zY|jA&q7UO=?mmJc8Pi-{$Yop(^}O7HlgFM+>@s+8VWj>nOqmM@y>{I5T&QLIjh6?^ z32y9vf!WDOr2~?ek{)rh?!(90O`!aD3KK6=go$Q$jC*7tXnAHtNm>kOxXhsegsi7j z3tDGty&Q>5C4{5L=kH6tDqE-B9!^W~pfO@1kd9-H-9}6VSA6}W5y2MfVC@QLZuI zw1vF9a6OGO;(w&lUVgX`966RWKp7f>y@(O$r%c-|)vcyvm9W%5Mvk-uU=!~c;wv%3 zU_{syli;UBLV}p4Ep+1O$^lL4ynAsy21QT>XAF(R7SaRdkHEI!BKhF6Dsl`C9>+~V zm8tZ{Fq!6b7Z_x%?a+&5-3JL{pzA#?Ef0D9?$iX;oFwKra zj4y`giXvA(HLjkCtN@faCN3_{yfeVeSiALXa?J)UderoDQX#6>eaKH)!^}Nv3irR{ zhjvKR%RK=V%aq(hErM`%k&@{U@nJiKnEVl0TVuy=Tb;)`|3!e?bW_v4@QGheO`bZn z)wK9j&UKDpK3dhUlNr_Ua~bd78wNiqTV9@a_3cgD3u9L3`lqosB92^c9crxF@ro+O z|9V-#Ps*JdKiyn7!RHcx0gpQT(~gJsR$$Q=i8dUmW|SxXmY-?toY_FT;wR^T{?+s6cS;lw(l&UJ%UZ%vK#I^y@N4nHh4tb#Q#u5q z>7=3n8JOd{F9<4UW-sqSOu$0CH)66I)zcX^Y+m^7%ZNrkVH|V@BBlV#**2zbG549jp^cJ$PN?T_-@^@-S_E~xqu)6_ug_s6A5hgpW(I6gMO zoa`l+)A?i)kOG;=Kn5^i^ug8sldrXE)$xzj$ESOgxYnRt+sp1w1!_SO>X*jfG}X;` z;jkTBQ|r0xpdbT)&6#&|T~8pJ$*f4KoU-zA8vwG4*na?>M%sS4$00HZivpZRv|$7! zC$2*L6TMv)j;dcO!81JPwVYGxvG808iHZB!GmaacJH=N+XDu4kj^}%!@GY$TTCT3? zpHap^2ig42&;oz^`jumNUZyEv8>E}bHS4>mXaod^j)0oxmJ7iHei~iO9!g4~Ck)8$ zUAvO%Ck=o`8>wDEi*MA+8?FT@c~^Lpq@<+nm;Xd?MmyfidlHJE1_4c%h92eR=}4L? zn|SrXW;t)BzoM;??r>}bVjF--PlSdTIW6Hi?QAVA|E>iPGA_~j59)koz3=Dfrv~K>Rli>uqoijYQ?#*3@D@w`oe6}T%muS^+n|gXYn?>*nS+*)$4Tyl4=ZH>GDH?SY$k;_)YbzeEI;5AqM|9nv36X7UBJ+{mna{P4Ow<@+D*`X;#3Y`H%r*13TpU@oTl?ey%5mMKr&*HS5~n_ zd(@#rheE&+jLsQ30Mxf_-dxQ^=}T8arr6gOBQXh{7aq+A9l*632hCblrA~W!N7%q# z-iAT53c+*-AC4cHcUu?f!cVMaERA{BtoN_Z&ALxHd`DvSID+fu_F?t| z2QW^=g#vvI@WGp3pWjvzYZt@FwuSO!$?OG=JlsK27-26Ufxq(5`ql zU{Y8!?;@Qh`tl#>n(Kyn?a+#-I(c>cE}KPv{xNU**2Yl>7MNy7>lg%1NpH$NtvPf_ z?XD+v!uleN&(Y~-h!f=FMwNU2!ci6&;cOdAqB=W`?X&|els6vZOl&CKyBM_ilFwg9 zO(F&n$PWfN_pNr9f~dR8f+Okz@=6$sl;wKcwR~nSebJ`n>xo95u#`Ic$rMOwm)&2j zKy%+jbwJeCtyBy2mL#62vzZv;5qUwT5ynRQeh*xoQNLPo@709AF^2Mw{Z{LvHOA}x zGe21|>V?geQ;CUB8bucS)Y;T_zH>yP?VOCE?K`-K{PE9U`f%Jkx4j3<{hm5C?NpjM z+ShHb=FJUDKLyuwwedMqT^OjhG_<|Z>_1gkWUqG_uNSb!OM|Fz<*XL+cbw`$-2-{y zE0umTnoefb0Ul+W-KJ(AcvQC8rNpU@rDY4vmCtJ{{9>ogl_zy?fRZnephVh0Yl z%&hIPtL=_mcKSLa!FpafLhG zrUmCowon(Z*bI_{sCK`+EhjfL!0?=?0eQ`IJQeadU>RX0-7&PhD0d8+wF^e(F76=Y zq^*N8dR|4~_^Yf;qUKKws?v~AbDR~_-Ia; zwSRwu8>aYrDLy2d+)r!n!z0>f!NDe~5zU5ho{q%(RXslouqt{AC895~!Vj2zHS63& z_d^{=iHrc+=b-)tfTq_?TZ3|?rin>v_oEN-%u{MT&$zj{nIu~n^-}5ejs0dII>IZ; zT8pS;l~|=r-JYGdfqTw;ovJwn<(M0#aOT8326*9Q6&<*$&UY{`{7>MvO(chb90!GD zDv_Hy5H3gh)6<7cn?D!vA(a6EKKDVUTynAgDSyYj_@2rzx{D2oDuUPY`cG?LIoQZj zU{Jvc$P({5FN#w`GW8J@pED}E*XgD6@!$#r&;L0WTEA||{}vJYb;2ee%ZZV!nG>|w zQ1e@igIvc3+~I#H6urC~08sP#@k_$<>4XzcPyWqeh#MTf7o}+eW?kndm--=Zmzr6v zdv_(a*cQ)yfbTP-w^QsV^c<#tJO4oksGr^vXT?KpP$F{@ZmhBFIAUEr&`wOKcGkBX zJlSSs#G)OA4l{Rp{@fcs0K6Thojx|+%I$2R(>V`8ZnD-w1PFOSs&Sd+p&%k~sIl%b zL3lSBJQ-6A8Nu9)Z4^Ibbq)XmuRCfPqHL0|a$yTMrfAP;TS!OdBDA1%7K^YAgysccEh?Ra9N@Hp=_q&qHj>e#B4ZF)l) zlB`{1GvTjcbUKDrO(#=#L++@=g@8p2B!n#RxhYVRtATdusWIuHgyb8Z)1$h?WyaL; z-B4?~fAOGoAI@VR-@?qtu5pu_%d8N#!h94@CqR+nxBGRN^+(&?J73(2me7A;IT0^1 zeW@J)grcIfmcw7vXvyhBYfVXp<93n|;F2p5K~&q2k-}(Fx71L89WijB7RVZJvS`>F z_woWw>|)TDi@c9zc?~fy5`xpq`^%AYwH&jYJT+IPb>y8GgK?XdRAz@r-X9nWb5CWM zs&sygRNOvyLJ~aFlv06C$2E3(E9x9N>@tt{5Q*j?>>9h z1eU3_-*C$Zjc6wW(6ux4dE=$JX|~1vUCEEQhklscM^^}PUPu5$)vjG@v3SN|Nq2B? z$e<f z_xymzvu>0VER&==K>>?OsGuO4yW3QOZFVb#B$;?*P4%17R^`EB$v6k&qTpj{P?x@a zr``yIc;`Ni21<9Tu9Bd{f6G1QJ`E%#UPwGj@n+4MDUb`%LS=$q3X=zui1g&j#=^yS zQ}D)q03;GcIyKXF>z&->v8N505wU<}6a8_v*~q~MoE(dhr#A+d14(I^QXGo_bF4@L z7@a2LZw$}L_}tOWCG(sXQevfGH+)q}8Cp3OI#i4cD6#8?=KT6)M^53FUmYArT zI5<>T?(fg6K~M&f^Fj!ed`s>AcGu3G3qYd*Pc||&JF|=db`LngXQ1^2!9jbez7e1i zrYh&!eJ0~-4J^fY1fnUj*S+nB?4@cK8J#6d^kgur+3;8@R3;ZgA-vC%O9Gdn1z1pc%W&(q`Kpw?`r36iqV5&8$F!)0qUAFeQKg^_m24HWz*$IL2F+NSGabL(!YBKZhL{zAkf+6O! zi>k*`m1}hCRzIini^WLSCIX0nNOQ>0jY8~~e;-*_NvYv=D46jOzx8soh)%uX3tbOw zLtgg9h3wLNZ|SwsT^&{5hzJA7XU65J-jnXXcwq$3%24e>pcVC4UQCd{ed-<^hmS?G z3hb;oYsWER=={4h^3`@58rxF$q2=rdUxIMwI-h!a> zX7iF9@%0Vz+KknK4766-aMDD|Wm5ECJbN|+Nof1!!^q78>s_Dwa_V6Z)P=jGhn?t2 zPF3p%VH7tgp2+DLn(V$Xy5- zME*yHT`zlFt%;{&G~p_eAq)Tra$r2CanNZRL?R|{{RlsCV(hOCj4W&69gKGEFcY^C zD5ASNYa@YkI1b%Nu?Np`86b4&0_FoFRdG$rW?1s5gEI{p$A&FQ(hvz901vj6Aw4GW z_KK&9Eh~UWND;pAc-XI!_h@q18N1RC@?^HLccZFc7{KyAx9+s|q zBqX@xUAIoG$yLE{?Z5oyYfesm#lP~Re>j${^PSPIy7rf=AMAr0Gm%XpjW95ZWd2fx z+{FSTV>1+^7cDyd%x5WlH-s#`cLqY4Ewe6+W4D|rBLtaeNwHP~`GlJc;f}1JX4=au z?!1;eT=&APAaS+AuhxfoqGgo~IlIEA!`7ftVN#8dopY`)M=O4I8ATGv+zCh(PV1%n z{MkXHxfpT8(h&GMuA)|h9(LHu1AQ;G=&f7jU0?5pau&%M`h@^f*oXuGOx5!NEQ%4m zdp2#ct(sS_!3Cet_{y9>f1F%u7tNWvK%PiFqYGC%Cg^hAGTk!Zk-;P zfi6}{G~3_J)@tOG{5qmyNOjql*Ug+9U$R>*O>mP$S67-qVo~EkbaPy4>aDctGbt-rop&ETRH90u3nh)SX{-Ci zDBZ@edWcaUJ2FhDRjHU%9DdXQ13<-NV>VaC&CmbM;cc60YRI{c_|P1|7?$CkmuHVh z6wMFa(|*V{jtKM1w6#6@_H9+ykbfCFhI;ouF`;dLU>RdBQhD7$u98}UDy)vx^bXpy zA;OSo$$I98YFK>>q72I1a{6^rwJeG{S^28=BsH_{Ab- zSc`uqKI`?bv$Q^PI+j?!c{iq8eSRXU@G&2}4y=OuxIqQ#o2kV{O)%nk$k=B6!VzJM zs3Jh)tNQ=-I77P<3)w)Xqn@5D^|mAiod6tqG#B!E%v?)Ybh!4Ey;ec28#v^?#0I~o4v?yes( z=!I()4R0nyij2ntLnG*9D@)=y3zlz;ocz6wjs>$%?s49*| zIS=O`gC}V^)9gMpmg$ikt!>8cRk^>o>U{N^Hab-7Eln2?c2f&=e{>~M)64_Wz;_X~ z?nZqooP7ikhfGNixi=^f=%buNLPAVm;|&*OD)QnQ3JJ+!T9=~nQme+v?vT7R|6`hS zCpboy?0X`OTiHVpnB(+*V^v;6I@-^RbZDo}n6dhI4RluO%%1J|B`zv*)5{6pLYAgd z0cJNYleuh-hc@JLcZ}xpbnZl_cgvY8sdwCy4m`rXXCARG>wmovtO>X;d7GB zd;}J?MK(!Q&MI+b6&%xXstPEu-LSz8K2;cPxe`P-$hZ_$jq*EVF-KNWeYC?x93 zaBv?s>k$D_WknS?KDDdJbJ`R;+~G&$zDFDakw2mv;=Eo6H}{&$fC&p$k7dI9UWRvI z35QdQUR!{I4bF&;wLBFg|RQuzsweV+faI>?+IXkmx|5eU! zrlRC{naYOy=Jo3}?3^Q1YfL)r$%8kCs$mG7Nv0_h666uJYSy%46=iOsbQBXKhsA^Y z_j6o)+g^%XP!)44T?qJL08DpbJKhL@~^IN zQx9hkB$i+;uL;KF{&?R?aV%(G7k9SdX~Udr&Xl`^%6bv30SP9BJ{L<4a+hnmP(Lmk zF<@K)6A{vt=hv-y{h5Y_a|{j1r>ZN`)vTB^a19?oOO)cc2 z+!}XyfZ#$^ivWa!Yr~JxHD=4^G{BcUmn4D>5r31rIn2O!#C;@0C^!u@yhNtir_XMr z5SgqhJ32#pfy1C7FqH>l7os2h#7oKI>+%Zp_c-tW-L<@32gSe_nGV8xX>Q2)dc!21 z5at&y&ShVBUB^9<`D0g{T?@i!8@+R40iOb0;c4=-iWcmk$$&9)OP5$!+(jVNcDYqq!LXLiWsCrXe<7s6?>77G+c~DDmu7!@GoS)bNBx`O z8@|${O(R(ll9kl0=I?!9#|4bBuiC^EZBc@Lx&JR_vWFdACv1h4+3?oivYMK~E}35-&C0xfPG16x zcJ9!c-K@BCZu-O*|KKsx+l2H}E4cPwJf=<@?NR%R)GZwOt^-|f+?&$~vVY6T@lDrEz2-&=gKd_d6I#Ohw5Wl`O0|2g~5Q8vCI^2emPU=E3=xknJ^f#H17X7w7{)fF}{5nW*oX%Ehp zlv7~P-)^I1B^RjTZI7}wU%uuopkNT6Cn8zBxKbZU4HAz8%qJP0^;4>UeP}8}LulR- ztk*Tw1*LYOeZWnJZ_6c@^H1zkBs~Bp+aAktev{?(-5SlBkT5`C?t3!YX;2|>0N}v|{;gc)y#n2VfaPR+2ia6g3AtuX_^KyAE_#Ao(;tBu zXMn;ue*cy?*Sf<3mHeW1+75pZRArL)$^va{yiqDhkmP@{y0#JmUjAHIG7|P*3=Yf_ zxh@zhhNYYFO4^zAP2LSo1F!1JWC$Fb(t)GZQz#Ht35W~xvLCzWA=6Fz6O#x9zj5U2FsW42Ci%H&u9NOR|PbXv~6WRj)eGusq^W6 zAb~l_Eu9m#(${=--tEFBe3!~c1?0zw$BJ}iP6=iww9?pyGGPmmH-HKUCUwJ`BbGHf z0&%l}3)a<#S-8u@6>+xFZ6utT+JSX~F17>6`Q}q$!(Bg`pw$9)aF!6Pr(6gdU@riUIbAR#$G@b_Z3n;tp^`Zs>5lh?KZGR~rGVH7#x)cIo5|w(k}5<_+6) z-O**~^gcbhH0bqUd^3|i)}L$CUTA@fvviQou*WA}*~EJpXm4KcYV&OH;JlG}2eO~6 zae>&9lr8*1SOLnma;i5x z4qP^1;J`5mj+vrDX*~n54#5SmlHIt z{P$Gufbqsdcm)9TU?iC-NP)+6X=cvQvp(~KW_4vFq-*JWdx&<-ojVF{f{^q){M1FO z+*M?B7h52d409xw-=W*z4uBaAe1>=p#yzQRf1pU+F3m2FQCB?i875liF~GR^FC|3@ zBsF9V)n^N?ocS?3Mf4&XI3}sM(MPddK?M2Juh3xxt{@mLxq=vL-UG^X!`L$=NmH#5o{ zVgVr!>p_6*j`^^f$zc~w!kUQw8^$^kM(6sY)r7$i4y)UvrI=s<5tggkknHdZ!?p;h znm6CZEfe4cPGAmGKoNP!F(EgR8Ub88hT=}4;Ehk-aer#4%}m?ta1kQL;|j--z(xY7 zEF(fnUEJI(k^W+nzt8?Dld38JrxjZ$$*~+N5v{}{wCd0m^F3h~a<6#9CeJ=+B+7V# z8^pm)D2`d!$r-zSIX18#K!U zvicH0IrglddCo`@C|SCi8k3j;DfVSh8pIoo30~n@3>nTQUtLuam5`D$0nUx1d=H~@ z{}?es0w&~8gp`n{4JRarpYgXm0ga7%je|fU>8WLd`*HJ+X&u>oU}0)-f|X8SQuvAX z=VuH_h&fD)%<$98>D;l?h)!e*F<4(WFDM?(UOt8hJuB;3MGDA36P0f9X zsp+zsDJSx?m!QvOzA+@MR%Z`Cac7+*8}Q+$-;uV!-Sjh`~<1oEWp=>FZhZO6Y7i}8(r zkw5qZh<<~w#!{Z6JKZ+{)1Q&sdYcRlKPc+PjX zl7MB-{+?Y6>urrt>Z_O!Cg5dj0T#R;X~3Vfg0PEBW%#6G5SPaR#)t|AD-`UvM=wFP z@q#Gy(2pKIbgNv?j9X|MH8n2gB>k?M8hLmp@rh`Km_<(1kuZEmZ2w<3V9%UuY;4Hw zQ&yfIzIHnVp9FHDB%Q)S*V8-T>pK;qx&LV$NfO0rHXWU5h%T@7K^4^0%;+lwi>Sgb^Wg2?>xWL*>W7=X$Pl7bsao-X2&ZN_vmcpcq0kU z6qkS`7jrVn?2hEC)Qk6FYMAK@?8Di2!%4Emxu4ih@f|DA&Nim;_`(4*(0nJ0`2hv{ zcnDY=5LgDcGbbXs6!*aHCxg_S_X(qpN*Z>C-@6Okcc3-9(}yPCo6x>@YVIwwO~`&2 zA3nK}hAHzfm?*(&kl#UlY`Ofw4rjX_RnU}g@SPqY&2Tn~{9ZF-59AE(tD|EZAIvqu zFVD^^$NT-VsK}VSTSj}(j7>$=r6H3bj3!OeRz^@1ZCp7#)PU}bQ6%n6iB>9SQ)V0Q z(HZaPBZgiwRo=#Zug63Wl3N12yjT@Cv$J>QjGjF^x8rv`qD1ibPwbeMm$#QUuIr#d zA*2U{Yj}orYCHVoU9aC`n~^qnc_9fifbI@Qf)T`KcLQ%vn2F3U+OO{s3PU&3sp@+| zm9Yg`1Rdw?y#4A#*jzMHn>{_JlAp~65d?rCuqhV8o4L7r*qne=p(PjSX0P;5$a`{a zp0*M-uw}*GQ)!e?0rVQ18NEHSz05p5eMyWe6bKSoO%)h*F=X*zj z-WqlrHemnso^wa`+dIhgaeUgD0jmcTZObvLn|CfCbE>ih6Ex{`tcjx0G{1+1!RcbD zRrCc$jWK}%9``_GJgMAHChzbpCbc~^)=bVhjtt?fln&Q=^X=|k6dT3A#v2$?|M@&sv0ljl(npyK zT7x*+P7`{SY~(5>L5IfH0QQ0{Y1hOPB_oTyCVs70cWLTZzYJxKd#+z! zjqYp{TI=U@Ui9R3n3={WWt!gI4-Qz6%Fr?k$I5=;ncR`(h;t^1l91AX@Ts&LN8mp) zv)(a=F3L<&PkK2_b>0WYxq)&?;`4z|MswopoOJfNXp(F47s2?fUy&?3PM{o@k;YUO zGDsEG+8(zN;pa=eldZ8L8x;)6Whg;Ch7Ty%UC`NHFvzKVdY6Wh%eOY#&Mi1K=QW@$ z6QmRhfM232a}3`VSv}0k5>$tFT@gBN?4jw$cl%Zp>Q_mY2AxUU;VaikVhz2gNWKu2 zSuy&6BSJe5XVu4mT{4^Y#uN{aX3xhLB;~(9VHzGw?g?HW4fbNEiO~(9VkvX0{nmhW zoTM{PCaMvF(TK$-gBL9(5` zz|L|VJy2cxU6hn4U4x_AQA&7HiF6?887I*gUhchZ2B0RALB?FP&Ep^6KULlA{p<6N zGYN{Zwi$spU)>Ht)KUWiLY8=*L37+aOqJt1#oh~dq-)x}SFd?BAd8YWjKOE-MfNLC zL0EIA{*v@16PEG228_?dw#AgSnTabKivt<-+$r{H*RbQ{&~hB$x1=CTTbWQ_XWD{3 zO?b8v7=lx<0rhG}22r=EznCFz+HX&vIdh%PtDh4SMB+HJF6CDC@gYy%A!9M zBBi8YA-JqIh_HNK#r8dWeyE0)70Nhd*%kCFifbVJZ`7iNr+|m_S{)}3Y?j#eE(0aj z<3tE*PL))^IR^G-|81^`H&QndvC31Hkg7ZLa*%keOL&?;0#`dPdmPyjA}M8i>55L0 zHYCXPR|ewA=tp`?u<)cTkRKMHHEQjMlAhzceAHOmbkM zR3&qZ$5u}vo?Bq1XFN@za%nIZ69^Ff%7xS#w$;`iH1hFfZTdLx1Ar?b^yE3^$}R{I zVj^ZN-_QJtpw`WsHhqQ~H|DJFvs61%)ai{B#CJ@*_{ySV)?Y}Y4)`Td51xYL`O_r zZFy@)x0%G|p>m_wA4VG>UJsUuz_Gec*@?Dp>gQ&`$AFGDVDL8686dM`lh1%DT!S)4 zb0Vp-^1=$Wu=(N6Z{f5Zs!gaw`_B4ezUXo-)ddhc)Sd|IPS<2U$eZ@8y0N z!klild%(xvGkYbDk5^PS{?cUn8V9HTI++bLp7Zcdn4n_2E;17sDcnX0Iq|p}QD1b% z{3XYQ(z;1J3fX-l)-)H?0Rt<0QqD5^hihyFqPw?-15ER4MfdN`B^MxdQn^Tn^2?( z4|a8WKeiYtL#to!Evl>=d$BIJ^Ip#(#Rt8r*eN+P=$d?;-IWnSaDrpaet3m~p7bb0 z0?Tc|5E(ZXz2#3sXikng#)PFqUCtE#xTJh7L&Z=`4%vBfN|YVXu~XA}-IU7FW9CMs z^|6TWB%CGB9=p%LTK&YJ<&RHa#ydEg8N{fsX002hsGad zB1aUFI-MGCy@|z`0E%&35cz3IxhGBa9(8rzar+hhw;?TOQo0P#IF+s(o zAOq$xfIo1Cbj{|2do}NHUd3G}a+6Rc7kZd@uq7 z(6k6}F*tk6^`cL7h3wnol68ax|3UL&yY%%^gHTvYn!N)tWW*EDHnL ztz|4Pt%&p~fZ?g!KiHL{7(Y1&n6o=|+_*Z-M4OBb9o_UES_P(wydzA5GjNHk>p|W{ zD3UHIHx7c-c6ItZ{8}I$AZ0Pldry1@t`d<%&F)ik`0_!Y88?ZgmzLQrg>O=0W&qi ze8p+lWr7vXh&F>3DFOoDDt@T5nS;*SY`NY`55J8Wqr>(PmRBC zR*n!`UV1K@PfDm`N5qyFTne^imJS;{I2{GB7t;%YxLO<9I~GE|Y#-U4$bmkXb$vc_ z!)0a;^Kgt<%4d)#&wpP<6(>Ukk-c_2G$qy6JJ(vppaKL<`~07iXb#UTEu5rOYLe%C{=$X8KYMqHn>i6E%HzbPhrGArSAWV+FrlxF zAZ9R-&sM8;?FWBQVoQ8B8!Uk(kJe#kyL2yz`X2~zay}it;nSc&gNMYAz5{Qh1HX@O|0wdj zJ8F>;5%Lm(&l{j;NV{@)3H1<*F@jBi)jY5C>5<(F?1EK?+Z_5(U_3#&Y|`OF4??uRrL}wQDU&#KVBL zp2s^Pse51V0NhahOr|)9tb|!yZ?g>an+r<@7$ox z{)D;aWCx6LJo$rY&SDeEJrNrQt;933P71qks!^!6uiaw14&eX(ba1CWEyp_lCb(GU zm{3VbK&;D!cemGthCcnHN_7pDS}~r40%{HL*n7jR06$xbF^wj~3c>!q4hc!mw4KOG zC^LM)>30Lw^qpxn)$Xbf)_+#)vx|Wo$Mttq^KjFC8|K5JQ~%V!)CdLV-&z2c;nIs? zD&;6TpaqFlip9Bxs3R7O8;Y=*!HZYU>EdLv_=ooI|M72sTCM$$_f>}vEPXMvyK$eg z_vsoF_V;(a=62@(^Z73S{@b4wUrK)%-Z5XZYT28cB|h3%bgL{JnvA<_m3Q`6RYkV| zhh;U)mMt5X6I3U^S_iv~_+GQmK6q6o&-BkBe=S*D@zieE-@i4cx4TY*B8$73yLJ!! zuJN>r_@zVMx0nqJ{tGU6`>rwdEkRfdQkugvAu zvs+0eYaUflIV)bw)hkTQxlyHBQ<-s}!FdotRZ**wmxjeUQ!dknQ|~9UMs-TjWJ;1N zo&?F;xu>#(Ms8H6CSBtV5R9A;`NtnQ$nMI3-|I-$hWNX0&BmR;D6q$k*E~>M8{u zrx=#hMi+x(%>uR@bl$_zVSP3z_1OP{*02p5?3M-@7303It5!qGa-;JwNe#dWNWOkF zPM>m`K_qQk4Y+htxFCsa3ZLvnh|8st%I`z7Yq#has*b;q2{KQ+-tAR#1UpZ2e4!cR zyfFbXmN+`PXzJ3H89xpb_P4st?0PD3@ks)K6JlFK0iG&gG7cvt>=7W%f-DJA@c-61D zW&rv@1hm%H3sZM=d>>*x?)#^F&vBhb!yP~^@HhO))hkO9AZ*IY$^a5tKyj753^GA0 z-VlHe1Xe7j0*IFY*>@Z|bWg2~z(dafKJVR$VKO;wDw1b9r)pvM7BoqsacbGJWg^-K z(vTo5KpsT1c)!s1Gfq{uc8?c-O|vpRWxjeEY{< zPWMDm3Z4hcb`xt7##a;yqbs_BoHWs%m_DvGxB~o6_>m*uAI@8NOueypGE?H}; zsg?jau+Y5ybZR%71nBnbXSG%{9A=ltwr{XYbq;4zj$@2Fd$1u0ng%P!w4?7+rW;FF zPfIK-lQIqe#3*9Kc{B_pswD$Qq&woB;!ct7wE@zP*jj5|aN(APmxJS^S5_$4leWa) zJDTM7*Ja^z+3S@3`f1R}3ALrLWQ{>RiUOPCbUSIe93Mi%ad$>S?C?lpi4EXwu!Qg< ztY|pH8zq#OEl2_g3i)!$fEA$t%@qN>y1dIWk4j!S z!~_%~ywHe5M)PSxx6NR6gSTx2^1_=L`<~W{WA@9<{E*;a83u%K3-L(`6)r$YH3e!> zEB7C8MZ3Ub$(+=e1D{g&v9>{6#<;9p&*|b zZIlFLS>go}799UPS2xDT7e|X%uxs)-kF@G#XF;2?2B889Sa)Bn1-db%05rK8ml=Edv_NIdQ}~?`47eZx5g$(9+f&qq6cJwfXCFT{{5u&s)=?rI zkRS=X$<_m~v}byr-LVQ-DIuGAGdQaf3rhC1*)DuBS0*(X?Y$aNyj=eLyj6(GD2I64 zS~9(v_E}Bw7dmYbmcb!&+OpyfFbcYdCy}ju5@h&d#O0^kXtkAKE3sdQF+%`yl2%WR z-2&3l5txF#9#k3sa>hFox#eiUq*8+>6+@0hiW?oe3K>X7K}$siQdGqensr&YpHbAEV0ace8VF(3)=k@C<%)r$YGCv#iuYmoP#8~}C8XD`ltC5^d zq;-v?o8`RQ%rC21yLQB}V@8g7Iw#JLQF%g#Fq#sc@UaQG-V#)ol>&q6tiL$9%h!nUE8K zFC>rf7{~7_SSvFN3lqS^4xF4&fbe1=A%=jlGRPF!x6EFLM#+jf0_EI9!<3B$N_F)V zSu5BA+V1_%xZ@1`rEg{hG;KZRUK1Js$hoq|AG5R)cpgFnVyW52#x(PX%=YFWSdh?e z_{x#UYb?L+*|Fn#sY)5}dO%QMSNPNO#S_MlmrFrh)rEfyg{QjxN8P#|P^`lp(>u)k zfor=P`f7-48Nf~hq>MnL7#J27wxt(-$ArG_%tVJ%jKUa60N>zaM~}8wMxg5vvhC+Z z;R6Ya5cG$ZZ6u7Y875G&tjbd|N{7*ugG|T__m^#1>JW_CpLqXD$y-Wjqos9}VMShE ze3-6aUS3}8v38(s_dbO5dEl?R@bNh4%Mm~4%${uoL;Bsjey>t)+>l}HuU@`n2aG@!va^sO?=m(_ zNKNiOoh&DvE5Wvmh4rY?!w!0%la}dC42A|b>q@x;4$1(6b4Rr}=VV4aMFbSAjN_ry z%Y1PP%lQN5+&8!j~p%ln+w!azL%hM zo-HGxupMr>Xaz6rdnDjG%OfZph9-TK*XcuCp$Pg;sEgG`>x$k zr}~N%PS(o{01BbLZ_Z<&bT6;2NVD$H-WRGwYe~{azm4C zd!oal2S z^EC7u>WgCH5XGWbMuuh1&W?(~LxzYn59syHgF}CSH)Vh2P?GWsbi>NhGI4&MxEI&2 zPa0q{d0O{olnDc;Yg@DsnV8X`(nESvyEe6E8HwOeB6EPy;#>cS{c_{!)44X&P706J zXGcD5rF1(~?TKhcIxZsbU&f2@b`ct19nw%S5=U!4oTGH=hR0PeR_$AT%+~^$5|@mc zXi%xt=HOTwXl1d2chu4O5;eV~-4^aP;j9fwu#gIjj>)((nhzS}*jR)5ZzJ<5%{!WO zYHVMY?_GSm#d);u+Es#%U84VDP7R-=V zB^uIif1zOMFx^gcF9_QPK6dr~{kZWz;S^KQ2rT}4?9ArW71BkMm->1s zz4=<}R{rRsD1JHOv)3H?n~MFN1j`+CVPFNLDz#%AcG!{5$her;FZdEz?punQSGjdw zgKkLk*G*7Zq1T!>udSe~*^I$SVaZ39o@g5mLSJ3dFM*o7x`xZ&2htR@QS~q4M zLk)lwWE3-cKISL*v%*?aVo@)zwEceKki7LIN;;a&m=Q^BCr} zSH^~=?9(SEM~~*})9%n;L!IIV4vCnM;v88t zYZet*RXN`6W6>fzH}?gzhyEhET_I*)W=m~i9c#5|6Gy+UkYX@s*=0MyQMPQ^B7=4y ze%6U1c-HCK^r4zTWrwxDs~3`XkqDxlYD+!gLJdH7r?V#Jh~!eoLgPs40BhOp^(nF6 zCR%(N9yV-6Lz-;K_u^=b1v_rqtXT)-a^o*l+S5B`I)n*$!Ht(6hZ}iA13PCYCz)v5 zJWTU_$@ui_=IZBCGk)+OZWy+6+eDGu3K^+?Zsl~co~f&_N$9`q4821Fbo4aHh8|T+!Kqm)iP^~lncoI#E2)O1A&6~9>@95vHV{kB5VP&qr1#v9O8)4$|fyR$@unV}b+ zctx{uX_eTnh-xMEV**PamOAWpV5Z`oLgoGsjKj*e?X(INV&uEM|R&9k)?k{8xYvQyup)&#tnFgkp@sC!=q z=T?-?o1l!DbI|~;BGrwsSM)iD4j=BHb8Kc$ZJJ#!VaM*>shxDGkpU7LVAkUlv)H4; zXS{;OTLVQ^r$%)k z^tS^U25lk^DhRm^yfRw;CTq@)+N;k}-#)o(CnCYwpC*Q$WHF+^lYI!$wXLJ$1pHx{ zxp$2p&9Z4HKMKm1rG9aSAS%9Kl&8*|>BJF|IcUhxp^Y`}lQJP+0^(~A(*6Zu;eFtQ zYcDgPB!{}b8Oc8Y)gLI&=>3d~5sTzINw;p}P&$Gkh){ zUi_%M#=j7iiZw6dZ4#eZY3cj5OZn8Gw;`FPzr{|TM-~^sNCg!=TUB1ovuaLp`C3rq z7r~#~D>-zl(^=29@Ll;-Vrw`{EEg{HBV7$gj?jMhjNc|c_i&tRY93?YzN(F1i-(qL z7S?U9`DApDIdg2=-Wy4OpKfq&psQ=;YG-MWqLbWg0;s;LM#?C8jAGJ`T&b#Db8__H z#qNn*Kyzz2Y>px2&xH#L)~Nclmhc)R)11dTlV-TJLL|4wKFsb}vA);*Nn4%L9yJ0T zuH0G%jukd*z`gYln_7bv003)G=C^X{CaEPS`wgxziD>3x?&QCD_LBo8eVF(F7k4Wu zcw@nLlh_)+xyE9L7lu<0RQ-%et-JSi+QUD6D=iKktNdivaoU_GjxF~6x5>S3u6{?_ zM#OyFoiwdbedfCz;jKSL`ZhfG{>DFu%fDH}dw&?W8G7o#PvbKW9wl$RQ(&w$w6u|G zuHNHr0qX0AMf?1_i#hX;izzKIoRE}u=ihOl&k~;x9DDq=U%<qoAG zDX$>%H?8tAB^2VmdUSJSRaf}hZA3{AUj$&;f$3!&;EN9SqFAqs&}$}GRo2hTN~z zUenvH`1qcwi{LHm0C0zwA;Rtvkw--d{w=C;0O zetGpkwl>oGF^y$~^nY`S{Q%vFSPQW%@;4?zMqdQg>Q2fY&GS_}Kw)c3>q3TF+1uBp zngTa1pc)(W)cvP3R?pLxeM#n&`3Aeoan+7g1|$*%N;UlKbGehUB?JF36@W0{x*i24 z0ShdeOR#X*vN}pI*P7%NZ@`CdL&UR;egXXFTxZbWh6S_jAKqY|;4=AR8RxTkbhRk6 zZHP3eIhPFK9VTm9Xh)ujnq&v?Vf|CCm2`-6|ECrJ{R7#cqU}VjSQs^9Bf>bzl%#km z06w$HYTdG>C581HB(?_Ux7HzjOM|(YjC2_)Bl~*x>DH~Tw3Q@DE_`}vD2Tx7s6&V|P(-V6kgRYN9b*8NfI)P$(Ahz2|?nG8g66Z(F|F@*2lm^}2YKa0(n z7^@!9M?|R9w0!|S!k35KoXVHQ`et=Akf?ID z@(n-@3thMv>3MfOV`=w7M33g zIqTbv84ULkOZK$5q9tj(*dGNWSF8hzrG)$P^()d6JlEb#pqU@mf^;;@e##y_{vZx` zrP7bh^cr2=zz0;ki^((~1o`2JSKWM#zb1)39^>NNn`Xv&N6)6N$QIh_2xr|m+6QG8Z@6Xr6v!mL&$B5y?0;A+)ae|%okY)<-yQ_s?GYRse=En z-}Qfm-&OUW#ZLZ@`&~7Uzx-cLP$tg$j}Vka{r|=6n%=Z+%6Xb|j-}fEDahz*umOm2 zgQHbODc}(qNqi7=kZ7|9=9xgYjSinIrx8Lz5|gd6Wb!uqGLyy(*_^WdM*XcFL?}jz zGl9);dFxXOp3}+CzI_Eo9W}TmO^^&sV{1URx7WE4O9v;p3>rtoFF=&y8Px&8p)Kdm z4Wh-eV*V)MUeQ`KwP1w8>O#k0#PD%hsRhdkwkR|3#Kg-P2H;*>A;Y!h2Tbm|3D_8> zY4+oD=ML^i!tV{E&=Ir+7)HMG@M(p$64J=T)9Dr6`Zt2xi~zoHM5 zpQe-_i0^l3X3ratL5ulfDN~^77hL=xLw0EhIdjC20lze8n0+Pbv9V;4O1%oo0UG8U zpO1uMGipKF=9CBqf0{OUbX4<=6?nNP zt#0-wYKI4i`M2IH%s?tArja{w-faOFkZ=#mh^St3>fYHH?cg6Wg7ammQm} z4kMzJ%I@$;pbjS65EPj*64>UmV3{-njYjkcw6=M(3RZXvJF-Y~Srw2KmFFLy8{&?7 zP`Y@{feuZ94dT9#3b$ovAB(&0`YVgqq2Wz@HxNzlE2OSz3$#0%+S)qT8m6cVh^Exb5^>YN^eKua~N>oj)0Rudv|4$&{G)7d~cv&Iw#g zfbf+2Y4Kl9&+0S8FA}#M=$6&D`H0zh&MH+?hTO7OcK^N~Q4BooQ9`=<55>;mqGd>k zpDS>tGNNHl613!}~w=P{g>1I=zxbye&o?>Zv zz-TQIk6lDN)3^p` zPHs^bq#tA1fn;6KSpfb(EXTzKP*d^(HcoNDV!8Zqm*}@!E_^c_JNCD+mYlUS#BGxk zFm10J`072^3E`8sU%YrR*JYB4Nf!jwdw>*qf&{BR7v67Yz_$eK>{x@t>~u> z&dSVuaA4F%v&wHJm+tWP1mho0hdIgJF>8)f2jvnM7t?ZvNf{%@V`-~=eSP_4_Gp$I zoMX@KBMhow@M@x1Q-Rx9l*zaDq+H{%5Li5lm+XW4J9rLb$w7*^)Hxx?hf+%ad7r)g z=G|(l+wk3o^CR?Dg0_8O^VDD3*7S%|#O+thn*N;7dHj_g2Ys5n-La~)X7Y(SW4D$J z>sFq(GIwtylb=miOue-tKQQ;#eS~6Y&Q0e1oWj`!K8JDXI#MP{+1dO@i|UHJWhB0B z#45~=AdW`f!sTwA**_o%!m^bCv78vat| z7D%Ec+1;0REbvw-ucxHgVCSY?QZZ*Od*Pdcyer63CeI3XG`Kdv{Y^|Sq$@VShuF6e zZ?89cLV@c-PO+V%Biz)6qKPCdHz2Ju2>|)gluiJKUq-(od2=+$vT(wTlzw!J!C;$V zyAdkMFnFeayB(WWkz$3`QR0DVp}4)V@$n14F8ccAOUpSyUhu1;H9G!qYqE8-pKCOh zmn$a|Zi%QW{Fvdb=KTRLrIF2_1ZUy|s({v%*t9;@7 zr9kzPFnlreEs`iy0#xns8=hzklWtw+VzS(xOBln*_6h}>$bJ_B@D`)loVf}E36mYO z7nWGuvj_W}lI$;+s+D5J089lU=GCA>{Vv=`3ntH=cVaD5z!jAT^N1AZw^q|i5e*~1 z(qUFRoS}CP?^*Fk1_zZm{S?l{=_hba_bvaUQ` zQD^qV94}-zM~(z*P)mSuIQ&{_UbF%S9K41lT@j?v`7}L|XOVnSO@Y1^7G9C!(lfJi z**?~8a-w~qE zlAfAzXtCF>T)tfJ_`z)};sj^S{g!3Z!2kV$p}WmLr$oOsfY}5?zy0O>tgP9mlQNgw zVhdYeFqrrJ0Bera?Q84|2g1WkR3{zh&3j~(Fni9NClUD{>dZb}_^pg-uFewe)n~*> zR|ns|=izY>)3%4SB0$CGcyFY$(b2wWG$qaFPCue<%%)F2r80^#>%SRcN)3(mUW>6v z>~!){DJ5-~OP5qLv+iz6s}|nLL^as2*eH@pw!!&3(!QMM!Aan?RthEO^Fvq4L=J`` z{msLuCbaVFD0o8j(m_&WFa>fS^mH06TBOXaznDw~gSUw4t|23um4Df1&_`hlS!D}NY z5ZEcREX7SR_4X(grP9c$Q=3qPOeQDb^+7Wh3H$bSaEO4A5H*yd%Y89!6lS(Pmk|G< zblQ6GRE~NVa#<7}ZKw1m6VUsHWzr`|0Z$(Ys-J^WjXe!tr?TvCLpRrC39$ z%1dd@bss%`{2Kd(1PAf59MP*;d4Xp)D%u?}ymjKbYLM23;06=ydO{EQo6 z*Va2F<)wYmx`M%y%p-a0K#6$KQ{PeXMjB=@%b=4%`$oiCuk%WD(c+jk4=yTMLq=@- zAwyaLPBC9YAR6;JNahXQ-SuRoWMC-;&sFMgfaB{}ZUTaXB{(O_*&j%pVm_tRA57M@ z%qL{%?%TI+#WDs?Rtzfd>)?e~PiACgEpWZYvttZyv~iw;eNSX^>tHlRZVV;IEVG55 z@0ghvEqXiT-D0E@&EKOV_=8S1xaF6upJ>Tn;D2;_{+;520JG|fndaul%eHdB9Chnv z@cG~ySQ*8E(U+YURXm0cW(LeIXCaLFIN1pv`V{(ik9xH!DKZ&wmu~6?Kg$Ivm%<}d zUFLogk5#Q=mCqYa=`rx$KlgsSS)o%hvV9{NQX>;?P&7-T_o>2j^EXDn+L5MXAoEc= zcYY2L=H8*-xq%7g!;3g82M-$beQS%}BU+qU=GCj@W`*9fWRBf;M9a(ySOSFF_a^UqK)yg&*Jurf??2aFw#3@w*X4!$0Tek-?uSU80%gDgLI{P+ZfshtAG z-Q>sc;*O%vK%s7jtW~&BacfeY_0v27;3o7Y3oO$1a-DM2t0s#smDD!g-G8%}7pr3e zNvgP@RInp_KjIx4ar+c$5D*AH?LmgUYZ;(OC}_1j2CLj`{{y~m@RSGNzVWH<*L4zGzA5^?<@HS>vo z$GsyHi=wsLfB`|KPC%60{ylxIs^{e8CFpE(rO{+;b0Am7A7rpfDOU4Ld#l;8Mf)Q|z7;9iagM&l_&ohx%?To|p_6r4z0N@M}Zh!!583PH#iG_&VnHlDt# zkATI}fCi206v2VL+slh{-vFvw2YEBUNQbFEx;E)>vi3-8ep)9@oQ!x^2V8J=yl{W^PsT>4aul!_=%+CD$nT(^b;w(U zkO2us2Ua2PMo<4KgYMEii$4=DLes{LuiA2#4kNG*BD{zva32OE_d^vmKmZ-AEHK18 zEjyn&+Y-r^sTf9ut;Flg+V7V;kCsy|oR~t2rMSojbW_MehYTAw6)hd>eloJe+6sNgu9NIZY*kpNXEuDgN8KS!ZBU^V zPl+Y-3m2aLp{cL$MNd@@?C)+Ad1!_2^X#|153d@Mc*&%N>-#$%Caudpd%vtK&@L@4 z;jha#iev-^t1h(sRkwg9?@gmc$M*9aWrKKJjE#MBBQ;`%@(y_- zX%3Hz6E&s8DS)1@8vC=|?imjUE$MnYV)i^u0rzrnV9MrYWV zF$E@?F|;~EKR-#?sOImXl*D+T2|IgxD^yBrJ&T_etvgqvZJXbD{vjbLSMqY!etSF5 z1~niW&33_GS3Wqhl7l__+>sSJg;VLkVO0wBTUmyN8?giw3EN`8(wL1X>H8ew!eR%e?lMkGY!ACv!1$p_t~#v zgmis(urA-Acotm$)}^)l#c}=YYiNl7-xoZ$yowU4?AN$+kcFh$<#*4c_V|O| zh>q>}4vQ;mTY-6*ysRJ7U6?+^MO5u;NneY({OfYP3neE08d@ndaD^W814|-`-V)L^ z(#&j!5z_{Kg4#m#l|Sr%X`$YmC~c=`<2Jm-GGGrzQXo+vOvO6l2O$ToX`6+#$o$fTCckhM_Xxs8olsAZrq@JHRW_iV-r70@dEjp(|BYzKe}p#KZh6Jb9`=zW z(s&`NMv~J{R)l1Ra-Cull!Qoe6!jHizl4f!!Oj?Tr?}U}ghO5cxJ-_!FBb_iAwYmx z_s!!v{Z6()WYZa6yqJ`V8W54713^Z;HB(0e?1+j!nC#({Fkgkw(iOQ`5Mn?1Y0txD! zdgk4CBE^j|*(7BMnHToG> zYdrMuj-Kjx7PezFC^LMTujrE{8PtTB3J>sOa`qcPLHUJwT1^qm0+D_?o&tI$TYF zZN)l4*vN?)jP06TWrGZ&#jz}dz{H;hUWfHfW-;>?p~rOVett{+R@K2}Bte#80&`-x z|FoZaCErnih}xufLoG3YK0|!=Y;eQh2}JXc_~%&|7L|TVWFi`=;_49D{LLDPtGj|g z4+;R;P3c`MsTul=J=wu25j@R-Q3sp})<`i=>?;^7gFbtQoZfuy^#V{&D`w!yCPh(~-FuDU2eDA{{1!9B z2jjW<2{&#e%By-jNM?C4OKW6j3&}s{5Ti?*xr&WSEV-nQ@_Hvqo)z()=DP%JWrveU z=4sPvDa1cdqbvqMRymQpxw*4_+Ud8&bbI+%GAPOUJoCISO#tde zNwj?V7SI&zNZA9C8rP{;PfVa7HJk9`9e?DTnd|ZC=?D>t0SWTsHjHdo#qTV_Xea6;64v;Ng6MHT*R~^Rqg4>J(ec29YYuXR($i z{rc66j3)SzS6Z98<5I%en1p-0m~isGdv_DZOo=XoMA`5p1_7{aJuWlk3#QzA6fHDB zXICFS8AgJcOkfjWjuF-nMpu!=ivgdhs{uEn zDo)?iizMzdHlOW20!de93~!zXL~5WCs#|hWxm17x4^IBmUDA{E zDvHMl4+n3jk>Vmq5ZgYfti{jAz~)C_CF++DL0ii^f89k33rNhEA~p*p;?*Cdx!`)F zg#O}D_1&Y*!U-@p(H04zf9J72ethZ4sSze_tC}D6?$hh{AjW1-oY)7y1{Mq!m;lk) z{6%!=W^?C4C!@Iq3^=$XpWSBaq)C28yC-T#@-8;ws@}h!ASPbryX|cxr&{;4QfyS z#PqE%S9%={I?vpZr`owdT5#V9R+Y}P_2f!k}O{FrI1U({U z8*2po+4~5G8^&3>4=|`;cgMPP#_m6h1`(foZeg3vo}R~jHB>5aho+4hjRgM?U@}vm z{rC$$=O!9#OwC%gY?&_JR+M$P0K@_*uWsMI#am03y11+wtxHpmZm>Cq-3MSyG^JbJ z%bu(48SD~qOGtZD-cFKdf}!J*Li6|CKALel^T&B!C=R~29PbCXXri~~XD!N(T@Rl! zzIR9l8>N|T)X9_Z^J{oWJr{ND-rc(3!G}JAE3e%!^o*^EdOJPa@_B$!pipxT>GkVp znru-qFl?2UEE@nKm`R!#RpbjA`$#TgPqtb-B*PwHmm6*3shS z&C(e#U?cFgNFrrhn0pB*D&uDhypjtKT)EN~w$y4K?vv3rHXS3=(YdJc_SRH1J1>P( zpDSh6`8=ZO^s2*=?b9ng(oo$;b6`CJmlvFA{_pt77yqC9WKD%+?{XfKx40-ExWptG zjL*`>CgYepXS+3?OBT)sP~*r?f1Y1bi!Z$M8UtNk+D+t7p6rwGAH!DU{}^*VLd?cCWsuUF-Jqi>j^rd8ae+(~lL!o6KnL2uVmwi3E! zX}M13<|Kx>=5eD@?n1tB;<7?U^?Fb2&E6cd&%MP;)z_o4;z--6%lf~6^Tv{_U&#$Y zEtHO8C)jcCtOi9TO%!tW)9rvJ?DWMPqs`{@2lWDZuJzjtZAw;Ow8z5ZztAxVttM$o zBxs+@QRBehLj9*ejQfmo=-FVuXd|Ni*s&*Cip9@OB$N~jQ27ex6lHX8hEtL9Z;*E{^eUXEXW8g6N#7bo0N{ig2(Bf{$^*hw|n04*-nT z@TqvWW~3g19urEPg*|+2StCk=EzI_%*bB=Z@xCj-c=0EXs@BvQ(vh`Zbm{){HAD?W zB9BA!InV7gMZ*F9&$t~Z?5~OU+?%C47+n1CGM*=#kG}fb$SJyHKJUhagsKVc-f-@y3=AUv`l}@`Jshx2pV|`E8~Fu_ z<2mQdiBEHHq(L5H=nGYZO*K5#U85uG`)8gRT z0rT&SM0?bG?xix-oK>raQ1vsRKKV@`Y5Lih*9@XL$nDVP*8c?InTR1ql2!<11gLn% zlUE?cPO_WRM^iu_HiF8}VO8xrcdkWZy<8V7WSVwKwDHGwF&Rj5K^Y?Dlu%Mq(-1g6 zeQz(C5eb0+aV~IP+2PG1A!;%)vRvde=;h{ zs>yirq*0P9tEady^}=~te99+K@%8k^GX4g^H*>_CI`q`jYYX%*3AYn&p`iJLf(cGp zgNoL6!PQ4esi}3@Wo*i2I?d3vHzw@AgIa7Rv)T~Wt)rxUAw%A?aC;B90G`&37e4c_ z^I4spAg%(q6omF|S}VakxhHf4q0oYSTv2(c_EP zfaZWQR#O14O{=(0?%2aCHzQXC94)UubHbT(@q!fWG|2@udkP4Un@w2DL*C%OBUT{F|*txU10>VQ4>5!GAQ^s51fqwbn!$jyUDkA$EKCMW`OeI2B z5qY74A-%lbu=B_bWjLjH82Ns( z`=H1&NGO^1aTtRoSwYi1Z-f*K=3kn#u?Mih${>yIu5GZ3ads;KUw+Vw&6-1qE&WR4 zttwZ91=CNOb(hPcBJ4_t?dG{rcY9mtb}j`4lHWx2Lv@GdZo5b#ra|`!{l`yakMpta z7;;-;c>x`6K7Cqw$n+rZl$hmEREqmhGKZm~&@r^WIRA&)fU~CBK>_jcy6`~MfE}zqgS#;@1mGHT&|+K5q%$?stfnajmYW7RPo8Sn7Ec zO&7q^IM8<;X4HtFQSxVDU@DAEm3iBwKOr?l6UZgQ7%moE(&6i8e4q01FoTNIj&cHt zYO38Bb~6xnL%a`1+<*OGk8CdPSAJOo#&IU+v#bM{3V|Oloz^o-Jfyz9k_J22@BQ{R zWG`)($HKMqxa8JJhs@;(>qd|;Q)sznc?akU7^9~IY!(u6^QKMPfe+|8<-bv-Z-=7g z9{nKmDSIhqtO@cS@v2zI?PBjse6wFtT0eaL16SjaE~CKBMEh0nDZrum1krJw94p@v^VjHn;eUHs9lJy z#d1uWMYipBeh1+`$DI?XIECkjhJ&R%J>2NY;FRmvw^QH1V@?4R4B{^B5~3KL96;gM znsZ`0?;EzlkJrnN#U+$!*MR6`FUp8OL}%P7eu-oeFMk?I!%~eTI55dC(4I%`hrVd_y!P|ar!@i#~ zYCReHIc!0Vb=Ok!lRO^dG32i+S%0-*)0rs(goNRn+o|&>v(!UF+7y@NZzh$Ep1o@M z5U)BA1>jt!Yjj@Bx(@@#2|_Wi!BD_97g7j->_-}#1ezdfYRL=+qK1keXKSRb{PIQv zRUna_W^c9+pZlcDH2-FJ?1gFzjNWhGWwAbZbyhfS=7|qI@y4~Ay@%NOlEOF<|GB=_ zkGuIF=2HFM{0onJ(tgobC7n*-gqsGQ}AXi@w|l4yYxb?ET(FG~jX zQH}gHK{;$=S+~|4=as7{5-JBQeGNTY1P^OT-$~@;#x}`W79PK@hTZ3E#_;g&Y^%NK zp8{v3dtCSNJ+N@njiv3(tECK#s3&+1XRys2iFj45w3*34i0!L`MF z&1rA^J<}gNvk&}NX7^Hh?gYaKS8pLhK77vUmEiiaWUtqV%jM@(dbKN^=Ly@oc39c+ z;?iGDz6z-fKU`6E-?VtZ0RHvg_}`UdlT5Pz{@?ytHCdz8OBStoo%Fu8{|qy=RhM4Y z&0`HS%TT;zWD5mfK>j6zS1Fr-u-0f-BW|+?ptp=)(L)LvJS&8%YN>c8=<(_c+vrrpzI?4bRSQtAwhx>CObdJN5qT`Gg|VqWeO##+|t>%s=6nK&>=!Q zOx$gNf9@C6#B-CCnH# zsB(P27t1mV3%3GcUxPFPZItNN-2LB~Mjm{MlOYMn7`e)Iit8|M^_N|oJVqg~0|ei` zf4?s=q%z+Io~kLT0%+DA@~4#jryYe=K_^VTX2XDh6Kf%sPTb|T0%k23KcCz+8Fz60 z&y!1xk5^yuZj-EEfCB=B=DlKy-4?c0c2fRECsmp0cQ!u$2Fui!J}$V_@67p*E6a*I zAWDI6RA*Ejf8#a!+YkzLEL}P--I=kL)p%cIY6i3z8u|kIP8m%Yuzh>th9ufAA9ftQ z(`(rZG0G-#?rJn`S~Gm|P3;YIB_JOg(Vw88LRTfRiE@uHjefGvbK;UB8`E*O#;m`oi(d2>LYZ> zGDQUa?gq}xE$ZzmA09X5d`zSgLNDVhZ{(a9rVX(tM?kN2FVa?uR+8F&3}h}Q#>98H z7;EBt^w*2??njQ%W-}7iV{{=QW3=`a=e}hmRDSJ4j`-`kRh=8vkrc2?y2~}OU6QRR zVKE#s9v$w%Ww}3ZH_1KNwP4ydjp$d6i;^wAmjBmWYU@X@Tq%GAT47X_44r;G4Am+L zr8PkfU?GJwCvX=Zy~F2KVyhw*pIt51;nA_>sRY1|lQHDo=Yb_BlOY3#l3u)H(Rimc zwcd=g#CiO@i&NUnvkuLe7Ay*2AxD976qOZrR?wN=7Ve8295!s-iWaCoEZVGcHhTVp z7tM&x!=t9i1l0EWV&f6%Ew3jYPSwRtx(oy>)fqzs6%R=Ua>T)J-?TlIaMp%n)_2g; zm66HKy!_6T+uz3uErMDIiw&d0M*_DC3d80Po+?rVX*76$8Z~deg>9UwMh)qhAZEyB zHiDI1g~UTyRZSs>2RSIKdY8U^_wL32UQFtT&ut*6EtmiJtM`?jZQR+1cJ#T~2XlFu*;=(>i)+9Ru)YtEDJ450MH0TRFlDcT zM12|lifIk{@lklZA`!W~TqfL;7Q)4|^|tj&Zn117Yz9@s`mx^4`?b5j(_Qzc23#p` zlnp!fi3$_~90{V=K~Q?*))2ojmpx z9Y`VKl5=@|EumM1e?;y_^=(#K3*cFfC6c3pG5#QuQG-I-K+_8lCSm1qwz+rjHiQg> z?z8mX%M#tpf21R-A!>e`DNK`LYat1*DZIN#nU^|&NcKhXNUtLI7OKP$TvXAXC@WzQfQI;@S%LME%%rGe{FCW!A#sq-2yUCyk(id2x2egJwHcQT19f2!J55Bz{`;^#{^Ax%0?ib(87C`tr`> zcmOw#A97b*65~2;n5caXHcxc;ibM`HKeE9^(RBOvAJnP{(Wmwp1Asx3L*?9$Mqm6H zt!9sQJg+kU^*B%ubD;vw?G2!O%D7k@Q$i#g9cnB@*t_Ek->TI`1=oe;T$< z>{D7w7dn5ooa z$d{qmuC6MqwB}9=)712?9H`7n+g??7-^2->?X5fXdb)H^?~p9h{in9JO<~D0$YsN? zP|T?uAATIFml-<>-*@|+_{(VYT}eq}D~2N?gcL&}VR6EfK<1NEIEMgC=RDon1oS~L zUR>c1D1Ekt5bJbPXJ>u~~kI_{T5g9VdE& zZE!ily)u%sM?OCRM@WE584l2aS{9olrU!CiP46cYr`JL|3~4(2t7czI#pTa;he@cc zO2t_s-nefoOifL{Fr05Pha=9$smQx%YXaygPx~JU{v>U5(|Yz`R3?wF;a>xE{W*vD zSZaz7=jiiDCxr@T+|L_!Zz6h;jKX|AU7B^j(8@3kg{_(pO21B?I3cVu6U{m+BUmdk z7AFb{MjE2O=SK2;HHB6wQG;jGu}g1%i@8jz?HEk+E+{Y%^Y9MHL7aY5;JJ>Uj*API zxK%Ob(vQ%80uKD`FdebBjDgPkB;AX_u26=#xw$(SRfIyB7i;K4KVDP7Rae7*Lh zeMgrp0)>#cUP}Bu{pW2n(pqRT+%D>#mlxDOo9a|}d1{smD@!7B_xX7py>LMWZNXcf z);d5c@mH7AMW6D{`RAM)N-#Jd(YQD zJ#jKq3k}rOWx&l56mDm3tAQVH*CPsVb(@lv6-TG)-hWj(bla~(ft~W3JxD!ccB`zL zuXX0hi0fthcT_kJFEaKz@@wnGZm%vb8ecTQkP~+!X{wUbGw=N@6BBi6s?ltO)Bd^) zOC(ZQG~%cOvv2J~1|tG2HY(p6IEmkXP6l^(+bn z=6M#aok9_H3kjFGMgHOR@4LH{FH@q`DE)XQKxPauYvOEX_?&C4=}=rjN>I+u+(hYl zVScg-+7YXM20>&v4ls{{CvyZvk<3$cxt9NqrxZ=yf8r@YcmMz+UX>gE!%#l?l$-iA zeJ|1{1?=VWAsGx*fF!?+c|;;wX`i za#4;pn@xXNO93S7O8j-pMaQZ?vUj;#Rxw`kE=)FhxjhntXtL$s`zOC_ISru8!%>?9ZcGWW_C$!6e*3-8L=vbwr* z6WS!s{jMayqrF||aFGu!y$wu&oL#eY@C$F{!C*W>{|estXrUwC_=Ry6?B0~lDE+M5 z@0uUGPpqzJP>BO4a%S(}4$6)jUljHPOiJfTllmDIiAo^>xfQ)Mqofjs8kX(sL<719bTRJ|uio~>VO)qgTg`ommfn%w`lX|hB=e$?K(cMZwGpv0(k z(=76=Woi%0Pw$_WTnP+(>v=F7C*Ycp_|684SL&253m7uEm59t({@lZx4lagCInttYHTag3A_g z;+o@$*w_ubn#9)3GJZ#ew$IypE3Y&&2nU{*F}d7Bp22RIps4Wa6cP~l2PQCPtaV4E zC1dneyLBAp(2Au!sYu8njtqdm(VEGY*EaM^m{(+`k;t7PDkIV7=fZL;25F&(k!|X5yWLI7LH%Ti{L_sC^7iP8i4pDe zY@KJ?$lu^cMWxU?AKrzbECwbf=`FK#97-CJ95>e}Zn@vKfMv68V<7=he@sIHNQX7FXE00R1J)85!OjgQIwW!#{rt#Fzb^N$ zo}*nJgMwWj=!L&;`X+7MW2)GJj69?T|7+}qOo%A@U|4ao)1u5E^oRsqQ8NTS*gUEqqj0ye!sPY+3y8dwPj9KH*7+e(Apaf9 zq}zYTGMR}2`hUtY`G(r$oYnt~Wzq|8hvFu!tte2bYID)^eaCC0-F9gdf2bwKVwHN*6?2(QWgF&8ZZNR0E=u& z>kJ66df-kaIM1=Ak7b#%dEIVpI`Y#vgg|=wG>Hzod~e1VtfLWowjZOeGW@mGOe&?mQw915plYq42SH?nkxol6#dNp2g$(~2mU=d z*p)=a3oGJD*ziVWORbv$l49O#Hq@DTDoJTHpED;sYW-y*aNA?$<}{0pq7{@83xHjS z087AO&RXSt`xX)P#R+d{dCjxUzb_yER#Q`RpySCzMofI*WB%RbnL=OE065H*EKbEhkn<=TJH-c8T9dO7V+Y@SCvEc_1ETV zcYa;3&InHW@ze3fSFfbJE%CaYy-3k@96fT~hnw(8(Omw0bZMN@fUD(WgGT_Un}?V$ z&pg}0$u;gUpr>SQsU)!mAk ziaxxrhQ?gJ&2xSRv*lg=2iTmZ$LZxtLYl^n8#g|_ecRPI%G%8dr{8=)8vRBIbl>DC zr1A&TPNrXgO5!7!_x5`ky~yjiFvP9I;(TtW?Gw?-saS0SkB%K!yb;Yo@F-LvZcx;-YwIU=`vt~ z-ImKnj}Ww+9XTDXCf|?ND=5M6Oh!7x`ot@V{NG01dkS+NSi_Wq+GRYLDSTxfJxgHeAlO{|c^|Jr$i%SzJA))v z3%%>#^Z6yRayEQ;q*pRoPkHgml{O*HI2U~R2&4r88lU3Dq_XR6yob>_?FbzZ`64qK z2++?1AlGPEoA2LaZ@g4>3}ru}wFDEz1ZyTqAncb*w~mOg=r{>tlw{s*s;dC!Vxpt- zQ22wbq!LZP^rEFqRfVbql*ugoi8Z@h{+bb+OFrDf0!aj~CSp?lib2dnUcG$DN0+g< zMH~63NjOo>=&VA_*P!kVx^5P3F_`cb!drA}DhC^_p}68_*&(SQCuf9X>G$4AC*Zu+ zqE$5yds6c2R{_9OV%d1O%_BpW8M3xHwO21cx4Y-_qX;I^K4X++!R*++9b(D+sJj(z zT!f6Qj?9nif)cI-J^O^+2f%g{1n@(Gm?SKjyAHbk;^MOL;TEaf%L}NwgI>mV%Q<_t z8m|q;lVH=3Q`Qk@u(yVvJ9p5}Z~BrdqEtpm?>K)DGQOQZ&Zd-)Kq1Pzz>fnC`eVhz zl$3eAp2&V?Xb&GQ3Ek6UYj_sGiYe5d_(;rs^)0jv&9(e{`ESt;Io9XLd)yi<+fVDX z%PT5&6|cKtoz`{Sjz?HQfxlxeU3$;y(&x+jlSDA%SZoc-aWJvKQvY5rs4p^R+ zHo9Vx?^va6H<1NQQ?3iL8=JD>I4W2su0D^n8{c@F>TC6AXl(R`3^&<=c6>Q=5kRl| z7EC5}2i~18d8$kN_%>Df*)YVJOE2C}PJRnyJ7V33X;)#(8M8?~p$-*j@Q4f7!;yvY z{ENKy>$m?h(fD0S#}4bl*DH;4dNudOss69e_PcSsY3YwW@3uOXPyA)Xe>d*_Bxd0z z5!eA0Z#7JvC;YncaLY8a)v85l2I~9Y(z6eE)yxOK=U6^@@Fl}cEynSPMG{mHZN4kT zoTMQPIla!_7j}Iv18C^OcfGTB&RB0?64Lp7@y^xX9l8!(STOJO$9j;7-K!>OV~pOa zefQ$r`6cgHD1nTf?KdRA%`G9vo42bQ)+R!##aVNzzW2V|F+1ycE{!7T40g`gapfWi z44a*us4K=>TDC`M)AacH@)-=_E52=$F2U#GeuKPnh2(C%T&3INZIj22&0U!+7OB~b z26XM3xYeyLQ7dQLJEhsKpM`EF=jt)emEBZ#6PxucYU}e05B0zOvbZgd1^9dL$-J$c}9Ks?NA+ zXa1a%8M>hF^PckGlX0Q$^K+JcX@KEPMP(%C16$0b*B-8ygOp1#l2nsoS`9`EBM|Cx?D<-lWGUO^k%(DdpGy|Kkj zJo!I>>!cZDFAS$!Us=h={ zqBoQVk?|CvmDjB!l0YC4ZMvdYjEm}Y*vF?fDPnc&)w^-vm+oJ#8+;~f3+{VI7cSYLl@akf6k_j5LXaRQrWUbS6kQ1*>~3?{x~OI?yHSC;cLDQT=iy5 z@~~y94P+J`CeO;$72&IH9bhsY4KKwkQkHf|i|Z(`_}^lBxCr^sw85=(J)ZIRKyxOK zv_MSTBGtd&Z1v7RnDwEjw{>{(Zck4M@;H(4vB|>_`|XxW`_(6pCEyi$woYUvc)XZo zz~d*WkK?$u32jMc#yqs>kl0_%p4()Q*4hO?mO(Twh9|f3n<|62gTC5JBw?8pdkMvX zdKzzS-I8uvZ9@Ee;o`;Z$Bxa}6L%(* zGI=I<(Qb4XqHm{8P4my5!tyrcxa*Q?^Dh?I`p9rZ+qZd8GK_*Ds=v2a-mNVAVN;rV zz@|rz{gb;MZwQMp~aS^2*~Z{NyfmveZC@*aI$_h_*ew^RCxn#T@^9b3v* zYqf7m4r7ZhU79Pz>>9RvgrD(tzC`y@Q(F2bS3N3X;tmmsizyiA>aMjjFotR#plHBm zU=E65cHdj@8Y*W>i}a`k6Blc>=l;fD=qSZIRCk=VQY_!^q@HVi=nX?$O7IJH-=z7s z=0xVkjM^{-yI1z5m0rA}Onls#&$bS7JO&AqtS!)3#m#_g(=Z)~3D&BA5>CPrbHmv) zXL|hdwWCAg2+HGh5G84h<(G+*RRPxxsV9$TS063_TuwmxC*O(bF?OuSwjj~S`{lhS zFQX0zy8*~PQzyNT9N7glE`tE&-Qp$TJ~o0tLZjSJfnZE2(`ic6uP3X9kX$8w4fV=6 zM@Jot^Hz-%_RIh{VHzU&WGvOW{MFgnm-=%mXhNAVg27g%Dqp0Sm{i|i#014)=N zJ*_Js9S<=uXZ72BhRz*a+>SBTr8rH}DOT80-++}q2~|?a&|!ue z34zVoWgV8B6J79LG^IS|I}{R$9Zme454>-IHWAEe9^Q=lGaN@T`PDyFla?jYDk7^p z6qxq0Dc8SSRCgfxREBH9C%RCzuJ*s7y?P(1OOTxFzGVMFZFGI+W}Jmh+6}79vlG`I zyHS$@C7@FFqg*u=g{Tu~Ke~~VMUlcP7GG*F>w{~VU=hJM@doFEM6?osN<0$lkJQ=Q#w)1qTvQu!+r%d#BIoSyY7%3`@y}?`MkqZ>v*N3J zSkIU7OSms&U}izV4z{*kZ3gLFIfgwarkPIY$x-jVWZ?- z9LVxhjM3M3yEW6wM0ZB!Lbj8!ycCf*ZV3Uqh3}c;ML;fqGCIc{=(z=y<;}l|SGT@G zCW`$XbH0PKb7zO{ywa|QC-wN@ylaWY1Yn8}6zj49HG#~dE1LHW~-U^#>70pjSyKi^$yar0>o< z={5Q`6!`^cKbE~hA)Y&M=sa*wQGatHxU(GI@;ZpdMm8d%1Y}AU#>eS**BXpC5!A7v z0+TFEdPQoxtY1BPm_`?M)UEubm^!~r)P+;Ky^4X~*Weq-t)XN8oN=Z(bvwDED*%4R z=){tJPUBz9;TZ#MWgXGeh_+z|u8T!%+Mpj-Xc~Op@w`VYd)n7$K1A=aW5>#3n9Jjd zBXX+FFCitxGa2CTc6m?Q`Yn)#e#ZHOU)Y%a65NFrAO5~qgC=cN4>$wrlS}1YT0>2} ziS0T4Jg8~WvzqJxbV972_L1bPd*W_~aX1Awh zb#Oty<0K~4cmm($Zc=piMzT8S?dTFl|LVkX1iaCrMt5yNlWXe>)vLdK_;8PzU;EVf zEH~-7_o>l7-KQ5Dz0oSpV|edHBz{;$&f8vELxSRBJG(nHX8YCB0(8^R3td2x+QspE zE&jm5k$9D6;31(X$=$_P)jXV;i5LQ}FIx0-MZ1 zwA=J!Jfbtk!Lq>{`G{SSa@wnl?i8Q<4>cu3nYD>1Rx+aYCMuic5CH4D!ziG8Po zgCk)Iaw}!xnIpXK1RnqocBxiQ?14^d$-L3rT3@b|&9su5F7#X~`1A-~Rqz0Q$3d!C zMa}oL*HKj+Q&JIn_I?3RkOmnq=W!a3zP)-4Y+DC!)A2F69t2UUDvb2?%(bTiK?VSX z@Lf^41W!5B-H3POF(?vy4-)UpF#P~I{pioBW7YQ;A5S-Rj5azk*U>R0HS~Y-aXre z3)LGmZ9jitGu;RF-%kzgZbSz~&TW9&bBdDABRA}pMZ*WX^)2o2kXc*3+V10F+ zCpCFT33wW$tz90HTW%!V+RTT%*G<=0t$*KQdyfS=-gEAKu=zTzw4mef9z6!-C#e}e zIPzdm;nP(=cC4@LzoFBmKPwLx{p)!hhX6BHw+z#C4O^2>woCl93Yl$8CPWJ{vtg}Wv1&P$_`#to+M}=B zcjQQ)1M#G89wY+*rYXa?hsuzm%#4)JjA^*DD8Ok4@W9Q4gj0a%nIW#^f57X^rTlKF zq!jk6e;6nqbcx5`f$5=-(?++qQ(OHddq`1=nL*L-Zn?z&AbfIk7?%SZoHOa@{zNJfUU)=T#KnscY#!$fO4w8;Yox|7sNO+ zILsEdOaTFS^lWR=_-bL?>|Zcdftuznf^h_eYRAnwwR?k3=mHD^YWrqtq}|q7ttauJ zzCvr|>yFe=UatyEHp({XSH6f&Qph>Fqq(@mgN}X#=0cv^((XLhGqvgn3J@WP*bREk z3Z2qX2s6B8di3m6yciG%82cf(G^n@DGQ4#vewk@!rgX|YKc^zHBDR(-tT$WgIBH6h z208y^wDVp9F~U07AEAix3{0 zriVDV*(9#ac(#Z$&uhmoL@j(i55))`1$+XqgMwpMEUU06^#{S zBwe~S+{AmOJWcj6LjYK@TEZT@0iq_p*pSE|{hyOD-33=$nN)?D4w85oP7G1XA{h3{ zzF1Q;Y>#N5W?o&V!Tz;qU=7Z-TC%t?yQS9%e)w!}#gBU#eNwg#IMR4gj8t)Ojtm-? zg}u7NtPu0tgz>t4C?@~a-KLwGc5BuQ33Gj;eF@Z1`lCX}1`3&h?Pdq$ zb5iSB973+9A&#f-X2&^=q*6^xcDUkr{_NR?9L?_55#yIHKeza|u8e%QX=lI;B2*Xh=KBg&16!kEFz!4cl5`gD)=U~h^#?lJ8C`qHn(mDB(5k7$hPX!xdLH7udE6qlK3 zCDbYtmLz+ON1$-wbgW}^XV)nTSSdPXuwrScJ-bmm>E|g$Z*_|cN~}7WM6Zloy^rxO zskwR3w_m-wk@C&K0xa2$;hqfMsmDQ-yEbb91%aaa`uj3EFL5V}PMmQQj)*b|4>2?~ z_38Do{2Fl3>=0+zqLWZo&&ZKMww>U4m=+d~W7oY?^S_j8`TkBk=~R4f^@*%P!CmrT zuM%k--{j?W?~EGQpRqYNVv-P)B*7x?+?*;t&#dIk3M($-KYD@iz&DSyat+ZgQmHik`kJ-IWIJ{ zX8)EmgJ_?6?_DplPza@4ZA;>FG9Uoh*2Yj8(;C6%aG6)-gI!* zRb-JB&CA9KBrpO!PA)sWu=f}-6O}@qJO~6|Rg*s5r1hNmElw;APbceX(_FsARZMZ* zn?T!>V8D5PEpfy7`vHsrjU-8geDu8dnyLY0AIb0zwtvs!M3!*d<@bFrH8q0x8GBy? z1gDhBLyX`E40QZ-Cm+^oB!RxX<8&EC&sGB`p-7zz0-0-ghrKhB0MJTm3(|x2_vmhl zit0GCh4XXmJR_fbh^wB4&G3Suqdyz`;K74Uq^9+cy$Qi$q})3ApcrCC z>3DU^-nDdHiz=t7+B&$lxja03W=)W`;Dh;*FM7kKU~^Whr@HzMDnTaX1)=J7uK054 zOjP`7HA|K!{E`;Qy{Zl2@gO`~h<)p=nIFdaw$TUSo8{{|yO)~ZkJH|yoms1ZJ=_&W}PbFza8UfV4$N3p=Smf+BfB=Ol5WjsWLpOwvOL8 zugPb-xAbR(b;NrV(;vz2r0s{L%Nle3WV%dM9|uRoIcKDEfaxC)ZkW5dl^*$&zq>s= zPQY`rEj4-P=a-mo)6erdn-4$d3-&su8|o<^JbJWmiUmW+vF?VXFFr`zwefP02^z=e zq#0eka;0j4%15eSrU!55ky05P9lWU7Plr+D>nFtZ@t^89u>&pHvcpj za(GNe=uF|tOm6Q-2+Bn*d~n!Axh~sE(Un3?Mx($|*3q?pdKD-6K~RN7z%hJ>8`rNN zq`&0AiCy|+C)VfrkTyUYQ}AW&7aeS{n)lTG0X121^hHs49fDRuoih-`xatQolFY?g z@Q@ubr9o^Cr?>DSnziq%|J`}kcgFw6@T@xOPMcN442|&r+PaUVKu$A>o%y^iC(%U6 z&5M?kQTUsvI6(iFmzD~NjoGCBrJ>l}*RVmMAwY#N@3^s%Q6TQzDQ8Z`$H(`YnpOC- zBVZx>(MK%3z&iM-Zam&{{@jBJYdA#Udx!M8#_3#9@^M_a#YALNnt#U?HTxkmHt2$k%c0^*hzdFcDyEj$|d46c?*3NY1zOA_x~0 z^BHER)hi@IZs@Y7y8$HF!$#KAJDWKym9n~kU3z6v4JbX^0TPEP_s>ZDZSwy;JA8C4 z!n;U(6IeFR79ZH3vSy6Ih=HR<9owdQfhJqPAJv900n!JD4?aF#Wl78c4eyM#%O72Q z+ONsa_3kV7U$s9OTjDTO@hq~eL)~8=2mD1Y{%)T|d0a>4VSaT#Goh;M^WWMPdXp2k zCg`>~G-(I#(v5W^Cv@xGyD=N4-|O}!83Z|w^J#bL@ zkJx%#N#=pWQ{iC0-9w+<0;n99r7cDvQ`ZR-R;wm%)gLkoERk}mN8F`oI_Uw4d*9x1 z@7}LpQ>umfQ)g85p#|9VHbVBmIu_?t5wWQp zg?<6NPY209hd(5W^wx~nxn=~DynpY`ap+nM1XA+#mCy{}7+}l=%kui-FWU;d1^Cl_ z!ES#k@!u2{DnU;B1#!qH1TT&CI6-NU<4wT#3d0g3o|CfYsL(fHX z=|07UWEsAtZ-BW-U^UkkOmdB)C=;d~9-N0&!*u#ay#z5~gSd#Hdaggmq9_+7IirUDx*3x@^%; zMyAuS-v--ED8|AgrlCQHn(!O3xpPQU;)OgUP==@Eb+Hh;u=wjB+1pLJy37Zl1d5L6 zgw~LW{D$FB&RG-BVJQ?ao|N%$nIyg13rCH8j!g;5`i>w+%%f>bSCMNw5H}8ta?O=5 zsSh4(w+uI=xKdV8sZDBI|GHc*7*&0A_3(&zc7!Jb(sP5@Am(6<3dGJh>YyTMJzJjq z;-}}@0idG*q)Yw${_5kIhnz5=$sbd^L5Qg~tc@a3p#TQp9hNvp_D(VV0QAWxr(+PH z|JS#i<`8}BK4mt-i0Y_*O{g*IAyqnVK(Ouhaq!d9*0=6w**5ctnhe1dYau_Yt?riV z_I)9IL@GkbEZR`Sspcce)debkMr%2`{6lv*2czyU`|Re&Q=Ji_0f??cocs{ zok9e3;18(*|AMHJY*CgM_XX}VsWFID}AB05cG(Hs0&+ZiTO~*n%co@ z)L$UpDw^~#zs<*eFX5fBB{2sQXomBxp}+~k=>jweQbc#Ds4RPY8!+pvd1%r@hR28q zA^qz5WeB+=ucOkZ0A8FPR8PXd=s6XoYultCsOR59x`}z{0dVS6cj|5XIPBdVvOk}_hmzX`p%zFhN%rz!Itlf-xIC~gmAsPl(y%|R z;+jZW9}|pGRq&gLB~PWVlv3bR1V{u)Gf#P7GtUfohPXlDqbD}>VB^gskV9+S4LB;%wutz`+_Vb}kLQ?Jg z0v!{`-losbG%58_BSR}59U=HsG-g8lP+*UvjMi|SM+t8-8U)S8vFB(hHc(Ego}DXx z$Jaa%Q(M}^u`lnw*NzwGX>hS;lFYQB@&Ca~IpBUA0UH7|@CM{VQ706mD%}kpt5V}k z2E^3kIvTOd#*W=)|JnJ=ROGXy67PBD@9V3NqZ>KwV-jO?oNI2__XWw|n$~UDP=Iin2|#i3ZsfP?cR+)D-tiKT zfbK6^3*-B4hK*AqL?I57VhBc*CTPH0=jF}%B7>|Uv#I;LREUTJvc{tZMs5eBOv{>$ z@VmV66MO*v-*9eE@Imlj01z9%$*tefFB*U9`I(VpD{Hs|^gnOuK#Y7uAx*t9)5_{T zIujT12pF9{b0!)b%-LvkufBcf19~4kbV#E-5Wvh{hfhY`A|AYD3|9v=GNpWf0sZ+md8&lJ< zxGtcbSykKE1JsRx9A_MzX#O5vuvwxDTY>738nf4(Xmk{AE{X0~6y z0@-qfGNQfEdgd^cVJN(@nzRM@TBW*Pxdj_fMfP1 zq%5cFU;ahjrcu9Z$B@>EYrOspb(y}RY4F1sq{2L7$NSpW<4eQr|6uXW(_R0vrq=4(#sZ*cI zbEJ3K8+fU4(BU_imc|%^)Fv*BgR{NDyeXvGspsWP|SB03`~_iC+xe>c(cl zbM%(YCS#Pk@5*HYC}F!`lhDV0yKO_0*!(aWUh zZz48v&f8BZiX=)PW)#?>ol+G&64Z#rwf^WG6fQ6)cTh%gwNuR(RL+@o!(j*t+1@2n zIsMLAUTh45HHPBIK;t3p>f3?>^}AMobEaF*luriY;u>x}e?IH_w;1i!61gN2cwxFZ z_PZG$fs+ulDm@GEYi8$Gt=woDJgzBIHVrO)hT$@S&*&YAgOB zV?o{*;~$YU$y%jme|BiorUAS9ho258Yy*)`nm!rt;Bd9AS^l-9oP9~6v%fO6PxKW8 z*MBK5U%tGs`S+is_&s{_a?SKR8Sfe?zL%6F5V%$g)F5&I-lU?}o4@IuOc#A26Fuv! z)1DJtb|tht%)EDI=yWBR+fo>su;s2)1`IW?qo}KQlcxYpl^FT!Kl8I+)BYnrYaeWf zdrE`WFJWlb(u*)rA(^i^xLm(!4I1R|`M}okH5O0Orlw>l@DOa1d|T;c>A=KxO(m>5 zG^=+&ml=L90_NL|m|DE&Q|YYfBPy99xI?ue1Ygjt%0f;m3F<(Rr>8=dpBrvM6_Q() z*1dT|uLjfIxw$t9*Ltb9>_u!p!cD^N&xW7Yr>C|F=@k24*j1A-!W-rF0H-=hUofv8 z>Cp0q{Y|cJs0J|5Q&9uW?7upS5?&rSBxP5cm-c%KA{$+UTUCEhBnIZf!anNz)|4^q zp#@^k<~OXpNQOgtA$18-8@85jki z`Qp3vjC~dT+HU%svI3^tNCuM5hI2>{Q3tmk(AXCno|jN_9Ex7Yeh@}a^E&A$lRhg* z1X=a=cD;@xH++t?!^A_jl6yo%L@$5Q#|v4>#-kJ6NzI#CoOlGNHgmb{)B789r0gNCnF8$rcjlX-x7tU&@wOCm& z3#m496Fk%A_|w?bD*bW%J;>@!ezst3*pQ3p9c1b;%DbRbh2IRp(t@koG3=PQn|YKN zcxP04ZlUOgu5}we+V4_c#E-gn_A zh5Z_3?f2Xf3eJ(}dLb`iZMHKD%RncMZP~X2!xKAEbwvz{J)Y^$Rxu8Fn242+>TFWM zoi_b-wE6U^fRd5ZlT%ZYu2P(m>y>HSq&(*v^9B2G+qznpGL2y*Jp?A!fSQw=a#0vZ zlq5UnEA3`7Y3-YKDzQCVbnLjBSEJ}R&$wZ@g%U+08-+4_9OQESL03}B%ZxT%J<2dd zTee$?pGl|)L(9DymGbH-KRK&Se5_{uIiOdszcO$*&1pH#qzsQZA29vNZ`d{&tO1x- ztFr8z2YY88C7>Ia(t-YSEc32JM|Cvs#72wW{4Wyz)zv>xGi`H6yU^lr;6B__-{txH?t)16XPR; zoIG!hAeXnk%2k@u3*2EH22F^5k~fqu`VI*;!2B?hy>WiVi6rEtCndQfChTBDFXqE5 zv5-PTTxN#)`qz~`E%*df>h-@^4$WliRoqSr80u1jhjKkfn%s=SJe_!*ZO zJV}K4A>Os1Cb1j|I0sL=ySw*Fc4+ad9~l~;M7d18nBYTs&)AB71FSBO*<*NzA4Gv} zf;*AhT0dtbCLKiBQI=Irw3Qna=}{8 zY+J6KXz7XD$96RyiB>vfTCZp4W({t75#GvFuHQG)sRF8nK3cCJC4C z09@HpMMYemKn6vmjw4&>PJTo(eES-5c_Mt(qC$)EjsCiYczTqb%#`cmBcAaofX5ch zN5hZ{r7TuiwoV^46sU0a4`H{EejjBAT!eX!(hTG3v(V{%R#%Q9GM>Be{FG5kjxZVQ z784aEx8tXS)5Tm{sz-P;ICTsjLEJX!blE5B*Q;s0S|RSIOV<~5{5|hu(MmBlU%?6Kg(IOVDL<^ z2{X2rw1QlncN{DwMMZ%ODv@Wj>W@SV_Ge-LSxBN3)zZ|Q%gl-&KYl#N+68g_>qEM$ z^sV`^2up!s(DSlLnKuheRaIFjQAPM5jSEGFjMavpm!6Sfc69vor)gv?`KIRc$5;D@JC+XQ?RySz!=2jcb1&tt2ccLn@sT{ljstC7dZj&l z@E{m{A@ib#(@r2H@Bl$ScOjrpYDPXf_ybs}hxQ^VS`AkJd5Brh4WBYS5f?^iUE2Q+ zwfb{z%hhYwqNzynH28+;CQJYFNB;MJ$4@Q-V(#0g&q+Lc9MSjSe5R1&3#{J4`ONiW zvtt(Da&d9#9qdLO$cK5$s4YYg`XS2~jOg`WiAp8z%jTb-3{Za%EN~Qf0gJY;5zpmc z1aEG?zJB@reBd;daC$9$_bc?U&;LhGRo{v#-W-WG6{i?j6&jc!e`fHe0g-x(9HMIv zwcZ_ccN+g3?muI+9TGBsdB)r`Std^mn@(P6&%82ZQCPoK&U zp1W}R5R9ddkB`jE=5Ik0ljo`}?YQKYI<()V`KYCyI9zsd<9n*8>>$cB)G%l8%sB)` zSEO8gu*)Ifyj#M_3EAWSA_D*QKY#t(f7nI7OW(FS>k{sM=x_OE^ryBaOJ)AoKfgTN z2N`zO?$*X}ZqDaD&Xs1}ewGjNZPljxlBA*Qnm5rqc6Gf;*n!CT0rH=Db9|>x5f{7s z<$k?>z3AlCw&(7LIyCyGyWR5QZW9cx3i}=n76^T*u@?{O7Wn7C*h_~!@o=U42QGg( zzQ*`3o3YW)eV^c=qrPkgwVLoTwqB#;VQSXSpey87lzUmu$SmFV1=xCrX^0yuG3eyU zrhAVOck-)q}R7sJ3tabnQ0pDq4@ew2Jam%sB$N>esW0*S=pqui=h$;`2x zkS?Q|1O}xID$XAM0<`(f+qYYZ4wx{jpoi4Yf`_87d}DGO{%kB{vdA*2rQ03dPo~lp zt7e$;(3S1X=kx>fcPq6%HoigmqCKf?8X0jFcccb%`sw$sZp1+NSVKq}fBd4^g!-a7+S?g`GoN}@Xho}qF#iS;Kh{RS(%wGY469`*mNNRi{G4P;@PpA2{DHW zYAdG(60IF1>2kcfrsfBQhBlU1A*LPg#P@Qyo%nzWaa!5WhM-5n0zuGSX;EB~@-&rh zK`1c9N9IfNt)W@l4*RnswuNR$wLtYgzu45L zZg<{_hE`Y!#<)D6p59oT2`lx*5`+!gxEeU~GYvRPzjpZY!Q_&^SfLlK< zlSzw-iTPbRoe{zZMtjSW>L`?_^o66_kDL>|uqg+-C;FAHIy%><$JwFvKyY(&b77ip zc!w@NUUKa)y%Yt_S6#6}dKi;Z{~b(6ItQ3X;ymJ{ZHbu>Ed@ixN65TX>g$U;M=`}tm7^%6 zFE&$f7Bbcx^WugKX~5hqY@(GT+bqWjC&UeH%p&a1+?-56QL-v6?)&DYc0tsx!tTXX*ZBB!*nT-X)>h^dM{u+>7ov->zDP z{<5$2^|q)i(Y)fZt4mbd3Fhy>F3X=&r~__nWtq?n)KieyG``}}{aa2&Td1>@!I?z$ zi~l|nRBhol<%V_H)o)TyUfB4}I%Z`~XKa{ss<+u>ynTJ$U@W&`u4KN=EmVbXhzp=& zVK>2E-4H-9yjzZ>0uUiaPy7}JOz1qsnYF|NDr0vkrd+XO*^+Bz&%I)3L{oq&fF|S^Z``t^5X_}^Gxs_KSVN_pzCoBJ3ND*+#{~;|93Sl@sn#;f2DJt*4}}YT_rMM1 zK~)zs``0vrE3F?mts$BPfT*sp7_(NnySed3vFbW9T9g@(TLuiuFDO7cEn;8g#@j`Z zt@zg-c$0jIfdq(wb9N;~OQGDhZ9|@y#8>h463<5HBr@);)Xe;Vt!eMYb#+3p@%+qE~lbO~5V0EPHxT%tUmXO+YmJ&b%;2<6dK^S^aKvhrVtfFA2=- z4Tb@uoltMwJ{ol?p&9$YWvl))vP9Aye2`6~!#vihBy7_Htp53Fda(U1A__P~P~$8} zp%(~b9vLD8IsX8IsiGGREr95j2m#9M!AFh=xr-uRqS~1cpxINMR%RToBT8)%ikO*Y zEc~{I2wG$aGRCu2V^PznSM{dKB6cSc)66f^UwUTOjSD2>ncIObp6qFbf9iI4Xik0Q zHf`M5u)7lW0bsVB8KkM>3OXH*uHTe)jC3_uJ{*CGps@2j9iv`<7u}Jno@=#7ieR z#b5#wovZH#ihBG0{oReqNph{rQj(Waj1#mN1XFnOd9W|E#BC)GUYX~G7)qw@GVURH zx6#Cjci6ba9vcPr1p-HJYqhzu%`d4iUZ}!+#;X#YW83#3jH6b-Hv3$oka+-kqIV zm{k@K)6N_hx?T+#an>8p)Ags_%3am_bU&6ba@UflUpro%*1!9q>szhov!Gjb>hz$p zQ@eIXEc+gvy~fx4(5)HABC-1fKo!CiLO{{+;3N!XWY7)t?YiGz=PX^k_yoI^_&!>+ zSn=|*w|FhOb#tI3eE#A^M&Te95hJkd7rYYblFUE3(Qk?8$ehqBsxXhV_wi9*#Z^PZ zjwc-H%caGCv~F!G9UMio78;Jzl?;Xt0!%wlR>1%`d@nr~T4g+beDG>~YQTX5h7|4J z^o39zG;CN;z-Ru?8PM5*>oG9}a$y`|XP}4%!GghGeIqgfq^m{r&|y`ls6}= zPk#`On8S{o0+gEfZr*ee+9i}>h~0uXdmhVd%QV98A9FV96Kgvk^)>gUJGc;uG?8Az z{376a?R7dQCL~;6*R;R?M3RciD!4;3%A|4yUrtsLKM$AYG{l&U7VXg^ovtD$yZ`Jv zd)q(1zG<*T0^gVEsokKqWO`(a7Hq?Ns8q)B6HC}yn315;qJ>egDY3Vk2K?Ac$IDj# zmBQ8y+!ZR%iN=077DS#FNT$AxRlpP%(7b_(;}II8@GpC3Ga?30q3%Aiu}1}$Fx z;KIFKbdPhTekr`cAK24aR7@1~LNaQgpV@6B%0zX+1&H?D(_uVNLicV#RQUtfzTk6& zfO44QCWhCN={npWHUCsbMNLSKhub;Ce&grwpTYV%8ykC|_{WbBGQcKP5-2f|(?+dZ zHzxFm?|NPTK7Dq1cu=^@V&ej2Bv0t{u)|+c*`tIe(35 zuU0ax)NtzI$4#eKdF|N~EK?AuLl&c5Lll|yxh;Za&8vRIZE?Ky{`Q0OH<>gj>N##6 zm@QB`YgNV5jb^~p;{!((6K^84{*2g`l$Q<$#8<&yYB6WZZ}c{gvj~9i*Ax~OCKHSq zd|zK51nUHwxd|D9Kfk>2C*a$N%`0%~=rXtCy3SL$W$FyV0kA14`DoUy+vD`~KCP->Wn0Dx!zi}`_naWb);*5uJhn9c}5!pOzmJw}mT2%a?au5(#gQQk9>t<22sB>2WU~XeGm3siZ`s#B>ef@VBE2x2MF0b5_?tJfE({+JXh8?zS z)(@JK=h(JntuE+ zi=!Am1IEhL^!V8^sxDM79HNwJ2ak=QoFNx1h(il{@($<5A+7Uw&7;`z=^x^L);33k zOEWevveebO*8l(o&Zz0xdMlcOZby9ZJCfs4pE@_B^qNY1qIok5~7;IP_}^xHTqh{Ojxip;hKG6NbfTEgCate-f}6E zJHHy1k`BOyxwb1lTJ}>ea`VYKm2Fn9TbBS}xDN{vpVKeEDgCZTLx4v;cho9iezLnujqTK|>c%R-cR2s&ju)*1S?=*Q!p z(6B=@))C=Zk1LY-0m43Z&YVsNNfa*da_X~BaH!F#%@rprx@C&XTinv2{fz4DCIyH` zFpWX2-M0DpJ(>Np2S>OvXkyg{YzMn4QO^h>U*?xJe@aduDmI-Cun17 zbhS}$zd>vJx!;j!3%)!fXmhCXLVCw@WHEzO9mI@GlT>NiIQz)`mT;g<1ssD`@zCZF zhV#;2jP2W+(xElY;>uq|ZUyB7lpBf!EBKp<0pzd<;1tT9`V{XwOmnKY1O%IEpv^rr zX&>Enfo~Z+9aEkZ3M3D>@T!AXZ}w(93g`;iD0Rl{iauN2xlQGqg+#}Y^J9lJTn0f9 z>TfcK{3NG}T$UtsR$VlvtMGIS!-i?MwgPIymYqimZ3v~{5-W>5jYluylndZx7V9C;@af1rK*m$dy!PIsm`-(k6 z#(3@zd-(o`45#8z;z-#yVjdW+G+!+zu;m)g{U-?+ApxIzQR|j?>NwoSSX;lIxa%C^ zc~`2C^ro+o-V*83?&R`G0b$|ciQEmCSfYTxdv)5=x=ourm~4d$uk?*e0jBaKf%>J~ zL(ok)_wh}dH~H>|d`GiU&R?)V)9&}_w3!#RoLHs#OsIW5aXnSlcD54&ht8XSMUD^n z5p?R*yWpWDS0u5j4xNY`lD`WpBKjsOt>319%@j2=!|j}WYU16bB;KJ;ZP`;hr0VJ4 zd+@-3tY{q6i059MtHuy^lA7-_@Cw#pw01XRTI@=^zjoPxY@4+k$^vZ{EZC07;qB`i zn-wmQx+l+_Yl7}qJf1vyg@XS1eu2p{MB}wtKBA_SKqV>;A(??rf(;zac2%?~9Nw`R zy0C*<$JnmlQl+x`A6OSoZb!fl%5N`Qqe|isk!c=7mVvty&Nn@6wn&zJktPWY-(Z>Dm=AztDo6JViEXv!* zmcNIRVjhZ%NyqU2?e7fS{@~OOYR&Gn*7|i>-DH}mFW6jU`M3UZEU8dzl!scK!w4sW zVyV{Aypq(~TrnK}O;VgOEQ$FVS_Apc;go9Bm^Ai>JayrYU*sk!nBV` zVwvcD`WTZ=_Wx;56>N2FL-?#04o_}4mX~Ny)~qJ5&g$yAM!*0MMt8`vsKA6&ONQ!1Eo|| zc<1ehfRsc#!V;D+$dyl=*=A+o_vld%b#*E857FUfeeNh_gB6W--tS=%#-4<(t5cjs zUAUl%bdieXBr+!D7VI~jz;2ZNQyD9IzhiO@XjsDgAD&5hExeMmJnNuV^{4|vK1 zNj;4P%b;r#@``$?s=9=RhWhSpw&?yrJ$_0M+Je)c;eeU72Y%u~$lww(AdFWa!}2x# z04+Ajsk|f*s$l=tt+o6P9PlB>>yK8gVwNtQ0!<1|^yzW1l1z-Gw%u4TA)9H0P(O4f z*ow0(dl-Zctx~0dRCiiW^mYt$%w=N8M@EVADLwVOF)ZpNx>iOR zW&%llB4gkSSt1DB4LFe}wLeGD`6he&ePFEneV(*04!6E>7$9sc)E#XFFw}lf20vfl zeKY%><`v&RMpbomz*GQii>qHbY&ClK4mJ2aBXAl~6s?#JA~Q((&ADo>F=u?=)@G2( zM!j0~4j#xfCl#-x?nr^IVgn_>I8_7Qnd(JDTia6OhN<72xNpAq*gf_$iG*rM9xOM7 zy<<{#C_fU0hyjn2jFWi#8HXs-F{N6h*Ug!|bDe{OcGI>4T}mBCkI>eZo=Qq<0Q`fS zvks1aaxnWte!cUMUy>{?v}gLzFJKDesbQr2Q3^lIgU`HQM{ zkEhVCgzf#w%eI&ucK1s2#IX5sYw{Z(I(%4G2E75|`BNuP-bv_+1tb`;ul%v6{_Ru4 zhb@?m7#YXzB!k*y0f9Rydx6=Y8PliBlvf^bF`3Lz0s9bi zP+~u#9Zp7cK^Kfj*^8uy2Rhm`>OJ|g9z1>Qtdo|r-o~>Ls9`&h<-zVCBWVag+rvsv z20hU4=4~1}-|XrvE2~KWrfRH2UVq!pNsE_`wpcJ#Ch1ZXT3%k&Ra!&VM2LAx%r)w( z_2SdU(6sF9a^nKP`IZ4`Jqb?7H&yS@!Gi(-(Ejt}GjVqd-oaX;(4VB!Q^oTva5}HW z!KB$T=#@4rlNch#OmrnN15nv?M1vuxH+5*=KF%WhoBL6f`oFug8992R%Q?p`(VegN8EISgZF^GQ zTWmTtmpkga!5%O)(K7#Ji@trkMAe)PpK;=HXtOh4Q!e~4t~YPJ@rl!?>wlc3LH>36 zl<>UvuiDD(6`@wfM-$`=Dl7_ z-&O8)PliJ<_4f`!fRA57`}VEb@T^`3C8gAO9}Tn9*VjMPQBvZc^}Pv#ne*{mlUQHc zn5bc{`SB`4e@8ZIwX=S(^Q^L&{P%3jg(}eJ<~FHOpKp1t$&#PRn>_&plmG<&s2r#YCa{`qxa zW5UQy9~(NK7*=uMUK`sSi@PaFDM`OvzclER6K^63XftQNj;~p)@i}ET_w}D&`E|C> zxe$p1FlG?7V1pJ%mOgx3?(j65s+lwUc&Fuj{{zkE-ik$eLlY@O^hn2@7JALGY!L7X z7BV@RZ}=&&1&o56f-H5TmmF5HiE4;#2U$l)D2Vc2^{+5jgPUr%CH|dV)0aXu0ul9{ z73FsEsIv#V_HAq{l(9*mAP0+ibyUIK1vek(TAZrPD=aMt!vok%Zyd0W3z-wEuJ49^ zn4rbC7@W#`eVa`-B7jOxukW2{E_fdpKUrd3yJ({XJW82IE#m0=`=edSfIS>HW4n3B zl>Zj;9cu0K{K^4==g&8TP7$rQ<>Ub|#cww^WRQ902~4q*?~EkyPrjM9N{XU|Qo`!GBUfjnXnHP4YP8cNZrmC3WxF zvjOj8$mIRCFD;QoU9qDGWSa5~Up1Pe@Af+U#&KKW+U^~@p>$O!Nrk@T$e&#`k|FFj zXo@c0u@;~WNPp9^ zGTwB6dzpJH(VFa^vl`Mk2*JS4rMyebaY^j?l4l}$zGNTkyAc{m_h3gEi5E|d(%>1#kva-Tczc#~G>3S4nz%eO}=#~A3YRmXocC^dat}!QsBmaR^kHvU=FmHZi~fz;|Uoru|fT?}-o{<|tSB!yIWiu*G+=Ouf2<({#J1*+Mh&i^Qi|in{gd z-_Fi1KY7%qGvV8CxMbh#eXg|xsV}abYD+fem0m(YTITSf|3zKLwX#D?c}Aj*IM1VN zJ@=V8#V^3@VyT0fZkBJrbOSTZ!7Vh8tfBd&_@0|hk2sgdMgx6|H&g~}D#j2g*I9SV zZcw;x&{yyb^JZv%-qzG(*RFB2et}>F5MHU1u+cj6FnVGpFIgQ&f7Q0 z9Yq38=OD9q(BYYzn~#||QHdf&{yZiohUDBj0LL=1Rw0xKk2Ahy8H}lzDFrwJ2%x!4 z1lawX(S@O=6J)kS=g+UMwc+l%Q=XD#^c~5uT8|!c(O=MJ%9bz?;}*P;DzUdzOuWlu z$&d=WH;iZ~naax&Mx@_x`HB^*5l|~!#;A@VFO4u{cUHxq{pI+Z>hb!J*943@R#jO9 zs4M&Wqwr(Zu@g`t9U5Z|FphR>fD=`_3LMdPYCBoTEW3jDBM(1LJ&>KRdkZBlFB+qR z>Ood=CJk2l3fTZAOdoKE*`3_eZ*xYo{W52T@skUqPnrD5T9qI(S`xvBqV100&|pf` zTNF+AKlW5G_E<}Yh2|WWG zx1mM=Ut=odR1MXslP0;*fllm+iLV+|N(S`8?xABnXuF!y9j=-`75D)n_{f)K*lMvY z`hE@RJ!eaXW*rxn=H1ghR2s1%D-{2Q&r)t7ZziOtnA%}wB_oS%a9ybQg2MdQUz42iT_*efIffxWn+ z?iQ>3Zex430;;JC4#L2RHar0WhEK0~hyp@^DHQj{4g}=`vz9Hr-~JHB7^Dl+tgWjp zf2EU71!I*Tye4p(2M1We;5|9%e{BrQb{;6ba=LPPp4B%dR(vSj)uCm?&O@C3Ui2$? zd*Zcn#3jN(YXB!?eKiZV#lE_71RY^L(1Q(s-0APm&nmRv%-`%f zXpjo7#9kwqEo##{r)@1GIA19_mznx}-QD zGiz8NZ!Eo45=h-@Wb%R*(;#mn=$v<_2!DGnubNl}0>i>m>o)kde%p$S`bb3^HfdsR zyo^kk+o0GInGY4i>2qM#ax@3?0Au(9I*n%VUJsTBQSZx?b6g5;lhikB%(>A+4r8h} z7Y33c)nK#Ohwp*FR9i@EnF1{1u8B}nU%@QQ=8&B=?g)8ADL`IF1M!Yptpz`qxm1DU z+_}T|TOU(*?kfTgq+r1Wky^;axInLA%GHksJGiB+5K$Her|$6$PK5 zHfC#HLoxg!$vFt1yX<2MkH9Z(ykGzLj|b@=Rat4GW??s!vT5Po*$h@g1hx&R9n*Gw zkavV7O!6}TcOXe-HSDeUiGc&BJvU@XghW$Rb48?b^if~p1nE8q5S>de$VfDzTfZjeXu)rEo`~LpfZRq z!Q>^%Q^i}B6>(?W@A!ih$spON#vj;!e#KjT;#&LH1zU8XpBfsDWlVM9q7+@IS7kTG zp-aaa&t$C(O}ll==H@&WtF4{g+V$&o!-v0YBf=gA!}rWo<(3}aZt48$RGkVsfyx#1 zeBf3G;~^%-#-rHiP#1KiizKq@^LJZa#LwSsbJ;B`1%d^Mg?KqS^!3chK5|9fY(4Wk zdTl#mN9$p5?#D&h1qB7$emcEY{q(2_VP3W!{;cAhsYFG|*3QM(7@mVU*8X zt;BxKVaBPLZX$|dUzlmIx0i*5SjezQVOWbstH4A=77Jy&(0*OZ=;~e;#PM~vW5PVF z+uUPgjd1ESAahoiU8nY-kw6kXF^h7@rK6UY;sE;vO;N0U>~cXR5t!qsxCg_TuD z(@q`u#nW`;42MsfJcd3;R$%|ZgWD@*jxt>(BbjYWu!)EDkarb^(DzS&*Wh?VD}5Yy z#d6bPYI}wB?9mTc0w&#hbo}JLsWL;8gRW*fkl};svsdPRwJIO2AT0)KL_p)j>`>4SCQOq=#0^RhrG)O8G`vUWB~umtoti_qAh&TobC7Wd8`ebx5#JE z^`fzH0pERY-uPdXj8ouJW|Z`kfjCHgR%rhW(aSPC2ad6|9PR%ip#0nb?HIW5WbmIM zqeAAFo6t$b^HIB09|19FoQ+yNl4*-mY^iU|`km097ewVE%P*@!JQS7M&3 z^j>YzgK#9|T+2e>gYPCu; zwrBTEt6w+nxkue8oJ8h0j`?=XOs^JL`WF|;Z0r5wcu*dlJgZ4?-DTNNMHu2NN?f&VG(y|(Z|fG z*`JY~-hphEed`R_Eip%oJ^~Fc^l10^bWVQ$I!dDWM0x@fZeDBjtIuE?B!?R57ESvd zut811V27bkQH==|Yv70g!FM((Se9zYiQuY(U9^ zjss^Kbr@RJ*q9VKuI?{>i`!abQS06VKot=gbmNrs5t|@W;wto(;lA3YNH5*HgFj8M zD5mjFtVG{$0MDaG5WMAt@Bq%AdP2MBTIKG>vB5CN{-5GEN;t1-r~hWJX%xTILmx0PoEP7g(NxBzz(n zoJ_wSu!1!QJWbYZ0b%p4!@?btG-w|Mcz*s0m|RPJvjiGlWn+^~QP0oqW){l48i{`3eq9|H zQ6ivLf2arys$Sx+&p;D*IP!chJ*B8b^l<3Bc^;%9D3pEyJS&l@{Y%_MyI?YPAFR<8(u%YP*N#0YLm!KY*f@7XxVSJ< zPFOdc45YN0=E9|7r*`hqF7$B}0Y=KJpjms_Lha~y0phyf(BQ$%ne6R7ieN4>yatNV zKR7OmYj0@giR5Sg(PhW_*gc#X*ryiqjAV9$K>E0F@LN46?Ad}I4vsHHXg#`-L~jHb z(!0w>$fs+R8geGmKBw2a7zE!@DB5|-i~zk57XP7zCPE!X9svK1h>GgNlQy6~jjhZ^ zh38^Qa&jkLNP*RrN=$sdq24q$F}W^GlaEg-@n|oT>X|nFB^6`u-y7)gQF*5KVr()? z#G4R!;qKk-ywrXA^((lc&zm?@%}_~mNCRw5%z^OH5d^jrkZ3L3i26}Qwj~UZhc~vY|SdZW-`4N@ui{>BER1U+``|4Cva2$qh z$fn`(3Oq`^%s{Z%O?#RgidZy}%(`2%W`n*vNSk5QKWYL7*n8_APS(B4dzNqMd?cnvv7d_pMfaTRi6d^#HAXBc(WzGe!q!14jqrnTb2M;C-jWc6#I#CU_%&4NQ zbYrV)--X$q>s3`x@AT^Y#4>Mx(vtEoc8r?8t?X)L`1Jnu55r%~FShr%T@*j*s}|4g zeB1x^71QzIX0wv|nRM}gvg_)$mSy($4$bUs*!9h41AFpSJNE8o?k$hzrvo#06xSH~ zo?4Y^W=B90AZkWYgg$3kj{(CgQ7-g6(RSefvJ+<0?=5niThR%~zv6U?6c=QUpim)! zre`25Oh|HMco}KccTBQfsyv%KgrDrt@x-i7mXn#^2Gzphx@UctwG^mw;} IF06Y&9?ykG}{_cDCjdA~c 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 diff --git a/doc/old_ramses/tools/profiling/10_Profiling.dox b/doc/old_ramses/tools/profiling/10_Profiling.dox deleted file mode 100644 index 6d025f23b..000000000 --- a/doc/old_ramses/tools/profiling/10_Profiling.dox +++ /dev/null @@ -1,174 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** -@page Profiling Performance and profiling - -# Overview - -There are multiple ways to find bottlenecks in your assets, your code using Ramses, or Ramses itself. - -# Inspecting the contents of your scene and optimizing it - -The first source of performance problems in a 3D application is usually the content. Having large textures or geometry, -heavy shaders, or simply rendering suboptimally - those are all things that can be easily solved in the content/scene directly -by a skilled technical artist. - -Ramses provides a tool to perform such analysis, called the ramses scene viewer (see its documentation in @ref SceneViewer). - -# Looking at the Ramses periodic performance logs - -A great source of information about what's going on in the Ramses threads (specifically when having network involved) are -the ramses periodic logs (RPER). See below how -to read and use these logs. - -## General periodic logs - -Any Ramses application (regardless if it has a client, a renderer, or both) has a periodic log whichs looks like this: - - 20210212-16:33:50.770 | Info | RPER | Version: 27.0.6 Hash:cca2a0b60f Commit:28143 Type:Release Env:(unknown) SyncT:1613144030770ms (dtSteady:2023 - dtSync:2023 -> 0) PUp:31679 RUp:31575 RInit:1 RParallel:1 - 20210212-16:33:50.775 | Info | RPER | Connected Participant(s): 00000000-0000-0000-0000-000000000003, 00000000-0000-0000-0000-000000000004, 00000000-0000-0000-0000-000000000002, 00000000-0000-0000-0000-000000000005 - -First line: -- Ramses version, commithash, commit no, build type -- RInit: 1 instance of ramses initialized (framework) -- RParallel: 1 instance of ramses running now - -Second line: Participants connected to this instance of ramses - -## Client periodic logs - -A Ramses client typically reports logs like this: - - 20210212-16:33:50.778 | Info | RPER | Client: 1 scene(s): 123 Subscribed - 20210212-16:33:50.778 | Info | RPER | msgIn (0) msgO (0) res+ (27/32/29) res- (0) resNr (218/245/231) resF (0) resFS (0) - 20210212-16:33:50.782 | Info | RPER | scene: 123 flush (60/61/60) obj+ (50/75/62) obj- (0) objNr (2670/2720/2695) actG (1560/1671/1615) actGS (30988/33020/32004) actO (1560/1671/1615) actSkp (0) ar# (173/197/185) aras (53460/54325/53892) arms (408272) er# (11) eras (1241) erms (2388) tr# (19/20/19) tras (1099739/1114657/1107198) trms (2097144) - - -The client-side periodic logger collects the stats values (flush, obj+, etc) every second. However the time interval -for logging output is configurable and by default set to 2 seconds. - -The collected values are printed in two fashions depending on the stat value: -- `suX` lists the largest collected values of the last logging period (value1, value2, ..., valueN) -- For all other stats there are typically 3 values printed (min/max/avg): --* min: the smallest value that was collected since the last log output --* max: the largest value that was collected since the last log output --* avg: the average of all collected values since the last log output: ((value1 + value2 + ..valueN) / n) - -If the smallest and the largest value are equal (min == max), only 1 value will be printed: (value) - -Examples: - -- Time interval is set to 4 seconds. The logger collected 4 values: 20, 25, 21, 55; Logs will print: (20/55/30) -- Time interval is set to 2 seconds, the logger collected 2 values: 42, 42; Logs will print (42) - -The logs explained line by line: - -First line: -- Client participant id -- List of scenes owned by the client and their status - - -Second line (General client performance stats): - -- msgIn: Number of messages received -- msgO: Number of message sent -- res+: Client Resources created -- res-: Client Resources destroyed -- resNr: Client Resource Count -- resF: Number of Client Resources loaded from File -- resFS: Size of Client Resources loaded from file - -Third line and over (Scene related stats): - -- scene: scene id -- flush: Number of flushes triggered per second -- obj+: Number of scene objects created (ramses::SceneObject) per second -- obj-: Number of scene objects destroyed (ramses::SceneObject) per second -- objNr: Number of currently existing scene objects (ramses::SceneObject) -- actG: Number of scene actions generated per second -- actGS: Size of scene actions generated per second -- actO: Number of scene actions sent to renderer(s) per second (will be counted for each scene subscriber) -- actSkp: Number of skipped scene actions per second (usually an optimization to avoid empty updates) -- suG: Scene updates generated per second. Number of scene update packages generated for network send (might be more than # of sceneupdates) -- suGS: Scene update generated size per second. Accumulated size of scene update packages generated for network -send -- suX: The size of the largest scene update package within 1 second -- ar#: Number of currently used array resources -- aras: Average size of a single array resource ((totalSize of currently used array resources) / ar#) -- arms: Largest currently used array resource -- er#: Number of currently used Effects -- eras: Average size of a single effect resource ((totalSize of currently used effects) / er#) -- erms: Largest currently used effect resource -- tr#: Number of currently used texture resources -- tras: Average size of a single texture resource ((totalSize of currently used textures) / tr#) -- trms: Largest currently used texture resource - -Caveats: - -Some stats describe changes/deltas to the scene: flush, obj+, obj-, act*, su* - others describe a snapshot of the current scene state: objNr, ar*, er* tr* -Resource stats (ar*,er*, tr*) are only logged if there was a flush during the logging interval - -## Renderer periodic logs - -A Ramses application which also contains a renderer component has periodic logs which look like this: - - 20210212-16:33:48.791 | Info | RPER | Renderer: 1 scene(s): 123 Rendered - Avg framerate: 52.63158 FPS [minFrameTime 381us, maxFrameTime 59404us], drawcallsPerFrame 35, numFrames 107, resUploaded 128 (13207530 B) - FB0: 92 - Scene 123: rendered 92, framesFArrived 92, framesFApplied 92, framesFBlocked 0, maxFramesWithNoFApplied 3, maxFramesFBlocked 0, FArrived 1397, FApplied 1397, actions/F (10/215/20.180386), dt/F (1/21295/34.343594), RC+/F (0/28/0.09234073), RC-/F (0/28/0.09520401), RS/F (0/0/0) - Time budgets: sceneResourceUpload 315360000000000us resourceUpload 315360000000000us obRender 315360000000000us - Longest frame(us)[avg]:59190 RendererCommands:749 [387] UpdateClientResources:32 [372] ApplySceneActions:7841 [2781] UpdateSceneResources:159 [114] UpdateEmbeddedCompositingResources:14 [8] UpdateStreamTextures:10 [5] UpdateScenesToBeMapped:13 [8] UpdateResourceCache:1039 [3234] UpdateAnimations:8 [5] UpdateTransformations:290 [138] UpdateDataLinks:44 [26] HandleDisplayEvents:34 [316] DrawScenes:1908 [1393] SwapBuffersNotifyClients:47049 [6854] MaxFramerateSleep:0 [2870] - -Read these logs like this: - -First line: -- Renderer participant id -- List of scenes known to renderer and status - -Second line (General renderer performance stats): -- Number of frames per second -- minimal and maximal frame time within time period -- drawcalls per frame -- Number of frames rendered in time period -- resources uploaded in time period - -Third line and over(scene related stats): -- Scene id -- rendered: Number of frames rendered -- FrameFArrived: Number of frames where flushes arrived -- FramesFApplied: Number of frames where flushes applied -- FrameFBlocked: Number of frames where applying a flush was blocked -- maxFramesWithNoFApplied: How many consecutive frame there was no flush applied -- maxFramesFBlocked: How many consecutive frames flushes were blocked from applying -- FArrived: Number of flushes arrived -- FApplied: Number of flushes applied -- actions/F: number of scene actions per flush -- dt/F: flush latency -- RC+/F RC-/F: Number of client resources added/removed per flush -- RS/F: Number of scene resource actions per flush -- RSUploaded: Size of scene resources uploaded - -Time budgets: Information about how much time per frame an action may take as a maximum, set by application - -Advanced stats: -- Longest Frame: How long did the longest frame take to render alltogether -- Rest: Advanced stats for profiling renderer which need internal understanding of the Ramses renderer. - - - -# Using specialized tools - -If the above methods didn't yield the results you expected, or you still think your application can perform better, -you can also use some of the professional tools for profiling: - -- NVidia NSight - a great tool by NVidia which can analyze any OpenGL-based application. Limitation: requires an NVidia graphics card. -- Standard profilers like the MSVC Profiler or gprof - great for finding CPU bottlenecks or memory attrition issues. -- Android Profiler - a great all-round tool for finding issues, also in native libs such as Ramses. Works only on Android. - -*/ diff --git a/doc/old_ramses/tools/scene-viewer/10_SceneViewer.dox b/doc/old_ramses/tools/scene-viewer/10_SceneViewer.dox deleted file mode 100644 index 11d7246dd..000000000 --- a/doc/old_ramses/tools/scene-viewer/10_SceneViewer.dox +++ /dev/null @@ -1,141 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -/** -@page SceneViewer ramses-scene-viewer - -# Synopsis - -`ramses-scene-viewer-PLATFORM [options] -s SCENE_FILE` - -PLATFORM typically means "windows-wgl-4-2-core" on Windows and "x11-egl-es-3-0" on Linux desktop. - -# Description - -`ramses-scene-viewer` is a tool to display and inspect Ramses scene files (e.g. created by \ref ramses::Scene::saveToFile()). -It helps to find errors and unused objects. The inspection GUI visualizes the relations between the scene objects and allows to do simple modifications. - -The window size is auto-detected based on the first camera viewport in the scene. This can be overridden by the options -w, --width or -h, --height - -## Inspection GUI - -The inspection GUI provides different views on the scene objects (described in the sections below). - -These features are common to all object types: -- scene objects are displayed in the format `Type[id]: name` and can be unfolded to show the details / relationships. -- The relationships are bi-directional. Reverse lookups are shown in _Used by_ tree items. -- Most scene object properties can be modified. The modification is immediately applied to the rendered scene. -- _grayed out_ objects are detected as "unnecessary". This actually means different things depending on the object type, e.g. - - a Node might not contain any MeshNode - - a MeshNode might not be be part of a RenderGroup - - a ResourceObject might use the same underlying LowLevel-Resource than another ResourceObject -- _red_ objects contain an error message (unless validation is switched off by command line) - -### File - -Allows to save the current scene to another file. This is mainly useful to store scene modifications done in the UI. - -### Scene objects - -Provides a list of all scene objects grouped by type. For each type the number of instances is displayed. Types with zero instances are not listed. - -The objects can be filtered by name. It's also possible to apply negative filtering by a prefix "-". - -### Node hierarchy - -Shows the node tree. The first layer only shows nodes without parent (root nodes). Children can be unfolded. - -### Resources - -Shows the total number of resources and the amount of memory that they are consuming. -"Not loaded" resources are currently not used by any render pass (possibly because some parent mesh node is switched off). - -Additionally all resource objects are listed in descending size order. The "Display limit" can be used to only show the largest x resources and their consumed memory. - -Grayed out resources are typically duplicates, i.e. they refer to the same underlying byte buffer identified by a hash value. If that's the case, the duplicates are listed in the details. -This is not indicating a problem because the underlying data will only be stored once in memory. - -Texture2D objects can be exported to png (not all texture formats may be supported though). The list of all Texture2D objects can be copied to clipboard (CSV-format) for further external processing. - -### Render hierarchy - -Shows the render order of RenderPasses, RenderGroups and MeshNodes. The order can be modified in the UI, but will not be applied immediately to the scene. Use the "refresh" button to apply changes. This will also reorder the lists. - -### Objects with errors/warnings - -Contains a list of objects where the scene validation found an issue. If the scene validation did not run (see -nv option) or no error was found, this section will not be displayed. - -# Options - - -s, --scene SCENE_FILE - -The scene file that shall be loaded (required argument). - ---- - --nv, --no-validation - -Disable scene validation. - -Scene validation noticably increases startup time. If the validation state is not needed or is already known, it might make sense to skip this step. - ---- - --vd, --validation-output-directory DIRECTORY - -If this option is provided the `ramses-scene-viewer` will dump the validation information and the summary of unused scene objects to this directory. - ---- - --x, --screenshot-file FILE - -Store a screenshot to FILE and exit. The file is stored in png format. - ---- - --ns, --no-skub - -Disable skub (skipping of unmodified buffer). - -Rendering will be triggered unconditionally for every frame. This is e.g. useful if the scene shall be analysed with a GPU profiling tool. - ---- - --gui, --gui MODE - -Configure the inspection gui mode (default: on) - -Possible values: -- \c on the scene is rendered to an offscreen buffer. The inspection gui can be displayed as an overlay window or the scene can be displayed in a scalable window side-by-side. -- off The inspection gui is switched off -- overlay The scene is directly rendered to the window (no offscreen buffer). The inspection gui is displayed as an overlay window. - -\note -The gui is automatically switched off in screenshot mode (-x, --screenshot). - ---- - --w, --width WIDTH - -Set the window width in pixels. - ---- - --h, --height HEIGHT - -Set the window height in pixels. - ---- - --h, --help - -Print usage information and exit - ---- - -*/ diff --git a/doc/sphinx/build.rst b/doc/sphinx/build.rst index 53dc960f0..cc9e2693b 100644 --- a/doc/sphinx/build.rst +++ b/doc/sphinx/build.rst @@ -1,6 +1,6 @@ .. ------------------------------------------------------------------------- - Copyright (C) 2020 BMW AG + Copyright (C) 2023 BMW AG ------------------------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this @@ -35,8 +35,8 @@ Build requirements ``RAMSES`` requires CMake 3.13 or newer and a C++17-compatible compiler: -* Clang6 or newer -* GCC7 or newer +* Clang12 or newer +* GCC9 or newer * MSVC2017 (_MSC_VER >= 1914) ``RAMSES`` requires the following @@ -48,7 +48,7 @@ additional build dependencies: * X11 or Wayland (or both) developer libraries * OpenGL + EGL developer libraries -For example, for ``Ubuntu 18.04 LTS`` these can be installed like this: +For example, for ``Ubuntu 20.04 LTS`` these can be installed like this: .. code-block:: bash @@ -68,7 +68,7 @@ modify some of the build settings: * -Dramses-sdk_WARNINGS_AS_ERRORS * options: ON/OFF * default: ON - * treats compiler warnings as errors and aborts the build. Use this option if your compiler generates warnings which are not fixed yet. + * Treats compiler warnings as errors and aborts the build. Use this option if your compiler generates warnings which are not fixed yet. * -Dramses-sdk_ENABLE_INSTALL * options: ON/OFF @@ -79,17 +79,7 @@ modify some of the build settings: * -Dramses-sdk_BUILD_WITH_LTO * options: ON/OFF * default: OFF - * turns clang's link-time optimizations on (details `here `_) - -* -DCMAKE_TOOLCHAIN_FILE= - * options: any of the files in `cmake/toolchain `_ or your custom cross-compilation toolchain file - * default: not set - * This is a standard CMake feature. We provide several toolchain files for popular compilers, use them or create your own - -* -Dramses-sdk_PACKAGE_TYPE= - * options: any of the `supported CPack generators `_ - * default: TGZ - * Allows to control which type of package is built by CMake/CPack when the 'package' target is built. See CPack docs for 'CPACK_GENERATOR' for details + * Turns clang's link-time optimizations on (details `here `_). ``Ramses`` can produce two types of shared libs, and both types can be enabled or disabled independently: @@ -130,6 +120,11 @@ Supported window types can be controlled with the cmake options below: * default: ON * Enables building window type *Android*. +* -Dramses-sdk_ENABLE_WINDOW_TYPE_IOS + * options: ON/OFF + * default: ON + * Enables building window type *iOS*. + * -Dramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI * options: ON/OFF * default: ON @@ -146,17 +141,12 @@ You can use the following options to disable some of the Ramses features: * -Dramses-sdk_ENABLE_LOGIC * options: ON/OFF * default: ON - * Enables the logic subsystem of ramses alongside its dependencies (Lua, Sol, ...) - -* -Dramses-sdk_BUILD_DAEMON - * options: ON/OFF - * default: ON - * Builds the ramses-daemon executable (for distributed rendering) + * Enables the logic subsystem of ramses alongside its dependencies (Lua, Sol, ...). * -Dramses-sdk_TEXT_SUPPORT * options: ON/OFF * default: ON - * enables the text subsystem of ramses alongside its dependencies (freetype, harfbuzz...) + * Enables the text subsystem of ramses alongside its dependencies (freetype, harfbuzz...). * -Dramses-sdk_ENABLE_TCP_SUPPORT * options: ON/OFF @@ -173,27 +163,32 @@ Additionally, you can disable additional examples, demos and tools: * -Dramses-sdk_BUILD_EXAMPLES * options: ON/OFF * default: ON if ``Ramses`` is a top level project, otherwise OFF by default - * set to OFF if you don't need the examples and want to reduce building time + * Set to OFF if you don't need the examples and want to reduce building time. * -Dramses-sdk_BUILD_DEMOS * options: ON/OFF * default: ON if ``Ramses`` is a top level project, otherwise OFF by default - * set to OFF if you don't need the demos and want to reduce building time + * Set to OFF if you don't need the demos and want to reduce building time. * -Dramses-sdk_BUILD_TOOLS * options: ON/OFF * default: ON if ``Ramses`` is a top level project, otherwise OFF by default - * set to OFF if you don't need the tools (e.g. imgui-based viewer) and want to reduce building time + * Set to OFF if you don't need the tools (e.g. imgui-based viewer) and want to reduce building time. * -Dramses-sdk_BUILD_TESTS * options: ON/OFF * default: ON if ``Ramses`` is a top level project, otherwise OFF by default * Build ramses tests. -* -Dramses-sdk_ENABLE_TEST_COVERAGE +* -Dramses-sdk_ENABLE_COVERAGE + * options: ON/OFF + * default: OFF + * Enables clang's options to generate code coverage from test executables. + +* -Dramses-sdk_FORCE_BUILD_DOCS * options: ON/OFF * default: OFF - * enables clang's options to generate code coverage from test executables + * Forces to build documentation when ramses is not the top level project. By default the documentation is built only when ramses is built as standalone project. For other supported cmake options, please refer to CMakeLists.txt. diff --git a/doc/sphinx/classes/generate_classes.py b/doc/sphinx/classes/generate_classes.py index 2d1d6588a..6185a14df 100755 --- a/doc/sphinx/classes/generate_classes.py +++ b/doc/sphinx/classes/generate_classes.py @@ -26,7 +26,6 @@ 'IRendererSceneControlEventHandler', 'RamsesFrameworkConfig', 'RendererConfig', - 'StatusObject', 'Appearance', 'ArrayBuffer', 'ArrayResource', @@ -38,7 +37,7 @@ 'EffectDescription', 'Effect', 'EffectInput', - 'GeometryBinding', + 'Geometry', 'IClientEventHandler', 'MeshNode', 'Node', @@ -92,7 +91,6 @@ 'EVisibilityMode', 'ETextureSamplingMethod', 'ETextureAddressMode', - 'ERenderBufferType', 'ERenderBufferAccessMode', 'ETextureFormat', 'ETextureCubeFace', @@ -152,14 +150,14 @@ 'LuaInterface', 'LuaScript', 'Property', - 'RamsesAppearanceBinding', + 'AppearanceBinding', 'RamsesBinding', - 'RamsesCameraBinding', - 'RamsesNodeBinding', - 'RamsesRenderPassBinding', - 'RamsesRenderGroupBinding', - 'RamsesRenderGroupBindingElements', - 'RamsesMeshNodeBinding', + 'CameraBinding', + 'NodeBinding', + 'RenderPassBinding', + 'RenderGroupBinding', + 'RenderGroupBindingElements', + 'MeshNodeBinding', 'SkinBinding', 'SaveFileConfig', 'TimerNode', @@ -200,13 +198,6 @@ 'SetDefaultLogging', ] }, - { - 'TOC_caption': 'Free functions', - 'namespace_prefix': 'rlogic::', - 'functions': [ - 'GetRamsesLogicVersion', - ] - }, ] diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py index 15bf20ab7..49bc84550 100644 --- a/doc/sphinx/conf.py +++ b/doc/sphinx/conf.py @@ -17,9 +17,6 @@ # Have to invoke doxygen directly on Read the Docs' servers because it has no CMake if 'READTHEDOCS' in os.environ: - # TODO this will not work in ReadTheDocs until we either fix the include paths - # or make this script parse all scattered include paths of ramses (which have optional - # components now such as text) api_includes_dir = '../../include' output_dir = 'doxygen_build' diff --git a/doc/sphinx/core.rst b/doc/sphinx/core.rst index 3c5f1e814..de8fdafe3 100644 --- a/doc/sphinx/core.rst +++ b/doc/sphinx/core.rst @@ -144,13 +144,14 @@ made in that flush as they are 'blocked' until all the resources are resolved. ^^^^^^^^^^^^^^^^^^^^^^ -Return status +API error handling ^^^^^^^^^^^^^^^^^^^^^^ The RAMSES API is designed to check most errors on usage - for example trying to create :class:`ramses::TextureSampler` using a write-only -:class:`ramses::RenderBuffer` -will result in error. The error message will appear in log and/or can be retrieved by using :func:`ramses::StatusObject::getStatusMessage` and passing -the status code returned from the API call. +:class:`ramses::RenderBuffer` will result in error. +In case of an error the API method returns `nullptr` or `false` depending on its signature and the error message will appear in log. +For most API calls the error message and object that caused it can also be retrieved by calling :func:`ramses::RamsesFramework::getLastError`, +note that only last error that occurred is available this way and it will be cleared after the method is called. It is highly recommended to check the status of every RAMSES API call, at least in debug configuration. See :ref:`Validation` to find out other ways of checking the state of RAMSES objects. @@ -160,7 +161,7 @@ Validation ^^^^^^^^^^^^^^^^^^^^^^ Some of the content issues are too expensive to be handled by the normal -:ref:`return code ` of the Ramses API. Examples of such cases include: +:ref:`return code ` of the Ramses API. Examples of such cases include: * invalid states are very expensive to check, or... * valid states which probably do not produce the desired result @@ -185,20 +186,14 @@ as expected. Client side validation """""""""""""""""""""""""" -The state of a RAMSES object can be checked at any time by calling :func:`ramses::StatusObject::validate` method on its instance. -The :func:`ramses::StatusObject::validate` method will do two things: +The state of any RAMSES object can be checked at any time by calling :func:`ramses::RamsesObject::validate` method on its instance. +The :func:`ramses::RamsesObject::validate` method returns :class:`ramses::ValidationReport` which contains list of issues that were found +for the instance that was validated as well as its dependent objects (e.g. :class:`ramses::Appearance` validates also its assigned :class:`ramses::Effect` +which is its dependency). Typical use case can be validating a :class:`ramses::Scene` which will validate all its contents. -* return an overall result status of the object (valid or invalid) -* produce a human-readable report of the state of that object - -The report can be obtained by calling :func:`ramses::StatusObject::getValidationReport` -immediately after calling :func:`ramses::StatusObject::validate`. - -The :func:`ramses::StatusObject::validate` method works hierarchically - -each instance will recursively validate all its objects. -For example, the :class:`ramses::RamsesClient` version of the -method will validate all its resources and all its scenes, -each scene will validate all its objects created within that scene and so on. +The returned :class:`ramses::ValidationReport` provides list of issues - each containing severity (warning or error), human readable message describing +the issue and pointer to an object that reported this issue. This allows filtering and sorting to suit any concrete needs of the caller (e.g. only +issues of certain object can be filtered). It is also possible to call validate from the shell. For that, one must enable the shell by calling :func:`ramses::RamsesFrameworkConfig::setRequestedRamsesShellType`. @@ -230,7 +225,7 @@ There can be a case when a scene is in valid state on the client (according to v but it is not rendered in the the desired way. Such cases can be difficult to analyze. One simple tool to show the state of the renderer and get an overview of what is being rendered is to the "rinfo" shell command (only available on a :ref:`renderer `). -While :func:`ramses::StatusObject::validate` focuses on scene content, the purpose of "rinfo" is to get +While :func:`ramses::RamsesObject::validate` focuses on scene content, the purpose of "rinfo" is to get overview of how is that content interpreted. It reports all the information about displays, scenes it knows and their states, more or less detailed rendering queue for each shown scene and so on. @@ -414,7 +409,7 @@ Text Shaders """""""""""""""""""""" In order to create a renderable mesh representing text, the mesh has to have a valid shader. -User needs to provide a RAMSES effect that will be assigned to both GeometryBinding and Appearance +User needs to provide a RAMSES effect that will be assigned to both Geometry and Appearance created by the text logic. The effect has to have semantics assigned to various uniform and attribute inputs so that the text logic knows where to bind necessary inputs like vertices, texture coordinates and sampler with glyph texture. diff --git a/doc/sphinx/examples/core/00_minimal.rst b/doc/sphinx/examples/core/00_minimal.rst index d6362d89e..1276d0abf 100644 --- a/doc/sphinx/examples/core/00_minimal.rst +++ b/doc/sphinx/examples/core/00_minimal.rst @@ -15,5 +15,5 @@ Minimal example ========================= .. literalinclude:: ../../../../examples/ramses-example-minimal/src/main.cpp - :start-after: #include "ramses-client.h" + :start-after: #include "ramses/client/ramses-client.h" diff --git a/doc/sphinx/examples/logic/00_minimal.rst b/doc/sphinx/examples/logic/00_minimal.rst index 5089f4684..c3f46120e 100644 --- a/doc/sphinx/examples/logic/00_minimal.rst +++ b/doc/sphinx/examples/logic/00_minimal.rst @@ -15,5 +15,5 @@ Minimal logic example ========================= .. literalinclude:: ../../../../examples/logic/00_minimal/main.cpp - :start-after: #include "ramses-logic/Property.h" + :start-after: #include "ramses/client/logic/Property.h" diff --git a/doc/sphinx/logic.rst b/doc/sphinx/logic.rst index 638c95c3b..02ff2e966 100644 --- a/doc/sphinx/logic.rst +++ b/doc/sphinx/logic.rst @@ -43,14 +43,14 @@ the input of another node (:ref:`more info on links `). +(:ref:`more info on bindings `). The greyed-out slots in the image above represent input properties which are neither linked nor have a statically configured value. In bindings, this denotes that the corresponding ``Ramses`` property is not being updated by the ``Logic Engine`` (see also :ref:`the section on data flow `). In other nodes, these properties will receive a default value at runtime (``0``, ``0.0f``, ``""``, ``true`` etc.) unless explicitly set by the application logic. Bindings' input values are initialized with the values of the bound `Ramses` object, for all bindings except -:class:`ramses::RamsesAppearanceBinding`. +:class:`ramses::AppearanceBinding`. Finally, ``Interface nodes`` have no execution logic, only inputs (and implicit outputs which are identical to the inputs). Interfaces are supposed to be the bridge between application code/data and the Ramses/Logic asset. @@ -86,7 +86,7 @@ However, it's guaranteed that: * TimerNodes will be executed always, regardless if their inputs were set -Additionally, bindings' properties are applied selectively - e.g. setting the ``scaling`` property of a :class:`ramses::RamsesNodeBinding` +Additionally, bindings' properties are applied selectively - e.g. setting the ``scaling`` property of a :class:`ramses::NodeBinding` will result in a call to ``ramses::Node::setScaling()``, but will not cause setting any other ``ramses::Node`` properties. This can be useful if you want to have your own logic e.g. to control the visibility of all ``Ramses`` nodes, and only use a ``Logic Engine`` to control transformation properties. In that case @@ -97,13 +97,6 @@ you should never set the ``visibility`` property of a Binding object, instead se We strongly discourage setting values to ``Ramses`` objects and to ``Ramses Logic`` bindings in the same update cycle for the same property to avoid unexpected behavior. At any given time, use one *or* the other, not both mechanisms to set values! -The ``Logic Engine`` can be also serialized and deserialized into binary files for fast loading. -The above data flow rules still apply as if all the scripts and binding objects were just created. The first call to -:func:`ramses::LogicEngine::update` after loading from file will execute all scripts. Binding values will only be passed further to ``Ramses`` -if their values were modified, e.g. by a link which produced a different value than before saving, or if the application -called :func:`ramses::Property::set` explicitly on any of the bindings' input properties. For more details on saving and loading, -see the :ref:`section further down `. - =================================== Logic node creation =================================== @@ -112,9 +105,9 @@ The entry point to ``RAMSES logic`` is a factory-style class :class:`ramses::Log create instances of all other types of objects supported by ``RAMSES Logic``: * :class:`ramses::LuaScript` -* :class:`ramses::RamsesNodeBinding` -* :class:`ramses::RamsesAppearanceBinding` -* :class:`ramses::RamsesCameraBinding` +* :class:`ramses::NodeBinding` +* :class:`ramses::AppearanceBinding` +* :class:`ramses::CameraBinding` See the full list over at the :ref:`class index `. @@ -127,7 +120,7 @@ You can create scripts using the :class:`ramses::LogicEngine` class like this: :linenos: :emphasize-lines: 5-14,16-17 - #include "ramses-logic/LogicEngine.h" + #include "ramses/client/logic/LogicEngine.h" using namespace ramses::logic; @@ -157,18 +150,17 @@ function - see :func:`ramses::LogicNode::getInputs()` and :func:`ramses::LogicNo You can :ref:`link nodes ` to form a more sophisticated logic execution graph. -You can :ref:`bind to Ramses objects ` to control a 3D ``Ramses`` scene. - -Finally, the :class:`ramses::LogicEngine` class and all its content can be also saved/loaded from a file. Refer to -:ref:`the section on saving/loading from files for more details `. +You can :ref:`bind to Ramses objects ` to control a 3D ``Ramses`` scene. ================================================== Object lifecycle ================================================== -All objects besides the :class:`ramses::LogicEngine` instance follow a strict factory pattern. -An object ``X`` is created by a method of the shape ``X* LogicEngine::createX(...)``. The pointer -returned shall not be freed or deleted, instead objects must be destroyed by calling :func:`ramses::LogicEngine::destroy`. +All logic objects are managed by the :class:`ramses::LogicEngine` instance which they were created from. +A logic object ``X`` is created by a method ``X* LogicEngine::createX(...)``. The pointer +returned shall not be freed or deleted, instead objects can be destroyed by calling :func:`ramses::LogicEngine::destroy`. +Logic objects are automatically destroyed when their owning :class:`ramses::LogicEngine` instance is destroyed. +The :class:`ramses::LogicEngine` itself is managed by the :class:`ramses::Scene` it was created from following same principle. .. note:: @@ -177,18 +169,12 @@ returned shall not be freed or deleted, instead objects must be destroyed by cal when combining different CRTs. In order to provide a stable API on Windows we chose to use raw pointers and hide object creation/deletion behind a pimpl/factory pattern. -The :class:`ramses::LogicEngine` doesn't create or destroy objects on its own - all data is -explicitly created by calling ``create`` and ``destroy`` methods. There are two special cases worth mentioning: +There are two cases when objects are implicitly created or destroyed by :class:`ramses::LogicEngine`: -* if :class:`ramses::LogicEngine` is destroyed, all objects are destroyed as well and theirs pointers invalidated -* :func:`ramses::LogicEngine::loadFromFile` destroys all objects previously created before the new content is loaded from the file +* if :class:`ramses::LogicEngine` is destroyed, all its objects are destroyed as well +* :func:`ramses::LogicEngine::loadFromFile` destroys all previously created objects and creates new objects from loaded file -.. note:: - - Loading data from files will invalidate all previous pointers to objects in - the :class:`ramses::LogicEngine`. To avoid that, we recommend generally avoiding using - a logicengine instance which already has content to load from files, and instead always - create a fresh instance. +In both the cases above any existing pointers to destroyed objects are invalidated. ================================================== Creating links between nodes @@ -264,30 +250,30 @@ and :func:`ramses::LogicEngine::unlink` documentation. The `data flow section `. In fact, they derive from the +There are different binding types depending on the type of scene object - refer to :class:`ramses::RamsesBinding` for the full list of derived classes. +Bindings can be linked in the exact same way as :ref:`scripts can `, they derive from the same base class - :class:`ramses::LogicNode`. The only -difference is that the bindings have only input properties (the outputs are implicitly defined and statically linked to the Ramses +difference is that the bindings have only input properties (the outputs are implicitly defined and statically linked to the scene objects attached to them), whereas scripts have inputs and outputs explicitly defined in the script interface. One might wonder, why not allow to directly link script outputs to ``Ramses`` objects? The reason for that is two-fold: -* Separation of concerns between pure script logic and ``Ramses``-related scene updates +* Separation of concerns between pure script logic and scene updates * This allows to handle all inputs and outputs in a generic way using the :class:`ramses::LogicNode` class' interface from - which both :class:`ramses::LuaScript` and :class:`ramses::RamsesNodeBinding` derive + which both :class:`ramses::LuaScript` and :class:`ramses::NodeBinding` derive The `section on data flow `_ describes how data is passed throughout the network of logic nodes and when bound Ramses objects are updated and when not. .. note:: - Binding input values are initialized with the same values as the `Ramses` objects they "bind". The only + Binding input values are initialized with the same values as the scene objects they "bind". The only exception to this are Appearance bindings - extracting all data from Ramses Appearances would incur performance costs not worth the convenience. @@ -296,10 +282,10 @@ Dynamic sorting of content ================================================== The ``Logic Engine`` provides a mechanism to dynamically sort select ``ramses::MeshNode`` and ``ramses::RenderGroup`` -objects. To do so, you can use the :class:`ramses::RamsesRenderGroupBinding` class. The class works similarly to -other binding classes - it statically binds to Ramses content (configured using :class:`ramses::RamsesRenderGroupBindingElements`) +objects. To do so, you can use the :class:`ramses::RenderGroupBinding` class. The class works similarly to +other binding classes - it statically binds to Ramses content (configured using :class:`ramses::RenderGroupBindingElements`) and allows setting the rendering priority of the bound content by setting input properties of type ``Int32`` with a name as -configured in :class:`ramses::RamsesRenderGroupBindingElements`. +configured in :class:`ramses::RenderGroupBindingElements`. ========================= Animations @@ -380,27 +366,26 @@ Error handling ========================= Some of the ``RAMSES Logic`` classes' methods can issue errors when used incorrectly or when -a ``Lua`` script encounters a compile-time or run-time error. Those errors are globally collected -by the :class:`ramses::LogicEngine` class and can be obtained by calling :func:`ramses::LogicEngine::getErrors()`. -The error information stored in :struct:`ramses::ErrorData` contains additional stack trace information for Lua runtime errors, -and a pointer to the originating :class:`ramses::LogicNode` which caused the error for errors which occured during :func:`ramses::LogicEngine::update()` -and can't be directly attributed to a specific API call. -Beware that any of the mutable methods of :class:`ramses::LogicEngine` clear the previously generated errors -in the list, so that the list only ever contains the errors since the last method call! +a ``Lua`` script encounters a compile-time or run-time error. Those errors are collected via the Ramses SDK error handling mechanism +which uses :class:`ramses::RamsesFramework` to register always the last occurred error until it is retrieved by calling +:func:`ramses::RamsesFramework::getLastError()`. +The :struct:`ramses::Issue` contains additional information: human readable message (e.g. stack trace information for Lua runtime errors) +and optionally a pointer to the originating :class:`ramses::RamsesObject` which reported or caused the error (for errors which occured during +:func:`ramses::LogicEngine::update()` and can't be directly attributed to a specific API call). For code samples which demonstrate how compile-time and runtime errors can be gracefully handled, have a look at the :ref:`examples `. To intercept and fix potential content problems, you can use :func:`ramses::LogicEngine::validate()`. This method will scan the contents of the ``Logic Engine`` and report pontential issues which are not fatal, but may result in suboptimal -performance, data inconsistency or serialization bugs. It is highly advised to use this method in conjunction with the -``Ramses`` validation methods (StatusObject::validate) to prevent issues during runtime. +performance, data inconsistency or serialization bugs. It is highly recommended to also validate the scene this ``LogicEngine`` is used with +(:func:`ramses::Scene::validate()`) to prevent issues during runtime. ===================================== Iterating over object collections ===================================== -Iterating over objects can be useful, for example when :ref:`loading content from files ` +Iterating over objects can be useful, for example when the logic was loaded from file or when applying search or filter algorithms over all objects from a specific type. The :class:`ramses::LogicEngine` class provides iterator-style access to all of its objects: @@ -425,155 +410,6 @@ other STL algorithms or libraries which adhere to STL principles. The iterators the ``Ramses Logic`` to performance ends. Be careful not to depend on any internals of the classes (mostly the Internally wrapped STL containers) to avoid compatibility problems when updating the ``Ramses Logic`` version! -===================================== -Saving/Loading from file -===================================== - -The :class:`ramses::LogicEngine` class and its content can be stored in a file and loaded from file again using the functions -:func:`ramses::LogicEngine::saveToFile` and :func:`ramses::LogicEngine::loadFromFile`. The latter has an optional argument -to provide a ``Ramses`` scene which should be used to resolve references to Ramses objects in the Logic Engine file. Read -further for more details. - -.. note:: - - Even though it would be technically possible to combine the storing and loading of Ramses scenes together with the Logic Engine - and its scripts in a single file, we decided to not do this but instead keep the content in separate files and load/save it independently. - This allows to have the same Ramses scene stored multiple times or with different settings, but using the same logic content, - as well as the other way around - having different logic implementations based on the same Ramses scene. It also leaves more freedom - to choose how to store the Ramses scene. This implies that at most a single Ramses scene can be referenced at the time of saving, - having more than one scene will result in error. - --------------------------------------------------- -Object lifecycle when saving and loading to files --------------------------------------------------- - -After loading, -the current state of the logic engine objects will be completely overwritten by the contents from the file. If you don't want this behavior, -use two different instances of the class - one dedicated for loading from files and nothing else. - -Here is a simple example which demonstrates how saving/loading from file works in the simplest case (i.e. no references to Ramses objects): - -.. code-block:: - :linenos: - - // Creates an empty LogicEngine instance, saves it to file and destroys the object - { - ramses::LogicEngine engine; - engine.saveToFile("logicEngine.bin"); - } - // Loads the file we saved above into a freshly created LogicEngine instance - { - ramses::LogicEngine engine; - engine.loadFromFile("logicEngine.bin"); - } - -After the call to :func:`ramses::LogicEngine::loadFromFile` successfully returns (refer to the :ref:`Error handling` section -for info on handling errors), the state of the :class:`ramses::LogicEngine` class will be overwritten with -the contents loaded from the file. This implies that all objects created prior loading will be deleted and pointers to them -will be pointing to invalid memory locations. We advise designing your object lifecycles around this and immediately dispose -such pointers after loading from file. - --------------------------------------------------- -File compatibility --------------------------------------------------- - -Starting with version ``1.1.0``, the ``Logic Engine`` supports ``feature levels`` which ensure that -you can use a newer version of the library but keep your assets to the older version. This allows integrating -fixes and supporting multiple versions of the content toolchain at the same time. This mechanism is called -``feature toggle`` in other projects and essentially enables trying out new features and rolling back if -they prove to be unstable and need more work. - -How does it work? The Logic Engine provides an enum :enum:`rligic::EFeatureLevel` which represents the different -feature levels introduced since the last major version of the lib. For example, Ramses Logic 1.0 and 1.1 provide two -different feature levels each (``1`` and ``2``). You can use ``1`` or ``2`` selectively if you use version 1.1 of the library, -or you can use -only ``1`` if you use version 1.0. You can select at runtime which version to use by specifying a value -in the overloaded constructor of the class :func:`ramses::LogicEngine::LogicEngine`. You can also check which version -is used in a binary file by using :func:`ramses::LogicEngine::GetFeatureLevelFromFile`. Note that to load a file you must -always use the exact feature level that was used to export the file, otherwise the loading will fail. - -For more details on the available feature levels and how to use them, see :enum:`ramses::EFeatureLevel`. - ------------------------------------------------------------ -File compatibility (prior version 1.1 and feature levels) ------------------------------------------------------------ - -Since version ``0.7.0``, Ramses Logic binary files are backwards compatible. -This means that a newer version of the runtime can be used to load an older binary file, unless the file format version -had a breaking change and a newer version of the Logic Engine must be used. -The exact compatibility info is documented in the `version matrix `_. -There are some limitations: - -* Loading a file older than v0.7.0 will result in an error with a runtime equal or newer than v0.7.0 -* Adding new features will still break the format and require re-export. We will explicitly list such breaking changes in the version matrix. - --------------------------------------------------- -Saving and loading together with a Ramses scene --------------------------------------------------- - -In a slightly less simple, but more realistic setup, the Logic Engine will contain objects of type ``RamsesBinding`` which -contain references to Ramses objects. In that case, use the optional ``ramses::Scene*`` argument to :func:`ramses::LogicEngine::loadFromFile` -to specify the scene from which the references to Ramses objects should be resolved. ``Ramses Logic`` uses the ``getSceneObjectId()`` method of the -``ramses::SceneObject`` class to track references to scene objects. This implies that those IDs must be the same after loading, otherwise -:func:`ramses::LogicEngine::loadFromFile` will report error and fail. ``Ramses Logic`` makes no assumptions on the origin of the scene, its name -or ID. - -For a full-fledged example, have a look at :ref:`the serialization example `. - -.. warning:: - - The ``LogicEngine`` expects that immediately after loading, the state of the ``Ramses`` scene is the same as it was right before saving, and will not - modify ``Ramses`` objects which are attached to bindings in the ``LogicEngine`` in its first update, unless they are linked to scripts or explicitly - overwritten by :func:`ramses::Property::set` calls after loading from the file. We strongly advice to always save and load - both the ``Ramses`` scene and the ``LogicEngine`` scene together to avoid data inconsistencies! - --------------------------------------------------- -Using memory buffer instead of file --------------------------------------------------- - -You can use :func:`ramses::LogicEngine::loadFromBuffer` to load the contents of the logic engine from your own memory. This can be useful -if you have your own file management logic, or the data comes from a different source than a file on disk. Be mindful that passing data buffers -over the boundaries of libraries can be unsafe with C++, and some errors/abuse can't be reliably prevented. Make sure you check the size of -the buffer and don't load from memory of untrusted origins. - -========================= -Logging -========================= - -Internally there are four log levels available. - -* Info -* Debug -* Warn -* Error - -By default all internal logging messages are sent to std::cout. You can toggle this with :func:`ramses::Logger::SetDefaultLogging`. -In addition, it is possible to have a custom log handler function which is called each time a log message is issued. - -.. code-block:: - :linenos: - - #include - - Logger::SetLogHandler([](ElogMessageType msgType, std::string_view message){ - switch(type) - { - case ELogMessageType::ERROR: - std::cout << "Error: " << message << std::endl; - break; - default: - std::cout << message << std::endl; - break; - } - }); - -Inside the log handler function, you get the type of the message and the message itself as a std::string_view. -Keep in mind, that you can't store the std::string_view. It will be invalid after the call to the log handler -function. If you need the message for later usage, store it in a std::string. - -The amount of logging can be configured with :func:`ramses::Logger::SetLogVerbosity`. This affects both the default -logging and the custom logger. - ====================================== Security and memory safety diff --git a/doc/sphinx/requirements.txt b/doc/sphinx/requirements.txt index 4d66748c9..c5e7595f1 100644 --- a/doc/sphinx/requirements.txt +++ b/doc/sphinx/requirements.txt @@ -1,40 +1,13 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2020 BMW AG +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -alabaster==0.7.12 -attrs==21.2.0 -Babel==2.9.1 -breathe==4.18.1 -certifi==2023.7.22 -chardet==4.0.0 -docutils==0.16 -idna==2.10 -imagesize==1.2.0 -Jinja2==3.0.0 -markdown-it-py==2.2.0 -MarkupSafe==2.0.0 -mdit-py-plugins==0.2.8 -myst-parser==0.14.0 -packaging==20.9 -Pygments==2.15.0 -pyparsing==2.4.7 -pytz==2021.1 -PyYAML==5.4.1 -requests==2.31.0 -six==1.16.0 -snowballstemmer==2.1.0 -Sphinx==3.0.4 -sphinx-rtd-theme==0.4.3 -sphinxcontrib-applehelp==1.0.2 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==1.0.3 -sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.4 -urllib3==1.26.17 +sphinx==6.2.1 +sphinx-rtd-theme==1.2.2 +myst-parser==2.0.0 +breathe==4.35.0 diff --git a/doc/sphinx/viewer.rst b/doc/sphinx/viewer.rst index 7046ecbe5..17e270de9 100644 --- a/doc/sphinx/viewer.rst +++ b/doc/sphinx/viewer.rst @@ -18,20 +18,20 @@ ramses-logic-viewer Synopsis -------- -**ramses-logic-viewer** [options] [ ] +**ramses-logic-viewer** [options] [] ----------- Description ----------- :program:`ramses-logic-viewer` is a tool which can load, configure and display -Ramses (````) and Ramses Logic (````) binary files alongside with a custom configuration (````). +Ramses (````) binary files alongside with a custom configuration (````). It also provides a GUI to inspect the displayed scene. -* Both ```` and ```` are typically created by the Ramses Composer. +* ```` is typically created by the Ramses Composer. * ```` is an optional configuration file written in lua to modify the scene view (see :ref:`lua_configuration_api` for details) -* Both ```` and ```` are found in the same path as ```` if not provided as arguments. +* ```` is found in the same path as ```` if not provided as an argument. For auto-detection the file extensions `rlogic` and `lua` are expected. * If no ```` is found, the viewer will show the scene and propose to store a default configuration. * Display size is auto-detected based on the first camera viewport found in the scene. @@ -90,7 +90,7 @@ Options .. option:: --write-config [filename] Writes the default lua configuration to the given filename. If the filename is omitted, the viewer will use - the ````'s name with lua extension. + the ````'s name with lua extension. .. option:: --log-level-console [off|fatal|error|warn|info|debug|trace] @@ -116,12 +116,12 @@ The module ``rlogic`` provides members to access all Logic Node types: * ``rlogic.scripts`` (:cpp:class:`ramses::LuaScript`) * ``rlogic.animationNodes`` (:cpp:class:`ramses::AnimationNode`) * ``rlogic.timerNodes`` (:cpp:class:`ramses::TimerNode`) -* ``rlogic.nodeBindings`` (:cpp:class:`ramses::RamsesNodeBinding`) -* ``rlogic.appearanceBindings`` (:cpp:class:`ramses::RamsesAppearanceBinding`) -* ``rlogic.cameraBindings`` (:cpp:class:`ramses::RamsesCameraBinding`) -* ``rlogic.renderPassBindings`` (:cpp:class:`ramses::RamsesRenderPassBinding`) -* ``rlogic.renderGroupBindings`` (:cpp:class:`ramses::RamsesRenderGroupBinding`) -* ``rlogic.meshNodeBindings`` (:cpp:class:`ramses::RamsesMeshNodeBinding`) +* ``rlogic.nodeBindings`` (:cpp:class:`ramses::NodeBinding`) +* ``rlogic.appearanceBindings`` (:cpp:class:`ramses::AppearanceBinding`) +* ``rlogic.cameraBindings`` (:cpp:class:`ramses::CameraBinding`) +* ``rlogic.renderPassBindings`` (:cpp:class:`ramses::RenderPassBinding`) +* ``rlogic.renderGroupBindings`` (:cpp:class:`ramses::RenderGroupBinding`) +* ``rlogic.meshNodeBindings`` (:cpp:class:`ramses::MeshNodeBinding`) * ``rlogic.anchorPoints`` (:cpp:class:`ramses::AnchorPoint`) * ``rlogic.skinBindings`` (:cpp:class:`ramses::SkinBinding`) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4a8c1e1e7..fea7ec5ea 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -6,6 +6,12 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- +if(ramses-sdk_BUILD_FULL_SHARED_LIB) + add_library(ramses::shared-lib-for-examples ALIAS ramses-shared-lib) +else() + add_library(ramses::shared-lib-for-examples ALIAS ramses-shared-lib-headless) +endif() + add_subdirectory(ramses-example-minimal) add_subdirectory(ramses-example-basic-blending) add_subdirectory(ramses-example-basic-effect-from-glsl) @@ -20,23 +26,25 @@ add_subdirectory(ramses-example-data-buffers-vertices) add_subdirectory(ramses-example-data-buffers-texture) add_subdirectory(ramses-example-geometry-instancing) add_subdirectory(ramses-example-renderonce) -add_subdirectory(ramses-example-local-client) -add_subdirectory(ramses-example-local-displays) -add_subdirectory(ramses-example-local-datalink) -add_subdirectory(ramses-example-local-offscreenbuffer) -add_subdirectory(ramses-example-local-dma-offscreenbuffer) -add_subdirectory(ramses-example-local-viewport-link) -add_subdirectory(ramses-example-local-pick-handling) -add_subdirectory(ramses-example-local-scene-referencing) -add_subdirectory(ramses-example-local-geometry-shaders) -add_subdirectory(ramses-example-local-compositing) if(ramses-sdk_TEXT_SUPPORT) add_subdirectory(ramses-example-text-basic) add_subdirectory(ramses-example-text-languages) endif() add_subdirectory(ramses-example-interleaved-vertex-buffers) +if(ramses-sdk_BUILD_FULL_SHARED_LIB) + add_subdirectory(ramses-example-local-client) + add_subdirectory(ramses-example-local-displays) + add_subdirectory(ramses-example-local-datalink) + add_subdirectory(ramses-example-local-offscreenbuffer) + add_subdirectory(ramses-example-local-dma-offscreenbuffer) + add_subdirectory(ramses-example-local-viewport-link) + add_subdirectory(ramses-example-local-pick-handling) + add_subdirectory(ramses-example-local-scene-referencing) + add_subdirectory(ramses-example-local-geometry-shaders) + add_subdirectory(ramses-example-local-compositing) +endif() + if(ramses-sdk_ENABLE_LOGIC) add_subdirectory(logic) endif() - diff --git a/examples/logic/00_minimal/main.cpp b/examples/logic/00_minimal/main.cpp index 199936b8f..ee917b0f3 100644 --- a/examples/logic/00_minimal/main.cpp +++ b/examples/logic/00_minimal/main.cpp @@ -8,9 +8,10 @@ #include -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" /** * This example is designed to be minimal, but still provide insight into @@ -21,8 +22,14 @@ int main() /** * Create an instance of the LogicEngine class. This holds all data * and offers methods to load and execute scripts among other things. + * We need to instantiate the basic components of Ramses ecosystem first, + * start with framework, then client and then scene which will be the owner + * of the logic instance. */ - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Create a script by providing the source code of the script with a string. diff --git a/examples/logic/01a_primitive_properties/main.cpp b/examples/logic/01a_primitive_properties/main.cpp index b18ab5047..d88d011c9 100644 --- a/examples/logic/01a_primitive_properties/main.cpp +++ b/examples/logic/01a_primitive_properties/main.cpp @@ -6,9 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include @@ -17,7 +18,10 @@ */ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // Create a simple script which multiplies two numbers and stores the result in a string ramses::LuaScript* multiplyScript = logicEngine.createLuaScript(R"( diff --git a/examples/logic/01b_struct_properties/main.cpp b/examples/logic/01b_struct_properties/main.cpp index 8af1b3bc5..c1d2452cc 100644 --- a/examples/logic/01b_struct_properties/main.cpp +++ b/examples/logic/01b_struct_properties/main.cpp @@ -6,9 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include #include @@ -20,7 +21,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // Create a script with inputs and outputs of the same type (consists of nested structs) ramses::LuaScript* script = logicEngine.createLuaScript(R"( diff --git a/examples/logic/01c_array_properties/main.cpp b/examples/logic/01c_array_properties/main.cpp index c8108412c..5a4da37f9 100644 --- a/examples/logic/01c_array_properties/main.cpp +++ b/examples/logic/01c_array_properties/main.cpp @@ -6,9 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include #include @@ -19,7 +20,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // A script which demonstrates how to access vector and array properties ramses::LuaScript* script = logicEngine.createLuaScript(R"( diff --git a/examples/logic/02_errors_compile_time/main.cpp b/examples/logic/02_errors_compile_time/main.cpp index 3a3b40975..f69248907 100644 --- a/examples/logic/02_errors_compile_time/main.cpp +++ b/examples/logic/02_errors_compile_time/main.cpp @@ -6,7 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/ramses-client.h" #include #include @@ -16,7 +17,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Try to compile a script which has invalid Lua syntax @@ -34,21 +38,17 @@ int main() if (nullptr == faultyScript) { /** - * To get further information about the issue, fetch errors from LogicEngine - * Note that this list will be reset with the next API call to logicEngine! + * To get further information about the issue, fetch last error from RamsesFramework. + * Note that the error will be reset once getLastError is called. */ - auto errorList = logicEngine.getErrors(); - assert(!errorList.empty()); + const auto lastError = framework.getLastError(); + assert(lastError.has_value()); - // Print out the error information - for (auto& error : errorList) - { - /** - * Note that this error has no stack trace, because there is no stack - the script failed compiling - * Furthermore, the source code line indication is also "Lua style" - it starts at 1, not 0 - */ - std::cout << error.message << std::endl; - } + /** + * Note that this error has no stack trace, because there is no stack - the script failed compiling + * Furthermore, the source code line indication is also "Lua style" - it starts at 1, not 0 + */ + std::cout << lastError->message << std::endl; } return 0; diff --git a/examples/logic/03_errors_runtime/main.cpp b/examples/logic/03_errors_runtime/main.cpp index 4b872e267..0048188d2 100644 --- a/examples/logic/03_errors_runtime/main.cpp +++ b/examples/logic/03_errors_runtime/main.cpp @@ -6,8 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/ramses-client.h" #include #include @@ -17,7 +18,10 @@ */ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * This script contains a runtime error, i.e. from Lua point of view this is @@ -45,15 +49,17 @@ int main() * https://ramses-logic.readthedocs.io/en/latest/lua_syntax.html#the-global-in-and-out-objects for more information * how to read the stack trace */ - logicEngine.update(); + const bool status = logicEngine.update(); - // The LogicEngine provides a list of errors from the last call. Use it to get - // the error message and a pointer to the object which causes the error. + // Ramses keeps last error that occurred during some API call until the getLastError is called, then the error is cleared. + // Use it to get the error message and a pointer to the object which caused the error. + const auto issue = framework.getLastError(); assert( - logicEngine.getErrors().size() == 1u && - logicEngine.getErrors()[0].object == faultyScript); + !status && + issue.has_value() && + issue->object == faultyScript); logicEngine.destroy(*faultyScript); - return 0; + return status ? -1 : 0; } diff --git a/examples/logic/04_ramses_scene/main.cpp b/examples/logic/04_ramses_scene/main.cpp index b836686ab..5c20a5df7 100644 --- a/examples/logic/04_ramses_scene/main.cpp +++ b/examples/logic/04_ramses_scene/main.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include #include @@ -64,13 +64,13 @@ int main() */ auto [scene, triangleNode] = CreateSceneWithTriangle(*ramsesClient); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Create a binding object which serves as a bridge between logic scripts and animations on one end * and a Ramses scene on the other end */ - ramses::RamsesNodeBinding* nodeBinding = logicEngine.createRamsesNodeBinding(*triangleNode, ramses::ERotationType::Euler_XYZ, "binding to triangle mesh node"); + ramses::NodeBinding* nodeBinding = logicEngine.createNodeBinding(*triangleNode, ramses::ERotationType::Euler_XYZ, "binding to triangle mesh node"); /** * Create a simple script which takes the current time in milliseconds @@ -89,7 +89,7 @@ int main() )"); /** - * Connect the script output 'rotationZ' with the rotation property of the RamsesNodeBinding object. + * Connect the script output 'rotationZ' with the rotation property of the NodeBinding object. * After this, the value computed in the script will be propagated to the ramses node. */ logicEngine.link( @@ -136,14 +136,14 @@ int main() SceneAndNode CreateSceneWithTriangle(ramses::RamsesClient& client) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "red triangle scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "red triangle scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, 1.0f, 0.1f, 100.0f); camera->setViewport(0, 0, 800, 800); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -175,18 +175,18 @@ SceneAndNode CreateSceneWithTriangle(ramses::RamsesClient& client) effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Effect* effect = scene->createEffect(effectDesc); ramses::Appearance* appearance = scene->createAppearance(*effect); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); diff --git a/examples/logic/05_serialization/main.cpp b/examples/logic/05_serialization/main.cpp index f0b253ba1..94548e7da 100644 --- a/examples/logic/05_serialization/main.cpp +++ b/examples/logic/05_serialization/main.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include #include @@ -26,7 +26,7 @@ */ -void CreateAndSaveContent(const std::string& ramsesSceneFile, const std::string& ramsesLogicFile); +void CreateAndSaveContent(const std::string& ramsesSceneFile); int main() { @@ -34,12 +34,11 @@ int main() * Define file names to use where content will be saved */ const std::string ramsesSceneFile = "scene.ramses"; - const std::string ramsesLogicFile = "scene.logic"; /** * Create a simple triangle scene and a script which controls it. Save both to its own file. */ - CreateAndSaveContent(ramsesSceneFile, ramsesLogicFile); + CreateAndSaveContent(ramsesSceneFile); /** * Load the Ramses scene from the file. It has to be loaded first, so that we can @@ -51,18 +50,15 @@ int main() ramses::Scene* scene = client.loadSceneFromFile(ramsesSceneFile.c_str()); /** - * Load the logic content from its file, and provide a pointer to the ramses scene as argument. - * After loadFromFile() returns, the bindings which point to Ramses objects will point - * to objects from the scene provided as an argument. + * The loaded scene contains the Logic instance */ - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; - logicEngine.loadFromFile(ramsesLogicFile, scene); + ramses::LogicEngine& logicEngine = *scene->findObject("example logic"); /** * ramses::LogicEngine provides iterable collections to its objects. We can use them to resolve objects after loading */ ramses::Collection loadedScripts = logicEngine.getCollection(); - ramses::Collection loadedNodeBindings = logicEngine.getCollection(); + ramses::Collection loadedNodeBindings = logicEngine.getCollection(); /** * We can use any STL algorithm on the collections. @@ -74,11 +70,11 @@ int main() /** * We can do the same to find a ramses node binding. We can use the binding to obtain a pointer to the ramses::Node further down. - * This is an alternative to ramses::Scene::findObjectById()/findObjectByName() methods. + * This is an alternative to ramses::Scene::findObject() methods. * Note that this example uses fully qualified names and type traits for documentation sake. You can also just use 'auto'. */ - ramses::Collection::const_iterator triangleNodeBinding = std::find_if(loadedNodeBindings.cbegin(), loadedNodeBindings.cend(), - [](const ramses::RamsesNodeBinding* binding) {return binding->getName() == "link to triangle node"; }); + ramses::Collection::const_iterator triangleNodeBinding = std::find_if(loadedNodeBindings.cbegin(), loadedNodeBindings.cend(), + [](const ramses::NodeBinding* binding) {return binding->getName() == "link to triangle node"; }); /** * The LogicEngine iterators work just like any other STL forward iterator - can be compared, dereferenced, incremented etc. @@ -106,7 +102,7 @@ int main() * Helper method which creates a simple ramses scene, a simple script, and * saves the content in two separate files */ -void CreateAndSaveContent(const std::string &ramsesSceneFile, const std::string& ramsesLogicFile) +void CreateAndSaveContent(const std::string &ramsesSceneFile) { /** * Boilerplate Ramses code which saves a red triangle scene in a file. For more ramses @@ -116,14 +112,14 @@ void CreateAndSaveContent(const std::string &ramsesSceneFile, const std::string& ramses::RamsesFramework ramsesFramework(config); ramses::RamsesClient& client = *ramsesFramework.createClient("example client"); - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "red triangle scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "red triangle scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, 1.0f, 0.1f, 100.0f); camera->setViewport(0, 0, 800, 800); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -155,28 +151,28 @@ void CreateAndSaveContent(const std::string &ramsesSceneFile, const std::string& effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Effect* effect = scene->createEffect(effectDesc); ramses::Appearance* appearance = scene->createAppearance(*effect); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + std::optional optPositionsInput = effect->findAttributeInput("a_position"); + assert(optPositionsInput.has_value()); + geometry->setInputBuffer(*optPositionsInput, *vertexPositions); ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); scene->flush(); /** - * Create a temporary LogicEngine instance for creating and saving a simple script which references a ramses Node + * Create a LogicEngine instance for creating and saving a simple script which references a ramses Node */ - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; - ramses::RamsesNodeBinding* nodeBinding = logicEngine.createRamsesNodeBinding(*meshNode, ramses::ERotationType::Euler_XYZ, "link to triangle node"); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine("example logic") }; + ramses::NodeBinding* nodeBinding = logicEngine.createNodeBinding(*meshNode, ramses::ERotationType::Euler_XYZ, "link to triangle node"); /** * Create a simple script which sets the rotation values of a node based on simulated time @@ -205,17 +201,12 @@ void CreateAndSaveContent(const std::string &ramsesSceneFile, const std::string& logicEngine.update(); /** - * Save the script, the node binding and their link to a file so that they can be loaded later + * Save the ramses scene (includes the script, the node binding and the link) * * Note: in this example validation on saving is disabled for simplification, since normally a warning * gets generated for script input that is not linked. */ ramses::SaveFileConfig saveFileConfig; saveFileConfig.setValidationEnabled(false); - logicEngine.saveToFile(ramsesLogicFile, saveFileConfig); - - /** - * Finally, we save the Ramses scene with the values/data which has been applied by the LogicEngine above - */ - [[maybe_unused]] auto status = scene->saveToFile(ramsesSceneFile.c_str(), false); + [[maybe_unused]] auto status = scene->saveToFile(ramsesSceneFile.c_str(), saveFileConfig); } diff --git a/examples/logic/07_links/main.cpp b/examples/logic/07_links/main.cpp index 161cff313..41bcf51ea 100644 --- a/examples/logic/07_links/main.cpp +++ b/examples/logic/07_links/main.cpp @@ -6,9 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include #include @@ -20,7 +21,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // Create a simple script which increments an integer and prints the result const std::string_view scriptSrc = R"( diff --git a/examples/logic/08a_static_animation/main.cpp b/examples/logic/08a_static_animation/main.cpp index c7ea719ba..a9959e071 100644 --- a/examples/logic/08a_static_animation/main.cpp +++ b/examples/logic/08a_static_animation/main.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/TimerNode.h" - -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/TimerNode.h" + +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include "SimpleRenderer.h" @@ -58,14 +58,14 @@ int main() */ auto [scene, tri1, tri2] = CreateSceneWithTriangles(*renderer.getClient()); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Create a binding object which serves as a bridge between logic nodes and animations on one end * and a Ramses scene on the other end. */ - ramses::RamsesNodeBinding* nodeBinding1 = logicEngine.createRamsesNodeBinding(*tri1); - ramses::RamsesNodeBinding* nodeBinding2 = logicEngine.createRamsesNodeBinding(*tri2); + ramses::NodeBinding* nodeBinding1 = logicEngine.createNodeBinding(*tri1); + ramses::NodeBinding* nodeBinding2 = logicEngine.createNodeBinding(*tri2); /** * Create two simple animations (cubic and linear) by providing keyframes and timestamps. @@ -96,7 +96,7 @@ int main() ramses::AnimationNode* linearAnimNode = logicEngine.createAnimationNode(animConfigLinear); /** - * Connect the animation channel 'rotationZ' output with the rotation property of the RamsesNodeBinding object. + * Connect the animation channel 'rotationZ' output with the rotation property of the NodeBinding object. * After this, the value computed in the animation output channel will be propagated to the ramses node's rotation property. */ logicEngine.link( @@ -227,14 +227,14 @@ int main() SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "red triangle scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "red triangle scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, 1280.f/800.f, 0.1f, 100.0f); camera->setViewport(0, 0, 1280, 800); camera->setTranslation({0.0f, 0.0f, 10.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -266,22 +266,22 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client) effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Effect* effect = scene->createEffect(effectDesc); ramses::Appearance* appearance = scene->createAppearance(*effect); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode1 = scene->createMeshNode("triangle mesh node 1"); ramses::MeshNode* meshNode2 = scene->createMeshNode("triangle mesh node 2"); meshNode1->setAppearance(*appearance); meshNode1->setIndexCount(3); - meshNode1->setGeometryBinding(*geometry); + meshNode1->setGeometry(*geometry); meshNode2->setAppearance(*appearance); meshNode2->setIndexCount(3); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); meshNode1->setTranslation({-1.f, -0.8f, 0.f}); meshNode2->setTranslation({1.f, -0.8f, 0.f}); diff --git a/examples/logic/08b_dynamic_animation/main.cpp b/examples/logic/08b_dynamic_animation/main.cpp index bf3d56757..819006552 100644 --- a/examples/logic/08b_dynamic_animation/main.cpp +++ b/examples/logic/08b_dynamic_animation/main.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/TimerNode.h" - -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/TimerNode.h" + +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include "SimpleRenderer.h" @@ -60,13 +60,13 @@ int main() */ auto [scene, tri] = CreateSceneWithTriangle(*renderer.getClient()); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Create a binding object which serves as a bridge between logic nodes and animations on one end * and a Ramses scene on the other end. */ - ramses::RamsesNodeBinding* nodeBinding = logicEngine.createRamsesNodeBinding(*tri); + ramses::NodeBinding* nodeBinding = logicEngine.createNodeBinding(*tri); /** * Create data arrays which contain the time stamp data and the keyframe data points. @@ -91,7 +91,7 @@ int main() ramses::AnimationNode* animNode = logicEngine.createAnimationNode(animConfig); /** - * Connect the animation channel 'translation' output with the translation property of the RamsesNodeBinding object. + * Connect the animation channel 'translation' output with the translation property of the NodeBinding object. * After this, the value computed in the animation output channel will be propagated to the ramses node's rotation property. */ logicEngine.link( @@ -247,14 +247,14 @@ int main() SceneAndNode CreateSceneWithTriangle(ramses::RamsesClient& client) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "triangle scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "triangle scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, 1280.f/480.f, 0.1f, 100.0f); camera->setViewport(0, 0, 1280, 480); camera->setTranslation({0.0f, 0.0f, 15.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -286,18 +286,18 @@ SceneAndNode CreateSceneWithTriangle(ramses::RamsesClient& client) effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Effect* effect = scene->createEffect(effectDesc); ramses::Appearance* appearance = scene->createAppearance(*effect); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); meshNode->setScaling({0.05f, 0.05f, 0.05f}); // adjust triangle size renderGroup->addMeshNode(*meshNode); diff --git a/examples/logic/09_modules/main.cpp b/examples/logic/09_modules/main.cpp index 37788b23f..4b206b8e4 100644 --- a/examples/logic/09_modules/main.cpp +++ b/examples/logic/09_modules/main.cpp @@ -6,10 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include #include @@ -20,7 +21,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // Create a LuaConfig object which we use to configure how the module will be built. // In this example, we use the 'print' method, so we add the 'Base' standard Lua library diff --git a/examples/logic/10_globals/main.cpp b/examples/logic/10_globals/main.cpp index bc49ddaa0..fba35558a 100644 --- a/examples/logic/10_globals/main.cpp +++ b/examples/logic/10_globals/main.cpp @@ -6,9 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include #include @@ -19,7 +20,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // Create a script which initializes global variables in its init() method // They are used during the interface definition as well as at runtime diff --git a/examples/logic/11_interfaces/main.cpp b/examples/logic/11_interfaces/main.cpp index 263c68cf7..81375bb11 100644 --- a/examples/logic/11_interfaces/main.cpp +++ b/examples/logic/11_interfaces/main.cpp @@ -6,10 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-client.h" #include #include @@ -20,7 +21,10 @@ int main() { - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::RamsesFramework framework{ ramses::RamsesFrameworkConfig{ ramses::EFeatureLevel_Latest } }; + ramses::RamsesClient* client = framework.createClient("client"); + ramses::Scene* scene = client->createScene(ramses::sceneId_t{ 123u }); + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; // Create an interface which could represent a scene with a node that should be translated const std::string_view interfaceSrc = R"( diff --git a/examples/logic/12_anchor_point/main.cpp b/examples/logic/12_anchor_point/main.cpp index aa425228b..19d05f6dc 100644 --- a/examples/logic/12_anchor_point/main.cpp +++ b/examples/logic/12_anchor_point/main.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" - -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" + +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include "SimpleRenderer.h" @@ -51,7 +51,7 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client, std::array< /** * Helper to create a simple animation for given node, see animation example for more details on how to create an animation. */ -void CreateAnimationForNode(ramses::LogicEngine& logicEngine, const ramses::RamsesNodeBinding* nodeBinding); +void CreateAnimationForNode(ramses::LogicEngine& logicEngine, ramses::NodeBinding* nodeBinding); int main() { @@ -68,12 +68,12 @@ int main() */ auto [scene, node3dToTrack, camera3d, cameraSimulating2d] = CreateSceneWithTriangles(*renderer.getClient(), SimpleRenderer::GetDisplaySize()); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * First we need to create a Ramses Logic representation of the node we will track using Ramses binding. */ - ramses::RamsesNodeBinding* nodeToTrackBinding = logicEngine.createRamsesNodeBinding(*node3dToTrack); + ramses::NodeBinding* nodeToTrackBinding = logicEngine.createNodeBinding(*node3dToTrack); /** * Create a simple animation for the 3D mesh we are about to track, see animation example for more details on how to create an animation. @@ -88,7 +88,7 @@ int main() /** * Create camera binding for the camera that is used for rendering the 3D mesh we want to track */ - ramses::RamsesCameraBinding* camera3dBinding = logicEngine.createRamsesCameraBinding(*camera3d); + ramses::CameraBinding* camera3dBinding = logicEngine.createCameraBinding(*camera3d); /** * Finally create anchor point, we must provide not only the node we want to track but also the camera that is used to render the 3D mesh @@ -138,7 +138,7 @@ int main() SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client, std::array displaySize) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "red triangle scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "red triangle scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, float(displaySize[0])/float(displaySize[1]), 0.1f, 100.0f); @@ -146,7 +146,7 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client, std::array< camera->setTranslation({0.0f, 0.0f, 10.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -180,24 +180,24 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client, std::array< effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + const ramses::Effect* effect = scene->createEffect(effectDesc); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); ramses::Appearance* appearanceRed = scene->createAppearance(*effect); - appearanceRed->setInputValue(colorInput, ramses::vec4f{ 1.f, 0.f, 0.f, 1.f }); + appearanceRed->setInputValue(*colorInput, ramses::vec4f{ 1.f, 0.f, 0.f, 1.f }); ramses::Appearance* appearanceWhite = scene->createAppearance(*effect); - appearanceWhite->setInputValue(colorInput, ramses::vec4f{ 1.f, 1.f, 1.f, 1.f }); + appearanceWhite->setInputValue(*colorInput, ramses::vec4f{ 1.f, 1.f, 1.f, 1.f }); appearanceWhite->setDepthFunction(ramses::EDepthFunc::Always); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode1 = scene->createMeshNode("triangle mesh node 1"); meshNode1->setAppearance(*appearanceRed); meshNode1->setIndexCount(3); - meshNode1->setGeometryBinding(*geometry); + meshNode1->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode1); @@ -211,12 +211,12 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client, std::array< ramses::MeshNode* meshNode2 = scene->createMeshNode("triangle mesh node 2"); meshNode2->setAppearance(*appearanceWhite); meshNode2->setIndexCount(3); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); meshNode2->setTranslation({0.f, -5.f, -1.f}); meshNode2->setScaling({10.f, 10.f, 1.f}); ramses::RenderPass* renderPassOrtho = scene->createRenderPass(); - renderPassOrtho->setClearFlags(ramses::EClearFlags_None); + renderPassOrtho->setClearFlags(ramses::EClearFlag::None); renderPassOrtho->setCamera(*orthoCamera); ramses::RenderGroup* renderGroupOrtho = scene->createRenderGroup(); renderPassOrtho->addRenderGroup(*renderGroupOrtho); @@ -230,7 +230,7 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client, std::array< return SceneAndNodes{ scene, meshNode1, camera, orthoCamera }; } -void CreateAnimationForNode(ramses::LogicEngine& logicEngine, const ramses::RamsesNodeBinding* nodeBinding) +void CreateAnimationForNode(ramses::LogicEngine& logicEngine, ramses::NodeBinding* nodeBinding) { ramses::DataArray* animTimestamps = logicEngine.createDataArray(std::vector{ 0.f, 5.f, 10.f, 15.f, 20.f }); // will be interpreted as seconds ramses::DataArray* animKeyframes = logicEngine.createDataArray(std::vector{ {-3.f, -1.f, -1.f}, { 3.f, -1.f, -1.f }, { 3.f, 0.5f, -1.f }, { -3.f, 0.5f, -1.f }, { -3.f, -1.f, -1.f } }); diff --git a/examples/logic/13_render_order/main.cpp b/examples/logic/13_render_order/main.cpp index 191dfeeb3..c11be94d7 100644 --- a/examples/logic/13_render_order/main.cpp +++ b/examples/logic/13_render_order/main.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/EPropertyType.h" -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include "SimpleRenderer.h" @@ -55,16 +55,16 @@ int main() */ auto [scene, renderGroup, triMesh1, triMesh2] = CreateSceneWithTriangles(*renderer.getClient()); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * In order to create a render group binding we need to first specify what elements we want to expose for render order control, * in our case it is the two triangles. */ - ramses::RamsesRenderGroupBindingElements renderGroupElements; + ramses::RenderGroupBindingElements renderGroupElements; renderGroupElements.addElement(*triMesh1, "tri1"); renderGroupElements.addElement(*triMesh2, "tri2"); - ramses::RamsesRenderGroupBinding* renderGroupBinding = logicEngine.createRamsesRenderGroupBinding(*renderGroup, renderGroupElements); + ramses::RenderGroupBinding* renderGroupBinding = logicEngine.createRenderGroupBinding(*renderGroup, renderGroupElements); /** * Show the scene on the renderer @@ -119,7 +119,7 @@ int main() SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "red triangle scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "red triangle scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, float(SimpleRenderer::GetDisplaySize()[0])/float(SimpleRenderer::GetDisplaySize()[1]), 0.1f, 100.0f); @@ -127,7 +127,7 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client) camera->setTranslation({0.0f, 0.0f, 10.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -156,12 +156,12 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client) effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); const ramses::Effect* effect = scene->createEffect(effectDesc); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); ramses::Appearance* appearanceRed = scene->createAppearance(*effect); - appearanceRed->setInputValue(colorInput, ramses::vec4f{ 1.f, 0.f, 0.f, 0.5f }); + appearanceRed->setInputValue(*colorInput, ramses::vec4f{ 1.f, 0.f, 0.f, 0.5f }); ramses::Appearance* appearanceWhite = scene->createAppearance(*effect); - appearanceWhite->setInputValue(colorInput, ramses::vec4f{ 1.f, 1.f, 1.f, 0.5f }); + appearanceWhite->setInputValue(*colorInput, ramses::vec4f{ 1.f, 1.f, 1.f, 0.5f }); // set up blending appearanceRed->setBlendingFactors(ramses::EBlendFactor::SrcAlpha, ramses::EBlendFactor::OneMinusSrcAlpha, ramses::EBlendFactor::One, ramses::EBlendFactor::Zero); @@ -171,21 +171,21 @@ SceneAndNodes CreateSceneWithTriangles(ramses::RamsesClient& client) appearanceWhite->setBlendingOperations(ramses::EBlendOperation::Add, ramses::EBlendOperation::Add); appearanceWhite->setDepthFunction(ramses::EDepthFunc::Disabled); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode1 = scene->createMeshNode("triangle mesh node 1"); meshNode1->setAppearance(*appearanceRed); meshNode1->setIndexCount(3); - meshNode1->setGeometryBinding(*geometry); + meshNode1->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode1); ramses::MeshNode* meshNode2 = scene->createMeshNode("triangle mesh node 2"); meshNode2->setAppearance(*appearanceWhite); meshNode2->setIndexCount(3); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); meshNode2->setTranslation({0.5f, -0.2f, 0.7f}); renderGroup->addMeshNode(*meshNode1); diff --git a/examples/logic/14_skinbinding/main.cpp b/examples/logic/14_skinbinding/main.cpp index 28cefa6d5..6b7050dc8 100644 --- a/examples/logic/14_skinbinding/main.cpp +++ b/examples/logic/14_skinbinding/main.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/TimerNode.h" - -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/TimerNode.h" + +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include "SimpleRenderer.h" @@ -51,7 +51,7 @@ SceneAndAppearance CreateSceneWithSkinnableMesh(ramses::RamsesClient& client); * Helper method which sets up simple animation for a skeleton joint node. * Details on how to set up animations are covered in the animation example. */ -void SetupJointAnimation(const ramses::RamsesNodeBinding& node, ramses::LogicEngine& logicEngine); +void SetupJointAnimation(ramses::NodeBinding& node, ramses::LogicEngine& logicEngine); int main() { @@ -67,7 +67,7 @@ int main() */ auto [scene, appearance] = CreateSceneWithSkinnableMesh(*renderer.getClient()); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Show the scene on the renderer @@ -78,11 +78,11 @@ int main() * First create skeleton joints, each skeleton joint is represented by a Ramses node and a node binding. * Joint 2 is slightly offset in Y axis so that both joints form a simple 2-bone skeleton. */ - auto skeletonJoint1 = scene->createNode(); - auto skeletonJoint2 = scene->createNode(); + auto* skeletonJoint1 = scene->createNode(); + auto* skeletonJoint2 = scene->createNode(); skeletonJoint2->setTranslation({0.f, 1.f, 0.f}); - const auto skeletonJointBinding1 = logicEngine.createRamsesNodeBinding(*skeletonJoint1); - const auto skeletonJointBinding2 = logicEngine.createRamsesNodeBinding(*skeletonJoint2); + auto* skeletonJointBinding1 = logicEngine.createNodeBinding(*skeletonJoint1); + auto* skeletonJointBinding2 = logicEngine.createNodeBinding(*skeletonJoint2); /** * Set up a simple rotation animation for joint 2. @@ -108,16 +108,16 @@ int main() * - appearance binding of the appearance used to render the mesh * - uniform input of the appearance where joint matrices are expected */ - const std::vector skinBindingJoints = { + const std::vector skinBindingJoints = { skeletonJointBinding1, skeletonJointBinding2 }; const std::vector skinBindingInverseBindMatrices = { inverseBindMatrix1, inverseBindMatrix2 }; - ramses::RamsesAppearanceBinding* appearanceBinding = logicEngine.createRamsesAppearanceBinding(*appearance); + ramses::AppearanceBinding* appearanceBinding = logicEngine.createAppearanceBinding(*appearance); - ramses::UniformInput jointMatUniform; - appearance->getEffect().findUniformInput("u_jointMat", jointMatUniform); + std::optional jointMatUniform = appearance->getEffect().findUniformInput("u_jointMat"); + assert(jointMatUniform.has_value()); /** * Finally create instance of skin binding using all the data. @@ -126,7 +126,7 @@ int main() skinBindingJoints, skinBindingInverseBindMatrices, *appearanceBinding, - jointMatUniform); + *jointMatUniform); /** * Note that after this point there is no application logic needed, all the steps needed for skinning happen @@ -166,7 +166,7 @@ int main() SceneAndAppearance CreateSceneWithSkinnableMesh(ramses::RamsesClient& client) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "skinning scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "skinning scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, float(SimpleRenderer::GetDisplaySize()[0])/float(SimpleRenderer::GetDisplaySize()[1]), 0.1f, 100.0f); @@ -174,7 +174,7 @@ SceneAndAppearance CreateSceneWithSkinnableMesh(ramses::RamsesClient& client) camera->setTranslation({0.0f, 1.0f, 10.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -267,22 +267,20 @@ SceneAndAppearance CreateSceneWithSkinnableMesh(ramses::RamsesClient& client) const ramses::Effect* effect = scene->createEffect(effectDesc); ramses::Appearance* appearance = scene->createAppearance(*effect); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); + ramses::Geometry* geometry = scene->createGeometry(*effect); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - ramses::AttributeInput weightsInput; - effect->findAttributeInput("a_weight", weightsInput); - geometry->setInputBuffer(weightsInput, *weights); - ramses::AttributeInput jointIndicesInput; - effect->findAttributeInput("a_joint", jointIndicesInput); - geometry->setInputBuffer(jointIndicesInput, *jointIndices); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional weightsInput = effect->findAttributeInput("a_weight"); + std::optional jointIndicesInput = effect->findAttributeInput("a_joint"); + assert(positionsInput.has_value() && weightsInput.has_value() && jointIndicesInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*weightsInput, *weights); + geometry->setInputBuffer(*jointIndicesInput, *jointIndices); ramses::MeshNode* meshNode = scene->createMeshNode("mesh"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(uint32_t(indexArray.size())); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); @@ -292,7 +290,7 @@ SceneAndAppearance CreateSceneWithSkinnableMesh(ramses::RamsesClient& client) return { scene, appearance }; } -void SetupJointAnimation(const ramses::RamsesNodeBinding& node, ramses::LogicEngine& logicEngine) +void SetupJointAnimation(ramses::NodeBinding& node, ramses::LogicEngine& logicEngine) { ramses::DataArray* animTimestamps = logicEngine.createDataArray(std::vector{ 0.f, 2.f, 4.f }); // will be interpreted as seconds ramses::DataArray* animKeyframes = logicEngine.createDataArray(std::vector{ { 0.f, 0.f, -90.f }, { 0.f, 0.f, 90.f }, { 0.f, 0.f, -90.f } }); diff --git a/examples/logic/15_meshnodebinding/main.cpp b/examples/logic/15_meshnodebinding/main.cpp index 564cb8e1e..1274d992c 100644 --- a/examples/logic/15_meshnodebinding/main.cpp +++ b/examples/logic/15_meshnodebinding/main.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/TimerNode.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/TimerNode.h" -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include "SimpleRenderer.h" @@ -23,7 +23,7 @@ #include /** -* This example demonstrates how to use RamsesMeshNodeBinding in a simple scene. +* This example demonstrates how to use MeshNodeBinding in a simple scene. */ struct SceneAndMesh @@ -52,7 +52,7 @@ int main() */ auto [scene, meshNode] = CreateSceneWithTriangles(*renderer.getClient()); - ramses::LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine() }; /** * Show the scene on the renderer. @@ -60,7 +60,7 @@ int main() renderer.showScene(scene->getSceneId()); /** - * Create control script which will output values meaningful for some of the RamsesMeshNodeBinding inputs. + * Create control script which will output values meaningful for some of the MeshNodeBinding inputs. * This script takes in time ticker and adjusts mesh parameters so that it cycles through up to 3 instances * of it also cycles through subsets of vertices to be used to render it. */ @@ -91,10 +91,10 @@ int main() *controlScript->getInputs()->getChild("ticker")); /** - * Finally we create the RamsesMeshNodeBinding which binds to our mesh. - * Each control script output is linked to the corresponding RamsesMeshNodeBinding's input. + * Finally we create the MeshNodeBinding which binds to our mesh. + * Each control script output is linked to the corresponding MeshNodeBinding's input. */ - const auto meshNodeBinding = logicEngine.createRamsesMeshNodeBinding(*meshNode, "meshNodeBinding"); + const auto meshNodeBinding = logicEngine.createMeshNodeBinding(*meshNode, "meshNodeBinding"); logicEngine.link( *controlScript->getOutputs()->getChild("vertexOffset"), *meshNodeBinding->getInputs()->getChild("vertexOffset")); @@ -105,7 +105,7 @@ int main() *controlScript->getOutputs()->getChild("instanceCount"), *meshNodeBinding->getInputs()->getChild("instanceCount")); /** - * Note that we do not link all the RamsesMeshNodeBinding input properties (e.g. 'indexOffset' is not used here), + * Note that we do not link all the MeshNodeBinding input properties (e.g. 'indexOffset' is not used here), * meaning it will be initialized by Ramses and not modified in any way by Ramses logic. */ @@ -140,7 +140,7 @@ int main() SceneAndMesh CreateSceneWithTriangles(ramses::RamsesClient& client) { - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "skinning scene"); + ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u), "skinning scene"); ramses::PerspectiveCamera* camera = scene->createPerspectiveCamera(); camera->setFrustum(19.0f, float(SimpleRenderer::GetDisplaySize()[0])/float(SimpleRenderer::GetDisplaySize()[1]), 0.1f, 100.0f); @@ -148,7 +148,7 @@ SceneAndMesh CreateSceneWithTriangles(ramses::RamsesClient& client) camera->setTranslation({0.0f, 1.0f, 10.0f}); ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -215,16 +215,16 @@ SceneAndMesh CreateSceneWithTriangles(ramses::RamsesClient& client) ramses::Appearance* appearance = scene->createAppearance(*effect); appearance->setDrawMode(ramses::EDrawMode::TriangleStrip); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); + ramses::Geometry* geometry = scene->createGeometry(*effect); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode = scene->createMeshNode("mesh"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(uint32_t(indexArray.size())); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); diff --git a/examples/logic/CMakeLists.txt b/examples/logic/CMakeLists.txt index ce6d19fd8..ecf3bd602 100644 --- a/examples/logic/CMakeLists.txt +++ b/examples/logic/CMakeLists.txt @@ -21,7 +21,7 @@ function(add_example) INCLUDE_PATHS shared ENABLE_INSTALL OFF SRC_FILES ${EXAMPLE_FILES} - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) if(TARGET ${EXAMPLE_NAME} AND ramses-sdk_BUILD_TESTS AND ${EXAMPLE_MAKE_TEST}) @@ -45,11 +45,13 @@ add_example(NAME 09_modules FILES 09_modules/main.cpp add_example(NAME 10_globals FILES 10_globals/main.cpp MAKE_TEST) add_example(NAME 11_interfaces FILES 11_interfaces/main.cpp MAKE_TEST) -# Can't execute ramses animation examples as test - they have a loop which blocks test automation and wastes resources -add_example(NAME 08a_static_animation FILES 08a_static_animation/main.cpp) -add_example(NAME 08b_dynamic_animation FILES 08b_dynamic_animation/main.cpp) -# Can't execute as test - it is essentially animation with loop which blocks test automation and wastes resources -add_example(NAME 14_skinbinding FILES 14_skinbinding/main.cpp) -add_example(NAME 12_anchor_point FILES 12_anchor_point/main.cpp) -add_example(NAME 13_render_order FILES 13_render_order/main.cpp) -add_example(NAME 15_meshnodebinding FILES 15_meshnodebinding/main.cpp) +if(ramses-sdk_BUILD_FULL_SHARED_LIB) + # Can't execute ramses animation examples as test - they have a loop which blocks test automation and wastes resources + add_example(NAME 08a_static_animation FILES 08a_static_animation/main.cpp) + add_example(NAME 08b_dynamic_animation FILES 08b_dynamic_animation/main.cpp) + # Can't execute as test - it is essentially animation with loop which blocks test automation and wastes resources + add_example(NAME 14_skinbinding FILES 14_skinbinding/main.cpp) + add_example(NAME 12_anchor_point FILES 12_anchor_point/main.cpp) + add_example(NAME 13_render_order FILES 13_render_order/main.cpp) + add_example(NAME 15_meshnodebinding FILES 15_meshnodebinding/main.cpp) +endif() diff --git a/examples/logic/shared/SimpleRenderer.h b/examples/logic/shared/SimpleRenderer.h index f9b5f82be..c67344fac 100644 --- a/examples/logic/shared/SimpleRenderer.h +++ b/examples/logic/shared/SimpleRenderer.h @@ -8,13 +8,13 @@ #pragma once -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererEventHandler.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererEventHandler.h" #include #include diff --git a/examples/ramses-example-basic-blending/CMakeLists.txt b/examples/ramses-example-basic-blending/CMakeLists.txt index 1cdecd3c3..17262db64 100644 --- a/examples/ramses-example-basic-blending/CMakeLists.txt +++ b/examples/ramses-example-basic-blending/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-blending/src/main.cpp b/examples/ramses-example-basic-blending/src/main.cpp index 8d9717fd4..f9f904087 100644 --- a/examples/ramses-example-basic-blending/src/main.cpp +++ b/examples/ramses-example-basic-blending/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include #include @@ -25,7 +25,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "triangles scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "triangles scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -33,7 +34,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -50,7 +51,7 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-blending.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* effect = scene->createEffect(effectDesc, "glsl shader"); // create an appearance for red triangle ramses::Appearance* appearanceRed = scene->createAppearance(*effect, "red triangle appearance"); @@ -59,11 +60,11 @@ int main() // create an appearance for blue triangle ramses::Appearance* appearanceBlue = scene->createAppearance(*effect, "blue triangle appearance"); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // create mesh nodes to define the triangles with different appearances ramses::MeshNode* meshNodeRed = scene->createMeshNode("red triangle mesh node"); @@ -77,11 +78,11 @@ int main() meshNodeBlue->setTranslation({0.2f, 0.2f, -10.f}); // get handle to appearances' input and set color with alpha smaller than 1 - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearanceRed->setInputValue(colorInput, ramses::vec4f{ 1.f, 0.f, 0.f, 0.9f }); - appearanceGreen->setInputValue(colorInput, ramses::vec4f{ 0.f, 1.f, 0.f, 0.6f }); - appearanceBlue->setInputValue(colorInput, ramses::vec4f{ 0.f, 0.f, 1.f, 0.3f }); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); + appearanceRed->setInputValue(*colorInput, ramses::vec4f{ 1.f, 0.f, 0.f, 0.9f }); + appearanceGreen->setInputValue(*colorInput, ramses::vec4f{ 0.f, 1.f, 0.f, 0.6f }); + appearanceBlue->setInputValue(*colorInput, ramses::vec4f{ 0.f, 0.f, 1.f, 0.3f }); /// [Basic Blending Example] // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. @@ -101,9 +102,9 @@ int main() meshNodeBlue->setAppearance(*appearanceBlue); // set same geometry to all mesh nodes - meshNodeRed->setGeometryBinding(*geometry); - meshNodeGreen->setGeometryBinding(*geometry); - meshNodeBlue->setGeometryBinding(*geometry); + meshNodeRed->setGeometry(*geometry); + meshNodeGreen->setGeometry(*geometry); + meshNodeBlue->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered // set render order so that triangles are rendered back to front @@ -116,7 +117,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic std::this_thread::sleep_for(std::chrono::seconds(10)); diff --git a/examples/ramses-example-basic-effect-from-glsl/CMakeLists.txt b/examples/ramses-example-basic-effect-from-glsl/CMakeLists.txt index 93cb3ef26..23af8d9b7 100644 --- a/examples/ramses-example-basic-effect-from-glsl/CMakeLists.txt +++ b/examples/ramses-example-basic-effect-from-glsl/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-effect-from-glsl/src/main.cpp b/examples/ramses-example-basic-effect-from-glsl/src/main.cpp index 52b20b35f..c9af88e43 100644 --- a/examples/ramses-example-basic-effect-from-glsl/src/main.cpp +++ b/examples/ramses-example-basic-effect-from-glsl/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include @@ -24,7 +24,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic glsl effect scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic glsl effect scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -32,7 +33,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -53,32 +54,32 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-effect-from-glsl.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* effect = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effect, "triangle appearance"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); - ramses::UniformInput scaleAndShearInput; - effect->findUniformInput("u_transformations", scaleAndShearInput); + std::optional scaleAndShearInput = effect->findUniformInput("u_transformations"); + assert(scaleAndShearInput.has_value()); const std::array scaleAndShearArrayData = { ramses::vec4f{0.3f, 0.6f, 0.0f, 0.0f}, ramses::vec4f{0.3f, 0.6f, 0.0f, 0.0f} }; - appearance->setInputValue(scaleAndShearInput, 2, scaleAndShearArrayData.data()); + appearance->setInputValue(*scaleAndShearInput, 2, scaleAndShearArrayData.data()); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 0.1f, 0.5f, 0.2f, 1.f }); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); + appearance->setInputValue(*colorInput, ramses::vec4f{ 0.1f, 0.5f, 0.2f, 1.f }); /// [Basic GLSL Import Example] @@ -86,7 +87,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic std::this_thread::sleep_for(std::chrono::seconds(100)); diff --git a/examples/ramses-example-basic-file-loading/CMakeLists.txt b/examples/ramses-example-basic-file-loading/CMakeLists.txt index e553310b8..ae4ac18c3 100644 --- a/examples/ramses-example-basic-file-loading/CMakeLists.txt +++ b/examples/ramses-example-basic-file-loading/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-file-loading/src/main.cpp b/examples/ramses-example-basic-file-loading/src/main.cpp index 291987abf..dceb91963 100644 --- a/examples/ramses-example-basic-file-loading/src/main.cpp +++ b/examples/ramses-example-basic-file-loading/src/main.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include /** @@ -17,20 +17,21 @@ int main() { - ramses::status_t status{}; + bool status = false; // create a scene and write it to a file { ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest}; ramses::RamsesFramework framework(config); ramses::RamsesClient& ramses(*framework.createClient("ramses-example-file-loading")); - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic scene loading from file"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic scene loading from file"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); camera->setViewport(0, 0, 1280u, 480u); camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -57,34 +58,31 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-file-loading-texturing.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effectTex, "triangle appearance"); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effectTex, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effectTex, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - effectTex->findAttributeInput("a_position", positionsInput); - effectTex->findAttributeInput("a_texcoord", texcoordsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texcoordsInput, *textureCoords); - - ramses::UniformInput textureInput; - effectTex->findUniformInput("textureSampler", textureInput); - appearance->setInputTexture(textureInput, *sampler); + std::optional positionsInput = effectTex->findAttributeInput("a_position"); + std::optional texcoordsInput = effectTex->findAttributeInput("a_texcoord"); + std::optional textureInput = effectTex->findUniformInput("textureSampler"); + assert(positionsInput.has_value() && texcoordsInput.has_value() && textureInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*texcoordsInput, *textureCoords); + appearance->setInputTexture(*textureInput, *sampler); ramses::Node* scaleNode = scene->createNode("scale node"); ramses::MeshNode* meshNode = scene->createMeshNode("textured triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); scaleNode->addChild(*meshNode); - status = scene->saveToFile("tempfile.ramses", false); + status = scene->saveToFile("tempfile.ramses", {}); scene->destroy(*vertexPositions); scene->destroy(*textureCoords); @@ -92,35 +90,38 @@ int main() ramses.destroy(*scene); } - // load the saved file - if (status == ramses::StatusOK) + if (!status) { - ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework(config); - ramses::RamsesClient& ramses(*framework.createClient("ramses-example-file-loading")); + return EXIT_FAILURE; + } - /// [Basic File Loading Example] - // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. - // This should not be the case for real applications. - ramses::Scene* loadedScene = ramses.loadSceneFromFile("tempfile.ramses"); + // load the saved file + ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework(config); + ramses::RamsesClient& ramses(*framework.createClient("ramses-example-file-loading")); - // make changes to loaded scene - ramses::RamsesObject* loadedObject = loadedScene->findObjectByName("scale node"); - ramses::Node* loadedScaleNode = ramses::RamsesUtils::TryConvert(*loadedObject); - /// [Basic File Loading Example] + /// [Basic File Loading Example] + // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. + // This should not be the case for real applications. + // Load scene with remote publication enabled. + ramses::Scene* loadedScene = ramses.loadSceneFromFile("tempfile.ramses", ramses::SceneConfig({}, ramses::EScenePublicationMode::LocalAndRemote)); - framework.connect(); + // make changes to loaded scene + auto* loadedScaleNode = loadedScene->findObject("scale node"); + /// [Basic File Loading Example] - loadedScene->publish(); + framework.connect(); - loadedScaleNode->setScaling({2, 2, 2}); + loadedScene->publish(ramses::EScenePublicationMode::LocalAndRemote); - loadedScene->flush(); - std::this_thread::sleep_for(std::chrono::seconds(30)); + loadedScaleNode->setScaling({2, 2, 2}); + + loadedScene->flush(); + std::this_thread::sleep_for(std::chrono::seconds(30)); + + loadedScene->unpublish(); + ramses.destroy(*loadedScene); + framework.disconnect(); - loadedScene->unpublish(); - ramses.destroy(*loadedScene); - framework.disconnect(); - } return 0; } diff --git a/examples/ramses-example-basic-geometry/CMakeLists.txt b/examples/ramses-example-basic-geometry/CMakeLists.txt index 6728ae4eb..2ea1b4386 100644 --- a/examples/ramses-example-basic-geometry/CMakeLists.txt +++ b/examples/ramses-example-basic-geometry/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-geometry/src/main.cpp b/examples/ramses-example-basic-geometry/src/main.cpp index be6777c7d..30d40fc45 100644 --- a/examples/ramses-example-basic-geometry/src/main.cpp +++ b/examples/ramses-example-basic-geometry/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include @@ -24,7 +24,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic geometry scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic geometry scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -32,7 +33,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -51,25 +52,25 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-geometry.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* effect = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effect, "triangle appearance"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect, "triangle geometry"); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect, "triangle geometry"); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // get input data of appearance and bind required data - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); @@ -79,7 +80,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic std::this_thread::sleep_for(std::chrono::seconds(10)); diff --git a/examples/ramses-example-basic-rendergroups/CMakeLists.txt b/examples/ramses-example-basic-rendergroups/CMakeLists.txt index 56d94ef72..f04d4d6cf 100644 --- a/examples/ramses-example-basic-rendergroups/CMakeLists.txt +++ b/examples/ramses-example-basic-rendergroups/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-rendergroups/src/main.cpp b/examples/ramses-example-basic-rendergroups/src/main.cpp index 3cde21d4c..e5835f5b0 100644 --- a/examples/ramses-example-basic-rendergroups/src/main.cpp +++ b/examples/ramses-example-basic-rendergroups/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include @@ -24,7 +24,8 @@ int main() framework.connect(); // create a scene - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic renderpasses scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic renderpasses scene"); // prepare triangle geometry: vertex position array and index array const std::array vertexPositionsData{ ramses::vec3f{-0.5f, 0.f, -1.f}, ramses::vec3f{0.5f, 0.f, -1.f}, ramses::vec3f{-0.5f, 1.f, -1.f}, ramses::vec3f{0.5f, 1.f, -1.f} }; @@ -37,7 +38,7 @@ int main() effectDesc.setVertexShaderFromFile("res/ramses-example-basic-rendergroups.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-rendergroups.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); /// [Basic Render Groups Example] // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. @@ -53,7 +54,7 @@ int main() // create render pass ramses::RenderPass* renderPass = scene->createRenderPass("renderpass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); // set valid camera for the pass renderPass->setCamera(*camera); @@ -71,31 +72,31 @@ int main() ramses::Appearance* appearanceC = scene->createAppearance(*effectTex, "triangle appearance"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effectTex, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effectTex, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effectTex->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effectTex->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // use three appearances, each with a different color for distinguishing the meshes - ramses::UniformInput color; - effectTex->findUniformInput("color", color); - appearanceA->setInputValue(color, ramses::vec4f{ 1.0f,0.0f,0.0f,1.0f }); - appearanceB->setInputValue(color, ramses::vec4f{ 0.0f,1.0f,0.0f,1.0f }); - appearanceC->setInputValue(color, ramses::vec4f{ 0.0f,0.0f,1.0f,1.0f }); + std::optional color = effectTex->findUniformInput("color"); + assert(color.has_value()); + appearanceA->setInputValue(*color, ramses::vec4f{ 1.0f,0.0f,0.0f,1.0f }); + appearanceB->setInputValue(*color, ramses::vec4f{ 0.0f,1.0f,0.0f,1.0f }); + appearanceC->setInputValue(*color, ramses::vec4f{ 0.0f,0.0f,1.0f,1.0f }); ramses::MeshNode* meshNodeA = scene->createMeshNode("red triangle mesh node"); meshNodeA->setAppearance(*appearanceA); - meshNodeA->setGeometryBinding(*geometry); + meshNodeA->setGeometry(*geometry); ramses::MeshNode* meshNodeB = scene->createMeshNode("green triangle mesh node"); meshNodeB->setAppearance(*appearanceB); - meshNodeB->setGeometryBinding(*geometry); + meshNodeB->setGeometry(*geometry); meshNodeB->setTranslation({0.5f, 0.5f, 0.0f}); ramses::MeshNode* meshNodeC = scene->createMeshNode("blue triangle mesh node"); meshNodeC->setAppearance(*appearanceC); - meshNodeC->setGeometryBinding(*geometry); + meshNodeC->setGeometry(*geometry); meshNodeC->setTranslation({0.75f, -0.25f, 0.0f}); // Add meshA with render order '1' to the render group where already the nested render group with render order '3' was added. @@ -113,7 +114,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic std::this_thread::sleep_for(std::chrono::seconds(10)); diff --git a/examples/ramses-example-basic-renderpasses/CMakeLists.txt b/examples/ramses-example-basic-renderpasses/CMakeLists.txt index bfbcbf736..127ce7125 100644 --- a/examples/ramses-example-basic-renderpasses/CMakeLists.txt +++ b/examples/ramses-example-basic-renderpasses/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-renderpasses/src/main.cpp b/examples/ramses-example-basic-renderpasses/src/main.cpp index 5e366a416..508ca821c 100644 --- a/examples/ramses-example-basic-renderpasses/src/main.cpp +++ b/examples/ramses-example-basic-renderpasses/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include @@ -24,7 +24,8 @@ int main() framework.connect(); // create a scene - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic renderpasses scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic renderpasses scene"); // prepare triangle geometry: vertex position array and index array const std::array vertexPositionsData{ ramses::vec3f{-0.5f, 0.f, -1.f}, ramses::vec3f{0.5f, 0.f, -1.f}, ramses::vec3f{-0.5f, 1.f, -1.f}, ramses::vec3f{0.5f, 1.f, -1.f} }; @@ -37,7 +38,7 @@ int main() effectDesc.setVertexShaderFromFile("res/ramses-example-basic-renderpasses.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-renderpasses.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); /// [Basic Renderpasses Example] // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. @@ -63,8 +64,8 @@ int main() // create two renderpasses ramses::RenderPass* renderPassA = scene->createRenderPass("renderpass A"); ramses::RenderPass* renderPassB = scene->createRenderPass("renderpass B"); - renderPassA->setClearFlags(ramses::EClearFlags_None); - renderPassB->setClearFlags(ramses::EClearFlags_None); + renderPassA->setClearFlags(ramses::EClearFlag::None); + renderPassB->setClearFlags(ramses::EClearFlag::None); // set valid cameras for the passes renderPassA->setCamera(*cameraA); @@ -80,25 +81,25 @@ int main() ramses::Appearance* appearanceB = scene->createAppearance(*effectTex, "triangle appearance"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effectTex, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effectTex, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effectTex->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effectTex->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // use two appearances, each with a different color for distinguishing the meshes - ramses::UniformInput color; - effectTex->findUniformInput("color", color); - appearanceA->setInputValue(color, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); - appearanceB->setInputValue(color, ramses::vec4f{ 0.0f, 1.0f, 0.0f, 1.0f }); + std::optional color = effectTex->findUniformInput("color"); + assert(color.has_value()); + appearanceA->setInputValue(*color, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); + appearanceB->setInputValue(*color, ramses::vec4f{ 0.0f, 1.0f, 0.0f, 1.0f }); ramses::MeshNode* meshNodeA = scene->createMeshNode("red triangle mesh node"); meshNodeA->setAppearance(*appearanceA); - meshNodeA->setGeometryBinding(*geometry); + meshNodeA->setGeometry(*geometry); ramses::MeshNode* meshNodeB = scene->createMeshNode("green triangle mesh node"); meshNodeB->setAppearance(*appearanceB); - meshNodeB->setGeometryBinding(*geometry); + meshNodeB->setGeometry(*geometry); // add each mesh to one pass renderGroupA->addMeshNode(*meshNodeA); @@ -109,7 +110,7 @@ int main() // signal the scene it is in a state that can be rendered scene->flush(); - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // distribute the scene to RAMSES std::this_thread::sleep_for(std::chrono::seconds(2)); @@ -117,7 +118,7 @@ int main() uint32_t loops = 10000; - while (--loops) + while (--loops != 0u) { std::this_thread::sleep_for(std::chrono::seconds(2)); } diff --git a/examples/ramses-example-basic-rendertarget/CMakeLists.txt b/examples/ramses-example-basic-rendertarget/CMakeLists.txt index 004031892..905afa770 100644 --- a/examples/ramses-example-basic-rendertarget/CMakeLists.txt +++ b/examples/ramses-example-basic-rendertarget/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-rendertarget/src/main.cpp b/examples/ramses-example-basic-rendertarget/src/main.cpp index 252c6a7bf..93cad79b2 100644 --- a/examples/ramses-example-basic-rendertarget/src/main.cpp +++ b/examples/ramses-example-basic-rendertarget/src/main.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-client-api/RenderTargetDescription.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/RenderTargetDescription.h" #include @@ -25,7 +25,8 @@ int main() framework.connect(); // create a scene - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic rendertarget scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic rendertarget scene"); // prepare triangle geometry: vertex position array and index array const std::array vertexPositionsQuadArray{ ramses::vec3f{-1.5f, -0.75f, -1.f}, ramses::vec3f{1.5f, -0.75f, -1.f}, ramses::vec3f{-1.5f, 0.75f, -1.f}, ramses::vec3f{1.5f, 0.75f, -1.f} }; @@ -45,13 +46,13 @@ int main() triangleEffectDesc.setVertexShaderFromFile("res/ramses-example-basic-rendertarget-simple-color.vert"); triangleEffectDesc.setFragmentShaderFromFile("res/ramses-example-basic-rendertarget-simple-color.frag"); triangleEffectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* triangleEffect = scene->createEffect(triangleEffectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* triangleEffect = scene->createEffect(triangleEffectDesc, "glsl shader"); ramses::EffectDescription quadEffectDesc; quadEffectDesc.setVertexShaderFromFile("res/ramses-example-basic-rendertarget-texturing.vert"); quadEffectDesc.setFragmentShaderFromFile("res/ramses-example-basic-rendertarget-texturing.frag"); quadEffectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* quadEffect = scene->createEffect(quadEffectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* quadEffect = scene->createEffect(quadEffectDesc, "glsl shader"); /// [Basic Rendertarget Example] // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. @@ -76,7 +77,7 @@ int main() // create the renderpasses ramses::RenderPass* renderPassA = scene->createRenderPass("renderpass A"); ramses::RenderPass* renderPassB = scene->createRenderPass("renderpass B"); - renderPassB->setClearFlags(ramses::EClearFlags_None); + renderPassB->setClearFlags(ramses::EClearFlag::None); renderPassA->setClearColor({1.f, 1.f, 1.f, 1.f}); // set valid cameras for the passes @@ -90,7 +91,7 @@ int main() renderPassB->addRenderGroup(*renderGroupB); // create a render target and assign it to renderpass A - ramses::RenderBuffer* renderBuffer = scene->createRenderBuffer(64u, 64u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); + ramses::RenderBuffer* renderBuffer = scene->createRenderBuffer(64u, 64u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); ramses::RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(*renderBuffer); ramses::RenderTarget* renderTarget = scene->createRenderTarget(rtDesc); @@ -99,17 +100,17 @@ int main() // create triangle appearance, get input data and bind required data ramses::Appearance* appearanceA = scene->createAppearance(*triangleEffect, "triangle appearance"); { - ramses::UniformInput color; - triangleEffect->findUniformInput("color", color); - appearanceA->setInputValue(color, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); + std::optional color = triangleEffect->findUniformInput("color"); + assert(color.has_value()); + appearanceA->setInputValue(*color, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); } - ramses::GeometryBinding* geometryA = scene->createGeometryBinding(*triangleEffect, "triangle geometry"); + ramses::Geometry* geometryA = scene->createGeometry(*triangleEffect, "triangle geometry"); { geometryA->setIndices(*indicesTriangle); - ramses::AttributeInput positionsInput; - triangleEffect->findAttributeInput("a_position", positionsInput); - geometryA->setInputBuffer(positionsInput, *vertexPositionsTriangle); + std::optional positionsInput = triangleEffect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometryA->setInputBuffer(*positionsInput, *vertexPositionsTriangle); } // create quad appearance, get input data and bind required data @@ -124,30 +125,29 @@ int main() *renderBuffer); // set render target sampler as input - ramses::UniformInput textureInput; - quadEffect->findUniformInput("textureSampler", textureInput); - appearanceB->setInputTexture(textureInput, *sampler); + std::optional textureInput = quadEffect->findUniformInput("textureSampler"); + assert(textureInput.has_value()); + appearanceB->setInputTexture(*textureInput, *sampler); } - ramses::GeometryBinding* geometryB = scene->createGeometryBinding(*quadEffect, "quad geometry"); + ramses::Geometry* geometryB = scene->createGeometry(*quadEffect, "quad geometry"); { geometryB->setIndices(*indicesQuad); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - quadEffect->findAttributeInput("a_position", positionsInput); - quadEffect->findAttributeInput("a_texcoord", texcoordsInput); - geometryB->setInputBuffer(positionsInput, *vertexPositionsQuad); - geometryB->setInputBuffer(texcoordsInput, *textureCoords); + std::optional positionsInput = quadEffect->findAttributeInput("a_position"); + std::optional texcoordsInput = quadEffect->findAttributeInput("a_texcoord"); + assert(positionsInput.has_value() && texcoordsInput.has_value()); + geometryB->setInputBuffer(*positionsInput, *vertexPositionsQuad); + geometryB->setInputBuffer(*texcoordsInput, *textureCoords); } // create meshs ramses::MeshNode* meshNodeA = scene->createMeshNode("red triangle mesh node"); meshNodeA->setAppearance(*appearanceA); - meshNodeA->setGeometryBinding(*geometryA); + meshNodeA->setGeometry(*geometryA); ramses::MeshNode* meshNodeB = scene->createMeshNode("texture quad mesh node"); meshNodeB->setAppearance(*appearanceB); - meshNodeB->setGeometryBinding(*geometryB); + meshNodeB->setGeometry(*geometryB); // add triangle mesh to first pass and quad to second one renderGroupA->addMeshNode(*meshNodeA); @@ -158,12 +158,12 @@ int main() // signal the scene it is in a state that can be rendered scene->flush(); - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic uint32_t loops = 10000; - while (--loops) + while (--loops != 0u) { std::this_thread::sleep_for(std::chrono::seconds(2)); } diff --git a/examples/ramses-example-basic-scenegraph/CMakeLists.txt b/examples/ramses-example-basic-scenegraph/CMakeLists.txt index 95f8456ce..e6d7efa3e 100644 --- a/examples/ramses-example-basic-scenegraph/CMakeLists.txt +++ b/examples/ramses-example-basic-scenegraph/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-scenegraph/src/main.cpp b/examples/ramses-example-basic-scenegraph/src/main.cpp index 1f4eb0638..2538dd0f6 100644 --- a/examples/ramses-example-basic-scenegraph/src/main.cpp +++ b/examples/ramses-example-basic-scenegraph/src/main.cpp @@ -6,23 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/SceneGraphIterator.h" -#include "ramses-client-api/SceneObjectIterator.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" - -#include +#include "ramses/client/ramses-client.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/SceneGraphIterator.h" +#include "ramses/client/SceneObjectIterator.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" + #include +#include /** * @example ramses-example-basic-scenegraph/src/main.cpp @@ -38,7 +38,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic scenegraph scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic scenegraph scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -46,7 +47,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -63,19 +64,19 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-scenegraph.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // get input data of appearance and bind required data - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 0.5f, 1.0f, 0.3f, 1.0f }); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); + appearance->setInputValue(*colorInput, ramses::vec4f{ 0.5f, 1.0f, 0.3f, 1.0f }); /// [Basic Scene Graph Example] // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. @@ -92,8 +93,8 @@ int main() // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); - meshNode->setTranslation({column * 0.2f, row * 0.2f, 0.0f}); + meshNode->setGeometry(*geometry); + meshNode->setTranslation({static_cast(column) * 0.2f, static_cast(row) * 0.2f, 0.0f}); meshNode->setParent(*group); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); @@ -102,15 +103,15 @@ int main() /// [Basic Scene Graph Example] // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); //example: how to traverse scene graph ramses::SceneGraphIterator graphIterator(*group, ramses::ETreeTraversalStyle::DepthFirst); - ramses::Node* nextNode; - printf("Scene graph traversed in depth first order: \n"); + ramses::Node* nextNode = nullptr; + std::cout << "Scene graph traversed in depth first order:" << std::endl; while ((nextNode = graphIterator.getNext()) != nullptr) { - printf("Node: %s \n", nextNode->getName()); + std::cout << "Node: " << nextNode->getName() << std::endl; } //example: how to iterate through objects of scene @@ -120,7 +121,7 @@ int main() { ++numberOfMeshes; } - printf("Scene contains %i meshnodes\n", numberOfMeshes); + std::cout << "Scene contains " << numberOfMeshes << " meshnodes" << std::endl; scene->flush(); diff --git a/examples/ramses-example-basic-texturing/CMakeLists.txt b/examples/ramses-example-basic-texturing/CMakeLists.txt index e63938569..016fc32e5 100644 --- a/examples/ramses-example-basic-texturing/CMakeLists.txt +++ b/examples/ramses-example-basic-texturing/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-basic-texturing/src/main.cpp b/examples/ramses-example-basic-texturing/src/main.cpp index 6509961b4..7a9cca559 100644 --- a/examples/ramses-example-basic-texturing/src/main.cpp +++ b/examples/ramses-example-basic-texturing/src/main.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include @@ -25,7 +25,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic texturing scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic texturing scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -33,7 +34,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -67,27 +68,24 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-basic-texturing.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effectTex, "triangle appearance"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effectTex, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effectTex, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - effectTex->findAttributeInput("a_position", positionsInput); - effectTex->findAttributeInput("a_texcoord", texcoordsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texcoordsInput, *textureCoords); - - ramses::UniformInput textureInput; - effectTex->findUniformInput("textureSampler", textureInput); - appearance->setInputTexture(textureInput, *sampler); + std::optional positionsInput = effectTex->findAttributeInput("a_position"); + std::optional texcoordsInput = effectTex->findAttributeInput("a_texcoord"); + std::optional textureInput = effectTex->findUniformInput("textureSampler"); + assert(positionsInput.has_value() && texcoordsInput.has_value() && textureInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*texcoordsInput, *textureCoords); + appearance->setInputTexture(*textureInput, *sampler); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = scene->createMeshNode("textured triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); /// [Basic Texturing Example] @@ -96,7 +94,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic std::this_thread::sleep_for(std::chrono::seconds(100)); diff --git a/examples/ramses-example-data-buffers-texture/CMakeLists.txt b/examples/ramses-example-data-buffers-texture/CMakeLists.txt index a122c4f48..8f40a68a6 100644 --- a/examples/ramses-example-data-buffers-texture/CMakeLists.txt +++ b/examples/ramses-example-data-buffers-texture/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-data-buffers-texture/src/main.cpp b/examples/ramses-example-data-buffers-texture/src/main.cpp index 713b19fee..4ab6f85da 100644 --- a/examples/ramses-example-data-buffers-texture/src/main.cpp +++ b/examples/ramses-example-data-buffers-texture/src/main.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include #include @@ -50,7 +50,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic texturing scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic texturing scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -58,7 +59,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 8.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -106,17 +107,17 @@ int main() const uint32_t regionDataSizeLevel0 = regionWidthLevel0 * regionHeightLevel0 * numChannels; const uint32_t regionDataSizeLevel1 = regionWidthLevel1 * regionHeightLevel1 * numChannels; - std::array textureDataLevel0; - std::array textureDataLevel1; - std::array regionDataLevel0; - std::array regionDataLevel1; + std::array textureDataLevel0{}; + std::array textureDataLevel1{}; + std::array regionDataLevel0{}; + std::array regionDataLevel1{}; // A helper function to cast and map a float value [0.f, 1.f[ to uint8 [0,255] auto convertToUInt8 = [](float value)->uint8_t { // by substracting the integer part, we just keep // the fractional part [0.f,1.f[ - value -= static_cast(value); + value -= static_cast(static_cast(value)); return static_cast(value * 255.f); }; @@ -165,45 +166,42 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-data-buffers-texture.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearanceNear = scene->createAppearance(*effectTex, "quad appearance (near)"); ramses::Appearance* appearanceFar = scene->createAppearance(*effectTex, "quad appearance (far)"); // set vertex positions directly in geometry - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - ramses::UniformInput mipmapLevelInput; - effectTex->findAttributeInput("a_position", positionsInput); - effectTex->findAttributeInput("a_texcoord", texcoordsInput); + std::optional positionsInput = effectTex->findAttributeInput("a_position"); + std::optional texcoordsInput = effectTex->findAttributeInput("a_texcoord"); // for sake of showing the different mipmap levels explicitly, a uniform selecting // a specific mipmap level has been added to the fragment shader - effectTex->findUniformInput("mipmapLevel", mipmapLevelInput); + std::optional mipmapLevelInput = effectTex->findUniformInput("mipmapLevel"); + assert(positionsInput.has_value() && texcoordsInput.has_value() && mipmapLevelInput.has_value()); - ramses::GeometryBinding* geometryNear = scene->createGeometryBinding(*effectTex, "quad geometry (near)"); - geometryNear->setInputBuffer(positionsInput, *vertexPositionsNear); - geometryNear->setInputBuffer(texcoordsInput, *textureCoords); + ramses::Geometry* geometryNear = scene->createGeometry(*effectTex, "quad geometry (near)"); + geometryNear->setInputBuffer(*positionsInput, *vertexPositionsNear); + geometryNear->setInputBuffer(*texcoordsInput, *textureCoords); geometryNear->setIndices(*indices); - ramses::GeometryBinding* geometryFar = scene->createGeometryBinding(*effectTex, "quad geometry (far)"); - geometryFar->setInputBuffer(positionsInput, *vertexPositionsFar); - geometryFar->setInputBuffer(texcoordsInput, *textureCoords); + ramses::Geometry* geometryFar = scene->createGeometry(*effectTex, "quad geometry (far)"); + geometryFar->setInputBuffer(*positionsInput, *vertexPositionsFar); + geometryFar->setInputBuffer(*texcoordsInput, *textureCoords); geometryFar->setIndices(*indices); - ramses::UniformInput textureInput; - effectTex->findUniformInput("textureSampler", textureInput); - appearanceNear->setInputTexture(textureInput, *sampler); - appearanceFar->setInputTexture(textureInput, *sampler); + std::optional textureInput = effectTex->findUniformInput("textureSampler"); + appearanceNear->setInputTexture(*textureInput, *sampler); + appearanceFar->setInputTexture(*textureInput, *sampler); - appearanceNear->setInputValue(mipmapLevelInput, 0); - appearanceFar->setInputValue(mipmapLevelInput, 1); + appearanceNear->setInputValue(*mipmapLevelInput, 0); + appearanceFar->setInputValue(*mipmapLevelInput, 1); // create a mesh node to define the quad with chosen appearance ramses::MeshNode* meshNodeNear = scene->createMeshNode("textured quad mesh node (near)"); meshNodeNear->setAppearance(*appearanceNear); - meshNodeNear->setGeometryBinding(*geometryNear); + meshNodeNear->setGeometry(*geometryNear); ramses::MeshNode* meshNodeFar = scene->createMeshNode("textured quad mesh node (far)"); meshNodeFar->setAppearance(*appearanceFar); - meshNodeFar->setGeometryBinding(*geometryFar); + meshNodeFar->setGeometry(*geometryFar); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered @@ -214,7 +212,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); /// [Data Buffer Texture Example Update Loop] // application logic @@ -325,7 +323,7 @@ template< uint32_t TotalWidth, uint32_t TotalHeight, float IndexIterator::getPositionXLocal(float cycleOffset) { const float shiftedPosition = (static_cast(x) / static_cast(RegionWidth)) + cycleOffset; - const float positionFractionalPart = shiftedPosition - static_cast(shiftedPosition); + const auto positionFractionalPart = shiftedPosition - static_cast(static_cast(shiftedPosition)); return positionFractionalPart; } @@ -346,7 +344,7 @@ template< uint32_t TotalWidth, uint32_t TotalHeight, float IndexIterator::getPositionYLocal(float cycleOffset) { const float shiftedPosition = (static_cast(y) / static_cast(RegionHeight)) + cycleOffset; - const float positionFractionalPart = shiftedPosition - static_cast(shiftedPosition); + const auto positionFractionalPart = shiftedPosition - static_cast(static_cast(shiftedPosition)); return positionFractionalPart; } diff --git a/examples/ramses-example-data-buffers-vertices/CMakeLists.txt b/examples/ramses-example-data-buffers-vertices/CMakeLists.txt index 5c74c2b7f..a571e5c4d 100644 --- a/examples/ramses-example-data-buffers-vertices/CMakeLists.txt +++ b/examples/ramses-example-data-buffers-vertices/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-data-buffers-vertices/src/main.cpp b/examples/ramses-example-data-buffers-vertices/src/main.cpp index 58bfdb053..8fb48bcbe 100644 --- a/examples/ramses-example-data-buffers-vertices/src/main.cpp +++ b/examples/ramses-example-data-buffers-vertices/src/main.cpp @@ -6,9 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include #include #include #include @@ -28,7 +27,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "triangle scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "triangle scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -36,7 +36,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 35.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -47,14 +47,14 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-data-buffers-vertices.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effect, "triangle appearance data buffer"); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect, "triangle geometry data buffer"); + ramses::Geometry* geometry = scene->createGeometry(*effect, "triangle geometry data buffer"); // get input data of appearance and set color values - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 0.0f, 1.0f, 1.0f, 1.0f }); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); + appearance->setInputValue(*colorInput, ramses::vec4f{ 0.0f, 1.0f, 1.0f, 1.0f }); // the raw data for vertices and indices const std::vector vertexPositions = { @@ -83,8 +83,8 @@ int main() // Create the ArrayBuffers // The data buffers need more information about size - const uint32_t NumVertices = uint32_t(vertexPositions.size()); - const uint32_t NumIndices = uint32_t(indexData.size()); + const auto NumVertices = uint32_t(vertexPositions.size()); + const auto NumIndices = uint32_t(indexData.size()); // then create the buffers via the _scene_ ramses::ArrayBuffer* vertices = scene->createArrayBuffer(ramses::EDataType::Vector3F, NumVertices, "some varying vertices"); @@ -97,9 +97,9 @@ int main() /// [Data Buffer Example Setup] // applying the vertex/index data to the geometry binding is the same for both - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertices); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertices); geometry->setIndices(*indices); @@ -107,7 +107,7 @@ int main() ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node data buffers"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); @@ -116,11 +116,11 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic auto translateVertex = [&vertexPositions](ramses::vec3f& updatedValues, uint32_t index, uint64_t timeStamp, float scaleFactor){ - const float currentFactor = static_cast(std::sin(0.005f*timeStamp)); + const auto currentFactor = std::sin(0.005f * static_cast(timeStamp)); const float xValue = vertexPositions[index][0]; const float yValue = vertexPositions[index][1]; const float zValue = vertexPositions[index][2]; diff --git a/examples/ramses-example-geometry-instancing/CMakeLists.txt b/examples/ramses-example-geometry-instancing/CMakeLists.txt index 3904f8e2e..d819a9b32 100644 --- a/examples/ramses-example-geometry-instancing/CMakeLists.txt +++ b/examples/ramses-example-geometry-instancing/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-geometry-instancing/src/main.cpp b/examples/ramses-example-geometry-instancing/src/main.cpp index 81ae5cc81..458ad7e33 100644 --- a/examples/ramses-example-geometry-instancing/src/main.cpp +++ b/examples/ramses-example-geometry-instancing/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include #include @@ -25,7 +25,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "geometry instancing scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "geometry instancing scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -33,7 +34,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -55,29 +56,27 @@ int main() effectDescUniform.setFragmentShaderFromFile("res/ramses-example-geometry-instancing-uniform.frag"); effectDescUniform.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* uniformEffect = scene->createEffect(effectDescUniform, ramses::ResourceCacheFlag_DoNotCache, "uniform-instancing"); + const ramses::Effect* uniformEffect = scene->createEffect(effectDescUniform, "uniform-instancing"); ramses::Appearance* uniformAppearance = scene->createAppearance(*uniformEffect, "triangle_uniforms"); // get input data of appearance and bind required data - ramses::UniformInput uniformInstanceTranslationInput; - uniformEffect->findUniformInput("translations", uniformInstanceTranslationInput); - std::array translations; - - ramses::UniformInput uniformInstanceColorInput; - uniformEffect->findUniformInput("colors", uniformInstanceColorInput); - std::array colors; + std::array translations{}; + std::array colors{}; + std::optional uniformInstanceTranslationInput = uniformEffect->findUniformInput("translations"); + std::optional uniformInstanceColorInput = uniformEffect->findUniformInput("colors"); + assert(uniformInstanceTranslationInput.has_value() && uniformInstanceColorInput.has_value()); // set vertex positions directly in geometry - ramses::GeometryBinding* uniformGeometry = scene->createGeometryBinding(*uniformEffect, "triangle geometry uniforms"); + ramses::Geometry* uniformGeometry = scene->createGeometry(*uniformEffect, "triangle geometry uniforms"); uniformGeometry->setIndices(*indices); - ramses::AttributeInput uniformPositionInput; - uniformEffect->findAttributeInput("a_position", uniformPositionInput); - uniformGeometry->setInputBuffer(uniformPositionInput, *vertexPositions); + std::optional uniformPositionInput = uniformEffect->findAttributeInput("a_position"); + assert(uniformPositionInput.has_value()); + uniformGeometry->setInputBuffer(*uniformPositionInput, *vertexPositions); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* uniformMeshNode = scene->createMeshNode("uniform-instanced triangle"); uniformMeshNode->setAppearance(*uniformAppearance); - uniformMeshNode->setGeometryBinding(*uniformGeometry); + uniformMeshNode->setGeometry(*uniformGeometry); uniformMeshNode->setInstanceCount(10u); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*uniformMeshNode); @@ -90,15 +89,15 @@ int main() effectDescVertex.setFragmentShaderFromFile("res/ramses-example-geometry-instancing-vertex.frag"); effectDescVertex.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* vertexEffect = scene->createEffect(effectDescVertex, ramses::ResourceCacheFlag_DoNotCache, "vertex-instancing"); + const ramses::Effect* vertexEffect = scene->createEffect(effectDescVertex, "vertex-instancing"); ramses::Appearance* vertexAppearance = scene->createAppearance(*vertexEffect, "triangle_uniforms"); // set vertex positions directly in geometry - ramses::GeometryBinding* vertexGeometry = scene->createGeometryBinding(*vertexEffect, "triangle geometry uniforms"); + ramses::Geometry* vertexGeometry = scene->createGeometry(*vertexEffect, "triangle geometry uniforms"); vertexGeometry->setIndices(*indices); - ramses::AttributeInput vertexPositionInput; - vertexEffect->findAttributeInput("a_position", vertexPositionInput); - vertexGeometry->setInputBuffer(vertexPositionInput, *vertexPositions); + std::optional vertexPositionInput = vertexEffect->findAttributeInput("a_position"); + assert(vertexPositionInput.has_value()); + vertexGeometry->setInputBuffer(*vertexPositionInput, *vertexPositions); // prepare triangle geometry: vertex position array and index array const std::array vertexInstanceTranslationArray{ @@ -116,24 +115,23 @@ int main() const ramses::ArrayResource* vertexTranslations = scene->createArrayResource(4u, vertexInstanceTranslationArray.data()); const ramses::ArrayResource* vertexColors = scene->createArrayResource(4u, vertexInstanceColorArray.data()); - ramses::AttributeInput vertexTranslationInput; - vertexEffect->findAttributeInput("translation", vertexTranslationInput); - vertexGeometry->setInputBuffer(vertexTranslationInput, *vertexTranslations, 1u); - ramses::AttributeInput vertexColorInput; - vertexEffect->findAttributeInput("color", vertexColorInput); - vertexGeometry->setInputBuffer(vertexColorInput, *vertexColors, 1u); + std::optional vertexTranslationInput = vertexEffect->findAttributeInput("translation"); + std::optional vertexColorInput = vertexEffect->findAttributeInput("color"); + assert(vertexTranslationInput.has_value() && vertexColorInput.has_value()); + vertexGeometry->setInputBuffer(*vertexTranslationInput, *vertexTranslations, 1u); + vertexGeometry->setInputBuffer(*vertexColorInput, *vertexColors, 1u); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* vertexMeshNode = scene->createMeshNode("vertex-instanced triangle"); vertexMeshNode->setAppearance(*vertexAppearance); - vertexMeshNode->setGeometryBinding(*vertexGeometry); + vertexMeshNode->setGeometry(*vertexGeometry); vertexMeshNode->setInstanceCount(4u); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*vertexMeshNode); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic uint32_t t = 0; @@ -142,21 +140,21 @@ int main() // update translations of triangle instances for (uint32_t i = 0; i < 10; i++) { - translations[i][0] = -3.0f + i * 0.7f; - translations[i][1] = -0.5f + static_cast(std::sin(i+0.05f*t)); + translations[i][0] = -3.0f + static_cast(i) * 0.7f; + translations[i][1] = -0.5f + std::sin(static_cast(i) + 0.05f * static_cast(t)); translations[i][2] = -5.0f; } - uniformAppearance->setInputValue(uniformInstanceTranslationInput, 10, translations.data()); + uniformAppearance->setInputValue(*uniformInstanceTranslationInput, 10, translations.data()); // update color of triangle instances for (uint32_t i = 0; i < 10; i++) { colors[i][0] = 1.0f; - colors[i][1] = 0.75f * static_cast(std::sin(i+0.05f*t)); + colors[i][1] = 0.75f * std::sin(static_cast(i) + 0.05f * static_cast(t)); colors[i][2] = 0.2f; colors[i][3] = 1.0f; } - uniformAppearance->setInputValue(uniformInstanceColorInput, 10, colors.data()); + uniformAppearance->setInputValue(*uniformInstanceColorInput, 10, colors.data()); scene->flush(); t++; diff --git a/examples/ramses-example-interleaved-vertex-buffers/CMakeLists.txt b/examples/ramses-example-interleaved-vertex-buffers/CMakeLists.txt index e1d986df3..27173ec99 100644 --- a/examples/ramses-example-interleaved-vertex-buffers/CMakeLists.txt +++ b/examples/ramses-example-interleaved-vertex-buffers/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-interleaved-vertex-buffers/src/main.cpp b/examples/ramses-example-interleaved-vertex-buffers/src/main.cpp index 916feb889..663f56dfd 100644 --- a/examples/ramses-example-interleaved-vertex-buffers/src/main.cpp +++ b/examples/ramses-example-interleaved-vertex-buffers/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include @@ -24,7 +24,8 @@ int main() framework.connect(); // create a scene for distributing content - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "interleaved vertex buffers scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "interleaved vertex buffers scene"); // every scene needs a render pass with camera auto* camera = scene->createPerspectiveCamera("my camera"); @@ -32,7 +33,7 @@ int main() camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -54,7 +55,7 @@ int main() }; // interleaved data must be created as ByteBlob and passed as byte array ramses::ArrayBuffer* vertexDataBuffer = scene->createArrayBuffer(ramses::EDataType::ByteBlob, sizeof(vertexData)); - vertexDataBuffer->updateData(0u, sizeof(vertexData), reinterpret_cast(vertexData.data())); + vertexDataBuffer->updateData(0u, sizeof(vertexData), reinterpret_cast(vertexData.data())); // create an appearance for triangle ramses::EffectDescription effectDesc; @@ -62,15 +63,14 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-interleaved-vertex-buffers.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* effect = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effect, "appearance"); // set vertex positions and color in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect, "geometry"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput colorsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_color", colorsInput); + ramses::Geometry* geometry = scene->createGeometry(*effect, "geometry"); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional colorsInput = effect->findAttributeInput("a_color"); + assert(positionsInput.has_value() && colorsInput.has_value()); //positions have Zero offset constexpr uint16_t positionsOffset = 0u; @@ -85,14 +85,14 @@ int main() //constexpr uint16_t colorsOffset = positionsOffset + uint16_t(ramses::GetSizeOfDataType(ramses::EDataType::Vector4F)); //constexpr uint16_t stride = colorsOffset + uint16_t(ramses::GetSizeOfDataType(ramses::EDataType::Vector3F)); - geometry->setInputBuffer(positionsInput, *vertexDataBuffer, positionsOffset, stride); - geometry->setInputBuffer(colorsInput, *vertexDataBuffer, colorsOffset, stride); + geometry->setInputBuffer(*positionsInput, *vertexDataBuffer, positionsOffset, stride); + geometry->setInputBuffer(*colorsInput, *vertexDataBuffer, colorsOffset, stride); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); /// [Interleaved Vertex Buffers Example] // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); @@ -102,7 +102,7 @@ int main() scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic std::this_thread::sleep_for(std::chrono::seconds(10)); diff --git a/examples/ramses-example-local-client/src/main.cpp b/examples/ramses-example-local-client/src/main.cpp index 03a355c67..9a863a076 100644 --- a/examples/ramses-example-local-client/src/main.cpp +++ b/examples/ramses-example-local-client/src/main.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" #include #include #include @@ -59,14 +59,14 @@ int main() //client scene const ramses::sceneId_t sceneId(1u); - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local client example scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "local client example scene"); // every scene needs a render pass with camera auto* camera = clientScene->createPerspectiveCamera("my camera"); camera->setViewport(0, 0, 1280u, 480u); camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -83,27 +83,25 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-local-client-test.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional colorInput = effect->findUniformInput("color"); + assert(positionsInput.has_value() && colorInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); meshNode->setTranslation({0.0f, 0.0f, -5.0f}); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); clientScene->publish(ramses::EScenePublicationMode::LocalOnly); clientScene->flush(); diff --git a/examples/ramses-example-local-compositing/src/main.cpp b/examples/ramses-example-local-compositing/src/main.cpp index e7ccca37c..35335be35 100644 --- a/examples/ramses-example-local-compositing/src/main.cpp +++ b/examples/ramses-example-local-compositing/src/main.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-utils.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include "ramses-cli.h" -#include #include +#include /** * @example ramses-example-local-compositing/src/main.cpp @@ -36,11 +36,11 @@ class RendererEventHandler : public ramses::RendererEventHandlerEmpty, public ra { } - void displayCreated(ramses::displayId_t, ramses::ERendererEventResult result) override + void displayCreated(ramses::displayId_t /*displayId*/, ramses::ERendererEventResult result) override { if (result != ramses::ERendererEventResult::Ok) { - printf("Failed creating display!\n"); + std::cout << "Failed creating display!" << std::endl; return; } @@ -131,7 +131,7 @@ int main(int argc, char* argv[]) CLI11_PARSE(cli, argc, argv); - printf("using wayland ivi surface id: %u for stream buffer\n", surfaceId.getValue()); + std::cout << "using wayland ivi surface id: " << surfaceId.getValue() << " for stream buffer" << std::endl; ramses::RamsesFramework framework(config); ramses::RamsesClient& client(*framework.createClient("ramses-local-compositing-example")); @@ -153,7 +153,7 @@ int main(int argc, char* argv[]) camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -188,35 +188,29 @@ int main(int argc, char* argv[]) effectDesc.setVertexShaderFromFile("res/ramses-example-local-compositing.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-example-local-compositing.frag"); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effectTex, "triangle appearance"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effectTex, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effectTex, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - effectTex->findAttributeInput("a_position", positionsInput); - effectTex->findAttributeInput("a_texcoord", texcoordsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texcoordsInput, *textureCoords); - - ramses::UniformInput textureInput; - effectTex->findUniformInput("textureSampler", textureInput); - appearance->setInputTexture(textureInput, *sampler); + std::optional positionsInput = effectTex->findAttributeInput("a_position"); + std::optional texcoordsInput = effectTex->findAttributeInput("a_texcoord"); + std::optional textureInput = effectTex->findUniformInput("textureSampler"); + assert(positionsInput.has_value() && texcoordsInput.has_value() && textureInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*texcoordsInput, *textureCoords); + appearance->setInputTexture(*textureInput, *sampler); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = scene->createMeshNode("textured triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); - // signal the scene it is in a state that can be rendered - scene->flush(); - // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalOnly); // show the scene on the renderer sceneControlAPI.setSceneMapping(sceneId, display); @@ -227,6 +221,8 @@ int main(int argc, char* argv[]) RendererEventHandler eventHandler(renderer, display, surfaceId); for (uint32_t i = 0u; i < 6000u; ++i) { + scene->flush(); // flush scene periodically so its data can be transferred to renderer + renderer.dispatchEvents(eventHandler); sceneControlAPI.dispatchEvents(eventHandler); diff --git a/examples/ramses-example-local-datalink/src/main.cpp b/examples/ramses-example-local-datalink/src/main.cpp index dabd57c12..3be75c61a 100644 --- a/examples/ramses-example-local-datalink/src/main.cpp +++ b/examples/ramses-example-local-datalink/src/main.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/ramses-utils.h" #include #include #include @@ -37,12 +37,12 @@ class RendererAndSceneStateEventHandler : public ramses::RendererEventHandlerEmp { } - void dataProviderCreated(ramses::sceneId_t sceneId, ramses::dataProviderId_t) override + void dataProviderCreated(ramses::sceneId_t sceneId, ramses::dataProviderId_t /*dataProviderId*/) override { scenesWithCreatedProviderOrConsumer.insert(sceneId); } - void dataConsumerCreated(ramses::sceneId_t sceneId, ramses::dataConsumerId_t) override + void dataConsumerCreated(ramses::sceneId_t sceneId, ramses::dataConsumerId_t /*dataConsumerId*/) override { scenesWithCreatedProviderOrConsumer.insert(sceneId); } @@ -53,9 +53,16 @@ class RendererAndSceneStateEventHandler : public ramses::RendererEventHandlerEmp mappedScenes.insert(sceneId); } - void waitForSceneReady(ramses::sceneId_t sceneId) + void waitForSceneReady(ramses::Scene& scene) { - waitUntilOrTimeout([&] { return mappedScenes.count(sceneId) != 0; }); + const std::chrono::steady_clock::time_point timeoutTS = std::chrono::steady_clock::now() + std::chrono::seconds{ 5 }; + while (mappedScenes.count(scene.getSceneId()) == 0 && std::chrono::steady_clock::now() < timeoutTS) + { + scene.flush(); + m_renderer.doOneLoop(); + m_renderer.dispatchEvents(*this); + m_sceneControlApi.dispatchEvents(*this); + } } void waitForDataProviderOrConsumerCreated(ramses::sceneId_t sceneId) @@ -67,7 +74,7 @@ class RendererAndSceneStateEventHandler : public ramses::RendererEventHandlerEmp } } - void windowClosed(ramses::displayId_t) override + void windowClosed(ramses::displayId_t /*displayId*/) override { m_windowClosed = true; } @@ -78,19 +85,6 @@ class RendererAndSceneStateEventHandler : public ramses::RendererEventHandlerEmp } private: - bool waitUntilOrTimeout(const std::function& conditionFunction) - { - const std::chrono::steady_clock::time_point timeoutTS = std::chrono::steady_clock::now() + std::chrono::seconds{ 5 }; - while (!conditionFunction() && std::chrono::steady_clock::now() < timeoutTS) - { - m_renderer.doOneLoop(); - m_renderer.dispatchEvents(*this); - m_sceneControlApi.dispatchEvents(*this); - } - - return conditionFunction(); - } - ramses::RendererSceneControl& m_sceneControlApi; ramses::RamsesRenderer& m_renderer; std::unordered_set mappedScenes; @@ -131,14 +125,14 @@ std::unique_ptr createTriangleSceneContent(ramses::RamsesClie { auto sceneInfo = std::make_unique(); - sceneInfo->scene = client.createScene(sceneId, ramses::SceneConfig(), "triangle scene"); + sceneInfo->scene = client.createScene(sceneId, "triangle scene"); // every scene needs a render pass with camera auto* camera = sceneInfo->scene->createPerspectiveCamera("my camera"); camera->setViewport(0, 0, 1280u, 480u); camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); ramses::RenderPass* renderPass = sceneInfo->scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = sceneInfo->scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -156,25 +150,24 @@ std::unique_ptr createTriangleSceneContent(ramses::RamsesClie effectDesc.setFragmentShaderFromFile("res/ramses-example-local-datalink.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = sceneInfo->scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = sceneInfo->scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = sceneInfo->scene->createAppearance(*effect, "triangle appearance"); appearance->setCullingMode(ramses::ECullMode::Disabled); appearance->setDepthFunction(ramses::EDepthFunc::Always); - ramses::GeometryBinding* geometry = sceneInfo->scene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = sceneInfo->scene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional colorInput = effect->findUniformInput("color"); + assert(positionsInput.has_value() && colorInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); //bind input to data object sceneInfo->colorData = sceneInfo->scene->createDataObject(ramses::EDataType::Vector4F, "colorData"); sceneInfo->colorData->setValue(ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); - appearance->bindInput(colorInput, *sceneInfo->colorData); + appearance->bindInput(*colorInput, *sceneInfo->colorData); ramses::Node* rootTranslation = sceneInfo->scene->createNode("root scene translation node"); rootTranslation->setTranslation({0.0f, 0.0f, -1.0f}); @@ -185,7 +178,7 @@ std::unique_ptr createTriangleSceneContent(ramses::RamsesClie // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = sceneInfo->scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); meshNode->setParent(*sceneInfo->translateNode); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); @@ -201,14 +194,14 @@ std::unique_ptr createQuadSceneContent(ramses::RamsesClient& clie { auto sceneInfo = std::make_unique(); - sceneInfo->scene = client.createScene(sceneId, ramses::SceneConfig(), "quad scene"); + sceneInfo->scene = client.createScene(sceneId, "quad scene"); // every scene needs a render pass with camera auto* camera = sceneInfo->scene->createPerspectiveCamera("my camera"); camera->setViewport(0, 0, 1280u, 480u); camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); ramses::RenderPass* renderPass = sceneInfo->scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = sceneInfo->scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -235,32 +228,26 @@ std::unique_ptr createQuadSceneContent(ramses::RamsesClient& clie effectDesc.setFragmentShaderFromFile("res/ramses-example-local-datalink-texturing.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = sceneInfo->scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = sceneInfo->scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = sceneInfo->scene->createAppearance(*effect, "quad appearance"); appearance->setDepthFunction(ramses::EDepthFunc::Always); appearance->setCullingMode(ramses::ECullMode::Disabled); - ramses::GeometryBinding* geometry = sceneInfo->scene->createGeometryBinding(*effect, "quad geometry"); + ramses::Geometry* geometry = sceneInfo->scene->createGeometry(*effect, "quad geometry"); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional texCoordsInput = effect->findAttributeInput("a_texcoord"); + std::optional colorInput = effect->findUniformInput("color"); + std::optional textureInput = effect->findUniformInput("textureSampler"); + assert(positionsInput.has_value() && texCoordsInput.has_value() && colorInput.has_value() && textureInput.has_value()); geometry->setIndices(*indices); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*texCoordsInput, *texCoords); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 1.0f, 1.0f, 1.0f }); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_texcoord", texCoordsInput); - geometry->setInputBuffer(texCoordsInput, *texCoords); - - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 1.0f, 1.0f, 1.0f }); - - ramses::UniformInput textureInput; - effect->findUniformInput("textureSampler", textureInput); ramses::Texture2D* fallbackTexture = ramses::RamsesUtils::CreateTextureResourceFromPng("res/ramses-example-local-datalink-texture-fallback.png", *sceneInfo->scene); sceneInfo->textureSampler = sceneInfo->scene->createTextureSampler(ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Linear, ramses::ETextureSamplingMethod::Linear, *fallbackTexture); - appearance->setInputTexture(textureInput, *sceneInfo->textureSampler); + appearance->setInputTexture(*textureInput, *sceneInfo->textureSampler); sceneInfo->consumerNode = sceneInfo->scene->createNode("quad root node"); sceneInfo->rotateNode = sceneInfo->scene->createNode(""); @@ -268,7 +255,7 @@ std::unique_ptr createQuadSceneContent(ramses::RamsesClient& clie // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = sceneInfo->scene->createMeshNode("quad mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); meshNode->setParent(*sceneInfo->rotateNode); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered @@ -339,15 +326,11 @@ int main() triangleScene->createTextureProvider(*triangleInfo->textures[0], textureProviderId); quadScene2->createTextureConsumer(*quadInfo2->textureSampler, textureConsumerId); - triangleScene->flush(); - quadScene->flush(); - quadScene2->flush(); - /// [Data Linking Example Client] framework.connect(); - triangleScene->publish(); - quadScene->publish(); - quadScene2->publish(); + triangleScene->publish(ramses::EScenePublicationMode::LocalOnly); + quadScene->publish(ramses::EScenePublicationMode::LocalOnly); + quadScene2->publish(ramses::EScenePublicationMode::LocalOnly); const auto fbId = renderer.getDisplayFramebuffer(display); @@ -367,9 +350,9 @@ int main() sceneControlAPI.flush(); RendererAndSceneStateEventHandler eventHandler(sceneControlAPI, renderer); - eventHandler.waitForSceneReady(triangleSceneId); - eventHandler.waitForSceneReady(quadSceneId); - eventHandler.waitForSceneReady(quadSceneId2); + eventHandler.waitForSceneReady(*triangleScene); + eventHandler.waitForSceneReady(*quadScene); + eventHandler.waitForSceneReady(*quadScene2); eventHandler.waitForDataProviderOrConsumerCreated(triangleSceneId); eventHandler.waitForDataProviderOrConsumerCreated(quadSceneId); eventHandler.waitForDataProviderOrConsumerCreated(quadSceneId2); @@ -399,7 +382,7 @@ int main() triangleInfo->scene->updateTextureProvider(*triangleInfo->textures[textureId], textureProviderId); } - triangleInfo->translateNode->setTranslation({std::sin(timeStamp * 0.05f) * 0.2f, 0.0f, 0.0f}); + triangleInfo->translateNode->setTranslation({std::sin(static_cast(timeStamp) * 0.05f) * 0.2f, 0.0f, 0.0f}); triangleInfo->scene->flush(); rotationFactor += 1.f; quadInfo->rotateNode->setRotation({0.0f, 0.0f, rotationFactor}, ramses::ERotationType::Euler_XYZ); @@ -407,7 +390,7 @@ int main() quadInfo2->rotateNode->setRotation({0.0f, rotationFactor, 0.0f}, ramses::ERotationType::Euler_XYZ); quadInfo2->scene->flush(); - quadInfo->colorData->setValue(ramses::vec4f{ std::sin(timeStamp * 0.1f), 0.0f, 0.5f, 1.0f }); + quadInfo->colorData->setValue(ramses::vec4f{ std::sin(static_cast(timeStamp) * 0.1f), 0.0f, 0.5f, 1.0f }); renderer.doOneLoop(); timeStamp++; diff --git a/examples/ramses-example-local-displays/src/main.cpp b/examples/ramses-example-local-displays/src/main.cpp index b5bbb6b70..432c11f4a 100644 --- a/examples/ramses-example-local-displays/src/main.cpp +++ b/examples/ramses-example-local-displays/src/main.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" #include #include @@ -47,7 +47,7 @@ uint64_t nowMs() ramses::Scene* createScene1(ramses::RamsesClient& client, ramses::sceneId_t sceneId) { //client scene - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local displays example scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "local displays example scene"); //fill scene with content, i.e. use high level api // every scene needs a render pass with camera @@ -56,7 +56,7 @@ ramses::Scene* createScene1(ramses::RamsesClient& client, ramses::sceneId_t scen camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -73,26 +73,24 @@ ramses::Scene* createScene1(ramses::RamsesClient& client, ramses::sceneId_t scen effectDesc.setFragmentShaderFromFile("res/ramses-local-displays-test.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional colorInput = effect->findUniformInput("color"); + assert(positionsInput.has_value() && colorInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 1.0f, 0.3f, 1.0f }); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 1.0f, 0.3f, 1.0f }); return clientScene; } @@ -100,7 +98,7 @@ ramses::Scene* createScene1(ramses::RamsesClient& client, ramses::sceneId_t scen ramses::Scene* createScene2(ramses::RamsesClient& client, ramses::sceneId_t sceneId) { //client scene - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local displays example scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "local displays example scene"); //fill scene with content, i.e. use high level api // every scene needs a render pass with camera @@ -109,7 +107,7 @@ ramses::Scene* createScene2(ramses::RamsesClient& client, ramses::sceneId_t scen camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -126,26 +124,25 @@ ramses::Scene* createScene2(ramses::RamsesClient& client, ramses::sceneId_t scen effectDesc.setFragmentShaderFromFile("res/ramses-local-displays-test.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional colorInput = effect->findUniformInput("color"); + assert(positionsInput.has_value() && colorInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.5f, 1.0f }); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.5f, 1.0f }); return clientScene; } @@ -203,8 +200,8 @@ int main() renderer.startThread(); renderer.flush(); - ramses::MeshNode* meshScene1 = ramses::RamsesUtils::TryConvert(*scene1->findObjectByName("triangle mesh node")); - ramses::MeshNode* meshScene2 = ramses::RamsesUtils::TryConvert(*scene2->findObjectByName("triangle mesh node")); + auto* meshScene1 = scene1->findObject("triangle mesh node"); + auto* meshScene2 = scene2->findObject("triangle mesh node"); RendererEventHandler eventHandler; float rotationZ = 0.f; diff --git a/examples/ramses-example-local-dma-offscreenbuffer/CMakeLists.txt b/examples/ramses-example-local-dma-offscreenbuffer/CMakeLists.txt index d7bbd7fa9..b2dc9dd6e 100644 --- a/examples/ramses-example-local-dma-offscreenbuffer/CMakeLists.txt +++ b/examples/ramses-example-local-dma-offscreenbuffer/CMakeLists.txt @@ -6,13 +6,16 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -createModule( - NAME ramses-example-local-dma-offscreenbuffer - TYPE BINARY - ENABLE_INSTALL ON - SRC_FILES src/* - RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib - gbm - libdrm -) +importDependenciesAndCheckMissing(MISSING_DEPENDENCY gbm libdrm) +if(NOT MISSING_DEPENDENCY) + createModule( + NAME ramses-example-local-dma-offscreenbuffer + TYPE BINARY + ENABLE_INSTALL ON + SRC_FILES src/* + RESOURCE_FOLDERS res + DEPENDENCIES ramses-shared-lib + gbm + libdrm + ) +endif() diff --git a/examples/ramses-example-local-dma-offscreenbuffer/src/main.cpp b/examples/ramses-example-local-dma-offscreenbuffer/src/main.cpp index 14fa09016..6a11e0c0c 100644 --- a/examples/ramses-example-local-dma-offscreenbuffer/src/main.cpp +++ b/examples/ramses-example-local-dma-offscreenbuffer/src/main.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/client/ramses-utils.h" #include #include #include @@ -26,6 +26,7 @@ #include #include #include +#include /** * @example ramses-example-local-dma-offscreenbuffer/src/main.cpp @@ -100,19 +101,23 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public { } - void framebufferPixelsRead(const uint8_t* pixelData, const uint32_t pixelDataSize, ramses::displayId_t, ramses::displayBufferId_t, ramses::ERendererEventResult result) override + void framebufferPixelsRead(const uint8_t* pixelData, const uint32_t pixelDataSize, ramses::displayId_t /*displayId*/, ramses::displayBufferId_t /*displayBuffer*/, ramses::ERendererEventResult result) override { if(result == ramses::ERendererEventResult::Ok) { static uint32_t filePostifx = 0u; const std::string fileName = "./dmaBufExampleScreenshot_" + std::to_string(filePostifx++) + ".png"; - if(!ramses::RamsesUtils::SaveImageBufferToPng(fileName, {pixelData, pixelData + pixelDataSize}, DisplayWidth, DisplayHeight)) - printf("Error: Failed saving screenshot to %s !!\n", fileName.c_str()); + if (!ramses::RamsesUtils::SaveImageBufferToPng(fileName, {pixelData, pixelData + pixelDataSize}, DisplayWidth, DisplayHeight)) + { + std::cout << "Error: Failed saving screenshot to " << fileName << std::endl; + } else - printf("Saved screenshot to %s\n", fileName.c_str()); + { + std::cout << "Saved screenshot to " << fileName << std::endl; + } } else - printf("Error: Failed read pixels!!\n"); + std::cout << "Error: Failed read pixels!!" << std::endl; } void sceneStateChanged(ramses::sceneId_t sceneId, ramses::RendererSceneState state) override @@ -128,7 +133,7 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public } } - void offscreenBufferCreated(ramses::displayId_t, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override + void offscreenBufferCreated(ramses::displayId_t /*displayId*/, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override { if (ramses::ERendererEventResult::Failed != result) { @@ -136,7 +141,7 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public } } - void offscreenBufferLinked(ramses::displayBufferId_t, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t, bool success) override + void offscreenBufferLinked(ramses::displayBufferId_t /*offscreenBufferId*/, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t /*consumerId*/, bool success) override { if (success) { @@ -176,7 +181,7 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public const auto result = conditionFunction(); if(!result) - printf("Error: timed out waiting for state/condition!\n"); + std::cout << "Error: timed out waiting for state/condition!" << std::endl; return result; } @@ -222,7 +227,7 @@ ramses::Effect& createEffect(ramses::Scene& scene, const std::string& effectName effectDesc.setFragmentShader(fragmentShader); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - return *scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, effectName.c_str()); + return *scene.createEffect(effectDesc, effectName.c_str()); } ramses::MeshNode& createQuadWithTexture(ramses::Scene& scene, ramses::Effect& effect, ramses::TextureSampler& textureSampler) @@ -243,23 +248,22 @@ ramses::MeshNode& createQuadWithTexture(ramses::Scene& scene, ramses::Effect& ef const ramses::ArrayResource* textureCoords = scene.createArrayResource(4u, textureCoordsArray.data()); ramses::Appearance* appearance = scene.createAppearance(effect, "quad appearance"); - ramses::GeometryBinding* geometry = scene.createGeometryBinding(effect, "quad geometry"); + ramses::Geometry* geometry = scene.createGeometry(effect, "quad geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - ramses::AttributeInput texCoordsInput; - effect.findAttributeInput("a_texcoord", texCoordsInput); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + std::optional positionsInput = effect.findAttributeInput("a_position"); + std::optional texCoordsInput = effect.findAttributeInput("a_texcoord"); + assert(positionsInput.has_value() && texCoordsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*texCoordsInput, *textureCoords); ramses::MeshNode* meshNode = scene.createMeshNode(); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); - ramses::UniformInput textureInput; - effect.findUniformInput("textureSampler", textureInput); - appearance->setInputTexture(textureInput, textureSampler); + std::optional textureInput = effect.findUniformInput("textureSampler"); + assert(textureInput.has_value()); + appearance->setInputTexture(*textureInput, textureSampler); return *meshNode; } @@ -269,7 +273,7 @@ ramses::Scene& createMainScene(ramses::RamsesClient& client, ramses::sceneId_t s //scene consists of two quads with textures //the left quad is stream tex input rendered as is //the right quad is output result from processing input (from two frames ago) - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "main scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "main scene"); ramses::OrthographicCamera* camera = clientScene->createOrthographicCamera("main scene camera"); camera->setTranslation({0.0f, 0.0f, 5.0f}); @@ -315,7 +319,7 @@ ramses::Scene& createMainScene(ramses::RamsesClient& client, ramses::sceneId_t s ramses::Scene& createSourceScene(ramses::RamsesClient& client, ramses::sceneId_t sceneId) { //scene consists of one quad with a the stream texture that fills whole OB where scene is rendered - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "source scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "source scene"); ramses::OrthographicCamera* camera = clientScene->createOrthographicCamera("source scene camera"); camera->setTranslation({0.0f, 0.0f, 5.0f}); @@ -361,7 +365,7 @@ ramses::displayBufferId_t createDmaOffscreenBuffer(ramses::RamsesRenderer& rende const auto bufferId = renderer.createDmaOffscreenBuffer(display, OffscreenBufferWidth, OffscreenBufferHeight, DmaBufferFourccFormat, usageFlags, modifier); if(!bufferId.isValid()) - printf("Error: Failed creating offscreen buffer!\n"); + std::cout << "Error: Failed creating offscreen buffer!" << std::endl; return bufferId; } @@ -371,29 +375,29 @@ bool mapDmaOffscreenBuffer(ramses::RamsesRenderer& renderer, ramses::displayId_t errno = 0; if(bufferInfoMap.find(displayBuffer) != bufferInfoMap.cend()) { - printf("Error: Buffer already mapped!\n"); + std::cout << "Error: Buffer already mapped!" << std::endl; return false; } int bufferFD = -1; uint32_t bufferStride = 0; - if(renderer.getDmaOffscreenBufferFDAndStride(display, displayBuffer, bufferFD, bufferStride) != ramses::StatusOK) + if(!renderer.getDmaOffscreenBufferFDAndStride(display, displayBuffer, bufferFD, bufferStride)) { - printf("Error: Failed to get FD and stride!\n"); + std::cout << "Error: Failed to get FD and stride!" << std::endl; return false; } - const std::size_t fileSize = static_cast(lseek(bufferFD, 0u, SEEK_END)); + const auto fileSize = static_cast(lseek(bufferFD, 0u, SEEK_END)); const auto mappingPreviliges = (readyOnly? (PROT_READ) : (PROT_WRITE | PROT_READ)); void* mappedMemory = mmap(nullptr, fileSize, mappingPreviliges, MAP_SHARED, bufferFD, 0u); - if(mappedMemory == MAP_FAILED) + if (mappedMemory == MAP_FAILED) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) { const auto errorValue = errno; - printf("Error: Failed to map memory (errno=%i, errnoString=%s)!\n", errorValue, strerror(errorValue)); + std::cout << "Error: Failed to map memory (errno=" << errorValue << ", errnoString=" << strerror(errorValue) << ")!" << std::endl; return false; } - printf("Mapped OB %u with FD %i successfully to %p\n", displayBuffer.getValue(), bufferFD, mappedMemory); + std::cout << "Mapped OB " << displayBuffer.getValue() << " with FD " << bufferFD << " successfully to " << mappedMemory << std::endl; bufferInfoMap[displayBuffer] = {bufferFD, bufferStride, fileSize, mappedMemory}; return true; @@ -404,17 +408,18 @@ bool startBufferAccess(ramses::displayBufferId_t displayBuffer) const auto bufferInfoIt = bufferInfoMap.find(displayBuffer); if(bufferInfoIt == bufferInfoMap.cend()) { - printf("Error: Failed to find buffer for start sync!\n"); + std::cout << "Error: Failed to find buffer for start sync!" << std::endl; return false; } dma_buf_sync syncStartFlags = { 0 }; syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive const auto syncStartResult = ioctl(bufferInfoIt->second.fd, DMA_BUF_IOCTL_SYNC, &syncStartFlags); if(syncStartResult != 0) { - printf("Error: Failed to start sync!\n"); + std::cout << "Error: Failed to start sync!" << std::endl; return false; } @@ -426,16 +431,17 @@ bool endBufferAccess(ramses::displayBufferId_t displayBuffer) const auto bufferInfoIt = bufferInfoMap.find(displayBuffer); if(bufferInfoIt == bufferInfoMap.cend()) { - printf("Error: Failed to find buffer for end sync!\n"); + std::cout << "Error: Failed to find buffer for end sync!" << std::endl; return false; } dma_buf_sync syncEndFlags = { 0 }; syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive const auto syncEndResult = ioctl(bufferInfoIt->second.fd, DMA_BUF_IOCTL_SYNC, &syncEndFlags); if(syncEndResult != 0) { - printf("Failed to end sync!\n"); + std::cout << "Failed to end sync!" << std::endl; return false; } @@ -463,20 +469,20 @@ bool doProcessing(ramses::displayBufferId_t inputBuffer, ramses::displayBufferId uint8_t* outputPixelMem = reinterpret_cast(outputBufferInfo.mappedMem) + row * bufferStride + col * 4u; //Magic values for demonestrating noticeable change in several color channels - const float magicValue1 = 1.f * col / OffscreenBufferWidth; - const uint16_t magicValue2 = uint8_t(255.f * col / OffscreenBufferWidth); - const uint8_t magicValue3 = uint8_t(255.f * row / OffscreenBufferHeight); + const auto magicValue1 = 1.f * static_cast(col) / static_cast(OffscreenBufferWidth); + const auto magicValue2 = uint16_t(255.f * static_cast(col) / static_cast(OffscreenBufferWidth)); + const auto magicValue3 = uint8_t(255.f * static_cast(row) / static_cast(OffscreenBufferHeight)); - switch (DmaBufferFourccFormat) + if (DmaBufferFourccFormat == DRM_FORMAT_ARGB8888) //ARGB little endian { - case DRM_FORMAT_ARGB8888: //ARGB little endian outputPixelMem[0] = inputPixelMem[0]; //Blue channel - outputPixelMem[1] = std::min(uint8_t(magicValue1 * inputPixelMem[1]), 255u); //Green channel - outputPixelMem[2] = uint8_t(std::min(magicValue2 + inputPixelMem[2], 255u)); //Red channel + outputPixelMem[1] = static_cast(magicValue1 * static_cast(inputPixelMem[1])); //Green channel + outputPixelMem[2] = static_cast(std::min(magicValue2 + inputPixelMem[2], 255u)); //Red channel outputPixelMem[3] = magicValue3; //Alpha channel - break; - default: - printf("Error: Unsupported format!\n"); + } + else + { + std::cout << "Error: Unsupported format!" << std::endl; return false; } } @@ -554,15 +560,15 @@ int main() if(!mapDmaOffscreenBuffer(renderer, display, dmaOffscreenBufferWrite2, false)) return 1; - printf("\n************* All OBs mapped successfully !! **********\n"); + std::cout << std::endl <<"************* All OBs mapped successfully !! **********" << std::endl; //disable clearing for OBs in order to control when does content get overwritten //for example, the content of the write OBs (final result after processing) is written to memory //on CPU side, so GPU should not clear that content while rendering - renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferRead1, ramses::EClearFlags_None); - renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferRead2, ramses::EClearFlags_None); - renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferWrite1, ramses::EClearFlags_None); - renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferWrite2, ramses::EClearFlags_None); + renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferRead1, ramses::EClearFlag::None); + renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferRead2, ramses::EClearFlag::None); + renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferWrite1, ramses::EClearFlag::None); + renderer.setDisplayBufferClearFlags(display, dmaOffscreenBufferWrite2, ramses::EClearFlag::None); renderer.flush(); const ramses::dataConsumerId_t samplerConsumerId(457u); @@ -570,7 +576,7 @@ int main() const ramses::sceneId_t mainSceneId{1u}; const std::string& processingOutputSamplerName = "processingOutputTexSampler"; ramses::Scene& mainScene = createMainScene(client, mainSceneId, processingOutputSamplerName); - const ramses::TextureSampler& processingOutputSampler = *ramses::RamsesUtils::TryConvert(*mainScene.findObjectByName(processingOutputSamplerName.c_str())); + const auto& processingOutputSampler = *mainScene.findObject(processingOutputSamplerName); mainScene.createTextureConsumer(processingOutputSampler, samplerConsumerId); mainScene.flush(); @@ -621,14 +627,14 @@ int main() if(result == -1) { const auto errorValue = errno; - printf("Error: Failed to unmap memory for OB %u (errno=%i, errnoString=%s)!\n", bufferInfo.first.getValue(), errorValue, strerror(errorValue)); + std::cout << "Error: Failed to unmap memory for OB " << bufferInfo.first.getValue() << " (errno=" << errorValue << ", errnoString=" << strerror(errorValue) << ")!" << std::endl; return 1; } - printf("Unmapped OB %u with FD %i successfully!\n", bufferInfo.first.getValue(), bufferInfo.second.fd); + std::cout << "Unmapped OB " << bufferInfo.first.getValue() << " with FD " << bufferInfo.second.fd << " successfully!" << std::endl; } - printf("\n************* All OBs unmapped successfully !! **********\n"); + std::cout << std::endl << "************* All OBs unmapped successfully !! **********" << std::endl; return 0; } diff --git a/examples/ramses-example-local-geometry-shaders/src/main.cpp b/examples/ramses-example-local-geometry-shaders/src/main.cpp index 772e13648..e9047db5f 100644 --- a/examples/ramses-example-local-geometry-shaders/src/main.cpp +++ b/examples/ramses-example-local-geometry-shaders/src/main.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" #include #include @@ -58,14 +58,14 @@ int main() //client scene const ramses::sceneId_t sceneId(1u); - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local client example scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "local client example scene"); // every scene needs a render pass with camera auto* camera = clientScene->createPerspectiveCamera("my camera"); camera->setViewport(0, 0, 1280u, 480u); camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -84,20 +84,16 @@ int main() effectDesc.setGeometryShaderFromFile("res/ramses-example-local-geometry-shaders.geom"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); appearance->setDrawMode(ramses::EDrawMode::Points); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - - ramses::UniformInput xMultiplierInput; - effect->findUniformInput("x_multiplier", xMultiplierInput); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional colorInput = effect->findUniformInput("color"); + std::optional xMultiplierInput = effect->findUniformInput("x_multiplier"); + assert(positionsInput.has_value() && colorInput.has_value() && xMultiplierInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); /// [Geometry Shaders Example] @@ -105,11 +101,11 @@ int main() ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); meshNode->setTranslation({0.0f, 0.0f, -5.0f}); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroup->addMeshNode(*meshNode); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); clientScene->publish(); clientScene->flush(); @@ -124,7 +120,7 @@ int main() while (!eventHandler.isWindowClosed()) { renderer.dispatchEvents(eventHandler); - appearance->setInputValue(xMultiplierInput, xMultiplierValue); + appearance->setInputValue(*xMultiplierInput, xMultiplierValue); clientScene->flush(); xMultiplierValue += 0.1f; diff --git a/examples/ramses-example-local-offscreenbuffer/src/main.cpp b/examples/ramses-example-local-offscreenbuffer/src/main.cpp index 9cbf6153c..0d622cef3 100644 --- a/examples/ramses-example-local-offscreenbuffer/src/main.cpp +++ b/examples/ramses-example-local-offscreenbuffer/src/main.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/client/ramses-utils.h" #include #include #include @@ -43,7 +43,7 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public m_scenes[sceneId].version = sceneVersion; } - void offscreenBufferCreated(ramses::displayId_t, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override + void offscreenBufferCreated(ramses::displayId_t /*displayId*/, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override { if (ramses::ERendererEventResult::Failed != result) { @@ -51,7 +51,7 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public } } - void offscreenBufferLinked(ramses::displayBufferId_t, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t, bool success) override + void offscreenBufferLinked(ramses::displayBufferId_t /*offscreenBufferId*/, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t /*consumerId*/, bool success) override { if (success) { @@ -69,9 +69,9 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public waitUntilOrTimeout([&] {return m_scenesConsumingOffscreenBuffer.count(sceneId) > 0; }); } - void waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + void waitForSceneState(ramses::Scene& scene, ramses::RendererSceneState state) { - waitUntilOrTimeout([&] { return m_scenes[sceneId].state == state; }); + waitUntilOrTimeout([&] { return m_scenes[scene.getSceneId()].state == state; }, &scene); } bool waitForFlush(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t sceneVersion) @@ -79,15 +79,18 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public return waitUntilOrTimeout([&] { return m_scenes[sceneId].version == sceneVersion; }); } - bool waitUntilOrTimeout(const std::function& conditionFunction) + bool waitUntilOrTimeout(const std::function& conditionFunction, ramses::Scene* scene = nullptr) { const std::chrono::steady_clock::time_point timeoutTS = std::chrono::steady_clock::now() + std::chrono::seconds{ 5 }; while (!conditionFunction() && std::chrono::steady_clock::now() < timeoutTS) { - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); // will give the renderer time to process changes + if (scene) + scene->flush(); // local only scenes have to be flushed periodically when getting scene to READY state m_renderer.dispatchEvents(*this); m_renderer.getSceneControlAPI()->dispatchEvents(*this); + + std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); // will give the renderer time to process changes } return conditionFunction(); @@ -150,27 +153,29 @@ static constexpr uint32_t DisplayHeight = 800u; ramses::MeshNode* createTexturedQuad(ramses::Scene* clientScene, const ramses::Effect* effect, const ramses::ArrayResource* indices, const ramses::ArrayResource* vertexPositions, const ramses::ArrayResource* textureCoords) { ramses::Appearance* appearance = clientScene->createAppearance(*effect, "quad appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "quad geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "quad geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_texcoord", texCoordsInput); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + std::optional positionsInput = effect->findAttributeInput("a_position"); + std::optional texCoordsInput = effect->findAttributeInput("a_texcoord"); + assert(positionsInput.has_value() && texCoordsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); + geometry->setInputBuffer(*texCoordsInput, *textureCoords); ramses::MeshNode* meshNode = clientScene->createMeshNode(); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); - ramses::UniformInput sampleCountUnif; + std::optional sampleCountUnif; for (uint32_t i = 0; i < effect->getUniformInputCount(); ++i) - if (ramses::StatusOK == effect->getUniformInput(i, sampleCountUnif) && strcmp(sampleCountUnif.getName(), "sampleCount") == 0) + { + sampleCountUnif = effect->getUniformInput(i); + if (sampleCountUnif.has_value() && strcmp(sampleCountUnif->getName(), "sampleCount") == 0) { - appearance->setInputValue(sampleCountUnif, static_cast(SampleCount)); + appearance->setInputValue(*sampleCountUnif, static_cast(SampleCount)); break; } + } return meshNode; } @@ -192,7 +197,7 @@ void createClearPass(ramses::Scene* scene, ramses::RenderBuffer* renderBuffer) //Clear RenderBuffer to visible color clearPass->setRenderTarget(&renderTarget); clearPass->setClearColor({1.f, 0.f, 0.f, 1.f}); - clearPass->setClearFlags(ramses::EClearFlags_All); + clearPass->setClearFlags(ramses::EClearFlag::All); } ramses::Effect* createEffect(ramses::Scene* scene, const std::string& shaderName, const std::string& effectName) @@ -202,19 +207,19 @@ ramses::Effect* createEffect(ramses::Scene* scene, const std::string& shaderName effectDesc.setFragmentShaderFromFile(("res/" + shaderName + ".frag").c_str()); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - return scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, effectName.c_str()); + return scene->createEffect(effectDesc, effectName.c_str()); } ramses::Scene* createConsumerScene(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const char* samplerConsumerName, const char* samplerConsumerNameMS) { - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local displays example"); + ramses::Scene* clientScene = client.createScene(sceneId, "local displays example"); ramses::OrthographicCamera* camera = clientScene->createOrthographicCamera("MyCamera"); camera->setTranslation({0.0f, 0.0f, 5.0f}); camera->setFrustum(-2.f, 2.f, -2.f, 2.f, 0.1f, 100.f); camera->setViewport(0, 0u, DisplayWidth, DisplayHeight); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -241,9 +246,9 @@ ramses::Scene* createConsumerScene(ramses::RamsesClient& client, ramses::sceneId // Create fallback texture to show when sampler not linked to an offscreen buffer ramses::Texture2D* fallbackTexture = ramses::RamsesUtils::CreateTextureResourceFromPng("res/ramses-example-offscreenbuffer-fallback.png", *clientScene); ramses::TextureSampler* textureSampler = clientScene->createTextureSampler(ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, ramses::ETextureSamplingMethod::Linear, ramses::ETextureSamplingMethod::Linear, *fallbackTexture, 1u, samplerConsumerName); - ramses::UniformInput textureUnif; - effect->findUniformInput("textureSampler", textureUnif); - meshNode->getAppearance()->setInputTexture(textureUnif, *textureSampler); + std::optional textureUnif = effect->findUniformInput("textureSampler"); + assert(textureUnif.has_value()); + meshNode->getAppearance()->setInputTexture(*textureUnif, *textureSampler); // create an appearance for grey quad multi sampled ramses::Effect* effectMS = createEffect(clientScene, "ramses-example-offscreenbuffer_textureMS","quad effect MS"); @@ -256,20 +261,20 @@ ramses::Scene* createConsumerScene(ramses::RamsesClient& client, ramses::sceneId renderGroup->addMeshNode(*meshNodeMS); // Multisampled texture cannot use a texture as fallback like the quad above but it has to use a multisampled render buffer as fallback instead in case the offscreen buffer is not linked. - ramses::RenderBuffer* fallbackBuffer = clientScene->createRenderBuffer(ObWidth, ObHeight, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite, SampleCount); + ramses::RenderBuffer* fallbackBuffer = clientScene->createRenderBuffer(ObWidth, ObHeight, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite, SampleCount); // Buffer gets cleared to solid color so it is visible when sampler is not linked to the offscreen buffer createClearPass(clientScene, fallbackBuffer); ramses::TextureSamplerMS* textureSamplerMS = clientScene->createTextureSamplerMS(*fallbackBuffer, samplerConsumerNameMS); - ramses::UniformInput textureUnifMS; - effectMS->findUniformInput("textureSampler", textureUnifMS); - meshNodeMS->getAppearance()->setInputTexture(textureUnifMS, *textureSamplerMS); + std::optional textureUnifMS = effectMS->findUniformInput("textureSampler"); + assert(textureUnifMS.has_value()); + meshNodeMS->getAppearance()->setInputTexture(*textureUnifMS, *textureSamplerMS); return clientScene; } ramses::Scene* createProviderScene(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const char* rotationNodeName) { - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local displays example scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "local displays example scene"); ramses::OrthographicCamera* camera = clientScene->createOrthographicCamera (); camera->setTranslation({0.0f, 0.0f, 5.0f}); @@ -277,7 +282,7 @@ ramses::Scene* createProviderScene(ramses::RamsesClient& client, ramses::sceneId camera->setViewport ( 0, 0u, ObWidth, ObHeight ); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -291,22 +296,22 @@ ramses::Scene* createProviderScene(ramses::RamsesClient& client, ramses::sceneId ramses::ArrayResource* vertexPositions = clientScene->createArrayResource(4u, vertexPositionsQuadArray.data()); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "Quad appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "Quad geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "Quad geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.3f, 0.5f, 1.0f }); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); + appearance->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.3f, 0.5f, 1.0f }); ramses::MeshNode* meshNode = clientScene->createMeshNode("quad mesh node"); ramses::Node* rotationNode = clientScene->createNode(rotationNodeName); meshNode->setParent(*rotationNode); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); return clientScene; @@ -364,12 +369,12 @@ int main() sceneControlAPI.setSceneState(providerSceneId2, ramses::RendererSceneState::Ready); sceneControlAPI.flush(); - eventHandler.waitForSceneState(consumerSceneId, ramses::RendererSceneState::Ready); - eventHandler.waitForSceneState(providerSceneId, ramses::RendererSceneState::Ready); - eventHandler.waitForSceneState(providerSceneId2, ramses::RendererSceneState::Ready); + eventHandler.waitForSceneState(*consumerScene, ramses::RendererSceneState::Ready); + eventHandler.waitForSceneState(*providerScene, ramses::RendererSceneState::Ready); + eventHandler.waitForSceneState(*providerScene2, ramses::RendererSceneState::Ready); - const ramses::TextureSamplerMS* textureSamplerConsumerMS = ramses::RamsesUtils::TryConvert(*consumerScene->findObjectByName("textureSamplerConsumerMS")); - const ramses::TextureSampler* textureSamplerConsumer = ramses::RamsesUtils::TryConvert(*consumerScene->findObjectByName("textureSamplerConsumer")); + const auto* textureSamplerConsumerMS = consumerScene->findObject("textureSamplerConsumerMS"); + const auto* textureSamplerConsumer = consumerScene->findObject("textureSamplerConsumer"); /// [Offscreen Buffer Example] // IMPORTANT NOTE: For simplicity and readability the example code does not check return values from API calls. @@ -422,8 +427,8 @@ int main() uint64_t timeStamp = 1u; float rotationZ = 0.f; - ramses::Node* rotationNode = ramses::RamsesUtils::TryConvert(*providerScene->findObjectByName("rotationNode")); - ramses::Node* rotationNodeMS = ramses::RamsesUtils::TryConvert(*providerScene2->findObjectByName("rotationNodeMS")); + auto* rotationNode = providerScene->findObject("rotationNode"); + auto* rotationNodeMS = providerScene2->findObject("rotationNodeMS"); while (!eventHandler.isWindowClosed()) { diff --git a/examples/ramses-example-local-pick-handling/src/main.cpp b/examples/ramses-example-local-pick-handling/src/main.cpp index ef004f63a..6bcb1a50b 100644 --- a/examples/ramses-example-local-pick-handling/src/main.cpp +++ b/examples/ramses-example-local-pick-handling/src/main.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/client/ramses-client.h" + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" #include #include #include @@ -46,12 +46,12 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public { } - void mouseEvent(ramses::displayId_t, ramses::EMouseEvent eventType, int32_t mousePosX, int32_t mousePosY) override + void mouseEvent(ramses::displayId_t /*displayId*/, ramses::EMouseEvent eventType, int32_t mousePosX, int32_t mousePosY) override { if (eventType == ramses::EMouseEvent::LeftButtonDown) { //Flip PosY - mousePosY = DisplayHeight - mousePosY; + mousePosY = static_cast(DisplayHeight) - mousePosY; //normalized coordinate calculation const float mouseXNormalized = ((2.0f * static_cast(mousePosX) / DisplayWidth)) - 1.f; @@ -76,12 +76,16 @@ class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty, public // change color of clicked triangles to random value if (pickedObjects[po].getValue() == 1) { - m_appearanceA.getEffect().findUniformInput("color", m_colorInput); + std::optional colorInput = m_appearanceA.getEffect().findUniformInput("color"); + assert(colorInput.has_value()); + m_colorInput = std::move(*colorInput); m_appearanceA.setInputValue(m_colorInput, ramses::vec4f{ r, g, b, 1.0f }); } else if (pickedObjects[po].getValue() == 2) { - m_appearanceB.getEffect().findUniformInput("color", m_colorInput); + std::optional colorInput = m_appearanceB.getEffect().findUniformInput("color"); + assert(colorInput.has_value()); + m_colorInput = std::move(*colorInput); m_appearanceB.setInputValue(m_colorInput, ramses::vec4f{ r, g, b, 1.0f }); } } @@ -129,7 +133,7 @@ int main() //client scene const ramses::sceneId_t sceneId(1u); - ramses::Scene* clientScene = client.createScene(sceneId, ramses::SceneConfig(), "local client example scene"); + ramses::Scene* clientScene = client.createScene(sceneId, "local client example scene"); // every scene needs a render pass with camera ramses::OrthographicCamera* orthographicCamera = clientScene->createOrthographicCamera("my orthographicCamera"); @@ -147,8 +151,8 @@ int main() perspectiveCamera->scale({1.f, 2.f, 1.f}); ramses::RenderPass* renderPassA = clientScene->createRenderPass("my render pass A"); ramses::RenderPass* renderPassB = clientScene->createRenderPass("my render pass B"); - renderPassA->setClearFlags(ramses::EClearFlags_None); - renderPassB->setClearFlags(ramses::EClearFlags_None); + renderPassA->setClearFlags(ramses::EClearFlag::None); + renderPassB->setClearFlags(ramses::EClearFlag::None); renderPassA->setCamera(*perspectiveCamera); renderPassB->setCamera(*orthographicCamera); ramses::RenderGroup* renderGroupA = clientScene->createRenderGroup(); @@ -172,18 +176,18 @@ int main() effectDesc.setFragmentShaderFromFile("res/ramses-example-local-pick-handling.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearanceA = clientScene->createAppearance(*effect, "triangle appearance A"); ramses::Appearance* appearanceB = clientScene->createAppearance(*effect, "triangle appearance B"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); // create two mesh nodes to define the triangles with chosen appearance ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); @@ -193,7 +197,7 @@ int main() meshNode->setTranslation({1.5f, -0.3f, 0.4f}); meshNode->setRotation({-12.f, -30.f, -10.f}, ramses::ERotationType::Euler_XYZ); meshNode->setScaling({1.2f, 1.5f, 0.7f}); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered renderGroupA->addMeshNode(*meshNode); @@ -201,11 +205,11 @@ int main() meshNode2->setRotation({-10.f, -34.f, -19.f}, ramses::ERotationType::Euler_XYZ); meshNode2->setScaling({0.5f, 0.5f, 0.5f}); meshNode2->setAppearance(*appearanceB); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); renderGroupB->addMeshNode(*meshNode2); - appearanceA->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); - appearanceB->setInputValue(colorInput, ramses::vec4f{ 0.0f, 1.0f, 0.3f, 1.0f }); + appearanceA->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.3f, 1.0f }); + appearanceB->setInputValue(*colorInput, ramses::vec4f{ 0.0f, 1.0f, 0.3f, 1.0f }); /// [Pick Handling Example] // use triangle's vertex position array as PickableObject geometry @@ -225,7 +229,7 @@ int main() clientScene->publish(); clientScene->flush(); - SceneStateEventHandler eventHandler(sceneControlAPI, *appearanceA, *appearanceB, colorInput); + SceneStateEventHandler eventHandler(sceneControlAPI, *appearanceA, *appearanceB, *colorInput); // show the scene on the renderer sceneControlAPI.setSceneMapping(sceneId, display); diff --git a/examples/ramses-example-local-scene-referencing/src/main.cpp b/examples/ramses-example-local-scene-referencing/src/main.cpp index cd529ed83..48f976574 100644 --- a/examples/ramses-example-local-scene-referencing/src/main.cpp +++ b/examples/ramses-example-local-scene-referencing/src/main.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-client-api/SceneReference.h" +#include "ramses/client/SceneReference.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" #include #include @@ -47,11 +47,11 @@ class SceneReferenceEventHandler final : public ramses::IClientEventHandler : m_client(client) {} - void sceneFileLoadFailed(std::string_view) override {} - void sceneFileLoadSucceeded(std::string_view, ramses::Scene*) override {} - void sceneReferenceFlushed(ramses::SceneReference&, ramses::sceneVersionTag_t) override {} - void dataLinked(ramses::sceneId_t, ramses::dataProviderId_t, ramses::sceneId_t, ramses::dataConsumerId_t, bool) override {} - void dataUnlinked(ramses::sceneId_t, ramses::dataConsumerId_t, bool) override {} + void sceneFileLoadFailed(std::string_view /*filename*/) override {} + void sceneFileLoadSucceeded(std::string_view /*filename*/, ramses::Scene* /*loadedScene*/) override {} + void sceneReferenceFlushed(ramses::SceneReference& /*sceneRef*/, ramses::sceneVersionTag_t /*versionTag*/) override {} + void dataLinked(ramses::sceneId_t /*providerScene*/, ramses::dataProviderId_t /*providerId*/, ramses::sceneId_t /*consumerScene*/, ramses::dataConsumerId_t /*consumerId*/, bool /*success*/) override {} + void dataUnlinked(ramses::sceneId_t /*consumerScene*/, ramses::dataConsumerId_t /*consumerId*/, bool /*success*/) override {} void sceneReferenceStateChanged(ramses::SceneReference& sceneRef, ramses::RendererSceneState state) override { @@ -62,6 +62,10 @@ class SceneReferenceEventHandler final : public ramses::IClientEventHandler { while (m_sceneRefState.count(sceneId) == 0 || m_sceneRefState.find(sceneId)->second != state) { + ramses::SceneIterator sceneIter{ m_client }; + while (auto scene = sceneIter.getNext()) + scene->flush(); // local only scenes have to be flushed periodically when getting scene to READY state + std::this_thread::sleep_for(std::chrono::milliseconds(5)); m_client.dispatchEvents(*this); } @@ -113,7 +117,7 @@ void createContentProviderScene(ramses::RamsesClient& client, ramses::sceneId_t clientScene->createDataConsumer(*vpSizeData, VPSizeConsumerId); ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -137,38 +141,38 @@ void main(void) { })glsl"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); ramses::MeshNode* meshNode2 = clientScene->createMeshNode("triangle mesh node 2"); ramses::Appearance* appearance2 = clientScene->createAppearance(*effect, "triangle appearance"); meshNode2->setAppearance(*appearance2); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode2); meshNode2->setTranslation({0, -10, -5}); meshNode2->setScaling({100, 100, 1}); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); // Create data objects to hold color and bind them to appearance inputs auto color1 = clientScene->createDataObject(ramses::EDataType::Vector4F); auto color2 = clientScene->createDataObject(ramses::EDataType::Vector4F); color1->setValue(ramses::vec4f{ 1.f, 1.f, 1.f, 1.f }); color1->setValue(ramses::vec4f{ 0.5f, 0.5f, 0.5f, 1.f }); - appearance->bindInput(colorInput, *color1); - appearance2->bindInput(colorInput, *color2); + appearance->bindInput(*colorInput, *color1); + appearance2->bindInput(*colorInput, *color2); clientScene->createDataConsumer(*color1, Color1ConsumerId); clientScene->createDataConsumer(*color2, Color2ConsumerId); @@ -296,10 +300,10 @@ int main() sceneControlAPI.flush(); // These are master scene's data objects linked to scene1 and scene2 cameras' viewports - auto scene1vpOffset = ramses::RamsesUtils::TryConvert(*masterScene->findObjectByName("vp1offset")); - auto scene1vpSize = ramses::RamsesUtils::TryConvert(*masterScene->findObjectByName("vp1size")); - auto scene2vpOffset = ramses::RamsesUtils::TryConvert(*masterScene->findObjectByName("vp2offset")); - auto scene2vpSize = ramses::RamsesUtils::TryConvert(*masterScene->findObjectByName("vp2size")); + auto scene1vpOffset = masterScene->findObject("vp1offset"); + auto scene1vpSize = masterScene->findObject("vp1size"); + auto scene2vpOffset = masterScene->findObject("vp2offset"); + auto scene2vpSize = masterScene->findObject("vp2size"); // start animating data provider values after scenes are being rendered eventHandler.waitForSceneRefState(refSceneId1, ramses::RendererSceneState::Rendered); diff --git a/examples/ramses-example-local-viewport-link/src/main.cpp b/examples/ramses-example-local-viewport-link/src/main.cpp index 571d4b294..f16ee6aac 100644 --- a/examples/ramses-example-local-viewport-link/src/main.cpp +++ b/examples/ramses-example-local-viewport-link/src/main.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-client-api/PerspectiveCamera.h" +#include "ramses/client/ramses-client.h" + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/client/PerspectiveCamera.h" #include #include #include -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" /** * @example ramses-example-local-viewport-link/src/main.cpp @@ -61,10 +61,11 @@ class SceneStateEventHandler : public ramses::RendererSceneControlEventHandlerEm m_scenes[sceneId] = state; } - void waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + void waitForSceneState(ramses::Scene& scene, ramses::RendererSceneState state) { - while (m_scenes.count(sceneId) == 0 || m_scenes.find(sceneId)->second != state) + while (m_scenes.count(scene.getSceneId()) == 0 || m_scenes.find(scene.getSceneId())->second != state) { + scene.flush(); // local only scenes have to be flushed periodically when getting scene to READY state m_renderer.doOneLoop(); m_renderer.getSceneControlAPI()->dispatchEvents(*this); } @@ -121,7 +122,7 @@ ramses::Scene* createContentProviderScene(ramses::RamsesClient& client, ramses:: /// [Viewport Link Example Content] ramses::RenderPass* renderPass = clientScene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = clientScene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -136,38 +137,38 @@ ramses::Scene* createContentProviderScene(ramses::RamsesClient& client, ramses:: effectDesc.setFragmentShaderFromFile("res/ramses-example-local-viewport-link.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = clientScene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effect = clientScene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = clientScene->createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = clientScene->createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = clientScene->createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + std::optional positionsInput = effect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometry->setInputBuffer(*positionsInput, *vertexPositions); ramses::MeshNode* meshNode = clientScene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); ramses::MeshNode* meshNode2 = clientScene->createMeshNode("triangle mesh node 2"); ramses::Appearance* appearance2 = clientScene->createAppearance(*effect, "triangle appearance"); meshNode2->setAppearance(*appearance2); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode2); meshNode2->setTranslation({0, -10, -5}); meshNode2->setScaling({100, 100, 1}); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); + std::optional colorInput = effect->findUniformInput("color"); + assert(colorInput.has_value()); // Create data objects to hold color and bind them to appearance inputs auto color1 = clientScene->createDataObject(ramses::EDataType::Vector4F); auto color2 = clientScene->createDataObject(ramses::EDataType::Vector4F); color1->setValue(ramses::vec4f{ 1.f, 1.f, 1.f, 1.f }); color1->setValue(ramses::vec4f{ 0.5f, 0.5f, 0.5f, 1.f }); - appearance->bindInput(colorInput, *color1); - appearance2->bindInput(colorInput, *color2); + appearance->bindInput(*colorInput, *color1); + appearance2->bindInput(*colorInput, *color2); clientScene->createDataConsumer(*color1, Color1ConsumerId); clientScene->createDataConsumer(*color2, Color2ConsumerId); @@ -262,9 +263,9 @@ int main() sceneControlAPI.setSceneState(sceneIdMaster, ramses::RendererSceneState::Ready); sceneControlAPI.flush(); - eventHandler.waitForSceneState(sceneId1, ramses::RendererSceneState::Rendered); - eventHandler.waitForSceneState(sceneId2, ramses::RendererSceneState::Rendered); - eventHandler.waitForSceneState(sceneIdMaster, ramses::RendererSceneState::Ready); + eventHandler.waitForSceneState(*scene1, ramses::RendererSceneState::Rendered); + eventHandler.waitForSceneState(*scene2, ramses::RendererSceneState::Rendered); + eventHandler.waitForSceneState(*sceneMaster, ramses::RendererSceneState::Ready); #if 1 /// [Viewport Link Example] @@ -285,16 +286,16 @@ int main() #endif // These are master scene's data objects linked to scene1 and scene2 cameras' viewports - auto scene1vpOffset = ramses::RamsesUtils::TryConvert(*sceneMaster->findObjectByName("vp1offset")); - auto scene1vpSize = ramses::RamsesUtils::TryConvert(*sceneMaster->findObjectByName("vp1size")); - auto scene2vpOffset = ramses::RamsesUtils::TryConvert(*sceneMaster->findObjectByName("vp2offset")); - auto scene2vpSize = ramses::RamsesUtils::TryConvert(*sceneMaster->findObjectByName("vp2size")); + auto* scene1vpOffset = sceneMaster->findObject("vp1offset"); + auto* scene1vpSize = sceneMaster->findObject("vp1size"); + auto* scene2vpOffset = sceneMaster->findObject("vp2offset"); + auto* scene2vpSize = sceneMaster->findObject("vp2size"); int animParam = 0; bool animInc = true; - ramses::MeshNode* meshScene1 = ramses::RamsesUtils::TryConvert(*scene1->findObjectByName("triangle mesh node")); - ramses::MeshNode* meshScene2 = ramses::RamsesUtils::TryConvert(*scene2->findObjectByName("triangle mesh node")); + auto* meshScene1 = scene1->findObject("triangle mesh node"); + auto* meshScene2 = scene2->findObject("triangle mesh node"); RendererEventHandler rendererEventHandler; float rotationFactor = 0.f; diff --git a/examples/ramses-example-minimal/CMakeLists.txt b/examples/ramses-example-minimal/CMakeLists.txt index fca174c2d..f78f35656 100644 --- a/examples/ramses-example-minimal/CMakeLists.txt +++ b/examples/ramses-example-minimal/CMakeLists.txt @@ -12,5 +12,5 @@ createModule( ENABLE_INSTALL ON SRC_FILES include/*.h src/*.cpp - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-minimal/src/main.cpp b/examples/ramses-example-minimal/src/main.cpp index f50c2698f..375c431e6 100644 --- a/examples/ramses-example-minimal/src/main.cpp +++ b/examples/ramses-example-minimal/src/main.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" #include @@ -25,18 +25,19 @@ int main() framework.connect(); // create a scene and register it at RAMSES daemon - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u)); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig); // signal the scene it is in a state that can be rendered scene->flush(); // distribute the scene to RAMSES - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic uint32_t loops = 100; - while (--loops) + while (--loops != 0u) { std::this_thread::sleep_for(std::chrono::seconds(1)); } diff --git a/examples/ramses-example-renderonce/CMakeLists.txt b/examples/ramses-example-renderonce/CMakeLists.txt index d6d898139..11921706f 100644 --- a/examples/ramses-example-renderonce/CMakeLists.txt +++ b/examples/ramses-example-renderonce/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES include/*.h src/*.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-renderonce/src/main.cpp b/examples/ramses-example-renderonce/src/main.cpp index 7077793b4..b0faee671 100644 --- a/examples/ramses-example-renderonce/src/main.cpp +++ b/examples/ramses-example-renderonce/src/main.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-client-api/RenderTargetDescription.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/RenderTargetDescription.h" #include @@ -26,7 +26,8 @@ int main() ramses::RamsesClient& ramses(*framework.createClient("ramses-example-renderonce")); framework.connect(); - ramses::Scene* scene = ramses.createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), "basic rendertarget scene"); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = ramses.createScene(sceneConfig, "basic rendertarget scene"); // prepare triangle geometry: vertex position array and index array const std::array vertexPositionsQuadArray{ ramses::vec3f{-1.5f, -0.75f, -1.f}, ramses::vec3f{1.5f, -0.75f, -1.f}, ramses::vec3f{-1.5f, 0.75f, -1.f}, ramses::vec3f{1.5f, 0.75f, -1.f} }; @@ -45,13 +46,13 @@ int main() triangleEffectDesc.setVertexShaderFromFile("res/ramses-example-renderonce-simple-color.vert"); triangleEffectDesc.setFragmentShaderFromFile("res/ramses-example-renderonce-simple-color.frag"); triangleEffectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* triangleEffect = scene->createEffect(triangleEffectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* triangleEffect = scene->createEffect(triangleEffectDesc, "glsl shader"); ramses::EffectDescription quadEffectDesc; quadEffectDesc.setVertexShaderFromFile("res/ramses-example-renderonce-texturing.vert"); quadEffectDesc.setFragmentShaderFromFile("res/ramses-example-renderonce-texturing.frag"); quadEffectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* quadEffect = scene->createEffect(quadEffectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* quadEffect = scene->createEffect(quadEffectDesc, "glsl shader"); // every render pass needs a camera to define rendering parameters ramses::Node* cameraTranslate = scene->createNode(); @@ -74,7 +75,7 @@ int main() // create the render once renderpass ramses::RenderPass* renderPassRT = scene->createRenderPass("renderpass to render target"); - renderPassRT->setClearFlags(ramses::EClearFlags_All); + renderPassRT->setClearFlags(ramses::EClearFlag::All); renderPassRT->setClearColor({1.f, 1.f, 1.f, 1.f}); renderPassRT->setCamera(*cameraA); renderPassRT->setRenderOnce(true); @@ -82,7 +83,7 @@ int main() /// [Render Once Example Setup] // create a render target and assign it to renderpass A - ramses::RenderBuffer* renderBuffer = scene->createRenderBuffer(RTSize, RTSize, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); + ramses::RenderBuffer* renderBuffer = scene->createRenderBuffer(RTSize, RTSize, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); ramses::RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(*renderBuffer); ramses::RenderTarget* renderTarget = scene->createRenderTarget(rtDesc); @@ -90,7 +91,7 @@ int main() // create final render pass ramses::RenderPass* renderPassFinal = scene->createRenderPass("renderpass to screen"); - renderPassFinal->setClearFlags(ramses::EClearFlags_None); + renderPassFinal->setClearFlags(ramses::EClearFlag::None); renderPassFinal->setCamera(*cameraB); // create render group for each renderpass @@ -100,19 +101,20 @@ int main() renderPassFinal->addRenderGroup(*renderGroupB); // create triangle appearance, get input data and bind required data - ramses::UniformInput color; + std::optional color; ramses::Appearance* appearanceA = scene->createAppearance(*triangleEffect, "triangle appearance"); { - triangleEffect->findUniformInput("color", color); - appearanceA->setInputValue(color, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); + color = triangleEffect->findUniformInput("color"); + assert(color.has_value()); + appearanceA->setInputValue(*color, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); } - ramses::GeometryBinding* geometryA = scene->createGeometryBinding(*triangleEffect, "triangle geometry"); + ramses::Geometry* geometryA = scene->createGeometry(*triangleEffect, "triangle geometry"); { geometryA->setIndices(*indices); - ramses::AttributeInput positionsInput; - triangleEffect->findAttributeInput("a_position", positionsInput); - geometryA->setInputBuffer(positionsInput, *vertexPositionsTriangle); + std::optional positionsInput = triangleEffect->findAttributeInput("a_position"); + assert(positionsInput.has_value()); + geometryA->setInputBuffer(*positionsInput, *vertexPositionsTriangle); } // create quad appearance, get input data and bind required data @@ -127,31 +129,29 @@ int main() *renderBuffer); // set render target sampler as input - ramses::UniformInput textureInput; - quadEffect->findUniformInput("textureSampler", textureInput); - appearanceB->setInputTexture(textureInput, *sampler); + std::optional textureInput = quadEffect->findUniformInput("textureSampler"); + appearanceB->setInputTexture(*textureInput, *sampler); } - ramses::GeometryBinding* geometryB = scene->createGeometryBinding(*quadEffect, "quad geometry"); + ramses::Geometry* geometryB = scene->createGeometry(*quadEffect, "quad geometry"); { geometryB->setIndices(*indices); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - quadEffect->findAttributeInput("a_position", positionsInput); - quadEffect->findAttributeInput("a_texcoord", texcoordsInput); - geometryB->setInputBuffer(positionsInput, *vertexPositionsQuad); - geometryB->setInputBuffer(texcoordsInput, *textureCoords); + std::optional positionsInput = quadEffect->findAttributeInput("a_position"); + std::optional texcoordsInput = quadEffect->findAttributeInput("a_texcoord"); + assert(positionsInput.has_value() && texcoordsInput.has_value()); + geometryB->setInputBuffer(*positionsInput, *vertexPositionsQuad); + geometryB->setInputBuffer(*texcoordsInput, *textureCoords); } // create meshes ramses::MeshNode* meshNodeA = scene->createMeshNode("red triangle mesh node"); meshNodeA->setAppearance(*appearanceA); - meshNodeA->setGeometryBinding(*geometryA); + meshNodeA->setGeometry(*geometryA); meshNodeA->setIndexCount(3u); // using only 3 indices from index buffer which defines quad ramses::MeshNode* meshNodeB = scene->createMeshNode("texture quad mesh node"); meshNodeB->setAppearance(*appearanceB); - meshNodeB->setGeometryBinding(*geometryB); + meshNodeB->setGeometry(*geometryB); // add triangle mesh to first pass and quad to second one renderGroupA->addMeshNode(*meshNodeA); @@ -159,18 +159,18 @@ int main() // signal the scene it is in a state that can be rendered scene->flush(); - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); // application logic uint32_t loops = 100; /// [Render Once Example Retrigger] - while (--loops) + while (--loops != 0u) { // change color once per second // this will re-render the mesh to the render target only once per color change std::this_thread::sleep_for(std::chrono::seconds(1)); - appearanceA->setInputValue(color, ramses::vec4f{ (loops % 2 ? 1.0f : 0.0f), 0.0f, (loops % 2 ? 0.0f : 1.0f), 1.0f }); + appearanceA->setInputValue(*color, ramses::vec4f{((loops % 2) != 0u ? 1.0f : 0.0f), 0.0f, ((loops % 2) != 0u ? 0.0f : 1.0f), 1.0f}); renderPassRT->retriggerRenderOnce(); scene->flush(); } diff --git a/examples/ramses-example-text-basic/CMakeLists.txt b/examples/ramses-example-text-basic/CMakeLists.txt index 662ad31e3..e29e512b3 100644 --- a/examples/ramses-example-text-basic/CMakeLists.txt +++ b/examples/ramses-example-text-basic/CMakeLists.txt @@ -13,5 +13,5 @@ createModule( SRC_FILES src/*.cpp src/*.h RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-text-basic/src/main.cpp b/examples/ramses-example-text-basic/src/main.cpp index 26daa0994..b1fe808fa 100644 --- a/examples/ramses-example-text-basic/src/main.cpp +++ b/examples/ramses-example-text-basic/src/main.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text-api/TextCache.h" +#include "ramses/client/text/FontRegistry.h" +#include "ramses/client/text/TextCache.h" #include @@ -33,7 +33,8 @@ int main() ramses::RamsesFramework framework(config); ramses::RamsesClient& client(*framework.createClient("ExampleTextBasic")); - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u)); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = client.createScene(sceneConfig); // create font registry to hold font memory and text cache to cache text meshes ramses::FontRegistry fontRegistry; @@ -48,7 +49,7 @@ int main() // create render pass ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -61,7 +62,7 @@ int main() effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); effectDesc.setVertexShaderFromFile("res/ramses-example-text-basic-effect.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-example-text-basic-effect.frag"); - ramses::Effect* textEffect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "simpleTextShader"); + ramses::Effect* textEffect = scene->createEffect(effectDesc, "simpleTextShader"); // create font instance ramses::FontId font = fontRegistry.createFreetype2Font("res/ramses-example-text-basic-Roboto-Bold.ttf"); @@ -99,7 +100,7 @@ int main() scene->flush(); /// [Basic Text Example] - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); std::this_thread::sleep_for(std::chrono::seconds(30)); scene->unpublish(); diff --git a/examples/ramses-example-text-languages/CMakeLists.txt b/examples/ramses-example-text-languages/CMakeLists.txt index 52ce4d81e..fbc955006 100644 --- a/examples/ramses-example-text-languages/CMakeLists.txt +++ b/examples/ramses-example-text-languages/CMakeLists.txt @@ -12,5 +12,5 @@ createModule( ENABLE_INSTALL OFF # disabled installation to save space in release tarball SRC_FILES src/main.cpp RESOURCE_FOLDERS res - DEPENDENCIES ramses-shared-lib + DEPENDENCIES ramses::shared-lib-for-examples ) diff --git a/examples/ramses-example-text-languages/src/main.cpp b/examples/ramses-example-text-languages/src/main.cpp index 9151e9fdf..69fae67c5 100644 --- a/examples/ramses-example-text-languages/src/main.cpp +++ b/examples/ramses-example-text-languages/src/main.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text-api/TextCache.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/text/FontRegistry.h" +#include "ramses/client/text/TextCache.h" #include /** @@ -28,7 +28,8 @@ int main() ramses::RamsesFramework framework(config); ramses::RamsesClient& client(*framework.createClient("ExampleTextDirections")); - ramses::Scene* scene = client.createScene(ramses::sceneId_t(123u)); + const ramses::SceneConfig sceneConfig(ramses::sceneId_t{123}, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* scene = client.createScene(sceneConfig); // create font registry to hold font memory and text cache to cache text meshes ramses::FontRegistry fontRegistry; @@ -43,7 +44,7 @@ int main() // create render pass ramses::RenderPass* renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -56,7 +57,7 @@ int main() effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); effectDesc.setVertexShaderFromFile("res/ramses-example-text-languages-effect.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-example-text-languages-effect.frag"); - const ramses::Effect* textEffect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "simpleTextShader"); + const ramses::Effect* textEffect = scene->createEffect(effectDesc, "simpleTextShader"); /// [Languages Text Example] // create font instances const ramses::FontId hebrewFont = fontRegistry.createFreetype2Font("res/ramses-example-text-languages-Arimo-Regular.ttf"); @@ -109,7 +110,7 @@ int main() renderGroup->addMeshNode(*textLine5->meshNode); scene->flush(); - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); std::this_thread::sleep_for(std::chrono::seconds(30)); scene->unpublish(); diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0199f2fd2..b3d7110d4 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -48,6 +48,7 @@ target_link_libraries(ramses-gmock-main INTERFACE gmock_main gtest) if (TARGET fmt::fmt) message(STATUS "+ fmt (existing target)") else() + set(FMT_INSTALL OFF) add_subdirectory("fmt") target_compile_definitions(fmt PUBLIC "-DFMT_EXCEPTIONS=0") folderizeTarget(fmt) @@ -150,6 +151,23 @@ createModule( glslang-os-dep/GenericSingleThreaded/ossource.cpp ) +createModule( + NAME lodepng + TYPE STATIC_LIBRARY + ENABLE_INSTALL OFF + INCLUDE_PATHS lodepng + SRC_FILES lodepng/*.h + lodepng/lodepng.cpp + ) + +createModule( + NAME cityhash + TYPE STATIC_LIBRARY + ENABLE_INSTALL OFF + INCLUDE_PATHS cityhash/src + SRC_FILES cityhash/src/*.h + cityhash/src/city.cc + ) if(ramses-sdk_TEXT_SUPPORT) # find freetype with harfbuzz support @@ -162,7 +180,11 @@ if(ramses-sdk_TEXT_SUPPORT) # no 'd' suffix on debug libs set(DISABLE_FORCE_DEBUG_POSTFIX TRUE) + # freetype's FindHarfbuzz.cmake won't find our internal harfbuzz - force harfbuzz usage + set(HARFBUZZ_FOUND TRUE) + add_subdirectory(freetype) + target_include_directories(freetype PRIVATE "harfbuzz/src") message(STATUS "+ freetype") set(HB_BUILD_SUBSET OFF CACHE BOOL "don't build harfbuzz subset" FORCE) @@ -172,6 +194,12 @@ if(ramses-sdk_TEXT_SUPPORT) folderizeTarget(freetype) folderizeTarget(harfbuzz) + # Verify that harfbuzz is actually used + file(READ "${CMAKE_CURRENT_BINARY_DIR}/freetype/include/freetype/config/ftoption.h" FTOPTION_H) + string(REGEX MATCH "\n#define FT_CONFIG_OPTION_USE_HARFBUZZ" FREETYPE_USES_HARFBUZZ "${FTOPTION_H}") + if (NOT FREETYPE_USES_HARFBUZZ) + message(FATAL_ERROR "Failed to enable harfbuzz support for internal freetype.") + endif() endif() else() message(STATUS "- harfbuzz/freetype (text support disabled)") @@ -194,33 +222,29 @@ FIND_PACKAGE(wayland-client QUIET) FIND_PACKAGE(wayland-server QUIET) # wayland ivi extension library -IF (NOT ramses-sdk_DISABLE_WAYLAND_IVI_EXTENSION) - FIND_PACKAGE(wayland-ivi-extension QUIET) - IF (wayland-ivi-extension_FOUND) - message(STATUS "+ wayland-ivi-extension (system)") - ELSEIF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/wayland-ivi-extension") - IF (wayland-client_FOUND AND wayland-server_FOUND) - createModule( - NAME wayland-ivi-extension - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - - INCLUDE_PATHS wayland-ivi-extension/ivi-extension-protocol - SRC_FILES wayland-ivi-extension/ivi-extension-protocol/*.h - wayland-ivi-extension/ivi-extension-protocol/*.c - ) - ENDIF() - ENDIF() +FIND_PACKAGE(wayland-ivi-extension QUIET) +IF (wayland-ivi-extension_FOUND) + message(STATUS "+ wayland-ivi-extension (system)") +ELSEIF (wayland-client_FOUND AND wayland-server_FOUND) + createModule( + NAME wayland-ivi-extension + TYPE STATIC_LIBRARY + ENABLE_INSTALL OFF + + INCLUDE_PATHS wayland-ivi-extension/ivi-extension-protocol + SRC_FILES wayland-ivi-extension/ivi-extension-protocol/*.h + wayland-ivi-extension/ivi-extension-protocol/*.c + ) ENDIF() -option(ramses-sdk_BUILD_IVI_TEST_APPS "Enable build of ivi test applications" ON) -if (ramses-sdk_BUILD_IVI_TEST_APPS AND (wayland-ivi-extension_FOUND OR TARGET wayland-ivi-extension) AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/wayland-ivi-example-client") +importDependenciesAndCheckMissing(WAYLAND_IVI_EXT_MISSING wayland-ivi-extension) +if (ramses-sdk_BUILD_TOOLS AND (NOT WAYLAND_IVI_EXT_MISSING)) # wayland ivi example/test application createModule( NAME ivi-gears TYPE BINARY ENABLE_INSTALL ON - SRC_FILES wayland-ivi-example-client/gears.c + SRC_FILES wayland-ivi-example-client/gears.c DEPENDENCIES EGL OpenGL wayland-client @@ -245,7 +269,9 @@ IF (wayland-client_FOUND AND wayland-server_FOUND AND EXISTS "${CMAKE_CURRENT_SO ) ENDIF() -if (wayland-ivi-extension_FOUND OR TARGET wayland-ivi-extension) + +importDependenciesAndCheckMissing(MISSING_DEPENDENCY wayland-ivi-extension gbm libdrm) +if (ramses-sdk_BUILD_TOOLS AND (NOT MISSING_DEPENDENCY)) # wayland ivi example/test application 2 createModule( NAME ivi-simple-dmabuf-egl @@ -289,12 +315,11 @@ if(ramses-sdk_BUILD_TOOLS) ) endif() -# LZ4 de-/compression library -OPTION(ramses-sdk_ALLOW_PLATFORM_LZ4 "Enable to search for platform provided lz4" ON) # TODO (MacOS) find out why linking against the platform LZ4 doesn't work on Darwin IF((${CMAKE_SYSTEM_NAME} MATCHES "iOS") OR (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) SET(ramses-sdk_ALLOW_PLATFORM_LZ4 OFF) + message(WARNING "Force ramses-sdk_ALLOW_PLATFORM_LZ4 set to OFF") ENDIF() IF (ramses-sdk_ALLOW_PLATFORM_LZ4) @@ -343,7 +368,8 @@ if(ramses-sdk_ENABLE_LOGIC) ################ Lua ################## ################################################ - if(NOT TARGET lua) + if(NOT TARGET lua::lua) + message(STATUS "+ lua (internal) (STATIC_LIBRARY)") ensureSubmoduleExists(lua) # Collect all source and header files @@ -375,10 +401,9 @@ if(ramses-sdk_ENABLE_LOGIC) ) folderizeTarget(lua) + add_library(lua::lua ALIAS lua) endif() - add_library(lua::lua ALIAS lua) - ################################################ ################ Sol ################## ################################################ @@ -396,8 +421,6 @@ if(ramses-sdk_ENABLE_LOGIC) # Ensure sol is expecting c++ compiled lua target_compile_definitions(sol2 INTERFACE - # we compile lua with c++, make sol not use extern C etc - SOL_USING_CXX_LUA=1 # catch and redirect exception to user handler func instead of # prapagating them directly through lua SOL_EXCEPTIONS_ALWAYS_UNSAFE=1 @@ -408,6 +431,13 @@ if(ramses-sdk_ENABLE_LOGIC) # Make sure Debug flags are equivalent to Release flags in terms of behavior SOL_IN_DEBUG_DETECTED=0 ) + + if(NOT ramses-sdk_USE_PLATFORM_LUAJIT) + target_compile_definitions(sol2 INTERFACE + # we compile lua with c++, make sol not use extern C etc + SOL_USING_CXX_LUA=1 + ) + endif() endif() ################################################ @@ -439,3 +469,118 @@ if(ramses-sdk_ENABLE_LOGIC) folderizeTarget(flatc) endif() endif() + + +# ANGLE - Build for iOS support only +IF((${CMAKE_SYSTEM_NAME} MATCHES "iOS")) + # Define File Copy Function that WebKit ANGLE build expects to be there + function(WEBKIT_COPY_FILES target_name) + # This CMake macro is from the WebKit Repository and licensed as follows: + # BSD License + # + # Copyright (C) 2009 Apple Inc. All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # 1. Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. + # + # 2. Redistributions in binary form must reproduce the above copyright notice, this + # list of conditions and the following disclaimer in the documentation and/or other + # materials provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS “AS IS” AND ANY + # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + # EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + set(options FLATTENED) + set(oneValueArgs DESTINATION) + set(multiValueArgs FILES) + cmake_parse_arguments(opt "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(files ${opt_FILES}) + set(dst_files) + foreach (file IN LISTS files) + if (IS_ABSOLUTE ${file}) + set(src_file ${file}) + else () + set(src_file ${CMAKE_CURRENT_SOURCE_DIR}/${file}) + endif () + if (opt_FLATTENED) + get_filename_component(filename ${file} NAME) + set(dst_file ${opt_DESTINATION}/${filename}) + else () + get_filename_component(file_dir ${file} DIRECTORY) + file(MAKE_DIRECTORY ${opt_DESTINATION}/${file_dir}) + set(dst_file ${opt_DESTINATION}/${file}) + endif () + add_custom_command(OUTPUT ${dst_file} + COMMAND ${CMAKE_COMMAND} -E copy ${src_file} ${dst_file} + MAIN_DEPENDENCY ${file} + VERBATIM + ) + list(APPEND dst_files ${dst_file}) + endforeach () + add_custom_target(${target_name} ALL DEPENDS ${dst_files}) + endfunction() + + # Define Config Function that WebKit ANGLE build expects to be there. For iOS we need to define our own configuration, no config file exists. + macro(WEBKIT_INCLUDE_CONFIG_FILES_IF_EXISTS) + find_library(COREGRAPHICS_LIBRARY CoreGraphics) + find_library(FOUNDATION_LIBRARY Foundation) + find_library(IOKIT_LIBRARY IOKit) + find_library(IOSURFACE_LIBRARY IOSurface) + find_library(METAL_LIBRARY Metal) + find_package(ZLIB REQUIRED) + + list(APPEND ANGLE_SOURCES + ${metal_backend_sources} + + ${angle_translator_lib_metal_sources} + ${angle_translator_apple_sources} + ${angle_translator_glsl_apple_sources} + + ${libangle_gpu_info_util_ios_sources} + ${libangle_gpu_info_util_sources} + ${libangle_ios_sources} + ) + + list(APPEND ANGLE_DEFINITIONS + ANGLE_ENABLE_METAL + ANGLE_PLATFORM_IOS + ANGLE_ENABLE_APPLE_WORKAROUNDS + ) + + list(APPEND ANGLEGLESv2_LIBRARIES + ${COREGRAPHICS_LIBRARY} + ${FOUNDATION_LIBRARY} + ${IOKIT_LIBRARY} + ${IOSURFACE_LIBRARY} + ${METAL_LIBRARY} + ) + endmacro() + + # Configure the ANGLE build + set(USE_ANGLE_EGL ON) + set(ENABLE_WEBGL OFF) + set(ANGLE_FRAMEWORK_HEADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/ANGLE/FRAMEWORK_HEADERS_DESTINATION) + set(PORT Mac) + set(is_apple ON) + set(is_ios ON) + set(is_mac OFF) + + # Add Angle CMake build + add_subdirectory(ANGLE) + + target_include_directories(GLESv2 INTERFACE ANGLE/include) + target_include_directories(EGL INTERFACE ANGLE/include) + + # Alias ANGLE as OpenGL, so it is a drop in replacement. + add_library(OpenGL ALIAS GLESv2) +ENDIF() diff --git a/external/cityhash/src/city.cc b/external/cityhash/src/city.cc index b3e05a411..3717c497d 100644 --- a/external/cityhash/src/city.cc +++ b/external/cityhash/src/city.cc @@ -33,12 +33,6 @@ #include #include // for memcpy and memset -#include "Utils/Warnings.h" -WARNING_DISABLE_LINUX(-Wshadow) -#if defined(_MSC_VER) -#pragma warning(push, 2) -#endif - using namespace std; static cityhash::uint64 UNALIGNED_LOAD64(const char *p) { diff --git a/external/fmt b/external/fmt index 7bdf0628b..f5e54359d 160000 --- a/external/fmt +++ b/external/fmt @@ -1 +1 @@ -Subproject commit 7bdf0628b1276379886c7f6dda2cef2b3b374f0b +Subproject commit f5e54359df4c26b6230fc61d38aa294581393084 diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt deleted file mode 100644 index c7691ca24..000000000 --- a/framework/CMakeLists.txt +++ /dev/null @@ -1,270 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2016 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -file(GLOB - RAMSES_FRAMEWORK_FILES_SOURCE - ramses-framework/src/*.cpp) -file(GLOB - RAMSES_FRAMEWORK_API_INCLUDE_BASE - ramses-framework-api/include) - -add_library(ramses-framework-api INTERFACE) -target_include_directories(ramses-framework-api INTERFACE ${RAMSES_FRAMEWORK_API_INCLUDE_BASE}) -target_link_libraries(ramses-framework-api INTERFACE glm::glm) - -if (ramses-sdk_ENABLE_TCP_SUPPORT) - set(ramses-framework-TCP_MIXIN - DEPENDENCIES asio - - INCLUDE_PATHS Communication/TransportTCP/include - SRC_FILES Communication/TransportTCP/include/TransportTCP/*.h - Communication/TransportTCP/src/*.cpp) - - set(ramses-framework-test-TCP_MIXIN - INCLUDE_PATHS Communication/TransportTCP/test - SRC_FILES Communication/TransportTCP/test/*.h - Communication/TransportTCP/test/*.cpp) -endif() - -if(ramses-sdk_HAS_DLT) - set(ramses-framework-DLT_MIXIN - DEPENDENCIES automotive-dlt - - SRC_FILES DltLogAppender/include/DltLogAppender/DltAdapterImpl/*.h - DltLogAppender/src/DltAdapterImpl/*.cpp) -endif() - -find_package(AndroidSDK) -if (AndroidSDK_FOUND) - set(ramses-framework-AndroidLogger_MIXIN - DEPENDENCIES AndroidSDK - - INCLUDE_PATHS Core/Utils/AndroidLogger/include - SRC_FILES Core/Utils/AndroidLogger/include/*.h - Core/Utils/AndroidLogger/src/*.cpp - ) -endif() - -createModule( - NAME ramses-framework - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - - # public api - SRC_FILES ramses-framework-api/include/*.h - ramses-framework-api/include/ramses-framework-api/*.h - - # Internals - INCLUDE_PATHS ramses-framework/include - SRC_FILES ramses-framework/include/*.h - ramses-framework/include/ramses-framework/*.h - ${RAMSES_FRAMEWORK_FILES_SOURCE} - INCLUDE_PATHS Watchdog/include - SRC_FILES Watchdog/include/Watchdog/*.h - Watchdog/src/*.cpp - INCLUDE_PATHS PlatformAbstraction/include - SRC_FILES PlatformAbstraction/include/*.h - PlatformAbstraction/include/Collections/*.h - PlatformAbstraction/include/PlatformAbstraction/*.h - PlatformAbstraction/include/PlatformAbstraction/internal/*.h - PlatformAbstraction/src/*.cpp - INCLUDE_PATHS Core/Math3d/include - SRC_FILES Core/Math3d/include/Math3d/*.h - Core/Math3d/src/*.cpp - INCLUDE_PATHS Core/Utils/include - SRC_FILES Core/Utils/include/Utils/*.h - Core/Utils/src/*.cpp - INCLUDE_PATHS Core/Common/include - SRC_FILES Core/Common/include/Common/*.h - Core/Common/src/*.cpp - INCLUDE_PATHS Core/TaskFramework/include - SRC_FILES Core/TaskFramework/include/*.h - Core/TaskFramework/include/TaskFramework/*.h - Core/TaskFramework/src/*.cpp - INCLUDE_PATHS SceneGraph/SceneAPI/include - SRC_FILES SceneGraph/SceneAPI/include/SceneAPI/*.h - SceneGraph/SceneAPI/src/*.cpp - INCLUDE_PATHS SceneGraph/Scene/include - SRC_FILES SceneGraph/Scene/include/Scene/*.h - SceneGraph/Scene/include/Scene/*/*.h - SceneGraph/Scene/src/*.cpp - SceneGraph/Scene/src/*/*.cpp - INCLUDE_PATHS SceneGraph/Resource/include - SRC_FILES SceneGraph/Resource/include/Resource/*.h - SceneGraph/Resource/src/*.cpp - INCLUDE_PATHS SceneGraph/SceneUtils/include - SRC_FILES SceneGraph/SceneUtils/include/SceneUtils/*.h - SceneGraph/SceneUtils/src/*.cpp - INCLUDE_PATHS Communication/TransportCommon/include - SRC_FILES Communication/TransportCommon/include/TransportCommon/*.h - Communication/TransportCommon/src/*.cpp - INCLUDE_PATHS Ramsh/include - SRC_FILES Ramsh/include/Ramsh/*.h - Ramsh/src/*.cpp - INCLUDE_PATHS Components/include - SRC_FILES Components/include/*.h - Components/include/Components/*.h - Components/src/*.cpp - INCLUDE_PATHS DltLogAppender/include - SRC_FILES DltLogAppender/include/DltLogAppender/*.h - DltLogAppender/include/DltLogAppender/DltAdapterDummy/*.h - DltLogAppender/src/DltAdapterDummy/*.cpp - DltLogAppender/src/*.cpp - INCLUDE_PATHS SceneReferencing/include - SRC_FILES SceneReferencing/include/SceneReferencing/*.h - SceneReferencing/src/*.cpp - - # TODO create targets for these... - # cityhash from external - INCLUDE_PATHS ../external/cityhash/src - SRC_FILES ../external/cityhash/src/*.h - ../external/cityhash/src/city.cc - # lodepng from external - INCLUDE_PATHS ../external/lodepng - SRC_FILES ../external/lodepng/*.h - ../external/lodepng/lodepng.cpp - - DEPENDENCIES ramses-framework-api - ramses-common-base - lz4 - fmt::fmt - ramses-abseil - # conditional values - ${ramses-framework-TCP_MIXIN} - ${ramses-framework-DLT_MIXIN} - ${ramses-framework-AndroidLogger_MIXIN} - ) - -if (ramses-sdk_ENABLE_TCP_SUPPORT) - message(STATUS "+ TCP communication system support enabled") - target_compile_definitions(ramses-framework PUBLIC "-DHAS_TCP_COMM=1") -else() - message(STATUS "- TCP communication system support disabled") -endif() - -if (ramses-sdk_HAS_DLT) - target_compile_definitions(ramses-framework PUBLIC "-DDLT_ENABLED") - - if (automotive-dlt_HAS_FILETRANSFER) - target_compile_definitions(ramses-framework PUBLIC "-DDLT_HAS_FILETRANSFER") - endif() -endif() - -if(ramses-sdk_USE_LINUX_DEV_PTP) - target_compile_definitions(ramses-framework PUBLIC "-DRAMSES_LINUX_USE_DEV_PTP=1") -endif() - -# Thread priority and binding for worker threads -if (DEFINED ramses-sdk_WORKER_THREAD_PRIORITY AND NOT ramses-sdk_WORKER_THREAD_PRIORITY STREQUAL "") - target_compile_definitions(ramses-framework PRIVATE "-DRAMSES_WORKER_THREAD_PRIORITY=${ramses-sdk_WORKER_THREAD_PRIORITY}") -endif() - -if (DEFINED ramses-sdk_WORKER_THREAD_CORE_BINDING AND NOT ramses-sdk_WORKER_THREAD_CORE_BINDING STREQUAL "") - target_compile_definitions(ramses-framework PRIVATE "-DRAMSES_WORKER_THREAD_CORE_BINDING=${ramses-sdk_WORKER_THREAD_CORE_BINDING}") -endif() - -if (DEFINED ramses-sdk_CONN_KEEPALIVE_THREAD_CORE_BINDING AND NOT ramses-sdk_CONN_KEEPALIVE_THREAD_CORE_BINDING STREQUAL "") - target_compile_definitions(ramses-framework PRIVATE "-DRAMSES_CONN_KEEPALIVE_THREAD_CORE_BINDING=${ramses-sdk_CONN_KEEPALIVE_THREAD_CORE_BINDING}") -endif() - - -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - target_compile_options(ramses-framework PRIVATE "-Wsuggest-override") -endif() - -if(ramses-sdk_ENABLE_INSTALL) - install(DIRECTORY ramses-framework-api/include/ DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}" COMPONENT ramses-sdk-devel) -endif() - -set(ramses-shared-lib-MIXIN - ${ramses-shared-lib-MIXIN} - INCLUDE_PATHS ${RAMSES_FRAMEWORK_API_INCLUDE_BASE} - SRC_FILES ${RAMSES_FRAMEWORK_FILES_SOURCE} - DEPENDENCIES ramses-framework - CACHE INTERNAL "") - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME FrameworkTestUtils - TYPE STATIC_LIBRARY - INCLUDE_PATHS FrameworkTestUtils/include - SRC_FILES FrameworkTestUtils/include/*.h - FrameworkTestUtils/src/*.cpp - DEPENDENCIES ramses-framework - ramses-gmock - ) - - # all tests in one binary - createModule( - NAME ramses-framework-test - TYPE BINARY - - INCLUDE_PATHS PlatformAbstraction/test - SRC_FILES PlatformAbstraction/test/*.h - PlatformAbstraction/test/*.cpp - INCLUDE_PATHS ramses-framework/test - SRC_FILES ramses-framework/test/*.h - ramses-framework/test/*.cpp - INCLUDE_PATHS Core/Math3d/test - SRC_FILES Core/Math3d/test/*.h - Core/Math3d/test/*.cpp - INCLUDE_PATHS Core/Utils/test - SRC_FILES Core/Utils/test/*.h - Core/Utils/test/*.cpp - SRC_FILES Core/Common/test/*.h - Core/Common/test/*.cpp - INCLUDE_PATHS Core/TaskFramework/test - SRC_FILES Core/TaskFramework/test/*.h - Core/TaskFramework/test/*.cpp - INCLUDE_PATHS SceneGraph/Scene/test - SRC_FILES SceneGraph/Scene/test/*.h - SceneGraph/Scene/test/*.cpp - INCLUDE_PATHS SceneGraph/Resource/test - SRC_FILES SceneGraph/Resource/test/*.h - SceneGraph/Resource/test/*.cpp - INCLUDE_PATHS SceneGraph/SceneAPI/test - SRC_FILES SceneGraph/SceneAPI/test/*.h - SceneGraph/SceneAPI/test/*.cpp - INCLUDE_PATHS Communication/TransportCommon/test - SRC_FILES Communication/TransportCommon/test/*.h - Communication/TransportCommon/test/*.cpp - INCLUDE_PATHS Ramsh/test - SRC_FILES Ramsh/test/*.h - Ramsh/test/*.cpp - INCLUDE_PATHS Components/test - SRC_FILES Components/test/*.h - Components/test/*.cpp - INCLUDE_PATHS DltLogAppender/test - SRC_FILES DltLogAppender/test/*.h - DltLogAppender/test/*.cpp - INCLUDE_PATHS Watchdog/test - SRC_FILES Watchdog/test/*.h - Watchdog/test/*.cpp - INCLUDE_PATHS SceneReferencing/test - SRC_FILES SceneReferencing/test/*.h - SceneReferencing/test/*.cpp - - ${ramses-framework-test-TCP_MIXIN} - - SRC_FILES test/main.cpp - RESOURCE_FOLDERS test/res - - DEPENDENCIES ramses-framework - FrameworkTestUtils - ramses-gmock-main - ) - - # disable maybe uninitialized - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0) - target_compile_options(ramses-framework-test PRIVATE -Wno-maybe-uninitialized) - endif() - endif() - - makeTestFromTarget(TARGET ramses-framework-test SUFFIX UNITTEST) -endif() diff --git a/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializationHelper.h b/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializationHelper.h deleted file mode 100644 index 5c2932903..000000000 --- a/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializationHelper.h +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENEUPDATESERIALIZATIONHELPER_H -#define RAMSES_SCENEUPDATESERIALIZATIONHELPER_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include "absl/types/span.h" -#include -#include - -namespace ramses_internal -{ - class SceneActionCollection; - class IResource; - struct FlushInformation; - - namespace SceneActionSerialization - { - absl::Span SerializeDescription(const SceneActionCollection& actions, std::vector& workingMemory); - absl::Span SerializeData(const SceneActionCollection& actions); - - SceneActionCollection Deserialize(absl::Span description, absl::Span data); - }; - - namespace ResourceSerialization - { - absl::Span SerializeDescription(const IResource& resource, std::vector& workingMemory); - absl::Span SerializeData(const IResource& resource); - - std::unique_ptr Deserialize(absl::Span description, absl::Span data); - } - - namespace FlushInformationSerialization - { - absl::Span SerializeInfos(const FlushInformation& flushInfos, std::vector& workingMemory); - FlushInformation Deserialize(absl::Span flushInfoBlob); - } -} - -#endif diff --git a/framework/Core/Common/include/Common/StronglyTypedValue.h b/framework/Core/Common/include/Common/StronglyTypedValue.h deleted file mode 100644 index 6691f15b5..000000000 --- a/framework/Core/Common/include/Common/StronglyTypedValue.h +++ /dev/null @@ -1,101 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_INTERNAL_STRONGLYTYPEDVALUE_H -#define RAMSES_INTERNAL_STRONGLYTYPEDVALUE_H - -#include "Utils/Warnings.h" -#include "PlatformAbstraction/Hash.h" -#include "Collections/StringOutputStream.h" -#include "PlatformAbstraction/Macros.h" -#include -#include - -namespace ramses_internal -{ - template - class StronglyTypedValue final - { - public: - using BaseType = BaseT; - - static constexpr StronglyTypedValue Invalid() noexcept - { - return StronglyTypedValue(_invalid); - } - - constexpr explicit StronglyTypedValue(BaseType value) noexcept - : m_value(value) - { - } - - constexpr StronglyTypedValue() noexcept - : m_value(_invalid) - { - } - - RNODISCARD constexpr BaseType getValue() const - { - return m_value; - } - - RNODISCARD constexpr BaseType& getReference() - { - return m_value; - } - - RNODISCARD constexpr bool operator==(const StronglyTypedValue& other) const - { - return m_value == other.m_value; - } - - RNODISCARD constexpr bool operator!=(const StronglyTypedValue& other) const - { - return m_value != other.m_value; - } - - RNODISCARD constexpr bool isValid() const - { - return m_value != _invalid; - } - - static_assert(std::is_arithmetic::value || std::is_pointer::value, "expected arithmetic or pointer basetype"); - private: - BaseType m_value; - }; - -} - -#define MAKE_STRONGLYTYPEDVALUE_PRINTABLE(stronglyType) \ - template <> \ - struct fmt::formatter<::stronglyType> { \ - template \ - constexpr auto parse(ParseContext& ctx) { \ - return ctx.begin(); \ - } \ - template \ - constexpr auto format(const ::stronglyType& str, FormatContext& ctx) { \ - return fmt::format_to(ctx.out(), "{}", str.getValue()); \ - } \ - }; - - -namespace std -{ - template - struct hash> - { - public: - size_t operator()(const ramses_internal::StronglyTypedValue& v) const - { - return static_cast(hash()(v.getValue())); - } - }; -} - -#endif diff --git a/framework/Core/Common/test/StronglyTypedValueTest.cpp b/framework/Core/Common/test/StronglyTypedValueTest.cpp deleted file mode 100644 index 04c8b2ea5..000000000 --- a/framework/Core/Common/test/StronglyTypedValueTest.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "Common/StronglyTypedValue.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "UnsafeTestMemoryHelpers.h" -#include "gtest/gtest.h" -#include -#include - -namespace ramses_internal -{ - namespace - { - struct UInt32Tag {}; - using StronglyTypedUInt32 = StronglyTypedValue; - - using StronglyTypedPtr = StronglyTypedValue; - } -} - -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::StronglyTypedUInt32); - -namespace ramses_internal -{ - TEST(AStronglyTypedValue, CanBeCreatedWithDefaultValue) - { - StronglyTypedUInt32 stronglyTypedUInt; - EXPECT_EQ(10u, stronglyTypedUInt.getValue()); - - StronglyTypedPtr stronglyTypedPtr; - EXPECT_EQ(nullptr, stronglyTypedPtr.getValue()); - } - - TEST(AStronglyTypedValue, ReturnsInvalidValueFromInvalid) - { - EXPECT_EQ(10u, StronglyTypedUInt32::Invalid().getValue()); - EXPECT_EQ(nullptr, StronglyTypedPtr::Invalid().getValue()); - } - - TEST(AStronglyTypedValue, CanBeCreatedWithUInt32) - { - StronglyTypedUInt32 stronglyTypedUInt(12u); - (void)stronglyTypedUInt; - } - - TEST(AStronglyTypedValue, ReturnsCorrectUInt32Value) - { - StronglyTypedUInt32 stronglyTypedUInt(12u); - EXPECT_EQ(12u, stronglyTypedUInt.getValue()); - } - - TEST(AStronglyTypedValue, ReturnsCorrectUInt32Reference) - { - StronglyTypedUInt32 stronglyTypedUInt(12u); - EXPECT_EQ(12u, stronglyTypedUInt.getReference()); - } - - TEST(AStronglyTypedValue, ReturnedReferenceChangeEffectsValue_UInt32) - { - StronglyTypedUInt32 stronglyTypedUInt(12u); - stronglyTypedUInt.getReference() = 34u; - EXPECT_EQ(34u, stronglyTypedUInt.getReference()); - } - - TEST(AStronglyTypedValue, EqualsToOtherIfSameUInt32Value) - { - StronglyTypedUInt32 stronglyTypedUInt1(12u); - StronglyTypedUInt32 stronglyTypedUInt2(12u); - EXPECT_TRUE(stronglyTypedUInt1 == stronglyTypedUInt2); - EXPECT_FALSE(stronglyTypedUInt1 != stronglyTypedUInt2); - } - - TEST(AStronglyTypedValue, DoesNotEqualToOtherIfDifferentUInt32Value) - { - StronglyTypedUInt32 stronglyTypedUInt1(12u); - StronglyTypedUInt32 stronglyTypedUInt2(34u); - EXPECT_FALSE(stronglyTypedUInt1 == stronglyTypedUInt2); - EXPECT_TRUE(stronglyTypedUInt1 != stronglyTypedUInt2); - } - - TEST(AStronglyTypedValue, CanBeCopiedWithUInt32AsValue) - { - StronglyTypedUInt32 stronglyTypedUInt1(12u); - StronglyTypedUInt32 stronglyTypedUInt2(stronglyTypedUInt1); - EXPECT_EQ(stronglyTypedUInt1, stronglyTypedUInt2); - } - - TEST(AStronglyTypedValue, CanBeCopyAssignedWithUInt32AsValue) - { - StronglyTypedUInt32 stronglyTypedUInt1(12u); - StronglyTypedUInt32 stronglyTypedUInt2(34u); - stronglyTypedUInt2 = stronglyTypedUInt1; - EXPECT_EQ(stronglyTypedUInt1, stronglyTypedUInt2); - } - - TEST(AStronglyTypedValue, canBeUsedWithPointer) - { - int i = 1; - int j = 2; - StronglyTypedPtr p1(&i); - StronglyTypedPtr p2(&i); - StronglyTypedPtr p3(&j); - - EXPECT_TRUE(p1 == p2); - EXPECT_TRUE(p1 != p3); - EXPECT_FALSE(p1 == p3); - EXPECT_TRUE(p1.getValue() == &i); - - p3 = p1; - EXPECT_TRUE(p1 == p3); - - StronglyTypedPtr p4(nullptr); - EXPECT_TRUE(p4.getValue() == nullptr); - - p4.getReference() = &j; - EXPECT_TRUE(p4.getValue() == &j); - } - - TEST(AStronglyTypedValue, canBeUsedInStdHashContainerWithIntegral) - { - StronglyTypedUInt32 i1(1); - StronglyTypedUInt32 i2(2); - StronglyTypedUInt32 i3(3); - - std::unordered_set si; - si.insert(i1); - si.insert(i2); - EXPECT_TRUE(si.find(i1) != si.end()); - EXPECT_TRUE(si.find(i2) != si.end()); - EXPECT_FALSE(si.find(i3) != si.end()); - } - - TEST(AStronglyTypedValue, canBeUsedInStdHashContainerWithPointer) - { - int i = 1; - int j = 2; - StronglyTypedPtr p1(&i); - StronglyTypedPtr p2(nullptr); - StronglyTypedPtr p3(&j); - - std::unordered_set sp; - sp.insert(p1); - sp.insert(p2); - EXPECT_TRUE(sp.find(p1) != sp.end()); - EXPECT_TRUE(sp.find(p2) != sp.end()); - EXPECT_FALSE(sp.find(p3) != sp.end()); - } - - TEST(AStronglyTypedValue, canBeCheckedForValid) - { - EXPECT_FALSE(StronglyTypedUInt32().isValid()); - EXPECT_FALSE(StronglyTypedUInt32::Invalid().isValid()); - EXPECT_FALSE(StronglyTypedUInt32(10u).isValid()); - - EXPECT_FALSE(StronglyTypedPtr().isValid()); - EXPECT_FALSE(StronglyTypedPtr::Invalid().isValid()); - EXPECT_FALSE(StronglyTypedPtr(nullptr).isValid()); - - EXPECT_TRUE(StronglyTypedUInt32(123).isValid()); - EXPECT_TRUE(StronglyTypedUInt32(0).isValid()); - - EXPECT_TRUE(StronglyTypedPtr(UnsafeTestMemoryHelpers::ForgeArbitraryPointer(123u)).isValid()); - EXPECT_TRUE(StronglyTypedPtr(UnsafeTestMemoryHelpers::ForgeArbitraryPointer(1u)).isValid()); - } - - TEST(AStronglyTypedValue, canUseWithFmtlib) - { - StronglyTypedUInt32 val(123); - EXPECT_EQ("123", fmt::to_string(val)); - } - - // enforce performance guarantees - static_assert(std::is_trivially_copyable::value, "expectation failed"); - static_assert(std::is_trivially_destructible::value, "expectation failed"); -} diff --git a/framework/Core/Math3d/test/CameraMatrixHelperTest.cpp b/framework/Core/Math3d/test/CameraMatrixHelperTest.cpp deleted file mode 100644 index 427123a31..000000000 --- a/framework/Core/Math3d/test/CameraMatrixHelperTest.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2013 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "framework_common_gmock_header.h" -#include "gtest/gtest.h" -#include "Math3d/CameraMatrixHelper.h" - -using namespace ramses_internal; - -TEST(ACameraMatrixHelper, ComputesProjectionMatrix) -{ - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(30.0f, 2.0f, 0.1f, 10.0f)); - // Don't use matrix comparison operator here, but compare exact floats - // Rationale: tests should be explicit, should not use internal behaviour of matrix class - EXPECT_FLOAT_EQ(1.8660254f, projMatrix[0][0]); - EXPECT_FLOAT_EQ(0, projMatrix[1][0]); - EXPECT_FLOAT_EQ(0, projMatrix[2][0]); - EXPECT_FLOAT_EQ(0, projMatrix[3][0]); - EXPECT_FLOAT_EQ(0, projMatrix[0][1]); - EXPECT_FLOAT_EQ(3.73205f, projMatrix[1][1]); - EXPECT_FLOAT_EQ(0, projMatrix[2][1]); - EXPECT_FLOAT_EQ(0, projMatrix[3][1]); - EXPECT_FLOAT_EQ(0, projMatrix[0][2]); - EXPECT_FLOAT_EQ(0, projMatrix[1][2]); - EXPECT_FLOAT_EQ(-1.020202f, projMatrix[2][2]); - EXPECT_FLOAT_EQ(-0.20202021f, projMatrix[3][2]); - EXPECT_FLOAT_EQ(0, projMatrix[0][3]); - EXPECT_FLOAT_EQ(0, projMatrix[1][3]); - EXPECT_FLOAT_EQ(-1, projMatrix[2][3]); - EXPECT_FLOAT_EQ(0, projMatrix[3][3]); -} - -TEST(ACameraMatrixHelper, ComputesOrthographicMatrix) -{ - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Frustum(ECameraProjectionType::Orthographic, 30, 40, 50, 60, 10, 20)); - // Don't use matrix comparison operator here, but compare exact floats - // Rationale: tests should be explicit, should not use internal behaviour of matrix class - EXPECT_FLOAT_EQ(0.2f, projMatrix[0][0]); - EXPECT_FLOAT_EQ(0, projMatrix[1][0]); - EXPECT_FLOAT_EQ(0, projMatrix[2][0]); - EXPECT_FLOAT_EQ(-7.0f, projMatrix[3][0]); - EXPECT_FLOAT_EQ(0, projMatrix[0][1]); - EXPECT_FLOAT_EQ(0.2f, projMatrix[1][1]); - EXPECT_FLOAT_EQ(0, projMatrix[2][1]); - EXPECT_FLOAT_EQ(-11.0f, projMatrix[3][1]); - EXPECT_FLOAT_EQ(0, projMatrix[0][2]); - EXPECT_FLOAT_EQ(0, projMatrix[1][2]); - EXPECT_FLOAT_EQ(-0.2f, projMatrix[2][2]); - EXPECT_FLOAT_EQ(-3.0f, projMatrix[3][2]); - EXPECT_FLOAT_EQ(0, projMatrix[0][3]); - EXPECT_FLOAT_EQ(0, projMatrix[1][3]); - EXPECT_FLOAT_EQ(0, projMatrix[2][3]); - EXPECT_FLOAT_EQ(1, projMatrix[3][3]); -} - -// Confidence test (above tests already test enough) -TEST(APerspectiveProjectionMatrix, ProjectsPointsCloserToViewerFartherFromCenterInNormalizedDeviceSpace) -{ - const auto perpectivematrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(30.0f, 2.666f, 0.00001f, 100.f)); - // In right handed coordinate systems camera view ray is pointing towards -1, thus bigger negative Z means farther away from viewer - glm::vec4 vecClose(1.0f, 12.0f, -50.0f, 1.0f); - glm::vec4 vecFar(1.0f, 12.0f, -100.0f, 1.0f); - - glm::vec4 resultClose = perpectivematrix * vecClose; - glm::vec4 resultFar = perpectivematrix * vecFar; - EXPECT_GT(resultClose.x / resultClose.w, resultFar.x / resultFar.w); - EXPECT_GT(resultClose.y / resultClose.w, resultFar.y / resultFar.w); -} - -// Confidence test (above tests already test enough) -TEST(AOrthographicProjectionMatrix, ProjectsPointsWithDifferentDepthToTheSamePixelInNormalizedDeviceSpace) -{ - const auto orthographicMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Frustum(ECameraProjectionType::Orthographic, - -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 100.0f)); - glm::vec4 vecA(1.0f, 2.0f, 0.0f, 1.f); - glm::vec4 vecB(1.0f, 2.0f, -50.0f, 1.f); - - glm::vec4 resultA = orthographicMatrix* vecA; - glm::vec4 resultB = orthographicMatrix* vecB; - EXPECT_FLOAT_EQ(resultA.x / resultA.w, resultB.x / resultB.w); - EXPECT_FLOAT_EQ(resultA.y / resultA.w, resultB.y / resultB.w); -} diff --git a/framework/Core/Utils/include/Utils/LoggingUtils.h b/framework/Core/Utils/include/Utils/LoggingUtils.h deleted file mode 100644 index 3d53de592..000000000 --- a/framework/Core/Utils/include/Utils/LoggingUtils.h +++ /dev/null @@ -1,109 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_LOGGINGUTILS_H -#define RAMSES_LOGGINGUTILS_H - -#include "PlatformAbstraction/FmtBase.h" -#include -#include -#include -#include - -// declares mapping function from enum to strings -// Caution: enum has range from zero to lastElement -#define ENUM_TO_STRING(type, elements, lastElement) \ - /* ensure that elements have the same number of elements as the enum types */ \ - static_assert(static_cast(lastElement) == elements.size(), "number of elements does not match"); \ - static inline const char* EnumToString(type index) \ - { \ - const std::size_t value = static_cast(index); \ - if (value >= static_cast(lastElement)) \ - { \ - assert(false && "EnumToString called with invalid value"); \ - return ""; \ - } \ - return elements[value]; \ - }; - -#define ENUM_TO_STRING_NO_EXTRA_LAST(type, elements, lastElement) \ - /* ensure that elements have the same number of elements as the enum types */ \ - static_assert(static_cast(lastElement) + 1 == elements.size(), "number of elements does not match"); \ - static inline const char* EnumToString(type index) \ - { \ - const std::size_t value = static_cast(index); \ - if (value > static_cast(lastElement)) \ - { \ - assert(false && "EnumToString called with invalid value"); \ - return ""; \ - } \ - return elements[value]; \ - }; - -#define MAKE_ENUM_CLASS_PRINTABLE(type, enumName, elementNameArray, oneAfterLastElement) \ - /* ensure that elementNameArray have the same number of elements as the enum types */ \ - static_assert(static_cast(oneAfterLastElement) < std::numeric_limits::max()); \ - static_assert(static_cast(oneAfterLastElement) == static_cast(elementNameArray.size()), "number of elements does not match"); \ - static_assert(std::is_enum::value && !std::is_convertible::value, "Must use with enum class"); \ - template <> struct fmt::formatter \ - { \ - bool shortLog = false; \ - template constexpr auto parse(ParseContext& ctx) \ - { \ - auto it = ctx.begin(); \ - const auto end = ctx.end(); \ - if (it != end && *it == 's') \ - { \ - ++it; \ - shortLog = true; \ - } \ - if (it != end && *it != '}') \ - assert(false && "invalid format for enum class"); \ - return it; \ - } \ - template constexpr auto format(type index, FormatContext& ctx) \ - { \ - const auto value = static_cast(index); \ - if (value >= static_cast(oneAfterLastElement)) \ - return fmt::format_to(ctx.out(), "", value); \ - return shortLog ? fmt::format_to(ctx.out(), "{}", elementNameArray[value]) : fmt::format_to(ctx.out(), "{}::{}", enumName, elementNameArray[value]); \ - } \ - }; - -#define MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(type, enumName, elementNameArray, lastEnumElement) \ - /* ensure that elementNameArray have the same number of elements as the enum types */ \ - static_assert(static_cast(lastEnumElement) < std::numeric_limits::max()); \ - static_assert(static_cast(lastEnumElement) + 1 == static_cast(elementNameArray.size()), "number of elements does not match"); \ - static_assert(std::is_enum::value && !std::is_convertible::value, "Must use with enum class"); \ - template <> struct fmt::formatter \ - { \ - static constexpr const auto lastValue = static_cast(lastEnumElement); \ - bool shortLog = false; \ - template constexpr auto parse(ParseContext& ctx) \ - { \ - auto it = ctx.begin(); \ - const auto end = ctx.end(); \ - if (it != end && *it == 's') \ - { \ - ++it; \ - shortLog = true; \ - } \ - if (it != end && *it != '}') \ - assert(false && "invalid format for enum class"); \ - return it; \ - } \ - template constexpr auto format(type index, FormatContext& ctx) \ - { \ - const auto value = static_cast(index); \ - if (value > lastValue) \ - return fmt::format_to(ctx.out(), "", value); \ - return shortLog ? fmt::format_to(ctx.out(), "{}", elementNameArray[value]) : fmt::format_to(ctx.out(), "{}::{}", enumName, elementNameArray[value]); \ - } \ - }; - -#endif diff --git a/framework/Core/Utils/include/Utils/MessagePool.h b/framework/Core/Utils/include/Utils/MessagePool.h deleted file mode 100644 index de100170e..000000000 --- a/framework/Core/Utils/include/Utils/MessagePool.h +++ /dev/null @@ -1,102 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_MESSAGEPOOL_H -#define RAMSES_MESSAGEPOOL_H - -#include "Collections/Vector.h" -#include "PlatformAbstraction/PlatformStringUtils.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include - -namespace ramses_internal -{ - struct MessageEntry final - { - static const uint32_t MaxMessageLength = 128u; - - std::array m_text; - }; - - template - class MessagePool final - { - public: - MessagePool(); - - uint32_t addMessage(const char* message); - [[nodiscard]] const char* getMessage(uint32_t id) const; - [[nodiscard]] bool isMessageCached(uint32_t id) const; - - static const char* getSuccessText(); - static const char* getUnknownText(); - - static const uint32_t MaxMessageEntries = MaxEntries; - static const uint32_t SuccessMessageID = SuccessMessageEntryID; - - private: - uint32_t m_nextIndex; - std::vector m_messages; - }; - - template - MessagePool::MessagePool() - : m_nextIndex(SuccessMessageEntryID + 1u) - { - m_messages.resize(MaxEntries); - } - - template - uint32_t MessagePool::addMessage(const char* message) - { - const uint32_t arrayIndex = (m_nextIndex % MaxEntries); - PlatformMemory::Set(m_messages[arrayIndex].m_text.data(), 0, MessageEntry::MaxMessageLength); - PlatformStringUtils::Copy(m_messages[arrayIndex].m_text.data(), MessageEntry::MaxMessageLength, message); - const uint32_t id = m_nextIndex; - ++m_nextIndex; - - return id; - } - - template - const char* MessagePool::getMessage(uint32_t id) const - { - if (id == SuccessMessageEntryID) - { - return getSuccessText(); - } - - if (!isMessageCached(id)) - { - return getUnknownText(); - } - - return m_messages[id % MaxEntries].m_text.data(); - } - - template - bool MessagePool::isMessageCached(uint32_t id) const - { - const uint32_t oldestCachedIndex = (m_nextIndex < MaxEntries ? 0u : m_nextIndex - MaxEntries); - return (id >= oldestCachedIndex && id < m_nextIndex); - } - - template - const char* MessagePool::getSuccessText() - { - return "OK"; - } - - template - const char* MessagePool::getUnknownText() - { - return "Unknown"; - } -} - -#endif diff --git a/framework/Core/Utils/src/UserLogAppender.cpp b/framework/Core/Utils/src/UserLogAppender.cpp deleted file mode 100644 index a7f0fdb56..000000000 --- a/framework/Core/Utils/src/UserLogAppender.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "Utils/UserLogAppender.h" -#include "Utils/LogMessage.h" -#include "Utils/LogContext.h" - -namespace -{ - ramses::ELogLevel GetELogLevel(ramses_internal::ELogLevel logLevel) - { - switch (logLevel) - { - case ramses_internal::ELogLevel::Off: - return ramses::ELogLevel::Off; - case ramses_internal::ELogLevel::Fatal: - return ramses::ELogLevel::Fatal; - case ramses_internal::ELogLevel::Error: - return ramses::ELogLevel::Error; - case ramses_internal::ELogLevel::Warn: - return ramses::ELogLevel::Warn; - case ramses_internal::ELogLevel::Info: - return ramses::ELogLevel::Info; - case ramses_internal::ELogLevel::Debug: - return ramses::ELogLevel::Debug; - case ramses_internal::ELogLevel::Trace: - return ramses::ELogLevel::Trace; - } - - return ramses::ELogLevel::Off; - } -} - -namespace ramses_internal -{ - UserLogAppender::UserLogAppender(const ramses::LogHandlerFunc& f) - : m_func(f) - { - } - - void UserLogAppender::log(const LogMessage& logMessage) - { - m_func(GetELogLevel(logMessage.getLogLevel()), logMessage.getContext().getContextId(), logMessage.getStream().data()); - } -} diff --git a/framework/Core/Utils/test/ImageTest.cpp b/framework/Core/Utils/test/ImageTest.cpp deleted file mode 100644 index 748206f1f..000000000 --- a/framework/Core/Utils/test/ImageTest.cpp +++ /dev/null @@ -1,374 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "framework_common_gmock_header.h" -#include "gtest/gtest.h" -#include "Utils/Image.h" -#include "Utils/BinaryFileInputStream.h" -#include -#include - -using namespace ramses_internal; - -TEST(AnImage, canGetResolution) -{ - uint8_t data[2 * 3 * 4] = { 3 }; // width*height*channels - - Image bitmap(2u, 3u, data, data + sizeof(data)); - EXPECT_EQ(2u, bitmap.getWidth()); - EXPECT_EQ(3u, bitmap.getHeight()); - EXPECT_EQ(6u, bitmap.getNumberOfPixels()); -} - -TEST(AnImage, canSaveAndLoadPNG) -{ - const uint32_t w = 10u; - const uint32_t h = 5u; - std::vector data(w * h * 4); - std::iota(data.begin(), data.end(), uint8_t(0u)); - const Image bitmap(w, h, std::move(data)); - bitmap.saveToFilePNG("bitmapTest.png"); - Image loadedBitmap; - loadedBitmap.loadFromFilePNG("bitmapTest.png"); - EXPECT_EQ(bitmap, loadedBitmap); - EXPECT_EQ(bitmap.getWidth(), loadedBitmap.getWidth()); - EXPECT_EQ(bitmap.getHeight(), loadedBitmap.getHeight()); - EXPECT_EQ(bitmap.getData(), loadedBitmap.getData()); -} - -TEST(AnImage, ReportsTheSumOfAllItsPixels) -{ - uint8_t data[400]; - - for (uint8_t i = 0; i < 100; ++i) - { - data[4 * i + 0] = 1; - data[4 * i + 1] = 2; - data[4 * i + 2] = 3; - data[4 * i + 3] = 4; - } - - Image bitmap(10, 10, data, data + sizeof(data)); - - const glm::ivec4 expectedSumOfPixelData{ 100u, 200u, 300u, 400u }; - EXPECT_EQ(expectedSumOfPixelData, bitmap.getSumOfPixelValues()); -} - -TEST(AnImage, ReportsTheSumOfAllItsPixels_4K_Image) -{ - constexpr uint32_t width = 4096; - constexpr uint32_t height = 2160; - - constexpr uint8_t maxPixelValue = std::numeric_limits::max(); - std::vector data(width * height * 4, maxPixelValue); - const Image bitmap(width, height, std::move(data)); - - const glm::ivec4 resultSumOfPixelValues = bitmap.getSumOfPixelValues(); - ASSERT_EQ(resultSumOfPixelValues.x, resultSumOfPixelValues.y); - ASSERT_EQ(resultSumOfPixelValues.x, resultSumOfPixelValues.z); - ASSERT_EQ(resultSumOfPixelValues.x, resultSumOfPixelValues.w); - - constexpr int32_t maxSumValue = std::numeric_limits::max(); - EXPECT_EQ(resultSumOfPixelValues.x, maxSumValue); -} - -TEST(AnImage, CanGenerateSeparateColorAndAlphaImages) -{ - std::vector data; - std::vector expectedColorData; - std::vector expectedAlphaData; - data.reserve(400u); - expectedColorData.reserve(400u); - expectedAlphaData.reserve(400u); - - for (uint8_t i = 0; i < 100; ++i) - { - data.push_back(1); - data.push_back(2); - data.push_back(3); - data.push_back(4); - - expectedColorData.push_back(1); - expectedColorData.push_back(2); - expectedColorData.push_back(3); - expectedColorData.push_back(255); - - expectedAlphaData.push_back(4); - expectedAlphaData.push_back(4); - expectedAlphaData.push_back(4); - expectedAlphaData.push_back(255); - } - - const Image bitmap(10, 10, std::move(data)); - const Image expectedColorBitmap(10, 10, std::move(expectedColorData)); - const Image expectedAlphaBitmap(10, 10, std::move(expectedAlphaData)); - - const auto resultBitmaps = bitmap.createSeparateColorAndAlphaImages(); - EXPECT_EQ(expectedColorBitmap, resultBitmaps.first); - EXPECT_EQ(expectedAlphaBitmap, resultBitmaps.second); -} - -TEST(AnImage, CanBeSubtractedFromOtherBitmapWithSameSize) -{ - constexpr size_t Width = 5; - constexpr size_t Height = 3; - uint8_t data1[Width * Height * 4]; - uint8_t data2[Width * Height * 4]; - - for (uint8_t i = 0; i < Width * Height; ++i) - { - // create mosaic difference - const uint8_t value1 = (i % 2) ? 1 : 3; - const uint8_t value2 = (i % 2) ? 3 : 1; - - data1[4 * i + 0] = value1; - data1[4 * i + 1] = value1; - data1[4 * i + 2] = value1; - data1[4 * i + 3] = value1; - - data2[4 * i + 0] = value2; - data2[4 * i + 1] = value2; - data2[4 * i + 2] = value2; - data2[4 * i + 3] = value2; - } - - const Image bitmap1(Width, Height, data1, data1 + sizeof(data1)); - const Image bitmap2(Width, Height, data2, data2 + sizeof(data2)); - const Image bitmapDiff1 = bitmap1.createDiffTo(bitmap2); - const Image bitmapDiff2 = bitmap2.createDiffTo(bitmap1); - - // W*H pixels, times a difference of 2 (because of shifted mosaic pattern) - const glm::ivec4 expectedDiff(2u * Width * Height); - - EXPECT_EQ(expectedDiff, bitmapDiff1.getSumOfPixelValues()); - EXPECT_EQ(expectedDiff, bitmapDiff2.getSumOfPixelValues()); - EXPECT_EQ(2u, bitmapDiff2.getData()[3]); - EXPECT_EQ(2u, bitmapDiff1.getData()[3]); -} - -TEST(AnImage, YieldsBlackImageWhenSubtractedFromItself) -{ - constexpr size_t Width = 3; - constexpr size_t Height = 5; - uint8_t data[Width * Height * 4]; - - for (uint8_t i = 0; i < Width*Height; ++i) - { - data[4 * i + 0] = 12u; - data[4 * i + 1] = 14u; - data[4 * i + 2] = 11u; - data[4 * i + 3] = 13u; - } - - const Image bitmap(Width, Height, data, data + sizeof(data)); - const Image bitmapDiff = bitmap.createDiffTo(bitmap); - - EXPECT_EQ(glm::ivec4(0u), bitmapDiff.getSumOfPixelValues()); -} - -TEST(AnImage, GivesEmptyImageIfEnlargingToSmallerSize) -{ - const Image image(2, 2, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); - EXPECT_EQ(Image(), image.createEnlarged(1, 3)); - EXPECT_EQ(Image(), image.createEnlarged(3, 1)); - EXPECT_EQ(Image(), image.createEnlarged(1, 1)); -} - -TEST(AnImage, GivesImageCopyIfEnlargingToSameSize) -{ - const Image image(2, 2, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); - EXPECT_EQ(image, image.createEnlarged(2, 2)); -} - -TEST(AnImage, CreatesEnlargedImageContainingOriginalAndRestFilledWithGivenValue_extendedWidth) -{ - const Image image(2, 2, - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16 - }); - const Image expectedImage(3, 2, - { - 1, 2, 3, 4, 5, 6, 7, 8, 33, 44, 55, 66, - 9, 10, 11, 12, 13, 14, 15, 16, 33, 44, 55, 66 - }); - EXPECT_EQ(expectedImage, image.createEnlarged(3, 2, {33, 44, 55, 66})); -} - -TEST(AnImage, CreatesEnlargedImageContainingOriginalAndRestFilledWithGivenValue_extendedHeight) -{ - const Image image(2, 2, - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16 - }); - const Image expectedImage(2, 3, - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 33, 44, 55, 66, 33, 44, 55, 66 - }); - EXPECT_EQ(expectedImage, image.createEnlarged(2, 3, { 33, 44, 55, 66 })); -} - -TEST(AnImage, CreatesEnlargedImageContainingOriginalAndRestFilledWithGivenValue_extendedWidthAndHeight) -{ - const Image image(2, 2, - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16 - }); - const Image expectedImage(3, 3, - { - 1, 2, 3, 4, 5, 6, 7, 8, 33, 44, 55, 66, - 9, 10, 11, 12, 13, 14, 15, 16, 33, 44, 55, 66, - 33, 44, 55, 66, 33, 44, 55, 66, 33, 44, 55, 66 - }); - EXPECT_EQ(expectedImage, image.createEnlarged(3, 3, { 33, 44, 55, 66 })); -} - -TEST(AnImage, CopyConstructedBitmapIsSame) -{ - uint8_t data[400] = { 0 }; - - for (uint8_t i = 0; i < 100; ++i) - { - data[4 * i + 0] = 12u; - data[4 * i + 1] = 14u; - data[4 * i + 2] = 11u; - } - - Image bitmap(10, 10, data, data + sizeof(data)); - Image copied(bitmap); - (void)copied; - - EXPECT_EQ(bitmap, copied); -} - -TEST(AnImage, AssignedBitmapIsSame) -{ - const Image image(2, 2, - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16 - }); - - Image image2(1, 1, { 1, 2, 3, 4 }); - - image2 = image; - EXPECT_EQ(image, image2); -} - -TEST(AnImage, MoveAssignedBitmapIsSame) -{ - const Image image(2, 2, - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16 - }); - - Image image2(1, 1, { 1, 2, 3, 4 }); - - const Image origImage = image; - (void)origImage; - - image2 = std::move(image); - EXPECT_EQ(origImage, image2); -} - -TEST(AnImage, UnequalSizeBitmapsAreDifferent) -{ - static const uint8_t data[5 * 3 * 4] = { 0 }; - const Image bitmap(5, 3, data, data + sizeof(data)); - - const Image otherHeight(5, 2, data, data + 5*2*4); - const Image otherWidth(4, 3, data, data + 4*3*4); - const Image otherSizeSamePixelCount(3, 5, data, data + sizeof(data)); - - EXPECT_NE(bitmap, otherHeight); - EXPECT_NE(bitmap, otherWidth); - EXPECT_NE(bitmap, otherSizeSamePixelCount); -} - -TEST(AnImage, FlipsImage2x1) -{ - const std::vector data - { - 1, 2, 3, 4, 5, 6, 7, 8 - }; - const Image expectedImage(2, 1, - { - 1, 2, 3, 4, 5, 6, 7, 8 - }); - - EXPECT_EQ(expectedImage, Image(2, 1, data.cbegin(), data.cend(), true)); -} - -TEST(AnImage, FlipsImage2x2) -{ - const std::vector data - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16 - }; - const Image expectedImage(2, 2, - { - 9, 10, 11, 12, 13, 14, 15, 16, - 1, 2, 3, 4, 5, 6, 7, 8 - }); - - EXPECT_EQ(expectedImage, Image(2, 2, data.cbegin(), data.cend(), true)); -} - -TEST(AnImage, FlipsImage2x3) -{ - const std::vector data - { - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24 - }; - const Image expectedImage(2, 3, - { - 17, 18, 19, 20, 21, 22, 23, 24, - 9, 10, 11, 12, 13, 14, 15, 16, - 1, 2, 3, 4, 5, 6, 7, 8, - }); - - EXPECT_EQ(expectedImage, Image(2, 3, data.cbegin(), data.cend(), true)); -} - -TEST(AnImage, FlipsImage4x4) -{ - const std::vector data - { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 - }; - const Image expectedImage(4, 4, - { - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 - }); - - EXPECT_EQ(expectedImage, Image(4, 4, data.cbegin(), data.cend(), true)); -} - -TEST(AnImage, getsNumOfNonBlackPixels) -{ - std::array data{ { 0, 1, 0, 0, 0, 2, 1, 0, 5, 2, 1, 0, 13, 20, 1, 0 } }; - const Image bitmap(2, 2, data.cbegin(), data.cend()); - EXPECT_EQ(3u, bitmap.getNumberOfNonBlackPixels()); - EXPECT_EQ(2u, bitmap.getNumberOfNonBlackPixels(2)); - EXPECT_EQ(1u, bitmap.getNumberOfNonBlackPixels(5)); - EXPECT_EQ(0u, bitmap.getNumberOfNonBlackPixels(20)); -} diff --git a/framework/DltLogAppender/include/DltLogAppender/DltAdapter.h b/framework/DltLogAppender/include/DltLogAppender/DltAdapter.h deleted file mode 100644 index 12688b227..000000000 --- a/framework/DltLogAppender/include/DltLogAppender/DltAdapter.h +++ /dev/null @@ -1,20 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DLTADAPTER_H -#define RAMSES_DLTADAPTER_H - -#ifdef DLT_ENABLED -#include "DltLogAppender/DltAdapterImpl/DltAdapterImpl.h" -using DltAdapter = ramses_internal::DltAdapterImpl; -#else -#include "DltLogAppender/DltAdapterDummy/DltAdapterDummy.h" -using DltAdapter = ramses_internal::DltAdapterDummy; -#endif - -#endif // RAMSES_DLTADAPTER_H diff --git a/framework/DltLogAppender/test/DltAdapterTest.cpp b/framework/DltLogAppender/test/DltAdapterTest.cpp deleted file mode 100644 index c6e80d51b..000000000 --- a/framework/DltLogAppender/test/DltAdapterTest.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "DltLogAppender/DltAdapter.h" -#include "Utils/LogContext.h" -#include "Utils/LogMessage.h" - -#include - -using namespace ramses_internal; - -class ADltAdapter : public ::testing::Test -{ -public: - ADltAdapter() - : adapter(DltAdapter::getDltAdapter()) - , context(new LogContext("RXXX", "Test context")) - , logLevelChangeCallback([](const std::string&, int){}) - { - } - - void SetUp() override - { - if (DltAdapter::IsDummyAdapter()) - GTEST_SKIP(); - - if (adapter && adapter->isInitialized()) - adapter->uninitialize(); - } - - ~ADltAdapter() override - { - if (adapter && adapter->isInitialized()) - adapter->uninitialize(); - } - - DltAdapter* adapter; - std::unique_ptr context; - std::function logLevelChangeCallback; -}; - -// run always -TEST(DltAdapter, singletonNotNull) -{ - EXPECT_TRUE(DltAdapter::getDltAdapter() != nullptr); -} - -TEST_F(ADltAdapter, initiallyUninitialized) -{ - EXPECT_FALSE(adapter->isInitialized()); -} - -TEST_F(ADltAdapter, initializeAndUninitialize) -{ - EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); - EXPECT_TRUE(adapter->isInitialized()); - - adapter->uninitialize(); - EXPECT_FALSE(adapter->isInitialized()); -} - -TEST_F(ADltAdapter, doubleInitFails) -{ - EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); - EXPECT_FALSE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); -} - -TEST_F(ADltAdapter, appIdMustBeValid) -{ - EXPECT_FALSE(adapter->initialize("", "Test application", true, logLevelChangeCallback, {context.get()}, true)); - EXPECT_FALSE(adapter->initialize("TEST123", "Test application", true, logLevelChangeCallback, {context.get()}, true)); -} - -TEST_F(ADltAdapter, contextListMayNotBeEmpty) -{ - EXPECT_FALSE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {}, true)); -} - -TEST_F(ADltAdapter, canLogOnContext) -{ - EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); - StringOutputStream sos(std::string("foo")); - LogMessage msg(*context, ELogLevel::Info, sos); - EXPECT_TRUE(adapter->logMessage(msg)); -} - -TEST_F(ADltAdapter, logFailsWithoutInit) -{ - StringOutputStream sos(std::string("foo")); - LogMessage msg(*context, ELogLevel::Info, sos); - EXPECT_FALSE(adapter->logMessage(msg)); -} - -TEST_F(ADltAdapter, canLogWithLogLevelOff) -{ - EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); - StringOutputStream sos(std::string("foo")); - LogMessage msg(*context, ELogLevel::Off, sos); - EXPECT_TRUE(adapter->logMessage(msg)); -} - -TEST_F(ADltAdapter, logFailsWithNonDltContext) -{ - EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); - - std::unique_ptr otherContext(new LogContext("RYYY", "Other test context")); - StringOutputStream sos(std::string("foo")); - LogMessage msg(*otherContext, ELogLevel::Info, sos); - EXPECT_FALSE(adapter->logMessage(msg)); -} - -#if defined(DLT_ENABLED) - -WARNINGS_PUSH -WARNING_DISABLE_LINUX(-Wold-style-cast) -#include -WARNINGS_POP - -TEST_F(ADltAdapter, canUseAlreadyRegisteredApp) -{ - DLT_REGISTER_APP("RAPP", "Ramses test app"); - EXPECT_TRUE(adapter->initialize("TEST", "Test application", false, logLevelChangeCallback, {context.get()}, true)); - adapter->uninitialize(); - DLT_UNREGISTER_APP(); -} -#endif diff --git a/framework/FrameworkTestUtils/include/ScopedLogContextLevel.h b/framework/FrameworkTestUtils/include/ScopedLogContextLevel.h deleted file mode 100644 index e5998c511..000000000 --- a/framework/FrameworkTestUtils/include/ScopedLogContextLevel.h +++ /dev/null @@ -1,37 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_FRAMEWORKTESTUTILS_SCOPEDLOGCONTEXTLEVEL_H -#define RAMSES_FRAMEWORKTESTUTILS_SCOPEDLOGCONTEXTLEVEL_H - -#include "Utils/LogContext.h" - -namespace ramses_internal -{ - class ScopedLogContextLevel - { - public: - ScopedLogContextLevel(LogContext& context, ELogLevel scopeLogLevel) - : m_context(context) - , m_savedLogLevel(m_context.getLogLevel()) - { - m_context.setLogLevel(scopeLogLevel); - } - - ~ScopedLogContextLevel() - { - m_context.setLogLevel(m_savedLogLevel); - } - - private: - LogContext& m_context; - ELogLevel m_savedLogLevel; - }; -} - -#endif diff --git a/framework/FrameworkTestUtils/include/framework_common_gmock_header.h b/framework/FrameworkTestUtils/include/framework_common_gmock_header.h deleted file mode 100644 index 5ae18dc44..000000000 --- a/framework/FrameworkTestUtils/include/framework_common_gmock_header.h +++ /dev/null @@ -1,44 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_FRAMEWORK_COMMON_GMOCK_HEADER_H -#define RAMSES_FRAMEWORK_COMMON_GMOCK_HEADER_H - -#include "SceneAPI/Handles.h" -#include -#include "DataTypesImpl.h" - -namespace ramses_internal -{ - // These are custom printers, which shows ramses type values in mocks and tests - // It needs to be in a separate file visible by ALL compilation units (!!!) - // The reason for this is that PrintTo has a template version in gmock/gtest - // which violates the one definition rule (http://en.wikipedia.org/wiki/One_Definition_Rule) - // Can also read more about it here: https://groups.google.com/forum/#!topic/googletestframework/mBlPZtprtr8 - - class SceneActionCollection; - class RamshInput; - class Guid; - struct SceneInfo; - - void PrintTo(const SceneActionCollection& actions, std::ostream* os); - void PrintTo(const DataFieldHandle& field, ::std::ostream* os); - void PrintTo(const Guid& guid, ::std::ostream* os); - void PrintTo(const SceneInfo&, ::std::ostream* os); - void PrintTo(const RamshInput&, ::std::ostream* os); -} - -namespace glm -{ - void PrintTo(const mat4& matrix, ::std::ostream* os); - void PrintTo(const vec3& value, ::std::ostream* os); - void PrintTo(const vec4& value, ::std::ostream* os); -} - - -#endif diff --git a/framework/FrameworkTestUtils/src/TestPngHeader.cpp b/framework/FrameworkTestUtils/src/TestPngHeader.cpp deleted file mode 100644 index b16df29d0..000000000 --- a/framework/FrameworkTestUtils/src/TestPngHeader.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "TestPngHeader.h" -#include - -namespace ramses_internal -{ - namespace TestPngHeader - { - std::vector GetValidHeader() - { - // 33 header bytes created via "xxd -i -l33 framework/test/res/sampleImage.png" - return { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, - 0x08, 0x02, 0x00, 0x00, 0x00, 0xfc, 0x18, 0xed, 0xa3 - }; - } - - std::vector GetValidHeaderWithFakeData() - { - auto vec = GetValidHeader(); - const auto dataStart = vec.size(); - vec.resize(2000); - std::iota(vec.begin() + dataStart, vec.end(), static_cast(10)); - return vec; - } - - std::vector GetInvalidHeader() - { - auto vec = GetValidHeader(); - vec[6] = 0; - return vec; - } - } -} diff --git a/framework/FrameworkTestUtils/src/framework_common_gmock_header.cpp b/framework/FrameworkTestUtils/src/framework_common_gmock_header.cpp deleted file mode 100644 index 5aa97d720..000000000 --- a/framework/FrameworkTestUtils/src/framework_common_gmock_header.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "framework_common_gmock_header.h" - -#include "gmock/gmock.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneId.h" -#include "Scene/SceneActionCollection.h" - -namespace glm -{ - void PrintTo(const mat4& matrix, ::std::ostream* os) - { - *os << "[" << matrix[0][0] << "," << matrix[1][0] << "," << matrix[2][0] << "," << matrix[3][0] << "]"; - *os << "[" << matrix[0][1] << "," << matrix[1][1] << "," << matrix[2][1] << "," << matrix[3][1] << "]"; - *os << "[" << matrix[0][2] << "," << matrix[1][2] << "," << matrix[2][2] << "," << matrix[3][2] << "]"; - *os << "[" << matrix[0][3] << "," << matrix[1][3] << "," << matrix[2][3] << "," << matrix[3][3] << "]"; - } - - void PrintTo(const vec3& value, ::std::ostream* os) - { - *os << "xyz(" << value.x; - *os << "," << value.y; - *os << "," << value.z << ")"; - } - - void PrintTo(const vec4& value, ::std::ostream* os) - { - *os << "xyzw(" << value.x; - *os << "," << value.y; - *os << "," << value.z; - *os << "," << value.w << ")"; - } -} - -namespace ramses_internal -{ - void PrintTo(const SceneActionCollection& actions, std::ostream* os) - { - *os << "SceneActionCollection numberOfActions=" << actions.numberOfActions() << ", dataSize=" << actions.collectionData().size(); - } - - void PrintTo(const DataFieldHandle& field, ::std::ostream* os) - { - *os << field.asMemoryHandle(); - } - - void PrintTo(const Guid& guid, ::std::ostream* os) - { - *os << guid.toString(); - } - - void PrintTo(const SceneInfo& si, ::std::ostream* os) - { - *os << "ScenInfo(sceneId " << si.sceneID.getValue() << ", name " << si.friendlyName.c_str() << ")"; - } -} diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTypes.h b/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTypes.h deleted file mode 100644 index 396fe7665..000000000 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTypes.h +++ /dev/null @@ -1,22 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2009-2010 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_PLATFORMTYPES_H -#define RAMSES_PLATFORMTYPES_H - -#include - -#define UNUSED(x) ((void)(x)) - -namespace ramses_internal -{ - using Int = std::intptr_t; - using Byte = unsigned char; -} - -#endif diff --git a/framework/PlatformAbstraction/test/HashSetTest.cpp b/framework/PlatformAbstraction/test/HashSetTest.cpp deleted file mode 100644 index bd7b6df62..000000000 --- a/framework/PlatformAbstraction/test/HashSetTest.cpp +++ /dev/null @@ -1,395 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "Collections/HashSet.h" -#include "ComplexTestType.h" -#include -#include - -namespace ramses_internal -{ -using TestType = ComplexTestType<>; - -TEST(HashSet, Constructor_Default) -{ - HashSet set; - (void)set; -} - -TEST(HashSet, copyConstructor) -{ - HashSet set; - set.put(1); - set.put(2); - set.put(3); - - HashSet set2 = set; // copy - - set.clear(); - - EXPECT_TRUE(set2.contains(1)); - EXPECT_TRUE(set2.contains(2)); - EXPECT_TRUE(set2.contains(3)); -} - -TEST(HashSet, put) -{ - int32_t value2 = 10; - int32_t value = 5; - - HashSeth1; - - auto it1 = h1.put(value); - auto it2 = h1.put(value2); - auto it3 = h1.put(value2); - - EXPECT_NE(h1.end(), it1); - EXPECT_NE(h1.end(), it2); - EXPECT_NE(h1.end(), it3); - - EXPECT_EQ(h1.find(value), it1); - EXPECT_EQ(h1.find(value2), it2); - EXPECT_EQ(it2, it3); -} - -TEST(HashSet, count) -{ - int32_t value2 = 10; - int32_t value = 5; - HashSeth1; - - EXPECT_EQ(0u, h1.size()); - h1.put(value); - h1.put(value2); - - EXPECT_EQ(2u, h1.size()); - EXPECT_TRUE(h1.remove(value2)); - EXPECT_EQ(1u, h1.size()); -} - -TEST(HashSet, clear) -{ - int32_t value = 5; - int32_t value2 = 6; - - HashSeth1; - h1.put(value); - h1.put(value2); - - EXPECT_EQ(2u, h1.size()); - h1.clear(); - EXPECT_EQ(0u, h1.size()); -} - -TEST(HashSet, remove) -{ - int32_t value = 5; - int32_t value2 = 6; - - HashSet h1; - - h1.put(value); - EXPECT_FALSE(h1.remove(value2)); - - h1.put(value2); - EXPECT_EQ(2u, h1.size()); - - EXPECT_TRUE(h1.remove(value2)); - EXPECT_EQ(1u, h1.size()); -} - -TEST(HashSet, hasElement) -{ - int32_t value = 5; - int32_t value2 = 6; - - HashSet h1; - - h1.put(value); - EXPECT_FALSE(h1.contains(value2)); - - h1.put(value2); - EXPECT_TRUE(h1.contains(value2)); -} - -TEST(HashSet, findsContainedElement) -{ - HashSet container; - container.put(5); - container.put(6); - container.put(7); - - ASSERT_NE(container.end(), container.find(5)); - ASSERT_NE(container.end(), container.find(6)); - ASSERT_NE(container.end(), container.find(7)); - EXPECT_EQ(5, *container.find(5)); - EXPECT_EQ(6, *container.find(6)); - EXPECT_EQ(7, *container.find(7)); - - // const access - const HashSet& ccontainer = container; - ASSERT_NE(ccontainer.end(), ccontainer.find(5)); - ASSERT_NE(ccontainer.end(), ccontainer.find(6)); - ASSERT_NE(ccontainer.end(), ccontainer.find(7)); - EXPECT_EQ(5, *ccontainer.find(5)); - EXPECT_EQ(6, *ccontainer.find(6)); - EXPECT_EQ(7, *ccontainer.find(7)); -} - -TEST(HashSet, doesNotFindElementNotContained) -{ - HashSet container; - container.put(5); - container.put(6); - container.put(7); - - EXPECT_EQ(container.end(), container.find(55)); - EXPECT_EQ(container.end(), container.find(99)); - - // const access - const HashSet& ccontainer = container; - EXPECT_EQ(ccontainer.end(), ccontainer.find(55)); - EXPECT_EQ(ccontainer.end(), ccontainer.find(99)); -} - -TEST(HashSetIterator, hasNext) -{ - int32_t value = 10; - int32_t value2 = 12; - - HashSet h1; - HashSet::Iterator it = h1.begin(); - - EXPECT_EQ(it, h1.end()); - - h1.put(value); - h1.put(value2); - - it = h1.begin(); - EXPECT_NE(it, h1.end()); -} - -TEST(HashSetIterator, next) -{ - int32_t value = 10; - int32_t value2 = 12; - - HashSet h1; - - int32_t check_value = 0; - int32_t check_value2 = 0; - - HashSet::Iterator it = h1.begin(); - - EXPECT_TRUE(it == h1.end()); - h1.put(value); - h1.put(value2); - - it = h1.begin(); - check_value = *it; - EXPECT_TRUE(check_value == value || check_value == value2); - - it++; - check_value = *it; - EXPECT_TRUE(check_value == value || check_value == value2); - - EXPECT_NE(check_value, check_value2); -} - -TEST(HashSetIterator, ForEach) -{ - HashSet hashSet; - - hashSet.put(32); - hashSet.put(43); - hashSet.put(44); - - HashSet testHashSet; - for (auto el : hashSet) - { - testHashSet.put(el); - } - - EXPECT_TRUE(testHashSet.contains(32)); - EXPECT_TRUE(testHashSet.contains(43)); - EXPECT_TRUE(testHashSet.contains(44)); - -} - -TEST(HashSet, returnedIteratorPointsToNextElementAfterDeletion) -{ - HashSet set; - - set.put(1); - set.put(2); - set.put(3); - - // point to middle element - HashSet::Iterator i1 = set.begin(); - HashSet::Iterator i2 = set.begin(); - ++i2; - HashSet::Iterator i3 = set.begin(); - ++i3; - ++i3; - - // all iterators point to different elements - // no assumption which value each iterator points to, because no order is defined - ASSERT_NE(*i1, *i2); - ASSERT_NE(*i2, *i3); - ASSERT_NE(*i1, *i3); - - i2 = set.remove(i2); - - // i2 now points at next element -> i3 - EXPECT_EQ(i2, i3); - - i1 = set.remove(i1); - EXPECT_EQ(i1, i3); -} - -TEST(HashSet, canRemoveElementsDuringCycle) -{ - HashSet set; - - set.put(1); - set.put(2); - set.put(3); - - HashSet::Iterator iter = set.begin(); - while (iter != set.end()) - { - iter = set.remove(iter); - } - - EXPECT_EQ(0u, set.size()); -} - -TEST(HashSet, HashSetWithComplexType) -{ - TestType::Reset(); - { - HashSet s; - std::vector v; - for (size_t i = 0; i < 20; ++i) - { - TestType ctt(i*10); - v.push_back(ctt); - s.put(ctt); - } - - EXPECT_EQ(v.size(), s.size()); - for (const auto& ctt : v) - { - EXPECT_TRUE(s.contains(ctt)); - } - } - EXPECT_EQ(TestType::dtor_count, TestType::ctor_count + TestType::copyctor_count); -} - -TEST(HashSet, swapMemberFunction) -{ - HashSet first; - HashSet second; - - first.put(1); - first.put(2); - second.put(3); - - first.swap(second); - EXPECT_EQ(2u, second.size()); - EXPECT_EQ(1u, first.size()); - - EXPECT_TRUE(second.contains(1)); - EXPECT_TRUE(second.contains(2)); - EXPECT_TRUE(first.contains(3)); -} - -TEST(HashSet, swapGlobal) -{ - HashSet first; - HashSet second; - - first.put(1); - first.put(2); - second.put(3); - - using std::swap; - swap(first, second); - EXPECT_EQ(2u, second.size()); - EXPECT_EQ(1u, first.size()); -} - -TEST(HashSet, TestMoveConstructor) -{ - HashSet set1; - set1.put(1); - set1.put(2); - set1.put(3); - - HashSet set2(std::move(set1)); - - EXPECT_EQ(3u, set2.size()); - EXPECT_TRUE(set2.contains(1)); - EXPECT_TRUE(set2.contains(2)); - EXPECT_TRUE(set2.contains(3)); -} - -TEST(HashSet, TestAssign) -{ - HashSet set1; - set1.put(1); - set1.put(2); - set1.put(3); - - HashSet set2; - set2 = set1; - - EXPECT_EQ(3u, set1.size()); - EXPECT_TRUE(set1.contains(1)); - EXPECT_TRUE(set1.contains(2)); - EXPECT_TRUE(set1.contains(3)); - - EXPECT_EQ(3u, set2.size()); - EXPECT_TRUE(set2.contains(1)); - EXPECT_TRUE(set2.contains(2)); - EXPECT_TRUE(set2.contains(3)); -} - - -TEST(HashSet, TestMoveAssign) -{ - HashSet set1; - set1.put(1); - set1.put(2); - set1.put(3); - - HashSet set2; - set2 = std::move(set1); - - EXPECT_EQ(3u, set2.size()); - EXPECT_TRUE(set2.contains(1)); - EXPECT_TRUE(set2.contains(2)); - EXPECT_TRUE(set2.contains(3)); -} - -TEST(HashSet, TestReserve) -{ - HashSet set(40); - EXPECT_GE(set.capacity(), 40u); - EXPECT_LE(set.capacity(), 2 * 40u); - - set.reserve(100); - EXPECT_GE(set.capacity(), 100u); - EXPECT_LE(set.capacity(), 2 * 100u); - - const size_t old = set.capacity(); - set.reserve(90); - EXPECT_EQ(old, set.capacity()); -} - -} diff --git a/framework/PlatformAbstraction/test/NumericLimitsTest.cpp b/framework/PlatformAbstraction/test/NumericLimitsTest.cpp deleted file mode 100644 index 39aad2867..000000000 --- a/framework/PlatformAbstraction/test/NumericLimitsTest.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "PlatformAbstraction/PlatformTypes.h" -#include "gtest/gtest.h" -#include - -TEST(NumericLimits, ensureAvailableForAlltypes) -{ - (void)std::numeric_limits::max(); - (void)std::numeric_limits::max(); - - (void)std::numeric_limits::max(); - (void)std::numeric_limits::max(); - - (void)std::numeric_limits::max(); - (void)std::numeric_limits::max(); - - (void)std::numeric_limits::max(); - (void)std::numeric_limits::max(); - - (void)std::numeric_limits::max(); - (void)std::numeric_limits::max(); - - (void)std::numeric_limits::max(); - (void)std::numeric_limits::max(); -} - -TEST(NumericLimits, testLimits) -{ - EXPECT_EQ(std::numeric_limits::max(), static_cast(0x7fffffffffffffffLL)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(0x8000000000000000LL)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(0xffffffffffffffffULL)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(0x0)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(0x7fffffff)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(0x80000000)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(0xffffffff)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(0x0)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(32767)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(-32768)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(65535)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(0)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(127)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(-128)); - EXPECT_EQ(std::numeric_limits::max(), static_cast(255)); - EXPECT_EQ(std::numeric_limits::min(), static_cast(0)); -} diff --git a/framework/PlatformAbstraction/test/PlatformStringUtilsTest.cpp b/framework/PlatformAbstraction/test/PlatformStringUtilsTest.cpp deleted file mode 100644 index 56ae38923..000000000 --- a/framework/PlatformAbstraction/test/PlatformStringUtilsTest.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "PlatformAbstraction/PlatformStringUtils.h" -#include "gtest/gtest.h" - -namespace ramses_internal -{ -TEST(APlatformStringUtils, CopyWithEnoughBuffer) -{ - char string1[20] = "My String"; - char string2[30]; - PlatformStringUtils::Copy(string2, sizeof(string2), string1); - EXPECT_STREQ(string1, string2); -} - -TEST(APlatformStringUtils, CopyWithTruncation) -{ - char string1[20] = "My String"; - char string2[20]; - std::memset(string2, 42, sizeof(string2)); - PlatformStringUtils::Copy(string2, 4, string1); - EXPECT_STREQ("My ", string2); - - // check that only 4 bytes are written, an no more - for (size_t i = 4; i < sizeof(string2); ++i) - EXPECT_EQ(42, string2[i]); -} -} diff --git a/framework/SceneGraph/Resource/include/Resource/EResourceCompressionStatus.h b/framework/SceneGraph/Resource/include/Resource/EResourceCompressionStatus.h deleted file mode 100644 index 04b0e1ae4..000000000 --- a/framework/SceneGraph/Resource/include/Resource/EResourceCompressionStatus.h +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_ERESOURCECOMPRESSIONSTATUS_H -#define RAMSES_ERESOURCECOMPRESSIONSTATUS_H - -namespace ramses_internal -{ - enum EResourceCompressionStatus //enum to support multiple compression formats in the future - { - EResourceCompressionStatus_Uncompressed = 0, - EResourceCompressionStatus_Compressed - }; -} - -#endif // RAMSES_ERESOURCECOMPRESSIONSTATUS_H diff --git a/framework/SceneGraph/Resource/include/Resource/ResourceTypes.h b/framework/SceneGraph/Resource/include/Resource/ResourceTypes.h deleted file mode 100644 index 60cf58d23..000000000 --- a/framework/SceneGraph/Resource/include/Resource/ResourceTypes.h +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RESOURCE_RESOURCETYPES_H -#define RAMSES_RESOURCE_RESOURCETYPES_H - -#include "Collections/HeapArray.h" -#include "Common/StronglyTypedValue.h" -#include "Utils/LoggingUtils.h" - -namespace ramses_internal -{ - using ResourceBlob = HeapArray; - using CompressedResourceBlob = HeapArray; - - struct ResourceCacheFlagTag {}; - using ResourceCacheFlag = StronglyTypedValue(-1), ResourceCacheFlagTag>; - static const ResourceCacheFlag ResourceCacheFlag_DoNotCache(ResourceCacheFlag::Invalid()); - - enum EResourceType - { - EResourceType_Invalid = 0x00000000, - - EResourceType_VertexArray, - EResourceType_IndexArray, - EResourceType_Texture2D, - EResourceType_Texture3D, - EResourceType_TextureCube, - EResourceType_Effect, - EResourceType_NUMBER_OF_ELEMENTS - }; - - const std::array ResourceTypeNames = - { - "EResourceType_Invalid", - "EResourceType_VertexArray", - "EResourceType_IndexArray", - "EResourceType_Texture2D", - "EResourceType_Texture3D", - "EResourceType_TextureCube", - "EResourceType_Effect" - }; - - ENUM_TO_STRING(EResourceType, ResourceTypeNames, EResourceType_NUMBER_OF_ELEMENTS); -} - -#endif diff --git a/framework/SceneGraph/Resource/test/ArrayResourceTest.cpp b/framework/SceneGraph/Resource/test/ArrayResourceTest.cpp deleted file mode 100644 index 102dc837b..000000000 --- a/framework/SceneGraph/Resource/test/ArrayResourceTest.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "framework_common_gmock_header.h" -#include "gtest/gtest.h" -#include "Resource/ArrayResource.h" - -namespace ramses_internal -{ - TEST(AArrayResource, hasCorrectArrayType) - { - ArrayResource vertArray(EResourceType_VertexArray, 5, EDataType::Vector3F, nullptr, ResourceCacheFlag(0u), {}); - ArrayResource indArray(EResourceType_IndexArray, 5, EDataType::Vector3F, nullptr, ResourceCacheFlag(0u), {}); - - EXPECT_EQ(EResourceType_VertexArray, vertArray.getTypeID()); - EXPECT_EQ(EResourceType_IndexArray, indArray.getTypeID()); - } - - TEST(AArrayResource, hasCorrectElementCount) - { - ArrayResource vertArray(EResourceType_VertexArray, 5, EDataType::Vector3F, nullptr, ResourceCacheFlag(0u), {}); - - EXPECT_EQ(5u, vertArray.getElementCount()); - } - - TEST(AArrayResource, hasCorrectElementType) - { - ArrayResource vertArray(EResourceType_VertexArray, 5, EDataType::Vector3F, nullptr, ResourceCacheFlag(0u), {}); - - EXPECT_EQ(EDataType::Vector3F, vertArray.getElementType()); - } - - TEST(AArrayResource, hasCorrectCacheFlag) - { - const ResourceCacheFlag flag(15u); - ArrayResource vertArray(EResourceType_VertexArray, 5, EDataType::Vector3F, nullptr, flag, {}); - EXPECT_EQ(flag, vertArray.getCacheFlag()); - } -} diff --git a/framework/SceneGraph/Scene/include/Scene/EScenePublicationMode.h b/framework/SceneGraph/Scene/include/Scene/EScenePublicationMode.h deleted file mode 100644 index 34cbf7c4c..000000000 --- a/framework/SceneGraph/Scene/include/Scene/EScenePublicationMode.h +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENE_ESCENEPUBLICATIONMODE_H -#define RAMSES_SCENE_ESCENEPUBLICATIONMODE_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include - -namespace ramses_internal -{ - /** - * Specifies the mode of scene publication. - */ - enum EScenePublicationMode - { - EScenePublicationMode_Unpublished = 0, - EScenePublicationMode_LocalAndRemote, - EScenePublicationMode_LocalOnly - }; - - inline const char* EnumToString(EScenePublicationMode publicationMode) - { - switch (publicationMode) - { - case EScenePublicationMode_Unpublished: - return "EScenePublicationMode_Unpublished"; - case EScenePublicationMode_LocalAndRemote: - return "EScenePublicationMode_LocalAndRemote"; - case EScenePublicationMode_LocalOnly: - return "EScenePublicationMode_LocalOnly"; - default: - assert(false); - break; - } - - return "EScenePublicationMode UNKNOWN"; - } -} - -#endif diff --git a/framework/SceneGraph/Scene/test/SceneTest_IteratableMemoryPools.cpp b/framework/SceneGraph/Scene/test/SceneTest_IteratableMemoryPools.cpp deleted file mode 100644 index cb501a747..000000000 --- a/framework/SceneGraph/Scene/test/SceneTest_IteratableMemoryPools.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "framework_common_gmock_header.h" -#include "SceneAPI/RenderState.h" -#include "SceneAPI/TextureSampler.h" -#include "gtest/gtest.h" -#include "ActionTestScene.h" -#include "Scene/ResourceChangeCollectingScene.h" -#include "Scene/DataLayoutCachedScene.h" -#include - -using namespace testing; - -namespace ramses_internal -{ - using IteratableSceneTypes = ::testing::Types< - Scene, - TransformationCachedScene, - ActionCollectingScene, - ResourceChangeCollectingScene - >; - - template - class AnIteratableScene : public testing::Test - { - protected: - SCENE m_scene; - - template - void runTest( - std::function allocateHandleF, - std::function releaseHandleF, - const MemPoolT& memPool) - { - using HandleT = typename MemPoolT::handle_type; - - const HandleT handle1 = allocateHandleF(); - const HandleT handle2 = allocateHandleF(); - const HandleT handle3 = allocateHandleF(); - const HandleT handle4 = allocateHandleF(); - const HandleT handle5 = allocateHandleF(); - const HandleT handle6 = allocateHandleF(); - - releaseHandleF(handle3); - const HandleT handle7 = allocateHandleF(); - releaseHandleF(handle1); - const HandleT handle8 = allocateHandleF(); - releaseHandleF(handle4); - releaseHandleF(handle5); - - ASSERT_EQ(HandleT{ 1u }, handle2); - ASSERT_EQ(HandleT{ 5u }, handle6); - ASSERT_EQ(HandleT{ 2u }, handle7); - ASSERT_EQ(HandleT{ 0u }, handle8); - - std::vector handles; - for (const auto& it : memPool) - { - handles.push_back(it.first); - } - - std::vector exepctedHandles{ handle8, handle2, handle7, handle6 }; - EXPECT_EQ(handles, exepctedHandles); - } - }; - - TYPED_TEST_SUITE(AnIteratableScene, IteratableSceneTypes); - - TYPED_TEST(AnIteratableScene, CanIterateOverRenderables) - { - auto allocateF = std::bind(&TypeParam::allocateRenderable, &this->m_scene, NodeHandle{}, RenderableHandle{}); - auto releaseF = std::bind(&TypeParam::releaseRenderable, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getRenderables()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverRenderStates) - { - auto allocateF = std::bind(&TypeParam::allocateRenderState, &this->m_scene, RenderStateHandle{}); - auto releaseF = std::bind(&TypeParam::releaseRenderState, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getRenderStates()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverCameras) - { - auto allocateF = std::bind(&TypeParam::allocateCamera, &this->m_scene, ECameraProjectionType::Orthographic, NodeHandle{}, DataInstanceHandle{}, CameraHandle{}); - auto releaseF = std::bind(&TypeParam::releaseCamera, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getCameras()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverNodes) - { - auto allocateF = std::bind(&TypeParam::allocateNode, &this->m_scene, 0u, NodeHandle{}); - auto releaseF = std::bind(&TypeParam::releaseNode, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getNodes()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverTransforms) - { - const auto nodeHandle = this->m_scene.allocateNode(); - - auto allocateF = std::bind(&TypeParam::allocateTransform, &this->m_scene, nodeHandle, TransformHandle{}); - auto releaseF = std::bind(&TypeParam::releaseTransform, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getTransforms()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverDataLayouts) - { - auto allocateF = [&]() { - static uint64_t resourceHash = 1u; - return this->m_scene.allocateDataLayout(DataFieldInfoVector{}, ResourceContentHash{ resourceHash++, 0u }); - }; - - auto releaseF = std::bind(&TypeParam::releaseDataLayout, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getDataLayouts()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverDataInstances) - { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u)); - - auto allocateF = std::bind(&TypeParam::allocateDataInstance, &this->m_scene, dataLayout, DataInstanceHandle{}); - auto releaseF = std::bind(&TypeParam::releaseDataInstance, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getDataInstances()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverTextureSamplers) - { - const TextureSampler sampler({}, ResourceContentHash{ 123u, 0u }); - - auto allocateF = std::bind(&TypeParam::allocateTextureSampler, &this->m_scene, sampler, TextureSamplerHandle{}); - auto releaseF = std::bind(&TypeParam::releaseTextureSampler, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getTextureSamplers()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverRenderGroups) - { - auto allocateF = std::bind(&TypeParam::allocateRenderGroup, &this->m_scene, 0u, 0u, RenderGroupHandle{}); - auto releaseF = std::bind(&TypeParam::releaseRenderGroup, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getRenderGroups()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverRenderPasses) - { - auto allocateF = std::bind(&TypeParam::allocateRenderPass, &this->m_scene, 0u, RenderPassHandle{}); - auto releaseF = std::bind(&TypeParam::releaseRenderPass, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getRenderPasses()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverBlitPasses) - { - const auto srcRenderBufferHandle = this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }); - const auto dstRenderBufferHandle = this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }); - - auto allocateF = std::bind(&TypeParam::allocateBlitPass, &this->m_scene, srcRenderBufferHandle, dstRenderBufferHandle, BlitPassHandle{}); - auto releaseF = std::bind(&TypeParam::releaseBlitPass, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getBlitPasses()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverPickableObjects) - { - auto allocateF = std::bind(&TypeParam::allocatePickableObject, &this->m_scene, DataBufferHandle{}, NodeHandle{}, PickableObjectId{ 1u }, PickableObjectHandle{}); - auto releaseF = std::bind(&TypeParam::releasePickableObject, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getPickableObjects()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverRenderTargets) - { - auto allocateF = std::bind(&TypeParam::allocateRenderTarget, &this->m_scene, RenderTargetHandle{}); - auto releaseF = std::bind(&TypeParam::releaseRenderTarget, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getRenderTargets()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverRenderBuffers) - { - const RenderBuffer renderBuffer{ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }; - auto allocateF = std::bind(&TypeParam::allocateRenderBuffer, &this->m_scene, renderBuffer, RenderBufferHandle{}); - auto releaseF = std::bind(&TypeParam::releaseRenderBuffer, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getRenderBuffers()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverDataBuffers) - { - auto allocateF = std::bind(&TypeParam::allocateDataBuffer, &this->m_scene, EDataBufferType::IndexBuffer, EDataType::Int32, 4u, DataBufferHandle{}); - auto releaseF = std::bind(&TypeParam::releaseDataBuffer, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getDataBuffers()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverTextureBuffers) - { - auto allocateF = std::bind(&TypeParam::allocateTextureBuffer, &this->m_scene, ETextureFormat::R16F, MipMapDimensions{ {1, 1} }, TextureBufferHandle{}); - auto releaseF = std::bind(&TypeParam::releaseTextureBuffer, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getTextureBuffers()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverDataSlots) - { - const DataSlot dataSlot{ EDataSlotType_TransformationConsumer, DataSlotId{6u}, NodeHandle{5u}, DataInstanceHandle{1u}, ResourceContentHash{1u, 0u}, TextureSamplerHandle{1u} }; - - auto allocateF = std::bind(&TypeParam::allocateDataSlot, &this->m_scene, dataSlot, DataSlotHandle{}); - auto releaseF = std::bind(&TypeParam::releaseDataSlot, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getDataSlots()); - } - - TYPED_TEST(AnIteratableScene, CanIterateOverSceneReferences) - { - auto allocateF = std::bind(&TypeParam::allocateSceneReference, &this->m_scene, SceneId{123u}, SceneReferenceHandle{}); - auto releaseF = std::bind(&TypeParam::releaseSceneReference, &this->m_scene, std::placeholders::_1); - this->runTest(allocateF, releaseF, this->m_scene.getSceneReferences()); - } -} diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataSlotType.h b/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataSlotType.h deleted file mode 100644 index b0b1b86b3..000000000 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataSlotType.h +++ /dev/null @@ -1,43 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EDATASLOTTYPE_H -#define RAMSES_EDATASLOTTYPE_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LoggingUtils.h" - -namespace ramses_internal -{ - enum EDataSlotType - { - EDataSlotType_TransformationProvider = 0, - EDataSlotType_TransformationConsumer, - EDataSlotType_DataProvider, - EDataSlotType_DataConsumer, - EDataSlotType_TextureProvider, - EDataSlotType_TextureConsumer, - EDataSlotType_Undefined, - EDataSlotType_NUMBER_OF_ELEMENTS - }; - - const std::array DataSlotTypeNames = - { - "EDataSlotType_TransformationProvider", - "EDataSlotType_TransformationConsumer", - "EDataSlotType_DataProvider", - "EDataSlotType_DataConsumer", - "EDataSlotType_TextureProvider", - "EDataSlotType_TextureConsumer", - "EDataSlotType_Undefined" - }; - - ENUM_TO_STRING(EDataSlotType, DataSlotTypeNames, EDataSlotType_NUMBER_OF_ELEMENTS); -} - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/IScene.h b/framework/SceneGraph/SceneAPI/include/SceneAPI/IScene.h deleted file mode 100644 index 55662f4da..000000000 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/IScene.h +++ /dev/null @@ -1,317 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_ISCENE_H -#define RAMSES_ISCENE_H - -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/EDataBufferType.h" -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/TextureEnums.h" -#include "SceneAPI/ERenderableDataSlotType.h" -#include "SceneAPI/ECameraProjectionType.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/DataFieldInfo.h" -#include "SceneAPI/MipMapSize.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/RendererSceneState.h" -#include "SceneAPI/ERotationType.h" - -#include "Collections/HashMap.h" -#include "Collections/Vector.h" -#include "Components/FlushTimeInformation.h" -#include "DataTypesImpl.h" - -#include - -namespace ramses_internal -{ - class DataLayout; - struct SceneSizeInformation; - struct PixelRectangle; - struct Camera; - struct RenderPass; - struct RenderGroup; - struct TextureSampler; - struct TextureSamplerStates; - struct TextureBuffer; - struct GeometryDataBuffer; - struct RenderBuffer; - struct BlitPass; - struct PickableObject; - struct SceneReference; - struct TopologyTransform; - - class IScene - { - public: - IScene() = default; - virtual ~IScene() = default; - - // scene should never be copied or moved - IScene(const IScene&) = delete; - IScene& operator=(const IScene&) = delete; - IScene(IScene&&) = delete; - IScene& operator=(IScene&&) = delete; - - [[nodiscard]] virtual const std::string& getName () const = 0; - [[nodiscard]] virtual SceneId getSceneId () const = 0; - - virtual void setEffectTimeSync(FlushTime::Clock::time_point t) = 0; - [[nodiscard]] virtual FlushTime::Clock::time_point getEffectTimeSync() const = 0; - - virtual void preallocateSceneSize (const SceneSizeInformation& sizeInfo) = 0; - - // Renderable - virtual RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle = RenderableHandle::Invalid()) = 0; - virtual void releaseRenderable (RenderableHandle renderableHandle) = 0; - [[nodiscard]] virtual bool isRenderableAllocated (RenderableHandle renderableHandle) const = 0; - [[nodiscard]] virtual uint32_t getRenderableCount () const = 0; - virtual void setRenderableDataInstance (RenderableHandle renderableHandle, ERenderableDataSlotType slot, DataInstanceHandle newDataInstance) = 0; - virtual void setRenderableStartIndex (RenderableHandle renderableHandle, uint32_t startIndex) = 0; - virtual void setRenderableIndexCount (RenderableHandle renderableHandle, uint32_t indexCount) = 0; - virtual void setRenderableRenderState (RenderableHandle renderableHandle, RenderStateHandle stateHandle) = 0; - virtual void setRenderableVisibility (RenderableHandle renderableHandle, EVisibilityMode visibility) = 0; - virtual void setRenderableInstanceCount (RenderableHandle renderableHandle, uint32_t instanceCount) = 0; - virtual void setRenderableStartVertex (RenderableHandle renderableHandle, uint32_t startVertex) = 0; - [[nodiscard]] virtual const Renderable& getRenderable (RenderableHandle renderableHandle) const = 0; - - // Render state - virtual RenderStateHandle allocateRenderState (RenderStateHandle stateHandle = RenderStateHandle::Invalid()) = 0; - virtual void releaseRenderState (RenderStateHandle stateHandle) = 0; - [[nodiscard]] virtual bool isRenderStateAllocated (RenderStateHandle stateHandle) const = 0; - [[nodiscard]] virtual uint32_t getRenderStateCount () const = 0; - virtual void setRenderStateBlendFactors (RenderStateHandle stateHandle, EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) = 0; - virtual void setRenderStateBlendOperations (RenderStateHandle stateHandle, EBlendOperation operationColor, EBlendOperation operationAlpha) = 0; - virtual void setRenderStateBlendColor (RenderStateHandle stateHandle, const glm::vec4& color) = 0; - virtual void setRenderStateCullMode (RenderStateHandle stateHandle, ECullMode cullMode) = 0; - virtual void setRenderStateDrawMode (RenderStateHandle stateHandle, EDrawMode drawMode) = 0; - virtual void setRenderStateDepthFunc (RenderStateHandle stateHandle, EDepthFunc func) = 0; - virtual void setRenderStateDepthWrite (RenderStateHandle stateHandle, EDepthWrite flag) = 0; - virtual void setRenderStateScissorTest (RenderStateHandle stateHandle, EScissorTest flag, const RenderState::ScissorRegion& region) = 0; - virtual void setRenderStateStencilFunc (RenderStateHandle stateHandle, EStencilFunc func, uint8_t ref, uint8_t mask) = 0; - virtual void setRenderStateStencilOps (RenderStateHandle stateHandle, EStencilOp sfail, EStencilOp dpfail, EStencilOp dppass) = 0; - virtual void setRenderStateColorWriteMask (RenderStateHandle stateHandle, ColorWriteMask colorMask) = 0; - [[nodiscard]] virtual const RenderState& getRenderState (RenderStateHandle stateHandle) const = 0; - - // Camera - virtual CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle = CameraHandle::Invalid()) = 0; - virtual void releaseCamera (CameraHandle cameraHandle) = 0; - [[nodiscard]] virtual bool isCameraAllocated (CameraHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getCameraCount () const = 0; - [[nodiscard]] virtual const Camera& getCamera (CameraHandle cameraHandle) const = 0; - - // Nodes - virtual NodeHandle allocateNode (uint32_t childrenCount = 0u, NodeHandle handle = NodeHandle::Invalid()) = 0; - virtual void releaseNode (NodeHandle nodeHandle) = 0; - [[nodiscard]] virtual bool isNodeAllocated (NodeHandle node) const = 0; - [[nodiscard]] virtual uint32_t getNodeCount () const = 0; - [[nodiscard]] virtual NodeHandle getParent (NodeHandle nodeHandle) const = 0; - virtual void addChildToNode (NodeHandle parent, NodeHandle child) = 0; - virtual void removeChildFromNode (NodeHandle parent, NodeHandle child) = 0; - [[nodiscard]] virtual uint32_t getChildCount (NodeHandle parent) const = 0; - [[nodiscard]] virtual NodeHandle getChild (NodeHandle parent, uint32_t childNumber) const = 0; - - // Transformation - constexpr static glm::vec3 IdentityTranslation = {0.f, 0.f, 0.f}; - constexpr static glm::vec4 IdentityRotation = {0.f, 0.f, 0.f, 1.f}; // zero rotation for both euler and quaternion - constexpr static glm::vec3 IdentityScaling = {1.f, 1.f, 1.f}; - - virtual TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle = TransformHandle::Invalid()) = 0; - virtual void releaseTransform (TransformHandle transform) = 0; - [[nodiscard]] virtual bool isTransformAllocated (TransformHandle transformHandle) const = 0; - [[nodiscard]] virtual uint32_t getTransformCount () const = 0; - [[nodiscard]] virtual NodeHandle getTransformNode (TransformHandle handle) const = 0; - [[nodiscard]] virtual const glm::vec3& getTranslation (TransformHandle handle) const = 0; - [[nodiscard]] virtual const glm::vec4& getRotation (TransformHandle handle) const = 0; - [[nodiscard]] virtual ERotationType getRotationType (TransformHandle handle) const = 0; - [[nodiscard]] virtual const glm::vec3& getScaling (TransformHandle handle) const = 0; - virtual void setTranslation (TransformHandle handle, const glm::vec3& translation) = 0; - virtual void setRotation (TransformHandle handle, const glm::vec4& rotation, ERotationType rotationType) = 0; - virtual void setScaling (TransformHandle handle, const glm::vec3& scaling) = 0; - - virtual DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle = DataLayoutHandle::Invalid()) = 0; - virtual void releaseDataLayout (DataLayoutHandle layoutHandle) = 0; - [[nodiscard]] virtual bool isDataLayoutAllocated (DataLayoutHandle layoutHandle) const = 0; - [[nodiscard]] virtual uint32_t getDataLayoutCount () const = 0; - - [[nodiscard]] virtual const DataLayout& getDataLayout (DataLayoutHandle layoutHandle) const = 0; - - virtual DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle = DataInstanceHandle::Invalid()) = 0; - virtual void releaseDataInstance (DataInstanceHandle containerHandle) = 0; - [[nodiscard]] virtual bool isDataInstanceAllocated (DataInstanceHandle containerHandle) const = 0; - [[nodiscard]] virtual uint32_t getDataInstanceCount () const = 0; - [[nodiscard]] virtual DataLayoutHandle getLayoutOfDataInstance (DataInstanceHandle containerHandle) const = 0; - - [[nodiscard]] virtual const float* getDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::vec2* getDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::vec3* getDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::vec4* getDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const int32_t* getDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::mat2* getDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::mat3* getDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::mat4* getDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::ivec2* getDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::ivec3* getDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::ivec4* getDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const ResourceField& getDataResource (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual TextureSamplerHandle getDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual DataInstanceHandle getDataReference (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - - virtual void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) = 0; - virtual void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) = 0; - virtual void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) = 0; - virtual void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) = 0; - virtual void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) = 0; - virtual void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) = 0; - virtual void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) = 0; - virtual void setDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec4* data) = 0; - virtual void setDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat2* data) = 0; - virtual void setDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat3* data) = 0; - virtual void setDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat4* data) = 0; - virtual void setDataResource (DataInstanceHandle containerHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) = 0; - virtual void setDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) = 0; - virtual void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) = 0; - - // get/setData*Array wrappers for elementCount == 1 - [[nodiscard]] virtual float getDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::vec2& getDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::vec3& getDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::vec4& getDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual int32_t getDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::mat2& getDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::mat3& getDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::mat4& getDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::ivec2& getDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::ivec3& getDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - [[nodiscard]] virtual const glm::ivec4& getDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; - - virtual void setDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field, float data) = 0; - virtual void setDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec2& data) = 0; - virtual void setDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec3& data) = 0; - virtual void setDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec4& data) = 0; - virtual void setDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) = 0; - virtual void setDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec2& data) = 0; - virtual void setDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec3& data) = 0; - virtual void setDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec4& data) = 0; - virtual void setDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat2& data) = 0; - virtual void setDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat3& data) = 0; - virtual void setDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat4& data) = 0; - - // Texture sampler description - virtual TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle = TextureSamplerHandle::Invalid()) = 0; - virtual void releaseTextureSampler (TextureSamplerHandle handle) = 0; - [[nodiscard]] virtual bool isTextureSamplerAllocated (TextureSamplerHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getTextureSamplerCount () const = 0; - [[nodiscard]] virtual const TextureSampler& getTextureSampler (TextureSamplerHandle handle) const = 0; - - // Render groups - virtual RenderGroupHandle allocateRenderGroup (uint32_t renderableCount = 0u, uint32_t nestedGroupCount = 0u, RenderGroupHandle groupHandle = RenderGroupHandle::Invalid()) = 0; - virtual void releaseRenderGroup (RenderGroupHandle groupHandle) = 0; - [[nodiscard]] virtual bool isRenderGroupAllocated (RenderGroupHandle groupHandle) const = 0; - [[nodiscard]] virtual uint32_t getRenderGroupCount () const = 0; - virtual void addRenderableToRenderGroup (RenderGroupHandle groupHandle, RenderableHandle renderableHandle, int32_t order) = 0; - virtual void removeRenderableFromRenderGroup (RenderGroupHandle groupHandle, RenderableHandle renderableHandle) = 0; - virtual void addRenderGroupToRenderGroup (RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild, int32_t order) = 0; - virtual void removeRenderGroupFromRenderGroup(RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild) = 0; - [[nodiscard]] virtual const RenderGroup& getRenderGroup (RenderGroupHandle groupHandle) const = 0; - - // Render passes - virtual RenderPassHandle allocateRenderPass (uint32_t renderGroupCount = 0u, RenderPassHandle passHandle = RenderPassHandle::Invalid()) = 0; - virtual void releaseRenderPass (RenderPassHandle passHandle) = 0; - [[nodiscard]] virtual bool isRenderPassAllocated (RenderPassHandle passHandle) const = 0; - [[nodiscard]] virtual uint32_t getRenderPassCount () const = 0; - virtual void setRenderPassClearColor (RenderPassHandle passHandle, const glm::vec4& clearColor) = 0; - virtual void setRenderPassClearFlag (RenderPassHandle passHandle, uint32_t clearFlag) = 0; - virtual void setRenderPassCamera (RenderPassHandle passHandle, CameraHandle cameraHandle) = 0; - virtual void setRenderPassRenderTarget (RenderPassHandle passHandle, RenderTargetHandle targetHandle) = 0; - virtual void setRenderPassRenderOrder (RenderPassHandle passHandle, int32_t renderOrder) = 0; - virtual void setRenderPassEnabled (RenderPassHandle passHandle, bool isEnabled) = 0; - virtual void setRenderPassRenderOnce (RenderPassHandle passHandle, bool enable) = 0; - virtual void retriggerRenderPassRenderOnce (RenderPassHandle passHandle) = 0; - virtual void addRenderGroupToRenderPass (RenderPassHandle passHandle, RenderGroupHandle groupHandle, int32_t order) = 0; - virtual void removeRenderGroupFromRenderPass (RenderPassHandle passHandle, RenderGroupHandle groupHandle) = 0; - [[nodiscard]] virtual const RenderPass& getRenderPass (RenderPassHandle passHandle) const = 0; - - //Blit pass - virtual BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) = 0; - virtual void releaseBlitPass (BlitPassHandle passHandle) = 0; - [[nodiscard]] virtual bool isBlitPassAllocated (BlitPassHandle passHandle) const = 0; - [[nodiscard]] virtual uint32_t getBlitPassCount () const = 0; - virtual void setBlitPassRenderOrder (BlitPassHandle passHandle, int32_t renderOrder) = 0; - virtual void setBlitPassEnabled (BlitPassHandle passHandle, bool isEnabled) = 0; - virtual void setBlitPassRegions (BlitPassHandle passHandle, const PixelRectangle& sourceRegion, const PixelRectangle& destinationRegion) = 0; - [[nodiscard]] virtual const BlitPass& getBlitPass (BlitPassHandle passHandle) const = 0; - - [[nodiscard]] virtual SceneSizeInformation getSceneSizeInformation() const = 0; - - //Pickable object - virtual PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle = PickableObjectHandle::Invalid()) = 0; - virtual void releasePickableObject (PickableObjectHandle pickableHandle) = 0; - [[nodiscard]] virtual bool isPickableObjectAllocated (PickableObjectHandle pickableHandle) const = 0; - [[nodiscard]] virtual uint32_t getPickableObjectCount () const = 0; - virtual void setPickableObjectId (PickableObjectHandle pickableHandle, PickableObjectId id) = 0; - virtual void setPickableObjectCamera (PickableObjectHandle pickableHandle, CameraHandle cameraHandle) = 0; - virtual void setPickableObjectEnabled(PickableObjectHandle pickableHandel, bool isEnabled) = 0; - [[nodiscard]] virtual const PickableObject& getPickableObject (PickableObjectHandle pickableHandle) const = 0; - - // Render targets - virtual RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle = RenderTargetHandle::Invalid()) = 0; - virtual void releaseRenderTarget (RenderTargetHandle targetHandle) = 0; - [[nodiscard]] virtual bool isRenderTargetAllocated (RenderTargetHandle targetHandle) const = 0; - [[nodiscard]] virtual uint32_t getRenderTargetCount () const = 0; - virtual void addRenderTargetRenderBuffer (RenderTargetHandle targetHandle, RenderBufferHandle bufferHandle) = 0; - [[nodiscard]] virtual uint32_t getRenderTargetRenderBufferCount(RenderTargetHandle targetHandle) const = 0; - [[nodiscard]] virtual RenderBufferHandle getRenderTargetRenderBuffer (RenderTargetHandle targetHandle, uint32_t bufferIndex) const = 0; - - // Render buffers - virtual RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle = RenderBufferHandle::Invalid()) = 0; - virtual void releaseRenderBuffer (RenderBufferHandle handle) = 0; - [[nodiscard]] virtual bool isRenderBufferAllocated (RenderBufferHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getRenderBufferCount () const = 0; - [[nodiscard]] virtual const RenderBuffer& getRenderBuffer (RenderBufferHandle handle) const = 0; - - // Data buffers - virtual DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle = DataBufferHandle::Invalid()) = 0; - virtual void releaseDataBuffer (DataBufferHandle handle) = 0; - [[nodiscard]] virtual bool isDataBufferAllocated (DataBufferHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getDataBufferCount () const = 0; - virtual void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) = 0; - [[nodiscard]] virtual const GeometryDataBuffer& getDataBuffer (DataBufferHandle handle) const = 0; - - //Texture buffers - virtual TextureBufferHandle allocateTextureBuffer (ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()) = 0; - virtual void releaseTextureBuffer (TextureBufferHandle handle) = 0; - [[nodiscard]] virtual bool isTextureBufferAllocated (TextureBufferHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getTextureBufferCount () const = 0; - virtual void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) = 0; - [[nodiscard]] virtual const TextureBuffer& getTextureBuffer (TextureBufferHandle handle) const = 0; - - // Data slots - virtual DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) = 0; - virtual void releaseDataSlot (DataSlotHandle handle) = 0; - virtual void setDataSlotTexture (DataSlotHandle handle, const ResourceContentHash& texture) = 0; - [[nodiscard]] virtual bool isDataSlotAllocated (DataSlotHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getDataSlotCount () const = 0; - [[nodiscard]] virtual const DataSlot& getDataSlot (DataSlotHandle handle) const = 0; - - // Scene references - virtual SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle = {}) = 0; - virtual void releaseSceneReference (SceneReferenceHandle handle) = 0; - virtual void requestSceneReferenceState (SceneReferenceHandle handle, RendererSceneState state) = 0; - virtual void requestSceneReferenceFlushNotifications(SceneReferenceHandle handle, bool enable) = 0; - virtual void setSceneReferenceRenderOrder (SceneReferenceHandle handle, int32_t renderOrder) = 0; - [[nodiscard]] virtual bool isSceneReferenceAllocated (SceneReferenceHandle handle) const = 0; - [[nodiscard]] virtual uint32_t getSceneReferenceCount () const = 0; - [[nodiscard]] virtual const SceneReference& getSceneReference (SceneReferenceHandle handle) const = 0; - }; -} - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureEnums.h b/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureEnums.h deleted file mode 100644 index 59d4712f2..000000000 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureEnums.h +++ /dev/null @@ -1,351 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_FRAMEWORK_TEXTUREENUMS_H -#define RAMSES_FRAMEWORK_TEXTUREENUMS_H - -#include -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LoggingUtils.h" - -namespace ramses_internal -{ - enum class ESamplingMethod : uint8_t - { - Nearest = 0, - Linear, - Nearest_MipMapNearest, - Nearest_MipMapLinear, - Linear_MipMapNearest, - Linear_MipMapLinear, - NUMBER_OF_ELEMENTS - }; - - const std::array SamplingMethodNames = - { - "ESamplingMethod_Nearest", - "ESamplingMethod_Linear", - "ESamplingMethod_Nearest_MipMapNearest", - "ESamplingMethod_Nearest_MipMapLinear", - "ESamplingMethod_Linear_MipMapNearest", - "ESamplingMethod_Linear_MipMapLinear", - }; - ENUM_TO_STRING(ESamplingMethod, SamplingMethodNames, ESamplingMethod::NUMBER_OF_ELEMENTS); - - enum class EWrapMethod : uint8_t - { - Clamp = 0, - Repeat, - RepeatMirrored, - NUMBER_OF_ELEMENTS - }; - - const std::array WrapMethodNames = - { - "EWrapMethod_Clamp", - "EWrapMethod_Repeat", - "EWrapMethod_RepeatMirrored" - }; - ENUM_TO_STRING(EWrapMethod, WrapMethodNames, EWrapMethod::NUMBER_OF_ELEMENTS); - - enum ERenderBufferType - { - ERenderBufferType_InvalidBuffer = 0, - ERenderBufferType_DepthBuffer, - ERenderBufferType_DepthStencilBuffer, - ERenderBufferType_ColorBuffer, - - ERenderBufferType_NUMBER_OF_ELEMENTS - }; - - const std::array RenderBufferTypeNames = - { - "ERenderBufferType_InvalidBuffer", - "ERenderBufferType_DepthBuffer", - "ERenderBufferType_DepthStencilBuffer", - "ERenderBufferType_ColorBuffer" - }; - ENUM_TO_STRING(ERenderBufferType, RenderBufferTypeNames, ERenderBufferType_NUMBER_OF_ELEMENTS); - - enum ERenderBufferAccessMode - { - ERenderBufferAccessMode_Invalid = 0, - ERenderBufferAccessMode_WriteOnly, - ERenderBufferAccessMode_ReadWrite, - - ERenderBufferAccessMode_NUMBER_OF_ELEMENTS - }; - - const std::array RenderBufferAccessModeNames = - { - "ERenderBufferAccessMode_Invalid", - "ERenderBufferAccessMode_WriteOnly", - "ERenderBufferAccessMode_ReadWrite" - }; - ENUM_TO_STRING(ERenderBufferAccessMode, RenderBufferAccessModeNames, ERenderBufferAccessMode_NUMBER_OF_ELEMENTS); - - enum class ETextureFormat - { - Invalid = 0, - - R8, - R16, - - RG8, - RG16, - - RGB16, - RGB8, - RGB565, - - RGBA16, - RGBA8, - RGBA4, - RGBA5551, - - ETC2RGB, // ericsson texture compression 2 - ETC2RGBA, // ericsson texture compression 2 with alpha - DXT1RGB, // S3 texture compression 1 - DXT3RGBA, // S3 texture compression 3 with alpha - DXT5RGBA, // S3 texture compression 5 with alpha - - Depth16, - Depth24, - Depth24_Stencil8, - - // Floating point formats - R16F, - R32F, - RG16F, - RG32F, - RGB16F, - RGB32F, - RGBA16F, - RGBA32F, - - // sRGB formats - SRGB8, - SRGB8_ALPHA8, - - // ASTC formats - ASTC_RGBA_4x4, - ASTC_RGBA_5x4, - ASTC_RGBA_5x5, - ASTC_RGBA_6x5, - ASTC_RGBA_6x6, - ASTC_RGBA_8x5, - ASTC_RGBA_8x6, - ASTC_RGBA_8x8, - ASTC_RGBA_10x5, - ASTC_RGBA_10x6, - ASTC_RGBA_10x8, - ASTC_RGBA_10x10, - ASTC_RGBA_12x10, - ASTC_RGBA_12x12, - ASTC_SRGBA_4x4, - ASTC_SRGBA_5x4, - ASTC_SRGBA_5x5, - ASTC_SRGBA_6x5, - ASTC_SRGBA_6x6, - ASTC_SRGBA_8x5, - ASTC_SRGBA_8x6, - ASTC_SRGBA_8x8, - ASTC_SRGBA_10x5, - ASTC_SRGBA_10x6, - ASTC_SRGBA_10x8, - ASTC_SRGBA_10x10, - ASTC_SRGBA_12x10, - ASTC_SRGBA_12x12, - - NUMBER_OF_TYPES - }; - - enum ETextureCubeFace - { - ETextureCubeFace_PositiveX = 0, - ETextureCubeFace_NegativeX, - ETextureCubeFace_PositiveY, - ETextureCubeFace_NegativeY, - ETextureCubeFace_PositiveZ, - ETextureCubeFace_NegativeZ - }; - - enum class ETextureChannelColor : uint8_t - { - Red, - Green, - Blue, - Alpha, - One, - Zero, - - NUMBER_OF_ELEMENTS, - }; - - const std::array TextureChannelColorNames = - { - "Red", - "Green", - "Blue", - "Alpha", - "One", - "Zero" - }; - ENUM_TO_STRING(ETextureChannelColor, TextureChannelColorNames, ETextureChannelColor::NUMBER_OF_ELEMENTS); - - static inline bool IsFormatCompressed(ramses_internal::ETextureFormat textureformat) - { - switch (textureformat) - { - case ramses_internal::ETextureFormat::ETC2RGB: - case ramses_internal::ETextureFormat::ETC2RGBA: - case ramses_internal::ETextureFormat::DXT1RGB: - case ramses_internal::ETextureFormat::DXT3RGBA: - case ramses_internal::ETextureFormat::DXT5RGBA: - case ramses_internal::ETextureFormat::ASTC_RGBA_4x4: - case ramses_internal::ETextureFormat::ASTC_RGBA_5x4: - case ramses_internal::ETextureFormat::ASTC_RGBA_5x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_6x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_6x6: - case ramses_internal::ETextureFormat::ASTC_RGBA_8x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_8x6: - case ramses_internal::ETextureFormat::ASTC_RGBA_8x8: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x5: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x6: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x8: - case ramses_internal::ETextureFormat::ASTC_RGBA_10x10: - case ramses_internal::ETextureFormat::ASTC_RGBA_12x10: - case ramses_internal::ETextureFormat::ASTC_RGBA_12x12: - case ramses_internal::ETextureFormat::ASTC_SRGBA_4x4: - case ramses_internal::ETextureFormat::ASTC_SRGBA_5x4: - case ramses_internal::ETextureFormat::ASTC_SRGBA_5x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_6x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_6x6: - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x6: - case ramses_internal::ETextureFormat::ASTC_SRGBA_8x8: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x5: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x6: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x8: - case ramses_internal::ETextureFormat::ASTC_SRGBA_10x10: - case ramses_internal::ETextureFormat::ASTC_SRGBA_12x10: - case ramses_internal::ETextureFormat::ASTC_SRGBA_12x12: - return true; - default: - return false; - } - } - - static inline uint32_t GetTexelSizeFromFormat(ETextureFormat texelFormat) - { - switch (texelFormat) - { - case ETextureFormat::R8: - return 1u; - case ETextureFormat::R16: - case ETextureFormat::R16F: - case ETextureFormat::RG8: - case ETextureFormat::RGB565: - case ETextureFormat::RGBA4: - case ETextureFormat::RGBA5551: - case ETextureFormat::Depth16: - return 2u; - case ETextureFormat::RGB8: - case ETextureFormat::Depth24: - case ETextureFormat::SRGB8: - return 3u; - case ETextureFormat::RG16: - case ETextureFormat::RG16F: - case ETextureFormat::RGBA8: - case ETextureFormat::Depth24_Stencil8: - case ETextureFormat::R32F: - case ETextureFormat::SRGB8_ALPHA8: - return 4u; - case ETextureFormat::RGB16: - case ETextureFormat::RGB16F: - return 6u; - case ETextureFormat::RG32F: - case ETextureFormat::RGBA16: - case ETextureFormat::RGBA16F: - return 8u; - case ETextureFormat::RGB32F: - return 12u; - case ETextureFormat::RGBA32F: - return 16u; - - default: - assert(false && "Unknown texel size"); - return 1u; - } - } - - const std::array TextureFormatNames = { - "ETextureFormat_Invalid", - "ETextureFormat_R8", - "ETextureFormat_R16", - "ETextureFormat_RG8", - "ETextureFormat_RG16", - "ETextureFormat_RGB16", - "ETextureFormat_RGB8", - "ETextureFormat_RGB565", - "ETextureFormat_RGBA16", - "ETextureFormat_RGBA8", - "ETextureFormat_RGBA4", - "ETextureFormat_RGBA5551", - "ETextureFormat_ETC2RGB", - "ETextureFormat_ETC2RGBA", - "ETextureFormat_DXT1RGB", - "ETextureFormat_DXT3RGBA", - "ETextureFormat_DXT5RGBA", - "ETextureFormat_Depth16", - "ETextureFormat_Depth24", - "ETextureFormat_Depth24_Stencil8", - "ETextureFormat_R16F", - "ETextureFormat_R32F", - "ETextureFormat_RG16F", - "ETextureFormat_RG32F", - "ETextureFormat_RGB16F", - "ETextureFormat_RGB32F", - "ETextureFormat_RGBA16F", - "ETextureFormat_RGBA32F", - "ETextureFormat_SRGB8", - "ETextureFormat_SRGB8_ALPHA8", - "ETextureFormat_ASTC_RGBA_4x4", - "ETextureFormat_ASTC_RGBA_5x4", - "ETextureFormat_ASTC_RGBA_5x5", - "ETextureFormat_ASTC_RGBA_6x5", - "ETextureFormat_ASTC_RGBA_6x6", - "ETextureFormat_ASTC_RGBA_8x5", - "ETextureFormat_ASTC_RGBA_8x6", - "ETextureFormat_ASTC_RGBA_8x8", - "ETextureFormat_ASTC_RGBA_10x5", - "ETextureFormat_ASTC_RGBA_10x6", - "ETextureFormat_ASTC_RGBA_10x8", - "ETextureFormat_ASTC_RGBA_10x10", - "ETextureFormat_ASTC_RGBA_12x10", - "ETextureFormat_ASTC_RGBA_12x12", - "ETextureFormat_ASTC_SRGBA_4x4", - "ETextureFormat_ASTC_SRGBA_5x4", - "ETextureFormat_ASTC_SRGBA_5x5", - "ETextureFormat_ASTC_SRGBA_6x5", - "ETextureFormat_ASTC_SRGBA_6x6", - "ETextureFormat_ASTC_SRGBA_8x5", - "ETextureFormat_ASTC_SRGBA_8x6", - "ETextureFormat_ASTC_SRGBA_8x8", - "ETextureFormat_ASTC_SRGBA_10x5", - "ETextureFormat_ASTC_SRGBA_10x6", - "ETextureFormat_ASTC_SRGBA_10x8", - "ETextureFormat_ASTC_SRGBA_10x10", - "ETextureFormat_ASTC_SRGBA_12x10", - "ETextureFormat_ASTC_SRGBA_12x12" - }; - - ENUM_TO_STRING(ETextureFormat, TextureFormatNames, ETextureFormat::NUMBER_OF_TYPES); -} - -#endif diff --git a/framework/SceneGraph/SceneAPI/test/TextureSamplerStatesHashTest.cpp b/framework/SceneGraph/SceneAPI/test/TextureSamplerStatesHashTest.cpp deleted file mode 100644 index 6c9a9f8be..000000000 --- a/framework/SceneGraph/SceneAPI/test/TextureSamplerStatesHashTest.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "SceneAPI/TextureSamplerStates.h" -#include "gtest/gtest.h" - -namespace ramses_internal -{ - class TextureSamplerStatesHash : public ::testing::Test - { - protected: - void expectSameHash(const TextureSamplerStates& state1, const TextureSamplerStates& state2) - { - const auto hash1 = state1.hash(); - const auto hash2 = state2.hash(); - EXPECT_EQ(hash1, hash2); - } - - void expectNotSameHash(const TextureSamplerStates& state1, const TextureSamplerStates& state2) - { - const auto hash1 = state1.hash(); - const auto hash2 = state2.hash(); - EXPECT_NE(hash1, hash2); - } - }; - - TEST_F(TextureSamplerStatesHash, SameStatesHaveSameHash) - { - { - const TextureSamplerStates samplerStates; - const TextureSamplerStates otherSamplerStates; - expectSameHash(samplerStates, otherSamplerStates); - } - - { - const TextureSamplerStates samplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - const TextureSamplerStates otherSamplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - expectSameHash(samplerStates, otherSamplerStates); - } - } - - TEST_F(TextureSamplerStatesHash, DifferentStatesHaveDifferentHash) - { - const TextureSamplerStates samplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - - //address mode U - { - const TextureSamplerStates otherSamplerStates(EWrapMethod::Repeat, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - expectNotSameHash(samplerStates, otherSamplerStates); - } - //address mode V - { - const TextureSamplerStates otherSamplerStates(EWrapMethod::Clamp, EWrapMethod::RepeatMirrored, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - expectNotSameHash(samplerStates, otherSamplerStates); - } - //address mode R - { - const TextureSamplerStates otherSamplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::Clamp, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - expectNotSameHash(samplerStates, otherSamplerStates); - } - //min sampling - { - const TextureSamplerStates otherSamplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Nearest_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 8u); - expectNotSameHash(samplerStates, otherSamplerStates); - } - //mag sampling - { - const TextureSamplerStates otherSamplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Linear, 8u); - expectNotSameHash(samplerStates, otherSamplerStates); - } - //anisotropy level - { - const TextureSamplerStates otherSamplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Nearest_MipMapNearest, 1u); - expectNotSameHash(samplerStates, otherSamplerStates); - } - } -} diff --git a/framework/Watchdog/test/PlatformWatchDogMock.h b/framework/Watchdog/test/PlatformWatchDogMock.h deleted file mode 100644 index 88a6e02d0..000000000 --- a/framework/Watchdog/test/PlatformWatchDogMock.h +++ /dev/null @@ -1,26 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_PLATFORMWATCHDOGMOCK_H -#define RAMSES_PLATFORMWATCHDOGMOCK_H - -#include "gmock/gmock.h" -#include "ramses-framework-api/IThreadWatchdogNotification.h" - -namespace ramses_internal -{ - class PlatformWatchdogMockCallback : public ramses::IThreadWatchdogNotification - { - public: - MOCK_METHOD(void, notifyThread, (ramses::ERamsesThreadIdentifier), (override)); - MOCK_METHOD(void, registerThread, (ramses::ERamsesThreadIdentifier), (override)); - MOCK_METHOD(void, unregisterThread, (ramses::ERamsesThreadIdentifier), (override)); - }; -} - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/StatusObject.h b/framework/ramses-framework-api/include/ramses-framework-api/StatusObject.h deleted file mode 100644 index 3c1c954f2..000000000 --- a/framework/ramses-framework-api/include/ramses-framework-api/StatusObject.h +++ /dev/null @@ -1,104 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_STATUSOBJECT_H -#define RAMSES_STATUSOBJECT_H - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/APIExport.h" -#include "EValidationSeverity.h" -#include - -namespace ramses -{ - class StatusObjectImpl; - - /** - * @brief The StatusObject provides status message handling - */ - class StatusObject - { - public: - /** - * @brief Generates verbose validation of the object. - * @details Checks validity of internal values, references, states and performance warnings. - * Will validate object it is called on and all its dependencies recursively, e.g. Scene->MeshNode->Appearance->Resource ... - * Result can be obtained by calling #getValidationReport. - * - * @return StatusOK if no issues found, otherwise an index to a status message - * which will be either "Validation warning" or "Validation error" depending on severity of the issues found. - * The status message can be obtained by calling #getStatusMessage. - */ - [[nodiscard]] RAMSES_API status_t validate() const; - - /** - * @brief Provides verbose report in human readable form generated by #validate. - * - * If \c minSeverity is set to #ramses::EValidationSeverity::Error, only objects with errors and their descriptions - * are returned, if filtering is set to #EValidationSeverity::Warning additionally all objects with warnings and - * their descriptions are returned. If filtering is set to #EValidationSeverity::Info all errors and warnings are printed - * and additionally some objects will print more information like number of instances or similar. - * - * @param[in] minSeverity Optional filter, only messages with greater or equal severity will be added to report - * @return Validation string containing human readable status of the object, - * if #validate was not called a pointer to an empty string is returned. - */ - [[nodiscard]] RAMSES_API const char* getValidationReport(EValidationSeverity minSeverity = EValidationSeverity::Info) const; - - /** - * @brief Get the string description for a status provided by a RAMSES API function - * - * @param status Status returned by a RAMSES API function call - * @return If value refers to an existing status message, the string with text description for status is returned. - * If no status message for value is available, unknown status message is returned. - */ - [[nodiscard]] RAMSES_API const char* getStatusMessage(status_t status) const; - - /** - * @brief Deleted copy constructor - */ - StatusObject(const StatusObject&) = delete; - - /** - * @brief Deleted move constructor - */ - StatusObject(StatusObject&&) = delete; - - /** - * @brief Deleted copy assignment - * @return unused - */ - StatusObject& operator=(const StatusObject&) = delete; - - /** - * @brief Deleted move assignment - * @return unused - */ - StatusObject& operator=(StatusObject&&) = delete; - - protected: - /** - * @brief Constructor for StatusObject. - * - * @param[in] impl Internal data for implementation specifics of StatusObject (sink - instance becomes owner) - */ - explicit StatusObject(std::unique_ptr impl); - - /** - * @brief Destructor of the StatusObject - */ - virtual ~StatusObject(); - - /** - * Stores internal data for implementation specifics of StatusObject. - */ - std::unique_ptr m_impl; - }; -} - -#endif diff --git a/framework/ramses-framework/include/DataTypeUtils.h b/framework/ramses-framework/include/DataTypeUtils.h deleted file mode 100644 index 57df1cce4..000000000 --- a/framework/ramses-framework/include/DataTypeUtils.h +++ /dev/null @@ -1,173 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_FRAMEWORK_DATATYPEUTILS_H -#define RAMSES_FRAMEWORK_DATATYPEUTILS_H - -#include "ramses-framework-api/EDataType.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EDataBufferType.h" -#include - -namespace ramses -{ - class DataTypeUtils - { - public: - static EDataType ConvertDataTypeFromInternal(ramses_internal::EDataType type) - { - switch (type) - { - case ramses_internal::EDataType::Int32: - return EDataType::Int32; - case ramses_internal::EDataType::UInt16: - return EDataType::UInt16; - case ramses_internal::EDataType::UInt32: - return EDataType::UInt32; - case ramses_internal::EDataType::Float: - return EDataType::Float; - case ramses_internal::EDataType::Vector2F: - return EDataType::Vector2F; - case ramses_internal::EDataType::Vector3F: - return EDataType::Vector3F; - case ramses_internal::EDataType::Vector4F: - return EDataType::Vector4F; - case ramses_internal::EDataType::Vector2I: - return EDataType::Vector2I; - case ramses_internal::EDataType::Vector3I: - return EDataType::Vector3I; - case ramses_internal::EDataType::Vector4I: - return EDataType::Vector4I; - case ramses_internal::EDataType::Matrix22F: - return EDataType::Matrix22F; - case ramses_internal::EDataType::Matrix33F: - return EDataType::Matrix33F; - case ramses_internal::EDataType::Matrix44F: - return EDataType::Matrix44F; - case ramses_internal::EDataType::ByteBlob: - return EDataType::ByteBlob; - - // internal attribure array types are converted back to their element type on public API - case ramses_internal::EDataType::UInt16Buffer: - return EDataType::UInt16; - case ramses_internal::EDataType::FloatBuffer: - return EDataType::Float; - case ramses_internal::EDataType::Vector2Buffer: - return EDataType::Vector2F; - case ramses_internal::EDataType::Vector3Buffer: - return EDataType::Vector3F; - case ramses_internal::EDataType::Vector4Buffer: - return EDataType::Vector4F; - - case ramses_internal::EDataType::TextureSampler2D: - return EDataType::TextureSampler2D; - case ramses_internal::EDataType::TextureSampler2DMS: - return EDataType::TextureSampler2DMS; - case ramses_internal::EDataType::TextureSampler3D: - return EDataType::TextureSampler3D; - case ramses_internal::EDataType::TextureSamplerCube: - return EDataType::TextureSamplerCube; - case ramses_internal::EDataType::TextureSamplerExternal: - return EDataType::TextureSamplerExternal; - - default: - assert(false); - return EDataType::ByteBlob; - } - } - - static constexpr ramses_internal::EDataType ConvertDataTypeToInternal(EDataType type) - { - switch (type) - { - case EDataType::Int32: - return ramses_internal::EDataType::Int32; - case EDataType::UInt16: - return ramses_internal::EDataType::UInt16; - case EDataType::UInt32: - return ramses_internal::EDataType::UInt32; - case EDataType::Float: - return ramses_internal::EDataType::Float; - case EDataType::Vector2F: - return ramses_internal::EDataType::Vector2F; - case EDataType::Vector3F: - return ramses_internal::EDataType::Vector3F; - case EDataType::Vector4F: - return ramses_internal::EDataType::Vector4F; - case EDataType::Vector2I: - return ramses_internal::EDataType::Vector2I; - case EDataType::Vector3I: - return ramses_internal::EDataType::Vector3I; - case EDataType::Vector4I: - return ramses_internal::EDataType::Vector4I; - case EDataType::Matrix22F: - return ramses_internal::EDataType::Matrix22F; - case EDataType::Matrix33F: - return ramses_internal::EDataType::Matrix33F; - case EDataType::Matrix44F: - return ramses_internal::EDataType::Matrix44F; - case EDataType::ByteBlob: - return ramses_internal::EDataType::ByteBlob; - case EDataType::TextureSampler2D: - return ramses_internal::EDataType::TextureSampler2D; - case EDataType::TextureSampler2DMS: - return ramses_internal::EDataType::TextureSampler2DMS; - case EDataType::TextureSampler3D: - return ramses_internal::EDataType::TextureSampler3D; - case EDataType::TextureSamplerCube: - return ramses_internal::EDataType::TextureSamplerCube; - case EDataType::TextureSamplerExternal: - return ramses_internal::EDataType::TextureSamplerExternal; - } - - assert(false); - return ramses_internal::EDataType::Invalid; - } - - static ramses_internal::EResourceType DeductResourceTypeFromDataType(EDataType type) - { - if (IsValidIndicesType(type)) - return ramses_internal::EResourceType_IndexArray; - else if (IsValidVerticesType(type)) - return ramses_internal::EResourceType_VertexArray; - - assert(false); - return ramses_internal::EResourceType_Invalid; - } - - static ramses_internal::EDataBufferType DeductBufferTypeFromDataType(EDataType type) - { - if (IsValidIndicesType(type)) - return ramses_internal::EDataBufferType::IndexBuffer; - else if (IsValidVerticesType(type)) - return ramses_internal::EDataBufferType::VertexBuffer; - - assert(false); - return ramses_internal::EDataBufferType::Invalid; - } - - static bool IsValidIndicesType(EDataType type) - { - return - type == EDataType::UInt16 || - type == EDataType::UInt32; - } - - static bool IsValidVerticesType(EDataType type) - { - return - type == EDataType::Float || - type == EDataType::Vector2F || - type == EDataType::Vector3F || - type == EDataType::Vector4F || - type == EDataType::ByteBlob; - } - }; -} - -#endif diff --git a/framework/ramses-framework/include/RamsesFrameworkConfigImpl.h b/framework/ramses-framework/include/RamsesFrameworkConfigImpl.h deleted file mode 100644 index f13a5e476..000000000 --- a/framework/ramses-framework/include/RamsesFrameworkConfigImpl.h +++ /dev/null @@ -1,87 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESFRAMEWORKCONFIGIMPL_H -#define RAMSES_RAMSESFRAMEWORKCONFIGIMPL_H - -#include "StatusObjectImpl.h" -#include "TCPConfig.h" -#include "Utils/RamsesLogger.h" -#include "ramses-framework-api/IThreadWatchdogNotification.h" -#include "ramses-framework-api/EFeatureLevel.h" -#include "ThreadWatchdogConfig.h" -#include "TransportCommon/EConnectionProtocol.h" -#include "Collections/Guid.h" - -#include - -namespace ramses -{ - class RamsesFrameworkConfigImpl : public StatusObjectImpl - { - public: - explicit RamsesFrameworkConfigImpl(EFeatureLevel featureLevel); - ~RamsesFrameworkConfigImpl() override; - - status_t setFeatureLevel(EFeatureLevel featureLevel); - EFeatureLevel getFeatureLevel() const; - - status_t enableDLTApplicationRegistration(bool state); - bool getDltApplicationRegistrationEnabled() const; - - void setDLTApplicationID(std::string_view id); - std::string_view getDLTApplicationID() const; - - void setDLTApplicationDescription(std::string_view description); - std::string_view getDLTApplicationDescription() const; - - uint32_t getProtocolVersion() const; - - status_t setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier thread, uint32_t interval); - status_t setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback); - - status_t setRequestedRamsesShellType(ERamsesShellType shellType); - - ramses_internal::EConnectionProtocol getUsedProtocol() const; - uint32_t getWatchdogNotificationInterval(ERamsesThreadIdentifier thread) const; - IThreadWatchdogNotification* getWatchdogNotificationCallback() const; - - void setLogLevel(ELogLevel logLevel); - status_t setLogLevel(std::string_view context, ELogLevel logLevel); - void setLogLevelConsole(ELogLevel logLevel); - - void setPeriodicLogInterval(std::chrono::seconds interval); - - status_t setParticipantGuid(uint64_t guid); - ramses_internal::Guid getUserProvidedGuid() const; - - status_t setParticipantName(std::string_view name); - const std::string& getParticipantName() const; - - status_t setConnectionSystem(EConnectionSystem connectionSystem); - - TCPConfig m_tcpConfig; - ERamsesShellType m_shellType; - ramses_internal::ThreadWatchdogConfig m_watchdogConfig; - bool m_periodicLogsEnabled; - - ramses_internal::RamsesLoggerConfig loggerConfig; - uint32_t periodicLogTimeout = 2u; - - void setFeatureLevelNoCheck(EFeatureLevel featureLevel); - - private: - EFeatureLevel m_featureLevel = EFeatureLevel_01; - ramses_internal::EConnectionProtocol m_usedProtocol; - std::string m_participantName; - bool m_enableDltApplicationRegistration = true; - ramses_internal::Guid m_userProvidedGuid; - }; -} - -#endif diff --git a/framework/ramses-framework/include/RamsesFrameworkImpl.h b/framework/ramses-framework/include/RamsesFrameworkImpl.h deleted file mode 100644 index c4dd18bd0..000000000 --- a/framework/ramses-framework/include/RamsesFrameworkImpl.h +++ /dev/null @@ -1,106 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESFRAMEWORKIMPL_H -#define RAMSES_RAMSESFRAMEWORKIMPL_H - -#include "StatusObjectImpl.h" -#include "TaskFramework/ThreadedTaskExecutor.h" -#include "Components/ResourceComponent.h" -#include "Components/SceneGraphComponent.h" -#include "Common/ParticipantIdentifier.h" -#include "Utils/PeriodicLogger.h" -#include "Utils/StatisticCollection.h" -#include "TransportCommon/LogConnectionInfo.h" -#include "TransportCommon/EConnectionProtocol.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/EFeatureLevel.h" -#include "RamsesObjectFactoryInterfaces.h" - -#include -#include -#include - -namespace ramses_internal -{ - class ICommunicationSystem; - class RamshStandardSetup; - class Ramsh; - class PublicRamshCommand; -} - -namespace ramses -{ - class RamsesFrameworkConfig; - class RamsesFrameworkConfigImpl; - class RamsesClient; - class RamsesRenderer; - class RendererConfig; - class IRamshCommand; - - class RamsesFrameworkImpl : public StatusObjectImpl - { - public: - ~RamsesFrameworkImpl() override; - - RamsesRenderer* createRenderer(const RendererConfig& config); - RamsesClient* createClient(std::string_view applicationName); - - status_t destroyRenderer(RamsesRenderer& renderer); - status_t destroyClient(RamsesClient& client); - - status_t connect(); - bool isConnected() const; - status_t disconnect(); - - EFeatureLevel getFeatureLevel() const; - ramses_internal::ResourceComponent& getResourceComponent(); - ramses_internal::SceneGraphComponent& getScenegraphComponent(); - ramses_internal::ParticipantIdentifier getParticipantAddress() const; - ramses_internal::Ramsh& getRamsh(); - ramses_internal::PlatformLock& getFrameworkLock(); - const ramses_internal::ThreadWatchdogConfig& getThreadWatchdogConfig() const; - ramses_internal::ITaskQueue& getTaskQueue(); - ramses_internal::PeriodicLogger& getPeriodicLogger(); - ramses_internal::StatisticCollectionFramework& getStatisticCollection(); - static void SetLogHandler(const LogHandlerFunc& logHandlerFunc); - status_t addRamshCommand(const std::shared_ptr& command); - status_t executeRamshCommand(const std::string& input); - - static std::unique_ptr CreateImpl(const RamsesFrameworkConfig& config); - - private: - RamsesFrameworkImpl(const RamsesFrameworkConfigImpl& config, const ramses_internal::ParticipantIdentifier& participantAddress); - static void LogEnvironmentVariableIfSet(std::string_view envVarName); - static void LogAvailableCommunicationStacks(); - static void LogBuildInformation(); - - // the framework-wide mutex that is used by all framework-base classes to synchronize access to shared resource - // has to be used by all logic, component, etc classes - ramses_internal::PlatformLock m_frameworkLock; - std::unique_ptr m_ramsh; - std::vector> m_publicRamshCommands; - ramses_internal::StatisticCollectionFramework m_statisticCollection; - ramses_internal::ParticipantIdentifier m_participantAddress; - ramses_internal::EConnectionProtocol m_connectionProtocol; - std::unique_ptr m_communicationSystem; - ramses_internal::PeriodicLogger m_periodicLogger; - bool m_connected; - const ramses_internal::ThreadWatchdogConfig m_threadWatchdogConfig; - ramses_internal::ThreadedTaskExecutor m_threadedTaskExecutor; - ramses_internal::ResourceComponent m_resourceComponent; - ramses_internal::SceneGraphComponent m_scenegraphComponent; - std::shared_ptr m_ramshCommandLogConnectionInformation; - - EFeatureLevel m_featureLevel; - std::unordered_map m_ramsesClients; - RendererUniquePtr m_ramsesRenderer; - }; -} - -#endif diff --git a/framework/ramses-framework/include/StatusObjectImpl.h b/framework/ramses-framework/include/StatusObjectImpl.h deleted file mode 100644 index 953388782..000000000 --- a/framework/ramses-framework/include/StatusObjectImpl.h +++ /dev/null @@ -1,70 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_STATUSOBJECTIMPL_H -#define RAMSES_STATUSOBJECTIMPL_H - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/EValidationSeverity.h" -#include "Utils/MessagePool.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "APILoggingMacros.h" - -#include -#include - -#define CHECK_RETURN_ERR(expr) \ - { \ - const ramses::status_t statusRet = (expr); \ - if (statusRet != ramses::StatusOK) \ - { \ - return statusRet; \ - } \ - } - -namespace ramses -{ - class StatusObjectImpl - { - public: - StatusObjectImpl() = default; - virtual ~StatusObjectImpl() = default; - - RNODISCARD status_t addErrorEntry(const char* message) const; - RNODISCARD status_t addErrorEntry(const std::string& message) const; - const char* getStatusMessage(status_t status) const; - - virtual status_t validate() const; - const char* getValidationReport(EValidationSeverity minSeverity) const; - - protected: - virtual status_t addValidationMessage(EValidationSeverity severity, std::string message) const; - status_t addValidationOfDependentObject(const StatusObjectImpl& dependentObject) const; - - private: - EValidationSeverity getMaxValidationSeverity() const; - void writeMessagesToStream(EValidationSeverity minSeverity, ramses_internal::StringOutputStream& stream, size_t indent, std::unordered_set& visitedObjs) const; - - struct ValidationMessage - { - EValidationSeverity severity; - std::string message; - }; - using ValidationMessages = std::vector; - - mutable ValidationMessages m_validationMessages; - mutable std::string m_validationReport; - mutable std::vector m_dependentObjects; - - static std::recursive_mutex m_statusCacheLock; - using StatusCache = ramses_internal::MessagePool<32U, StatusOK>; - static StatusCache m_statusCache; - }; -} - -#endif diff --git a/framework/ramses-framework/include/ThreadWatchdogConfig.h b/framework/ramses-framework/include/ThreadWatchdogConfig.h deleted file mode 100644 index f8568b526..000000000 --- a/framework/ramses-framework/include/ThreadWatchdogConfig.h +++ /dev/null @@ -1,71 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_THREADWATCHDOGCONFIG_H -#define RAMSES_THREADWATCHDOGCONFIG_H - -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/IThreadWatchdogNotification.h" - -namespace ramses_internal -{ - class ThreadWatchdogConfig - { - public: - ThreadWatchdogConfig() - : m_callback(nullptr) - , m_workerThreadsWatchdogInterval(1000) - , m_rendererThreadWatchdogInterval(1000) - { - } - void setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier thread, uint32_t interval) - { - switch(thread) - { - case ramses::ERamsesThreadIdentifier::Workers: - m_workerThreadsWatchdogInterval = interval; - break; - case ramses::ERamsesThreadIdentifier::Renderer: - m_rendererThreadWatchdogInterval = interval; - break; - case ramses::ERamsesThreadIdentifier::Unknown: - break; - } - } - - void setThreadWatchDogCallback(ramses::IThreadWatchdogNotification* callback) - { - m_callback = callback; - } - - [[nodiscard]] uint32_t getWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier thread) const - { - switch (thread) - { - case ramses::ERamsesThreadIdentifier::Workers: - return m_workerThreadsWatchdogInterval; - case ramses::ERamsesThreadIdentifier::Renderer: - return m_rendererThreadWatchdogInterval; - case ramses::ERamsesThreadIdentifier::Unknown: - return 0; - } - return 0; - } - - [[nodiscard]] ramses::IThreadWatchdogNotification* getCallBack() const - { - return m_callback; - } - private: - ramses::IThreadWatchdogNotification* m_callback; - uint32_t m_workerThreadsWatchdogInterval; - uint32_t m_rendererThreadWatchdogInterval; - }; -} - -#endif diff --git a/framework/ramses-framework/src/RamsesFramework.cpp b/framework/ramses-framework/src/RamsesFramework.cpp deleted file mode 100644 index 28fce7aff..000000000 --- a/framework/ramses-framework/src/RamsesFramework.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-framework-api/RamsesFramework.h" -#include "RamsesFrameworkImpl.h" -#include "PlatformAbstraction/PlatformTime.h" - -#include "APILoggingHelper.h" - -#include - -namespace ramses -{ - RamsesFramework::RamsesFramework(const RamsesFrameworkConfig& config) - : StatusObject{ RamsesFrameworkImpl::CreateImpl(config) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - LOG_HL_CLIENT_API1(LOG_API_VOID, LOG_API_GENERIC_OBJECT_STRING(config)); - } - - ramses::RamsesRenderer* RamsesFramework::createRenderer(const RendererConfig& config) - { - auto result = m_impl.createRenderer(config); - LOG_HL_CLIENT_API1(LOG_API_GENERIC_PTR_STRING(result), LOG_API_GENERIC_PTR_STRING(&config)); - return result; - } - - ramses::RamsesClient* RamsesFramework::createClient(std::string_view applicationName) - { - auto result = m_impl.createClient(applicationName); - LOG_HL_CLIENT_API1(LOG_API_GENERIC_PTR_STRING(result), applicationName); - return result; - } - - status_t RamsesFramework::destroyRenderer(RamsesRenderer& renderer) - { - const status_t result = m_impl.destroyRenderer(renderer); - LOG_HL_CLIENT_API1(result, LOG_API_GENERIC_OBJECT_STRING(renderer)); - return result; - } - - status_t RamsesFramework::destroyClient(RamsesClient& client) - { - const status_t result = m_impl.destroyClient(client); - LOG_HL_CLIENT_API1(result, LOG_API_GENERIC_OBJECT_STRING(client)); - return result; - } - - RamsesFramework::~RamsesFramework() - { - LOG_HL_CLIENT_API_NOARG(LOG_API_VOID); - } - - status_t RamsesFramework::connect() - { - const status_t result = m_impl.connect(); - LOG_HL_CLIENT_API_NOARG(result); - return result; - } - - bool RamsesFramework::isConnected() const - { - return m_impl.isConnected(); - } - - status_t RamsesFramework::disconnect() - { - const status_t result = m_impl.disconnect(); - LOG_HL_CLIENT_API_NOARG(result); - return result; - } - - void RamsesFramework::SetLogHandler(const LogHandlerFunc& logHandlerFunc) - { - RamsesFrameworkImpl::SetLogHandler(logHandlerFunc); - } - - uint64_t RamsesFramework::GetSynchronizedClockMilliseconds() - { - return ramses_internal::PlatformTime::GetMillisecondsSynchronized(); - } - - status_t RamsesFramework::addRamshCommand(const std::shared_ptr& command) - { - const status_t result = m_impl.addRamshCommand(command); - LOG_HL_CLIENT_API1(result, LOG_API_GENERIC_PTR_STRING(command.get())); - return result; - } - - status_t RamsesFramework::executeRamshCommand(const std::string& input) - { - return m_impl.executeRamshCommand(input); - } -} diff --git a/framework/ramses-framework/src/RamsesFrameworkConfig.cpp b/framework/ramses-framework/src/RamsesFrameworkConfig.cpp deleted file mode 100644 index d32c85033..000000000 --- a/framework/ramses-framework/src/RamsesFrameworkConfig.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "RamsesFrameworkConfigImpl.h" - -namespace ramses -{ - RamsesFrameworkConfig::RamsesFrameworkConfig(EFeatureLevel featureLevel) - : StatusObject{ std::make_unique(featureLevel) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RamsesFrameworkConfig::~RamsesFrameworkConfig() = default; - - RamsesFrameworkConfig::RamsesFrameworkConfig(const RamsesFrameworkConfig& other) - : StatusObject{ std::make_unique(other.m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RamsesFrameworkConfig::RamsesFrameworkConfig(RamsesFrameworkConfig&& other) noexcept - : StatusObject{ std::move(other.StatusObject::m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RamsesFrameworkConfig& RamsesFrameworkConfig::operator=(const RamsesFrameworkConfig& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - RamsesFrameworkConfig& RamsesFrameworkConfig::operator=(RamsesFrameworkConfig&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - status_t RamsesFrameworkConfig::setFeatureLevel(EFeatureLevel featureLevel) - { - return m_impl.get().setFeatureLevel(featureLevel); - } - - EFeatureLevel RamsesFrameworkConfig::getFeatureLevel() const - { - return m_impl.get().getFeatureLevel(); - } - - status_t RamsesFrameworkConfig::setRequestedRamsesShellType(ERamsesShellType requestedShellType) - { - const status_t status = m_impl.get().setRequestedRamsesShellType(requestedShellType); - return status; - } - - status_t RamsesFrameworkConfig::setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval) - { - return m_impl.get().setWatchdogNotificationInterval(thread, interval); - } - - status_t RamsesFrameworkConfig::setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback) - { - return m_impl.get().setWatchdogNotificationCallBack(callback); - } - - status_t RamsesFrameworkConfig::disableDLTApplicationRegistration() - { - return m_impl.get().enableDLTApplicationRegistration(false); - } - - void RamsesFrameworkConfig::setDLTApplicationID(std::string_view id) - { - m_impl.get().setDLTApplicationID(id); - } - - std::string_view RamsesFrameworkConfig::getDLTApplicationID() const - { - return m_impl.get().getDLTApplicationID(); - } - - void RamsesFrameworkConfig::setDLTApplicationDescription(std::string_view description) - { - m_impl.get().setDLTApplicationDescription(description); - } - - std::string_view RamsesFrameworkConfig::getDLTApplicationDescription() const - { - return m_impl.get().getDLTApplicationDescription(); - } - - void RamsesFrameworkConfig::setLogLevel(ELogLevel logLevel) - { - m_impl.get().setLogLevel(logLevel); - } - - status_t RamsesFrameworkConfig::setLogLevel(std::string_view context, ELogLevel logLevel) - { - return m_impl.get().setLogLevel(context, logLevel); - } - - void RamsesFrameworkConfig::setLogLevelConsole(ELogLevel logLevel) - { - m_impl.get().setLogLevelConsole(logLevel); - } - - void RamsesFrameworkConfig::setPeriodicLogInterval(std::chrono::seconds interval) - { - m_impl.get().setPeriodicLogInterval(interval); - } - - status_t RamsesFrameworkConfig::setParticipantGuid(uint64_t guid) - { - return m_impl.get().setParticipantGuid(guid); - } - - status_t RamsesFrameworkConfig::setParticipantName(std::string_view name) - { - return m_impl.get().setParticipantName(name); - } - - status_t RamsesFrameworkConfig::setConnectionSystem(EConnectionSystem connectionSystem) - { - return m_impl.get().setConnectionSystem(connectionSystem); - } - - void RamsesFrameworkConfig::setInterfaceSelectionIPForTCPCommunication(std::string_view ip) - { - m_impl.get().m_tcpConfig.setIPAddress(ip); - } - - void RamsesFrameworkConfig::setInterfaceSelectionPortForTCPCommunication(uint16_t port) - { - m_impl.get().m_tcpConfig.setPort(port); - } - - void RamsesFrameworkConfig::setDaemonIPForTCPCommunication(std::string_view ip) - { - m_impl.get().m_tcpConfig.setDaemonIPAddress(ip); - } - - void RamsesFrameworkConfig::setDaemonPortForTCPCommunication(uint16_t port) - { - m_impl.get().m_tcpConfig.setDaemonPort(port); - } - - status_t RamsesFrameworkConfig::setConnectionKeepaliveSettings(std::chrono::milliseconds interval, std::chrono::milliseconds timeout) - { - m_impl.get().m_tcpConfig.setAliveInterval(interval); - m_impl.get().m_tcpConfig.setAliveTimeout(timeout); - return StatusOK; - } -} diff --git a/framework/ramses-framework/src/RamsesFrameworkTypesImpl.cpp b/framework/ramses-framework/src/RamsesFrameworkTypesImpl.cpp deleted file mode 100644 index dabb1735b..000000000 --- a/framework/ramses-framework/src/RamsesFrameworkTypesImpl.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RamsesFrameworkTypesImpl.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneVersionTag.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneTypes.h" -#include "Resource/ResourceTypes.h" - -// ensure internal and api types match -static_assert(std::is_same::value, "SceneID type mismatch"); -static_assert(ramses::sceneId_t::Invalid().getValue() == ramses_internal::SceneId::Invalid().getValue(), "SceneID default mismatch"); - -static_assert(std::is_same::value, "SceneVersionTag type mismatch"); -static_assert(ramses::InvalidSceneVersionTag == ramses_internal::SceneVersionTag::Invalid().getValue(), "SceneVersionTag default mismatch"); - -static_assert(std::is_same::value, "dataProviderId_t type mismatch"); -static_assert(ramses::dataProviderId_t::Invalid().getValue() == ramses_internal::DataSlotId::Invalid().getValue(), "dataProviderId_t invalid value mismatch"); - -static_assert(std::is_same::value, "dataConsumerId_t type mismatch"); -static_assert(ramses::dataConsumerId_t::Invalid().getValue() == ramses_internal::DataSlotId::Invalid().getValue(), "dataConsumerId_t invalid value mismatch"); - -static_assert(std::is_same::value, "NodeHandle type mismatch"); -static_assert(ramses::nodeId_t::Invalid().getValue() == ramses_internal::NodeHandle::Invalid().asMemoryHandle(), "NodeHandle default mismatch"); - -static_assert(std::is_same::value, "ResourceCacheFlag type mismatch"); -static_assert(ramses::resourceCacheFlag_t::Invalid().getValue() == ramses_internal::ResourceCacheFlag::Invalid().getValue(), "ResourceCacheFlag default mismatch"); - -static_assert(std::is_same::value, "PickableObjectId type mismatch"); -static_assert(ramses::pickableObjectId_t::Invalid().getValue() == ramses_internal::PickableObjectId::Invalid().getValue(), "PickableObjectId default mismatch"); diff --git a/framework/ramses-framework/src/StatusObject.cpp b/framework/ramses-framework/src/StatusObject.cpp deleted file mode 100644 index 1bc1cd006..000000000 --- a/framework/ramses-framework/src/StatusObject.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-framework-api/StatusObject.h" - -// internal -#include "StatusObjectImpl.h" -#include "Collections/HashSet.h" - -namespace ramses -{ - StatusObject::StatusObject(std::unique_ptr impl) - : m_impl{ std::move(impl) } - { - } - - StatusObject::~StatusObject() = default; - - status_t StatusObject::validate() const - { - return m_impl->validate(); - } - - const char* StatusObject::getValidationReport(EValidationSeverity minSeverity) const - { - return m_impl->getValidationReport(minSeverity); - } - - const char* StatusObject::getStatusMessage(status_t status) const - { - return m_impl->getStatusMessage(status); - } -} diff --git a/framework/ramses-framework/src/StatusObjectImpl.cpp b/framework/ramses-framework/src/StatusObjectImpl.cpp deleted file mode 100644 index 098d30ec9..000000000 --- a/framework/ramses-framework/src/StatusObjectImpl.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "StatusObjectImpl.h" -#include "Utils/LogMacros.h" -#include "Collections/StringOutputStream.h" - -#include - -namespace ramses -{ - StatusObjectImpl::StatusCache StatusObjectImpl::m_statusCache; - std::recursive_mutex StatusObjectImpl::m_statusCacheLock; - - status_t StatusObjectImpl::addErrorEntry(const char* message) const - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, message); - std::lock_guard g(m_statusCacheLock); - return m_statusCache.addMessage(message); - } - - RNODISCARD status_t StatusObjectImpl::addErrorEntry(const std::string& message) const - { - return addErrorEntry(message.c_str()); - } - - const char* StatusObjectImpl::getStatusMessage(status_t status) const - { - std::lock_guard g(m_statusCacheLock); - return m_statusCache.getMessage(status); - } - - status_t StatusObjectImpl::validate() const - { - status_t status = StatusOK; - - m_dependentObjects.clear(); - - std::lock_guard g(m_statusCacheLock); - m_validationMessages.clear(); - - return status; - } - - const char* StatusObjectImpl::getValidationReport(EValidationSeverity minSeverity) const - { - ramses_internal::StringOutputStream stringStream; - std::unordered_set visitedObjs; - writeMessagesToStream(minSeverity, stringStream, 0u, visitedObjs); - m_validationReport = stringStream.c_str(); - - return m_validationReport.c_str(); - } - - status_t StatusObjectImpl::addValidationMessage(EValidationSeverity severity, std::string message) const - { - m_validationMessages.push_back({ severity, std::move(message) }); - std::lock_guard g(m_statusCacheLock); - switch (severity) - { - default: - case ramses::EValidationSeverity::Info: - return StatusOK; - case ramses::EValidationSeverity::Warning: - return m_statusCache.addMessage("Validation warning"); - case ramses::EValidationSeverity::Error: - return m_statusCache.addMessage("Validation error"); - } - } - - status_t StatusObjectImpl::addValidationOfDependentObject(const StatusObjectImpl& dependentObject) const - { - assert(&dependentObject != this); - m_dependentObjects.push_back(&dependentObject); - - return dependentObject.validate(); - } - - EValidationSeverity StatusObjectImpl::getMaxValidationSeverity() const - { - EValidationSeverity maxSeverity = EValidationSeverity::Info; - for (const auto& msg : m_validationMessages) - maxSeverity = std::max(maxSeverity, msg.severity); - - return maxSeverity; - } - - void StatusObjectImpl::writeMessagesToStream(EValidationSeverity minSeverity, ramses_internal::StringOutputStream& stream, size_t indent, std::unordered_set& visitedObjs) const - { - if (minSeverity > EValidationSeverity::Info && visitedObjs.count(this) != 0) - return; - visitedObjs.insert(this); - - std::string indentStr; - if (minSeverity == EValidationSeverity::Info) - indentStr.assign(indent * 2u, ' '); - - // write this object's messages - for (const auto& message : m_validationMessages) - { - if (message.severity >= minSeverity) - { - stream << indentStr; - switch (message.severity) - { - default: - case EValidationSeverity::Info: - break; - case EValidationSeverity::Warning: - stream << "WARNING: "; - break; - case EValidationSeverity::Error: - stream << "ERROR: "; - break; - } - stream << message.message << "\n"; - } - } - - // write all dependent objects' messages (recursively) - if (!m_dependentObjects.empty()) - { - if (minSeverity == EValidationSeverity::Info) - stream << indentStr << "- " << m_dependentObjects.size() << " dependent objects:\n"; - for (const auto& dep : m_dependentObjects) - dep->writeMessagesToStream(minSeverity, stream, indent + 1, visitedObjs); - } - } -} diff --git a/framework/ramses-framework/test/DataTypeTest.cpp b/framework/ramses-framework/test/DataTypeTest.cpp deleted file mode 100644 index 9d5d200b2..000000000 --- a/framework/ramses-framework/test/DataTypeTest.cpp +++ /dev/null @@ -1,281 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "DataTypesImpl.h" -#include "ramses-framework-api/EDataType.h" -#include "SceneAPI/EDataType.h" -#include "DataTypeUtils.h" - -namespace ramses -{ - using namespace testing; - - TEST(Datatype, CanBeConstructedFromInitializerList) - { - vec2f v2f{ 1.f, 2.f }; - vec3f v3f{ 1.f, 2.f, 3.f }; - vec4f v4f{ 1.f, 2.f, 3.f, 4.f }; - vec2i v2i{ 1, 2 }; - vec3i v3i{ 1, 2, 3 }; - vec4i v4i{ 1, 2, 3, 4 }; - matrix22f m2{ 1.f, 2.f, 3.f, 4.f }; - matrix33f m3{ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f }; - matrix44f m4{ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f }; - UNUSED(v2f); - UNUSED(v3f); - UNUSED(v4f); - UNUSED(v2i); - UNUSED(v3i); - UNUSED(v4i); - UNUSED(m2); - UNUSED(m3); - UNUSED(m4); - } - - TEST(Datatype, CanBeCopyConstructed) - { - vec2f v2f{ vec2f{ 1.f, 2.f } }; - vec3f v3f{ vec3f{ 1.f, 2.f, 3.f } }; - vec4f v4f{ vec4f{ 1.f, 2.f, 3.f, 4.f } }; - vec2i v2i{ vec2i{ 1, 2 } }; - vec3i v3i{ vec3i{ 1, 2, 3 } }; - vec4i v4i{ vec4i{ 1, 2, 3, 4 } }; - matrix22f m2{ matrix22f{ 1.f, 2.f, 3.f, 4.f } }; - matrix33f m3{ matrix33f{ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f } }; - matrix44f m4{ matrix44f{ 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f } }; - UNUSED(v2f); - UNUSED(v3f); - UNUSED(v4f); - UNUSED(v2i); - UNUSED(v3i); - UNUSED(v4i); - UNUSED(m2); - UNUSED(m3); - UNUSED(m4); - } - - TEST(EDatatype, CanReturnNumberOfComponentsAtCompiletime) - { - static_assert(1u == GetNumberOfComponents(EDataType::UInt16), "Invalid number of components"); - static_assert(1u == GetNumberOfComponents(EDataType::UInt32), "Invalid number of components"); - static_assert(1u == GetNumberOfComponents(EDataType::Float), "Invalid number of components"); - static_assert(2u == GetNumberOfComponents(EDataType::Vector2F), "Invalid number of components"); - static_assert(3u == GetNumberOfComponents(EDataType::Vector3F), "Invalid number of components"); - static_assert(4u == GetNumberOfComponents(EDataType::Vector4F), "Invalid number of components"); - static_assert(1u == GetNumberOfComponents(EDataType::ByteBlob), "Invalid number of components"); - static_assert(1u == GetNumberOfComponents(EDataType::Int32), "Invalid number of components"); - static_assert(2u == GetNumberOfComponents(EDataType::Vector2I), "Invalid number of components"); - static_assert(3u == GetNumberOfComponents(EDataType::Vector3I), "Invalid number of components"); - static_assert(4u == GetNumberOfComponents(EDataType::Vector4I), "Invalid number of components"); - static_assert(4u == GetNumberOfComponents(EDataType::Matrix22F), "Invalid number of components"); - static_assert(9u == GetNumberOfComponents(EDataType::Matrix33F), "Invalid number of components"); - static_assert(16u == GetNumberOfComponents(EDataType::Matrix44F), "Invalid number of components"); - - static_assert(0u == GetNumberOfComponents(EDataType::TextureSampler2D), "Invalid number of components"); - static_assert(0u == GetNumberOfComponents(EDataType::TextureSampler2DMS), "Invalid number of components"); - static_assert(0u == GetNumberOfComponents(EDataType::TextureSampler3D), "Invalid number of components"); - static_assert(0u == GetNumberOfComponents(EDataType::TextureSamplerCube), "Invalid number of components"); - static_assert(0u == GetNumberOfComponents(EDataType::TextureSamplerExternal), "Invalid number of components"); - } - - TEST(EDatatype, CanReturnSizeOfComponentAtCompiletime) - { - static_assert(sizeof(uint16_t) == GetSizeOfComponent(EDataType::UInt16), "component of EDataType::UInt16 should have size of uint16_t"); - static_assert(sizeof(uint32_t) == GetSizeOfComponent(EDataType::UInt32), "component of EDataType::UInt32 should have size of uint32_t"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Float), "component of EDataType::Float should have size of float"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Vector2F), "component of EDataType::Vector2F should have size of float"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Vector3F), "component of EDataType::Vector3F should have size of float"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Vector4F), "component of EDataType::Vector4F should have size of float"); - static_assert(sizeof(ramses::Byte) == GetSizeOfComponent(EDataType::ByteBlob), "component of EDataType::ByteBlob should have size of byte"); - static_assert(sizeof(int32_t) == GetSizeOfComponent(EDataType::Int32), "Invalid component size"); - static_assert(sizeof(int32_t) == GetSizeOfComponent(EDataType::Vector2I), "Invalid component size"); - static_assert(sizeof(int32_t) == GetSizeOfComponent(EDataType::Vector3I), "Invalid component size"); - static_assert(sizeof(int32_t) == GetSizeOfComponent(EDataType::Vector4I), "Invalid component size"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Matrix22F), "Invalid component size"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Matrix33F), "Invalid component size"); - static_assert(sizeof(float) == GetSizeOfComponent(EDataType::Matrix44F), "Invalid component size"); - - static_assert(0u == GetSizeOfComponent(EDataType::TextureSampler2D), "Invalid component size"); - static_assert(0u == GetSizeOfComponent(EDataType::TextureSampler2DMS), "Invalid component size"); - static_assert(0u == GetSizeOfComponent(EDataType::TextureSampler3D), "Invalid component size"); - static_assert(0u == GetSizeOfComponent(EDataType::TextureSamplerCube), "Invalid component size"); - static_assert(0u == GetSizeOfComponent(EDataType::TextureSamplerExternal), "Invalid component size"); - } - - TEST(EDatatype, CanReturnSizesAtCompiletime) - { - static_assert(sizeof(uint16_t) == GetSizeOfDataType(EDataType::UInt16), "Invalid data type size"); - static_assert(sizeof(uint32_t) == GetSizeOfDataType(EDataType::UInt32), "Invalid data type size"); - static_assert(sizeof(float) == GetSizeOfDataType(EDataType::Float), "Invalid data type size"); - static_assert(sizeof(ramses::vec2f) == GetSizeOfDataType(EDataType::Vector2F), "Invalid data type size"); - static_assert(sizeof(ramses::vec3f) == GetSizeOfDataType(EDataType::Vector3F), "Invalid data type size"); - static_assert(sizeof(ramses::vec4f) == GetSizeOfDataType(EDataType::Vector4F), "Invalid data type size"); - static_assert(sizeof(ramses::Byte) == GetSizeOfDataType(EDataType::ByteBlob), "Invalid data type size"); - static_assert(sizeof(int32_t) == GetSizeOfDataType(EDataType::Int32), "Invalid data type size"); - static_assert(sizeof(ramses::vec2i) == GetSizeOfDataType(EDataType::Vector2I), "Invalid data type size"); - static_assert(sizeof(ramses::vec3i) == GetSizeOfDataType(EDataType::Vector3I), "Invalid data type size"); - static_assert(sizeof(ramses::vec4i) == GetSizeOfDataType(EDataType::Vector4I), "Invalid data type size"); - static_assert(sizeof(ramses::matrix22f) == GetSizeOfDataType(EDataType::Matrix22F), "Invalid data type size"); - static_assert(sizeof(ramses::matrix33f) == GetSizeOfDataType(EDataType::Matrix33F), "Invalid data type size"); - static_assert(sizeof(ramses::matrix44f) == GetSizeOfDataType(EDataType::Matrix44F), "Invalid data type size"); - - static_assert(0u == GetSizeOfDataType(EDataType::TextureSampler2D), "Invalid data type size"); - static_assert(0u == GetSizeOfDataType(EDataType::TextureSampler2DMS), "Invalid data type size"); - static_assert(0u == GetSizeOfDataType(EDataType::TextureSampler3D), "Invalid data type size"); - static_assert(0u == GetSizeOfDataType(EDataType::TextureSamplerCube), "Invalid data type size"); - static_assert(0u == GetSizeOfDataType(EDataType::TextureSamplerExternal), "Invalid data type size"); - } - - TEST(EDataType, EnsureGivesSameSizeOnPublicAPI) - { - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::UInt16) == ramses_internal::EnumToSize(ramses_internal::EDataType::UInt16)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::UInt32) == ramses_internal::EnumToSize(ramses_internal::EDataType::UInt32)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Float) == ramses_internal::EnumToSize(ramses_internal::EDataType::Float)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector2F) == ramses_internal::EnumToSize(ramses_internal::EDataType::Vector2F)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector3F) == ramses_internal::EnumToSize(ramses_internal::EDataType::Vector3F)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector4F) == ramses_internal::EnumToSize(ramses_internal::EDataType::Vector4F)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::ByteBlob) == ramses_internal::EnumToSize(ramses_internal::EDataType::ByteBlob)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Int32) == ramses_internal::EnumToSize(ramses_internal::EDataType::Int32)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector2I) == ramses_internal::EnumToSize(ramses_internal::EDataType::Vector2I)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector3I) == ramses_internal::EnumToSize(ramses_internal::EDataType::Vector3I)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector4I) == ramses_internal::EnumToSize(ramses_internal::EDataType::Vector4I)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Matrix22F) == ramses_internal::EnumToSize(ramses_internal::EDataType::Matrix22F)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Matrix33F) == ramses_internal::EnumToSize(ramses_internal::EDataType::Matrix33F)); - static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Matrix44F) == ramses_internal::EnumToSize(ramses_internal::EDataType::Matrix44F)); - - // not relevant for sampler types - } - - TEST(EDataType, EnsureGivesSameNumberOfComponentsOnPublicAPI) - { - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::UInt16) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::UInt16)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::UInt32) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::UInt32)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Float) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Float)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector2F) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Vector2F)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector3F) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Vector3F)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector4F) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Vector4F)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::ByteBlob) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::ByteBlob)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Int32) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Int32)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector2I) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Vector2I)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector3I) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Vector3I)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector4I) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Vector4I)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Matrix22F) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Matrix22F)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Matrix33F) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Matrix33F)); - static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Matrix44F) == ramses_internal::EnumToNumComponents(ramses_internal::EDataType::Matrix44F)); - - // not relevant for sampler types - } - - TEST(EDataType, IsUniformInputDataType) - { - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - static_assert(ramses::IsUniformInputDataType()); - - static_assert(!ramses::IsUniformInputDataType()); - static_assert(!ramses::IsUniformInputDataType()); - static_assert(!ramses::IsUniformInputDataType()); - } - - TEST(EDataType, IsArrayResourceDataType) - { - static_assert(ramses::IsArrayResourceDataType()); - static_assert(ramses::IsArrayResourceDataType()); - static_assert(ramses::IsArrayResourceDataType()); - static_assert(ramses::IsArrayResourceDataType()); - static_assert(ramses::IsArrayResourceDataType()); - static_assert(ramses::IsArrayResourceDataType()); - static_assert(ramses::IsArrayResourceDataType()); - - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - static_assert(!ramses::IsArrayResourceDataType()); - } - - TEST(EDataType, GetEDataType) - { - static_assert(ramses::EDataType::UInt16 == ramses::GetEDataType()); - static_assert(ramses::EDataType::UInt32 == ramses::GetEDataType()); - static_assert(ramses::EDataType::Float == ramses::GetEDataType()); - static_assert(ramses::EDataType::Vector2F == ramses::GetEDataType()); - static_assert(ramses::EDataType::Vector3F == ramses::GetEDataType()); - static_assert(ramses::EDataType::Vector4F == ramses::GetEDataType()); - static_assert(ramses::EDataType::ByteBlob == ramses::GetEDataType()); - static_assert(ramses::EDataType::Int32 == ramses::GetEDataType()); - static_assert(ramses::EDataType::Vector2I == ramses::GetEDataType()); - static_assert(ramses::EDataType::Vector3I == ramses::GetEDataType()); - static_assert(ramses::EDataType::Vector4I == ramses::GetEDataType()); - static_assert(ramses::EDataType::Matrix22F == ramses::GetEDataType()); - static_assert(ramses::EDataType::Matrix33F == ramses::GetEDataType()); - static_assert(ramses::EDataType::Matrix44F == ramses::GetEDataType()); - } - - TEST(EDataType, InternalToPublic) - { - EXPECT_EQ(ramses::EDataType::Int32, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Int32)); - EXPECT_EQ(ramses::EDataType::UInt16, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::UInt16)); - EXPECT_EQ(ramses::EDataType::UInt32, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::UInt32)); - EXPECT_EQ(ramses::EDataType::Float, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Float)); - EXPECT_EQ(ramses::EDataType::Vector2F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector2F)); - EXPECT_EQ(ramses::EDataType::Vector3F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector3F)); - EXPECT_EQ(ramses::EDataType::Vector4F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector4F)); - EXPECT_EQ(ramses::EDataType::Vector2I, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector2I)); - EXPECT_EQ(ramses::EDataType::Vector3I, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector3I)); - EXPECT_EQ(ramses::EDataType::Vector4I, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector4I)); - EXPECT_EQ(ramses::EDataType::Matrix22F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Matrix22F)); - EXPECT_EQ(ramses::EDataType::Matrix33F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Matrix33F)); - EXPECT_EQ(ramses::EDataType::Matrix44F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Matrix44F)); - EXPECT_EQ(ramses::EDataType::TextureSampler2D, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::TextureSampler2D)); - EXPECT_EQ(ramses::EDataType::TextureSampler2DMS, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::TextureSampler2DMS)); - EXPECT_EQ(ramses::EDataType::TextureSampler3D, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::TextureSampler3D)); - EXPECT_EQ(ramses::EDataType::TextureSamplerCube, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::TextureSamplerCube)); - EXPECT_EQ(ramses::EDataType::TextureSamplerExternal, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::TextureSamplerExternal)); - - EXPECT_EQ(ramses::EDataType::Float, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::FloatBuffer)); - EXPECT_EQ(ramses::EDataType::UInt16, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::UInt16Buffer)); - EXPECT_EQ(ramses::EDataType::Vector2F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector2Buffer)); - EXPECT_EQ(ramses::EDataType::Vector3F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector3Buffer)); - EXPECT_EQ(ramses::EDataType::Vector4F, DataTypeUtils::ConvertDataTypeFromInternal(ramses_internal::EDataType::Vector4Buffer)); - } - - TEST(EDataType, PublicToInternal) - { - EXPECT_EQ(ramses_internal::EDataType::Int32, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Int32)); - EXPECT_EQ(ramses_internal::EDataType::UInt16, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::UInt16)); - EXPECT_EQ(ramses_internal::EDataType::UInt32, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::UInt32)); - EXPECT_EQ(ramses_internal::EDataType::Float, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Float)); - EXPECT_EQ(ramses_internal::EDataType::Vector2F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector2F)); - EXPECT_EQ(ramses_internal::EDataType::Vector3F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector3F)); - EXPECT_EQ(ramses_internal::EDataType::Vector4F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector4F)); - EXPECT_EQ(ramses_internal::EDataType::Vector2I, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector2I)); - EXPECT_EQ(ramses_internal::EDataType::Vector3I, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector3I)); - EXPECT_EQ(ramses_internal::EDataType::Vector4I, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector4I)); - EXPECT_EQ(ramses_internal::EDataType::Matrix22F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Matrix22F)); - EXPECT_EQ(ramses_internal::EDataType::Matrix33F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Matrix33F)); - EXPECT_EQ(ramses_internal::EDataType::Matrix44F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Matrix44F)); - EXPECT_EQ(ramses_internal::EDataType::TextureSampler2D, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSampler2D)); - EXPECT_EQ(ramses_internal::EDataType::TextureSampler2DMS, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSampler2DMS)); - EXPECT_EQ(ramses_internal::EDataType::TextureSampler3D, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSampler3D)); - EXPECT_EQ(ramses_internal::EDataType::TextureSamplerCube, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSamplerCube)); - EXPECT_EQ(ramses_internal::EDataType::TextureSamplerExternal, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSamplerExternal)); - } -} diff --git a/framework/ramses-framework/test/RamsesFrameworkConfigTest.cpp b/framework/ramses-framework/test/RamsesFrameworkConfigTest.cpp deleted file mode 100644 index 78323492c..000000000 --- a/framework/ramses-framework/test/RamsesFrameworkConfigTest.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gmock/gmock.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "RamsesFrameworkConfigImpl.h" - -using namespace ramses; -using namespace ramses_internal; - -class ARamsesFrameworkConfig : public testing::Test -{ -protected: - RamsesFrameworkConfig frameworkConfig{EFeatureLevel_01}; -}; - -TEST_F(ARamsesFrameworkConfig, IsInitializedCorrectly) -{ - EXPECT_EQ(EFeatureLevel_01, frameworkConfig.getFeatureLevel()); - EXPECT_EQ(ERamsesShellType::Default, frameworkConfig.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, FailsToIntializeFeatureLevel) -{ - RamsesFrameworkConfig config{static_cast(99)}; - EXPECT_EQ(EFeatureLevel_01, config.getFeatureLevel()); -} - -TEST_F(ARamsesFrameworkConfig, CanBeCopyAndMoveConstructed) -{ - frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); - - RamsesFrameworkConfig configCopy{ frameworkConfig }; - EXPECT_EQ(ERamsesShellType::Console, configCopy.m_impl.get().m_shellType); - - RamsesFrameworkConfig configMove{ std::move(frameworkConfig) }; - EXPECT_EQ(ERamsesShellType::Console, configMove.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, CanBeCopyAndMoveAssigned) -{ - frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); - - RamsesFrameworkConfig configCopy{EFeatureLevel_Latest}; - configCopy = frameworkConfig; - EXPECT_EQ(ERamsesShellType::Console, configCopy.m_impl.get().m_shellType); - - RamsesFrameworkConfig configMove{EFeatureLevel_Latest}; - configMove = std::move(frameworkConfig); - EXPECT_EQ(ERamsesShellType::Console, configMove.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, CanBeSelfAssigned) -{ - frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - frameworkConfig = frameworkConfig; - EXPECT_EQ(ERamsesShellType::Console, frameworkConfig.m_impl.get().m_shellType); - frameworkConfig = std::move(frameworkConfig); - // NOLINTNEXTLINE(bugprone-use-after-move) - EXPECT_EQ(ERamsesShellType::Console, frameworkConfig.m_impl.get().m_shellType); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -} - -TEST_F(ARamsesFrameworkConfig, CanSetFeatureLevel) -{ - EXPECT_EQ(StatusOK, frameworkConfig.setFeatureLevel(EFeatureLevel_Latest)); - EXPECT_EQ(EFeatureLevel_Latest, frameworkConfig.getFeatureLevel()); -} - -TEST_F(ARamsesFrameworkConfig, FailsToSetUnsupportedFeatureLevel) -{ - EXPECT_NE(StatusOK, frameworkConfig.setFeatureLevel(static_cast(99))); - EXPECT_EQ(EFeatureLevel_01, frameworkConfig.getFeatureLevel()); -} - -TEST_F(ARamsesFrameworkConfig, CanSetShellConsoleType) -{ - frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); - EXPECT_EQ(ERamsesShellType::Console, frameworkConfig.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, CanSetShellTypeNone) -{ - frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::None); - EXPECT_EQ(ERamsesShellType::None, frameworkConfig.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, CanSetShellTypeDefault) -{ - frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Default); - EXPECT_EQ(ERamsesShellType::Default, frameworkConfig.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, TestSetandGetApplicationInformation) -{ - const char* application_id = "myap"; - const char* application_description = "mydescription"; - - frameworkConfig.setDLTApplicationID(application_id); - frameworkConfig.setDLTApplicationDescription(application_description); - - EXPECT_EQ(application_id, frameworkConfig.getDLTApplicationID()); - EXPECT_EQ(application_description, frameworkConfig.getDLTApplicationDescription()); -} - -TEST_F(ARamsesFrameworkConfig, CanSetLogLevel) -{ - EXPECT_FALSE(frameworkConfig.m_impl.get().loggerConfig.logLevel.has_value()); - frameworkConfig.setLogLevel(ramses::ELogLevel::Debug); - EXPECT_EQ(ramses_internal::ELogLevel::Debug, frameworkConfig.m_impl.get().loggerConfig.logLevel); - frameworkConfig.setLogLevel(ramses::ELogLevel::Warn); - EXPECT_EQ(ramses_internal::ELogLevel::Warn, frameworkConfig.m_impl.get().loggerConfig.logLevel); -} - -TEST_F(ARamsesFrameworkConfig, CanSetLogLevelContext) -{ - EXPECT_TRUE(frameworkConfig.m_impl.get().loggerConfig.logLevelContexts.empty()); - frameworkConfig.setLogLevel("RRND", ramses::ELogLevel::Debug); - EXPECT_EQ(ramses_internal::ELogLevel::Debug, frameworkConfig.m_impl.get().loggerConfig.logLevelContexts["RRND"]); - frameworkConfig.setLogLevel("RRND", ramses::ELogLevel::Error); - EXPECT_EQ(ramses_internal::ELogLevel::Error, frameworkConfig.m_impl.get().loggerConfig.logLevelContexts["RRND"]); -} - -TEST_F(ARamsesFrameworkConfig, CanSetLogLevelConsole) -{ - EXPECT_FALSE(frameworkConfig.m_impl.get().loggerConfig.logLevelConsole.has_value()); - frameworkConfig.setLogLevelConsole(ramses::ELogLevel::Debug); - EXPECT_EQ(ramses_internal::ELogLevel::Debug, frameworkConfig.m_impl.get().loggerConfig.logLevelConsole); -} - -TEST_F(ARamsesFrameworkConfig, CanSetParticipantGuid) -{ - EXPECT_EQ(Guid(), frameworkConfig.m_impl.get().getUserProvidedGuid()); - EXPECT_EQ(StatusOK, frameworkConfig.setParticipantGuid(4400)); - EXPECT_EQ(Guid(4400), frameworkConfig.m_impl.get().getUserProvidedGuid()); - EXPECT_NE(StatusOK, frameworkConfig.setParticipantGuid(1)); - EXPECT_EQ(Guid(1), frameworkConfig.m_impl.get().getUserProvidedGuid()); - EXPECT_NE(StatusOK, frameworkConfig.setParticipantGuid(0)); - EXPECT_EQ(Guid(0), frameworkConfig.m_impl.get().getUserProvidedGuid()); -} - -TEST_F(ARamsesFrameworkConfig, CanSetParticipantName) -{ - EXPECT_EQ("", frameworkConfig.m_impl.get().getParticipantName()); - EXPECT_EQ(StatusOK, frameworkConfig.setParticipantName("foo")); - EXPECT_EQ("foo", frameworkConfig.m_impl.get().getParticipantName()); - EXPECT_EQ(StatusOK, frameworkConfig.setParticipantName("foo/bar")); - EXPECT_EQ("foo/bar", frameworkConfig.m_impl.get().getParticipantName()); -} - -TEST_F(ARamsesFrameworkConfig, CanSetPeriodicLogInterval) -{ - EXPECT_EQ(2u, frameworkConfig.m_impl.get().periodicLogTimeout); - EXPECT_TRUE(frameworkConfig.m_impl.get().m_periodicLogsEnabled); - frameworkConfig.setPeriodicLogInterval(std::chrono::seconds(0)); - EXPECT_EQ(0u, frameworkConfig.m_impl.get().periodicLogTimeout); - EXPECT_FALSE(frameworkConfig.m_impl.get().m_periodicLogsEnabled); - frameworkConfig.setPeriodicLogInterval(std::chrono::seconds(3)); - EXPECT_EQ(3u, frameworkConfig.m_impl.get().periodicLogTimeout); - EXPECT_TRUE(frameworkConfig.m_impl.get().m_periodicLogsEnabled); -} - -TEST_F(ARamsesFrameworkConfig, CanSetInterfaceSelectionSocket) -{ - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getIPAddress(), "127.0.0.1"); - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getPort(), 0); - frameworkConfig.setInterfaceSelectionIPForTCPCommunication("192.168.1.1"); - frameworkConfig.setInterfaceSelectionPortForTCPCommunication(5543); - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getIPAddress(), "192.168.1.1"); - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getPort(), 5543); -} - -TEST_F(ARamsesFrameworkConfig, CanSetDaemonSocket) -{ - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getDaemonIPAddress(), "127.0.0.1"); - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getDaemonPort(), 5999); - frameworkConfig.setDaemonIPForTCPCommunication("192.168.1.1"); - frameworkConfig.setDaemonPortForTCPCommunication(5543); - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getDaemonIPAddress(), "192.168.1.1"); - EXPECT_EQ(frameworkConfig.m_impl.get().m_tcpConfig.getDaemonPort(), 5543); -} - -TEST_F(ARamsesFrameworkConfig, CanSetConnectionSystem) -{ - EXPECT_EQ(EConnectionProtocol::TCP, frameworkConfig.m_impl.get().getUsedProtocol()); - EXPECT_EQ(StatusOK, frameworkConfig.setConnectionSystem(EConnectionSystem::Off)); - EXPECT_EQ(EConnectionProtocol::Off, frameworkConfig.m_impl.get().getUsedProtocol()); - EXPECT_EQ(StatusOK, frameworkConfig.setConnectionSystem(EConnectionSystem::TCP)); - EXPECT_EQ(EConnectionProtocol::TCP, frameworkConfig.m_impl.get().getUsedProtocol()); -} - -TEST_F(ARamsesFrameworkConfig, CanSetTCPKeepAlive) -{ - EXPECT_EQ(std::chrono::milliseconds(300), frameworkConfig.m_impl.get().m_tcpConfig.getAliveInterval()); - EXPECT_EQ(std::chrono::milliseconds(300*6), frameworkConfig.m_impl.get().m_tcpConfig.getAliveTimeout()); - frameworkConfig.setConnectionKeepaliveSettings(std::chrono::milliseconds(250), std::chrono::milliseconds(9000)); - EXPECT_EQ(std::chrono::milliseconds(250), frameworkConfig.m_impl.get().m_tcpConfig.getAliveInterval()); - EXPECT_EQ(std::chrono::milliseconds(9000), frameworkConfig.m_impl.get().m_tcpConfig.getAliveTimeout()); -} - diff --git a/framework/ramses-framework/test/StatusObjectTest.cpp b/framework/ramses-framework/test/StatusObjectTest.cpp deleted file mode 100644 index c686c1581..000000000 --- a/framework/ramses-framework/test/StatusObjectTest.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "StatusObjectImpl.h" -#include "gtest/gtest.h" - -#include - -using namespace ramses; -using namespace ramses_internal; - -class TestStatusObject : public StatusObjectImpl -{ -public: - explicit TestStatusObject(std::string_view prefix = {}, std::vector dependentObjs = {}, EValidationSeverity validationSeverityToReturn = EValidationSeverity::Error) - : m_prefix(prefix) - , m_depObjs(std::move(dependentObjs)) - , m_validationSeverityToReturn(validationSeverityToReturn) - { - } - - status_t validate() const override - { - StatusObjectImpl::validate(); - const auto infoStatus = addValidationMessage(EValidationSeverity::Info, m_prefix + "info"); - const auto warnStatus = addValidationMessage(EValidationSeverity::Warning, m_prefix + "warn"); - const auto errStatus = addValidationMessage(EValidationSeverity::Error, m_prefix + "err"); - - for (const auto depObj : m_depObjs) - addValidationOfDependentObject(*depObj); - - switch (m_validationSeverityToReturn) - { - default: - case ramses::EValidationSeverity::Info: - return infoStatus; - case ramses::EValidationSeverity::Warning: - return warnStatus; - case ramses::EValidationSeverity::Error: - return errStatus; - } - } - - std::string m_prefix; - std::vector m_depObjs; - EValidationSeverity m_validationSeverityToReturn; -}; - -class AStatusObject : public ::testing::Test -{ -public: - TestStatusObject dummy; -}; - -TEST_F(AStatusObject, canAddAndGetStatusMessage) -{ - const status_t s = dummy.addErrorEntry("foobar"); - EXPECT_STREQ("foobar", dummy.getStatusMessage(s)); -} - -TEST_F(AStatusObject, canAddAndGetMultipleStatusMessage) -{ - const status_t s1 = dummy.addErrorEntry("foo"); - const status_t s2 = dummy.addErrorEntry("bar"); - const status_t s3 = dummy.addErrorEntry("baz"); - EXPECT_STREQ("baz", dummy.getStatusMessage(s3)); - EXPECT_STREQ("foo", dummy.getStatusMessage(s1)); - EXPECT_STREQ("bar", dummy.getStatusMessage(s2)); -} - -TEST_F(AStatusObject, canGetInvalidMessage) -{ - EXPECT_STREQ("Unknown", dummy.getStatusMessage(999999)); -} - -TEST_F(AStatusObject, canGetSuccessMessage) -{ - EXPECT_STREQ("OK", dummy.getStatusMessage(StatusOK)); -} - -TEST_F(AStatusObject, canAddAndGetStdString) -{ - const status_t s = dummy.addErrorEntry(std::string("boo")); - EXPECT_STREQ("boo", dummy.getStatusMessage(s)); -} - -TEST_F(AStatusObject, canUseWithFmtlib) -{ - const status_t s = dummy.addErrorEntry(fmt::format("hello {}", "world")); - EXPECT_STREQ("hello world", dummy.getStatusMessage(s)); -} - -TEST_F(AStatusObject, generatesValidationReport_info) -{ - // hierarchy of dependent object to validate - // obj1 - obj3 - // \ obj2 - obj3 - // obj3 exists twice in the tree - const TestStatusObject objLvl3{ "3" }; - const TestStatusObject objLvl2{ "2", {&objLvl3} }; - const TestStatusObject objLvl1{ "1", {&objLvl2, &objLvl3} }; - - EXPECT_NE(StatusOK, objLvl1.validate()); - std::string report = objLvl1.getValidationReport(EValidationSeverity::Info); - - StringOutputStream expectedReport; - expectedReport << "1info\n" << "WARNING: 1warn\n" << "ERROR: 1err\n"; - expectedReport << "- 2 dependent objects:\n"; - expectedReport << " 2info\n" << " WARNING: 2warn\n" << " ERROR: 2err\n"; - expectedReport << " - 1 dependent objects:\n"; - expectedReport << " 3info\n" << " WARNING: 3warn\n" << " ERROR: 3err\n"; - expectedReport << " 3info\n" << " WARNING: 3warn\n" << " ERROR: 3err\n"; - EXPECT_STREQ(expectedReport.c_str(), report.c_str()); - - // obj3 only - EXPECT_NE(StatusOK, objLvl3.validate()); - report = objLvl3.getValidationReport(EValidationSeverity::Info); - StringOutputStream expectedReport2; - expectedReport2 << "3info\n" << "WARNING: 3warn\n" << "ERROR: 3err\n"; - EXPECT_STREQ(expectedReport2.c_str(), report.c_str()); -} - -TEST_F(AStatusObject, generatesValidationReport_warning) -{ - // hierarchy of dependent object to validate - // obj1 - obj3 - // \ obj2 - obj3 - // obj3 exists twice in the tree - const TestStatusObject objLvl3{ "3" }; - const TestStatusObject objLvl2{ "2", {&objLvl3} }; - const TestStatusObject objLvl1{ "1", {&objLvl2, &objLvl3} }; - - EXPECT_NE(StatusOK, objLvl1.validate()); - std::string report = objLvl1.getValidationReport(EValidationSeverity::Warning); - - // other than info level won't indent and won't add any extra messages - // it also won't report any object more than once - - StringOutputStream expectedReport; - expectedReport << "WARNING: 1warn\n" << "ERROR: 1err\n"; - expectedReport << "WARNING: 2warn\n" << "ERROR: 2err\n"; - expectedReport << "WARNING: 3warn\n" << "ERROR: 3err\n"; - EXPECT_STREQ(expectedReport.c_str(), report.c_str()); - - // obj3 only - EXPECT_NE(StatusOK, objLvl3.validate()); - report = objLvl3.getValidationReport(EValidationSeverity::Warning); - StringOutputStream expectedReport2; - expectedReport2 << "WARNING: 3warn\n" << "ERROR: 3err\n"; - EXPECT_STREQ(expectedReport2.c_str(), report.c_str()); -} - -TEST_F(AStatusObject, generatesValidationReport_error) -{ - // hierarchy of dependent object to validate - // obj1 - obj3 - // \ obj2 - obj3 - // obj3 exists twice in the tree - const TestStatusObject objLvl3{ "3" }; - const TestStatusObject objLvl2{ "2", {&objLvl3} }; - const TestStatusObject objLvl1{ "1", {&objLvl2, &objLvl3} }; - - EXPECT_NE(StatusOK, objLvl1.validate()); - std::string report = objLvl1.getValidationReport(EValidationSeverity::Error); - - // other than info level won't indent and won't add any extra messages - // it also won't report any object more than once - - StringOutputStream expectedReport; - expectedReport << "ERROR: 1err\n"; - expectedReport << "ERROR: 2err\n"; - expectedReport << "ERROR: 3err\n"; - EXPECT_STREQ(expectedReport.c_str(), report.c_str()); - - // obj3 only - EXPECT_NE(StatusOK, objLvl3.validate()); - report = objLvl3.getValidationReport(EValidationSeverity::Error); - StringOutputStream expectedReport2; - expectedReport2 << "ERROR: 3err\n"; - EXPECT_STREQ(expectedReport2.c_str(), report.c_str()); -} - -TEST_F(AStatusObject, reportsStatusBasedSeverityOfValidation) -{ - const TestStatusObject objLvl1{ "1", {}, EValidationSeverity::Info }; - const TestStatusObject objLvl2{ "2", {}, EValidationSeverity::Warning }; - const TestStatusObject objLvl3{ "3", {}, EValidationSeverity::Error }; - - EXPECT_EQ(StatusOK, objLvl1.validate()); - EXPECT_STREQ("Validation warning", objLvl2.getStatusMessage(objLvl2.validate())); - EXPECT_STREQ("Validation error", objLvl2.getStatusMessage(objLvl3.validate())); -} diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 000000000..43c92db61 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,76 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +################################### +######## API HEADERS LIB ########## +################################### + +add_library(ramses-api INTERFACE) +target_include_directories(ramses-api INTERFACE .) +target_link_libraries(ramses-api INTERFACE glm::glm) + +# framework +file(GLOB + RAMSES_FRAMEWORK_API_FILES_HEADER + ramses/framework/*.h) + +# client +file(GLOB + RAMSES_CLIENT_API_FILES_HEADER + ramses/client/*.h) + +if(ramses-sdk_TEXT_SUPPORT) + file(GLOB + RAMSES_CLIENT_TEXT_API_FILES_HEADER + ramses/client/text/*.h) + + target_compile_definitions(ramses-api INTERFACE RAMSES_TEXT_ENABLED) +endif() + +if(ramses-sdk_ENABLE_LOGIC) + file(GLOB + RAMSES_CLIENT_LOGIC_API_FILES_HEADER + ramses/client/logic/*.h) +endif() + +# renderer +if(ANY_WINDOW_TYPE_ENABLED) + file(GLOB + RAMSES_RENDERER_API_FILES_HEADER + ramses/renderer/*.h) +endif() + +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.19.0") + target_sources(ramses-api PUBLIC ${RAMSES_FRAMEWORK_API_FILES_HEADER} + ${RAMSES_CLIENT_API_FILES_HEADER} + ${RAMSES_CLIENT_TEXT_API_FILES_HEADER} + ${RAMSES_CLIENT_LOGIC_API_FILES_HEADER} + ${RAMSES_RENDERER_API_FILES_HEADER}) + + folderizeTarget(ramses-api) +endif() + +################################### +############ INSTALL ############## +################################### + +if(ramses-sdk_ENABLE_INSTALL) + install(FILES ${RAMSES_FRAMEWORK_API_FILES_HEADER} DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}/ramses/framework" COMPONENT ramses-sdk-devel) + + install(FILES ${RAMSES_CLIENT_API_FILES_HEADER} DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}/ramses/client" COMPONENT ramses-sdk-devel) + if(ramses-sdk_TEXT_SUPPORT) + install(FILES ${RAMSES_CLIENT_TEXT_API_FILES_HEADER} DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}/ramses/client/text" COMPONENT ramses-sdk-devel) + endif() + if(ramses-sdk_ENABLE_LOGIC) + install(FILES ${RAMSES_CLIENT_LOGIC_API_FILES_HEADER} DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}/ramses/client/logic" COMPONENT ramses-sdk-devel) + endif() + + if(ANY_WINDOW_TYPE_ENABLED) + install(FILES ${RAMSES_RENDERER_API_FILES_HEADER} DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}/ramses/renderer" COMPONENT ramses-sdk-devel) + endif() +endif() diff --git a/client/ramses-client-api/include/ramses-client-api/Appearance.h b/include/ramses/client/Appearance.h similarity index 56% rename from client/ramses-client-api/include/ramses-client-api/Appearance.h rename to include/ramses/client/Appearance.h index c262562c3..75e964ef7 100644 --- a/client/ramses-client-api/include/ramses-client-api/Appearance.h +++ b/include/ramses/client/Appearance.h @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_APPEARANCE_H -#define RAMSES_APPEARANCE_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-framework-api/AppearanceEnums.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/AppearanceEnums.h" +#include "ramses/framework/DataTypes.h" namespace ramses { + namespace internal + { + class AppearanceImpl; + } + class UniformInput; class DataObject; class TextureSampler; @@ -29,21 +33,20 @@ namespace ramses * as int (0 is false, anything else is true) - similar to OpenGL conventions. * @ingroup CoreAPI */ - class Appearance : public SceneObject + class RAMSES_API Appearance : public SceneObject { public: /** * @brief Sets blending factors for source/destination color/alpha. - * Blending operations need to be set as well in order to enable blending. + * Blending operations need to be set as well in order to enable blending (see #ramses::Appearance::setBlendingOperations()). * - * @param[in] srcColor Source color blending factor - * @param[in] destColor Destination color blending factor - * @param[in] srcAlpha Source alpha blending factor - * @param[in] destAlpha Destination alpha blending factor - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] srcColor Source color blending factor, default: #ramses::EBlendFactor::SrcAlpha + * @param[in] destColor Destination color blending factor, default: #ramses::EBlendFactor::OneMinusSrcAlpha + * @param[in] srcAlpha Source alpha blending factor, default: #ramses::EBlendFactor::One + * @param[in] destAlpha Destination alpha blending factor, default: #ramses::EBlendFactor::One + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha); + bool setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha); /** * @brief Gets blending factors for source/destination color/alpha. @@ -52,108 +55,99 @@ namespace ramses * @param[out] destColor Destination color blending factor * @param[out] srcAlpha Source alpha blending factor * @param[out] destAlpha Destination alpha blending factor - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const; + bool getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const; /** * @brief Sets blending operation for color and alpha. - * Blending factors need to be set as well in order to enable blending. + * To use blending also set the correct blending factors (see #ramses::Appearance::setBlendingFactors()). + * Blending operations are disabled by default * - * @param[in] operationColor Blending operation for color - * @param[in] operationAlpha Blending operation for alpha - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] operationColor Blending operation for color, default: #ramses::EBlendOperation::Disabled + * @param[in] operationAlpha Blending operation for alpha, default: #ramses::EBlendOperation::Disabled + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha); + bool setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha); /** * @brief Gets blending operation for color and alpha. * * @param[out] operationColor Blending operation for color * @param[out] operationAlpha Blending operation for alpha - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const; + bool getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const; /** * @brief Sets blending color that can be used as blending color constant for some blending factors. * The default value is (0,0,0,0) * * @param[in] color RGBA channels of blending color to set ([0;1] range) - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setBlendingColor(const vec4f& color); + bool setBlendingColor(const vec4f& color); /** * @brief Gets blending color set via #setBlendingColor * * @param[out] color RGBA channels of blending color ([0;1] range) - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getBlendingColor(vec4f& color) const; + bool getBlendingColor(vec4f& color) const; /** * @brief Enables or disables writing to depth buffer. + * The default value is #ramses::EDepthWrite::Enabled * * @param[in] mode Flag denoting enabling or disabling depth writes. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setDepthWrite(EDepthWrite mode); + bool setDepthWrite(EDepthWrite mode); /** * @brief Gets the current state of writing to depth buffer. * @param[out] mode Depth write mode - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getDepthWriteMode(EDepthWrite& mode) const; + bool getDepthWriteMode(EDepthWrite& mode) const; /** * @brief Sets depth comparison function. - * Depth writing has to be enabled in order for this to have any effect. - * Default depth comparison function is less or equal. + * Default depth comparison function is #ramses::EDepthFunc::LessEqual. * * @param[in] func Depth comparison function to be used - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setDepthFunction(EDepthFunc func); + bool setDepthFunction(EDepthFunc func); /** * @brief Gets depth comparison function. * * @param[out] func Depth comparison function to be used - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getDepthFunction(EDepthFunc& func) const; + bool getDepthFunction(EDepthFunc& func) const; /** * @brief Enables or disables scissor test and sets region for scissor test + * Scissor test is disabled by default (#ramses::EScissorTest::Disabled) * * @param[in] state Flag denoting enabling or disabling scissor test. * @param[in] x Offset of scissor region on x-axis. * @param[in] y Offset of scissor region on y-axis. * @param[in] width Width of scissor region. * @param[in] height Height of scissor region. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setScissorTest(EScissorTest state, int16_t x, int16_t y, uint16_t width, uint16_t height); + bool setScissorTest(EScissorTest state, int16_t x, int16_t y, uint16_t width, uint16_t height); /** * @brief Gets the current state of scissor test. * @param[out] state State of scissor test - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getScissorTestState(EScissorTest& state) const; + bool getScissorTestState(EScissorTest& state) const; /** * @brief Gets region for scissor test @@ -162,22 +156,20 @@ namespace ramses * @param[out] y Offset of scissor region on y-axis. * @param[out] width Width of scissor region. * @param[out] height Height of scissor region. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const; + bool getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const; /** * @brief Sets stencil function, reference and mask value for stencil testing. - * Stencil is disabled by default. + * Stencil test is disabled by default. * - * @param[in] func Stencil function to be used - * @param[in] ref Stencil reference value to be used - * @param[in] mask Stencil mask value to be used - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] func Stencil function to be used, default: #ramses::EStencilFunc::Disabled + * @param[in] ref Stencil reference value to be used, default: 0 + * @param[in] mask Stencil mask value to be used, default: 0xff + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setStencilFunction(EStencilFunc func, uint8_t ref, uint8_t mask); + bool setStencilFunction(EStencilFunc func, uint8_t ref, uint8_t mask); /** * @brief Gets stencil function, reference and mask value @@ -185,22 +177,20 @@ namespace ramses * @param[out] func Stencil function currently set * @param[out] ref Stencil reference value currently set * @param[out] mask Stencil mask value currently set - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getStencilFunction(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const; + bool getStencilFunction(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const; /** * @brief Sets stencil operations for stencil testing. - * Default stencil operation values are keep. + * Default stencil operation values are #ramses::EStencilOperation::Keep. * * @param[in] sfail Stencil operation when stencil test fails * @param[in] dpfail Stencil operation when the stencil test passes, but the depth test fails * @param[in] dppass Stencil operation when both the stencil test and the depth test pass - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass); + bool setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass); /** * @brief Gets stencil operations @@ -208,20 +198,18 @@ namespace ramses * @param[out] sfail Stencil operation when stencil test fails * @param[out] dpfail Stencil operation when the stencil test passes, but the depth test fails * @param[out] dppass Stencil operation when both the stencil test and the depth test pass - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const; + bool getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const; /** * @brief Sets the culling mode indicating which side of mesh will be removed before rasterization. - * Default culling mode is BackFaceCulling. + * Default culling mode is #ramses::ECullMode::BackFacing. * * @param[in] mode Culling mode to be used. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setCullingMode(ECullMode mode); + bool setCullingMode(ECullMode mode); /** * @brief Sets the draw mode indicating by which primitive the mesh will be rendered @@ -230,28 +218,25 @@ namespace ramses * as draw mode automatically when #Appearance is created. * * @param[in] mode Draw mode to be used. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setDrawMode(EDrawMode mode); + bool setDrawMode(EDrawMode mode); /** * @brief Gets the culling mode indicating which side of mesh will be removed before rasterization. * * @param[out] mode Culling mode to be used. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getCullingMode(ECullMode& mode) const; + bool getCullingMode(ECullMode& mode) const; /** * @brief Gets the draw mode indicating by which primitive the mesh will be rendered * * @param[out] mode draw mode to be used. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getDrawMode(EDrawMode& mode) const; + bool getDrawMode(EDrawMode& mode) const; /** * @brief Sets color write mask. @@ -262,10 +247,9 @@ namespace ramses * @param[in] writeGreen Enable/disable flag for green channel * @param[in] writeBlue Enable/disable flag for blue channel * @param[in] writeAlpha Enable/disable flag for alpha channel - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha); + bool setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha); /** * @brief Gets color write mask. @@ -274,10 +258,9 @@ namespace ramses * @param[out] writeGreen Enable/disable flag for green channel * @param[out] writeBlue Enable/disable flag for blue channel * @param[out] writeAlpha Enable/disable flag for alpha channel - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const; + bool getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const; /** * @brief Sets value to uniform input. @@ -287,11 +270,10 @@ namespace ramses * * @param[in] input The effect uniform input to set the value to * @param[in] value The value to set - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t setInputValue(const UniformInput& input, T&& value); + bool setInputValue(const UniformInput& input, T&& value); /** * @brief Sets value(s) to uniform input. @@ -303,11 +285,10 @@ namespace ramses * @param[in] elementCount The number of elements to use from \c values. * Must match #ramses::UniformInput::getElementCount. * @param[in] values Pointer the the values, must contain at least \c elementCount elements. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t setInputValue(const UniformInput& input, size_t elementCount, const T* values); + bool setInputValue(const UniformInput& input, size_t elementCount, const T* values); /** * @brief Gets value of uniform input. @@ -317,11 +298,10 @@ namespace ramses * * @param[in] input The effect uniform input * @param[out] value The value - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t getInputValue(const UniformInput& input, T& value) const; + bool getInputValue(const UniformInput& input, T& value) const; /** * @brief Gets value(s) of uniform input. @@ -333,41 +313,37 @@ namespace ramses * @param[in] elementCount The number of elements to copy to \c valuesOut. * Must match #ramses::UniformInput::getElementCount. * @param[out] valuesOut location where the values are copied to - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t getInputValue(const UniformInput& input, size_t elementCount, T* valuesOut) const; + bool getInputValue(const UniformInput& input, size_t elementCount, T* valuesOut) const; /** * @brief Sets texture sampler to the input * * @param[in] input The effect uniform input to set the value to * @param[in] textureSampler The texture sampler - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputTexture(const UniformInput& input, const TextureSampler& textureSampler); + bool setInputTexture(const UniformInput& input, const TextureSampler& textureSampler); /** * @brief Sets multisampled texture sampler to the input * * @param[in] input The multisampled texture sampler uniform input to set the value to * @param[in] textureSampler The multisampled texture sampler - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputTexture(const UniformInput& input, const TextureSamplerMS& textureSampler); + bool setInputTexture(const UniformInput& input, const TextureSamplerMS& textureSampler); /** * @brief Sets external texture sampler to the input * * @param[in] input The external texture sampler uniform input to set the value to * @param[in] textureSampler The external texture sampler - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputTexture(const UniformInput& input, const TextureSamplerExternal& textureSampler); + bool setInputTexture(const UniformInput& input, const TextureSamplerExternal& textureSampler); /** * @brief Gets texture sampler currently set to the input @@ -375,10 +351,9 @@ namespace ramses * @param[in] input The effect uniform input * @param[out] textureSampler Will set texture sampler pointer to the TextureSampler object set to the uniform input, * nullptr if none set or there was an error - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getInputTexture(const UniformInput& input, const TextureSampler*& textureSampler) const; + bool getInputTexture(const UniformInput& input, const TextureSampler*& textureSampler) const; /** * @brief Gets texture sampler currently set to the input @@ -386,10 +361,9 @@ namespace ramses * @param[in] input The effect uniform input * @param[out] textureSampler Will set texture sampler pointer to the TextureSamplerMS object set to the uniform input, * nullptr if none set or there was an error - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getInputTextureMS(const UniformInput& input, const TextureSamplerMS*& textureSampler) const; + bool getInputTextureMS(const UniformInput& input, const TextureSamplerMS*& textureSampler) const; /** * @brief Gets texture sampler currently set to the input @@ -397,10 +371,9 @@ namespace ramses * @param[in] input The effect uniform input * @param[out] textureSampler Will set texture sampler pointer to the TextureSamplerExternal object set to the uniform input, * nullptr if none set or there was an error - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getInputTextureExternal(const UniformInput& input, const TextureSamplerExternal*& textureSampler) const; + bool getInputTextureExternal(const UniformInput& input, const TextureSamplerExternal*& textureSampler) const; /** * @brief Bind a DataObject to the Appearance's uniform input. @@ -414,10 +387,9 @@ namespace ramses * * @param[in] input The effect uniform input to bind the DataObject to * @param[in] dataObject The DataObject to be bound - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t bindInput(const UniformInput& input, const DataObject& dataObject); + bool bindInput(const UniformInput& input, const DataObject& dataObject); /** * @brief Unbind a previously bound DataObject from the Appearance's uniform input. @@ -426,10 +398,9 @@ namespace ramses * or another DataObject can be bound. * * @param[in] input The effect uniform input to unbind the DataObject from - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t unbindInput(const UniformInput& input); + bool unbindInput(const UniformInput& input); /** * @brief Check whether a uniform input has any DataObject bound to it. @@ -437,7 +408,7 @@ namespace ramses * @param[in] input The effect uniform input to check * @return \c true if there is any DataObject bound to the input, false otherwise */ - [[nodiscard]] RAMSES_API bool isInputBound(const UniformInput& input) const; + [[nodiscard]] bool isInputBound(const UniformInput& input) const; /** * @brief Gets the data object bound to a uniform input. @@ -445,67 +416,75 @@ namespace ramses * @param[in] input The effect uniform input to get the bound data object for * @return \c The data object bound the uniform input if existing, otherwise returns nullptr */ - [[nodiscard]] RAMSES_API const DataObject* getDataObjectBoundToInput(const UniformInput& input) const; + [[nodiscard]] const DataObject* getDataObjectBoundToInput(const UniformInput& input) const; /** * @brief Gets the effect used to create this appearance * * @return The effect used to create the appearance. */ - [[nodiscard]] RAMSES_API const Effect& getEffect() const; + [[nodiscard]] const Effect& getEffect() const; /** - * Stores internal data for implementation specifics of Appearance. - */ - class AppearanceImpl& m_impl; + * Get the internal data for implementation specifics of Appearance. + */ + [[nodiscard]] internal::AppearanceImpl& impl(); + + /** + * Get the internal data for implementation specifics of Appearance. + */ + [[nodiscard]] const internal::AppearanceImpl& impl() const; protected: /** * @brief Scene is the factory for creating Appearance instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of Appearance * * @param[in] impl Internal data for implementation specifics of Appearance (sink - instance becomes owner) */ - explicit Appearance(std::unique_ptr impl); + explicit Appearance(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Appearance. + */ + internal::AppearanceImpl& m_impl; private: /// Internal implementation of #setInputValue - template RAMSES_API status_t setInputValueInternal(const UniformInput& input, T&& value); + template bool setInputValueInternal(const UniformInput& input, T&& value); /// Internal implementation of #setInputValue - template RAMSES_API status_t setInputValueInternal(const UniformInput& input, size_t elementCount, const T* values); + template bool setInputValueInternal(const UniformInput& input, size_t elementCount, const T* values); /// Internal implementation of #getInputValue - template RAMSES_API status_t getInputValueInternal(const UniformInput& input, T& value) const; + template bool getInputValueInternal(const UniformInput& input, T& value) const; /// Internal implementation of #getInputValue - template RAMSES_API status_t getInputValueInternal(const UniformInput& input, size_t elementCount, T* valuesOut) const; + template bool getInputValueInternal(const UniformInput& input, size_t elementCount, T* valuesOut) const; }; - template status_t Appearance::setInputValue(const UniformInput& input, T&& value) + template bool Appearance::setInputValue(const UniformInput& input, T&& value) { static_assert(IsUniformInputDataType(), "Unsupported uniform data type!"); return setInputValueInternal(input, std::forward(value)); } - template status_t Appearance::setInputValue(const UniformInput& input, size_t elementCount, const T* values) + template bool Appearance::setInputValue(const UniformInput& input, size_t elementCount, const T* values) { static_assert(IsUniformInputDataType(), "Unsupported uniform data type!"); return setInputValueInternal(input, elementCount, values); } - template status_t Appearance::getInputValue(const UniformInput& input, T& value) const + template bool Appearance::getInputValue(const UniformInput& input, T& value) const { static_assert(IsUniformInputDataType(), "Unsupported uniform data type!"); return getInputValueInternal(input, value); } - template status_t Appearance::getInputValue(const UniformInput& input, size_t elementCount, T* valuesOut) const + template bool Appearance::getInputValue(const UniformInput& input, size_t elementCount, T* valuesOut) const { static_assert(IsUniformInputDataType(), "Unsupported uniform data type!"); return getInputValueInternal(input, elementCount, valuesOut); } } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/ArrayBuffer.h b/include/ramses/client/ArrayBuffer.h similarity index 71% rename from client/ramses-client-api/include/ramses-client-api/ArrayBuffer.h rename to include/ramses/client/ArrayBuffer.h index 30e2a1d3c..504f9658b 100644 --- a/client/ramses-client-api/include/ramses-client-api/ArrayBuffer.h +++ b/include/ramses/client/ArrayBuffer.h @@ -6,26 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ARRAYBUFFER_H -#define RAMSES_ARRAYBUFFER_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-framework-api/EDataType.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/EDataType.h" +#include "ramses/framework/DataTypes.h" namespace ramses { + namespace internal + { + class ArrayBufferImpl; + } + /** * @ingroup CoreAPI - * @brief The ArrayBuffer is a data object used to provide vertex or index data to #ramses::GeometryBinding::setInputBuffer - * and #ramses::GeometryBinding::setIndices. The buffer data of an ArrayBuffer is not filled initially and can be fully + * @brief The ArrayBuffer is a data object used to provide vertex or index data to #ramses::Geometry::setInputBuffer + * and #ramses::Geometry::setIndices. The buffer data of an ArrayBuffer is not filled initially and can be fully * or partially updated in between scene flushes. * * @details If an #ArrayBuffer object is created with type #ramses::EDataType::ByteBlob then an element * is defined as one byte, rather than a logical vertex element. Hence, all functions of #ArrayBuffer * referring to element refer to a single byte within byte array. */ - class ArrayBuffer : public SceneObject + class RAMSES_API ArrayBuffer : public SceneObject { public: /** @@ -45,11 +49,10 @@ namespace ramses * @param bufferData Pointer in memory to the data provided for update. The function makes * a copy of the data into ArrayBuffer data structures. ArrayBuffer * object does not take ownership of the memory data passed to it. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t updateData(uint32_t firstElement, uint32_t numElements, const T* bufferData); + bool updateData(uint32_t firstElement, uint32_t numElements, const T* bufferData); /** * @brief Returns the maximum number of data elements that can be stored in the data buffer. @@ -59,7 +62,7 @@ namespace ramses * * @return Maximum number of elements */ - [[nodiscard]] RAMSES_API uint32_t getMaximumNumberOfElements() const; + [[nodiscard]] uint32_t getMaximumNumberOfElements() const; /** * @brief Returns the used number of data elements. @@ -70,14 +73,14 @@ namespace ramses * * @return Used size in number of elements */ - [[nodiscard]] RAMSES_API uint32_t getUsedNumberOfElements() const; + [[nodiscard]] uint32_t getUsedNumberOfElements() const; /** * @brief Returns the data type associated with the buffer data * * @return data type of buffer data */ - [[nodiscard]] RAMSES_API EDataType getDataType() const; + [[nodiscard]] EDataType getDataType() const; /** * @brief Copies the data of the data buffer into a user-provided buffer. @@ -89,48 +92,55 @@ namespace ramses * * @param[out] buffer The buffer where the buffer data will be copied into * @param[in] numElements The number of elements to copy - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t getData(T* buffer, uint32_t numElements) const; + bool getData(T* buffer, uint32_t numElements) const; /** - * Stores internal data for implementation specifics of ArrayBuffer. - */ - class ArrayBufferImpl& m_impl; + * Get the internal data for implementation specifics of ArrayBuffer. + */ + [[nodiscard]] internal::ArrayBufferImpl& impl(); + + /** + * Get the internal data for implementation specifics of ArrayBuffer. + */ + [[nodiscard]] const internal::ArrayBufferImpl& impl() const; protected: /** * @brief Scene is the factory for creating ArrayBuffer instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for ArrayBuffer. * * @param[in] impl Internal data for implementation specifics of ArrayBuffer (sink - instance becomes owner) */ - explicit ArrayBuffer(std::unique_ptr impl); + explicit ArrayBuffer(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of ArrayBuffer. + */ + internal::ArrayBufferImpl& m_impl; private: /// Internal implementation of #updateData - template RAMSES_API status_t updateDataInternal(uint32_t firstElement, uint32_t numElements, const T* bufferData); + template bool updateDataInternal(uint32_t firstElement, uint32_t numElements, const T* bufferData); /// Internal implementation of #getData - template RAMSES_API status_t getDataInternal(T* buffer, uint32_t numElements) const; + template bool getDataInternal(T* buffer, uint32_t numElements) const; }; - template status_t ArrayBuffer::updateData(uint32_t firstElement, uint32_t numElements, const T* bufferData) + template bool ArrayBuffer::updateData(uint32_t firstElement, uint32_t numElements, const T* bufferData) { static_assert(IsArrayResourceDataType(), "Unsupported data type!"); return updateDataInternal(firstElement, numElements, bufferData); } - template status_t ArrayBuffer::getData(T* buffer, uint32_t numElements) const + template bool ArrayBuffer::getData(T* buffer, uint32_t numElements) const { static_assert(IsArrayResourceDataType(), "Unsupported data type!"); return getDataInternal(buffer, numElements); } } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/ArrayResource.h b/include/ramses/client/ArrayResource.h similarity index 64% rename from client/ramses-client-api/include/ramses-client-api/ArrayResource.h rename to include/ramses/client/ArrayResource.h index 60e9d6939..f75d6f85d 100644 --- a/client/ramses-client-api/include/ramses-client-api/ArrayResource.h +++ b/include/ramses/client/ArrayResource.h @@ -6,54 +6,66 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ARRAYRESOURCE_H -#define RAMSES_ARRAYRESOURCE_H +#pragma once -#include "ramses-client-api/Resource.h" -#include "ramses-framework-api/EDataType.h" +#include "ramses/client/Resource.h" +#include "ramses/framework/EDataType.h" namespace ramses { + namespace internal + { + class ArrayResourceImpl; + } + /** * @ingroup CoreAPI * @brief The #ArrayResource stores a data array of a given type. The data is immutable. - * The resource can be used as input for a #ramses::GeometryBinding. + * The resource can be used as input for a #ramses::Geometry. * * @details If an #ArrayResource object is created with type #ramses::EDataType::ByteBlob then an element * is defined as one byte, rather than a logical vertex element. Hence, number of elements is * the same as size in bytes. */ - class ArrayResource : public Resource + class RAMSES_API ArrayResource : public Resource { public: - /** - * Stores internal data for implementation specifics of ArrayResource. - */ - class ArrayResourceImpl& m_impl; - /** * @brief Returns number of elements of the array. */ - [[nodiscard]] RAMSES_API uint32_t getNumberOfElements() const; + [[nodiscard]] uint32_t getNumberOfElements() const; /** * @brief Returns the data type of the data array. */ - [[nodiscard]] RAMSES_API EDataType getDataType() const; + [[nodiscard]] EDataType getDataType() const; + + /** + * Get the internal data for implementation specifics of ArrayResource. + */ + [[nodiscard]] internal::ArrayResourceImpl& impl(); + + /** + * Get the internal data for implementation specifics of ArrayResource. + */ + [[nodiscard]] const internal::ArrayResourceImpl& impl() const; protected: /** * @brief Scene is the factory for creating ArrayResource instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of ArrayResource * * @param[in] impl Internal data for implementation specifics of ArrayResource (sink - instance becomes owner) */ - explicit ArrayResource(std::unique_ptr impl); + explicit ArrayResource(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of ArrayResource. + */ + internal::ArrayResourceImpl& m_impl; }; } - -#endif diff --git a/include/ramses/client/AttributeInput.h b/include/ramses/client/AttributeInput.h new file mode 100644 index 000000000..177fcf4e7 --- /dev/null +++ b/include/ramses/client/AttributeInput.h @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/client/EffectInput.h" +#include "ramses/client/EffectInputSemantic.h" + +namespace ramses +{ + namespace internal + { + class EffectImpl; + } + + /** + * @ingroup CoreAPI + * @brief The AttributeInput is a description of an attribute effect input + */ + class RAMSES_API AttributeInput : public EffectInput + { + public: + /** + * @brief Returns the effect input semantics. + * + * @return Effect input semantics + */ + [[nodiscard]] EEffectAttributeSemantic getSemantics() const; + + protected: + /** + * @brief Default constructor of AttributeInput. + * The default constructor is forbidden from public access. Users are not + * expected to create AttributeInput objects. Objects can only be created + * via copy and move constructors, or obtained from #ramses::Effect (see + * #ramses::Effect::getAttributeInput and #ramses::Effect::findAttributeInput). + */ + AttributeInput(); + + friend class internal::EffectImpl; + }; +} diff --git a/client/ramses-client-api/include/ramses-client-api/BlitPass.h b/include/ramses/client/BlitPass.h similarity index 72% rename from client/ramses-client-api/include/ramses-client-api/BlitPass.h rename to include/ramses/client/BlitPass.h index a5e477afd..ddf33c163 100644 --- a/client/ramses-client-api/include/ramses-client-api/BlitPass.h +++ b/include/ramses/client/BlitPass.h @@ -6,13 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BLITPASS_H -#define RAMSES_BLITPASS_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class BlitPassImpl; + } + class RenderBuffer; /** @@ -22,7 +26,7 @@ namespace ramses * using a render order, which is also shared with RenderPass objects, i.e, BlitPass and RenderPass objects * can all be ordered relative to each other. */ - class BlitPass : public SceneObject + class RAMSES_API BlitPass : public SceneObject { public: /** @@ -30,14 +34,14 @@ namespace ramses * * @return The source render buffer. */ - [[nodiscard]] RAMSES_API const RenderBuffer& getSourceRenderBuffer() const; + [[nodiscard]] const RenderBuffer& getSourceRenderBuffer() const; /** * @brief Get the destination render buffer used for blitting. * * @return The destination render buffer. */ - [[nodiscard]] RAMSES_API const RenderBuffer& getDestinationRenderBuffer() const; + [[nodiscard]] const RenderBuffer& getDestinationRenderBuffer() const; /** * @brief Set the region for blitting from source and destination render buffers. @@ -52,10 +56,9 @@ namespace ramses * @param destinationY Offset on y-axis for blitting to destination render buffer * @param width Width of blitting region, used for source and destination blitting regions * @param height Height of blitting region, used for source and destination blitting regions - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height); + bool setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height); /** * @brief Get the blitting region in source and destination render buffers. @@ -68,7 +71,7 @@ namespace ramses * @param[out] height Height of blitting region, used for source and destination blitting regions * */ - RAMSES_API void getBlittingRegion(uint32_t& sourceX, uint32_t& sourceY, uint32_t& destinationX, uint32_t& destinationY, uint32_t& width, uint32_t& height) const; + void getBlittingRegion(uint32_t& sourceX, uint32_t& sourceY, uint32_t& destinationX, uint32_t& destinationY, uint32_t& width, uint32_t& height) const; /** * @brief Set the render order for the blit pass. @@ -79,52 +82,58 @@ namespace ramses * The default render order is Zero. * * @param renderOrder Render order used for ordering the blit pass - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRenderOrder(int32_t renderOrder); + bool setRenderOrder(int32_t renderOrder); /** * @brief Get the render order of this blit pass. * * @return The render order of this blit pass. */ - [[nodiscard]] RAMSES_API int32_t getRenderOrder() const; + [[nodiscard]] int32_t getRenderOrder() const; /** * @brief Enable/Disable blit pass * * @param enable The enable flag which indicates if the blit pass is rendered (Default:true) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setEnabled(bool enable); + bool setEnabled(bool enable); /** * @brief Get the enable state of the blit pass * * @return Indicates if the blit pass is enabled */ - [[nodiscard]] RAMSES_API bool isEnabled() const; + [[nodiscard]] bool isEnabled() const; /** - * Stores internal data for implementation specifics of BlitPass. - */ - class BlitPassImpl& m_impl; + * Get the internal data for implementation specifics of BlitPass. + */ + [[nodiscard]] internal::BlitPassImpl& impl(); + + /** + * Get the internal data for implementation specifics of BlitPass. + */ + [[nodiscard]] const internal::BlitPassImpl& impl() const; protected: /** * @brief Scene is the factory for creating BlitPass instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for BlitPass. * * @param impl Internal data for implementation specifics of BlitPass (sink - instance becomes owner) */ - explicit BlitPass(std::unique_ptr impl); + explicit BlitPass(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of BlitPass. + */ + internal::BlitPassImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Camera.h b/include/ramses/client/Camera.h similarity index 79% rename from client/ramses-client-api/include/ramses-client-api/Camera.h rename to include/ramses/client/Camera.h index d53c16d64..81a4746a4 100644 --- a/client/ramses-client-api/include/ramses-client-api/Camera.h +++ b/include/ramses/client/Camera.h @@ -6,13 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CAMERA_H -#define RAMSES_CAMERA_H +#pragma once -#include "ramses-client-api/Node.h" +#include "ramses/client/Node.h" namespace ramses { + namespace internal + { + class CameraNodeImpl; + } + class DataObject; /** @@ -21,7 +25,7 @@ namespace ramses * defined by the client application. It is also a #Node with transformation. * @details A valid camera for rendering must have viewport and frustum set. */ - class Camera : public Node + class RAMSES_API Camera : public Node { public: /** @@ -46,10 +50,9 @@ namespace ramses * Top opening angle if camera is perspective. * @param[in] nearPlane Near plane of the camera frustum. * @param[in] farPlane Far plane of the camera frustum. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane); + bool setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane); /** * @brief Sets the viewport to be used when rendering with this camera. @@ -68,10 +71,9 @@ namespace ramses * @param[in] y vertical offset of the viewport rectangle in pixels (zero = bottommost pixel) * @param[in] width horizontal size of the viewport rectangle in pixels (min 1, max 32768) * @param[in] height vertical size of the viewport rectangle in pixels (min 1, max 32768) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); + bool setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); /** * @brief Returns the horizontal offset of the viewport in pixels relative to lower left corner of destination render buffer. @@ -80,7 +82,7 @@ namespace ramses * * @return horizontal offset of the viewport in pixels */ - [[nodiscard]] RAMSES_API int32_t getViewportX() const; + [[nodiscard]] int32_t getViewportX() const; /** * @brief Returns the vertical offset of the viewport in pixels relative to lower left corner of destination render buffer. @@ -89,7 +91,7 @@ namespace ramses * * @return vertical offset of the viewport in pixels */ - [[nodiscard]] RAMSES_API int32_t getViewportY() const; + [[nodiscard]] int32_t getViewportY() const; /** * @brief Returns the viewport width in pixels @@ -98,7 +100,7 @@ namespace ramses * * @return viewport width in pixels */ - [[nodiscard]] RAMSES_API uint32_t getViewportWidth() const; + [[nodiscard]] uint32_t getViewportWidth() const; /** * @brief Returns the viewport height in pixels @@ -107,7 +109,7 @@ namespace ramses * * @return viewport height in pixels */ - [[nodiscard]] RAMSES_API uint32_t getViewportHeight() const; + [[nodiscard]] uint32_t getViewportHeight() const; /** * @brief Returns the left plane of the camera frustum @@ -116,7 +118,7 @@ namespace ramses * * @return the left plane of the Camera */ - [[nodiscard]] RAMSES_API float getLeftPlane() const; + [[nodiscard]] float getLeftPlane() const; /** * @brief Returns the right plane of the camera frustum @@ -125,7 +127,7 @@ namespace ramses * * @return the right plane of the #Camera */ - [[nodiscard]] RAMSES_API float getRightPlane() const; + [[nodiscard]] float getRightPlane() const; /** * @brief Returns the bottom plane of the camera frustum @@ -134,7 +136,7 @@ namespace ramses * * @return the bottom plane of the #Camera */ - [[nodiscard]] RAMSES_API float getBottomPlane() const; + [[nodiscard]] float getBottomPlane() const; /** * @brief Returns the top plane of the camera frustum @@ -143,7 +145,7 @@ namespace ramses * * @return the top plane of the #Camera */ - [[nodiscard]] RAMSES_API float getTopPlane() const; + [[nodiscard]] float getTopPlane() const; /** * @brief Returns the near plane of the camera frustum @@ -152,7 +154,7 @@ namespace ramses * * @return the near plane of the #Camera */ - [[nodiscard]] RAMSES_API float getNearPlane() const; + [[nodiscard]] float getNearPlane() const; /** * @brief Returns the far plane of the camera frustum @@ -161,7 +163,7 @@ namespace ramses * * @return the far plane of the #Camera */ - [[nodiscard]] RAMSES_API float getFarPlane() const; + [[nodiscard]] float getFarPlane() const; /** * @brief Gets projection matrix based on camera parameters. @@ -169,10 +171,9 @@ namespace ramses * were set and are valid. * * @param[out] projectionMatrix Will be filled with the projection matrix 4x4 column-major - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - [[nodiscard]] RAMSES_API status_t getProjectionMatrix(matrix44f& projectionMatrix) const; + [[nodiscard]] bool getProjectionMatrix(matrix44f& projectionMatrix) const; /** * @brief Binds a #ramses::DataObject to be used as source for viewport offset values. @@ -182,10 +183,9 @@ namespace ramses * see #ramses::DataObject for possible use cases. * * @param[in] offsetData Data object of type #ramses::EDataType::Vector2I that will be used as source for viewport offset values - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t bindViewportOffset(const DataObject& offsetData); + bool bindViewportOffset(const DataObject& offsetData); /** * @brief Binds a #ramses::DataObject to be used as source for viewport size values. @@ -196,10 +196,9 @@ namespace ramses * see #ramses::DataObject for possible use cases. * * @param[in] sizeData Data object of type #ramses::EDataType::Vector2I that will be used as source for viewport size values - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t bindViewportSize(const DataObject& sizeData); + bool bindViewportSize(const DataObject& sizeData); /** * @brief Binds #ramses::DataObject to be used as source for frustum planes values. @@ -216,71 +215,75 @@ namespace ramses * The (x, y, z, w) floats represent (left, right, bottom, top) frustum planes. * @param[in] nearFarPlanesData Data object of type #ramses::EDataType::Vector2F that will be used as source for frustum planes values. * The (x, y) floats represent (near, far) frustum planes. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarPlanesData); + bool bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarPlanesData); /** * @brief Unbinds any bound #ramses::DataObject from viewport offset (see #bindViewportOffset). * Does nothing if no #ramses::DataObject bound. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t unbindViewportOffset(); + bool unbindViewportOffset(); /** * @brief Unbinds any bound #ramses::DataObject from viewport size (see #bindViewportSize). * Does nothing if no #ramses::DataObject bound. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t unbindViewportSize(); + bool unbindViewportSize(); /** * @brief Unbinds any bound #ramses::DataObject from frustum planes (see #bindFrustumPlanes). * Does nothing if no #ramses::DataObject bound. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t unbindFrustumPlanes(); + bool unbindFrustumPlanes(); /** * @brief Checks if there is a #ramses::DataObject bound to viewport offset (see #bindViewportOffset). * @return True if there is any #ramses::DataObject bound, false otherwise. */ - [[nodiscard]] RAMSES_API bool isViewportOffsetBound() const; + [[nodiscard]] bool isViewportOffsetBound() const; /** * @brief Checks if there is a #ramses::DataObject bound to viewport size (see #bindViewportSize). * @return True if there is any #ramses::DataObject bound, false otherwise. */ - [[nodiscard]] RAMSES_API bool isViewportSizeBound() const; + [[nodiscard]] bool isViewportSizeBound() const; /** * @brief Checks if there is a #ramses::DataObject bound to viewport size (see #bindFrustumPlanes). * @return True if there is any #ramses::DataObject bound, false otherwise. */ - [[nodiscard]] RAMSES_API bool isFrustumPlanesBound() const; + [[nodiscard]] bool isFrustumPlanesBound() const; /** - * Stores internal data for implementation specifics of Camera. - */ - class CameraNodeImpl& m_impl; + * Get the internal data for implementation specifics of Camera. + */ + [[nodiscard]] internal::CameraNodeImpl& impl(); + + /** + * Get the internal data for implementation specifics of Camera. + */ + [[nodiscard]] const internal::CameraNodeImpl& impl() const; protected: /** * @brief Scene is the factory for creating #Camera instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for #Camera. * * @param[in] impl Internal data for implementation specifics of #Camera (sink - instance becomes owner) */ - explicit Camera(std::unique_ptr impl); + explicit Camera(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Camera. + */ + internal::CameraNodeImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/ClientObject.h b/include/ramses/client/ClientObject.h similarity index 60% rename from client/ramses-client-api/include/ramses-client-api/ClientObject.h rename to include/ramses/client/ClientObject.h index 307d979ba..04ad5c72e 100644 --- a/client/ramses-client-api/include/ramses-client-api/ClientObject.h +++ b/include/ramses/client/ClientObject.h @@ -6,24 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTOBJECT_H -#define RAMSES_CLIENTOBJECT_H +#pragma once -#include "ramses-client-api/RamsesObject.h" +#include "ramses/framework/RamsesObject.h" namespace ramses { + namespace internal + { + class ClientObjectImpl; + } + /** * @ingroup CoreAPI * @brief The ClientObject is a base class for all client API objects owned by a RamsesClient. */ - class ClientObject : public RamsesObject + class RAMSES_API ClientObject : public RamsesObject { public: /** - * Stores internal data for implementation specifics of ClientObject. - */ - class ClientObjectImpl& m_impl; + * Get the internal data for implementation specifics of ClientObject. + */ + [[nodiscard]] internal::ClientObjectImpl& impl(); + + /** + * Get the internal data for implementation specifics of ClientObject. + */ + [[nodiscard]] const internal::ClientObjectImpl& impl() const; protected: /** @@ -31,8 +40,11 @@ namespace ramses * * @param[in] impl Internal data for implementation specifics of ClientObject (sink - instance becomes owner) */ - explicit ClientObject(std::unique_ptr impl); + explicit ClientObject(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of ClientObject. + */ + internal::ClientObjectImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/DataObject.h b/include/ramses/client/DataObject.h similarity index 68% rename from client/ramses-client-api/include/ramses-client-api/DataObject.h rename to include/ramses/client/DataObject.h index 3c3ed44c0..1bfbe9a80 100644 --- a/client/ramses-client-api/include/ramses-client-api/DataObject.h +++ b/include/ramses/client/DataObject.h @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAOBJECT_H -#define RAMSES_DATAOBJECT_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-framework-api/EDataType.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/EDataType.h" +#include "ramses/framework/DataTypes.h" namespace ramses { + namespace internal + { + class DataObjectImpl; + } + /** * @ingroup CoreAPI * @brief The DataObject is a data container for storing data within a scene. @@ -28,7 +32,7 @@ namespace ramses * (#ramses::Scene::createDataProvider and #ramses::Scene::createDataConsumer) see SDK examples for typical use cases. * A value set to a #DataObject is then propagated everywhere it is bound to and it is linked to. */ - class DataObject : public SceneObject + class RAMSES_API DataObject : public SceneObject { public: /** @@ -36,66 +40,72 @@ namespace ramses * * @return data type held in this #DataObject */ - [[nodiscard]] RAMSES_API EDataType getDataType() const; + [[nodiscard]] EDataType getDataType() const; + + /** + * Get the internal data for implementation specifics of DataObject. + */ + [[nodiscard]] internal::DataObjectImpl& impl(); + + /** + * Get the internal data for implementation specifics of DataObject. + */ + [[nodiscard]] const internal::DataObjectImpl& impl() const; /** * @brief Sets/updates the stored value. * @details Type of \c value must match #getDataType (see #ramses::GetEDataType). * * @param[in] value new value. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t setValue(T&& value); + bool setValue(T&& value); /** * @brief Gets the stored value. * @details Type of \c value must match #getDataType (see #ramses::GetEDataType). * * @param[out] value stored value. - * @return status == 0 for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ template - status_t getValue(T& value) const; - - /** - * Stores internal data for implementation specifics - */ - class DataObjectImpl& m_impl; + bool getValue(T& value) const; protected: /** * @brief Scene is the factory for creating DataObject instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of DataObject * * @param[in] impl Internal data for implementation specifics of DataObject */ - explicit DataObject(std::unique_ptr impl); + explicit DataObject(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics + */ + internal::DataObjectImpl& m_impl; private: /// Internal implementation of #setValue - template RAMSES_API status_t setValueInternal(T&& value); + template bool setValueInternal(T&& value); /// Internal implementation of #getValue - template RAMSES_API status_t getValueInternal(T& value) const; + template bool getValueInternal(T& value) const; }; - template status_t DataObject::setValue(T&& value) + template bool DataObject::setValue(T&& value) { static_assert(IsUniformInputDataType(), "Unsupported data type!"); return setValueInternal(std::forward(value)); } - template status_t DataObject::getValue(T& value) const + template bool DataObject::getValue(T& value) const { static_assert(IsUniformInputDataType(), "Unsupported data type!"); return getValueInternal(value); } } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Effect.h b/include/ramses/client/Effect.h similarity index 52% rename from client/ramses-client-api/include/ramses-client-api/Effect.h rename to include/ramses/client/Effect.h index 0c51fb88c..e1fe07951 100644 --- a/client/ramses-client-api/include/ramses-client-api/Effect.h +++ b/include/ramses/client/Effect.h @@ -6,18 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECT_H -#define RAMSES_EFFECT_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/Resource.h" -#include "ramses-client-api/EffectInputSemantic.h" -#include "ramses-framework-api/AppearanceEnums.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/Resource.h" +#include "ramses/client/EffectInputSemantic.h" +#include "ramses/framework/AppearanceEnums.h" +#include #include namespace ramses { + namespace internal + { + class EffectImpl; + } + class UniformInput; class AttributeInput; @@ -25,7 +30,7 @@ namespace ramses * @ingroup CoreAPI * @brief An effect describes how an object will be rendered to the screen. */ - class Effect : public Resource + class RAMSES_API Effect : public Resource { public: /** @@ -33,81 +38,69 @@ namespace ramses * * @return Number of uniform inputs */ - [[nodiscard]] RAMSES_API size_t getUniformInputCount() const; + [[nodiscard]] size_t getUniformInputCount() const; /** * @brief Gets number of attribute inputs. * * @return Number of attribute inputs */ - [[nodiscard]] RAMSES_API size_t getAttributeInputCount() const; + [[nodiscard]] size_t getAttributeInputCount() const; /** * @brief Gets uniform input at given index. * * @param[in] index Index of uniform input to retrieve - * @param[out] uniformInput Uniform input - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return #ramses::UniformInput if the index is valid, otherwise, std::nullopt */ - RAMSES_API status_t getUniformInput(size_t index, UniformInput& uniformInput) const; + [[nodiscard]] std::optional getUniformInput(size_t index) const; /** * @brief Gets attribute input at given index. * * @param[in] index Index of attribute input to retrieve - * @param[out] attributeInput Attribute input - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return #ramses::AttributeInput if the index is valid, otherwise, std::nullopt */ - RAMSES_API status_t getAttributeInput(size_t index, AttributeInput& attributeInput) const; + [[nodiscard]] std::optional getAttributeInput(size_t index) const; /** * @brief Finds uniform input by input name. * * @param[in] inputName Name of uniform input to retrieve - * @param[out] uniformInput Uniform input - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return #ramses::UniformInput if successful, otherwise, std::nullopt */ - RAMSES_API status_t findUniformInput(std::string_view inputName, UniformInput& uniformInput) const; + [[nodiscard]] std::optional findUniformInput(std::string_view inputName) const; /** * @brief Finds attribute input by input name. * * @param[in] inputName Name of attribute input to retrieve - * @param[out] attributeInput Attribute input - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return #ramses::AttributeInput if successful, otherwise, std::nullopt */ - RAMSES_API status_t findAttributeInput(std::string_view inputName, AttributeInput& attributeInput) const; + [[nodiscard]] std::optional findAttributeInput(std::string_view inputName) const; /** * @brief Finds uniform input that represents a semantic input (if existing). * * @param[in] uniformSemantic Semantic of uniform input to retrieve - * @param[out] uniformInput Uniform input - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return #ramses::UniformInput if successful, otherwise, std::nullopt */ - RAMSES_API status_t findUniformInput(EEffectUniformSemantic uniformSemantic, UniformInput& uniformInput) const; + [[nodiscard]] std::optional findUniformInput(EEffectUniformSemantic uniformSemantic) const; /** * @brief Finds attribute input that represents a semantic input (if existing). * * @param[in] attributeSemantic Semantic of attribute input to retrieve - * @param[out] attributeInput Attribute input - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return #ramses::AttributeInput if successful, otherwise, std::nullopt */ - RAMSES_API status_t findAttributeInput(EEffectAttributeSemantic attributeSemantic, AttributeInput& attributeInput) const; + [[nodiscard]] std::optional findAttributeInput(EEffectAttributeSemantic attributeSemantic) const; /** * @brief Checks if the \p effect has a geometry shader attached to it. * * @return true if the effect has a geometry shader attached to it, false otherwise */ - [[nodiscard]] RAMSES_API bool hasGeometryShader() const; + [[nodiscard]] bool hasGeometryShader() const; /** * @brief If the \p effect has a geometry shader attached to it (see #hasGeometryShader) this method @@ -117,29 +110,36 @@ namespace ramses * See also #ramses::Appearance::setDrawMode(). * * @param[out] expectedGeometryInputType geometry type expected by the geometry shader of /p effect - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getGeometryShaderInputType(EDrawMode& expectedGeometryInputType) const; + bool getGeometryShaderInputType(EDrawMode& expectedGeometryInputType) const; /** - * @brief Stores internal data for implementation specifics of Effect. - */ - class EffectImpl& m_impl; + * Get the internal data for implementation specifics of Effect. + */ + [[nodiscard]] internal::EffectImpl& impl(); + + /** + * Get the internal data for implementation specifics of Effect. + */ + [[nodiscard]] const internal::EffectImpl& impl() const; protected: /** * @brief Scene is the convenience library for application developers */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of Effect * * @param[in] impl Internal data for implementation specifics of Effect (sink - instance becomes owner) */ - explicit Effect(std::unique_ptr impl); + explicit Effect(std::unique_ptr impl); + + /** + * @brief Stores internal data for implementation specifics of Effect. + */ + internal::EffectImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/EffectDescription.h b/include/ramses/client/EffectDescription.h similarity index 57% rename from client/ramses-client-api/include/ramses-client-api/EffectDescription.h rename to include/ramses/client/EffectDescription.h index ee85ffe37..ca40476f1 100644 --- a/client/ramses-client-api/include/ramses-client-api/EffectDescription.h +++ b/include/ramses/client/EffectDescription.h @@ -6,84 +6,80 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECTDESCRIPTION_H -#define RAMSES_EFFECTDESCRIPTION_H +#pragma once -#include "ramses-framework-api/StatusObject.h" -#include "ramses-client-api/EffectInputSemantic.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/EffectInputSemantic.h" #include +#include namespace ramses { - class EffectDescriptionImpl; + namespace internal + { + class EffectDescriptionImpl; + } /** * @ingroup CoreAPI * @brief An effect description holds all necessary information for an effect to be created. */ - class EffectDescription : public StatusObject + class RAMSES_API EffectDescription { public: /** * @brief Constructor of EffectDescription */ - RAMSES_API EffectDescription(); + EffectDescription(); /** * @brief Destructor of EffectDescription */ - RAMSES_API ~EffectDescription() override; + ~EffectDescription(); /** * @brief Sets vertex shader source from string. * @param[in] shaderSource Vertex shader source code. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setVertexShader(std::string_view shaderSource); + bool setVertexShader(std::string_view shaderSource); /** * @brief Sets fragment shader source from string. * @param[in] shaderSource Fragment shader source code. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setFragmentShader(std::string_view shaderSource); + bool setFragmentShader(std::string_view shaderSource); /** * @brief Sets geometry shader source from string. * @param[in] shaderSource Geometry shader source code. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setGeometryShader(std::string_view shaderSource); + bool setGeometryShader(std::string_view shaderSource); /** * @brief Reads and sets vertex shader source from file. * @param[in] shaderSourceFileName File with vertex shader source code. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setVertexShaderFromFile(std::string_view shaderSourceFileName); + bool setVertexShaderFromFile(std::string_view shaderSourceFileName); /** * @brief Reads and sets fragment shader source from file. * @param[in] shaderSourceFileName File with fragment shader source code. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setFragmentShaderFromFile(std::string_view shaderSourceFileName); + bool setFragmentShaderFromFile(std::string_view shaderSourceFileName); /** * @brief Reads and sets geometry shader source from file. * @param[in] shaderSourceFileName File with geometry shader source code. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setGeometryShaderFromFile(std::string_view shaderSourceFileName); + bool setGeometryShaderFromFile(std::string_view shaderSourceFileName); /** * @brief Adds a compiler define. * The define string will be injected as defined into the final shader code. * @param[in] define Definition name to be set at compilation time. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t addCompilerDefine(std::string_view define); + bool addCompilerDefine(std::string_view define); /** * @brief Sets an uniform semantic. @@ -93,10 +89,9 @@ namespace ramses * will be automatically set based on its semantic type. * @param[in] inputName Name of the effect input as used in the shader source code. * @param[in] semanticType Semantic type to be used for given input. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setUniformSemantic(std::string_view inputName, EEffectUniformSemantic semanticType); + bool setUniformSemantic(std::string_view inputName, EEffectUniformSemantic semanticType); /** * @brief Sets an attribute semantic. @@ -104,70 +99,78 @@ namespace ramses * These attributes are then set to use data provided by Ramses, not user. * @param[in] inputName Name of the effect input as used in the shader source code. * @param[in] semanticType Semantic type to be used for given input. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setAttributeSemantic(std::string_view inputName, EEffectAttributeSemantic semanticType); + bool setAttributeSemantic(std::string_view inputName, EEffectAttributeSemantic semanticType); /** * @brief Gets vertex shader code. * @return Vertex shader source code. Empty string if not previously set. */ - [[nodiscard]] RAMSES_API const char* getVertexShader() const; + [[nodiscard]] const char* getVertexShader() const; /** * @brief Gets fragment shader code that is currently set. * @return Fragment shader source code. Empty string if not previously set. */ - [[nodiscard]] RAMSES_API const char* getFragmentShader() const; + [[nodiscard]] const char* getFragmentShader() const; /** * @brief Gets geometry shader code that is currently set. * @return Geometry shader source code. Empty string if not previously set. */ - [[nodiscard]] RAMSES_API const char* getGeometryShader() const; + [[nodiscard]] const char* getGeometryShader() const; /** * @brief Gets number of compiler defines. * @return Number of compiler defines that were previously added. */ - [[nodiscard]] RAMSES_API size_t getNumberOfCompilerDefines() const; + [[nodiscard]] size_t getNumberOfCompilerDefines() const; /** * @brief Gets compiler define. * @param[in] index Index of define to retrieve. * @return Compiler define for given index. nullptr if not previously set. */ - [[nodiscard]] RAMSES_API const char* getCompilerDefine(size_t index) const; + [[nodiscard]] const char* getCompilerDefine(size_t index) const; /** * @brief Copy constructor * @param other source to copy from */ - RAMSES_API EffectDescription(const EffectDescription& other); + EffectDescription(const EffectDescription& other); /** * @brief Move constructor * @param other source to move from */ - RAMSES_API EffectDescription(EffectDescription&& other) noexcept; + EffectDescription(EffectDescription&& other) noexcept; /** * @brief Copy assignment * @param other source to copy from * @return this instance */ - RAMSES_API EffectDescription& operator=(const EffectDescription& other); + EffectDescription& operator=(const EffectDescription& other); /** * @brief Move assignment * @param other source to move from * @return this instance */ - RAMSES_API EffectDescription& operator=(EffectDescription&& other) noexcept; + EffectDescription& operator=(EffectDescription&& other) noexcept; + + /** + * Get the internal data for implementation specifics of EffectDescription. + */ + [[nodiscard]] internal::EffectDescriptionImpl& impl(); + /** + * Get the internal data for implementation specifics of EffectDescription. + */ + [[nodiscard]] const internal::EffectDescriptionImpl& impl() const; + + protected: /** * @brief Stores internal data for implementation specifics of EffectDescription. */ - std::reference_wrapper m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/include/ramses/client/EffectInput.h b/include/ramses/client/EffectInput.h new file mode 100644 index 000000000..4629e93a2 --- /dev/null +++ b/include/ramses/client/EffectInput.h @@ -0,0 +1,98 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/APIExport.h" +#include "ramses/framework/EDataType.h" +#include +#include + +namespace ramses +{ + namespace internal + { + class EffectInputImpl; + } + + /** + * @ingroup CoreAPI + * @brief The EffectInput is a description of an effect input + */ + class RAMSES_API EffectInput + { + public: + /** + * @brief Destructor of EffectInput + */ + ~EffectInput(); + + /** + * @brief Copy constructor + * @param other source to copy from + */ + EffectInput(const EffectInput& other); + + /** + * @brief Move constructor + * @param other source to move from + */ + EffectInput(EffectInput&& other) noexcept; + + /** + * @brief Copy assignment + * @param other source to copy from + * @return this instance + */ + EffectInput& operator=(const EffectInput& other); + + /** + * @brief Move assignment + * @param other source to move from + * @return this instance + */ + EffectInput& operator=(EffectInput&& other) noexcept; + + /** + * @brief Returns the name of the effect input. + * + * @return Name of the effect input + */ + [[nodiscard]] const char* getName() const; + + /** + * @brief Returns the effect input data type. + * + * @return Effect input data type + */ + [[nodiscard]] EDataType getDataType() const; + + /** + * Get the internal data for implementation specifics of EffectInput. + */ + [[nodiscard]] internal::EffectInputImpl& impl(); + + /** + * Get the internal data for implementation specifics of EffectInput. + */ + [[nodiscard]] const internal::EffectInputImpl& impl() const; + + protected: + /** + * @brief Constructor of EffectInput. + * + * @param[in] effectInputImpl Internal data for implementation specifics of EffectInput (sink - instance becomes owner) + */ + explicit EffectInput(std::unique_ptr effectInputImpl); + + /** + * Stores internal data for implementation specifics of EffectInput. + */ + std::unique_ptr m_impl; + }; +} diff --git a/client/ramses-client-api/include/ramses-client-api/EffectInputSemantic.h b/include/ramses/client/EffectInputSemantic.h similarity index 96% rename from client/ramses-client-api/include/ramses-client-api/EffectInputSemantic.h rename to include/ramses/client/EffectInputSemantic.h index 3fac5ab34..4b1c54e3d 100644 --- a/client/ramses-client-api/include/ramses-client-api/EffectInputSemantic.h +++ b/include/ramses/client/EffectInputSemantic.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECTINPUTSEMANTIC_H -#define RAMSES_EFFECTINPUTSEMANTIC_H +#pragma once namespace ramses { @@ -47,5 +46,3 @@ namespace ramses TextTextureCoordinates ///< Text specific - texture coordinates input. MUST be of type vec2 }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/GeometryBinding.h b/include/ramses/client/Geometry.h similarity index 62% rename from client/ramses-client-api/include/ramses-client-api/GeometryBinding.h rename to include/ramses/client/Geometry.h index d22a99544..ffbdac9d3 100644 --- a/client/ramses-client-api/include/ramses-client-api/GeometryBinding.h +++ b/include/ramses/client/Geometry.h @@ -6,13 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GEOMETRYBINDING_H -#define RAMSES_GEOMETRYBINDING_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class GeometryImpl; + } + class ArrayBuffer; class AttributeInput; class ArrayResource; @@ -22,7 +26,7 @@ namespace ramses * @ingroup CoreAPI * @brief A geometry binding together with an appearance describe how an object will be rendered to the screen. */ - class GeometryBinding : public SceneObject + class RAMSES_API Geometry : public SceneObject { public: /** @@ -31,10 +35,9 @@ namespace ramses * Indices are optional, when not provided rendering uses sequential attribute elements. * * @param[in] indicesResource An array resource carrying indices data. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setIndices(const ArrayResource& indicesResource); + bool setIndices(const ArrayResource& indicesResource); /** * @brief Assign indices (using index data buffer) to be used when accessing vertex data. @@ -42,10 +45,9 @@ namespace ramses * Indices are optional, when not provided rendering uses sequential attribute elements. * * @param[in] arrayBuffer Indices data buffer. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setIndices(const ArrayBuffer& arrayBuffer); + bool setIndices(const ArrayBuffer& arrayBuffer); /** * @brief Assign a data array resource to a given effect attribute input. @@ -53,10 +55,9 @@ namespace ramses * @param[in] attributeInput The effect attribute input to set the buffer to * @param[in] arrayResource An array resource carrying vertex data. * @param[in] instancingDivisor The vertex attribute divisor used during instanced rendering. Default is 0, which means no instancing. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint32_t instancingDivisor = 0); + bool setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint32_t instancingDivisor = 0); /** * @brief Assign a data array resource to a given effect attribute input with offset and stride. @@ -66,10 +67,9 @@ namespace ramses * @param[in] arrayResource The vertex attribute buffer. * @param[in] offset Offset in bytes for where the attribute data is starting within the data blob. * @param[in] stride Stride of the interleaved vertex attribute. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint16_t offset, uint16_t stride); + bool setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint16_t offset, uint16_t stride); /** * @brief Assign a vertex attribute buffer to a given effect attribute input. @@ -77,10 +77,9 @@ namespace ramses * @param[in] attributeInput The effect attribute input to set the buffer to * @param[in] arrayBuffer The vertex attribute buffer. * @param[in] instancingDivisor The vertex attribute divisor used during instanced rendering. Default is 0, which means no instancing. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint32_t instancingDivisor = 0); + bool setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint32_t instancingDivisor = 0); /** * @brief Assign vertex attribute buffer with offset and stride. @@ -90,36 +89,43 @@ namespace ramses * @param[in] arrayBuffer The vertex attribute buffer. * @param[in] offset Offset in bytes for where the attribute data is starting within the data blob. * @param[in] stride Stride of the interleaved vertex attribute. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint16_t offset, uint16_t stride); + bool setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint16_t offset, uint16_t stride); /** * @brief Gets the effect used to create this geometry binding * * @return The effect used to create the geometry binding. */ - [[nodiscard]] RAMSES_API const Effect& getEffect() const; + [[nodiscard]] const Effect& getEffect() const; /** - * @brief Stores internal data for implementation specifics of GeometryBinding. - */ - class GeometryBindingImpl& m_impl; + * Get the internal data for implementation specifics of Geometry. + */ + [[nodiscard]] internal::GeometryImpl& impl(); + + /** + * Get the internal data for implementation specifics of Geometry. + */ + [[nodiscard]] const internal::GeometryImpl& impl() const; protected: /** - * @brief Scene is the factory for creating GeometryBinding instances. + * @brief Scene is the factory for creating Geometry instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** - * @brief Constructor of GeometryBinding + * @brief Constructor of Geometry * - * @param[in] impl Internal data for implementation specifics of GeometryBinding (sink - instance becomes owner) + * @param[in] impl Internal data for implementation specifics of Geometry (sink - instance becomes owner) */ - explicit GeometryBinding(std::unique_ptr impl); + explicit Geometry(std::unique_ptr impl); + + /** + * @brief Stores internal data for implementation specifics of Geometry. + */ + internal::GeometryImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/IClientEventHandler.h b/include/ramses/client/IClientEventHandler.h similarity index 96% rename from client/ramses-client-api/include/ramses-client-api/IClientEventHandler.h rename to include/ramses/client/IClientEventHandler.h index 015418330..f54f92381 100644 --- a/client/ramses-client-api/include/ramses-client-api/IClientEventHandler.h +++ b/include/ramses/client/IClientEventHandler.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ICLIENTEVENTHANDLER_H -#define RAMSES_ICLIENTEVENTHANDLER_H +#pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/RendererSceneState.h" +#include "ramses/framework/APIExport.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/RendererSceneState.h" #include @@ -112,5 +111,3 @@ namespace ramses virtual ~IClientEventHandler() = default; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/MeshNode.h b/include/ramses/client/MeshNode.h similarity index 55% rename from client/ramses-client-api/include/ramses-client-api/MeshNode.h rename to include/ramses/client/MeshNode.h index fbd0fae02..91ad41250 100644 --- a/client/ramses-client-api/include/ramses-client-api/MeshNode.h +++ b/include/ramses/client/MeshNode.h @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MESHNODE_H -#define RAMSES_MESHNODE_H +#pragma once -#include "ramses-client-api/Node.h" +#include "ramses/client/Node.h" namespace ramses { + namespace internal + { + class MeshNodeImpl; + } + class Appearance; - class GeometryBinding; + class Geometry; class Effect; /** @@ -22,145 +26,146 @@ namespace ramses * @brief The MeshNode holds all information which is * needed to render an object to the screen. */ - class MeshNode : public Node + class RAMSES_API MeshNode : public Node { public: /** * @brief Sets the Appearance of the MeshNode. * * @param appearance The Appearance for the MeshNode. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setAppearance(Appearance& appearance); + bool setAppearance(Appearance& appearance); /** - * @brief Sets the GeometryBinding of the MeshNode. + * @brief Sets the Geometry of the MeshNode. * - * When the given GeometryBinding has indices set, also sets start index to 0 and index count to the number of - * indices of geometry. When the GeometryBinding does not provide indices, the index count has to be set by + * When the given Geometry has indices set, also sets start index to 0 and index count to the number of + * indices of geometry. When the Geometry does not provide indices, the index count has to be set by * setIndexCount in addition. * - * @param geometry The GeometryBinding for the MeshNode. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param geometry The Geometry for the MeshNode. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setGeometryBinding(GeometryBinding& geometry); + bool setGeometry(Geometry& geometry); /** * @brief Sets the first index of indices array that will be used for rendering. * - * @param[in] startIndex First index of indices to be used. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] startIndex First index of indices to be used (default: 0). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setStartIndex(uint32_t startIndex); + bool setStartIndex(uint32_t startIndex); /** * @brief Gets the first index of indices array that will be used for rendering. * @return the first index of indices array that will be used for rendering. */ - [[nodiscard]] RAMSES_API uint32_t getStartIndex() const; + [[nodiscard]] uint32_t getStartIndex() const; /** * @brief Sets the offset of the first vertex to use from each vertex array * that will be used for rendering the mesh. * - * @param[in] startVertex First vertex of vertex arrays to be used. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] startVertex First vertex of vertex arrays to be used (default: 0). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setStartVertex(uint32_t startVertex); + bool setStartVertex(uint32_t startVertex); /** * @brief Gets the first vertex of vertex arrays that will be used for rendering. * @return the first index of indices array that will be used for rendering. */ - [[nodiscard]] RAMSES_API uint32_t getStartVertex() const; + [[nodiscard]] uint32_t getStartVertex() const; /** * @brief Sets the number of indices that will be used for rendering. * * @param[in] indexCount Number of indices to be used. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * Default value is set from geometry binding, see #setGeometry. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setIndexCount(uint32_t indexCount); + bool setIndexCount(uint32_t indexCount); /** * @brief Gets the number of indices that will be used for rendering. * @return the number of indices that will be used for rendering. */ - [[nodiscard]] RAMSES_API uint32_t getIndexCount() const; + [[nodiscard]] uint32_t getIndexCount() const; /** * @brief Returns the appearance. * @return The appearance, null on failure or if none is set. */ - [[nodiscard]] RAMSES_API const Appearance* getAppearance() const; + [[nodiscard]] const Appearance* getAppearance() const; /** - * @brief Removes the Appearance and GeometryBinding previously set to the MeshNode. + * @brief Removes the Appearance and Geometry previously set to the MeshNode. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeAppearanceAndGeometry(); + bool removeAppearanceAndGeometry(); /** * @brief Returns the appearance. * @return The appearance, null on failure or if none is set. */ - RAMSES_API Appearance* getAppearance(); + [[nodiscard]] Appearance* getAppearance(); /** * @brief Returns the geometry binding. * @return The geometry binding, null on failure or if none is set. */ - [[nodiscard]] RAMSES_API const GeometryBinding* getGeometryBinding() const; + [[nodiscard]] const Geometry* getGeometry() const; /** * @brief Returns the geometry binding. * @return The geometry binding, null on failure or if none is set. */ - RAMSES_API GeometryBinding* getGeometryBinding(); + [[nodiscard]] Geometry* getGeometry(); /** * @brief Sets the number of instances that will be drawn for this * mesh by the renderer. * * @param[in] instanceCount Number of instances to be drawn (default: 1) - * Cannot be 0. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setInstanceCount(uint32_t instanceCount); + bool setInstanceCount(uint32_t instanceCount); /** * @brief Gets the number of instance that will be drawn for this mesh * by the renderer. * @return the number of instances of this mesh drawn on rendering. */ - [[nodiscard]] RAMSES_API uint32_t getInstanceCount() const; + [[nodiscard]] uint32_t getInstanceCount() const; /** - * Stores internal data for implementation specifics of MeshNode. - */ - class MeshNodeImpl& m_impl; + * Get the internal data for implementation specifics of MeshNode. + */ + [[nodiscard]] internal::MeshNodeImpl& impl(); + + /** + * Get the internal data for implementation specifics of MeshNode. + */ + [[nodiscard]] const internal::MeshNodeImpl& impl() const; protected: /** * @brief Scene is the factory for creating MeshNode instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for MeshNode. * * @param[in] impl Internal data for implementation specifics of MeshNode (sink - instance becomes owner) */ - explicit MeshNode(std::unique_ptr impl); + explicit MeshNode(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of MeshNode. + */ + internal::MeshNodeImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/MipLevelData.h b/include/ramses/client/MipLevelData.h similarity index 80% rename from client/ramses-client-api/include/ramses-client-api/MipLevelData.h rename to include/ramses/client/MipLevelData.h index 9cf224c62..e8419d5e2 100644 --- a/client/ramses-client-api/include/ramses-client-api/MipLevelData.h +++ b/include/ramses/client/MipLevelData.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MIPLEVELDATA_H -#define RAMSES_MIPLEVELDATA_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/APIExport.h" namespace ramses { @@ -38,6 +37,13 @@ namespace ramses */ MipLevelData(uint32_t size, const uint8_t* data) : m_size(size) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + , m_data(reinterpret_cast(data)) + { + } + + MipLevelData(uint32_t size, const std::byte* data) + : m_size(size) , m_data(data) { } @@ -45,7 +51,7 @@ namespace ramses /// The size of mipmap data in bytes uint32_t m_size{0}; /// Pointer to raw bytes data of mipmap level - const uint8_t* m_data{nullptr}; + const std::byte* m_data{nullptr}; }; @@ -75,9 +81,9 @@ namespace ramses * @param[in] dataNZ Data for face in negative Z direction */ CubeMipLevelData(uint32_t size, - const uint8_t* dataPX, const uint8_t* dataNX, - const uint8_t* dataPY, const uint8_t* dataNY, - const uint8_t* dataPZ, const uint8_t* dataNZ) + const std::byte* dataPX, const std::byte* dataNX, + const std::byte* dataPY, const std::byte* dataNY, + const std::byte* dataPZ, const std::byte* dataNZ) : m_faceDataSize(size) , m_dataPX(dataPX) , m_dataNX(dataNX) @@ -93,18 +99,16 @@ namespace ramses uint32_t m_faceDataSize{0}; /// Data for face in positive X direction - const uint8_t* m_dataPX{nullptr}; + const std::byte* m_dataPX{nullptr}; /// Data for face in negative X direction - const uint8_t* m_dataNX{nullptr}; + const std::byte* m_dataNX{nullptr}; /// Data for face in positive Y direction - const uint8_t* m_dataPY{nullptr}; + const std::byte* m_dataPY{nullptr}; /// Data for face in negative Y direction - const uint8_t* m_dataNY{nullptr}; + const std::byte* m_dataNY{nullptr}; /// Data for face in positive Z direction - const uint8_t* m_dataPZ{nullptr}; + const std::byte* m_dataPZ{nullptr}; /// Data for face in negative Z direction - const uint8_t* m_dataNZ{nullptr}; + const std::byte* m_dataNZ{nullptr}; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Node.h b/include/ramses/client/Node.h similarity index 59% rename from client/ramses-client-api/include/ramses-client-api/Node.h rename to include/ramses/client/Node.h index 913cd235e..44d20ae1d 100644 --- a/client/ramses-client-api/include/ramses-client-api/Node.h +++ b/include/ramses/client/Node.h @@ -6,22 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_NODE_H -#define RAMSES_NODE_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-client-api/EVisibilityMode.h" -#include "ramses-client-api/ERotationType.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/EVisibilityMode.h" +#include "ramses/framework/ERotationType.h" +#include "ramses/framework/DataTypes.h" namespace ramses { + namespace internal + { + class NodeImpl; + } + /** * @ingroup CoreAPI * @brief The Node is the base class of all nodes and provides * scene graph functionality which propagates to its children. */ - class Node : public SceneObject + class RAMSES_API Node : public SceneObject { public: /** @@ -29,14 +33,14 @@ namespace ramses * * @return true if this Node has at least one child Node, false otherwise. */ - [[nodiscard]] RAMSES_API bool hasChild() const; + [[nodiscard]] bool hasChild() const; /** * @brief Gets the number of child Nodes of this node. * * @return Number of child Nodes of this Node. */ - [[nodiscard]] RAMSES_API size_t getChildCount() const; + [[nodiscard]] size_t getChildCount() const; /** * @brief Gets child node at provided index. @@ -46,71 +50,66 @@ namespace ramses * @return Pointer to Node, if child at index exists. * @return nullptr if child at index does not exist. */ - [[nodiscard]] RAMSES_API Node* getChild(size_t index); + [[nodiscard]] Node* getChild(size_t index); /** @copydoc getChild(size_t) */ - [[nodiscard]] RAMSES_API const Node* getChild(size_t index) const; + [[nodiscard]] const Node* getChild(size_t index) const; /** * @brief Adds child Node to this node. * * @param[in] node Node to be set as child for this Node. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t addChild(Node& node); + bool addChild(Node& node); /** * @brief Removes a child Node from this node. * * @param[in] node Node to be removed as child from this Node. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeChild(Node& node); + bool removeChild(Node& node); /** * @brief Removes all child Nodes from this node. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeAllChildren(); + bool removeAllChildren(); /** * @brief Returns if Node has a parent Node. * * @return true if Node has a parent Node, false otherwise. */ - [[nodiscard]] RAMSES_API bool hasParent() const; + [[nodiscard]] bool hasParent() const; /** * @brief Gets parent Node of this Node. * * @return Pointer to parent Node, if parent exists, nullptr otherwise. */ - [[nodiscard]] RAMSES_API Node* getParent(); + [[nodiscard]] Node* getParent(); /** @copydoc getParent() */ - [[nodiscard]] RAMSES_API const Node* getParent() const; + [[nodiscard]] const Node* getParent() const; /** * @brief Sets parent Node for this node. * * @param[in] node Node to be set as parent for this Node. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setParent(Node& node); + bool setParent(Node& node); /** * @brief Removes the parent Node from this Node. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeParent(); + bool removeParent(); /** * @brief Gets model (world in scene space) matrix computed from the scene graph. @@ -119,10 +118,9 @@ namespace ramses * performance overhead as long as topology or transformation in the chain does not change. * * @param[out] modelMatrix Will be filled with the model matrix 4x4 column-major - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getModelMatrix(matrix44f& modelMatrix) const; + bool getModelMatrix(matrix44f& modelMatrix) const; /** * @brief Gets inverse model (world in scene space) matrix computed from the scene graph. @@ -131,10 +129,9 @@ namespace ramses * performance overhead as long as topology or transformation in the chain does not change. * * @param[out] inverseModelMatrix Will be filled with the inverse model matrix 4x4 column-major - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getInverseModelMatrix(matrix44f& inverseModelMatrix) const; + bool getInverseModelMatrix(matrix44f& inverseModelMatrix) const; /** * @brief Sets the absolute rotation in all three directions for right-handed rotation using the chosen Euler @@ -143,11 +140,10 @@ namespace ramses * Will return an error if the given rotationType is not an Euler convention. * * @param[in] rotation Euler angles in degrees - * @param[in] rotationType The rotation convention to use for calculation of rotation matrix. Default value set to Euler_XYZ rotation convention. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] rotationType The rotation convention to use for calculation of rotation matrix. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRotation(const vec3f& rotation, ERotationType rotationType = ERotationType::Euler_XYZ); + bool setRotation(const vec3f& rotation, ERotationType rotationType); /** * @brief Sets the absolute rotation defined by a quaternion. @@ -155,10 +151,9 @@ namespace ramses * * @param[in] rotation a normalized quaternion * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRotation(const quat& rotation); + bool setRotation(const quat& rotation); /** * @brief Returns the current rotation type applied to the node @@ -167,7 +162,7 @@ namespace ramses * * @return rotation type */ - [[nodiscard]] RAMSES_API ERotationType getRotationType() const; + [[nodiscard]] ERotationType getRotationType() const; /** * @brief Retrieves the absolute rotation for right-handed rotation in all three directions for the used Euler @@ -177,10 +172,9 @@ namespace ramses * Default value is 0 for all components. * * @param[out] rotation Euler angles in degrees - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getRotation(vec3f& rotation) const; + bool getRotation(vec3f& rotation) const; /** * @brief Retrieves the absolute rotation defined by a quaternion. @@ -188,64 +182,57 @@ namespace ramses * Default value is an identity quaternion: glm::identity() * * @param[out] rotation quaternion - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getRotation(quat& rotation) const; + bool getRotation(quat& rotation) const; /** * @brief Translates in all three directions with the given values. * * @param[in] translation relative translation from origin. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t translate(const vec3f& translation); + bool translate(const vec3f& translation); /** * @brief Sets the absolute translation the absolute values. * * @param[in] translation absolute translation value - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setTranslation(const vec3f& translation); + bool setTranslation(const vec3f& translation); /** * @brief Retrieves the current absolute translation. * * @param[out] translation Current translation - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getTranslation(vec3f& translation) const; + bool getTranslation(vec3f& translation) const; /** * @brief Scales in all three directions with the given values. * * @param[in] scaling The relative scaling factor. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t scale(const vec3f& scaling); + bool scale(const vec3f& scaling); /** * @brief Sets the absolute scale in all three dimensions. * * @param[in] scaling scaling factor. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setScaling(const vec3f& scaling); + bool setScaling(const vec3f& scaling); /** * @brief Retrieves the current absolute scale in all three dimensions. * * @param[out] scaling The current scaling factor. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getScaling(vec3f& scaling) const; + bool getScaling(vec3f& scaling) const; /** * @brief Sets the visibility of the Node. @@ -256,38 +243,45 @@ namespace ramses * resources are only loaded if none of its parents are Off. * * @param[in] mode Visibility mode for this node (default is Visible) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setVisibility(EVisibilityMode mode); + bool setVisibility(EVisibilityMode mode); /** * @brief Gets the visibility property of the Node. This is just the visibility * state of this node object, NOT the hierarchically accumulated visibility of its parents. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return visibility mode of this #Node. */ - [[nodiscard]] RAMSES_API EVisibilityMode getVisibility() const; + [[nodiscard]] EVisibilityMode getVisibility() const; /** - * Stores internal data for implementation specifics of Node. - */ - class NodeImpl& m_impl; + * Get the internal data for implementation specifics of Node. + */ + [[nodiscard]] internal::NodeImpl& impl(); + + /** + * Get the internal data for implementation specifics of Node. + */ + [[nodiscard]] const internal::NodeImpl& impl() const; + protected: /** * @brief Scene is the factory for creating Node instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for Node. * * @param[in] impl Internal data for implementation specifics of Node (sink - instance becomes owner) */ - explicit Node(std::unique_ptr impl); + explicit Node(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Node. + */ + internal::NodeImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/OrthographicCamera.h b/include/ramses/client/OrthographicCamera.h similarity index 80% rename from client/ramses-client-api/include/ramses-client-api/OrthographicCamera.h rename to include/ramses/client/OrthographicCamera.h index 9705f7d3f..0b6741302 100644 --- a/client/ramses-client-api/include/ramses-client-api/OrthographicCamera.h +++ b/include/ramses/client/OrthographicCamera.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ORTHOGRAPHICCAMERA_H -#define RAMSES_ORTHOGRAPHICCAMERA_H +#pragma once -#include "ramses-client-api/Camera.h" +#include "ramses/client/Camera.h" namespace ramses { @@ -19,21 +18,19 @@ namespace ramses * @details A valid camera for rendering must have viewport and frustum set, see #ramses::Camera * for ways to set these parameters. */ - class OrthographicCamera : public Camera + class RAMSES_API OrthographicCamera : public Camera { protected: /** * @brief Scene is the factory for creating OrthographicCamera instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for OrthographicCamera. * * @param[in] impl Internal data for implementation specifics of OrthographicCamera (sink - instance becomes owner) */ - explicit OrthographicCamera(std::unique_ptr impl); + explicit OrthographicCamera(std::unique_ptr impl); }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/PerspectiveCamera.h b/include/ramses/client/PerspectiveCamera.h similarity index 85% rename from client/ramses-client-api/include/ramses-client-api/PerspectiveCamera.h rename to include/ramses/client/PerspectiveCamera.h index 2ee0f7783..96488ef1d 100644 --- a/client/ramses-client-api/include/ramses-client-api/PerspectiveCamera.h +++ b/include/ramses/client/PerspectiveCamera.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PERSPECTIVECAMERA_H -#define RAMSES_PERSPECTIVECAMERA_H +#pragma once -#include "ramses-client-api/Camera.h" +#include "ramses/client/Camera.h" namespace ramses { @@ -20,7 +19,7 @@ namespace ramses * Frustum planes can be set using #ramses::Camera::setFrustum or #ramses::PerspectiveCamera::setFrustum, * depending if input is concrete frustum planes or field of view and aspect ratio. */ - class PerspectiveCamera : public Camera + class RAMSES_API PerspectiveCamera : public Camera { public: /** @@ -47,10 +46,9 @@ namespace ramses * but typically matches the viewport aspect ratio. * @param[in] nearPlane Near plane of the camera frustum, must be > 0. * @param[in] farPlane Far plane of the camera frustum, must be > nearPlane. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setFrustum(float fov, float aspectRatio, float nearPlane, float farPlane); + bool setFrustum(float fov, float aspectRatio, float nearPlane, float farPlane); /** * @brief Gets the vertical field of view opening angle in degrees. @@ -59,7 +57,7 @@ namespace ramses * * @return Vertical field of view of this camera. */ - [[nodiscard]] RAMSES_API float getVerticalFieldOfView() const; + [[nodiscard]] float getVerticalFieldOfView() const; /** * @brief Gets the aspect ratio between camera frustum width and height (set via #setFrustum, not viewport). @@ -68,21 +66,19 @@ namespace ramses * * @return Aspect ratio of this camera. */ - [[nodiscard]] RAMSES_API float getAspectRatio() const; + [[nodiscard]] float getAspectRatio() const; protected: /** * @brief Scene is the factory for creating PerspectiveCamera instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for PerspectiveCamera. * * @param[in] impl Internal data for implementation specifics of PerspectiveCamera (sink - instance becomes owner) */ - explicit PerspectiveCamera(std::unique_ptr impl); + explicit PerspectiveCamera(std::unique_ptr impl); }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/PickableObject.h b/include/ramses/client/PickableObject.h similarity index 77% rename from client/ramses-client-api/include/ramses-client-api/PickableObject.h rename to include/ramses/client/PickableObject.h index a28f1e6e7..b099f5b74 100644 --- a/client/ramses-client-api/include/ramses-client-api/PickableObject.h +++ b/include/ramses/client/PickableObject.h @@ -6,13 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PICKABLEOBJECT_H -#define RAMSES_PICKABLEOBJECT_H +#pragma once -#include "ramses-client-api/Node.h" +#include "ramses/client/Node.h" namespace ramses { + namespace internal + { + class PickableObjectImpl; + } + class ArrayBuffer; class Camera; @@ -41,7 +45,7 @@ namespace ramses * 4) Assign the camera used to render the car to our PickableObject - the pickable geometry was computed from the car's geometry * and therefore should use same camera for both rendering and picking. */ - class PickableObject : public Node + class RAMSES_API PickableObject : public Node { public: /** @@ -50,7 +54,7 @@ namespace ramses * @return The geometry buffer. * **/ - [[nodiscard]] RAMSES_API const ArrayBuffer& getGeometryBuffer() const; + [[nodiscard]] const ArrayBuffer& getGeometryBuffer() const; /** * @@ -59,7 +63,7 @@ namespace ramses * @return The PickableObject's camera, nullptr if no camera assigned. * **/ - [[nodiscard]] RAMSES_API const Camera* getCamera() const; + [[nodiscard]] const Camera* getCamera() const; /** * @@ -70,10 +74,9 @@ namespace ramses * * @param[in] camera Camera to be used to unproject geometry. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). **/ - RAMSES_API status_t setCamera(const Camera& camera); + bool setCamera(const Camera& camera); /** * @@ -82,7 +85,7 @@ namespace ramses * @return The PickableObject's user ID * **/ - [[nodiscard]] RAMSES_API pickableObjectId_t getPickableObjectId() const; + [[nodiscard]] pickableObjectId_t getPickableObjectId() const; /** * @@ -91,10 +94,9 @@ namespace ramses * @param[in] id User ID assigned to PickableObject, it will be used in callback ramses::IRendererEventHandler::objectsPicked * when this PickableObject is picked. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). **/ - RAMSES_API status_t setPickableObjectId(pickableObjectId_t id); + bool setPickableObjectId(pickableObjectId_t id); /** * @brief Enable/Disable PickableObject @@ -103,36 +105,43 @@ namespace ramses * * @param[in] enabled The enable flag which indicates if the PickableObject is enabled * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setEnabled(bool enabled); + bool setEnabled(bool enabled); /** * @brief Get the enabled state of the PickableObject * * @return Indicates if the PickableObject is enabled */ - [[nodiscard]] RAMSES_API bool isEnabled() const; + [[nodiscard]] bool isEnabled() const; /** - * Stores internal data for implementation specifics of PickableObject. - */ - class PickableObjectImpl& m_impl; + * Get the internal data for implementation specifics of PickableObject. + */ + [[nodiscard]] internal::PickableObjectImpl& impl(); + + /** + * Get the internal data for implementation specifics of PickableObject. + */ + [[nodiscard]] const internal::PickableObjectImpl& impl() const; protected: /** * @brief Scene is the factory for creating PickableObject instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for PickableObject. * * @param impl Internal data for implementation specifics of PickableObject (sink - instance becomes owner) */ - explicit PickableObject(std::unique_ptr impl); + explicit PickableObject(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of PickableObject. + */ + internal::PickableObjectImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RamsesClient.h b/include/ramses/client/RamsesClient.h similarity index 58% rename from client/ramses-client-api/include/ramses-client-api/RamsesClient.h rename to include/ramses/client/RamsesClient.h index b569d15d9..e9712875b 100644 --- a/client/ramses-client-api/include/ramses-client-api/RamsesClient.h +++ b/include/ramses/client/RamsesClient.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESCLIENT_H -#define RAMSES_RAMSESCLIENT_H +#pragma once -#include "ramses-client-api/RamsesObject.h" -#include "ramses-client-api/SceneConfig.h" -#include "ramses-framework-api/RamsesFramework.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/RamsesObject.h" +#include "ramses/client/SceneConfig.h" #include @@ -24,6 +23,12 @@ */ namespace ramses { + namespace internal + { + class ClientFactory; + class RamsesClientImpl; + } + class Scene; class IClientEventHandler; @@ -33,18 +38,26 @@ namespace ramses * * The RAMSES client class handles application state and and is a factory for scenes and client resources. */ - class RamsesClient : public RamsesObject + class RAMSES_API RamsesClient : public RamsesObject { public: + /** + * @brief Create a new empty Scene. The scene is published local-only (#ramses::EScenePublicationMode::LocalOnly) + * + * @param[in] sceneId The scene id for global identification of the Scene (is used for scene mapping on renderer side). + * @param[in] name The optional name of the created Scene. + * @return A pointer to the created Scene, null on failure + */ + Scene* createScene(sceneId_t sceneId, std::string_view name = {}); + /** * @brief Create a new empty Scene. * - * @param[in] sceneId The scene id for global identification of the Scene (is used for scene mapping on renderer side). - * @param[in] sceneConfig The optional scene configuration that can be used to change parameters of scene to be created. + * @param[in] sceneConfig The scene configuration, see #ramses::SceneConfig for details. A unique sceneId must be provided. * @param[in] name The optional name of the created Scene. * @return A pointer to the created Scene, null on failure */ - RAMSES_API Scene* createScene(sceneId_t sceneId, const SceneConfig& sceneConfig = SceneConfig(), std::string_view name = {}); + Scene* createScene(const SceneConfig& sceneConfig, std::string_view name = {}); /** * @brief Loads scene contents and resources from a file. @@ -54,12 +67,10 @@ namespace ramses * if trying to load scene files saved using older Ramses SDK version. * * @param[in] fileName File name to load the scene from. - * @param[in] localOnly Marks the scene to be loaded as valid for local only - * optimization. This has the same effect as calling - * SceneConfig::setPublicationMode(EScenePublicationMode_LocalOnly) before saving. + * @param[in] config optional configuration object to override default behavior, see #ramses::SceneConfig for details * @return New instance of scene with contents loaded from a file. */ - RAMSES_API Scene* loadSceneFromFile(std::string_view fileName, bool localOnly = false); + Scene* loadSceneFromFile(std::string_view fileName, const SceneConfig& config = {}); /** * @brief Loads scene contents and resources from a memory buffer. @@ -77,42 +88,14 @@ namespace ramses * * @param[in] data Memory buffer to load the scene from. * @param[in] size The size in bytes of the data memory. - * @param[in] localOnly Marks the scene to be loaded as valid for local only - * optimization. This has the same effect as calling - * SceneConfig::setPublicationMode(EScenePublicationMode_LocalOnly) before saving. + * @param[in] config optional configuration object to override default behavior, see #ramses::SceneConfig for details * @return New instance of scene with contents loaded from a file. */ // NOLINTNEXTLINE(modernize-avoid-c-arrays) - RAMSES_API Scene* loadSceneFromMemory(std::unique_ptr data, size_t size, bool localOnly = false); - - /** - * @brief Loads scene contents and resources from an open file descriptor. - * - * The file format has to match current Ramses SDK version in major and minor version number. - * This method is not back compatible and will fail - * if trying to load scene files saved using older Ramses SDK version. - * - * The ramses scene must be in the already opened filedescriptor at absolute position offset within - * the file. The filedescriptor must be opened for read access and may not be modified anymore after - * this call. The filedescriptor must support seeking. - * Ramses takes ownership of the filedescriptor and will close it when not needed anymore. - * - * The behavior is undefined if the filedescriptor does not contain a complete serialized ramses scene - * at offset. - * - * @param[in] fd Open and readable filedescriptor. - * @param[in] offset Absolute starting position of ramses scene within fd. - * @param[in] length Size of the scene data within fd. - * @param[in] localOnly Marks the scene to be loaded as valid for local only - * optimization. This has the same effect as calling - * SceneConfig::setPublicationMode(EScenePublicationMode_LocalOnly) before saving. - * @return New instance of scene with contents loaded from a file. - */ - RAMSES_API Scene* loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, bool localOnly = false); + Scene* loadSceneFromMemory(std::unique_ptr data, size_t size, const SceneConfig& config = {}); /** * @brief Loads scene contents and resources from an open file descriptor. - * This overload overrides the sceneId stored in the file. * * The file format has to match current Ramses SDK version in major and minor version number. * This method is not back compatible and will fail @@ -126,16 +109,13 @@ namespace ramses * The behavior is undefined if the filedescriptor does not contain a complete serialized ramses scene * at offset. * - * @param[in] sceneId The sceneId to use for the returned scene. * @param[in] fd Open and readable filedescriptor. * @param[in] offset Absolute starting position of ramses scene within fd. * @param[in] length Size of the scene data within fd. - * @param[in] localOnly Marks the scene to be loaded as valid for local only - * optimization. This has the same effect as calling - * SceneConfig::setPublicationMode(EScenePublicationMode_LocalOnly) before saving. + * @param[in] config optional configuration object to override default behavior, see #ramses::SceneConfig for details * @return New instance of scene with contents loaded from a file. */ - RAMSES_API Scene* loadSceneFromFileDescriptor(sceneId_t sceneId, int fd, size_t offset, size_t length, bool localOnly = false); + Scene* loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, const SceneConfig& config = {}); /** * @brief Loads scene contents and resources asynchronously from a file. @@ -149,13 +129,10 @@ namespace ramses * event for the scene file. * * @param[in] fileName File name to load the scene from. - * @param[in] localOnly Marks the scene to be loaded as valid for local only - * optimization. This has the same effect as calling - * SceneConfig::setPublicationMode(EScenePublicationMode_LocalOnly) before saving. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] config optional configuration object to override default behavior, see #ramses::SceneConfig for details + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t loadSceneFromFileAsync(std::string_view fileName, bool localOnly = false); + bool loadSceneFromFileAsync(std::string_view fileName, const SceneConfig& config = {}); /** * Attempts to parse feature level from a Ramses scene file. @@ -164,7 +141,7 @@ namespace ramses * @param[out] detectedFeatureLevel feature level detected in given file (valid only if parsing successful!) * @return true if parsing was successful, false otherwise. */ - RAMSES_API static bool GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel); + static bool GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel); /** * Attempts to parse feature level from a Ramses scene file. @@ -175,16 +152,15 @@ namespace ramses * @param[out] detectedFeatureLevel feature level detected in given file (valid only if parsing successful!) * @return true if parsing was successful, false otherwise. */ - RAMSES_API static bool GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel); + static bool GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel); /** * @brief Destroys the given Scene. The reference of Scene is invalid after this call * * @param[in] scene The Scene to destroy - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroy(Scene& scene); + bool destroy(Scene& scene); /** * @brief Find a scene from the client by name. @@ -192,12 +168,12 @@ namespace ramses * @param[in] name The name of the scene to find. * @return Pointer to the scene if found, nullptr otherwise. */ - [[nodiscard]] RAMSES_API const Scene* findSceneByName(std::string_view name) const; + [[nodiscard]] const Scene* findSceneByName(std::string_view name) const; /** * @copydoc findSceneByName(std::string_view) const **/ - RAMSES_API Scene* findSceneByName(std::string_view name); + [[nodiscard]] Scene* findSceneByName(std::string_view name); /** * @brief Get a scene from the client by scene id. @@ -205,12 +181,12 @@ namespace ramses * @param[in] sceneId The id of the scene to get. * @return Pointer to the scene if found, nullptr otherwise. */ - [[nodiscard]] RAMSES_API const Scene* getScene(sceneId_t sceneId) const; + [[nodiscard]] const Scene* getScene(sceneId_t sceneId) const; /** * @copydoc getScene(sceneId_t) const **/ - RAMSES_API Scene* getScene(sceneId_t sceneId); + [[nodiscard]] Scene* getScene(sceneId_t sceneId); /** * @brief Some methods on the client provide asynchronous results. These can be synchronously @@ -222,27 +198,43 @@ namespace ramses * * @param[in] clientEventHandler User class that implements the callbacks that can be triggered if a corresponding event happened. * Check IClientEventHandler documentation for more details. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + */ + bool dispatchEvents(IClientEventHandler& clientEventHandler); + + /** + * @brief Get #ramses::RamsesFramework which was used to create this #ramses::RamsesClient instance. + * @return Reference to #ramses::RamsesFramework which was used to create this #ramses::RamsesClient instance. */ - RAMSES_API status_t dispatchEvents(IClientEventHandler& clientEventHandler); + [[nodiscard]] const RamsesFramework& getRamsesFramework() const; + /** @copydoc getRamsesFramework() const */ + [[nodiscard]] RamsesFramework& getRamsesFramework(); + + /** + * Get the internal data for implementation specifics of RamsesClient. + */ + [[nodiscard]] internal::RamsesClientImpl& impl(); + /** + * Get the internal data for implementation specifics of RamsesClient. + */ + [[nodiscard]] const internal::RamsesClientImpl& impl() const; + + protected: /** * Stores internal data for implementation specifics of the API. */ - class RamsesClientImpl& m_impl; + internal::RamsesClientImpl& m_impl; private: /** * @brief Constructor of RamsesClient */ - explicit RamsesClient(std::unique_ptr); + explicit RamsesClient(std::unique_ptr impl); /** * @brief ClientFactory is the factory for RamsesClient */ - friend class ClientFactory; + friend class internal::ClientFactory; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderBuffer.h b/include/ramses/client/RenderBuffer.h similarity index 68% rename from client/ramses-client-api/include/ramses-client-api/RenderBuffer.h rename to include/ramses/client/RenderBuffer.h index 48a5bfa0b..9c70af1f4 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderBuffer.h +++ b/include/ramses/client/RenderBuffer.h @@ -6,14 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERBUFFER_H -#define RAMSES_RENDERBUFFER_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-client-api/TextureEnums.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/TextureEnums.h" namespace ramses { + namespace internal + { + class RenderBufferImpl; + } + /** * @ingroup CoreAPI * @brief RenderBuffer can be used with RenderTarget as buffer for writing or with TextureSampler as buffer for reading. @@ -22,7 +26,7 @@ namespace ramses * There are two basic types of RenderBuffers - color and depth, depth buffer can have additionally stencil data. * A RenderBuffer can be used by one or more TextureSamplers for sampling data from it in a shader. */ - class RenderBuffer : public SceneObject + class RAMSES_API RenderBuffer : public SceneObject { public: /** @@ -30,61 +34,62 @@ namespace ramses * * @returns width in pixels */ - [[nodiscard]] RAMSES_API uint32_t getWidth() const; + [[nodiscard]] uint32_t getWidth() const; /** * @brief Returns the height of the RenderBuffer in pixels * * @returns height in pixels */ - [[nodiscard]] RAMSES_API uint32_t getHeight() const; - - /** - * @brief Returns the type of the RenderBuffer - * - * @returns RenderBuffer type (color, depth, depth/stencil) - */ - [[nodiscard]] RAMSES_API ERenderBufferType getBufferType() const; + [[nodiscard]] uint32_t getHeight() const; /** * @brief Returns the data format of the RenderBuffer * * @returns RenderBuffer data format */ - [[nodiscard]] RAMSES_API ERenderBufferFormat getBufferFormat() const; + [[nodiscard]] ERenderBufferFormat getBufferFormat() const; /** * @brief Returns the read/write access mode of the RenderBuffer * * @returns RenderBuffer access mode */ - [[nodiscard]] RAMSES_API ERenderBufferAccessMode getAccessMode() const; + [[nodiscard]] ERenderBufferAccessMode getAccessMode() const; /** * @brief Returns the sample count used for MSAA * * @returns RenderBuffer sample count */ - [[nodiscard]] RAMSES_API uint32_t getSampleCount() const; + [[nodiscard]] uint32_t getSampleCount() const; /** - * Stores internal data for implementation specifics of RenderBuffer. - */ - class RenderBufferImpl& m_impl; + * Get the internal data for implementation specifics of RenderBuffer. + */ + [[nodiscard]] internal::RenderBufferImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderBuffer. + */ + [[nodiscard]] const internal::RenderBufferImpl& impl() const; protected: /** * @brief Scene is the factory for creating RenderBuffer instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for RenderBuffer. * * @param[in] impl Internal data for implementation specifics of RenderBuffer (sink - instance becomes owner) */ - explicit RenderBuffer(std::unique_ptr impl); + explicit RenderBuffer(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of RenderBuffer. + */ + internal::RenderBufferImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderGroup.h b/include/ramses/client/RenderGroup.h similarity index 66% rename from client/ramses-client-api/include/ramses-client-api/RenderGroup.h rename to include/ramses/client/RenderGroup.h index fd7fdaf40..c24d9485c 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderGroup.h +++ b/include/ramses/client/RenderGroup.h @@ -6,13 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERGROUP_H -#define RAMSES_RENDERGROUP_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class RenderGroupImpl; + } + class MeshNode; /** @@ -28,7 +32,7 @@ namespace ramses * The order inside a nested RenderGroup is local, i.e. all its renderables/RenderGroups are * rendered before the next renderable/RenderGroup of its parent RenderGroup. */ - class RenderGroup : public SceneObject + class RAMSES_API RenderGroup : public SceneObject { public: /** @@ -39,19 +43,17 @@ namespace ramses * @param[in] mesh The mesh to add to this RenderGroup. * @param[in] orderWithinGroup Order within the RenderGroup that will be used for rendering. * Mesh with lower number will be rendered before a mesh with higher number. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t addMeshNode(const MeshNode& mesh, int32_t orderWithinGroup = 0); + bool addMeshNode(const MeshNode& mesh, int32_t orderWithinGroup = 0); /** * @brief Remove a mesh from this RenderGroup. * * @param[in] mesh The mesh to remove from this RenderGroup. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeMeshNode(const MeshNode& mesh); + bool removeMeshNode(const MeshNode& mesh); /** * @brief Checks whether a mesh was added to this RenderGroup. @@ -59,17 +61,16 @@ namespace ramses * @param[in] mesh The mesh to query * @return \c true if the mesh is contained in this RenderGroup \c false otherwise */ - [[nodiscard]] RAMSES_API bool containsMeshNode(const MeshNode& mesh) const; + [[nodiscard]] bool containsMeshNode(const MeshNode& mesh) const; /** * @brief Gets a render order of given MeshNode within this RenderGroup. * * @param[in] mesh The MeshNode to query order for * @param[out] orderWithinGroup Order of the MeshNode within this RenderGroup - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getMeshNodeOrder(const MeshNode& mesh, int32_t& orderWithinGroup) const; + bool getMeshNodeOrder(const MeshNode& mesh, int32_t& orderWithinGroup) const; /** * @brief Add a RenderGroup to this RenderGroup. @@ -79,19 +80,17 @@ namespace ramses * @param[in] renderGroup The RenderGroup to add to this RenderGroup. * @param[in] orderWithinGroup Order within the RenderGroup that will be used for rendering. * RenderGroup with lower number will be rendered before a RenderGroup with higher number. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinGroup = 0); + bool addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinGroup = 0); /** * @brief Remove a RenderGroup from this RenderGroup. * * @param[in] renderGroup The RenderGroup to remove from this RenderGroup. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeRenderGroup(const RenderGroup& renderGroup); + bool removeRenderGroup(const RenderGroup& renderGroup); /** * @brief Checks whether a RenderGroup was added to this RenderGroup. @@ -99,54 +98,59 @@ namespace ramses * @param[in] renderGroup The RenderGroup to query * @return \c true if the RenderGroup is contained in this RenderGroup \c false otherwise */ - [[nodiscard]] RAMSES_API bool containsRenderGroup(const RenderGroup& renderGroup) const; + [[nodiscard]] bool containsRenderGroup(const RenderGroup& renderGroup) const; /** * @brief Gets a render order of given RenderGroup within this RenderGroup. * * @param[in] renderGroup The RenderGroup to query order for * @param[out] orderWithinGroup Order of the RenderGroup within this RenderGroup - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinGroup) const; + bool getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinGroup) const; /** * @brief Will remove all renderables from this RenderGroup. * Renderables in nested RenderGroups will stay. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeAllRenderables(); + bool removeAllRenderables(); /** * @brief Will remove all RenderGroups from this RenderGroup. * This is done NON-recursively, i.e. the RenderGroups themselves stay untouched. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeAllRenderGroups(); + bool removeAllRenderGroups(); /** - * Stores internal data for implementation specifics of RenderGroup. - */ - class RenderGroupImpl& m_impl; + * Get the internal data for implementation specifics of RenderGroup. + */ + [[nodiscard]] internal::RenderGroupImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderGroup. + */ + [[nodiscard]] const internal::RenderGroupImpl& impl() const; protected: /** * @brief Scene is the factory for creating RenderGroup instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for RenderGroup. * * @param[in] impl Internal data for implementation specifics of RenderGroup (sink - instance becomes owner) */ - explicit RenderGroup(std::unique_ptr impl); + explicit RenderGroup(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of RenderGroup. + */ + internal::RenderGroupImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderGroupMeshIterator.h b/include/ramses/client/RenderGroupMeshIterator.h similarity index 73% rename from client/ramses-client-api/include/ramses-client-api/RenderGroupMeshIterator.h rename to include/ramses/client/RenderGroupMeshIterator.h index 94312c44c..f01a50850 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderGroupMeshIterator.h +++ b/include/ramses/client/RenderGroupMeshIterator.h @@ -6,25 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERGROUPMESHITERATOR_H -#define RAMSES_RENDERGROUPMESHITERATOR_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include namespace ramses { + namespace internal + { + class MeshNodeImpl; + template + class IteratorImpl; + } + class RenderGroup; class MeshNode; - class MeshNodeImpl; - template - class IteratorImpl; /** * @ingroup CoreAPI * @brief The RenderGroupMeshIterator traverses MeshNodes in a RenderGroup. */ - class RenderGroupMeshIterator + class RAMSES_API RenderGroupMeshIterator { public: /** @@ -32,12 +35,12 @@ namespace ramses * * @param[in] renderGroup RenderGroup whose MeshNodes to iterate through **/ - RAMSES_API explicit RenderGroupMeshIterator(const RenderGroup& renderGroup); + explicit RenderGroupMeshIterator(const RenderGroup& renderGroup); /** * @brief Destructor **/ - RAMSES_API ~RenderGroupMeshIterator(); + ~RenderGroupMeshIterator(); /** * @brief Iterate through all MeshNodes. @@ -47,11 +50,9 @@ namespace ramses * * Iterator is invalid and may no longer be used if any objects are added or removed. **/ - RAMSES_API const MeshNode* getNext(); + const MeshNode* getNext(); private: - std::unique_ptr> m_impl; + std::unique_ptr> m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderPass.h b/include/ramses/client/RenderPass.h similarity index 67% rename from client/ramses-client-api/include/ramses-client-api/RenderPass.h rename to include/ramses/client/RenderPass.h index 7d0486a8e..45dcb5806 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderPass.h +++ b/include/ramses/client/RenderPass.h @@ -6,14 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERPASS_H -#define RAMSES_RENDERPASS_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/DataTypes.h" namespace ramses { + namespace internal + { + class RenderPassImpl; + } + class Camera; class RenderGroup; class RenderTarget; @@ -29,27 +33,26 @@ namespace ramses * which is also shared with BlitPass objects, i.e, RenderPass and BlitPass objects * can all be ordered relative to each other. */ - class RenderPass : public SceneObject + class RAMSES_API RenderPass : public SceneObject { public: /** * @brief Set the camera to use for rendering the objects of this renderpass. * @param camera The camera to use. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setCamera(const Camera& camera); + bool setCamera(const Camera& camera); /** * Get the camera of this RenderPass. * @return The camera or null if Camera has not been set. */ - [[nodiscard]] RAMSES_API const Camera* getCamera() const; + [[nodiscard]] const Camera* getCamera() const; /** * @copydoc getCamera() const */ - [[nodiscard]] RAMSES_API Camera* getCamera(); + [[nodiscard]] Camera* getCamera(); /** * @brief Add a RenderGroup to this RenderPass for rendering. @@ -59,19 +62,17 @@ namespace ramses * @param[in] renderGroup The RenderGroup to be added. * @param[in] orderWithinPass Order within the RenderPass that will be used for rendering. * RenderGroup with lower number will be rendered before a RenderGroup with higher number. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinPass = 0); + bool addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinPass = 0); /** * @brief Remove a RenderGroup from this RenderPass. * * @param[in] renderGroup The RenderGroup to be removed. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeRenderGroup(const RenderGroup& renderGroup); + bool removeRenderGroup(const RenderGroup& renderGroup); /** * @brief Checks whether a RenderGroup is part of the RenderPass @@ -79,41 +80,38 @@ namespace ramses * @param[in] renderGroup The RenderGroup to look for * @return \c true if the mesh is used in this RenderPass \c false otherwise */ - [[nodiscard]] RAMSES_API bool containsRenderGroup(const RenderGroup& renderGroup) const; + [[nodiscard]] bool containsRenderGroup(const RenderGroup& renderGroup) const; /** * @brief Gets a render order of given RenderGroup within this RenderPass. * * @param[in] renderGroup The RenderGroup to query order for * @param[out] orderWithinPass Order of the RenderGroup within this RenderPass - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinPass) const; + bool getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinPass) const; /** * @brief Will make the RenderPass empty. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t removeAllRenderGroups(); + bool removeAllRenderGroups(); /** * @brief Set the render target for the render pass to render into. * * @param renderTarget The render target to render into, set to null for direct framebuffer rendering - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRenderTarget(RenderTarget* renderTarget); + bool setRenderTarget(RenderTarget* renderTarget); /** * @brief Get the render target of this render pass. * * @return The render target or null if render target has not been set. */ - [[nodiscard]] RAMSES_API const RenderTarget* getRenderTarget() const; + [[nodiscard]] const RenderTarget* getRenderTarget() const; /** * @brief Set the render order for the render pass. @@ -124,17 +122,16 @@ namespace ramses * The default render order is Zero. * * @param renderOrder Render order used for ordering the render pass - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRenderOrder(int32_t renderOrder); + bool setRenderOrder(int32_t renderOrder); /** * @brief Get the render order of this render pass. * * @return The render order of this render pass. */ - [[nodiscard]] RAMSES_API int32_t getRenderOrder() const; + [[nodiscard]] int32_t getRenderOrder() const; /** * @brief Set the clear color for the RenderPass (default: [0,0,0,1]) @@ -142,51 +139,48 @@ namespace ramses * if clear flag is enabled, see setClearFlag. * * @param color color channels of clear color (RGBA in 0-1 range) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setClearColor(const vec4f& color); + bool setClearColor(const vec4f& color); /** * @brief Returns the clear color of the RenderPass * * @return color channels of clear color */ - [[nodiscard]] RAMSES_API vec4f getClearColor() const; + [[nodiscard]] vec4f getClearColor() const; /** - * @brief Set the clear flags which enable/disable the clearing of the render target assigned to this RenderPass(default: #ramses::EClearFlags_All) + * @brief Set the clear flags which enable/disable the clearing of the render target assigned to this RenderPass(default: #ramses::EClearFlag::All) * @details The clear flags have no effect on render passes with no render target assigned, i.e. it is not allowed to control clearing * of display buffer (whether it is framebuffer or offscreen buffer) on renderer side from within scene's render pass. * - * @param clearFlags clear flags, which is a bitmask of the #ramses::EClearFlags enum - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param clearFlags clear flags, which is a bitmask of the #ramses::EClearFlag enum + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setClearFlags(uint32_t clearFlags); + bool setClearFlags(ClearFlags clearFlags); /** * @brief Returns the clear flags of the RenderPass * - * @returns clear flags, which is a bitmask of the #ramses::EClearFlags enum + * @returns clear flags, which is a bitmask of the #ramses::EClearFlag enum */ - [[nodiscard]] RAMSES_API uint32_t getClearFlags() const; + [[nodiscard]] ClearFlags getClearFlags() const; /** * @brief Enable/Disable render pass * * @param enable The enable flag which indicates if the render pass is rendered (Default:true) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setEnabled(bool enable); + bool setEnabled(bool enable); /** * @brief Get the enable state of the render pass * * @return Indicates if the render pass is enabled */ - [[nodiscard]] RAMSES_API bool isEnabled() const; + [[nodiscard]] bool isEnabled() const; /** * @brief Set/unset render once flag - rendering of the render pass only once. @@ -203,17 +197,16 @@ namespace ramses * on renderer side). * * @param enable The flag which indicates if the render pass is to be rendered only once (Default:false) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRenderOnce(bool enable); + bool setRenderOnce(bool enable); /** * @brief Get the render once state of the render pass * * @return Indicates if the render pass is to be rendered only once */ - [[nodiscard]] RAMSES_API bool isRenderOnce() const; + [[nodiscard]] bool isRenderOnce() const; /** * @brief Will re-render a render once pass. @@ -224,29 +217,36 @@ namespace ramses * Note that Ramses does not track changes of render pass content and will not automatically * re-render a render once pass if its content changed. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t retriggerRenderOnce(); + bool retriggerRenderOnce(); /** - * Stores internal data for implementation specifics of RenderPass. - */ - class RenderPassImpl& m_impl; + * Get the internal data for implementation specifics of RenderPass. + */ + [[nodiscard]] internal::RenderPassImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderPass. + */ + [[nodiscard]] const internal::RenderPassImpl& impl() const; protected: /** * @brief Scene is the factory for creating RenderPass instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for RenderPass. * * @param[in] impl Internal data for implementation specifics of RenderPass (sink - instance becomes owner) */ - explicit RenderPass(std::unique_ptr impl); + explicit RenderPass(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of RenderPass. + */ + internal::RenderPassImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderPassGroupIterator.h b/include/ramses/client/RenderPassGroupIterator.h similarity index 73% rename from client/ramses-client-api/include/ramses-client-api/RenderPassGroupIterator.h rename to include/ramses/client/RenderPassGroupIterator.h index 3db8f4c28..0a86e20e4 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderPassGroupIterator.h +++ b/include/ramses/client/RenderPassGroupIterator.h @@ -6,25 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERPASSGROUPITERATOR_H -#define RAMSES_RENDERPASSGROUPITERATOR_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include namespace ramses { + namespace internal + { + class RenderGroupImpl; + template + class IteratorImpl; + } + class RenderPass; class RenderGroup; - class RenderGroupImpl; - template - class IteratorImpl; /** * @ingroup CoreAPI * @brief The RenderPassGroupIterator traverses RenderGroups in a RenderPass. */ - class RenderPassGroupIterator + class RAMSES_API RenderPassGroupIterator { public: /** @@ -32,12 +35,12 @@ namespace ramses * * @param[in] renderPass RenderPass whose RenderGroups to iterate through **/ - RAMSES_API explicit RenderPassGroupIterator(const RenderPass& renderPass); + explicit RenderPassGroupIterator(const RenderPass& renderPass); /** * @brief Destructor **/ - RAMSES_API ~RenderPassGroupIterator(); + ~RenderPassGroupIterator(); /** * @brief Iterate through all RenderGroups. @@ -47,11 +50,9 @@ namespace ramses * * Iterator is invalid and may no longer be used if any objects are added or removed. **/ - RAMSES_API const RenderGroup* getNext(); + const RenderGroup* getNext(); private: - std::unique_ptr> m_impl; + std::unique_ptr> m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderTarget.h b/include/ramses/client/RenderTarget.h similarity index 62% rename from client/ramses-client-api/include/ramses-client-api/RenderTarget.h rename to include/ramses/client/RenderTarget.h index 5988ddefa..d18b03fc4 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderTarget.h +++ b/include/ramses/client/RenderTarget.h @@ -6,18 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERTARGET_H -#define RAMSES_RENDERTARGET_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class RenderTargetImpl; + } + /** * @ingroup CoreAPI * @brief The RenderTarget can be used as an output for a RenderPass */ - class RenderTarget : public SceneObject + class RAMSES_API RenderTarget : public SceneObject { public: @@ -26,33 +30,41 @@ namespace ramses * * @returns width in pixels */ - [[nodiscard]] RAMSES_API uint32_t getWidth() const; + [[nodiscard]] uint32_t getWidth() const; /** * @brief Returns the height of the RenderTarget in pixels * * @returns height in pixels */ - [[nodiscard]] RAMSES_API uint32_t getHeight() const; + [[nodiscard]] uint32_t getHeight() const; /** - * Stores internal data for implementation specifics of RenderTarget. - */ - class RenderTargetImpl& m_impl; + * Get the internal data for implementation specifics of RenderTarget. + */ + [[nodiscard]] internal::RenderTargetImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderTarget. + */ + [[nodiscard]] const internal::RenderTargetImpl& impl() const; protected: /** * @brief Scene is the factory for creating RenderTarget instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for RenderTarget. * * @param[in] impl Internal data for implementation specifics of RenderTarget (sink - instance becomes owner) */ - explicit RenderTarget(std::unique_ptr impl); + explicit RenderTarget(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of RenderTarget. + */ + internal::RenderTargetImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/RenderTargetDescription.h b/include/ramses/client/RenderTargetDescription.h similarity index 58% rename from client/ramses-client-api/include/ramses-client-api/RenderTargetDescription.h rename to include/ramses/client/RenderTargetDescription.h index 27ee55805..fa46e5119 100644 --- a/client/ramses-client-api/include/ramses-client-api/RenderTargetDescription.h +++ b/include/ramses/client/RenderTargetDescription.h @@ -6,32 +6,42 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERTARGETDESCRIPTION_H -#define RAMSES_RENDERTARGETDESCRIPTION_H +#pragma once -#include "ramses-framework-api/StatusObject.h" +#include "ramses/framework/APIExport.h" +#include +#include namespace ramses { + namespace internal + { + class RenderTargetDescriptionImpl; + } + class ValidationReport; class RenderBuffer; - class RenderTargetDescriptionImpl; /** * @ingroup CoreAPI * @brief RenderTargetDescription holds all necessary information for a RenderTarget to be created. */ - class RenderTargetDescription : public StatusObject + class RAMSES_API RenderTargetDescription { public: /** * @brief Constructor of RenderTargetDescription */ - RAMSES_API RenderTargetDescription(); + RenderTargetDescription(); /** * @brief Destructor of RenderTargetDescription */ - RAMSES_API ~RenderTargetDescription() override; + ~RenderTargetDescription(); + + /** + * @copydoc #ramses::RamsesObject::validate + */ + void validate(ValidationReport& report) const; /** * @brief Adds a RenderBuffer to the RenderTargetDescription. @@ -42,42 +52,52 @@ namespace ramses * All added render buffers must have same MSAA sample count. Trying to add render buffers with different * sample count values will fail and generate error. * @param[in] renderBuffer RenderBuffer to be added to the RenderTargetDescription. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[out] errorMsg In case of an error this string will be filled with description of the error. In case of success it will be empty. + * @return true on success, false if an error occurred. + * Pass the optional \c errorMsg argument to get human readable description of the error, the error is also logged. */ - RAMSES_API status_t addRenderBuffer(const RenderBuffer& renderBuffer); + bool addRenderBuffer(const RenderBuffer& renderBuffer, std::string* errorMsg = nullptr); /** * @brief Copy constructor * @param other source to copy from */ - RAMSES_API RenderTargetDescription(const RenderTargetDescription& other); + RenderTargetDescription(const RenderTargetDescription& other); /** * @brief Move constructor * @param other source to move from */ - RAMSES_API RenderTargetDescription(RenderTargetDescription&& other) noexcept; + RenderTargetDescription(RenderTargetDescription&& other) noexcept; /** * @brief Copy assignment * @param other source to copy from * @return this instance */ - RAMSES_API RenderTargetDescription& operator=(const RenderTargetDescription& other); + RenderTargetDescription& operator=(const RenderTargetDescription& other); /** * @brief Move assignment * @param other source to move from * @return this instance */ - RAMSES_API RenderTargetDescription& operator=(RenderTargetDescription&& other) noexcept; + RenderTargetDescription& operator=(RenderTargetDescription&& other) noexcept; + /** + * Get the internal data for implementation specifics of RenderTargetDescription. + */ + [[nodiscard]] internal::RenderTargetDescriptionImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderTargetDescription. + */ + [[nodiscard]] const internal::RenderTargetDescriptionImpl& impl() const; + + protected: /** * @brief Stores internal data for implementation specifics of RenderTargetDescription. */ - std::reference_wrapper m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Resource.h b/include/ramses/client/Resource.h similarity index 61% rename from client/ramses-client-api/include/ramses-client-api/Resource.h rename to include/ramses/client/Resource.h index cbbf92372..fdb74372d 100644 --- a/client/ramses-client-api/include/ramses-client-api/Resource.h +++ b/include/ramses/client/Resource.h @@ -6,30 +6,39 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCE_H -#define RAMSES_RESOURCE_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class ResourceImpl; + } + /** * @ingroup CoreAPI * @brief The Resource is the base class of all resources, such as arrays and textures. */ - class Resource : public SceneObject + class RAMSES_API Resource : public SceneObject { public: /** * @brief Get resource Id. * @return the Id of the resource. */ - [[nodiscard]] RAMSES_API resourceId_t getResourceId() const; + [[nodiscard]] resourceId_t getResourceId() const; /** - * Stores internal data for implementation specifics of Resource. - */ - class ResourceImpl& m_impl; + * Get the internal data for implementation specifics of Resource. + */ + [[nodiscard]] internal::ResourceImpl& impl(); + + /** + * Get the internal data for implementation specifics of Resource. + */ + [[nodiscard]] const internal::ResourceImpl& impl() const; protected: /** @@ -37,8 +46,11 @@ namespace ramses * * @param[in] impl Internal data for implementation specifics of Resource (sink - instance becomes owner) */ - explicit Resource(std::unique_ptr impl); + explicit Resource(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Resource. + */ + internal::ResourceImpl& m_impl; }; } - -#endif diff --git a/client/logic/include/ramses-logic/SaveFileConfig.h b/include/ramses/client/SaveFileConfig.h similarity index 70% rename from client/logic/include/ramses-logic/SaveFileConfig.h rename to include/ramses/client/SaveFileConfig.h index b9f154c8d..a524272ab 100644 --- a/client/logic/include/ramses-logic/SaveFileConfig.h +++ b/include/ramses/client/SaveFileConfig.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/ELuaSavingMode.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/ELuaSavingMode.h" #include #include @@ -22,23 +22,23 @@ namespace ramses::internal namespace ramses { /** - * Holds configuration settings for saving #ramses::LogicEngine instances into a file. + * Holds configuration settings for saving #ramses::Scene instances into a file. * This config file is designed to work with the Ramses Composer editor, but you can use the metadata to store * version and export information from any exporter (or script). Use the config to trace the origin and * export environment of assets during runtime. */ - class SaveFileConfig + class RAMSES_API SaveFileConfig { public: - RAMSES_API SaveFileConfig() noexcept; + SaveFileConfig() noexcept; /** - * Adds custom string metadata to the binary file saved by #ramses::LogicEngine. Can be any string. It is not + * Adds custom string metadata to the binary file saved by #ramses::Scene. Can be any string. It is not * used by anything other than logging info, particularly when loading a file and when errors occur. * * @param metadata the string to be written together with the saved binary data */ - RAMSES_API void setMetadataString(std::string_view metadata); + void setMetadataString(std::string_view metadata); /** * Specifies the semantic version of the exporter (binary, or script, or runtime logic). Use this to match a @@ -50,16 +50,16 @@ namespace ramses * @param patch the patch version number * @param fileFormatVersion the file format used by the exporter itself (use 0 if not applicable) */ - RAMSES_API void setExporterVersion(uint32_t major, uint32_t minor, uint32_t patch, uint32_t fileFormatVersion); + void setExporterVersion(uint32_t major, uint32_t minor, uint32_t patch, uint32_t fileFormatVersion); /** - * By default, saving to file validates the content and issues warnings (see #ramses::LogicEngine::validate). + * By default, saving to file validates the content and issues warnings (see #ramses::Scene::validate). * This behavior can be disabled here. Calling this with validationEnabled=false will itself cause a INFO log, * but will silence further warnings in the content. * * @param validationEnabled flag to disable/enable validation upon saving to file */ - RAMSES_API void setValidationEnabled(bool validationEnabled); + void setValidationEnabled(bool validationEnabled); /** * Sets saving mode for all #ramses::LuaScript and/or #ramses::LuaModule instances. @@ -74,39 +74,58 @@ namespace ramses * * @param mode selected saving mode, default is #ramses::ELuaSavingMode::SourceAndByteCode */ - RAMSES_API void setLuaSavingMode(ELuaSavingMode mode); + void setLuaSavingMode(ELuaSavingMode mode); + + /** + * By default, ramses resources (Textures, Shaders) will be stored uncompressed + * This avoids some extra processing when loading resources, but causes larger asset files. + * + * @param compressionEnabled flag to disable/enable resource compression + */ + void setCompressionEnabled(bool compressionEnabled); /** * Destructor of #SaveFileConfig */ - RAMSES_API ~SaveFileConfig() noexcept; + ~SaveFileConfig() noexcept; /** * Copy Constructor of #SaveFileConfig * @param other the other #SaveFileConfig to copy from */ - RAMSES_API SaveFileConfig(const SaveFileConfig& other); + SaveFileConfig(const SaveFileConfig& other); /** * Move Constructor of #SaveFileConfig * @param other the other #SaveFileConfig to move from */ - RAMSES_API SaveFileConfig(SaveFileConfig&& other) noexcept; + SaveFileConfig(SaveFileConfig&& other) noexcept; /** * Assignment operator of #SaveFileConfig * @param other the other #SaveFileConfig to copy from * @return self */ - RAMSES_API SaveFileConfig& operator=(const SaveFileConfig& other); + SaveFileConfig& operator=(const SaveFileConfig& other); /** * Move assignment operator of #SaveFileConfig * @param other the other #SaveFileConfig to move from * @return self */ - RAMSES_API SaveFileConfig& operator=(SaveFileConfig&& other) noexcept; + SaveFileConfig& operator=(SaveFileConfig&& other) noexcept; + + /** + * Get the internal data for implementation specifics of #SaveFileConfig. + */ + [[nodiscard]] internal::SaveFileConfigImpl& impl(); + + /** + * Get the internal data for implementation specifics of #SaveFileConfig. + */ + [[nodiscard]] const internal::SaveFileConfigImpl& impl() const; + protected: /** * Implementation detail of #SaveFileConfig */ diff --git a/client/ramses-client-api/include/ramses-client-api/Scene.h b/include/ramses/client/Scene.h similarity index 77% rename from client/ramses-client-api/include/ramses-client-api/Scene.h rename to include/ramses/client/Scene.h index 38cff0a73..739b9331b 100644 --- a/client/ramses-client-api/include/ramses-client-api/Scene.h +++ b/include/ramses/client/Scene.h @@ -6,32 +6,38 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENE_H -#define RAMSES_SCENE_H - -#include "ramses-client-api/ClientObject.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/EScenePublicationMode.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-framework-api/EDataType.h" -#include "ramses-framework-api/DataTypes.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#pragma once + +#include "ramses/client/ClientObject.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/client/SaveFileConfig.h" +#include "ramses/client/SceneObject.h" +#include "ramses/client/logic/LogicObject.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/framework/EScenePublicationMode.h" +#include "ramses/framework/EDataType.h" +#include "ramses/framework/DataTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include #include namespace ramses { + namespace internal + { + class SceneImpl; + class RamsesClientImpl; + } + class Node; class MeshNode; class PerspectiveCamera; class OrthographicCamera; class Appearance; class Effect; - class GeometryBinding; - class SceneImpl; - class RamsesClientImpl; + class Geometry; class RenderGroup; class RenderPass; class RenderBuffer; @@ -59,6 +65,7 @@ namespace ramses class TextureCube; class Effect; class Resource; + class LogicEngine; /** * @ingroup CoreAPI @@ -66,7 +73,7 @@ namespace ramses * It is the essential class for distributing * content to the ramses system. */ - class Scene : public ClientObject + class RAMSES_API Scene : public ClientObject { public: /** @@ -78,13 +85,11 @@ namespace ramses * flushes will send scene updates to them. * * @param[in] publicationMode A flag to signal if a scene should be only available - * to local renderer(s) and not published outside of the client. By default, scene - * is published and everyone in the RAMSES world can subscribe to it + * to local renderer(s) and not published to remote renderer(s), this is default. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t publish(EScenePublicationMode publicationMode = EScenePublicationMode::LocalAndRemote); + bool publish(EScenePublicationMode publicationMode = EScenePublicationMode::LocalOnly); /** * @brief Unpublish the scene from the ramses system @@ -92,10 +97,9 @@ namespace ramses * The scene will be removed from the ramses system. * Subsequent changes to the scene will therefore not be propagated to the system. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t unpublish(); + bool unpublish(); /** * @brief Returns whether scene is currently published to the ramses system @@ -103,25 +107,39 @@ namespace ramses * @return true, if scene is currently published. * @return false, if scene is currently not published. */ - [[nodiscard]] RAMSES_API bool isPublished() const; + [[nodiscard]] bool isPublished() const; /** * @brief Returns scene id defined at scene creation time * * @return Scene id. */ - [[nodiscard]] RAMSES_API sceneId_t getSceneId() const; + [[nodiscard]] sceneId_t getSceneId() const; /** * @brief Saves all scene contents to a file. * * @param[in] fileName File name to save the scene to. - * @param[in] compress if set to true, resources might be compressed before saving - * otherwise, uncompressed data will be saved - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] config optional configuration object with exporter and asset metadata info, see #ramses::SaveFileConfig for details + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + */ + [[nodiscard]] bool saveToFile(std::string_view fileName, const SaveFileConfig& config = {}) const; + + /** + * @brief Creates a #ramses::LogicEngine instance. + * @details Logic is a crucial Ramses component and can be created from a #Scene, + * all its bindings (e.g. #ramses::NodeBinding, etc.) must be from + * this #Scene - the scene that owns the #ramses::LogicEngine instance. + * Like other scene objects #ramses::LogicEngine can be destroyed using #destroy + * and will be automatically destroyed when #Scene is destroyed. + * + * Note that multiple instances of #ramses::LogicEngine can be created for single #Scene + * but generally this is only meant for special usecases and not recommended. + * + * @param[in] name The optional name of the #ramses::LogicEngine + * @return A pointer to the created #ramses::LogicEngine instance. */ - [[nodiscard]] RAMSES_API status_t saveToFile(std::string_view fileName, bool compress) const; + LogicEngine* createLogicEngine(std::string_view name = {}); /** * @brief Creates a Perspective Camera in this Scene @@ -129,7 +147,7 @@ namespace ramses * @param[in] name The optional name of the Camera * @return Pointer to the created Camera, null on failure */ - RAMSES_API PerspectiveCamera* createPerspectiveCamera(std::string_view name = {}); + PerspectiveCamera* createPerspectiveCamera(std::string_view name = {}); /** * @brief Creates a Orthographic Camera in this Scene @@ -137,7 +155,7 @@ namespace ramses * @param[in] name The optional name of the Camera * @return Pointer to the created Camera, null on failure */ - RAMSES_API OrthographicCamera* createOrthographicCamera(std::string_view name = {}); + OrthographicCamera* createOrthographicCamera(std::string_view name = {}); /** * @brief Creates a new Appearance. @@ -146,16 +164,16 @@ namespace ramses * @param[in] name The optional name of the created Appearance. * @return A pointer to the created Appearance, null on failure */ - RAMSES_API Appearance* createAppearance(const Effect& effect, std::string_view name = {}); + Appearance* createAppearance(const Effect& effect, std::string_view name = {}); /** - * @brief Creates a new GeometryBinding. + * @brief Creates a new Geometry. * - * @param[in] effect The effect which is used to create the GeometryBinding. - * @param[in] name The optional name of the created GeometryBinding. - * @return A pointer to the created GeometryBinding, null on failure + * @param[in] effect The effect which is used to create the Geometry. + * @param[in] name The optional name of the created Geometry. + * @return A pointer to the created Geometry, null on failure */ - RAMSES_API GeometryBinding* createGeometryBinding(const Effect& effect, std::string_view name = {}); + Geometry* createGeometry(const Effect& effect, std::string_view name = {}); /** * @brief Creates a scene graph node. @@ -170,7 +188,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created Node, nullptr on failure. **/ - RAMSES_API Node* createNode(std::string_view name = {}); + Node* createNode(std::string_view name = {}); /** * @brief Creates a scene graph MeshNode. @@ -180,7 +198,7 @@ namespace ramses * @param[in] name The optional name of the MeshNode. * @return Pointer to the created MeshNode, null on failure. */ - RAMSES_API MeshNode* createMeshNode(std::string_view name = {}); + MeshNode* createMeshNode(std::string_view name = {}); /** * @brief Destroys a previously created object using this scene @@ -189,10 +207,9 @@ namespace ramses * SceneObjects will automatically be destroyed once the scene is destroyed. * * @param object The object of the Scene to destroy - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroy(SceneObject& object); + bool destroy(SceneObject& object); /** * @brief Expiration timestamp is a point in time till which the scene is considered to be up-to-date. @@ -213,10 +230,9 @@ namespace ramses * @param[in] ptpExpirationTimestampInMilliseconds Expiration timestamp in milliseconds from synchronized clock. * To avoid issues, keep this up-to-date reasonably enough in future. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds); + bool setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds); /** * @brief Commits all changes done to the scene since the last flush or since scene creation. This makes a new @@ -227,10 +243,9 @@ namespace ramses * scene update has been applied. Invalid scene version ids are ignored. * Defaults to InvalidSceneVersionTag. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t flush(sceneVersionTag_t sceneVersionTag = InvalidSceneVersionTag); + bool flush(sceneVersionTag_t sceneVersionTag = InvalidSceneVersionTag); /** * @brief resets the semantic uniform #ramses::EEffectUniformSemantic::TimeMs @@ -238,10 +253,9 @@ namespace ramses * Use this method to avoid possible overflow issues. * The reset will be applied to the rendered scene with the next flush. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t resetUniformTimeMs(); + bool resetUniformTimeMs(); /** * @brief Gets the current value used for the semantic uniform #ramses::EEffectUniformSemantic::TimeMs @@ -252,33 +266,36 @@ namespace ramses * * @return time in milliseconds, value range is 0 .. std::numeric_limits::max() (~24 days) */ - [[nodiscard]] RAMSES_API int32_t getUniformTimeMs() const; + [[nodiscard]] int32_t getUniformTimeMs() const; /** * @brief Get an object from the scene by name + * Note that this will not find #ramses::LogicObject instances created from #ramses::LogicEngine, + * use #ramses::LogicEngine::findObject for those. + * Note that giving a concrete object template type might result in faster search because it is limited to only objects of that type. * * @param[in] name The name of the object to get. - * @return Pointer to the object if found, nullptr otherwise. + * @return Pointer to the object if found and convertible to the demanded type, nullptr otherwise. */ - [[nodiscard]] RAMSES_API const RamsesObject* findObjectByName(std::string_view name) const; + template [[nodiscard]] T* findObject(std::string_view name); /** - * @copydoc findObjectByName(std::string_view) const + * @copydoc findObject(std::string_view) **/ - RAMSES_API RamsesObject* findObjectByName(std::string_view name); + template [[nodiscard]] const T* findObject(std::string_view name) const; /** * @brief Get an object from the scene by id * * @param[in] id The id of the object to get. - * @return Pointer to the object if found, nullptr otherwise. + * @return Pointer to the object if found and convertible to the demanded type, nullptr otherwise. */ - [[nodiscard]] RAMSES_API const SceneObject* findObjectById(sceneObjectId_t id) const; + template [[nodiscard]] T* findObject(sceneObjectId_t id); /** - * @copydoc findObjectById(sceneObjectId_t id) const + * @copydoc findObject(sceneObjectId_t id) **/ - RAMSES_API SceneObject* findObjectById(sceneObjectId_t id); + template [[nodiscard]] const T* findObject(sceneObjectId_t id) const; /** * @brief Create a RenderGroup instance in the scene. @@ -286,7 +303,7 @@ namespace ramses * @param[in] name The optional name of the created RenderGroup instance. * @return A pointer to the created RenderGroup, null on failure **/ - RAMSES_API RenderGroup* createRenderGroup(std::string_view name = {}); + RenderGroup* createRenderGroup(std::string_view name = {}); /** * @brief Create a render pass in the scene. @@ -294,7 +311,7 @@ namespace ramses * @param[in] name The optional name of the created render pass. * @return A render pass. **/ - RAMSES_API RenderPass* createRenderPass(std::string_view name = {}); + RenderPass* createRenderPass(std::string_view name = {}); /** * @brief Create a blit pass in the scene. @@ -306,7 +323,7 @@ namespace ramses * @param[in] name The optional name of the created blit pass. * @return A pointer to a BlitPass if successful or nullptr on failure. **/ - RAMSES_API BlitPass* createBlitPass(const RenderBuffer& sourceRenderBuffer, const RenderBuffer& destinationRenderBuffer, std::string_view name = {}); + BlitPass* createBlitPass(const RenderBuffer& sourceRenderBuffer, const RenderBuffer& destinationRenderBuffer, std::string_view name = {}); /** * @brief Create a RenderBuffer to be used with RenderTarget for rendering into and TextureSampler for sampling from. @@ -317,14 +334,13 @@ namespace ramses * * @param[in] width The width of the RenderBuffer in pixel. * @param[in] height The height of the RenderBuffer in pixel. - * @param[in] bufferType Type of the RenderBuffer to be created (color, depth, depth/stecil). * @param[in] bufferFormat Data format to use for the RenderBuffer to be created. * @param[in] accessMode Read/Write access mode of render buffer * @param[in] sampleCount Optional sample count for MSAA number of samples. Default value is 0 for no MSAA. * @param[in] name Optional name of the object. * @return Pointer to the created RenderBuffer, null on failure. **/ - RAMSES_API RenderBuffer* createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferType bufferType, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount = 0u, std::string_view name = {}); + RenderBuffer* createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount = 0u, std::string_view name = {}); /** * @brief Create a PickableObject. @@ -343,7 +359,7 @@ namespace ramses * @param[in] name Name of the PickableObject. * @return Pointer to the created PickableObject, nullptr on failure. **/ - RAMSES_API PickableObject* createPickableObject(const ArrayBuffer& geometryBuffer, const pickableObjectId_t id, std::string_view name = {}); + PickableObject* createPickableObject(const ArrayBuffer& geometryBuffer, const pickableObjectId_t id, std::string_view name = {}); /** * @brief Create a render target providing a set of RenderBuffers. @@ -352,7 +368,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created RenderTarget, null on failure. **/ - RAMSES_API RenderTarget* createRenderTarget(const RenderTargetDescription& rtDesc, std::string_view name = {}); + RenderTarget* createRenderTarget(const RenderTargetDescription& rtDesc, std::string_view name = {}); /** * @brief Creates a texture sampler object. @@ -367,7 +383,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created TextureSampler, null on failure. */ - RAMSES_API TextureSampler* createTextureSampler( + TextureSampler* createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, @@ -387,7 +403,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created TextureSampler, null on failure. */ - RAMSES_API TextureSampler* createTextureSampler( + TextureSampler* createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureAddressMode wrapRMode, @@ -409,7 +425,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created TextureSampler, null on failure. */ - RAMSES_API TextureSampler* createTextureSampler( + TextureSampler* createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, @@ -431,7 +447,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created TextureSampler, null on failure. */ - RAMSES_API TextureSampler* createTextureSampler( + TextureSampler* createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, @@ -453,7 +469,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created TextureSampler, null on failure. */ - RAMSES_API TextureSampler* createTextureSampler( + TextureSampler* createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, @@ -468,7 +484,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created #ramses::TextureSamplerMS, null on failure. */ - RAMSES_API TextureSamplerMS* createTextureSamplerMS(const RenderBuffer& renderBuffer, std::string_view name); + TextureSamplerMS* createTextureSamplerMS(const RenderBuffer& renderBuffer, std::string_view name); /** * @brief Creates a texture sampler object that can sample from external textures. @@ -491,7 +507,7 @@ namespace ramses * @param[in] name Optional name of the object. * @return Pointer to the created TextureSampler, null on failure. */ - RAMSES_API TextureSamplerExternal* createTextureSamplerExternal( + TextureSamplerExternal* createTextureSamplerExternal( ETextureSamplingMethod minSamplingMethod, ETextureSamplingMethod magSamplingMethod, std::string_view name = {}); @@ -501,14 +517,13 @@ namespace ramses * Data type must be a type that passes #ramses::IsArrayResourceDataType, i.e. one of #ramses::EDataType. * See #ramses::ArrayResource for more details. * - * @details If an #ramses::ArrayResource object is created with type #ramses::Byte (#ramses::EDataType::ByteBlob) then an element + * @details If an #ramses::ArrayResource object is created with type std::byte (#ramses::EDataType::ByteBlob) then an element * is defined as one byte, rather than a logical vertex element. Hence, functions of the class * #ramses::ArrayResource referring to element refer to a single byte within byte array, element size is 1 byte * and number of elements is the same as max size in bytes. * * @param[in] numElements The number of elements of the given data type to use for the resource. * @param[in] arrayData Pointer to the data to be used to create the array from. - * @param[in] cacheFlag The optional flag sent to the renderer. The value describes how the cache implementation should handle the resource. * @param[in] name The optional name of the ArrayResource. * @return A pointer to the created ArrayResource, null on failure */ @@ -516,7 +531,6 @@ namespace ramses ArrayResource* createArrayResource( uint32_t numElements, const T* arrayData, - resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache, std::string_view name = {}); /** @@ -534,11 +548,10 @@ namespace ramses * @param[in] swizzle Describes how RGBA channels of the texture are swizzled, * where each member of the struct represents one destination channel that the source channel should get sampled from. * @param[in] generateMipChain Auto generate mipmap levels. Cannot be used if custom data for lower mipmap levels provided. - * @param[in] cacheFlag The optional flag sent to the renderer. The value describes how the cache implementation should handle the resource. * @param[in] name The name of the Texture2D. * @return A pointer to the created Texture2D, null on failure. Will fail with data == nullptr and/or width/height == 0. */ - RAMSES_API Texture2D* createTexture2D( + Texture2D* createTexture2D( ETextureFormat format, uint32_t width, uint32_t height, @@ -546,7 +559,6 @@ namespace ramses const MipLevelData mipLevelData[], // NOLINT(modernize-avoid-c-arrays) bool generateMipChain = false, const TextureSwizzle& swizzle = {}, - resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache, std::string_view name = {}); /** @@ -563,11 +575,10 @@ namespace ramses * conform to GL specification. Order is lowest level (biggest * resolution) to highest level (smallest resolution). * @param[in] generateMipChain Auto generate mipmap levels. Cannot be used if custom data for lower mipmap levels provided. - * @param[in] cacheFlag The optional flag sent to the renderer. The value describes how the cache implementation should handle the resource. * @param[in] name The name of the Texture3D. * @return A pointer to the created Texture3D, null on failure. Will fail with data == nullptr and/or width/height/depth == 0. */ - RAMSES_API Texture3D* createTexture3D( + Texture3D* createTexture3D( ETextureFormat format, uint32_t width, uint32_t height, @@ -575,7 +586,6 @@ namespace ramses size_t mipMapCount, const MipLevelData mipLevelData[], // NOLINT(modernize-avoid-c-arrays) bool generateMipChain = false, - resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache, std::string_view name = {}); /** @@ -591,18 +601,16 @@ namespace ramses * resolution) to highest level (smallest resolution). * @param[in] generateMipChain Auto generate mipmap levels. Cannot be used if custom data for lower mipmap levels provided. * @param[in] swizzle Describes how RGBA channels of the texture are swizzled, - * @param[in] cacheFlag The optional flag sent to the renderer. The value describes how the cache implementation should handle the resource. * @param[in] name The name of the Cube Texture. * @return A pointer to the created Cube Texture, null on failure. Will fail with any face-data == nullptr and/or size == 0. */ - RAMSES_API TextureCube* createTextureCube( + TextureCube* createTextureCube( ETextureFormat format, uint32_t size, size_t mipMapCount, const CubeMipLevelData mipLevelData[], // NOLINT(modernize-avoid-c-arrays) bool generateMipChain = false, const TextureSwizzle& swizzle = {}, - resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache, std::string_view name = {}); /** @@ -611,22 +619,21 @@ namespace ramses * See #ramses::Effect for more details. * * @param[in] effectDesc Effect description. - * @param[in] cacheFlag The optional flag sent to the renderer. The value describes how the cache implementation should handle the resource. * @param[in] name The name of the created Effect. * @return A pointer to the created Effect, null on failure */ - RAMSES_API Effect* createEffect(const EffectDescription& effectDesc, resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache, std::string_view name = {}); + Effect* createEffect(const EffectDescription& effectDesc, std::string_view name = {}); /** * @brief Get the GLSL error messages that were produced at the creation of the last Effect * * @return A string containing the GLSL error messages of the last effect */ - [[nodiscard]] RAMSES_API std::string getLastEffectErrorMessages() const; + [[nodiscard]] std::string getLastEffectErrorMessages() const; /** * @brief Create a new #ramses::ArrayBuffer. The created object is a mutable buffer object that can be used as index or - * as vertex buffer in #ramses::GeometryBinding. + * as vertex buffer in #ramses::Geometry. * * @details The created object has mutable contents and immutable size that has * to be specified at creation time. Upon creation the contents are undefined. The contents of the object @@ -642,7 +649,7 @@ namespace ramses * @param[in] name The optional name of the created array buffer. * @return A pointer to the created array buffer. */ - RAMSES_API ArrayBuffer* createArrayBuffer(EDataType dataType, uint32_t maxNumElements, std::string_view name = {}); + ArrayBuffer* createArrayBuffer(EDataType dataType, uint32_t maxNumElements, std::string_view name = {}); /** * @brief Create a new Texture2DBuffer. The created object is a mutable buffer object that can be used @@ -660,7 +667,7 @@ namespace ramses * @param[in] name The optional name of the created Texture2DBuffer. * @return A pointer to the created Texture2DBuffer. */ - RAMSES_API Texture2DBuffer* createTexture2DBuffer(ETextureFormat textureFormat, uint32_t width, uint32_t height, size_t mipLevelCount, std::string_view name = {}); + Texture2DBuffer* createTexture2DBuffer(ETextureFormat textureFormat, uint32_t width, uint32_t height, size_t mipLevelCount, std::string_view name = {}); /** * @brief Creates a #ramses::DataObject within the scene, which holds a data value of given type. @@ -669,7 +676,7 @@ namespace ramses * @param[in] name optional name of the object. * @return Pointer to the created #ramses::DataObject, null on failure. */ - RAMSES_API DataObject* createDataObject(EDataType dataType, std::string_view name = {}); + DataObject* createDataObject(EDataType dataType, std::string_view name = {}); /** * @brief Annotates a Node as a transformation data provider. @@ -677,10 +684,9 @@ namespace ramses * Linking data means that the consumer's data property will be overridden by provider's data property. * @param[in] node from which transformation data shall be provided. * @param[in] dataId id to reference the provider node in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createTransformationDataProvider(const Node& node, dataProviderId_t dataId); + bool createTransformationDataProvider(const Node& node, dataProviderId_t dataId); /** * @brief Annotates a Node as a transformation data consumer. @@ -688,10 +694,9 @@ namespace ramses * Linking data means that the consumer's data property will be overridden by provider's data property. * @param[in] node which shall consume data from another node. * @param[in] dataId id to reference the consumer node in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createTransformationDataConsumer(const Node& node, dataConsumerId_t dataId); + bool createTransformationDataConsumer(const Node& node, dataConsumerId_t dataId); /** * @brief Annotates a DataObject as a data provider. @@ -699,10 +704,9 @@ namespace ramses * Linking data means that the consumer's data property will be overridden by provider's data property. * @param[in] dataObject from which data shall be provided. * @param[in] dataId id to reference the provider in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createDataProvider(const DataObject& dataObject, dataProviderId_t dataId); + bool createDataProvider(const DataObject& dataObject, dataProviderId_t dataId); /** * @brief Annotates a DataObject as a data consumer. @@ -710,10 +714,9 @@ namespace ramses * Linking data means that the consumer's data property will be overridden by provider's data property. * @param[in] dataObject which shall consume data from another DataObject. * @param[in] dataId id to reference the consumer in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createDataConsumer(const DataObject& dataObject, dataConsumerId_t dataId); + bool createDataConsumer(const DataObject& dataObject, dataConsumerId_t dataId); /** * @brief Annotates a Texture2D as a content provider. @@ -721,10 +724,9 @@ namespace ramses * Linking textures means that the consumer's sampler will use provider's texture as content. * @param[in] texture from which content shall be provided. * @param[in] dataId id to reference the provider in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createTextureProvider(const Texture2D& texture, dataProviderId_t dataId); + bool createTextureProvider(const Texture2D& texture, dataProviderId_t dataId); /** * @brief Sets a new texture to an existing provider. @@ -733,10 +735,9 @@ namespace ramses * need to recreate or relink any provider/consumer. * @param[in] texture from which content shall be provided. * @param[in] dataId id to reference the provider in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t updateTextureProvider(const Texture2D& texture, dataProviderId_t dataId); + bool updateTextureProvider(const Texture2D& texture, dataProviderId_t dataId); /** * @brief Annotates a #ramses::TextureSampler as a content consumer. @@ -744,10 +745,9 @@ namespace ramses * Linking textures means that the consumer's sampler will use provider's texture as content. * @param[in] sampler which shall consume texture content from provider texture. * @param[in] dataId id to reference the consumer in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createTextureConsumer(const TextureSampler& sampler, dataConsumerId_t dataId); + bool createTextureConsumer(const TextureSampler& sampler, dataConsumerId_t dataId); /** * @brief Annotates a #ramses::TextureSamplerMS as a content consumer. @@ -755,10 +755,9 @@ namespace ramses * Linking textures means that the consumer's sampler will use provider's texture as content. * @param[in] sampler which shall consume texture content from provider texture. * @param[in] dataId id to reference the consumer in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t dataId); + bool createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t dataId); /** * @brief Annotates a #ramses::TextureSamplerExternal as a content consumer. @@ -770,10 +769,9 @@ namespace ramses * * @param[in] sampler which shall consume texture content from provider texture. * @param[in] dataId id to reference the consumer in this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t dataId); + bool createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t dataId); /** * @brief Creates a new SceneReference object. @@ -793,7 +791,7 @@ namespace ramses * @param[in] name The optional name of the created SceneReference. * @return A pointer to the created SceneReference. */ - RAMSES_API SceneReference* createSceneReference(sceneId_t referencedScene, std::string_view name = {}); + SceneReference* createSceneReference(sceneId_t referencedScene, std::string_view name = {}); /** * @brief Tell the RamsesRenderer to link a data provider to a data consumer across two scenes. @@ -804,7 +802,7 @@ namespace ramses * A link between provider and consumer is possible, if they are either part of the this scene * or one of its scene references. * - * If the function returns StatusOK, #ramses::IClientEventHandler::dataLinked will be emitted after the link is + * If the function returns true, #ramses::IClientEventHandler::dataLinked will be emitted after the link is * processed by renderer. The link will fail (reported via callback result argument) if either the data provider * or data consumer does not exist or their data type does not match. * @@ -823,30 +821,28 @@ namespace ramses * @param providerId The id of the data provider within the providerScene or providerReference. * @param consumerReference A pointer to the SceneReference which consumes the data, or nullptr to use this scene. * @param consumerId The id of the data consumer within the consumerScene or consumerReference. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId); + bool linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId); /** * @brief Removes an existing link between two scenes (see #ramses::Scene::linkData). - * @details If the function returns StatusOK, #ramses::IClientEventHandler::dataUnlinked will be emitted after it is + * @details If the function returns true, #ramses::IClientEventHandler::dataUnlinked will be emitted after it is * processed by renderer. If successful the operation can be assumed to be effective in the next frame consumer * scene is rendered after flushed. * * @param consumerReference The pointer to the SceneReference which consumes the data, or nullptr to use this scene. * @param consumerId The id of the data consumer within the consumerReference. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId); + bool unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId); /** * @brief Getter for #ramses::RamsesClient this Scene was created from * * @return the parent RamsesCLient */ - RAMSES_API RamsesClient& getRamsesClient(); + RamsesClient& getRamsesClient(); /** * @brief Get a resource which is owned by the scene by id @@ -854,41 +850,93 @@ namespace ramses * @param[in] id The resource id of the resource to get. * @return Pointer to the resource if found, nullptr otherwise. */ - [[nodiscard]] RAMSES_API const Resource* getResource(resourceId_t id) const; + [[nodiscard]] const Resource* getResource(resourceId_t id) const; /** * @copydoc getResource(resourceId_t id) const **/ - RAMSES_API Resource* getResource(resourceId_t id); + [[nodiscard]] Resource* getResource(resourceId_t id); /** - * Stores internal data for implementation specifics of Scene. - */ - SceneImpl& m_impl; + * Get the internal data for implementation specifics of Scene. + */ + [[nodiscard]] internal::SceneImpl& impl(); + + /** + * Get the internal data for implementation specifics of Scene. + */ + [[nodiscard]] const internal::SceneImpl& impl() const; protected: /** * @brief RamsesClient is the factory for creating Scene instances. */ - friend class RamsesClientImpl; + friend class internal::RamsesClientImpl; /** * @brief Constructor of the Scene * * @param[in] impl Internal data for implementation specifics of Scene (sink - instance becomes owner) */ - explicit Scene(std::unique_ptr impl); + explicit Scene(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Scene. + */ + internal::SceneImpl& m_impl; private: /// Internal implementation of #createArrayResource - template RAMSES_API ArrayResource* createArrayResourceInternal(uint32_t numElements, const T* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name); + template ArrayResource* createArrayResourceInternal(uint32_t numElements, const T* arrayData, std::string_view name); + + template [[nodiscard]] const T* findObjectByNameInternal(std::string_view name) const; + template [[nodiscard]] T* findObjectByNameInternal(std::string_view name); + [[nodiscard]] const SceneObject* findObjectById(sceneObjectId_t id) const; + [[nodiscard]] SceneObject* findObjectById(sceneObjectId_t id); + + /// Internal static helper to validate type + template + static void StaticTypeCheck(); }; - template ArrayResource* Scene::createArrayResource(uint32_t numElements, const T* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name) + template ArrayResource* Scene::createArrayResource(uint32_t numElements, const T* arrayData, std::string_view name) { static_assert(IsArrayResourceDataType(), "Unsupported data type!"); - return createArrayResourceInternal(numElements, arrayData, cacheFlag, name); + return createArrayResourceInternal(numElements, arrayData, name); + } + + template T* Scene::findObject(std::string_view name) + { + StaticTypeCheck(); + return findObjectByNameInternal(name); + } + + template const T* Scene::findObject(std::string_view name) const + { + StaticTypeCheck(); + return findObjectByNameInternal(name); } -} -#endif + template T* Scene::findObject(sceneObjectId_t id) + { + StaticTypeCheck(); + if constexpr (std::is_same_v) + return findObjectById(id); + return object_cast(findObjectById(id)); + } + + template const T* Scene::findObject(sceneObjectId_t id) const + { + StaticTypeCheck(); + if constexpr (std::is_same_v) + return findObjectById(id); + return object_cast(findObjectById(id)); + } + + template + void Scene::StaticTypeCheck() + { + static_assert(std::is_base_of_v, "Type not derived from SceneObject or undefined! Make sure you include the header for the target type class."); + static_assert(!std::is_base_of_v, "To find logic objects use LogicEngine::findObject."); + } +} diff --git a/include/ramses/client/SceneConfig.h b/include/ramses/client/SceneConfig.h new file mode 100644 index 000000000..b2fd703be --- /dev/null +++ b/include/ramses/client/SceneConfig.h @@ -0,0 +1,119 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/APIExport.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/EScenePublicationMode.h" + +#include + +namespace ramses +{ + namespace internal + { + class SceneConfigImpl; + } + + /** + * @ingroup CoreAPI + * @brief The SceneConfig holds a set of parameters to be used when creating a scene or loading the scene from file/memory. + * Some parameters are only relevant for loading scene. + */ + class RAMSES_API SceneConfig + { + public: + /** + * @brief default constructor + */ + SceneConfig(); + + /** + * @brief constructor of SceneConfig + * + * @param sceneId Each scene requires a sceneId for global identification (sceneId is used for scene mapping on renderer side). + * If no sceneId is provided Ramses will use the sceneId stored in the serialized file or raise an error if the scene is created at runtime. + * @param publicationMode see #ramses::SceneConfig::setPublicationMode for details + */ + explicit SceneConfig(sceneId_t sceneId, EScenePublicationMode publicationMode = EScenePublicationMode::LocalOnly); + + /** + * @brief Destructor of SceneConfig + */ + ~SceneConfig(); + + /** + * @brief Set the publication mode that will be used for this scene. + * + * Later calls to publish must use the same value as given here. + * Setting this to #ramses::EScenePublicationMode::LocalOnly for scenes that will never be + * published remotely enables optimization possibilities. + * + * @param publicationMode Publication mode to use with scene. #ramses::EScenePublicationMode::LocalOnly is default. + */ + void setPublicationMode(EScenePublicationMode publicationMode); + + /** + * Sets the sceneId for the loaded/created scene. By default the id stored in the binary will be used. + * + * @param sceneId The sceneId for the loaded scene + */ + void setSceneId(sceneId_t sceneId); + + /** + * By default, ramses performs sanity checks on the binary data when the scene is loaded from file. + * This behavior can be disabled here (not recommended unless there are other measures to ensure valid binary data). + * + * @param enabled flag to disable/enable memory verification when loading the scene + */ + void setMemoryVerificationEnabled(bool enabled); + + /** + * @brief Copy constructor + * @param other source to copy from + */ + SceneConfig(const SceneConfig& other); + + /** + * @brief Move constructor + * @param other source to move from + */ + SceneConfig(SceneConfig&& other) noexcept; + + /** + * @brief Copy assignment + * @param other source to copy from + * @return this instance + */ + SceneConfig& operator=(const SceneConfig& other); + + /** + * @brief Move assignment + * @param other source to move from + * @return this instance + */ + SceneConfig& operator=(SceneConfig&& other) noexcept; + + /** + * Get the internal data for implementation specifics of SceneConfig. + */ + [[nodiscard]] internal::SceneConfigImpl& impl(); + + /** + * Get the internal data for implementation specifics of SceneConfig. + */ + [[nodiscard]] const internal::SceneConfigImpl& impl() const; + + protected: + /** + * Stores internal data for implementation specifics of SceneConfig. + */ + std::unique_ptr m_impl; + }; +} diff --git a/client/ramses-client-api/include/ramses-client-api/SceneGraphIterator.h b/include/ramses/client/SceneGraphIterator.h similarity index 77% rename from client/ramses-client-api/include/ramses-client-api/SceneGraphIterator.h rename to include/ramses/client/SceneGraphIterator.h index 2f0b5a706..99b526e8e 100644 --- a/client/ramses-client-api/include/ramses-client-api/SceneGraphIterator.h +++ b/include/ramses/client/SceneGraphIterator.h @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEGRAPHITERATOR_H -#define RAMSES_SCENEGRAPHITERATOR_H +#pragma once -#include "ramses-client-api/RamsesObjectTypes.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/RamsesObjectTypes.h" +#include "ramses/framework/APIExport.h" #include namespace ramses { + namespace internal + { + class SceneGraphIteratorImpl; + } + /// Tree traversal style enum class ETreeTraversalStyle { @@ -22,14 +26,13 @@ namespace ramses BreadthFirst }; - class SceneGraphIteratorImpl; class Node; /** * @ingroup CoreAPI * @brief A SceneObjectIterator can iterate through the nodes in the scene graph with the order specified as traversal style */ - class SceneGraphIterator + class RAMSES_API SceneGraphIterator { public: /** @@ -40,12 +43,12 @@ namespace ramses * @param[in] traversalStyle traversal style that should be used * @param[in] objectType Type of objects to iterate through */ - RAMSES_API SceneGraphIterator(Node& startNode, ETreeTraversalStyle traversalStyle, ERamsesObjectType objectType = ERamsesObjectType::Node); + SceneGraphIterator(Node& startNode, ETreeTraversalStyle traversalStyle, ERamsesObjectType objectType = ERamsesObjectType::Node); /** * @brief Destructor. */ - RAMSES_API ~SceneGraphIterator(); + ~SceneGraphIterator(); /** * @brief Returns the next node while iterating. @@ -54,15 +57,13 @@ namespace ramses * * Iterator is invalid and may no longer be used if any nodes are added or removed. */ - RAMSES_API Node* getNext(); + Node* getNext(); protected: /** * @brief Stores internal data for implementation specifics of SceneGraphIterator. */ - std::unique_ptr m_impl; + std::unique_ptr m_impl; }; } -#endif - diff --git a/client/ramses-client-api/include/ramses-client-api/SceneIterator.h b/include/ramses/client/SceneIterator.h similarity index 77% rename from client/ramses-client-api/include/ramses-client-api/SceneIterator.h rename to include/ramses/client/SceneIterator.h index d150e20da..dc2dc34ad 100644 --- a/client/ramses-client-api/include/ramses-client-api/SceneIterator.h +++ b/include/ramses/client/SceneIterator.h @@ -6,14 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEITERATOR_H -#define RAMSES_SCENEITERATOR_H +#pragma once -#include "ramses-client-api/RamsesObject.h" +#include "ramses/framework/RamsesObject.h" namespace ramses { - class SceneIteratorImpl; + namespace internal + { + class SceneIteratorImpl; + } + class RamsesClient; class Scene; @@ -23,7 +26,7 @@ namespace ramses * * It provides a way to traverse all scenes created with a given client. */ - class SceneIterator + class RAMSES_API SceneIterator { public: /** @@ -31,12 +34,12 @@ namespace ramses * * @param[in] client RamsesClient whose scenes to iterate through **/ - RAMSES_API explicit SceneIterator(const RamsesClient& client); + explicit SceneIterator(const RamsesClient& client); /** * Destructor **/ - RAMSES_API ~SceneIterator(); + ~SceneIterator(); /** * @brief Returns the next scene while iterating. @@ -44,11 +47,9 @@ namespace ramses * @return The next scene, null when no more scenes are available. * The iterator is invalid and may not be used after any scenes are added or removed. **/ - RAMSES_API Scene* getNext(); + Scene* getNext(); private: - std::unique_ptr m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/SceneObject.h b/include/ramses/client/SceneObject.h similarity index 50% rename from client/ramses-client-api/include/ramses-client-api/SceneObject.h rename to include/ramses/client/SceneObject.h index e22b43e51..f458818ee 100644 --- a/client/ramses-client-api/include/ramses-client-api/SceneObject.h +++ b/include/ramses/client/SceneObject.h @@ -6,38 +6,54 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEOBJECT_H -#define RAMSES_SCENEOBJECT_H +#pragma once -#include "ramses-client-api/ClientObject.h" +#include "ramses/client/ClientObject.h" namespace ramses { + namespace internal + { + class SceneObjectImpl; + class SceneObjectRegistry; + } + class Scene; + /** * @ingroup CoreAPI * @brief The SceneObject is a base class for all client API objects owned by a Scene. */ - class SceneObject : public ClientObject + class RAMSES_API SceneObject : public ClientObject { public: - /** - * Stores internal data for implementation specifics of SceneObject. - */ - class SceneObjectImpl& m_impl; - /** * @brief Returns scene object id which is automatically assigned at creation time of object and is unique within scope of one scene. * * @return Scene object id. */ - [[nodiscard]] RAMSES_API sceneObjectId_t getSceneObjectId() const; + [[nodiscard]] sceneObjectId_t getSceneObjectId() const; /** - * @brief Returns sceneid to which this object belongs to - * - * @return Scene id this object belongs to - */ - [[nodiscard]] RAMSES_API sceneId_t getSceneId() const; + * Get the owning #ramses::Scene. + * @return owning #ramses::Scene + */ + [[nodiscard]] const Scene& getScene() const; + + /** + * Get the owning #ramses::Scene. + * @return owning #ramses::Scene + */ + [[nodiscard]] Scene& getScene(); + + /** + * Get the internal data for implementation specifics of SceneObject. + */ + [[nodiscard]] internal::SceneObjectImpl& impl(); + + /** + * Get the internal data for implementation specifics of SceneObject. + */ + [[nodiscard]] const internal::SceneObjectImpl& impl() const; protected: /** @@ -45,8 +61,16 @@ namespace ramses * * @param[in] impl Internal data for implementation specifics of SceneObject (sink - instance becomes owner) */ - explicit SceneObject(std::unique_ptr impl); + explicit SceneObject(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of SceneObject. + */ + internal::SceneObjectImpl& m_impl; + + /** + * @brief Scene is the factory for creating SceneReference instances. + */ + friend class internal::SceneObjectRegistry; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/SceneObjectIterator.h b/include/ramses/client/SceneObjectIterator.h similarity index 69% rename from client/ramses-client-api/include/ramses-client-api/SceneObjectIterator.h rename to include/ramses/client/SceneObjectIterator.h index 217d67c09..22e94582f 100644 --- a/client/ramses-client-api/include/ramses-client-api/SceneObjectIterator.h +++ b/include/ramses/client/SceneObjectIterator.h @@ -6,14 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEOBJECTITERATOR_H -#define RAMSES_SCENEOBJECTITERATOR_H +#pragma once -#include "ramses-client-api/RamsesObject.h" +#include "ramses/framework/RamsesObject.h" namespace ramses { - class ObjectIteratorImpl; + namespace internal + { + class ObjectIteratorImpl; + } + class Scene; /** @@ -22,21 +25,23 @@ namespace ramses * * It provides a way to traverse all objects owned by a given scene. */ - class SceneObjectIterator + class RAMSES_API SceneObjectIterator { public: /** * @brief A SceneObjectIterator can iterate through objects of given type within a scene. + * Note that this will not iterate over #ramses::LogicObject instances created from #ramses::LogicEngine, + * use #ramses::LogicEngine::getCollection for those. * * @param[in] scene Scene whose objects to iterate through * @param[in] objectType Optional type of objects to iterate through. **/ - RAMSES_API explicit SceneObjectIterator(const Scene& scene, ERamsesObjectType objectType = ERamsesObjectType::RamsesObject); + explicit SceneObjectIterator(const Scene& scene, ERamsesObjectType objectType = ERamsesObjectType::RamsesObject); /** * @brief Destructor **/ - RAMSES_API ~SceneObjectIterator(); + ~SceneObjectIterator(); /** * @brief Iterate through all objects of given type @@ -44,11 +49,9 @@ namespace ramses * * Iterator is invalid and may no longer be used if any objects are added or removed. **/ - RAMSES_API RamsesObject* getNext(); + RamsesObject* getNext(); private: - std::unique_ptr m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/SceneReference.h b/include/ramses/client/SceneReference.h similarity index 81% rename from client/ramses-client-api/include/ramses-client-api/SceneReference.h rename to include/ramses/client/SceneReference.h index f8ee4978b..2a92ef0a6 100644 --- a/client/ramses-client-api/include/ramses-client-api/SceneReference.h +++ b/include/ramses/client/SceneReference.h @@ -6,14 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEREFERENCE_H -#define RAMSES_SCENEREFERENCE_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "ramses-framework-api/RendererSceneState.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/RendererSceneState.h" namespace ramses { + namespace internal + { + class SceneReferenceImpl; + } + /** * @ingroup CoreAPI * @brief The SceneReference object refers to another ramses scene using its sceneId. @@ -34,7 +38,7 @@ namespace ramses * so it is possible to change its master scene regardless of its actual state on renderer side (even if actively rendered) * but this should be done only with extra caution and understanding of the consequences mentioned above. */ - class SceneReference : public SceneObject + class RAMSES_API SceneReference : public SceneObject { public: /** @@ -61,22 +65,21 @@ namespace ramses * of external conditions. * * @param[in] requestedState - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t requestState(RendererSceneState requestedState); + bool requestState(RendererSceneState requestedState); /** * @brief Get the currently requested state for this scene reference. * @return The state of the reference. */ - [[nodiscard]] RAMSES_API RendererSceneState getRequestedState() const; + [[nodiscard]] RendererSceneState getRequestedState() const; /** * @brief Get the sceneId of the referenced scene. * @return The scene id of the referenced scene */ - [[nodiscard]] RAMSES_API sceneId_t getReferencedSceneId() const; + [[nodiscard]] sceneId_t getReferencedSceneId() const; /** * @brief Request callbacks (#ramses::IClientEventHandler::sceneReferenceFlushed) to be triggered whenever a flush @@ -91,10 +94,9 @@ namespace ramses * Scene reference has to be in Ready state to be able to receive scene version tag notifications. * * @param[in] flag enable/disable notifications for this scene - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t requestNotificationsForSceneVersionTags(bool flag); + bool requestNotificationsForSceneVersionTags(bool flag); /** * @brief Set scene render order @@ -104,29 +106,36 @@ namespace ramses * master scene render order is set on renderer side (#ramses::RendererSceneControl::setSceneDisplayBufferAssignment). * * @param[in] renderOrder Lower value means that a scene is rendered before a scene with higher value. Default is 0. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setRenderOrder(int32_t renderOrder); + bool setRenderOrder(int32_t renderOrder); /** - * Stores internal data for implementation specifics of SceneReference. - */ - class SceneReferenceImpl& m_impl; + * Get the internal data for implementation specifics of SceneReference. + */ + [[nodiscard]] internal::SceneReferenceImpl& impl(); + + /** + * Get the internal data for implementation specifics of SceneReference. + */ + [[nodiscard]] const internal::SceneReferenceImpl& impl() const; protected: /** * @brief Scene is the factory for creating SceneReference instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for SceneReference. * * @param[in] impl Internal data for implementation specifics of SceneReference (sink - instance becomes owner) */ - explicit SceneReference(std::unique_ptr impl); + explicit SceneReference(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of SceneReference. + */ + internal::SceneReferenceImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Texture2D.h b/include/ramses/client/Texture2D.h similarity index 60% rename from client/ramses-client-api/include/ramses-client-api/Texture2D.h rename to include/ramses/client/Texture2D.h index e281ff18e..05ce03dc0 100644 --- a/client/ramses-client-api/include/ramses-client-api/Texture2D.h +++ b/include/ramses/client/Texture2D.h @@ -6,22 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2D_H -#define RAMSES_TEXTURE2D_H +#pragma once -#include "ramses-client-api/Resource.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "TextureEnums.h" +#include "ramses/client/Resource.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/TextureEnums.h" #include "MipLevelData.h" #include "TextureSwizzle.h" namespace ramses { + namespace internal + { + class Texture2DImpl; + } + /** * @ingroup CoreAPI * @brief Texture represents a 2-D texture resource. */ - class Texture2D : public Resource + class RAMSES_API Texture2D : public Resource { public: /** @@ -29,47 +33,55 @@ namespace ramses * * @return Texture width */ - [[nodiscard]] RAMSES_API uint32_t getWidth() const; + [[nodiscard]] uint32_t getWidth() const; /** * @brief Gets texture height * * @return Texture height */ - [[nodiscard]] RAMSES_API uint32_t getHeight() const; + [[nodiscard]] uint32_t getHeight() const; /** * @brief Gets texture format * * @return Texture format */ - [[nodiscard]] RAMSES_API ETextureFormat getTextureFormat() const; + [[nodiscard]] ETextureFormat getTextureFormat() const; /** * @brief Gets swizzle description * * @return Swizzle Description */ - [[nodiscard]] RAMSES_API const TextureSwizzle& getTextureSwizzle() const; + [[nodiscard]] const TextureSwizzle& getTextureSwizzle() const; /** - * Stores internal data for implementation specifics of Texture. - */ - class Texture2DImpl& m_impl; + * Get the internal data for implementation specifics of Texture. + */ + [[nodiscard]] internal::Texture2DImpl& impl(); + + /** + * Get the internal data for implementation specifics of Texture. + */ + [[nodiscard]] const internal::Texture2DImpl& impl() const; protected: /** * @brief Scene is the factory for creating Texture instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of Texture * * @param[in] impl Internal data for implementation specifics of Texture (sink - instance becomes owner) */ - explicit Texture2D(std::unique_ptr impl); + explicit Texture2D(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Texture. + */ + internal::Texture2DImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Texture2DBuffer.h b/include/ramses/client/Texture2DBuffer.h similarity index 72% rename from client/ramses-client-api/include/ramses-client-api/Texture2DBuffer.h rename to include/ramses/client/Texture2DBuffer.h index 347c1d9c2..597eddf89 100644 --- a/client/ramses-client-api/include/ramses-client-api/Texture2DBuffer.h +++ b/include/ramses/client/Texture2DBuffer.h @@ -6,14 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DBUFFER_H -#define RAMSES_TEXTURE2DBUFFER_H +#pragma once -#include "ramses-client-api/SceneObject.h" -#include "TextureEnums.h" +#include "ramses/client/SceneObject.h" +#include "ramses/framework/TextureEnums.h" namespace ramses { + namespace internal + { + class Texture2DBufferImpl; + } + /** * @ingroup CoreAPI * @brief The Texture2DBuffer is a mutable texture buffer used to hold texture data with the possibility @@ -23,7 +27,7 @@ namespace ramses * according to OpenGL specification (each further mipMap level has half the size of the previous * mipMap level). Refer to documentation of glTexStorage2D for more details. */ - class Texture2DBuffer : public SceneObject + class RAMSES_API Texture2DBuffer : public SceneObject { public: /** @@ -40,10 +44,9 @@ namespace ramses * @param data Pointer in memory to the data provided for update. The function makes * a copy of the data into Texture2DBuffer data structures. Texture2DBuffer * object does not take ownership of the memory data passed to it. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t updateData(size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height, const void* data); + bool updateData(size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height, const void* data); /** * @brief Returns the number of mipmap levels created for the Texture2DBuffer (same as provided in @@ -51,7 +54,7 @@ namespace ramses * * @return number of mipmap levels */ - [[nodiscard]] RAMSES_API size_t getMipLevelCount() const; + [[nodiscard]] size_t getMipLevelCount() const; /** * @brief Returns the size of a specific mipmap level in texels @@ -59,10 +62,9 @@ namespace ramses * @param[in] mipLevel The mipMap level of which the size will be returned * @param[out] widthOut the width of the mipMap level which was specified * @param[out] heightOut the height of the mipMap level which was specified - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const; + bool getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const; /** * @brief Returns the size of a specific mipmap level in bytes @@ -70,14 +72,14 @@ namespace ramses * @param[in] mipLevel The mipMap level of which the size will be returned * @return Size of data in bytes for given mip level, 0 if mipLevel invalid */ - [[nodiscard]] RAMSES_API size_t getMipLevelDataSizeInBytes(size_t mipLevel) const; + [[nodiscard]] size_t getMipLevelDataSizeInBytes(size_t mipLevel) const; /** * @brief Returns the texel format provided at creation * * @return The texel format provided at creation */ - [[nodiscard]] RAMSES_API ETextureFormat getTexelFormat() const; + [[nodiscard]] ETextureFormat getTexelFormat() const; /** * @brief Copies the data of a single mip-level into a user-provided buffer. @@ -87,29 +89,36 @@ namespace ramses * @param[in] mipLevel The mipMap level of which the data will be returned * @param[out] buffer The buffer where the mip data will be copied into * @param[in] bufferSize The size of given buffer in bytes - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getMipLevelData(size_t mipLevel, void* buffer, size_t bufferSize) const; + bool getMipLevelData(size_t mipLevel, void* buffer, size_t bufferSize) const; /** - * Stores internal data for implementation specifics of Texture2DBuffer. - */ - class Texture2DBufferImpl& m_impl; + * Get the internal data for implementation specifics of Texture2DBuffer. + */ + [[nodiscard]] internal::Texture2DBufferImpl& impl(); + + /** + * Get the internal data for implementation specifics of Texture2DBuffer. + */ + [[nodiscard]] const internal::Texture2DBufferImpl& impl() const; protected: /** * @brief Scene is the factory for creating Texture2DBuffer instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for Texture2DBuffer. * * @param[in] impl Internal data for implementation specifics of Texture2DBuffer (sink - instance becomes owner) */ - explicit Texture2DBuffer(std::unique_ptr impl); + explicit Texture2DBuffer(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Texture2DBuffer. + */ + internal::Texture2DBufferImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/Texture3D.h b/include/ramses/client/Texture3D.h similarity index 59% rename from client/ramses-client-api/include/ramses-client-api/Texture3D.h rename to include/ramses/client/Texture3D.h index 8d37f562f..e0b97d4e9 100644 --- a/client/ramses-client-api/include/ramses-client-api/Texture3D.h +++ b/include/ramses/client/Texture3D.h @@ -6,20 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE3D_H -#define RAMSES_TEXTURE3D_H +#pragma once -#include "ramses-client-api/Resource.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "TextureEnums.h" +#include "ramses/client/Resource.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/TextureEnums.h" namespace ramses { + namespace internal + { + class Texture3DImpl; + } + /** * @ingroup CoreAPI * @brief Texture represents a texture resource */ - class Texture3D : public Resource + class RAMSES_API Texture3D : public Resource { public: /** @@ -27,47 +31,55 @@ namespace ramses * * @return Texture width */ - [[nodiscard]] RAMSES_API uint32_t getWidth() const; + [[nodiscard]] uint32_t getWidth() const; /** * @brief Gets texture height * * @return Texture height */ - [[nodiscard]] RAMSES_API uint32_t getHeight() const; + [[nodiscard]] uint32_t getHeight() const; /** * @brief Gets texture depth * * @return Texture depth */ - [[nodiscard]] RAMSES_API uint32_t getDepth() const; + [[nodiscard]] uint32_t getDepth() const; /** * @brief Gets texture format * * @return Texture format */ - [[nodiscard]] RAMSES_API ETextureFormat getTextureFormat() const; + [[nodiscard]] ETextureFormat getTextureFormat() const; /** - * Stores internal data for implementation specifics of Texture. - */ - class Texture3DImpl& m_impl; + * Get the internal data for implementation specifics of Texture. + */ + [[nodiscard]] internal::Texture3DImpl& impl(); + + /** + * Get the internal data for implementation specifics of Texture. + */ + [[nodiscard]] const internal::Texture3DImpl& impl() const; protected: /** * @brief Scene is the factory for creating Texture instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of Texture * * @param[in] impl Internal data for implementation specifics of Texture (sink - instance becomes owner) */ - explicit Texture3D(std::unique_ptr impl); + explicit Texture3D(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of Texture. + */ + internal::Texture3DImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/TextureCube.h b/include/ramses/client/TextureCube.h similarity index 59% rename from client/ramses-client-api/include/ramses-client-api/TextureCube.h rename to include/ramses/client/TextureCube.h index 79dc2109f..8c5be83cc 100644 --- a/client/ramses-client-api/include/ramses-client-api/TextureCube.h +++ b/include/ramses/client/TextureCube.h @@ -6,23 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURECUBE_H -#define RAMSES_TEXTURECUBE_H +#pragma once -#include "ramses-client-api/Resource.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/TextureEnums.h" +#include "ramses/client/Resource.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/TextureEnums.h" #include "TextureSwizzle.h" namespace ramses { + namespace internal + { + class TextureCubeImpl; + } + /** * @ingroup CoreAPI * @brief TextureCube stores pixel data with 6 equally sized quadratic faces. */ - class TextureCube : public Resource + class RAMSES_API TextureCube : public Resource { public: /** @@ -30,40 +34,48 @@ namespace ramses * * @return Texture cube edge length */ - [[nodiscard]] RAMSES_API uint32_t getSize() const; + [[nodiscard]] uint32_t getSize() const; /** * @brief Gets texture format * * @return Texture format */ - [[nodiscard]] RAMSES_API ETextureFormat getTextureFormat() const; + [[nodiscard]] ETextureFormat getTextureFormat() const; /** * @brief Gets swizzle description * * @return Swizzle Description */ - [[nodiscard]] RAMSES_API const TextureSwizzle& getTextureSwizzle() const; + [[nodiscard]] const TextureSwizzle& getTextureSwizzle() const; /** - * Stores internal data for implementation specifics of TextureCube. - */ - class TextureCubeImpl& m_impl; + * Get the internal data for implementation specifics of TextureCube. + */ + [[nodiscard]] internal::TextureCubeImpl& impl(); + + /** + * Get the internal data for implementation specifics of TextureCube. + */ + [[nodiscard]] const internal::TextureCubeImpl& impl() const; protected: /** * @brief Scene is the factory for creating TextureCube instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor of TextureCube * * @param[in] impl Internal data for implementation specifics of TextureCube (sink - instance becomes owner) */ - explicit TextureCube(std::unique_ptr impl); + explicit TextureCube(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of TextureCube. + */ + internal::TextureCubeImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/TextureSampler.h b/include/ramses/client/TextureSampler.h similarity index 64% rename from client/ramses-client-api/include/ramses-client-api/TextureSampler.h rename to include/ramses/client/TextureSampler.h index 90bbcdef5..41eff0df6 100644 --- a/client/ramses-client-api/include/ramses-client-api/TextureSampler.h +++ b/include/ramses/client/TextureSampler.h @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURESAMPLER_H -#define RAMSES_TEXTURESAMPLER_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" -#include "TextureEnums.h" +#include "ramses/framework/TextureEnums.h" namespace ramses { + namespace internal + { + class TextureSamplerImpl; + } + class Texture2D; class Texture3D; class TextureCube; @@ -25,7 +29,7 @@ namespace ramses * @ingroup CoreAPI * @brief The TextureSampler holds a texture and its sampling parameters */ - class TextureSampler : public SceneObject + class RAMSES_API TextureSampler : public SceneObject { public: /** @@ -33,49 +37,49 @@ namespace ramses * * @return ETextureAddressMode wrap mode for u axis */ - [[nodiscard]] RAMSES_API ETextureAddressMode getWrapUMode() const; + [[nodiscard]] ETextureAddressMode getWrapUMode() const; /** * @brief Gets the texture wrap mode for the v axis * * @return ETextureAddressMode wrap mode for v axis */ - [[nodiscard]] RAMSES_API ETextureAddressMode getWrapVMode() const; + [[nodiscard]] ETextureAddressMode getWrapVMode() const; /** * @brief Gets the texture wrap mode for the r axis * * @return ETextureAddressMode wrap mode for r axis */ - [[nodiscard]] RAMSES_API ETextureAddressMode getWrapRMode() const; + [[nodiscard]] ETextureAddressMode getWrapRMode() const; /** * @brief Gets the texture min sampling method * * @return ETextureSamplingMethod min sampling method */ - [[nodiscard]] RAMSES_API ETextureSamplingMethod getMinSamplingMethod() const; + [[nodiscard]] ETextureSamplingMethod getMinSamplingMethod() const; /** * @brief Gets the texture mag sampling method * * @return ETextureSamplingMethod mag sampling method */ - [[nodiscard]] RAMSES_API ETextureSamplingMethod getMagSamplingMethod() const; + [[nodiscard]] ETextureSamplingMethod getMagSamplingMethod() const; /** * @brief Gets the texture sampling anisotropy level * * @return The texture sampling anisotropy level. */ - [[nodiscard]] RAMSES_API uint32_t getAnisotropyLevel() const; + [[nodiscard]] uint32_t getAnisotropyLevel() const; /** * @brief Gets the type of the texture * * @return Type of the texture, see ERamsesObjectType enum for possible values. */ - [[nodiscard]] RAMSES_API ERamsesObjectType getTextureType() const; + [[nodiscard]] ERamsesObjectType getTextureType() const; /** * @brief Replaces current texture content source with a new one. @@ -85,37 +89,44 @@ namespace ramses * to be used for data linking (see ramses::Scene::createTextureConsumer) * * @param[in] dataSource Texture data source to be used with this sampler. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setTextureData(const Texture2D& dataSource); + bool setTextureData(const Texture2D& dataSource); /// @copybrief setTextureData(const Texture2D&) - RAMSES_API status_t setTextureData(const Texture3D& dataSource); + bool setTextureData(const Texture3D& dataSource); /// @copybrief setTextureData(const Texture2D&) - RAMSES_API status_t setTextureData(const TextureCube& dataSource); + bool setTextureData(const TextureCube& dataSource); /// @copybrief setTextureData(const Texture2D&) - RAMSES_API status_t setTextureData(const Texture2DBuffer& dataSource); + bool setTextureData(const Texture2DBuffer& dataSource); /// @copybrief setTextureData(const Texture2D&) - RAMSES_API status_t setTextureData(const RenderBuffer& dataSource); + bool setTextureData(const RenderBuffer& dataSource); /** - * Stores internal data for implementation specifics of TextureSampler. - */ - class TextureSamplerImpl& m_impl; + * Get the internal data for implementation specifics of TextureSampler. + */ + [[nodiscard]] internal::TextureSamplerImpl& impl(); + + /** + * Get the internal data for implementation specifics of TextureSampler. + */ + [[nodiscard]] const internal::TextureSamplerImpl& impl() const; protected: /** * @brief Scene is the factory for creating TextureSampler instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for TextureSampler. * * @param[in] impl Internal data for implementation specifics of TextureSampler (sink - instance becomes owner) */ - explicit TextureSampler(std::unique_ptr impl); + explicit TextureSampler(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of TextureSampler. + */ + internal::TextureSamplerImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/TextureSamplerExternal.h b/include/ramses/client/TextureSamplerExternal.h similarity index 62% rename from client/ramses-client-api/include/ramses-client-api/TextureSamplerExternal.h rename to include/ramses/client/TextureSamplerExternal.h index d10127738..b77a786ff 100644 --- a/client/ramses-client-api/include/ramses-client-api/TextureSamplerExternal.h +++ b/include/ramses/client/TextureSamplerExternal.h @@ -6,39 +6,51 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURESAMPLEREXTERNAL_H -#define RAMSES_TEXTURESAMPLEREXTERNAL_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class TextureSamplerImpl; + } + /** * @ingroup CoreAPI * @brief The #ramses::TextureSamplerExternal is used to sample from external textures data when bound * to a #ramses::Appearance uniform input (#ramses::Appearance::setInputTexture called with #ramses::TextureSamplerExternal) */ - class TextureSamplerExternal : public SceneObject + class RAMSES_API TextureSamplerExternal : public SceneObject { public: /** - * Stores internal data for implementation specifics of TextureSamplerExternal. - */ - class TextureSamplerImpl& m_impl; + * Get the internal data for implementation specifics of TextureSamplerExternal. + */ + [[nodiscard]] internal::TextureSamplerImpl& impl(); + + /** + * Get the internal data for implementation specifics of TextureSamplerExternal. + */ + [[nodiscard]] const internal::TextureSamplerImpl& impl() const; protected: /** * @brief Scene is the factory for creating TextureSamplerExternal instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for TextureSamplerExternal. * * @param[in] impl Internal data for implementation specifics of TextureSamplerExternal (sink - instance becomes owner) */ - explicit TextureSamplerExternal(std::unique_ptr impl); + explicit TextureSamplerExternal(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of TextureSamplerExternal. + */ + internal::TextureSamplerImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/TextureSamplerMS.h b/include/ramses/client/TextureSamplerMS.h similarity index 62% rename from client/ramses-client-api/include/ramses-client-api/TextureSamplerMS.h rename to include/ramses/client/TextureSamplerMS.h index 93cc694d7..9e8fa7f46 100644 --- a/client/ramses-client-api/include/ramses-client-api/TextureSamplerMS.h +++ b/include/ramses/client/TextureSamplerMS.h @@ -6,39 +6,51 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURESAMPLERMS_H -#define RAMSES_TEXTURESAMPLERMS_H +#pragma once -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" namespace ramses { + namespace internal + { + class TextureSamplerImpl; + } + /** * @ingroup CoreAPI * @brief The #ramses::TextureSamplerMS is used to sample multisampled data when bound * to a #ramses::Appearance uniform input (#ramses::Appearance::setInputTexture called with #ramses::TextureSamplerMS) */ - class TextureSamplerMS : public SceneObject + class RAMSES_API TextureSamplerMS : public SceneObject { public: /** - * Stores internal data for implementation specifics of TextureSamplerMS. - */ - class TextureSamplerImpl& m_impl; + * Get the internal data for implementation specifics of TextureSamplerMS. + */ + [[nodiscard]] internal::TextureSamplerImpl& impl(); + + /** + * Get the internal data for implementation specifics of TextureSamplerMS. + */ + [[nodiscard]] const internal::TextureSamplerImpl& impl() const; protected: /** * @brief Scene is the factory for creating TextureSamplerMS instances. */ - friend class RamsesObjectRegistry; + friend class internal::SceneObjectRegistry; /** * @brief Constructor for TextureSamplerMS. * * @param[in] impl Internal data for implementation specifics of TextureSamplerMS (sink - instance becomes owner) */ - explicit TextureSamplerMS(std::unique_ptr impl); + explicit TextureSamplerMS(std::unique_ptr impl); + + /** + * Stores internal data for implementation specifics of TextureSamplerMS. + */ + internal::TextureSamplerImpl& m_impl; }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/TextureSwizzle.h b/include/ramses/client/TextureSwizzle.h similarity index 94% rename from client/ramses-client-api/include/ramses-client-api/TextureSwizzle.h rename to include/ramses/client/TextureSwizzle.h index f13717c51..88fe50bd3 100644 --- a/client/ramses-client-api/include/ramses-client-api/TextureSwizzle.h +++ b/include/ramses/client/TextureSwizzle.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURESWIZZLE_H -#define RAMSES_TEXTURESWIZZLE_H +#pragma once -#include "TextureEnums.h" +#include "ramses/framework/TextureEnums.h" namespace ramses { @@ -32,5 +31,3 @@ namespace ramses ETextureChannelColor channelAlpha = ETextureChannelColor::Alpha; }; } - -#endif diff --git a/include/ramses/client/UniformInput.h b/include/ramses/client/UniformInput.h new file mode 100644 index 000000000..c537a1906 --- /dev/null +++ b/include/ramses/client/UniformInput.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/client/EffectInput.h" +#include "ramses/client/EffectInputSemantic.h" + +namespace ramses +{ + namespace internal + { + class EffectImpl; + } + + /** + * @ingroup CoreAPI + * @brief The UniformInput is a description of an uniform effect input + */ + class RAMSES_API UniformInput : public EffectInput + { + public: + /** + * @brief Returns the effect input semantics. + * + * @return Effect input semantics + */ + [[nodiscard]] EEffectUniformSemantic getSemantics() const; + + /** + * @brief Returns the number of elements that are assigned to this effect input + * @return the element count or 0 if not initialized + */ + [[nodiscard]] size_t getElementCount() const; + + protected: + /** + * @brief Default constructor of UniformInput. + * The default constructor is forbidden from public access. Users are not + * expected to create UniformInput objects. Objects can only be created + * via copy and move constructors, or obtained from #ramses::Effect (see + * #ramses::Effect::getUniformInput and #ramses::Effect::findUniformInput). + */ + UniformInput(); + + friend class internal::EffectImpl; + }; +} diff --git a/client/logic/include/ramses-logic/AnchorPoint.h b/include/ramses/client/logic/AnchorPoint.h similarity index 88% rename from client/logic/include/ramses-logic/AnchorPoint.h rename to include/ramses/client/logic/AnchorPoint.h index 7188dff56..69d645176 100644 --- a/client/logic/include/ramses-logic/AnchorPoint.h +++ b/include/ramses/client/logic/AnchorPoint.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicNode.h" +#include "ramses/client/logic/LogicNode.h" #include namespace ramses @@ -27,7 +27,7 @@ namespace ramses /** * Anchor point is a #ramses::LogicNode which calculates viewport coordinates (and depth) of a Ramses node's origin. * The projected coordinates are accessible via its output property and can be linked to another logic node. - * Anchor point requires a #ramses::RamsesNodeBinding and a #ramses::RamsesCameraBinding at creation time, see #ramses::LogicEngine::createAnchorPoint. + * Anchor point requires a #ramses::NodeBinding and a #ramses::CameraBinding at creation time, see #ramses::LogicEngine::createAnchorPoint. * * The update logic retrieves a model matrix from the provided Ramses node (via its binding), a projection and view matrix from the provided * Ramses camera (via its binding) and projects a point (0, 0, 0) (which represents origin of the given node's local space) using those matrices: @@ -65,7 +65,7 @@ namespace ramses * and therefore it has to be updated every time #ramses::LogicEngine::update is called. For this reason it is highly recommended * to keep the number of anchor nodes to a necessary minimum. */ - class AnchorPoint : public LogicNode + class RAMSES_API AnchorPoint : public LogicNode { public: /** @@ -73,19 +73,24 @@ namespace ramses * * @return Ramses node to track */ - [[nodiscard]] RAMSES_API const ramses::Node& getRamsesNode() const; + [[nodiscard]] const ramses::Node& getRamsesNode() const; /** * Returns given ramses camera which is used to calculate coordinates. * * @return Ramses camera associated with node to track */ - [[nodiscard]] RAMSES_API const ramses::Camera& getRamsesCamera() const; + [[nodiscard]] const ramses::Camera& getRamsesCamera() const; /** - * Implementation of AnchorPoint - */ - internal::AnchorPointImpl& m_anchorPointImpl; + * Get the internal data for implementation specifics of #AnchorPoint. + */ + [[nodiscard]] internal::AnchorPointImpl& impl(); + + /** + * Get the internal data for implementation specifics of #AnchorPoint. + */ + [[nodiscard]] const internal::AnchorPointImpl& impl() const; protected: /** @@ -95,6 +100,11 @@ namespace ramses */ explicit AnchorPoint(std::unique_ptr impl) noexcept; + /** + * Implementation of AnchorPoint + */ + internal::AnchorPointImpl& m_anchorPointImpl; + friend class internal::ApiObjects; }; } diff --git a/client/logic/include/ramses-logic/AnimationNode.h b/include/ramses/client/logic/AnimationNode.h similarity index 88% rename from client/logic/include/ramses-logic/AnimationNode.h rename to include/ramses/client/logic/AnimationNode.h index ce9a515e3..aff6b5494 100644 --- a/client/logic/include/ramses-logic/AnimationNode.h +++ b/include/ramses/client/logic/AnimationNode.h @@ -8,9 +8,9 @@ #pragma once -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/EPropertyType.h" #include #include @@ -65,7 +65,7 @@ namespace ramses * when updating the animation node with progress 0, the logic will execute and update outputs to their * first keyframes. */ - class AnimationNode : public LogicNode + class RAMSES_API AnimationNode : public LogicNode { public: /** @@ -77,12 +77,17 @@ namespace ramses * * @return animation channels used in this animation. */ - [[nodiscard]] RAMSES_API const AnimationChannels& getChannels() const; + [[nodiscard]] const AnimationChannels& getChannels() const; /** - * Implementation of AnimationNode - */ - internal::AnimationNodeImpl& m_animationNodeImpl; + * Get the internal data for implementation specifics of #AnimationNode. + */ + [[nodiscard]] internal::AnimationNodeImpl& impl(); + + /** + * Get the internal data for implementation specifics of #AnimationNode. + */ + [[nodiscard]] const internal::AnimationNodeImpl& impl() const; protected: /** @@ -92,6 +97,11 @@ namespace ramses */ explicit AnimationNode(std::unique_ptr impl) noexcept; + /** + * Implementation of AnimationNode + */ + internal::AnimationNodeImpl& m_animationNodeImpl; + friend class internal::ApiObjects; }; } diff --git a/client/logic/include/ramses-logic/AnimationNodeConfig.h b/include/ramses/client/logic/AnimationNodeConfig.h similarity index 79% rename from client/logic/include/ramses-logic/AnimationNodeConfig.h rename to include/ramses/client/logic/AnimationNodeConfig.h index 32a5e5cc4..fda224095 100644 --- a/client/logic/include/ramses-logic/AnimationNodeConfig.h +++ b/include/ramses/client/logic/AnimationNodeConfig.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/AnimationTypes.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/AnimationTypes.h" #include namespace ramses::internal @@ -22,10 +22,10 @@ namespace ramses /** * Holds data and settings for #ramses::AnimationNode creation using #ramses::LogicEngine::createAnimationNode. */ - class AnimationNodeConfig + class RAMSES_API AnimationNodeConfig { public: - RAMSES_API AnimationNodeConfig() noexcept; + AnimationNodeConfig() noexcept; /** * Adds a data to animate. #ramses::AnimationNode can use multiple animation channels to animate, @@ -36,14 +36,14 @@ namespace ramses * @return \c true if the channel was added successfully, \c false otherwise. * In case of an error, check the logs. */ - RAMSES_API bool addChannel(const AnimationChannel& channelData); + bool addChannel(const AnimationChannel& channelData); /** * Returns all channels data added to this #AnimationNodeConfig so far. * * @return animation channels in this #AnimationNodeConfig. */ - [[nodiscard]] RAMSES_API const AnimationChannels& getChannels() const; + [[nodiscard]] const AnimationChannels& getChannels() const; /** * If enabled, the created #ramses::AnimationNode will expose its basic channel data (timestamps and keyframes) in form of logic node properties. @@ -67,46 +67,57 @@ namespace ramses * @return \c true if enabled successfully, \c false otherwise. * In case of an error, check the logs. */ - RAMSES_API bool setExposingOfChannelDataAsProperties(bool enabled); + bool setExposingOfChannelDataAsProperties(bool enabled); /** * Returns the currently set state of the exposing of channel data as properties. * * @return the currently set state of the exposing of channel data as properties. */ - [[nodiscard]] RAMSES_API bool getExposingOfChannelDataAsProperties() const; + [[nodiscard]] bool getExposingOfChannelDataAsProperties() const; /** * Destructor of #AnimationNodeConfig */ - RAMSES_API ~AnimationNodeConfig() noexcept; + ~AnimationNodeConfig() noexcept; /** * Copy Constructor of #AnimationNodeConfig * @param other the other #AnimationNodeConfig to copy from */ - RAMSES_API AnimationNodeConfig(const AnimationNodeConfig& other); + AnimationNodeConfig(const AnimationNodeConfig& other); /** * Move Constructor of #AnimationNodeConfig * @param other the other #AnimationNodeConfig to move from */ - RAMSES_API AnimationNodeConfig(AnimationNodeConfig&& other) noexcept; + AnimationNodeConfig(AnimationNodeConfig&& other) noexcept; /** * Assignment operator of #AnimationNodeConfig * @param other the other #AnimationNodeConfig to copy from * @return self */ - RAMSES_API AnimationNodeConfig& operator=(const AnimationNodeConfig& other); + AnimationNodeConfig& operator=(const AnimationNodeConfig& other); /** * Move assignment operator of #AnimationNodeConfig * @param other the other #AnimationNodeConfig to move from * @return self */ - RAMSES_API AnimationNodeConfig& operator=(AnimationNodeConfig&& other) noexcept; + AnimationNodeConfig& operator=(AnimationNodeConfig&& other) noexcept; + /** + * Get the internal data for implementation specifics of #AnimationNodeConfig. + */ + [[nodiscard]] internal::AnimationNodeConfigImpl& impl(); + + /** + * Get the internal data for implementation specifics of #AnimationNodeConfig. + */ + [[nodiscard]] const internal::AnimationNodeConfigImpl& impl() const; + + protected: /** * Implementation detail of #AnimationNodeConfig */ diff --git a/client/logic/include/ramses-logic/AnimationTypes.h b/include/ramses/client/logic/AnimationTypes.h similarity index 98% rename from client/logic/include/ramses-logic/AnimationTypes.h rename to include/ramses/client/logic/AnimationTypes.h index 4dc3d4d31..7325d0ed2 100644 --- a/client/logic/include/ramses-logic/AnimationTypes.h +++ b/include/ramses/client/logic/AnimationTypes.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include diff --git a/client/logic/include/ramses-logic/RamsesAppearanceBinding.h b/include/ramses/client/logic/AppearanceBinding.h similarity index 54% rename from client/logic/include/ramses-logic/RamsesAppearanceBinding.h rename to include/ramses/client/logic/AppearanceBinding.h index 9eab1a829..e1cacaecd 100644 --- a/client/logic/include/ramses-logic/RamsesAppearanceBinding.h +++ b/include/ramses/client/logic/AppearanceBinding.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/RamsesBinding.h" #include @@ -20,22 +20,22 @@ namespace ramses namespace ramses::internal { - class RamsesAppearanceBindingImpl; + class AppearanceBindingImpl; } namespace ramses { /** - * The RamsesAppearanceBinding is a type of #ramses::RamsesBinding which allows the #ramses::LogicEngine to control instances - * of ramses::Appearance. RamsesAppearanceBinding's can be created with #ramses::LogicEngine::createRamsesAppearanceBinding. + * The AppearanceBinding is a type of #ramses::RamsesBinding which allows the #ramses::LogicEngine to control instances + * of ramses::Appearance. AppearanceBinding's can be created with #ramses::LogicEngine::createAppearanceBinding. * - * The #RamsesAppearanceBinding has a static link to a ramses::Appearance. After creation, #ramses::LogicNode::getInputs will + * The #AppearanceBinding has a static link to a ramses::Appearance. After creation, #ramses::LogicNode::getInputs will * return a struct property with children equivalent to the uniform inputs of the provided ramses Appearance. * - * Since the RamsesAppearanceBinding derives from #ramses::RamsesBinding, it also provides the #ramses::LogicNode::getInputs + * Since the AppearanceBinding derives from #ramses::RamsesBinding, it also provides the #ramses::LogicNode::getInputs * and #ramses::LogicNode::getOutputs method. For this particular implementation, the methods behave as follows: * - #ramses::LogicNode::getInputs: returns the inputs corresponding to the available shader uniforms of the bound ramses::Appearance - * - #ramses::LogicNode::getOutputs: returns always nullptr, because a #RamsesAppearanceBinding does not have outputs, + * - #ramses::LogicNode::getOutputs: returns always nullptr, because a #AppearanceBinding does not have outputs, * it implicitly controls the ramses Appearance * - The values of this binding's inputs are initialized to default values (0, 0.0f, etc) and *not* loaded from the values in Ramses * @@ -47,27 +47,37 @@ namespace ramses * Uniform types which are not supported are not available when queried over #ramses::LogicNode::getInputs. * */ - class RamsesAppearanceBinding : public RamsesBinding + class RAMSES_API AppearanceBinding : public RamsesBinding { public: /** * Returns the bound Ramses Appearance. * @return the bound ramses appearance */ - [[nodiscard]] RAMSES_API ramses::Appearance& getRamsesAppearance() const; + [[nodiscard]] ramses::Appearance& getRamsesAppearance() const; /** - * Implementation detail of RamsesAppearanceBinding + * Get the internal data for implementation specifics of AppearanceBinding. */ - internal::RamsesAppearanceBindingImpl& m_appearanceBinding; + [[nodiscard]] internal::AppearanceBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of AppearanceBinding. + */ + [[nodiscard]] const internal::AppearanceBindingImpl& impl() const; protected: /** - * Constructor of RamsesAppearanceBinding. User is not supposed to call this - RamsesAppearanceBinding are created by other factory classes + * Constructor of AppearanceBinding. User is not supposed to call this - AppearanceBinding are created by other factory classes * - * @param impl implementation details of the RamsesAppearanceBinding + * @param impl implementation details of the AppearanceBinding + */ + explicit AppearanceBinding(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of AppearanceBinding */ - explicit RamsesAppearanceBinding(std::unique_ptr impl) noexcept; + internal::AppearanceBindingImpl& m_appearanceBinding; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/RamsesCameraBinding.h b/include/ramses/client/logic/CameraBinding.h similarity index 68% rename from client/logic/include/ramses-logic/RamsesCameraBinding.h rename to include/ramses/client/logic/CameraBinding.h index 35f3c93bd..58e6879a7 100644 --- a/client/logic/include/ramses-logic/RamsesCameraBinding.h +++ b/include/ramses/client/logic/CameraBinding.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/RamsesBinding.h" #include @@ -20,17 +20,17 @@ namespace ramses namespace ramses::internal { - class RamsesCameraBindingImpl; + class CameraBindingImpl; } namespace ramses { /** - * The RamsesCameraBinding is a type of #ramses::RamsesBinding which allows the #ramses::LogicEngine to control instances of ramses::Camera. - * RamsesCameraBinding's can be created with #ramses::LogicEngine::createRamsesCameraBinding or #ramses::LogicEngine::createRamsesCameraBindingWithFrustumPlanes, + * The CameraBinding is a type of #ramses::RamsesBinding which allows the #ramses::LogicEngine to control instances of ramses::Camera. + * CameraBinding's can be created with #ramses::LogicEngine::createCameraBinding or #ramses::LogicEngine::createCameraBindingWithFrustumPlanes, * which affects the set of input properties that will be used to control camera frustum as described below. * - * The #RamsesCameraBinding has a static link to a ramses::Camera. After creation, #ramses::LogicNode::getInputs will + * The #CameraBinding has a static link to a ramses::Camera. After creation, #ramses::LogicNode::getInputs will * return a struct property with children equivalent to the camera settings of the provided ramses::Camera. * * There are two types of ramses::Camera: @@ -38,17 +38,17 @@ namespace ramses * - ramses::OrthographicCamera * * Both camera types are defined through their viewport and their frustum properties. These are represented as two separate property structs - * in the RamsesCameraBinding. Be aware if you set one or more values to one of the structs on the binding and update the LogicEngine it will lead + * in the CameraBinding. Be aware if you set one or more values to one of the structs on the binding and update the LogicEngine it will lead * to all properties of this struct being set on the actual ramses::Camera. * For example if you only set the 'offsetX' of the Camera viewport it will set * all other viewport properties as well (offsetY, width and height) to whatever their state is at that moment. - * The values of the #RamsesCameraBinding inputs are initialized with the values of the provided ramses::Camera during creation. + * The values of the #CameraBinding inputs are initialized with the values of the provided ramses::Camera during creation. * The frustum values of the ramses::Camera are not affected when setting viewport values, and vice-versa. * Check the ramses::Camera API to see which values belong together. * To avoid unexpected behavior, we highly recommend setting all viewport values together, and also setting all frustum planes together * (either by link or by setting them directly). This way unwanted behavior can be avoided. * - * Since the RamsesCameraBinding derives from #ramses::RamsesBinding, it also provides the #ramses::LogicNode::getInputs + * Since the CameraBinding derives from #ramses::RamsesBinding, it also provides the #ramses::LogicNode::getInputs * and #ramses::LogicNode::getOutputs method. For this class, the methods behave as follows: * - #ramses::LogicNode::getInputs: returns inputs struct with two child properties: viewport and frustum. * - 'viewport' (type struct) with these children: @@ -70,34 +70,44 @@ namespace ramses * - 'fieldOfView' (type Float) - frustum field of view in degrees * - 'aspectRatio' (type Float) - aspect ratio of frustum width / frustum height * Full set of frustum planes properties will be present if camera is ramses::Orthographic (regardless of which create method was used) - * or camera is ramses::PerspectiveCamera and #ramses::LogicEngine::createRamsesCameraBindingWithFrustumPlanes was used to create it. - * Simplified set of frustum properties will be present if camera is ramses::PerspectiveCamera and #ramses::LogicEngine::createRamsesCameraBinding was used to create it. + * or camera is ramses::PerspectiveCamera and #ramses::LogicEngine::createCameraBindingWithFrustumPlanes was used to create it. + * Simplified set of frustum properties will be present if camera is ramses::PerspectiveCamera and #ramses::LogicEngine::createCameraBinding was used to create it. * Refer to ramses::Camera, ramses::PerspectiveCamera and ramses::OrthographicCamera for meaning and constraints of all these inputs. * - * - #ramses::LogicNode::getOutputs: returns always nullptr, because a #RamsesCameraBinding does not have outputs, + * - #ramses::LogicNode::getOutputs: returns always nullptr, because a #CameraBinding does not have outputs, * it implicitly controls the ramses Camera */ - class RamsesCameraBinding : public RamsesBinding + class RAMSES_API CameraBinding : public RamsesBinding { public: /** * Returns the bound ramses camera. * @return the bound ramses camera */ - [[nodiscard]] RAMSES_API ramses::Camera& getRamsesCamera() const; + [[nodiscard]] ramses::Camera& getRamsesCamera() const; /** - * Implementation detail of RamsesCameraBinding + * Get the internal data for implementation specifics of CameraBinding. */ - internal::RamsesCameraBindingImpl& m_cameraBinding; + [[nodiscard]] internal::CameraBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of CameraBinding. + */ + [[nodiscard]] const internal::CameraBindingImpl& impl() const; protected: /** - * Constructor of RamsesCameraBinding. User is not supposed to call this - RamsesCameraBindings are created by other factory classes + * Constructor of CameraBinding. User is not supposed to call this - CameraBindings are created by other factory classes * - * @param impl implementation details of the RamsesCameraBinding + * @param impl implementation details of the CameraBinding + */ + explicit CameraBinding(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of CameraBinding */ - explicit RamsesCameraBinding(std::unique_ptr impl) noexcept; + internal::CameraBindingImpl& m_cameraBinding; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/Collection.h b/include/ramses/client/logic/Collection.h similarity index 98% rename from client/logic/include/ramses-logic/Collection.h rename to include/ramses/client/logic/Collection.h index 6708ff54b..0b9b75d22 100644 --- a/client/logic/include/ramses-logic/Collection.h +++ b/include/ramses/client/logic/Collection.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/Iterator.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/Iterator.h" #include #include diff --git a/client/logic/include/ramses-logic/DataArray.h b/include/ramses/client/logic/DataArray.h similarity index 80% rename from client/logic/include/ramses-logic/DataArray.h rename to include/ramses/client/logic/DataArray.h index 471435d2d..47987259b 100644 --- a/client/logic/include/ramses-logic/DataArray.h +++ b/include/ramses/client/logic/DataArray.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-logic/LogicObject.h" -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/LogicObject.h" +#include "ramses/client/logic/EPropertyType.h" #include #include @@ -25,7 +25,7 @@ namespace ramses /** * Storage for data - e.g. animation data like keyframes or timestamps. */ - class DataArray : public LogicObject + class RAMSES_API DataArray : public LogicObject { public: /** @@ -33,7 +33,7 @@ namespace ramses * * @return the data type */ - [[nodiscard]] RAMSES_API EPropertyType getDataType() const; + [[nodiscard]] EPropertyType getDataType() const; /** * Returns the data stored in this #DataArray. @@ -54,12 +54,17 @@ namespace ramses * * @return the number of elements */ - [[nodiscard]] RAMSES_API size_t getNumElements() const; + [[nodiscard]] size_t getNumElements() const; /** - * Implementation detail of DataArray - */ - internal::DataArrayImpl& m_impl; + * Get the internal data for implementation specifics of #DataArray. + */ + [[nodiscard]] internal::DataArrayImpl& impl(); + + /** + * Get the internal data for implementation specifics of #DataArray. + */ + [[nodiscard]] const internal::DataArrayImpl& impl() const; protected: /** @@ -69,6 +74,11 @@ namespace ramses */ explicit DataArray(std::unique_ptr impl) noexcept; + /** + * Implementation detail of DataArray + */ + internal::DataArrayImpl& m_impl; + private: /** * Internal implementation of #getData. @@ -76,7 +86,7 @@ namespace ramses * @return vector of data or nullptr if wrong template type provided */ template - [[nodiscard]] RAMSES_API const std::vector* getDataInternal() const; + [[nodiscard]] const std::vector* getDataInternal() const; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/ELuaSavingMode.h b/include/ramses/client/logic/ELuaSavingMode.h similarity index 100% rename from client/logic/include/ramses-logic/ELuaSavingMode.h rename to include/ramses/client/logic/ELuaSavingMode.h diff --git a/client/logic/include/ramses-logic/EPropertyType.h b/include/ramses/client/logic/EPropertyType.h similarity index 99% rename from client/logic/include/ramses-logic/EPropertyType.h rename to include/ramses/client/logic/EPropertyType.h index bbb00808f..8034e5158 100644 --- a/client/logic/include/ramses-logic/EPropertyType.h +++ b/include/ramses/client/logic/EPropertyType.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/framework/APIExport.h" +#include "ramses/framework/DataTypes.h" #include #include #include diff --git a/client/logic/include/ramses-logic/EStandardModule.h b/include/ramses/client/logic/EStandardModule.h similarity index 100% rename from client/logic/include/ramses-logic/EStandardModule.h rename to include/ramses/client/logic/EStandardModule.h diff --git a/client/logic/include/ramses-logic/Iterator.h b/include/ramses/client/logic/Iterator.h similarity index 99% rename from client/logic/include/ramses-logic/Iterator.h rename to include/ramses/client/logic/Iterator.h index 976ec21e9..9a7c59c98 100644 --- a/client/logic/include/ramses-logic/Iterator.h +++ b/include/ramses/client/logic/Iterator.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include diff --git a/client/logic/include/ramses-logic/LogicEngine.h b/include/ramses/client/logic/LogicEngine.h similarity index 56% rename from client/logic/include/ramses-logic/LogicEngine.h rename to include/ramses/client/logic/LogicEngine.h index 5bbb7988c..459f83703 100644 --- a/client/logic/include/ramses-logic/LogicEngine.h +++ b/include/ramses/client/logic/LogicEngine.h @@ -8,26 +8,24 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-client-api/ERotationType.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/Collection.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/ErrorData.h" -#include "ramses-logic/LogicEngineReport.h" -#include "ramses-logic/LuaConfig.h" -#include "ramses-logic/SaveFileConfig.h" -#include "ramses-logic/WarningData.h" -#include "ramses-logic/PropertyLink.h" -#include "ramses-framework-api/DataTypes.h" -#include "ramses-framework-api/EFeatureLevel.h" +#include "ramses/framework/APIExport.h" +#include "ramses/framework/ERotationType.h" +#include "ramses/client/SceneObject.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/Collection.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/LogicEngineReport.h" +#include "ramses/client/logic/LuaConfig.h" +#include "ramses/client/logic/PropertyLink.h" +#include "ramses/client/logic/ELuaSavingMode.h" +#include "ramses/framework/DataTypes.h" +#include "ramses/framework/EFeatureLevel.h" #include #include namespace ramses { - class Scene; class Node; class Appearance; class Camera; @@ -35,6 +33,7 @@ namespace ramses class RenderGroup; class UniformInput; class MeshNode; + class LogicObject; } namespace ramses::internal @@ -54,58 +53,41 @@ namespace ramses class LuaInterface; class LuaModule; class Property; - class RamsesNodeBinding; - class RamsesAppearanceBinding; - class RamsesCameraBinding; - class RamsesRenderPassBinding; - class RamsesRenderGroupBinding; - class RamsesRenderGroupBindingElements; - class RamsesMeshNodeBinding; + class NodeBinding; + class AppearanceBinding; + class CameraBinding; + class RenderPassBinding; + class RenderGroupBinding; + class RenderGroupBindingElements; + class MeshNodeBinding; class SkinBinding; class DataArray; class AnimationNode; class AnimationNodeConfig; class TimerNode; class AnchorPoint; - enum class ELogLevel; + + /** + * Logging mode for the periodic statistics. + */ + enum class EStatisticsLogMode + { + Compact, ///< general statistics about update, total time, number of nodes executed, etc. + Detailed ///< additionally logs slowest nodes with individual times (this might add slight performance overhead of measuring) + }; /** * @ingroup LogicAPI * Central object which creates and manages the lifecycle and execution * of scripts, bindings, and all other objects supported by the Ramses Logic library. - * All objects created by this class' methods must be destroyed with #destroy! * * - Use the create[Type] methods to create various objects, use #destroy() to delete them. * - Use #link and #unlink to connect data properties between these objects * - use #update() to trigger the execution of all objects */ - class LogicEngine + class RAMSES_API LogicEngine : public SceneObject { public: - /** - * Constructor of #LogicEngine with a feature level specified. - * See #ramses::EFeatureLevel for more details what features are available. - * - * @param featureLevel Feature level to activate in this #LogicEngine instance. A feature level always includes - * a previous feature level if any, e.g. a feature level released after base level will contain - * all features from base level, however base level will include only base level features - * and none from a newer feature level. Use #ramses::EFeatureLevel_Latest to get all available features. - */ - RAMSES_API explicit LogicEngine(ramses::EFeatureLevel featureLevel) noexcept; - - /** - * Destructor of #LogicEngine - */ - RAMSES_API ~LogicEngine() noexcept; - - /** - * Returns the feature level this #LogicEngine instance was configured to use when created. - * See #LogicEngine(ramses::EFeatureLevel) and #ramses::EFeatureLevel for more details. - * - * @return feature level this #LogicEngine instance was configured to use - */ - [[nodiscard]] RAMSES_API ramses::EFeatureLevel getFeatureLevel() const; - /** * Returns an iterable #ramses::Collection of all instances of \c T created by this #LogicEngine. * \c T must be a concrete logic object type (e.g. #ramses::LuaScript) or #ramses::LogicObject which will retrieve @@ -117,33 +99,38 @@ namespace ramses [[nodiscard]] Collection getCollection() const; /** - * Returns a pointer to the first occurrence of an object with a given \p name of the type \c T. + * Returns a pointer to the first occurrence of an object with a given \p name and type \c T (#ramses::LogicObject by default). * \c T must be a concrete logic object type (e.g. #ramses::LuaScript) or #ramses::LogicObject which will search * any object with given name regardless of its type (see #ramses::LogicObject::as to convert to concrete type). + * Note that giving a concrete object template type might result in faster search because it is limited to only objects of that type. * * @param name the name of the logic object to search for * @return a pointer to the logic object, or nullptr if none was found */ - template - [[nodiscard]] const T* findByName(std::string_view name) const; + template + [[nodiscard]] const T* findObject(std::string_view name) const; - /** @copydoc findByName(std::string_view) const */ - template - [[nodiscard]] T* findByName(std::string_view name); + /** @copydoc findObject(std::string_view) const */ + template + [[nodiscard]] T* findObject(std::string_view name); /** - * Returns a pointer to the first occurrence of an object with a given \p id regardless of its type. - * To convert the object to a concrete type (e.g. LuaScript) use #ramses::LogicObject::as() e.g.: - * auto myLuaScript = logicEngine.findLogicObjectById(1u)->as()); - * Be aware that this function behaves as \c dynamic_cast and will return nullptr (without error) if - * given type doesn't match the objects type. This can later lead to crash if ignored. + * Returns a pointer to an object with a given #ramses::sceneObjectId_t and type \c T (#ramses::LogicObject by default). + * \c T must be a concrete logic object type (e.g. #ramses::LuaScript) or #ramses::LogicObject which will search + * any object with given ID regardless of its type (see #ramses::LogicObject::as to convert to concrete type). + * Note that giving a concrete object template type might result in faster search because it is limited to only objects of that type. * - * @param id the id of the logic object to search for + * @param id The id of the object to find. * @return a pointer to the logic object, or nullptr if none was found */ - [[nodiscard]] RAMSES_API const LogicObject* findLogicObjectById(uint64_t id) const; - /// @copydoc findLogicObjectById(uint64_t id) const - [[nodiscard]] RAMSES_API LogicObject* findLogicObjectById(uint64_t id); + template + [[nodiscard]] T* findObject(sceneObjectId_t id); + + /** + * @copydoc findObject(sceneObjectId_t id) + **/ + template + [[nodiscard]] const T* findObject(sceneObjectId_t id) const; /** * Creates a new Lua script from a source string. Refer to the #ramses::LuaScript class @@ -152,16 +139,14 @@ namespace ramses * under their configured alias name for use by the script. The provided module dependencies * must exactly match the declared dependencies in source code (see #extractLuaDependencies). * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param source the Lua source code * @param config configuration options, e.g. for module dependencies * @param scriptName name to assign to the script once it's created * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The script can be destroyed by calling the #destroy method */ - RAMSES_API LuaScript* createLuaScript( + LuaScript* createLuaScript( std::string_view source, const LuaConfig& config = {}, std::string_view scriptName = ""); @@ -177,37 +162,17 @@ namespace ramses * under their configured alias name for use in the interface source. The provided module dependencies * must exactly match the declared dependencies in source code (see #extractLuaDependencies). * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param source the Lua source code * @param interfaceName name to assign to the interface once it's created. This name must be unique! * @param config configuration options, e.g. for module dependencies * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The interface can be destroyed by calling the #destroy method */ - RAMSES_API LuaInterface* createLuaInterface( + LuaInterface* createLuaInterface( std::string_view source, std::string_view interfaceName, - const LuaConfig& config); - - /** - * Deprecated! Use #createLuaInterface(std::string_view, std::string_view, const LuaConfig&) instead. - * - * Same as #createLuaInterface(std::string_view, std::string_view, const LuaConfig&) but without - * support for using #ramses::LuaModule in the interface, also will ignore any \c modules - * declaration within the provided interface source. - * - * @deprecated - * @param source the Lua source code - * @param interfaceName name to assign to the interface once it's created. This name must be unique! - * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. - * The interface can be destroyed by calling the #destroy method - */ - RAMSES_API LuaInterface* createLuaInterface( - std::string_view source, - std::string_view interfaceName); + const LuaConfig& config = {}); /** * Creates a new #ramses::LuaModule from Lua source code. @@ -217,16 +182,14 @@ namespace ramses * under their configured alias name for use by the module. The provided module dependencies * must exactly match the declared dependencies in source code (see #extractLuaDependencies). * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param source module source code * @param config configuration options, e.g. for module dependencies * @param moduleName name to assign to the module once it's created * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The script can be destroyed by calling the #destroy method */ - RAMSES_API LuaModule* createLuaModule( + LuaModule* createLuaModule( std::string_view source, const LuaConfig& config = {}, std::string_view moduleName = ""); @@ -257,117 +220,107 @@ namespace ramses * highly recommended to put the modules declaration always at the beginning of every script * before any other code so it will get executed even if there is runtime error later in the code. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param source source code of module or script to parse for dependencies * @param callbackFunc function callback will be called for each dependency found * @return \c true if extraction succeeded (also if no dependencies found) or \c false if - * something went wrong. In that case, use #getErrors() to obtain errors. + * something went wrong. In that case, use #ramses::RamsesFramework::getLastError. */ - RAMSES_API bool extractLuaDependencies( + bool extractLuaDependencies( std::string_view source, const std::function& callbackFunc); /** - * Creates a new #ramses::RamsesNodeBinding which can be used to set the properties of a Ramses Node object. + * Creates a new #ramses::NodeBinding which can be used to set the properties of a Ramses Node object. * The initial values of the binding's properties are loaded from the \p ramsesNode. Rotation values are * taken over from the \p ramsesNode only if the conventions are compatible (see \ref ramses::ERotationType). - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * The creation will fail if provided #ramses::Node is not from same #ramses::Scene as this #LogicEngine instance. * * @param ramsesNode the ramses::Node object to control with the binding. * @param rotationType the type of rotation to use (will affect the 'rotation' property semantics and type). - * @param name a name for the new #ramses::RamsesNodeBinding. + * @param name a name for the new #ramses::NodeBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesNodeBinding* createRamsesNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType = ramses::ERotationType::Euler_XYZ, std::string_view name = ""); + NodeBinding* createNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType = ramses::ERotationType::Euler_XYZ, std::string_view name = ""); /** - * Creates a new #ramses::RamsesAppearanceBinding which can be used to set the properties of a Ramses Appearance object. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * Creates a new #ramses::AppearanceBinding which can be used to set the properties of a Ramses Appearance object. + * The creation will fail if provided #ramses::Appearance is not from same #ramses::Scene as this #LogicEngine instance. * * @param ramsesAppearance the ramses::Appearance object to control with the binding. - * @param name a name for the the new #ramses::RamsesAppearanceBinding. + * @param name a name for the the new #ramses::AppearanceBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesAppearanceBinding* createRamsesAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name = ""); + AppearanceBinding* createAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name = ""); /** - * Creates a new #ramses::RamsesCameraBinding which can be used to set the properties of a Ramses Camera object. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * Creates a new #ramses::CameraBinding which can be used to set the properties of a Ramses Camera object. + * The creation will fail if provided #ramses::Camera is not from same #ramses::Scene as this #LogicEngine instance. * * @param ramsesCamera the ramses::Camera object to control with the binding. - * @param name a name for the the new #ramses::RamsesCameraBinding. + * @param name a name for the the new #ramses::CameraBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesCameraBinding* createRamsesCameraBinding(ramses::Camera& ramsesCamera, std::string_view name =""); + CameraBinding* createCameraBinding(ramses::Camera& ramsesCamera, std::string_view name =""); /** - * Same as #createRamsesCameraBinding but the created #ramses::RamsesCameraBinding will have an input property - * for each frustum plane also for perspective camera. See #ramses::RamsesCameraBinding for details. - * Note that ramses::OrthographicCamera binding will always have frustum planes as properties whether #createRamsesCameraBinding - * or #createRamsesCameraBindingWithFrustumPlanes is used to create it. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * Same as #createCameraBinding but the created #ramses::CameraBinding will have an input property + * for each frustum plane also for perspective camera. See #ramses::CameraBinding for details. + * Note that ramses::OrthographicCamera binding will always have frustum planes as properties whether #createCameraBinding + * or #createCameraBindingWithFrustumPlanes is used to create it. * * @param ramsesCamera the ramses::Camera object to control with the binding. - * @param name a name for the the new #ramses::RamsesCameraBinding. + * @param name a name for the the new #ramses::CameraBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesCameraBinding* createRamsesCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name =""); + CameraBinding* createCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name =""); /** - * Creates a new #ramses::RamsesRenderPassBinding which can be used to set the properties of a ramses::RenderPass object. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * Creates a new #ramses::RenderPassBinding which can be used to set the properties of a ramses::RenderPass object. + * The creation will fail if provided #ramses::RenderPass is not from same #ramses::Scene as this #LogicEngine instance. * * @param ramsesRenderPass the ramses::RenderPass object to control with the binding. - * @param name a name for the the new #ramses::RamsesRenderPassBinding. + * @param name a name for the the new #ramses::RenderPassBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesRenderPassBinding* createRamsesRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name =""); + RenderPassBinding* createRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name =""); /** - * Creates a new #ramses::RamsesRenderGroupBinding which can be used to control some properties of a ramses::RenderGroup object. - * #ramses::RamsesRenderGroupBinding can be used to control render order of elements it contains on Ramses side - ramses::MeshNode or ramses::RenderGroup, - * the elements to control must be provided explicitly at creation time, see #ramses::RamsesRenderGroupBindingElements and #ramses::RamsesRenderGroupBinding + * Creates a new #ramses::RenderGroupBinding which can be used to control some properties of a ramses::RenderGroup object. + * #ramses::RenderGroupBinding can be used to control render order of elements it contains on Ramses side - ramses::MeshNode or ramses::RenderGroup, + * the elements to control must be provided explicitly at creation time, see #ramses::RenderGroupBindingElements and #ramses::RenderGroupBinding * to learn how these elements form the binding's input properties. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * The creation will fail if provided #ramses::RenderGroup or the elements are not from same #ramses::Scene as this #LogicEngine instance. * * @param ramsesRenderGroup the ramses::RenderGroup object to control with the binding. - * @param elements collection of elements (MeshNode or RenderGroup) to control with this #ramses::RamsesRenderGroupBinding. - * @param name a name for the the new #ramses::RamsesRenderGroupBinding. + * @param elements collection of elements (MeshNode or RenderGroup) to control with this #ramses::RenderGroupBinding. + * @param name a name for the the new #ramses::RenderGroupBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesRenderGroupBinding* createRamsesRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElements& elements, std::string_view name =""); + RenderGroupBinding* createRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElements& elements, std::string_view name =""); /** - * Creates a new #ramses::RamsesMeshNodeBinding which can be used to control some properties of a ramses::MeshNode object. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() + * Creates a new #ramses::MeshNodeBinding which can be used to control some properties of a ramses::MeshNode object. + * The creation will fail if provided #ramses::MeshNode is not from same #ramses::Scene as this #LogicEngine instance. * * @param ramsesMeshNode the ramses::MeshNode object to control with the binding. - * @param name a name for the the new #ramses::RamsesMeshNodeBinding. + * @param name a name for the the new #ramses::MeshNodeBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API RamsesMeshNodeBinding* createRamsesMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name =""); + MeshNodeBinding* createMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name =""); /** * Creates a new #ramses::SkinBinding which can be used for vertex skinning (bone animations). @@ -380,7 +333,7 @@ namespace ramses * - the shader uniform that \c jointMatInput points to must be of type array of ramses::EDataType::Matrix44F * with number of elements matching number of joints * - \c jointMatInput must not be bound to any data object in its Ramses appearance - * Attention! This method clears all previous errors! See also docs of #getErrors() + * The creation will also fail if any of the provided Ramses objects or bindings are not from same #ramses::Scene as this #LogicEngine instance. * * @param joints bindings to Ramses nodes which will act as skeleton nodes. * @param inverseBindMatrices inverse transformation matrices (one for each joint node), values are expected ordered in column-major fashion. @@ -388,13 +341,13 @@ namespace ramses * @param jointMatInput Ramses appearance uniform input for the resulting joint matrices to be set. * @param name a name for the the new #ramses::SkinBinding. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The binding can be destroyed by calling the #destroy method */ - RAMSES_API SkinBinding* createSkinBinding( - const std::vector& joints, + SkinBinding* createSkinBinding( + const std::vector& joints, const std::vector& inverseBindMatrices, - RamsesAppearanceBinding& appearanceBinding, + AppearanceBinding& appearanceBinding, const ramses::UniformInput& jointMatInput, std::string_view name = {}); @@ -406,12 +359,10 @@ namespace ramses * When using std::vector as element data type (corresponds to #ramses::EPropertyType::Array), * the sizes of all the elements (std::vector instances) must be equal, otherwise creation will fail. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param data source data to move into #ramses::DataArray, must not be empty. * @param name a name for the the new #ramses::DataArray. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. */ template DataArray* createDataArray(const std::vector& data, std::string_view name =""); @@ -422,41 +373,35 @@ namespace ramses * There must be at least one channel provided in the #ramses::AnimationNodeConfig, * please see #ramses::AnimationChannel requirements for all the data. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param config list of animation channels to be animated with this animation node and other configuration flags. * @param name a name for the the new #ramses::AnimationNode. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. */ - RAMSES_API AnimationNode* createAnimationNode(const AnimationNodeConfig& config, std::string_view name = ""); + AnimationNode* createAnimationNode(const AnimationNodeConfig& config, std::string_view name = ""); /** * Creates a new #ramses::TimerNode for generate and/or propagate timing information. * Refer to #ramses::TimerNode for more information about its use. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param name a name for the the new #ramses::TimerNode. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. */ - RAMSES_API TimerNode* createTimerNode(std::string_view name = ""); + TimerNode* createTimerNode(std::string_view name = ""); /** * Creates a new #ramses::AnchorPoint that can be used to calculate projected coordinates of given ramses::Node when viewed using given ramses::Camera. * See #ramses::AnchorPoint for more details and usage of this special purpose logic node. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param nodeBinding binding referencing ramses::Node to use for model transformation when calculating projected coordinates. * @param cameraBinding binding referencing ramses::Camera to use for view and projection transformation when calculating projected coordinates. * @param name a name for the the new #ramses::AnchorPoint. * @return a pointer to the created object or nullptr if - * something went wrong during creation. In that case, use #getErrors() to obtain errors. + * something went wrong during creation. In that case, use #ramses::RamsesFramework::getLastError. * The #ramses::AnchorPoint can be destroyed by calling the #destroy method */ - RAMSES_API AnchorPoint* createAnchorPoint(RamsesNodeBinding& nodeBinding, RamsesCameraBinding& cameraBinding, std::string_view name =""); + AnchorPoint* createAnchorPoint(NodeBinding& nodeBinding, CameraBinding& cameraBinding, std::string_view name =""); /** * Updates all #ramses::LogicNode's which were created by this #LogicEngine instance. @@ -468,12 +413,10 @@ namespace ramses * has changed since the last call to #update. If the links between logic nodes create a loop, * this method will fail with an error and will not execute any of the logic nodes. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @return true if the update was successful, false otherwise - * In case of an error, use #getErrors() to obtain errors. + * In case of an error, use #ramses::RamsesFramework::getLastError. */ - RAMSES_API bool update(); + bool update(); /** * Enables collecting of statistics during call to #update which can be obtained using #getLastUpdateReport. @@ -481,16 +424,18 @@ namespace ramses * which can be useful for profiling and optimizing the network of logic nodes. * Note that when enabled there is a slight performance overhead to collect the data, it is recommended * to use this only during a development phase. + * Note that using #setStatisticsLoggingRate with #EStatisticsLogMode::Detailed will also implicitly enable + * generating of update report for internal use during periodic statistics logging. * * @param enable true or false to enable or disable update reports. */ - RAMSES_API void enableUpdateReport(bool enable); + void enableUpdateReport(bool enable); /** * Returns collection of statistics from last call to #update if reporting is enabled (#enableUpdateReport). * The report contains lists of logic nodes that were executed and not executed and other useful data collected * during last #update. See #ramses::LogicEngineReport for details. - * The report data is generated only if previously enabled using #enableUpdateReport and is empty otherwise. + * The report data is generated if previously enabled using #enableUpdateReport and is empty otherwise. * The data is only relevant for the last #update and is overwritten during next #update. * Note that if #update fails the report contents are undefined. * @@ -499,40 +444,30 @@ namespace ramses * * @return collected statistics from last #update. */ - [[nodiscard]] RAMSES_API LogicEngineReport getLastUpdateReport() const; + [[nodiscard]] LogicEngineReport getLastUpdateReport() const; /** * Set the logging rate, i.e. how often statistics will be logged. Logging rate of \c N means * every \c Nth call to #update statistics will be logged. - * Whether the the logs are actually logged is also influenced by the statistics log level that can be set with #setStatisticsLogLevel. * The logging rate also determines how many collected sets will be used to calculate min/max and average. - * These statistics include: + * These compact statistics include: * - \p Time since last log in seconds * - \p Update execution time in microseconds (Avg, Min, Max) * - \p Time between #update calls in microseconds (Avg, Min, Max) * - \p Count of nodes executed in percentage of total count (Avg, Min, Max) * - \p Links activated (Avg, Min, Max) + * The detailed statistics additionally logs slowest nodes and their individual maximum update times during measured period. * When loggingRate is set to 0 the logging of statistics is disabled. * Note that there is a slight performance overhead for collecting the statistics data, * however on most platforms this should be marginal. - * To get more detailed information about update execution timings see #getLastUpdateReport. - * - * @param loggingRate rate of \c N means statistics will be logged every \c Nth call to #update. By default loggingRate is 60. - */ - RAMSES_API void setStatisticsLoggingRate(size_t loggingRate); - - /** - * Update statistics default logLevel is #ELogLevel::Debug. For the statistics to be logged the logLevel - * has to be <= then the result returned from #ramses::Logger::GetLogVerbosityLimit. - * For example if #ramses::Logger::GetLogVerbosityLimit returns #ELogLevel::Info you have to - * set statistics logLevel to #ELogLevel::Info or a smaller level (e.g. #ELogLevel::Warn) to display statistics. - * Note that setting the statistics log level only influences the periodic statistic logs. All other logs are not influenced - * by this method. - * To control the rate after how many updates a log is produced refer to #setStatisticsLoggingRate. + * Note that using #EStatisticsLogMode::Detailed will also implicitly enable + * generating of update report (see #enableUpdateReport) for internal use during periodic statistics logging, + * and vice versa if #EStatisticsLogMode::Compact is set it will disable update report generating (even if previously enabled by user). * - * @param logLevel the logLevel of statistics messages + * @param loggingRate rate of \c N means statistics will be logged every \c Nth call to #update. By default, loggingRate is 60. + * @param mode logging mode of the statistics (See #EStatisticsLogMode) By default, mode is #EStatisticsLogMode::Compact. */ - RAMSES_API void setStatisticsLogLevel(ELogLevel logLevel); + void setStatisticsLoggingRate(size_t loggingRate, EStatisticsLogMode mode = EStatisticsLogMode::Compact); /** * Links a property of a #ramses::LogicNode to another #ramses::Property of another #ramses::LogicNode. @@ -557,14 +492,12 @@ namespace ramses * After calling #link, the value of the \p targetProperty will not change until the next call to #update. Creating * and destroying links generally has no effect until #update is called. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param sourceProperty the output property which will provide data for \p targetProperty * @param targetProperty the target property which will receive its value from \p sourceProperty * @return true if linking was successful, false otherwise. To get more detailed - * error information use #getErrors() + * error information use #ramses::RamsesFramework::getLastError */ - RAMSES_API bool link(const Property& sourceProperty, const Property& targetProperty); + bool link(Property& sourceProperty, Property& targetProperty); /** * A weak link is for the most part equivalent to #link - it has the same requirements and data propagation behavior, @@ -588,14 +521,12 @@ namespace ramses * to the last node, this will result in the need to execute those nodes in all upcoming #update calls. * Try to avoid such case and if it is not possible at least try to limit this update loop to a minimum of nodes. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param sourceProperty the output property which will provide data for \p targetProperty * @param targetProperty the target property which will receive its value from \p sourceProperty * @return true if linking was successful, false otherwise. To get more detailed - * error information use #getErrors() + * error information use #ramses::RamsesFramework::getLastError */ - RAMSES_API bool linkWeak(const Property& sourceProperty, const Property& targetProperty); + bool linkWeak(Property& sourceProperty, Property& targetProperty); /** * Unlinks two properties which were linked with #link. After a link is destroyed, @@ -603,21 +534,19 @@ namespace ramses * the input value of the \p targetProperty. The value of the \p targetProperty will remain as it was after the last call to #update - * it will **not** be restored to a default value or to any value which was set manually with calls to #ramses::Property::set(). * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param sourceProperty the output property which is currently linked to \p targetProperty * @param targetProperty the property which will no longer receive the value from \p sourceProperty * @return true if unlinking was successful, false otherwise. To get more detailed - * error information use #getErrors(). + * error information use #ramses::RamsesFramework::getLastError. */ - RAMSES_API bool unlink(const Property& sourceProperty, const Property& targetProperty); + bool unlink(Property& sourceProperty, Property& targetProperty); /** * Checks if an input or output of a given LogicNode is linked to another LogicNode * @param logicNode the node to check for linkage. * @return true if the given LogicNode is linked to any other LogicNode, false otherwise. */ - [[nodiscard]] RAMSES_API bool isLinked(const LogicNode& logicNode) const; + [[nodiscard]] bool isLinked(const LogicNode& logicNode) const; /** * Collect and retrieve all existing links between properties of logic nodes. @@ -629,30 +558,12 @@ namespace ramses * * @return all existing links between properties of logic nodes. */ - [[nodiscard]] RAMSES_API const std::vector& getPropertyLinks() const; + [[nodiscard]] const std::vector& getPropertyLinks() const; /** - * Returns the list of all errors which occurred during the last API call to a #LogicEngine method - * or any other method of its subclasses (scripts, bindings etc). Note that errors get wiped by all - * mutable methods of the #LogicEngine. - * - * This method can be used in two different ways: - * - To debug the correct usage of the Logic Engine API (e.g. by wrapping all API calls with a check - * for their return value and using this method to find out the cause of the error) - * - To check for runtime errors of scripts which come from a dynamic source, e.g. by calling the - * method after an unsuccessful call to #update() with a faulty script - * - * @return a list of errors + * @copydoc getPropertyLinks() const */ - [[nodiscard]] RAMSES_API const std::vector& getErrors() const; - - /** - * Performs a (potentially slow!) validation of the LogicEngine content and reports a list of warnings - * for things which could be fixed/changed for better performance, consistency or resource usage. - * - * @return a list of warnings - */ - [[nodiscard]] RAMSES_API const std::vector& validate() const; + [[nodiscard]] const std::vector& getPropertyLinks(); /** * Destroys an instance of an object created with #LogicEngine. @@ -667,98 +578,10 @@ namespace ramses * * In case of a #ramses::LuaModule, destroy will fail if it is used in any #ramses::LuaScript. * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * * @param object the object instance to destroy - * @return true if object destroyed, false otherwise. Call #getErrors() for error details upon failure. + * @return true if object destroyed, false otherwise. Call #ramses::RamsesFramework::getLastError for error details upon failure. */ - RAMSES_API bool destroy(LogicObject& object); - - /** - * Writes the whole #LogicEngine and all of its objects to a binary file with the given filename. The RAMSES scene - * potentially referenced by #ramses::RamsesBinding objects is not saved - that is left to the application. - * #LogicEngine saves the references to those object, and restores them after loading. - * Thus, deleting Ramses objects which are being referenced from within the #LogicEngine - * will result in errors if the Logic Engine is loaded from the file again. Note that it is not sufficient - * to have objects with the same name, they have to be the exact same objects as during saving! - * For more in-depth information regarding saving and loading, refer to the online documentation at - * https://ramses-logic.readthedocs.io/en/latest/api.html#saving-loading-from-file - * - * Note: The method reports error and aborts if the #ramses::RamsesBinding objects reference more than one - * Ramses scene (this is acceptable during runtime, but not for saving to file). - * - * Note: This method fails and reports error if validation failed and was not disabled by - * calling #ramses::SaveFileConfig::setValidationEnabled with false - * - * Note: This method fails and reports error if there is any Lua script or module with debug log functions, - * (see #ramses::LuaConfig::enableDebugLogFunctions), these must be destroyed before saving to file. - * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * - * @param filename path to file to save the data (relative or absolute). The file will be created or overwritten if it exists! - * @param config optional configuration object with exporter and asset metadata info, see #ramses::SaveFileConfig for details - * @return true if saving was successful, false otherwise. To get more detailed - * error information use #getErrors() - */ - RAMSES_API bool saveToFile(std::string_view filename, const SaveFileConfig& config = {}); - - /** - * Loads the whole LogicEngine data from the given file. See also #saveToFile(). - * After loading, the previous state of the #LogicEngine will be overwritten with the - * contents loaded from the file, i.e. all previously created objects (scripts, bindings, etc.) - * will be deleted and pointers to them will be invalid. The (optionally) provided ramsesScene - * will be used to resolve potential #ramses::RamsesBinding objects which point to Ramses objects. - * You can provide a nullptr if you know for sure that the - * #LogicEngine loaded from the file has no #ramses::RamsesBinding objects which point to a Ramses scene object. - * Otherwise, the call to #loadFromFile will fail with an error. In case of errors, the #LogicEngine - * may be left in an inconsistent state. - * For more in-depth information regarding saving and loading, refer to the online documentation at - * https://ramses-logic.readthedocs.io/en/latest/api.html#saving-loading-from-file - * - * Attention! This method clears all previous errors! See also docs of #getErrors() - * - * @param filename path to file from which to load content (relative or absolute) - * @param ramsesScene pointer to the Ramses Scene which holds the objects referenced in the Ramses Logic file - * @param enableMemoryVerification flag to enable memory verifier (a flatbuffers feature which checks bounds and ranges). - * Disable this only if the file comes from a trusted source and performance is paramount. - * @return true if deserialization was successful, false otherwise. To get more detailed - * error information use #getErrors() - */ - RAMSES_API bool loadFromFile(std::string_view filename, ramses::Scene* ramsesScene = nullptr, bool enableMemoryVerification = true); - - /** - * Loads the whole LogicEngine data from the given file descriptor. This method is equivalent to #loadFromFile(). - * - * The file descriptor must be opened for read access and must support seeking. - * It will be in closed state after this call. - * - * @param[in] fd Open and readable filedescriptor. - * @param[in] offset Absolute starting position of LogicEngine data within fd. - * @param[in] length Size of the data within fd. - * @param ramsesScene pointer to the Ramses Scene which holds the objects referenced in the Ramses Logic file - * @param enableMemoryVerification flag to enable memory verifier (a flatbuffers feature which checks bounds and ranges). - * Disable this only if the file comes from a trusted source and performance is paramount. - * @return true if deserialization was successful, false otherwise. To get more detailed - * error information use #getErrors() - */ - RAMSES_API bool loadFromFileDescriptor(int fd, size_t offset, size_t length, ramses::Scene* ramsesScene = nullptr, bool enableMemoryVerification = true); - - /** - * Loads the whole LogicEngine data from the given memory buffer. This method is equivalent to - * #loadFromFile but allows to have the file-opening - * logic done by the user and only pass the data as a buffer. The logic engine only reads the - * data, does not take ownership of it and does not modify it. The memory can be freed or - * modified after the call returns, the #LogicEngine keeps no references to it. - * - * @param rawBuffer pointer to the raw data in memory - * @param bufferSize size of the data (bytes) - * @param ramsesScene pointer to the Ramses Scene which holds the objects referenced in the Ramses Logic file - * @param enableMemoryVerification flag to enable memory verifier (a flatbuffers feature which checks bounds and ranges). - * Disable this only if the file comes from a trusted source and performance is paramount. - * @return true if deserialization was successful, false otherwise. To get more detailed - * error information use #getErrors() - */ - RAMSES_API bool loadFromBuffer(const void* rawBuffer, size_t bufferSize, ramses::Scene* ramsesScene = nullptr, bool enableMemoryVerification = true); + bool destroy(LogicObject& object); /** * Calculates the serialized size of all objects contained in this LogicEngine instance. @@ -768,7 +591,7 @@ namespace ramses * default is #ramses::ELuaSavingMode::SourceAndByteCode. * @return size in bytes of the serialized LogicEngine. */ - [[nodiscard]] RAMSES_API size_t getTotalSerializedSize(ELuaSavingMode luaSavingMode = ELuaSavingMode::SourceAndByteCode) const; + [[nodiscard]] size_t getTotalSerializedSize(ELuaSavingMode luaSavingMode = ELuaSavingMode::SourceAndByteCode) const; /** * Calculates the serialized size of all objects of a specific type in this LogicEngine instance. @@ -783,57 +606,31 @@ namespace ramses [[nodiscard]] size_t getSerializedSize(ELuaSavingMode luaSavingMode = ELuaSavingMode::SourceAndByteCode) const; /** - * Attempts to parse feature level from a Ramses Logic file. - * - * @param[in] filename file path to Ramses Logic file - * @param[out] detectedFeatureLevel feature level detected in given file (valid only if parsing successful!) - * @return true if parsing was successful, false otherwise. - */ - [[nodiscard]] RAMSES_API static bool GetFeatureLevelFromFile(std::string_view filename, ramses::EFeatureLevel& detectedFeatureLevel); - - /** - * Attempts to parse feature level from a Ramses Logic buffer. - * - * @param[in] logname additional name for errors logging (e.g. Ramses Logic file name) - * @param[in] buffer data buffer with Ramses Logic file content - * @param[in] bufferSize size of the data buffer - * @param[out] detectedFeatureLevel feature level detected in given file (valid only if parsing successful!) - * @return true if parsing was successful, false otherwise. - */ - [[nodiscard]] RAMSES_API static bool GetFeatureLevelFromBuffer(std::string_view logname, const void* buffer, size_t bufferSize, ramses::EFeatureLevel& detectedFeatureLevel); - - /** - * Copy Constructor of LogicEngine is deleted because logic engines hold named resources and are not supposed to be copied - * - * @param other logic engine to copy from - */ - LogicEngine(const LogicEngine& other) = delete; + * Get the internal data for implementation specifics of #LogicEngine. + */ + [[nodiscard]] internal::LogicEngineImpl& impl(); /** - * Move Constructor of LogicEngine - * - * @param other logic engine to move from - */ - RAMSES_API LogicEngine(LogicEngine&& other) noexcept; + * Get the internal data for implementation specifics of #LogicEngine. + */ + [[nodiscard]] const internal::LogicEngineImpl& impl() const; + protected: /** - * Assignment operator of LogicEngine is deleted because logic engines hold named resources and are not supposed to be copied - * - * @param other logic engine to assign from + * Implementation detail of LogicEngine */ - LogicEngine& operator=(const LogicEngine& other) = delete; + internal::LogicEngineImpl& m_impl; /** - * Move assignment operator of LogicEngine - * - * @param other logic engine to move from + * @brief Constructor for LogicEngine. Use #ramses::Scene::createLogicEngine to instantiate this class. + * @param[in] impl Internal data for implementation specifics of LogicEngine (sink - instance becomes owner) */ - RAMSES_API LogicEngine& operator=(LogicEngine&& other) noexcept; + explicit LogicEngine(std::unique_ptr impl); /** - * Implementation detail of LogicEngine + * @brief Scene is the factory for creating LogicEngine instances. */ - std::unique_ptr m_impl; + friend class internal::SceneObjectRegistry; private: /** @@ -841,7 +638,7 @@ namespace ramses * @return collection of objects */ template - [[nodiscard]] RAMSES_API Collection getLogicObjectsInternal() const; + [[nodiscard]] Collection getLogicObjectsInternal() const; /** * Internal implementation of type specific size getter @@ -850,7 +647,7 @@ namespace ramses * @return size in bytes of the serialized objects. */ template - [[nodiscard]] RAMSES_API size_t getSerializedSizeInternal(ELuaSavingMode luaSavingMode) const; + [[nodiscard]] size_t getSerializedSizeInternal(ELuaSavingMode luaSavingMode) const; /** * Internal implementation of object finder @@ -858,7 +655,7 @@ namespace ramses * @return found object */ template - [[nodiscard]] RAMSES_API const T* findLogicObjectInternal(std::string_view name) const; + [[nodiscard]] const T* findLogicObjectInternal(std::string_view name) const; /** * Internal implementation of object finder @@ -866,7 +663,23 @@ namespace ramses * @return found object */ template - [[nodiscard]] RAMSES_API T* findLogicObjectInternal(std::string_view name); + [[nodiscard]] T* findLogicObjectInternal(std::string_view name); + + /** + * Internal implementation of object finder + * @param id object id + * @return found object + */ + template + [[nodiscard]] const T* findLogicObjectInternal(sceneObjectId_t id) const; + + /** + * Internal implementation of object finder + * @param id object id + * @return found object + */ + template + [[nodiscard]] T* findLogicObjectInternal(sceneObjectId_t id); /** * Internal implementation of #createDataArray @@ -876,7 +689,7 @@ namespace ramses * @return a pointer to the created object or nullptr on error */ template - [[nodiscard]] RAMSES_API DataArray* createDataArrayInternal(const std::vector& data, std::string_view name); + [[nodiscard]] DataArray* createDataArrayInternal(const std::vector& data, std::string_view name); /// Internal static helper to validate type template @@ -891,19 +704,33 @@ namespace ramses } template - const T* LogicEngine::findByName(std::string_view name) const + const T* LogicEngine::findObject(std::string_view name) const { StaticTypeCheck(); return findLogicObjectInternal(name); } template - T* LogicEngine::findByName(std::string_view name) + T* LogicEngine::findObject(std::string_view name) { StaticTypeCheck(); return findLogicObjectInternal(name); } + template + const T* LogicEngine::findObject(sceneObjectId_t id) const + { + StaticTypeCheck(); + return findLogicObjectInternal(id); + } + + template + T* LogicEngine::findObject(sceneObjectId_t id) + { + StaticTypeCheck(); + return findLogicObjectInternal(id); + } + template DataArray* LogicEngine::createDataArray(const std::vector& data, std::string_view name) { @@ -920,12 +747,12 @@ namespace ramses std::is_same_v || std::is_same_v || std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || diff --git a/client/logic/include/ramses-logic/LogicEngineReport.h b/include/ramses/client/logic/LogicEngineReport.h similarity index 81% rename from client/logic/include/ramses-logic/LogicEngineReport.h rename to include/ramses/client/logic/LogicEngineReport.h index d7b3021f6..4ce2ddffa 100644 --- a/client/logic/include/ramses-logic/LogicEngineReport.h +++ b/include/ramses/client/logic/LogicEngineReport.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include #include @@ -28,7 +28,7 @@ namespace ramses * Can be obtained using #ramses::LogicEngine::getLastUpdateReport after an update with enabled reporting * (#ramses::LogicEngine::enableUpdateReport). */ - class LogicEngineReport + class RAMSES_API LogicEngineReport { public: /// LogicNode with measured update execution @@ -43,7 +43,7 @@ namespace ramses * * @return list of updated nodes with update execution time, sorted by execution order */ - [[nodiscard]] RAMSES_API const std::vector& getNodesExecuted() const; + [[nodiscard]] const std::vector& getNodesExecuted() const; /** * List of logic nodes that were not updated because their inputs did not change therefore there was no need to. @@ -52,7 +52,7 @@ namespace ramses * * @return list of nodes which did not need update */ - [[nodiscard]] RAMSES_API const std::vector& getNodesSkippedExecution() const; + [[nodiscard]] const std::vector& getNodesSkippedExecution() const; /** * Time it took to sort logic nodes by their topology during update. @@ -61,7 +61,7 @@ namespace ramses * * @return time to sort logic nodes */ - [[nodiscard]] RAMSES_API std::chrono::microseconds getTopologySortExecutionTime() const; + [[nodiscard]] std::chrono::microseconds getTopologySortExecutionTime() const; /** * Time it took to update the whole logic nodes network. @@ -70,31 +70,31 @@ namespace ramses * * @return time to update all logic nodes */ - [[nodiscard]] RAMSES_API std::chrono::microseconds getTotalUpdateExecutionTime() const; + [[nodiscard]] std::chrono::microseconds getTotalUpdateExecutionTime() const; /** * Obtain the number of links activated during update. * * @return the number of links activated during update */ - [[nodiscard]] RAMSES_API size_t getTotalLinkActivations() const; + [[nodiscard]] size_t getTotalLinkActivations() const; /** * Default constructor of LogicEngineReport. */ - RAMSES_API LogicEngineReport() noexcept; + LogicEngineReport() noexcept; /** * Constructor of LogicEngineReport. Do not construct, use #ramses::LogicEngine::getLastUpdateReport to obtain. * * @param impl implementation details of the LogicEngineReport */ - RAMSES_API explicit LogicEngineReport(std::unique_ptr impl) noexcept; + explicit LogicEngineReport(std::unique_ptr impl) noexcept; /** * Class destructor */ - RAMSES_API ~LogicEngineReport(); + ~LogicEngineReport(); /** * Copying disabled, move instead. @@ -106,7 +106,7 @@ namespace ramses * * @param other source */ - RAMSES_API LogicEngineReport(LogicEngineReport&& other) noexcept; + LogicEngineReport(LogicEngineReport&& other) noexcept; /** * Copying disabled, move instead. @@ -118,7 +118,7 @@ namespace ramses * * @param other source */ - RAMSES_API LogicEngineReport& operator=(LogicEngineReport&& other) noexcept; + LogicEngineReport& operator=(LogicEngineReport&& other) noexcept; private: /** diff --git a/client/logic/include/ramses-logic/LogicNode.h b/include/ramses/client/logic/LogicNode.h similarity index 80% rename from client/logic/include/ramses-logic/LogicNode.h rename to include/ramses/client/logic/LogicNode.h index 49172ea62..472842b29 100644 --- a/client/logic/include/ramses-logic/LogicNode.h +++ b/include/ramses/client/logic/LogicNode.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicObject.h" +#include "ramses/client/logic/LogicObject.h" #include @@ -25,12 +25,12 @@ namespace ramses * A base class for multiple logic classes which provides a unified interface to their * inputs and outputs. Some subclasses don't have inputs or outputs - in that case the * #getInputs or #getOutputs methods respectively will return nullptr. Some subclasses, - * like the #ramses::RamsesAppearanceBinding, will have their inputs depending on their + * like the #ramses::AppearanceBinding, will have their inputs depending on their * current state (in this example the GLSL uniforms of the shader to which the bound ramses * Appearance belongs). In those cases, #getInputs()/#getOutputs() will return a #ramses::Property * which represents an empty struct (type Struct, but no child properties). */ - class LogicNode : public LogicObject + class RAMSES_API LogicNode : public LogicObject { public: /** @@ -44,12 +44,12 @@ namespace ramses * of each derived class for more information on the properties. * @return a tree like structure with the inputs of the #LogicNode */ - [[nodiscard]] RAMSES_API Property* getInputs(); + [[nodiscard]] Property* getInputs(); /** * @copydoc getInputs() */ - [[nodiscard]] RAMSES_API const Property* getInputs() const; + [[nodiscard]] const Property* getInputs() const; /** * Returns a property of type Struct which holds the outputs of the #LogicNode @@ -62,12 +62,22 @@ namespace ramses * of each derived class for more information on the properties. * @return a tree like structure with the outputs of the LogicNode */ - [[nodiscard]] RAMSES_API const Property* getOutputs() const; + [[nodiscard]] Property* getOutputs(); /** - * Implementation detail of LogicNode + * @copydoc getOutputs() */ - internal::LogicNodeImpl& m_impl; + [[nodiscard]] const Property* getOutputs() const; + + /** + * Get the internal data for implementation specifics of #LogicNode. + */ + [[nodiscard]] internal::LogicNodeImpl& impl(); + + /** + * Get the internal data for implementation specifics of #LogicNode. + */ + [[nodiscard]] const internal::LogicNodeImpl& impl() const; protected: /** @@ -76,5 +86,10 @@ namespace ramses * @param impl implementation details of the LogicNode */ explicit LogicNode(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of LogicNode + */ + internal::LogicNodeImpl& m_impl; }; } diff --git a/include/ramses/client/logic/LogicObject.h b/include/ramses/client/logic/LogicObject.h new file mode 100644 index 000000000..e3be921af --- /dev/null +++ b/include/ramses/client/logic/LogicObject.h @@ -0,0 +1,52 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/client/SceneObject.h" + +namespace ramses::internal +{ + class LogicObjectImpl; + class ApiObjects; +} + +namespace ramses +{ + /** + * A base class for all rlogic API objects + */ + class RAMSES_API LogicObject : public SceneObject + { + public: + /** + * Get the internal data for implementation specifics of #LogicObject. + */ + [[nodiscard]] internal::LogicObjectImpl& impl(); + + /** + * Get the internal data for implementation specifics of #LogicObject. + */ + [[nodiscard]] const internal::LogicObjectImpl& impl() const; + + protected: + /** + * Constructor of #LogicObject. User is not supposed to call this - LogcNodes are created by subclasses + * + * @param impl implementation details of the #LogicObject + */ + explicit LogicObject(std::unique_ptr impl) noexcept; + + /** + * Stores internal data for implementation specifics of #LogicObject. + */ + internal::LogicObjectImpl& m_impl; + + friend class internal::ApiObjects; + }; +} diff --git a/client/logic/include/ramses-logic/LuaConfig.h b/include/ramses/client/logic/LuaConfig.h similarity index 78% rename from client/logic/include/ramses-logic/LuaConfig.h rename to include/ramses/client/logic/LuaConfig.h index 4e26271a4..0ecac1625 100644 --- a/client/logic/include/ramses-logic/LuaConfig.h +++ b/include/ramses/client/logic/LuaConfig.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/EStandardModule.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/EStandardModule.h" #include #include @@ -27,10 +27,10 @@ namespace ramses * Holds configuration settings for Lua script and module creation. Can be default-constructed, moved * and copied. */ - class LuaConfig + class RAMSES_API LuaConfig { public: - RAMSES_API LuaConfig() noexcept; + LuaConfig() noexcept; /** * Adds a #ramses::LuaModule as a dependency to be added when this config is used for script or module @@ -47,7 +47,7 @@ namespace ramses * @return true if the dependency was added successfully, false otherwise * In case of an error, check the logs. */ - RAMSES_API bool addDependency(std::string_view aliasName, const LuaModule& moduleInstance); + bool addDependency(std::string_view aliasName, const LuaModule& moduleInstance); /** * Adds a standard module dependency. The module is mapped under a name as documented in #ramses::EStandardModule. @@ -56,7 +56,7 @@ namespace ramses * @return true if the standard module was added successfully, false otherwise * In case of an error, check the logs. */ - RAMSES_API bool addStandardModuleDependency(EStandardModule stdModule); + bool addStandardModuleDependency(EStandardModule stdModule); /** * Will expose these log functions in Lua: @@ -66,42 +66,53 @@ namespace ramses * Each with single string as argument. * Call from Lua script or module to any of these will forward the string message to a corresponding internal Ramses Logic logger. * - * IMPORTANT: These functions are meant for debug/prototype purposes only and attempt to save the project to file (#ramses::LogicEngine::saveToFile) + * IMPORTANT: These functions are meant for debug/prototype purposes only and attempt to save the project to file (#ramses::Scene::saveToFile) * with any script or module with these functions enabled will result in failure! */ - RAMSES_API void enableDebugLogFunctions(); + void enableDebugLogFunctions(); /** * Destructor of #LuaConfig */ - RAMSES_API ~LuaConfig() noexcept; + ~LuaConfig() noexcept; /** * Copy Constructor of #LuaConfig * @param other the other #LuaConfig to copy from */ - RAMSES_API LuaConfig(const LuaConfig& other); + LuaConfig(const LuaConfig& other); /** * Move Constructor of #LuaConfig * @param other the other #LuaConfig to move from */ - RAMSES_API LuaConfig(LuaConfig&& other) noexcept; + LuaConfig(LuaConfig&& other) noexcept; /** * Assignment operator of #LuaConfig * @param other the other #LuaConfig to copy from * @return self */ - RAMSES_API LuaConfig& operator=(const LuaConfig& other); + LuaConfig& operator=(const LuaConfig& other); /** * Move assignment operator of #LuaConfig * @param other the other #LuaConfig to move from * @return self */ - RAMSES_API LuaConfig& operator=(LuaConfig&& other) noexcept; + LuaConfig& operator=(LuaConfig&& other) noexcept; + /** + * Get the internal data for implementation specifics of #LuaConfig. + */ + [[nodiscard]] internal::LuaConfigImpl& impl(); + + /** + * Get the internal data for implementation specifics of #LuaConfig. + */ + [[nodiscard]] const internal::LuaConfigImpl& impl() const; + + protected: /** * Implementation detail of #LuaConfig */ diff --git a/client/logic/include/ramses-logic/LuaInterface.h b/include/ramses/client/logic/LuaInterface.h similarity index 76% rename from client/logic/include/ramses-logic/LuaInterface.h rename to include/ramses/client/logic/LuaInterface.h index 8ec752ff8..c62d15b0b 100644 --- a/client/logic/include/ramses-logic/LuaInterface.h +++ b/include/ramses/client/logic/LuaInterface.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicNode.h" +#include "ramses/client/logic/LogicNode.h" #include #include @@ -33,20 +33,24 @@ namespace ramses * - A Lua interface contains only one global function: interface() * - The interface() function takes only one parameter that represents both inputs and outputs, e.g., interface(INOUT_PARAMS) * - * Violating any of these requirements will result in errors, which can be obtained by calling - * #ramses::LogicEngine::getErrors(). + * Violating any of these requirements will result in error, which can be obtained by calling #ramses::RamsesFramework::getLastError(). * For other than interface definition purposes, see #ramses::LuaModule and #ramses::LuaScript for details. * * See also the full documentation at https://ramses-logic.readthedocs.io/en/latest/api.html for more details on Lua and * its interaction with C++. */ - class LuaInterface : public LogicNode + class RAMSES_API LuaInterface : public LogicNode { public: /** - * Implementation detail of LuaInterface - */ - internal::LuaInterfaceImpl& m_interface; + * Get the internal data for implementation specifics of #LuaInterface. + */ + [[nodiscard]] internal::LuaInterfaceImpl& impl(); + + /** + * Get the internal data for implementation specifics of #LuaInterface. + */ + [[nodiscard]] const internal::LuaInterfaceImpl& impl() const; protected: /** @@ -56,6 +60,11 @@ namespace ramses */ explicit LuaInterface(std::unique_ptr impl) noexcept; + /** + * Implementation detail of LuaInterface + */ + internal::LuaInterfaceImpl& m_interface; + friend class internal::ApiObjects; }; } diff --git a/client/logic/include/ramses-logic/LuaModule.h b/include/ramses/client/logic/LuaModule.h similarity index 87% rename from client/logic/include/ramses-logic/LuaModule.h rename to include/ramses/client/logic/LuaModule.h index 4f778622f..aa649520d 100644 --- a/client/logic/include/ramses-logic/LuaModule.h +++ b/include/ramses/client/logic/LuaModule.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicObject.h" +#include "ramses/client/logic/LogicObject.h" namespace ramses::internal { @@ -58,13 +58,18 @@ namespace ramses * * The label 'Type' is a reserved keyword and must not be overwritten for other purposes (e.g. name of variable or function). */ - class LuaModule : public LogicObject + class RAMSES_API LuaModule : public LogicObject { public: /** - * Implementation detail of #LuaModule - */ - internal::LuaModuleImpl& m_impl; + * Get the internal data for implementation specifics of #LuaModule. + */ + [[nodiscard]] internal::LuaModuleImpl& impl(); + + /** + * Get the internal data for implementation specifics of #LuaModule. + */ + [[nodiscard]] const internal::LuaModuleImpl& impl() const; protected: /** @@ -74,6 +79,11 @@ namespace ramses */ explicit LuaModule(std::unique_ptr impl) noexcept; + /** + * Implementation detail of #LuaModule + */ + internal::LuaModuleImpl& m_impl; + friend class internal::ApiObjects; }; } diff --git a/client/logic/include/ramses-logic/LuaScript.h b/include/ramses/client/logic/LuaScript.h similarity index 88% rename from client/logic/include/ramses-logic/LuaScript.h rename to include/ramses/client/logic/LuaScript.h index e5727ae8b..5382df439 100644 --- a/client/logic/include/ramses-logic/LuaScript.h +++ b/include/ramses/client/logic/LuaScript.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicNode.h" +#include "ramses/client/logic/LogicNode.h" #include #include @@ -64,19 +64,23 @@ namespace ramses * - the string provided will be the name under which the module will be available in other functions * - See also #ramses::LuaModule for more info on modules and their syntax * - * Violating any of these requirements will result in errors, which can be obtained by calling - * #ramses::LogicEngine::getErrors(). + * Violating any of these requirements will result in error, which can be obtained by calling #ramses::RamsesFramework::getLastError(). * * See also the full documentation at https://ramses-logic.readthedocs.io/en/latest/api.html for more details on Lua and * its interaction with C++. */ - class LuaScript : public LogicNode + class RAMSES_API LuaScript : public LogicNode { public: /** - * Implementation detail of LuaScript - */ - internal::LuaScriptImpl& m_script; + * Get the internal data for implementation specifics of #LuaScript. + */ + [[nodiscard]] internal::LuaScriptImpl& impl(); + + /** + * Get the internal data for implementation specifics of #LuaScript. + */ + [[nodiscard]] const internal::LuaScriptImpl& impl() const; protected: /** @@ -86,6 +90,11 @@ namespace ramses */ explicit LuaScript(std::unique_ptr impl) noexcept; + /** + * Implementation detail of LuaScript + */ + internal::LuaScriptImpl& m_script; + friend class internal::ApiObjects; }; } diff --git a/client/logic/include/ramses-logic/RamsesMeshNodeBinding.h b/include/ramses/client/logic/MeshNodeBinding.h similarity index 60% rename from client/logic/include/ramses-logic/RamsesMeshNodeBinding.h rename to include/ramses/client/logic/MeshNodeBinding.h index 764818463..ad89fa8a9 100644 --- a/client/logic/include/ramses-logic/RamsesMeshNodeBinding.h +++ b/include/ramses/client/logic/MeshNodeBinding.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/RamsesBinding.h" namespace ramses { @@ -18,16 +18,16 @@ namespace ramses namespace ramses::internal { - class RamsesMeshNodeBindingImpl; + class MeshNodeBindingImpl; } namespace ramses { /** - * The #RamsesMeshNodeBinding binds to a Ramses object instance of ramses::MeshNode and exposes a set of input properties + * The #MeshNodeBinding binds to a Ramses object instance of ramses::MeshNode and exposes a set of input properties * allowing to control certain states the MeshNode. * - * #RamsesMeshNodeBinding has these input properties: + * #MeshNodeBinding has these input properties: * 'vertexOffset' (type int32) * - binds to MeshNode's vertex offset, i.e. start offset into vertex arrays of the mesh (ramses::MeshNode::setStartVertex). * 'indexOffset' (type int32) @@ -37,16 +37,16 @@ namespace ramses * 'instanceCount' (type int32) * - binds to MeshNode's instance count, i.e. number of instances to render when using geometry instancing (ramses::MeshNode::setInstanceCount). * - * The initial values of the input properties are taken from the bound ramses::MeshNode when the #RamsesMeshNodeBinding is created. + * The initial values of the input properties are taken from the bound ramses::MeshNode when the #MeshNodeBinding is created. * - * The #RamsesMeshNodeBinding class has no output properties (thus #ramses::LogicNode::getOutputs() will return nullptr) because + * The #MeshNodeBinding class has no output properties (thus #ramses::LogicNode::getOutputs() will return nullptr) because * the outputs are implicitly forwarded to the bound ramses::MeshNode. * * The changes via binding objects are applied to the bound object right away when calling ramses::LogicEngine::update(), * however keep in mind that Ramses has a mechanism for bundling scene changes and applying them at once using ramses::Scene::flush, * so the changes will be applied all the way only after calling this method on the scene. */ - class RamsesMeshNodeBinding : public RamsesBinding + class RAMSES_API MeshNodeBinding : public RamsesBinding { public: /** @@ -54,25 +54,37 @@ namespace ramses * * @return the bound Ramses MeshNode */ - [[nodiscard]] RAMSES_API const ramses::MeshNode& getRamsesMeshNode() const; + [[nodiscard]] const ramses::MeshNode& getRamsesMeshNode() const; /** * Returns the bound Ramses MeshNode. * * @return the bound Ramses MeshNode */ - [[nodiscard]] RAMSES_API ramses::MeshNode& getRamsesMeshNode(); + [[nodiscard]] ramses::MeshNode& getRamsesMeshNode(); - /// Implementation detail of RamsesMeshNodeBinding - internal::RamsesMeshNodeBindingImpl& m_meshNodeBinding; + /** + * Get the internal data for implementation specifics of MeshNodeBinding. + */ + [[nodiscard]] internal::MeshNodeBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of MeshNodeBinding. + */ + [[nodiscard]] const internal::MeshNodeBindingImpl& impl() const; protected: /** - * Constructor of RamsesMeshNodeBinding. User is not supposed to call this - RamsesMeshNodeBindings are created by other factory classes + * Constructor of MeshNodeBinding. User is not supposed to call this - MeshNodeBindings are created by other factory classes * - * @param impl implementation details of the RamsesMeshNodeBinding + * @param impl implementation details of the MeshNodeBinding */ - explicit RamsesMeshNodeBinding(std::unique_ptr impl) noexcept; + explicit MeshNodeBinding(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of MeshNodeBinding + */ + internal::MeshNodeBindingImpl& m_meshNodeBinding; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/RamsesNodeBinding.h b/include/ramses/client/logic/NodeBinding.h similarity index 67% rename from client/logic/include/ramses-logic/RamsesNodeBinding.h rename to include/ramses/client/logic/NodeBinding.h index a9128051a..2e4c36b84 100644 --- a/client/logic/include/ramses-logic/RamsesNodeBinding.h +++ b/include/ramses/client/logic/NodeBinding.h @@ -8,9 +8,9 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-client-api/ERotationType.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/framework/ERotationType.h" +#include "ramses/client/logic/RamsesBinding.h" #include @@ -21,16 +21,16 @@ namespace ramses namespace ramses::internal { - class RamsesNodeBindingImpl; + class NodeBindingImpl; } namespace ramses { /** - * The RamsesNodeBinding is a #ramses::RamsesBinding which allows manipulation of a Ramses node. - * RamsesNodeBinding can be created using #ramses::LogicEngine::createRamsesNodeBinding. + * The NodeBinding is a #ramses::RamsesBinding which allows manipulation of a Ramses node. + * NodeBinding can be created using #ramses::LogicEngine::createNodeBinding. * - * The RamsesNodeBinding has a fixed set of inputs which correspond to the properties of a ramses::Node: + * The NodeBinding has a fixed set of inputs which correspond to the properties of a ramses::Node: * 'visibility' (type bool) - binds to node's visibility mode to switch between visible and invisible, * see also 'enabled' input for more options * 'rotation' (type vec3f, or vec4f in case of quaternion) - binds to node's rotation, @@ -50,14 +50,14 @@ namespace ramses * ramses node match (both are either Quaternion or Euler with the same axis ordering). Otherwise a warning is * issued and the rotation values are set to 0. * - * The RamsesNodeBinding class has no output properties (thus getOutputs() will return nullptr) because + * The NodeBinding class has no output properties (thus getOutputs() will return nullptr) because * the outputs are implicitly the properties of the bound Ramses node. * * \rst .. note:: - Not all states of the #ramses::Node must be managed via #RamsesNodeBinding, input properties which are not modified - via #RamsesNodeBinding (i.e. user never set a value explicitly nor linked the input) will not alter the corresponding Ramses object + Not all states of the #ramses::Node must be managed via #NodeBinding, input properties which are not modified + via #NodeBinding (i.e. user never set a value explicitly nor linked the input) will not alter the corresponding Ramses object state. This allows the user code to control some states via Ramses logic nodes and some directly using Ramses object. \endrst @@ -66,7 +66,7 @@ namespace ramses * however keep in mind that Ramses has a mechanism for bundling scene changes and applying them at once using ramses::Scene::flush, * so the changes will be applied all the way only after calling this method on the scene. */ - class RamsesNodeBinding : public RamsesBinding + class RAMSES_API NodeBinding : public RamsesBinding { public: /** @@ -74,27 +74,37 @@ namespace ramses * * @return the bound ramses node */ - [[nodiscard]] RAMSES_API ramses::Node& getRamsesNode() const; + [[nodiscard]] ramses::Node& getRamsesNode() const; /** * Returns the statically configured rotation type for the node rotation property. * * @return the currently used rotation type */ - [[nodiscard]] RAMSES_API ramses::ERotationType getRotationType() const; + [[nodiscard]] ramses::ERotationType getRotationType() const; /** - * Implementation detail of RamsesNodeBinding + * Get the internal data for implementation specifics of NodeBinding. */ - internal::RamsesNodeBindingImpl& m_nodeBinding; + [[nodiscard]] internal::NodeBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of NodeBinding. + */ + [[nodiscard]] const internal::NodeBindingImpl& impl() const; protected: /** - * Constructor of RamsesNodeBinding. User is not supposed to call this - RamsesNodeBindings are created by other factory classes + * Constructor of NodeBinding. User is not supposed to call this - NodeBindings are created by other factory classes * - * @param impl implementation details of the RamsesNodeBinding + * @param impl implementation details of the NodeBinding */ - explicit RamsesNodeBinding(std::unique_ptr impl) noexcept; + explicit NodeBinding(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of NodeBinding + */ + internal::NodeBindingImpl& m_nodeBinding; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/Property.h b/include/ramses/client/logic/Property.h similarity index 80% rename from client/logic/include/ramses-logic/Property.h rename to include/ramses/client/logic/Property.h index bf367f6b3..026bd5bcb 100644 --- a/client/logic/include/ramses-logic/Property.h +++ b/include/ramses/client/logic/Property.h @@ -8,9 +8,9 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/PropertyLink.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/PropertyLink.h" #include #include @@ -32,7 +32,7 @@ namespace ramses * Complex types can have "children", i.e. nested properties: named fields (structs), or indexed * fields (arrays). */ - class Property + class RAMSES_API Property { public: /** @@ -40,7 +40,7 @@ namespace ramses * * @return the type of this Property */ - [[nodiscard]] RAMSES_API EPropertyType getType() const; + [[nodiscard]] EPropertyType getType() const; /** * Returns the name of this Property. Note that not all properties have a name - @@ -49,7 +49,7 @@ namespace ramses * * @return the name of this Property */ - [[nodiscard]] RAMSES_API std::string_view getName() const; + [[nodiscard]] std::string_view getName() const; /** * Returns the amount of available child (nested) properties. If the Property @@ -60,7 +60,7 @@ namespace ramses * * @return the number of nested properties. */ - [[nodiscard]] RAMSES_API size_t getChildCount() const; + [[nodiscard]] size_t getChildCount() const; /** * Return whether the #ramses::Property has a child with the given name @@ -68,7 +68,7 @@ namespace ramses * @param name the name of the child that should be checked for existence * @return whether the child with the given name exists */ - [[nodiscard]] RAMSES_API bool hasChild(std::string_view name) const; + [[nodiscard]] bool hasChild(std::string_view name) const; /** * Returns the child property with the given \p index. \p index must be < #getChildCount(). @@ -87,12 +87,12 @@ namespace ramses * @param[in] index zero based index of child property to retrieve * @return the child with the given index, or nullptr if the property is primitive or the index is out of range */ - [[nodiscard]] RAMSES_API const Property* getChild(size_t index) const; + [[nodiscard]] const Property* getChild(size_t index) const; /** * @copydoc getChild(size_t index) const */ - [[nodiscard]] RAMSES_API Property* getChild(size_t index); + [[nodiscard]] Property* getChild(size_t index); /** * Searches for a child with the given name. Only properties of type #ramses::EPropertyType::Struct can return a child by name. @@ -103,12 +103,12 @@ namespace ramses * @param[in] name name of child property to retrieve * @return the child with the given name, or nullptr if property is not of type #ramses::EPropertyType::Struct */ - [[nodiscard]] RAMSES_API Property* getChild(std::string_view name); + [[nodiscard]] Property* getChild(std::string_view name); /** * @copydoc getChild(std::string_view name) */ - [[nodiscard]] RAMSES_API const Property* getChild(std::string_view name) const; + [[nodiscard]] const Property* getChild(std::string_view name) const; /** * Returns the value of this property. The supported template types are defined by @@ -146,7 +146,7 @@ namespace ramses * * @return true if the property is linked, false otherwise. */ - [[nodiscard]] RAMSES_API bool isLinked() const; + [[nodiscard]] bool isLinked() const; /** * Checks if there is any incoming link to this property, i.e. data is flowing to it from another node's property. @@ -154,7 +154,7 @@ namespace ramses * * @return true if the property is an input and is linked, false otherwise. */ - [[nodiscard]] RAMSES_API bool hasIncomingLink() const; + [[nodiscard]] bool hasIncomingLink() const; /** * Checks if there is any outgoing link from this property, i.e. data is flowing from it to another node's property. @@ -162,7 +162,7 @@ namespace ramses * * @return true if the property is an output and is linked, false otherwise. */ - [[nodiscard]] RAMSES_API bool hasOutgoingLink() const; + [[nodiscard]] bool hasOutgoingLink() const; /** * Returns information about the incoming link to this property, namely which property is the source of the link. @@ -170,7 +170,10 @@ namespace ramses * * @return incoming link data or std::nullopt if there is no link. */ - [[nodiscard]] RAMSES_API std::optional getIncomingLink() const; + [[nodiscard]] std::optional getIncomingLink() const; + + /// @copydoc getIncomingLink() const + [[nodiscard]] std::optional getIncomingLink(); /** * Returns the number of outgoing links from this property. @@ -178,7 +181,7 @@ namespace ramses * * @return number of outgoing links from this property. */ - [[nodiscard]] RAMSES_API size_t getOutgoingLinksCount() const; + [[nodiscard]] size_t getOutgoingLinksCount() const; /** * Returns information about the outgoing link from this property, namely which property is the target of the link with given index. @@ -188,7 +191,10 @@ namespace ramses * @param[in] index zero based index of outgoing link to retrieve * @return outgoing link data or std::nullopt if there is no link with given index. */ - [[nodiscard]] RAMSES_API std::optional getOutgoingLink(size_t index) const; + [[nodiscard]] std::optional getOutgoingLink(size_t index) const; + + /// @copydoc getOutgoingLink(size_t index) const + [[nodiscard]] std::optional getOutgoingLink(size_t index); /** * Get #ramses::LogicNode that owns this property. @@ -196,10 +202,20 @@ namespace ramses * * @return #ramses::LogicNode that owns this property. */ - [[nodiscard]] RAMSES_API const LogicNode& getOwningLogicNode() const; + [[nodiscard]] const LogicNode& getOwningLogicNode() const; /// @copydoc getOwningLogicNode() const - [[nodiscard]] RAMSES_API LogicNode& getOwningLogicNode(); + [[nodiscard]] LogicNode& getOwningLogicNode(); + + /** + * Get the internal data for implementation specifics of #Property. + */ + [[nodiscard]] internal::PropertyImpl& impl(); + + /** + * Get the internal data for implementation specifics of #Property. + */ + [[nodiscard]] const internal::PropertyImpl& impl() const; /** * Copy Constructor of Property is deleted because properties are not supposed to be copied @@ -225,33 +241,33 @@ namespace ramses */ Property& operator=(Property&& other) = delete; + protected: /** - * Implementation details of the Property class - */ - std::unique_ptr m_impl; + * Constructor of Property. User is not supposed to call this - properties are created by other factory classes + * + * @param impl implementation details of the property + */ + explicit Property(std::unique_ptr impl) noexcept; - protected: - /** - * Constructor of Property. User is not supposed to call this - properties are created by other factory classes - * - * @param impl implementation details of the property - */ - explicit Property(std::unique_ptr impl) noexcept; - - /** - * Destructor of Property. User is not supposed to call this - properties are destroyed by other factory classes - */ - ~Property() noexcept; + /** + * Destructor of Property. User is not supposed to call this - properties are destroyed by other factory classes + */ + ~Property() noexcept; + + /** + * Implementation details of the Property class + */ + std::unique_ptr m_impl; private: /** * Internal implementation of #get */ - template [[nodiscard]] RAMSES_API std::optional getInternal() const; + template [[nodiscard]] std::optional getInternal() const; /** * Internal implementation of #set */ - template RAMSES_API bool setInternal(T value); + template bool setInternal(T value); friend class internal::PropertyImpl; }; diff --git a/client/logic/include/ramses-logic/PropertyLink.h b/include/ramses/client/logic/PropertyLink.h similarity index 78% rename from client/logic/include/ramses-logic/PropertyLink.h rename to include/ramses/client/logic/PropertyLink.h index d03db6aa6..08de3abe1 100644 --- a/client/logic/include/ramses-logic/PropertyLink.h +++ b/include/ramses/client/logic/PropertyLink.h @@ -21,6 +21,17 @@ namespace ramses * or #ramses::LogicEngine::getPropertyLinks. */ struct PropertyLink + { + /// data flow source (some #ramses::LogicNode's output) + Property* source = nullptr; + /// data flow target (another #ramses::LogicNode's input) + Property* target = nullptr; + /// \c true if this is a weak link - see #ramses::LogicEngine::linkWeak + bool isWeakLink = false; + }; + + /// @copydoc PropertyLink + struct PropertyLinkConst { /// data flow source (some #ramses::LogicNode's output) const Property* source = nullptr; diff --git a/client/logic/include/ramses-logic/RamsesBinding.h b/include/ramses/client/logic/RamsesBinding.h similarity index 91% rename from client/logic/include/ramses-logic/RamsesBinding.h rename to include/ramses/client/logic/RamsesBinding.h index 1aa1aedc0..a73a42b20 100644 --- a/client/logic/include/ramses-logic/RamsesBinding.h +++ b/include/ramses/client/logic/RamsesBinding.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicNode.h" +#include "ramses/client/logic/LogicNode.h" namespace ramses::internal { @@ -21,7 +21,7 @@ namespace ramses * The RamsesBinding is a shared base class for bindings to Ramses objects. * For details on each type of binding, look at the derived classes. */ - class RamsesBinding : public LogicNode + class RAMSES_API RamsesBinding : public LogicNode { protected: /** diff --git a/client/logic/include/ramses-logic/RamsesRenderGroupBinding.h b/include/ramses/client/logic/RenderGroupBinding.h similarity index 65% rename from client/logic/include/ramses-logic/RamsesRenderGroupBinding.h rename to include/ramses/client/logic/RenderGroupBinding.h index 8f39e6b7a..9e1ce289f 100644 --- a/client/logic/include/ramses-logic/RamsesRenderGroupBinding.h +++ b/include/ramses/client/logic/RenderGroupBinding.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/RamsesBinding.h" namespace ramses { @@ -18,20 +18,20 @@ namespace ramses namespace ramses::internal { - class RamsesRenderGroupBindingImpl; + class RenderGroupBindingImpl; } namespace ramses { /** - * The RamsesRenderGroupBinding binds to a Ramses object instance of ramses::RenderGroup. - * RamsesRenderGroupBinding allows controlling the rendering order of selected elements + * The RenderGroupBinding binds to a Ramses object instance of ramses::RenderGroup. + * RenderGroupBinding allows controlling the rendering order of selected elements * contained in the ramses::RenderGroup - these can be ramses::MeshNode and other (nested) ramses::RenderGroup objects. * - * #RamsesRenderGroupBinding is initialized with a ramses::RenderGroup and a set of elements to expose for render order + * #RenderGroupBinding is initialized with a ramses::RenderGroup and a set of elements to expose for render order * control through the binding's input properties. Not all the elements contained in the ramses::RenderGroup have to be provided, * the binding can be used to control only a subset of the elements in the render group. The set of provided elements is then static - * for the lifetime of the #RamsesRenderGroupBinding. + * for the lifetime of the #RenderGroupBinding. * * It is not possible to add or remove elements from the bound ramses::RenderGroup using this binding * and this binding will not reflect changes made to the ramses::RenderGroup via its Ramses API. @@ -41,18 +41,18 @@ namespace ramses * - An element is removed from the bound ramses::RenderGroup via Ramses API - if the removed element was part of the provided * initial set at construction of the binding, the binding is invalidated and should not be used because any attempt to change * render order of the removed element will result in update fail. - * If #RamsesRenderGroupBinding gets invalidated it needs to be destroyed and recreated with a valid set of elements contained in the render group. + * If #RenderGroupBinding gets invalidated it needs to be destroyed and recreated with a valid set of elements contained in the render group. * In this case property links (if any) get destroyed with the binding and need to be re-created. * - * When #RamsesRenderGroupBinding is created using #ramses::LogicEngine::createRamsesRenderGroupBinding it will create - * property inputs based on the provided set of elements (see #ramses::RamsesRenderGroupBindingElements): + * When #RenderGroupBinding is created using #ramses::LogicEngine::createRenderGroupBinding it will create + * property inputs based on the provided set of elements (see #ramses::RenderGroupBindingElements): * 'renderOrders' (type struct) - contains all the elements provided as children: * [element1Name] (type int32) - binds to render order of that element within the bound ramses::RenderGroup * ... * [elementNName] (type int32) - * The property name for each element can be specified when adding the element using #ramses::RamsesRenderGroupBindingElements::addElement. + * The property name for each element can be specified when adding the element using #ramses::RenderGroupBindingElements::addElement. * Note that the order of the render order input properties exactly matches the order of adding elements - * via #ramses::RamsesRenderGroupBindingElements::addElement. + * via #ramses::RenderGroupBindingElements::addElement. * * The render order is directly forwarded to Ramses and follows Ramses behavior of ordering, * i.e. element with lower render order value gets rendered before element with higher value. @@ -61,14 +61,14 @@ namespace ramses * * The initial render order values of the input properties are taken from the bound ramses::RenderGroup's elements provided during construction. * - * The #RamsesRenderGroupBinding class has no output properties (thus #ramses::LogicNode::getOutputs() will return nullptr) because + * The #RenderGroupBinding class has no output properties (thus #ramses::LogicNode::getOutputs() will return nullptr) because * the outputs are implicitly forwarded to the bound ramses::RenderGroup. * * The changes via binding objects are applied to the bound object right away when calling ramses::LogicEngine::update(), * however keep in mind that Ramses has a mechanism for bundling scene changes and applying them at once using ramses::Scene::flush, * so the changes will be applied all the way only after calling this method on the scene. */ - class RamsesRenderGroupBinding : public RamsesBinding + class RAMSES_API RenderGroupBinding : public RamsesBinding { public: /** @@ -76,25 +76,37 @@ namespace ramses * * @return the bound ramses render group */ - [[nodiscard]] RAMSES_API const ramses::RenderGroup& getRamsesRenderGroup() const; + [[nodiscard]] const ramses::RenderGroup& getRamsesRenderGroup() const; /** * Returns the bound ramses render group. * * @return the bound ramses render group */ - [[nodiscard]] RAMSES_API ramses::RenderGroup& getRamsesRenderGroup(); + [[nodiscard]] ramses::RenderGroup& getRamsesRenderGroup(); - /// Implementation detail of RamsesRenderGroupBinding - internal::RamsesRenderGroupBindingImpl& m_renderGroupBinding; + /** + * Get the internal data for implementation specifics of RenderGroupBinding. + */ + [[nodiscard]] internal::RenderGroupBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderGroupBinding. + */ + [[nodiscard]] const internal::RenderGroupBindingImpl& impl() const; protected: /** - * Constructor of RamsesRenderGroupBinding. User is not supposed to call this - RamsesRenderGroupBindings are created by other factory classes + * Constructor of RenderGroupBinding. User is not supposed to call this - RenderGroupBindings are created by other factory classes * - * @param impl implementation details of the RamsesRenderGroupBinding + * @param impl implementation details of the RenderGroupBinding */ - explicit RamsesRenderGroupBinding(std::unique_ptr impl) noexcept; + explicit RenderGroupBinding(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of RenderGroupBinding + */ + internal::RenderGroupBindingImpl& m_renderGroupBinding; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/RamsesRenderGroupBindingElements.h b/include/ramses/client/logic/RenderGroupBindingElements.h similarity index 50% rename from client/logic/include/ramses-logic/RamsesRenderGroupBindingElements.h rename to include/ramses/client/logic/RenderGroupBindingElements.h index 5121b8607..d664f1acb 100644 --- a/client/logic/include/ramses-logic/RamsesRenderGroupBindingElements.h +++ b/include/ramses/client/logic/RenderGroupBindingElements.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include @@ -20,56 +20,69 @@ namespace ramses namespace ramses::internal { - class RamsesRenderGroupBindingElementsImpl; + class RenderGroupBindingElementsImpl; } namespace ramses { /** - * RamsesRenderGroupBindingElements is a helper class holding a set of references to elements to be provided when constructing #ramses::RamsesRenderGroupBinding. + * RenderGroupBindingElements is a helper class holding a set of references to elements to be provided when constructing #ramses::RenderGroupBinding. * These elements are either ramses::MeshNode or ramses::RenderGroup. * Note that ramses::RenderGroup can contain other (nested) ramses::RenderGroup objects, in such case the parent ramses::RenderGroup corresponds - * to the #ramses::RamsesRenderGroupBinding to be created and the nested ramses::RenderGroup is the element provided here. + * to the #ramses::RenderGroupBinding to be created and the nested ramses::RenderGroup is the element provided here. */ - class RamsesRenderGroupBindingElements + class RAMSES_API RenderGroupBindingElements { public: - /// Constructor of RamsesRenderGroupBindingElements. - RAMSES_API RamsesRenderGroupBindingElements() noexcept; - /// Destructor of RamsesRenderGroupBindingElements. - RAMSES_API ~RamsesRenderGroupBindingElements() noexcept; + /// Constructor of RenderGroupBindingElements. + RenderGroupBindingElements() noexcept; + /// Destructor of RenderGroupBindingElements. + ~RenderGroupBindingElements() noexcept; /// Copy constructor - RAMSES_API RamsesRenderGroupBindingElements(const RamsesRenderGroupBindingElements& other); + RenderGroupBindingElements(const RenderGroupBindingElements& other); /// Move constructor - RAMSES_API RamsesRenderGroupBindingElements(RamsesRenderGroupBindingElements&& other) noexcept; + RenderGroupBindingElements(RenderGroupBindingElements&& other) noexcept; /// Assignment operator - RAMSES_API RamsesRenderGroupBindingElements& operator=(const RamsesRenderGroupBindingElements& other); + RenderGroupBindingElements& operator=(const RenderGroupBindingElements& other); /// Move assignment operator - RAMSES_API RamsesRenderGroupBindingElements& operator=(RamsesRenderGroupBindingElements&& other) noexcept; + RenderGroupBindingElements& operator=(RenderGroupBindingElements&& other) noexcept; /** - * Add ramses::MeshNode element to control its render order when provided to #ramses::RamsesRenderGroupBinding. + * Add ramses::MeshNode element to control its render order when provided to #ramses::RenderGroupBinding. * Will fail if given element is already contained. * * @param meshNode ramses::MeshNode element to add to be exposed for render order control. - * @param elementName This name will be used to name the input property in the created #ramses::RamsesRenderGroupBinding. + * @param elementName This name will be used to name the input property in the created #ramses::RenderGroupBinding. * If none provided, name of the ramses::MeshNode will be used. * @return \c true if successful, \c false otherwise. */ - RAMSES_API bool addElement(const ramses::MeshNode& meshNode, std::string_view elementName = {}); + bool addElement(const ramses::MeshNode& meshNode, std::string_view elementName = {}); /** - * Add ramses::RenderGroup element to control its render order when provided to #ramses::RamsesRenderGroupBinding. + * Add ramses::RenderGroup element to control its render order when provided to #ramses::RenderGroupBinding. * Will fail if given element is already contained. * * @param nestedRenderGroup ramses::RenderGroup element to add to be exposed for render order control. - * @param elementName This name will be used to name the input property in the created #ramses::RamsesRenderGroupBinding. + * @param elementName This name will be used to name the input property in the created #ramses::RenderGroupBinding. * If none provided, name of the ramses::RenderGroup will be used. * @return \c true if successful, \c false otherwise. */ - RAMSES_API bool addElement(const ramses::RenderGroup& nestedRenderGroup, std::string_view elementName = {}); + bool addElement(const ramses::RenderGroup& nestedRenderGroup, std::string_view elementName = {}); - /// Implementation detail of RamsesRenderGroupBindingElements - std::unique_ptr m_impl; + /** + * Get the internal data for implementation specifics of RenderGroupBindingElements. + */ + [[nodiscard]] internal::RenderGroupBindingElementsImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderGroupBindingElements. + */ + [[nodiscard]] const internal::RenderGroupBindingElementsImpl& impl() const; + + protected: + /** + * Implementation detail of RenderGroupBindingElements + */ + std::unique_ptr m_impl; }; } diff --git a/client/logic/include/ramses-logic/RamsesRenderPassBinding.h b/include/ramses/client/logic/RenderPassBinding.h similarity index 57% rename from client/logic/include/ramses-logic/RamsesRenderPassBinding.h rename to include/ramses/client/logic/RenderPassBinding.h index 1aafe7caf..c9e8428df 100644 --- a/client/logic/include/ramses-logic/RamsesRenderPassBinding.h +++ b/include/ramses/client/logic/RenderPassBinding.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/RamsesBinding.h" namespace ramses { @@ -18,18 +18,18 @@ namespace ramses namespace ramses::internal { - class RamsesRenderPassBindingImpl; + class RenderPassBindingImpl; } namespace ramses { /** - * The RamsesRenderPassBinding binds to a Ramses object instance like other types derived from + * The RenderPassBinding binds to a Ramses object instance like other types derived from * #ramses::RamsesBinding, in this case it binds to a ramses::RenderPass. - * RamsesRenderPassBinding can be created using #ramses::LogicEngine::createRamsesRenderPassBinding + * RenderPassBinding can be created using #ramses::LogicEngine::createRenderPassBinding * after providing an instance of a ramses::RenderPass. * - * The RamsesRenderPassBinding has a fixed set of inputs which correspond to some of the parameters in ramses::RenderPass: + * The RenderPassBinding has a fixed set of inputs which correspond to some of the parameters in ramses::RenderPass: * 'enabled' (type bool) - binds to enable/disable state (see ramses::RenderPass::setEnabled) * 'renderOrder' (type int32) - binds to render order (see ramses::RenderPass::setRenderOrder) * 'clearColor' (type vec4f) - binds to color used when clearing render pass (see ramses::RenderPass::setClearColor) @@ -37,14 +37,14 @@ namespace ramses * * The initial values of the input properties are taken from the bound ramses::RenderPass provided during construction. * - * The #RamsesRenderPassBinding class has no output properties (thus #ramses::LogicNode::getOutputs() will return nullptr) because + * The #RenderPassBinding class has no output properties (thus #ramses::LogicNode::getOutputs() will return nullptr) because * the outputs are implicitly the properties of the bound ramses::RenderPass. * * \rst .. note:: - Not all states of the #ramses::RenderPass must be managed via #RamsesRenderPassBinding, input properties which are not modified - via #RamsesRenderPassBinding (i.e. user never set a value explicitly nor linked the input) will not alter the corresponding Ramses object + Not all states of the #ramses::RenderPass must be managed via #RenderPassBinding, input properties which are not modified + via #RenderPassBinding (i.e. user never set a value explicitly nor linked the input) will not alter the corresponding Ramses object state. This allows the user code to control some states via Ramses logic nodes and some directly using Ramses object. \endrst @@ -53,7 +53,7 @@ namespace ramses * however keep in mind that Ramses has a mechanism for bundling scene changes and applying them at once using ramses::Scene::flush, * so the changes will be applied all the way only after calling this method on the scene. */ - class RamsesRenderPassBinding : public RamsesBinding + class RAMSES_API RenderPassBinding : public RamsesBinding { public: /** @@ -61,25 +61,37 @@ namespace ramses * * @return the bound ramses render pass */ - [[nodiscard]] RAMSES_API const ramses::RenderPass& getRamsesRenderPass() const; + [[nodiscard]] const ramses::RenderPass& getRamsesRenderPass() const; /** * Returns the bound ramses render pass. * * @return the bound ramses render pass */ - [[nodiscard]] RAMSES_API ramses::RenderPass& getRamsesRenderPass(); + [[nodiscard]] ramses::RenderPass& getRamsesRenderPass(); - /// Implementation detail of RamsesRenderPassBinding - internal::RamsesRenderPassBindingImpl& m_renderPassBinding; + /** + * Get the internal data for implementation specifics of RenderPassBinding. + */ + [[nodiscard]] internal::RenderPassBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of RenderPassBinding. + */ + [[nodiscard]] const internal::RenderPassBindingImpl& impl() const; protected: /** - * Constructor of RamsesRenderPassBinding. User is not supposed to call this - RamsesRenderPassBindings are created by other factory classes + * Constructor of RenderPassBinding. User is not supposed to call this - RenderPassBindings are created by other factory classes * - * @param impl implementation details of the RamsesRenderPassBinding + * @param impl implementation details of the RenderPassBinding */ - explicit RamsesRenderPassBinding(std::unique_ptr impl) noexcept; + explicit RenderPassBinding(std::unique_ptr impl) noexcept; + + /** + * Implementation detail of RenderPassBinding + */ + internal::RenderPassBindingImpl& m_renderPassBinding; friend class internal::ApiObjects; }; diff --git a/client/logic/include/ramses-logic/SkinBinding.h b/include/ramses/client/logic/SkinBinding.h similarity index 77% rename from client/logic/include/ramses-logic/SkinBinding.h rename to include/ramses/client/logic/SkinBinding.h index 0d009a73a..d0d2148d5 100644 --- a/client/logic/include/ramses-logic/SkinBinding.h +++ b/include/ramses/client/logic/SkinBinding.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/APIExport.h" -#include "ramses-logic/RamsesBinding.h" +#include "ramses/framework/APIExport.h" +#include "ramses/client/logic/RamsesBinding.h" #include namespace ramses @@ -24,8 +24,8 @@ namespace ramses::internal namespace ramses { - class RamsesNodeBinding; - class RamsesAppearanceBinding; + class NodeBinding; + class AppearanceBinding; /** * #SkinBinding is a special kind of binding which holds all the data needed to calculate vertex skinning matrices @@ -35,7 +35,7 @@ namespace ramses * as part of the skeleton structure. * - inverse bind matrices - inverse transformation matrix for each joint node, these are static for the lifecycle * of the #SkinBinding. - * - #ramses::RamsesAppearanceBinding - binds to ramses::Appearance with an ramses::Effect specifying a vertex shader + * - #ramses::AppearanceBinding - binds to ramses::Appearance with an ramses::Effect specifying a vertex shader * which is expected to apply the final stage of vertex skinning calculation using the joint matrices calculated * in this #SkinBinding. * - ramses::UniformInput - specifies a vertex shader input (interface to the shader) where the joint matrices @@ -60,7 +60,7 @@ namespace ramses * however keep in mind that Ramses has a mechanism for bundling scene changes and applying them at once using ramses::Scene::flush, * so the changes will be applied all the way only after calling this method on the ramses scene. */ - class SkinBinding : public RamsesBinding + class RAMSES_API SkinBinding : public RamsesBinding { public: /** @@ -68,26 +68,38 @@ namespace ramses * * @return the appearance binding that this skin is bound to */ - [[nodiscard]] RAMSES_API const RamsesAppearanceBinding& getAppearanceBinding() const; + [[nodiscard]] const AppearanceBinding& getAppearanceBinding() const; /** * Returns the uniform input used to set joint matrices to the bound appearance. * * @return the uniform input used to set joint matrices to the bound appearance */ - [[nodiscard]] RAMSES_API const ramses::UniformInput& getAppearanceUniformInput() const; + [[nodiscard]] const ramses::UniformInput& getAppearanceUniformInput() const; - /// Implementation detail of SkinBinding - internal::SkinBindingImpl& m_skinBinding; + /** + * Get the internal data for implementation specifics of SkinBinding. + */ + [[nodiscard]] internal::SkinBindingImpl& impl(); + + /** + * Get the internal data for implementation specifics of SkinBinding. + */ + [[nodiscard]] const internal::SkinBindingImpl& impl() const; protected: /** - * Constructor of SkinBinding. User is not supposed to call this - SkinBindings are created by other factory classes - * - * @param impl implementation details of the SkinBinding - */ + * Constructor of SkinBinding. User is not supposed to call this - SkinBindings are created by other factory classes + * + * @param impl implementation details of the SkinBinding + */ explicit SkinBinding(std::unique_ptr impl) noexcept; + /** + * Implementation detail of SkinBinding + */ + internal::SkinBindingImpl& m_skinBinding; + friend class internal::ApiObjects; }; } diff --git a/client/logic/include/ramses-logic/TimerNode.h b/include/ramses/client/logic/TimerNode.h similarity index 87% rename from client/logic/include/ramses-logic/TimerNode.h rename to include/ramses/client/logic/TimerNode.h index 37db0658e..f65cc6f74 100644 --- a/client/logic/include/ramses-logic/TimerNode.h +++ b/include/ramses/client/logic/TimerNode.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicNode.h" +#include "ramses/client/logic/LogicNode.h" #include namespace ramses::internal @@ -37,13 +37,18 @@ namespace ramses * Note that unlike other logic nodes a TimerNode is always updated on every #ramses::LogicEngine::update call regardless of if any of its * inputs were modified or not. */ - class TimerNode : public LogicNode + class RAMSES_API TimerNode : public LogicNode { public: /** - * Implementation of TimerNode - */ - internal::TimerNodeImpl& m_timerNodeImpl; + * Get the internal data for implementation specifics of TimerNode. + */ + [[nodiscard]] internal::TimerNodeImpl& impl(); + + /** + * Get the internal data for implementation specifics of TimerNode. + */ + [[nodiscard]] const internal::TimerNodeImpl& impl() const; protected: /** @@ -53,6 +58,11 @@ namespace ramses */ explicit TimerNode(std::unique_ptr impl) noexcept; + /** + * Implementation of TimerNode + */ + internal::TimerNodeImpl& m_timerNodeImpl; + friend class internal::ApiObjects; }; } diff --git a/include/ramses/client/ramses-client.h b/include/ramses/client/ramses-client.h new file mode 100644 index 000000000..db0a25f91 --- /dev/null +++ b/include/ramses/client/ramses-client.h @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +/** + * @defgroup CoreAPI The Ramses Core API + * This group contains all of the Ramses Core API types. + */ + +#include "ramses/client/RamsesClient.h" + +// Scene +#include "ramses/client/Scene.h" +#include "ramses/client/Node.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/IClientEventHandler.h" +#include "ramses/client/DataObject.h" + +// Effect +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" + +// Resources +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" + +// Data Buffers +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" + +// Iterators +#include "ramses/client/SceneIterator.h" +#include "ramses/client/RenderPassGroupIterator.h" +#include "ramses/client/RenderGroupMeshIterator.h" +#include "ramses/client/SceneGraphIterator.h" +#include "ramses/client/SceneObjectIterator.h" diff --git a/client/ramses-client-api/include/ramses-utils.h b/include/ramses/client/ramses-utils.h similarity index 77% rename from client/ramses-client-api/include/ramses-utils.h rename to include/ramses/client/ramses-utils.h index ec040d42e..88282b1d0 100644 --- a/client/ramses-client-api/include/ramses-utils.h +++ b/include/ramses/client/ramses-utils.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSES_UTILS_H -#define RAMSES_RAMSES_UTILS_H +#pragma once -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/APIExport.h" #include #include @@ -41,27 +40,8 @@ namespace ramses * @brief Temporary functions for convenience. All of these can be implemented on top * of the RAMSES Client API, but are offered here as convenience. */ - class RAMSES_API RamsesUtils + namespace RamsesUtils { - public: - /** - * @brief Converts object to a compatible object type. - * Object can be converted to any of its base classes. - * Eg. MeshNode can be converted to Node, SceneObject, ClientObject or RamsesObject. - * - * @param[in] obj RamsesObject to convert. - * @return Pointer to an object of a specific type, - * NULL if object type is not compatible with desired object class. - */ - template - static const T* TryConvert(const RamsesObject& obj); - - /** - * @copydoc TryConvert() - **/ - template - static T* TryConvert(RamsesObject& obj); - /** * @brief Creates a Texture from the given png file. * @@ -71,7 +51,7 @@ namespace ramses * @param[in] name Name for the created texture * @return Created texture object or nullptr on error */ - static Texture2D* CreateTextureResourceFromPng(const char* pngFilePath, Scene& scene, const TextureSwizzle& swizzle = {}, std::string_view name = {}); + RAMSES_API Texture2D* CreateTextureResourceFromPng(const char* pngFilePath, Scene& scene, const TextureSwizzle& swizzle = {}, std::string_view name = {}); /** * @brief Creates a Texture from the given png memory buffer. @@ -82,7 +62,7 @@ namespace ramses * @param[in] name Name for the created texture * @return Created texture object or nullptr on error */ - static Texture2D* CreateTextureResourceFromPngBuffer(const std::vector& pngData, Scene& scene, const TextureSwizzle& swizzle = {}, std::string_view name = {}); + RAMSES_API Texture2D* CreateTextureResourceFromPngBuffer(const std::vector& pngData, Scene& scene, const TextureSwizzle& swizzle = {}, std::string_view name = {}); /** * @brief Generate mip maps from original texture 2D data. You obtain ownership of all the @@ -98,7 +78,7 @@ namespace ramses * only the original mip map level is part of the result. * You are responsible to destroy the generated data, e.g. by using RamsesUtils::DeleteGeneratedMipMaps */ - static MipLevelData* GenerateMipMapsTexture2D(uint32_t width, uint32_t height, uint8_t bytesPerPixel, uint8_t* data, size_t& mipMapCount); + RAMSES_API MipLevelData* GenerateMipMapsTexture2D(uint32_t width, uint32_t height, uint8_t bytesPerPixel, std::byte* data, size_t& mipMapCount); /** * @brief Creates a png from image data, e.g. data generated by RamsesClientService::readPixels. @@ -112,7 +92,7 @@ namespace ramses * @param[in] height Height of the image * @return Success of png creation */ - static bool SaveImageBufferToPng(const std::string& filePath, const std::vector& imageData, uint32_t width, uint32_t height); + RAMSES_API bool SaveImageBufferToPng(const std::string& filePath, const std::vector& imageData, uint32_t width, uint32_t height); /** * @brief Creates a png from image data, e.g. data generated by RamsesClientService::readPixels. @@ -131,7 +111,7 @@ namespace ramses * @param[in] flipImageBufferVertically Vertical Flipping of image data * @return Success of png creation */ - static bool SaveImageBufferToPng(const std::string& filePath, std::vector& imageData, uint32_t width, uint32_t height, bool flipImageBufferVertically); + RAMSES_API bool SaveImageBufferToPng(const std::string& filePath, std::vector& imageData, uint32_t width, uint32_t height, bool flipImageBufferVertically); /** * @brief Generate mip maps from original texture cube data. You obtain ownership of all the @@ -147,21 +127,21 @@ namespace ramses * only the original mip map level is part of the result. * You are responsible to destroy the generated data, e.g. using RamsesUtils::DeleteGeneratedMipMaps */ - static CubeMipLevelData* GenerateMipMapsTextureCube(uint32_t faceWidth, uint32_t faceHeight, uint8_t bytesPerPixel, uint8_t* data, size_t& mipMapCount); + RAMSES_API CubeMipLevelData* GenerateMipMapsTextureCube(uint32_t faceWidth, uint32_t faceHeight, uint8_t bytesPerPixel, std::byte* data, size_t& mipMapCount); /** * @brief Deletes mip map data created with RamsesUtils::GenerateMipMapsTexture2D. * @param[in, out] data Generated mip map data. * @param[in] mipMapCount Number of mip map levels in the generated data. */ - static void DeleteGeneratedMipMaps(MipLevelData*& data, size_t mipMapCount); + RAMSES_API void DeleteGeneratedMipMaps(MipLevelData*& data, size_t mipMapCount); /** * @brief Deletes mip map data created with RamsesUtils::GenerateMipMapsTextureCube. * @param[in, out] data Generated mip map data. * @param[in] mipMapCount Number of mip map levels in the generated data. */ - static void DeleteGeneratedMipMaps(CubeMipLevelData*& data, size_t mipMapCount); + RAMSES_API void DeleteGeneratedMipMaps(CubeMipLevelData*& data, size_t mipMapCount); /** * @brief Returns the identifier of a node, which is printed in the renderer logs. The identifier is guaranteed to be @@ -170,7 +150,7 @@ namespace ramses * @param[in] node The node * @return the identifier of the given node. */ - static nodeId_t GetNodeId(const Node& node); + RAMSES_API nodeId_t GetNodeId(const Node& node); /** * @brief Convenience method to set perspective camera frustum using FOV and aspect ratio @@ -192,7 +172,7 @@ namespace ramses * @param[in] nearFarPlanesData Data object where resulting near/far frustum planes will be set to, must be created with #ramses::EDataType::Vector2F. * @return True for success, false otherwise. */ - static bool SetPerspectiveCameraFrustumToDataObjects(float fov, float aspectRatio, float nearPlane, float farPlane, DataObject& frustumPlanesData, DataObject& nearFarPlanesData); + RAMSES_API bool SetPerspectiveCameraFrustumToDataObjects(float fov, float aspectRatio, float nearPlane, float farPlane, DataObject& frustumPlanesData, DataObject& nearFarPlanesData); /** * @brief Convenience wrapper for RamsesClient::loadSceneFromMemory with automatic deleter @@ -209,11 +189,11 @@ namespace ramses * @param[in] client The ramses client to use for loading the scene * @param[in] data Memory buffer with scene data. * @param[in] size Size in bytes of the scene data within data buffer - * @param[in] localOnly Mark for local only optimization + * @param[in] config optional configuration object to override default behavior, see #ramses::SceneConfig for details * @return The loaded ramses Scene or nullptr on error * */ - static Scene* LoadSceneFromMemory(RamsesClient& client, std::unique_ptr data, size_t size, bool localOnly); // NOLINT(modernize-avoid-c-arrays) + RAMSES_API Scene* LoadSceneFromMemory(RamsesClient& client, std::unique_ptr data, size_t size, const SceneConfig& config = {}); // NOLINT(modernize-avoid-c-arrays) /** * @brief Dumps all objects of a scene which do not contribute to the visual appearance of the scene on screen. @@ -223,7 +203,7 @@ namespace ramses * * @param[in] scene the source scene */ - static void DumpUnrequiredSceneObjects(const Scene& scene); + RAMSES_API void DumpUnrequiredSceneObjects(const Scene& scene); /** * @brief As #DumpUnrequiredSceneObjects but write to given stream. @@ -231,16 +211,14 @@ namespace ramses * @param[in] scene the source scene * @param[out] out stream to write to */ - static void DumpUnrequiredSceneObjectsToFile(const Scene& scene, std::ostream& out); - }; + RAMSES_API void DumpUnrequiredSceneObjectsToFile(const Scene& scene, std::ostream& out); + } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - inline Scene* RamsesUtils::LoadSceneFromMemory(RamsesClient& client, std::unique_ptr data, size_t size, bool localOnly) + inline Scene* RamsesUtils::LoadSceneFromMemory(RamsesClient& client, std::unique_ptr data, size_t size, const SceneConfig& config) { // NOLINTNEXTLINE(modernize-avoid-c-arrays) - std::unique_ptr dataWithDeleter(data.release(), [](const unsigned char* ptr) { delete[] ptr; }); - return client.loadSceneFromMemory(std::move(dataWithDeleter), size, localOnly); + std::unique_ptr dataWithDeleter(data.release(), [](const auto* ptr) { delete[] ptr; }); + return client.loadSceneFromMemory(std::move(dataWithDeleter), size, config); } } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/FontCascade.h b/include/ramses/client/text/FontCascade.h similarity index 94% rename from client/ramses-text-api/include/ramses-text-api/FontCascade.h rename to include/ramses/client/text/FontCascade.h index 3f418a7a8..c5d53d0e6 100644 --- a/client/ramses-text-api/include/ramses-text-api/FontCascade.h +++ b/include/ramses/client/text/FontCascade.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FONTCASCADE_H -#define RAMSES_FONTCASCADE_H +#pragma once -#include "ramses-text-api/FontInstanceId.h" -#include "ramses-text-api/FontInstanceOffsets.h" +#include "ramses/client/text/FontInstanceId.h" +#include "ramses/client/text/FontInstanceOffsets.h" #include #include @@ -62,5 +61,3 @@ namespace ramses }; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/FontInstanceId.h b/include/ramses/client/text/FontInstanceId.h similarity index 90% rename from client/ramses-text-api/include/ramses-text-api/FontInstanceId.h rename to include/ramses/client/text/FontInstanceId.h index bb7643afd..95804bb08 100644 --- a/client/ramses-text-api/include/ramses-text-api/FontInstanceId.h +++ b/include/ramses/client/text/FontInstanceId.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FONTINSTANCEID_H -#define RAMSES_FONTINSTANCEID_H +#pragma once -#include "ramses-framework-api/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" #include #include @@ -38,5 +37,3 @@ namespace ramses */ using FontInstanceId = StronglyTypedValue::max(), FontInstanceIdTag>; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/FontInstanceOffsets.h b/include/ramses/client/text/FontInstanceOffsets.h similarity index 90% rename from client/ramses-text-api/include/ramses-text-api/FontInstanceOffsets.h rename to include/ramses/client/text/FontInstanceOffsets.h index 144f48946..f8f4360df 100644 --- a/client/ramses-text-api/include/ramses-text-api/FontInstanceOffsets.h +++ b/include/ramses/client/text/FontInstanceOffsets.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FONTINSTANCEOFFSETS_H -#define RAMSES_FONTINSTANCEOFFSETS_H +#pragma once -#include "ramses-text-api/FontInstanceId.h" +#include "ramses/client/text/FontInstanceId.h" #include namespace ramses @@ -34,5 +33,3 @@ namespace ramses /// Vector of FontInstanceOffset elements using FontInstanceOffsets = std::vector; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/FontRegistry.h b/include/ramses/client/text/FontRegistry.h similarity index 95% rename from client/ramses-text-api/include/ramses-text-api/FontRegistry.h rename to include/ramses/client/text/FontRegistry.h index fb6d803e1..2a42ddc4b 100644 --- a/client/ramses-text-api/include/ramses-text-api/FontRegistry.h +++ b/include/ramses/client/text/FontRegistry.h @@ -6,16 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FONTREGISTRY_H -#define RAMSES_FONTREGISTRY_H +#pragma once -#include "ramses-text-api/IFontAccessor.h" -#include "ramses-text-api/IFontInstance.h" +#include "ramses/client/text/IFontAccessor.h" +#include "ramses/client/text/IFontInstance.h" #include namespace ramses { + namespace internal + { + class FontRegistryImpl; + } + /** * @ingroup TextAPI * @brief Font registry can be used to load Freetype2 fonts and create font instances (optionally with Harfbuzz). @@ -107,7 +111,7 @@ namespace ramses /** * Stores internal data for implementation specifics of FontRegistry. */ - class FontRegistryImpl& impl; + class internal::FontRegistryImpl& impl; /** * @brief Deleted copy constructor @@ -123,5 +127,3 @@ namespace ramses FontRegistry& operator=(const FontRegistry& other) = delete; }; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/Glyph.h b/include/ramses/client/text/Glyph.h similarity index 95% rename from client/ramses-text-api/include/ramses-text-api/Glyph.h rename to include/ramses/client/text/Glyph.h index a3af7c13d..f4ab82a23 100644 --- a/client/ramses-text-api/include/ramses-text-api/Glyph.h +++ b/include/ramses/client/text/Glyph.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLYPH_H -#define RAMSES_GLYPH_H +#pragma once -#include "ramses-text-api/FontInstanceId.h" -#include "ramses-framework-api/StronglyTypedValue.h" +#include "ramses/client/text/FontInstanceId.h" +#include "ramses/framework/StronglyTypedValue.h" #include #include #include @@ -104,5 +103,3 @@ namespace std } }; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/GlyphMetrics.h b/include/ramses/client/text/GlyphMetrics.h similarity index 87% rename from client/ramses-text-api/include/ramses-text-api/GlyphMetrics.h rename to include/ramses/client/text/GlyphMetrics.h index 37eec33bb..125936faa 100644 --- a/client/ramses-text-api/include/ramses-text-api/GlyphMetrics.h +++ b/include/ramses/client/text/GlyphMetrics.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLYPHMETRICS_H -#define RAMSES_GLYPHMETRICS_H +#pragma once -#include "ramses-text-api/Glyph.h" +#include "ramses/client/text/Glyph.h" namespace ramses { @@ -28,19 +27,17 @@ namespace ramses /// Glyph key (GlyphId + FontInstanceId) GlyphKey key; /// Glyph width - uint32_t width; + uint32_t width = 0u; /// Glyph height - uint32_t height; + uint32_t height = 0u; /// Glyph offset on x-axis - int32_t posX; + int32_t posX = 0; /// Glyph offset on y-axis - int32_t posY; + int32_t posY = 0; /// Glyph advance (distance from origin of this glyph to where origin of next glyph should start) - int32_t advance; + int32_t advance = 0; }; /// Vector of GlyphMetrics elements using GlyphMetricsVector = std::vector; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/IFontAccessor.h b/include/ramses/client/text/IFontAccessor.h similarity index 92% rename from client/ramses-text-api/include/ramses-text-api/IFontAccessor.h rename to include/ramses/client/text/IFontAccessor.h index 8c4b825f1..bb9183b80 100644 --- a/client/ramses-text-api/include/ramses-text-api/IFontAccessor.h +++ b/include/ramses/client/text/IFontAccessor.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IFONTACCESSOR_H -#define RAMSES_IFONTACCESSOR_H +#pragma once -#include "ramses-text-api/FontInstanceId.h" +#include "ramses/client/text/FontInstanceId.h" namespace ramses { @@ -40,5 +39,3 @@ namespace ramses [[nodiscard]] virtual IFontInstance* getFontInstance(FontInstanceId fontInstanceId) const = 0; }; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/IFontInstance.h b/include/ramses/client/text/IFontInstance.h similarity index 93% rename from client/ramses-text-api/include/ramses-text-api/IFontInstance.h rename to include/ramses/client/text/IFontInstance.h index 261dc51c2..6216af657 100644 --- a/client/ramses-text-api/include/ramses-text-api/IFontInstance.h +++ b/include/ramses/client/text/IFontInstance.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXT_IFONTINSTANCE_H -#define RAMSES_TEXT_IFONTINSTANCE_H +#pragma once -#include "ramses-text-api/GlyphMetrics.h" +#include "ramses/client/text/GlyphMetrics.h" #include #include #include @@ -39,7 +38,7 @@ namespace ramses * @brief Get all characters that are supported (mapped to a glyph) by IFontInstance * @return set with all supported UTF32 char codes */ - virtual std::unordered_set getAllSupportedCharacters() = 0; + [[nodiscard]] virtual std::unordered_set getAllSupportedCharacters() = 0; /** * @brief Get the line height of the font instance @@ -77,5 +76,3 @@ namespace ramses virtual GlyphData loadGlyphBitmapData(GlyphId glyphId, uint32_t& sizeX, uint32_t& sizeY) = 0; }; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/LayoutUtils.h b/include/ramses/client/text/LayoutUtils.h similarity index 90% rename from client/ramses-text-api/include/ramses-text-api/LayoutUtils.h rename to include/ramses/client/text/LayoutUtils.h index af778ba47..a7a42ca4e 100644 --- a/client/ramses-text-api/include/ramses-text-api/LayoutUtils.h +++ b/include/ramses/client/text/LayoutUtils.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LAYOUTUTILS_H -#define RAMSES_LAYOUTUTILS_H +#pragma once -#include "ramses-text-api/GlyphMetrics.h" +#include "ramses/client/text/GlyphMetrics.h" namespace ramses { @@ -30,15 +29,15 @@ namespace ramses struct StringBoundingBox { /// X axis offset of bounding box from origin - int32_t offsetX; + int32_t offsetX = 0; /// Y axis offset of bounding box from origin - int32_t offsetY; + int32_t offsetY = 0; /// Bounding box width - uint32_t width; + uint32_t width = 0; /// Bounding box height - uint32_t height; + uint32_t height = 0; /// Advance to next StringBoundingBox - int32_t combinedAdvance; + int32_t combinedAdvance = 0; }; /** @@ -57,5 +56,3 @@ namespace ramses RAMSES_API StringBoundingBox GetBoundingBoxForString(const GlyphMetricsVector::const_reverse_iterator& first, const GlyphMetricsVector::const_reverse_iterator& last); } } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/TextCache.h b/include/ramses/client/text/TextCache.h similarity index 96% rename from client/ramses-text-api/include/ramses-text-api/TextCache.h rename to include/ramses/client/text/TextCache.h index e966208f2..a0e13c924 100644 --- a/client/ramses-text-api/include/ramses-text-api/TextCache.h +++ b/include/ramses/client/text/TextCache.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTCACHE_H -#define RAMSES_TEXTCACHE_H +#pragma once -#include "ramses-text-api/TextLine.h" -#include "ramses-text-api/FontInstanceOffsets.h" +#include "ramses/client/text/TextLine.h" +#include "ramses/client/text/FontInstanceOffsets.h" #include namespace ramses @@ -18,7 +17,11 @@ namespace ramses class Scene; class Effect; class IFontAccessor; - class TextCacheImpl; + + namespace internal + { + class TextCacheImpl; + } /** * @ingroup TextAPI @@ -38,13 +41,13 @@ namespace ramses * - A texture sampler with bilinear filtering (to blur out edges) * * The scene objects created for each text line are: - * - a GeometryBinding with Vertex/Index arrays (holding a list of quads for each glyph in the text line) + * - a Geometry with Vertex/Index arrays (holding a list of quads for each glyph in the text line) * - an Appearance (based off the effect provided in createTextLine()). The appearance will have it's semantic texture sampler * pointing to the texture atlas page where the text line's glyphs are located * - a MeshNode which holds above objects together for rendering * * It's important to note that the Appearance of each TextLine has a reference to the texture page holding its - * glyph data, and the GeometryBinding has links to the texture quad data generated by TextCache. We highly recommend not to + * glyph data, and the Geometry has links to the texture quad data generated by TextCache. We highly recommend not to * tamper with these objects, unless you have in-depth understanding of how text rendering works and know what you are * doing. However, setting custom uniforms in the Appearance is valid, as long as you are not touching the semantic texture * sampler which was provided when creating the text line. @@ -203,7 +206,7 @@ namespace ramses /** * Stores internal data for implementation specifics of TextCache. */ - class TextCacheImpl* impl; + class internal::TextCacheImpl* impl; /** * @brief Deleted copy constructor @@ -219,5 +222,3 @@ namespace ramses TextCache& operator=(const TextCache& other) = delete; }; } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/TextLine.h b/include/ramses/client/text/TextLine.h similarity index 94% rename from client/ramses-text-api/include/ramses-text-api/TextLine.h rename to include/ramses/client/text/TextLine.h index 1a48907be..11b571a78 100644 --- a/client/ramses-text-api/include/ramses-text-api/TextLine.h +++ b/include/ramses/client/text/TextLine.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTLINE_H -#define RAMSES_TEXTLINE_H +#pragma once -#include "ramses-text-api/GlyphMetrics.h" +#include "ramses/client/text/GlyphMetrics.h" #include namespace ramses @@ -50,5 +49,3 @@ namespace ramses static_assert(std::is_move_constructible::value, "TextLine must be movable"); static_assert(std::is_move_assignable::value, "TextLine must be movable"); } - -#endif diff --git a/client/ramses-text-api/include/ramses-text-api/UtfUtils.h b/include/ramses/client/text/UtfUtils.h similarity index 97% rename from client/ramses-text-api/include/ramses-text-api/UtfUtils.h rename to include/ramses/client/text/UtfUtils.h index 04d5ed21c..ec5049bcf 100644 --- a/client/ramses-text-api/include/ramses-text-api/UtfUtils.h +++ b/include/ramses/client/text/UtfUtils.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UTFUTILS_H -#define RAMSES_UTFUTILS_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include @@ -80,5 +79,3 @@ namespace ramses RAMSES_API ExtractedUnicodePoint ExtractUnicodePointFromUTF16(std::u16string::const_iterator strBegin, std::u16string::const_iterator strEnd); } } - -#endif diff --git a/client/ramses-text-api/include/ramses-text.h b/include/ramses/client/text/ramses-text.h similarity index 53% rename from client/ramses-text-api/include/ramses-text.h rename to include/ramses/client/text/ramses-text.h index 91487e6c0..9c148cf18 100644 --- a/client/ramses-text-api/include/ramses-text.h +++ b/include/ramses/client/text/ramses-text.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSES_TEXT_H -#define RAMSES_RAMSES_TEXT_H +#pragma once /** * @defgroup TextAPI The Ramses Text API * This group contains all of the Ramses Text API types. */ -#include "ramses-text-api/FontInstanceId.h" -#include "ramses-text-api/FontInstanceOffsets.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text-api/Glyph.h" -#include "ramses-text-api/GlyphMetrics.h" -#include "ramses-text-api/IFontAccessor.h" -#include "ramses-text-api/IFontInstance.h" -#include "ramses-text-api/TextCache.h" -#include "ramses-text-api/TextLine.h" -#include "ramses-text-api/UtfUtils.h" -#include "ramses-text-api/LayoutUtils.h" - -#endif +#include "ramses/client/text/FontInstanceId.h" +#include "ramses/client/text/FontInstanceOffsets.h" +#include "ramses/client/text/FontRegistry.h" +#include "ramses/client/text/Glyph.h" +#include "ramses/client/text/GlyphMetrics.h" +#include "ramses/client/text/IFontAccessor.h" +#include "ramses/client/text/IFontInstance.h" +#include "ramses/client/text/TextCache.h" +#include "ramses/client/text/TextLine.h" +#include "ramses/client/text/UtfUtils.h" +#include "ramses/client/text/LayoutUtils.h" diff --git a/framework/ramses-framework-api/include/ramses-framework-api/APIExport.h b/include/ramses/framework/APIExport.h similarity index 94% rename from framework/ramses-framework-api/include/ramses-framework-api/APIExport.h rename to include/ramses/framework/APIExport.h index 78cf0d853..32314906e 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/APIExport.h +++ b/include/ramses/framework/APIExport.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_APIEXPORT_H -#define RAMSES_APIEXPORT_H +#pragma once #ifndef RAMSES_API @@ -32,5 +31,3 @@ #endif #endif - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/AppearanceEnums.h b/include/ramses/framework/AppearanceEnums.h similarity index 97% rename from framework/ramses-framework-api/include/ramses-framework-api/AppearanceEnums.h rename to include/ramses/framework/AppearanceEnums.h index e3d6a0694..df2257091 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/AppearanceEnums.h +++ b/include/ramses/framework/AppearanceEnums.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_APPEARANCEENUMS_H -#define RAMSES_APPEARANCEENUMS_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include namespace ramses @@ -231,5 +230,3 @@ namespace ramses * @} */ } - -#endif //RAMSES_APPEARANCEENUMS_H diff --git a/framework/ramses-framework-api/include/ramses-framework-api/DataTypes.h b/include/ramses/framework/DataTypes.h similarity index 94% rename from framework/ramses-framework-api/include/ramses-framework-api/DataTypes.h rename to include/ramses/framework/DataTypes.h index cd766e170..d23508408 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/DataTypes.h +++ b/include/ramses/framework/DataTypes.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-framework-api/EDataType.h" +#include "ramses/framework/EDataType.h" #include #include #include "glm/mat4x4.hpp" @@ -30,8 +30,6 @@ namespace ramses using matrix44f = glm::mat4; using quat = glm::quat; - using Byte = unsigned char; - /** * @brief Static query of data type compatibility with uniform inputs (e.g. #ramses::Appearance::setInputValue). * @return true if data type can be used with uniform inputs @@ -39,7 +37,8 @@ namespace ramses template constexpr bool IsUniformInputDataType() { using RawType = std::remove_cv_t>; - return std::is_same_v + return std::is_same_v + || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v @@ -64,7 +63,7 @@ namespace ramses || std::is_same_v || std::is_same_v || std::is_same_v - || std::is_same_v; + || std::is_same_v; } /** @@ -84,6 +83,7 @@ namespace ramses case EDataType::Vector4F: case EDataType::ByteBlob: return true; + case EDataType::Bool: case EDataType::Int32: case EDataType::Vector2I: case EDataType::Vector3I: @@ -112,6 +112,7 @@ namespace ramses { switch (dataType) { + case EDataType::Bool: case EDataType::Int32: case EDataType::Float: case EDataType::Vector2F: @@ -169,10 +170,14 @@ namespace ramses { return EDataType::Vector4F; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return EDataType::ByteBlob; } + else if constexpr (std::is_same_v) + { + return EDataType::Bool; + } else if constexpr (std::is_same_v) { return EDataType::Int32; diff --git a/framework/ramses-framework-api/include/ramses-framework-api/EDataType.h b/include/ramses/framework/EDataType.h similarity index 94% rename from framework/ramses-framework-api/include/ramses-framework-api/EDataType.h rename to include/ramses/framework/EDataType.h index d38d9556e..e26cd3f27 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/EDataType.h +++ b/include/ramses/framework/EDataType.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EDATATYPE_H -#define RAMSES_EDATATYPE_H +#pragma once #include // for integer datatypes #include // for size_t #include @@ -26,8 +25,9 @@ namespace ramses Vector2F, ///< #ramses::vec2f (two components of type float) (per data element if array) Vector3F, ///< #ramses::vec3f (three components of type float) (per data element if array) Vector4F, ///< #ramses::vec4f (four components of type float) (per data element if array) - ByteBlob, ///< array of #ramses::Byte which gets typed later (e.g. interleaved vertex buffer) where one element is always sized as 1 byte + ByteBlob, ///< array of std::byte which gets typed later (e.g. interleaved vertex buffer) where one element is always sized as 1 byte + Bool, ///< one component of type bool Int32, ///< one component of type int32_t Vector2I, ///< #ramses::vec2i (two components of type int32_t) (per data element if array) Vector3I, ///< #ramses::vec3i (three components of type int32_t) (per data element if array) @@ -53,6 +53,7 @@ namespace ramses { switch (dataType) { + case EDataType::Bool: case EDataType::Int32: case EDataType::UInt16: case EDataType::UInt32: @@ -112,6 +113,8 @@ namespace ramses case EDataType::Matrix33F: case EDataType::Matrix44F: return sizeof(float); + case EDataType::Bool: + return sizeof(bool); case EDataType::Int32: case EDataType::Vector2I: case EDataType::Vector3I: @@ -141,5 +144,3 @@ namespace ramses return GetNumberOfComponents(dataType) * GetSizeOfComponent(dataType); } } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/EFeatureLevel.h b/include/ramses/framework/EFeatureLevel.h similarity index 91% rename from framework/ramses-framework-api/include/ramses-framework-api/EFeatureLevel.h rename to include/ramses/framework/EFeatureLevel.h index 11a2a5b31..5be34d1b5 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/EFeatureLevel.h +++ b/include/ramses/framework/EFeatureLevel.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFEATURELEVEL_H -#define RAMSES_EFEATURELEVEL_H +#pragma once #include #include @@ -53,11 +52,4 @@ namespace ramses /// Equals to the latest feature level EFeatureLevel_Latest = EFeatureLevel_01 }; - - /// List of all supported feature levels - constexpr std::array AllFeatureLevels{ EFeatureLevel_01 }; - - static_assert(AllFeatureLevels.back() == EFeatureLevel_Latest, "All feature levels array inconsistent!"); } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/ERotationType.h b/include/ramses/framework/ERotationType.h similarity index 98% rename from client/ramses-client-api/include/ramses-client-api/ERotationType.h rename to include/ramses/framework/ERotationType.h index 003ab3aab..a67037e43 100644 --- a/client/ramses-client-api/include/ramses-client-api/ERotationType.h +++ b/include/ramses/framework/ERotationType.h @@ -8,6 +8,8 @@ #pragma once +#include + namespace ramses { /** @@ -29,7 +31,7 @@ namespace ramses * axis specified by the rotation order. Check the specific enum documentation for the exact rotation order * in terms of 'which rotation is applied first'. */ - enum class ERotationType + enum class ERotationType : uint8_t { Euler_XYZ, ///< rotates around X then Y then Z axis in world coordinate system Euler_XZY, ///< rotates around X then Z then Y axis in world coordinate system diff --git a/client/ramses-client-api/include/ramses-client-api/EScenePublicationMode.h b/include/ramses/framework/EScenePublicationMode.h similarity index 90% rename from client/ramses-client-api/include/ramses-client-api/EScenePublicationMode.h rename to include/ramses/framework/EScenePublicationMode.h index 4905ef137..5eb0b43b1 100644 --- a/client/ramses-client-api/include/ramses-client-api/EScenePublicationMode.h +++ b/include/ramses/framework/EScenePublicationMode.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ESCENEPUBLICATIONMODE_H -#define RAMSES_ESCENEPUBLICATIONMODE_H +#pragma once namespace ramses { @@ -25,5 +24,3 @@ namespace ramses }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/EVisibilityMode.h b/include/ramses/framework/EVisibilityMode.h similarity index 88% rename from client/ramses-client-api/include/ramses-client-api/EVisibilityMode.h rename to include/ramses/framework/EVisibilityMode.h index 3026848eb..2475f210c 100644 --- a/client/ramses-client-api/include/ramses-client-api/EVisibilityMode.h +++ b/include/ramses/framework/EVisibilityMode.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EVISIBILITYMODE_H -#define RAMSES_EVISIBILITYMODE_H +#pragma once #include @@ -18,12 +17,10 @@ namespace ramses * Specifies the mode of node visibility. */ - enum class EVisibilityMode : uint32_t + enum class EVisibilityMode : int8_t { Off = 0, ///< A node shall be invisible and the node shall not trigger its resources to be loaded. Invisible, ///< A node shall be invisible, but the node shall trigger its resources to be loaded. Visible ///< A node shall be fully loaded and visible. }; } - -#endif diff --git a/include/ramses/framework/Flags.h b/include/ramses/framework/Flags.h new file mode 100644 index 000000000..5c2e7365a --- /dev/null +++ b/include/ramses/framework/Flags.h @@ -0,0 +1,131 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/APIExport.h" +#include + +namespace ramses +{ + /** + * @brief Disables global Flags operators by default + * + * Create a template specialization to enable them for a custom enum type. + * + * Example: + * \code{.cpp} + * enum class EFoo{Bar, Baz}; + * template <> struct is_flag : std::true_type {}; + * \endcode + */ + template struct is_flag : std::false_type + { + }; + + /** + * @brief Helper class to create type safe flags from an enum + */ + template + class Flags + { + public: + using enum_type = E; + using value_type = std::underlying_type_t; + + explicit constexpr Flags(value_type value) noexcept + : m_value(value) + { + } + + // NOLINTNEXTLINE(google-explicit-constructor) enum E shall auto-convert to Flags + constexpr Flags(E value) noexcept + : Flags(static_cast(value)) + { + } + + constexpr Flags() noexcept + : Flags(0) + { + } + + constexpr Flags& setFlag(E value, bool on) + { + if (on) + { + m_value |= static_cast(value); + } + else + { + m_value &= ~static_cast(value); + } + return *this; + } + + [[nodiscard]] constexpr bool isSet(E value) const noexcept + { + return (static_cast(value) & m_value) == static_cast(value); + } + + [[nodiscard]] constexpr bool isSet(Flags value) const noexcept + { + return (value.m_value & m_value) == value.m_value; + } + + [[nodiscard]] constexpr value_type value() const noexcept + { + return m_value; + } + + [[nodiscard]] constexpr Flags operator|(E other) const noexcept + { + return Flags(m_value | static_cast(other)); + } + + [[nodiscard]] constexpr bool operator==(const Flags& other) const noexcept + { + return m_value == other.m_value; + } + + [[nodiscard]] constexpr bool operator!=(const Flags& other) const noexcept + { + return m_value != other.m_value; + } + + static_assert(std::is_enum_v, "expected enum type"); + static_assert(is_flag::value, "is_flag trait needs to be declared to support global operators. Example: 'template <> struct is_flag : std::true_type {};'"); + + private: + value_type m_value; + }; + + template ::value> > + [[nodiscard]] constexpr inline Flags operator|(E v1, E v2) noexcept + { + return Flags(v1) | v2; + } + + template ::value> > + [[nodiscard]] constexpr inline Flags operator|(E v1, Flags v2) noexcept + { + return v2 | v1; + } + + template ::value> > + [[nodiscard]] constexpr inline bool operator==(E v1, Flags v2) noexcept + { + return v2 == v1; + } + + template ::value> > + [[nodiscard]] constexpr inline bool operator!=(E v1, Flags v2) noexcept + { + return v2 != v1; + } +} + diff --git a/framework/ramses-framework-api/include/ramses-framework-api/IRamshCommand.h b/include/ramses/framework/IRamshCommand.h similarity index 94% rename from framework/ramses-framework-api/include/ramses-framework-api/IRamshCommand.h rename to include/ramses/framework/IRamshCommand.h index c5ee16214..d62c4c284 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/IRamshCommand.h +++ b/include/ramses/framework/IRamshCommand.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_API_IRAMSHCOMMAND_H -#define RAMSES_API_IRAMSHCOMMAND_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include @@ -58,5 +57,3 @@ namespace ramses virtual bool execute(const std::vector& input) = 0; }; } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/IThreadWatchdogNotification.h b/include/ramses/framework/IThreadWatchdogNotification.h similarity index 95% rename from framework/ramses-framework-api/include/ramses-framework-api/IThreadWatchdogNotification.h rename to include/ramses/framework/IThreadWatchdogNotification.h index c2b435e61..82f475c90 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/IThreadWatchdogNotification.h +++ b/include/ramses/framework/IThreadWatchdogNotification.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITHREADWATCHDOGNOTIFICATION_H -#define RAMSES_ITHREADWATCHDOGNOTIFICATION_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" namespace ramses { @@ -71,5 +70,3 @@ namespace ramses virtual void unregisterThread(ERamsesThreadIdentifier threadID) = 0; }; } - -#endif diff --git a/include/ramses/framework/Issue.h b/include/ramses/framework/Issue.h new file mode 100644 index 000000000..a9654d7a9 --- /dev/null +++ b/include/ramses/framework/Issue.h @@ -0,0 +1,55 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include + +namespace ramses +{ + class RamsesObject; + + enum class EIssueType + { + Error, + Warning, + }; + + /** + * Holds information about a message returned by #ramses::Scene::validate() + */ + struct Issue + { + /** + * Issue classification + */ + EIssueType type = EIssueType::Warning; + + /** + * human-readable text message. + */ + std::string message; + + /** + * The #ramses::RamsesObject which raised the message. Can be nullptr if the warning was not originating from a specific object. + */ + const RamsesObject* object = nullptr; + + bool operator==(const Issue& rhs) const + { + return type == rhs.type + && object == rhs.object + && message == rhs.message; + } + + bool operator!=(const Issue& rhs) const + { + return !operator==(rhs); + } + }; +} diff --git a/framework/ramses-framework-api/include/ramses-framework-api/RamsesFramework.h b/include/ramses/framework/RamsesFramework.h similarity index 70% rename from framework/ramses-framework-api/include/ramses-framework-api/RamsesFramework.h rename to include/ramses/framework/RamsesFramework.h index 629bfe0ad..8c250124d 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/RamsesFramework.h +++ b/include/ramses/framework/RamsesFramework.h @@ -6,21 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESFRAMEWORK_H -#define RAMSES_RAMSESFRAMEWORK_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "ramses-framework-api/StatusObject.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "ramses/framework/Issue.h" +#include "ramses/framework/APIExport.h" #include #include #include #include +#include namespace ramses { + namespace internal + { + class RamsesFrameworkImpl; + } + class RamsesRenderer; class RamsesClient; class RendererConfig; @@ -30,7 +35,7 @@ namespace ramses * @brief Class representing ramses framework components that are needed * to initialize an instance of ramses client and renderer. */ - class RamsesFramework : public StatusObject + class RAMSES_API RamsesFramework { public: /** @@ -38,7 +43,7 @@ namespace ramses * * @param[in] config Configuration object */ - RAMSES_API explicit RamsesFramework(const RamsesFrameworkConfig& config); + explicit RamsesFramework(const RamsesFrameworkConfig& config); /** * @brief Tries to establish a connection to the RAMSES system. @@ -46,25 +51,31 @@ namespace ramses * If only local rendering is desired this does not need to be called - but all * scenes must be published in #ramses::EScenePublicationMode::LocalOnly. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using #getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t connect(); + bool connect(); /** * @brief Check if the RamsesClient is connected or not * * @return true if connected, false otherwise. */ - [[nodiscard]] RAMSES_API bool isConnected() const; + [[nodiscard]] bool isConnected() const; /** * @brief Disconnects the RamsesClient from the system * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using #getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t disconnect(); + bool disconnect(); + + /** + * Returns the feature level this #RamsesFramework instance was configured to use when created. + * See #ramses::RamsesFrameworkConfig::setFeatureLevel and #ramses::EFeatureLevel for more details. + * + * @return feature level this #RamsesFramework instance was configured to use + */ + [[nodiscard]] EFeatureLevel getFeatureLevel() const; /** * @brief Create a new #ramses::RamsesClient linked to this framework. Creation of multiple clients is @@ -80,7 +91,7 @@ namespace ramses * * @return The new #ramses::RamsesClient object or nullptr if the creation failed or was denied. */ - RAMSES_API RamsesClient* createClient(std::string_view applicationName); + RamsesClient* createClient(std::string_view applicationName); /** * @brief Destroy a #ramses::RamsesClient created with this framework. @@ -90,10 +101,9 @@ namespace ramses * * @param client the object to destroy * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using #getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroyClient(RamsesClient& client); + bool destroyClient(RamsesClient& client); /** * @brief Create a new #ramses::RamsesRenderer linked to this framework. @@ -110,7 +120,7 @@ namespace ramses * * @return The new #ramses::RamsesRenderer object or nullptr if the creation failed or was denied. */ - RAMSES_API RamsesRenderer* createRenderer(const RendererConfig& config); + RamsesRenderer* createRenderer(const RendererConfig& config); /** * @brief Destroy a #ramses::RamsesRenderer created with this framework. @@ -120,10 +130,21 @@ namespace ramses * * @param renderer the object to destroy * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using #getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + */ + bool destroyRenderer(RamsesRenderer& renderer); + + /** + * Returns the last error which occurred during an API call within the Ramses SDK and clears it. + * Subsequent calls to this method will return std:nullopt until some API call produces a new error. + * + * It is highly recommended to always check return value from all Ramses API (typically bool or pointer to object) + * and in case there is problem (returned false or nullptr) use this method to get more details about what happened. + * Note that all errors are also logged regardless if using this method or not. + * + * @return last error if any occurred and not already cleared by calling this method, std::nullopt otherwise */ - RAMSES_API status_t destroyRenderer(RamsesRenderer& renderer); + [[nodiscard]] std::optional getLastError(); /** * Sets a custom log handler function, which is called each time a log message occurs. @@ -140,7 +161,7 @@ namespace ramses * -# logHandlerFunc may never call back into ramses code * -# this method should not be used in target code, it is only for testing and tooling */ - RAMSES_API static void SetLogHandler(const LogHandlerFunc& logHandlerFunc); + static void SetLogHandler(const LogHandlerFunc& logHandlerFunc); /** * Gets the current value of the synchronized clock in milliseconds @@ -154,7 +175,7 @@ namespace ramses * * @return current time point of synchronized clock in milliseconds */ - RAMSES_API static uint64_t GetSynchronizedClockMilliseconds(); + static uint64_t GetSynchronizedClockMilliseconds(); /** * @brief Register a ramsh command that can be invoked via console and DLT injection @@ -170,10 +191,9 @@ namespace ramses * of the RamsesFramework object. * * @param[in] command the ramsh command - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using #getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t addRamshCommand(const std::shared_ptr& command); + bool addRamshCommand(const std::shared_ptr& command); /** * @brief Execute a ramsh command programmatically @@ -183,10 +203,9 @@ namespace ramses * For setting the consoleLogLevel the input string with command and argument would be: "setLogLevelConsole trace". * * @param[in] input a a string containing the ramsh command and args - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using #getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t executeRamshCommand(const std::string& input); + bool executeRamshCommand(const std::string& input); /** * @brief Destructor of RamsesFramework @@ -195,7 +214,7 @@ namespace ramses * so there is no general need to explicitly destroy these objects individually, if not specifically intended to do so. * */ - RAMSES_API ~RamsesFramework() override; + ~RamsesFramework(); /** * @brief Deleted copy constructor @@ -219,11 +238,20 @@ namespace ramses */ RamsesFramework& operator=(RamsesFramework&&) = delete; + /** + * Get the internal data for implementation specifics of RamsesFramework. + */ + [[nodiscard]] internal::RamsesFrameworkImpl& impl(); + + /** + * Get the internal data for implementation specifics of RamsesFramework. + */ + [[nodiscard]] const internal::RamsesFrameworkImpl& impl() const; + + protected: /** * Stores internal data for implementation specifics of RamsesFramework */ - class RamsesFrameworkImpl& m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/RamsesFrameworkConfig.h b/include/ramses/framework/RamsesFrameworkConfig.h similarity index 63% rename from framework/ramses-framework-api/include/ramses-framework-api/RamsesFrameworkConfig.h rename to include/ramses/framework/RamsesFrameworkConfig.h index adfb68aa9..79f58c143 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/RamsesFrameworkConfig.h +++ b/include/ramses/framework/RamsesFrameworkConfig.h @@ -6,25 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESFRAMEWORKCONFIG_H -#define RAMSES_RAMSESFRAMEWORKCONFIG_H +#pragma once -#include "ramses-framework-api/StatusObject.h" -#include "ramses-framework-api/IThreadWatchdogNotification.h" -#include "ramses-framework-api/APIExport.h" -#include "ramses-framework-api/EFeatureLevel.h" +#include "ramses/framework/IThreadWatchdogNotification.h" +#include "ramses/framework/APIExport.h" +#include "ramses/framework/EFeatureLevel.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include #include #include namespace ramses { - class RamsesFrameworkConfigImpl; + namespace internal + { + class RamsesFrameworkConfigImpl; + } /** * @brief The RamsesFrameworkConfig holds a set of parameters to be used * to initialize ramses. */ - class RamsesFrameworkConfig : public StatusObject + class RAMSES_API RamsesFrameworkConfig { public: /** @@ -32,12 +35,12 @@ namespace ramses * * @param[in] featureLevel feature level to use (See #ramses::EFeatureLevel for more details) */ - RAMSES_API explicit RamsesFrameworkConfig(EFeatureLevel featureLevel); + explicit RamsesFrameworkConfig(EFeatureLevel featureLevel); /** * @brief Destructor of RamsesFrameworkConfig */ - RAMSES_API ~RamsesFrameworkConfig() override; + ~RamsesFrameworkConfig(); /** * @brief Set feature level @@ -50,10 +53,9 @@ namespace ramses * See #ramses::EFeatureLevel for more details. * * @param[in] featureLevel feature level to use - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setFeatureLevel(EFeatureLevel featureLevel); + bool setFeatureLevel(EFeatureLevel featureLevel); /** * @brief Get feature level @@ -62,25 +64,23 @@ namespace ramses * * @return currently set feature level. */ - [[nodiscard]] RAMSES_API EFeatureLevel getFeatureLevel() const; + [[nodiscard]] EFeatureLevel getFeatureLevel() const; /** * @brief Request a certain type of ramses shell * @param[in] requestedShellType type of ramses shell - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setRequestedRamsesShellType(ERamsesShellType requestedShellType); + bool setRequestedRamsesShellType(ERamsesShellType requestedShellType); /** * @brief Set watchdog notification interval of ramses threads * * @param[in] thread which thread identifier to set the interval for * @param[in] interval interval in ms which is used to call given callback - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval); + bool setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval); /** * @brief Set watchdog callback @@ -90,10 +90,9 @@ namespace ramses * and should be 'monitored' by user code in an appropriate way. * * @param[in] callback callback class to use for watchdog reporting - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback); + bool setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback); /** * @brief Disable DLT application registration @@ -104,55 +103,53 @@ namespace ramses * * When not disabled, ramses will manage DLT application registration itself. * - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t disableDLTApplicationRegistration(); + bool disableDLTApplicationRegistration(); /** * @brief Set the application ID name for DLT (4 chars) * * @param[in] id to use as DLT application id */ - RAMSES_API void setDLTApplicationID(std::string_view id); + void setDLTApplicationID(std::string_view id); /** * @brief Return the DLT application id value set in configuration object * * @return DLT application id value set in this configuration object */ - [[nodiscard]] RAMSES_API std::string_view getDLTApplicationID() const; + [[nodiscard]] std::string_view getDLTApplicationID() const; /** * @brief Set the application description for DLT * * @param[in] description to use as DLT application description */ - RAMSES_API void setDLTApplicationDescription(std::string_view description); + void setDLTApplicationDescription(std::string_view description); /** * @brief Return the DLT application description set in configuration object * * @return DLT application description set in this configuration object */ - [[nodiscard]] RAMSES_API std::string_view getDLTApplicationDescription() const; + [[nodiscard]] std::string_view getDLTApplicationDescription() const; /** * @brief Sets the log level for all contexts * * @param[in] logLevel the log level to be applied */ - RAMSES_API void setLogLevel(ELogLevel logLevel); + void setLogLevel(ELogLevel logLevel); /** * @brief Sets the log level for the provided context * * @param[in] context the log context to modify * @param[in] logLevel the log level to be applied - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setLogLevel(std::string_view context, ELogLevel logLevel); + bool setLogLevel(std::string_view context, ELogLevel logLevel); /** * @brief Sets the maximum log level for all contexts on console output @@ -161,7 +158,7 @@ namespace ramses * * @param[in] logLevel the log level to be applied */ - RAMSES_API void setLogLevelConsole(ELogLevel logLevel); + void setLogLevelConsole(ELogLevel logLevel); /** * @brief Sets the logging interval for the periodic log messages @@ -173,7 +170,7 @@ namespace ramses * * @param[in] interval logging interval in seconds. */ - RAMSES_API void setPeriodicLogInterval(std::chrono::seconds interval); + void setPeriodicLogInterval(std::chrono::seconds interval); /** * @brief Sets the participant identifier @@ -183,10 +180,9 @@ namespace ramses * Guid values < 256 are reserved and may not be used (an error will be returned) * * @param[in] guid participant identifier - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setParticipantGuid(uint64_t guid); + bool setParticipantGuid(uint64_t guid); /** * @brief Sets the participant name in a distributed rendering setup @@ -194,47 +190,45 @@ namespace ramses * Default value is an empty string. * * @param[in] name human readable participant name - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setParticipantName(std::string_view name); + bool setParticipantName(std::string_view name); /** * @brief Sets the connection system for a distributed setup * * @param[in] connectionSystem the connection system to use (default: TCP) - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setConnectionSystem(EConnectionSystem connectionSystem); + bool setConnectionSystem(EConnectionSystem connectionSystem); /** * @brief Sets the IP address that is used to select the local network interface * * @param[in] ip IP to use */ - RAMSES_API void setInterfaceSelectionIPForTCPCommunication(std::string_view ip); + void setInterfaceSelectionIPForTCPCommunication(std::string_view ip); /** * @brief Sets the port that is used to select the local network interface * * @param[in] port Port to use */ - RAMSES_API void setInterfaceSelectionPortForTCPCommunication(uint16_t port); + void setInterfaceSelectionPortForTCPCommunication(uint16_t port); /** * @brief Sets the IP address of the communication daemon * * @param[in] ip IP to use */ - RAMSES_API void setDaemonIPForTCPCommunication(std::string_view ip); + void setDaemonIPForTCPCommunication(std::string_view ip); /** * @brief Sets the port of the communication daemon * * @param[in] port Port to use */ - RAMSES_API void setDaemonPortForTCPCommunication(uint16_t port); + void setDaemonPortForTCPCommunication(uint16_t port); /** * @brief Configures the network connection monitoring @@ -246,42 +240,50 @@ namespace ramses * @param interval time interval for sending keepalive messages * @param timeout maximum time to tolerate a missing keepalive message * - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setConnectionKeepaliveSettings(std::chrono::milliseconds interval, std::chrono::milliseconds timeout); + bool setConnectionKeepaliveSettings(std::chrono::milliseconds interval, std::chrono::milliseconds timeout); /** * @brief Copy constructor * @param other source to copy from */ - RAMSES_API RamsesFrameworkConfig(const RamsesFrameworkConfig& other); + RamsesFrameworkConfig(const RamsesFrameworkConfig& other); /** * @brief Move constructor * @param other source to move from */ - RAMSES_API RamsesFrameworkConfig(RamsesFrameworkConfig&& other) noexcept; + RamsesFrameworkConfig(RamsesFrameworkConfig&& other) noexcept; /** * @brief Copy assignment * @param other source to copy from * @return this instance */ - RAMSES_API RamsesFrameworkConfig& operator=(const RamsesFrameworkConfig& other); + RamsesFrameworkConfig& operator=(const RamsesFrameworkConfig& other); /** * @brief Move assignment * @param other source to move from * @return this instance */ - RAMSES_API RamsesFrameworkConfig& operator=(RamsesFrameworkConfig&& other) noexcept; + RamsesFrameworkConfig& operator=(RamsesFrameworkConfig&& other) noexcept; + + /** + * Get the internal data for implementation specifics of RamsesFrameworkConfig. + */ + [[nodiscard]] internal::RamsesFrameworkConfigImpl& impl(); + /** + * Get the internal data for implementation specifics of RamsesFrameworkConfig. + */ + [[nodiscard]] const internal::RamsesFrameworkConfigImpl& impl() const; + + protected: /** * Stores internal data for implementation specifics of RamsesFrameworkConfig */ - std::reference_wrapper m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/RamsesFrameworkTypes.h b/include/ramses/framework/RamsesFrameworkTypes.h similarity index 82% rename from framework/ramses-framework-api/include/ramses-framework-api/RamsesFrameworkTypes.h rename to include/ramses/framework/RamsesFrameworkTypes.h index a1fc8aee6..2745b9e7d 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/RamsesFrameworkTypes.h +++ b/include/ramses/framework/RamsesFrameworkTypes.h @@ -6,31 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESFRAMEWORKTYPES_H -#define RAMSES_RAMSESFRAMEWORKTYPES_H +#pragma once -#include "ramses-framework-api/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" +#include "ramses/framework/Flags.h" #include #include #include namespace ramses { - /** - * @brief Status is a handle to the result of an API call. - * - * All functions of th RAMSES client API that may fail for some reason - * return a status. This is a handle to request a full status message - * using getStatusMessage() function. - */ - using status_t = uint32_t; - - /** - * @brief Status returned from RAMSES client API methods that succeeded. - * - */ - constexpr const status_t StatusOK = 0u; - /** * @brief Struct used as unique id for the strongly typed scene id. */ @@ -185,27 +170,12 @@ namespace ramses * function is called for each log message separately. * Example * \code{.cpp} - * ramses::RamsesFramework::SetLogHandler([](ELogLevel logLevel, const std::string& context, const std::string& message){ + * ramses::RamsesFramework::SetLogHandler([](ELogLevel logLevel, std::string_view context, std::string_view message){ * std::cout << message << std::endl; * }); * \endcode */ - using LogHandlerFunc = std::function; - - /** - * @brief Struct used as unique id for the strongly-typed cache flag. - */ - struct resourceCacheFlagTag {}; - - /** - * @brief Cache flag value used for passing a strong-typed flag value to a renderer. - */ - using resourceCacheFlag_t = StronglyTypedValue::max(), resourceCacheFlagTag>; - - /** - * @brief Requests the render to not cache a resource. This is the default value. - */ - constexpr const resourceCacheFlag_t ResourceCacheFlag_DoNotCache = resourceCacheFlag_t::Invalid(); + using LogHandlerFunc = std::function; /// Dummy struct to uniquely define ramses::pickableObjectId_t struct pickableObjectTag {}; @@ -244,22 +214,45 @@ namespace ramses */ using waylandIviLayerId_t = StronglyTypedValue::max(), waylandIviLayerIdTag>; + /** + * @brief Struct used as unique id for the strongly typed X11 window handle. + */ + struct X11WindowHandleTag {}; + + /** + * @brief Platform handle to a X11 window + */ + // NOLINTNEXTLINE(google-runtime-int): unsigned long is used in X11 + using X11WindowHandle = StronglyTypedValue::max(), X11WindowHandleTag>; + + /** + * @brief Struct used as unique id for the strongly typed iOS window pointer. + */ + struct IOSNativeWindowPtrTag {}; + + /** + * @brief Platform handle to an iOS window native pointer (CAMetalLayer) + */ + using IOSNativeWindowPtr = StronglyTypedValue; + /** * @brief Clear flags used to specify which components of a render target or display buffer should be cleared. * */ - enum EClearFlags + enum class EClearFlag : uint32_t { - EClearFlags_None = 0, + None = 0, - EClearFlags_Color = 1 << 0, - EClearFlags_Depth = 1 << 1, - EClearFlags_Stencil = 1 << 2, + Color = 1 << 0, + Depth = 1 << 1, + Stencil = 1 << 2, - // NOLINTNEXTLINE(hicpp-signed-bitwise) - EClearFlags_All = EClearFlags_Color | EClearFlags_Depth | EClearFlags_Stencil + All = Color | Depth | Stencil }; + using ClearFlags = Flags; + template <> struct is_flag : std::true_type {}; + /** * @brief Supported connection systems for distributed rendering */ @@ -269,5 +262,3 @@ namespace ramses Off }; } - -#endif diff --git a/include/ramses/framework/RamsesObject.h b/include/ramses/framework/RamsesObject.h new file mode 100644 index 000000000..a2a2cc168 --- /dev/null +++ b/include/ramses/framework/RamsesObject.h @@ -0,0 +1,206 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/APIExport.h" +#include "ramses/framework/RamsesObjectTypes.h" +#include "ramses/framework/ValidationReport.h" + +#include + +namespace ramses +{ + namespace internal + { + class RamsesObjectImpl; + } + + /** + * @ingroup CoreAPI + * @brief The RamsesObject is a base class for all client API objects owned by the framework. + */ + class RAMSES_API RamsesObject + { + public: + /** + * @brief Returns the name of the object. + * + * @return Name of the object + */ + [[nodiscard]] std::string_view getName() const; + + /** + * @brief Changes the name of the object. + * + * @param name New name of the object + * @return true for success, false otherwise + */ + bool setName(std::string_view name); + + /** + * @brief Gets type of the object. + * + * @return Type of the object, see ERamsesObjectType enum for possible values. + */ + [[nodiscard]] ERamsesObjectType getType() const; + + /** + * @brief Checks if the object is of given type. + * + * @param[in] type Type to check against. + * @return True if object is of given type, ie. it can be converted to given type. + */ + [[nodiscard]] bool isOfType(ERamsesObjectType type) const; + + /** + * Set user ID for this object. + * User ID is an optional identifier of #RamsesObject stored as number of up to 128 bits. + * User ID is often logged together with name (#getName) when referring to this object and is serialized, thus persistent. + * Note that user IDs do not have to be unique, it is user's choice and responsibility if uniqueness is desired. + * User ID is logged only if other than [0,0] was set and the format is hexadecimal 'highId|lowId' with all digits printed. + * + * @param highId high 64 bits of user ID to set + * @param lowId low 64 bits of user ID to set + * @return true if successful, false if failed + */ + bool setUserId(uint64_t highId, uint64_t lowId); + + /** + * Returns the user ID set using #setUserId. + * + * @return the user ID [highId, lowId] or [0, 0] if no user ID was set + */ + [[nodiscard]] std::pair getUserId() const; + + /** + * @brief Performs a (potentially slow!) validation of this object and its dependencies + * + * validate() may append issues to the provided report object, classified by warning or error: + * - errors need to be fixed, otherwise the object's behaviour will be undefined + * - warnings indicate issues that are undesireable, but not necessarily cause problems + * (e.g. unused resources, performance issues) + * + * @note validate() will skip objects that are already part of the provided report. (Provide an empty report to force re-validation) + * + * @param report The report that the object writes to + */ + void validate(ValidationReport& report) const; + + /** + * Casts this object to given type. + * Has same behavior as \c dynamic_cast, will return nullptr (without error) if given type does not match this object. + * + * @return ramses object cast to given type or nullptr if wrong type provided + */ + template + [[nodiscard]] const T* as() const; + + /** + * @copydoc as() const + */ + template + [[nodiscard]] T* as(); + + /** + * Get the internal data for implementation specifics of RamsesObject. + */ + [[nodiscard]] internal::RamsesObjectImpl& impl(); + + /** + * Get the internal data for implementation specifics of RamsesObject. + */ + [[nodiscard]] const internal::RamsesObjectImpl& impl() const; + + /** + * @brief Deleted copy constructor + */ + RamsesObject(const RamsesObject&) = delete; + + /** + * @brief Deleted move constructor + */ + RamsesObject(RamsesObject&&) = delete; + + /** + * @brief Deleted copy assignment + * @return unused + */ + RamsesObject& operator=(const RamsesObject&) = delete; + + /** + * @brief Deleted move assignment + * @return unused + */ + RamsesObject& operator=(RamsesObject&&) = delete; + + protected: + /** + * @brief Constructor for RamsesObject. + * + * @param[in] impl Internal data for implementation specifics of RamsesObject (sink - instance becomes owner) + */ + explicit RamsesObject(std::unique_ptr impl); + + /** + * @brief Destructor of the RamsesObject + */ + virtual ~RamsesObject(); + + /** + * Internal implementation of #as with check that T is the correct type + */ + template + [[nodiscard]] const T* internalCast() const; + + /** + * @copydoc internalCast() const + */ + template + [[nodiscard]] T* internalCast(); + + /** + * Stores internal data for implementation specifics of RamsesObject. + */ + std::unique_ptr m_impl; + }; + + template const T* RamsesObject::as() const + { + static_assert(std::is_base_of::value, "T in as must be a subclass of RamsesObject!"); + return internalCast(); + } + + template T* RamsesObject::as() + { + static_assert(std::is_base_of::value, "T in as must be a subclass of RamsesObject!"); + return internalCast(); + } + + /** + * Returns the given object cast to type T if the object is convertible to T + * Otherwise a nullptr is returned + */ + template T object_cast(RamsesObject* obj) + { + using ObjType = typename std::remove_cv_t>; + static_assert(std::is_base_of_v, "T must be a subclass of RamsesObject!"); + return (obj != nullptr) ? obj->as() : nullptr; + } + + /** + * @copydoc object_cast() + */ + template T object_cast(const RamsesObject* obj) + { + using ObjType = typename std::remove_cv_t>; + static_assert(std::is_base_of_v, "T must be a subclass of RamsesObject!"); + return (obj != nullptr) ? obj->as() : nullptr; + } +} diff --git a/client/ramses-client-api/include/ramses-client-api/RamsesObjectTypes.h b/include/ramses/framework/RamsesObjectTypes.h similarity index 70% rename from client/ramses-client-api/include/ramses-client-api/RamsesObjectTypes.h rename to include/ramses/framework/RamsesObjectTypes.h index 409b5eea9..3a79e430c 100644 --- a/client/ramses-client-api/include/ramses-client-api/RamsesObjectTypes.h +++ b/include/ramses/framework/RamsesObjectTypes.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESOBJECTTYPES_H -#define RAMSES_RAMSESOBJECTTYPES_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" namespace ramses { @@ -25,14 +24,16 @@ namespace ramses SceneObject, // base type Client, Scene, - Node, // base type + LogicEngine, + LogicObject, // base type + Node, // base type and concrete type MeshNode, Camera, // base type PerspectiveCamera, OrthographicCamera, Effect, Appearance, - GeometryBinding, + Geometry, PickableObject, Resource, // base type Texture2D, @@ -46,18 +47,11 @@ namespace ramses TextureSamplerMS, RenderBuffer, RenderTarget, - ArrayBufferObject, + ArrayBuffer, Texture2DBuffer, DataObject, SceneReference, TextureSamplerExternal, - // Whenever new type of object is added - // its traits must be registered in RamsesObjectTypeTraits.h using helper macros - // and added to appropriate test type list(s) in RamsesObjectTestTypes.h - // and added a conversion template instantiation in RamsesObjectTypeUtils.cpp - NUMBER_OF_TYPES }; } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/RamsesVersion.h b/include/ramses/framework/RamsesVersion.h similarity index 90% rename from framework/ramses-framework-api/include/ramses-framework-api/RamsesVersion.h rename to include/ramses/framework/RamsesVersion.h index b3994dbb2..f27534024 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/RamsesVersion.h +++ b/include/ramses/framework/RamsesVersion.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESVERSION_H -#define RAMSES_RAMSESVERSION_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" namespace ramses { @@ -35,5 +34,3 @@ namespace ramses */ RAMSES_API RamsesVersion GetRamsesVersion(); } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/RendererSceneState.h b/include/ramses/framework/RendererSceneState.h similarity index 93% rename from framework/ramses-framework-api/include/ramses-framework-api/RendererSceneState.h rename to include/ramses/framework/RendererSceneState.h index e166c7a9c..061520860 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/RendererSceneState.h +++ b/include/ramses/framework/RendererSceneState.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENESTATE_H -#define RAMSES_RENDERERSCENESTATE_H +#pragma once #include @@ -23,5 +22,3 @@ namespace ramses Rendered ///< Scene is being rendered. }; } - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/StronglyTypedValue.h b/include/ramses/framework/StronglyTypedValue.h similarity index 89% rename from framework/ramses-framework-api/include/ramses-framework-api/StronglyTypedValue.h rename to include/ramses/framework/StronglyTypedValue.h index 6fba406cc..a6338ffca 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/StronglyTypedValue.h +++ b/include/ramses/framework/StronglyTypedValue.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STRONGLYTYPEDVALUE_H -#define RAMSES_STRONGLYTYPEDVALUE_H +#pragma once -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include #include @@ -33,7 +32,7 @@ namespace ramses */ static constexpr StronglyTypedValue Invalid() { - return StronglyTypedValue(_invalid); + return {}; } /** @@ -43,14 +42,15 @@ namespace ramses explicit constexpr StronglyTypedValue(BaseType value) : m_value(value) { + static_assert(std::is_trivially_copyable_v, "should be trivially copyable"); } /** * @brief Default constructor with invalid value */ constexpr StronglyTypedValue() - : m_value(_invalid) { + static_assert(std::is_trivially_copyable_v, "should be trivially copyable"); } /** @@ -113,10 +113,10 @@ namespace ramses return m_value != other.m_value; } - static_assert(std::is_arithmetic::value || std::is_pointer::value, "expected arithmetic or pointer basetype"); + static_assert(std::is_arithmetic_v || std::is_pointer_v, "expected arithmetic or pointer basetype"); private: - BaseType m_value; + BaseType m_value{_invalid}; }; } @@ -138,5 +138,3 @@ namespace std } }; } - -#endif diff --git a/client/ramses-client-api/include/ramses-client-api/TextureEnums.h b/include/ramses/framework/TextureEnums.h similarity index 94% rename from client/ramses-client-api/include/ramses-client-api/TextureEnums.h rename to include/ramses/framework/TextureEnums.h index e9d68a566..a5fac306c 100644 --- a/client/ramses-client-api/include/ramses-client-api/TextureEnums.h +++ b/include/ramses/framework/TextureEnums.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTUREENUMS_H -#define RAMSES_TEXTUREENUMS_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/APIExport.h" namespace ramses { @@ -20,7 +19,7 @@ namespace ramses */ /// Texture sampling method - enum class ETextureSamplingMethod + enum class ETextureSamplingMethod : uint8_t { Nearest, Linear, @@ -31,7 +30,7 @@ namespace ramses }; /// Texture address mode - enum class ETextureAddressMode + enum class ETextureAddressMode : uint8_t { Clamp, Repeat, @@ -39,14 +38,10 @@ namespace ramses }; /// Texture data format - enum class ETextureFormat + enum class ETextureFormat : uint8_t { - Invalid, - R8, - RG8, - RGB8, RGB565, @@ -123,14 +118,6 @@ namespace ramses DepthStencil }; - /// Enum for type of a RenderBuffer - enum class ERenderBufferType - { - Color, - Depth, - DepthStencil - }; - /// Enum for format of a RenderBuffer enum class ERenderBufferFormat { @@ -148,7 +135,9 @@ namespace ramses RGBA16F, RGBA32F, + Depth16, Depth24, + Depth32, Depth24_Stencil8 }; @@ -257,5 +246,3 @@ namespace ramses * @} */ } - -#endif diff --git a/include/ramses/framework/ValidationReport.h b/include/ramses/framework/ValidationReport.h new file mode 100644 index 000000000..abb43f7a2 --- /dev/null +++ b/include/ramses/framework/ValidationReport.h @@ -0,0 +1,101 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/APIExport.h" +#include "ramses/framework/Issue.h" +#include +#include + +namespace ramses +{ + namespace internal + { + class ValidationReportImpl; + } + + /** + * @ingroup CoreAPI + * @brief ValidationReport contains a list of issues that is reported by #ramses::RamsesObject::validate(). + * + * @see #ramses::RamsesObject::validate() + */ + class RAMSES_API ValidationReport + { + public: + /** + * @brief Creates an empty report + */ + ValidationReport(); + + /** + * @brief Destructor + */ + ~ValidationReport(); + + /** + * @brief Deleted copy constructor + */ + ValidationReport(const ValidationReport& other); + + /** + * @brief Move constructor + * @param other source to move from + */ + ValidationReport(ValidationReport&& other) noexcept; + + /** + * @brief Deleted copy assignment + */ + ValidationReport& operator=(const ValidationReport& other); + + /** + * @brief Move assignment + * @param other source to move from + * @return this instance + */ + ValidationReport& operator=(ValidationReport&& other) noexcept; + + /** + * @brief Erases all issues from the report. After this call getIssues() returns an empty vector + */ + void clear(); + + /** + * @brief Checks whether the report contains an issue that is qualified as an error + * @return true if the report contains an error, false otherwise + */ + [[nodiscard]] bool hasError() const; + + /** + * @brief Checks whether the report contains any issues (warnings or errors) + * @return true if the report contains any error or warning, false otherwise + */ + [[nodiscard]] bool hasIssue() const; + + /** + * @brief Gets all reported issues + * @return vector of issues + */ + [[nodiscard]] const std::vector& getIssues() const; + + /** + * Get the internal implementation + */ + [[nodiscard]] internal::ValidationReportImpl& impl(); + + /** + * Get the internal implementation + */ + [[nodiscard]] const internal::ValidationReportImpl& impl() const; + + private: + std::unique_ptr m_impl; + }; +} diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/BinaryShaderCache.h b/include/ramses/renderer/BinaryShaderCache.h similarity index 89% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/BinaryShaderCache.h rename to include/ramses/renderer/BinaryShaderCache.h index ac5ddfb24..e3e77d478 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/BinaryShaderCache.h +++ b/include/ramses/renderer/BinaryShaderCache.h @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERAPI_BINARYSHADERCACHE_H -#define RAMSES_RENDERERAPI_BINARYSHADERCACHE_H +#pragma once -#include "ramses-renderer-api/IBinaryShaderCache.h" +#include "ramses/renderer/IBinaryShaderCache.h" #include namespace ramses { + namespace internal + { + class BinaryShaderCacheImpl; + } + /** * @ingroup RendererAPI * @brief Provide default implementation for IBinaryShaderCache interface, to be used in RamsesRenderer by setting in RendererConfig. @@ -86,7 +90,7 @@ namespace ramses * @param buffer pointer to the buffer to get the binary shader data * @param bufferSize the size of the buffer to get the binary shader data */ - void getBinaryShaderData(effectId_t effectId, uint8_t* buffer, uint32_t bufferSize) const override; + void getBinaryShaderData(effectId_t effectId, std::byte* buffer, uint32_t bufferSize) const override; /** * @brief Store the binary shader into the cache. @@ -96,7 +100,7 @@ namespace ramses * @param binaryShaderDataSize size of the binary shader data * @param binaryShaderFormat format of the binary shader */ - void storeBinaryShader(effectId_t effectId, sceneId_t sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) override; + void storeBinaryShader(effectId_t effectId, sceneId_t sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) override; /** * @brief Save all binary shaders in the cache to the file with the given path @@ -136,11 +140,20 @@ namespace ramses */ BinaryShaderCache& operator=(const BinaryShaderCache& other) = delete; + /** + * Get the internal data for implementation specifics of BinaryShaderCache. + */ + [[nodiscard]] internal::BinaryShaderCacheImpl& impl(); + + /** + * Get the internal data for implementation specifics of BinaryShaderCache. + */ + [[nodiscard]] const internal::BinaryShaderCacheImpl& impl() const; + + protected: /** * Stores internal data for implementation specifics of this class. */ - class BinaryShaderCacheImpl& impl; + internal::BinaryShaderCacheImpl& m_impl; }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/DisplayConfig.h b/include/ramses/renderer/DisplayConfig.h similarity index 68% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/DisplayConfig.h rename to include/ramses/renderer/DisplayConfig.h index 7e57ed2b7..e3e7bff7c 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/DisplayConfig.h +++ b/include/ramses/renderer/DisplayConfig.h @@ -6,68 +6,74 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERAPI_DISPLAYCONFIG_H -#define RAMSES_RENDERERAPI_DISPLAYCONFIG_H +#pragma once -#include "ramses-renderer-api/Types.h" -#include "ramses-framework-api/StatusObject.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/renderer/Types.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/DataTypes.h" +#include namespace ramses { - class DisplayConfigImpl; + namespace internal + { + class DisplayConfigImpl; + } + class ValidationReport; /** * @ingroup RendererAPI * @brief The DisplayConfig holds a set of parameters to be used to initialize a display. */ - class DisplayConfig : public StatusObject + class RAMSES_API DisplayConfig { public: /** * @brief Default constructor of DisplayConfig */ - RAMSES_API DisplayConfig(); + DisplayConfig(); /** * @brief Destructor of DisplayConfig */ - RAMSES_API ~DisplayConfig() override; + ~DisplayConfig(); + + /** + * @copydoc #ramses::RamsesObject::validate + */ + void validate(ValidationReport& report) const; /** * @brief Sets the requested device type used for display context and device creation. * By default a device type GLES 3.0 is requested. * * @param[in] deviceType Device type. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setDeviceType(EDeviceType deviceType); + bool setDeviceType(EDeviceType deviceType); /** * @brief Get the device type used for display context and device creation. * * @return Device type used for display context and device creation. Default value is device type GLES 3.0. */ - [[nodiscard]] RAMSES_API EDeviceType getDeviceType() const; + [[nodiscard]] EDeviceType getDeviceType() const; /** * @brief Sets the window type used for display creation. * By default any of the supported window types will be picked depending on build configuration. * * @param[in] windowType Window type. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWindowType(EWindowType windowType); + bool setWindowType(EWindowType windowType); /** * @brief Get the window type used for display creation. * * @return Window type used for display creation. Default value depends on build configuration. */ - [[nodiscard]] RAMSES_API EWindowType getWindowType() const; + [[nodiscard]] EWindowType getWindowType() const; /** * @brief Sets the window size and position in display pixel space. @@ -77,10 +83,9 @@ namespace ramses * @param[in] y Vertical offset (distance from top border of the display) * @param[in] width Width of the window * @param[in] height Height of the window - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height); + bool setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height); /** * @brief Get the window size and position in display pixel space, as it was specified by setWindowRectangle() or command line options @@ -90,36 +95,25 @@ namespace ramses * @param[out] y Vertical offset (distance from top border of the display) * @param[out] width Width of the window * @param[out] height Height of the window - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const; + bool getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const; /** * @brief Automatically sets the window size so that it fills the entire display. * Overrides DisplayConfig::setWindowRectangle() when set to true. * * @param[in] fullscreen Flag specifying whether window should be fullscreen - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWindowFullscreen(bool fullscreen); + bool setWindowFullscreen(bool fullscreen); /** * @brief Gets the currently set fullscreen state, which was set * either via DisplayConfig::setWindowFullscreen or parsed from command line arguments. * @return True if this DisplayConfig is set to use fullscreen window, false otherwise. */ - [[nodiscard]] RAMSES_API bool isWindowFullscreen() const; - - /** - * @brief Sets window hints/properties to tell the window manager to disable window borders - * - * @param[in] borderless flag specifying whether window should be drawn without window-border - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - */ - RAMSES_API status_t setWindowBorderless(bool borderless); + [[nodiscard]] bool isWindowFullscreen() const; /** * @brief Set number of samples to be used for multisampled rendering. @@ -132,19 +126,17 @@ namespace ramses * in fatal error. * * @param[in] numSamples Number of samples per pixel. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setMultiSampling(uint32_t numSamples); + bool setMultiSampling(uint32_t numSamples); /** * @brief Get number of samples currently set. * * @param[out] numSamples Number of samples per pixel. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t getMultiSamplingSamples(uint32_t& numSamples) const; + bool getMultiSamplingSamples(uint32_t& numSamples) const; /** * @brief [Mandatory on Wayland] Set IVI layer ID to use for attaching the IVI surface created by the display. @@ -156,17 +148,16 @@ namespace ramses * This values is ignored on other systems than Wayland. * * @param[in] waylandIviLayerID IVI layer ID to use for attaching the surface used by the display - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID); + bool setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID); /** * @brief Get the ID of the Wayland IVI layer to which the IVI surface used by the display is attached. * * @return the current setting of wayland IVI layer ID, returns waylandIviLayerId_t::Invalid() if no value has been set yet */ - [[nodiscard]] RAMSES_API waylandIviLayerId_t getWaylandIviLayerID() const; + [[nodiscard]] waylandIviLayerId_t getWaylandIviLayerID() const; /** * @brief [Mandatory on Wayland] Set IVI surface ID to use when creating the display window on Wayland. @@ -176,24 +167,23 @@ namespace ramses * - If the surface is not already existing it will be created during display creation * * @param[in] waylandIviSurfaceID IVI surface ID to use for the display window - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID); + bool setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID); /** * @brief Get the current setting of IVI surface ID * * @return the current setting of IVI surface ID, returns waylandIviSurfaceId_t::Invalid() if no value has been set yet */ - [[nodiscard]] RAMSES_API waylandIviSurfaceId_t getWaylandIviSurfaceID() const; + [[nodiscard]] waylandIviSurfaceId_t getWaylandIviSurfaceID() const; /** * @brief Get the current setting of Android native window * * @return the current setting of Android native window, returns nullptr if no value has been set yet */ - [[nodiscard]] RAMSES_API void* getAndroidNativeWindow() const; + [[nodiscard]] void* getAndroidNativeWindow() const; /** * @brief [Mandatory on Android] Set native window to use for rendering on Android. @@ -202,32 +192,35 @@ namespace ramses * * No ownership is transferred, the user is responsible to call ANativeWindow_release after destroying the RAMSES Renderer. * - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setAndroidNativeWindow(void* nativeWindowPtr); + bool setAndroidNativeWindow(void* nativeWindowPtr); /** - * @brief Set IVI window to be visible right after window creation + * @brief Get the current setting of iOS native window. * - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return the current setting of iOS native window (CAMetalLayer), returns nullptr if no value has been set yet */ - RAMSES_API status_t setWindowIviVisible(); + [[nodiscard]] IOSNativeWindowPtr getIOSNativeWindow() const; /** - * @brief By default uploaded effects are kept forever in VRAM to avoid - * recompiling and reuploading. As their size is mostly negligible - * this should not have any disadvantages. - * If however it is desired to delete unused effects same as any other resources - * use this method to disable that optimization. - * Note that GPU cache can still prevent effect deletion (see ramses::DisplayConfig::setGPUMemoryCacheSize). + * @brief [Mandatory on iOS] Set native window to use for rendering on iOS. * - * @param[in] enable Set to true if effects should be always kept in VRAM (default), false if they should be deleted if unused as other resources - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] nativeWindowPtr Strongly typed value that wraps CAMetalLayer* which should be added to + * the view where RAMSES should rendered to. + * + * No ownership is transferred, the user is responsible to clean up the CAMetalLayer after destroying the RAMSES Renderer. + * + * @return true for success, false otherwise (check log for details). */ - RAMSES_API status_t keepEffectsUploaded(bool enable); + bool setIOSNativeWindow(IOSNativeWindowPtr nativeWindowPtr); + + /** + * @brief Set IVI window to be visible right after window creation + * + * @return true on success, false if an error occurred (error is logged) + */ + bool setWindowIviVisible(); /** * @brief Set the amount of GPU memory in bytes that will be used as cache for resources. @@ -244,28 +237,25 @@ namespace ramses * Cache is disabled by default (size is 0). * * @param[in] size GPU resource cache size in bytes. Disabled if 0 (default) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setGPUMemoryCacheSize(uint64_t size); + bool setGPUMemoryCacheSize(uint64_t size); /** * @brief Enables/disables resizing of the window (Default=Disabled) * @param[in] resizable The resizable flag * - * @return StatusOK on success, otherwise the returned status can be used to resolve - * to resolve error message using getStatusMessage() + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setResizable(bool resizable); + bool setResizable(bool resizable); /** * @brief Sets the clear color of the displays framebuffer (Default=0.0, 0.0, 0.0, 1.0) * @param[in] color clear color (rgba, channel values in range [0,1]) * - * @return StatusOK on success, otherwise the returned status can be used to resolve - * to resolve error message using getStatusMessage() + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setClearColor(const vec4f& color); + bool setClearColor(const vec4f& color); /** * @brief Sets whether depth/stencil buffer should be created for the framebuffer of the display. @@ -280,10 +270,9 @@ namespace ramses * * @param[in] depthBufferType Configure depth and stencil buffers. * - * @return StatusOK on success, otherwise the returned status can be used to resolve - * to resolve error message using getStatusMessage() + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setDepthStencilBufferType(EDepthBufferType depthBufferType); + bool setDepthStencilBufferType(EDepthBufferType depthBufferType); /** * @brief [Only for X11] Set the X11 window handle to create a ramses display from an existing X11 window. @@ -293,10 +282,9 @@ namespace ramses * * @param[in] x11WindowHandle native X11 window id to use for the display window. * The type is equivalent to \verbatim ::Window \endverbatim from the X11 headers. - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setX11WindowHandle(unsigned long x11WindowHandle); // NOLINT(google-runtime-int) use platform type + bool setX11WindowHandle(X11WindowHandle x11WindowHandle); /** * @brief [Only for X11] Get the current setting of the X11 window handle @@ -304,7 +292,7 @@ namespace ramses * @return the current setting of the X11 window handle, returns numerical maximum value if no value has been set yet. * The returned type is equivalent to \verbatim ::Window \endverbatim from the X11 headers. */ - [[nodiscard]] RAMSES_API unsigned long getX11WindowHandle() const; // NOLINT(google-runtime-int) use platform type + [[nodiscard]] X11WindowHandle getX11WindowHandle() const; /** * @brief [Only for Windows] Set the HWND handle to create a ramses display from an existing HWND window on a Window platform. @@ -313,34 +301,32 @@ namespace ramses * - On other systems except for Windows, calling this method has no meaning * * @param[in] hwnd Windows window handle to use for the display window - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWindowsWindowHandle(void* hwnd); + bool setWindowsWindowHandle(void* hwnd); /** * @brief Get the current setting of the Windows window handle (HWND) * * @return the current setting of the Windows window handle, returns nullptr if no value has been set yet */ - [[nodiscard]] RAMSES_API void* getWindowsWindowHandle() const; + [[nodiscard]] void* getWindowsWindowHandle() const; /** * @brief Set the Wayland display name to connect to. * This will override the default behavior which is to use WAYLAND_DISPLAY environment variable * * @param[in] waylandDisplay Wayland display name to use for connection - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandDisplay(std::string_view waylandDisplay); + bool setWaylandDisplay(std::string_view waylandDisplay); /** * @brief Get the current setting of Wayland display name * * @return Wayland display name to use for connection, empty means default */ - [[nodiscard]] RAMSES_API std::string_view getWaylandDisplay() const; + [[nodiscard]] std::string_view getWaylandDisplay() const; /** * @brief Sets whether async shader/effect compilation and upload should be enabled. @@ -361,10 +347,9 @@ namespace ramses * * @param[in] enabled Set to true to enable async effect upload, false to disable it. * - * @return StatusOK on success, otherwise the returned status can be used to resolve - * to resolve error message using getStatusMessage() + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setAsyncEffectUploadEnabled(bool enabled); + bool setAsyncEffectUploadEnabled(bool enabled); /** * @brief Set the name to be used for the embedded compositing @@ -392,27 +377,24 @@ namespace ramses * * @param[in] socketname The file name of the socket file. * - * @return StatusOK for success, otherwise the returned status can - * be used to resolve error message using - * getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandEmbeddedCompositingSocketName(std::string_view socketname); + bool setWaylandEmbeddedCompositingSocketName(std::string_view socketname); /** * @brief Get the current setting of embedded compositing display socket name * * @return Wayland display name to use for embedded compositing socket */ - [[nodiscard]] RAMSES_API std::string_view getWaylandEmbeddedCompositingSocketName() const; + [[nodiscard]] std::string_view getWaylandEmbeddedCompositingSocketName() const; /** * @brief Request that the embedded compositing display socket belongs to the given group. * * @param[in] groupname The group name of the socket. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname); + bool setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname); /** * @brief Set the file descriptor for the embedded compositor @@ -438,11 +420,9 @@ namespace ramses * @param socketFileDescriptor The file descriptor of the socket * for the embedded compositor. * - * @return StatusOK for success, otherwise the returned status can - * be used to resolve error message using - * getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandEmbeddedCompositingSocketFD(int socketFileDescriptor); + bool setWaylandEmbeddedCompositingSocketFD(int socketFileDescriptor); /** * @brief Request that the embedded compositing display socket obtains the permissions given. @@ -459,20 +439,18 @@ namespace ramses * setWaylandEmbeddedCompositingSocketName(), not when passed in as filedescriptor. * * @param[in] permissions The permissions of the socket. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions); + bool setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions); /** * @brief Set the Wayland display name to connect system compositor to. * This will override the default behavior which is to use WAYLAND_DISPLAY environment variable * * @param[in] waylandDisplay Wayland display name to use for connection - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setSystemCompositorWaylandDisplay(std::string_view waylandDisplay); + bool setSystemCompositorWaylandDisplay(std::string_view waylandDisplay); /** * @brief Set the render node to use for creating GBM buffer objects used for creating DMA Offscreen buffers @@ -490,10 +468,9 @@ namespace ramses * * * @param[in] renderNode Render node used to load for creating of GBM device using gbm_create_device - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setPlatformRenderNode(std::string_view renderNode); + bool setPlatformRenderNode(std::string_view renderNode); /** * @brief Specifies the minimum number of video frames that are displayed before a buffer swap will occur @@ -503,10 +480,9 @@ namespace ramses * Default value is platform dependent. * * @param[in] interval Minimum number of video frames that are displayed before a buffer swap will occur - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setSwapInterval(int32_t interval); + bool setSwapInterval(int32_t interval); /** * @brief Specifies the scene's priority on this display @@ -521,10 +497,9 @@ namespace ramses * * @param[in] sceneId scene id of the preferred scene * @param[in] priority scene priority. Higher value means less priority - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setScenePriority(sceneId_t sceneId, int32_t priority); + bool setScenePriority(sceneId_t sceneId, int32_t priority); /** * @brief Sets the batch size for resource uploads @@ -536,42 +511,50 @@ namespace ramses * The batch size may not be 0. * * @param[in] batchSize the number of resources to upload in a single step (default: 10) - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setResourceUploadBatchSize(uint32_t batchSize); + bool setResourceUploadBatchSize(uint32_t batchSize); /** * @brief Copy constructor * @param other source to copy from */ - RAMSES_API DisplayConfig(const DisplayConfig& other); + DisplayConfig(const DisplayConfig& other); /** * @brief Move constructor * @param other source to move from */ - RAMSES_API DisplayConfig(DisplayConfig&& other) noexcept; + DisplayConfig(DisplayConfig&& other) noexcept; /** * @brief Copy assignment * @param other source to copy from * @return this instance */ - RAMSES_API DisplayConfig& operator=(const DisplayConfig& other); + DisplayConfig& operator=(const DisplayConfig& other); /** * @brief Move assignment * @param other source to move from * @return this instance */ - RAMSES_API DisplayConfig& operator=(DisplayConfig&& other) noexcept; + DisplayConfig& operator=(DisplayConfig&& other) noexcept; + /** + * Get the internal data for implementation specifics of DisplayConfig. + */ + [[nodiscard]] internal::DisplayConfigImpl& impl(); + + /** + * Get the internal data for implementation specifics of DisplayConfig. + */ + [[nodiscard]] const internal::DisplayConfigImpl& impl() const; + + protected: /** * Stores internal data for implementation specifics of DisplayConfig. */ - std::reference_wrapper m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/IBinaryShaderCache.h b/include/ramses/renderer/IBinaryShaderCache.h similarity index 96% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/IBinaryShaderCache.h rename to include/ramses/renderer/IBinaryShaderCache.h index c5f51d7f2..ad63570fc 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/IBinaryShaderCache.h +++ b/include/ramses/renderer/IBinaryShaderCache.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERAPI_IBINARYSHADERCACHE_H -#define RAMSES_RENDERERAPI_IBINARYSHADERCACHE_H +#pragma once -#include "ramses-renderer-api/Types.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/renderer/Types.h" +#include "ramses/framework/APIExport.h" namespace ramses { @@ -151,7 +150,7 @@ namespace ramses * @param buffer pointer to the buffer to be filled the binary shader data * @param bufferSize the size of 'buffer' */ - virtual void getBinaryShaderData(effectId_t effectId, uint8_t* buffer, uint32_t bufferSize) const = 0; + virtual void getBinaryShaderData(effectId_t effectId, std::byte* buffer, uint32_t bufferSize) const = 0; /** * @brief Used by RamsesRenderer to provide the application with a binary shader data an effect with the given effectId @@ -172,7 +171,7 @@ namespace ramses * @param[in] binaryShaderDataSize size of the binary shader data * @param[in] binaryShaderFormat format of the binary shader - platform/driver specific binary shader format ID */ - virtual void storeBinaryShader(effectId_t effectId, sceneId_t sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) = 0; + virtual void storeBinaryShader(effectId_t effectId, sceneId_t sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) = 0; /** * @brief Used by RamsesRenderer to provide a callback with information on the result of a binary shader upload operation. @@ -186,5 +185,3 @@ namespace ramses virtual void binaryShaderUploaded(effectId_t effectId, bool success) const = 0; }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererEventHandler.h b/include/ramses/renderer/IRendererEventHandler.h similarity index 98% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererEventHandler.h rename to include/ramses/renderer/IRendererEventHandler.h index b10060e5b..26292a3fe 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererEventHandler.h +++ b/include/ramses/renderer/IRendererEventHandler.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDEREREVENTHANDLER_H -#define RAMSES_IRENDEREREVENTHANDLER_H +#pragma once #include "Types.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/APIExport.h" #include namespace ramses @@ -81,7 +80,7 @@ namespace ramses * @param keyModifiers Modifiers used while pressing a key (bit mask of EKeyModifier) * @param keyCode The actual key which was pressed */ - virtual void keyEvent(displayId_t displayId, EKeyEvent eventType, uint32_t keyModifiers, EKeyCode keyCode) = 0; + virtual void keyEvent(displayId_t displayId, EKeyEvent eventType, KeyModifiers keyModifiers, EKeyCode keyCode) = 0; /** @@ -224,7 +223,7 @@ namespace ramses /** * @copydoc ramses::IRendererEventHandler::keyEvent */ - void keyEvent(displayId_t displayId, EKeyEvent eventType, uint32_t keyModifiers, EKeyCode keyCode) override + void keyEvent(displayId_t displayId, EKeyEvent eventType, KeyModifiers keyModifiers, EKeyCode keyCode) override { (void)displayId; (void)eventType; @@ -304,5 +303,3 @@ namespace ramses }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererSceneControlEventHandler.h b/include/ramses/renderer/IRendererSceneControlEventHandler.h similarity index 98% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererSceneControlEventHandler.h rename to include/ramses/renderer/IRendererSceneControlEventHandler.h index f39578680..e36ca1d97 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererSceneControlEventHandler.h +++ b/include/ramses/renderer/IRendererSceneControlEventHandler.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERERSCENECONTROLEVENTHANDLER_H -#define RAMSES_IRENDERERSCENECONTROLEVENTHANDLER_H +#pragma once -#include "ramses-renderer-api/Types.h" -#include "ramses-framework-api/RendererSceneState.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/renderer/Types.h" +#include "ramses/framework/RendererSceneState.h" +#include "ramses/framework/APIExport.h" namespace ramses { @@ -386,5 +385,3 @@ namespace ramses } }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/RamsesRenderer.h b/include/ramses/renderer/RamsesRenderer.h similarity index 79% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/RamsesRenderer.h rename to include/ramses/renderer/RamsesRenderer.h index ad943e744..c6e5c59be 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/RamsesRenderer.h +++ b/include/ramses/renderer/RamsesRenderer.h @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESRENDERER_H -#define RAMSES_RAMSESRENDERER_H +#pragma once -#include "ramses-framework-api/StatusObject.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/DataTypes.h" +#include "ramses/renderer/RendererConfig.h" #include @@ -23,6 +21,12 @@ namespace ramses { + namespace internal + { + class RendererFactory; + class RamsesRendererImpl; + } + class SystemCompositorController; class DisplayConfig; class IRendererEventHandler; @@ -36,11 +40,11 @@ namespace ramses * they are then executed asynchronously in the renderer core, the order of execution is preserved. * Most of the commands have a corresponding callback which reports the result back to the caller * via #ramses::RamsesRenderer::dispatchEvents. - * Some commands can fail immediately by returning a status with value other than StatusOK, - * in such case there will be no callback, because the command will not even be submitted. + * Some commands can fail immediately by returning false, in such case there will be no callback, + * because the command will not even be submitted. * #ramses::RamsesRenderer API is not thread-safe. */ - class RamsesRenderer : public StatusObject + class RAMSES_API RamsesRenderer { public: /** @@ -49,10 +53,9 @@ namespace ramses * For active rendering it is recommended to use threaded mode instead (#startThread). * Once this method is called a threaded mode cannot be used anymore. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t doOneLoop(); + bool doOneLoop(); /** * @brief Starts update and render loop in threaded mode. @@ -60,27 +63,25 @@ namespace ramses * First call to this method enables threaded mode, afterwards it is not possible to call * #doOneLoop anymore. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t startThread(); + bool startThread(); /** * @brief Stops thread(s) running the update and render of displays. * @details This function can only be used if startThread was successfully called before. * The looping can be restarted by calling #startThread again. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t stopThread(); + bool stopThread(); /** * @brief Get the current state of rendering thread(s) running * * @return Returns true if thread is running (started and not stopped), false otherwise. */ - [[nodiscard]] RAMSES_API bool isThreadRunning() const; + [[nodiscard]] bool isThreadRunning() const; /** * @brief Sets the maximum frame rate per second for the update/render loop when in threaded mode. @@ -92,14 +93,13 @@ namespace ramses * (i.e. #ramses::IRendererEventHandler::displayCreated event has not been dispatched yet), * this makes it easier to set a FPS limit on a display right after creation without having to wait * for the result. If setting FPS limit on display which was previously destroyed this method - * will return #ramses::StatusOK but will have no effect. + * will return true but will have no effect. * @param displayId The ID of the display the framerate should be applied to. The default value is 60 FPS. * @param fpsLimit The maximum frame rate per second to set for the render loop. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setFramerateLimit(displayId_t displayId, float fpsLimit); + bool setFramerateLimit(displayId_t displayId, float fpsLimit); /** * @brief Get the maximum frame rate per second set for given display using #setFramerateLimit. @@ -110,7 +110,7 @@ namespace ramses * * @return The FPS limit for given display set by user or default FPS limit. */ - [[nodiscard]] RAMSES_API float getFramerateLimit(displayId_t displayId) const; + [[nodiscard]] float getFramerateLimit(displayId_t displayId) const; /** * @brief Sets the mode of operation for render loop. @@ -119,17 +119,16 @@ namespace ramses * By default loop mode is set to render and update. * * @param loopMode The mode to be used for render loop. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setLoopMode(ELoopMode loopMode); + bool setLoopMode(ELoopMode loopMode); /** * @brief Get the current value for loop mode set using #setLoopMode * * @return The loop mode */ - [[nodiscard]] RAMSES_API ELoopMode getLoopMode() const; + [[nodiscard]] ELoopMode getLoopMode() const; /** * @brief Sets time limits for time-out of different sections of render and update loop. @@ -154,11 +153,9 @@ namespace ramses * @param[in] limitForSceneResourcesUpload Time limit in microseconds (since beginning of frame) for uploading scene resources to GPU * @param[in] limitForClientResourcesUpload Time limit in microseconds (since beginning of frame) for uploading client resources to GPU * @param[in] limitForOffscreenBufferRender Time limit in microseconds (since beginning of frame) for rendering scenes that are mapped to interruptible offscreen buffers - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee successful read back, the result event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); + bool setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); /** * @brief Sets the number of pending flushes accepted before force-applying them to their scene, or forcefully insubscribing the scene. @@ -178,10 +175,9 @@ namespace ramses * * @param[in] forceApplyFlushLimit Number of flushes that can be pending before force applying occurs. * @param[in] forceUnsubscribeSceneLimit Number of flushes that can be pending before force un-subscribe occurs. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit); + bool setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit); /** * @brief Enable or disable skipping of rendering of unmodified buffers. @@ -192,11 +188,9 @@ namespace ramses * or debugging of a graphical glitch. * * @param[in] enable Enable or disable the feature (enabled initially) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee successful read back, the result event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setSkippingOfUnmodifiedBuffers(bool enable = true); + bool setSkippingOfUnmodifiedBuffers(bool enable = true); /** * @brief Creates a display based on provided display config. @@ -210,7 +204,7 @@ namespace ramses * if a valid display id is returned, the result of the actual creation * can be retrieved via dispatchEvents. */ - RAMSES_API displayId_t createDisplay(const DisplayConfig& config); + displayId_t createDisplay(const DisplayConfig& config); /** * @brief Destroy a display. @@ -220,10 +214,9 @@ namespace ramses * after the asynchronous action was processed. * * @param displayId The display id of the display to destroy. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroyDisplay(displayId_t displayId); + bool destroyDisplay(displayId_t displayId); /** * @brief Get display's framebuffer ID. @@ -233,7 +226,7 @@ namespace ramses * @param displayId The ID of display for which the framebuffer ID is being queried. * @return Display's framebuffer ID or invalid ID if display does not exist. */ - [[nodiscard]] RAMSES_API displayBufferId_t getDisplayFramebuffer(displayId_t displayId) const; + [[nodiscard]] displayBufferId_t getDisplayFramebuffer(displayId_t displayId) const; /** * @brief Will create an offscreen buffer that can be used to render scenes into (see #ramses::RendererSceneControl::setSceneDisplayBufferAssignment) @@ -245,15 +238,15 @@ namespace ramses * maximum supported (creation will succeeded with a warning log). * * @param[in] display id of display for which the buffer should be created - * @param[in] width width of the buffer to be created (has to be higher than 0 and lower than 4096) - * @param[in] height height of the buffer to be created (has to be higher than 0 and lower than 4096) + * @param[in] width width of the buffer to be created (has to be higher than 0) + * @param[in] height height of the buffer to be created (has to be higher than 0) * @param[in] sampleCount Optional sample count for MSAA. Default value is 0 for no MSAA. * @param[in] depthBufferType Optional setting to configure depth and stencil buffers, default is depth/stencil buffer will be created in addition to color buffer. * @return Identifier of the created offscreen buffer. * In case of unsupported resolution \c displayBufferId_t::Invalid() will be returned with no renderer event generated. * Note that the buffer will be created asynchronously and there will be a renderer event once the operation is finished. */ - RAMSES_API displayBufferId_t createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount = 0u, EDepthBufferType depthBufferType = EDepthBufferType::DepthStencil); + displayBufferId_t createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount = 0u, EDepthBufferType depthBufferType = EDepthBufferType::DepthStencil); /** * @brief Additional API to create an offscreen buffer as interruptible. (see #createOffscreenBuffer) @@ -273,14 +266,14 @@ namespace ramses * The created offscreen buffer always has a color buffer. Depth/stencil buffer can be configured (depth/stencil combined is attached by default). * * @param[in] display Id of display for which the buffer should be created - * @param[in] width Width of the buffer to be created (has to be higher than 0 and lower than 4096) - * @param[in] height Height of the buffer to be created (has to be higher than 0 and lower than 4096) + * @param[in] width Width of the buffer to be created (has to be higher than 0) + * @param[in] height Height of the buffer to be created (has to be higher than 0) * @param[in] depthBufferType Optional setting to configure depth and stencil buffers, default is depth/stencil buffer will be created in addition to color buffer. * @return Identifier of the created offscreen buffer. * In case of unsupported resolution \c displayBufferId_t::Invalid() will be returned with no renderer event generated. * Note that the buffer will be created asynchronously and there will be a renderer event once the operation is finished. */ - RAMSES_API displayBufferId_t createInterruptibleOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, EDepthBufferType depthBufferType = EDepthBufferType::DepthStencil); + displayBufferId_t createInterruptibleOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, EDepthBufferType depthBufferType = EDepthBufferType::DepthStencil); /** * @brief Additional API to create an offscreen buffer using DMA buffer for internal storage. (see #createOffscreenBuffer) @@ -309,8 +302,8 @@ namespace ramses * without invoking a callback #ramses::IRendererEventHandler::offscreenBufferCreated. * * @param[in] display Id of display for which the buffer should be created - * @param[in] width Width of the buffer to be created (has to be higher than 0 and lower than 4096) - * @param[in] height Height of the buffer to be created (has to be higher than 0 and lower than 4096) + * @param[in] width Width of the buffer to be created (has to be higher than 0) + * @param[in] height Height of the buffer to be created (has to be higher than 0) * @param[in] bufferFourccFormat Format to be used for underlying storage of the buffer, as specified in drm_fourcc.h on the target platform * @param[in] usageFlags Usage flags used for creation of the underlying GBM buffer object, as specific in enum gbm_bo_flags on the target platform * @param[in] modifier Optional format modifier. If not needed set to DRM_FORMAT_MOD_INVALID. @@ -318,7 +311,7 @@ namespace ramses * In case of unsupported resolution or renderer running in own thread \c displayBufferId_t::Invalid() will be returned with no renderer event generated. * Note that the buffer will be created asynchronously and there will be a renderer event once the operation is finished. */ - RAMSES_API displayBufferId_t createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t usageFlags, uint64_t modifier); + displayBufferId_t createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t usageFlags, uint64_t modifier); /** * @brief Get the FD and stride for a DMA offscreen buffer previously created on the given display @@ -342,10 +335,9 @@ namespace ramses * @param[in] displayBufferId Id of the DMA offscreen buffer for which the FD should be returned * @param[out] fd File descriptor of underlying GBM buffer object for DMA offscreen buffer * @param[out] stride Stride of DMA offsceen buffer in bytes - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; + bool getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; /** * @brief Will destroy a previously created offscreen buffer. @@ -355,10 +347,9 @@ namespace ramses * * @param[in] display id of display which the buffer belongs to * @param[in] offscreenBuffer id of buffer to destroy - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer); + bool destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer); /** * @brief Creates a buffer using OpenGL External textures for storage. @@ -388,7 +379,7 @@ namespace ramses * @return Identifier of the created external buffer. * In case renderer is running in own thread \c externalBufferId_t::Invalid() will be returned. */ - RAMSES_API externalBufferId_t createExternalBuffer(displayId_t display); + externalBufferId_t createExternalBuffer(displayId_t display); /** * @brief Will destroy a previously created external buffer. @@ -396,10 +387,9 @@ namespace ramses * * @param[in] display id of display which the buffer belongs to * @param[in] externalBuffer id of buffer to destroy - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroyExternalBuffer(displayId_t display, externalBufferId_t externalBuffer); + bool destroyExternalBuffer(displayId_t display, externalBufferId_t externalBuffer); /** * @brief Creates a buffer for viewing wayland surfaces from the embedded compositor. @@ -409,7 +399,7 @@ namespace ramses * @param[in] surfaceId Id of the wayland surface that the buffer should render from. * @return Identifier of the created external buffer. */ - RAMSES_API streamBufferId_t createStreamBuffer(displayId_t display, ramses::waylandIviSurfaceId_t surfaceId); + streamBufferId_t createStreamBuffer(displayId_t display, ramses::waylandIviSurfaceId_t surfaceId); /** * @brief Will destroy a previously created stream buffer. @@ -417,10 +407,9 @@ namespace ramses * * @param[in] display id of display which the buffer belongs to * @param[in] bufferId id of buffer to destroy - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t destroyStreamBuffer(displayId_t display, ramses::streamBufferId_t bufferId); + bool destroyStreamBuffer(displayId_t display, ramses::streamBufferId_t bufferId); /** * @brief Sets clear flags for a display buffer (display's framebuffer or offscreen buffer). @@ -432,12 +421,11 @@ namespace ramses * @param[in] display Id of display that the buffer to set clearing belongs to. * @param[in] displayBuffer Id of display buffer to set clearing, * if #ramses::displayBufferId_t::Invalid() is passed then the clearing is set for display's framebuffer. - * @param[in] clearFlags Bitmask of the #ramses::EClearFlags, use bit OR to select which buffer component to clear - * or #ramses::EClearFlags_All to clear all (default). - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @param[in] clearFlags Bitmask of the #ramses::EClearFlag, use bit OR to select which buffer component to clear + * or #ramses::EClearFlag::All to clear all (default). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, uint32_t clearFlags); + bool setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, ClearFlags clearFlags); /** * @brief Sets clear color of a display buffer (display's framebuffer or offscreen buffer). @@ -450,10 +438,9 @@ namespace ramses * @param[in] displayBuffer Id of display buffer to set clear color, * if #ramses::displayBufferId_t::Invalid() is passed then the clear color is set for display's framebuffer. * @param[in] color Clear color (rgba, channel values in range [0,1]) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color); + bool setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color); /** * @brief Updates display window size after a resize event on windows not owned by renderer. @@ -473,10 +460,9 @@ namespace ramses * @param[in] display Id of display that the resized window belongs to. * @param[in] width New width of the resized window. * @param[in] height New height of the resized window. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height); + bool setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height); /** * @brief Triggers an asynchronous read back of a display buffer memory from GPU to system memory. @@ -498,11 +484,10 @@ namespace ramses * The origin of the image is supposed to be in the lower left corner. * @param[in] width The width of the read image in pixels. Must be greater than Zero. * @param[in] height The height of the read image in pixels. Must be greater than Zero. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee successful read back, the result event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee successful read back, the result event has its own status. */ - RAMSES_API status_t readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height); + bool readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height); /** * @brief Get scene control API @@ -524,7 +509,7 @@ namespace ramses * * @return Pointer to scene control API, or nullptr on error */ - RAMSES_API RendererSceneControl* getSceneControlAPI(); + RendererSceneControl* getSceneControlAPI(); ///////////////////////////////////////////////// // System Compositor API @@ -534,19 +519,17 @@ namespace ramses * @brief Set visibility of given surface at the system compositor * @param surfaceId id of the surface to set visibility of * @param visibility visibility to set - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). **/ - RAMSES_API status_t setSurfaceVisibility(uint32_t surfaceId, bool visibility); + bool setSurfaceVisibility(uint32_t surfaceId, bool visibility); /** * @brief Set opacity of given surface at the system compositor * @param surfaceId id of the surface to set opacity of * @param opacity Opacity in the range 0.0 (fully transparent) to 1.0 (fully opaque) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). **/ - RAMSES_API status_t setSurfaceOpacity(uint32_t surfaceId, float opacity); + bool setSurfaceOpacity(uint32_t surfaceId, float opacity); /** * @brief Set output rectangle of given surface at the system compositor @@ -555,29 +538,26 @@ namespace ramses * @param y Output position of surface along the y-axis * @param width Output width of surface * @param height Output height of surface - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height); + bool setSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height); /** * @brief Set visibility of given layer at the system compositor * @param layerId id identifying the layer * @param visibility If \c true the layer's visibility will be enabled, otherwise disabled - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). **/ - RAMSES_API status_t setLayerVisibility(uint32_t layerId, bool visibility); + bool setLayerVisibility(uint32_t layerId, bool visibility); /** * @brief Trigger the System Compositor to take a screenshot and store it in a file. * @param fileName File name including path, for storing the screenshot. * @param screenIviId >= 0 to trigger a screenshot on the given IVI screen id, -1 to trigger screenshot * on a single existing screen (fails asynchronously if more than one screen exists) - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t takeSystemCompositorScreenshot(std::string_view fileName, int32_t screenIviId); + bool takeSystemCompositorScreenshot(std::string_view fileName, int32_t screenIviId); ///////////////////////////////////////////////// // End of System Compositor API @@ -595,44 +575,76 @@ namespace ramses * * @param rendererEventHandler User class that implements the callbacks that can be triggered if a corresponding event happened. * Check ramses::IRendererEventHandler documentation for more details. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t dispatchEvents(IRendererEventHandler& rendererEventHandler); + bool dispatchEvents(IRendererEventHandler& rendererEventHandler); /** * @brief Submits renderer commands (API calls on this instance of RamsesRenderer) * since previous flush to be executed in the next renderer update loop. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t flush(); + bool flush(); /** * @brief Prints detailed information about renderer state and contents to the log output. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t logRendererInfo(); + bool logRendererInfo(); /** - * Stores internal data for implementation specifics of RamsesRenderer - */ - class RamsesRendererImpl& m_impl; + * @brief Deleted copy constructor + */ + RamsesRenderer(const RamsesRenderer&) = delete; + + /** + * @brief Deleted move constructor + */ + RamsesRenderer(RamsesRenderer&&) = delete; + + /** + * @brief Deleted copy assignment + * @return unused + */ + RamsesRenderer& operator=(const RamsesRenderer&) = delete; + + /** + * @brief Deleted move assignment + * @return unused + */ + RamsesRenderer& operator=(RamsesRenderer&&) = delete; + + /** + * Get the internal data for implementation specifics of RamsesRenderer. + */ + [[nodiscard]] internal::RamsesRendererImpl& impl(); + + /** + * Get the internal data for implementation specifics of RamsesRenderer. + */ + [[nodiscard]] const internal::RamsesRendererImpl& impl() const; private: /** * @brief Constructor of RamsesRenderer */ - explicit RamsesRenderer(std::unique_ptr); + explicit RamsesRenderer(std::unique_ptr impl); + + /** + * Destructor + */ + ~RamsesRenderer(); + + /** + * Stores internal data for implementation specifics of RamsesRenderer + */ + std::unique_ptr m_impl; /** * @brief RendererFactory is the factory for RamsesRenderer */ - friend class RendererFactory; + friend class internal::RendererFactory; }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/RendererConfig.h b/include/ramses/renderer/RendererConfig.h similarity index 59% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/RendererConfig.h rename to include/ramses/renderer/RendererConfig.h index 73c45a6b0..e5cb143fb 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/RendererConfig.h +++ b/include/ramses/renderer/RendererConfig.h @@ -6,62 +6,54 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERAPI_RENDERERCONFIG_H -#define RAMSES_RENDERERAPI_RENDERERCONFIG_H +#pragma once -#include "ramses-renderer-api/Types.h" -#include "ramses-framework-api/StatusObject.h" +#include "ramses/renderer/Types.h" #include +#include namespace ramses { + namespace internal + { + class RendererConfigImpl; + } + class IBinaryShaderCache; - class IRendererResourceCache; - class RendererConfigImpl; /** * @brief The RendererConfig holds a set of parameters to be used * to initialize a renderer. * @ingroup RendererAPI */ - class RendererConfig : public StatusObject + class RAMSES_API RendererConfig { public: /** * @brief Default constructor of RendererConfig */ - RAMSES_API RendererConfig(); + RendererConfig(); /** * @brief Destructor of RendererConfig */ - RAMSES_API ~RendererConfig() override; + ~RendererConfig(); /** * @brief Set the Binary Shader Cache to be used in Renderer. * @param[in] cache the binary shader cache to be used by the Renderer - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - */ - RAMSES_API status_t setBinaryShaderCache(IBinaryShaderCache& cache); - - /** - * @brief Set the resource cache implementation to be used by the renderer. - * @param[in] cache the resource cache to be used by the renderer. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setRendererResourceCache(IRendererResourceCache& cache); + bool setBinaryShaderCache(IBinaryShaderCache& cache); /** * @brief Enable the renderer to communicate with the system compositor. * This flag needs to be enabled before calling any of the system compositor * related calls, otherwise an error will be reported when issuing such commands * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t enableSystemCompositorControl(); + bool enableSystemCompositorControl(); /** * @brief Set the maximum time to wait for the system compositor frame callback @@ -73,28 +65,25 @@ namespace ramses * * @param[in] waitTimeInUsec The maximum time wait for a frame callback, in microseconds * - * @return StatusOK for success, otherwise the returned status can - * be used to resolve error message using - * getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec); + bool setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec); /** * @brief Set the Wayland display name to connect system compositor to. * This will override the default behavior which is to use WAYLAND_DISPLAY environment variable * * @param[in] waylandDisplay Wayland display name to use for connection - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setSystemCompositorWaylandDisplay(std::string_view waylandDisplay); + bool setSystemCompositorWaylandDisplay(std::string_view waylandDisplay); /** * @brief Get the current setting of Wayland display name * * @return Wayland display name to use for connection, empty means default */ - [[nodiscard]] RAMSES_API std::string_view getSystemCompositorWaylandDisplay() const; + [[nodiscard]] std::string_view getSystemCompositorWaylandDisplay() const; /** * @brief Set the desired reporting period for first display loop timings. @@ -104,49 +93,57 @@ namespace ramses * A value of zero disables reporting and is the default. * * @param[in] period Cyclic time period after which timing information should be reported - * @return StatusOK on success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true on success, false if an error occurred (error is logged) */ - RAMSES_API status_t setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period); + bool setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period); /** * @brief Get the current reporting period for renderThread loop timings * * @return Reporting period for renderThread loop timings */ - [[nodiscard]] RAMSES_API std::chrono::milliseconds getRenderThreadLoopTimingReportingPeriod() const; + [[nodiscard]] std::chrono::milliseconds getRenderThreadLoopTimingReportingPeriod() const; /** * @brief Copy constructor * @param other source to copy from */ - RAMSES_API RendererConfig(const RendererConfig& other); + RendererConfig(const RendererConfig& other); /** * @brief Move constructor * @param other source to move from */ - RAMSES_API RendererConfig(RendererConfig&& other) noexcept; + RendererConfig(RendererConfig&& other) noexcept; /** * @brief Copy assignment * @param other source to copy from * @return this instance */ - RAMSES_API RendererConfig& operator=(const RendererConfig& other); + RendererConfig& operator=(const RendererConfig& other); /** * @brief Move assignment * @param other source to move from * @return this instance */ - RAMSES_API RendererConfig& operator=(RendererConfig&& other) noexcept; + RendererConfig& operator=(RendererConfig&& other) noexcept; + + /** + * Get the internal data for implementation specifics of RendererConfig. + */ + [[nodiscard]] internal::RendererConfigImpl& impl(); + + /** + * Get the internal data for implementation specifics of RendererConfig. + */ + [[nodiscard]] const internal::RendererConfigImpl& impl() const; + protected: /** * Stores internal data for implementation specifics of RendererConfig. */ - std::reference_wrapper m_impl; + std::unique_ptr m_impl; }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/RendererSceneControl.h b/include/ramses/renderer/RendererSceneControl.h similarity index 77% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/RendererSceneControl.h rename to include/ramses/renderer/RendererSceneControl.h index d5aea83d1..4e7dd129f 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/RendererSceneControl.h +++ b/include/ramses/renderer/RendererSceneControl.h @@ -6,15 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENECONTROL_H -#define RAMSES_RENDERERSCENECONTROL_H +#pragma once -#include "ramses-framework-api/StatusObject.h" -#include "ramses-framework-api/RendererSceneState.h" -#include "ramses-framework-api/APIExport.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/RendererSceneState.h" +#include "ramses/framework/APIExport.h" +#include namespace ramses { + namespace internal + { + class RamsesRendererImpl; + class RendererSceneControlImpl; + } + class IRendererSceneControlEventHandler; /** @@ -27,10 +33,10 @@ namespace ramses * they are then executed asynchronously in the renderer core, the order of execution is preserved. * Most of the commands have a corresponding callback which reports the result back to the caller * via #dispatchEvents. - * Some commands can fail immediately by returning a status with value other than StatusOK, - * in such case there will be no callback, because the command will not even be submitted. + * Some commands can fail immediately by returning false, in such case there will be no callback, + * because the command will not even be submitted. */ - class RendererSceneControl : public StatusObject + class RAMSES_API RendererSceneControl { public: /** @@ -55,11 +61,10 @@ namespace ramses * * @param[in] sceneId Scene to request state change for. * @param[in] state Scene state to request. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee success, the result argument in dispatched event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee success, the result argument in dispatched event has its own status. */ - RAMSES_API status_t setSceneState(sceneId_t sceneId, RendererSceneState state); + bool setSceneState(sceneId_t sceneId, RendererSceneState state); /** * @brief Set scene display mapping @@ -82,10 +87,9 @@ namespace ramses * * @param[in] sceneId Scene to set display mapping. * @param[in] displayId Display to set as target for scene when mapped. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setSceneMapping(sceneId_t sceneId, displayId_t displayId); + bool setSceneMapping(sceneId_t sceneId, displayId_t displayId); /** * @brief Set scene display buffer assignment @@ -111,10 +115,9 @@ namespace ramses * @param[in] displayBuffer Id of display buffer (framebuffer or offscreen buffer) the scene should be assigned to. * If provided buffer Id is invalid, framebuffer of display where scene is mapped is used. * @param[in] sceneRenderOrder Lower value means that a scene is rendered before a scene with higher value. Default is 0. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder = 0); + bool setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder = 0); /** * @brief Links display's offscreen buffer to a data consumer in scene. @@ -132,11 +135,10 @@ namespace ramses * @param[in] offscreenBufferId ID of the offscreen buffer to use as texture provider. * @param[in] consumerSceneId Scene which consumes the data. * @param[in] consumerDataSlotId Data consumer within the consumer scene (#ramses::Scene::createTextureConsumer). - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee success, the result argument in dispatched event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee success, the result argument in dispatched event has its own status. */ - RAMSES_API status_t linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); + bool linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); /** * @brief Links display's stream buffer to a data consumer in scene. @@ -158,11 +160,10 @@ namespace ramses * @param[in] streamBufferId ID of the stream buffer to use as texture provider. * @param[in] consumerSceneId Scene which consumes the data. * @param[in] consumerDataSlotId Data consumer within the consumer scene (#ramses::Scene::createTextureConsumer). - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee success, the result argument in dispatched event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee success, the result argument in dispatched event has its own status. */ - RAMSES_API status_t linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); + bool linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); /** * @brief Links external buffer to a data consumer in scene. @@ -181,11 +182,10 @@ namespace ramses * @param[in] externalBufferId ID of the external buffer to use as external texture provider. * @param[in] consumerSceneId Scene which consumes the data. * @param[in] consumerDataSlotId Data consumer within the consumer scene (#ramses::Scene::createTextureConsumer). - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee success, the result argument in dispatched event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee success, the result argument in dispatched event has its own status. */ - RAMSES_API status_t linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); + bool linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); /** * @brief Links a data provider from one scene to a data consumer in another scene. @@ -204,11 +204,10 @@ namespace ramses * @param providerId ID of provided data. * @param consumerSceneId Consumer scene containing consumer slot. * @param consumerId ID of consumer slot. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee success, the result argument in dispatched event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee success, the result argument in dispatched event has its own status. */ - RAMSES_API status_t linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId); + bool linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId); /** * @brief Removes an existing link between data provider and consumer (#linkData) @@ -219,11 +218,10 @@ namespace ramses * * @param consumerSceneId Consumer scene containing consumer slot. * @param consumerId ID of consumer slot. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). - * StatusOK does not guarantee success, the result argument in dispatched event has its own status. + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). + * true does not guarantee success, the result argument in dispatched event has its own status. */ - RAMSES_API status_t unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId); + bool unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId); /** * @brief Trigger renderer to test if given pick event with coordinates intersects with any instances @@ -245,10 +243,9 @@ namespace ramses * @param sceneId Id of scene to check for intersected PickableObjects. * @param bufferNormalizedCoordX Normalized X pick coordinate within buffer size (see \ref PickCoordinates). * @param bufferNormalizedCoordY Normalized Y pick coordinate within buffer size (see \ref PickCoordinates). - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t handlePickEvent(sceneId_t sceneId, float bufferNormalizedCoordX, float bufferNormalizedCoordY); + bool handlePickEvent(sceneId_t sceneId, float bufferNormalizedCoordX, float bufferNormalizedCoordY); /** * @brief Submits scene control commands (API calls on RendererSceneControl) @@ -257,10 +254,9 @@ namespace ramses * For example one scene can be hidden, another scene shown and data linked, all within single frame. * Not all state changes can be executed within a single update loop, see #setSceneState for details. * - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t flush(); + bool flush(); /** * @brief RendererSceneControl methods push commands to an internal queue which is submitted @@ -274,20 +270,59 @@ namespace ramses * * @param eventHandler User class that implements the callbacks that can be triggered if a corresponding event happened. * Check #ramses::IRendererSceneControlEventHandler documentation for more details. - * @return StatusOK for success, otherwise the returned status can be used - * to resolve error message using getStatusMessage(). + * @return true for success, false otherwise (check log or #ramses::RamsesFramework::getLastError for details). */ - RAMSES_API status_t dispatchEvents(IRendererSceneControlEventHandler& eventHandler); + bool dispatchEvents(IRendererSceneControlEventHandler& eventHandler); + + /** + * @brief Deleted copy constructor + */ + RendererSceneControl(const RendererSceneControl&) = delete; + + /** + * @brief Deleted move constructor + */ + RendererSceneControl(RendererSceneControl&&) = delete; - /// Stores internal data for implementation specifics - class RendererSceneControlImpl& m_impl; + /** + * @brief Deleted copy assignment + * @return unused + */ + RendererSceneControl& operator=(const RendererSceneControl&) = delete; + + /** + * @brief Deleted move assignment + * @return unused + */ + RendererSceneControl& operator=(RendererSceneControl&&) = delete; + + /** + * Get the internal data for implementation specifics of RendererSceneControl. + */ + [[nodiscard]] internal::RendererSceneControlImpl& impl(); + + /** + * Get the internal data for implementation specifics of RendererSceneControl. + */ + [[nodiscard]] const internal::RendererSceneControlImpl& impl() const; + + protected: + /** + * Stores internal data for implementation specifics + */ + std::unique_ptr m_impl; private: - friend class RamsesRendererImpl; + friend class internal::RamsesRendererImpl; - /// Constructor - explicit RendererSceneControl(std::unique_ptr); + /** + * Constructor + */ + explicit RendererSceneControl(std::unique_ptr impl); + + /** + * Destructor + */ + ~RendererSceneControl(); }; } - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/Types.h b/include/ramses/renderer/Types.h similarity index 94% rename from renderer/ramses-renderer-api/include/ramses-renderer-api/Types.h rename to include/ramses/renderer/Types.h index fbb05a477..87921b6d0 100644 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/Types.h +++ b/include/ramses/renderer/Types.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERAPI_TYPES_H -#define RAMSES_RENDERERAPI_TYPES_H +#pragma once #include -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" namespace ramses { @@ -252,16 +251,19 @@ namespace ramses * @brief Specifies key modifiers for keyboard input. * */ - enum EKeyModifier + enum class EKeyModifier : uint32_t { - EKeyModifier_NoModifier = 0, - EKeyModifier_Ctrl = 1 << 0, - EKeyModifier_Shift = 1 << 1, - EKeyModifier_Alt = 1 << 2, - EKeyModifier_Function = 1 << 3, - EKeyModifier_Numpad = 1 << 4 + NoModifier = 0, + Ctrl = 1 << 0, + Shift = 1 << 1, + Alt = 1 << 2, + Function = 1 << 3, + Numpad = 1 << 4 }; + using KeyModifiers = Flags; + template <> struct is_flag : std::true_type {}; + /** * @brief Specifies behavior of render loop * @@ -304,12 +306,11 @@ namespace ramses X11, Wayland_IVI, Wayland_Shell, - Android + Android, + iOS }; /** * @} */ } - -#endif diff --git a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferRenderingTests.h b/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferRenderingTests.h deleted file mode 100644 index ca31227be..000000000 --- a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferRenderingTests.h +++ /dev/null @@ -1,44 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DMAOFFSCREENBUFFERRENDERINGTESTS_H -#define RAMSES_DMAOFFSCREENBUFFERRENDERINGTESTS_H - -#include "RendererTestsFramework.h" -#include "DmaOffscreenBufferTests.h" -#include "Utils/LogMacros.h" - -#include -#include - -class DmaOffscreenBufferRenderingTests -{ -public: - DmaOffscreenBufferRenderingTests(const std::vector& filterIn, const std::vector& filterOut, bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) - : m_testFramework(generateScreenshots, config) - { - m_dmaOffscreenBufferTests.setUpTestCases(m_testFramework); - m_testFramework.filterTestCases(filterIn, filterOut); - } - - bool runTests() - { - return m_testFramework.runAllTests(); - } - - void logReport() - { - fmt::print("{}\n", m_testFramework.generateReport()); - } - -protected: - RendererTestsFramework m_testFramework; - DmaOffscreenBufferTests m_dmaOffscreenBufferTests; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.cpp b/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.cpp deleted file mode 100644 index 70f2fe423..000000000 --- a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.cpp +++ /dev/null @@ -1,555 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DmaOffscreenBufferTests.h" -#include "TestScenes/TextureLinkScene.h" -#include "TestScenes/MultipleTrianglesScene.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Utils/LogMacros.h" -#include -#include -#include -#include -#include - -using namespace ramses_internal; - -void DmaOffscreenBufferTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferRead, *this, "DmaOffscreenBufferTest_BufferRead"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferWrite, *this, "DmaOffscreenBufferTest_BufferWrite"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferReadWrite, *this, "DmaOffscreenBufferTest_BufferReadWrite"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_ReadContentWithAlphaBlending, *this, "DmaOffscreenBufferTest_ContentWithAlphaBlending"); - - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_XBGR8888, *this, "DmaOffscreenBufferTest_Format_XBGR8888"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_XRGB8888, *this, "DmaOffscreenBufferTest_Format_XRGB8888"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_ARGB8888, *this, "DmaOffscreenBufferTest_Format_ARGB8888"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_BGR888, *this, "DmaOffscreenBufferTest_Format_BGR888"); - - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_UsageFlagRendering, *this, "DmaOffscreenBufferTest_UsageFlagRendering"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut, *this, "DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut"); - - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_200x200, *this, "DmaOffscreenBufferTest_BufferSize_200x200"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_256x256, *this, "DmaOffscreenBufferTest_BufferSize_256x256"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_128x64, *this, "DmaOffscreenBufferTest_BufferSize_128x64"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_128x256, *this, "DmaOffscreenBufferTest_BufferSize_128x256"); - - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead, *this, "DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead"); - testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_TwoDisplays, *this, "DmaOffscreenBufferTest_TwoDisplays"); -} - -bool DmaOffscreenBufferTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - //Set big frame callback poll time, to make sure no frames are skipped due to late frame callbacks - auto rendererConfig = RendererTestUtils::CreateTestRendererConfig(); - rendererConfig.setFrameCallbackMaxPollTime(1000000u); - testFramework.initializeRenderer(rendererConfig); - - ramses::DisplayConfig dmaDisplayConfig = RendererTestUtils::CreateTestDisplayConfig(1u); - dmaDisplayConfig.setWindowRectangle(0, 0, ramses_internal::IntegrationScene::DefaultViewportWidth, ramses_internal::IntegrationScene::DefaultViewportHeight); - dmaDisplayConfig.setPlatformRenderNode("/dev/dri/renderD128"); - testFramework.createDisplay(dmaDisplayConfig); - - if(testCase.m_id == DmaOffscreenBufferTest_TwoDisplays) - { - ramses::DisplayConfig dmaDisplayConfig2 = RendererTestUtils::CreateTestDisplayConfig(2u); - dmaDisplayConfig2.setWindowRectangle(ramses_internal::IntegrationScene::DefaultViewportWidth, 0, ramses_internal::IntegrationScene::DefaultViewportWidth, ramses_internal::IntegrationScene::DefaultViewportHeight); - dmaDisplayConfig2.setPlatformRenderNode("/dev/dri/renderD128"); - testFramework.createDisplay(dmaDisplayConfig2); - } - - switch (testCase.m_id) - { - case DmaOffscreenBufferTest_BufferRead: - return runBufferReadTest(testFramework); - case DmaOffscreenBufferTest_BufferWrite: - return runBufferWriteTest(testFramework); - case DmaOffscreenBufferTest_BufferReadWrite: - return runBufferReadWriteTest(testFramework); - case DmaOffscreenBufferTest_ReadContentWithAlphaBlending: - return runContentWithAlphaBlendingTest(testFramework); - - case DmaOffscreenBufferTest_Format_ARGB8888: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_ARGB8888); - case DmaOffscreenBufferTest_Format_XRGB8888: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_XRGB8888); - case DmaOffscreenBufferTest_Format_XBGR8888: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_XBGR8888); - case DmaOffscreenBufferTest_Format_BGR888: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_BGR888); - - case DmaOffscreenBufferTest_UsageFlagRendering: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, GBM_BO_USE_RENDERING); - case DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT); - - case DmaOffscreenBufferTest_BufferSize_200x200: - return runBufferReadWriteTest(testFramework,0u, 200u, 200u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferReadWrite"); - case DmaOffscreenBufferTest_BufferSize_256x256: - return runBufferReadWriteTest(testFramework,0u, 256u, 256u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferWrite_256x256"); - case DmaOffscreenBufferTest_BufferSize_128x64: - return runBufferReadWriteTest(testFramework,0u, 128u, 64u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferWrite_128x64"); - case DmaOffscreenBufferTest_BufferSize_128x256: - return runBufferReadWriteTest(testFramework,0u, 128u, 256u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferWrite_128x256"); - - case DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead: - return runDoubleBufferedReadTest(testFramework); - case DmaOffscreenBufferTest_TwoDisplays: - return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, DefaultUsageFlags) - && runBufferReadWriteTest(testFramework,1u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, DefaultUsageFlags); - default: - assert(false && "undefined test case"); - } - - return false; -} - -template -ramses::sceneId_t DmaOffscreenBufferTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth, uint32_t vpHeight) -{ - const auto sceneId = testFramework.getScenesRegistry().createScene(sceneState, camPos, vpWidth, vpHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId, displayIdx); - - return sceneId; -} - -template -bool DmaOffscreenBufferTests::renderSceneToBufferAndExpectScreenshot(RendererTestsFramework& testFramework, const TestBufferInfo& testBuffer, uint32_t sceneState, const std::string& screenshotImage) -{ - //render a scene into OB, then read mapped memory for that OB and compare it with screenshot image - const auto sceneId = createAndShowScene(testFramework, testBuffer.displayIdx, sceneState, glm::vec3{0.f}, testBuffer.width, testBuffer.height); - testFramework.assignSceneToDisplayBuffer(sceneId, testBuffer.displaBufferId); - - //make two loops to make sure swap buffers was called twice after scene was assigned to OB - testFramework.flushRendererAndDoOneLoop(); - testFramework.flushRendererAndDoOneLoop(); - - //check content rendered into OB from two loops ago - const Image obMemImageResult = readMappedMemoryContentToImage(testBuffer); - return RendererTestUtils::CompareBitmapToImageInFile(obMemImageResult, screenshotImage, RendererTestUtils::DefaultMaxAveragePercentPerPixel, true); -} - -bool DmaOffscreenBufferTests::startSyncBuffer(const TestBufferInfo& bufferInfo) -{ - dma_buf_sync syncStartFlags = { 0 }; - switch (bufferInfo.accessRights) - { - case EMappingBufferAccessRight::ReadOnly: - syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ; - break; - case EMappingBufferAccessRight::WriteOnly: - syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE; - break; - case EMappingBufferAccessRight::ReadWrite: - syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW; - break; - } - assert(syncStartFlags.flags != 0); - - const auto syncStartResult = ioctl(bufferInfo.fd, DMA_BUF_IOCTL_SYNC, &syncStartFlags); - if(syncStartResult != 0) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Failed to start sync!"); - return false; - } - - return true; -} - -bool DmaOffscreenBufferTests::endSyncBuffer(const TestBufferInfo& bufferInfo) -{ - dma_buf_sync syncEndFlags = { 0 }; - switch (bufferInfo.accessRights) - { - case EMappingBufferAccessRight::ReadOnly: - syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ; - break; - case EMappingBufferAccessRight::WriteOnly: - syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE; - break; - case EMappingBufferAccessRight::ReadWrite: - syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW; - break; - } - assert(syncEndFlags.flags != 0); - - const auto syncEndResult = ioctl(bufferInfo.fd, DMA_BUF_IOCTL_SYNC, &syncEndFlags); - if(syncEndResult != 0) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Failed to end sync!"); - return false; - } - - return true; -} - -Image DmaOffscreenBufferTests::readMappedMemoryContentToImage(const TestBufferInfo& bufferInfo) -{ - if(!startSyncBuffer(bufferInfo)) - return {}; - - std::vector mappedMemImageData; - mappedMemImageData.reserve(bufferInfo.height * bufferInfo.width * 4u); - - auto readPixelARGB8888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 4u * col; - const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //R=p[2], G=p[1], B=p[0], A=p[3] - mappedMemImageData.push_back(pixelData[2]); - mappedMemImageData.push_back(pixelData[1]); - mappedMemImageData.push_back(pixelData[0]); - mappedMemImageData.push_back(pixelData[3]); - }; - - auto readPixelXRGB8888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 4u * col; - const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //R=p[2], G=p[1], B=p[0], A=255 - mappedMemImageData.push_back(pixelData[2]); - mappedMemImageData.push_back(pixelData[1]); - mappedMemImageData.push_back(pixelData[0]); - mappedMemImageData.push_back(255u); - }; - - auto readPixelXBGR8888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 4u * col; - const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //R=p[0], G=p[1], B=p[2], A=255 - mappedMemImageData.push_back(pixelData[0]); - mappedMemImageData.push_back(pixelData[1]); - mappedMemImageData.push_back(pixelData[2]); - mappedMemImageData.push_back(255u); - }; - - auto readPixelBGR888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 3u * col; - const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //R=p[0], G=p[1], B=p[2], A=255 - mappedMemImageData.push_back(pixelData[0]); - mappedMemImageData.push_back(pixelData[1]); - mappedMemImageData.push_back(pixelData[2]); - mappedMemImageData.push_back(255u); - }; - - switch(bufferInfo.fourccFormat) - { - case DRM_FORMAT_ARGB8888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - readPixelARGB8888(row, col); - break; - case DRM_FORMAT_XRGB8888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - readPixelXRGB8888(row, col); - break; - case DRM_FORMAT_XBGR8888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - readPixelXBGR8888(row, col); - break; - case DRM_FORMAT_BGR888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - readPixelBGR888(row, col); - break; - default: - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Trying to read content from unknown buffer format!"); - assert(false); - return {}; - } - - if(!endSyncBuffer(bufferInfo)) - return {}; - - return Image(bufferInfo.width, bufferInfo.height, std::move(mappedMemImageData)); -} - -bool DmaOffscreenBufferTests::writeTestContentToMappedMemory(const DmaOffscreenBufferTests::TestBufferInfo& bufferInfo) -{ - if(!startSyncBuffer(bufferInfo)) - return false; - - auto calculatePixelMagicValue = [&bufferInfo](uint32_t row, uint32_t col) - { - const uint8_t r = static_cast((1.f * col / bufferInfo.width) * 255); - const uint8_t g = static_cast((1.f * row / bufferInfo.height) * 255); - const uint8_t b = 128u; - const uint8_t a = 255u; - - return std::array{r, g, b, a}; - }; - - auto writePixelARGB8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * row + 4u * col; - uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //p[0]=B, p[1]=G, p[2]=R, p[3]=A - const auto val = calculatePixelMagicValue(row, col); - pixelData[0] = val[2]; - pixelData[1] = val[1]; - pixelData[2] = val[0]; - pixelData[3] = val[3]; - }; - - auto writePixelXRGB8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * row + 4u * col; - uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //p[0]=B, p[1]=G, p[2]=R, p[3]=X - const auto val = calculatePixelMagicValue(row, col); - pixelData[0] = val[2]; - pixelData[1] = val[1]; - pixelData[2] = val[0]; - pixelData[3] = 0u; - }; - - auto writePixelXBGR8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * row + 4u * col; - uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //p[0]=R, p[1]=G, p[2]=B, p[3]=X - const auto val = calculatePixelMagicValue(row, col); - pixelData[0] = val[0]; - pixelData[1] = val[1]; - pixelData[2] = val[2]; - pixelData[3] = 0u; - }; - - auto writePixelBGR8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) - { - const std::size_t pixelDataOffset = bufferInfo.stride * row + 3u * col; - uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; - - //p[0]=R, p[1]=G, p[2]=B - const auto val = calculatePixelMagicValue(row, col); - pixelData[0] = val[0]; - pixelData[1] = val[1]; - pixelData[2] = val[2]; - }; - - switch(bufferInfo.fourccFormat) - { - case DRM_FORMAT_ARGB8888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - writePixelARGB8888(row, col); - break; - case DRM_FORMAT_XRGB8888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - writePixelXRGB8888(row, col); - break; - case DRM_FORMAT_XBGR8888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - writePixelXBGR8888(row, col); - break; - case DRM_FORMAT_BGR888: - for(uint32_t row = 0u; row < bufferInfo.height; ++row) - for(uint32_t col = 0u; col < bufferInfo.width; ++col) - writePixelBGR8888(row, col); - break; - default: - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Trying to write test content to unknown buffer format!"); - assert(false); - return false; - } - - if(!endSyncBuffer(bufferInfo)) - return false; - - return true; -} - -bool DmaOffscreenBufferTests::createAndMapDmaOffscreenBuffer(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t bufferWidth, uint32_t bufferHeight, uint32_t fourccFormat, uint32_t bufferUsageFlags, EMappingBufferAccessRight accessRights, TestBufferInfo& bufferInfo, bool disableClearing) -{ - bufferInfo.displayIdx = displayIdx; - bufferInfo.width = bufferWidth; - bufferInfo.height = bufferHeight; - bufferInfo.fourccFormat = fourccFormat; - - bufferInfo.displaBufferId = testFramework.createDmaOffscreenBuffer(displayIdx, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, DRM_FORMAT_MOD_INVALID); - if(!bufferInfo.displaBufferId.isValid()) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Invalid offscreen buffer handle!"); - return false; - } - - if(disableClearing) - { - testFramework.setClearFlags(displayIdx, bufferInfo.displaBufferId, ramses::EClearFlags_None); - testFramework.flushRendererAndDoOneLoop(); - } - - if(!testFramework.getDmaOffscreenBufferFDAndStride(displayIdx, bufferInfo.displaBufferId, bufferInfo.fd, bufferInfo.stride)) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Could not get FD and stride for DMA offscreen buffer!"); - return false; - } - - bufferInfo.accessRights = accessRights; - int accessRightsFlags = 0; - switch(accessRights) - { - case EMappingBufferAccessRight::ReadOnly: - accessRightsFlags = PROT_READ; - break; - case EMappingBufferAccessRight::WriteOnly: - accessRightsFlags = PROT_WRITE; - break; - case EMappingBufferAccessRight::ReadWrite: - accessRightsFlags = PROT_READ | PROT_WRITE; - break; - } - assert(accessRightsFlags != 0); - - const auto fileSize = lseek(bufferInfo.fd, 0u, SEEK_END); - bufferInfo.mappedMemory = mmap(nullptr, fileSize, accessRightsFlags, MAP_SHARED, bufferInfo.fd, 0u); - if(bufferInfo.mappedMemory == MAP_FAILED) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Failed to map memory!"); - return false; - } - - return true; -} - -bool DmaOffscreenBufferTests::runBufferWriteTest(RendererTestsFramework& testFramework) -{ - constexpr uint32_t bufferWidth = 256u; - constexpr uint32_t bufferHeight = 256u; - constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; - constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; - - //write some content into mapped memory of DMA OB, then link to tex consumer and make screenshot test - if(!createAndMapDmaOffscreenBuffer(testFramework, 0u, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::WriteOnly, testBuffer1)) - return false; - - if(!writeTestContentToMappedMemory(testBuffer1)) - return false; - - const auto sceneIdConsumer = createAndShowScene(testFramework, 0u, TextureLinkScene::DATA_CONSUMER, m_cameraTranslation); - testFramework.createBufferDataLink(testBuffer1.displaBufferId, sceneIdConsumer, TextureLinkScene::DataConsumerId); - - return testFramework.renderAndCompareScreenshot("DmaOffscreenBufferTest_BufferWrite"); -} - -bool DmaOffscreenBufferTests::runBufferReadTest(RendererTestsFramework &testFramework) -{ - constexpr uint32_t bufferWidth = 200u; - constexpr uint32_t bufferHeight = 200u; - constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; - constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; - - if(!createAndMapDmaOffscreenBuffer(testFramework, 0u, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer1, false)) - return false; - - return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::THREE_TRIANGLES, "MultipleTrianglesScene_ThreeTriangles"); -} - -bool DmaOffscreenBufferTests::runContentWithAlphaBlendingTest(RendererTestsFramework &testFramework) -{ - constexpr uint32_t bufferWidth = 200u; - constexpr uint32_t bufferHeight = 200u; - constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; - constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; - - if(!createAndMapDmaOffscreenBuffer(testFramework, 0u, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer1, false)) - return false; - - return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::ALPHA_BLENDING, "MultipleTrianglesScene_AlphaBlending"); -} - -bool DmaOffscreenBufferTests::runBufferReadWriteTest(RendererTestsFramework &testFramework, uint32_t displayIdx, uint32_t bufferWidth, uint32_t bufferHeight, uint32_t fourccFormat, uint32_t bufferUsageFlags, const std::string& screenshotImage) -{ - if(!createAndMapDmaOffscreenBuffer(testFramework, displayIdx, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadWrite, testBuffer1)) - return false; - - //write color gradient to OB - if(!writeTestContentToMappedMemory(testBuffer1)) - return false; - - //render triangles scene above (since clearing is disabled) the written color gradient - return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::THREE_TRIANGLES, screenshotImage); -} - -bool DmaOffscreenBufferTests::runDoubleBufferedReadTest(RendererTestsFramework& testFramework) -{ - //The test creates a scene and 2 DMA OBs, then keeps alternating the scene between both OBs each frame - //The contents of the DMA OB are checked against a screenshot from 2 frames ago to make sure the content was already rendered - constexpr uint32_t bufferWidth = 200u; - constexpr uint32_t bufferHeight = 200u; - constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; - constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; - const float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel; - - //Create a dummy scene and assign to FB to make sure swap buffers is called every frame - createAndShowScene(testFramework, 0u, MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3{0.f}, bufferWidth, bufferHeight); - - const auto sceneIdProvider = createAndShowScene(testFramework, 0u, MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3{0.f}, bufferWidth, bufferHeight); - - if(!createAndMapDmaOffscreenBuffer(testFramework, 0, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer1)) - return false; - if(!createAndMapDmaOffscreenBuffer(testFramework, 0, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer2)) - return false; - - testFramework.assignSceneToDisplayBuffer(sceneIdProvider, testBuffer1.displaBufferId); - testFramework.flushRendererAndDoOneLoop(); - - //change scene state and render to OB2 - testFramework.getScenesRegistry().setSceneState(sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(sceneIdProvider).flush(); - testFramework.assignSceneToDisplayBuffer(sceneIdProvider, testBuffer2.displaBufferId); - testFramework.flushRendererAndDoOneLoop(); - - //check result of OB1 from two loops ago - const Image ob1MemImageResult = readMappedMemoryContentToImage(testBuffer1); - if(!RendererTestUtils::CompareBitmapToImageInFile(ob1MemImageResult, "MultipleTrianglesScene_ThreeTriangles", expectedPixelError, true)) - return false; - - testFramework.getScenesRegistry().destroyScenes(); - testFramework.flushRendererAndDoOneLoop(); - - //check result of OB2 from two loops ago - const Image ob2MemImageResult = readMappedMemoryContentToImage(testBuffer2); - if(!RendererTestUtils::CompareBitmapToImageInFile(ob2MemImageResult, "MultipleTrianglesScene_RenderingOrderChanged", expectedPixelError, true)) - return false; - - return true; -} - -bool DmaOffscreenBufferTests::runTwoDisplaysTest(RendererTestsFramework &testFramework) -{ - if(!createAndMapDmaOffscreenBuffer(testFramework, 0u, 256u, 256u, DefaultFourccBufferFormat, DefaultUsageFlags, EMappingBufferAccessRight::ReadWrite, testBuffer1)) - return false; - if(!createAndMapDmaOffscreenBuffer(testFramework, 1u, 128u, 64u, DefaultFourccBufferFormat, DefaultUsageFlags, EMappingBufferAccessRight::ReadWrite, testBuffer2)) - return false; - - //write color gradient to OBs - if(!writeTestContentToMappedMemory(testBuffer1)) - return false; - if(!writeTestContentToMappedMemory(testBuffer2)) - return false; - - if(!renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::THREE_TRIANGLES, "DmaOffscreenBufferTest_BufferWrite_256x256")) - return false; - return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer2, MultipleTrianglesScene::THREE_TRIANGLES, "DmaOffscreenBufferTest_BufferWrite_128x64"); -} diff --git a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.h b/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.h deleted file mode 100644 index 6e915f0c5..000000000 --- a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.h +++ /dev/null @@ -1,106 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DMAOFFSCREENBUFFERTESTS_H -#define RAMSES_DMAOFFSCREENBUFFERTESTS_H - -#include "IRendererTest.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include -#include -#include - -class DmaOffscreenBufferTests : public IRendererTest -{ -public: - explicit DmaOffscreenBufferTests() = default; - - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - enum - { - DmaOffscreenBufferTest_BufferRead, - DmaOffscreenBufferTest_BufferWrite, - DmaOffscreenBufferTest_BufferReadWrite, - DmaOffscreenBufferTest_ReadContentWithAlphaBlending, - - DmaOffscreenBufferTest_Format_ARGB8888, - DmaOffscreenBufferTest_Format_XRGB8888, - DmaOffscreenBufferTest_Format_XBGR8888, - DmaOffscreenBufferTest_Format_BGR888, - - DmaOffscreenBufferTest_UsageFlagRendering, - DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut, - - DmaOffscreenBufferTest_BufferSize_200x200, - DmaOffscreenBufferTest_BufferSize_256x256, - DmaOffscreenBufferTest_BufferSize_128x64, - DmaOffscreenBufferTest_BufferSize_128x256, - - DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead, - DmaOffscreenBufferTest_TwoDisplays, - }; - - enum class EMappingBufferAccessRight - { - ReadOnly, - WriteOnly, - ReadWrite - }; - - struct TestBufferInfo - { - uint32_t displayIdx = 0u; - ramses::displayBufferId_t displaBufferId; - int fd = -1; - uint32_t stride = 0u; - void* mappedMemory = nullptr; - EMappingBufferAccessRight accessRights = EMappingBufferAccessRight::ReadOnly; - uint32_t width; - uint32_t height; - uint32_t fourccFormat; - }; - - template - ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth = ramses_internal::IntegrationScene::DefaultViewportWidth, uint32_t vpHeight = ramses_internal::IntegrationScene::DefaultViewportHeight); - template - bool renderSceneToBufferAndExpectScreenshot(RendererTestsFramework& testFramework, const TestBufferInfo& testBuffer, uint32_t sceneState, const std::string& screenshotImage); - - ramses_internal::Image readMappedMemoryContentToImage(const TestBufferInfo& bufferInfo); - bool writeTestContentToMappedMemory(const TestBufferInfo& bufferInfo); - - bool createAndMapDmaOffscreenBuffer(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t bufferWidth, uint32_t bufferHeight, uint32_t fourccFormat, uint32_t bufferUsageFlags, EMappingBufferAccessRight accessRights, TestBufferInfo& bufferInfo, bool disableClearing = true); - bool startSyncBuffer(const TestBufferInfo& bufferInfo); - bool endSyncBuffer(const TestBufferInfo& bufferInfo); - - bool runBufferWriteTest(RendererTestsFramework& testFramework); - bool runBufferReadTest(RendererTestsFramework& testFramework); - bool runContentWithAlphaBlendingTest(RendererTestsFramework& testFramework); - bool runBufferReadWriteTest(RendererTestsFramework& testFramework, - uint32_t displayIdx = 0u, - uint32_t bufferWidth = DefaultBufferWidth, - uint32_t bufferHeight = DefaultBufferHeight, - uint32_t fourccFormat = DefaultFourccBufferFormat, - uint32_t bufferUsageFlags = DefaultUsageFlags, - const std::string& screenshotImage = "DmaOffscreenBufferTest_BufferReadWrite"); - bool runDoubleBufferedReadTest(RendererTestsFramework& testFramework); - bool runTwoDisplaysTest(RendererTestsFramework& testFramework); - - static constexpr uint32_t DefaultBufferWidth = 200u; - static constexpr uint32_t DefaultBufferHeight = 200u; - static constexpr uint32_t DefaultUsageFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; - static constexpr uint32_t DefaultFourccBufferFormat = DRM_FORMAT_ARGB8888; - - const glm::vec3 m_cameraTranslation{ 0.f, 0.f, 8.f }; - TestBufferInfo testBuffer1; - TestBufferInfo testBuffer2; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RendererLifecycleTests/ExternalWindowTests.cpp b/integration/SandwichTests/RendererTests/RendererLifecycleTests/ExternalWindowTests.cpp deleted file mode 100644 index a40c9594e..000000000 --- a/integration/SandwichTests/RendererTests/RendererLifecycleTests/ExternalWindowTests.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererLifecycleTests.h" -#include "TestScenes/MultipleTrianglesScene.h" - -#include "ramses-renderer-api/DisplayConfig.h" -#include "DisplayConfigImpl.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "Utils/ThreadLocalLog.h" - -#ifdef __WIN32 -#include "Window_Windows/Window_Windows.h" -#elif __linux__ -#include "Platform_X11/Window_X11.h" -#endif - -namespace ramses_internal -{ - TEST_F(ARendererLifecycleTest, CanRenderSceneToExternallyOwnedWindow) - { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); - testScenesAndRenderer.initializeRenderer(); - - class DummyEventHandler : public IWindowEventHandler - { - public: - ~DummyEventHandler() override {}; - - void onKeyEvent(EKeyEventType , uint32_t , EKeyCode ) override {} - void onMouseEvent(EMouseEventType , int32_t , int32_t ) override {} - void onClose() override {} - void onResize(uint32_t , uint32_t ) override {} - void onWindowMove(int32_t , int32_t) override {} - }; - - ramses::DisplayConfig dispConfigExternalWindow = RendererTestUtils::CreateTestDisplayConfig(0u, false); - dispConfigExternalWindow.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); - DummyEventHandler dummyEventHandler; - - auto dispConfig = RendererTestUtils::CreateTestDisplayConfig(0u, false); - dispConfig.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); - -#ifdef __WIN32 - Window_Windows window(dispConfigExternalWindow.m_impl.get().getInternalDisplayConfig(), dummyEventHandler, 1); - ASSERT_TRUE(window.init()); - dispConfig.setWindowsWindowHandle(window.getNativeWindowHandle()); -#elif __linux__ - ThreadLocalLog::SetPrefix(1); - Window_X11 window(dispConfigExternalWindow.m_impl.get().getInternalDisplayConfig(), dummyEventHandler, 1); - ASSERT_TRUE(window.init()); - dispConfig.setX11WindowHandle(window.getNativeWindowHandle()); -#endif - - const ramses::displayId_t display = testRenderer.createDisplay(dispConfig); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); - - testScenesAndRenderer.publish(sceneId); - testScenesAndRenderer.flush(sceneId); - testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); - - ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); - - testScenesAndRenderer.unpublish(sceneId); - testScenesAndRenderer.destroyRenderer(); - } -} diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.cpp b/integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.cpp deleted file mode 100644 index ec67b9053..000000000 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.cpp +++ /dev/null @@ -1,623 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererTestsFramework.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "DisplayConfigImpl.h" -#include "IRendererTest.h" -#include "DisplayConfigImpl.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "RendererTestUtils.h" - -RendererTestsFramework::RendererTestsFramework(bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) - : m_generateScreenshots(generateScreenshots) - , m_testScenesAndRenderer(config) - , m_testRenderer(m_testScenesAndRenderer.getTestRenderer()) - , m_activeTestCase(nullptr) - , m_elapsedTime(0u) -{ -} - -RendererTestsFramework::~RendererTestsFramework() -{ - assert(m_activeTestCase == nullptr); - for(const auto& testCase : m_testCases) - { - delete testCase; - } - - destroyDisplays(); - m_testScenesAndRenderer.destroyRenderer(); -} - -void RendererTestsFramework::initializeRenderer() -{ - m_testScenesAndRenderer.initializeRenderer(); -} - -void RendererTestsFramework::initializeRenderer(const ramses::RendererConfig& rendererConfig) -{ - m_testScenesAndRenderer.initializeRenderer(rendererConfig); -} - -void RendererTestsFramework::destroyRenderer() -{ - m_testScenesAndRenderer.destroyRenderer(); -} - -ramses::displayId_t RendererTestsFramework::createDisplay(const ramses::DisplayConfig& displayConfig) -{ - m_testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); - const ramses::displayId_t displayId = m_testRenderer.createDisplay(displayConfig); - m_testRenderer.setLoopMode(ramses::ELoopMode::UpdateAndRender); - if (displayId != ramses::displayId_t::Invalid()) - { - m_displays.push_back({ displayId, displayConfig, {}, {} }); - } - - return displayId; -} - -ramses::displayBufferId_t RendererTestsFramework::getDisplayFramebufferId(uint32_t testDisplayIdx) const -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - return m_testRenderer.getDisplayFramebufferId(displayId); -} - -ramses_internal::TestRenderer& RendererTestsFramework::getTestRenderer() -{ - return m_testRenderer; -} - -RenderingTestCase& RendererTestsFramework::createTestCase(uint32_t id, IRendererTest& rendererTest, const std::string& name) -{ - RenderingTestCase* testCase = new RenderingTestCase(id, rendererTest, name, true); - m_testCases.push_back(testCase); - - return *testCase; -} - -RenderingTestCase& RendererTestsFramework::createTestCaseWithDefaultDisplay(uint32_t id, IRendererTest& rendererTest, const std::string& name, bool iviWindowStartVisible) -{ - RenderingTestCase& testCase = createTestCase(id, rendererTest, name); - ramses::DisplayConfig displayConfig = RendererTestUtils::CreateTestDisplayConfig(0, iviWindowStartVisible); - displayConfig.setWindowRectangle(0, 0, ramses_internal::IntegrationScene::DefaultViewportWidth, ramses_internal::IntegrationScene::DefaultViewportHeight); - testCase.m_displayConfigs.push_back(displayConfig); - - return testCase; -} - -RenderingTestCase& RendererTestsFramework::createTestCaseWithoutRenderer(uint32_t id, IRendererTest &rendererTest, const std::string &name) -{ - RenderingTestCase* testCase = new RenderingTestCase(id, rendererTest, name, false); - m_testCases.push_back(testCase); - - return *testCase; -} - -TestScenes& RendererTestsFramework::getScenesRegistry() -{ - return m_testScenesAndRenderer.getScenesRegistry(); -} - -ramses::RamsesClient& RendererTestsFramework::getClient() -{ - return m_testScenesAndRenderer.getClient(); -} - -void RendererTestsFramework::setSceneMapping(ramses::sceneId_t sceneId, uint32_t testDisplayIdx) -{ - m_testRenderer.setSceneMapping(sceneId, m_displays[testDisplayIdx].displayId); -} - -bool RendererTestsFramework::getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) -{ - return m_testRenderer.getSceneToState(sceneId, state); -} - -bool RendererTestsFramework::getSceneToRendered(ramses::sceneId_t sceneId, uint32_t testDisplayIdx) -{ - setSceneMapping(sceneId, testDisplayIdx); - return getSceneToState(sceneId, ramses::RendererSceneState::Rendered); -} - -void RendererTestsFramework::dispatchRendererEvents(ramses::IRendererEventHandler& eventHandler, ramses::IRendererSceneControlEventHandler& sceneControlEventHandler) -{ - m_testRenderer.dispatchEvents(eventHandler, sceneControlEventHandler); -} - -ramses::displayBufferId_t RendererTestsFramework::createOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount, ramses::EDepthBufferType depthBufferType) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - ramses::displayBufferId_t buffer = m_testRenderer.createOffscreenBuffer(displayId, width, height, interruptible, sampleCount, depthBufferType); - m_displays[testDisplayIdx].offscreenBuffers.push_back(buffer); - return buffer; -} - -ramses::displayBufferId_t RendererTestsFramework::createDmaOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - ramses::displayBufferId_t buffer = m_testRenderer.createDmaOffscreenBuffer(displayId, width, height, bufferFourccFormat, bufferUsageFlags, modifier); - m_displays[testDisplayIdx].offscreenBuffers.push_back(buffer); - return buffer; -} - -bool RendererTestsFramework::getDmaOffscreenBufferFDAndStride(uint32_t testDisplayIdx, ramses::displayBufferId_t displayBufferId, int &fd, uint32_t &stride) const -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - return m_testRenderer.getDmaOffscreenBufferFDAndStride(displayId, displayBufferId, fd, stride); -} - -void RendererTestsFramework::destroyOffscreenBuffer(uint32_t testDisplayIdx, ramses::displayBufferId_t buffer) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - auto& offscreenBuffers = m_displays[testDisplayIdx].offscreenBuffers; - - auto bufferIter = ramses_internal::find_c(offscreenBuffers, buffer); - assert(bufferIter != offscreenBuffers.end()); - offscreenBuffers.erase(bufferIter); - - m_testRenderer.destroyOffscreenBuffer(displayId, buffer); -} - -ramses::streamBufferId_t RendererTestsFramework::createStreamBuffer(uint32_t testDisplayIdx, ramses::waylandIviSurfaceId_t source) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - ramses::streamBufferId_t buffer = m_testRenderer.createStreamBuffer(displayId, source); - m_displays[testDisplayIdx].streamBuffers.push_back(buffer); - return buffer; -} - -void RendererTestsFramework::destroyStreamBuffer(uint32_t testDisplayIdx, ramses::streamBufferId_t buffer) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - auto& streamBuffers = m_displays[testDisplayIdx].streamBuffers; - - auto bufferIter = ramses_internal::find_c(streamBuffers, buffer); - assert(bufferIter != streamBuffers.end()); - streamBuffers.erase(bufferIter); - - m_testRenderer.destroyStreamBuffer(displayId, buffer); -} - -void RendererTestsFramework::assignSceneToDisplayBuffer(ramses::sceneId_t sceneId, ramses::displayBufferId_t buffer, int32_t renderOrder) -{ - m_testRenderer.assignSceneToDisplayBuffer(sceneId, buffer, renderOrder); -} - -void RendererTestsFramework::createBufferDataLink(ramses::displayBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) -{ - m_testRenderer.createBufferDataLink(providerBuffer, consumerScene, consumerTag); -} - -void RendererTestsFramework::createBufferDataLink(ramses::streamBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) -{ - m_testRenderer.createBufferDataLink(providerBuffer, consumerScene, consumerTag); -} - -void RendererTestsFramework::createDataLink(ramses::sceneId_t providerScene, ramses::dataProviderId_t providerTag, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) -{ - m_testRenderer.createDataLink(providerScene, providerTag, consumerScene, consumerTag); -} - -void RendererTestsFramework::removeDataLink(ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) -{ - m_testRenderer.removeDataLink(consumerScene, consumerTag); -} - -void RendererTestsFramework::setClearFlags(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, uint32_t clearFlags) -{ - assert(testDisplayIdx < m_displays.size()); - m_testRenderer.setClearFlags(m_displays[testDisplayIdx].displayId, ob, clearFlags); - // clearing state is persistent if display kept for next test, force re-init - m_forceDisplaysReinitForNextTestCase = true; -} - -void RendererTestsFramework::setClearColor(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, const glm::vec4& clearColor) -{ - assert(testDisplayIdx < m_displays.size()); - m_testRenderer.setClearColor(m_displays[testDisplayIdx].displayId, ob, clearColor); - // clear color change is persistent if display kept for next test, force re-init - m_forceDisplaysReinitForNextTestCase = true; -} - -void RendererTestsFramework::publishAndFlushScene(ramses::sceneId_t sceneId) -{ - m_testScenesAndRenderer.flush(sceneId); - m_testScenesAndRenderer.publish(sceneId); -} - -void RendererTestsFramework::flushRendererAndDoOneLoop() -{ - m_testRenderer.flushRenderer(); - m_testRenderer.doOneLoop(); -} - -bool RendererTestsFramework::renderAndCompareScreenshot(const std::string& expectedImageName, uint32_t testDisplayIdx, float maxAveragePercentErrorPerPixel, bool readPixelsTwice, bool saveDiffOnError) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - const ramses::DisplayConfig& displayConfig = m_displays[testDisplayIdx].config; - - return compareScreenshotInternal( - expectedImageName, - displayId, - {}, - maxAveragePercentErrorPerPixel, - 0u, - 0u, - displayConfig.m_impl.get().getInternalDisplayConfig().getDesiredWindowWidth(), - displayConfig.m_impl.get().getInternalDisplayConfig().getDesiredWindowHeight(), - readPixelsTwice, - saveDiffOnError); -} - -bool RendererTestsFramework::renderAndCompareScreenshotOffscreenBuffer(const std::string& expectedImageName, uint32_t testDisplayIdx, ramses::displayBufferId_t displayBuffer, uint32_t width, uint32_t height, float maxAveragePercentErrorPerPixel) -{ - assert(testDisplayIdx < m_displays.size()); - const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; - - return compareScreenshotInternal( - expectedImageName, - displayId, - displayBuffer, - maxAveragePercentErrorPerPixel, - 0u, - 0u, - width, - height, - false, - true); -} - -bool RendererTestsFramework::renderAndCompareScreenshotSubimage(const std::string& expectedImageName, uint32_t subimageX, uint32_t subimageY, uint32_t subimageWidth, uint32_t subimageHeight, float maxAveragePercentErrorPerPixel, bool readPixelsTwice) -{ - return compareScreenshotInternal(expectedImageName, m_displays[0].displayId, {}, maxAveragePercentErrorPerPixel, subimageX, subimageY, subimageWidth, subimageHeight, readPixelsTwice, true); -} - -void RendererTestsFramework::setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender) -{ - m_testRenderer.setFrameTimerLimits(limitForClientResourcesUpload, limitForOffscreenBufferRender); - m_testRenderer.flushRenderer(); -} - -bool RendererTestsFramework::compareScreenshotInternal( - const std::string& expectedImageName, - ramses::displayId_t displayId, - ramses::displayBufferId_t bufferId, - float maxAveragePercentErrorPerPixel, - uint32_t subimageX, - uint32_t subimageY, - uint32_t subimageWidth, - uint32_t subimageHeight, - bool readPixelsTwice, - bool saveDiffOnError) -{ - if (m_generateScreenshots) - { - m_testRenderer.saveScreenshotForDisplay(displayId, bufferId, subimageX, subimageY, subimageWidth, subimageHeight, expectedImageName); - return true; - } - - const bool comparisonResult = m_testRenderer.performScreenshotCheck(displayId, bufferId, subimageX, subimageY, subimageWidth, subimageHeight, expectedImageName, maxAveragePercentErrorPerPixel, readPixelsTwice, saveDiffOnError); - - if (!comparisonResult && saveDiffOnError) - { - assert(m_activeTestCase != nullptr); - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Screenshot comparison failed for rendering test case: " << m_activeTestCase->m_name << " -> expected screenshot: " << expectedImageName); - } - - return comparisonResult; -} - -bool RendererTestsFramework::NameMatchesFilter(const std::string& name, const std::vector& filters) -{ - if (ramses_internal::contains_c(filters, "*")) - { - return true; - } - - for(const auto& filter : filters) - { - if (name.find(filter) != std::string::npos) - { - return true; - } - } - - return false; -} - -void RendererTestsFramework::filterTestCases(const std::vector& filterIn, const std::vector& filterOut) -{ - const bool processFilterIn = !filterIn.empty(); - const bool processFilterOut = !filterOut.empty(); - - if (!processFilterIn && !processFilterOut) - { - return; - } - - RenderingTestCases testCases; - testCases.swap(m_testCases); - - for(const auto& testCase : testCases) - { - const auto& testCaseName = testCase->m_name; - const bool excludedByFilterIn = processFilterIn && !NameMatchesFilter(testCaseName, filterIn); - const bool excludedByFilterOut = processFilterOut && NameMatchesFilter(testCaseName, filterOut); - - if (excludedByFilterIn || excludedByFilterOut) - { - delete testCase; - } - else - { - m_testCases.push_back(testCase); - } - } -} - -bool areEqual(const DisplayConfigVector& a, const DisplayConfigVector& b) -{ - if (a.size() != b.size()) - { - return false; - } - - for (size_t i = 0; i < a.size(); ++i) - { - const ramses_internal::DisplayConfig& displayConfigA = a[i].m_impl.get().getInternalDisplayConfig(); - const ramses_internal::DisplayConfig& displayConfigB = b[i].m_impl.get().getInternalDisplayConfig(); - - if (displayConfigA != displayConfigB) - { - return false; - } - } - - return true; -} - -void RendererTestsFramework::sortTestCases() -{ - // split test cases into groups using the same renderer/displays setup - const size_t numCases = m_testCases.size(); - - RenderingTestCases unsortedCases; - unsortedCases.swap(m_testCases); - std::vector processedFlags(numCases, false); - - while (m_testCases.size() != numCases) - { - uint32_t nextUnprocessed = 0u; - while (processedFlags[nextUnprocessed]) - { - ++nextUnprocessed; - } - m_testCases.push_back(unsortedCases[nextUnprocessed]); - processedFlags[nextUnprocessed] = true; - - const DisplayConfigVector& groupSetup = unsortedCases[nextUnprocessed]->m_displayConfigs; - for (uint32_t i = nextUnprocessed + 1u; i < numCases; ++i) - { - if (processedFlags[i]) - { - continue; - } - - RenderingTestCase* testCase = unsortedCases[i]; - if (areEqual(groupSetup, testCase->m_displayConfigs)) - { - m_testCases.push_back(testCase); - processedFlags[i] = true; - } - } - } -} - -bool RendererTestsFramework::currentDisplaySetupMatchesTestCase(const RenderingTestCase& testCase) const -{ - if (testCase.m_displayConfigs.size() != m_displays.size()) - { - return false; - } - - for (size_t i = 0u; i < m_displays.size(); ++i) - { - assert(i < m_displays.size()); - - ramses_internal::DisplayConfig currentDisplayConfig = m_displays[i].config.m_impl.get().getInternalDisplayConfig(); - ramses_internal::DisplayConfig requestedDisplayConfig = testCase.m_displayConfigs[i].m_impl.get().getInternalDisplayConfig(); - - // ignore wayland ID in comparison as this is different for every test display config - requestedDisplayConfig.setWaylandIviSurfaceID(currentDisplayConfig.getWaylandIviSurfaceID()); - - if (currentDisplayConfig != requestedDisplayConfig) - { - return false; - } - } - - return true; -} - -bool RendererTestsFramework::applyRendererAndDisplaysConfigurationForTest(const RenderingTestCase& testCase) -{ - if(!testCase.m_defaultRendererRequired) - { - //destroy renderer and displays if needed - if(m_testRenderer.isRendererInitialized()) - { - destroyDisplays(); - destroyRenderer(); - } - - return true; - } - - //create renderer and displays if needed - if(!m_testRenderer.isRendererInitialized()) - initializeRenderer(); - - if (!m_forceDisplaysReinitForNextTestCase && currentDisplaySetupMatchesTestCase(testCase)) - { - return true; - } - - destroyDisplays(); - - for(const auto& displayConfig : testCase.m_displayConfigs) - { - const ramses::displayId_t displayId = createDisplay(displayConfig); - - if (displayId == ramses::displayId_t::Invalid()) - return false; - } - m_forceDisplaysReinitForNextTestCase = false; - - return true; -} - -void RendererTestsFramework::destroyDisplays() -{ - for(const auto& display : m_displays) - { - m_testRenderer.destroyDisplay(display.displayId); - } - m_displays.clear(); -} - -void RendererTestsFramework::destroyScenes() -{ - m_testScenesAndRenderer.getScenesRegistry().destroyScenes(); -} - -void RendererTestsFramework::destroyBuffers() -{ - for (auto& display : m_displays) - { - for(const auto buffer : display.offscreenBuffers) - m_testRenderer.destroyOffscreenBuffer(display.displayId, buffer); - display.offscreenBuffers.clear(); - for (const auto buffer : display.streamBuffers) - m_testRenderer.destroyStreamBuffer(display.displayId, buffer); - display.streamBuffers.clear(); - } -} - -bool RendererTestsFramework::runAllTests() -{ - bool testResult = true; - - const uint64_t startTime = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - - assert(m_activeTestCase == nullptr); - m_passedTestCases.clear(); - m_failedTestCases.clear(); - - sortTestCases(); - - for(const auto& testCase : m_testCases) - { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "====== Running rendering test case: " << testCase->m_name << " ======"); - printf("======\nRunning rendering test case: %s\n======\n", testCase->m_name.c_str()); - fflush(stdout); - - if (applyRendererAndDisplaysConfigurationForTest(*testCase)) - { - testResult &= runTestCase(*testCase); - } - else - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "Renderer/display initialization failed for rendering test case: " << testCase->m_name); - testResult = false; - } - - destroyScenes(); - destroyBuffers(); - } - - const uint64_t endTime = ramses_internal::PlatformTime::GetMillisecondsMonotonic(); - m_elapsedTime = endTime - startTime; - - return testResult; -} - -bool RendererTestsFramework::runTestCase(RenderingTestCase& testCase) -{ - m_activeTestCase = &testCase; - - const bool testResult = m_activeTestCase->m_rendererTest.run(*this, testCase); - if (testResult) - { - m_passedTestCases.push_back(m_activeTestCase); - } - else - { - m_failedTestCases.push_back(m_activeTestCase); - } - - m_activeTestCase = nullptr; - - return testResult; -} - -std::string RendererTestsFramework::generateReport() const -{ - ramses_internal::StringOutputStream str; - - str << "\n\n--- Rendering test report begin ---\n"; - { - str << "\n Passed rendering test cases: " << m_passedTestCases.size(); - for(const auto& testCase : m_passedTestCases) - { - str << "\n " << testCase->m_name; - } - - str << "\n\n Failed rendering test cases: " << m_failedTestCases.size(); - for (const auto& testCase : m_failedTestCases) - { - str << "\n " << testCase->m_name; - } - - if (m_failedTestCases.empty()) - { - str << "\n"; - str << "\n ------------------"; - str << "\n --- ALL PASSED ---"; - str << "\n ------------------"; - } - else - { - str << "\n"; - str << "\n !!!!!!!!!!!!!!!!!!!!"; - str << "\n !!! FAILED TESTS !!!"; - str << "\n !!!!!!!!!!!!!!!!!!!!"; - } - - str << "\n\n Total time elapsed: " << static_cast(m_elapsedTime) * 0.001f << " s"; - } - str << "\n\n--- End of rendering test report ---\n\n"; - - return str.release(); -} - -const RendererTestsFramework::TestDisplays& RendererTestsFramework::getDisplays() const -{ - return m_displays; -} diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.h b/integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.h deleted file mode 100644 index a8b2b6921..000000000 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/RendererTestsFramework.h +++ /dev/null @@ -1,158 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERTESTSFRAMEWORK_H -#define RAMSES_RENDERERTESTSFRAMEWORK_H - -#include "TestScenesAndRenderer.h" -#include "RenderingTestCase.h" -#include "Collections/Pair.h" -#include "RendererTestUtils.h" - -#include -#include - -class IRendererTest; - -using RenderingTestCases = std::vector; - -namespace ramses -{ - class IRendererEventHandler; - class IRendererSceneControlEventHandler; -} - -class RendererTestsFramework -{ -public: - explicit RendererTestsFramework(bool generateScreenshots, const ramses::RamsesFrameworkConfig& config); - ~RendererTestsFramework(); - - void initializeRenderer(); - void initializeRenderer(const ramses::RendererConfig& rendererConfig); - void destroyRenderer(); - ramses::displayId_t createDisplay(const ramses::DisplayConfig& displayConfig); - [[nodiscard]] ramses::displayBufferId_t getDisplayFramebufferId(uint32_t testDisplayIdx) const; - void destroyDisplays(); - ramses_internal::TestRenderer& getTestRenderer(); - TestScenes& getScenesRegistry(); - ramses::RamsesClient& getClient(); - - RenderingTestCase& createTestCase(uint32_t id, IRendererTest& rendererTest, const std::string& name); - RenderingTestCase& createTestCaseWithDefaultDisplay(uint32_t id, IRendererTest& rendererTest, const std::string& name, bool iviWindowStartVisible = true); - RenderingTestCase& createTestCaseWithoutRenderer(uint32_t id, IRendererTest& rendererTest, const std::string& name); - - void setSceneMapping(ramses::sceneId_t sceneId, uint32_t testDisplayIdx); - bool getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); - bool getSceneToRendered(ramses::sceneId_t sceneId, uint32_t testDisplayIdx = 0); - - void dispatchRendererEvents(ramses::IRendererEventHandler& eventHandler, ramses::IRendererSceneControlEventHandler& sceneControlEventHandler); - ramses::displayBufferId_t createOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount = 0u, ramses::EDepthBufferType depthBufferType = ramses::EDepthBufferType::DepthStencil); - ramses::displayBufferId_t createDmaOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier); - bool getDmaOffscreenBufferFDAndStride(uint32_t testDisplayIdx, ramses::displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; - void destroyOffscreenBuffer(uint32_t testDisplayIdx, ramses::displayBufferId_t buffer); - ramses::streamBufferId_t createStreamBuffer(uint32_t testDisplayIdx, ramses::waylandIviSurfaceId_t source); - void destroyStreamBuffer(uint32_t testDisplayIdx, ramses::streamBufferId_t buffer); - void assignSceneToDisplayBuffer(ramses::sceneId_t sceneId, ramses::displayBufferId_t buffer, int32_t renderOrder = 0); - void createBufferDataLink(ramses::displayBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); - void createBufferDataLink(ramses::streamBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); - void createDataLink(ramses::sceneId_t providerScene, ramses::dataProviderId_t providerTag, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); - void removeDataLink(ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); - void setClearFlags(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, uint32_t clearFlags); - void setClearColor(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, const glm::vec4& clearColor); - void publishAndFlushScene(ramses::sceneId_t sceneId); - void flushRendererAndDoOneLoop(); - bool renderAndCompareScreenshot( - const std::string& expectedImageName, - uint32_t testDisplayIdx = 0u, - float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, - bool readPixelsTwice = false, - bool saveDiffOnError = true); - bool renderAndCompareScreenshotOffscreenBuffer(const std::string& expectedImageName, - uint32_t testDisplayIdx, ramses::displayBufferId_t displayBuffer, uint32_t width, uint32_t height, - float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel); - bool renderAndCompareScreenshotSubimage(const std::string& expectedImageName, - uint32_t subimageX, uint32_t subimageY, uint32_t subimageWidth, uint32_t subimageHeight, - float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, bool readPixelsTwice = false); - void setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); - void filterTestCases(const std::vector& filterIn, const std::vector& filterOut); - - bool runAllTests(); - [[nodiscard]] std::string generateReport() const; - - static bool NameMatchesFilter(const std::string& name, const std::vector& filter); - - template - ramses::sceneId_t createAndShowScene( - uint32_t sceneState, - const glm::vec3& cameraPosition = glm::vec3(0.0f), - const ramses::SceneConfig& sceneConfig = {}) - { - const ramses::sceneId_t sceneId = getScenesRegistry().createScene(sceneState, cameraPosition, sceneConfig); - publishAndFlushScene(sceneId); - getSceneToRendered(sceneId); - return sceneId; - } - - template - ramses::sceneId_t createAndShowScene(uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight) - { - const ramses::sceneId_t sceneId = getScenesRegistry().createScene(sceneState, { 0, 0, 0 }, vpWidth, vpHeight); - publishAndFlushScene(sceneId); - getSceneToRendered(sceneId); - return sceneId; - } - -protected: - struct TestDisplayInfo - { - ramses::displayId_t displayId; - ramses::DisplayConfig config; - std::vector offscreenBuffers; - std::vector streamBuffers; - }; - - using TestDisplays = std::vector; - [[nodiscard]] const TestDisplays& getDisplays() const; - -private: - bool compareScreenshotInternal( - const std::string& expectedImageName, - ramses::displayId_t displayId, - ramses::displayBufferId_t bufferId, - float maxAveragePercentErrorPerPixel, - uint32_t subimageX, - uint32_t subimageY, - uint32_t subimageWidth, - uint32_t subimageHeight, - bool readPixelsTwice, - bool saveDiffOnError); - - void sortTestCases(); - [[nodiscard]] bool currentDisplaySetupMatchesTestCase(const RenderingTestCase& testCase) const; - bool applyRendererAndDisplaysConfigurationForTest(const RenderingTestCase& testCase); - void destroyScenes(); - void destroyBuffers(); - bool runTestCase(RenderingTestCase& testCase); - - const bool m_generateScreenshots; - ramses_internal::TestScenesAndRenderer m_testScenesAndRenderer; - ramses_internal::TestRenderer& m_testRenderer; - TestDisplays m_displays; - bool m_forceDisplaysReinitForNextTestCase = false; - - RenderingTestCases m_testCases; - - // for logging and debugging purpose - RenderingTestCase* m_activeTestCase; - RenderingTestCases m_passedTestCases; - RenderingTestCases m_failedTestCases; - uint64_t m_elapsedTime; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/RenderingTestCase.h b/integration/SandwichTests/RendererTests/RendererTestFramework/RenderingTestCase.h deleted file mode 100644 index 3f71bbfbd..000000000 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/RenderingTestCase.h +++ /dev/null @@ -1,39 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERINGTESTCASE_H -#define RAMSES_RENDERINGTESTCASE_H - -#include "ramses-renderer-api/DisplayConfig.h" -#include "Collections/Vector.h" - -#include -#include - -class IRendererTest; - -using DisplayConfigVector = std::vector; - -struct RenderingTestCase -{ - RenderingTestCase(uint32_t id, IRendererTest& rendererTest, std::string_view name, bool defaultRendererRequired) - : m_id(id) - , m_name(name) - , m_rendererTest(rendererTest) - , m_defaultRendererRequired(defaultRendererRequired) - { - } - - const uint32_t m_id; - const std::string m_name; - DisplayConfigVector m_displayConfigs; - IRendererTest& m_rendererTest; - bool m_defaultRendererRequired; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.h b/integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.h deleted file mode 100644 index 33c41f486..000000000 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.h +++ /dev/null @@ -1,104 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_TESTRENDERER_H -#define RAMSES_TESTRENDERER_H - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/Types.h" -#include "ramses-framework-api/RendererSceneState.h" -#include "RendererAPI/Types.h" -#include "RendererTestUtils.h" - -#include -#include - -namespace ramses -{ - class RamsesFramework; - class IRendererEventHandler; - class IRendererSceneControlEventHandler; -} - -namespace ramses_internal -{ - class IEmbeddedCompositingManager; - class IEmbeddedCompositor; - - class TestRenderer - { - public: - virtual ~TestRenderer() = default; - - void initializeRendererWithFramework(ramses::RamsesFramework& ramsesFramework, const ramses::RendererConfig& rendererConfig); - [[nodiscard]] bool isRendererInitialized() const; - void destroyRendererWithFramework(ramses::RamsesFramework& ramsesFramework); - void flushRenderer(); - - ramses::displayId_t createDisplay(const ramses::DisplayConfig& displayConfig); - void destroyDisplay(ramses::displayId_t displayId); - [[nodiscard]] ramses::displayBufferId_t getDisplayFramebufferId(ramses::displayId_t displayId) const; - - void setSceneMapping(ramses::sceneId_t sceneId, ramses::displayId_t display); - bool getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); - void setSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); - bool waitForSceneStateChange(ramses::sceneId_t sceneId, ramses::RendererSceneState state); - void waitForFlush(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t sceneVersionTag); - bool checkScenesExpired(std::initializer_list sceneIds); - bool checkScenesNotExpired(std::initializer_list sceneIds); - bool waitForStreamSurfaceAvailabilityChange(ramses::waylandIviSurfaceId_t streamSource, bool available); - - void dispatchEvents(ramses::IRendererEventHandler& eventHandler, ramses::IRendererSceneControlEventHandler& sceneControlEventHandler); - - void setLoopMode(ramses::ELoopMode loopMode); - void startRendererThread(); - void stopRendererThread(); - [[nodiscard]] bool isRendererThreadEnabled() const; - void doOneLoop(); - - ramses::displayBufferId_t createOffscreenBuffer(ramses::displayId_t displayId, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount = 0u, ramses::EDepthBufferType depthBufferType = ramses::EDepthBufferType::DepthStencil); - ramses::displayBufferId_t createDmaOffscreenBuffer(ramses::displayId_t displayId, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier); - void destroyOffscreenBuffer(ramses::displayId_t displayId, ramses::displayBufferId_t buffer); - void assignSceneToDisplayBuffer(ramses::sceneId_t sceneId, ramses::displayBufferId_t buffer, int32_t renderOrder); - void setClearFlags(ramses::displayId_t displayId, ramses::displayBufferId_t buffer, uint32_t clearFlags); - void setClearColor(ramses::displayId_t displayId, ramses::displayBufferId_t buffer, const glm::vec4& clearColor); - bool getDmaOffscreenBufferFDAndStride(ramses::displayId_t displayId, ramses::displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; - - ramses::streamBufferId_t createStreamBuffer(ramses::displayId_t displayId, ramses::waylandIviSurfaceId_t source); - void destroyStreamBuffer(ramses::displayId_t displayId, ramses::streamBufferId_t buffer); - - void createBufferDataLink(ramses::displayBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); - void createBufferDataLink(ramses::streamBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); - void createDataLink(ramses::sceneId_t providerScene, ramses::dataProviderId_t providerId, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerId); - void removeDataLink(ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerId); - - bool performScreenshotCheck( - const ramses::displayId_t displayId, - ramses::displayBufferId_t bufferId, - uint32_t x, uint32_t y, uint32_t width, uint32_t height, - const std::string& comparisonImageFile, - float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, - bool readPixelsTwice = false, - bool saveDiffOnError = true); - void saveScreenshotForDisplay(const ramses::displayId_t displayId, ramses::displayBufferId_t bufferId, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& imageFile); - void readPixels(ramses::displayId_t displayId, uint32_t x, uint32_t y, uint32_t width, uint32_t height); - void setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); - void setSurfaceVisibility(WaylandIviSurfaceId surfaceId, bool visibility); - - IEmbeddedCompositor& getEmbeddedCompositor(ramses::displayId_t displayId); - IEmbeddedCompositingManager& getEmbeddedCompositorManager(ramses::displayId_t displayId); - - [[nodiscard]] bool hasSystemCompositorController() const; - - private: - ramses::RamsesRenderer* m_renderer = nullptr; - ramses::RendererSceneControl* m_sceneControlAPI = nullptr; - }; -} - -#endif diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.cpp b/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.cpp deleted file mode 100644 index ae48eae74..000000000 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "TestScenes.h" -#include "TestScenes/FileLoadingScene.h" -#include - -TestScenes::TestScenes(ramses::RamsesClient& client) - : m_client(client) -{ -} - -TestScenes::~TestScenes() -{ - for (const auto& sceneEntry : m_scenes) - { - delete sceneEntry.value.integrationScene; - } -} - -void TestScenes::createFileLoadingScene(ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const ramses::RamsesFrameworkConfig& config, uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight) -{ - // This creates the scene internally inside the instance - ramses_internal::FileLoadingScene fileLoadingScene(m_client, sceneState, sceneId, cameraPosition, ".", config, vpWidth, vpHeight); - SceneData data; - data.clientScene = fileLoadingScene.getCreatedScene(); - m_scenes.put(sceneId, data); -} - -const ramses::Scene& TestScenes::getScene(ramses::sceneId_t sceneId) const -{ - assert(m_scenes.contains(sceneId)); - return *m_scenes.get(sceneId)->clientScene; -} - -ramses::Scene& TestScenes::getScene(ramses::sceneId_t sceneId) -{ - assert(m_scenes.contains(sceneId)); - return *m_scenes.get(sceneId)->clientScene; -} - -void TestScenes::destroyScenes() -{ - while (m_scenes.size() != 0u) - { - destroyScene(m_scenes.begin()->key); - } -} - -void TestScenes::destroyScene(ramses::sceneId_t sceneId) -{ - auto it = m_scenes.find(sceneId); - assert(it != m_scenes.end()); - - auto& clientScene = *it->value.clientScene; - delete it->value.integrationScene; - m_client.destroy(clientScene); - m_scenes.remove(it); -} diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.h b/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.h deleted file mode 100644 index d6ce14bea..000000000 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenes.h +++ /dev/null @@ -1,105 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_TESTSCENES_H -#define RAMSES_TESTSCENES_H - -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "TestScenes/IntegrationScene.h" -#include "Collections/HashMap.h" -#include - -class TestScenes -{ -public: - explicit TestScenes(ramses::RamsesClient& client); - ~TestScenes(); - - template - void createScene( - uint32_t state, - ramses::sceneId_t sceneId, - const glm::vec3& cameraPosition = glm::vec3(0.0f), - const ramses::SceneConfig& sceneConfig = {}) - { - SceneData data; - data.clientScene = m_client.createScene(sceneId, sceneConfig); - data.integrationScene = new INTEGRATION_SCENE(*data.clientScene, state, cameraPosition); - m_scenes.put(sceneId, data); - - } - template - ramses::sceneId_t createScene( - uint32_t state, - const glm::vec3& cameraPosition = glm::vec3(0.0f), - const ramses::SceneConfig& sceneConfig = {}) - { - const ramses::sceneId_t sceneId = m_nextSceneId; - m_nextSceneId.getReference()++; - createScene(state, sceneId, cameraPosition, sceneConfig); - return sceneId; - } - - template - void createScene( - uint32_t state, - ramses::sceneId_t sceneId, - const glm::vec3& cameraPosition, - uint32_t vpWidth, - uint32_t vpHeight) - { - SceneData data; - data.clientScene = m_client.createScene(sceneId); - data.integrationScene = new INTEGRATION_SCENE(*data.clientScene, state, cameraPosition, vpWidth, vpHeight); - m_scenes.put(sceneId, data); - } - template - ramses::sceneId_t createScene( - uint32_t state, - const glm::vec3& cameraPosition, - uint32_t vpWidth, - uint32_t vpHeight) - { - const ramses::sceneId_t sceneId = m_nextSceneId; - m_nextSceneId.getReference()++; - createScene(state, sceneId, cameraPosition, vpWidth, vpHeight); - return sceneId; - } - - void createFileLoadingScene(ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const ramses::RamsesFrameworkConfig& config, uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight); - - [[nodiscard]] const ramses::Scene& getScene(ramses::sceneId_t sceneId) const; - ramses::Scene& getScene(ramses::sceneId_t sceneId); - - template - void setSceneState(ramses::sceneId_t sceneId, uint32_t state) - { - assert(m_scenes.contains(sceneId)); - assert(m_scenes.get(sceneId)->integrationScene); - INTEGRATION_SCENE& scene = static_cast(*m_scenes.get(sceneId)->integrationScene); - scene.setState(state); - } - - void destroyScenes(); - void destroyScene(ramses::sceneId_t sceneId); - -private: - struct SceneData - { - ramses::Scene* clientScene = nullptr; - ramses_internal::IntegrationScene* integrationScene = nullptr; - }; - using SceneMap = ramses_internal::HashMap; - - ramses::RamsesClient& m_client; - ramses::sceneId_t m_nextSceneId{1u}; - SceneMap m_scenes; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.cpp deleted file mode 100644 index f830b1e36..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.cpp +++ /dev/null @@ -1,401 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DataLinkingTests.h" -#include "TestScenes/TransformationLinkScene.h" -#include "TestScenes/MultiTransformationLinkScene.h" -#include "TestScenes/DataLinkScene.h" -#include "TestScenes/CameraDataLinkScene.h" -#include "TestScenes/TextureLinkScene.h" -#include "TestScenes/MultiTypeLinkScene.h" -#include "TestScenes/MultipleTrianglesScene.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Texture2D.h" -#include "RamsesObjectTypeUtils.h" - -using namespace ramses_internal; - -void DataLinkingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConsumerNotLinkedToProvider, *this, "TransformationLinkTest_ConsumerNotLinkedToProvider"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConsumerLinkedToProvider, *this, "TransformationLinkTest_ConsumerLinkedToProvider"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_LinkRemoved, *this, "TransformationLinkTest_LinkRemoved"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_LinkOverridesComsumerTransform, *this, "TransformationLinkTest_LinkOverridesComsumerTransform"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConsumerLinkedToProviderNested, *this, "TransformationLinkTest_ConsumerLinkedToProviderNested"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_RemovedProviderFromScene, *this, "TransformationLinkTest_RemovedProviderFromScene"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_RemovedConsumerFromScene, *this, "TransformationLinkTest_RemovedConsumerFromScene"); - testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConfidenceMultiLinkTest, *this, "TransformationLinkTest_ConfidenceMultiLinkTest"); - - testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_NoLinks, *this, "DataLinkTest_NoLinks"); - testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_Linked, *this, "DataLinkTest_Linked"); - testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_LinksRemoved, *this, "DataLinkTest_LinksRemoved"); - testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_ProviderRemoved, *this, "DataLinkTest_ProviderRemoved"); - testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_ConsumerRemoved, *this, "DataLinkTest_ConsumerRemoved"); - - testFramework.createTestCaseWithDefaultDisplay(CameraDataLinkTest_NoLinks, *this, "CameraDataLinkTest_NoLinks"); - testFramework.createTestCaseWithDefaultDisplay(CameraDataLinkTest_ViewportLinked, *this, "CameraDataLinkTest_ViewportLinked"); - testFramework.createTestCaseWithDefaultDisplay(CameraDataLinkTest_FrustumLinked, *this, "CameraDataLinkTest_FrustumLinked"); - - testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_NoLinks, *this, "TextureLinkTest_NoLinks"); - testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_Linked, *this, "TextureLinkTest_Linked"); - testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_LinksRemoved, *this, "TextureLinkTest_LinksRemoved"); - testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_ProviderSceneUnmapped, *this, "TextureLinkTest_ProviderSceneUnmapped"); - // TODO(Carsten): Find a solution for this test with Vaclav - //testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_ProvidedTextureChanges, *this, "TextureLinkTest_ProvidedTextureChanges"); - - testFramework.createTestCaseWithDefaultDisplay(MultiTypeLinkTest_NoLinks, *this, "MultiTypeLinkTest_NoLinks"); - testFramework.createTestCaseWithDefaultDisplay(MultiTypeLinkTest_Linked, *this, "MultiTypeLinkTest_Linked"); -} - -bool DataLinkingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - std::string expectedImageName("!!unknown!!"); - float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel; - switch (testCase.m_id) - { - case TransformationLinkTest_ConsumerNotLinkedToProvider: - { - const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerOvrSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_OVERRIDEN, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consAndProvSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_AND_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - testFramework.publishAndFlushScene(providerSceneId); - testFramework.publishAndFlushScene(consumerSceneId); - testFramework.publishAndFlushScene(consumerOvrSceneId); - testFramework.publishAndFlushScene(consAndProvSceneId); - testFramework.getSceneToRendered(providerSceneId); - testFramework.getSceneToRendered(consumerSceneId); - testFramework.getSceneToRendered(consumerOvrSceneId); - testFramework.getSceneToRendered(consAndProvSceneId); - expectedImageName = "DataLinkTest_AllLinksDisabled"; - break; - } - case TransformationLinkTest_ConsumerLinkedToProvider: - { - const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, - glm::vec3(0.0f, 0.0f, 12.0f)); - testFramework.publishAndFlushScene(providerSceneId); - testFramework.publishAndFlushScene(consumerSceneId); - testFramework.getSceneToRendered(providerSceneId); - testFramework.getSceneToRendered(consumerSceneId); - testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); - expectedImageName = "DataLinkTest_ConsumerLinkedToProvider"; - break; - } - case TransformationLinkTest_LinkOverridesComsumerTransform: - { - const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerOvrSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_OVERRIDEN, - glm::vec3(0.0f, 0.0f, 12.0f)); - testFramework.publishAndFlushScene(providerSceneId); - testFramework.publishAndFlushScene(consumerOvrSceneId); - testFramework.getSceneToRendered(providerSceneId); - testFramework.getSceneToRendered(consumerOvrSceneId); - testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerOvrSceneId, TransformationLinkScene::transformConsumerDataId); - expectedImageName = "DataLinkTest_LinkOverridesComsumerTransform"; - break; - } - case TransformationLinkTest_LinkRemoved: - { - const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, - glm::vec3(0.0f, 0.0f, 12.0f)); - testFramework.publishAndFlushScene(providerSceneId); - testFramework.publishAndFlushScene(consumerSceneId); - testFramework.getSceneToRendered(providerSceneId); - testFramework.getSceneToRendered(consumerSceneId); - testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); - testFramework.removeDataLink(consumerSceneId, TransformationLinkScene::transformConsumerDataId); - expectedImageName = "DataLinkTest_LinkRemoved"; - break; - } - case TransformationLinkTest_ConsumerLinkedToProviderNested: - { - const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consAndProvSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_AND_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, - glm::vec3(0.0f, 0.0f, 12.0f)); - testFramework.publishAndFlushScene(providerSceneId); - testFramework.publishAndFlushScene(consAndProvSceneId); - testFramework.publishAndFlushScene(consumerSceneId); - testFramework.getSceneToRendered(providerSceneId); - testFramework.getSceneToRendered(consAndProvSceneId); - testFramework.getSceneToRendered(consumerSceneId); - testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consAndProvSceneId, TransformationLinkScene::transformConsumerDataId); - testFramework.createDataLink(consAndProvSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); - expectedImageName = "DataLinkTest_ConsumerLinkedToProviderNested"; - break; - } - case TransformationLinkTest_RemovedProviderFromScene: - case TransformationLinkTest_RemovedConsumerFromScene: - { - const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, - glm::vec3(0.0f, 0.0f, 12.0f)); - const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, - glm::vec3(0.0f, 0.0f, 12.0f)); - testFramework.publishAndFlushScene(providerSceneId); - testFramework.publishAndFlushScene(consumerSceneId); - testFramework.getSceneToRendered(providerSceneId); - testFramework.getSceneToRendered(consumerSceneId); - testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); - - if (TransformationLinkTest_RemovedProviderFromScene == testCase.m_id) - { - ramses::Scene& scene = testFramework.getScenesRegistry().getScene(providerSceneId); - ramses::RamsesObject* provider = scene.findObjectByName("transform provider"); - assert(provider != nullptr); - - scene.destroy(ramses::RamsesObjectTypeUtils::ConvertTo(*provider)); - scene.flush(); - } - else if (TransformationLinkTest_RemovedConsumerFromScene == testCase.m_id) - { - ramses::Scene& scene = testFramework.getScenesRegistry().getScene(consumerSceneId); - ramses::RamsesObject* consumer = scene.findObjectByName("transform consumer"); - assert(consumer != nullptr); - - scene.destroy(ramses::RamsesObjectTypeUtils::ConvertTo(*consumer)); - scene.flush(); - } - - expectedImageName = "DataLinkTest_LinkRemoved"; - break; - } - case TransformationLinkTest_ConfidenceMultiLinkTest: - { - m_sceneIdProvider = testFramework.getScenesRegistry().createScene(MultiTransformationLinkScene::PROVIDER_SCENE); - m_sceneIdProviderConsumer = testFramework.getScenesRegistry().createScene(MultiTransformationLinkScene::PROVIDER_CONSUMER_SCENE); - m_sceneIdConsumer = testFramework.getScenesRegistry().createScene(MultiTransformationLinkScene::CONSUMER_SCENE); - - testFramework.publishAndFlushScene(m_sceneIdProvider); - testFramework.publishAndFlushScene(m_sceneIdProviderConsumer); - testFramework.publishAndFlushScene(m_sceneIdConsumer); - testFramework.getSceneToRendered(m_sceneIdProvider); - testFramework.getSceneToRendered(m_sceneIdProviderConsumer); - testFramework.getSceneToRendered(m_sceneIdConsumer); - - for (uint32_t rowId = MultiTransformationLinkScene::DataIdRowStart; rowId < MultiTransformationLinkScene::DataIdRowStart + 4u; ++rowId) - { - testFramework.createDataLink(m_sceneIdProvider, ramses::dataProviderId_t{rowId}, m_sceneIdProviderConsumer, ramses::dataConsumerId_t{rowId}); - } - - for (uint32_t meshId = MultiTransformationLinkScene::DataIdMeshStart; meshId < MultiTransformationLinkScene::DataIdMeshStart + 16u; ++meshId) - { - testFramework.createDataLink(m_sceneIdProviderConsumer, ramses::dataProviderId_t{meshId}, m_sceneIdConsumer, ramses::dataConsumerId_t{meshId}); - } - - expectedImageName = "DataLinkTest_ConfidenceMultiLink"; - break; - } - - case DataLinkTest_NoLinks: - createAndShowDataLinkScenes(testFramework); - expectedImageName = "DataLinkTest_NoLinks"; - break; - case DataLinkTest_Linked: - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); - expectedImageName = "DataLinkTest_Linked"; - break; - case DataLinkTest_LinksRemoved: - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshot("DataLinkTest_Linked", 0u, 0.0f)) - { - return false; - } - testFramework.removeDataLink(m_sceneIdConsumer, DataLinkScene::DataConsumerId); - testFramework.removeDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); - expectedImageName = "DataLinkTest_NoLinks"; - break; - case DataLinkTest_ProviderRemoved: - { - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshot("DataLinkTest_Linked", 0u, 0.0f)) - { - return false; - } - - ramses::Scene& scene = testFramework.getScenesRegistry().getScene(m_sceneIdProvider); - ramses::Appearance& consumerAppearance1 = ramses::RamsesObjectTypeUtils::ConvertTo(*scene.findObjectByName("dataLinkAppearance1")); - ramses::Appearance& consumerAppearance2 = ramses::RamsesObjectTypeUtils::ConvertTo(*scene.findObjectByName("dataLinkAppearance2")); - ramses::UniformInput colorInput; - consumerAppearance1.getEffect().findUniformInput("color", colorInput); - consumerAppearance1.unbindInput(colorInput); - consumerAppearance2.unbindInput(colorInput); - - ramses::SceneObject& colorData = ramses::RamsesObjectTypeUtils::ConvertTo(*scene.findObjectByName("dataLinkColorData")); - scene.destroy(colorData); - scene.flush(); - - expectedImageName = "DataLinkTest_RemovedProvider"; - } - break; - case DataLinkTest_ConsumerRemoved: - { - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshot("DataLinkTest_Linked", 0u, 0.0f)) - { - return false; - } - - ramses::Scene& scene = testFramework.getScenesRegistry().getScene(m_sceneIdConsumer); - ramses::Appearance& consumerAppearance1 = ramses::RamsesObjectTypeUtils::ConvertTo(*scene.findObjectByName("dataLinkAppearance1")); - ramses::Appearance& consumerAppearance2 = ramses::RamsesObjectTypeUtils::ConvertTo(*scene.findObjectByName("dataLinkAppearance2")); - ramses::UniformInput colorInput; - consumerAppearance1.getEffect().findUniformInput("color", colorInput); - consumerAppearance1.unbindInput(colorInput); - consumerAppearance2.unbindInput(colorInput); - - ramses::SceneObject& colorData = ramses::RamsesObjectTypeUtils::ConvertTo(*scene.findObjectByName("dataLinkColorData")); - scene.destroy(colorData); - scene.flush(); - - expectedImageName = "DataLinkTest_RemovedConsumer"; - } - break; - - case CameraDataLinkTest_NoLinks: - testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_CONSUMER, m_cameraMid); - expectedImageName = "CameraData_NoLinks"; - break; - case CameraDataLinkTest_ViewportLinked: - { - const auto providerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_PROVIDER, m_cameraMid); - const auto consumerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_CONSUMER, m_cameraMid); - testFramework.createDataLink(providerSceneId, CameraDataLinkScene::ViewportOffsetProviderId, consumerSceneId, CameraDataLinkScene::ViewportOffsetConsumerId); - testFramework.createDataLink(providerSceneId, CameraDataLinkScene::ViewportSizeProviderId, consumerSceneId, CameraDataLinkScene::ViewportSizeConsumerId); - expectedImageName = "CameraData_ViewportLinked"; - } - break; - case CameraDataLinkTest_FrustumLinked: - { - const auto providerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_PROVIDER, m_cameraMid); - const auto consumerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_CONSUMER, m_cameraMid); - testFramework.createDataLink(providerSceneId, CameraDataLinkScene::FrustumPlanesProviderId, consumerSceneId, CameraDataLinkScene::FrustumPlanesConsumerId); - testFramework.createDataLink(providerSceneId, CameraDataLinkScene::FrustumPlanesNearFarProviderId, consumerSceneId, CameraDataLinkScene::FrustumPlanesNearFarConsumerId); - expectedImageName = "CameraData_FrustumLinked"; - } - break; - - case TextureLinkTest_NoLinks: - createAndShowDataLinkScenes(testFramework); - expectedImageName = "TextureLinkTest_NoLinks"; - break; - case TextureLinkTest_Linked: - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - expectedImageName = "TextureLinkTest_Linked"; - break; - case TextureLinkTest_LinksRemoved: - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshot("TextureLinkTest_Linked", 0u)) - { - return false; - } - testFramework.removeDataLink(m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - testFramework.removeDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); - expectedImageName = "TextureLinkTest_NoLinks"; - break; - case TextureLinkTest_ProviderSceneUnmapped: - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshot("TextureLinkTest_Linked", 0u)) - { - return false; - } - - // unmapping scene will remove any links to it - testFramework.getSceneToState(m_sceneIdProviderConsumer, ramses::RendererSceneState::Available); - - // when scene is mapped and shown again it should be in an initial state without links - testFramework.getSceneToState(m_sceneIdProviderConsumer, ramses::RendererSceneState::Rendered); - - expectedImageName = "TextureLinkTest_NoLinks"; - break; - case TextureLinkTest_ProvidedTextureChanges: - createAndShowDataLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshot("TextureLinkTest_Linked", 0u)) - { - return false; - } - - { - ramses::Scene& providerConsumerScene = testFramework.getScenesRegistry().getScene(m_sceneIdProviderConsumer); - const ramses::Texture2D& otherTexture = ramses::RamsesObjectTypeUtils::ConvertTo(*providerConsumerScene.findObjectByName("ConsumerProviderTexture")); - ramses::Scene& providerScene = testFramework.getScenesRegistry().getScene(m_sceneIdProvider); - providerScene.updateTextureProvider(otherTexture, TextureLinkScene::DataProviderId); - providerScene.flush(); - } - - expectedImageName = "TextureLinkTest_ProviderTextureChanged"; - break; - - case MultiTypeLinkTest_NoLinks: - createAndShowMultiTypeLinkScenes(testFramework); - expectedImageName = "MultiTypeLinkTest_NoLinks"; - break; - case MultiTypeLinkTest_Linked: - createAndShowMultiTypeLinkScenes(testFramework); - testFramework.createDataLink(m_sceneIdProvider, MultiTypeLinkScene::DataProviderId, m_sceneIdConsumer, MultiTypeLinkScene::DataConsumerId); - testFramework.createDataLink(m_sceneIdProvider, MultiTypeLinkScene::TextureProviderId, m_sceneIdConsumer, MultiTypeLinkScene::TextureConsumerId); - testFramework.createDataLink(m_sceneIdConsumer, MultiTypeLinkScene::TransformationProviderId, m_sceneIdProvider, MultiTypeLinkScene::TransformationConsumerId); - expectedImageName = "MultiTypeLinkTest_Linked"; - break; - default: - assert(false && "undefined test case"); - } - - return renderAndCompareScreenshot(testFramework, expectedImageName, 0u, expectedPixelError); -} - -bool DataLinkingTests::renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx, float expectedPixelError) -{ - // changes to offscreen buffers are delayed by 1 frame, render one frame before taking screenshot - testFramework.flushRendererAndDoOneLoop(); - return testFramework.renderAndCompareScreenshot(expectedImageName, testDisplayIdx, expectedPixelError); -} - -template -void DataLinkingTests::createAndShowDataLinkScenes(RendererTestsFramework& testFramework) -{ - m_sceneIdProvider = testFramework.createAndShowScene(LINKSCENE::DATA_PROVIDER , m_cameraHigh); - m_sceneIdProviderConsumer = testFramework.createAndShowScene(LINKSCENE::DATA_CONSUMER_AND_PROVIDER, m_cameraMid); - m_sceneIdConsumer = testFramework.createAndShowScene(LINKSCENE::DATA_CONSUMER , m_cameraLow); -} - -void DataLinkingTests::createAndShowMultiTypeLinkScenes(RendererTestsFramework& testFramework) -{ - m_sceneIdConsumer = testFramework.createAndShowScene(MultiTypeLinkScene::TRANSFORMATION_PROVIDER_DATA_AND_TEXTURE_CONSUMER, m_cameraLow); - m_sceneIdProvider = testFramework.createAndShowScene(MultiTypeLinkScene::TRANSFORMATION_CONSUMER_DATA_AND_TEXTURE_PROVIDER, m_cameraHigh); -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.h deleted file mode 100644 index 9bd607a1c..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/DataLinkingTests.h +++ /dev/null @@ -1,71 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DATALINKINGTESTS_H -#define RAMSES_DATALINKINGTESTS_H - -#include "IRendererTest.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -#include - -class DataLinkingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - bool renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx = 0u, float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel); - - template - void createAndShowDataLinkScenes(RendererTestsFramework& testFramework); - - void createAndShowMultiTypeLinkScenes(RendererTestsFramework& testFramework); - - enum - { - TransformationLinkTest_ConsumerNotLinkedToProvider, - TransformationLinkTest_ConsumerLinkedToProvider, - TransformationLinkTest_LinkRemoved, - TransformationLinkTest_LinkOverridesComsumerTransform, - TransformationLinkTest_ConsumerLinkedToProviderNested, - TransformationLinkTest_RemovedProviderFromScene, - TransformationLinkTest_RemovedConsumerFromScene, - TransformationLinkTest_ConfidenceMultiLinkTest, - - DataLinkTest_NoLinks, - DataLinkTest_Linked, - DataLinkTest_LinksRemoved, - DataLinkTest_ProviderRemoved, - DataLinkTest_ConsumerRemoved, - - CameraDataLinkTest_NoLinks, - CameraDataLinkTest_ViewportLinked, - CameraDataLinkTest_FrustumLinked, - - TextureLinkTest_NoLinks, - TextureLinkTest_Linked, - TextureLinkTest_LinksRemoved, - TextureLinkTest_ProviderSceneUnmapped, - TextureLinkTest_ProvidedTextureChanges, - - MultiTypeLinkTest_NoLinks, - MultiTypeLinkTest_Linked - }; - - ramses::sceneId_t m_sceneIdProvider; - ramses::sceneId_t m_sceneIdProviderConsumer; - ramses::sceneId_t m_sceneIdConsumer; - - const glm::vec3 m_cameraLow{ -1.f, 2.f, 8.f }; - const glm::vec3 m_cameraHigh{ 1.f, -2.f, 8.f }; - const glm::vec3 m_cameraMid{ 0.f, 0.f, 8.f }; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.cpp deleted file mode 100644 index 9cc827d81..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.cpp +++ /dev/null @@ -1,392 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DisplayRenderingTests.h" -#include "TestScenes/MultipleTrianglesScene.h" -#include "TestScenes/RenderTargetScene.h" -#include "TestScenes/TextScene.h" -#include "RendererTestUtils.h" -#include "DisplayConfigImpl.h" - -using namespace ramses_internal; - -void DisplayRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - ramses::DisplayConfig displayConfig1 = RendererTestUtils::CreateTestDisplayConfig(0); - displayConfig1.setWindowRectangle(0, 0, DisplayWidth, DisplayHeight); - - ramses::DisplayConfig displayConfig2 = RendererTestUtils::CreateTestDisplayConfig(1); - displayConfig2.setWindowRectangle(0, 0, DisplayWidth, DisplayHeight); - - ramses::DisplayConfig displayConfigWithoutDepthAndStencil = RendererTestUtils::CreateTestDisplayConfig(3); - displayConfigWithoutDepthAndStencil.setWindowRectangle(0, 0, ramses_internal::IntegrationScene::DefaultViewportWidth, ramses_internal::IntegrationScene::DefaultViewportHeight); - displayConfigWithoutDepthAndStencil.setDepthStencilBufferType(ramses::EDepthBufferType::None); - - ramses::DisplayConfig displayConfigWithoutStencil = RendererTestUtils::CreateTestDisplayConfig(4); - displayConfigWithoutStencil.setWindowRectangle(0, 0, ramses_internal::IntegrationScene::DefaultViewportWidth, ramses_internal::IntegrationScene::DefaultViewportHeight); - displayConfigWithoutStencil.setDepthStencilBufferType(ramses::EDepthBufferType::Depth); - - ramses::DisplayConfig displayConfigWithoutAsyncEffectUpload = RendererTestUtils::CreateTestDisplayConfig(3); - displayConfigWithoutAsyncEffectUpload.setWindowRectangle(0, 0, DisplayWidth, DisplayHeight); - displayConfigWithoutAsyncEffectUpload.setAsyncEffectUploadEnabled(false); - - testFramework.createTestCase(DisplayRenderingTest_TwoScenes, *this, "DisplayRenderingTest_TwoScenes").m_displayConfigs.push_back(displayConfig1); - testFramework.createTestCase(DisplayRenderingTest_UnpublishScene, *this, "DisplayRenderingTest_UnpublishScene").m_displayConfigs.push_back(displayConfig1); - testFramework.createTestCase(DisplayRenderingTest_HideScene, *this, "DisplayRenderingTest_HideScene").m_displayConfigs.push_back(displayConfig1); - testFramework.createTestCase(DisplayRenderingTest_SceneRenderOrder, *this, "DisplayRenderingTest_SceneRenderOrder").m_displayConfigs.push_back(displayConfig1); - testFramework.createTestCase(DisplayRenderingTest_SceneRenderOrderInversed, *this, "DisplayRenderingTest_SceneRenderOrderInversed").m_displayConfigs.push_back(displayConfig1); - - // These tests are using default display config from framework which uses higher resolution than other display tests - testFramework.createTestCaseWithDefaultDisplay(DisplayRenderingTest_Subimage, *this, "DisplayRenderingTest_Subimage"); - - RenderingTestCase& testCaseRemap = testFramework.createTestCase(DisplayRenderingTest_RemapScene, *this, "DisplayRenderingTest_RemapScene"); - testCaseRemap.m_displayConfigs.push_back(displayConfig1); - testCaseRemap.m_displayConfigs.push_back(displayConfig2); - - RenderingTestCase& testCaseSwap = testFramework.createTestCase(DisplayRenderingTest_SwapScenes, *this, "DisplayRenderingTest_SwapScenes"); - testCaseSwap.m_displayConfigs.push_back(displayConfig1); - testCaseSwap.m_displayConfigs.push_back(displayConfig2); - - RenderingTestCase& testCaseRenderTarget = testFramework.createTestCase(DisplayRenderingTest_RemapSceneWithRenderTarget, *this, "DisplayRenderingTest_RemapSceneWithRenderTarget"); - testCaseRenderTarget.m_displayConfigs.push_back(displayConfig1); - testCaseRenderTarget.m_displayConfigs.push_back(displayConfig2); - -#if defined(RAMSES_TEXT_ENABLED) - RenderingTestCase& testCaseText = testFramework.createTestCase(DisplayRenderingTest_RemapSceneWithText, *this, "DisplayRenderingTest_RemapSceneWithText"); - testCaseText.m_displayConfigs.push_back(displayConfig1); - testCaseText.m_displayConfigs.push_back(displayConfig2); -#endif - - RenderingTestCase& testCaseRemapChanged = testFramework.createTestCase(DisplayRenderingTest_RemapSceneWithChangedContent, *this, "DisplayRenderingTest_RemapSceneWithChangedContent"); - testCaseRemapChanged.m_displayConfigs.push_back(displayConfig1); - testCaseRemapChanged.m_displayConfigs.push_back(displayConfig2); - - testFramework.createTestCase(DisplayRenderingTest_ResubscribeScene, *this, "DisplayRenderingTest_ResubscribeScene").m_displayConfigs.push_back(displayConfig1); - - testFramework.createTestCase(DisplayRenderingTest_FramebufferWithoutDepthAndStencil, *this, "DisplayRenderingTest_FramebufferWithoutDepthAndStencil").m_displayConfigs.push_back(displayConfigWithoutDepthAndStencil); - testFramework.createTestCase(DisplayRenderingTest_FramebufferWithoutStencil , *this, "DisplayRenderingTest_FramebufferWithoutStencil").m_displayConfigs.push_back(displayConfigWithoutStencil); - - testFramework.createTestCase(DisplayRenderingTest_AsyncEffectUploadDisabled, *this, "DisplayRenderingTest_AsyncEffectUploadDisabled").m_displayConfigs.push_back(displayConfigWithoutAsyncEffectUpload); -} - -bool DisplayRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - switch (testCase.m_id) - { - case DisplayRenderingTest_AsyncEffectUploadDisabled: - case DisplayRenderingTest_TwoScenes: - return runTwoScenesTest(testFramework); - case DisplayRenderingTest_UnpublishScene: - return runUnpublishTest(testFramework); - case DisplayRenderingTest_HideScene: - return runHideTest(testFramework); - case DisplayRenderingTest_SceneRenderOrder: - return runSceneRenderOrderTest(testFramework); - case DisplayRenderingTest_SceneRenderOrderInversed: - return runSceneRenderOrderInversedTest(testFramework); - case DisplayRenderingTest_Subimage: - return runSubimageTest(testFramework); - case DisplayRenderingTest_RemapScene: - return runRemapSceneTest(testFramework); - case DisplayRenderingTest_SwapScenes: - return runSwapScenesTest(testFramework); - case DisplayRenderingTest_RemapSceneWithRenderTarget: - return runRemapSceneWithRenderTargetTest(testFramework); -#if defined(RAMSES_TEXT_ENABLED) - case DisplayRenderingTest_RemapSceneWithText: - return runRemapSceneWithTextTest(testFramework); -#endif - case DisplayRenderingTest_RemapSceneWithChangedContent: - return runRemapSceneWithChangedContentTest(testFramework); - case DisplayRenderingTest_ResubscribeScene: - return runResubscribeSceneTest(testFramework); - case DisplayRenderingTest_FramebufferWithoutDepthAndStencil: - return runFramebufferWithoutDepthAndStencilTest(testFramework); - case DisplayRenderingTest_FramebufferWithoutStencil: - return runFramebufferWithoutStencil(testFramework); - default: - assert(!"Invalid renderer test ID!"); - return false; - } -} - -bool DisplayRenderingTests::runTwoScenesTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::SUBTRACTIVE_BLENDING, - glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId); - testFramework.publishAndFlushScene(otherId); - testFramework.getSceneToRendered(sceneId); - testFramework.getSceneToRendered(otherId); - - return testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenes"); -} - -bool DisplayRenderingTests::runUnpublishTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::SUBTRACTIVE_BLENDING, - glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId); - testFramework.publishAndFlushScene(otherId); - testFramework.getSceneToRendered(sceneId); - testFramework.getSceneToRendered(otherId); - - bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenes"); - - testFramework.getScenesRegistry().getScene(otherId).unpublish(); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_UnpublishedScene"); - - return testResult; -} - -bool DisplayRenderingTests::runHideTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::SUBTRACTIVE_BLENDING, - glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId); - testFramework.publishAndFlushScene(otherId); - testFramework.getSceneToRendered(sceneId); - testFramework.getSceneToRendered(otherId); - - bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenes"); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Ready); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_HiddenScene"); - - return testResult; -} - -bool DisplayRenderingTests::runSceneRenderOrderTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::ALPHA_BLENDING, - glm::vec3(0.5f, 0.5f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(-0.5f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId); - testFramework.publishAndFlushScene(otherId); - testFramework.getSceneToRendered(sceneId); - testFramework.getSceneToRendered(otherId); - testFramework.assignSceneToDisplayBuffer(sceneId, testFramework.getDisplayFramebufferId(0u), 1); - testFramework.assignSceneToDisplayBuffer(otherId, testFramework.getDisplayFramebufferId(0u), 2); - - return testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenesOrdered"); -} - -bool DisplayRenderingTests::runSceneRenderOrderInversedTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::ALPHA_BLENDING, - glm::vec3(0.5f, 0.5f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(-0.5f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId); - testFramework.publishAndFlushScene(otherId); - testFramework.getSceneToRendered(sceneId); - testFramework.getSceneToRendered(otherId); - testFramework.assignSceneToDisplayBuffer(sceneId, testFramework.getDisplayFramebufferId(0u), 2); - testFramework.assignSceneToDisplayBuffer(otherId, testFramework.getDisplayFramebufferId(0u), 1); - - return testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenesInverseOrdered"); -} - -bool DisplayRenderingTests::runSubimageTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::SUBIMAGES); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - return testFramework.renderAndCompareScreenshotSubimage("MultipleTrianglesScene_Subimages_middle", 89u, 69u, 30u, 102u); -} - -bool DisplayRenderingTests::runRemapSceneTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId, 0u); - bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); - testFramework.setSceneMapping(sceneId, 1u); - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); - - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 1u); - - return testResult; -} - -bool DisplayRenderingTests::runSwapScenesTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId1 = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t sceneId2 = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED, - glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId1); - testFramework.publishAndFlushScene(sceneId2); - testFramework.setSceneMapping(sceneId1, 0u); - testFramework.setSceneMapping(sceneId2, 1u); - testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); - testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); - - bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Triangles_reordered", 1u); - - testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Available); - testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Available); - testFramework.setSceneMapping(sceneId1, 1u); - testFramework.setSceneMapping(sceneId2, 0u); - testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); - testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); - - testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Triangles_reordered", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 1u); - - return testResult; -} - -bool DisplayRenderingTests::runRemapSceneWithRenderTargetTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::RenderTargetScene::PERSPECTIVE_PROJECTION, - glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId, 0u); - bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_RenderTarget", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); - testFramework.setSceneMapping(sceneId, 1u); - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); - - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_RenderTarget", 1u); - - return testResult; -} - -#if defined(RAMSES_TEXT_ENABLED) -bool DisplayRenderingTests::runRemapSceneWithTextTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::TextScene::EState_INITIAL_128_BY_64_VIEWPORT, - glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_SimpleText", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); - testFramework.setSceneMapping(sceneId, 1u); - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_SimpleText", 1u); - - return testResult; -} -#endif - -bool DisplayRenderingTests::runRemapSceneWithChangedContentTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId1 = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - const ramses::sceneId_t sceneId2 = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED, - glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); - - testFramework.publishAndFlushScene(sceneId1); - testFramework.publishAndFlushScene(sceneId2); - testFramework.getSceneToRendered(sceneId1, 0u); - testFramework.getSceneToRendered(sceneId2, 0u); - - bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_ModifiedScenes1", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); - - // unmap scenes - testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Available); - testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Available); - - // modify scenes - testFramework.getScenesRegistry().setSceneState(sceneId1, ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().setSceneState(sceneId2, ramses_internal::MultipleTrianglesScene::ADDITIVE_BLENDING); - testFramework.getScenesRegistry().getScene(sceneId1).flush(); - testFramework.getScenesRegistry().getScene(sceneId2).flush(); - - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); - - // map scenes to second display - testFramework.setSceneMapping(sceneId1, 1u); - testFramework.setSceneMapping(sceneId2, 1u); - testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); - testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); - - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_ModifiedScenes2", 1u); - - return testResult; -} - -bool DisplayRenderingTests::runResubscribeSceneTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, - glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); - testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); - testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); - - return testResult; -} - -bool DisplayRenderingTests::runFramebufferWithoutDepthAndStencilTest(RendererTestsFramework& testFramework) -{ - ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::DEPTH_FUNC); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - bool testResult = testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_DepthFunc_NoDepthBuffer"); - - testFramework.getScenesRegistry().getScene(sceneId).unpublish(); - - sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::STENCIL_TEST_1); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - testResult &= testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer"); - - return testResult; -} - -bool DisplayRenderingTests::runFramebufferWithoutStencil(RendererTestsFramework& testFramework) -{ - ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::DEPTH_FUNC); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - bool testResult = testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_DepthFunc"); - - testFramework.getScenesRegistry().getScene(sceneId).unpublish(); - - sceneId = testFramework.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::STENCIL_TEST_1); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - testResult &= testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_StencilTest_1_NoStencilBuffer"); - - return testResult; -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.h deleted file mode 100644 index 9907b329a..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/DisplayRenderingTests.h +++ /dev/null @@ -1,61 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DISPLAYRENDERINGTESTS_H -#define RAMSES_DISPLAYRENDERINGTESTS_H - -#include "IRendererTest.h" - -class DisplayRenderingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - bool runTwoScenesTest(RendererTestsFramework& testFramework); - bool runUnpublishTest(RendererTestsFramework& testFramework); - bool runHideTest(RendererTestsFramework& testFramework); - bool runSceneRenderOrderTest(RendererTestsFramework& testFramework); - bool runSceneRenderOrderInversedTest(RendererTestsFramework& testFramework); - bool runSubimageTest(RendererTestsFramework& testFramework); - bool runRemapSceneTest(RendererTestsFramework& testFramework); - bool runSwapScenesTest(RendererTestsFramework& testFramework); - bool runRemapSceneWithRenderTargetTest(RendererTestsFramework& testFramework); - bool runRemapSceneWithTextTest(RendererTestsFramework& testFramework); - bool runRemapSceneWithChangedContentTest(RendererTestsFramework& testFramework); - bool runResubscribeSceneTest(RendererTestsFramework& testFramework); - bool runFramebufferWithoutDepthAndStencilTest(RendererTestsFramework& testFramework); - bool runFramebufferWithoutStencil(RendererTestsFramework& testFramework); - - enum - { - DisplayRenderingTest_TwoScenes = 0, - DisplayRenderingTest_UnpublishScene, - DisplayRenderingTest_HideScene, - DisplayRenderingTest_SceneRenderOrder, - DisplayRenderingTest_SceneRenderOrderInversed, - DisplayRenderingTest_Subimage, - DisplayRenderingTest_RemapScene, - DisplayRenderingTest_SwapScenes, - DisplayRenderingTest_RemapSceneWithRenderTarget, -#if defined(RAMSES_TEXT_ENABLED) - DisplayRenderingTest_RemapSceneWithText, -#endif - DisplayRenderingTest_RemapSceneWithChangedContent, - DisplayRenderingTest_ResubscribeScene, - DisplayRenderingTest_FramebufferWithoutDepthAndStencil, - DisplayRenderingTest_FramebufferWithoutStencil, - DisplayRenderingTest_AsyncEffectUploadDisabled - }; - - static constexpr uint32_t DisplayWidth = 128u; - static constexpr uint32_t DisplayHeight = 64u; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.cpp deleted file mode 100644 index 9ad528998..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "EffectRenderingTests.h" -#include "TestScenes/ShaderTestScene.h" -#include "TestScenes/SingleAppearanceScene.h" -#include "TestScenes/CustomShaderTestScene.h" - -using namespace ramses_internal; - -void EffectRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(EffectTest_Shaders, *this, "EffectTest_Shaders"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_Discard_Shaders, *this, "EffectTest_Discard_Shaders"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_UniformWithSameNameInBothStages, *this, "EffectTest_UniformWithSameNameInBothStages"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_ExplicitAttributeLocation, *this, "EffectTest_ExplicitAttributeLocation"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_ExplicitAttributeLocationSwapped, *this, "EffectTest_ExplicitAttributeLocationSwapped"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_StructUniform, *this, "EffectTest_StructUniform"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_BoolUniform, *this, "EffectTest_BoolUniform"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_TextureSize, *this, "EffectTest_TextureSize"); - testFramework.createTestCaseWithDefaultDisplay(EffectTest_OptimizedInput, *this, "EffectTest_OptimizedInput"); -} - -bool EffectRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - switch (testCase.m_id) - { - case EffectTest_Shaders: - return runBasicTest(testFramework, SingleAppearanceScene::RED_TRIANGLES, "SingleAppearanceScene_RedTriangles"); - case EffectTest_Discard_Shaders: - return runBasicTest(testFramework, ShaderTestScene::DISCARD, "ShaderTestScene_Discard"); - case EffectTest_OptimizedInput: - return runBasicTest(testFramework, ShaderTestScene::OPTIMIZED_INPUT, "ShaderTestScene_OptimizedInput"); - case EffectTest_UniformWithSameNameInBothStages: - return runBasicTest(testFramework, ShaderTestScene::UNIFORM_WITH_SAME_NAME_IN_BOTH_STAGES, "ShaderTestScene_MultiStageUniform"); - case EffectTest_ExplicitAttributeLocation: - return runBasicTest(testFramework, CustomShaderTestScene::EXPLICIT_ATTRIBUTE_LOCATION, "ShaderTestScene_TexturedQuad"); - case EffectTest_ExplicitAttributeLocationSwapped: - return runBasicTest(testFramework, CustomShaderTestScene::EXPLICIT_ATTRIBUTE_LOCATION_SWAPPED, "ShaderTestScene_TexturedQuad"); - case EffectTest_StructUniform: - return runBasicTest(testFramework, ShaderTestScene::STRUCT_UNIFORM, "ShaderTestScene_StructUniform"); - case EffectTest_BoolUniform: - return runBasicTest(testFramework, ShaderTestScene::BOOL_UNIFORM, "ShaderTestScene_BoolUniform"); - case EffectTest_TextureSize: - return runBasicTest(testFramework, ShaderTestScene::TEXTURE_SIZE, "ShaderTestScene_TextureSize"); - default: - assert(!"Invalid renderer test ID!"); - return false; - } -} - -template -bool EffectRenderingTests::runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - return testFramework.renderAndCompareScreenshot(expectedImageName, 0u); -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.h deleted file mode 100644 index 616163d6e..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/EffectRenderingTests.h +++ /dev/null @@ -1,38 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EFFECTRENDERINGTESTS_H -#define RAMSES_EFFECTRENDERINGTESTS_H - -#include "IRendererTest.h" - -class EffectRenderingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - template - bool runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName); - - enum - { - EffectTest_Shaders = 0, - EffectTest_Discard_Shaders, - EffectTest_OptimizedInput, - EffectTest_UniformWithSameNameInBothStages, - EffectTest_ExplicitAttributeLocation, - EffectTest_ExplicitAttributeLocationSwapped, - EffectTest_StructUniform, - EffectTest_BoolUniform, - EffectTest_TextureSize, - }; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.cpp deleted file mode 100644 index 7f73f4615..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.cpp +++ /dev/null @@ -1,296 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "InterruptibleOffscreenBufferLinkTests.h" -#include "TestScenes/TextureLinkScene.h" -#include "TestScenes/MultipleTrianglesScene.h" -#include "RenderExecutor.h" - -using namespace ramses_internal; - -void InterruptibleOffscreenBufferLinkTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene, *this, "InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes, *this, "InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene, *this, "InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene, *this, "InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted, *this, "InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted, *this, "InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered, *this, "InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer, *this, "InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); - testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer, *this, "InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer") - .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); -} - -bool InterruptibleOffscreenBufferLinkTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - testFramework.setFrameTimerLimits(std::numeric_limits::max(), 0u); - // Override the number of meshes rendered between the time budget checks, for the test we need to interrupt at every single renderable - ramses_internal::RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = 1u; - - bool testResultValue = true; - switch (testCase.m_id) - { - case InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - // provider scene has two meshes, there is interruption after each, that means 3 frames to finish the rendering - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 3u); - // once more frame to render FB with the finished OB contents - testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedFinished"); - break; - } - case InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER, m_cameraMid - glm::vec3{ 1.f, 0.f, 0.f }, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER, m_cameraMid + glm::vec3{ 1.f, 0.f, 0.f }, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - // each provider scene has two meshes, there is interruption after each mesh, that means 5 frames to finish the rendering - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 5u); - // once more frame to render FB with the finished OB contents - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes"); - break; - } - case InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene: - { - const ramses::sceneId_t trianglesSceneId = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES); - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState0"); - - testFramework.getScenesRegistry().setSceneState(trianglesSceneId, MultipleTrianglesScene::ALPHA_BLENDING); - testFramework.getScenesRegistry().getScene(trianglesSceneId).flush(); - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState1"); - - testFramework.getScenesRegistry().setSceneState(trianglesSceneId, MultipleTrianglesScene::COLOR_MASK); - testFramework.getScenesRegistry().getScene(trianglesSceneId).flush(); - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState2"); - - testFramework.getScenesRegistry().setSceneState(trianglesSceneId, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(trianglesSceneId).flush(); - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState3Linked"); - break; - } - case InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - const ramses::sceneId_t m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); - testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); - - // each provider scene has two meshes, there is interruption after each mesh, that means 3 frames to finish the 1st provider - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted2", 3u); - // in the 3rd frame 1st provider is finished and 2nd provider has one mesh rendered - - // 1st provider will be visible next frame, another 2 frames needed to finish 2nd provider - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedFinished2", 2u); - - // 2nd provider will be visible next frame - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene"); - - break; - } - case InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - // render 1 mesh and interrupt - testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedInterrupted"); - - // change scene state - testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); - - // render 3 more frames to finish initial scene state (and new state started to be rendered into OB's 'backbuffer') - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 3u); - // next frame initial scene state is shown in FB and the new state is finished after 4 more frames - testResultValue &= renderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles", 4u); - - // next frame new scene state is shown in FB - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered"); - break; - } - - case InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - const ramses::sceneId_t m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); - testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); - - // render 1 mesh and interrupt - testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedInterrupted2"); - - // change scene state - testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); - - // render 3 more frames to finish initial provider1 state (and provider2 started to be rendered) - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted2", 3u); - // next frame initial provider1 state is shown in FB and provider2 is finished after 2 more frames - testResultValue &= renderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles2", 2u); - // next frame provider2 is shown in FB and new state provider1 renders and finishes in 4 frames - testResultValue &= renderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles3", 4u); - // new state provider1 is shown in FB - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered2"); - break; - } - case InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - const ramses::sceneId_t m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); - testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); - - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted2", 4u); - //initial state of 1st provider scene gets visible (provider scene has 3 meshes, so get visible after 5 loops) - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_ThreeTriangles2"); - - //modify state of 1st provider (now 2nd provider is being rendered) - testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); - - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_ThreeTriangles2"); - //2nd provider gets visible (has 2 meshes) - testResultValue &= renderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles3", 4u); - //modification of 1st provider gets visible - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered2"); - break; - } - case InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid + glm::vec3{5.0f, 0.0f, 0.0f}, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer, 0); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer, -1); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - //render once then update provider - testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedInterrupted"); - - testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); - - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 5u); - //both provider get visible (have 5 meshes together, need 7 loops) - testResultValue &= renderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB", 6u); - //modification of 1st provider gets visible - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB"); - break; - } - case InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer: - { - const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid + glm::vec3{ 5.0f, 0.0f, 0.0f }, OBWidth, OBHeight); - const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer, 0); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer, -1); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 4u); - - //modify state of 1st provider (now 2nd provider is being rendered) - testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); - testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); - - testResultValue &= renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 2u); - //2nd provider gets visible (has 2 meshes) - testResultValue &= renderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB", 6u); - //modification of 1st provider gets visible - testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB"); - break; - } - - default: - assert(false && "undefined test case"); - } - - // Restore the default value for time budget checks - ramses_internal::RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = ramses_internal::RenderExecutor::DefaultNumRenderablesToRenderInBetweenTimeBudgetChecks; - - return testResultValue; -} - -template -ramses::sceneId_t InterruptibleOffscreenBufferLinkTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth, uint32_t vpHeight) -{ - const auto sceneId = testFramework.getScenesRegistry().createScene(sceneState, camPos, vpWidth, vpHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - - return sceneId; -} - -bool InterruptibleOffscreenBufferLinkTests::renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImage, uint32_t numFramesToRender) -{ - bool result = true; - - for (uint32_t i = 0u; i < numFramesToRender; ++i) - result &= testFramework.renderAndCompareScreenshot(expectedImage); - - return result; -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.h b/integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.h deleted file mode 100644 index 660f7fb2f..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/InterruptibleOffscreenBufferLinkTests.h +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_INTERRUPTIBLEOFFSCREENBUFFERLINKTESTS_H -#define RAMSES_INTERRUPTIBLEOFFSCREENBUFFERLINKTESTS_H - -#include "IRendererTest.h" - -#include - -class InterruptibleOffscreenBufferLinkTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final override; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final override; - -private: - enum - { - InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene, - InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes, - InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene, - InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene, - InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted, - InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted, - InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered, - InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer, - InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer, - }; - - template - ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos = { 0, 0, 0 }, uint32_t vpWidth = ramses_internal::IntegrationScene::DefaultViewportWidth, uint32_t vpHeight = ramses_internal::IntegrationScene::DefaultViewportHeight); - bool renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImage, uint32_t numFramesToRender = 1u); - - const glm::vec3 m_cameraMid{ 0.f, 0.f, 8.f }; - - static constexpr uint32_t OBWidth = 256u; - static constexpr uint32_t OBHeight = 256u; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.cpp deleted file mode 100644 index 0c5aa6a11..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.cpp +++ /dev/null @@ -1,318 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "OffscreenBufferLinkTests.h" -#include "TestScenes/TextureLinkScene.h" -#include "TestScenes/MultipleTrianglesScene.h" -#include "PlatformAbstraction/PlatformThread.h" - -using namespace ramses_internal; - -OffscreenBufferLinkTests::OffscreenBufferLinkTests(bool useInterruptibleBuffers) - : m_interruptibleBuffers(useInterruptibleBuffers) -{ -} - -void OffscreenBufferLinkTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - const std::string baseTestName = (m_interruptibleBuffers ? "InterruptibleOffscreenBufferLinkTest_" : "OffscreenBufferLinkTest_"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerLinkedToEmptyBuffer, *this, baseTestName + "ConsumerLinkedToEmptyBuffer"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumersLinkedToBufferWithOneScene, *this, baseTestName + "ConsumersLinkedToBufferWithOneScene"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_OneOfTwoLinksRemoved, *this, baseTestName + "OneOfTwoLinksRemoved"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerLinkedToBufferWithTwoScenes, *this, baseTestName + "ConsumerLinkedToBufferWithTwoScenes"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerRelinkedToAnotherBuffer, *this, baseTestName + "ConsumerRelinkedToAnotherBuffer"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ProviderBufferDestroyed, *this, baseTestName + "ProviderBufferDestroyed"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_SourceSceneHidden, *this, baseTestName + "SourceSceneHidden"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_SourceSceneAssignedToFBWhileShown, *this, baseTestName + "SourceSceneAssignedToFBWhileShown"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_OneOfTwoSourceScenesUnmapped, *this, baseTestName + "OneOfTwoSourceScenesUnmapped"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ProviderSceneUsesDepthTest, *this, baseTestName + "ProviderSceneUsesDepthTest"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ProviderSceneUsesStencilTest, *this, baseTestName + "ProviderSceneUsesStencilTest"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_SetClearColor, *this, baseTestName + "OffscreenBufferLinkTest_SetClearColor"); - if (!m_interruptibleBuffers) - { - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerLinkedToMSAABuffer, *this, baseTestName + "OffscreenBufferLinkTest_MSAA"); - testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerUnlinkedMSAABuffer, *this, baseTestName + "OffscreenBufferLinkTest_MSAAUnlinked"); - } -} - -bool OffscreenBufferLinkTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - std::string expectedImageName("!!unknown!!"); - float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel; - switch (testCase.m_id) - { - case OffscreenBufferLinkTest_ConsumerLinkedToEmptyBuffer: - { - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - expectedImageName = "OffscreenBufferLinkTest_Black"; - break; - } - case OffscreenBufferLinkTest_ConsumersLinkedToBufferWithOneScene: - { - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked_OB_Content", 0u, offscreenBuffer, OBWidth, OBHeight)) - return false; - - expectedImageName = "OffscreenBufferLinkTest_Linked"; - break; - } - case OffscreenBufferLinkTest_ConsumerLinkedToMSAABuffer: - { - m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.2f, 0.5f, -5.f), 16u, 16u); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_MS, { 0.2f, 0.f, -3.f }); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, 16u, 16u, m_interruptibleBuffers, 4u); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - expectedImageName = "OffscreenBufferLinkTest_MSAABuffer"; - break; - } - case OffscreenBufferLinkTest_ConsumerUnlinkedMSAABuffer: - { - m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.2f, 0.5f, -5.f), 16u, 16u); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_MS, { 0.2f, 0.f, -3.f }); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, 16u, 16u, m_interruptibleBuffers, 4u); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_MSAABuffer", 0u)) - { - return false; - } - - testFramework.removeDataLink(m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - expectedImageName = "OffscreenBufferLinkTest_UnlinkedMSAABuffer"; - break; - } - case OffscreenBufferLinkTest_ConsumerLinkedToBufferWithTwoScenes: - { - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_AND_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content", 0u, offscreenBuffer, OBWidth, OBHeight)) - return false; - - expectedImageName = "OffscreenBufferLinkTest_LinkedTwoScenes"; - break; - } - case OffscreenBufferLinkTest_ConsumerRelinkedToAnotherBuffer: - { - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_AND_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); - - const ramses::displayBufferId_t offscreenBuffer1 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer1); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); - testFramework.createBufferDataLink(offscreenBuffer1, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked_OB_Content", 0u, offscreenBuffer1, OBWidth, OBHeight)) - return false; - if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked2_OB_Content", 0u, offscreenBuffer2, OBWidth, OBHeight)) - return false; - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) - return false; - - // relink to another buffer - testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked2_OB_Content", 0u, offscreenBuffer2, OBWidth, OBHeight)) - return false; - - expectedImageName = "OffscreenBufferLinkTest_Linked2"; - break; - } - case OffscreenBufferLinkTest_OneOfTwoLinksRemoved: - { - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraHigh); - m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); - if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content", 0u, offscreenBuffer, OBWidth, OBHeight)) - return false; - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedTwoConsumers", 0u)) - { - return false; - } - - testFramework.removeDataLink(m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - expectedImageName = "OffscreenBufferLinkTest_Unlinked"; - break; - } - case OffscreenBufferLinkTest_ProviderBufferDestroyed: - { - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) - { - return false; - } - - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, testFramework.getDisplayFramebufferId(0)); - testFramework.destroyOffscreenBuffer(0, offscreenBuffer); - - expectedImageName = "OffscreenBufferLinkTest_BufferDestroyed"; - break; - } - case OffscreenBufferLinkTest_SourceSceneHidden: - { - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) - { - return false; - } - - testFramework.getSceneToState(m_sceneIdProvider, ramses::RendererSceneState::Ready); - - expectedImageName = "OffscreenBufferLinkTest_Black"; - break; - } - case OffscreenBufferLinkTest_SourceSceneAssignedToFBWhileShown: - { - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) - { - return false; - } - - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, testFramework.getDisplayFramebufferId(0)); - - expectedImageName = "OffscreenBufferLinkTest_BlackOB"; - break; - } - case OffscreenBufferLinkTest_OneOfTwoSourceScenesUnmapped: - { - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_AND_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedTwoScenes", 0u)) - { - return false; - } - - testFramework.getSceneToState(m_sceneIdProvider2, ramses::RendererSceneState::Available); - - // Scene unmapped -> buffer is still there, but is not cleared any more - expectedImageName = "OffscreenBufferLinkTest_Linked"; - break; - } - case OffscreenBufferLinkTest_ProviderSceneUsesDepthTest: - { - m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::DEPTH_FUNC, glm::vec3(0.2f, 0.5f, -5.f), OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, glm::vec3(0.f, 0.f, -3.f)); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers, 0u, ramses::EDepthBufferType::Depth); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - expectedImageName = "OffscreenBufferLinkTest_DepthTest"; - break; - } - case OffscreenBufferLinkTest_ProviderSceneUsesStencilTest: - { - m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::STENCIL_TEST_1, glm::vec3(0.f, 0.5f, -5.f), OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, glm::vec3(0.f, 0.f, -4.f)); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers, 0u, ramses::EDepthBufferType::DepthStencil); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - expectedImageName = "OffscreenBufferLinkTest_StencilTest"; - break; - } - case OffscreenBufferLinkTest_SetClearColor: - { - m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); - m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); - - const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); - testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); - testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); - - if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) - return false; - - testFramework.setClearColor(0u, ramses::displayBufferId_t::Invalid(), { 0, 1, 0, 1 }); - testFramework.setClearColor(0u, offscreenBuffer, { 0, 0, 1, 1 }); - - expectedImageName = "OffscreenBufferLinkTest_LinkedCustomClearColor"; - break; - } - default: - assert(false && "undefined test case"); - } - - return renderAndCompareScreenshot(testFramework, expectedImageName, 0u, expectedPixelError); -} - -template -ramses::sceneId_t OffscreenBufferLinkTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth, uint32_t vpHeight) -{ - const auto sceneId = testFramework.getScenesRegistry().createScene(sceneState, camPos, vpWidth, vpHeight); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - - return sceneId; -} - -bool OffscreenBufferLinkTests::renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx, float expectedPixelError) -{ - // Any changes to interruptible offscreen buffers affect the framebuffer (with consumer) only the next frame due to being executed always as last in each frame. - // Adding simply another doOneLoop here might not always work on wayland platforms, where a frame is not guaranteed to be rendered (see DisplayController::canRenderNewFrame). - // There is no way to find out if a frame was rendered and therefore the solution here is to execute reading of pixels twice before checking result - // (argument flag passed in call below), the read pixels command guarantees that a frame was rendered. - // Generally it still cannot be guaranteed if interruptible OB is really finished (not interrupted) but we assume no interruptions due to no rendering time limits used. - - return testFramework.renderAndCompareScreenshot(expectedImageName, testDisplayIdx, expectedPixelError, m_interruptibleBuffers); -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.h b/integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.h deleted file mode 100644 index 8f313509b..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/OffscreenBufferLinkTests.h +++ /dev/null @@ -1,63 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_OFFSCREENBUFFERLINKTESTS_H -#define RAMSES_OFFSCREENBUFFERLINKTESTS_H - -#include "IRendererTest.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -#include - -class OffscreenBufferLinkTests : public IRendererTest -{ -public: - explicit OffscreenBufferLinkTests(bool useInterruptibleBuffers); - - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - template - ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth = ramses_internal::IntegrationScene::DefaultViewportWidth, uint32_t vpHeight = ramses_internal::IntegrationScene::DefaultViewportHeight); - bool renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx = 0u, float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel); - - enum - { - OffscreenBufferLinkTest_ConsumerLinkedToEmptyBuffer, - OffscreenBufferLinkTest_ConsumersLinkedToBufferWithOneScene, - OffscreenBufferLinkTest_OneOfTwoLinksRemoved, - OffscreenBufferLinkTest_ConsumerLinkedToBufferWithTwoScenes, - OffscreenBufferLinkTest_ConsumerRelinkedToAnotherBuffer, - OffscreenBufferLinkTest_ProviderBufferDestroyed, - OffscreenBufferLinkTest_SourceSceneHidden, - OffscreenBufferLinkTest_SourceSceneAssignedToFBWhileShown, - OffscreenBufferLinkTest_OneOfTwoSourceScenesUnmapped, - OffscreenBufferLinkTest_ProviderSceneUsesDepthTest, - OffscreenBufferLinkTest_ProviderSceneUsesStencilTest, - OffscreenBufferLinkTest_SetClearColor, - OffscreenBufferLinkTest_ConsumerLinkedToMSAABuffer, - OffscreenBufferLinkTest_ConsumerUnlinkedMSAABuffer - }; - - ramses::sceneId_t m_sceneIdProvider; - ramses::sceneId_t m_sceneIdProvider2; - ramses::sceneId_t m_sceneIdConsumer; - ramses::sceneId_t m_sceneIdConsumer2; - - const glm::vec3 m_cameraLow{ -1.f, 2.f, 8.f }; - const glm::vec3 m_cameraHigh{ 1.f, -2.f, 8.f }; - const glm::vec3 m_cameraMid{ 0.f, 0.f, 8.f }; - - const bool m_interruptibleBuffers; - - static constexpr uint32_t OBWidth = 256u; - static constexpr uint32_t OBHeight = 256u; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.cpp deleted file mode 100644 index fe9c187dd..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RenderPassRenderingTests.h" -#include "TestScenes/RenderPassOnceScene.h" -#include "RendererTestUtils.h" - -using namespace ramses_internal; - -void RenderPassRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RenderOnce, *this, "RenderPassTest_RenderOnce"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RetriggerRenderOnce, *this, "RenderPassTest_RetriggerRenderOnce"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RemapSceneWithRenderOnce, *this, "RenderPassTest_RemapSceneWithRenderOnce"); -} - -bool RenderPassRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - switch (testCase.m_id) - { - case RenderPassTest_RenderOnce: - return runRenderOnceTest(testFramework); - case RenderPassTest_RetriggerRenderOnce: - return runRetriggerRenderOnceTest(testFramework); - case RenderPassTest_RemapSceneWithRenderOnce: - return runRemapSceneWithRenderOnceTest(testFramework); - default: - assert(!"Invalid renderer test ID!"); - return false; - } -} - -ramses::sceneId_t RenderPassRenderingTests::createAndSetupInitialRenderOnceScene(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(RenderPassOnceScene::INITIAL_RENDER_ONCE); - - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - - return sceneId; -} - -bool RenderPassRenderingTests::runRenderOnceTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = createAndSetupInitialRenderOnceScene(testFramework); - - // take 2 screenshots to make sure final image stays even with render once pass - if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial") || - !testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) - { - return false; - } - - // change clear color but do not retrigger render once pass, expect same result - testFramework.getScenesRegistry().setSceneState(sceneId, RenderPassOnceScene::CHANGE_CLEAR_COLOR); - testFramework.getScenesRegistry().getScene(sceneId).flush(); - - return testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial"); -} - -bool RenderPassRenderingTests::runRetriggerRenderOnceTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = createAndSetupInitialRenderOnceScene(testFramework); - - if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) - { - return false; - } - - // change clear color but do not retrigger render once pass, expect same result - testFramework.getScenesRegistry().setSceneState(sceneId, RenderPassOnceScene::CHANGE_CLEAR_COLOR); - testFramework.getScenesRegistry().getScene(sceneId).flush(); - - if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) - { - return false; - } - - // retrigger render once pass and expect new result - testFramework.getScenesRegistry().setSceneState(sceneId, RenderPassOnceScene::RETRIGGER_PASS); - testFramework.getScenesRegistry().getScene(sceneId).flush(); - - // take 2 screenshots to make sure final image stays even with render once pass - return testFramework.renderAndCompareScreenshot("RenderPassOnce_Retriggered") - && testFramework.renderAndCompareScreenshot("RenderPassOnce_Retriggered"); -} - -bool RenderPassRenderingTests::runRemapSceneWithRenderOnceTest(RendererTestsFramework& testFramework) -{ - const ramses::sceneId_t sceneId = createAndSetupInitialRenderOnceScene(testFramework); - - if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) - { - return false; - } - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); - if (!testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_Black")) - { - return false; - } - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); - - return testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial"); -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.h deleted file mode 100644 index 63669f121..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/RenderPassRenderingTests.h +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERPASSRENDERINGTESTS_H -#define RAMSES_RENDERPASSRENDERINGTESTS_H - -#include "IRendererTest.h" - -class RenderPassRenderingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - ramses::sceneId_t createAndSetupInitialRenderOnceScene(RendererTestsFramework& testFramework); - - bool runRenderOnceTest(RendererTestsFramework& testFramework); - bool runRetriggerRenderOnceTest(RendererTestsFramework& testFramework); - bool runRemapSceneWithRenderOnceTest(RendererTestsFramework& testFramework); - - enum - { - RenderPassTest_RenderOnce = 0, - RenderPassTest_RetriggerRenderOnce, - RenderPassTest_RemapSceneWithRenderOnce, - }; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.cpp deleted file mode 100644 index c0d277455..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RenderTargetRenderingTests.h" -#include "TestScenes/RenderTargetScene.h" -#include "TestScenes/RenderBufferScene.h" -#include "TestScenes/MsaaRenderBufferScene.h" -#include "TestScenes/MultipleRenderTargetScene.h" - -using namespace ramses_internal; - -void RenderTargetRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Perspective, *this, "RenderTarget_Perspective"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Orthographic, *this, "RenderTarget_Orthographic"); - - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGBA4, *this, "RenderTarget_Format_RGBA4"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_R8, *this, "RenderTarget_Format_R8"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RG8, *this, "RenderTarget_Format_RG8"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGB8, *this, "RenderTarget_Format_RGB8"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_R16F, *this, "RenderTarget_Format_R16F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_R32F, *this, "RenderTarget_Format_R32F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RG16F, *this, "RenderTarget_Format_RG16F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RG32F, *this, "RenderTarget_Format_RG32F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGB16F, *this, "RenderTarget_Format_RGB16F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGB32F, *this, "RenderTarget_Format_RGB32F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGBA16F, *this, "RenderTarget_Format_RGBA16F"); - testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGBA32F, *this, "RenderTarget_Format_RGBA32F"); - - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_TwoColorBuffersCleared, *this, "MultipleRenderTarget_TwoColorBuffersCleared"); - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_TwoColorBuffersWritten, *this, "MultipleRenderTarget_TwoColorBuffersWritten"); - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4, *this, "MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4"); - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne, *this, "MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne"); - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ReuseSameColorBufferInTwoRTs, *this, "MultipleRenderTarget_ReuseSameColorBufferInTwoRTs"); - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs, *this, "MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs"); - testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ReadFromDepth, *this, "MultipleRenderTarget_ReadFromDepth"); - - testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferNoDepthOrStencil, *this, "RenderBuffer_OneColorBufferNoDepthOrStencil"); - testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer, *this, "RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer"); - testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer, *this, "RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer"); - testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount2Blit, *this, "RenderBuffer_MsaaSampleCount2Blit"); - testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount4Blit, *this, "RenderBuffer_MsaaSampleCount4Blit"); - testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount4TexelFetch, *this, "RenderBuffer_MsaaSampleCount4TexelFetch"); -} - -bool RenderTargetRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - switch (testCase.m_id) - { - case RenderTarget_Perspective: - return runBasicTest(testFramework, RenderTargetScene::PERSPECTIVE_PROJECTION, "RenderTargetScene_PerspectiveProjection"); - case RenderTarget_Orthographic: - return runBasicTest(testFramework, RenderTargetScene::ORTHOGRAPHIC_PROJECTION, "RenderTargetScene_OrthographicProjection"); - case RenderTarget_Format_RGBA4: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGBA4, "RenderTargetScene_RedGreenBlue", 3.0f); // higher threshold needed due to conversion imprecision from 4 to 8 bit - case RenderTarget_Format_R8: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_R8, "RenderTargetScene_Red"); - case RenderTarget_Format_RG8: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RG8, "RenderTargetScene_RedGreen"); - case RenderTarget_Format_RGB8: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGB8, "RenderTargetScene_RedGreenBlue_NoAlpha"); - case RenderTarget_Format_R16F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_R16F, "RenderTargetScene_Red"); - case RenderTarget_Format_R32F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_R32F, "RenderTargetScene_Red"); - case RenderTarget_Format_RG16F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RG16F, "RenderTargetScene_RedGreen"); - case RenderTarget_Format_RG32F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RG32F, "RenderTargetScene_RedGreen"); - case RenderTarget_Format_RGB16F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGB16F, "RenderTargetScene_RedGreenBlue_NoAlpha"); - case RenderTarget_Format_RGB32F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGB32F, "RenderTargetScene_RedGreenBlue"); - case RenderTarget_Format_RGBA16F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGBA16F, "RenderTargetScene_RedGreenBlue", 0.35f); // higher threshold needed due to floating formats get sampled differently on different platforms - case RenderTarget_Format_RGBA32F: - return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGBA32F, "RenderTargetScene_RedGreenBlue", 0.35f); // higher threshold needed due to floating formats get sampled differently on different platforms - case MultipleRenderTarget_TwoColorBuffersCleared: - return runBasicTest(testFramework, MultipleRenderTargetScene::CLEAR_MRT, "MultiRenderTarget_ClearTwoColorBuffers"); - case MultipleRenderTarget_TwoColorBuffersWritten: - return runBasicTest(testFramework, MultipleRenderTargetScene::TWO_COLOR_BUFFERS, "MultiRenderTarget_TwoColorBuffers"); - case MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4: - return runBasicTest(testFramework, MultipleRenderTargetScene::TWO_COLOR_BUFFERS_RGBA8_AND_RGBA4, "MultiRenderTarget_TwoColorBuffers", 0.5f); // higher threshold needed due to conversion imprecision from 4 to 8 bit - case MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne: - return runBasicTest(testFramework, MultipleRenderTargetScene::SHADER_WRITES_TWO_COLOR_BUFFERS_RT_HAS_ONE, "MultiRenderTarget_OneColorBufferWritten"); - case MultipleRenderTarget_ReuseSameColorBufferInTwoRTs: - return runBasicTest(testFramework, MultipleRenderTargetScene::COLOR_WRITTEN_BY_TWO_DIFFERENT_RTS, "MultiRenderTarget_ColorBufferWrittenByTwoPasses"); - case MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs: - return runBasicTest(testFramework, MultipleRenderTargetScene::DEPTH_WRITTEN_AND_USED_BY_DIFFERENT_RT, "MultiRenderTarget_DepthBufferUsedByTwoPasses"); - case MultipleRenderTarget_ReadFromDepth: - return runBasicTest(testFramework, MultipleRenderTargetScene::DEPTH_WRITTEN_AND_READ, "MultiRenderTarget_DepthRead"); - case RenderBuffer_OneColorBufferNoDepthOrStencil: - return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL, "RenderBuffer_OneColorBufferNoDepthOrStencil"); - case RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer: - return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER, "RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer"); - case RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer: - return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER, "RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer"); - case RenderBuffer_MsaaSampleCount2Blit: - return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_2_BLIT, "RenderBuffer_MsaaSampleCount2Blit", 0.33f); - case RenderBuffer_MsaaSampleCount4Blit: - return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_4_BLIT, "RenderBuffer_MsaaSampleCount4Blit", 0.33f); - case RenderBuffer_MsaaSampleCount4TexelFetch: - return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_4_TEXEL_FETCH, "RenderBuffer_MsaaSampleCount4TexelFetch"); - - default: - assert(!"Invalid renderer test ID!"); - return false; - } -} - -template -bool RenderTargetRenderingTests::runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - return testFramework.renderAndCompareScreenshot(expectedImageName, 0u, maxAveragePercentErrorPerPixel); -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.h deleted file mode 100644 index c2bdc98c6..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/RenderTargetRenderingTests.h +++ /dev/null @@ -1,61 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERTARGETRENDERINGTESTS_H -#define RAMSES_RENDERTARGETRENDERINGTESTS_H - -#include "IRendererTest.h" - -#include - -class RenderTargetRenderingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - template - bool runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel); - - enum - { - RenderTarget_Perspective, - RenderTarget_Orthographic, - - RenderTarget_Format_RGBA4, - RenderTarget_Format_R8, - RenderTarget_Format_RG8, - RenderTarget_Format_RGB8, - RenderTarget_Format_R16F, - RenderTarget_Format_R32F, - RenderTarget_Format_RG16F, - RenderTarget_Format_RG32F, - RenderTarget_Format_RGB16F, - RenderTarget_Format_RGB32F, - RenderTarget_Format_RGBA16F, - RenderTarget_Format_RGBA32F, - - MultipleRenderTarget_TwoColorBuffersCleared, - MultipleRenderTarget_TwoColorBuffersWritten, - MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4, - MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne, - MultipleRenderTarget_ReuseSameColorBufferInTwoRTs, - MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs, - MultipleRenderTarget_ReadFromDepth, - - RenderBuffer_OneColorBufferNoDepthOrStencil, - RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer, - RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer, - RenderBuffer_MsaaSampleCount2Blit, - RenderBuffer_MsaaSampleCount4Blit, - RenderBuffer_MsaaSampleCount4TexelFetch - }; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/RenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/RenderingTests.h deleted file mode 100644 index 1a49b39d4..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/RenderingTests.h +++ /dev/null @@ -1,75 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERINGTESTS_H -#define RAMSES_RENDERINGTESTS_H - -#include "RendererTestsFramework.h" -#include "SceneRenderingTests.h" -#include "RenderTargetRenderingTests.h" -#include "RenderPassRenderingTests.h" -#include "EffectRenderingTests.h" -#include "TextureRenderingTests.h" -#include "DisplayRenderingTests.h" -#include "DataLinkingTests.h" -#include "OffscreenBufferLinkTests.h" -#include "Utils/LogMacros.h" -#include "InterruptibleOffscreenBufferLinkTests.h" - -#include -#include - -class RenderingTests -{ -public: - RenderingTests(const std::vector& filterIn, const std::vector& filterOut, bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) - : m_testFramework(generateScreenshots, config) - , m_offscreenBufferLinkTests(false) - , m_offscreenBufferLinkTestsUsingInterruptible(true) - , m_interruptibleOffscreenBufferLinkTests() - { - m_sceneRenderingTests.setUpTestCases(m_testFramework); - m_renderTargetRenderingTests.setUpTestCases(m_testFramework); - m_renderPassRenderingTests.setUpTestCases(m_testFramework); - m_effectRendererTests.setUpTestCases(m_testFramework); - m_textureRenderingTests.setUpTestCases(m_testFramework); - m_displayRenderingTests.setUpTestCases(m_testFramework); - m_dataLinkingTests.setUpTestCases(m_testFramework); - m_offscreenBufferLinkTests.setUpTestCases(m_testFramework); - m_offscreenBufferLinkTestsUsingInterruptible.setUpTestCases(m_testFramework); - m_interruptibleOffscreenBufferLinkTests.setUpTestCases(m_testFramework); - - m_testFramework.filterTestCases(filterIn, filterOut); - } - - bool runTests() - { - return m_testFramework.runAllTests(); - } - - void logReport() - { - fmt::print("{}\n", m_testFramework.generateReport()); - } - -protected: - RendererTestsFramework m_testFramework; - - SceneRenderingTests m_sceneRenderingTests; - RenderTargetRenderingTests m_renderTargetRenderingTests; - RenderPassRenderingTests m_renderPassRenderingTests; - EffectRenderingTests m_effectRendererTests; - TextureRenderingTests m_textureRenderingTests; - DisplayRenderingTests m_displayRenderingTests; - DataLinkingTests m_dataLinkingTests; - OffscreenBufferLinkTests m_offscreenBufferLinkTests; - OffscreenBufferLinkTests m_offscreenBufferLinkTestsUsingInterruptible; - InterruptibleOffscreenBufferLinkTests m_interruptibleOffscreenBufferLinkTests; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.cpp deleted file mode 100644 index 593e10d57..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.cpp +++ /dev/null @@ -1,414 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "SceneRenderingTests.h" -#include "SceneAPI/RenderState.h" -#include "RamsesRendererImpl.h" -#include "TestScenes/MultipleTrianglesScene.h" -#include "TestScenes/SingleAppearanceScene.h" -#include "TestScenes/HierarchicalRedTrianglesScene.h" -#include "TestScenes/MultipleGeometryScene.h" -#include "TestScenes/IndexArray32BitScene.h" -#include "TestScenes/RenderPassScene.h" -#include "TestScenes/BlitPassScene.h" -#include "TestScenes/RenderPassClearScene.h" -#include "TestScenes/TextScene.h" -#include "TestScenes/MultiLanguageTextScene.h" -#include "TestScenes/AntiAliasingScene.h" -#include "TestScenes/ArrayInputScene.h" -#include "TestScenes/GeometryInstanceScene.h" -#include "TestScenes/RenderTargetScene.h" -#include "TestScenes/ArrayBufferScene.h" -#include "TestScenes/GeometryShaderScene.h" -#include "TestScenes/ArrayResourceScene.h" - -using namespace ramses_internal; - -void SceneRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_Culling, *this, "RenderStateTest_Culling"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_ColorMask, *this, "RenderStateTest_ColorMask"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_DepthFunc, *this, "RenderStateTest_DepthFunc"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_DrawMode, *this, "RenderStateTest_DrawMode"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_StencilTest1, *this, "RenderStateTest_StencilTest1"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_StencilTest2, *this, "RenderStateTest_StencilTest2"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_StencilTest3, *this, "RenderStateTest_StencilTest3"); - testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_ScissorTest, *this, "RenderStateTest_ScissorTest"); - - testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_RedTriangles, *this, "AppearanceTest_RedTriangles"); - testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_GreenTriangles, *this, "AppearanceTest_GreenTriangles"); - testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_ChangeAppearance, *this, "AppearanceTest_ChangeAppearance"); - testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_TrianglesWithSharedColor, *this, "AppearanceTest_TrianglesWithSharedColor"); - testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_TrianglesWithUnsharedColor, *this, "AppearanceTest_TrianglesWithUnsharedColor"); - - testFramework.createTestCaseWithDefaultDisplay(BlendingTest_BlendingDisabled, *this, "BlendingTest_BlendingDisabled"); - testFramework.createTestCaseWithDefaultDisplay(BlendingTest_AlphaBlending, *this, "BlendingTest_AlphaBlending"); - testFramework.createTestCaseWithDefaultDisplay(BlendingTest_SubtractiveBlending, *this, "BlendingTest_SubtractiveBlending"); - testFramework.createTestCaseWithDefaultDisplay(BlendingTest_AdditiveBlending, *this, "BlendingTest_AdditiveBlending"); - testFramework.createTestCaseWithDefaultDisplay(BlendingTest_BlendingConstant, *this, "BlendingTest_BlendingConstant"); - testFramework.createTestCaseWithDefaultDisplay(BlendingTest_BlendingDstColorAndAlpha, *this, "BlendingTest_BlendingDstColorAndAlpha"); - - testFramework.createTestCaseWithDefaultDisplay(CameraTest_Perspective, *this, "CameraTest_Perspective"); - testFramework.createTestCaseWithDefaultDisplay(CameraTest_Orthographic, *this, "CameraTest_Orthographic"); - testFramework.createTestCaseWithDefaultDisplay(CameraTest_Viewport, *this, "CameraTest_Viewport"); - - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_DeleteMeshNode, *this, "SceneModificationTest_DeleteMeshNode"); - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_Invisible, *this, "SceneModificationTest_NoVisibility"); - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_VisibilityOff, *this, "SceneModificationTest_VisibilityOff"); - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_PartialVisibility, *this, "SceneModificationTest_PartialVisibility"); - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_RotateAndScale, *this, "SceneModificationTest_RotateAndScale"); - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_CameraTransformation, *this, "SceneModificationTest_CameraTransformation"); - testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_MeshRenderOrder, *this, "SceneModificationTest_MeshRenderOrder"); - - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_SharedAppearance, *this, "GeometryTest_SharedAppearance"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_TriangleListWithoutIndexArray, *this, "GeometryTest_TriangleListWithoutIndexArray"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_TriangleStripWithoutIndexArray, *this, "GeometryTest_TriangleStripWithoutIndexArray"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_32bitIndices, *this, "GeometryTest_32bitIndices"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_32bitIndicesWithOffset, *this, "GeometryTest_32bitIndicesWithOffset"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_16bitIndices, *this, "GeometryTest_16bitIndices"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_16bitIndicesWithOffset, *this, "GeometryTest_16bitIndicesWithOffset"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingWithUniform, *this, "GeometryTest_InstancingWithUniform"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingWithVertexArray, *this, "GeometryTest_InstancingWithVertexArray"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingAndNotInstancing, *this, "GeometryTest_InstancingAndNotInstancing"); - testFramework.createTestCaseWithDefaultDisplay(GeometryTest_VertexArraysWithOffset, *this, "GeometryTest_VertexArraysWithOffset"); - - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_MeshesNotInPassNotRendered, *this, "RenderPassTest_MeshesNotInPassNotRendered"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_DifferentCameras, *this, "RenderPassTest_DifferentCameras"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_MeshesInMultiplePasses, *this, "RenderPassTest_MeshesInMultiplePasses"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RenderOrder, *this, "RenderPassTest_RenderOrder"); - - testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsColorBuffer, *this, "BlitPassTest_BlitsColorBuffer"); - testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsSubregion, *this, "BlitPassTest_BlitsSubregion"); - testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsDepthBuffer, *this, "BlitPassTest_BlitsDepthBuffer"); - testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsDepthStencilBuffer, *this, "BlitPassTest_BlitsDepthStencilBuffer"); - - testFramework.createTestCaseWithDefaultDisplay(RenderGroupTest_RenderOrder, *this, "RenderGroupTest_RenderOrder"); - testFramework.createTestCaseWithDefaultDisplay(RenderGroupTest_RenderOrderWithNestedGroups, *this, "RenderGroupTest_RenderOrderWithNestedGroups"); - -#if defined(RAMSES_TEXT_ENABLED) - testFramework.createTestCaseWithDefaultDisplay(TextTest_SimpleText, *this, "TextTest_SimpleText"); - testFramework.createTestCaseWithDefaultDisplay(TextTest_DeletedTextsAndNode, *this, "TextTest_DeletedTextsAndNode"); - testFramework.createTestCaseWithDefaultDisplay(TextTest_DifferentLanguages, *this, "TextTest_DifferentLanguages"); - testFramework.createTestCaseWithDefaultDisplay(TextTest_ForceAutoHinting, *this, "TextTest_ForceAutoHinting"); - testFramework.createTestCaseWithDefaultDisplay(TextTest_FontCascade, *this, "TextTest_FontCascade"); - testFramework.createTestCaseWithDefaultDisplay(TextTest_FontCascadeWithVerticalOffset, *this, "TextTest_FontCascadeWithVerticalOffset"); - testFramework.createTestCaseWithDefaultDisplay(TextTest_Shaping, *this, "TextTest_Shaping"); -#endif - - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_None, *this, "RenderPassClear_None"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_Color, *this, "RenderPassClear_Color"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_Depth, *this, "RenderPassClear_Depth"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_Stencil, *this, "RenderPassClear_Stencil"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_ColorStencil, *this, "RenderPassClear_ColorStencil"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_ColorDepth, *this, "RenderPassClear_ColorDepth"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_StencilDepth, *this, "RenderPassClear_StencilDepth"); - testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_ColorStencilDepth, *this, "RenderPassClear_ColorStencilDepth"); - - testFramework.createTestCaseWithDefaultDisplay(ArrayInputTest_ArrayInputVec4, *this, "ArrayInputTest_ArrayInputVec4"); - testFramework.createTestCaseWithDefaultDisplay(ArrayInputTest_ArrayInputInt32, *this, "ArrayInputTest_ArrayInputInt32"); - testFramework.createTestCaseWithDefaultDisplay(ArrayInputTest_ArrayInputInt32DynamicIndex, *this, "ArrayInputTest_ArrayInputInt32DynamicIndex"); - - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_IndexDataBufferUInt16, *this, "DataBuffer_IndexDataBufferUInt16"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_IndexDataBufferUInt32, *this, "DataBuffer_IndexDataBufferUInt32"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferFloat, *this, "DataBuffer_VertexDataBufferFloat"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferVector2f, *this, "DataBuffer_VertexDataBufferVector2f"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferVector3f, *this, "DataBuffer_VertexDataBufferVector3f"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferVector4f, *this, "DataBuffer_VertexDataBufferVector4f"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_IndexDataBufferGetsUpdated, *this, "DataBuffer_IndexDataBufferGetsUpdated"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferGetsUpdated, *this, "DataBuffer_VertexDataBufferGetsUpdated"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_SwitchFromClientArrayResourceToDataBuffer, *this, "DataBuffer_SwitchFromClientArrayResourceToDataBuffer"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_SwitchFromDataBufferToClientArrayResource, *this, "DataBuffer_SwitchFromDataBufferToClientArrayResource"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute, *this, "DataBuffer_InterleavedVertexAttribute"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_GetsUpdated, *this, "DataBuffer_InterleavedVertexAttribute_GetsUpdated"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_TwoStrides, *this, "DataBuffer_InterleavedVertexAttribute_TwoStrides"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_SingleAttrib, *this, "DataBuffer_InterleavedVertexAttribute_SingleAttrib"); - testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_StartVertexOffset, *this, "DataBuffer_InterleavedVertexAttribute_StartVertexOffset"); - - testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute, *this, "ArrayResource_InterleavedVertexAttribute"); - testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute_TwoStrides, *this, "ArrayResource_InterleavedVertexAttribute_TwoStrides"); - testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute_SingleAttrib, *this, "ArrayResource_InterleavedVertexAttribute_SingleAttrib"); - testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute_StartVertexOffset, *this, "ArrayResource_InterleavedVertexAttribute_StartVertexOffset"); - - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_PointsInTriangleStripOut, *this, "GeometryShaderGlslV320_PointsInTriangleStripOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_PointsInLineStripOut, *this, "GeometryShaderGlslV320_PointsInLineStripOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_PointsInPointsOut, *this, "GeometryShaderGlslV320_PointsInPointsOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_TrianglesInTriangleStripOut, *this, "GeometryShaderGlslV320_TrianglesInTriangleStripOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_TrianglesInPointsOut, *this, "GeometryShaderGlslV320_TrianglesInPointsOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_PointsInTriangleStripOut, *this, "GeometryShaderGlslV310Extension_PointsInTriangleStripOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_PointsInLineStripOut, *this, "GeometryShaderGlslV310Extension_PointsInLineStripOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_PointsInPointsOut, *this, "GeometryShaderGlslV310Extension_PointsInPointsOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut, *this, "GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut"); - testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_TrianglesInPointsOut, *this, "GeometryShaderGlslV310Extension_TrianglesInPointsOut"); - - testFramework.createTestCaseWithDefaultDisplay(EulerRotationConventions, *this, "EulerRotationConventions"); - - testFramework.createTestCaseWithDefaultDisplay(Display_SetClearColor, *this, "Display_SetClearColor").m_displayConfigs.front().setClearColor({0.5f, 0.25f, 0.75f, 1.f}); - - RenderingTestCase& testCase = testFramework.createTestCaseWithDefaultDisplay(AntiAliasingTest_MSAA4, *this, "AntiAliasingTest_MSAA4"); - testCase.m_displayConfigs.front().setMultiSampling(4u); -} - -bool SceneRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - switch (testCase.m_id) - { - case RenderStateTest_Culling: - return runBasicTest(testFramework, MultipleTrianglesScene::FACE_CULLING, "MultipleTrianglesScene_FaceCulling"); - case RenderStateTest_ColorMask: - return runBasicTest(testFramework, MultipleTrianglesScene::COLOR_MASK, "MultipleTrianglesScene_ColorMask"); - case RenderStateTest_DepthFunc: - return runBasicTest(testFramework, MultipleTrianglesScene::DEPTH_FUNC, "MultipleTrianglesScene_DepthFunc"); - case RenderStateTest_DrawMode: - return runBasicTest(testFramework, MultipleTrianglesScene::DRAW_MODE, "MultipleTrianglesScene_DrawMode", 1.1f); - case RenderStateTest_StencilTest1: - return runBasicTest(testFramework, MultipleTrianglesScene::STENCIL_TEST_1, "MultipleTrianglesScene_StencilTest_1"); - case RenderStateTest_StencilTest2: - return runBasicTest(testFramework, MultipleTrianglesScene::STENCIL_TEST_2, "MultipleTrianglesScene_StencilTest_2"); - case RenderStateTest_StencilTest3: - return runBasicTest(testFramework, MultipleTrianglesScene::STENCIL_TEST_3, "MultipleTrianglesScene_StencilTest_3"); - case RenderStateTest_ScissorTest: - return runBasicTest(testFramework, MultipleTrianglesScene::SCISSOR_TEST, "MultipleTrianglesScene_ScissorTest"); - case AppearanceTest_RedTriangles: - return runBasicTest(testFramework, SingleAppearanceScene::RED_TRIANGLES, "SingleAppearanceScene_RedTriangles"); - case AppearanceTest_GreenTriangles: - return runBasicTest(testFramework, SingleAppearanceScene::GREEN_TRIANGLES, "SingleAppearanceScene_GreenTriangles"); - case AppearanceTest_ChangeAppearance: - return runBasicTest(testFramework, SingleAppearanceScene::CHANGE_APPEARANCE, "SingleAppearanceScene_ChangeAppearance"); - case AppearanceTest_TrianglesWithSharedColor: - return runBasicTest(testFramework, MultipleTrianglesScene::THREE_TRIANGLES_WITH_SHARED_COLOR, "MultipleTrianglesScene_ThreeTrianglesRed"); - case AppearanceTest_TrianglesWithUnsharedColor: - return runBasicTest(testFramework, MultipleTrianglesScene::THREE_TRIANGLES_WITH_UNSHARED_COLOR, "MultipleTrianglesScene_ThreeTriangles"); - - case BlendingTest_BlendingDisabled: - return runBasicTest(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, "MultipleTrianglesScene_ThreeTriangles"); - case BlendingTest_AlphaBlending: - return runBasicTest(testFramework, MultipleTrianglesScene::ALPHA_BLENDING, "MultipleTrianglesScene_AlphaBlending"); - case BlendingTest_SubtractiveBlending: - return runBasicTest(testFramework, MultipleTrianglesScene::SUBTRACTIVE_BLENDING, "MultipleTrianglesScene_SubtractiveBlending"); - case BlendingTest_AdditiveBlending: - return runBasicTest(testFramework, MultipleTrianglesScene::ADDITIVE_BLENDING, "MultipleTrianglesScene_AdditiveBlending"); - case BlendingTest_BlendingConstant: - return runBasicTest(testFramework, MultipleTrianglesScene::BLENDING_CONSTANT, "MultipleTrianglesScene_BlendingConstant"); - case BlendingTest_BlendingDstColorAndAlpha: - return runBasicTest(testFramework, MultipleTrianglesScene::BLENDING_DST_COLOR_AND_ALPHA, "MultipleTrianglesScene_BlendingDstColorAndAlpha"); - - case CameraTest_Perspective: - return runBasicTest(testFramework, MultipleTrianglesScene::PERSPECTIVE_CAMERA, "MultipleTrianglesScene_PerspectiveCamera"); - case CameraTest_Orthographic: - return runBasicTest(testFramework, MultipleTrianglesScene::ORTHOGRAPHIC_CAMERA, "MultipleTrianglesScene_OrthographicCamera"); - case CameraTest_Viewport: - return runBasicTest(testFramework, RenderPassScene::PASSES_WITH_LEFT_AND_RIGHT_VIEWPORT, "RenderPassScene_LeftRightViewport"); - - case SceneModificationTest_DeleteMeshNode: - return runBasicTest(testFramework, HierarchicalRedTrianglesScene::DELETE_MESHNODE, "HierarchicalRedTrianglesScene_DeleteMeshNode"); - case SceneModificationTest_Invisible: - return runBasicTest(testFramework, HierarchicalRedTrianglesScene::INVISIBLE, "HierarchicalRedTrianglesScene_AllTrianglesInvisible"); - case SceneModificationTest_VisibilityOff: - return runBasicTest(testFramework, HierarchicalRedTrianglesScene::VISIBILITY_OFF, "HierarchicalRedTrianglesScene_AllTrianglesInvisible"); - case SceneModificationTest_PartialVisibility: - return runBasicTest(testFramework, HierarchicalRedTrianglesScene::PARTIAL_VISIBILITY, "HierarchicalRedTrianglesScene_SomeTrianglesInvisible"); - case SceneModificationTest_RotateAndScale: - return runBasicTest(testFramework, HierarchicalRedTrianglesScene::ROTATE_AND_SCALE, "HierarchicalRedTrianglesScene_RotateAndScale"); - case SceneModificationTest_CameraTransformation: - return runBasicTest(testFramework, MultipleTrianglesScene::CAMERA_TRANSFORMATION, "MultipleTrianglesScene_CameraTransformation"); - case SceneModificationTest_MeshRenderOrder: - return runBasicTest(testFramework, MultipleTrianglesScene::TRIANGLES_REORDERED, "MultipleTrianglesScene_RenderingOrderChanged"); - - case GeometryTest_SharedAppearance: - return runBasicTest(testFramework, MultipleGeometryScene::MULTI_TRIANGLE_LIST_GEOMETRY_WITH_INDEX_ARRAY, "MultipleGeometryScene_MultipleGeometry"); - case GeometryTest_TriangleListWithoutIndexArray: - return runBasicTest(testFramework, MultipleGeometryScene::MULTI_TRIANGLE_LIST_GEOMETRY_WITHOUT_INDEX_ARRAY, "MultipleGeometryScene_MultipleGeometry"); - case GeometryTest_TriangleStripWithoutIndexArray: - return runBasicTest(testFramework, MultipleGeometryScene::MULTI_TRIANGLE_STRIP_GEOMETRY_WITHOUT_INDEX_ARRAY, "MultipleGeometryScene_MultipleGeometry"); - case GeometryTest_32bitIndices: - return runBasicTest(testFramework, IndexArray32BitScene::NO_OFFSET_32BIT_INDICES, "IndexArray32BitScene_NoOffset32BitIndices"); - case GeometryTest_32bitIndicesWithOffset: - return runBasicTest(testFramework, IndexArray32BitScene::OFFSET_32BIT_INDICES, "IndexArray32BitScene_Offset32BitIndices"); - case GeometryTest_16bitIndices: - return runBasicTest(testFramework, IndexArray32BitScene::NO_OFFSET_16BIT_INDICES, "IndexArray32BitScene_NoOffset16BitIndices"); - case GeometryTest_16bitIndicesWithOffset: - return runBasicTest(testFramework, IndexArray32BitScene::OFFSET_16BIT_INDICES, "IndexArray32BitScene_Offset16BitIndices"); - case GeometryTest_InstancingWithUniform: - return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_UNIFORM, "GeometryInstanceScene_Instancing"); - case GeometryTest_InstancingWithVertexArray: - return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_VERTEX, "GeometryInstanceScene_Instancing"); - case GeometryTest_InstancingAndNotInstancing: - return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_AND_NOT_INSTANCE, "GeometryInstanceScene_InstancingAndNotInstancing"); - case GeometryTest_VertexArraysWithOffset: - return runBasicTest(testFramework, MultipleGeometryScene::VERTEX_ARRAYS_WITH_OFFSET, "MultipleGeometryScene_MultipleGeometry"); - - case RenderPassTest_MeshesNotInPassNotRendered: - return runBasicTest(testFramework, RenderPassScene::MESHES_NOT_IN_PASS, "RenderPassScene_MeshNotInPass"); - case RenderPassTest_DifferentCameras: - return runBasicTest(testFramework, RenderPassScene::ONE_MESH_PER_PASS, "RenderPassScene_OneMeshPerPass"); - case RenderPassTest_MeshesInMultiplePasses: - return runBasicTest(testFramework, RenderPassScene::MESH_IN_MULTIPLE_PASSES, "RenderPassScene_MeshInMultiplePasses"); - case RenderPassTest_RenderOrder: - return runBasicTest(testFramework, RenderPassScene::PASSES_WITH_DIFFERENT_RENDER_ORDER, "RenderPassScene_PassesWithDifferentRenderOrder"); - - case BlitPassTest_BlitsColorBuffer: - return runBasicTest(testFramework, BlitPassScene::BLITS_COLOR_BUFFER, "BlitPassTest_BlitsColorBuffer"); - case BlitPassTest_BlitsSubregion: - return runBasicTest(testFramework, BlitPassScene::BLITS_SUBREGION, "BlitPassTest_BlitsSubregion", 0.25f); - case BlitPassTest_BlitsDepthBuffer: - return runBasicTest(testFramework, BlitPassScene::BLITS_DEPTH_BUFFER, "BlitPassTest_BlitsDepthBuffer", 0.25f); - case BlitPassTest_BlitsDepthStencilBuffer: - return runBasicTest(testFramework, BlitPassScene::BLITS_DEPTH_STENCIL_BUFFER, "BlitPassTest_BlitsDepthStencilBuffer"); - - case RenderGroupTest_RenderOrder: - return runBasicTest(testFramework, RenderPassScene::GROUPS_WITH_DIFFERENT_RENDER_ORDER, "RenderPassScene_PassesWithDifferentRenderOrder"); - case RenderGroupTest_RenderOrderWithNestedGroups: - return runBasicTest(testFramework, RenderPassScene::NESTED_GROUPS, "RenderPassScene_PassesWithDifferentRenderOrder"); - -#if defined(RAMSES_TEXT_ENABLED) - case TextTest_SimpleText: - return runBasicTest(testFramework, TextScene::EState_INITIAL, "TextScene_SimpleText"); - case TextTest_DeletedTextsAndNode: - return runBasicTest(testFramework, TextScene::EState_DELETED_TEXTS, "TextScene_DeletedTextsAndNode"); - case TextTest_ForceAutoHinting: - return runBasicTest(testFramework, TextScene::EState_FORCE_AUTO_HINTING, "TextScene_ForceAutoHinting"); - case TextTest_FontCascade: - return runBasicTest(testFramework, TextScene::EState_FONT_CASCADE, "TextScene_FontCascade"); - case TextTest_FontCascadeWithVerticalOffset: - return runBasicTest(testFramework, TextScene::EState_FONT_CASCADE_WITH_VERTICAL_OFFSET, "TextScene_VerticalOffset"); - case TextTest_Shaping: - return runBasicTest(testFramework, TextScene::EState_SHAPING, "TextScene_Shaping"); - case TextTest_DifferentLanguages: - return runBasicTest(testFramework, MultiLanguageTextScene::EState_INITIAL, "MultiLanguageScene_MultiLanguageText"); -#endif - - case AntiAliasingTest_MSAA4: - return runBasicTest(testFramework, AntiAliasingScene::MSAA_4_STATE, "AntiAliasingScene_MSAAx4", 2.5f); - - case RenderPassClear_None: - return runBasicTest(testFramework, EClearFlags_None, "RenderPassClear_None"); - case RenderPassClear_Color: - return runBasicTest(testFramework, EClearFlags_Color, "RenderPassClear_Color"); - case RenderPassClear_Depth: - return runBasicTest(testFramework, EClearFlags_Depth, "RenderPassClear_Depth"); - case RenderPassClear_Stencil: - return runBasicTest(testFramework, EClearFlags_Stencil, "RenderPassClear_Stencil"); - case RenderPassClear_ColorStencil: - return runBasicTest(testFramework, EClearFlags_Color | EClearFlags_Stencil, "RenderPassClear_ColorStencil"); - case RenderPassClear_ColorDepth: - return runBasicTest(testFramework, EClearFlags_Color | EClearFlags_Depth, "RenderPassClear_ColorDepth"); - case RenderPassClear_StencilDepth: - return runBasicTest(testFramework, EClearFlags_Stencil | EClearFlags_Depth, "RenderPassClear_StencilDepth"); - case RenderPassClear_ColorStencilDepth: - return runBasicTest(testFramework, EClearFlags_All, "RenderPassClear_ColorStencilDepth"); - - case ArrayInputTest_ArrayInputVec4: - return runBasicTest(testFramework, ArrayInputScene::ARRAY_INPUT_VEC4, "ArrayInputScene_ArrayInputVec4"); - case ArrayInputTest_ArrayInputInt32: - return runBasicTest(testFramework, ArrayInputScene::ARRAY_INPUT_INT32, "ArrayInputScene_ArrayInputInt32"); - case ArrayInputTest_ArrayInputInt32DynamicIndex: - return runBasicTest(testFramework, ArrayInputScene::ARRAY_INPUT_INT32_DYNAMIC_INDEX, "ArrayInputScene_ArrayInputInt32DynamicIndex"); - - case DataBuffer_IndexDataBufferUInt16: - return runBasicTest(testFramework, ArrayBufferScene::INDEX_DATA_BUFFER_UINT16, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_IndexDataBufferUInt32: - return runBasicTest(testFramework, ArrayBufferScene::INDEX_DATA_BUFFER_UINT32, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_VertexDataBufferFloat: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_FLOAT, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_VertexDataBufferVector2f: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR2F, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_VertexDataBufferVector3f: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR3F, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_VertexDataBufferVector4f: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_IndexDataBufferGetsUpdated: - { - const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::INDEX_DATA_BUFFER_UINT32, glm::vec3(2, -1, 18)); - testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::UPDATE_INDEX_DATA_BUFFER); - return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangleInverted", 0u); - } - case DataBuffer_VertexDataBufferGetsUpdated: - { - const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F, glm::vec3(2, -1, 18)); - testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::UPDATE_VERTEX_DATA_BUFFER); - return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangleInverted", 0u); - } - case DataBuffer_SwitchFromClientArrayResourceToDataBuffer: - { - const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_ARRAY_RESOURCE_VECTOR4F, glm::vec3(2, -1, 18)); - testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F); - return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangle", 0u); - } - case DataBuffer_SwitchFromDataBufferToClientArrayResource: - { - const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F, glm::vec3(2, -1, 18)); - testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::VERTEX_ARRAY_RESOURCE_VECTOR4F); - return testFramework.renderAndCompareScreenshot("DataBufferScene_EquilateralTriangle", 0u); - } - case DataBuffer_InterleavedVertexAttribute: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_InterleavedVertexAttribute_GetsUpdated: - { - const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED, glm::vec3(2, -1, 18)); - testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::UPDATE_INTERLEAVED_VERTEX_DATA_BUFFER); - return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangleInverted", 0u); - } - case DataBuffer_InterleavedVertexAttribute_TwoStrides: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED_TWO_STRIDES, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case ArrayResource_InterleavedVertexAttribute: - return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case ArrayResource_InterleavedVertexAttribute_TwoStrides: - return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED_TWO_STRIDES, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case ArrayResource_InterleavedVertexAttribute_SingleAttrib: - return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED_SINGLE_ATTRIB, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case ArrayResource_InterleavedVertexAttribute_StartVertexOffset: - return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED_START_VERTEX, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_InterleavedVertexAttribute_SingleAttrib: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED_SINGLE_ATTRIB, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case DataBuffer_InterleavedVertexAttribute_StartVertexOffset: - return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED_START_VERTEX, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); - case Display_SetClearColor: - return testFramework.renderAndCompareScreenshot("Display_SetClearColor", 0u, 0.4f); - case GeometryShaderGlslV320_PointsInTriangleStripOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL320_POINTS_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_PointsInTriangleStripOut", 0.f); - case GeometryShaderGlslV320_PointsInLineStripOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL320_POINTS_IN_LINE_STRIP_OUT, "GeometryShaderScene_PointsInLineStripOut", 0.f); - case GeometryShaderGlslV320_PointsInPointsOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL320_POINTS_IN_POINTS_OUT, "GeometryShaderScene_PointsInPointsOut", .1f); - case GeometryShaderGlslV320_TrianglesInTriangleStripOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL320_TRIANGLES_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_TrianglesInTriangleStripOut", 0.f); - case GeometryShaderGlslV320_TrianglesInPointsOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL320_TRIANGLES_IN_POINTS_OUT, "GeometryShaderScene_TrianglesInPointsOut", .1f); - - case GeometryShaderGlslV310Extension_PointsInTriangleStripOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL310_POINTS_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_PointsInTriangleStripOut", 0.f); - case GeometryShaderGlslV310Extension_PointsInLineStripOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL310_POINTS_IN_LINE_STRIP_OUT, "GeometryShaderScene_PointsInLineStripOut", 0.f); - case GeometryShaderGlslV310Extension_PointsInPointsOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL310_POINTS_IN_POINTS_OUT, "GeometryShaderScene_PointsInPointsOut", .1f); - case GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL310_TRIANGLES_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_TrianglesInTriangleStripOut", 0.f); - case GeometryShaderGlslV310Extension_TrianglesInPointsOut: - return runBasicTest(testFramework, GeometryShaderScene::GLSL310_TRIANGLES_IN_POINTS_OUT, "GeometryShaderScene_TrianglesInPointsOut", .1f); - - case EulerRotationConventions: - return runBasicTest(testFramework, MultipleTrianglesScene::EULER_ROTATION_CONVENTIONS, "MultipleTriangleScene_EulerRotationConventions"); - - default: - assert(!"Invalid renderer test ID!"); - return false; - } -} - -template -bool SceneRenderingTests::runBasicTest( - RendererTestsFramework& testFramework, - uint32_t sceneState, - const std::string& expectedImageName, - float maxAveragePercentErrorPerPixel, - const glm::vec3& cameraTranslation, - bool saveDiffOnError) -{ - testFramework.createAndShowScene(sceneState, cameraTranslation); - return testFramework.renderAndCompareScreenshot(expectedImageName, 0u, maxAveragePercentErrorPerPixel, false, saveDiffOnError); -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.h deleted file mode 100644 index ceae2448a..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/SceneRenderingTests.h +++ /dev/null @@ -1,158 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENERENDERINGTESTS_H -#define RAMSES_SCENERENDERINGTESTS_H - -#include "IRendererTest.h" - -#include - -class SceneRenderingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - template - bool runBasicTest( - RendererTestsFramework& testFramework, - uint32_t sceneState, - const std::string& expectedImageName, - float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, - const glm::vec3& cameraTranslation = glm::vec3(0.0f), - bool saveDiffOnError = true); - - enum - { - RenderStateTest_Culling = 0, - RenderStateTest_ColorMask, - RenderStateTest_DepthFunc, - RenderStateTest_DrawMode, - RenderStateTest_StencilTest1, - RenderStateTest_StencilTest2, - RenderStateTest_StencilTest3, - RenderStateTest_ScissorTest, - - AppearanceTest_RedTriangles, - AppearanceTest_GreenTriangles, - AppearanceTest_ChangeAppearance, - AppearanceTest_TrianglesWithSharedColor, - AppearanceTest_TrianglesWithUnsharedColor, - - BlendingTest_BlendingDisabled, - BlendingTest_AlphaBlending, - BlendingTest_SubtractiveBlending, - BlendingTest_AdditiveBlending, - BlendingTest_BlendingConstant, - BlendingTest_BlendingDstColorAndAlpha, - - CameraTest_Perspective, - CameraTest_Orthographic, - CameraTest_Viewport, - - SceneModificationTest_DeleteMeshNode, - SceneModificationTest_Invisible, - SceneModificationTest_VisibilityOff, - SceneModificationTest_PartialVisibility, - SceneModificationTest_RotateAndScale, - SceneModificationTest_CameraTransformation, - SceneModificationTest_MeshRenderOrder, - - GeometryTest_SharedAppearance, - GeometryTest_32bitIndices, - GeometryTest_32bitIndicesWithOffset, - GeometryTest_16bitIndices, - GeometryTest_16bitIndicesWithOffset, - GeometryTest_InstancingWithUniform, - GeometryTest_InstancingWithVertexArray, - GeometryTest_InstancingAndNotInstancing, - GeometryTest_TriangleListWithoutIndexArray, - GeometryTest_TriangleStripWithoutIndexArray, - GeometryTest_VertexArraysWithOffset, - - RenderPassTest_MeshesNotInPassNotRendered, - RenderPassTest_DifferentCameras, - RenderPassTest_MeshesInMultiplePasses, - RenderPassTest_RenderOrder, - - BlitPassTest_BlitsColorBuffer, - BlitPassTest_BlitsSubregion, - BlitPassTest_BlitsDepthBuffer, - BlitPassTest_BlitsDepthStencilBuffer, - - RenderGroupTest_RenderOrder, - RenderGroupTest_RenderOrderWithNestedGroups, - -#if defined(RAMSES_TEXT_ENABLED) - TextTest_SimpleText, - TextTest_DeletedTextsAndNode, - TextTest_DifferentLanguages, - TextTest_ForceAutoHinting, - TextTest_FontCascade, - TextTest_FontCascadeWithVerticalOffset, - TextTest_Shaping, -#endif - - AnimationTest_AnimatedScene, - - AntiAliasingTest_MSAA4, - - RenderPassClear_None, - RenderPassClear_Color, - RenderPassClear_Depth, - RenderPassClear_Stencil, - RenderPassClear_ColorStencil, - RenderPassClear_ColorDepth, - RenderPassClear_StencilDepth, - RenderPassClear_ColorStencilDepth, - - ArrayInputTest_ArrayInputVec4, - ArrayInputTest_ArrayInputInt32, - ArrayInputTest_ArrayInputInt32DynamicIndex, - - DataBuffer_IndexDataBufferUInt16, - DataBuffer_IndexDataBufferUInt32, - DataBuffer_VertexDataBufferFloat, - DataBuffer_VertexDataBufferVector2f, - DataBuffer_VertexDataBufferVector3f, - DataBuffer_VertexDataBufferVector4f, - DataBuffer_IndexDataBufferGetsUpdated, - DataBuffer_VertexDataBufferGetsUpdated, - DataBuffer_SwitchFromClientArrayResourceToDataBuffer, - DataBuffer_SwitchFromDataBufferToClientArrayResource, - DataBuffer_InterleavedVertexAttribute, - DataBuffer_InterleavedVertexAttribute_GetsUpdated, - DataBuffer_InterleavedVertexAttribute_TwoStrides, - DataBuffer_InterleavedVertexAttribute_SingleAttrib, - DataBuffer_InterleavedVertexAttribute_StartVertexOffset, - - ArrayResource_InterleavedVertexAttribute, - ArrayResource_InterleavedVertexAttribute_TwoStrides, - ArrayResource_InterleavedVertexAttribute_SingleAttrib, - ArrayResource_InterleavedVertexAttribute_StartVertexOffset, - - Display_SetClearColor, - - GeometryShaderGlslV320_PointsInTriangleStripOut, - GeometryShaderGlslV320_PointsInLineStripOut, - GeometryShaderGlslV320_PointsInPointsOut, - GeometryShaderGlslV320_TrianglesInTriangleStripOut, - GeometryShaderGlslV320_TrianglesInPointsOut, - GeometryShaderGlslV310Extension_PointsInTriangleStripOut, - GeometryShaderGlslV310Extension_PointsInLineStripOut, - GeometryShaderGlslV310Extension_PointsInPointsOut, - GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut, - GeometryShaderGlslV310Extension_TrianglesInPointsOut, - - EulerRotationConventions - }; -}; - -#endif diff --git a/integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.cpp b/integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.cpp deleted file mode 100644 index ca963819e..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.cpp +++ /dev/null @@ -1,297 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "TextureRenderingTests.h" - -#include "TestScenes/Texture2DAnisotropicTextureFilteringScene.h" -#include "TestScenes/Texture2DFormatScene.h" -#include "TestScenes/Texture2DSamplingScene.h" -#include "TestScenes/Texture2DGenerateMipMapScene.h" -#include "TestScenes/Texture2DCompressedMipMapScene.h" -#include "TestScenes/TextureAddressScene.h" -#include "TestScenes/Texture3DScene.h" -#include "TestScenes/CubeTextureScene.h" -#include "TestScenes/TextureCubeAnisotropicTextureFilteringScene.h" -#include "TestScenes/TextureBufferScene.h" -#include "TestScenes/TextureSamplerScene.h" - -using namespace ramses_internal; - -void TextureRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) -{ - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_R8, *this, "TextureTest_Texture2D_Format_R8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RG8, *this, "TextureTest_Texture2D_Format_RG8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha, *this, "TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB8, *this, "TextureTest_Texture2D_Format_RGB8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB565, *this, "TextureTest_Texture2D_Format_RGB565"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA8, *this, "TextureTest_Texture2D_Format_RGBA8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA4, *this, "TextureTest_Texture2D_Format_RGBA4"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA5551, *this, "TextureTest_Texture2D_Format_RGBA5551"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_Swizzled_BGR8, *this, "TextureTest_Texture2D_Format_Swizzled_BGR8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_Swizzled_BGRA8, *this, "TextureTest_Texture2D_Format_Swizzled_BGRA8"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_R16F, *this, "TextureTest_Texture2D_Format_R16F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_R32F, *this, "TextureTest_Texture2D_Format_R32F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RG16F, *this, "TextureTest_Texture2D_Format_RG16F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RG32F, *this, "TextureTest_Texture2D_Format_RG32F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB16F, *this, "TextureTest_Texture2D_Format_RGB16F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB32F, *this, "TextureTest_Texture2D_Format_RGB32F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA16F, *this, "TextureTest_Texture2D_Format_RGBA16F"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA32F, *this, "TextureTest_Texture2D_Format_RGBA32F"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_SRGB8, *this, "TextureTest_Texture2D_Format_SRGB8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_SRGB8_ALPHA8, *this, "TextureTest_Texture2D_Format_SRGB8_ALPHA8"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ETC2RGB, *this, "TextureTest_Texture2D_Format_ETC2RGB"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ETC2RGBA, *this, "TextureTest_Texture2D_Format_ETC2RGBA"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ASTC_RGBA_4x4, *this, "TextureTest_Texture2D_Format_ASTC_RGBA_4x4"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4, *this, "TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_Nearest, *this, "TextureTest_Texture2D_Sampling_Nearest"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_NearestWithMipMaps, *this, "TextureTest_Texture2D_Sampling_NearestWithMipMaps"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_Bilinear, *this, "TextureTest_Texture2D_Sampling_Bilinear"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_BilinearWithMipMaps, *this, "TextureTest_Texture2D_Sampling_BilinearWithMipMaps"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_Trilinear, *this, "TextureTest_Texture2D_Sampling_Trilinear"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_MinLinearMagNearest, *this, "TextureTest_Texture2D_Sampling_MinLinearMagNearest"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_MinNearestMagLinear, *this, "TextureTest_Texture2D_Sampling_MinNearestMagLinear"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_AddressMode, *this, "TextureTest_Texture2D_AddressMode"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_AnisotropicFilter, *this, "TextureTest_Texture2D_AnisotropicFilter"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_GenerateMipMapSingle, *this, "TextureTest_Texture2D_GenerateSingleMipMap"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_GenerateMipMapMultiple, *this, "TextureTest_Texture2D_GenerateMultipleMipMaps"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_CompressedMipMap, *this, "TextureTest_Texture2D_CompressedMipMap"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture3D_RGBA8, *this, "TextureTest_Texture3D_RGBA8"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_CubeMap_RGBA8, *this, "TextureTest_CubeMap_RGBA8"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_CubeMap_BGRA_Swizzled, *this, "TextureTest_CubeMap_BGRA_Swizzled"); - testFramework.createTestCaseWithDefaultDisplay(TextureTest_CubeMap_Float, *this, "TextureTest_CubeMap_Float"); - - testFramework.createTestCaseWithDefaultDisplay(TextureTest_TextureCube_AnisotropicFilter, *this, "TextureTest_TextureCube_AnisotropicFilter"); - - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_RGBA8_OneMip, *this, "TextureBufferTest_RGBA8_OneMip"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_RGBA8_ThreeMips, *this, "TextureBufferTest_RGBA8_ThreeMips"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdate, *this, "TextureBufferTest_PartialUpdate"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdateMipMap, *this, "TextureBufferTest_PartialUpdateMipMap"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdateMipMap_RG8, *this, "TextureBufferTest_PartialUpdateMipMap_RG8"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchSceneTextureToClientTexture, *this, "TextureBufferTest_SwitchSceneTextureToClientTexture"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchClientTextureToSceneTexture, *this, "TextureBufferTest_SwitchClientTextureToSceneTexture"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchClientTextureToSceneTextureAndBack, *this, "TextureBufferTest_SwitchClientTextureToSceneTextureAndBack"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate, *this, "TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate"); - testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_ReMapScene, *this, "TextureBufferTest_ReMapScene"); - - testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_ClientTexture, *this, "SamplerTest_ChangeData_ClientTexture"); - testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_TextureBufferToClientTexture, *this, "SamplerTest_ChangeData_TextureBufferToClientTexture"); - testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_ClientTextureToTextureBuffer, *this, "SamplerTest_ChangeData_ClientTextureToTextureBuffer"); - testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_ClientTextureToRenderBuffer, *this, "SamplerTest_ChangeData_ClientTextureToRenderBuffer"); -} - -bool TextureRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) -{ - switch (testCase.m_id) - { - case TextureTest_Texture2D_Format_R8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_R8, "Texture2DFormatScene_R8"); - case TextureTest_Texture2D_Format_RG8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RG8, "Texture2DFormatScene_RG8"); - case TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha: - return runBasicTest(testFramework, Texture2DFormatScene::EState_Swizzled_Luminance_Alpha, "Texture2DFormatScene_Swizzled_Luminance_Alpha"); - case TextureTest_Texture2D_Format_RGB8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB8, "Texture2DFormatScene_RGB8"); - case TextureTest_Texture2D_Format_RGB565: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB565, "Texture2DFormatScene_RGB565"); - case TextureTest_Texture2D_Format_RGBA8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA8, "Texture2DFormatScene_RGBA8"); - case TextureTest_Texture2D_Format_RGBA4: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA4, "Texture2DFormatScene_RGBA4"); - case TextureTest_Texture2D_Format_RGBA5551: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA5551, "Texture2DFormatScene_RGBA5551"); - - case TextureTest_Texture2D_Format_R16F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_R16F, "Texture2DFormatScene_FloatRed"); - case TextureTest_Texture2D_Format_R32F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_R32F, "Texture2DFormatScene_FloatRed"); - case TextureTest_Texture2D_Format_RG16F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RG16F, "Texture2DFormatScene_FloatRG"); - case TextureTest_Texture2D_Format_RG32F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RG32F, "Texture2DFormatScene_FloatRG"); - case TextureTest_Texture2D_Format_RGB16F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB16F, "Texture2DFormatScene_FloatRGB"); - case TextureTest_Texture2D_Format_RGB32F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB32F, "Texture2DFormatScene_FloatRGB"); - case TextureTest_Texture2D_Format_RGBA16F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA16F, "Texture2DFormatScene_FloatRGB"); - case TextureTest_Texture2D_Format_RGBA32F: - return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA32F, "Texture2DFormatScene_FloatRGB"); - case TextureTest_Texture2D_Format_SRGB8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_SRGB8, "Texture2DFormatScene_SRGB8"); - case TextureTest_Texture2D_Format_SRGB8_ALPHA8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_SRGB8_ALPHA8, "Texture2DFormatScene_SRGB8_ALPHA8"); - case TextureTest_Texture2D_Format_ASTC_RGBA_4x4: - return runBasicTest(testFramework, Texture2DFormatScene::EState_ASTC_RGBA_4x4, "Texture2DFormatScene_ASTC_RGBA_4x4"); - case TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4: - return runBasicTest(testFramework, Texture2DFormatScene::EState_ASTC_SRGB_ALPHA_4x4, "Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4"); - - case TextureTest_Texture2D_Format_Swizzled_BGR8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_Swizzled_BGR8, "Texture2DFormatScene_Swizzled_BGR8"); - case TextureTest_Texture2D_Format_Swizzled_BGRA8: - return runBasicTest(testFramework, Texture2DFormatScene::EState_Swizzled_BGRA8, "Texture2DFormatScene_Swizzled_BGRA8"); - case TextureTest_Texture2D_Format_ETC2RGB: - return runBasicTest(testFramework, Texture2DFormatScene::EState_ETC2RGB, "Texture2DFormatScene_ETC2RGB"); - case TextureTest_Texture2D_Format_ETC2RGBA: - return runBasicTest(testFramework, Texture2DFormatScene::EState_ETC2RGBA, "Texture2DFormatScene_ETC2RGBA"); - case TextureTest_Texture2D_Sampling_Nearest: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_Nearest, "Texture2DSamplingScene_Nearest"); - case TextureTest_Texture2D_Sampling_NearestWithMipMaps: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_NearestWithMipMaps, "Texture2DSamplingScene_NearestWithMipMaps"); - case TextureTest_Texture2D_Sampling_Bilinear: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_Bilinear, "Texture2DSamplingScene_Bilinear", 1.0f); - case TextureTest_Texture2D_Sampling_BilinearWithMipMaps: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_BilinearWithMipMaps, "Texture2DSamplingScene_BilinearWithMipMaps"); - case TextureTest_Texture2D_Sampling_Trilinear: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_Trilinear, "Texture2DSamplingScene_Trilinear", 6.0f); - case TextureTest_Texture2D_Sampling_MinLinearMagNearest: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_MinLinearMagNearest, "Texture2DSamplingScene_MinLinearMagNearest", 1.0f); - case TextureTest_Texture2D_Sampling_MinNearestMagLinear: - return runBasicTest(testFramework, Texture2DSamplingScene::EState_MinNearestMagLinear, "Texture2DSamplingScene_MinNearestMagLinear"); - - case TextureTest_Texture2D_AddressMode: - return runBasicTest(testFramework, TextureAddressScene::ADDRESS_MODE_STATE, "TextureAddressScene_AllAddressModes", 0.4f); - case TextureTest_Texture2D_AnisotropicFilter: - return runBasicTest(testFramework, Texture2DAnisotropicTextureFilteringScene::EState_Anisotropic, "Texture2DAnisotropicTextureFilteringScene_Anisotropic"); - case TextureTest_Texture2D_GenerateMipMapSingle: - return runBasicTest(testFramework, Texture2DGenerateMipMapScene::EState_GenerateMipMapSingle, "Texture2DGenerateMipMapScene_GenerateSingleMipMap", 1.0f); - case TextureTest_Texture2D_GenerateMipMapMultiple: - return runBasicTest(testFramework, Texture2DGenerateMipMapScene::EState_GenerateMipMapMultiple, "Texture2DGenerateMipMapScene_GenerateMultipleMipMaps", 1.0f); - case TextureTest_Texture2D_CompressedMipMap: - return runBasicTest(testFramework, Texture2DCompressedMipMapScene::EState_CompressedMipMap, "Texture2DCompressedMipMapScene_CompressedMipMap"); - case TextureTest_Texture3D_RGBA8: - return runBasicTest(testFramework, Texture3DScene::SLICES_4, "Texture3DScene_4Slices"); - case TextureTest_CubeMap_RGBA8: - return runBasicTest(testFramework, CubeTextureScene::EState_RGBA8, "CubeTextureScene_CubeMap"); - case TextureTest_CubeMap_BGRA_Swizzled: - return runBasicTest(testFramework, CubeTextureScene::EState_BGRA_Swizzled, "CubeTextureScene_CubeMapSwizzled"); - case TextureTest_CubeMap_Float: - return runBasicTest(testFramework, CubeTextureScene::EState_Float, "CubeTextureScene_CubeMapFloat", 0.28f); - case TextureTest_TextureCube_AnisotropicFilter: - return runBasicTest(testFramework, TextureCubeAnisotropicTextureFilteringScene::EState_Anisotropic, "TextureCubeAnisotropicTextureFilteringScene_Anisotropic"); - - case TextureBufferTest_RGBA8_OneMip: - return runBasicTest(testFramework, TextureBufferScene::EState_RGBA8_OneMip, "TextureBuffer_RGBA8_OneMip"); - case TextureBufferTest_RGBA8_ThreeMips: - return runBasicTest(testFramework, TextureBufferScene::EState_RGBA8_ThreeMips, "TextureBuffer_RGBA8_ThreeMips"); - case TextureBufferTest_PartialUpdate: - return runBasicTest(testFramework, TextureBufferScene::EState_PartialUpdate, "TextureBuffer_PartialUpdate"); - case TextureBufferTest_PartialUpdateMipMap: - return runBasicTest(testFramework, TextureBufferScene::EState_PartialUpdateMipMap, "TextureBuffer_PartialUpdateMipMap"); - case TextureBufferTest_PartialUpdateMipMap_RG8: - return runBasicTest(testFramework, TextureBufferScene::EState_PartialUpdateMipMap_RG8, "TextureBuffer_PartialUpdateMipMap_RG8"); - case TextureBufferTest_SwitchSceneTextureToClientTexture: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_RGBA8_OneMip); - const bool sceneTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); - testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_ClientTextureResource_RGBA8); - const bool switchToClientTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); - return sceneTextureCorrect && switchToClientTexture; - } - case TextureBufferTest_SwitchClientTextureToSceneTexture: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_ClientTextureResource_RGBA8); - const bool clientTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); - testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_RGBA8_OneMip); - const bool switchToSceneTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); - return clientTextureCorrect && switchToSceneTextureCorrect; - } - case TextureBufferTest_SwitchClientTextureToSceneTextureAndBack: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_ClientTextureResource_RGBA8); - const bool clientTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); - testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_RGBA8_OneMip); - const bool switchToSceneTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); - testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_SwitchBackToClientTexture); - const bool switchBackToClientTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); - return clientTextureCorrect && switchToSceneTexture && switchBackToClientTexture; - } - case TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_RGBA8_OneMip); - const bool textureBufferCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); - testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_ClientTextureResource_RGBA8); - const bool switchToClientTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); - testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_SwitchBackToExistingTextureBufferAndUpdate); - const bool switchBackToTextureBuffer = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipRed", 0u); - return textureBufferCorrect && switchToClientTexture && switchBackToTextureBuffer; - } - case TextureBufferTest_ReMapScene: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_RGBA8_OneMip); - const bool beforeRemapping = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); - // just for confidence (reuse existing black image) - const bool blackAfterUnmap = testFramework.renderAndCompareScreenshot("DistributedScene_UnpublishedScene", 0u); - - testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); - const bool afterRemapping = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); - - return beforeRemapping && blackAfterUnmap && afterRemapping; - } - case SamplerTest_ChangeData_ClientTexture: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_ClientTexture); - if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) - return false; - testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetClientTexture); - return testFramework.renderAndCompareScreenshot("TextureSamplerScene_Changed"); - } - case SamplerTest_ChangeData_TextureBufferToClientTexture: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_TextureBuffer); - if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) - return false; - testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetClientTexture); - return testFramework.renderAndCompareScreenshot("TextureSamplerScene_Changed"); - } - case SamplerTest_ChangeData_ClientTextureToTextureBuffer: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_ClientTexture); - if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) - return false; - testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetTextureBuffer); - return testFramework.renderAndCompareScreenshot("TextureSamplerScene_Changed"); - } - case SamplerTest_ChangeData_ClientTextureToRenderBuffer: - { - const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_ClientTexture); - if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) - return false; - testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetRenderBuffer); - return testFramework.renderAndCompareScreenshot("TextureSamplerScene_ChangedBlue"); - } - default: - assert(!"Invalid texture rendering test ID!"); - return false; - } -} - -template -bool TextureRenderingTests::runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel) -{ - createAndShowScene(testFramework, sceneState); - return testFramework.renderAndCompareScreenshot(expectedImageName, 0u, maxAveragePercentErrorPerPixel); -} - -template -ramses::sceneId_t TextureRenderingTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState) -{ - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState); - testFramework.publishAndFlushScene(sceneId); - testFramework.getSceneToRendered(sceneId); - - return sceneId; -} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.h b/integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.h deleted file mode 100644 index aa79515ca..000000000 --- a/integration/SandwichTests/RendererTests/RenderingTests/TextureRenderingTests.h +++ /dev/null @@ -1,94 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_TEXTURERENDERINGTESTS_H -#define RAMSES_TEXTURERENDERINGTESTS_H - -#include "IRendererTest.h" - -#include - -class TextureRenderingTests : public IRendererTest -{ -public: - void setUpTestCases(RendererTestsFramework& testFramework) final; - bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; - -private: - template - ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState); - - template - bool runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel); - - enum - { - TextureTest_Texture2D_Format_R8 = 0, - TextureTest_Texture2D_Format_RG8, - TextureTest_Texture2D_Format_RGB8, - TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha, - TextureTest_Texture2D_Format_RGB565, - TextureTest_Texture2D_Format_RGBA8, - TextureTest_Texture2D_Format_RGBA4, - TextureTest_Texture2D_Format_RGBA5551, - TextureTest_Texture2D_Format_Swizzled_BGR8, - TextureTest_Texture2D_Format_Swizzled_BGRA8, - TextureTest_Texture2D_Format_ETC2RGB, - TextureTest_Texture2D_Format_ETC2RGBA, - TextureTest_Texture2D_Format_R16F, - TextureTest_Texture2D_Format_R32F, - TextureTest_Texture2D_Format_RG16F, - TextureTest_Texture2D_Format_RG32F, - TextureTest_Texture2D_Format_RGB16F, - TextureTest_Texture2D_Format_RGB32F, - TextureTest_Texture2D_Format_RGBA16F, - TextureTest_Texture2D_Format_RGBA32F, - TextureTest_Texture2D_Format_SRGB8, - TextureTest_Texture2D_Format_SRGB8_ALPHA8, - TextureTest_Texture2D_Format_ASTC_RGBA_4x4, - TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4, - - TextureTest_Texture2D_Sampling_Nearest, - TextureTest_Texture2D_Sampling_NearestWithMipMaps, - TextureTest_Texture2D_Sampling_Bilinear, - TextureTest_Texture2D_Sampling_BilinearWithMipMaps, - TextureTest_Texture2D_Sampling_Trilinear, - TextureTest_Texture2D_Sampling_MinLinearMagNearest, - TextureTest_Texture2D_Sampling_MinNearestMagLinear, - - TextureTest_Texture2D_AddressMode, - TextureTest_Texture2D_AnisotropicFilter, - TextureTest_Texture2D_GenerateMipMapSingle, - TextureTest_Texture2D_GenerateMipMapMultiple, - TextureTest_Texture2D_CompressedMipMap, - - TextureTest_Texture3D_RGBA8, - TextureTest_CubeMap_RGBA8, - TextureTest_CubeMap_BGRA_Swizzled, - TextureTest_CubeMap_Float, - TextureTest_TextureCube_AnisotropicFilter, - - TextureBufferTest_RGBA8_OneMip, - TextureBufferTest_RGBA8_ThreeMips, - TextureBufferTest_PartialUpdate, - TextureBufferTest_PartialUpdateMipMap, - TextureBufferTest_PartialUpdateMipMap_RG8, - TextureBufferTest_SwitchSceneTextureToClientTexture, - TextureBufferTest_SwitchClientTextureToSceneTexture, - TextureBufferTest_SwitchClientTextureToSceneTextureAndBack, - TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate, - TextureBufferTest_ReMapScene, - - SamplerTest_ChangeData_ClientTexture, - SamplerTest_ChangeData_TextureBufferToClientTexture, - SamplerTest_ChangeData_ClientTextureToTextureBuffer, - SamplerTest_ChangeData_ClientTextureToRenderBuffer, - }; -}; - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/StressTestRenderer.h b/integration/StressTests/ResourceStressTests/src/StressTestRenderer.h deleted file mode 100644 index 3c055fb24..000000000 --- a/integration/StressTests/ResourceStressTests/src/StressTestRenderer.h +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_STRESSTESTRENDERER_H -#define RAMSES_STRESSTESTRENDERER_H - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "RendererAndSceneTestEventHandler.h" - -namespace ramses -{ - class RamsesRenderer; - class RamsesFramework; -} - -namespace ramses_internal -{ - class StressTestRenderer - { - public: - StressTestRenderer(ramses::RamsesFramework& framework, const ramses::RendererConfig& config); - ~StressTestRenderer(); - - ramses::displayId_t createDisplay(uint32_t offsetX, uint32_t width, uint32_t height, uint32_t displayIndex, const ramses::DisplayConfig& config); - ramses::displayBufferId_t createOffscreenBuffer(ramses::displayId_t displayId, uint32_t width, uint32_t height, bool interruptable); - void startLooping(); - void setFPS(ramses::displayId_t display, uint32_t fpsAsInteger); - void setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); - void setSkippingOfUnmodifiedBuffers(bool enabled); - - void setSceneDisplayAndBuffer(ramses::sceneId_t sceneId, ramses::displayId_t display, ramses::displayBufferId_t displayBuffer = {}); - void setSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); - void waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); - void linkOffscreenBufferToSceneTexture(ramses::sceneId_t sceneId, ramses::displayBufferId_t offscreenBuffer, ramses::dataConsumerId_t consumerTexture); - - void waitForFlush(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t flushVersion); - void consumePendingEvents(); - - private: - ramses::RamsesFramework& m_framework; - ramses::RamsesRenderer& m_renderer; - ramses::RendererSceneControl& m_sceneControlAPI; - ramses::RendererAndSceneTestEventHandler m_eventHandler; - }; -} - -#endif diff --git a/integration/TestContent/src/RenderBufferScene.cpp b/integration/TestContent/src/RenderBufferScene.cpp deleted file mode 100644 index 26f07b69b..000000000 --- a/integration/TestContent/src/RenderBufferScene.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "TestScenes/RenderBufferScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include - -namespace ramses_internal -{ - RenderBufferScene::RenderBufferScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) - : CommonRenderBufferTestScene(scene, cameraPosition) - , m_readWriteColorRenderBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) - , m_writeOnlyDepthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Depth, ramses::ERenderBufferFormat::Depth24, ramses::ERenderBufferAccessMode::WriteOnly)) - , m_writeOnlyDepthStencilBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::DepthStencil, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::WriteOnly)) - { - initClearPass(state); - initRenderingPass(state); - addRenderPassUsingRenderBufferAsQuadTexture(createQuadWithTexture(m_readWriteColorRenderBuffer)); - } - - ramses::RenderTarget& RenderBufferScene::createRenderTarget(uint32_t state) - { - ramses::RenderTargetDescription rtDesc; - - switch (state) - { - case ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL: - rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); - break; - case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER: - rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); - rtDesc.addRenderBuffer(m_writeOnlyDepthBuffer); - break; - case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER: - rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); - rtDesc.addRenderBuffer(m_writeOnlyDepthStencilBuffer); - break; - default: - assert(false); - break; - } - - return *m_scene.createRenderTarget(rtDesc); - } - - void RenderBufferScene::initClearPass(uint32_t state) - { - ramses::RenderPass* renderPass = m_scene.createRenderPass(); - renderPass->setRenderOrder(-100); - renderPass->setCamera(createCamera()); - - ramses::RenderTarget& renderTarget = createRenderTarget(state); - - renderPass->setRenderTarget(&renderTarget); - renderPass->setClearColor({1.f, 0.f, 1.f, 0.5f}); - renderPass->setClearFlags(ramses::EClearFlags_All); - } - - void RenderBufferScene::initRenderingPass(uint32_t state) - { - ramses::MeshNode& meshNode = createMesh(getEffectRenderOneBuffer()); - - //fill stencil buffer with value of 1 for every fragment that gets rendered into - meshNode.getAppearance()->setStencilFunction(ramses::EStencilFunc::Always, 1, 0xff); - meshNode.getAppearance()->setStencilOperation(ramses::EStencilOperation::Replace, ramses::EStencilOperation::Replace, ramses::EStencilOperation::Replace); - - ramses::Node& transNode = *m_scene.createNode(); - transNode.addChild(meshNode); - transNode.translate({0.0f, -0.5f, -5.0f}); - - ramses::RenderGroup& renderGroup = *m_scene.createRenderGroup(); - renderGroup.addMeshNode(meshNode); - - ramses::RenderPass& renderPass = *m_scene.createRenderPass(); - renderPass.setRenderOrder(0); - renderPass.addRenderGroup(renderGroup); - - ramses::PerspectiveCamera& camera = createCamera(); - renderPass.setCamera(camera); - - ramses::RenderTarget& renderTarget = createRenderTarget(state); - renderPass.setRenderTarget(&renderTarget); - renderPass.setClearFlags(ramses::EClearFlags_None); - - - ramses::Node& farTriangleTransNode = *m_scene.createNode(); - - if (state == ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER) - { - farTriangleTransNode.translate({0.5f, 0.0f, 0.1f}); - } - else - { - farTriangleTransNode.translate({0.5f, 0.0f, -0.1f}); - } - - ramses::MeshNode& meshNode2 = createMesh(getEffectRenderOneBuffer(), ramses::TriangleAppearance::EColor_Blue); - farTriangleTransNode.addChild(meshNode2); - transNode.addChild(farTriangleTransNode); - - meshNode2.getAppearance()->setDepthFunction(ramses::EDepthFunc::LessEqual); - meshNode2.getAppearance()->setStencilFunction(ramses::EStencilFunc::NotEqual, 0u, 0xff); - renderGroup.addMeshNode(meshNode2); - - renderGroup.addMeshNode(meshNode2); - } -} diff --git a/integration/TestContent/src/StreamTextureScene.cpp b/integration/TestContent/src/StreamTextureScene.cpp deleted file mode 100644 index 380c5c9bc..000000000 --- a/integration/TestContent/src/StreamTextureScene.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "TestScenes/StreamTextureScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" -#include - -namespace ramses_internal -{ - StreamTextureScene::StreamTextureScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) - : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) - , m_effect(nullptr) - { - m_effect = getTestEffect("ramses-test-client-textured"); - m_root = m_scene.createNode(); - - const ramses::vec3f A{ -0.5f, 0.5f, -0.5f }; - const ramses::vec3f B{ -0.5f, -0.5f, -0.5f }; - const ramses::vec3f C{ 0.5f, -0.5f, -0.5f }; - const ramses::vec3f D{ 0.5f, 0.5f, -0.5f }; - const ramses::vec3f E{ -0.5f, 0.5f, 0.5f }; - const ramses::vec3f F{ -0.5f, -0.5f, 0.5f }; - const ramses::vec3f G{ 0.5f, -0.5f, 0.5f }; - const ramses::vec3f H{ 0.5f, 0.5f, 0.5f }; - - using QuadVerts = std::array; - const QuadVerts verts1{{A, B, C, D}}; - const QuadVerts verts2{{C, G, H, D}}; - const QuadVerts verts3{{H, E, A, D}}; - const QuadVerts verts4{{F, G, C, B}}; - const QuadVerts verts5{{F, B, A, E}}; - const QuadVerts verts6{{F, E, H, G}}; - - switch (state) - { - case MULTI_SOURCE_SAME_FALLBACK_TEXTURE: - { - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts1, TextureConsumers[0]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[1]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts3, TextureConsumers[2]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts4, TextureConsumers[3]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts5, TextureConsumers[4]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts6, TextureConsumers[5]); - break; - } - case SAME_SOURCE_MULTI_FALLBACK: - { - addPngQuad("res/ramses-test-client-embedded-compositing-1.png", verts1, TextureConsumers[0]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[1]); - addPngQuad("res/ramses-test-client-embedded-compositing-3.png", verts3, TextureConsumers[2]); - addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[3]); - addPngQuad("res/ramses-test-client-embedded-compositing-5.png", verts5, TextureConsumers[4]); - addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[5]); - break; - } - case MULTI_SOURCE_MULTI_FALLBACK: - { - //Create two separate half cubes which are visible from the camera position - //Each half cube is formed from 3 surfaces that uses vertex arrays 2, 4 and 6 - ramses::Node* translateLeftNode = m_scene.createNode("translate left node"); - translateLeftNode->translate({-1.0f, 0.0f, 0.0f}); - m_root->addChild(*translateLeftNode); - - ramses::Node* translateRightNode = m_scene.createNode("translate right node"); - translateRightNode->translate({1.0f, 0.0f, 0.0f}); - m_root->addChild(*translateRightNode); - - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[0], translateLeftNode); - addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[1], translateLeftNode); - addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[2], translateLeftNode); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[3], translateRightNode); - addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[4], translateRightNode); - addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[5], translateRightNode); - break; - - } - case INITIAL_STATE: - { - addPngQuad("res/ramses-test-client-embedded-compositing-1.png", verts1, TextureConsumers[0]); - addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[1]); - addPngQuad("res/ramses-test-client-embedded-compositing-3.png", verts3, TextureConsumers[2]); - addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[3]); - addPngQuad("res/ramses-test-client-embedded-compositing-5.png", verts5, TextureConsumers[4]); - addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[5]); - break; - } - } - - m_root->setRotation({-35.264385f, -45.000427f, 0.000427f}, ramses::ERotationType::Euler_XYZ); // rotate the cube onto one corner - } - - void StreamTextureScene::addPngQuad(const char* pngFilePath, const std::array& vertexPositionsArray, ramses::dataConsumerId_t consumerId, ramses::Node* parentNode) - { - const ramses::Texture2D* texture = ramses::RamsesUtils::CreateTextureResourceFromPng(pngFilePath, m_scene); - const ramses::TextureSampler* sampler = m_scene.createTextureSampler( - ramses::ETextureAddressMode::Repeat, - ramses::ETextureAddressMode::Repeat, - ramses::ETextureSamplingMethod::Nearest, - ramses::ETextureSamplingMethod::Nearest, - *texture); - - m_scene.createTextureConsumer(*sampler, consumerId); - - ramses::Appearance* appearance = m_scene.createAppearance(*m_effect, "triangle appearance"); - - const ramses::ArrayResource* vertexPositions = m_scene.createArrayResource(4u, vertexPositionsArray.data()); - - const std::array textureCoordsArray - { - ramses::vec2f{ 0.f, 1.f }, //A A-----D - ramses::vec2f{ 0.f, 0.f }, //B | | - ramses::vec2f{ 1.f, 0.f }, //C | | - ramses::vec2f{ 1.f, 1.f } //D B-----C - }; - const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(4u, textureCoordsArray.data()); - - const uint16_t indicesArray[] = - { - 0, 2, 1, //ACB - 0, 3, 2 //ADC - }; - const ramses::ArrayResource* indices = m_scene.createArrayResource(6u, indicesArray); - - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - m_effect->findAttributeInput("a_position", positionsInput); - m_effect->findAttributeInput("a_texcoord", texCoordsInput); - - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*m_effect, "triangle geometry"); - geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); - - ramses::UniformInput textureInput; - m_effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); - - // create a mesh node to define the triangle with chosen appearance - ramses::MeshNode* meshNode = m_scene.createMeshNode("textured triangle mesh node"); - addMeshNodeToDefaultRenderGroup(*meshNode); - - if (parentNode == nullptr) - parentNode = m_root; - parentNode->addChild(*meshNode); - - meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); - } -} diff --git a/integration/TestContent/src/TriangleAppearance.cpp b/integration/TestContent/src/TriangleAppearance.cpp deleted file mode 100644 index 6bd6c1f20..000000000 --- a/integration/TestContent/src/TriangleAppearance.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include - -#include "PlatformAbstraction/PlatformTypes.h" - -#include "TestScenes/TriangleAppearance.h" - -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/DataObject.h" - -namespace ramses -{ - TriangleAppearance::TriangleAppearance(Scene& scene, const Effect& effect, enum TriangleAppearance::EColor color, float alpha) - : m_appearance(createAppearance(effect, scene)) - { - if (StatusOK == effect.findUniformInput("color", m_colorInput)) - { - setColor(color, alpha); - } - } - - Appearance& TriangleAppearance::createAppearance(const Effect& effect, Scene& scene) - { - return *scene.createAppearance(effect, "appearance"); - } - - void TriangleAppearance::setColor(enum EColor color, float alpha) - { - status_t status = StatusOK; - switch (color) - { - case EColor_Red: - status = m_appearance.setInputValue(m_colorInput, vec4f{ 1.f, 0.f, 0.f, alpha }); - break; - case EColor_Blue: - status = m_appearance.setInputValue(m_colorInput, vec4f{ 0.f, 0.f, 1.f, alpha }); - break; - case EColor_Green: - status = m_appearance.setInputValue(m_colorInput, vec4f{ 0.f, 1.f, 0.f, alpha }); - break; - case EColor_White: - status = m_appearance.setInputValue(m_colorInput, vec4f{ 1.f, 1.f, 1.f, alpha }); - break; - case EColor_Grey: - status = m_appearance.setInputValue(m_colorInput, vec4f{ 0.5f, 0.5f, 0.5f, alpha }); - break; - default: - assert(false && "Chosen color for triangle is not available!"); - break; - } - - assert(status == StatusOK); - UNUSED(status); - } - - void TriangleAppearance::bindColor(const DataObject& colorDataObject) - { - const status_t status = m_appearance.bindInput(m_colorInput, colorDataObject); - assert(status == StatusOK); - UNUSED(status); - } - - void TriangleAppearance::unbindColor() - { - const status_t status = m_appearance.unbindInput(m_colorInput); - assert(status == StatusOK); - UNUSED(status); - } -} diff --git a/ramses-cli/test/ramses-cli-test.cpp b/ramses-cli/test/ramses-cli-test.cpp deleted file mode 100644 index 2cfcf1b79..000000000 --- a/ramses-cli/test/ramses-cli-test.cpp +++ /dev/null @@ -1,348 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2023 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- -#include "gmock/gmock.h" -#include "ramses-cli.h" -#include "RamsesFrameworkConfigImpl.h" -#include "DisplayConfigImpl.h" -#include "RendererConfigImpl.h" - -using namespace ramses; -using namespace ramses_internal; - -class ARamsesFrameworkConfig : public testing::Test -{ -public: - ARamsesFrameworkConfig() - { - ramses::registerOptions(cli, config); - } - -protected: - CLI::App cli; - RamsesFrameworkConfig config{EFeatureLevel_Latest}; -}; - -class ARendererConfig : public testing::Test -{ -public: - ARendererConfig() - { - ramses::registerOptions(cli, config); - } - -protected: - CLI::App cli; - ramses::RendererConfig config; -}; - -class ADisplayConfig : public testing::Test -{ -public: - ADisplayConfig() - { - ramses::registerOptions(cli, config); - } - -protected: - CLI::App cli; - ramses::DisplayConfig config; -}; - - -TEST_F(ARamsesFrameworkConfig, cliConnection) -{ - EXPECT_EQ(EConnectionProtocol::TCP, config.m_impl.get().getUsedProtocol()); - EXPECT_THROW(cli.parse(std::vector{"--connection"}), CLI::ParseError); - cli.parse(std::vector{"--connection=off"}); - EXPECT_EQ(EConnectionProtocol::Off, config.m_impl.get().getUsedProtocol()); - cli.parse(std::vector{"--connection=tcp"}); - EXPECT_EQ(EConnectionProtocol::TCP, config.m_impl.get().getUsedProtocol()); -} - -TEST_F(ARamsesFrameworkConfig, cliRamsh) -{ - EXPECT_EQ(ERamsesShellType::Default, config.m_impl.get().m_shellType); - cli.parse(std::vector{"--ramsh"}); - EXPECT_EQ(ERamsesShellType::Console, config.m_impl.get().m_shellType); - cli.parse(std::vector{"--no-ramsh"}); - EXPECT_EQ(ERamsesShellType::None, config.m_impl.get().m_shellType); -} - -TEST_F(ARamsesFrameworkConfig, cliGuid) -{ - EXPECT_EQ(Guid(), config.m_impl.get().getUserProvidedGuid()); - EXPECT_THROW(cli.parse(std::vector{"--guid"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--guid=foo"}), CLI::ParseError); - cli.parse(std::vector{"--guid=266"}); - EXPECT_EQ(Guid(266), config.m_impl.get().getUserProvidedGuid()); -} - -TEST_F(ARamsesFrameworkConfig, cliIp) -{ - EXPECT_EQ("127.0.0.1", config.m_impl.get().m_tcpConfig.getIPAddress()); - EXPECT_THROW(cli.parse(std::vector{"--ip"}), CLI::ParseError); - cli.parse(std::vector{"--ip=localhost"}); - EXPECT_EQ("localhost", config.m_impl.get().m_tcpConfig.getIPAddress()); -} - -TEST_F(ARamsesFrameworkConfig, cliPort) -{ - EXPECT_EQ(0u, config.m_impl.get().m_tcpConfig.getPort()); - EXPECT_THROW(cli.parse(std::vector{"--port"}), CLI::ParseError); - cli.parse(std::vector{"--port=8937"}); - EXPECT_EQ(8937u, config.m_impl.get().m_tcpConfig.getPort()); -} - -TEST_F(ARamsesFrameworkConfig, cliDaemonIp) -{ - EXPECT_EQ("127.0.0.1", config.m_impl.get().m_tcpConfig.getDaemonIPAddress()); - EXPECT_THROW(cli.parse(std::vector{"--daemon-ip"}), CLI::ParseError); - cli.parse(std::vector{"--daemon-ip=localhost"}); - EXPECT_EQ("localhost", config.m_impl.get().m_tcpConfig.getDaemonIPAddress()); -} - -TEST_F(ARamsesFrameworkConfig, cliDaemonPort) -{ - EXPECT_EQ(5999u, config.m_impl.get().m_tcpConfig.getDaemonPort()); - EXPECT_THROW(cli.parse(std::vector{"--daemon-port"}), CLI::ParseError); - cli.parse(std::vector{"--daemon-port=8937"}); - EXPECT_EQ(8937u, config.m_impl.get().m_tcpConfig.getDaemonPort()); -} - -TEST_F(ARamsesFrameworkConfig, cliTcpAlive) -{ - EXPECT_EQ(std::chrono::milliseconds(300u), config.m_impl.get().m_tcpConfig.getAliveInterval()); - EXPECT_THROW(cli.parse(std::vector{"--tcp-alive"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--tcp-alive=500"}), CLI::ParseError); - cli.parse("--tcp-alive=500 5000"); - EXPECT_EQ(std::chrono::milliseconds(500u), config.m_impl.get().m_tcpConfig.getAliveInterval()); - EXPECT_EQ(std::chrono::milliseconds(5000u), config.m_impl.get().m_tcpConfig.getAliveTimeout()); -} - -TEST_F(ARamsesFrameworkConfig, cliPeriodicLogTimeout) -{ - EXPECT_EQ(2u, config.m_impl.get().periodicLogTimeout); - EXPECT_THROW(cli.parse(std::vector{"--logp"}), CLI::ParseError); - cli.parse(std::vector{"--logp=27"}); - EXPECT_EQ(27u, config.m_impl.get().periodicLogTimeout); - EXPECT_TRUE(config.m_impl.get().m_periodicLogsEnabled); - cli.parse(std::vector{"--logp=0"}); - EXPECT_EQ(0u, config.m_impl.get().periodicLogTimeout); - EXPECT_FALSE(config.m_impl.get().m_periodicLogsEnabled); -} - -TEST_F(ARamsesFrameworkConfig, cliLogLevel) -{ - EXPECT_THROW(cli.parse(std::vector{"--log-level"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--log-level=foo"}), CLI::ParseError); - cli.parse(std::vector{"--log-level=trace"}); - EXPECT_EQ(ramses_internal::ELogLevel::Trace, config.m_impl.get().loggerConfig.logLevel.value()); - cli.parse(std::vector{"--log-level=1"}); - EXPECT_EQ(ramses_internal::ELogLevel::Fatal, config.m_impl.get().loggerConfig.logLevel.value()); - cli.parse(std::vector{"-linfo"}); - EXPECT_EQ(ramses_internal::ELogLevel::Info, config.m_impl.get().loggerConfig.logLevel.value()); - cli.parse(std::vector{"-l0"}); - EXPECT_EQ(ramses_internal::ELogLevel::Off, config.m_impl.get().loggerConfig.logLevel.value()); -} - -TEST_F(ARamsesFrameworkConfig, cliLogLevelConsole) -{ - EXPECT_THROW(cli.parse(std::vector{"--log-level-console"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--log-level-console=foo"}), CLI::ParseError); - cli.parse(std::vector{"--log-level-console=trace"}); - EXPECT_EQ(ramses_internal::ELogLevel::Trace, config.m_impl.get().loggerConfig.logLevelConsole.value()); - cli.parse(std::vector{"--log-level-console=1"}); - EXPECT_EQ(ramses_internal::ELogLevel::Fatal, config.m_impl.get().loggerConfig.logLevelConsole.value()); -} - -TEST_F(ARamsesFrameworkConfig, cliLogContexts) -{ - EXPECT_THROW(cli.parse(std::vector{"--log-context"}), CLI::ParseError); - EXPECT_THAT([&]() { cli.parse(std::vector{"--log-context=badformat"}); }, - testing::ThrowsMessage(testing::HasSubstr("':' missing. Expected: CONTEXT:LOGLEVEL"))); - EXPECT_THROW(cli.parse(std::vector{"--log-context=ctx:42"}), CLI::ParseError); - cli.parse("--log-context=ctx1:trace"); - EXPECT_EQ(ramses_internal::ELogLevel::Trace, config.m_impl.get().loggerConfig.logLevelContexts["ctx1"]); - cli.parse("--log-context=ctx1:trace ctx1:debug ctx2:info"); - EXPECT_EQ(ramses_internal::ELogLevel::Debug, config.m_impl.get().loggerConfig.logLevelContexts["ctx1"]); - EXPECT_EQ(ramses_internal::ELogLevel::Info, config.m_impl.get().loggerConfig.logLevelContexts["ctx2"]); - cli.parse("--log-context=ctx1:0 ctx2:FaTAL"); - EXPECT_EQ(ramses_internal::ELogLevel::Off, config.m_impl.get().loggerConfig.logLevelContexts["ctx1"]); - EXPECT_EQ(ramses_internal::ELogLevel::Fatal, config.m_impl.get().loggerConfig.logLevelContexts["ctx2"]); - cli.parse("--log-context=ctx1:trace --log-context ctx2:info"); - EXPECT_EQ(ramses_internal::ELogLevel::Trace, config.m_impl.get().loggerConfig.logLevelContexts["ctx1"]); - EXPECT_EQ(ramses_internal::ELogLevel::Info, config.m_impl.get().loggerConfig.logLevelContexts["ctx2"]); -} - -TEST_F(ARamsesFrameworkConfig, cliDltAppId) -{ - EXPECT_EQ("RAMS", config.m_impl.get().getDLTApplicationID()); - EXPECT_THROW(cli.parse(std::vector{"--dlt-app-id"}), CLI::ParseError); - cli.parse(std::vector{"--dlt-app-id=RROO"}); - EXPECT_EQ("RROO", config.m_impl.get().getDLTApplicationID()); -} - -TEST_F(ARamsesFrameworkConfig, cliDltAppDescription) -{ - EXPECT_THROW(cli.parse(std::vector{"--dlt-app-description"}), CLI::ParseError); - cli.parse(std::vector{"--dlt-app-description=foo"}); - EXPECT_EQ("foo", config.m_impl.get().getDLTApplicationDescription()); -} - -TEST_F(ARamsesFrameworkConfig, cliParticipantName) -{ - EXPECT_EQ("", config.m_impl.get().getParticipantName()); - cli.parse("foo", true); - EXPECT_EQ("foo", config.m_impl.get().getParticipantName()); - cli.parse("path/to/bar", true); - EXPECT_EQ("bar", config.m_impl.get().getParticipantName()); - cli.parse(R"(c:\path\to\foobar.exe)", true); - EXPECT_EQ("foobar.exe", config.m_impl.get().getParticipantName()); -} - -TEST_F(ADisplayConfig, cliDeviceType) -{ - cli.parse(std::vector{"--device-type=gl42"}); - EXPECT_EQ(ramses::EDeviceType::GL_4_2, config.getDeviceType()); - - cli.parse(std::vector{"--device-type=GL45"}); - EXPECT_EQ(ramses::EDeviceType::GL_4_5, config.getDeviceType()); - - cli.parse(std::vector{"--device-type=GLES_3_0"}); - EXPECT_EQ(ramses::EDeviceType::GLES_3_0, config.getDeviceType()); -} - -TEST_F(ADisplayConfig, cliWindowType) -{ - cli.parse(std::vector{"--window-type=windows"}); - EXPECT_EQ(ramses::EWindowType::Windows, config.getWindowType()); - - cli.parse(std::vector{"--window-type=WAYLAND-IVI"}); - EXPECT_EQ(ramses::EWindowType::Wayland_IVI, config.getWindowType()); -} - -TEST_F(ADisplayConfig, cliWindowRectangle) -{ - cli.parse(std::vector{"--xpos=5", "--ypos=10", "--width=420", "--height=998"}); - int32_t x = 0u; - int32_t y = 0u; - uint32_t w = 0u; - uint32_t h = 0u; - config.m_impl.get().getWindowRectangle(x, y, w, h); - EXPECT_EQ(5, x); - EXPECT_EQ(10, y); - EXPECT_EQ(420u, w); - EXPECT_EQ(998u, h); -} - -TEST_F(ADisplayConfig, cliFullscreen) -{ - cli.parse(std::vector{"--fullscreen"}); - EXPECT_TRUE(config.isWindowFullscreen()); - config.setWindowFullscreen(false); - cli.parse(std::vector{"-f"}); - EXPECT_TRUE(config.isWindowFullscreen()); -} - -TEST_F(ADisplayConfig, cliBorderless) -{ - cli.parse(std::vector{"--borderless"}); - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().getBorderlessState()); -} - -TEST_F(ADisplayConfig, cliResizable) -{ - cli.parse(std::vector{"--resizable"}); - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().isResizable()); -} - -TEST_F(ADisplayConfig, cliDeleteEffects) -{ - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().getKeepEffectsUploaded()); - cli.parse(std::vector{"--delete-effects"}); - EXPECT_FALSE(config.m_impl.get().getInternalDisplayConfig().getKeepEffectsUploaded()); -} - -TEST_F(ADisplayConfig, cliMsaa) -{ - EXPECT_THROW(cli.parse(std::vector{"--msaa"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--msaa=7"}), CLI::ParseError); - EXPECT_EQ(1u, config.m_impl.get().getInternalDisplayConfig().getAntialiasingSampleCount()); - cli.parse(std::vector{"--msaa=2"}); - EXPECT_EQ(2u, config.m_impl.get().getInternalDisplayConfig().getAntialiasingSampleCount()); - cli.parse(std::vector{"--msaa=4"}); - EXPECT_EQ(4u, config.m_impl.get().getInternalDisplayConfig().getAntialiasingSampleCount()); - cli.parse(std::vector{"--msaa=8"}); - EXPECT_EQ(8u, config.m_impl.get().getInternalDisplayConfig().getAntialiasingSampleCount()); -} - -TEST_F(ADisplayConfig, cliIviVisible) -{ - EXPECT_FALSE(config.m_impl.get().getInternalDisplayConfig().getStartVisibleIvi()); - cli.parse(std::vector{"--ivi-visible"}); - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().getStartVisibleIvi()); -} - -TEST_F(ADisplayConfig, cliIviLayer) -{ - EXPECT_THROW(cli.parse(std::vector{"--ivi-layer"}), CLI::ParseError); - cli.parse(std::vector{"--ivi-layer=42"}); - EXPECT_EQ(42u, config.m_impl.get().getInternalDisplayConfig().getWaylandIviLayerID().getValue()); -} - -TEST_F(ADisplayConfig, cliIviSurface) -{ - EXPECT_THROW(cli.parse(std::vector{"--ivi-surface"}), CLI::ParseError); - cli.parse(std::vector{"--ivi-surface=78"}); - EXPECT_EQ(78u, config.m_impl.get().getInternalDisplayConfig().getWaylandIviSurfaceID().getValue()); -} - -TEST_F(ADisplayConfig, cliClear) -{ - EXPECT_THROW(cli.parse(std::vector{"--clear"}), CLI::ParseError); - cli.parse(std::vector{"--clear=1,0,0.4,0.7"}); - EXPECT_FLOAT_EQ(1.f, config.m_impl.get().getInternalDisplayConfig().getClearColor()[0]); - EXPECT_FLOAT_EQ(0.f, config.m_impl.get().getInternalDisplayConfig().getClearColor()[1]); - EXPECT_FLOAT_EQ(0.4f, config.m_impl.get().getInternalDisplayConfig().getClearColor()[2]); - EXPECT_FLOAT_EQ(0.7f, config.m_impl.get().getInternalDisplayConfig().getClearColor()[3]); -} - -TEST_F(ADisplayConfig, cliThrowsErrorsForExtras) -{ - EXPECT_THROW(cli.parse(std::vector{"--foo"}), CLI::ExtrasError); -} - -TEST_F(ADisplayConfig, cliEcDisplay) -{ - EXPECT_THROW(cli.parse(std::vector{"--ec-display"}), CLI::ParseError); - cli.parse(std::vector{"--ec-display=wse"}); - EXPECT_EQ("wse", config.getWaylandEmbeddedCompositingSocketName()); -} - -TEST_F(ADisplayConfig, cliEcGroup) -{ - EXPECT_THROW(cli.parse(std::vector{"--ec-socket-group"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--ec-socket-group=grp"}), CLI::RequiresError); - cli.parse(std::vector{"--ec-socket-group=grp", "--ec-display=wse"}); - EXPECT_EQ("grp", config.m_impl.get().getWaylandSocketEmbeddedGroup()); -} - -TEST_F(ADisplayConfig, cliEcPermissions) -{ - EXPECT_THROW(cli.parse(std::vector{"--ec-socket-permissions"}), CLI::ParseError); - EXPECT_THROW(cli.parse(std::vector{"--ec-socket-permissions=0744"}), CLI::RequiresError); - cli.parse(std::vector{"--ec-socket-permissions=0744", "--ec-display=wse"}); - EXPECT_EQ(0744u, config.m_impl.get().getWaylandSocketEmbeddedPermissions()); -} - -TEST_F(ARendererConfig, cliIviControl) -{ - EXPECT_FALSE(config.m_impl.get().getInternalRendererConfig().getSystemCompositorControlEnabled()); - cli.parse(std::vector{"--ivi-control"}); - EXPECT_TRUE(config.m_impl.get().getInternalRendererConfig().getSystemCompositorControlEnabled()); -} diff --git a/ramses-shared-lib/CMakeLists.txt b/ramses-shared-lib/CMakeLists.txt deleted file mode 100644 index ef71d9cae..000000000 --- a/ramses-shared-lib/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -ADD_DEFINITIONS(-DRAMSES_LINK_SHARED_EXPORT) - -if (ramses-sdk_BUILD_FULL_SHARED_LIB) - createModuleWithRenderer( - NAME ramses-shared-lib - TYPE SHARED_LIBRARY - ENABLE_INSTALL ON - ${ramses-shared-lib-MIXIN} - ${ramses-shared-lib-renderer-MIXIN} - ) - - target_link_libraries(ramses-shared-lib PUBLIC glm::glm) -endif() - -if (ramses-sdk_BUILD_HEADLESS_SHARED_LIB) - - # build shared library without renderer - createModule( - NAME ramses-shared-lib-headless - TYPE SHARED_LIBRARY - ENABLE_INSTALL ON - - ${ramses-shared-lib-MIXIN} - ) - - target_link_libraries(ramses-shared-lib-headless PUBLIC glm::glm) -endif() diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt deleted file mode 100644 index 76fd6c496..000000000 --- a/renderer/CMakeLists.txt +++ /dev/null @@ -1,58 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2023 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -add_subdirectory(ramses-renderer-api) -add_subdirectory(RendererLib) - -add_subdirectory(Platform/Device_GL) - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS) - add_subdirectory(Platform/Window_Windows) - add_subdirectory(Platform/Context_WGL) - add_subdirectory(Platform/Platform_Windows_WGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL OR ramses-sdk_ENABLE_WINDOW_TYPE_X11 OR ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID) - add_subdirectory(Platform/Context_EGL) - add_subdirectory(Platform/Platform_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) - add_subdirectory(Platform/Platform_X11_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID) - add_subdirectory(Platform/Platform_Android_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) - add_subdirectory(Platform/WaylandEGLExtensionProcs) - add_subdirectory(Platform/WaylandUtilities) - add_subdirectory(Platform/Device_EGL_Extension) - add_subdirectory(Platform/Window_Wayland) - add_subdirectory(Platform/Window_Wayland_Test) - - add_subdirectory(Platform/EmbeddedCompositor_Wayland) - - add_subdirectory(Platform/Platform_Wayland_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) - add_subdirectory(Platform/Window_Wayland_Shell) - add_subdirectory(Platform/Platform_Wayland_Shell_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) - add_subdirectory(Platform/SystemCompositorController_Wayland_IVI) - add_subdirectory(Platform/Window_Wayland_IVI) - add_subdirectory(Platform/Platform_Wayland_IVI_EGL) -endif() - -add_subdirectory(PlatformFactory) -add_subdirectory(ramses-renderer-impl) -add_subdirectory(RendererTestUtils) diff --git a/renderer/Platform/Context_WGL/CMakeLists.txt b/renderer/Platform/Context_WGL/CMakeLists.txt deleted file mode 100644 index bab9b149f..000000000 --- a/renderer/Platform/Context_WGL/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Context_WGL - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Context_WGL/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - Window_Windows - OpenGL -) - -# This module is tested in the renderer sandwich tests (/integration/ramses-renderer-tests) diff --git a/renderer/Platform/Device_EGL_Extension/CMakeLists.txt b/renderer/Platform/Device_EGL_Extension/CMakeLists.txt deleted file mode 100644 index c3c45baf8..000000000 --- a/renderer/Platform/Device_EGL_Extension/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2021 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -find_package(OpenGL) - -createModule( - NAME Device_EGL_Extension - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Device_EGL_Extension/*.h - src/*.cpp - DEPENDENCIES Context_EGL - WaylandEGLExtensionProcs - libdrm - gbm - INTERFACE_DEFINES DEVICE_EGL_EXTENSION_SUPPORTED - # TODO this should not be needed - PUBLIC_DEFINES ${OpenGL_DEFINITIONS} -) diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/CMakeLists.txt b/renderer/Platform/EmbeddedCompositor_Wayland/CMakeLists.txt deleted file mode 100644 index 711a985a9..000000000 --- a/renderer/Platform/EmbeddedCompositor_Wayland/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME EmbeddedCompositor_Wayland - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/EmbeddedCompositor_Wayland/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - wayland-server - wayland-ivi-extension - wayland-zwp-linux-dmabuf-v1-extension - libdrm - WaylandUtilities - Context_EGL - WaylandEGLExtensionProcs -) - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME EmbeddedCompositor_Wayland_Test - TYPE BINARY - ENABLE_INSTALL ON - SRC_FILES ../EmbeddedCompositor_Wayland/test/*.cpp - ../EmbeddedCompositor_Wayland/test/*.h - DEPENDENCIES EmbeddedCompositor_Wayland - wayland-client - Device_GL - RendererTestCommon - WaylandTestUtils - WaylandUtilities - ) - - makeTestFromTarget( - TARGET EmbeddedCompositor_Wayland_Test - SUFFIX RNDSANDWICHTEST_SWRAST - SKIPPABLE) -endif() diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBuffer.cpp b/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBuffer.cpp deleted file mode 100644 index 047165dac..000000000 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBuffer.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "EmbeddedCompositor_Wayland/WaylandBuffer.h" -#include "EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" -#include - -namespace ramses_internal -{ - WaylandBuffer::WaylandBuffer(WaylandBufferResource& bufferResource, IEmbeddedCompositor_Wayland& compositor) - : m_bufferResource(*bufferResource.clone()) - , m_refCount(0) - , m_compositor(compositor) - { - LOG_DEBUG(CONTEXT_RENDERER, "WaylandBuffer::WaylandBuffer"); - - m_destroyListener.notify = ClientBufferDestroyed; - m_bufferResource.addDestroyListener(&m_destroyListener); - } - - WaylandBuffer::~WaylandBuffer() - { - LOG_DEBUG(CONTEXT_RENDERER, "WaylandBuffer::~WaylandBuffer"); - delete &m_bufferResource; - } - - WaylandBufferResource& WaylandBuffer::getResource() const - { - return m_bufferResource; - } - - void WaylandBuffer::ClientBufferDestroyed(wl_listener* listener, void* data) - { - UNUSED(data); - LOG_DEBUG(CONTEXT_RENDERER, "WaylandBuffer::ClientBufferDestroyed data: " << data); - - WaylandBuffer* buffer(nullptr); - -WARNINGS_PUSH -WARNING_DISABLE_LINUX(-Winvalid-offsetof) -WARNING_DISABLE_LINUX(-Wcast-align) -WARNING_DISABLE_LINUX(-Wold-style-cast) - - buffer = wl_container_of(listener, buffer, m_destroyListener); - -WARNINGS_POP - - assert(buffer != nullptr); - - buffer->m_compositor.handleBufferDestroyed(*buffer); - delete buffer; - } - - /** Increase reference counter of buffer. */ - void WaylandBuffer::reference() - { - m_refCount++; - } - - /** Decrease reference counter of buffer. */ - void WaylandBuffer::release() - { - assert(m_refCount > 0); - - if (--m_refCount == 0) - { - m_bufferResource.bufferSendRelease(); - } - } - - bool WaylandBuffer::isSharedMemoryBuffer() const - { - return m_bufferResource.bufferGetSharedMemoryData() != nullptr; - } -} diff --git a/renderer/Platform/Platform_EGL/CMakeLists.txt b/renderer/Platform/Platform_EGL/CMakeLists.txt deleted file mode 100644 index 1df50eee1..000000000 --- a/renderer/Platform/Platform_EGL/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2020 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -SET(Platfom_EGL_Dependencies Context_EGL - Device_GL - ramses-renderer-lib) - -# It should be possible to have Platform_EGL without Device_EGL_Extension if gbm and libdrm -# are not supported on some platforms -if (TARGET Device_EGL_Extension) - SET(Platfom_EGL_Dependencies ${Platfom_EGL_Dependencies} Device_EGL_Extension) -endif() - -createModule( - NAME Platform_EGL - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Platform_EGL/*.h - src/*.cpp - DEPENDENCIES ${Platfom_EGL_Dependencies} -) diff --git a/renderer/Platform/Platform_Wayland_EGL/CMakeLists.txt b/renderer/Platform/Platform_Wayland_EGL/CMakeLists.txt deleted file mode 100644 index 3685656da..000000000 --- a/renderer/Platform/Platform_Wayland_EGL/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Platform_Wayland_EGL - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Platform_Wayland_EGL/*.h - src/*.cpp - DEPENDENCIES Window_Wayland - Platform_EGL - EmbeddedCompositor_Wayland -) - -# This module is tested in the renderer sandwich tests (/integration/ramses-renderer-tests) diff --git a/renderer/Platform/Platform_Wayland_IVI_EGL/CMakeLists.txt b/renderer/Platform/Platform_Wayland_IVI_EGL/CMakeLists.txt deleted file mode 100644 index 1b4bf88ce..000000000 --- a/renderer/Platform/Platform_Wayland_IVI_EGL/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Platform_Wayland_IVI_EGL - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Platform_Wayland_IVI_EGL/*.h - src/*.cpp - DEPENDENCIES Platform_Wayland_EGL - Window_Wayland_IVI - SystemCompositorController_Wayland_IVI -) diff --git a/renderer/Platform/Platform_Windows_WGL/CMakeLists.txt b/renderer/Platform/Platform_Windows_WGL/CMakeLists.txt deleted file mode 100644 index 0b7789caa..000000000 --- a/renderer/Platform/Platform_Windows_WGL/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Platform_Windows_WGL - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Platform_Windows_WGL/*.h - src/*.cpp - DEPENDENCIES Window_Windows - Context_WGL - Device_GL - ramses-renderer-lib -) - -# This module is tested in the renderer sandwich tests (/integration/ramses-renderer-tests) diff --git a/renderer/Platform/Platform_Windows_WGL/include/Platform_Windows_WGL/Platform_Windows_WGL.h b/renderer/Platform/Platform_Windows_WGL/include/Platform_Windows_WGL/Platform_Windows_WGL.h deleted file mode 100644 index 03059f77a..000000000 --- a/renderer/Platform/Platform_Windows_WGL/include/Platform_Windows_WGL/Platform_Windows_WGL.h +++ /dev/null @@ -1,34 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_PLATFORM_WINDOWS_WGL_H -#define RAMSES_PLATFORM_WINDOWS_WGL_H - -#include "Platform_Base/Platform_Base.h" -#include "RendererAPI/EDeviceType.h" -#include "Context_WGL/WglExtensions.h" - -namespace ramses_internal -{ - class Platform_Windows_WGL : public Platform_Base - { - public: - explicit Platform_Windows_WGL(const RendererConfig& rendererConfig); - - protected: - virtual bool createWindow(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler) override final; - virtual bool createContext(const DisplayConfig& displayConfig) override final; - virtual bool createContextUploading() override final; - virtual bool createDevice() override final; - virtual bool createDeviceUploading() override final; - - WglExtensions m_wglExtensions; - }; -} - -#endif diff --git a/renderer/Platform/Platform_X11_EGL/CMakeLists.txt b/renderer/Platform/Platform_X11_EGL/CMakeLists.txt deleted file mode 100644 index 0b78ade36..000000000 --- a/renderer/Platform/Platform_X11_EGL/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Platform_X11_EGL - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Platform_X11_EGL/*.h - src/*.cpp - DEPENDENCIES X11 - Platform_EGL -) - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME platform-x11-test - TYPE BINARY - SRC_FILES test/*.cpp - test/*.h - DEPENDENCIES Platform_X11_EGL - ramses-gmock-main - RendererTestCommon - ) - - makeTestFromTarget( - TARGET platform-x11-test - SUFFIX RNDSANDWICHTEST_SWRAST - SKIPPABLE) -endif() diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/CMakeLists.txt b/renderer/Platform/SystemCompositorController_Wayland_IVI/CMakeLists.txt deleted file mode 100644 index 180194e3c..000000000 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME SystemCompositorController_Wayland_IVI - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/SystemCompositorController_Wayland_IVI/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - WaylandUtilities - wayland-ivi-extension - wayland-client -) - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME SystemCompositorController_Wayland_IVI_Test - TYPE BINARY - SRC_FILES test/*.cpp - test/*.h - DEPENDENCIES SystemCompositorController_Wayland_IVI - WaylandTestUtils - RendererTestCommon - WaylandUtilities - ) - - makeTestFromTarget( - TARGET SystemCompositorController_Wayland_IVI_Test - SUFFIX RNDSANDWICHTEST_SWRAST - SKIPPABLE) -endif() diff --git a/renderer/Platform/WaylandEGLExtensionProcs/CMakeLists.txt b/renderer/Platform/WaylandEGLExtensionProcs/CMakeLists.txt deleted file mode 100644 index b6f2c269c..000000000 --- a/renderer/Platform/WaylandEGLExtensionProcs/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2021 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME WaylandEGLExtensionProcs - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/WaylandEGLExtensionProcs/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - wayland-zwp-linux-dmabuf-v1-extension - EGL -) - - -if (TARGET WaylandEGLExtensionProcs) - # check for eglmesaext header and ensure its included when availble - find_file(ramses-sdk_HAS_EGLMESAEXT "EGL/eglmesaext.h") - if (ramses-sdk_HAS_EGLMESAEXT) - target_compile_definitions(WaylandEGLExtensionProcs PUBLIC "RAMSES_HAS_EGLMESAEXT=1") - endif() -endif() diff --git a/renderer/Platform/WaylandUtilities/CMakeLists.txt b/renderer/Platform/WaylandUtilities/CMakeLists.txt deleted file mode 100644 index 4f336a564..000000000 --- a/renderer/Platform/WaylandUtilities/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME WaylandUtilities - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/WaylandUtilities/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - LinuxInput - wayland-server -) - - - -IF (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME WaylandTestUtils - TYPE STATIC_LIBRARY - INCLUDE_PATHS WaylandTestUtils - SRC_FILES WaylandTestUtils/*.h - WaylandTestUtils/*.cpp - DEPENDENCIES WaylandUtilities - ramses-gmock - ) -ENDIF() diff --git a/renderer/Platform/Window_Wayland/CMakeLists.txt b/renderer/Platform/Window_Wayland/CMakeLists.txt deleted file mode 100644 index 71fb0d6f3..000000000 --- a/renderer/Platform/Window_Wayland/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Window_Wayland - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Window_Wayland/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - WaylandUtilities - wayland-client - wayland-egl - LinuxInput -) - diff --git a/renderer/Platform/Window_Wayland_IVI/CMakeLists.txt b/renderer/Platform/Window_Wayland_IVI/CMakeLists.txt deleted file mode 100644 index e73caa23b..000000000 --- a/renderer/Platform/Window_Wayland_IVI/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Window_Wayland_IVI - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Window_Wayland_IVI/*.h - src/*.cpp - DEPENDENCIES Window_Wayland - wayland-ivi-extension -) - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME Window_Wayland_IVI_Test - TYPE BINARY - SRC_FILES test/*.cpp - test/*.h - DEPENDENCIES Window_Wayland_IVI - Window_Wayland_Test - SystemCompositorController_Wayland_IVI - ) - - # This module is tested in the renderer sandwich tests (/integration/ramses-renderer-tests) - - makeTestFromTarget( - TARGET Window_Wayland_IVI_Test - SUFFIX RNDSANDWICHTEST_SWRAST - SKIPPABLE - EXTRA_ARGS --gtest_filter=-*AWindowWaylandWithEventHandling*) -endif() diff --git a/renderer/Platform/Window_Wayland_Shell/CMakeLists.txt b/renderer/Platform/Window_Wayland_Shell/CMakeLists.txt deleted file mode 100644 index 39a61dc6a..000000000 --- a/renderer/Platform/Window_Wayland_Shell/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Window_Wayland_Shell - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Window_Wayland_Shell/*.h - src/*.cpp - DEPENDENCIES Window_Wayland -) - -if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME Window_Wayland_Shell_Test - TYPE BINARY - SRC_FILES test/*.cpp - test/*.h - DEPENDENCIES Window_Wayland_Shell - Window_Wayland_Test - ) - - makeTestFromTarget( - TARGET Window_Wayland_Shell_Test - SUFFIX RNDSANDWICHTEST_SWRAST - SKIPPABLE) -endif() diff --git a/renderer/Platform/Window_Wayland_Test/CMakeLists.txt b/renderer/Platform/Window_Wayland_Test/CMakeLists.txt deleted file mode 100644 index cb037d682..000000000 --- a/renderer/Platform/Window_Wayland_Test/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME Window_Wayland_Test - TYPE STATIC_LIBRARY - INCLUDE_PATHS include - SRC_FILES include/Window_Wayland_Test/*.h - DEPENDENCIES Window_Wayland - EGL - LinuxInput - RendererTestCommon - WaylandTestUtils -) diff --git a/renderer/Platform/Window_Windows/CMakeLists.txt b/renderer/Platform/Window_Windows/CMakeLists.txt deleted file mode 100644 index dabd1a3ec..000000000 --- a/renderer/Platform/Window_Windows/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- -IF (${CMAKE_SYSTEM_NAME} MATCHES "Windows") - createModule( - NAME Window_Windows - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Window_Windows/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib - ) - - if (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME Window_Windows_Test - TYPE BINARY - SRC_FILES test/*.cpp - test/*.h - DEPENDENCIES Window_Windows - RendererTestCommon - ramses-gmock-main - ) - - makeTestFromTarget( - TARGET Window_Windows_Test - SUFFIX RNDSANDWICHTEST - SKIPPABLE) - endif() -ENDIF() diff --git a/renderer/PlatformFactory/CMakeLists.txt b/renderer/PlatformFactory/CMakeLists.txt deleted file mode 100644 index ae02a2549..000000000 --- a/renderer/PlatformFactory/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2023 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME PlatformFactory - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/PlatformFactory/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-lib -) - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) - target_link_libraries(PlatformFactory PUBLIC Platform_Wayland_IVI_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) - target_link_libraries(PlatformFactory PUBLIC Platform_Wayland_Shell_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) - target_link_libraries(PlatformFactory PUBLIC Platform_X11_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID) - target_link_libraries(PlatformFactory PUBLIC Platform_Android_EGL) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS) - target_link_libraries(PlatformFactory PUBLIC Platform_Windows_WGL) -endif() diff --git a/renderer/RendererLib/CMakeLists.txt b/renderer/RendererLib/CMakeLists.txt deleted file mode 100644 index 9ab2243d3..000000000 --- a/renderer/RendererLib/CMakeLists.txt +++ /dev/null @@ -1,87 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -createModule( - NAME ramses-renderer-lib - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - - INCLUDE_PATHS RendererAPI/include - SRC_FILES RendererAPI/include/RendererAPI/*.h - RendererAPI/src/*.cpp - - INCLUDE_PATHS Platform_Base/include - SRC_FILES Platform_Base/include/Platform_Base/*.h - Platform_Base/src/*.cpp - - INCLUDE_PATHS RendererCommands/include - SRC_FILES RendererCommands/include/RendererCommands/*.h - RendererCommands/src/*.cpp - - INCLUDE_PATHS RendererFramework/include - SRC_FILES RendererFramework/include/RendererFramework/*.h - RendererFramework/include/*.h - RendererFramework/src/*.cpp - - INCLUDE_PATHS RendererLib/include - SRC_FILES RendererLib/include/RendererLib/*.h - RendererLib/include/*.h - RendererLib/src/*.cpp - DEPENDENCIES ramses-renderer-api - ramses-framework -) - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS) - target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) - target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_X11) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID) - target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_ANDROID) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) - target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) -endif() - -if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) - target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) -endif() - -IF (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME RendererTestCommon - TYPE STATIC_LIBRARY - - INCLUDE_PATHS RendererTestCommon - SRC_FILES RendererTestCommon/*.h - RendererTestCommon/*.cpp - DEPENDENCIES ramses-renderer-lib - FrameworkTestUtils - ) - - createModule( - NAME ramses-renderer-lib-test - TYPE BINARY - SRC_FILES RendererFramework/test/*.cpp - RendererFramework/test/*.h - RendererLib/test/*.cpp - RendererLib/test/*.h - RendererCommands/test/*.cpp - RendererCommands/test/*.h - DEPENDENCIES RendererTestCommon - ramses-gmock-main - ) - - makeTestFromTarget( - TARGET ramses-renderer-lib-test - SUFFIX UNITTEST) -endif() diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/ELoopMode.h b/renderer/RendererLib/RendererAPI/include/RendererAPI/ELoopMode.h deleted file mode 100644 index 05cf604b0..000000000 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/ELoopMode.h +++ /dev/null @@ -1,21 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_ELOOPMODE_H -#define RAMSES_ELOOPMODE_H - -namespace ramses_internal -{ - enum class ELoopMode - { - UpdateAndRender = 0, - UpdateOnly - }; -} - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/EWindowType.h b/renderer/RendererLib/RendererAPI/include/RendererAPI/EWindowType.h deleted file mode 100644 index 5b234be9c..000000000 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/EWindowType.h +++ /dev/null @@ -1,46 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2023 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EWINDOWTYPE_H -#define RAMSES_EWINDOWTYPE_H - -#include - -namespace ramses_internal -{ - enum class EWindowType - { - Windows, - X11, - Wayland_IVI, - Wayland_Shell, - Android - }; - - //This is needed to decide default platform based on build configuration - constexpr static std::array SupportedWindowTypes{ -#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS) - EWindowType::Windows, -#endif - -#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_X11) - EWindowType::X11, -#endif -#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) - EWindowType::Wayland_IVI, -#endif -#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) - EWindowType::Wayland_Shell, -#endif -#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_ANDROID) - EWindowType::Android -#endif - }; -} - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IRendererResourceCache.h b/renderer/RendererLib/RendererAPI/include/RendererAPI/IRendererResourceCache.h deleted file mode 100644 index fdabf908f..000000000 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IRendererResourceCache.h +++ /dev/null @@ -1,32 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_IRENDERERRESOURCECACHE_H -#define RAMSES_IRENDERERRESOURCECACHE_H - -#include "Resource/IResource.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/ResourceContentHash.h" -#include "RendererAPI/Types.h" - -namespace ramses_internal -{ - // Internal version of ramses::IRendererResourceCache; same members, but using the corresponding internal datatypes - class IRendererResourceCache - { - public: - virtual ~IRendererResourceCache() {}; - - virtual bool hasResource(ResourceContentHash resourceId, uint32_t& size) const = 0; - virtual bool getResourceData(ResourceContentHash resourceId, uint8_t* buffer, uint32_t bufferSize) const = 0; - [[nodiscard]] virtual bool shouldResourceBeCached(ResourceContentHash resourceId, uint32_t resourceDataSize, ResourceCacheFlag cacheFlag, SceneId sceneId) const = 0; - virtual void storeResource(ResourceContentHash resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, ResourceCacheFlag cacheFlag, SceneId sceneId) = 0; - }; -} - -#endif diff --git a/renderer/RendererLib/RendererAPI/src/Types.cpp b/renderer/RendererLib/RendererAPI/src/Types.cpp deleted file mode 100644 index a85de4312..000000000 --- a/renderer/RendererLib/RendererAPI/src/Types.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererAPI/Types.h" -#include "Utils/LoggingUtils.h" -#include "RamsesFrameworkTypesImpl.h" -#include "ramses-renderer-api/Types.h" - -static_assert(std::is_same::value, "DisplayHandle type mismatch"); -static_assert(ramses::displayId_t::Invalid().getValue() == ramses_internal::DisplayHandle::Invalid().asMemoryHandle(), "DisplayHandle default mismatch"); - -static_assert(std::is_same::value, "OffscreenBufferHandle type mismatch"); -static_assert(ramses::displayBufferId_t::Invalid().getValue() == ramses_internal::OffscreenBufferHandle::Invalid().asMemoryHandle(), "OffscreenBufferHandle default mismatch"); - -static_assert(std::is_same::value, "WaylandIviSurfaceId type mismatch"); -static_assert(ramses::waylandIviSurfaceId_t::Invalid().getValue() == ramses_internal::WaylandIviSurfaceId::Invalid().getValue(), "WaylandIviSurfaceId default mismatch"); -static_assert(std::is_same::value, "WaylandIviSurfaceId type mismatch"); -static_assert(ramses::waylandIviSurfaceId_t::Invalid().getValue() == ramses_internal::WaylandIviSurfaceId::Invalid().getValue(), "WaylandIviSurfaceId default mismatch"); - -static_assert(std::is_same::value, "WaylandIviLayerId type mismatch"); -static_assert(ramses::waylandIviLayerId_t::Invalid().getValue() == ramses_internal::WaylandIviLayerId::Invalid().getValue(), "WaylandIviLayerId default mismatch"); - -static_assert(std::is_same::value, "BinaryShaderFormatID type mismatch"); -static_assert(ramses::binaryShaderFormatId_t::Invalid().getValue() == ramses_internal::BinaryShaderFormatID::Invalid().getValue(), "BinaryShaderFormatID default mismatch"); diff --git a/renderer/RendererLib/RendererCommands/src/SetClearColor.cpp b/renderer/RendererLib/RendererCommands/src/SetClearColor.cpp deleted file mode 100644 index ce92e7f85..000000000 --- a/renderer/RendererLib/RendererCommands/src/SetClearColor.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - - -#include "RendererCommands/SetClearColor.h" -#include "RendererLib/RendererCommandBuffer.h" - -namespace ramses_internal -{ - SetClearColor::SetClearColor(RendererCommandBuffer& rendererCommandBuffer) - : m_rendererCommandBuffer(rendererCommandBuffer) - { - description = "set display clear color"; - registerKeyword("clc"); - getArgument<0>().setDescription("display id"); - getArgument<1>().setDescription("red channel value"); - getArgument<2>().setDescription("green channel value"); - getArgument<3>().setDescription("blue channel value"); - getArgument<4>().setDescription("alpha channel value"); - } - - bool SetClearColor::execute(uint32_t& displayId, float& red, float& green, float& blue, float& alpha) const - { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SetClearColor{ DisplayHandle{displayId}, {}, glm::vec4{red, green, blue, alpha} }); - return true; - } -} diff --git a/renderer/RendererLib/RendererCommands/test/ScreenshotTest.cpp b/renderer/RendererLib/RendererCommands/test/ScreenshotTest.cpp deleted file mode 100644 index 94285ea77..000000000 --- a/renderer/RendererLib/RendererCommands/test/ScreenshotTest.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererCommands/Screenshot.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererCommandVisitorMock.h" - -#include - -using namespace ramses_internal; -using namespace ::testing; - -class AScreenshot : public ::testing::Test -{ -public: - AScreenshot() - : m_defaultFilename("unnamed.png") - , m_cmd(m_rendererCommandBuffer) - { - } - -protected: - void expectScreenshotCommand(std::string_view filename, const DisplayHandle& displayHandle = DisplayHandle(0u), bool autoSize = true) - { - StrictMock cmdVisitor; - EXPECT_CALL(cmdVisitor, handleReadPixels(displayHandle, OffscreenBufferHandle::Invalid(), _, _, _, _, autoSize, filename)); - cmdVisitor.visit(m_rendererCommandBuffer); - } - - const std::string m_defaultFilename; - RendererCommandBuffer m_rendererCommandBuffer; - Screenshot m_cmd; -}; - -TEST_F(AScreenshot, executesScreenshotNoArgsDefined) -{ - EXPECT_TRUE(m_cmd.executeInput(std::vector{})); - expectScreenshotCommand(m_defaultFilename); -} - -TEST_F(AScreenshot, executesScreenshotWithFilename) -{ - const std::string filename("someFilename.png"); - EXPECT_TRUE(m_cmd.executeInput({"-filename", filename})); - expectScreenshotCommand(filename); -} - -TEST_F(AScreenshot, executesScreenshotWithDisplay) -{ - const DisplayHandle displayHandle(23u); - EXPECT_TRUE(m_cmd.executeInput(std::vector{"-displayId", "23"})); - expectScreenshotCommand(m_defaultFilename, displayHandle); -} - -TEST_F(AScreenshot, brokenArgumentsAreNotExecuted) -{ - EXPECT_FALSE(m_cmd.executeInput(std::vector{"foo"})); - EXPECT_FALSE(m_cmd.executeInput(std::vector{"-filename", "firstfile.png", "secondfile.png"})); - EXPECT_FALSE(m_cmd.executeInput(std::vector{"-displayId", "0", "1", "2"})); -} - -TEST_F(AScreenshot, emptyOptionsAreValid) -{ - EXPECT_TRUE(m_cmd.executeInput(std::vector{"-filename", "-displayId"})); - expectScreenshotCommand(m_defaultFilename); -} diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/EKeyModifier.h b/renderer/RendererLib/RendererLib/include/RendererLib/EKeyModifier.h deleted file mode 100644 index ad0bbb1a9..000000000 --- a/renderer/RendererLib/RendererLib/include/RendererLib/EKeyModifier.h +++ /dev/null @@ -1,65 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EKEYMODIFIER_H -#define RAMSES_EKEYMODIFIER_H - -#include "Common/BitForgeMacro.h" - -#include - -namespace ramses_internal -{ - - enum EKeyModifier : uint32_t - { - EKeyModifier_NoModifier = 0, - EKeyModifier_Ctrl = BIT(0u), - EKeyModifier_Shift = BIT(1u), - EKeyModifier_Alt = BIT(2u), - EKeyModifier_Function = BIT(3u), - EKeyModifier_Numpad = BIT(4u) - }; - - static inline std::string KeyModifierToString(uint32_t flags) - { - std::string s = "("; - if (flags == EKeyModifier_NoModifier) - { - s.append("EKeyModifier_NoModifier"); - } - else - { - if (flags & EKeyModifier_Ctrl) - { - s.append("EKeyModifier_Ctrl | "); - } - if (flags & EKeyModifier_Shift) - { - s.append("EKeyModifier_Shift | "); - } - if (flags & EKeyModifier_Alt) - { - s.append("EKeyModifier_Alt | "); - } - if (flags & EKeyModifier_Function) - { - s.append("EKeyModifier_Function | "); - } - if (flags & EKeyModifier_Numpad) - { - s.append("EKeyModifier_Numpad | "); - } - } - s.append(")"); - - return s; - }; - -} -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/EMouseEventType.h b/renderer/RendererLib/RendererLib/include/RendererLib/EMouseEventType.h deleted file mode 100644 index d96152a75..000000000 --- a/renderer/RendererLib/RendererLib/include/RendererLib/EMouseEventType.h +++ /dev/null @@ -1,57 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_EMOUSEEVENTTYPE_H -#define RAMSES_EMOUSEEVENTTYPE_H - -#include "Utils/LoggingUtils.h" -#include "PlatformAbstraction/PlatformTypes.h" - -namespace ramses_internal -{ - enum EMouseEventType - { - EMouseEventType_Invalid = 0, - - EMouseEventType_LeftButtonDown, - EMouseEventType_LeftButtonUp, - EMouseEventType_RightButtonDown, - EMouseEventType_RightButtonUp, - EMouseEventType_MiddleButtonDown, - EMouseEventType_MiddleButtonUp, - - EMouseEventType_WheelUp, - EMouseEventType_WheelDown, - - EMouseEventType_Move, - - EMouseEventType_WindowEnter, - EMouseEventType_WindowLeave, - - EMouseEventType_NUMBER_OF_ELEMENTS - }; - - const std::array MouseEventTypeNames = - { - "EMouseEventType_Invalid", - "EMouseEventType_LeftButtonDown", - "EMouseEventType_LeftButtonUp", - "EMouseEventType_RightButtonDown", - "EMouseEventType_RightButtonUp", - "EMouseEventType_MiddleButtonDown", - "EMouseEventType_MiddleButtonUp", - "EMouseEventType_WheelUp", - "EMouseEventType_WheelDown", - "EMouseEventType_Move", - "EMouseEventType_WindowEnter", - "EMouseEventType_WindowLeave" - }; - - ENUM_TO_STRING(EMouseEventType, MouseEventTypeNames, EMouseEventType_NUMBER_OF_ELEMENTS); -} -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManagerUtils.h b/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManagerUtils.h deleted file mode 100644 index fcab7b781..000000000 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManagerUtils.h +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERRESOURCEMANAGERUTILS_H -#define RAMSES_RENDERERRESOURCEMANAGERUTILS_H - -#include "Components/ManagedResource.h" -#include "SceneAPI/SceneId.h" - -namespace ramses_internal -{ - class IResource; - class IRendererResourceCache; - - class RendererResourceManagerUtils - { - public: - - static ManagedResource TryLoadResource(ResourceContentHash resourceId, uint32_t resourceSize, const IRendererResourceCache* cache); - static void StoreResource(IRendererResourceCache* cache, const IResource* resource, SceneId sceneId); - }; -} - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneDisplayTracker.h b/renderer/RendererLib/RendererLib/include/RendererLib/SceneDisplayTracker.h deleted file mode 100644 index 5f58d1b13..000000000 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneDisplayTracker.h +++ /dev/null @@ -1,88 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENEDISPLAYTRACKER_H -#define RAMSES_SCENEDISPLAYTRACKER_H - -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" -#include "RendererLib/RendererCommands.h" -#include "RendererLib/RendererEvent.h" -#include -#include - -namespace ramses_internal -{ - class SceneDisplayTracker - { - public: - void setSceneOwnership(SceneId scene, DisplayHandle display); - [[nodiscard]] DisplayHandle getSceneOwnership(SceneId scene) const; - void unregisterDisplay(DisplayHandle display); - - // 3 return types - valid display, invalid display (error), no value (broadcast) - [[nodiscard]] std::optional determineDisplayFromRendererCommand(const RendererCommand::Variant& cmd) const; - - private: - // scene commands - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::ReceiveScene& cmd) const { return getSceneOwnership(cmd.info.sceneID); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::UpdateScene& cmd) const { return getSceneOwnership(cmd.scene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetSceneState& cmd) const { return getSceneOwnership(cmd.scene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetSceneDisplayBufferAssignment& cmd) const { return getSceneOwnership(cmd.scene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::PickEvent& cmd) const { return getSceneOwnership(cmd.scene); } - // link commands - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkData& cmd) const { return getSceneOwnership(cmd.consumerScene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkOffscreenBuffer& cmd) const { return getSceneOwnership(cmd.consumerScene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkStreamBuffer& cmd) const { return getSceneOwnership(cmd.consumerScene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkExternalBuffer& cmd) const { return getSceneOwnership(cmd.consumerScene); } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::UnlinkData& cmd) const { return getSceneOwnership(cmd.consumerScene); } - // display commands - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetSceneMapping& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::CreateDisplay& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::DestroyDisplay& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::CreateOffscreenBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::CreateDmaOffscreenBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::DestroyOffscreenBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::CreateStreamBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::DestroyStreamBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::CreateExternalBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::DestroyExternalBuffer& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetClearFlags& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetClearColor& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetExterallyOwnedWindowSize& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::ReadPixels& cmd) const { return cmd.display; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::ConfirmationEcho& cmd) const { return cmd.display; } - // broadcast commands - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::ScenePublished&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SceneUnpublished&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetSkippingOfUnmodifiedBuffers&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LogStatistics&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LogInfo&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCListIviSurfaces&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCSetIviSurfaceVisibility&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCSetIviSurfaceOpacity&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCSetIviSurfaceDestRectangle&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCScreenshot&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCAddIviSurfaceToIviLayer&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCSetIviLayerVisibility&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCRemoveIviSurfaceFromIviLayer&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SCDestroyIviSurface&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetLimits_FrameBudgets&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetLimits_FlushesForceApply&) const { return {}; } - [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetLimits_FlushesForceUnsubscribe&) const { return {}; } - - std::unordered_map m_sceneToDisplay; - }; - - inline std::optional SceneDisplayTracker::determineDisplayFromRendererCommand(const RendererCommand::Variant& cmd) const - { - return std::visit([&](const auto& c) { return getDisplayOf(c); }, cmd); - } -} - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererResourceManagerUtils.cpp b/renderer/RendererLib/RendererLib/src/RendererResourceManagerUtils.cpp deleted file mode 100644 index 9eee1a375..000000000 --- a/renderer/RendererLib/RendererLib/src/RendererResourceManagerUtils.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererLib/RendererResourceManagerUtils.h" -#include "RendererAPI/IRendererResourceCache.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/BinaryOutputStream.h" -#include "Resource/IResource.h" -#include "Components/SingleResourceSerialization.h" -#include "Components/ResourceDeleterCallingCallback.h" - -namespace ramses_internal -{ - ManagedResource RendererResourceManagerUtils::TryLoadResource(ResourceContentHash resourceId, uint32_t resourceSize, const IRendererResourceCache* cache) - { - std::vector readBuffer(resourceSize); - - cache->getResourceData(resourceId, readBuffer.data(), resourceSize); - - BinaryInputStream resourceStream(readBuffer.data()); - std::unique_ptr resourceObject = SingleResourceSerialization::DeserializeResource(resourceStream, resourceId); - - static ResourceDeleterCallingCallback deleter = ResourceDeleterCallingCallback(DefaultManagedResourceDeleterCallback::GetInstance()); - - return ManagedResource{ resourceObject.release(), deleter }; - } - - void RendererResourceManagerUtils::StoreResource(IRendererResourceCache* cache, const IResource* resource, SceneId sceneId) - { - assert(cache); - assert(resource); - - const uint32_t serializedSize = SingleResourceSerialization::SizeOfSerializedResource(*resource); - const ResourceContentHash resHash = resource->getHash(); - const ResourceCacheFlag cacheFlag = resource->getCacheFlag(); - - // First check if the cache wants the resource, before going through the work of preparing it for storage. - if (cache->shouldResourceBeCached(resHash, serializedSize, cacheFlag, sceneId)) - { - BinaryOutputStream serializerStream(serializedSize); - SingleResourceSerialization::SerializeResource(serializerStream, *resource); - assert(serializerStream.getSize() == serializedSize); - - cache->storeResource(resHash, reinterpret_cast(serializerStream.getData()), serializedSize, cacheFlag, sceneId); - } - } -} diff --git a/renderer/RendererLib/RendererLib/test/BufferLinksTest.cpp b/renderer/RendererLib/RendererLib/test/BufferLinksTest.cpp deleted file mode 100644 index eb3db14bb..000000000 --- a/renderer/RendererLib/RendererLib/test/BufferLinksTest.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/BufferLinks.h" - -using namespace testing; -using namespace ramses_internal; - -using BufferTypes = ::testing::Types < - OffscreenBufferHandle, - StreamBufferHandle > ; - -template -class ABufferLinks : public ::testing::Test -{ -protected: - static bool LinksEqual(const BufferLink& link1, const BufferLink& link2) - { - return link1.providerBuffer == link2.providerBuffer - && link1.consumerSceneId == link2.consumerSceneId - && link1.consumerSlot == link2.consumerSlot; - } - - static bool ContainsLink(const BufferLinkVector& links, const BufferLink& link) - { - for(const auto& linkIter : links) - { - if (LinksEqual(linkIter, link)) - { - return true; - } - } - - return false; - } - - void expectNoLinks() - { - EXPECT_FALSE(this->sceneLinks.hasAnyLinksToProvider(this->consumerScene1)); - EXPECT_FALSE(this->sceneLinks.hasAnyLinksToProvider(this->consumerScene2)); - EXPECT_FALSE(this->sceneLinks.hasAnyLinksToConsumer(this->providerBuffer1)); - EXPECT_FALSE(this->sceneLinks.hasAnyLinksToConsumer(this->providerBuffer2)); - EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene1, this->consumerSlot1)); - EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene1, this->consumerSlot2)); - EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene2, this->consumerSlot1)); - EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene2, this->consumerSlot2)); - expectLinkCount(this->providerBuffer1, 0u); - expectLinkCount(this->providerBuffer2, 0u); - expectLinkCount(this->consumerScene1, 0u); - expectLinkCount(this->consumerScene2, 0u); - } - - void expectLinkCount(SceneId sceneId, uint32_t providerScenesLinked) - { - BufferLinkVector links; - this->sceneLinks.getLinkedProviders(sceneId, links); - EXPECT_EQ(providerScenesLinked, links.size()); - } - - void expectLinkCount(BUFFERHANDLE providerBuffer, uint32_t consumerScenesLinked) - { - BufferLinkVector links; - this->sceneLinks.getLinkedConsumers(providerBuffer, links); - EXPECT_EQ(consumerScenesLinked, links.size()); - } - - void expectLink( - BUFFERHANDLE providerBuffer, - SceneId consumerSceneId, - DataSlotHandle consumerSlotHandle) - { - EXPECT_TRUE(this->sceneLinks.hasAnyLinksToProvider(consumerSceneId)); - EXPECT_TRUE(this->sceneLinks.hasAnyLinksToConsumer(providerBuffer)); - EXPECT_TRUE(this->sceneLinks.hasLinkedProvider(consumerSceneId, consumerSlotHandle)); - - BufferLink expectedLink; - expectedLink.providerBuffer = providerBuffer; - expectedLink.consumerSceneId = consumerSceneId; - expectedLink.consumerSlot = consumerSlotHandle; - - BufferLinkVector links; - this->sceneLinks.getLinkedProviders(consumerSceneId, links); - EXPECT_TRUE(ContainsLink(links, expectedLink)); - links.clear(); - - EXPECT_TRUE(LinksEqual(expectedLink, this->sceneLinks.getLinkedProvider(consumerSceneId, consumerSlotHandle))); - - this->sceneLinks.getLinkedConsumers(providerBuffer, links); - EXPECT_TRUE(ContainsLink(links, expectedLink)); - } - - BufferLinks sceneLinks; - - const BUFFERHANDLE providerBuffer1{ 1u }; - const BUFFERHANDLE providerBuffer2{ 2u }; - const SceneId consumerScene1{ 3u }; - const SceneId consumerScene2{ 4u }; - - const DataSlotHandle consumerSlot1{ 5u }; - const DataSlotHandle consumerSlot2{ 6u }; -}; - -TYPED_TEST_SUITE(ABufferLinks, BufferTypes); - -TYPED_TEST(ABufferLinks, reportsNoLinksInitially) -{ - this->expectNoLinks(); -} - -TYPED_TEST(ABufferLinks, canAddLink) -{ - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); - this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); - this->expectLinkCount(this->providerBuffer1, 1u); - this->expectLinkCount(this->consumerScene1, 1u); -} - -TYPED_TEST(ABufferLinks, canAddAndRemoveLink) -{ - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); - this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot1); - this->expectNoLinks(); -} - -TYPED_TEST(ABufferLinks, canAddAndRemoveMultipleLinks) -{ - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene2, this->consumerSlot1); - this->sceneLinks.addLink(this->providerBuffer2, this->consumerScene2, this->consumerSlot2); - - this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); - this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); - this->expectLink(this->providerBuffer1, this->consumerScene2, this->consumerSlot1); - this->expectLink(this->providerBuffer2, this->consumerScene2, this->consumerSlot2); - - this->expectLinkCount(this->providerBuffer1, 3u); - this->expectLinkCount(this->consumerScene1, 2u); - this->expectLinkCount(this->consumerScene2, 2u); - - this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot1); - this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot2); - this->sceneLinks.removeLink(this->consumerScene2, this->consumerSlot1); - this->sceneLinks.removeLink(this->consumerScene2, this->consumerSlot2); - this->expectNoLinks(); -} - -TYPED_TEST(ABufferLinks, removingLinkDoesNotAffectOtherLinks) -{ - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); - this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); - this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot1); - - this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); - this->expectLinkCount(this->providerBuffer1, 1u); - this->expectLinkCount(this->consumerScene1, 1u); - - this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot2); - this->expectNoLinks(); -} diff --git a/renderer/RendererLib/RendererLib/test/DataReferenceLinkCachedSceneTest.cpp b/renderer/RendererLib/RendererLib/test/DataReferenceLinkCachedSceneTest.cpp deleted file mode 100644 index 2c3b3fa4a..000000000 --- a/renderer/RendererLib/RendererLib/test/DataReferenceLinkCachedSceneTest.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/DataReferenceLinkCachedScene.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" -#include "SceneAllocateHelper.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -class ADataReferenceLinkCachedScene : public ::testing::Test -{ -public: - ADataReferenceLinkCachedScene() - : rendererScenes(rendererEventCollector) - , scene(rendererScenes.createScene(SceneInfo(SceneId(3u)))) - , sceneAllocator(scene) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - const DataLayoutHandle layout = sceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Int32) }, ResourceContentHash::Invalid()); - dataRef = sceneAllocator.allocateDataInstance(layout); - - dataSlot = sceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, DataSlotId(1u), NodeHandle(), dataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }); - } - -protected: - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - DataReferenceLinkCachedScene& scene; - SceneAllocateHelper sceneAllocator; - - DataInstanceHandle dataRef; - DataSlotHandle dataSlot; -}; - -TEST_F(ADataReferenceLinkCachedScene, canSetAndGetValue) -{ - scene.setDataSingleInteger(dataRef, DataFieldHandle(0u), 13); - EXPECT_EQ(13, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); -} - -TEST_F(ADataReferenceLinkCachedScene, canSetValueDirectlyWithoutUpdatingFallbackValue) -{ - scene.setValueWithoutUpdatingFallbackValue(dataRef, DataFieldHandle(0u), DataInstanceValueVariant(33)); - EXPECT_EQ(33, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); -} - -TEST_F(ADataReferenceLinkCachedScene, canRestoreFallbackValue) -{ - scene.setDataSingleInteger(dataRef, DataFieldHandle(0u), 13); - - scene.setValueWithoutUpdatingFallbackValue(dataRef, DataFieldHandle(0u), DataInstanceValueVariant(33)); - - scene.restoreFallbackValue(dataRef, DataFieldHandle(0u)); - EXPECT_EQ(13, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); -} - -TEST_F(ADataReferenceLinkCachedScene, storesFallbackValueWhenSlotAllocated) -{ - scene.releaseDataSlot(dataSlot); - - scene.setDataSingleInteger(dataRef, DataFieldHandle(0u), 13); - - sceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, DataSlotId(1u), NodeHandle(), dataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }); - - scene.restoreFallbackValue(dataRef, DataFieldHandle(0u)); - EXPECT_EQ(13, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); -} diff --git a/renderer/RendererLib/RendererLib/test/DataReferenceLinkManagerTest.cpp b/renderer/RendererLib/RendererLib/test/DataReferenceLinkManagerTest.cpp deleted file mode 100644 index 6a08ed618..000000000 --- a/renderer/RendererLib/RendererLib/test/DataReferenceLinkManagerTest.cpp +++ /dev/null @@ -1,466 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" -#include "SceneUtils/ISceneDataArrayAccessor.h" -#include "SceneAllocateHelper.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -class ADataReferenceLinkManager : public ::testing::Test -{ -public: - explicit ADataReferenceLinkManager(bool createSlots = true) - : rendererScenes(rendererEventCollector) - , sceneLinksManager(rendererScenes.getSceneLinksManager()) - , dataReferenceLinkManager(sceneLinksManager.getDataReferenceLinkManager()) - , providerSceneId(3u) - , consumerSceneId(4u) - , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) - , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) - , providerSceneAllocator(providerScene) - , consumerSceneAllocator(consumerScene) - , providerSlotHandle(55u) - , consumerSlotHandle(66u) - , providerId(33u) - , consumerId(44u) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - if (createSlots) - { - const DataLayoutHandle providerLayout = providerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - providerDataRef = providerSceneAllocator.allocateDataInstance(providerLayout, DataInstanceHandle(3u)); - - const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - consumerDataRef = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(5u)); - - providerSceneAllocator.allocateDataSlot({ EDataSlotType_DataProvider, providerId, NodeHandle(), providerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, providerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, consumerId, NodeHandle(), consumerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); - } - } - -protected: - void expectRendererEvent(ERendererEventType event, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(event, events.front().eventType); - EXPECT_EQ(providerSId, events.front().providerSceneId); - EXPECT_EQ(consumerSId, events.front().consumerSceneId); - EXPECT_EQ(pId, events.front().providerdataId); - EXPECT_EQ(cId, events.front().consumerdataId); - } - - void expectRendererEvent(ERendererEventType event, SceneId consumerSId, DataSlotId cId, SceneId providerSId) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(event, events.front().eventType); - EXPECT_EQ(consumerSId, events.front().consumerSceneId); - EXPECT_EQ(cId, events.front().consumerdataId); - EXPECT_EQ(providerSId, events.front().providerSceneId); - } - - void setDataValue(DataInstanceHandle dataRef, IScene& scene, float value) - { - scene.setDataSingleFloat(dataRef, DataFieldHandle(0u), value); - } - - void expectDataValue(DataInstanceHandle dataRef, const IScene& scene, float value) - { - const float actualValue = scene.getDataSingleFloat(dataRef, DataFieldHandle(0u)); - EXPECT_EQ(value, actualValue); - } - - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - SceneLinksManager& sceneLinksManager; - const DataReferenceLinkManager& dataReferenceLinkManager; - const SceneId providerSceneId; - const SceneId consumerSceneId; - IScene& providerScene; - DataReferenceLinkCachedScene& consumerScene; - SceneAllocateHelper providerSceneAllocator; - SceneAllocateHelper consumerSceneAllocator; - - DataInstanceHandle providerDataRef; - DataInstanceHandle consumerDataRef; - - const DataSlotHandle providerSlotHandle; - const DataSlotHandle consumerSlotHandle; - const DataSlotId providerId; - const DataSlotId consumerId; -}; - -TEST_F(ADataReferenceLinkManager, failsToCreateLinkOfMismatchingDataTypes) -{ - const DataLayoutHandle providerLayout = providerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Int32) }, ResourceContentHash::Invalid()); - const DataInstanceHandle providerDataRef2 = providerSceneAllocator.allocateDataInstance(providerLayout, DataInstanceHandle(9u)); - - const DataSlotId providerId2(999u); - const DataSlotHandle slotHandle(43u); - providerSceneAllocator.allocateDataSlot({ EDataSlotType_DataProvider, providerId2, NodeHandle(), providerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); - - sceneLinksManager.createDataLink(providerSceneId, providerId2, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinkFailed, providerSceneId, providerId2, consumerSceneId, consumerId); -} - -TEST_F(ADataReferenceLinkManager, canResolveLinkedData) -{ - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(consumerDataRef, consumerScene, 666.f); - expectDataValue(providerDataRef, providerScene, 666.f); -} - -TEST_F(ADataReferenceLinkManager, canResolveLinkedDataToMultipleConsumers) -{ - const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - const DataInstanceHandle consumerDataRef2 = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(9u)); - - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle(43u); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, consumerId2, NodeHandle(), consumerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - setDataValue(consumerDataRef2, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId2); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(consumerDataRef, consumerScene, 666.f); - expectDataValue(consumerDataRef2, consumerScene, 666.f); - expectDataValue(providerDataRef, providerScene, 666.f); -} - -TEST_F(ADataReferenceLinkManager, doesNotResolveLinkedDataIfConsumerUnlinked) -{ - const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - const DataInstanceHandle consumerDataRef2 = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(9u)); - - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle(43u); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, consumerId2, NodeHandle(), consumerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - setDataValue(consumerDataRef2, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId2); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(consumerDataRef, consumerScene, -1.f); - expectDataValue(consumerDataRef2, consumerScene, 666.f); - expectDataValue(providerDataRef, providerScene, 666.f); -} - -TEST_F(ADataReferenceLinkManager, confidenceTest_canResolveLinkedWithThreeScenesAndTwoLinks) -{ - const SceneId middleSceneId(145u); - DataReferenceLinkCachedScene& middleScene = rendererScenes.createScene(SceneInfo(middleSceneId)); - SceneAllocateHelper middleSceneAllocator(middleScene); - - DataInstanceHandle middleProviderDataRef; - DataInstanceHandle middleConsumerDataRef; - - const DataLayoutHandle layout = middleSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - middleProviderDataRef = middleSceneAllocator.allocateDataInstance(layout, DataInstanceHandle(3u)); - middleConsumerDataRef = middleSceneAllocator.allocateDataInstance(layout, DataInstanceHandle(5u)); - - const DataSlotHandle middleProviderSlotHandle(18u); - const DataSlotHandle middleConsumerSlotHandle(19u); - const DataSlotId middleProviderId(18u); - const DataSlotId middleConsumerId(19u); - middleSceneAllocator.allocateDataSlot({ EDataSlotType_DataProvider, middleProviderId, NodeHandle(), middleProviderDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, middleProviderSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, middleSceneId, middleProviderId, SceneId(0u), DataSlotId(0u)); - middleSceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, middleConsumerId, NodeHandle(), middleConsumerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, middleConsumerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), middleSceneId, middleConsumerId); - - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(middleConsumerDataRef, middleScene, -1.f); - - setDataValue(middleProviderDataRef, middleScene, 333.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, middleSceneId, middleConsumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, middleSceneId, middleConsumerId); - sceneLinksManager.createDataLink(middleSceneId, middleProviderId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, middleSceneId, middleProviderId, consumerSceneId, consumerId); - - dataReferenceLinkManager.resolveLinksForConsumerScene(middleScene); - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(providerDataRef, providerScene, 666.f); - expectDataValue(middleConsumerDataRef, middleScene, 666.f); - expectDataValue(middleProviderDataRef, middleScene, 333.f); - expectDataValue(consumerDataRef, consumerScene, 333.f); -} - -TEST_F(ADataReferenceLinkManager, unlinkedDataFallsBackToPreviousValue) -{ - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(providerDataRef, providerScene, 666.f); - expectDataValue(consumerDataRef, consumerScene, 666.f); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); - - expectDataValue(providerDataRef, providerScene, 666.f); - expectDataValue(consumerDataRef, consumerScene, -1.f); -} - -TEST_F(ADataReferenceLinkManager, dataFallsBackToPreviouslySetValueIfProviderSceneDestroyed) -{ - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - expectDataValue(providerDataRef, providerScene, 666.f); - expectDataValue(consumerDataRef, consumerScene, 666.f); - - setDataValue(consumerDataRef, consumerScene, -333.f); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - expectDataValue(providerDataRef, providerScene, 666.f); - expectDataValue(consumerDataRef, consumerScene, 666.f); - - rendererScenes.destroyScene(providerSceneId); - - expectDataValue(consumerDataRef, consumerScene, -333.f); -} - -TEST_F(ADataReferenceLinkManager, confidenceTest_createTwoLinksChangeValueAndUnlink) -{ - const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - const DataInstanceHandle consumerDataRef2 = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(9u)); - - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle(43u); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, consumerId2, NodeHandle(), consumerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - setDataValue(providerDataRef, providerScene, 666.f); - setDataValue(consumerDataRef, consumerScene, -1.f); - setDataValue(consumerDataRef2, consumerScene, -1.f); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId2); - - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(consumerDataRef, consumerScene, 666.f); - expectDataValue(consumerDataRef2, consumerScene, 666.f); - expectDataValue(providerDataRef, providerScene, 666.f); - - setDataValue(providerDataRef, providerScene, 123.f); - dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); - - expectDataValue(consumerDataRef, consumerScene, 123.f); - expectDataValue(consumerDataRef2, consumerScene, 123.f); - expectDataValue(providerDataRef, providerScene, 123.f); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); - - expectDataValue(consumerDataRef, consumerScene, -1.f); - expectDataValue(consumerDataRef2, consumerScene, 123.f); - expectDataValue(providerDataRef, providerScene, 123.f); -} - -template -class ADataReferenceLinkManagerTyped : public ADataReferenceLinkManager -{ -public: - ADataReferenceLinkManagerTyped() - : ADataReferenceLinkManager(false) - { - } -}; - -using ItemTypes = ::testing::Types < - int32_t, - float, - glm::vec2, - glm::vec3, - glm::vec4, - glm::ivec2, - glm::ivec3, - glm::ivec4, - glm::mat2, - glm::mat3, - glm::mat4 ->; - -TYPED_TEST_SUITE(ADataReferenceLinkManagerTyped, ItemTypes); - -template -T GetSomeValue() -{ - return T(5); -} - -template <> -glm::mat2 GetSomeValue() -{ - return glm::mat2(1,2,3,4); -} - -template <> -glm::mat3 GetSomeValue() -{ - return glm::mat3(1, 2, 3, 4, 5, 6, 7, 8, 9); -} - -template <> -glm::mat4 GetSomeValue() -{ - return glm::mat4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); -} - -template -T GetSomeValue2() -{ - return T(13); -} - -template <> -glm::mat2 GetSomeValue2() -{ - return glm::mat2(-1, -2, -3, -4); -} - -template <> -glm::mat3 GetSomeValue2() -{ - return glm::mat3(-1, -2, -3, -4, -5, -6, -7, -8, -9); -} - -template <> -glm::mat4 GetSomeValue2() -{ - return glm::mat4(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16); -} - -template -T GetSomeValue3() -{ - return T(-1513); -} - -template <> -glm::mat2 GetSomeValue3() -{ - return glm::mat2(-11, -22, -33, -44); -} - -template <> -glm::mat3 GetSomeValue3() -{ - return glm::mat3(-11, -21, -31, -41, -51, -61, -71, -81, -91); -} - -template <> -glm::mat4 GetSomeValue3() -{ - return glm::mat4(-12, -22, -32, -42, -52, -62, -72, -82, -92, -102, -112, -122, -132, -142, -152, -162); -} - -TYPED_TEST(ADataReferenceLinkManagerTyped, confidenceTest_linkAndChangeAndUnlink) -{ - const DataLayoutHandle providerLayout = this->providerSceneAllocator.allocateDataLayout({ DataFieldInfo(TypeToEDataTypeTraits::DataType) }, ResourceContentHash::Invalid()); - this->providerDataRef = this->providerSceneAllocator.allocateDataInstance(providerLayout, DataInstanceHandle(23u)); - - const DataLayoutHandle consumerLayout = this->consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(TypeToEDataTypeTraits::DataType) }, ResourceContentHash::Invalid()); - this->consumerDataRef = this->consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(24u)); - - this->providerSceneAllocator.allocateDataSlot({ EDataSlotType_DataProvider, this->providerId, NodeHandle(), this->providerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, this->providerSlotHandle); - this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->providerSceneId, this->providerId, SceneId(0u), DataSlotId(0u)); - this->consumerSceneAllocator.allocateDataSlot({ EDataSlotType_DataConsumer, this->consumerId, NodeHandle(), this->consumerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, this->consumerSlotHandle); - this->expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), this->consumerSceneId, this->consumerId); - - const TypeParam providerValue = GetSomeValue(); - const TypeParam consumerValueInitial = GetSomeValue2(); - const TypeParam consumerValueNew = GetSomeValue3(); - - // create link - ISceneDataArrayAccessor::SetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u), 1u, &providerValue); - ISceneDataArrayAccessor::SetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u), 1u, &consumerValueInitial); - - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - - this->dataReferenceLinkManager.resolveLinksForConsumerScene(this->consumerScene); - TypeParam actualProviderValue = ISceneDataArrayAccessor::GetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u))[0]; - TypeParam actualConsumerValue = ISceneDataArrayAccessor::GetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u))[0]; - EXPECT_EQ(providerValue, actualProviderValue); - EXPECT_EQ(providerValue, actualConsumerValue); - - // change value on consumer while linked - ISceneDataArrayAccessor::SetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u), 1u, &consumerValueNew); - this->dataReferenceLinkManager.resolveLinksForConsumerScene(this->consumerScene); - actualProviderValue = ISceneDataArrayAccessor::GetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u))[0]; - actualConsumerValue = ISceneDataArrayAccessor::GetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u))[0]; - EXPECT_EQ(providerValue, actualProviderValue); - EXPECT_EQ(providerValue, actualConsumerValue); - - // remove link - this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinked, this->consumerSceneId, this->consumerId, this->providerSceneId); - - actualProviderValue = ISceneDataArrayAccessor::GetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u))[0]; - actualConsumerValue = ISceneDataArrayAccessor::GetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u))[0]; - EXPECT_EQ(providerValue, actualProviderValue); - EXPECT_EQ(consumerValueNew, actualConsumerValue); -} diff --git a/renderer/RendererLib/RendererLib/test/DisplayConfigTest.cpp b/renderer/RendererLib/RendererLib/test/DisplayConfigTest.cpp deleted file mode 100644 index 4d204ef94..000000000 --- a/renderer/RendererLib/RendererLib/test/DisplayConfigTest.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/DisplayConfig.h" - -class AInternalDisplayConfig : public ::testing::Test -{ -public: - ramses_internal::DisplayConfig m_config; -}; - -TEST_F(AInternalDisplayConfig, hasDefaultValues) -{ - EXPECT_EQ(ramses_internal::EDeviceType::GLES_3_0, m_config.getDeviceType()); - EXPECT_FALSE(m_config.getFullscreenState()); - EXPECT_FALSE(m_config.getBorderlessState()); - EXPECT_EQ(1u, m_config.getAntialiasingSampleCount()); - EXPECT_EQ(1280u, m_config.getDesiredWindowWidth()); - EXPECT_EQ(480u, m_config.getDesiredWindowHeight()); - EXPECT_EQ(0, m_config.getWindowPositionX()); - EXPECT_EQ(0, m_config.getWindowPositionY()); - EXPECT_TRUE(m_config.getKeepEffectsUploaded()); - EXPECT_TRUE(!m_config.getWaylandIviLayerID().isValid()); - EXPECT_FALSE(m_config.getStartVisibleIvi()); - EXPECT_FALSE(m_config.isResizable()); - EXPECT_EQ(0u, m_config.getGPUMemoryCacheSize()); - EXPECT_EQ(glm::vec4(0.f,0.f,0.f,1.f), m_config.getClearColor()); - EXPECT_EQ("", m_config.getWaylandDisplay()); - EXPECT_EQ(ramses_internal::ERenderBufferType_DepthStencilBuffer, m_config.getDepthStencilBufferType()); - EXPECT_TRUE(m_config.isAsyncEffectUploadEnabled()); - EXPECT_EQ(std::string(""), m_config.getWaylandSocketEmbedded()); - EXPECT_EQ(std::string(""), m_config.getWaylandSocketEmbeddedGroup()); - EXPECT_EQ(-1, m_config.getWaylandSocketEmbeddedFD()); - EXPECT_EQ(std::string(""), m_config.getPlatformRenderNode()); - EXPECT_EQ(-1, m_config.getSwapInterval()); - EXPECT_EQ(0, m_config.getScenePriority(ramses_internal::SceneId())); - EXPECT_EQ(0, m_config.getScenePriority(ramses_internal::SceneId(15562))); - EXPECT_EQ(10u, m_config.getResourceUploadBatchSize()); -} - -TEST_F(AInternalDisplayConfig, setAndGetValues) -{ - m_config.setDeviceType(ramses_internal::EDeviceType::GL_4_2); - EXPECT_EQ(ramses_internal::EDeviceType::GL_4_2, m_config.getDeviceType()); - - m_config.setWindowType(ramses_internal::EWindowType::Android); - EXPECT_EQ(ramses_internal::EWindowType::Android, m_config.getWindowType()); - - m_config.setFullscreenState(true); - EXPECT_TRUE(m_config.getFullscreenState()); - - m_config.setBorderlessState(true); - EXPECT_TRUE(m_config.getBorderlessState()); - - m_config.setAntialiasingSampleCount(2u); - EXPECT_EQ(2u, m_config.getAntialiasingSampleCount()); - - m_config.setDesiredWindowWidth(100u); - EXPECT_EQ(100u, m_config.getDesiredWindowWidth()); - - m_config.setDesiredWindowHeight(200u); - EXPECT_EQ(200u, m_config.getDesiredWindowHeight()); - - m_config.setWindowPositionX(-10); - EXPECT_EQ(-10, m_config.getWindowPositionX()); - - m_config.setWindowPositionY(10); - EXPECT_EQ(10, m_config.getWindowPositionY()); - - m_config.setWaylandIviLayerID(ramses_internal::WaylandIviLayerId(102u)); - EXPECT_EQ(102u, m_config.getWaylandIviLayerID().getValue()); - - m_config.setStartVisibleIvi(true); - EXPECT_TRUE(m_config.getStartVisibleIvi()); - - m_config.setKeepEffectsUploaded(false); - EXPECT_FALSE(m_config.getKeepEffectsUploaded()); - - m_config.setGPUMemoryCacheSize(256u); - EXPECT_EQ(256u, m_config.getGPUMemoryCacheSize()); - - m_config.setResizable(false); - EXPECT_FALSE(m_config.isResizable()); - - const glm::vec4 clearColor(0.1f, 0.2f, 0.3f, 0.4f); - m_config.setClearColor(clearColor); - EXPECT_EQ(clearColor, m_config.getClearColor()); - - m_config.setDepthStencilBufferType(ramses_internal::ERenderBufferType_DepthBuffer); - EXPECT_EQ(ramses_internal::ERenderBufferType_DepthBuffer, m_config.getDepthStencilBufferType()); - - m_config.setWaylandDisplay("ramses display"); - EXPECT_EQ("ramses display", m_config.getWaylandDisplay()); - - m_config.setAsyncEffectUploadEnabled(false); - EXPECT_FALSE(m_config.isAsyncEffectUploadEnabled()); - - m_config.setWaylandEmbeddedCompositingSocketName("wayland-11"); - EXPECT_EQ(std::string("wayland-11"), m_config.getWaylandSocketEmbedded()); - - m_config.setWaylandEmbeddedCompositingSocketFD(42); - EXPECT_EQ(42, m_config.getWaylandSocketEmbeddedFD()); - - m_config.setWaylandEmbeddedCompositingSocketGroup("groupname1"); - EXPECT_EQ(std::string("groupname1"), m_config.getWaylandSocketEmbeddedGroup()); - - m_config.setWaylandEmbeddedCompositingSocketPermissions(0654); - EXPECT_EQ(0654u, m_config.getWaylandSocketEmbeddedPermissions()); - - m_config.setPlatformRenderNode("/some/render/node"); - EXPECT_EQ(std::string("/some/render/node"), m_config.getPlatformRenderNode()); - - m_config.setSwapInterval(2); - EXPECT_EQ(2, m_config.getSwapInterval()); - - m_config.setResourceUploadBatchSize(3); - EXPECT_EQ(3u, m_config.getResourceUploadBatchSize()); - - m_config.setScenePriority(ramses_internal::SceneId(15562), -1); - EXPECT_EQ(-1, m_config.getScenePriority(ramses_internal::SceneId(15562))); - EXPECT_EQ(0, m_config.getScenePriority(ramses_internal::SceneId(15562 + 1))); - EXPECT_EQ(1u, m_config.getScenePriorities().size()); - EXPECT_EQ(-1, m_config.getScenePriorities().at(ramses_internal::SceneId(15562))); -} - -TEST_F(AInternalDisplayConfig, canBeCompared) -{ - ramses_internal::DisplayConfig config1; - ramses_internal::DisplayConfig config2; - EXPECT_EQ(config1, config2); - - config1.setAntialiasingSampleCount(4u); - EXPECT_NE(config1, config2); -} diff --git a/renderer/RendererLib/RendererLib/test/DisplayEventHandlerTest.cpp b/renderer/RendererLib/RendererLib/test/DisplayEventHandlerTest.cpp deleted file mode 100644 index 6e538befa..000000000 --- a/renderer/RendererLib/RendererLib/test/DisplayEventHandlerTest.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gtest/gtest.h" -#include "RendererAPI/Types.h" -#include "RendererLib/DisplayEventHandler.h" -#include "RendererEventCollector.h" -#include "RendererLib/EKeyEventType.h" -#include "RendererLib/EKeyModifier.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -class ADisplayEventHandler : public ::testing::Test -{ -protected: - ADisplayEventHandler() - : m_displayHandle(5110u) - , m_displayEventHandler(m_displayHandle, m_eventCollector) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - } - - [[nodiscard]] const RendererEvent getRendererEvent(uint32_t index) const - { - const auto events = m_eventCollector.getRendererEvents(); - if (events.size() <= index) - { - return RendererEvent(ERendererEventType::Invalid); - } - return events[index]; - } - - RendererEventCollector m_eventCollector; - DisplayHandle m_displayHandle; - DisplayEventHandler m_displayEventHandler; -}; - -TEST_F(ADisplayEventHandler, createsRendererEventOnKeyPressEvent) -{ - uint32_t modifier = EKeyModifier_Alt | EKeyModifier_Shift; - EKeyCode keyCode = EKeyCode_4; - EKeyEventType keyEventType = EKeyEventType_Pressed; - m_displayEventHandler.onKeyEvent(keyEventType, modifier, keyCode); - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowKeyEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(keyEventType, event.keyEvent.type); - EXPECT_EQ(modifier, event.keyEvent.modifier); - EXPECT_EQ(keyCode, event.keyEvent.keyCode); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnKeyReleasedEvent) -{ - uint32_t modifier = EKeyModifier_Alt | EKeyModifier_Shift; - EKeyCode keyCode = EKeyCode_A; - EKeyEventType keyEventType = EKeyEventType_Released; - m_displayEventHandler.onKeyEvent(keyEventType, modifier, keyCode); - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowKeyEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(keyEventType, event.keyEvent.type); - EXPECT_EQ(modifier, event.keyEvent.modifier); - EXPECT_EQ(keyCode, event.keyEvent.keyCode); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnMouseMoveEvent) -{ - const glm::ivec2 mousePos(5, 20); - m_displayEventHandler.onMouseEvent(EMouseEventType_Move, mousePos.x, mousePos.y); - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(EMouseEventType_Move, event.mouseEvent.type); - EXPECT_EQ(mousePos, event.mouseEvent.pos); -} - -TEST_F(ADisplayEventHandler, createsRendererEventAndInvokesTouchHandlerOnMouseEvent) -{ - glm::ivec2 mousePosition(90u, 250u); - EMouseEventType mouseEventType = EMouseEventType_LeftButtonDown; - m_displayEventHandler.onMouseEvent(mouseEventType, mousePosition.x, mousePosition.y); - - // check renderer event collector - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(mouseEventType, event.mouseEvent.type); - EXPECT_EQ(mousePosition, event.mouseEvent.pos); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnMouseEventWindowEnter) -{ - m_displayEventHandler.onMouseEvent(EMouseEventType_WindowEnter, 5, 10); - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(EMouseEventType_WindowEnter, event.mouseEvent.type); - EXPECT_EQ(glm::ivec2(5, 10), event.mouseEvent.pos); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnMouseEventWindowLeave) -{ - m_displayEventHandler.onMouseEvent(EMouseEventType_WindowLeave, 10, 20); - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(EMouseEventType_WindowLeave, event.mouseEvent.type); - EXPECT_EQ(glm::ivec2(10, 20), event.mouseEvent.pos); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnWindowClosedEvent) -{ - m_displayEventHandler.onClose(); - - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowClosed, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnWindowResizeEvent) -{ - m_displayEventHandler.onResize(1280u, 480u); - - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowResizeEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(1280u, event.resizeEvent.width); - EXPECT_EQ(480u, event.resizeEvent.height); -} - -TEST_F(ADisplayEventHandler, createsRendererEventOnWindowMoveEvent) -{ - m_displayEventHandler.onWindowMove(1280, 480); - - const RendererEvent event = getRendererEvent(0u); - EXPECT_EQ(ERendererEventType::WindowMoveEvent, event.eventType); - EXPECT_EQ(m_displayHandle, event.displayHandle); - EXPECT_EQ(1280, event.moveEvent.posX); - EXPECT_EQ(480, event.moveEvent.posY); -} diff --git a/renderer/RendererLib/RendererLib/test/DisplaySetupTest.cpp b/renderer/RendererLib/RendererLib/test/DisplaySetupTest.cpp deleted file mode 100644 index 48d75f85d..000000000 --- a/renderer/RendererLib/RendererLib/test/DisplaySetupTest.cpp +++ /dev/null @@ -1,545 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/DisplaySetup.h" -#include "SceneAPI/RenderState.h" - -using namespace testing; -using namespace ramses_internal; - -class ADisplaySetup : public ::testing::Test -{ -protected: - void expectAssignedScenesInOrder(DeviceResourceHandle buffer, std::initializer_list assignedScenes) - { - for (const auto& mappedScene : assignedScenes) - EXPECT_EQ(buffer, displaySetup.findDisplayBufferSceneIsAssignedTo(mappedScene.sceneId)); - - const AssignedScenes& bufferScenes = displaySetup.getDisplayBuffer(buffer).scenes; - ASSERT_EQ(assignedScenes.size(), bufferScenes.size()); - const bool areEqual = std::equal(assignedScenes.begin(), assignedScenes.end(), bufferScenes.cbegin(), [](const AssignedSceneInfo& a, const AssignedSceneInfo& b) - { - return a.sceneId == b.sceneId - && a.globalSceneOrder == b.globalSceneOrder - && a.shown == b.shown; - }); - EXPECT_TRUE(areEqual); - } - - DisplaySetup displaySetup; - - const Viewport viewport{ 1,2,3,4 }; - const glm::vec4 clearColor{ 10,20,30,40 }; -}; - -TEST_F(ADisplaySetup, hasNoBuffersInitially) -{ - EXPECT_TRUE(displaySetup.getDisplayBuffers().empty()); - EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); - EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); -} - -TEST_F(ADisplaySetup, canRegisterFramebufferInfoWithInitialValues) -{ - const DeviceResourceHandle bufferHandle(33u); - displaySetup.registerDisplayBuffer(bufferHandle, viewport, clearColor, false, false); - - const auto& bufferInfo = displaySetup.getDisplayBuffer(bufferHandle); - EXPECT_EQ(viewport, bufferInfo.viewport); - EXPECT_EQ(clearColor, bufferInfo.clearColor); - EXPECT_TRUE(bufferInfo.scenes.empty()); - EXPECT_FALSE(bufferInfo.isInterruptible); - EXPECT_TRUE(bufferInfo.needsRerender); - - EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); - EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); -} - -TEST_F(ADisplaySetup, canRegisterNonInterruptibleOBInfoWithInitialValues) -{ - const DeviceResourceHandle bufferHandle(33u); - displaySetup.registerDisplayBuffer(bufferHandle, viewport, clearColor, true, false); - - const auto& bufferInfo = displaySetup.getDisplayBuffer(bufferHandle); - EXPECT_EQ(viewport, bufferInfo.viewport); - EXPECT_EQ(clearColor, bufferInfo.clearColor); - EXPECT_TRUE(bufferInfo.scenes.empty()); - EXPECT_FALSE(bufferInfo.isInterruptible); - EXPECT_TRUE(bufferInfo.needsRerender); - - EXPECT_EQ(DeviceHandleVector{ bufferHandle }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - - EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); -} - -TEST_F(ADisplaySetup, canRegisterInterruptibleOBInfoWithInitialValues) -{ - const DeviceResourceHandle bufferHandle(33u); - displaySetup.registerDisplayBuffer(bufferHandle, viewport, clearColor, true, true); - - const auto& bufferInfo = displaySetup.getDisplayBuffer(bufferHandle); - EXPECT_EQ(viewport, bufferInfo.viewport); - EXPECT_EQ(clearColor, bufferInfo.clearColor); - EXPECT_TRUE(bufferInfo.scenes.empty()); - EXPECT_TRUE(bufferInfo.isInterruptible); - EXPECT_TRUE(bufferInfo.needsRerender); - - EXPECT_EQ(DeviceHandleVector{ bufferHandle }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); - - EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); -} - -TEST_F(ADisplaySetup, canBeQueriedForAnyRegisteredTypeOfBufferViaItsHandle) -{ - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleFB).isOffscreenBuffer); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).isOffscreenBuffer); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).isInterruptible); -} - -TEST_F(ADisplaySetup, canUnregisterOB) -{ - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - displaySetup.unregisterDisplayBuffer(bufferHandleOB); - displaySetup.unregisterDisplayBuffer(bufferHandleOBint); - - EXPECT_TRUE(displaySetup.getDisplayBuffers().empty()); - EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); - EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); -} - -TEST_F(ADisplaySetup, marksNewlyRegisteredBuffersToRerender) -{ - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} - -TEST_F(ADisplaySetup, canSetRerenderFlagOfABuffer) -{ - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - - EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); - EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); - - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, true); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, true); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, true); - - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} - -TEST_F(ADisplaySetup, canMapAndUnmapSceneToBuffer) -{ - const SceneId scene1(12u); - const SceneId scene2(13u); - const SceneId scene3(14u); - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 0); - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 0); - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 0); - - expectAssignedScenesInOrder(bufferHandleFB, { { scene1, 0, false } }); - expectAssignedScenesInOrder(bufferHandleOB, { { scene2, 0, false } }); - expectAssignedScenesInOrder(bufferHandleOBint, { { scene3, 0, false } }); - - displaySetup.unassignScene(scene1); - displaySetup.unassignScene(scene2); - displaySetup.unassignScene(scene3); - - expectAssignedScenesInOrder(bufferHandleFB, {}); - expectAssignedScenesInOrder(bufferHandleOB, {}); - expectAssignedScenesInOrder(bufferHandleOBint, {}); - EXPECT_FALSE(displaySetup.findDisplayBufferSceneIsAssignedTo(scene1).isValid()); - EXPECT_FALSE(displaySetup.findDisplayBufferSceneIsAssignedTo(scene2).isValid()); - EXPECT_FALSE(displaySetup.findDisplayBufferSceneIsAssignedTo(scene3).isValid()); -} - -TEST_F(ADisplaySetup, storesAssignedScenesInRenderingOrder) -{ - const SceneId scene1(12u); - const SceneId scene2(13u); - const SceneId scene3(14u); - const DeviceResourceHandle bufferHandleOB(34u); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 10); - expectAssignedScenesInOrder(bufferHandleOB, { { scene2, 10, false } }); - - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOB, -10); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene2, 10, false } }); - - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOB, 0); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, false },{ scene2, 10, false } }); - - displaySetup.unassignScene(scene1); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene2, 10, false } }); - - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOB, 666); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene2, 10, false },{ scene1, 666, false } }); - - displaySetup.unassignScene(scene2); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 666, false } }); - - displaySetup.unassignScene(scene3); - expectAssignedScenesInOrder(bufferHandleOB, { { scene1, 666, false } }); - - displaySetup.unassignScene(scene1); - expectAssignedScenesInOrder(bufferHandleOB, {}); -} - -TEST_F(ADisplaySetup, triggersRerenderForBufferWithMappedOrUnmappedScene) -{ - const SceneId scene1(12u); - const SceneId scene2(13u); - const SceneId scene3(14u); - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - // reset re-render state - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // trigger re-render by mapping - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 0); - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 0); - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 0); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); - - // reset re-render state - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // trigger re-render by unmapping - displaySetup.unassignScene(scene1); - displaySetup.unassignScene(scene2); - displaySetup.unassignScene(scene3); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} - -TEST_F(ADisplaySetup, canSetSceneShown) -{ - const SceneId scene1(12u); - const SceneId scene2(13u); - const SceneId scene3(14u); - const DeviceResourceHandle bufferHandleOB(34u); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOB, 0); - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 10); - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOB, -10); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, false },{ scene2, 10, false } }); - - displaySetup.setSceneShown(scene1, true); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, true },{ scene2, 10, false } }); - displaySetup.setSceneShown(scene2, true); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, true },{ scene2, 10, true } }); - displaySetup.setSceneShown(scene3, true); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, true },{ scene1, 0, true },{ scene2, 10, true } }); - - displaySetup.setSceneShown(scene1, false); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, true },{ scene1, 0, false },{ scene2, 10, true } }); - displaySetup.setSceneShown(scene2, false); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, true },{ scene1, 0, false },{ scene2, 10, false } }); - displaySetup.setSceneShown(scene3, false); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, false },{ scene2, 10, false } }); -} - -TEST_F(ADisplaySetup, triggersRerenderForBufferWithChangedSceneShowState) -{ - const SceneId scene1(12u); - const SceneId scene2(13u); - const SceneId scene3(14u); - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 0); - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 0); - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 0); - - // reset re-render state - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // trigger re-render by scene showing - displaySetup.setSceneShown(scene1, true); - displaySetup.setSceneShown(scene2, true); - displaySetup.setSceneShown(scene3, true); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); - - // reset re-render state - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // trigger re-render by scene hiding - displaySetup.setSceneShown(scene1, false); - displaySetup.setSceneShown(scene2, false); - displaySetup.setSceneShown(scene3, false); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} - -TEST_F(ADisplaySetup, canSetClearFlagsForAnyRegisteredBuffer) -{ - constexpr DeviceResourceHandle bufferHandleFB{ 33u }; - constexpr DeviceResourceHandle bufferHandleOB{ 34u }; - constexpr DeviceResourceHandle bufferHandleOBint{ 35u }; - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - EXPECT_EQ(EClearFlags_All, displaySetup.getDisplayBuffer(bufferHandleFB).clearFlags); - EXPECT_EQ(EClearFlags_All, displaySetup.getDisplayBuffer(bufferHandleOB).clearFlags); - EXPECT_EQ(EClearFlags_All, displaySetup.getDisplayBuffer(bufferHandleOBint).clearFlags); - - displaySetup.setClearFlags(bufferHandleFB, EClearFlags_None); - displaySetup.setClearFlags(bufferHandleOB, EClearFlags_Color); - displaySetup.setClearFlags(bufferHandleOBint, EClearFlags_All); - - EXPECT_EQ(EClearFlags_None, displaySetup.getDisplayBuffer(bufferHandleFB).clearFlags); - EXPECT_EQ(EClearFlags_Color, displaySetup.getDisplayBuffer(bufferHandleOB).clearFlags); - EXPECT_EQ(EClearFlags_All, displaySetup.getDisplayBuffer(bufferHandleOBint).clearFlags); -} - -TEST_F(ADisplaySetup, canSetClearColorForAnyRegisteredBuffer) -{ - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - EXPECT_EQ(clearColor, displaySetup.getDisplayBuffer(bufferHandleFB).clearColor); - EXPECT_EQ(clearColor, displaySetup.getDisplayBuffer(bufferHandleOB).clearColor); - EXPECT_EQ(clearColor, displaySetup.getDisplayBuffer(bufferHandleOBint).clearColor); - - const glm::vec4 clr1{ 9, 8, 7, 6 }; - const glm::vec4 clr2{ 5, 4, 3, 2 }; - const glm::vec4 clr3{ 1, 0, -1, -2 }; - displaySetup.setClearColor(bufferHandleFB, clr1); - displaySetup.setClearColor(bufferHandleOB, clr2); - displaySetup.setClearColor(bufferHandleOBint, clr3); - - EXPECT_EQ(clr1, displaySetup.getDisplayBuffer(bufferHandleFB).clearColor); - EXPECT_EQ(clr2, displaySetup.getDisplayBuffer(bufferHandleOB).clearColor); - EXPECT_EQ(clr3, displaySetup.getDisplayBuffer(bufferHandleOBint).clearColor); -} - -TEST_F(ADisplaySetup, triggersRerenderForBufferWithChangedClearColor) -{ - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - // reset re-render state - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // trigger re-render of all buffers by setting clear color for a single buffer - displaySetup.setClearColor(bufferHandleOB, clearColor); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} - -TEST_F(ADisplaySetup, canResizeDisplayBuffer) -{ - const DeviceResourceHandle bufferHandleFB(33u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - - EXPECT_EQ(viewport.width, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.width); - EXPECT_EQ(viewport.height, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.height); - - displaySetup.setDisplayBufferSize(bufferHandleFB, 100u, 999u); - EXPECT_EQ(100u, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.width); - EXPECT_EQ(999u, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.height); -} - -TEST_F(ADisplaySetup, triggersRerenderForBufferWithResizedDisplayBuffer) -{ - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - // reset re-render state - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // trigger re-render of all buffers by resizing any display buffer - displaySetup.setDisplayBufferSize(bufferHandleOB, 1u, 2u); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} - -TEST_F(ADisplaySetup, getsListOfInterruptibleBuffersToRerenderStartingFromInterruptedBuffer) -{ - const DeviceResourceHandle bufferHandle1(33u); - const DeviceResourceHandle bufferHandle2(34u); - const DeviceResourceHandle bufferHandle3(35u); - displaySetup.registerDisplayBuffer(bufferHandle1, viewport, clearColor, true, true); - displaySetup.registerDisplayBuffer(bufferHandle2, viewport, clearColor, true, true); - displaySetup.registerDisplayBuffer(bufferHandle3, viewport, clearColor, true, true); - - const DeviceHandleVector expectedBuffersToRerender{ bufferHandle2, bufferHandle3 }; - EXPECT_EQ(expectedBuffersToRerender, displaySetup.getInterruptibleOffscreenBuffersToRender(bufferHandle2)); -} - -TEST_F(ADisplaySetup, getsListOfInterruptibleBuffersToRerenderStartingFromInterruptedBufferAndOnlyThoseMarkedToRerender) -{ - const DeviceResourceHandle bufferHandle1(33u); - const DeviceResourceHandle bufferHandle2(34u); - const DeviceResourceHandle bufferHandle3(35u); - const DeviceResourceHandle bufferHandle4(36u); - const DeviceResourceHandle bufferHandle5(37u); - displaySetup.registerDisplayBuffer(bufferHandle1, viewport, clearColor, true, true); - displaySetup.registerDisplayBuffer(bufferHandle2, viewport, clearColor, true, true); - displaySetup.registerDisplayBuffer(bufferHandle3, viewport, clearColor, true, true); - displaySetup.registerDisplayBuffer(bufferHandle4, viewport, clearColor, true, true); - displaySetup.registerDisplayBuffer(bufferHandle5, viewport, clearColor, true, true); - - displaySetup.setDisplayBufferToBeRerendered(bufferHandle2, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandle4, false); - - // the interrupted buffer (2) is always put to list regardless of its re-render flag, - // buffer1 is skipped because it is before interrupted buffer, - // buffer4 is skipped because it is not marked to re-render - const DeviceHandleVector expectedBuffersToRerender{ bufferHandle2, bufferHandle3, bufferHandle5 }; - EXPECT_EQ(expectedBuffersToRerender, displaySetup.getInterruptibleOffscreenBuffersToRender(bufferHandle2)); -} - -TEST_F(ADisplaySetup, canAssignAnAlreadyAssignedSceneToAnotherBufferAndPreserveShowState) -{ - const SceneId scene1(12u); - const SceneId scene2(13u); - const SceneId scene3(14u); - const DeviceResourceHandle bufferHandleFB(33u); - const DeviceResourceHandle bufferHandleOB(34u); - const DeviceResourceHandle bufferHandleOBint(35u); - displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); - displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); - displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); - - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 1); - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 2); - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 3); - - displaySetup.setSceneShown(scene1, true); - displaySetup.setSceneShown(scene2, false); - displaySetup.setSceneShown(scene3, true); - - expectAssignedScenesInOrder(bufferHandleFB, { { scene1, 1, true } }); - expectAssignedScenesInOrder(bufferHandleOB, { { scene2, 2, false } }); - expectAssignedScenesInOrder(bufferHandleOBint, { { scene3, 3, true } }); - - // reset re-render states - displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); - displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); - - // change scenes assignments - displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleFB, 1); - displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOB, 2); - displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOBint, 3); - - expectAssignedScenesInOrder(bufferHandleFB, { { scene2, 1, false } }); - expectAssignedScenesInOrder(bufferHandleOB, { { scene3, 2, true } }); - expectAssignedScenesInOrder(bufferHandleOBint, { { scene1, 3, true } }); - - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); - EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); - EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); -} diff --git a/renderer/RendererLib/RendererLib/test/EmbeddedCompositingManagerTest.cpp b/renderer/RendererLib/RendererLib/test/EmbeddedCompositingManagerTest.cpp deleted file mode 100644 index 8f8bebe4b..000000000 --- a/renderer/RendererLib/RendererLib/test/EmbeddedCompositingManagerTest.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/EmbeddedCompositingManager.h" -#include "DeviceMock.h" -#include "EmbeddedCompositorMock.h" -#include "Platform_Base/TextureUploadingAdapter_Base.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -class AnEmbeddedCompositingManager : public ::testing::Test -{ -public: - AnEmbeddedCompositingManager() - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - } - - void expectStreamTexUpload(WaylandIviSurfaceId sourceId, DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle) - { - EXPECT_CALL(deviceMock, uploadStreamTexture2D(_, _, _, _, _, _)).WillOnce(Return(textureDeviceHandle)); - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(sourceId)).WillOnce(Return(false)); - } - - void expectStreamTexUnload(DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle) - { - EXPECT_CALL(deviceMock, deleteTexture(textureDeviceHandle)); - } - - void addStreamReference(WaylandIviSurfaceId sourceId, DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle, bool expectTextureUpload = true) - { - if (expectTextureUpload) - expectStreamTexUpload(sourceId, textureDeviceHandle); - embeddedCompositingManager.refStream(sourceId); - } - - void removeStreamReference(WaylandIviSurfaceId sourceId, DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle, bool expectTextureUnload = true) - { - if (expectTextureUnload) - expectStreamTexUnload(textureDeviceHandle); - embeddedCompositingManager.unrefStream(sourceId); - } - - void expectStreamTextureChangedState(const WaylandIviSurfaceIdVector& expectedStreamsWithStateChange, const WaylandIviSurfaceIdVector& expectedNewStreams, const WaylandIviSurfaceIdVector& expectedObsoleteStreams) - { - WaylandIviSurfaceIdVector streamsWithStateChange; - WaylandIviSurfaceIdVector newStreams; - WaylandIviSurfaceIdVector obsoleteStreams; - embeddedCompositingManager.dispatchStateChangesOfSources(streamsWithStateChange, newStreams, obsoleteStreams); - - EXPECT_EQ(expectedStreamsWithStateChange, streamsWithStateChange); - EXPECT_EQ(expectedNewStreams, newStreams); - EXPECT_EQ(expectedObsoleteStreams, obsoleteStreams); - } - -protected: - StrictMock deviceMock; - StrictMock embeddedCompositorMock; - TextureUploadingAdapter_Base textureUploadingAdapter = TextureUploadingAdapter_Base(deviceMock); - EmbeddedCompositingManager embeddedCompositingManager = EmbeddedCompositingManager(deviceMock, embeddedCompositorMock, textureUploadingAdapter); - const WaylandIviSurfaceId streamTextureSourceId = WaylandIviSurfaceId(11u); - const WaylandIviSurfaceId streamTextureSourceId2 = WaylandIviSurfaceId(12u); - - static constexpr DeviceResourceHandle compositedTextureDeviceHandle{ 111u }; -}; - -TEST_F(AnEmbeddedCompositingManager, CanUploadAndDeleteStreamTexture) -{ - addStreamReference(streamTextureSourceId); - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); - - removeStreamReference(streamTextureSourceId); -} - -TEST_F(AnEmbeddedCompositingManager, ReturnsInvalidDeviceResourceHandleForInvalidStreamSourceId) -{ - const WaylandIviSurfaceId fakeId(234); - - //simulate compositing content is not available - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(fakeId)).WillOnce(Return(false)); - EXPECT_EQ(DeviceResourceHandle::Invalid(), embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(fakeId)); -} - -TEST_F(AnEmbeddedCompositingManager, CanGetCorrectDeviceHandleForValidStreamTextureId) -{ - addStreamReference(streamTextureSourceId); - - //simulate compositing content is available - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - //expect composited texture - EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); - - //simulate compositing content is NOT available - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(false)); - //expect invalid texture handle - EXPECT_EQ(DeviceResourceHandle::Invalid(), embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); - - removeStreamReference(streamTextureSourceId); -} - -TEST_F(AnEmbeddedCompositingManager, CanUpdateCompositingResources) -{ - const WaylandIviSurfaceIdSet fakeUpdatedStreamTextureSourceIds{ streamTextureSourceId, streamTextureSourceId2 }; - - addStreamReference(streamTextureSourceId); - addStreamReference(streamTextureSourceId2); - - { - InSequence s; - EXPECT_CALL(embeddedCompositorMock, handleRequestsFromClients()); - EXPECT_CALL(embeddedCompositorMock, endFrame(false)); - embeddedCompositingManager.processClientRequests(); - - EXPECT_CALL(embeddedCompositorMock, hasUpdatedStreamTextureSources()).WillOnce(Return(true)); - EXPECT_TRUE(embeddedCompositingManager.hasUpdatedContentFromStreamSourcesToUpload()); - - EXPECT_CALL(embeddedCompositorMock, dispatchUpdatedStreamTextureSourceIds()).WillOnce(Return(fakeUpdatedStreamTextureSourceIds)); - } - - // these expectations don't necessarily come in same order - EXPECT_CALL(embeddedCompositorMock, uploadCompositingContentForStreamTexture(streamTextureSourceId, _, _)).WillOnce(Return(13u)); - EXPECT_CALL(embeddedCompositorMock, uploadCompositingContentForStreamTexture(streamTextureSourceId2, _, _)).WillOnce(Return(6u)); - StreamSourceUpdates updates; - embeddedCompositingManager.uploadResourcesAndGetUpdates(updates); - ASSERT_EQ(2u, updates.size()); - int idx1 = 0; - int idx2 = 1; - // updates might come in different order - if (updates.front().first != streamTextureSourceId) - std::swap(idx1, idx2); - EXPECT_EQ(streamTextureSourceId, updates[idx1].first); - EXPECT_EQ(13u, updates[idx1].second); - EXPECT_EQ(streamTextureSourceId2, updates[idx2].first); - EXPECT_EQ(6u, updates[idx2].second); -} - -TEST_F(AnEmbeddedCompositingManager, CanNotifyClients) -{ - InSequence s; - EXPECT_CALL(embeddedCompositorMock, endFrame(true)); - embeddedCompositingManager.notifyClients(); -} - -TEST_F(AnEmbeddedCompositingManager, UploadsOneCompositedTextureForTwoEqualStreamTexturesSourceIds) -{ - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); - - //make sure it returns the composited texture device handle for the stream textures id - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillRepeatedly(Return(true)); - EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); -} - -TEST_F(AnEmbeddedCompositingManager, UploadsTwoCompositedTextureForTwoDifferentStreamTextureSourceIds) -{ - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); - - const WaylandIviSurfaceId secondStreamTextureSourceId(22); - const DeviceResourceHandle secondCompositedTextureDeviceHandle(222); - addStreamReference(secondStreamTextureSourceId, secondCompositedTextureDeviceHandle); - - //make sure it returns a different composited texture device handle for each stream texture source id - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); - - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(secondStreamTextureSourceId)).WillOnce(Return(true)); - EXPECT_EQ(secondCompositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(secondStreamTextureSourceId)); -} - -TEST_F(AnEmbeddedCompositingManager, DeletesCompositedTextureOnlyAfterAllReferencesRemoved) -{ - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); - - removeStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); - removeStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); -} - -TEST_F(AnEmbeddedCompositingManager, CanUploadAndDeleteStreamTextureWithAvailableContent) -{ - EXPECT_CALL(deviceMock, uploadStreamTexture2D(_, _, _, _, _, _)).WillOnce(Return(compositedTextureDeviceHandle)); - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - EXPECT_CALL(embeddedCompositorMock, uploadCompositingContentForStreamTexture(streamTextureSourceId, _, _)); - - embeddedCompositingManager.refStream(streamTextureSourceId); - - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); - - removeStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); -} - -TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_NoChange) -{ - const WaylandIviSurfaceIdSet empty; - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(empty)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); - - expectStreamTextureChangedState({}, {}, {}); -} - -TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_IfStreamSourceBecomesAvailable) -{ - const WaylandIviSurfaceIdSet empty; - const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); - - expectStreamTextureChangedState({}, { streamTextureSourceId }, {}); -} - -TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_EmptyAfterDispatching) -{ - const WaylandIviSurfaceIdSet empty; - const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); - expectStreamTextureChangedState({}, { streamTextureSourceId }, {}); - - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(empty)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); - expectStreamTextureChangedState({}, {}, {}); -} - -TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_IfStreamSourceBecomesUnavailable) -{ - const WaylandIviSurfaceIdSet empty; - const WaylandIviSurfaceIdSet obsoleteStreams{ streamTextureSourceId }; - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(empty)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(obsoleteStreams)); - - expectStreamTextureChangedState({}, {}, { streamTextureSourceId }); -} - -TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_multipleRefs) -{ - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); - - const WaylandIviSurfaceIdSet empty; - const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - - expectStreamTextureChangedState({ streamTextureSourceId }, { streamTextureSourceId }, {}); -} - -TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_multipleSources) -{ - addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); - addStreamReference(streamTextureSourceId2, compositedTextureDeviceHandle); - - const WaylandIviSurfaceIdSet empty; - const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; - EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); - EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); - EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId2)).WillOnce(Return(true)); - - // order changed due to hashmap, not important for test - expectStreamTextureChangedState({ streamTextureSourceId2, streamTextureSourceId }, { streamTextureSourceId }, {}); -} diff --git a/renderer/RendererLib/RendererLib/test/IntersectionUtilsTest.cpp b/renderer/RendererLib/RendererLib/test/IntersectionUtilsTest.cpp deleted file mode 100644 index bdc3806e1..000000000 --- a/renderer/RendererLib/RendererLib/test/IntersectionUtilsTest.cpp +++ /dev/null @@ -1,580 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2019 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gtest/gtest.h" -#include "RendererLib/IntersectionUtils.h" -#include "RendererEventCollector.h" -#include "RendererLib/RendererScenes.h" -#include "SceneAllocateHelper.h" -#include "Math3d/ProjectionParams.h" -#include "Math3d/CameraMatrixHelper.h" -#include "glm/gtx/transform.hpp" - -using namespace ramses_internal; - -static DataBufferHandle prepareGeometryBuffer(TransformationLinkCachedScene& scene, SceneAllocateHelper& sceneAllocator, const float vertexPositionsTriangle[], const uint32_t bufferSize) -{ - DataBufferHandle geometryBuffer = sceneAllocator.allocateDataBuffer(EDataBufferType::VertexBuffer, EDataType::Vector3F, bufferSize); - const Byte* data = reinterpret_cast(vertexPositionsTriangle); - scene.updateDataBuffer(geometryBuffer, 0, bufferSize, data); - return geometryBuffer; -} - -static CameraHandle preparePickableCamera(TransformationLinkCachedScene& scene, SceneAllocateHelper& sceneAllocator, const glm::ivec2 viewportOffset, const glm::ivec2 viewportSize, const glm::vec3 translation, const glm::vec3 rotation, const glm::vec3 scale) -{ - NodeHandle cameraNodeHandle = sceneAllocator.allocateNode(); - const auto dataLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference} }, {}); - const auto dataInstance = sceneAllocator.allocateDataInstance(dataLayout); - const auto vpDataRefLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I} }, {}); - const auto vpOffsetInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); - const auto vpSizeInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); - const auto frustumPlanesLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector4F} }, {}); - const auto frustumPlanes = sceneAllocator.allocateDataInstance(frustumPlanesLayout); - const auto frustumNearFarLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2F} }, {}); - const auto frustumNearFar = sceneAllocator.allocateDataInstance(frustumNearFarLayout); - scene.setDataReference(dataInstance, Camera::ViewportOffsetField, vpOffsetInstance); - scene.setDataReference(dataInstance, Camera::ViewportSizeField, vpSizeInstance); - scene.setDataReference(dataInstance, Camera::FrustumPlanesField, frustumPlanes); - scene.setDataReference(dataInstance, Camera::FrustumNearFarPlanesField, frustumNearFar); - const CameraHandle cameraHandle = sceneAllocator.allocateCamera(ECameraProjectionType::Perspective, cameraNodeHandle, dataInstance); - scene.setDataSingleVector2i(vpOffsetInstance, DataFieldHandle{ 0 }, viewportOffset); - scene.setDataSingleVector2i(vpSizeInstance, DataFieldHandle{ 0 }, viewportSize); - const ProjectionParams params = ramses_internal::ProjectionParams::Perspective(19.f, static_cast(viewportSize.x) / static_cast(viewportSize.y), 0.1f, 100.f); - scene.setDataSingleVector4f(frustumPlanes, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); - scene.setDataSingleVector2f(frustumNearFar, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); - - TransformHandle cameraTransformation = sceneAllocator.allocateTransform(cameraNodeHandle); - scene.setTranslation(cameraTransformation, translation); - scene.setRotation(cameraTransformation, glm::vec4(rotation, 1.f), ERotationType::Euler_XYZ); - scene.setScaling(cameraTransformation, scale); - return cameraHandle; -} - -static void preparePickableObject(TransformationLinkCachedScene& scene, SceneAllocateHelper& sceneAllocator, DataBufferHandle geometryBuffer, CameraHandle cameraHandle, PickableObjectId pickableId, const glm::vec3 translation, const glm::vec3 rotation, const glm::vec3 scale) -{ - const NodeHandle pickableNodeHandle = sceneAllocator.allocateNode(); - const PickableObjectHandle pickableHandle = sceneAllocator.allocatePickableObject(geometryBuffer, pickableNodeHandle, pickableId); - scene.setPickableObjectCamera(pickableHandle, cameraHandle); - TransformHandle pickableTransformation = sceneAllocator.allocateTransform(pickableNodeHandle); - scene.setTranslation(pickableTransformation, translation); - scene.setRotation(pickableTransformation, glm::vec4(rotation, 1.f), ERotationType::Euler_XYZ); - scene.setScaling(pickableTransformation, scale); -} - -static void checkSceneForIntersectedPickableObjects(const TransformationLinkCachedScene& scene, const glm::vec2 coords, const glm::ivec2 dispResolution, const PickableObjectIds& expectedPickables) -{ - const int32_t xCoordDisplaySpace = static_cast(std::lroundf((coords.x + 1.f) * dispResolution.x / 2.f)); - const int32_t yCoordDisplaySpace = static_cast(std::lroundf((coords.y + 1.f) * dispResolution.y / 2.f)); - - PickableObjectIds resultPickables; - IntersectionUtils::CheckSceneForIntersectedPickableObjects(scene, {xCoordDisplaySpace, yCoordDisplaySpace}, resultPickables); - - EXPECT_EQ(resultPickables, expectedPickables); -} - -TEST(IntersectionUtilsTest, canIntersectTriangle1) -{ - const glm::vec3 rayOrig(0, 0, 0); - const glm::vec3 v0(0, 0, 5); - const glm::vec3 v1(1, 0, 5); - const glm::vec3 v2(0, 1, 5); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayHit(0, 0, 1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayHit, intersectionPoint, distance); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(5.f, distance); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); - EXPECT_FLOAT_EQ(5.f, intersectionPoint.z); -} - -TEST(IntersectionUtilsTest, canIntersectTriangle2) -{ - const glm::vec3 rayOrig(1, 1, 1); - const glm::vec3 v0(1, 1, 5); - const glm::vec3 v1(2, 1, 5); - const glm::vec3 v2(1, 2, 5); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayHit(0, 0, 1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayHit, intersectionPoint, distance); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(4.f, distance); - EXPECT_FLOAT_EQ(1.f, intersectionPoint.x); - EXPECT_FLOAT_EQ(1.f, intersectionPoint.y); - EXPECT_FLOAT_EQ(5.f, intersectionPoint.z); -} - -TEST(IntersectionUtilsTest, noIntersectRayNotPointingToTriangle) -{ - const glm::vec3 rayOrig(0, 0, 0); - const glm::vec3 v0(0, 0, 5); - const glm::vec3 v1(1, 0, 5); - const glm::vec3 v2(0, 1, 5); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayMiss(0, 20, 1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, glm::normalize(rayMiss), intersectionPoint, distance); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, noIntersectRayPrallelToTriangle) -{ - const glm::vec3 rayOrig(0, 0, 0); - const glm::vec3 v0(0, 0, 5); - const glm::vec3 v1(1, 0, 5); - const glm::vec3 v2(0, 1, 5); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayParallel(0, 1, 0); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayParallel, intersectionPoint, distance); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay1) -{ - const glm::vec3 rayOrig(0, 0, 1); - const glm::vec3 v0(0, 0, 0); - const glm::vec3 v1(1, 0, 0); - const glm::vec3 v2(0, 1, 0); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayDir(0, 0, 1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay2) -{ - const glm::vec3 rayOrig(0, 0, 1); - const glm::vec3 v0(0, 0, 0); - const glm::vec3 v1(1, 0, 0); - const glm::vec3 v2(0, 1, 0); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayDir(0, 0, -1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(1.f, distance); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.z); -} - -TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay3) -{ - const glm::vec3 rayOrig(0, 0, -1); - const glm::vec3 v0(0, 0, 0); - const glm::vec3 v1(1, 0, 0); - const glm::vec3 v2(0, 1, 0); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayDir(0, 0, 1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(1.f, distance); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.z); -} - -TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay4) -{ - const glm::vec3 rayOrig(0, 0, -1); - const glm::vec3 v0(0, 0, 0); - const glm::vec3 v1(1, 0, 0); - const glm::vec3 v2(0, 1, 0); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayDir(0, 0, -1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay5) -{ - const glm::vec3 rayOrig(1, 1, 3); - const glm::vec3 v0(1, 1, 1); - const glm::vec3 v1(2, 1, 1); - const glm::vec3 v2(1, 2, 1); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - const glm::vec3 rayDir(0, 0, 1); - glm::vec3 intersectionPoint; - float distance; - bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, canCalculateNormalOfPlane) -{ - const glm::vec3 v0(0, 0, 5); - const glm::vec3 v1(1, 0, 5); - const glm::vec3 v2(0, 1, 5); - const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; - glm::vec3 normal = IntersectionUtils::CalculatePlaneNormal(triangle); - glm::vec3 expectedNormal(0.0f, 0.0f, 1.0f); - - for (uint32_t i = 0; i < 3; ++i) - { - EXPECT_FLOAT_EQ(normal[i], expectedNormal[i]); - } -} - -TEST(IntersectionUtilsTest, canPickSingleTriangle) -{ - const std::vector triangleData{ -1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f }; - - const auto modelMatrix = glm::identity(); - const auto cameraTransformationMatrix = glm::translate(glm::vec3{ 0.0f, 0.0f, 1.0f }); - const auto viewMatrix = glm::inverse(cameraTransformationMatrix); - const auto projectionMatrix = glm::identity(); - - const glm::vec2 ndsPickCoords(0.0f, 0.0f); - - glm::vec3 intersectionPoint; - bool intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangleData.data(), triangleData.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(intersectionPoint.x, 0.f); - EXPECT_FLOAT_EQ(intersectionPoint.y, 0.f); - EXPECT_FLOAT_EQ(intersectionPoint.z, 0.f); - - const auto scalingModelMatrix = glm::scale(glm::vec3{ 1.0f, 2.0f, 3.0f }); - - intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangleData.data(), triangleData.size(), scalingModelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(intersectionPoint.x, 0.f); - EXPECT_FLOAT_EQ(intersectionPoint.y, 0.f); - EXPECT_FLOAT_EQ(intersectionPoint.z, 0.f); -} - -TEST(IntersectionUtilsTest, noPickSingleTriangleTranslatedAwayFromPick) -{ - const std::vector triangle1Data{ -1.0f, 0.0f, 0.0f, - 1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f }; - - const auto modelMatrix = glm::translate(glm::vec3{ 0.0f, 1.0f, 0.0f }); - const auto cameraTransformationMatrix = glm::translate(glm::vec3{ 0.0f, 0.0f, 1.0f }); - const auto viewMatrix = glm::inverse(cameraTransformationMatrix); - const auto projectionMatrix = glm::identity(); - const glm::vec2 ndsPickCoords(0.0f, 0.0f); - - glm::vec3 intersectionPoint; - bool intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangle1Data.data(), triangle1Data.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - EXPECT_FALSE(intersectionHappened); - - const std::vector triangle2Data{-4.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 7.0f, 0.0f}; - - intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangle2Data.data(), triangle2Data.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, canIntersectMultipleTriangleGeometry) -{ - const std::vector triangle1Data{ 0.0f, -1.0f, 0.0f, - -1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f }; - const std::vector triangle2Data{ 0.0f, -1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 1.0f, 0.0f, 0.0f }; - //quad including origin - std::vector quad; - quad.insert(quad.end(), triangle1Data.begin(), triangle1Data.end()); - quad.insert(quad.end(), triangle2Data.begin(), triangle2Data.end()); - - const auto modelMatrix = glm::identity(); - const auto cameraTransformationMatrix = glm::translate(glm::vec3{0.0f, 0.0f, 1.0f}); - const auto viewMatrix = glm::inverse(cameraTransformationMatrix); - const auto projectionMatrix = glm::identity(); - const glm::vec2 ndsPickCoords(0.0f, 0.0f); - - glm::vec3 intersectionPoint; - bool intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, quad.data(), quad.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - EXPECT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(intersectionPoint.x, 0.f); - EXPECT_FLOAT_EQ(intersectionPoint.y, 0.f); - EXPECT_FLOAT_EQ(intersectionPoint.z, 0.f); -} - -TEST(IntersectionUtilsTest, noIntersectPickNotPointingToMultipleTriangles) -{ - const std::vector triangle1Data{ -2.0f, 0.0f, 0.0f, - -2.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f }; - const std::vector triangle2Data{ -1.0f, 0.0f, 0.0f, - -2.0f, 0.0f, 0.0f, - -1.0f, 1.0f, 0.0f }; - // quad not including origin - std::vector quad; - quad.insert(quad.end(), triangle1Data.begin(), triangle1Data.end()); - quad.insert(quad.end(), triangle2Data.begin(), triangle2Data.end()); - - const auto modelMatrix = glm::identity(); - const auto cameraTransformationMatrix = glm::translate(glm::vec3{0.0f, 0.0f, 1.0f}); - const auto viewMatrix = glm::inverse(cameraTransformationMatrix); - const auto projectionMatrix = glm::identity(); - const glm::vec2 ndsPickCoords(0.0f, 0.0f); - - glm::vec3 intersectionPoint; - bool intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, quad.data(), quad.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - EXPECT_FALSE(intersectionHappened); -} - -TEST(IntersectionUtilsTest, findsPickedObjectInSceneWhenIntersected) -{ - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes(rendererEventCollector); - TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); - SceneAllocateHelper sceneAllocator(scene); - float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; - const glm::ivec2 dispResolution = { 1280, 480 }; - - //Prepare PickableCamera - const CameraHandle cameraHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { -4.f, 0.f, 11.f }, { 0.f, -40.f, 0.f }, { 1.f, 1.f, 1.f }); - - //Prepare GeometryBuffer - DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); - - //Prepare PickableObject - PickableObjectId pickableId(341u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId, { 0.1f, 1.0f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 10.0f, 10.0f, 10.0f }); - - //Testing scene for intersection - const glm::vec2 coordsInViewportSpaceHit1 = {0.310937f, 0.354166f}; - const glm::vec2 coordsInViewportSpaceMiss1_1 = {0.309374f, 0.395833f}; - const glm::vec2 coordsInViewportSpaceMiss1_2 = {0.312500f, 0.312500f}; - - const glm::vec2 coordsInViewportSpaceHit2 = { -0.603125f, 0.945833f }; - const glm::vec2 coordsInViewportSpaceMiss2 = { -0.603125f, 0.987500f }; - - const glm::vec2 coordsInViewportSpaceHit3 = { -0.970313f, 0.533333f }; - const glm::vec2 coordsInViewportSpaceMiss3 = { -0.968750f, 0.495833f }; - - //Test first area of PickableObject - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHit1, dispResolution, { pickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1_1, dispResolution, {}); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1_2, dispResolution, {}); - - //Test second area of PickableObject - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHit2, dispResolution, { pickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss2, dispResolution, {}); - - //Test third area of PickableObject - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHit3, dispResolution, { pickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss3, dispResolution, {}); -} - -TEST(IntersectionUtilsTest, findsMultiplePickedObjectsNotOverlappingInSceneWhenIntersected) -{ - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes(rendererEventCollector); - TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); - SceneAllocateHelper sceneAllocator(scene); - float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; - const glm::ivec2 dispResolution = { 1280, 480 }; - - //Prepare PickableCamera - const CameraHandle cameraHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { -4.f, 0.f, 11.f }, { 0.f, -40.f, 0.f }, { 1.f, 1.f, 1.f }); - - //Prepare GeometryBuffer - DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); - - //Prepare PickableObject1 - PickableObjectId pickableId(341u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId, { 0.1f, 1.0f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); - - //Prepare PickableObject2 - PickableObjectId pickableId2(235u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId2, { 6.0f, 0.9f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); - - //Testing scene for intersection - const glm::vec2 coordsInViewportSpaceHitPickable1 = { -0.382812f, 0.441667f }; - const glm::vec2 coordsInViewportSpaceHitPickable2 = { -0.379687f, 0.395833f }; - const glm::vec2 coordsInViewportSpaceMiss1 = { -0.382812f, 0.466667f }; - const glm::vec2 coordsInViewportSpaceMiss2 = { -0.378125f, 0.425000f }; - const glm::vec2 coordsInViewportSpaceMiss3 = { -0.379687f, 0.383333f }; - - //Test PickableObjects - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable1, dispResolution, { pickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable2, dispResolution, { pickableId2 }); - - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1, dispResolution, {}); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss2, dispResolution, {}); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss3, dispResolution, {}); -} - -TEST(IntersectionUtilsTest, findsMultiplePickedObjectsOverlappingInSceneWhenIntersected) -{ - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes(rendererEventCollector); - TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); - SceneAllocateHelper sceneAllocator(scene); - float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; - const glm::ivec2 dispResolution = { 1280, 480 }; - - //Prepare PickableCamera - const CameraHandle cameraHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { -4.f, 0.f, 11.f }, { 0.f, -40.f, 0.f }, { 1.f, 1.f, 1.f }); - - //Prepare GeometryBuffer - DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); - - //Prepare PickableObject1 - PickableObjectId pickableId(341u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId, { 0.1f, 1.0f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); - - //Prepare PickableObject2 - PickableObjectId pickableId2(235u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId2, { 4.0f, 0.9f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); - - //Testing scene for intersection - const glm::vec2 coordsInViewportSpaceHitPickable1 = { -0.585938f, 0.575000f }; - const glm::vec2 coordsInViewportSpaceHitPickable2 = { -0.590625f, 0.458333f }; - const glm::vec2 coordsInViewportSpaceHitPickable1and2 = { -0.582812f, 0.512500f }; - const glm::vec2 coordsInViewportSpaceMiss1 = { -0.585938f, 0.595833f }; - const glm::vec2 coordsInViewportSpaceMiss2 = { -0.607812f, 0.408333f }; - - //Test PickableObjects - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable1, dispResolution, { pickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable2, dispResolution, { pickableId2 }); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable1and2, dispResolution, { pickableId, pickableId2 }); - - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1, dispResolution, {}); - checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss2, dispResolution, {}); -} - -TEST(IntersectionUtilsTest, picksIntersectionPointAtNearestTriangleInPickable) -{ - const std::vector trianglesVertices{ - -1.0f, 0.0f, -10.0f, - 1.0f, 0.0f, -10.0f, - 0.0f, 1.0f, -10.0f, - - -1.0f, 0.0f, -12.0f, - 1.0f, 0.0f, -12.0f, - 0.0f, 1.0f, -12.0f, - - 0.0f, -1.0f, -9.0f, - 1.0f, 0.0f, -9.0f, - 0.0f, 1.0f, -9.0f, - - 0.0f, -1.0f, -3.0f, - 1.0f, 0.0f, -3.0f, - 0.0f, 1.0f, -3.0f, - - 0.0f, -1.0f, -15.0f, - 1.0f, 0.0f, -15.0f, - 0.0f, 1.0f, -15.0f - }; - - const auto modelMatrix = glm::identity(); - const auto cameraTransformationMatrix = glm::translate(glm::vec3{ 0.0f, 0.0f, 1.0f }); - const auto viewMatrix = glm::inverse(cameraTransformationMatrix); - const auto projectionMatrix(CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(30.f, 1.f, 0.1f, 100.f))); - const glm::vec2 ndsPickCoords(0.0f, 0.0f); - - glm::vec3 intersectionPoint; - const bool intersectionHappened = - IntersectionUtils::TestGeometryPicked(ndsPickCoords, trianglesVertices.data(), trianglesVertices.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); - ASSERT_TRUE(intersectionHappened); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); - EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); - EXPECT_FLOAT_EQ(-3.f, intersectionPoint.z); -} - -TEST(IntersectionUtilsTest, canSortPickedPickablesWithDistanceWhenPickablesUseDifferentCameras) -{ - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes(rendererEventCollector); - TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); - SceneAllocateHelper sceneAllocator(scene); - float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; - const glm::ivec2 dispResolution = { 1280, 480 }; - - //Prepare PickableCamera - const CameraHandle cameraFrontPickableHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { 0.f, 0.f, 11.f }, { 0.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }); - const CameraHandle cameraBackPickableHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { 15.f, 0.f, 25.f }, { 0.f, 0.f, 0.f }, { 1.f, 5.f, .01f }); - - //Prepare GeometryBuffer - const DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); - - //Prepare pibckable that appears in the front - const PickableObjectId frontPickableId(341u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraFrontPickableHandle, frontPickableId, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 1.0f, 1.0f, 1.0f }); - - //Prepare pibckable that appears in the back (even though the distance between pickable and camera is 1.0, the camera is scaled big enough to make this pickable partially occluded by the former pickable) - const PickableObjectId backPickableId(235u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraBackPickableHandle, backPickableId, { 12.5f, 0.f, 24.0f }, { 0.0f, 0.0f, 0.0f }, { 10.0f, 10.0f, 1.0f }); - - //Testing scene for intersection - const glm::vec2 coordsInNDSHitFrontPickable = { -0.0625f, 0.16666666666f }; - const glm::vec2 coordsInNDSHitBackPickable = { -0.21875f, 0.02083333333f }; - const glm::vec2 coordsInNDSHitBothPickables = { -0.1875f, 0.02083333333f }; - - //Test PickableObjects - checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitFrontPickable, dispResolution, { frontPickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBackPickable, dispResolution, { backPickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBothPickables, dispResolution, {frontPickableId, backPickableId}); -} - -TEST(IntersectionUtilsTest, handlesPickableViewportCorrectly) -{ - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes(rendererEventCollector); - TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); - SceneAllocateHelper sceneAllocator(scene); - float vertexPositionsTriangle[] = { -1.f, -1.f, 0.f, 1.f, -1.f, 0.f, 0.f, 1.f, 0.f }; - const glm::ivec2 dispResolution = { 1280, 480 }; - - //Prepare two cameras with overlapping viewports - const glm::ivec2 viewportSize = { 1280 * 2 / 3, 480 * 2 / 3 }; - const glm::ivec2 viewportOffset1 = { 0, 0 }; - const glm::ivec2 viewportOffset2 = { 1280 / 3, 480 / 3 }; - const CameraHandle bottomLeftCamera = preparePickableCamera(scene, sceneAllocator, viewportOffset1, viewportSize, { 0.f, 0.f, 1.f }, { 0.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }); - const CameraHandle topRightCamera = preparePickableCamera(scene, sceneAllocator, viewportOffset2, viewportSize, { 0.f, 0.f, 1.01f }, { 0.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }); - - //Prepare GeometryBuffer - const DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); - - const PickableObjectId bottomLeftPickableId(341u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, bottomLeftCamera, bottomLeftPickableId, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 1.0f, 1.0f, 1.0f }); - - const PickableObjectId topRightPickableId(235u); - preparePickableObject(scene, sceneAllocator, geometryBuffer, topRightCamera, topRightPickableId, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 1.0f, 1.0f, 1.0f }); - - //Testing scene for intersection - const glm::vec2 coordsInNDSHitBottomLeftPickable = { -.5f, -.5f }; - const glm::vec2 coordsInNDSHitTopRightPickable = { .5f, .5f }; - const glm::vec2 coordsInNDSHitBothPickables = { 0.f, 0.f }; - const glm::vec2 coordsInNDSMissPickablesInBottomRight = { .5f, -.5f }; - const glm::vec2 coordsInNDSMissPickablesInTopLeft = { -.5f, .5f }; - - //Test PickableObjects - checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBottomLeftPickable, dispResolution, { bottomLeftPickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitTopRightPickable, dispResolution, { topRightPickableId }); - checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBothPickables, dispResolution, { bottomLeftPickableId, topRightPickableId }); - - checkSceneForIntersectedPickableObjects(scene, coordsInNDSMissPickablesInTopLeft, dispResolution, {}); - checkSceneForIntersectedPickableObjects(scene, coordsInNDSMissPickablesInBottomRight, dispResolution, {}); -} diff --git a/renderer/RendererLib/RendererLib/test/LinkManagerBaseTest.cpp b/renderer/RendererLib/RendererLib/test/LinkManagerBaseTest.cpp deleted file mode 100644 index 7cb8913a7..000000000 --- a/renderer/RendererLib/RendererLib/test/LinkManagerBaseTest.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/TransformationLinkManager.h" -#include "RendererLib/DataReferenceLinkManager.h" -#include "RendererLib/TransformationLinkCachedScene.h" -#include "RendererLib/TextureLinkManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" -#include "SceneLinksTestUtils.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -template -class ALinkManager : public ::testing::Test -{ -public: - ALinkManager() - : rendererScenes(rendererEventCollector) - , linkManager(rendererScenes) - , providerSceneId(3u) - , consumerSceneId(4u) - , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) - , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) - , providerSceneAllocator(providerScene) - , consumerSceneAllocator(consumerScene) - , providerSlotHandle(55u) - , consumerSlotHandle(66u) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - createDataSlot(providerSceneAllocator, providerSlotHandle, DataSlotId(33u), true); - createDataSlot(consumerSceneAllocator, consumerSlotHandle, DataSlotId(44u), false); - } - -protected: - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - T linkManager; - - const SceneId providerSceneId; - const SceneId consumerSceneId; - IScene& providerScene; - IScene& consumerScene; - SceneAllocateHelper providerSceneAllocator; - SceneAllocateHelper consumerSceneAllocator; - const DataSlotHandle providerSlotHandle; - const DataSlotHandle consumerSlotHandle; -}; - -using ManagerTypes = ::testing::Types < - LinkManagerBase, - TransformationLinkManager, - DataReferenceLinkManager, - TextureLinkManager ->; - -TYPED_TEST_SUITE(ALinkManager, ManagerTypes); - -TYPED_TEST(ALinkManager, reportsNoDependenciesInitially) -{ - EXPECT_TRUE(this->linkManager.getDependencyChecker().isEmpty()); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); -} - -TYPED_TEST(ALinkManager, canCreateLinkFromConsumerToProvider) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); - const SceneLink& link = this->linkManager.getSceneLinks().getLinkedProvider(this->consumerSceneId, this->consumerSlotHandle); - EXPECT_EQ(this->providerSceneId, link.providerSceneId); - EXPECT_EQ(this->consumerSceneId, link.consumerSceneId); - EXPECT_EQ(this->providerSlotHandle, link.providerSlot); - EXPECT_EQ(this->consumerSlotHandle, link.consumerSlot); - - EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); - EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->providerSceneId)); -} - -TYPED_TEST(ALinkManager, reportsNoLinksForConsumerSceneIfSceneLinksRemoved) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - this->linkManager.removeSceneLinks(this->consumerSceneId); - EXPECT_TRUE(this->linkManager.getDependencyChecker().isEmpty()); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); -} - -TYPED_TEST(ALinkManager, reportsNoLinksForConsumerSceneIfProviderSceneDestroyed) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - this->linkManager.removeSceneLinks(this->providerSceneId); - EXPECT_TRUE(this->linkManager.getDependencyChecker().isEmpty()); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); -} - -TYPED_TEST(ALinkManager, reportsNoLinksForSceneWithRemovedLink) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_TRUE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); - EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); -} - -TYPED_TEST(ALinkManager, canLinkUnlinkAndLinkAgain) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_TRUE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - - const SceneLink& link = this->linkManager.getSceneLinks().getLinkedProvider(this->consumerSceneId, this->consumerSlotHandle); - EXPECT_EQ(this->providerSceneId, link.providerSceneId); - EXPECT_EQ(this->consumerSceneId, link.consumerSceneId); - EXPECT_EQ(this->providerSlotHandle, link.providerSlot); - EXPECT_EQ(this->consumerSlotHandle, link.consumerSlot); - - EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); - EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->providerSceneId)); -} - -TYPED_TEST(ALinkManager, createsTwoLinksSameProviderDifferentConsumers) -{ - const DataSlotHandle consumerSlot2(43u); - createDataSlot(this->consumerSceneAllocator, consumerSlot2, DataSlotId(88u), false); - - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, consumerSlot2)); - - const SceneLink& link = this->linkManager.getSceneLinks().getLinkedProvider(this->consumerSceneId, consumerSlot2); - EXPECT_EQ(this->providerSceneId, link.providerSceneId); - EXPECT_EQ(this->consumerSceneId, link.consumerSceneId); - EXPECT_EQ(this->providerSlotHandle, link.providerSlot); - EXPECT_EQ(consumerSlot2, link.consumerSlot); - - EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); - EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->providerSceneId)); -} - -TYPED_TEST(ALinkManager, failsToCreateLinksCausingCyclicDependency) -{ - const DataSlotHandle slotHandle(43u); - createDataSlot(this->consumerSceneAllocator, slotHandle, DataSlotId(88u), true); - - const DataSlotHandle slotHandle2(43u); - createDataSlot(this->providerSceneAllocator, slotHandle2, DataSlotId(89u), false); - - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_FALSE(this->linkManager.createDataLink(this->consumerSceneId, slotHandle, this->providerSceneId, slotHandle2)); -} - -TYPED_TEST(ALinkManager, failsToCreateLinkIfConsumerAlreadyLinked) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_FALSE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); -} - -TYPED_TEST(ALinkManager, failsToRemoveLinkIfConsumerNotLinked) -{ - EXPECT_FALSE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); -} - -TYPED_TEST(ALinkManager, failsToRemoveLinkIfConsumerAlreadyUnlinked) -{ - EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_TRUE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); - EXPECT_FALSE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); -} diff --git a/renderer/RendererLib/RendererLib/test/PendingSceneResourcesUtilsTest.cpp b/renderer/RendererLib/RendererLib/test/PendingSceneResourcesUtilsTest.cpp deleted file mode 100644 index 59eedb0a5..000000000 --- a/renderer/RendererLib/RendererLib/test/PendingSceneResourcesUtilsTest.cpp +++ /dev/null @@ -1,312 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererAPI/Types.h" -#include "RendererLib/PendingSceneResourcesUtils.h" -#include "RendererResourceManagerMock.h" -#include "Scene/Scene.h" -#include "SceneAllocateHelper.h" -#include "SceneUtils/ResourceUtils.h" - -namespace ramses_internal { -using namespace testing; - -class APendingSceneResourcesUtils : public ::testing::Test -{ -public: - APendingSceneResourcesUtils() - : scene(SceneInfo(sceneID)) - , allocateHelper(scene) - { - allocateHelper.allocateRenderTarget(renderTargetHandle); - allocateHelper.allocateRenderBuffer({ 16u, 16u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }, renderBufferHandle); - allocateHelper.allocateBlitPass(RenderBufferHandle(81u), RenderBufferHandle(82u), blitPassHandle); - allocateHelper.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, dataBufferHandle); - allocateHelper.allocateTextureBuffer(ETextureFormat::R8, { { 4, 4 },{ 2, 2 },{ 1, 1 } }, textureBufferHandle); - } - -protected: - const SceneId sceneID = SceneId(13u); - const RenderTargetHandle renderTargetHandle = RenderTargetHandle(5u); - const RenderBufferHandle renderBufferHandle = RenderBufferHandle(6u); - const BlitPassHandle blitPassHandle = BlitPassHandle(8u); - const DataBufferHandle dataBufferHandle = DataBufferHandle(9u); - const TextureBufferHandle textureBufferHandle = TextureBufferHandle(10u); - - const MemoryHandle dummyHandle = MemoryHandle(123u); - const MemoryHandle dummyHandle2 = MemoryHandle(124u); - - Scene scene; - SceneAllocateHelper allocateHelper; - StrictMock resourceManager; -}; - -struct BasicActionSet -{ - explicit BasicActionSet(ESceneResourceAction c = ESceneResourceAction_Invalid, ESceneResourceAction d = ESceneResourceAction_Invalid, ESceneResourceAction u = ESceneResourceAction_Invalid) - : create(c) - , destroy(d) - , update(u) - { - } - - ESceneResourceAction create; - ESceneResourceAction destroy; - ESceneResourceAction update; -}; - -static const BasicActionSet ActionSet_RenderTarget{ ESceneResourceAction_CreateRenderTarget, ESceneResourceAction_DestroyRenderTarget }; -static const BasicActionSet ActionSet_RenderBuffer{ ESceneResourceAction_CreateRenderBuffer, ESceneResourceAction_DestroyRenderBuffer }; -static const BasicActionSet ActionSet_BlitPass{ ESceneResourceAction_CreateBlitPass, ESceneResourceAction_DestroyBlitPass }; -static const BasicActionSet ActionSet_DataBuffer{ ESceneResourceAction_CreateDataBuffer, ESceneResourceAction_DestroyDataBuffer, ESceneResourceAction_UpdateDataBuffer }; -static const BasicActionSet ActionSet_TextureBuffer{ ESceneResourceAction_CreateTextureBuffer, ESceneResourceAction_DestroyTextureBuffer, ESceneResourceAction_UpdateTextureBuffer }; - -static const BasicActionSet TestSceneResourceActions[] = -{ - ActionSet_RenderTarget, - ActionSet_RenderBuffer, - ActionSet_BlitPass, - ActionSet_DataBuffer, - ActionSet_TextureBuffer -}; - -static const BasicActionSet TestSceneResourceActions_Buffers[] = -{ - ActionSet_DataBuffer, - ActionSet_TextureBuffer -}; - -TEST_F(APendingSceneResourcesUtils, appliesSceneResourceActions) -{ - SceneResourceActionVector actions; - actions.push_back(SceneResourceAction(renderBufferHandle.asMemoryHandle(), ESceneResourceAction_CreateRenderBuffer)); - actions.push_back(SceneResourceAction(renderTargetHandle.asMemoryHandle(), ESceneResourceAction_CreateRenderTarget)); - actions.push_back(SceneResourceAction(blitPassHandle.asMemoryHandle(), ESceneResourceAction_CreateBlitPass)); - actions.push_back(SceneResourceAction(dataBufferHandle.asMemoryHandle(), ESceneResourceAction_CreateDataBuffer)); - actions.push_back(SceneResourceAction(dataBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateDataBuffer)); - actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_CreateTextureBuffer)); - actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); - - InSequence seq; - EXPECT_CALL(resourceManager, uploadRenderTargetBuffer(renderBufferHandle, sceneID, _)); - EXPECT_CALL(resourceManager, uploadRenderTarget(renderTargetHandle, _, sceneID)); - EXPECT_CALL(resourceManager, uploadBlitPassRenderTargets(blitPassHandle, _, _, sceneID)); - EXPECT_CALL(resourceManager, uploadDataBuffer(dataBufferHandle, _, _, _, sceneID)); - EXPECT_CALL(resourceManager, updateDataBuffer(dataBufferHandle, _, _, sceneID)); - EXPECT_CALL(resourceManager, uploadTextureBuffer(textureBufferHandle, _, _, _, _, sceneID)); - EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, _, _, _, _, _, _, sceneID)).Times(3u); // 3 mips - PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); -} - -TEST_F(APendingSceneResourcesUtils, appliesSceneResourceActions_Unloads) -{ - SceneResourceActionVector actions; - actions.push_back(SceneResourceAction(renderTargetHandle.asMemoryHandle(), ESceneResourceAction_DestroyRenderTarget)); - actions.push_back(SceneResourceAction(renderBufferHandle.asMemoryHandle(), ESceneResourceAction_DestroyRenderBuffer)); - actions.push_back(SceneResourceAction(blitPassHandle.asMemoryHandle(), ESceneResourceAction_DestroyBlitPass)); - actions.push_back(SceneResourceAction(dataBufferHandle.asMemoryHandle(), ESceneResourceAction_DestroyDataBuffer)); - actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_DestroyTextureBuffer)); - - InSequence seq; - EXPECT_CALL(resourceManager, unloadRenderTarget(renderTargetHandle, sceneID)); - EXPECT_CALL(resourceManager, unloadRenderTargetBuffer(renderBufferHandle, sceneID)); - EXPECT_CALL(resourceManager, unloadBlitPassRenderTargets(blitPassHandle, sceneID)); - EXPECT_CALL(resourceManager, unloadDataBuffer(dataBufferHandle, sceneID)); - EXPECT_CALL(resourceManager, unloadTextureBuffer(textureBufferHandle, sceneID)); - PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); -} - -TEST_F(APendingSceneResourcesUtils, getsSceneResourcesFromSceneAndApliesThem) -{ - SceneResourceActionVector actions; - size_t resSize = 999u; - ResourceUtils::GetAllSceneResourcesFromScene(actions, scene, resSize); - EXPECT_FALSE(actions.empty()); - EXPECT_EQ(21u, resSize); - - InSequence seq; - EXPECT_CALL(resourceManager, uploadRenderTargetBuffer(renderBufferHandle, sceneID, _)); - EXPECT_CALL(resourceManager, uploadRenderTarget(renderTargetHandle, _, sceneID)); - EXPECT_CALL(resourceManager, uploadBlitPassRenderTargets(blitPassHandle, RenderBufferHandle(81), RenderBufferHandle(82), sceneID)); - EXPECT_CALL(resourceManager, uploadDataBuffer(dataBufferHandle, _, _, _, sceneID)); - EXPECT_CALL(resourceManager, updateDataBuffer(dataBufferHandle, _, _, sceneID)); - - EXPECT_CALL(resourceManager, uploadTextureBuffer(textureBufferHandle, _, _, _, _, sceneID)); - EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 0u, 0u, 0u, 4u, 4u, _, sceneID)); - EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 1u, 0u, 0u, 2u, 2u, _, sceneID)); - EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 2u, 0u, 0u, 1u, 1u, _, sceneID)); - PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); -} - -TEST_F(APendingSceneResourcesUtils, cancelsOutCreateAndDeleteDuringConsolidation) -{ - for (const auto& crateDestroyPair : TestSceneResourceActions) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // test create/destroy pair across old/new and also another pair within new only - actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - EXPECT_TRUE(actions.empty()); - } -} - -TEST_F(APendingSceneResourcesUtils, doesNotCancelOutDestroyAndCreateDuringConsolidation) -{ - for (const auto& crateDestroyPair : TestSceneResourceActions) - { - SceneResourceActionVector actionsNew; - SceneResourceActionVector actions; - - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(crateDestroyPair.destroy, actions.front().action); - EXPECT_EQ(crateDestroyPair.create, actions.back().action); - } -} - -TEST_F(APendingSceneResourcesUtils, doesNotCancelOutCreateAndDeleteOfDifferentHandlesDuringConsolidation) -{ - for (const auto& crateDestroyPair : TestSceneResourceActions) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // test create/destroy pair across old/new and also another pair within new only - actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle2, crateDestroyPair.destroy)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(SceneResourceAction(dummyHandle, crateDestroyPair.create), actions.front()); - EXPECT_EQ(SceneResourceAction(dummyHandle2, crateDestroyPair.destroy), actions.back()); - } -} - -TEST_F(APendingSceneResourcesUtils, keepsAdditionalCreateActionAfterCanceledCreateAndDeleteDuringConsolidation) -{ - for (const auto& crateDestroyPair : TestSceneResourceActions) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // test create/destroy pair across old/new and also another pair within new only - actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - // additional create - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(1u, actions.size()); - EXPECT_EQ(crateDestroyPair.create, actions.front().action); - } -} - -TEST_F(APendingSceneResourcesUtils, keepsAdditionalDestroyActionAfterCanceledCreateAndDeleteDuringConsolidation) -{ - for (const auto& crateDestroyPair : TestSceneResourceActions) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // test create/destroy pair across old/new and also another pair within new only - actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - // additional create - actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(1u, actions.size()); - EXPECT_EQ(crateDestroyPair.destroy, actions.front().action); - } -} - -TEST_F(APendingSceneResourcesUtils, squashesBufferUpdateActionsToSingleActionDuringConsolidation) -{ - for (const auto& bufferAction : TestSceneResourceActions_Buffers) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // updates both in old and new actions - actions.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(1u, actions.size()); - EXPECT_EQ(bufferAction.update, actions.front().action); - } -} - -TEST_F(APendingSceneResourcesUtils, removesAnyDataBufferUpdateActionsBeforeDestroyDuringConsolidation) -{ - for (const auto& bufferAction : TestSceneResourceActions_Buffers) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // updates both in old and new actions - actions.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - - // destroy buffer - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.destroy)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(1u, actions.size()); - EXPECT_EQ(bufferAction.destroy, actions.front().action); - } -} - -TEST_F(APendingSceneResourcesUtils, keepsDataBufferUpdateActionForNewlyCreatedAfterRemovedPreviousUpdatesDueToDestroy) -{ - for (const auto& bufferAction : TestSceneResourceActions_Buffers) - { - SceneResourceActionVector actions; - SceneResourceActionVector actionsNew; - - // create buffer - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.create)); - // updates both in old and new actions - actions.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - // destroy buffer - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.destroy)); - // all previous ones should be canceled - - // create new buffer - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.create)); - // update new buffer - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); - - PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); - ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(bufferAction.create, actions.front().action); - EXPECT_EQ(bufferAction.update, actions.back().action); - } -} -} diff --git a/renderer/RendererLib/RendererLib/test/RenderExecutorTest.cpp b/renderer/RendererLib/RendererLib/test/RenderExecutorTest.cpp deleted file mode 100644 index 3b164529c..000000000 --- a/renderer/RendererLib/RendererLib/test/RenderExecutorTest.cpp +++ /dev/null @@ -1,1773 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererResourceManagerMock.h" -#include "RenderBackendMock.h" -#include "RenderExecutor.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/Renderer.h" -#include "RendererLib/RendererScenes.h" -#include "SceneUtils/DataLayoutCreationHelper.h" -#include "RendererEventCollector.h" -#include "SceneAllocateHelper.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "Components/EffectUniformTime.h" -#include "MockResourceHash.h" -#include "glm/gtx/transform.hpp" -#include "glm/gtx/quaternion.hpp" - -namespace ramses_internal { -using namespace testing; - -namespace -{ - const float fakeFieldOfView = 19.f; - const float fakeAspectRatio = 0.5f; - const float fakeNearPlane = 0.1f; - const float fakeFarPlane = 1500.f; - - const int32_t fakeViewportX = 15; - const int32_t fakeViewportY = 16; - const uint32_t fakeViewportWidth = 17u; - const uint32_t fakeViewportHeight = 18u; - - const uint32_t startIndex = 12u; - const uint32_t indexCount = 13; - const uint32_t startVertex = 14u; - - const ExternalBufferHandle fakeExternalBuffer{ 1240u }; - const DeviceResourceHandle fakeExternalTextureDeviceHandle{ 1050u }; -} - -using DataInstances = std::pair; - -// This Matrix comparison matcher is needed to compare the MVP etc matrices. It must allow -// a relatively high error because heavy optimizations on some platforms may lead to significant -// differences in precision (likely because values are kept in 80bit FP registers) -MATCHER_P(PermissiveMatrixEq, other, "") -{ - UNUSED(result_listener); - for (glm::length_t i = 0; i < arg.length(); ++i) - { - for (glm::length_t k = 0; k < arg[i].length(); ++k) - { - const auto a = arg[i][k]; - const auto b = other[i][k]; - - const float fDelta = std::abs(a - b); - if (fDelta > 1e-5f) - { - float relativeError = 0.f; - if (std::abs(b) > std::abs(a)) - { - relativeError = std::abs((a - b) / b); - } - else - { - relativeError = std::abs((a - b) / a); - } - if (relativeError > 1.e-7f) - { - return false; - } - } - } - } - - return true; -} - -static int32_t GetElapsed(int32_t start, int32_t end) -{ - if (start == end) - return 0; - if (start < end) - { - return end - start; - } - else - { - constexpr auto limit = std::numeric_limits::max(); - return (limit - start) + end + 1; - } -} - -MATCHER_P(TimeEq, expectedTime, "") -{ - UNUSED(result_listener); - const int32_t tolerance = 10000; // tolerate stuck during test execution - return GetElapsed(expectedTime, arg) < tolerance; -} - -class FakeEffectInputs -{ -public: - explicit FakeEffectInputs(bool withTimeMs) - { - uniformInputs.push_back(EffectInputInformation("dataRefField1", 1, EDataType::Float, EFixedSemantics::Invalid)); - dataRefField1 = DataFieldHandle(0); - uniformInputs.push_back(EffectInputInformation("fieldModelMatrix", 1, EDataType::Matrix44F, EFixedSemantics::ModelMatrix)); - fieldModelMatrix = DataFieldHandle(1); - uniformInputs.push_back(EffectInputInformation("fieldViewMatrix", 1, EDataType::Matrix44F, EFixedSemantics::ViewMatrix)); - fieldViewMatrix = DataFieldHandle(2); - uniformInputs.push_back(EffectInputInformation("fieldProjMatrix", 1, EDataType::Matrix44F, EFixedSemantics::ProjectionMatrix)); - fieldProjMatrix = DataFieldHandle(3); - uniformInputs.push_back(EffectInputInformation("textureField", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid)); - textureField = DataFieldHandle(4); - uniformInputs.push_back(EffectInputInformation("textureFieldMS", 1, EDataType::TextureSampler2DMS, EFixedSemantics::Invalid)); - textureFieldMS = DataFieldHandle(5); - uniformInputs.push_back(EffectInputInformation("dataRefField2", 1, EDataType::Float, EFixedSemantics::Invalid)); - dataRefField2 = DataFieldHandle(6); - uniformInputs.push_back(EffectInputInformation("dataRefFieldMatrix22f", 1, EDataType::Matrix22F, EFixedSemantics::Invalid)); - dataRefFieldMatrix22f = DataFieldHandle(7); - uniformInputs.push_back(EffectInputInformation("textureFieldExternal", 1, EDataType::TextureSamplerExternal, EFixedSemantics::Invalid)); - textureFieldExternal = DataFieldHandle(8); - - if (withTimeMs) - { - uniformInputs.push_back(EffectInputInformation("timeMs", 1, EDataType::Int32, EFixedSemantics::TimeMs)); - dataRefTimeMs = DataFieldHandle(9); - } - - attributeInputs.push_back(EffectInputInformation("vertPosField", 1, EDataType::Vector3Buffer, EFixedSemantics::Invalid)); - vertPosField = DataFieldHandle(0); - attributeInputs.push_back(EffectInputInformation("vertTexcoordField", 1, EDataType::Vector2Buffer, EFixedSemantics::TextTextureCoordinatesAttribute)); - vertTexcoordField = DataFieldHandle(1); - } - - EffectInputInformationVector uniformInputs; - EffectInputInformationVector attributeInputs; - - DataFieldHandle vertPosField; - DataFieldHandle vertTexcoordField; - DataFieldHandle dataRefField1; - DataFieldHandle dataRefField2; - DataFieldHandle dataRefFieldMatrix22f; - DataFieldHandle dataRefTimeMs; - DataFieldHandle textureField; - DataFieldHandle textureFieldMS; - DataFieldHandle textureFieldExternal; - DataFieldHandle fieldModelMatrix; - DataFieldHandle fieldViewMatrix; - DataFieldHandle fieldProjMatrix; -}; - - -class ARenderExecutorBase: public ::testing::Test -{ -public: - explicit ARenderExecutorBase(bool withTimeMs) - : device(renderer.deviceMock) - , renderContext{ DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, fakeViewportWidth, fakeViewportHeight, {}, EClearFlags_All, glm::vec4{0.f}, false } - , rendererScenes(rendererEventCollector) - , scene(rendererScenes.createScene(SceneInfo())) - , sceneAllocator(scene) - , fakeEffectInputs(withTimeMs) - , indicesField(0u) - , vertPosField(1u) - , vertTexcoordField(2u) - , textureField (fakeEffectInputs.textureField ) - , textureFieldMS (fakeEffectInputs.textureFieldMS ) - , textureFieldExternal (fakeEffectInputs.textureFieldExternal ) - , fieldModelMatrix (fakeEffectInputs.fieldModelMatrix ) - , fieldViewMatrix (fakeEffectInputs.fieldViewMatrix ) - , fieldProjMatrix (fakeEffectInputs.fieldProjMatrix ) - { - InputIndexVector referencedInputs; - scene.preallocateSceneSize(SceneSizeInformation(0u, 0u, 0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)); - uniformLayout = DataLayoutCreationHelper::CreateUniformDataLayoutMatchingEffectInputs(scene, fakeEffectInputs.uniformInputs, referencedInputs, MockResourceHash::EffectHash, DataLayoutHandle(0u)); - - DataFieldInfoVector dataFields(3u); - dataFields[indicesField.asMemoryHandle()] = DataFieldInfo(EDataType::Indices, 1u, EFixedSemantics::Indices); - dataFields[vertPosField.asMemoryHandle()] = DataFieldInfo(EDataType::Vector3Buffer, 1u, EFixedSemantics::Invalid); - dataFields[vertTexcoordField.asMemoryHandle()] = DataFieldInfo(EDataType::Vector2Buffer, 1u, EFixedSemantics::TextPositionsAttribute); - geometryLayout = sceneAllocator.allocateDataLayout(dataFields, MockResourceHash::EffectHash, DataLayoutHandle(2u)); - } - -protected: - enum class EExpectedRenderStateChange - { - None, - CausedByClear, - All, - }; - - StrictMock renderer; - StrictMock& device; - NiceMock resourceManager; - - RenderingContext renderContext; - - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - RendererCachedScene& scene; - SceneAllocateHelper sceneAllocator; - - FakeEffectInputs fakeEffectInputs; - TextureSamplerHandle sampler; - TextureSamplerHandle samplerMS; - TextureSamplerHandle samplerExternal; - const DataFieldHandle indicesField; - const DataFieldHandle vertPosField; - const DataFieldHandle vertTexcoordField; - const DataFieldHandle textureField; - const DataFieldHandle textureFieldMS; - const DataFieldHandle textureFieldExternal; - const DataFieldHandle fieldModelMatrix; - const DataFieldHandle fieldViewMatrix; - const DataFieldHandle fieldProjMatrix; - DataInstanceHandle dataRef1; - DataInstanceHandle dataRef2; - DataInstanceHandle dataRefMatrix22f; - - DataLayoutHandle uniformLayout; - DataLayoutHandle geometryLayout; - - Sequence deviceSequence; - - ProjectionParams getDefaultProjectionParams(ECameraProjectionType cameraProjType = ECameraProjectionType::Perspective) - { - if (cameraProjType == ECameraProjectionType::Perspective) - return ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane); - return ProjectionParams::Frustum(ECameraProjectionType::Orthographic, -1.f, 1.f, -10.f, 10.f, 1.f, 10.f); - } - - RenderPassHandle createRenderPassWithCamera(const ProjectionParams& params, const Viewport& viewport = { fakeViewportX, fakeViewportY, fakeViewportWidth, fakeViewportHeight }) - { - const RenderPassHandle pass = sceneAllocator.allocateRenderPass(); - const NodeHandle cameraNode = sceneAllocator.allocateNode(); - const auto dataLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference} }, {}); - const auto dataInstance = sceneAllocator.allocateDataInstance(dataLayout); - const auto vpDataRefLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I} }, {}); - const auto vpOffsetInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); - const auto vpSizeInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); - const auto frustumPlanesLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector4F} }, {}); - const auto frustumPlanes = sceneAllocator.allocateDataInstance(frustumPlanesLayout); - const auto frustumNearFarLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2F} }, {}); - const auto frustumNearFar = sceneAllocator.allocateDataInstance(frustumNearFarLayout); - scene.setDataReference(dataInstance, Camera::ViewportOffsetField, vpOffsetInstance); - scene.setDataReference(dataInstance, Camera::ViewportSizeField, vpSizeInstance); - scene.setDataReference(dataInstance, Camera::FrustumPlanesField, frustumPlanes); - scene.setDataReference(dataInstance, Camera::FrustumNearFarPlanesField, frustumNearFar); - const CameraHandle camera = sceneAllocator.allocateCamera(params.getProjectionType(), cameraNode, dataInstance); - - scene.setDataSingleVector4f(frustumPlanes, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); - scene.setDataSingleVector2f(frustumNearFar, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); - - scene.setDataSingleVector2i(vpOffsetInstance, DataFieldHandle{ 0 }, { viewport.posX, viewport.posY }); - scene.setDataSingleVector2i(vpSizeInstance, DataFieldHandle{ 0 }, { int32_t(viewport.width), int32_t(viewport.height) }); - - sceneAllocator.allocateTransform(cameraNode); - scene.setRenderPassCamera(pass, camera); - return pass; - } - - [[nodiscard]] TransformHandle findTransformForNode(NodeHandle node) const - { - for (TransformHandle transform(0u); transform < scene.getTransformCount(); ++transform) - { - if (scene.isTransformAllocated(transform) && scene.getTransformNode(transform) == node) - { - return transform; - } - } - - assert(false); - return TransformHandle::Invalid(); - } - - DataInstances createTestDataInstance(bool setTextureSampler = true, bool setIndexArray = true) - { - //create samplers - sampler = sceneAllocator.allocateTextureSampler({ { EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Nearest_MipMapNearest, ESamplingMethod::Nearest, 2u }, MockResourceHash::TextureHash }); - samplerMS = sceneAllocator.allocateTextureSampler({ {}, MockResourceHash::TextureHash }); - samplerExternal = sceneAllocator.allocateTextureSampler({ {EWrapMethod::Repeat, EWrapMethod::Clamp, EWrapMethod::RepeatMirrored, ESamplingMethod::Nearest_MipMapNearest, ESamplingMethod::Nearest, 1u }, TextureSampler::ContentType::ExternalTexture, ResourceContentHash::Invalid(), fakeExternalBuffer.asMemoryHandle() }); - - // create data instance - DataInstances dataInstances; - dataInstances.first = sceneAllocator.allocateDataInstance(uniformLayout); - dataInstances.second = sceneAllocator.allocateDataInstance(geometryLayout); - - // create referenced data instance - // explicit preallocation needed because here we use DataLayoutCreationHelper which allocates inside, - // we cannot use scene allocation helper - MemoryHandle nextHandle = std::max(scene.getDataInstanceCount(), scene.getDataLayoutCount()); - scene.preallocateSceneSize(SceneSizeInformation(0u, 0u, 0u, 0u, 0u, nextHandle + 3u, nextHandle + 3u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)); - dataRef1 = ramses_internal::DataLayoutCreationHelper::CreateAndBindDataReference(scene, dataInstances.first, fakeEffectInputs.dataRefField1, EDataType::Float, DataLayoutHandle(nextHandle), DataInstanceHandle(nextHandle)); - dataRef2 = ramses_internal::DataLayoutCreationHelper::CreateAndBindDataReference(scene, dataInstances.first, fakeEffectInputs.dataRefField2, EDataType::Float, DataLayoutHandle(nextHandle + 1u), DataInstanceHandle(nextHandle + 1u)); - dataRefMatrix22f = ramses_internal::DataLayoutCreationHelper::CreateAndBindDataReference(scene, dataInstances.first, fakeEffectInputs.dataRefFieldMatrix22f, EDataType::Matrix22F, DataLayoutHandle(nextHandle + 2u), DataInstanceHandle(nextHandle + 2u)); - scene.setDataSingleFloat(dataRef1, DataFieldHandle(0u), 0.1f); - scene.setDataSingleFloat(dataRef2, DataFieldHandle(0u), -666.f); - scene.setDataSingleMatrix22f(dataRefMatrix22f, DataFieldHandle(0u), glm::mat2(1, 2, 3, 4)); - - if (setIndexArray) - { - scene.setDataResource(dataInstances.second, indicesField, MockResourceHash::IndexArrayHash, DataBufferHandle::Invalid(), 2u, 0u, 0u); - } - scene.setDataResource(dataInstances.second, vertPosField, MockResourceHash::VertArrayHash, DataBufferHandle::Invalid(), 3u, 17u, 77u); - scene.setDataResource(dataInstances.second, vertTexcoordField, MockResourceHash::VertArrayHash2, DataBufferHandle::Invalid(), 4u, 18u, 88u); - ON_CALL(resourceManager, getResourceDeviceHandle(MockResourceHash::VertArrayHash2)).WillByDefault(Return(DeviceMock::FakeVertexBufferDeviceHandle)); - - if (setTextureSampler) - { - scene.setDataTextureSamplerHandle(dataInstances.first, textureField, sampler); - scene.setDataTextureSamplerHandle(dataInstances.first, textureFieldMS, samplerMS); - scene.setDataTextureSamplerHandle(dataInstances.first, textureFieldExternal, samplerExternal); - } - ON_CALL(resourceManager, getExternalBufferDeviceHandle(fakeExternalBuffer)).WillByDefault(Return(fakeExternalTextureDeviceHandle)); - - return dataInstances; - } - - RenderGroupHandle createRenderGroup(RenderPassHandle pass) - { - const RenderGroupHandle renderGroup = sceneAllocator.allocateRenderGroup(); - scene.addRenderGroupToRenderPass(pass, renderGroup, 0); - return renderGroup; - } - - RenderableHandle createTestRenderable( - DataInstances dataInstances, - RenderGroupHandle group = RenderGroupHandle::Invalid(), - RenderableHandle renderableHandle = RenderableHandle::Invalid()) - { - const RenderableHandle renderable = sceneAllocator.allocateRenderable(sceneAllocator.allocateNode(), renderableHandle); - - scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Uniforms, dataInstances.first); - scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Geometry, dataInstances.second); - scene.setRenderableRenderState(renderable, sceneAllocator.allocateRenderState()); - - scene.setRenderableStartIndex(renderable, startIndex); - scene.setRenderableIndexCount(renderable, indexCount); - scene.setRenderableStartVertex(renderable, startVertex); - - if (group.isValid()) - { - scene.addRenderableToRenderGroup(group, renderable, 0); - } - - return renderable; - } - - TransformHandle addTransformToNode(NodeHandle node) - { - return sceneAllocator.allocateTransform(node); - } - - TransformHandle addTransformToRenderable(RenderableHandle renderableHandle) - { - const NodeHandle node = scene.getRenderable(renderableHandle).node; - return addTransformToNode(node); - } - - RenderTargetHandle createRenderTarget(uint32_t width = 800u, uint32_t height = 600u) - { - const RenderTargetHandle targetHandle = sceneAllocator.allocateRenderTarget(); - const RenderBufferHandle colorBufferHandle = sceneAllocator.allocateRenderBuffer({ width, height, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }); - const RenderBufferHandle depthBufferHandle = sceneAllocator.allocateRenderBuffer({ width, height, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_WriteOnly, 0u }); - scene.addRenderTargetRenderBuffer(targetHandle, colorBufferHandle); - scene.addRenderTargetRenderBuffer(targetHandle, depthBufferHandle); - - return targetHandle; - } - - RenderBufferHandle createRenderbuffer() - { - const RenderBufferHandle bufferHandle = sceneAllocator.allocateRenderBuffer({ 10u, 20u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }); - - return bufferHandle; - } - - BlitPassHandle createBlitPass(RenderBufferHandle sourceRenderBuffer, RenderBufferHandle destinationRenderBuffer) - { - const BlitPassHandle passHandle = sceneAllocator.allocateBlitPass(sourceRenderBuffer, destinationRenderBuffer); - return passHandle; - } - - void expectFrameWithSinglePass( - RenderableHandle renderable, - const ProjectionParams& projectionParams, - glm::mat4 expectedModelMatrix = glm::mat4(1.f), - glm::mat4 expectedViewMatrix = glm::mat4(1.f)) - { - expectActivateRenderTarget(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, true); - if (renderContext.displayBufferClearPending != EClearFlags_None) - expectClearRenderTarget(renderContext.displayBufferClearPending); - const glm::mat4 projMatrix = CameraMatrixHelper::ProjectionMatrix(projectionParams); - expectFrameRenderCommands(renderable, expectedModelMatrix, expectedViewMatrix, projMatrix); - } - - void expectFrameRenderCommands(RenderableHandle /*renderable*/, - glm::mat4 expectedModelMatrix = glm::mat4(1.f), - glm::mat4 expectedViewMatrix = glm::mat4(1.f), - glm::mat4 expectedProjMatrix = glm::mat4(1.f), - bool expectShaderActivation = true, - EExpectedRenderStateChange expectRenderStateChanges = EExpectedRenderStateChange::All, - uint32_t instanceCount = 1u, - bool expectIndexedRendering = true, - int32_t expectedUniformTimeMs = 0) - { - // TODO violin this is not entirely needed, only need to check that draw call is at the end of the commands - InSequence seq; - - const DeviceResourceHandle FakeShaderDeviceHandle = DeviceMock::FakeShaderDeviceHandle ; - const DeviceResourceHandle FakeTextureDeviceHandle = DeviceMock::FakeTextureDeviceHandle ; - - // RetiresOnSaturation makes it possible to invoke this expect method multiple times without - // the expectations override each other .RetiresOnSaturation(); - - if (expectRenderStateChanges == EExpectedRenderStateChange::All) - { - EXPECT_CALL(device, scissorTest(_, _)) .InSequence(deviceSequence); - EXPECT_CALL(device, depthFunc(_)) .InSequence(deviceSequence); - EXPECT_CALL(device, depthWrite(_)) .InSequence(deviceSequence); - EXPECT_CALL(device, stencilFunc(_,_,_)) .InSequence(deviceSequence); - EXPECT_CALL(device, stencilOp(_,_,_)) .InSequence(deviceSequence); - EXPECT_CALL(device, blendOperations(_, _)) .InSequence(deviceSequence); - EXPECT_CALL(device, blendFactors(_, _, _, _)) .InSequence(deviceSequence); - EXPECT_CALL(device, blendColor(_)) .InSequence(deviceSequence); - EXPECT_CALL(device, colorMask(_, _, _, _)) .InSequence(deviceSequence); - EXPECT_CALL(device, cullMode(_)) .InSequence(deviceSequence); - } - else if (expectRenderStateChanges == EExpectedRenderStateChange::CausedByClear) - { - EXPECT_CALL(device, scissorTest(_, _)).InSequence(deviceSequence); - EXPECT_CALL(device, depthWrite(_)).InSequence(deviceSequence); - EXPECT_CALL(device, colorMask(_, _, _, _)).InSequence(deviceSequence); - } - EXPECT_CALL(device, drawMode(_)).InSequence(deviceSequence); - - if (expectShaderActivation) - { - EXPECT_CALL(device, activateShader(FakeShaderDeviceHandle)) .InSequence(deviceSequence); - } - EXPECT_CALL(device, activateVertexArray(_)) .InSequence(deviceSequence); - EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefField1, 1, Matcher(Pointee(Eq(0.1f))))) .InSequence(deviceSequence); - EXPECT_CALL(device, setConstant(fieldModelMatrix, 1, Matcher(Pointee(PermissiveMatrixEq(expectedModelMatrix))))) .InSequence(deviceSequence); - EXPECT_CALL(device, setConstant(fieldViewMatrix, 1, Matcher(Pointee(PermissiveMatrixEq(expectedViewMatrix))))) .InSequence(deviceSequence); - EXPECT_CALL(device, setConstant(fieldProjMatrix, 1, Matcher(Pointee(PermissiveMatrixEq(expectedProjMatrix))))) .InSequence(deviceSequence); - EXPECT_CALL(device, activateTexture(FakeTextureDeviceHandle, textureField)) .InSequence(deviceSequence); - const TextureSamplerStates expectedSamplerStates(EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Nearest_MipMapNearest, ESamplingMethod::Nearest, 2u); - EXPECT_CALL(device, activateTextureSamplerObject(Property(&TextureSamplerStates::hash, Eq(expectedSamplerStates.hash())), textureField)).InSequence(deviceSequence); - EXPECT_CALL(device, activateTexture(FakeTextureDeviceHandle, textureFieldMS)) .InSequence(deviceSequence); - - EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefField2, 1, Matcher(Pointee(Eq(-666.f))))) .InSequence(deviceSequence); - EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefFieldMatrix22f, 1, Matcher(Pointee(Eq(glm::mat2(1,2,3,4)))))) .InSequence(deviceSequence); - - EXPECT_CALL(device, activateTexture(fakeExternalTextureDeviceHandle, textureFieldExternal)).InSequence(deviceSequence); - const TextureSamplerStates expectedSamplerExternalStates(EWrapMethod::Repeat, EWrapMethod::Clamp, EWrapMethod::RepeatMirrored, ESamplingMethod::Nearest_MipMapNearest, ESamplingMethod::Nearest, 1u); - EXPECT_CALL(device, activateTextureSamplerObject(Property(&TextureSamplerStates::hash, Eq(expectedSamplerExternalStates.hash())), textureFieldExternal)).InSequence(deviceSequence); - - if (fakeEffectInputs.dataRefTimeMs.isValid()) - { - EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefTimeMs, 1, Matcher(Pointee(TimeEq(expectedUniformTimeMs))))) .InSequence(deviceSequence); - } - if (expectIndexedRendering) - { - EXPECT_CALL(device, drawIndexedTriangles(startIndex, indexCount, instanceCount)).InSequence(deviceSequence); - } - else - { - EXPECT_CALL(device, drawTriangles(startIndex, indexCount, instanceCount)).InSequence(deviceSequence); - } - } - - void expectActivateRenderTarget(DeviceResourceHandle rtDeviceHandle, bool expectViewport = true, const Viewport& viewport = Viewport(fakeViewportX, fakeViewportY, fakeViewportWidth, fakeViewportHeight)) - { - EXPECT_CALL(device, activateRenderTarget(rtDeviceHandle)).InSequence(deviceSequence); - if (expectViewport) - { - EXPECT_CALL(device, setViewport(viewport.posX, viewport.posY, viewport.width, viewport.height)).InSequence(deviceSequence); - } - } - - void expectActivateFramebufferRenderTarget(bool expectViewport = true) - { - EXPECT_CALL(device, activateRenderTarget(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle)).InSequence(deviceSequence); - if (expectViewport) - EXPECT_CALL(device, setViewport(fakeViewportX, fakeViewportY, fakeViewportWidth, fakeViewportHeight)).InSequence(deviceSequence); - } - - void expectClearRenderTarget(uint32_t clearFlags = EClearFlags_All) - { - if (clearFlags & EClearFlags_Color) - { - EXPECT_CALL(device, colorMask(true, true, true, true)).InSequence(deviceSequence); - EXPECT_CALL(device, clearColor(_)).InSequence(deviceSequence); - } - if (clearFlags & EClearFlags_Depth) - { - EXPECT_CALL(device, depthWrite(EDepthWrite::Enabled)).InSequence(deviceSequence); - } - - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(device, scissorTest(EScissorTest::Disabled, scissorRegion)).InSequence(deviceSequence); - - EXPECT_CALL(device, clear(clearFlags)).InSequence(deviceSequence); - } - - void expectDepthStencilDiscard() - { - EXPECT_CALL(device, discardDepthStencil()).InSequence(deviceSequence); - } - - void updateScenes(const RenderableVector& renderablesWithUpdatedVAOs) - { - scene.updateRenderablesAndResourceCache(resourceManager); - scene.updateRenderableVertexArrays(resourceManager, renderablesWithUpdatedVAOs); - scene.markVertexArraysClean(); - scene.updateRenderableWorldMatrices(); - } - - void expectRenderingWithProjection(RenderableHandle renderable, const glm::mat4& projMatrix, const uint32_t instanceCount = 1u) - { - updateScenes({renderable}); - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable, glm::identity(), glm::identity(), projMatrix, true, EExpectedRenderStateChange::All, instanceCount); - executeScene(); - - Mock::VerifyAndClearExpectations(&renderer); - } - - SceneRenderExecutionIterator executeScene(const FrameTimer* frameTimer = nullptr) - { - RenderExecutor executor(device, renderContext, frameTimer); - return executor.executeScene(scene); - } -}; - -class ARenderExecutor : public ARenderExecutorBase -{ -public: - ARenderExecutor() - : ARenderExecutorBase(false) - { - } -}; - -class ARenderExecutorTimeMs : public ARenderExecutorBase -{ -public: - ARenderExecutorTimeMs() - : ARenderExecutorBase(true) - { - } -}; - -TEST(CustomMatrixPrinter, PrintsMatrixValuesNotAByteString) -{ - // see matrix_printer.h for more info if this test ever fails - glm::mat4 mat = glm::translate(glm::vec3(0.6f)); - EXPECT_EQ(std::string("[1,0,0,0.6][0,1,0,0.6][0,0,1,0.6][0,0,0,1]"), ::testing::PrintToString(mat)); -} - -TEST_F(ARenderExecutor, RendersEmptyFrameForEmptyScene) -{ - updateScenes({}); - executeScene({}); -} - -TEST_F(ARenderExecutor, RenderRenderPassIntoRenderTarget) -{ - const RenderPassHandle pass = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - scene.setRenderPassClearFlag(pass, ramses_internal::EClearFlags::EClearFlags_None); - scene.setRenderPassRenderTarget(pass, targetHandle); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - updateScenes({ renderable }); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - { - InSequence seq; - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectFrameRenderCommands(renderable, glm::identity(), glm::identity(), expectedProjectionMatrix); - } - - executeScene(); -} - -TEST_F(ARenderExecutor, RenderRenderableWithoutIndexArray) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(true, false), createRenderGroup(pass)); - scene.setRenderPassClearFlag(pass, ramses_internal::EClearFlags::EClearFlags_None); - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - updateScenes({ renderable }); - - { - InSequence seq; - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable, glm::identity(), glm::identity(), expectedProjectionMatrix, true, EExpectedRenderStateChange::All, 1, false); - } - - executeScene(); -} - -TEST_F(ARenderExecutor, RenderMultipleConsecutiveRenderPassesIntoOneRenderTarget) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(pass1, targetHandle); - - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass2, EClearFlags_None); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); - scene.setRenderPassRenderTarget(pass2, targetHandle); - - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - updateScenes({ renderable1, renderable2 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - { - InSequence seq; - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - expectDepthStencilDiscard(); - } - - executeScene(); -} - -TEST_F(ARenderExecutor, RenderMultipleRenderPassesIntoMultipleRenderTargets) -{ - const auto projParams1 = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const Viewport fakeVp1(1, 2, 3, 4); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams1, fakeVp1); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - const RenderTargetHandle targetHandle1 = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(pass1, targetHandle1); - - const auto projParams2 = getDefaultProjectionParams(ECameraProjectionType::Orthographic); - const Viewport fakeVp2(5, 6, 7, 8); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams2, fakeVp2); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); - const RenderTargetHandle targetHandle2 = createRenderTarget(17, 21); - scene.setRenderPassRenderTarget(pass2, targetHandle2); - - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - updateScenes({ renderable1, renderable2 }); - - const auto expectedProjectionMatrix1 = CameraMatrixHelper::ProjectionMatrix(projParams1); - const auto expectedProjectionMatrix2 = CameraMatrixHelper::ProjectionMatrix(projParams2); - - const DeviceResourceHandle renderTargetDeviceHandle1 = resourceManager.getRenderTargetDeviceHandle(targetHandle1, scene.getSceneId()); - const DeviceResourceHandle renderTargetDeviceHandle2 = resourceManager.getRenderTargetDeviceHandle(targetHandle2, scene.getSceneId()); - - { - InSequence seq; - - expectActivateRenderTarget(renderTargetDeviceHandle1, true, fakeVp1); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix1); - expectDepthStencilDiscard(); - expectActivateRenderTarget(renderTargetDeviceHandle2, true, fakeVp2); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix2, false, EExpectedRenderStateChange::CausedByClear); - expectDepthStencilDiscard(); - } - - executeScene(); -} - -TEST_F(ARenderExecutor, ResetsCachedRenderStatesAfterClearingRenderTargets) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); - const RenderTargetHandle targetHandle2 = createRenderTarget(17, 21); - scene.setRenderPassRenderTarget(pass2, targetHandle2); - - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - updateScenes({ renderable1, renderable2 }); - - const DeviceResourceHandle renderTargetDeviceHandle2 = resourceManager.getRenderTargetDeviceHandle(targetHandle2, scene.getSceneId()); - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - { - InSequence seq; - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), projMatrix); - expectActivateRenderTarget(renderTargetDeviceHandle2, false); - expectClearRenderTarget(); - //render states are set again but shader and index buffer do not have to be set again - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), projMatrix, false, EExpectedRenderStateChange::CausedByClear); - expectDepthStencilDiscard(); - } - - executeScene(); -} - -TEST_F(ARenderExecutor, RenderMultipleRenderPassesIntoOneRenderTargetAndEachClearsWithDifferentFlags) -{ - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass1, EClearFlags_All); - scene.setRenderPassRenderTarget(pass1, targetHandle); - - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass2, EClearFlags_Color); - scene.setRenderPassRenderTarget(pass2, targetHandle); - - const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass3, EClearFlags_Depth); - scene.setRenderPassRenderTarget(pass3, targetHandle); - - const RenderPassHandle pass4 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass4, EClearFlags_Stencil); - scene.setRenderPassRenderTarget(pass4, targetHandle); - - const RenderPassHandle pass5 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass5, EClearFlags_Color | EClearFlags_Depth); - scene.setRenderPassRenderTarget(pass5, targetHandle); - - const RenderPassHandle pass6 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass6, EClearFlags_Color | EClearFlags_Stencil); - scene.setRenderPassRenderTarget(pass6, targetHandle); - - const RenderPassHandle pass7 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass7, EClearFlags_Depth | EClearFlags_Stencil); - scene.setRenderPassRenderTarget(pass7, targetHandle); - - const RenderPassHandle pass8 = createRenderPassWithCamera(projParams); - scene.setRenderPassClearFlag(pass8, EClearFlags_None); - scene.setRenderPassRenderTarget(pass8, targetHandle); - - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - scene.setRenderPassRenderOrder(pass3, 2); - scene.setRenderPassRenderOrder(pass4, 3); - scene.setRenderPassRenderOrder(pass5, 4); - scene.setRenderPassRenderOrder(pass6, 5); - scene.setRenderPassRenderOrder(pass7, 6); - scene.setRenderPassRenderOrder(pass8, 7); - - updateScenes({}); - - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - { - InSequence seq; - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(EClearFlags_All); // pass 1 - expectClearRenderTarget(EClearFlags_Color); // pass 2 - expectClearRenderTarget(EClearFlags_Depth); // pass 3 - expectClearRenderTarget(EClearFlags_Stencil); // pass 4 - expectClearRenderTarget(EClearFlags_Color | EClearFlags_Depth); // pass 5 - expectClearRenderTarget(EClearFlags_Color | EClearFlags_Stencil); // pass 6 - expectDepthStencilDiscard(); - expectClearRenderTarget(EClearFlags_Depth | EClearFlags_Stencil); // pass 7 - // pass 8 no clear expectation - expectDepthStencilDiscard(); - } - - executeScene(); -} - -TEST_F(ARenderExecutor, DoesNotRenderRenderableIfResourcesInvalid) -{ - const RenderPassHandle pass = createRenderPassWithCamera(getDefaultProjectionParams()); - DataInstances InvalidDataInstances; - createTestRenderable(InvalidDataInstances, createRenderGroup(pass)); - - updateScenes({}); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - // empty frame - - executeScene(); -} - -TEST_F(ARenderExecutor, DoesNotRenderRenderableWithNoResourcesAssigned) -{ - const RenderPassHandle pass = createRenderPassWithCamera(getDefaultProjectionParams()); - const RenderGroupHandle group = createRenderGroup(pass); - const RenderableHandle renderable = sceneAllocator.allocateRenderable(sceneAllocator.allocateNode()); - scene.addRenderableToRenderGroup(group, renderable, 0); - - updateScenes({ renderable }); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - // empty frame - - executeScene(); -} - -TEST_F(ARenderExecutor, DoesNotRenderRenderableWithoutDataInstance) -{ - const RenderPassHandle pass = createRenderPassWithCamera(getDefaultProjectionParams()); - - DataInstances InvalidDataInstances; - createTestRenderable(InvalidDataInstances, createRenderGroup(pass), RenderableHandle::Invalid()); - - updateScenes({}); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - executeScene(); -} - -TEST_F(ARenderExecutor, expectUpdateSceneDefaultMatricesIdentity) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); - - updateScenes({ renderable }); - expectFrameWithSinglePass(renderable, projParams); - executeScene(); - - Mock::VerifyAndClearExpectations(&device); -} - -TEST_F(ARenderExecutor, SceneViewMatrixChangeViaCameraTransformation) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); - - updateScenes({ renderable }); - expectFrameWithSinglePass(renderable, projParams); - executeScene(); - Mock::VerifyAndClearExpectations(&device); - - const NodeHandle cameraNode = scene.getCamera(scene.getRenderPass(pass).camera).node; - const TransformHandle cameraTransform = findTransformForNode(cameraNode); - scene.setTranslation(cameraTransform, glm::vec3(2.f, 3.f, 4.f)); - - updateScenes({}); - expectFrameWithSinglePass(renderable, projParams, glm::identity(), glm::translate(glm::vec3(-2.f, -3.f, -4.f))); - executeScene(); - Mock::VerifyAndClearExpectations(&device); -} - -TEST_F(ARenderExecutor, RendersRenderableWithRendererProjection) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle renderPass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass)); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - expectRenderingWithProjection(renderable, projMatrix); -} - -TEST_F(ARenderExecutor, RendersMultipleRenderableInstancesWithRendererProjection) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle renderPass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass)); - const uint32_t instanceCount = 23u; - scene.setRenderableInstanceCount(renderable, instanceCount); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - expectRenderingWithProjection(renderable, projMatrix, instanceCount); -} - -TEST_F(ARenderExecutor, RendersRenderableWithOrthographicProjection) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Orthographic); - const RenderPassHandle renderPass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass)); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - expectRenderingWithProjection(renderable, projMatrix); -} - -TEST_F(ARenderExecutor, RendersRenderableInTwoPassesUsingTheSameCamera) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle renderPass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle renderPass2 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass1)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass2)); - const CameraHandle camera1 = scene.getRenderPass(renderPass1).camera; - const CameraHandle camera2 = scene.getRenderPass(renderPass2).camera; - const NodeHandle cameraNode1 = scene.getCamera(camera1).node; - const NodeHandle cameraNode2 = scene.getCamera(camera2).node; - const TransformHandle cameraTransform1 = findTransformForNode(cameraNode1); - const TransformHandle cameraTransform2 = findTransformForNode(cameraNode2); - - scene.setRenderPassRenderOrder(renderPass1, 0); - scene.setRenderPassRenderOrder(renderPass2, 1); - - scene.setTranslation(cameraTransform1, glm::vec3(1.f, 2.f, 3.f)); - scene.setTranslation(cameraTransform2, glm::vec3(4.f, 5.f, 6.f)); - - updateScenes({ renderable1, renderable2 }); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - expectFrameRenderCommands(renderable1, glm::identity(), glm::translate(glm::vec3(-1.f, -2.f, -3.f)), projMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::translate(glm::vec3(-4.f, -5.f, -6.f)), projMatrix, false, EExpectedRenderStateChange::None); - executeScene(); - Mock::VerifyAndClearExpectations(&device); -} - -TEST_F(ARenderExecutor, RenderStatesAppliedOnceIfSameForMultipleRenderables) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle renderPass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle renderPass2 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass1)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass2)); - - updateScenes({ renderable1, renderable2 }); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), projMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), projMatrix, false, EExpectedRenderStateChange::None); - - executeScene(); - Mock::VerifyAndClearExpectations(&device); -} - -TEST_F(ARenderExecutor, RenderStatesAppliedForEachRenderableIfDifferent) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle renderPass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle renderPass2 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass1)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass2)); - - const RenderStateHandle state1 = scene.getRenderable(renderable1).renderState; - const RenderStateHandle state2 = scene.getRenderable(renderable2).renderState; - - scene.setRenderStateScissorTest(state1, EScissorTest::Disabled, {}); - scene.setRenderStateScissorTest(state2, EScissorTest::Enabled, {}); - scene.setRenderStateDepthWrite(state1, EDepthWrite::Enabled); - scene.setRenderStateDepthWrite(state2, EDepthWrite::Disabled); - scene.setRenderStateDepthFunc(state1, EDepthFunc::Always); - scene.setRenderStateDepthFunc(state2, EDepthFunc::Disabled); - scene.setRenderStateStencilFunc(state1, EStencilFunc::Always, 0u, 0u); - scene.setRenderStateStencilFunc(state2, EStencilFunc::Equal, 0u, 0u); - scene.setRenderStateBlendFactors(state1, EBlendFactor::ConstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor); - scene.setRenderStateBlendFactors(state2, EBlendFactor::DstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor); - scene.setRenderStateBlendOperations(state1, EBlendOperation::Add, EBlendOperation::Add); - scene.setRenderStateBlendOperations(state2, EBlendOperation::Subtract, EBlendOperation::Add); - scene.setRenderStateBlendColor(state1, glm::vec4(1.f)); - scene.setRenderStateBlendColor(state2, glm::vec4(.5f)); - scene.setRenderStateColorWriteMask(state1, EColorWriteFlag_Red); - scene.setRenderStateColorWriteMask(state2, EColorWriteFlag_Blue); - scene.setRenderStateCullMode(state1, ECullMode::BackFacing); - scene.setRenderStateCullMode(state2, ECullMode::Disabled); - - updateScenes({ renderable1, renderable2 }); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), projMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), projMatrix, false, EExpectedRenderStateChange::All); - - executeScene(); - Mock::VerifyAndClearExpectations(&device); -} - -TEST_F(ARenderExecutor, UpdatesModelMatrixWhenChangingTranslationRotationOrScalingOfNode) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); - const TransformHandle transform = addTransformToRenderable(renderable); - - scene.setTranslation(transform, glm::vec3(0.5f)); - updateScenes({ renderable }); - expectFrameWithSinglePass(renderable, projParams, glm::translate(glm::vec3(0.5f))); - executeScene(); - - Mock::VerifyAndClearExpectations(&device); - - scene.setTranslation(transform, glm::vec3(0.15f)); - scene.setScaling(transform, glm::vec3(0.25f)); - const auto rotation = glm::normalize(glm::quat(1.f, 2.f, 3.f, 4.f)); - scene.setRotation(transform, glm::vec4(rotation.x, rotation.y, rotation.z, rotation.w), ERotationType::Quaternion); - updateScenes({}); - expectFrameWithSinglePass(renderable, projParams, - glm::translate(glm::vec3(0.15f)) * glm::scale(glm::vec3(0.25f)) * glm::toMat4(rotation)); - executeScene(); -} - -TEST_F(ARenderExecutor, AppliesParentTransformationToBothChildNodes) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderGroupHandle group = createRenderGroup(pass); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), group); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), group); - const NodeHandle childNode1 = scene.getRenderable(renderable1).node; - const NodeHandle childNode2 = scene.getRenderable(renderable2).node; - const NodeHandle parentNode = sceneAllocator.allocateNode(); - scene.addChildToNode(parentNode, childNode1); - scene.addChildToNode(parentNode, childNode2); - const TransformHandle transformChild1 = addTransformToNode(childNode1); - const TransformHandle transformChild2 = addTransformToNode(childNode2); - const TransformHandle transformParent = addTransformToNode(parentNode); - - scene.setTranslation(transformParent, glm::vec3(0.25f)); - scene.setTranslation(transformChild1, glm::vec3(0.15f)); - scene.setTranslation(transformChild2, glm::vec3(0.05f)); - updateScenes({ renderable1, renderable2 }); - - const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - expectFrameRenderCommands(renderable1, glm::translate(glm::vec3(0.40f)), glm::identity(), projMatrix); - expectFrameRenderCommands(renderable2, glm::translate(glm::vec3(0.30f)), glm::identity(), projMatrix, false, EExpectedRenderStateChange::None); - executeScene(); - - Mock::VerifyAndClearExpectations(&device); - - scene.setTranslation(transformParent, glm::vec3(0.0f)); - updateScenes({}); - - expectActivateFramebufferRenderTarget(); - expectFrameRenderCommands(renderable1, glm::translate(glm::vec3(0.15f)), glm::identity(), projMatrix); - expectFrameRenderCommands(renderable2, glm::translate(glm::vec3(0.05f)), glm::identity(), projMatrix, false, EExpectedRenderStateChange::None); - executeScene(); - - Mock::VerifyAndClearExpectations(&device); -} - -TEST_F(ARenderExecutor, ExecutesBlitPass) -{ - const RenderBufferHandle sourceRenderBuffer = createRenderbuffer(); - const RenderBufferHandle destinationRenderBuffer = createRenderbuffer(); - const BlitPassHandle blitPassHandle = createBlitPass(sourceRenderBuffer, destinationRenderBuffer); - updateScenes({}); - - EXPECT_CALL(device, blitRenderTargets(_, _, _, _, _)).InSequence(deviceSequence); - executeScene(); - - scene.releaseRenderBuffer(sourceRenderBuffer); - scene.releaseRenderBuffer(destinationRenderBuffer); - scene.releaseBlitPass(blitPassHandle); - updateScenes({}); -} - -TEST_F(ARenderExecutor, ActivatesRenderTargetForRenderPassAfterExecutingBlitPass) -{ - const RenderBufferHandle sourceRenderBuffer = createRenderbuffer(); - const RenderBufferHandle destinationRenderBuffer = createRenderbuffer(); - const BlitPassHandle blitPass = createBlitPass(sourceRenderBuffer, destinationRenderBuffer); - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle firstRenderPass = createRenderPassWithCamera(projParams); - const RenderPassHandle secondRenderPass = createRenderPassWithCamera(projParams); - - scene.setRenderPassRenderOrder(firstRenderPass , 0); - scene.setBlitPassRenderOrder(blitPass , 1); - scene.setRenderPassRenderOrder(secondRenderPass , 2); - updateScenes({}); - - { - //first render pass - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - //blit pass - EXPECT_CALL(device, blitRenderTargets(_, _, _, _, _)).InSequence(deviceSequence); - - //second render pass - expectActivateFramebufferRenderTarget(false); - } - - executeScene(); - - scene.releaseRenderBuffer(sourceRenderBuffer); - scene.releaseRenderBuffer(destinationRenderBuffer); - scene.releaseBlitPass(blitPass); - updateScenes({}); -} - -TEST_F(ARenderExecutor, willRenderAllRenderablesIfWithinTimeBudget) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const DataInstances dataInstances = createTestDataInstance(); - const RenderableHandle renderable1 = createTestRenderable(dataInstances, createRenderGroup(pass1)); - const RenderableHandle renderable2 = createTestRenderable(dataInstances, createRenderGroup(pass1)); - const RenderableHandle renderable3 = createTestRenderable(dataInstances, createRenderGroup(pass2)); - const RenderableHandle renderable4 = createTestRenderable(dataInstances, createRenderGroup(pass2)); - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(pass1, targetHandle); - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - updateScenes({ renderable1 ,renderable2, renderable3, renderable4 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - { - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - expectDepthStencilDiscard(); - - expectActivateFramebufferRenderTarget(false); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable3, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::CausedByClear); - expectFrameRenderCommands(renderable4, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - } - - FrameTimer frameTimer; - frameTimer.startFrame(); - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, std::numeric_limits::max()); - - const auto renderIterator = executeScene(&frameTimer); - EXPECT_EQ(SceneRenderExecutionIterator(), renderIterator); // finished -} - -TEST_F(ARenderExecutor, willRenderAtLeastOneRenderableBatchIfExceededTimeBudget) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const DataInstances dataInstances = createTestDataInstance(); - const RenderGroupHandle renderGroup = createRenderGroup(pass1); - std::array batchRenderables; - for (auto& renderable : batchRenderables) - renderable = createTestRenderable(dataInstances, renderGroup); - const RenderableHandle renderableOutOfBudget = createTestRenderable(dataInstances, renderGroup); - - updateScenes({ batchRenderables.cbegin(), batchRenderables.cend() }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - - { - InSequence s; - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - - // one batch of renderables is rendered, first sets states - expectFrameRenderCommands(batchRenderables.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); - for (auto it = batchRenderables.cbegin() + 1; it != batchRenderables.cend(); ++it) - expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - - // otherRenderable is not rendered - UNUSED(renderableOutOfBudget); - } - - FrameTimer frameTimer; - frameTimer.startFrame(); - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, 0u); - - const auto renderIterator = executeScene(&frameTimer); - EXPECT_NE(SceneRenderExecutionIterator(), renderIterator); // not finished -} - -TEST_F(ARenderExecutor, willContinueRenderingWhereLeftOffLastRenderWhenInterrupted) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const DataInstances dataInstances = createTestDataInstance(); - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(pass1, targetHandle); - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - const RenderGroupHandle renderGroup1 = createRenderGroup(pass1); - const RenderGroupHandle renderGroup2 = createRenderGroup(pass2); - std::array batchRenderables1; - std::array batchRenderables2; - std::array batchRenderables3; - std::array batchRenderables4; - for (auto& renderable : batchRenderables1) - renderable = createTestRenderable(dataInstances, renderGroup1); - for (auto& renderable : batchRenderables2) - renderable = createTestRenderable(dataInstances, renderGroup1); - for (auto& renderable : batchRenderables3) - renderable = createTestRenderable(dataInstances, renderGroup2); - for (auto& renderable : batchRenderables4) - renderable = createTestRenderable(dataInstances, renderGroup2); - - RenderableVector allRenderables; - allRenderables.insert(allRenderables.end(), batchRenderables1.cbegin(), batchRenderables1.cend()); - allRenderables.insert(allRenderables.end(), batchRenderables2.cbegin(), batchRenderables2.cend()); - allRenderables.insert(allRenderables.end(), batchRenderables3.cbegin(), batchRenderables3.cend()); - allRenderables.insert(allRenderables.end(), batchRenderables4.cbegin(), batchRenderables4.cend()); - - updateScenes(allRenderables); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - FrameTimer frameTimer; - frameTimer.startFrame(); - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, 0u); - - { - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(); - expectFrameRenderCommands(batchRenderables1.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); - for (auto it = batchRenderables1.cbegin() + 1; it != batchRenderables1.cend(); ++it) - expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - } - SceneRenderExecutionIterator renderIterator = executeScene(&frameTimer); - EXPECT_EQ(0u, renderIterator.getRenderPassIdx()); - EXPECT_EQ(1u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); - EXPECT_EQ(1u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); - - { - expectActivateRenderTarget(renderTargetDeviceHandle); // no clear - expectFrameRenderCommands(batchRenderables2.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); - for (auto it = batchRenderables2.cbegin() + 1; it != batchRenderables2.cend(); ++it) - expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - } - renderContext.renderFrom = renderIterator; - renderIterator = executeScene(&frameTimer); - EXPECT_EQ(0u, renderIterator.getRenderPassIdx()); - EXPECT_EQ(2u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); - EXPECT_EQ(2u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); - - { - // last render interrupted at last renderable from pass1, so it continues from there including activating of pass1's RT - // even though there are no more renderables to render - expectActivateRenderTarget(renderTargetDeviceHandle); - expectDepthStencilDiscard(); - - expectActivateFramebufferRenderTarget(false); - expectClearRenderTarget(); - expectFrameRenderCommands(batchRenderables3.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); - for (auto it = batchRenderables3.cbegin() + 1; it != batchRenderables3.cend(); ++it) - expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - } - renderContext.renderFrom = renderIterator; - renderIterator = executeScene(&frameTimer); - EXPECT_EQ(1u, renderIterator.getRenderPassIdx()); - EXPECT_EQ(1u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); - EXPECT_EQ(3u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); - - { - expectActivateFramebufferRenderTarget(); - expectFrameRenderCommands(batchRenderables4.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); - for (auto it = batchRenderables4.cbegin() + 1; it != batchRenderables4.cend(); ++it) - expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - } - renderContext.renderFrom = renderIterator; - renderIterator = executeScene(&frameTimer); - EXPECT_EQ(1u, renderIterator.getRenderPassIdx()); - EXPECT_EQ(2u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); - EXPECT_EQ(4u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); - - { - // last render interrupted at last renderable from pass2, so it continues from there including activating of pass2's RT being FB - // even though there are no more renderables to render - expectActivateFramebufferRenderTarget(); - // no additional render operation (eg. blit passes) - } - renderContext.renderFrom = renderIterator; - renderIterator = executeScene(&frameTimer); - EXPECT_EQ(SceneRenderExecutionIterator(), renderIterator); // finished -} - -TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_MainOnly) -{ - const RenderPassHandle passMain = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - updateScenes({ renderable1 }); - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - - executeScene(); - - // main disp buffer was cleared, no pending clear in rendering context - EXPECT_EQ(EClearFlags_None, renderContext.displayBufferClearPending); -} - -TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_RenderTargetThenMain) -{ - const RenderPassHandle passRT = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderPassHandle passMain = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passRT)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); - const RenderTargetHandle rt = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(passRT, rt); - scene.setRenderPassRenderOrder(passRT, 1); - scene.setRenderPassRenderOrder(passMain, 2); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - updateScenes({ renderable1, renderable2 }); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectDepthStencilDiscard(); - - expectActivateFramebufferRenderTarget(false); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, ARenderExecutor::EExpectedRenderStateChange::CausedByClear); - - executeScene(); - - // main disp buffer was cleared, no pending clear in rendering context - EXPECT_EQ(EClearFlags_None, renderContext.displayBufferClearPending); -} - -TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_MainThenRenderTarget) -{ - const RenderPassHandle passMain = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderPassHandle passRT = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(passRT)); - const RenderTargetHandle rt = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(passRT, rt); - scene.setRenderPassRenderOrder(passMain, 1); - scene.setRenderPassRenderOrder(passRT, 2); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - updateScenes({ renderable1, renderable2 }); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectActivateRenderTarget(renderTargetDeviceHandle, false); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, ARenderExecutor::EExpectedRenderStateChange::CausedByClear); - expectDepthStencilDiscard(); - - executeScene(); - - // main disp buffer was cleared, no pending clear in rendering context - EXPECT_EQ(EClearFlags_None, renderContext.displayBufferClearPending); -} - -TEST_F(ARenderExecutor, DoesNotClearDispBufferWhenNotRenderingIntoIt_RenderTargetOnly) -{ - const RenderPassHandle passRT = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passRT)); - const RenderTargetHandle rt = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(passRT, rt); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - updateScenes({ renderable1 }); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectDepthStencilDiscard(); - - executeScene(); - - // main disp buffer was NOT cleared, pending clear still set in rendering context - EXPECT_EQ(EClearFlags_All, renderContext.displayBufferClearPending); -} - -TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_InterruptedRenderTargetThenMain) -{ - const RenderPassHandle passRT = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const DataInstances dataInstances = createTestDataInstance(); - const RenderTargetHandle rt = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(passRT, rt); - - const RenderGroupHandle renderGroup = createRenderGroup(passRT); - RenderableVector batchRenderables; - batchRenderables.resize(RenderExecutor::DefaultNumRenderablesToRenderInBetweenTimeBudgetChecks); - for (auto& renderable : batchRenderables) - renderable = createTestRenderable(dataInstances, renderGroup); - - const RenderPassHandle passMain = createRenderPassWithCamera(getDefaultProjectionParams(ECameraProjectionType::Perspective)); - const RenderableHandle renderableMain = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); - - scene.setRenderPassRenderOrder(passRT, 1); - scene.setRenderPassRenderOrder(passMain, 2); - - auto allRenderables = batchRenderables; - allRenderables.push_back(renderableMain); - updateScenes(allRenderables); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(EClearFlags_All); - - // one batch of renderables is rendered, first sets states - expectFrameRenderCommands(batchRenderables.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); - for (auto it = batchRenderables.cbegin() + 1; it != batchRenderables.cend(); ++it) - expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - - FrameTimer frameTimer; - frameTimer.startFrame(); - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, 0u); - - const auto renderIterator = executeScene(&frameTimer); - EXPECT_NE(SceneRenderExecutionIterator(), renderIterator); // not finished - - // main disp buffer was NOT cleared due to interrupt, pending clear kept in rendering context - EXPECT_EQ(EClearFlags_All, renderContext.displayBufferClearPending); - - expectActivateRenderTarget(renderTargetDeviceHandle); // will be activated to finish previous pass (although no more renderables to render in it) - expectDepthStencilDiscard(); - - expectActivateFramebufferRenderTarget(false); - expectClearRenderTarget(EClearFlags_All); - expectFrameRenderCommands(renderableMain, glm::identity(), glm::identity(), expectedProjectionMatrix); - - renderContext.renderFrom = renderIterator; - executeScene(); - - // main disp buffer was cleared, no pending clear in rendering context - EXPECT_EQ(EClearFlags_None, renderContext.displayBufferClearPending); -} - -TEST_F(ARenderExecutor, discardsDepthStencilAfterLastPassRenderingToFB_ifAllowedByRenderContext) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); - scene.setRenderPassClearFlag(pass2, EClearFlags_None); - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - updateScenes({ renderable1, renderable2 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - expectDepthStencilDiscard(); - - renderContext.displayBufferDepthDiscard = true; - executeScene(); -} - -TEST_F(ARenderExecutor, doesNotDiscardDepthStencil_ifNotAllowedByRenderContext) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); - scene.setRenderPassClearFlag(pass2, EClearFlags_None); - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - - updateScenes({ renderable1, renderable2 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - // no discard - - renderContext.displayBufferDepthDiscard = false; - executeScene(); -} - -TEST_F(ARenderExecutor, discardsDepthStencilAfterLastPassRenderingToFB_withRTPassInBetween) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); - const RenderableHandle renderable3 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass3)); - scene.setRenderPassClearFlag(pass3, EClearFlags_None); - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - scene.setRenderPassRenderOrder(pass3, 2); - - // second pass uses RT - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(pass2, targetHandle); - - updateScenes({ renderable1, renderable2, renderable3 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - // pass1 - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - // no discard - - // pass2 - expectActivateRenderTarget(renderTargetDeviceHandle, false); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::CausedByClear); - expectDepthStencilDiscard(); // discard for RT - - // pass3 - expectActivateFramebufferRenderTarget(false); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); - expectDepthStencilDiscard(); // discard for FB - - renderContext.displayBufferDepthDiscard = true; - executeScene(); -} - -TEST_F(ARenderExecutor, discardsDepthStencilWhenRenderToRTWithClear) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - const RenderTargetHandle targetHandle = createRenderTarget(16, 20); - scene.setRenderPassRenderTarget(pass1, targetHandle); - - updateScenes({ renderable1 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - expectDepthStencilDiscard(); - - executeScene(); -} - -TEST_F(ARenderExecutor, doesNotDiscardDepthStencilWhenRenderToRTWithNoDepthComponent) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); - const RenderTargetHandle targetHandle = sceneAllocator.allocateRenderTarget(); - const RenderBufferHandle colorBufferHandle = sceneAllocator.allocateRenderBuffer({ 1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }); - scene.addRenderTargetRenderBuffer(targetHandle, colorBufferHandle); - scene.setRenderPassRenderTarget(pass1, targetHandle); - - updateScenes({ renderable1 }); - - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); - // dicard would be here if there was a depth component to discard - - executeScene(); -} - -TEST_F(ARenderExecutor, discardsDepthStencilWhenRenderToRT_onlyIfNextPassClearsDepthAndStencil) -{ - const RenderTargetHandle targetHandle = createRenderTarget(1, 1); - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass4 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass5 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass6 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass7 = createRenderPassWithCamera(projParams); - const RenderPassHandle pass8 = createRenderPassWithCamera(projParams); - - scene.setRenderPassRenderTarget(pass1, targetHandle); - scene.setRenderPassRenderTarget(pass2, targetHandle); - scene.setRenderPassRenderTarget(pass3, targetHandle); - scene.setRenderPassRenderTarget(pass4, targetHandle); - scene.setRenderPassRenderTarget(pass5, targetHandle); - scene.setRenderPassRenderTarget(pass6, targetHandle); - // pass7 is FB pass - scene.setRenderPassRenderTarget(pass8, targetHandle); - - scene.setRenderPassClearFlag(pass1, EClearFlags_None); - scene.setRenderPassClearFlag(pass2, EClearFlags_Color); - scene.setRenderPassClearFlag(pass3, EClearFlags_Depth); - scene.setRenderPassClearFlag(pass4, EClearFlags_Stencil); - scene.setRenderPassClearFlag(pass5, EClearFlags_Color | EClearFlags_Depth); - scene.setRenderPassClearFlag(pass6, EClearFlags_Color | EClearFlags_Stencil); - scene.setRenderPassClearFlag(pass7, EClearFlags_All); - scene.setRenderPassClearFlag(pass8, EClearFlags_Depth | EClearFlags_Stencil); - - scene.setRenderPassRenderOrder(pass1, 0); - scene.setRenderPassRenderOrder(pass2, 1); - scene.setRenderPassRenderOrder(pass3, 2); - scene.setRenderPassRenderOrder(pass4, 3); - scene.setRenderPassRenderOrder(pass5, 4); - scene.setRenderPassRenderOrder(pass6, 5); - scene.setRenderPassRenderOrder(pass7, 6); - scene.setRenderPassRenderOrder(pass8, 7); - - updateScenes({}); - - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - expectActivateRenderTarget(renderTargetDeviceHandle); - // pass 1, no clear - expectClearRenderTarget(EClearFlags_Color); // pass 2 - expectClearRenderTarget(EClearFlags_Depth); // pass 3 - expectClearRenderTarget(EClearFlags_Stencil); // pass 4 - expectClearRenderTarget(EClearFlags_Color | EClearFlags_Depth); // pass 5 - expectClearRenderTarget(EClearFlags_Color | EClearFlags_Stencil); // pass 6 - expectDepthStencilDiscard(); // next pass (pass8) rendering to RT clears depth+stencil, can discard - - // pass 7 to FB - expectActivateFramebufferRenderTarget(false); - expectClearRenderTarget(); - expectDepthStencilDiscard(); // no other pass rendering to FB, can discard - - // pass 8 - expectActivateRenderTarget(renderTargetDeviceHandle, false); - expectClearRenderTarget(EClearFlags_Depth | EClearFlags_Stencil); - // next pass is pass1 next frame which does not clear, therefore cannot discard here even though at end of frame - - renderContext.displayBufferDepthDiscard = true; - executeScene(); -} - -TEST_F(ARenderExecutor, doesNotDiscardDepthStencilIfUpcomingPassUsesItAsBlitPassSource) -{ - const RenderTargetHandle targetHandle = createRenderTarget(1, 1); - const RenderBufferHandle depthBuffer = scene.getRenderTargetRenderBuffer(targetHandle, 1u); - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - - const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); - const BlitPassHandle pass2 = createBlitPass(depthBuffer, createRenderbuffer()); - const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); - - scene.setRenderPassRenderTarget(pass1, targetHandle); - scene.setRenderPassRenderTarget(pass3, targetHandle); - - scene.setRenderPassClearFlag(pass1, EClearFlags_All); - scene.setRenderPassClearFlag(pass3, EClearFlags_All); - - scene.setRenderPassRenderOrder(pass1, 0); - scene.setBlitPassRenderOrder(pass2, 1); - scene.setRenderPassRenderOrder(pass3, 2); - - updateScenes({}); - - // pass1 - const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); - expectActivateRenderTarget(renderTargetDeviceHandle); - expectClearRenderTarget(); - // no discard due to blit using depth as source next - - // pass2 blit - EXPECT_CALL(device, blitRenderTargets(_, _, _, _, _)).InSequence(deviceSequence); - - // pass3 - expectActivateRenderTarget(renderTargetDeviceHandle, false); - expectClearRenderTarget(); - expectDepthStencilDiscard(); - - executeScene(); -} - -TEST_F(ARenderExecutorTimeMs, SetsActiveShaderAnimationFlagOnRenderedScene) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(true, false), createRenderGroup(pass)); - scene.setRenderPassClearFlag(pass, ramses_internal::EClearFlags::EClearFlags_None); - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - updateScenes({ renderable }); - - { - InSequence seq; - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable, - glm::identity(), - glm::identity(), - expectedProjectionMatrix, - true, - EExpectedRenderStateChange::All, - 1, - false, - EffectUniformTime::GetMilliseconds({})); - } - - EXPECT_FALSE(scene.hasActiveShaderAnimation()); - executeScene(); - EXPECT_TRUE(scene.hasActiveShaderAnimation()); -} - -TEST_F(ARenderExecutorTimeMs, UniformTimeMsStartsAtZeroAfterEffectTimeSync) -{ - const auto projParams = getDefaultProjectionParams(ECameraProjectionType::Perspective); - const RenderPassHandle pass = createRenderPassWithCamera(projParams); - const RenderableHandle renderable = createTestRenderable(createTestDataInstance(true, false), createRenderGroup(pass)); - scene.setRenderPassClearFlag(pass, ramses_internal::EClearFlags::EClearFlags_None); - const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); - - scene.setEffectTimeSync(FlushTime::Clock::now()); - - updateScenes({ renderable }); - - { - InSequence seq; - - expectActivateFramebufferRenderTarget(); - expectClearRenderTarget(); - expectFrameRenderCommands(renderable, - glm::identity(), - glm::identity(), - expectedProjectionMatrix, - true, - EExpectedRenderStateChange::All, - 1, - false, - 0); - } - - executeScene(); -} - -} diff --git a/renderer/RendererLib/RendererLib/test/RendererCommandBufferTest.cpp b/renderer/RendererLib/RendererLib/test/RendererCommandBufferTest.cpp deleted file mode 100644 index f03ccbe00..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererCommandBufferTest.cpp +++ /dev/null @@ -1,325 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gtest/gtest.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererCommandVisitorMock.h" -#include "Utils/ThreadBarrier.h" -#include -#include - -namespace ramses_internal { -using namespace testing; - -class ARendererCommandBuffer : public ::testing::Test -{ -protected: - void fillBufferWithCommands(RendererCommandBuffer& buffer); - void expectFilledBufferVisits(); - - StrictMock visitor; - - const SceneId sceneId{ 12u }; - const SceneInfo sceneInfo{ sceneId, "testScene" }; - const DisplayHandle displayHandle{ 1u }; - const DisplayConfig displayConfig; - const OffscreenBufferHandle obHandle{ 6u }; - const SceneId providerSceneId; - const SceneId consumerSceneId; - const DataSlotId providerId{ 1 }; - const DataSlotId consumerId{ 2 }; - const glm::vec4 clearColor{ 1.f, 0.f, 0.5f, 0.2f }; - const StreamBufferHandle streamBuffer{ 7u }; - const WaylandIviSurfaceId source{ 8u }; - const WaylandIviLayerId layer{ 9u }; - IBinaryShaderCache* dummyBinaryShaderCache = reinterpret_cast(std::uintptr_t{ 0x1 }); -}; - -void ARendererCommandBuffer::fillBufferWithCommands(RendererCommandBuffer& buffer) -{ - buffer.enqueueCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode_LocalAndRemote }); - buffer.enqueueCommand(RendererCommand::SceneUnpublished{ sceneId }); - buffer.enqueueCommand(RendererCommand::ReceiveScene{ sceneInfo }); - buffer.enqueueCommand(RendererCommand::SetSceneState{ sceneId, RendererSceneState::Rendered }); - buffer.enqueueCommand(RendererCommand::SetSceneMapping{ sceneId, displayHandle }); - buffer.enqueueCommand(RendererCommand::SetSceneDisplayBufferAssignment{ sceneId, obHandle, -13 }); - buffer.enqueueCommand(RendererCommand::CreateDisplay{ displayHandle, displayConfig, dummyBinaryShaderCache }); - buffer.enqueueCommand(RendererCommand::DestroyDisplay{ displayHandle }); - buffer.enqueueCommand(RendererCommand::LinkData{ providerSceneId, providerId, consumerSceneId, consumerId }); - buffer.enqueueCommand(RendererCommand::UnlinkData{ consumerSceneId, consumerId }); - buffer.enqueueCommand(RendererCommand::SetClearFlags{ displayHandle, obHandle, EClearFlags_Color }); - buffer.enqueueCommand(RendererCommand::SetClearColor{ displayHandle, obHandle, clearColor }); - buffer.enqueueCommand(RendererCommand::SetExterallyOwnedWindowSize{ displayHandle, 1u, 2u}); - buffer.enqueueCommand(RendererCommand::CreateStreamBuffer{ displayHandle, streamBuffer, source }); - buffer.enqueueCommand(RendererCommand::DestroyStreamBuffer{ displayHandle, streamBuffer }); - buffer.enqueueCommand(RendererCommand::LinkStreamBuffer{ streamBuffer, consumerSceneId, consumerId }); - buffer.enqueueCommand(RendererCommand::SCListIviSurfaces{}); - buffer.enqueueCommand(RendererCommand::SCSetIviSurfaceVisibility{ source, true }); - buffer.enqueueCommand(RendererCommand::SCSetIviSurfaceOpacity{ source, 0.5f }); - buffer.enqueueCommand(RendererCommand::SCSetIviLayerVisibility{ layer, true }); - buffer.enqueueCommand(RendererCommand::SCAddIviSurfaceToIviLayer{ source, layer }); - buffer.enqueueCommand(RendererCommand::SCRemoveIviSurfaceFromIviLayer{ source, layer }); - buffer.enqueueCommand(RendererCommand::SCDestroyIviSurface{ source }); -} - -void ARendererCommandBuffer::expectFilledBufferVisits() -{ - InSequence seq; - EXPECT_CALL(visitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)); - EXPECT_CALL(visitor, handleSceneUnpublished(sceneId)); - EXPECT_CALL(visitor, handleSceneReceived(sceneInfo)); - EXPECT_CALL(visitor, setSceneState(sceneId, RendererSceneState::Rendered)); - EXPECT_CALL(visitor, setSceneMapping(sceneId, displayHandle)); - EXPECT_CALL(visitor, setSceneDisplayBufferAssignment(sceneId, obHandle, -13)); - EXPECT_CALL(visitor, createDisplayContext(displayConfig, displayHandle, dummyBinaryShaderCache)); - EXPECT_CALL(visitor, destroyDisplayContext(displayHandle)); - EXPECT_CALL(visitor, handleSceneDataLinkRequest(providerSceneId, providerId, consumerSceneId, consumerId)); - EXPECT_CALL(visitor, handleDataUnlinkRequest(consumerSceneId, consumerId)); - EXPECT_CALL(visitor, handleSetClearFlags(displayHandle, obHandle, EClearFlags_Color)); - EXPECT_CALL(visitor, handleSetClearColor(displayHandle, obHandle, clearColor)); - EXPECT_CALL(visitor, handleSetExternallyOwnedWindowSize(displayHandle, 1u, 2u)); - EXPECT_CALL(visitor, handleBufferCreateRequest(streamBuffer, displayHandle, source)); - EXPECT_CALL(visitor, handleBufferDestroyRequest(streamBuffer, displayHandle)); - EXPECT_CALL(visitor, handleBufferToSceneDataLinkRequest(streamBuffer, consumerSceneId, consumerId)); - EXPECT_CALL(visitor, systemCompositorListIviSurfaces()); - EXPECT_CALL(visitor, systemCompositorSetIviSurfaceVisibility(source, true)); - EXPECT_CALL(visitor, systemCompositorSetIviSurfaceOpacity(source, 0.5f)); - EXPECT_CALL(visitor, systemCompositorSetIviLayerVisibility(layer, true)); - EXPECT_CALL(visitor, systemCompositorAddIviSurfaceToIviLayer(source, layer)); - EXPECT_CALL(visitor, systemCompositorRemoveIviSurfaceFromIviLayer(source, layer)); - EXPECT_CALL(visitor, systemCompositorDestroyIviSurface(source)); -} - -TEST_F(ARendererCommandBuffer, canEnqueueCommands) -{ - RendererCommandBuffer buffer; - fillBufferWithCommands(buffer); - - expectFilledBufferVisits(); - visitor.visit(buffer); -} - -TEST_F(ARendererCommandBuffer, canAddCommandsFromCommandsContainer) -{ - RendererCommandBuffer buffer; - fillBufferWithCommands(buffer); - RendererCommands cmds; - buffer.swapCommands(cmds); - - RendererCommandBuffer destinationContainer; - destinationContainer.addAndConsumeCommandsFrom(cmds); - - // expect no visits - visitor.visit(buffer); - - expectFilledBufferVisits(); - visitor.visit(destinationContainer); -} - -TEST_F(ARendererCommandBuffer, canAddCommandsFromTwoContainers) -{ - RendererCommandBuffer destinationContainer; - - RendererCommandBuffer buffer; - RendererCommands cmds; - fillBufferWithCommands(buffer); - buffer.swapCommands(cmds); - destinationContainer.addAndConsumeCommandsFrom(cmds); - - // expect no visits on consumed buffer - visitor.visit(buffer); - - fillBufferWithCommands(buffer); - buffer.swapCommands(cmds); - destinationContainer.addAndConsumeCommandsFrom(cmds); - - // expect no visits on consumed buffer - visitor.visit(buffer); - - InSequence seq; - expectFilledBufferVisits(); - expectFilledBufferVisits(); - visitor.visit(destinationContainer); -} - -TEST_F(ARendererCommandBuffer, addCommandsFromVariousContainersToContainerInAnotherThread) -{ - // 2 threads providing commands (e.g. scene control and renderer HL cmd queues) - // 1 thread consuming those and adding to destContainer (e.g. dispatcher queue) - // 1 thread consuming destContainer and adding to finalContainer (e.g. executor in displaythread) - - RendererCommandBuffer destContainer; - RendererCommandBuffer finalContainer; - - ThreadBarrier initBarrier{ 4 }; - ThreadBarrier producersDoneBarrier{ 4 }; - ThreadBarrier consumerProducerDoneBarrier{ 2 }; - ThreadBarrier allDoneBarrier{ 4 }; - - std::thread cmdProducer1{ [&]() - { - initBarrier.wait(); - RendererCommandBuffer buffer; - { - buffer.enqueueCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode_LocalAndRemote }); - buffer.enqueueCommand(RendererCommand::SceneUnpublished{ sceneId }); - } - RendererCommands cmds; - buffer.swapCommands(cmds); - destContainer.addAndConsumeCommandsFrom(cmds); - producersDoneBarrier.wait(); - allDoneBarrier.wait(); - } }; - - std::thread cmdProducer2{ [&]() - { - initBarrier.wait(); - RendererCommandBuffer buffer; - { - buffer.enqueueCommand(RendererCommand::ReceiveScene{ sceneInfo }); - buffer.enqueueCommand(RendererCommand::SetSceneState{ sceneId, RendererSceneState::Rendered }); - } - RendererCommands cmds; - buffer.swapCommands(cmds); - destContainer.addAndConsumeCommandsFrom(cmds); - producersDoneBarrier.wait(); - allDoneBarrier.wait(); - } }; - - std::thread cmdConsumerAndProducer{ [&]() - { - initBarrier.wait(); - RendererCommandBuffer buffer; - { - buffer.enqueueCommand(RendererCommand::SetSceneMapping{ sceneId, displayHandle }); - buffer.enqueueCommand(RendererCommand::SetSceneDisplayBufferAssignment{ sceneId, obHandle, -13 }); - } - RendererCommands cmds; - buffer.swapCommands(cmds); - destContainer.addAndConsumeCommandsFrom(cmds); - producersDoneBarrier.wait(); - - destContainer.swapCommands(cmds); - finalContainer.addAndConsumeCommandsFrom(cmds); - { - buffer.enqueueCommand(RendererCommand::CreateDisplay{ displayHandle, displayConfig, dummyBinaryShaderCache }); - buffer.enqueueCommand(RendererCommand::DestroyDisplay{ displayHandle }); - } - buffer.swapCommands(cmds); - finalContainer.addAndConsumeCommandsFrom(cmds); - consumerProducerDoneBarrier.wait(); - allDoneBarrier.wait(); - } }; - - std::thread cmdConsumer{ [&]() - { - initBarrier.wait(); - producersDoneBarrier.wait(); - consumerProducerDoneBarrier.wait(); - - Sequence s1; - Sequence s2; - Sequence s3; - Sequence s4; - EXPECT_CALL(visitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)).InSequence(s1); - EXPECT_CALL(visitor, handleSceneUnpublished(sceneId)).InSequence(s1); - EXPECT_CALL(visitor, handleSceneReceived(sceneInfo)).InSequence(s2); - EXPECT_CALL(visitor, setSceneState(sceneId, RendererSceneState::Rendered)).InSequence(s2); - EXPECT_CALL(visitor, setSceneMapping(sceneId, displayHandle)).InSequence(s3); - EXPECT_CALL(visitor, setSceneDisplayBufferAssignment(sceneId, obHandle, -13)).InSequence(s3); - EXPECT_CALL(visitor, createDisplayContext(displayConfig, displayHandle, dummyBinaryShaderCache)).InSequence(s4); - EXPECT_CALL(visitor, destroyDisplayContext(displayHandle)).InSequence(s4); - visitor.visit(finalContainer); - - allDoneBarrier.wait(); - } }; - - cmdProducer1.join(); - cmdProducer2.join(); - cmdConsumerAndProducer.join(); - cmdConsumer.join(); -} - -TEST_F(ARendererCommandBuffer, notifiesAnotherThreadAboutNewCommands) -{ - RendererCommandBuffer buffer; - - ThreadBarrier initialCommandsBarrier{ 2 }; - ThreadBarrier otherCommandsBarrier{ 2 }; - ThreadBarrier doneBarrier{ 2 }; - - bool producerCanContinue = false; - std::mutex producerCanContinueLock; - std::condition_variable producerCanContinueCvar; - - std::thread cmdProducer{ [&]() - { - // some init cmd - buffer.enqueueCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode_LocalAndRemote }); - initialCommandsBarrier.wait(); - - // add another cmd and wait for listener set - buffer.enqueueCommand(RendererCommand::SceneUnpublished{ sceneId }); - { - std::unique_lock lk{ producerCanContinueLock }; - producerCanContinueCvar.wait(lk, [&] { return producerCanContinue; }); - } - - // this cmd is guaranteed to be pushed after consumer waiting using cvar - buffer.enqueueCommand(RendererCommand::DestroyDisplay{ DisplayHandle{} }); - otherCommandsBarrier.wait(); - - buffer.enqueueCommand(RendererCommand::DestroyDisplay{ DisplayHandle{} }); - doneBarrier.wait(); - } }; - - std::thread cmdListener{ [&]() - { - initialCommandsBarrier.wait(); - - { - std::unique_lock lk{ producerCanContinueLock }; - producerCanContinue = true; - producerCanContinueCvar.notify_one(); - } - - // wait forever (20s) till new command comes and do something with it - RendererCommands cmds; - buffer.blockingSwapCommands(cmds, std::chrono::milliseconds{20000}); - ASSERT_FALSE(cmds.empty()); - - // any other commands will be pushed without notification - otherCommandsBarrier.wait(); - - doneBarrier.wait(); - } }; - - cmdProducer.join(); - cmdListener.join(); -} - -TEST_F(ARendererCommandBuffer, canInterruptBlockingSwapCommands) -{ - RendererCommandBuffer buffer; - ThreadBarrier initialCommandsBarrier{ 2 }; - - std::thread unblocker{ [&]() - { - initialCommandsBarrier.wait(); - buffer.interruptBlockingSwapCommands(); - } }; - - initialCommandsBarrier.wait(); - RendererCommands cmds; - buffer.blockingSwapCommands(cmds, std::chrono::milliseconds{1000000000}); // wait forever - - EXPECT_TRUE(cmds.empty()); - - unblocker.join(); -} -} diff --git a/renderer/RendererLib/RendererLib/test/RendererCommandExecutorTest.cpp b/renderer/RendererLib/RendererLib/test/RendererCommandExecutorTest.cpp deleted file mode 100644 index f95d685c1..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererCommandExecutorTest.cpp +++ /dev/null @@ -1,498 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "ResourceDeviceHandleAccessorMock.h" -#include "RendererMock.h" -#include "DisplayControllerMock.h" -#include "RenderBackendMock.h" -#include "RendererLib/RendererCommandExecutor.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/RendererResourceManager.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererLib/RendererSceneControlLogic.h" -#include "RendererLib/RendererCommands.h" -#include "RendererEventCollector.h" -#include "ComponentMocks.h" -#include "RendererSceneUpdaterFacade.h" -#include "RendererSceneControlLogicMock.h" -#include "SceneReferenceLogicMock.h" -#include "SystemCompositorControllerMock.h" -#include "EmbeddedCompositingManagerMock.h" -#include "RendererSceneEventSenderMock.h" -#include "RendererSceneUpdaterMock.h" -#include "Utils/ThreadLocalLog.h" - -namespace ramses_internal { - -class ARendererCommandExecutor : public ::testing::Test -{ -public: - ARendererCommandExecutor() - : m_rendererScenes(m_rendererEventCollector) - , m_expirationMonitor(m_rendererScenes, m_rendererEventCollector, m_rendererStatistics) - , m_renderer(m_displayHandle, m_rendererScenes, m_rendererEventCollector, m_expirationMonitor, m_rendererStatistics) - , m_sceneStateExecutor(m_renderer, m_sceneEventSender, m_rendererEventCollector) - , m_commandExecutor(m_renderer, m_commandBuffer, m_sceneUpdater, m_sceneControlLogic, m_rendererEventCollector, m_frameTimer) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - // enable SC - ON_CALL(m_renderer.m_platform, getSystemCompositorController()).WillByDefault(Return(&m_renderer.m_platform.systemCompositorControllerMock)); - } - - void doCommandExecutorLoop() - { - m_renderer.getProfilerStatistics().markFrameFinished(std::chrono::microseconds{ 0u }); - m_commandExecutor.executePendingCommands(); - } - - void addDisplayController() - { - ASSERT_FALSE(m_renderer.hasDisplayController()); - m_renderer.createDisplayContext({}); - } - - void removeDisplayController() - { - ASSERT_TRUE(m_renderer.hasDisplayController()); - EXPECT_CALL(m_renderer.m_platform.renderBackendMock.windowMock, getWaylandIviSurfaceID()); - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, destroySurface(_)); - EXPECT_CALL(m_renderer.m_platform, destroyRenderBackend()); - m_renderer.destroyDisplayContext(); - } - - StrictMock& getDisplayControllerMock() - { - return *m_renderer.m_displayController; - } - - RendererEventVector consumeRendererEvents() - { - RendererEventVector events; - RendererEventVector dummy; - m_rendererEventCollector.appendAndConsumePendingEvents(events, dummy); - return events; - } - - RendererEventVector consumeSceneControlEvents() - { - RendererEventVector events; - RendererEventVector dummy; - m_rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - return events; - } - -protected: - DisplayHandle m_displayHandle{ 1u }; - RendererEventCollector m_rendererEventCollector; - RendererScenes m_rendererScenes; - SceneExpirationMonitor m_expirationMonitor; - StrictMock m_sceneRefLogic; - RendererStatistics m_rendererStatistics; - StrictMock m_renderer; - StrictMock m_sceneEventSender; - RendererCommandBuffer m_commandBuffer; - SceneStateExecutor m_sceneStateExecutor; - FrameTimer m_frameTimer; - StrictMock m_sceneUpdater; - StrictMock m_sceneControlLogic; - RendererCommandExecutor m_commandExecutor; -}; - -TEST_F(ARendererCommandExecutor, setSceneState) -{ - constexpr SceneId sceneId{ 123 }; - m_commandBuffer.enqueueCommand(RendererCommand::SetSceneState{ sceneId, RendererSceneState::Ready }); - - EXPECT_CALL(m_sceneControlLogic, setSceneState(sceneId, RendererSceneState::Ready)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setSceneMappingIsNoop) -{ - constexpr SceneId sceneId{ 123 }; - constexpr DisplayHandle display{ 2 }; - m_commandBuffer.enqueueCommand(RendererCommand::SetSceneMapping{ sceneId, display }); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setSceneDisplayBufferAssignment) -{ - constexpr SceneId sceneId{ 123 }; - constexpr OffscreenBufferHandle ob{ 2 }; - m_commandBuffer.enqueueCommand(RendererCommand::SetSceneDisplayBufferAssignment{ sceneId, ob, -13 }); - - EXPECT_CALL(m_sceneControlLogic, setSceneDisplayBufferAssignment(sceneId, ob, -13)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, readPixelsFromDisplayBuffer) -{ - constexpr DisplayHandle display{ 1u }; - constexpr OffscreenBufferHandle buffer{ 2u }; - - m_commandBuffer.enqueueCommand(RendererCommand::ReadPixels{ display, buffer, 1, 2, 3, 4, true, true, "file" }); - EXPECT_CALL(m_sceneUpdater, handleReadPixels(buffer, _)).WillOnce(Invoke([&](auto, auto&& info) - { - EXPECT_EQ(1u, info.rectangle.x); - EXPECT_EQ(2u, info.rectangle.y); - EXPECT_EQ(3u, info.rectangle.width); - EXPECT_EQ(4u, info.rectangle.height); - EXPECT_EQ(std::string("file"), info.filename); - EXPECT_TRUE(info.fullScreen); - EXPECT_TRUE(info.sendViaDLT); - })); - doCommandExecutorLoop(); - - EXPECT_TRUE(consumeRendererEvents().empty()); // events are only added by the WindowedRenderer -} - -TEST_F(ARendererCommandExecutor, createOffscreenBuffer) -{ - constexpr DisplayHandle display{ 1 }; - constexpr OffscreenBufferHandle buffer{ 2 }; - - m_commandBuffer.enqueueCommand(RendererCommand::CreateOffscreenBuffer{ display, buffer, 1u, 2u, 3u, true, ERenderBufferType_DepthStencilBuffer }); - EXPECT_CALL(m_sceneUpdater, handleBufferCreateRequest(buffer, 1u, 2u, 3u, true, ERenderBufferType_DepthStencilBuffer)).WillOnce(Return(true)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, createDmaOffscreenBuffer) -{ - constexpr DisplayHandle display{ 1 }; - constexpr OffscreenBufferHandle buffer{ 2 }; - constexpr DmaBufferFourccFormat fourccFormat{ 555u }; - constexpr DmaBufferUsageFlags usageFlags{ 666u }; - constexpr DmaBufferModifiers modifiers{ 777u }; - - m_commandBuffer.enqueueCommand(RendererCommand::CreateDmaOffscreenBuffer{ display, buffer, 1u, 2u, fourccFormat, usageFlags, modifiers }); - EXPECT_CALL(m_sceneUpdater, handleDmaBufferCreateRequest(buffer, 1u, 2u, fourccFormat, usageFlags, modifiers)).WillOnce(Return(true)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, destroysOffscreenBufferAndGenerateEvent) -{ - constexpr DisplayHandle display{ 1 }; - constexpr OffscreenBufferHandle buffer{ 2 }; - - m_commandBuffer.enqueueCommand(RendererCommand::DestroyOffscreenBuffer{ display, buffer }); - EXPECT_CALL(m_sceneUpdater, handleBufferDestroyRequest(buffer)).WillOnce(Return(true)); - doCommandExecutorLoop(); - - const RendererEventVector events = consumeRendererEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(ERendererEventType::OffscreenBufferDestroyed, events.front().eventType); - EXPECT_EQ(display, events.front().displayHandle); - EXPECT_EQ(buffer, events.front().offscreenBuffer); - EXPECT_EQ(-1, events.front().dmaBufferFD); - EXPECT_EQ(0u, events.front().dmaBufferStride); -} - -TEST_F(ARendererCommandExecutor, destroysOffscreenBufferAndGenerateEventOnFail) -{ - constexpr DisplayHandle display{ 1 }; - constexpr OffscreenBufferHandle buffer{ 2 }; - - m_commandBuffer.enqueueCommand(RendererCommand::DestroyOffscreenBuffer{ display, buffer }); - EXPECT_CALL(m_sceneUpdater, handleBufferDestroyRequest(buffer)).WillOnce(Return(false)); - doCommandExecutorLoop(); - - const RendererEventVector events = consumeRendererEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(ERendererEventType::OffscreenBufferDestroyFailed, events.front().eventType); - EXPECT_EQ(display, events.front().displayHandle); - EXPECT_EQ(buffer, events.front().offscreenBuffer); -} - -TEST_F(ARendererCommandExecutor, triggersLinkOffscreenBufferToConsumer) -{ - const OffscreenBufferHandle buffer(1u); - const SceneId consumerScene(2u); - const DataSlotId consumerData(3u); - m_commandBuffer.enqueueCommand(RendererCommand::LinkOffscreenBuffer{ buffer, consumerScene, consumerData }); - - EXPECT_CALL(m_sceneUpdater, handleBufferToSceneDataLinkRequest(buffer, consumerScene, consumerData)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, createStreamBuffer) -{ - constexpr StreamBufferHandle buffer{ 123u }; - constexpr DisplayHandle display{ 1 }; - constexpr WaylandIviSurfaceId source{ 2 }; - - m_commandBuffer.enqueueCommand(RendererCommand::CreateStreamBuffer{ display, buffer, source }); - EXPECT_CALL(m_sceneUpdater, handleBufferCreateRequest(buffer, source)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, destroysStreamBuffer) -{ - constexpr StreamBufferHandle buffer{ 123u }; - constexpr DisplayHandle display{ 1 }; - - m_commandBuffer.enqueueCommand(RendererCommand::DestroyStreamBuffer{ display, buffer }); - EXPECT_CALL(m_sceneUpdater, handleBufferDestroyRequest(buffer)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, linkStreamBufferToConsumer) -{ - constexpr StreamBufferHandle buffer{ 1u }; - constexpr SceneId consumerScene{ 2u }; - constexpr DataSlotId consumerData{ 3u }; - m_commandBuffer.enqueueCommand(RendererCommand::LinkStreamBuffer{ buffer, consumerScene, consumerData }); - - EXPECT_CALL(m_sceneUpdater, handleBufferToSceneDataLinkRequest(buffer, consumerScene, consumerData)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, createExternalBuffer) -{ - constexpr ExternalBufferHandle buffer{ 123u }; - constexpr DisplayHandle display{ 1 }; - - m_commandBuffer.enqueueCommand(RendererCommand::CreateExternalBuffer{ display, buffer }); - EXPECT_CALL(m_sceneUpdater, handleExternalBufferCreateRequest(buffer)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, destroysExternalBuffer) -{ - constexpr ExternalBufferHandle buffer{ 123u }; - constexpr DisplayHandle display{ 1 }; - - m_commandBuffer.enqueueCommand(RendererCommand::DestroyExternalBuffer{ display, buffer }); - EXPECT_CALL(m_sceneUpdater, handleExternalBufferDestroyRequest(buffer)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, linkExternalBufferToConsumer) -{ - constexpr ExternalBufferHandle buffer{ 1u }; - constexpr SceneId consumerScene{ 2u }; - constexpr DataSlotId consumerData{ 3u }; - m_commandBuffer.enqueueCommand(RendererCommand::LinkExternalBuffer{ buffer, consumerScene, consumerData }); - - EXPECT_CALL(m_sceneUpdater, handleBufferToSceneDataLinkRequest(buffer, consumerScene, consumerData)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, createsAndDestroysDisplay) -{ - const DisplayHandle displayHandle(33u); - const DisplayConfig displayConfig; - - m_commandBuffer.enqueueCommand(RendererCommand::CreateDisplay{ displayHandle, displayConfig, nullptr }); - EXPECT_CALL(m_sceneUpdater, createDisplayContext(displayConfig, nullptr)); - doCommandExecutorLoop(); - - m_commandBuffer.enqueueCommand(RendererCommand::DestroyDisplay{ displayHandle }); - EXPECT_CALL(m_sceneUpdater, destroyDisplayContext()); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, linkData) -{ - constexpr SceneId sceneProviderId(1u); - constexpr SceneId sceneConsumerId(2u); - constexpr DataSlotId providerSlotId(3u); - constexpr DataSlotId consumerSlotId(4u); - - m_commandBuffer.enqueueCommand(RendererCommand::LinkData{ sceneProviderId, providerSlotId, sceneConsumerId, consumerSlotId }); - EXPECT_CALL(m_sceneUpdater, handleSceneDataLinkRequest(sceneProviderId, providerSlotId, sceneConsumerId, consumerSlotId)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, unlinkData) -{ - constexpr SceneId sceneConsumerId(2u); - constexpr DataSlotId consumerSlotId(4u); - - m_commandBuffer.enqueueCommand(RendererCommand::UnlinkData{ sceneConsumerId, consumerSlotId }); - EXPECT_CALL(m_sceneUpdater, handleDataUnlinkRequest(sceneConsumerId, consumerSlotId)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, processesSceneActions) -{ - const SceneId sceneId(7u); - const NodeHandle node(33u); - - SceneUpdate actions; - SceneActionCollectionCreator creator(actions.actions); - creator.allocateNode(0u, node); - actions.flushInfos.flushCounter = 1u; - actions.flushInfos.containsValidInformation = true; - m_commandBuffer.enqueueCommand(RendererCommand::UpdateScene{ sceneId, std::move(actions) }); - - EXPECT_CALL(m_sceneUpdater, handleSceneUpdate(sceneId, _)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setsLayerVisibility) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviLayerVisibility{ WaylandIviLayerId(23u), true }); - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviLayerVisibility{ WaylandIviLayerId(25u), false }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(23u),true)); - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(25u),false)); - - m_commandExecutor.executePendingCommands(); -} - -TEST_F(ARendererCommandExecutor, setsSurfaceVisibility) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceVisibility{ WaylandIviSurfaceId(11u), true }); - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceVisibility{ WaylandIviSurfaceId(22u), false }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(11u),true)); - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(22u),false)); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, listsIviSurfaces) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCListIviSurfaces{}); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, listIVISurfaces()); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setsSurfaceOpacity) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceOpacity{ WaylandIviSurfaceId(1u), 1.f }); - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceOpacity{ WaylandIviSurfaceId(2u), 0.2f }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceOpacity(WaylandIviSurfaceId(1u),1.f)); - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceOpacity(WaylandIviSurfaceId(2u),0.2f)); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setsSurfaceRectangle) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceDestRectangle{ WaylandIviSurfaceId(111u), 0, 0, 640, 480 }); - m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceDestRectangle{ WaylandIviSurfaceId(222u), -1,-10, 1920, 1080 }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceDestinationRectangle(WaylandIviSurfaceId(111u), 0, 0, 640, 480)); - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceDestinationRectangle(WaylandIviSurfaceId(222u), -1,-10, 1920, 1080)); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, callsSystemCompositorAddSurfaceToLayer) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCAddIviSurfaceToIviLayer{ WaylandIviSurfaceId(222u), WaylandIviLayerId(111u) }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, addSurfaceToLayer(WaylandIviSurfaceId(222u), WaylandIviLayerId(111u))); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, callsSystemCompositorRemoveSurfaceFromLayer) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCRemoveIviSurfaceFromIviLayer{ WaylandIviSurfaceId(345u), WaylandIviLayerId(123u) }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, removeSurfaceFromLayer(WaylandIviSurfaceId(345u), WaylandIviLayerId(123u))); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, callsSystemCompositorDestroySurface) -{ - m_commandBuffer.enqueueCommand(RendererCommand::SCDestroyIviSurface{ WaylandIviSurfaceId(51u) }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, destroySurface(WaylandIviSurfaceId(51u))); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, callsSystemCompositorScreenshot) -{ - const std::string firstName("somefilename.png"); - const std::string otherName("somethingelse.png"); - - m_commandBuffer.enqueueCommand(RendererCommand::SCScreenshot{ 1, firstName }); - m_commandBuffer.enqueueCommand(RendererCommand::SCScreenshot{ -1, otherName }); - - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, doScreenshot(std::string_view{firstName}, 1)); - EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, doScreenshot(std::string_view{otherName}, -1)); - - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setClearFlags) -{ - constexpr DisplayHandle display{ 1 }; - constexpr OffscreenBufferHandle buffer{ 2 }; - - m_commandBuffer.enqueueCommand(RendererCommand::SetClearFlags{ display, buffer, EClearFlags_Color }); - EXPECT_CALL(m_sceneUpdater, handleSetClearFlags(buffer, EClearFlags_Color)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setClearColor) -{ - constexpr DisplayHandle display{ 1 }; - constexpr OffscreenBufferHandle buffer{ 2 }; - constexpr glm::vec4 clearColor(1.f, 0.f, 0.2f, 0.3f); - - m_commandBuffer.enqueueCommand(RendererCommand::SetClearColor{ display, buffer, clearColor }); - EXPECT_CALL(m_sceneUpdater, handleSetClearColor(buffer, clearColor)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, resizeDisplayWindowExterally) -{ - constexpr DisplayHandle display{ 1 }; - - m_commandBuffer.enqueueCommand(RendererCommand::SetExterallyOwnedWindowSize{ display, 1u, 2u }); - EXPECT_CALL(m_sceneUpdater, handleSetExternallyOwnedWindowSize(1u, 2u)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, setFrameTimerLimits) -{ - //default values - ASSERT_EQ(PlatformTime::InfiniteDuration, m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::SceneResourcesUpload)); - ASSERT_EQ(PlatformTime::InfiniteDuration, m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::ResourcesUpload)); - ASSERT_EQ(PlatformTime::InfiniteDuration, m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::OffscreenBufferRender)); - - m_commandBuffer.enqueueCommand(RendererCommand::SetLimits_FrameBudgets{ 4u, 1u, 3u }); - doCommandExecutorLoop(); - - EXPECT_EQ(std::chrono::microseconds(4u), m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::SceneResourcesUpload)); - EXPECT_EQ(std::chrono::microseconds(1u), m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::ResourcesUpload)); - EXPECT_EQ(std::chrono::microseconds(3u), m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::OffscreenBufferRender)); -} - -TEST_F(ARendererCommandExecutor, forwardsPickEventToSceneUpdater) -{ - const SceneId sceneId{ 123u }; - const glm::vec2 coords{ 0.1f, 0.2f }; - m_commandBuffer.enqueueCommand(RendererCommand::PickEvent{ sceneId, coords }); - EXPECT_CALL(m_sceneUpdater, handlePickEvent(sceneId, coords)); - doCommandExecutorLoop(); -} - -TEST_F(ARendererCommandExecutor, triggersLogRinfo) -{ - m_commandBuffer.enqueueCommand(RendererCommand::LogInfo{ ERendererLogTopic::Displays, true, NodeHandle{ 6u } }); - EXPECT_CALL(m_sceneUpdater, logRendererInfo(ERendererLogTopic::Displays, true, NodeHandle{ 6u })); - doCommandExecutorLoop(); -} -} diff --git a/renderer/RendererLib/RendererLib/test/RendererConfigTest.cpp b/renderer/RendererLib/RendererLib/test/RendererConfigTest.cpp deleted file mode 100644 index 7befe2dd2..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererConfigTest.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/RendererConfig.h" - -TEST(AInternalRendererConfig, hasDefaultValues) -{ - ramses_internal::RendererConfig config; - EXPECT_FALSE(config.getSystemCompositorControlEnabled()); - EXPECT_EQ(std::chrono::microseconds{10000u}, config.getFrameCallbackMaxPollTime()); - EXPECT_EQ("", config.getWaylandDisplayForSystemCompositorController()); -} - -TEST(AInternalRendererConfig, canEnableSystemCompositorControl) -{ - ramses_internal::RendererConfig config; - config.enableSystemCompositorControl(); - EXPECT_TRUE(config.getSystemCompositorControlEnabled()); -} - -TEST(AInternalRendererConfig, canSetGetMaxFramecallbackPollTime) -{ - ramses_internal::RendererConfig config; - - config.setFrameCallbackMaxPollTime(std::chrono::microseconds{123u}); - EXPECT_EQ(std::chrono::microseconds{123u}, config.getFrameCallbackMaxPollTime()); -} - -TEST(AInternalRendererConfig, canSetGetWaylandDisplayForSystemCompositorController) -{ - ramses_internal::RendererConfig config; - - config.setWaylandDisplayForSystemCompositorController("ramses wd"); - EXPECT_EQ("ramses wd", config.getWaylandDisplayForSystemCompositorController()); -} - diff --git a/renderer/RendererLib/RendererLib/test/RendererLogContextTest.cpp b/renderer/RendererLib/RendererLib/test/RendererLogContextTest.cpp deleted file mode 100644 index 909c898a8..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererLogContextTest.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gtest/gtest.h" -#include "RendererLib/RendererLogContext.h" - -using namespace ramses_internal; - -class ARendererLogContext : public ::testing::Test -{ -public: - ARendererLogContext() - : contextError (ERendererLogLevelFlag_Error) - , contextWarn (ERendererLogLevelFlag_Warn) - , contextInfo (ERendererLogLevelFlag_Info) - , contextDetails(ERendererLogLevelFlag_Details) - { - } - - ~ARendererLogContext() override - { - } - - RendererLogContext contextError; - RendererLogContext contextWarn; - RendererLogContext contextInfo; - RendererLogContext contextDetails; -}; - -TEST_F(ARendererLogContext, activatesLogLevelFlagsErrorCorrectly) -{ - EXPECT_TRUE (contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); - EXPECT_FALSE(contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); - EXPECT_FALSE(contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); - EXPECT_FALSE(contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); -} -TEST_F(ARendererLogContext, activatesLogLevelFlagsWarnCorrectly) -{ - EXPECT_TRUE (contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); - EXPECT_TRUE (contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); - EXPECT_FALSE(contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); - EXPECT_FALSE(contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); -} - -TEST_F(ARendererLogContext, activatesLogLevelFlagsInfoCorrectly) -{ - EXPECT_TRUE (contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); - EXPECT_TRUE (contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); - EXPECT_TRUE (contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); - EXPECT_FALSE(contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); -} - -TEST_F(ARendererLogContext, activatesLogLevelFlagsDetailsCorrectly) -{ - EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); - EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); - EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); - EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); -} - -TEST_F(ARendererLogContext, matchesAllNodesByDefault) -{ - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(0u))); - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(123u))); - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle::Invalid())); -} - -TEST_F(ARendererLogContext, matchesExactHandle) -{ - contextInfo.setNodeHandleFilter(NodeHandle(123u)); - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(123u))); - EXPECT_FALSE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(0u))); - EXPECT_FALSE(contextInfo.isMatchingNodeHandeFilter(NodeHandle::Invalid())); -} - -TEST_F(ARendererLogContext, matchesAllNodesWithWildcard) -{ - contextInfo.setNodeHandleFilter(NodeHandle::Invalid()); - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(0u))); - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(123u))); - EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle::Invalid())); -} diff --git a/renderer/RendererLib/RendererLib/test/RendererResourceManagerTest.cpp b/renderer/RendererLib/RendererLib/test/RendererResourceManagerTest.cpp deleted file mode 100644 index be33b6f37..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererResourceManagerTest.cpp +++ /dev/null @@ -1,1448 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererLib/RendererResourceManager.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/RendererStatistics.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/EDataBufferType.h" -#include "SceneAPI/TextureEnums.h" -#include "ResourceDeviceHandleAccessorMock.h" -#include "RendererResourceCacheMock.h" -#include "RendererResourceCacheFake.h" -#include "PlatformMock.h" -#include "EmbeddedCompositingManagerMock.h" -#include "MockResourceHash.h" -#include "ResourceUploaderMock.h" -#include "Watchdog/ThreadAliveNotifierMock.h" -#include "Utils/ThreadLocalLog.h" - -namespace ramses_internal { -using namespace testing; - -namespace -{ - DisplayConfig makeConfig(bool keepEffects) - { - DisplayConfig cfg; - cfg.setKeepEffectsUploaded(keepEffects); - return cfg; - } -} - -class ARendererResourceManager : public ::testing::Test -{ -public: - explicit ARendererResourceManager(bool keepEffects = false) - : fakeSceneId(66u) - , asyncEffectUploader(platform, platform.renderBackendMock, notifier, 1) - , resourceManager(platform.renderBackendMock, std::unique_ptr{ resUploader }, asyncEffectUploader, embeddedCompositingManager, makeConfig(keepEffects), frameTimer, stats) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - InSequence s; - EXPECT_CALL(platform.renderBackendMock.contextMock, disable()).WillOnce(Return(true)); - EXPECT_CALL(platform, createResourceUploadRenderBackend()); - EXPECT_CALL(platform.renderBackendMock.contextMock, enable()).WillOnce(Return(true)); - const bool status = asyncEffectUploader.createResourceUploadRenderBackendAndStartThread(); - EXPECT_TRUE(status); - } - - ~ARendererResourceManager() override - { - // no actual unload expected but clears internal lists - resourceManager.unloadAllSceneResourcesForScene(fakeSceneId); - - EXPECT_CALL(platform, destroyResourceUploadRenderBackend()); - asyncEffectUploader.destroyResourceUploadRenderBackendAndStopThread(); - } - - void referenceResource(ResourceContentHash hash, SceneId sceneId) - { - const ResourceContentHashVector list{ hash }; - - resourceManager.referenceResourcesForScene(sceneId, list); - EXPECT_TRUE(contains_c(*resourceManager.getResourcesInUseByScene(sceneId), hash)); - } - - void unreferenceResource(ResourceContentHash hash, SceneId sceneId) - { - const ResourceContentHashVector list{ hash }; - - resourceManager.unreferenceResourcesForScene(sceneId, list); - EXPECT_TRUE(!resourceManager.getResourcesInUseByScene(sceneId) || !contains_c(*resourceManager.getResourcesInUseByScene(sceneId), hash)); - } - - void expectStreamUsedBy(WaylandIviSurfaceId source, const std::vector& sbUsage) - { - EXPECT_THAT(sbUsage, ::testing::UnorderedElementsAreArray(resourceManager.getStreamUsage(source))); - } - - void expectResourceUploaded(const ResourceContentHash& hash, EResourceType type, DeviceResourceHandle resultDeviceHandle = ResourceUploaderMock::FakeResourceDeviceHandle) - { - EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([hash, type, resultDeviceHandle](auto&, const auto& rd, auto&) { - EXPECT_EQ(hash, rd.hash); - EXPECT_EQ(type, rd.type); - - return resultDeviceHandle; - })); - } - - void expectResourceUnloaded(const ResourceContentHash& hash, EResourceType type, DeviceResourceHandle deviceHandle = ResourceUploaderMock::FakeResourceDeviceHandle) - { - EXPECT_CALL(*resUploader, unloadResource(Ref(platform.renderBackendMock), type, hash , deviceHandle)); - } - - void expectDeviceFlushOnWindows() - { -#if defined(_WIN32) - EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, flush()); -#endif - } - - void uploadShader(const ResourceContentHash& hash, bool expectSuccess = true) - { - EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([hash](auto&, const auto& rd, auto&) { - EXPECT_EQ(hash, rd.hash); - EXPECT_EQ(EResourceType_Effect, rd.type); - - return std::optional{}; - })); - - if(expectSuccess) - { - EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); - EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(Ref(platform.renderBackendMock), _, _, _)); - } - else - { - EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillRepeatedly(Invoke([](const auto&) {return std::move(std::unique_ptr{}); })); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)).Times(0); - EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(Ref(platform.renderBackendMock), _, _, _)).Times(0); - } - - resourceManager.uploadAndUnloadPendingResources(); - ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(hash)); - - constexpr std::chrono::seconds timeoutTime{ 2u }; - const auto startTime = std::chrono::steady_clock::now(); - while (resourceManager.getResourceStatus(hash) == EResourceStatus::ScheduledForUpload - && std::chrono::steady_clock::now() - startTime < timeoutTime) - { - std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); - resourceManager.uploadAndUnloadPendingResources(); - } - } - -protected: - StrictMock platform; - StrictMock additionalRenderer; - StrictMock embeddedCompositingManager; - SceneId fakeSceneId; - RendererStatistics stats; - StrictMock* resUploader = new StrictMock; - FrameTimer frameTimer; - NiceMock notifier; - AsyncEffectUploader asyncEffectUploader; - RendererResourceManager resourceManager; -}; - -TEST_F(ARendererResourceManager, PerformsCorrectlyResourceLifecycle) -{ - constexpr ResourceContentHash resource = MockResourceHash::VertArrayHash; - - // register some resource - referenceResource(resource, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); - EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - // provide data - const auto managedRes = MockResourceHash::GetManagedResource(resource); - resourceManager.provideResourceData(managedRes); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(resource)); - EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - // upload the resource - expectResourceUploaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); - EXPECT_TRUE(resourceManager.getResourceDeviceHandle(resource).isValid()); - EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); - - // delete resource - ResourceContentHashVector resources; - resources.push_back(resource); - resourceManager.unreferenceResourcesForScene(fakeSceneId, { resource }); - expectResourceUnloaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - // Make sure the resource was deleted before the resourceManager gets out of scope - // and deletes it automatically - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); -} - -TEST_F(ARendererResourceManager, ReferencedResourceHasCorrectStatus) -{ - ResourceContentHash resource = MockResourceHash::VertArrayHash; - - referenceResource(resource, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); - EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - unreferenceResource(resource, fakeSceneId); -} - -TEST_F(ARendererResourceManager, ReferencedAndProvidedResourceHasCorrectStatus) -{ - ResourceContentHash resource = MockResourceHash::VertArrayHash; - - referenceResource(resource, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); - EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); - EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - const auto managedRes = MockResourceHash::GetManagedResource(resource); - resourceManager.provideResourceData(managedRes); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(resource)); - EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - unreferenceResource(resource, fakeSceneId); -} - -TEST_F(ARendererResourceManager, unreferencingResourceThatWasNotUploadedYetWillNotReportItAsPendingForUploadAnymore) -{ - ResourceContentHash resource = MockResourceHash::VertArrayHash; - - referenceResource(resource, fakeSceneId); - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(resource)); - - unreferenceResource(resource, fakeSceneId); - EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); -} - -TEST_F(ARendererResourceManager, DeletesResourceUsedBySingleSceneWhenUnreferenced) -{ - ResourceContentHash resource = MockResourceHash::VertArrayHash; - - // request some resource - referenceResource(resource, fakeSceneId); - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - expectResourceUploaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); - EXPECT_TRUE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - resourceManager.unreferenceResourcesForScene(fakeSceneId, { resource }); - expectResourceUnloaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - // Make sure the resource was deleted before the resourceManager gets out of scope - // and deletes it automatically - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); -} - -TEST_F(ARendererResourceManager, referencingAnUnloadedResourceAgainShouldUploadItAgain) -{ - ResourceContentHash resource = MockResourceHash::VertArrayHash; - const auto managedRes = MockResourceHash::GetManagedResource(resource); - - // ref - referenceResource(resource, fakeSceneId); - resourceManager.provideResourceData(managedRes); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - expectResourceUploaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - // unref - ResourceContentHashVector resources; - resources.push_back(resource); - resourceManager.unreferenceResourcesForScene(fakeSceneId, resources); - expectResourceUnloaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); - - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); - - // ref again - referenceResource(resource, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); - resourceManager.provideResourceData(managedRes); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - expectResourceUploaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - //general clean-up (expect needed because of strict mock) - unreferenceResource(resource, fakeSceneId); - expectResourceUnloaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); -} - -TEST_F(ARendererResourceManager, deletesNoLongerNeededResourcesWhenSceneDestroyed) -{ - ResourceContentHash resource = MockResourceHash::VertArrayHash; - - referenceResource(resource, fakeSceneId); - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - expectResourceUploaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - ResourceContentHashVector usedResources; - usedResources.push_back(resource); - resourceManager.unreferenceResourcesForScene(fakeSceneId, usedResources); - expectResourceUnloaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - - // Make sure the resource was deleted before the resourceManager gets out of scope - // and deletes it automatically - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); -} - -TEST_F(ARendererResourceManager, keepsTrackOfStreamUsageByStreamBuffers) -{ - constexpr WaylandIviSurfaceId source{ 666u }; - constexpr WaylandIviSurfaceId source2{ 667u }; - constexpr SceneId scene1{ 13u }; - constexpr SceneId scene2{ 14u }; - constexpr StreamBufferHandle sb1{ 21u }; - constexpr StreamBufferHandle sb2{ 22u }; - constexpr StreamBufferHandle sb3{ 23u }; - - EXPECT_CALL(embeddedCompositingManager, refStream(source)).Times(2); - EXPECT_CALL(embeddedCompositingManager, refStream(source2)); - resourceManager.uploadStreamBuffer(sb1, source); - resourceManager.uploadStreamBuffer(sb2, source); - resourceManager.uploadStreamBuffer(sb3, source2); - expectStreamUsedBy(source, { sb1, sb2 }); - expectStreamUsedBy(source2, { sb3 }); - - EXPECT_CALL(embeddedCompositingManager, unrefStream(source)).Times(2); - EXPECT_CALL(embeddedCompositingManager, unrefStream(source2)); - resourceManager.unloadStreamBuffer(sb1); - resourceManager.unloadStreamBuffer(sb2); - resourceManager.unloadStreamBuffer(sb3); - expectStreamUsedBy(source, {}); - expectStreamUsedBy(source2, {}); - - resourceManager.unloadAllSceneResourcesForScene(scene1); - resourceManager.unloadAllSceneResourcesForScene(scene2); -} - -TEST_F(ARendererResourceManager, doesNotDeleteResourcesNeededByAnotherSceneWhenSceneDestroyed) -{ - ResourceContentHash vertResource = MockResourceHash::VertArrayHash; - ResourceContentHash indexResource = MockResourceHash::IndexArrayHash; - - const DeviceResourceHandle vertDeviceHandle{ 111u }; - const DeviceResourceHandle indexDeviceHandle{ 222u }; - - const SceneId fakeSceneId2(4u); - - //index array is also needed by scene 2 - referenceResource(vertResource, fakeSceneId); - referenceResource(indexResource, fakeSceneId); - referenceResource(indexResource, fakeSceneId2); - - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(vertResource)); - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(indexResource)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - InSequence s; - expectResourceUploaded(vertResource, EResourceType_VertexArray, vertDeviceHandle); - expectResourceUploaded(indexResource, EResourceType_IndexArray, indexDeviceHandle); - resourceManager.uploadAndUnloadPendingResources(); - - resourceManager.unreferenceResourcesForScene(fakeSceneId, { vertResource, indexResource }); - expectResourceUnloaded(vertResource, EResourceType_VertexArray, vertDeviceHandle); - resourceManager.uploadAndUnloadPendingResources(); - - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); - - //general clean-up (expect needed because of strict mock) - unreferenceResource(indexResource, fakeSceneId2); - expectResourceUnloaded(indexResource, EResourceType_IndexArray, indexDeviceHandle); - resourceManager.uploadAndUnloadPendingResources(); -} - -TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadDataBuffer_IndexBuffer) -{ - const DataBufferHandle dataBuffer(1u); - const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; - const EDataType dataType = EDataType::UInt32; - const uint32_t sizeInBytes = 1024u; - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateIndexBuffer(dataType, sizeInBytes)); - resourceManager.uploadDataBuffer(dataBuffer, dataBufferType, dataType, sizeInBytes, fakeSceneId); - - EXPECT_EQ(DeviceMock::FakeIndexBufferDeviceHandle, resourceManager.getDataBufferDeviceHandle(dataBuffer, fakeSceneId)); - - const Byte dummyData[10] = {}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadIndexBufferData(DeviceMock::FakeIndexBufferDeviceHandle, dummyData, 7u)); - resourceManager.updateDataBuffer(dataBuffer, 7u, dummyData, fakeSceneId); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteIndexBuffer(DeviceMock::FakeIndexBufferDeviceHandle)); - resourceManager.unloadDataBuffer(dataBuffer, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadDataBuffer_VertexBuffer) -{ - const DataBufferHandle dataBuffer(1u); - const EDataBufferType dataBufferType = EDataBufferType::VertexBuffer; - const EDataType dataType = EDataType::UInt32; - const uint32_t sizeInBytes = 1024u; - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexBuffer(sizeInBytes)); - resourceManager.uploadDataBuffer(dataBuffer, dataBufferType, dataType, sizeInBytes, fakeSceneId); - - EXPECT_EQ(DeviceMock::FakeVertexBufferDeviceHandle, resourceManager.getDataBufferDeviceHandle(dataBuffer, fakeSceneId)); - - const Byte dummyData[10] = {}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadVertexBufferData(DeviceMock::FakeVertexBufferDeviceHandle, dummyData, 7u)); - resourceManager.updateDataBuffer(dataBuffer, 7u, dummyData, fakeSceneId); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexBuffer(DeviceMock::FakeVertexBufferDeviceHandle)); - resourceManager.unloadDataBuffer(dataBuffer, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadTextureBuffer_WithOneMipLevel) -{ - InSequence seq; - const TextureBufferHandle textureBuffer(1u); - const uint32_t expectedSize = (10u * 20u) * 4u; - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateTexture2D(10u, 20u, ETextureFormat::RGBA8, DefaultTextureSwizzleArray, 1u, expectedSize)); - resourceManager.uploadTextureBuffer(textureBuffer, 10u, 20u, ETextureFormat::RGBA8, 1u, fakeSceneId); - - EXPECT_EQ(DeviceMock::FakeTextureDeviceHandle, resourceManager.getTextureBufferDeviceHandle(textureBuffer, fakeSceneId)); - - const Byte dummyTexture[10] = {}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, bindTexture(DeviceMock::FakeTextureDeviceHandle)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadTextureData(DeviceMock::FakeTextureDeviceHandle, 0u, 2u, 3u, 0u, 4u, 5u, 1u, dummyTexture, 0u)); - resourceManager.updateTextureBuffer(textureBuffer, 0u, 2u, 3u, 4u, 5u, dummyTexture, fakeSceneId); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(DeviceMock::FakeTextureDeviceHandle)); - resourceManager.unloadTextureBuffer(textureBuffer, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadTextureBuffer_WithSeveralMipLevels) -{ - InSequence seq; - const TextureBufferHandle textureBuffer(1u); - const uint32_t expectedSize = (10u * 20u + 5u * 10u)* 4u; - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateTexture2D(10u, 20u, ETextureFormat::RGBA8, DefaultTextureSwizzleArray, 2u, expectedSize)); - resourceManager.uploadTextureBuffer(textureBuffer, 10u, 20u, ETextureFormat::RGBA8, 2u, fakeSceneId); - - EXPECT_EQ(DeviceMock::FakeTextureDeviceHandle, resourceManager.getTextureBufferDeviceHandle(textureBuffer, fakeSceneId)); - - const Byte dummyTexture[10] = {}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, bindTexture(DeviceMock::FakeTextureDeviceHandle)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadTextureData(DeviceMock::FakeTextureDeviceHandle, 1u, 2u, 3u, 0u, 4u, 5u, 1u, _, 0u)); - resourceManager.updateTextureBuffer(textureBuffer, 1u, 2u, 3u, 4u, 5u, dummyTexture, fakeSceneId); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(DeviceMock::FakeTextureDeviceHandle)); - resourceManager.unloadTextureBuffer(textureBuffer, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUnloadRenderTargetBuffer) -{ - RenderBufferHandle bufferHandle(1u); - const RenderBuffer colorBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)); - resourceManager.uploadRenderTargetBuffer(bufferHandle, fakeSceneId, colorBuffer); - - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getRenderTargetBufferDeviceHandle(bufferHandle, fakeSceneId)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)); - resourceManager.unloadRenderTargetBuffer(bufferHandle, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUnloadBlitPassRenderTargets) -{ - const BlitPassHandle blitPassHandle(100u); - const RenderBufferHandle sourceRenderBufferHandle(101u); - const RenderBufferHandle destinationRenderBufferHandle(102u); - - const RenderBuffer renderBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u); - - const DeviceResourceHandle sourceRenderBufferDeviceHandle(201u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, - uploadRenderBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)) - .WillOnce(Return(sourceRenderBufferDeviceHandle)); - resourceManager.uploadRenderTargetBuffer(sourceRenderBufferHandle, fakeSceneId, renderBuffer); - - const DeviceResourceHandle destinationRenderBufferDeviceHandle(202u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, - uploadRenderBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)) - .WillOnce(Return(destinationRenderBufferDeviceHandle)); - resourceManager.uploadRenderTargetBuffer(destinationRenderBufferHandle, fakeSceneId, renderBuffer); - - const DeviceResourceHandle blittingRenderTargetSrcDeviceHandle(203u); - const DeviceResourceHandle blittingRenderTargetDstDeviceHandle(204u); - DeviceHandleVector rbsSrc; - rbsSrc.push_back(sourceRenderBufferDeviceHandle); - DeviceHandleVector rbsDst; - rbsDst.push_back(destinationRenderBufferDeviceHandle); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(rbsSrc))).WillOnce(Return(blittingRenderTargetSrcDeviceHandle)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(rbsDst))).WillOnce(Return(blittingRenderTargetDstDeviceHandle)); - resourceManager.uploadBlitPassRenderTargets(blitPassHandle, sourceRenderBufferHandle, destinationRenderBufferHandle, fakeSceneId); - - DeviceResourceHandle actualBlitPassRTSrc; - DeviceResourceHandle actualBlitPassRTDst; - resourceManager.getBlitPassRenderTargetsDeviceHandle(blitPassHandle, fakeSceneId, actualBlitPassRTSrc, actualBlitPassRTDst); - EXPECT_EQ(blittingRenderTargetSrcDeviceHandle, actualBlitPassRTSrc); - EXPECT_EQ(blittingRenderTargetDstDeviceHandle, actualBlitPassRTDst); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(actualBlitPassRTSrc)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(actualBlitPassRTDst)); - resourceManager.unloadBlitPassRenderTargets(blitPassHandle, fakeSceneId); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2); - resourceManager.unloadRenderTargetBuffer(sourceRenderBufferHandle, fakeSceneId); - resourceManager.unloadRenderTargetBuffer(destinationRenderBufferHandle, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUnloadRenderTargetWithBuffers) -{ - RenderTargetHandle targetHandle(1u); - - RenderBufferHandleVector bufferHandles; - bufferHandles.push_back(RenderBufferHandle(1u)); - bufferHandles.push_back(RenderBufferHandle(5u)); - - const RenderBuffer colorBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u); - const RenderBuffer depthBuffer(800u, 600u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_ReadWrite, 0u); - - { - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_ReadWrite, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - } - - resourceManager.uploadRenderTargetBuffer(bufferHandles[0], fakeSceneId, colorBuffer); - resourceManager.uploadRenderTargetBuffer(bufferHandles[1], fakeSceneId, depthBuffer); - - resourceManager.uploadRenderTarget(targetHandle, bufferHandles, fakeSceneId); - - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getRenderTargetBufferDeviceHandle(bufferHandles[0], fakeSceneId)); - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getRenderTargetBufferDeviceHandle(bufferHandles[1], fakeSceneId)); - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getRenderTargetDeviceHandle(targetHandle, fakeSceneId)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2); - resourceManager.unloadRenderTargetBuffer(bufferHandles[0], fakeSceneId); - resourceManager.unloadRenderTargetBuffer(bufferHandles[1], fakeSceneId); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - resourceManager.unloadRenderTarget(targetHandle, fakeSceneId); -} - -TEST_F(ARendererResourceManager, canUploadAndUnloadVertexArray) -{ - VertexArrayInfo vaInfo; - vaInfo.shader = DeviceMock::FakeShaderDeviceHandle; - vaInfo.indexBuffer = DeviceMock::FakeIndexBufferDeviceHandle; - vaInfo.vertexBuffers.push_back({ DeviceMock::FakeVertexBufferDeviceHandle,DataFieldHandle{2u}, 3u, 4u, EDataType::Vector2F, 5u, 6u }); - - const RenderableHandle renderable{ 123u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexArray(Ref(vaInfo))); - resourceManager.uploadVertexArray(renderable, vaInfo, fakeSceneId); - - EXPECT_EQ(DeviceMock::FakeVertexArrayDeviceHandle, resourceManager.getVertexArrayDeviceHandle(renderable, fakeSceneId)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexArray(DeviceMock::FakeVertexArrayDeviceHandle)); - resourceManager.unloadVertexArray(renderable, fakeSceneId); -} - -TEST_F(ARendererResourceManager, GetsInvalidDeviceHandleForUnknownOffscreenBuffer) -{ - const OffscreenBufferHandle bufferHandle(1u); - EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); - EXPECT_FALSE(resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle).isValid()); -} - -TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorBuffer) -{ - const OffscreenBufferHandle bufferHandle(1u); - - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, ERenderBufferType_InvalidBuffer); - - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)); -} - -TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthBuffers) -{ - const OffscreenBufferHandle bufferHandle(1u); - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_WriteOnly, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, ERenderBufferType_DepthBuffer); - - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); -} - -TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthStencilAttached) -{ - const OffscreenBufferHandle bufferHandle(1u); - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_DepthStencilBuffer, ETextureFormat::Depth24_Stencil8, ERenderBufferAccessMode_WriteOnly, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer); - - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); -} - -TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthStencilAttachedWithMSAAenabled) -{ - const OffscreenBufferHandle bufferHandle(1u); - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 4u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_DepthStencilBuffer, ETextureFormat::Depth24_Stencil8, ERenderBufferAccessMode_WriteOnly, 4u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 4u, false, ERenderBufferType_DepthStencilBuffer); - - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); -} - -TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorBuffer_WithDoubleBuffering) -{ - const OffscreenBufferHandle bufferHandle(1u); - const DeviceResourceHandle colorBufferDeviceHandle1{ 7771u }; - const DeviceResourceHandle colorBufferDeviceHandle2{ 7778u }; - const DeviceResourceHandle offscreenBufferDeviceHandle1{ 7798u }; - const DeviceResourceHandle offscreenBufferDeviceHandle2{ 7799u }; - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)) - .WillOnce(Return(colorBufferDeviceHandle1)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({ colorBufferDeviceHandle1 })))).WillOnce(Return(offscreenBufferDeviceHandle1)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)) - .WillOnce(Return(colorBufferDeviceHandle2)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({ colorBufferDeviceHandle2 })))).WillOnce(Return(offscreenBufferDeviceHandle2)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle1)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle2)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, ERenderBufferType_InvalidBuffer); - - EXPECT_EQ(offscreenBufferDeviceHandle1, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(colorBufferDeviceHandle1, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(offscreenBufferDeviceHandle1)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2); - EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); -} - -TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthStencilBuffers_WithDoubleBuffering) -{ - const OffscreenBufferHandle bufferHandle(1u); - const DeviceResourceHandle colorBufferDeviceHandle1{ 7771u }; - const DeviceResourceHandle colorBufferDeviceHandle2{ 7778u }; - const DeviceResourceHandle depthBufferDeviceHandle{ 7796u }; - const DeviceResourceHandle offscreenBufferDeviceHandle1{ 7798u }; - const DeviceResourceHandle offscreenBufferDeviceHandle2{ 7799u }; - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)) - .WillOnce(Return(colorBufferDeviceHandle1)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_DepthStencilBuffer, ETextureFormat::Depth24_Stencil8, ERenderBufferAccessMode_WriteOnly, 0u)) - .WillOnce(Return(depthBufferDeviceHandle)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({colorBufferDeviceHandle1, depthBufferDeviceHandle})))).WillOnce(Return(offscreenBufferDeviceHandle1)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)) - .WillOnce(Return(colorBufferDeviceHandle2)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({ colorBufferDeviceHandle2, depthBufferDeviceHandle })))).WillOnce(Return(offscreenBufferDeviceHandle2)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle1)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle2)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, ERenderBufferType_DepthStencilBuffer); - - EXPECT_EQ(offscreenBufferDeviceHandle1, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(colorBufferDeviceHandle1, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(offscreenBufferDeviceHandle1)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2); - EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(3u); -} - -TEST_F(ARendererResourceManager, UploadsDmaOffscreenBuffer) -{ - constexpr OffscreenBufferHandle bufferHandle(1u); - - constexpr DmaBufferFourccFormat bufferFormat{ 777u }; - constexpr DmaBufferUsageFlags bufferUsageFlags{ 888u }; - constexpr DmaBufferModifiers bufferModifiers{ 999u }; - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadDmaRenderBuffer(10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadDmaOffscreenBuffer(bufferHandle, 10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers); - - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(DeviceMock::FakeDmaRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, getDmaRenderBufferFD(DeviceMock::FakeDmaRenderBufferDeviceHandle)).WillOnce(Return(123)); - EXPECT_EQ(123, resourceManager.getDmaOffscreenBufferFD(bufferHandle)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, getDmaRenderBufferStride(DeviceMock::FakeDmaRenderBufferDeviceHandle)).WillOnce(Return(432u)); - EXPECT_EQ(432u, resourceManager.getDmaOffscreenBufferStride(bufferHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, destroyDmaRenderBuffer(_)); -} - -TEST_F(ARendererResourceManager, CanUnloadOffscreenBuffer_WithColorBuffer) -{ - const OffscreenBufferHandle bufferHandle(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _, _)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, ERenderBufferType_InvalidBuffer); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)); - resourceManager.unloadOffscreenBuffer(bufferHandle); - - EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); -} - -TEST_F(ARendererResourceManager, CanUnloadOffscreenBuffer_WithColorAndDepthStencilBuffers) -{ - const OffscreenBufferHandle bufferHandle(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _, _)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); - resourceManager.unloadOffscreenBuffer(bufferHandle); - - EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); -} - -TEST_F(ARendererResourceManager, CanUnloadDoubleBufferedOffscreenBuffer_WithColorBuffer) -{ - const OffscreenBufferHandle bufferHandle(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _, _)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)).Times(2u); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, ERenderBufferType_InvalidBuffer); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); - resourceManager.unloadOffscreenBuffer(bufferHandle); - - EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); -} - -TEST_F(ARendererResourceManager, CanUnloadDoubleBufferedOffscreenBuffer_WithColorAndDepthStencilBuffers) -{ - const OffscreenBufferHandle bufferHandle(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _, _)).Times(3u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)).Times(2u); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); - resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, ERenderBufferType_DepthStencilBuffer); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(3u); - resourceManager.unloadOffscreenBuffer(bufferHandle); - - EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); -} - -TEST_F(ARendererResourceManager, CanUnloadDmaOffscreenBuffer) -{ - constexpr OffscreenBufferHandle bufferHandle(1u); - - constexpr DmaBufferFourccFormat bufferFormat{ 777u }; - constexpr DmaBufferUsageFlags bufferUsageFlags{ 888u }; - constexpr DmaBufferModifiers bufferModifiers{ 999u }; - InSequence seq; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadDmaRenderBuffer(10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); - RenderState::ScissorRegion scissorRegion{}; - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); - resourceManager.uploadDmaOffscreenBuffer(bufferHandle, 10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers); - - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(DeviceMock::FakeDmaRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); - EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, destroyDmaRenderBuffer(_)); - resourceManager.unloadOffscreenBuffer(bufferHandle); - EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); -} - -TEST_F(ARendererResourceManager, UploadsAndUnloadsStreamBuffers) -{ - constexpr StreamBufferHandle sbHandle1{ 1u }; - constexpr StreamBufferHandle sbHandle2{ 2u }; - constexpr WaylandIviSurfaceId sbSrc1{ 11u }; - constexpr WaylandIviSurfaceId sbSrc2{ 12u }; - EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc1)); - resourceManager.uploadStreamBuffer(sbHandle1, sbSrc1); - EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc2)); - resourceManager.uploadStreamBuffer(sbHandle2, sbSrc2); - - EXPECT_CALL(embeddedCompositingManager, getCompositedTextureDeviceHandleForStreamTexture(sbSrc1)).WillOnce(Return(DeviceMock::FakeRenderTargetDeviceHandle)); - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getStreamBufferDeviceHandle(sbHandle1)); - EXPECT_CALL(embeddedCompositingManager, getCompositedTextureDeviceHandleForStreamTexture(sbSrc2)).WillOnce(Return(DeviceMock::FakeRenderBufferDeviceHandle)); - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getStreamBufferDeviceHandle(sbHandle2)); - - EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc1)); - resourceManager.unloadStreamBuffer(sbHandle1); - EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc2)); - resourceManager.unloadStreamBuffer(sbHandle2); -} - -TEST_F(ARendererResourceManager, returnsInvalidDeviceHandleForUnknownStreamBuffer) -{ - EXPECT_FALSE(resourceManager.getStreamBufferDeviceHandle(StreamBufferHandle::Invalid()).isValid()); - EXPECT_FALSE(resourceManager.getStreamBufferDeviceHandle(StreamBufferHandle{ 9999u }).isValid()); -} - -TEST_F(ARendererResourceManager, UploadsAndUnloadsExternalBuffers) -{ - constexpr ExternalBufferHandle ebHandle1{ 1u }; - constexpr ExternalBufferHandle ebHandle2{ 2u }; - - constexpr DeviceResourceHandle deviceHandle1{ 11111u }; - constexpr DeviceResourceHandle deviceHandle2{ 22222u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle1)); - resourceManager.uploadExternalBuffer(ebHandle1); - EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle2)); - resourceManager.uploadExternalBuffer(ebHandle2); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle1)); - resourceManager.unloadExternalBuffer(ebHandle1); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle2)); - resourceManager.unloadExternalBuffer(ebHandle2); -} - -TEST_F(ARendererResourceManager, FailsUploadingExternalBuffersIfExtensionNotSupported) -{ - constexpr ExternalBufferHandle ebHandle{ 1u }; - - EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(false)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).Times(0); - resourceManager.uploadExternalBuffer(ebHandle); -} - -TEST_F(ARendererResourceManager, CanGetDeviceHandleForExternalBuffer) -{ - constexpr ExternalBufferHandle ebHandle{ 1u }; - - constexpr DeviceResourceHandle deviceHandle{ 11111u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle)); - resourceManager.uploadExternalBuffer(ebHandle); - - EXPECT_EQ(deviceHandle, resourceManager.getExternalBufferDeviceHandle(ebHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle)); - resourceManager.unloadExternalBuffer(ebHandle); -} - -TEST_F(ARendererResourceManager, ReturnsInvalidDeviceHandleForUnknownExternalBuffer) -{ - constexpr ExternalBufferHandle ebHandle{ 1u }; - EXPECT_FALSE(resourceManager.getExternalBufferDeviceHandle(ebHandle).isValid()); -} - -TEST_F(ARendererResourceManager, CanGetGlIdForExternalBuffer) -{ - constexpr ExternalBufferHandle ebHandle{ 1u }; - - constexpr DeviceResourceHandle deviceHandle{ 11111u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle)); - resourceManager.uploadExternalBuffer(ebHandle); - - uint32_t glId{ 567u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, getTextureAddress(deviceHandle)).WillOnce(Return(glId)); - EXPECT_EQ(glId, resourceManager.getExternalBufferGlId(ebHandle)); - - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle)); - resourceManager.unloadExternalBuffer(ebHandle); -} - -TEST_F(ARendererResourceManager, ReturnsInvalidGlIdForUnknownExternalBuffer) -{ - constexpr ExternalBufferHandle ebHandle{ 1u }; - EXPECT_EQ(0u, resourceManager.getExternalBufferGlId(ebHandle)); -} - -TEST_F(ARendererResourceManager, UploadAndDeleteValidShader) -{ - auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, "", ResourceCacheFlag_DoNotCache); - const ResourceContentHash resHash = effectRes->getHash(); - - // request some resources - referenceResource(resHash, fakeSceneId); - resourceManager.provideResourceData(std::move(effectRes)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - uploadShader(resHash); - - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resHash)); - unreferenceResource(resHash, fakeSceneId); - expectResourceUnloaded(resHash, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); - resourceManager.uploadAndUnloadPendingResources(); -} - -TEST_F(ARendererResourceManager, DoesNotUnregisterResourceThatWasUploaded) -{ - const ResourceContentHash resource = MockResourceHash::VertArrayHash; - - referenceResource(resource, fakeSceneId); - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - expectResourceUploaded(resource, EResourceType_VertexArray); - resourceManager.uploadAndUnloadPendingResources(); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); - EXPECT_TRUE(resourceManager.getResourceDeviceHandle(resource).isValid()); - - resourceManager.unreferenceResourcesForScene(fakeSceneId, { resource }); - ASSERT_TRUE(resourceManager.getRendererResourceRegistry().containsResource(resource)); - - expectResourceUnloaded(resource, EResourceType_VertexArray); -} - -TEST_F(ARendererResourceManager, DoesNotUnregisterResourceThatWasScheduledForUpload) -{ - auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, "", ResourceCacheFlag_DoNotCache); - const ResourceContentHash resHash = effectRes->getHash(); - - // request some resources - referenceResource(resHash, fakeSceneId); - resourceManager.provideResourceData(std::move(effectRes)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - //simulate shader was not uploaded by ResourceUploader (not found in bin shader cache) so it gets uploaded by AsyncEffectUploader - EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([](auto&, const auto&, auto&) { - return std::optional{}; - })); - - EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); - EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(Ref(platform.renderBackendMock), _, _, _)); - resourceManager.uploadAndUnloadPendingResources(); - ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(resHash)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); - ASSERT_TRUE(resourceManager.getRendererResourceRegistry().containsResource(resHash)); - - constexpr std::chrono::seconds timeoutTime{ 2u }; - const auto startTime = std::chrono::steady_clock::now(); - while (resourceManager.getResourceStatus(resHash) == EResourceStatus::ScheduledForUpload - && std::chrono::steady_clock::now() - startTime < timeoutTime) - { - std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); - resourceManager.uploadAndUnloadPendingResources(); - } - - expectResourceUnloaded(resHash, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); -} - -TEST_F(ARendererResourceManager, CanUploadAndUnloadEffectOwnedBySceneThatGetsDestroyed_ConfidenceTest) -{ - auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, "", ResourceCacheFlag_DoNotCache); - const ResourceContentHash resHash = effectRes->getHash(); - - // request some resources - referenceResource(resHash, fakeSceneId); - resourceManager.provideResourceData(std::move(effectRes)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - //simulate shader was not uploaded by ResourceUploader (not found in bin shader cache) so it gets uploaded by AsyncEffectUploader - EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([resHash](auto&, const auto& rd, auto&) { - EXPECT_EQ(resHash, rd.hash); - EXPECT_EQ(EResourceType_Effect, rd.type); - - return std::optional{}; - })); - - //block call to upload shader to simulate scene getting unreferenced while AsyncEffectUploader is still uploading - std::promise barrier; - auto future = barrier.get_future(); - EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([&](const auto&) { - future.get(); - return std::make_unique(1u, 2u); - })); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); - EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(_, _, _, _)); - - resourceManager.uploadAndUnloadPendingResources(); - ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(resHash)); - - expectResourceUnloaded(resHash, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); - ASSERT_TRUE(resourceManager.getRendererResourceRegistry().containsResource(resHash)); - - barrier.set_value(); - - constexpr std::chrono::seconds timeoutTime{ 2u }; - const auto startTime = std::chrono::steady_clock::now(); - while (resourceManager.getRendererResourceRegistry().containsResource(resHash) - && std::chrono::steady_clock::now() - startTime < timeoutTime) - { - std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); - resourceManager.uploadAndUnloadPendingResources(); - } - ASSERT_FALSE(resourceManager.getRendererResourceRegistry().containsResource(resHash)); -} - -TEST_F(ARendererResourceManager, DoesNotUnloadEffectThatGetsUnreferencedAndReReferencedWhileCompiling) -{ - auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, "", ResourceCacheFlag_DoNotCache); - const ResourceContentHash resHash = effectRes->getHash(); - - // request some resources - referenceResource(resHash, fakeSceneId); - resourceManager.provideResourceData(std::move(effectRes)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - //simulate shader was not uploaded by ResourceUploader (not found in bin shader cache) so it gets uploaded by AsyncEffectUploader - EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([resHash](auto&, const auto& rd, auto&) { - EXPECT_EQ(resHash, rd.hash); - EXPECT_EQ(EResourceType_Effect, rd.type); - - return std::optional{}; - })); - - //block call to upload shader to simulate scene getting unreferenced while AsyncEffectUploader is still uploading - std::promise barrier; - EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([&](const auto&) { - barrier.get_future().get(); - return std::make_unique(1u, 2u); - })); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); - EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(_, _, _, _)); - - resourceManager.uploadAndUnloadPendingResources(); - ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(resHash)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); - resourceManager.uploadAndUnloadPendingResources(); - - const SceneId fakeSceneId2{ 432u }; - ASSERT_NE(fakeSceneId, fakeSceneId2); - referenceResource(resHash, fakeSceneId2); - resourceManager.uploadAndUnloadPendingResources(); - - barrier.set_value(); - - constexpr std::chrono::seconds timeoutTime{ 2u }; - const auto startTime = std::chrono::steady_clock::now(); - while (EResourceStatus::Uploaded != resourceManager.getResourceStatus(resHash) - && std::chrono::steady_clock::now() - startTime < timeoutTime) - { - std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); - resourceManager.uploadAndUnloadPendingResources(); - } - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resHash)); - const auto& effectSceneUsage = resourceManager.getRendererResourceRegistry().getResourceDescriptor(resHash).sceneUsage; - ASSERT_EQ(1u, effectSceneUsage.size()); - EXPECT_EQ(fakeSceneId2, effectSceneUsage[0]); - - expectResourceUnloaded(resHash, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); - unreferenceResource(resHash, fakeSceneId2); -} - -TEST_F(ARendererResourceManager, UploadInvalidShaderResultsInBrokenResource) -{ - auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, "", ResourceCacheFlag_DoNotCache); - const ResourceContentHash resHash = effectRes->getHash(); - - // request some resources - referenceResource(resHash, fakeSceneId); - resourceManager.provideResourceData(std::move(effectRes)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - - uploadShader(resHash, false); - - // resource must be broken - EXPECT_EQ(EResourceStatus::Broken, resourceManager.getResourceStatus(resHash)); - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); - - EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); - - // not expecting any unload - unreferenceResource(resHash, fakeSceneId); -} - -class ARendererResourceManagerWithDisabledEffectDeletion : public ARendererResourceManager -{ -public: - ARendererResourceManagerWithDisabledEffectDeletion() - : ARendererResourceManager(true) - { - } -}; - -TEST_F(ARendererResourceManagerWithDisabledEffectDeletion, DeletesEffectOnlyWhenDestructed_NotWhenUnrequested) -{ - const ResourceContentHash resource = MockResourceHash::EffectHash; - - // request some resources - referenceResource(resource, fakeSceneId); - resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); - EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); - uploadShader(resource); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); - - unreferenceResource(resource, fakeSceneId); - - // Make sure the effect was not deleted, before resource manager is destroyed - Mock::VerifyAndClearExpectations(&platform.renderBackendMock.deviceMock); - - // delete resource - expectResourceUnloaded(resource, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); -} - -TEST_F(ARendererResourceManager, unloadsAllSceneResources) -{ - // upload render buffer - RenderTargetHandle targetHandle(1u); - RenderBufferHandleVector bufferHandles; - bufferHandles.push_back(RenderBufferHandle(1u)); - bufferHandles.push_back(RenderBufferHandle(5u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_ReadWrite, 0u)); - const RenderBuffer colorBuffer(800u, 600u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u); - const RenderBuffer depthBuffer(800u, 600u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_ReadWrite, 0u); - resourceManager.uploadRenderTargetBuffer(bufferHandles[0], fakeSceneId, colorBuffer); - resourceManager.uploadRenderTargetBuffer(bufferHandles[1], fakeSceneId, depthBuffer); - - // upload render target - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); - resourceManager.uploadRenderTarget(targetHandle, bufferHandles, fakeSceneId); - - // upload blit pass - const BlitPassHandle blitPassHandle(100u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); - resourceManager.uploadBlitPassRenderTargets(blitPassHandle, bufferHandles[0], bufferHandles[1], fakeSceneId); - - //upload index data buffer - const DataBufferHandle indexDataBufferHandle(123u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateIndexBuffer(_, _)); - resourceManager.uploadDataBuffer(indexDataBufferHandle, EDataBufferType::IndexBuffer, EDataType::Float, 10u, fakeSceneId); - - //upload vertex data buffer - const DataBufferHandle vertexDataBufferHandle(777u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexBuffer(_)); - resourceManager.uploadDataBuffer(vertexDataBufferHandle, EDataBufferType::VertexBuffer, EDataType::Float, 10u, fakeSceneId); - - //upload texture buffer - const TextureBufferHandle textureBufferHandle(666u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateTexture2D(_, _, _, _, _, _)); - resourceManager.uploadTextureBuffer(textureBufferHandle, 1u, 2u, ETextureFormat::RGBA8, 1u, fakeSceneId); - - //upload vertex array - VertexArrayInfo vaInfo; - vaInfo.shader = DeviceMock::FakeShaderDeviceHandle; - vaInfo.indexBuffer = DeviceMock::FakeIndexBufferDeviceHandle; - vaInfo.vertexBuffers.push_back({ DeviceMock::FakeVertexBufferDeviceHandle,DataFieldHandle{2u}, 3u, 4u, EDataType::Vector2F, 5u, 6u }); - - const RenderableHandle renderable{ 123u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexArray(Ref(vaInfo))); - resourceManager.uploadVertexArray(renderable, vaInfo, fakeSceneId); - - // unload all scene resources - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(3); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteIndexBuffer(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexBuffer(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(_)); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexArray(_)); - resourceManager.unloadAllSceneResourcesForScene(fakeSceneId); - - // Make sure the resource was deleted before the resourceManager gets out of scope - // and deletes it automatically - Mock::VerifyAndClearExpectations(&platform.renderBackendMock); -} - -TEST_F(ARendererResourceManager, canUnreferenceAllResourcesUsedByAScene) -{ - const SceneId fakeSceneId2(fakeSceneId.getValue() + 1u); - const auto resource1 = MockResourceHash::VertArrayHash; - const auto resource2 = MockResourceHash::IndexArrayHash; - const auto resource3 = MockResourceHash::TextureHash; - - // request some resources - referenceResource(resource1, fakeSceneId); - referenceResource(resource2, fakeSceneId); - referenceResource(resource2, fakeSceneId2); - referenceResource(resource3, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource1)); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource2)); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource3)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); - - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource2)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId2); -} - -TEST_F(ARendererResourceManager, canProvideResourceData) -{ - const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); - // resource must be referenced by scene before providing data - referenceResource(MockResourceHash::EffectHash, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.provideResourceData(managedRes); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); -} - -TEST_F(ARendererResourceManager, providingResourceDataMulitpleTimesIsNoop) -{ - const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); - // resource must be referenced by scene before providing data - referenceResource(MockResourceHash::EffectHash, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.provideResourceData(managedRes); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - // resource is already provided so providing data again is noop - resourceManager.provideResourceData(managedRes); - resourceManager.provideResourceData(managedRes); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); -} - -TEST_F(ARendererResourceManager, providingResourceDataWhenAlreadyUploadedIsNoop) -{ - const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); - // resource must be referenced by scene before providing data - referenceResource(MockResourceHash::EffectHash, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.provideResourceData(managedRes); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - // upload the resource - uploadShader(MockResourceHash::EffectHash); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - EXPECT_TRUE(resourceManager.getResourceDeviceHandle(MockResourceHash::EffectHash).isValid()); - - // resource is already provided so providing data again is noop - resourceManager.provideResourceData(managedRes); - resourceManager.provideResourceData(managedRes); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - EXPECT_TRUE(resourceManager.getResourceDeviceHandle(MockResourceHash::EffectHash).isValid()); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); - expectResourceUnloaded(MockResourceHash::EffectHash, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); -} - -TEST_F(ARendererResourceManagerWithDisabledEffectDeletion, rereferencingResourceKeptInCacheIsNotUploadedAgain) -{ - const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); - // resource must be referenced by scene before providing data - referenceResource(MockResourceHash::EffectHash, fakeSceneId); - EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.provideResourceData(managedRes); - EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - // upload the resource - uploadShader(MockResourceHash::EffectHash); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - EXPECT_TRUE(resourceManager.getResourceDeviceHandle(MockResourceHash::EffectHash).isValid()); - - unreferenceResource(MockResourceHash::EffectHash, fakeSceneId); - resourceManager.uploadAndUnloadPendingResources(); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - referenceResource(MockResourceHash::EffectHash, fakeSceneId); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.uploadAndUnloadPendingResources(); - EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); - - resourceManager.unreferenceAllResourcesForScene(fakeSceneId); - expectResourceUnloaded(MockResourceHash::EffectHash, EResourceType_Effect, DeviceMock::FakeShaderDeviceHandle); -} - -TEST_F(ARendererResourceManager, UnloadsAllRemainingOffscreenBuffersAndStreamBuffersAtDestruction) -{ - // 2 offscreen buffers - constexpr OffscreenBufferHandle obHandle{ 1u }; - constexpr OffscreenBufferHandle obHandle2{ 2u }; - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _, _)).Times(4u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, RenderState::ScissorRegion{})).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)).Times(2u); - resourceManager.uploadOffscreenBuffer(obHandle, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer); - resourceManager.uploadOffscreenBuffer(obHandle2, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer); - - // 2 stream buffers - constexpr StreamBufferHandle sbHandle{ 1u }; - constexpr StreamBufferHandle sbHandle2{ 2u }; - constexpr WaylandIviSurfaceId sbSrc1{ 11u }; - constexpr WaylandIviSurfaceId sbSrc2{ 12u }; - EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc1)); - EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc2)); - resourceManager.uploadStreamBuffer(sbHandle, sbSrc1); - resourceManager.uploadStreamBuffer(sbHandle2, sbSrc2); - - // will destroy OBs directly on device - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2u); - EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(4u); - - // will destroy SBs via EC manager - EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc1)); - EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc2)); -} - -} diff --git a/renderer/RendererLib/RendererLib/test/RendererResourceRegistryTest.cpp b/renderer/RendererLib/RendererLib/test/RendererResourceRegistryTest.cpp deleted file mode 100644 index 0e71d2375..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererResourceRegistryTest.cpp +++ /dev/null @@ -1,434 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/RendererResourceRegistry.h" -#include "Components/ResourceDeleterCallingCallback.h" -#include "Resource/ArrayResource.h" -#include "ResourceMock.h" -#include "MockResourceHash.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -class ARendererResourceRegistry : public ::testing::Test -{ -public: -protected: - ARendererResourceRegistry() - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - } - - void expectResourcesUsedByScene(SceneId sceneId, const ResourceContentHashVector& res) - { - if (res.empty()) - { - EXPECT_EQ(nullptr, registry.getResourcesInUseByScene(sceneId)); - } - else - { - ASSERT_NE(nullptr, registry.getResourcesInUseByScene(sceneId)); - EXPECT_EQ(res, *registry.getResourcesInUseByScene(sceneId)); - } - } - - ManagedResource testManagedResource{ MockResourceHash::GetManagedResource(MockResourceHash::IndexArrayHash) }; - RendererResourceRegistry registry; -}; - -TEST_F(ARendererResourceRegistry, doesNotContainAnythingInitially) -{ - const ResourceContentHash resource(123u, 0u); - EXPECT_EQ(0u, registry.getAllResourceDescriptors().size()); - EXPECT_FALSE(registry.containsResource(resource)); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); - expectResourcesUsedByScene(SceneId{ 3u }, {}); -} - -TEST_F(ARendererResourceRegistry, canRegisterResource) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - - EXPECT_EQ(1u, registry.getAllResourceDescriptors().size()); - EXPECT_TRUE(registry.containsResource(resource)); - - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_EQ(resource, rd.hash); -} - -TEST_F(ARendererResourceRegistry, registeredResourceHasCorrectStatus) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_FALSE(rd.deviceHandle.isValid()); - EXPECT_FALSE(rd.resource); - EXPECT_TRUE(rd.sceneUsage.empty()); - EXPECT_EQ(EResourceStatus::Registered, rd.status); - EXPECT_EQ(EResourceType_Invalid, rd.type); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, unregisteredResourceIsNotContainedAnymore) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.unregisterResource(resource); - - EXPECT_EQ(0u, registry.getAllResourceDescriptors().size()); - EXPECT_FALSE(registry.containsResource(resource)); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canAddSceneUsageRef) -{ - const SceneId sceneId(11u); - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - - expectResourcesUsedByScene(sceneId, {}); - registry.addResourceRef(resource, sceneId); - expectResourcesUsedByScene(sceneId, { resource }); - - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - ASSERT_EQ(1u, rd.sceneUsage.size()); - EXPECT_EQ(sceneId, rd.sceneUsage[0]); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canRemoveSceneUsageRef) -{ - const SceneId sceneId(11u); - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - - registry.addResourceRef(resource, sceneId); - registry.removeResourceRef(resource, sceneId); - expectResourcesUsedByScene(sceneId, {}); - - EXPECT_FALSE(registry.containsResource(resource)); -} - -TEST_F(ARendererResourceRegistry, canSetResourceData) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_EQ(EResourceStatus::Provided, rd.status); - EXPECT_EQ(testManagedResource, rd.resource); - EXPECT_EQ(EResourceType_IndexArray, rd.type); - EXPECT_EQ(0u, rd.compressedSize); - EXPECT_EQ(1u, rd.decompressedSize); - EXPECT_FALSE(rd.deviceHandle.isValid()); - - EXPECT_FALSE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canSetResourceFromProvidedToUploaded_willReleaseResourceData) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_EQ(EResourceStatus::Provided, rd.status); - - const DeviceResourceHandle deviceHandle(123456u); - registry.setResourceUploaded(resource, deviceHandle, 666u); - - EXPECT_EQ(EResourceStatus::Uploaded, rd.status); - EXPECT_EQ(deviceHandle, rd.deviceHandle); - EXPECT_FALSE(rd.resource); - EXPECT_EQ(EResourceType_IndexArray, rd.type); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canSetResourceProvidedToBroken_willReleaseResourceData) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - - registry.setResourceBroken(resource); - - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_EQ(EResourceStatus::Broken, rd.status); - EXPECT_FALSE(rd.resource); - EXPECT_EQ(EResourceType_IndexArray, rd.type); - EXPECT_FALSE(rd.deviceHandle.isValid()); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canSetResourceFromScheduledForUploadToUploaded_willReleaseResourceData) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_EQ(EResourceStatus::Provided, rd.status); - - registry.setResourceScheduledForUpload(resource); - EXPECT_EQ(EResourceStatus::ScheduledForUpload, rd.status); - - const DeviceResourceHandle deviceHandle(123456u); - registry.setResourceUploaded(resource, deviceHandle, 666u); - - EXPECT_EQ(EResourceStatus::Uploaded, rd.status); - EXPECT_EQ(deviceHandle, rd.deviceHandle); - EXPECT_FALSE(rd.resource); - EXPECT_EQ(EResourceType_IndexArray, rd.type); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canSetResourceScheduledForUploadToBroken_willReleaseResourceData) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - registry.setResourceScheduledForUpload(resource); - - registry.setResourceBroken(resource); - - const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); - EXPECT_EQ(EResourceStatus::Broken, rd.status); - EXPECT_FALSE(rd.resource); - EXPECT_EQ(EResourceType_IndexArray, rd.type); - EXPECT_FALSE(rd.deviceHandle.isValid()); - - EXPECT_TRUE(registry.getAllProvidedResources().empty()); - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, providedResourceIsInProvidedList) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - - const ResourceContentHashVector& resources = registry.getAllProvidedResources(); - ASSERT_EQ(1u, resources.size()); - EXPECT_TRUE(contains_c(resources, resource)); -} - -TEST_F(ARendererResourceRegistry, reportsIfHasAnyResourcesScheduledForUpload) -{ - EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); - const ResourceContentHash resource(123u, 0u); - - registry.registerResource(resource); - EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); - - registry.setResourceData(resource, testManagedResource); - EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); - - registry.setResourceScheduledForUpload(resource); - EXPECT_EQ(EResourceStatus::ScheduledForUpload, registry.getResourceStatus(resource)); - EXPECT_TRUE(registry.hasAnyResourcesScheduledForUpload()); - - const DeviceResourceHandle deviceHandle(123456u); - registry.setResourceUploaded(resource, deviceHandle, 666u); - EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); -} - -TEST_F(ARendererResourceRegistry, uploadedResourceIsRemovedFromProvidedList) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - EXPECT_FALSE(registry.getAllProvidedResources().empty()); - - registry.setResourceUploaded(resource, DeviceResourceHandle{ 123u }, 666u); - EXPECT_TRUE(registry.getAllProvidedResources().empty()); -} - -TEST_F(ARendererResourceRegistry, scheduledForUploadResourceIsRemovedFromProvidedList) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - EXPECT_FALSE(registry.getAllProvidedResources().empty()); - - registry.setResourceScheduledForUpload(resource); - EXPECT_TRUE(registry.getAllProvidedResources().empty()); -} - -TEST_F(ARendererResourceRegistry, brokenResourceIsRemovedFromProvidedList) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - EXPECT_FALSE(registry.getAllProvidedResources().empty()); - - registry.setResourceBroken(resource); - EXPECT_TRUE(registry.getAllProvidedResources().empty()); -} - -TEST_F(ARendererResourceRegistry, unregisteredResourceIsRemovedFromProvidedList) -{ - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.setResourceData(resource, testManagedResource); - EXPECT_FALSE(registry.getAllProvidedResources().empty()); - - registry.unregisterResource(resource); - EXPECT_TRUE(registry.getAllProvidedResources().empty()); -} - -TEST_F(ARendererResourceRegistry, unusedResourceIsRemovedFromProvidedList) -{ - const SceneId sceneId(11u); - const ResourceContentHash resource(123u, 0u); - registry.registerResource(resource); - registry.addResourceRef(resource, sceneId); - - registry.setResourceData(resource, testManagedResource); - EXPECT_FALSE(registry.getAllProvidedResources().empty()); - - registry.removeResourceRef(resource, sceneId); - EXPECT_TRUE(registry.getAllProvidedResources().empty()); -} - -TEST_F(ARendererResourceRegistry, resourceWithoutReferenceIsPutInUnusedListIfUploadedOrUnregisteredIfNotUploaded) -{ - const SceneId sceneId(11u); - - const ResourceContentHash resource1(123u, 0u); - const ResourceContentHash resource2(124u, 0u); - const ResourceContentHash resource3(125u, 0u); - const ResourceContentHash resource4(126u, 0u); - - registry.registerResource(resource1); - registry.registerResource(resource2); - registry.registerResource(resource3); - registry.registerResource(resource4); - - registry.addResourceRef(resource1, sceneId); - registry.addResourceRef(resource2, sceneId); - registry.addResourceRef(resource3, sceneId); - registry.addResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); - - registry.setResourceData(resource4, testManagedResource); - registry.setResourceUploaded(resource4, DeviceResourceHandle{ 123u }, 666u); - - const ResourceContentHashVector& resources = registry.getAllResourcesNotInUseByScenes(); - EXPECT_TRUE(resources.empty()); - - registry.removeResourceRef(resource1, sceneId); - registry.removeResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource2, resource3 }); - - ASSERT_EQ(1u, resources.size()); - EXPECT_TRUE(contains_c(resources, resource4)); - - EXPECT_FALSE(registry.containsResource(resource1)); -} - -TEST_F(ARendererResourceRegistry, previouslyUnusedResourceIsRemovedFromUnusedListWhenUsedAgain) -{ - const SceneId sceneId(11u); - - const ResourceContentHash resource1(123u, 0u); - const ResourceContentHash resource2(124u, 0u); - const ResourceContentHash resource3(125u, 0u); - const ResourceContentHash resource4(126u, 0u); - - registry.registerResource(resource1); - registry.registerResource(resource2); - registry.registerResource(resource3); - registry.registerResource(resource4); - - registry.addResourceRef(resource1, sceneId); - registry.addResourceRef(resource2, sceneId); - registry.addResourceRef(resource3, sceneId); - registry.addResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); - - registry.removeResourceRef(resource1, sceneId); - registry.removeResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource2, resource3 }); - - EXPECT_FALSE(registry.containsResource(resource1)); - EXPECT_FALSE(registry.containsResource(resource4)); - - registry.registerResource(resource1); - registry.registerResource(resource4); - - registry.addResourceRef(resource1, sceneId); - registry.addResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource2, resource3, resource1, resource4 }); - - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} - -TEST_F(ARendererResourceRegistry, canReferenceResourceMultipleTimes) -{ - const SceneId sceneId(11u); - - const ResourceContentHash resource1(123u, 0u); - const ResourceContentHash resource2(124u, 0u); - const ResourceContentHash resource3(125u, 0u); - const ResourceContentHash resource4(126u, 0u); - - registry.registerResource(resource1); - registry.registerResource(resource2); - registry.registerResource(resource3); - registry.registerResource(resource4); - - registry.addResourceRef(resource1, sceneId); - registry.addResourceRef(resource2, sceneId); - registry.addResourceRef(resource3, sceneId); - registry.addResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); - - registry.addResourceRef(resource1, sceneId); - registry.addResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); - - registry.removeResourceRef(resource1, sceneId); - registry.removeResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); - - registry.removeResourceRef(resource1, sceneId); - registry.removeResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource2, resource3 }); - - EXPECT_FALSE(registry.containsResource(resource1)); - EXPECT_FALSE(registry.containsResource(resource4)); - - registry.registerResource(resource1); - registry.registerResource(resource4); - - registry.addResourceRef(resource1, sceneId); - registry.addResourceRef(resource4, sceneId); - expectResourcesUsedByScene(sceneId, { resource2, resource3, resource1, resource4 }); - - EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); -} diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneResourceRegistryTest.cpp b/renderer/RendererLib/RendererLib/test/RendererSceneResourceRegistryTest.cpp deleted file mode 100644 index 9983d64c0..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererSceneResourceRegistryTest.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/RendererSceneResourceRegistry.h" -#include "SceneAPI/EDataBufferType.h" - -using namespace testing; -using namespace ramses_internal; - -class ARendererSceneResourceRegistry : public ::testing::Test -{ -public: -protected: - RendererSceneResourceRegistry registry; -}; - -TEST_F(ARendererSceneResourceRegistry, doesNotContainAnythingInitially) -{ - RenderBufferHandleVector rbs; - RenderTargetHandleVector rts; - BlitPassHandleVector bps; - DataBufferHandleVector dbs; - TextureBufferHandleVector tbs; - - registry.getAllRenderBuffers(rbs); - registry.getAllRenderTargets(rts); - registry.getAllBlitPasses(bps); - registry.getAllDataBuffers(dbs); - registry.getAllTextureBuffers(tbs); - - - EXPECT_TRUE(rbs.empty()); - EXPECT_TRUE(rts.empty()); - EXPECT_TRUE(bps.empty()); - EXPECT_TRUE(dbs.empty()); - EXPECT_TRUE(tbs.empty()); -} - -TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveRenderBuffer) -{ - const RenderBufferHandle rb(13u); - const DeviceResourceHandle deviceHandle(123u); - registry.addRenderBuffer(rb, deviceHandle, 0u, false); - - EXPECT_EQ(deviceHandle, registry.getRenderBufferDeviceHandle(rb)); - - RenderBufferHandleVector rbs; - registry.getAllRenderBuffers(rbs); - ASSERT_EQ(1u, rbs.size()); - EXPECT_EQ(rb, rbs[0]); - rbs.clear(); - - registry.removeRenderBuffer(rb); - registry.getAllRenderBuffers(rbs); - EXPECT_TRUE(rbs.empty()); -} - -TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveRenderTarget) -{ - const RenderTargetHandle rt(13u); - const DeviceResourceHandle deviceHandle(123u); - registry.addRenderTarget(rt, deviceHandle); - - EXPECT_EQ(deviceHandle, registry.getRenderTargetDeviceHandle(rt)); - - RenderTargetHandleVector rts; - registry.getAllRenderTargets(rts); - ASSERT_EQ(1u, rts.size()); - EXPECT_EQ(rt, rts[0]); - rts.clear(); - - registry.removeRenderTarget(rt); - registry.getAllRenderTargets(rts); - EXPECT_TRUE(rts.empty()); -} - -TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveBlitPass) -{ - const BlitPassHandle bp(13u); - const DeviceResourceHandle deviceHandle1(123u); - const DeviceResourceHandle deviceHandle2(124u); - registry.addBlitPass(bp, deviceHandle1, deviceHandle2); - - DeviceResourceHandle deviceHandle1actual; - DeviceResourceHandle deviceHandle2actual; - registry.getBlitPassDeviceHandles(bp, deviceHandle1actual, deviceHandle2actual); - EXPECT_EQ(deviceHandle1, deviceHandle1actual); - EXPECT_EQ(deviceHandle2, deviceHandle2actual); - - BlitPassHandleVector bps; - registry.getAllBlitPasses(bps); - ASSERT_EQ(1u, bps.size()); - EXPECT_EQ(bp, bps[0]); - bps.clear(); - - registry.removeBlitPass(bp); - registry.getAllBlitPasses(bps); - EXPECT_TRUE(bps.empty()); -} - -TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveDataBuffers) -{ - const DataBufferHandle db(13u); - const DeviceResourceHandle deviceHandle(123u); - const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; - registry.addDataBuffer(db, deviceHandle, dataBufferType, 0u); - - DataBufferHandleVector dbs; - registry.getAllDataBuffers(dbs); - ASSERT_EQ(1u, dbs.size()); - EXPECT_EQ(db, dbs[0]); - dbs.clear(); - - registry.removeDataBuffer(db); - registry.getAllDataBuffers(dbs); - EXPECT_TRUE(dbs.empty()); -} - -TEST_F(ARendererSceneResourceRegistry, canGetDataBufferDeviceHandle) -{ - const DataBufferHandle db(13u); - const DeviceResourceHandle deviceHandle(123u); - const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; - registry.addDataBuffer(db, deviceHandle, dataBufferType, 0u); - - EXPECT_EQ(deviceHandle, registry.getDataBufferDeviceHandle(db)); - registry.removeDataBuffer(db); -} - -TEST_F(ARendererSceneResourceRegistry, canGetDataBufferType) -{ - const DataBufferHandle db(13u); - const DeviceResourceHandle deviceHandle(123u); - const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; - registry.addDataBuffer(db, deviceHandle, dataBufferType, 0u); - - EXPECT_EQ(dataBufferType, registry.getDataBufferType(db)); - registry.removeDataBuffer(db); -} - -TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveTextureBuffers) -{ - const TextureBufferHandle tb(13u); - const DeviceResourceHandle deviceHandle(123u); - registry.addTextureBuffer(tb, deviceHandle, ETextureFormat::RG8, 0u); - - TextureBufferHandleVector tbs; - registry.getAllTextureBuffers(tbs); - ASSERT_EQ(1u, tbs.size()); - EXPECT_EQ(tb, tbs[0]); - tbs.clear(); - - registry.removeTextureBuffer(tb); - registry.getAllTextureBuffers(tbs); - EXPECT_TRUE(tbs.empty()); -} - -TEST_F(ARendererSceneResourceRegistry, canGetTextureBufferAttributes) -{ - const TextureBufferHandle tb(13u); - const DeviceResourceHandle deviceHandle(123u); - registry.addTextureBuffer(tb, deviceHandle, ETextureFormat::RG8, 0u); - - EXPECT_EQ(deviceHandle, registry.getTextureBufferDeviceHandle(tb)); - EXPECT_EQ(ETextureFormat::RG8, registry.getTextureBufferFormat(tb)); - registry.removeTextureBuffer(tb); -} diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.cpp b/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.cpp deleted file mode 100644 index 0d2878b4d..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.cpp +++ /dev/null @@ -1,5219 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererSceneUpdaterTest.h" -#include - -namespace ramses_internal { - -/////////////////////////// -// General tests -/////////////////////////// - -TEST_F(ARendererSceneUpdater, doesNotGenerateRendererEventForUnnamedFlush) -{ - createPublishAndSubscribeScene(); - expectNoEvent(); - - performFlush(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, generatesRendererEventForNamedFlush) -{ - createPublishAndSubscribeScene(); - expectNoEvent(); - - const SceneVersionTag version(15u); - performFlush(0u, version); - update(); - - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[0].eventType); - EXPECT_EQ(version, events[0].sceneVersionTag); -} - -TEST_F(ARendererSceneUpdater, lastAppliedFlushVersionTagIsTracked) -{ - createPublishAndSubscribeScene(); - expectNoEvent(); - - EXPECT_FALSE(rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag.isValid()); - - SceneVersionTag version{ 15u }; - performFlush(0u, version); - update(); - expectSceneEvent(ERendererEventType::SceneFlushed); - EXPECT_EQ(version, rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag); - - version = SceneVersionTag{ 1111u }; - performFlush(0u, version); - update(); - expectSceneEvent(ERendererEventType::SceneFlushed); - EXPECT_EQ(version, rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag); - - // invalid version tag is also tracked - performFlush(0u, SceneVersionTag::Invalid()); - update(); - EXPECT_FALSE(rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag.isValid()); - - version = SceneVersionTag{ 2222u }; - performFlush(0u, version); - update(); - expectSceneEvent(ERendererEventType::SceneFlushed); - EXPECT_EQ(version, rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag); -} - -TEST_F(ARendererSceneUpdater, preallocatesSceneForSizesProvidedInFlushInfo) -{ - createPublishAndSubscribeScene(); - expectNoEvent(); - - // perform empty flush only with notification of scene size change - SceneSizeInformation sizeInfo; - sizeInfo.renderableCount = 12u; - performFlush(0u, SceneVersionTag::Invalid(), &sizeInfo); - update(); - - const IScene& rendererScene = rendererScenes.getScene(getSceneId()); - EXPECT_EQ(sizeInfo, rendererScene.getSceneSizeInformation()); -} - -TEST_F(ARendererSceneUpdater, ignoresSceneActionsForNotSubscribedScene) -{ - createStagingScene(); - publishScene(); - - performFlush(0u, SceneVersionTag(1u)); - update(); - // there is no scene to apply actions to - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, ignoresSceneActionsForNotReceivedScene) -{ - createStagingScene(); - publishScene(); - requestSceneSubscription(); - - performFlush(0u, SceneVersionTag(1u)); - update(); - // there is no scene to apply actions to - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, ignoresSceneActionsAddedAfterSceneWasUnsubscribed) -{ - createPublishAndSubscribeScene(); - - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); - EXPECT_FALSE(rendererScenes.hasScene(getSceneId())); - - performFlush(0u, SceneVersionTag(1u)); - update(); - // there is no scene to apply actions to - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, ignoresSceneActionsAddedAfterSceneWasUnsubscribedByError) -{ - createPublishAndSubscribeScene(); - - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), true); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribedIndirect); - EXPECT_FALSE(rendererScenes.hasScene(getSceneId())); - - performFlush(0u, SceneVersionTag(1u)); - update(); - // there is no scene to apply actions to - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, ignoresSceneActionsAddedBetweenUnsubscriptionAndResubscription) -{ - createPublishAndSubscribeScene(); - - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); - - performFlush(0u, SceneVersionTag(1u)); - - EXPECT_CALL(sceneEventSender, sendSubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneSubscriptionRequest(getSceneId()); - rendererSceneUpdater->handleSceneReceived(SceneInfo(getSceneId())); - - // named flush ignored - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, canCreateAndDestroyDisplayContext) -{ - createDisplayAndExpectSuccess(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, createDisplayFailsIfCreationOfResourceUploadRenderBackendFails) -{ - EXPECT_CALL(renderer.m_platform, createResourceUploadRenderBackend()).WillOnce(Return(nullptr)); - EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); - rendererSceneUpdater->createDisplayContext({}, nullptr); - EXPECT_FALSE(renderer.hasDisplayController()); - expectEvent(ERendererEventType::DisplayCreateFailed); -} - -TEST_F(ARendererSceneUpdater, canNotDestroyNonExistantDisplay) -{ - destroyDisplay(true); -} - -TEST_F(ARendererSceneUpdater, canNotDestroyDisplayIfItHasMapRequestedScenes) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - requestMapScene(); - - destroyDisplay(true); - - unpublishMapRequestedScene(); - destroyDisplay(); - - EXPECT_FALSE(renderer.hasDisplayController()); -} - -TEST_F(ARendererSceneUpdater, canNotDestroyDisplayIfItHasMappedScenes) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - destroyDisplay(true); - - unmapScene(); - destroyDisplay(); - - EXPECT_FALSE(renderer.hasDisplayController()); -} - -TEST_F(ARendererSceneUpdater, canNotDestroyDisplayIfItHasRenderedScenes) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - destroyDisplay(true); - EXPECT_TRUE(renderer.hasDisplayController()); - - hideScene(); - unmapScene(); - destroyDisplay(); - - EXPECT_FALSE(renderer.hasDisplayController()); -} - -TEST_F(ARendererSceneUpdater, canDestroyDisplayIfMappedSceneGetsUnmapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDestroyDisplayIfMappedSceneGetsUnpublished) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - unpublishMappedScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, destroyingSceneUpdaterUnmapsAnyMappedSceneFromRenderer) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); - - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(getSceneId()).isValid()); -} - -TEST_F(ARendererSceneUpdater, destroyingSceneUpdaterDestroysAllDisplayContexts) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); - - EXPECT_FALSE(renderer.hasDisplayController()); -} - -TEST_F(ARendererSceneUpdater, renderOncePassesAreRetriggeredWhenSceneMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - auto& stageScene = *stagingScene[0]; - const RenderPassHandle pass = stageScene.allocateRenderPass(); - const auto dataLayout = stageScene.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I}, DataFieldInfo{EDataType::Vector2I} }, ResourceContentHash::Invalid()); - const CameraHandle camera = stageScene.allocateCamera(ECameraProjectionType::Orthographic, stageScene.allocateNode(), stageScene.allocateDataInstance(dataLayout)); - stageScene.setRenderPassCamera(pass, camera); - stageScene.setRenderPassRenderOnce(pass, true); - performFlush(); - - RendererCachedScene& scene = rendererScenes.getScene(stageScene.getSceneId()); - - // simulate render frame - // pass is in list to render - update(); - scene.markAllRenderOncePassesAsRendered(); - EXPECT_EQ(pass, scene.getSortedRenderingPasses()[0].getRenderPassHandle()); - - // simulate render frame - // pass is not in list to render anymore - update(); - scene.markAllRenderOncePassesAsRendered(); - EXPECT_TRUE(scene.getSortedRenderingPasses().empty()); - - // unmap scene - hideScene(); - unmapScene(); - update(); - scene.markAllRenderOncePassesAsRendered(); - - // remap scene and expect that render once pass is in list to render again - mapScene(); - showScene(); - update(); - scene.markAllRenderOncePassesAsRendered(); - EXPECT_EQ(pass, scene.getSortedRenderingPasses()[0].getRenderPassHandle()); - - // simulate render frame - // pass is not in list to render anymore - update(); - scene.markAllRenderOncePassesAsRendered(); - EXPECT_TRUE(scene.getSortedRenderingPasses().empty()); - - hideScene(); - unmapScene(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canHideSceneIfNotShownYet) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - update(); - - // request show and hide within same frame - rendererSceneUpdater->handleSceneShowRequest(getSceneId()); - rendererSceneUpdater->handleSceneHideRequest(getSceneId()); - expectInternalSceneStateEvents({ ERendererEventType::SceneShowFailed, ERendererEventType::SceneHidden }); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - - // show failed (was canceled) and scene is still in mapped state - update(); - expectNoEvent(); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canNotUnmapSceneWhichWasRequestedToBeShown) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - update(); - - rendererSceneUpdater->handleSceneShowRequest(getSceneId()); - - rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); - expectInternalSceneStateEvents({ ERendererEventType::SceneUnmapFailed }); - EXPECT_EQ(ESceneState::RenderRequested, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - expectInternalSceneStateEvent(ERendererEventType::SceneShown); - EXPECT_EQ(ESceneState::Rendered, sceneStateExecutor.getSceneState(getSceneId())); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canUnsubscribeSceneIfSubscriptionRequested) -{ - createStagingScene(); - publishScene(); - requestSceneSubscription(); - // scene is now in subscription requested state, waiting for scene to arrive - update(); - - // request unsubscribe cancels the subscription and reports unsubscribed - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); - expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - expectNoEvent(); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); -} - -TEST_F(ARendererSceneUpdater, canUnsubscribeSceneIfSubscriptionPending) -{ - createStagingScene(); - publishScene(); - requestSceneSubscription(); - receiveScene(); - // scene is now in subscription pending state, scene arrived, waiting for initial flush - update(); - - // request unsubscribe cancels the subscription and reports unsubscribed - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); - expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - expectNoEvent(); - EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); -} - - -/////////////////////////// -// Offscreen buffer tests -/////////////////////////// - -TEST_F(ARendererSceneUpdater, canCreateOffscreenBuffer_WithColorBufferOnly) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, false, ERenderBufferType_InvalidBuffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 4u, false, ERenderBufferType_InvalidBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canCreateOffscreenBuffer_WithDepthStencilBuffers) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 4u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canCreateDoubleBufferedOffscreenBuffer_WithColorBufferOnly) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true, ERenderBufferType_InvalidBuffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, ERenderBufferType_InvalidBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canCreateDoubleBufferedOffscreenBuffer_WithDepthStencilBuffers) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canCreateDmaOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - constexpr OffscreenBufferHandle buffer(1u); - constexpr DmaBufferFourccFormat fourccFormat{ 123u }; - constexpr DmaBufferUsageFlags usageFlags{ 456u }; - constexpr DmaBufferModifiers modifiers{ 789u }; - - expectDmaOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, fourccFormat, usageFlags, modifiers); - - constexpr int resultFD = 111; - constexpr uint32_t resultStride = 222u; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getDmaOffscreenBufferFD(buffer)).WillOnce(Return(resultFD)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getDmaOffscreenBufferStride(buffer)).WillOnce(Return(resultStride)); - EXPECT_TRUE(rendererSceneUpdater->handleDmaBufferCreateRequest(buffer, 1u, 1u, fourccFormat, usageFlags, modifiers)); - - RendererEventVector rendererEvents; - RendererEventVector sceneEvents; - rendererEventCollector.appendAndConsumePendingEvents(rendererEvents, sceneEvents); - ASSERT_EQ(rendererEvents.size(), 1u); - EXPECT_EQ(sceneEvents.size(), 0u); - - const auto& event = rendererEvents.front(); - EXPECT_EQ(ERendererEventType::OffscreenBufferCreated, event.eventType); - EXPECT_EQ(Display, event.displayHandle); - EXPECT_EQ(buffer, event.offscreenBuffer); - EXPECT_EQ(resultFD, event.dmaBufferFD); - EXPECT_EQ(resultStride, event.dmaBufferStride); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateOffscreenBufferOnUnknownDisplay) -{ - const OffscreenBufferHandle buffer(1u); - EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreateFailed); -} - -TEST_F(ARendererSceneUpdater, failsToCreateDmaOffscreenBufferOnUnknownDisplay) -{ - const OffscreenBufferHandle buffer(1u); - constexpr DmaBufferFourccFormat fourccFormat{ 123u }; - constexpr DmaBufferUsageFlags usageFlags{ 456u }; - constexpr DmaBufferModifiers modifiers{ 789u }; - EXPECT_FALSE(rendererSceneUpdater->handleDmaBufferCreateRequest(buffer, 1u, 1u, fourccFormat, usageFlags, modifiers)); - expectEvent(ERendererEventType::OffscreenBufferCreateFailed); -} - -TEST_F(ARendererSceneUpdater, failsToCreateOffscreenBufferWithSameID) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreateFailed); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateDmaOffscreenBufferWithSameID) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - constexpr DmaBufferFourccFormat fourccFormat{ 123u }; - constexpr DmaBufferUsageFlags usageFlags{ 456u }; - constexpr DmaBufferModifiers modifiers{ 789u }; - - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_FALSE(rendererSceneUpdater->handleDmaBufferCreateRequest(buffer, 1u, 1u, fourccFormat, usageFlags, modifiers)); - expectEvent(ERendererEventType::OffscreenBufferCreateFailed); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDestroyOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDestroyDoubleBufferedOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToDestroyOffscreenBufferOnUnknownDisplay) -{ - const OffscreenBufferHandle buffer(1u); - EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); -} - -TEST_F(ARendererSceneUpdater, failsToDestroyUnknownOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canAssignSceneToOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - mapScene(0u); - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer, 11)); - - unmapScene(0u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canAssignSceneToFramebuffer_MultipleTimes) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - mapScene(0u); - // Scene can be always assigned to framebuffer - EXPECT_TRUE(assignSceneToDisplayBuffer(0)); - EXPECT_TRUE(assignSceneToDisplayBuffer(0)); - EXPECT_TRUE(assignSceneToDisplayBuffer(0)); - - unmapScene(0u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToAssignSceneToDisplayBufferOnInvalidDisplay) -{ - createPublishAndSubscribeScene(); - EXPECT_FALSE(assignSceneToDisplayBuffer(0)); -} - -TEST_F(ARendererSceneUpdater, failsToAssignSceneToNonExistingDisplayBuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - - createPublishAndSubscribeScene(); - mapScene(0u); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - EXPECT_FALSE(assignSceneToDisplayBuffer(0, buffer)); - - unmapScene(0u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, confidence_failsToDestroyOffscreenBufferIfScenesAreAssignedToIt_DestroysAfterSceneIsAssignedToFramebuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - mapScene(0u); - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); - - EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0)); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - unmapScene(0u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, setsClearFlagsForOB) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - EXPECT_CALL(renderer, setClearFlags(DeviceMock::FakeRenderTargetDeviceHandle, EClearFlags_Color)); - rendererSceneUpdater->handleSetClearFlags(buffer, EClearFlags_Color); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, setsClearFlagsForFBIfNoOBSpecified) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - EXPECT_CALL(renderer, setClearFlags(renderer.getDisplayController().getDisplayBuffer(), EClearFlags_Color)); - rendererSceneUpdater->handleSetClearFlags(OffscreenBufferHandle::Invalid(), EClearFlags_Color); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotSetClearFlagsIfOBSpecifiedButNotFound) -{ - createDisplayAndExpectSuccess(); - - constexpr OffscreenBufferHandle invalidOB{ 1234u }; - EXPECT_CALL(renderer, setClearFlags(_, _)).Times(0u); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(invalidOB)).WillOnce(Return(DeviceResourceHandle::Invalid())); - rendererSceneUpdater->handleSetClearFlags(invalidOB, EClearFlags_Color); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotSetClearFlagsIfDisplayInvalid) -{ - EXPECT_CALL(renderer, setClearFlags(_, _)).Times(0u); - rendererSceneUpdater->handleSetClearFlags({}, EClearFlags_Color); -} - -TEST_F(ARendererSceneUpdater, setsClearColorForOB) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - - EXPECT_CALL(renderer, setClearColor(DeviceMock::FakeRenderTargetDeviceHandle, glm::vec4{ 1, 2, 3, 4 })); - rendererSceneUpdater->handleSetClearColor(buffer, { 1, 2, 3, 4 }); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, setsClearColorForFBIfNoOBSpecified) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - EXPECT_CALL(renderer, setClearColor(renderer.getDisplayController().getDisplayBuffer(), glm::vec4{ 1, 2, 3, 4 })); - rendererSceneUpdater->handleSetClearColor(OffscreenBufferHandle::Invalid(), { 1, 2, 3, 4 }); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotSetClearColorIfOBSpecifiedButNotFound) -{ - createDisplayAndExpectSuccess(); - - constexpr OffscreenBufferHandle invalidOB{ 1234u }; - EXPECT_CALL(renderer, setClearColor(_, _)).Times(0u); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(invalidOB)).WillOnce(Return(DeviceResourceHandle::Invalid())); - rendererSceneUpdater->handleSetClearColor(invalidOB, { 1, 2, 3, 4 }); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotSetClearColorIfDisplayInvalid) -{ - EXPECT_CALL(renderer, setClearColor(_, _)).Times(0u); - rendererSceneUpdater->handleSetClearColor({}, { 1, 2, 3, 4 }); -} - -TEST_F(ARendererSceneUpdater, resizesExternallyOwnedDisplayWindow) -{ - createDisplayAndExpectSuccess(); - - EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(123u, 456u)).WillOnce(Return(true)); - rendererSceneUpdater->handleSetExternallyOwnedWindowSize(123u, 456u); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotResizeExternallyOwnedDisplayWindowIfDisplayInvalid) -{ - EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(_, _)).Times(0); - rendererSceneUpdater->handleSetExternallyOwnedWindowSize(123u, 456u); -} - -TEST_F(ARendererSceneUpdater, readPixelsFromDisplayFramebuffer) -{ - createDisplayAndExpectSuccess(); - - const uint32_t x = 1u; - const uint32_t y = 2u; - const uint32_t width = 3u; - const uint32_t height = 4u; - const bool fullScreen = false; - const bool sendViaDLT = false; - const std::string_view filename; - - readPixels({}, x, y, width, height, fullScreen, sendViaDLT, filename); - expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, x, y, width, height); - doRenderLoop(); - rendererSceneUpdater->processScreenshotResults(); - expectReadPixelsEvents({ {OffscreenBufferHandle::Invalid(), true} }); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, readPixelsFromOffscreenbuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 10u, 10u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - const uint32_t x = 1u; - const uint32_t y = 2u; - const uint32_t width = 3u; - const uint32_t height = 4u; - const bool fullScreen = false; - const bool sendViaDLT = false; - const std::string_view filename; - - readPixels(buffer, x, y, width, height, fullScreen, sendViaDLT, filename); - expectDisplayControllerReadPixels(DeviceMock::FakeRenderTargetDeviceHandle, x, y, width, height); - doRenderLoop(); - rendererSceneUpdater->processScreenshotResults(); - expectReadPixelsEvents({ {buffer, true} }); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, createsReadPixelsFailedEventIfInvalidOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - - const uint32_t x = 1u; - const uint32_t y = 2u; - const uint32_t width = 3u; - const uint32_t height = 4u; - const bool fullScreen = false; - const bool sendViaDLT = false; - const std::string_view filename; - - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - readPixels(buffer, x, y, width, height, fullScreen, sendViaDLT, filename); - - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - doRenderLoop(); - expectReadPixelsEvents({ {buffer, false} }); - - rendererSceneUpdater->processScreenshotResults(); - expectNoEvent(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, createsReadPixelsFailedEventIfRectangleIsOutOfDisplayBounds) -{ - const auto displayWidth = WindowMock::FakeWidth; - const auto displayHeight = WindowMock::FakeHeight; - DisplayConfig dispConfig; - dispConfig.setDesiredWindowWidth(displayWidth); - dispConfig.setDesiredWindowHeight(displayHeight); - createDisplayAndExpectSuccess(dispConfig); - - const bool fullScreen = false; - const bool sendViaDLT = false; - const std::string_view filename; - - - readPixels({}, displayWidth, 0u, 1u, 1u, fullScreen, sendViaDLT, filename); - readPixels({}, 0u, displayHeight, 1u, 1u, fullScreen, sendViaDLT, filename); - readPixels({}, 0u, 0u, displayWidth + 1u, 1u, fullScreen, sendViaDLT, filename); - readPixels({}, 0u, 0u, 0u, displayHeight + 1u, fullScreen, sendViaDLT, filename); - - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - doRenderLoop(); - - expectReadPixelsEvents({ { OffscreenBufferHandle::Invalid(), false }, - { OffscreenBufferHandle::Invalid(), false }, - { OffscreenBufferHandle::Invalid(), false }, - { OffscreenBufferHandle::Invalid(), false } }); - - rendererSceneUpdater->processScreenshotResults(); - expectNoEvent(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, createsReadPixelsFailedEventIfRectangleIsOutOfOffscreenBufferBounds) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 10u, 10u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - const bool fullScreen = false; - const bool sendViaDLT = false; - const std::string_view filename; - - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).Times(4).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - readPixels(buffer, 10, 0u, 1u, 1u, fullScreen, sendViaDLT, filename); - readPixels(buffer, 0u, 10, 1u, 1u, fullScreen, sendViaDLT, filename); - readPixels(buffer, 0u, 0u, 11u, 1u, fullScreen, sendViaDLT, filename); - readPixels(buffer, 0u, 0u, 0u, 11u, fullScreen, sendViaDLT, filename); - - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - doRenderLoop(); - - expectReadPixelsEvents({ { buffer, false }, - { buffer, false }, - { buffer, false }, - { buffer, false } }); - - rendererSceneUpdater->processScreenshotResults(); - expectNoEvent(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, readPixelsFromDisplayAndSaveToFileWithoutGeneratingEvent) -{ - createDisplayAndExpectSuccess(); - - const uint32_t x = 1u; - const uint32_t y = 2u; - const uint32_t width = 3u; - const uint32_t height = 4u; - const bool fullScreen = false; - const bool sendViaDLT = false; - const std::string_view filename{"testScreenshot"}; - - readPixels({}, x, y, width, height, fullScreen, sendViaDLT, filename); - expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, x, y, width, height); - doRenderLoop(); - expectNoEvent(); - - rendererSceneUpdater->processScreenshotResults(); - expectNoEvent(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, readPixelsFromDisplayFullscreen) -{ - const auto displayWidth = WindowMock::FakeWidth; - const auto displayHeight = WindowMock::FakeHeight; - DisplayConfig dispConfig; - dispConfig.setDesiredWindowWidth(displayWidth); - dispConfig.setDesiredWindowHeight(displayHeight); - createDisplayAndExpectSuccess(dispConfig); - - const uint32_t x = 1u; - const uint32_t y = 2u; - const uint32_t width = 3u; - const uint32_t height = 4u; - const bool fullScreen = true; - const bool sendViaDLT = false; - const std::string_view filename; - - readPixels({}, x, y, width, height, fullScreen, sendViaDLT, filename); - expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, 0, 0, WindowMock::FakeWidth, WindowMock::FakeHeight); - doRenderLoop(); - - rendererSceneUpdater->processScreenshotResults(); - expectReadPixelsEvents({ {OffscreenBufferHandle::Invalid(), true} }); - - destroyDisplay(); -} - -/////////////////////////// -// Stream buffer tests -/////////////////////////// - -TEST_F(ARendererSceneUpdater, canCreateStreamBuffer) -{ - createDisplayAndExpectSuccess(); - - constexpr StreamBufferHandle buffer{ 1u }; - constexpr WaylandIviSurfaceId source{ 2u }; - expectStreamBufferUploaded(buffer, source); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateStreamBufferOnUnknownDisplay) -{ - constexpr StreamBufferHandle buffer{ 1u }; - constexpr WaylandIviSurfaceId source{ 2u }; - EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); -} - -TEST_F(ARendererSceneUpdater, canDestroyStreamBuffer) -{ - createDisplayAndExpectSuccess(); - - constexpr StreamBufferHandle buffer{ 1u }; - constexpr WaylandIviSurfaceId source{ 2u }; - expectStreamBufferUploaded(buffer, source); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); - - expectStreamBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToDestroyStreamBufferOnUnknownDisplay) -{ - constexpr StreamBufferHandle buffer{ 1u }; - EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); -} - -/////////////////////////// -// External buffer tests -/////////////////////////// - -TEST_F(ARendererSceneUpdater, canCreateExternalBuffer) -{ - createDisplayAndExpectSuccess(); - - constexpr ExternalBufferHandle buffer{ 1u }; - expectExternalBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreated); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, FailsToCreateExternalBufferIfResourceManagerFailsToUpload) -{ - createDisplayAndExpectSuccess(); - - constexpr ExternalBufferHandle buffer{ 1u }; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadExternalBuffer(buffer)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferDeviceHandle(buffer)).Times(1u).WillOnce(Return(DeviceResourceHandle::Invalid())); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferGlId(buffer)).Times(0); - - EXPECT_FALSE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreateFailed); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateExternalBufferOnUnknownDisplay) -{ - constexpr ExternalBufferHandle buffer{ 1u }; - EXPECT_FALSE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreateFailed); -} - -TEST_F(ARendererSceneUpdater, canDestroyExternalBuffer) -{ - createDisplayAndExpectSuccess(); - - constexpr ExternalBufferHandle buffer{ 1u }; - expectExternalBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreated); - - expectExternalBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferDestroyRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferDestroyed); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToDestroyExternalBufferOnUnknownDisplay) -{ - constexpr ExternalBufferHandle buffer{ 1u }; - EXPECT_FALSE(rendererSceneUpdater->handleExternalBufferDestroyRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferDestroyFailed); -} - -/////////////////////////// -// Data linking tests -/////////////////////////// - -TEST_F(ARendererSceneUpdater, updatesDataLinks) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - DataInstanceHandle consumerDataRef; - const float providedValue(333.f); - createDataSlotsAndLinkThem(consumerDataRef, providedValue); - - update(); - - IScene& scene2 = rendererScenes.getScene(getSceneId(1u)); - const float consumedValue = scene2.getDataSingleFloat(consumerDataRef, DataFieldHandle(0u)); - EXPECT_FLOAT_EQ(providedValue, consumedValue); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, updatesDataLinksAlsoIfProviderSceneNotShown) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - // only consumer scene is mapped and shown - mapScene(1u); - showScene(1u); - - DataInstanceHandle consumerDataRef; - const float providedValue(333.f); - createDataSlotsAndLinkThem(consumerDataRef, providedValue); - - update(); - - IScene& scene2 = rendererScenes.getScene(getSceneId(1u)); - const float consumedValue = scene2.getDataSingleFloat(consumerDataRef, DataFieldHandle(0u)); - EXPECT_FLOAT_EQ(providedValue, consumedValue); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotUpdatesDataLinksIfConsumerSceneNotShown) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - DataInstanceHandle consumerDataRef; - const float providedValue(333.f); - createDataSlotsAndLinkThem(consumerDataRef, providedValue); - - hideScene(1u); - update(); - - IScene& scene2 = rendererScenes.getScene(getSceneId(1u)); - const float consumedValue = scene2.getDataSingleFloat(consumerDataRef, DataFieldHandle(0u)); - EXPECT_FLOAT_EQ(0.f, consumedValue); - - hideScene(0u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, removesTextureLinkWhenProviderSceneUnmapped) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); - createTextureSlotsAndLinkThem(); - update(); - - hideScene(0u); - unmapScene(0u); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, removesTextureLinkWhenConsumerSceneUnmapped) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); - createTextureSlotsAndLinkThem(); - update(); - - hideScene(1u); - unmapScene(1u); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - unmapScene(0u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateTextureLinkIfProviderSceneNotMapped) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(1u); - showScene(1u); - - createTextureSlotsAndLinkThem(nullptr, 0u, 1u, false); - update(); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateTextureLinkIfBothProviderAndConsumerSceneNotMapped) -{ - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - createTextureSlotsAndLinkThem(nullptr, 0u, 1u, false); - update(); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); -} - -TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenBufferDestroyed_OB) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - const DataSlotId consumerId1 = createTextureConsumer(0u); - const DataSlotId consumerId2 = createTextureConsumer(1u); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - createBufferLink(buffer, getSceneId(0u), consumerId1); - createBufferLink(buffer, getSceneId(1u), consumerId2); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenBufferDestroyed_SB) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - const DataSlotId consumerId1 = createTextureConsumer(0u); - const DataSlotId consumerId2 = createTextureConsumer(1u); - - constexpr StreamBufferHandle buffer{ 1u }; - constexpr WaylandIviSurfaceId source{ 2u }; - expectStreamBufferUploaded(buffer, source); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - createBufferLink(buffer, getSceneId(0u), consumerId1); - createBufferLink(buffer, getSceneId(1u), consumerId2); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - expectStreamBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenBufferDestroyed_EB) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - const DataSlotId consumerId1 = createTextureConsumer(0u, TextureSampler::ContentType::ExternalTexture); - const DataSlotId consumerId2 = createTextureConsumer(1u, TextureSampler::ContentType::ExternalTexture); - - constexpr ExternalBufferHandle buffer{ 1u }; - expectExternalBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreated); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - createBufferLink(buffer, getSceneId(0u), consumerId1); - createBufferLink(buffer, getSceneId(1u), consumerId2); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - expectExternalBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferDestroyRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferDestroyed); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenConsumerSceneUnmapped_keepsOtherConsumerLinked_OB) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - const DataSlotId consumerId1 = createTextureConsumer(0u); - const DataSlotId consumerId2 = createTextureConsumer(1u); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - createBufferLink(buffer, getSceneId(0u), consumerId1); - createBufferLink(buffer, getSceneId(1u), consumerId2); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - unmapScene(0u); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenConsumerSceneUnmapped_keepsOtherConsumerLinked_SB) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - const DataSlotId consumerId1 = createTextureConsumer(0u); - const DataSlotId consumerId2 = createTextureConsumer(1u); - - constexpr StreamBufferHandle buffer{ 1u }; - constexpr WaylandIviSurfaceId source{ 2u }; - expectStreamBufferUploaded(buffer, source); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - createBufferLink(buffer, getSceneId(0u), consumerId1); - createBufferLink(buffer, getSceneId(1u), consumerId2); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - unmapScene(0u); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenConsumerSceneUnmapped_keepsOtherConsumerLinked_EB) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0u); - mapScene(1u); - - showScene(0u); - showScene(1u); - - const DataSlotId consumerId1 = createTextureConsumer(0u, TextureSampler::ContentType::ExternalTexture); - const DataSlotId consumerId2 = createTextureConsumer(1u, TextureSampler::ContentType::ExternalTexture); - - constexpr ExternalBufferHandle buffer{ 1u }; - expectExternalBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreated); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - createBufferLink(buffer, getSceneId(0u), consumerId1); - createBufferLink(buffer, getSceneId(1u), consumerId2); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(0u); - unmapScene(0u); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfConsumerSceneNotOnDisplay_OB) -{ - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createBufferLink(buffer, SceneId{ 666u }, DataSlotId{ 1u }, true); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfConsumerSceneNotOnDisplay_SB) -{ - createDisplayAndExpectSuccess(); - - constexpr StreamBufferHandle buffer{ 1u }; - constexpr WaylandIviSurfaceId source{ 2u }; - expectStreamBufferUploaded(buffer, source); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); - - createBufferLink(buffer, SceneId{ 666u }, DataSlotId{ 1u }, true); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfConsumerSceneNotOnDisplay_EB) -{ - createDisplayAndExpectSuccess(); - - constexpr ExternalBufferHandle buffer{ 1u }; - expectExternalBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); - expectEvent(ERendererEventType::ExternalBufferCreated); - - createBufferLink(buffer, SceneId{ 666u }, DataSlotId{ 1u }, true); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfDisplayInvalid) -{ - createBufferLink(OffscreenBufferHandle{ 3u }, SceneId{ 666u }, DataSlotId{ 1u }, true); - createBufferLink(StreamBufferHandle{ 3u }, SceneId{ 666u }, DataSlotId{ 1u }, true); -} - -TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfBufferUnknown_OB) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - constexpr OffscreenBufferHandle buffer{ 123u }; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - createBufferLink(buffer, getSceneId(), DataSlotId{ 1u }, true); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfBufferUnknown_EB) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - constexpr ExternalBufferHandle buffer{ 123u }; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - createBufferLink(buffer, getSceneId(), DataSlotId{ 1u }, true); - - EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); - - unmapScene(); - destroyDisplay(); -} - -///////////////////////////////////////////// -// Other tests -///////////////////////////////////////////// - -TEST_F(ARendererSceneUpdater, updateSceneStreamTexturesDirtinessGeneratesEventsForNewAndObsoleteStreamSurfaces) -{ - createDisplayAndExpectSuccess(); - - const WaylandIviSurfaceId newStreamId(7563u); - const WaylandIviSurfaceId obsoleteStreamId(8883u); - const WaylandIviSurfaceIdVector newStreams{ newStreamId }; - const WaylandIviSurfaceIdVector obsoleteStreams{ obsoleteStreamId }; - - expectNoEvent(); - - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(DoAll(SetArgReferee<1>(newStreams), SetArgReferee<2>(obsoleteStreams))); - - update(); - - RendererEventVector resultEvents; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, resultEvents); - ASSERT_EQ(2u, resultEvents.size()); - EXPECT_EQ(ERendererEventType::StreamSurfaceAvailable, resultEvents[0].eventType); - EXPECT_EQ(newStreamId, resultEvents[0].streamSourceId); - EXPECT_EQ(ERendererEventType::StreamSurfaceUnavailable, resultEvents[1].eventType); - EXPECT_EQ(obsoleteStreamId, resultEvents[1].streamSourceId); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, willShowSceneEvenIfFlushesPending) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - performFlushWithCreateNodeAction(); - mapScene(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - - // simulate effect not uploaded yet - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - // scene shown even if flush pending - showScene(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, appliesBigPendingWithinOneUpdate) -{ - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - performFlushWithCreateNodeAction(0, 10000); - performFlushWithCreateNodeAction(1, 10000); - performFlushWithCreateNodeAction(0, 10000); - performFlushWithCreateNodeAction(1, 10000); - performFlushWithCreateNodeAction(0, 10000); - performFlushWithCreateNodeAction(1, 10000); - - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(0u)); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(1u)); -} - -///////////////////////////////////////////// -// Tests for marking scenes as modified -///////////////////////////////////////////// - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfStreamBufferStateIsNotUpdated) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - constexpr StreamBufferHandle streamBuffer{ 13u }; - - const auto dataSlotId = createRenderableWithTextureConsumer(); - createBufferLink(streamBuffer, getSceneId(0u), dataSlotId); - update(); - - expectRenderableResourcesClean(); - - constexpr WaylandIviSurfaceId source{ 12u }; - const StreamUsage fakeStreamUsage{ {}, { streamBuffer} }; - const WaylandIviSurfaceIdVector changedSources{ source }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); - update(); - expectRenderableResourcesClean(); - - { - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)); - - expectNoModifiedScenesReportedToRenderer(); - update(); - } - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfStreamSourceContentIsUpdated_usedByStreamBufferLinkedToScenes) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - constexpr WaylandIviSurfaceId source{ 12u }; - constexpr StreamBufferHandle sb1{ 13u }; - constexpr StreamBufferHandle sb2{ 14u }; - const StreamUsage fakeStreamUsage{ sb1, sb2 }; - - // link both SBs to scenes - const auto dataSlotId1 = createTextureConsumer(0u); - const auto dataSlotId2 = createTextureConsumer(1u); - createBufferLink(sb1, getSceneId(0u), dataSlotId1); - createBufferLink(sb2, getSceneId(1u), dataSlotId2); - update(); - - StreamSourceUpdates updates{ {source, 1u } }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, hasUpdatedContentFromStreamSourcesToUpload()).WillOnce(Return(true)); - EXPECT_CALL(renderer.m_embeddedCompositingManager, uploadResourcesAndGetUpdates(_)).WillOnce(SetArgReferee<0>(updates)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); - - expectModifiedScenesReportedToRenderer({ 0u, 1u }); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfStreamSourceAvailabilityChanged_usedByStreamBufferLinkedToScenes) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - constexpr WaylandIviSurfaceId source{ 12u }; - constexpr StreamBufferHandle sb1{ 13u }; - constexpr StreamBufferHandle sb2{ 14u }; - const StreamUsage fakeStreamUsage{ sb1, sb2 }; - - // link both SBs to scenes - const auto dataSlotId1 = createTextureConsumer(0u); - const auto dataSlotId2 = createTextureConsumer(1u); - createBufferLink(sb1, getSceneId(0u), dataSlotId1); - createBufferLink(sb2, getSceneId(1u), dataSlotId2); - update(); - - WaylandIviSurfaceIdVector changedSources{ source }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); - - expectModifiedScenesReportedToRenderer({ 0u, 1u }); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, WillNotUnlinkStreamBuffer_IfStreamSourceBecameAvailabilityChanges_sameSource) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - constexpr WaylandIviSurfaceId source{ 12u }; - constexpr StreamBufferHandle sb1{ 13u }; - constexpr StreamBufferHandle sb2{ 14u }; - const StreamUsage fakeStreamUsage{ sb1, sb2 }; - - // link both SBs to scenes - const auto dataSlotId1 = createTextureConsumer(0u); - const auto dataSlotId2 = createTextureConsumer(1u); - createBufferLink(sb1, getSceneId(0u), dataSlotId1); - createBufferLink(sb2, getSceneId(1u), dataSlotId2); - update(); - - WaylandIviSurfaceIdVector changedSources{ source }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); - - expectModifiedScenesReportedToRenderer({ 0u, 1u }); - update(); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb1)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb2)); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, WillNotUnlinkStreamBuffer_IfOneOfStreamSourcesChangesAvailability) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - constexpr WaylandIviSurfaceId source1{ 12u }; - constexpr WaylandIviSurfaceId source2{ 13u }; - constexpr StreamBufferHandle sb1{ 13u }; - constexpr StreamBufferHandle sb2{ 14u }; - const StreamUsage fakeStreamUsage1{ {}, { sb1 } }; - const StreamUsage fakeStreamUsage2{ {}, { sb2 } }; - - // link both SBs to scenes - const auto dataSlotId1 = createTextureConsumer(0u); - const auto dataSlotId2 = createTextureConsumer(1u); - createBufferLink(sb1, getSceneId(0u), dataSlotId1); - createBufferLink(sb2, getSceneId(1u), dataSlotId2); - update(); - - const WaylandIviSurfaceIdVector changedSources{ source1, source2 }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source1)).WillOnce(ReturnRef(fakeStreamUsage1)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source2)).WillOnce(ReturnRef(fakeStreamUsage2)); - - expectModifiedScenesReportedToRenderer({ 1u }); - update(); - - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb1)); - EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb2)); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfNonEmptyFlushApplied) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - update(); - - performFlushWithCreateNodeAction(); - expectModifiedScenesReportedToRenderer(); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfEmptyFlushAppliedOnEmptyScene) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - update(); - - expectNoModifiedScenesReportedToRenderer(); - performFlush(0u); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfEmptyFlushAppliedOnNonEmptyScene) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - performFlushWithCreateNodeAction(); - expectModifiedScenesReportedToRenderer(); - update(); - - expectNoModifiedScenesReportedToRenderer(); - performFlush(0u); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfFlushAppliedWithResources) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - - expectModifiedScenesReportedToRenderer(); - expectVertexArrayUploaded(); - update(); - expectRenderableResourcesClean(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfPendingFlushNotApplied) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - - { - setRenderableResources(); - expectModifiedScenesReportedToRenderer(); - expectVertexArrayUploaded(); - update(); - expectRenderableResourcesClean(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - { - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - - expectNoModifiedScenesReportedToRenderer(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - } - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_AfterPendingFlushApplied) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - { - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectModifiedScenesReportedToRenderer(); - expectVertexArrayUploaded(); - update(); - expectRenderableResourcesClean(); - } - expectNoModifiedScenesReportedToRenderer(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - { - // blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).WillOnce(Return(DeviceResourceHandle::Invalid())).RetiresOnSaturation(); - } - expectNoModifiedScenesReportedToRenderer(); - expectVertexArrayUnloaded(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - { - // unblock flush - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); - } - expectModifiedScenesReportedToRenderer(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_IfSceneIsConsumerAndProviderSceneIsUpdated) -{ - // s0 [modified] -> s1 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - DataInstanceHandle consumerDataRef; - DataInstanceHandle providerDataRef; - createDataSlotsAndLinkThem(consumerDataRef, 333.f, &providerDataRef); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - updateProviderDataSlot(0u, providerDataRef, 777.f); - performFlush(); - expectModifiedScenesReportedToRenderer({0u, 1u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_DataLinking_IfSceneIsProviderAndConsumerIsUpdated) -{ - // s0 -> s1 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(1u); - showScene(1u); - - DataInstanceHandle consumerDataRef; - const float providedValue(333.f); - createDataSlotsAndLinkThem(consumerDataRef, providedValue); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - stagingScene[1u]->setDataSingleFloat(consumerDataRef, DataFieldHandle(0u), 324.f); - performFlush(1u); - expectModifiedScenesReportedToRenderer({1u}); //only consumer if marked as modified - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(1u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_IndirectlyDependantConsumersIfProviderUpdated) -{ - // s0 [modified] -> s1 [modified] -> s2 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - DataInstanceHandle providerDataRef; - { - //link scene 1 to scene 0 - DataInstanceHandle consumerDataRef; - createDataSlotsAndLinkThem(consumerDataRef, 333.f, &providerDataRef); - update(); - } - - { - //linke scene 2 to scene 1 - DataInstanceHandle consumerDataRef2; - createDataSlotsAndLinkThem(consumerDataRef2, 666.f, nullptr, 1u, 2u); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - updateProviderDataSlot(0u, providerDataRef, 1.0f); - performFlush(); - expectModifiedScenesReportedToRenderer({0u, 1u, 2u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_OnlyDependantConsumersIfProviderUpdated) -{ - // s0 -> s1 [modified] -> s2 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - { - //link scene 1 to scene 0 - DataInstanceHandle consumerDataRef; - createDataSlotsAndLinkThem(consumerDataRef, 333.f); - update(); - } - - DataInstanceHandle providerDataRef; - { - //linke scene 2 to scene 1 - DataInstanceHandle consumerDataRef2; - createDataSlotsAndLinkThem(consumerDataRef2, 666.f, &providerDataRef, 1u, 2u); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - updateProviderDataSlot(1u, providerDataRef, 1.0f); - performFlush(1u); - expectModifiedScenesReportedToRenderer({1u, 2u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_IfSceneConsumesFromAnotherConsumerOfDifferentLinkingType) -{ - // tex linking : s0 -> s1 - // data ref linking : s1 [modified] -> s2 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - { - //tex. link scene 1 to scene 0 - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); - createTextureSlotsAndLinkThem(); - update(); - } - - DataInstanceHandle providerDataRef; - { - //data ref. linke scene 2 to scene 1 - DataInstanceHandle consumerDataRef2; - createDataSlotsAndLinkThem(consumerDataRef2, 666.f, &providerDataRef, 1u, 2u); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - updateProviderDataSlot(1u, providerDataRef, 1.0f); - performFlush(1u); - expectModifiedScenesReportedToRenderer({1u, 2u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_ConfidenceTest) -{ - // s0 -> s1 [modified] -> s2 [modified] -> s3 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(1u); - mapScene(2u); - mapScene(3u); - showScene(1u); - showScene(2u); - showScene(3u); - - { - //link scene 1 to scene 0 - DataInstanceHandle consumerDataRef; - createDataSlotsAndLinkThem(consumerDataRef, 333.f); - update(); - } - - DataInstanceHandle providerDataRef; - { - //linke scene 2 to scene 1 - DataInstanceHandle consumerDataRef; - createDataSlotsAndLinkThem(consumerDataRef, 666.f, &providerDataRef, 1u, 2u); - update(); - } - - { - //linke scene 3 to scene 2 - DataInstanceHandle consumerDataRef; - createDataSlotsAndLinkThem(consumerDataRef, 766.f, nullptr, 2u, 3u); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - updateProviderDataSlot(1u, providerDataRef, 1.0f); - performFlush(1u); - expectModifiedScenesReportedToRenderer({1u, 2u, 3u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(1u); - hideScene(2u); - hideScene(3u); - unmapScene(1u); - unmapScene(2u); - unmapScene(3u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TextureLinking_IfSceneIsConsumerAndProviderSceneIsUpdated) -{ - // s0 [modified] -> s1 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - showScene(0u); - mapScene(1u); - showScene(1u); - - DataSlotHandle providerDataSlotHandle; - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); - createTextureSlotsAndLinkThem(&providerDataSlotHandle); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - expectResourcesUnreferenced({ MockResourceHash::TextureHash }); - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash2 }); - updateProviderTextureSlot(0u, providerDataSlotHandle, MockResourceHash::TextureHash2); - performFlush(); - expectModifiedScenesReportedToRenderer({0u, 1u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TextureLinking_ConfidenceTest) -{ - // s0 -> s1 [modified] -> s2 [modified] -> s3 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(); - mapScene(1u); - mapScene(2u); - mapScene(3u); - showScene(1u); - showScene(2u); - showScene(3u); - - { - //link scene 1 to scene 0 - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); - createTextureSlotsAndLinkThem(); - update(); - } - - DataSlotHandle providerDataSlotHandle; - { - //link scene 2 to scene 1 - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, 1u); - createTextureSlotsAndLinkThem(&providerDataSlotHandle, 1u, 2u); - update(); - } - - { - //link scene 3 to scene 2 - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, 2u); - createTextureSlotsAndLinkThem(nullptr, 2u, 3u); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - expectResourcesUnreferenced({ MockResourceHash::TextureHash }, 1u); - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash2 }, 1u); - updateProviderTextureSlot(1u, providerDataSlotHandle, MockResourceHash::TextureHash2); - performFlush(1u); - expectModifiedScenesReportedToRenderer({1u, 2u, 3u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(1u); - hideScene(2u); - hideScene(3u); - unmapScene(); - unmapScene(1u); - unmapScene(2u); - unmapScene(3u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TransformationLinking_IfSceneIsConsumerAndProviderSceneIsUpdated) -{ - // s0 [modified] -> s1 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - TransformHandle providerTransformHandle; - createTransformationSlotsAndLinkThem(&providerTransformHandle); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - stagingScene[0]->setTranslation(providerTransformHandle, {0.f, 1.f, 2.f}); - performFlush(); - expectModifiedScenesReportedToRenderer({0u, 1u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TransformationLinking_ConfidenceTest) -{ - // s0 -> s1 [modified] -> s2 [modified] -> s3 [modified] - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(); - mapScene(1u); - mapScene(2u); - mapScene(3u); - showScene(1u); - showScene(2u); - showScene(3u); - - { - //link scene 1 to scene 0 - createTransformationSlotsAndLinkThem(); - update(); - } - - TransformHandle providerTransformHandle; - { - //linke scene 2 to scene 1 - createTransformationSlotsAndLinkThem(&providerTransformHandle, 1u, 2u); - update(); - } - - { - //linke scene 3 to scene 2 - createTransformationSlotsAndLinkThem(nullptr, 2u, 3u); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - stagingScene[1]->setTranslation(providerTransformHandle, {0.f, 1.f, 2.f}); - performFlush(1u); - expectModifiedScenesReportedToRenderer({1u, 2u, 3u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(1u); - hideScene(2u); - hideScene(3u); - unmapScene(); - unmapScene(1u); - unmapScene(2u); - unmapScene(3u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_IfSceneConsumesFromModifiedOffscreenBuffer) -{ - // s0 [modified] -> ob1 -> s1 [modified] - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); - - const DataSlotId consumerId = createTextureConsumer(1u); - createBufferLink(buffer, stagingScene[1u]->getSceneId() , consumerId); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update scene mapped to buffer - performFlushWithCreateNodeAction(0u); - //expect both scenes modified - expectModifiedScenesReportedToRenderer({0u, 1u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_IfSceneConsumesFromModifiedOffscreenBufferWithSeveralScenes) -{ - // s0 -- - // |-> ob1 -> s2 [modified] - // s1 [modified] -- - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); - EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffer)); - - const DataSlotId consumerId = createTextureConsumer(2u); - createBufferLink(buffer, getSceneId(2u), consumerId); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update scene mapped to buffer - performFlushWithCreateNodeAction(1u); - - expectModifiedScenesReportedToRenderer({ 1u, 2u }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_OffscreenBufferLinking_UnmodifiedProviderToOffscreenBuffer) -{ - // s0 -> ob1 -> s1 [modified] - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); - - const DataSlotId consumerId = createTextureConsumer(1u); - createBufferLink(buffer, stagingScene[1u]->getSceneId(), consumerId); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update consumer - performFlushWithCreateNodeAction(1u); - //expect only consumer modified - expectModifiedScenesReportedToRenderer({ 1u }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_TwoConsumersFromModifiedOffscreenBuffer) -{ - // --> s1 [modified] - // s0 [modified] -> ob1-| - // --> s2 [modified] - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); - - { - const DataSlotId consumerId = createTextureConsumer(1u); - createBufferLink(buffer, stagingScene[1u]->getSceneId() , consumerId); - update(); - } - { - const DataSlotId consumerId = createTextureConsumer(2u); - createBufferLink(buffer, stagingScene[2u]->getSceneId() , consumerId); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update scene mapped to buffer - performFlushWithCreateNodeAction(0u); - //expect both scenes modified - expectModifiedScenesReportedToRenderer({0u, 1u, 2u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_IndirectConsumerFromModifiedOffscreenBuffer) -{ - // s0 [modified] -> ob1 -> s1 [modified] -> ob2 -> s2 [modified] - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer1(1u); - expectOffscreenBufferUploaded(buffer1); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer1, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - const OffscreenBufferHandle buffer2(2u); - const DeviceResourceHandle offscreenBufferDeviceHandle(5556u); - expectOffscreenBufferUploaded(buffer2, offscreenBufferDeviceHandle); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer2, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer1)); - - { - EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffer2)); - const DataSlotId consumerId = createTextureConsumer(1u); - createBufferLink(buffer1, stagingScene[1u]->getSceneId() , consumerId); - update(); - } - { - const DataSlotId consumerId = createTextureConsumer(2u); - createBufferLink(buffer2, stagingScene[2u]->getSceneId() , consumerId); - update(); - } - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update scene mapped to buffer 1 - performFlushWithCreateNodeAction(0u); - //expect all scenes modified - expectModifiedScenesReportedToRenderer({0u, 1u, 2u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_OffscreenBufferLinking_ConsumerFromUnmodifiedOffscreenBuffer) -{ - // s0 [modified] -> ob1 - // s1 -> ob2 -> s2 - createDisplayAndExpectSuccess(); - - const OffscreenBufferHandle buffer1(1u); - expectOffscreenBufferUploaded(buffer1); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer1, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - const OffscreenBufferHandle buffer2(2u); - const DeviceResourceHandle offscreenBufferDeviceHandle(5556u); - expectOffscreenBufferUploaded(buffer2, offscreenBufferDeviceHandle); - - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer2, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - mapScene(2u); - showScene(0u); - showScene(1u); - showScene(2u); - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer1)); - EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffer2)); - - const DataSlotId consumerId = createTextureConsumer(2u); - createBufferLink(buffer2, stagingScene[2u]->getSceneId() , consumerId); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update scene mapped to buffer 1 - performFlushWithCreateNodeAction(0u); - //expect only this scene to be modified - expectModifiedScenesReportedToRenderer({0u}); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(0u); - hideScene(1u); - hideScene(2u); - unmapScene(0u); - unmapScene(1u); - unmapScene(2u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_ConfidenceTest1) -{ - // --> s1 - // s0 -> ob0 -| --> s3 [modified] - // --> s2 [modified] -> ob1 -| - // --> s4 [modified] -> ob2 -> s5 [modified] - - createDisplayAndExpectSuccess(); - - const std::array buffers{ { OffscreenBufferHandle{ 1u }, OffscreenBufferHandle{ 2u }, OffscreenBufferHandle{ 3u } } }; - for (const auto buffer : buffers) - { - expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(buffer.asMemoryHandle())); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - } - - for (uint32_t i = 0u; i < 6; ++i) - { - createPublishAndSubscribeScene(); - mapScene(i); - showScene(i); - } - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffers[0])); - EXPECT_TRUE(assignSceneToDisplayBuffer(2, buffers[1])); - EXPECT_TRUE(assignSceneToDisplayBuffer(4, buffers[2])); - - { - const DataSlotId consumerId = createTextureConsumer(1u); - createBufferLink(buffers[0u], getSceneId(1u), consumerId); - } - { - const DataSlotId consumerId = createTextureConsumer(2u); - createBufferLink(buffers[0u], getSceneId(2u), consumerId); - } - { - const DataSlotId consumerId = createTextureConsumer(3u); - createBufferLink(buffers[1u], getSceneId(3u), consumerId); - } - { - const DataSlotId consumerId = createTextureConsumer(4u); - createBufferLink(buffers[1u], getSceneId(4u), consumerId); - } - { - const DataSlotId consumerId = createTextureConsumer(5u); - createBufferLink(buffers[2u], getSceneId(5u), consumerId); - } - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update s2 - performFlushWithCreateNodeAction(2u); - - expectModifiedScenesReportedToRenderer({ 2u, 3u, 4u, 5u }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - - for (uint32_t i = 0u; i < 6; ++i) - { - hideScene(i); - unmapScene(i); - } - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_ConfidenceTest2) -{ - // s0 -- - // |-> ob0 -> s2 ------------> ob1 -- - // s1 -- |-> s4 [modified] - // s3 [modified] -> ob2 -- - - createDisplayAndExpectSuccess(); - - const std::array buffers{ { OffscreenBufferHandle{ 1u }, OffscreenBufferHandle{ 2u }, OffscreenBufferHandle{ 3u } } }; - for (const auto buffer : buffers) - { - expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(buffer.asMemoryHandle())); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - } - - for (uint32_t i = 0u; i < 5; ++i) - { - createPublishAndSubscribeScene(); - mapScene(i); - showScene(i); - } - - EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffers[0])); - EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffers[0])); - EXPECT_TRUE(assignSceneToDisplayBuffer(2, buffers[1])); - EXPECT_TRUE(assignSceneToDisplayBuffer(3, buffers[2])); - - { - const DataSlotId consumerId = createTextureConsumer(2u); - createBufferLink(buffers[0u], getSceneId(2u), consumerId); - } - { - const DataSlotId consumerId = createTextureConsumer(4u); - createBufferLink(buffers[1u], getSceneId(4u), consumerId); - } - { - const DataSlotId consumerId = createTextureConsumer(4u); - createBufferLink(buffers[2u], getSceneId(4u), consumerId); - } - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - //update s3 - performFlushWithCreateNodeAction(3u); - - expectModifiedScenesReportedToRenderer({ 3u, 4u }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - for (uint32_t i = 0u; i < 5; ++i) - { - hideScene(i); - unmapScene(i); - } - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfShaderAnimationIsActive) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - - createRenderable(); - setRenderableResources(); - - expectModifiedScenesReportedToRenderer(); - expectVertexArrayUploaded(); - update(); - expectRenderableResourcesClean(); - - // simulate rendering - auto& rendererScene = rendererScenes.getScene(stagingScene[0]->getSceneId()); - EXPECT_FALSE(rendererScene.hasActiveShaderAnimation()); - rendererScene.setActiveShaderAnimation(true); - - expectModifiedScenesReportedToRenderer(); - update(); - - // flush resets shader animation - EXPECT_TRUE(rendererScene.hasActiveShaderAnimation()); - performFlush(); - // empty flush marks scene modified if shader animation was active before - expectModifiedScenesReportedToRenderer(); - update(); - EXPECT_FALSE(rendererScene.hasActiveShaderAnimation()); - - performFlush(); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfOtherSceneHasShaderAnimation) -{ - createDisplayAndExpectSuccess(); - - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - mapScene(0); - mapScene(1); - - showScene(0); - showScene(1); - - // simulate rendering - auto& rendererScene1 = rendererScenes.getScene(getSceneId(0)); - auto& rendererScene2 = rendererScenes.getScene(getSceneId(1)); - - EXPECT_FALSE(rendererScene1.hasActiveShaderAnimation()); - EXPECT_FALSE(rendererScene2.hasActiveShaderAnimation()); - rendererScene2.setActiveShaderAnimation(true); - - expectModifiedScenesReportedToRenderer({1}); - update(); - - EXPECT_FALSE(rendererScene1.hasActiveShaderAnimation()); - EXPECT_TRUE(rendererScene2.hasActiveShaderAnimation()); - - hideScene(0); - hideScene(1); - unmapScene(0); - unmapScene(1); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfEffectTimeIsSynced) -{ - createDisplayAndExpectSuccess(); - const auto scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - - createRenderable(); - setRenderableResources(); - - expectModifiedScenesReportedToRenderer(); - expectVertexArrayUploaded(); - update(); - expectRenderableResourcesClean(); - - const auto& rendererScene = rendererScenes.getScene(stagingScene[0]->getSceneId()); - EXPECT_EQ(FlushTime::InvalidTimestamp, rendererScene.getEffectTimeSync()); - performFlushWithUniformTimeSync(scene, 1000u); - - expectModifiedScenesReportedToRenderer(); - update(); - EXPECT_EQ(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u)), rendererScene.getEffectTimeSync()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfOffscreenBufferLinkedToScene) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene1 = createPublishAndSubscribeScene(); - const uint32_t scene2 = createPublishAndSubscribeScene(); - mapScene(scene1); - mapScene(scene2); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_TRUE(assignSceneToDisplayBuffer(scene2, buffer)); - - const DataSlotId consumer = createTextureConsumer(scene1); - - showScene(scene1); - showScene(scene2); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - createBufferLink(buffer, getSceneId(scene1), consumer); - expectModifiedScenesReportedToRenderer({ scene1 }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(scene1); - hideScene(scene2); - unmapScene(scene1); - unmapScene(scene2); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfOffscreenBufferUnlinkedFromScene) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene1 = createPublishAndSubscribeScene(); - const uint32_t scene2 = createPublishAndSubscribeScene(); - mapScene(scene1); - mapScene(scene2); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_TRUE(assignSceneToDisplayBuffer(scene2, buffer)); - - const DataSlotId consumer = createTextureConsumer(scene1); - - showScene(scene1); - showScene(scene2); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - createBufferLink(buffer, getSceneId(scene1), consumer); - expectModifiedScenesReportedToRenderer({ scene1 }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - unlinkConsumer(getSceneId(scene1), consumer); - expectModifiedScenesReportedToRenderer({ scene1 }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(scene1); - hideScene(scene2); - unmapScene(scene1); - unmapScene(scene2); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_WhenProviderSceneAssignedToOBIsShownAfterOBLinked) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene1 = createPublishAndSubscribeScene(); - const uint32_t scene2 = createPublishAndSubscribeScene(); - const DataSlotId consumer = createTextureConsumer(scene1); - mapScene(scene1); - mapScene(scene2); - showScene(scene1); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_TRUE(assignSceneToDisplayBuffer(scene2, buffer)); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - createBufferLink(buffer, getSceneId(scene1), consumer); - expectModifiedScenesReportedToRenderer({ scene1 }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - expectModifiedScenesReportedToRenderer({ scene1, scene2 }); - showScene(scene2); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(scene1); - hideScene(scene2); - unmapScene(scene1); - unmapScene(scene2); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_IfTransformationConsumerAndProviderLinkedOrUnlinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t providerScene = createPublishAndSubscribeScene(); - const uint32_t consumerScene = createPublishAndSubscribeScene(); - mapScene(providerScene); - mapScene(consumerScene); - showScene(providerScene); - showScene(consumerScene); - - const auto providerConsumer = createTransformationSlots(nullptr, providerScene, consumerScene); - update(); - - linkProviderToConsumer(getSceneId(providerScene), providerConsumer.first, getSceneId(consumerScene), providerConsumer.second); - expectModifiedScenesReportedToRenderer({ consumerScene }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - unlinkConsumer(getSceneId(consumerScene), providerConsumer.second); - expectModifiedScenesReportedToRenderer({ consumerScene }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(providerScene); - hideScene(consumerScene); - unmapScene(providerScene); - unmapScene(consumerScene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_IfDataConsumerAndProviderLinkedOrUnlinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t providerScene = createPublishAndSubscribeScene(); - const uint32_t consumerScene = createPublishAndSubscribeScene(); - mapScene(providerScene); - mapScene(consumerScene); - showScene(providerScene); - showScene(consumerScene); - - DataInstanceHandle dataRef; - const auto providerConsumer = createDataSlots(dataRef, 1.f, nullptr, providerScene, consumerScene); - update(); - - linkProviderToConsumer(getSceneId(providerScene), providerConsumer.first, getSceneId(consumerScene), providerConsumer.second); - expectModifiedScenesReportedToRenderer({ consumerScene }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - unlinkConsumer(getSceneId(consumerScene), providerConsumer.second); - expectModifiedScenesReportedToRenderer({ consumerScene }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(providerScene); - hideScene(consumerScene); - unmapScene(providerScene); - unmapScene(consumerScene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarkSceneAsModified_IfTextureConsumerAndProviderLinkedOrUnlinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t providerScene = createPublishAndSubscribeScene(); - const uint32_t consumerScene = createPublishAndSubscribeScene(); - mapScene(providerScene); - mapScene(consumerScene); - showScene(providerScene); - showScene(consumerScene); - - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, providerScene); - const auto providerConsumer = createTextureSlots(nullptr, providerScene, consumerScene); - update(); - - linkProviderToConsumer(getSceneId(providerScene), providerConsumer.first, getSceneId(consumerScene), providerConsumer.second); - expectModifiedScenesReportedToRenderer({ consumerScene }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - unlinkConsumer(getSceneId(consumerScene), providerConsumer.second); - expectModifiedScenesReportedToRenderer({ consumerScene }); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(providerScene); - hideScene(consumerScene); - unmapScene(providerScene); - unmapScene(consumerScene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfModifiedSceneNotShown) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - update(); - - performFlushWithCreateNodeAction(); - expectNoModifiedScenesReportedToRenderer(); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfSceneNotModifiedButSkippingDisabled) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - update(); - - performFlushWithCreateNodeAction(); - expectModifiedScenesReportedToRenderer(); - update(); - - expectNoModifiedScenesReportedToRenderer(); - update(); - - // no change to scene but disabled skipping feature - rendererSceneUpdater->setSkippingOfUnmodifiedScenes(false); - expectModifiedScenesReportedToRenderer(); - update(); - - expectModifiedScenesReportedToRenderer(); - update(); - - rendererSceneUpdater->setSkippingOfUnmodifiedScenes(true); - expectNoModifiedScenesReportedToRenderer(); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfSkippingDisabledButSceneNotShown) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - update(); - - performFlushWithCreateNodeAction(); - expectNoModifiedScenesReportedToRenderer(); - update(); - - // no change to scene but disabled skipping feature - rendererSceneUpdater->setSkippingOfUnmodifiedScenes(false); - // still not reported to re-render because not shown - expectNoModifiedScenesReportedToRenderer(); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfInterruptedSceneIsHidden) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(sceneInterrupted); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneIsHidden) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneIsShown) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - const uint32_t sceneToShow = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - mapScene(sceneToShow); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - showScene(sceneToShow); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(sceneToShow); - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - unmapScene(sceneToShow); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotResetInterruptedRenderingIfAnotherSceneIsMapped) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - const uint32_t sceneToMap = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - mapScene(sceneToMap); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - unmapScene(sceneToMap); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotResetInterruptedRenderingIfAnotherSceneIsUnmapped) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - const uint32_t sceneToUnmap = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - mapScene(sceneToUnmap); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - unmapScene(sceneToUnmap); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfInterruptedSceneAssignedToFramebuffer) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - EXPECT_TRUE(assignSceneToDisplayBuffer(sceneInterrupted)); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneAssignedToFramebuffer) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - const uint32_t sceneToAssign = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - mapScene(sceneToAssign); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(321u)); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_TRUE(assignSceneToDisplayBuffer(sceneToAssign, buffer)); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - EXPECT_TRUE(assignSceneToDisplayBuffer(sceneToAssign)); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - unmapScene(sceneToAssign); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneAssignedToOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - const auto buffer = showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - EXPECT_TRUE(assignSceneToDisplayBuffer(scene, buffer)); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneLinkedToOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - const DataSlotId texConsumer = createTextureConsumer(scene); - - const auto buffer = showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - createBufferLink(buffer, getSceneId(scene), texConsumer); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneUnlinkedFromOffscreenBuffer) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - const uint32_t sceneToUnlink = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - mapScene(sceneToUnlink); - - const OffscreenBufferHandle buffer(1u); - expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(321u)); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - const DataSlotId texConsumer = createTextureConsumer(sceneToUnlink); - createBufferLink(buffer, getSceneId(sceneToUnlink), texConsumer); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - unlinkConsumer(getSceneId(sceneToUnlink), texConsumer); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - unmapScene(sceneToUnlink); - - expectOffscreenBufferDeleted(buffer); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTransformationLinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - // provider/consumer order does not matter here - const auto providerConsumerId = createTransformationSlots(nullptr, scene, sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTransformationUnlinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - // provider/consumer order does not matter here - const auto providerConsumerId = createTransformationSlots(nullptr, scene, sceneInterrupted); - linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - unlinkConsumer(getSceneId(sceneInterrupted), providerConsumerId.second); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfDataLinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - // provider/consumer order does not matter here - DataInstanceHandle dataRef; - const auto providerConsumerId = createDataSlots(dataRef, 1.f, nullptr, scene, sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfDataUnlinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - // provider/consumer order does not matter here - DataInstanceHandle dataRef; - const auto providerConsumerId = createDataSlots(dataRef, 1.f, nullptr, scene, sceneInterrupted); - linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - unlinkConsumer(getSceneId(sceneInterrupted), providerConsumerId.second); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTexturesLinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - // provider/consumer order does not matter here - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, scene); - const auto providerConsumerId = createTextureSlots(nullptr, scene, sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTexturesUnlinked) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - // provider/consumer order does not matter here - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, scene); - const auto providerConsumerId = createTextureSlots(nullptr, scene, sceneInterrupted); - linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - unlinkConsumer(getSceneId(sceneInterrupted), providerConsumerId.second); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfOffscreenBufferCreated) -{ - createDisplayAndExpectSuccess(); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - const OffscreenBufferHandle buffer2(1u); - expectOffscreenBufferUploaded(buffer2, DeviceResourceHandle(321u)); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer2, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - expectOffscreenBufferDeleted(buffer2); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer2)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfOffscreenBufferDeleted) -{ - createDisplayAndExpectSuccess(); - - constexpr OffscreenBufferHandle buffer(1u); - constexpr DeviceResourceHandle bufferDeviceHandle{ 321u }; - expectOffscreenBufferUploaded(buffer, bufferDeviceHandle); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - expectOffscreenBufferDeleted(buffer, bufferDeviceHandle); - EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); - - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, blocksFlushesForSceneAssignedToInterruptibleOBWhenThereIsInterruption) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - performFlush(sceneInterrupted); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - performFlush(sceneInterrupted); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - - performFlush(sceneInterrupted); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - - renderer.resetRenderInterruptState(); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotBlockFlushesForSceneAssignedToNormalOBWhenThereIsInterruption) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - mapScene(scene); - mapScene(sceneInterrupted); - - performFlush(sceneInterrupted); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(scene)); - - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - performFlush(scene); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - - performFlush(scene); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - - renderer.resetRenderInterruptState(); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(scene)); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfMaximumNumberOfPendingFlushesReached) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); - - createRenderable(sceneInterrupted); - setRenderableResources(sceneInterrupted); - - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, sceneInterrupted); - mapScene(scene); - mapScene(sceneInterrupted); - - expectVertexArrayUploaded(sceneInterrupted); - showAndInitiateInterruptedRendering(scene, sceneInterrupted); - - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - // add blocking flush so that upcoming flushes are queuing up - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }, sceneInterrupted); - setRenderableResources(sceneInterrupted, MockResourceHash::IndexArrayHash2); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - update(); - - // flushes are blocked due to unresolved resource - const SceneVersionTag pendingFlushTag(124u); - performFlush(sceneInterrupted, pendingFlushTag); - update(); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - // will force apply and log blocking resources - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, sceneInterrupted); - expectVertexArrayUnloaded(sceneInterrupted); - for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(sceneInterrupted); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - expectSceneEvent(ERendererEventType::SceneFlushed); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - - hideScene(scene); - hideScene(sceneInterrupted); - unmapScene(scene); - unmapScene(sceneInterrupted); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsExpirationForSubscribedSceneButWithNoFurtherFlushes) -{ - const uint32_t scene = createPublishAndSubscribeScene(false); - // initial flush and enable monitoring - performFlushWithExpiration(scene, 1000u + 1u); - expectInternalSceneStateEvent(ERendererEventType::SceneSubscribed); - - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - // no further flush, no expiration updates - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - - expectSceneEvent(ERendererEventType::SceneExpired); -} - -TEST_F(ARendererSceneUpdater, doesNotReportExpirationForNotShownSceneBeingFlushedRegularly) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - // enable monitoring - performFlushWithExpiration(scene, 2000); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - expectNoEvent(); - } -} - -TEST_F(ARendererSceneUpdater, reportsExpirationForNotShownSceneNotBeingFlushed) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - // flush once only to set limit - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - - expectSceneEvent(ERendererEventType::SceneExpired); -} - -TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpiredForNotShownScene) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - // flush once only to set limit - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - // expect exactly one event - expectSceneEvent(ERendererEventType::SceneExpired); - - for (uint32_t i = 10u; i < 20u; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - // expect exactly one event - expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); -} - -TEST_F(ARendererSceneUpdater, doesNotReportExpirationForSceneBeingFlushedAndRenderedRegularly) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // enable monitoring - performFlushWithExpiration(scene, 2000); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.onRendered(getSceneId()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - expectNoEvent(); - } - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsExpirationForSceneBeingFlushedButNotRenderedRegularly) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // scene must be rendered at least once with valid expiration, only then it can expire - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - const NodeHandle nodeHandle(3u); - const TransformHandle transform(2u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - sceneAllocator.allocateTransform(nodeHandle, transform); - - for (uint32_t i = 0u; i < 10u; ++i) - { - iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpired); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotReportExpirationForSceneBeingFlushedButNotRenderedBecauseItIsHidden) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // scene must be rendered at least once with valid expiration, only then it can expire - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - hideScene(); - - const NodeHandle nodeHandle(3u); - const TransformHandle transform(2u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - sceneAllocator.allocateTransform(nodeHandle, transform); - - for (uint32_t i = 0u; i < 10u; ++i) - { - iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectNoEvent(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsExpirationForSceneNotBeingFlushedOnlyRendered) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - // flush once only to set limit - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - expirationMonitor.onRendered(getSceneId()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - - expectSceneEvent(ERendererEventType::SceneExpired); -} - -TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpirationForSceneNotBeingRenderedRegularly_byRenderingRegularlyAgain) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // scene must be rendered at least once with valid expiration, only then it can expire - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - const NodeHandle nodeHandle(3u); - const TransformHandle transform(2u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - sceneAllocator.allocateTransform(nodeHandle, transform); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpired); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.onRendered(getSceneId()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpirationForSceneNotBeingRenderedRegularly_byHidingSceneAndKeepingRegularFlushes) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // scene must be rendered at least once with valid expiration, only then it can expire - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - const NodeHandle nodeHandle(3u); - const TransformHandle transform(2u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - sceneAllocator.allocateTransform(nodeHandle, transform); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpired); - - hideScene(); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotReportExpirationForSceneBeingFlushedRegularlyButSkippedRenderingDueToEmptyFlushes) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // enable monitoring - performFlushWithExpiration(scene, 2000); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - expectNoEvent(); - } - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsExpirationForNotShownSceneBeingFlushedButBlockedByMissingResource) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - - // flush with expiration info to initiate monitoring - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - // blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(scene); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); // blocked due to missing resource - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpired); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsExpirationSceneBeingFlushedAndRenderedButBlockedByMissingResource) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // flush with expiration info to initiate monitoring - performFlushWithExpiration(scene, 1000u + 2u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - // blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(scene); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2; ++i) - { - performFlushWithExpiration(scene, 1000u + i + 2u); // blocked due to missing resource - update(); - expirationMonitor.onRendered(getSceneId()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpired); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpirationForSceneBeingFlushedAndRenderedButBlockedByMissingResource) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - uint32_t expTS = 1000u + 2u; - - // flush with expiration info to initiate monitoring - performFlushWithExpiration(scene, expTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - // blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderableNoFlush(scene); - performFlushWithExpiration(scene, expTS); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - expectVertexArrayUploaded(); - update(); - - for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) - { - performFlushWithExpiration(scene, expTS++); // blocked due to missing resource - update(); - expirationMonitor.onRendered(getSceneId()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpired); - - // simulate upload - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - update(); - - for (uint32_t i = ForceApplyFlushesLimit / 2u; i < ForceApplyFlushesLimit; ++i) - { - performFlushWithExpiration(scene, expTS++); // not blocked anymore - update(); - expirationMonitor.onRendered(getSceneId()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withEmptyFlush) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - update(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 1u; i < 10u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); - } - expectNoEvent(); - - // disable expiration - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withNonEmptyFlush) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - update(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 1u; i < 10u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); - } - expectNoEvent(); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(3u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withEmptyFlush_rendered) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 1u; i < 10u; ++i) - { - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); - } - expectNoEvent(); - - // disable expiration - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - hideScene(scene); - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withNonEmptyFlush_rendered) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 1u; i < 10u; ++i) - { - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); - } - expectNoEvent(); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(3u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - hideScene(scene); - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withEmptyFlush_hiddenAfterRendered) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 1u; i < 10u; ++i) - { - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); - } - expectNoEvent(); - - hideScene(scene); - - // disable expiration - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withNonEmptyFlush_hiddenAfterRendered) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 1u; i < 10u; ++i) - { - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); - } - expectNoEvent(); - - hideScene(scene); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(3u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_whileModifyingScene_confidenceTest) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - // make change - const NodeHandle nodeHandle(i); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, initialTS + i); - - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i - 1))); - } - expectNoEvent(); - - hideScene(scene); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(10u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 10u; i < 20u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_whenHiddenInBetweenFlushes_confidenceTest) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - // make change - const NodeHandle nodeHandle(i); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, initialTS + i); - - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i - 1))); - - // hide scene in between applying flushes with timestamps - if (i == 8) - hideScene(scene); - } - expectNoEvent(); - - // disable expiration - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 10u; i < 20u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_whenHiddenInBetweenEmptyFlushes_confidenceTest) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - update(); - doRenderLoop(); - - // flush once only to set limit - const uint32_t initialTS = 1000u; - performFlushWithExpiration(scene, initialTS); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); - - for (uint32_t i = 0u; i < 10u; ++i) - { - performFlushWithExpiration(scene, initialTS + i); - - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i - 1))); - - // hide scene in between applying flushes with timestamps - if (i == 8) - hideScene(scene); - } - expectNoEvent(); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(10u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - update(); - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - for (uint32_t i = 10u; i < 20u; ++i) - { - update(); - doRenderLoop(); - // check with TS after initial TS - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); - } - // expect no expiration as last flush disabled expiration - expectNoEvent(); - - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - - for (uint32_t i = 0u; i < 5u; ++i) - { - performFlushWithExpiration(scene, 1000u + 2u); // will expire - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); - - // disable expiration - performFlushWithExpiration(scene, 0u); - - for (uint32_t i = 0u; i < 5u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired_rendered) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - for (uint32_t i = 0u; i < 5u; ++i) - { - performFlushWithExpiration(scene, 1000u + 2u); // will expire - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); - - // disable expiration - performFlushWithExpiration(scene, 0u); - - for (uint32_t i = 0u; i < 5u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - hideScene(scene); - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired_nonEmptyFlush) -{ - const uint32_t scene = createPublishAndSubscribeScene(); - - for (uint32_t i = 0u; i < 5u; ++i) - { - performFlushWithExpiration(scene, 1000u + 2u); // will expire - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(10u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - - for (uint32_t i = 0u; i < 5u; ++i) - { - update(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); -} - -TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired_rendered_nonEmptyFlush) -{ - createDisplayAndExpectSuccess(); - const uint32_t scene = createPublishAndSubscribeScene(); - mapScene(scene); - showScene(scene); - - for (uint32_t i = 0u; i < 5u; ++i) - { - performFlushWithExpiration(scene, 1000u + 2u); // will expire - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); - - // disable expiration together with some scene changes - const NodeHandle nodeHandle(10u); - IScene& iscene = *stagingScene[scene]; - SceneAllocateHelper sceneAllocator(iscene); - sceneAllocator.allocateNode(0u, nodeHandle); - performFlushWithExpiration(scene, 0u); - - for (uint32_t i = 0u; i < 5u; ++i) - { - update(); - doRenderLoop(); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); - } - expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); - - hideScene(scene); - unmapScene(scene); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, ignoresPickEventForUnknownScene) -{ - rendererSceneUpdater->handlePickEvent(SceneId{ 123u }, { 0, 0 }); - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, reportsNoPickedObjectsForSceneWithNoPickableObjects) -{ - createPublishAndSubscribeScene(); - rendererSceneUpdater->handlePickEvent(getSceneId(), { 0, 0 }); - expectNoEvent(); -} - -TEST_F(ARendererSceneUpdater, reportsPickedObjects) -{ - DisplayConfig config; - config.setDesiredWindowWidth(1280u); - config.setDesiredWindowHeight(480u); - createDisplayAndExpectSuccess(config); - const auto sceneIdx = createPublishAndSubscribeScene(); - mapScene(); - - // create scene with 2 pickable triangles around origin - IScene& iscene = *stagingScene[sceneIdx]; - SceneAllocateHelper sceneAllocator(iscene); - const NodeHandle nodeHandle(0u); - sceneAllocator.allocateNode(0u, nodeHandle); - const std::array geomData{ -1.f, 0.f, -0.5f, 0.f, 1.f, -0.5f, 0.f, 0.f, -0.5f }; - const auto geomHandle = sceneAllocator.allocateDataBuffer(EDataBufferType::VertexBuffer, EDataType::Vector3F, uint32_t(geomData.size() * sizeof(float))); - iscene.updateDataBuffer(geomHandle, 0, uint32_t(geomData.size() * sizeof(float)), reinterpret_cast(geomData.data())); - - const auto dataLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference} }, {}); - const auto dataInstance = sceneAllocator.allocateDataInstance(dataLayout); - const auto vpDataRefLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I} }, {}); - const auto vpOffsetInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); - const auto vpSizeInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); - const auto frustumPlanesLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector4F} }, {}); - const auto frustumPlanes = sceneAllocator.allocateDataInstance(frustumPlanesLayout); - const auto frustumNearFarLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2F} }, {}); - const auto frustumNearFar = sceneAllocator.allocateDataInstance(frustumNearFarLayout); - iscene.setDataReference(dataInstance, Camera::ViewportOffsetField, vpOffsetInstance); - iscene.setDataReference(dataInstance, Camera::ViewportSizeField, vpSizeInstance); - iscene.setDataReference(dataInstance, Camera::FrustumPlanesField, frustumPlanes); - iscene.setDataReference(dataInstance, Camera::FrustumNearFarPlanesField, frustumNearFar); - const auto cameraHandle = sceneAllocator.allocateCamera(ECameraProjectionType::Orthographic, nodeHandle, dataInstance); - - iscene.setDataSingleVector2i(vpOffsetInstance, ramses_internal::DataFieldHandle{ 0 }, { 0, 0 }); - iscene.setDataSingleVector2i(vpSizeInstance, ramses_internal::DataFieldHandle{ 0 }, { 1280, 480 }); - const ProjectionParams params = ramses_internal::ProjectionParams::Frustum(ECameraProjectionType::Orthographic, -1.f, 1.f, -1.f, 1.f, 0.1f, 100.f); - iscene.setDataSingleVector4f(frustumPlanes, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); - iscene.setDataSingleVector2f(frustumNearFar, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); - - const PickableObjectId id1{ 666u }; - const PickableObjectId id2{ 667u }; - const auto pickableHandle1 = sceneAllocator.allocatePickableObject(geomHandle, nodeHandle, id1); - const auto pickableHandle2 = sceneAllocator.allocatePickableObject(geomHandle, nodeHandle, id2); - iscene.setPickableObjectCamera(pickableHandle1, cameraHandle); - iscene.setPickableObjectCamera(pickableHandle2, cameraHandle); - performFlush(); - - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadDataBuffer(_, _, _, _, _)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, updateDataBuffer(_, _, _, _)); - update(); - - EXPECT_CALL(*rendererSceneUpdater, handlePickEvent(_, _)); - rendererSceneUpdater->handlePickEvent(getSceneId(), { -0.375000f, 0.250000f }); - expectSceneEvent(ERendererEventType::ObjectsPicked); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, emitsSequenceOfSceneStateChangesWhenRepublished_fromSubscribed) -{ - createPublishAndSubscribeScene(); - - const SceneId sceneId = stagingScene[0]->getSceneId(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); - rendererSceneUpdater->handleSceneUnpublished(sceneId); - rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote); - expectInternalSceneStateEvents({ ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished, ERendererEventType::ScenePublished }); -} - -TEST_F(ARendererSceneUpdater, emitsSequenceOfSceneStateChangesWhenRepublished_fromMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectUnloadOfSceneResources(); - - const SceneId sceneId = stagingScene[0]->getSceneId(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); - rendererSceneUpdater->handleSceneUnpublished(sceneId); - rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote); - expectInternalSceneStateEvents({ ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished, ERendererEventType::ScenePublished }); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, emitsSequenceOfSceneStateChangesWhenRepublished_fromShown) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectUnloadOfSceneResources(); - - const SceneId sceneId = stagingScene[0]->getSceneId(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); - rendererSceneUpdater->handleSceneUnpublished(sceneId); - rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote); - expectInternalSceneStateEvents({ ERendererEventType::SceneHiddenIndirect, ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished, ERendererEventType::ScenePublished }); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, propagatesGeneratedSceneReferenceActionsToSceneReferenceControl) -{ - createPublishAndSubscribeScene(); - - constexpr SceneReferenceAction action1{ SceneReferenceActionType::LinkData, SceneReferenceHandle{1}, DataSlotId{2}, SceneReferenceHandle{3}, DataSlotId{4} }; - constexpr SceneReferenceAction action2{ SceneReferenceActionType::UnlinkData, SceneReferenceHandle{5}, DataSlotId{6}, SceneReferenceHandle{7}, DataSlotId{8} }; - const SceneReferenceActionVector sceneRefActions{ action1, action2 }; - - performFlush(0u, {}, nullptr, {}, sceneRefActions); - - EXPECT_CALL(sceneReferenceLogic, addActions(getSceneId(), _)).WillOnce([&](auto, const auto& actions) - { - ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(action1.type, actions[0].type); - EXPECT_EQ(action1.providerScene, actions[0].providerScene); - EXPECT_EQ(action1.providerId, actions[0].providerId); - EXPECT_EQ(action1.consumerScene, actions[0].consumerScene); - EXPECT_EQ(action1.consumerId, actions[0].consumerId); - EXPECT_EQ(action2.type, actions[1].type); - EXPECT_EQ(action2.providerScene, actions[1].providerScene); - EXPECT_EQ(action2.providerId, actions[1].providerId); - EXPECT_EQ(action2.consumerScene, actions[1].consumerScene); - EXPECT_EQ(action2.consumerId, actions[1].consumerId); - }); - update(); - - //make empty flush to make sure actions were consumed - performFlush(0u, {}, nullptr, {}, {}); - EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0u); - update(); -} - -TEST_F(ARendererSceneUpdater, propagatesGeneratedSceneReferenceActionsToSceneReferenceControlOnlyAfterFlushApplied) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // simulate blocking flush on missing resource - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - expectVertexArrayUploaded(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // send scene reference actions in flush - constexpr SceneReferenceAction action1{ SceneReferenceActionType::LinkData, SceneReferenceHandle{1}, DataSlotId{2}, SceneReferenceHandle{3}, DataSlotId{4} }; - constexpr SceneReferenceAction action2{ SceneReferenceActionType::UnlinkData, SceneReferenceHandle{5}, DataSlotId{6}, SceneReferenceHandle{7}, DataSlotId{8} }; - const SceneReferenceActionVector sceneRefActions{ action1, action2 }; - performFlush(0u, {}, nullptr, {}, sceneRefActions); - EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock pending flushes by providing resource - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - - EXPECT_CALL(sceneReferenceLogic, addActions(getSceneId(), _)).WillOnce([&](auto, const auto& actions) - { - ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(action1.type, actions[0].type); - EXPECT_EQ(action1.providerScene, actions[0].providerScene); - EXPECT_EQ(action1.providerId, actions[0].providerId); - EXPECT_EQ(action1.consumerScene, actions[0].consumerScene); - EXPECT_EQ(action1.consumerId, actions[0].consumerId); - EXPECT_EQ(action2.type, actions[1].type); - EXPECT_EQ(action2.providerScene, actions[1].providerScene); - EXPECT_EQ(action2.providerId, actions[1].providerId); - EXPECT_EQ(action2.consumerScene, actions[1].consumerScene); - EXPECT_EQ(action2.consumerId, actions[1].consumerId); - }); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - //make empty flush to make sure actions were consumed - performFlush(0u, {}, nullptr, {}, {}); - EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0u); - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, queuesAndPropagatesSceneReferenceActionsFromMultipleFlushesToSceneReferenceControl) -{ - createPublishAndSubscribeScene(); - - constexpr SceneReferenceAction action1{ SceneReferenceActionType::LinkData, SceneReferenceHandle{1}, DataSlotId{2}, SceneReferenceHandle{3}, DataSlotId{4} }; - constexpr SceneReferenceAction action2{ SceneReferenceActionType::UnlinkData, SceneReferenceHandle{5}, DataSlotId{6}, SceneReferenceHandle{7}, DataSlotId{8} }; - - performFlush(0u, {}, nullptr, {}, { action1 }); - performFlush(0u, {}, nullptr, {}, {}); - performFlush(0u, {}, nullptr, {}, { action2 }); - - EXPECT_CALL(sceneReferenceLogic, addActions(getSceneId(), _)).WillOnce([&](auto, const auto& actions) - { - ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(action1.type, actions[0].type); - EXPECT_EQ(action1.providerScene, actions[0].providerScene); - EXPECT_EQ(action1.providerId, actions[0].providerId); - EXPECT_EQ(action1.consumerScene, actions[0].consumerScene); - EXPECT_EQ(action1.consumerId, actions[0].consumerId); - EXPECT_EQ(action2.type, actions[1].type); - EXPECT_EQ(action2.providerScene, actions[1].providerScene); - EXPECT_EQ(action2.providerId, actions[1].providerId); - EXPECT_EQ(action2.consumerScene, actions[1].consumerScene); - EXPECT_EQ(action2.consumerId, actions[1].consumerId); - }); - update(); - - //make empty flush to make sure actions were consumed - performFlush(0u, {}, nullptr, {}, {}); - EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0u); - update(); -} - -} diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.h b/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.h deleted file mode 100644 index 97fbf27ea..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.h +++ /dev/null @@ -1,1031 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERSCENEUPDATERTEST_H -#define RAMSES_RENDERERSCENEUPDATERTEST_H - -#include "renderer_common_gmock_header.h" -#include "RendererMock.h" -#include "RendererEventCollector.h" -#include "SceneAPI/TextureSampler.h" -#include "Scene/ActionCollectingScene.h" -#include "SceneUtils/ResourceUtils.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "ComponentMocks.h" -#include "ResourceDeviceHandleAccessorMock.h" -#include "RendererResourceCacheMock.h" -#include "MockResourceHash.h" -#include "SceneReferenceLogicMock.h" -#include "SceneAllocateHelper.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Collections/Pair.h" -#include "RendererSceneEventSenderMock.h" -#include "RendererSceneUpdaterFacade.h" -#include "Components/SceneUpdate.h" -#include "Watchdog/ThreadAliveNotifierMock.h" -#include "Utils/ThreadLocalLog.h" - -#include -#include -#include - -namespace ramses_internal { - -class ARendererSceneUpdater : public ::testing::Test -{ -public: - ARendererSceneUpdater() - : rendererScenes(rendererEventCollector) - , expirationMonitor(rendererScenes, rendererEventCollector, rendererStatistics) - , renderer(Display, rendererScenes, rendererEventCollector, expirationMonitor, rendererStatistics) - , sceneStateExecutor(renderer, sceneEventSender, rendererEventCollector) - , rendererSceneUpdater(new RendererSceneUpdaterFacade(Display, renderer.m_platform, renderer, rendererScenes, sceneStateExecutor, rendererEventCollector, frameTimer, expirationMonitor, notifier, &rendererResourceCacheMock)) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - rendererSceneUpdater->setSceneReferenceLogicHandler(sceneReferenceLogic); - frameTimer.startFrame(); - rendererSceneUpdater->setLimitFlushesForceApply(ForceApplyFlushesLimit); - rendererSceneUpdater->setLimitFlushesForceUnsubscribe(ForceUnsubscribeFlushLimit); - - EXPECT_CALL(renderer, setClearColor(_, _)).Times(0); - // called explicitly from tests, no sense in tracking - EXPECT_CALL(*rendererSceneUpdater, handleSceneUpdate(_, _)).Times(AnyNumber()); - // resource cache exists but reports it has all resources already - only specific tests cases test further - EXPECT_CALL(rendererResourceCacheMock, hasResource(_, _)).Times(AnyNumber()).WillRepeatedly(Return(true)); - } - - void TearDown() override - { - expectNoEvent(); - } - -protected: - void update() - { - frameTimer.startFrame(); - renderer.getProfilerStatistics().markFrameFinished(std::chrono::microseconds{ 0u }); - EXPECT_CALL(sceneReferenceLogic, update()); - if (rendererSceneUpdater->m_resourceManagerMock) - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, hasResourcesToBeUploaded()); - rendererSceneUpdater->updateScenes(); - } - - uint32_t createStagingScene() - { - const uint32_t sceneIndex = static_cast(stagingScene.size()); - const SceneId sceneId(sceneIndex); - stagingScene.emplace_back(new ActionCollectingScene(SceneInfo(sceneId))); - - return sceneIndex; - } - - SceneId getSceneId(uint32_t sceneIndex = 0u) - { - return stagingScene[sceneIndex]->getSceneId(); - } - - void publishScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = getSceneId(sceneIndex); - rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote); - EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::Published); - expectInternalSceneStateEvent(ERendererEventType::ScenePublished); - } - - void requestSceneSubscription(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = getSceneId(sceneIndex); - EXPECT_CALL(sceneEventSender, sendSubscribeScene(sceneId)); - rendererSceneUpdater->handleSceneSubscriptionRequest(sceneId); - EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::SubscriptionRequested); - } - - void receiveScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = getSceneId(sceneIndex); - rendererSceneUpdater->handleSceneReceived(SceneInfo(sceneId)); - EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::SubscriptionPending); - EXPECT_TRUE(rendererScenes.hasScene(sceneId)); - } - - uint32_t createPublishAndSubscribeScene(bool withInitialFlush = true) - { - const uint32_t sceneIndex = createStagingScene(); - const SceneId sceneId = getSceneId(sceneIndex); - - publishScene(sceneIndex); - requestSceneSubscription(sceneIndex); - receiveScene(sceneIndex); - - if (withInitialFlush) - { - //receive initial flush - performFlush(sceneIndex); - EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::Subscribed); - expectInternalSceneStateEvent(ERendererEventType::SceneSubscribed); - } - - return sceneIndex; - } - - void expectEventsEqual(const std::initializer_list expectedEvents, const RendererEventVector& actualEvents) - { - ASSERT_EQ(expectedEvents.size(), actualEvents.size()); - auto eventIt = actualEvents.cbegin(); - for (auto expectedEvent : expectedEvents) - { - EXPECT_EQ(expectedEvent, eventIt->eventType); - ++eventIt; - } - } - - void expectEvents(const std::initializer_list expectedEvents) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(events, dummy); - expectEventsEqual(expectedEvents, events); - EXPECT_TRUE(dummy.empty()) << " expected renderer events only but there are also unchecked scene events"; - } - - void expectSceneEvents(const std::initializer_list expectedEvents) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - expectEventsEqual(expectedEvents, events); - EXPECT_TRUE(dummy.empty()) << " expected scene events only but there are also unchecked renderer events"; - } - - void expectInternalSceneStateEvents(const std::initializer_list expectedEvents) - { - InternalSceneStateEvents events; - rendererEventCollector.dispatchInternalSceneStateEvents(events); - ASSERT_EQ(expectedEvents.size(), events.size()) << "internal scene state events"; - auto expectedIt = expectedEvents.begin(); - for (size_t i = 0; i < events.size(); ++i) - EXPECT_EQ(*expectedIt++, events[i].type) << "internal scene state event #" << i; - } - - void expectNoEvent() - { - RendererEventVector events; - RendererEventVector sceneEvents; - rendererEventCollector.appendAndConsumePendingEvents(events, sceneEvents); - EXPECT_TRUE(events.empty()) << " pending unchecked renderer events"; - EXPECT_TRUE(sceneEvents.empty()) << " pending unchecked scene events"; - - expectInternalSceneStateEvents({}); - } - - void expectEvent(ERendererEventType eventType) - { - expectEvents({ eventType }); - } - - void expectSceneEvent(ERendererEventType eventType) - { - expectSceneEvents({ eventType }); - } - - void expectInternalSceneStateEvent(ERendererEventType eventType) - { - expectInternalSceneStateEvents({ eventType }); - } - - void requestMapScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - rendererSceneUpdater->handleSceneMappingRequest(sceneId); - expectNoEvent(); - } - - void mapScene(uint32_t sceneIndex = 0u) - { - requestMapScene(sceneIndex); - update(); // will set from map requested to being mapped and uploaded (if all pending flushes applied) - update(); // will set to mapped (if all resources uploaded) - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - } - - bool assignSceneToDisplayBuffer(uint32_t sceneIndex, OffscreenBufferHandle buffer = {}, int32_t renderOrder = 0) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - const bool result = rendererSceneUpdater->handleSceneDisplayBufferAssignmentRequest(sceneId, buffer, renderOrder); - if (result) - { - EXPECT_EQ(renderOrder, renderer.getSceneGlobalOrder(sceneId)); - } - - return result; - } - - void showScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - rendererSceneUpdater->handleSceneShowRequest(sceneId); - update(); - expectInternalSceneStateEvent(ERendererEventType::SceneShown); - } - - void hideScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - rendererSceneUpdater->handleSceneHideRequest(sceneId); - expectInternalSceneStateEvent(ERendererEventType::SceneHidden); - } - - void unmapScene(uint32_t sceneIndex = 0u, bool expectResourcesUnload = true) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - if (expectResourcesUnload) - expectUnloadOfSceneResources(sceneIndex); - - rendererSceneUpdater->handleSceneUnmappingRequest(sceneId); - expectInternalSceneStateEvent(ERendererEventType::SceneUnmapped); - expectNoResourceReferencedByScene(sceneIndex); - } - - void expectNoResourceReferencedByScene(uint32_t sceneIndex = 0u) - { - rendererSceneUpdater->m_resourceManagerMock->expectNoResourceReferencesForScene(getSceneId(sceneIndex)); - } - - int getResourceRefCount(ResourceContentHash resource) - { - return rendererSceneUpdater->m_resourceManagerMock->getResourceRefCount(resource); - } - - void expectUnloadOfSceneResources(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadAllSceneResourcesForScene(sceneId)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceAllResourcesForScene(sceneId)); - } - - void unpublishMapRequestedScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); - rendererSceneUpdater->handleSceneUnpublished(sceneId); - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished }); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - void unpublishMappedScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - expectUnloadOfSceneResources(sceneIndex); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); - rendererSceneUpdater->handleSceneUnpublished(sceneId); - expectInternalSceneStateEvents({ ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished }); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - void unpublishShownScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); - rendererSceneUpdater->handleSceneUnpublished(sceneId); - expectInternalSceneStateEvents({ ERendererEventType::SceneHiddenIndirect, ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished }); - - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - void unsubscribeMapRequestedScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(sceneId, true); - - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect }); - - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - void unsubscribeMappedScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(sceneId, true); - - expectInternalSceneStateEvents({ ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect }); - - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - void unsubscribeShownScene(uint32_t sceneIndex = 0u) - { - const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); - rendererSceneUpdater->handleSceneUnsubscriptionRequest(sceneId, true); - - expectInternalSceneStateEvents({ ERendererEventType::SceneHiddenIndirect, ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect }); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - NodeHandle performFlushWithCreateNodeAction(uint32_t sceneIndex = 0u, size_t numNodes = 1u) - { - NodeHandle nodeHandle; - IScene& scene = *stagingScene[sceneIndex]; - SceneAllocateHelper sceneAllocator(scene); - for (size_t i = 0; i < numNodes; ++i) - nodeHandle = sceneAllocator.allocateNode(); - performFlush(sceneIndex); - - return nodeHandle; - } - - void expectReadPixelsEvents(const std::initializer_list>& expectedEvents) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(events, dummy); - ASSERT_EQ(events.size(), expectedEvents.size()); - - auto eventsIt = events.cbegin(); - for (const auto& expectedEvent : expectedEvents) - { - const auto& event = *eventsIt; - EXPECT_EQ(event.offscreenBuffer, std::get<0>(expectedEvent)); - if (std::get<1>(expectedEvent)) - EXPECT_EQ(event.eventType, ERendererEventType::ReadPixelsFromFramebuffer); - else - EXPECT_EQ(event.eventType, ERendererEventType::ReadPixelsFromFramebufferFailed); - } - - } - - void performFlush(uint32_t sceneIndex = 0u, SceneVersionTag version = SceneVersionTag::Invalid(), const SceneSizeInformation* sizeInfo = nullptr, const FlushTimeInformation& timeInfo = {}, const SceneReferenceActionVector& sceneRefActions = {}) - { - ActionCollectingScene& scene = *stagingScene[sceneIndex]; - const SceneSizeInformation newSizeInfo = (sizeInfo ? *sizeInfo : scene.getSceneSizeInformation()); - const SceneSizeInformation currSizeInfo = rendererScenes.hasScene(scene.getSceneId()) ? rendererScenes.getScene(scene.getSceneId()).getSceneSizeInformation() : SceneSizeInformation(); - - SceneUpdate update; - update.actions = (std::move(scene.getSceneActionCollection())); - - SceneActionCollectionCreator creator(update.actions); - - ResourceContentHashVector resources; - ResourceChanges resourceChanges; - ResourceUtils::GetAllResourcesFromScene(resources, scene); - ResourceUtils::DiffResources(previousResources[sceneIndex], resources, resourceChanges); - previousResources[sceneIndex].swap(resources); - resourceChanges.m_sceneResourceActions = scene.getSceneResourceActions(); - - for (const auto& resHash : resourceChanges.m_resourcesAdded) - update.resources.push_back(MockResourceHash::GetManagedResource(resHash)); - - update.flushInfos = {1u, version, newSizeInfo, resourceChanges, sceneRefActions, timeInfo, newSizeInfo>currSizeInfo, true}; - scene.resetResourceChanges(); - rendererSceneUpdater->handleSceneUpdate(stagingScene[sceneIndex]->getSceneId(), std::move(update)); - } - - void performFlushWithUniformTimeSync(uint32_t sceneIndex, uint32_t internalTS) - { - const FlushTimeInformation timeInfo{ {}, FlushTime::Clock::time_point(std::chrono::milliseconds(internalTS)), FlushTime::Clock::getClockType(), true }; - performFlush(sceneIndex, {}, nullptr, timeInfo); - } - - void performFlushWithExpiration(uint32_t sceneIndex, uint32_t expirationTS) - { - const FlushTimeInformation timeInfo{ FlushTime::Clock::time_point(std::chrono::milliseconds(expirationTS)), {}, FlushTime::Clock::getClockType(), false }; - performFlush(sceneIndex, {}, nullptr, timeInfo); - } - - bool lastFlushWasAppliedOnRendererScene(uint32_t sceneIndex = 0u) - { - return !rendererSceneUpdater->hasPendingFlushes(stagingScene[sceneIndex]->getSceneId()); - } - - void createDisplayAndExpectSuccess(const DisplayConfig& displayConfig = DisplayConfig()) - { - ASSERT_TRUE(rendererSceneUpdater->m_resourceManagerMock == nullptr); - EXPECT_CALL(*rendererSceneUpdater, createResourceManager(_, _, _, _)); - EXPECT_CALL(renderer.m_platform, createResourceUploadRenderBackend()); - rendererSceneUpdater->createDisplayContext(displayConfig, nullptr); - EXPECT_TRUE(renderer.hasDisplayController()); - expectEvent(ERendererEventType::DisplayCreated); - - // no offscreen buffers reported uploaded by default - ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(_)).WillByDefault(Return(DeviceResourceHandle::Invalid())); - ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferColorBufferDeviceHandle(_)).WillByDefault(Return(DeviceResourceHandle::Invalid())); - // scene updater queries display/offscreen buffer from device handle when updating modified scenes states - these are invalid for display framebuffer - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferHandle(DisplayControllerMock::FakeFrameBufferHandle)).Times(AnyNumber()).WillRepeatedly(Return(OffscreenBufferHandle::Invalid())); - - // scene updater logic might call resource manager for ref/unref of resources with empty list - no need to track those - ResourceContentHashVector emptyResources; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(_, emptyResources)).Times(AnyNumber()); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceResourcesForScene(_, emptyResources)).Times(AnyNumber()); - - // querying of resources state happens often, concrete tests can control status reported - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceStatus(_)).Times(AnyNumber()); - - // by default skip path triggering upload/unload, concrete tests can override - ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, hasResourcesToBeUploaded()).WillByDefault(Return(false)); - } - - void destroyDisplay(bool expectFail = false) - { - if (!expectFail) - { - ASSERT_TRUE(rendererSceneUpdater->m_resourceManagerMock != nullptr); - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); - EXPECT_CALL(*rendererSceneUpdater, destroyResourceManager()); - rendererSceneUpdater->m_resourceManagerMock->expectNoResourceReferences(); - } - rendererSceneUpdater->destroyDisplayContext(); - if (expectFail) - { - expectEvent(ERendererEventType::DisplayDestroyFailed); - } - else - { - EXPECT_FALSE(renderer.hasDisplayController()); - expectEvent(ERendererEventType::DisplayDestroyed); - } - } - - void createRenderable(uint32_t sceneIndex = 0u, bool withVertexArray = false, bool withTextureSampler = false, EVisibilityMode visibility = EVisibilityMode::Visible) - { - createRenderableNoFlush(sceneIndex, withVertexArray, withTextureSampler, visibility); - performFlush(sceneIndex); - } - - void createRenderableNoFlush(uint32_t sceneIndex = 0u, bool withVertexArray = false, bool withTextureSampler = false, EVisibilityMode visibility = EVisibilityMode::Visible) - { - const NodeHandle renderableNode(1u); - const RenderPassHandle renderPassHandle(2u); - const RenderGroupHandle renderGroupHandle(3u); - const CameraHandle cameraHandle(4u); - const DataLayoutHandle uniformDataLayoutHandle(0u); - const DataLayoutHandle geometryDataLayoutHandle(1u); - const DataLayoutHandle camDataLayoutHandle(2u); - const DataInstanceHandle camDataHandle(2u); - - IScene& scene = *stagingScene[sceneIndex]; - - scene.allocateRenderPass(0u, renderPassHandle); - scene.allocateRenderGroup(0u, 0u, renderGroupHandle); - scene.allocateNode(0u, renderableNode); - scene.allocateRenderable(renderableNode, renderableHandle); - scene.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I}, DataFieldInfo{EDataType::Vector2I} }, MockResourceHash::EffectHash, camDataLayoutHandle); - scene.allocateCamera(ECameraProjectionType::Perspective, scene.allocateNode(), scene.allocateDataInstance(camDataLayoutHandle, camDataHandle), cameraHandle); - - scene.addRenderableToRenderGroup(renderGroupHandle, renderableHandle, 0u); - scene.addRenderGroupToRenderPass(renderPassHandle, renderGroupHandle, 0u); - scene.setRenderPassCamera(renderPassHandle, cameraHandle); - scene.setRenderPassEnabled(renderPassHandle, true); - - DataFieldInfoVector uniformDataFields; - if (withTextureSampler) - { - uniformDataFields.push_back(DataFieldInfo{ EDataType::TextureSampler2D }); - } - scene.allocateDataLayout(uniformDataFields, MockResourceHash::EffectHash, uniformDataLayoutHandle); - scene.allocateDataInstance(uniformDataLayoutHandle, uniformDataInstanceHandle); - scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Uniforms, uniformDataInstanceHandle); - - DataFieldInfoVector geometryDataFields; - geometryDataFields.push_back(DataFieldInfo{ EDataType::Indices, 1u, EFixedSemantics::Indices }); - if (withVertexArray) - { - geometryDataFields.push_back(DataFieldInfo{ EDataType::Vector3Buffer, 1u, EFixedSemantics::Invalid }); - } - scene.allocateDataLayout(geometryDataFields, MockResourceHash::EffectHash, geometryDataLayoutHandle); - scene.allocateDataInstance(geometryDataLayoutHandle, geometryDataInstanceHandle); - scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Geometry, geometryDataInstanceHandle); - scene.setRenderableVisibility(renderableHandle, visibility); - } - - void destroyRenderable(uint32_t sceneIndex = 0u) - { - const RenderGroupHandle renderGroupHandle(3u); - IScene& scene = *stagingScene[sceneIndex]; - scene.removeRenderableFromRenderGroup(renderGroupHandle, renderableHandle); - scene.releaseRenderable(renderableHandle); - - performFlush(sceneIndex); - } - - void setRenderableResources(uint32_t sceneIndex = 0u, ResourceContentHash indexArrayHash = MockResourceHash::IndexArrayHash) - { - setRenderableResourcesNoFlush(sceneIndex, indexArrayHash); - performFlush(sceneIndex); - } - - void setRenderableResourcesNoFlush(uint32_t sceneIndex = 0u, ResourceContentHash indexArrayHash = MockResourceHash::IndexArrayHash) - { - IScene& scene = *stagingScene[sceneIndex]; - scene.setDataResource(geometryDataInstanceHandle, DataFieldHandle(0u), indexArrayHash, DataBufferHandle::Invalid(), 0u, 0u, 0u); - } - - DataSlotId createRenderableWithTextureConsumer(uint32_t sceneIndex = 0u) - { - expectVertexArrayUploaded(sceneIndex); - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, sceneIndex); - createRenderable(sceneIndex, false, true); - setRenderableResources(sceneIndex); - update(); - - IScene& scene = *stagingScene[sceneIndex]; - scene.allocateTextureSampler({ {}, MockResourceHash::TextureHash }, samplerHandle); - scene.setDataTextureSamplerHandle(uniformDataInstanceHandle, DataFieldHandle(0u), samplerHandle); - const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); - scene.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), samplerHandle }); - - expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, sceneIndex); - performFlush(sceneIndex); - update(); - - expectSceneEvent(ERendererEventType::SceneDataSlotConsumerCreated); - - return consumerId; - } - - void removeRenderableResources(uint32_t sceneIndex = 0u) - { - IScene& scene = *stagingScene[sceneIndex]; - // it is not allowed to set both resource and data buffer as invalid, so for the purpose of the test cases here we set to non-existent data buffer with handle 0 - scene.setDataResource(geometryDataInstanceHandle, DataFieldHandle(0u), ResourceContentHash::Invalid(), DataBufferHandle(0u), 0u, 0u, 0u); - - performFlush(sceneIndex); - } - - void expectResourcesReferenced(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u, int times = 1) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(getSceneId(sceneIdx), resources)).Times(times); - } - - void expectResourcesProvided(int times = 1) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, provideResourceData(_)).Times(times); - } - - void expectResourcesReferencedAndProvided(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u) - { - // most tests add 1 resource in 1 flush, referencing logic is executed per flush - for (const auto& res : resources) - expectResourcesReferenced({ res }, sceneIdx, 1); - expectResourcesProvided(int(resources.size())); - } - - void expectResourcesReferencedAndProvided_altogether(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(getSceneId(sceneIdx), resources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, provideResourceData(_)).Times(int(resources.size())); - } - - void expectResourcesUnreferenced(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u, int times = 1) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceResourcesForScene(getSceneId(sceneIdx), resources)).Times(times); - } - - void reportResourceAs(ResourceContentHash resource, EResourceStatus status) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceStatus(resource)).Times(AnyNumber()).WillRepeatedly(Return(status)); - } - - void expectOffscreenBufferUploaded(OffscreenBufferHandle buffer, DeviceResourceHandle rtDeviceHandleToReturn = DeviceMock::FakeRenderTargetDeviceHandle, bool doubleBuffered = false, ERenderBufferType depthStencilBufferType = ERenderBufferType_DepthStencilBuffer) - { - { - InSequence seq; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadOffscreenBuffer(buffer, _, _, _, doubleBuffered, depthStencilBufferType)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillRepeatedly(Return(rtDeviceHandleToReturn)); - } - // scene updater queries offscreen buffer from device handle when updating modified scenes states - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferHandle(rtDeviceHandleToReturn)).Times(AnyNumber()).WillRepeatedly(Return(buffer)); - } - - void expectDmaOffscreenBufferUploaded(OffscreenBufferHandle buffer, DeviceResourceHandle rtDeviceHandleToReturn, DmaBufferFourccFormat fourccFormat, DmaBufferUsageFlags usageFlags, DmaBufferModifiers modifiers) - { - { - InSequence seq; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadDmaOffscreenBuffer(buffer, _, _, fourccFormat, usageFlags, modifiers)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillRepeatedly(Return(rtDeviceHandleToReturn)); - } - // scene updater queries offscreen buffer from device handle when updating modified scenes states - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferHandle(rtDeviceHandleToReturn)).Times(AnyNumber()).WillRepeatedly(Return(buffer)); - } - - void expectOffscreenBufferDeleted(OffscreenBufferHandle buffer, DeviceResourceHandle deviceHandle = DeviceMock::FakeRenderTargetDeviceHandle) - { - InSequence seq; - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(deviceHandle)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadOffscreenBuffer(buffer)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - } - - void expectStreamBufferUploaded(StreamBufferHandle buffer, WaylandIviSurfaceId source, DeviceResourceHandle rtDeviceHandleToReturn = DeviceMock::FakeRenderTargetDeviceHandle) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadStreamBuffer(buffer, source)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).Times(AnyNumber()).WillRepeatedly(Return(rtDeviceHandleToReturn)); - } - - void expectStreamBufferDeleted(StreamBufferHandle buffer) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadStreamBuffer(buffer)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - } - - void expectExternalBufferUploaded(ExternalBufferHandle buffer, DeviceResourceHandle deviceHandleToReturn = DeviceMock::FakeExternalTextureDeviceHandle) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadExternalBuffer(buffer)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferDeviceHandle(buffer)).Times(AnyNumber()).WillRepeatedly(Return(deviceHandleToReturn)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferGlId(buffer)).Times(AnyNumber()); - } - - void expectExternalBufferDeleted(ExternalBufferHandle buffer) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadExternalBuffer(buffer)); - } - - void expectBlitPassUploaded() - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(0u), _)).Times(2); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadBlitPassRenderTargets(_, _, _, getSceneId(0u))); - } - - void expectBlitPassDeleted() - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadRenderTargetBuffer(_, getSceneId(0u))).Times(2); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadBlitPassRenderTargets(_, getSceneId(0u))); - } - - void expectStreamTextureUploaded() - { - static constexpr DeviceResourceHandle FakeStreamTextureDeviceHandle{ 987 }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, getCompositedTextureDeviceHandleForStreamTexture(_)).WillRepeatedly(Return(FakeStreamTextureDeviceHandle)); - } - - void expectVertexArrayUploaded(uint32_t sceneIdx = 0u) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadVertexArray(_, _, getSceneId(sceneIdx))); - } - - void expectVertexArrayUnloaded(uint32_t sceneIdx = 0u) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadVertexArray(_, getSceneId(sceneIdx))); - } - - void createRenderTargetWithBuffers(uint32_t sceneIndex = 0u, RenderTargetHandle renderTargetHandle = RenderTargetHandle{ 0u }, RenderBufferHandle bufferHandle = RenderBufferHandle{ 0u }, RenderBufferHandle depthHandle = RenderBufferHandle{ 1u }) - { - IScene& scene = *stagingScene[sceneIndex]; - scene.allocateRenderBuffer({ 16u, 16u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }, bufferHandle); - scene.allocateRenderBuffer({ 16u, 16u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_ReadWrite, 0u }, depthHandle); - scene.allocateRenderTarget(renderTargetHandle); - scene.addRenderTargetRenderBuffer(renderTargetHandle, bufferHandle); - scene.addRenderTargetRenderBuffer(renderTargetHandle, depthHandle); - performFlush(sceneIndex); - } - - void expectRenderTargetUploaded(uint32_t sceneIdx = 0u) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(sceneIdx), _)).Times(2); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTarget(_, _, getSceneId(sceneIdx))); - } - - void expectRenderTargetUnloaded(uint32_t sceneIdx = 0u) - { - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadRenderTargetBuffer(_, getSceneId(sceneIdx))).Times(2); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadRenderTarget(_, getSceneId(sceneIdx))); - } - - void createBlitPass() - { - const BlitPassHandle blitPassHandle(0u); - IScene& scene = *stagingScene[0]; - const RenderBufferHandle sourceBufferHandle = scene.allocateRenderBuffer({ 16u, 16u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }); - const RenderBufferHandle destinationBufferHandle = scene.allocateRenderBuffer({ 16u, 16u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 0u }); - scene.allocateBlitPass(sourceBufferHandle, destinationBufferHandle, blitPassHandle); - performFlush(); - } - - void destroyRenderTargetWithBuffers(uint32_t sceneIndex = 0u) - { - const RenderTargetHandle renderTargetHandle(0u); - const RenderBufferHandle bufferHandle(0u); - const RenderBufferHandle depthHandle(1u); - IScene& scene = *stagingScene[sceneIndex]; - scene.releaseRenderTarget(renderTargetHandle); - scene.releaseRenderBuffer(bufferHandle); - scene.releaseRenderBuffer(depthHandle); - performFlush(sceneIndex); - } - - void destroyBlitPass() - { - const BlitPassHandle blitPassHandle(0u); - IScene& scene = *stagingScene[0]; - scene.releaseBlitPass(blitPassHandle); - performFlush(); - } - - std::pair createDataSlots(DataInstanceHandle& dataRefConsumer, float providedValue, DataInstanceHandle* dataRefProviderOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) - { - DataInstanceHandle dataRefProvider; - - IScene& scene1 = *stagingScene[providerSceneIdx]; - IScene& scene2 = *stagingScene[consumerSceneIdx]; - - const DataLayoutHandle dataLayout1 = scene1.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - const DataLayoutHandle dataLayout2 = scene2.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - dataRefProvider = scene1.allocateDataInstance(dataLayout1); - dataRefConsumer = scene2.allocateDataInstance(dataLayout2); - if (nullptr != dataRefProviderOut) - *dataRefProviderOut = dataRefProvider; - - scene1.setDataSingleFloat(dataRefProvider, DataFieldHandle(0u), providedValue); - scene2.setDataSingleFloat(dataRefConsumer, DataFieldHandle(0u), 0.f); - - const DataSlotId providerId(getNextFreeDataSlotIdForDataLinking()); - const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); - scene1.allocateDataSlot({ EDataSlotType_DataProvider, providerId, NodeHandle(), dataRefProvider, ResourceContentHash::Invalid(), TextureSamplerHandle() }); - scene2.allocateDataSlot({ EDataSlotType_DataConsumer, consumerId, NodeHandle(), dataRefConsumer, ResourceContentHash::Invalid(), TextureSamplerHandle() }); - - performFlush(providerSceneIdx); - performFlush(consumerSceneIdx); - update(); - expectSceneEvents({ ERendererEventType::SceneDataSlotConsumerCreated, ERendererEventType::SceneDataSlotProviderCreated }); - - return{ providerId, consumerId }; - } - - void createDataSlotsAndLinkThem(DataInstanceHandle& dataRefConsumer, float providedValue, DataInstanceHandle* dataRefProviderOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) - { - const auto providerConsumer = createDataSlots(dataRefConsumer, providedValue, dataRefProviderOut, providerSceneIdx, consumerSceneIdx); - linkProviderToConsumer(getSceneId(providerSceneIdx), providerConsumer.first, getSceneId(consumerSceneIdx), providerConsumer.second); - } - - void updateProviderDataSlot(uint32_t sceneIdx, DataInstanceHandle dataRefProvider, float newProvidedValue) - { - IScene& scene = *stagingScene[sceneIdx]; - scene.setDataSingleFloat(dataRefProvider, DataFieldHandle(0u), newProvidedValue); - } - - std::pair createTextureSlots(DataSlotHandle* providerDataSlotHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) - { - IScene& scene1 = *stagingScene[providerSceneIdx]; - IScene& scene2 = *stagingScene[consumerSceneIdx]; - - const TextureSamplerHandle sampler = scene2.allocateTextureSampler({ {}, RenderBufferHandle(999) }); - - const DataSlotId providerId(getNextFreeDataSlotIdForDataLinking()); - const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); - DataSlotHandle providerDataSlot = scene1.allocateDataSlot({ EDataSlotType_TextureProvider, providerId, NodeHandle(), DataInstanceHandle::Invalid(), MockResourceHash::TextureHash, TextureSamplerHandle() }); - if (nullptr != providerDataSlotHandleOut) - *providerDataSlotHandleOut = providerDataSlot; - scene2.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }); - - performFlush(providerSceneIdx); - performFlush(consumerSceneIdx); - update(); - expectSceneEvents({ ERendererEventType::SceneDataSlotConsumerCreated, ERendererEventType::SceneDataSlotProviderCreated }); - - return{ providerId, consumerId }; - } - - void createTextureSlotsAndLinkThem(DataSlotHandle* providerDataSlotHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u, bool expectSuccess = true) - { - const auto providerConsumer = createTextureSlots(providerDataSlotHandleOut, providerSceneIdx, consumerSceneIdx); - linkProviderToConsumer(getSceneId(providerSceneIdx), providerConsumer.first, getSceneId(consumerSceneIdx), providerConsumer.second, expectSuccess); - } - - DataSlotId createTextureConsumer(uint32_t sceneIndex, TextureSampler::ContentType contentType = TextureSampler::ContentType::RenderBuffer) - { - IScene& scene = *stagingScene[sceneIndex]; - const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, contentType, {}, MemoryHandle{999u} }); - const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); - scene.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }); - - performFlush(sceneIndex); - update(); - expectSceneEvent(ERendererEventType::SceneDataSlotConsumerCreated); - - return consumerId; - } - - template - void createBufferLink(BUFFERHANDLE buffer, SceneId consumerSceneId, DataSlotId consumerId, bool expectFail = false) - { - rendererSceneUpdater->handleBufferToSceneDataLinkRequest(buffer, consumerSceneId, consumerId); - expectSceneEvent(expectFail ? ERendererEventType::SceneDataBufferLinkFailed : ERendererEventType::SceneDataBufferLinked); - } - - void updateProviderTextureSlot(uint32_t sceneIdx, DataSlotHandle providerDataSlot, ResourceContentHash newProvidedValue) - { - IScene& scene = *stagingScene[sceneIdx]; - scene.setDataSlotTexture(providerDataSlot, newProvidedValue); - } - - std::pair createTransformationSlots(TransformHandle* providerTransformHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) - { - IScene& scene1 = *stagingScene[providerSceneIdx]; - IScene& scene2 = *stagingScene[consumerSceneIdx]; - - const auto nodeHandle1 = scene1.allocateNode(); - const auto nodeHandle2 = scene2.allocateNode(); - - const auto providerTransformHandle = scene1.allocateTransform(nodeHandle1); - scene2.allocateTransform(nodeHandle2); - if (nullptr != providerTransformHandleOut) - *providerTransformHandleOut = providerTransformHandle; - - const DataSlotId providerId(getNextFreeDataSlotIdForDataLinking()); - const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); - scene1.allocateDataSlot({ EDataSlotType_TransformationProvider, providerId, nodeHandle1, {}, ResourceContentHash::Invalid(), TextureSamplerHandle() }); - scene2.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerId, nodeHandle2, {}, ResourceContentHash::Invalid(), TextureSamplerHandle() }); - - performFlush(providerSceneIdx); - performFlush(consumerSceneIdx); - update(); - expectSceneEvents({ ERendererEventType::SceneDataSlotConsumerCreated, ERendererEventType::SceneDataSlotProviderCreated }); - - return { providerId, consumerId }; - } - - void createTransformationSlotsAndLinkThem(TransformHandle* providerTransformHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) - { - const auto providerConsumer = createTransformationSlots(providerTransformHandleOut, providerSceneIdx, consumerSceneIdx); - linkProviderToConsumer(getSceneId(providerSceneIdx), providerConsumer.first, getSceneId(consumerSceneIdx), providerConsumer.second); - } - - void linkProviderToConsumer(SceneId providerSceneId, DataSlotId providerId, SceneId consumerSceneId, DataSlotId consumerId, bool expectSuccess = true) - { - rendererSceneUpdater->handleSceneDataLinkRequest(providerSceneId, providerId, consumerSceneId, consumerId); - expectSceneEvent(expectSuccess ? ERendererEventType::SceneDataLinked : ERendererEventType::SceneDataLinkFailed); - } - - void unlinkConsumer(SceneId consumerSceneId, DataSlotId consumerId) - { - rendererSceneUpdater->handleDataUnlinkRequest(consumerSceneId, consumerId); - expectSceneEvent(ERendererEventType::SceneDataUnlinked); - } - - void destroySceneUpdater() - { - EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); - rendererSceneUpdater.reset(); - expectEvent(ERendererEventType::DisplayDestroyed); - } - - void expectRenderableResourcesClean(uint32_t sceneIndex = 0u) - { - const RendererCachedScene& scene = rendererScenes.getScene(stagingScene[sceneIndex]->getSceneId()); - EXPECT_FALSE(scene.renderableResourcesDirty(renderableHandle)); - } - - void expectRenderableResourcesDirty(uint32_t sceneIndex = 0u) - { - const RendererCachedScene& scene = rendererScenes.getScene(stagingScene[sceneIndex]->getSceneId()); - EXPECT_TRUE(scene.renderableResourcesDirty(renderableHandle)); - } - - void expectModifiedScenesReportedToRenderer(std::initializer_list indices = {0u}) - { - for (auto idx : indices) - { - const SceneId sceneId = stagingScene[idx]->getSceneId(); - EXPECT_CALL(renderer, markBufferWithSceneForRerender(sceneId)); - } - } - - void expectNoModifiedScenesReportedToRenderer() - { - EXPECT_CALL(renderer, markBufferWithSceneForRerender(_)).Times(0u); - } - - DataSlotId getNextFreeDataSlotIdForDataLinking() - { - return DataSlotId(dataSlotIdForDataLinking.getReference()++); - } - - OffscreenBufferHandle showAndInitiateInterruptedRendering(uint32_t sceneIdx, uint32_t interruptedSceneIdx) - { - // assign scene to double buffered OB - const OffscreenBufferHandle buffer(111u); - expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true); - EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, ERenderBufferType_DepthStencilBuffer)); - expectEvent(ERendererEventType::OffscreenBufferCreated); - EXPECT_TRUE(assignSceneToDisplayBuffer(interruptedSceneIdx, buffer)); - - showScene(sceneIdx); - showScene(interruptedSceneIdx); - update(); - - EXPECT_CALL(*renderer.m_displayController, getRenderBackend()).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, getDisplayBuffer()).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, clearBuffer(_, _, _)).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).Times(AnyNumber()); - EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).Times(AnyNumber()); - - EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()); - EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).WillOnce(Return(true)); - EXPECT_CALL(*renderer.m_displayController, swapBuffers()); - - EXPECT_CALL(*renderer.m_displayController, renderScene(Ref(rendererScenes.getScene(getSceneId(sceneIdx))), _, nullptr)).WillOnce( - [](const auto&, const RenderingContext& renderContext, const auto*) { - EXPECT_EQ(DisplayControllerMock::FakeFrameBufferHandle, renderContext.displayBufferDeviceHandle); - return SceneRenderExecutionIterator{}; - }); - - SceneRenderExecutionIterator interruptedState; - interruptedState.incrementRenderableIdx(); - EXPECT_CALL(*renderer.m_displayController, renderScene(Ref(rendererScenes.getScene(getSceneId(interruptedSceneIdx))), _, _)).WillOnce( - [interruptedState](const auto&, const RenderingContext& renderContext, const auto*) { - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, renderContext.displayBufferDeviceHandle); - return interruptedState; - }); - - renderer.doOneRenderLoop(); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - - return buffer; - } - - void doRenderLoop() - { - EXPECT_CALL(*renderer.m_displayController, getRenderBackend()).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, getDisplayBuffer()).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, clearBuffer(_, _, _)).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).Times(AnyNumber()); - EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).Times(AnyNumber()); - - EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()); - EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).WillOnce(Return(true)); - EXPECT_CALL(*renderer.m_displayController, swapBuffers()).Times(AnyNumber()); - EXPECT_CALL(*renderer.m_displayController, renderScene(_, _, _)).Times(AnyNumber()); - - renderer.doOneRenderLoop(); - } - - void readPixels(OffscreenBufferHandle obHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, bool fullscreen, bool sendViaDLT, std::string_view filename) - { - ScreenshotInfo screenshotInfo; - screenshotInfo.rectangle.x = x; - screenshotInfo.rectangle.y = y; - screenshotInfo.rectangle.width = width; - screenshotInfo.rectangle.height = height; - screenshotInfo.fullScreen = fullscreen; - screenshotInfo.sendViaDLT = sendViaDLT; - screenshotInfo.filename = filename; - - rendererSceneUpdater->handleReadPixels(obHandle, std::move(screenshotInfo)); - } - - void expectDisplayControllerReadPixels(DeviceResourceHandle deviceHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height) - { - EXPECT_CALL(*renderer.m_displayController, readPixels(deviceHandle, x, y, width, height, _)).WillOnce(Invoke( - [](auto, auto, auto, auto w, auto h, auto& dataOut) { - dataOut.resize(w * h * 4); - } - )); - } - - static constexpr DisplayHandle Display{ 1u }; - - const RenderableHandle renderableHandle{ 1 }; - const DataInstanceHandle uniformDataInstanceHandle{ 0 }; - const DataInstanceHandle geometryDataInstanceHandle{ 1 }; - - const TextureSamplerHandle samplerHandle{ 2 }; - - std::vector> stagingScene; - DataSlotId dataSlotIdForDataLinking{9911u}; - - static constexpr size_t ForceApplyFlushesLimit = 10u; - static constexpr size_t ForceUnsubscribeFlushLimit = 20u; - - StrictMock rendererResourceCacheMock; - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - SceneExpirationMonitor expirationMonitor; - StrictMock sceneReferenceLogic; - RendererStatistics rendererStatistics; - StrictMock renderer; - StrictMock sceneEventSender; - FrameTimer frameTimer; - SceneStateExecutor sceneStateExecutor; - NiceMock notifier; - std::unique_ptr rendererSceneUpdater; - - std::unordered_map previousResources; -}; -} - -#endif diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest_Resources.cpp b/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest_Resources.cpp deleted file mode 100644 index e1b15d942..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest_Resources.cpp +++ /dev/null @@ -1,2450 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererSceneUpdaterTest.h" -#include "TestRandom.h" -#include "Resource/EffectResource.h" -#include - -namespace ramses_internal { - -TEST_F(ARendererSceneUpdater, referencesAndProvidesResourcesWhenSceneMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - mapScene(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, referencesAndProvidesOnlyResourcesInUseWhenSceneMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - setRenderableResources(0u, MockResourceHash::IndexArrayHash3); - update(); - - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - mapScene(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesNotInUse) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }); - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash3 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash3); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesNotInUseOnlyAfterPendingFlushesApplied) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - // pending flush - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // unblock resource - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, sameResourceComingFromMultipleScenesIsReferencedAndProvidedMultipleTimes) -{ - createDisplayAndExpectSuccess(); - const auto s1 = createPublishAndSubscribeScene(); - const auto s2 = createPublishAndSubscribeScene(); - const auto s3 = createPublishAndSubscribeScene(); - mapScene(s1); - mapScene(s2); - mapScene(s3); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); - createRenderable(s1); - setRenderableResources(s1); - update(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s2); - createRenderable(s2); - setRenderableResources(s2); - update(); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s3); - createRenderable(s3); - setRenderableResources(s3); - update(); - EXPECT_EQ(3, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(3, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(s1); - unmapScene(s2); - unmapScene(s3); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, sameResourceComingFromMultipleScenesIsUnreferencedWhenSceneUnmappedOrUnpublished) -{ - createDisplayAndExpectSuccess(); - const auto s1 = createPublishAndSubscribeScene(); - const auto s2 = createPublishAndSubscribeScene(); - const auto s3 = createPublishAndSubscribeScene(); - mapScene(s1); - mapScene(s2); - mapScene(s3); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); - createRenderable(s1); - setRenderableResources(s1); - update(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s2); - createRenderable(s2); - setRenderableResources(s2); - update(); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s3); - createRenderable(s3); - setRenderableResources(s3); - update(); - EXPECT_EQ(3, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(3, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // destroy renderable in s1 - destroyRenderable(s1); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); - update(); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - // unmap s1 - unmapScene(s1); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - // unmap s2 - unmapScene(s2); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - // unpublish s3 - unpublishMappedScene(s3); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, offersProvidedResourceToResourceCacheAndStoresOnlyIfCacheWants) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - - EXPECT_CALL(rendererResourceCacheMock, hasResource(MockResourceHash::EffectHash, _)).WillOnce(Return(false)); - EXPECT_CALL(rendererResourceCacheMock, hasResource(MockResourceHash::IndexArrayHash, _)).WillOnce(Return(false)); - - // wants effect - EXPECT_CALL(rendererResourceCacheMock, shouldResourceBeCached(MockResourceHash::EffectHash, _, ResourceCacheFlag_DoNotCache, getSceneId())).WillOnce(Return(true)); - // does not want indices - EXPECT_CALL(rendererResourceCacheMock, shouldResourceBeCached(MockResourceHash::IndexArrayHash, _, ResourceCacheFlag_DoNotCache, getSceneId())).WillOnce(Return(false)); - - // store effect - EXPECT_CALL(rendererResourceCacheMock, storeResource(MockResourceHash::EffectHash, _, _, ResourceCacheFlag_DoNotCache, getSceneId())); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, triggersResourceUploadAndUnloadWhenResourceProvided) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - - // trigger unload/upload code path - ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, hasResourcesToBeUploaded()).WillByDefault(Return(true)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadAndUnloadPendingResources()); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMapRequestedSceneWhenSceneGetsUnpublished) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - // blocking flush so that scene will never get mapped - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - requestMapScene(); - update(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectUnloadOfSceneResources(); - unpublishMapRequestedScene(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByRenderedSceneWhenSceneGetsUnpublished) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectVertexArrayUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectUnloadOfSceneResources(); - unpublishShownScene(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMappedSceneWhenSceneGetsUnsubscribedByError) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectUnloadOfSceneResources(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); - unsubscribeMappedScene(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMapRequestedSceneWhenSceneGetsUnsubscribedByError) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - // blocking flush so that scene will never get mapped - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - requestMapScene(); - update(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectUnloadOfSceneResources(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); - unsubscribeMapRequestedScene(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByRenderedSceneWhenSceneGetsUnsubscribedByError) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectVertexArrayUploaded(); - update(); - - expectUnloadOfSceneResources(); - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); - unsubscribeShownScene(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMappedSceneWhenUpdaterDestructed) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - update(); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); -} - -TEST_F(ARendererSceneUpdater, confidenceTest_doesNotReferenceOrUnreferenceResourcesIfSceneNotMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createRenderable(); - setRenderableResources(); - - update(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, updatesScenesCachedStreamBufferHandles_SingleScene) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - constexpr StreamBufferHandle streamBuffer{ 13u }; - - const auto dataSlotId = createRenderableWithTextureConsumer(); - createBufferLink(streamBuffer, getSceneId(0u), dataSlotId); - update(); - - expectRenderableResourcesClean(); - - constexpr WaylandIviSurfaceId source{ 12u }; - const StreamUsage fakeStreamUsage{ {}, { streamBuffer} }; - const WaylandIviSurfaceIdVector changedSources{ source }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); - update(); - expectRenderableResourcesClean(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, updatesScenesCachedStreamBufferHandles_MultipleScenes) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - - constexpr StreamBufferHandle streamBuffer{ 13u }; - - const auto dataSlotId1 = createRenderableWithTextureConsumer(0u); - const auto dataSlotId2 = createRenderableWithTextureConsumer(1u); - createBufferLink(streamBuffer, getSceneId(0u), dataSlotId1); - createBufferLink(streamBuffer, getSceneId(1u), dataSlotId2); - update(); - - expectRenderableResourcesClean(0u); - expectRenderableResourcesClean(1u); - - constexpr WaylandIviSurfaceId source{ 112u }; - const StreamUsage fakeStreamUsage{ {}, { streamBuffer} }; - - const WaylandIviSurfaceIdVector changedSources{ source }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillRepeatedly(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer)).Times(2u); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); - update(); - expectRenderableResourcesClean(0u); - expectRenderableResourcesClean(1u); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, updatesScenesCachedStreamBufferHandles_MultipleScenes_MultipleSources) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - mapScene(0u); - mapScene(1u); - showScene(0u); - showScene(1u); - - - constexpr StreamBufferHandle streamBuffer1{ 13u }; - constexpr StreamBufferHandle streamBuffer2{ 15u }; - - const auto dataSlotId1 = createRenderableWithTextureConsumer(0u); - const auto dataSlotId2 = createRenderableWithTextureConsumer(1u); - createBufferLink(streamBuffer1, getSceneId(0u), dataSlotId1); - createBufferLink(streamBuffer2, getSceneId(1u), dataSlotId2); - update(); - - expectRenderableResourcesClean(0u); - expectRenderableResourcesClean(1u); - - constexpr WaylandIviSurfaceId source1{ 112u }; - constexpr WaylandIviSurfaceId source2{ 121u }; - const StreamUsage fakeStreamUsage1{ {}, { streamBuffer1} }; - const StreamUsage fakeStreamUsage2{ {}, { streamBuffer2} }; - - const WaylandIviSurfaceIdVector changedSources{ source1, source2 }; - EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillRepeatedly(SetArgReferee<0>(changedSources)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer1)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer2)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source1)).WillOnce(ReturnRef(fakeStreamUsage1)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source2)).WillOnce(ReturnRef(fakeStreamUsage2)); - update(); - expectRenderableResourcesClean(0u); - expectRenderableResourcesClean(1u); - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, pendingFlushesAreNotAppliedIfResourcesNotReady) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, appliesPendingFlushesAtOnceAndInOrderWhenUnblocked_mapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectNoEvent(); - - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - const SceneVersionTag version1(1u); - const SceneVersionTag version2(2u); - const SceneVersionTag version3(3u); - - performFlush(0u, version1); - update(); - expectNoEvent(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(0u, version2); - update(); - expectNoEvent(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(0u, version3); - update(); - expectNoEvent(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock pending flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(3u, events.size()); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[0].eventType); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[1].eventType); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[2].eventType); - EXPECT_EQ(version1.getValue(), events[0].sceneVersionTag.getValue()); - EXPECT_EQ(version2.getValue(), events[1].sceneVersionTag.getValue()); - EXPECT_EQ(version3.getValue(), events[2].sceneVersionTag.getValue()); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); -} - -TEST_F(ARendererSceneUpdater, uploadsAndUnloadsVertexArray) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - createRenderable(); - setRenderableResources(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - expectVertexArrayUploaded(); - update(); - expectRenderableResourcesClean(); - - destroyRenderable(); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - expectVertexArrayUnloaded(); - update(); - expectRenderableResourcesClean(); - - //next call to update does not release to any more attempts to unload - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotUploadVertexArrayIfRenderableVisilityOff) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - createRenderable(0u, false, false, EVisibilityMode::Off); - setRenderableResources(); - - update(); - expectRenderableResourcesDirty(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, appliesPendingFlushesAtOnceAndInOrderWhenUnblocked_shown) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectVertexArrayUploaded(); - expectNoEvent(); - - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - const SceneVersionTag version1(1u); - const SceneVersionTag version2(2u); - const SceneVersionTag version3(3u); - - performFlush(0u, version1); - update(); - expectNoEvent(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(0u, version2); - update(); - expectNoEvent(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(0u, version3); - update(); - expectNoEvent(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock pending flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(3u, events.size()); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[0].eventType); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[1].eventType); - EXPECT_EQ(ERendererEventType::SceneFlushed, events[2].eventType); - EXPECT_EQ(version1.getValue(), events[0].sceneVersionTag.getValue()); - EXPECT_EQ(version2.getValue(), events[1].sceneVersionTag.getValue()); - EXPECT_EQ(version3.getValue(), events[2].sceneVersionTag.getValue()); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); -} - -TEST_F(ARendererSceneUpdater, pendingFlushesAreNotAppliedUntilBlockingResourceUploadedEvenIfUnreferencedByOtherFlush) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectVertexArrayUploaded(); - expectNoEvent(); - - // block on indices not uploaded - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // replace indices with another one which is uploaded right away - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - performFlush(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // even though not used anymore wait until previously blocking resource uploaded - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - // it will be unreferenced as unused right after - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - performFlush(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, swappingResourcesWhileBlockedEndsUpInEqualRefAndUnrefCounts) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - - // block on indices1 - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // swap indices1 for indices2 - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // swap back - indices1 is referenced and provided again (client sends resource once more) - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // swap once more - indices2 is referenced and provided again (client sends resource once more) - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // unblock both - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); - // indices is unused and therefore unreferenced - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 2); // was reffed twice, no zero - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); // unrefs are applied sequentially after apply - indices2 was swapped in/out - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // destroy renderable so there are no more resources in use - destroyRenderable(); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash2 }); - update(); - - expectNoResourceReferencedByScene(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, pendingFlushesWillBeAppliedIfSceneGetsUnmapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(); - - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, resourceUsedByRenderedSceneUnreferencedInFlushWillBeUnreferencedOnceFlushIsReady) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // renderable with resources uploaded and shown - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - expectVertexArrayUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // swap indices and block on it - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).WillOnce(Return(DeviceResourceHandle::Invalid())).RetiresOnSaturation(); - expectVertexArrayUnloaded(); - - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canRemapScene) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - mapScene(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); -} - -TEST_F(ARendererSceneUpdater, canRemapSceneWithUnusedTextureSampler) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(0u, false, true); // with sampler that will not be set - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - mapScene(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); -} - -TEST_F(ARendererSceneUpdater, mappedSceneWithBlockingFlushGetsUnmappedAndRemapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // resource will not be blocked next time used - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - - expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - mapScene(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); - expectUnloadOfSceneResources(); - destroySceneUpdater(); -} - -TEST_F(ARendererSceneUpdater, newRenderTargetIsUploadedOnlyAfterPendingFlushApplied) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - update(); - - // emulate blocking flush with new RT - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - createRenderTargetWithBuffers(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock pending flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - expectRenderTargetUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, renderTargetIsUnloadedOnlyAfterPendingFlushAppliedIfAlreadyUsedByRendererScene) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - createRenderTargetWithBuffers(); - expectRenderTargetUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // emulate blocking flush with release of RT - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - destroyRenderTargetWithBuffers(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock pending flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - expectRenderTargetUnloaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, renderTargetIsUploadedOnceWhenMappedIfCreatedBeforeMapping) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createRenderTargetWithBuffers(); - update(); - - expectRenderTargetUploaded(); - mapScene(); - - expectRenderTargetUnloaded(); - destroyRenderTargetWithBuffers(); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, renderTargetIsUploadedOnceWhenCreatedIfAlreadyMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - createRenderTargetWithBuffers(); - expectRenderTargetUploaded(); - update(); - - expectRenderTargetUnloaded(); - destroyRenderTargetWithBuffers(); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unloadsAndUploadsRenderTargetWithSameHandleDestroyedAndCreatedInSameLoop) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - createRenderTargetWithBuffers(); - expectRenderTargetUploaded(); - update(); - - destroyRenderTargetWithBuffers(); - createRenderTargetWithBuffers(); - expectRenderTargetUnloaded(); - expectRenderTargetUploaded(); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, doesNotUploadOrUnloadRenderTargetWithSameHandleCreatedAndDestroyedInSameLoop) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - createRenderTargetWithBuffers(); - destroyRenderTargetWithBuffers(); - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, renderTargetAndBuffersAreReuploadedAfterSceneRemapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - createRenderTargetWithBuffers(); - expectRenderTargetUploaded(); - update(); - - unmapScene(); - update(); - - expectRenderTargetUploaded(); - mapScene(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, blitPassesReuploadedAfterSceneRemapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - createBlitPass(); - expectBlitPassUploaded(); - update(); - - unmapScene(); - update(); - - expectBlitPassUploaded(); - mapScene(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, blockedResourceAndUnmapWillReferenceThatResourceWithNextMap) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - mapScene(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canDestroyDisplayIfThereArePendingUploads) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, onlyMapsASceneIfAllNeededResourcesAreUploaded) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // simulate upload - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - - update(); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, onlyMapsASceneIfAllNeededResourcesAreUploaded_WithTwoDifferentBlockingResources) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - // block 2 resources - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock 1st resource - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - - // still blocked - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock 2nd resource - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - - // only now is scene mapped - update(); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canUnmapSceneWhenSceneIsMapRequestedWithMapFailedEvent) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnmapped }); - EXPECT_EQ(ESceneState::Subscribed, sceneStateExecutor.getSceneState(getSceneId())); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canUnmapSceneWhenSceneIsMappingAndUploadingWithMapFailedEvent) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - expectUnloadOfSceneResources(); - rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnmapped }); - EXPECT_EQ(ESceneState::Subscribed, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, unmappingSceneWhenSceneIsMappingAndUploadingWillUnloadItsResources) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - setRenderableResources(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - expectUnloadOfSceneResources(); - rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnmapped }); - EXPECT_EQ(ESceneState::Subscribed, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, renderTargetIsUploadedWhenSceneMappingAndUploadingEvenIfMappingBlockedByFlush) -{ - // create scene with RT - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createRenderable(); - createRenderTargetWithBuffers(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // request map but block resource - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - expectRenderTargetUploaded(); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock flush by making resource available - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - - update(); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - - update(); - - unmapScene(); - destroyDisplay(); -} - -// This is to reproduce and prove fix for a crash that happens in very very special constellation -// Note: this is probably outdated setup not reproducible in current code, keeping as confidence test -TEST_F(ARendererSceneUpdater, confidenceTest_renderTargetIsUploadedInCorrectOrderAfterSceneMappedWithShowRequestAndBlockingFlushComingAtSameTime) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - // create renderable - createRenderTargetWithBuffers(); - createRenderable(); - setRenderableResources(); - - // request map scene - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - // block resource - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - expectRenderTargetUploaded(); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // do one loop in uploading state that will upload resolvable resources - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // swap invalid resource for another invalid (this will put the first invalid into 'to be unreferenced' list in current implementation) - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock mapping - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - - update(); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - - ////////////////// - // unmap scene will unload render target, invalid resources is still in 'to be unreferenced' list - unmapScene(); - update(); - - // request map - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash2 }); - expectRenderTargetUploaded(); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // the scene will get to mapped state and within that very frame do: - // 1. add dummy sync flush - // 2. switch to rendered state (show scene) - performFlush(0u); - update(); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - expectVertexArrayUploaded(); - // request show scene - showScene(); - - // if render target (or any other scene resources) is not uploaded, this would crash/assert - update(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, multipleFlushesWithRemoveAndAddResourceReferenceWhileAlreadyBlocked) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // create blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - createRenderable(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // add new resource reference - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // remove and add again the previously added resource reference - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }, 0u); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - - // update - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 1); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }); - expectVertexArrayUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // update - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, multipleFlushesWithAddAndRemoveBlockingResourceWhileAlreadyBlocked) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // create blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - createRenderable(); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // remove and add again the blocking resource - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }); - expectResourcesProvided(2); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - - // update - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock flushes - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }); - expectVertexArrayUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // update - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, multipleFlushesWithAddAndRemoveAndAddBlockingResourceReferenceWhileAlreadyBlocked) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // create blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - createRenderable(); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // add, remove and add again another blocking resource (MockResourceHash::IndexArrayHash) - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, 0u, 2); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - expectResourcesProvided(3); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - update(); - - // update - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 1); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); - expectVertexArrayUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // update - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, multipleFlushesWithRemoveAndAddAndRemoveBlockingResourceReferenceWhileAlreadyBlocked) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - // create blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - createRenderable(); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // remove, add and remove again another blocking resource (MockResourceHash::IndexArrayHash) - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 2); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, 0u, 1); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - expectResourcesProvided(3); - update(); - - // update - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unblock flushes - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 2); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); - expectVertexArrayUploaded(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // update - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, willForceMapSceneAfterMaximumNumberOfPendingFlushesReached) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - // mapping blocked by effect - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - update(); - - // add blocking flush so that upcoming flushes are queuing up - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - setRenderableResources(); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // will force apply and log blocking resources - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); - for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(); - update(); - } - // expect force mapped - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, willForceMapSceneAfterMaximumWaitingTimeReached) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - createRenderable(); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - // mapping blocked by effect - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - update(); - - constexpr std::chrono::milliseconds testTimeout{ 30 }; - rendererSceneUpdater->setForceMapTimeout(testTimeout); - std::this_thread::sleep_for(testTimeout * 2); - update(); - update(); - - // expect force mapped - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, forceAppliesPendingFlushesAfterMaximumNumberReachedWhenSceneMappedOrRendered) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - update(); - - // add blocking flush so that upcoming flushes are queuing up - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - - setRenderableResources(); - update(); - - // type queried for logging of missing resources - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); - - // mapped state - { - // flushes are blocked due to unresolved resource - for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - } - - showScene(); - - // rendered state - { - // add new blocking flush so that upcoming flushes are queuing up - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - - // flushes are blocked due to unresolved resource - for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - } - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, reactsOnDynamicChangesOfFlushForceApplyLimit) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - update(); - - // add blocking flush so that upcoming flushes are queuing up - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - setRenderableResources(); - update(); - - // Reduce flush limit -> expect force flush earlier - constexpr size_t newShorterFlushLimit = ForceApplyFlushesLimit / 2u; - rendererSceneUpdater->setLimitFlushesForceApply(newShorterFlushLimit); - - // type queried for logging of missing resources - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); - - // mapped state - { - // flushes are blocked due to unresolved resource - for (size_t i = 0u; i < newShorterFlushLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - } - - showScene(); - - // rendered state - { - // add new blocking flush so that upcoming flushes are queuing up - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - - // flushes are blocked due to unresolved resource - for (size_t i = 0u; i < newShorterFlushLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - } - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, applyingPendingFlushesAfterMaximumNumberOfPendingFlushesReachedDoesNotAffectOtherScene) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createPublishAndSubscribeScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }, 0u); - createRenderable(0u); - mapScene(0u); - expectVertexArrayUploaded(0u); - showScene(0u); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }, 1u); - createRenderable(1u); - mapScene(1u); - expectVertexArrayUploaded(1u); - showScene(1u); - - // blocking resource - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }, 0u); - setRenderableResources(0u, MockResourceHash::IndexArrayHash); - expectVertexArrayUnloaded(0u); - update(); - - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }, 1u); - setRenderableResources(1u, MockResourceHash::IndexArrayHash); - expectVertexArrayUnloaded(1u); - update(); - - // type queried for logging of missing resources - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); - - { - // flushes are blocked due to unresolved resource - expectVertexArrayUploaded(1u); - for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(1u); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - // but only for scene 1, scene 0 stays blocked as the number of pending flushes there is below the maximum threshold - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene(0u)); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(1u)); - - // repeat for scene 0 - expectVertexArrayUploaded(0u); - for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) - { - performFlushWithCreateNodeAction(0u); - update(); - } - - // after maximum of pending flushes was reached the flushes were applied regardless of missing resource - // also for scene 0 now - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(0u)); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(1u)); - } - - hideScene(0u); - hideScene(1u); - unmapScene(0u); - unmapScene(1u); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, nonBlockingFlushesGetAppliedEvenIfSceneIsBlockedToBeMappedDueToInvalidResourceFromBefore) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - // scene is now blocked to be mapped due to effect - - // blocking flush cannot be applied - expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); - setRenderableResources(); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - // scene still cannot be mapped because it still uses effect, also flushes are blocking now - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock indices meaning that the pending flushes are non-blocking - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); - update(); - // pending flushes were applied even though scene is waiting to be mapped - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - // scene still cannot be mapped because it still uses effect - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - - // unblock also effect - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - update(); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canUnmapSceneWithPendingFlushAndRequestMapInSingleLoop) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - // blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - - // unmap and request map - unmapScene(); - expectResourcesReferenced({ MockResourceHash::EffectHash }); - requestMapScene(); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - - // scene is not mapped so pending flushes are applied to scene - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - // and scene switches to mapping/uploading state - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // next update causes all resources in use to be uploaded if available - update(); - // scene is now blocked to be mapped due to use of effect - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock effect - reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); - update(); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, canUnmapSceneWithPendingFlushAndRequestMapAndAddAnotherPendingFlushInSingleLoop) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - // blocking flush - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); - update(); - EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // unmap and request map - unmapScene(); - requestMapScene(); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // in addition add one more blocking flush replacing previous blocking resource - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - // indices1 was unreferenced when unmapped - // it will be also replaced with indices2 when flush from above gets applied so it will not be requested again when mapped - // scene is not mapped so pending flushes are applied to scene - expectResourcesReferenced({ MockResourceHash::EffectHash }, 0u, 1); // sync logic to compensate missing ref for new logic when scene re-mapped - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); // new logic (new flush with provided indices2) - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - // and scene switches to mapping/uploading state - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); // new logic ref comes later before providing - - // next update causes all resources in use to be uploaded if available - expectResourcesProvided(1); // only indices2 is provided because it was kept by new logic for mapping - update(); - // scene is now blocked to be mapped due to use of indices2 - EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); - - // unblock indices2 - reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); - update(); - expectInternalSceneStateEvent(ERendererEventType::SceneMapped); - EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); - - update(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, forceUnsubscribesSceneIfSceneResourcesUploadExceedsBudgetWhileMapping) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::SceneResourcesUpload, 0u); - - // create many scene resources (if not enough scene resources the budget is not even checked) - for (uint32_t i = 0; i < 40; ++i) - { - createRenderTargetWithBuffers(0u, RenderTargetHandle{ i }, RenderBufferHandle{ i * 2 }, RenderBufferHandle{ i * 2 + 1 }); - update(); - expectNoEvent(); - } - - // expect scene unsubscribe - requestMapScene(); - - // context is enabled twice, first before uploading, second when unloading due to forced unsubscribe/destroy - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(), _)).Times(AtLeast(2)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTarget(_, _, getSceneId())).Times(AnyNumber()); - // render buffers are collected first therefore render targets might or might not be uploaded before interruption, depending on checking frequency (internal logic of scene resources uploader) - - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); - - expectUnloadOfSceneResources(); - update(); - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect }); - - update(); - expectNoEvent(); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, forceUnsubscribesSceneIfSceneResourcesUploadExceedsBudgetWhileMapping_doesNotAffectOtherAlreadyRenderedScene) -{ - createDisplayAndExpectSuccess(); - const auto sceneIdx1 = createPublishAndSubscribeScene(); - - // create scene with some resources and render it - mapScene(sceneIdx1); - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(sceneIdx1); - setRenderableResources(sceneIdx1); - expectVertexArrayUploaded(); - update(); - showScene(sceneIdx1); - - // next one is malicious scene with too many scene resources - const auto sceneIdx2 = createPublishAndSubscribeScene(); - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::SceneResourcesUpload, 0u); - - // create many scene resources (if not enough scene resources the budget is not even checked) - for (uint32_t i = 0; i < 40; ++i) - { - createRenderTargetWithBuffers(sceneIdx2, RenderTargetHandle{ i }, RenderBufferHandle{ i * 2 }, RenderBufferHandle{ i * 2 + 1 }); - update(); - expectNoEvent(); - } - - // expect scene unsubscribe - requestMapScene(sceneIdx2); - - // context is enabled twice, first before uploading, second when unloading due to forced unsubscribe/destroy - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(sceneIdx2), _)).Times(AtLeast(2)); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTarget(_, _, getSceneId(sceneIdx2))).Times(AnyNumber()); - // render buffers are collected first therefore render targets might or might not be uploaded before interruption, depending on checking frequency (internal logic of scene resources uploader) - - EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); - - expectUnloadOfSceneResources(sceneIdx2); - update(); - expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect }); - - update(); - expectNoEvent(); - - // hide and unmap first scene - hideScene(sceneIdx1); - unmapScene(sceneIdx1); - - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, confidenceTest_forceApplyPendingFlushes_keepCyclingThroughResourcesAndSimulateRandomlyCannotUpload) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - showScene(); - - const std::array resourcesToCycle{ MockResourceHash::IndexArrayHash, MockResourceHash::IndexArrayHash2, MockResourceHash::IndexArrayHash3 }; - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, resourcesToCycle[0] }); - createRenderable(); - setRenderableResources(0u, resourcesToCycle[0]); - expectVertexArrayUploaded(); - update(); - - constexpr int NumFlushesToForceApply = 5; - rendererSceneUpdater->setLimitFlushesForceApply(NumFlushesToForceApply); - - // due to random resources not uploaded strict un/ref mocking is not feasible - // important check is at end that resources ref count reaches zero - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(getSceneId(), _)).Times(AnyNumber()); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceResourcesForScene(getSceneId(), _)).Times(AnyNumber()); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, provideResourceData(_)).Times(AnyNumber()); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadVertexArray(_, _, getSceneId())).Times(AnyNumber()); - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadVertexArray(_, getSceneId())).Times(AnyNumber()); - // will force apply and log blocking resources - EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); - - // start from 1, 0 is active at this point - for (int i = 1; i < 100; ++i) - { - ResourceContentHash nextRes = resourcesToCycle[i % 3]; - - if (lastFlushWasAppliedOnRendererScene()) - { - // reset reporting of all resources to default - uploaded - for (const auto r : resourcesToCycle) - reportResourceAs(r, EResourceStatus::Uploaded); - - // randomly block next resource - if (TestRandom::Get(0, 10) > 7) - reportResourceAs(nextRes, EResourceStatus::Provided); - } - - setRenderableResources(0u, nextRes); - update(); - } - - destroyRenderable(); - update(); - - // in case loop ended with blocking flush reset reporting of all resources to uploaded - for (const auto r : resourcesToCycle) - reportResourceAs(r, EResourceStatus::Uploaded); - update(); - EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); - - // make sure that after destroying renderable reference counts reach zero - expectNoResourceReferencedByScene(); - - hideScene(); - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, referencesAndUnreferencesResourcesSharedByTwoRenderablesFromTwoScenes) -{ - createDisplayAndExpectSuccess(); - - const auto s1 = createPublishAndSubscribeScene(); - const auto s2 = createPublishAndSubscribeScene(); - mapScene(s1); - mapScene(s2); - - expectResourcesReferenced({ MockResourceHash::EffectHash }, s1); - expectResourcesReferenced({ MockResourceHash::EffectHash }, s2); - expectResourcesProvided(2); - createRenderable(s1); - createRenderable(s2); - update(); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); - - // res1 used - { - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s1); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s2); - expectResourcesProvided(2); - setRenderableResources(s1); - setRenderableResources(s2); - update(); - } - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); - - // res2 used, res1 unneeded - { - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, s1); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, s2); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, s1); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, s2); - expectResourcesProvided(2); - setRenderableResources(s1, MockResourceHash::IndexArrayHash2); - setRenderableResources(s2, MockResourceHash::IndexArrayHash2); - update(); - } - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); - - // res3 used, res2 unneeded, res1 unloaded - { - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, s1); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, s2); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash3 }, s1); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash3 }, s2); - expectResourcesProvided(2); - setRenderableResources(s1, MockResourceHash::IndexArrayHash3); - setRenderableResources(s2, MockResourceHash::IndexArrayHash3); - update(); - } - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash3)); - - // res1 used, res3 unneeded, res2 unloaded - { - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash3 }, s1); - expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash3 }, s2); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s1); - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s2); - expectResourcesProvided(2); - setRenderableResources(s1, MockResourceHash::IndexArrayHash); - setRenderableResources(s2, MockResourceHash::IndexArrayHash); - update(); - } - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); - - destroyRenderable(s1); - destroyRenderable(s2); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s2); - update(); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); - expectNoResourceReferencedByScene(s1); - expectNoResourceReferencedByScene(s2); - - unmapScene(s1); - unmapScene(s2); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, ConsolidatesNoMoreNeededResourcesBeforeMapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - createRenderable(); - - // res1 used - setRenderableResources(); - update(); - - // res2 used, res1 unneeded - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - update(); - - // res3 used, res1 + res2 unneeded - setRenderableResources(0u, MockResourceHash::IndexArrayHash3); - update(); - - // even if cycled through 3 resources only last one is uploaded - expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash3 }); - mapScene(); - - destroyRenderable(); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash3 }); - update(); - expectNoResourceReferencedByScene(); - - unmapScene(); - destroyDisplay(); -} - -///////////////////// -// tests to make sure request logic and new push logic can co-exist -///////////////////// - -TEST_F(ARendererSceneUpdater, keepsTrackOfRefsAfterSceneRemapped) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - update(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - unmapScene(); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - mapScene(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - destroyRenderable(); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - update(); - expectNoResourceReferencedByScene(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, keepsTrackOfRefsAfterSceneRemapped_resourceAddedInBetweenReMappingIsProvided) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); - createRenderable(); - update(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - unmapScene(); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - setRenderableResources(); - - expectResourcesReferenced({ MockResourceHash::EffectHash }); // extra ref due to re-map for new logic - expectResourcesReferenced({ MockResourceHash::IndexArrayHash }); // ref for new logic from arrived resource - expectResourcesProvided(); // provided resource added in between mapping - mapScene(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - - destroyRenderable(); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - update(); - expectNoResourceReferencedByScene(); - - unmapScene(); - destroyDisplay(); -} - -TEST_F(ARendererSceneUpdater, keepsTrackOfRefsAfterSceneRemapped_resourceAddedAndRemovedInBetweenReMapping) -{ - createDisplayAndExpectSuccess(); - createPublishAndSubscribeScene(); - mapScene(); - - expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); - createRenderable(); - setRenderableResources(); - update(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - unmapScene(); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - setRenderableResources(0u, MockResourceHash::IndexArrayHash2); - - expectResourcesReferenced({ MockResourceHash::EffectHash }); // extra ref due to re-map for new logic - expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }); // ref for new logic from arrived resource - expectResourcesProvided(); // provided resource added in between mapping - mapScene(); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); - EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); - EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); - - destroyRenderable(); - expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash2 }); - update(); - expectNoResourceReferencedByScene(); - - unmapScene(); - destroyDisplay(); -} - -} diff --git a/renderer/RendererLib/RendererLib/test/RendererScenesTest.cpp b/renderer/RendererLib/RendererLib/test/RendererScenesTest.cpp deleted file mode 100644 index ad9d7f881..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererScenesTest.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" - -#include - -using namespace testing; -using namespace ramses_internal; - -class ARendererScenes : public ::testing::Test -{ -public: - ARendererScenes() - : rendererScenes(eventCollector) - { - } - -protected: - RendererEventCollector eventCollector; - RendererScenes rendererScenes; -}; - -TEST_F(ARendererScenes, isEmptyInitially) -{ - EXPECT_EQ(rendererScenes.begin(), rendererScenes.end()); - EXPECT_EQ(0u, rendererScenes.size()); -} - -TEST_F(ARendererScenes, isEmptyInitiallyConst) -{ - const RendererScenes rendererScenesConst(eventCollector); - EXPECT_EQ(rendererScenesConst.begin(), rendererScenesConst.end()); - EXPECT_EQ(0u, rendererScenesConst.size()); -} - -TEST_F(ARendererScenes, createsSceneAndStagingInfo) -{ - const std::string sceneName("bla"); - const SceneId sceneID(12u); - SceneInfo sceneInfo(sceneID, sceneName); - IScene& createdScene = rendererScenes.createScene(sceneInfo); - - SceneSizeInformation sceneSizeInfo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18); - createdScene.preallocateSceneSize(sceneSizeInfo); - - EXPECT_EQ(1u, rendererScenes.size()); - EXPECT_NE(rendererScenes.begin(), rendererScenes.end()); - EXPECT_TRUE(rendererScenes.hasScene(sceneID)); - EXPECT_EQ(&createdScene, &rendererScenes.getScene(sceneID)); - EXPECT_EQ(sceneSizeInfo, createdScene.getSceneSizeInformation()); - EXPECT_EQ(sceneName, createdScene.getName()); - EXPECT_EQ(sceneID, createdScene.getSceneId()); - rendererScenes.getStagingInfo(sceneID); -} - -TEST_F(ARendererScenes, destroysSceneAndStagingInfo) -{ - const SceneId sceneID(12u); - rendererScenes.createScene(SceneInfo(sceneID)); - - rendererScenes.destroyScene(sceneID); - - EXPECT_EQ(0u, rendererScenes.size()); - EXPECT_EQ(rendererScenes.begin(), rendererScenes.end()); - EXPECT_FALSE(rendererScenes.hasScene(sceneID)); -} - -TEST_F(ARendererScenes, canIterateOverScenes) -{ - const SceneId sceneID1(12u); - rendererScenes.createScene(SceneInfo(sceneID1)); - - const SceneId sceneID2(13u); - rendererScenes.createScene(SceneInfo(sceneID2)); - - const SceneId sceneID3(14u); - rendererScenes.createScene(SceneInfo(sceneID3)); - - EXPECT_EQ(3u, rendererScenes.size()); - EXPECT_NE(rendererScenes.begin(), rendererScenes.end()); - EXPECT_TRUE(rendererScenes.hasScene(sceneID1)); - EXPECT_TRUE(rendererScenes.hasScene(sceneID2)); - EXPECT_TRUE(rendererScenes.hasScene(sceneID3)); - - uint32_t count = 0u; - for(auto rendScene : rendererScenes) - { - EXPECT_TRUE(sceneID1 == rendScene.key || sceneID2 == rendScene.key || sceneID3 == rendScene.key); - EXPECT_TRUE(nullptr != rendScene.value.scene); - EXPECT_TRUE(nullptr != rendScene.value.stagingInfo); - ++count; - } - EXPECT_EQ(3u, count); -} diff --git a/renderer/RendererLib/RendererLib/test/RendererStatisticsTest.cpp b/renderer/RendererLib/RendererLib/test/RendererStatisticsTest.cpp deleted file mode 100644 index 9ca8c92f1..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererStatisticsTest.cpp +++ /dev/null @@ -1,422 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererLib/RendererStatistics.h" -#include "Utils/LogMacros.h" -#include "gmock/gmock.h" - -using namespace testing; -using namespace ramses_internal; - -class ARendererStatistics : public ::testing::Test -{ -public: - [[nodiscard]] std::string logOutput() const - { - StringOutputStream strstr; - stats.writeStatsToStream(strstr); - return strstr.release(); - } - -protected: - RendererStatistics stats; - const SceneId sceneId1{ 11 }; - const SceneId sceneId2{ 22 }; - const DeviceResourceHandle ob1{ 11 }; - const DeviceResourceHandle ob2{ 22 }; - const DeviceResourceHandle ob3{ 33 }; -}; - -TEST_F(ARendererStatistics, tracksDrawCallsPerFrame) -{ - stats.frameFinished(1u); - stats.frameFinished(2u); - stats.frameFinished(3u); - stats.frameFinished(4u); - EXPECT_EQ(2u, stats.getDrawCallsPerFrame()); - - stats.reset(); - EXPECT_EQ(0u, stats.getDrawCallsPerFrame()); - - stats.frameFinished(3u); - stats.frameFinished(3u); - EXPECT_EQ(3u, stats.getDrawCallsPerFrame()); -} - -TEST_F(ARendererStatistics, tracksFrameCount) -{ - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 3")); -} - -TEST_F(ARendererStatistics, tracksSceneRenderedCount) -{ - stats.sceneRendered(sceneId1); - stats.frameFinished(0u); - stats.sceneRendered(sceneId1); - stats.sceneRendered(sceneId2); - stats.frameFinished(0u); - stats.sceneRendered(sceneId1); - stats.sceneRendered(sceneId2); - stats.frameFinished(0u); - stats.sceneRendered(sceneId2); - - EXPECT_THAT(logOutput(), HasSubstr("Scene 11: rendered 3")); - EXPECT_THAT(logOutput(), HasSubstr("Scene 22: rendered 3")); -} - -TEST_F(ARendererStatistics, untracksScene) -{ - stats.sceneRendered(sceneId1); - stats.frameFinished(0u); - stats.sceneRendered(sceneId1); - stats.sceneRendered(sceneId2); - stats.frameFinished(0u); - - stats.untrackScene(sceneId1); - - EXPECT_THAT(logOutput(), Not(HasSubstr("Scene 11"))); - EXPECT_THAT(logOutput(), HasSubstr("Scene 22: rendered 1")); -} - -TEST_F(ARendererStatistics, tracksSceneArrivedFlushesIndependentlyFromFrames) -{ - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.trackArrivedFlush(sceneId2, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.trackArrivedFlush(sceneId2, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.frameFinished(0u); - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - - EXPECT_THAT(logOutput(), HasSubstr("Scene 11:")); - EXPECT_THAT(logOutput(), HasSubstr("FArrived 3")); - EXPECT_THAT(logOutput(), HasSubstr("Scene 22:")); - EXPECT_THAT(logOutput(), HasSubstr("FArrived 2")); -} - -TEST_F(ARendererStatistics, tracksFramesWhereSceneFlushArrived) -{ - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.frameFinished(0u); - stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 4")); - EXPECT_THAT(logOutput(), HasSubstr("framesFArrived 2")); -} - -TEST_F(ARendererStatistics, tracksFramesWhereSceneFlushApplied) -{ - stats.flushApplied(sceneId1); - stats.flushApplied(sceneId1); - stats.flushApplied(sceneId1); - stats.frameFinished(0u); - stats.flushApplied(sceneId1); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 4")); - EXPECT_THAT(logOutput(), HasSubstr("framesFApplied 2")); -} - -TEST_F(ARendererStatistics, tracksFramesWhereSceneFlushBlocked) -{ - stats.flushBlocked(sceneId1); - stats.flushBlocked(sceneId1); - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 4")); - EXPECT_THAT(logOutput(), HasSubstr("framesFBlocked 2")); -} - -TEST_F(ARendererStatistics, tracksMaximumConsecutiveFramesWhereSceneFlushNotApplied) -{ - stats.flushApplied(sceneId1); - stats.flushApplied(sceneId1); - stats.flushApplied(sceneId1); - stats.frameFinished(0u); - stats.flushApplied(sceneId1); - stats.frameFinished(0u); - stats.frameFinished(0u); //x - stats.frameFinished(0u); //x - stats.flushApplied(sceneId1); - stats.flushApplied(sceneId1); - stats.frameFinished(0u); - stats.frameFinished(0u); //x - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 6")); - EXPECT_THAT(logOutput(), HasSubstr("FApplied 6")); - EXPECT_THAT(logOutput(), HasSubstr("framesFApplied 3")); - EXPECT_THAT(logOutput(), HasSubstr("maxFramesWithNoFApplied 2")); -} - -TEST_F(ARendererStatistics, tracksMaximumConsecutiveFramesWhereSceneFlushBlocked) -{ - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - stats.flushBlocked(sceneId1); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 6")); - EXPECT_THAT(logOutput(), HasSubstr("framesFBlocked 5")); - EXPECT_THAT(logOutput(), HasSubstr("maxFramesFBlocked 3")); -} - -TEST_F(ARendererStatistics, tracksFramebufferAndOffscreenBufferSwapCounts) -{ - stats.framebufferSwapped(); - stats.offscreenBufferSwapped(ob1, false); - stats.framebufferSwapped(); - stats.offscreenBufferSwapped(ob2, false); - stats.offscreenBufferSwapped(ob3, false); - stats.frameFinished(0u); - - stats.framebufferSwapped(); - stats.offscreenBufferSwapped(ob1, false); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("FB: 3; OB11: 2; OB22: 1; OB33: 1")); -} - -TEST_F(ARendererStatistics, tracksInterruptibleOffscreenBuffer) -{ - stats.offscreenBufferInterrupted(ob1); - stats.frameFinished(0u); - stats.offscreenBufferSwapped(ob1, true); - stats.frameFinished(0u); - stats.offscreenBufferInterrupted(ob1); - stats.frameFinished(0u); - stats.offscreenBufferSwapped(ob1, true); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("OB11: 2 (intr: 2)")); -} - -TEST_F(ARendererStatistics, untracksOffscreenBuffer) -{ - stats.offscreenBufferSwapped(ob1, false); - stats.offscreenBufferSwapped(ob2, false); - stats.offscreenBufferSwapped(ob3, false); - stats.frameFinished(0u); - stats.untrackOffscreenBuffer(ob2); - - EXPECT_THAT(logOutput(), Not(HasSubstr("OB22"))); -} - -TEST_F(ARendererStatistics, tracksStreamTextureSource) -{ - const WaylandIviSurfaceId src{ 99u }; - stats.streamTextureUpdated(src, 2u); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.streamTextureUpdated(src, 9u); - stats.frameFinished(0u); - stats.streamTextureUpdated(src, 1u); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("numFrames 5")); - EXPECT_THAT(logOutput(), HasSubstr("SourceId 99: upd 12, framesUpd 3, maxUpdInFrame 9, maxFramesWithNoUpd 2")); -} - -TEST_F(ARendererStatistics, logsValidNumbersWhenStreamTextureInactive) -{ - const WaylandIviSurfaceId src{ 99u }; - stats.streamTextureUpdated(src, 2u); // will register source - stats.reset(); - stats.frameFinished(0u); - stats.frameFinished(0u); - stats.frameFinished(0u); - - EXPECT_THAT(logOutput(), HasSubstr("SourceId 99: upd 0, framesUpd 0, maxUpdInFrame 0, maxFramesWithNoUpd 3")); -} - -TEST_F(ARendererStatistics, untracksStreamTextureSource) -{ - const WaylandIviSurfaceId src{ 99u }; - stats.streamTextureUpdated(src, 2u); - stats.frameFinished(0u); - - stats.untrackStreamTexture(src); - - EXPECT_THAT(logOutput(), Not(HasSubstr("SourceId 99"))); -} - -TEST_F(ARendererStatistics, tracksResourceUploads) -{ - stats.resourceUploaded(2u); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("resUploaded 1 (2 B)")); - - stats.reset(); - EXPECT_THAT(logOutput(), Not(HasSubstr("resUploaded"))); - - stats.resourceUploaded(2u); - stats.resourceUploaded(77u); - stats.resourceUploaded(100u); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("resUploaded 3 (179 B)")); - - stats.reset(); - EXPECT_THAT(logOutput(), Not(HasSubstr("resUploaded"))); -} - -TEST_F(ARendererStatistics, tracksSceneResourceUploads) -{ - stats.sceneResourceUploaded(sceneId1, 2u); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("RSUploaded 1 (2 B)")); - - stats.reset(); - EXPECT_THAT(logOutput(), Not(HasSubstr("RSUploaded"))); - - stats.sceneResourceUploaded(sceneId1, 2u); - stats.sceneResourceUploaded(sceneId1, 77u); - stats.sceneResourceUploaded(sceneId2, 100u); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("RSUploaded 2 (79 B)")); //scene1 - EXPECT_THAT(logOutput(), HasSubstr("RSUploaded 1 (100 B)")); //scene2 - - stats.reset(); - EXPECT_THAT(logOutput(), Not(HasSubstr("RSUploaded"))); -} - -TEST_F(ARendererStatistics, tracksShaderCompilationAndTimes) -{ - stats.shaderCompiled(std::chrono::microseconds(2u), "some effect", SceneId(123)); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("shadersCompiled 1")); - EXPECT_THAT(logOutput(), HasSubstr("for total ms:0")); - - stats.reset(); - EXPECT_THAT(logOutput(), Not(HasSubstr("shadersCompiled"))); - - stats.shaderCompiled(std::chrono::microseconds(3000u), "some effect name", SceneId(123)); - stats.shaderCompiled(std::chrono::microseconds(5000u), "some effect name", SceneId(124)); - stats.shaderCompiled(std::chrono::microseconds(7000u), "longest effect name", SceneId(125)); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("shadersCompiled 3")); - EXPECT_THAT(logOutput(), HasSubstr("for total ms:15")); - EXPECT_THAT(logOutput(), HasSubstr("longest: longest effect name")); - EXPECT_THAT(logOutput(), HasSubstr("from scene:125")); - EXPECT_THAT(logOutput(), HasSubstr("ms:7")); - - stats.reset(); - EXPECT_THAT(logOutput(), Not(HasSubstr("shadersCompiled"))); -} - - -TEST_F(ARendererStatistics, tracksExpirationOffsets) -{ - stats.addExpirationOffset(sceneId1, -100); - stats.addExpirationOffset(sceneId1, -80); - stats.addExpirationOffset(sceneId1, -120); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/3:-120/-80/-100)")); - stats.reset(); - - stats.addExpirationOffset(sceneId1, -30); - stats.addExpirationOffset(sceneId1, 10); - stats.addExpirationOffset(sceneId1, -10); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (1/3:-30/10/-10)")); - stats.reset(); - - stats.addExpirationOffset(sceneId1, -30); - stats.addExpirationOffset(sceneId2, 30); - stats.addExpirationOffset(sceneId1, -20); - stats.addExpirationOffset(sceneId2, 20); - stats.addExpirationOffset(sceneId1, -10); - stats.addExpirationOffset(sceneId2, 10); - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/3:-30/-10/-20)")); - EXPECT_THAT(logOutput(), HasSubstr("Exp (3/3:10/30/20)")); - stats.reset(); - - stats.frameFinished(0u); - EXPECT_THAT(logOutput(), Not(HasSubstr("Exp ("))); -} - - -TEST_F(ARendererStatistics, confidenceTest_fullLogOutput) -{ - for (size_t period = 0u; period < 2u; ++period) - { - stats.trackArrivedFlush(sceneId1, 123, 5, 3, 4, std::chrono::milliseconds{2}); - stats.trackArrivedFlush(sceneId2, 6, 7, 8, 9, std::chrono::milliseconds{5}); - stats.flushBlocked(sceneId1); - stats.flushApplied(sceneId2); - stats.framebufferSwapped(); - stats.frameFinished(0); - - stats.resourceUploaded(2u); - stats.resourceUploaded(77u); - stats.shaderCompiled(std::chrono::microseconds(0u), "", SceneId(54321)); - stats.shaderCompiled(std::chrono::microseconds(1000u), "slow effect", SceneId(12345)); - - stats.trackArrivedFlush(sceneId1, 123, 5, 3, 4, std::chrono::milliseconds{3}); - stats.flushBlocked(sceneId1); - stats.sceneRendered(sceneId2); - stats.framebufferSwapped(); - stats.frameFinished(100); - - stats.trackArrivedFlush(sceneId1, 123, 5, 3, 4, std::chrono::milliseconds{6}); - stats.trackArrivedFlush(sceneId2, 6, 7, 8, 9, std::chrono::milliseconds{11}); - stats.flushApplied(sceneId1); - stats.flushBlocked(sceneId2); - stats.sceneRendered(sceneId1); - stats.sceneRendered(sceneId2); - stats.framebufferSwapped(); - stats.offscreenBufferInterrupted(ob1); - stats.framebufferSwapped(); - stats.frameFinished(0); - - stats.sceneResourceUploaded(sceneId1, 3u); - stats.sceneResourceUploaded(sceneId1, 77u); - stats.sceneResourceUploaded(sceneId2, 200u); - - stats.sceneRendered(sceneId1); - stats.framebufferSwapped(); - stats.offscreenBufferSwapped(ob1, true); - stats.frameFinished(0); - - EXPECT_THAT(logOutput(), HasSubstr("Avg framerate: ")); - EXPECT_THAT(logOutput(), HasSubstr("FPS [minFrameTime ")); - EXPECT_THAT(logOutput(), HasSubstr("us, maxFrameTime ")); - EXPECT_THAT(logOutput(), HasSubstr("], drawcallsPerFrame 25, numFrames 4")); - EXPECT_THAT(logOutput(), HasSubstr("resUploaded 2 (79 B)")); - EXPECT_THAT(logOutput(), HasSubstr("shadersCompiled 2")); - EXPECT_THAT(logOutput(), HasSubstr("for total ms:1")); - EXPECT_THAT(logOutput(), HasSubstr("FB: 5; OB11: 1 (intr: 1)")); - EXPECT_THAT(logOutput(), HasSubstr("Scene 11: rendered 2, framesFArrived 3, framesFApplied 1, framesFBlocked 2, maxFramesWithNoFApplied 2, maxFramesFBlocked 2, FArrived 3, FApplied 1, actions/F (123/123/123), dt/F (2/6/3.6666667), RC+/F (5/5/5), RC-/F (3/3/3), RS/F (4/4/4), RSUploaded 2 (80 B)")); - EXPECT_THAT(logOutput(), HasSubstr("Scene 22: rendered 2, framesFArrived 2, framesFApplied 1, framesFBlocked 1, maxFramesWithNoFApplied 3, maxFramesFBlocked 1, FArrived 2, FApplied 1, actions/F (6/6/6), dt/F (5/11/8), RC+/F (7/7/7), RC-/F (8/8/8), RS/F (9/9/9), RSUploaded 1 (200 B)")); - EXPECT_THAT(logOutput(), HasSubstr("slow effect")); - stats.reset(); - } -} diff --git a/renderer/RendererLib/RendererLib/test/RendererTest.cpp b/renderer/RendererLib/RendererLib/test/RendererTest.cpp deleted file mode 100644 index fc4ca05fd..000000000 --- a/renderer/RendererLib/RendererLib/test/RendererTest.cpp +++ /dev/null @@ -1,2664 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererLib/RendererConfig.h" -#include "RenderBackendMock.h" -#include "PlatformMock.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/DisplayController.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererEventCollector.h" -#include "DisplayControllerMock.h" -#include "Collections/Pair.h" -#include "RendererMock.h" -#include "ComponentMocks.h" -#include "TestSceneHelper.h" -#include -#include "Utils/ThreadLocalLog.h" - -using namespace ramses_internal; - -class ARenderer : public ::testing::TestWithParam // parametrized with/out system compositor -{ -public: - ARenderer() - : rendererScenes(rendererEventCollector) - , expirationMonitor(rendererScenes, rendererEventCollector, rendererStatistics) - , renderer(DisplayHandle{ 1u }, rendererScenes, rendererEventCollector, expirationMonitor, rendererStatistics) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - sceneRenderInterrupted.incrementRenderableIdx(); - sceneRenderInterrupted2.incrementRenderableIdx(); - sceneRenderInterrupted2.incrementRenderableIdx(); - - // Enable/disable SC - if (GetParam()) - ON_CALL(renderer.m_platform, getSystemCompositorController()).WillByDefault(Return(&renderer.m_platform.systemCompositorControllerMock)); - } - - ~ARenderer() override - { - if (renderer.hasDisplayController()) - destroyDisplayController(); - for (const auto& sceneIt : rendererScenes) - expirationMonitor.onDestroyed(sceneIt.key); - } - - void createDisplayController() - { - ASSERT_FALSE(renderer.hasDisplayController()); - renderer.createDisplayContext({}); - } - - void destroyDisplayController() - { - ASSERT_TRUE(renderer.hasDisplayController()); - if (GetParam()) - { - EXPECT_CALL(*renderer.m_displayController, getRenderBackend()); - EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, getWaylandIviSurfaceID()); - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, destroySurface(_)); - } - EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); - renderer.destroyDisplayContext(); - } - - void expectOffscreenBufferCleared(DeviceResourceHandle buffer, uint32_t clearFlags = EClearFlags_All, const glm::vec4& clearColor = Renderer::DefaultClearColor) - { - EXPECT_CALL(*renderer.m_displayController, clearBuffer(buffer, clearFlags, clearColor)).InSequence(SeqRender); - } - - void expectInterruptibleOffscreenBufferSwapped(DeviceResourceHandle buffer) - { - EXPECT_CALL(renderer.m_platform.renderBackendMock.deviceMock, swapDoubleBufferedRenderTarget(buffer)).InSequence(SeqRender); - - // rendering to interruptible buffers calls this uninteresting getter which gets reset with gmock verification - EXPECT_CALL(*renderer.m_displayController, getRenderBackend()).Times(AnyNumber()); - } - - void expectFrameBufferRendered(bool expectRerender = true, uint32_t expectRendererClear = EClearFlags_All, const glm::vec4& clearColor = Renderer::DefaultClearColor) - { - EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()).InSequence(SeqPreRender); - EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).InSequence(SeqPreRender).WillOnce(Return(true)); - - if (expectRerender) - { - // normally render executor clears, in some cases (no scene assigned) renderer clears instead - if (expectRendererClear != EClearFlags_None) - EXPECT_CALL(*renderer.m_displayController, clearBuffer(DisplayControllerMock::FakeFrameBufferHandle, expectRendererClear, clearColor)); - } - else - { - EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).InSequence(SeqRender); - EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).InSequence(SeqRender); - } - } - - void expectSwapBuffers() - { - EXPECT_CALL(*renderer.m_displayController, swapBuffers()).InSequence(SeqRender); - EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).InSequence(SeqRender); - EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).InSequence(SeqRender); - } - - void doOneRendererLoop() - { - renderer.getProfilerStatistics().markFrameFinished(std::chrono::microseconds{ 0u }); - renderer.doOneRenderLoop(); - } - - enum class EDiscardDepth - { - Allowed, - Disallowed - }; - - void expectSceneRenderedExt( - SceneId sceneId, - DeviceResourceHandle buffer, - uint32_t dispBufferClearFlags, - const glm::vec4& dispBufferClearColor, - SceneRenderExecutionIterator expectedRenderBegin, - SceneRenderExecutionIterator iteratorToReturn, - uint32_t clearFlagsToModify, - EDiscardDepth discardAllowed, - const FrameTimer* frameTimer = nullptr) - { - EXPECT_CALL(*renderer.m_displayController, renderScene(Ref(rendererScenes.getScene(sceneId)), _, frameTimer)) - .WillOnce([=](const auto&, RenderingContext& renderContext, const auto*) { - EXPECT_EQ(buffer, renderContext.displayBufferDeviceHandle); - EXPECT_EQ(expectedRenderBegin, renderContext.renderFrom); - EXPECT_EQ(dispBufferClearFlags, renderContext.displayBufferClearPending); - if (dispBufferClearFlags != EClearFlags_None) // color is relevant only if clearing something - { - EXPECT_EQ(dispBufferClearColor, renderContext.displayBufferClearColor); - } - renderContext.displayBufferClearPending = clearFlagsToModify; - EXPECT_EQ((discardAllowed == EDiscardDepth::Allowed), renderContext.displayBufferDepthDiscard); - return iteratorToReturn; - }); - } - - void expectSceneRendered(SceneId sceneId, DeviceResourceHandle buffer = DisplayControllerMock::FakeFrameBufferHandle, - uint32_t dispBufferClearFlags = EClearFlags_All, const glm::vec4& dispBufferClearColor = Renderer::DefaultClearColor) - { - expectSceneRenderedExt(sceneId, buffer, dispBufferClearFlags, dispBufferClearColor, sceneRenderBegin, sceneRenderBegin, dispBufferClearFlags, EDiscardDepth::Disallowed); - } - - void expectSceneRenderedWithInterruptionEnabled(SceneId sceneId, DeviceResourceHandle buffer, SceneRenderExecutionIterator expectedRenderBegin, - SceneRenderExecutionIterator stateToSimulate, uint32_t dispBufferClearFlags = EClearFlags_All) - { - expectSceneRenderedExt(sceneId, buffer, dispBufferClearFlags, Renderer::DefaultClearColor, expectedRenderBegin, stateToSimulate, dispBufferClearFlags, EDiscardDepth::Disallowed, &renderer.FrameTimerInstance); - } - - void expectSceneRendered(SceneId sceneId, DeviceResourceHandle buffer, EDiscardDepth discardAllowed) - { - expectSceneRenderedExt(sceneId, buffer, EClearFlags_All, Renderer::DefaultClearColor, sceneRenderBegin, sceneRenderBegin, EClearFlags_All, discardAllowed); - } - - void expectDisplayControllerReadPixels(DeviceResourceHandle deviceHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height) - { - EXPECT_CALL(*renderer.m_displayController, readPixels(deviceHandle, x, y, width, height, _)).WillOnce(Invoke( - [](auto, auto, auto, auto w, auto h, auto& dataOut) { - dataOut.resize(w * h * 4); - } - )); - } - - IScene& createScene(SceneId sceneId = SceneId()) - { - rendererScenes.createScene(SceneInfo(sceneId)); - return rendererScenes.getScene(sceneId); - } - - void assignSceneToDisplayBuffer(SceneId sceneId, int32_t sceneRenderOrder, DeviceResourceHandle displayBuffer = DisplayControllerMock::FakeFrameBufferHandle) - { - renderer.assignSceneToDisplayBuffer(sceneId, displayBuffer, sceneRenderOrder); - EXPECT_EQ(displayBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_EQ(sceneRenderOrder, renderer.getSceneGlobalOrder(sceneId)); - } - - void unassignScene(SceneId sceneId) - { - renderer.unassignScene(sceneId); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); - } - - void showScene(SceneId sceneId) - { - renderer.setSceneShown(sceneId, true); - } - - void hideScene(SceneId sceneId) - { - renderer.setSceneShown(sceneId, false); - } - - void initiateExpirationMonitoring(std::initializer_list scenes) - { - // A workaround to be able to check if scene was reported as rendered. - // Expiration monitor holds TS for applied flushes and TS for rendered scene. - // On rendered the TS of rendered scene is simply TS of last applied flush. - // Rendered scene TS can either be invalid - never reported as rendered, - // or the value below if reported as rendered - for (auto sceneId : scenes) - expirationMonitor.onFlushApplied(sceneId, currentFakeTime, {}, 0); - } - - void expectScenesReportedToExpirationMonitorAsRendered(std::initializer_list expectedScenesToBeReported) - { - for (auto sceneId : expectedScenesToBeReported) - { - EXPECT_NE(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(sceneId)); - } - } - - void scheduleScreenshot(const DeviceResourceHandle bufferHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height) - { - ScreenshotInfo screenshot; - screenshot.rectangle = { x, y, width, height }; - renderer.scheduleScreenshot(bufferHandle, std::move(screenshot)); - } - -protected: - RendererCommandBuffer rendererCommandBuffer; - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - SceneExpirationMonitor expirationMonitor; - RendererStatistics rendererStatistics; - StrictMock renderer; - - const SceneRenderExecutionIterator sceneRenderBegin{}; - SceneRenderExecutionIterator sceneRenderInterrupted; - SceneRenderExecutionIterator sceneRenderInterrupted2; - - // sequence of ordered expectations during render - Sequence SeqRender; - // sequence of ordered expectations at beginning of render (display events, can render frame, etc.) - Sequence SeqPreRender; - - const FlushTime::Clock::time_point currentFakeTime{ std::chrono::milliseconds(1000) }; -}; - -INSTANTIATE_TEST_SUITE_P(, ARenderer, ::testing::Values(false, true)); - -TEST_P(ARenderer, ListIviSurfacesInSystemCompositorController) -{ - if (GetParam()) - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, listIVISurfaces()); - renderer.systemCompositorListIviSurfaces(); -} - -TEST_P(ARenderer, SetsVisibilityInSystemCompositorController) -{ - if (GetParam()) - { - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(1), false)); - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(2), true)); - } - renderer.systemCompositorSetIviSurfaceVisibility(WaylandIviSurfaceId(1), false); - renderer.systemCompositorSetIviSurfaceVisibility(WaylandIviSurfaceId(2), true); -} - -TEST_P(ARenderer, TakesScreenshotFromSystemCompositorController) -{ - std::string_view fileName("screenshot.png"); - const int32_t screenIviId = 3; - if (GetParam()) - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, doScreenshot(fileName, screenIviId)); - renderer.systemCompositorScreenshot(fileName, screenIviId); -} - -TEST_P(ARenderer, rendersOneLoop) -{ - createDisplayController(); - EXPECT_TRUE(renderer.hasDisplayController()); - - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, unregisteredSceneIsNotMapped) -{ - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(SceneId(0u)).isValid()); -} - -TEST_P(ARenderer, doesNotMapCreatedScene) -{ - const SceneId sceneId(12u); - createScene(sceneId); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); -} - -TEST_P(ARenderer, canMapSceneOnDisplay) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - unassignScene(sceneId); -} - -TEST_P(ARenderer, assignsSceneToNativeFramebufferOfDisplayWhenMappingScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - - EXPECT_EQ(DisplayControllerMock::FakeFrameBufferHandle, renderer.getBufferSceneIsAssignedTo(sceneId)); - unassignScene(sceneId); -} - -TEST_P(ARenderer, assignsSceneToOffscreenBuffer) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - EXPECT_EQ(fakeOffscreenBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, assignsSceneToInterruptibleOffscreenBuffer) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - EXPECT_EQ(fakeOffscreenBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_TRUE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, assignsSceneFromInterruptibleOffscreenBufferToNormalOB) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - const DeviceResourceHandle ob(313u); - const DeviceResourceHandle obInterruptible(314u); - renderer.registerOffscreenBuffer(ob, 1u, 1u, false); - renderer.registerOffscreenBuffer(obInterruptible, 1u, 1u, true); - - assignSceneToDisplayBuffer(sceneId, 0, obInterruptible); - EXPECT_EQ(obInterruptible, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_TRUE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); - - assignSceneToDisplayBuffer(sceneId, 0, ob); - EXPECT_EQ(ob, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, SetsLayerVisibilityInSystemCompositorController) -{ - if (GetParam()) - { - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(18u), false)); - EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(17u), true)); - } - renderer.systemCompositorSetIviLayerVisibility(WaylandIviLayerId(18u), false); - renderer.systemCompositorSetIviLayerVisibility(WaylandIviLayerId(17u), true); -} - -TEST_P(ARenderer, doesNotClearOrRenderToOffscreenBufferIfThereIsNoSceneAssignedToIt) -{ - createDisplayController(); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - - expectFrameBufferRendered(true, EClearFlags_All); - // no offscreen buffer clear expectation - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, clearsOffscreenBufferIfThereIsSceneAssignedToItAndNotShown) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - - expectOffscreenBufferCleared(fakeOffscreenBuffer); - expectFrameBufferRendered(true, EClearFlags_All); - expectSwapBuffers(); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearsOffscreenBufferAndFramebufferWithRelatedColors) -{ - const glm::vec4 displayClearColor(.1f, .2f, .3f, .4f); - createDisplayController(); - renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, displayClearColor); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - - expectOffscreenBufferCleared(fakeOffscreenBuffer); - expectFrameBufferRendered(true, EClearFlags_All, displayClearColor); - expectSwapBuffers(); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearsFramebufferWithCustomClearColor) -{ - const glm::vec4 displayClearColor(.1f, .2f, .3f, .4f); - createDisplayController(); - renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, displayClearColor); - expectFrameBufferRendered(true, EClearFlags_All, displayClearColor); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, clearsOffscreenBufferWithCustomClearColor) -{ - createDisplayController(); - - const glm::vec4 obClearColor(.1f, .2f, .3f, .4f); - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - renderer.setClearColor(fakeOffscreenBuffer, obClearColor); - - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - - expectOffscreenBufferCleared(fakeOffscreenBuffer, EClearFlags_All, obClearColor); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearsBothFramebufferAndOffscreenBufferWithDifferentClearColors) -{ - createDisplayController(); - const glm::vec4 displayClearColor(.4f, .3f, .2f, .1f); - renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, displayClearColor); - - const glm::vec4 obClearColor(.1f, .2f, .3f, .4f); - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - renderer.setClearColor(fakeOffscreenBuffer, obClearColor); - - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - - expectOffscreenBufferCleared(fakeOffscreenBuffer, EClearFlags_All, obClearColor); - expectFrameBufferRendered(true, EClearFlags_All, displayClearColor); - expectSwapBuffers(); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearsFBIfNoSceneAssigned) -{ - createDisplayController(); - - // use some non-default clear flags - EXPECT_CALL(renderer, setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_Depth)); - renderer.setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_Depth); - - expectFrameBufferRendered(true, EClearFlags_Depth); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, clearsFBIfNoShownSceneAssigned) -{ - createDisplayController(); - - // assign scene to trigger render of OB - constexpr SceneId sceneId1{ 12u }; - constexpr SceneId sceneId2{ 13u }; - createScene(sceneId1); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneId1, 0); - assignSceneToDisplayBuffer(sceneId2, 0); - - // use some non-default clear flags - EXPECT_CALL(renderer, setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_Depth)); - renderer.setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_Depth); - - expectFrameBufferRendered(true, EClearFlags_Depth); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, clearsOBOnRerenderIfNoSceneAssigned) -{ - createDisplayController(); - - constexpr DeviceResourceHandle fakeOffscreenBuffer1{ 313u }; - constexpr DeviceResourceHandle fakeOffscreenBuffer2{ 314u }; - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, true); - - // use some non-default clear flags - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer1, EClearFlags_Depth)); - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer2, EClearFlags_Depth)); - EXPECT_CALL(renderer, setClearColor(fakeOffscreenBuffer1, glm::vec4{ 1,2,3,4 })); - EXPECT_CALL(renderer, setClearColor(fakeOffscreenBuffer2, Renderer::DefaultClearColor)); - renderer.setClearFlags(fakeOffscreenBuffer1, EClearFlags_Depth); - renderer.setClearFlags(fakeOffscreenBuffer2, EClearFlags_Depth); - renderer.setClearColor(fakeOffscreenBuffer1, glm::vec4{ 1,2,3,4 }); - renderer.setClearColor(fakeOffscreenBuffer2, Renderer::DefaultClearColor); - - expectOffscreenBufferCleared(fakeOffscreenBuffer1, EClearFlags_Depth, glm::vec4{ 1,2,3,4 }); - expectFrameBufferRendered(); - expectOffscreenBufferCleared(fakeOffscreenBuffer2, EClearFlags_Depth); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, clearsOBOnRerenderIfNoShownSceneAssigned) -{ - createDisplayController(); - - constexpr DeviceResourceHandle fakeOffscreenBuffer1{ 313u }; - constexpr DeviceResourceHandle fakeOffscreenBuffer2{ 314u }; - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, true); - - // assign scene to trigger render of OB - constexpr SceneId sceneId1{ 12u }; - constexpr SceneId sceneId2{ 13u }; - createScene(sceneId1); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId2, 0, fakeOffscreenBuffer2); - - // use some non-default clear flags - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer1, EClearFlags_Depth)); - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer2, EClearFlags_Depth)); - renderer.setClearFlags(fakeOffscreenBuffer1, EClearFlags_Depth); - renderer.setClearFlags(fakeOffscreenBuffer2, EClearFlags_Depth); - - expectOffscreenBufferCleared(fakeOffscreenBuffer1, EClearFlags_Depth); - expectFrameBufferRendered(); - expectOffscreenBufferCleared(fakeOffscreenBuffer2, EClearFlags_Depth); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); - expectSwapBuffers(); - doOneRendererLoop(); - - unassignScene(sceneId1); - unassignScene(sceneId2); -} - -TEST_P(ARenderer, rendersTwoOffscreenBuffersWithContentInCorrectOrder) -{ - createDisplayController(); - - DeviceResourceHandle fakeOffscreenBuffer1(313u); - DeviceResourceHandle fakeOffscreenBuffer2(314u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, false); - - const SceneId sceneId1(12u); - const SceneId sceneId2(13u); - createScene(sceneId1); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId2, 0, fakeOffscreenBuffer2); - showScene(sceneId1); - showScene(sceneId2); - - expectSceneRendered(sceneId1, fakeOffscreenBuffer1, EDiscardDepth::Allowed); - expectSceneRendered(sceneId2, fakeOffscreenBuffer2, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - unassignScene(sceneId1); - unassignScene(sceneId2); -} - -TEST_P(ARenderer, assignSceneToFramebufferFromPreviouslyAssignedToOffscreenBuffer) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId); - unassignScene(sceneId); - expectOffscreenBufferCleared(fakeOffscreenBuffer); // will also clear OB after scene is removed from it - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - const DeviceResourceHandle framebuffer = DisplayControllerMock::FakeFrameBufferHandle; - assignSceneToDisplayBuffer(sceneId, 0, framebuffer); - showScene(sceneId); - EXPECT_EQ(framebuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneId); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, rendersScenesInOrderAccordingToLocalOrderWithinDisplayBuffer) -{ - createDisplayController(); - - const DeviceResourceHandle fakeOffscreenBuffer1(313u); - const DeviceResourceHandle fakeOffscreenBuffer2(314u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, false); - - const SceneId sceneId1(12u); - const SceneId sceneId2(13u); - const SceneId sceneId3(14u); - const SceneId sceneId4(15u); - createScene(sceneId1); - createScene(sceneId2); - createScene(sceneId3); - createScene(sceneId4); - assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId2, 1, fakeOffscreenBuffer2); - assignSceneToDisplayBuffer(sceneId3, 2, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId4, 3, fakeOffscreenBuffer2); - showScene(sceneId1); - showScene(sceneId2); - showScene(sceneId3); - showScene(sceneId4); - - { - InSequence s; - expectSceneRendered(sceneId1, fakeOffscreenBuffer1, EDiscardDepth::Disallowed); - expectSceneRendered(sceneId3, fakeOffscreenBuffer1, EDiscardDepth::Allowed); - } - { - InSequence s; - expectSceneRendered(sceneId2, fakeOffscreenBuffer2, EDiscardDepth::Disallowed); - expectSceneRendered(sceneId4, fakeOffscreenBuffer2, EDiscardDepth::Allowed); - } - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - hideScene(sceneId3); - hideScene(sceneId4); - unassignScene(sceneId1); - unassignScene(sceneId2); - unassignScene(sceneId3); - unassignScene(sceneId4); -} - -TEST_P(ARenderer, confidence_assignsSceneToOffscreenBufferAndReassignsToFramebuffer) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - EXPECT_EQ(fakeOffscreenBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); - - const DeviceResourceHandle framebuffer = DisplayControllerMock::FakeFrameBufferHandle; - assignSceneToDisplayBuffer(sceneId, 0, framebuffer); - EXPECT_EQ(framebuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); - EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, returnsInvalidDisplayWhenQueryingLocationOfUnmappedScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - unassignScene(sceneId); - EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); -} - -TEST_P(ARenderer, doesNotRenderAMappedScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, rendersMappedAndShownScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, rendersTwoMappedAndShownScenes) -{ - createDisplayController(); - - const SceneId sceneId1(12u); - createScene(sceneId1); - assignSceneToDisplayBuffer(sceneId1, 0); - showScene(sceneId1); - - const SceneId sceneId2(13u); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneId2, 0); - showScene(sceneId2); - - expectSceneRendered(sceneId1); - expectSceneRendered(sceneId2); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - unassignScene(sceneId1); - unassignScene(sceneId2); -} - -TEST_P(ARenderer, rendersTwoMappedAndShownScenesWithAscendingRenderOrder) -{ - createDisplayController(); - - const SceneId sceneId1(12u); - createScene(sceneId1); - assignSceneToDisplayBuffer(sceneId1, 1); - showScene(sceneId1); - - const SceneId sceneId2(13u); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneId2, 2); - showScene(sceneId2); - - { - InSequence seq; - expectSceneRendered(sceneId1); - expectSceneRendered(sceneId2); - } - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - unassignScene(sceneId1); - unassignScene(sceneId2); -} - -TEST_P(ARenderer, rendersTwoMappedAndShownScenesWithDescendingRenderOrder) -{ - createDisplayController(); - - const SceneId sceneId1(12u); - createScene(sceneId1); - assignSceneToDisplayBuffer(sceneId1, 2); - showScene(sceneId1); - - - const SceneId sceneId2(13u); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneId2, 1); - showScene(sceneId2); - - { - InSequence seq; - expectSceneRendered(sceneId2); - expectSceneRendered(sceneId1); - } - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - unassignScene(sceneId1); - unassignScene(sceneId2); -} - -TEST_P(ARenderer, doesNotRenderSceneThatWasNotMapped) -{ - createDisplayController(); - createScene(); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, doesNotRenderUnmappedScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - unassignScene(sceneId); - - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); -} - -TEST_P(ARenderer, skipsFrameIfDisplayControllerCanNotRenderNewFrame) -{ - createDisplayController(); - - EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()); - //mock that disp controller can not render new frame by returning false - EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).WillRepeatedly(Return(false)); - - //renderer must not try to render on that display - EXPECT_CALL(*renderer.m_displayController, swapBuffers()).Times(0); - EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).Times(0); - EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).Times(0); - - renderer.doOneRenderLoop(); -} - -TEST_P(ARenderer, canTakeASingleScreenshot_Framebuffer) -{ - createDisplayController(); - - scheduleScreenshot(DisplayControllerMock::FakeFrameBufferHandle, 20u, 30u, 100u, 100u); - - expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, 20u, 30u, 100u, 100u); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - auto screenshots = renderer.dispatchProcessedScreenshots(); - ASSERT_EQ(1u, screenshots.size()); - EXPECT_EQ(DisplayControllerMock::FakeFrameBufferHandle, screenshots.begin()->first); - - // check that screenshot request got deleted - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - screenshots = renderer.dispatchProcessedScreenshots(); - EXPECT_EQ(0u, screenshots.size()); -} - -TEST_P(ARenderer, canTakeASingleScreenshot_Offscreenbuffer) -{ - createDisplayController(); - const DeviceResourceHandle obDeviceHandle{ 567u }; - renderer.registerOffscreenBuffer(obDeviceHandle, 10u, 20u, false); - - scheduleScreenshot(obDeviceHandle, 1u, 2u, 3u, 4u); - - expectDisplayControllerReadPixels(obDeviceHandle, 1u, 2u, 3u, 4u); - expectOffscreenBufferCleared(obDeviceHandle); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - auto screenshots = renderer.dispatchProcessedScreenshots(); - ASSERT_EQ(1u, screenshots.size()); - EXPECT_EQ(obDeviceHandle, screenshots.begin()->first); - - // check that screenshot request got deleted - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - screenshots = renderer.dispatchProcessedScreenshots(); - EXPECT_EQ(0u, screenshots.size()); -} - -TEST_P(ARenderer, canTakeASingleScreenshot_InterruptibleOffscreenbuffer) -{ - createDisplayController(); - const DeviceResourceHandle obDeviceHandle{ 567u }; - renderer.registerOffscreenBuffer(obDeviceHandle, 10u, 20u, true); - - scheduleScreenshot(obDeviceHandle, 1u, 2u, 3u, 4u); - - expectDisplayControllerReadPixels(obDeviceHandle, 1u, 2u, 3u, 4u); - expectFrameBufferRendered(); - expectOffscreenBufferCleared(obDeviceHandle); - expectInterruptibleOffscreenBufferSwapped(obDeviceHandle); - expectSwapBuffers(); - doOneRendererLoop(); - - auto screenshots = renderer.dispatchProcessedScreenshots(); - ASSERT_EQ(1u, screenshots.size()); - EXPECT_EQ(obDeviceHandle, screenshots.begin()->first); - - // check that screenshot request got deleted - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - screenshots = renderer.dispatchProcessedScreenshots(); - EXPECT_EQ(0u, screenshots.size()); -} - -TEST_P(ARenderer, takeMultipleScreenshotsOfADisplayOverritesPreviousScreenshot) -{ - createDisplayController(); - - scheduleScreenshot(DisplayControllerMock::FakeFrameBufferHandle, 10u, 10u, 110u, 110u); - scheduleScreenshot(DisplayControllerMock::FakeFrameBufferHandle, 30u, 30u, 130u, 130u); - - expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, 30u, 30u, 130u, 130u); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - auto screenshots1 = renderer.dispatchProcessedScreenshots(); - ASSERT_EQ(1u, screenshots1.size()); - const auto& screenshots1FB = std::find_if(std::cbegin(screenshots1), std::cend(screenshots1), [&](const auto& p) {return p.first == DisplayControllerMock::FakeFrameBufferHandle; }); - ASSERT_NE(screenshots1.cend(), screenshots1FB); - - // check that screenshot request got deleted - EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - screenshots1 = renderer.dispatchProcessedScreenshots(); - ASSERT_EQ(0u, screenshots1.size()); -} - -TEST_P(ARenderer, marksRenderOncePassesAsRenderedAfterRenderingScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - auto& scene = rendererScenes.getScene(sceneId); - TestSceneHelper sceneHelper(scene); - const auto dataLayout = sceneHelper.m_sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I}, DataFieldInfo{EDataType::Vector2I} }, ResourceContentHash::Invalid()); - const CameraHandle camera = sceneHelper.m_sceneAllocator.allocateCamera(ECameraProjectionType::Orthographic, sceneHelper.m_sceneAllocator.allocateNode(), sceneHelper.m_sceneAllocator.allocateDataInstance(dataLayout)); - const RenderPassHandle pass = sceneHelper.m_sceneAllocator.allocateRenderPass(); - scene.setRenderPassCamera(pass, camera); - - // render - scene.updateRenderablesAndResourceCache(sceneHelper.resourceManager); - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // pass in list means it was rendered - const auto& passesToRender = scene.getSortedRenderingPasses(); - ASSERT_EQ(1u, passesToRender.size()); - EXPECT_EQ(pass, passesToRender[0].getRenderPassHandle()); - - // set as render once pass - // now this is expected to be rendered once only - scene.setRenderPassRenderOnce(pass, true); - - // render - scene.updateRenderablesAndResourceCache(sceneHelper.resourceManager); - expectSceneRendered(sceneId); - renderer.markBufferWithSceneForRerender(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // pass in list means it was rendered - ASSERT_EQ(1u, passesToRender.size()); - EXPECT_EQ(pass, passesToRender[0].getRenderPassHandle()); - - // render - scene.updateRenderablesAndResourceCache(sceneHelper.resourceManager); - expectSceneRendered(sceneId); - renderer.markBufferWithSceneForRerender(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // pass not in list means it was not rendered anymore - EXPECT_TRUE(passesToRender.empty()); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, doesNotClearAndRerenderIfNoChangeToScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no further expectations for scene render or clear - expectFrameBufferRendered(false); - doOneRendererLoop(); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, doesNotClearAndRerenderOffscreenBufferIfNoChangeToScene) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // no further expectations for scene render or clear - expectFrameBufferRendered(false); - doOneRendererLoop(); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderIfSceneMarkedAsChanged) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - // mark change - renderer.markBufferWithSceneForRerender(sceneId); - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - // mark change - renderer.markBufferWithSceneForRerender(sceneId); - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderOffscreenBufferIfSceneMarkedAsChanged) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - // mark change - renderer.markBufferWithSceneForRerender(sceneId); - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(false); - doOneRendererLoop(); // framebuffer not cleared/swapped as there is no scene assigned - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - // mark change - renderer.markBufferWithSceneForRerender(sceneId); - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(false); - doOneRendererLoop(); // framebuffer not cleared/swapped as there is no scene assigned - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderBothFramebufferAndOffscreenBufferIfSceneAssignedFromOneToTheOther) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // assign back to FB causes clear/render of both buffers - renderer.assignSceneToDisplayBuffer(sceneId, DisplayControllerMock::FakeFrameBufferHandle, 0); - expectOffscreenBufferCleared(fakeOffscreenBuffer); - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneId, DisplayControllerMock::FakeFrameBufferHandle); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // assign back to offscreen buffer causes clear/render of both buffers - renderer.assignSceneToDisplayBuffer(sceneId, fakeOffscreenBuffer, 0); - expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); - expectFrameBufferRendered(true); // framebuffer cleared/swapped - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderBufferIfSceneHidden) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // hiding scene causes clear of FB but no render as scene is hidden - hideScene(sceneId); - expectFrameBufferRendered(true); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderBufferIfClearColorChanged) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // setting clear color causes clear/render - renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, { 1, 2, 3, 4 }); - expectSceneRendered(sceneId, DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_All, { 1, 2, 3, 4 }); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderBothFramebufferAndOffscreenBufferIfOBClearColorChanges) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - - const glm::vec4 obClearColor1(.1f, .2f, .3f, .4f); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); - renderer.setClearColor(fakeOffscreenBuffer, obClearColor1); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectSceneRenderedExt(sceneId, fakeOffscreenBuffer, EClearFlags_All, obClearColor1, {}, {}, EClearFlags_All, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // change clear color - const glm::vec4 obClearColor2(.2f, .3f, .4f, .5f); - renderer.setClearColor(fakeOffscreenBuffer, obClearColor2); - expectFrameBufferRendered(true); - expectSceneRenderedExt(sceneId, fakeOffscreenBuffer, EClearFlags_All, obClearColor2, {}, {}, EClearFlags_All, EDiscardDepth::Allowed); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndRerenderBuffersIfExternallyOwnedWindowResized) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // external resizing causes clear/render - EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(1u, 2u)).WillOnce(Return(true)); - renderer.setExternallyOwnedWindowSize(1u, 2u); - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, doesNotClearAndRerenderBuffersIfExternallyOwnedWindowResizeFails) -{ - createDisplayController(); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0); - showScene(sceneId); - - expectSceneRendered(sceneId); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - // failed external resizing causes clear/render - EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(1u, 2u)).WillOnce(Return(false)); - renderer.setExternallyOwnedWindowSize(1u, 2u); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneId); - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndSwapInterruptibleOBOnlyOnceIfNoMoreShownScenes) -{ - createDisplayController(); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, true); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectFrameBufferRendered(); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - expectSceneRenderedWithInterruptionEnabled(sceneId, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin, EClearFlags_All); - expectSwapBuffers(); - doOneRendererLoop(); - - // re-render FB to reflect finished interruptible OB in previous frame - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // hide scene will trigger re-render and extra clear of OB - hideScene(sceneId); - expectFrameBufferRendered(false); - expectOffscreenBufferCleared(fakeOffscreenBuffer); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - // re-render FB to reflect finished interruptible OB in previous frame - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - unassignScene(sceneId); -} - -TEST_P(ARenderer, clearAndSwapInterruptibleOBOnlyOnceIfNoMoreMappedScenes) -{ - createDisplayController(); - - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, true); - - const SceneId sceneId(12u); - createScene(sceneId); - assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); - showScene(sceneId); - - expectFrameBufferRendered(); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - expectSceneRenderedWithInterruptionEnabled(sceneId, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); - expectSwapBuffers(); - doOneRendererLoop(); - - // re-render FB to reflect finished interruptible OB in previous frame - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // hide scene will trigger re-render of OB - hideScene(sceneId); - unassignScene(sceneId); - expectFrameBufferRendered(false); - expectOffscreenBufferCleared(fakeOffscreenBuffer); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - // re-render FB to reflect finished interruptible OB in previous frame - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change - expectFrameBufferRendered(false); - doOneRendererLoop(); - expectFrameBufferRendered(false); - doOneRendererLoop(); -} - -TEST_P(ARenderer, rendersScenesToFBAndOBWithNoInterruption) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneIdOB(13u); - createScene(sceneIdFB); - createScene(sceneIdOB); - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - - showScene(sceneIdFB); - showScene(sceneIdOB); - - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneIdFB); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneIdOB); - hideScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, doesNotSwapOffscreenBufferIfRenderingIntoItInterrupted) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdOB(13u); - createScene(sceneIdOB); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - showScene(sceneIdOB); - - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted, EClearFlags_All); // expect clear - - expectFrameBufferRendered(); - // expect no OB swap due to interruption - expectSwapBuffers(); - doOneRendererLoop(); - - renderer.resetRenderInterruptState(); - hideScene(sceneIdOB); - unassignScene(sceneIdOB); -} - -TEST_P(ARenderer, doesNotClearAndPassesPreviousStateWhenInterruptedAndSwapsAfterFinishing) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdOB(13u); - createScene(sceneIdOB); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - showScene(sceneIdOB); - - // start rendering and interrupt - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted, EClearFlags_All); // expect clear - expectFrameBufferRendered(); - // expect no OB swap due to interruption - expectSwapBuffers(); - doOneRendererLoop(); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // continue from interruption point and interrupt again - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderInterrupted2, EClearFlags_None); // no clear due to previous interruption - expectFrameBufferRendered(false); - // no OB swap due to interruption again - doOneRendererLoop(); - EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // continue from interruption point and finish - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted2, sceneRenderBegin, EClearFlags_None); // no clear due to previous interruption - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); // swap after finish - doOneRendererLoop(); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // re-render FB to reflect change happened to OB in previous frame - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneIdOB); - unassignScene(sceneIdOB); -} - -TEST_P(ARenderer, rendersScenesToFBEvenIfOBInterrupted) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneIdOB(13u); - createScene(sceneIdFB); - createScene(sceneIdOB); - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - - showScene(sceneIdFB); - showScene(sceneIdOB); - - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(false, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - // re-render FB to reflect change happened to OB in previous frame - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneIdOB); - hideScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, alwaysRendersScenesToFBWhenModifiedEvenIfOBInterrupted) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneIdOB(13u); - createScene(sceneIdFB); - createScene(sceneIdOB); - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - - showScene(sceneIdFB); - showScene(sceneIdOB); - - //FB rendered, OB interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - //modify FB scene - renderer.markBufferWithSceneForRerender(sceneIdFB); - //FB rendered, OB interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderInterrupted2, EClearFlags_None); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - //modify FB scene - renderer.markBufferWithSceneForRerender(sceneIdFB); - //FB rendered, OB finished - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted2, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - expectSwapBuffers(); - doOneRendererLoop(); - - // re-render FB to reflect change happened to OB in previous frame - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneIdOB); - hideScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, doesNotSkipFramesTillAllInterruptionsFinishedAndRendered) -{ - // 1 FB, 1 OB, 1 scene per OB - - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneIdOB(13u); - createScene(sceneIdFB); - createScene(sceneIdOB); - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - - showScene(sceneIdFB); - showScene(sceneIdOB); - - // FB rendered, OB interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // FB skipped, OB finished - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - // re-render FB to reflect change happened to OB in previous frame - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneIdOB); - hideScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleScenes) -{ - // 1 FB, 1 OB, 2 scenes per OB - - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneId1OB(13u); - const SceneId sceneId2OB(14u); - createScene(sceneIdFB); - createScene(sceneId1OB); - createScene(sceneId2OB); - assignSceneToDisplayBuffer(sceneId2OB, 1, fakeOffscreenBuffer); - assignSceneToDisplayBuffer(sceneId1OB, 0, fakeOffscreenBuffer); - assignSceneToDisplayBuffer(sceneIdFB, 0); - - showScene(sceneIdFB); - showScene(sceneId1OB); - showScene(sceneId2OB); - - // FB rendered, OB scene1 interrupted, OB scene2 skipped - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped, OB scene1 finished, OB scene2 interrupted - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted, EClearFlags_None); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped (as OB is not done rendering yet), OB scene1 skipped, OB scene2 finished - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // re-render FB to reflect change happened to OB scene 1 and scene 2 in previous frames - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId2OB); - hideScene(sceneId1OB); - hideScene(sceneIdFB); - unassignScene(sceneId2OB); - unassignScene(sceneId1OB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleScenes_FirstSceneNeverInterrupted) -{ - // 1 FB, 1 OB, 2 scenes per OB - - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneId1OB(13u); - const SceneId sceneId2OB(14u); - createScene(sceneIdFB); - createScene(sceneId1OB); - createScene(sceneId2OB); - assignSceneToDisplayBuffer(sceneId2OB, 1, fakeOffscreenBuffer); - assignSceneToDisplayBuffer(sceneId1OB, 0, fakeOffscreenBuffer); - assignSceneToDisplayBuffer(sceneIdFB, 0); - - showScene(sceneIdFB); - showScene(sceneId1OB); - showScene(sceneId2OB); - - // FB rendered, OB scene1 fully rendered, OB scene2 interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped, OB scene1 skipped, OB scene2 finished - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // re-render FB to reflect change happened to OB scene 1 and scene 2 in previous frames - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId2OB); - hideScene(sceneId1OB); - hideScene(sceneIdFB); - unassignScene(sceneId2OB); - unassignScene(sceneId1OB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleOffscreenBuffers) -{ - // 1 FB, 2 OB, 1 scene per OB - - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer1(313u); - const DeviceResourceHandle fakeOffscreenBuffer2(314u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 1u, true); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneIdOB1(13u); - const SceneId sceneIdOB2(14u); - createScene(sceneIdFB); - createScene(sceneIdOB1); - createScene(sceneIdOB2); - assignSceneToDisplayBuffer(sceneIdOB2, 0, fakeOffscreenBuffer2); - assignSceneToDisplayBuffer(sceneIdOB1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneIdFB, 0); - - showScene(sceneIdFB); - showScene(sceneIdOB1); - showScene(sceneIdOB2); - - // FB rendered, OB1 interrupted, OB2 skipped - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB1, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped, OB1 finished, OB2 interrupted - expectSceneRenderedWithInterruptionEnabled(sceneIdOB1, fakeOffscreenBuffer1, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB2, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB re-rendered to reflect changed in OB1, OB1 skipped, OB2 finished - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB2, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // re-render FB to reflect change happened to OB2 in previous frame - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneIdOB2); - hideScene(sceneIdOB1); - hideScene(sceneIdFB); - unassignScene(sceneIdOB2); - unassignScene(sceneIdOB1); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleOffscreenBuffersAndScenes) -{ - // 1 FB, 2 OB, 2 scene per OB - - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer1(313u); - const DeviceResourceHandle fakeOffscreenBuffer2(314u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 1u, true); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneId1OB1(13u); - const SceneId sceneId2OB1(14u); - const SceneId sceneId1OB2(15u); - const SceneId sceneId2OB2(16u); - createScene(sceneIdFB); - createScene(sceneId1OB1); - createScene(sceneId2OB1); - createScene(sceneId1OB2); - createScene(sceneId2OB2); - assignSceneToDisplayBuffer(sceneId1OB2, 0, fakeOffscreenBuffer2); - assignSceneToDisplayBuffer(sceneId2OB2, 1, fakeOffscreenBuffer2); - assignSceneToDisplayBuffer(sceneId1OB1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId2OB1, 1, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneIdFB, 0); - - showScene(sceneIdFB); - showScene(sceneId1OB1); - showScene(sceneId2OB1); - showScene(sceneId1OB2); - showScene(sceneId2OB2); - - // FB rendered, OB1 scene1 interrupted, rest skipped - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB1, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped, OB1 scene1 finished, OB1 scene2 interrupted, rest skipped - expectSceneRenderedWithInterruptionEnabled(sceneId1OB1, fakeOffscreenBuffer1, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB1, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderInterrupted, EClearFlags_None); - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped, OB1 scene1 skipped, OB1 scene2 finished, OB2 scene1 interrupted, rest skipped - expectSceneRenderedWithInterruptionEnabled(sceneId2OB1, fakeOffscreenBuffer1, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB2, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB re-rendered to reflect changes in OB1, OB1 skipped, OB2 scene1 finished, OB2 scene2 interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB2, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB2, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted, EClearFlags_None); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB skipped, OB1 skipped, OB2 scene2 finished - expectSceneRenderedWithInterruptionEnabled(sceneId2OB2, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB re-rendered to reflect changes in OB2, rest skipped - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId2OB2); - hideScene(sceneId1OB2); - hideScene(sceneId2OB1); - hideScene(sceneId1OB1); - hideScene(sceneIdFB); - unassignScene(sceneId2OB2); - unassignScene(sceneId1OB2); - unassignScene(sceneId2OB1); - unassignScene(sceneId1OB1); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, willRerenderSceneThatWasRenderedAndModifiedWhileOtherSceneInterrupted) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneId1OB(13u); - const SceneId sceneId2OB(14u); - createScene(sceneIdFB); - createScene(sceneId1OB); - createScene(sceneId2OB); - assignSceneToDisplayBuffer(sceneId2OB, 1, fakeOffscreenBuffer); - assignSceneToDisplayBuffer(sceneId1OB, 0, fakeOffscreenBuffer); - assignSceneToDisplayBuffer(sceneIdFB, 0); - - showScene(sceneIdFB); - showScene(sceneId1OB); - showScene(sceneId2OB); - - // FB rendered, OB scene1 fully rendered, OB scene2 interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // modify OB scene1 - renderer.markBufferWithSceneForRerender(sceneId1OB); - - // FB skipped, OB scene1 skipped, OB scene2 finished - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // re-render FB to reflect changes - // re-render OB scene1 (and OB scene2 as they share buffer) also as it was modified while interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // re-render FB one more time to reflect changes - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId2OB); - hideScene(sceneId1OB); - hideScene(sceneIdFB); - unassignScene(sceneId2OB); - unassignScene(sceneId1OB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, willRerenderSceneThatWasRenderedAndModifiedWhileOtherSceneOnAnotherInterruptibleOBInterrupted) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer1(313u); - const DeviceResourceHandle fakeOffscreenBuffer2(314u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 1u, true); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneId1OB(13u); - const SceneId sceneId2OB(14u); - createScene(sceneIdFB); - createScene(sceneId1OB); - createScene(sceneId2OB); - assignSceneToDisplayBuffer(sceneId2OB, 0, fakeOffscreenBuffer2); - assignSceneToDisplayBuffer(sceneId1OB, 1, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneIdFB, 0); - - showScene(sceneIdFB); - showScene(sceneId1OB); - showScene(sceneId2OB); - - // FB rendered, OB1 scene fully rendered, OB2 scene interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // modify OB1 scene - renderer.markBufferWithSceneForRerender(sceneId1OB); - - // FB re-rendered to reflect finished OB1, OB1 scene skipped due to interruption, OB2 scene finished - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB re-rendered to reflect finished OB2, OB1 scene re-rendered due to modification, OB2 scene skipped - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderBegin); - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB re-rendered once more to reflect changes from OB1 - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneId2OB); - hideScene(sceneId1OB); - hideScene(sceneIdFB); - unassignScene(sceneId2OB); - unassignScene(sceneId1OB); - unassignScene(sceneIdFB); -} - -TEST_P(ARenderer, willRenderAllScenesFromAllBuffersInOneFrameIfWithinBudget) -{ - // 1 OB, 2 interruptible OBs, 2 scenes per OB - - createDisplayController(); - DeviceResourceHandle disp1OB(313u); - DeviceResourceHandle disp1OBint1(315u); - DeviceResourceHandle disp1OBint2(316u); - renderer.registerOffscreenBuffer(disp1OB, 1u, 1u, false); - renderer.registerOffscreenBuffer(disp1OBint1, 1u, 1u, true); - renderer.registerOffscreenBuffer(disp1OBint2, 1u, 1u, true); - - const SceneId sceneIdDisp1FB(12u); - const SceneId sceneIdDisp1OBscene(14u); - const SceneId sceneIdDisp1OBint1scene1(16u); - const SceneId sceneIdDisp1OBint1scene2(17u); - const SceneId sceneIdDisp1OBint2scene1(18u); - const SceneId sceneIdDisp1OBint2scene2(19u); - - createScene(sceneIdDisp1FB); - createScene(sceneIdDisp1OBscene); - createScene(sceneIdDisp1OBint1scene1); - createScene(sceneIdDisp1OBint1scene2); - createScene(sceneIdDisp1OBint2scene1); - createScene(sceneIdDisp1OBint2scene2); - - assignSceneToDisplayBuffer(sceneIdDisp1FB, 0); - assignSceneToDisplayBuffer(sceneIdDisp1OBscene, 0, disp1OB); - assignSceneToDisplayBuffer(sceneIdDisp1OBint2scene1, 0, disp1OBint2); - assignSceneToDisplayBuffer(sceneIdDisp1OBint2scene2, 1, disp1OBint2); - assignSceneToDisplayBuffer(sceneIdDisp1OBint1scene1, 0, disp1OBint1); - assignSceneToDisplayBuffer(sceneIdDisp1OBint1scene2, 1, disp1OBint1); - - showScene(sceneIdDisp1FB); - showScene(sceneIdDisp1OBscene); - showScene(sceneIdDisp1OBint1scene1); - showScene(sceneIdDisp1OBint1scene2); - showScene(sceneIdDisp1OBint2scene1); - showScene(sceneIdDisp1OBint2scene2); - - // all rendered - expectSceneRendered(sceneIdDisp1OBscene, disp1OB, EDiscardDepth::Allowed); - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneIdDisp1FB); - - expectInterruptibleOffscreenBufferSwapped(disp1OBint1); - expectInterruptibleOffscreenBufferSwapped(disp1OBint2); - expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint1scene1, disp1OBint1, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint1scene2, disp1OBint1, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint2scene1, disp1OBint2, sceneRenderBegin, sceneRenderBegin); - expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint2scene2, disp1OBint2, sceneRenderBegin, sceneRenderBegin); - - expectSwapBuffers(); - - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // FB has to be re-rendered because there were some interruptible OBs finished last frame, - // interruptible OBs are rendered at end of frame so the FBs have to be rendered again next frame - // in order to use the latest state of the OBs wherever they are used in FBs - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneIdDisp1FB); - expectSwapBuffers(); - - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - Mock::VerifyAndClearExpectations(renderer.m_displayController); - - hideScene(sceneIdDisp1FB); - hideScene(sceneIdDisp1OBscene); - hideScene(sceneIdDisp1OBint1scene1); - hideScene(sceneIdDisp1OBint1scene2); - hideScene(sceneIdDisp1OBint2scene1); - hideScene(sceneIdDisp1OBint2scene2); - - unassignScene(sceneIdDisp1FB); - unassignScene(sceneIdDisp1OBscene); - unassignScene(sceneIdDisp1OBint1scene1); - unassignScene(sceneIdDisp1OBint1scene2); - unassignScene(sceneIdDisp1OBint2scene1); - unassignScene(sceneIdDisp1OBint2scene2); -} - -TEST_P(ARenderer, canMapSceneWhileThereIsInterruption) -{ - createDisplayController(); - const DeviceResourceHandle fakeOffscreenBuffer(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); - - const SceneId sceneIdFB(12u); - const SceneId sceneIdOB(13u); - const SceneId sceneId2(14u); - createScene(sceneIdFB); - createScene(sceneIdOB); - createScene(sceneId2); - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); - - showScene(sceneIdFB); - showScene(sceneIdOB); - - // FB rendered, OB interrupted - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // map other scene to FB while interrupted - assignSceneToDisplayBuffer(sceneId2, 0); - - // FB rendered because of new scene mapped, OB finished - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); - expectSwapBuffers(); - doOneRendererLoop(); - - // re-render FB to reflect change happened to OB in previous frame - expectSceneRendered(sceneIdFB); - expectFrameBufferRendered(true, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - // no change -> nothing rendered - expectFrameBufferRendered(false); - doOneRendererLoop(); - - hideScene(sceneIdOB); - hideScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdFB); - unassignScene(sceneId2); -} - -TEST_P(ARenderer, doesNotReportSceneIfNotRenderedToExpirationMonitor) -{ - createDisplayController(); - const SceneId sceneIdFB(1u); - const SceneId sceneIdOB(2u); - const SceneId sceneIdOBint(3u); - createScene(sceneIdFB); - createScene(sceneIdOB); - createScene(sceneIdOBint); - - initiateExpirationMonitoring({ sceneIdOB, sceneIdFB, sceneIdOBint }); - - DeviceResourceHandle ob(316u); - DeviceResourceHandle obInt(317u); - renderer.registerOffscreenBuffer(ob, 1u, 1u, false); - renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); - - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, ob); - assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); - - expectOffscreenBufferCleared(ob); - expectFrameBufferRendered(); - expectOffscreenBufferCleared(obInt); - expectInterruptibleOffscreenBufferSwapped(obInt); - expectSwapBuffers(); - doOneRendererLoop(); - - expectScenesReportedToExpirationMonitorAsRendered({}); - - unassignScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdOBint); - expirationMonitor.onDestroyed(sceneIdFB); - expirationMonitor.onDestroyed(sceneIdOB); - expirationMonitor.onDestroyed(sceneIdOBint); -} - -TEST_P(ARenderer, reportsSceneAsRenderedToExpirationMonitor) -{ - createDisplayController(); - const SceneId sceneIdFB(1u); - const SceneId sceneIdOB(2u); - const SceneId sceneIdOBint(3u); - createScene(sceneIdFB); - createScene(sceneIdOB); - createScene(sceneIdOBint); - - initiateExpirationMonitoring({ sceneIdOB, sceneIdFB, sceneIdOBint }); - - DeviceResourceHandle ob(316u); - DeviceResourceHandle obInt(317u); - renderer.registerOffscreenBuffer(ob, 1u, 1u, false); - renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); - - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOB, 0, ob); - assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); - - showScene(sceneIdFB); - showScene(sceneIdOB); - showScene(sceneIdOBint); - - expectFrameBufferRendered(true, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(obInt); - expectSceneRendered(sceneIdOB, ob, EDiscardDepth::Allowed); - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOBint, obInt, sceneRenderBegin, sceneRenderBegin); - expectSwapBuffers(); - doOneRendererLoop(); - - expectScenesReportedToExpirationMonitorAsRendered({ sceneIdOB, sceneIdFB, sceneIdOBint }); - - hideScene(sceneIdFB); - hideScene(sceneIdOB); - hideScene(sceneIdOBint); - unassignScene(sceneIdFB); - unassignScene(sceneIdOB); - unassignScene(sceneIdOBint); - expirationMonitor.onDestroyed(sceneIdFB); - expirationMonitor.onDestroyed(sceneIdOB); - expirationMonitor.onDestroyed(sceneIdOBint); -} - -TEST_P(ARenderer, reportsSceneAsRenderedToExpirationMonitorOnlyAfterFullyRenderedAndNotDuringInterruption) -{ - createDisplayController(); - const SceneId sceneIdFB(1u); - const SceneId sceneIdOBint(3u); - createScene(sceneIdFB); - createScene(sceneIdOBint); - - initiateExpirationMonitoring({ sceneIdFB, sceneIdOBint }); - - DeviceResourceHandle obInt(317u); - renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); - - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); - - showScene(sceneIdFB); - showScene(sceneIdOBint); - - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneIdFB); - expectSceneRenderedWithInterruptionEnabled(sceneIdOBint, obInt, sceneRenderBegin, sceneRenderInterrupted); - expectSwapBuffers(); - doOneRendererLoop(); - - // only FB scene reported as rendered, OB scene is interrupted - expectScenesReportedToExpirationMonitorAsRendered({ sceneIdFB }); - - expectFrameBufferRendered(false); - expectInterruptibleOffscreenBufferSwapped(obInt); - expectSceneRenderedWithInterruptionEnabled(sceneIdOBint, obInt, sceneRenderInterrupted, sceneRenderBegin, EClearFlags_None); - doOneRendererLoop(); - - // OB scene is reported now as it was fully rendered - expectScenesReportedToExpirationMonitorAsRendered({ sceneIdOBint }); - - hideScene(sceneIdFB); - hideScene(sceneIdOBint); - unassignScene(sceneIdFB); - unassignScene(sceneIdOBint); - expirationMonitor.onDestroyed(sceneIdFB); - expirationMonitor.onDestroyed(sceneIdOBint); -} - -TEST_P(ARenderer, pendingClearFlagPersistsAcrossScenes_FB) -{ - createDisplayController(); - constexpr SceneId scene1{ 1u }; - constexpr SceneId scene2{ 3u }; - createScene(scene1); - createScene(scene2); - - assignSceneToDisplayBuffer(scene1, 0); - assignSceneToDisplayBuffer(scene2, 0); - - showScene(scene1); - showScene(scene2); - - expectFrameBufferRendered(true, EClearFlags_None); - // simulate that the render executor cleared and reset pending clear in context - expectSceneRenderedExt(scene1, DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_All, Renderer::DefaultClearColor, {}, {}, EClearFlags_None, EDiscardDepth::Disallowed); - // next scene render will pass the modified flags - expectSceneRendered(scene2, DisplayControllerMock::FakeFrameBufferHandle, EClearFlags_None); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(scene1); - hideScene(scene2); - unassignScene(scene1); - unassignScene(scene2); -} - -TEST_P(ARenderer, pendingClearFlagPersistsAcrossScenes_OB) -{ - createDisplayController(); - constexpr SceneId scene1{ 1u }; - constexpr SceneId scene2{ 3u }; - createScene(scene1); - createScene(scene2); - - constexpr DeviceResourceHandle ob{ 317u }; - renderer.registerOffscreenBuffer(ob, 1u, 1u, false); - - assignSceneToDisplayBuffer(scene1, 0, ob); - assignSceneToDisplayBuffer(scene2, 0, ob); - - showScene(scene1); - showScene(scene2); - - expectFrameBufferRendered(); - // simulate that the render executor cleared and reset pending clear in context - expectSceneRenderedExt(scene1, ob, EClearFlags_All, Renderer::DefaultClearColor, {}, {}, EClearFlags_None, EDiscardDepth::Disallowed); - // next scene render will pass the modified flags - expectSceneRenderedExt(scene2, ob, EClearFlags_None, Renderer::DefaultClearColor, {}, {}, EClearFlags_None, EDiscardDepth::Allowed); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(scene1); - hideScene(scene2); - unassignScene(scene1); - unassignScene(scene2); -} - -TEST_P(ARenderer, pendingClearFlagPersistsAcrossScenes_OBinterruptible) -{ - createDisplayController(); - constexpr SceneId scene1{ 1u }; - constexpr SceneId scene2{ 3u }; - createScene(scene1); - createScene(scene2); - - constexpr DeviceResourceHandle ob{ 317u }; - renderer.registerOffscreenBuffer(ob, 1u, 1u, true); - - assignSceneToDisplayBuffer(scene1, 0, ob); - assignSceneToDisplayBuffer(scene2, 0, ob); - - showScene(scene1); - showScene(scene2); - - expectFrameBufferRendered(); - // simulate that the render executor cleared and reset pending clear in context - expectSceneRenderedExt(scene1, ob, EClearFlags_All, Renderer::DefaultClearColor, {}, {}, EClearFlags_None, EDiscardDepth::Disallowed, &renderer.FrameTimerInstance); - // next scene render will pass the modified flags - expectSceneRenderedWithInterruptionEnabled(scene2, ob, {}, {}, EClearFlags_None); - expectInterruptibleOffscreenBufferSwapped(ob); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(scene1); - hideScene(scene2); - unassignScene(scene1); - unassignScene(scene2); -} - -TEST_P(ARenderer, allowDepthDiscardOnlyForLastRenderedSceneToOffscreenbuffer) -{ - createDisplayController(); - - const DeviceResourceHandle fakeOffscreenBuffer1(313u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); - - const SceneId sceneId1(12u); - const SceneId sceneId2(13u); - const SceneId sceneId3(14u); - createScene(sceneId1); - createScene(sceneId2); - createScene(sceneId3); - assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId2, 1, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId3, 2, fakeOffscreenBuffer1); - showScene(sceneId1); - showScene(sceneId2); - // sceneId3 not shown - - expectSceneRendered(sceneId1, fakeOffscreenBuffer1, EDiscardDepth::Disallowed); - expectSceneRendered(sceneId2, fakeOffscreenBuffer1, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - unassignScene(sceneId1); - unassignScene(sceneId2); - unassignScene(sceneId3); -} - -TEST_P(ARenderer, allowDepthDiscardOnlyIfBothDepthAndStencilClearEnabled) -{ - createDisplayController(); - - const DeviceResourceHandle fakeOffscreenBuffer1(313u); - const DeviceResourceHandle fakeOffscreenBuffer2(314u); - const DeviceResourceHandle fakeOffscreenBuffer3(315u); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, false); - renderer.registerOffscreenBuffer(fakeOffscreenBuffer3, 1u, 2u, false); - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer1, EClearFlags_Color | EClearFlags_Stencil)); - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer2, EClearFlags_Color | EClearFlags_Depth)); - EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer3, EClearFlags_Depth | EClearFlags_Stencil)); - renderer.setClearFlags(fakeOffscreenBuffer1, EClearFlags_Color | EClearFlags_Stencil); - renderer.setClearFlags(fakeOffscreenBuffer2, EClearFlags_Color | EClearFlags_Depth); - renderer.setClearFlags(fakeOffscreenBuffer3, EClearFlags_Depth | EClearFlags_Stencil); - - const SceneId sceneId1(12u); - const SceneId sceneId2(13u); - const SceneId sceneId3(14u); - createScene(sceneId1); - createScene(sceneId2); - createScene(sceneId3); - assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); - assignSceneToDisplayBuffer(sceneId2, 0, fakeOffscreenBuffer2); - assignSceneToDisplayBuffer(sceneId3, 0, fakeOffscreenBuffer3); - showScene(sceneId1); - showScene(sceneId2); - showScene(sceneId3); - - expectSceneRenderedExt(sceneId1, fakeOffscreenBuffer1, EClearFlags_Color | EClearFlags_Stencil, Renderer::DefaultClearColor, {}, {}, 0u, EDiscardDepth::Disallowed); - expectSceneRenderedExt(sceneId2, fakeOffscreenBuffer2, EClearFlags_Color | EClearFlags_Depth, Renderer::DefaultClearColor, {}, {}, 0u, EDiscardDepth::Disallowed); - expectSceneRenderedExt(sceneId3, fakeOffscreenBuffer3, EClearFlags_Depth | EClearFlags_Stencil, Renderer::DefaultClearColor, {}, {}, 0u, EDiscardDepth::Allowed); - expectFrameBufferRendered(); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneId1); - hideScene(sceneId2); - hideScene(sceneId3); - unassignScene(sceneId1); - unassignScene(sceneId2); - unassignScene(sceneId3); -} - -TEST_P(ARenderer, neverAllowsDepthDiscardForFBOrInterruptibleOB) -{ - createDisplayController(); - const SceneId sceneIdFB(1u); - const SceneId sceneIdOBint(3u); - createScene(sceneIdFB); - createScene(sceneIdOBint); - - DeviceResourceHandle obInt(317u); - renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); - - assignSceneToDisplayBuffer(sceneIdFB, 0); - assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); - - showScene(sceneIdFB); - showScene(sceneIdOBint); - - expectFrameBufferRendered(true, EClearFlags_None); - expectSceneRendered(sceneIdFB, DisplayControllerMock::FakeFrameBufferHandle, EDiscardDepth::Disallowed); - expectSceneRenderedExt(sceneIdOBint, obInt, EClearFlags_All, Renderer::DefaultClearColor, {}, {}, 0u, EDiscardDepth::Disallowed, &renderer.FrameTimerInstance); - expectInterruptibleOffscreenBufferSwapped(obInt); - expectSwapBuffers(); - doOneRendererLoop(); - - hideScene(sceneIdFB); - hideScene(sceneIdOBint); - unassignScene(sceneIdFB); - unassignScene(sceneIdOBint); -} diff --git a/renderer/RendererLib/RendererLib/test/ResourceUploaderTest.cpp b/renderer/RendererLib/RendererLib/test/ResourceUploaderTest.cpp deleted file mode 100644 index aeef32920..000000000 --- a/renderer/RendererLib/RendererLib/test/ResourceUploaderTest.cpp +++ /dev/null @@ -1,584 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererLib/ResourceUploader.h" -#include "RendererAPI/IBinaryShaderCache.h" -#include "RenderBackendMock.h" -#include "ResourceMock.h" -#include "Resource/EffectResource.h" -#include "Resource/TextureResource.h" -#include "Resource/EffectResource.h" -#include "Resource/ArrayResource.h" -#include "RendererLib/ResourceDescriptor.h" -#include "Components/ResourceDeleterCallingCallback.h" -#include "Utils/ThreadBarrier.h" -#include "Utils/ThreadLocalLog.h" -#include - -using namespace ramses_internal; - -class BinaryShaderProviderMock : public IBinaryShaderCache -{ -public: - MOCK_METHOD(void, deviceSupportsBinaryShaderFormats, (const std::vector&), (override)); - MOCK_METHOD(bool, hasBinaryShader, (ResourceContentHash), (const, override)); - MOCK_METHOD(uint32_t, getBinaryShaderSize, (ResourceContentHash), (const, override)); - MOCK_METHOD(BinaryShaderFormatID, getBinaryShaderFormat, (ResourceContentHash), (const, override)); - MOCK_METHOD(bool, shouldBinaryShaderBeCached, (ResourceContentHash, SceneId), (const, override)); - MOCK_METHOD(void, getBinaryShaderData, (ResourceContentHash, uint8_t*, uint32_t), (const, override)); - MOCK_METHOD(void, storeBinaryShader, (ResourceContentHash, SceneId, const uint8_t*, uint32_t, BinaryShaderFormatID), (override)); - MOCK_METHOD(void, binaryShaderUploaded, (ResourceContentHash, bool), (const, override)); - MOCK_METHOD(std::once_flag&, binaryShaderFormatsReported, (), (override)); -}; - -class BinaryShaderProviderFake final : public StrictMock -{ -public: - BinaryShaderProviderFake() - : m_effectHash() - , m_binaryShaderData(nullptr) - , m_binaryShaderDataSize(0) - , m_binaryShaderFormat(0) - { - ON_CALL(*this, shouldBinaryShaderBeCached(_,_)).WillByDefault(Return(true)); - ON_CALL(*this, binaryShaderFormatsReported()).WillByDefault(ReturnRef(m_binaryShaderFormatReported)); - } - - [[nodiscard]] bool hasBinaryShader(ResourceContentHash effectHash) const override - { - BinaryShaderProviderMock::hasBinaryShader(effectHash); - if (effectHash == m_effectHash) - { - return true; - } - - return false; - } - - [[nodiscard]] uint32_t getBinaryShaderSize(ResourceContentHash effectHash) const override - { - BinaryShaderProviderMock::getBinaryShaderSize(effectHash); - if (effectHash == m_effectHash) - { - return m_binaryShaderDataSize; - } - - return 0; - } - - [[nodiscard]] BinaryShaderFormatID getBinaryShaderFormat(ResourceContentHash effectHash) const override - { - BinaryShaderProviderMock::getBinaryShaderFormat(effectHash); - return (effectHash == m_effectHash ? m_binaryShaderFormat : BinaryShaderFormatID::Invalid()); - } - - ResourceContentHash m_effectHash; - const uint8_t* m_binaryShaderData; - uint32_t m_binaryShaderDataSize; - BinaryShaderFormatID m_binaryShaderFormat; - std::once_flag m_binaryShaderFormatReported; -}; - -class AResourceUploader : public ::testing::Test -{ -public: - AResourceUploader() - : uploader(true) - , dummyManagedResourceCallback(managedResourceDeleter) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - } - - StrictMock renderer; - ResourceUploader uploader; - - StrictMock managedResourceDeleter; - ResourceDeleterCallingCallback dummyManagedResourceCallback; - uint32_t vramSize = 0; -}; - -TEST_F(AResourceUploader, uploadsVertexArrayResource) -{ - const ArrayResource res(EResourceType_VertexArray, 1, EDataType::Float, nullptr, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateVertexBuffer(res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadVertexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(res.getDecompressedDataSize(), vramSize); -} - -TEST_F(AResourceUploader, uploadsIndexArrayResource16) -{ - const ArrayResource res(EResourceType_IndexArray, 1, EDataType::UInt16, nullptr, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateIndexBuffer(res.getElementType(), res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadIndexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(res.getDecompressedDataSize(), vramSize); -} - -TEST_F(AResourceUploader, uploadsTexture2DResource) -{ - const uint32_t mipCount = 2u; - const TextureMetaInfo texDesc(2u, 2u, 1u, ETextureFormat::R8, false, DefaultTextureSwizzleArray, { 4, 1 }); - TextureResource res(EResourceType_Texture2D, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateTexture2D(2u, 2u, ETextureFormat::R8, DefaultTextureSwizzleArray, mipCount, 5u)).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 2u, 2u, 1u, _, _)); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 1u, 0u, 0u, 0u, 1u, 1u, 1u, _, _)); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(2u * 2 + 1, vramSize); -} - -TEST_F(AResourceUploader, uploadsTexture2DResourceWithNonDefaultTextureSwizzleArray) -{ - const uint32_t mipCount = 1u; - const TextureSwizzleArray swizzle {ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Green, ETextureChannelColor::Red}; - const TextureMetaInfo texDesc(2u, 2u, 1u, ETextureFormat::R8, false, swizzle, { 4 }); - TextureResource res(EResourceType_Texture2D, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateTexture2D(2u, 2u, ETextureFormat::R8, swizzle, mipCount, 4u)).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 2u, 2u, 1u, _, _)); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(2u * 2, vramSize); -} - -TEST_F(AResourceUploader, uploadsTexture2DResourceWithMipGen) -{ - const TextureMetaInfo texDesc(4u, 1u, 1u, ETextureFormat::R8, true, DefaultTextureSwizzleArray, { 4 }); - TextureResource res(EResourceType_Texture2D, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateTexture2D(4u, 1u, ETextureFormat::R8, DefaultTextureSwizzleArray, 3u, 7u)).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 4u, 1u, 1u, _, _)); - EXPECT_CALL(renderer.deviceMock, generateMipmaps(DeviceResourceHandle(123))); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(4u + 2 + 1, vramSize); -} - -TEST_F(AResourceUploader, uploadsTexture3DResource) -{ - const uint32_t mipCount = 2u; - const TextureMetaInfo texDesc(2u, 2u, 2u, ETextureFormat::R8, false, DefaultTextureSwizzleArray, { 8, 1 }); - TextureResource res(EResourceType_Texture3D, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateTexture3D(2u, 2u, 2u, ETextureFormat::R8, mipCount, 9u)).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 2u, 2u, 2u, _, _)); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 1u, 0u, 0u, 0u, 1u, 1u, 1u, _, _)); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(2u * 2 * 2 + 1, vramSize); -} - -TEST_F(AResourceUploader, uploadsTexture3DResourceWithMipGen) -{ - const TextureMetaInfo texDesc(4u, 1u, 2u, ETextureFormat::R8, true, DefaultTextureSwizzleArray, { 8 }); - TextureResource res(EResourceType_Texture3D, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateTexture3D(4u, 1u, 2u, ETextureFormat::R8, 3u, 11u)).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 4u, 1u, 2u, _, _)); - EXPECT_CALL(renderer.deviceMock, generateMipmaps(DeviceResourceHandle(123))); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(4u * 1 * 2 + 2 * 1 * 1 + 1, vramSize); -} - -TEST_F(AResourceUploader, uploadsTextureCubeResource) -{ - const uint32_t mipCount = 2u; - const TextureMetaInfo texDesc(2u, 1u, 1u, ETextureFormat::R8, false, DefaultTextureSwizzleArray, { 4, 1 }); - TextureResource res(EResourceType_TextureCube, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - InSequence seq; - EXPECT_CALL(renderer.deviceMock, allocateTextureCube(2u, ETextureFormat::R8, DefaultTextureSwizzleArray, mipCount, 6 * 5)).WillOnce(Return(DeviceResourceHandle(123))); - for (uint32_t i = 0u; i < 6u; ++i) - { - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, i, 2u, 2u, 1u, _, _)); - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 1u, 0u, 0u, i, 1u, 1u, 1u, _, _)); - } - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(6u * (2 * 2 + 1), vramSize); -} - -TEST_F(AResourceUploader, uploadsTextureCubeResourceWithMipGen) -{ - const TextureMetaInfo texDesc(4u, 1u, 1u, ETextureFormat::R8, true, DefaultTextureSwizzleArray, { 16 }); - TextureResource res(EResourceType_TextureCube, texDesc, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - InSequence seq; - EXPECT_CALL(renderer.deviceMock, allocateTextureCube(4u, ETextureFormat::R8, DefaultTextureSwizzleArray, 3u, 6 * (16 + 4 + 1))).WillOnce(Return(DeviceResourceHandle(123))); - for (uint32_t i = 0u; i < 6u; ++i) - { - EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, i, 4u, 4u, 1u, _, _)); - } - EXPECT_CALL(renderer.deviceMock, generateMipmaps(DeviceResourceHandle(123))); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - EXPECT_EQ(6u * (4 * 4 + 2 * 2 + 1), vramSize); -} - -TEST_F(AResourceUploader, canStoreBinaryShader) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - const SceneId sceneUsingResource(14); - - BinaryShaderProviderFake binaryShaderProvider; - EXPECT_CALL(binaryShaderProvider, shouldBinaryShaderBeCached(res.getHash(), _)).WillOnce(Return(true)); - EXPECT_CALL(renderer.deviceMock, getBinaryShader(DeviceMock::FakeShaderDeviceHandle, _, _)).WillOnce(DoAll(SetArgReferee<1>(UInt8Vector(10)), Return(true))); - EXPECT_CALL(binaryShaderProvider, storeBinaryShader(_, sceneUsingResource, _, _, _)); - - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - uploaderWithBinaryProvider.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); -} - -TEST_F(AResourceUploader, doesNoStoreBinaryShaderIfFailedToGetBinaryShaderFromDevice) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - const SceneId sceneUsingResource(14); - - BinaryShaderProviderFake binaryShaderProvider; - - EXPECT_CALL(binaryShaderProvider, shouldBinaryShaderBeCached(res.getHash(), _)).WillOnce(Return(true)); - EXPECT_CALL(renderer.deviceMock, getBinaryShader(DeviceMock::FakeShaderDeviceHandle, _, _)).WillOnce(Return(false)); - EXPECT_CALL(binaryShaderProvider, storeBinaryShader(_, sceneUsingResource, _, _, _)).Times(0); - - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - uploaderWithBinaryProvider.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); -} - -TEST_F(AResourceUploader, ifShaderShouldNotBeCachedNoDownloadWillHappen) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - const SceneId sceneUsingResource(14); - - BinaryShaderProviderFake binaryShaderProvider; - EXPECT_CALL(binaryShaderProvider, shouldBinaryShaderBeCached(res.getHash(), _)).WillOnce(Return(false)); - EXPECT_CALL(renderer.deviceMock, getBinaryShader(DeviceMock::FakeShaderDeviceHandle, _, _)).Times(0); - EXPECT_CALL(binaryShaderProvider, storeBinaryShader(_, sceneUsingResource, _, _, _)).Times(0); - - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - uploaderWithBinaryProvider.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); -} - -TEST_F(AResourceUploader, doesNotTryToStoreBinaryShaderIfNoBinaryShaderCacheAvailable) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - const SceneId sceneUsingResource(14); - - EXPECT_CALL(renderer.deviceMock, getBinaryShader(_, _, _)).Times(0); - uploader.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); -} - -TEST_F(AResourceUploader, doesNotReturnDeviceHandleForEffectResourceWithoutBinaryShaderCache) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); - EXPECT_FALSE(uploader.uploadResource(renderer, resourceObject, vramSize).has_value()); -} - -TEST_F(AResourceUploader, uploadsShaderWithoutBinaryShaderCacheIfAsyncUploadDisabled) -{ - ResourceUploader uploaderWithoutCacheOrAsyncUpload(false); - - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); - EXPECT_CALL(renderer.deviceMock, uploadShader(_)); - EXPECT_CALL(renderer.deviceMock, registerShader(_)); - EXPECT_EQ(DeviceMock::FakeShaderDeviceHandle, uploaderWithoutCacheOrAsyncUpload.uploadResource(renderer, resourceObject, vramSize)); -} - -TEST_F(AResourceUploader, doesNotReturnDeviceHandleIfShaderIsNotCached) -{ - BinaryShaderProviderFake binaryShaderProvider; - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); - - EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); - EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); - EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())).WillOnce(Return(false)); - - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject, vramSize).has_value()); -} - -TEST_F(AResourceUploader, uploadShaderIfShaderIsNotCachedAndAsyncUploadDisabled) -{ - BinaryShaderProviderFake binaryShaderProvider; - ResourceUploader uploaderWithCacheButNoAsyncUpload(false, &binaryShaderProvider); - - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); - - EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); - EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); - EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())).WillOnce(Return(false)); - - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(renderer.deviceMock, uploadShader(_)); - EXPECT_CALL(renderer.deviceMock, registerShader(_)); - EXPECT_EQ(DeviceMock::FakeShaderDeviceHandle, uploaderWithCacheButNoAsyncUpload.uploadResource(renderer, resourceObject, vramSize)); -} - -TEST_F(AResourceUploader, uploadsEffectResourceFromBinaryShaderCacheWhenCacheHit) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - - uint8_t binaryShaderData[] = { 1u, 2u, 3u, 4u }; - - BinaryShaderProviderFake binaryShaderProvider; - binaryShaderProvider.m_effectHash = res.getHash(); - binaryShaderProvider.m_binaryShaderData = binaryShaderData; - binaryShaderProvider.m_binaryShaderDataSize = sizeof(binaryShaderData) / sizeof(uint8_t); - binaryShaderProvider.m_binaryShaderFormat = BinaryShaderFormatID{ 12u }; - - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); - - EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); - EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); - EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())); - EXPECT_CALL(binaryShaderProvider, getBinaryShaderSize(res.getHash())); - EXPECT_CALL(binaryShaderProvider, getBinaryShaderFormat(res.getHash())); - EXPECT_CALL(binaryShaderProvider, getBinaryShaderData(res.getHash(), _, _)); - EXPECT_CALL(binaryShaderProvider, binaryShaderUploaded(_, true)).Times(1); - - EXPECT_CALL(renderer.deviceMock, uploadBinaryShader(_, _, _, _)).WillOnce(Return(DeviceResourceHandle(123))); - - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_EQ(123u, uploaderWithBinaryProvider.uploadResource(renderer, resourceObject, vramSize)); -} - -TEST_F(AResourceUploader, doesNotReturnDeviceHandleForEffectResourceWithBrokenBinaryShaderCache) -{ - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - SceneId sceneUsingResource(14); - - uint8_t binaryShaderData[] = { 1u, 2u, 3u, 4u }; - - BinaryShaderProviderFake binaryShaderProvider; - binaryShaderProvider.m_effectHash = res.getHash(); - binaryShaderProvider.m_binaryShaderData = binaryShaderData; - binaryShaderProvider.m_binaryShaderDataSize = sizeof(binaryShaderData) / sizeof(uint8_t); - binaryShaderProvider.m_binaryShaderFormat = BinaryShaderFormatID{ 12u }; - - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); - - // Set up so it looks like there is a valid binary shader cache - EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); - EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); - EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())); - EXPECT_CALL(binaryShaderProvider, getBinaryShaderSize(res.getHash())); - EXPECT_CALL(binaryShaderProvider, getBinaryShaderFormat(res.getHash())); - EXPECT_CALL(binaryShaderProvider, getBinaryShaderData(res.getHash(), _, _)); - - // Make the upload from cache fail and expect the callback telling that the cache was broken - EXPECT_CALL(renderer.deviceMock, uploadBinaryShader(_, _, _, _)).WillOnce(Return(DeviceResourceHandle::Invalid())); - EXPECT_CALL(binaryShaderProvider, binaryShaderUploaded(_, false)).Times(1); - - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - resourceObject.sceneUsage = { sceneUsingResource }; - EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject, vramSize).has_value()); -} - -TEST_F(AResourceUploader, providesSupportedBinaryShaderFormatsOnlyOnce) -{ - BinaryShaderProviderFake binaryShaderProvider; - ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); - - EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(3); - - EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()).Times(3); - EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })).Times(1); - - EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())).Times(3); - - ResourceDescriptor resourceObject1; - resourceObject1.resource = ManagedResource{ &res, dummyManagedResourceCallback }; - resourceObject1.sceneUsage = { SceneId{1u} }; - ResourceDescriptor resourceObject2 = resourceObject1; - resourceObject2.resource = ManagedResource{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject3 = resourceObject1; - resourceObject3.resource = ManagedResource{ &res, dummyManagedResourceCallback }; - - EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject1, vramSize).has_value()); - EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject2, vramSize).has_value()); - EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject3, vramSize).has_value()); -} - -TEST_F(AResourceUploader, providesSupportedBinaryShaderFormatsOnlyOnceFromMultipleThreadsBeforeFirstHasBinaryShader) -{ - BinaryShaderProviderFake binaryShaderProvider; - - ResourceUploader uploaderWithBinaryProvider1(true, &binaryShaderProvider); - ResourceUploader uploaderWithBinaryProvider2(true, &binaryShaderProvider); - ResourceUploader uploaderWithBinaryProvider3(true, &binaryShaderProvider); - - EffectResource res1("1", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - EffectResource res2("2", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - EffectResource res3("3", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(3); - EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()).Times(3); - - { - InSequence inseq; // it would be wrong if any hasBinaryShader from "the other two" threads get called before deviceSupportsBinaryShaderFormats - EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })).Times(1).WillOnce( - [](auto) { std::this_thread::sleep_for(std::chrono::milliseconds(2)); }); - EXPECT_CALL(binaryShaderProvider, hasBinaryShader(_)).Times(3); - } - - ResourceDescriptor resourceObject1; - ResourceDeleterCallingCallback dummyManagedResourceCallback1(managedResourceDeleter); - resourceObject1.resource = ManagedResource{ &res1, dummyManagedResourceCallback1 }; - resourceObject1.sceneUsage = { SceneId{1u} }; - ResourceDescriptor resourceObject2; - ResourceDeleterCallingCallback dummyManagedResourceCallback2(managedResourceDeleter); - resourceObject2.resource = ManagedResource{ &res2, dummyManagedResourceCallback2 }; - resourceObject2.sceneUsage = { SceneId{2u} }; - ResourceDescriptor resourceObject3; - ResourceDeleterCallingCallback dummyManagedResourceCallback3(managedResourceDeleter); - resourceObject3.resource = ManagedResource{ &res3, dummyManagedResourceCallback3 }; - resourceObject3.sceneUsage = { SceneId{3u} }; - - ThreadBarrier startBarrier(3); - std::thread t1([&]() { - ThreadLocalLog::SetPrefix(2); - startBarrier.wait(); - uint32_t ramSize = 0; - EXPECT_FALSE(uploaderWithBinaryProvider1.uploadResource(renderer, resourceObject1, ramSize).has_value()); - }); - std::thread t2([&]() { - ThreadLocalLog::SetPrefix(3); - startBarrier.wait(); - uint32_t ramSize = 0; - EXPECT_FALSE(uploaderWithBinaryProvider2.uploadResource(renderer, resourceObject2, ramSize).has_value()); - }); - std::thread t3([&]() { - ThreadLocalLog::SetPrefix(4); - startBarrier.wait(); - uint32_t ramSize = 0; - EXPECT_FALSE(uploaderWithBinaryProvider3.uploadResource(renderer, resourceObject3, ramSize).has_value()); - }); - - t1.join(); - t2.join(); - t3.join(); -} - - -TEST_F(AResourceUploader, unloadsVertexArrayResource) -{ - const DeviceResourceHandle handle(123u); - EXPECT_CALL(renderer.deviceMock, deleteVertexBuffer(handle)); - uploader.unloadResource(renderer, EResourceType_VertexArray, ResourceContentHash(456u, 0), handle); -} - -TEST_F(AResourceUploader, unloadsIndexArrayResource) -{ - const DeviceResourceHandle handle(123u); - EXPECT_CALL(renderer.deviceMock, deleteIndexBuffer(handle)); - uploader.unloadResource(renderer, EResourceType_IndexArray, ResourceContentHash(456u, 0), handle); -} - -TEST_F(AResourceUploader, unloadsTexture2DResource) -{ - const DeviceResourceHandle handle(123u); - EXPECT_CALL(renderer.deviceMock, deleteTexture(handle)); - uploader.unloadResource(renderer, EResourceType_Texture2D, ResourceContentHash(456u, 0), handle); -} - -TEST_F(AResourceUploader, unloadsTexture3DResource) -{ - const DeviceResourceHandle handle(123u); - EXPECT_CALL(renderer.deviceMock, deleteTexture(handle)); - uploader.unloadResource(renderer, EResourceType_Texture3D, ResourceContentHash(456u, 0), handle); -} - -TEST_F(AResourceUploader, unloadsTextureCubeResource) -{ - const DeviceResourceHandle handle(123u); - EXPECT_CALL(renderer.deviceMock, deleteTexture(handle)); - uploader.unloadResource(renderer, EResourceType_TextureCube, ResourceContentHash(456u, 0), handle); -} - -TEST_F(AResourceUploader, unloadsEffectResource) -{ - const DeviceResourceHandle handle(123u); - EXPECT_CALL(renderer.deviceMock, deleteShader(handle)); - uploader.unloadResource(renderer, EResourceType_Effect, ResourceContentHash(456u, 0), handle); -} - -TEST_F(AResourceUploader, uploadsWithMultipleRenderBackends) -{ - StrictMock additionalRenderer; - - const ArrayResource res(EResourceType_VertexArray, 1, EDataType::Float, nullptr, ResourceCacheFlag_DoNotCache, {}); - ManagedResource managedRes{ &res, dummyManagedResourceCallback }; - ResourceDescriptor resourceObject; - resourceObject.resource = managedRes; - EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); - - EXPECT_CALL(renderer.deviceMock, allocateVertexBuffer(res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(renderer.deviceMock, uploadVertexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); - EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); - - EXPECT_CALL(additionalRenderer.deviceMock, allocateVertexBuffer(res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); - EXPECT_CALL(additionalRenderer.deviceMock, uploadVertexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); - EXPECT_EQ(123u, uploader.uploadResource(additionalRenderer, resourceObject, vramSize)); -} diff --git a/renderer/RendererLib/RendererLib/test/ResourceUploadingManagerTest.cpp b/renderer/RendererLib/RendererLib/test/ResourceUploadingManagerTest.cpp deleted file mode 100644 index e98de0c11..000000000 --- a/renderer/RendererLib/RendererLib/test/ResourceUploadingManagerTest.cpp +++ /dev/null @@ -1,890 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererLib/ResourceUploadingManager.h" -#include "RendererLib/RendererResourceRegistry.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererLib/DisplayConfig.h" -#include "Resource/ArrayResource.h" -#include "Resource/EffectResource.h" -#include "ResourceUploaderMock.h" -#include "ResourceMock.h" -#include "PlatformMock.h" -#include "MockResourceHash.h" -#include "Components/ResourceDeleterCallingCallback.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Watchdog/ThreadAliveNotifierMock.h" -#include "Utils/ThreadLocalLog.h" - - -namespace ramses_internal -{ - -namespace -{ - DisplayConfig makeConfig(bool keepEffects, uint64_t resourceCacheSize, SceneId preferredScene, SceneId deprivedScene) - { - DisplayConfig cfg; - cfg.setKeepEffectsUploaded(keepEffects); - cfg.setGPUMemoryCacheSize(resourceCacheSize); - if (preferredScene.isValid()) - { - cfg.setScenePriority(preferredScene, -1); - } - if (deprivedScene.isValid()) - { - cfg.setScenePriority(deprivedScene, 12); - } - return cfg; - } -} - -class AResourceUploadingManager : public ::testing::Test -{ -public: - explicit AResourceUploadingManager(const DisplayConfig& cfg = DisplayConfig()) - : dummyResource(EResourceType_IndexArray, 5, EDataType::UInt16, reinterpret_cast(m_dummyData), ResourceCacheFlag_DoNotCache, {}) - , dummyEffectResource("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache) - , dummyManagedResourceCallback(managedResourceDeleter) - , sceneId(66u) - , uploader(new StrictMock) - , asyncEffectUploader(platformMock, platformMock.renderBackendMock, notifier, 1) - , rendererResourceUploader(resourceRegistry, std::unique_ptr{ uploader }, platformMock.renderBackendMock, asyncEffectUploader, cfg, frameTimer, stats) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - InSequence s; - EXPECT_CALL(platformMock.renderBackendMock.contextMock, disable()).WillOnce(Return(true)); - EXPECT_CALL(platformMock, createResourceUploadRenderBackend()); - EXPECT_CALL(platformMock.renderBackendMock.contextMock, enable()).WillOnce(Return(true)); - const bool status = asyncEffectUploader.createResourceUploadRenderBackendAndStartThread(); - EXPECT_TRUE(status); - } - - ~AResourceUploadingManager() override - { - EXPECT_CALL(platformMock, destroyResourceUploadRenderBackend()); - asyncEffectUploader.destroyResourceUploadRenderBackendAndStopThread(); - } - - void registerAndProvideResource(ResourceContentHash hash, bool effectResource = false, const IResource* resource = nullptr, SceneId id = {}) - { - resourceRegistry.registerResource(hash); - resourceRegistry.addResourceRef(hash, id.isValid() ? id : sceneId); - - if (!resource) - resource = (effectResource ? static_cast(&dummyEffectResource) : &dummyResource); - resourceRegistry.setResourceData(hash, ManagedResource{ resource, dummyManagedResourceCallback }); - - ASSERT_TRUE(contains_c(resourceRegistry.getAllProvidedResources(), hash)); - } - - void makeResourceUnused(ResourceContentHash hash, SceneId id = {}) - { - resourceRegistry.removeResourceRef(hash, id.isValid() ? id : sceneId); - if (resourceRegistry.containsResource(hash)) - { - ASSERT_TRUE(contains_c(resourceRegistry.getAllResourcesNotInUseByScenes(), hash)); - } - } - - void expectResourceUploaded(ResourceContentHash hash, DeviceResourceHandle deviceHandle = ResourceUploaderMock::FakeResourceDeviceHandle) - { - ASSERT_TRUE(resourceRegistry.containsResource(hash)); - const ResourceDescriptor& rd = resourceRegistry.getResourceDescriptor(hash); - EXPECT_EQ(EResourceStatus::Uploaded, rd.status); - EXPECT_EQ(deviceHandle, rd.deviceHandle); - EXPECT_FALSE(rd.resource); - } - - void expectResourceUploadFailed(ResourceContentHash hash) - { - ASSERT_TRUE(resourceRegistry.containsResource(hash)); - const ResourceDescriptor& rd = resourceRegistry.getResourceDescriptor(hash); - EXPECT_EQ(EResourceStatus::Broken, rd.status); - EXPECT_FALSE(rd.deviceHandle.isValid()); - EXPECT_FALSE(rd.resource); - } - - void expectResourceStatus(ResourceContentHash hash, EResourceStatus status) - { - ASSERT_TRUE(resourceRegistry.containsResource(hash)); - const ResourceDescriptor& rd = resourceRegistry.getResourceDescriptor(hash); - EXPECT_EQ(status, rd.status); - } - - void expectResourceUnloaded(ResourceContentHash hash) - { - EXPECT_FALSE(resourceRegistry.containsResource(hash)); - } - - void expectDeviceFlushOnWindows() - { -#if defined(_WIN32) - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, flush()); -#endif - } - - void uploadShader(const ResourceContentHash& hash, bool expectSuccess = true) - { - const std::optional unsetDeviceHandle; - EXPECT_CALL(*uploader, uploadResource(_, _, _)).WillOnce(Return(unsetDeviceHandle)); - - if (expectSuccess) - { - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platformMock.renderBackendMock.deviceMock, registerShader(_)); - EXPECT_CALL(*uploader, storeShaderInBinaryShaderCache(Ref(platformMock.renderBackendMock), DeviceMock::FakeShaderDeviceHandle, hash, sceneId)); - } - else - { - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([](const auto&) {return std::move(std::unique_ptr{}); })); - expectDeviceFlushOnWindows(); - EXPECT_CALL(platformMock.renderBackendMock.deviceMock, registerShader(_)).Times(0); - EXPECT_CALL(*uploader, storeShaderInBinaryShaderCache(_, _, _, _)).Times(0); - } - - rendererResourceUploader.uploadAndUnloadPendingResources(); - ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceRegistry.getResourceStatus(hash)); - - constexpr std::chrono::seconds timeoutTime{ 2u }; - const auto startTime = std::chrono::steady_clock::now(); - while (resourceRegistry.getResourceStatus(hash) == EResourceStatus::ScheduledForUpload - && std::chrono::steady_clock::now() - startTime < timeoutTime) - { - std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); - rendererResourceUploader.uploadAndUnloadPendingResources(); - } - } - -protected: - static const uint16_t m_dummyData[5]; - - RendererResourceRegistry resourceRegistry; - StrictMock platformMock; - - const ArrayResource dummyResource; - const EffectResource dummyEffectResource; - NiceMock managedResourceDeleter; - ResourceDeleterCallingCallback dummyManagedResourceCallback; - - const SceneId sceneId; - - FrameTimer frameTimer; - RendererStatistics stats; - NiceMock notifier; - StrictMock* uploader; - AsyncEffectUploader asyncEffectUploader; - ResourceUploadingManager rendererResourceUploader; -}; - -const uint16_t AResourceUploadingManager::m_dummyData[5] = { 0x1C }; - -class AResourceUploadingManager_KeepingEffects : public AResourceUploadingManager -{ -public: - AResourceUploadingManager_KeepingEffects() - : AResourceUploadingManager(makeConfig(true, 0u, {}, {})) - { - } -}; - -class AResourceUploadingManager_WithVRAMCache : public AResourceUploadingManager -{ -public: - AResourceUploadingManager_WithVRAMCache() - : AResourceUploadingManager(makeConfig(false, 30u, {}, {})) - { - } -}; - -class AResourceUploadingManager_ScenePriority : public AResourceUploadingManager -{ -public: - AResourceUploadingManager_ScenePriority() - : AResourceUploadingManager(makeConfig(false, 0u, getPreferredScene(), getDeprivedScene())) - { - } - - static SceneId getPreferredScene() - { - return SceneId{113114}; - } - - static SceneId getDeprivedScene() - { - return SceneId{551122}; - } -}; - -TEST_F(AResourceUploadingManager, hasNothingToUploadUnloadInitially) -{ - EXPECT_FALSE(rendererResourceUploader.hasAnythingToUpload()); - // no call expectations - rendererResourceUploader.uploadAndUnloadPendingResources(); -} - -TEST_F(AResourceUploadingManager, reportsItemsToUploadWhenRegistryHasProvidedResource) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res); - - EXPECT_TRUE(rendererResourceUploader.hasAnythingToUpload()); - - makeResourceUnused(res); -} - -TEST_F(AResourceUploadingManager, reportsItemsToUploadWhenRegistryHasScheduledForUploadResource) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res); - resourceRegistry.setResourceScheduledForUpload(res); - EXPECT_TRUE(resourceRegistry.hasAnyResourcesScheduledForUpload()); - EXPECT_EQ(EResourceStatus::ScheduledForUpload, resourceRegistry.getResourceStatus(res)); - - EXPECT_TRUE(rendererResourceUploader.hasAnythingToUpload()); - - resourceRegistry.setResourceBroken(res); - makeResourceUnused(res); -} - -TEST_F(AResourceUploadingManager, reportsNothingToUploadWhenProvidedResourceUnused) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res); - makeResourceUnused(res); - - EXPECT_FALSE(rendererResourceUploader.hasAnythingToUpload()); -} - -TEST_F(AResourceUploadingManager, uploadsProvidedResource) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)); - makeResourceUnused(res); -} - -TEST_F(AResourceUploadingManager, unloadsUnusedResource) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res); - - makeResourceUnused(res); - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUnloaded(res); -} - -TEST_F(AResourceUploadingManager, setsBrokenStatusForResourceFailedToUpload) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)).WillOnce(Return(DeviceResourceHandle::Invalid())); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploadFailed(res); - - makeResourceUnused(res); -} - -TEST_F(AResourceUploadingManager, uploadEffectAndStoreInBinaryShaderCacheIfResourceUploaderDidNotReturnDeviceHandle) -{ - const auto resHash = dummyEffectResource.getHash(); - registerAndProvideResource(resHash, true); - - uploadShader(resHash); - expectResourceUploaded(resHash, DeviceMock::FakeShaderDeviceHandle); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)); - makeResourceUnused(resHash); -} - -TEST_F(AResourceUploadingManager, setsBrokenStatusForEffectIfUploadFailed) -{ - const auto resHash = dummyEffectResource.getHash(); - registerAndProvideResource(resHash, true); - - uploadShader(resHash, false); - expectResourceUploadFailed(resHash); - - makeResourceUnused(resHash); -} - -TEST_F(AResourceUploadingManager, uploadsAllProvidedResourcesInOneUpdate_defaultUploadStrategy) -{ - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - const ResourceContentHash res5(1238u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2); - registerAndProvideResource(res3); - registerAndProvideResource(res4); - registerAndProvideResource(res5); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(5u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - expectResourceUploaded(res1); - expectResourceUploaded(res2); - expectResourceUploaded(res3); - expectResourceUploaded(res4); - expectResourceUploaded(res5); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - makeResourceUnused(res4); - makeResourceUnused(res5); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(5); -} - -TEST_F(AResourceUploadingManager, willUnloadAnyRemainingResourcesWhenDestructed) -{ - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2, true); - registerAndProvideResource(res3); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(3u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - Mock::VerifyAndClearExpectations(&uploader); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); -} - -TEST_F(AResourceUploadingManager_KeepingEffects, willNotUnloadEffectResourceUntilDestructor) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res, true); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res); - - makeResourceUnused(res); - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res); - Mock::VerifyAndClearExpectations(&uploader); - - // destructor will unload kept effects - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); -} - -TEST_F(AResourceUploadingManager, uploadsAtLeastOneResourcePerUpdateIfOutOfTimeBudget) -{ - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - - registerAndProvideResource(res1, false); - registerAndProvideResource(res2, false); - registerAndProvideResource(res3, false); - - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, 0u); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res1); - expectResourceStatus(res2, EResourceStatus::Provided); - expectResourceStatus(res3, EResourceStatus::Provided); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res1); - expectResourceUploaded(res2); - expectResourceStatus(res3, EResourceStatus::Provided); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(2u); -} - -TEST_F(AResourceUploadingManager, uploadsBatchOfResourceBeforeCheckingIfOutOfTimeBudget) -{ - // first resource is always uploaded so batch is check frequency constant + 1 - const auto numResourcesInBatch = static_cast(rendererResourceUploader.getResourceUploadBatchSize()) + 1; - // create resources to fill one batch and extra resource in addition - std::vector resList(numResourcesInBatch + 1); - uint64_t hash = 1234u; - for (auto& res : resList) - { - res = ResourceContentHash(hash++, 0u); - registerAndProvideResource(res, false); - } - - // set budget to infinite to make sure the batch gets to be processed - // then right after set budget to 0 and expect rest of batch to be uploaded till next budget check - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(numResourcesInBatch) - .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); return ResourceUploaderMock::FakeResourceDeviceHandle; })) - .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, 0u); return ResourceUploaderMock::FakeResourceDeviceHandle; })) - .WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); - - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - for (int i = 0; i < numResourcesInBatch; ++i) - expectResourceUploaded(resList[i]); - - // last resource is outside of batch and was skipped for uploading - expectResourceStatus(resList.back(), EResourceStatus::Provided); - - for (const auto& res : resList) - makeResourceUnused(res); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(numResourcesInBatch); -} - -TEST_F(AResourceUploadingManager, checksTimeBudgetForEachLargeResourceWhenUploading) -{ - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - - const std::vector dummyData(ResourceUploadingManager::LargeResourceByteSizeThreshold / 4 + 1, 0u); - const ArrayResource largeResource(EResourceType_IndexArray, static_cast(dummyData.size()), EDataType::UInt32, dummyData.data(), ResourceCacheFlag_DoNotCache, ""); - - registerAndProvideResource(res1, false, &largeResource); - registerAndProvideResource(res2, false, &largeResource); - registerAndProvideResource(res3, false, &largeResource); - - // set budget to infinite to make sure more than just first resource is processed - // then right after set budget to 0 and test if the other resources were uploaded - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2) - .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); return ResourceUploaderMock::FakeResourceDeviceHandle; })) - .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, 0u); return ResourceUploaderMock::FakeResourceDeviceHandle; })); - - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res1); - expectResourceUploaded(res2); - // last resource was skipped because even though within batch it is large resources and those are checked for time budget separately - expectResourceStatus(res3, EResourceStatus::Provided); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(2); -} - -TEST_F(AResourceUploadingManager, uploadsOnlyResourcesFittingIntoTimeBudgetInOneUpdate_uploadSlow) -{ - const std::vector dummyData(ResourceUploadingManager::LargeResourceByteSizeThreshold / 4 + 1, 0u); - const ArrayResource largeResource(EResourceType_IndexArray, static_cast(dummyData.size()), EDataType::UInt32, dummyData.data(), ResourceCacheFlag_DoNotCache, ""); - - // using large resources so that time budget checks happen on every resources, not just once per batch - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - - registerAndProvideResource(res1, false, &largeResource); - registerAndProvideResource(res2, false, &largeResource); - registerAndProvideResource(res3, false, &largeResource); - registerAndProvideResource(res4, false, &largeResource); - - //set section time budget so that to be enough for upload of several resources - const uint32_t sectionTimeBudgetMiillis = 10u; - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, sectionTimeBudgetMiillis * 1000u); - - //make sure that not all resources will be uploaded - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtMost(3)).WillRepeatedly(InvokeWithoutArgs([&]() {PlatformThread::Sleep(4); return ResourceUploaderMock::FakeResourceDeviceHandle; })); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - // expect first res uploaded - expectResourceUploaded(res1); - // expect last res not uploaded - expectResourceStatus(res4, EResourceStatus::Provided); - - // upload rest - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res2); - expectResourceUploaded(res3); - expectResourceUploaded(res4); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - makeResourceUnused(res4); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(4); -} - -TEST_F(AResourceUploadingManager, uploadsOnlyResourcesFittingIntoTimeBudgetInOneUpdate_decompressSlow) -{ - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - - NiceMock resource1{ res1, EResourceType_IndexArray }; - NiceMock resource2{ res2, EResourceType_IndexArray }; - NiceMock resource3{ res3, EResourceType_IndexArray }; - NiceMock resource4{ res4, EResourceType_IndexArray }; - - // simulate large resources so that time budget checks happen on every resources, not just once per batch - ON_CALL(resource1, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); - ON_CALL(resource2, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); - ON_CALL(resource3, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); - ON_CALL(resource4, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); - ON_CALL(resource1, isDeCompressedAvailable()).WillByDefault(Return(true)); - ON_CALL(resource2, isDeCompressedAvailable()).WillByDefault(Return(true)); - ON_CALL(resource3, isDeCompressedAvailable()).WillByDefault(Return(true)); - ON_CALL(resource4, isDeCompressedAvailable()).WillByDefault(Return(true)); - - registerAndProvideResource(res1, false, &resource1); - registerAndProvideResource(res2, false, &resource2); - registerAndProvideResource(res3, false, &resource3); - registerAndProvideResource(res4, false, &resource4); - - const uint32_t sectionTimeBudgetMiillis = 10u; - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, sectionTimeBudgetMiillis * 1000u); - - //make sure that not all resources will be uploaded - EXPECT_CALL(resource1, decompress()).Times(AtMost(3)).WillRepeatedly(InvokeWithoutArgs([]() { PlatformThread::Sleep(10); })); - - frameTimer.startFrame(); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - // expect first res uploaded - expectResourceUploaded(res1); - // expect last res not uploaded - expectResourceStatus(res4, EResourceStatus::Provided); - - // upload rest - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); - frameTimer.startFrame(); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res2); - expectResourceUploaded(res3); - expectResourceUploaded(res4); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - makeResourceUnused(res4); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(4); -} - -TEST_F(AResourceUploadingManager_KeepingEffects, doesNotReportKeptEffectAsPendingUnload) -{ - const ResourceContentHash res(1234u, 0u); - registerAndProvideResource(res, true); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res); - - makeResourceUnused(res); - - EXPECT_FALSE(rendererResourceUploader.hasAnythingToUpload()); - - // destructor will unload kept effects - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); -} - -TEST_F(AResourceUploadingManager_WithVRAMCache, willUploadResourcesEvenIfExceedingCacheSize) -{ - // test resource has size of 10 bytes - // cache is set to 30 bytes - - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - const ResourceContentHash res5(1238u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2); - registerAndProvideResource(res3); - registerAndProvideResource(res4); - registerAndProvideResource(res5); - - // all uploaded even though cache is actually smaller - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(5u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - expectResourceUploaded(res1); - expectResourceUploaded(res2); - expectResourceUploaded(res3); - expectResourceUploaded(res4); - expectResourceUploaded(res5); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - makeResourceUnused(res4); - makeResourceUnused(res5); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(5); -} - -TEST_F(AResourceUploadingManager_WithVRAMCache, willUnloadUnusedCachedResourcesIfCacheSizeExceeded) -{ - // test resource has size of 10 bytes - // cache is set to 30 bytes - - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - const ResourceContentHash res5(1238u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2); - registerAndProvideResource(res3); - registerAndProvideResource(res4); - registerAndProvideResource(res5); - - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(5u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - makeResourceUnused(res1); - makeResourceUnused(res2); - makeResourceUnused(res3); - makeResourceUnused(res4); - makeResourceUnused(res5); - - // unload anything exceeding cache limit - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(2u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - expectResourceUploaded(res3); - expectResourceUploaded(res4); - expectResourceUploaded(res5); - Mock::VerifyAndClearExpectations(&uploader); - - // destructor will unload kept resources - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); -} - -TEST_F(AResourceUploadingManager_WithVRAMCache, willOnlyUnloadResourcesWhenNewResourcesAreToBeUploadedAndSoThatCacheSizeIsUsedAtMaximum) -{ - // test resource has size of 10 bytes - // cache is set to 30 bytes - - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2); - - // cache is 20/30 filled - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - makeResourceUnused(res2); - - // cache stays unchanged, 10/30 used resource, 10/30 unused resource, 10/30 available - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - registerAndProvideResource(res3); - registerAndProvideResource(res4); - - // 20 bytes is needed, 10/30 is available right away, 10/30 needs to be freed - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - expectResourceUploaded(res1); - expectResourceUploaded(res3); - expectResourceUploaded(res4); - Mock::VerifyAndClearExpectations(&uploader); - - makeResourceUnused(res1); - makeResourceUnused(res3); - makeResourceUnused(res4); - - // destructor will unload kept resources - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); -} - -TEST_F(AResourceUploadingManager_WithVRAMCache, willNotUnloadAnyCachedUnusedResourceIfThereIsEnoughRemainingCacheAvailable) -{ - // test resource has size of 10 bytes - // cache is set to 30 bytes - - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2); - - // cache is 20/30 filled - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - makeResourceUnused(res2); - - // cache stays unchanged, 10/30 used resource, 10/30 unused resource, 10/30 available - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - registerAndProvideResource(res3); - - // 10 bytes is needed, 10/30 is available right away, nothing needs to be freed - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - expectResourceUploaded(res1); - expectResourceUploaded(res2); - expectResourceUploaded(res3); - Mock::VerifyAndClearExpectations(&uploader); - - makeResourceUnused(res1); - makeResourceUnused(res3); - - // destructor will unload kept resources - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); -} - -TEST_F(AResourceUploadingManager_WithVRAMCache, willUnloadResourcesOfSameOrGreaterSizeOfResourcesToBeUploaded_AlreadyUsingMoreThanCacheSize) -{ - // test resource has size of 10 bytes - // cache is set to 30 bytes - - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - const ResourceContentHash res5(1238u, 0u); - - registerAndProvideResource(res1); - registerAndProvideResource(res2); - registerAndProvideResource(res3); - registerAndProvideResource(res4); - - // more than cache size is used - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(4u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - makeResourceUnused(res1); - makeResourceUnused(res2); - - // unload unused resources exceeding cache limit - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res2); - - registerAndProvideResource(res5); - - // cache is full and exactly 1 unused cached resource will be freed for 1 new resource to be uploaded - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1u); - rendererResourceUploader.uploadAndUnloadPendingResources(); - - expectResourceUploaded(res3); - expectResourceUploaded(res4); - expectResourceUploaded(res5); - Mock::VerifyAndClearExpectations(&uploader); - - makeResourceUnused(res3); - makeResourceUnused(res4); - makeResourceUnused(res5); - - // destructor will unload kept resources - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); -} - -TEST_F(AResourceUploadingManager_ScenePriority, uploadsPreferredResourcesFirst) -{ - const std::vector dummyData(ResourceUploadingManager::LargeResourceByteSizeThreshold / 4 + 1, 0u); - const ArrayResource largeResource(EResourceType_IndexArray, static_cast(dummyData.size()), EDataType::UInt32, dummyData.data(), ResourceCacheFlag_DoNotCache, ""); - - // using large resources so that time budget checks happen on every resource, not just once per batch - const ResourceContentHash res1(1234u, 0u); - const ResourceContentHash res2(1235u, 0u); - const ResourceContentHash res3(1236u, 0u); - const ResourceContentHash res4(1237u, 0u); - - registerAndProvideResource(res1, false, &largeResource, getDeprivedScene()); - registerAndProvideResource(res2, false, &largeResource, getDeprivedScene()); - registerAndProvideResource(res3, false, &largeResource, getPreferredScene()); - registerAndProvideResource(res4, false, &largeResource); - - //set section time budget so the first resource upload will exceed it - const uint32_t sectionTimeBudgetMillis = 10u; - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, sectionTimeBudgetMillis * 1000u); - - //make sure that only 1 resource will be uploaded - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1).WillRepeatedly(InvokeWithoutArgs([&]() {PlatformThread::Sleep(12); return ResourceUploaderMock::FakeResourceDeviceHandle; })); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - // expect preferred resource uploaded - expectResourceUploaded(res3); - // expect others not uploaded - expectResourceStatus(res1, EResourceStatus::Provided); - expectResourceStatus(res2, EResourceStatus::Provided); - expectResourceStatus(res4, EResourceStatus::Provided); - - //make sure that only 1 resource will be uploaded - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1).WillRepeatedly(InvokeWithoutArgs([&]() {PlatformThread::Sleep(12); return ResourceUploaderMock::FakeResourceDeviceHandle; })); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - // expect preferred resource uploaded - expectResourceUploaded(res4); - // expect others not uploaded - expectResourceStatus(res1, EResourceStatus::Provided); - expectResourceStatus(res2, EResourceStatus::Provided); - - // upload rest - frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); - EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); - frameTimer.startFrame(); - rendererResourceUploader.uploadAndUnloadPendingResources(); - expectResourceUploaded(res1); - expectResourceUploaded(res2); - - makeResourceUnused(res1, getDeprivedScene()); - makeResourceUnused(res2, getDeprivedScene()); - makeResourceUnused(res3, getPreferredScene()); - makeResourceUnused(res4); - - EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(4); -} - -} diff --git a/renderer/RendererLib/RendererLib/test/SceneDependencyCheckerTest.cpp b/renderer/RendererLib/RendererLib/test/SceneDependencyCheckerTest.cpp deleted file mode 100644 index f617ebfc1..000000000 --- a/renderer/RendererLib/RendererLib/test/SceneDependencyCheckerTest.cpp +++ /dev/null @@ -1,407 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "SceneDependencyChecker.h" -#include "Scene/Scene.h" -#include "RendererAPI/Types.h" - -using namespace ramses_internal; - -class ASceneDependencyChecker : public ::testing::Test -{ -public: - ramses_internal::SceneDependencyChecker dependencyChecker; - - [[nodiscard]] bool checkSceneOrder(SceneId sceneFirst, SceneId sceneSecond, const SceneIdVector& sceneList) const - { - bool foundFirst = false; - for (size_t i = 0; i < sceneList.size(); i++) - { - if (sceneList[i] == sceneFirst) - { - foundFirst = true; - } - if (sceneList[i] == sceneSecond) - { - if (foundFirst) - { - return true; - } - return false; - } - } - return false; - } -}; - -TEST_F(ASceneDependencyChecker, isEmptyInitially) -{ - EXPECT_TRUE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canAddDependencyToIndependentScenes) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //check that the scenes are not dependent in th first place - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveDependencyOfDependentScenes) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - dependencyChecker.removeDependency(scene1, scene2); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - EXPECT_TRUE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, returnsCorrectDependencyListForOneDependency) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - //check scene dependency list - const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); - EXPECT_EQ(2u, sceneList.size()); - EXPECT_TRUE(checkSceneOrder(scene1, scene2, sceneList)); -} - -TEST_F(ASceneDependencyChecker, canNotAddReverseDependencyToDependentScenes) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - //add reverse dependency should not be possible - EXPECT_FALSE(dependencyChecker.addDependency(scene2, scene1)); - - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canAddMultipleDependenciesForOneScene) -{ - SceneId scene1(3u); - SceneId scene2(5u); - SceneId scene3(7u); - - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene3)); - - //check scene dependency list - const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); - EXPECT_EQ(3u, sceneList.size()); - EXPECT_TRUE(checkSceneOrder(scene1, scene2, sceneList)); - EXPECT_TRUE(checkSceneOrder(scene1, scene3, sceneList)); - - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canAddMultipleDependenciesForMultipleScenes) -{ - SceneId scene1(3u); - SceneId scene2(5u); - SceneId scene3(7u); - SceneId scene4(17u); - - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene4)); - - //check scene dependency list - const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); - EXPECT_EQ(4u, sceneList.size()); - EXPECT_TRUE(checkSceneOrder(scene1, scene2, sceneList)); - EXPECT_TRUE(checkSceneOrder(scene3, scene4, sceneList)); - - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canAddMultipleCascadingDependenciesForMultipleScenes) -{ - SceneId scene1(3u); - SceneId scene2(5u); - SceneId scene3(7u); - SceneId scene4(17u); - - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); - - //check scene dependency list - const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); - EXPECT_EQ(4u, sceneList.size()); - EXPECT_TRUE(checkSceneOrder(scene1, scene2, sceneList)); - EXPECT_TRUE(checkSceneOrder(scene1, scene3, sceneList)); - EXPECT_TRUE(checkSceneOrder(scene1, scene4, sceneList)); - EXPECT_TRUE(checkSceneOrder(scene2, scene3, sceneList)); - EXPECT_TRUE(checkSceneOrder(scene2, scene4, sceneList)); - - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveSingleDependentScene) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - //check dependencies - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - EXPECT_EQ(2u, dependencyChecker.getDependentScenesInOrder().size()); - - //remove scene - dependencyChecker.removeScene(scene2); - - //check dependency removal for both scenes - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - EXPECT_EQ(0u, dependencyChecker.getDependentScenesInOrder().size()); - EXPECT_TRUE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveSingleSceneWithDependency) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - //check dependencies - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_EQ(2u, dependencyChecker.getDependentScenesInOrder().size()); - - //remove scene - dependencyChecker.removeScene(scene1); - - //check dependency removal for both scenes - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - EXPECT_EQ(0u, dependencyChecker.getDependentScenesInOrder().size()); - EXPECT_TRUE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveSingleDependentSceneFromCascadingDependency) -{ - SceneId scene1(3u); - SceneId scene2(5u); - SceneId scene3(7u); - SceneId scene4(17u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); - - //check dependencies - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); - EXPECT_EQ(4u, dependencyChecker.getDependentScenesInOrder().size()); - - //remove scene - dependencyChecker.removeScene(scene1); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); - - EXPECT_EQ(3u, dependencyChecker.getDependentScenesInOrder().size()); - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveSingleSceneWithDependencyFromCascadingDependency) -{ - SceneId scene1(3u); - SceneId scene2(5u); - SceneId scene3(7u); - SceneId scene4(17u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); - - //check dependencies - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); - EXPECT_EQ(4u, dependencyChecker.getDependentScenesInOrder().size()); - - //remove scene - dependencyChecker.removeScene(scene4); - - //check dependency removal for both scenes - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene4)); - - EXPECT_EQ(3u, dependencyChecker.getDependentScenesInOrder().size()); - EXPECT_FALSE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveSingleDependentSceneWithDependenciesFromCascadingDependency) -{ - SceneId scene1(3u); - SceneId scene2(5u); - SceneId scene3(7u); - SceneId scene4(17u); - - //add a dependency - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); - - //check dependencies - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); - EXPECT_EQ(4u, dependencyChecker.getDependentScenesInOrder().size()); - - //remove scene - dependencyChecker.removeScene(scene2); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene4)); - - EXPECT_EQ(0u, dependencyChecker.getDependentScenesInOrder().size()); - EXPECT_TRUE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, canRemoveDependencyAddedTwiceWithTwoRemovalsOnly) -{ - SceneId scene1(3u); - SceneId scene2(5u); - - //add a dependency twice - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - dependencyChecker.removeDependency(scene1, scene2); - - //scene2 should still be dependent - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - dependencyChecker.removeDependency(scene1, scene2); - - //now it should not be dependent anymore - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - - EXPECT_TRUE(dependencyChecker.isEmpty()); -} - -TEST_F(ASceneDependencyChecker, confidenceTest_complexDependencyHierarchy) -{ - const SceneId scene1(1u); - const SceneId scene2(2u); - const SceneId scene3(3u); - const SceneId scene4(4u); - const SceneId scene5(5u); - const SceneId scene6(6u); - const SceneId scene7(7u); - - EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene5)); - EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene5)); - EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene5)); - EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene7)); - EXPECT_TRUE(dependencyChecker.addDependency(scene5, scene4)); - EXPECT_TRUE(dependencyChecker.addDependency(scene6, scene1)); - EXPECT_TRUE(dependencyChecker.addDependency(scene6, scene2)); - EXPECT_TRUE(dependencyChecker.addDependency(scene7, scene2)); - - // fail to create cyclic dependency - EXPECT_FALSE(dependencyChecker.addDependency(scene4, scene7)); - - // query dependencies - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene5)); - EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene7)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene6)); - - // get ordered list of scenes - const auto& orderedScenes = dependencyChecker.getDependentScenesInOrder(); - ASSERT_EQ(7u, orderedScenes.size()); - EXPECT_EQ(scene6, orderedScenes[0]); - EXPECT_EQ(scene3, orderedScenes[1]); - EXPECT_EQ(scene1, orderedScenes[2]); - EXPECT_EQ(scene7, orderedScenes[3]); - EXPECT_EQ(scene2, orderedScenes[4]); - EXPECT_EQ(scene5, orderedScenes[5]); - EXPECT_EQ(scene4, orderedScenes[6]); - - // remove dependencies - dependencyChecker.removeDependency(scene1, scene5); - dependencyChecker.removeDependency(scene2, scene5); - dependencyChecker.removeDependency(scene3, scene2); - dependencyChecker.removeDependency(scene3, scene5); - dependencyChecker.removeDependency(scene3, scene7); - dependencyChecker.removeDependency(scene5, scene4); - dependencyChecker.removeDependency(scene6, scene1); - dependencyChecker.removeDependency(scene6, scene2); - dependencyChecker.removeDependency(scene7, scene2); - - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene3)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene4)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene5)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene6)); - EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene7)); - - EXPECT_TRUE(dependencyChecker.getDependentScenesInOrder().empty()); -} diff --git a/renderer/RendererLib/RendererLib/test/SceneExpirationMonitorTest.cpp b/renderer/RendererLib/RendererLib/test/SceneExpirationMonitorTest.cpp deleted file mode 100644 index 5b91992f5..000000000 --- a/renderer/RendererLib/RendererLib/test/SceneExpirationMonitorTest.cpp +++ /dev/null @@ -1,943 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererEventCollector.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -class ASceneExpirationMonitor : public ::testing::Test -{ -public: - ASceneExpirationMonitor() - : rendererScenes(eventCollector) - , expirationMonitor(rendererScenes, eventCollector, statistics) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - rendererScenes.createScene(SceneInfo{ scene1 }); - rendererScenes.createScene(SceneInfo{ scene2 }); - rendererScenes.createScene(SceneInfo{ scene3 }); - rendererScenes.createScene(SceneInfo{ scene4 }); - rendererScenes.createScene(SceneInfo{ scene5 }); - } - - ~ASceneExpirationMonitor() override - { - rendererScenes.destroyScene(scene1); - rendererScenes.destroyScene(scene2); - rendererScenes.destroyScene(scene3); - rendererScenes.destroyScene(scene4); - rendererScenes.destroyScene(scene5); - expectNoEvent(); - } - -protected: - void expectNoEvent() - { - RendererEventVector events; - RendererEventVector dummy; - eventCollector.appendAndConsumePendingEvents(dummy, events); - EXPECT_TRUE(events.empty()); - for (const auto& e : events) - { - EXPECT_EQ(ERendererEventType::Invalid, e.eventType); - EXPECT_EQ(0u, e.sceneId.getValue()); - } - } - - void expectEvents(std::initializer_list< std::pair > expectedEvents) - { - RendererEventVector events; - RendererEventVector dummy; - eventCollector.appendAndConsumePendingEvents(dummy, events); - EXPECT_EQ(expectedEvents.size(), events.size()); - for (const auto& expectedEvent : expectedEvents) - { - auto matchesEvent = [expectedEvent](const RendererEvent& e) { return e.sceneId == expectedEvent.first && e.eventType == expectedEvent.second; }; - auto it = std::find_if(events.begin(), events.end(), matchesEvent); - ASSERT_TRUE(it != events.end()); - events.erase(it); - } - } - - void stopMonitoringScenes(std::initializer_list scenes) - { - for (const auto scene : scenes) - { - expirationMonitor.onDestroyed(scene); - expectEvents({ { scene, ERendererEventType::SceneExpirationMonitoringDisabled } }); - } - } - - void stopMonitoringSceneAndDiscardAllEvents(SceneId sceneId) - { - expirationMonitor.onDestroyed(sceneId); - RendererEventVector dummy; - eventCollector.appendAndConsumePendingEvents(dummy, dummy); - } - - [[nodiscard]] std::string logOutput() const - { - StringOutputStream strstr; - statistics.writeStatsToStream(strstr); - return strstr.release(); - } - - RendererEventCollector eventCollector; - RendererScenes rendererScenes; - SceneExpirationMonitor expirationMonitor; - RendererStatistics statistics; - - const FlushTime::Clock::time_point currentFakeTime{ std::chrono::milliseconds(10000) }; - - const SceneId scene1{ 22u }; - const SceneId scene2{ 23u }; - const SceneId scene3{ 24u }; - const SceneId scene4{ 25u }; - const SceneId scene5{ 26u }; -}; - -TEST_F(ASceneExpirationMonitor, reportInvalidTSForScenesWithNoExpiration) -{ - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene3)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene4)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene5)); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, doesNotCheckAnythingIfNoScenesMonitored) -{ - expirationMonitor.checkExpiredScenes(currentFakeTime); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, doesNotGenerateEventIfZeroLimit) -{ - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expirationMonitor.onFlushApplied(scene2, FlushTime::InvalidTimestamp, {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, TSFromFlushAppliedDoesNotAffectRenderedTS) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - stopMonitoringScenes({ scene1, scene2 }); -} - -TEST_F(ASceneExpirationMonitor, lastTSFromFlushAppliedBecomesRenderedTSWhenSceneReportedRendered) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onRendered(scene1); - EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onRendered(scene2); - EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - stopMonitoringScenes({ scene1, scene2 }); -} - -TEST_F(ASceneExpirationMonitor, renderedTSIsResetIfSceneHidden) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onHidden(scene1); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onHidden(scene2); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - stopMonitoringScenes({ scene1, scene2 }); -} - -TEST_F(ASceneExpirationMonitor, renderedTSIsTakenFromLastAppliedFlushIfSceneRenderedAgainAfterHidden) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - expirationMonitor.onHidden(scene1); - expirationMonitor.onHidden(scene2); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - // if rendered again, TS becomes valid - taken from last applied flush - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); - - stopMonitoringScenes({ scene1, scene2 }); -} - -TEST_F(ASceneExpirationMonitor, doesNotGenerateEventIfLimitNotExceeded) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime); - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, AlwaysExpiresIfZeroTimeComesFromClock) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - // Zero time comes when PTP is broken or has lost sync for too long - expirationMonitor.checkExpiredScenes( FlushTime::Clock::time_point{ std::chrono::milliseconds(0) }); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, canHandleTimeStampInFutureAndTreatsAsNotExceeded) -{ - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::time_point(currentFakeTime + std::chrono::hours(100)) + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime); - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventIfAppliedFlushTSExpired) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventIfLastAppliedFlushTSExpired) -{ - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectNoEvent(); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventIfRenderedTSExpired) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectEvents({ {scene1, ERendererEventType::SceneExpired} }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventIfLastAppliedFlushTSExpiredEvenIfRenderedTSNotExpired) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventIfLastPendingFlushExpired) -{ - // enable monitoring by applying at least one flush with timestamp - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - // create some pending flushes - auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; - pendingFlushes.resize(3u); - pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; - pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; - pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::hours(1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectNoEvent(); - - pendingFlushes.push_back({}); - pendingFlushes[3].timeInfo.expirationTimestamp = currentFakeTime; - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventIfAnyOfPendingFlushesExpiredEvenIfAppliedAndRenderedNotExpired) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - expirationMonitor.onRendered(scene1); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - // create some pending flushes - auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; - pendingFlushes.resize(3u); - pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; - pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; - pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::hours(1); - expectNoEvent(); - - pendingFlushes.push_back({}); - pendingFlushes[3].timeInfo.expirationTimestamp = currentFakeTime; - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventOnlyOnceIfExpiredAndCheckedMultipleTimesWithSingleApplyOnly) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - // still in exceeded state, no more events - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(4)); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(5)); - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, evaluatesExpirationBasedOnLimitFromLastAppliedFlush) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectNoEvent(); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(100)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventForAllScenesWhereAppropriate) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene3, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene3, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - expirationMonitor.onRendered(scene3); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectEvents({ { scene1, ERendererEventType::SceneExpired },{ scene3, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1, scene2, scene3 }); -} - -TEST_F(ASceneExpirationMonitor, generatesEventOnlyOnceIfSceneKeepsBeingExpired) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - for (size_t i = 0; i < 5u; ++i) - { - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 2)); - } - // is still exceeded - expirationMonitor.onRendered(scene1); - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesRecoveryEventWhenExceedingStops) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); - expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesRecoveryEventOnlyOnceWhenExceedingStopsAndKeepsBelowLimit) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); - expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration } }); - - for (size_t i = 0; i < 5u; ++i) - { - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 10)); - } - // is still below limit - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, willReportSceneThatKeepsBeingFlushedButNotRendered) -{ - // scene must be rendered at least once with valid expiration, only then it can expire - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - RendererEventVector events; - for (size_t i = 0; i < 5; ++i) - { - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(2), {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 1)); - } - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, willNotReportSceneThatKeepsBeingFlushedButHidden) -{ - // scene must be rendered at least once with valid expiration, only then it can expire - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.onHidden(scene1); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - RendererEventVector events; - for (size_t i = 0; i < 5; ++i) - { - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(2), {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 1)); - } - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, willReportSceneThatKeepsBeingRenderedButNotFlushed) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(2), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - RendererEventVector events; - for (size_t i = 0; i < 5; ++i) - { - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i)); - } - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, doesNotGenerateEventIfLastRenderedTSExpiredButSceneHidden) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime); - expectNoEvent(); - - expirationMonitor.onHidden(scene1); - - // keep flushing up-to-date, no rendering - for (int i = 0; i < 5; ++i) - { - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1 + i), {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i)); - } - // if renderedTS would be checked there would be expiration event - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, generatesRecoveryEventIfRenderedTSExpiredButSceneGetsHiddenAndNewFlushesArrive) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - // hiding alone will not cause recovery - last applied flush still expired - expirationMonitor.onHidden(scene1); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - expectNoEvent(); - - // keep scene hidden and apply new up-to-date flush - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(5), {}, 0); - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); - expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, confidenceTest_checkingOfOneSceneDoesNotAffectCheckingOfOtherScene) -{ - // flush 1 - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - // few cycles of scene 2 - expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene2); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectEvents({ { scene1, ERendererEventType::SceneExpired },{ scene2, ERendererEventType::SceneExpired } }); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene2); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene2); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectNoEvent(); - - // both exceeded, get both back to normal - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration },{ scene2, ERendererEventType::SceneRecoveredFromExpiration } }); - - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene2); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - // both still below limit - expectNoEvent(); - - stopMonitoringScenes({ scene1, scene2 }); -} - -TEST_F(ASceneExpirationMonitor, confidenceTest_severalScenesWithDifferentBehaviorAndLimits) -{ - //scene1 checking enabled, sometimes exceeds - //scene2 checking disabled - //scene3 checking enabled, never exceeds - //scene4 checking enabled, always exceeds - //scene5 checking enabled, sometimes exceeds - - // 'randomize' times of apply/render/checks - - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene3, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene5, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene3, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene5, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene2); - expirationMonitor.onRendered(scene5); - - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectEvents({ {scene5, ERendererEventType::SceneExpired} }); - - expirationMonitor.onFlushApplied(scene4, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene4, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene3); - - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectEvents({ { scene4, ERendererEventType::SceneExpired } }); - - expirationMonitor.onFlushApplied(scene5, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onRendered(scene2); - expirationMonitor.onRendered(scene4); - expirationMonitor.onRendered(scene5); - - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectEvents({ { scene5, ERendererEventType::SceneRecoveredFromExpiration } }); - - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene3, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); - expirationMonitor.onFlushApplied(scene4, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - expirationMonitor.onRendered(scene3); - expirationMonitor.onRendered(scene5); - - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectNoEvent(); - - expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); - expectNoEvent(); - - stopMonitoringScenes({ scene1, scene3, scene4, scene5 }); -} - -TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneAndEmitsDisabledEventOnDestroy) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - expirationMonitor.onDestroyed(scene1); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - - // this would trigger exceeded event if still monitored - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - - // monitoring already disabled, no more disabled event on destroyed - expirationMonitor.onDestroyed(scene1); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneOnInvalidTS) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - // disable - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - - // this would trigger exceeded event if still monitored - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - - // monitoring already disabled, no more disabled event on destroyed - expirationMonitor.onDestroyed(scene1); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneOnInvalidTS_alreadyExpired) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - // expire - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - // disable - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); - - // this would trigger exceeded event if still monitored - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - - // monitoring already disabled, no more disabled event on destroyed - expirationMonitor.onDestroyed(scene1); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneOnInvalidTS_withExpiredPendingFlush) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - // disable - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - // create expired pending flush - auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; - pendingFlushes.resize(1u); - pendingFlushes[0].timeInfo.expirationTimestamp = currentFakeTime; - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); - - // monitoring already disabled, no more disabled event on destroyed - expirationMonitor.onDestroyed(scene1); - expectNoEvent(); -} - -TEST_F(ASceneExpirationMonitor, canReenableMonitoringAfterDisable) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - // disable - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - // this would trigger exceeded event if still monitored - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectNoEvent(); - - // re-enable - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - // now it triggers exceeded - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, willNotReportRecoveredIfExpiredThenDisabledAndReenabled) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - // make expired - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - // disable - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - // this would trigger exceeded event if still monitored - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectNoEvent(); - - // re-enable - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - // if monitored the whole time this would trigger recovered event - // but there won't be any event because monitoring was disabled before - expirationMonitor.checkExpiredScenes(currentFakeTime - std::chrono::milliseconds(10)); - expectNoEvent(); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, willReportExpiredIfReenabledWhileExpired) -{ - RendererEventVector events; - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - expirationMonitor.onRendered(scene1); - - // make expired - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - // disable - expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); - - // this would trigger exceeded event if still monitored - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectNoEvent(); - - // re-enable - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); - - // scene is expired already before it was re-enabled, a new expire event will be emitted - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - expectEvents({ { scene1, ERendererEventType::SceneExpired } }); - - stopMonitoringScenes({ scene1 }); -} - -TEST_F(ASceneExpirationMonitor, willReportExpirationAppliedToStatistics) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - - expirationMonitor.checkExpiredScenes(currentFakeTime); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-1/-1/-1)")); - - statistics.reset(); - - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:9/9/9)")); - - stopMonitoringSceneAndDiscardAllEvents(scene1); -} - -TEST_F(ASceneExpirationMonitor, willReportExpirationRenderedToStatistics) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - - expirationMonitor.checkExpiredScenes(currentFakeTime); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-1/-1/-1)")); - - statistics.reset(); - - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:9/9/9)")); - - stopMonitoringSceneAndDiscardAllEvents(scene1); -} - -TEST_F(ASceneExpirationMonitor, willReportExpirationPendingToStatistics) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); - - auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; - pendingFlushes.resize(3u); - pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; - pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; - pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::milliseconds(1); - - expirationMonitor.checkExpiredScenes(currentFakeTime); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-1/-1/-1)")); - - statistics.reset(); - - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:9/9/9)")); - - stopMonitoringSceneAndDiscardAllEvents(scene1); -} - -TEST_F(ASceneExpirationMonitor, willReportMaximumExpirationToStatistics) -{ - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(5), {}, 0); - expirationMonitor.onRendered(scene1); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(4), {}, 0); - - auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; - pendingFlushes.resize(3u); - pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; - pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; - pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::milliseconds(3); - - expirationMonitor.checkExpiredScenes(currentFakeTime); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-3/-3/-3)")); - - statistics.reset(); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(2), {}, 0); - expirationMonitor.onRendered(scene1); - - expirationMonitor.checkExpiredScenes(currentFakeTime); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-2/-2/-2)")); - - statistics.reset(); - - expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); - expirationMonitor.onRendered(scene1); - - expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); - statistics.frameFinished(0u); - EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:1/1/1)")); - - statistics.reset(); - - stopMonitoringSceneAndDiscardAllEvents(scene1); -} diff --git a/renderer/RendererLib/RendererLib/test/SceneLinksManagerTest.cpp b/renderer/RendererLib/RendererLib/test/SceneLinksManagerTest.cpp deleted file mode 100644 index beb65a0fa..000000000 --- a/renderer/RendererLib/RendererLib/test/SceneLinksManagerTest.cpp +++ /dev/null @@ -1,354 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" -#include "SceneLinksTestUtils.h" -#include "Utils/ThreadLocalLog.h" - -using namespace testing; -using namespace ramses_internal; - -template -class ASceneLinksManager : public ::testing::Test -{ -public: - ASceneLinksManager() - : rendererScenes(rendererEventCollector) - , sceneLinksManager(rendererScenes.getSceneLinksManager()) - , concreteLinkManager(GetConcreteLinkManager(sceneLinksManager)) - , providerSceneId(3u) - , consumerSceneId(4u) - , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) - , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) - , providerSceneAllocator(providerScene) - , consumerSceneAllocator(consumerScene) - , providerId(33u) - , consumerId(44u) - , providerSlotHandle(5u) - , consumerSlotHandle(6u) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - createDataSlot(providerSceneAllocator, providerSlotHandle, providerId, true); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); - - createDataSlot(consumerSceneAllocator, consumerSlotHandle, consumerId, false); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); - } - -protected: - void expectRendererEvent(ERendererEventType event, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(event, events.front().eventType); - EXPECT_EQ(providerSId, events.front().providerSceneId); - EXPECT_EQ(consumerSId, events.front().consumerSceneId); - EXPECT_EQ(pId, events.front().providerdataId); - EXPECT_EQ(cId, events.front().consumerdataId); - } - - void expectRendererEvent(ERendererEventType event, SceneId consumerSId, DataSlotId cId, SceneId providerSId) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(event, events.front().eventType); - EXPECT_EQ(consumerSId, events.front().consumerSceneId); - EXPECT_EQ(cId, events.front().consumerdataId); - EXPECT_EQ(providerSId, events.front().providerSceneId); - } - - void expectConsumerLinkedToProvider() - { - EXPECT_TRUE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); - SceneLinkVector links; - this->concreteLinkManager.getSceneLinks().getLinkedConsumers(this->providerSceneId, this->providerSlotHandle, links); - ASSERT_EQ(1u, links.size()); - EXPECT_EQ(this->providerSceneId, links.front().providerSceneId); - EXPECT_EQ(this->providerSlotHandle, links.front().providerSlot); - EXPECT_EQ(this->consumerSceneId, links.front().consumerSceneId); - EXPECT_EQ(this->consumerSlotHandle, links.front().consumerSlot); - } - - void expectRendererEventUnlinkedAndDestroyedSlot(bool slotIsProvider, SceneId sceneIdOfDestroyedSlot, DataSlotId destroyedSlotId, SceneId consumerSId, DataSlotId cId, SceneId consumerSceneId2 = SceneId(0u), DataSlotId consumerId2 = DataSlotId(0u)) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - - const bool hasTwoLinksRemoved = (consumerId2.getValue() != 0u); - if (hasTwoLinksRemoved) - { - ASSERT_EQ(3u, events.size()); - } - else - { - ASSERT_EQ(2u, events.size()); - } - - uint32_t eventIdx = 0u; - - EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); - EXPECT_EQ(consumerSId, events[eventIdx].consumerSceneId); - EXPECT_EQ(cId, events[eventIdx].consumerdataId); - - if (hasTwoLinksRemoved) - { - ++eventIdx; - EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); - EXPECT_EQ(consumerSceneId2, events[eventIdx].consumerSceneId); - EXPECT_EQ(consumerId2, events[eventIdx].consumerdataId); - } - - ++eventIdx; - if (slotIsProvider) - { - EXPECT_EQ(ERendererEventType::SceneDataSlotProviderDestroyed, events[eventIdx].eventType); - EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].providerSceneId); - EXPECT_EQ(destroyedSlotId, events[eventIdx].providerdataId); - } - else - { - EXPECT_EQ(ERendererEventType::SceneDataSlotConsumerDestroyed, events[eventIdx].eventType); - EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].consumerSceneId); - EXPECT_EQ(destroyedSlotId, events[eventIdx].consumerdataId); - } - } - - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - SceneLinksManager& sceneLinksManager; - const T& concreteLinkManager; - const SceneId providerSceneId; - const SceneId consumerSceneId; - IScene& providerScene; - IScene& consumerScene; - SceneAllocateHelper providerSceneAllocator; - SceneAllocateHelper consumerSceneAllocator; - const DataSlotId providerId; - const DataSlotId consumerId; - const DataSlotHandle providerSlotHandle; - const DataSlotHandle consumerSlotHandle; -}; - -using ManagerTypes = ::testing::Types < - TransformationLinkManager, - DataReferenceLinkManager, - TextureLinkManager ->; - -TYPED_TEST_SUITE(ASceneLinksManager, ManagerTypes); - -TYPED_TEST(ASceneLinksManager, reportsLinkCreationEvent) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectConsumerLinkedToProvider(); -} - -TYPED_TEST(ASceneLinksManager, reportsLinkRemovalEvent) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinked, this->consumerSceneId, this->consumerId, this->providerSceneId); -} - -TYPED_TEST(ASceneLinksManager, reportsProviderSlotRemoved) -{ - this->providerScene.releaseDataSlot(this->providerSlotHandle); - this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderDestroyed, this->providerSceneId, this->providerId, SceneId(0u), DataSlotId(0u)); -} - -TYPED_TEST(ASceneLinksManager, reportsConsumerSlotRemoved) -{ - this->consumerScene.releaseDataSlot(this->consumerSlotHandle); - this->expectRendererEvent(ERendererEventType::SceneDataSlotConsumerDestroyed, SceneId(0u), DataSlotId(0u), this->consumerSceneId, this->consumerId); -} - -TYPED_TEST(ASceneLinksManager, removesLinkForSceneWithRemovedProviderSlot) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->providerScene.releaseDataSlot(this->providerSlotHandle); - this->expectRendererEventUnlinkedAndDestroyedSlot(true, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - - EXPECT_FALSE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); - EXPECT_FALSE(this->concreteLinkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); -} - -TYPED_TEST(ASceneLinksManager, removesLinkForSceneWithRemovedConsumerSlot) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->consumerScene.releaseDataSlot(this->consumerSlotHandle); - this->expectRendererEventUnlinkedAndDestroyedSlot(false, this->consumerSceneId, this->consumerId, this->consumerSceneId, this->consumerId); - - EXPECT_FALSE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); - EXPECT_FALSE(this->concreteLinkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); -} - -TYPED_TEST(ASceneLinksManager, secondLinkToConsumerOverwritesPreviousLink) -{ - const DataSlotId providerId2(999u); - const DataSlotHandle slotHandle(43u); - createDataSlot(this->providerSceneAllocator, slotHandle, providerId2, true); - this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); - - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.createDataLink(this->providerSceneId, providerId2, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, providerId2, this->consumerSceneId, this->consumerId); -} - -TYPED_TEST(ASceneLinksManager, failsToCreateLinkForInvalidSceneId) -{ - this->sceneLinksManager.createDataLink(SceneId(0u), this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, SceneId(0u), this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, SceneId(0u), this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, this->providerId, SceneId(0u), this->consumerId); -} - -TYPED_TEST(ASceneLinksManager, failsToCreateLinkForInvalidDataSlotId) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, DataSlotId(), this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, DataSlotId(), this->consumerSceneId, this->consumerId); - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, DataSlotId()); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, this->providerId, this->consumerSceneId, DataSlotId()); -} - -TYPED_TEST(ASceneLinksManager, failsToCreateLinkForSwappedDataSlotId) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->consumerId, this->consumerSceneId, this->providerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, this->consumerId, this->consumerSceneId, this->providerId); - this->sceneLinksManager.createDataLink(this->consumerSceneId, this->providerId, this->providerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->consumerSceneId, this->providerId, this->providerSceneId, this->consumerId); -} - -TYPED_TEST(ASceneLinksManager, failsToCreateLinkForSwappedConsumerAndProvider) -{ - this->sceneLinksManager.createDataLink(this->consumerSceneId, this->consumerId, this->providerSceneId, this->providerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->consumerSceneId, this->consumerId, this->providerSceneId, this->providerId); -} - -TYPED_TEST(ASceneLinksManager, canCreateLinkTwiceForSameConsumer) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); -} - -TYPED_TEST(ASceneLinksManager, failsToRemoveLinkForInvalidSceneId) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.removeDataLink(SceneId(0u), this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, SceneId(0u), this->consumerId, SceneId::Invalid()); -} - -TYPED_TEST(ASceneLinksManager, failsToRemoveLinkForInvalidDataSlotId) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.removeDataLink(this->consumerSceneId, DataSlotId()); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, this->consumerSceneId, DataSlotId(), SceneId::Invalid()); -} - -TYPED_TEST(ASceneLinksManager, failsToRemoveLinkWhenProvidingProviderInsteadOfConsumer) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.removeDataLink(this->providerSceneId, this->providerId); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, this->providerSceneId, this->providerId, SceneId::Invalid()); -} - -TYPED_TEST(ASceneLinksManager, failsToRemoveLinkIfThereIsNoLink) -{ - this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, this->consumerSceneId, this->consumerId, SceneId::Invalid()); -} - -TYPED_TEST(ASceneLinksManager, failsToCreateLinksCausingCyclicDependency) -{ - const DataSlotId providerId2(999u); - const DataSlotHandle slotHandle(43u); - createDataSlot(this->consumerSceneAllocator, slotHandle, providerId2, true); - this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->consumerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); - - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle2(43u); - createDataSlot(this->providerSceneAllocator, slotHandle2, consumerId2, true); - this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); - - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.createDataLink(this->consumerSceneId, providerId2, this->providerSceneId, consumerId2); - this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->consumerSceneId, providerId2, this->providerSceneId, consumerId2); -} - -TYPED_TEST(ASceneLinksManager, canLinkUnlinkAndLinkAgain) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataUnlinked, this->consumerSceneId, this->consumerId, this->providerSceneId); - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectConsumerLinkedToProvider(); -} - -TYPED_TEST(ASceneLinksManager, removesAllLinksToProviderOnProviderSlotDestruction) -{ - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle(43u); - createDataSlot(this->consumerSceneAllocator, slotHandle, consumerId2, false); - this->expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), this->consumerSceneId, consumerId2); - - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, consumerId2); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, consumerId2); - - this->providerScene.releaseDataSlot(this->providerSlotHandle); - this->expectRendererEventUnlinkedAndDestroyedSlot(true, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId, this->consumerSceneId, consumerId2); - - EXPECT_FALSE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); - EXPECT_FALSE(this->concreteLinkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); -} - -using SceneLinksTextureManager = ASceneLinksManager; - -TEST_F(SceneLinksTextureManager, unlinksTextureLinksForUnmappedProviderScene) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - - this->sceneLinksManager.handleSceneUnmapped(this->providerSceneId); - EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); - EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(this->consumerSceneId)); -} - -TEST_F(SceneLinksTextureManager, unlinksTextureLinksForUnmappedConsumerScene) -{ - this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); - - this->sceneLinksManager.handleSceneUnmapped(this->consumerSceneId); - EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(this->providerSceneId)); - EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); -} diff --git a/renderer/RendererLib/RendererLib/test/SceneLinksTestUtils.h b/renderer/RendererLib/RendererLib/test/SceneLinksTestUtils.h deleted file mode 100644 index 402485507..000000000 --- a/renderer/RendererLib/RendererLib/test/SceneLinksTestUtils.h +++ /dev/null @@ -1,80 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENELINKSTESTUTILS_H -#define RAMSES_SCENELINKSTESTUTILS_H - -#include "SceneAPI/IScene.h" -#include "SceneAllocateHelper.h" -#include "RendererLib/SceneLinksManager.h" - -namespace ramses_internal { - -template -const T& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) -{ - assert(false); - return sceneLinksManager.getTransformationLinkManager(); -} - -template <> -inline const TransformationLinkManager& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) -{ - return sceneLinksManager.getTransformationLinkManager(); -} - -template <> -inline const DataReferenceLinkManager& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) -{ - return sceneLinksManager.getDataReferenceLinkManager(); -} - -template <> -inline const TextureLinkManager& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) -{ - return sceneLinksManager.getTextureLinkManager(); -} - -template -void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) -{ - scene.allocateDataSlot({ (providerType ? EDataSlotType_TransformationProvider : EDataSlotType_TransformationConsumer), slotId, NodeHandle(12u), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slot); -} - -template <> -inline void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) -{ - const NodeHandle node = scene.allocateNode(); - scene.allocateTransform(node); - scene.allocateDataSlot({ (providerType ? EDataSlotType_TransformationProvider : EDataSlotType_TransformationConsumer), slotId, node, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slot); -} - -template <> -inline void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) -{ - const DataLayoutHandle layout = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); - const DataInstanceHandle dataRef = scene.allocateDataInstance(layout); - - scene.allocateDataSlot({ (providerType ? EDataSlotType_DataProvider : EDataSlotType_DataConsumer), slotId, NodeHandle(), dataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slot); -} - -template <> -inline void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) -{ - if (providerType) - { - scene.allocateDataSlot({ EDataSlotType_TextureProvider, slotId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash(1234u, 0), TextureSamplerHandle() }, slot); - } - else - { - const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, RenderBufferHandle(999) }); - scene.allocateDataSlot({ EDataSlotType_TextureConsumer, slotId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }, slot); - } -} -} -#endif diff --git a/renderer/RendererLib/RendererLib/test/SceneResourceUploaderTest.cpp b/renderer/RendererLib/RendererLib/test/SceneResourceUploaderTest.cpp deleted file mode 100644 index b1568e587..000000000 --- a/renderer/RendererLib/RendererLib/test/SceneResourceUploaderTest.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2021 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "RendererAPI/Types.h" -#include "RendererLib/SceneResourceUploader.h" -#include "RendererResourceManagerMock.h" -#include "Scene/Scene.h" -#include "SceneAllocateHelper.h" -#include "SceneUtils/ResourceUtils.h" - -namespace ramses_internal { -using namespace testing; - -class ASceneResourceUploader : public ::testing::Test -{ -public: - ASceneResourceUploader() - : scene(SceneInfo(sceneID)) - , allocateHelper(scene) - { - } - -protected: - const SceneId sceneID{ 13u }; - Scene scene; - SceneAllocateHelper allocateHelper; - StrictMock resourceManager; -}; - - -TEST_F(ASceneResourceUploader, UploadVertexArray) -{ - const auto node = allocateHelper.allocateNode(); - const auto renderable = allocateHelper.allocateRenderable(node); - const DataFieldInfo indexField(EDataType::Indices, 1u, EFixedSemantics::Indices); - const DataFieldInfo vert1Field(EDataType::Vector2Buffer, 1u); - const DataFieldInfo vert2Field(EDataType::Vector3Buffer, 1u); - - const ResourceContentHash fakeEffectHash {123456u, 0u}; - const ResourceContentHash fakeIndexBufferHash {123456u, 1u}; - const ResourceContentHash fakeVertexBuffer1Hash {123456u, 2u}; - const ResourceContentHash fakeVertexBuffer2tHash {123456u, 3u}; - - const DeviceResourceHandle fakeEffectDeviceHandle { 999990u }; - const DeviceResourceHandle fakeIndexBufferDeviceHandle { 999991u }; - const DeviceResourceHandle fakeVertexBuffer1DeviceHandle{ 999992u }; - const DeviceResourceHandle fakeVertexBuffer2DeviceHandle{ 999993u }; - - const auto geomLayout = allocateHelper.allocateDataLayout({ indexField, vert1Field, vert2Field }, fakeEffectHash); - const auto geomInstance = allocateHelper.allocateDataInstance(geomLayout); - - scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Geometry, geomInstance); - scene.setRenderableStartVertex(renderable, 17u); - scene.setDataResource(geomInstance, DataFieldHandle{ 0u }, fakeIndexBufferHash, {}, 0u, 0u, 0u); - scene.setDataResource(geomInstance, DataFieldHandle{ 1u }, fakeVertexBuffer1Hash, {}, 11u, 12u, 13u); - scene.setDataResource(geomInstance, DataFieldHandle{ 2u }, fakeVertexBuffer2tHash, {}, 14u, 15u, 16u); - - VertexArrayInfo expectedVertexArrayInfo; - expectedVertexArrayInfo.shader = fakeEffectDeviceHandle; - expectedVertexArrayInfo.indexBuffer = fakeIndexBufferDeviceHandle; - expectedVertexArrayInfo.vertexBuffers.push_back({ fakeVertexBuffer1DeviceHandle, DataFieldHandle{1u}, 11u, 17u, EDataType::Vector2F, 12u, 13u }); - expectedVertexArrayInfo.vertexBuffers.push_back({ fakeVertexBuffer2DeviceHandle, DataFieldHandle{2u}, 14u, 17u, EDataType::Vector3F, 15u, 16u }); - - InSequence seq; - EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeEffectHash)) .WillOnce(Return(fakeEffectDeviceHandle)); - EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeIndexBufferHash)) .WillOnce(Return(fakeIndexBufferDeviceHandle)); - EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeVertexBuffer1Hash)) .WillOnce(Return(fakeVertexBuffer1DeviceHandle)); - EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeVertexBuffer2tHash)) .WillOnce(Return(fakeVertexBuffer2DeviceHandle)); - - VertexArrayInfo resultVertexArrayInfo; - EXPECT_CALL(resourceManager, uploadVertexArray(renderable, _, sceneID)).WillOnce(Invoke([&](auto, auto vai, auto) { - resultVertexArrayInfo = std::move(vai); - return DeviceResourceHandle{}; - })); - SceneResourceUploader::UploadVertexArray(scene, renderable, resourceManager); - - EXPECT_EQ(fakeEffectDeviceHandle, resultVertexArrayInfo.shader); - EXPECT_EQ(fakeIndexBufferDeviceHandle, resultVertexArrayInfo.indexBuffer); - - ASSERT_EQ(2u, resultVertexArrayInfo.vertexBuffers.size()); - - EXPECT_EQ(fakeVertexBuffer1DeviceHandle , resultVertexArrayInfo.vertexBuffers[0].deviceHandle); - EXPECT_EQ(DataFieldHandle{ 0u } , resultVertexArrayInfo.vertexBuffers[0].field); - EXPECT_EQ(11u , resultVertexArrayInfo.vertexBuffers[0].instancingDivisor); - EXPECT_EQ(17u , resultVertexArrayInfo.vertexBuffers[0].startVertex); - EXPECT_EQ(EDataType::Vector2Buffer , resultVertexArrayInfo.vertexBuffers[0].bufferDataType); - EXPECT_EQ(12u , resultVertexArrayInfo.vertexBuffers[0].offsetWithinElement); - EXPECT_EQ(13u , resultVertexArrayInfo.vertexBuffers[0].stride); - - EXPECT_EQ(fakeVertexBuffer2DeviceHandle , resultVertexArrayInfo.vertexBuffers[1].deviceHandle); - EXPECT_EQ(DataFieldHandle{ 1u } , resultVertexArrayInfo.vertexBuffers[1].field); - EXPECT_EQ(14u , resultVertexArrayInfo.vertexBuffers[1].instancingDivisor); - EXPECT_EQ(17u , resultVertexArrayInfo.vertexBuffers[1].startVertex); - EXPECT_EQ(EDataType::Vector3Buffer , resultVertexArrayInfo.vertexBuffers[1].bufferDataType); - EXPECT_EQ(15u , resultVertexArrayInfo.vertexBuffers[1].offsetWithinElement); - EXPECT_EQ(16u , resultVertexArrayInfo.vertexBuffers[1].stride); -} -} diff --git a/renderer/RendererLib/RendererLib/test/TextureLinkManagerTest.cpp b/renderer/RendererLib/RendererLib/test/TextureLinkManagerTest.cpp deleted file mode 100644 index c0df92ffa..000000000 --- a/renderer/RendererLib/RendererLib/test/TextureLinkManagerTest.cpp +++ /dev/null @@ -1,1141 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "SceneAPI/TextureSampler.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/ResourceCachedScene.h" -#include "RendererEventCollector.h" -#include "TestSceneHelper.h" -#include "SceneAllocateHelper.h" -#include "MockResourceHash.h" -#include "Utils/ThreadLocalLog.h" - -namespace ramses_internal { -using namespace testing; - -// Tests also TextureLinkCachedScene class - -class ATextureLinkManager : public ::testing::Test -{ -public: - ATextureLinkManager() - : rendererScenes(rendererEventCollector) - , sceneLinksManager(rendererScenes.getSceneLinksManager()) - , textureLinkManager(sceneLinksManager.getTextureLinkManager()) - , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) - , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) - , providerSceneAllocator(providerScene) - , consumerSceneAllocator(consumerScene) - , sceneHelper(consumerScene) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - consumerSceneAllocator.allocateTextureSampler({ {}, consumerTextureHash }, sampler); - consumerSceneAllocator.allocateTextureSampler({ {}, consumerTextureHash }, sampler2); - consumerSceneAllocator.allocateTextureSampler({ {}, consumerTextureHash }, sampler3); - consumerSceneAllocator.allocateTextureSampler({ {}, TextureSampler::ContentType::ExternalTexture, {}, InvalidMemoryHandle }, samplerExternal); - - providerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureProvider, providerId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }, providerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }, consumerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerId2, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler2 }, consumerSlotHandle2); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerId3, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler3 }, consumerSlotHandle3); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId3); - - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureConsumer, consumerExternalId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), samplerExternal }, consumerExternalSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerExternalId); - providerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureProvider, providerExternalId, NodeHandle(), DataInstanceHandle::Invalid(), {}, TextureSamplerHandle() }, providerExternalSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerExternalId, SceneId(0u), DataSlotId(0u)); - - renderable = createRenderableWithResourcesAndMakeClean(sampler); - // create a renderable with sampler2 and sampler3 as well so it is used for device handle caching - createRenderableWithResourcesAndMakeClean(sampler2); - createRenderableWithResourcesAndMakeClean(sampler3); - - renderableWithExternalTexture = createRenderableWithExternalTextureSampler(samplerExternal); - } - -protected: - RendererEvent expectRendererEvent(ERendererEventType type) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - EXPECT_EQ(1u, events.size()); - if (events.size() == 1u) - { - RendererEvent event = events.front(); - EXPECT_EQ(type, event.eventType); - return event; - } - return {}; - } - - void expectRendererEvent(ERendererEventType type, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) - { - const RendererEvent event = expectRendererEvent(type); - EXPECT_EQ(providerSId, event.providerSceneId); - EXPECT_EQ(consumerSId, event.consumerSceneId); - EXPECT_EQ(pId, event.providerdataId); - EXPECT_EQ(cId, event.consumerdataId); - } - - void expectRendererEvent(ERendererEventType type, SceneId consumerSId, DataSlotId cId, SceneId providerSId) - { - const RendererEvent event = expectRendererEvent(type); - EXPECT_EQ(consumerSId, event.consumerSceneId); - EXPECT_EQ(cId, event.consumerdataId); - EXPECT_EQ(providerSId, event.providerSceneId); - } - - void expectRendererEvent(ERendererEventType type, OffscreenBufferHandle buffer, SceneId consumerSId, DataSlotId cId) - { - const RendererEvent event = expectRendererEvent(type); - EXPECT_EQ(buffer, event.offscreenBuffer); - EXPECT_FALSE(event.streamBuffer.isValid()); - EXPECT_FALSE(event.externalBuffer.isValid()); - EXPECT_EQ(consumerSId, event.consumerSceneId); - EXPECT_EQ(cId, event.consumerdataId); - } - - void expectRendererEvent(ERendererEventType type, StreamBufferHandle buffer, SceneId consumerSId, DataSlotId cId) - { - const RendererEvent event = expectRendererEvent(type); - EXPECT_EQ(buffer, event.streamBuffer); - EXPECT_FALSE(event.offscreenBuffer.isValid()); - EXPECT_FALSE(event.externalBuffer.isValid()); - EXPECT_EQ(consumerSId, event.consumerSceneId); - EXPECT_EQ(cId, event.consumerdataId); - } - - void expectRendererEvent(ERendererEventType type, ExternalBufferHandle buffer, SceneId consumerSId, DataSlotId cId) - { - const RendererEvent event = expectRendererEvent(type); - EXPECT_EQ(buffer, event.externalBuffer); - EXPECT_FALSE(event.offscreenBuffer.isValid()); - EXPECT_FALSE(event.streamBuffer.isValid()); - EXPECT_EQ(consumerSId, event.consumerSceneId); - EXPECT_EQ(cId, event.consumerdataId); - } - - void updateConsumerSceneResourcesCache() - { - consumerScene.updateRenderableResources(sceneHelper.resourceManager); - } - - RenderableHandle createRenderableWithResourcesAndMakeClean(TextureSamplerHandle samplerHandle) - { - const RenderableHandle renderableHandle = sceneHelper.createRenderable(); - const DataInstanceHandle uniformData = consumerSceneAllocator.allocateDataInstance(sceneHelper.testUniformLayout); - sceneHelper.m_scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Uniforms, uniformData); - sceneHelper.m_scene.setDataTextureSamplerHandle(uniformData, sceneHelper.samplerField, samplerHandle); - sceneHelper.createAndAssignVertexDataInstance(renderableHandle); - sceneHelper.setResourcesToRenderable(renderableHandle); - updateConsumerSceneResourcesCache(); - EXPECT_FALSE(consumerScene.renderableResourcesDirty(renderableHandle)); - return renderableHandle; - } - - RenderableHandle createRenderableWithExternalTextureSampler(TextureSamplerHandle samplerHandle) - { - const RenderableHandle renderableHandle = sceneHelper.createRenderable(); - DataFieldInfoVector uniformDataFields(1u); - constexpr DataFieldHandle sampleDataField{ 0u }; - uniformDataFields[sampleDataField.asMemoryHandle()] = DataFieldInfo(EDataType::TextureSamplerExternal); - const auto dataLayout = consumerSceneAllocator.allocateDataLayout(uniformDataFields, MockResourceHash::EffectHash); - const auto uniformDataInstance = consumerSceneAllocator.allocateDataInstance(dataLayout); - sceneHelper.m_scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Uniforms, uniformDataInstance); - sceneHelper.m_scene.setDataTextureSamplerHandle(uniformDataInstance, sampleDataField, samplerHandle); - sceneHelper.createAndAssignVertexDataInstance(renderableHandle); - sceneHelper.setResourcesToRenderable(renderableHandle); - - updateConsumerSceneResourcesCache(); - return renderableHandle; - } - - void expectNoTextureLink() - { - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_EQ(TextureSampler::ContentType::ClientTexture, consumerScene.getTextureSampler(sampler).contentType); - EXPECT_EQ(consumerTextureHash, consumerScene.getTextureSampler(sampler).textureResource); - } - - void expectTextureLink(ResourceContentHash hash) - { - EXPECT_TRUE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_EQ(hash, textureLinkManager.getLinkedTexture(consumerSceneId, sampler)); - EXPECT_EQ(TextureSampler::ContentType::ClientTexture, consumerScene.getTextureSampler(sampler).contentType); - EXPECT_EQ(hash, consumerScene.getTextureSampler(sampler).textureResource); - } - - void expectNoBufferLink(TextureSamplerHandle samplerHandle) - { - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); - EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); - updateConsumerSceneResourcesCache(); - if (consumerScene.getTextureSampler(samplerHandle).contentType == TextureSampler::ContentType::ExternalTexture) - { - // sampler uses empty external textrue - EXPECT_EQ(DeviceMock::FakeEmptyExternalTextureDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[samplerHandle.asMemoryHandle()]); - } - else - { - // sampler uses fallback texture - EXPECT_EQ(DeviceMock::FakeTextureDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[samplerHandle.asMemoryHandle()]); - } - } - - void updateCacheAndExpectDeviceHandles(TextureSamplerHandle obSampler, TextureSamplerHandle sbSampler, TextureSamplerHandle ebSampler) - { - if (obSampler.isValid()) - EXPECT_CALL(sceneHelper.resourceManager, getOffscreenBufferColorBufferDeviceHandle(providerOffscreenBuffer)); - if (sbSampler.isValid()) - EXPECT_CALL(sceneHelper.resourceManager, getStreamBufferDeviceHandle(providerStreamBuffer)); - if(ebSampler.isValid()) - EXPECT_CALL(sceneHelper.resourceManager, getExternalBufferDeviceHandle(providerExternalBuffer)).WillOnce(Return(DeviceMock::FakeExternalTextureDeviceHandle)); - - updateConsumerSceneResourcesCache(); - - if (obSampler.isValid()) - { - EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[obSampler.asMemoryHandle()]); - } - if (sbSampler.isValid()) - { - EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[sbSampler.asMemoryHandle()]); - } - - if (ebSampler.isValid()) - { - EXPECT_EQ(DeviceMock::FakeExternalTextureDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[ebSampler.asMemoryHandle()]); - } - } - - void expectOffscreenBufferLink(TextureSamplerHandle samplerHandle, bool withResourceCacheUpdate = true) - { - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); - EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); - EXPECT_TRUE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); - EXPECT_EQ(providerOffscreenBuffer, textureLinkManager.getLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); - EXPECT_EQ(TextureSampler::ContentType::OffscreenBuffer, consumerScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(providerOffscreenBuffer.asMemoryHandle(), consumerScene.getTextureSampler(samplerHandle).contentHandle); - - if (withResourceCacheUpdate) - updateCacheAndExpectDeviceHandles(samplerHandle, {}, {}); - } - - void expectStreamBufferLink(TextureSamplerHandle samplerHandle, bool withResourceCacheUpdate = true) - { - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); - EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); - EXPECT_TRUE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); - EXPECT_EQ(providerStreamBuffer, textureLinkManager.getLinkedStreamBuffer(consumerSceneId, samplerHandle)); - EXPECT_EQ(TextureSampler::ContentType::StreamBuffer, consumerScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(providerStreamBuffer.asMemoryHandle(), consumerScene.getTextureSampler(samplerHandle).contentHandle); - - if (withResourceCacheUpdate) - updateCacheAndExpectDeviceHandles({}, samplerHandle, {}); - } - - void expectExternalBufferLink(TextureSamplerHandle samplerHandle, bool withResourceCacheUpdate = true) - { - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); - EXPECT_TRUE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); - EXPECT_EQ(providerExternalBuffer, textureLinkManager.getLinkedExternalBuffer(consumerSceneId, samplerHandle)); - EXPECT_EQ(TextureSampler::ContentType::ExternalTexture, consumerScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(providerExternalBuffer.asMemoryHandle(), consumerScene.getTextureSampler(samplerHandle).contentHandle); - - if (withResourceCacheUpdate) - updateCacheAndExpectDeviceHandles({}, {}, samplerHandle); - } - - void expectRenderableDirtinessState(RenderableHandle handle, bool dirty) - { - consumerScene.updateRenderablesResourcesDirtiness(); - EXPECT_EQ(dirty, consumerScene.renderableResourcesDirty(handle)); - } - - void setFallbackTextureToConsumerSampler(TextureSamplerHandle samplerHandle, ResourceContentHash resource, RenderBufferHandle renderBufferHandle) - { - DataSlotId dataSlotId; - DataSlotHandle dataSlot; - for (DataSlotHandle d(0u); d < consumerScene.getDataSlotCount(); ++d) - { - if (consumerScene.isDataSlotAllocated(d) && consumerScene.getDataSlot(d).attachedTextureSampler == samplerHandle) - { - dataSlot = d; - dataSlotId = consumerScene.getDataSlot(d).id; - } - } - if (dataSlot.isValid()) - { - consumerScene.releaseDataSlot(dataSlot); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerDestroyed); - } - - if (resource.isValid()) - sceneHelper.recreateSamplerWithDifferentContent(samplerHandle, resource); - else if (renderBufferHandle.isValid()) - sceneHelper.recreateSamplerWithDifferentContent(samplerHandle, renderBufferHandle); - - if (dataSlot.isValid()) - { - consumerScene.allocateDataSlot({ EDataSlotType_TextureConsumer, dataSlotId, {}, {}, {}, samplerHandle }, dataSlot); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated); - } - } - - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - SceneLinksManager& sceneLinksManager; - const TextureLinkManager& textureLinkManager; - const SceneId providerSceneId{ 3u }; - const SceneId consumerSceneId{ 4u }; - IScene& providerScene; - ResourceCachedScene& consumerScene; - SceneAllocateHelper providerSceneAllocator; - SceneAllocateHelper consumerSceneAllocator; - - TestSceneHelper sceneHelper; - RenderableHandle renderable; - RenderableHandle renderableWithExternalTexture; - - const OffscreenBufferHandle providerOffscreenBuffer{ 333u }; - const StreamBufferHandle providerStreamBuffer{ 444u }; - const ExternalBufferHandle providerExternalBuffer{ 555u }; - const DataSlotHandle providerSlotHandle{ 55u }; - const DataSlotHandle providerExternalSlotHandle{ 56u }; - const DataSlotHandle consumerSlotHandle{ 66u }; - const DataSlotHandle consumerSlotHandle2{ 77u }; - const DataSlotHandle consumerSlotHandle3{ 88u }; - const DataSlotHandle consumerExternalSlotHandle{ 89u }; - const DataSlotId providerId{ 33u }; - const DataSlotId providerExternalId{ 34u }; - const DataSlotId consumerId{ 44u }; - const DataSlotId consumerId2{ 45u }; - const DataSlotId consumerId3{ 46u }; - const DataSlotId consumerExternalId{ 47u }; - - const TextureSamplerHandle sampler{ 3u }; - const TextureSamplerHandle sampler2{ 5u }; - const TextureSamplerHandle sampler3{ 7u }; - const TextureSamplerHandle samplerExternal{ 71u }; - const ResourceContentHash providerTextureHash{ MockResourceHash::TextureHash2 }; - const ResourceContentHash consumerTextureHash{ MockResourceHash::TextureHash }; -}; - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerInitially) -{ - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinked) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - expectTextureLink(providerTextureHash); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndUnlinked) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSceneRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - rendererScenes.destroyScene(providerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - rendererScenes.destroyScene(consumerSceneId); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSlotRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - providerScene.releaseDataSlot(providerSlotHandle); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSceneUnmapped) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - sceneLinksManager.handleSceneUnmapped(providerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneUnmapped) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - - sceneLinksManager.handleSceneUnmapped(consumerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinked) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenUnlinked) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenConsumerSceneRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.handleSceneRemoved(consumerSceneId); - expectRenderableDirtinessState(renderable, true); - expectNoTextureLink(); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenConsumerSceneUnmapped) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.handleSceneUnmapped(consumerSceneId); - expectRenderableDirtinessState(renderable, true); - expectNoTextureLink(); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenProviderSceneRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.handleSceneRemoved(providerSceneId); - expectRenderableDirtinessState(renderable, true); - expectNoTextureLink(); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenProviderSceneUnmapped) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.handleSceneUnmapped(providerSceneId); - expectRenderableDirtinessState(renderable, true); - expectNoTextureLink(); -} - -TEST_F(ATextureLinkManager, doesNotMarkRenderableUsingSamplerDirtyWhenUnlinkedWithoutBeingLinked) -{ - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, consumerSceneId, consumerId, SceneId::Invalid()); - expectRenderableDirtinessState(renderable, false); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinkedAndProviderSceneRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - rendererScenes.destroyScene(providerSceneId); - expectRenderableDirtinessState(renderable, true); - expectNoTextureLink(); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinkedAndProviderSlotRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - providerScene.releaseDataSlot(providerSlotHandle); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinkedAndConsumerSlotRemoved) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectRenderableDirtinessState(renderable, true); -} - -// buffer links tests -TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinkedBuffer_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - expectOffscreenBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinkedBuffer_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - expectStreamBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinkedBuffer_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); - expectExternalBufferLink(samplerExternal); - EXPECT_FALSE(consumerScene.renderableResourcesDirty(renderableWithExternalTexture)); -} - -TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToUnknownConsumerSlot_OB) -{ - constexpr DataSlotId unknownDataId{ 131313u }; - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, unknownDataId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerOffscreenBuffer, consumerSceneId, unknownDataId); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToUnknownConsumerSlot_SB) -{ - constexpr DataSlotId unknownDataId{ 131313u }; - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, unknownDataId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerStreamBuffer, consumerSceneId, unknownDataId); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToUnknownConsumerSlot_EB) -{ - constexpr DataSlotId unknownDataId{ 131313u }; - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, unknownDataId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerExternalBuffer, consumerSceneId, unknownDataId); - expectNoBufferLink(samplerExternal); -} - -TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToConsumerSlotWithWrongType_OB) -{ - // attempt to link to a provider type slot - constexpr DataSlotId providerDataId{ 131313u }; - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureProvider, providerDataId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, consumerSceneId, providerDataId, SceneId(0u), DataSlotId(0u)); - - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, providerDataId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerOffscreenBuffer, consumerSceneId, providerDataId); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToConsumerSlotWithWrongType_SB) -{ - // attempt to link to a provider type slot - constexpr DataSlotId providerDataId{ 131313u }; - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureProvider, providerDataId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, consumerSceneId, providerDataId, SceneId(0u), DataSlotId(0u)); - - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, providerDataId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerStreamBuffer, consumerSceneId, providerDataId); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToConsumerSlotWithWrongType_EB) -{ - // attempt to link to a provider type slot - constexpr DataSlotId providerDataId{ 131313u }; - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TextureProvider, providerDataId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, consumerSceneId, providerDataId, SceneId(0u), DataSlotId(0u)); - - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, providerDataId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerExternalBuffer, consumerSceneId, providerDataId); - expectNoBufferLink(samplerExternal); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndUnlinked_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, SceneId::Invalid()); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndUnlinked_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, SceneId::Invalid()); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndUnlinked_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerExternalId, SceneId::Invalid()); - expectNoBufferLink(samplerExternal); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneRemoved_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - - rendererScenes.destroyScene(consumerSceneId); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneRemoved_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - - rendererScenes.destroyScene(consumerSceneId); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneRemoved_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); - - rendererScenes.destroyScene(consumerSceneId); - EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerExternal)); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSlotRemoved_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - expectOffscreenBufferLink(sampler); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSlotRemoved_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - expectStreamBufferLink(sampler); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSlotRemoved_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); - expectExternalBufferLink(samplerExternal); - - consumerScene.releaseDataSlot(consumerExternalSlotHandle); - expectNoBufferLink(samplerExternal); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneUnmapped_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - - sceneLinksManager.handleSceneUnmapped(consumerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneUnmapped_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - - sceneLinksManager.handleSceneUnmapped(consumerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneUnmapped_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); - - sceneLinksManager.handleSceneUnmapped(consumerSceneId); - expectNoBufferLink(samplerExternal); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndBufferRemoved_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - - sceneLinksManager.handleBufferDestroyed(providerOffscreenBuffer); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndBufferRemoved_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - - sceneLinksManager.handleBufferDestroyedOrSourceUnavailable(providerStreamBuffer); - expectNoTextureLink(); - expectNoBufferLink(sampler); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndBufferRemoved_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); - - sceneLinksManager.handleBufferDestroyed(providerExternalBuffer); - expectNoBufferLink(samplerExternal); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinked_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - consumerScene.updateRenderablesResourcesDirtiness(); - EXPECT_TRUE(consumerScene.renderableResourcesDirty(renderable)); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinked_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - consumerScene.updateRenderablesResourcesDirtiness(); - EXPECT_TRUE(consumerScene.renderableResourcesDirty(renderable)); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinked_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - consumerScene.updateRenderablesResourcesDirtiness(); - EXPECT_TRUE(consumerScene.renderableResourcesDirty(renderableWithExternalTexture)); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferUnlinked_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferUnlinked_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferUnlinked_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerExternalId); - expectRenderableDirtinessState(renderable, false); -} - -TEST_F(ATextureLinkManager, doesNotMarkRenderableUsingSamplerDirtyWhenUnlinkedWithoutBeingBufferLinked) -{ - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, consumerSceneId, consumerId, SceneId::Invalid()); - expectRenderableDirtinessState(renderable, false); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndConsumerSlotRemoved_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndConsumerSlotRemoved_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndConsumerSlotRemoved_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - updateConsumerSceneResourcesCache(); - //expectRenderableDirtinessState(renderableWithExternalTexture, false); - - consumerScene.releaseDataSlot(consumerExternalSlotHandle); - expectRenderableDirtinessState(renderableWithExternalTexture, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndBufferRemoved_OB) -{ - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.handleBufferDestroyed(providerOffscreenBuffer); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndBufferRemoved_SB) -{ - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - sceneLinksManager.handleBufferDestroyedOrSourceUnavailable(providerStreamBuffer); - expectRenderableDirtinessState(renderable, true); -} - -TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndBufferRemoved_EB) -{ - sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderableWithExternalTexture, false); - - sceneLinksManager.handleBufferDestroyed(providerExternalBuffer); - expectRenderableDirtinessState(renderableWithExternalTexture, true); -} - -TEST_F(ATextureLinkManager, reportsLinkAndMarksRenderableUsingSamplerDirtyWhenProviderChangesTextureAssignedToSlot) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectTextureLink(providerTextureHash); - updateConsumerSceneResourcesCache(); - expectRenderableDirtinessState(renderable, false); - - const ResourceContentHash newTexture(123u, 456u); - providerScene.setDataSlotTexture(providerSlotHandle, newTexture); - expectTextureLink(newTexture); - expectRenderableDirtinessState(renderable, true); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectNoTextureLink(); -} - -// combined buffer and texture links tests -TEST_F(ATextureLinkManager, canCreateTextureLinkAndBufferLink) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - expectTextureLink(providerTextureHash); - - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - expectOffscreenBufferLink(sampler2); - - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - expectStreamBufferLink(sampler3); -} - -TEST_F(ATextureLinkManager, differentTypesOfLinksOverwriteEachOther) -{ - // OB - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - expectOffscreenBufferLink(sampler); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); - - // OB -> tex - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - expectTextureLink(providerTextureHash); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); - - // tex -> OB - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - expectOffscreenBufferLink(sampler); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); - - // OB -> SB - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - expectStreamBufferLink(sampler); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - - // SB -> tex - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - expectTextureLink(providerTextureHash); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); - - // tex -> SB - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); - expectStreamBufferLink(sampler); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); - - // SB -> OB - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); - expectOffscreenBufferLink(sampler); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSceneRemoved_KeepsBufferLink) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - rendererScenes.destroyScene(providerSceneId); - expectNoTextureLink(); - expectOffscreenBufferLink(sampler2, false); - expectStreamBufferLink(sampler3, false); - updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndBufferRemoved_KeepsTextureLink_OB) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - - sceneLinksManager.handleBufferDestroyed(providerOffscreenBuffer); - expectTextureLink(providerTextureHash); - expectNoBufferLink(sampler2); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndBufferRemoved_KeepsTextureLink_SB) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId2); - - sceneLinksManager.handleBufferDestroyedOrSourceUnavailable(providerStreamBuffer); - expectTextureLink(providerTextureHash); - expectNoBufferLink(sampler2); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneRemoved_BufferAndTextureLinks) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - rendererScenes.destroyScene(consumerSceneId); - EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); - EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler2)); - EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler3)); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSlotRemoved_KeepsBufferLink) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - providerScene.releaseDataSlot(providerSlotHandle); - expectNoTextureLink(); - expectOffscreenBufferLink(sampler2, false); - expectStreamBufferLink(sampler3, false); - updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved_KeepsBufferLink) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - consumerScene.releaseDataSlot(consumerSlotHandle); - expectNoTextureLink(); - expectOffscreenBufferLink(sampler2, false); - expectStreamBufferLink(sampler3, false); - updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved_KeepsTextureLink_OB) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - - consumerScene.releaseDataSlot(consumerSlotHandle2); - expectTextureLink(providerTextureHash); - expectNoBufferLink(sampler2); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved_KeepsTextureLink_SB) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId2); - - consumerScene.releaseDataSlot(consumerSlotHandle2); - expectTextureLink(providerTextureHash); - expectNoBufferLink(sampler2); -} - -TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneUnmapped_BufferAndTextureLinks) -{ - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - sceneLinksManager.handleSceneUnmapped(consumerSceneId); - expectNoTextureLink(); - expectNoBufferLink(sampler2); - expectNoBufferLink(sampler3); -} - -TEST_F(ATextureLinkManager, canCreateTextureLinkAndBufferLinkIfPreviouslyUsingRenderTarget) -{ - const RenderBufferHandle renderBuffer(14u); - setFallbackTextureToConsumerSampler(sampler, {}, renderBuffer); - setFallbackTextureToConsumerSampler(sampler2, {}, renderBuffer); - setFallbackTextureToConsumerSampler(sampler3, {}, renderBuffer); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - expectTextureLink(providerTextureHash); - expectOffscreenBufferLink(sampler2, false); - expectStreamBufferLink(sampler3, false); - updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); -} - -TEST_F(ATextureLinkManager, unlinkingTextureAndBufferFallsBackToPreviouslySetRenderTarget) -{ - const RenderBufferHandle renderBuffer(14u); - setFallbackTextureToConsumerSampler(sampler, {}, renderBuffer); - setFallbackTextureToConsumerSampler(sampler2, {}, renderBuffer); - setFallbackTextureToConsumerSampler(sampler3, {}, renderBuffer); - - sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); - sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); - sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); - - expectTextureLink(providerTextureHash); - expectOffscreenBufferLink(sampler2, false); - expectStreamBufferLink(sampler3, false); - updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); - - sceneLinksManager.removeDataLink(consumerSceneId, consumerId); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); - sceneLinksManager.removeDataLink(consumerSceneId, consumerId2); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId2, SceneId::Invalid()); - sceneLinksManager.removeDataLink(consumerSceneId, consumerId3); - expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId3, SceneId::Invalid()); - - EXPECT_EQ(TextureSampler::ContentType::RenderBuffer, consumerScene.getTextureSampler(sampler).contentType); - EXPECT_EQ(TextureSampler::ContentType::RenderBuffer, consumerScene.getTextureSampler(sampler2).contentType); - EXPECT_EQ(TextureSampler::ContentType::RenderBuffer, consumerScene.getTextureSampler(sampler3).contentType); - EXPECT_EQ(renderBuffer.asMemoryHandle(), consumerScene.getTextureSampler(sampler).contentHandle); - EXPECT_EQ(renderBuffer.asMemoryHandle(), consumerScene.getTextureSampler(sampler2).contentHandle); - EXPECT_EQ(renderBuffer.asMemoryHandle(), consumerScene.getTextureSampler(sampler3).contentHandle); -} -} diff --git a/renderer/RendererLib/RendererLib/test/TransformationLinkManagerTest.cpp b/renderer/RendererLib/RendererLib/test/TransformationLinkManagerTest.cpp deleted file mode 100644 index b958aa0e8..000000000 --- a/renderer/RendererLib/RendererLib/test/TransformationLinkManagerTest.cpp +++ /dev/null @@ -1,364 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gtest/gtest.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/TransformationLinkCachedScene.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" -#include "SceneAllocateHelper.h" -#include "Utils/ThreadLocalLog.h" -#include "TestEqualHelper.h" -#include "glm/gtx/transform.hpp" - -using namespace testing; -using namespace ramses_internal; - -class ATransformationLinkManager : public ::testing::Test -{ -public: - ATransformationLinkManager() - : rendererScenes(rendererEventCollector) - , sceneLinksManager(rendererScenes.getSceneLinksManager()) - , transformationLinkManager(const_cast(sceneLinksManager.getTransformationLinkManager())) - , providerSceneId(3u) - , consumerSceneId(4u) - , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) - , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) - , providerSceneAllocator(providerScene) - , consumerSceneAllocator(consumerScene) - , providerNode(8u) - , consumerNode(12u) - , providerTransform(9u) - , providerSlotHandle(55u) - , consumerSlotHandle(66u) - , providerId(33u) - , consumerId(44u) - { - // caller is expected to have a display prefix for logs - ThreadLocalLog::SetPrefix(1); - - providerSceneAllocator.allocateNode(0u, providerNode); - consumerSceneAllocator.allocateNode(0u, consumerNode); - - providerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationProvider, providerId, providerNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, providerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerId, consumerNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumerSlotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); - } - -protected: - void expectRendererEvent(ERendererEventType event, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(event, events.front().eventType); - EXPECT_EQ(providerSId, events.front().providerSceneId); - EXPECT_EQ(consumerSId, events.front().consumerSceneId); - EXPECT_EQ(pId, events.front().providerdataId); - EXPECT_EQ(cId, events.front().consumerdataId); - } - - void expectRendererEvent(ERendererEventType event, SceneId consumerSId, DataSlotId cId) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - ASSERT_EQ(1u, events.size()); - EXPECT_EQ(event, events.front().eventType); - EXPECT_EQ(consumerSId, events.front().consumerSceneId); - EXPECT_EQ(cId, events.front().consumerdataId); - } - - void expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType destroyedSlotType, SceneId sceneIdOfDestroyedSlot, DataSlotId destroyedSlotId, SceneId consumerSId, DataSlotId cId, SceneId consumerSceneId2 = SceneId(0u), DataSlotId consumerId2 = DataSlotId(0u)) - { - RendererEventVector events; - RendererEventVector dummy; - rendererEventCollector.appendAndConsumePendingEvents(dummy, events); - - const bool hasTwoLinksRemoved = (consumerId2.getValue() != 0u); - if (hasTwoLinksRemoved) - { - ASSERT_EQ(3u, events.size()); - } - else - { - ASSERT_EQ(2u, events.size()); - } - - uint32_t eventIdx = 0u; - - EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); - EXPECT_EQ(consumerSId, events[eventIdx].consumerSceneId); - EXPECT_EQ(cId, events[eventIdx].consumerdataId); - - if (hasTwoLinksRemoved) - { - ++eventIdx; - EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); - EXPECT_EQ(consumerSceneId2, events[eventIdx].consumerSceneId); - EXPECT_EQ(consumerId2, events[eventIdx].consumerdataId); - } - - ++eventIdx; - if (destroyedSlotType == EDataSlotType_TransformationProvider) - { - EXPECT_EQ(ERendererEventType::SceneDataSlotProviderDestroyed, events[eventIdx].eventType); - EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].providerSceneId); - EXPECT_EQ(destroyedSlotId, events[eventIdx].providerdataId); - } - else - { - EXPECT_EQ(ERendererEventType::SceneDataSlotConsumerDestroyed, events[eventIdx].eventType); - EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].consumerSceneId); - EXPECT_EQ(destroyedSlotId, events[eventIdx].consumerdataId); - } - } - - RendererEventCollector rendererEventCollector; - RendererScenes rendererScenes; - SceneLinksManager& sceneLinksManager; - TransformationLinkManager& transformationLinkManager; - - const SceneId providerSceneId; - const SceneId consumerSceneId; - TransformationLinkCachedScene& providerScene; - TransformationLinkCachedScene& consumerScene; - SceneAllocateHelper providerSceneAllocator; - SceneAllocateHelper consumerSceneAllocator; - - const NodeHandle providerNode; - const NodeHandle consumerNode; - const TransformHandle providerTransform; - - const DataSlotHandle providerSlotHandle; - const DataSlotHandle consumerSlotHandle; - const DataSlotId providerId; - const DataSlotId consumerId; -}; - -TEST_F(ATransformationLinkManager, reportsNoLinkForInvalidNode) -{ - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(providerSceneId, NodeHandle())); -} - -TEST_F(ATransformationLinkManager, reportsNoLinkForNonlinkedNode) -{ - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(providerSceneId, providerNode)); -} - -TEST_F(ATransformationLinkManager, reportsExistingLinkForNodeAssociatedWithLink) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, reportsNoLinkToProviderForProviderNode) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(providerSceneId, providerNode)); -} - -TEST_F(ATransformationLinkManager, retrievesWorldTransformationFromLinkedProviderNode) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - providerSceneAllocator.allocateTransform(providerNode, providerTransform); - const glm::vec3 transVec(1.f, 2.f, 3.f); - providerScene.setTranslation(providerTransform, transVec); - - const auto expectedMat = glm::translate(transVec); - const auto transMat = transformationLinkManager.getLinkedTransformationFromDataProvider(ETransformationMatrixType_World, consumerSceneId, consumerNode); - - expectMatrixFloatEqual(expectedMat, transMat); -} - -TEST_F(ATransformationLinkManager, retrievesObjectTransformationFromLinkedProviderNode) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - providerSceneAllocator.allocateTransform(providerNode, providerTransform); - const glm::vec3 transVec(1.f, 2.f, 3.f); - providerScene.setTranslation(providerTransform, transVec); - - const auto expectedMat = glm::translate(-transVec); - const auto transMat = transformationLinkManager.getLinkedTransformationFromDataProvider(ETransformationMatrixType_Object, consumerSceneId, consumerNode); - - expectMatrixFloatEqual(expectedMat, transMat); -} - -TEST_F(ATransformationLinkManager, reportsNoLinksForSceneWithRemovedLink) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle)); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, reportsNoLinksForConsumerSceneIfDestroyed) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - rendererScenes.destroyScene(consumerSceneId); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, reportsNoLinksForConsumerSceneIfProviderSceneDestroyed) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - rendererScenes.destroyScene(providerSceneId); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, canLinkUnlinkAndLinkAgain) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - - EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, removesLinkForSceneWithRemovedProviderSlot) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - providerScene.releaseDataSlot(providerSlotHandle); - expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType_TransformationProvider, providerSceneId, providerId, consumerSceneId, consumerId); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, removesLinkForSceneWithRemovedConsumerSlot) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - consumerScene.releaseDataSlot(consumerSlotHandle); - expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType_TransformationConsumer, consumerSceneId, consumerId, consumerSceneId, consumerId); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); -} - -TEST_F(ATransformationLinkManager, createsTwoLinksSameProviderDifferentConsumers) -{ - const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle(43u); - - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, slotHandle)); - EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); - EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode2)); -} - -TEST_F(ATransformationLinkManager, removesAllLinksToProviderOnProviderSlotDestruction) -{ - const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); - const DataSlotId consumerId2(999u); - const DataSlotHandle slotHandle(43u); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, slotHandle)); - - providerScene.releaseDataSlot(providerSlotHandle); - expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType_TransformationProvider, providerSceneId, providerId, consumerSceneId, consumerId, consumerSceneId, consumerId2); - - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode2)); -} - -TEST_F(ATransformationLinkManager, removingAllLinksToProviderOnProviderSlotDestructionDoesNotAffectOtherLinks) -{ - const NodeHandle providerNode2 = providerSceneAllocator.allocateNode(); - const DataSlotId providerId2(999u); - const DataSlotHandle slotHandle(43u); - providerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationProvider, providerId2, providerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); - expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); - - const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); - const DataSlotId consumerId2(888u); - const DataSlotHandle slotHandle2(654u); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle2); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, slotHandle, consumerSceneId, slotHandle2)); - - providerScene.releaseDataSlot(providerSlotHandle); - expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType_TransformationProvider, providerSceneId, providerId, consumerSceneId, consumerId); - - EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); - EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode2)); -} - -void markNodeTransformationClean(const TransformationLinkCachedScene& scene, NodeHandle node) -{ - scene.updateMatrixCache(ETransformationMatrixType_World, node); - EXPECT_FALSE(scene.isMatrixCacheDirty(ETransformationMatrixType_World, node)); -} - -TEST_F(ATransformationLinkManager, canPropagateTransformationDirtinessToConsumerScene) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - markNodeTransformationClean(consumerScene, consumerNode); - - transformationLinkManager.propagateTransformationDirtinessToConsumers(providerSceneId, providerNode); - EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); -} - -TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenCreatingLink) -{ - markNodeTransformationClean(consumerScene, consumerNode); - - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); -} - -TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenRemovingLink) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - markNodeTransformationClean(consumerScene, consumerNode); - - EXPECT_TRUE(transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle)); - EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); -} - -TEST_F(ATransformationLinkManager, doesNotPropagateTransformationDirtinessToConsumerWhenRemovingLinkFailed) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - markNodeTransformationClean(consumerScene, consumerNode); - - const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); - const DataSlotId consumerId2(888u); - const DataSlotHandle slotHandle2(654u); - consumerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle2); - expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); - - EXPECT_FALSE(transformationLinkManager.removeDataLink(consumerSceneId, slotHandle2)); - EXPECT_FALSE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); -} - -TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenNodeTransformationModified) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - markNodeTransformationClean(consumerScene, consumerNode); - - providerSceneAllocator.allocateTransform(providerNode, providerTransform); - providerScene.setTranslation(providerTransform, glm::vec3(1.f)); - EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); -} - -TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenProviderSceneDestroyed) -{ - EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); - markNodeTransformationClean(consumerScene, consumerNode); - rendererScenes.destroyScene(providerSceneId); - - EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); -} diff --git a/renderer/RendererLib/RendererTestCommon/DisplayControllerMock.cpp b/renderer/RendererLib/RendererTestCommon/DisplayControllerMock.cpp deleted file mode 100644 index c8d544e5a..000000000 --- a/renderer/RendererLib/RendererTestCommon/DisplayControllerMock.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DisplayControllerMock.h" -#include "WindowMock.h" -namespace ramses_internal { - -const DeviceResourceHandle DisplayControllerMock::FakeFrameBufferHandle(15); -const ProjectionParams DisplayControllerMock::FakeProjectionParams(ProjectionParams::Perspective(30.0f, 2.666f, 0.00001f, 100.f)); - -DisplayControllerMock::DisplayControllerMock() -{ - using namespace ::testing; - ON_CALL(*this, getDisplayBuffer()).WillByDefault(Return(FakeFrameBufferHandle)); - ON_CALL(*this, getDisplayWidth()).WillByDefault(Return(WindowMock::FakeWidth)); - ON_CALL(*this, getDisplayHeight()).WillByDefault(Return(WindowMock::FakeHeight)); - ON_CALL(*this, renderScene(_, _, _)).WillByDefault(Return(SceneRenderExecutionIterator())); -} - -DisplayControllerMock::~DisplayControllerMock() -{ -} -} diff --git a/renderer/RendererLib/RendererTestCommon/DisplayControllerMock.h b/renderer/RendererLib/RendererTestCommon/DisplayControllerMock.h deleted file mode 100644 index 3b26ea9ba..000000000 --- a/renderer/RendererLib/RendererTestCommon/DisplayControllerMock.h +++ /dev/null @@ -1,48 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DISPLAYCONTROLLERMOCK_H -#define RAMSES_DISPLAYCONTROLLERMOCK_H - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererAPI/IDisplayController.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "RendererAPI/RenderingContext.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/RendererLogContext.h" -#include "Math3d/CameraMatrixHelper.h" - -namespace ramses_internal{ - -class DisplayControllerMock : public IDisplayController -{ -public: - DisplayControllerMock(); - ~DisplayControllerMock() override; - - static const DeviceResourceHandle FakeFrameBufferHandle; - static const ProjectionParams FakeProjectionParams; - - MOCK_METHOD(void, handleWindowEvents, (), (override)); - MOCK_METHOD(bool, canRenderNewFrame, (), (const, override)); - MOCK_METHOD(void, enableContext, (), (override)); - MOCK_METHOD(void, swapBuffers, (), (override)); - MOCK_METHOD(void, clearBuffer, (DeviceResourceHandle, uint32_t clearFlags, const glm::vec4&), (override)); - MOCK_METHOD(SceneRenderExecutionIterator, renderScene, (const RendererCachedScene&, RenderingContext&, const FrameTimer*), (override)); - MOCK_METHOD(DeviceResourceHandle, getDisplayBuffer, (), (const, override)); - MOCK_METHOD(void, readPixels, (DeviceResourceHandle framebufferHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, std::vector& dataOut), (override)); - MOCK_METHOD(uint32_t, getDisplayWidth, (), (const, override)); - MOCK_METHOD(uint32_t, getDisplayHeight, (), (const, override)); - MOCK_METHOD(IRenderBackend&, getRenderBackend, (), (const, override)); - MOCK_METHOD(IEmbeddedCompositingManager&, getEmbeddedCompositingManager, (), (override)); - MOCK_METHOD(void, validateRenderingStatusHealthy, (), (const, override)); -}; -} -#endif diff --git a/renderer/RendererLib/RendererTestCommon/MockResourceHash.h b/renderer/RendererLib/RendererTestCommon/MockResourceHash.h deleted file mode 100644 index ef91e2d8b..000000000 --- a/renderer/RendererLib/RendererTestCommon/MockResourceHash.h +++ /dev/null @@ -1,53 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_MOCKRESOURCEHASH_H -#define RAMSES_MOCKRESOURCEHASH_H - -#include "SceneAPI/ResourceContentHash.h" -#include "Resource/ArrayResource.h" -#include "Resource/EffectResource.h" -#include "Resource/TextureResource.h" -#include "Components/ManagedResource.h" - -namespace ramses_internal -{ -namespace MockResourceHash -{ - static constexpr ResourceContentHash EffectHash{ 120u, 0u }; - static constexpr ResourceContentHash VertArrayHash{ 123u, 0u }; - static constexpr ResourceContentHash VertArrayHash2{ 124u, 0u }; - static constexpr ResourceContentHash IndexArrayHash{ 125u, 0u }; - static constexpr ResourceContentHash IndexArrayHash2{ 126u, 0u }; - static constexpr ResourceContentHash IndexArrayHash3{ 127u, 0u }; - static constexpr ResourceContentHash TextureHash{ 128u, 0u }; - static constexpr ResourceContentHash TextureHash2{ 129u, 0u }; - - static inline ManagedResource GetManagedResource(const ResourceContentHash& hash) - { - std::unique_ptr res; - if (hash == EffectHash) - res = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag_DoNotCache); - else if (hash == VertArrayHash || hash == VertArrayHash2) - res = std::make_unique(EResourceType_VertexArray, 0, EDataType::Float, nullptr, ResourceCacheFlag_DoNotCache, std::string_view{}); - else if (hash == IndexArrayHash || hash == IndexArrayHash2 || hash == IndexArrayHash3) - res = std::make_unique(EResourceType_IndexArray, 0, EDataType::UInt16, nullptr, ResourceCacheFlag_DoNotCache, std::string_view{}); - else if (hash == TextureHash) - res = std::make_unique(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, std::string_view{}); - else if (hash == TextureHash2) - res = std::make_unique(EResourceType_Texture2D, TextureMetaInfo(2u, 2u, 1u, ETextureFormat::R8, true, {}, { 4u }), ResourceCacheFlag_DoNotCache, std::string_view{}); - - if (res) - res->setResourceData(ResourceBlob{ 1 }, hash); - - return res; - } -} -} - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/RendererMock.cpp b/renderer/RendererLib/RendererTestCommon/RendererMock.cpp deleted file mode 100644 index 9f62935c2..000000000 --- a/renderer/RendererLib/RendererTestCommon/RendererMock.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererMock.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/RendererScenes.h" -#include "RendererAPI/IDisplayController.h" -#include "RendererAPI/ISystemCompositorController.h" -#include "RendererLib/RendererStatistics.h" - -namespace ramses_internal { -using namespace testing; - -const FrameTimer RendererMock::FrameTimerInstance; - -RendererMock::RendererMock(DisplayHandle display, const IPlatform& platform, const RendererScenes& rendererScenes, - const RendererEventCollector& eventCollector, const SceneExpirationMonitor& expirationMonitor, const RendererStatistics& statistics) - : Renderer(display, const_cast(platform), const_cast(rendererScenes), - const_cast(eventCollector), FrameTimerInstance, const_cast(expirationMonitor), const_cast(statistics)) -{ - // by default do not track modified scenes, concrete tests will override - EXPECT_CALL(*this, markBufferWithSceneForRerender(_)).Times(AnyNumber()); - // by default do not track set clear color, concrete tests will override - EXPECT_CALL(*this, setClearColor(_, _)).Times(AnyNumber()); -} - -RendererMock::~RendererMock() = default; - -template class MOCK_TYPE> -RendererMockWithMockDisplay::RendererMockWithMockDisplay(DisplayHandle display, const RendererScenes& rendererScenes, - const RendererEventCollector& eventCollector, const SceneExpirationMonitor& expirationMonitor, const RendererStatistics& statistics) - : RendererMock(display, m_platform, const_cast(rendererScenes), - const_cast(eventCollector), const_cast(expirationMonitor), const_cast(statistics)) -{ -} - -template class MOCK_TYPE> -RendererMockWithMockDisplay::~RendererMockWithMockDisplay() = default; - -template class MOCK_TYPE> -IDisplayController* RendererMockWithMockDisplay::createDisplayControllerFromConfig(const DisplayConfig& displayConfig) -{ - assert(m_displayController == nullptr); - m_displayController = new MOCK_TYPE < DisplayControllerMock >; - - ON_CALL(*m_displayController, getRenderBackend()).WillByDefault(ReturnRef(m_platform.renderBackendMock)); - ON_CALL(*m_displayController, getEmbeddedCompositingManager()).WillByDefault(ReturnRef(m_embeddedCompositingManager)); - - EXPECT_CALL(*m_displayController, getDisplayWidth()).Times(AnyNumber()).WillRepeatedly(Return(displayConfig.getDesiredWindowWidth())); - EXPECT_CALL(*m_displayController, getDisplayHeight()).Times(AnyNumber()).WillRepeatedly(Return(displayConfig.getDesiredWindowHeight())); - EXPECT_CALL(*m_displayController, getDisplayBuffer()).Times(AnyNumber()); - EXPECT_CALL(*m_displayController, getRenderBackend()).Times(AnyNumber()); - EXPECT_CALL(*m_displayController, getEmbeddedCompositingManager()).Times(AnyNumber()); - - EXPECT_CALL(m_platform.renderBackendMock.contextMock, disable()).Times(AtMost(1)).WillRepeatedly(Return(true)); - EXPECT_CALL(m_platform.renderBackendMock.contextMock, enable()).Times(AtMost(1)).WillRepeatedly(Return(true)); - - EXPECT_CALL(*this, setClearColor(_, displayConfig.getClearColor())); - - return m_displayController; -} - -template class MOCK_TYPE> -void RendererMockWithMockDisplay::markBufferWithSceneForRerender(SceneId sceneId) -{ - Renderer::markBufferWithSceneForRerender(sceneId); // NOLINT clang-tidy: We really mean to call into Renderer - RendererMock::markBufferWithSceneForRerender(sceneId); -} - -template class MOCK_TYPE> -void ramses_internal::RendererMockWithMockDisplay::setClearFlags(DeviceResourceHandle bufferDeviceHandle, uint32_t clearFlags) -{ - Renderer::setClearFlags(bufferDeviceHandle, clearFlags); // NOLINT clang-tidy: We really mean to call into Renderer - RendererMock::setClearFlags(bufferDeviceHandle, clearFlags); -} - -template class MOCK_TYPE> -void ramses_internal::RendererMockWithMockDisplay::setClearColor(DeviceResourceHandle bufferDeviceHandle, const glm::vec4& clearColor) -{ - Renderer::setClearColor(bufferDeviceHandle, clearColor); // NOLINT clang-tidy: We really mean to call into Renderer - RendererMock::setClearColor(bufferDeviceHandle, clearColor); -} - -template class RendererMockWithMockDisplay < NiceMock >; -template class RendererMockWithMockDisplay < StrictMock >; -} diff --git a/renderer/RendererLib/RendererTestCommon/RendererResourceCacheFake.h b/renderer/RendererLib/RendererTestCommon/RendererResourceCacheFake.h deleted file mode 100644 index 54de15f57..000000000 --- a/renderer/RendererLib/RendererTestCommon/RendererResourceCacheFake.h +++ /dev/null @@ -1,60 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERRESOURCECACHEFAKE_H -#define RAMSES_RENDERERRESOURCECACHEFAKE_H - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererAPI/IRendererResourceCache.h" -#include "SceneAPI/ResourceContentHash.h" -#include "RendererResourceCacheMock.h" -#include "Collections/Vector.h" -#include "PlatformAbstraction/PlatformMemory.h" - -namespace ramses_internal { - -class RendererResourceCacheFake : public RendererResourceCacheMock -{ -public: - - bool hasResource(ResourceContentHash resourceId, uint32_t& size) const override - { - RendererResourceCacheMock::hasResource(resourceId, size); - - size = static_cast(m_data.size()); - return !m_data.empty(); - } - - bool getResourceData(ResourceContentHash resourceId, uint8_t* buffer, uint32_t bufferSize) const override - { - RendererResourceCacheMock::getResourceData(resourceId, buffer, bufferSize); - - if (m_data.empty()) - { - return false; - } - - PlatformMemory::Copy(buffer, m_data.data(), m_data.size()); - return true; - } - - void storeResource(ResourceContentHash resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, ramses_internal::ResourceCacheFlag cacheFlag, ramses_internal::SceneId sceneId) override - { - RendererResourceCacheMock::storeResource(resourceId, resourceData, resourceDataSize, cacheFlag, sceneId); - - m_data.resize(resourceDataSize); - PlatformMemory::Copy(m_data.data(), resourceData, resourceDataSize); - } - -private: - - std::vector m_data; -}; -} -#endif diff --git a/renderer/RendererLib/RendererTestCommon/RendererResourceCacheMock.h b/renderer/RendererLib/RendererTestCommon/RendererResourceCacheMock.h deleted file mode 100644 index 07c71222f..000000000 --- a/renderer/RendererLib/RendererTestCommon/RendererResourceCacheMock.h +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERRESOURCECACHEMOCK_H -#define RAMSES_RENDERERRESOURCECACHEMOCK_H - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererAPI/IRendererResourceCache.h" -#include "SceneAPI/ResourceContentHash.h" - -namespace ramses_internal{ - -class RendererResourceCacheMock : public IRendererResourceCache -{ -public: - - MOCK_METHOD(bool, hasResource, (ResourceContentHash, uint32_t&), (const, override)); - MOCK_METHOD(bool, getResourceData, (ResourceContentHash, uint8_t*, uint32_t), (const, override)); - MOCK_METHOD(bool, shouldResourceBeCached, (ResourceContentHash, uint32_t, ResourceCacheFlag, SceneId), (const, override)); - MOCK_METHOD(void, storeResource, (ResourceContentHash, const uint8_t*, uint32_t, ResourceCacheFlag, SceneId), (override)); -}; -} -#endif diff --git a/renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.cpp b/renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.cpp deleted file mode 100644 index b833343b2..000000000 --- a/renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.cpp +++ /dev/null @@ -1,149 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererResourceManagerMock.h" -#include "DeviceMock.h" -#include "PlatformAbstraction/FmtBase.h" -#include "MockResourceHash.h" - -#include - -namespace ramses_internal { -using namespace testing; - -RendererResourceManagerMock::RendererResourceManagerMock() -{ - // report common fake resources as uploaded by default - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::EffectHash)).WillByDefault(Return(DeviceMock::FakeShaderDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::VertArrayHash)).WillByDefault(Return(DeviceMock::FakeVertexBufferDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::VertArrayHash2)).WillByDefault(Return(DeviceMock::FakeVertexBufferDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::IndexArrayHash3)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::TextureHash)).WillByDefault(Return(DeviceMock::FakeTextureDeviceHandle)); - ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::TextureHash2)).WillByDefault(Return(DeviceMock::FakeTextureDeviceHandle)); - - ON_CALL(*this, getResourceStatus(MockResourceHash::EffectHash)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::VertArrayHash)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::VertArrayHash2)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::IndexArrayHash)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::IndexArrayHash2)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::IndexArrayHash3)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::TextureHash)).WillByDefault(Return(EResourceStatus::Uploaded)); - ON_CALL(*this, getResourceStatus(MockResourceHash::TextureHash2)).WillByDefault(Return(EResourceStatus::Uploaded)); - - ON_CALL(*this, getResourceType(MockResourceHash::EffectHash)).WillByDefault(Return(EResourceType_Effect)); - ON_CALL(*this, getResourceType(MockResourceHash::VertArrayHash)).WillByDefault(Return(EResourceType_VertexArray)); - ON_CALL(*this, getResourceType(MockResourceHash::VertArrayHash2)).WillByDefault(Return(EResourceType_VertexArray)); - ON_CALL(*this, getResourceType(MockResourceHash::IndexArrayHash)).WillByDefault(Return(EResourceType_IndexArray)); - ON_CALL(*this, getResourceType(MockResourceHash::IndexArrayHash2)).WillByDefault(Return(EResourceType_IndexArray)); - ON_CALL(*this, getResourceType(MockResourceHash::IndexArrayHash3)).WillByDefault(Return(EResourceType_IndexArray)); - ON_CALL(*this, getResourceType(MockResourceHash::TextureHash)).WillByDefault(Return(EResourceType_Texture2D)); - ON_CALL(*this, getResourceType(MockResourceHash::TextureHash2)).WillByDefault(Return(EResourceType_Texture2D)); - - ON_CALL(*this, getRenderTargetDeviceHandle(_, _)).WillByDefault(Return(DeviceMock::FakeRenderTargetDeviceHandle)); - ON_CALL(*this, getRenderTargetBufferDeviceHandle(_, _)).WillByDefault(Return(DeviceMock::FakeRenderBufferDeviceHandle)); - ON_CALL(*this, getOffscreenBufferDeviceHandle(_)).WillByDefault(Return(DeviceMock::FakeRenderTargetDeviceHandle)); - ON_CALL(*this, getOffscreenBufferColorBufferDeviceHandle(_)).WillByDefault(Return(DeviceMock::FakeRenderBufferDeviceHandle)); - ON_CALL(*this, getOffscreenBufferHandle(DeviceResourceHandle::Invalid())).WillByDefault(Return(OffscreenBufferHandle::Invalid())); - ON_CALL(*this, getStreamBufferDeviceHandle(_)).WillByDefault(Return(DeviceMock::FakeRenderTargetDeviceHandle)); - ON_CALL(*this, getVertexArrayDeviceHandle(_, _)).WillByDefault(Return(DeviceMock::FakeVertexArrayDeviceHandle)); - ON_CALL(*this, getExternalBufferDeviceHandle(Ne(ExternalBufferHandle::Invalid()))).WillByDefault(Return(DeviceMock::FakeExternalTextureDeviceHandle)); - ON_CALL(*this, getEmptyExternalBufferDeviceHandle()).WillByDefault(Return(DeviceMock::FakeEmptyExternalTextureDeviceHandle)); - - ON_CALL(*this, getBlitPassRenderTargetsDeviceHandle(_, _, _, _)).WillByDefault(DoAll(SetArgReferee<2>(DeviceMock::FakeBlitPassRenderTargetDeviceHandle), SetArgReferee<3>(DeviceMock::FakeBlitPassRenderTargetDeviceHandle))); - - // no need to strictly test getters - EXPECT_CALL(*this, getResourceDeviceHandle(_)).Times(AnyNumber()); - EXPECT_CALL(*this, getDataBufferDeviceHandle(_, _)).Times(AnyNumber()); - EXPECT_CALL(*this, getVertexArrayDeviceHandle(_, _)).Times(AnyNumber()); - EXPECT_CALL(*this, getTextureBufferDeviceHandle(_, _)).Times(AnyNumber()); - EXPECT_CALL(*this, getRenderTargetDeviceHandle(_, _)).Times(AnyNumber()); - EXPECT_CALL(*this, getRenderTargetBufferDeviceHandle(_, _)).Times(AnyNumber()); - EXPECT_CALL(*this, getBlitPassRenderTargetsDeviceHandle(_, _, _, _)).Times(AnyNumber()); - EXPECT_CALL(*this, getOffscreenBufferColorBufferDeviceHandle(_)).Times(AnyNumber()); - EXPECT_CALL(*this, getStreamBufferDeviceHandle(_)).Times(AnyNumber()); - EXPECT_CALL(*this, getResourcesInUseByScene(_)).Times(AnyNumber()); - EXPECT_CALL(*this, getExternalBufferDeviceHandle(_)).Times(AnyNumber()); - EXPECT_CALL(*this, getEmptyExternalBufferDeviceHandle()).Times(AnyNumber()); -} - -RendererResourceManagerRefCountMock::~RendererResourceManagerRefCountMock() -{ - expectNoResourceReferences(); -} - -void RendererResourceManagerRefCountMock::referenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) -{ - for (const auto r : resources) - m_refCounts[sceneId][r]++; - RendererResourceManagerMock::referenceResourcesForScene(sceneId, resources); -} - -void RendererResourceManagerRefCountMock::unreferenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) -{ - for (const auto r : resources) - { - m_refCounts[sceneId][r]--; - ASSERT_GE(m_refCounts[sceneId][r], 0) << fmt::format("Resource {} from scene {} has {} refs", r, sceneId, m_refCounts[sceneId][r]).c_str(); - } - RendererResourceManagerMock::unreferenceResourcesForScene(sceneId, resources); -} - -void RendererResourceManagerRefCountMock::unreferenceAllResourcesForScene(SceneId sceneId) -{ - m_refCounts.erase(sceneId); - RendererResourceManagerMock::unreferenceAllResourcesForScene(sceneId); -} - -const ResourceContentHashVector* RendererResourceManagerRefCountMock::getResourcesInUseByScene(SceneId sceneId) const -{ - RendererResourceManagerMock::getResourcesInUseByScene(sceneId); - - const auto it = m_refCounts.find(sceneId); - if (it == m_refCounts.cend()) - return nullptr; - - m_tempUsedResources.clear(); - for (const auto& r : it->second) - if (r.second > 0) - m_tempUsedResources.push_back(r.first); - - return &m_tempUsedResources; -} - -void RendererResourceManagerRefCountMock::expectNoResourceReferencesForScene(SceneId sceneId) const -{ - const auto it = m_refCounts.find(sceneId); - if (it != m_refCounts.cend()) - { - for (const auto& r : it->second) - { - EXPECT_EQ(0, r.second) << fmt::format("Resource {} from scene {} has {} refs", r.first, sceneId, r.second).c_str(); - } - } -} - -void RendererResourceManagerRefCountMock::expectNoResourceReferences() const -{ - for (const auto& sr : m_refCounts) - expectNoResourceReferencesForScene(sr.first); -} - -int RendererResourceManagerRefCountMock::getResourceRefCount(ResourceContentHash resource) const -{ - return std::accumulate(m_refCounts.cbegin(), m_refCounts.cend(), 0, [&](int refs, const auto& sceneRefsIt) - { - const auto it = sceneRefsIt.second.find(resource); - if (it != sceneRefsIt.second.cend()) - refs += it->second; - return refs; - }); -} - -} diff --git a/renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.h b/renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.h deleted file mode 100644 index f1510356c..000000000 --- a/renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.h +++ /dev/null @@ -1,104 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERRESOURCEMANAGERMOCK_H -#define RAMSES_RENDERERRESOURCEMANAGERMOCK_H - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/ResourceContentHash.h" -#include "RendererLib/IRendererResourceManager.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/EResourceStatus.h" -#include "Components/ManagedResource.h" -#include - -namespace ramses_internal{ - -class RendererResourceManagerMock : public IRendererResourceManager -{ -public: - RendererResourceManagerMock(); - // IResourceDeviceHandleAccessor - MOCK_METHOD(DeviceResourceHandle, getResourceDeviceHandle, (const ResourceContentHash&), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getRenderTargetDeviceHandle, (RenderTargetHandle, SceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getRenderTargetBufferDeviceHandle, (RenderBufferHandle, SceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferDeviceHandle, (OffscreenBufferHandle), (const, override)); - MOCK_METHOD(OffscreenBufferHandle, getOffscreenBufferHandle, (DeviceResourceHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferColorBufferDeviceHandle, (OffscreenBufferHandle), (const, override)); - MOCK_METHOD(int, getDmaOffscreenBufferFD, (OffscreenBufferHandle), (const, override)); - MOCK_METHOD(uint32_t, getDmaOffscreenBufferStride, (OffscreenBufferHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getStreamBufferDeviceHandle, (StreamBufferHandle bufferHandle), (const, override)); - MOCK_METHOD(void, getBlitPassRenderTargetsDeviceHandle, (BlitPassHandle, SceneId, DeviceResourceHandle&, DeviceResourceHandle&), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getDataBufferDeviceHandle, (DataBufferHandle, SceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getTextureBufferDeviceHandle, (TextureBufferHandle, SceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getExternalBufferDeviceHandle, (ExternalBufferHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getEmptyExternalBufferDeviceHandle, (), (const, override)); - MOCK_METHOD(uint32_t, getExternalBufferGlId, (ExternalBufferHandle), (const, override)); - // IRendererResourceManager - MOCK_METHOD(EResourceStatus, getResourceStatus, (const ResourceContentHash& hash), (const, override)); - MOCK_METHOD(EResourceType, getResourceType, (const ResourceContentHash& hash), (const, override)); - MOCK_METHOD(void, referenceResourcesForScene, (SceneId sceneId, const ResourceContentHashVector& resources), (override)); - MOCK_METHOD(void, unreferenceResourcesForScene, (SceneId sceneId, const ResourceContentHashVector& resources), (override)); - MOCK_METHOD(void, unloadAllSceneResourcesForScene, (SceneId sceneId), (override)); - MOCK_METHOD(void, unreferenceAllResourcesForScene, (SceneId sceneId), (override)); - MOCK_METHOD(const ResourceContentHashVector*, getResourcesInUseByScene, (SceneId sceneId), (const, override)); - MOCK_METHOD(void, provideResourceData, (const ManagedResource& mr), (override)); - MOCK_METHOD(bool, hasResourcesToBeUploaded, (), (const, override)); - MOCK_METHOD(void, uploadAndUnloadPendingResources, (), (override)); - MOCK_METHOD(void, uploadRenderTargetBuffer, (RenderBufferHandle renderBufferHandle, SceneId sceneId, const RenderBuffer& renderBuffer), (override)); - MOCK_METHOD(void, unloadRenderTargetBuffer, (RenderBufferHandle renderBufferHandle, SceneId sceneId), (override)); - MOCK_METHOD(void, uploadRenderTarget, (RenderTargetHandle renderTarget, const RenderBufferHandleVector& rtBufferHandles, SceneId sceneId), (override)); - MOCK_METHOD(void, unloadRenderTarget, (RenderTargetHandle renderTarget, SceneId sceneId), (override)); - MOCK_METHOD(void, uploadOffscreenBuffer, (OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType), (override)); - MOCK_METHOD(void, uploadDmaOffscreenBuffer, (OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers), (override)); - MOCK_METHOD(void, unloadOffscreenBuffer, (OffscreenBufferHandle bufferHandle), (override)); - MOCK_METHOD(void, uploadStreamBuffer, (StreamBufferHandle bufferHandle, WaylandIviSurfaceId surfaceId), (override)); - MOCK_METHOD(void, unloadStreamBuffer, (StreamBufferHandle bufferHandle), (override)); - MOCK_METHOD(void, uploadBlitPassRenderTargets, (BlitPassHandle, RenderBufferHandle, RenderBufferHandle, SceneId), (override)); - MOCK_METHOD(void, unloadBlitPassRenderTargets, (BlitPassHandle, SceneId), (override)); - MOCK_METHOD(void, uploadDataBuffer, (DataBufferHandle dataBufferHandle, EDataBufferType dataBufferType, EDataType dataType, uint32_t elementCount, SceneId sceneId), (override)); - MOCK_METHOD(void, unloadDataBuffer, (DataBufferHandle dataBufferHandle, SceneId sceneId), (override)); - MOCK_METHOD(void, updateDataBuffer, (DataBufferHandle handle, uint32_t dataSizeInBytes, const Byte* data, SceneId sceneId), (override)); - - MOCK_METHOD(void, uploadTextureBuffer, (TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, ETextureFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId), (override)); - MOCK_METHOD(void, unloadTextureBuffer, (TextureBufferHandle textureBufferHandle, SceneId sceneId), (override)); - MOCK_METHOD(void, updateTextureBuffer, (TextureBufferHandle textureBufferHandle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data, SceneId sceneId), (override)); - - MOCK_METHOD(void, uploadVertexArray, (RenderableHandle renderableHandle, const VertexArrayInfo& vertexArrayInfo, SceneId sceneId), (override)); - MOCK_METHOD(void, unloadVertexArray, (RenderableHandle renderableHandle, SceneId sceneId), (override)); - MOCK_METHOD(DeviceResourceHandle, getVertexArrayDeviceHandle, (RenderableHandle renderableHandle, SceneId sceneId), (const, override)); - - MOCK_METHOD(void, uploadExternalBuffer, (ExternalBufferHandle), (override)); - MOCK_METHOD(void, unloadExternalBuffer, (ExternalBufferHandle), (override)); - - MOCK_METHOD(const StreamUsage&, getStreamUsage, (WaylandIviSurfaceId source), (const, override)); -}; - -class RendererResourceManagerRefCountMock : public RendererResourceManagerMock -{ -public: - ~RendererResourceManagerRefCountMock() override; - - void referenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) override; - void unreferenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) override; - [[nodiscard]] const ResourceContentHashVector* getResourcesInUseByScene(SceneId sceneId) const override; - void unreferenceAllResourcesForScene(SceneId sceneId) override; - - void expectNoResourceReferencesForScene(SceneId sceneId) const; - void expectNoResourceReferences() const; - [[nodiscard]] int getResourceRefCount(ResourceContentHash resource) const; - -private: - std::unordered_map> m_refCounts; - mutable ResourceContentHashVector m_tempUsedResources; -}; - -} -#endif diff --git a/renderer/RendererLib/RendererTestCommon/ResourceDeviceHandleAccessorMock.h b/renderer/RendererLib/RendererTestCommon/ResourceDeviceHandleAccessorMock.h deleted file mode 100644 index c903b73e8..000000000 --- a/renderer/RendererLib/RendererTestCommon/ResourceDeviceHandleAccessorMock.h +++ /dev/null @@ -1,41 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RESOURCEDEVICEHANDLEACCESSORMOCK_H -#define RAMSES_RESOURCEDEVICEHANDLEACCESSORMOCK_H - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererLib/IResourceDeviceHandleAccessor.h" - -namespace ramses_internal -{ - -class ResourceDeviceHandleAccessorMock : public IResourceDeviceHandleAccessor -{ -public: - MOCK_METHOD(DeviceResourceHandle, getResourceDeviceHandle, (const ResourceContentHash& resourceHash), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getRenderTargetDeviceHandle, (RenderTargetHandle targetHandle, SceneId sceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getRenderTargetBufferDeviceHandle, (RenderBufferHandle bufferHandle, SceneId sceneId), (const, override)); - MOCK_METHOD(void, getBlitPassRenderTargetsDeviceHandle, (BlitPassHandle blitPassHandle, SceneId sceneId, DeviceResourceHandle&, DeviceResourceHandle&), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferDeviceHandle, (OffscreenBufferHandle bufferHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferColorBufferDeviceHandle, (OffscreenBufferHandle bufferHandle), (const, override)); - MOCK_METHOD(int, getDmaOffscreenBufferFD, (OffscreenBufferHandle), (const, override)); - MOCK_METHOD(uint32_t, getDmaOffscreenBufferStride, (OffscreenBufferHandle), (const, override)); - MOCK_METHOD(OffscreenBufferHandle, getOffscreenBufferHandle, (DeviceResourceHandle bufferDeviceHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getStreamBufferDeviceHandle, (StreamBufferHandle bufferHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getDataBufferDeviceHandle, (DataBufferHandle dataBufferHandle, SceneId sceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getTextureBufferDeviceHandle, (TextureBufferHandle textureBufferHandle, SceneId sceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getVertexArrayDeviceHandle, (RenderableHandle renderableHandle, SceneId sceneId), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getExternalBufferDeviceHandle, (ExternalBufferHandle), (const, override)); - MOCK_METHOD(DeviceResourceHandle, getEmptyExternalBufferDeviceHandle, (), (const, override)); - MOCK_METHOD(uint32_t, getExternalBufferGlId, (ExternalBufferHandle), (const, override)); -}; - -} -#endif diff --git a/renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.cpp b/renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.cpp deleted file mode 100644 index b87fff1c4..000000000 --- a/renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RendererConfig.h" - -namespace ramses_internal -{ - void PrintTo(const DisplayConfig& config, ::std::ostream* os) - { - *os << "Fullscreen=" << config.getFullscreenState(); - *os << "\nBorderlesss=" << config.getBorderlessState(); - - *os << "\nAntialiasing samples=" << config.getAntialiasingSampleCount(); - - *os << "\nWindowWidth=" << config.getDesiredWindowWidth(); - *os << "\nWindowHeight=" << config.getDesiredWindowHeight(); - *os << "\nWindowPositionX=" << config.getWindowPositionX(); - *os << "\nWindowPositionY=" << config.getWindowPositionY(); - *os << "\nWaylandSocketEmbedded=" << config.getWaylandSocketEmbedded(); - *os << "\nWaylandSocketEmbeddedFD=" << config.getWaylandSocketEmbeddedFD(); - } - - void PrintTo(const RendererConfig& config, ::std::ostream* os) - { - *os << "SCEnabled=" << config.getSystemCompositorControlEnabled(); - } -} diff --git a/renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.h b/renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.h deleted file mode 100644 index a002736c7..000000000 --- a/renderer/RendererLib/RendererTestCommon/renderer_common_gmock_header.h +++ /dev/null @@ -1,27 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2012 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERER_COMMON_GMOCK_HEADER_H -#define RAMSES_RENDERER_COMMON_GMOCK_HEADER_H - -#include "framework_common_gmock_header.h" - -namespace ramses_internal -{ - // See base header framework_common_gmock_header.h for more info why this file is needed - - class DisplayConfig; - class RendererConfig; - - // fill out with renderer specific PrintTo function declarations (definitions go to cpp file!) - void PrintTo(const DisplayConfig& config, ::std::ostream* os); - void PrintTo(const RendererConfig& config, ::std::ostream* os); -} - - -#endif diff --git a/renderer/RendererTestUtils/include/ReadPixelCallbackHandler.h b/renderer/RendererTestUtils/include/ReadPixelCallbackHandler.h deleted file mode 100644 index a6e77de08..000000000 --- a/renderer/RendererTestUtils/include/ReadPixelCallbackHandler.h +++ /dev/null @@ -1,33 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_READPIXELCALLBACKHANDLER_H -#define RAMSES_READPIXELCALLBACKHANDLER_H - -#include "ramses-renderer-api/IRendererEventHandler.h" - -class ReadPixelCallbackHandler : public ramses::RendererEventHandlerEmpty -{ -public: - void framebufferPixelsRead(const uint8_t* pixelData, const uint32_t pixelDataSize, ramses::displayId_t, ramses::displayBufferId_t, ramses::ERendererEventResult result) override - { - m_pixelData.clear(); - - assert(result == ramses::ERendererEventResult::Ok); - UNUSED(result); - m_pixelData.resize(pixelDataSize); - ramses_internal::PlatformMemory::Copy(&m_pixelData[0], pixelData, pixelDataSize * sizeof(uint8_t)); - - m_pixelDataRead = true; - } - - ramses_internal::UInt8Vector m_pixelData; - bool m_pixelDataRead = false; -}; - -#endif diff --git a/renderer/RendererTestUtils/include/RendererTestUtils.h b/renderer/RendererTestUtils/include/RendererTestUtils.h deleted file mode 100644 index 9046c589f..000000000 --- a/renderer/RendererTestUtils/include/RendererTestUtils.h +++ /dev/null @@ -1,90 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERTESTUTILS_H -#define RAMSES_RENDERERTESTUTILS_H - -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "Utils/Image.h" - -#include -#include -#include - -namespace ramses -{ - class RamsesRenderer; - class RamsesDisplay; -} - -class RendererTestUtils -{ -public: - static void SaveScreenshotForDisplay( - ramses::RamsesRenderer& renderer, - ramses::displayId_t displayId, - ramses::displayBufferId_t displayBuffer, - uint32_t x, - uint32_t y, - uint32_t width, - uint32_t height, - const std::string& screenshotFileName); - - static bool PerformScreenshotTestForDisplay( - ramses::RamsesRenderer& renderer, - ramses::displayId_t displayId, - ramses::displayBufferId_t displayBuffer, - uint32_t x, - uint32_t y, - uint32_t width, - uint32_t height, - const std::string& screenshotFileName, - float maxAveragePercentErrorPerPixel = DefaultMaxAveragePercentPerPixel, - bool saveDiffOnError = true - ); - - static ramses_internal::Image ReadPixelData( - ramses::RamsesRenderer& renderer, - ramses::displayId_t displayId, - ramses::displayBufferId_t displayBuffer, - uint32_t x, - uint32_t y, - uint32_t width, - uint32_t height); - - static bool CompareBitmapToImageInFile( - const ramses_internal::Image& actualBitmap, - const std::string& expectedScreenshotFileName, - float maxAveragePercentErrorPerPixel, - bool saveDiffOnError); - - static ramses::displayId_t CreateDisplayImmediate(ramses::RamsesRenderer& renderer, const ramses::DisplayConfig& displayConfig); - static void DestroyDisplayImmediate(ramses::RamsesRenderer& renderer, ramses::displayId_t displayId); - - // All renderer tests should use this renderer config which adds dummy embedded compositor - static ramses::RendererConfig CreateTestRendererConfig(); - // All renderer tests should use this display config which makes window visible by default for Wayland - static ramses::DisplayConfig CreateTestDisplayConfig(uint32_t iviSurfaceIdOffset, bool iviWindowStartVisible = true); - static void SetMaxFrameCallbackPollingTimeForAllTests(std::chrono::microseconds time); - static void SetDefaultConfigForAllTests(const ramses::RendererConfig& rendererConfig, const ramses::DisplayConfig& displayConfig); - - static void SetWaylandDisplayForSystemCompositorControllerForAllTests(const std::string& wd); - - static const float DefaultMaxAveragePercentPerPixel; - -private: - static std::optional MaxFrameCallbackPollingTime; - static std::optional WaylandDisplayForSystemCompositorController; - - // config objects are not copyable.. - static std::unique_ptr defaultRendererConfig; - static std::unique_ptr defaultDisplayConfig; -}; - -#endif diff --git a/renderer/RendererTestUtils/src/RendererTestUtils.cpp b/renderer/RendererTestUtils/src/RendererTestUtils.cpp deleted file mode 100644 index c1a358352..000000000 --- a/renderer/RendererTestUtils/src/RendererTestUtils.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererTestUtils.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "RamsesRendererImpl.h" -#include "DisplayConfigImpl.h" -#include "RendererConfigImpl.h" -#include "Utils/Image.h" -#include "Utils/File.h" -#include "RendererAndSceneTestEventHandler.h" -#include "RendererAPI/IRenderBackend.h" -#include "ReadPixelCallbackHandler.h" - -using namespace ramses_internal; - -const float RendererTestUtils::DefaultMaxAveragePercentPerPixel = 0.2f; -std::optional RendererTestUtils::MaxFrameCallbackPollingTime; -std::optional RendererTestUtils::WaylandDisplayForSystemCompositorController; -std::unique_ptr RendererTestUtils::defaultRendererConfig = std::make_unique(); -std::unique_ptr RendererTestUtils::defaultDisplayConfig = std::make_unique(); - -namespace -{ - // If multiple platform backends are created at the same time, they have to have different id's - // The numbers have special meaning, therefore the test uses 100, 101, 102, ... to make sure they - // don't clash with existing applications - const uint32_t firstIviSurfaceId = 10010; -} - -void RendererTestUtils::SaveScreenshotForDisplay(ramses::RamsesRenderer& renderer, ramses::displayId_t displayId, ramses::displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& screenshotFileName) -{ - const Image screenshotBitmap = ReadPixelData(renderer, displayId, displayBuffer, x, y, width, height); - screenshotBitmap.saveToFilePNG("./res/" + screenshotFileName + ".PNG"); -} - -bool RendererTestUtils::PerformScreenshotTestForDisplay( - ramses::RamsesRenderer& renderer, - ramses::displayId_t displayId, - ramses::displayBufferId_t displayBuffer, - uint32_t x, uint32_t y, uint32_t width, uint32_t height, - const std::string& screenshotFileName, - float maxAveragePercentErrorPerPixel, - bool saveDiffOnError) -{ - const Image screenshotBitmap = ReadPixelData(renderer, displayId, displayBuffer, x, y, width, height); - return CompareBitmapToImageInFile(screenshotBitmap, screenshotFileName, maxAveragePercentErrorPerPixel, saveDiffOnError); -} - -bool RendererTestUtils::CompareBitmapToImageInFile(const Image& actualBitmap, const std::string& expectedScreenshotFileName, float maxAveragePercentErrorPerPixel, bool saveDiffOnError) -{ - Image expectedBitmap; - expectedBitmap.loadFromFilePNG("./res/" + expectedScreenshotFileName + ".PNG"); - if (expectedBitmap.getWidth() != actualBitmap.getWidth() || - expectedBitmap.getHeight() != actualBitmap.getHeight()) - { - printf("Screenshot comparison failed: size of expected image %u/%u does not match size of actual image %u/%u\n", - expectedBitmap.getWidth(), expectedBitmap.getHeight(), actualBitmap.getWidth(), actualBitmap.getHeight()); - - return false; - } - const Image diff = expectedBitmap.createDiffTo(actualBitmap); - assert(diff.getNumberOfPixels() == actualBitmap.getNumberOfPixels()); - - const auto sumOfPixelDiff = diff.getSumOfPixelValues(); - const uint32_t totalNumberOfPixels = diff.getNumberOfPixels(); - - if (totalNumberOfPixels == 0) - { - assert(false); - printf("Screenshot comparison failed: difference bitmap has no pixels!\n"); - return false; - } - - const float averagePercentErrorPerPixel = (100.0f * (sumOfPixelDiff.x + sumOfPixelDiff.y + sumOfPixelDiff.z + sumOfPixelDiff.w) / 256.0f) / totalNumberOfPixels; - - if (averagePercentErrorPerPixel > maxAveragePercentErrorPerPixel) - { - if (saveDiffOnError) - { - printf("Screenshot comparison failed: %s (%ux%u)\n", expectedScreenshotFileName.c_str(), diff.getWidth(), diff.getHeight()); - printf(" - avg error per pixel %.2f, maximum allowed error is %.2f\n", averagePercentErrorPerPixel, maxAveragePercentErrorPerPixel); - printf(" - total error R=%i, G=%i, B=%i, A=%i\n", sumOfPixelDiff.x, sumOfPixelDiff.y, sumOfPixelDiff.z, sumOfPixelDiff.w); - printf(" - number of pixels different by more than 1 (one or more color channels): %u\n", diff.getNumberOfNonBlackPixels(1)); - printf(" - number of pixels different by more than 64 (one or more color channels): %u\n", diff.getNumberOfNonBlackPixels(64)); - printf(" - number of pixels different by more than 128 (one or more color channels): %u\n", diff.getNumberOfNonBlackPixels(128)); - printf(" - number of pixels different by 255 (one or more color channels): %u\n", diff.getNumberOfNonBlackPixels(254)); - - const std::string screenShotPath{"./res/diffs"}; - File diffDirectory(screenShotPath); - diffDirectory.createDirectory(); - - actualBitmap.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_ACTUAL.PNG"); - diff.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_DIFF.PNG"); - - //Create separate RGB and Alpha diffs - std::pair colorAndAlphaDiffImages = diff.createSeparateColorAndAlphaImages(); - colorAndAlphaDiffImages.first.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_DIFF_RGB.PNG"); - colorAndAlphaDiffImages.second.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_DIFF_ALPHA.PNG"); - } - - return false; - } - - return true; -} - -ramses::displayId_t RendererTestUtils::CreateDisplayImmediate(ramses::RamsesRenderer& renderer, const ramses::DisplayConfig& displayConfig) -{ - const ramses::displayId_t displayId = renderer.createDisplay(displayConfig); - renderer.flush(); - ramses::RendererAndSceneTestEventHandler eventHandler(renderer); - if (eventHandler.waitForDisplayCreation(displayId)) - { - return displayId; - } - - assert(false && "Display construction failed or timed out!"); - return ramses::displayId_t::Invalid(); -} - -void RendererTestUtils::DestroyDisplayImmediate(ramses::RamsesRenderer& renderer, ramses::displayId_t displayId) -{ - renderer.destroyDisplay(displayId); - renderer.flush(); - ramses::RendererAndSceneTestEventHandler eventHandler(renderer); - eventHandler.registerAlreadyCreatedDisplay(displayId); - if (!eventHandler.waitForDisplayDestruction(displayId)) - { - assert(false && "Display destruction failed or timed out!"); - } -} - -ramses::RendererConfig RendererTestUtils::CreateTestRendererConfig() -{ - ramses::RendererConfig rendererConfig(*defaultRendererConfig); - ramses_internal::RendererConfig& internalRendererConfig = const_cast(rendererConfig.m_impl.get().getInternalRendererConfig()); - - if (WaylandDisplayForSystemCompositorController.has_value()) - { - internalRendererConfig.enableSystemCompositorControl(); - internalRendererConfig.setWaylandDisplayForSystemCompositorController(WaylandDisplayForSystemCompositorController.value()); - } - - if(MaxFrameCallbackPollingTime.has_value()) - internalRendererConfig.setFrameCallbackMaxPollTime(MaxFrameCallbackPollingTime.value()); - - return rendererConfig; -} - -void RendererTestUtils::SetWaylandDisplayForSystemCompositorControllerForAllTests(const std::string& wd) -{ - WaylandDisplayForSystemCompositorController = wd; -} - -void RendererTestUtils::SetDefaultConfigForAllTests(const ramses::RendererConfig& rendererConfig, const ramses::DisplayConfig& displayConfig) -{ - defaultRendererConfig = std::make_unique(rendererConfig); - defaultDisplayConfig = std::make_unique(displayConfig); -} - -void RendererTestUtils::SetMaxFrameCallbackPollingTimeForAllTests(std::chrono::microseconds time) -{ - MaxFrameCallbackPollingTime = time; -} - -ramses::DisplayConfig RendererTestUtils::CreateTestDisplayConfig(uint32_t iviSurfaceIdOffset, bool iviWindowStartVisible) -{ - ramses::DisplayConfig displayConfig(*defaultDisplayConfig); - displayConfig.setWaylandIviSurfaceID(ramses::waylandIviSurfaceId_t(firstIviSurfaceId + iviSurfaceIdOffset)); - - if(iviWindowStartVisible) - displayConfig.setWindowIviVisible(); - - return displayConfig; -} - -Image RendererTestUtils::ReadPixelData( - ramses::RamsesRenderer& renderer, - ramses::displayId_t displayId, - ramses::displayBufferId_t displayBuffer, - uint32_t x, - uint32_t y, - uint32_t width, - uint32_t height) -{ - const ramses::status_t status = renderer.readPixels(displayId, displayBuffer, x, y, width, height); - assert(status == ramses::StatusOK); - UNUSED(status); - renderer.flush(); - - constexpr std::chrono::seconds timeoutTime{ 10u }; - const auto startTime = std::chrono::steady_clock::now(); - ReadPixelCallbackHandler callbackHandler; - while (true) - { - if (!renderer.m_impl.isThreaded()) - renderer.doOneLoop(); - - renderer.dispatchEvents(callbackHandler); - if (callbackHandler.m_pixelDataRead) - break; - - if ((std::chrono::steady_clock::now() - startTime) > timeoutTime) - { - LOG_ERROR(CONTEXT_RENDERER, "RendererTestUtils::ReadPixelData: ran out of time!"); - return Image{}; - } - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); - } - - assert(callbackHandler.m_pixelData.size() == width * height * 4u); - - // flip image vertically so that the layout read from frame buffer (bottom-up) - // is converted to layout normally used in image files (top-down) - return Image(width, height, callbackHandler.m_pixelData.cbegin(), callbackHandler.m_pixelData.cend(), true); -} diff --git a/renderer/ramses-renderer-api/CMakeLists.txt b/renderer/ramses-renderer-api/CMakeLists.txt deleted file mode 100644 index 80240641d..000000000 --- a/renderer/ramses-renderer-api/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2023 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -file(GLOB - RAMSES_RENDERER_API_INCLUDE_BASE - include) - -add_library(ramses-renderer-api INTERFACE) -target_include_directories(ramses-renderer-api INTERFACE ${RAMSES_RENDERER_API_INCLUDE_BASE}) - -if(ramses-sdk_ENABLE_INSTALL) - install(DIRECTORY include/ DESTINATION "${RAMSES_INSTALL_HEADERS_PATH}" COMPONENT ramses-sdk-devel) -endif() - -set(ramses-shared-lib-renderer-MIXIN - ${ramses-shared-lib-renderer-MIXIN} - INCLUDE_PATHS ${RAMSES_RENDERER_API_INCLUDE_BASE} - CACHE INTERNAL "") diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/DefaultRendererResourceCache.h b/renderer/ramses-renderer-api/include/ramses-renderer-api/DefaultRendererResourceCache.h deleted file mode 100644 index f1b65457b..000000000 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/DefaultRendererResourceCache.h +++ /dev/null @@ -1,122 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERAPI_DEFAULTRENDERERRESOURCECACHE_H -#define RAMSES_RENDERERAPI_DEFAULTRENDERERRESOURCECACHE_H - -#include "ramses-renderer-api/IRendererResourceCache.h" - -#include - -namespace ramses -{ - /** - * @ingroup RendererAPI - * @brief The DefaultRendererResourceCache provides a simple example on how the IRendererResourceCache - * interface can be implemented. It is only intended as an example, as the optimal implementation - * would be very specific to how RAMSES is being utilized. - */ - class RAMSES_API DefaultRendererResourceCache : public IRendererResourceCache - { - public: - - /** - * @brief Construct a DefaultRendererResourceCache with a given maximum size. Whenever the size - * limit is exceeded, items will automatically be unloaded in a FIFO-manner. - * @param maxCacheSizeInBytes Maximum size of cache content in bytes - */ - explicit DefaultRendererResourceCache(uint32_t maxCacheSizeInBytes); - - /** - * @brief Destructor of DefaultRendererResourceCache - */ - ~DefaultRendererResourceCache() override; - - /** - * @brief Called by RamsesRenderer to ask for a resource with the given id. - * - * @param resourceId Id for the resource. - * @param[out] size The size of the found resource in bytes. This value is only relevant if - * the resource exists in the cache. - * @return true if a resource with the given resource id exists in the cache. - */ - bool hasResource(rendererResourceId_t resourceId, uint32_t& size) const override; - - /** - * @brief Called by RamsesRenderer to get the resource data associated with a given resource id. - * This method will be called immediately after hasResource(...), if the resource exists in the cache. - * - * @param resourceId Id for the resource. - * @param buffer A pre-allocated buffer which the resource data will be copied into. - * @param bufferSize The size of the pre-allocated buffer in bytes. It should be at least the size of the - * requested resource (returned by hasResource(...)). - * @return true if the resource was copied successfully into the buffer. - */ - bool getResourceData(rendererResourceId_t resourceId, uint8_t* buffer, uint32_t bufferSize) const override; - - /** - * @brief Called by RamsesRenderer when a resource was not in the cache and is now available from - * other source. The cache is asked if it wants to store a given resource or not. This - * avoids the overhead of preparing the resource data in case it is not to be cached. - * - * @param resourceId Id for the resource. - * @param resourceDataSize The size of the resource in bytes. - * @param cacheFlag The cache flag associated with the resource (set on client side). - * @param sceneId The id of the first scene which requested the resource. In case of multiple scenes - * using the same resource, only the first scene id is guaranteed to be reported. - * @return true if the cache wants to store the resource. - */ - [[nodiscard]] bool shouldResourceBeCached(rendererResourceId_t resourceId, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) const override; - - /** - * @brief Called by RamsesRenderer with the final resource for storing. This is called - * immediately after shouldResourceBeCached(...), if it was requested to be cached. - * - * @param resourceId Id for the resource. - * @param resourceData The resource data which will be copied into the cache. - * @param resourceDataSize The size of the resource in bytes. - * @param cacheFlag The cache flag associated with the resource (set on client side). - * @param sceneId The id of the first scene which requested the resource. In case of multiple scenes - * using the same resource, only the first scene id is guaranteed to be reported. - */ - void storeResource(rendererResourceId_t resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) override; - - /** - * @brief Save current content of the cache to a file. - * @param filePath The file path to save to. - */ - void saveToFile(std::string_view filePath) const; - - /** - * @brief Load all content from a file. It is assumed that the file has been created using saveToFile(...). - * @param filePath The file path to load from. - * @return true if the load was successful. - */ - bool loadFromFile(std::string_view filePath); - - /** - * @brief Deleted copy constructor - * @param other unused - */ - DefaultRendererResourceCache(const DefaultRendererResourceCache& other) = delete; - - /** - * @brief Deleted copy assignment - * @param other unused - * @return unused - */ - DefaultRendererResourceCache& operator=(const DefaultRendererResourceCache& other) = delete; - - /** - * Stores internal data for implementation specifics of this class. - */ - class DefaultRendererResourceCacheImpl& impl; - }; -} - -#endif diff --git a/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererResourceCache.h b/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererResourceCache.h deleted file mode 100644 index acced7ca2..000000000 --- a/renderer/ramses-renderer-api/include/ramses-renderer-api/IRendererResourceCache.h +++ /dev/null @@ -1,82 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERAPI_IRENDERERRESOURCECACHE_H -#define RAMSES_RENDERERAPI_IRENDERERRESOURCECACHE_H - -#include "Types.h" -#include "ramses-framework-api/APIExport.h" - -namespace ramses -{ - /** - * @brief An interface used to implement a caching mechanism for resources used on the RamsesRenderer. - * Important: Please note that the resource ids in this context do not match client resource ids. - * @ingroup RendererAPI - */ - class RAMSES_API IRendererResourceCache - { - public: - - /** - * @brief Destructor of IRendererResourceCache - */ - virtual ~IRendererResourceCache() = default; - - /** - * @brief Called by RamsesRenderer to ask for a resource with the given id. - * - * @param resourceId Id for the resource. - * @param[out] size The size of the found resource in bytes. This value is only relevant if - * the resource exists in the cache. - * @return true if a resource with the given resource id exists in the cache. - */ - virtual bool hasResource(rendererResourceId_t resourceId, uint32_t& size) const = 0; - - /** - * @brief Called by RamsesRenderer to get the resource data associated with a given resource id. - * This method will be called immediately after hasResource(...), if the resource exists in the cache. - * - * @param resourceId Id for the resource. - * @param buffer A pre-allocated buffer which the resource data will be copied into. - * @param bufferSize The size of the pre-allocated buffer in bytes. It should be at least the size of the - * requested resource (returned by hasResource(...)). - * @return true if the resource was copied successfully into the buffer. - */ - virtual bool getResourceData(rendererResourceId_t resourceId, uint8_t* buffer, uint32_t bufferSize) const = 0; - - /** - * @brief Called by RamsesRenderer when a resource was not in the cache and is now available from - * other source. The cache is asked if it wants to store a given resource or not. This - * avoids the overhead of preparing the resource data in case it is not to be cached. - * - * @param resourceId Id for the resource. - * @param resourceDataSize The size of the resource in bytes. - * @param cacheFlag The cache flag associated with the resource (set on client side). - * @param sceneId The id of the first scene which requested the resource. In case of multiple scenes - * using the same resource, only the first scene id is guaranteed to be reported. - * @return true if the cache wants to store the resource. - */ - [[nodiscard]] virtual bool shouldResourceBeCached(rendererResourceId_t resourceId, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) const = 0; - - /** - * @brief Called by RamsesRenderer with the final resource for storing. This is called - * immediately after shouldResourceBeCached(...), if it was requested to be cached. - * - * @param resourceId Id for the resource. - * @param resourceData The resource data which will be copied into the cache. - * @param resourceDataSize The size of the resource in bytes. - * @param cacheFlag The cache flag associated with the resource (set on client side). - * @param sceneId The id of the first scene which requested the resource. In case of multiple scenes - * using the same resource, only the first scene id is guaranteed to be reported. - */ - virtual void storeResource(rendererResourceId_t resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) = 0; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/CMakeLists.txt b/renderer/ramses-renderer-impl/CMakeLists.txt deleted file mode 100644 index d77adfa6e..000000000 --- a/renderer/ramses-renderer-impl/CMakeLists.txt +++ /dev/null @@ -1,47 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2023 BMW AG -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -file(GLOB - RAMSES_RENDERER_FILES_SOURCE - src/*.cpp) - -createModule( - NAME ramses-renderer-impl - TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - - INCLUDE_PATHS include - SRC_FILES include/*.h - ${RAMSES_RENDERER_FILES_SOURCE} - - DEPENDENCIES PlatformFactory -) - -set(ramses-shared-lib-renderer-MIXIN - ${ramses-shared-lib-renderer-MIXIN} - SRC_FILES ${RAMSES_RENDERER_FILES_SOURCE} - DEPENDENCIES ramses-renderer-lib - CACHE INTERNAL "") - - -IF (${ramses-sdk_BUILD_TESTS}) - createModule( - NAME ramses-renderer-impl-test - TYPE BINARY - SRC_FILES test/*.h - test/*.cpp - DEPENDENCIES ramses-client - ramses-renderer-impl - ramses-gmock-main - RendererTestCommon - ) - - makeTestFromTarget( - TARGET ramses-renderer-impl-test - SUFFIX UNITTEST) -endif() diff --git a/renderer/ramses-renderer-impl/include/BinaryShaderCacheImpl.h b/renderer/ramses-renderer-impl/include/BinaryShaderCacheImpl.h deleted file mode 100644 index ffbdbb0e6..000000000 --- a/renderer/ramses-renderer-impl/include/BinaryShaderCacheImpl.h +++ /dev/null @@ -1,71 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_BINARYSHADERCACHEIMPL_H -#define RAMSES_BINARYSHADERCACHEIMPL_H - -#include "Collections/HashMap.h" -#include "RendererAPI/Types.h" -#include "ramses-renderer-api/Types.h" -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/SceneId.h" - -#include -#include - -namespace ramses_internal -{ - class IOutputStream; - class IInputStream; -} - -namespace ramses -{ - class BinaryShaderCacheImpl - { - public: - void deviceSupportsBinaryShaderFormats(const binaryShaderFormatId_t* supportedFormats, uint32_t numSupportedFormats); - bool hasBinaryShader(const ramses_internal::ResourceContentHash& effectId) const; - uint32_t getBinaryShaderSize(const ramses_internal::ResourceContentHash& effectId) const; - binaryShaderFormatId_t getBinaryShaderFormat(const ramses_internal::ResourceContentHash& effectId) const; - bool shouldBinaryShaderBeCached(const ramses_internal::ResourceContentHash& effectId, ramses_internal::SceneId sceneId) const; - void getBinaryShaderData(const ramses_internal::ResourceContentHash& effectId, uint8_t* buffer, uint32_t bufferSize) const; - void storeBinaryShader(const ramses_internal::ResourceContentHash& effectId, ramses_internal::SceneId sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat); - void binaryShaderUploaded(ramses_internal::ResourceContentHash effectHash, bool success) const; - - void saveToFile(std::string_view filePath) const; - bool loadFromFile(std::string_view filePath); - - struct FileHeader - { - uint32_t fileSize; - uint32_t transportVersion; - uint64_t checksum; - }; - - private: - static void serializeBinaryShader(ramses_internal::IOutputStream& outputStream, const ramses_internal::ResourceContentHash& effectId, const ramses_internal::UInt8Vector& binaryShaderData, ramses_internal::BinaryShaderFormatID binaryShaderFormat); - static bool deserializeBinaryShader(ramses_internal::IInputStream& inputStream, ramses_internal::ResourceContentHash& effectId, ramses_internal::UInt8Vector& binaryShaderData, ramses_internal::BinaryShaderFormatID& binaryShaderFormat); - - struct BinaryShader - { - ramses_internal::UInt8Vector data; - ramses_internal::BinaryShaderFormatID format; - }; - using BinaryShaderTable = ramses_internal::HashMap; - - BinaryShaderTable m_binaryShaders; - std::vector m_supportedFormats; - // protects HashMap write of new shaders concurrently with saving of file - // WARNING: Does not protect loading from file concurrently with querying for shaders! - // Reason: Avoid performance degradation because unknown if Integrity mutexes are cheap without congestion. - mutable std::mutex m_hashMapLock; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/BinaryShaderCacheProxy.h b/renderer/ramses-renderer-impl/include/BinaryShaderCacheProxy.h deleted file mode 100644 index 12a471d3b..000000000 --- a/renderer/ramses-renderer-impl/include/BinaryShaderCacheProxy.h +++ /dev/null @@ -1,44 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_BINARYSHADERCACHEPROXY_H -#define RAMSES_BINARYSHADERCACHEPROXY_H - -#include "RendererAPI/IBinaryShaderCache.h" -#include "ramses-renderer-api/Types.h" -#include - -namespace ramses -{ - class IBinaryShaderCache; - class BinaryShaderCacheProxy final : public ramses_internal::IBinaryShaderCache - { - public: - explicit BinaryShaderCacheProxy(ramses::IBinaryShaderCache& cache); - ~BinaryShaderCacheProxy() override = default; - - void deviceSupportsBinaryShaderFormats(const std::vector& supportedFormats) override; - - bool hasBinaryShader(ramses_internal::ResourceContentHash effectHash) const override; - uint32_t getBinaryShaderSize(ramses_internal::ResourceContentHash effectHash) const override; - ramses_internal::BinaryShaderFormatID getBinaryShaderFormat(ramses_internal::ResourceContentHash effectHash) const override; - void getBinaryShaderData(ramses_internal::ResourceContentHash effectHash, uint8_t* buffer, uint32_t bufferSize) const override; - - bool shouldBinaryShaderBeCached(ramses_internal::ResourceContentHash effectHash, ramses_internal::SceneId sceneId) const override; - void storeBinaryShader(ramses_internal::ResourceContentHash effectHash, ramses_internal::SceneId sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, ramses_internal::BinaryShaderFormatID binaryShaderFormat) override; - void binaryShaderUploaded(ramses_internal::ResourceContentHash effectHash, bool success) const override; - std::once_flag& binaryShaderFormatsReported() override; - - private: - ramses::IBinaryShaderCache& m_cache; - mutable std::mutex m_mutex; - std::once_flag m_supportedFormatsReported; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/DefaultRendererResourceCacheImpl.h b/renderer/ramses-renderer-impl/include/DefaultRendererResourceCacheImpl.h deleted file mode 100644 index e6a4aa2ed..000000000 --- a/renderer/ramses-renderer-impl/include/DefaultRendererResourceCacheImpl.h +++ /dev/null @@ -1,71 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERAPI_DEFAULTRENDERERRESOURCECACHEIMPL_H -#define RAMSES_RENDERERAPI_DEFAULTRENDERERRESOURCECACHEIMPL_H - -#include "Collections/Vector.h" -#include "RendererAPI/Types.h" -#include "ramses-renderer-api/Types.h" -#include "ramses-renderer-api/IRendererResourceCache.h" - -#include -#include -#include - -namespace ramses -{ - class IDataFunctor; - - class DefaultRendererResourceCacheImpl : public IRendererResourceCache - { - public: - explicit DefaultRendererResourceCacheImpl(uint32_t maxCacheSizeInBytes); - ~DefaultRendererResourceCacheImpl() override; - - bool hasResource(rendererResourceId_t resourceId, uint32_t& size) const override; - bool getResourceData(rendererResourceId_t resourceId, uint8_t* buffer, uint32_t bufferSize) const override; - [[nodiscard]] bool shouldResourceBeCached(rendererResourceId_t resourceId, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) const override; - void storeResource(rendererResourceId_t resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) override; - - void saveToFile(std::string_view filePath) const; - bool loadFromFile(std::string_view filePath); - - struct FileHeader - { - uint32_t fileSize; - uint32_t transportVersion; - uint32_t checksum; - }; - - private: - - using ByteVector = std::vector; - - bool storeResourceInternal(rendererResourceId_t resourceId, ByteVector& data); - void clear(); - void makeSpaceForNewItem(uint32_t newItemSizeInBytes); - void removeOldestItem(); - - void iterateDataToSave(IDataFunctor& functor) const; - - struct ResourceData - { - rendererResourceId_t resourceId; - ByteVector data; - }; - - using ResourceDataTable = std::deque; - - ResourceDataTable m_resourceData; - uint32_t m_maxCacheSizeInBytes; - uint32_t m_currentCacheSizeInBytes; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/DisplayConfigImpl.h b/renderer/ramses-renderer-impl/include/DisplayConfigImpl.h deleted file mode 100644 index 89c714d23..000000000 --- a/renderer/ramses-renderer-impl/include/DisplayConfigImpl.h +++ /dev/null @@ -1,95 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_DISPLAYCONFIGIMPL_H -#define RAMSES_DISPLAYCONFIGIMPL_H - -#include "ramses-renderer-api/Types.h" -#include "RendererLib/DisplayConfig.h" -#include "StatusObjectImpl.h" -#include "DataTypesImpl.h" - -namespace CLI -{ - class App; -} - -namespace ramses -{ - class DisplayConfigImpl : public StatusObjectImpl - { - public: - DisplayConfigImpl(); - - status_t setDeviceType(EDeviceType deviceType); - EDeviceType getDeviceType() const; - status_t setWindowType(EWindowType windowType); - EWindowType getWindowType() const; - status_t setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height); - status_t getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const; - status_t setFullscreen(bool fullscreen); - bool isFullscreen() const; - status_t setBorderless(bool borderless); - status_t setMultiSampling(uint32_t numSamples); - status_t getMultiSamplingSamples(uint32_t& numSamples) const; - status_t setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID); - waylandIviSurfaceId_t getWaylandIviSurfaceID() const; - status_t setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID); - waylandIviLayerId_t getWaylandIviLayerID() const; - status_t setWaylandDisplay(std::string_view waylandDisplay); - std::string_view getWaylandDisplay() const; - void* getAndroidNativeWindow() const; - status_t setAndroidNativeWindow(void * nativeWindowPtr); - status_t setWindowIviVisible(bool visible); - status_t setResizable(bool resizable); - status_t keepEffectsUploaded(bool enable); - status_t setGPUMemoryCacheSize(uint64_t size); - status_t setClearColor(const vec4f& color); - status_t setOffscreen(bool offscreenFlag); - status_t setDepthStencilBufferType(EDepthBufferType depthBufferType); - status_t setX11WindowHandle(unsigned long x11WindowHandle); - unsigned long getX11WindowHandle() const; - status_t setWindowsWindowHandle(void* hwnd); - void* getWindowsWindowHandle() const; - status_t setAsyncEffectUploadEnabled(bool enabled); - - status_t setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname); - std::string_view getWaylandSocketEmbeddedGroup() const; - - status_t setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions); - uint32_t getWaylandSocketEmbeddedPermissions() const; - - status_t setWaylandEmbeddedCompositingSocketName(std::string_view socketname); - std::string_view getWaylandEmbeddedCompositingSocketName() const; - - status_t setWaylandEmbeddedCompositingSocketFD(int fd); - int getWaylandSocketEmbeddedFD() const; - - status_t setPlatformRenderNode(std::string_view renderNode); - std::string_view getPlatformRenderNode() const; - - status_t setSwapInterval(int32_t interval); - int32_t getSwapInterval() const; - - status_t setScenePriority(sceneId_t sceneId, int32_t priority); - int32_t getScenePriority(sceneId_t) const; - - status_t setResourceUploadBatchSize(uint32_t batchSize); - uint32_t getResourceUploadBatchSize() const; - - status_t validate() const override; - - //impl methods - const ramses_internal::DisplayConfig& getInternalDisplayConfig() const; - - private: - ramses_internal::DisplayConfig m_internalConfig; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/RamsesRendererImpl.h b/renderer/ramses-renderer-impl/include/RamsesRendererImpl.h deleted file mode 100644 index 83868c09e..000000000 --- a/renderer/ramses-renderer-impl/include/RamsesRendererImpl.h +++ /dev/null @@ -1,172 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESRENDERERIMPL_H -#define RAMSES_RAMSESRENDERERIMPL_H - -#include "ramses-renderer-api/Types.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "DataTypesImpl.h" -#include "StatusObjectImpl.h" -#include "CommandDispatchingThread.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererLib/DisplayDispatcher.h" -#include "RendererLib/RendererPeriodicLogSupplier.h" -#include "RendererAPI/ELoopMode.h" -#include "RendererFramework/RendererFrameworkLogic.h" -#include "Watchdog/ThreadWatchdog.h" -#include "RendererCommands/Screenshot.h" -#include "RendererCommands/LogRendererInfo.h" -#include "RendererCommands/PrintStatistics.h" -#include "RendererCommands/TriggerPickEvent.h" -#include "RendererCommands/SetClearColor.h" -#include "RendererCommands/SetSkippingOfUnmodifiedBuffers.h" -#include "RendererCommands/SystemCompositorControllerListIviSurfaces.h" -#include "RendererCommands/SystemCompositorControllerSetLayerVisibility.h" -#include "RendererCommands/SystemCompositorControllerSetSurfaceVisibility.h" -#include "RendererCommands/SystemCompositorControllerSetSurfaceOpacity.h" -#include "RendererCommands/SystemCompositorControllerSetSurfaceDestRectangle.h" -#include "RendererCommands/SystemCompositorControllerScreenshot.h" -#include "RendererCommands/SystemCompositorControllerAddSurfaceToLayer.h" -#include "RendererCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h" -#include "RendererCommands/SystemCompositorControllerDestroySurface.h" -#include "RendererCommands/SetFrameTimeLimits.h" - -#include -#include -#include - -namespace ramses_internal -{ - class IBinaryShaderCache; -} - -namespace ramses -{ - class RamsesFrameworkImpl; - class RendererConfig; - class DisplayConfig; - class IRendererEventHandler; - - class RamsesRendererImpl : public StatusObjectImpl - { - public: - RamsesRendererImpl(RamsesFrameworkImpl& framework, const RendererConfig& config); - ~RamsesRendererImpl() override; - - status_t doOneLoop(); - status_t flush(); - - displayId_t createDisplay(const DisplayConfig& config); - status_t destroyDisplay(displayId_t displayId); - displayBufferId_t getDisplayFramebuffer(displayId_t displayId) const; - const ramses_internal::DisplayDispatcher& getDisplayDispatcher() const; - ramses_internal::DisplayDispatcher& getDisplayDispatcher(); - - RendererSceneControl* getSceneControlAPI(); - - displayBufferId_t createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount, bool interruptible, EDepthBufferType depthBufferType); - displayBufferId_t createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, ramses_internal::DmaBufferFourccFormat dmaBufferFourccFormat, ramses_internal::DmaBufferUsageFlags dmaBufferUsageFlags, ramses_internal::DmaBufferModifiers dmaBufferModifiers); - status_t destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer); - status_t setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, uint32_t clearFlags); - status_t setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color); - status_t getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; - - streamBufferId_t allocateStreamBuffer(); - streamBufferId_t createStreamBuffer(displayId_t display, waylandIviSurfaceId_t source); - status_t destroyStreamBuffer(displayId_t display, streamBufferId_t streamBuffer); - - externalBufferId_t createExternalBuffer(displayId_t display); - status_t destroyExternalBuffer(displayId_t display, externalBufferId_t externalTexture); - status_t readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height); - - status_t systemCompositorSetIviSurfaceVisibility(uint32_t surfaceId, bool visibility); - status_t systemCompositorSetIviSurfaceOpacity(uint32_t surfaceId, float opacity); - status_t systemCompositorSetIviSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height); - status_t systemCompositorSetIviLayerVisibility(uint32_t layerId, bool visibility); - status_t systemCompositorTakeScreenshot(std::string_view fileName, int32_t screenIviId); - status_t systemCompositorAddIviSurfaceToIviLayer(uint32_t surfaceId, uint32_t layerId); - - status_t dispatchEvents(IRendererEventHandler& rendererEventHandler); - - void logConfirmationEcho(displayId_t display, const std::string& text); - status_t logRendererInfo(); - - status_t startThread(); - status_t stopThread(); - bool isThreadRunning() const; - bool isThreaded() const; - status_t setFramerateLimit(displayId_t displayId, float fpsLimit); - float getFramerateLimit(displayId_t displayId) const; - status_t setLoopMode(ELoopMode loopMode); - ELoopMode getLoopMode() const; - status_t setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); - status_t setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height); - - status_t setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit); - status_t setSkippingOfUnmodifiedBuffers(bool enable); - - const ramses_internal::RendererCommands& getPendingCommands() const; - void pushAndConsumeRendererCommands(ramses_internal::RendererCommands& cmds); - - using DisplayFrameBufferMap = std::unordered_map; - const DisplayFrameBufferMap& getDisplayFrameBuffers() const; - - private: - RamsesFrameworkImpl& m_framework; - std::unique_ptr m_binaryShaderCache; - std::unique_ptr m_rendererResourceCache; - - ramses_internal::RendererCommands m_pendingRendererCommands; - ramses_internal::RendererCommandBuffer m_rendererCommandBuffer; - ramses_internal::RendererFrameworkLogic m_rendererFrameworkLogic; - ramses_internal::ThreadWatchdog m_threadWatchdog; - std::unique_ptr m_displayDispatcher; - - displayId_t m_nextDisplayId{ 0u }; - displayBufferId_t m_nextDisplayBufferId{ 0u }; - streamBufferId_t m_nextStreamBufferId{ 0u }; - externalBufferId_t m_nextExternalBufferId{ 0u }; - DisplayFrameBufferMap m_displayFramebuffers; - bool m_systemCompositorEnabled; - - struct OffscreenDmaBufferInfo - { - displayId_t display; - displayBufferId_t displayBuffer; - int fd; - uint32_t stride; - }; - std::vector m_offscreenDmaBufferInfos; - - ramses_internal::ELoopMode m_loopMode; - std::unique_ptr m_commandDispatchingThread; - bool m_diplayThreadUpdating = false; - - enum ERendererLoopThreadType - { - ERendererLoopThreadType_Undefined = 0, - ERendererLoopThreadType_InRendererOwnThread, - ERendererLoopThreadType_UsingDoOneLoop - }; - ERendererLoopThreadType m_rendererLoopThreadType; - ramses_internal::RendererPeriodicLogSupplier m_periodicLogSupplier; //must be destructed before the RendererCommandBuffer! - - // scene control APIs can only be destructed within their friend RamsesRendererImpl class, - // use custom deleter to achieve that with unique ptr - template using UniquePtrWithDeleter = std::unique_ptr>; - UniquePtrWithDeleter m_sceneControlAPI; - - // keep allocated containers which are used to swap internal data - ramses_internal::RendererEventVector m_tempRendererEvents; - - std::vector < std::shared_ptr > m_ramshCommands; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/RamsesRendererUtils.h b/renderer/ramses-renderer-impl/include/RamsesRendererUtils.h deleted file mode 100644 index 77d2191c9..000000000 --- a/renderer/ramses-renderer-impl/include/RamsesRendererUtils.h +++ /dev/null @@ -1,29 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RAMSESRENDERERUTILS_H -#define RAMSES_RAMSESRENDERERUTILS_H - -#include "ramses-renderer-api/Types.h" -#include "RendererLib/EResourceStatus.h" -#include "RendererLib/EKeyCode.h" -#include "RendererLib/EKeyEventType.h" -#include "RendererLib/EMouseEventType.h" - -namespace ramses -{ - class RamsesRendererUtils final - { - public: - static EMouseEvent GetMouseEvent( ramses_internal::EMouseEventType type); - static EKeyEvent GetKeyEvent( ramses_internal::EKeyEventType type); - static EKeyCode GetKeyCode( ramses_internal::EKeyCode keyCode); - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/RendererConfigImpl.h b/renderer/ramses-renderer-impl/include/RendererConfigImpl.h deleted file mode 100644 index cf7b220c0..000000000 --- a/renderer/ramses-renderer-impl/include/RendererConfigImpl.h +++ /dev/null @@ -1,54 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERCONFIGIMPL_H -#define RAMSES_RENDERERCONFIGIMPL_H - -#include "RendererLib/RendererConfig.h" -#include "StatusObjectImpl.h" - -namespace ramses -{ - class IBinaryShaderCache; - class IRendererResourceCache; - - class RendererConfigImpl : public StatusObjectImpl - { - public: - RendererConfigImpl(); - - status_t enableSystemCompositorControl(); - - status_t setSystemCompositorWaylandDisplay(std::string_view waylandDisplay); - std::string_view getSystemCompositorWaylandDisplay() const; - - status_t setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec); - - status_t setBinaryShaderCache(IBinaryShaderCache& cache); - IBinaryShaderCache* getBinaryShaderCache() const; - - status_t setRendererResourceCache(IRendererResourceCache& cache); - IRendererResourceCache* getRendererResourceCache() const; - - status_t setOffscreenBufferDoubleBufferingEnabled(bool isDoubleBuffered); - bool isOffscreenBufferDoubleBufferingEnabled() const; - - status_t setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period); - std::chrono::milliseconds getRenderThreadLoopTimingReportingPeriod() const; - - //impl methods - const ramses_internal::RendererConfig& getInternalRendererConfig() const; - - private: - ramses_internal::RendererConfig m_internalConfig; - IBinaryShaderCache* m_binaryShaderCache; - IRendererResourceCache* m_rendererResourceCache; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/RendererResourceCacheProxy.h b/renderer/ramses-renderer-impl/include/RendererResourceCacheProxy.h deleted file mode 100644 index be0ea3896..000000000 --- a/renderer/ramses-renderer-impl/include/RendererResourceCacheProxy.h +++ /dev/null @@ -1,41 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERRESOURCECACHEPROXY_H -#define RAMSES_RENDERERRESOURCECACHEPROXY_H - -#include "RendererAPI/IRendererResourceCache.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/ResourceContentHash.h" -#include "ramses-renderer-api/Types.h" - -namespace ramses -{ - class IRendererResourceCache; - class RendererResourceCacheProxy : public ramses_internal::IRendererResourceCache - { - public: - explicit RendererResourceCacheProxy(ramses::IRendererResourceCache& cache); - ~RendererResourceCacheProxy() override; - - bool hasResource(ramses_internal::ResourceContentHash resourceId, uint32_t& size) const override; - bool getResourceData(ramses_internal::ResourceContentHash resourceId, uint8_t* buffer, uint32_t bufferSize) const override; - [[nodiscard]] bool shouldResourceBeCached(ramses_internal::ResourceContentHash resourceId, uint32_t resourceDataSize, ramses_internal::ResourceCacheFlag cacheFlag, ramses_internal::SceneId sceneId) const override; - void storeResource(ramses_internal::ResourceContentHash resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, ramses_internal::ResourceCacheFlag cacheFlag, ramses_internal::SceneId sceneId) override; - - private: - - static ramses::rendererResourceId_t ConvertInternalTypeToPublic(ramses_internal::ResourceContentHash input); - static ramses::resourceCacheFlag_t ConvertInternalTypeToPublic(ramses_internal::ResourceCacheFlag input); - static ramses::sceneId_t ConvertInternalTypeToPublic(ramses_internal::SceneId input); - - ramses::IRendererResourceCache& m_cache; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/include/RendererSceneControlImpl.h b/renderer/ramses-renderer-impl/include/RendererSceneControlImpl.h deleted file mode 100644 index e776683b2..000000000 --- a/renderer/ramses-renderer-impl/include/RendererSceneControlImpl.h +++ /dev/null @@ -1,85 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDERERSCENECONTROLIMPL_H -#define RAMSES_RENDERERSCENECONTROLIMPL_H - -#include "ramses-renderer-api/RendererSceneControl.h" -#include "StatusObjectImpl.h" -#include "RendererLib/RendererCommands.h" -#include "RendererLib/RendererEvent.h" -#include - -namespace ramses -{ - const std::array RendererSceneStateNames = - { - "Unavailable", - "Available", - "Ready", - "Rendered" - }; - ENUM_TO_STRING(ramses::RendererSceneState, RendererSceneStateNames, 4); - - class RamsesRendererImpl; - - class IRendererSceneControl - { - public: - virtual status_t setSceneState(sceneId_t sceneId, RendererSceneState state) = 0; - virtual status_t setSceneMapping(sceneId_t sceneId, displayId_t displayId) = 0; - virtual status_t setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) = 0; - virtual status_t linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) = 0; - virtual status_t linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) = 0; - virtual status_t linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) = 0; - virtual status_t unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) = 0; - virtual status_t handlePickEvent(sceneId_t scene, float bufferNormalizedCoordX, float bufferNormalizedCoordY) = 0; - virtual status_t flush() = 0; - virtual status_t dispatchEvents(IRendererSceneControlEventHandler& eventHandler) = 0; - - virtual ~IRendererSceneControl() = default; - }; - - class RendererSceneControlImpl final : public IRendererSceneControl, public StatusObjectImpl - { - public: - explicit RendererSceneControlImpl(RamsesRendererImpl& renderer); - - status_t setSceneState(sceneId_t sceneId, RendererSceneState state) override; - status_t setSceneMapping(sceneId_t sceneId, displayId_t displayId) override; - status_t setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) override; - status_t linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) override; - status_t linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) override; - virtual status_t linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); - status_t linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) override; - status_t unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) override; - status_t handlePickEvent(sceneId_t scene, float bufferNormalizedCoordX, float bufferNormalizedCoordY) override; - - status_t flush() override; - status_t dispatchEvents(IRendererSceneControlEventHandler& eventHandler) override; - - const ramses_internal::RendererCommands& getPendingCommands() const; - - private: - RamsesRendererImpl& m_renderer; - ramses_internal::RendererCommands m_pendingRendererCommands; - - struct SceneInfo - { - bool mappingSet = false; - RendererSceneState currState = RendererSceneState::Unavailable; - RendererSceneState targetState = RendererSceneState::Unavailable; - }; - std::unordered_map m_sceneInfos; - - // keep allocated containers which are used to swap internal data - ramses_internal::RendererEventVector m_tempRendererEvents; - }; -} - -#endif diff --git a/renderer/ramses-renderer-impl/src/BinaryShaderCache.cpp b/renderer/ramses-renderer-impl/src/BinaryShaderCache.cpp deleted file mode 100644 index 60a5192c8..000000000 --- a/renderer/ramses-renderer-impl/src/BinaryShaderCache.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-renderer-api/BinaryShaderCache.h" -#include "BinaryShaderCacheImpl.h" -#include "SceneAPI/ResourceContentHash.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" - -namespace ramses -{ - static ramses_internal::ResourceContentHash EffectIdToResourceContentHash(const effectId_t& effectId) - { - return ramses_internal::ResourceContentHash(effectId.lowPart, effectId.highPart); - } - - BinaryShaderCache::BinaryShaderCache() - : impl(*new BinaryShaderCacheImpl()) - { - - } - - BinaryShaderCache::~BinaryShaderCache() - { - delete &impl; - } - - void BinaryShaderCache::deviceSupportsBinaryShaderFormats(const binaryShaderFormatId_t* supportedFormats, uint32_t numSupportedFormats) - { - impl.deviceSupportsBinaryShaderFormats(supportedFormats, numSupportedFormats); - } - - bool BinaryShaderCache::hasBinaryShader(effectId_t effectId) const - { - return impl.hasBinaryShader(EffectIdToResourceContentHash(effectId)); - } - - uint32_t BinaryShaderCache::getBinaryShaderSize(effectId_t effectId) const - { - return impl.getBinaryShaderSize(EffectIdToResourceContentHash(effectId)); - } - - binaryShaderFormatId_t BinaryShaderCache::getBinaryShaderFormat(effectId_t effectId) const - { - return impl.getBinaryShaderFormat(EffectIdToResourceContentHash(effectId)); - } - - bool BinaryShaderCache::shouldBinaryShaderBeCached(effectId_t effectId, sceneId_t sceneId) const - { - return impl.shouldBinaryShaderBeCached(EffectIdToResourceContentHash(effectId), ramses_internal::SceneId{ sceneId.getValue() }); - } - - void BinaryShaderCache::getBinaryShaderData(effectId_t effectId, uint8_t* buffer, uint32_t bufferSize) const - { - impl.getBinaryShaderData(EffectIdToResourceContentHash(effectId), buffer, bufferSize); - } - - void BinaryShaderCache::storeBinaryShader(effectId_t effectId, sceneId_t sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) - { - impl.storeBinaryShader(EffectIdToResourceContentHash(effectId), ramses_internal::SceneId{ sceneId.getValue() }, binaryShaderData, binaryShaderDataSize, binaryShaderFormat); - } - - void BinaryShaderCache::saveToFile(std::string_view filePath) const - { - impl.saveToFile(filePath); - } - - bool BinaryShaderCache::loadFromFile(std::string_view filePath) - { - return impl.loadFromFile(filePath); - } - - void BinaryShaderCache::binaryShaderUploaded(effectId_t effectId, bool success) const - { - impl.binaryShaderUploaded(ramses_internal::ResourceContentHash(effectId.lowPart, effectId.highPart), success); - } -} diff --git a/renderer/ramses-renderer-impl/src/DefaultRendererResourceCache.cpp b/renderer/ramses-renderer-impl/src/DefaultRendererResourceCache.cpp deleted file mode 100644 index 09fd15583..000000000 --- a/renderer/ramses-renderer-impl/src/DefaultRendererResourceCache.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-renderer-api/DefaultRendererResourceCache.h" -#include "DefaultRendererResourceCacheImpl.h" - -namespace ramses -{ - DefaultRendererResourceCache::DefaultRendererResourceCache(uint32_t maxCacheSizeInBytes) - : impl(*new DefaultRendererResourceCacheImpl(maxCacheSizeInBytes)) - { } - - DefaultRendererResourceCache::~DefaultRendererResourceCache() - { - delete &impl; - } - - bool DefaultRendererResourceCache::hasResource(rendererResourceId_t resourceId, uint32_t& size) const - { - return impl.hasResource(resourceId, size); - } - - bool DefaultRendererResourceCache::getResourceData(rendererResourceId_t resourceId, uint8_t* buffer, uint32_t bufferSize) const - { - return impl.getResourceData(resourceId, buffer, bufferSize); - } - - bool DefaultRendererResourceCache::shouldResourceBeCached(rendererResourceId_t resourceId, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) const - { - return impl.shouldResourceBeCached(resourceId, resourceDataSize, cacheFlag, sceneId); - } - - void DefaultRendererResourceCache::storeResource(rendererResourceId_t resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) - { - impl.storeResource(resourceId, resourceData, resourceDataSize, cacheFlag, sceneId); - } - - void DefaultRendererResourceCache::saveToFile(std::string_view filePath) const - { - impl.saveToFile(filePath); - } - - bool DefaultRendererResourceCache::loadFromFile(std::string_view filePath) - { - return impl.loadFromFile(filePath); - } -} diff --git a/renderer/ramses-renderer-impl/src/DefaultRendererResourceCacheImpl.cpp b/renderer/ramses-renderer-impl/src/DefaultRendererResourceCacheImpl.cpp deleted file mode 100644 index 770b0aa87..000000000 --- a/renderer/ramses-renderer-impl/src/DefaultRendererResourceCacheImpl.cpp +++ /dev/null @@ -1,349 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DefaultRendererResourceCacheImpl.h" -#include "Utils/File.h" -#include "Utils/LogMacros.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/Adler32Checksum.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" -#include - -namespace ramses -{ - class IDataFunctor - { - public: - virtual ~IDataFunctor() {} - virtual void addData(const void* data, uint32_t size) = 0; - }; - - class ChecksumDataFunctor : public IDataFunctor - { - public: - ChecksumDataFunctor() - : m_totalSize(0) - { - } - - void addData(const void* data, uint32_t size) override - { - m_checksum.addData(reinterpret_cast(data), size); - m_totalSize += size; - } - - ramses_internal::Adler32Checksum m_checksum; - uint32_t m_totalSize; - }; - - class SaveDataFunctor : public IDataFunctor - { - public: - explicit SaveDataFunctor(ramses_internal::IOutputStream& outputStream) - : m_outputStream(outputStream) - { - } - - void addData(const void* data, uint32_t size) override - { - m_outputStream.write(data, size); - } - - ramses_internal::IOutputStream& m_outputStream; - }; - - DefaultRendererResourceCacheImpl::DefaultRendererResourceCacheImpl(uint32_t maxCacheSizeInBytes) - : m_maxCacheSizeInBytes(maxCacheSizeInBytes) - , m_currentCacheSizeInBytes(0) - { - assert(maxCacheSizeInBytes > 0); - } - - DefaultRendererResourceCacheImpl::~DefaultRendererResourceCacheImpl() - { - clear(); - } - - void DefaultRendererResourceCacheImpl::clear() - { - m_resourceData.clear(); - m_currentCacheSizeInBytes = 0; - } - - bool DefaultRendererResourceCacheImpl::hasResource(rendererResourceId_t resourceId, uint32_t& size) const - { - for (const auto &it : m_resourceData) - { - if (it.resourceId == resourceId) - { - size = static_cast(it.data.size()); - return true; - } - } - - size = 0; - return false; - } - - bool DefaultRendererResourceCacheImpl::getResourceData(rendererResourceId_t resourceId, uint8_t* buffer, uint32_t bufferSize) const - { - for (const auto &it : m_resourceData) - { - if (it.resourceId == resourceId) - { - if (bufferSize < it.data.size()) - { - return false; - } - - ramses_internal::PlatformMemory::Copy(buffer, it.data.data(), it.data.size()); - return true; - } - } - - assert(false); - return false; - } - - bool DefaultRendererResourceCacheImpl::shouldResourceBeCached(rendererResourceId_t resourceId, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) const - { - UNUSED(resourceId); - UNUSED(sceneId); - - // This is the place to put an algorithm for deciding if something should be cached or not. - - if (resourceDataSize > m_maxCacheSizeInBytes) - { - return false; - } - - return cacheFlag.getValue() != ResourceCacheFlag_DoNotCache.getValue(); - } - - void DefaultRendererResourceCacheImpl::storeResource(rendererResourceId_t resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, resourceCacheFlag_t cacheFlag, sceneId_t sceneId) - { - UNUSED(cacheFlag); - UNUSED(sceneId); - - ByteVector data; - data.resize(resourceDataSize); - ramses_internal::PlatformMemory::Copy(data.data(), resourceData, resourceDataSize); - - const bool storingSuccessful = storeResourceInternal(resourceId, data); - assert(storingSuccessful); - UNUSED(storingSuccessful); - } - - bool DefaultRendererResourceCacheImpl::storeResourceInternal(rendererResourceId_t resourceId, ByteVector& data) - { - uint32_t tempSize; - if (hasResource(resourceId, tempSize)) - { - return false; - } - UNUSED(tempSize); - - if (data.size() > m_maxCacheSizeInBytes || data.empty()) - { - return false; - } - - makeSpaceForNewItem(static_cast(data.size())); - if (m_currentCacheSizeInBytes + data.size() > m_maxCacheSizeInBytes) - { - return false; - } - - ResourceData newData; - newData.data.swap(data); - newData.resourceId = resourceId; - - m_resourceData.push_back(newData); - m_currentCacheSizeInBytes += static_cast(newData.data.size()); - - return m_currentCacheSizeInBytes <= m_maxCacheSizeInBytes; - } - - void DefaultRendererResourceCacheImpl::makeSpaceForNewItem(uint32_t newItemSizeInBytes) - { - assert(newItemSizeInBytes <= m_maxCacheSizeInBytes); - - while (m_currentCacheSizeInBytes + newItemSizeInBytes > m_maxCacheSizeInBytes) - { - removeOldestItem(); - } - } - - void DefaultRendererResourceCacheImpl::removeOldestItem() - { - assert(!m_resourceData.empty()); - m_currentCacheSizeInBytes -= static_cast(m_resourceData[0].data.size()); - m_resourceData.pop_front(); - } - - void DefaultRendererResourceCacheImpl::iterateDataToSave(IDataFunctor& functor) const - { - const uint32_t numberOfEntries = static_cast(m_resourceData.size()); - functor.addData(&numberOfEntries, sizeof(numberOfEntries)); - - for (const auto &it : m_resourceData) - { - const uint32_t dataSize = static_cast(it.data.size()); - - functor.addData(&it.resourceId, sizeof(it.resourceId)); - functor.addData(&dataSize, sizeof(dataSize)); - functor.addData(it.data.data(), dataSize); - } - } - - void DefaultRendererResourceCacheImpl::saveToFile(std::string_view filePath) const - { - ramses_internal::File file(filePath); - ramses_internal::BinaryFileOutputStream outputStream(file); - - if (outputStream.getState() != ramses_internal::EStatus::Ok) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "BinaryShaderCacheImpl::saveToFile: Failed to open for writing " << filePath); - file.close(); - return; - } - - ChecksumDataFunctor checksumFunctor; - iterateDataToSave(checksumFunctor); - - FileHeader header = {}; - header.fileSize = static_cast(checksumFunctor.m_totalSize + sizeof(header)); - header.transportVersion = RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR; - header.checksum = checksumFunctor.m_checksum.getResult(); - - outputStream.write(&header, sizeof(header)); - - SaveDataFunctor saveFunctor(outputStream); - iterateDataToSave(saveFunctor); - } - - bool DefaultRendererResourceCacheImpl::loadFromFile(std::string_view filePath) - { - clear(); - - ramses_internal::File file(filePath); - - if (!file.exists()) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "DefaultRendererResourceCacheImpl::loadFromFile: file does not exist: " << filePath); - return false; - } - - ramses_internal::BinaryFileInputStream inputStream(file); - if (inputStream.getState() != ramses_internal::EStatus::Ok) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "DefaultRendererResourceCacheImpl::loadFromFile: failed to load file: " << filePath << " errorstate: " << inputStream.getState()); - return false; - } - - FileHeader fileHeader; - - size_t actualFileSize = 0; - if (!file.getSizeInBytes(actualFileSize) || actualFileSize < sizeof(FileHeader)) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, - "DefaultRendererResourceCacheImpl::loadFromFile: Invalid file size, file is corrupt - cache needs to be repopulated and saved again"); - return false; - } - - inputStream >> fileHeader.fileSize; - - if (actualFileSize != fileHeader.fileSize) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, - "DefaultRendererResourceCacheImpl::loadFromFile: File did not match the size stored in the file header, file " - "is corrupt - cache needs to be repopulated and saved again"); - return false; - } - - inputStream >> fileHeader.transportVersion; - - if (fileHeader.transportVersion != RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, - "DefaultRendererResourceCacheImpl::loadFromFile: File version " - << fileHeader.transportVersion << " did not match the program version " - << RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR - << " - cache needs to be repopulated and saved again"); - return false; - } - - ramses_internal::Adler32Checksum calculatedChecksum; - - inputStream >> fileHeader.checksum; - - uint32_t itemCount = 0; - inputStream >> itemCount; - calculatedChecksum.addData(reinterpret_cast(&itemCount), sizeof(itemCount)); - - for (uint32_t i = 0; i < itemCount; i++) - { - uint64_t resIdLowPart = 0; - inputStream >> resIdLowPart; - calculatedChecksum.addData(reinterpret_cast(&resIdLowPart), sizeof(resIdLowPart)); - - uint64_t resIdHighPart = 0; - inputStream >> resIdHighPart; - calculatedChecksum.addData(reinterpret_cast(&resIdHighPart), sizeof(resIdHighPart)); - - uint32_t resourceSize = 0; - inputStream >> resourceSize; - calculatedChecksum.addData(reinterpret_cast(&resourceSize), sizeof(resourceSize)); - - if (resourceSize > actualFileSize) - { - // resourceSize is larger than whole file size, so it must be wrong - LOG_WARN(ramses_internal::CONTEXT_RENDERER, - "DefaultRendererResourceCacheImpl::loadFromFile: Wrong resourceSize: " << resourceSize << - ", file is corrupt - cache needs to be repopulated and saved again"); - clear(); - return false; - } - - ByteVector data; - data.resize(resourceSize); - inputStream.read(reinterpret_cast(data.data()), resourceSize); - calculatedChecksum.addData(data.data(), resourceSize); - - if (!storeResourceInternal(rendererResourceId_t(resIdLowPart, resIdHighPart), data)) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "DefaultRendererResourceCacheImpl::loadFromFile: storeResourceInternal failed" - << ", either file is corrupt or cache size too small - cache needs to be repopulated and saved again"); - clear(); - return false; - } - - if (inputStream.getState() != ramses_internal::EStatus::Ok) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, - "DefaultRendererResourceCacheImpl::loadFromFile: Reading failed, file is corrupt - cache " - "needs to be repopulated and saved again"); - clear(); - return false; - } - } - - if (calculatedChecksum.getResult() != fileHeader.checksum) - { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, - "DefaultRendererResourceCacheImpl::loadFromFile: Checksum was wrong, file is corrupt - cache " - "needs to be repopulated and saved again"); - clear(); - return false; - } - - return true; - } -} diff --git a/renderer/ramses-renderer-impl/src/DisplayConfig.cpp b/renderer/ramses-renderer-impl/src/DisplayConfig.cpp deleted file mode 100644 index 2ac8c6011..000000000 --- a/renderer/ramses-renderer-impl/src/DisplayConfig.cpp +++ /dev/null @@ -1,276 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-renderer-api/DisplayConfig.h" - -// internal -#include "DisplayConfigImpl.h" -#include "RamsesFrameworkTypesImpl.h" - -namespace ramses -{ - DisplayConfig::DisplayConfig() - : StatusObject{ std::make_unique() } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - DisplayConfig::~DisplayConfig() = default; - - DisplayConfig::DisplayConfig(const DisplayConfig& other) - : StatusObject{ std::make_unique(other.m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - DisplayConfig::DisplayConfig(DisplayConfig&& other) noexcept - : StatusObject{ std::move(other.StatusObject::m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - DisplayConfig& DisplayConfig::operator=(const DisplayConfig& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - DisplayConfig& DisplayConfig::operator=(DisplayConfig&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - status_t DisplayConfig::setDeviceType(EDeviceType deviceType) - { - return m_impl.get().setDeviceType(deviceType); - } - - EDeviceType DisplayConfig::getDeviceType() const - { - return m_impl.get().getDeviceType(); - } - - status_t DisplayConfig::setWindowType(EWindowType windowType) - { - return m_impl.get().setWindowType(windowType); - } - - EWindowType DisplayConfig::getWindowType() const - { - return m_impl.get().getWindowType(); - } - - status_t DisplayConfig::setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height) - { - const status_t status = m_impl.get().setWindowRectangle(x, y, width, height); - LOG_HL_RENDERER_API4(status, x, y, width, height); - return status; - } - - status_t DisplayConfig::getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const - { - return m_impl.get().getWindowRectangle(x, y, width, height); - } - - status_t DisplayConfig::setWindowFullscreen(bool fullscreen) - { - const status_t status = m_impl.get().setFullscreen(fullscreen); - LOG_HL_RENDERER_API1(status, fullscreen); - return status; - } - - bool DisplayConfig::isWindowFullscreen() const - { - return m_impl.get().isFullscreen(); - } - - status_t DisplayConfig::setWindowBorderless(bool borderless) - { - const status_t status = m_impl.get().setBorderless(borderless); - LOG_HL_RENDERER_API1(status, borderless); - return status; - } - - status_t DisplayConfig::setMultiSampling(uint32_t numSamples) - { - const status_t status = m_impl.get().setMultiSampling(numSamples); - LOG_HL_RENDERER_API1(status, numSamples); - return status; - } - - status_t DisplayConfig::getMultiSamplingSamples(uint32_t& numSamples) const - { - return m_impl.get().getMultiSamplingSamples(numSamples); - } - - status_t DisplayConfig::setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID) - { - const status_t status = m_impl.get().setWaylandIviLayerID(waylandIviLayerID); - LOG_HL_RENDERER_API1(status, waylandIviLayerID); - return status; - } - - waylandIviLayerId_t DisplayConfig::getWaylandIviLayerID() const - { - return m_impl.get().getWaylandIviLayerID(); - } - - status_t DisplayConfig::setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID) - { - const status_t status = m_impl.get().setWaylandIviSurfaceID(waylandIviSurfaceID); - LOG_HL_RENDERER_API1(status, waylandIviSurfaceID.getValue()); - return status; - } - - waylandIviSurfaceId_t DisplayConfig::getWaylandIviSurfaceID() const - { - return m_impl.get().getWaylandIviSurfaceID(); - } - - status_t DisplayConfig::setWaylandDisplay(std::string_view waylandDisplay) - { - return m_impl.get().setWaylandDisplay(waylandDisplay); - } - - std::string_view DisplayConfig::getWaylandDisplay() const - { - return m_impl.get().getWaylandDisplay(); - } - - status_t DisplayConfig::setAsyncEffectUploadEnabled(bool enabled) - { - const status_t status = m_impl.get().setAsyncEffectUploadEnabled(enabled); - LOG_HL_RENDERER_API1(status, enabled); - return status; - } - - void* DisplayConfig::getAndroidNativeWindow() const - { - return m_impl.get().getAndroidNativeWindow(); - } - - status_t DisplayConfig::setAndroidNativeWindow(void* nativeWindowPtr) - { - return m_impl.get().setAndroidNativeWindow(nativeWindowPtr); - } - - status_t DisplayConfig::setWindowIviVisible() - { - const status_t status = m_impl.get().setWindowIviVisible(true); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - status_t DisplayConfig::keepEffectsUploaded(bool enable) - { - const status_t status = m_impl.get().keepEffectsUploaded(enable); - LOG_HL_RENDERER_API1(status, enable); - return status; - } - - status_t DisplayConfig::setGPUMemoryCacheSize(uint64_t size) - { - const status_t status = m_impl.get().setGPUMemoryCacheSize(size); - LOG_HL_RENDERER_API1(status, size); - return status; - } - - status_t DisplayConfig::setResizable(bool resizable) - { - const status_t status = m_impl.get().setResizable(resizable); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - status_t DisplayConfig::setClearColor(const vec4f& color) - { - const status_t status = m_impl.get().setClearColor(color); - LOG_HL_RENDERER_API4(status, color.r, color.g, color.b, color.a); - return status; - } - - status_t DisplayConfig::setDepthStencilBufferType(EDepthBufferType depthBufferType) - { - const auto status = m_impl.get().setDepthStencilBufferType(depthBufferType); - LOG_HL_RENDERER_API1(status, depthBufferType); - return status; - } - - status_t DisplayConfig::setX11WindowHandle(unsigned long x11WindowHandle) - { - const status_t status = m_impl.get().setX11WindowHandle(x11WindowHandle); - LOG_HL_RENDERER_API1(status, x11WindowHandle); - return status; - } - - unsigned long DisplayConfig::getX11WindowHandle() const - { - return m_impl.get().getX11WindowHandle(); - } - - status_t DisplayConfig::setWindowsWindowHandle(void* hwnd) - { - const status_t status = m_impl.get().setWindowsWindowHandle(hwnd); - LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_PTR_STRING(hwnd)); - return status; - } - - void* DisplayConfig::getWindowsWindowHandle() const - { - return m_impl.get().getWindowsWindowHandle(); - } - - status_t DisplayConfig::setWaylandEmbeddedCompositingSocketName(std::string_view socketname) - { - return m_impl.get().setWaylandEmbeddedCompositingSocketName(socketname); - } - - std::string_view DisplayConfig::getWaylandEmbeddedCompositingSocketName() const - { - return m_impl.get().getWaylandEmbeddedCompositingSocketName(); - } - - status_t DisplayConfig::setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname) - { - return m_impl.get().setWaylandEmbeddedCompositingSocketGroup(groupname); - } - - status_t DisplayConfig::setWaylandEmbeddedCompositingSocketFD(int socketFileDescriptor) - { - return m_impl.get().setWaylandEmbeddedCompositingSocketFD(socketFileDescriptor); - } - - status_t DisplayConfig::setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions) - { - return m_impl.get().setWaylandEmbeddedCompositingSocketPermissions(permissions); - } - - status_t DisplayConfig::setPlatformRenderNode(std::string_view renderNode) - { - return m_impl.get().setPlatformRenderNode(renderNode); - } - - status_t DisplayConfig::setSwapInterval(int32_t interval) - { - return m_impl.get().setSwapInterval(interval); - } - - status_t DisplayConfig::setScenePriority(sceneId_t sceneId, int32_t priority) - { - return m_impl.get().setScenePriority(sceneId, priority); - } - - status_t DisplayConfig::setResourceUploadBatchSize(uint32_t batchSize) - { - return m_impl.get().setResourceUploadBatchSize(batchSize); - } -} diff --git a/renderer/ramses-renderer-impl/src/DisplayConfigImpl.cpp b/renderer/ramses-renderer-impl/src/DisplayConfigImpl.cpp deleted file mode 100644 index 288e687fb..000000000 --- a/renderer/ramses-renderer-impl/src/DisplayConfigImpl.cpp +++ /dev/null @@ -1,388 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "DisplayConfigImpl.h" -#include "RendererAPI/EDeviceType.h" - -namespace ramses -{ - DisplayConfigImpl::DisplayConfigImpl() - : StatusObjectImpl() - { - } - - status_t DisplayConfigImpl::setDeviceType(EDeviceType deviceType) - { - switch (deviceType) - { - case EDeviceType::GLES_3_0: - m_internalConfig.setDeviceType(ramses_internal::EDeviceType::GLES_3_0); - break; - case EDeviceType::GL_4_2: - m_internalConfig.setDeviceType(ramses_internal::EDeviceType::GL_4_2); - break; - case EDeviceType::GL_4_5: - m_internalConfig.setDeviceType(ramses_internal::EDeviceType::GL_4_5); - break; - } - - return StatusOK; - } - - EDeviceType DisplayConfigImpl::getDeviceType() const - { - switch (m_internalConfig.getDeviceType()) - { - case ramses_internal::EDeviceType::GLES_3_0: - return EDeviceType::GLES_3_0; - case ramses_internal::EDeviceType::GL_4_2: - return EDeviceType::GL_4_2; - case ramses_internal::EDeviceType::GL_4_5: - return EDeviceType::GL_4_5; - } - - assert(false); - return EDeviceType::GLES_3_0; - } - - status_t DisplayConfigImpl::setWindowType(EWindowType windowType) - { - switch (windowType) - { - case ramses::EWindowType::Windows: - m_internalConfig.setWindowType(ramses_internal::EWindowType::Windows); - break; - case ramses::EWindowType::X11: - m_internalConfig.setWindowType(ramses_internal::EWindowType::X11); - break; - case ramses::EWindowType::Wayland_IVI: - m_internalConfig.setWindowType(ramses_internal::EWindowType::Wayland_IVI); - break; - case ramses::EWindowType::Wayland_Shell: - m_internalConfig.setWindowType(ramses_internal::EWindowType::Wayland_Shell); - break; - case ramses::EWindowType::Android: - m_internalConfig.setWindowType(ramses_internal::EWindowType::Android); - break; - } - - return StatusOK; - } - - EWindowType DisplayConfigImpl::getWindowType() const - { - switch (m_internalConfig.getWindowType()) - { - case ramses_internal::EWindowType::Windows: - return EWindowType::Windows; - case ramses_internal::EWindowType::X11: - return EWindowType::X11; - case ramses_internal::EWindowType::Wayland_IVI: - return EWindowType::Wayland_IVI; - case ramses_internal::EWindowType::Wayland_Shell: - return EWindowType::Wayland_Shell; - case ramses_internal::EWindowType::Android: - return EWindowType::Android; - } - - assert(false); - return EWindowType::Windows; - } - - status_t DisplayConfigImpl::setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height) - { - if (width == 0u || height == 0u) - { - return addErrorEntry("DisplayConfig::setWindowRectangle failed - width and/or height cannot be 0!"); - } - - m_internalConfig.setWindowPositionX(x); - m_internalConfig.setWindowPositionY(y); - m_internalConfig.setDesiredWindowWidth(width); - m_internalConfig.setDesiredWindowHeight(height); - - return StatusOK; - } - - status_t DisplayConfigImpl::getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const - { - x = m_internalConfig.getWindowPositionX(); - y = m_internalConfig.getWindowPositionY(); - width = m_internalConfig.getDesiredWindowWidth(); - height = m_internalConfig.getDesiredWindowHeight(); - return StatusOK; - } - - status_t DisplayConfigImpl::setFullscreen(bool fullscreen) - { - m_internalConfig.setFullscreenState(fullscreen); - return StatusOK; - } - - bool DisplayConfigImpl::isFullscreen() const - { - return m_internalConfig.getFullscreenState(); - } - - status_t DisplayConfigImpl::setBorderless(bool borderless) - { - m_internalConfig.setBorderlessState(borderless); - return StatusOK; - } - - const ramses_internal::DisplayConfig& DisplayConfigImpl::getInternalDisplayConfig() const - { - return m_internalConfig; - } - - status_t DisplayConfigImpl::setMultiSampling(uint32_t numSamples) - { - if (!ramses_internal::contains_c({ 1u, 2u, 4u, 8u }, numSamples)) - return addErrorEntry("DisplayConfigImpl::setMultiSampling failed - sample count must be 1, 2, 4 or 8!"); - - m_internalConfig.setAntialiasingSampleCount(numSamples); - - return StatusOK; - } - - status_t DisplayConfigImpl::getMultiSamplingSamples(uint32_t& numSamples) const - { - const uint32_t sampleCount = m_internalConfig.getAntialiasingSampleCount(); - numSamples = sampleCount; - - return StatusOK; - } - - status_t DisplayConfigImpl::setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID) - { - m_internalConfig.setWaylandIviLayerID(ramses_internal::WaylandIviLayerId(waylandIviLayerID.getValue())); - return StatusOK; - } - - waylandIviLayerId_t DisplayConfigImpl::getWaylandIviLayerID() const - { - return waylandIviLayerId_t(m_internalConfig.getWaylandIviLayerID().getValue()); - } - - status_t DisplayConfigImpl::setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID) - { - m_internalConfig.setWaylandIviSurfaceID(ramses_internal::WaylandIviSurfaceId(waylandIviSurfaceID.getValue())); - return StatusOK; - } - - waylandIviSurfaceId_t DisplayConfigImpl::getWaylandIviSurfaceID() const - { - return waylandIviSurfaceId_t(m_internalConfig.getWaylandIviSurfaceID().getValue()); - } - - void* DisplayConfigImpl::getAndroidNativeWindow() const - { - return m_internalConfig.getAndroidNativeWindow().getValue(); - } - - status_t DisplayConfigImpl::setAndroidNativeWindow(void * nativeWindowPtr) - { - m_internalConfig.setAndroidNativeWindow(ramses_internal::AndroidNativeWindowPtr(nativeWindowPtr)); - return StatusOK; - } - - status_t DisplayConfigImpl::setWindowIviVisible(bool visible) - { - m_internalConfig.setStartVisibleIvi(visible); - return StatusOK; - } - - status_t DisplayConfigImpl::setResizable(bool resizable) - { - m_internalConfig.setResizable(resizable); - return StatusOK; - } - - status_t DisplayConfigImpl::keepEffectsUploaded(bool enable) - { - m_internalConfig.setKeepEffectsUploaded(enable); - return StatusOK; - } - - status_t DisplayConfigImpl::setGPUMemoryCacheSize(uint64_t size) - { - m_internalConfig.setGPUMemoryCacheSize(size); - return StatusOK; - } - - status_t DisplayConfigImpl::setClearColor(const vec4f& color) - { - m_internalConfig.setClearColor(color); - return StatusOK; - } - - status_t DisplayConfigImpl::setDepthStencilBufferType(EDepthBufferType depthBufferType) - { - switch (depthBufferType) - { - case EDepthBufferType::None: - m_internalConfig.setDepthStencilBufferType(ramses_internal::ERenderBufferType_InvalidBuffer); - break; - case EDepthBufferType::Depth: - m_internalConfig.setDepthStencilBufferType(ramses_internal::ERenderBufferType_DepthBuffer); - break; - case EDepthBufferType::DepthStencil: - m_internalConfig.setDepthStencilBufferType(ramses_internal::ERenderBufferType_DepthStencilBuffer); - break; - } - - return StatusOK; - } - - status_t DisplayConfigImpl::setX11WindowHandle(unsigned long x11WindowHandle) - { - m_internalConfig.setX11WindowHandle(ramses_internal::X11WindowHandle(x11WindowHandle)); - return StatusOK; - } - - unsigned long DisplayConfigImpl::getX11WindowHandle() const - { - return m_internalConfig.getX11WindowHandle().getValue(); - } - - status_t DisplayConfigImpl::setWindowsWindowHandle(void* hwnd) - { - m_internalConfig.setWindowsWindowHandle(ramses_internal::WindowsWindowHandle(hwnd)); - return StatusOK; - } - - void* DisplayConfigImpl::getWindowsWindowHandle() const - { - return m_internalConfig.getWindowsWindowHandle().getValue(); - } - - status_t DisplayConfigImpl::setWaylandDisplay(std::string_view waylandDisplay) - { - m_internalConfig.setWaylandDisplay(waylandDisplay); - return StatusOK; - } - - std::string_view DisplayConfigImpl::getWaylandDisplay() const - { - return m_internalConfig.getWaylandDisplay(); - } - - status_t DisplayConfigImpl::setAsyncEffectUploadEnabled(bool enabled) - { - m_internalConfig.setAsyncEffectUploadEnabled(enabled); - return StatusOK; - } - - status_t DisplayConfigImpl::setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname) - { - m_internalConfig.setWaylandEmbeddedCompositingSocketGroup(groupname); - return StatusOK; - } - - std::string_view DisplayConfigImpl::getWaylandSocketEmbeddedGroup() const - { - return m_internalConfig.getWaylandSocketEmbeddedGroup(); - } - - status_t DisplayConfigImpl::setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions) - { - if (m_internalConfig.setWaylandEmbeddedCompositingSocketPermissions(permissions)) - return StatusOK; - return addErrorEntry("DisplayConfig::setWaylandEmbeddedCompositingSocketPermissions failed"); - } - - uint32_t DisplayConfigImpl::getWaylandSocketEmbeddedPermissions() const - { - return m_internalConfig.getWaylandSocketEmbeddedPermissions(); - } - - status_t DisplayConfigImpl::setWaylandEmbeddedCompositingSocketName(std::string_view socketname) - { - m_internalConfig.setWaylandEmbeddedCompositingSocketName(socketname); - return StatusOK; - } - - std::string_view DisplayConfigImpl::getWaylandEmbeddedCompositingSocketName() const - { - return m_internalConfig.getWaylandSocketEmbedded(); - } - - status_t DisplayConfigImpl::setWaylandEmbeddedCompositingSocketFD(int fd) - { - m_internalConfig.setWaylandEmbeddedCompositingSocketFD(fd); - return StatusOK; - } - - int DisplayConfigImpl::getWaylandSocketEmbeddedFD() const - { - return m_internalConfig.getWaylandSocketEmbeddedFD(); - } - - status_t DisplayConfigImpl::setPlatformRenderNode(std::string_view renderNode) - { - m_internalConfig.setPlatformRenderNode(renderNode); - return StatusOK; - } - - std::string_view DisplayConfigImpl::getPlatformRenderNode() const - { - return m_internalConfig.getPlatformRenderNode(); - } - - status_t DisplayConfigImpl::setSwapInterval(int32_t interval) - { - m_internalConfig.setSwapInterval(interval); - return StatusOK; - } - - int32_t DisplayConfigImpl::getSwapInterval() const - { - return m_internalConfig.getSwapInterval(); - } - - status_t DisplayConfigImpl::setScenePriority(sceneId_t sceneId, int32_t priority) - { - m_internalConfig.setScenePriority(ramses_internal::SceneId(sceneId.getValue()), priority); - return StatusOK; - } - - int32_t DisplayConfigImpl::getScenePriority(sceneId_t sceneId) const - { - return m_internalConfig.getScenePriority(ramses_internal::SceneId(sceneId.getValue())); - } - - status_t DisplayConfigImpl::setResourceUploadBatchSize(uint32_t batchSize) - { - if (batchSize == 0) - { - return addErrorEntry("DisplayConfig::setResourceUploadBatchSize failed - batchSize cannot be 0!"); - } - m_internalConfig.setResourceUploadBatchSize(batchSize); - return StatusOK; - } - - uint32_t DisplayConfigImpl::getResourceUploadBatchSize() const - { - return m_internalConfig.getResourceUploadBatchSize(); - } - - status_t DisplayConfigImpl::validate() const - { - status_t status = StatusObjectImpl::validate(); - - const auto embeddedCompositorFilename = m_internalConfig.getWaylandSocketEmbedded(); - int embeddedCompositorFileDescriptor = m_internalConfig.getWaylandSocketEmbeddedFD(); - - if(embeddedCompositorFilename.size() == 0u && embeddedCompositorFileDescriptor < 0) - status = addValidationMessage(EValidationSeverity::Info, "no socket information for EmbeddedCompositor set (neither file descriptor nor file name). No embedded compositor available."); - else if(embeddedCompositorFilename.size() > 0u && embeddedCompositorFileDescriptor >= 0) - status = addValidationMessage(EValidationSeverity::Info, "Competing settings for EmbeddedCompositor are set (file descriptor and file name). File descriptor setting will be preferred."); - - return status; - } -} diff --git a/renderer/ramses-renderer-impl/src/RamsesRenderer.cpp b/renderer/ramses-renderer-impl/src/RamsesRenderer.cpp deleted file mode 100644 index fcabbf44d..000000000 --- a/renderer/ramses-renderer-impl/src/RamsesRenderer.cpp +++ /dev/null @@ -1,260 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "RamsesRendererImpl.h" -#include "RamsesFrameworkImpl.h" -#include "RamsesFrameworkTypesImpl.h" - -namespace ramses -{ - RamsesRenderer::RamsesRenderer(std::unique_ptr impl) - : StatusObject{ std::move(impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - status_t RamsesRenderer::doOneLoop() - { - const status_t status = m_impl.doOneLoop(); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - ramses::status_t RamsesRenderer::startThread() - { - const status_t status = m_impl.startThread(); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - ramses::status_t RamsesRenderer::stopThread() - { - const status_t status = m_impl.stopThread(); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - bool RamsesRenderer::isThreadRunning() const - { - return m_impl.isThreadRunning(); - } - - ramses::status_t RamsesRenderer::setFramerateLimit(displayId_t displayId, float fpsLimit) - { - const status_t status = m_impl.setFramerateLimit(displayId, fpsLimit); - LOG_HL_RENDERER_API2(status, displayId, fpsLimit); - return status; - } - - float RamsesRenderer::getFramerateLimit(displayId_t displayId) const - { - return m_impl.getFramerateLimit(displayId); - } - - ramses::status_t RamsesRenderer::setLoopMode(ELoopMode loopMode) - { - const status_t status = m_impl.setLoopMode(loopMode); - LOG_HL_RENDERER_API1(status, loopMode); - return status; - } - - ELoopMode RamsesRenderer::getLoopMode() const - { - return m_impl.getLoopMode(); - } - - displayId_t RamsesRenderer::createDisplay(const DisplayConfig& config) - { - const displayId_t displayId = m_impl.createDisplay(config); - LOG_HL_RENDERER_API1(displayId, LOG_API_GENERIC_OBJECT_STRING(config)); - return displayId; - } - - status_t RamsesRenderer::destroyDisplay(displayId_t displayId) - { - const status_t status = m_impl.destroyDisplay(displayId); - LOG_HL_RENDERER_API1(status, displayId); - return status; - } - - displayBufferId_t RamsesRenderer::getDisplayFramebuffer(displayId_t displayId) const - { - return m_impl.getDisplayFramebuffer(displayId); - } - - RendererSceneControl* RamsesRenderer::getSceneControlAPI() - { - return m_impl.getSceneControlAPI(); - } - - status_t RamsesRenderer::dispatchEvents(IRendererEventHandler& rendererEventHandler) - { - const status_t status = m_impl.dispatchEvents(rendererEventHandler); - LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(rendererEventHandler)); - return status; - } - - status_t RamsesRenderer::setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height) - { - const status_t status = m_impl.setExternallyOwnedWindowSize(display, width, height); - LOG_HL_RENDERER_API3(status, display, width, height); - return status; - } - - displayBufferId_t RamsesRenderer::createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount, EDepthBufferType depthBufferType) - { - const displayBufferId_t bufferId = m_impl.createOffscreenBuffer(display, width, height, sampleCount, false, depthBufferType); - LOG_HL_RENDERER_API5(bufferId, display, width, height, sampleCount, depthBufferType); - return bufferId; - } - - status_t RamsesRenderer::destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer) - { - const status_t status = m_impl.destroyOffscreenBuffer(display, offscreenBuffer); - LOG_HL_RENDERER_API2(status, display, offscreenBuffer); - return status; - } - - externalBufferId_t RamsesRenderer::createExternalBuffer(displayId_t display) - { - const auto bufferId = m_impl.createExternalBuffer(display); - LOG_HL_RENDERER_API1(bufferId, display); - return bufferId; - } - - status_t RamsesRenderer::destroyExternalBuffer(displayId_t display, externalBufferId_t externalBuffer) - { - const auto status = m_impl.destroyExternalBuffer(display, externalBuffer); - LOG_HL_RENDERER_API2(status, display, externalBuffer); - return status; - } - - streamBufferId_t RamsesRenderer::createStreamBuffer(displayId_t display, ramses::waylandIviSurfaceId_t surfaceId) - { - return m_impl.createStreamBuffer(display, surfaceId); - } - - status_t RamsesRenderer::destroyStreamBuffer(displayId_t display, ramses::streamBufferId_t bufferId) - { - return m_impl.destroyStreamBuffer(display, bufferId); - } - - status_t RamsesRenderer::setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, uint32_t clearFlags) - { - const status_t status = m_impl.setDisplayBufferClearFlags(display, displayBuffer, clearFlags); - LOG_HL_RENDERER_API3(status, display, displayBuffer, clearFlags); - return status; - } - - status_t RamsesRenderer::setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color) - { - const status_t status = m_impl.setDisplayBufferClearColor(display, displayBuffer, color); - LOG_HL_RENDERER_API6(status, display, displayBuffer, color.r, color.g, color.b, color.a); - return status; - } - - status_t RamsesRenderer::readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height) - { - const status_t status = m_impl.readPixels(displayId, displayBuffer, x, y, width, height); - LOG_HL_RENDERER_API6(status, displayId, displayBuffer, x, y, width, height); - return status; - } - - status_t RamsesRenderer::flush() - { - const status_t result = m_impl.flush(); - LOG_HL_RENDERER_API_NOARG(result); - return result; - } - - status_t RamsesRenderer::setSurfaceVisibility(uint32_t surfaceId, bool visibility) - { - const status_t status = m_impl.systemCompositorSetIviSurfaceVisibility(surfaceId, visibility); - LOG_HL_RENDERER_API2(status, surfaceId, visibility); - return status; - } - - status_t RamsesRenderer::setSurfaceOpacity(uint32_t surfaceId, float opacity) - { - const status_t status = m_impl.systemCompositorSetIviSurfaceOpacity(surfaceId, opacity); - LOG_HL_RENDERER_API2(status, surfaceId, opacity); - return status; - } - - status_t RamsesRenderer::setSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height) - { - const status_t status = m_impl.systemCompositorSetIviSurfaceRectangle(surfaceId, x, y, width, height); - LOG_HL_RENDERER_API5(status, surfaceId, x, y, width, height); - return status; - } - - status_t RamsesRenderer::setLayerVisibility(uint32_t layerId, bool visibility) - { - const status_t status = m_impl.systemCompositorSetIviLayerVisibility(layerId, visibility); - LOG_HL_RENDERER_API2(status, layerId, visibility); - return status; - } - - status_t RamsesRenderer::takeSystemCompositorScreenshot(std::string_view fileName, int32_t screenIviId) - { - const status_t status = m_impl.systemCompositorTakeScreenshot(fileName, screenIviId); - LOG_HL_RENDERER_API2(status, fileName, screenIviId); - return status; - } - - status_t RamsesRenderer::logRendererInfo() - { - const status_t status = m_impl.logRendererInfo(); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - status_t RamsesRenderer::setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender) - { - const status_t status = m_impl.setFrameTimerLimits(limitForSceneResourcesUpload, limitForClientResourcesUpload, limitForOffscreenBufferRender); - LOG_HL_RENDERER_API3(status, limitForSceneResourcesUpload, limitForClientResourcesUpload, limitForOffscreenBufferRender); - return status; - } - - status_t RamsesRenderer::setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit) - { - const status_t status = m_impl.setPendingFlushLimits(forceApplyFlushLimit, forceUnsubscribeSceneLimit); - LOG_HL_RENDERER_API2(status, forceApplyFlushLimit, forceUnsubscribeSceneLimit); - return status; - } - - displayBufferId_t RamsesRenderer::createInterruptibleOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, EDepthBufferType depthBufferType) - { - const auto bufferId = m_impl.createOffscreenBuffer(display, width, height, 0u, true, depthBufferType); - LOG_HL_RENDERER_API4(bufferId, display, width, height, depthBufferType); - return bufferId; - } - - displayBufferId_t RamsesRenderer::createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t usageFlags, uint64_t modifier) - { - ramses_internal::DmaBufferFourccFormat bufferFormat { bufferFourccFormat }; - ramses_internal::DmaBufferUsageFlags bufferUsage { usageFlags }; - ramses_internal::DmaBufferModifiers bufferModifer { modifier }; - const auto bufferId = m_impl.createDmaOffscreenBuffer(display, width, height, bufferFormat, bufferUsage, bufferModifer); - LOG_HL_RENDERER_API6(bufferId, display, width, height, bufferFourccFormat, usageFlags, modifier); - return bufferId; - } - - status_t RamsesRenderer::getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const - { - return m_impl.getDmaOffscreenBufferFDAndStride(display, displayBufferId, fd, stride); - } - - status_t RamsesRenderer::setSkippingOfUnmodifiedBuffers(bool enable) - { - const status_t status = m_impl.setSkippingOfUnmodifiedBuffers(enable); - LOG_HL_RENDERER_API1(status, enable); - return status; - } -} diff --git a/renderer/ramses-renderer-impl/src/RamsesRendererImpl.cpp b/renderer/ramses-renderer-impl/src/RamsesRendererImpl.cpp deleted file mode 100644 index 086ff01b5..000000000 --- a/renderer/ramses-renderer-impl/src/RamsesRendererImpl.cpp +++ /dev/null @@ -1,664 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RamsesRendererImpl.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "RamsesFrameworkImpl.h" -#include "RendererConfigImpl.h" -#include "DisplayConfigImpl.h" -#include "RendererSceneControlImpl.h" -#include "RamsesFrameworkTypesImpl.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/Handles.h" -#include "Utils/LogMacros.h" -#include "PlatformFactory/PlatformFactory.h" -#include "RendererAPI/ISystemCompositorController.h" -#include "RendererLib/RendererCommands.h" -#include "BinaryShaderCacheProxy.h" -#include "RendererResourceCacheProxy.h" -#include "RamsesRendererUtils.h" -#include "RendererFactory.h" -#include "FrameworkFactoryRegistry.h" -#include "Ramsh/Ramsh.h" -#include - -namespace ramses -{ - static const bool rendererRegisterSuccess = RendererFactory::RegisterRendererFactory(); - - RamsesRendererImpl::RamsesRendererImpl(RamsesFrameworkImpl& framework, const RendererConfig& config) - : m_framework(framework) - , m_binaryShaderCache(config.m_impl.get().getBinaryShaderCache() ? new BinaryShaderCacheProxy(*(config.m_impl.get().getBinaryShaderCache())) : nullptr) - , m_rendererResourceCache(config.m_impl.get().getRendererResourceCache() ? new RendererResourceCacheProxy(*(config.m_impl.get().getRendererResourceCache())) : nullptr) - , m_pendingRendererCommands() - , m_rendererFrameworkLogic(framework.getScenegraphComponent(), m_rendererCommandBuffer, framework.getFrameworkLock()) - , m_threadWatchdog(framework.getThreadWatchdogConfig(), ERamsesThreadIdentifier::Renderer) - , m_displayDispatcher{ std::make_unique(std::make_unique(), config.m_impl.get().getInternalRendererConfig(), m_rendererFrameworkLogic, m_threadWatchdog) } - , m_systemCompositorEnabled(config.m_impl.get().getInternalRendererConfig().getSystemCompositorControlEnabled()) - , m_loopMode(ramses_internal::ELoopMode::UpdateAndRender) - , m_rendererLoopThreadType(ERendererLoopThreadType_Undefined) - , m_periodicLogSupplier(framework.getPeriodicLogger(), m_rendererCommandBuffer) - { - assert(!framework.isConnected()); - - { //Add ramsh commands to ramsh, independent of whether it is enabled or not. - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); - for (const auto& cmd : m_ramshCommands) - m_framework.getRamsh().add(cmd); - LOG_DEBUG(ramses_internal::CONTEXT_SMOKETEST, "Ramsh commands registered from RamsesRenderer"); - } - - LOG_TRACE(ramses_internal::CONTEXT_PROFILING, "RamsesRenderer::RamsesRenderer finished initializing renderer"); - } - - RamsesRendererImpl::~RamsesRendererImpl() = default; - - status_t RamsesRendererImpl::doOneLoop() - { - if (ERendererLoopThreadType_InRendererOwnThread == m_rendererLoopThreadType) - { - return addErrorEntry("Can not call doOneLoop explicitly if renderer is (or was) running in its own thread!"); - } - - m_rendererLoopThreadType = ERendererLoopThreadType_UsingDoOneLoop; - m_displayDispatcher->dispatchCommands(m_rendererCommandBuffer); - m_displayDispatcher->doOneLoop(); - return StatusOK; - } - - status_t RamsesRendererImpl::flush() - { - pushAndConsumeRendererCommands(m_pendingRendererCommands); - return StatusOK; - } - - displayId_t RamsesRendererImpl::createDisplay(const DisplayConfig& config) - { - if (config.validate() != StatusOK) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "RamsesRenderer::createDisplay: failed to create display, using invalid display configuration - use validate method on object!"); - return displayId_t::Invalid(); - } - - const displayId_t displayId = m_nextDisplayId; - m_nextDisplayId.getReference() = m_nextDisplayId.getValue() + 1; - // display's framebuffer is also counted as display buffer together with offscreen buffers - assert(m_displayFramebuffers.count(displayId) == 0); - m_displayFramebuffers.insert({ displayId, m_nextDisplayBufferId }); - m_nextDisplayBufferId.getReference() = m_nextDisplayBufferId.getValue() + 1; - - ramses_internal::RendererCommand::CreateDisplay cmd{ ramses_internal::DisplayHandle(displayId.getValue()), config.m_impl.get().getInternalDisplayConfig(), m_binaryShaderCache.get() }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return displayId; - } - - status_t RamsesRendererImpl::destroyDisplay(displayId_t displayId) - { - ramses_internal::RendererCommand::DestroyDisplay cmd{ ramses_internal::DisplayHandle(displayId.getValue()) }; - m_pendingRendererCommands.push_back(std::move(cmd)); - m_displayFramebuffers.erase(displayId); - - return StatusOK; - } - - displayBufferId_t RamsesRendererImpl::getDisplayFramebuffer(displayId_t displayId) const - { - const auto it = m_displayFramebuffers.find(displayId); - if (it == m_displayFramebuffers.cend()) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "RamsesRenderer::getDisplayFramebuffer: there is no display with ID " << displayId); - return displayBufferId_t::Invalid(); - } - return it->second; - } - - const ramses_internal::DisplayDispatcher& RamsesRendererImpl::getDisplayDispatcher() const - { - return *m_displayDispatcher; - } - - ramses_internal::DisplayDispatcher& RamsesRendererImpl::getDisplayDispatcher() - { - return *m_displayDispatcher; - } - - RendererSceneControl* RamsesRendererImpl::getSceneControlAPI() - { - if (!m_sceneControlAPI) - { - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "RamsesRenderer: instantiating RendererSceneControl"); - auto m_impl = std::make_unique(*this); - m_sceneControlAPI = UniquePtrWithDeleter{ new RendererSceneControl{ std::move(m_impl) }, [](RendererSceneControl* api) { delete api; } }; - } - - return m_sceneControlAPI.get(); - } - - void RamsesRendererImpl::logConfirmationEcho(displayId_t display, const std::string& text) - { - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::ConfirmationEcho{ ramses_internal::DisplayHandle{ display.getValue() }, text }); - } - - const ramses_internal::RendererCommands& RamsesRendererImpl::getPendingCommands() const - { - return m_pendingRendererCommands; - } - - displayBufferId_t RamsesRendererImpl::createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount, bool interruptible, EDepthBufferType depthBufferType) - { - if (width < 1u || width > 4096u || - height < 1u || height > 4096u) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "RamsesRenderer::createOffscreenBuffer: failed to create offscreen buffer, resolution must be higher than 0x0 and lower than 4096x4096!"); - return {}; - } - - const ramses_internal::DisplayHandle displayHandle(display.getValue()); - const displayBufferId_t bufferId = m_nextDisplayBufferId; - const ramses_internal::OffscreenBufferHandle bufferHandle(bufferId.getValue()); - m_nextDisplayBufferId.getReference() = m_nextDisplayBufferId.getValue() + 1; - - ramses_internal::ERenderBufferType depthBufferTypeInternal = ramses_internal::ERenderBufferType_NUMBER_OF_ELEMENTS; - switch (depthBufferType) - { - case EDepthBufferType::None: - depthBufferTypeInternal = ramses_internal::ERenderBufferType_InvalidBuffer; - break; - case EDepthBufferType::Depth: - depthBufferTypeInternal = ramses_internal::ERenderBufferType_DepthBuffer; - break; - case EDepthBufferType::DepthStencil: - depthBufferTypeInternal = ramses_internal::ERenderBufferType_DepthStencilBuffer; - break; - } - - ramses_internal::RendererCommand::CreateOffscreenBuffer cmd{ displayHandle, bufferHandle, width, height, sampleCount, interruptible, depthBufferTypeInternal }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return bufferId; - } - - displayBufferId_t RamsesRendererImpl::createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, ramses_internal::DmaBufferFourccFormat dmaBufferFourccFormat, ramses_internal::DmaBufferUsageFlags dmaBufferUsageFlags, ramses_internal::DmaBufferModifiers dmaBufferModifiers) - { - if(isThreaded()) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "RamsesRenderer::createDmaOffscreenBuffer: failed to create offscreen buffer, renderer must be used only with doOneLoop (not running the renderer thread)!"); - return {}; - } - - if (width < 1u || width > 4096u || - height < 1u || height > 4096u) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "RamsesRenderer::createDmaOffscreenBuffer: failed to create offscreen buffer, resolution must be higher than 0x0 and lower than 4096x4096!"); - return {}; - } - - const ramses_internal::DisplayHandle displayHandle(display.getValue()); - const displayBufferId_t bufferId = m_nextDisplayBufferId; - const ramses_internal::OffscreenBufferHandle bufferHandle(bufferId.getValue()); - m_nextDisplayBufferId.getReference() = m_nextDisplayBufferId.getValue() + 1; - - ramses_internal::RendererCommand::CreateDmaOffscreenBuffer cmd{ displayHandle, bufferHandle, width, height, dmaBufferFourccFormat, dmaBufferUsageFlags, dmaBufferModifiers }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return bufferId; - } - - status_t RamsesRendererImpl::destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer) - { - const ramses_internal::DisplayHandle displayHandle(display.getValue()); - const ramses_internal::OffscreenBufferHandle bufferHandle(offscreenBuffer.getValue()); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::DestroyOffscreenBuffer{ displayHandle, bufferHandle }); - - return StatusOK; - } - - status_t RamsesRendererImpl::setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, uint32_t clearFlags) - { - static_assert( - static_cast(ramses::EClearFlags_None) == static_cast(ramses_internal::EClearFlags_None) && - static_cast(ramses::EClearFlags_Color) == static_cast(ramses_internal::EClearFlags_Color) && - static_cast(ramses::EClearFlags_Depth) == static_cast(ramses_internal::EClearFlags_Depth) && - static_cast(ramses::EClearFlags_Stencil) == static_cast(ramses_internal::EClearFlags_Stencil) && - static_cast(ramses::EClearFlags_All) == static_cast(ramses_internal::EClearFlags_All), "Type conversion mismatch"); - - const auto it = m_displayFramebuffers.find(display); - if (it == m_displayFramebuffers.cend()) - return addErrorEntry("RendererSceneControl::setDisplayBufferClearFlags failed: display does not exist."); - - ramses_internal::OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; - // if buffer to clear is display's framebuffer pass invalid OB to internal renderer - if (displayBuffer == it->second) - bufferHandle = ramses_internal::OffscreenBufferHandle::Invalid(); - - const ramses_internal::DisplayHandle displayHandle{ display.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetClearFlags{ displayHandle, bufferHandle, clearFlags }); - - return StatusOK; - } - - status_t RamsesRendererImpl::setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color) - { - const auto it = m_displayFramebuffers.find(display); - if (it == m_displayFramebuffers.cend()) - return addErrorEntry("RendererSceneControl::setDisplayBufferClearColor failed: display does not exist."); - - ramses_internal::OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; - // if buffer to clear is display's framebuffer pass invalid OB to internal renderer - if (displayBuffer == it->second) - bufferHandle = ramses_internal::OffscreenBufferHandle::Invalid(); - - const ramses_internal::DisplayHandle displayHandle{ display.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetClearColor{ displayHandle, bufferHandle, color }); - - return StatusOK; - } - - status_t RamsesRendererImpl::getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const - { - const auto it = std::find_if(m_offscreenDmaBufferInfos.cbegin(), m_offscreenDmaBufferInfos.cend(), [&](const auto& dmaBufInfo){ return dmaBufInfo.display == display && dmaBufInfo.displayBuffer == displayBufferId;}); - - if(it == m_offscreenDmaBufferInfos.cend()) - return addErrorEntry(::fmt::format("RamsesRenderer::getDmaOffscreenBufferFDAndStride: no DMA buffer created for buffer {} on display {}", displayBufferId, display)); - - fd = it->fd; - stride = it->stride; - - return StatusOK; - } - - ramses::streamBufferId_t RamsesRendererImpl::allocateStreamBuffer() - { - const streamBufferId_t bufferId = m_nextStreamBufferId; - m_nextStreamBufferId.getReference() = m_nextStreamBufferId.getValue() + 1; - return bufferId; - } - - streamBufferId_t RamsesRendererImpl::createStreamBuffer(displayId_t display, waylandIviSurfaceId_t source) - { - ramses::streamBufferId_t bufferId = allocateStreamBuffer(); - const ramses_internal::DisplayHandle displayHandle{ display.getValue() }; - const ramses_internal::StreamBufferHandle bufferHandle{ bufferId.getValue() }; - const ramses_internal::WaylandIviSurfaceId sourceLL{ source.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::CreateStreamBuffer{ displayHandle, bufferHandle, sourceLL }); - - return bufferId; - } - - status_t RamsesRendererImpl::destroyStreamBuffer(displayId_t display, streamBufferId_t streamBuffer) - { - const ramses_internal::DisplayHandle displayHandle{ display.getValue() }; - const ramses_internal::StreamBufferHandle bufferHandle{ streamBuffer.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::DestroyStreamBuffer{ displayHandle, bufferHandle }); - - return StatusOK; - } - - externalBufferId_t RamsesRendererImpl::createExternalBuffer(displayId_t display) - { - if (isThreaded()) - { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "RamsesRenderer::createExternalBuffer: can not create external buffers unless renderer is using doOneLoop"); - return {}; - } - - ramses::externalBufferId_t bufferId{ m_nextExternalBufferId }; - m_nextExternalBufferId.getReference() = m_nextExternalBufferId.getValue() + 1; - - const ramses_internal::DisplayHandle displayHandle{ display.getValue() }; - const ramses_internal::ExternalBufferHandle bufferHandle{ bufferId.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::CreateExternalBuffer{ displayHandle, bufferHandle }); - - return bufferId; - } - - status_t RamsesRendererImpl::destroyExternalBuffer(displayId_t display, externalBufferId_t externalTexture) - { - const ramses_internal::DisplayHandle displayHandle{ display.getValue() }; - const ramses_internal::ExternalBufferHandle bufferHandle{ externalTexture.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::DestroyExternalBuffer{ displayHandle, bufferHandle }); - - return StatusOK; - } - - status_t RamsesRendererImpl::readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height) - { - if (width == 0u || height == 0u) - return addErrorEntry("RamsesRenderer::readPixels failed: width and height must be greater than Zero"); - - const auto it = m_displayFramebuffers.find(displayId); - if (it == m_displayFramebuffers.cend()) - return addErrorEntry("RamsesRenderer::readPixels failed: display does not exist."); - - ramses_internal::OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; - // if buffer to read from is display's framebuffer pass invalid OB to internal renderer - if (displayBuffer == it->second) - bufferHandle = ramses_internal::OffscreenBufferHandle::Invalid(); - - ramses_internal::RendererCommand::ReadPixels cmd{ ramses_internal::DisplayHandle{ displayId.getValue() }, bufferHandle, x, y, width, height, false, false, {} }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RamsesRendererImpl::systemCompositorSetIviSurfaceVisibility(uint32_t surfaceId, bool visibility) - { - if (!m_systemCompositorEnabled) - return addErrorEntry("RamsesRenderer::setSurfaceVisibility failed: system compositor was not enabled when creating the renderer."); - - const ramses_internal::WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SCSetIviSurfaceVisibility{ waylandIviSurfaceId, visibility }); - return StatusOK; - } - - status_t RamsesRendererImpl::systemCompositorSetIviSurfaceOpacity(uint32_t surfaceId, float opacity) - { - if (!m_systemCompositorEnabled) - return addErrorEntry("RamsesRenderer::setSurfaceOpacity failed: system compositor was not enabled when creating the renderer."); - - const ramses_internal::WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SCSetIviSurfaceOpacity{ waylandIviSurfaceId, opacity }); - return StatusOK; - } - - status_t RamsesRendererImpl::systemCompositorSetIviSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height) - { - if (!m_systemCompositorEnabled) - return addErrorEntry("RamsesRenderer::setSurfaceRectangle failed: system compositor was not enabled when creating the renderer."); - - const ramses_internal::WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SCSetIviSurfaceDestRectangle{ waylandIviSurfaceId, x, y, width, height }); - return StatusOK; - } - - status_t RamsesRendererImpl::systemCompositorSetIviLayerVisibility(uint32_t layerId, bool visibility) - { - if (!m_systemCompositorEnabled) - return addErrorEntry("RamsesRenderer::setLayerVisibility failed: system compositor was not enabled when creating the renderer."); - - const ramses_internal::WaylandIviLayerId waylandIviLayerId(layerId); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SCSetIviLayerVisibility{ waylandIviLayerId, visibility }); - return StatusOK; - } - - status_t RamsesRendererImpl::systemCompositorTakeScreenshot(std::string_view fileName, int32_t screenIviId) - { - if (!m_systemCompositorEnabled) - return addErrorEntry("RamsesRenderer::takeSystemCompositorScreenshot failed: system compositor was not enabled when creating the renderer."); - - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SCScreenshot{ screenIviId, std::string{fileName} }); - return StatusOK; - } - - status_t RamsesRendererImpl::systemCompositorAddIviSurfaceToIviLayer(uint32_t surfaceId, uint32_t layerId) - { - if (!m_systemCompositorEnabled) - return addErrorEntry("RamsesRenderer::addSurfaceToLayer failed: system compositor was not enabled when creating the renderer."); - - const ramses_internal::WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); - const ramses_internal::WaylandIviLayerId waylandIviLayerId(layerId); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SCAddIviSurfaceToIviLayer{ waylandIviSurfaceId, waylandIviLayerId }); - return StatusOK; - } - - status_t RamsesRendererImpl::dispatchEvents(IRendererEventHandler& rendererEventHandler) - { - m_tempRendererEvents.clear(); - m_displayDispatcher->dispatchRendererEvents(m_tempRendererEvents); - - for(const auto& event : m_tempRendererEvents) - { - switch (event.eventType) - { - case ramses_internal::ERendererEventType::DisplayCreated: - rendererEventHandler.displayCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Ok); - break; - case ramses_internal::ERendererEventType::DisplayCreateFailed: - rendererEventHandler.displayCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Failed); - break; - case ramses_internal::ERendererEventType::DisplayDestroyed: - rendererEventHandler.displayDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Ok); - break; - case ramses_internal::ERendererEventType::DisplayDestroyFailed: - rendererEventHandler.displayDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Failed); - break; - case ramses_internal::ERendererEventType::ReadPixelsFromFramebuffer: - case ramses_internal::ERendererEventType::ReadPixelsFromFramebufferFailed: - { - const ramses_internal::UInt8Vector& pixelData = event.pixelData; - const displayId_t displayId{ event.displayHandle.asMemoryHandle() }; - const ramses_internal::OffscreenBufferHandle obHandle = event.offscreenBuffer; - const displayBufferId_t displayBuffer(obHandle.isValid() ? obHandle.asMemoryHandle() : getDisplayFramebuffer(displayId).getValue()); - const auto eventResult = (event.eventType == ramses_internal::ERendererEventType::ReadPixelsFromFramebuffer ? ERendererEventResult::Ok : ERendererEventResult::Failed); - assert((event.eventType == ramses_internal::ERendererEventType::ReadPixelsFromFramebuffer) ^ pixelData.empty()); - rendererEventHandler.framebufferPixelsRead(pixelData.data(), static_cast(pixelData.size()), displayId, displayBuffer, eventResult); - break; - } - case ramses_internal::ERendererEventType::OffscreenBufferCreated: - { - const displayId_t display{ event.displayHandle.asMemoryHandle() }; - const displayBufferId_t displayBuffer{ event.offscreenBuffer.asMemoryHandle() }; - assert(std::find_if(m_offscreenDmaBufferInfos.cbegin(), m_offscreenDmaBufferInfos.cend(), [&](const auto& dmaBufInfo){ return dmaBufInfo.display == display && dmaBufInfo.displayBuffer == displayBuffer;}) == m_offscreenDmaBufferInfos.cend()); - m_offscreenDmaBufferInfos.push_back({ display, displayBuffer, event.dmaBufferFD, event.dmaBufferStride }); - - rendererEventHandler.offscreenBufferCreated(display, displayBuffer, ERendererEventResult::Ok); - break; - } - case ramses_internal::ERendererEventType::OffscreenBufferCreateFailed: - rendererEventHandler.offscreenBufferCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, displayBufferId_t{ event.offscreenBuffer.asMemoryHandle() }, ERendererEventResult::Failed); - break; - case ramses_internal::ERendererEventType::OffscreenBufferDestroyed: - { - const displayId_t display{ event.displayHandle.asMemoryHandle() }; - const displayBufferId_t displayBuffer{ event.offscreenBuffer.asMemoryHandle() }; - rendererEventHandler.offscreenBufferDestroyed(display, displayBuffer, ERendererEventResult::Ok); - - const auto it = std::find_if(m_offscreenDmaBufferInfos.cbegin(), m_offscreenDmaBufferInfos.cend(), [&](const auto& dmaBufInfo){ return dmaBufInfo.display == display && dmaBufInfo.displayBuffer == displayBuffer;}); - if(it != m_offscreenDmaBufferInfos.cend()) - m_offscreenDmaBufferInfos.erase(it); - break; - } - case ramses_internal::ERendererEventType::OffscreenBufferDestroyFailed: - rendererEventHandler.offscreenBufferDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, displayBufferId_t{ event.offscreenBuffer.asMemoryHandle() }, ERendererEventResult::Failed); - break; - case ramses_internal::ERendererEventType::ExternalBufferCreated: - rendererEventHandler.externalBufferCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, event.textureGlId, ERendererEventResult::Ok); - break; - case ramses_internal::ERendererEventType::ExternalBufferCreateFailed: - rendererEventHandler.externalBufferCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, 0u, ERendererEventResult::Failed); - break; - case ramses_internal::ERendererEventType::ExternalBufferDestroyed: - rendererEventHandler.externalBufferDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, ERendererEventResult::Ok); - break; - case ramses_internal::ERendererEventType::ExternalBufferDestroyFailed: - rendererEventHandler.externalBufferDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, ERendererEventResult::Failed); - break; - case ramses_internal::ERendererEventType::WindowClosed: - rendererEventHandler.windowClosed(displayId_t{ event.displayHandle.asMemoryHandle() }); - break; - case ramses_internal::ERendererEventType::WindowKeyEvent: - rendererEventHandler.keyEvent(displayId_t{ event.displayHandle.asMemoryHandle() }, - RamsesRendererUtils::GetKeyEvent(event.keyEvent.type), - event.keyEvent.modifier, RamsesRendererUtils::GetKeyCode(event.keyEvent.keyCode)); - break; - case ramses_internal::ERendererEventType::WindowMouseEvent: - rendererEventHandler.mouseEvent(displayId_t{ event.displayHandle.asMemoryHandle() }, - RamsesRendererUtils::GetMouseEvent(event.mouseEvent.type), - event.mouseEvent.pos.x, event.mouseEvent.pos.y); - break; - case ramses_internal::ERendererEventType::WindowResizeEvent: - rendererEventHandler.windowResized(displayId_t{ event.displayHandle.asMemoryHandle() }, event.resizeEvent.width, event.resizeEvent.height); - break; - case ramses_internal::ERendererEventType::WindowMoveEvent: - rendererEventHandler.windowMoved(displayId_t{ event.displayHandle.asMemoryHandle() }, event.moveEvent.posX, event.moveEvent.posY); - break; - case ramses_internal::ERendererEventType::FrameTimingReport: - rendererEventHandler.renderThreadLoopTimings(displayId_t{ event.displayHandle.asMemoryHandle() }, event.frameTimings.maximumLoopTimeWithinPeriod, event.frameTimings.averageLoopTimeWithinPeriod); - break; - default: - assert(false); - return addErrorEntry("RamsesRenderer::dispatchEvents failed - unknown renderer event type!"); - } - } - - return StatusOK; - } - - status_t RamsesRendererImpl::logRendererInfo() - { - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::LogInfo{ ramses_internal::ERendererLogTopic::All, true, {} }); - return StatusOK; - } - - status_t RamsesRendererImpl::startThread() - { - if (m_rendererLoopThreadType == ERendererLoopThreadType_UsingDoOneLoop) - return addErrorEntry("RamsesRenderer::startThread Can not call startThread if doOneLoop is called before!"); - - m_displayDispatcher->startDisplayThreadsUpdating(); - m_diplayThreadUpdating = true; - - // First time starting thread, create dispatching thread. - // Dispatching thread must be created after dispatcher startDisplayThreadsUpdating above which enables display threaded mode - // and any existing queued up commands will be processed in threaded mode. - if (m_rendererLoopThreadType == ERendererLoopThreadType_Undefined) - m_commandDispatchingThread = std::make_unique(*m_displayDispatcher, m_rendererCommandBuffer, m_threadWatchdog); - m_rendererLoopThreadType = ERendererLoopThreadType_InRendererOwnThread; - - return StatusOK; - } - - status_t RamsesRendererImpl::stopThread() - { - if (ERendererLoopThreadType_InRendererOwnThread != m_rendererLoopThreadType) - return addErrorEntry("RamsesRenderer::stopThread Can not call stopThread if startThread was not called before!"); - - m_displayDispatcher->stopDisplayThreadsUpdating(); - m_diplayThreadUpdating = false; - - return StatusOK; - } - - bool RamsesRendererImpl::isThreadRunning() const - { - return m_diplayThreadUpdating; - } - - bool RamsesRendererImpl::isThreaded() const - { - return m_rendererLoopThreadType == ERendererLoopThreadType_InRendererOwnThread; - } - - status_t RamsesRendererImpl::setFramerateLimit(displayId_t displayId, float fpsLimit) - { - if (fpsLimit <= 0.0f) - return addErrorEntry("RamsesRenderer::setFramerateLimit must specify a positive fpsLimit!"); - - if (ERendererLoopThreadType_UsingDoOneLoop == m_rendererLoopThreadType) - return addErrorEntry("RamsesRenderer::setFramerateLimit cannot call setFramerateLimit if doOneLoop is called before because it can only control framerate when using rendering thread!"); - - m_displayDispatcher->setMinFrameDuration(std::chrono::microseconds{ std::lround(1000000 / fpsLimit) }, ramses_internal::DisplayHandle{ displayId.getValue() }); - - return StatusOK; - } - - float RamsesRendererImpl::getFramerateLimit(displayId_t displayId) const - { - const auto minFrameDuration = m_displayDispatcher->getMinFrameDuration(ramses_internal::DisplayHandle{ displayId.getValue() }); - return static_cast(1000000.0 / minFrameDuration.count()); - } - - status_t RamsesRendererImpl::setLoopMode(ELoopMode loopMode) - { - switch (loopMode) - { - case ELoopMode::UpdateAndRender: - m_loopMode = ramses_internal::ELoopMode::UpdateAndRender; - break; - case ELoopMode::UpdateOnly: - m_loopMode = ramses_internal::ELoopMode::UpdateOnly; - break; - } - - m_displayDispatcher->setLoopMode(m_loopMode); - - return StatusOK; - } - - ELoopMode RamsesRendererImpl::getLoopMode() const - { - switch (m_loopMode) - { - case ramses_internal::ELoopMode::UpdateAndRender: - return ELoopMode::UpdateAndRender; - case ramses_internal::ELoopMode::UpdateOnly: - return ELoopMode::UpdateOnly; - } - - assert(false); - return ELoopMode::UpdateAndRender; - } - - ramses::status_t RamsesRendererImpl::setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender) - { - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetLimits_FrameBudgets{ limitForSceneResourcesUpload, limitForClientResourcesUpload, limitForOffscreenBufferRender }); - return StatusOK; - } - - status_t RamsesRendererImpl::setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height) - { - const auto it = m_displayFramebuffers.find(display); - if (it == m_displayFramebuffers.cend()) - return addErrorEntry("RamsesRenderer::setExternallyOwnedWindowSize failed: display does not exist."); - - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetExterallyOwnedWindowSize{ ramses_internal::DisplayHandle{ display.getValue() }, width, height }); - return StatusOK; - } - - status_t RamsesRendererImpl::setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit) - { - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetLimits_FlushesForceApply{ forceApplyFlushLimit }); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetLimits_FlushesForceUnsubscribe{ forceUnsubscribeSceneLimit }); - return StatusOK; - } - - status_t RamsesRendererImpl::setSkippingOfUnmodifiedBuffers(bool enable) - { - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::SetSkippingOfUnmodifiedBuffers{ enable }); - return StatusOK; - } - - void RamsesRendererImpl::pushAndConsumeRendererCommands(ramses_internal::RendererCommands& cmds) - { - m_rendererCommandBuffer.addAndConsumeCommandsFrom(cmds); - } - - const ramses::RamsesRendererImpl::DisplayFrameBufferMap& RamsesRendererImpl::getDisplayFrameBuffers() const - { - return m_displayFramebuffers; - } -} diff --git a/renderer/ramses-renderer-impl/src/RamsesRendererUtils.cpp b/renderer/ramses-renderer-impl/src/RamsesRendererUtils.cpp deleted file mode 100644 index 064ba1129..000000000 --- a/renderer/ramses-renderer-impl/src/RamsesRendererUtils.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2018 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RamsesRendererUtils.h" - -namespace ramses -{ - ramses::EMouseEvent RamsesRendererUtils::GetMouseEvent(ramses_internal::EMouseEventType type) - { - switch (type) - { - case ramses_internal::EMouseEventType_LeftButtonDown: - return ramses::EMouseEvent::LeftButtonDown; - case ramses_internal::EMouseEventType_LeftButtonUp: - return ramses::EMouseEvent::LeftButtonUp; - case ramses_internal::EMouseEventType_MiddleButtonDown: - return ramses::EMouseEvent::MiddleButtonDown; - case ramses_internal::EMouseEventType_MiddleButtonUp: - return ramses::EMouseEvent::MiddleButtonUp; - case ramses_internal::EMouseEventType_RightButtonDown: - return ramses::EMouseEvent::RightButtonDown; - case ramses_internal::EMouseEventType_RightButtonUp: - return ramses::EMouseEvent::RightButtonUp; - case ramses_internal::EMouseEventType_WheelDown: - return ramses::EMouseEvent::WheelDown; - case ramses_internal::EMouseEventType_WheelUp: - return ramses::EMouseEvent::WheelUp; - case ramses_internal::EMouseEventType_Move: - return ramses::EMouseEvent::Move; - case ramses_internal::EMouseEventType_WindowEnter: - return ramses::EMouseEvent::WindowEnter; - case ramses_internal::EMouseEventType_WindowLeave: - return ramses::EMouseEvent::WindowLeave; - case ramses_internal::EMouseEventType_Invalid: - case ramses_internal::EMouseEventType_NUMBER_OF_ELEMENTS: - break; - } - assert(!"Not a mouse button event type"); - return ramses::EMouseEvent::Invalid; - } - - ramses::EKeyEvent RamsesRendererUtils::GetKeyEvent(ramses_internal::EKeyEventType type) - { - switch (type) - { - case ramses_internal::EKeyEventType_Pressed: - return ramses::EKeyEvent::Pressed; - case ramses_internal::EKeyEventType_Released: - return ramses::EKeyEvent::Released; - case ramses_internal::EKeyEventType_Invalid: - case ramses_internal::EKeyEventType_NUMBER_OF_ELEMENTS: - break; - } - assert(!"Not a key event type"); - return ramses::EKeyEvent::Invalid; - } - - ramses::EKeyCode RamsesRendererUtils::GetKeyCode(ramses_internal::EKeyCode keyCode) - { - switch (keyCode) - { - case ramses_internal::EKeyCode_Unknown: - return ramses::EKeyCode_Unknown; - case ramses_internal::EKeyCode_A: - return ramses::EKeyCode_A; - case ramses_internal::EKeyCode_B: - return ramses::EKeyCode_B; - case ramses_internal::EKeyCode_C: - return ramses::EKeyCode_C; - case ramses_internal::EKeyCode_D: - return ramses::EKeyCode_D; - case ramses_internal::EKeyCode_E: - return ramses::EKeyCode_E; - case ramses_internal::EKeyCode_F: - return ramses::EKeyCode_F; - case ramses_internal::EKeyCode_G: - return ramses::EKeyCode_G; - case ramses_internal::EKeyCode_H: - return ramses::EKeyCode_H; - case ramses_internal::EKeyCode_I: - return ramses::EKeyCode_I; - case ramses_internal::EKeyCode_J: - return ramses::EKeyCode_J; - case ramses_internal::EKeyCode_K: - return ramses::EKeyCode_K; - case ramses_internal::EKeyCode_L: - return ramses::EKeyCode_L; - case ramses_internal::EKeyCode_M: - return ramses::EKeyCode_M; - case ramses_internal::EKeyCode_N: - return ramses::EKeyCode_N; - case ramses_internal::EKeyCode_O: - return ramses::EKeyCode_O; - case ramses_internal::EKeyCode_P: - return ramses::EKeyCode_P; - case ramses_internal::EKeyCode_Q: - return ramses::EKeyCode_Q; - case ramses_internal::EKeyCode_R: - return ramses::EKeyCode_R; - case ramses_internal::EKeyCode_S: - return ramses::EKeyCode_S; - case ramses_internal::EKeyCode_T: - return ramses::EKeyCode_T; - case ramses_internal::EKeyCode_U: - return ramses::EKeyCode_U; - case ramses_internal::EKeyCode_V: - return ramses::EKeyCode_V; - case ramses_internal::EKeyCode_W: - return ramses::EKeyCode_W; - case ramses_internal::EKeyCode_X: - return ramses::EKeyCode_X; - case ramses_internal::EKeyCode_Y: - return ramses::EKeyCode_Y; - case ramses_internal::EKeyCode_Z: - return ramses::EKeyCode_Z; - - case ramses_internal::EKeyCode_0: - return ramses::EKeyCode_0; - case ramses_internal::EKeyCode_1: - return ramses::EKeyCode_1; - case ramses_internal::EKeyCode_2: - return ramses::EKeyCode_2; - case ramses_internal::EKeyCode_3: - return ramses::EKeyCode_3; - case ramses_internal::EKeyCode_4: - return ramses::EKeyCode_4; - case ramses_internal::EKeyCode_5: - return ramses::EKeyCode_5; - case ramses_internal::EKeyCode_6: - return ramses::EKeyCode_6; - case ramses_internal::EKeyCode_7: - return ramses::EKeyCode_7; - case ramses_internal::EKeyCode_8: - return ramses::EKeyCode_8; - case ramses_internal::EKeyCode_9: - return ramses::EKeyCode_9; - - case ramses_internal::EKeyCode_NumLock: - return ramses::EKeyCode_NumLock; - case ramses_internal::EKeyCode_Numpad_Add: - return ramses::EKeyCode_Numpad_Add; - case ramses_internal::EKeyCode_Numpad_Subtract: - return ramses::EKeyCode_Numpad_Subtract; - case ramses_internal::EKeyCode_Numpad_Multiply: - return ramses::EKeyCode_Numpad_Multiply; - case ramses_internal::EKeyCode_Numpad_Divide: - return ramses::EKeyCode_Numpad_Divide; - case ramses_internal::EKeyCode_Numpad_Enter: - return ramses::EKeyCode_Numpad_Enter; - case ramses_internal::EKeyCode_Numpad_Decimal: - return ramses::EKeyCode_Numpad_Decimal; - case ramses_internal::EKeyCode_Numpad_0: - return ramses::EKeyCode_Numpad_0; - case ramses_internal::EKeyCode_Numpad_1: - return ramses::EKeyCode_Numpad_1; - case ramses_internal::EKeyCode_Numpad_2: - return ramses::EKeyCode_Numpad_2; - case ramses_internal::EKeyCode_Numpad_3: - return ramses::EKeyCode_Numpad_3; - case ramses_internal::EKeyCode_Numpad_4: - return ramses::EKeyCode_Numpad_4; - case ramses_internal::EKeyCode_Numpad_5: - return ramses::EKeyCode_Numpad_5; - case ramses_internal::EKeyCode_Numpad_6: - return ramses::EKeyCode_Numpad_6; - case ramses_internal::EKeyCode_Numpad_7: - return ramses::EKeyCode_Numpad_7; - case ramses_internal::EKeyCode_Numpad_8: - return ramses::EKeyCode_Numpad_8; - case ramses_internal::EKeyCode_Numpad_9: - return ramses::EKeyCode_Numpad_9; - - case ramses_internal::EKeyCode_Return: - return ramses::EKeyCode_Return; - case ramses_internal::EKeyCode_Escape: - return ramses::EKeyCode_Escape; - case ramses_internal::EKeyCode_Backspace: - return ramses::EKeyCode_Backspace; - case ramses_internal::EKeyCode_Tab: - return ramses::EKeyCode_Tab; - case ramses_internal::EKeyCode_Space: - return ramses::EKeyCode_Space; - case ramses_internal::EKeyCode_Menu: - return ramses::EKeyCode_Menu; - case ramses_internal::EKeyCode_CapsLock: - return ramses::EKeyCode_CapsLock; - case ramses_internal::EKeyCode_ShiftLeft: - return ramses::EKeyCode_ShiftLeft; - case ramses_internal::EKeyCode_ShiftRight: - return ramses::EKeyCode_ShiftRight; - case ramses_internal::EKeyCode_AltLeft: - return ramses::EKeyCode_AltLeft; - case ramses_internal::EKeyCode_AltRight: - return ramses::EKeyCode_AltRight; - case ramses_internal::EKeyCode_ControlLeft: - return ramses::EKeyCode_ControlLeft; - case ramses_internal::EKeyCode_ControlRight: - return ramses::EKeyCode_ControlRight; - case ramses_internal::EKeyCode_WindowsLeft: - return ramses::EKeyCode_WindowsLeft; - case ramses_internal::EKeyCode_WindowsRight: - return ramses::EKeyCode_WindowsRight; - - case ramses_internal::EKeyCode_F1: - return ramses::EKeyCode_F1; - case ramses_internal::EKeyCode_F2: - return ramses::EKeyCode_F2; - case ramses_internal::EKeyCode_F3: - return ramses::EKeyCode_F3; - case ramses_internal::EKeyCode_F4: - return ramses::EKeyCode_F4; - case ramses_internal::EKeyCode_F5: - return ramses::EKeyCode_F5; - case ramses_internal::EKeyCode_F6: - return ramses::EKeyCode_F6; - case ramses_internal::EKeyCode_F7: - return ramses::EKeyCode_F7; - case ramses_internal::EKeyCode_F8: - return ramses::EKeyCode_F8; - case ramses_internal::EKeyCode_F9: - return ramses::EKeyCode_F9; - case ramses_internal::EKeyCode_F10: - return ramses::EKeyCode_F10; - case ramses_internal::EKeyCode_F11: - return ramses::EKeyCode_F11; - case ramses_internal::EKeyCode_F12: - return ramses::EKeyCode_F12; - - case ramses_internal::EKeyCode_PrintScreen: - return ramses::EKeyCode_PrintScreen; - case ramses_internal::EKeyCode_ScrollLock: - return ramses::EKeyCode_ScrollLock; - case ramses_internal::EKeyCode_Pause: - return ramses::EKeyCode_Pause; - - case ramses_internal::EKeyCode_Insert: - return ramses::EKeyCode_Insert; - case ramses_internal::EKeyCode_Home: - return ramses::EKeyCode_Home; - case ramses_internal::EKeyCode_PageUp: - return ramses::EKeyCode_PageUp; - case ramses_internal::EKeyCode_Delete: - return ramses::EKeyCode_Delete; - case ramses_internal::EKeyCode_End: - return ramses::EKeyCode_End; - case ramses_internal::EKeyCode_PageDown: - return ramses::EKeyCode_PageDown; - - case ramses_internal::EKeyCode_Right: - return ramses::EKeyCode_Right; - case ramses_internal::EKeyCode_Left: - return ramses::EKeyCode_Left; - case ramses_internal::EKeyCode_Up: - return ramses::EKeyCode_Up; - case ramses_internal::EKeyCode_Down: - return ramses::EKeyCode_Down; - - case ramses_internal::EKeyCode_Minus: - return ramses::EKeyCode_Minus; - case ramses_internal::EKeyCode_Equals: - return ramses::EKeyCode_Equals; - case ramses_internal::EKeyCode_LeftBracket: - return ramses::EKeyCode_LeftBracket; - case ramses_internal::EKeyCode_RightBracket: - return ramses::EKeyCode_RightBracket; - case ramses_internal::EKeyCode_Backslash: - return ramses::EKeyCode_Backslash; - case ramses_internal::EKeyCode_Semicolon: - return ramses::EKeyCode_Semicolon; - case ramses_internal::EKeyCode_Comma: - return ramses::EKeyCode_Comma; - case ramses_internal::EKeyCode_Period: - return ramses::EKeyCode_Period; - case ramses_internal::EKeyCode_Slash: - return ramses::EKeyCode_Slash; - case ramses_internal::EKeyCode_Apostrophe: - return ramses::EKeyCode_Apostrophe; - case ramses_internal::EKeyCode_Grave: - return ramses::EKeyCode_Grave; - case ramses_internal::EKeyCode_NumberSign: - return ramses::EKeyCode_NumberSign; - - default: - assert(!"Not a valid key code "); - return ramses::EKeyCode_Unknown; - } - } -} diff --git a/renderer/ramses-renderer-impl/src/RendererConfig.cpp b/renderer/ramses-renderer-impl/src/RendererConfig.cpp deleted file mode 100644 index 1021e6b1b..000000000 --- a/renderer/ramses-renderer-impl/src/RendererConfig.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -// API -#include "ramses-renderer-api/RendererConfig.h" - -// internal -#include "RendererConfigImpl.h" -#include "APILoggingMacros.h" -#include "ramses-renderer-api/IRendererResourceCache.h" -#include - -namespace ramses -{ - RendererConfig::RendererConfig() - : StatusObject{ std::make_unique() } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RendererConfig::~RendererConfig() = default; - - RendererConfig::RendererConfig(const RendererConfig& other) - : StatusObject{ std::make_unique(other.m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RendererConfig::RendererConfig(RendererConfig&& other) noexcept - : StatusObject{ std::move(other.StatusObject::m_impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - RendererConfig& RendererConfig::operator=(const RendererConfig& other) - { - StatusObject::m_impl = std::make_unique(other.m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - RendererConfig& RendererConfig::operator=(RendererConfig&& other) noexcept - { - StatusObject::m_impl = std::move(other.StatusObject::m_impl); - m_impl = static_cast(*StatusObject::m_impl); - return *this; - } - - ramses::status_t RendererConfig::setBinaryShaderCache(IBinaryShaderCache& cache) - { - const status_t status = m_impl.get().setBinaryShaderCache(cache); - LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(cache)); - return status; - } - - status_t RendererConfig::setRendererResourceCache(IRendererResourceCache& cache) - { - const status_t status = m_impl.get().setRendererResourceCache(cache); - LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(cache)); - return status; - } - - status_t RendererConfig::enableSystemCompositorControl() - { - const status_t status = m_impl.get().enableSystemCompositorControl(); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - status_t RendererConfig::setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec) - { - const status_t status = m_impl.get().setFrameCallbackMaxPollTime(waitTimeInUsec); - LOG_HL_RENDERER_API1(status, waitTimeInUsec); - return status; - } - - status_t RendererConfig::setSystemCompositorWaylandDisplay(std::string_view waylandDisplay) - { - return m_impl.get().setSystemCompositorWaylandDisplay(waylandDisplay); - } - - std::string_view RendererConfig::getSystemCompositorWaylandDisplay() const - { - return m_impl.get().getSystemCompositorWaylandDisplay(); - } - - status_t RendererConfig::setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period) - { - const status_t status = m_impl.get().setRenderThreadLoopTimingReportingPeriod(period); - LOG_HL_RENDERER_API1(status, period.count()); - return status; - } - - std::chrono::milliseconds RendererConfig::getRenderThreadLoopTimingReportingPeriod() const - { - return m_impl.get().getRenderThreadLoopTimingReportingPeriod(); - } - -} diff --git a/renderer/ramses-renderer-impl/src/RendererResourceCacheProxy.cpp b/renderer/ramses-renderer-impl/src/RendererResourceCacheProxy.cpp deleted file mode 100644 index 592ccc8d1..000000000 --- a/renderer/ramses-renderer-impl/src/RendererResourceCacheProxy.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererResourceCacheProxy.h" -#include "ramses-renderer-api/IRendererResourceCache.h" - -namespace ramses -{ - RendererResourceCacheProxy::RendererResourceCacheProxy(ramses::IRendererResourceCache& cache) - : m_cache(cache) - { - - } - - RendererResourceCacheProxy::~RendererResourceCacheProxy() - { - - } - - bool RendererResourceCacheProxy::hasResource(ramses_internal::ResourceContentHash resourceId, uint32_t& size) const - { - return m_cache.hasResource(ConvertInternalTypeToPublic(resourceId), size); - } - - bool RendererResourceCacheProxy::getResourceData(ramses_internal::ResourceContentHash resourceId, uint8_t* buffer, uint32_t bufferSize) const - { - return m_cache.getResourceData(ConvertInternalTypeToPublic(resourceId), buffer, bufferSize); - } - - bool RendererResourceCacheProxy::shouldResourceBeCached(ramses_internal::ResourceContentHash resourceId, uint32_t resourceDataSize, ramses_internal::ResourceCacheFlag cacheFlag, ramses_internal::SceneId sceneId) const - { - return m_cache.shouldResourceBeCached(ConvertInternalTypeToPublic(resourceId), resourceDataSize, ConvertInternalTypeToPublic(cacheFlag), ConvertInternalTypeToPublic(sceneId)); - } - - void RendererResourceCacheProxy::storeResource(ramses_internal::ResourceContentHash resourceId, const uint8_t* resourceData, uint32_t resourceDataSize, ramses_internal::ResourceCacheFlag cacheFlag, ramses_internal::SceneId sceneId) - { - m_cache.storeResource(ConvertInternalTypeToPublic(resourceId), resourceData, resourceDataSize, ConvertInternalTypeToPublic(cacheFlag), ConvertInternalTypeToPublic(sceneId)); - } - - ramses::rendererResourceId_t RendererResourceCacheProxy::ConvertInternalTypeToPublic(ramses_internal::ResourceContentHash input) - { - ramses::rendererResourceId_t result; - result.lowPart = input.lowPart; - result.highPart = input.highPart; - - return result; - } - - ramses::resourceCacheFlag_t RendererResourceCacheProxy::ConvertInternalTypeToPublic(ramses_internal::ResourceCacheFlag input) - { - return ramses::resourceCacheFlag_t(input.getValue()); - } - - ramses::sceneId_t RendererResourceCacheProxy::ConvertInternalTypeToPublic(ramses_internal::SceneId input) - { - return ramses::sceneId_t(input.getValue()); - } -} diff --git a/renderer/ramses-renderer-impl/src/RendererSceneControl.cpp b/renderer/ramses-renderer-impl/src/RendererSceneControl.cpp deleted file mode 100644 index c838d33cf..000000000 --- a/renderer/ramses-renderer-impl/src/RendererSceneControl.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-renderer-api/RendererSceneControl.h" -#include "RendererSceneControlImpl.h" -#include "RamsesFrameworkTypesImpl.h" -#include "APILoggingMacros.h" - -namespace ramses -{ - RendererSceneControl::RendererSceneControl(std::unique_ptr impl) - : StatusObject{ std::move(impl) } - , m_impl{ static_cast(*StatusObject::m_impl) } - { - } - - status_t RendererSceneControl::setSceneState(sceneId_t sceneId, RendererSceneState state) - { - const status_t status = m_impl.setSceneState(sceneId, state); - LOG_HL_RENDERER_API2(status, sceneId, EnumToString(state)); - return status; - } - - status_t RendererSceneControl::setSceneMapping(sceneId_t sceneId, displayId_t displayId) - { - const status_t status = m_impl.setSceneMapping(sceneId, displayId); - LOG_HL_RENDERER_API2(status, sceneId, displayId); - return status; - } - - status_t RendererSceneControl::setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) - { - const status_t status = m_impl.setSceneDisplayBufferAssignment(sceneId, displayBuffer, sceneRenderOrder); - LOG_HL_RENDERER_API3(status, sceneId, displayBuffer, sceneRenderOrder); - return status; - } - - status_t RendererSceneControl::linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) - { - const status_t status = m_impl.linkOffscreenBuffer(offscreenBufferId, consumerSceneId, consumerDataSlotId); - LOG_HL_RENDERER_API3(status, offscreenBufferId, consumerSceneId, consumerDataSlotId); - return status; - } - - status_t RendererSceneControl::linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) - { - return m_impl.linkStreamBuffer(streamBufferId, consumerSceneId, consumerDataSlotId); - } - - status_t RendererSceneControl::linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) - { - const status_t status = m_impl.linkExternalBuffer(externalBufferId, consumerSceneId, consumerDataSlotId); - LOG_HL_RENDERER_API3(status, externalBufferId, consumerSceneId, consumerDataSlotId); - return status; - } - - status_t RendererSceneControl::linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) - { - const status_t status = m_impl.linkData(providerSceneId, providerId, consumerSceneId, consumerId); - LOG_HL_RENDERER_API4(status, providerSceneId, providerId, consumerSceneId, consumerId); - return status; - } - - status_t RendererSceneControl::unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) - { - const status_t status = m_impl.unlinkData(consumerSceneId, consumerId); - LOG_HL_RENDERER_API2(status, consumerSceneId, consumerId); - return status; - } - - status_t RendererSceneControl::handlePickEvent(sceneId_t sceneId, float bufferNormalizedCoordX, float bufferNormalizedCoordY) - { - const status_t status = m_impl.handlePickEvent(sceneId, bufferNormalizedCoordX, bufferNormalizedCoordY); - LOG_HL_RENDERER_API3(status, sceneId, bufferNormalizedCoordX, bufferNormalizedCoordY); - return status; - } - - status_t RendererSceneControl::flush() - { - const status_t status = m_impl.flush(); - LOG_HL_RENDERER_API_NOARG(status); - return status; - } - - status_t RendererSceneControl::dispatchEvents(IRendererSceneControlEventHandler& eventHandler) - { - const status_t status = m_impl.dispatchEvents(eventHandler); - LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(eventHandler)); - return status; - } -} diff --git a/renderer/ramses-renderer-impl/src/RendererSceneControlImpl.cpp b/renderer/ramses-renderer-impl/src/RendererSceneControlImpl.cpp deleted file mode 100644 index 88b0ad425..000000000 --- a/renderer/ramses-renderer-impl/src/RendererSceneControlImpl.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "RendererSceneControlImpl.h" -#include "RamsesRendererImpl.h" -#include "RamsesFrameworkTypesImpl.h" -#include "RendererAPI/Types.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "Utils/LogMacros.h" - -namespace ramses -{ - RendererSceneControlImpl::RendererSceneControlImpl(RamsesRendererImpl& renderer) - : StatusObjectImpl() - , m_renderer(renderer) - { - } - - status_t RendererSceneControlImpl::setSceneState(sceneId_t sceneId, RendererSceneState state) - { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControl::setSceneState: scene " << sceneId << " " << EnumToString(state)); - - if (state == RendererSceneState::Unavailable) - return addErrorEntry("RendererSceneControl::setSceneState: Can not set scene state Unavailable. In order to release the scene from renderer set Available state."); - - auto& sceneInfo = m_sceneInfos[sceneId]; - if (state >= RendererSceneState::Ready && !sceneInfo.mappingSet) - return addErrorEntry("RendererSceneControl::setSceneState: cannot get scene to ready/rendered without mapping info, set mapping info via RendererSceneControl::setSceneMapping first!"); - sceneInfo.targetState = state; - - ramses_internal::RendererCommand::SetSceneState cmd{ ramses_internal::SceneId{ sceneId.getValue() }, static_cast(state) }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::setSceneMapping(sceneId_t sceneId, displayId_t displayId) - { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControl::setSceneMapping: scene " << sceneId << " display " << displayId); - - auto& sceneInfo = m_sceneInfos[sceneId]; - if (sceneInfo.currState >= RendererSceneState::Ready || sceneInfo.targetState >= RendererSceneState::Ready) - return addErrorEntry("RendererSceneControl::setSceneMapping: cannot change mapping properties, scene's current or desired state already set to READY/RENDERED." - " Set scene state to AVAILABLE first, adjust mapping properties and then it can be made READY/RENDERED with new mapping properties."); - sceneInfo.mappingSet = true; - - ramses_internal::RendererCommand::SetSceneMapping cmd{ ramses_internal::SceneId{ sceneId.getValue() }, ramses_internal::DisplayHandle{ displayId.getValue() } }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) - { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControl::setSceneDisplayBufferAssignment: scene " << sceneId << " displayBuffer " << displayBuffer << " renderOrder " << sceneRenderOrder); - - if (!m_sceneInfos[sceneId].mappingSet) - return addErrorEntry("RendererSceneControl::setSceneDisplayBufferAssignment: scene does not have valid mapping information, set its mapping first."); - - ramses_internal::OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; - // if buffer to assign to is display's framebuffer pass invalid OB to internal renderer - const auto& frameBuffers = m_renderer.getDisplayFrameBuffers(); - if (std::any_of(frameBuffers.cbegin(), frameBuffers.cend(), [displayBuffer](const auto& d) { return d.second == displayBuffer; })) - bufferHandle = ramses_internal::OffscreenBufferHandle::Invalid(); - - ramses_internal::RendererCommand::SetSceneDisplayBufferAssignment cmd{ ramses_internal::SceneId{ sceneId.getValue() }, bufferHandle, sceneRenderOrder }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) - { - const ramses_internal::OffscreenBufferHandle providerBuffer{ offscreenBufferId.getValue() }; - const ramses_internal::SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; - ramses_internal::RendererCommand::LinkOffscreenBuffer cmd{ providerBuffer, internalConsumerSceneId, ramses_internal::DataSlotId{ consumerDataSlotId.getValue() } }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) - { - const ramses_internal::StreamBufferHandle providerBuffer{ streamBufferId.getValue() }; - const ramses_internal::SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; - ramses_internal::RendererCommand::LinkStreamBuffer cmd{ providerBuffer, internalConsumerSceneId, ramses_internal::DataSlotId{ consumerDataSlotId.getValue() } }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) - { - const ramses_internal::ExternalBufferHandle externalTexHandle{externalBufferId.getValue()}; - const ramses_internal::SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; - ramses_internal::RendererCommand::LinkExternalBuffer cmd{ externalTexHandle, internalConsumerSceneId, ramses_internal::DataSlotId{ consumerDataSlotId.getValue() } }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) - { - if (providerSceneId == consumerSceneId) - return addErrorEntry("RendererSceneControl::linkData failed: provider and consumer scene must not be identical"); - - const ramses_internal::SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; - const ramses_internal::SceneId internalProviderSceneId{ providerSceneId.getValue() }; - const ramses_internal::DataSlotId providerSlot{ providerId.getValue() }; - const ramses_internal::DataSlotId consumerSlot{ consumerId.getValue() }; - ramses_internal::RendererCommand::LinkData cmd{ internalProviderSceneId, providerSlot, internalConsumerSceneId, consumerSlot }; - m_pendingRendererCommands.push_back(std::move(cmd)); - - return StatusOK; - } - - status_t RendererSceneControlImpl::unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) - { - const ramses_internal::SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; - const ramses_internal::DataSlotId consumerSlot{ consumerId.getValue() }; - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::UnlinkData{ internalConsumerSceneId, consumerSlot }); - return StatusOK; - } - - status_t RendererSceneControlImpl::handlePickEvent(sceneId_t scene, float bufferNormalizedCoordX, float bufferNormalizedCoordY) - { - const glm::vec2 coords(bufferNormalizedCoordX, bufferNormalizedCoordY); - const ramses_internal::SceneId sceneId(scene.getValue()); - m_pendingRendererCommands.push_back(ramses_internal::RendererCommand::PickEvent{ sceneId, coords }); - return StatusOK; - } - - status_t RendererSceneControlImpl::flush() - { - m_renderer.pushAndConsumeRendererCommands(m_pendingRendererCommands); - return StatusOK; - } - - status_t RendererSceneControlImpl::dispatchEvents(IRendererSceneControlEventHandler& eventHandler) - { - m_tempRendererEvents.clear(); - m_renderer.getDisplayDispatcher().dispatchSceneControlEvents(m_tempRendererEvents); - - for (const auto& event : m_tempRendererEvents) - { - switch (event.eventType) - { - case ramses_internal::ERendererEventType::SceneStateChanged: - { - const sceneId_t sceneId{ event.sceneId.getValue() }; - const auto state = static_cast(event.state); - m_sceneInfos[sceneId].currState = state; - if (state == RendererSceneState::Unavailable) - // reset stored target state if scene became unavailable - m_sceneInfos[sceneId].targetState = RendererSceneState::Unavailable; - eventHandler.sceneStateChanged(sceneId, state); - break; - } - case ramses_internal::ERendererEventType::SceneDataBufferLinked: - assert(event.offscreenBuffer.isValid()? (!event.streamBuffer.isValid() && !event.externalBuffer.isValid()) - : (event.streamBuffer.isValid() != event.externalBuffer.isValid())); - - if (event.offscreenBuffer.isValid()) - eventHandler.offscreenBufferLinked(displayBufferId_t{ event.offscreenBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, true); - if (event.streamBuffer.isValid()) - eventHandler.streamBufferLinked(streamBufferId_t(event.streamBuffer.asMemoryHandle()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), true); - if (event.externalBuffer.isValid()) - eventHandler.externalBufferLinked(externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, true); - - break; - case ramses_internal::ERendererEventType::SceneDataBufferLinkFailed: - assert(event.offscreenBuffer.isValid() ? (!event.streamBuffer.isValid() && !event.externalBuffer.isValid()) - : (event.streamBuffer.isValid() != event.externalBuffer.isValid())); - - if (event.offscreenBuffer.isValid()) - eventHandler.offscreenBufferLinked(displayBufferId_t { event.offscreenBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, false); - if (event.streamBuffer.isValid()) - eventHandler.streamBufferLinked(streamBufferId_t(event.streamBuffer.asMemoryHandle()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), false); - if (event.externalBuffer.isValid()) - eventHandler.externalBufferLinked(externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, false); - - break; - case ramses_internal::ERendererEventType::SceneDataLinked: - eventHandler.dataLinked(sceneId_t(event.providerSceneId.getValue()), dataProviderId_t(event.providerdataId.getValue()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), true); - break; - case ramses_internal::ERendererEventType::SceneDataLinkFailed: - eventHandler.dataLinked(sceneId_t(event.providerSceneId.getValue()), dataProviderId_t(event.providerdataId.getValue()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), false); - break; - case ramses_internal::ERendererEventType::SceneDataUnlinked: - eventHandler.dataUnlinked(sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), true); - break; - case ramses_internal::ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange: - // TODO vaclav remove this event, not useful - break; - case ramses_internal::ERendererEventType::SceneDataUnlinkFailed: - eventHandler.dataUnlinked(sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), false); - break; - case ramses_internal::ERendererEventType::SceneDataSlotProviderCreated: - eventHandler.dataProviderCreated(sceneId_t{ event.providerSceneId.getValue() }, dataProviderId_t{ event.providerdataId.getValue() }); - break; - case ramses_internal::ERendererEventType::SceneDataSlotProviderDestroyed: - eventHandler.dataProviderDestroyed(sceneId_t{ event.providerSceneId.getValue() }, dataProviderId_t{ event.providerdataId.getValue() }); - break; - case ramses_internal::ERendererEventType::SceneDataSlotConsumerCreated: - eventHandler.dataConsumerCreated(sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }); - break; - case ramses_internal::ERendererEventType::SceneDataSlotConsumerDestroyed: - eventHandler.dataConsumerDestroyed(sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }); - break; - case ramses_internal::ERendererEventType::ObjectsPicked: - static_assert(sizeof(ramses::pickableObjectId_t) == sizeof(std::remove_pointer::type), ""); - eventHandler.objectsPicked(ramses::sceneId_t(event.sceneId.getValue()), reinterpret_cast(event.pickedObjectIds.data()), event.pickedObjectIds.size()); - break; - case ramses_internal::ERendererEventType::SceneFlushed: - eventHandler.sceneFlushed(sceneId_t(event.sceneId.getValue()), event.sceneVersionTag.getValue()); - break; - case ramses_internal::ERendererEventType::SceneExpirationMonitoringEnabled: - eventHandler.sceneExpirationMonitoringEnabled(sceneId_t(event.sceneId.getValue())); - break; - case ramses_internal::ERendererEventType::SceneExpirationMonitoringDisabled: - eventHandler.sceneExpirationMonitoringDisabled(sceneId_t(event.sceneId.getValue())); - break; - case ramses_internal::ERendererEventType::SceneExpired: - eventHandler.sceneExpired(sceneId_t(event.sceneId.getValue())); - break; - case ramses_internal::ERendererEventType::SceneRecoveredFromExpiration: - eventHandler.sceneRecoveredFromExpiration(sceneId_t(event.sceneId.getValue())); - break; - case ramses_internal::ERendererEventType::StreamSurfaceAvailable: - eventHandler.streamAvailabilityChanged(ramses::waylandIviSurfaceId_t(event.streamSourceId.getValue()), true); - break; - case ramses_internal::ERendererEventType::StreamSurfaceUnavailable: - eventHandler.streamAvailabilityChanged(ramses::waylandIviSurfaceId_t(event.streamSourceId.getValue()), false); - break; - default: - assert(false); - break; - } - } - - return StatusOK; - } - - const ramses_internal::RendererCommands& RendererSceneControlImpl::getPendingCommands() const - { - return m_pendingRendererCommands; - } -} diff --git a/renderer/ramses-renderer-impl/test/BinaryShaderCacheTest.cpp b/renderer/ramses-renderer-impl/test/BinaryShaderCacheTest.cpp deleted file mode 100644 index fae079502..000000000 --- a/renderer/ramses-renderer-impl/test/BinaryShaderCacheTest.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-renderer-api/BinaryShaderCache.h" -#include "BinaryShaderCacheImpl.h" -#include "gtest/gtest.h" -#include "RendererAPI/Types.h" -#include "Utils/File.h" -#include - -#include -#include - -using namespace ramses_internal; - -class ABinaryShaderCache : public testing::Test -{ -public: - ABinaryShaderCache() - : m_binaryShaderFilePath("test.binaryshader") - { - - } - - ~ABinaryShaderCache() override - { - File binaryShaderFile(m_binaryShaderFilePath); - if (binaryShaderFile.exists()) - { - binaryShaderFile.remove(); - } - } - - void createTestFile() - { - ramses::BinaryShaderCache cache; - - uint8_t shaderData1[] = {12u, 34u, 56u, 78u}; - const uint32_t shaderDataSize1 = sizeof(shaderData1) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format1{ 123u }; - const ramses::effectId_t effectHash1 = {11u, 0}; - cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData1, shaderDataSize1, format1); - - uint8_t shaderData2[] = {13u, 14u, 66u, 7u, 89u, 10u}; - const uint32_t shaderDataSize2 = sizeof(shaderData2) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format2{ 112u }; - const ramses::effectId_t effectHash2 = {12u, 0}; - cache.storeBinaryShader(effectHash2, ramses::sceneId_t(2u), shaderData2, shaderDataSize2, format2); - - cache.saveToFile(m_binaryShaderFilePath.c_str()); - } - - void enlargeTestFile() - { - ramses_internal::File file(m_binaryShaderFilePath.c_str()); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); - EXPECT_TRUE(file.seek(fileSize, File::SeekOrigin::BeginningOfFile)); - char data(33); - EXPECT_TRUE(file.write(&data, sizeof(data))); - } - - void truncateTestFile(int32_t size) - { - ramses_internal::File file(m_binaryShaderFilePath.c_str()); - EXPECT_TRUE(file.open(File::Mode::ReadOnlyBinary)); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - std::vector data(fileSize); - - size_t numReadBytes; - EXPECT_EQ(EStatus::Ok, file.read(data.data(), fileSize, numReadBytes)); - - file.close(); - EXPECT_TRUE(file.open(File::Mode::WriteNewBinary)); - - int32_t newSize = (size < 0) ? static_cast(fileSize) + size : size; - - EXPECT_TRUE(file.write(data.data(), newSize)); - } - - void corruptTestFile() - { - ramses_internal::File file(m_binaryShaderFilePath); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); - const Int offset = sizeof(ramses::BinaryShaderCacheImpl::FileHeader); - EXPECT_TRUE(file.seek(offset, File::SeekOrigin::BeginningOfFile)); - char data = 0; - size_t numBytesRead; - EXPECT_EQ(EStatus::Ok, file.read(&data, sizeof(data), numBytesRead)); - EXPECT_TRUE(file.seek(offset, File::SeekOrigin::BeginningOfFile)); - data++; - EXPECT_TRUE(file.write(&data, sizeof(data))); - } - - void corruptVersionInTestFile() - { - ramses_internal::File file(m_binaryShaderFilePath); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); - const Int transportVersionOffset = offsetof(ramses::BinaryShaderCacheImpl::FileHeader, transportVersion); - EXPECT_TRUE(file.seek(transportVersionOffset, File::SeekOrigin::BeginningOfFile)); - char data = 0; - size_t numBytesRead; - EXPECT_EQ(EStatus::Ok, file.read(&data, sizeof(data), numBytesRead)); - EXPECT_TRUE(file.seek(transportVersionOffset, File::SeekOrigin::BeginningOfFile)); - data++; - EXPECT_TRUE(file.write(&data, sizeof(data))); - } - -protected: - ramses::BinaryShaderCache m_cache; - const std::string m_binaryShaderFilePath; -}; - -TEST_F(ABinaryShaderCache, canStoreAndGetBinaryShader) -{ - uint8_t shaderData[] = { 12u, 34u, 56u, 78u }; - const uint32_t shaderDataSize = sizeof(shaderData) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format{ 123u }; - const ramses::effectId_t effectHash1 = { 11u, 0 }; - m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData, shaderDataSize, format); - - m_cache.deviceSupportsBinaryShaderFormats(&format, 1u); - EXPECT_TRUE(m_cache.hasBinaryShader(effectHash1)); - EXPECT_EQ(shaderDataSize, m_cache.getBinaryShaderSize(effectHash1)); - EXPECT_EQ(format, m_cache.getBinaryShaderFormat(effectHash1)); - - UInt8Vector dataRead(shaderDataSize); - m_cache.getBinaryShaderData(effectHash1, &dataRead.front(), shaderDataSize); - - // make sure the data value is the same. - for (uint32_t index = 0; index < shaderDataSize; index++) - { - EXPECT_TRUE(shaderData[index] == dataRead[index]); - } -} - -TEST_F(ABinaryShaderCache, reportsNoShaderAvailableIfShaderCachedButWithWrongFormat) -{ - std::array shaderData{ 12u, 34u, 56u, 78u }; - const ramses::binaryShaderFormatId_t format{ 123u }; - const ramses::effectId_t effectHash1 = { 11u, 0 }; - m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData.data(), uint32_t(shaderData.size()), format); - - const ramses::binaryShaderFormatId_t supportedFormat{ 124u }; - m_cache.deviceSupportsBinaryShaderFormats(&supportedFormat, 1u); - EXPECT_FALSE(m_cache.hasBinaryShader(effectHash1)); -} - -TEST_F(ABinaryShaderCache, reportsNoShaderAvailableIfShaderNotCachedAndDeviceReportsFormatZeroSupported) // zero format is used as invalid format in cache impl -{ - const ramses::binaryShaderFormatId_t supportedFormat{ 0u }; - m_cache.deviceSupportsBinaryShaderFormats(&supportedFormat, 1u); - const ramses::effectId_t effectHash1 = { 11u, 0 }; - EXPECT_FALSE(m_cache.hasBinaryShader(effectHash1)); -} - -TEST_F(ABinaryShaderCache, canReturnFalseWhenBinaryShaderNotInCache) -{ - uint8_t shaderData[] = { 12u, 34u, 56u, 78u }; - const uint32_t shaderDataSize = sizeof(shaderData) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format{ 123u }; - const ramses::effectId_t effectHash1 = { 11u, 0 }; - m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData, shaderDataSize, format); - - ramses::effectId_t nonExistEffectHash = { 13u, 0 }; - EXPECT_FALSE(m_cache.hasBinaryShader(nonExistEffectHash)); - EXPECT_EQ(ramses::binaryShaderFormatId_t{ 0u }, m_cache.getBinaryShaderFormat(nonExistEffectHash)); - EXPECT_EQ(0u, m_cache.getBinaryShaderSize(nonExistEffectHash)); -} - -TEST_F(ABinaryShaderCache, canSaveToAndLoadFromBinaryShaderFile) -{ - uint8_t shaderData1[] = { 12u, 34u, 56u, 78u }; - const uint32_t shaderDataSize1 = sizeof(shaderData1) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format1{ 123u }; - const ramses::effectId_t effectHash1 = { 11u, 0 }; - m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData1, shaderDataSize1, format1); - - uint8_t shaderData2[] = { 13u, 14u, 66u, 7u, 89u, 10u }; - const uint32_t shaderDataSize2 = sizeof(shaderData2) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format2{ 112u }; - const ramses::effectId_t effectHash2 = { 12u, 0 }; - m_cache.storeBinaryShader(effectHash2, ramses::sceneId_t(2u), shaderData2, shaderDataSize2, format2); - - m_cache.saveToFile(m_binaryShaderFilePath.c_str()); - - ramses::BinaryShaderCache newCache; - EXPECT_TRUE(newCache.loadFromFile(m_binaryShaderFilePath.c_str())); - - // need to init with supported formats in order to report cached shaders - std::array supportedFormats = { format1, format2 }; - newCache.deviceSupportsBinaryShaderFormats(supportedFormats.data(), uint32_t(supportedFormats.size())); - - // check shader data 1 - EXPECT_TRUE(newCache.hasBinaryShader(effectHash1)); - EXPECT_EQ(format1, newCache.getBinaryShaderFormat(effectHash1)); - - const uint32_t shaderDataSize1Read = newCache.getBinaryShaderSize(effectHash1); - EXPECT_EQ(shaderDataSize1, shaderDataSize1Read); - - UInt8Vector shaderData1Read(shaderDataSize1Read); - newCache.getBinaryShaderData(effectHash1, &shaderData1Read.front(), shaderDataSize1Read); - for (uint32_t index = 0; index < shaderDataSize1Read; index++) - { - EXPECT_EQ(shaderData1[index], shaderData1Read[index]); - } - - // check shader data 2 - EXPECT_TRUE(newCache.hasBinaryShader(effectHash2)); - EXPECT_EQ(format2, newCache.getBinaryShaderFormat(effectHash2)); - - const uint32_t shaderDataSize2Read = newCache.getBinaryShaderSize(effectHash2); - EXPECT_EQ(shaderDataSize2, shaderDataSize2Read); - - UInt8Vector shaderData2Read(shaderDataSize2Read); - newCache.getBinaryShaderData(effectHash2, &shaderData2Read.front(), shaderDataSize2Read); - for (uint32_t index = 0; index < shaderDataSize2Read; index++) - { - EXPECT_EQ(shaderData2[index], shaderData2Read[index]); - } -} - -TEST_F(ABinaryShaderCache, canReturnFalseWhenTryToLoadFromNonExistFile) -{ - EXPECT_FALSE(m_cache.loadFromFile("NonExistFile.binaryshader")); -} - -TEST_F(ABinaryShaderCache, reportsSuccessOnCorrectFile) -{ - createTestFile(); - EXPECT_TRUE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); -} - -TEST_F(ABinaryShaderCache, reportsFailOnFileToLarge) -{ - createTestFile(); - enlargeTestFile(); - EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); -} - -TEST_F(ABinaryShaderCache, reportsFailOnFileToShort) -{ - createTestFile(); - truncateTestFile(-1); - EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); -} - -TEST_F(ABinaryShaderCache, reportsFailOnFileShorterThanHeader) -{ - createTestFile(); - truncateTestFile(sizeof(ramses::BinaryShaderCacheImpl::FileHeader) - 1); - EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); -} - -TEST_F(ABinaryShaderCache, reportsFailOnNotMatchingChecksum) -{ - createTestFile(); - corruptTestFile(); - EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); -} - -TEST_F(ABinaryShaderCache, reportsFailOnInvalidFileVersion) -{ - createTestFile(); - corruptVersionInTestFile(); - EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); -} - -TEST_F(ABinaryShaderCache, handlesDoubleStoreProperly) -{ - uint8_t shaderData[] = { 12u, 34u, 56u, 78u }; - const uint32_t shaderDataSize = sizeof(shaderData) / sizeof(uint8_t); - const ramses::binaryShaderFormatId_t format{ 123u }; - const ramses::effectId_t effectHash1 = { 11u, 0 }; - m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData, shaderDataSize, format); - m_cache.deviceSupportsBinaryShaderFormats(&format, 1u); - EXPECT_TRUE(m_cache.hasBinaryShader(effectHash1)); - m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData, shaderDataSize, format); - EXPECT_TRUE(m_cache.hasBinaryShader(effectHash1)); -} diff --git a/renderer/ramses-renderer-impl/test/DefaultRendererResourceCacheTest.cpp b/renderer/ramses-renderer-impl/test/DefaultRendererResourceCacheTest.cpp deleted file mode 100644 index 0d5f47c51..000000000 --- a/renderer/ramses-renderer-impl/test/DefaultRendererResourceCacheTest.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2017 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "ramses-renderer-api/DefaultRendererResourceCache.h" -#include "DefaultRendererResourceCacheImpl.h" -#include "gtest/gtest.h" -#include "RendererAPI/Types.h" -#include "Utils/File.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/Adler32Checksum.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" - -#include - -using namespace ramses_internal; - -class ADefaultRendererResourceCache : public testing::Test -{ -public: - ADefaultRendererResourceCache() - : m_saveFilePath("resCacheTest.dat") - { - } - - ~ADefaultRendererResourceCache() override - { - File binaryShaderFile(m_saveFilePath); - - if (binaryShaderFile.exists()) - { - binaryShaderFile.remove(); - } - } - - void createTestFile() - { - ramses::resourceCacheFlag_t cacheFlag(12345u); - ramses::sceneId_t sceneId(0x2288FFFF44); - - uint8_t data_1[] = {17u, 37u, 12u, 23u, 123u, 21u}; - ramses::rendererResourceId_t resId_1(0xFFFFFFFF123, 0x321FFFFFFFF); - - uint8_t data_2[] = {18u, 32u, 13u, 22u, 13u, 221u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 123u, 110u}; - ramses::rendererResourceId_t resId_2(0x0, 0x1); - - uint8_t data_3[] = {22u}; - ramses::rendererResourceId_t resId_3(0x123456, 0x12345); - - ramses::DefaultRendererResourceCache cache(100); - cache.storeResource(resId_1, data_1, sizeof(data_1), cacheFlag, sceneId); - cache.storeResource(resId_2, data_2, sizeof(data_2), cacheFlag, sceneId); - cache.storeResource(resId_3, data_3, sizeof(data_3), cacheFlag, sceneId); - cache.saveToFile(m_saveFilePath.c_str()); - } - - void enlargeTestFile() - { - ramses_internal::File file(m_saveFilePath); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); - EXPECT_TRUE(file.seek(fileSize, File::SeekOrigin::BeginningOfFile)); - char data(33); - EXPECT_TRUE(file.write(&data, sizeof(data))); - } - - void truncateTestFile(int32_t size) - { - ramses_internal::File file(m_saveFilePath); - EXPECT_TRUE(file.open(File::Mode::ReadOnlyBinary)); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - std::vector data(fileSize); - - size_t numReadBytes; - EXPECT_EQ(EStatus::Ok, file.read(data.data(), fileSize, numReadBytes)); - - file.close(); - EXPECT_TRUE(file.open(File::Mode::WriteNewBinary)); - - int32_t newSize = static_cast(size < 0 ? fileSize + size : size); - - EXPECT_TRUE(file.write(data.data(), newSize)); - } - - void corruptTestFile(uint32_t offset) - { - ramses_internal::File file(m_saveFilePath); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); - - EXPECT_TRUE(file.seek(offset, File::SeekOrigin::BeginningOfFile)); - char data = 0; - size_t numBytesRead; - EXPECT_EQ(EStatus::Ok, file.read(&data, sizeof(data), numBytesRead)); - EXPECT_TRUE(file.seek(offset, File::SeekOrigin::BeginningOfFile)); - data++; - EXPECT_TRUE(file.write(&data, sizeof(data))); - } - - void corruptVersionInTestFile() - { - ramses_internal::File file(m_saveFilePath); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); - const Int transportVersionOffset = offsetof(ramses::DefaultRendererResourceCacheImpl::FileHeader, transportVersion); - EXPECT_TRUE(file.seek(transportVersionOffset, File::SeekOrigin::BeginningOfFile)); - char data = 0; - size_t numBytesRead; - EXPECT_EQ(EStatus::Ok, file.read(&data, sizeof(data), numBytesRead)); - EXPECT_TRUE(file.seek(transportVersionOffset, File::SeekOrigin::BeginningOfFile)); - data++; - EXPECT_TRUE(file.write(&data, sizeof(data))); - } - -protected: - - static void CheckItemInCache(const ramses::DefaultRendererResourceCache& cache, ramses::rendererResourceId_t id, uint8_t* expectedData, uint32_t expectedDataSize) - { - uint32_t foundSize; - EXPECT_TRUE(cache.hasResource(id, foundSize)); - EXPECT_EQ(foundSize, expectedDataSize); - - uint8_t* readBuffer = new uint8_t[expectedDataSize]; - EXPECT_TRUE(cache.getResourceData(id, readBuffer, expectedDataSize)); - - for (uint32_t i = 0; i < expectedDataSize; i++) - { - EXPECT_EQ(readBuffer[i], expectedData[i]); - } - - delete[] readBuffer; - } - - const std::string m_saveFilePath; -}; - -TEST_F(ADefaultRendererResourceCache, canStoreAndGetResource) -{ - ramses::DefaultRendererResourceCache cache(100); - - const ramses::rendererResourceId_t resId(123, 123); - uint8_t resData[50]; - - // Add some non-zero data - for (size_t i = 0; i < sizeof(resData); i++) - { - resData[i] = i % 9; - } - - cache.storeResource(resId, resData, sizeof(resData), ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7)); - - uint32_t loadedSize; - EXPECT_TRUE(cache.hasResource(resId, loadedSize)); - EXPECT_EQ(loadedSize, sizeof(resData)); - - uint8_t readBuffer[200] = {0}; // On purpose this is larger than the result data - EXPECT_TRUE(cache.getResourceData(resId, readBuffer, sizeof(readBuffer))); - - for (size_t i = 0; i < sizeof(resData); i++) - { - EXPECT_EQ(resData[i], readBuffer[i]); - } -} - -TEST_F(ADefaultRendererResourceCache, doesNotAttempToStoreItemsTooLargeForCache) -{ - ramses::DefaultRendererResourceCache cache(100); - EXPECT_FALSE(cache.shouldResourceBeCached(ramses::rendererResourceId_t(123, 123), 200, ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7))); -} - -TEST_F(ADefaultRendererResourceCache, canReturnFalseIfResourceNotInCache) -{ - ramses::DefaultRendererResourceCache cache(100); - uint32_t size; - EXPECT_FALSE(cache.hasResource(ramses::rendererResourceId_t(123, 123), size)); -} - -TEST_F(ADefaultRendererResourceCache, unloadsOldestAddedItemWhenOutOfSpace) -{ - uint8_t inputBuffer[1000]; - uint32_t size; - ramses::DefaultRendererResourceCache cache(100); - - cache.storeResource(ramses::rendererResourceId_t(1, 0), inputBuffer, 40, ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7)); - cache.storeResource(ramses::rendererResourceId_t(2, 0), inputBuffer, 40, ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7)); - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(1, 0), size)); - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(2, 0), size)); - - // Add an item which will exceed the maximum capacity of the cache - cache.storeResource(ramses::rendererResourceId_t(3, 0), inputBuffer, 40, ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7)); - EXPECT_FALSE(cache.hasResource(ramses::rendererResourceId_t(1, 0), size)); // Now gone - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(2, 0), size)); - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(3, 0), size)); - - // Same story again - cache.storeResource(ramses::rendererResourceId_t(4, 0), inputBuffer, 40, ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7)); - EXPECT_FALSE(cache.hasResource(ramses::rendererResourceId_t(2, 0), size)); // Now gone - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(3, 0), size)); - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(4, 0), size)); - - // Finally add one item which takes up all space - cache.storeResource(ramses::rendererResourceId_t(5, 0), inputBuffer, 100, ramses::resourceCacheFlag_t(1), ramses::sceneId_t(7)); - EXPECT_FALSE(cache.hasResource(ramses::rendererResourceId_t(3, 0), size)); - EXPECT_FALSE(cache.hasResource(ramses::rendererResourceId_t(4, 0), size)); - EXPECT_TRUE(cache.hasResource(ramses::rendererResourceId_t(5, 0), size)); -} - -TEST_F(ADefaultRendererResourceCache, canSaveAndLoadFromFile) -{ - ramses::resourceCacheFlag_t cacheFlag(12345u); - ramses::sceneId_t sceneId(0x2288FFFF44); - - uint8_t data_1[] = { 17u, 37u, 12u, 23u, 123u, 21u }; - ramses::rendererResourceId_t resId_1(0xFFFFFFFF123, 0x321FFFFFFFF); - - uint8_t data_2[] = { 18u, 32u, 13u, 22u, 13u, 221u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 123u, 110u }; - ramses::rendererResourceId_t resId_2(0x0, 0x1); - - uint8_t data_3[] = { 22u }; - ramses::rendererResourceId_t resId_3(0x123456, 0x12345); - - ramses::DefaultRendererResourceCache savingCache(100); - savingCache.storeResource(resId_1, data_1, sizeof(data_1), cacheFlag, sceneId); - savingCache.storeResource(resId_2, data_2, sizeof(data_2), cacheFlag, sceneId); - savingCache.storeResource(resId_3, data_3, sizeof(data_3), cacheFlag, sceneId); - savingCache.saveToFile(m_saveFilePath.c_str()); - - ramses::DefaultRendererResourceCache loadedCache(100); - EXPECT_TRUE(loadedCache.loadFromFile(m_saveFilePath.c_str())); - CheckItemInCache(loadedCache, resId_1, data_1, sizeof(data_1)); - CheckItemInCache(loadedCache, resId_2, data_2, sizeof(data_2)); - CheckItemInCache(loadedCache, resId_3, data_3, sizeof(data_3)); -} - -TEST_F(ADefaultRendererResourceCache, reportsFailOnFileNotFound) -{ - ramses::DefaultRendererResourceCache cache(100); - EXPECT_FALSE(cache.loadFromFile("doesNotExist.dat")); -} - -TEST_F(ADefaultRendererResourceCache, reportsSuccessOnCorrectFile) -{ - ramses::DefaultRendererResourceCache cache(100); - createTestFile(); - EXPECT_TRUE(cache.loadFromFile(m_saveFilePath.c_str())); -} - -TEST_F(ADefaultRendererResourceCache, reportsFailOnFileToLarge) -{ - ramses::DefaultRendererResourceCache cache(100); - createTestFile(); - enlargeTestFile(); - EXPECT_FALSE(cache.loadFromFile(m_saveFilePath.c_str())); -} - -TEST_F(ADefaultRendererResourceCache, reportsFailOnFileToShort) -{ - ramses::DefaultRendererResourceCache cache(100); - createTestFile(); - truncateTestFile(-1); - EXPECT_FALSE(cache.loadFromFile(m_saveFilePath.c_str())); -} - -TEST_F(ADefaultRendererResourceCache, reportsFailOnFileShorterThanHeader) -{ - ramses::DefaultRendererResourceCache cache(100); - createTestFile(); - truncateTestFile(sizeof(ramses::DefaultRendererResourceCacheImpl::FileHeader) - 1); - EXPECT_FALSE(cache.loadFromFile(m_saveFilePath.c_str())); -} - -TEST_F(ADefaultRendererResourceCache, reportsFailForAnyByteCorruptionInFile) -{ - ramses::DefaultRendererResourceCache cache(100); - - createTestFile(); - - ramses_internal::File file(m_saveFilePath); - size_t fileSize(0); - EXPECT_TRUE(file.getSizeInBytes(fileSize)); - - for (size_t offset = 0; offset < fileSize; offset++) - { - if (offset > 0) - { - createTestFile(); - } - corruptTestFile(static_cast(offset)); - EXPECT_FALSE(cache.loadFromFile(m_saveFilePath.c_str())); - } -} - -TEST_F(ADefaultRendererResourceCache, reportsFailOnInvalidFileVersion) -{ - ramses::DefaultRendererResourceCache cache(100); - createTestFile(); - corruptVersionInTestFile(); - EXPECT_FALSE(cache.loadFromFile(m_saveFilePath.c_str())); -} - -TEST_F(ADefaultRendererResourceCache, reportsFailWhenCacheTooSmall) -{ - ramses::DefaultRendererResourceCache cache(1); - createTestFile(); - EXPECT_FALSE(cache.loadFromFile(m_saveFilePath.c_str())); -} diff --git a/renderer/ramses-renderer-impl/test/DisplayConfigTest.cpp b/renderer/ramses-renderer-impl/test/DisplayConfigTest.cpp deleted file mode 100644 index f67b2d64f..000000000 --- a/renderer/ramses-renderer-impl/test/DisplayConfigTest.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "ramses-renderer-api/DisplayConfig.h" -#include "DisplayConfigImpl.h" - -class ADisplayConfig : public ::testing::Test -{ -protected: - ramses::DisplayConfig config; -}; - -TEST_F(ADisplayConfig, hasDefaultValuesUponConstruction) -{ - const ramses_internal::DisplayConfig defaultDisplayConfig; - const ramses_internal::DisplayConfig& displayConfig = config.m_impl.get().getInternalDisplayConfig(); - - EXPECT_EQ(defaultDisplayConfig.getWindowPositionX(), displayConfig.getWindowPositionX()); - EXPECT_EQ(defaultDisplayConfig.getWindowPositionY(), displayConfig.getWindowPositionY()); - EXPECT_EQ(defaultDisplayConfig.getDesiredWindowWidth(), displayConfig.getDesiredWindowWidth()); - EXPECT_EQ(defaultDisplayConfig.getDesiredWindowHeight(), displayConfig.getDesiredWindowHeight()); - - EXPECT_EQ(defaultDisplayConfig.getFullscreenState(), displayConfig.getFullscreenState()); - EXPECT_EQ(defaultDisplayConfig.getBorderlessState(), displayConfig.getBorderlessState()); - EXPECT_EQ(defaultDisplayConfig.getKeepEffectsUploaded(), displayConfig.getKeepEffectsUploaded()); - - EXPECT_EQ(defaultDisplayConfig.getAntialiasingSampleCount(), displayConfig.getAntialiasingSampleCount()); - EXPECT_EQ(defaultDisplayConfig.getStartVisibleIvi(), displayConfig.getStartVisibleIvi()); - - EXPECT_EQ(defaultDisplayConfig.getGPUMemoryCacheSize(), displayConfig.getGPUMemoryCacheSize()); - EXPECT_EQ(defaultDisplayConfig.getClearColor(), displayConfig.getClearColor()); - - EXPECT_TRUE(defaultDisplayConfig.getWaylandDisplay().empty()); - - EXPECT_FALSE(defaultDisplayConfig.getWaylandIviSurfaceID().isValid()); - EXPECT_FALSE(defaultDisplayConfig.getWaylandIviLayerID().isValid()); - - EXPECT_FALSE(defaultDisplayConfig.getWindowsWindowHandle().isValid()); - EXPECT_FALSE(defaultDisplayConfig.getX11WindowHandle().isValid()); - - EXPECT_EQ(defaultDisplayConfig.getWaylandSocketEmbedded(), displayConfig.getWaylandSocketEmbedded()); - EXPECT_EQ(defaultDisplayConfig.getWaylandSocketEmbeddedGroup(), displayConfig.getWaylandSocketEmbeddedGroup()); - EXPECT_EQ(defaultDisplayConfig.getWaylandSocketEmbeddedFD(), displayConfig.getWaylandSocketEmbeddedFD()); - EXPECT_EQ(defaultDisplayConfig.getPlatformRenderNode(), displayConfig.getPlatformRenderNode()); -} - -TEST_F(ADisplayConfig, setsDeviceType) -{ - EXPECT_EQ(ramses::StatusOK, config.setDeviceType(ramses::EDeviceType::GL_4_2)); - EXPECT_EQ(ramses_internal::EDeviceType::GL_4_2, config.m_impl.get().getInternalDisplayConfig().getDeviceType()); -} - -TEST_F(ADisplayConfig, setsWindowType) -{ - EXPECT_EQ(ramses::StatusOK, config.setWindowType(ramses::EWindowType::Wayland_IVI)); - EXPECT_EQ(ramses_internal::EWindowType::Wayland_IVI, config.m_impl.get().getInternalDisplayConfig().getWindowType()); -} - -TEST_F(ADisplayConfig, setsFullscreenState) -{ - EXPECT_EQ(ramses::StatusOK, config.setWindowFullscreen(true)); - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().getFullscreenState()); - EXPECT_TRUE(config.isWindowFullscreen()); - - EXPECT_EQ(ramses::StatusOK, config.setWindowFullscreen(false)); - EXPECT_FALSE(config.m_impl.get().getInternalDisplayConfig().getFullscreenState()); - EXPECT_FALSE(config.isWindowFullscreen()); -} - -TEST_F(ADisplayConfig, setsBorderlessState) -{ - EXPECT_EQ(ramses::StatusOK, config.setWindowBorderless(true)); - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().getBorderlessState()); - - EXPECT_EQ(ramses::StatusOK, config.setWindowBorderless(false)); - EXPECT_FALSE(config.m_impl.get().getInternalDisplayConfig().getBorderlessState()); -} - -TEST_F(ADisplayConfig, setsWindowRect) -{ - EXPECT_EQ(ramses::StatusOK, config.setWindowRectangle(15, 16, 123u, 345u)); - int32_t x; - int32_t y; - uint32_t width; - uint32_t height; - config.getWindowRectangle(x, y, width, height); - EXPECT_EQ(15, x); - EXPECT_EQ(16, y); - EXPECT_EQ(123u, width); - EXPECT_EQ(345u, height); -} - -TEST_F(ADisplayConfig, failsToSetInvalidWindowRect) -{ - EXPECT_EQ(ramses::StatusOK, config.setWindowRectangle(15, 16, 1u, 2u)); - - EXPECT_NE(ramses::StatusOK, config.setWindowRectangle(15, 16, 0u, 2u)); - EXPECT_NE(ramses::StatusOK, config.setWindowRectangle(15, 16, 1u, 0u)); - EXPECT_NE(ramses::StatusOK, config.setWindowRectangle(15, 16, 0u, 0u)); - - EXPECT_EQ(15, config.m_impl.get().getInternalDisplayConfig().getWindowPositionX()); - EXPECT_EQ(16, config.m_impl.get().getInternalDisplayConfig().getWindowPositionY()); - EXPECT_EQ(1u, config.m_impl.get().getInternalDisplayConfig().getDesiredWindowWidth()); - EXPECT_EQ(2u, config.m_impl.get().getInternalDisplayConfig().getDesiredWindowHeight()); -} - -TEST_F(ADisplayConfig, failsToSetUnsupportedMultisampling) -{ - EXPECT_NE(ramses::StatusOK, config.setMultiSampling(0u)); - EXPECT_NE(ramses::StatusOK, config.setMultiSampling(3u)); - EXPECT_NE(ramses::StatusOK, config.setMultiSampling(5u)); - EXPECT_NE(ramses::StatusOK, config.setMultiSampling(6u)); - EXPECT_NE(ramses::StatusOK, config.setMultiSampling(7u)); - EXPECT_NE(ramses::StatusOK, config.setMultiSampling(9u)); - - EXPECT_EQ(1u, config.m_impl.get().getInternalDisplayConfig().getAntialiasingSampleCount()); -} - -TEST_F(ADisplayConfig, setsAndGetsMultisampling) -{ - EXPECT_EQ(ramses::StatusOK, config.setMultiSampling(2u)); - - EXPECT_EQ(2u, config.m_impl.get().getInternalDisplayConfig().getAntialiasingSampleCount()); - - uint32_t sampleCount = 0; - config.getMultiSamplingSamples(sampleCount); - EXPECT_EQ(2u, sampleCount); -} - -TEST_F(ADisplayConfig, disablesKeepingOfEffectsInVRAM) -{ - EXPECT_EQ(ramses::StatusOK, config.keepEffectsUploaded(false)); - EXPECT_FALSE(config.m_impl.get().getInternalDisplayConfig().getKeepEffectsUploaded()); -} - -TEST_F(ADisplayConfig, setsWindowIVIVisible) -{ - EXPECT_EQ(ramses::StatusOK, config.setWindowIviVisible()); - EXPECT_TRUE(config.m_impl.get().getInternalDisplayConfig().getStartVisibleIvi()); -} - -TEST_F(ADisplayConfig, setsAndGetsWaylandDisplay) -{ - EXPECT_EQ(ramses::StatusOK, config.setWaylandDisplay("xxx")); - EXPECT_EQ("xxx", config.getWaylandDisplay()); - EXPECT_EQ("xxx", config.m_impl.get().getInternalDisplayConfig().getWaylandDisplay()); -} - -TEST_F(ADisplayConfig, setsAndGetsWaylandIviSurfaceId) -{ - EXPECT_EQ(ramses::StatusOK, config.setWaylandIviSurfaceID(ramses::waylandIviSurfaceId_t(25))); - EXPECT_EQ(ramses::waylandIviSurfaceId_t(25), config.getWaylandIviSurfaceID()); - EXPECT_EQ(ramses_internal::WaylandIviSurfaceId(25), config.m_impl.get().getInternalDisplayConfig().getWaylandIviSurfaceID()); -} - -TEST_F(ADisplayConfig, setsAndGetsWaylandIviLayerId) -{ - EXPECT_EQ(ramses::StatusOK, config.setWaylandIviLayerID(ramses::waylandIviLayerId_t(36))); - EXPECT_EQ(ramses::waylandIviLayerId_t(36), config.getWaylandIviLayerID()); - EXPECT_EQ(ramses_internal::WaylandIviLayerId(36), config.m_impl.get().getInternalDisplayConfig().getWaylandIviLayerID()); -} - -TEST_F(ADisplayConfig, setsAndGetsX11WindowHandle) -{ - EXPECT_EQ(ramses::StatusOK, config.setX11WindowHandle(42u)); - EXPECT_EQ(42u, config.getX11WindowHandle()); - EXPECT_EQ(ramses_internal::X11WindowHandle(42), config.m_impl.get().getInternalDisplayConfig().getX11WindowHandle()); -} - -TEST_F(ADisplayConfig, IsValidUponConstruction) -{ - EXPECT_EQ(ramses::StatusOK, config.validate()); -} - -TEST_F(ADisplayConfig, CanBeCopyAndMoveConstructed) -{ - config.setWindowFullscreen(true); - - ramses::DisplayConfig configCopy{ config }; - EXPECT_TRUE(configCopy.isWindowFullscreen()); - - ramses::DisplayConfig configMove{ std::move(config) }; - EXPECT_TRUE(configMove.isWindowFullscreen()); -} - -TEST_F(ADisplayConfig, CanBeCopyAndMoveAssigned) -{ - config.setWindowFullscreen(true); - - ramses::DisplayConfig configCopy; - configCopy = config; - EXPECT_TRUE(configCopy.isWindowFullscreen()); - - ramses::DisplayConfig configMove; - configMove = std::move(config); - EXPECT_TRUE(configMove.isWindowFullscreen()); -} - -TEST_F(ADisplayConfig, CanBeSelfAssigned) -{ - config.setWindowFullscreen(true); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - config = config; - EXPECT_TRUE(config.isWindowFullscreen()); - config = std::move(config); - // NOLINTNEXTLINE(bugprone-use-after-move) - EXPECT_TRUE(config.isWindowFullscreen()); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -} - -TEST_F(ADisplayConfig, setClearColor) -{ - const float red = 0.1f; - const float green = 0.2f; - const float blue = 0.3f; - const float alpha = 0.4f; - EXPECT_EQ(ramses::StatusOK, config.setClearColor({red, green, blue, alpha})); - - const glm::vec4& clearColor = config.m_impl.get().getInternalDisplayConfig().getClearColor(); - EXPECT_EQ(clearColor.r, red); - EXPECT_EQ(clearColor.g, green); - EXPECT_EQ(clearColor.b, blue); - EXPECT_EQ(clearColor.a, alpha); -} - -TEST_F(ADisplayConfig, setDepthStencilBufferType) -{ - EXPECT_EQ(ramses::StatusOK, config.setDepthStencilBufferType(ramses::EDepthBufferType::Depth)); - EXPECT_EQ(ramses_internal::ERenderBufferType_DepthBuffer, config.m_impl.get().getInternalDisplayConfig().getDepthStencilBufferType()); -} - -TEST_F(ADisplayConfig, setAsyncEffectUploadEnabled) -{ - EXPECT_EQ(ramses::StatusOK, config.setAsyncEffectUploadEnabled(false)); - EXPECT_FALSE(config.m_impl.get().getInternalDisplayConfig().isAsyncEffectUploadEnabled()); -} - -TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketGroup) -{ - config.setWaylandEmbeddedCompositingSocketGroup("permissionGroup"); - EXPECT_EQ("permissionGroup", config.m_impl.get().getWaylandSocketEmbeddedGroup()); -} - -TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketPermissions) -{ - config.setWaylandEmbeddedCompositingSocketPermissions(0660); - EXPECT_EQ(0660u, config.m_impl.get().getWaylandSocketEmbeddedPermissions()); -} - -TEST_F(ADisplayConfig, cannotSetInvalidEmbeddedCompositingSocketPermissions) -{ - EXPECT_NE(ramses::StatusOK, config.setWaylandEmbeddedCompositingSocketPermissions(0)); -} - -TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketname) -{ - config.setWaylandEmbeddedCompositingSocketName("wayland-x123"); - EXPECT_EQ("wayland-x123", config.getWaylandEmbeddedCompositingSocketName()); -} - -TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketFD) -{ - config.setWaylandEmbeddedCompositingSocketFD(23); - EXPECT_EQ(23, config.m_impl.get().getWaylandSocketEmbeddedFD()); -} - -TEST_F(ADisplayConfig, canSetPlatformRenderNode) -{ - config.setPlatformRenderNode("abcd"); - EXPECT_EQ("abcd", config.m_impl.get().getPlatformRenderNode()); -} - -TEST_F(ADisplayConfig, canSetSwapInterval) -{ - EXPECT_EQ(-1, config.m_impl.get().getSwapInterval()); - EXPECT_EQ(ramses::StatusOK, config.setSwapInterval(0)); - EXPECT_EQ(0, config.m_impl.get().getSwapInterval()); -} - -TEST_F(ADisplayConfig, canSetScenePriority) -{ - EXPECT_EQ(0, config.m_impl.get().getScenePriority(ramses::sceneId_t(551))); - EXPECT_EQ(ramses::StatusOK, config.setScenePriority(ramses::sceneId_t(551), 4)); - EXPECT_EQ(4, config.m_impl.get().getScenePriority(ramses::sceneId_t(551))); -} - -TEST_F(ADisplayConfig, canSetResourceUploadBatchSize) -{ - EXPECT_EQ(10u, config.m_impl.get().getResourceUploadBatchSize()); - EXPECT_EQ(ramses::StatusOK, config.setResourceUploadBatchSize(1)); - EXPECT_EQ(1u, config.m_impl.get().getResourceUploadBatchSize()); - EXPECT_NE(ramses::StatusOK, config.setResourceUploadBatchSize(0)); - EXPECT_EQ(1u, config.m_impl.get().getResourceUploadBatchSize()); -} - diff --git a/renderer/ramses-renderer-impl/test/RamsesRendererTest.cpp b/renderer/ramses-renderer-impl/test/RamsesRendererTest.cpp deleted file mode 100644 index dd5eb201c..000000000 --- a/renderer/ramses-renderer-impl/test/RamsesRendererTest.cpp +++ /dev/null @@ -1,937 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2016 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "gmock/gmock.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" - -#include "RendererLib/RendererCommands.h" -#include "RamsesRendererImpl.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "PlatformAbstraction/PlatformEvent.h" -#include "RendererCommandVisitorMock.h" -#include "PlatformFactoryMock.h" -#include "SceneAPI/RenderState.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" - -using namespace testing; - -static ramses::RamsesRenderer* CreateRenderer(ramses::RamsesFramework& fw, const ramses::RendererConfig& config) -{ - auto* createdRenderer = fw.createRenderer(config); - createdRenderer->m_impl.getDisplayDispatcher().injectPlatformFactory(std::make_unique()); - return createdRenderer; -} - -class SafeThreadWatchdogNotificationMock : public ramses::IThreadWatchdogNotification -{ -public: - explicit SafeThreadWatchdogNotificationMock(ramses_internal::PlatformLock& lock) - : m_lock(lock) - { - } - - MOCK_METHOD(void, safe_notifyThread, (ramses::ERamsesThreadIdentifier)); - MOCK_METHOD(void, safe_registerThread, (ramses::ERamsesThreadIdentifier)); - MOCK_METHOD(void, safe_unregisterThread, (ramses::ERamsesThreadIdentifier)); - -private: - void notifyThread(ramses::ERamsesThreadIdentifier threadID) override - { - ramses_internal::PlatformGuard g(m_lock); - safe_notifyThread(threadID); - } - - void registerThread(ramses::ERamsesThreadIdentifier threadID) override - { - ramses_internal::PlatformGuard g(m_lock); - safe_registerThread(threadID); - } - - void unregisterThread(ramses::ERamsesThreadIdentifier threadID) override - { - ramses_internal::PlatformGuard g(m_lock); - safe_unregisterThread(threadID); - } - - ramses_internal::PlatformLock& m_lock; -}; - - -class ARamsesRenderer : public ::testing::Test -{ -protected: - explicit ARamsesRenderer(const ramses::RendererConfig& rendererConfig = ramses::RendererConfig()) - : framework(ramses::RamsesFrameworkConfig{ramses::EFeatureLevel_Latest}) - , renderer(*CreateRenderer(framework, rendererConfig)) - , commandBuffer(renderer.m_impl.getPendingCommands()) - { - } - - ramses::displayId_t addDisplay() - { - //Create a display - ramses::DisplayConfig displayConfig; - EXPECT_EQ(ramses::StatusOK, displayConfig.validate()); - return renderer.createDisplay(displayConfig); - } - -protected: - ramses::RamsesFramework framework; - ramses::RamsesRenderer& renderer; - const ramses_internal::RendererCommands& commandBuffer; - StrictMock cmdVisitor; -}; - -class ARamsesRendererWithDisplay : public ARamsesRenderer -{ - void SetUp() override - { - displayId = addDisplay(); - EXPECT_NE(ramses::displayId_t::Invalid(), displayId); - renderer.flush(); - } - - void TearDown() override - { - renderer.destroyDisplay(displayId); - } - -protected: - ramses::displayId_t displayId; -}; - -class ARamsesRendererWithSystemCompositorController : public ARamsesRenderer -{ -public: - ARamsesRendererWithSystemCompositorController() - : ARamsesRenderer(CreateRendererConfigWithSystemCompositor()) - { - } - - static ramses::RendererConfig CreateRendererConfigWithSystemCompositor() - { - ramses::RendererConfig config; - config.enableSystemCompositorControl(); - return config; - } -}; - -TEST_F(ARamsesRenderer, hasNoCommandsOnStartUp) -{ - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRenderer, canOnlyGetOneSceneControlAPI) -{ - const auto api = renderer.getSceneControlAPI(); - EXPECT_TRUE(api != nullptr); - EXPECT_EQ(api, renderer.getSceneControlAPI()); - EXPECT_EQ(api, renderer.getSceneControlAPI()); -} - -/* - * Display - */ -TEST_F(ARamsesRenderer, createsACommandForDisplayCreation) -{ - const ramses::displayId_t displayId = addDisplay(); - EXPECT_NE(ramses::displayId_t::Invalid(), displayId); - - EXPECT_CALL(cmdVisitor, createDisplayContext(_, ramses_internal::DisplayHandle{ displayId.getValue() }, _)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRenderer, createsMultipleCommandsForMultipleDisplayCreation) -{ - EXPECT_NE(ramses::displayId_t::Invalid(), addDisplay()); - EXPECT_NE(ramses::displayId_t::Invalid(), addDisplay()); - EXPECT_NE(ramses::displayId_t::Invalid(), addDisplay()); - - EXPECT_CALL(cmdVisitor, createDisplayContext(_, _, _)).Times(3); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForDisplayDestruction) -{ - EXPECT_EQ(ramses::StatusOK, renderer.destroyDisplay(displayId)); - EXPECT_CALL(cmdVisitor, destroyDisplayContext(_)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRenderer, canQueryDisplayFramebufferIDs) -{ - const ramses::displayId_t displayId1 = addDisplay(); - const auto fbId1 = renderer.getDisplayFramebuffer(displayId1); - EXPECT_TRUE(fbId1.isValid()); -} - -TEST_F(ARamsesRenderer, displayFramebufferIDsAreUnique) -{ - EXPECT_FALSE(renderer.getDisplayFramebuffer(ramses::displayId_t::Invalid()).isValid()); - - const ramses::displayId_t displayId1 = addDisplay(); - const ramses::displayId_t displayId2 = addDisplay(); - const ramses::displayId_t displayId3 = addDisplay(); - const auto fbId1 = renderer.getDisplayFramebuffer(displayId1); - const auto fbId2 = renderer.getDisplayFramebuffer(displayId2); - const auto fbId3 = renderer.getDisplayFramebuffer(displayId3); - EXPECT_TRUE(fbId1.isValid()); - EXPECT_TRUE(fbId2.isValid()); - EXPECT_TRUE(fbId3.isValid()); - EXPECT_NE(fbId1, fbId2); - EXPECT_NE(fbId1, fbId3); - EXPECT_NE(fbId2, fbId3); -} - -TEST_F(ARamsesRenderer, displayFramebufferIsInvalidIfDisplayDestroyed) -{ - const ramses::displayId_t displayId = addDisplay(); - EXPECT_TRUE(renderer.getDisplayFramebuffer(displayId).isValid()); - renderer.destroyDisplay(displayId); - EXPECT_FALSE(renderer.getDisplayFramebuffer(displayId).isValid()); -} - -TEST_F(ARamsesRenderer, createsCommandForLoggingRenderInfo) -{ - EXPECT_EQ(ramses::StatusOK, renderer.logRendererInfo()); - EXPECT_CALL(cmdVisitor, logInfo(_, _, _)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRenderer, canCreateDisplayWithEmbeddedCompositingSetOnDisplayConfig) -{ - ramses::DisplayConfig displayConfig; - displayConfig.setWaylandEmbeddedCompositingSocketName("ec-socket"); - const ramses::displayId_t displayId = renderer.createDisplay(displayConfig); - EXPECT_NE(ramses::displayId_t::Invalid(), displayId); - - EXPECT_CALL(cmdVisitor, createDisplayContext(_, ramses_internal::DisplayHandle{ displayId.getValue() }, _)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRenderer, displayIsCreatedWithDefaultFramerate) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework ramsesFramework{frameworkConfig}; - ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); - - ramsesRenderer->startThread(); - const ramses::displayId_t displayId = ramsesRenderer->createDisplay({}); - ramsesRenderer->flush(); - EXPECT_NEAR(60.f, ramsesRenderer->getFramerateLimit(displayId), 0.01f); - ramsesRenderer->stopThread(); -} - -TEST_F(ARamsesRenderer, canSetAndGetFPSLimitOfNonExistingDisplay) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework ramsesFramework{frameworkConfig}; - ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); - - constexpr ramses::displayId_t futureDisplay{ 22u }; - EXPECT_NEAR(60.f, ramsesRenderer->getFramerateLimit(futureDisplay), 0.01f); - - EXPECT_EQ(ramses::StatusOK, ramsesRenderer->setFramerateLimit(futureDisplay, 10.f)); - EXPECT_NEAR(10.f, ramsesRenderer->getFramerateLimit(futureDisplay), 0.01f); -} - -TEST_F(ARamsesRenderer, failsToSetNegativeOrZeroFPSLimit) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework ramsesFramework{frameworkConfig}; - ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); - - constexpr ramses::displayId_t futureDisplay{ 22u }; - EXPECT_NE(ramses::StatusOK, ramsesRenderer->setFramerateLimit(futureDisplay, 0.f)); - EXPECT_NE(ramses::StatusOK, ramsesRenderer->setFramerateLimit(futureDisplay, -10.f)); -} - -TEST_F(ARamsesRenderer, failsToSetFPSLimitInNonThreadedMode) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework ramsesFramework{frameworkConfig}; - ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); - ramsesRenderer->doOneLoop(); - - constexpr ramses::displayId_t futureDisplay{ 22u }; - EXPECT_NE(ramses::StatusOK, ramsesRenderer->setFramerateLimit(futureDisplay, 10.f)); -} - -/* -* Offscreen buffers -*/ -TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithDepthStencilBufferAsDefault) -{ - EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u)); - EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses_internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses_internal::ERenderBufferType_DepthStencilBuffer)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithoutDepthStencilBuffer) -{ - EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u, ramses::EDepthBufferType::None)); - EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses_internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses_internal::ERenderBufferType_InvalidBuffer)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithDepthBuffer) -{ - EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u, ramses::EDepthBufferType::Depth)); - EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses_internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses_internal::ERenderBufferType_DepthBuffer)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithDepthStencilBuffer) -{ - EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u, ramses::EDepthBufferType::DepthStencil)); - EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses_internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses_internal::ERenderBufferType_DepthStencilBuffer)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForDmaOffscreenBufferCreate) -{ - constexpr uint32_t fourccFormat = 777u; - constexpr uint32_t bufferUsageFlags = 888u; - constexpr uint64_t modifier = 999u; - EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 40u, 40u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_CALL(cmdVisitor, handleDmaBufferCreateRequest(_, ramses_internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, ramses_internal::DmaBufferFourccFormat(fourccFormat), ramses_internal::DmaBufferUsageFlags(bufferUsageFlags), ramses_internal::DmaBufferModifiers(modifier))); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, failsToCreateDmaOffscreenBufferIfRendererIsRunningInOwnThread) -{ - renderer.startThread(); - - constexpr uint32_t fourccFormat = 777u; - constexpr uint32_t bufferUsageFlags = 888u; - constexpr uint64_t modifier = 999u; - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 40u, 40u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_CALL(cmdVisitor, handleDmaBufferCreateRequest(_, _, _, _, _, _, _)).Times(0); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, canGetDmaOffscreenBufferFDAndStride) -{ - ramses_internal::RendererEvent event; - event.eventType = ramses_internal::ERendererEventType::OffscreenBufferCreated; - event.displayHandle = ramses_internal::DisplayHandle{displayId.getValue()}; - event.offscreenBuffer = ramses_internal::OffscreenBufferHandle{ 10u }; - event.dmaBufferFD = 20; - event.dmaBufferStride = 30u; - renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(event)); - - ramses::RendererEventHandlerEmpty dummyHandler; - renderer.dispatchEvents(dummyHandler); - int resultFD = -1; - uint32_t resultStride = 0u; - EXPECT_EQ(ramses::StatusOK, renderer.getDmaOffscreenBufferFDAndStride(displayId, ramses::displayBufferId_t{ 10u }, resultFD, resultStride)); - EXPECT_EQ(20, resultFD); - EXPECT_EQ(30u, resultStride); -} - -TEST_F(ARamsesRendererWithDisplay, reportsErrorIfGetingFDAndStrideForUnknownDmaOffscreenBuffer) -{ - int resultFD = -1; - uint32_t resultStride = 0u; - EXPECT_NE(ramses::StatusOK, renderer.getDmaOffscreenBufferFDAndStride(displayId, ramses::displayBufferId_t{ 10u }, resultFD, resultStride)); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferDestroy) -{ - const ramses::displayBufferId_t bufferId(0u); - EXPECT_EQ(ramses::StatusOK, renderer.destroyOffscreenBuffer(displayId, bufferId)); - EXPECT_CALL(cmdVisitor, handleBufferDestroyRequest(ramses_internal::OffscreenBufferHandle{ bufferId.getValue() }, ramses_internal::DisplayHandle{ displayId.getValue() })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, failsToCreateOffscreenBufferWithUnsupportedResolution) -{ - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 0u, 1u, 4u)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 1u, 0u, 4u)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 0u, 0u, 4u)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 5000u, 1u, 4u)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 1u, 5000u, 4u)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 5000u, 5000u, 4u)); -} - -TEST_F(ARamsesRendererWithDisplay, failsToCreateDmaOffscreenBufferWithUnsupportedResolution) -{ - constexpr uint32_t fourccFormat = 777u; - constexpr uint32_t bufferUsageFlags = 888u; - constexpr uint64_t modifier = 999u; - - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 0u, 1u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 1u, 0u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 0u, 0u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 5000u, 1u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 1u, 5000u, fourccFormat, bufferUsageFlags, modifier)); - EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 5000u, 5000u, fourccFormat, bufferUsageFlags, modifier));} - -/* -* Stream buffers -*/ -TEST_F(ARamsesRendererWithDisplay, createsCommandForStreamBufferCreate) -{ - constexpr ramses::waylandIviSurfaceId_t source{ 123u }; - const auto streamBuffer = renderer.createStreamBuffer(displayId, source); - EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(ramses_internal::StreamBufferHandle{ streamBuffer.getValue() }, ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::WaylandIviSurfaceId{ source.getValue() })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForStreamBufferDestroy) -{ - constexpr ramses::streamBufferId_t streamBuffer{ 123u }; - EXPECT_EQ(ramses::StatusOK, renderer.destroyStreamBuffer(displayId, streamBuffer)); - EXPECT_CALL(cmdVisitor, handleBufferDestroyRequest(ramses_internal::StreamBufferHandle{ streamBuffer.getValue() }, ramses_internal::DisplayHandle{ displayId.getValue() })); - cmdVisitor.visit(commandBuffer); -} - -/* -* External buffers -*/ -TEST_F(ARamsesRendererWithDisplay, createsCommandForExternalBufferCreate) -{ - const ramses::externalBufferId_t externalBuffer = renderer.createExternalBuffer(displayId); - EXPECT_NE(ramses::externalBufferId_t::Invalid(), externalBuffer); - EXPECT_CALL(cmdVisitor, handleExternalBufferCreateRequest(ramses_internal::ExternalBufferHandle{ externalBuffer.getValue() }, ramses_internal::DisplayHandle{ displayId.getValue() })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, failsToCreateExternalBufferIfRendererIsRunningInOwnThread) -{ - renderer.startThread(); - - EXPECT_FALSE(renderer.createExternalBuffer(displayId).isValid()); - EXPECT_CALL(cmdVisitor, handleExternalBufferCreateRequest(_, _)).Times(0); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForExternalBufferDestroy) -{ - ramses::externalBufferId_t externalBuffer{ 123u }; - EXPECT_EQ(ramses::StatusOK, renderer.destroyExternalBuffer(displayId, externalBuffer)); - EXPECT_CALL(cmdVisitor, handleExternalBufferDestroyRequest(ramses_internal::ExternalBufferHandle{ externalBuffer.getValue() }, ramses_internal::DisplayHandle{ displayId.getValue() })); - cmdVisitor.visit(commandBuffer); -} - -/* -* Read Pixels -*/ -TEST_F(ARamsesRendererWithDisplay, createsCommandForReadPixels) -{ - ramses::displayBufferId_t bufferId{ 123u }; - EXPECT_EQ(ramses::StatusOK, renderer.readPixels(displayId, bufferId, 1u, 2u, 3u, 4u)); - EXPECT_CALL(cmdVisitor, handleReadPixels(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{ bufferId.getValue() }, 1u, 2u, 3u, 4u, false, std::string_view{})); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, reportsErrorIfReadingPixelsForUnknownDisplay) -{ - EXPECT_NE(ramses::StatusOK, renderer.readPixels(ramses::displayId_t{ 999u }, {}, 1u, 2u, 3u, 4u)); -} - -TEST_F(ARamsesRendererWithDisplay, createsNoCommandForEmptyReadPixels) -{ - EXPECT_NE(ramses::StatusOK, renderer.readPixels(displayId, {}, 0u, 0u, 10u, 0u)); - EXPECT_NE(ramses::StatusOK, renderer.readPixels(displayId, {}, 0u, 0u, 0u, 10u)); - cmdVisitor.visit(commandBuffer); -} - -/* -* SystemCompositorControl -*/ -TEST_F(ARamsesRenderer, createsNoCommandForSystemCompositorControllerIfNotEnabledFromConfig) -{ - EXPECT_NE(ramses::StatusOK, renderer.setSurfaceVisibility(0, true)); - EXPECT_NE(ramses::StatusOK, renderer.setSurfaceOpacity(0, 0.2f)); - EXPECT_NE(ramses::StatusOK, renderer.setSurfaceRectangle(0, 1, 2, 3, 4)); - EXPECT_NE(ramses::StatusOK, renderer.setLayerVisibility(17,true)); - EXPECT_NE(ramses::StatusOK, renderer.takeSystemCompositorScreenshot("unused_name", -1)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetSurfaceVisibility) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setSurfaceVisibility(1, true)); - EXPECT_CALL(cmdVisitor, systemCompositorSetIviSurfaceVisibility(ramses_internal::WaylandIviSurfaceId{ 1u }, true)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetSurfaceOpacity) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setSurfaceOpacity(1, 0.2f)); - EXPECT_CALL(cmdVisitor, systemCompositorSetIviSurfaceOpacity(ramses_internal::WaylandIviSurfaceId{ 1u }, 0.2f)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetSurfaceRectangle) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setSurfaceRectangle(1, 2, 3, 4, 5)); - EXPECT_CALL(cmdVisitor, systemCompositorSetIviSurfaceDestRectangle(ramses_internal::WaylandIviSurfaceId{ 1u }, 2, 3, 4, 5)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerAddSurfaceToLayer) -{ - EXPECT_EQ(ramses::StatusOK, renderer.m_impl.systemCompositorAddIviSurfaceToIviLayer(1, 2)); - EXPECT_CALL(cmdVisitor, systemCompositorAddIviSurfaceToIviLayer(ramses_internal::WaylandIviSurfaceId{ 1u }, ramses_internal::WaylandIviLayerId{ 2u })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetLayerVisibility) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setLayerVisibility(17, true)); - EXPECT_CALL(cmdVisitor, systemCompositorSetIviLayerVisibility(ramses_internal::WaylandIviLayerId{ 17u }, true)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerTakeScreenshot) -{ - EXPECT_EQ(ramses::StatusOK, renderer.takeSystemCompositorScreenshot("name", -1)); - EXPECT_CALL(cmdVisitor, systemCompositorScreenshot(std::string_view{"name"}, -1)); - cmdVisitor.visit(commandBuffer); -} - -/* - * Threading and thread sanitizer tests - */ -class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty -{ -public: - explicit SceneStateEventHandler(ramses::RamsesRenderer& renderer) - : m_renderer(renderer) - { - } - - void displayCreated(ramses::displayId_t /*displayId*/, ramses::ERendererEventResult result) override - { - m_displayCreated = (result == ramses::ERendererEventResult::Ok); - } - - void waitForDisplayCreationEvent() - { - while (!m_displayCreated) - { - m_renderer.dispatchEvents(*this); - std::this_thread::sleep_for(std::chrono::milliseconds{ 10 }); - } - } - -private: - ramses::RamsesRenderer& m_renderer; - bool m_displayCreated = false; -}; - -TEST_F(ARamsesRenderer, canRunRendererInItsOwnThread) -{ - addDisplay(); - renderer.flush(); - EXPECT_EQ(ramses::StatusOK, renderer.startThread()); - SceneStateEventHandler eventHandler(renderer); - eventHandler.waitForDisplayCreationEvent(); - EXPECT_EQ(ramses::StatusOK, renderer.stopThread()); -} - -TEST_F(ARamsesRenderer, canRunRendererInItsOwnThreadAndCallAPIMethods) -{ - const ramses::displayId_t displayId = addDisplay(); - renderer.flush(); - EXPECT_EQ(ramses::StatusOK, renderer.startThread()); - SceneStateEventHandler eventHandler(renderer); - eventHandler.waitForDisplayCreationEvent(); - - // most of these will fail but the purpose is to create and submit renderer commands for renderer running in another thread - // thread sanitizer or other analyzer would catch race conditions when running this test - renderer.getSceneControlAPI()->handlePickEvent(ramses::sceneId_t(0u), 1u, 2u); - renderer.flush(); - - renderer.readPixels(displayId, {}, 1u, 2u, 3u, 4u); - const auto ob = renderer.createOffscreenBuffer(displayId, 1u, 1u, 4u); - renderer.destroyOffscreenBuffer(displayId, ob); - renderer.flush(); - - renderer.setSurfaceVisibility(0u, true); - renderer.setSurfaceOpacity(0u, 1.0f); - renderer.setSurfaceRectangle(0u, 0, 0, 0, 0); - renderer.takeSystemCompositorScreenshot("", -1); - renderer.setFrameTimerLimits(10001u, 10000u, 10000u); - renderer.setLayerVisibility(0u, true); - renderer.flush(); - - renderer.dispatchEvents(eventHandler); - EXPECT_EQ(ramses::StatusOK, renderer.stopThread()); -} - -TEST_F(ARamsesRenderer, createsThreadedDisplayIfThreadStartedRightAfterCreation) -{ - // Confidence test for a pre-existing race between starting threaded renderer and command dispatcher - // processing createDisplay command before display thread started - ending up in non-threaded display creation. - // 1. generate createDisplay cmd + flush - // 2. start 'general renderer' thread - // a. threaded mode for displays is enabled - // b. start dispatcher thread and process command - // c. display is created in threaded mode - // If a) would come last then display would be created non-threaded and won't be updated (and dispatcher asserts). - // Current implementation makes sure this does not happen, this test is an attempt (cannot be reliably reproduced) to catch regression. - - EXPECT_TRUE(renderer.createDisplay({}).isValid()); - EXPECT_EQ(ramses::StatusOK, renderer.flush()); - EXPECT_EQ(ramses::StatusOK, renderer.startThread()); - EXPECT_EQ(ramses::StatusOK, renderer.stopThread()); -} - -TEST(ARamsesRendererWithSeparateRendererThread, canNotifyPerWatchdog) -{ - ramses_internal::PlatformLock expectLock; - SafeThreadWatchdogNotificationMock notificationMock(expectLock); - - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - EXPECT_EQ(ramses::StatusOK, frameworkConfig.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Renderer, 500)); - EXPECT_EQ(ramses::StatusOK, frameworkConfig.setWatchdogNotificationCallBack(¬ificationMock)); - { - ramses_internal::PlatformGuard g(expectLock); - EXPECT_CALL(notificationMock, safe_registerThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); - EXPECT_CALL(notificationMock, safe_notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(AtLeast(1)); - } - ramses::RamsesFramework framework(frameworkConfig); - - { - ramses_internal::PlatformGuard g(expectLock); - EXPECT_CALL(notificationMock, safe_registerThread(ramses::ERamsesThreadIdentifier::Renderer)).Times(1); - } - - // syncWaiter must outlive renderer because renderer calls syncWaiter - // via mock until its dtor has run (only then thread gets really stopped!) - ramses_internal::PlatformEvent syncWaiter; - ramses::RamsesRenderer& renderer(*framework.createRenderer(ramses::RendererConfig())); - { - ramses_internal::PlatformGuard g(expectLock); - EXPECT_CALL(notificationMock, safe_notifyThread(ramses::ERamsesThreadIdentifier::Renderer)) - .Times(AtLeast(1)) - .WillRepeatedly(InvokeWithoutArgs([&]() { syncWaiter.signal(); })); - } - EXPECT_EQ(ramses::StatusOK, renderer.startThread()); - - EXPECT_TRUE(syncWaiter.wait(60000)); - - { - ramses_internal::PlatformGuard g(expectLock); - EXPECT_CALL(notificationMock, safe_unregisterThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); - EXPECT_CALL(notificationMock, safe_unregisterThread(ramses::ERamsesThreadIdentifier::Renderer)).Times(1); - } - EXPECT_EQ(ramses::StatusOK, renderer.stopThread()); - framework.destroyRenderer(renderer); -} - -class RenderThreadLoopTimingsNotification final : public ramses::RendererEventHandlerEmpty -{ -public: - void renderThreadLoopTimings(ramses::displayId_t displayId, std::chrono::microseconds, std::chrono::microseconds) override - { - m_timeReports[displayId]++; - } - - bool displaysReported(std::initializer_list displays, size_t minCount = 2u) - { - return m_timeReports.size() == displays.size() - && std::all_of(displays.begin(), displays.end(), [&](const auto d) { return m_timeReports[d] >= minCount; }); - } - -private: - std::unordered_map m_timeReports; -}; - -TEST(ARamsesRendererNonThreaded, reportsFrameTimings) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework{frameworkConfig}; - ramses::RendererConfig rConfig; - rConfig.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds{ 50 }); - ramses::RamsesRenderer& renderer(*CreateRenderer(framework, rConfig)); - - const auto display1 = renderer.createDisplay({}); - const auto display2 = renderer.createDisplay({}); - renderer.flush(); - - // wait for either 60 seconds or until event was received - RenderThreadLoopTimingsNotification eventHandler; - const auto startTS = std::chrono::steady_clock::now(); - while (!eventHandler.displaysReported({ display1, display2 }) - && std::chrono::steady_clock::now() - startTS < std::chrono::minutes{ 1 }) - { - renderer.doOneLoop(); - renderer.dispatchEvents(eventHandler); - } - EXPECT_TRUE(eventHandler.displaysReported({ display1, display2 })); - - framework.destroyRenderer(renderer); -} - -TEST(ARamsesRendererWithSeparateRendererThread, reportsFrameTimings) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework{frameworkConfig}; - ramses::RendererConfig rConfig; - rConfig.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds{ 50 }); - ramses::RamsesRenderer& renderer(*CreateRenderer(framework, rConfig)); - - const auto display1 = renderer.createDisplay({}); - const auto display2 = renderer.createDisplay({}); - renderer.flush(); - - EXPECT_EQ(ramses::StatusOK, renderer.startThread()); - - // wait for either 60 seconds or until event was received - RenderThreadLoopTimingsNotification eventHandler; - const auto startTS = std::chrono::steady_clock::now(); - while (!eventHandler.displaysReported({ display1, display2 }) - && std::chrono::steady_clock::now() - startTS < std::chrono::minutes{ 1 }) - { - renderer.dispatchEvents(eventHandler); - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); - } - EXPECT_TRUE(eventHandler.displaysReported({ display1, display2 })); - - EXPECT_EQ(ramses::StatusOK, renderer.stopThread()); - framework.destroyRenderer(renderer); -} - -TEST(ARamsesRendererWithSeparateRendererThread, willNotReportsFrameTimingsIfDisabled) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework{frameworkConfig}; - ramses::RendererConfig rConfig; - rConfig.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds{ 0 }); - ramses::RamsesRenderer& renderer(*CreateRenderer(framework, rConfig)); - - const auto display1 = renderer.createDisplay({}); - renderer.flush(); - - EXPECT_EQ(ramses::StatusOK, renderer.startThread()); - - RenderThreadLoopTimingsNotification eventHandler; - const auto startTS = std::chrono::steady_clock::now(); - while (!eventHandler.displaysReported({ display1 }) && std::chrono::steady_clock::now() - startTS < std::chrono::seconds{ 5 }) - { - renderer.dispatchEvents(eventHandler); - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); - } - EXPECT_FALSE(eventHandler.displaysReported({ display1 })); - - EXPECT_EQ(ramses::StatusOK, renderer.stopThread()); - framework.destroyRenderer(renderer); -} - -TEST(ARamsesRendererWithSeparateRendererThread, TSAN_periodicallyDispatchEvents) -{ - // this test is meant for TSAN - // - run renderer with display in thread with 30fps - // - keep dispatching events with higher frequency - - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework{frameworkConfig}; - ramses::RamsesRenderer& renderer(*CreateRenderer(framework, {})); - ramses::RamsesClient& client(*framework.createClient({})); - framework.connect(); - - auto scene = client.createScene(ramses::sceneId_t{ 321u }); - scene->createNode(); - scene->flush(); - scene->publish(ramses::EScenePublicationMode::LocalOnly); - - renderer.setFrameTimerLimits(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); - renderer.setLoopMode(ramses::ELoopMode::UpdateOnly); - const auto displayId = renderer.createDisplay({}); - renderer.setFramerateLimit(displayId, 30.f); - renderer.flush(); - - renderer.startThread(); - - ramses::RendererEventHandlerEmpty dummyHandler; - ramses::RendererSceneControlEventHandlerEmpty dummyHandler2; - const auto startTS = std::chrono::steady_clock::now(); - while (std::chrono::steady_clock::now() - startTS < std::chrono::seconds{ 5 }) - { - renderer.dispatchEvents(dummyHandler); - renderer.getSceneControlAPI()->dispatchEvents(dummyHandler2); - std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); - } - - renderer.stopThread(); - framework.disconnect(); - framework.destroyRenderer(renderer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingFrameTimerLimits) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setFrameTimerLimits(10001u, 10002u, 10003u)); - EXPECT_CALL(cmdVisitor, setLimitsFrameBudgets(10001u, 10002u, 10003u)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingSkippingUnmodifiedBuffersFeature) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setSkippingOfUnmodifiedBuffers(true)); - EXPECT_CALL(cmdVisitor, setSkippingOfUnmodifiedBuffers(true)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearFlags_FB) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setDisplayBufferClearFlags(displayId, renderer.getDisplayFramebuffer(displayId), ramses::EClearFlags_Color)); - EXPECT_CALL(cmdVisitor, handleSetClearFlags(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{}, ramses::EClearFlags_Color)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearFlags_FBImplicitlyUsingInvalidDisplayBuffer) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setDisplayBufferClearFlags(displayId, ramses::displayBufferId_t::Invalid(), ramses::EClearFlags_Color)); - EXPECT_CALL(cmdVisitor, handleSetClearFlags(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{}, ramses::EClearFlags_Color)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearFlags_OB) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setDisplayBufferClearFlags(displayId, ramses::displayBufferId_t{ 666u }, ramses::EClearFlags_Color)); - EXPECT_CALL(cmdVisitor, handleSetClearFlags(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{ 666u }, ramses::EClearFlags_Color)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, reportsErrorIfSettingClearFlagsForUnknownDisplay) -{ - EXPECT_NE(ramses::StatusOK, renderer.setDisplayBufferClearFlags(ramses::displayId_t{ 999u }, {}, ramses::EClearFlags_Color)); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearColor_FB) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setDisplayBufferClearColor(displayId, renderer.getDisplayFramebuffer(displayId), {1, 2, 3, 4})); - EXPECT_CALL(cmdVisitor, handleSetClearColor(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{}, glm::vec4{ 1, 2, 3, 4 })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearColor_FBImplicitlyUsingInvalidDisplayBuffer) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setDisplayBufferClearColor(displayId, ramses::displayBufferId_t::Invalid(), {1, 2, 3, 4})); - EXPECT_CALL(cmdVisitor, handleSetClearColor(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{}, glm::vec4{ 1, 2, 3, 4 })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearColor_OB) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setDisplayBufferClearColor(displayId, ramses::displayBufferId_t{666u}, {1, 2, 3, 4})); - EXPECT_CALL(cmdVisitor, handleSetClearColor(ramses_internal::DisplayHandle{ displayId.getValue() }, ramses_internal::OffscreenBufferHandle{ 666u }, glm::vec4{ 1, 2, 3, 4 })); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, reportsErrorIfSettingClearColorForUnknownDisplay) -{ - EXPECT_NE(ramses::StatusOK, renderer.setDisplayBufferClearColor(ramses::displayId_t{999u}, {}, {1, 2, 3, 4})); -} - -TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingExternallyOwnedWindowSize) -{ - EXPECT_EQ(ramses::StatusOK, renderer.setExternallyOwnedWindowSize(displayId, 123u, 456u)); - EXPECT_CALL(cmdVisitor, handleSetExternallyOwnedWindowSize(ramses_internal::DisplayHandle{ displayId.getValue() }, 123u, 456u)); - cmdVisitor.visit(commandBuffer); -} - -TEST_F(ARamsesRendererWithDisplay, reportsErrorIfSettingExternallyOwnedWindowSizeForUnknownDisplay) -{ - EXPECT_NE(ramses::StatusOK, renderer.setExternallyOwnedWindowSize({}, 123u, 456u)); -} - -TEST(ARamsesFrameworkInARendererLib, canCreateARenderer) -{ - ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework fw{fwConfig}; - - auto renderer = fw.createRenderer(ramses::RendererConfig()); - EXPECT_NE(nullptr, renderer); -} - -TEST(ARamsesFrameworkInARendererLib, canNotCreateMultipleRenderer) -{ - ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework fw{fwConfig}; - - auto renderer1 = fw.createRenderer(ramses::RendererConfig()); - auto renderer2 = fw.createRenderer(ramses::RendererConfig()); - EXPECT_NE(nullptr, renderer1); - EXPECT_EQ(nullptr, renderer2); -} - -TEST(ARamsesFrameworkInARendererLib, acceptsLocallyCreatedRendererForDestruction) -{ - ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework fw{fwConfig}; - - auto renderer = fw.createRenderer(ramses::RendererConfig()); - EXPECT_EQ(ramses::StatusOK, fw.destroyRenderer(*renderer)); -} - -TEST(ARamsesFrameworkInARendererLib, doesNotAcceptForeignCreatedRendererForDestruction) -{ - ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework fw1{fwConfig}; - ramses::RamsesFramework fw2{fwConfig}; - - auto renderer1 = fw1.createRenderer(ramses::RendererConfig()); - auto renderer2 = fw2.createRenderer(ramses::RendererConfig()); - EXPECT_NE(ramses::StatusOK, fw2.destroyRenderer(*renderer1)); - EXPECT_NE(ramses::StatusOK, fw1.destroyRenderer(*renderer2)); -} - -TEST(ARamsesFrameworkInARendererLib, doesNotAcceptSameRendererTwiceForDestruction) -{ - ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework fw{fwConfig}; - - auto renderer = fw.createRenderer(ramses::RendererConfig()); - EXPECT_EQ(ramses::StatusOK, fw.destroyRenderer(*renderer)); - EXPECT_NE(ramses::StatusOK, fw.destroyRenderer(*renderer)); -} - -TEST(ARamsesFrameworkInARendererLib, canCreateDestroyAndRecreateARenderer) -{ - ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework fw{fwConfig}; - - auto renderer = fw.createRenderer(ramses::RendererConfig()); - EXPECT_NE(nullptr, renderer); - EXPECT_EQ(fw.destroyRenderer(*renderer), ramses::StatusOK); - renderer = fw.createRenderer(ramses::RendererConfig()); - EXPECT_NE(nullptr, renderer); -} - -TEST(ARamsesFrameworkInARendererLib, createRendererFailsWhenConnected) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework{frameworkConfig}; - EXPECT_EQ(ramses::StatusOK, framework.connect()); - EXPECT_EQ(framework.createRenderer(ramses::RendererConfig()), nullptr); -} - -TEST(ARamsesFrameworkInARendererLib, destroyRendererFailsWhenConnected) -{ - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; - ramses::RamsesFramework framework{frameworkConfig}; - auto* renderer = framework.createRenderer(ramses::RendererConfig()); - ASSERT_NE(renderer, nullptr); - EXPECT_EQ(ramses::StatusOK, framework.connect()); - EXPECT_NE(ramses::StatusOK, framework.destroyRenderer(*renderer)); - EXPECT_EQ(ramses::StatusOK, framework.disconnect()); - EXPECT_EQ(ramses::StatusOK, framework.destroyRenderer(*renderer)); -} diff --git a/renderer/ramses-renderer-impl/test/RendererConfigTest.cpp b/renderer/ramses-renderer-impl/test/RendererConfigTest.cpp deleted file mode 100644 index 36206cdd1..000000000 --- a/renderer/ramses-renderer-impl/test/RendererConfigTest.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include -#include "ramses-renderer-api/BinaryShaderCache.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "RendererConfigImpl.h" - -TEST(ARendererConfig, hasDefaultValuesUponConstruction) -{ - const ramses_internal::RendererConfig defaultConfig; - ramses::RendererConfig config; - EXPECT_EQ(nullptr, config.m_impl.get().getBinaryShaderCache()); - - const ramses_internal::RendererConfig& internalConfig = config.m_impl.get().getInternalRendererConfig(); - - EXPECT_EQ(defaultConfig.getFrameCallbackMaxPollTime(), internalConfig.getFrameCallbackMaxPollTime()); - EXPECT_EQ(defaultConfig.getRenderThreadLoopTimingReportingPeriod(), internalConfig.getRenderThreadLoopTimingReportingPeriod()); - EXPECT_EQ(defaultConfig.getSystemCompositorControlEnabled(), internalConfig.getSystemCompositorControlEnabled()); -} - -TEST(ARendererConfig, canEnableSystemCompositor) -{ - ramses::RendererConfig config; - EXPECT_EQ(ramses::StatusOK, config.enableSystemCompositorControl()); - EXPECT_TRUE(config.m_impl.get().getInternalRendererConfig().getSystemCompositorControlEnabled()); -} - -TEST(ARendererConfig, CanBeCopyAndMoveConstructed) -{ - ramses::RendererConfig config; - config.setRenderThreadLoopTimingReportingPeriod(std::chrono::seconds{ 1 }); - - ramses::RendererConfig configCopy{ config }; - EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); - - ramses::RendererConfig configMove{ std::move(config) }; - EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); -} - -TEST(ARendererConfig, CanBeCopyAndMoveAssigned) -{ - ramses::RendererConfig config; - config.setRenderThreadLoopTimingReportingPeriod(std::chrono::seconds{ 1 }); - - ramses::RendererConfig configCopy; - configCopy = config; - EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); - - ramses::RendererConfig configMove; - configMove = std::move(config); - EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); -} - -TEST(ARendererConfig, CanBeSelfAssigned) -{ - ramses::RendererConfig config; - config.setRenderThreadLoopTimingReportingPeriod(std::chrono::seconds{ 1 }); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-move" -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - config = config; - EXPECT_EQ(std::chrono::seconds{ 1 }, config.getRenderThreadLoopTimingReportingPeriod()); - config = std::move(config); - // NOLINTNEXTLINE(bugprone-use-after-move) - EXPECT_EQ(std::chrono::seconds{ 1 }, config.getRenderThreadLoopTimingReportingPeriod()); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -} - -TEST(ARendererConfig, canSetBinaryShaderCache) -{ - ramses::RendererConfig config; - - ramses::BinaryShaderCache cache; - config.setBinaryShaderCache(cache); - EXPECT_EQ(&cache, config.m_impl.get().getBinaryShaderCache()); -} - -TEST(ARendererConfig, defaultRendererConfigValidates) -{ - ramses::RendererConfig config; - EXPECT_EQ(ramses::StatusOK, config.validate()); -} - -TEST(ARendererConfig, setsAndGetsWaylandDisplay) -{ - ramses::RendererConfig config; - EXPECT_EQ(ramses::StatusOK, config.setSystemCompositorWaylandDisplay("xxx")); - EXPECT_EQ("xxx", config.getSystemCompositorWaylandDisplay()); - EXPECT_EQ("xxx", config.m_impl.get().getInternalRendererConfig().getWaylandDisplayForSystemCompositorController()); -} - -TEST(ARendererConfig, setsAndGetsLoopCountPeriod) -{ - ramses::RendererConfig config; - EXPECT_EQ(ramses::StatusOK, config.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds(1234))); - EXPECT_EQ(std::chrono::milliseconds(1234), config.getRenderThreadLoopTimingReportingPeriod()); -} - diff --git a/renderer/ramses-renderer-impl/test/RendererEventTestHandler.h b/renderer/ramses-renderer-impl/test/RendererEventTestHandler.h deleted file mode 100644 index 021338e95..000000000 --- a/renderer/ramses-renderer-impl/test/RendererEventTestHandler.h +++ /dev/null @@ -1,360 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#ifndef RAMSES_RENDEREREVENTTESTHANDLER_H -#define RAMSES_RENDEREREVENTTESTHANDLER_H - -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "Collections/Vector.h" -#include - -enum ERendererEventTestType -{ - ERendererEventTestType_Undefined = 0, - - ERendererEventTestType_OffscreenBufferCreated, - ERendererEventTestType_OffscreenBufferDestroyed, - ERendererEventTestType_PixelsRead, - ERendererEventTestType_DisplayCreated, - ERendererEventTestType_DisplayDestroyed, - ERendererEventTestType_WindowClosed, - ERendererEventTestType_WindowResized, - ERendererEventTestType_WindowMoved, - ERendererEventTestType_KeyEvent, - ERendererEventTestType_MouseEvent, - ERendererEventTestType_RenderThreadPeriodicLoopTimes, - ERendererEventTestType_ExternalBufferCreated, - ERendererEventTestType_ExternalBufferDestroyed, -}; - -struct RendererTestEvent -{ - RendererTestEvent() - { - } - - bool operator==(const RendererTestEvent& other) const - { - return eventType == other.eventType - && result == other.result - && sceneId == other.sceneId - && displayId == other.displayId - && bufferId == other.bufferId - && keyEvent == other.keyEvent - && keyModifiers == other.keyModifiers - && keyCode == other.keyCode - && mouseEvent == other.mouseEvent - && mousePosX == other.mousePosX - && mousePosY == other.mousePosY - && windowWidth == other.windowWidth - && windowHeight == other.windowHeight - && externalBufferId == other.externalBufferId - && externalBufferTextureGlId == other.externalBufferTextureGlId; - } - - ERendererEventTestType eventType = ERendererEventTestType_Undefined; - ramses::ERendererEventResult result = ramses::ERendererEventResult::Failed; - - ramses::sceneId_t sceneId { 0u }; - ramses::displayId_t displayId; - ramses::displayBufferId_t bufferId; - - ramses::EKeyEvent keyEvent = ramses::EKeyEvent::Invalid; - uint32_t keyModifiers = 0u; - ramses::EKeyCode keyCode = ramses::EKeyCode_Unknown; - - ramses::EMouseEvent mouseEvent = ramses::EMouseEvent::Invalid; - int32_t mousePosX = 0; - int32_t mousePosY = 0; - uint32_t windowWidth = 0u; - uint32_t windowHeight = 0u; - int32_t windowPosX = 0; - int32_t windowPosY = 0; - - std::chrono::microseconds renderthread_maximumLoopTime{0}; - std::chrono::microseconds renderthread_avg_looptime{0}; - - ramses::externalBufferId_t externalBufferId; - uint32_t externalBufferTextureGlId { 0u }; -}; - -class RendererEventTestHandler : public ramses::IRendererEventHandler -{ -public: - ~RendererEventTestHandler() override - { - EXPECT_TRUE(m_events.empty()); - } - - void offscreenBufferCreated(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_OffscreenBufferCreated; - event.displayId = displayId; - event.bufferId = offscreenBufferId; - event.result = result; - m_events.push_back(event); - } - - void offscreenBufferDestroyed(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_OffscreenBufferDestroyed; - event.displayId = displayId; - event.bufferId = offscreenBufferId; - event.result = result; - m_events.push_back(event); - } - - void framebufferPixelsRead(const uint8_t* /*pixelData*/, const uint32_t /*pixelDataSize*/, ramses::displayId_t displayId, ramses::displayBufferId_t displayBufferId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_PixelsRead; - event.result = result; - event.displayId = displayId; - event.bufferId = displayBufferId; - m_events.push_back(event); - } - - void displayCreated(ramses::displayId_t displayId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_DisplayCreated; - event.result = result; - event.displayId = displayId; - m_events.push_back(event); - } - - void displayDestroyed(ramses::displayId_t displayId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_DisplayDestroyed; - event.result = result; - event.displayId = displayId; - m_events.push_back(event); - } - - void windowClosed(ramses::displayId_t displayId) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_WindowClosed; - event.displayId = displayId; - m_events.push_back(event); - } - - void windowResized(ramses::displayId_t displayId, uint32_t width, uint32_t height) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_WindowResized; - event.displayId = displayId; - event.windowWidth = width; - event.windowHeight = height; - m_events.push_back(event); - } - - void windowMoved(ramses::displayId_t displayId, int32_t posX, int32_t posY) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_WindowMoved; - event.displayId = displayId; - event.windowPosX = posX; - event.windowPosY = posY; - m_events.push_back(event); - } - - void keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent keyEvent, uint32_t keyModifiers, ramses::EKeyCode keyCode) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_KeyEvent; - event.displayId = displayId; - event.keyEvent = keyEvent; - event.keyModifiers = keyModifiers; - event.keyCode = keyCode; - m_events.push_back(event); - } - - void mouseEvent(ramses::displayId_t displayId, ramses::EMouseEvent mouseEvent, int32_t mousePosX, int32_t mousePosY) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_MouseEvent; - event.mouseEvent = mouseEvent; - event.displayId = displayId; - event.mousePosX = mousePosX; - event.mousePosY = mousePosY; - m_events.push_back(event); - } - - void externalBufferCreated(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, uint32_t textureGlId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_ExternalBufferCreated; - event.displayId = displayId; - event.externalBufferId = externalBufferId; - event.externalBufferTextureGlId = textureGlId; - event.result = result; - m_events.push_back(event); - } - - void externalBufferDestroyed(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, ramses::ERendererEventResult result) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_ExternalBufferDestroyed; - event.displayId = displayId; - event.externalBufferId = externalBufferId; - event.result = result; - m_events.push_back(event); - } - - void expectOffscreenBufferCreated(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_OffscreenBufferCreated; - event.displayId = displayId; - event.bufferId = offscreenBufferId; - event.result = result; - expectEvent(event); - } - - void expectOffscreenBufferDestroyed(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_OffscreenBufferDestroyed; - event.displayId = displayId; - event.bufferId = offscreenBufferId; - event.result = result; - expectEvent(event); - } - - void expectExternalBufferCreated(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, uint32_t textureGlId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_ExternalBufferCreated; - event.displayId = displayId; - event.externalBufferId = externalBufferId; - event.externalBufferTextureGlId = textureGlId; - event.result = result; - expectEvent(event); - } - - void expectExternalBufferDestroyed(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_ExternalBufferDestroyed; - event.displayId = displayId; - event.externalBufferId = externalBufferId; - event.result = result; - expectEvent(event); - } - - void expectFramebufferPixelsRead(const uint8_t* /*pixelData*/, const uint32_t /*pixelDataSize*/, ramses::displayId_t displayId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_PixelsRead; - event.result = result; - event.displayId = displayId; - expectEvent(event); - } - - void expectDisplayCreated(ramses::displayId_t displayId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_DisplayCreated; - event.result = result; - event.displayId = displayId; - expectEvent(event); - } - - void expectDisplayDestroyed(ramses::displayId_t displayId, ramses::ERendererEventResult result) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_DisplayDestroyed; - event.result = result; - event.displayId = displayId; - expectEvent(event); - } - - void expectWindowClosed(ramses::displayId_t displayId) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_WindowClosed; - event.displayId = displayId; - expectEvent(event); - } - - void expectWindowResized(ramses::displayId_t displayId, uint32_t width, uint32_t height) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_WindowResized; - event.displayId = displayId; - event.windowWidth = width; - event.windowHeight = height; - expectEvent(event); - } - - void expectWindowMoved(ramses::displayId_t displayId, int32_t posX, int32_t posY) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_WindowMoved; - event.displayId = displayId; - event.windowPosX = posX; - event.windowPosY = posY; - expectEvent(event); - } - - void expectKeyEvent(ramses::displayId_t displayId, ramses::EKeyEvent keyEvent, uint32_t keyModifiers, ramses::EKeyCode keyCode) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_KeyEvent; - event.displayId = displayId; - event.keyEvent = keyEvent; - event.keyModifiers = keyModifiers; - event.keyCode = keyCode; - expectEvent(event); - } - - void expectMouseEvent(ramses::displayId_t displayId, ramses::EMouseEvent mouseEvent, int32_t mousePosX, int32_t mousePosY) - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_MouseEvent; - event.displayId = displayId; - event.mouseEvent = mouseEvent; - event.mousePosX = mousePosX; - event.mousePosY = mousePosY; - expectEvent(event); - } - - void renderThreadLoopTimings(ramses::displayId_t displayId, std::chrono::microseconds maximumLoopTimeMilliseconds, std::chrono::microseconds averageLooptimeMilliseconds) override - { - RendererTestEvent event; - event.eventType = ERendererEventTestType_RenderThreadPeriodicLoopTimes; - event.displayId = displayId; - event.renderthread_maximumLoopTime = maximumLoopTimeMilliseconds; - event.renderthread_avg_looptime = averageLooptimeMilliseconds; - expectEvent(event); - } - - void expectNoEvent() - { - EXPECT_TRUE(m_events.empty()); - } - -private: - void expectEvent(const RendererTestEvent& event, const size_t withinLast = 1u) - { - auto it = std::find(m_events.begin(), m_events.begin() + std::min(m_events.size(), withinLast), event); - EXPECT_TRUE(it != m_events.end()) << "expected event " << event.eventType << " not emitted"; - - if (it != m_events.end()) - m_events.erase(it); - } - - std::vector m_events; -}; - -#endif diff --git a/renderer/ramses-renderer-impl/test/RendererMateTest.cpp b/renderer/ramses-renderer-impl/test/RendererMateTest.cpp deleted file mode 100644 index 677fad597..000000000 --- a/renderer/ramses-renderer-impl/test/RendererMateTest.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// ------------------------------------------------------------------------- -// Copyright (C) 2014 BMW Car IT GmbH -// ------------------------------------------------------------------------- -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -// ------------------------------------------------------------------------- - -#include "gmock/gmock.h" -#include "RendererMate.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "RendererLib/RendererCommands.h" -#include "RamsesRendererImpl.h" -#include "Utils/ThreadBarrier.h" -#include "RendererCommandVisitorMock.h" -#include "PlatformFactoryMock.h" - -using namespace ramses_internal; -using namespace testing; - -class ARendererMate : public Test -{ -public: - ARendererMate() - : renderer(*ramsesFramework.createRenderer(ramses::RendererConfig())) - , rendererMate(renderer.m_impl, ramsesFramework.m_impl) - , rendererMateRendererEventHandler(rendererMate) - , rendererMateEventHandler(rendererMate) - { - renderer.m_impl.getDisplayDispatcher().injectPlatformFactory(std::make_unique()); - - displayId = renderer.createDisplay({}); - rendererMate.setSceneMapping(sceneId, displayId); - StrictMock visitor; - EXPECT_CALL(visitor, createDisplayContext(_, _, _)); - visitor.visit(renderer.m_impl.getPendingCommands()); - clearCommands(); - } - -protected: - void expectLogConfirmationCommand() - { - StrictMock visitor; - EXPECT_CALL(visitor, handleConfirmationEcho(DisplayHandle{ displayId.getValue() }, _)); - visitor.visit(renderer.m_impl.getPendingCommands()); - clearCommands(); - } - - void clearCommands() - { - const_cast(renderer.m_impl.getPendingCommands()).clear(); - } - - void publishAndExpectToGetToState(ramses::RendererSceneState state, bool expectConfirmation = false) - { - assert(state != ramses::RendererSceneState::Available); - EXPECT_FALSE(isSceneShown()); - - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Available); - if (state != ramses::RendererSceneState::Unavailable) - rendererMateEventHandler.sceneStateChanged(sceneId, state); - - EXPECT_EQ(state, rendererMate.getLastReportedSceneState(sceneId)); - if (expectConfirmation) - expectLogConfirmationCommand(); - clearCommands(); - } - - [[nodiscard]] bool isSceneShown() const - { - return rendererMate.getLastReportedSceneState(sceneId) == ramses::RendererSceneState::Rendered; - } - -private: - ramses::RamsesFramework ramsesFramework{ ramses::RamsesFrameworkConfig{ramses::EFeatureLevel_Latest} }; - ramses::RamsesRenderer& renderer; // restrict access to renderer, acts only as dummy for RendererMate and to create (dummy) displays - -protected: - ramses::RendererMate rendererMate; - ramses::IRendererEventHandler& rendererMateRendererEventHandler; - ramses::IRendererSceneControlEventHandler& rendererMateEventHandler; - - const ramses::sceneId_t sceneId{ 33 }; - ramses::displayId_t displayId; - const ramses::displayBufferId_t offscreenBufferId{ 2 }; -}; - -TEST_F(ARendererMate, willShowSceneWhenPublished) -{ - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered); - publishAndExpectToGetToState(ramses::RendererSceneState::Rendered); -} - -TEST_F(ARendererMate, willShowSceneWhenPublishedAndLogsConfirmation) -{ - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, "dummy msg"); - publishAndExpectToGetToState(ramses::RendererSceneState::Rendered, true); -} - -TEST_F(ARendererMate, willLogConfirmationEvenIfSceneAlreadyRendered) -{ - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, ""); // no confirmation - publishAndExpectToGetToState(ramses::RendererSceneState::Rendered); - - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, "confirmation"); - expectLogConfirmationCommand(); -} - -TEST_F(ARendererMate, reportsCorrectSceneShowState) -{ - // show first time - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered); - publishAndExpectToGetToState(ramses::RendererSceneState::Rendered); - - // explicitly hide - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Ready); - EXPECT_TRUE(isSceneShown()); // hide command not processed yet - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Ready); - EXPECT_FALSE(isSceneShown()); - - // show again - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered); - EXPECT_FALSE(isSceneShown()); // show command not processed yet - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Rendered); - EXPECT_TRUE(isSceneShown()); - - // unexpected unpublish!! - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Unavailable); - EXPECT_FALSE(isSceneShown()); -} - -TEST_F(ARendererMate, continuesToShowSceneAndLogsConfirmationAfterReconnect) -{ - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Available); - rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, "scene is shown now"); - - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Available); - - // simulate disconnect, renderer framework sends unpublish for all its scenes - rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Unavailable); - - publishAndExpectToGetToState(ramses::RendererSceneState::Rendered, true); // expect confirmation -} - -TEST_F(ARendererMate, canBeCalledFromAnotherThread) -{ - // this test simulates 2 threads executing commands (e.g. ramsh commands) - // and a thread dispatching and checking some states (e.g. standalone renderer) - - ThreadBarrier initBarrier(3); - ThreadBarrier finishedBarrier(3); - - constexpr ramses::sceneId_t sceneId1{ 10123 }; - constexpr ramses::sceneId_t sceneId2{ 10124 }; - rendererMateEventHandler.sceneStateChanged(sceneId1, ramses::RendererSceneState::Available); - rendererMateEventHandler.sceneStateChanged(sceneId2, ramses::RendererSceneState::Available); - - auto executeMethods = [&](ramses::sceneId_t sId) - { - initBarrier.wait(); - EXPECT_TRUE(rendererMate.setSceneState(sId, ramses::RendererSceneState::Available)); - EXPECT_TRUE(rendererMate.setSceneMapping(sId, displayId)); - EXPECT_TRUE(rendererMate.setSceneDisplayBufferAssignment(sId, offscreenBufferId)); - EXPECT_TRUE(rendererMate.setSceneState(sId, ramses::RendererSceneState::Ready)); - rendererMate.linkOffscreenBuffer(offscreenBufferId, sId, ramses::dataConsumerId_t{ 123 }); - rendererMate.linkData(sId, ramses::dataProviderId_t{ 123 }, ramses::sceneId_t{ sId.getValue() + 10 }, ramses::dataConsumerId_t{ 123 }); - rendererMate.processConfirmationEchoCommand(displayId, "foo"); - finishedBarrier.wait(); - }; - - std::thread t1(executeMethods, sceneId1); - std::thread t2(executeMethods, sceneId2); - - std::thread t3([&] - { - ramses::RendererSceneControlEventHandlerEmpty handler; - initBarrier.wait(); - for (int i = 0; i < 10; ++i) - { - rendererMate.dispatchAndFlush(handler); - EXPECT_TRUE(rendererMate.isRunning()); - rendererMate.getLastReportedSceneState(sceneId1); - rendererMate.getLastReportedSceneState(sceneId2); - rendererMate.enableKeysHandling(); - } - finishedBarrier.wait(); - }); - - t1.join(); - t2.join(); - t3.join(); -} diff --git a/scripts/ci/build/build.py b/scripts/ci/build/build.py index 9eca87004..440b4d51e 100644 --- a/scripts/ci/build/build.py +++ b/scripts/ci/build/build.py @@ -35,8 +35,8 @@ def cmake_configure(self, flatbuf_gen, android_abi, disable_logic, use_imagemagick, headless, no_full_shared_lib, no_examples, no_demos, no_tests, no_tools, generator, - enable_dlt, enable_lto, test_coverage, enable_coverage, - package_name, cpp_std, cmake_modules): + enable_dlt, enable_lto, test_coverage, enable_coverage, sanitizer_name, + package_name, cpp_std, cmake_modules, enable_luajit): optional_args = [] if self.compiler == 'llvm': @@ -53,7 +53,6 @@ def cmake_configure(self, no_demos = True no_tests = True no_tools = True - optional_args.append('-Dramses-sdk_BUILD_DAEMON=OFF') optional_args.append('-Dramses-sdk_BUILD_IVI_TEST_APPS=OFF') # TODO These should not be needed, if using Gradle instead of CMake optional_args.append('-DANDROID_PLATFORM=21') @@ -68,6 +67,9 @@ def cmake_configure(self, if test_coverage: optional_args.append('-Dramses-sdk_ENABLE_COVERAGE=1') + if sanitizer_name: + optional_args.append(f'-Dramses-sdk_ENABLE_SANITIZER={sanitizer_name}') + if headless: optional_args.append('-Dramses-sdk_BUILD_HEADLESS_SHARED_LIB=1') @@ -90,6 +92,8 @@ def cmake_configure(self, optional_args.append('-Dramses-sdk_BUILD_TOOLS=0') if cmake_modules: optional_args.append(f'-DCMAKE_MODULE_PATH={cmake_modules}') + if enable_luajit: + optional_args.append('-Dramses-sdk_USE_PLATFORM_LUAJIT=1') generator = generator if generator: @@ -121,7 +125,9 @@ def cmake_configure(self, f'-B{self.build_dir}', ] print(f'CWD: {self.build_dir}\nRun:', ' '.join(args)) - subprocess.check_call(args, cwd=self.build_dir) + processResult = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.build_dir) + print(processResult.stdout.decode('utf-8')) + return processResult def cmake_build(self, target): args = [ @@ -165,6 +171,8 @@ def copy_package(self, package_target): @click.option('--enable-lto', is_flag=True, default=False, help='Build with LTO support') @click.option('--test-coverage', is_flag=True, default=False, help='Enable test coverage') @click.option('--enable-coverage', is_flag=True, default=False, help='Enable code coverage') +@click.option('--enable-luajit', is_flag=True, default=False, help='Enable system-provided luajit') +@click.option('--sanitizer-name', help='Enables provided sanitizers') @click.option('--package-name', default="", help='Use a different package name for CPack than the default') @click.option('--package-destination', type=click.Path(exists=True, file_okay=False), help='Specify a folder where the package shall be copied') @click.option('--cpp-std', type=click.Choice(CPP_STANDARDS), default=CPP_STANDARDS[0]) @@ -173,10 +181,10 @@ def copy_package(self, package_target): @click.option('--cmake-modules', help='Sets cmake module path') def build(compiler, config, build_dir, configure_only, build_target, package_destination, **kwargs): conf = BuildConfig(compiler, config, build_dir) - conf.cmake_configure(**kwargs) + cmake_process_result = conf.cmake_configure(**kwargs) if configure_only: - return + return cmake_process_result conf.cmake_build(build_target) diff --git a/scripts/ci/build/test-cmake-configurations.py b/scripts/ci/build/test-cmake-configurations.py index 5945e6870..32e73bfeb 100755 --- a/scripts/ci/build/test-cmake-configurations.py +++ b/scripts/ci/build/test-cmake-configurations.py @@ -12,30 +12,202 @@ from pathlib import Path import shutil import time +import subprocess +import re import build def test_cmake_configuration(cli_context, build_dir, expect_success, **cmake_options): print(f"Running test :{cmake_options}") - test_dir = Path(build_dir) / "cmake-configuration-tests-build" + test_dir = (Path(build_dir) / "cmake-configuration-tests-build").absolute() if test_dir.is_dir(): shutil.rmtree(test_dir) Path(test_dir).mkdir(parents=True, exist_ok=True) test_failure_msg = None - try: - cli_context.invoke(build.build, build_dir=test_dir, configure_only=True, disable_default_window_type=True, **cmake_options) + process_result = cli_context.invoke(build.build, build_dir=test_dir, configure_only=True, disable_default_window_type=True, **cmake_options) + if process_result.returncode == 0: if not expect_success: test_failure_msg = "Test failed! Death test was supposed to fail cmake configure, but it succeeded!" - - except Exception: - if expect_success: - test_failure_msg = "Test failed! Configuration did not succeed!" + elif expect_success: + test_failure_msg = "Test failed! Configuration did not succeed!" if test_failure_msg: raise Exception(test_failure_msg) + # if configure was expected to fail dont check output of configure + if not expect_success: + return + + # check if output from configure had the expected modules, and didnt have the unexpected modules + process_output = process_result.stdout.decode('utf-8') + + def check_expectations(condition, *expectations): + # generated cmake modules have the pattern: "+ module-name (TARGET_TYPE)", so the asserts downbelow + # check if each of the strings passed in `expectations` exists (or does not exist) preceeded + # by a `+` and followed by a space + if condition: + assert all(f'+ {e} ' in process_output for e in expectations) + else: + assert all(f'+ {e} ' not in process_output for e in expectations) + + # check expected and non-expected output based on logical consequences of each option, and their combination together + headless = "headless" in cmake_options + x11 = "enable_x11" in cmake_options + wayland_ivi = "enable_wayland_ivi" in cmake_options + wayland_shell = "enable_wayland_wl_shell" in cmake_options + tests = "no_tests" not in cmake_options + examples = "no_examples" not in cmake_options + tools = "no_tools" not in cmake_options + logic = "disable_logic" not in cmake_options + use_imagemagick = "use_imagemagick" in cmake_options + luajit = "enable_luajit" in cmake_options + + # renderer is enabled iff any window type is enabled + renderer = x11 or wayland_ivi or wayland_shell + # full shared lib is enabled iff cmake to disable full shared lib not set AND renderer enabled (any window type enabled) + full_shared_lib = renderer and "no_full_shared_lib" not in cmake_options + + # always existing + check_expectations(True, "ramses-client") + + # expect internal lua target unless luajit is configured + check_expectations(logic and not luajit, "lua (internal)") + if luajit: + assert "Found LuaJIT" in process_output + + # shared libs + check_expectations(headless, "ramses-shared-lib-headless") + check_expectations(full_shared_lib, "ramses-shared-lib") + + # renderer + check_expectations(x11, "X11") + check_expectations(wayland_ivi, "Wayland ivi") + check_expectations(wayland_shell, "Wayland wl_shell") + check_expectations(renderer, "ramses-renderer-lib", "ramses-renderer") + + # tools + check_expectations(tools, "ivi-gears", "ivi-simple-dmabuf-egl", "ramses-daemon") + check_expectations(tools and renderer, "ramses-renderer-standalone", "ramses-scene-viewer", "ramses-stream-viewer", "ramses-imgui") + check_expectations(tools and logic and headless, "ramses-logic-viewer-headless") + check_expectations(tools and logic and full_shared_lib, "ramses-logic-viewer", "test-asset-producer") + + # tests + check_expectations(tests, "ramses-framework-test", "ramses-client-test") + check_expectations(tests and renderer, "ramses-renderer-lib-test", "ramses-renderer-test", "rendering-tests", "ramses-test-client") + check_expectations(tests and logic, "ramses-logic-benchmarks") + check_expectations(x11 and tests, "window-x11-test") + check_expectations(wayland_ivi and tests, "window-wayland-ivi-test") + check_expectations(wayland_shell and tests, "window-wayland-wl-shell-test") + + # tool tests + check_expectations(tests and tools and logic, "ramses-logic-viewer-test") + check_expectations(tests and tools and logic and full_shared_lib and use_imagemagick, "ramses-logic-viewer-gui-test") + + # examples + check_expectations(examples and (headless or full_shared_lib), "ramses-example-basic-geometry") + check_expectations(examples and full_shared_lib, "ramses-example-local-client") + check_expectations(examples and (headless or full_shared_lib) and logic, "00_minimal") + check_expectations(examples and full_shared_lib and logic, "13_render_order") + + if tests: + ctestProcessResult = subprocess.run(["ctest", "--show-only"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=test_dir) + ctestStdOutput = ctestProcessResult.stdout.decode('utf-8') + + # step 1: get all tests from ctest output + # Example of test entry from ctest output: Test #13: ramses-client-test_UNITTEST + ctest_test_entry_regex = r"Test(\s+)#(\d+):(\s+)((\w|\-)+)" + ctest_test_entries = [e.group(4) for e in re.finditer(ctest_test_entry_regex, ctestStdOutput)] + + # step 2: get test count from ctest output and assert step 1 was correct + # Example of test count from ctest: Total Tests: 3 + ctest_test_count_regex = r"Total(\s+)Tests:(\s+)(\d+)$" + ctest_test_count = int(re.search(ctest_test_count_regex, ctestStdOutput).group(3)) + + assert ctest_test_count == len(ctest_test_entries) + + # step 3: build list of expected tests based on cmake options + expected_tests = ['ramses-framework-test_UNITTEST', 'ramses-client-test_UNITTEST'] + + if (headless or full_shared_lib) and examples and logic: + expected_tests += [ + '00_minimal_UNITTEST', + '01a_primitive_properties_UNITTEST', + '01b_struct_properties_UNITTEST', + '01c_array_properties_UNITTEST', + '02_errors_compile_time_UNITTEST', + '03_errors_runtime_UNITTEST', + '05_serialization_UNITTEST', + '07_links_UNITTEST', + '09_modules_UNITTEST', + '10_globals_UNITTEST', + '11_interfaces_UNITTEST'] + + if renderer: + expected_tests += [ + 'ramses-renderer-lib-test_UNITTEST', + 'ramses-renderer-test_UNITTEST', + 'ramses-cli-test_UNITTEST'] + + if x11: + expected_tests += [ + 'rendering-tests_x11-gles30_RNDSANDWICHTEST_SWRAST', + 'renderer-lifecycle-tests_x11-gles30_RNDSANDWICHTEST_SWRAST', + 'render-backend-tests_x11-gles30_RNDSANDWICHTEST_SWRAST', + 'window-x11-test_RNDSANDWICHTEST_SWRAST', + + 'rendering-tests_x11-gles30_RNDSANDWICHTEST', + 'render-backend-tests_x11-gles30_RNDSANDWICHTEST', + 'resource-stress-tests_x11-gles30_RNDSANDWICHTEST'] + + if wayland_ivi: + expected_tests += [ + 'rendering-tests_wayland-ivi-gles30_RNDSANDWICHTEST_SWRAST', + 'renderer-lifecycle-tests_wayland-ivi-gles30_RNDSANDWICHTEST_SWRAST', + 'render-backend-tests_wayland-ivi-gles30_RNDSANDWICHTEST_SWRAST', + 'embedded-compositing-rendering-tests_RNDSANDWICHTEST_SWRAST', + 'system-compositor-controller-wayland-test_RNDSANDWICHTEST_SWRAST', + 'embedded-compositor-wayland-test_RNDSANDWICHTEST_SWRAST', + 'window-wayland-ivi-test_RNDSANDWICHTEST_SWRAST', + + 'rendering-tests_wayland-ivi-gles30_RNDSANDWICHTEST_VALGRINDGATE', + 'renderer-lifecycle-tests_wayland-ivi-gles30_RNDSANDWICHTEST_VALGRINDGATE', + 'embedded-compositing-rendering-tests_wayland-ivi-gles30_RNDSANDWICHTEST_VALGRINDGATE', + + 'rendering-tests_wayland-ivi-gles30_RNDSANDWICHTEST', + 'render-backend-tests_wayland-ivi-gles30_RNDSANDWICHTEST', + 'resource-stress-tests_wayland-ivi-gles30_RNDSANDWICHTEST'] + + if wayland_shell: + expected_tests += [ + 'renderer-lifecycle-tests_wayland-wl-shell-gles30_RNDSANDWICHTEST_SWRAST', + 'embedded-compositor-wayland-test_RNDSANDWICHTEST_SWRAST', + 'window-wayland-wl-shell-test_RNDSANDWICHTEST_SWRAST', + + 'rendering-tests_wayland-wl-shell-gles30_RNDSANDWICHTEST', + 'render-backend-tests_wayland-wl-shell-gles30_RNDSANDWICHTEST', + 'resource-stress-tests_wayland-wl-shell-gles30_RNDSANDWICHTEST'] + + if logic and tools: + expected_tests += [ + 'ramses-logic-viewer-test_UNITTEST'] + + # step 4: find missing and/or unexpected tests + unexpected_tests = [t for t in ctest_test_entries if t not in expected_tests] + missing_tests = [t for t in expected_tests if t not in ctest_test_entries] + + test_failure_msg = "" + if len(unexpected_tests) != 0: + test_failure_msg = f"Unexpected tests found : {unexpected_tests}\n" + if len(missing_tests) != 0: + test_failure_msg += f"Missing tests : {missing_tests}\n" + + if test_failure_msg != "": + raise Exception(test_failure_msg) + + assert len(expected_tests) == len(ctest_test_entries) + print("Test succeeded..\n") shutil.rmtree(test_dir) @@ -47,6 +219,10 @@ def main(cli_context, build_dir): print("Running cmake configuration tests...") start_time = time.time() + # can configure with (external) luajit + test_cmake_configuration(cli_context, build_dir, True, headless=True, enable_luajit=True) + test_cmake_configuration(cli_context, build_dir, True, enable_x11=True, enable_luajit=True) + # can not configure if no useful feature is enabled, i.e., user gets a non-usable project without any shared libs or renderer test_cmake_configuration(cli_context, build_dir, False) test_cmake_configuration(cli_context, build_dir, False, no_full_shared_lib=True) diff --git a/scripts/ci/clang-tidy-wrapper.py b/scripts/ci/clang-tidy-wrapper.py index 6f559b4f6..968f01fe1 100755 --- a/scripts/ci/clang-tidy-wrapper.py +++ b/scripts/ci/clang-tidy-wrapper.py @@ -192,6 +192,7 @@ def print_overall_runtime(entries, num_threads, start_time, *, end_time=None): def main(): + print("Run:", " ".join(sys.argv)) parser = argparse.ArgumentParser() parser.add_argument('compdb', help='Full path of compile_commands.json') parser.add_argument('-f', '--filter', default=None, @@ -204,12 +205,18 @@ def main(): help='Path to configuration file') parser.add_argument('-r', '--repo', default=repo.get_sdkroot(), required=False, help='Repository root') + parser.add_argument('--fix', help='Apply clang-tidy fixes.', action='store_true') args = parser.parse_args() start_time = time.monotonic() config = yamlconfig.read_config_with_defaults(args.config, CONFIG_SCHEMA, CONFIG_DEFAULTS) entries = compilationdb.load_from_file(args.compdb, Path(args.repo).resolve()) + extra_args = [] print(f'Loaded {len(entries)} entries from {args.compdb}') + if args.fix: + # Multiple clang-tidy threads will interfer each other + args.threads = 1 + extra_args.append('--fix-errors') entries = process_compdb_entries(entries, config, args.filter) print(f'Check {len(entries)} remaining entries with {args.threads} threads\n') @@ -218,7 +225,7 @@ def main(): with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as executor: result_entries = [] unique_issues = {} - result_futures = map(lambda e: executor.submit(clangtidy.run_on_file, e), entries) + result_futures = map(lambda e: executor.submit(clangtidy.run_on_file, e, extra_args), entries) for f_result in concurrent.futures.as_completed(result_futures): e = f_result.result() result_entries.append(e) diff --git a/scripts/ci/collect-coverage.py b/scripts/ci/collect-coverage.py index 526666b65..ae9d29f74 100755 --- a/scripts/ci/collect-coverage.py +++ b/scripts/ci/collect-coverage.py @@ -55,7 +55,15 @@ def main(): merged_data = f'coverage-merged-{args.select}.profdata' print(f'Generate {merged_data}') - subprocess.check_call(['llvm-profdata', 'merge', '-o', merged_data] + prof_files, shell=False, cwd=profdir) + for attempt in range(3): + try: + subprocess.check_call(['llvm-profdata', 'merge', '-o', merged_data] + prof_files, shell=False, cwd=profdir) + except subprocess.CalledProcessError as e: + print(f'Attempt {attempt} failed: {e}') + else: + break + else: + raise Exception(f'Generate {merged_data} failed') merged_executable = f'merge-executable-{args.select}' print(f'Generate {merged_executable}') @@ -63,7 +71,7 @@ def main(): exclude_regex = f'{sdkroot}/(external)/' if args.select == 'release': - exclude_regex += f'|{sdkroot}/integration/|/test/|/Window_Wayland_Test/|/[^/]+TestUtils/' + exclude_regex += f'|{sdkroot}/integration/|/test/|/window-wayland-common/|/[^/]+test-utils/' if args.report_dir: reportdir = Path(args.report_dir).resolve() diff --git a/scripts/ci/common/clangtidy.py b/scripts/ci/common/clangtidy.py index 942ec3642..44270a33e 100644 --- a/scripts/ci/common/clangtidy.py +++ b/scripts/ci/common/clangtidy.py @@ -135,13 +135,13 @@ def compdb_entry(self): return self._compdb_entry -def run_on_file(compdb_entry): +def run_on_file(compdb_entry, extra_args=[]): compdb_path = Path(compdb_entry.compdb_path) if not (compdb_path.is_file() and compdb_path.name == 'compile_commands.json'): raise RuntimeError(f'compile_commands.json not found in {compdb_path}') start_time = time.time() - cmd = ['clang-tidy', f'-p={compdb_path}', '-quiet', compdb_entry.file] + cmd = ['clang-tidy', f'-p={compdb_path}', *extra_args, '-quiet', compdb_entry.file] p = subprocess.Popen(cmd, shell=False, cwd=compdb_entry.directory, diff --git a/scripts/ci/config/clang-tidy-wrapper.yaml b/scripts/ci/config/clang-tidy-wrapper.yaml index 1430ced74..b12d4ff8d 100644 --- a/scripts/ci/config/clang-tidy-wrapper.yaml +++ b/scripts/ci/config/clang-tidy-wrapper.yaml @@ -15,7 +15,7 @@ exclude: - ^external/ # exclude generated flatbuffers files - - ^client/logic/lib/flatbuffers/ + - ^src/client/internal/logic/flatbuffers/ sort-order: # run on tests first because tests tend to take long due to googletest macros @@ -23,32 +23,30 @@ sort-order: priority: 10 check-filter: - - check: cppcoreguidelines-pro-type-reinterpret-cast - include: - - ^framework/ - - ^client/ # false positives on clang-12 related to google test matchers - check: clang-analyzer-cplusplus.NewDeleteLeaks pattern: Potential leak of memory pointed to by field 'shared' exclude: - - /test/.*Test.cpp$ - - ^renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest_Resources.cpp$ - - ^renderer/RendererLib/RendererLib/test/TestSceneHelper.h$ - - ^renderer/RendererLib/RendererLib/test/RendererSceneUpdaterTest.h$ - - ^renderer/RendererLib/RendererTestCommon/RendererResourceManagerMock.cpp$ + - ^tests/* + - check: cppcoreguidelines-narrowing-conversions + exclude: + - ^tests/.* + - check: cppcoreguidelines-pro-type-reinterpret-cast + include: + - ^src/framework/ + - ^client/ + - check: cppcoreguidelines-pro-type-vararg + exclude: + # ImGui uses va_args in its api + - ^tools/ramses-scene-viewer/src/SceneViewerGui.cpp$ + - ^tools/ramses-imgui/src/ImguiWidgets.cpp$ - check: hicpp-signed-bitwise exclude: - ^examples/ramses-example-local-dma-offscreenbuffer/src/main.cpp$ - - ^integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/DmaOffscreenBufferTests.cpp$ + - ^tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.cpp$ - check: modernize-avoid-c-arrays exclude: - - ^client/test/.*Test.cpp$ - - ^framework/Core/Math3d/include/Math3d - - ^framework/.*/test/.*Test.cpp$ - - ^renderer/RendererLib/RendererLib/test/.*Test.cpp$ - - ^renderer/ramses-renderer-impl/test/.*Test.cpp$ - - ^integration/SandwichTests/RendererTests/EmbeddedCompositingTests - - ^integration/TestContent + - ^tests/.* remove-duplicate-sources: true remove-duplicate-reports: true diff --git a/scripts/ci/config/sanitizer/lsan_suppressions.txt b/scripts/ci/config/sanitizer/lsan_suppressions.txt index 0c50af274..6558c2416 100644 --- a/scripts/ci/config/sanitizer/lsan_suppressions.txt +++ b/scripts/ci/config/sanitizer/lsan_suppressions.txt @@ -1,15 +1,10 @@ leak:libxcb.so -# TODO(tobias) no idea what leaks here +# TODO no idea what leaks here leak:calloc -# TODO(tobias) suppress leaks from swrast executables -leak:RendererLifecycleTests-x11-egl-es-3-0 -leak:EmbeddedCompositor_Wayland_Test -leak:RendererLifecycleTests-wayland-ivi-egl-es-3-0 -leak:EmbeddedCompositingTests-wayland-ivi-egl-es-3-0 -leak:EmbeddedCompositingTests-WithFD-wayland-ivi-egl-es-3-0 -leak:RenderingTests-x11-egl-es-3-0 -leak:RenderingTests-wayland-ivi-egl-es-3-0 -leak:RenderBackendTests-wayland-ivi-egl-es-3-0 -leak:RenderBackendTests-x11-egl-es-3-0 +# EGL de/init in mesa swrast EGL implementation causes both leak (lsan, valgrind) and data race (tsan), +# it might be worth to check if still happening on future driver versions +leak:embedded-compositing-rendering-tests +leak:render-backend-tests +leak:renderer-lifecycle-tests diff --git a/scripts/ci/config/sanitizer/tsan_blacklist.txt b/scripts/ci/config/sanitizer/tsan_blacklist.txt index 50abd2003..557e10583 100644 --- a/scripts/ci/config/sanitizer/tsan_blacklist.txt +++ b/scripts/ci/config/sanitizer/tsan_blacklist.txt @@ -1,3 +1,6 @@ # ignore covesa dlt locking problems mutex:dlt_lock_mutex mutex:dlt_free +# EGL de/init in mesa swrast EGL implementation causes both leak (lsan, valgrind) and data race (tsan), +# it might be worth to check if still happening on future driver versions +race:~Context_EGL() diff --git a/scripts/ci/config/sanitizer/ubsan_blacklist.txt b/scripts/ci/config/sanitizer/ubsan_blacklist.txt new file mode 100644 index 000000000..1b9cbc445 --- /dev/null +++ b/scripts/ci/config/sanitizer/ubsan_blacklist.txt @@ -0,0 +1,2 @@ +#glslang uses EShLanguageMask enum as bitfield +fun:_ZN7glslang14TParseVersions12requireStageERKNS_10TSourceLocE15EShLanguageMaskPKc diff --git a/scripts/ci/config/valgrind/suppressions b/scripts/ci/config/valgrind/suppressions index fa8540a9a..5b92c24bd 100644 --- a/scripts/ci/config/valgrind/suppressions +++ b/scripts/ci/config/valgrind/suppressions @@ -36,15 +36,15 @@ { broken_image_comparison_from_uninit_values_in_readpixels_from_swrast_1 Memcheck:Cond - fun:_ZN17RendererTestUtils26CompareBitmapToImageInFileERKN15ramses_internal5ImageERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEfb + fun:_ZN6ramses8internal17RendererTestUtils26CompareBitmapToImageInFileERKNS0_5ImageERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEfb ... } { broken_image_comparison_from_uninit_values_in_readpixels_from_swrast_2 Memcheck:Cond - fun:_ZNK15ramses_internal5Image19getSumOfPixelValuesEv - fun:_ZN17RendererTestUtils26CompareBitmapToImageInFileERKN15ramses_internal5ImageERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEfb + fun:_ZNK6ramses8internal5Image19getSumOfPixelValuesEv + fun:_ZN6ramses8internal17RendererTestUtils26CompareBitmapToImageInFileERKNS0_5ImageERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEfb ... } @@ -89,7 +89,7 @@ obj:* obj:* obj:* - fun:_ZN15ramses_internal9Device_GL20drawIndexedTrianglesEiij + fun:_ZN6ramses8internal9Device_GL20drawIndexedTrianglesEiij ... } @@ -276,3 +276,29 @@ obj:/lib/x86_64-linux-gnu/ld-2.27.so ... } + +# EGL de/init in mesa swrast EGL implementation causes both leak (lsan, valgrind) and data race (tsan), +# it might be worth to check if still happening on future driver versions +{ + memory_leak_in_initialize_egl + Memcheck:Leak + match-leak-kinds: all + fun:_Znwm + ... + fun:_ZN6ramses8internal11Context_EGL13initializeEglEv +} + +# this appears only occassionally (even relatively rarely) in only a few rendering/lifecycle tests, +# given that all data passed from ramses side to openGL driver is deterministic we assume issue +# in driver itself +{ + swap_buffers_access_uninitialized_memory + Memcheck:Param + writev(vector[...]) + fun:__writev + ... + obj:/usr/lib/x86_64-linux-gnu/libEGL_mesa.so.0.0.0 + ... + fun:_ZN6ramses8internal11Context_EGL11swapBuffersEv + ... +} diff --git a/scripts/ci/installation-check/check-installation.py b/scripts/ci/installation-check/check-installation.py index 5a7d821ce..64bc01626 100755 --- a/scripts/ci/installation-check/check-installation.py +++ b/scripts/ci/installation-check/check-installation.py @@ -63,7 +63,7 @@ def main(): # TODO: remove, these are a tests/demos, not needed in the package r"^bin/ramses-client-test$", r"^bin/ramses-framework-test$", - r"^bin/ramses-logic-viewer-unittests$", + r"^bin/ramses-logic-viewer-test$", r"^bin/ivi-gears$", r"^bin/ivi-simple-dmabuf-egl$", ] @@ -84,41 +84,41 @@ def main(): r"^bin/ramses-logic-viewer$", # TODO: remove, these are a tests/demos, not needed in the package r"^bin/ramses-renderer-lib-test$", - r"^bin/ramses-renderer-impl-test$", + r"^bin/ramses-renderer-test$", r"^bin/ramses-cli-test$", - r"^bin/ramses-logic-viewer-swrast-tests$", + r"^bin/ramses-logic-viewer-gui-test$", r"^lib/libramses-shared-lib\.so$", r"^lib/libramses-shared-lib\.so\.\d+\.\d+$", # TODO: These files should be packaged separately - maybe as a tools package? - r"^bin/ramses-renderer$", + r"^bin/ramses-renderer-standalone$", r"^bin/ramses-scene-viewer$", r"^bin/ramses-stream-viewer$", # TODO: remove, these are a tests/demos, not needed in the package r"^bin/ramses-test-client$", r"^bin/ramses-local-client-test$", - r"^bin/ResourceStressTests$", - r"^bin/RenderBackendTests$", - r"^bin/RenderingTests$", - r"^bin/RendererLifecycleTests$", + r"^bin/resource-stress-tests$", + r"^bin/render-backend-tests$", + r"^bin/rendering-tests$", + r"^bin/renderer-lifecycle-tests$", ] # Everything else below should also not be in the package - expectNonHeaderFiles += [r"^bin/DmaOffscreenBufferRenderingTests$"] + expectNonHeaderFiles += [r"^bin/dma-offscreen-buffer-rendering-tests$"] for p in args.platform: if p == 'wayland-ivi-egl-es-3-0': expectNonHeaderFiles += [ - r"^bin/SystemCompositorController_Wayland_IVI_Test$", - r"^bin/Window_Wayland_IVI_Test$", - r"^bin/EmbeddedCompositor_Wayland_Test$", - r"^bin/EmbeddedCompositingTests$", + r"^bin/system-compositor-controller-wayland-test$", + r"^bin/window-wayland-ivi-test$", + r"^bin/embedded-compositor-wayland-test$", + r"^bin/embedded-compositing-rendering-tests$", ] if p == 'wayland-shell-egl-es-3-0': - expectNonHeaderFiles += [r"^bin/Window_Wayland_Shell_Test$"] + expectNonHeaderFiles += [r"^bin/window-wayland-wl-shell-test$"] if p == 'x11-egl-es-3-0': - expectNonHeaderFiles += [r"^bin/platform-x11-test$"] + expectNonHeaderFiles += [r"^bin/window-x11-test$"] expectNonHeaderFiles += [ r"^lib/cmake/ramses-shared-lib-\d+\.\d+/ramses-shared-libConfigVersion\.cmake$", @@ -149,7 +149,7 @@ def main(): # Ramses header file - add to special list to check compilation later if re.match(r'^include/', relPathStr): installedHeaders.append(str(path.relative_to(includePath))) - elif re.match(r'^bin/res', relPathStr): + elif re.match(r'^bin/res/', relPathStr): # Ignore resource files # TODO Violin: don't pollute installation packages with test resources! Don't install resources unless explicitly requested pass @@ -184,8 +184,8 @@ def main(): return 1 # Extract header files from the source tree - headlessApiHeaders = get_source_api_headers(args.src_dir, '.*/((ramses-(?!renderer)[^/]+-api)|logic)/include') - rendererApiHeaders = get_source_api_headers(args.src_dir, '.*/ramses-renderer-api/include') + headlessApiHeaders = get_source_api_headers(Path(args.src_dir) / 'include', 'ramses/(?!renderer)', False) + rendererApiHeaders = get_source_api_headers(Path(args.src_dir) / 'include', 'ramses/renderer', False) glmHeaders = get_source_api_headers(Path(args.src_dir) / 'external' / 'glm', 'glm', trim=False) srcApiHeaders = headlessApiHeaders diff --git a/scripts/ci/installation-check/check-shared-lib-symbols.py b/scripts/ci/installation-check/check-shared-lib-symbols.py new file mode 100755 index 000000000..7f34adb78 --- /dev/null +++ b/scripts/ci/installation-check/check-shared-lib-symbols.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +import click +import subprocess +import re +from pathlib import Path + + +@click.command(help='Check if there are missing symbols from shared lib') +@click.option('--headless', is_flag=True, default=False, help='Check headless shared lib') +@click.option('-b', '--build-dir', type=click.Path(), help='Build dir') +def main(build_dir, headless): + if not build_dir: + raise Exception("build dir needed") + + client_static_lib_dir = Path(build_dir) / 'src/client/libramses-client.a' + framework_static_lib_dir = Path(build_dir) / 'src/framework/libramses-framework.a' + renderer_static_lib_dir = Path(build_dir) / 'src/renderer/libramses-renderer.a' + headless_shared_lib_dir = Path(build_dir) / 'install/lib/libramses-shared-lib-headless.so' + full_shared_lib_dir = Path(build_dir) / 'install/lib/libramses-shared-lib.so' + + shared_lib_dir = headless_shared_lib_dir if headless else full_shared_lib_dir + + def file_to_symbols(lib_file, is_shared_lib): + if not lib_file.exists(): + raise Exception(f'File does not exist: {lib_file}') + + # run "nm" on the lib file (without demangling, since nm has limited demangling capability) + nm_command = ['nm', '--defined-only', lib_file] + if is_shared_lib: + nm_command += ['--dynamic'] + nm_process_result = subprocess.run(nm_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + # run c++filt to demangle output from nm + cppfilt_command = ['c++filt', '-r'] + cppfilt_process_result = subprocess.run(cppfilt_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, input=nm_process_result.stdout) + cppfilt_process_output_std = cppfilt_process_result.stdout.decode('utf-8') + + ignore_list = [ + # ignore ramses internal and Impl + r'ramses::internal::', + ] + + # Use (?!...) for negative match to filter out symbols in the ignore list + ignore_regex = ''.join([f'(?!.*{e})' for e in ignore_list]) + # Example of symbol line in output: + # 0000000000001d30 T ramses::Appearance::unbindInput(ramses::UniformInput const&) + symbol_regex = re.compile(rf"^([0-9A-Fa-f]+)(\s+)[TB](\s+)({ignore_regex}.*)", re.MULTILINE) + + lib_symbols = [s.group(4) for s in re.finditer(symbol_regex, cppfilt_process_output_std)] + + return lib_symbols + + static_lib_symbols = file_to_symbols(client_static_lib_dir, False) + static_lib_symbols += file_to_symbols(framework_static_lib_dir, False) + if not headless: + static_lib_symbols += file_to_symbols(renderer_static_lib_dir, False) + + shared_lib_symbols = file_to_symbols(shared_lib_dir, True) + + missing_symbols = [s for s in static_lib_symbols if s not in shared_lib_symbols] + if len(missing_symbols) > 0: + raise Exception(f"FOUND MISSING SYMBOLS: {missing_symbols}") + + +if __name__ == "__main__": + main() diff --git a/scripts/ci/installation-check/shared-lib-check/CMakeLists.txt b/scripts/ci/installation-check/shared-lib-check/CMakeLists.txt index 1653f476e..0c2772ce4 100644 --- a/scripts/ci/installation-check/shared-lib-check/CMakeLists.txt +++ b/scripts/ci/installation-check/shared-lib-check/CMakeLists.txt @@ -6,7 +6,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.13) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -53,16 +53,14 @@ if (NOT SYSTEM_GLM) endif() # build with exported target -if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") - set(tgtname "tgt-${name}") - add_executable(${tgtname} "ramses-shared-lib-check.cpp") - # ramses::ramses target resolves glm dependency - target_link_libraries(${tgtname} ramses::ramses) +set(tgtname "tgt-${name}") +add_executable(${tgtname} "ramses-shared-lib-check.cpp") +# ramses::ramses target resolves glm dependency +target_link_libraries(${tgtname} ramses::ramses) - add_custom_target(run-${tgtname} - COMMAND $ - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${tgtname} - COMMENT "Run executable ${tgtname}") - add_dependencies(run-all run-${tgtname}) -endif() +add_custom_target(run-${tgtname} + COMMAND $ + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${tgtname} + COMMENT "Run executable ${tgtname}") +add_dependencies(run-all run-${tgtname}) diff --git a/scripts/ci/installation-check/shared-lib-check/ramses-shared-lib-check.cpp b/scripts/ci/installation-check/shared-lib-check/ramses-shared-lib-check.cpp index f8b286eda..67f11a57d 100644 --- a/scripts/ci/installation-check/shared-lib-check/ramses-shared-lib-check.cpp +++ b/scripts/ci/installation-check/shared-lib-check/ramses-shared-lib-check.cpp @@ -9,12 +9,12 @@ #include #include -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-client.h" -#include "ramses-text.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-utils.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/text/ramses-text.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/client/ramses-utils.h" int main(int argc, char* argv[]) { @@ -22,7 +22,7 @@ int main(int argc, char* argv[]) printf("Start ramses-shared-lib-check\n"); ramses::RamsesFramework framework{frameworkConfig}; - framework.isConnected(); + const auto flag = framework.isConnected(); ramses::RendererConfig config; ramses::RamsesRenderer* renderer(framework.createRenderer(config)); @@ -31,12 +31,21 @@ int main(int argc, char* argv[]) ramses::RamsesClient* ramses(framework.createClient("ramses-shared-lib-check")); ramses::Scene* scene = ramses->createScene(ramses::sceneId_t(1u)); ramses::Node* node = scene->createNode(); - scene->isPublished(); + ramses::MeshNode* meshNode = scene->createMeshNode(); + const auto flag2 = scene->isPublished(); + + ramses::Node* meshNodeAsNode = meshNode; + ramses::MeshNode* meshNodeDynamic = dynamic_cast(meshNodeAsNode); + if(!meshNodeDynamic) + { + printf("Could not find dynamic casted mesh node!\n"); + exit(1); + } ramses::FontRegistry fontRegistry; ramses::nodeId_t nid = ramses::RamsesUtils::GetNodeId(*node); - nid.getValue(); + const auto val = nid.getValue(); printf("End ramses-shared-lib-check\n"); return 0; diff --git a/scripts/ci/installation-check/shared-lib-headless-check/CMakeLists.txt b/scripts/ci/installation-check/shared-lib-headless-check/CMakeLists.txt index a466fb824..b8acfad19 100644 --- a/scripts/ci/installation-check/shared-lib-headless-check/CMakeLists.txt +++ b/scripts/ci/installation-check/shared-lib-headless-check/CMakeLists.txt @@ -6,7 +6,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.13) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -42,14 +42,12 @@ add_custom_target(run-ramses-shared-lib-check add_dependencies(run-all run-ramses-shared-lib-check) # build with exported target -if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") - add_executable(tgt-ramses-shared-lib-check "ramses-shared-lib-check.cpp") - target_link_libraries(tgt-ramses-shared-lib-check ramses::headless) - - add_custom_target(run-tgt-ramses-shared-lib-check - COMMAND $ - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS tgt-ramses-shared-lib-check - COMMENT "Run executable tgt-ramses-shared-lib-check") - add_dependencies(run-all run-tgt-ramses-shared-lib-check) -endif() +add_executable(tgt-ramses-shared-lib-check "ramses-shared-lib-check.cpp") +target_link_libraries(tgt-ramses-shared-lib-check ramses::headless) + +add_custom_target(run-tgt-ramses-shared-lib-check + COMMAND $ + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS tgt-ramses-shared-lib-check + COMMENT "Run executable tgt-ramses-shared-lib-check") +add_dependencies(run-all run-tgt-ramses-shared-lib-check) diff --git a/scripts/ci/installation-check/shared-lib-headless-check/ramses-shared-lib-check.cpp b/scripts/ci/installation-check/shared-lib-headless-check/ramses-shared-lib-check.cpp index 180016133..215ab0df7 100644 --- a/scripts/ci/installation-check/shared-lib-headless-check/ramses-shared-lib-check.cpp +++ b/scripts/ci/installation-check/shared-lib-headless-check/ramses-shared-lib-check.cpp @@ -9,9 +9,9 @@ #include #include -#include "ramses-client.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-text.h" +#include "ramses/client/ramses-client.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/client/text/ramses-text.h" int main(int argc, char* argv[]) { @@ -19,11 +19,11 @@ int main(int argc, char* argv[]) printf("Start ramses-shared-lib-check headless\n"); ramses::RamsesFramework framework{config}; - framework.isConnected(); + const auto flag = framework.isConnected(); ramses::RamsesClient* ramses(framework.createClient("ramses-shared-lib-check")); ramses::Scene* scene = ramses->createScene(ramses::sceneId_t(1u)); - scene->isPublished(); + const auto flag2 = scene->isPublished(); ramses::FontRegistry fontRegistry; diff --git a/scripts/ci/installation-check/static-lib-check/CMakeLists.txt b/scripts/ci/installation-check/static-lib-check/CMakeLists.txt index 7bb6a108d..84d2b6a5c 100644 --- a/scripts/ci/installation-check/static-lib-check/CMakeLists.txt +++ b/scripts/ci/installation-check/static-lib-check/CMakeLists.txt @@ -6,7 +6,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.13) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/scripts/ci/installation-check/static-lib-check/ramses-static-lib-check.cpp b/scripts/ci/installation-check/static-lib-check/ramses-static-lib-check.cpp index 5795b1778..a2883e79c 100644 --- a/scripts/ci/installation-check/static-lib-check/ramses-static-lib-check.cpp +++ b/scripts/ci/installation-check/static-lib-check/ramses-static-lib-check.cpp @@ -9,12 +9,12 @@ #include #include -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-client.h" -#include "ramses-text.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-renderer-api/IRendererEventHandler.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/text/ramses-text.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/renderer/IRendererEventHandler.h" int main(int argc, char* argv[]) { diff --git a/scripts/ci/test/test_clangtidy.py b/scripts/ci/test/test_clangtidy.py index 4ac89abcd..5a60ed0e7 100644 --- a/scripts/ci/test/test_clangtidy.py +++ b/scripts/ci/test/test_clangtidy.py @@ -33,7 +33,7 @@ def test_can_parse_unrealted_text(self): def test_can_single_parse_entry(self): entry = """ /home/foobar/ramses/framework/Ramsh/src/RamshStandardSetup.cpp:10:1: error: #includes are not sorted properly [llvm-include-order] -#include "Ramsh/RamshCommunicationChannelDLT.h" +#include "internal/Ramsh/RamshCommunicationChannelDLT.h" ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "Ramsh/RamshCommunicationChannelConsole.h" """ @@ -86,7 +86,7 @@ def test_can_handle_multiple_checks(self): def test_can_parse_multiple_entries(self): entries = """ /home/foobar/ramses/framework/Ramsh/src/RamshStandardSetup.cpp:10:1: error: #includes are not sorted properly [llvm-include-order] -#include "Ramsh/RamshCommunicationChannelDLT.h" +#include "internal/Ramsh/RamshCommunicationChannelDLT.h" ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "Ramsh/RamshCommunicationChannelConsole.h" /home/foobar/ramses/framework/Ramsh/src/RamshStandardSetup.cpp:13:11: error: '__llvm_libc' needs to be the outermost namespace [llvmlibc-implementation-in-namespace] @@ -100,7 +100,7 @@ def test_can_parse_multiple_entries(self): assert result[0].line == 10 assert result[0].column == 1 assert result[0].text == """/home/foobar/ramses/framework/Ramsh/src/RamshStandardSetup.cpp:10:1: error: #includes are not sorted properly [llvm-include-order] -#include "Ramsh/RamshCommunicationChannelDLT.h" +#include "internal/Ramsh/RamshCommunicationChannelDLT.h" ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ "Ramsh/RamshCommunicationChannelConsole.h" """ # noqa E501 allow long string diff --git a/scripts/ci/test/test_compilationdb.py b/scripts/ci/test/test_compilationdb.py index e85bcd514..5b9fb918d 100644 --- a/scripts/ci/test/test_compilationdb.py +++ b/scripts/ci/test/test_compilationdb.py @@ -19,8 +19,8 @@ # test entry including all supported features TEST_ENTRY_FULL = { "directory": "/base/build", - "command": "/usr/bin/clang++ -DFMT_EXCEPTIONS=0 -DHAS_TCP_COMM=1 -DRAMSES_LINK_STATIC -D__CLANG_SUPPORT_DYN_ANNOTATION__ -IBuildConfig -I../framework/FrameworkTestUtils/include -I../external/cityhash/src -I../external/lodepng -I../framework/Animation/Animation/include -I../framework/Animation/AnimationAPI/include -I../framework/Communication/TransportCommon/include -I../framework/Communication/TransportTCP/include -isystem ../external/googletest/googlemock/include -isystem ../external/googletest/googlemock -isystem ../external/googletest/googletest/include -isystem ../external/googletest/googletest -m64 -fdiagnostics-color -std=c++14 -fPIC -pthread -fvisibility=hidden -g -ggdb -D_DEBUG -fno-omit-frame-pointer -O0 -Wall -Wextra -Wcast-align -Wshadow -Wformat -Wformat-security -Wvla -Wmissing-include-dirs -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast -Wunused -Werror -Wimplicit-fallthrough -Winconsistent-missing-override -Wmove -std=c++14 -o framework/CMakeFiles/FrameworkTestUtils.dir/FrameworkTestUtils/src/CommunicationSystemMock.cpp.o -c /base/framework/FrameworkTestUtils/src/CommunicationSystemMock.cpp", # noqa E501 allow long string - "file": "/base/framework/FrameworkTestUtils/src/CommunicationSystemMock.cpp" + "command": "/usr/bin/clang++ -DFMT_EXCEPTIONS=0 -DHAS_TCP_COMM=1 -DRAMSES_LINK_STATIC -D__CLANG_SUPPORT_DYN_ANNOTATION__ -IBuildConfig -I../framework/framework-test-utils/include -I../external/cityhash/src -I../external/lodepng -I../framework/Animation/Animation/include -I../framework/Animation/AnimationAPI/include -I../framework/Communication/TransportCommon/include -I../framework/Communication/TransportTCP/include -isystem ../external/googletest/googlemock/include -isystem ../external/googletest/googlemock -isystem ../external/googletest/googletest/include -isystem ../external/googletest/googletest -m64 -fdiagnostics-color -std=c++14 -fPIC -pthread -fvisibility=hidden -g -ggdb -D_DEBUG -fno-omit-frame-pointer -O0 -Wall -Wextra -Wcast-align -Wshadow -Wformat -Wformat-security -Wvla -Wmissing-include-dirs -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast -Wunused -Werror -Wimplicit-fallthrough -Winconsistent-missing-override -Wmove -std=c++14 -o framework/CMakeFiles/framework-test-utils.dir/framework-test-utils/src/CommunicationSystemMock.cpp.o -c /base/framework/framework-test-utils/src/CommunicationSystemMock.cpp", # noqa E501 allow long string + "file": "/base/framework/framework-test-utils/src/CommunicationSystemMock.cpp" } # test entry that lacks output file and system includes @@ -39,7 +39,7 @@ def test_can_create_from_full_entry_(): with pytest.raises(RuntimeError): e.relative_file assert e.includes == ["BuildConfig", - "../framework/FrameworkTestUtils/include", + "../framework/framework-test-utils/include", "../external/cityhash/src", "../external/lodepng", "../framework/Animation/Animation/include", @@ -47,7 +47,7 @@ def test_can_create_from_full_entry_(): "../framework/Communication/TransportCommon/include", "../framework/Communication/TransportTCP/include"] assert e.absolute_includes == ["/base/build/BuildConfig", - "/base/framework/FrameworkTestUtils/include", + "/base/framework/framework-test-utils/include", "/base/external/cityhash/src", "/base/external/lodepng", "/base/framework/Animation/Animation/include", @@ -66,8 +66,8 @@ def test_can_create_from_full_entry_(): "HAS_TCP_COMM=1", "RAMSES_LINK_STATIC", "__CLANG_SUPPORT_DYN_ANNOTATION__", "_DEBUG"] - assert e.output_file == "framework/CMakeFiles/FrameworkTestUtils.dir/FrameworkTestUtils/src/CommunicationSystemMock.cpp.o" - assert e.absolute_output_file == "/base/build/framework/CMakeFiles/FrameworkTestUtils.dir/FrameworkTestUtils/src/CommunicationSystemMock.cpp.o" + assert e.output_file == "framework/CMakeFiles/framework-test-utils.dir/framework-test-utils/src/CommunicationSystemMock.cpp.o" + assert e.absolute_output_file == "/base/build/framework/CMakeFiles/framework-test-utils.dir/framework-test-utils/src/CommunicationSystemMock.cpp.o" assert e.project_root is None assert e.compdb_path == __file__ @@ -98,7 +98,7 @@ def test_can_load_compdb_from_file(): entries = compilationdb.load_from_file(f.name) assert len(entries) == 2 - assert entries[0].file == "/base/framework/FrameworkTestUtils/src/CommunicationSystemMock.cpp" + assert entries[0].file == "/base/framework/framework-test-utils/src/CommunicationSystemMock.cpp" assert entries[0].compdb_path == f.name assert entries[1].file == "/base/external/protocol.c" assert entries[1].compdb_path == f.name diff --git a/scripts/code_style_checker/check_all_styles.py b/scripts/code_style_checker/check_all_styles.py index c58f983c8..dcc8e3b44 100755 --- a/scripts/code_style_checker/check_all_styles.py +++ b/scripts/code_style_checker/check_all_styles.py @@ -28,7 +28,6 @@ from check_tabbing_and_spacing import check_tabs_no_spaces from check_enum_style import check_enum_style from check_last_line_newline import check_last_line_newline -from check_api_export_symbols import check_api_export_symbols from check_comments import check_doxygen_singleline_comments from check_deprecated import check_deprecated from check_file_attributes import check_file_attributes @@ -82,7 +81,7 @@ def main(): } generated_files = { - r'^client/logic/lib/flatbuffers/generated' + r'^src/client/internal/logic/flatbuffers/generated' } # Externals are allowed to have their own code style @@ -106,7 +105,6 @@ def main(): check_deprecated(f, file_contents, clean_file_contents, file_lines, clean_file_lines) check_enum_style(f, clean_file_contents) check_last_line_newline(f, file_contents) - check_api_export_symbols(f, clean_file_contents) check_doxygen_singleline_comments(f, file_lines) shared_blacklist_non_src_files = shared_blacklist | generated_files | { @@ -118,10 +116,14 @@ def main(): r'.*/gradle\.properties$', r'.*/gradlew$', r'.*/gradlew\.bat$', + # Android demo r'^demo/android/ramses-renderer-android-app/app/src/main/res/', r'^demo/android/ramses-renderer-android-app/build\.gradle', r'^demo/android/ramses-renderer-android-app-native-activity/app/src/main/res/', r'^demo/android/ramses-renderer-android-app-native-activity/build\.gradle', + # iOS demo + r'Info\.plist$', + r'\.storyboard$', } blacklist_files_formatting = shared_blacklist_non_src_files | { @@ -146,6 +148,7 @@ def main(): r'asan_suppressions\.txt$', r'lsan_suppressions\.txt$', r'tsan_blacklist\.txt$', + r'ubsan_blacklist\.txt$', r'maven_settings\.xml$', r'\.config$', r'\.conf$', diff --git a/scripts/code_style_checker/check_api_export_symbols.py b/scripts/code_style_checker/check_api_export_symbols.py deleted file mode 100644 index 36bb3afde..000000000 --- a/scripts/code_style_checker/check_api_export_symbols.py +++ /dev/null @@ -1,69 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -import re -from common_modules import common - - -def check_api_export_symbols(filename, clean_file_contents): - """ - Check that public API symbols are exported - - A class has RAMSES_API prefix before the name, structs and enums don't have it - """ - - is_api = ("ramses-client-api" in filename) or ("ramses-text-api" in filename) or ("ramses-renderer-api" in filename) or ("ramses-framework-api" in filename) - is_header = (filename[-2:] == ".h") or (filename[-4:] == ".hpp") - is_api_header = is_api and is_header - - if is_api_header: - # symbol_def_re matches following patterns: - # (correct case) class RAMSES_API SymbolName { [...] - # (correct case) class RAMSES_API SymbolName : [...] - # (correct case) struct/enum|enum class SymbolName : [...] - # (correct case) class SymbolName : [..no symbol exported (but base class can be exported)..] - # (wrong case) class SymbolName { [..no symbol exported..] - symbol_def_re = re.compile(r'(template [^;]+?)?\s+(enum|class|struct|enum\s+class)(\s+)(\w+)(\s+)(\w*)(\s*)(\{|\:)') - - for symbol_match in re.finditer(symbol_def_re, clean_file_contents): - line_number = clean_file_contents[:symbol_match.start()].count("\n") - - symbolNameGroups = symbol_match.groups() - isTemplate = symbolNameGroups[0] is not None - isEnum = symbolNameGroups[1].strip() == "enum" - isStruct = symbolNameGroups[1].strip() == "struct" - isEnumClass = "enum " in symbolNameGroups[1].strip() - isDerived = (symbolNameGroups[7] is not None) and (symbolNameGroups[7].strip() == ":") - firstWord = symbolNameGroups[3].strip() - secondWord = symbolNameGroups[5].strip() - - # check special cases that should NOT have RAMSES_API - if isEnum: - if firstWord == "RAMSES_API": - common.log_warning("check_api_export_symbols", filename, line_number + 1, "Enum exported as RAMSES_API: " + secondWord) - elif isEnumClass: - if firstWord == "RAMSES_API": - common.log_warning("check_api_export_symbols", filename, line_number + 1, "Enum class exported as RAMSES_API: " + secondWord) - elif isStruct: - if firstWord == "RAMSES_API": - common.log_warning("check_api_export_symbols", filename, line_number + 1, "Struct exported as RAMSES_API: " + secondWord) - elif isTemplate: - if firstWord == "RAMSES_API": - common.log_warning("check_api_export_symbols", filename, line_number + 1, "Template exported as RAMSES_API: " + secondWord) - else: - # Ramses SDK uses selective export which is not easy to check via source parsing, this only ensures that classes which are not derived - # have any symbol exported (basic sanity check that reminds developer to export properly) - if (firstWord != "RAMSES_API") and ("RAMSES_API" not in clean_file_contents) and (not isDerived): - common.log_warning("check_api_export_symbols", filename, line_number + 1, "Public symbol not exported as RAMSES_API: " + firstWord) - else: - # Will find occurances of 'RAMSES_API' (whole word) but not containing 'template' (whole word), - # template instantiations can be in a non-API-header files. - RAMSES_API_re = re.compile(r'^(?!.*\b(template)\b).*\b(API)\b') - for symbol_match in re.finditer(RAMSES_API_re, clean_file_contents): - line_number = clean_file_contents[:symbol_match.start()].count("\n") - common.log_warning("check_api_export_symbols", filename, line_number + 1, - "Exporting API symbol in a non-API-header file! This symbol will be unusable.") diff --git a/scripts/code_style_checker/check_deprecated.py b/scripts/code_style_checker/check_deprecated.py index 66eebd0ec..c02013b59 100644 --- a/scripts/code_style_checker/check_deprecated.py +++ b/scripts/code_style_checker/check_deprecated.py @@ -49,7 +49,7 @@ def check_deprecated(filename, file_contents, clean_file_contents, file_lines, c if filename.endswith('.h') and g_re_unwanted_force_thread_local.search(line): common.log_warning("check_deprecated", filename, line_number + 1, - 'found disallowed #include "Utils/ThreadLocalLogForced.h" in header"', file_lines[line_number].strip(" \t\r\n")) + 'found disallowed #include "internal/Core/Utils/ThreadLocalLogForced.h" in header"', file_lines[line_number].strip(" \t\r\n")) # TODO: Fix offenders and reduce limit until some reasonable length is reached if len(line) > g_max_line_length: diff --git a/scripts/docker/ramses-basic/Dockerfile b/scripts/docker/ramses-basic/Dockerfile index f4dd11185..fab96a45f 100644 --- a/scripts/docker/ramses-basic/Dockerfile +++ b/scripts/docker/ramses-basic/Dockerfile @@ -1,27 +1,12 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -FROM alpine:3.11 - -RUN apk add --no-cache \ - bash \ - cmake \ - g++ \ - git \ - ninja \ - mesa-dev \ - mesa-dri-swrast \ - wayland-dev \ - ragel # needed for harfbuzz build - -COPY entrypoint.sh /entrypoint.sh -RUN chmod ugo+rx /entrypoint.sh -ENTRYPOINT ["/entrypoint.sh"] +FROM ubuntu:20.04 # export configuration flags ENV RAMSES_SOURCE=/home/ramses-build/git \ @@ -32,6 +17,45 @@ ENV RAMSES_SOURCE=/home/ramses-build/git \ VOLUME /home/ramses-build/git \ /home/ramses-build/build +COPY entrypoint.sh /entrypoint.sh +RUN chmod ugo+rx /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] + +RUN apt-get update \ + && env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + cmake \ + curl \ + doxygen \ + gcc g++ \ + git \ + libdrm-dev \ + libgbm-dev \ + libgles2-mesa-dev \ + libpng-dev \ + libwayland-client0 \ + libwayland-dev \ + libweston-8-dev \ + mesa-utils \ + ninja-build \ + pkg-config \ + python3-pip \ + software-properties-common \ + weston \ + && apt-get purge -y software-properties-common \ + && apt-get autoremove -y \ + && apt-get autoclean + +# add ramses user and groups +RUN groupadd --gid 1000 ramses-build \ + && useradd --create-home --uid 1000 --gid ramses-build ramses-build \ + && echo "%ramses-build ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + +# install python requirements +COPY requirements.txt /tmp/ +RUN pip3 install -r /tmp/requirements.txt && rm -f /tmp/requirements.txt + # add link to build script to container (for convenience) RUN ln -s $RAMSES_SOURCE/scripts/docker/runtime-files/build-ramses.sh /home/ramses-build/build-ramses.sh \ && ln -s $RAMSES_SOURCE/scripts/docker/runtime-files/run-unittests.sh /home/ramses-build/run-unittests.sh diff --git a/integration/PlatformTests/CMakeLists.txt b/scripts/docker/ramses-basic/requirements.txt similarity index 80% rename from integration/PlatformTests/CMakeLists.txt rename to scripts/docker/ramses-basic/requirements.txt index 66979579b..c5e7595f1 100644 --- a/integration/PlatformTests/CMakeLists.txt +++ b/scripts/docker/ramses-basic/requirements.txt @@ -1,9 +1,13 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -ADD_SUBDIRECTORY(RenderBackendTests) +sphinx==6.2.1 +sphinx-rtd-theme==1.2.2 +myst-parser==2.0.0 +breathe==4.35.0 + diff --git a/scripts/docker/runtime-files/build-ramses.sh b/scripts/docker/runtime-files/build-ramses.sh index fdd1fa4c2..30834db24 100755 --- a/scripts/docker/runtime-files/build-ramses.sh +++ b/scripts/docker/runtime-files/build-ramses.sh @@ -16,8 +16,8 @@ cmake \ -DCMAKE_INSTALL_PREFIX=$RAMSES_BUILD/install \ -Dramses-sdk_BUILD_TESTS=1 \ -Dramses-sdk_BUILD_EXAMPLES=1 \ - -Dramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_SHELL=1 \ -Dramses-sdk_ENABLE_DLT=0 \ + -Dramses-sdk_ENABLE_FLATBUFFERS_GENERATION=0 \ -G Ninja \ -Wno-dev \ $RAMSES_SOURCE diff --git a/scripts/migrate_to_28_0_0.py b/scripts/migrate_to_28_0_0.py new file mode 100755 index 000000000..51d30b16b --- /dev/null +++ b/scripts/migrate_to_28_0_0.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +import click + +import os +from pathlib import Path + + +class Common: + def __init__(self): + pass + + +@click.group() +@click.pass_context +def cli(ctx): + ctx.obj = Common() + + +@cli.command() +@click.pass_obj +@click.argument('path') +@click.option('-e', '--encoding', default='utf-8') +def includes(conf, path, encoding): + patterns = [ + ["#include \"ramses-framework-api/", "#include \"ramses/framework/"], + ["#include ", "#include "], + ["#include \"ramses-utils.h\"", "#include \"ramses/client/ramses-utils.h\""], + ["#include ", "#include "], + ["#include \"ramses-client-api/", "#include \"ramses/client/"], + ["#include ", "#include "], + ["#include \"ramses-text-api/", "#include \"ramses/client/text/"], + ["#include impl) + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + bool Appearance::setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) + { + const bool status = m_impl.setBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); + LOG_HL_CLIENT_API4(status, srcColor, destColor, srcAlpha, destAlpha); + return status; + } + + bool Appearance::getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const + { + return m_impl.getBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); + } + + bool Appearance::setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha) + { + const bool status = m_impl.setBlendingOperations(operationColor, operationAlpha); + LOG_HL_CLIENT_API2(status, operationColor, operationAlpha); + return status; + } + + bool Appearance::getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const + { + return m_impl.getBlendingOperations(operationColor, operationAlpha); + } + + bool Appearance::setBlendingColor(const vec4f& color) + { + const bool status = m_impl.setBlendingColor(color); + LOG_HL_CLIENT_API4(status, color[0], color[1], color[2], color[3]); + return status; + } + + bool Appearance::getBlendingColor(vec4f& color) const + { + return m_impl.getBlendingColor(color); + } + + bool Appearance::setDepthWrite(EDepthWrite mode) + { + const bool status = m_impl.setDepthWrite(mode); + LOG_HL_CLIENT_API1(status, mode); + return status; + } + + bool Appearance::getDepthWriteMode(EDepthWrite& mode) const + { + return m_impl.getDepthWriteMode(mode); + } + + bool Appearance::setDepthFunction(EDepthFunc func) + { + const bool status = m_impl.setDepthFunction(func); + LOG_HL_CLIENT_API1(status, func); + return status; + } + + bool Appearance::getDepthFunction(EDepthFunc& func) const + { + return m_impl.getDepthFunction(func); + } + + bool Appearance::setScissorTest(EScissorTest state, int16_t x, int16_t y, uint16_t width, uint16_t height) + { + const bool status = m_impl.setScissorTest(state, x, y, width, height); + LOG_HL_CLIENT_API5(status, state, x, y, width, height); + return status; + } + + bool Appearance::getScissorTestState(EScissorTest& state) const + { + return m_impl.getScissorTestState(state); + } + + bool Appearance::getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const + { + return m_impl.getScissorRegion(x, y, width, height); + } + + bool Appearance::setStencilFunction(EStencilFunc func, uint8_t ref, uint8_t mask) + { + const bool status = m_impl.setStencilFunc(func, ref, mask); + LOG_HL_CLIENT_API3(status, func, ref, mask); + return status; + } + + bool Appearance::getStencilFunction(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const + { + return m_impl.getStencilFunc(func, ref, mask); + } + + bool Appearance::setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass) + { + const bool status = m_impl.setStencilOperation(sfail, dpfail, dppass); + LOG_HL_CLIENT_API3(status, sfail, dpfail, dppass); + return status; + } + + bool Appearance::getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const + { + return m_impl.getStencilOperation(sfail, dpfail, dppass); + } + + bool Appearance::setCullingMode(ECullMode mode) + { + const bool status = m_impl.setCullingMode(mode); + LOG_HL_CLIENT_API1(status, mode); + return status; + } + + bool Appearance::getCullingMode(ECullMode& mode) const + { + return m_impl.getCullingMode(mode); + } + + bool Appearance::setDrawMode(EDrawMode mode) + { + const bool status = m_impl.setDrawMode(mode); + LOG_HL_CLIENT_API1(status, mode); + return status; + } + + bool Appearance::getDrawMode(EDrawMode& mode) const + { + return m_impl.getDrawMode(mode); + } + + bool Appearance::setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha) + { + const bool status = m_impl.setColorWriteMask(writeRed, writeGreen, writeBlue, writeAlpha); + LOG_HL_CLIENT_API4(status, writeRed, writeGreen, writeBlue, writeAlpha); + return status; + } + + bool Appearance::getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const + { + return m_impl.getColorWriteMask(writeRed, writeGreen, writeBlue, writeAlpha); + } + + bool Appearance::setInputTexture(const UniformInput& input, const TextureSampler& textureSampler) + { + const bool status = m_impl.setInputTexture(input.impl(), textureSampler.impl()); + LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(textureSampler)); + return status; + } + + bool Appearance::getInputTexture(const UniformInput& input, const TextureSampler*& textureSampler) const + { + return m_impl.getInputTexture(input.impl(), textureSampler); + } + + bool Appearance::setInputTexture(const UniformInput& input, const TextureSamplerMS& textureSampler) + { + const bool status = m_impl.setInputTexture(input.impl(), textureSampler.impl()); + LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(textureSampler)); + return status; + } + + bool Appearance::getInputTextureMS(const UniformInput& input, const TextureSamplerMS*& textureSampler) const + { + return m_impl.getInputTextureMS(input.impl(), textureSampler); + } + + bool Appearance::setInputTexture(const UniformInput& input, const TextureSamplerExternal& textureSampler) + { + const bool status = m_impl.setInputTexture(input.impl(), textureSampler.impl()); + LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(textureSampler)); + return status; + } + + bool Appearance::getInputTextureExternal(const UniformInput& input, const TextureSamplerExternal*& textureSampler) const + { + return m_impl.getInputTextureExternal(input.impl(), textureSampler); + } + + bool Appearance::bindInput(const UniformInput& input, const DataObject& dataObject) + { + const bool status = m_impl.bindInput(input.impl(), dataObject.impl()); + LOG_HL_CLIENT_API2(status, LOG_API_GENERIC_OBJECT_STRING(input), LOG_API_RAMSESOBJECT_STRING(dataObject)); + return status; + } + + bool Appearance::unbindInput(const UniformInput& input) + { + const bool status = m_impl.unbindInput(input.impl()); + LOG_HL_CLIENT_API1(status, LOG_API_GENERIC_OBJECT_STRING(input)); + return status; + } + + bool Appearance::isInputBound(const UniformInput& input) const + { + return m_impl.isInputBound(input.impl()); + } + + const DataObject* Appearance::getDataObjectBoundToInput(const UniformInput& input) const + { + return m_impl.getBoundDataObject(input.impl()); + } + + const Effect& Appearance::getEffect() const + { + return m_impl.getEffect(); + } + + internal::AppearanceImpl& Appearance::impl() + { + return m_impl; + } + + const internal::AppearanceImpl& Appearance::impl() const + { + return m_impl; + } + + template bool Appearance::setInputValueInternal(const UniformInput& input, T&& value) + { + // API uses ref/move forwarding for possibility to move but current implementation does not make use of it + return setInputValueInternal(input, 1u, &value); + } + + template bool Appearance::setInputValueInternal(const UniformInput& input, size_t elementCount, const T* values) + { + return m_impl.setInputValue(input.impl(), elementCount, values); + } + + template bool Appearance::getInputValueInternal(const UniformInput& input, T& value) const + { + return getInputValueInternal(input, 1u, &value); + } + + template bool Appearance::getInputValueInternal(const UniformInput& input, size_t elementCount, T* valuesOut) const + { + return m_impl.getInputValue(input.impl(), elementCount, valuesOut); + } + + // const l-value instances + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const bool&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const int32_t&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const float&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const vec2i&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const vec3i&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const vec4i&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const vec2f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const vec3f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const vec4f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const matrix22f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const matrix33f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, const matrix44f&); + + // l-value instances + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, bool&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, int32_t&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, float&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec2i&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec3i&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec4i&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec2f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec3f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec4f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, matrix22f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, matrix33f&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, matrix44f&); + + // r-value instances + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, bool&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, int32_t&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, float&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec2i&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec3i&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec4i&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec2f&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec3f&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, vec4f&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, matrix22f&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, matrix33f&&); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, matrix44f&&); + + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const bool*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const int32_t*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const float*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const vec2i*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const vec3i*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const vec4i*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const vec2f*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const vec3f*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const vec4f*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const matrix22f*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const matrix33f*); + template RAMSES_API bool Appearance::setInputValueInternal(const UniformInput&, size_t, const matrix44f*); + + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, bool&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, int32_t&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, float&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, vec2i&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, vec3i&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, vec4i&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, vec2f&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, vec3f&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, vec4f&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, matrix22f&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, matrix33f&) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, matrix44f&) const; + + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, bool*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, int32_t*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, float*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, vec2i*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, vec3i*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, vec4i*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, vec2f*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, vec3f*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, vec4f*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, matrix22f*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, matrix33f*) const; + template RAMSES_API bool Appearance::getInputValueInternal(const UniformInput&, size_t, matrix44f*) const; +} diff --git a/src/client/impl/AppearanceImpl.cpp b/src/client/impl/AppearanceImpl.cpp new file mode 100644 index 000000000..778e718d8 --- /dev/null +++ b/src/client/impl/AppearanceImpl.cpp @@ -0,0 +1,767 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// client API +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/Effect.h" +#include "ramses/client/DataObject.h" + +// internal +#include "impl/AppearanceImpl.h" +#include "impl/EffectImpl.h" +#include "impl/EffectInputImpl.h" +#include "impl/ObjectIteratorImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/AppearanceUtils.h" +#include "impl/SerializationContext.h" +#include "impl/SceneImpl.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "impl/DataTypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h" +#include "internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h" +#include "internal/SceneGraph/SceneUtils/DataInstanceHelper.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include + +namespace ramses::internal +{ + AppearanceImpl::AppearanceImpl(SceneImpl& scene, std::string_view appearancename) + : SceneObjectImpl(scene, ERamsesObjectType::Appearance, appearancename) + , m_effectImpl(nullptr) + { + } + + AppearanceImpl::~AppearanceImpl() = default; + + bool AppearanceImpl::setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) + { + getIScene().setRenderStateBlendFactors(m_renderStateHandle, srcColor, destColor, srcAlpha, destAlpha); + return true; + } + + bool AppearanceImpl::getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const + { + const RenderState& rs = getIScene().getRenderState(m_renderStateHandle); + srcColor = rs.blendFactorSrcColor; + destColor = rs.blendFactorDstColor; + srcAlpha = rs.blendFactorSrcAlpha; + destAlpha = rs.blendFactorDstAlpha; + return true; + } + + bool AppearanceImpl::setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha) + { + if ((operationColor == EBlendOperation::Disabled) != (operationAlpha == EBlendOperation::Disabled)) + { + getErrorReporting().set("Appearance::setBlendingOperations: invalid combination - one of operationColor or operationAlpha disabled and the other not!"); + return false; + } + + getIScene().setRenderStateBlendOperations(m_renderStateHandle, operationColor, operationAlpha); + + return true; + } + + bool AppearanceImpl::getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const + { + const RenderState& rs = getIScene().getRenderState(m_renderStateHandle); + operationColor = rs.blendOperationColor; + operationAlpha = rs.blendOperationAlpha; + return true; + } + + bool AppearanceImpl::setBlendingColor(const vec4f& color) + { + getIScene().setRenderStateBlendColor(m_renderStateHandle, color); + return true; + } + + bool AppearanceImpl::getBlendingColor(vec4f& color) const + { + color = getIScene().getRenderState(m_renderStateHandle).blendColor; + return true; + } + + bool AppearanceImpl::setDepthFunction(EDepthFunc func) + { + getIScene().setRenderStateDepthFunc(m_renderStateHandle, func); + return true; + } + + bool AppearanceImpl::getDepthFunction(EDepthFunc& func) const + { + func = getIScene().getRenderState(m_renderStateHandle).depthFunc; + return true; + } + + bool AppearanceImpl::setDepthWrite(EDepthWrite flag) + { + getIScene().setRenderStateDepthWrite(m_renderStateHandle, flag); + return true; + } + + bool AppearanceImpl::getDepthWriteMode(EDepthWrite& mode) const + { + mode = getIScene().getRenderState(m_renderStateHandle).depthWrite; + return true; + } + + bool AppearanceImpl::setScissorTest(EScissorTest flag, int16_t x, int16_t y, uint16_t width, uint16_t height) + { + getIScene().setRenderStateScissorTest(m_renderStateHandle, flag, { x, y, width, height }); + return true; + } + + bool AppearanceImpl::getScissorTestState(EScissorTest& mode) const + { + mode = getIScene().getRenderState(m_renderStateHandle).scissorTest; + return true; + } + + bool AppearanceImpl::getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const + { + const auto& scissorRegion = getIScene().getRenderState(m_renderStateHandle).scissorRegion; + x = scissorRegion.x; + y = scissorRegion.y; + width = scissorRegion.width; + height = scissorRegion.height; + + return true; + } + + bool AppearanceImpl::setStencilFunc(EStencilFunc func, uint8_t ref, uint8_t mask) + { + getIScene().setRenderStateStencilFunc(m_renderStateHandle, func, ref, mask); + return true; + } + + bool AppearanceImpl::getStencilFunc(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const + { + const RenderState& rs = getIScene().getRenderState(m_renderStateHandle); + func = rs.stencilFunc; + ref = rs.stencilRefValue; + mask = rs.stencilMask; + return true; + } + + bool AppearanceImpl::setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass) + { + getIScene().setRenderStateStencilOps( m_renderStateHandle, sfail, dpfail, dppass); + return true; + } + + bool AppearanceImpl::getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const + { + const RenderState& rs = getIScene().getRenderState(m_renderStateHandle); + sfail = rs.stencilOpFail; + dpfail = rs.stencilOpDepthFail; + dppass = rs.stencilOpDepthPass; + return true; + } + + bool AppearanceImpl::setCullingMode(ECullMode mode) + { + getIScene().setRenderStateCullMode(m_renderStateHandle, mode); + return true; + } + + bool AppearanceImpl::getCullingMode(ECullMode& mode) const + { + mode = getIScene().getRenderState(m_renderStateHandle).cullMode; + return true; + } + + bool AppearanceImpl::setDrawMode(EDrawMode mode) + { + if (m_effectImpl->hasGeometryShader()) + { + EDrawMode geometryShaderInputType; + m_effectImpl->getGeometryShaderInputType(geometryShaderInputType); + if (!AppearanceUtils::GeometryShaderCompatibleWithDrawMode(geometryShaderInputType, mode)) + { + getErrorReporting().set("Appearance::setDrawMode failed, source Effect has a geometry shader which expects a different draw mode."); + return false; + } + } + + getIScene().setRenderStateDrawMode(m_renderStateHandle, mode); + return true; + } + + bool AppearanceImpl::getDrawMode(EDrawMode& mode) const + { + mode = getIScene().getRenderState(m_renderStateHandle).drawMode; + return true; + } + + bool AppearanceImpl::setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha) + { + const ColorWriteMask colorMask = + (writeRed ? static_cast(EColorWriteFlag_Red ) : 0u) | + (writeGreen ? static_cast(EColorWriteFlag_Green) : 0u) | + (writeBlue ? static_cast(EColorWriteFlag_Blue ) : 0u) | + (writeAlpha ? static_cast(EColorWriteFlag_Alpha) : 0u); + getIScene().setRenderStateColorWriteMask(m_renderStateHandle, colorMask); + return true; + } + + bool AppearanceImpl::getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const + { + const ColorWriteMask mask = getIScene().getRenderState(m_renderStateHandle).colorWriteMask; + writeRed = (mask & EColorWriteFlag_Red ) != 0; + writeGreen = (mask & EColorWriteFlag_Green) != 0; + writeBlue = (mask & EColorWriteFlag_Blue ) != 0; + writeAlpha = (mask & EColorWriteFlag_Alpha) != 0; + return true; + } + + bool AppearanceImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << (m_effectImpl ? serializationContext.getIDForObject(m_effectImpl) : SerializationContext::GetObjectIDNull()); + + outStream << m_renderStateHandle; + outStream << m_uniformLayout; + outStream << m_uniformInstance; + + outStream << static_cast(m_bindableInputs.size()); + for (const auto& bindableInput : m_bindableInputs) + { + outStream << bindableInput.key; + outStream << (bindableInput.value.externallyBoundDataObject ? serializationContext.getIDForObject(bindableInput.value.externallyBoundDataObject) : SerializationContext::GetObjectIDNull()); + outStream << bindableInput.value.dataReference; + } + + return true; + } + + bool AppearanceImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_effectImpl); + + inStream >> m_renderStateHandle; + inStream >> m_uniformLayout; + inStream >> m_uniformInstance; + + uint32_t bindableInputCount = 0u; + inStream >> bindableInputCount; + + assert(m_bindableInputs.size() == 0u); + m_bindableInputs.reserve(bindableInputCount); + for (uint32_t i = 0u; i < bindableInputCount; ++i) + { + uint32_t inputIndex = 0u; + inStream >> inputIndex; + + BindableInput bindableInput; + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, bindableInput.externallyBoundDataObject); + inStream >> bindableInput.dataReference; + + m_bindableInputs.put(inputIndex, bindableInput); + } + + serializationContext.addForDependencyResolve(this); + + return true; + } + + bool AppearanceImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::resolveDeserializationDependencies(serializationContext)) + return false; + + serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_effectImpl); + + for (auto& bindableInput : m_bindableInputs) + serializationContext.resolveDependencyIDImplAndStoreAsPointer(bindableInput.value.externallyBoundDataObject); + + return true; + } + + void AppearanceImpl::onValidate(ValidationReportImpl& report) const + { + SceneObjectImpl::onValidate(report); + validateEffect(report); + validateUniforms(report); + } + + void AppearanceImpl::validateEffect(ValidationReportImpl& report) const + { + ObjectIteratorImpl iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::Effect); + RamsesObject* ramsesObject = iter.getNext(); + while (nullptr != ramsesObject) + { + const Effect& effect = RamsesObjectTypeUtils::ConvertTo(*ramsesObject); + if (&effect.impl() == m_effectImpl) + { + report.addDependentObject(*this, effect.impl()); + return; + } + + ramsesObject = iter.getNext(); + } + + report.add(EIssueType::Error, "Appearance is referring to an invalid Effect", &getRamsesObject()); + } + + void AppearanceImpl::validateUniforms(ValidationReportImpl& report) const + { + const DataLayout& layout = getIScene().getDataLayout(m_uniformLayout); + const uint32_t numFields = layout.getFieldCount(); + for (DataFieldHandle fieldHandle(0u); fieldHandle < numFields; ++fieldHandle) + { + const EDataType dataType = layout.getField(fieldHandle).dataType; + if (!IsTextureSamplerType(dataType)) + continue; + + const TextureSamplerHandle samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, fieldHandle); + if (!getIScene().isTextureSamplerAllocated(samplerHandle)) + report.add(EIssueType::Error, "Appearance is using a Texture Sampler that does not exist", &getRamsesObject()); + + SceneObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSampler); + while (const auto* sampler = iter.getNext()) + { + if (samplerHandle == sampler->impl().getTextureSamplerHandle()) + { + report.addDependentObject(*this, sampler->impl()); + break; + } + } + } + + for (const auto& bindableInput : m_bindableInputs) + { + if (bindableInput.value.externallyBoundDataObject) + { + const DataInstanceHandle boundInstance = getIScene().getDataReference(m_uniformInstance, DataFieldHandle(bindableInput.key)); + if (!getIScene().isDataInstanceAllocated(boundInstance)) + report.add(EIssueType::Error, "Appearance's input is bound to a DataObject that does not exist", &getRamsesObject()); + + ObjectIteratorImpl iterator(getSceneImpl().getObjectRegistry(), ERamsesObjectType::DataObject); + RamsesObject* ramsesObject = nullptr; + while (nullptr != (ramsesObject = iterator.getNext())) + { + const DataObject& dataObject = RamsesObjectTypeUtils::ConvertTo(*ramsesObject); + if (boundInstance == dataObject.impl().getDataReference()) + { + report.addDependentObject(*this, dataObject.impl()); + break; + } + } + } + } + } + + void AppearanceImpl::initializeFrameworkData(const EffectImpl& effect) + { + m_effectImpl = &effect; + + m_renderStateHandle = getIScene().allocateRenderState(RenderStateHandle::Invalid()); + createUniformDataInstance(effect); + + // Set draw mode to geometry shader's expected mode, if effect has such + if (effect.hasGeometryShader()) + { + EDrawMode geometryShaderInputType; + effect.getGeometryShaderInputType(geometryShaderInputType); + setDrawMode(geometryShaderInputType); + } + } + + void AppearanceImpl::deinitializeFrameworkData() + { + getIScene().releaseDataInstance(m_uniformInstance); + m_uniformInstance = DataInstanceHandle::Invalid(); + + for(const auto& bindableInput : m_bindableInputs) + { + const DataInstanceHandle dataRef = bindableInput.value.dataReference; + const DataLayoutHandle dataRefLayout = getIScene().getLayoutOfDataInstance(dataRef); + getIScene().releaseDataInstance(dataRef); + getIScene().releaseDataLayout(dataRefLayout); + } + m_bindableInputs.clear(); + + getIScene().releaseRenderState(m_renderStateHandle); + m_renderStateHandle = RenderStateHandle::Invalid(); + + getIScene().releaseDataLayout(m_uniformLayout); + m_uniformLayout = DataLayoutHandle::Invalid(); + } + + const EffectImpl* AppearanceImpl::getEffectImpl() const + { + return m_effectImpl; + } + + const Effect& AppearanceImpl::getEffect() const + { + return RamsesObjectTypeUtils::ConvertTo(m_effectImpl->getRamsesObject()); + } + + RenderStateHandle AppearanceImpl::getRenderStateHandle() const + { + return m_renderStateHandle; + } + + DataInstanceHandle AppearanceImpl::getUniformDataInstance() const + { + return m_uniformInstance; + } + + void AppearanceImpl::createUniformDataInstance(const EffectImpl& effect) + { + InputIndexVector referencedInputs; + const EffectInputInformationVector& uniformsInputInfo = effect.getUniformInputInformation(); + m_uniformLayout = DataLayoutCreationHelper::CreateUniformDataLayoutMatchingEffectInputs(getIScene(), uniformsInputInfo, referencedInputs, effect.getLowlevelResourceHash()); + + m_uniformInstance = getIScene().allocateDataInstance(m_uniformLayout, {}); + + m_bindableInputs.reserve(m_bindableInputs.size() + referencedInputs.size()); + for (const auto& refInput : referencedInputs) + { + const DataFieldHandle dataField(refInput); + + BindableInput bindableInput; + bindableInput.externallyBoundDataObject = nullptr; + bindableInput.dataReference = DataLayoutCreationHelper::CreateAndBindDataReference(getIScene(), m_uniformInstance, dataField, uniformsInputInfo[refInput].dataType); + m_bindableInputs.put(refInput, bindableInput); + } + } + + bool AppearanceImpl::checkEffectInputValidityAndValueCompatibility(const EffectInputImpl& input, size_t valueElementCount, std::initializer_list valueDataType) const + { + if (input.getEffectHash() != m_effectImpl->getLowlevelResourceHash()) + { + getErrorReporting().set("Appearance::set failed, input is not properly initialized or cannot be used with this appearance."); + return false; + } + + const auto result = std::find(valueDataType.begin(), valueDataType.end(), input.getInternalDataType()); + if (result == valueDataType.end()) + { + getErrorReporting().set(::fmt::format("Appearance::set failed, value type does not match input data type {}", EnumToString(input.getInternalDataType()))); + return false; + } + + if (input.getElementCount() != valueElementCount) + { + getErrorReporting().set("Appearance::set failed, element count does not match"); + return false; + } + + return true; + } + + DataInstanceHandle AppearanceImpl::getDataReference(DataFieldHandle dataField, [[maybe_unused]] ramses::internal::EDataType expectedDataType) const + { + const DataInstanceHandle dataReference = getIScene().getDataReference(m_uniformInstance, dataField); + assert(getIScene().getDataLayout(m_uniformLayout).getField(dataField).elementCount == 1u); + assert(getIScene().getDataLayout(getIScene().getLayoutOfDataInstance(dataReference)).getField(DataFieldHandle(0u)).dataType == expectedDataType); + + return dataReference; + } + + template + bool AppearanceImpl::setInputValue(const EffectInputImpl& input, size_t elementCount, const T* valuesIn) + { + return setDataArrayChecked(elementCount, valuesIn, input); + } + template + bool AppearanceImpl::getInputValue(const EffectInputImpl& input, size_t elementCount, T* valuesOut) const + { + return getDataArrayChecked(elementCount, valuesOut, input); + } + + template + bool AppearanceImpl::setDataArrayChecked(size_t elementCount, const T* values, const EffectInputImpl& input) + { + if (input.getSemantics() != EFixedSemantics::Invalid) + { + getErrorReporting().set("Appearance::set failed, can't access value of semantic uniform"); + return false; + } + if (!checkEffectInputValidityAndValueCompatibility(input, elementCount, { TypeToEDataTypeTraits::DataType })) + return false; + + const auto inputIndex = static_cast(input.getInputIndex()); + const BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + const bool isBindable = (bindableInput != nullptr); + if (isBindable && bindableInput->externallyBoundDataObject) + { + getErrorReporting().set("Appearance::set failed, given uniform input is currently bound to a DataObject. Either unbind it from input first or set value on the DataObject itself."); + return false; + } + + const DataFieldHandle dataField(inputIndex); + if (isBindable) + { + const DataInstanceHandle dataReference = getDataReference(dataField, input.getInternalDataType()); + const T* currentValues = ISceneDataArrayAccessor::GetDataArray(&getIScene(), dataReference, DataFieldHandle(0u)); + if (PlatformMemory::Compare(currentValues, values, 1u * sizeof(T)) != 0) + { + ISceneDataArrayAccessor::SetDataArray(&getIScene(), dataReference, DataFieldHandle(0u), 1u, values); + } + } + else + { + static_assert( std::is_same_v == true ); + assert(getIScene().getDataLayout(m_uniformLayout).getField(dataField).elementCount == static_cast(elementCount)); + const T* currentValues = ISceneDataArrayAccessor::GetDataArray(&getIScene(), m_uniformInstance, dataField); + if (PlatformMemory::Compare(currentValues, values, elementCount * sizeof(T)) != 0) + { + ISceneDataArrayAccessor::SetDataArray(&getIScene(), m_uniformInstance, dataField, static_cast(elementCount), values); + } + } + + return true; + } + + template + bool AppearanceImpl::getDataArrayChecked(size_t elementCount, T* values, const EffectInputImpl& input) const + { + if (input.getSemantics() != EFixedSemantics::Invalid) + { + getErrorReporting().set("Appearance::set failed, can't access value of semantic uniform"); + return false; + } + if (!checkEffectInputValidityAndValueCompatibility(input, elementCount, { TypeToEDataTypeTraits::DataType })) + return false; + + const BindableInput* bindableInput = m_bindableInputs.get(static_cast(input.getInputIndex())); + const bool isBindable = (bindableInput != nullptr); + if (isBindable && bindableInput->externallyBoundDataObject) + { + getErrorReporting().set("Appearance::get failed, given uniform input is currently bound to a DataObject. Either unbind it from input first or get value from the DataObject itself."); + return false; + } + + const DataFieldHandle dataField(static_cast(input.getInputIndex())); + if (isBindable) + { + const DataInstanceHandle dataReference = getDataReference(dataField, input.getInternalDataType()); + PlatformMemory::Copy(values, ISceneDataArrayAccessor::GetDataArray(&getIScene(), dataReference, DataFieldHandle(0u)), EnumToSize(input.getInternalDataType())); + } + else + { + PlatformMemory::Copy(values, ISceneDataArrayAccessor::GetDataArray(&getIScene(), m_uniformInstance, dataField), elementCount * EnumToSize(input.getInternalDataType())); + } + + return true; + } + + bool AppearanceImpl::setInputTexture(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler) + { + if (!isFromTheSameSceneAs(textureSampler)) + { + getErrorReporting().set("Appearance::setInputTexture failed, textureSampler is not from the same scene as this appearance"); + return false; + } + + return setInputTextureInternal(input, textureSampler); + } + + bool AppearanceImpl::getInputTexture(const EffectInputImpl& input, const ramses::TextureSampler*& textureSampler) + { + textureSampler = nullptr; + if (!checkEffectInputValidityAndValueCompatibility(input, 1u, + {ramses::internal::EDataType::TextureSampler2D, ramses::internal::EDataType::TextureSampler3D, ramses::internal::EDataType::TextureSamplerCube})) + return false; + + const DataFieldHandle dataField(static_cast(input.getInputIndex())); + const auto samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, dataField); + if (samplerHandle.isValid()) + { + SceneObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSampler); + while (const auto* sampler = iter.getNext()) + { + if (samplerHandle == sampler->impl().getTextureSamplerHandle()) + { + textureSampler = sampler; + break; + } + } + } + return true; + } + + bool AppearanceImpl::getInputTextureMS(const EffectInputImpl& input, const TextureSamplerMS*& textureSampler) + { + textureSampler = nullptr; + if (!checkEffectInputValidityAndValueCompatibility(input, 1u, + {ramses::internal::EDataType::TextureSampler2DMS})) + return false; + + const DataFieldHandle dataField(static_cast(input.getInputIndex())); + const auto samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, dataField); + if (samplerHandle.isValid()) + { + SceneObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSamplerMS); + while (const auto* sampler = iter.getNext()) + { + if (samplerHandle == sampler->impl().getTextureSamplerHandle()) + { + textureSampler = sampler; + break; + } + } + } + return true; + } + + bool AppearanceImpl::getInputTextureExternal(const EffectInputImpl& input, const TextureSamplerExternal*& textureSampler) + { + textureSampler = nullptr; + if (!checkEffectInputValidityAndValueCompatibility(input, 1u, + {ramses::internal::EDataType::TextureSamplerExternal})) + return false; + + const DataFieldHandle dataField(static_cast(input.getInputIndex())); + const auto samplerHandle = getIScene().getDataTextureSamplerHandle(m_uniformInstance, dataField); + if (samplerHandle.isValid()) + { + SceneObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::TextureSamplerExternal); + while (const auto* sampler = iter.getNext()) + { + if (samplerHandle == sampler->impl().getTextureSamplerHandle()) + { + textureSampler = sampler; + break; + } + } + } + return true; + } + + bool AppearanceImpl::bindInput(const EffectInputImpl& input, const DataObjectImpl& dataObject) + { + if (!isFromTheSameSceneAs(dataObject)) + { + getErrorReporting().set("Appearance::bindInput failed, dataObject is not from the same scene as this appearance"); + return false; + } + + if (!checkEffectInputValidityAndValueCompatibility(input, 1u, {DataTypeUtils::ConvertDataTypeToInternal(dataObject.getDataType())})) + return false; + + const auto inputIndex = static_cast(input.getInputIndex()); + BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + if (bindableInput == nullptr) + { + getErrorReporting().set("Appearance::bindInput failed, given uniform input cannot be bound to a DataObject."); + return false; + } + + return bindInputInternal(input, dataObject); + } + + bool AppearanceImpl::unbindInput(const EffectInputImpl& input) + { + const auto inputIndex = static_cast(input.getInputIndex()); + BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + if (bindableInput == nullptr || !bindableInput->externallyBoundDataObject) + { + getErrorReporting().set("Appearance::unbindInput failed, given uniform input is not bound to a DataObject."); + return false; + } + + return unbindInputInternal(input); + } + + bool AppearanceImpl::isInputBound(const EffectInputImpl& input) const + { + const auto inputIndex = static_cast(input.getInputIndex()); + const BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + return (bindableInput != nullptr) && bindableInput->externallyBoundDataObject != nullptr; + } + + bool AppearanceImpl::setInputTextureInternal(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler) + { + if (!checkEffectInputValidityAndValueCompatibility(input, 1u, {textureSampler.getTextureDataType()})) + return false; + + const DataFieldHandle dataField(static_cast(input.getInputIndex())); + const TextureSamplerHandle samplerHandle = textureSampler.getTextureSamplerHandle(); + getIScene().setDataTextureSamplerHandle(m_uniformInstance, dataField, samplerHandle); + return true; + } + + bool AppearanceImpl::bindInputInternal(const EffectInputImpl& input, const DataObjectImpl& dataObject) + { + const auto inputIndex = static_cast(input.getInputIndex()); + const DataFieldHandle dataField(inputIndex); + getIScene().setDataReference(m_uniformInstance, dataField, dataObject.getDataReference()); + + BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + assert(bindableInput != nullptr); + bindableInput->externallyBoundDataObject = &dataObject; + + return true; + } + + bool AppearanceImpl::unbindInputInternal(const EffectInputImpl& input) + { + const auto inputIndex = static_cast(input.getInputIndex()); + BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + const DataFieldHandle dataField(inputIndex); + getIScene().setDataReference(m_uniformInstance, dataField, bindableInput->dataReference); + bindableInput->externallyBoundDataObject = nullptr; + + return true; + } + + DataLayoutHandle AppearanceImpl::getUniformDataLayout() const + { + return m_uniformLayout; + } + + const DataObject* AppearanceImpl::getBoundDataObject(const EffectInputImpl& input) const + { + const auto inputIndex = static_cast(input.getInputIndex()); + const BindableInput* bindableInput = m_bindableInputs.get(inputIndex); + if (bindableInput && bindableInput->externallyBoundDataObject) + return &RamsesObjectTypeUtils::ConvertTo(bindableInput->externallyBoundDataObject->getRamsesObject()); + + return nullptr; + } + + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const bool*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, bool*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const int32_t*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, int32_t*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const float*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, float*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec2i*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec2i*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec3i*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec3i*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec4i*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec4i*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec2f*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec2f*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec3f*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec3f*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const vec4f*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, vec4f*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const matrix22f*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, matrix22f*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const matrix33f*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, matrix33f*) const; + template bool AppearanceImpl::setInputValue(const EffectInputImpl&, size_t, const matrix44f*); + template bool AppearanceImpl::getInputValue(const EffectInputImpl&, size_t, matrix44f*) const; +} diff --git a/src/client/impl/AppearanceImpl.h b/src/client/impl/AppearanceImpl.h new file mode 100644 index 000000000..b7bb0547f --- /dev/null +++ b/src/client/impl/AppearanceImpl.h @@ -0,0 +1,126 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// client api +#include "ramses/client/Appearance.h" +#include "ramses/framework/AppearanceEnums.h" + +// internal +#include "SceneObjectImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" + +#include +#include + +namespace ramses::internal +{ + struct DataReference; + class IScene; + class EffectImpl; + class EffectInputImpl; + class TextureSamplerImpl; + class DataObjectImpl; + + class AppearanceImpl final : public SceneObjectImpl + { + public: + AppearanceImpl(SceneImpl& scene, std::string_view appearancename); + ~AppearanceImpl() override; + + void initializeFrameworkData(const EffectImpl& effect); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + [[nodiscard]] const EffectImpl* getEffectImpl() const; + [[nodiscard]] const Effect& getEffect() const; + + bool setBlendingFactors(EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha); + bool getBlendingFactors(EBlendFactor& srcColor, EBlendFactor& destColor, EBlendFactor& srcAlpha, EBlendFactor& destAlpha) const; + bool setBlendingOperations(EBlendOperation operationColor, EBlendOperation operationAlpha); + bool getBlendingOperations(EBlendOperation& operationColor, EBlendOperation& operationAlpha) const; + bool setBlendingColor(const vec4f& color); + bool getBlendingColor(vec4f& color) const; + bool setDepthFunction(EDepthFunc func); + bool getDepthFunction(EDepthFunc& func) const; + bool setDepthWrite(EDepthWrite flag); + bool getDepthWriteMode(EDepthWrite& mode) const; + bool setScissorTest(EScissorTest flag, int16_t x, int16_t y, uint16_t width, uint16_t height); + bool getScissorTestState(EScissorTest& mode) const; + bool getScissorRegion(int16_t& x, int16_t& y, uint16_t& width, uint16_t& height) const; + bool setStencilFunc(EStencilFunc func, uint8_t ref, uint8_t mask); + bool getStencilFunc(EStencilFunc& func, uint8_t& ref, uint8_t& mask) const; + bool setStencilOperation(EStencilOperation sfail, EStencilOperation dpfail, EStencilOperation dppass); + bool getStencilOperation(EStencilOperation& sfail, EStencilOperation& dpfail, EStencilOperation& dppass) const; + bool setCullingMode(ECullMode mode); + bool getCullingMode(ECullMode& mode) const; + bool setDrawMode(EDrawMode mode); + bool getDrawMode(EDrawMode& mode) const; + bool setColorWriteMask(bool writeRed, bool writeGreen, bool writeBlue, bool writeAlpha); + bool getColorWriteMask(bool& writeRed, bool& writeGreen, bool& writeBlue, bool& writeAlpha) const; + + template + bool setInputValue(const EffectInputImpl& input, size_t elementCount, const T* valuesIn); + template + bool getInputValue(const EffectInputImpl& input, size_t elementCount, T* valuesOut) const; + + bool setInputTexture(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler); + bool getInputTexture(const EffectInputImpl& input, const ramses::TextureSampler*& textureSampler); + bool getInputTextureMS(const EffectInputImpl& input, const TextureSamplerMS*& textureSampler); + bool getInputTextureExternal(const EffectInputImpl& input, const TextureSamplerExternal*& textureSampler); + + bool bindInput(const EffectInputImpl& input, const DataObjectImpl& dataObject); + bool unbindInput(const EffectInputImpl& input); + [[nodiscard]] bool isInputBound(const EffectInputImpl& input) const; + [[nodiscard]] const DataObject* getBoundDataObject(const EffectInputImpl& input) const; + + [[nodiscard]] RenderStateHandle getRenderStateHandle() const; + [[nodiscard]] DataInstanceHandle getUniformDataInstance() const; + [[nodiscard]] DataLayoutHandle getUniformDataLayout() const; + + private: + void createUniformDataInstance(const EffectImpl& effect); + + [[nodiscard]] bool checkEffectInputValidityAndValueCompatibility(const EffectInputImpl& input, size_t valueElementCount, std::initializer_list valueDataType) const; + [[nodiscard]] DataInstanceHandle getDataReference(DataFieldHandle dataField, ramses::internal::EDataType expectedDataType) const; + + template + bool setDataArrayChecked(size_t elementCount, const T* values, const EffectInputImpl& input); + template + bool getDataArrayChecked(size_t elementCount, T* values, const EffectInputImpl& input) const; + + bool setInputTextureInternal(const EffectInputImpl& input, const TextureSamplerImpl& textureSampler); + bool bindInputInternal(const EffectInputImpl& input, const DataObjectImpl& dataObject); + bool unbindInputInternal(const EffectInputImpl& input); + + void validateEffect(ValidationReportImpl& report) const; + void validateUniforms(ValidationReportImpl& report) const; + + const EffectImpl* m_effectImpl; + RenderStateHandle m_renderStateHandle; + DataLayoutHandle m_uniformLayout; + DataInstanceHandle m_uniformInstance; + + struct BindableInput + { + const DataObjectImpl* externallyBoundDataObject = nullptr; + DataInstanceHandle dataReference; + }; + + using BindableInputMap = HashMap; + BindableInputMap m_bindableInputs; + }; +} diff --git a/client/ramses-client/impl/AppearanceUtils.h b/src/client/impl/AppearanceUtils.h similarity index 89% rename from client/ramses-client/impl/AppearanceUtils.h rename to src/client/impl/AppearanceUtils.h index d9ddc47d8..a80288214 100644 --- a/client/ramses-client/impl/AppearanceUtils.h +++ b/src/client/impl/AppearanceUtils.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_APPEARANCEUTILS_H -#define RAMSES_APPEARANCEUTILS_H +#pragma once -#include "ramses-framework-api/AppearanceEnums.h" -#include "SceneAPI/RenderState.h" +#include "ramses/framework/AppearanceEnums.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" -namespace ramses +namespace ramses::internal { class AppearanceUtils { @@ -41,5 +40,3 @@ namespace ramses } }; } - -#endif diff --git a/src/client/impl/ArrayBuffer.cpp b/src/client/impl/ArrayBuffer.cpp new file mode 100644 index 000000000..b8f9d91dc --- /dev/null +++ b/src/client/impl/ArrayBuffer.cpp @@ -0,0 +1,90 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/ArrayBuffer.h" + +// Internal +#include "impl/ArrayBufferImpl.h" +#include "impl/ErrorReporting.h" + +namespace ramses +{ + ArrayBuffer::ArrayBuffer(std::unique_ptr impl) + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + template bool ArrayBuffer::updateDataInternal(uint32_t firstElement, uint32_t numElements, const T* bufferData) + { + if (GetEDataType() != m_impl.getDataType()) + { + m_impl.getErrorReporting().set("ArrayBuffer::updateData: Wrong data type used to update buffer!"); + return false; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) we store all data types as bytes internally + const bool status = m_impl.updateData(firstElement, numElements, reinterpret_cast(bufferData)); + LOG_HL_CLIENT_API3(status, firstElement, numElements, LOG_API_GENERIC_PTR_STRING(bufferData)); + return status; + } + + uint32_t ArrayBuffer::getMaximumNumberOfElements() const + { + return m_impl.getMaximumNumberOfElements(); + } + + uint32_t ArrayBuffer::getUsedNumberOfElements() const + { + return m_impl.getUsedNumberOfElements(); + } + + EDataType ArrayBuffer::getDataType() const + { + return m_impl.getDataType(); + } + + template bool ArrayBuffer::getDataInternal(T* buffer, uint32_t numElements) const + { + if (GetEDataType() != m_impl.getDataType()) + { + m_impl.getErrorReporting().set("ArrayBuffer::getData: Wrong data type used to get data!"); + return false; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) we store all data types as bytes internally + return m_impl.getData(reinterpret_cast(buffer), numElements); + } + + internal::ArrayBufferImpl& ArrayBuffer::impl() + { + return m_impl; + } + + const internal::ArrayBufferImpl& ArrayBuffer::impl() const + { + return m_impl; + } + + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const uint16_t*); + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const uint32_t*); + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const float*); + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const vec2f*); + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const vec3f*); + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const vec4f*); + template RAMSES_API bool ArrayBuffer::updateDataInternal(uint32_t, uint32_t, const std::byte*); + + template RAMSES_API bool ArrayBuffer::getDataInternal(uint16_t*, uint32_t) const; + template RAMSES_API bool ArrayBuffer::getDataInternal(uint32_t*, uint32_t) const; + template RAMSES_API bool ArrayBuffer::getDataInternal(float*, uint32_t) const; + template RAMSES_API bool ArrayBuffer::getDataInternal(vec2f*, uint32_t) const; + template RAMSES_API bool ArrayBuffer::getDataInternal(vec3f*, uint32_t) const; + template RAMSES_API bool ArrayBuffer::getDataInternal(vec4f*, uint32_t) const; + template RAMSES_API bool ArrayBuffer::getDataInternal(std::byte*, uint32_t) const; +} diff --git a/src/client/impl/ArrayBufferImpl.cpp b/src/client/impl/ArrayBufferImpl.cpp new file mode 100644 index 000000000..a36ad07a9 --- /dev/null +++ b/src/client/impl/ArrayBufferImpl.cpp @@ -0,0 +1,173 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/ArrayBufferImpl.h" +#include "impl/SerializationContext.h" +#include "impl/DataTypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" + +namespace ramses::internal +{ + ArrayBufferImpl::ArrayBufferImpl(SceneImpl& scene, std::string_view databufferName) + : SceneObjectImpl(scene, ERamsesObjectType::ArrayBuffer, databufferName) + { + } + + ArrayBufferImpl::~ArrayBufferImpl() = default; + + void ArrayBufferImpl::initializeFrameworkData(ramses::EDataType dataType, uint32_t numElements) + { + assert(!m_dataBufferHandle.isValid()); + const ramses::internal::EDataBufferType dataBufferType = DataTypeUtils::DeductBufferTypeFromDataType(dataType); + const ramses::internal::EDataType dataTypeInternal = DataTypeUtils::ConvertDataTypeToInternal(dataType); + const uint32_t maximumSizeInBytes = EnumToSize(dataTypeInternal) * numElements; + m_dataBufferHandle = getIScene().allocateDataBuffer(dataBufferType, dataTypeInternal, maximumSizeInBytes, {}); + } + + void ArrayBufferImpl::deinitializeFrameworkData() + { + assert(m_dataBufferHandle.isValid()); + getIScene().releaseDataBuffer(m_dataBufferHandle); + m_dataBufferHandle = DataBufferHandle::Invalid(); + } + + + DataBufferHandle ArrayBufferImpl::getDataBufferHandle() const + { + return m_dataBufferHandle; + } + + uint32_t ArrayBufferImpl::getElementCount() const + { + const GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + return static_cast(dataBuffer.data.size()) / EnumToSize(dataBuffer.dataType); + } + + uint32_t ArrayBufferImpl::getUsedElementCount() const + { + const GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + return static_cast(dataBuffer.usedSize) / EnumToSize(dataBuffer.dataType); + } + + ramses::EDataType ArrayBufferImpl::getDataType() const + { + const GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + return DataTypeUtils::ConvertDataTypeFromInternal(dataBuffer.dataType); + } + + bool ArrayBufferImpl::getData(std::byte* buffer, uint32_t numElements) const + { + const auto& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + const uint32_t dataSizeToCopy = std::min(numElements * EnumToSize(dataBuffer.dataType), static_cast(dataBuffer.data.size())); + PlatformMemory::Copy(buffer, dataBuffer.data.data(), dataSizeToCopy); + + return true; + } + + bool ArrayBufferImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << m_dataBufferHandle; + + return true; + } + + bool ArrayBufferImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + inStream >> m_dataBufferHandle; + + return true; + } + + void ArrayBufferImpl::onValidate(ValidationReportImpl& report) const + { + SceneObjectImpl::onValidate(report); + + const auto& iscene = getIScene(); + + const bool isInitialized = (iscene.getDataBuffer(getDataBufferHandle()).usedSize > 0u); + + bool usedAsInput = false; + for (DataInstanceHandle di(0u); di < iscene.getDataInstanceCount() && !usedAsInput; ++di) + { + if (!iscene.isDataInstanceAllocated(di)) + continue; + + const auto dlh = iscene.getLayoutOfDataInstance(di); + const DataLayout& dl = iscene.getDataLayout(dlh); + for (DataFieldHandle df(0u); df < dl.getFieldCount(); ++df) + { + switch (dl.getField(df).dataType) + { + case ramses::internal::EDataType::Indices: + case ramses::internal::EDataType::UInt16Buffer: + case ramses::internal::EDataType::FloatBuffer: + case ramses::internal::EDataType::Vector2Buffer: + case ramses::internal::EDataType::Vector3Buffer: + case ramses::internal::EDataType::Vector4Buffer: + { + const auto& resource = iscene.getDataResource(di, df); + if (resource.dataBuffer == getDataBufferHandle()) + usedAsInput = true; + break; + } + default: + break; + } + } + } + + for (PickableObjectHandle po(0u); po < iscene.getPickableObjectCount() && !usedAsInput; ++po) + { + if (iscene.isPickableObjectAllocated(po) && iscene.getPickableObject(po).geometryHandle == getDataBufferHandle()) + usedAsInput = true; + } + + if (usedAsInput && !isInitialized) + report.add(EIssueType::Warning, "DataBuffer is used as geometry input but there is no data set, this could lead to graphical glitches if actually rendered.", &getRamsesObject()); + + if (!usedAsInput) + report.add(EIssueType::Warning, "DataBuffer is not used anywhere, destroy it if not needed.", &getRamsesObject()); + } + + bool ArrayBufferImpl::updateData(uint32_t firstElement, uint32_t numElements, const std::byte* bufferData) + { + const GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + const size_t maximumSizeInBytes = dataBuffer.data.size(); + const uint32_t offsetInBytes = firstElement * EnumToSize(dataBuffer.dataType); + const uint32_t dataSizeInBytes = numElements * EnumToSize(dataBuffer.dataType); + if (offsetInBytes + dataSizeInBytes > maximumSizeInBytes) + { + getErrorReporting().set("DataBuffer::update failed - trying to write data beyond maximum size", *this); + return false; + } + + getIScene().updateDataBuffer(m_dataBufferHandle, offsetInBytes, dataSizeInBytes, bufferData); + + return true; + } + + uint32_t ArrayBufferImpl::getMaximumNumberOfElements() const + { + const GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + return static_cast(dataBuffer.data.size()) / EnumToSize(dataBuffer.dataType); + } + + uint32_t ArrayBufferImpl::getUsedNumberOfElements() const + { + const GeometryDataBuffer& dataBuffer = getIScene().getDataBuffer(m_dataBufferHandle); + return dataBuffer.usedSize / EnumToSize(dataBuffer.dataType); + } +} diff --git a/src/client/impl/ArrayBufferImpl.h b/src/client/impl/ArrayBufferImpl.h new file mode 100644 index 000000000..ab73cce67 --- /dev/null +++ b/src/client/impl/ArrayBufferImpl.h @@ -0,0 +1,45 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "SceneObjectImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/framework/EDataType.h" + +#include + +namespace ramses::internal +{ + class ArrayBufferImpl : public SceneObjectImpl + { + public: + ArrayBufferImpl(SceneImpl& scene, std::string_view databufferName); + ~ArrayBufferImpl() override; + + void initializeFrameworkData(ramses::EDataType dataType, uint32_t numElements); + void deinitializeFrameworkData() override; + bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext) override; + + void onValidate(ValidationReportImpl& report) const override; + + bool updateData(uint32_t firstElement, uint32_t numElements, const std::byte* bufferData); + + [[nodiscard]] DataBufferHandle getDataBufferHandle() const; + [[nodiscard]] uint32_t getMaximumNumberOfElements() const; + [[nodiscard]] uint32_t getElementCount() const; + [[nodiscard]] uint32_t getUsedNumberOfElements() const; + [[nodiscard]] uint32_t getUsedElementCount() const; + [[nodiscard]] ramses::EDataType getDataType() const; + [[nodiscard]] bool getData(std::byte* buffer, uint32_t numElements) const; + + private: + DataBufferHandle m_dataBufferHandle; + }; +} diff --git a/client/ramses-client-api/ArrayResource.cpp b/src/client/impl/ArrayResource.cpp similarity index 63% rename from client/ramses-client-api/ArrayResource.cpp rename to src/client/impl/ArrayResource.cpp index ca6259cab..00277b7c5 100644 --- a/client/ramses-client-api/ArrayResource.cpp +++ b/src/client/impl/ArrayResource.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/ArrayResource.h" +#include "ramses/client/ArrayResource.h" -#include "ArrayResourceImpl.h" +#include "impl/ArrayResourceImpl.h" namespace ramses { - ArrayResource::ArrayResource(std::unique_ptr impl) + ArrayResource::ArrayResource(std::unique_ptr impl) : Resource{ std::move(impl) } - , m_impl{ static_cast(Resource::m_impl) } + , m_impl{ static_cast(Resource::m_impl) } { } @@ -28,4 +28,13 @@ namespace ramses return m_impl.getElementType(); } + internal::ArrayResourceImpl& ArrayResource::impl() + { + return m_impl; + } + + const internal::ArrayResourceImpl& ArrayResource::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/ArrayResourceImpl.cpp b/src/client/impl/ArrayResourceImpl.cpp similarity index 55% rename from client/ramses-client/impl/ArrayResourceImpl.cpp rename to src/client/impl/ArrayResourceImpl.cpp index 60d5c5bce..a21382640 100644 --- a/client/ramses-client/impl/ArrayResourceImpl.cpp +++ b/src/client/impl/ArrayResourceImpl.cpp @@ -6,47 +6,47 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ArrayResourceImpl.h" +#include "impl/ArrayResourceImpl.h" -namespace ramses +namespace ramses::internal { - ArrayResourceImpl::ArrayResourceImpl(ramses_internal::ResourceHashUsage arrayHash, SceneImpl& scene, std::string_view name) + ArrayResourceImpl::ArrayResourceImpl(ResourceHashUsage arrayHash, SceneImpl& scene, std::string_view name) : ResourceImpl(ERamsesObjectType::ArrayResource, std::move(arrayHash), scene, name) , m_elementCount(0) - , m_elementType(EDataType::UInt16) + , m_elementType(ramses::EDataType::UInt16) { } - ArrayResourceImpl::~ArrayResourceImpl() - { - } + ArrayResourceImpl::~ArrayResourceImpl() = default; - void ArrayResourceImpl::initializeFromFrameworkData(uint32_t elementCount, EDataType elementType) + void ArrayResourceImpl::initializeFromFrameworkData(uint32_t elementCount, ramses::EDataType elementType) { m_elementCount = elementCount; m_elementType = elementType; } - status_t ArrayResourceImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool ArrayResourceImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(ResourceImpl::serialize(outStream, serializationContext)); + if (!ResourceImpl::serialize(outStream, serializationContext)) + return false; outStream << m_elementCount; outStream << static_cast(m_elementType); - return StatusOK; + return true; } - status_t ArrayResourceImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool ArrayResourceImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(ResourceImpl::deserialize(inStream, serializationContext)); + if (!ResourceImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_elementCount; uint32_t enumInt = 0u; inStream >> enumInt; - m_elementType = static_cast(enumInt); + m_elementType = static_cast(enumInt); - return StatusOK; + return true; } uint32_t ArrayResourceImpl::getElementCount() const @@ -54,7 +54,7 @@ namespace ramses return m_elementCount; } - EDataType ArrayResourceImpl::getElementType() const + ramses::EDataType ArrayResourceImpl::getElementType() const { return m_elementType; } diff --git a/client/ramses-client/impl/ArrayResourceImpl.h b/src/client/impl/ArrayResourceImpl.h similarity index 50% rename from client/ramses-client/impl/ArrayResourceImpl.h rename to src/client/impl/ArrayResourceImpl.h index fb2709169..1483af6e2 100644 --- a/client/ramses-client/impl/ArrayResourceImpl.h +++ b/src/client/impl/ArrayResourceImpl.h @@ -6,34 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ARRAYRESOURCEIMPL_H -#define RAMSES_ARRAYRESOURCEIMPL_H +#pragma once // internal -#include "ResourceImpl.h" -#include "ramses-framework-api/EDataType.h" +#include "impl/ResourceImpl.h" +#include "ramses/framework/EDataType.h" #include -namespace ramses +namespace ramses::internal { class ArrayResourceImpl final : public ResourceImpl { public: - ArrayResourceImpl(ramses_internal::ResourceHashUsage arrayHash, SceneImpl& scene, std::string_view name); + ArrayResourceImpl(ResourceHashUsage arrayHash, SceneImpl& scene, std::string_view name); ~ArrayResourceImpl() override; - void initializeFromFrameworkData(uint32_t elementCount, EDataType elementType); - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + void initializeFromFrameworkData(uint32_t elementCount, ramses::EDataType elementType); + bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext) override; - uint32_t getElementCount() const; - EDataType getElementType() const; + [[nodiscard]] uint32_t getElementCount() const; + [[nodiscard]] ramses::EDataType getElementType() const; private: - uint32_t m_elementCount; - EDataType m_elementType; + uint32_t m_elementCount; + ramses::EDataType m_elementType; }; } - -#endif diff --git a/client/ramses-client-api/ClientObject.cpp b/src/client/impl/AttributeInput.cpp similarity index 62% rename from client/ramses-client-api/ClientObject.cpp rename to src/client/impl/AttributeInput.cpp index 698ec1d33..e3173c18c 100644 --- a/client/ramses-client-api/ClientObject.cpp +++ b/src/client/impl/AttributeInput.cpp @@ -6,17 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -// API -#include "ramses-client-api/ClientObject.h" - -// internal -#include "ClientObjectImpl.h" +#include "ramses/client/AttributeInput.h" +#include "impl/EffectInputImpl.h" namespace ramses { - ClientObject::ClientObject(std::unique_ptr impl) - : RamsesObject{ std::move(impl) } - , m_impl{ static_cast(RamsesObject::m_impl) } + AttributeInput::AttributeInput() + : EffectInput{ std::make_unique() } + { + } + + EEffectAttributeSemantic AttributeInput::getSemantics() const { + return m_impl->getAttributeSemantics(); } } diff --git a/client/ramses-client-api/BlitPass.cpp b/src/client/impl/BlitPass.cpp similarity index 64% rename from client/ramses-client-api/BlitPass.cpp rename to src/client/impl/BlitPass.cpp index df9be3c28..a70c924e3 100644 --- a/client/ramses-client-api/BlitPass.cpp +++ b/src/client/impl/BlitPass.cpp @@ -7,22 +7,22 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/BlitPass.h" +#include "ramses/client/BlitPass.h" // internal -#include "BlitPassImpl.h" +#include "impl/BlitPassImpl.h" namespace ramses { - BlitPass::BlitPass(std::unique_ptr impl) + BlitPass::BlitPass(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } - status_t BlitPass::setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height) + bool BlitPass::setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height) { - const status_t status = m_impl.setBlittingRegion(sourceX, sourceY, destinationX, destinationY, width, height); + const bool status = m_impl.setBlittingRegion(sourceX, sourceY, destinationX, destinationY, width, height); LOG_HL_CLIENT_API6(status, sourceX, sourceY, destinationX, destinationY, width, height); return status; } @@ -42,9 +42,9 @@ namespace ramses return m_impl.getDestinationRenderBuffer(); } - status_t BlitPass::setRenderOrder(int32_t renderOrder) + bool BlitPass::setRenderOrder(int32_t renderOrder) { - const status_t status = m_impl.setRenderOrder(renderOrder); + const bool status = m_impl.setRenderOrder(renderOrder); LOG_HL_CLIENT_API1(status, renderOrder); return status; } @@ -54,9 +54,9 @@ namespace ramses return m_impl.getRenderOrder(); } - status_t BlitPass::setEnabled(bool enable) + bool BlitPass::setEnabled(bool enable) { - const status_t status = m_impl.setEnabled(enable); + const bool status = m_impl.setEnabled(enable); LOG_HL_CLIENT_API1(status, enable); return status; } @@ -65,4 +65,14 @@ namespace ramses { return m_impl.isEnabled(); } + + internal::BlitPassImpl& BlitPass::impl() + { + return m_impl; + } + + const internal::BlitPassImpl& BlitPass::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/BlitPassImpl.cpp b/src/client/impl/BlitPassImpl.cpp similarity index 52% rename from client/ramses-client/impl/BlitPassImpl.cpp rename to src/client/impl/BlitPassImpl.cpp index ca03d956b..1b0662532 100644 --- a/client/ramses-client/impl/BlitPassImpl.cpp +++ b/src/client/impl/BlitPassImpl.cpp @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "BlitPassImpl.h" -#include "RenderBufferImpl.h" -#include "SerializationContext.h" -#include "SceneAPI/BlitPass.h" -#include "Scene/ClientScene.h" -#include "ramses-client-api/RenderBuffer.h" -#include "RamsesObjectTypeUtils.h" - -namespace ramses +#include "impl/BlitPassImpl.h" +#include "ramses/client/RenderBuffer.h" +#include "impl/RenderBufferImpl.h" +#include "impl/SerializationContext.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "internal/SceneGraph/Scene/ClientScene.h" + +namespace ramses::internal { BlitPassImpl::BlitPassImpl(SceneImpl& scene, std::string_view blitpassName) : SceneObjectImpl(scene, ERamsesObjectType::BlitPass, blitpassName) { } - BlitPassImpl::~BlitPassImpl() - { - } + BlitPassImpl::~BlitPassImpl() = default; void BlitPassImpl::initializeFrameworkData(const RenderBufferImpl& sourceRenderBuffer, const RenderBufferImpl& destinationRenderBuffer) { @@ -31,9 +30,9 @@ namespace ramses m_sourceRenderBufferImpl = &sourceRenderBuffer; m_destinationRenderBufferImpl = &destinationRenderBuffer; - const ramses_internal::RenderBufferHandle sourceRenderBufferHandle = sourceRenderBuffer.getRenderBufferHandle(); - const ramses_internal::RenderBufferHandle destinationRenderBufferHandle = destinationRenderBuffer.getRenderBufferHandle(); - m_blitPassHandle = getIScene().allocateBlitPass(sourceRenderBufferHandle, destinationRenderBufferHandle); + const RenderBufferHandle sourceRenderBufferHandle = sourceRenderBuffer.getRenderBufferHandle(); + const RenderBufferHandle destinationRenderBufferHandle = destinationRenderBuffer.getRenderBufferHandle(); + m_blitPassHandle = getIScene().allocateBlitPass(sourceRenderBufferHandle, destinationRenderBufferHandle, {}); setBlittingRegion(0u, 0u, 0u, 0u, sourceRenderBuffer.getWidth(), sourceRenderBuffer.getHeight()); } @@ -42,42 +41,44 @@ namespace ramses { assert(m_blitPassHandle.isValid()); getIScene().releaseBlitPass(m_blitPassHandle); - m_blitPassHandle = ramses_internal::BlitPassHandle::Invalid(); + m_blitPassHandle = BlitPassHandle::Invalid(); } - ramses::status_t BlitPassImpl::setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height) + bool BlitPassImpl::setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height) { - const ramses_internal::BlitPass& blitPass = getIScene().getBlitPass(m_blitPassHandle); - const ramses_internal::RenderBuffer& renderBufferSrc = getIScene().getRenderBuffer(blitPass.sourceRenderBuffer); - const ramses_internal::RenderBuffer& renderBufferDst = getIScene().getRenderBuffer(blitPass.destinationRenderBuffer); + const ramses::internal::BlitPass& blitPass = getIScene().getBlitPass(m_blitPassHandle); + const ramses::internal::RenderBuffer& renderBufferSrc = getIScene().getRenderBuffer(blitPass.sourceRenderBuffer); + const ramses::internal::RenderBuffer& renderBufferDst = getIScene().getRenderBuffer(blitPass.destinationRenderBuffer); if (sourceX + width > renderBufferSrc.width || sourceY + height > renderBufferSrc.height) { - return addErrorEntry("BlitPass::setBlittingRegion failed - invalid source region"); + getErrorReporting().set("BlitPass::setBlittingRegion failed - invalid source region", *this); + return false; } if (destinationX + width > renderBufferDst.width || destinationY + height > renderBufferDst.height) { - return addErrorEntry("BlitPass::setBlittingRegion failed - invalid destination region"); + getErrorReporting().set("BlitPass::setBlittingRegion failed - invalid destination region", *this); + return false; } - const ramses_internal::PixelRectangle sourceRegion = { + const PixelRectangle sourceRegion = { sourceX, sourceY, static_cast(width), static_cast(height) }; - const ramses_internal::PixelRectangle destinationRegion = { + const PixelRectangle destinationRegion = { destinationX, destinationY, static_cast(width), static_cast(height) }; getIScene().setBlitPassRegions(m_blitPassHandle, sourceRegion, destinationRegion); - return StatusOK; + return true; } - ramses::status_t BlitPassImpl::setRenderOrder(int32_t renderOrder) + bool BlitPassImpl::setRenderOrder(int32_t renderOrder) { getIScene().setBlitPassRenderOrder(m_blitPassHandle, renderOrder); - return StatusOK; + return true; } int32_t BlitPassImpl::getRenderOrder() const @@ -85,11 +86,11 @@ namespace ramses return getIScene().getBlitPass(m_blitPassHandle).renderOrder; } - ramses::status_t BlitPassImpl::setEnabled(bool isEnabeld) + bool BlitPassImpl::setEnabled(bool isEnabeld) { getIScene().setBlitPassEnabled(m_blitPassHandle, isEnabeld); - return StatusOK; + return true; } bool BlitPassImpl::isEnabled() const @@ -97,14 +98,15 @@ namespace ramses return getIScene().getBlitPass(m_blitPassHandle).isEnabled; } - ramses_internal::BlitPassHandle BlitPassImpl::getBlitPassHandle() const + BlitPassHandle BlitPassImpl::getBlitPassHandle() const { return m_blitPassHandle; } - status_t BlitPassImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool BlitPassImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << m_blitPassHandle; assert(m_sourceRenderBufferImpl != nullptr); @@ -112,57 +114,57 @@ namespace ramses outStream << serializationContext.getIDForObject(m_sourceRenderBufferImpl); outStream << serializationContext.getIDForObject(m_destinationRenderBufferImpl); - return StatusOK; + return true; } - status_t BlitPassImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool BlitPassImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_blitPassHandle; - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_sourceRenderBufferImpl); - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_destinationRenderBufferImpl); + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_sourceRenderBufferImpl); + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_destinationRenderBufferImpl); serializationContext.addForDependencyResolve(this); - return StatusOK; + return true; } - status_t BlitPassImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + bool BlitPassImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::resolveDeserializationDependencies(serializationContext)); + if (!SceneObjectImpl::resolveDeserializationDependencies(serializationContext)) + return false; serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_sourceRenderBufferImpl); serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_destinationRenderBufferImpl); - return StatusOK; + return true; } - status_t BlitPassImpl::validate() const + void BlitPassImpl::onValidate(ValidationReportImpl& report) const { - status_t status = SceneObjectImpl::validate(); + SceneObjectImpl::onValidate(report); - const ramses_internal::BlitPass& blitPass = getIScene().getBlitPass(m_blitPassHandle); + const ramses::internal::BlitPass& blitPass = getIScene().getBlitPass(m_blitPassHandle); if (!getIScene().isRenderBufferAllocated(blitPass.sourceRenderBuffer)) - status = addValidationMessage(EValidationSeverity::Error, "blitpass references a deleted source render buffer"); + report.add(EIssueType::Error, "blitpass references a deleted source render buffer", &getRamsesObject()); if (!getIScene().isRenderBufferAllocated(blitPass.destinationRenderBuffer)) - status = addValidationMessage(EValidationSeverity::Error, "blitpass references a deleted destination render buffer"); - - return status; + report.add(EIssueType::Error, "blitpass references a deleted destination render buffer", &getRamsesObject()); } - const RenderBuffer& BlitPassImpl::getSourceRenderBuffer() const + const ramses::RenderBuffer& BlitPassImpl::getSourceRenderBuffer() const { assert(nullptr != m_sourceRenderBufferImpl); - return RamsesObjectTypeUtils::ConvertTo(m_sourceRenderBufferImpl->getRamsesObject()); + return RamsesObjectTypeUtils::ConvertTo(m_sourceRenderBufferImpl->getRamsesObject()); } - const RenderBuffer& BlitPassImpl::getDestinationRenderBuffer() const + const ramses::RenderBuffer& BlitPassImpl::getDestinationRenderBuffer() const { assert(nullptr != m_destinationRenderBufferImpl); - return RamsesObjectTypeUtils::ConvertTo(m_destinationRenderBufferImpl->getRamsesObject()); + return RamsesObjectTypeUtils::ConvertTo(m_destinationRenderBufferImpl->getRamsesObject()); } void BlitPassImpl::getBlittingRegion(uint32_t& sourceX, uint32_t& sourceY, uint32_t& destinationX, uint32_t& destinationY, uint32_t& width, uint32_t& height) const diff --git a/src/client/impl/BlitPassImpl.h b/src/client/impl/BlitPassImpl.h new file mode 100644 index 000000000..2b95b6b75 --- /dev/null +++ b/src/client/impl/BlitPassImpl.h @@ -0,0 +1,57 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "SceneObjectImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" + +#include + +namespace ramses +{ + class RenderBuffer; +} + +namespace ramses::internal +{ + class RenderBufferImpl; + + class BlitPassImpl final : public SceneObjectImpl + { + public: + BlitPassImpl(SceneImpl& scene, std::string_view blitpassName); + ~BlitPassImpl() override; + + void initializeFrameworkData(const RenderBufferImpl& sourceRenderBuffer, const RenderBufferImpl& destinationRenderBuffer); + void deinitializeFrameworkData() override; + bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + [[nodiscard]] const ramses::RenderBuffer& getSourceRenderBuffer() const; + [[nodiscard]] const ramses::RenderBuffer& getDestinationRenderBuffer() const; + + bool setBlittingRegion(uint32_t sourceX, uint32_t sourceY, uint32_t destinationX, uint32_t destinationY, uint32_t width, uint32_t height); + void getBlittingRegion(uint32_t& sourceX, uint32_t& sourceY, uint32_t& destinationX, uint32_t& destinationY, uint32_t& width, uint32_t& height) const; + + bool setRenderOrder(int32_t renderOrder); + [[nodiscard]] int32_t getRenderOrder() const; + + bool setEnabled(bool isEnabeld); + [[nodiscard]] bool isEnabled() const; + + [[nodiscard]] BlitPassHandle getBlitPassHandle() const; + + private: + BlitPassHandle m_blitPassHandle; + const RenderBufferImpl* m_sourceRenderBufferImpl = nullptr; + const RenderBufferImpl* m_destinationRenderBufferImpl = nullptr; + }; +} diff --git a/client/ramses-client-api/Camera.cpp b/src/client/impl/Camera.cpp similarity index 62% rename from client/ramses-client-api/Camera.cpp rename to src/client/impl/Camera.cpp index 53a423594..c3c3a6f92 100644 --- a/client/ramses-client-api/Camera.cpp +++ b/src/client/impl/Camera.cpp @@ -7,30 +7,30 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/DataObject.h" +#include "ramses/client/Camera.h" +#include "ramses/client/DataObject.h" // internal -#include "CameraNodeImpl.h" +#include "impl/CameraNodeImpl.h" namespace ramses { - Camera::Camera(std::unique_ptr impl) + Camera::Camera(std::unique_ptr impl) : Node{ std::move(impl) } - , m_impl{ static_cast(Node::m_impl) } + , m_impl{ static_cast(Node::m_impl) } { } - status_t Camera::setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane) + bool Camera::setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane) { - const status_t status = m_impl.setFrustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane); + const bool status = m_impl.setFrustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane); LOG_HL_CLIENT_API6(status, leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane); return status; } - status_t Camera::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) + bool Camera::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) { - const status_t status = m_impl.setViewport(x, y, width, height); + const bool status = m_impl.setViewport(x, y, width, height); LOG_HL_CLIENT_API4(status, x, y, width, height); return status; } @@ -85,49 +85,49 @@ namespace ramses return m_impl.getFarPlane(); } - status_t Camera::getProjectionMatrix(matrix44f& projectionMatrix) const + bool Camera::getProjectionMatrix(matrix44f& projectionMatrix) const { return m_impl.getProjectionMatrix(projectionMatrix); } - status_t Camera::bindViewportOffset(const DataObject& offsetData) + bool Camera::bindViewportOffset(const DataObject& offsetData) { - const status_t status = m_impl.bindViewportOffset(offsetData); + const bool status = m_impl.bindViewportOffset(offsetData); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(offsetData)); return status; } - status_t Camera::bindViewportSize(const DataObject& sizeData) + bool Camera::bindViewportSize(const DataObject& sizeData) { - const status_t status = m_impl.bindViewportSize(sizeData); + const bool status = m_impl.bindViewportSize(sizeData); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(sizeData)); return status; } - status_t Camera::bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarPlanesData) + bool Camera::bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarPlanesData) { - const status_t status = m_impl.bindFrustumPlanes(frustumPlanesData, nearFarPlanesData); + const bool status = m_impl.bindFrustumPlanes(frustumPlanesData, nearFarPlanesData); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(frustumPlanesData), LOG_API_RAMSESOBJECT_STRING(nearFarPlanesData)); return status; } - status_t Camera::unbindViewportOffset() + bool Camera::unbindViewportOffset() { - const status_t status = m_impl.unbindViewportOffset(); + const bool status = m_impl.unbindViewportOffset(); LOG_HL_CLIENT_API_NOARG(status); return status; } - status_t Camera::unbindViewportSize() + bool Camera::unbindViewportSize() { - const status_t status = m_impl.unbindViewportSize(); + const bool status = m_impl.unbindViewportSize(); LOG_HL_CLIENT_API_NOARG(status); return status; } - status_t Camera::unbindFrustumPlanes() + bool Camera::unbindFrustumPlanes() { - const status_t status = m_impl.unbindFrustumPlanes(); + const bool status = m_impl.unbindFrustumPlanes(); LOG_HL_CLIENT_API_NOARG(status); return status; } @@ -146,4 +146,14 @@ namespace ramses { return m_impl.isFrustumPlanesBound(); } + + internal::CameraNodeImpl& Camera::impl() + { + return m_impl; + } + + const internal::CameraNodeImpl& Camera::impl() const + { + return m_impl; + } } diff --git a/src/client/impl/CameraNodeImpl.cpp b/src/client/impl/CameraNodeImpl.cpp new file mode 100644 index 000000000..4e6ba5bbe --- /dev/null +++ b/src/client/impl/CameraNodeImpl.cpp @@ -0,0 +1,431 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/DataObject.h" +#include "impl/CameraNodeImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/SerializationContext.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include + +namespace ramses::internal +{ + CameraNodeImpl::CameraNodeImpl(SceneImpl& scene, ERamsesObjectType cameraType, std::string_view cameraName) + : NodeImpl(scene, cameraType, cameraName) + { + } + + CameraNodeImpl::~CameraNodeImpl() = default; + + bool CameraNodeImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!NodeImpl::serialize(outStream, serializationContext)) + return false; + + outStream << m_cameraHandle; + outStream << m_dataLayout; + outStream << m_dataInstance; + outStream << m_viewportDataReferenceLayout; + outStream << m_viewportOffsetDataReference; + outStream << m_viewportSizeDataReference; + outStream << m_frustumPlanesDataReferenceLayout; + outStream << m_frustumPlanesDataReference; + outStream << m_frustumNearFarDataReferenceLayout; + outStream << m_frustumNearFarDataReference; + outStream << m_frustumInitialized; + outStream << m_viewportInitialized; + + return true; + } + + bool CameraNodeImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!NodeImpl::deserialize(inStream, serializationContext)) + return false; + + inStream >> m_cameraHandle; + inStream >> m_dataLayout; + inStream >> m_dataInstance; + inStream >> m_viewportDataReferenceLayout; + inStream >> m_viewportOffsetDataReference; + inStream >> m_viewportSizeDataReference; + inStream >> m_frustumPlanesDataReferenceLayout; + inStream >> m_frustumPlanesDataReference; + inStream >> m_frustumNearFarDataReferenceLayout; + inStream >> m_frustumNearFarDataReference; + inStream >> m_frustumInitialized; + inStream >> m_viewportInitialized; + + return true; + } + + void CameraNodeImpl::initializeFrameworkData() + { + NodeImpl::initializeFrameworkData(); + + // main data instance with all references + const DataFieldInfoVector dataRefFiels(4u, DataFieldInfo{ ramses::internal::EDataType::DataReference }); + m_dataLayout = getIScene().allocateDataLayout(dataRefFiels, {}, {}); + m_dataInstance = getIScene().allocateDataInstance(m_dataLayout, {}); + + // VP offset and size + m_viewportDataReferenceLayout = getIScene().allocateDataLayout({ DataFieldInfo{ramses::internal::EDataType::Vector2I} }, {}, {}); + m_viewportOffsetDataReference = getIScene().allocateDataInstance(m_viewportDataReferenceLayout, {}); + m_viewportSizeDataReference = getIScene().allocateDataInstance(m_viewportDataReferenceLayout, {}); + + // frustum planes + m_frustumPlanesDataReferenceLayout = getIScene().allocateDataLayout({ DataFieldInfo{ramses::internal::EDataType::Vector4F} }, {}, {}); + m_frustumPlanesDataReference = getIScene().allocateDataInstance(m_frustumPlanesDataReferenceLayout, {}); + + // frustum near/far planes + m_frustumNearFarDataReferenceLayout = getIScene().allocateDataLayout({ DataFieldInfo{ramses::internal::EDataType::Vector2F} }, {}, {}); + m_frustumNearFarDataReference = getIScene().allocateDataInstance(m_frustumNearFarDataReferenceLayout, {}); + + // link data references to data instances + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField, m_viewportOffsetDataReference); + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField, m_viewportSizeDataReference); + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField, m_frustumPlanesDataReference); + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField, m_frustumNearFarDataReference); + + // set default values, even though camera is considered invalid if all these are not set explicitly by user + getIScene().setDataSingleVector2i(m_viewportOffsetDataReference, DataFieldHandle{ 0 }, { 0, 0 }); + getIScene().setDataSingleVector2i(m_viewportSizeDataReference, DataFieldHandle{ 0 }, { 16, 16 }); + getIScene().setDataSingleVector4f(m_frustumPlanesDataReference, DataFieldHandle{ 0 }, { -1.f, 1.f, -1.f, 1.f }); + getIScene().setDataSingleVector2f(m_frustumNearFarDataReference, DataFieldHandle{ 0 }, { 0.1f, 1.f }); + + const auto projType = (getType() == ERamsesObjectType::PerspectiveCamera ? ECameraProjectionType::Perspective : ECameraProjectionType::Orthographic); + + m_cameraHandle = getIScene().allocateCamera(projType, getNodeHandle(), m_dataInstance, CameraHandle::Invalid()); + } + + void CameraNodeImpl::deinitializeFrameworkData() + { + getIScene().releaseDataInstance(m_dataInstance); + m_dataInstance = DataInstanceHandle::Invalid(); + getIScene().releaseDataLayout(m_dataLayout); + m_dataLayout = DataLayoutHandle::Invalid(); + getIScene().releaseDataInstance(m_viewportOffsetDataReference); + m_viewportOffsetDataReference = DataInstanceHandle::Invalid(); + getIScene().releaseDataInstance(m_viewportSizeDataReference); + m_viewportSizeDataReference = DataInstanceHandle::Invalid(); + getIScene().releaseDataLayout(m_viewportDataReferenceLayout); + m_viewportDataReferenceLayout = DataLayoutHandle::Invalid(); + getIScene().releaseDataInstance(m_frustumPlanesDataReference); + m_frustumPlanesDataReference = DataInstanceHandle::Invalid(); + getIScene().releaseDataLayout(m_frustumPlanesDataReferenceLayout); + m_frustumPlanesDataReferenceLayout = DataLayoutHandle::Invalid(); + getIScene().releaseDataInstance(m_frustumNearFarDataReference); + m_frustumNearFarDataReference = DataInstanceHandle::Invalid(); + getIScene().releaseDataLayout(m_frustumNearFarDataReferenceLayout); + m_frustumNearFarDataReferenceLayout = DataLayoutHandle::Invalid(); + getIScene().releaseCamera(m_cameraHandle); + m_cameraHandle = CameraHandle::Invalid(); + + NodeImpl::deinitializeFrameworkData(); + } + + void CameraNodeImpl::onValidate(ValidationReportImpl& report) const + { + NodeImpl::onValidate(report); + + if (!m_frustumInitialized && !isFrustumPlanesBound()) + report.add(EIssueType::Error, "Camera frustum is not initialized!", &getRamsesObject()); + + if (!getProjectionParams().isValid()) + report.add(EIssueType::Error, "Camera frustum invalid!", &getRamsesObject()); + + if (!m_viewportInitialized && !(isViewportOffsetBound() && isViewportSizeBound())) + report.add(EIssueType::Error, "Camera viewport is not initialized!", &getRamsesObject()); + + if (getViewportWidth() == 0 || getViewportHeight() == 0) + report.add(EIssueType::Error, "Camera viewport invalid!", &getRamsesObject()); + } + + ECameraProjectionType CameraNodeImpl::getProjectionType() const + { + return getIScene().getCamera(m_cameraHandle).projectionType; + } + + bool CameraNodeImpl::setPerspectiveFrustum(float fovY, float aspectRatio, float nearPlane, float farPlane) + { + assert(isOfType(ERamsesObjectType::PerspectiveCamera)); + + const auto params = ProjectionParams::Perspective(fovY, aspectRatio, nearPlane, farPlane); + if (!params.isValid()) + { + getErrorReporting().set("PerspectiveCamera::setFrustum failed - check validity of given frustum planes", *this); + return false; + } + + updateProjectionParamsOnScene(params); + m_frustumInitialized = true; + + return true; + } + + float CameraNodeImpl::getVerticalFieldOfView() const + { + assert(isOfType(ERamsesObjectType::PerspectiveCamera)); + return ProjectionParams::GetPerspectiveFovY(getProjectionParams()); + } + + float CameraNodeImpl::getAspectRatio() const + { + assert(isOfType(ERamsesObjectType::PerspectiveCamera)); + return ProjectionParams::GetAspectRatio(getProjectionParams()); + } + + bool CameraNodeImpl::setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane) + { + const auto params = ProjectionParams::Frustum( + getIScene().getCamera(m_cameraHandle).projectionType, leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane); + + if (!params.isValid()) + { + getErrorReporting().set("Camera::setFrustum failed - check validity of given frustum planes", *this); + return false; + } + + updateProjectionParamsOnScene(params); + m_frustumInitialized = true; + + return true; + } + + float CameraNodeImpl::getNearPlane() const + { + const auto nearFarData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField); + return getIScene().getDataSingleVector2f(nearFarData, DataFieldHandle{ 0 }).x; + } + + float CameraNodeImpl::getFarPlane() const + { + const auto nearFarData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField); + return getIScene().getDataSingleVector2f(nearFarData, DataFieldHandle{ 0 }).y; + } + + bool CameraNodeImpl::setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) + { + // Set a sane upper limit for viewport to avoid GL_INVALID_VALUE in glViewport() + if (width > 0 && width <= 32768u && height > 0 && height <= 32768u ) + { + getIScene().setDataSingleVector2i(m_viewportOffsetDataReference, DataFieldHandle{ 0 }, { x, y }); + getIScene().setDataSingleVector2i(m_viewportSizeDataReference, DataFieldHandle{ 0 }, { int32_t(width), int32_t(height) }); + m_viewportInitialized = true; + } + else + { + getErrorReporting().set("Camera::setViewport failed - width and height must be within [1, 32768]!", *this); + return false; + } + + return true; + } + + int32_t CameraNodeImpl::getViewportX() const + { + const auto vpOffsetData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField); + return getIScene().getDataSingleVector2i(vpOffsetData, DataFieldHandle{ 0 }).x; + } + + int32_t CameraNodeImpl::getViewportY() const + { + const auto vpOffsetData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField); + return getIScene().getDataSingleVector2i(vpOffsetData, DataFieldHandle{ 0 }).y; + } + + uint32_t CameraNodeImpl::getViewportWidth() const + { + const auto vpSizeData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField); + return getIScene().getDataSingleVector2i(vpSizeData, DataFieldHandle{ 0 }).x; + } + + uint32_t CameraNodeImpl::getViewportHeight() const + { + const auto vpSizeData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField); + return getIScene().getDataSingleVector2i(vpSizeData, DataFieldHandle{ 0 }).y; + } + + float CameraNodeImpl::getLeftPlane() const + { + const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField); + return getIScene().getDataSingleVector4f(frustumData, DataFieldHandle{ 0 }).x; + } + + float CameraNodeImpl::getRightPlane() const + { + const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField); + return getIScene().getDataSingleVector4f(frustumData, DataFieldHandle{ 0 }).y; + } + + float CameraNodeImpl::getBottomPlane() const + { + const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField); + return getIScene().getDataSingleVector4f(frustumData, DataFieldHandle{ 0 }).z; + } + + float CameraNodeImpl::getTopPlane() const + { + const auto frustumData = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField); + return getIScene().getDataSingleVector4f(frustumData, DataFieldHandle{ 0 }).w; + } + + CameraHandle CameraNodeImpl::getCameraHandle() const + { + return m_cameraHandle; + } + + bool CameraNodeImpl::getProjectionMatrix(matrix44f& projectionMatrix) const + { + if (!m_frustumInitialized) + { + getErrorReporting().set("CameraImpl::getProjectionMatrix failed - Camera frustum is not initialized!", *this); + return false; + } + + projectionMatrix = ramses::internal::CameraMatrixHelper::ProjectionMatrix(getProjectionParams()); + + return true; + } + + ProjectionParams CameraNodeImpl::getProjectionParams() const + { + const auto frustumDataInstance = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField); + const auto& frustumData = getIScene().getDataSingleVector4f(frustumDataInstance, DataFieldHandle{ 0 }); + const auto nearFarDataInstance = getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField); + const auto& nearFarData = getIScene().getDataSingleVector2f(nearFarDataInstance, DataFieldHandle{ 0 }); + + return ProjectionParams::Frustum( + getIScene().getCamera(m_cameraHandle).projectionType, + frustumData.x, + frustumData.y, + frustumData.z, + frustumData.w, + nearFarData.x, + nearFarData.y); + } + + void CameraNodeImpl::updateProjectionParamsOnScene(const ProjectionParams& params) + { + getIScene().setDataSingleVector4f(m_frustumPlanesDataReference, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); + getIScene().setDataSingleVector2f(m_frustumNearFarDataReference, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); + } + + bool CameraNodeImpl::bindViewportOffset(const DataObject& offsetData) + { + if (offsetData.getDataType() != ramses::EDataType::Vector2I) + { + getErrorReporting().set("Camera::bindViewportOffset failed, data object must be of type EDataType::Vector2I", *this); + return false; + } + if (!isFromTheSameSceneAs(offsetData.impl())) + { + getErrorReporting().set("Camera::bindViewportOffset failed, viewport offset data object is not from the same scene as this camera", *this); + return false; + } + + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField, offsetData.impl().getDataReference()); + return true; + } + + bool CameraNodeImpl::bindViewportSize(const DataObject& sizeData) + { + if (sizeData.getDataType() != ramses::EDataType::Vector2I) + { + getErrorReporting().set("Camera::bindViewportSize failed, data object must be of type EDataType::Vector2I", *this); + return false; + } + if (!isFromTheSameSceneAs(sizeData.impl())) + { + getErrorReporting().set("Camera::bindViewportSize failed, viewport size data object is not from the same scene as this camera", *this); + return false; + } + + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField, sizeData.impl().getDataReference()); + return true; + } + + bool CameraNodeImpl::bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarData) + { + if (frustumPlanesData.getDataType() != ramses::EDataType::Vector4F || nearFarData.getDataType() != ramses::EDataType::Vector2F) + { + getErrorReporting().set("Camera::bindFrustumPlanes failed, data objects must be of type EDataType::Vector4F and EDataType::Vector2F", *this); + return false; + } + if (!isFromTheSameSceneAs(frustumPlanesData.impl()) || !isFromTheSameSceneAs(nearFarData.impl())) + { + getErrorReporting().set("Camera::bindFrustumPlanes failed, one of the frustum planes data object is not from the same scene as this camera", *this); + return false; + } + + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField, frustumPlanesData.impl().getDataReference()); + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField, nearFarData.impl().getDataReference()); + return true; + } + + bool CameraNodeImpl::unbindViewportOffset() + { + if (isViewportOffsetBound()) + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField, m_viewportOffsetDataReference); + return true; + } + + bool CameraNodeImpl::unbindViewportSize() + { + if (isViewportSizeBound()) + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField, m_viewportSizeDataReference); + return true; + } + + bool CameraNodeImpl::unbindFrustumPlanes() + { + if (isFrustumPlanesBound()) + { + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField, m_frustumPlanesDataReference); + getIScene().setDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField, m_frustumNearFarDataReference); + } + return true; + } + + bool CameraNodeImpl::isViewportOffsetBound() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField) != m_viewportOffsetDataReference; + } + + bool CameraNodeImpl::isViewportSizeBound() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField) != m_viewportSizeDataReference; + } + + bool CameraNodeImpl::isFrustumPlanesBound() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField) != m_frustumPlanesDataReference; + } + + DataInstanceHandle CameraNodeImpl::getViewportOffsetHandle() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportOffsetField); + } + + DataInstanceHandle CameraNodeImpl::getViewportSizeHandle() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::ViewportSizeField); + } + + DataInstanceHandle CameraNodeImpl::getFrustrumPlanesHandle() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumPlanesField); + } + + DataInstanceHandle CameraNodeImpl::getFrustrumNearFarPlanesHandle() const + { + return getIScene().getDataReference(m_dataInstance, ramses::internal::Camera::FrustumNearFarPlanesField); + } +} diff --git a/src/client/impl/CameraNodeImpl.h b/src/client/impl/CameraNodeImpl.h new file mode 100644 index 000000000..f8b87d69b --- /dev/null +++ b/src/client/impl/CameraNodeImpl.h @@ -0,0 +1,104 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// API +#include "ramses/framework/RamsesFrameworkTypes.h" + +// internal +#include "impl/NodeImpl.h" +#include "internal/SceneGraph/SceneAPI/ECameraProjectionType.h" +#include "internal/Core/Math3d/ProjectionParams.h" + +#include + +namespace ramses +{ + class DataObject; +} + +namespace ramses::internal +{ + class CameraNodeImpl final : public NodeImpl + { + public: + CameraNodeImpl(SceneImpl& scene, ERamsesObjectType cameraType, std::string_view cameraName); + ~CameraNodeImpl() override; + + // Common for all camera types + [[nodiscard]] ECameraProjectionType getProjectionType() const; + + bool setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height); + [[nodiscard]] int32_t getViewportX() const; + [[nodiscard]] int32_t getViewportY() const; + [[nodiscard]] uint32_t getViewportWidth() const; + [[nodiscard]] uint32_t getViewportHeight() const; + + void initializeFrameworkData(); + void deinitializeFrameworkData() override; + bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext) override; + + void onValidate(ValidationReportImpl& report) const override; + [[nodiscard]] CameraHandle getCameraHandle() const; + + bool setFrustum(float leftPlane, float rightPlane, float bottomPlane, float topPlane, float nearPlane, float farPlane); + [[nodiscard]] float getLeftPlane() const; + [[nodiscard]] float getRightPlane() const; + [[nodiscard]] float getBottomPlane() const; + [[nodiscard]] float getTopPlane() const; + [[nodiscard]] float getNearPlane() const; + [[nodiscard]] float getFarPlane() const; + + bool setPerspectiveFrustum(float fovY, float aspectRatio, float nearPlane, float farPlane); + [[nodiscard]] float getVerticalFieldOfView() const; + [[nodiscard]] float getAspectRatio() const; + + bool getProjectionMatrix(matrix44f& projectionMatrix) const; + + bool bindViewportOffset(const DataObject& offsetData); + bool bindViewportSize(const DataObject& sizeData); + bool bindFrustumPlanes(const DataObject& frustumPlanesData, const DataObject& nearFarData); + bool unbindViewportOffset(); + bool unbindViewportSize(); + bool unbindFrustumPlanes(); + [[nodiscard]] bool isViewportOffsetBound() const; + [[nodiscard]] bool isViewportSizeBound() const; + [[nodiscard]] bool isFrustumPlanesBound() const; + + [[nodiscard]] DataInstanceHandle getViewportOffsetHandle() const; + [[nodiscard]] DataInstanceHandle getViewportSizeHandle() const; + [[nodiscard]] DataInstanceHandle getFrustrumPlanesHandle() const; + [[nodiscard]] DataInstanceHandle getFrustrumNearFarPlanesHandle() const; + + private: + [[nodiscard]] ProjectionParams getProjectionParams() const; + void updateProjectionParamsOnScene(const ProjectionParams& params); + + CameraHandle m_cameraHandle; + // Data layout/instance for data references to VP offset, VP size, frustum planes, frustum near/far planes. + // By default each reference points to a data instance with values settable/gettable via API (declared below), + // each can be however bound to external data reference (data object on HL). + DataLayoutHandle m_dataLayout; + DataInstanceHandle m_dataInstance; + // VP offset and size data instances holding values + DataLayoutHandle m_viewportDataReferenceLayout; + DataInstanceHandle m_viewportOffsetDataReference; + DataInstanceHandle m_viewportSizeDataReference; + // Frustum planes data instance holding values (left, right, bottom, top) + DataLayoutHandle m_frustumPlanesDataReferenceLayout; + DataInstanceHandle m_frustumPlanesDataReference; + // Frustum near/far planes data instance holding values (near, far) + DataLayoutHandle m_frustumNearFarDataReferenceLayout; + DataInstanceHandle m_frustumNearFarDataReference; + + bool m_frustumInitialized = false; + bool m_viewportInitialized = false; + }; +} diff --git a/client/ramses-client/impl/ClientFactory.cpp b/src/client/impl/ClientFactory.cpp similarity index 90% rename from client/ramses-client/impl/ClientFactory.cpp rename to src/client/impl/ClientFactory.cpp index d45877d87..723eb5807 100644 --- a/client/ramses-client/impl/ClientFactory.cpp +++ b/src/client/impl/ClientFactory.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "ClientFactory.h" -#include "FrameworkFactoryRegistry.h" -#include "RamsesClientImpl.h" +#include "impl/FrameworkFactoryRegistry.h" +#include "impl/RamsesClientImpl.h" -namespace ramses +namespace ramses::internal { ClientUniquePtr ClientFactory::createClient(RamsesFrameworkImpl& framework, std::string_view applicationName) const { diff --git a/client/ramses-client/impl/ClientFactory.h b/src/client/impl/ClientFactory.h similarity index 81% rename from client/ramses-client/impl/ClientFactory.h rename to src/client/impl/ClientFactory.h index 04d173a3b..097ce8f6a 100644 --- a/client/ramses-client/impl/ClientFactory.h +++ b/src/client/impl/ClientFactory.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTFACTORY_H -#define RAMSES_CLIENTFACTORY_H +#pragma once -#include "ramses-client-api/RamsesClient.h" -#include "RamsesObjectFactoryInterfaces.h" +#include "ramses/client/RamsesClient.h" +#include "impl/RamsesObjectFactoryInterfaces.h" #include -namespace ramses +namespace ramses::internal { class ClientFactory : public IClientFactory { @@ -24,4 +23,4 @@ namespace ramses ClientUniquePtr createClient(RamsesFrameworkImpl& framework, std::string_view applicationName) const override; }; } -#endif + diff --git a/src/client/impl/ClientObject.cpp b/src/client/impl/ClientObject.cpp new file mode 100644 index 000000000..2f73c014e --- /dev/null +++ b/src/client/impl/ClientObject.cpp @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/ClientObject.h" + +// internal +#include "ClientObjectImpl.h" + +namespace ramses +{ + ClientObject::ClientObject(std::unique_ptr impl) + : RamsesObject{ std::move(impl) } + , m_impl{ static_cast(*RamsesObject::m_impl) } + { + } + + internal::ClientObjectImpl& ClientObject::impl() + { + return m_impl; + } + + const internal::ClientObjectImpl& ClientObject::impl() const + { + return m_impl; + } +} diff --git a/client/ramses-client/impl/ClientObjectImpl.cpp b/src/client/impl/ClientObjectImpl.cpp similarity index 72% rename from client/ramses-client/impl/ClientObjectImpl.cpp rename to src/client/impl/ClientObjectImpl.cpp index 211f5df37..f8528ab4e 100644 --- a/client/ramses-client/impl/ClientObjectImpl.cpp +++ b/src/client/impl/ClientObjectImpl.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ClientObjectImpl.h" +#include "impl/ClientObjectImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkImpl.h" -namespace ramses +namespace ramses::internal { ClientObjectImpl::ClientObjectImpl(RamsesClientImpl& client, ERamsesObjectType type, std::string_view name) - : RamsesObjectImpl(type, name) - , m_client(client) + : RamsesObjectImpl{ type, name } + , m_client{ client } { } - ClientObjectImpl::~ClientObjectImpl() - { - } + ClientObjectImpl::~ClientObjectImpl() = default; const RamsesClientImpl& ClientObjectImpl::getClientImpl() const { @@ -30,6 +30,11 @@ namespace ramses return m_client; } + ErrorReporting& ClientObjectImpl::getErrorReporting() const + { + return m_client.getFramework().getErrorReporting(); + } + bool ClientObjectImpl::isFromTheSameClientAs(const ClientObjectImpl& otherObject) const { return &getClientImpl() == &(otherObject.getClientImpl()); diff --git a/client/ramses-client/impl/ClientObjectImpl.h b/src/client/impl/ClientObjectImpl.h similarity index 63% rename from client/ramses-client/impl/ClientObjectImpl.h rename to src/client/impl/ClientObjectImpl.h index 266a0adb3..1b553f485 100644 --- a/client/ramses-client/impl/ClientObjectImpl.h +++ b/src/client/impl/ClientObjectImpl.h @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTOBJECTIMPL_H -#define RAMSES_CLIENTOBJECTIMPL_H +#pragma once -#include "RamsesObjectImpl.h" +#include "impl/RamsesObjectImpl.h" #include -namespace ramses +namespace ramses::internal { class RamsesClientImpl; + class ErrorReporting; class ClientObjectImpl : public RamsesObjectImpl { @@ -23,15 +23,13 @@ namespace ramses explicit ClientObjectImpl(RamsesClientImpl& client, ERamsesObjectType type, std::string_view name); ~ClientObjectImpl() override; - // impl methods - const RamsesClientImpl& getClientImpl() const; - RamsesClientImpl& getClientImpl(); + [[nodiscard]] const RamsesClientImpl& getClientImpl() const; + [[nodiscard]] RamsesClientImpl& getClientImpl(); + [[nodiscard]] ErrorReporting& getErrorReporting() const; // const so error can be set from const methods of derived classes - bool isFromTheSameClientAs(const ClientObjectImpl& otherObject) const; + [[nodiscard]] bool isFromTheSameClientAs(const ClientObjectImpl& otherObject) const; private: RamsesClientImpl& m_client; }; } - -#endif diff --git a/src/client/impl/DataObject.cpp b/src/client/impl/DataObject.cpp new file mode 100644 index 000000000..be3d44ff0 --- /dev/null +++ b/src/client/impl/DataObject.cpp @@ -0,0 +1,105 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/DataObject.h" + +//internal +#include "impl/DataObjectImpl.h" + +namespace ramses +{ + DataObject::DataObject(std::unique_ptr impl) + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + EDataType DataObject::getDataType() const + { + return m_impl.getDataType(); + } + + internal::DataObjectImpl& DataObject::impl() + { + return m_impl; + } + + const internal::DataObjectImpl& DataObject::impl() const + { + return m_impl; + } + + template + bool DataObject::setValueInternal(T&& value) + { + return m_impl.setValue(value); + + } + + template + bool DataObject::getValueInternal(T& value) const + { + return m_impl.getValue(value); + } + + // const l-value instances + template RAMSES_API bool DataObject::setValueInternal(const bool&); + template RAMSES_API bool DataObject::setValueInternal(const int32_t&); + template RAMSES_API bool DataObject::setValueInternal(const float&); + template RAMSES_API bool DataObject::setValueInternal(const vec2i&); + template RAMSES_API bool DataObject::setValueInternal(const vec3i&); + template RAMSES_API bool DataObject::setValueInternal(const vec4i&); + template RAMSES_API bool DataObject::setValueInternal(const vec2f&); + template RAMSES_API bool DataObject::setValueInternal(const vec3f&); + template RAMSES_API bool DataObject::setValueInternal(const vec4f&); + template RAMSES_API bool DataObject::setValueInternal(const matrix22f&); + template RAMSES_API bool DataObject::setValueInternal(const matrix33f&); + template RAMSES_API bool DataObject::setValueInternal(const matrix44f&); + + // l-value instances + template RAMSES_API bool DataObject::setValueInternal(bool&); + template RAMSES_API bool DataObject::setValueInternal(int32_t&); + template RAMSES_API bool DataObject::setValueInternal(float&); + template RAMSES_API bool DataObject::setValueInternal(vec2i&); + template RAMSES_API bool DataObject::setValueInternal(vec3i&); + template RAMSES_API bool DataObject::setValueInternal(vec4i&); + template RAMSES_API bool DataObject::setValueInternal(vec2f&); + template RAMSES_API bool DataObject::setValueInternal(vec3f&); + template RAMSES_API bool DataObject::setValueInternal(vec4f&); + template RAMSES_API bool DataObject::setValueInternal(matrix22f&); + template RAMSES_API bool DataObject::setValueInternal(matrix33f&); + template RAMSES_API bool DataObject::setValueInternal(matrix44f&); + + // r-value instances + template RAMSES_API bool DataObject::setValueInternal(bool&&); + template RAMSES_API bool DataObject::setValueInternal(int32_t&&); + template RAMSES_API bool DataObject::setValueInternal(float&&); + template RAMSES_API bool DataObject::setValueInternal(vec2i&&); + template RAMSES_API bool DataObject::setValueInternal(vec3i&&); + template RAMSES_API bool DataObject::setValueInternal(vec4i&&); + template RAMSES_API bool DataObject::setValueInternal(vec2f&&); + template RAMSES_API bool DataObject::setValueInternal(vec3f&&); + template RAMSES_API bool DataObject::setValueInternal(vec4f&&); + template RAMSES_API bool DataObject::setValueInternal(matrix22f&&); + template RAMSES_API bool DataObject::setValueInternal(matrix33f&&); + template RAMSES_API bool DataObject::setValueInternal(matrix44f&&); + + template RAMSES_API bool DataObject::getValueInternal(bool&) const; + template RAMSES_API bool DataObject::getValueInternal(int32_t&) const; + template RAMSES_API bool DataObject::getValueInternal(float&) const; + template RAMSES_API bool DataObject::getValueInternal(vec2i&) const; + template RAMSES_API bool DataObject::getValueInternal(vec3i&) const; + template RAMSES_API bool DataObject::getValueInternal(vec4i&) const; + template RAMSES_API bool DataObject::getValueInternal(vec2f&) const; + template RAMSES_API bool DataObject::getValueInternal(vec3f&) const; + template RAMSES_API bool DataObject::getValueInternal(vec4f&) const; + template RAMSES_API bool DataObject::getValueInternal(matrix22f&) const; + template RAMSES_API bool DataObject::getValueInternal(matrix33f&) const; + template RAMSES_API bool DataObject::getValueInternal(matrix44f&) const; +} diff --git a/src/client/impl/DataObjectImpl.cpp b/src/client/impl/DataObjectImpl.cpp new file mode 100644 index 000000000..e54961df9 --- /dev/null +++ b/src/client/impl/DataObjectImpl.cpp @@ -0,0 +1,139 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/DataObjectImpl.h" +#include "impl/SceneObjectImpl.h" +#include "impl/DataTypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" + +namespace ramses::internal +{ + DataObjectImpl::DataObjectImpl(SceneImpl& scene, ERamsesObjectType ramsesType, ramses::EDataType dataType, std::string_view name) + : SceneObjectImpl{ scene, ramsesType, name } + , m_dataType{ dataType } + { + } + + DataObjectImpl::~DataObjectImpl() = default; + + void DataObjectImpl::initializeFrameworkData() + { + ClientScene& scene = getIScene(); + + // create data layout on scene + m_layoutHandle = scene.allocateDataLayout({DataFieldInfo(DataTypeUtils::ConvertDataTypeToInternal(m_dataType))}, ResourceContentHash::Invalid(), {}); + + // allocate data instance based on created layout + m_dataReference = scene.allocateDataInstance(m_layoutHandle, {}); + } + + void DataObjectImpl::deinitializeFrameworkData() + { + ClientScene& scene = getIScene(); + + scene.releaseDataInstance(m_dataReference); + scene.releaseDataLayout(m_layoutHandle); + + m_dataReference = DataInstanceHandle::Invalid(); + m_layoutHandle = DataLayoutHandle::Invalid(); + } + + bool DataObjectImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << static_cast(m_dataType); + outStream << m_layoutHandle; + outStream << m_dataReference; + + return true; + } + + bool DataObjectImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + uint32_t enumType = 0u; + inStream >> enumType; + m_dataType = static_cast(enumType); + inStream >> m_layoutHandle; + inStream >> m_dataReference; + + return true; + } + + template + bool DataObjectImpl::setValue(const T& value) + { + if (TypeToEDataTypeTraits::DataType != DataTypeUtils::ConvertDataTypeToInternal(m_dataType)) + { + getErrorReporting().set("DataObject::setValue failed, value type does not match DataObject data type", *this); + return false; + } + + ISceneDataArrayAccessor::SetDataArray(&getIScene(), m_dataReference, DataFieldHandle(0u), 1u, &value); + return true; + } + + template + bool DataObjectImpl::getValue(T& value) const + { + if (TypeToEDataTypeTraits::DataType != DataTypeUtils::ConvertDataTypeToInternal(m_dataType)) + { + getErrorReporting().set("DataObject::getValue failed, value type does not match DataObject data type", *this); + return false; + } + + const T* data = ISceneDataArrayAccessor::GetDataArray(&getIScene(), m_dataReference, DataFieldHandle(0u)); + assert(data != nullptr); + value = data[0]; + + return true; + } + + ramses::EDataType DataObjectImpl::getDataType() const + { + return m_dataType; + } + + DataInstanceHandle DataObjectImpl::getDataReference() const + { + return m_dataReference; + } + + template bool DataObjectImpl::setValue(const bool&); + template bool DataObjectImpl::setValue(const int32_t&); + template bool DataObjectImpl::setValue(const float&); + template bool DataObjectImpl::setValue(const glm::vec2&); + template bool DataObjectImpl::setValue(const glm::vec3&); + template bool DataObjectImpl::setValue(const glm::vec4&); + template bool DataObjectImpl::setValue(const glm::ivec2&); + template bool DataObjectImpl::setValue(const glm::ivec3&); + template bool DataObjectImpl::setValue(const glm::ivec4&); + template bool DataObjectImpl::setValue(const glm::mat2&); + template bool DataObjectImpl::setValue(const glm::mat3&); + template bool DataObjectImpl::setValue(const glm::mat4&); + + template bool DataObjectImpl::getValue(bool&) const; + template bool DataObjectImpl::getValue(int32_t&) const; + template bool DataObjectImpl::getValue(float&) const; + template bool DataObjectImpl::getValue(glm::vec2&) const; + template bool DataObjectImpl::getValue(glm::vec3&) const; + template bool DataObjectImpl::getValue(glm::vec4&) const; + template bool DataObjectImpl::getValue(glm::ivec2&) const; + template bool DataObjectImpl::getValue(glm::ivec3&) const; + template bool DataObjectImpl::getValue(glm::ivec4&) const; + template bool DataObjectImpl::getValue(glm::mat2&) const; + template bool DataObjectImpl::getValue(glm::mat3&) const; + template bool DataObjectImpl::getValue(glm::mat4&) const; +} diff --git a/client/ramses-client/impl/DataObjectImpl.h b/src/client/impl/DataObjectImpl.h similarity index 51% rename from client/ramses-client/impl/DataObjectImpl.h rename to src/client/impl/DataObjectImpl.h index 316de943f..07a3ab9fc 100644 --- a/client/ramses-client/impl/DataObjectImpl.h +++ b/src/client/impl/DataObjectImpl.h @@ -6,51 +6,45 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAOBJECTIMPL_H -#define RAMSES_DATAOBJECTIMPL_H +#pragma once //internal #include "SceneObjectImpl.h" -#include "ramses-framework-api/EDataType.h" -#include "DataTypesImpl.h" -#include "SceneAPI/Handles.h" +#include "ramses/framework/EDataType.h" +#include "impl/DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" #include -namespace ramses_internal +namespace ramses::internal { class IScene; -} -namespace ramses -{ class DataObjectImpl final : public SceneObjectImpl { public: - DataObjectImpl(SceneImpl& scene, ERamsesObjectType ramsesType, EDataType dataType, std::string_view name); + DataObjectImpl(SceneImpl& scene, ERamsesObjectType ramsesType, ramses::EDataType dataType, std::string_view name); ~DataObjectImpl() override; void initializeFrameworkData(); void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext) override; - EDataType getDataType() const; + [[nodiscard]] ramses::EDataType getDataType() const; template - status_t setValue(const T& value); + bool setValue(const T& value); template - status_t getValue(T& value) const; + bool getValue(T& value) const; - ramses_internal::DataInstanceHandle getDataReference() const; + [[nodiscard]] DataInstanceHandle getDataReference() const; private: - EDataType m_dataType; + ramses::EDataType m_dataType; - ramses_internal::DataLayoutHandle m_layoutHandle; - ramses_internal::DataInstanceHandle m_dataReference; + DataLayoutHandle m_layoutHandle; + DataInstanceHandle m_dataReference; }; } - -#endif diff --git a/src/client/impl/Effect.cpp b/src/client/impl/Effect.cpp new file mode 100644 index 000000000..5709b976f --- /dev/null +++ b/src/client/impl/Effect.cpp @@ -0,0 +1,84 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" + +// internal +#include "impl/EffectImpl.h" + +namespace ramses +{ + Effect::Effect(std::unique_ptr impl) + : Resource{ std::move(impl) } + , m_impl{ static_cast(Resource::m_impl) } + { + } + + size_t Effect::getUniformInputCount() const + { + return m_impl.getUniformInputCount(); + } + + size_t Effect::getAttributeInputCount() const + { + return m_impl.getAttributeInputCount(); + } + + std::optional Effect::getUniformInput(size_t index) const + { + return m_impl.getUniformInput(index); + } + + std::optional Effect::findUniformInput(EEffectUniformSemantic uniformSemantic) const + { + return m_impl.findUniformInput(uniformSemantic); + } + + std::optional Effect::getAttributeInput(size_t index) const + { + return m_impl.getAttributeInput(index); + } + + std::optional Effect::findAttributeInput(EEffectAttributeSemantic attributeSemantic) const + { + return m_impl.findAttributeInput(attributeSemantic); + } + + bool Effect::hasGeometryShader() const + { + return m_impl.hasGeometryShader(); + } + + bool Effect::getGeometryShaderInputType(EDrawMode& expectedGeometryInputType) const + { + return m_impl.getGeometryShaderInputType(expectedGeometryInputType); + } + + std::optional Effect::findUniformInput(std::string_view inputName) const + { + return m_impl.findUniformInput(inputName); + } + + std::optional Effect::findAttributeInput(std::string_view inputName) const + { + return m_impl.findAttributeInput(inputName); + } + + internal::EffectImpl& Effect::impl() + { + return m_impl; + } + + const internal::EffectImpl& Effect::impl() const + { + return m_impl; + } +} diff --git a/src/client/impl/EffectDescription.cpp b/src/client/impl/EffectDescription.cpp new file mode 100644 index 000000000..d8bd249bf --- /dev/null +++ b/src/client/impl/EffectDescription.cpp @@ -0,0 +1,137 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/EffectDescription.h" +#include "impl/APILoggingMacros.h" + +// internal +#include "impl/EffectDescriptionImpl.h" + +namespace ramses +{ + EffectDescription::EffectDescription() + : m_impl{ std::make_unique() } + { + } + + EffectDescription::~EffectDescription() = default; + + EffectDescription::EffectDescription(const EffectDescription& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + EffectDescription::EffectDescription(EffectDescription&& other) noexcept = default; + + EffectDescription& EffectDescription::operator=(const EffectDescription& other) + { + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + EffectDescription& EffectDescription::operator=(EffectDescription&& other) noexcept = default; + + bool EffectDescription::setVertexShader(std::string_view shaderSource) + { + const auto status = m_impl->setVertexShader(shaderSource); + LOG_HL_CLIENT_API1(status, shaderSource.size()); + return status; + } + + bool EffectDescription::setFragmentShader(std::string_view shaderSource) + { + const auto status = m_impl->setFragmentShader(shaderSource); + LOG_HL_CLIENT_API1(status, shaderSource.size()); + return status; + } + + bool EffectDescription::setGeometryShader(std::string_view shaderSource) + { + const auto status = m_impl->setGeometryShader(shaderSource); + LOG_HL_CLIENT_API1(status, shaderSource.size()); + return status; + } + + bool EffectDescription::setVertexShaderFromFile(std::string_view shaderSourceFileName) + { + const auto status = m_impl->setVertexShaderFromFile(shaderSourceFileName); + LOG_HL_CLIENT_API1(status, shaderSourceFileName); + return status; + } + + bool EffectDescription::setFragmentShaderFromFile(std::string_view shaderSourceFileName) + { + const auto status = m_impl->setFragmentShaderFromFile(shaderSourceFileName); + LOG_HL_CLIENT_API1(status, shaderSourceFileName); + return status; + } + + bool EffectDescription::setGeometryShaderFromFile(std::string_view shaderSourceFileName) + { + const auto status = m_impl->setGeometryShaderFromFile(shaderSourceFileName); + LOG_HL_CLIENT_API1(status, shaderSourceFileName); + return status; + } + + bool EffectDescription::addCompilerDefine(std::string_view define) + { + const auto status = m_impl->addCompilerDefine(define); + LOG_HL_CLIENT_API1(status, define); + return status; + } + + bool EffectDescription::setUniformSemantic(std::string_view inputName, EEffectUniformSemantic semanticType) + { + const auto status = m_impl->setUniformSemantic(inputName, semanticType); + LOG_HL_CLIENT_API2(status, inputName, semanticType); + return status; + } + + bool EffectDescription::setAttributeSemantic(std::string_view inputName, EEffectAttributeSemantic semanticType) + { + const auto status = m_impl->setAttributeSemantic(inputName, semanticType); + LOG_HL_CLIENT_API2(status, inputName, semanticType); + return status; + } + + const char* EffectDescription::getVertexShader() const + { + return m_impl->getVertexShader(); + } + + const char* EffectDescription::getFragmentShader() const + { + return m_impl->getFragmentShader(); + } + + const char* EffectDescription::getGeometryShader() const + { + return m_impl->getGeometryShader(); + } + + size_t EffectDescription::getNumberOfCompilerDefines() const + { + return m_impl->getNumberOfCompilerDefines(); + } + + const char* EffectDescription::getCompilerDefine(size_t index) const + { + return m_impl->getCompilerDefine(index); + } + + internal::EffectDescriptionImpl& EffectDescription::impl() + { + return *m_impl; + } + + const internal::EffectDescriptionImpl& EffectDescription::impl() const + { + return *m_impl; + } +} diff --git a/src/client/impl/EffectDescriptionImpl.cpp b/src/client/impl/EffectDescriptionImpl.cpp new file mode 100644 index 000000000..890981c5b --- /dev/null +++ b/src/client/impl/EffectDescriptionImpl.cpp @@ -0,0 +1,185 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// internal +#include "impl/EffectDescriptionImpl.h" +#include "impl/EffectInputSemanticUtils.h" + +// framework +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/LogMacros.h" + + +namespace ramses::internal +{ + EffectDescriptionImpl::EffectDescriptionImpl() = default; + + EffectDescriptionImpl::~EffectDescriptionImpl() = default; + + bool EffectDescriptionImpl::setVertexShader(std::string_view shaderSource) + { + m_vertexShaderSource = shaderSource; + return true; + } + + bool EffectDescriptionImpl::setFragmentShader(std::string_view shaderSource) + { + m_fragmentShaderSource = shaderSource; + return true; + } + + bool EffectDescriptionImpl::setGeometryShader(std::string_view shaderSource) + { + m_geometryShaderSource = shaderSource; + return true; + } + + bool EffectDescriptionImpl::setVertexShaderFromFile(std::string_view shaderSourceFileName) + { + if (!ReadFileContentsToString(shaderSourceFileName, m_vertexShaderSource)) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescription::setVertexShaderFromFile could not read file!"); + return false; + } + + return true; + } + + bool EffectDescriptionImpl::setFragmentShaderFromFile(std::string_view shaderSourceFileName) + { + if (!ReadFileContentsToString(shaderSourceFileName, m_fragmentShaderSource)) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescription::setFragmentShaderFromFile could not read file!"); + return false; + } + + return true; + } + + bool EffectDescriptionImpl::setGeometryShaderFromFile(std::string_view shaderSourceFileName) + { + if (!ReadFileContentsToString(shaderSourceFileName, m_geometryShaderSource)) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescription::setGeometryShaderFromFile could not read file!"); + return false; + } + + return true; + } + + bool EffectDescriptionImpl::addCompilerDefine(std::string_view define) + { + if (define.empty()) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescription::addCompilerDefine cannot add empty define!"); + return false; + } + + m_compilerDefines.emplace_back(define); + return true; + } + + bool EffectDescriptionImpl::setSemantic(std::string_view semanticName, EFixedSemantics semanticType) + { + if (semanticName.empty()) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescription::setSemantic cannot set empty semantic name!"); + return false; + } + + m_inputSemantics.put(std::string{semanticName}, semanticType); + return true; + } + + bool EffectDescriptionImpl::setUniformSemantic(std::string_view semanticName, EEffectUniformSemantic semanticType) + { + const EFixedSemantics semanticTypeInternal = EffectInputSemanticUtils::GetEffectInputSemanticInternal(semanticType); + return setSemantic(semanticName, semanticTypeInternal); + } + + bool EffectDescriptionImpl::setAttributeSemantic(std::string_view semanticName, EEffectAttributeSemantic semanticType) + { + const EFixedSemantics semanticTypeInternal = EffectInputSemanticUtils::GetEffectInputSemanticInternal(semanticType); + return setSemantic(semanticName, semanticTypeInternal); + } + + const char* EffectDescriptionImpl::getVertexShader() const + { + return m_vertexShaderSource.c_str(); + } + + const char* EffectDescriptionImpl::getFragmentShader() const + { + return m_fragmentShaderSource.c_str(); + } + + const char* EffectDescriptionImpl::getGeometryShader() const + { + return m_geometryShaderSource.c_str(); + } + + size_t EffectDescriptionImpl::getNumberOfCompilerDefines() const + { + return m_compilerDefines.size(); + } + + const std::vector& EffectDescriptionImpl::getCompilerDefines() const + { + return m_compilerDefines; + } + + const char* EffectDescriptionImpl::getCompilerDefine(size_t index) const + { + if (index < getNumberOfCompilerDefines()) + { + return m_compilerDefines[index].c_str(); + } + + return nullptr; + } + + const EffectDescriptionImpl::SemanticsMap& EffectDescriptionImpl::getSemanticsMap() const + { + return m_inputSemantics; + } + + bool EffectDescriptionImpl::ReadFileContentsToString(std::string_view fileName, std::string& fileContents) + { + File inFile(fileName); + if (!inFile.exists()) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: could not find file: " << fileName); + return false; + } + + if (!inFile.open(File::Mode::ReadOnlyBinary)) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: could not open file: " << fileName); + return false; + } + + size_t fileSize = 0; + size_t readBytes = 0; + if (!inFile.getSizeInBytes(fileSize)) + { + LOG_ERROR(CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: error reading file info: " << fileName); + return false; + } + + std::vector charVector(fileSize + 1u); + const EStatus stat = inFile.read(&charVector[0], fileSize, readBytes); + if (stat == EStatus::Ok || stat == EStatus::Eof) + { + charVector[readBytes] = '\0'; + fileContents = &charVector[0]; + return true; + } + LOG_ERROR(CONTEXT_CLIENT, "EffectDescriptionImpl::ReadFileContentsToString: error reading file contents: " << fileName); + return false; + } +} diff --git a/src/client/impl/EffectDescriptionImpl.h b/src/client/impl/EffectDescriptionImpl.h new file mode 100644 index 000000000..0bd986861 --- /dev/null +++ b/src/client/impl/EffectDescriptionImpl.h @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// API +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/EffectInputSemantic.h" + +// ramses framework +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" + +#include +#include +#include + +namespace ramses::internal +{ + class EffectDescriptionImpl + { + public: + using SemanticsMap = HashMap; + + EffectDescriptionImpl(); + ~EffectDescriptionImpl(); + + [[nodiscard]] bool setVertexShader(std::string_view shaderSource); + [[nodiscard]] bool setFragmentShader(std::string_view shaderSource); + [[nodiscard]] bool setGeometryShader(std::string_view shaderSource); + [[nodiscard]] bool setVertexShaderFromFile(std::string_view shaderSourceFileName); + [[nodiscard]] bool setFragmentShaderFromFile(std::string_view shaderSourceFileName); + [[nodiscard]] bool setGeometryShaderFromFile(std::string_view shaderSourceFileName); + [[nodiscard]] bool addCompilerDefine(std::string_view define); + [[nodiscard]] bool setUniformSemantic(std::string_view semanticName, EEffectUniformSemantic semanticType); + [[nodiscard]] bool setAttributeSemantic(std::string_view semanticName, EEffectAttributeSemantic semanticType); + + [[nodiscard]] const char* getVertexShader() const; + [[nodiscard]] const char* getFragmentShader() const; + [[nodiscard]] const char* getGeometryShader() const; + [[nodiscard]] size_t getNumberOfCompilerDefines() const; + [[nodiscard]] const std::vector& getCompilerDefines() const; + [[nodiscard]] const char* getCompilerDefine(size_t index) const; + [[nodiscard]] const SemanticsMap& getSemanticsMap() const; + + [[nodiscard]] static bool ReadFileContentsToString(std::string_view fileName, std::string& fileContents); + + private: + [[nodiscard]] bool setSemantic(std::string_view semanticName, EFixedSemantics semanticType); + + std::string m_vertexShaderSource; + std::string m_fragmentShaderSource; + std::string m_geometryShaderSource; + + std::vector m_compilerDefines; + SemanticsMap m_inputSemantics; + }; +} diff --git a/src/client/impl/EffectImpl.cpp b/src/client/impl/EffectImpl.cpp new file mode 100644 index 000000000..722d0aaf1 --- /dev/null +++ b/src/client/impl/EffectImpl.cpp @@ -0,0 +1,342 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/EffectImpl.h" +#include "impl/EffectInputImpl.h" +#include "impl/SerializationContext.h" +#include "impl/RamsesClientImpl.h" +#include "impl/EffectInputSemanticUtils.h" +#include "impl/ErrorReporting.h" + +#include "internal/Components/ManagedResource.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/Components/ResourceHashUsage.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" + +#include "fmt/format.h" + +#include + +namespace ramses::internal +{ + EffectImpl::EffectImpl(ResourceHashUsage hashUsage, SceneImpl& scene, std::string_view effectname) + : ResourceImpl(ERamsesObjectType::Effect, std::move(hashUsage), scene, effectname) + { + } + + EffectImpl::~EffectImpl() = default; + + void EffectImpl::deinitializeFrameworkData() + { + ResourceImpl::deinitializeFrameworkData(); + m_shaderWarnings.reset(); + } + + void EffectImpl::initializeFromFrameworkData(const EffectInputInformationVector& uniformInputs, const EffectInputInformationVector& attributeInputs, std::optional geometryShaderInputType) + { + m_effectUniformInputs = uniformInputs; + m_effectAttributeInputs = attributeInputs; + m_geometryShaderInputType = geometryShaderInputType; + + m_shaderWarnings.reset(); + } + + bool EffectImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!ResourceImpl::serialize(outStream, serializationContext)) + return false; + + outStream << static_cast(m_effectUniformInputs.size()); + for(const auto& input : m_effectUniformInputs) + { + outStream << input.inputName; + outStream << static_cast(input.dataType); + outStream << static_cast(input.elementCount); + outStream << static_cast(input.semantics); + } + + outStream << static_cast(m_effectAttributeInputs.size()); + for (const auto& input : m_effectAttributeInputs) + { + outStream << input.inputName; + outStream << static_cast(input.dataType); + outStream << static_cast(input.semantics); + } + + const int32_t gsInputType = (m_geometryShaderInputType ? static_cast(*m_geometryShaderInputType) : -1); + outStream << gsInputType; + + return true; + } + + bool EffectImpl::deserialize(IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!ResourceImpl::deserialize(inStream, serializationContext)) + return false; + + uint32_t count = 0u; + inStream >> count; + m_effectUniformInputs.resize(count); + for (uint32_t i = 0u; i < count; ++i) + { + inStream >> m_effectUniformInputs[i].inputName; + + uint32_t dataTypeAsUInt = 0; + inStream >> dataTypeAsUInt; + uint32_t elementCount = 0; + inStream >> elementCount; + uint32_t semanticAsUInt = 0; + inStream >> semanticAsUInt; + + m_effectUniformInputs[i].dataType = static_cast(dataTypeAsUInt); + m_effectUniformInputs[i].elementCount = elementCount; + m_effectUniformInputs[i].semantics = static_cast(semanticAsUInt); + } + + inStream >> count; + m_effectAttributeInputs.resize(count); + for (uint32_t i = 0u; i < count; ++i) + { + inStream >> m_effectAttributeInputs[i].inputName; + + uint32_t dataTypeAsUInt = 0; + inStream >> dataTypeAsUInt; + uint32_t semanticAsUInt = 0; + inStream >> semanticAsUInt; + + m_effectAttributeInputs[i].dataType = static_cast(dataTypeAsUInt); + m_effectAttributeInputs[i].elementCount = 1u; + m_effectAttributeInputs[i].semantics = static_cast(semanticAsUInt); + } + + int32_t gsInputType = -1; + inStream >> gsInputType; + if (gsInputType >= 0) + m_geometryShaderInputType = static_cast(gsInputType); + + m_shaderWarnings.reset(); + + return true; + } + + void EffectImpl::onValidate(ValidationReportImpl& report) const + { + ResourceImpl::onValidate(report); + if (!m_shaderWarnings.has_value()) + { + const auto resourceHash{getLowlevelResourceHash()}; + assert(resourceHash.isValid()); + + auto managedResource{getClientImpl().getResource(resourceHash)}; + if (!managedResource && !(managedResource = getClientImpl().getClientApplication().loadResource(resourceHash))) + { + report.add(EIssueType::Error, fmt::format("Unable to retrieve a resource by resource hash: {}", resourceHash), &getRamsesObject()); + return; + } + + const EffectResource& effectResource{*managedResource->convertTo()}; + if (!effectResource.isDeCompressedAvailable()) + { + if (effectResource.isCompressedAvailable()) + { + effectResource.decompress(); + } + else + { + report.add(EIssueType::Error, fmt::format("EffectResource without compressed/decompressed data: {}", resourceHash), &getRamsesObject()); + return; + } + } + + const GlslParser parser{effectResource.getVertexShader(), effectResource.getFragmentShader(), effectResource.getGeometryShader()}; + if (!parser.valid()) + { + report.add(EIssueType::Error, fmt::format("Can't parse shaders provided by EffectResource: {}", resourceHash), &getRamsesObject()); + return; + } + + m_shaderWarnings = parser.generateWarnings(); + } + + for (auto& w : *m_shaderWarnings) + { + report.add(EIssueType::Warning, fmt::format("{}: {}", w.stage, w.msg), &getRamsesObject()); + } + } + + size_t EffectImpl::getUniformInputCount() const + { + return m_effectUniformInputs.size(); + } + + size_t EffectImpl::getAttributeInputCount() const + { + return m_effectAttributeInputs.size(); + } + + std::optional EffectImpl::getUniformInput(size_t index) const + { + if (index >= getUniformInputCount()) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Effect: getUniformInput failed, index out of range!"); + return std::nullopt; + } + + UniformInput input; + initializeEffectInputData(*input.m_impl, m_effectUniformInputs[index], index); + + return input; + } + + std::optional EffectImpl::getAttributeInput(size_t index) const + { + if (index >= getAttributeInputCount()) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Effect: getAttributeInput failed, index out of range!"); + return std::nullopt; + } + + AttributeInput input; + initializeEffectInputData(*input.m_impl, m_effectAttributeInputs[index], index); + + return input; + } + + std::optional EffectImpl::findUniformInput(std::string_view inputName) const + { + const size_t index = GetEffectInputIndex(m_effectUniformInputs, inputName); + if (index == InvalidInputIndex) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Effect: findUniformInput failed, uniform input '{}' could not be found in effect '{}'", inputName, getName()); + return std::nullopt; + } + + UniformInput input; + initializeEffectInputData(*input.m_impl, m_effectUniformInputs[index], index); + + return input; + } + + std::optional EffectImpl::findAttributeInput(std::string_view inputName) const + { + const size_t index = GetEffectInputIndex(m_effectAttributeInputs, inputName); + if (index == InvalidInputIndex) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Effect: findAttributeInput failed, attribute input '{}' could not be found in effect '{}'", inputName, getName()); + return std::nullopt; + } + + AttributeInput input; + initializeEffectInputData(*input.m_impl, m_effectAttributeInputs[index], index); + + return input; + } + + std::optional EffectImpl::findUniformInput(EEffectUniformSemantic uniformSemantic) const + { + const size_t index = FindEffectInputIndex(m_effectUniformInputs, EffectInputSemanticUtils::GetEffectInputSemanticInternal(uniformSemantic)); + if (index == InvalidInputIndex) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Effect: findUniformInput failed, semantic is not defined in effect!"); + return std::nullopt; + } + + UniformInput input; + initializeEffectInputData(*input.m_impl, m_effectUniformInputs[index], index); + + return input; + } + + std::optional EffectImpl::findAttributeInput(EEffectAttributeSemantic attributeSemantic) const + { + const size_t index = FindEffectInputIndex(m_effectAttributeInputs, EffectInputSemanticUtils::GetEffectInputSemanticInternal(attributeSemantic)); + if (index == InvalidInputIndex) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Effect: findAttributeInput failed, semantic not defined in effect!"); + return std::nullopt; + } + + AttributeInput input; + initializeEffectInputData(*input.m_impl, m_effectAttributeInputs[index], index); + + return input; + } + + const EffectInputInformationVector& EffectImpl::getUniformInputInformation() const + { + return m_effectUniformInputs; + } + + const EffectInputInformationVector& EffectImpl::getAttributeInputInformation() const + { + return m_effectAttributeInputs; + } + + size_t EffectImpl::GetEffectInputIndex(const EffectInputInformationVector& effectInputVector, std::string_view inputName) + { + const size_t numInputs = effectInputVector.size(); + for (size_t i = 0u; i < numInputs; ++i) + { + const EffectInputInformation& effectInputInfo = effectInputVector[i]; + if (effectInputInfo.inputName == inputName) + { + return i; + } + } + + return InvalidInputIndex; + } + + size_t EffectImpl::FindEffectInputIndex(const EffectInputInformationVector& effectInputVector, EFixedSemantics inputSemantics) + { + if (EFixedSemantics::Invalid == inputSemantics) + return InvalidInputIndex; + + const size_t numInputs = effectInputVector.size(); + for (size_t i = 0u; i < numInputs; ++i) + { + const EffectInputInformation& effectInputInfo = effectInputVector[i]; + if (effectInputInfo.semantics == inputSemantics) + { + return i; + } + } + + return InvalidInputIndex; + } + + void EffectImpl::initializeEffectInputData(EffectInputImpl& effectInputImpl, const EffectInputInformation& effectInputInfo, size_t index) const + { + effectInputImpl.initialize( + getLowlevelResourceHash(), + effectInputInfo.inputName, + effectInputInfo.dataType, + effectInputInfo.semantics, + effectInputInfo.elementCount, + index + ); + } + + bool EffectImpl::hasGeometryShader() const + { + return m_geometryShaderInputType.has_value(); + } + + bool EffectImpl::getGeometryShaderInputType(EDrawMode& inputType) const + { + if (!hasGeometryShader()) + { + getErrorReporting().set((StringOutputStream() << "Effect::getGeometryShaderInputType: failed, effect '" << getName() << "' has no geometry shader attached to it!").c_str(), *this); + return false; + } + + inputType = *m_geometryShaderInputType; + return true; + } +} diff --git a/src/client/impl/EffectImpl.h b/src/client/impl/EffectImpl.h new file mode 100644 index 000000000..bd74c9593 --- /dev/null +++ b/src/client/impl/EffectImpl.h @@ -0,0 +1,75 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// client api +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/EffectInputSemantic.h" +#include "ramses/framework/RamsesFrameworkTypes.h" + +// internal +#include "impl/ResourceImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/glslEffectBlock/GlslParser.h" + +#include +#include +#include + +namespace ramses::internal +{ + class EffectInputImpl; + + class EffectImpl final : public ResourceImpl + { + public: + EffectImpl(ResourceHashUsage hashUsage, SceneImpl& scene, std::string_view effectname); + ~EffectImpl() override; + + void deinitializeFrameworkData() override; + void initializeFromFrameworkData(const EffectInputInformationVector& uniformInputs, const EffectInputInformationVector& attributeInputs, std::optional geometryShaderInputType); + + bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + size_t getUniformInputCount() const; + size_t getAttributeInputCount() const; + + bool hasGeometryShader() const; + bool getGeometryShaderInputType(EDrawMode& inputType) const; + + std::optional getUniformInput(size_t index) const; + std::optional getAttributeInput(size_t index) const; + std::optional findUniformInput(std::string_view inputName) const; + std::optional findAttributeInput(std::string_view inputName) const; + std::optional findUniformInput(EEffectUniformSemantic uniformSemantic) const; + std::optional findAttributeInput(EEffectAttributeSemantic attributeSemantic) const; + + const EffectInputInformationVector& getUniformInputInformation() const; + const EffectInputInformationVector& getAttributeInputInformation() const; + + private: + static const size_t InvalidInputIndex = 0xffff; + + static size_t GetEffectInputIndex(const EffectInputInformationVector& effectInputVector, std::string_view inputName); + static size_t FindEffectInputIndex(const EffectInputInformationVector& effectInputVector, EFixedSemantics inputSemantics); + void initializeEffectInputData(EffectInputImpl& effectInputImpl, const EffectInputInformation& effectInputInfo, size_t index) const; + + EffectInputInformationVector m_effectUniformInputs; + EffectInputInformationVector m_effectAttributeInputs; + std::optional m_geometryShaderInputType; + + mutable std::optional m_shaderWarnings; + }; +} diff --git a/src/client/impl/EffectInput.cpp b/src/client/impl/EffectInput.cpp new file mode 100644 index 000000000..171fbc66b --- /dev/null +++ b/src/client/impl/EffectInput.cpp @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/EffectInput.h" +#include "impl/EffectInputImpl.h" + +namespace ramses +{ + EffectInput::~EffectInput() = default; + + EffectInput::EffectInput(std::unique_ptr effectInputImpl) + : m_impl{ std::move(effectInputImpl) } + { + } + + EffectInput::EffectInput(const EffectInput& other) + : EffectInput{ std::make_unique(*other.m_impl) } + { + } + + EffectInput::EffectInput(EffectInput&& other) noexcept = default; + + EffectInput& EffectInput::operator=(const EffectInput& other) + { + EffectInput::m_impl = std::make_unique(*other.m_impl); + return *this; + } + + EffectInput& EffectInput::operator=(EffectInput&& other) noexcept = default; + + + const char* EffectInput::getName() const + { + return m_impl->getName().c_str(); + } + + EDataType EffectInput::getDataType() const + { + return m_impl->getDataType(); + } + + internal::EffectInputImpl& EffectInput::impl() + { + return *m_impl; + } + + const internal::EffectInputImpl& EffectInput::impl() const + { + return *m_impl; + } +} diff --git a/src/client/impl/EffectInputImpl.cpp b/src/client/impl/EffectInputImpl.cpp new file mode 100644 index 000000000..9a175a64d --- /dev/null +++ b/src/client/impl/EffectInputImpl.cpp @@ -0,0 +1,76 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/EffectInputImpl.h" +#include "impl/DataTypeUtils.h" +#include "impl/EffectInputSemanticUtils.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" + +namespace ramses::internal +{ + void EffectInputImpl::initialize( + const ResourceContentHash& effectHash, + std::string_view name, + ramses::internal::EDataType dataType, + EFixedSemantics semantics, + size_t elementCount, + size_t index) + { + m_effectHash = effectHash; + m_name = name; + m_dataType = dataType; + m_semantics = semantics; + m_elementCount = elementCount; + m_inputIndex = index; + } + + ResourceContentHash EffectInputImpl::getEffectHash() const + { + return m_effectHash; + } + + const std::string& EffectInputImpl::getName() const + { + return m_name; + } + + ramses::EDataType EffectInputImpl::getDataType() const + { + return DataTypeUtils::ConvertDataTypeFromInternal(m_dataType); + } + + ramses::internal::EDataType EffectInputImpl::getInternalDataType() const + { + return m_dataType; + } + + EFixedSemantics EffectInputImpl::getSemantics() const + { + return m_semantics; + } + + size_t EffectInputImpl::getElementCount() const + { + return m_elementCount; + } + + size_t EffectInputImpl::getInputIndex() const + { + return m_inputIndex; + } + + EEffectUniformSemantic EffectInputImpl::getUniformSemantics() const + { + return EffectInputSemanticUtils::GetEffectUniformSemanticFromInternal(m_semantics); + } + + EEffectAttributeSemantic EffectInputImpl::getAttributeSemantics() const + { + return EffectInputSemanticUtils::GetEffectAttributeSemanticFromInternal(m_semantics); + } +} diff --git a/src/client/impl/EffectInputImpl.h b/src/client/impl/EffectInputImpl.h new file mode 100644 index 000000000..a898e4614 --- /dev/null +++ b/src/client/impl/EffectInputImpl.h @@ -0,0 +1,58 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// client api +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/EDataType.h" +#include "ramses/client/EffectInputSemantic.h" + +// framework +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" + +#include +#include + +namespace ramses::internal +{ + class EffectInputImpl final + { + public: + EffectInputImpl() = default; + + void initialize( + const ResourceContentHash& effectHash, + std::string_view name, + ramses::internal::EDataType dataType, + EFixedSemantics semantics, + size_t elementCount, + size_t index); + + [[nodiscard]] ResourceContentHash getEffectHash() const; + [[nodiscard]] const std::string& getName() const; + [[nodiscard]] ramses::internal::EDataType getInternalDataType() const; + [[nodiscard]] EFixedSemantics getSemantics() const; + [[nodiscard]] size_t getElementCount() const; + [[nodiscard]] size_t getInputIndex() const; + + [[nodiscard]] ramses::EDataType getDataType() const; + [[nodiscard]] EEffectUniformSemantic getUniformSemantics() const; + [[nodiscard]] EEffectAttributeSemantic getAttributeSemantics() const; + + private: + ResourceContentHash m_effectHash{}; + std::string m_name; + ramses::internal::EDataType m_dataType{ramses::internal::EDataType::Invalid}; + EFixedSemantics m_semantics{EFixedSemantics::Invalid}; + size_t m_elementCount{0u}; + size_t m_inputIndex{std::numeric_limits::max()}; + }; +} diff --git a/client/ramses-client/impl/EffectInputSemanticUtils.h b/src/client/impl/EffectInputSemanticUtils.h similarity index 53% rename from client/ramses-client/impl/EffectInputSemanticUtils.h rename to src/client/impl/EffectInputSemanticUtils.h index 60d7bb64f..7fb1f7045 100644 --- a/client/ramses-client/impl/EffectInputSemanticUtils.h +++ b/src/client/impl/EffectInputSemanticUtils.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECTINPUTSEMANTICUTILS_H -#define RAMSES_EFFECTINPUTSEMANTICUTILS_H +#pragma once -#include "ramses-client-api/EffectInputSemantic.h" -#include "SceneAPI/EFixedSemantics.h" +#include "ramses/client/EffectInputSemantic.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" #include namespace ramses @@ -18,65 +17,65 @@ namespace ramses class EffectInputSemanticUtils { public: - static ramses_internal::EFixedSemantics GetEffectInputSemanticInternal(EEffectUniformSemantic semanticType) + static ramses::internal::EFixedSemantics GetEffectInputSemanticInternal(EEffectUniformSemantic semanticType) { switch (semanticType) { case EEffectUniformSemantic::ProjectionMatrix: - return ramses_internal::EFixedSemantics::ProjectionMatrix; + return ramses::internal::EFixedSemantics::ProjectionMatrix; case EEffectUniformSemantic::ModelMatrix: - return ramses_internal::EFixedSemantics::ModelMatrix; + return ramses::internal::EFixedSemantics::ModelMatrix; case EEffectUniformSemantic::CameraWorldPosition: - return ramses_internal::EFixedSemantics::CameraWorldPosition; + return ramses::internal::EFixedSemantics::CameraWorldPosition; case EEffectUniformSemantic::ViewMatrix: - return ramses_internal::EFixedSemantics::ViewMatrix; + return ramses::internal::EFixedSemantics::ViewMatrix; case EEffectUniformSemantic::ModelViewMatrix: - return ramses_internal::EFixedSemantics::ModelViewMatrix; + return ramses::internal::EFixedSemantics::ModelViewMatrix; case EEffectUniformSemantic::ModelViewMatrix33: - return ramses_internal::EFixedSemantics::ModelViewMatrix33; + return ramses::internal::EFixedSemantics::ModelViewMatrix33; case EEffectUniformSemantic::ModelViewProjectionMatrix: - return ramses_internal::EFixedSemantics::ModelViewProjectionMatrix; + return ramses::internal::EFixedSemantics::ModelViewProjectionMatrix; case EEffectUniformSemantic::NormalMatrix: - return ramses_internal::EFixedSemantics::NormalMatrix; + return ramses::internal::EFixedSemantics::NormalMatrix; case EEffectUniformSemantic::DisplayBufferResolution: - return ramses_internal::EFixedSemantics::DisplayBufferResolution; + return ramses::internal::EFixedSemantics::DisplayBufferResolution; case EEffectUniformSemantic::TextTexture: - return ramses_internal::EFixedSemantics::TextTexture; + return ramses::internal::EFixedSemantics::TextTexture; case EEffectUniformSemantic::TimeMs: - return ramses_internal::EFixedSemantics::TimeMs; + return ramses::internal::EFixedSemantics::TimeMs; case EEffectUniformSemantic::Invalid: - return ramses_internal::EFixedSemantics::Invalid; + return ramses::internal::EFixedSemantics::Invalid; } assert(false); - return ramses_internal::EFixedSemantics::Invalid; + return ramses::internal::EFixedSemantics::Invalid; } - static EEffectUniformSemantic GetEffectUniformSemanticFromInternal(ramses_internal::EFixedSemantics semanticType) + static EEffectUniformSemantic GetEffectUniformSemanticFromInternal(ramses::internal::EFixedSemantics semanticType) { switch (semanticType) { - case ramses_internal::EFixedSemantics::ProjectionMatrix: + case ramses::internal::EFixedSemantics::ProjectionMatrix: return EEffectUniformSemantic::ProjectionMatrix; - case ramses_internal::EFixedSemantics::ModelMatrix: + case ramses::internal::EFixedSemantics::ModelMatrix: return EEffectUniformSemantic::ModelMatrix; - case ramses_internal::EFixedSemantics::CameraWorldPosition: + case ramses::internal::EFixedSemantics::CameraWorldPosition: return EEffectUniformSemantic::CameraWorldPosition; - case ramses_internal::EFixedSemantics::ViewMatrix: + case ramses::internal::EFixedSemantics::ViewMatrix: return EEffectUniformSemantic::ViewMatrix; - case ramses_internal::EFixedSemantics::ModelViewMatrix: + case ramses::internal::EFixedSemantics::ModelViewMatrix: return EEffectUniformSemantic::ModelViewMatrix; - case ramses_internal::EFixedSemantics::ModelViewMatrix33: + case ramses::internal::EFixedSemantics::ModelViewMatrix33: return EEffectUniformSemantic::ModelViewMatrix33; - case ramses_internal::EFixedSemantics::ModelViewProjectionMatrix: + case ramses::internal::EFixedSemantics::ModelViewProjectionMatrix: return EEffectUniformSemantic::ModelViewProjectionMatrix; - case ramses_internal::EFixedSemantics::DisplayBufferResolution: + case ramses::internal::EFixedSemantics::DisplayBufferResolution: return EEffectUniformSemantic::DisplayBufferResolution; - case ramses_internal::EFixedSemantics::NormalMatrix: + case ramses::internal::EFixedSemantics::NormalMatrix: return EEffectUniformSemantic::NormalMatrix; - case ramses_internal::EFixedSemantics::TextTexture: + case ramses::internal::EFixedSemantics::TextTexture: return EEffectUniformSemantic::TextTexture; - case ramses_internal::EFixedSemantics::TimeMs: + case ramses::internal::EFixedSemantics::TimeMs: return EEffectUniformSemantic::TimeMs; default: return EEffectUniformSemantic::Invalid; @@ -85,29 +84,29 @@ namespace ramses return EEffectUniformSemantic::Invalid; } - static ramses_internal::EFixedSemantics GetEffectInputSemanticInternal(EEffectAttributeSemantic semanticType) + static ramses::internal::EFixedSemantics GetEffectInputSemanticInternal(EEffectAttributeSemantic semanticType) { switch (semanticType) { case EEffectAttributeSemantic::TextPositions: - return ramses_internal::EFixedSemantics::TextPositionsAttribute; + return ramses::internal::EFixedSemantics::TextPositionsAttribute; case EEffectAttributeSemantic::TextTextureCoordinates: - return ramses_internal::EFixedSemantics::TextTextureCoordinatesAttribute; + return ramses::internal::EFixedSemantics::TextTextureCoordinatesAttribute; case EEffectAttributeSemantic::Invalid: - return ramses_internal::EFixedSemantics::Invalid; + return ramses::internal::EFixedSemantics::Invalid; } assert(false); - return ramses_internal::EFixedSemantics::Invalid; + return ramses::internal::EFixedSemantics::Invalid; } - static EEffectAttributeSemantic GetEffectAttributeSemanticFromInternal(ramses_internal::EFixedSemantics semanticType) + static EEffectAttributeSemantic GetEffectAttributeSemanticFromInternal(ramses::internal::EFixedSemantics semanticType) { switch (semanticType) { - case ramses_internal::EFixedSemantics::TextPositionsAttribute: + case ramses::internal::EFixedSemantics::TextPositionsAttribute: return EEffectAttributeSemantic::TextPositions; - case ramses_internal::EFixedSemantics::TextTextureCoordinatesAttribute: + case ramses::internal::EFixedSemantics::TextTextureCoordinatesAttribute: return EEffectAttributeSemantic::TextTextureCoordinates; default: return EEffectAttributeSemantic::Invalid; @@ -115,5 +114,3 @@ namespace ramses } }; } - -#endif diff --git a/src/client/impl/Geometry.cpp b/src/client/impl/Geometry.cpp new file mode 100644 index 000000000..fcfc18409 --- /dev/null +++ b/src/client/impl/Geometry.cpp @@ -0,0 +1,82 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/ArrayBuffer.h" + +// internal +#include "impl/GeometryImpl.h" + +namespace ramses +{ + Geometry::Geometry(std::unique_ptr impl) + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + bool Geometry::setIndices(const ArrayResource& indicesResource) + { + const bool status = m_impl.setIndices(indicesResource.impl()); + LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(indicesResource)); + return status; + } + + bool Geometry::setIndices(const ArrayBuffer& arrayBuffer) + { + const bool status = m_impl.setIndices(arrayBuffer.impl()); + LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(arrayBuffer)); + return status; + } + + bool Geometry::setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint32_t instancingDivisor) + { + const bool status = m_impl.setInputBuffer(attributeInput.impl(), arrayResource.impl(), instancingDivisor, 0u, 0u); + LOG_HL_CLIENT_API3(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayResource), instancingDivisor); + return status; + } + + bool Geometry::setInputBuffer(const AttributeInput& attributeInput, const ArrayResource& arrayResource, uint16_t offset, uint16_t stride) + { + const bool status = m_impl.setInputBuffer(attributeInput.impl(), arrayResource.impl(), 0u, offset, stride); + LOG_HL_CLIENT_API4(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayResource), offset, stride); + return status; + } + + bool Geometry::setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint32_t instancingDivisor /*= 0*/) + { + const bool status = m_impl.setInputBuffer(attributeInput.impl(), arrayBuffer.impl(), instancingDivisor, 0u, 0u); + LOG_HL_CLIENT_API3(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayBuffer), instancingDivisor); + return status; + } + + bool Geometry::setInputBuffer(const AttributeInput& attributeInput, const ArrayBuffer& arrayBuffer, uint16_t offset, uint16_t stride) + { + const bool status = m_impl.setInputBuffer(attributeInput.impl(), arrayBuffer.impl(), 0u, offset, stride); + LOG_HL_CLIENT_API4(status, LOG_API_GENERIC_OBJECT_STRING(attributeInput), LOG_API_RAMSESOBJECT_STRING(arrayBuffer), offset, stride); + return status; + } + + const Effect& Geometry::getEffect() const + { + return m_impl.getEffect(); + } + + internal::GeometryImpl& Geometry::impl() + { + return m_impl; + } + + const internal::GeometryImpl& Geometry::impl() const + { + return m_impl; + } +} diff --git a/src/client/impl/GeometryImpl.cpp b/src/client/impl/GeometryImpl.cpp new file mode 100644 index 000000000..5541165af --- /dev/null +++ b/src/client/impl/GeometryImpl.cpp @@ -0,0 +1,406 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + + +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/ArrayBuffer.h" + +#include "impl/GeometryImpl.h" +#include "impl/EffectInputImpl.h" +#include "impl/ArrayResourceImpl.h" +#include "impl/EffectImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SceneObjectImpl.h" +#include "impl/SceneImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "impl/DataTypeUtils.h" +#include "impl/ObjectIteratorImpl.h" +#include "impl/SerializationContext.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" + +namespace ramses::internal +{ + GeometryImpl::GeometryImpl(SceneImpl& scene, std::string_view name) + : SceneObjectImpl(scene, ERamsesObjectType::Geometry, name) + , m_effectImpl(nullptr) + , m_indicesCount(0u) + { + } + + GeometryImpl::~GeometryImpl() = default; + + void GeometryImpl::initializeFrameworkData(const EffectImpl& effect) + { + m_effectImpl = &effect; + createDataLayout(); + } + + void GeometryImpl::deinitializeFrameworkData() + { + if (m_attributeInstance.isValid()) + { + getIScene().releaseDataInstance(m_attributeInstance); + m_attributeInstance = ramses::internal::DataInstanceHandle::Invalid(); + } + + if (m_attributeLayout.isValid()) + { + getIScene().releaseDataLayout(m_attributeLayout); + m_attributeLayout = ramses::internal::DataLayoutHandle::Invalid(); + } + } + + bool GeometryImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << (m_effectImpl ? serializationContext.getIDForObject(m_effectImpl) : SerializationContext::GetObjectIDNull()); + + outStream << m_attributeLayout; + outStream << m_attributeInstance; + outStream << m_indicesCount; + + return true; + } + + bool GeometryImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_effectImpl); + + inStream >> m_attributeLayout; + inStream >> m_attributeInstance; + inStream >> m_indicesCount; + + serializationContext.addForDependencyResolve(this); + + return true; + } + + bool GeometryImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::resolveDeserializationDependencies(serializationContext)) + return false; + + serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_effectImpl); + + return true; + } + + void GeometryImpl::onValidate(ValidationReportImpl& report) const + { + SceneObjectImpl::onValidate(report); + validateEffect(report); + validateAttribute(report); + } + + void GeometryImpl::validateEffect(ValidationReportImpl& report) const + { + ObjectIteratorImpl iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::Effect); + RamsesObject* ramsesObject = iter.getNext(); + while (nullptr != ramsesObject) + { + const Effect& effect = RamsesObjectTypeUtils::ConvertTo(*ramsesObject); + if (&effect.impl() == m_effectImpl) + return report.addDependentObject(*this, effect.impl()); + + ramsesObject = iter.getNext(); + } + + return report.add(EIssueType::Error, "Geometry is referring to an invalid Effect", &getRamsesObject()); + } + + void GeometryImpl::validateAttribute(ValidationReportImpl& report) const + { + const ramses::internal::DataLayout& layout = getIScene().getDataLayout(m_attributeLayout); + const uint32_t dataLayoutFieldCount = layout.getFieldCount(); + for (ramses::internal::DataFieldHandle fieldIndex(0u); fieldIndex < dataLayoutFieldCount; ++fieldIndex) + { + const ramses::internal::ResourceField& dataResource = + getIScene().getDataResource(m_attributeInstance, fieldIndex); + + if (dataResource.dataBuffer.isValid()) + { + const ramses::internal::EDataType fieldDataType = layout.getField(fieldIndex).dataType; + validateDataBuffer(report, dataResource.dataBuffer, fieldDataType); + } + else + { + if (dataResource.hash.isValid()) + { + validateResource(report, dataResource.hash); + } + } + } + } + + void GeometryImpl::validateResource(ValidationReportImpl& report, ramses::internal::ResourceContentHash resourceHash) const + { + const Resource* resource = getSceneImpl().scanForResourceWithHash(resourceHash); + if (nullptr == resource) + return report.add(EIssueType::Error, "Geometry is referring to resource that does not exist", &getRamsesObject()); + return report.addDependentObject(*this, resource->impl()); + } + + void GeometryImpl::validateDataBuffer(ValidationReportImpl& report, ramses::internal::DataBufferHandle dataBuffer, ramses::internal::EDataType fieldDataType) const + { + if (!getIScene().isDataBufferAllocated(dataBuffer)) + return report.add(EIssueType::Error, "Geometry is referring to data buffer that does not exist", &getRamsesObject()); + + const auto dataBufferType = getIScene().getDataBuffer(dataBuffer).dataType; + if (!dataTypeMatchesInputType(dataBufferType, fieldDataType)) + return report.add(EIssueType::Error, "Geometry is referring to data buffer with type that does not match data layout field type", &getRamsesObject()); + const ArrayBufferImpl* dataBufferImpl = findDataBuffer(dataBuffer); + assert(nullptr != dataBufferImpl); + + return report.addDependentObject(*this, *dataBufferImpl); + } + + ArrayBufferImpl* GeometryImpl::findDataBuffer(ramses::internal::DataBufferHandle dataBufferHandle) const + { + SceneObjectRegistryIterator arrayBufferIter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::ArrayBuffer); + while (auto* dataBuffer = arrayBufferIter.getNextNonConst()) + { + if (dataBuffer->impl().getDataBufferHandle() == dataBufferHandle) + { + return &dataBuffer->impl(); + } + } + + return nullptr; + } + + void GeometryImpl::createDataLayout() + { + assert(!m_attributeLayout.isValid()); + + const ramses::internal::EffectInputInformationVector& attributesList = m_effectImpl->getAttributeInputInformation(); + ramses::internal::DataFieldInfoVector dataFields; + dataFields.reserve(attributesList.size()); + + // Indices are always stored at fixed data slot with index IndicesDataFieldIndex + dataFields.push_back(ramses::internal::DataFieldInfo{ ramses::internal::EDataType::Indices, 1u, ramses::internal::EFixedSemantics::Indices }); + for (const auto& attribInfo : attributesList) + { + dataFields.push_back(ramses::internal::DataFieldInfo{ attribInfo.dataType, attribInfo.elementCount, attribInfo.semantics }); + } + + m_attributeLayout = getIScene().allocateDataLayout(dataFields, m_effectImpl->getLowlevelResourceHash(), {}); + m_attributeInstance = getIScene().allocateDataInstance(m_attributeLayout, {}); + } + + ramses::internal::ResourceContentHash GeometryImpl::getEffectHash() const + { + return m_effectImpl->getLowlevelResourceHash(); + } + + ramses::internal::DataLayoutHandle GeometryImpl::getAttributeDataLayout() const + { + return m_attributeLayout; + } + + ramses::internal::DataInstanceHandle GeometryImpl::getAttributeDataInstance() const + { + return m_attributeInstance; + } + + uint32_t GeometryImpl::getIndicesCount() const + { + return m_indicesCount; + } + + bool GeometryImpl::setIndices(const ArrayResourceImpl& arrayResource) + { + if (!isFromTheSameSceneAs(arrayResource)) + { + getErrorReporting().set("Geometry::setIndices failed, indicesResource is not from the same client as this Geometry.", *this); + return false; + } + + if (!DataTypeUtils::IsValidIndicesType(arrayResource.getElementType())) + { + getErrorReporting().set("Geometry::setIndices failed, indicesResource is not of valid data type.", *this); + return false; + } + + const ramses::internal::DataLayout& layout = getIScene().getDataLayout(m_attributeLayout); + const uint32_t fieldCount = layout.getFieldCount(); + ramses::internal::EFixedSemantics indicesFieldSemantics = ramses::internal::EFixedSemantics::Invalid; + const ramses::internal::DataFieldHandle field(IndicesDataFieldIndex); + + if (IndicesDataFieldIndex < fieldCount) + { + indicesFieldSemantics = layout.getField(field).semantics; + } + + if (indicesFieldSemantics == ramses::internal::EFixedSemantics::Indices) + { + getIScene().setDataResource(m_attributeInstance, field, arrayResource.getLowlevelResourceHash(), ramses::internal::DataBufferHandle::Invalid(), 0u, 0u, 0u); + m_indicesCount = arrayResource.getElementCount(); + + return true; + } + + getErrorReporting().set("Geometry::setIndices failed - indices slot was not enabled in this geometry.", *this); + return false; + } + + bool GeometryImpl::setIndices(const ArrayBufferImpl& dataBuffer) + { + if (!isFromTheSameSceneAs(dataBuffer)) + { + getErrorReporting().set("Geometry::setIndices failed, dataBuffer is not from the same scene as this Geometry.", *this); + return false; + } + + if (!DataTypeUtils::IsValidIndicesType(dataBuffer.getDataType())) + { + getErrorReporting().set("Geometry::setIndices failed, arrayBuffer is not of valid data type.", *this); + return false; + } + + const ramses::internal::DataLayout& layout = getIScene().getDataLayout(m_attributeLayout); + const uint32_t fieldCount = layout.getFieldCount(); + ramses::internal::EFixedSemantics indicesFieldSemantics = ramses::internal::EFixedSemantics::Invalid; + const ramses::internal::DataFieldHandle field(IndicesDataFieldIndex); + + if (IndicesDataFieldIndex < fieldCount) + { + indicesFieldSemantics = layout.getField(field).semantics; + } + + if (indicesFieldSemantics == ramses::internal::EFixedSemantics::Indices) + { + const ramses::internal::DataBufferHandle dataBufferHandle = dataBuffer.getDataBufferHandle(); + + getIScene().setDataResource(m_attributeInstance, field, ramses::internal::ResourceContentHash::Invalid(), dataBufferHandle, 0u, 0u, 0u); + + m_indicesCount = dataBuffer.getElementCount(); + + return true; + } + + getErrorReporting().set("Geometry::setIndices failed - indices slot was not enabled in this geometry.", *this); + return false; + } + + bool GeometryImpl::setInputBuffer(const EffectInputImpl& input, const ArrayResourceImpl& bufferResource, uint32_t instancingDivisor, uint16_t offset, uint16_t stride) + { + if (!isFromTheSameSceneAs(bufferResource)) + { + getErrorReporting().set("Geometry::setInputBuffer failed, array resource is not from the same client as the Geometry.", *this); + return false; + } + + if (!DataTypeUtils::IsValidVerticesType(bufferResource.getElementType())) + { + getErrorReporting().set("Geometry::setInputBuffer failed, array resource is not of valid data type.", *this); + return false; + } + + if (input.getEffectHash() != m_effectImpl->getLowlevelResourceHash()) + { + getErrorReporting().set("Geometry::setInputBuffer failed, input is not properly initialized or cannot be used with this geometry binding.", *this); + return false; + } + + if ((offset > 0 || stride > 0) && bufferResource.getElementType() != ramses::EDataType::ByteBlob) + { + getErrorReporting().set("Geometry::setInputBuffer failed, custom stride/offset can be used only with array resources of type byte blob", *this); + return false; + } + + if (!dataTypeMatchesInputType(DataTypeUtils::ConvertDataTypeToInternal(bufferResource.getElementType()), input.getInternalDataType())) + { + getErrorReporting().set("Geometry::setInputBuffer failed, array resource type does not match input data type", *this); + return false; + } + + // data field index on low level scene is indexed starting after reserved slot for indices + const ramses::internal::DataFieldHandle dataField(static_cast(input.getInputIndex()) + IndicesDataFieldIndex + 1u); + getIScene().setDataResource(m_attributeInstance, dataField, bufferResource.getLowlevelResourceHash(), ramses::internal::DataBufferHandle::Invalid(), instancingDivisor, offset, stride); + + return true; + } + + bool GeometryImpl::setInputBuffer(const EffectInputImpl& input, const ArrayBufferImpl& dataBuffer, uint32_t instancingDivisor, uint16_t offset, uint16_t stride) + { + if (!isFromTheSameSceneAs(dataBuffer)) + { + getErrorReporting().set("Geometry::setInputBuffer failed, dataBuffer is not from the same scene as the Geometry.", *this); + return false; + } + + if (!DataTypeUtils::IsValidVerticesType(dataBuffer.getDataType())) + { + getErrorReporting().set("Geometry::setInputBuffer failed, arrayBuffer is not of valid data type.", *this); + return false; + } + + if (input.getEffectHash() != m_effectImpl->getLowlevelResourceHash()) + { + getErrorReporting().set("Geometry::setInputBuffer failed, input is not properly initialized or cannot be used with this geometry binding.", *this); + return false; + } + + const ramses::internal::DataBufferHandle dataBufferHandle = dataBuffer.getDataBufferHandle(); + const ramses::internal::EDataType dataBufferDataType = getIScene().getDataBuffer(dataBufferHandle).dataType; + + if ((offset > 0 || stride > 0) && dataBuffer.getDataType() != ramses::EDataType::ByteBlob) + { + getErrorReporting().set("Geometry::setInputBuffer failed, custom stride/offset can be used only with data buffers of type byte blob", *this); + return false; + } + + if (!dataTypeMatchesInputType(dataBufferDataType, input.getInternalDataType())) + { + getErrorReporting().set("Geometry::setInputBuffer failed, vertex data buffer type does not match input data type", *this); + return false; + } + + // data field index on low level scene is indexed starting after reserved slot for indices + const ramses::internal::DataFieldHandle dataField(static_cast(input.getInputIndex()) + IndicesDataFieldIndex + 1u); + getIScene().setDataResource(m_attributeInstance, dataField, ramses::internal::ResourceContentHash::Invalid(), dataBufferHandle, instancingDivisor, offset, stride); + + return true; + } + + bool GeometryImpl::dataTypeMatchesInputType(ramses::internal::EDataType resourceType, ramses::internal::EDataType inputDataType) + { + switch (resourceType) + { + case ramses::internal::EDataType::UInt16: + case ramses::internal::EDataType::UInt32: + return inputDataType == ramses::internal::EDataType::Indices; + case ramses::internal::EDataType::ByteBlob: + return ramses::internal::IsBufferDataType(inputDataType); + case ramses::internal::EDataType::Float: + return inputDataType == ramses::internal::EDataType::FloatBuffer; + case ramses::internal::EDataType::Vector2F: + return inputDataType == ramses::internal::EDataType::Vector2Buffer; + case ramses::internal::EDataType::Vector3F: + return inputDataType == ramses::internal::EDataType::Vector3Buffer; + case ramses::internal::EDataType::Vector4F: + return inputDataType == ramses::internal::EDataType::Vector4Buffer; + default: + return false; + } + } + + const Effect& GeometryImpl::getEffect() const + { + return RamsesObjectTypeUtils::ConvertTo(m_effectImpl->getRamsesObject()); + } +} diff --git a/src/client/impl/GeometryImpl.h b/src/client/impl/GeometryImpl.h new file mode 100644 index 000000000..1edf20759 --- /dev/null +++ b/src/client/impl/GeometryImpl.h @@ -0,0 +1,77 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// internal +#include "SceneObjectImpl.h" +#include "impl/ArrayResourceImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/EDataType.h" + +#include + +namespace ramses +{ + class AttributeInput; +} + +namespace ramses::internal +{ + class IScene; + class ResourceImpl; + class EffectImpl; + class ArrayBufferImpl; + class ArrayResourceImpl; + + class GeometryImpl final : public SceneObjectImpl + { + public: + GeometryImpl(SceneImpl& scene, std::string_view name); + ~GeometryImpl() override; + + void initializeFrameworkData(const EffectImpl& effect); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + bool setInputBuffer(const EffectInputImpl& input, const ArrayResourceImpl& bufferResource, uint32_t instancingDivisor, uint16_t offset, uint16_t stride); + bool setInputBuffer(const EffectInputImpl& input, const ArrayBufferImpl& dataBuffer, uint32_t instancingDivisor, uint16_t offset, uint16_t stride); + bool setIndices(const ArrayResourceImpl& arrayResource); + bool setIndices(const ArrayBufferImpl& dataBuffer); + + [[nodiscard]] ramses::internal::ResourceContentHash getEffectHash() const; + [[nodiscard]] ramses::internal::DataLayoutHandle getAttributeDataLayout() const; + [[nodiscard]] ramses::internal::DataInstanceHandle getAttributeDataInstance() const; + [[nodiscard]] uint32_t getIndicesCount() const; + + [[nodiscard]] const Effect& getEffect() const; + + static const uint32_t IndicesDataFieldIndex = 0u; + + private: + void createDataLayout(); + + void validateEffect(ValidationReportImpl& report) const; + void validateAttribute(ValidationReportImpl& report) const; + void validateResource(ValidationReportImpl& report, ramses::internal::ResourceContentHash resourceHash) const; + void validateDataBuffer(ValidationReportImpl& report, ramses::internal::DataBufferHandle dataBuffer, ramses::internal::EDataType fieldDataType) const; + + [[nodiscard]] ArrayBufferImpl* findDataBuffer(ramses::internal::DataBufferHandle dataBufferHandle) const; + + static bool dataTypeMatchesInputType(ramses::internal::EDataType resourceType, ramses::internal::EDataType inputDataType); + + const EffectImpl* m_effectImpl; + ramses::internal::DataLayoutHandle m_attributeLayout; + ramses::internal::DataInstanceHandle m_attributeInstance; + uint32_t m_indicesCount; + }; +} diff --git a/client/ramses-client/impl/IteratorImpl.h b/src/client/impl/IteratorImpl.h similarity index 87% rename from client/ramses-client/impl/IteratorImpl.h rename to src/client/impl/IteratorImpl.h index dda22af1b..d303be607 100644 --- a/client/ramses-client/impl/IteratorImpl.h +++ b/src/client/impl/IteratorImpl.h @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITERATORIMPL_H -#define RAMSES_ITERATORIMPL_H +#pragma once +#include #include -namespace ramses +namespace ramses::internal { template class IteratorImpl @@ -27,8 +27,9 @@ namespace ramses { } + // NOLINTNEXTLINE(modernize-pass-by-value) dedicated overloads for rvalue and lvalue references explicit IteratorImpl(const ObjectVector& objects) - : m_objects{ objects } + : m_objects{objects} , m_objectIterator{ m_objects.begin() } { } @@ -50,5 +51,3 @@ namespace ramses typename ObjectVector::iterator m_objectIterator; }; } - -#endif diff --git a/client/ramses-client-api/MeshNode.cpp b/src/client/impl/MeshNode.cpp similarity index 56% rename from client/ramses-client-api/MeshNode.cpp rename to src/client/impl/MeshNode.cpp index 6b881856d..8ec190141 100644 --- a/client/ramses-client-api/MeshNode.cpp +++ b/src/client/impl/MeshNode.cpp @@ -7,60 +7,60 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" // internal -#include "NodeImpl.h" -#include "MeshNodeImpl.h" +#include "impl/NodeImpl.h" +#include "impl/MeshNodeImpl.h" namespace ramses { - MeshNode::MeshNode(std::unique_ptr impl) + MeshNode::MeshNode(std::unique_ptr impl) : Node{ std::move(impl) } - , m_impl{ static_cast(Node::m_impl) } + , m_impl{ static_cast(Node::m_impl) } { } - status_t MeshNode::setAppearance(Appearance& appearance) + bool MeshNode::setAppearance(Appearance& appearance) { - const status_t status = m_impl.setAppearance(appearance.m_impl); + const bool status = m_impl.setAppearance(appearance.impl()); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(appearance)); return status; } - status_t MeshNode::setGeometryBinding(GeometryBinding& geometry) + bool MeshNode::setGeometry(Geometry& geometry) { - const status_t status = m_impl.setGeometryBinding(geometry.m_impl); + const bool status = m_impl.setGeometry(geometry.impl()); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(geometry)); return status; } - status_t MeshNode::removeAppearanceAndGeometry() + bool MeshNode::removeAppearanceAndGeometry() { - const status_t status = m_impl.removeAppearanceAndGeometry(); + const bool status = m_impl.removeAppearanceAndGeometry(); LOG_HL_CLIENT_API_NOARG(status); return status; } - status_t MeshNode::setStartIndex(uint32_t startIndex) + bool MeshNode::setStartIndex(uint32_t startIndex) { - const status_t status = m_impl.setStartIndex(startIndex); + const bool status = m_impl.setStartIndex(startIndex); LOG_HL_CLIENT_API1(status, startIndex); return status; } - status_t MeshNode::setStartVertex(uint32_t startVertex) + bool MeshNode::setStartVertex(uint32_t startVertex) { - const status_t status = m_impl.setStartVertex(startVertex); + const bool status = m_impl.setStartVertex(startVertex); LOG_HL_CLIENT_API1(status, startVertex); return status; } - status_t MeshNode::setIndexCount(uint32_t indexCount) + bool MeshNode::setIndexCount(uint32_t indexCount) { - const status_t status = m_impl.setIndexCount(indexCount); + const bool status = m_impl.setIndexCount(indexCount); LOG_HL_CLIENT_API1(status, indexCount); return status; } @@ -75,14 +75,14 @@ namespace ramses return m_impl.getAppearance(); } - const GeometryBinding* MeshNode::getGeometryBinding() const + const Geometry* MeshNode::getGeometry() const { - return m_impl.getGeometryBinding(); + return m_impl.getGeometry(); } - GeometryBinding* MeshNode::getGeometryBinding() + Geometry* MeshNode::getGeometry() { - return m_impl.getGeometryBinding(); + return m_impl.getGeometry(); } uint32_t MeshNode::getStartIndex() const @@ -100,7 +100,7 @@ namespace ramses return m_impl.getIndexCount(); } - status_t MeshNode::setInstanceCount(uint32_t instanceCount) + bool MeshNode::setInstanceCount(uint32_t instanceCount) { return m_impl.setInstanceCount(instanceCount); } @@ -109,4 +109,14 @@ namespace ramses { return m_impl.getInstanceCount(); } + + internal::MeshNodeImpl& MeshNode::impl() + { + return m_impl; + } + + const internal::MeshNodeImpl& MeshNode::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/MeshNodeImpl.cpp b/src/client/impl/MeshNodeImpl.cpp similarity index 53% rename from client/ramses-client/impl/MeshNodeImpl.cpp rename to src/client/impl/MeshNodeImpl.cpp index c250cf398..5c7aa47a7 100644 --- a/client/ramses-client/impl/MeshNodeImpl.cpp +++ b/src/client/impl/MeshNodeImpl.cpp @@ -7,26 +7,26 @@ // ------------------------------------------------------------------------- // internal -#include "MeshNodeImpl.h" +#include "impl/MeshNodeImpl.h" // API -#include "ramses-client-api/GeometryBinding.h" +#include "ramses/client/Geometry.h" // impls -#include "AppearanceImpl.h" -#include "EffectImpl.h" -#include "ArrayResourceImpl.h" -#include "AppearanceImpl.h" -#include "GeometryBindingImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "VisibilityModeUtils.h" +#include "impl/AppearanceImpl.h" +#include "impl/EffectImpl.h" +#include "impl/ArrayResourceImpl.h" +#include "impl/AppearanceImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SerializationContext.h" +#include "impl/ErrorReporting.h" // internal -#include "Resource/IResource.h" -#include "SerializationContext.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/SceneGraph/Scene/ClientScene.h" -namespace ramses +namespace ramses::internal { MeshNodeImpl::MeshNodeImpl(SceneImpl& scene, std::string_view nodeName) : NodeImpl(scene, ERamsesObjectType::MeshNode, nodeName) @@ -35,13 +35,12 @@ namespace ramses { } - MeshNodeImpl::~MeshNodeImpl() - { - } + MeshNodeImpl::~MeshNodeImpl() = default; - status_t MeshNodeImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool MeshNodeImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(NodeImpl::serialize(outStream, serializationContext)); + if (!NodeImpl::serialize(outStream, serializationContext)) + return false; outStream << m_renderableHandle; if (m_appearanceImpl != nullptr) @@ -61,61 +60,68 @@ namespace ramses outStream << SerializationContext::GetObjectIDNull(); } - return StatusOK; + return true; } - status_t MeshNodeImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool MeshNodeImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(NodeImpl::deserialize(inStream, serializationContext)); + if (!NodeImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_renderableHandle; - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_appearanceImpl); - serializationContext.ReadDependentPointerAndStoreAsID(inStream, m_geometryImpl); + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_appearanceImpl); + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_geometryImpl); serializationContext.addForDependencyResolve(this); - return StatusOK; + return true; } - status_t MeshNodeImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + bool MeshNodeImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(NodeImpl::resolveDeserializationDependencies(serializationContext)); + if (!NodeImpl::resolveDeserializationDependencies(serializationContext)) + return false; serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_appearanceImpl); serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_geometryImpl); - return StatusOK; + return true; } - status_t MeshNodeImpl::validate() const + void MeshNodeImpl::onValidate(ValidationReportImpl& report) const { - status_t status = NodeImpl::validate(); + NodeImpl::onValidate(report); if (nullptr == m_appearanceImpl) - status = addValidationMessage(EValidationSeverity::Error, "meshnode does not have an appearance set"); + { + report.add(EIssueType::Error, "meshnode does not have an appearance set", &getRamsesObject()); + } else - status = std::max(status, addValidationOfDependentObject(*m_appearanceImpl)); + { + report.addDependentObject(*this, *m_appearanceImpl); + } if (nullptr == m_geometryImpl) - status = addValidationMessage(EValidationSeverity::Error, "meshnode does not have a geometryBinding set"); + { + report.add(EIssueType::Error, "meshnode does not have a geometryBinding set", &getRamsesObject()); + } else { - status = std::max(status, addValidationOfDependentObject(*m_geometryImpl)); + report.addDependentObject(*this, *m_geometryImpl); const bool hasIndexArray = m_geometryImpl->getIndicesCount() > 0; if (hasIndexArray && (m_geometryImpl->getIndicesCount() < getStartIndex() + getIndexCount())) - status = addValidationMessage(EValidationSeverity::Error, "startIndex + indexCount exceeds indices of indexarray"); + report.add(EIssueType::Error, "startIndex + indexCount exceeds indices of indexarray", &getRamsesObject()); if (getIndexCount() == 0) - status = addValidationMessage(EValidationSeverity::Error, "indexCount must be greater 0"); + report.add(EIssueType::Error, "indexCount must be greater 0", &getRamsesObject()); } - return status; } void MeshNodeImpl::initializeFrameworkData() { NodeImpl::initializeFrameworkData(); - m_renderableHandle = getIScene().allocateRenderable(getNodeHandle(), ramses_internal::RenderableHandle::Invalid()); + m_renderableHandle = getIScene().allocateRenderable(getNodeHandle(), ramses::internal::RenderableHandle::Invalid()); } void MeshNodeImpl::deinitializeFrameworkData() @@ -126,109 +132,108 @@ namespace ramses NodeImpl::deinitializeFrameworkData(); } - status_t MeshNodeImpl::setAppearance(AppearanceImpl& appearance) + bool MeshNodeImpl::setAppearance(AppearanceImpl& appearance) { if (!isFromTheSameSceneAs(appearance)) { - return addErrorEntry("MeshNode::setAppearance failed - appearance is not from the same scene as this MeshNode."); + getErrorReporting().set("MeshNode::setAppearance failed - appearance is not from the same scene as this MeshNode.", *this); + return false; } if (m_appearanceImpl == &appearance) { - return StatusOK; + return true; } if (m_geometryImpl != nullptr && !AreGeometryAndAppearanceCompatible(*m_geometryImpl, appearance)) { - return addErrorEntry("MeshNode::setAppearance failed - previously assigned geometry does not provide all vertex attributes required by the appearance!"); + getErrorReporting().set("MeshNode::setAppearance failed - previously assigned geometry does not provide all vertex attributes required by the appearance!", *this); + return false; } m_appearanceImpl = &appearance; getIScene().setRenderableUniformsDataInstanceAndState(m_renderableHandle, appearance.getUniformDataInstance(), appearance.getRenderStateHandle()); - return StatusOK; + return true; } - status_t MeshNodeImpl::setGeometryBinding(GeometryBindingImpl& geometry) + bool MeshNodeImpl::setGeometry(GeometryImpl& geometry) { if (!isFromTheSameSceneAs(geometry)) { - return addErrorEntry("MeshNode::setGeometryBinding failed - geometry is not from the same scene as this MeshNode."); + getErrorReporting().set("MeshNode::setGeometry failed - geometry is not from the same scene as this MeshNode.", *this); + return false; } if (m_geometryImpl == &geometry) { - return StatusOK; + return true; } if (m_appearanceImpl != nullptr && !AreGeometryAndAppearanceCompatible(geometry, *m_appearanceImpl)) { - return addErrorEntry("MeshNode::setGeometry failed - the geometry does not provide all vertex attributes required by previously assigned appearance!"); + getErrorReporting().set("MeshNode::setGeometry failed - the geometry does not provide all vertex attributes required by previously assigned appearance!", *this); + return false; } m_geometryImpl = &geometry; - const uint32_t numberOfIndicesFromGeometryBinding = geometry.getIndicesCount(); - const bool geometryBindingProvidesIndexArray = numberOfIndicesFromGeometryBinding > 0; + const uint32_t numberOfIndicesFromGeometry = geometry.getIndicesCount(); + const bool geometryBindingProvidesIndexArray = numberOfIndicesFromGeometry > 0; if (geometryBindingProvidesIndexArray) { setStartIndex(0u); - setIndexCount(numberOfIndicesFromGeometryBinding); + setIndexCount(numberOfIndicesFromGeometry); } - getIScene().setRenderableDataInstance(m_renderableHandle, ramses_internal::ERenderableDataSlotType_Geometry, geometry.getAttributeDataInstance()); + getIScene().setRenderableDataInstance(m_renderableHandle, ramses::internal::ERenderableDataSlotType_Geometry, geometry.getAttributeDataInstance()); - return StatusOK; + return true; } - status_t MeshNodeImpl::removeAppearanceAndGeometry() + bool MeshNodeImpl::removeAppearanceAndGeometry() { m_appearanceImpl = nullptr; - getIScene().setRenderableUniformsDataInstanceAndState(m_renderableHandle, ramses_internal::DataInstanceHandle::Invalid(), ramses_internal::RenderStateHandle::Invalid()); + getIScene().setRenderableUniformsDataInstanceAndState(m_renderableHandle, ramses::internal::DataInstanceHandle::Invalid(), ramses::internal::RenderStateHandle::Invalid()); m_geometryImpl = nullptr; - getIScene().setRenderableDataInstance(m_renderableHandle, ramses_internal::ERenderableDataSlotType_Geometry, ramses_internal::DataInstanceHandle::Invalid()); + getIScene().setRenderableDataInstance(m_renderableHandle, ramses::internal::ERenderableDataSlotType_Geometry, ramses::internal::DataInstanceHandle::Invalid()); return setIndexCount(0u); } - status_t MeshNodeImpl::setStartIndex(uint32_t startIndex) + bool MeshNodeImpl::setStartIndex(uint32_t startIndex) { getIScene().setRenderableStartIndex(m_renderableHandle, startIndex); - return StatusOK; + return true; } - status_t MeshNodeImpl::setIndexCount(uint32_t indexCount) + bool MeshNodeImpl::setIndexCount(uint32_t indexCount) { getIScene().setRenderableIndexCount(m_renderableHandle, indexCount); - return StatusOK; + return true; } - status_t MeshNodeImpl::setFlattenedVisibility(EVisibilityMode mode) + bool MeshNodeImpl::setFlattenedVisibility(EVisibilityMode mode) { - getIScene().setRenderableVisibility(m_renderableHandle, VisibilityModeUtils::ConvertToLL(mode)); - return StatusOK; + getIScene().setRenderableVisibility(m_renderableHandle, mode); + return true; } - status_t MeshNodeImpl::setInstanceCount(uint32_t instanceCount) + bool MeshNodeImpl::setInstanceCount(uint32_t instanceCount) { - if (instanceCount == 0) - { - return addErrorEntry("MeshNode::setInstanceCount failed: instance count must not be 0!"); - } - getIScene().setRenderableInstanceCount(m_renderableHandle, instanceCount); - return StatusOK; + return true; } - ramses::status_t MeshNodeImpl::setStartVertex(uint32_t startVertex) + bool MeshNodeImpl::setStartVertex(uint32_t startVertex) { getIScene().setRenderableStartVertex(m_renderableHandle, startVertex); - return StatusOK; + return true; } - ramses_internal::RenderableHandle MeshNodeImpl::getRenderableHandle() const + ramses::internal::RenderableHandle MeshNodeImpl::getRenderableHandle() const { return m_renderableHandle; } @@ -254,25 +259,25 @@ namespace ramses return const_cast((const_cast(*this)).getAppearance()); } - const GeometryBindingImpl* MeshNodeImpl::getGeometryBindingImpl() const + const GeometryImpl* MeshNodeImpl::getGeometryImpl() const { return m_geometryImpl; } - const GeometryBinding* MeshNodeImpl::getGeometryBinding() const + const Geometry* MeshNodeImpl::getGeometry() const { if (m_geometryImpl != nullptr) { - return &RamsesObjectTypeUtils::ConvertTo(m_geometryImpl->getRamsesObject()); + return &RamsesObjectTypeUtils::ConvertTo(m_geometryImpl->getRamsesObject()); } return nullptr; } - GeometryBinding* MeshNodeImpl::getGeometryBinding() + Geometry* MeshNodeImpl::getGeometry() { - // non-const version of getGeometryBinding cast to its const version to avoid duplicating code - return const_cast((const_cast(*this)).getGeometryBinding()); + // non-const version of getGeometry cast to its const version to avoid duplicating code + return const_cast((const_cast(*this)).getGeometry()); } uint32_t MeshNodeImpl::getStartIndex() const @@ -287,7 +292,7 @@ namespace ramses EVisibilityMode MeshNodeImpl::getFlattenedVisibility() const { - return VisibilityModeUtils::ConvertToHL(getIScene().getRenderable(m_renderableHandle).visibilityMode); + return getIScene().getRenderable(m_renderableHandle).visibilityMode; } uint32_t MeshNodeImpl::getInstanceCount() const @@ -295,7 +300,7 @@ namespace ramses return getIScene().getRenderable(m_renderableHandle).instanceCount; } - bool MeshNodeImpl::AreGeometryAndAppearanceCompatible(const GeometryBindingImpl& geometry, const AppearanceImpl& appearance) + bool MeshNodeImpl::AreGeometryAndAppearanceCompatible(const GeometryImpl& geometry, const AppearanceImpl& appearance) { return geometry.getEffectHash() == appearance.getEffectImpl()->getLowlevelResourceHash(); } diff --git a/src/client/impl/MeshNodeImpl.h b/src/client/impl/MeshNodeImpl.h new file mode 100644 index 000000000..4265df05e --- /dev/null +++ b/src/client/impl/MeshNodeImpl.h @@ -0,0 +1,77 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// internal +#include "impl/NodeImpl.h" + +#include + +namespace ramses::internal +{ + class ClientApplicationLogic; +} + +namespace ramses +{ + class Appearance; + class Geometry; +} + +namespace ramses::internal +{ + class GeometryImpl; + class AppearanceImpl; + + class MeshNodeImpl final : public NodeImpl + { + public: + MeshNodeImpl(SceneImpl& scene, std::string_view nodeName); + ~MeshNodeImpl() override; + + void initializeFrameworkData(); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + bool setAppearance(AppearanceImpl& appearanceImpl); + bool setGeometry(GeometryImpl& geometryImpl); + bool removeAppearanceAndGeometry(); + bool setStartIndex(uint32_t startIndex); + [[nodiscard]] uint32_t getStartIndex() const; + bool setIndexCount(uint32_t indexCount); + [[nodiscard]] uint32_t getIndexCount() const; + bool setFlattenedVisibility(EVisibilityMode mode); + [[nodiscard]] EVisibilityMode getFlattenedVisibility() const; + bool setInstanceCount(uint32_t instanceCount); + [[nodiscard]] uint32_t getInstanceCount() const; + bool setStartVertex(uint32_t startVertex); + [[nodiscard]] uint32_t getStartVertex() const; + + [[nodiscard]] ramses::internal::RenderableHandle getRenderableHandle() const; + + [[nodiscard]] const AppearanceImpl* getAppearanceImpl() const; + [[nodiscard]] const Appearance* getAppearance() const; + [[nodiscard]] Appearance* getAppearance(); + + [[nodiscard]] const GeometryImpl* getGeometryImpl() const; + [[nodiscard]] const Geometry* getGeometry() const; + [[nodiscard]] Geometry* getGeometry(); + + private: + static bool AreGeometryAndAppearanceCompatible(const GeometryImpl& geometry, const AppearanceImpl& appearance); + + ramses::internal::RenderableHandle m_renderableHandle; + + const AppearanceImpl* m_appearanceImpl; + const GeometryImpl* m_geometryImpl; + }; +} diff --git a/client/ramses-client-api/Node.cpp b/src/client/impl/Node.cpp similarity index 58% rename from client/ramses-client-api/Node.cpp rename to src/client/impl/Node.cpp index 7e38664a6..2c861dee3 100644 --- a/client/ramses-client-api/Node.cpp +++ b/src/client/impl/Node.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/Node.h" +#include "ramses/client/Node.h" // internal -#include "NodeImpl.h" -#include "VisibilityModeUtils.h" +#include "impl/NodeImpl.h" +#include "internal/VisibilityModeUtils.h" namespace ramses { - Node::Node(std::unique_ptr impl) + Node::Node(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } @@ -41,23 +41,23 @@ namespace ramses return m_impl.getChild(index); } - status_t Node::addChild(Node& node) + bool Node::addChild(Node& node) { - const status_t status = m_impl.addChild(node.m_impl); + const bool status = m_impl.addChild(node.m_impl); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(node)); return status; } - status_t Node::removeChild(Node& node) + bool Node::removeChild(Node& node) { - const status_t status = m_impl.removeChild(node.m_impl); + const bool status = m_impl.removeChild(node.m_impl); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(node)); return status; } - status_t Node::removeAllChildren() + bool Node::removeAllChildren() { - const status_t status = m_impl.removeAllChildren(); + const bool status = m_impl.removeAllChildren(); LOG_HL_CLIENT_API_NOARG(status); return status; } @@ -77,99 +77,99 @@ namespace ramses return m_impl.getParent(); } - status_t Node::setParent(Node& node) + bool Node::setParent(Node& node) { - const status_t status = m_impl.setParent(node.m_impl); + const bool status = m_impl.setParent(node.m_impl); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(node)); return status; } - status_t Node::removeParent() + bool Node::removeParent() { return m_impl.removeParent(); } - status_t Node::getModelMatrix(matrix44f& modelMatrix) const + bool Node::getModelMatrix(matrix44f& modelMatrix) const { return m_impl.getModelMatrix(modelMatrix); } - status_t Node::getInverseModelMatrix(matrix44f& inverseModelMatrix) const + bool Node::getInverseModelMatrix(matrix44f& inverseModelMatrix) const { return m_impl.getInverseModelMatrix(inverseModelMatrix); } - status_t Node::setRotation(const vec3f& rotation, ERotationType rotationType) + bool Node::setRotation(const vec3f& rotation, ERotationType rotationType) { - const status_t status = m_impl.setRotation(rotation, rotationType); + const bool status = m_impl.setRotation(rotation, rotationType); LOG_HL_CLIENT_API4(status, rotation.x, rotation.y, rotation.z, rotationType); return status; } - status_t Node::setRotation(const quat& rotation) + bool Node::setRotation(const quat& rotation) { - const status_t status = m_impl.setRotation(rotation); + const bool status = m_impl.setRotation(rotation); LOG_HL_CLIENT_API4(status, rotation.w, rotation.x, rotation.y, rotation.z); return status; } - ramses::ERotationType Node::getRotationType() const + ERotationType Node::getRotationType() const { return m_impl.getRotationType(); } - ramses::status_t Node::getRotation(vec3f& rotation) const + bool Node::getRotation(vec3f& rotation) const { return m_impl.getRotation(rotation); } - ramses::status_t Node::getRotation(quat& rotation) const + bool Node::getRotation(quat& rotation) const { return m_impl.getRotation(rotation); } - ramses::status_t Node::translate(const vec3f& translation) + bool Node::translate(const vec3f& translation) { - const status_t status = m_impl.translate(translation); + const bool status = m_impl.translate(translation); LOG_HL_CLIENT_API3(status, translation.x, translation.y, translation.z); return status; } - ramses::status_t Node::setTranslation(const vec3f& translation) + bool Node::setTranslation(const vec3f& translation) { - const status_t status = m_impl.setTranslation(translation); + const bool status = m_impl.setTranslation(translation); LOG_HL_CLIENT_API3(status, translation.x, translation.y, translation.z); return status; } - ramses::status_t Node::getTranslation(vec3f& translation) const + bool Node::getTranslation(vec3f& translation) const { return m_impl.getTranslation(translation); } - ramses::status_t Node::scale(const vec3f& scaling) + bool Node::scale(const vec3f& scaling) { - const status_t status = m_impl.scale(scaling); + const bool status = m_impl.scale(scaling); LOG_HL_CLIENT_API3(status, scaling.x, scaling.y, scaling.z); return status; } - ramses::status_t Node::setScaling(const vec3f& scaling) + bool Node::setScaling(const vec3f& scaling) { - const status_t status = m_impl.setScaling(scaling); + const bool status = m_impl.setScaling(scaling); LOG_HL_CLIENT_API3(status, scaling.x, scaling.y, scaling.z); return status; } - ramses::status_t Node::getScaling(vec3f& scaling) const + bool Node::getScaling(vec3f& scaling) const { return m_impl.getScaling(scaling); } - ramses::status_t Node::setVisibility(EVisibilityMode mode) + bool Node::setVisibility(EVisibilityMode mode) { - const status_t status = m_impl.setVisibility(mode); - LOG_HL_CLIENT_API1(status, VisibilityModeUtils::ToString(mode)); + const bool status = m_impl.setVisibility(mode); + LOG_HL_CLIENT_API1(status, ramses::internal::EnumToString(mode)); return status; } @@ -177,4 +177,14 @@ namespace ramses { return m_impl.getVisibility(); } + + internal::NodeImpl& Node::impl() + { + return m_impl; + } + + const internal::NodeImpl& Node::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/NodeImpl.cpp b/src/client/impl/NodeImpl.cpp similarity index 57% rename from client/ramses-client/impl/NodeImpl.cpp rename to src/client/impl/NodeImpl.cpp index 61c5e8827..20999fe67 100644 --- a/client/ramses-client/impl/NodeImpl.cpp +++ b/src/client/impl/NodeImpl.cpp @@ -6,16 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "NodeImpl.h" -#include "SceneImpl.h" -#include "ramses-client-api/Node.h" -#include "SerializationContext.h" -#include "RamsesObjectTypeUtils.h" -#include "Scene/ClientScene.h" -#include "RotationTypeUtils.h" +#include "ramses/client/Node.h" +#include "impl/NodeImpl.h" +#include "impl/SceneImpl.h" +#include "impl/SerializationContext.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SceneObjectRegistry.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include "glm/gtc/type_ptr.hpp" -namespace ramses +namespace ramses::internal { NodeImpl::NodeImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view nodeName) : SceneObjectImpl(scene, type, nodeName) @@ -24,24 +25,24 @@ namespace ramses { } - NodeImpl::~NodeImpl() - { - } + NodeImpl::~NodeImpl() = default; - status_t NodeImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool NodeImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << m_nodeHandle; outStream << m_visibilityMode; outStream << m_transformHandle; - return StatusOK; + return true; } - status_t NodeImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool NodeImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_nodeHandle; inStream >> m_visibilityMode; @@ -50,20 +51,22 @@ namespace ramses serializationContext.addForDependencyResolve(this); serializationContext.addNodeHandleToNodeImplMapping(m_nodeHandle, this); - return StatusOK; + return true; } - status_t NodeImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + bool NodeImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::resolveDeserializationDependencies(serializationContext)); + if (!SceneObjectImpl::resolveDeserializationDependencies(serializationContext)) + return false; - const ramses_internal::NodeHandle parentNodeHandle = getIScene().getParent(m_nodeHandle); + const ramses::internal::NodeHandle parentNodeHandle = getIScene().getParent(m_nodeHandle); if (parentNodeHandle.isValid()) { m_parent = serializationContext.getNodeImplForHandle(parentNodeHandle); if (m_parent == nullptr) { - return addErrorEntry("Node::resolveDeserializationDependencies failed, m_parent is NULL."); + getErrorReporting().set("Node::resolveDeserializationDependencies failed, m_parent is NULL."); + return false; } } @@ -71,22 +74,23 @@ namespace ramses m_children.reserve(childCount); for (uint32_t i = 0; i < childCount; i++) { - const ramses_internal::NodeHandle childNodeHandle = getIScene().getChild(m_nodeHandle, i); + const ramses::internal::NodeHandle childNodeHandle = getIScene().getChild(m_nodeHandle, i); NodeImpl* childNode = serializationContext.getNodeImplForHandle(childNodeHandle); if (childNode == nullptr) { - return addErrorEntry("Node::resolveDeserializationDependencies failed, childNode is NULL."); + getErrorReporting().set("Node::resolveDeserializationDependencies failed, childNode is NULL."); + return false; } m_children.push_back(childNode); } - return StatusOK; + return true; } void NodeImpl::initializeFrameworkData() { - m_nodeHandle = getIScene().allocateNode(0u, ramses_internal::NodeHandle::Invalid()); + m_nodeHandle = getIScene().allocateNode(0u, ramses::internal::NodeHandle::Invalid()); } void NodeImpl::deinitializeFrameworkData() @@ -94,12 +98,12 @@ namespace ramses if (m_transformHandle.isValid()) { getIScene().releaseTransform(m_transformHandle); - m_transformHandle = ramses_internal::TransformHandle::Invalid(); + m_transformHandle = ramses::internal::TransformHandle::Invalid(); } assert(m_nodeHandle.isValid()); getIScene().releaseNode(m_nodeHandle); - m_nodeHandle = ramses_internal::NodeHandle::Invalid(); + m_nodeHandle = ramses::internal::NodeHandle::Invalid(); } bool NodeImpl::hasChild() const @@ -140,27 +144,28 @@ namespace ramses return *m_children[index]; } - status_t NodeImpl::removeChild(NodeImpl& node) + bool NodeImpl::removeChild(NodeImpl& node) { - const auto it = ramses_internal::find_c(m_children, &node); + const auto it = ramses::internal::find_c(m_children, &node); if (it == m_children.end()) { - return node.addErrorEntry("Node::removeChild failed, child not found."); + getErrorReporting().set("Node::removeChild failed, child not found.", *this); + return false; } removeChildInternally(it); - return StatusOK; + return true; } - status_t NodeImpl::removeAllChildren() + bool NodeImpl::removeAllChildren() { while (!m_children.empty()) { removeChildInternally(m_children.end() - 1); } - return StatusOK; + return true; } bool NodeImpl::hasParent() const @@ -188,21 +193,24 @@ namespace ramses return m_parent; } - status_t NodeImpl::addChild(NodeImpl& childNode) + bool NodeImpl::addChild(NodeImpl& childNode) { if (!isFromTheSameSceneAs(childNode)) { - return addErrorEntry("Node::addChildToNode failed, nodes were created in different scenes."); + getErrorReporting().set("Node::addChildToNode failed, nodes were created in different scenes.", *this); + return false; } if (this == &childNode) { - return addErrorEntry("Node::addChildToNode failed, trying to reparent node to itself."); + getErrorReporting().set("Node::addChildToNode failed, trying to reparent node to itself.", *this); + return false; } - if (childNode.hasParent() && (childNode.removeParent() != StatusOK)) + if (childNode.hasParent() && !childNode.removeParent()) { - return addErrorEntry("Node::addChildToNode failed, failed to remove current parent."); + getErrorReporting().set("Node::addChildToNode failed, failed to remove current parent.", *this); + return false; } childNode.markDirty(); @@ -214,42 +222,43 @@ namespace ramses childNode.m_parent = this; m_children.push_back(&childNode); - return StatusOK; + return true; } - status_t NodeImpl::setParent(NodeImpl& parentNode) + bool NodeImpl::setParent(NodeImpl& parentNode) { return parentNode.addChild(*this); } - status_t NodeImpl::removeParent() + bool NodeImpl::removeParent() { if (!hasParent()) { - return addErrorEntry("Node::removeParent failed, node has no parent."); + getErrorReporting().set("Node::removeParent failed, node has no parent.", *this); + return false; } return m_parent->removeChild(*this); } - status_t NodeImpl::getModelMatrix(matrix44f& modelMatrix) const + bool NodeImpl::getModelMatrix(matrix44f& modelMatrix) const { - modelMatrix = getIScene().updateMatrixCache(ramses_internal::ETransformationMatrixType_World, m_nodeHandle); - return StatusOK; + modelMatrix = getIScene().updateMatrixCache(ramses::internal::ETransformationMatrixType_World, m_nodeHandle); + return true; } - status_t NodeImpl::getInverseModelMatrix(matrix44f& inverseModelMatrix) const + bool NodeImpl::getInverseModelMatrix(matrix44f& inverseModelMatrix) const { - inverseModelMatrix = getIScene().updateMatrixCache(ramses_internal::ETransformationMatrixType_Object, m_nodeHandle); - return StatusOK; + inverseModelMatrix = getIScene().updateMatrixCache(ramses::internal::ETransformationMatrixType_Object, m_nodeHandle); + return true; } - ramses_internal::SceneId NodeImpl::getSceneId() const + ramses::internal::SceneId NodeImpl::getSceneId() const { return getIScene().getSceneId(); } - ramses_internal::NodeHandle NodeImpl::getNodeHandle() const + ramses::internal::NodeHandle NodeImpl::getNodeHandle() const { return m_nodeHandle; } @@ -275,28 +284,28 @@ namespace ramses getIScene().removeChildFromNode(m_nodeHandle, child.m_nodeHandle); } - status_t NodeImpl::translate(const vec3f& translation) + bool NodeImpl::translate(const vec3f& translation) { if (!m_transformHandle.isValid()) { - if (translation == ramses_internal::IScene::IdentityTranslation) + if (translation == ramses::internal::IScene::IdentityTranslation) { - return StatusOK; + return true; } initializeTransform(); } const auto& previous = getIScene().getTranslation(m_transformHandle); getIScene().setTranslation(m_transformHandle, previous + glm::vec3(translation)); - return StatusOK; + return true; } - status_t NodeImpl::setTranslation(const vec3f& translation) + bool NodeImpl::setTranslation(const vec3f& translation) { if (!m_transformHandle.isValid()) { - if (translation == ramses_internal::IScene::IdentityTranslation) + if (translation == ramses::internal::IScene::IdentityTranslation) { - return StatusOK; + return true; } initializeTransform(); } @@ -304,38 +313,38 @@ namespace ramses { getIScene().setTranslation(m_transformHandle, translation); } - return StatusOK; + return true; } - status_t NodeImpl::getTranslation(vec3f& translation) const + bool NodeImpl::getTranslation(vec3f& translation) const { if (!m_transformHandle.isValid()) { - translation = ramses_internal::IScene::IdentityTranslation; - return StatusOK; + translation = ramses::internal::IScene::IdentityTranslation; + return true; } translation = getIScene().getTranslation(m_transformHandle); - return StatusOK; + return true; } - ramses::status_t NodeImpl::setRotation(const vec3f& rotation, ERotationType rotationType) + bool NodeImpl::setRotation(const vec3f& rotation, ERotationType rotationType) { if (rotationType == ERotationType::Quaternion) { - return addErrorEntry("Invalid rotation rotationType: Quaternion"); + getErrorReporting().set("Invalid rotation rotationType: Quaternion", *this); + return false; } - const auto rotationConventionInternal = RotationTypeUtils::ConvertRotationTypeToInternal(rotationType); - return setRotationInternal({rotation.x, rotation.y, rotation.z, 1.f}, rotationConventionInternal); + return setRotationInternal({rotation.x, rotation.y, rotation.z, 1.f}, rotationType); } - ramses::status_t NodeImpl::setRotationInternal(glm::vec4&& rotation, ramses_internal::ERotationType rotationType) + bool NodeImpl::setRotationInternal(glm::vec4&& rotation, ERotationType rotationType) { if (!m_transformHandle.isValid()) { - if (rotation == ramses_internal::IScene::IdentityRotation) + if (rotation == ramses::internal::IScene::IdentityRotation) { - return StatusOK; + return true; } initializeTransform(); } @@ -346,7 +355,7 @@ namespace ramses getIScene().setRotation(m_transformHandle, rotation, rotationType); } - return StatusOK; + return true; } ERotationType NodeImpl::getRotationType() const @@ -355,74 +364,75 @@ namespace ramses { return ERotationType::Euler_XYZ; } - const auto rotationConventionInternal = getIScene().getRotationType(m_transformHandle); - return RotationTypeUtils::ConvertRotationTypeFromInternal(rotationConventionInternal); + return getIScene().getRotationType(m_transformHandle); } - status_t NodeImpl::getRotation(vec3f& rotation) const + bool NodeImpl::getRotation(vec3f& rotation) const { if (!m_transformHandle.isValid()) { - rotation = ramses_internal::IScene::IdentityRotation; - return StatusOK; + rotation = ramses::internal::IScene::IdentityRotation; + return true; } - if (ramses_internal::ERotationType::Quaternion == getIScene().getRotationType(m_transformHandle)) + if (ERotationType::Quaternion == getIScene().getRotationType(m_transformHandle)) { - return addErrorEntry("Node::getRotation(vec3f&) failed: rotation was set by quaternion before. Check Node::getRotationType()."); + getErrorReporting().set("Node::getRotation(vec3f&) failed: rotation was set by quaternion before. Check Node::getRotationType().", *this); + return false; } rotation = getIScene().getRotation(m_transformHandle); - return StatusOK; + return true; } - status_t NodeImpl::setRotation(const quat& rotation) + bool NodeImpl::setRotation(const quat& rotation) { glm::vec4 vec{rotation.x, rotation.y, rotation.z, rotation.w}; - return setRotationInternal(std::move(vec), ramses_internal::ERotationType::Quaternion); + return setRotationInternal(std::move(vec), ERotationType::Quaternion); } - ramses::status_t NodeImpl::getRotation(quat& rotation) const + bool NodeImpl::getRotation(quat& rotation) const { if (!m_transformHandle.isValid()) { rotation = glm::identity(); - return StatusOK; + return true; } - if (ramses_internal::ERotationType::Quaternion != getIScene().getRotationType(m_transformHandle)) + if (ERotationType::Quaternion != getIScene().getRotationType(m_transformHandle)) { - return addErrorEntry("Node::getRotation(quat&) failed: rotation was set by euler angles before. Check Node::getRotationType()."); + getErrorReporting().set("Node::getRotation(quat&) failed: rotation was set by euler angles before. Check Node::getRotationType().", *this); + return false; } const auto& value = getIScene().getRotation(m_transformHandle); rotation = quat(value.w, value.x, value.y, value.z); - return StatusOK; + return true; } - status_t NodeImpl::scale(const vec3f& scaling) + bool NodeImpl::scale(const vec3f& scaling) { if (!m_transformHandle.isValid()) { - if (scaling == ramses_internal::IScene::IdentityScaling) + if (scaling == ramses::internal::IScene::IdentityScaling) { - return StatusOK; + return true; } initializeTransform(); } const auto& previous = getIScene().getScaling(m_transformHandle); getIScene().setScaling(m_transformHandle, previous * scaling); - return StatusOK; + return true; } - status_t NodeImpl::setScaling(const vec3f& scaling) + bool NodeImpl::setScaling(const vec3f& scaling) { if (!m_transformHandle.isValid()) { - if (scaling == ramses_internal::IScene::IdentityScaling) + if (scaling == ramses::internal::IScene::IdentityScaling) { - return StatusOK; + return true; } initializeTransform(); } @@ -431,35 +441,35 @@ namespace ramses getIScene().setScaling(m_transformHandle, scaling); } - return StatusOK; + return true; } - status_t NodeImpl::getScaling(vec3f& scaling) const + bool NodeImpl::getScaling(vec3f& scaling) const { if (!m_transformHandle.isValid()) { - scaling = ramses_internal::IScene::IdentityScaling; - return StatusOK; + scaling = ramses::internal::IScene::IdentityScaling; + return true; } scaling = getIScene().getScaling(m_transformHandle); - return StatusOK; + return true; } void NodeImpl::initializeTransform() { if (!m_transformHandle.isValid()) { - m_transformHandle = getIScene().allocateTransform(getNodeHandle(), ramses_internal::TransformHandle::Invalid()); + m_transformHandle = getIScene().allocateTransform(getNodeHandle(), ramses::internal::TransformHandle::Invalid()); } } - ramses_internal::TransformHandle NodeImpl::getTransformHandle() const + ramses::internal::TransformHandle NodeImpl::getTransformHandle() const { return m_transformHandle; } - status_t NodeImpl::setVisibility(EVisibilityMode mode) + bool NodeImpl::setVisibility(EVisibilityMode mode) { if (m_visibilityMode != mode) { @@ -467,7 +477,7 @@ namespace ramses markDirty(); } - return StatusOK; + return true; } EVisibilityMode NodeImpl::getVisibility() const diff --git a/src/client/impl/NodeImpl.h b/src/client/impl/NodeImpl.h new file mode 100644 index 000000000..253a738bf --- /dev/null +++ b/src/client/impl/NodeImpl.h @@ -0,0 +1,109 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// internal +#include "SceneObjectImpl.h" +#include "ramses/framework/EVisibilityMode.h" +#include "ramses/framework/ERotationType.h" +#include "impl/DataTypesImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" + +#include + +namespace ramses +{ + class Node; +} + +namespace ramses::internal +{ + class SceneImpl; + + class NodeImpl : public SceneObjectImpl + { + public: + NodeImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view nodeName); + ~NodeImpl() override; + + void initializeFrameworkData(); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + + bool addChild(NodeImpl& childNode); + bool removeChild(NodeImpl& node); + bool removeParent(); + bool setParent(NodeImpl& parentNode); + bool removeAllChildren(); + + [[nodiscard]] bool hasChild() const; + [[nodiscard]] size_t getChildCount() const; + [[nodiscard]] Node* getChild(size_t index); + [[nodiscard]] const Node* getChild(size_t index) const; + + [[nodiscard]] NodeImpl& getChildImpl(size_t index); + [[nodiscard]] const NodeImpl& getChildImpl(size_t index) const; + + [[nodiscard]] bool hasParent() const; + [[nodiscard]] Node* getParent(); + [[nodiscard]] const Node* getParent() const; + [[nodiscard]] NodeImpl* getParentImpl(); + [[nodiscard]] const NodeImpl* getParentImpl() const; + + bool getModelMatrix(glm::mat4x4& modelMatrix) const; + bool getInverseModelMatrix(glm::mat4x4& inverseModelMatrix) const; + + bool translate(const vec3f& translation); + bool setTranslation(const vec3f& translation); + bool getTranslation(vec3f& translation) const; + bool setRotation(const vec3f& rotation, ERotationType rotationType); + [[nodiscard]] ERotationType getRotationType() const; + bool getRotation(vec3f& rotation) const; + bool setRotation(const quat& rotation); + bool getRotation(quat& rotation) const; + bool scale(const vec3f& scaling); + bool setScaling(const vec3f& scaling); + bool getScaling(vec3f& scaling) const; + + bool setVisibility(EVisibilityMode mode); + [[nodiscard]] EVisibilityMode getVisibility() const; + + void initializeTransform(); + [[nodiscard]] ramses::internal::TransformHandle getTransformHandle() const; + + [[nodiscard]] ramses::internal::SceneId getSceneId() const; + [[nodiscard]] ramses::internal::NodeHandle getNodeHandle() const; + + void markDirty(); + [[nodiscard]] bool isDirty() const; + + private: + using NodeVector = std::vector; + + void removeChildInternally(NodeVector::iterator childIt); + bool setRotationInternal(glm::vec4&& rotation, ERotationType rotationType); + + ramses::internal::NodeHandle m_nodeHandle; + + NodeVector m_children; + NodeImpl* m_parent; + + ramses::internal::TransformHandle m_transformHandle; + + //The actual visibility + EVisibilityMode m_visibilityMode; + }; +} diff --git a/client/ramses-client/impl/ObjectIteratorImpl.h b/src/client/impl/ObjectIteratorImpl.h similarity index 64% rename from client/ramses-client/impl/ObjectIteratorImpl.h rename to src/client/impl/ObjectIteratorImpl.h index b32b6d3ad..95e40e717 100644 --- a/client/ramses-client/impl/ObjectIteratorImpl.h +++ b/src/client/impl/ObjectIteratorImpl.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_OBJECTITERATORIMPL_H -#define RAMSES_OBJECTITERATORIMPL_H +#pragma once -#include "IteratorImpl.h" -#include "ramses-client-api/RamsesObjectTypes.h" -#include "RamsesObjectRegistry.h" +#include "impl/IteratorImpl.h" +#include "ramses/framework/RamsesObjectTypes.h" +#include "impl/SceneObjectRegistry.h" -namespace ramses +namespace ramses::internal { - class ObjectIteratorImpl : public IteratorImpl + class ObjectIteratorImpl : public IteratorImpl { public: - ObjectIteratorImpl(const RamsesObjectRegistry& objRegistry, ERamsesObjectType objType) + ObjectIteratorImpl(const SceneObjectRegistry& objRegistry, ERamsesObjectType objType) { objRegistry.getObjectsOfType(this->m_objects, objType); this->m_objectIterator = this->m_objects.begin(); } }; } - -#endif diff --git a/client/ramses-client-api/OrthographicCamera.cpp b/src/client/impl/OrthographicCamera.cpp similarity index 76% rename from client/ramses-client-api/OrthographicCamera.cpp rename to src/client/impl/OrthographicCamera.cpp index 6ff163b1d..bb387f02a 100644 --- a/client/ramses-client-api/OrthographicCamera.cpp +++ b/src/client/impl/OrthographicCamera.cpp @@ -7,14 +7,14 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/OrthographicCamera.h" //internal -#include "CameraNodeImpl.h" +#include "impl/CameraNodeImpl.h" namespace ramses { - OrthographicCamera::OrthographicCamera(std::unique_ptr impl) + OrthographicCamera::OrthographicCamera(std::unique_ptr impl) : Camera{ std::move(impl) } { } diff --git a/client/ramses-client-api/PerspectiveCamera.cpp b/src/client/impl/PerspectiveCamera.cpp similarity index 71% rename from client/ramses-client-api/PerspectiveCamera.cpp rename to src/client/impl/PerspectiveCamera.cpp index 357f0be22..93898bc53 100644 --- a/client/ramses-client-api/PerspectiveCamera.cpp +++ b/src/client/impl/PerspectiveCamera.cpp @@ -7,21 +7,21 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/PerspectiveCamera.h" +#include "ramses/client/PerspectiveCamera.h" // internal -#include "CameraNodeImpl.h" +#include "impl/CameraNodeImpl.h" namespace ramses { - PerspectiveCamera::PerspectiveCamera(std::unique_ptr impl) + PerspectiveCamera::PerspectiveCamera(std::unique_ptr impl) : Camera{ std::move(impl) } { } - status_t PerspectiveCamera::setFrustum(float fov, float aspectRatio, float nearPlane, float farPlane) + bool PerspectiveCamera::setFrustum(float fov, float aspectRatio, float nearPlane, float farPlane) { - const status_t status = m_impl.setPerspectiveFrustum(fov, aspectRatio, nearPlane, farPlane); + const bool status = m_impl.setPerspectiveFrustum(fov, aspectRatio, nearPlane, farPlane); LOG_HL_CLIENT_API4(status, fov, aspectRatio, nearPlane, farPlane); return status; } diff --git a/client/ramses-client-api/PickableObject.cpp b/src/client/impl/PickableObject.cpp similarity index 59% rename from client/ramses-client-api/PickableObject.cpp rename to src/client/impl/PickableObject.cpp index cda2f012b..093ede848 100644 --- a/client/ramses-client-api/PickableObject.cpp +++ b/src/client/impl/PickableObject.cpp @@ -7,18 +7,18 @@ // ------------------------------------------------------------------------- //API -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/Camera.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/Camera.h" //internal -#include "PickableObjectImpl.h" -#include "RamsesFrameworkTypesImpl.h" +#include "impl/PickableObjectImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" namespace ramses { - PickableObject::PickableObject(std::unique_ptr impl) + PickableObject::PickableObject(std::unique_ptr impl) : Node{ std::move(impl) } - , m_impl{ static_cast(Node::m_impl) } + , m_impl{ static_cast(Node::m_impl) } { } @@ -32,9 +32,9 @@ namespace ramses return m_impl.getCamera(); } - status_t PickableObject::setCamera(const Camera& camera) + bool PickableObject::setCamera(const Camera& camera) { - const status_t status = m_impl.setCamera(camera.m_impl); + const bool status = m_impl.setCamera(camera.impl()); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(camera)); return status; } @@ -44,16 +44,16 @@ namespace ramses return m_impl.getPickableObjectId(); } - status_t PickableObject::setPickableObjectId(pickableObjectId_t id) + bool PickableObject::setPickableObjectId(pickableObjectId_t id) { - const status_t status = m_impl.setPickableObjectId(id); + const bool status = m_impl.setPickableObjectId(id); LOG_HL_CLIENT_API1(status, id); return status; } - status_t PickableObject::setEnabled(bool enable) + bool PickableObject::setEnabled(bool enable) { - const status_t status = m_impl.setEnabled(enable); + const bool status = m_impl.setEnabled(enable); LOG_HL_CLIENT_API1(status, enable); return status; } @@ -62,4 +62,14 @@ namespace ramses { return m_impl.isEnabled(); } + + internal::PickableObjectImpl& PickableObject::impl() + { + return m_impl; + } + + const internal::PickableObjectImpl& PickableObject::impl() const + { + return m_impl; + } } diff --git a/src/client/impl/PickableObjectImpl.cpp b/src/client/impl/PickableObjectImpl.cpp new file mode 100644 index 000000000..02f11b4d6 --- /dev/null +++ b/src/client/impl/PickableObjectImpl.cpp @@ -0,0 +1,180 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2019 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/PickableObjectImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/SerializationContext.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/ErrorReporting.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Camera.h" +#include "internal/SceneGraph/SceneAPI/PickableObject.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/Scene.h" + +namespace ramses::internal +{ + PickableObjectImpl::PickableObjectImpl(SceneImpl& scene, std::string_view pickableObjectName) + : NodeImpl(scene, ERamsesObjectType::PickableObject, pickableObjectName) + { + } + + void PickableObjectImpl::initializeFrameworkData(const ArrayBufferImpl& geometryBuffer, pickableObjectId_t id) + { + NodeImpl::initializeFrameworkData(); + + assert(!m_pickableObjectHandle.isValid()); + m_geometryBufferImpl = &geometryBuffer; + + const ramses::internal::DataBufferHandle geometryBufferHandle = geometryBuffer.getDataBufferHandle(); + m_pickableObjectHandle = getIScene().allocatePickableObject(geometryBufferHandle, this->getNodeHandle(), ramses::internal::PickableObjectId{ id.getValue() }, {}); + } + + void PickableObjectImpl::deinitializeFrameworkData() + { + assert(m_pickableObjectHandle.isValid()); + getIScene().releasePickableObject(m_pickableObjectHandle); + m_pickableObjectHandle = ramses::internal::PickableObjectHandle::Invalid(); + + NodeImpl::deinitializeFrameworkData(); + } + + bool PickableObjectImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!NodeImpl::serialize(outStream, serializationContext)) + return false; + + outStream << m_pickableObjectHandle; + assert(m_geometryBufferImpl != nullptr); + + outStream << serializationContext.getIDForObject(m_geometryBufferImpl); + if (m_cameraImpl != nullptr) + { + outStream << serializationContext.getIDForObject(m_cameraImpl); + } + else + { + outStream << SerializationContext::GetObjectIDNull(); + } + return true; + } + + bool PickableObjectImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!NodeImpl::deserialize(inStream, serializationContext)) + return false; + + inStream >> m_pickableObjectHandle; + + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_geometryBufferImpl); + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_cameraImpl); + serializationContext.addForDependencyResolve(this); + return true; + } + + bool PickableObjectImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + { + if (!NodeImpl::resolveDeserializationDependencies(serializationContext)) + return false; + + serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_geometryBufferImpl); + serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_cameraImpl); + + return true; + } + + void PickableObjectImpl::onValidate(ValidationReportImpl& report) const + { + NodeImpl::onValidate(report); + + const ramses::internal::PickableObject& pickableObject = getIScene().getPickableObject(m_pickableObjectHandle); + + if (!getIScene().isDataBufferAllocated(pickableObject.geometryHandle)) + { + report.add(EIssueType::Error, "pickable object references a deleted geometry buffer", &getRamsesObject()); + } + else + { + report.addDependentObject(*this, *m_geometryBufferImpl); + } + + if (!pickableObject.cameraHandle.isValid()) + { + report.add(EIssueType::Warning, "pickable object references no camera, a valid camera must be set", &getRamsesObject()); + } + else if (!getIScene().isCameraAllocated(pickableObject.cameraHandle)) + { + report.add(EIssueType::Error, "pickable object references a deleted camera", &getRamsesObject()); + } + } + + const ArrayBuffer& PickableObjectImpl::getGeometryBuffer() const + { + assert(nullptr != m_geometryBufferImpl); + return RamsesObjectTypeUtils::ConvertTo(m_geometryBufferImpl->getRamsesObject()); + } + + bool PickableObjectImpl::setCamera(const CameraNodeImpl& cameraImpl) + { + if (!isFromTheSameSceneAs(cameraImpl)) + { + getErrorReporting().set("PickableObject::setCamera failed - camera is not from the same scene as this PickableObject", *this); + return false; + } + + ValidationReportImpl cameraReport; + cameraImpl.validate(cameraReport); + if (!cameraReport.hasError()) + { + m_cameraImpl = &cameraImpl; + getIScene().setPickableObjectCamera(m_pickableObjectHandle, cameraImpl.getCameraHandle()); + } + else + { + getErrorReporting().set(fmt::format("PickableObject::setCamera failed - camera is not valid, maybe camera was not initialized:\n{}", cameraReport.toString()), *this); + return false; + } + + return true; + } + + const ramses::Camera* PickableObjectImpl::getCamera() const + { + return (m_cameraImpl ? &RamsesObjectTypeUtils::ConvertTo(m_cameraImpl->getRamsesObject()) : nullptr); + } + + bool PickableObjectImpl::setPickableObjectId(pickableObjectId_t id) + { + getIScene().setPickableObjectId(m_pickableObjectHandle, ramses::internal::PickableObjectId(id.getValue())); + return true; + } + + pickableObjectId_t PickableObjectImpl::getPickableObjectId() const + { + return pickableObjectId_t(getIScene().getPickableObject(m_pickableObjectHandle).id.getValue()); + } + + bool PickableObjectImpl::setEnabled(bool enable) + { + getIScene().setPickableObjectEnabled(m_pickableObjectHandle, enable); + return true; + } + + bool PickableObjectImpl::isEnabled() const + { + return getIScene().getPickableObject(m_pickableObjectHandle).isEnabled; + } + + ramses::internal::PickableObjectHandle PickableObjectImpl::getPickableObjectHandle() const + { + return m_pickableObjectHandle; + } +} + diff --git a/src/client/impl/PickableObjectImpl.h b/src/client/impl/PickableObjectImpl.h new file mode 100644 index 000000000..4683e7060 --- /dev/null +++ b/src/client/impl/PickableObjectImpl.h @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2019 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "SceneObjectImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "impl/NodeImpl.h" + +#include + +namespace ramses +{ + class ArrayBuffer; + class Camera; +} + +namespace ramses::internal +{ + class ArrayBufferImpl; + class CameraNodeImpl; + + class PickableObjectImpl final : public NodeImpl + { + public: + PickableObjectImpl(SceneImpl& scene, std::string_view pickableObjectName); + ~PickableObjectImpl() override = default; + + void initializeFrameworkData(const ArrayBufferImpl& geometryBuffer, pickableObjectId_t id); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + [[nodiscard]] const ArrayBuffer& getGeometryBuffer() const; + + bool setCamera(const CameraNodeImpl& cameraImpl); + [[nodiscard]] const ramses::Camera* getCamera() const; + + bool setPickableObjectId(pickableObjectId_t id); + [[nodiscard]] pickableObjectId_t getPickableObjectId() const; + + bool setEnabled(bool enable); + [[nodiscard]] bool isEnabled() const; + + [[nodiscard]] ramses::internal::PickableObjectHandle getPickableObjectHandle() const; + + private: + ramses::internal::PickableObjectHandle m_pickableObjectHandle; + const ArrayBufferImpl* m_geometryBufferImpl = nullptr; + const CameraNodeImpl* m_cameraImpl = nullptr; + }; +} diff --git a/client/ramses-client-api/RamsesClient.cpp b/src/client/impl/RamsesClient.cpp similarity index 50% rename from client/ramses-client-api/RamsesClient.cpp rename to src/client/impl/RamsesClient.cpp index 302f2938e..ff1b2fd95 100644 --- a/client/ramses-client-api/RamsesClient.cpp +++ b/src/client/impl/RamsesClient.cpp @@ -7,84 +7,83 @@ // ------------------------------------------------------------------------- // api -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/SceneConfig.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/client/SceneConfig.h" // private -#include "RamsesClientImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/SceneConfigImpl.h" #include "RamsesClientTypesImpl.h" -#include "SceneConfigImpl.h" namespace ramses { - RamsesClient::RamsesClient(std::unique_ptr impl) + RamsesClient::RamsesClient(std::unique_ptr impl) : RamsesObject{ std::move(impl) } - , m_impl{ static_cast(RamsesObject::m_impl) } + , m_impl{ static_cast(*RamsesObject::m_impl) } { m_impl.setHLObject(this); } - Scene* RamsesClient::createScene(sceneId_t sceneId, const SceneConfig& sceneConfig /*= SceneConfig()*/, std::string_view name) + Scene* RamsesClient::createScene(sceneId_t sceneId, std::string_view name) { - Scene* scene = m_impl.createScene(sceneId, sceneConfig.m_impl, name); - LOG_HL_CLIENT_API2(LOG_API_RAMSESOBJECT_PTR_STRING(scene), sceneId, name); + return createScene(SceneConfig(sceneId), name); + } + + Scene* RamsesClient::createScene(const SceneConfig& sceneConfig, std::string_view name) + { + Scene* scene = m_impl.createScene(sceneConfig.impl(), name); + LOG_HL_CLIENT_API2(LOG_API_RAMSESOBJECT_PTR_STRING(scene), sceneConfig.impl().getSceneId(), name); return scene; } - status_t RamsesClient::destroy(Scene& scene) + bool RamsesClient::destroy(Scene& scene) { - const status_t status = m_impl.destroy(scene); + const bool status = m_impl.destroy(scene); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(scene)); return status; } - ramses::Scene* RamsesClient::loadSceneFromFile(std::string_view fileName, bool localOnly) + ramses::Scene* RamsesClient::loadSceneFromFile(std::string_view fileName, const SceneConfig& config) { - auto scene = m_impl.loadSceneFromFile(fileName, localOnly); - LOG_HL_CLIENT_API2(LOG_API_RAMSESOBJECT_PTR_STRING(scene), fileName, localOnly); + auto scene = m_impl.loadSceneFromFile(fileName, config.impl()); + LOG_HL_CLIENT_API1(LOG_API_RAMSESOBJECT_PTR_STRING(scene), fileName); return scene; } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - ramses::Scene* RamsesClient::loadSceneFromMemory(std::unique_ptr data, size_t size, bool localOnly) - { - auto scene = m_impl.loadSceneFromMemory(std::move(data), size, localOnly); - LOG_HL_CLIENT_API2(LOG_API_RAMSESOBJECT_PTR_STRING(scene), size, localOnly); - return scene; - } - - ramses::Scene* RamsesClient::loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, bool localOnly) + ramses::Scene* RamsesClient::loadSceneFromMemory(std::unique_ptr data, size_t size, const SceneConfig& config) { - auto scene = m_impl.loadSceneFromFileDescriptor(fd, offset, length, localOnly); - LOG_HL_CLIENT_API4(LOG_API_RAMSESOBJECT_PTR_STRING(scene), fd, offset, length, localOnly); + auto scene = m_impl.loadSceneFromMemory(std::move(data), size, config.impl()); + LOG_HL_CLIENT_API1(LOG_API_RAMSESOBJECT_PTR_STRING(scene), size); return scene; } - ramses::Scene* RamsesClient::loadSceneFromFileDescriptor(sceneId_t sceneId, int fd, size_t offset, size_t length, bool localOnly) + ramses::Scene* RamsesClient::loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, const SceneConfig& config) { - auto scene = m_impl.loadSceneFromFileDescriptor(sceneId, fd, offset, length, localOnly); - LOG_HL_CLIENT_API5(LOG_API_RAMSESOBJECT_PTR_STRING(scene), sceneId, fd, offset, length, localOnly); + auto scene = m_impl.loadSceneFromFileDescriptor(fd, offset, length, config.impl()); + LOG_HL_CLIENT_API3(LOG_API_RAMSESOBJECT_PTR_STRING(scene), fd, offset, length); return scene; } - status_t RamsesClient::loadSceneFromFileAsync(std::string_view fileName, bool localOnly) + bool RamsesClient::loadSceneFromFileAsync(std::string_view fileName, const SceneConfig& config) { - auto status = m_impl.loadSceneFromFileAsync(fileName, localOnly); - LOG_HL_CLIENT_API2(status, fileName, localOnly); + auto status = m_impl.loadSceneFromFileAsync(fileName, config.impl()); + LOG_HL_CLIENT_API1(status, fileName); return status; } bool RamsesClient::GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel) { - const bool ret = RamsesClientImpl::GetFeatureLevelFromFile(fileName, detectedFeatureLevel); + const bool ret = internal::RamsesClientImpl::GetFeatureLevelFromFile(fileName, detectedFeatureLevel); LOG_HL_CLIENT_STATIC_API1(ret, fileName); return ret; } bool RamsesClient::GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel) { - const bool ret = RamsesClientImpl::GetFeatureLevelFromFile(fd, offset, length, detectedFeatureLevel); + const bool ret = internal::RamsesClientImpl::GetFeatureLevelFromFile(fd, offset, length, detectedFeatureLevel); LOG_HL_CLIENT_STATIC_API3(ret, fd, offset, length); return ret; } @@ -109,10 +108,30 @@ namespace ramses return m_impl.getScene(sceneId); } - status_t RamsesClient::dispatchEvents(IClientEventHandler& clientEventHandler) + bool RamsesClient::dispatchEvents(IClientEventHandler& clientEventHandler) { auto status = m_impl.dispatchEvents(clientEventHandler); LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(clientEventHandler)); return status; } + + const RamsesFramework& RamsesClient::getRamsesFramework() const + { + return m_impl.getFramework().getHLRamsesFramework(); + } + + RamsesFramework& RamsesClient::getRamsesFramework() + { + return m_impl.getFramework().getHLRamsesFramework(); + } + + internal::RamsesClientImpl& RamsesClient::impl() + { + return m_impl; + } + + const internal::RamsesClientImpl& RamsesClient::impl() const + { + return m_impl; + } } diff --git a/src/client/impl/RamsesClientImpl.cpp b/src/client/impl/RamsesClientImpl.cpp new file mode 100644 index 000000000..62fc2db03 --- /dev/null +++ b/src/client/impl/RamsesClientImpl.cpp @@ -0,0 +1,818 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/Scene.h" +#include "ramses/client/Resource.h" +#include "ramses/client/EffectDescription.h" +#include "ramses-sdk-build-config.h" + +// internal +#include "impl/SceneImpl.h" +#include "impl/SceneConfigImpl.h" +#include "impl/ResourceImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "impl/SerializationHelper.h" +#include "internal/RamsesVersion.h" +#include "impl/SceneReferenceImpl.h" +#include "impl/SaveFileConfigImpl.h" + +// framework +#include "internal/SceneGraph/SceneAPI/SceneCreationInformation.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/Scene/ScenePersistation.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/Components/ResourcePersistation.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/Components/FileInputStreamContainer.h" +#include "internal/Components/MemoryInputStreamContainer.h" +#include "internal/Components/OffsetFileInputStreamContainer.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/ClientCommands/PrintSceneList.h" +#include "internal/ClientCommands/FlushSceneVersion.h" +#include "impl/SerializationContext.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/File.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "ClientFactory.h" +#include "impl/FrameworkFactoryRegistry.h" +#include "internal/Ramsh/Ramsh.h" +#include "impl/DataTypeUtils.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/glslEffectBlock/GlslEffect.h" +#include "impl/EffectDescriptionImpl.h" +#include "impl/TextureUtils.h" + +#include +#include + +namespace ramses::internal +{ + static const bool clientRegisterSuccess = ClientFactory::RegisterClientFactory(); + + RamsesClientImpl::RamsesClientImpl(RamsesFrameworkImpl& framework, std::string_view applicationName) + : RamsesObjectImpl(ERamsesObjectType::Client, applicationName) + , m_appLogic(framework.getParticipantAddress().getParticipantId(), framework.getFrameworkLock()) + , m_framework(framework) + , m_loadFromFileTaskQueue(framework.getTaskQueue()) + , m_deleteSceneQueue(framework.getTaskQueue()) + { + assert(!framework.isConnected()); + + m_appLogic.init(framework.getResourceComponent(), framework.getScenegraphComponent()); + m_cmdPrintSceneList = std::make_shared(*this); + m_cmdPrintValidation = std::make_shared(*this); + m_cmdFlushSceneVersion = std::make_shared(*this); + m_cmdDumpSceneToFile = std::make_shared(*this); + m_cmdLogResourceMemoryUsage = std::make_shared(*this); + framework.getRamsh().add(m_cmdPrintSceneList); + framework.getRamsh().add(m_cmdPrintValidation); + framework.getRamsh().add(m_cmdFlushSceneVersion); + framework.getRamsh().add(m_cmdDumpSceneToFile); + framework.getRamsh().add(m_cmdLogResourceMemoryUsage); + m_framework.getPeriodicLogger().registerPeriodicLogSupplier(&m_framework.getScenegraphComponent()); + } + + RamsesClientImpl::~RamsesClientImpl() + { + LOG_INFO(CONTEXT_CLIENT, "RamsesClientImpl::~RamsesClientImpl"); + m_deleteSceneQueue.disableAcceptingTasksAfterExecutingCurrentQueue(); + m_loadFromFileTaskQueue.disableAcceptingTasksAfterExecutingCurrentQueue(); + + // delete async loaded scenes that were never collected via calling dispatchEvents + ramses::internal::PlatformGuard g(m_clientLock); + m_asyncSceneLoadStatusVec.clear(); + + LOG_INFO(CONTEXT_CLIENT, "RamsesClientImpl::~RamsesClientImpl deleting scenes"); + m_scenes.clear(); + + m_framework.getPeriodicLogger().removePeriodicLogSupplier(&m_framework.getScenegraphComponent()); + } + + void RamsesClientImpl::setHLObject(RamsesClient* hlClient) + { + assert(hlClient); + m_hlClient = hlClient; + } + + + void RamsesClientImpl::deinitializeFrameworkData() + { + } + + const ramses::internal::ClientApplicationLogic& RamsesClientImpl::getClientApplication() const + { + return m_appLogic; + } + + ramses::internal::ClientApplicationLogic& RamsesClientImpl::getClientApplication() + { + return m_appLogic; + } + + ramses::Scene* RamsesClientImpl::createScene(const SceneConfigImpl& sceneConfig, std::string_view name) + { + if (!sceneConfig.getSceneId().isValid()) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::createScene: invalid sceneId"); + return nullptr; + } + + ramses::internal::PlatformGuard g(m_clientLock); + ramses::internal::SceneInfo sceneInfo; + sceneInfo.friendlyName = name; + sceneInfo.sceneID = ramses::internal::SceneId(sceneConfig.getSceneId().getValue()); + + ramses::internal::ClientScene* internalScene = m_sceneFactory.createScene(sceneInfo); + if (nullptr == internalScene) + { + return nullptr; + } + + auto impl = std::make_unique(*internalScene, sceneConfig, *m_hlClient); + impl->initializeFrameworkData(); + m_scenes.push_back(SceneOwningPtr{ new ramses::Scene{ std::move(impl) }, [](ramses::Scene* s) { delete s; } }); + + return m_scenes.back().get(); + } + + RamsesClientImpl::DeleteSceneRunnable::DeleteSceneRunnable(SceneOwningPtr scene, InternalSceneOwningPtr llscene) + : m_scene{ std::move(scene) } + , m_lowLevelScene{ std::move(llscene) } + { + } + + void RamsesClientImpl::DeleteSceneRunnable::execute() + { + m_scene.reset(); + m_lowLevelScene.reset(); + } + + bool RamsesClientImpl::destroy(ramses::Scene& scene) + { + ramses::internal::PlatformGuard g(m_clientLock); + auto iter = std::find_if(m_scenes.begin(), m_scenes.end(), [&scene](auto& s) { return s.get() == &scene; }); + if (iter != m_scenes.end()) + { + auto sceneOwnPtr = std::move(*iter); + m_scenes.erase(iter); + + const ramses::internal::SceneId sceneID(scene.getSceneId().getValue()); + auto llscene = m_sceneFactory.releaseScene(sceneID); + + getClientApplication().removeScene(sceneID); + + scene.impl().closeSceneFile(); + auto task = new DeleteSceneRunnable(std::move(sceneOwnPtr), std::move(llscene)); + m_deleteSceneQueue.enqueue(*task); + task->release(); + + return true; + } + + m_framework.getErrorReporting().set("RamsesClient::destroy failed, scene is not in this client."); + return false; + } + + void RamsesClientImpl::writeLowLevelResourcesToStream(const ResourceObjects& resources, ramses::internal::IOutputStream& resourceOutputStream, bool compress) const + { + //getting names for resources (names are transmitted only for debugging purposes) + ramses::internal::ManagedResourceVector managedResources; + managedResources.reserve(resources.size()); + for (const auto res : resources) + { + assert(res != nullptr); + const ramses::internal::ResourceContentHash& hash = res->impl().getLowlevelResourceHash(); + const ramses::internal::ManagedResource managedRes = getClientApplication().getResource(hash); + if (managedRes) + { + managedResources.push_back(managedRes); + } + else + { + const ramses::internal::ManagedResource loadedResource = getClientApplication().loadResource(hash); + assert(loadedResource); + managedResources.push_back(loadedResource); + } + } + + // sort resources by hash to maintain a deterministic order in which we write them to file, remove duplicates + std::sort(managedResources.begin(), managedResources.end(), [](auto const& a, auto const& b) { return a->getHash() < b->getHash(); }); + managedResources.erase(std::unique(managedResources.begin(), managedResources.end()), managedResources.end()); + + // write LL-TOC and LL resources + ramses::internal::ResourcePersistation::WriteNamedResourcesWithTOCToStream(resourceOutputStream, managedResources, compress); + } + + ramses::internal::ManagedResource RamsesClientImpl::getResource(ramses::internal::ResourceContentHash hash) const + { + return m_appLogic.getResource(hash); + } + + SceneOwningPtr RamsesClientImpl::loadSceneObjectFromStream(const std::string& caller, + std::string const& filename, + ramses::internal::IInputStream& inputStream, + const SceneConfigImpl& config) + { + LOG_TRACE(ramses::internal::CONTEXT_CLIENT, "RamsesClient::prepareSceneFromInputStream: start loading scene from input stream"); + + ramses::internal::SceneCreationInformation createInfo; + ramses::internal::ScenePersistation::ReadSceneMetadataFromStream(inputStream, createInfo); + if (config.getSceneId().isValid()) + { + const auto newSceneId = ramses::internal::SceneId(config.getSceneId().getValue()); + LOG_INFO_P(ramses::internal::CONTEXT_CLIENT, "RamsesClient::{}: Override stored scene id: {} with user provided scene id: {}", caller, createInfo.m_id, newSceneId); + createInfo.m_id = newSceneId; + } + const ramses::internal::SceneSizeInformation& sizeInformation = createInfo.m_sizeInfo; + const ramses::internal::SceneInfo sceneInfo(createInfo.m_id, createInfo.m_name); + + LOG_DEBUG(ramses::internal::CONTEXT_CLIENT, "RamsesClient::prepareSceneFromInputStream: scene to be loaded has " << sizeInformation); + + ramses::internal::ClientScene* internalScene = nullptr; + { + ramses::internal::PlatformGuard g(m_clientLock); + internalScene = m_sceneFactory.createScene(sceneInfo); + } + if (nullptr == internalScene) + { + return nullptr; + } + internalScene->preallocateSceneSize(sizeInformation); + + // need first to create the pimpl, so that internal framework components know the new scene + if (config.getPublicationMode() == EScenePublicationMode::LocalOnly) + { + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RamsesClient::" << caller << ": Mark file loaded from " << filename << " with sceneId " << createInfo.m_id << " as local only"); + } + else + { + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RamsesClient::" << caller << ": Mark file loaded from " << filename << " with sceneId " << createInfo.m_id << " as local and remote"); + } + + auto impl = std::make_unique(*internalScene, config, *m_hlClient); + + // now the scene is registered, so it's possible to load the low level content into the scene + LOG_TRACE(ramses::internal::CONTEXT_CLIENT, " Reading low level scene from stream"); + ramses::internal::ScenePersistation::ReadSceneFromStream(inputStream, *internalScene); + + LOG_TRACE(ramses::internal::CONTEXT_CLIENT, " Deserializing high level scene objects from stream"); + DeserializationContext deserializationContext(config); + SerializationHelper::DeserializeObjectID(inputStream); + if (!impl->deserialize(inputStream, deserializationContext)) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, " Failed to deserialize high level scene:"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, m_framework.getErrorReporting().getError().value_or(Issue{}).message); + return nullptr; + } + + LOG_TRACE(ramses::internal::CONTEXT_CLIENT, " Done with preparing scene from input stream."); + + return SceneOwningPtr{ new ramses::Scene{ std::move(impl) }, [](ramses::Scene* s) { delete s; } }; + } + + SceneOwningPtr RamsesClientImpl::loadSceneFromCreationConfig(const SceneCreationConfig& cconfig) + { + // this stream contains scene data AND resource data and will be handed over to and held open by resource component as resource stream + ramses::internal::IInputStream& inputStream = cconfig.streamContainer->getStream(); + if (inputStream.getState() != ramses::internal::EStatus::Ok) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::" << cconfig.caller << ": failed to open scene source " << cconfig.dataSource); + return nullptr; + } + + if (!ReadRamsesVersionAndPrintWarningOnMismatch(inputStream, "scene file", getFramework().getFeatureLevel())) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::" << cconfig.caller << ": failed to read from scene source " << cconfig.dataSource); + return nullptr; + } + + SaveFileConfigImpl::ExporterVersion exporter; + std::string metadataString; + inputStream >> exporter; + inputStream >> metadataString; + + LOG_INFO_P(CONTEXT_CLIENT, "Metadata: '{}'", metadataString); + LOG_INFO_P(CONTEXT_CLIENT, "Exporter version: {}.{}.{} (file format version {})", exporter.major, exporter.minor, exporter.patch, exporter.fileFormat); + + uint64_t sceneObjectStart = 0; + uint64_t llResourceStart = 0; + inputStream >> sceneObjectStart; + inputStream >> llResourceStart; + + SceneOwningPtr scene; + if (cconfig.prefetchData) + { + std::vector sceneData(static_cast(llResourceStart - sceneObjectStart)); + inputStream.read(sceneData.data(), sceneData.size()); + + if (inputStream.getState() != ramses::internal::EStatus::Ok) + { + LOG_ERROR_P(ramses::internal::CONTEXT_CLIENT, "RamsesClient::{}: Failed reading scene from file: {} ", cconfig.caller, inputStream.getState()); + return nullptr; + } + + ramses::internal::BinaryInputStream sceneDataStream(sceneData.data()); + scene = loadSceneObjectFromStream(cconfig.caller, cconfig.dataSource, sceneDataStream, cconfig.config); + } + else + { + // this path will be used in the future when creating scene from user provided stream + scene = loadSceneObjectFromStream(cconfig.caller, cconfig.dataSource, inputStream, cconfig.config); + } + if (!scene) + { + LOG_ERROR_P(ramses::internal::CONTEXT_CLIENT, "RamsesClient::{}: scene creation for '{}' failed", cconfig.caller, cconfig.dataSource); + return nullptr; + } + + // calls on m_appLogic are thread safe + // register stream for on-demand resource loading (LL-Resources) + ramses::internal::ResourceTableOfContents loadedTOC; + loadedTOC.readTOCPosAndTOCFromStream(inputStream); + const ramses::internal::SceneFileHandle fileHandle = m_appLogic.addResourceFile(cconfig.streamContainer, loadedTOC); + scene->m_impl.setSceneFileHandle(fileHandle); + + LOG_INFO_P(CONTEXT_CLIENT, "RamsesClient::{}: Source '{}' has handle {}", cconfig.caller, cconfig.dataSource, fileHandle); + + return scene; + } + + ramses::Scene* RamsesClientImpl::loadSceneFromFile(std::string_view fileName, const SceneConfigImpl& config) + { + if (fileName.empty()) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFile: filename may not be empty"); + return nullptr; + } + + return loadSceneSynchonousCommon({ + "loadSceneFromFile", + std::string{fileName}, + std::make_shared(fileName), true, config + }); + } + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + ramses::Scene* RamsesClientImpl::loadSceneFromMemory(std::unique_ptr data, size_t size, const SceneConfigImpl& config) + { + if (!data) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromMemory: data may not be null"); + return nullptr; + } + if (size == 0) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromMemory: size may not be 0"); + return nullptr; + } + + return loadSceneSynchonousCommon({ + "loadSceneFromMemory", + fmt::format("", size), + std::make_shared(std::move(data)), + false, + config + }); + } + + ramses::Scene* RamsesClientImpl::loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, const SceneConfigImpl& config) + { + if (fd <= 0) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: filedescriptor must be valid " << fd); + return nullptr; + } + if (length == 0) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileDescriptor: length may not be 0"); + return nullptr; + } + + return loadSceneSynchonousCommon(SceneCreationConfig{ + "loadSceneFromFileDescriptor", + fmt::format("", fd, offset, length), + std::make_shared(fd, offset, length), + true, + config + }); + } + + ramses::Scene* RamsesClientImpl::loadSceneSynchonousCommon(const SceneCreationConfig& cconfig) + { + const uint64_t start = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + auto scene = loadSceneFromCreationConfig(cconfig); + if (!scene) + return nullptr; + + auto* scenePtr = scene.get(); + finalizeLoadedScene(std::move(scene)); + + const uint64_t end = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RamsesClient::" << cconfig.caller << " ramses::Scene loaded from '" << cconfig.dataSource << "' in " << (end - start) << " ms"); + + return scenePtr; + } + + void RamsesClientImpl::finalizeLoadedScene(SceneOwningPtr scene) + { + // add to the known list of scenes + ramses::internal::PlatformGuard g(m_clientLock); + m_scenes.push_back(std::move(scene)); + } + + void RamsesClientImpl::WriteCurrentBuildVersionToStream(ramses::internal::IOutputStream& stream, EFeatureLevel featureLevel) + { + ramses::internal::RamsesVersion::WriteToStream(stream, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, featureLevel); + } + + bool RamsesClientImpl::GetFeatureLevelFromStream(ramses::internal::IInputStreamContainer& streamContainer, const std::string& desc, EFeatureLevel& detectedFeatureLevel) + { + ramses::internal::RamsesVersion::VersionInfo readVersion; + if (!ramses::internal::RamsesVersion::ReadFromStream(streamContainer.getStream(), readVersion, detectedFeatureLevel)) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::GetFeatureLevelFromSceneFile: failed to read RAMSES version and feature level from '" << desc << "'."); + return false; + } + + return true; + } + + bool RamsesClientImpl::GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel) + { + ramses::internal::FileInputStreamContainer streamContainer{ fileName }; + return GetFeatureLevelFromStream(streamContainer, std::string{fileName}, detectedFeatureLevel); + } + + bool RamsesClientImpl::GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel) + { + if (fd <= 0) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::GetFeatureLevelFromFile: filedescriptor must be valid " << fd); + return false; + } + if (length == 0u) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::GetFeatureLevelFromFile: length may not be 0"); + return false; + } + + ramses::internal::OffsetFileInputStreamContainer streamContainer{ fd, offset, length }; + return GetFeatureLevelFromStream(streamContainer, fmt::format("fileDescriptor fd:{} offset:{} length:{}", fd, offset, length), detectedFeatureLevel); + } + + bool RamsesClientImpl::ReadRamsesVersionAndPrintWarningOnMismatch(ramses::internal::IInputStream& inputStream, std::string_view verboseFileName, EFeatureLevel featureLevel) + { + // return false on read error only, not version mismatch + ramses::internal::RamsesVersion::VersionInfo readVersion; + EFeatureLevel featureLevelFromFile = EFeatureLevel_01; + if (!ramses::internal::RamsesVersion::ReadFromStream(inputStream, readVersion, featureLevelFromFile)) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::ReadRamsesVersionAndPrintWarningOnMismatch: failed to read RAMSES version for " << verboseFileName << ", file probably corrupt. Loading aborted."); + return false; + } + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RAMSES version in file '" << verboseFileName << "': [" << readVersion.versionString << "]; GitHash: [" << readVersion.gitHash << "]; FeatureLevel: [" << featureLevelFromFile << "];"); + + if (!ramses::internal::RamsesVersion::MatchesMajorMinor(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR_INT, ::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR_INT, readVersion)) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::ReadRamsesVersionAndPrintWarningOnMismatch: Version of file " << verboseFileName << " does not match MAJOR.MINOR of this build. Cannot load the file."); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "SDK version of loader: [" << ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION << "]; GitHash: [" << ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH << "]"); + return false; + } + + if (featureLevelFromFile != featureLevel) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::ReadRamsesVersionAndPrintWarningOnMismatch: Feature level of file '" << verboseFileName + << "' is " << featureLevelFromFile << " which does not match feature level of this Ramses instance (" << featureLevel << "). Cannot load the file."); + return false; + } + + return true; + } + + bool RamsesClientImpl::loadSceneFromFileAsync(std::string_view fileName, const SceneConfigImpl& config) + { + const std::string stdFilename(fileName); + if (stdFilename.empty()) + { + m_framework.getErrorReporting().set("RamsesClient::loadSceneFromFileAsync: filename may not be empty"); + return false; + } + + auto* task = + new LoadSceneRunnable(*this, SceneCreationConfig{ + "loadSceneFromFileAsync", + stdFilename, + std::make_shared(stdFilename), + true, + config + }); + m_loadFromFileTaskQueue.enqueue(*task); + task->release(); + return true; + } + + ramses::SceneReference* RamsesClientImpl::findSceneReference(sceneId_t masterSceneId, sceneId_t referencedSceneId) + { + for (auto const& scene : getListOfScenes()) + { + if (masterSceneId == scene->getSceneId()) + return scene->m_impl.getSceneReference(referencedSceneId); + } + + return nullptr; + } + + bool RamsesClientImpl::dispatchEvents(IClientEventHandler& clientEventHandler) + { + std::vector localAsyncSceneLoadStatus; + { + ramses::internal::PlatformGuard g(m_clientLock); + localAsyncSceneLoadStatus.swap(m_asyncSceneLoadStatusVec); + } + + for (auto& sceneStatus : localAsyncSceneLoadStatus) + { + if (sceneStatus.scene) + { + // finalize scene + ramses::Scene* scene = sceneStatus.scene.get(); + const uint64_t start = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + finalizeLoadedScene(std::move(sceneStatus.scene)); + const uint64_t end = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RamsesClient::dispatchEvents(sceneFileLoadSucceeded): Synchronous postprocessing of scene loaded from '" << + sceneStatus.sceneFilename << "' (sceneName: " << scene->getName() << ", sceneId " << scene->getSceneId() << ") in " << (end - start) << " ms"); + + clientEventHandler.sceneFileLoadSucceeded(sceneStatus.sceneFilename.c_str(), scene); + } + else + { + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RamsesClient::dispatchEvents(sceneFileLoadFailed): " << sceneStatus.sceneFilename); + clientEventHandler.sceneFileLoadFailed(sceneStatus.sceneFilename.c_str()); + } + } + + const auto clientRendererEvents = getClientApplication().popSceneReferenceEvents(); + for (const auto& rendererEvent : clientRendererEvents) + { + switch (rendererEvent.type) + { + case ramses::internal::SceneReferenceEventType::SceneStateChanged: + { + auto sr = findSceneReference(sceneId_t{ rendererEvent.masterSceneId.getValue() }, sceneId_t{ rendererEvent.referencedScene.getValue() }); + if (sr) + { + LOG_INFO(CONTEXT_CLIENT, "RamsesClient::dispatchEvents master:reference scene state changed: " + << rendererEvent.masterSceneId << ":" << rendererEvent.referencedScene << " " << ramses::internal::EnumToString(rendererEvent.sceneState)); + + sr->impl().setReportedState(rendererEvent.sceneState); + clientEventHandler.sceneReferenceStateChanged(*sr, rendererEvent.sceneState); + } + else + { + LOG_WARN(CONTEXT_CLIENT, + "RamsesClientImpl::dispatchEvents: did not find SceneReference for a SceneStateChanged event: " + << rendererEvent.masterSceneId << " " << rendererEvent.referencedScene << " " << ramses::internal::EnumToString(rendererEvent.sceneState)); + } + break; + } + case ramses::internal::SceneReferenceEventType::SceneFlushed: + { + auto sr = findSceneReference(sceneId_t{ rendererEvent.masterSceneId.getValue() }, sceneId_t{ rendererEvent.referencedScene.getValue() }); + if (sr) + { + clientEventHandler.sceneReferenceFlushed(*sr, sceneVersionTag_t{ rendererEvent.tag.getValue() }); + } + else + { + LOG_WARN(CONTEXT_CLIENT, + "RamsesClientImpl::dispatchEvents: did not find SceneReference for a SceneFlushed event: " + << rendererEvent.masterSceneId << " " << rendererEvent.referencedScene << " " << rendererEvent.tag); + } + break; + } + case ramses::internal::SceneReferenceEventType::DataLinked: + clientEventHandler.dataLinked(sceneId_t{ rendererEvent.providerScene.getValue() }, dataProviderId_t{ rendererEvent.dataProvider.getValue() }, + sceneId_t{ rendererEvent.consumerScene.getValue() }, dataConsumerId_t{ rendererEvent.dataConsumer.getValue() }, rendererEvent.status); + break; + case ramses::internal::SceneReferenceEventType::DataUnlinked: + clientEventHandler.dataUnlinked(sceneId_t{ rendererEvent.consumerScene.getValue() }, dataConsumerId_t{ rendererEvent.dataConsumer.getValue() }, rendererEvent.status); + break; + } + } + + return true; + } + + RamsesClientImpl::LoadSceneRunnable::LoadSceneRunnable(RamsesClientImpl& client, SceneCreationConfig&& cconfig) + : m_client(client) + , m_cconfig(std::move(cconfig)) + { + } + + void RamsesClientImpl::LoadSceneRunnable::execute() + { + const uint64_t start = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + auto scene = m_client.loadSceneFromCreationConfig(m_cconfig); + const uint64_t end = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + + if (scene) + { + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "RamsesClient::loadSceneFromFileAsync: ramses::Scene loaded from '" << m_cconfig.dataSource << + "' (sceneName: " << scene->getName() << ", sceneId " << scene->getSceneId() << ") in " << (end - start) << " ms"); + } + + ramses::internal::PlatformGuard g(m_client.m_clientLock); + // NOTE: only used for real files by name for now, not sure how to report other cases to user. + // therefore can assume dataSource is the filename + m_client.m_asyncSceneLoadStatusVec.push_back({std::move(scene), m_cconfig.dataSource}); + } + + const SceneVector& RamsesClientImpl::getListOfScenes() const + { + ramses::internal::PlatformGuard g(m_clientLock); + return m_scenes; + } + + ramses::internal::ResourceHashUsage RamsesClientImpl::getHashUsage_ThreadSafe(const ramses::internal::ResourceContentHash& hash) const + { + return m_appLogic.getHashUsage(hash); + } + + ramses::internal::ManagedResource RamsesClientImpl::getResource_ThreadSafe(ramses::internal::ResourceContentHash hash) const + { + return m_appLogic.getResource(hash); + } + + ramses::internal::ManagedResource RamsesClientImpl::loadResource_ThreadSafe(const ramses::internal::ResourceContentHash& hash) const + { + return m_appLogic.loadResource(hash); + } + + const ramses::Scene* RamsesClientImpl::findSceneByName(std::string_view name) const + { + ramses::internal::PlatformGuard g(m_clientLock); + for (const auto& scene : m_scenes) + { + if (scene->getName() == name) + return scene.get(); + } + + return nullptr; + } + + ramses::Scene* RamsesClientImpl::findSceneByName(std::string_view name) + { + // Non-const version of findObjectByName cast to its const version to avoid duplicating code + return const_cast((const_cast(*this)).findSceneByName(name)); + } + + const ramses::Scene* RamsesClientImpl::getScene(sceneId_t sceneId) const + { + ramses::internal::PlatformGuard g(m_clientLock); + for (const auto& scene : m_scenes) + { + if (scene->getSceneId() == sceneId) + return scene.get(); + } + + return nullptr; + } + + ramses::Scene* RamsesClientImpl::getScene(sceneId_t sceneId) + { + // Non-const version of findObjectByName cast to its const version to avoid duplicating code + return const_cast((const_cast(*this)).getScene(sceneId)); + } + + + ramses::internal::ManagedResource RamsesClientImpl::manageResource(const ramses::internal::IResource* res) + { + ramses::internal::ManagedResource managedRes = m_appLogic.addResource(res); + LOG_HL_CLIENT_API_STR("Created resource with internal hash " << managedRes->getHash() << ", name: " << managedRes->getName()); + + return managedRes; + } + + RamsesClientImpl& RamsesClientImpl::createImpl(std::string_view name, RamsesFrameworkImpl& components) + { + return *new RamsesClientImpl(components, name); + } + + const RamsesFrameworkImpl& RamsesClientImpl::getFramework() const + { + return m_framework; + } + + RamsesFrameworkImpl& RamsesClientImpl::getFramework() + { + return m_framework; + } + + void RamsesClientImpl::onValidate(ValidationReportImpl& report) const + { + RamsesObjectImpl::onValidate(report); + validateScenes(report); + } + + void RamsesClientImpl::validateScenes(ValidationReportImpl& report) const + { + ramses::internal::PlatformGuard g(m_clientLock); + + for (const auto& scene : m_scenes) + { + scene->impl().validate(report); + } + } + + ramses::internal::ManagedResource RamsesClientImpl::createManagedArrayResource(uint32_t numElements, ramses::EDataType type, const void* arrayData, std::string_view name) + { + if (0u == numElements || nullptr == arrayData) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClientImpl::createManagedArrayResource Array resource must have element count > 0 and data must not be nullptr!"); + return {}; + } + + ramses::internal::EDataType elementType = DataTypeUtils::ConvertDataTypeToInternal(type); + ramses::internal::EResourceType resourceType = DataTypeUtils::DeductResourceTypeFromDataType(type); + + auto resource = new ramses::internal::ArrayResource(resourceType, numElements, elementType, arrayData, name); + return manageResource(resource); + } + + template + ramses::internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses::internal::EResourceType textureType, + uint32_t width, uint32_t height, uint32_t depth, + ETextureFormat format, + uint32_t mipMapCount, const MipDataStorageType mipLevelData[], bool generateMipChain, // NOLINT(modernize-avoid-c-arrays) + const TextureSwizzle& swizzle, std::string_view name) + { + if (!TextureUtils::TextureParametersValid(width, height, depth, mipMapCount) || !TextureUtils::MipDataValid(width, height, depth, mipMapCount, mipLevelData, format)) + { + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::createTexture: invalid parameters"); + return {}; + } + + if (generateMipChain && (!FormatSupportsMipChainGeneration(format) || (mipMapCount > 1))) + { + LOG_WARN(ramses::internal::CONTEXT_CLIENT, "RamsesClient::createTexture: cannot auto generate mipmaps when custom mipmap data provided or unsupported format used"); + generateMipChain = false; + } + + ramses::internal::TextureMetaInfo texDesc; + texDesc.m_width = width; + texDesc.m_height = height; + texDesc.m_depth = depth; + texDesc.m_format = TextureUtils::GetTextureFormatInternal(format); + texDesc.m_generateMipChain = generateMipChain; + texDesc.m_swizzle = TextureUtils::GetTextureSwizzleInternal(swizzle); + TextureUtils::FillMipDataSizes(texDesc.m_dataSizes, mipMapCount, mipLevelData); + + auto* resource = new ramses::internal::TextureResource(textureType, texDesc, name); + TextureUtils::FillMipData(const_cast(resource->getResourceData().data()), mipMapCount, mipLevelData); + + return manageResource(resource); + } + template ramses::internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses::internal::EResourceType textureType, + uint32_t width, uint32_t height, uint32_t depth, + ETextureFormat format, + uint32_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, + const TextureSwizzle& swizzle, std::string_view name); + template ramses::internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses::internal::EResourceType textureType, + uint32_t width, uint32_t height, uint32_t depth, + ETextureFormat format, + uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, + const TextureSwizzle& swizzle, std::string_view name); + + ramses::internal::ManagedResource RamsesClientImpl::createManagedEffect(const EffectDescription& effectDesc, std::string_view name, std::string& errorMessages) + { + //create effect using vertex and fragment shaders + ramses::internal::GlslEffect effectBlock(effectDesc.getVertexShader(), effectDesc.getFragmentShader(), effectDesc.getGeometryShader(), effectDesc.impl().getCompilerDefines(), + effectDesc.impl().getSemanticsMap(), name); + errorMessages.clear(); + ramses::internal::EffectResource* effectResource = effectBlock.createEffectResource(); + if (!effectResource) + { + errorMessages = effectBlock.getEffectErrorMessages(); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::createEffect Failed to create effect resource (name: '" << name << "') :\n " << effectBlock.getEffectErrorMessages()); + return {}; + } + return manageResource(effectResource); + } +} diff --git a/src/client/impl/RamsesClientImpl.h b/src/client/impl/RamsesClientImpl.h new file mode 100644 index 000000000..8dc853358 --- /dev/null +++ b/src/client/impl/RamsesClientImpl.h @@ -0,0 +1,228 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include + +// client api +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/client/IClientEventHandler.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/client/Scene.h" + +// RAMSES framework +#include "ramses/framework/EFeatureLevel.h" +#include "internal/Core/Utils/LogContext.h" +#include "impl/SceneFactory.h" +#include "internal/ClientApplicationLogic.h" +#include "impl/RamsesObjectImpl.h" +#include "impl/SceneObjectRegistry.h" +#include "ResourceObjects.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "impl/RamsesObjectVector.h" +#include "internal/ClientCommands/ValidateCommand.h" +#include "internal/ClientCommands/DumpSceneToFile.h" +#include "internal/ClientCommands/LogResourceMemoryUsage.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/Core/TaskFramework/ITask.h" +#include "internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h" +#include "internal/Core/TaskFramework/TaskForwardingQueue.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/SceneImpl.h" +#include "impl/SceneConfigImpl.h" + +#include +#include + +namespace ramses +{ + class Effect; + class Texture3D; + class Texture2D; + class ArrayResource; + class Texture; + class TextureCube; + class EffectDescription; + class RamsesClient; +} + +namespace ramses::internal +{ + class IInputStream; + class PrintSceneList; + class FlushSceneVersion; + class BinaryFileOutputStream; + class BinaryFileInputStream; + class ClientScene; + class ArrayResourceImpl; + class Texture2DImpl; + class ClientObjectImpl; + class SceneImpl; + class SceneConfigImpl; + class ResourceImpl; + class RamsesFrameworkImpl; + class SceneConfigImpl; + + using SceneOwningPtr = std::unique_ptr>; + using SceneVector = std::vector; + using InternalSceneOwningPtr = std::unique_ptr; + using ResourceVector = std::vector; // resources are owned by Scene's object registry + + class RamsesClientImpl final : public RamsesObjectImpl + { + public: + RamsesClientImpl(RamsesFrameworkImpl& ramsesFramework, std::string_view applicationName); + ~RamsesClientImpl() override; + + void setHLObject(RamsesClient* hlClient); + + void deinitializeFrameworkData() override; + + virtual ramses::internal::ManagedResource getResource(ramses::internal::ResourceContentHash hash) const; + template + const T* getResourceData(const ramses::internal::ResourceContentHash& hash) const + { + ramses::internal::ManagedResource managedResource = getResource(hash); + const ramses::internal::IResource* untypedResource = managedResource.get(); + return untypedResource->convertTo(); + } + const ramses::internal::ClientApplicationLogic& getClientApplication() const; + ramses::internal::ClientApplicationLogic& getClientApplication(); + + Scene* createScene(const SceneConfigImpl& sceneConfig, std::string_view name); + bool destroy(ramses::Scene& scene); + + Scene* loadSceneFromFile(std::string_view fileName, const SceneConfigImpl& config); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + Scene* loadSceneFromMemory(std::unique_ptr data, size_t size, const SceneConfigImpl& config); + Scene* loadSceneFromFileDescriptor(int fd, size_t offset, size_t length, const SceneConfigImpl& config); + + bool loadSceneFromFileAsync(std::string_view fileName, const SceneConfigImpl& config); + + bool dispatchEvents(IClientEventHandler& clientEventHandler); + + const SceneVector& getListOfScenes() const; + const Scene* findSceneByName(std::string_view name) const; + Scene* findSceneByName(std::string_view name); + const Scene* getScene(sceneId_t sceneId) const; + Scene* getScene(sceneId_t sceneId); + + const RamsesFrameworkImpl& getFramework() const; + RamsesFrameworkImpl& getFramework(); + static RamsesClientImpl& createImpl(std::string_view name, RamsesFrameworkImpl& components); + + void onValidate(ValidationReportImpl& report) const override; + + template + void enqueueSceneCommand(sceneId_t sceneId, T cmd); + + // special wrappers for known thread safe function + ramses::internal::ResourceHashUsage getHashUsage_ThreadSafe(const ramses::internal::ResourceContentHash& hash) const; + ramses::internal::ManagedResource getResource_ThreadSafe(ramses::internal::ResourceContentHash hash) const; + ramses::internal::ManagedResource loadResource_ThreadSafe(const ramses::internal::ResourceContentHash& hash) const; + + ramses::SceneReference* findSceneReference(sceneId_t masterSceneId, sceneId_t referencedSceneId); + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + ramses::internal::ManagedResource createManagedArrayResource(uint32_t numElements, ramses::EDataType type, const void* arrayData, std::string_view name); + template // NOLINTNEXTLINE(modernize-avoid-c-arrays) + ramses::internal::ManagedResource createManagedTexture(ramses::internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, uint32_t mipMapCount, const MipDataStorageType mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); + ramses::internal::ManagedResource createManagedEffect(const EffectDescription& effectDesc, std::string_view name, std::string& errorMessages); + + void writeLowLevelResourcesToStream(const ResourceObjects& resources, ramses::internal::IOutputStream& resourceOutputStream, bool compress) const; + static bool ReadRamsesVersionAndPrintWarningOnMismatch(ramses::internal::IInputStream& inputStream, std::string_view verboseFileName, EFeatureLevel featureLevel); + static void WriteCurrentBuildVersionToStream(ramses::internal::IOutputStream& stream, EFeatureLevel featureLevel); + static bool GetFeatureLevelFromFile(std::string_view fileName, EFeatureLevel& detectedFeatureLevel); + static bool GetFeatureLevelFromFile(int fd, size_t offset, size_t length, EFeatureLevel& detectedFeatureLevel); + + private: + struct SceneCreationConfig + { + std::string caller; + std::string dataSource; + ramses::internal::InputStreamContainerSPtr streamContainer; + bool prefetchData; + SceneConfigImpl config; + }; + + class LoadSceneRunnable : public ramses::internal::ITask + { + public: + LoadSceneRunnable(RamsesClientImpl& client, SceneCreationConfig&& cconfig); + void execute() override; + + private: + RamsesClientImpl& m_client; + SceneCreationConfig m_cconfig; + }; + + class DeleteSceneRunnable : public ramses::internal::ITask + { + public: + DeleteSceneRunnable(SceneOwningPtr scene, InternalSceneOwningPtr llscene); + void execute() override; + + private: + SceneOwningPtr m_scene; + InternalSceneOwningPtr m_lowLevelScene; + }; + + struct SceneLoadStatus + { + SceneOwningPtr scene; + std::string sceneFilename; + }; + + friend class LoadSceneRunnable; + + ramses::internal::ManagedResource manageResource(const ramses::internal::IResource* res); + + Scene* loadSceneSynchonousCommon(const SceneCreationConfig& cconf); + SceneOwningPtr loadSceneFromCreationConfig(const SceneCreationConfig& cconf); + SceneOwningPtr loadSceneObjectFromStream(const std::string& caller, + std::string const& filename, + ramses::internal::IInputStream& inputStream, const SceneConfigImpl& config); + void finalizeLoadedScene(SceneOwningPtr scene); + + void validateScenes(ValidationReportImpl& report) const; + + static bool GetFeatureLevelFromStream(ramses::internal::IInputStreamContainer& streamContainer, const std::string& desc, EFeatureLevel& detectedFeatureLevel); + + RamsesClient* m_hlClient = nullptr; + ramses::internal::ClientApplicationLogic m_appLogic; + ramses::internal::SceneFactory m_sceneFactory; + + SceneVector m_scenes; + + std::shared_ptr m_cmdPrintSceneList; + std::shared_ptr m_cmdPrintValidation; + std::shared_ptr m_cmdFlushSceneVersion; + std::shared_ptr m_cmdDumpSceneToFile; + std::shared_ptr m_cmdLogResourceMemoryUsage; + + RamsesFrameworkImpl& m_framework; + mutable ramses::internal::PlatformLock m_clientLock; + + ramses::internal::TaskForwardingQueue m_loadFromFileTaskQueue; + ramses::internal::EnqueueOnlyOneAtATimeQueue m_deleteSceneQueue; + + std::vector m_asyncSceneLoadStatusVec; + }; + + template + void RamsesClientImpl::enqueueSceneCommand(sceneId_t sceneId, T cmd) + { + ramses::internal::PlatformGuard guard(m_clientLock); + auto it = std::find_if(m_scenes.begin(), m_scenes.end(), [&](const auto& scene) { return scene->getSceneId() == sceneId; }); + if (it != m_scenes.end()) + (*it)->m_impl.enqueueSceneCommand(std::move(cmd)); + } +} diff --git a/client/ramses-client/impl/RamsesClientTypesImpl.cpp b/src/client/impl/RamsesClientTypesImpl.cpp similarity index 100% rename from client/ramses-client/impl/RamsesClientTypesImpl.cpp rename to src/client/impl/RamsesClientTypesImpl.cpp diff --git a/src/client/impl/RamsesClientTypesImpl.h b/src/client/impl/RamsesClientTypesImpl.h new file mode 100644 index 000000000..5f138c95a --- /dev/null +++ b/src/client/impl/RamsesClientTypesImpl.h @@ -0,0 +1,66 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/EDataType.h" +#include "ramses/client/TextureSwizzle.h" +#include "impl/TextureUtils.h" +#include "impl/TextureEnumsImpl.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/Core/Common/TypedMemoryHandle.h" + +template <> +struct fmt::formatter : public ramses::internal::SimpleFormatterBase +{ + template + constexpr auto format(const ramses::TextureSwizzle& swizzle, FormatContext& ctx) + { + return fmt::format_to(ctx.out(), "TextureSwizzling:[{};{};{};{}]", + ramses::EnumToString(swizzle.channelRed), + ramses::EnumToString(swizzle.channelGreen), + ramses::EnumToString(swizzle.channelBlue), + ramses::EnumToString(swizzle.channelAlpha)); + } +}; + +namespace ramses +{ + const std::array DataTypeNames = + { + "UInt16", + "UInt32", + "Float", + "Vector2F", + "Vector3F", + "Vector4F", + "ByteBlob", + "Bool", + "Int32", + "Vector2I", + "Vector3I", + "Vector4I", + "Matrix22F", + "Matrix33F", + "Matrix44F", + "TextureSampler2D", + "TextureSampler2DMS", + "TextureSampler3D", + "TextureSamplerCube", + "TextureSamplerExternal", + }; + + namespace internal + { + struct SceneObjectRegistryHandleTag {}; + using SceneObjectRegistryHandle = ramses::internal::TypedMemoryHandle; + } +} + +MAKE_ENUM_CLASS_PRINTABLE(ramses::EDataType, "EDataType", ramses::DataTypeNames, ramses::EDataType::TextureSamplerExternal); diff --git a/client/ramses-client/impl/RamsesObjectVector.h b/src/client/impl/RamsesObjectVector.h similarity index 69% rename from client/ramses-client/impl/RamsesObjectVector.h rename to src/client/impl/RamsesObjectVector.h index 9fa209e5c..49aa2202c 100644 --- a/client/ramses-client/impl/RamsesObjectVector.h +++ b/src/client/impl/RamsesObjectVector.h @@ -6,19 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESOBJECTVECTOR_H -#define RAMSES_RAMSESOBJECTVECTOR_H +#pragma once -#include "Collections/Vector.h" -#include "Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" namespace ramses { + namespace internal + { + class NodeImpl; + } + class RamsesObject; - class NodeImpl; using RamsesObjectVector = std::vector; - using NodeImplSet = ramses_internal::HashSet; + using NodeImplSet = internal::HashSet; } - -#endif diff --git a/client/ramses-client-api/RenderBuffer.cpp b/src/client/impl/RenderBuffer.cpp similarity index 72% rename from client/ramses-client-api/RenderBuffer.cpp rename to src/client/impl/RenderBuffer.cpp index 308bf2e22..72787f2d3 100644 --- a/client/ramses-client-api/RenderBuffer.cpp +++ b/src/client/impl/RenderBuffer.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/RenderBuffer.h" +#include "ramses/client/RenderBuffer.h" // internal -#include "RenderBufferImpl.h" +#include "impl/RenderBufferImpl.h" namespace ramses { - RenderBuffer::RenderBuffer(std::unique_ptr impl) + RenderBuffer::RenderBuffer(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } @@ -30,11 +30,6 @@ namespace ramses return m_impl.getHeight(); } - ERenderBufferType RenderBuffer::getBufferType() const - { - return m_impl.getBufferType(); - } - ERenderBufferFormat RenderBuffer::getBufferFormat() const { return m_impl.getBufferFormat(); @@ -49,4 +44,14 @@ namespace ramses { return m_impl.getSampleCount(); } + + internal::RenderBufferImpl& RenderBuffer::impl() + { + return m_impl; + } + + const internal::RenderBufferImpl& RenderBuffer::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/RenderBufferImpl.cpp b/src/client/impl/RenderBufferImpl.cpp similarity index 57% rename from client/ramses-client/impl/RenderBufferImpl.cpp rename to src/client/impl/RenderBufferImpl.cpp index 1f23cd437..88914a70d 100644 --- a/client/ramses-client/impl/RenderBufferImpl.cpp +++ b/src/client/impl/RenderBufferImpl.cpp @@ -6,25 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RenderBufferImpl.h" -#include "Scene/ClientScene.h" -#include "TextureUtils.h" +#include "impl/RenderBufferImpl.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "impl/TextureUtils.h" +#include "impl/TextureEnumsImpl.h" -namespace ramses +namespace ramses::internal { RenderBufferImpl::RenderBufferImpl(SceneImpl& scene, std::string_view name) : SceneObjectImpl(scene, ERamsesObjectType::RenderBuffer, name) { } - RenderBufferImpl::~RenderBufferImpl() - { - } + RenderBufferImpl::~RenderBufferImpl() = default; - void RenderBufferImpl::initializeFrameworkData(uint32_t width, uint32_t height, ERenderBufferType bufferType, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount) + void RenderBufferImpl::initializeFrameworkData(uint32_t width, uint32_t height, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount) { assert(!m_renderBufferHandle.isValid()); - m_renderBufferHandle = getIScene().allocateRenderBuffer({ width, height, TextureUtils::GetRenderBufferTypeInternal(bufferType), TextureUtils::GetRenderBufferFormatInternal(bufferFormat), TextureUtils::GetRenderBufferAccessModeInternal(accessMode), sampleCount }); + m_renderBufferHandle = getIScene().allocateRenderBuffer({ width, height, TextureUtils::GetRenderBufferFormatInternal(bufferFormat), accessMode, sampleCount }, {}); assert(m_renderBufferHandle.isValid()); } @@ -32,7 +31,7 @@ namespace ramses { assert(m_renderBufferHandle.isValid()); getIScene().releaseRenderBuffer(m_renderBufferHandle); - m_renderBufferHandle = ramses_internal::RenderBufferHandle::Invalid(); + m_renderBufferHandle = ramses::internal::RenderBufferHandle::Invalid(); } uint32_t RenderBufferImpl::getWidth() const @@ -47,11 +46,6 @@ namespace ramses return getIScene().getRenderBuffer(m_renderBufferHandle).height; } - ERenderBufferType RenderBufferImpl::getBufferType() const - { - return TextureUtils::GetRenderBufferTypeFromInternal(getIScene().getRenderBuffer(m_renderBufferHandle).type); - } - ERenderBufferFormat RenderBufferImpl::getBufferFormat() const { return TextureUtils::GetRenderBufferFormatFromInternal(getIScene().getRenderBuffer(m_renderBufferHandle).format); @@ -59,7 +53,7 @@ namespace ramses ERenderBufferAccessMode RenderBufferImpl::getAccessMode() const { - return TextureUtils::GetRenderBufferAccessModeFromInternal(getIScene().getRenderBuffer(m_renderBufferHandle).accessMode); + return getIScene().getRenderBuffer(m_renderBufferHandle).accessMode; } uint32_t RenderBufferImpl::getSampleCount() const @@ -68,39 +62,41 @@ namespace ramses return getIScene().getRenderBuffer(m_renderBufferHandle).sampleCount; } - ramses_internal::RenderBufferHandle RenderBufferImpl::getRenderBufferHandle() const + ramses::internal::RenderBufferHandle RenderBufferImpl::getRenderBufferHandle() const { return m_renderBufferHandle; } - status_t RenderBufferImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool RenderBufferImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << m_renderBufferHandle; - return StatusOK; + return true; } - status_t RenderBufferImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool RenderBufferImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_renderBufferHandle; - return StatusOK; + return true; } - status_t RenderBufferImpl::validate() const + void RenderBufferImpl::onValidate(ValidationReportImpl& report) const { - status_t status = SceneObjectImpl::validate(); + SceneObjectImpl::onValidate(report); const auto& iscene = getIScene(); - const bool isColorBuffer = (iscene.getRenderBuffer(getRenderBufferHandle()).type == ramses_internal::ERenderBufferType_ColorBuffer); + const bool isColorBuffer = !IsDepthOrStencilFormat(iscene.getRenderBuffer(getRenderBufferHandle()).format); bool usedAsTexture = false; - for (ramses_internal::TextureSamplerHandle sampler(0u); sampler < iscene.getTextureSamplerCount() && !usedAsTexture; ++sampler) + for (ramses::internal::TextureSamplerHandle sampler(0u); sampler < iscene.getTextureSamplerCount() && !usedAsTexture; ++sampler) { usedAsTexture = iscene.isTextureSamplerAllocated(sampler) && @@ -109,8 +105,8 @@ namespace ramses } bool usedInRenderPass = false; - ramses_internal::RenderTargetHandle rtHandle; - for (ramses_internal::RenderTargetHandle rt(0u); rt < iscene.getRenderTargetCount() && !rtHandle.isValid(); ++rt) + ramses::internal::RenderTargetHandle rtHandle; + for (ramses::internal::RenderTargetHandle rt(0u); rt < iscene.getRenderTargetCount() && !rtHandle.isValid(); ++rt) { if (!iscene.isRenderTargetAllocated(rt)) continue; @@ -126,7 +122,7 @@ namespace ramses } if (rtHandle.isValid()) { - for (ramses_internal::RenderPassHandle rp(0u); rp < iscene.getRenderPassCount(); ++rp) + for (ramses::internal::RenderPassHandle rp(0u); rp < iscene.getRenderPassCount(); ++rp) { if (iscene.isRenderPassAllocated(rp) && iscene.getRenderPass(rp).renderTarget == rtHandle && iscene.getRenderPass(rp).isEnabled) { @@ -138,8 +134,8 @@ namespace ramses bool usedAsBlitDestination = false; bool usedAsBlitSource = false; - ramses_internal::BlitPassHandle blitHandle; - for (ramses_internal::BlitPassHandle bp(0u); bp < iscene.getBlitPassCount() && !blitHandle.isValid(); ++bp) + ramses::internal::BlitPassHandle blitHandle; + for (ramses::internal::BlitPassHandle bp(0u); bp < iscene.getBlitPassCount() && !blitHandle.isValid(); ++bp) { if (!iscene.isBlitPassAllocated(bp)) continue; @@ -163,30 +159,30 @@ namespace ramses // explicitly warn about usage of potentially uninitialized buffer if (usedAsTexture && !(usedInRenderPass || usedAsBlitDestination)) { - addValidationMessage(EValidationSeverity::Warning, "RenderBuffer is used in a TextureSampler for reading but is not set as destination in any RenderPass or BlitPass, this can lead to usage of uninitialized data."); + report.add(EIssueType::Warning, + "RenderBuffer is used in a TextureSampler for reading but is not set as destination in any RenderPass or BlitPass, this can lead to usage of " + "uninitialized data.", &getRamsesObject()); hasIssue = true; } if (!usedInRenderPass && !usedAsBlitDestination) { hasIssue = true; - addValidationMessage(EValidationSeverity::Warning, "RenderBuffer is not set as destination in any RenderPass or BlitPass, destroy it if not needed."); + report.add(EIssueType::Warning, "RenderBuffer is not set as destination in any RenderPass or BlitPass, destroy it if not needed.", &getRamsesObject()); } if (!usedAsTexture && !usedAsBlitSource && isColorBuffer) // depth/stencil buffer does not need to be validated for usage as texture { hasIssue = true; - addValidationMessage(EValidationSeverity::Warning, "RenderBuffer is neither used in a TextureSampler for reading nor set as source in a BlitPass, destroy it if not needed."); + report.add(EIssueType::Warning, "RenderBuffer is neither used in a TextureSampler for reading nor set as source in a BlitPass, destroy it if not needed.", &getRamsesObject()); } if (hasIssue) { - ramses_internal::StringOutputStream rbDesc; - const ramses_internal::RenderBuffer& rb = getIScene().getRenderBuffer(m_renderBufferHandle); - rbDesc << " [" << rb.width << "x" << rb.height << "; " << ramses_internal::EnumToString(rb.type) << "; " << ramses_internal::EnumToString(rb.format) << "; " << ramses_internal::EnumToString(rb.accessMode) << "; " << rb.sampleCount << " samples]"; - return addValidationMessage(EValidationSeverity::Warning, rbDesc.c_str()); + ramses::internal::StringOutputStream rbDesc; + const ramses::internal::RenderBuffer& rb = getIScene().getRenderBuffer(m_renderBufferHandle); + rbDesc << " [" << rb.width << "x" << rb.height << "; " << ramses::internal::EnumToString(rb.format) << "; " << EnumToString(rb.accessMode) << "; " << rb.sampleCount << " samples]"; + return report.add(EIssueType::Warning, rbDesc.release(), &getRamsesObject()); } - - return status; } } diff --git a/src/client/impl/RenderBufferImpl.h b/src/client/impl/RenderBufferImpl.h new file mode 100644 index 000000000..4ede5f871 --- /dev/null +++ b/src/client/impl/RenderBufferImpl.h @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/TextureEnums.h" + +// internal +#include "SceneObjectImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/Handles.h" + +#include + +namespace ramses::internal +{ + class RenderBufferImpl final : public SceneObjectImpl + { + public: + RenderBufferImpl(SceneImpl& scene, std::string_view name); + ~RenderBufferImpl() override; + + void initializeFrameworkData(uint32_t width, uint32_t height, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + + void onValidate(ValidationReportImpl& report) const override; + + [[nodiscard]] uint32_t getWidth() const; + [[nodiscard]] uint32_t getHeight() const; + [[nodiscard]] ERenderBufferFormat getBufferFormat() const; + [[nodiscard]] ERenderBufferAccessMode getAccessMode() const; + [[nodiscard]] uint32_t getSampleCount() const; + + [[nodiscard]] ramses::internal::RenderBufferHandle getRenderBufferHandle() const; + + private: + ramses::internal::RenderBufferHandle m_renderBufferHandle; + }; +} diff --git a/src/client/impl/RenderGroup.cpp b/src/client/impl/RenderGroup.cpp new file mode 100644 index 000000000..fda8bbe6e --- /dev/null +++ b/src/client/impl/RenderGroup.cpp @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" + +// internal +#include "impl/RenderGroupImpl.h" + +namespace ramses +{ + RenderGroup::RenderGroup(std::unique_ptr impl) + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + bool RenderGroup::addMeshNode(const MeshNode& mesh, int32_t orderWithinGroup) + { + const bool status = m_impl.addMeshNode(mesh.impl(), orderWithinGroup); + LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(mesh), orderWithinGroup); + return status; + } + + bool RenderGroup::removeMeshNode(const MeshNode& mesh) + { + const bool status = m_impl.remove(mesh.impl()); + LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(mesh)); + return status; + } + + bool RenderGroup::containsMeshNode(const MeshNode& mesh) const + { + return m_impl.contains(mesh.impl()); + } + + bool RenderGroup::getMeshNodeOrder(const MeshNode& mesh, int32_t& orderWithinGroup) const + { + return m_impl.getMeshNodeOrder(mesh.impl(), orderWithinGroup); + } + + bool RenderGroup::addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinGroup) + { + const bool status = m_impl.addRenderGroup(renderGroup.m_impl, orderWithinGroup); + LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(renderGroup), orderWithinGroup); + return status; + } + + bool RenderGroup::removeRenderGroup(const RenderGroup& renderGroup) + { + const bool status = m_impl.remove(renderGroup.m_impl); + LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(renderGroup)); + return status; + } + + bool RenderGroup::containsRenderGroup(const RenderGroup& renderGroup) const + { + return m_impl.contains(renderGroup.m_impl); + } + + bool RenderGroup::getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinGroup) const + { + return m_impl.getRenderGroupOrder(renderGroup.m_impl, orderWithinGroup); + } + + bool RenderGroup::removeAllRenderables() + { + const bool status = m_impl.removeAllRenderables(); + LOG_HL_CLIENT_API_NOARG(status); + return status; + } + + bool RenderGroup::removeAllRenderGroups() + { + const bool status = m_impl.removeAllRenderGroups(); + LOG_HL_CLIENT_API_NOARG(status); + return status; + } + + internal::RenderGroupImpl& RenderGroup::impl() + { + return m_impl; + } + + const internal::RenderGroupImpl& RenderGroup::impl() const + { + return m_impl; + } +} diff --git a/src/client/impl/RenderGroupImpl.cpp b/src/client/impl/RenderGroupImpl.cpp new file mode 100644 index 000000000..dc4282791 --- /dev/null +++ b/src/client/impl/RenderGroupImpl.cpp @@ -0,0 +1,320 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RenderGroupImpl.h" + +#include "ramses/client/MeshNode.h" + +#include "impl/SerializationContext.h" +#include "impl/MeshNodeImpl.h" +#include "impl/SceneObjectImpl.h" +#include "impl/SceneImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneAPI/RenderGroup.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" + + +namespace ramses::internal +{ + RenderGroupImpl::RenderGroupImpl(SceneImpl& scene, std::string_view name) + : SceneObjectImpl(scene, ERamsesObjectType::RenderGroup, name) + { + } + + RenderGroupImpl::~RenderGroupImpl() = default; + + template + void serializeObjects(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext, const std::vector& objects) + { + outStream << static_cast(objects.size()); + for(const auto& object : objects) + { + assert(nullptr != object); + outStream << serializationContext.getIDForObject(object); + } + } + + bool RenderGroupImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << m_renderGroupHandle; + + serializeObjects(outStream, serializationContext, m_meshes); + serializeObjects(outStream, serializationContext, m_renderGroups); + + return true; + } + + template + void deserializeObjects(ramses::internal::IInputStream& inStream, [[maybe_unused]] DeserializationContext& serializationContext, std::vector& objects) + { + uint32_t numberOfObjects = 0; + inStream >> numberOfObjects; + assert(objects.empty()); + objects.reserve(numberOfObjects); + + for (uint32_t i = 0; i < numberOfObjects; ++i) + { + OBJECT* object = nullptr; + serializationContext.ReadDependentPointerAndStoreAsID(inStream, object); + objects.push_back(object); + } + } + + bool RenderGroupImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + inStream >> m_renderGroupHandle; + + deserializeObjects(inStream, serializationContext, m_meshes); + deserializeObjects(inStream, serializationContext, m_renderGroups); + + serializationContext.addForDependencyResolve(this); + + return true; + } + + bool RenderGroupImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::resolveDeserializationDependencies(serializationContext)) + return false; + + for (auto& mesh : m_meshes) + { + serializationContext.resolveDependencyIDImplAndStoreAsPointer(mesh); + } + + for (auto& group : m_renderGroups) + { + serializationContext.resolveDependencyIDImplAndStoreAsPointer(group); + } + + return true; + } + + void RenderGroupImpl::onValidate(ValidationReportImpl& report) const + { + SceneObjectImpl::onValidate(report); + + if (m_meshes.empty() && m_renderGroups.empty()) + report.add(EIssueType::Warning, "rendergroup does not contain any meshes", &getRamsesObject()); + + for (const auto& element : m_meshes) + report.addDependentObject(*this, *element); + + for (const auto& element : m_renderGroups) + report.addDependentObject(*this, *element); + } + + void RenderGroupImpl::initializeFrameworkData() + { + m_renderGroupHandle = getIScene().allocateRenderGroup(0, 0, {}); + } + + void RenderGroupImpl::deinitializeFrameworkData() + { + assert(m_renderGroupHandle.isValid()); + getIScene().releaseRenderGroup(m_renderGroupHandle); + m_renderGroupHandle = ramses::internal::RenderGroupHandle::Invalid(); + } + + bool RenderGroupImpl::contains(const MeshNodeImpl& meshImpl) const + { + // const cast just to get ptr for query + return ramses::internal::contains_c(m_meshes, &meshImpl); + } + + const MeshNodeImplVector& RenderGroupImpl::getAllMeshes() const + { + return m_meshes; + } + + bool RenderGroupImpl::getMeshNodeOrder(const MeshNodeImpl& mesh, int32_t& orderWithinGroup) const + { + if (!contains(mesh)) + { + getErrorReporting().set("RenderGroup::getMeshNodeOrder failed - mesh not contained in RenderGroup", *this); + return false; + } + + const auto& internalRenderGroup = getIScene().getRenderGroup(m_renderGroupHandle); + const auto renderableEntryIt = ramses::internal::RenderGroupUtils::FindRenderableEntry(mesh.getRenderableHandle(), internalRenderGroup); + if (renderableEntryIt == internalRenderGroup.renderables.cend()) + { + assert(false); + getErrorReporting().set("RenderGroup::getMeshNodeOrder failed - fatal, mesh not found in internal render group", *this); + return false; + } + + orderWithinGroup = renderableEntryIt->order; + return true; + } + + bool RenderGroupImpl::addMeshNode(const MeshNodeImpl& meshImpl, int32_t orderWithinGroup) + { + if (!isFromTheSameSceneAs(meshImpl)) + { + getErrorReporting().set("RenderGroup::addMeshNode failed - meshNode is not from the same scene as this RenderGroup.", *this); + return false; + } + + if (ramses::internal::contains_c(m_meshes, &meshImpl)) + { + remove(meshImpl); + } + + const ramses::internal::RenderableHandle renderableToAdd = meshImpl.getRenderableHandle(); + getIScene().addRenderableToRenderGroup(m_renderGroupHandle, renderableToAdd, orderWithinGroup); + m_meshes.push_back(&meshImpl); + + return true; + } + + bool RenderGroupImpl::remove(const MeshNodeImpl& mesh) + { + auto iter = ramses::internal::find_c(m_meshes, &mesh); + if (iter == m_meshes.end()) + { + getErrorReporting().set("RenderGroup::remove failed - could not remove MeshNode from RenderGroup because it was not contained", *this); + return false; + } + + removeInternal(iter); + + return true; + } + + void RenderGroupImpl::removeIfContained(const MeshNodeImpl& mesh) + { + auto iter = ramses::internal::find_c(m_meshes, &mesh); + if (iter != m_meshes.end()) + { + removeInternal(iter); + } + } + + bool RenderGroupImpl::addRenderGroup(const RenderGroupImpl& renderGroupImpl, int32_t orderWithinGroup) + { + if (!isFromTheSameSceneAs(renderGroupImpl)) + { + getErrorReporting().set("RenderGroup::addRenderGroup failed - renderGroup is not from the same scene as this RenderGroup.", *this); + return false; + } + + if (ramses::internal::contains_c(m_renderGroups, &renderGroupImpl)) + { + remove(renderGroupImpl); + } + + const ramses::internal::RenderGroupHandle renderGroupToAdd = renderGroupImpl.getRenderGroupHandle(); + getIScene().addRenderGroupToRenderGroup(m_renderGroupHandle, renderGroupToAdd, orderWithinGroup); + m_renderGroups.push_back(&renderGroupImpl); + + return true; + } + + bool RenderGroupImpl::remove(const RenderGroupImpl& renderGroup) + { + auto iter = ramses::internal::find_c(m_renderGroups, &renderGroup); + if (iter == m_renderGroups.end()) + { + getErrorReporting().set("RenderGroup::removeRenderGroup failed - could not remove render group from RenderGroup because it was not contained", *this); + return false; + } + + removeInternal(iter); + + return true; + } + + void RenderGroupImpl::removeIfContained(const RenderGroupImpl& renderGroup) + { + auto iter = ramses::internal::find_c(m_renderGroups, &renderGroup); + if (iter != m_renderGroups.end()) + { + removeInternal(iter); + } + } + + bool RenderGroupImpl::contains(const RenderGroupImpl& renderGroup) const + { + return ramses::internal::contains_c(m_renderGroups, &renderGroup); + } + + bool RenderGroupImpl::getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinGroup) const + { + if (!contains(renderGroup)) + { + getErrorReporting().set("RenderGroup::getRenderGroupOrder failed - render group not contained in RenderGroup", *this); + return false; + } + + const auto& internalRenderGroup = getIScene().getRenderGroup(m_renderGroupHandle); + const auto renderGroupEntryIt = ramses::internal::RenderGroupUtils::FindRenderGroupEntry(renderGroup.getRenderGroupHandle(), internalRenderGroup); + if (renderGroupEntryIt == internalRenderGroup.renderGroups.cend()) + { + getErrorReporting().set("RenderGroup::getRenderGroupOrder failed - fatal, render group not found in internal render group", *this); + assert(false); + return false; + } + + orderWithinGroup = renderGroupEntryIt->order; + return true; + } + + const RenderGroupImplVector& RenderGroupImpl::getAllRenderGroups() const + { + return m_renderGroups; + } + + bool RenderGroupImpl::removeAllRenderables() + { + while (!m_meshes.empty()) + { + if (!remove(*m_meshes.front())) + return false; + } + + return true; + } + + bool RenderGroupImpl::removeAllRenderGroups() + { + while (!m_renderGroups.empty()) + { + if (!remove(*m_renderGroups.front())) + return false; + } + + return true; + } + + ramses::internal::RenderGroupHandle RenderGroupImpl::getRenderGroupHandle() const + { + return m_renderGroupHandle; + } + + void RenderGroupImpl::removeInternal(MeshNodeImplVector::iterator iter) + { + const ramses::internal::RenderableHandle renderableToRemove = (*iter)->getRenderableHandle(); + getIScene().removeRenderableFromRenderGroup(m_renderGroupHandle, renderableToRemove); + m_meshes.erase(iter); + } + + void RenderGroupImpl::removeInternal(RenderGroupImplVector::iterator iter) + { + const ramses::internal::RenderGroupHandle renderGroupToRemove = (*iter)->getRenderGroupHandle(); + getIScene().removeRenderGroupFromRenderGroup(m_renderGroupHandle, renderGroupToRemove); + m_renderGroups.erase(iter); + } +} diff --git a/src/client/impl/RenderGroupImpl.h b/src/client/impl/RenderGroupImpl.h new file mode 100644 index 000000000..5392d5e26 --- /dev/null +++ b/src/client/impl/RenderGroupImpl.h @@ -0,0 +1,66 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "SceneObjectImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" + +#include +#include + +namespace ramses::internal +{ + class MeshNodeImpl; + class RenderGroupImpl; + + using MeshNodeImplVector = std::vector; + using RenderGroupImplVector = std::vector; + + class RenderGroupImpl final : public SceneObjectImpl + { + public: + RenderGroupImpl(SceneImpl& scene, std::string_view name); + ~RenderGroupImpl() override; + + void initializeFrameworkData(); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + bool addMeshNode(const MeshNodeImpl& mesh, int32_t orderWithinGroup); + bool remove(const MeshNodeImpl& mesh); + void removeIfContained(const MeshNodeImpl& mesh); + [[nodiscard]] bool contains(const MeshNodeImpl& mesh) const; + bool getMeshNodeOrder(const MeshNodeImpl& mesh, int32_t& orderWithinGroup) const; + [[nodiscard]] const MeshNodeImplVector& getAllMeshes() const; + + bool addRenderGroup(const RenderGroupImpl& renderGroup, int32_t orderWithinGroup); + bool remove(const RenderGroupImpl& renderGroup); + void removeIfContained(const RenderGroupImpl& renderGroup); + [[nodiscard]] bool contains(const RenderGroupImpl& renderGroup) const; + bool getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinGroup) const; + [[nodiscard]] const RenderGroupImplVector& getAllRenderGroups() const; + + bool removeAllRenderables(); + bool removeAllRenderGroups(); + + [[nodiscard]] ramses::internal::RenderGroupHandle getRenderGroupHandle() const; + + private: + void removeInternal(MeshNodeImplVector::iterator iter); + void removeInternal(RenderGroupImplVector::iterator iter); + + ramses::internal::RenderGroupHandle m_renderGroupHandle; + + MeshNodeImplVector m_meshes; + RenderGroupImplVector m_renderGroups; + }; +} diff --git a/client/ramses-client/impl/RenderGroupMeshIterator.cpp b/src/client/impl/RenderGroupMeshIterator.cpp similarity index 58% rename from client/ramses-client/impl/RenderGroupMeshIterator.cpp rename to src/client/impl/RenderGroupMeshIterator.cpp index 78845e39a..e1bb0bc82 100644 --- a/client/ramses-client/impl/RenderGroupMeshIterator.cpp +++ b/src/client/impl/RenderGroupMeshIterator.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/RenderGroupMeshIterator.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/MeshNode.h" -#include "RenderGroupImpl.h" -#include "MeshNodeImpl.h" -#include "IteratorImpl.h" -#include "RamsesObjectTypeUtils.h" +#include "ramses/client/RenderGroupMeshIterator.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" +#include "impl/RenderGroupImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/IteratorImpl.h" +#include "impl/RamsesObjectTypeUtils.h" namespace ramses { RenderGroupMeshIterator::RenderGroupMeshIterator(const RenderGroup& renderGroup) - : m_impl{ std::make_unique>(renderGroup.m_impl.getAllMeshes()) } + : m_impl{ std::make_unique>(renderGroup.impl().getAllMeshes()) } { } @@ -25,10 +25,10 @@ namespace ramses const MeshNode* RenderGroupMeshIterator::getNext() { - const MeshNodeImpl* meshNode = m_impl->getNext(); + const internal::MeshNodeImpl* meshNode = m_impl->getNext(); if (meshNode == nullptr) return nullptr; - return &RamsesObjectTypeUtils::ConvertTo(meshNode->getRamsesObject()); + return &internal::RamsesObjectTypeUtils::ConvertTo(meshNode->getRamsesObject()); } } diff --git a/client/ramses-client-api/RenderPass.cpp b/src/client/impl/RenderPass.cpp similarity index 53% rename from client/ramses-client-api/RenderPass.cpp rename to src/client/impl/RenderPass.cpp index 4bd81a71e..f4d1869ce 100644 --- a/client/ramses-client-api/RenderPass.cpp +++ b/src/client/impl/RenderPass.cpp @@ -7,25 +7,26 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/Camera.h" +#include "ramses/client/RenderGroup.h" +#include "impl/RamsesFrameworkTypesImpl.h" // internal -#include "RenderPassImpl.h" +#include "impl/RenderPassImpl.h" namespace ramses { - RenderPass::RenderPass(std::unique_ptr impl) + RenderPass::RenderPass(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } - status_t RenderPass::setCamera(const Camera& camera) + bool RenderPass::setCamera(const Camera& camera) { - const status_t status = m_impl.setCamera(camera.m_impl); + const bool status = m_impl.setCamera(camera.impl()); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(camera)); return status; } @@ -40,40 +41,40 @@ namespace ramses return m_impl.getCamera(); } - status_t RenderPass::addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinPass) + bool RenderPass::addRenderGroup(const RenderGroup& renderGroup, int32_t orderWithinPass) { - const status_t status = m_impl.addRenderGroup(renderGroup.m_impl, orderWithinPass); + const bool status = m_impl.addRenderGroup(renderGroup.impl(), orderWithinPass); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(renderGroup), orderWithinPass); return status; } - status_t RenderPass::removeRenderGroup(const RenderGroup& renderGroup) + bool RenderPass::removeRenderGroup(const RenderGroup& renderGroup) { - const status_t status = m_impl.remove(renderGroup.m_impl); + const bool status = m_impl.remove(renderGroup.impl()); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(renderGroup)); return status; } bool RenderPass::containsRenderGroup(const RenderGroup& renderGroup) const { - return m_impl.contains(renderGroup.m_impl); + return m_impl.contains(renderGroup.impl()); } - status_t RenderPass::getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinPass) const + bool RenderPass::getRenderGroupOrder(const RenderGroup& renderGroup, int32_t& orderWithinPass) const { - return m_impl.getRenderGroupOrder(renderGroup.m_impl, orderWithinPass); + return m_impl.getRenderGroupOrder(renderGroup.impl(), orderWithinPass); } - status_t RenderPass::removeAllRenderGroups() + bool RenderPass::removeAllRenderGroups() { - const status_t status = m_impl.removeAllRenderGroups(); + const bool status = m_impl.removeAllRenderGroups(); LOG_HL_CLIENT_API_NOARG(status); return status; } - status_t RenderPass::setRenderTarget(RenderTarget* renderTarget) + bool RenderPass::setRenderTarget(RenderTarget* renderTarget) { - const status_t status = m_impl.setRenderTarget(renderTarget ? &renderTarget->m_impl : nullptr); + const bool status = m_impl.setRenderTarget(renderTarget ? &renderTarget->impl() : nullptr); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_PTR_STRING(renderTarget)); return status; } @@ -83,9 +84,9 @@ namespace ramses return m_impl.getRenderTarget(); } - status_t RenderPass::setClearColor(const vec4f& color) + bool RenderPass::setClearColor(const vec4f& color) { - const status_t status = m_impl.setClearColor(color); + const bool status = m_impl.setClearColor(color); LOG_HL_CLIENT_API4(status, color.r, color.g, color.b, color.a); return status; } @@ -95,21 +96,21 @@ namespace ramses return m_impl.getClearColor(); } - status_t RenderPass::setClearFlags(uint32_t clearFlags) + bool RenderPass::setClearFlags(ClearFlags clearFlags) { - const status_t status = m_impl.setClearFlags(clearFlags); + const bool status = m_impl.setClearFlags(clearFlags); LOG_HL_CLIENT_API1(status, clearFlags); return status; } - uint32_t RenderPass::getClearFlags() const + ClearFlags RenderPass::getClearFlags() const { return m_impl.getClearFlags(); } - status_t RenderPass::setRenderOrder(int32_t renderOrder) + bool RenderPass::setRenderOrder(int32_t renderOrder) { - const status_t status = m_impl.setRenderOrder(renderOrder); + const bool status = m_impl.setRenderOrder(renderOrder); LOG_HL_CLIENT_API1(status, renderOrder); return status; } @@ -119,9 +120,9 @@ namespace ramses return m_impl.getRenderOrder(); } - status_t RenderPass::setEnabled(bool enable) + bool RenderPass::setEnabled(bool enable) { - const status_t status = m_impl.setEnabled(enable); + const bool status = m_impl.setEnabled(enable); LOG_HL_CLIENT_API1(status, enable); return status; } @@ -131,9 +132,9 @@ namespace ramses return m_impl.isEnabled(); } - status_t RenderPass::setRenderOnce(bool enable) + bool RenderPass::setRenderOnce(bool enable) { - const status_t status = m_impl.setRenderOnce(enable); + const bool status = m_impl.setRenderOnce(enable); LOG_HL_CLIENT_API1(status, enable); return status; } @@ -143,10 +144,20 @@ namespace ramses return m_impl.isRenderOnce(); } - status_t RenderPass::retriggerRenderOnce() + bool RenderPass::retriggerRenderOnce() { - const status_t status = m_impl.retriggerRenderOnce(); + const bool status = m_impl.retriggerRenderOnce(); LOG_HL_CLIENT_API_NOARG(status); return status; } + + internal::RenderPassImpl& RenderPass::impl() + { + return m_impl; + } + + const internal::RenderPassImpl& RenderPass::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/RenderPassGroupIterator.cpp b/src/client/impl/RenderPassGroupIterator.cpp similarity index 58% rename from client/ramses-client/impl/RenderPassGroupIterator.cpp rename to src/client/impl/RenderPassGroupIterator.cpp index d9928e152..66634bcb6 100644 --- a/client/ramses-client/impl/RenderPassGroupIterator.cpp +++ b/src/client/impl/RenderPassGroupIterator.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/RenderPassGroupIterator.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "RenderPassImpl.h" -#include "RenderGroupImpl.h" -#include "IteratorImpl.h" -#include "RamsesObjectTypeUtils.h" +#include "ramses/client/RenderPassGroupIterator.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "impl/RenderPassImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/IteratorImpl.h" +#include "impl/RamsesObjectTypeUtils.h" namespace ramses { RenderPassGroupIterator::RenderPassGroupIterator(const RenderPass& renderPass) - : m_impl{ std::make_unique>(renderPass.m_impl.getAllRenderGroups()) } + : m_impl{ std::make_unique>(renderPass.impl().getAllRenderGroups()) } { } @@ -25,10 +25,10 @@ namespace ramses const RenderGroup* RenderPassGroupIterator::getNext() { - const RenderGroupImpl* renderGroup = m_impl->getNext(); + const internal::RenderGroupImpl* renderGroup = m_impl->getNext(); if (renderGroup == nullptr) return nullptr; - return &RamsesObjectTypeUtils::ConvertTo(renderGroup->getRamsesObject()); + return &internal::RamsesObjectTypeUtils::ConvertTo(renderGroup->getRamsesObject()); } } diff --git a/src/client/impl/RenderPassImpl.cpp b/src/client/impl/RenderPassImpl.cpp new file mode 100644 index 000000000..02d37d708 --- /dev/null +++ b/src/client/impl/RenderPassImpl.cpp @@ -0,0 +1,397 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RenderPassImpl.h" + +#include "ramses/client/Camera.h" +#include "ramses/client/RenderTarget.h" + +#include "impl/SerializationContext.h" +#include "impl/CameraNodeImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/ErrorReporting.h" + +#include "internal/SceneGraph/Scene/ClientScene.h" + +#include + +namespace ramses::internal +{ + RenderPassImpl::RenderPassImpl(SceneImpl& scene, std::string_view renderpassName) + : SceneObjectImpl(scene, ERamsesObjectType::RenderPass, renderpassName) + , m_cameraImpl(nullptr) + , m_renderTargetImpl(nullptr) + { + } + + RenderPassImpl::~RenderPassImpl() = default; + + bool RenderPassImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << m_renderPassHandle; + + if (m_cameraImpl != nullptr) + { + outStream << serializationContext.getIDForObject(m_cameraImpl); + } + else + { + outStream << SerializationContext::GetObjectIDNull(); + } + + if (m_renderTargetImpl != nullptr) + { + outStream << serializationContext.getIDForObject(m_renderTargetImpl); + } + else + { + outStream << SerializationContext::GetObjectIDNull(); + } + + outStream << static_cast(m_renderGroups.size()); + for (const auto groupImpl : m_renderGroups) + { + assert(nullptr != groupImpl); + outStream << serializationContext.getIDForObject(groupImpl); + } + + return true; + } + + bool RenderPassImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + inStream >> m_renderPassHandle; + + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_cameraImpl); + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, m_renderTargetImpl); + + uint32_t numberOfGroups = 0; + inStream >> numberOfGroups; + assert(m_renderGroups.empty()); + m_renderGroups.reserve(numberOfGroups); + + for (uint32_t i = 0; i < numberOfGroups; ++i) + { + RenderGroupImpl* groupImpl = nullptr; + DeserializationContext::ReadDependentPointerAndStoreAsID(inStream, groupImpl); + m_renderGroups.push_back(groupImpl); + } + + serializationContext.addForDependencyResolve(this); + + return true; + } + + bool RenderPassImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::resolveDeserializationDependencies(serializationContext)) + return false; + + serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_cameraImpl); + serializationContext.resolveDependencyIDImplAndStoreAsPointer(m_renderTargetImpl); + + for (auto& renderGroup : m_renderGroups) + { + serializationContext.resolveDependencyIDImplAndStoreAsPointer(renderGroup); + } + + return true; + } + + void RenderPassImpl::onValidate(ValidationReportImpl& report) const + { + SceneObjectImpl::onValidate(report); + + if (nullptr == m_cameraImpl) + { + report.add(EIssueType::Warning, "renderpass does not have a camera set", &getRamsesObject()); + } + + if (m_renderGroups.empty()) + { + report.add(EIssueType::Warning, "renderpass does not contain any rendergroups", &getRamsesObject()); + } + else + { + for (const auto& renderGroup : m_renderGroups) + report.addDependentObject(*this, *renderGroup); + } + + if (nullptr != m_renderTargetImpl) + { + report.addDependentObject(*this, *m_renderTargetImpl); + } + else if (getClearFlags() != EClearFlag::None) + { + report.add(EIssueType::Warning, "renderpass has clear flags enabled whithout any rendertarget, clear flags will have no effect", &getRamsesObject()); + } + } + + void RenderPassImpl::initializeFrameworkData() + { + m_renderPassHandle = getIScene().allocateRenderPass(0, {}); + } + + void RenderPassImpl::deinitializeFrameworkData() + { + assert(m_renderPassHandle.isValid()); + getIScene().releaseRenderPass(m_renderPassHandle); + m_renderPassHandle = ramses::internal::RenderPassHandle::Invalid(); + } + + bool RenderPassImpl::setCamera(const CameraNodeImpl& cameraImpl) + { + if (!isFromTheSameSceneAs(cameraImpl)) + { + getErrorReporting().set("RenderPass::setCamera failed - camera is not from the same scene as this RenderPass", *this); + return false; + } + ValidationReportImpl cameraReport; + cameraImpl.validate(cameraReport); + if (!cameraReport.hasError()) + { + m_cameraImpl = &cameraImpl; + getIScene().setRenderPassCamera(m_renderPassHandle, cameraImpl.getCameraHandle()); + } + else + { + getErrorReporting().set(fmt::format("RenderPass::setCamera failed - camera is not valid, maybe camera was not initialized:\n{}", cameraReport.toString()), *this); + return false; + } + + return true; + } + + const ramses::Camera* RenderPassImpl::getCamera() const + { + if (m_cameraImpl != nullptr) + { + return &RamsesObjectTypeUtils::ConvertTo(m_cameraImpl->getRamsesObject()); + } + + return nullptr; + } + + ramses::Camera* RenderPassImpl::getCamera() + { + // non-const version of getCamera cast to its const version to avoid duplicating code + return const_cast((const_cast(*this)).getCamera()); + } + + bool RenderPassImpl::setClearColor(const glm::vec4& clearColor) + { + getIScene().setRenderPassClearColor(m_renderPassHandle, clearColor); + return true; + } + + const glm::vec4& RenderPassImpl::getClearColor() const + { + return getIScene().getRenderPass(m_renderPassHandle).clearColor; + } + + bool RenderPassImpl::setClearFlags(ClearFlags clearFlags) + { + getIScene().setRenderPassClearFlag(m_renderPassHandle, clearFlags); + return true; + } + + ClearFlags RenderPassImpl::getClearFlags() const + { + return getIScene().getRenderPass(m_renderPassHandle).clearFlags; + } + + bool RenderPassImpl::addRenderGroup(const RenderGroupImpl& renderGroup, int32_t orderWithinPass) + { + if (!isFromTheSameSceneAs(renderGroup)) + { + getErrorReporting().set("RenderPass::addRenderGroup failed - renderGroup is not from the same scene as this RenderPass", *this); + return false; + } + + if (!ramses::internal::contains_c(m_renderGroups, &renderGroup)) + { + const ramses::internal::RenderGroupHandle renderGroupHandle = renderGroup.getRenderGroupHandle(); + getIScene().addRenderGroupToRenderPass(m_renderPassHandle, renderGroupHandle, orderWithinPass); + m_renderGroups.push_back(&renderGroup); + } + + return true; + } + + bool RenderPassImpl::remove(const RenderGroupImpl& renderGroup) + { + auto iter = ramses::internal::find_c(m_renderGroups, &renderGroup); + if (iter == m_renderGroups.end()) + { + getErrorReporting().set("RenderPass::removeRenderGroup failed - could not remove RenderGroup from Renderpass because it was not contained", *this); + return false; + } + + removeInternal(iter); + + return true; + } + + void RenderPassImpl::removeIfContained(const RenderGroupImpl& renderGroup) + { + auto iter = ramses::internal::find_c(m_renderGroups, &renderGroup); + if (iter != m_renderGroups.end()) + { + removeInternal(iter); + } + } + + bool RenderPassImpl::contains(const RenderGroupImpl& renderGroup) const + { + return ramses::internal::contains_c(m_renderGroups, &renderGroup); + } + + bool RenderPassImpl::getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinPass) const + { + if (!contains(renderGroup)) + { + getErrorReporting().set("RenderPass::getRenderGroupOrder failed - render group not contained in RenderPass", *this); + return false; + } + + const ramses::internal::RenderGroupHandle renderGroupHandle = renderGroup.getRenderGroupHandle(); + const ramses::internal::RenderPass& internalRP = getIScene().getRenderPass(m_renderPassHandle); + for (const auto& rgEntry : internalRP.renderGroups) + { + if (rgEntry.renderGroup == renderGroupHandle) + { + orderWithinPass = rgEntry.order; + return true; + } + } + + getErrorReporting().set("RenderPass::getRenderGroupOrder failed - fatal, render group not found in internal render pass", *this); + assert(false); + return false; + } + + const RenderGroupVector& RenderPassImpl::getAllRenderGroups() const + { + return m_renderGroups; + } + + bool RenderPassImpl::removeAllRenderGroups() + { + while (!m_renderGroups.empty()) + { + if (!remove(*m_renderGroups.front())) + return false; + } + + return true; + } + + ramses::internal::RenderPassHandle RenderPassImpl::getRenderPassHandle() const + { + return m_renderPassHandle; + } + + bool RenderPassImpl::setRenderTarget(RenderTargetImpl* renderTargetImpl) + { + if (renderTargetImpl == m_renderTargetImpl) + return true; + + ramses::internal::RenderTargetHandle rtHandle(ramses::internal::RenderTargetHandle::Invalid()); + if (nullptr != renderTargetImpl) + { + if (nullptr == m_cameraImpl) + { + getErrorReporting().set("RenderPass::setRenderTarget failed - must explicitly assign a custom camera (perspective or orthographic) before rendering to render terget.", *this); + return false; + } + + if (!isFromTheSameSceneAs(*renderTargetImpl)) + { + getErrorReporting().set("RenderPass::setRenderTarget failed - renderTarget is not from the same scene as this RenderPass.", *this); + return false; + } + + rtHandle = renderTargetImpl->getRenderTargetHandle(); + } + + m_renderTargetImpl = renderTargetImpl; + getIScene().setRenderPassRenderTarget(m_renderPassHandle, rtHandle); + + return true; + } + + const ramses::RenderTarget* RenderPassImpl::getRenderTarget() const + { + if (m_renderTargetImpl != nullptr) + { + return &RamsesObjectTypeUtils::ConvertTo< ramses::RenderTarget>(m_renderTargetImpl->getRamsesObject()); + } + + return nullptr; + } + + bool RenderPassImpl::setRenderOrder(int32_t renderOrder) + { + getIScene().setRenderPassRenderOrder(m_renderPassHandle, renderOrder); + return true; + } + + int32_t RenderPassImpl::getRenderOrder() const + { + return getIScene().getRenderPass(m_renderPassHandle).renderOrder; + } + + bool RenderPassImpl::setEnabled(bool isEnabeld) + { + getIScene().setRenderPassEnabled(m_renderPassHandle, isEnabeld); + return true; + } + + bool RenderPassImpl::isEnabled() const + { + return getIScene().getRenderPass(m_renderPassHandle).isEnabled; + } + + void RenderPassImpl::removeInternal(RenderGroupVector::iterator iter) + { + const ramses::internal::RenderGroupHandle renderGroupHandle = (*iter)->getRenderGroupHandle(); + getIScene().removeRenderGroupFromRenderPass(m_renderPassHandle, renderGroupHandle); + m_renderGroups.erase(iter); + } + + bool RenderPassImpl::setRenderOnce(bool enable) + { + getIScene().setRenderPassRenderOnce(m_renderPassHandle, enable); + return true; + } + + bool RenderPassImpl::isRenderOnce() const + { + return getIScene().getRenderPass(m_renderPassHandle).isRenderOnce; + } + + bool RenderPassImpl::retriggerRenderOnce() + { + if (!isRenderOnce()) + { + getErrorReporting().set("RenderPass::retriggerRenderOnce - cannot retrigger rendering of render pass that does not have render once flag set", *this); + return false; + } + + getIScene().retriggerRenderPassRenderOnce(m_renderPassHandle); + return true; + } +} diff --git a/src/client/impl/RenderPassImpl.h b/src/client/impl/RenderPassImpl.h new file mode 100644 index 000000000..325864455 --- /dev/null +++ b/src/client/impl/RenderPassImpl.h @@ -0,0 +1,90 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "SceneObjectImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "impl/DataTypesImpl.h" + +#include +#include + +namespace ramses +{ + class Camera; + class RenderPass; + class RenderTarget; +} + +namespace ramses::internal +{ + class IScene; + class CameraImpl; + class CameraNodeImpl; + class RenderGroupImpl; + class RenderTargetImpl; + + using RenderGroupVector = std::vector; + + class RenderPassImpl final : public SceneObjectImpl + { + public: + RenderPassImpl(SceneImpl& scene, std::string_view renderpassName); + ~RenderPassImpl() override; + + void initializeFrameworkData(); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + bool setCamera(const CameraNodeImpl& cameraImpl); + [[nodiscard]] const ramses::Camera* getCamera()const; + [[nodiscard]] ramses::Camera* getCamera(); + + bool setClearColor(const glm::vec4& clearColor); + [[nodiscard]] const glm::vec4& getClearColor() const; + + bool setClearFlags(ClearFlags clearFlags); + [[nodiscard]] ClearFlags getClearFlags() const; + + bool addRenderGroup(const RenderGroupImpl& renderGroup, int32_t orderWithinPass); + bool remove(const RenderGroupImpl& renderGroup); + void removeIfContained(const RenderGroupImpl& renderGroup); + [[nodiscard]] bool contains(const RenderGroupImpl& renderGroup) const; + bool getRenderGroupOrder(const RenderGroupImpl& renderGroup, int32_t& orderWithinPass) const; + [[nodiscard]] const RenderGroupVector& getAllRenderGroups() const; + bool removeAllRenderGroups(); + + bool setRenderTarget(RenderTargetImpl* renderTarget); + [[nodiscard]] const ramses::RenderTarget* getRenderTarget() const; + + bool setRenderOrder(int32_t renderOrder); + [[nodiscard]] int32_t getRenderOrder() const; + + bool setEnabled(bool isEnabeld); + [[nodiscard]] bool isEnabled() const; + + bool setRenderOnce(bool enable); + [[nodiscard]] bool isRenderOnce() const; + bool retriggerRenderOnce(); + + [[nodiscard]] RenderPassHandle getRenderPassHandle() const; + + private: + void removeInternal(RenderGroupVector::iterator iter); + + RenderPassHandle m_renderPassHandle; + const CameraNodeImpl* m_cameraImpl; + const RenderTargetImpl* m_renderTargetImpl; + + RenderGroupVector m_renderGroups; + }; +} diff --git a/client/ramses-client-api/RenderTarget.cpp b/src/client/impl/RenderTarget.cpp similarity index 63% rename from client/ramses-client-api/RenderTarget.cpp rename to src/client/impl/RenderTarget.cpp index ade9e292e..cca9e28ec 100644 --- a/client/ramses-client-api/RenderTarget.cpp +++ b/src/client/impl/RenderTarget.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/RenderTarget.h" +#include "ramses/client/RenderTarget.h" // internal -#include "RenderTargetImpl.h" +#include "impl/RenderTargetImpl.h" namespace ramses { - RenderTarget::RenderTarget(std::unique_ptr impl) + RenderTarget::RenderTarget(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } @@ -29,4 +29,14 @@ namespace ramses { return m_impl.getHeight(); } + + internal::RenderTargetImpl& RenderTarget::impl() + { + return m_impl; + } + + const internal::RenderTargetImpl& RenderTarget::impl() const + { + return m_impl; + } } diff --git a/src/client/impl/RenderTargetDescription.cpp b/src/client/impl/RenderTargetDescription.cpp new file mode 100644 index 000000000..8f3d5ddd7 --- /dev/null +++ b/src/client/impl/RenderTargetDescription.cpp @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/framework/ValidationReport.h" +#include "impl/APILoggingMacros.h" + +// internal +#include "impl/RenderTargetDescriptionImpl.h" + +namespace ramses +{ + RenderTargetDescription::RenderTargetDescription() + : m_impl{ std::make_unique() } + { + } + + RenderTargetDescription::~RenderTargetDescription() = default; + + RenderTargetDescription::RenderTargetDescription(const RenderTargetDescription& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + RenderTargetDescription::RenderTargetDescription(RenderTargetDescription&& other) noexcept = default; + + RenderTargetDescription& RenderTargetDescription::operator=(const RenderTargetDescription& other) + { + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + RenderTargetDescription& RenderTargetDescription::operator=(RenderTargetDescription&& other) noexcept = default; + + void RenderTargetDescription::validate(ValidationReport& report) const + { + m_impl->validate(report.impl()); + } + + bool RenderTargetDescription::addRenderBuffer(const RenderBuffer& renderBuffer, std::string* errorMsg) + { + const auto status = m_impl->addRenderBuffer(renderBuffer.impl(), errorMsg); + LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(renderBuffer)); + return status; + } + + internal::RenderTargetDescriptionImpl& RenderTargetDescription::impl() + { + return *m_impl; + } + + const internal::RenderTargetDescriptionImpl& RenderTargetDescription::impl() const + { + return *m_impl; + } +} diff --git a/src/client/impl/RenderTargetDescriptionImpl.cpp b/src/client/impl/RenderTargetDescriptionImpl.cpp new file mode 100644 index 000000000..a8d83be25 --- /dev/null +++ b/src/client/impl/RenderTargetDescriptionImpl.cpp @@ -0,0 +1,92 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RenderTargetDescriptionImpl.h" +#include "ramses/client/SceneObjectIterator.h" +#include "impl/SceneImpl.h" +#include "impl/RenderBufferImpl.h" +#include "internal/SceneGraph/Scene/ClientScene.h" + +namespace ramses::internal +{ + RenderTargetDescriptionImpl::RenderTargetDescriptionImpl() = default; + + RenderTargetDescriptionImpl::~RenderTargetDescriptionImpl() = default; + + void RenderTargetDescriptionImpl::validate(ValidationReportImpl& report) const + { + if (m_renderBuffers.empty()) + { + report.add(EIssueType::Error, "there is no RenderBuffer added", nullptr); + } + else + { + assert(m_scene != nullptr); + for(const auto& rb : m_renderBuffers) + { + if (!m_scene->getIScene().isRenderBufferAllocated(rb)) + report.add(EIssueType::Error, "referencing one or more RenderBuffers that do not exist in scene anymore", nullptr); + } + } + } + + bool RenderTargetDescriptionImpl::addRenderBuffer(const RenderBufferImpl& renderBuffer, std::string* errorMsg) + { + auto processError = [errorMsg](std::string_view msg) { + LOG_ERROR(CONTEXT_CLIENT, msg); + if (errorMsg) + *errorMsg = msg; + return false; + }; + + if (errorMsg) + errorMsg->clear(); + + const ramses::internal::RenderBufferHandle bufferHandle = renderBuffer.getRenderBufferHandle(); + if (contains_c(m_renderBuffers, bufferHandle)) + return processError("RenderTargetDescription::addRenderBuffer failed: trying to add a render buffer that is already contained!"); + + const SceneImpl& scene = renderBuffer.getSceneImpl(); + if (m_scene != nullptr) + { + assert(!m_renderBuffers.empty()); + + if (&scene != m_scene) + return processError("RenderTargetDescription::addRenderBuffer failed: all render buffers must be from the same scene!"); + + const ramses::internal::IScene& iscene = m_scene->getIScene(); + const ramses::internal::RenderBuffer& renderBufferData = iscene.getRenderBuffer(bufferHandle); + const ramses::internal::RenderBuffer& existingRenderBuffer = iscene.getRenderBuffer(m_renderBuffers.front()); + if (renderBufferData.width != existingRenderBuffer.width || renderBufferData.height != existingRenderBuffer.height) + return processError("RenderTargetDescription::addRenderBuffer failed: all render buffers must have the same resolution!"); + + if (ramses::internal::IsDepthOrStencilFormat(renderBufferData.format)) + { + for(const auto& rb : m_renderBuffers) + { + if (ramses::internal::IsDepthOrStencilFormat(iscene.getRenderBuffer(rb).format)) + return processError("RenderTargetDescription::addRenderBuffer failed: cannot add more than one depth/stencil buffer!"); + } + } + + if (existingRenderBuffer.sampleCount != renderBufferData.sampleCount) + return processError("RenderTargetDescription::addRenderBuffer failed: all render buffers must have same MSAA sample count!"); + } + + m_renderBuffers.push_back(bufferHandle); + m_scene = &scene; + + return true; + } + + const ramses::internal::RenderBufferHandleVector& RenderTargetDescriptionImpl::getRenderBuffers() const + { + return m_renderBuffers; + } + +} diff --git a/client/ramses-client/impl/RenderTargetDescriptionImpl.h b/src/client/impl/RenderTargetDescriptionImpl.h similarity index 51% rename from client/ramses-client/impl/RenderTargetDescriptionImpl.h rename to src/client/impl/RenderTargetDescriptionImpl.h index 8cc1d1a08..c17ee6521 100644 --- a/client/ramses-client/impl/RenderTargetDescriptionImpl.h +++ b/src/client/impl/RenderTargetDescriptionImpl.h @@ -6,33 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERTARGETDESCRIPTIONIMPL_H -#define RAMSES_RENDERTARGETDESCRIPTIONIMPL_H +#pragma once -#include "StatusObjectImpl.h" -#include "SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" -namespace ramses +namespace ramses::internal { class SceneImpl; class RenderBufferImpl; + class ValidationReportImpl; - class RenderTargetDescriptionImpl : public StatusObjectImpl + class RenderTargetDescriptionImpl { public: RenderTargetDescriptionImpl(); - ~RenderTargetDescriptionImpl() override; + ~RenderTargetDescriptionImpl(); - status_t validate() const override; + void validate(ValidationReportImpl& report) const; - status_t addRenderBuffer(const RenderBufferImpl& renderBuffer); + [[nodiscard]] bool addRenderBuffer(const RenderBufferImpl& renderBuffer, std::string* errorMsg); - const ramses_internal::RenderBufferHandleVector& getRenderBuffers() const; + [[nodiscard]] const ramses::internal::RenderBufferHandleVector& getRenderBuffers() const; private: - const SceneImpl* m_scene; - ramses_internal::RenderBufferHandleVector m_renderBuffers; + const SceneImpl* m_scene{nullptr}; + ramses::internal::RenderBufferHandleVector m_renderBuffers{}; }; } - -#endif diff --git a/client/ramses-client/impl/RenderTargetImpl.cpp b/src/client/impl/RenderTargetImpl.cpp similarity index 65% rename from client/ramses-client/impl/RenderTargetImpl.cpp rename to src/client/impl/RenderTargetImpl.cpp index fdcedad0f..530e281d8 100644 --- a/client/ramses-client/impl/RenderTargetImpl.cpp +++ b/src/client/impl/RenderTargetImpl.cpp @@ -6,31 +6,29 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RenderTargetImpl.h" -#include "RenderTargetDescriptionImpl.h" -#include "Scene/ClientScene.h" +#include "impl/RenderTargetImpl.h" +#include "impl/RenderTargetDescriptionImpl.h" +#include "internal/SceneGraph/Scene/ClientScene.h" -namespace ramses +namespace ramses::internal { RenderTargetImpl::RenderTargetImpl(SceneImpl& scene, std::string_view name) : SceneObjectImpl(scene, ERamsesObjectType::RenderTarget, name) - , m_renderTargetHandle( ramses_internal::RenderTargetHandle::Invalid() ) + , m_renderTargetHandle( ramses::internal::RenderTargetHandle::Invalid() ) { } - RenderTargetImpl::~RenderTargetImpl() - { - } + RenderTargetImpl::~RenderTargetImpl() = default; void RenderTargetImpl::initializeFrameworkData(const RenderTargetDescriptionImpl& rtDesc) { assert(!m_renderTargetHandle.isValid()); assert(!rtDesc.getRenderBuffers().empty()); - m_renderTargetHandle = getIScene().allocateRenderTarget(); + m_renderTargetHandle = getIScene().allocateRenderTarget({}); assert(m_renderTargetHandle.isValid()); - const ramses_internal::RenderBufferHandleVector& rtBuffers = rtDesc.getRenderBuffers(); + const ramses::internal::RenderBufferHandleVector& rtBuffers = rtDesc.getRenderBuffers(); for(const auto& rb : rtBuffers) { getIScene().addRenderTargetRenderBuffer(m_renderTargetHandle, rb); @@ -41,7 +39,7 @@ namespace ramses { assert(m_renderTargetHandle.isValid()); getIScene().releaseRenderTarget(m_renderTargetHandle); - m_renderTargetHandle = ramses_internal::RenderTargetHandle::Invalid(); + m_renderTargetHandle = ramses::internal::RenderTargetHandle::Invalid(); } uint32_t RenderTargetImpl::getWidth() const @@ -52,7 +50,7 @@ namespace ramses assert(m_renderTargetHandle.isValid()); assert(getIScene().getRenderTargetRenderBufferCount(m_renderTargetHandle) != 0u); - const ramses_internal::RenderBufferHandle firstRenderBufferHandle = getIScene().getRenderTargetRenderBuffer(m_renderTargetHandle, 0u); + const ramses::internal::RenderBufferHandle firstRenderBufferHandle = getIScene().getRenderTargetRenderBuffer(m_renderTargetHandle, 0u); return getIScene().getRenderBuffer(firstRenderBufferHandle).width; } @@ -64,30 +62,32 @@ namespace ramses assert(m_renderTargetHandle.isValid()); assert(getIScene().getRenderTargetRenderBufferCount(m_renderTargetHandle) != 0u); - const ramses_internal::RenderBufferHandle firstRenderBufferHandle = getIScene().getRenderTargetRenderBuffer(m_renderTargetHandle, 0u); + const ramses::internal::RenderBufferHandle firstRenderBufferHandle = getIScene().getRenderTargetRenderBuffer(m_renderTargetHandle, 0u); return getIScene().getRenderBuffer(firstRenderBufferHandle).height; } - ramses_internal::RenderTargetHandle RenderTargetImpl::getRenderTargetHandle() const + ramses::internal::RenderTargetHandle RenderTargetImpl::getRenderTargetHandle() const { return m_renderTargetHandle; } - status_t RenderTargetImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool RenderTargetImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << m_renderTargetHandle; - return StatusOK; + return true; } - status_t RenderTargetImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool RenderTargetImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_renderTargetHandle; - return StatusOK; + return true; } } diff --git a/client/ramses-client/impl/RenderTargetImpl.h b/src/client/impl/RenderTargetImpl.h similarity index 52% rename from client/ramses-client/impl/RenderTargetImpl.h rename to src/client/impl/RenderTargetImpl.h index 922c942dd..bf4a9f4af 100644 --- a/client/ramses-client/impl/RenderTargetImpl.h +++ b/src/client/impl/RenderTargetImpl.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERTARGETIMPL_H -#define RAMSES_RENDERTARGETIMPL_H +#pragma once // internal #include "SceneObjectImpl.h" // ramses framework -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" #include -namespace ramses +namespace ramses::internal { class RenderTargetDescriptionImpl; @@ -27,19 +26,17 @@ namespace ramses RenderTargetImpl(SceneImpl& scene, std::string_view name); ~RenderTargetImpl() override; - void initializeFrameworkData(const RenderTargetDescriptionImpl& rtDesc); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + void initializeFrameworkData(const RenderTargetDescriptionImpl& rtDesc); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - uint32_t getWidth() const; - uint32_t getHeight() const; + [[nodiscard]] uint32_t getWidth() const; + [[nodiscard]] uint32_t getHeight() const; - ramses_internal::RenderTargetHandle getRenderTargetHandle() const; + [[nodiscard]] ramses::internal::RenderTargetHandle getRenderTargetHandle() const; private: - ramses_internal::RenderTargetHandle m_renderTargetHandle; + ramses::internal::RenderTargetHandle m_renderTargetHandle; }; } - -#endif diff --git a/client/ramses-client-api/Resource.cpp b/src/client/impl/Resource.cpp similarity index 63% rename from client/ramses-client-api/Resource.cpp rename to src/client/impl/Resource.cpp index d38af1c31..3209b9a74 100644 --- a/client/ramses-client-api/Resource.cpp +++ b/src/client/impl/Resource.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/Resource.h" +#include "ramses/client/Resource.h" // internal -#include "ResourceImpl.h" +#include "impl/ResourceImpl.h" namespace ramses { - Resource::Resource(std::unique_ptr impl) + Resource::Resource(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } @@ -24,4 +24,14 @@ namespace ramses { return m_impl.getResourceId(); } + + internal::ResourceImpl& Resource::impl() + { + return m_impl; + } + + const internal::ResourceImpl& Resource::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/ResourceImpl.cpp b/src/client/impl/ResourceImpl.cpp similarity index 59% rename from client/ramses-client/impl/ResourceImpl.cpp rename to src/client/impl/ResourceImpl.cpp index da36203db..73d3457fb 100644 --- a/client/ramses-client/impl/ResourceImpl.cpp +++ b/src/client/impl/ResourceImpl.cpp @@ -7,28 +7,28 @@ // ------------------------------------------------------------------------- // internal -#include "ResourceImpl.h" -#include "RamsesClientImpl.h" -#include "RamsesFrameworkTypesImpl.h" +#include "impl/ResourceImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" -#include "ramses-client-api/Resource.h" +#include "ramses/client/Resource.h" // framework -#include "ClientApplicationLogic.h" -#include "Components/ManagedResource.h" +#include "internal/ClientApplicationLogic.h" +#include "internal/Components/ManagedResource.h" -#include "Collections/StringOutputStream.h" -#include "Utils/BinaryOutputStream.h" -#include "RamsesObjectTypeUtils.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "impl/RamsesObjectTypeUtils.h" #include "city.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses +namespace ramses::internal { ResourceImpl::ResourceImpl(ERamsesObjectType type, - ramses_internal::ResourceHashUsage hashUsage, + ramses::internal::ResourceHashUsage hashUsage, SceneImpl& scene, std::string_view name) : SceneObjectImpl(scene, type, name) @@ -51,11 +51,11 @@ namespace ramses return m_resourceId; } - resourceId_t ResourceImpl::CreateResourceHash(ramses_internal::ResourceContentHash llhash, const std::string& name, ERamsesObjectType type) + resourceId_t ResourceImpl::CreateResourceHash(ramses::internal::ResourceContentHash llhash, const std::string& name, ERamsesObjectType type) { resourceId_t hash; - ramses_internal::BinaryOutputStream metaDataStream(1024); + ramses::internal::BinaryOutputStream metaDataStream(1024); metaDataStream << llhash; metaDataStream << name; metaDataStream << static_cast(type); @@ -79,49 +79,41 @@ namespace ramses { } - ramses::status_t ResourceImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool ResourceImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << getLowlevelResourceHash(); outStream << m_resourceId.highPart; outStream << m_resourceId.lowPart; - return StatusOK; + return true; } - status_t ResourceImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool ResourceImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; - ramses_internal::ResourceContentHash llhash; + ramses::internal::ResourceContentHash llhash; inStream >> llhash; m_hashUsage = getClientImpl().getHashUsage_ThreadSafe(llhash); inStream >> m_resourceId.highPart; inStream >> m_resourceId.lowPart; - return StatusOK; + return true; } - ramses_internal::ResourceContentHash ResourceImpl::getLowlevelResourceHash() const + ramses::internal::ResourceContentHash ResourceImpl::getLowlevelResourceHash() const { return m_hashUsage.getHash(); } - status_t ResourceImpl::validate() const + bool ResourceImpl::setName(std::string_view name) { - const status_t status = SceneObjectImpl::validate(); - ramses_internal::StringOutputStream stringStream; - stringStream << "Resource ID: " << m_resourceId; - stringStream << " Resource Hash: " << m_hashUsage.getHash(); - addValidationMessage(EValidationSeverity::Info, stringStream.release()); - return status; - } - - status_t ResourceImpl::setName(RamsesObject& object, std::string_view name) - { - const status_t status = SceneObjectImpl::setName(object, name); + const auto status = SceneObjectImpl::setName(name); // name is also included in resource hash updateResourceHash(); diff --git a/src/client/impl/ResourceImpl.h b/src/client/impl/ResourceImpl.h new file mode 100644 index 000000000..c1b2fd8aa --- /dev/null +++ b/src/client/impl/ResourceImpl.h @@ -0,0 +1,55 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +// internal +#include "impl/SceneImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/ResourceHashUsage.h" + +#include +#include + +namespace ramses::internal +{ + class ClientApplicationLogic; +} + +namespace ramses::internal +{ + class ResourceImpl : public SceneObjectImpl + { + public: + ResourceImpl(ERamsesObjectType type, + ramses::internal::ResourceHashUsage hashUsage, + SceneImpl& scene, + std::string_view name); + ~ResourceImpl() override; + + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + + [[nodiscard]] resourceId_t getResourceId() const; + [[nodiscard]] ramses::internal::ResourceContentHash getLowlevelResourceHash() const; + + bool setName(std::string_view name) override; + + [[nodiscard]] static resourceId_t CreateResourceHash(ramses::internal::ResourceContentHash llhash, const std::string& name, ERamsesObjectType type); + + private: + void updateResourceHash(); + + ramses::internal::ResourceHashUsage m_hashUsage; + resourceId_t m_resourceId; + }; +} diff --git a/client/ramses-client/impl/ResourceObjects.h b/src/client/impl/ResourceObjects.h similarity index 84% rename from client/ramses-client/impl/ResourceObjects.h rename to src/client/impl/ResourceObjects.h index 2f560f117..962920545 100644 --- a/client/ramses-client/impl/ResourceObjects.h +++ b/src/client/impl/ResourceObjects.h @@ -6,15 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEOBJECTS_H -#define RAMSES_RESOURCEOBJECTS_H +#pragma once -#include "Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" namespace ramses { class Resource; using ResourceObjects = std::vector; } - -#endif diff --git a/client/logic/lib/impl/SaveFileConfig.cpp b/src/client/impl/SaveFileConfig.cpp similarity index 81% rename from client/logic/lib/impl/SaveFileConfig.cpp rename to src/client/impl/SaveFileConfig.cpp index 282e2778c..cf8669ab3 100644 --- a/client/logic/lib/impl/SaveFileConfig.cpp +++ b/src/client/impl/SaveFileConfig.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/SaveFileConfig.h" +#include "ramses/client/SaveFileConfig.h" #include "impl/SaveFileConfigImpl.h" @@ -52,4 +52,19 @@ namespace ramses { m_impl->setLuaSavingMode(mode); } + + void SaveFileConfig::setCompressionEnabled(bool compressionEnabled) + { + m_impl->setCompressionEnabled(compressionEnabled); + } + + internal::SaveFileConfigImpl& SaveFileConfig::impl() + { + return *m_impl; + } + + const internal::SaveFileConfigImpl& SaveFileConfig::impl() const + { + return *m_impl; + } } diff --git a/client/logic/lib/impl/SaveFileConfigImpl.cpp b/src/client/impl/SaveFileConfigImpl.cpp similarity index 65% rename from client/logic/lib/impl/SaveFileConfigImpl.cpp rename to src/client/impl/SaveFileConfigImpl.cpp index a39f202ec..65e878e42 100644 --- a/client/logic/lib/impl/SaveFileConfigImpl.cpp +++ b/src/client/impl/SaveFileConfigImpl.cpp @@ -7,7 +7,7 @@ // ------------------------------------------------------------------------- #include "impl/SaveFileConfigImpl.h" -#include "impl/LoggerImpl.h" +#include "internal/Core/Utils/LogMacros.h" namespace ramses::internal { @@ -18,17 +18,17 @@ namespace ramses::internal void SaveFileConfigImpl::setExporterVersion(uint32_t major, uint32_t minor, uint32_t patch, uint32_t fileFormatVersion) { - m_exporterMajorVersion = major; - m_exporterMinorVersion = minor; - m_exporterPatchVersion = patch; - m_exporterFileFormatVersion = fileFormatVersion; + m_exporterVersion.major = major; + m_exporterVersion.minor = minor; + m_exporterVersion.patch = patch; + m_exporterVersion.fileFormat = fileFormatVersion; } void SaveFileConfigImpl::setValidationEnabled(bool validationEnabled) { if (validationEnabled == false) { - LOG_INFO("Validation before saving was disabled during save*() calls! Possible content issues will not yield further warnings."); + LOG_INFO_P(CONTEXT_CLIENT, "Validation before saving was disabled during save*() calls! Possible content issues will not yield further warnings."); } m_validationEnabled = validationEnabled; } @@ -38,24 +38,9 @@ namespace ramses::internal return m_validationEnabled; } - uint32_t SaveFileConfigImpl::getExporterMajorVersion() const + const SaveFileConfigImpl::ExporterVersion& SaveFileConfigImpl::getExporterVersion() const { - return m_exporterMajorVersion; - } - - uint32_t SaveFileConfigImpl::getExporterMinorVersion() const - { - return m_exporterMinorVersion; - } - - uint32_t SaveFileConfigImpl::getExporterPatchVersion() const - { - return m_exporterPatchVersion; - } - - uint32_t SaveFileConfigImpl::getExporterFileFormatVersion() const - { - return m_exporterFileFormatVersion; + return m_exporterVersion; } const std::string& SaveFileConfigImpl::getMetadataString() const @@ -72,4 +57,14 @@ namespace ramses::internal { return m_luaSavingMode; } + + void SaveFileConfigImpl::setCompressionEnabled(bool compressionEnabled) + { + m_compressionEnabled = compressionEnabled; + } + + bool SaveFileConfigImpl::getCompressionEnabled() const + { + return m_compressionEnabled; + } } diff --git a/src/client/impl/SaveFileConfigImpl.h b/src/client/impl/SaveFileConfigImpl.h new file mode 100644 index 000000000..f2bc9e6c2 --- /dev/null +++ b/src/client/impl/SaveFileConfigImpl.h @@ -0,0 +1,85 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include "ramses/client/logic/ELuaSavingMode.h" +#include "internal/PlatformAbstraction/FmtBase.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" + +namespace ramses +{ + class LuaModule; +} + +namespace ramses::internal +{ + class SaveFileConfigImpl + { + public: + void setMetadataString(std::string_view metadata); + void setExporterVersion(uint32_t major, uint32_t minor, uint32_t patch, uint32_t fileFormatVersion); + void setValidationEnabled(bool validationEnabled); + void setLuaSavingMode(ELuaSavingMode mode); + void setCompressionEnabled(bool compressionEnabled); + + struct ExporterVersion + { + uint32_t major = 0u; + uint32_t minor = 0u; + uint32_t patch = 0u; + uint32_t fileFormat = 0u; + }; + + [[nodiscard]] const std::string& getMetadataString() const; + [[nodiscard]] const ExporterVersion& getExporterVersion() const; + [[nodiscard]] bool getValidationEnabled() const; + [[nodiscard]] ELuaSavingMode getLuaSavingMode() const; + [[nodiscard]] bool getCompressionEnabled() const; + + private: + std::string m_metadata; + ExporterVersion m_exporterVersion; + bool m_validationEnabled = true; + bool m_compressionEnabled = false; + ELuaSavingMode m_luaSavingMode = ELuaSavingMode::SourceAndByteCode; + }; + + inline IOutputStream& operator<<(IOutputStream& os, const SaveFileConfigImpl::ExporterVersion& exporter) + { + os << exporter.major << exporter.minor << exporter.patch << exporter.fileFormat; + return os; + } + + inline IInputStream& operator>>(IInputStream& is, SaveFileConfigImpl::ExporterVersion& exporter) + { + is >> exporter.major >> exporter.minor >> exporter.patch >> exporter.fileFormat; + return is; + } +} + +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase +{ + template constexpr auto format(const ramses::internal::SaveFileConfigImpl& c, FormatContext& ctx) + { + const auto& exporter = c.getExporterVersion(); + return fmt::format_to(ctx.out(), "'{}' exporter:{}.{}.{}.{} validate:{} compress:{} lua:{}", + c.getMetadataString(), + exporter.major, + exporter.minor, + exporter.patch, + exporter.fileFormat, + c.getValidationEnabled(), + c.getCompressionEnabled(), + c.getLuaSavingMode() + ); + } +}; diff --git a/client/ramses-client-api/Scene.cpp b/src/client/impl/Scene.cpp similarity index 52% rename from client/ramses-client-api/Scene.cpp rename to src/client/impl/Scene.cpp index dc51e18a5..73a81f27a 100644 --- a/client/ramses-client-api/Scene.cpp +++ b/src/client/impl/Scene.cpp @@ -7,45 +7,48 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/ArrayResource.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Camera.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Node.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/Effect.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicObject.h" // internal -#include "SceneImpl.h" -#include "EffectImpl.h" -#include "TextureSamplerImpl.h" -#include "RamsesFrameworkTypesImpl.h" -#include "RamsesClientTypesImpl.h" +#include "impl/SceneImpl.h" +#include "impl/EffectImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/SaveFileConfigImpl.h" +#include "impl/RamsesClientTypesImpl.h" namespace ramses { - Scene::Scene(std::unique_ptr impl) + Scene::Scene(std::unique_ptr impl) : ClientObject{ std::move(impl) } - , m_impl{ static_cast(ClientObject::m_impl) } + , m_impl{ static_cast(ClientObject::m_impl) } { } @@ -70,9 +73,9 @@ namespace ramses return appearance; } - GeometryBinding* Scene::createGeometryBinding(const Effect& effect, std::string_view name) + Geometry* Scene::createGeometry(const Effect& effect, std::string_view name) { - GeometryBinding* geomBinding = m_impl.createGeometryBinding(effect, name); + Geometry* geomBinding = m_impl.createGeometry(effect, name); LOG_HL_CLIENT_API2(LOG_API_RAMSESOBJECT_PTR_STRING(geomBinding), LOG_API_RAMSESOBJECT_STRING(effect), name); return geomBinding; } @@ -91,16 +94,16 @@ namespace ramses return meshNode; } - status_t Scene::publish(EScenePublicationMode publicationMode) + bool Scene::publish(EScenePublicationMode publicationMode) { - const status_t status = m_impl.publish(publicationMode); + const bool status = m_impl.publish(publicationMode); LOG_HL_CLIENT_API1(status, publicationMode); return status; } - status_t Scene::unpublish() + bool Scene::unpublish() { - const status_t status = m_impl.unpublish(); + const bool status = m_impl.unpublish(); LOG_HL_CLIENT_API_NOARG(status); return status; } @@ -115,35 +118,42 @@ namespace ramses return m_impl.getSceneId(); } - status_t Scene::saveToFile(std::string_view fileName, bool compress) const + bool Scene::saveToFile(std::string_view fileName, const SaveFileConfig& config) const { - const auto status = m_impl.saveToFile(fileName, compress); - LOG_HL_CLIENT_API2(status, fileName, compress); + const auto status = m_impl.saveToFile(fileName, config.impl()); + LOG_HL_CLIENT_API2(status, fileName, config.impl()); return status; } - status_t Scene::destroy(SceneObject& object) + LogicEngine* Scene::createLogicEngine(std::string_view name) { - const status_t status = m_impl.destroy(object); + auto logic = m_impl.createLogicEngine(name); + LOG_HL_CLIENT_API1(LOG_API_RAMSESOBJECT_PTR_STRING(logic), name); + return logic; + } + + bool Scene::destroy(SceneObject& object) + { + const bool status = m_impl.destroy(object); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(object)); return status; } - status_t Scene::setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds) + bool Scene::setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds) { - const status_t status = m_impl.setExpirationTimestamp(ptpExpirationTimestampInMilliseconds); + const bool status = m_impl.setExpirationTimestamp(ptpExpirationTimestampInMilliseconds); LOG_HL_CLIENT_API1(status, ptpExpirationTimestampInMilliseconds); return status; } - status_t Scene::flush(sceneVersionTag_t sceneVersionTag) + bool Scene::flush(sceneVersionTag_t sceneVersionTag) { - const status_t status = m_impl.flush(sceneVersionTag); + const bool status = m_impl.flush(sceneVersionTag); LOG_HL_CLIENT_API2(status, sceneVersionTag, m_impl.getSceneId()); return status; } - status_t Scene::resetUniformTimeMs() + bool Scene::resetUniformTimeMs() { const auto status = m_impl.resetUniformTimeMs(); LOG_HL_CLIENT_API_NOARG(status); @@ -176,28 +186,28 @@ namespace ramses return sceneReference; } - status_t Scene::linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId) + bool Scene::linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId) { auto status = m_impl.linkData(providerReference, providerId, consumerReference, consumerId); LOG_HL_CLIENT_API4(status, LOG_API_RAMSESOBJECT_PTR_STRING(providerReference), providerId, LOG_API_RAMSESOBJECT_PTR_STRING(consumerReference), consumerId); return status; } - ramses::status_t Scene::unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId) + bool Scene::unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId) { auto status = m_impl.unlinkData(consumerReference, consumerId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_PTR_STRING(consumerReference), consumerId); return status; } - const RamsesObject* Scene::findObjectByName(std::string_view name) const + template T* Scene::findObjectByNameInternal(std::string_view name) { - return m_impl.findObjectByName(name); + return m_impl.findObjectByName(name); } - RamsesObject* Scene::findObjectByName(std::string_view name) + template const T* Scene::findObjectByNameInternal(std::string_view name) const { - return m_impl.findObjectByName(name); + return m_impl.findObjectByName(name); } const SceneObject* Scene::findObjectById(sceneObjectId_t id) const @@ -238,16 +248,16 @@ namespace ramses return pickableObject; } - RenderBuffer* Scene::createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferType bufferType, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount, std::string_view name) + RenderBuffer* Scene::createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount, std::string_view name) { - RenderBuffer* renderBuffer = m_impl.createRenderBuffer(width, height, bufferType, bufferFormat, accessMode, sampleCount, name); - LOG_HL_CLIENT_API7(LOG_API_RAMSESOBJECT_PTR_STRING(renderBuffer), width, height, bufferType, bufferFormat, accessMode, sampleCount, name); + RenderBuffer* renderBuffer = m_impl.createRenderBuffer(width, height, bufferFormat, accessMode, sampleCount, name); + LOG_HL_CLIENT_API6(LOG_API_RAMSESOBJECT_PTR_STRING(renderBuffer), width, height, bufferFormat, accessMode, sampleCount, name); return renderBuffer; } RenderTarget* Scene::createRenderTarget(const RenderTargetDescription& rtDesc, std::string_view name) { - RenderTarget* renderTarget = m_impl.createRenderTarget(rtDesc.m_impl, name); + RenderTarget* renderTarget = m_impl.createRenderTarget(rtDesc.impl(), name); LOG_HL_CLIENT_API2(LOG_API_RAMSESOBJECT_PTR_STRING(renderTarget), LOG_API_GENERIC_OBJECT_STRING(rtDesc), name); return renderTarget; } @@ -336,65 +346,65 @@ namespace ramses return texSampler; } - status_t Scene::createTransformationDataProvider(const Node& node, dataProviderId_t dataId) + bool Scene::createTransformationDataProvider(const Node& node, dataProviderId_t dataId) { - status_t status = m_impl.createTransformationDataProvider(node, dataId); + bool status = m_impl.createTransformationDataProvider(node, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(node), dataId); return status; } - status_t Scene::createTransformationDataConsumer(const Node& node, dataConsumerId_t dataId) + bool Scene::createTransformationDataConsumer(const Node& node, dataConsumerId_t dataId) { - status_t status = m_impl.createTransformationDataConsumer(node, dataId); + bool status = m_impl.createTransformationDataConsumer(node, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(node), dataId); return status; } - status_t Scene::createDataProvider(const DataObject& dataObject, dataProviderId_t dataId) + bool Scene::createDataProvider(const DataObject& dataObject, dataProviderId_t dataId) { - status_t status = m_impl.createDataProvider(dataObject, dataId); + bool status = m_impl.createDataProvider(dataObject, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(dataObject), dataId); return status; } - status_t Scene::createDataConsumer(const DataObject& dataObject, dataConsumerId_t dataId) + bool Scene::createDataConsumer(const DataObject& dataObject, dataConsumerId_t dataId) { - status_t status = m_impl.createDataConsumer(dataObject, dataId); + bool status = m_impl.createDataConsumer(dataObject, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(dataObject), dataId); return status; } - status_t Scene::createTextureProvider(const Texture2D& texture, dataProviderId_t dataId) + bool Scene::createTextureProvider(const Texture2D& texture, dataProviderId_t dataId) { - status_t status = m_impl.createTextureProvider(texture, dataId); + bool status = m_impl.createTextureProvider(texture, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(texture), dataId); return status; } - status_t Scene::updateTextureProvider(const Texture2D& texture, dataProviderId_t dataId) + bool Scene::updateTextureProvider(const Texture2D& texture, dataProviderId_t dataId) { - status_t status = m_impl.updateTextureProvider(texture, dataId); + bool status = m_impl.updateTextureProvider(texture, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(texture), dataId); return status; } - status_t Scene::createTextureConsumer(const TextureSampler& sampler, dataConsumerId_t dataId) + bool Scene::createTextureConsumer(const TextureSampler& sampler, dataConsumerId_t dataId) { - status_t status = m_impl.createTextureConsumer(sampler, dataId); + bool status = m_impl.createTextureConsumer(sampler, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(sampler), dataId); return status; } - status_t Scene::createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t dataId) + bool Scene::createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t dataId) { - status_t status = m_impl.createTextureConsumer(sampler, dataId); + bool status = m_impl.createTextureConsumer(sampler, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(sampler), dataId); return status; } - status_t Scene::createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t dataId) + bool Scene::createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t dataId) { - status_t status = m_impl.createTextureConsumer(sampler, dataId); + bool status = m_impl.createTextureConsumer(sampler, dataId); LOG_HL_CLIENT_API2(status, LOG_API_RAMSESOBJECT_STRING(sampler), dataId); return status; } @@ -412,41 +422,41 @@ namespace ramses } template - ArrayResource* Scene::createArrayResourceInternal(uint32_t numElements, const T* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name) + ArrayResource* Scene::createArrayResourceInternal(uint32_t numElements, const T* arrayData, std::string_view name) { - auto arr = m_impl.createArrayResource(numElements, arrayData, cacheFlag, name); - LOG_HL_CLIENT_API5(LOG_API_RESOURCE_PTR_STRING(arr), numElements, GetEDataType(), LOG_API_GENERIC_PTR_STRING(arrayData), cacheFlag, name); + auto arr = m_impl.createArrayResource(numElements, arrayData, name); + LOG_HL_CLIENT_API4(LOG_API_RESOURCE_PTR_STRING(arr), numElements, GetEDataType(), LOG_API_GENERIC_PTR_STRING(arrayData), name); return arr; } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture2D* Scene::createTexture2D(ETextureFormat format, uint32_t width, uint32_t height, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name /* = {} */) + Texture2D* Scene::createTexture2D(ETextureFormat format, uint32_t width, uint32_t height, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name /* = {} */) { - Texture2D* tex = m_impl.createTexture2D(width, height, format, mipMapCount, mipLevelData, generateMipChain, swizzle, cacheFlag, name); - LOG_HL_CLIENT_API9(LOG_API_RESOURCE_PTR_STRING(tex), width, height, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, swizzle, cacheFlag, name); + Texture2D* tex = m_impl.createTexture2D(width, height, format, mipMapCount, mipLevelData, generateMipChain, swizzle, name); + LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), width, height, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, swizzle, name); return tex; } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture3D* Scene::createTexture3D(ETextureFormat format, uint32_t width, uint32_t height, uint32_t depth, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, resourceCacheFlag_t cacheFlag, std::string_view name /* = {} */) + Texture3D* Scene::createTexture3D(ETextureFormat format, uint32_t width, uint32_t height, uint32_t depth, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, std::string_view name /* = {} */) { - Texture3D* tex = m_impl.createTexture3D(width, height, depth, format, mipMapCount, mipLevelData, generateMipChain, cacheFlag, name); - LOG_HL_CLIENT_API9(LOG_API_RESOURCE_PTR_STRING(tex), width, height, depth, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, cacheFlag, name); + Texture3D* tex = m_impl.createTexture3D(width, height, depth, format, mipMapCount, mipLevelData, generateMipChain, name); + LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), width, height, depth, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, name); return tex; } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - TextureCube* Scene::createTextureCube(ETextureFormat format, uint32_t size, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name /* = {} */) + TextureCube* Scene::createTextureCube(ETextureFormat format, uint32_t size, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name /* = {} */) { - TextureCube* tex = m_impl.createTextureCube(size, format, mipMapCount, mipLevelData, generateMipChain, swizzle, cacheFlag, name); - LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), size, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, swizzle, cacheFlag, name); + TextureCube* tex = m_impl.createTextureCube(size, format, mipMapCount, mipLevelData, generateMipChain, swizzle, name); + LOG_HL_CLIENT_API7(LOG_API_RESOURCE_PTR_STRING(tex), size, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, swizzle, name); return tex; } - Effect* Scene::createEffect(const EffectDescription& effectDesc, resourceCacheFlag_t cacheFlag, std::string_view name) + Effect* Scene::createEffect(const EffectDescription& effectDesc, std::string_view name) { - Effect* effect = m_impl.createEffect(effectDesc, cacheFlag, name); - LOG_HL_CLIENT_API3(LOG_API_RESOURCE_PTR_STRING(effect), LOG_API_GENERIC_OBJECT_STRING(effectDesc), cacheFlag, name); + Effect* effect = m_impl.createEffect(effectDesc, name); + LOG_HL_CLIENT_API2(LOG_API_RESOURCE_PTR_STRING(effect), LOG_API_GENERIC_OBJECT_STRING(effectDesc), name); return effect; } @@ -465,11 +475,81 @@ namespace ramses return m_impl.getResource(id); } - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const uint16_t*, resourceCacheFlag_t, std::string_view); - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const uint32_t*, resourceCacheFlag_t, std::string_view); - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const float*, resourceCacheFlag_t, std::string_view); - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const vec2f*, resourceCacheFlag_t, std::string_view); - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const vec3f*, resourceCacheFlag_t, std::string_view); - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const vec4f*, resourceCacheFlag_t, std::string_view); - template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const Byte*, resourceCacheFlag_t, std::string_view); + internal::SceneImpl& Scene::impl() + { + return m_impl; + } + + const internal::SceneImpl& Scene::impl() const + { + return m_impl; + } + + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const uint16_t*, std::string_view); + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const uint32_t*, std::string_view); + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const float*, std::string_view); + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const vec2f*, std::string_view); + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const vec3f*, std::string_view); + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const vec4f*, std::string_view); + template RAMSES_API ArrayResource* Scene::createArrayResourceInternal(uint32_t, const std::byte*, std::string_view); + + template RAMSES_API SceneObject* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API LogicEngine* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API LogicObject* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Node* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API MeshNode* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Camera* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API PerspectiveCamera* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API OrthographicCamera* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Effect* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Appearance* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Geometry* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API PickableObject* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Resource* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Texture2D* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Texture3D* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API TextureCube* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API ArrayResource* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API RenderGroup* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API RenderPass* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API BlitPass* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API TextureSampler* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API TextureSamplerMS* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API RenderBuffer* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API RenderTarget* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API ArrayBuffer* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API Texture2DBuffer* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API DataObject* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API SceneReference* Scene::findObjectByNameInternal(std::string_view); + template RAMSES_API TextureSamplerExternal* Scene::findObjectByNameInternal(std::string_view); + + template RAMSES_API const SceneObject* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const LogicEngine* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const LogicObject* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Node* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const MeshNode* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Camera* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const PerspectiveCamera* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const OrthographicCamera* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Effect* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Appearance* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Geometry* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const PickableObject* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Resource* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Texture2D* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Texture3D* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const TextureCube* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const ArrayResource* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const RenderGroup* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const RenderPass* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const BlitPass* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const TextureSampler* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const TextureSamplerMS* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const RenderBuffer* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const RenderTarget* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const ArrayBuffer* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const Texture2DBuffer* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const DataObject* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const SceneReference* Scene::findObjectByNameInternal(std::string_view) const; + template RAMSES_API const TextureSamplerExternal* Scene::findObjectByNameInternal(std::string_view) const; } diff --git a/src/client/impl/SceneConfig.cpp b/src/client/impl/SceneConfig.cpp new file mode 100644 index 000000000..bd469d234 --- /dev/null +++ b/src/client/impl/SceneConfig.cpp @@ -0,0 +1,74 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/client/SceneConfig.h" + +// internal +#include "impl/SceneConfigImpl.h" +#include "impl/APILoggingMacros.h" + +namespace ramses +{ + SceneConfig::SceneConfig() + : m_impl{ std::make_unique() } + { + } + + SceneConfig::SceneConfig(sceneId_t sceneId, EScenePublicationMode publicationMode) + : m_impl{ std::make_unique() } + { + m_impl->setSceneId(sceneId); + m_impl->setPublicationMode(publicationMode); + } + + SceneConfig::~SceneConfig() = default; + + SceneConfig::SceneConfig(const SceneConfig& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + SceneConfig::SceneConfig(SceneConfig&& other) noexcept = default; + + SceneConfig& SceneConfig::operator=(const SceneConfig& other) + { + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + SceneConfig& SceneConfig::operator=(SceneConfig&& other) noexcept = default; + + internal::SceneConfigImpl& SceneConfig::impl() + { + return *m_impl; + } + + const internal::SceneConfigImpl& SceneConfig::impl() const + { + return *m_impl; + } + + void SceneConfig::setPublicationMode(EScenePublicationMode publicationMode) + { + m_impl->setPublicationMode(publicationMode); + LOG_HL_CLIENT_API1(true, publicationMode); + } + + void SceneConfig::setSceneId(sceneId_t sceneId) + { + m_impl->setSceneId(sceneId); + LOG_HL_CLIENT_API1(true, sceneId.getValue()); + } + + void SceneConfig::setMemoryVerificationEnabled(bool enabled) + { + m_impl->setMemoryVerificationEnabled(enabled); + LOG_HL_CLIENT_API1(true, enabled); + } +} diff --git a/src/client/impl/SceneConfigImpl.cpp b/src/client/impl/SceneConfigImpl.cpp new file mode 100644 index 000000000..edd76efc4 --- /dev/null +++ b/src/client/impl/SceneConfigImpl.cpp @@ -0,0 +1,42 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/SceneConfigImpl.h" + +namespace ramses::internal +{ + void SceneConfigImpl::setPublicationMode(EScenePublicationMode publicationMode) + { + m_publicationMode = publicationMode; + } + + EScenePublicationMode SceneConfigImpl::getPublicationMode() const + { + return m_publicationMode; + } + + void SceneConfigImpl::setSceneId(sceneId_t sceneId) + { + m_sceneId = sceneId; + } + + sceneId_t SceneConfigImpl::getSceneId() const + { + return m_sceneId; + } + + void SceneConfigImpl::setMemoryVerificationEnabled(bool enabled) + { + m_memoryVerificationEnabled = enabled; + } + + bool SceneConfigImpl::getMemoryVerificationEnabled() const + { + return m_memoryVerificationEnabled; + } +} diff --git a/src/client/impl/SceneConfigImpl.h b/src/client/impl/SceneConfigImpl.h new file mode 100644 index 000000000..b115ba70d --- /dev/null +++ b/src/client/impl/SceneConfigImpl.h @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/EScenePublicationMode.h" + +namespace ramses::internal +{ + class SceneConfigImpl + { + public: + void setPublicationMode(EScenePublicationMode publicationMode); + void setMemoryVerificationEnabled(bool enabled); + void setSceneId(sceneId_t sceneId); + + [[nodiscard]] EScenePublicationMode getPublicationMode() const; + [[nodiscard]] bool getMemoryVerificationEnabled() const; + [[nodiscard]] sceneId_t getSceneId() const; + + private: + EScenePublicationMode m_publicationMode = EScenePublicationMode::LocalOnly; + sceneId_t m_sceneId; + bool m_memoryVerificationEnabled = true; + }; +} diff --git a/client/ramses-client/impl/SceneDumper.cpp b/src/client/impl/SceneDumper.cpp similarity index 66% rename from client/ramses-client/impl/SceneDumper.cpp rename to src/client/impl/SceneDumper.cpp index 2868c0ed7..729766684 100644 --- a/client/ramses-client/impl/SceneDumper.cpp +++ b/src/client/impl/SceneDumper.cpp @@ -6,90 +6,90 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneDumper.h" - -#include "ramses-client-api/RamsesObjectTypes.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/SceneObject.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Resource.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Camera.h" - -#include "Texture2DBufferImpl.h" -#include "GeometryBindingImpl.h" -#include "Texture2DBufferImpl.h" +#include "impl/SceneDumper.h" + +#include "ramses/framework/RamsesObjectTypes.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/SceneObject.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Resource.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Camera.h" + +#include "impl/Texture2DBufferImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/Texture2DBufferImpl.h" #include "ObjectIteratorImpl.h" -#include "RamsesClientImpl.h" -#include "RenderBufferImpl.h" -#include "RenderTargetImpl.h" -#include "RenderGroupImpl.h" -#include "AppearanceImpl.h" -#include "RenderPassImpl.h" -#include "CameraNodeImpl.h" -#include "MeshNodeImpl.h" -#include "ResourceImpl.h" -#include "BlitPassImpl.h" -#include "EffectImpl.h" -#include "SceneImpl.h" - -#include "Scene/ClientScene.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/BlitPass.h" - -namespace ramses +#include "impl/RamsesClientImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/AppearanceImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/ResourceImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/EffectImpl.h" +#include "impl/SceneImpl.h" + +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" + +namespace ramses::internal { template <> - ramses_internal::TextureSamplerHandle SceneDumper::GetHandle(const TextureSamplerImpl& impl) + ramses::internal::TextureSamplerHandle SceneDumper::GetHandle(const TextureSamplerImpl& impl) { return impl.getTextureSamplerHandle(); } - template <> ramses_internal::RenderBufferHandle SceneDumper::GetHandle(const RenderBufferImpl& impl) + template <> ramses::internal::RenderBufferHandle SceneDumper::GetHandle(const RenderBufferImpl& impl) { return impl.getRenderBufferHandle(); } - template <> ramses_internal::TextureBufferHandle SceneDumper::GetHandle(const Texture2DBufferImpl& impl) + template <> ramses::internal::TextureBufferHandle SceneDumper::GetHandle(const Texture2DBufferImpl& impl) { return impl.getTextureBufferHandle(); } template - void SceneDumper::setupMap(ramses_internal::HashMap& map) + void SceneDumper::setupMap(ramses::internal::HashMap& map) { map.clear(); addToMap(map); } template - void SceneDumper::addToMap(ramses_internal::HashMap& map) + void SceneDumper::addToMap(ramses::internal::HashMap& map) { - RamsesObjectRegistryIterator iterator(m_objectRegistry, TYPE_ID_OF_RAMSES_OBJECT::ID); - while (const ObjectType* object = iterator.getNext()) + SceneObjectRegistryIterator iterator(m_objectRegistry, TYPE_ID_OF_RAMSES_OBJECT::ID); + while (const auto* object = iterator.getNext()) { - map.put(GetHandle(object->m_impl), &object->m_impl); + map.put(GetHandle(object->impl()), &object->impl()); } } - SceneDumper::SceneDumper(const ramses::SceneImpl& scene) + SceneDumper::SceneDumper(const SceneImpl& scene) : m_client(scene.getClientImpl()) , m_objectRegistry(scene.getObjectRegistry()) { } - void SceneDumper::dumpUnrequiredObjects(ramses_internal::StringOutputStream& output) + void SceneDumper::dumpUnrequiredObjects(ramses::internal::StringOutputStream& output) { setupMaps(); @@ -107,8 +107,8 @@ namespace ramses TextureSamplerSet requiredTextureSamplers = markRequiredTextureSampler(requiredAppearances); markRequiredEffects(requiredAppearances); markRequiredTextures(requiredTextureSamplers); - GeometryBindingSet requiredGeometryBindings = markRequiredGeometryBindings(requiredMeshNodes); - markRequiredVertexAndIndexBuffers(requiredGeometryBindings); + GeometrySet requiredGeometries = markRequiredGeometries(requiredMeshNodes); + markRequiredVertexAndIndexBuffers(requiredGeometries); markRequiredRenderTargets(requiredRenderPasses); RenderBufferSet requiredRenderBuffers = markRequiredRenderBuffer(requiredTextureSamplers); markRequiredTextureBuffer(requiredTextureSamplers); @@ -126,11 +126,11 @@ namespace ramses void SceneDumper::setupMaps() { - setupMap(m_textureSamplerHandleToObjectMap); - addToMap(m_textureSamplerHandleToObjectMap); - addToMap(m_textureSamplerHandleToObjectMap); - setupMap(m_renderBufferHandleToObjectMap); - setupMap(m_textureBufferHandleToObjectMap); + setupMap(m_textureSamplerHandleToObjectMap); + addToMap(m_textureSamplerHandleToObjectMap); + addToMap(m_textureSamplerHandleToObjectMap); + setupMap(m_renderBufferHandleToObjectMap); + setupMap(m_textureBufferHandleToObjectMap); setupResourceMap(); setupRenderPassMap(); @@ -145,7 +145,7 @@ namespace ramses auto resourceAsObject = iter.getNext(); while (resourceAsObject) { - const ResourceImpl* resource = &RamsesObjectTypeUtils::ConvertTo(*resourceAsObject).m_impl; + const ResourceImpl* resource = &RamsesObjectTypeUtils::ConvertTo(*resourceAsObject).impl(); m_resourceContentHashToObjectMap.put(resource->getLowlevelResourceHash(), resource); resourceAsObject = iter.getNext(); } @@ -154,24 +154,24 @@ namespace ramses void SceneDumper::setupRenderPassMap() { m_destinationRenderBufferHandleToRenderPassSetMap.clear(); - RamsesObjectRegistryIterator renderPassIterator(m_objectRegistry, ERamsesObjectType::RenderPass); - while (const RenderPass* renderPass = renderPassIterator.getNext()) + SceneObjectRegistryIterator renderPassIterator(m_objectRegistry, ERamsesObjectType::RenderPass); + while (const auto* renderPass = renderPassIterator.getNext()) { - const RenderTarget* renderTarget = renderPass->getRenderTarget(); + const ramses::RenderTarget* renderTarget = renderPass->getRenderTarget(); if (nullptr != renderTarget) { - const ramses_internal::ClientScene& clientScene = renderTarget->m_impl.getIScene(); + const ramses::internal::ClientScene& clientScene = renderTarget->impl().getIScene(); - const ramses_internal::RenderTargetHandle renderTargetHandle = - renderTarget->m_impl.getRenderTargetHandle(); + const ramses::internal::RenderTargetHandle renderTargetHandle = + renderTarget->impl().getRenderTargetHandle(); uint32_t renderBufferCount = clientScene.getRenderTargetRenderBufferCount(renderTargetHandle); for (uint32_t i = 0; i < renderBufferCount; i++) { - const ramses_internal::RenderBufferHandle renderBufferHandle = + const ramses::internal::RenderBufferHandle renderBufferHandle = clientScene.getRenderTargetRenderBuffer(renderTargetHandle, i); - m_destinationRenderBufferHandleToRenderPassSetMap[renderBufferHandle].put(&renderPass->m_impl); + m_destinationRenderBufferHandleToRenderPassSetMap[renderBufferHandle].put(&renderPass->impl()); } } } @@ -180,12 +180,12 @@ namespace ramses void SceneDumper::setupRenderBufferSetMap() { m_blitPassDestinationRenderBufferToSourceRenderBuffersMap.clear(); - RamsesObjectRegistryIterator blitPassIterator(m_objectRegistry, ERamsesObjectType::BlitPass); - while (const BlitPass* blitPass = blitPassIterator.getNext()) + SceneObjectRegistryIterator blitPassIterator(m_objectRegistry, ERamsesObjectType::BlitPass); + while (const auto* blitPass = blitPassIterator.getNext()) { - const ramses_internal::ClientScene& clientScene = blitPass->m_impl.getIScene(); - const ramses_internal::BlitPassHandle blitPassHandle = blitPass->m_impl.getBlitPassHandle(); - const ramses_internal::BlitPass& blitPassData = clientScene.getBlitPass(blitPassHandle); + const ramses::internal::ClientScene& clientScene = blitPass->impl().getIScene(); + const ramses::internal::BlitPassHandle blitPassHandle = blitPass->impl().getBlitPassHandle(); + const ramses::internal::BlitPass& blitPassData = clientScene.getBlitPass(blitPassHandle); const RenderBufferImpl** sourceRenderBuffer = m_renderBufferHandleToObjectMap.get(blitPassData.sourceRenderBuffer); if (nullptr != sourceRenderBuffer) @@ -195,7 +195,7 @@ namespace ramses } else { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "SceneDumper::setupRenderBufferSetMap Could not lookup render buffer handle: " << blitPassData.sourceRenderBuffer << " !!!"); assert(false); @@ -210,24 +210,21 @@ namespace ramses m_requiredObjects.put(&object); return true; } - else - { - return false; - } + return false; } SceneDumper::RenderPassSet SceneDumper::markRequiredScreenRenderPasses() { RenderPassSet requiredRenderPasses; - RamsesObjectRegistryIterator objectIterator(m_objectRegistry, ERamsesObjectType::RenderPass); - while (const RenderPass* renderPass = objectIterator.getNext()) + SceneObjectRegistryIterator objectIterator(m_objectRegistry, ERamsesObjectType::RenderPass); + while (const auto* renderPass = objectIterator.getNext()) { if (renderPass->isEnabled() && nullptr == renderPass->getRenderTarget()) { - if (addToRequiredObjects(renderPass->m_impl)) + if (addToRequiredObjects(renderPass->impl())) { - requiredRenderPasses.put(&renderPass->m_impl); + requiredRenderPasses.put(&renderPass->impl()); } } } @@ -333,20 +330,20 @@ namespace ramses for (auto appearance : requiredAppearances) { - ramses_internal::DataInstanceHandle uniformHandle = appearance->getUniformDataInstance(); - const ramses_internal::ClientScene& scene = appearance->getIScene(); - ramses_internal::DataLayoutHandle layoutHandle = scene.getLayoutOfDataInstance(uniformHandle); + ramses::internal::DataInstanceHandle uniformHandle = appearance->getUniformDataInstance(); + const ramses::internal::ClientScene& scene = appearance->getIScene(); + ramses::internal::DataLayoutHandle layoutHandle = scene.getLayoutOfDataInstance(uniformHandle); uint32_t fieldCount = scene.getDataLayout(layoutHandle).getFieldCount(); for (uint32_t field = 0; field < fieldCount; field++) { - ramses_internal::DataFieldHandle fieldHandle(field); + ramses::internal::DataFieldHandle fieldHandle(field); const auto fieldDataType = scene.getDataLayout(layoutHandle).getField(fieldHandle).dataType; if (IsTextureSamplerType(fieldDataType)) { - ramses_internal::TextureSamplerHandle textureSamplerHandle = + ramses::internal::TextureSamplerHandle textureSamplerHandle = scene.getDataTextureSamplerHandle(uniformHandle, fieldHandle); - if (ramses_internal::TextureSamplerHandle::Invalid() != textureSamplerHandle) + if (ramses::internal::TextureSamplerHandle::Invalid() != textureSamplerHandle) { const TextureSamplerImpl** textureSampler = m_textureSamplerHandleToObjectMap.get(textureSamplerHandle); @@ -362,7 +359,7 @@ namespace ramses else { LOG_ERROR( - ramses_internal::CONTEXT_CLIENT, + ramses::internal::CONTEXT_CLIENT, "SceneDumper::markRequiredTextureSampler Could not lookup texture sampler handle: " << textureSamplerHandle << " !!!"); assert(false); @@ -376,7 +373,7 @@ namespace ramses void SceneDumper::markRequiredEffects(const AppearanceSet& requiredAppearances) { - ramses_internal::HashSet requiredResourceHashes; + ramses::internal::HashSet requiredResourceHashes; for (auto appearance : requiredAppearances) { const EffectImpl* effect = appearance->getEffectImpl(); @@ -390,15 +387,15 @@ namespace ramses void SceneDumper::markRequiredTextures(const TextureSamplerSet& requiredTextureSamplers) { - ramses_internal::HashSet requiredTextureResourceHashes; + ramses::internal::HashSet requiredTextureResourceHashes; for (auto textureSampler : requiredTextureSamplers) { - ramses_internal::TextureSamplerHandle textureSamplerHandle = textureSampler->getTextureSamplerHandle(); - if (ramses_internal::TextureSamplerHandle::Invalid() != textureSamplerHandle) + ramses::internal::TextureSamplerHandle textureSamplerHandle = textureSampler->getTextureSamplerHandle(); + if (ramses::internal::TextureSamplerHandle::Invalid() != textureSamplerHandle) { - ramses_internal::ResourceContentHash textureResourceHash = + ramses::internal::ResourceContentHash textureResourceHash = textureSampler->getIScene().getTextureSampler(textureSamplerHandle).textureResource; - if (ramses_internal::ResourceContentHash::Invalid() != textureResourceHash) + if (ramses::internal::ResourceContentHash::Invalid() != textureResourceHash) { requiredTextureResourceHashes.put(textureResourceHash); } @@ -407,44 +404,44 @@ namespace ramses markRequiredResourcesFromHash(requiredTextureResourceHashes); } - SceneDumper::GeometryBindingSet SceneDumper::markRequiredGeometryBindings(const MeshNodeSet& requiredMeshNodes) + SceneDumper::GeometrySet SceneDumper::markRequiredGeometries(const MeshNodeSet& requiredMeshNodes) { - SceneDumper::GeometryBindingSet requiredGeometryBindings; + SceneDumper::GeometrySet requiredGeometries; for (auto meshNode : requiredMeshNodes) { - const GeometryBindingImpl* geometryBinding = meshNode->getGeometryBindingImpl(); + const GeometryImpl* geometryBinding = meshNode->getGeometryImpl(); if (nullptr != geometryBinding) { if (addToRequiredObjects(*geometryBinding)) { - requiredGeometryBindings.put(geometryBinding); + requiredGeometries.put(geometryBinding); } } } - return requiredGeometryBindings; + return requiredGeometries; } - void SceneDumper::markRequiredVertexAndIndexBuffers(const GeometryBindingSet& requiredGeometryBindings) + void SceneDumper::markRequiredVertexAndIndexBuffers(const GeometrySet& requiredGeometries) { ResourceContentHashSet requiredBufferResourceHashes; - for (auto geometryBinding : requiredGeometryBindings) + for (auto geometryBinding : requiredGeometries) { - const ramses_internal::ClientScene& iscene = geometryBinding->getIScene(); - ramses_internal::DataInstanceHandle dataInstanceHandle = geometryBinding->getAttributeDataInstance(); + const ramses::internal::ClientScene& iscene = geometryBinding->getIScene(); + ramses::internal::DataInstanceHandle dataInstanceHandle = geometryBinding->getAttributeDataInstance(); - ramses_internal::DataLayoutHandle layoutHandle = geometryBinding->getAttributeDataLayout(); + ramses::internal::DataLayoutHandle layoutHandle = geometryBinding->getAttributeDataLayout(); - if (ramses_internal::DataLayoutHandle::Invalid() != layoutHandle) + if (ramses::internal::DataLayoutHandle::Invalid() != layoutHandle) { const uint32_t fieldCount = iscene.getDataLayout(layoutHandle).getFieldCount(); - for (ramses_internal::DataFieldHandle field(0); field < fieldCount; ++field) + for (ramses::internal::DataFieldHandle field(0); field < fieldCount; ++field) { - const ramses_internal::ResourceField& dataResource = + const ramses::internal::ResourceField& dataResource = iscene.getDataResource(dataInstanceHandle, field); - ramses_internal::ResourceContentHash bufferResourceHash = dataResource.hash; - if (ramses_internal::ResourceContentHash::Invalid() != bufferResourceHash) + ramses::internal::ResourceContentHash bufferResourceHash = dataResource.hash; + if (ramses::internal::ResourceContentHash::Invalid() != bufferResourceHash) { requiredBufferResourceHashes.put(bufferResourceHash); } @@ -459,12 +456,12 @@ namespace ramses CameraNodeSet requiredCameras; for (auto renderPass : requiredRenderPasses) { - const Camera* camera = renderPass->getCamera(); + const ramses::Camera* camera = renderPass->getCamera(); if (nullptr != camera) { - if (addToRequiredObjects(camera->m_impl)) + if (addToRequiredObjects(camera->impl())) { - requiredCameras.put(&camera->m_impl); + requiredCameras.put(&camera->impl()); } } } @@ -484,7 +481,7 @@ namespace ramses } else { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "SceneDumper::markRequiredResourcesFromHash Could not lookup resource content hash: " << requiredResourceHash << " !!!"); assert(false); @@ -496,10 +493,10 @@ namespace ramses { for (auto renderPass : requiredRenderPasses) { - const RenderTarget* renderTarget = renderPass->getRenderTarget(); + const ramses::RenderTarget* renderTarget = renderPass->getRenderTarget(); if (nullptr != renderTarget) { - addToRequiredObjects(renderTarget->m_impl); + addToRequiredObjects(renderTarget->impl()); } } } @@ -511,9 +508,9 @@ namespace ramses for (auto textureSampler : requiredTextureSamplers) { const auto sampler = textureSampler->getIScene().getTextureSampler(textureSampler->getTextureSamplerHandle()); - if (sampler.isRenderBuffer() && ramses_internal::RenderBufferHandle::Invalid() != sampler.contentHandle) + if (sampler.isRenderBuffer() && ramses::internal::RenderBufferHandle::Invalid() != sampler.contentHandle) { - const ramses_internal::RenderBufferHandle renderBufferHandle(sampler.contentHandle); + const ramses::internal::RenderBufferHandle renderBufferHandle(sampler.contentHandle); const RenderBufferImpl** renderBuffer = m_renderBufferHandleToObjectMap.get(renderBufferHandle); if (nullptr != renderBuffer) @@ -523,7 +520,7 @@ namespace ramses } else { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "SceneDumper::getRequiredRenderBuffer Could not lookup render buffer handle: " << renderBufferHandle << " !!!"); assert(false); @@ -536,14 +533,14 @@ namespace ramses void SceneDumper::markRequiredTextureBuffer(const TextureSamplerSet& requiredTextureSamplers) { - ramses_internal::HashSet requiredTextureResourceHashes; + ramses::internal::HashSet requiredTextureResourceHashes; for (auto textureSampler : requiredTextureSamplers) { const auto sampler = textureSampler->getIScene().getTextureSampler(textureSampler->getTextureSamplerHandle()); - if (ramses_internal::TextureSampler::ContentType::TextureBuffer == sampler.contentType && ramses_internal::TextureBufferHandle::Invalid() != sampler.contentHandle) + if (ramses::internal::TextureSampler::ContentType::TextureBuffer == sampler.contentType && ramses::internal::TextureBufferHandle::Invalid() != sampler.contentHandle) { - const ramses_internal::TextureBufferHandle textureBufferHandle(sampler.contentHandle); + const ramses::internal::TextureBufferHandle textureBufferHandle(sampler.contentHandle); const Texture2DBufferImpl** textureBuffer = m_textureBufferHandleToObjectMap.get(textureBufferHandle); @@ -554,7 +551,7 @@ namespace ramses } else { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "SceneDumper::getRequiredTextureBuffers Could not lookup texture buffer handle: " << textureBufferHandle << " !!!"); assert(false); @@ -576,7 +573,7 @@ namespace ramses } void SceneDumper::addRenderGroupRecursive(const RenderGroupImpl& renderGroup, - ramses_internal::HashSet& requiredRenderGroups) + ramses::internal::HashSet& requiredRenderGroups) { if (addToRequiredObjects(renderGroup)) { @@ -591,8 +588,8 @@ namespace ramses SceneDumper::RamsesObjectSet SceneDumper::getSceneAndClientObjects() { - RamsesObjectSet objects; - RamsesObjectVector sceneObjects; + RamsesObjectSet objects; + SceneObjectVector sceneObjects; m_objectRegistry.getObjectsOfType(sceneObjects, ERamsesObjectType::RamsesObject); for (auto sceneObject : sceneObjects) { @@ -608,19 +605,19 @@ namespace ramses uint32_t total = 0; }; - void SceneDumper::outputNotRequiredObjects(ramses_internal::StringOutputStream& output) + void SceneDumper::outputNotRequiredObjects(ramses::internal::StringOutputStream& output) { RamsesObjectSet objects = getSceneAndClientObjects(); output << "SceneDumper::outputNotRequiredObjects Unrequired objects:\n"; - ramses_internal::HashMap typeStatistic; + ramses::internal::HashMap typeStatistic; for (RamsesObject* object : objects) { - const RamsesObjectImpl* objectImpl = &object->m_impl; + const RamsesObjectImpl* objectImpl = &object->impl(); ERamsesObjectType type = object->getType(); if (false == m_requiredObjects.contains(objectImpl)) { - outputNotRequiredObject(*object, output); + OutputNotRequiredObject(*object, output); typeStatistic[type].unrequired++; } typeStatistic[type].total++; @@ -631,7 +628,7 @@ namespace ramses AddString("OBEJCT TYPE", output, 39); AddString("#UNREQUIRED / #TOTAL\n\n", output); - for (uint32_t type = 0; type < static_cast(ERamsesObjectType::NUMBER_OF_TYPES); type++) + for (uint32_t type = 0; type < static_cast(RamsesObjectTypeCount); type++) { if (RamsesObjectTypeUtils::IsConcreteType(ERamsesObjectType(type))) { @@ -639,9 +636,9 @@ namespace ramses if (counter.total > 0) { - ramses_internal::StringOutputStream unrequiredString; + ramses::internal::StringOutputStream unrequiredString; unrequiredString << counter.unrequired; - ramses_internal::StringOutputStream totalString; + ramses::internal::StringOutputStream totalString; totalString << counter.total; AddString(RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType(type)), output, 45); @@ -656,7 +653,7 @@ namespace ramses } void SceneDumper::AddString(std::string_view stringToAppend, - ramses_internal::StringOutputStream& string, + ramses::internal::StringOutputStream& string, uint32_t width, bool rightAlign) { @@ -677,13 +674,8 @@ namespace ramses } } - - void SceneDumper::outputNotRequiredObject(const RamsesObject& object, - ramses_internal::StringOutputStream& outputStream) + void SceneDumper::OutputNotRequiredObject(const RamsesObject& object, ramses::internal::StringOutputStream& outputStream) { - outputStream << "Not required " << RamsesObjectTypeUtils::GetRamsesObjectTypeName(object.getType()); - outputStream << " name: \"" << object.getName() << "\" handle: " << object.m_impl.getObjectRegistryHandle() - << "\n"; - + outputStream << "Not required " << object.impl().getIdentificationString(); } } diff --git a/client/ramses-client/impl/SceneDumper.h b/src/client/impl/SceneDumper.h similarity index 57% rename from client/ramses-client/impl/SceneDumper.h rename to src/client/impl/SceneDumper.h index 71b09243c..e41d135dd 100644 --- a/client/ramses-client/impl/SceneDumper.h +++ b/src/client/impl/SceneDumper.h @@ -6,27 +6,29 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEDUMPER_H -#define RAMSES_SCENEDUMPER_H +#pragma once -#include "Collections/StringOutputStream.h" -#include "RamsesObjectRegistryIterator.h" -#include "ramses-client-api/Scene.h" -#include "Collections/HashSet.h" -#include "Collections/HashMap.h" -#include "SceneAPI/Handles.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "ramses/client/Scene.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" -#include "TextureSamplerImpl.h" +#include "impl/TextureSamplerImpl.h" #include namespace ramses { - class RamsesObjectRegistry; class RamsesObject; +} +namespace ramses::internal +{ + class SceneObjectRegistry; class ArrayBufferImpl; - class GeometryBindingImpl; + class GeometryImpl; class Texture2DBufferImpl; class TextureSamplerImpl; class TextureBufferImpl; @@ -47,33 +49,33 @@ namespace ramses class SceneDumper { public: - using RamsesObjectImplSet = ramses_internal::HashSet; - explicit SceneDumper(const ramses::SceneImpl& scene); - void dumpUnrequiredObjects(ramses_internal::StringOutputStream& output); + using RamsesObjectImplSet = ramses::internal::HashSet; + explicit SceneDumper(const SceneImpl& scene); + void dumpUnrequiredObjects(ramses::internal::StringOutputStream& output); [[nodiscard]] const RamsesObjectImplSet& getRequiredObjects() const; private: - using GeometryBindingSet = ramses_internal::HashSet; - using TextureSamplerSet = ramses_internal::HashSet; - using RenderTargetSet = ramses_internal::HashSet; - using RenderBufferSet = ramses_internal::HashSet; - using RenderGroupSet = ramses_internal::HashSet; - using RenderPassSet = ramses_internal::HashSet; - using AppearanceSet = ramses_internal::HashSet; - using CameraNodeSet = ramses_internal::HashSet; - using MeshNodeSet = ramses_internal::HashSet; - using NodeSet = ramses_internal::HashSet; - - using ResourceContentHashSet = ramses_internal::HashSet; - using RenderBufferHandleSet = ramses_internal::HashSet; - using RamsesObjectSet = ramses_internal::HashSet; - - - using TextureSamplerHandleToObjectMap = ramses_internal::HashMap; - using TextureBufferHandleToObjectMap = ramses_internal::HashMap; - using RenderBufferHandleToObjectMap = ramses_internal::HashMap; - using RenderBufferHandleRenderBufferSetMap = ramses_internal::HashMap; - using ResourceContentHashToObjectMap = ramses_internal::HashMap; - using RenderBufferHandleRenderPassSetMap = ramses_internal::HashMap; + using GeometrySet = ramses::internal::HashSet; + using TextureSamplerSet = ramses::internal::HashSet; + using RenderTargetSet = ramses::internal::HashSet; + using RenderBufferSet = ramses::internal::HashSet; + using RenderGroupSet = ramses::internal::HashSet; + using RenderPassSet = ramses::internal::HashSet; + using AppearanceSet = ramses::internal::HashSet; + using CameraNodeSet = ramses::internal::HashSet; + using MeshNodeSet = ramses::internal::HashSet; + using NodeSet = ramses::internal::HashSet; + + using ResourceContentHashSet = ramses::internal::HashSet; + using RenderBufferHandleSet = ramses::internal::HashSet; + using RamsesObjectSet = ramses::internal::HashSet; + + + using TextureSamplerHandleToObjectMap = ramses::internal::HashMap; + using TextureBufferHandleToObjectMap = ramses::internal::HashMap; + using RenderBufferHandleToObjectMap = ramses::internal::HashMap; + using RenderBufferHandleRenderBufferSetMap = ramses::internal::HashMap; + using ResourceContentHashToObjectMap = ramses::internal::HashMap; + using RenderBufferHandleRenderPassSetMap = ramses::internal::HashMap; void setupMaps(); @@ -81,10 +83,10 @@ namespace ramses static HandleType GetHandle(const ObjectImplType& impl); template - void setupMap(ramses_internal::HashMap& map); + void setupMap(ramses::internal::HashMap& map); template - void addToMap(ramses_internal::HashMap& map); + void addToMap(ramses::internal::HashMap& map); void setupRenderBufferSetMap(); void setupRenderPassMap(); @@ -101,8 +103,8 @@ namespace ramses TextureSamplerSet markRequiredTextureSampler(const AppearanceSet& requiredAppearances); void markRequiredEffects(const AppearanceSet& requiredAppearances); void markRequiredTextures(const TextureSamplerSet& requiredTextureSamplers); - GeometryBindingSet markRequiredGeometryBindings(const MeshNodeSet& requiredMeshNodes); - void markRequiredVertexAndIndexBuffers(const GeometryBindingSet& requiredGeometryBindings); + GeometrySet markRequiredGeometries(const MeshNodeSet& requiredMeshNodes); + void markRequiredVertexAndIndexBuffers(const GeometrySet& requiredGeometries); CameraNodeSet markRequiredCameras(const RenderPassSet& requiredRenderPasses); void markRequiredRenderTargets(const RenderPassSet& requiredRenderPasses); RenderBufferSet markRequiredRenderBuffer(const TextureSamplerSet& requiredTextureSamplers); @@ -113,21 +115,21 @@ namespace ramses template void markAllParentNodesAsRequired(const T& requiredNodes); RamsesObjectSet getSceneAndClientObjects(); - void outputNotRequiredObjects(ramses_internal::StringOutputStream& output); - void outputNotRequiredObject(const RamsesObject& object, ramses_internal::StringOutputStream& outputStream); + void outputNotRequiredObjects(ramses::internal::StringOutputStream& output); + static void OutputNotRequiredObject(const RamsesObject& object, ramses::internal::StringOutputStream& outputStream); void addRenderGroupRecursive(const RenderGroupImpl& renderGroup, RenderGroupSet& requiredRenderGroups); void addNodeWithAllParentNodes(const NodeImpl* node); void addRenderBufferRecursive(const RenderBufferImpl& renderBuffer, RenderBufferSet& requiredRenderBuffers); static void AddString(std::string_view stringToAppend, - ramses_internal::StringOutputStream& string, + ramses::internal::StringOutputStream& string, uint32_t width = 0, bool rightAlign = false); - const RamsesClientImpl& m_client; - const RamsesObjectRegistry& m_objectRegistry; - RamsesObjectImplSet m_requiredObjects; + const RamsesClientImpl& m_client; + const SceneObjectRegistry& m_objectRegistry; + RamsesObjectImplSet m_requiredObjects; RenderBufferHandleRenderBufferSetMap m_blitPassDestinationRenderBufferToSourceRenderBuffersMap; RenderBufferHandleRenderPassSetMap m_destinationRenderBufferHandleToRenderPassSetMap; @@ -146,5 +148,3 @@ namespace ramses } } } - -#endif diff --git a/client/ramses-client/impl/SceneFactory.cpp b/src/client/impl/SceneFactory.cpp similarity index 78% rename from client/ramses-client/impl/SceneFactory.cpp rename to src/client/impl/SceneFactory.cpp index a9448fa99..7206ca6ac 100644 --- a/client/ramses-client/impl/SceneFactory.cpp +++ b/src/client/impl/SceneFactory.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneFactory.h" -#include "Scene/ClientScene.h" -#include "Utils/LogMacros.h" +#include "impl/SceneFactory.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { ClientScene* SceneFactory::createScene(const SceneInfo& sceneInfo) { if (m_scenes.count(sceneInfo.sceneID) != 0) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "SceneFactory::createScene: scene with id " << sceneInfo.sceneID << " already exists, scene ID must be unique!"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "SceneFactory::createScene: scene with id " << sceneInfo.sceneID << " already exists, scene ID must be unique!"); return nullptr; } diff --git a/client/ramses-client/impl/SceneFactory.h b/src/client/impl/SceneFactory.h similarity index 87% rename from client/ramses-client/impl/SceneFactory.h rename to src/client/impl/SceneFactory.h index 1d869e29a..c3e0b18b2 100644 --- a/client/ramses-client/impl/SceneFactory.h +++ b/src/client/impl/SceneFactory.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEFACTORY_H -#define RAMSES_SCENEFACTORY_H +#pragma once -#include "SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" #include -namespace ramses_internal +namespace ramses::internal { class ClientScene; using InternalSceneOwningPtr = std::unique_ptr; @@ -28,5 +27,3 @@ namespace ramses_internal SceneMap m_scenes; }; } - -#endif diff --git a/client/ramses-client-api/SceneGraphIterator.cpp b/src/client/impl/SceneGraphIterator.cpp similarity index 84% rename from client/ramses-client-api/SceneGraphIterator.cpp rename to src/client/impl/SceneGraphIterator.cpp index 974b0dea9..719b76570 100644 --- a/client/ramses-client-api/SceneGraphIterator.cpp +++ b/src/client/impl/SceneGraphIterator.cpp @@ -7,7 +7,7 @@ // ------------------------------------------------------------------------- -#include "ramses-client-api/SceneGraphIterator.h" +#include "ramses/client/SceneGraphIterator.h" // internal #include "SceneGraphIteratorImpl.h" @@ -15,7 +15,7 @@ namespace ramses { SceneGraphIterator::SceneGraphIterator(Node& startNode, ETreeTraversalStyle traversalStyle, ERamsesObjectType objectType) - : m_impl{ std::make_unique(startNode, traversalStyle, objectType) } + : m_impl{ std::make_unique(startNode, traversalStyle, objectType) } { } diff --git a/client/ramses-client/impl/SceneGraphIteratorImpl.cpp b/src/client/impl/SceneGraphIteratorImpl.cpp similarity index 88% rename from client/ramses-client/impl/SceneGraphIteratorImpl.cpp rename to src/client/impl/SceneGraphIteratorImpl.cpp index a3f92fd60..86fb5a0fc 100644 --- a/client/ramses-client/impl/SceneGraphIteratorImpl.cpp +++ b/src/client/impl/SceneGraphIteratorImpl.cpp @@ -7,18 +7,19 @@ // ------------------------------------------------------------------------- #include "SceneGraphIteratorImpl.h" -#include "ramses-client-api/Node.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "NodeImpl.h" -#include "RamsesObjectTypeUtils.h" +#include "ramses/client/Node.h" +#include "impl/NodeImpl.h" +#include "impl/RamsesObjectTypeUtils.h" -namespace ramses +#include + +namespace ramses::internal { SceneGraphIteratorImpl::SceneGraphIteratorImpl(Node& startNode, ETreeTraversalStyle traversalStyle, ERamsesObjectType objectFilterType) : m_traversalStyle(traversalStyle) , m_objectFilterType(objectFilterType) { - m_nodesToExpand.push_back(&startNode.m_impl); + m_nodesToExpand.push_back(&startNode.impl()); } Node* SceneGraphIteratorImpl::getNext() @@ -47,7 +48,7 @@ namespace ramses void SceneGraphIteratorImpl::expandDepthFirst(NodeImpl& node) { - const int32_t childCount = static_cast(node.getChildCount()); + const auto childCount = static_cast(node.getChildCount()); for (int32_t i = childCount - 1; i >= 0; --i) { m_nodesToExpand.push_front(&node.getChildImpl(i)); diff --git a/client/ramses-client/impl/SceneGraphIteratorImpl.h b/src/client/impl/SceneGraphIteratorImpl.h similarity index 79% rename from client/ramses-client/impl/SceneGraphIteratorImpl.h rename to src/client/impl/SceneGraphIteratorImpl.h index 19a350ba5..ac0f7f433 100644 --- a/client/ramses-client/impl/SceneGraphIteratorImpl.h +++ b/src/client/impl/SceneGraphIteratorImpl.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEGRAPHITERATORIMPL_H -#define RAMSES_SCENEGRAPHITERATORIMPL_H +#pragma once -#include "ramses-client-api/SceneGraphIterator.h" -#include "ramses-client-api/RamsesObjectTypes.h" +#include "ramses/client/SceneGraphIterator.h" +#include "ramses/framework/RamsesObjectTypes.h" #include -#include "NodeImpl.h" +#include "impl/NodeImpl.h" -namespace ramses +namespace ramses::internal { class SceneGraphIteratorImpl { @@ -33,5 +32,3 @@ namespace ramses }; } - -#endif //RAMSES_SCENEGRAPHITERATORIMPL_H diff --git a/client/ramses-client/impl/SceneImpl.cpp b/src/client/impl/SceneImpl.cpp similarity index 50% rename from client/ramses-client/impl/SceneImpl.cpp rename to src/client/impl/SceneImpl.cpp index 46f0586d5..5b05c710a 100644 --- a/client/ramses-client/impl/SceneImpl.cpp +++ b/src/client/impl/SceneImpl.cpp @@ -6,111 +6,116 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneImpl.h" - -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EScenePublicationMode.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/SceneReference.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Resource.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EffectDescription.h" +#include "impl/SceneImpl.h" + +#include "ramses/client/RamsesClient.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/SceneReference.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Resource.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/Effect.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicObject.h" +#include "ramses/framework/EScenePublicationMode.h" + +#include "impl/CameraNodeImpl.h" +#include "impl/EffectImpl.h" +#include "impl/NodeImpl.h" +#include "impl/AppearanceImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/RenderTargetDescriptionImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/TextureUtils.h" +#include "impl/Texture2DImpl.h" +#include "impl/Texture3DImpl.h" +#include "impl/TextureCubeImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/EffectInputImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/SceneConfigImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "impl/SerializationHelper.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/Texture2DBufferImpl.h" +#include "impl/PickableObjectImpl.h" +#include "impl/SceneReferenceImpl.h" +#include "impl/DataTypeUtils.h" +#include "impl/AppearanceUtils.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/SaveFileConfigImpl.h" +#include "impl/logic/LogicObjectImpl.h" +#include "impl/SerializationContext.h" + +#include "internal/SceneGraph/Scene/Scene.h" +#include "internal/SceneGraph/Scene/ScenePersistation.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/ClientCommands/SceneCommandBuffer.h" +#include "internal/ClientCommands/SceneCommandVisitor.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/EffectUniformTime.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Core/Utils/TextureMathUtils.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/DataSlotUtils.h" +#include "internal/RamsesVersion.h" + #include "ramses-sdk-build-config.h" -#include "ramses-utils.h" - -#include "Scene/Scene.h" -#include "CameraNodeImpl.h" -#include "EffectImpl.h" -#include "NodeImpl.h" -#include "AppearanceImpl.h" -#include "GeometryBindingImpl.h" -#include "SerializationContext.h" -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "RenderGroupImpl.h" -#include "RenderPassImpl.h" -#include "RenderBufferImpl.h" -#include "RenderTargetImpl.h" -#include "RenderTargetDescriptionImpl.h" -#include "TextureSamplerImpl.h" -#include "TextureUtils.h" -#include "Texture2DImpl.h" -#include "Texture3DImpl.h" -#include "TextureCubeImpl.h" -#include "MeshNodeImpl.h" -#include "EffectInputImpl.h" -#include "RamsesFrameworkImpl.h" -#include "Scene/EScenePublicationMode.h" -#include "RamsesClientImpl.h" -#include "SceneConfigImpl.h" -#include "Scene/ClientScene.h" -#include "SceneUtils.h" -#include "DataObjectImpl.h" -#include "BlitPassImpl.h" -#include "ClientCommands/SceneCommandBuffer.h" -#include "ClientCommands/SceneCommandVisitor.h" -#include "RamsesObjectRegistryIterator.h" -#include "SerializationHelper.h" -#include "ArrayBufferImpl.h" -#include "Texture2DBufferImpl.h" -#include "DataSlotUtils.h" -#include "PickableObjectImpl.h" -#include "SceneReferenceImpl.h" -#include "DataTypeUtils.h" -#include "RamsesVersion.h" -#include "Scene/ScenePersistation.h" -#include "AppearanceUtils.h" - -#include "Resource/ArrayResource.h" -#include "Resource/TextureResource.h" -#include "Resource/EffectResource.h" - -#include "Components/FlushTimeInformation.h" -#include "Components/EffectUniformTime.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "Utils/TextureMathUtils.h" -#include "Components/FlushTimeInformation.h" #include "fmt/format.h" - #include +#include -namespace ramses +namespace ramses::internal { - SceneImpl::SceneImpl(ramses_internal::ClientScene& scene, const SceneConfigImpl& sceneConfig, RamsesClient& ramsesClient) - : ClientObjectImpl(ramsesClient.m_impl, ERamsesObjectType::Scene, scene.getName().c_str()) + SceneImpl::SceneImpl(ramses::internal::ClientScene& scene, const SceneConfigImpl& sceneConfig, RamsesClient& ramsesClient) + : ClientObjectImpl(ramsesClient.impl(), ERamsesObjectType::Scene, scene.getName().c_str()) , m_scene(scene) , m_nextSceneVersion(InvalidSceneVersionTag) , m_futurePublicationMode(sceneConfig.getPublicationMode()) , m_hlClient(ramsesClient) { - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "Scene::Scene: sceneId " << scene.getSceneId() << + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "Scene::Scene: sceneId " << scene.getSceneId() << ", publicationMode " << (sceneConfig.getPublicationMode() == EScenePublicationMode::LocalAndRemote ? "LocalAndRemote" : "LocalOnly")); getClientImpl().getFramework().getPeriodicLogger().registerStatisticCollectionScene(m_scene.getSceneId(), m_scene.getStatisticCollection()); const bool enableLocalOnlyOptimization = sceneConfig.getPublicationMode() == EScenePublicationMode::LocalOnly; @@ -132,21 +137,23 @@ namespace ramses { } - status_t SceneImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool SceneImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(ClientObjectImpl::serialize(outStream, serializationContext)); + if (!ClientObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << static_cast(std::chrono::duration_cast(m_expirationTimestamp.time_since_epoch()).count()); - CHECK_RETURN_ERR(SerializationHelper::SerializeObjectsInRegistry(outStream, serializationContext, m_objectRegistry)); + if (!SerializationHelper::SerializeObjectsInRegistry(outStream, serializationContext, m_objectRegistry)) + return false; outStream << m_lastSceneObjectId.getValue(); - return StatusOK; + return true; } template ::value, T>::type* = nullptr> - std::unique_ptr createImplHelper(SceneImpl& scene, ERamsesObjectType) + std::unique_ptr createImplHelper(SceneImpl& scene, ERamsesObjectType /*unused*/) { return std::make_unique(scene, ""); } @@ -155,27 +162,26 @@ namespace ramses { return std::make_unique(scene, type, ""); } - template ::value, T>::type* = nullptr> - std::unique_ptr createImplHelper(SceneImpl& scene, ERamsesObjectType) + template ::value, T>::type* = nullptr> + std::unique_ptr createImplHelper(SceneImpl& scene, ERamsesObjectType /*unused*/) { - return std::make_unique(ramses_internal::ResourceHashUsage{}, scene, ""); + return std::make_unique(ramses::internal::ResourceHashUsage{}, scene, ""); } - template ::value, T>::type* = nullptr> + template ::value, T>::type* = nullptr> std::unique_ptr createImplHelper(SceneImpl& scene, ERamsesObjectType type) { - return std::make_unique(scene, type, EDataType::Int32, ""); + return std::make_unique(scene, type, ramses::EDataType::Int32, ""); } template - status_t SceneImpl::createAndDeserializeObjectImpls(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext, uint32_t count) + bool SceneImpl::createAndDeserializeObjectImpls(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext, uint32_t count) { for (uint32_t i = 0u; i < count; ++i) { auto impl = createImplHelper(*this, TYPE_ID_OF_RAMSES_OBJECT::ID); ObjectIDType objectID = SerializationHelper::DeserializeObjectID(inStream); - auto status = impl->deserialize(inStream, serializationContext); - if (status != StatusOK) - return status; + if (!impl->deserialize(inStream, serializationContext)) + return false; serializationContext.registerObjectImpl(impl.get(), objectID); @@ -185,16 +191,17 @@ namespace ramses m_resources.insert({ object.getResourceId(), &object }); } - return StatusOK; + return true; } - status_t SceneImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool SceneImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(ClientObjectImpl::deserialize(inStream, serializationContext)); + if (!ClientObjectImpl::deserialize(inStream, serializationContext)) + return false; uint64_t expirationTS = 0u; inStream >> expirationTS; - m_expirationTimestamp = ramses_internal::FlushTime::Clock::time_point(std::chrono::milliseconds(expirationTS)); + m_expirationTimestamp = ramses::internal::FlushTime::Clock::time_point(std::chrono::milliseconds(expirationTS)); uint32_t totalCount = 0u; uint32_t typesCount = 0u; @@ -202,7 +209,7 @@ namespace ramses serializationContext.resize(totalCount, m_scene.getNodeCount()); m_objectRegistry.reserveAdditionalGeneralCapacity(totalCount); - std::array(ERamsesObjectType::NUMBER_OF_TYPES)> objectCounts = {}; + std::array objectCounts = {}; for (uint32_t i = 0u; i < typesCount; ++i) { @@ -212,7 +219,7 @@ namespace ramses m_objectRegistry.reserveAdditionalObjectCapacity(type, count); objectCounts[i] = count; - status_t status = StatusOK; + bool status = true; switch (type) { case ERamsesObjectType::Node: @@ -230,32 +237,32 @@ namespace ramses case ERamsesObjectType::Appearance: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; - case ERamsesObjectType::GeometryBinding: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + case ERamsesObjectType::Geometry: + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::RenderGroup: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::RenderPass: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::BlitPass: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::PickableObject: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::SceneReference: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::RenderBuffer: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::RenderTarget: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::TextureSampler: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::TextureSamplerMS: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); @@ -266,14 +273,14 @@ namespace ramses case ERamsesObjectType::DataObject: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; - case ERamsesObjectType::ArrayBufferObject: + case ERamsesObjectType::ArrayBuffer: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::Texture2DBuffer: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::ArrayResource: - status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; case ERamsesObjectType::Texture2D: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); @@ -287,7 +294,9 @@ namespace ramses case ERamsesObjectType::Effect: status = createAndDeserializeObjectImpls(inStream, serializationContext, count); break; - case ERamsesObjectType::NUMBER_OF_TYPES: + case ERamsesObjectType::LogicEngine: + status = createAndDeserializeObjectImpls(inStream, serializationContext, count); + break; case ERamsesObjectType::Invalid: case ERamsesObjectType::ClientObject: case ERamsesObjectType::RamsesObject: @@ -296,15 +305,18 @@ namespace ramses case ERamsesObjectType::Camera: case ERamsesObjectType::Client: case ERamsesObjectType::Scene: - return addErrorEntry("Scene::deserialize failed, unexpected object type in file stream."); + case ERamsesObjectType::LogicObject: + getErrorReporting().set("Scene::deserialize failed, unexpected object type in file stream."); + return false; } - CHECK_RETURN_ERR(status); + if (!status) + return false; } inStream >> m_lastSceneObjectId.getReference(); - LOG_DEBUG_F(ramses_internal::CONTEXT_PROFILING, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(ramses::internal::CONTEXT_PROFILING, ([&](ramses::internal::StringOutputStream& sos) { sos << "SceneImpl::deserialize: HL scene object counts for SceneID " << m_scene.getSceneId() << "\n"; for (uint32_t i = 0; i < objectCounts.size(); i++) { @@ -315,61 +327,50 @@ namespace ramses } })); - CHECK_RETURN_ERR(serializationContext.resolveDependencies()); - - return StatusOK; + return serializationContext.resolveDependencies(); } - status_t SceneImpl::validate() const + void SceneImpl::onValidate(ValidationReportImpl& report) const { - status_t status = ClientObjectImpl::validate(); - - std::array(ERamsesObjectType::NUMBER_OF_TYPES)> objectCount; - for (uint32_t i = 0u; i < objectCount.size(); ++i) + for (size_t i = 0u; i < RamsesObjectTypeCount; ++i) { - const ERamsesObjectType type = static_cast(i); + const auto type = static_cast(i); if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ERamsesObjectType::SceneObject) && RamsesObjectTypeUtils::IsConcreteType(type)) { - objectCount[i] = 0u; - RamsesObjectRegistryIterator iter(getObjectRegistry(), ERamsesObjectType(i)); - while (const RamsesObject* obj = iter.getNext()) + SceneObjectRegistryIterator iter(getObjectRegistry(), ERamsesObjectType(i)); + while (const auto* obj = iter.getNext()) { - status = std::max(status, addValidationOfDependentObject(obj->m_impl)); - ++objectCount[i]; + obj->impl().validate(report); } } } - for (uint32_t i = 0u; i < objectCount.size(); ++i) - { - const ERamsesObjectType type = static_cast(i); - if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ERamsesObjectType::SceneObject) - && RamsesObjectTypeUtils::IsConcreteType(type)) - { - ramses_internal::StringOutputStream msg; - msg << "Number of " << RamsesObjectTypeUtils::GetRamsesObjectTypeName(type) << " instances: " << objectCount[i]; - addValidationMessage(EValidationSeverity::Info, msg.c_str()); - } - } - // special validation (see SceneImpl::createTextureConsumer(const TextureSamplerExternal&, dataConsumerId_t)), // duplicate IDs are temporarily allowed but validation still reports them as errors // TODO vaclav do this properly - std::unordered_set texConsumerIds; + std::unordered_set texConsumerIds; for (const auto& it : m_scene.getDataSlots()) { - if (it.second->type == ramses_internal::EDataSlotType_TextureConsumer) + if (it.second->type == ramses::internal::EDataSlotType::TextureConsumer) { const auto consumerId = it.second->id; if (texConsumerIds.count(consumerId) > 0u) - status = addValidationMessage(EValidationSeverity::Error, - fmt::format("Duplicate texture consumer ID '{}' is not allowed and will result in unknown behavior when linking on renderer", consumerId.getValue())); + { + report.add( + EIssueType::Error, + fmt::format("Duplicate texture consumer ID '{}' is not allowed and will result in unknown behavior when linking on renderer", consumerId.getValue()) + , &getRamsesObject()); + } texConsumerIds.insert(consumerId); } } + } - return status; + LogicEngine* SceneImpl::createLogicEngine(std::string_view name) + { + auto pimpl = std::make_unique(*this, name); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } PerspectiveCamera* SceneImpl::createPerspectiveCamera(std::string_view name) @@ -390,94 +391,87 @@ namespace ramses Appearance* SceneImpl::createAppearance(const Effect& effect, std::string_view name) { - if (this != &effect.m_impl.getSceneImpl()) + if (this != &effect.impl().getSceneImpl()) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createAppearance failed, effect is not from this scene."); return nullptr; } auto pimpl = std::make_unique(*this, name); - pimpl->initializeFrameworkData(effect.m_impl); + pimpl->initializeFrameworkData(effect.impl()); return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - GeometryBinding* SceneImpl::createGeometryBinding(const Effect& effect, std::string_view name) + Geometry* SceneImpl::createGeometry(const Effect& effect, std::string_view name) { - if (this != &effect.m_impl.getSceneImpl()) + if (this != &effect.impl().getSceneImpl()) { - LOG_ERROR(CONTEXT_CLIENT, "Scene::createGeometryBinding failed, effect is not from this scene."); + LOG_ERROR(CONTEXT_CLIENT, "Scene::createGeometry failed, effect is not from this scene."); return nullptr; } - auto pimpl = std::make_unique(*this, name); - pimpl->initializeFrameworkData(effect.m_impl); + auto pimpl = std::make_unique(*this, name); + pimpl->initializeFrameworkData(effect.impl()); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - status_t SceneImpl::destroy(SceneObject& object) + bool SceneImpl::destroy(SceneObject& object) { - if (!containsSceneObject(object.m_impl)) - return addErrorEntry("Scene::destroy failed, object is not in this scene."); + if (!containsSceneObject(object.impl())) + { + getErrorReporting().set("Scene::destroy failed, object is not in this scene.", *this); + return false; + } - status_t returnStatus = StatusOK; switch (object.getType()) { case ERamsesObjectType::RenderTarget: - returnStatus = destroyRenderTarget(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyRenderTarget(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::OrthographicCamera: case ERamsesObjectType::PerspectiveCamera: - returnStatus = destroyCamera(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyCamera(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::RenderGroup: - returnStatus = destroyRenderGroup(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyRenderGroup(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::Node: case ERamsesObjectType::PickableObject: - returnStatus = destroyNode(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyNode(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::MeshNode: - returnStatus = destroyMeshNode(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyMeshNode(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::DataObject: - returnStatus = destroyDataObject(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyDataObject(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::TextureSampler: - destroyTextureSampler(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyTextureSampler(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::TextureSamplerMS: - destroyTextureSampler(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyTextureSampler(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::TextureSamplerExternal: - destroyTextureSampler(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyTextureSampler(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::Appearance: - case ERamsesObjectType::GeometryBinding: + case ERamsesObjectType::Geometry: case ERamsesObjectType::RenderPass: case ERamsesObjectType::BlitPass: case ERamsesObjectType::RenderBuffer: - case ERamsesObjectType::ArrayBufferObject: + case ERamsesObjectType::ArrayBuffer: case ERamsesObjectType::Texture2DBuffer: - returnStatus = destroyObject(object); - break; + case ERamsesObjectType::LogicEngine: + return destroyObject(object); + case ERamsesObjectType::LogicObject: + getErrorReporting().set("Scene::destroy cannot destroy logic object, use LogicEngine::destroy to destroy logic objects.", *this); + return false; case ERamsesObjectType::SceneReference: { - auto& sceneReference = RamsesObjectTypeUtils::ConvertTo(object); - LOG_INFO_P(ramses_internal::CONTEXT_CLIENT, "Scene::destroySceneReference: (master {} / ref {})", getSceneId(), sceneReference.getReferencedSceneId()); + auto& sceneReference = RamsesObjectTypeUtils::ConvertTo(object); + LOG_INFO_P(ramses::internal::CONTEXT_CLIENT, "Scene::destroySceneReference: (master {} / ref {})", getSceneId(), sceneReference.getReferencedSceneId()); m_sceneReferences.remove(sceneReference.getReferencedSceneId()); - returnStatus = destroyObject(object); - break; + return destroyObject(object); } case ERamsesObjectType::Texture2D: case ERamsesObjectType::Texture3D: case ERamsesObjectType::TextureCube: case ERamsesObjectType::Effect: case ERamsesObjectType::ArrayResource: - returnStatus = destroyResource(RamsesObjectTypeUtils::ConvertTo(object)); - break; + return destroyResource(RamsesObjectTypeUtils::ConvertTo(object)); case ERamsesObjectType::Invalid: - case ERamsesObjectType::NUMBER_OF_TYPES: case ERamsesObjectType::ClientObject: case ERamsesObjectType::RamsesObject: case ERamsesObjectType::SceneObject: @@ -485,49 +479,51 @@ namespace ramses case ERamsesObjectType::Camera: case ERamsesObjectType::Client: case ERamsesObjectType::Scene: - assert(false); - returnStatus = addErrorEntry("Scene::destroy internal error, cannot destroy object!"); + getErrorReporting().set("Scene::destroy internal error, cannot destroy object!"); break; } - return returnStatus; + assert(false); + return false; } - status_t SceneImpl::destroyRenderTarget(RenderTarget& renderTarget) + bool SceneImpl::destroyRenderTarget(ramses::RenderTarget& renderTarget) { - RamsesObjectRegistryIterator iterator(m_objectRegistry, ERamsesObjectType::RenderPass); - const RenderPass* renderPass = nullptr; - while ((renderPass = iterator.getNext()) != nullptr) + SceneObjectRegistryIterator iterator(m_objectRegistry, ERamsesObjectType::RenderPass); + const ramses::RenderPass* renderPass = nullptr; + while ((renderPass = iterator.getNext()) != nullptr) { - if (&renderTarget == renderPass->m_impl.getRenderTarget()) + if (&renderTarget == renderPass->impl().getRenderTarget()) { - return addErrorEntry("Scene::destroy can not destroy render target while it is still assigned to a render pass!"); + getErrorReporting().set("Scene::destroy can not destroy render target while it is still assigned to a render pass!", *this); + return false; } } return destroyObject(renderTarget); } - status_t SceneImpl::destroyCamera(Camera& camera) + bool SceneImpl::destroyCamera(ramses::Camera& camera) { if (cameraIsAssignedToRenderPasses(camera)) { - return addErrorEntry("Scene::destroy can not destroy camera while it is still assigned to a render pass!"); + getErrorReporting().set("Scene::destroy can not destroy camera while it is still assigned to a render pass!", *this); + return false; } return destroyNode(camera); } - status_t SceneImpl::destroyRenderGroup(RenderGroup& group) + bool SceneImpl::destroyRenderGroup(ramses::RenderGroup& group) { - removeObjectFromAllContainers(group); - removeObjectFromAllContainers(group); + removeObjectFromAllContainers(group); + removeObjectFromAllContainers(group); return destroyObject(group); } - status_t SceneImpl::destroyMeshNode(MeshNode& mesh) + bool SceneImpl::destroyMeshNode(MeshNode& mesh) { - removeObjectFromAllContainers(mesh); + removeObjectFromAllContainers(mesh); return destroyNode(mesh); } @@ -535,11 +531,11 @@ namespace ramses { for (size_t i = 0u; i < node.getChildCount(); ++i) { - m_objectRegistry.setNodeDirty(node.m_impl.getChildImpl(i), true); + m_objectRegistry.setNodeDirty(node.impl().getChildImpl(i), true); } } - status_t SceneImpl::destroyNode(Node& node) + bool SceneImpl::destroyNode(Node& node) { markAllChildrenDirty(node); @@ -558,11 +554,11 @@ namespace ramses return destroyObject(node); } - status_t SceneImpl::destroyDataObject(DataObject& dataObject) + bool SceneImpl::destroyDataObject(DataObject& dataObject) { - const ramses_internal::DataInstanceHandle dataRef = dataObject.m_impl.getDataReference(); + const ramses::internal::DataInstanceHandle dataRef = dataObject.impl().getDataReference(); const uint32_t slotHandleCount = m_scene.getDataSlotCount(); - for (ramses_internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) + for (ramses::internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) { if (m_scene.isDataSlotAllocated(slotHandle) && m_scene.getDataSlot(slotHandle).attachedDataReference == dataRef) @@ -575,11 +571,11 @@ namespace ramses } template - status_t SceneImpl::destroyTextureSampler(SAMPLER& sampler) + bool SceneImpl::destroyTextureSampler(SAMPLER& sampler) { - const ramses_internal::TextureSamplerHandle& samplerHandle = sampler.m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle& samplerHandle = sampler.impl().getTextureSamplerHandle(); const uint32_t slotHandleCount = m_scene.getDataSlotCount(); - for (ramses_internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) + for (ramses::internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) { if (m_scene.isDataSlotAllocated(slotHandle) && m_scene.getDataSlot(slotHandle).attachedTextureSampler == samplerHandle) @@ -591,9 +587,9 @@ namespace ramses return destroyObject(sampler); } - status_t SceneImpl::destroyResource(Resource& resource) + bool SceneImpl::destroyResource(Resource& resource) { - const resourceId_t resId = resource.m_impl.getResourceId(); + const resourceId_t resId = resource.impl().getResourceId(); const bool found = removeResourceWithIdFromResources(resId, resource); if (!found) assert(false); @@ -601,41 +597,43 @@ namespace ramses return destroyObject(resource); } - status_t SceneImpl::destroyObject(SceneObject& object) + bool SceneImpl::destroyObject(SceneObject& object) { - object.m_impl.deinitializeFrameworkData(); + object.impl().deinitializeFrameworkData(); m_objectRegistry.destroyAndUnregisterObject(object); - return StatusOK; + return true; } - status_t SceneImpl::publish(EScenePublicationMode requestedPublicationMode) + bool SceneImpl::publish(EScenePublicationMode requestedPublicationMode) { if (isPublished()) { - return addErrorEntry((ramses_internal::StringOutputStream() << "Scene(" << m_scene.getSceneId() << ")::publish: ignored, scene is already published").c_str()); + getErrorReporting().set((ramses::internal::StringOutputStream() << "Scene(" << m_scene.getSceneId() << ")::publish: ignored, scene is already published").c_str(), *this); + return false; } if (requestedPublicationMode == EScenePublicationMode::LocalAndRemote && m_futurePublicationMode == EScenePublicationMode::LocalOnly) { - return addErrorEntry((ramses_internal::StringOutputStream() << "Scene(" << m_scene.getSceneId() << ")::publish: Enabled local only optimisations from SceneConfig, cannot remote publish later").c_str()); + getErrorReporting().set((ramses::internal::StringOutputStream() << "Scene(" << m_scene.getSceneId() << ")::publish: Enabled local only optimisations from SceneConfig, cannot remote publish later").c_str(), *this); + return false; } if (requestedPublicationMode != EScenePublicationMode::LocalOnly && !getClientImpl().getFramework().isConnected()) { - LOG_INFO(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::publish(LocalAndRemote): Scene is only published locally until framework is connected (RamsesFramework::connect)"); + LOG_INFO(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::publish(LocalAndRemote): Scene is only published locally until framework is connected (RamsesFramework::connect)"); } - const ramses_internal::EScenePublicationMode internalMode = SceneUtils::GetScenePublicationModeInternal(requestedPublicationMode); - getClientImpl().getClientApplication().publishScene(m_scene.getSceneId(), internalMode); - return StatusOK; + getClientImpl().getClientApplication().publishScene(m_scene.getSceneId(), requestedPublicationMode); + return true; } - status_t SceneImpl::unpublish() + bool SceneImpl::unpublish() { if (!isPublished()) { - return addErrorEntry((ramses_internal::StringOutputStream() << "Scene(" << m_scene.getSceneId() << ")::unpublish ignored, scene is not published.").c_str()); + getErrorReporting().set((ramses::internal::StringOutputStream() << "Scene(" << m_scene.getSceneId() << ")::unpublish ignored, scene is not published.").c_str(), *this); + return false; } getClientImpl().getClientApplication().unpublishScene(m_scene.getSceneId()); - return StatusOK; + return true; } bool SceneImpl::isPublished() const @@ -669,123 +667,113 @@ namespace ramses return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - RenderGroup* SceneImpl::createRenderGroup(std::string_view name) + ramses::RenderGroup* SceneImpl::createRenderGroup(std::string_view name) { auto pimpl = std::make_unique(*this, name); pimpl->initializeFrameworkData(); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - RenderPass* SceneImpl::createRenderPass(std::string_view name /*= {}*/) + ramses::RenderPass* SceneImpl::createRenderPass(std::string_view name /*= {}*/) { return createRenderPassInternal(name); } - BlitPass* SceneImpl::createBlitPass(const RenderBuffer& sourceRenderBuffer, const RenderBuffer& destinationRenderBuffer, std::string_view name) + ramses::BlitPass* SceneImpl::createBlitPass(const ramses::RenderBuffer& sourceRenderBuffer, const ramses::RenderBuffer& destinationRenderBuffer, std::string_view name) { - if (!containsSceneObject(sourceRenderBuffer.m_impl)) + if (!containsSceneObject(sourceRenderBuffer.impl())) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source render buffer is not from this scene."); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source render buffer is not from this scene."); return nullptr; } - if (!containsSceneObject(destinationRenderBuffer.m_impl)) + if (!containsSceneObject(destinationRenderBuffer.impl())) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, destination render buffer is not from this scene."); - return nullptr; - } - - if (sourceRenderBuffer.getBufferType() != destinationRenderBuffer.getBufferType()) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination buffers have different buffer types"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, destination render buffer is not from this scene."); return nullptr; } if (sourceRenderBuffer.getBufferFormat() != destinationRenderBuffer.getBufferFormat()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination buffers have different buffer formats"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination buffers have different buffer formats"); return nullptr; } if (sourceRenderBuffer.getHeight() != destinationRenderBuffer.getHeight() || sourceRenderBuffer.getWidth() != destinationRenderBuffer.getWidth()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination buffers have different dimensions"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination buffers have different dimensions"); return nullptr; } if (&sourceRenderBuffer == &destinationRenderBuffer) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination cannot be the same buffer"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createBlitPass failed, source and destination cannot be the same buffer"); return nullptr; } auto pimpl = std::make_unique(*this, name); - pimpl->initializeFrameworkData(sourceRenderBuffer.m_impl, destinationRenderBuffer.m_impl); + pimpl->initializeFrameworkData(sourceRenderBuffer.impl(), destinationRenderBuffer.impl()); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - PickableObject* SceneImpl::createPickableObject(const ArrayBuffer& geometryBuffer, const pickableObjectId_t id, std::string_view name) + ramses::PickableObject* SceneImpl::createPickableObject(const ArrayBuffer& geometryBuffer, const pickableObjectId_t id, std::string_view name) { - if (!containsSceneObject(geometryBuffer.m_impl)) + if (!containsSceneObject(geometryBuffer.impl())) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createPickableObject failed, geometry buffer is not from this scene."); return nullptr; } - if (geometryBuffer.getDataType() != EDataType::Vector3F || 0 != (geometryBuffer.m_impl.getElementCount() % 3)) + if (geometryBuffer.getDataType() != ramses::EDataType::Vector3F || 0 != (geometryBuffer.impl().getElementCount() % 3)) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createPickableObject failed, geometry buffer has the wrong format."); return nullptr; } auto pimpl = std::make_unique(*this, name); - pimpl->initializeFrameworkData(geometryBuffer.m_impl, id); + pimpl->initializeFrameworkData(geometryBuffer.impl(), id); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - RenderBuffer* SceneImpl::createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferType bufferType, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount, std::string_view name) + ramses::RenderBuffer* SceneImpl::createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount, std::string_view name) { if (0 == width || 0 == height) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createRenderBuffer failed: cannot create a render buffer with 0 width and/or height!"); - return nullptr; - } - - if (!TextureUtils::IsRenderBufferTypeCompatibleWithFormat(bufferType, bufferFormat)) - { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createRenderBuffer failed: render buffer format incompatible with its type!"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene(" << m_scene.getSceneId() << ")::createRenderBuffer failed: cannot create a render buffer with 0 width and/or height!"); return nullptr; } auto pimpl = std::make_unique(*this, name); - pimpl->initializeFrameworkData(width, height, bufferType, bufferFormat, accessMode, sampleCount); + pimpl->initializeFrameworkData(width, height, bufferFormat, accessMode, sampleCount); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - RenderTarget* SceneImpl::createRenderTarget(const RenderTargetDescriptionImpl& rtDesc, std::string_view name) + ramses::RenderTarget* SceneImpl::createRenderTarget(const RenderTargetDescriptionImpl& rtDesc, std::string_view name) { - if (rtDesc.validate() != StatusOK) + ValidationReportImpl report; + rtDesc.validate(report); + if (report.hasError()) { - LOG_ERROR(CONTEXT_CLIENT, "Scene::createRenderTarget failed, RenderTargetDescription is invalid."); + LOG_ERROR(CONTEXT_CLIENT, "Scene::createRenderTarget failed, RenderTargetDescription is invalid: " << report.toString()); return nullptr; } auto pimpl = std::make_unique(*this, name); pimpl->initializeFrameworkData(rtDesc); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - TextureSampler* SceneImpl::createTextureSampler( + ramses::TextureSampler* SceneImpl::createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, @@ -794,7 +782,7 @@ namespace ramses const Texture2D& texture, std::string_view name /*= {}*/) { - if (this != &texture.m_impl.getSceneImpl()) + if (this != &texture.impl().getSceneImpl()) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, texture 2D is not from this scene."); return nullptr; @@ -806,13 +794,13 @@ namespace ramses magSamplingMethod, anisotropyLevel, ERamsesObjectType::Texture2D, - ramses_internal::TextureSampler::ContentType::ClientTexture, - texture.m_impl.getLowlevelResourceHash(), - ramses_internal::InvalidMemoryHandle, + ramses::internal::TextureSampler::ContentType::ClientTexture, + texture.impl().getLowlevelResourceHash(), + ramses::internal::InvalidMemoryHandle, name); } - TextureSampler* SceneImpl::createTextureSampler( + ramses::TextureSampler* SceneImpl::createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureAddressMode wrapRMode, @@ -821,7 +809,7 @@ namespace ramses const Texture3D& texture, std::string_view name /*= {}*/) { - if (this != &texture.m_impl.getSceneImpl()) + if (this != &texture.impl().getSceneImpl()) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, texture 3D is not from this scene."); return nullptr; @@ -830,13 +818,13 @@ namespace ramses return createTextureSamplerImpl( wrapUMode, wrapVMode, wrapRMode, minSamplingMethod, magSamplingMethod, 1u, ERamsesObjectType::Texture3D, - ramses_internal::TextureSampler::ContentType::ClientTexture, - texture.m_impl.getLowlevelResourceHash(), - ramses_internal::InvalidMemoryHandle, + ramses::internal::TextureSampler::ContentType::ClientTexture, + texture.impl().getLowlevelResourceHash(), + ramses::internal::InvalidMemoryHandle, name); } - TextureSampler* SceneImpl::createTextureSampler( + ramses::TextureSampler* SceneImpl::createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, @@ -845,7 +833,7 @@ namespace ramses const TextureCube& texture, std::string_view name /*= {}*/) { - if (this != &texture.m_impl.getSceneImpl()) + if (this != &texture.impl().getSceneImpl()) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, texture Cube is not from this scene."); return nullptr; @@ -854,19 +842,19 @@ namespace ramses return createTextureSamplerImpl( wrapUMode, wrapVMode, ETextureAddressMode::Clamp, minSamplingMethod, magSamplingMethod, anisotropyLevel, ERamsesObjectType::TextureCube, - ramses_internal::TextureSampler::ContentType::ClientTexture, - texture.m_impl.getLowlevelResourceHash(), - ramses_internal::InvalidMemoryHandle, + ramses::internal::TextureSampler::ContentType::ClientTexture, + texture.impl().getLowlevelResourceHash(), + ramses::internal::InvalidMemoryHandle, name); } - TextureSampler* SceneImpl::createTextureSampler( + ramses::TextureSampler* SceneImpl::createTextureSampler( ETextureAddressMode wrapUMode, ETextureAddressMode wrapVMode, ETextureSamplingMethod minSamplingMethod, ETextureSamplingMethod magSamplingMethod , uint32_t anisotropyLevel, - const RenderBuffer& renderBuffer, + const ramses::RenderBuffer& renderBuffer, std::string_view name) { if (renderBuffer.getSampleCount() > 0) @@ -875,13 +863,13 @@ namespace ramses return nullptr; } - if (!containsSceneObject(renderBuffer.m_impl)) + if (!containsSceneObject(renderBuffer.impl())) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, render buffer is not from this scene."); return nullptr; } - if (ERenderBufferAccessMode::WriteOnly == renderBuffer.m_impl.getAccessMode()) + if (ERenderBufferAccessMode::WriteOnly == renderBuffer.impl().getAccessMode()) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, render buffer has access mode write only."); return nullptr; @@ -890,9 +878,9 @@ namespace ramses return createTextureSamplerImpl( wrapUMode, wrapVMode, ETextureAddressMode::Clamp, minSamplingMethod, magSamplingMethod, anisotropyLevel, ERamsesObjectType::RenderBuffer, - ramses_internal::TextureSampler::ContentType::RenderBuffer, - ramses_internal::ResourceContentHash::Invalid(), - renderBuffer.m_impl.getRenderBufferHandle().asMemoryHandle(), + ramses::internal::TextureSampler::ContentType::RenderBuffer, + ramses::internal::ResourceContentHash::Invalid(), + renderBuffer.impl().getRenderBufferHandle().asMemoryHandle(), name); } @@ -905,7 +893,7 @@ namespace ramses const Texture2DBuffer& textureBuffer, std::string_view name) { - if (!containsSceneObject(textureBuffer.m_impl)) + if (!containsSceneObject(textureBuffer.impl())) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, texture2D buffer is not from this scene."); return nullptr; @@ -914,21 +902,21 @@ namespace ramses return createTextureSamplerImpl( wrapUMode, wrapVMode, ETextureAddressMode::Clamp, minSamplingMethod, magSamplingMethod, anisotropyLevel, ERamsesObjectType::Texture2DBuffer, - ramses_internal::TextureSampler::ContentType::TextureBuffer, - ramses_internal::ResourceContentHash::Invalid(), - textureBuffer.m_impl.getTextureBufferHandle().asMemoryHandle(), + ramses::internal::TextureSampler::ContentType::TextureBuffer, + ramses::internal::ResourceContentHash::Invalid(), + textureBuffer.impl().getTextureBufferHandle().asMemoryHandle(), name); } - ramses::TextureSamplerMS* SceneImpl::createTextureSamplerMS(const RenderBuffer& renderBuffer, std::string_view name) + ramses::TextureSamplerMS* SceneImpl::createTextureSamplerMS(const ramses::RenderBuffer& renderBuffer, std::string_view name) { - if (!containsSceneObject(renderBuffer.m_impl)) + if (!containsSceneObject(renderBuffer.impl())) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, render buffer is not from this scene."); return nullptr; } - if (ERenderBufferAccessMode::WriteOnly == renderBuffer.m_impl.getAccessMode()) + if (ERenderBufferAccessMode::WriteOnly == renderBuffer.impl().getAccessMode()) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureSampler failed, render buffer has access mode write only."); return nullptr; @@ -938,9 +926,9 @@ namespace ramses samplerImpl->initializeFrameworkData( {}, ERamsesObjectType::RenderBuffer, - ramses_internal::TextureSampler::ContentType::RenderBufferMS, - ramses_internal::ResourceContentHash::Invalid(), - renderBuffer.m_impl.getRenderBufferHandle().asMemoryHandle()); + ramses::internal::TextureSampler::ContentType::RenderBufferMS, + ramses::internal::ResourceContentHash::Invalid(), + renderBuffer.impl().getRenderBufferHandle().asMemoryHandle()); return &m_objectRegistry.createAndRegisterObject(std::move(samplerImpl)); } @@ -962,24 +950,21 @@ namespace ramses } //According to spec clamp to edge so the only allowed wrap mode - constexpr ETextureAddressMode wrapUMode = ETextureAddressMode::Clamp; - constexpr ETextureAddressMode wrapVMode = ETextureAddressMode::Clamp; - - ramses_internal::TextureSamplerStates samplerStates( - TextureUtils::GetTextureAddressModeInternal(wrapUMode), - TextureUtils::GetTextureAddressModeInternal(wrapVMode), - ramses_internal::EWrapMethod::Clamp, - TextureUtils::GetTextureSamplingInternal(minSamplingMethod), - TextureUtils::GetTextureSamplingInternal(magSamplingMethod) + ramses::internal::TextureSamplerStates samplerStates( + ETextureAddressMode::Clamp, + ETextureAddressMode::Clamp, + ETextureAddressMode::Clamp, + minSamplingMethod, + magSamplingMethod ); auto samplerImpl = std::make_unique(*this, ERamsesObjectType::TextureSamplerExternal, name); samplerImpl->initializeFrameworkData( samplerStates, ERamsesObjectType::TextureSamplerExternal, - ramses_internal::TextureSampler::ContentType::ExternalTexture, - ramses_internal::ResourceContentHash::Invalid(), - ramses_internal::InvalidMemoryHandle); + ramses::internal::TextureSampler::ContentType::ExternalTexture, + ramses::internal::ResourceContentHash::Invalid(), + ramses::internal::InvalidMemoryHandle); return &m_objectRegistry.createAndRegisterObject(std::move(samplerImpl)); } @@ -992,9 +977,9 @@ namespace ramses ETextureSamplingMethod magSamplingMethod, uint32_t anisotropyLevel, ERamsesObjectType samplerType, - ramses_internal::TextureSampler::ContentType contentType, - ramses_internal::ResourceContentHash textureResourceHash, - ramses_internal::MemoryHandle contentHandle, + ramses::internal::TextureSampler::ContentType contentType, + ramses::internal::ResourceContentHash textureResourceHash, + ramses::internal::MemoryHandle contentHandle, std::string_view name /*= {}*/) { if (ETextureSamplingMethod::Nearest != magSamplingMethod && ETextureSamplingMethod::Linear != magSamplingMethod) @@ -1009,22 +994,22 @@ namespace ramses return nullptr; } - ramses_internal::TextureSamplerStates samplerStates( - TextureUtils::GetTextureAddressModeInternal(wrapUMode), - TextureUtils::GetTextureAddressModeInternal(wrapVMode), - TextureUtils::GetTextureAddressModeInternal(wrapRMode), - TextureUtils::GetTextureSamplingInternal(minSamplingMethod), - TextureUtils::GetTextureSamplingInternal(magSamplingMethod), + ramses::internal::TextureSamplerStates samplerStates( + wrapUMode, + wrapVMode, + wrapRMode, + minSamplingMethod, + magSamplingMethod, anisotropyLevel ); auto samplerImpl = std::make_unique(*this, ERamsesObjectType::TextureSampler, name); samplerImpl->initializeFrameworkData(samplerStates, samplerType, contentType, textureResourceHash, contentHandle); - return &m_objectRegistry.createAndRegisterObject(std::move(samplerImpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(samplerImpl)); } - DataObject* SceneImpl::createDataObject(EDataType dataType, std::string_view name /*= {}*/) + DataObject* SceneImpl::createDataObject(ramses::EDataType dataType, std::string_view name /*= {}*/) { if (!IsDataObjectDataType(dataType)) { @@ -1038,140 +1023,157 @@ namespace ramses return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - status_t SceneImpl::createTransformationDataProvider(const Node& node, dataProviderId_t id) + bool SceneImpl::createTransformationDataProvider(const Node& node, dataProviderId_t id) { - if (!containsSceneObject(node.m_impl)) + if (!containsSceneObject(node.impl())) { - return addErrorEntry("Scene::createTransformationDataProvider failed, node is not from this scene."); + getErrorReporting().set("Scene::createTransformationDataProvider failed, node is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); - if (ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); + if (ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) { - return addErrorEntry("Scene::createTransformationDataProvider failed, duplicate data slot id"); + getErrorReporting().set("Scene::createTransformationDataProvider failed, duplicate data slot id", *this); + return false; } - const ramses_internal::NodeHandle nodeHandle = node.m_impl.getNodeHandle(); - if (ramses_internal::DataSlotUtils::HasDataSlotIdForNode(m_scene, nodeHandle)) + const ramses::internal::NodeHandle nodeHandle = node.impl().getNodeHandle(); + if (ramses::internal::DataSlotUtils::HasDataSlotIdForNode(m_scene, nodeHandle)) { - return addErrorEntry("Scene::createTransformationDataProvider failed, Node already has a transformation data slot assigned"); + getErrorReporting().set("Scene::createTransformationDataProvider failed, Node already has a transformation data slot assigned", *this); + return false; } - m_scene.allocateDataSlot({ ramses_internal::EDataSlotType_TransformationProvider, internalDataSlotId, nodeHandle, ramses_internal::DataInstanceHandle::Invalid(), ramses_internal::ResourceContentHash::Invalid(), ramses_internal::TextureSamplerHandle() }); - return StatusOK; + m_scene.allocateDataSlot({ ramses::internal::EDataSlotType::TransformationProvider, internalDataSlotId, nodeHandle, ramses::internal::DataInstanceHandle::Invalid(), ramses::internal::ResourceContentHash::Invalid(), ramses::internal::TextureSamplerHandle() }, {}); + return true; } - status_t SceneImpl::createTransformationDataConsumer(const Node& node, dataConsumerId_t id) + bool SceneImpl::createTransformationDataConsumer(const Node& node, dataConsumerId_t id) { - if (!containsSceneObject(node.m_impl)) + if (!containsSceneObject(node.impl())) { - return addErrorEntry("Scene::createTransformationDataConsumer failed, Group Node is not from this scene."); + getErrorReporting().set("Scene::createTransformationDataConsumer failed, Group Node is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); - if (ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); + if (ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) { - return addErrorEntry("Scene::createTransformationDataConsumer failed, duplicate data slot id"); + getErrorReporting().set("Scene::createTransformationDataConsumer failed, duplicate data slot id", *this); + return false; } - const ramses_internal::NodeHandle nodeHandle = node.m_impl.getNodeHandle(); - if (ramses_internal::DataSlotUtils::HasDataSlotIdForNode(m_scene, nodeHandle)) + const ramses::internal::NodeHandle nodeHandle = node.impl().getNodeHandle(); + if (ramses::internal::DataSlotUtils::HasDataSlotIdForNode(m_scene, nodeHandle)) { - return addErrorEntry("Scene::createTransformationDataConsumer failed, Node already has a transformation data slot assigned"); + getErrorReporting().set("Scene::createTransformationDataConsumer failed, Node already has a transformation data slot assigned", *this); + return false; } - m_scene.allocateDataSlot({ ramses_internal::EDataSlotType_TransformationConsumer, internalDataSlotId, nodeHandle, ramses_internal::DataInstanceHandle::Invalid(), ramses_internal::ResourceContentHash::Invalid(), ramses_internal::TextureSamplerHandle() }); - return StatusOK; + m_scene.allocateDataSlot({ ramses::internal::EDataSlotType::TransformationConsumer, internalDataSlotId, nodeHandle, ramses::internal::DataInstanceHandle::Invalid(), ramses::internal::ResourceContentHash::Invalid(), ramses::internal::TextureSamplerHandle() }, {}); + return true; } - status_t SceneImpl::createDataProvider(const DataObject& dataObject, dataProviderId_t id) + bool SceneImpl::createDataProvider(const DataObject& dataObject, dataProviderId_t id) { - if (!containsSceneObject(dataObject.m_impl)) + if (!containsSceneObject(dataObject.impl())) { - return addErrorEntry("Scene::createDataProvider failed, data object is not from this scene."); + getErrorReporting().set("Scene::createDataProvider failed, data object is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); - if (ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); + if (ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) { - return addErrorEntry("Scene::createDataProvider failed, duplicate data slot id"); + getErrorReporting().set("Scene::createDataProvider failed, duplicate data slot id", *this); + return false; } - const ramses_internal::DataInstanceHandle dataRef = dataObject.m_impl.getDataReference(); - if (ramses_internal::DataSlotUtils::HasDataSlotIdForDataObject(m_scene, dataRef)) + const ramses::internal::DataInstanceHandle dataRef = dataObject.impl().getDataReference(); + if (ramses::internal::DataSlotUtils::HasDataSlotIdForDataObject(m_scene, dataRef)) { - return addErrorEntry("Scene::createDataProvider failed, data object already has a data slot assigned"); + getErrorReporting().set("Scene::createDataProvider failed, data object already has a data slot assigned", *this); + return false; } - m_scene.allocateDataSlot({ ramses_internal::EDataSlotType_DataProvider, internalDataSlotId, ramses_internal::NodeHandle(), dataRef, ramses_internal::ResourceContentHash::Invalid(), ramses_internal::TextureSamplerHandle() }); - return StatusOK; + m_scene.allocateDataSlot({ ramses::internal::EDataSlotType::DataProvider, internalDataSlotId, ramses::internal::NodeHandle(), dataRef, ramses::internal::ResourceContentHash::Invalid(), ramses::internal::TextureSamplerHandle() }, {}); + return true; } - status_t SceneImpl::createDataConsumer(const DataObject& dataObject, dataConsumerId_t id) + bool SceneImpl::createDataConsumer(const DataObject& dataObject, dataConsumerId_t id) { - if (!containsSceneObject(dataObject.m_impl)) + if (!containsSceneObject(dataObject.impl())) { - return addErrorEntry("Scene::createDataConsumer failed, data object is not from this scene."); + getErrorReporting().set("Scene::createDataConsumer failed, data object is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); - if (ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); + if (ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) { - return addErrorEntry("Scene::createDataConsumer failed, duplicate data slot id"); + getErrorReporting().set("Scene::createDataConsumer failed, duplicate data slot id", *this); + return false; } - const ramses_internal::DataInstanceHandle dataRef = dataObject.m_impl.getDataReference(); - if (ramses_internal::DataSlotUtils::HasDataSlotIdForDataObject(m_scene, dataRef)) + const ramses::internal::DataInstanceHandle dataRef = dataObject.impl().getDataReference(); + if (ramses::internal::DataSlotUtils::HasDataSlotIdForDataObject(m_scene, dataRef)) { - return addErrorEntry("Scene::createDataConsumer failed, data object already has a data slot assigned"); + getErrorReporting().set("Scene::createDataConsumer failed, data object already has a data slot assigned", *this); + return false; } - m_scene.allocateDataSlot({ ramses_internal::EDataSlotType_DataConsumer, internalDataSlotId, ramses_internal::NodeHandle(), dataRef, ramses_internal::ResourceContentHash::Invalid(), ramses_internal::TextureSamplerHandle() }); - return StatusOK; + m_scene.allocateDataSlot({ ramses::internal::EDataSlotType::DataConsumer, internalDataSlotId, ramses::internal::NodeHandle(), dataRef, ramses::internal::ResourceContentHash::Invalid(), ramses::internal::TextureSamplerHandle() }, {}); + return true; } - status_t SceneImpl::createTextureProvider(const Texture2D& texture, dataProviderId_t id) + bool SceneImpl::createTextureProvider(const Texture2D& texture, dataProviderId_t id) { - if (this != &texture.m_impl.getSceneImpl()) + if (this != &texture.impl().getSceneImpl()) { - return addErrorEntry("Scene::createTextureProvider failed, texture is not from this scene."); + getErrorReporting().set("Scene::createTextureProvider failed, texture is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); - if (ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); + if (ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) { - return addErrorEntry("Scene::createTextureProvider failed, duplicate data slot id"); + getErrorReporting().set("Scene::createTextureProvider failed, duplicate data slot id", *this); + return false; } - const ramses_internal::ResourceContentHash& textureHash = texture.m_impl.getLowlevelResourceHash(); - if (ramses_internal::DataSlotUtils::HasDataSlotIdForTexture(m_scene, textureHash)) + const ramses::internal::ResourceContentHash& textureHash = texture.impl().getLowlevelResourceHash(); + if (ramses::internal::DataSlotUtils::HasDataSlotIdForTexture(m_scene, textureHash)) { - return addErrorEntry("Scene::createTextureProvider failed, texture already has a data slot assigned in this scene"); + getErrorReporting().set("Scene::createTextureProvider failed, texture already has a data slot assigned in this scene", *this); + return false; } - m_scene.allocateDataSlot({ ramses_internal::EDataSlotType_TextureProvider, internalDataSlotId, ramses_internal::NodeHandle(), ramses_internal::DataInstanceHandle::Invalid(), textureHash, ramses_internal::TextureSamplerHandle() }); - return StatusOK; + m_scene.allocateDataSlot({ ramses::internal::EDataSlotType::TextureProvider, internalDataSlotId, ramses::internal::NodeHandle(), ramses::internal::DataInstanceHandle::Invalid(), textureHash, ramses::internal::TextureSamplerHandle() }, {}); + return true; } - status_t SceneImpl::updateTextureProvider(const Texture2D& texture, dataProviderId_t id) + bool SceneImpl::updateTextureProvider(const Texture2D& texture, dataProviderId_t id) { - if (this != &texture.m_impl.getSceneImpl()) + if (this != &texture.impl().getSceneImpl()) { - return addErrorEntry("Scene::updateTextureProvider failed, texture is not from this scene."); + getErrorReporting().set("Scene::updateTextureProvider failed, texture is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); - if (!ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); + if (!ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) { - return addErrorEntry("Scene::updateTextureProvider failed, provider has not been created before."); + getErrorReporting().set("Scene::updateTextureProvider failed, provider has not been created before.", *this); + return false; } const uint32_t slotHandleCount = m_scene.getDataSlotCount(); - for (ramses_internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) + for (ramses::internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) { if (m_scene.isDataSlotAllocated(slotHandle) && m_scene.getDataSlot(slotHandle).id == internalDataSlotId) { - const ramses_internal::ResourceContentHash& textureHash = texture.m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash& textureHash = texture.impl().getLowlevelResourceHash(); if (m_scene.getDataSlot(slotHandle).attachedTexture != textureHash) { m_scene.setDataSlotTexture(slotHandle, textureHash); @@ -1180,25 +1182,26 @@ namespace ramses } } - return StatusOK; + return true; } - status_t SceneImpl::createTextureConsumer(const TextureSampler& sampler, dataConsumerId_t id) + bool SceneImpl::createTextureConsumer(const ramses::TextureSampler& sampler, dataConsumerId_t id) { - if (sampler.m_impl.getTextureType() != ERamsesObjectType::Texture2D) + if (sampler.impl().getTextureType() != ERamsesObjectType::Texture2D) { - return addErrorEntry("Scene::createTextureConsumer failed, only texture sampler using 2D texture can be used for linking.."); + getErrorReporting().set("Scene::createTextureConsumer failed, only texture sampler using 2D texture can be used for linking..", *this); + return false; } return createTextureConsumerImpl(sampler, id); } - status_t SceneImpl::createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t id) + bool SceneImpl::createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t id) { return createTextureConsumerImpl(sampler, id); } - status_t SceneImpl::createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t id) + bool SceneImpl::createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t id) { // Allow duplicate consumer ID for external samplers (special need for ramses composer). // This will NOT work properly if consumers with same ID get to renderer side and attempt to be linked @@ -1209,41 +1212,46 @@ namespace ramses } template - status_t SceneImpl::createTextureConsumerImpl(const SAMPLER& sampler, dataConsumerId_t id, bool checkDuplicate) + bool SceneImpl::createTextureConsumerImpl(const SAMPLER& sampler, dataConsumerId_t id, bool checkDuplicate) { - if (!containsSceneObject(sampler.m_impl)) + if (!containsSceneObject(sampler.impl())) { - return addErrorEntry("Scene::createTextureConsumer failed, texture sampler is not from this scene."); + getErrorReporting().set("Scene::createTextureConsumer failed, texture sampler is not from this scene.", *this); + return false; } - const ramses_internal::DataSlotId internalDataSlotId(id.getValue()); + const ramses::internal::DataSlotId internalDataSlotId(id.getValue()); if (checkDuplicate) { - if (ramses_internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) - return addErrorEntry("Scene::createTextureConsumer failed, duplicate data slot id"); + if (ramses::internal::DataSlotUtils::HasDataSlotId(m_scene, internalDataSlotId)) + { + getErrorReporting().set("Scene::createTextureConsumer failed, duplicate data slot id", *this); + return false; + } } - const ramses_internal::TextureSamplerHandle& samplerHandle = sampler.m_impl.getTextureSamplerHandle(); - if (ramses_internal::DataSlotUtils::HasDataSlotIdForTextureSampler(m_scene, samplerHandle)) + const ramses::internal::TextureSamplerHandle& samplerHandle = sampler.impl().getTextureSamplerHandle(); + if (ramses::internal::DataSlotUtils::HasDataSlotIdForTextureSampler(m_scene, samplerHandle)) { - return addErrorEntry("Scene::createTextureConsumer failed, texture sampler already has a data slot assigned"); + getErrorReporting().set("Scene::createTextureConsumer failed, texture sampler already has a data slot assigned", *this); + return false; } - m_scene.allocateDataSlot({ ramses_internal::EDataSlotType_TextureConsumer, internalDataSlotId, ramses_internal::NodeHandle(), ramses_internal::DataInstanceHandle::Invalid(), ramses_internal::ResourceContentHash::Invalid(), samplerHandle }); - return StatusOK; + m_scene.allocateDataSlot({ ramses::internal::EDataSlotType::TextureConsumer, internalDataSlotId, ramses::internal::NodeHandle(), ramses::internal::DataInstanceHandle::Invalid(), ramses::internal::ResourceContentHash::Invalid(), samplerHandle }, {}); + return true; } - status_t SceneImpl::setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds) + bool SceneImpl::setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds) { - m_expirationTimestamp = ramses_internal::FlushTime::Clock::time_point(std::chrono::milliseconds{ ptpExpirationTimestampInMilliseconds }); - return StatusOK; + m_expirationTimestamp = ramses::internal::FlushTime::Clock::time_point(std::chrono::milliseconds{ ptpExpirationTimestampInMilliseconds }); + return true; } - status_t SceneImpl::flush(sceneVersionTag_t sceneVersion) + bool SceneImpl::flush(sceneVersionTag_t sceneVersion) { - const auto timestampOfFlushCall = m_sendEffectTimeSync ? getIScene().getEffectTimeSync() : ramses_internal::FlushTime::Clock::now(); + const auto timestampOfFlushCall = m_sendEffectTimeSync ? getIScene().getEffectTimeSync() : ramses::internal::FlushTime::Clock::now(); - LOG_DEBUG_P(CONTEXT_CLIENT, "Scene::flush: sceneVersion {}, prevSceneVersion {}, syncFlushTime {}", sceneVersion, m_nextSceneVersion, ramses_internal::asMilliseconds(timestampOfFlushCall)); + LOG_DEBUG_P(CONTEXT_CLIENT, "Scene::flush: sceneVersion {}, prevSceneVersion {}, syncFlushTime {}", sceneVersion, m_nextSceneVersion, ramses::internal::asMilliseconds(timestampOfFlushCall)); if (m_nextSceneVersion != InvalidSceneVersionTag && sceneVersion == InvalidSceneVersionTag) { @@ -1251,37 +1259,40 @@ namespace ramses m_nextSceneVersion = InvalidSceneVersionTag; } - const ramses_internal::SceneVersionTag sceneVersionInternal(sceneVersion); + const ramses::internal::SceneVersionTag sceneVersionInternal(sceneVersion); - m_commandBuffer.execute(ramses_internal::SceneCommandVisitor(*this)); + m_commandBuffer.execute(ramses::internal::SceneCommandVisitor(*this)); applyHierarchicalVisibility(); - const ramses_internal::FlushTimeInformation flushTimeInfo { m_expirationTimestamp, timestampOfFlushCall, ramses_internal::FlushTime::Clock::getClockType(), m_sendEffectTimeSync }; + const ramses::internal::FlushTimeInformation flushTimeInfo { m_expirationTimestamp, timestampOfFlushCall, ramses::internal::FlushTime::Clock::getClockType(), m_sendEffectTimeSync }; m_sendEffectTimeSync = false; if (!getClientImpl().getClientApplication().flush(m_scene.getSceneId(), flushTimeInfo, sceneVersionInternal)) - return addErrorEntry("Scene::flush: Flushing scene failed, consult logs for more details."); + { + getErrorReporting().set("Scene::flush: Flushing scene failed, consult logs for more details.", *this); + return false; + } getStatisticCollection().statFlushesTriggered.incCounter(1); - return StatusOK; + return true; } - status_t SceneImpl::resetUniformTimeMs() + bool SceneImpl::resetUniformTimeMs() { - const auto now = ramses_internal::FlushTime::Clock::now(); + const auto now = ramses::internal::FlushTime::Clock::now(); const auto nowMs = std::chrono::time_point_cast(now); LOG_INFO_P(CONTEXT_CLIENT, "Scene({})::resetUniformTimeMs: {}", getSceneId(), nowMs.time_since_epoch().count()); m_sendEffectTimeSync = true; getIScene().setEffectTimeSync(now); - return StatusOK; + return true; } int32_t SceneImpl::getUniformTimeMs() const { - return ramses_internal::EffectUniformTime::GetMilliseconds(getIScene().getEffectTimeSync()); + return ramses::internal::EffectUniformTime::GetMilliseconds(getIScene().getEffectTimeSync()); } - RamsesObjectRegistry& SceneImpl::getObjectRegistry() + SceneObjectRegistry& SceneImpl::getObjectRegistry() { return m_objectRegistry; } @@ -1291,16 +1302,6 @@ namespace ramses return &object.getSceneImpl() == this; } - const RamsesObject* SceneImpl::findObjectByName(std::string_view name) const - { - return m_objectRegistry.findObjectByName(name); - } - - RamsesObject* SceneImpl::findObjectByName(std::string_view name) - { - return m_objectRegistry.findObjectByName(name); - } - const SceneObject* SceneImpl::findObjectById(sceneObjectId_t id) const { return m_objectRegistry.findObjectById(id); @@ -1311,7 +1312,7 @@ namespace ramses return m_objectRegistry.findObjectById(id); } - const RamsesObjectRegistry& SceneImpl::getObjectRegistry() const + const SceneObjectRegistry& SceneImpl::getObjectRegistry() const { return m_objectRegistry; } @@ -1320,19 +1321,19 @@ namespace ramses void SceneImpl::removeObjectFromAllContainers(const OBJECT& object) { const ERamsesObjectType type = TYPE_ID_OF_RAMSES_OBJECT::ID; - RamsesObjectRegistryIterator iterator(m_objectRegistry, type); + SceneObjectRegistryIterator iterator(m_objectRegistry, type); CONTAINER* container = nullptr; while ((container = iterator.getNextNonConst()) != nullptr) { - container->m_impl.removeIfContained(object.m_impl); + container->impl().removeIfContained(object.impl()); } } void SceneImpl::removeAllDataSlotsForNode(const Node& node) { - const ramses_internal::NodeHandle nodeHandle = node.m_impl.getNodeHandle(); + const ramses::internal::NodeHandle nodeHandle = node.impl().getNodeHandle(); const uint32_t slotHandleCount = m_scene.getDataSlotCount(); - for (ramses_internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) + for (ramses::internal::DataSlotHandle slotHandle(0u); slotHandle < slotHandleCount; slotHandle++) { if (m_scene.isDataSlotAllocated(slotHandle) && m_scene.getDataSlot(slotHandle).attachedNode == nodeHandle) { @@ -1341,19 +1342,19 @@ namespace ramses } } - RenderPass* SceneImpl::createRenderPassInternal(std::string_view name) + ramses::RenderPass* SceneImpl::createRenderPassInternal(std::string_view name) { auto pimpl = std::make_unique(*this, name); pimpl->initializeFrameworkData(); - return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - bool SceneImpl::cameraIsAssignedToRenderPasses(const Camera& camera) + bool SceneImpl::cameraIsAssignedToRenderPasses(const ramses::Camera& camera) { - RamsesObjectRegistryIterator iterator(m_objectRegistry, ERamsesObjectType::RenderPass); - const RenderPass* renderPass = nullptr; - while ((renderPass = iterator.getNext()) != nullptr) + SceneObjectRegistryIterator iterator(m_objectRegistry, ERamsesObjectType::RenderPass); + const ramses::RenderPass* renderPass = nullptr; + while ((renderPass = iterator.getNext()) != nullptr) { if (renderPass->getCamera() == &camera) { @@ -1381,7 +1382,7 @@ namespace ramses const ERamsesObjectType nodeType = node.getType(); if (nodeType == ERamsesObjectType::MeshNode) { - MeshNodeImpl& meshNode = static_cast(node); + auto& meshNode = static_cast(node); const EVisibilityMode currentVisibility = meshNode.getFlattenedVisibility(); if (currentVisibility != visibilityToApply) @@ -1457,7 +1458,7 @@ namespace ramses m_nextSceneVersion = sceneVersion; } - ArrayBuffer* SceneImpl::createArrayBuffer(EDataType dataType, uint32_t maxNumElements, std::string_view name) + ArrayBuffer* SceneImpl::createArrayBuffer(ramses::EDataType dataType, uint32_t maxNumElements, std::string_view name) { if (!IsArrayResourceDataType(dataType)) { @@ -1480,14 +1481,14 @@ namespace ramses } // More than one mips have size 1x1 -> error - const size_t maxMipCount = ramses_internal::TextureMathUtils::GetMipLevelCount(width, height, 1u); + const size_t maxMipCount = ramses::internal::TextureMathUtils::GetMipLevelCount(width, height, 1u); if (mipLevels > maxMipCount) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTexture2DBuffer failed, mipLevels too large for the provided texture size."); return nullptr; } - ramses_internal::MipMapDimensions mipMapSizes; + ramses::internal::MipMapDimensions mipMapSizes; uint32_t currentWidth = width; uint32_t currentHeight = height; @@ -1505,7 +1506,7 @@ namespace ramses return &m_objectRegistry.createAndRegisterObject(std::move(pimpl)); } - ramses_internal::StatisticCollectionScene& SceneImpl::getStatisticCollection() + ramses::internal::StatisticCollectionScene& SceneImpl::getStatisticCollection() { return m_scene.getStatisticCollection(); } @@ -1514,13 +1515,13 @@ namespace ramses { if (!referencedScene.isValid()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene::createSceneReference: cannot reference a scene with invalid scene ID."); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene::createSceneReference: cannot reference a scene with invalid scene ID."); return nullptr; } if (referencedScene == getSceneId()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene::createSceneReference: cannot self reference."); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene::createSceneReference: cannot self reference."); return nullptr; } @@ -1528,59 +1529,77 @@ namespace ramses { if (getClientImpl().findSceneReference(scene->getSceneId(), referencedScene) != nullptr) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene::createSceneReference: there is already a SceneReference with sceneId " + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene::createSceneReference: there is already a SceneReference with sceneId " << referencedScene << " in master scene " << scene->getSceneId() << ", cannot create another one"); return nullptr; } } - LOG_INFO_P(ramses_internal::CONTEXT_CLIENT, "Scene::createSceneReference: creating scene reference (master {} / ref {})", getSceneId(), referencedScene); + LOG_INFO_P(ramses::internal::CONTEXT_CLIENT, "Scene::createSceneReference: creating scene reference (master {} / ref {})", getSceneId(), referencedScene); auto pimpl = std::make_unique(*this, name); pimpl->initializeFrameworkData(referencedScene); - auto& sr = m_objectRegistry.createAndRegisterObject(std::move(pimpl)); + auto& sr = m_objectRegistry.createAndRegisterObject(std::move(pimpl)); m_sceneReferences.put(referencedScene, &sr); return &sr; } - status_t SceneImpl::linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId) + bool SceneImpl::linkData(ramses::SceneReference* providerReference, dataProviderId_t providerId, ramses::SceneReference* consumerReference, dataConsumerId_t consumerId) { if (!providerReference && !consumerReference) - return addErrorEntry("Scene::linkData: can't link an object to another object in the same scene"); + { + getErrorReporting().set("Scene::linkData: can't link an object to another object in the same scene", *this); + return false; + } if (consumerReference == providerReference) - return addErrorEntry("Scene::linkData: can't link an object to another object in the same scene reference"); + { + getErrorReporting().set("Scene::linkData: can't link an object to another object in the same scene reference", *this); + return false; + } - if ((providerReference != nullptr && providerReference->m_impl.getSceneImpl().getSceneId() != getSceneId()) || - (consumerReference != nullptr && consumerReference->m_impl.getSceneImpl().getSceneId() != getSceneId())) - return addErrorEntry("Scene::linkData: can't link to object of a scene reference with a different master scene"); + if ((providerReference != nullptr && providerReference->impl().getSceneImpl().getSceneId() != getSceneId()) || + (consumerReference != nullptr && consumerReference->impl().getSceneImpl().getSceneId() != getSceneId())) + { + getErrorReporting().set("Scene::linkData: can't link to object of a scene reference with a different master scene", *this); + return false; + } - if (providerReference && providerReference->m_impl.getReportedState() < RendererSceneState::Ready) - return addErrorEntry("Scene::linkData: Provider SceneReference state has to be at least Ready"); + if (providerReference && providerReference->impl().getReportedState() < RendererSceneState::Ready) + { + getErrorReporting().set("Scene::linkData: Provider SceneReference state has to be at least Ready", *this); + return false; + } - if (consumerReference && consumerReference->m_impl.getReportedState() < RendererSceneState::Ready) - return addErrorEntry("Scene::linkData: Consumer SceneReference state has to be at least Ready"); + if (consumerReference && consumerReference->impl().getReportedState() < RendererSceneState::Ready) + { + getErrorReporting().set("Scene::linkData: Consumer SceneReference state has to be at least Ready", *this); + return false; + } - const auto providerScene = (providerReference ? providerReference->m_impl.getSceneReferenceHandle() : ramses_internal::SceneReferenceHandle{}); - const auto consumerScene = (consumerReference ? consumerReference->m_impl.getSceneReferenceHandle() : ramses_internal::SceneReferenceHandle{}); - getIScene().linkData(providerScene, ramses_internal::DataSlotId{ providerId.getValue() }, consumerScene, ramses_internal::DataSlotId{ consumerId.getValue() }); + const auto providerScene = (providerReference ? providerReference->impl().getSceneReferenceHandle() : ramses::internal::SceneReferenceHandle{}); + const auto consumerScene = (consumerReference ? consumerReference->impl().getSceneReferenceHandle() : ramses::internal::SceneReferenceHandle{}); + getIScene().linkData(providerScene, ramses::internal::DataSlotId{ providerId.getValue() }, consumerScene, ramses::internal::DataSlotId{ consumerId.getValue() }); - return StatusOK; + return true; } - status_t SceneImpl::unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId) + bool SceneImpl::unlinkData(ramses::SceneReference* consumerReference, dataConsumerId_t consumerId) { - if (consumerReference != nullptr && consumerReference->m_impl.getSceneImpl().getSceneId() != getSceneId()) - return addErrorEntry("Scene::unlinkData: can't unlink object of a scene reference with a different master scene"); + if (consumerReference != nullptr && consumerReference->impl().getSceneImpl().getSceneId() != getSceneId()) + { + getErrorReporting().set("Scene::unlinkData: can't unlink object of a scene reference with a different master scene"); + return false; + } - const auto consumerScene = (consumerReference ? consumerReference->m_impl.getSceneReferenceHandle() : ramses_internal::SceneReferenceHandle{}); - getIScene().unlinkData(consumerScene, ramses_internal::DataSlotId{ consumerId.getValue() }); + const auto consumerScene = (consumerReference ? consumerReference->impl().getSceneReferenceHandle() : ramses::internal::SceneReferenceHandle{}); + getIScene().unlinkData(consumerScene, ramses::internal::DataSlotId{ consumerId.getValue() }); - return StatusOK; + return true; } - SceneReference* SceneImpl::getSceneReference(sceneId_t referencedSceneId) + ramses::SceneReference* SceneImpl::getSceneReference(sceneId_t referencedSceneId) { auto it = m_sceneReferences.find(referencedSceneId); return (it != m_sceneReferences.end()) ? it->value : nullptr; @@ -1598,15 +1617,15 @@ namespace ramses } template - ArrayResource* SceneImpl::createArrayResource(uint32_t numElements, const T* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name) + ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t numElements, const T* arrayData, std::string_view name) { if (0u == numElements || nullptr == arrayData) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "Scene::createArrayResource: Array resource must have element count > 0 and data must not be nullptr!"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "Scene::createArrayResource: Array resource must have element count > 0 and data must not be nullptr!"); return nullptr; } - ramses_internal::ManagedResource res = getClientImpl().createManagedArrayResource(numElements, GetEDataType(), arrayData, cacheFlag, name); + ramses::internal::ManagedResource res = getClientImpl().createManagedArrayResource(numElements, GetEDataType(), arrayData, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createArrayResource: failed to create managed array resource"); @@ -1615,25 +1634,25 @@ namespace ramses return createHLArrayResource(res, name); } - ArrayResource* SceneImpl::createHLArrayResource(ramses_internal::ManagedResource const& resource, std::string_view name) + ramses::ArrayResource* SceneImpl::createHLArrayResource(ramses::internal::ManagedResource const& resource, std::string_view name) { - assert(resource->getTypeID() == ramses_internal::EResourceType_IndexArray || - resource->getTypeID() == ramses_internal::EResourceType_VertexArray); + assert(resource->getTypeID() == ramses::internal::EResourceType::IndexArray || + resource->getTypeID() == ramses::internal::EResourceType::VertexArray); - const auto arrayRes = resource->convertTo(); - ramses_internal::ResourceHashUsage usage = getClientImpl().getClientApplication().getHashUsage(arrayRes->getHash()); + const auto arrayRes = resource->convertTo(); + ramses::internal::ResourceHashUsage usage = getClientImpl().getClientApplication().getHashUsage(arrayRes->getHash()); auto pimpl = std::make_unique(usage, *this, name); pimpl->initializeFromFrameworkData(arrayRes->getElementCount(), DataTypeUtils::ConvertDataTypeFromInternal(arrayRes->getElementType())); - return ®isterCreatedResourceObject(std::move(pimpl)); + return ®isterCreatedResourceObject(std::move(pimpl)); } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture2D* SceneImpl::createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name) + Texture2D* SceneImpl::createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) { - ramses_internal::ManagedResource res = getClientImpl().createManagedTexture( - ramses_internal::EResourceType_Texture2D, width, height, 1u, format, static_cast(mipMapCount), mipLevelData, generateMipChain, swizzle, cacheFlag, name); + ramses::internal::ManagedResource res = getClientImpl().createManagedTexture( + ramses::internal::EResourceType::Texture2D, width, height, 1u, format, static_cast(mipMapCount), mipLevelData, generateMipChain, swizzle, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTexture2D: failed to create managed Texture2D resource"); @@ -1642,12 +1661,12 @@ namespace ramses return createHLTexture2D(res, name); } - Texture2D* SceneImpl::createHLTexture2D(ramses_internal::ManagedResource const& resource, std::string_view name) + Texture2D* SceneImpl::createHLTexture2D(ramses::internal::ManagedResource const& resource, std::string_view name) { - assert(resource->getTypeID() == ramses_internal::EResourceType_Texture2D); + assert(resource->getTypeID() == ramses::internal::EResourceType::Texture2D); - const auto texRes = resource->convertTo(); - ramses_internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); + const auto texRes = resource->convertTo(); + ramses::internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); auto pimpl = std::make_unique(hashUsage, *this, name); pimpl->initializeFromFrameworkData(texRes->getWidth(), texRes->getHeight(), @@ -1658,10 +1677,10 @@ namespace ramses } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture3D* SceneImpl::createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, resourceCacheFlag_t cacheFlag, std::string_view name) + Texture3D* SceneImpl::createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, std::string_view name) { - ramses_internal::ManagedResource res = getClientImpl().createManagedTexture( - ramses_internal::EResourceType_Texture3D, width, height, depth, format, static_cast(mipMapCount), mipLevelData, generateMipChain, {}, cacheFlag, name); + ramses::internal::ManagedResource res = getClientImpl().createManagedTexture( + ramses::internal::EResourceType::Texture3D, width, height, depth, format, static_cast(mipMapCount), mipLevelData, generateMipChain, {}, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTexture3D: failed to create managed Texture3D resource"); @@ -1670,11 +1689,11 @@ namespace ramses return createHLTexture3D(res, name); } - Texture3D* SceneImpl::createHLTexture3D(ramses_internal::ManagedResource const& resource, std::string_view name) + Texture3D* SceneImpl::createHLTexture3D(ramses::internal::ManagedResource const& resource, std::string_view name) { - assert(resource->getTypeID() == ramses_internal::EResourceType_Texture3D); - const auto texRes = resource->convertTo(); - ramses_internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); + assert(resource->getTypeID() == ramses::internal::EResourceType::Texture3D); + const auto texRes = resource->convertTo(); + ramses::internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); auto pimpl = std::make_unique(hashUsage, *this, name); pimpl->initializeFromFrameworkData(texRes->getWidth(), texRes->getHeight(), texRes->getDepth(), @@ -1684,10 +1703,10 @@ namespace ramses } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - TextureCube* SceneImpl::createTextureCube(uint32_t size, ETextureFormat format, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name) + TextureCube* SceneImpl::createTextureCube(uint32_t size, ETextureFormat format, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) { - ramses_internal::ManagedResource res = getClientImpl().createManagedTexture( - ramses_internal::EResourceType_TextureCube, size, 1u, 1u, format, static_cast(mipMapCount), mipLevelData, generateMipChain, swizzle, cacheFlag, name); + ramses::internal::ManagedResource res = getClientImpl().createManagedTexture( + ramses::internal::EResourceType::TextureCube, size, 1u, 1u, format, static_cast(mipMapCount), mipLevelData, generateMipChain, swizzle, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureCube: failed to create managed TextureCube resource"); @@ -1696,12 +1715,12 @@ namespace ramses return createHLTextureCube(res, name); } - TextureCube* SceneImpl::createHLTextureCube(ramses_internal::ManagedResource const& resource, std::string_view name) + TextureCube* SceneImpl::createHLTextureCube(ramses::internal::ManagedResource const& resource, std::string_view name) { - assert(resource->getTypeID() == ramses_internal::EResourceType_TextureCube); + assert(resource->getTypeID() == ramses::internal::EResourceType::TextureCube); - const auto texRes = resource->convertTo(); - ramses_internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); + const auto texRes = resource->convertTo(); + ramses::internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); auto pimpl = std::make_unique(hashUsage, *this, name); pimpl->initializeFromFrameworkData(texRes->getWidth(), @@ -1711,9 +1730,9 @@ namespace ramses return ®isterCreatedResourceObject(std::move(pimpl)); } - Effect* SceneImpl::createEffect(const EffectDescription& effectDesc, resourceCacheFlag_t cacheFlag, std::string_view name) + Effect* SceneImpl::createEffect(const EffectDescription& effectDesc, std::string_view name) { - ramses_internal::ManagedResource res = getClientImpl().createManagedEffect(effectDesc, cacheFlag, name, m_effectErrorMessages); + ramses::internal::ManagedResource res = getClientImpl().createManagedEffect(effectDesc, name, m_effectErrorMessages); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createEffect: failed to create managed effect resource: " << m_effectErrorMessages); @@ -1723,12 +1742,12 @@ namespace ramses return createHLEffect(res, name); } - Effect* SceneImpl::createHLEffect(ramses_internal::ManagedResource const& resource, std::string_view name) + Effect* SceneImpl::createHLEffect(ramses::internal::ManagedResource const& resource, std::string_view name) { - assert(resource->getTypeID() == ramses_internal::EResourceType_Effect); + assert(resource->getTypeID() == ramses::internal::EResourceType::Effect); - const auto effectRes = resource->convertTo(); - ramses_internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); + const auto effectRes = resource->convertTo(); + ramses::internal::ResourceHashUsage hashUsage = getClientImpl().getClientApplication().getHashUsage(resource->getHash()); auto pimpl = std::make_unique(hashUsage, *this, name); pimpl->initializeFromFrameworkData(effectRes->getUniformInputs(), effectRes->getAttributeInputs(), effectRes->getGeometryShaderInputType()); @@ -1759,79 +1778,125 @@ namespace ramses return (range.first != range.second) ? range.first->second : nullptr; } - Resource* SceneImpl::scanForResourceWithHash(ramses_internal::ResourceContentHash hash) const + Resource* SceneImpl::scanForResourceWithHash(ramses::internal::ResourceContentHash hash) const { for (const auto& res : m_resources) { - if (hash == res.second->m_impl.getLowlevelResourceHash()) + if (hash == res.second->impl().getLowlevelResourceHash()) return res.second; } return nullptr; } - status_t SceneImpl::writeSceneObjectsToStream(ramses_internal::IOutputStream& outputStream) const + bool SceneImpl::writeSceneObjectsToStream(ramses::internal::IOutputStream& outputStream, const SaveFileConfigImpl& saveConfig) const { - ramses_internal::ScenePersistation::WriteSceneMetadataToStream(outputStream, getIScene()); - ramses_internal::ScenePersistation::WriteSceneToStream(outputStream, getIScene()); + ramses::internal::ScenePersistation::WriteSceneMetadataToStream(outputStream, getIScene()); + ramses::internal::ScenePersistation::WriteSceneToStream(outputStream, getIScene()); - SerializationContext serializationContext; + SerializationContext serializationContext{saveConfig}; return serialize(outputStream, serializationContext); } - status_t SceneImpl::saveToFile(std::string_view fileName, bool compress) const + bool SceneImpl::serialize(std::vector& outputBuffer, const SaveFileConfigImpl& config) const { - if (fileName.empty()) - return addErrorEntry("Scene::saveToFile failed: empty filename"); + if (config.getValidationEnabled()) + { + ValidationReportImpl report; + validate(report); - LOG_INFO_P(CONTEXT_CLIENT, "Scene::saveToFile: filename '{}', compress {}", fileName, compress); + for (const auto& msg : report.getIssues()) + { + if (msg.object) + { + LOG_WARN_P(CONTEXT_CLIENT, "[{}] {}", msg.object->impl().getIdentificationString(), msg.message); + } + else + { + LOG_WARN_P(CONTEXT_CLIENT, "{}", msg.message); + } + } - ramses_internal::File outputFile(fileName); - ramses_internal::BinaryFileOutputStream outputStream(outputFile); - if (!outputFile.isOpen()) - return addErrorEntry(fmt::format("Scene::saveToFile failed, could not open file for writing: '{}'", fileName)); + if (report.hasError()) + { + getErrorReporting().set( + "Failed to saveToFile() because validation errors were encountered! " + "Refer to the documentation of saveToFile() for details how to address these gracefully."); + return false; + } + } - const EFeatureLevel featureLevel = m_hlClient.m_impl.getFramework().getFeatureLevel(); - ramses_internal::RamsesVersion::WriteToStream(outputStream, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, featureLevel); + ramses::internal::BinaryOutputStream outputStream; + const EFeatureLevel featureLevel = m_hlClient.impl().getFramework().getFeatureLevel(); + ramses::internal::RamsesVersion::WriteToStream(outputStream, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, featureLevel); + outputStream << config.getExporterVersion(); + outputStream << config.getMetadataString(); - size_t bytesForVersion = 0; - if (!outputFile.getPos(bytesForVersion)) - return addErrorEntry(fmt::format("Scene::saveToFile failed, error getting save file position: '{}'", fileName)); + const auto headerOffset = outputStream.getSize(); // reserve space for offset to SceneObjects and LL-Objects - const uint64_t bytesForOffsets = sizeof(uint64_t) * 2u; - const uint64_t offsetSceneObjectsStart = bytesForVersion + bytesForOffsets; - - if (!outputFile.seek(static_cast(offsetSceneObjectsStart), ramses_internal::File::SeekOrigin::BeginningOfFile)) - return addErrorEntry(fmt::format("Scene::saveToFile failed, error seeking file: '{}'", fileName)); - - const status_t status = writeSceneObjectsToStream(outputStream); - - size_t offsetLLResourcesStart = 0; - if (!outputFile.getPos(offsetLLResourcesStart)) - return addErrorEntry(fmt::format("Scene::saveToFile failed, error getting save file position: '{}'", fileName)); + outputStream << static_cast(0); + outputStream << static_cast(0); + const uint64_t offsetSceneObjectsStart = outputStream.getSize(); + const auto status = writeSceneObjectsToStream(outputStream, config); + const auto offsetLLResourcesStart = outputStream.getSize(); ResourceObjects resources; resources.reserve(m_resources.size()); for (auto const& res : m_resources) resources.push_back(res.second); - getClientImpl().writeLowLevelResourcesToStream(resources, outputStream, compress); - - if (!outputFile.seek(bytesForVersion, ramses_internal::File::SeekOrigin::BeginningOfFile)) - return addErrorEntry(fmt::format("Scene::saveToFile failed, error seeking file: '{}'", fileName)); + getClientImpl().writeLowLevelResourcesToStream(resources, outputStream, config.getCompressionEnabled()); + outputBuffer = outputStream.release(); outputStream << static_cast(offsetSceneObjectsStart); outputStream << static_cast(offsetLLResourcesStart); + const auto offsets = outputStream.release(); + + assert(offsets.size() == 2*sizeof(uint64_t)); + std::copy(offsets.begin(), offsets.end(), outputBuffer.begin() + static_cast(headerOffset)); + + return status; + } + + bool SceneImpl::saveToFile(std::string_view fileName, const SaveFileConfigImpl& config) const + { + if (fileName.empty()) + { + getErrorReporting().set("Scene::saveToFile failed: empty filename", *this); + return false; + } + + LOG_INFO_P(CONTEXT_CLIENT, "Scene::saveToFile: filename '{}', compress {}", fileName, config.getCompressionEnabled()); + + std::vector outputBuffer; + if (!serialize(outputBuffer, config)) + return false; + + ramses::internal::File outputFile(fileName); + if (!outputFile.open(ramses::internal::File::Mode::WriteNewBinary)) + { + getErrorReporting().set(fmt::format("Scene::saveToFile failed, could not open file for writing: '{}'", fileName), *this); + return false; + } + + if (!outputFile.write(outputBuffer.data(), outputBuffer.size())) + { + getErrorReporting().set(fmt::format("Scene::saveToFile failed, write failed: '{}'", fileName), *this); + return false; + } if (!outputFile.close()) - return addErrorEntry(fmt::format("Scene::saveToFile failed, close file failed: '{}'", fileName)); + { + getErrorReporting().set(fmt::format("Scene::saveToFile failed, close file failed: '{}'", fileName), *this); + return false; + } - LOG_INFO_P(ramses_internal::CONTEXT_CLIENT, "Scene::saveToFile: done writing '{}'", fileName); + LOG_INFO_P(ramses::internal::CONTEXT_CLIENT, "Scene::saveToFile: done writing '{}'", fileName); - return status; + return true; } - void SceneImpl::setSceneFileHandle(ramses_internal::SceneFileHandle handle) + void SceneImpl::setSceneFileHandle(ramses::internal::SceneFileHandle handle) { m_sceneFileHandle = handle; } @@ -1843,10 +1908,10 @@ namespace ramses getClientImpl().getClientApplication().removeResourceFile(m_sceneFileHandle); LOG_INFO(CONTEXT_CLIENT, "SceneImpl::closeSceneFile closed: " << m_sceneFileHandle); - m_sceneFileHandle = ramses_internal::SceneFileHandle::Invalid(); + m_sceneFileHandle = ramses::internal::SceneFileHandle::Invalid(); } - ramses_internal::SceneFileHandle SceneImpl::getSceneFileHandle() const + ramses::internal::SceneFileHandle SceneImpl::getSceneFileHandle() const { return m_sceneFileHandle; } @@ -1872,11 +1937,11 @@ namespace ramses m_resources.insert({ resourceWithNewId.getResourceId(), &resourceWithNewId }); } - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const uint16_t*, resourceCacheFlag_t, std::string_view); - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const uint32_t*, resourceCacheFlag_t, std::string_view); - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const float*, resourceCacheFlag_t, std::string_view); - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const vec2f*, resourceCacheFlag_t, std::string_view); - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const vec3f*, resourceCacheFlag_t, std::string_view); - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const vec4f*, resourceCacheFlag_t, std::string_view); - template ArrayResource* SceneImpl::createArrayResource(uint32_t, const Byte*, resourceCacheFlag_t, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const uint16_t*, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const uint32_t*, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const float*, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const vec2f*, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const vec3f*, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const vec4f*, std::string_view); + template ramses::ArrayResource* SceneImpl::createArrayResource(uint32_t, const std::byte*, std::string_view); } diff --git a/client/ramses-client/impl/SceneImpl.h b/src/client/impl/SceneImpl.h similarity index 52% rename from client/ramses-client/impl/SceneImpl.h rename to src/client/impl/SceneImpl.h index 1f065bb00..7130f78ca 100644 --- a/client/ramses-client/impl/SceneImpl.h +++ b/src/client/impl/SceneImpl.h @@ -6,71 +6,58 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEIMPL_H -#define RAMSES_SCENEIMPL_H +#pragma once -#include "ramses-client-api/EScenePublicationMode.h" -#include "ramses-client-api/EVisibilityMode.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/SceneReference.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-framework-api/EDataType.h" +#include "ramses/framework/EScenePublicationMode.h" +#include "ramses/framework/EVisibilityMode.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/SceneReference.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/framework/EDataType.h" +#include "ramses/framework/Issue.h" // internal -#include "ClientObjectImpl.h" -#include "RamsesObjectRegistry.h" -#include "ClientCommands/SceneCommandBuffer.h" -#include "AppearanceImpl.h" -#include "Components/FlushTimeInformation.h" -#include "Components/SceneFileHandle.h" +#include "impl/ClientObjectImpl.h" +#include "impl/SceneObjectRegistry.h" +#include "impl/AppearanceImpl.h" +#include "internal/ClientCommands/SceneCommandBuffer.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/SceneFileHandle.h" // ramses framework -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/EDataSlotType.h" -#include "SceneAPI/TextureSampler.h" -#include "Resource/ResourceTypes.h" -#include "Components/ManagedResource.h" - -#include "Collections/Pair.h" -#include "Utils/StatisticCollection.h" -#include "RamsesFrameworkTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/EDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" +#include "internal/Components/ManagedResource.h" + +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "impl/RamsesFrameworkTypesImpl.h" #include #include #include -namespace ramses_internal -{ - class TextureResource; - class IScene; - class ClientScene; - class EffectResource; -} - namespace ramses { class Appearance; - class RamsesClientImpl; class Camera; class PerspectiveCamera; class OrthographicCamera; - class Appearance; class Node; class Effect; class MeshNode; - class GeometryBinding; + class Geometry; class AttributeInput; - class NodeImpl; class RenderGroup; class RenderPass; class RenderBuffer; class RenderTarget; class DataObject; - class SceneConfigImpl; - class RenderTargetDescriptionImpl; class BlitPass; class PickableObject; class TextureSampler; @@ -80,49 +67,63 @@ namespace ramses class Texture3D; class TextureCube; class ArrayBuffer; - class ArrayBufferImpl; class Texture2DBuffer; - class Texture2DBufferImpl; class RamsesClient; class ArrayResource; - class Texture2D; - class Texture3D; - class TextureCube; - class Effect; class EffectDescription; class Resource; + class LogicEngine; +} + +namespace ramses::internal +{ + class TextureResource; + class IScene; + class ClientScene; + class EffectResource; + class RamsesClientImpl; + class NodeImpl; + class SceneConfigImpl; + class RenderTargetDescriptionImpl; + class ArrayBufferImpl; + class Texture2DBufferImpl; + class SaveFileConfigImpl; class SceneImpl final : public ClientObjectImpl { public: - SceneImpl(ramses_internal::ClientScene& scene, const SceneConfigImpl& sceneConfig, RamsesClient& ramsesClient); + SceneImpl(ramses::internal::ClientScene& scene, const SceneConfigImpl& sceneConfig, RamsesClient& ramsesClient); ~SceneImpl() override; - void initializeFrameworkData(); - void deinitializeFrameworkData() override; - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - status_t validate() const override; + void initializeFrameworkData(); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + + void onValidate(ValidationReportImpl& report) const override; - status_t publish(EScenePublicationMode publicationMode = EScenePublicationMode::LocalAndRemote); - status_t unpublish(); - bool isPublished() const; - sceneId_t getSceneId() const; + bool publish(EScenePublicationMode publicationMode = EScenePublicationMode::LocalAndRemote); + bool unpublish(); + bool isPublished() const; + sceneId_t getSceneId() const; EScenePublicationMode getPublicationModeSetFromSceneConfig() const; - status_t saveToFile(std::string_view fileName, bool compress) const; + bool serialize(std::vector& outputBuffer, const SaveFileConfigImpl& config) const; + bool saveToFile(std::string_view fileName, const SaveFileConfigImpl& config) const; + + LogicEngine* createLogicEngine(std::string_view name); PerspectiveCamera* createPerspectiveCamera(std::string_view name); OrthographicCamera* createOrthographicCamera(std::string_view name); Appearance* createAppearance(const Effect& effect, std::string_view name); - GeometryBinding* createGeometryBinding(const Effect& effect, std::string_view name); + Geometry* createGeometry(const Effect& effect, std::string_view name); Node* createNode(std::string_view name); MeshNode* createMeshNode(std::string_view name); ramses::RenderGroup* createRenderGroup(std::string_view name); ramses::RenderPass* createRenderPass(std::string_view name); ramses::BlitPass* createBlitPass(const RenderBuffer& sourceRenderBuffer, const RenderBuffer& destinationRenderBuffer, std::string_view name); ramses::PickableObject* createPickableObject(const ArrayBuffer& geometryBuffer, const pickableObjectId_t id, std::string_view name); - ramses::RenderBuffer* createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferType bufferType, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount, std::string_view name); + ramses::RenderBuffer* createRenderBuffer(uint32_t width, uint32_t height, ERenderBufferFormat bufferFormat, ERenderBufferAccessMode accessMode, uint32_t sampleCount, std::string_view name); ramses::RenderTarget* createRenderTarget(const RenderTargetDescriptionImpl& rtDesc, std::string_view name); ramses::TextureSampler* createTextureSampler( @@ -177,71 +178,71 @@ namespace ramses ETextureSamplingMethod magSamplingMethod, std::string_view name); - DataObject* createDataObject(EDataType dataType, std::string_view name); + DataObject* createDataObject(ramses::EDataType dataType, std::string_view name); - status_t createTransformationDataProvider(const Node& node, dataProviderId_t id); - status_t createTransformationDataConsumer(const Node& node, dataConsumerId_t id); - status_t createDataProvider(const DataObject& dataObject, dataProviderId_t id); - status_t createDataConsumer(const DataObject& dataObject, dataConsumerId_t id); - status_t createTextureProvider(const Texture2D& texture, dataProviderId_t id); - status_t updateTextureProvider(const Texture2D& texture, dataProviderId_t id); - status_t createTextureConsumer(const TextureSampler& sampler, dataConsumerId_t id); - status_t createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t id); - status_t createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t id); + bool createTransformationDataProvider(const Node& node, dataProviderId_t id); + bool createTransformationDataConsumer(const Node& node, dataConsumerId_t id); + bool createDataProvider(const DataObject& dataObject, dataProviderId_t id); + bool createDataConsumer(const DataObject& dataObject, dataConsumerId_t id); + bool createTextureProvider(const Texture2D& texture, dataProviderId_t id); + bool updateTextureProvider(const Texture2D& texture, dataProviderId_t id); + bool createTextureConsumer(const ramses::TextureSampler& sampler, dataConsumerId_t id); + bool createTextureConsumer(const TextureSamplerMS& sampler, dataConsumerId_t id); + bool createTextureConsumer(const TextureSamplerExternal& sampler, dataConsumerId_t id); - ArrayBuffer* createArrayBuffer(EDataType dataType, uint32_t maxNumElements, std::string_view name); - Texture2DBuffer* createTexture2DBuffer (size_t mipLevels, uint32_t width, uint32_t height, ETextureFormat textureFormat, std::string_view name); + ArrayBuffer* createArrayBuffer(ramses::EDataType dataType, uint32_t maxNumElements, std::string_view name); + Texture2DBuffer* createTexture2DBuffer (size_t mipLevels, uint32_t width, uint32_t height, ETextureFormat textureFormat, std::string_view name); SceneReference* createSceneReference(sceneId_t referencedScene, std::string_view name); - status_t linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId); - status_t unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId); + bool linkData(SceneReference* providerReference, dataProviderId_t providerId, SceneReference* consumerReference, dataConsumerId_t consumerId); + bool unlinkData(SceneReference* consumerReference, dataConsumerId_t consumerId); - status_t destroy(SceneObject& object); + bool destroy(SceneObject& object); - status_t setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds); + bool setExpirationTimestamp(uint64_t ptpExpirationTimestampInMilliseconds); - status_t flush(sceneVersionTag_t sceneVersion); + bool flush(sceneVersionTag_t sceneVersion); - status_t resetUniformTimeMs(); + bool resetUniformTimeMs(); int32_t getUniformTimeMs() const; template // NOLINTNEXTLINE(modernize-avoid-c-arrays) - ArrayResource* createArrayResource(uint32_t numElements, const T* arrayData, resourceCacheFlag_t cacheFlag, std::string_view name); + ramses::ArrayResource* createArrayResource(uint32_t numElements, const T* arrayData, std::string_view name); // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture2D* createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name); + Texture2D* createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture3D* createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, resourceCacheFlag_t cacheFlag, std::string_view name); + Texture3D* createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, std::string_view name); // NOLINTNEXTLINE(modernize-avoid-c-arrays) - TextureCube* createTextureCube(uint32_t size, ETextureFormat format, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, resourceCacheFlag_t cacheFlag, std::string_view name); - Effect* createEffect(const EffectDescription& effectDesc, resourceCacheFlag_t cacheFlag, std::string_view name); + TextureCube* createTextureCube(uint32_t size, ETextureFormat format, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); + Effect* createEffect(const EffectDescription& effectDesc, std::string_view name); std::string getLastEffectErrorMessages() const; - ArrayResource* createHLArrayResource(ramses_internal::ManagedResource const& resource, std::string_view name); - Texture2D* createHLTexture2D(ramses_internal::ManagedResource const& resource, std::string_view name); - Texture3D* createHLTexture3D(ramses_internal::ManagedResource const& resource, std::string_view name); - TextureCube* createHLTextureCube(ramses_internal::ManagedResource const& resource, std::string_view name); - Effect* createHLEffect(ramses_internal::ManagedResource const& resource, std::string_view name); + ramses::ArrayResource* createHLArrayResource(ramses::internal::ManagedResource const& resource, std::string_view name); + Texture2D* createHLTexture2D(ramses::internal::ManagedResource const& resource, std::string_view name); + Texture3D* createHLTexture3D(ramses::internal::ManagedResource const& resource, std::string_view name); + TextureCube* createHLTextureCube(ramses::internal::ManagedResource const& resource, std::string_view name); + Effect* createHLEffect(ramses::internal::ManagedResource const& resource, std::string_view name); - const ramses_internal::ClientScene& getIScene() const; - ramses_internal::ClientScene& getIScene(); + const ramses::internal::ClientScene& getIScene() const; + ramses::internal::ClientScene& getIScene(); - bool containsSceneObject(const SceneObjectImpl& object) const; - const RamsesObject* findObjectByName(std::string_view name) const; - RamsesObject* findObjectByName(std::string_view name); + bool containsSceneObject(const SceneObjectImpl& object) const; + template [[nodiscard]] const T* findObjectByName(std::string_view name) const; + template [[nodiscard]] T* findObjectByName(std::string_view name); Resource* getResource(resourceId_t rid) const; const SceneObject* findObjectById(sceneObjectId_t id) const; SceneObject* findObjectById(sceneObjectId_t id); - RamsesObjectRegistry& getObjectRegistry(); - const RamsesObjectRegistry& getObjectRegistry() const; + SceneObjectRegistry& getObjectRegistry(); + const SceneObjectRegistry& getObjectRegistry() const; void setSceneVersionForNextFlush(sceneVersionTag_t sceneVersion); sceneObjectId_t getNextSceneObjectId(); - ramses_internal::StatisticCollectionScene& getStatisticCollection(); + ramses::internal::StatisticCollectionScene& getStatisticCollection(); SceneReference* getSceneReference(sceneId_t referencedSceneId); RamsesClient& getHlRamsesClient(); @@ -249,14 +250,14 @@ namespace ramses template void enqueueSceneCommand(T commands); - Resource* scanForResourceWithHash(ramses_internal::ResourceContentHash hash) const; + Resource* scanForResourceWithHash(ramses::internal::ResourceContentHash hash) const; template - status_t createAndDeserializeObjectImpls(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext, uint32_t count); + bool createAndDeserializeObjectImpls(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext, uint32_t count); - void setSceneFileHandle(ramses_internal::SceneFileHandle handle); + void setSceneFileHandle(ramses::internal::SceneFileHandle handle); void closeSceneFile(); - ramses_internal::SceneFileHandle getSceneFileHandle() const; + ramses::internal::SceneFileHandle getSceneFileHandle() const; void updateResourceId(resourceId_t const& oldId, Resource& resourceWithNewId); @@ -269,13 +270,13 @@ namespace ramses ETextureSamplingMethod magSamplingMethod, uint32_t anisotropyLevel, ERamsesObjectType samplerType, - ramses_internal::TextureSampler::ContentType contentType, - ramses_internal::ResourceContentHash textureResourceHash, // The sampler stores either a texture, or... - ramses_internal::MemoryHandle contentHandle, // a render target's color buffer, or a texture buffer, or a stream texture + ramses::internal::TextureSampler::ContentType contentType, + ramses::internal::ResourceContentHash textureResourceHash, // The sampler stores either a texture, or... + ramses::internal::MemoryHandle contentHandle, // a render target's color buffer, or a texture buffer, or a stream texture std::string_view name /*= 0*/); template - status_t createTextureConsumerImpl(const SAMPLER& sampler, dataConsumerId_t id, bool checkDuplicate = true); + bool createTextureConsumerImpl(const SAMPLER& sampler, dataConsumerId_t id, bool checkDuplicate = true); RenderPass* createRenderPassInternal(std::string_view name); @@ -288,20 +289,20 @@ namespace ramses void removeObjectFromAllContainers(const OBJECT& object); template - status_t destroyTextureSampler(SAMPLER& sampler); + bool destroyTextureSampler(SAMPLER& sampler); void markAllChildrenDirty(Node& node); bool cameraIsAssignedToRenderPasses(const Camera& camera); - status_t destroyRenderTarget(RenderTarget& renderTarget); - status_t destroyCamera(Camera& camera); - status_t destroyRenderGroup(RenderGroup& group); - status_t destroyMeshNode(MeshNode& mesh); - status_t destroyNode(Node& node); - status_t destroyDataObject(DataObject& dataObject); - status_t destroyResource(Resource& resource); - status_t destroyObject(SceneObject& object); + bool destroyRenderTarget(RenderTarget& renderTarget); + bool destroyCamera(Camera& camera); + bool destroyRenderGroup(RenderGroup& group); + bool destroyMeshNode(MeshNode& mesh); + bool destroyNode(Node& node); + bool destroyDataObject(DataObject& dataObject); + bool destroyResource(Resource& resource); + bool destroyObject(SceneObject& object); using NodeVisibilityPair = std::pair; using NodeVisibilityInfoVector = std::vector; @@ -310,52 +311,60 @@ namespace ramses void prepareListOfDirtyNodesForHierarchicalVisibility(NodeVisibilityInfoVector& nodesToProcess); void applyHierarchicalVisibility(); - status_t writeSceneObjectsToStream(ramses_internal::IOutputStream& outputStream) const; + bool writeSceneObjectsToStream(ramses::internal::IOutputStream& outputStream, const SaveFileConfigImpl& saveConfig) const; bool removeResourceWithIdFromResources(resourceId_t const& id, Resource& resource); - ramses_internal::ClientScene& m_scene; - ramses_internal::SceneCommandBuffer m_commandBuffer; + ramses::internal::ClientScene& m_scene; + ramses::internal::SceneCommandBuffer m_commandBuffer; sceneVersionTag_t m_nextSceneVersion; sceneObjectId_t m_lastSceneObjectId; - RamsesObjectRegistry m_objectRegistry; - std::unordered_multimap m_resources; + SceneObjectRegistry m_objectRegistry; + std::unordered_multimap m_resources; // This is essentially a local variable only used in the "applyVisibilityToSubtree" method. // This is for performance reasons, so we can re-use the same vector each time the method is called. NodeVisibilityInfoVector m_dataStackForSubTreeVisibilityApplying; EScenePublicationMode m_futurePublicationMode; - ramses_internal::FlushTime::Clock::time_point m_expirationTimestamp{ ramses_internal::FlushTime::InvalidTimestamp }; + ramses::internal::FlushTime::Clock::time_point m_expirationTimestamp{ ramses::internal::FlushTime::InvalidTimestamp }; - ramses_internal::HashMap m_sceneReferences; + ramses::internal::HashMap m_sceneReferences; RamsesClient& m_hlClient; std::string m_effectErrorMessages; - ramses_internal::SceneFileHandle m_sceneFileHandle; + ramses::internal::SceneFileHandle m_sceneFileHandle; bool m_sendEffectTimeSync = false; }; // define here to allow inlining - inline const ramses_internal::ClientScene& SceneImpl::getIScene() const + inline const ramses::internal::ClientScene& SceneImpl::getIScene() const { return m_scene; } - inline ramses_internal::ClientScene& SceneImpl::getIScene() + inline ramses::internal::ClientScene& SceneImpl::getIScene() { return m_scene; } + template T* SceneImpl::findObjectByName(std::string_view name) + { + return m_objectRegistry.findObjectByName(name); + } + + template const T* SceneImpl::findObjectByName(std::string_view name) const + { + return m_objectRegistry.findObjectByName(name); + } + template void SceneImpl::enqueueSceneCommand(T commands) { m_commandBuffer.enqueueCommand(std::move(commands)); } } - -#endif diff --git a/client/ramses-client/impl/SceneIterator.cpp b/src/client/impl/SceneIterator.cpp similarity index 77% rename from client/ramses-client/impl/SceneIterator.cpp rename to src/client/impl/SceneIterator.cpp index c7660c07a..55ba9f6cf 100644 --- a/client/ramses-client/impl/SceneIterator.cpp +++ b/src/client/impl/SceneIterator.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/SceneIterator.h" +#include "ramses/client/SceneIterator.h" #include "SceneIteratorImpl.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RamsesClient.h" -#include "RamsesClientImpl.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RamsesClient.h" +#include "impl/RamsesClientImpl.h" namespace ramses { SceneIterator::SceneIterator(const RamsesClient& client) - : m_impl{ std::make_unique(client.m_impl.getListOfScenes()) } + : m_impl{ std::make_unique(client.impl().getListOfScenes()) } { } diff --git a/client/ramses-client/impl/SceneIteratorImpl.h b/src/client/impl/SceneIteratorImpl.h similarity index 82% rename from client/ramses-client/impl/SceneIteratorImpl.h rename to src/client/impl/SceneIteratorImpl.h index ee5dd0031..bd584d5df 100644 --- a/client/ramses-client/impl/SceneIteratorImpl.h +++ b/src/client/impl/SceneIteratorImpl.h @@ -6,18 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEITERATORIMPL_H -#define RAMSES_SCENEITERATORIMPL_H +#pragma once -#include "IteratorImpl.h" -#include "RamsesClientImpl.h" -#include "ramses-client-api/RamsesObjectTypes.h" -#include "Collections/Vector.h" +#include "impl/IteratorImpl.h" +#include "impl/RamsesClientImpl.h" +#include "ramses/framework/RamsesObjectTypes.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" namespace ramses { class Scene; +} +namespace ramses::internal +{ class SceneIteratorImpl : public IteratorImpl { public: @@ -38,5 +40,3 @@ namespace ramses } }; } - -#endif diff --git a/client/ramses-client-api/SceneObject.cpp b/src/client/impl/SceneObject.cpp similarity index 57% rename from client/ramses-client-api/SceneObject.cpp rename to src/client/impl/SceneObject.cpp index f3d49f2cd..7f7325d48 100644 --- a/client/ramses-client-api/SceneObject.cpp +++ b/src/client/impl/SceneObject.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" // internal #include "SceneObjectImpl.h" namespace ramses { - SceneObject::SceneObject(std::unique_ptr impl) + SceneObject::SceneObject(std::unique_ptr impl) : ClientObject{ std::move(impl) } - , m_impl{ static_cast(ClientObject::m_impl) } + , m_impl{ static_cast(ClientObject::m_impl) } { } @@ -25,8 +25,23 @@ namespace ramses return m_impl.getSceneObjectId(); } - sceneId_t SceneObject::getSceneId() const + const Scene& SceneObject::getScene() const { - return m_impl.getSceneId(); + return impl().getScene(); + } + + Scene& SceneObject::getScene() + { + return impl().getScene(); + } + + internal::SceneObjectImpl& SceneObject::impl() + { + return m_impl; + } + + const internal::SceneObjectImpl& SceneObject::impl() const + { + return m_impl; } } diff --git a/src/client/impl/SceneObjectImpl.cpp b/src/client/impl/SceneObjectImpl.cpp new file mode 100644 index 000000000..babfe999c --- /dev/null +++ b/src/client/impl/SceneObjectImpl.cpp @@ -0,0 +1,113 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "SceneObjectImpl.h" +#include "ramses/client/Scene.h" +#include "impl/SceneImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SerializationContext.h" +#include "impl/RamsesFrameworkTypesImpl.h" + +namespace ramses::internal +{ + SceneObjectImpl::SceneObjectImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name) + : ClientObjectImpl{ scene.getClientImpl(), type, name } + , m_sceneObjectId{ scene.getNextSceneObjectId() } + , m_scene{ scene } + { + m_scene.getStatisticCollection().statObjectsCreated.incCounter(1); + } + + SceneObjectImpl::~SceneObjectImpl() + { + m_scene.getStatisticCollection().statObjectsDestroyed.incCounter(1); + } + + const SceneImpl& SceneObjectImpl::getSceneImpl() const + { + return m_scene; + } + + SceneImpl& SceneObjectImpl::getSceneImpl() + { + return m_scene; + } + + const ramses::internal::ClientScene& SceneObjectImpl::getIScene() const + { + return m_scene.getIScene(); + } + + ramses::internal::ClientScene& SceneObjectImpl::getIScene() + { + return m_scene.getIScene(); + } + + bool SceneObjectImpl::isFromTheSameSceneAs(const SceneObjectImpl& otherObject) const + { + return &getIScene() == &(otherObject.getIScene()); + } + + bool SceneObjectImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!RamsesObjectImpl::serialize(outStream, serializationContext)) + return false; + + assert(m_sceneObjectId.isValid()); + outStream << (serializationContext.getSerializeSceneObjectIds() ? m_sceneObjectId.getValue() : sceneObjectId_t::Invalid().getValue()); + + return true; + } + + bool SceneObjectImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!RamsesObjectImpl::deserialize(inStream, serializationContext)) + return true; + + sceneObjectId_t id; + inStream >> id.getReference(); + + if (id.isValid()) + m_sceneObjectId = std::move(id); + + return true; + } + + sceneObjectId_t SceneObjectImpl::getSceneObjectId() const + { + return m_sceneObjectId; + } + + const Scene& SceneObjectImpl::getScene() const + { + return RamsesObjectTypeUtils::ConvertTo(m_scene.getRamsesObject()); + } + + Scene& SceneObjectImpl::getScene() + { + return RamsesObjectTypeUtils::ConvertTo(m_scene.getRamsesObject()); + } + + std::string SceneObjectImpl::getIdentificationString() const + { + auto idString = RamsesObjectImpl::getIdentificationString(); + idString.insert(idString.size() - 1, fmt::format(" ScnObjId={}", m_sceneObjectId)); + + return idString; + } + + void SceneObjectImpl::setObjectRegistryHandle(SceneObjectRegistryHandle handle) + { + m_objectRegistryHandle = handle; + } + + SceneObjectRegistryHandle SceneObjectImpl::getObjectRegistryHandle() const + { + return m_objectRegistryHandle; + } +} diff --git a/src/client/impl/SceneObjectImpl.h b/src/client/impl/SceneObjectImpl.h new file mode 100644 index 000000000..6f46c9615 --- /dev/null +++ b/src/client/impl/SceneObjectImpl.h @@ -0,0 +1,58 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "impl/ClientObjectImpl.h" +#include "impl/RamsesClientTypesImpl.h" +#include + +namespace ramses +{ + class Scene; +} + +namespace ramses::internal +{ + class ClientScene; + class SceneImpl; + + class SceneObjectImpl : public ClientObjectImpl + { + public: + explicit SceneObjectImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name); + ~SceneObjectImpl() override; + + [[nodiscard]] sceneObjectId_t getSceneObjectId() const; + [[nodiscard]] const Scene& getScene() const; + [[nodiscard]] Scene& getScene(); + + // impl methods + void setObjectRegistryHandle(SceneObjectRegistryHandle handle); + [[nodiscard]] SceneObjectRegistryHandle getObjectRegistryHandle() const; + + [[nodiscard]] const SceneImpl& getSceneImpl() const; + [[nodiscard]] SceneImpl& getSceneImpl(); + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + + [[nodiscard]] const ramses::internal::ClientScene& getIScene() const; + [[nodiscard]] ramses::internal::ClientScene& getIScene(); + + [[nodiscard]] bool isFromTheSameSceneAs(const SceneObjectImpl& otherObject) const; + + [[nodiscard]] std::string getIdentificationString() const final; + + protected: + sceneObjectId_t m_sceneObjectId; + + private: + SceneImpl& m_scene; + SceneObjectRegistryHandle m_objectRegistryHandle; + }; +} diff --git a/client/ramses-client/impl/SceneObjectIterator.cpp b/src/client/impl/SceneObjectIterator.cpp similarity index 78% rename from client/ramses-client/impl/SceneObjectIterator.cpp rename to src/client/impl/SceneObjectIterator.cpp index eccabca8f..2c94eb697 100644 --- a/client/ramses-client/impl/SceneObjectIterator.cpp +++ b/src/client/impl/SceneObjectIterator.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/SceneObjectIterator.h" -#include "ramses-client-api/Scene.h" +#include "ramses/client/SceneObjectIterator.h" +#include "ramses/client/Scene.h" #include "ObjectIteratorImpl.h" -#include "SceneImpl.h" +#include "impl/SceneImpl.h" namespace ramses { SceneObjectIterator::SceneObjectIterator(const Scene& scene, ERamsesObjectType objectType) - : m_impl{ std::make_unique(scene.m_impl.getObjectRegistry(), objectType) } + : m_impl{ std::make_unique(scene.impl().getObjectRegistry(), objectType) } { } diff --git a/src/client/impl/SceneObjectRegistry.cpp b/src/client/impl/SceneObjectRegistry.cpp new file mode 100644 index 000000000..fc10e6359 --- /dev/null +++ b/src/client/impl/SceneObjectRegistry.cpp @@ -0,0 +1,167 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/SceneObjectRegistry.h" +#include "ramses/framework/RamsesObject.h" +#include "ramses/client/Node.h" +#include "impl/RamsesObjectImpl.h" +#include "ObjectIteratorImpl.h" +#include "impl/NodeImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "internal/PlatformAbstraction/PlatformStringUtils.h" + +namespace ramses::internal +{ + void SceneObjectRegistry::registerObjectInternal(SceneObject& object) + { + assert(!object.isOfType(ERamsesObjectType::LogicObject)); // logic objects have their own registry in corresponding LogicEngine + assert(!containsObject(object)); + + const ERamsesObjectType type = object.impl().getType(); + const SceneObjectRegistryHandle handle = m_objects[static_cast(type)].allocate(); + *m_objects[static_cast(type)].getMemory(handle) = &object; + object.impl().setObjectRegistryHandle(handle); + + trackSceneObjectById(object); + } + + void SceneObjectRegistry::destroyAndUnregisterObject(SceneObject& object) + { + assert(containsObject(object)); + + if (object.isOfType(ERamsesObjectType::Node)) + setNodeDirty(RamsesObjectTypeUtils::ConvertTo(object).m_impl, false); + + if (object.isOfType(ERamsesObjectType::SceneObject)) + { + const sceneObjectId_t sceneObjectId = RamsesObjectTypeUtils::ConvertTo(object).getSceneObjectId(); + assert(m_objectsById.contains(sceneObjectId)); + m_objectsById.remove(sceneObjectId); + } + + const SceneObjectRegistryHandle handle = object.impl().getObjectRegistryHandle(); + const auto type = static_cast(object.impl().getType()); + m_objects[type].release(handle); + + auto it = std::find_if(m_objectsOwningContainer.begin(), m_objectsOwningContainer.end(), [&object](auto& ro) { return ro.get() == &object; }); + assert(it != m_objectsOwningContainer.end()); + m_objectsOwningContainer.erase(it); + } + + void SceneObjectRegistry::reserveAdditionalGeneralCapacity(uint32_t additionalCount) + { + m_objectsOwningContainer.reserve(m_objectsOwningContainer.size() + additionalCount); + } + + void SceneObjectRegistry::reserveAdditionalObjectCapacity(ERamsesObjectType type, uint32_t additionalCount) + { + assert(RamsesObjectTypeUtils::IsConcreteType(type)); + const auto index = static_cast(type); + m_objects[index].preallocateSize(m_objects[index].getActualCount() + additionalCount); + } + + uint32_t SceneObjectRegistry::getNumberOfObjects(ERamsesObjectType type) const + { + assert(RamsesObjectTypeUtils::IsConcreteType(type)); + return m_objects[static_cast(type)].getActualCount(); + } + + bool SceneObjectRegistry::containsObject(const SceneObject& object) const + { + const SceneObjectRegistryHandle handle = object.impl().getObjectRegistryHandle(); + const ERamsesObjectType type = object.impl().getType(); + const SceneObjectsPool& objectsPool = m_objects[static_cast(type)]; + return objectsPool.isAllocated(handle) && (*objectsPool.getMemory(handle) == &object); + } + + void SceneObjectRegistry::trackSceneObjectById(SceneObject& object) + { + if (object.isOfType(ERamsesObjectType::SceneObject)) + { + auto& sceneObject = RamsesObjectTypeUtils::ConvertTo(object); + const sceneObjectId_t sceneObjectId = RamsesObjectTypeUtils::ConvertTo(object).getSceneObjectId(); + assert(!m_objectsById.contains(sceneObjectId)); + m_objectsById.put(sceneObjectId, &sceneObject); + } + } + + SceneObject* SceneObjectRegistry::findObjectById(sceneObjectId_t id) + { + SceneObject* object(nullptr); + m_objectsById.get(id, object); + + return object; + } + + const SceneObject* SceneObjectRegistry::findObjectById(sceneObjectId_t id) const + { + // const version of findObjectById cast to its non-const version to avoid duplicating code + return const_cast((const_cast(*this)).findObjectById(id)); + } + + void SceneObjectRegistry::getObjectsOfType(SceneObjectVector& objects, ERamsesObjectType ofType) const + { + assert(objects.empty()); + + // preallocate memory in container + uint32_t objectCount = 0u; + for (size_t i = 0u; i < RamsesObjectTypeCount; ++i) + { + const auto type = ERamsesObjectType(i); + if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ofType)) + { + objectCount += m_objects[i].getActualCount(); + } + } + objects.reserve(objectCount); + + for (size_t i = 0u; i < RamsesObjectTypeCount; ++i) + { + const auto type = ERamsesObjectType(i); + if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ofType)) + { + const SceneObjectsPool& objectsPool = m_objects[i]; + for (SceneObjectRegistryHandle handle(0u); handle < objectsPool.getTotalCount(); ++handle) + { + if (objectsPool.isAllocated(handle)) + { + objects.push_back(*objectsPool.getMemory(handle)); + } + } + } + } + } + + void SceneObjectRegistry::setNodeDirty(NodeImpl& node, bool dirty) + { + if (dirty) + { + m_dirtyNodes.put(&node); + } + else + { + m_dirtyNodes.remove(&node); + } + } + + bool SceneObjectRegistry::isNodeDirty(const NodeImpl& node) const + { + NodeImpl* nodeImplPtr = &const_cast(node); + return m_dirtyNodes.contains(nodeImplPtr); + } + + const NodeImplSet& SceneObjectRegistry::getDirtyNodes() const + { + return m_dirtyNodes; + } + + void SceneObjectRegistry::clearDirtyNodes() + { + m_dirtyNodes.clear(); + } +} diff --git a/src/client/impl/SceneObjectRegistry.h b/src/client/impl/SceneObjectRegistry.h new file mode 100644 index 000000000..cb717dd3f --- /dev/null +++ b/src/client/impl/SceneObjectRegistry.h @@ -0,0 +1,111 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/SceneObject.h" + +#include "impl/SceneObjectImpl.h" +#include "impl/RamsesObjectVector.h" +#include "impl/RamsesObjectTypeTraits.h" +#include "impl/RamsesObjectTypeUtils.h" + +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/Core/Utils/MemoryPool.h" + +#include +#include +#include + +namespace ramses::internal +{ + using SceneObjectVector = std::vector; + + class SceneObjectRegistry final + { + public: + template + T& createAndRegisterObject(std::unique_ptr impl); + void destroyAndUnregisterObject(SceneObject& object); + + void reserveAdditionalGeneralCapacity(uint32_t additionalCount); + void reserveAdditionalObjectCapacity(ERamsesObjectType type, uint32_t additionalCount); + [[nodiscard]] uint32_t getNumberOfObjects(ERamsesObjectType type) const; + + void getObjectsOfType(SceneObjectVector& objects, ERamsesObjectType ofType) const; + + template [[nodiscard]] const T* findObjectByName(std::string_view name) const; + template [[nodiscard]] T* findObjectByName(std::string_view name); + + [[nodiscard]] const SceneObject* findObjectById(sceneObjectId_t id) const; + SceneObject* findObjectById(sceneObjectId_t id); + + void setNodeDirty(NodeImpl& node, bool dirty); + [[nodiscard]] bool isNodeDirty(const NodeImpl& node) const; + + [[nodiscard]] const NodeImplSet& getDirtyNodes() const; + void clearDirtyNodes(); + + private: + void registerObjectInternal(SceneObject& object); + [[nodiscard]] bool containsObject(const SceneObject& object) const; + void trackSceneObjectById(SceneObject& object); + + using ObjectIdMap = ramses::internal::HashMap; + ObjectIdMap m_objectsById; + + using SceneObjectsPool = ramses::internal::MemoryPool; + std::array m_objects; + + using SceneObjectUniquePtr = std::unique_ptr>; + std::vector m_objectsOwningContainer; + + NodeImplSet m_dirtyNodes; + + friend class SceneObjectRegistryIterator; + }; + + template + T& SceneObjectRegistry::createAndRegisterObject(std::unique_ptr impl) + { + static_assert(std::is_base_of_v, "Meant for SceneObject instances only"); + + std::unique_ptr> object{ new T{ std::move(impl) }, [](SceneObject* o) { delete o; } }; + T* objectRawPtr = object.get(); + this->m_objectsOwningContainer.push_back(std::move(object)); + + this->registerObjectInternal(*objectRawPtr); + + return *objectRawPtr; + } + + template T* SceneObjectRegistry::findObjectByName(std::string_view name) + { + constexpr ERamsesObjectType typeToReturn = TYPE_ID_OF_RAMSES_OBJECT::ID; + for (size_t typeIdx = 0u; typeIdx < RamsesObjectTypeCount; ++typeIdx) + { + const auto type = static_cast(typeIdx); + if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, typeToReturn)) + { + const auto& objs = m_objects[typeIdx]; + const auto it = std::find_if(objs.begin(), objs.end(), [name](const auto o) { return (*o.second)->getName() == name; }); + if (it != objs.end()) + return (*it->second)->template as(); + } + } + + return nullptr; + } + + template const T* SceneObjectRegistry::findObjectByName(std::string_view name) const + { + // const version of findObjectByName cast to its non-const version to avoid duplicating code + return const_cast((const_cast(*this)).findObjectByName(name)); + } +} diff --git a/client/ramses-client/impl/RamsesObjectRegistryIterator.h b/src/client/impl/SceneObjectRegistryIterator.h similarity index 65% rename from client/ramses-client/impl/RamsesObjectRegistryIterator.h rename to src/client/impl/SceneObjectRegistryIterator.h index fc0839b9f..c54b5cc94 100644 --- a/client/ramses-client/impl/RamsesObjectRegistryIterator.h +++ b/src/client/impl/SceneObjectRegistryIterator.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESOBJECTREGISTRYITERATOR_H -#define RAMSES_RAMSESOBJECTREGISTRYITERATOR_H +#pragma once -#include "ramses-client-api/RamsesObject.h" -#include "RamsesObjectRegistry.h" -#include "RamsesObjectTypeUtils.h" +#include "ramses/framework/RamsesObject.h" +#include "impl/SceneObjectRegistry.h" +#include "impl/RamsesObjectTypeUtils.h" -namespace ramses +namespace ramses::internal { - class RamsesObjectRegistryIterator + class SceneObjectRegistryIterator { public: - RamsesObjectRegistryIterator(const RamsesObjectRegistry& registry, ERamsesObjectType type) + SceneObjectRegistryIterator(const SceneObjectRegistry& registry, ERamsesObjectType type) : m_objects(registry.m_objects[static_cast(type)]) , m_objectsTotalCount(m_objects.getTotalCount()) , m_current(0u) @@ -26,15 +25,16 @@ namespace ramses assert(RamsesObjectTypeUtils::IsConcreteType(type)); } - template + template const T* getNext() { + static_assert(std::is_base_of_v); assert((m_objectsTotalCount == m_objects.getTotalCount()) && "Container size changed while iterating!"); for (; m_current < m_objectsTotalCount; ++m_current) { if (m_objects.isAllocated(m_current)) { - const RamsesObject& obj = **m_objects.getMemory(m_current); + const SceneObject& obj = **m_objects.getMemory(m_current); ++m_current; return &RamsesObjectTypeUtils::ConvertTo(obj); } @@ -43,7 +43,7 @@ namespace ramses return nullptr; } - template + template T* getNextNonConst() { // Non-const version of getNext cast to its const version to avoid duplicating code @@ -51,10 +51,8 @@ namespace ramses } private: - const RamsesObjectRegistry::RamsesObjectsPool& m_objects; - const uint32_t m_objectsTotalCount; - RamsesObjectHandle m_current; + const SceneObjectRegistry::SceneObjectsPool& m_objects; + const uint32_t m_objectsTotalCount; + SceneObjectRegistryHandle m_current; }; } - -#endif diff --git a/client/ramses-client-api/SceneReference.cpp b/src/client/impl/SceneReference.cpp similarity index 59% rename from client/ramses-client-api/SceneReference.cpp rename to src/client/impl/SceneReference.cpp index 9cb7b50a0..2b5b6a3c2 100644 --- a/client/ramses-client-api/SceneReference.cpp +++ b/src/client/impl/SceneReference.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneReferenceImpl.h" -#include "ramses-client-api/SceneReference.h" +#include "impl/SceneReferenceImpl.h" +#include "ramses/client/SceneReference.h" namespace ramses { - SceneReference::SceneReference(std::unique_ptr impl) + SceneReference::SceneReference(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } - status_t SceneReference::requestState(RendererSceneState requestedState) + bool SceneReference::requestState(RendererSceneState requestedState) { - const status_t status = m_impl.requestState(requestedState); + const bool status = m_impl.requestState(requestedState); LOG_HL_CLIENT_API1(status, static_cast(requestedState)); return status; } @@ -29,22 +29,32 @@ namespace ramses return m_impl.getReferencedSceneId(); } - status_t SceneReference::requestNotificationsForSceneVersionTags(bool flag) + bool SceneReference::requestNotificationsForSceneVersionTags(bool flag) { const auto status = m_impl.requestNotificationsForSceneVersionTags(flag); LOG_HL_CLIENT_API1(status, flag); return status; } - status_t SceneReference::setRenderOrder(int32_t renderOrder) + bool SceneReference::setRenderOrder(int32_t renderOrder) { const auto status = m_impl.setRenderOrder(renderOrder); LOG_HL_CLIENT_API1(status, renderOrder); return status; } - ramses::RendererSceneState SceneReference::getRequestedState() const + RendererSceneState SceneReference::getRequestedState() const { return m_impl.getRequestedState(); } + + internal::SceneReferenceImpl& SceneReference::impl() + { + return m_impl; + } + + const internal::SceneReferenceImpl& SceneReference::impl() const + { + return m_impl; + } } diff --git a/src/client/impl/SceneReferenceImpl.cpp b/src/client/impl/SceneReferenceImpl.cpp new file mode 100644 index 000000000..eaf21623e --- /dev/null +++ b/src/client/impl/SceneReferenceImpl.cpp @@ -0,0 +1,105 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/SceneReferenceImpl.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "impl/SceneObjectImpl.h" +#include "impl/SceneImpl.h" +#include "impl/ErrorReporting.h" + +namespace ramses::internal +{ + SceneReferenceImpl::SceneReferenceImpl(SceneImpl& scene, std::string_view name) + : SceneObjectImpl(scene, ERamsesObjectType::SceneReference, name) + { + } + + void SceneReferenceImpl::initializeFrameworkData(sceneId_t referencedScene) + { + m_sceneReferenceHandle = getIScene().allocateSceneReference(ramses::internal::SceneId{referencedScene.getValue()}, {}); + } + + void SceneReferenceImpl::deinitializeFrameworkData() + { + getIScene().releaseSceneReference(m_sceneReferenceHandle); + } + + bool SceneReferenceImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << m_sceneReferenceHandle; + + return true; + } + + bool SceneReferenceImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + inStream >> m_sceneReferenceHandle; + + return true; + } + + sceneId_t SceneReferenceImpl::getReferencedSceneId() const + { + return sceneId_t{ getIScene().getSceneReference(m_sceneReferenceHandle).sceneId.getValue() }; + } + + bool SceneReferenceImpl::requestState(RendererSceneState requestedState) + { + if (requestedState == RendererSceneState::Unavailable) + { + getErrorReporting().set("SceneReference::requestState: Can not request scene reference state Unavailable. In order to release the scene from renderer request Available state", *this); + return false; + } + + getIScene().requestSceneReferenceState(m_sceneReferenceHandle, requestedState); + return true; + } + + + RendererSceneState SceneReferenceImpl::getRequestedState() const + { + return getIScene().getSceneReference(m_sceneReferenceHandle).requestedState; + } + + bool SceneReferenceImpl::requestNotificationsForSceneVersionTags(bool flag) + { + getIScene().requestSceneReferenceFlushNotifications(m_sceneReferenceHandle, flag); + return true; + } + + bool SceneReferenceImpl::setRenderOrder(int32_t renderOrder) + { + getIScene().setSceneReferenceRenderOrder(m_sceneReferenceHandle, renderOrder); + return true; + } + + ramses::internal::SceneReferenceHandle SceneReferenceImpl::getSceneReferenceHandle() const + { + return m_sceneReferenceHandle; + } + + RendererSceneState SceneReferenceImpl::getReportedState() const + { + return m_reportedState; + } + + void SceneReferenceImpl::setReportedState(RendererSceneState state) + { + m_reportedState = state; + } + +} diff --git a/src/client/impl/SceneReferenceImpl.h b/src/client/impl/SceneReferenceImpl.h new file mode 100644 index 000000000..f040118bb --- /dev/null +++ b/src/client/impl/SceneReferenceImpl.h @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RendererSceneState.h" + +// internal +#include "SceneObjectImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" + +#include + +namespace ramses::internal +{ + class SceneReferenceImpl final : public SceneObjectImpl + { + public: + SceneReferenceImpl(SceneImpl& scene, std::string_view name); + + void initializeFrameworkData(sceneId_t referencedScene); + + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + + bool requestState(RendererSceneState requestedState); + [[nodiscard]] sceneId_t getReferencedSceneId() const; + [[nodiscard]] RendererSceneState getRequestedState() const; + bool requestNotificationsForSceneVersionTags(bool flag); + bool setRenderOrder(int32_t renderOrder); + + [[nodiscard]] ramses::internal::SceneReferenceHandle getSceneReferenceHandle() const; + + [[nodiscard]] RendererSceneState getReportedState() const; + void setReportedState(RendererSceneState state); + + private: + RendererSceneState m_reportedState = RendererSceneState::Unavailable; + + ramses::internal::SceneReferenceHandle m_sceneReferenceHandle; + }; +} diff --git a/client/ramses-client/impl/SerializationHelper.h b/src/client/impl/SerializationHelper.h similarity index 59% rename from client/ramses-client/impl/SerializationHelper.h rename to src/client/impl/SerializationHelper.h index a616c97ce..1f07aa1d8 100644 --- a/client/ramses-client/impl/SerializationHelper.h +++ b/src/client/impl/SerializationHelper.h @@ -6,50 +6,43 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SERIALIZATIONHELPER_H -#define RAMSES_SERIALIZATIONHELPER_H - -#include "SerializationContext.h" -#include "Collections/IOutputStream.h" -#include "RamsesObjectImpl.h" -#include "RamsesObjectRegistry.h" -#include "RamsesObjectRegistryIterator.h" -#include "RamsesObjectTypeUtils.h" -#include "Collections/Pair.h" -#include "Collections/Vector.h" - -namespace ramses +#pragma once + +#include "impl/SerializationContext.h" +#include "impl/SceneObjectImpl.h" +#include "impl/SceneObjectRegistry.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" + +namespace ramses::internal { class SerializationHelper { public: - static ObjectIDType DeserializeObjectID(ramses_internal::IInputStream& inStream) + static ObjectIDType DeserializeObjectID(ramses::internal::IInputStream& inStream) { ObjectIDType objectID = DeserializationContext::GetObjectIDNull(); inStream >> objectID; return objectID; } - static status_t DeserializeObjectImpl(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext, RamsesObjectImpl& impl, ObjectIDType& objectID) - { - objectID = SerializationHelper::DeserializeObjectID(inStream); - return impl.deserialize(inStream, serializationContext); - } - template - static status_t SerializeObjectsInRegistry(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext, const RamsesObjectRegistry& registry) + static bool SerializeObjectsInRegistry(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext, const SceneObjectRegistry & registry) { const ERamsesObjectType baseType = TYPE_ID_OF_RAMSES_OBJECT::ID; using TypeCountPair = std::pair; std::vector typesToSerialize; - typesToSerialize.reserve(static_cast(ERamsesObjectType::NUMBER_OF_TYPES)); + typesToSerialize.reserve(RamsesObjectTypeCount); uint32_t totalCount = 0u; - for (uint32_t typeIdx = 0u; typeIdx < static_cast(ERamsesObjectType::NUMBER_OF_TYPES); ++typeIdx) + for (uint32_t typeIdx = 0u; typeIdx < static_cast(RamsesObjectTypeCount); ++typeIdx) { - const ERamsesObjectType type = static_cast(typeIdx); + const auto type = static_cast(typeIdx); if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, baseType)) { @@ -73,23 +66,24 @@ namespace ramses outStream << static_cast(type); outStream << typeCountIter.second; - RamsesObjectRegistryIterator iter(registry, type); - while (const ObjectsBaseType* obj = iter.getNext()) + SceneObjectRegistryIterator iter(registry, type); + while (const auto* obj = iter.getNext()) { - CHECK_RETURN_ERR(obj->m_impl.serialize(outStream, serializationContext)); + if (!obj->impl().serialize(outStream, serializationContext)) + return false; } } - return StatusOK; + return true; } - static void DeserializeNumberOfObjectTypes(ramses_internal::IInputStream& inStream, uint32_t& totalCount, uint32_t& typesCount) + static void DeserializeNumberOfObjectTypes(ramses::internal::IInputStream& inStream, uint32_t& totalCount, uint32_t& typesCount) { inStream >> totalCount; inStream >> typesCount; } - static ERamsesObjectType DeserializeObjectTypeAndCount(ramses_internal::IInputStream& inStream, uint32_t& count) + static ERamsesObjectType DeserializeObjectTypeAndCount(ramses::internal::IInputStream& inStream, uint32_t& count) { auto typeInt = static_cast(ERamsesObjectType::Invalid); inStream >> typeInt; @@ -99,5 +93,3 @@ namespace ramses } }; } - -#endif diff --git a/client/ramses-client-api/Texture2D.cpp b/src/client/impl/Texture2D.cpp similarity index 70% rename from client/ramses-client-api/Texture2D.cpp rename to src/client/impl/Texture2D.cpp index b52f30dfd..e3ecb22ad 100644 --- a/client/ramses-client-api/Texture2D.cpp +++ b/src/client/impl/Texture2D.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Texture2DImpl.h" -#include "ramses-client-api/Texture2D.h" +#include "impl/Texture2DImpl.h" +#include "ramses/client/Texture2D.h" namespace ramses { - Texture2D::Texture2D(std::unique_ptr impl) + Texture2D::Texture2D(std::unique_ptr impl) : Resource{ std::move(impl) } - , m_impl{ static_cast(Resource::m_impl) } + , m_impl{ static_cast(Resource::m_impl) } { } @@ -37,5 +37,15 @@ namespace ramses { return m_impl.getTextureSwizzle(); } + + internal::Texture2DImpl& Texture2D::impl() + { + return m_impl; + } + + const internal::Texture2DImpl& Texture2D::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client-api/Texture2DBuffer.cpp b/src/client/impl/Texture2DBuffer.cpp similarity index 58% rename from client/ramses-client-api/Texture2DBuffer.cpp rename to src/client/impl/Texture2DBuffer.cpp index 2c84af178..8b4530d85 100644 --- a/client/ramses-client-api/Texture2DBuffer.cpp +++ b/src/client/impl/Texture2DBuffer.cpp @@ -7,22 +7,22 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/Texture2DBuffer.h" +#include "ramses/client/Texture2DBuffer.h" // Internal -#include "Texture2DBufferImpl.h" +#include "impl/Texture2DBufferImpl.h" namespace ramses { - Texture2DBuffer::Texture2DBuffer(std::unique_ptr impl) + Texture2DBuffer::Texture2DBuffer(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } - status_t Texture2DBuffer::updateData(size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height, const void* data) + bool Texture2DBuffer::updateData(size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height, const void* data) { - const status_t status = m_impl.setData(static_cast(data), mipLevel, offsetX, offsetY, width, height); + const bool status = m_impl.setData(static_cast(data), mipLevel, offsetX, offsetY, width, height); LOG_HL_CLIENT_API6(status, LOG_API_GENERIC_PTR_STRING(data), mipLevel, offsetX, offsetY, width, height); return status; } @@ -32,7 +32,7 @@ namespace ramses return m_impl.getMipLevelCount(); } - status_t Texture2DBuffer::getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const + bool Texture2DBuffer::getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const { return m_impl.getMipLevelSize(mipLevel, widthOut, heightOut); } @@ -47,9 +47,18 @@ namespace ramses return m_impl.getTexelFormat(); } - status_t Texture2DBuffer::getMipLevelData(size_t mipLevel, void* buffer, size_t bufferSize) const + bool Texture2DBuffer::getMipLevelData(size_t mipLevel, void* buffer, size_t bufferSize) const { return m_impl.getMipLevelData(mipLevel, static_cast(buffer), bufferSize); } + internal::Texture2DBufferImpl& Texture2DBuffer::impl() + { + return m_impl; + } + + const internal::Texture2DBufferImpl& Texture2DBuffer::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/Texture2DBufferImpl.cpp b/src/client/impl/Texture2DBufferImpl.cpp similarity index 55% rename from client/ramses-client/impl/Texture2DBufferImpl.cpp rename to src/client/impl/Texture2DBufferImpl.cpp index 4d58fba08..5c6f1b7e6 100644 --- a/client/ramses-client/impl/Texture2DBufferImpl.cpp +++ b/src/client/impl/Texture2DBufferImpl.cpp @@ -6,82 +6,86 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Texture2DBufferImpl.h" -#include "SerializationContext.h" -#include "DataTypeUtils.h" -#include "Scene/ClientScene.h" -#include "TextureUtils.h" - -namespace ramses +#include "impl/Texture2DBufferImpl.h" +#include "impl/SerializationContext.h" +#include "impl/DataTypeUtils.h" +#include "impl/TextureUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" + +namespace ramses::internal { Texture2DBufferImpl::Texture2DBufferImpl(SceneImpl& scene, std::string_view textureBufferName) : SceneObjectImpl(scene, ERamsesObjectType::Texture2DBuffer, textureBufferName) { } - Texture2DBufferImpl::~Texture2DBufferImpl() - { - } + Texture2DBufferImpl::~Texture2DBufferImpl() = default; - void Texture2DBufferImpl::initializeFrameworkData(const ramses_internal::MipMapDimensions& mipDimensions, ETextureFormat textureFormat) + void Texture2DBufferImpl::initializeFrameworkData(const ramses::internal::MipMapDimensions& mipDimensions, ETextureFormat textureFormat) { assert(!m_textureBufferHandle.isValid()); - m_textureBufferHandle = getIScene().allocateTextureBuffer(TextureUtils::GetTextureFormatInternal(textureFormat), mipDimensions); + m_textureBufferHandle = getIScene().allocateTextureBuffer(TextureUtils::GetTextureFormatInternal(textureFormat), mipDimensions, {}); } void Texture2DBufferImpl::deinitializeFrameworkData() { assert(m_textureBufferHandle.isValid()); getIScene().releaseTextureBuffer(m_textureBufferHandle); - m_textureBufferHandle = ramses_internal::TextureBufferHandle::Invalid(); + m_textureBufferHandle = ramses::internal::TextureBufferHandle::Invalid(); } - ramses_internal::TextureBufferHandle Texture2DBufferImpl::getTextureBufferHandle() const + ramses::internal::TextureBufferHandle Texture2DBufferImpl::getTextureBufferHandle() const { return m_textureBufferHandle; } - status_t Texture2DBufferImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool Texture2DBufferImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(SceneObjectImpl::serialize(outStream, serializationContext)); + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; outStream << m_textureBufferHandle; - return StatusOK; + return true; } - status_t Texture2DBufferImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool Texture2DBufferImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(SceneObjectImpl::deserialize(inStream, serializationContext)); + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_textureBufferHandle; - return StatusOK; + return true; } - status_t Texture2DBufferImpl::setData(const ramses_internal::Byte* data, size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height) + bool Texture2DBufferImpl::setData(const std::byte* data, size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height) { if (width == 0 || height == 0) { - return addErrorEntry("Texture2DBuffer::setData failed, width and height can not be zero."); + getErrorReporting().set("Texture2DBuffer::setData failed, width and height can not be zero."); + return false; } const auto& mipMaps = getIScene().getTextureBuffer(m_textureBufferHandle).mipMaps; if (mipLevel >= mipMaps.size()) { - return addErrorEntry("Texture2DBuffer::setData failed, mipLevel exceeds the number of allocated mips."); + getErrorReporting().set("Texture2DBuffer::setData failed, mipLevel exceeds the number of allocated mips."); + return false; } const auto& mip = mipMaps[mipLevel]; if (offsetX + width > mip.width || offsetY + height > mip.height) { - return addErrorEntry("Texture2DBuffer::setData failed, updated subregion exceeds the size of the target mipLevel."); + getErrorReporting().set("Texture2DBuffer::setData failed, updated subregion exceeds the size of the target mipLevel."); + return false; } getIScene().updateTextureBuffer(m_textureBufferHandle, static_cast(mipLevel), offsetX, offsetY, width, height, data); - return StatusOK; + return true; } size_t Texture2DBufferImpl::getMipLevelCount() const @@ -94,33 +98,35 @@ namespace ramses return TextureUtils::GetTextureFormatFromInternal(getIScene().getTextureBuffer(m_textureBufferHandle).textureFormat); } - status_t Texture2DBufferImpl::getMipLevelData(size_t mipLevel, char* buffer, size_t bufferSize) const + bool Texture2DBufferImpl::getMipLevelData(size_t mipLevel, char* buffer, size_t bufferSize) const { const auto& texBuffer = getIScene().getTextureBuffer(m_textureBufferHandle); if (mipLevel >= texBuffer.mipMaps.size()) { - return addErrorEntry("Texture2DBuffer::getMipLevelData failed, requested mipLevel does not exist in Texture2DBuffer."); + getErrorReporting().set("Texture2DBuffer::getMipLevelData failed, requested mipLevel does not exist in Texture2DBuffer."); + return false; } const auto& mipData = texBuffer.mipMaps[mipLevel].data; const size_t dataSizeToCopy = std::min(bufferSize, mipData.size()); - ramses_internal::PlatformMemory::Copy(buffer, mipData.data(), dataSizeToCopy); + ramses::internal::PlatformMemory::Copy(buffer, mipData.data(), dataSizeToCopy); - return StatusOK; + return true; } - status_t Texture2DBufferImpl::getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const + bool Texture2DBufferImpl::getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const { const auto& mipMaps = getIScene().getTextureBuffer(m_textureBufferHandle).mipMaps; if (mipLevel >= mipMaps.size()) { - return addErrorEntry("Texture2DBuffer::getMipLevelSize failed, requested mipLevel does not exist in Texture2DBuffer."); + getErrorReporting().set("Texture2DBuffer::getMipLevelSize failed, requested mipLevel does not exist in Texture2DBuffer."); + return false; } widthOut = mipMaps[mipLevel].width; heightOut = mipMaps[mipLevel].height; - return StatusOK; + return true; } size_t Texture2DBufferImpl::getMipLevelDataSizeInBytes(size_t mipLevel) const @@ -133,23 +139,21 @@ namespace ramses return mip.width * mip.height * GetTexelSizeFromFormat(getIScene().getTextureBuffer(m_textureBufferHandle).textureFormat); } - status_t Texture2DBufferImpl::validate() const + void Texture2DBufferImpl::onValidate(ValidationReportImpl& report) const { - status_t status = SceneObjectImpl::validate(); + SceneObjectImpl::onValidate(report); const auto& iscene = getIScene(); const auto& mips = iscene.getTextureBuffer(getTextureBufferHandle()).mipMaps; const bool isInitialized = (std::accumulate(mips.cbegin(), mips.cend(), 0, [](int32_t area, const auto& m) { return area + m.usedRegion.getArea(); }) > 0); const bool usedAsInput = std::any_of(iscene.getTextureSamplers().cbegin(), iscene.getTextureSamplers().cend(), [handle=getTextureBufferHandle()](const auto& ts) { - return ts.second->contentType == ramses_internal::TextureSampler::ContentType::TextureBuffer && ts.second->contentHandle == handle; + return ts.second->contentType == ramses::internal::TextureSampler::ContentType::TextureBuffer && ts.second->contentHandle == handle; }); if (usedAsInput && !isInitialized) - return addValidationMessage(EValidationSeverity::Warning, "TextureBuffer is used in a sampler but there is no data set, this could lead to graphical glitches if actually rendered."); + return report.add(EIssueType::Warning, "TextureBuffer is used in a sampler but there is no data set, this could lead to graphical glitches if actually rendered.", &getRamsesObject()); if (!usedAsInput) - return addValidationMessage(EValidationSeverity::Warning, "TextureBuffer is not used anywhere, destroy it if not needed."); - - return status; + return report.add(EIssueType::Warning, "TextureBuffer is not used anywhere, destroy it if not needed.", &getRamsesObject()); } } diff --git a/src/client/impl/Texture2DBufferImpl.h b/src/client/impl/Texture2DBufferImpl.h new file mode 100644 index 000000000..6fa053d0b --- /dev/null +++ b/src/client/impl/Texture2DBufferImpl.h @@ -0,0 +1,45 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "SceneObjectImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/MipMapSize.h" +#include "ramses/framework/EDataType.h" +#include "ramses/framework/TextureEnums.h" + +#include + +namespace ramses::internal +{ + class Texture2DBufferImpl : public SceneObjectImpl + { + public: + Texture2DBufferImpl(SceneImpl& scene, std::string_view textureBufferName); + ~Texture2DBufferImpl() override; + + void initializeFrameworkData(const ramses::internal::MipMapDimensions& mipDimensions, ETextureFormat textureFormat); + void deinitializeFrameworkData() override; + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + bool setData(const std::byte* data, size_t mipLevel, uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height); + [[nodiscard]] size_t getMipLevelCount() const; + [[nodiscard]] ETextureFormat getTexelFormat() const; + bool getMipLevelData(size_t mipLevel, char* buffer, size_t bufferSize) const; + bool getMipLevelSize(size_t mipLevel, uint32_t& widthOut, uint32_t& heightOut) const; + [[nodiscard]] size_t getMipLevelDataSizeInBytes(size_t mipLevel) const; + + [[nodiscard]] ramses::internal::TextureBufferHandle getTextureBufferHandle() const; + + private: + ramses::internal::TextureBufferHandle m_textureBufferHandle; + }; +} diff --git a/client/ramses-client/impl/Texture2DImpl.cpp b/src/client/impl/Texture2DImpl.cpp similarity index 70% rename from client/ramses-client/impl/Texture2DImpl.cpp rename to src/client/impl/Texture2DImpl.cpp index f238f2ce4..17b6c08d7 100644 --- a/client/ramses-client/impl/Texture2DImpl.cpp +++ b/src/client/impl/Texture2DImpl.cpp @@ -6,30 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Texture2DImpl.h" -#include "TextureUtils.h" -#include "Components/ManagedResource.h" -#include "ResourceImpl.h" -#include "ClientApplicationLogic.h" +#include "impl/Texture2DImpl.h" +#include "impl/TextureUtils.h" +#include "internal/Components/ManagedResource.h" +#include "impl/ResourceImpl.h" +#include "internal/ClientApplicationLogic.h" #include -namespace ramses +namespace ramses::internal { - Texture2DImpl::Texture2DImpl(ramses_internal::ResourceHashUsage resource, + Texture2DImpl::Texture2DImpl(ramses::internal::ResourceHashUsage resource, SceneImpl& scene, std::string_view name, ERamsesObjectType overrideType /* = ERamsesObjectType_Texture2D*/) : ResourceImpl(overrideType, std::move(resource), scene, name) , m_width(0) , m_height(0) - , m_textureFormat(ETextureFormat::Invalid) + , m_textureFormat(ETextureFormat::RGBA8) { } - Texture2DImpl::~Texture2DImpl() - { - } + Texture2DImpl::~Texture2DImpl() = default; void Texture2DImpl::initializeFromFrameworkData(uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzle& swizzle) { @@ -39,9 +37,10 @@ namespace ramses m_swizzle = swizzle; } - status_t Texture2DImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool Texture2DImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(ResourceImpl::serialize(outStream, serializationContext)); + if (!ResourceImpl::serialize(outStream, serializationContext)) + return false; outStream << m_width; outStream << m_height; @@ -51,12 +50,13 @@ namespace ramses outStream << m_swizzle.channelBlue; outStream << m_swizzle.channelAlpha; - return StatusOK; + return true; } - status_t Texture2DImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool Texture2DImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(ResourceImpl::deserialize(inStream, serializationContext)); + if (!ResourceImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_width; inStream >> m_height; @@ -68,7 +68,7 @@ namespace ramses inStream >> m_swizzle.channelBlue; inStream >> m_swizzle.channelAlpha; - return StatusOK; + return true; } uint32_t Texture2DImpl::getWidth() const diff --git a/client/ramses-client/impl/Texture2DImpl.h b/src/client/impl/Texture2DImpl.h similarity index 60% rename from client/ramses-client/impl/Texture2DImpl.h rename to src/client/impl/Texture2DImpl.h index ea3c3f03a..a0d6d3b8d 100644 --- a/client/ramses-client/impl/Texture2DImpl.h +++ b/src/client/impl/Texture2DImpl.h @@ -6,27 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DIMPL_H -#define RAMSES_TEXTURE2DIMPL_H +#pragma once -#include "ramses-client-api/TextureEnums.h" -#include "ResourceImpl.h" -#include "ramses-client-api/TextureSwizzle.h" +#include "ramses/framework/TextureEnums.h" +#include "impl/ResourceImpl.h" +#include "ramses/client/TextureSwizzle.h" #include -namespace ramses_internal +namespace ramses::internal { class Texture2DResource; } -namespace ramses +namespace ramses::internal { class Texture2DImpl final : public ResourceImpl { public: // overrideType is required if another texture type is reusing the impl, but needs a different type ID - Texture2DImpl(ramses_internal::ResourceHashUsage resource, + Texture2DImpl(ramses::internal::ResourceHashUsage resource, SceneImpl& scene, std::string_view name, ERamsesObjectType overrideType = ERamsesObjectType::Texture2D); @@ -34,13 +33,13 @@ namespace ramses ~Texture2DImpl() override; void initializeFromFrameworkData(uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzle& swizzle); - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + [[nodiscard]] bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + [[nodiscard]] bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - uint32_t getWidth() const; - uint32_t getHeight() const; - ETextureFormat getTextureFormat() const; - const TextureSwizzle& getTextureSwizzle() const; + [[nodiscard]] uint32_t getWidth() const; + [[nodiscard]] uint32_t getHeight() const; + [[nodiscard]] ETextureFormat getTextureFormat() const; + [[nodiscard]] const TextureSwizzle& getTextureSwizzle() const; private: uint32_t m_width; @@ -50,5 +49,3 @@ namespace ramses }; } -#endif - diff --git a/client/ramses-client-api/Texture3D.cpp b/src/client/impl/Texture3D.cpp similarity index 70% rename from client/ramses-client-api/Texture3D.cpp rename to src/client/impl/Texture3D.cpp index 60be1cdfb..9d17a4174 100644 --- a/client/ramses-client-api/Texture3D.cpp +++ b/src/client/impl/Texture3D.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Texture3DImpl.h" -#include "ramses-client-api/Texture3D.h" +#include "impl/Texture3DImpl.h" +#include "ramses/client/Texture3D.h" namespace ramses { - Texture3D::Texture3D(std::unique_ptr impl) + Texture3D::Texture3D(std::unique_ptr impl) : Resource{ std::move(impl) } - , m_impl{ static_cast(Resource::m_impl) } + , m_impl{ static_cast(Resource::m_impl) } { } @@ -36,5 +36,15 @@ namespace ramses { return m_impl.getTextureFormat(); } + + internal::Texture3DImpl& Texture3D::impl() + { + return m_impl; + } + + const internal::Texture3DImpl& Texture3D::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/Texture3DImpl.cpp b/src/client/impl/Texture3DImpl.cpp similarity index 67% rename from client/ramses-client/impl/Texture3DImpl.cpp rename to src/client/impl/Texture3DImpl.cpp index dffc750b4..3671b506e 100644 --- a/client/ramses-client/impl/Texture3DImpl.cpp +++ b/src/client/impl/Texture3DImpl.cpp @@ -6,29 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Texture3DImpl.h" -#include "TextureUtils.h" -#include "Components/ManagedResource.h" -#include "ClientApplicationLogic.h" +#include "impl/Texture3DImpl.h" +#include "impl/TextureUtils.h" +#include "internal/Components/ManagedResource.h" +#include "internal/ClientApplicationLogic.h" #include -namespace ramses +namespace ramses::internal { - Texture3DImpl::Texture3DImpl(ramses_internal::ResourceHashUsage resource, + Texture3DImpl::Texture3DImpl(ramses::internal::ResourceHashUsage resource, SceneImpl& scene, std::string_view name) : ResourceImpl(ERamsesObjectType::Texture3D, std::move(resource), scene, name) , m_width(0) , m_height(0) , m_depth(0) - , m_textureFormat(ETextureFormat::Invalid) + , m_textureFormat(ETextureFormat::RGBA8) { } - Texture3DImpl::~Texture3DImpl() - { - } + Texture3DImpl::~Texture3DImpl() = default; void Texture3DImpl::initializeFromFrameworkData(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat) { @@ -38,21 +36,23 @@ namespace ramses m_textureFormat = textureFormat; } - status_t Texture3DImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool Texture3DImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(ResourceImpl::serialize(outStream, serializationContext)); + if (!ResourceImpl::serialize(outStream, serializationContext)) + return false; outStream << m_width; outStream << m_height; outStream << m_depth; outStream << static_cast(m_textureFormat); - return StatusOK; + return true; } - status_t Texture3DImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool Texture3DImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(ResourceImpl::deserialize(inStream, serializationContext)); + if (!ResourceImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_width; inStream >> m_height; @@ -61,7 +61,7 @@ namespace ramses inStream >> enumInt; m_textureFormat = static_cast(enumInt); - return StatusOK; + return true; } uint32_t Texture3DImpl::getWidth() const diff --git a/client/ramses-client/impl/Texture3DImpl.h b/src/client/impl/Texture3DImpl.h similarity index 57% rename from client/ramses-client/impl/Texture3DImpl.h rename to src/client/impl/Texture3DImpl.h index dc12f0937..b6c0f890a 100644 --- a/client/ramses-client/impl/Texture3DImpl.h +++ b/src/client/impl/Texture3DImpl.h @@ -6,38 +6,37 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE3DIMPL_H -#define RAMSES_TEXTURE3DIMPL_H +#pragma once -#include "ramses-client-api/TextureEnums.h" -#include "ResourceImpl.h" +#include "ramses/framework/TextureEnums.h" +#include "impl/ResourceImpl.h" #include -namespace ramses_internal +namespace ramses::internal { class Texture3DResource; } -namespace ramses +namespace ramses::internal { class Texture3DImpl final : public ResourceImpl { public: - Texture3DImpl(ramses_internal::ResourceHashUsage resource, + Texture3DImpl(ramses::internal::ResourceHashUsage resource, SceneImpl& scene, std::string_view name); ~Texture3DImpl() override; void initializeFromFrameworkData(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat); - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + [[nodiscard]] bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + [[nodiscard]] bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - uint32_t getWidth() const; - uint32_t getHeight() const; - uint32_t getDepth() const; - ETextureFormat getTextureFormat() const; + [[nodiscard]] uint32_t getWidth() const; + [[nodiscard]] uint32_t getHeight() const; + [[nodiscard]] uint32_t getDepth() const; + [[nodiscard]] ETextureFormat getTextureFormat() const; private: uint32_t m_width; uint32_t m_height; @@ -46,5 +45,3 @@ namespace ramses }; } -#endif - diff --git a/client/ramses-client-api/TextureCube.cpp b/src/client/impl/TextureCube.cpp similarity index 67% rename from client/ramses-client-api/TextureCube.cpp rename to src/client/impl/TextureCube.cpp index 68b5edb55..393c6c4d6 100644 --- a/client/ramses-client-api/TextureCube.cpp +++ b/src/client/impl/TextureCube.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TextureCubeImpl.h" -#include "ramses-client-api/TextureCube.h" +#include "impl/TextureCubeImpl.h" +#include "ramses/client/TextureCube.h" namespace ramses { - TextureCube::TextureCube(std::unique_ptr impl) + TextureCube::TextureCube(std::unique_ptr impl) : Resource{ std::move(impl) } - , m_impl{ static_cast(Resource::m_impl) } + , m_impl{ static_cast(Resource::m_impl) } { } @@ -31,4 +31,14 @@ namespace ramses { return m_impl.getTextureSwizzle(); } + + internal::TextureCubeImpl& TextureCube::impl() + { + return m_impl; + } + + const internal::TextureCubeImpl& TextureCube::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/TextureCubeImpl.cpp b/src/client/impl/TextureCubeImpl.cpp similarity index 66% rename from client/ramses-client/impl/TextureCubeImpl.cpp rename to src/client/impl/TextureCubeImpl.cpp index 99a66a0ea..869bd125b 100644 --- a/client/ramses-client/impl/TextureCubeImpl.cpp +++ b/src/client/impl/TextureCubeImpl.cpp @@ -6,23 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TextureCubeImpl.h" -#include "TextureUtils.h" -#include "Components/ManagedResource.h" -#include "ClientApplicationLogic.h" +#include "impl/TextureCubeImpl.h" +#include "impl/TextureUtils.h" +#include "internal/Components/ManagedResource.h" +#include "internal/ClientApplicationLogic.h" -namespace ramses +namespace ramses::internal { - TextureCubeImpl::TextureCubeImpl(ramses_internal::ResourceHashUsage texture, SceneImpl& scene, std::string_view name) + TextureCubeImpl::TextureCubeImpl(ramses::internal::ResourceHashUsage texture, SceneImpl& scene, std::string_view name) : ResourceImpl(ERamsesObjectType::TextureCube, std::move(texture), scene, name) , m_size(0) - , m_textureFormat(ETextureFormat::Invalid) + , m_textureFormat(ETextureFormat::RGBA8) { } - TextureCubeImpl::~TextureCubeImpl() - { - } + TextureCubeImpl::~TextureCubeImpl() = default; void TextureCubeImpl::initializeFromFrameworkData(uint32_t size, ETextureFormat textureFormat, const TextureSwizzle& swizzle) { @@ -31,9 +29,10 @@ namespace ramses m_swizzle = swizzle; } - status_t TextureCubeImpl::serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const + bool TextureCubeImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const { - CHECK_RETURN_ERR(ResourceImpl::serialize(outStream, serializationContext)); + if (!ResourceImpl::serialize(outStream, serializationContext)) + return false; outStream << m_size; outStream << static_cast(m_textureFormat); @@ -42,12 +41,13 @@ namespace ramses outStream << m_swizzle.channelBlue; outStream << m_swizzle.channelAlpha; - return StatusOK; + return true; } - status_t TextureCubeImpl::deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) + bool TextureCubeImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) { - CHECK_RETURN_ERR(ResourceImpl::deserialize(inStream, serializationContext)); + if (!ResourceImpl::deserialize(inStream, serializationContext)) + return false; inStream >> m_size; uint32_t enumInt = 0u; @@ -58,7 +58,7 @@ namespace ramses inStream >> m_swizzle.channelBlue; inStream >> m_swizzle.channelAlpha; - return StatusOK; + return true; } uint32_t TextureCubeImpl::getSize() const diff --git a/client/ramses-client/impl/TextureCubeImpl.h b/src/client/impl/TextureCubeImpl.h similarity index 57% rename from client/ramses-client/impl/TextureCubeImpl.h rename to src/client/impl/TextureCubeImpl.h index 69f8ee8a5..0f0783b20 100644 --- a/client/ramses-client/impl/TextureCubeImpl.h +++ b/src/client/impl/TextureCubeImpl.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURECUBEIMPL_H -#define RAMSES_TEXTURECUBEIMPL_H +#pragma once -#include "ramses-client-api/TextureEnums.h" -#include "ResourceImpl.h" -#include "ramses-client-api/TextureSwizzle.h" +#include "ramses/framework/TextureEnums.h" +#include "impl/ResourceImpl.h" +#include "ramses/client/TextureSwizzle.h" #include -namespace ramses_internal +namespace ramses::internal { class TextureCubeResource; } -namespace ramses +namespace ramses::internal { class TextureCubeImpl final : public ResourceImpl { @@ -28,16 +27,16 @@ namespace ramses /** * @brief This creates a Cube Texture. All texels are pre-allocated and initialized to 0. */ - TextureCubeImpl(ramses_internal::ResourceHashUsage texture, SceneImpl& scene, std::string_view name); + TextureCubeImpl(ramses::internal::ResourceHashUsage texture, SceneImpl& scene, std::string_view name); ~TextureCubeImpl() override; void initializeFromFrameworkData(uint32_t size, ETextureFormat textureFormat, const TextureSwizzle& swizzle); - status_t serialize(ramses_internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; - status_t deserialize(ramses_internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + [[nodiscard]] bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + [[nodiscard]] bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; - uint32_t getSize() const; - ETextureFormat getTextureFormat() const; - const TextureSwizzle& getTextureSwizzle() const; + [[nodiscard]] uint32_t getSize() const; + [[nodiscard]] ETextureFormat getTextureFormat() const; + [[nodiscard]] const TextureSwizzle& getTextureSwizzle() const; private: uint32_t m_size; @@ -45,5 +44,3 @@ namespace ramses TextureSwizzle m_swizzle; }; } - -#endif diff --git a/client/ramses-client-api/TextureSampler.cpp b/src/client/impl/TextureSampler.cpp similarity index 59% rename from client/ramses-client-api/TextureSampler.cpp rename to src/client/impl/TextureSampler.cpp index 35c3722f2..6834e4521 100644 --- a/client/ramses-client-api/TextureSampler.cpp +++ b/src/client/impl/TextureSampler.cpp @@ -7,22 +7,22 @@ // ------------------------------------------------------------------------- // API -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/RenderBuffer.h" -#include "APILoggingMacros.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/RenderBuffer.h" +#include "impl/APILoggingMacros.h" // internal -#include "TextureSamplerImpl.h" +#include "impl/TextureSamplerImpl.h" namespace ramses { - TextureSampler::TextureSampler(std::unique_ptr impl) + TextureSampler::TextureSampler(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } @@ -61,38 +61,48 @@ namespace ramses return m_impl.getTextureType(); } - status_t TextureSampler::setTextureData(const Texture2D& dataSource) + bool TextureSampler::setTextureData(const Texture2D& dataSource) { - const status_t status = m_impl.setTextureData(dataSource); + const bool status = m_impl.setTextureData(dataSource); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(dataSource)); return status; } - status_t TextureSampler::setTextureData(const Texture3D& dataSource) + bool TextureSampler::setTextureData(const Texture3D& dataSource) { - const status_t status = m_impl.setTextureData(dataSource); + const bool status = m_impl.setTextureData(dataSource); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(dataSource)); return status; } - status_t TextureSampler::setTextureData(const TextureCube& dataSource) + bool TextureSampler::setTextureData(const TextureCube& dataSource) { - const status_t status = m_impl.setTextureData(dataSource); + const bool status = m_impl.setTextureData(dataSource); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(dataSource)); return status; } - status_t TextureSampler::setTextureData(const Texture2DBuffer& dataSource) + bool TextureSampler::setTextureData(const Texture2DBuffer& dataSource) { - const status_t status = m_impl.setTextureData(dataSource); + const bool status = m_impl.setTextureData(dataSource); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(dataSource)); return status; } - status_t TextureSampler::setTextureData(const RenderBuffer& dataSource) + bool TextureSampler::setTextureData(const RenderBuffer& dataSource) { - const status_t status = m_impl.setTextureData(dataSource); + const bool status = m_impl.setTextureData(dataSource); LOG_HL_CLIENT_API1(status, LOG_API_RAMSESOBJECT_STRING(dataSource)); return status; } + + internal::TextureSamplerImpl& TextureSampler::impl() + { + return m_impl; + } + + const internal::TextureSamplerImpl& TextureSampler::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client-api/TextureSamplerExternal.cpp b/src/client/impl/TextureSamplerExternal.cpp similarity index 59% rename from client/ramses-client-api/TextureSamplerExternal.cpp rename to src/client/impl/TextureSamplerExternal.cpp index 71afaa7c7..12a24658b 100644 --- a/client/ramses-client-api/TextureSamplerExternal.cpp +++ b/src/client/impl/TextureSamplerExternal.cpp @@ -7,16 +7,26 @@ // ------------------------------------------------------------------------- //API -#include "ramses-client-api/TextureSamplerExternal.h" +#include "ramses/client/TextureSamplerExternal.h" // internal -#include "TextureSamplerImpl.h" +#include "impl/TextureSamplerImpl.h" namespace ramses { - TextureSamplerExternal::TextureSamplerExternal(std::unique_ptr impl) + TextureSamplerExternal::TextureSamplerExternal(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } + + internal::TextureSamplerImpl& TextureSamplerExternal::impl() + { + return m_impl; + } + + const internal::TextureSamplerImpl& TextureSamplerExternal::impl() const + { + return m_impl; + } } diff --git a/src/client/impl/TextureSamplerImpl.cpp b/src/client/impl/TextureSamplerImpl.cpp new file mode 100644 index 000000000..3bc911ff2 --- /dev/null +++ b/src/client/impl/TextureSamplerImpl.cpp @@ -0,0 +1,328 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "impl/Texture2DImpl.h" +#include "impl/Texture3DImpl.h" +#include "impl/TextureCubeImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/Texture2DBufferImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/SceneImpl.h" +#include "impl/TextureUtils.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "impl/ErrorReporting.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/DataSlotUtils.h" + +namespace ramses::internal +{ + TextureSamplerImpl::TextureSamplerImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name) + : SceneObjectImpl(scene, type, name) + , m_textureType(ERamsesObjectType::Invalid) + { + } + + TextureSamplerImpl::~TextureSamplerImpl() = default; + + void TextureSamplerImpl::initializeFrameworkData( + const ramses::internal::TextureSamplerStates& samplerStates, + ERamsesObjectType textureType, + ramses::internal::TextureSampler::ContentType contentType, + ramses::internal::ResourceContentHash textureHash, + ramses::internal::MemoryHandle contentHandle) + { + m_textureType = textureType; + m_textureSamplerHandle = getIScene().allocateTextureSampler({ samplerStates, contentType, textureHash, contentHandle }, {}); + } + + void TextureSamplerImpl::deinitializeFrameworkData() + { + assert(m_textureSamplerHandle.isValid()); + getIScene().releaseTextureSampler(m_textureSamplerHandle); + m_textureSamplerHandle = ramses::internal::TextureSamplerHandle::Invalid(); + } + + bool TextureSamplerImpl::setTextureData(const Texture2D& texture) + { + if (!isFromTheSameSceneAs(texture.impl())) + { + getErrorReporting().set("TextureSampler::setTextureData failed, client texture is not from the same client as this sampler."); + return false; + } + + return setTextureDataInternal(ERamsesObjectType::Texture2D, ramses::internal::TextureSampler::ContentType::ClientTexture, texture.impl().getLowlevelResourceHash(), ramses::internal::InvalidMemoryHandle); + } + + bool TextureSamplerImpl::setTextureData(const Texture3D& texture) + { + if (m_textureType != ERamsesObjectType::Texture3D) + { + getErrorReporting().set("TextureSampler::setTextureData failed, changing data from non 3D texture to 3D texture is not supported. Create a new TextureSampler instead."); + return false; + } + + if (!isFromTheSameSceneAs(texture.impl())) + { + getErrorReporting().set("TextureSampler::setTextureData failed, client texture is not from the same client as this sampler."); + return false; + } + + return setTextureDataInternal(ERamsesObjectType::Texture3D, ramses::internal::TextureSampler::ContentType::ClientTexture, texture.impl().getLowlevelResourceHash(), ramses::internal::InvalidMemoryHandle); + } + + bool TextureSamplerImpl::setTextureData(const TextureCube& texture) + { + if (!isFromTheSameSceneAs(texture.impl())) + { + getErrorReporting().set("TextureSampler::setTextureData failed, client texture is not from the same client as this sampler."); + return false; + } + + return setTextureDataInternal(ERamsesObjectType::TextureCube, ramses::internal::TextureSampler::ContentType::ClientTexture, texture.impl().getLowlevelResourceHash(), ramses::internal::InvalidMemoryHandle); + } + + bool TextureSamplerImpl::setTextureData(const Texture2DBuffer& texture) + { + if (!getSceneImpl().containsSceneObject(texture.impl())) + { + getErrorReporting().set("TextureSampler::setTextureData failed, texture2D buffer is not from the same scene as this sampler."); + return false; + } + + return setTextureDataInternal(ERamsesObjectType::Texture2DBuffer, ramses::internal::TextureSampler::ContentType::TextureBuffer, {}, texture.impl().getTextureBufferHandle().asMemoryHandle()); + } + + bool TextureSamplerImpl::setTextureData(const ramses::RenderBuffer& texture) + { + if (!getSceneImpl().containsSceneObject(texture.impl())) + { + getErrorReporting().set("TextureSampler::setTextureData failed, render buffer is not from the same scene as this sampler."); + return false; + } + + if (ERenderBufferAccessMode::WriteOnly == texture.impl().getAccessMode()) + { + getErrorReporting().set("TextureSampler::setTextureData failed, render buffer has access mode write only."); + return false; + } + + return setTextureDataInternal(ERamsesObjectType::RenderBuffer, ramses::internal::TextureSampler::ContentType::RenderBuffer, {}, texture.impl().getRenderBufferHandle().asMemoryHandle()); + } + + bool TextureSamplerImpl::setTextureDataInternal(ERamsesObjectType textureType, + ramses::internal::TextureSampler::ContentType contentType, + ramses::internal::ResourceContentHash textureHash, + ramses::internal::MemoryHandle contentHandle) + { + if (ramses::internal::DataSlotUtils::HasDataSlotIdForTextureSampler(getIScene(), m_textureSamplerHandle)) + { + // Additional logic would have to be added on renderer side to support change of content to update consumer fallback sampler state. + // Also more checks would be required here to make sure only 2D textures are involved. + getErrorReporting().set("TextureSampler::setTextureData failed, changing texture sampler data for a sampler marked as texture link consumer is not supported. Create a new TextureSampler instead."); + return false; + } + + // With current internal logic re-creating the texture sampler instance adds minimal overhead + // and thus extra support for change of data source on internal scene level is not worth the additional complexity. + // This should be revisited whenever internal logic changes. + + const ramses::internal::TextureSamplerStates samplerStates = getIScene().getTextureSampler(m_textureSamplerHandle).states; + getIScene().releaseTextureSampler(m_textureSamplerHandle); + + // re-allocate with same handle + [[maybe_unused]] const auto handle = getIScene().allocateTextureSampler({ samplerStates, contentType, textureHash, contentHandle }, m_textureSamplerHandle); + assert(m_textureSamplerHandle == handle); + + m_textureType = textureType; + + return true; + } + + ETextureAddressMode TextureSamplerImpl::getWrapUMode() const + { + return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_addressModeU; + } + + ETextureAddressMode TextureSamplerImpl::getWrapVMode() const + { + return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_addressModeV; + } + + ETextureAddressMode TextureSamplerImpl::getWrapRMode() const + { + return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_addressModeR; + } + + ETextureSamplingMethod TextureSamplerImpl::getMinSamplingMethod() const + { + return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_minSamplingMode; + } + + ETextureSamplingMethod TextureSamplerImpl::getMagSamplingMethod() const + { + return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_magSamplingMode; + } + + uint32_t TextureSamplerImpl::getAnisotropyLevel() const + { + return getIScene().getTextureSampler(m_textureSamplerHandle).states.m_anisotropyLevel; + } + + ramses::internal::TextureSamplerHandle TextureSamplerImpl::getTextureSamplerHandle() const + { + return m_textureSamplerHandle; + } + + ERamsesObjectType TextureSamplerImpl::getTextureType() const + { + return m_textureType; + } + + ramses::internal::EDataType TextureSamplerImpl::getTextureDataType() const + { + if (getIScene().getTextureSampler(m_textureSamplerHandle).contentType == ramses::internal::TextureSampler::ContentType::RenderBufferMS) + return ramses::internal::EDataType::TextureSampler2DMS; + + switch (m_textureType) + { + case ERamsesObjectType::Texture2D: + case ERamsesObjectType::RenderBuffer: + case ERamsesObjectType::Texture2DBuffer: return ramses::internal::EDataType::TextureSampler2D; + case ERamsesObjectType::Texture3D: return ramses::internal::EDataType::TextureSampler3D; + case ERamsesObjectType::TextureCube: return ramses::internal::EDataType::TextureSamplerCube; + case ERamsesObjectType::TextureSamplerExternal: return ramses::internal::EDataType::TextureSamplerExternal; + default: break; + } + return ramses::internal::EDataType::Invalid; + } + + bool TextureSamplerImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + outStream << static_cast(m_textureType); + outStream << m_textureSamplerHandle; + + return true; + } + + bool TextureSamplerImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + uint32_t value = 0; + + inStream >> value; + m_textureType = static_cast(value); + + inStream >> m_textureSamplerHandle; + + return true; + } + + void TextureSamplerImpl::onValidate(ValidationReportImpl& report) const + { + SceneObjectImpl::onValidate(report); + + const Resource* resource = nullptr; + const ramses::internal::TextureSampler& sampler = getIScene().getTextureSampler(m_textureSamplerHandle); + switch (sampler.contentType) + { + case ramses::internal::TextureSampler::ContentType::ClientTexture: + resource = getSceneImpl().scanForResourceWithHash(sampler.textureResource); + if (resource == nullptr) + return report.add(EIssueType::Error, "Client texture set in TextureSampler does not exist", &getRamsesObject()); + validateResource(report, resource); + break; + case ramses::internal::TextureSampler::ContentType::RenderBuffer: + case ramses::internal::TextureSampler::ContentType::RenderBufferMS: + validateRenderBuffer(report, ramses::internal::RenderBufferHandle(sampler.contentHandle)); + break; + case ramses::internal::TextureSampler::ContentType::TextureBuffer: + validateTextureBuffer(report, ramses::internal::TextureBufferHandle(sampler.contentHandle)); + break; + case ramses::internal::TextureSampler::ContentType::ExternalTexture: + break; + case ramses::internal::TextureSampler::ContentType::OffscreenBuffer: + case ramses::internal::TextureSampler::ContentType::StreamBuffer: + case ramses::internal::TextureSampler::ContentType::None: + return report.add(EIssueType::Error, "There is no valid content source set in TextureSampler", &getRamsesObject()); + } + } + + void TextureSamplerImpl::validateRenderBuffer(ValidationReportImpl& report, ramses::internal::RenderBufferHandle renderBufferHandle) const + { + bool foundRenderBuffer = false; + + const bool isRenderBufferValid = getIScene().isRenderBufferAllocated(renderBufferHandle); + if (isRenderBufferValid) + { + SceneObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::RenderBuffer); + while (const auto* renderBuffer = iter.getNext()) + { + if (renderBufferHandle == renderBuffer->impl().getRenderBufferHandle()) + { + foundRenderBuffer = true; + break; + } + } + } + + if (!foundRenderBuffer) + report.add(EIssueType::Error, "Texture Sampler is using a RenderBuffer which does not exist", &getRamsesObject()); + } + + void TextureSamplerImpl::validateTextureBuffer(ValidationReportImpl& report, ramses::internal::TextureBufferHandle textureBufferHandle) const + { + bool foundTextureBuffer = false; + + const bool isTextureBufferValid = getIScene().isTextureBufferAllocated(textureBufferHandle); + if (isTextureBufferValid) + { + SceneObjectRegistryIterator iter(getSceneImpl().getObjectRegistry(), ERamsesObjectType::Texture2DBuffer); + while (const auto* textureBuffer = iter.getNext()) + { + if (textureBufferHandle == textureBuffer->impl().getTextureBufferHandle()) + { + foundTextureBuffer = true; + break; + } + } + } + + if (!foundTextureBuffer) + report.add(EIssueType::Error, "Texture Sampler is using a TextureBuffer which does not exist", &getRamsesObject()); + } + + void TextureSamplerImpl::validateResource(ValidationReportImpl& report, const Resource* resource) const + { + assert(resource); + const ERamsesObjectType resourceType = resource->getType(); + switch (resourceType) + { + case ERamsesObjectType::Texture2D: + case ERamsesObjectType::Texture3D: + case ERamsesObjectType::TextureCube: + report.addDependentObject(*this, resource->impl()); + break; + default: + assert(false); + break; + } + } +} diff --git a/src/client/impl/TextureSamplerImpl.h b/src/client/impl/TextureSamplerImpl.h new file mode 100644 index 000000000..5d18c79f9 --- /dev/null +++ b/src/client/impl/TextureSamplerImpl.h @@ -0,0 +1,85 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/TextureEnums.h" + +// internal +#include "SceneObjectImpl.h" + +// ramses framework +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" + +#include + +namespace ramses +{ + class Texture2D; + class Texture3D; + class TextureCube; + class Texture2DBuffer; + class RenderBuffer; + class Resource; +} + +namespace ramses::internal +{ + class TextureSamplerImpl final : public SceneObjectImpl + { + public: + TextureSamplerImpl(SceneImpl& scene, ERamsesObjectType type, std::string_view name); + ~TextureSamplerImpl() override; + + void initializeFrameworkData( + const ramses::internal::TextureSamplerStates& samplerStates, + ERamsesObjectType textureType, + ramses::internal::TextureSampler::ContentType contentType, + ramses::internal::ResourceContentHash textureHash, + ramses::internal::MemoryHandle contentHandle); + + void deinitializeFrameworkData() override; + [[nodiscard]] bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + [[nodiscard]] bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + void onValidate(ValidationReportImpl& report) const override; + + [[nodiscard]] ETextureAddressMode getWrapUMode() const; + [[nodiscard]] ETextureAddressMode getWrapVMode() const; + [[nodiscard]] ETextureAddressMode getWrapRMode() const; + [[nodiscard]] ETextureSamplingMethod getMinSamplingMethod() const; + [[nodiscard]] ETextureSamplingMethod getMagSamplingMethod() const; + [[nodiscard]] uint32_t getAnisotropyLevel() const; + + [[nodiscard]] bool setTextureData(const Texture2D& texture); + [[nodiscard]] bool setTextureData(const Texture3D& texture); + [[nodiscard]] bool setTextureData(const TextureCube& texture); + [[nodiscard]] bool setTextureData(const Texture2DBuffer& texture); + [[nodiscard]] bool setTextureData(const ramses::RenderBuffer& texture); + + [[nodiscard]] ramses::internal::TextureSamplerHandle getTextureSamplerHandle() const; + [[nodiscard]] ramses::internal::EDataType getTextureDataType() const; + [[nodiscard]] ERamsesObjectType getTextureType() const; + + private: + [[nodiscard]] bool setTextureDataInternal(ERamsesObjectType textureType, + ramses::internal::TextureSampler::ContentType contentType, + ramses::internal::ResourceContentHash textureHash, + ramses::internal::MemoryHandle contentHandle); + + void validateRenderBuffer(ValidationReportImpl& report, ramses::internal::RenderBufferHandle renderBufferHandle) const; + void validateTextureBuffer(ValidationReportImpl& report, ramses::internal::TextureBufferHandle textureBufferHandle) const; + void validateResource(ValidationReportImpl& report, const Resource* resource) const; + + ERamsesObjectType m_textureType; + + ramses::internal::TextureSamplerHandle m_textureSamplerHandle; + }; +} diff --git a/client/ramses-client-api/TextureSamplerMS.cpp b/src/client/impl/TextureSamplerMS.cpp similarity index 55% rename from client/ramses-client-api/TextureSamplerMS.cpp rename to src/client/impl/TextureSamplerMS.cpp index 689d7e6c3..11704311a 100644 --- a/client/ramses-client-api/TextureSamplerMS.cpp +++ b/src/client/impl/TextureSamplerMS.cpp @@ -7,16 +7,26 @@ // ------------------------------------------------------------------------- //API -#include "ramses-client-api/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerMS.h" // internal -#include "TextureSamplerImpl.h" +#include "impl/TextureSamplerImpl.h" namespace ramses { - TextureSamplerMS::TextureSamplerMS(std::unique_ptr impl) + TextureSamplerMS::TextureSamplerMS(std::unique_ptr impl) : SceneObject{ std::move(impl) } - , m_impl{ static_cast(SceneObject::m_impl) } + , m_impl{ static_cast(SceneObject::m_impl) } { } + + internal::TextureSamplerImpl& TextureSamplerMS::impl() + { + return m_impl; + } + + const internal::TextureSamplerImpl& TextureSamplerMS::impl() const + { + return m_impl; + } } diff --git a/client/ramses-client/impl/TextureUtils.cpp b/src/client/impl/TextureUtils.cpp similarity index 60% rename from client/ramses-client/impl/TextureUtils.cpp rename to src/client/impl/TextureUtils.cpp index 679f24dca..688a95ce0 100644 --- a/client/ramses-client/impl/TextureUtils.cpp +++ b/src/client/impl/TextureUtils.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TextureUtils.h" -#include "Utils/TextureMathUtils.h" -#include "Utils/LogMacros.h" -#include "Utils/Image.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "impl/TextureUtils.h" +#include "internal/Core/Utils/TextureMathUtils.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/Image.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" -namespace ramses +namespace ramses::internal { // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipDataSizes(ramses_internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const MipLevelData mipLevelData[]) + void TextureUtils::FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const MipLevelData mipLevelData[]) { assert(mipDataSizes.empty()); mipDataSizes.reserve(mipMapCount); @@ -27,7 +27,7 @@ namespace ramses } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipDataSizes(ramses_internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]) + void TextureUtils::FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]) { assert(mipDataSizes.empty()); mipDataSizes.reserve(mipMapCount); @@ -39,53 +39,53 @@ namespace ramses } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipData(uint8_t* dest, uint32_t mipMapCount, const MipLevelData mipLevelData[]) + void TextureUtils::FillMipData(std::byte* dest, uint32_t mipMapCount, const MipLevelData mipLevelData[]) { for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_size; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_data, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_data, mipDataSize); dest += mipDataSize; } } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipData(uint8_t* dest, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]) + void TextureUtils::FillMipData(std::byte* dest, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]) { for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPX, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPX, mipDataSize); dest += mipDataSize; } for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNX, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNX, mipDataSize); dest += mipDataSize; } for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPY, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPY, mipDataSize); dest += mipDataSize; } for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNY, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNY, mipDataSize); dest += mipDataSize; } for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPZ, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPZ, mipDataSize); dest += mipDataSize; } for (uint32_t i = 0u; i < mipMapCount; ++i) { const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses_internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNZ, mipDataSize); + ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNZ, mipDataSize); dest += mipDataSize; } } @@ -105,26 +105,26 @@ namespace ramses return false; } - const uint32_t mipWidth = ramses_internal::TextureMathUtils::GetMipSize(i, width); - const uint32_t mipHeight = ramses_internal::TextureMathUtils::GetMipSize(i, height); - const uint32_t mipDepth = ramses_internal::TextureMathUtils::GetMipSize(i, depth); - const ramses_internal::ETextureFormat internalFormat = TextureUtils::GetTextureFormatInternal(format); - if (!ramses_internal::IsFormatCompressed(internalFormat)) + const uint32_t mipWidth = ramses::internal::TextureMathUtils::GetMipSize(i, width); + const uint32_t mipHeight = ramses::internal::TextureMathUtils::GetMipSize(i, height); + const uint32_t mipDepth = ramses::internal::TextureMathUtils::GetMipSize(i, depth); + const ramses::internal::EPixelStorageFormat internalFormat = TextureUtils::GetTextureFormatInternal(format); + if (!ramses::internal::IsFormatCompressed(internalFormat)) { - const uint32_t expectedMipDataSize = mipWidth * mipHeight * mipDepth * ramses_internal::GetTexelSizeFromFormat(internalFormat); + const uint32_t expectedMipDataSize = mipWidth * mipHeight * mipDepth * ramses::internal::GetTexelSizeFromFormat(internalFormat); if (mipLevelData[i].m_size < expectedMipDataSize) { return false; } - else if (mipLevelData[i].m_size > expectedMipDataSize) + if (mipLevelData[i].m_size > expectedMipDataSize) { - LOG_WARN(ramses_internal::CONTEXT_CLIENT, "Provided texture mip data does not match expected size, texture might not be as expected"); + LOG_WARN(ramses::internal::CONTEXT_CLIENT, "Provided texture mip data does not match expected size, texture might not be as expected"); } } if (!TextureUtils::IsTextureSizeSupportedByFormat(mipWidth, mipHeight, format)) { - LOG_WARN(ramses_internal::CONTEXT_CLIENT, "Provided texture mip " << i << " might fail to be uploaded due to its size " + LOG_WARN(ramses::internal::CONTEXT_CLIENT, "Provided texture mip " << i << " might fail to be uploaded due to its size " << mipWidth << "x" << mipHeight << " not supported by used format " << toString(format)); } } @@ -133,11 +133,9 @@ namespace ramses } // NOLINTNEXTLINE(modernize-avoid-c-arrays) - bool TextureUtils::MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format) + bool TextureUtils::MipDataValid(uint32_t width, [[maybe_unused]] uint32_t height, [[maybe_unused]] uint32_t depth, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format) { // wrapper so that this function can be called from template following 2D/3D texture function signature - UNUSED(height); - UNUSED(depth); return MipDataValid(width, mipMapCount, mipLevelData, format); } @@ -162,24 +160,24 @@ namespace ramses return false; } - const uint32_t mipSize = ramses_internal::TextureMathUtils::GetMipSize(i, size); - const ramses_internal::ETextureFormat internalFormat = TextureUtils::GetTextureFormatInternal(format); - if (!ramses_internal::IsFormatCompressed(internalFormat)) + const uint32_t mipSize = ramses::internal::TextureMathUtils::GetMipSize(i, size); + const ramses::internal::EPixelStorageFormat internalFormat = TextureUtils::GetTextureFormatInternal(format); + if (!ramses::internal::IsFormatCompressed(internalFormat)) { - const uint32_t expectedMipDataSize = mipSize * mipSize * ramses_internal::GetTexelSizeFromFormat(internalFormat); + const uint32_t expectedMipDataSize = mipSize * mipSize * ramses::internal::GetTexelSizeFromFormat(internalFormat); if (mipLevelData[i].m_faceDataSize < expectedMipDataSize) { return false; } - else if (mipLevelData[i].m_faceDataSize > expectedMipDataSize) + if (mipLevelData[i].m_faceDataSize > expectedMipDataSize) { - LOG_WARN(ramses_internal::CONTEXT_CLIENT, "Provided texture mip data does not match expected size, texture might not be as expected"); + LOG_WARN(ramses::internal::CONTEXT_CLIENT, "Provided texture mip data does not match expected size, texture might not be as expected"); } } if (!TextureUtils::IsTextureSizeSupportedByFormat(mipSize, mipSize, format)) { - LOG_WARN(ramses_internal::CONTEXT_CLIENT, "Provided texture mip " << i << " might fail to be uploaded due to its size " + LOG_WARN(ramses::internal::CONTEXT_CLIENT, "Provided texture mip " << i << " might fail to be uploaded due to its size " << mipSize << "x" << mipSize << " not supported by used format " << toString(format)); } } @@ -191,14 +189,14 @@ namespace ramses { if (width == 0u || height == 0u || depth == 0u) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "TextureParametersValid: texture size cannot be 0."); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "TextureParametersValid: texture size cannot be 0."); return false; } - const uint32_t fullChainMipMapCount = ramses_internal::TextureMathUtils::GetMipLevelCount(width, height, depth); + const uint32_t fullChainMipMapCount = ramses::internal::TextureMathUtils::GetMipLevelCount(width, height, depth); if (mipMapCount > fullChainMipMapCount) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "TextureParametersValid: too many mip levels provided."); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "TextureParametersValid: too many mip levels provided."); return false; } diff --git a/src/client/impl/TextureUtils.h b/src/client/impl/TextureUtils.h new file mode 100644 index 000000000..77ea4ac60 --- /dev/null +++ b/src/client/impl/TextureUtils.h @@ -0,0 +1,503 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/client/RamsesClient.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/client/TextureSwizzle.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses +{ + class Texture2D; +} + +namespace ramses::internal +{ + class TextureUtils + { + public: + static ramses::internal::EPixelStorageFormat GetTextureFormatInternal(ETextureFormat textureformat) + { + switch (textureformat) + { + case ETextureFormat::R8: + return ramses::internal::EPixelStorageFormat::R8; + + case ETextureFormat::RG8: + return ramses::internal::EPixelStorageFormat::RG8; + + case ETextureFormat::RGB8: + return ramses::internal::EPixelStorageFormat::RGB8; + case ETextureFormat::RGB565: + return ramses::internal::EPixelStorageFormat::RGB565; + + case ETextureFormat::RGBA8: + return ramses::internal::EPixelStorageFormat::RGBA8; + case ETextureFormat::RGBA4: + return ramses::internal::EPixelStorageFormat::RGBA4; + case ETextureFormat::RGBA5551: + return ramses::internal::EPixelStorageFormat::RGBA5551; + + case ETextureFormat::ETC2RGB: + return ramses::internal::EPixelStorageFormat::ETC2RGB; + case ETextureFormat::ETC2RGBA: + return ramses::internal::EPixelStorageFormat::ETC2RGBA; + case ETextureFormat::ASTC_RGBA_4x4: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_4x4; + case ETextureFormat::ASTC_RGBA_5x4: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_5x4; + case ETextureFormat::ASTC_RGBA_5x5: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_5x5; + case ETextureFormat::ASTC_RGBA_6x5: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_6x5; + case ETextureFormat::ASTC_RGBA_6x6: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_6x6; + case ETextureFormat::ASTC_RGBA_8x5: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x5; + case ETextureFormat::ASTC_RGBA_8x6: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x6; + case ETextureFormat::ASTC_RGBA_8x8: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x8; + case ETextureFormat::ASTC_RGBA_10x5: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x5; + case ETextureFormat::ASTC_RGBA_10x6: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x6; + case ETextureFormat::ASTC_RGBA_10x8: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x8; + case ETextureFormat::ASTC_RGBA_10x10: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x10; + case ETextureFormat::ASTC_RGBA_12x10: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_12x10; + case ETextureFormat::ASTC_RGBA_12x12: + return ramses::internal::EPixelStorageFormat::ASTC_RGBA_12x12; + case ETextureFormat::ASTC_SRGBA_4x4: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_4x4; + case ETextureFormat::ASTC_SRGBA_5x4: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_5x4; + case ETextureFormat::ASTC_SRGBA_5x5: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_5x5; + case ETextureFormat::ASTC_SRGBA_6x5: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_6x5; + case ETextureFormat::ASTC_SRGBA_6x6: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_6x6; + case ETextureFormat::ASTC_SRGBA_8x5: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x5; + case ETextureFormat::ASTC_SRGBA_8x6: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x6; + case ETextureFormat::ASTC_SRGBA_8x8: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x8; + case ETextureFormat::ASTC_SRGBA_10x5: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x5; + case ETextureFormat::ASTC_SRGBA_10x6: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x6; + case ETextureFormat::ASTC_SRGBA_10x8: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x8; + case ETextureFormat::ASTC_SRGBA_10x10: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x10; + case ETextureFormat::ASTC_SRGBA_12x10: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_12x10; + case ETextureFormat::ASTC_SRGBA_12x12: + return ramses::internal::EPixelStorageFormat::ASTC_SRGBA_12x12; + case ETextureFormat::R16F: + return ramses::internal::EPixelStorageFormat::R16F; + case ETextureFormat::R32F: + return ramses::internal::EPixelStorageFormat::R32F; + case ETextureFormat::RG16F: + return ramses::internal::EPixelStorageFormat::RG16F; + case ETextureFormat::RG32F: + return ramses::internal::EPixelStorageFormat::RG32F; + case ETextureFormat::RGB16F: + return ramses::internal::EPixelStorageFormat::RGB16F; + case ETextureFormat::RGB32F: + return ramses::internal::EPixelStorageFormat::RGB32F; + case ETextureFormat::RGBA16F: + return ramses::internal::EPixelStorageFormat::RGBA16F; + case ETextureFormat::RGBA32F: + return ramses::internal::EPixelStorageFormat::RGBA32F; + + case ETextureFormat::SRGB8: + return ramses::internal::EPixelStorageFormat::SRGB8; + case ETextureFormat::SRGB8_ALPHA8: + return ramses::internal::EPixelStorageFormat::SRGB8_ALPHA8; + } + + assert(false); + return ramses::internal::EPixelStorageFormat::RGBA8; + } + + static ETextureFormat GetTextureFormatFromInternal(ramses::internal::EPixelStorageFormat textureformat) + { + switch (textureformat) + { + case ramses::internal::EPixelStorageFormat::R8: + return ETextureFormat::R8; + + case ramses::internal::EPixelStorageFormat::RG8: + return ETextureFormat::RG8; + + case ramses::internal::EPixelStorageFormat::RGB8: + return ETextureFormat::RGB8; + case ramses::internal::EPixelStorageFormat::RGB565: + return ETextureFormat::RGB565; + + case ramses::internal::EPixelStorageFormat::RGBA8: + return ETextureFormat::RGBA8; + case ramses::internal::EPixelStorageFormat::RGBA4: + return ETextureFormat::RGBA4; + case ramses::internal::EPixelStorageFormat::RGBA5551: + return ETextureFormat::RGBA5551; + + case ramses::internal::EPixelStorageFormat::ETC2RGB: + return ETextureFormat::ETC2RGB; + case ramses::internal::EPixelStorageFormat::ETC2RGBA: + return ETextureFormat::ETC2RGBA; + + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_4x4: + return ETextureFormat::ASTC_RGBA_4x4; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_5x4: + return ETextureFormat::ASTC_RGBA_5x4; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_5x5: + return ETextureFormat::ASTC_RGBA_5x5; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_6x5: + return ETextureFormat::ASTC_RGBA_6x5; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_6x6: + return ETextureFormat::ASTC_RGBA_6x6; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x5: + return ETextureFormat::ASTC_RGBA_8x5; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x6: + return ETextureFormat::ASTC_RGBA_8x6; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x8: + return ETextureFormat::ASTC_RGBA_8x8; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x5: + return ETextureFormat::ASTC_RGBA_10x5; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x6: + return ETextureFormat::ASTC_RGBA_10x6; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x8: + return ETextureFormat::ASTC_RGBA_10x8; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x10: + return ETextureFormat::ASTC_RGBA_10x10; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_12x10: + return ETextureFormat::ASTC_RGBA_12x10; + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_12x12: + return ETextureFormat::ASTC_RGBA_12x12; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_4x4: + return ETextureFormat::ASTC_SRGBA_4x4; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_5x4: + return ETextureFormat::ASTC_SRGBA_5x4; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_5x5: + return ETextureFormat::ASTC_SRGBA_5x5; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_6x5: + return ETextureFormat::ASTC_SRGBA_6x5; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_6x6: + return ETextureFormat::ASTC_SRGBA_6x6; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x5: + return ETextureFormat::ASTC_SRGBA_8x5; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x6: + return ETextureFormat::ASTC_SRGBA_8x6; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x8: + return ETextureFormat::ASTC_SRGBA_8x8; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x5: + return ETextureFormat::ASTC_SRGBA_10x5; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x6: + return ETextureFormat::ASTC_SRGBA_10x6; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x8: + return ETextureFormat::ASTC_SRGBA_10x8; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x10: + return ETextureFormat::ASTC_SRGBA_10x10; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_12x10: + return ETextureFormat::ASTC_SRGBA_12x10; + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_12x12: + return ETextureFormat::ASTC_SRGBA_12x12; + + case ramses::internal::EPixelStorageFormat::R16F: + return ETextureFormat::R16F; + case ramses::internal::EPixelStorageFormat::R32F: + return ETextureFormat::R32F; + case ramses::internal::EPixelStorageFormat::RG16F: + return ETextureFormat::RG16F; + case ramses::internal::EPixelStorageFormat::RG32F: + return ETextureFormat::RG32F; + case ramses::internal::EPixelStorageFormat::RGB16F: + return ETextureFormat::RGB16F; + case ramses::internal::EPixelStorageFormat::RGB32F: + return ETextureFormat::RGB32F; + case ramses::internal::EPixelStorageFormat::RGBA16F: + return ETextureFormat::RGBA16F; + case ramses::internal::EPixelStorageFormat::RGBA32F: + return ETextureFormat::RGBA32F; + + case ramses::internal::EPixelStorageFormat::SRGB8: + return ETextureFormat::SRGB8; + case ramses::internal::EPixelStorageFormat::SRGB8_ALPHA8: + return ETextureFormat::SRGB8_ALPHA8; + + case ramses::internal::EPixelStorageFormat::Depth16: + case ramses::internal::EPixelStorageFormat::Depth24: + case ramses::internal::EPixelStorageFormat::Depth32: + case ramses::internal::EPixelStorageFormat::Depth24_Stencil8: + case ramses::internal::EPixelStorageFormat::Invalid: + break; + } + assert(false); + return ETextureFormat::RGBA8; + } + + static ramses::internal::TextureSwizzleArray GetTextureSwizzleInternal(const TextureSwizzle& swizzle) + { + return ramses::internal::TextureSwizzleArray{ + swizzle.channelRed, + swizzle.channelGreen, + swizzle.channelBlue, + swizzle.channelAlpha, + }; + } + + static TextureSwizzle GetTextureSwizzleFromInternal(const ramses::internal::TextureSwizzleArray& swizzle) + { + return TextureSwizzle{ + swizzle[0], + swizzle[1], + swizzle[2], + swizzle[3] + }; + } + + static bool IsTextureSizeSupportedByFormat(uint32_t width, uint32_t height, ETextureFormat textureformat) + { + switch (textureformat) + { + case ETextureFormat::R8: + case ETextureFormat::RG8: + case ETextureFormat::RGB8: + case ETextureFormat::RGB565: + case ETextureFormat::RGBA8: + case ETextureFormat::RGBA4: + case ETextureFormat::RGBA5551: + case ETextureFormat::R16F: + case ETextureFormat::R32F: + case ETextureFormat::RG16F: + case ETextureFormat::RG32F: + case ETextureFormat::RGB16F: + case ETextureFormat::RGB32F: + case ETextureFormat::RGBA16F: + case ETextureFormat::RGBA32F: + case ETextureFormat::SRGB8: + case ETextureFormat::SRGB8_ALPHA8: + // no special requirements + return true; + + case ETextureFormat::ETC2RGB: + case ETextureFormat::ETC2RGBA: + case ETextureFormat::ASTC_RGBA_4x4: + case ETextureFormat::ASTC_SRGBA_4x4: + return (width % 4 == 0) && (height % 4 == 0); + + case ETextureFormat::ASTC_RGBA_5x4: + case ETextureFormat::ASTC_SRGBA_5x4: + return (width % 5 == 0) && (height % 4 == 0); + + case ETextureFormat::ASTC_RGBA_5x5: + case ETextureFormat::ASTC_SRGBA_5x5: + return (width % 5 == 0) && (height % 5 == 0); + + case ETextureFormat::ASTC_RGBA_6x5: + case ETextureFormat::ASTC_SRGBA_6x5: + return (width % 6 == 0) && (height % 5 == 0); + + case ETextureFormat::ASTC_RGBA_6x6: + case ETextureFormat::ASTC_SRGBA_6x6: + return (width % 6 == 0) && (height % 6 == 0); + + case ETextureFormat::ASTC_RGBA_8x5: + case ETextureFormat::ASTC_SRGBA_8x5: + return (width % 8 == 0) && (height % 5 == 0); + + case ETextureFormat::ASTC_RGBA_8x6: + case ETextureFormat::ASTC_SRGBA_8x6: + return (width % 8 == 0) && (height % 6 == 0); + + case ETextureFormat::ASTC_RGBA_8x8: + case ETextureFormat::ASTC_SRGBA_8x8: + return (width % 8 == 0) && (height % 8 == 0); + + case ETextureFormat::ASTC_RGBA_10x5: + case ETextureFormat::ASTC_SRGBA_10x5: + return (width % 10 == 0) && (height % 5 == 0); + + case ETextureFormat::ASTC_RGBA_10x6: + case ETextureFormat::ASTC_SRGBA_10x6: + return (width % 10 == 0) && (height % 6 == 0); + + case ETextureFormat::ASTC_RGBA_10x8: + case ETextureFormat::ASTC_SRGBA_10x8: + return (width % 10 == 0) && (height % 8 == 0); + + case ETextureFormat::ASTC_RGBA_10x10: + case ETextureFormat::ASTC_SRGBA_10x10: + return (width % 10 == 0) && (height % 10 == 0); + + case ETextureFormat::ASTC_RGBA_12x10: + case ETextureFormat::ASTC_SRGBA_12x10: + return (width % 12 == 0) && (height % 10 == 0); + + case ETextureFormat::ASTC_RGBA_12x12: + case ETextureFormat::ASTC_SRGBA_12x12: + return (width % 12 == 0) && (height % 12 == 0); + } + + assert(false); + return false; + } + + static ramses::internal::EPixelStorageFormat GetRenderBufferFormatInternal(ERenderBufferFormat bufferFormat) + { + switch (bufferFormat) + { + case ERenderBufferFormat::RGBA4: + return ramses::internal::EPixelStorageFormat::RGBA4; + case ERenderBufferFormat::R8: + return ramses::internal::EPixelStorageFormat::R8; + case ERenderBufferFormat::RG8: + return ramses::internal::EPixelStorageFormat::RG8; + case ERenderBufferFormat::RGB8: + return ramses::internal::EPixelStorageFormat::RGB8; + case ERenderBufferFormat::RGBA8: + return ramses::internal::EPixelStorageFormat::RGBA8; + case ERenderBufferFormat::R16F: + return ramses::internal::EPixelStorageFormat::R16F; + case ERenderBufferFormat::R32F: + return ramses::internal::EPixelStorageFormat::R32F; + case ERenderBufferFormat::RG16F: + return ramses::internal::EPixelStorageFormat::RG16F; + case ERenderBufferFormat::RG32F: + return ramses::internal::EPixelStorageFormat::RG32F; + case ERenderBufferFormat::RGB16F: + return ramses::internal::EPixelStorageFormat::RGB16F; + case ERenderBufferFormat::RGB32F: + return ramses::internal::EPixelStorageFormat::RGB32F; + case ERenderBufferFormat::RGBA16F: + return ramses::internal::EPixelStorageFormat::RGBA16F; + case ERenderBufferFormat::RGBA32F: + return ramses::internal::EPixelStorageFormat::RGBA32F; + case ERenderBufferFormat::Depth16: + return ramses::internal::EPixelStorageFormat::Depth16; + case ERenderBufferFormat::Depth24: + return ramses::internal::EPixelStorageFormat::Depth24; + case ERenderBufferFormat::Depth32: + return ramses::internal::EPixelStorageFormat::Depth32; + case ERenderBufferFormat::Depth24_Stencil8: + return ramses::internal::EPixelStorageFormat::Depth24_Stencil8; + } + assert(false); + return ramses::internal::EPixelStorageFormat::Invalid; + } + + static ERenderBufferFormat GetRenderBufferFormatFromInternal(ramses::internal::EPixelStorageFormat bufferFormat) + { + switch (bufferFormat) + { + case ramses::internal::EPixelStorageFormat::RGBA4: + return ERenderBufferFormat::RGBA4; + case ramses::internal::EPixelStorageFormat::R8: + return ERenderBufferFormat::R8; + case ramses::internal::EPixelStorageFormat::RG8: + return ERenderBufferFormat::RG8; + case ramses::internal::EPixelStorageFormat::RGB8: + return ERenderBufferFormat::RGB8; + case ramses::internal::EPixelStorageFormat::RGBA8: + return ERenderBufferFormat::RGBA8; + case ramses::internal::EPixelStorageFormat::R16F: + return ERenderBufferFormat::R16F; + case ramses::internal::EPixelStorageFormat::R32F: + return ERenderBufferFormat::R32F; + case ramses::internal::EPixelStorageFormat::RG16F: + return ERenderBufferFormat::RG16F; + case ramses::internal::EPixelStorageFormat::RG32F: + return ERenderBufferFormat::RG32F; + case ramses::internal::EPixelStorageFormat::RGB16F: + return ERenderBufferFormat::RGB16F; + case ramses::internal::EPixelStorageFormat::RGB32F: + return ERenderBufferFormat::RGB32F; + case ramses::internal::EPixelStorageFormat::RGBA16F: + return ERenderBufferFormat::RGBA16F; + case ramses::internal::EPixelStorageFormat::RGBA32F: + return ERenderBufferFormat::RGBA32F; + + case ramses::internal::EPixelStorageFormat::Depth16: + return ERenderBufferFormat::Depth16; + case ramses::internal::EPixelStorageFormat::Depth24: + return ERenderBufferFormat::Depth24; + case ramses::internal::EPixelStorageFormat::Depth32: + return ERenderBufferFormat::Depth32; + case ramses::internal::EPixelStorageFormat::Depth24_Stencil8: + return ERenderBufferFormat::Depth24_Stencil8; + + case ramses::internal::EPixelStorageFormat::Invalid: + case ramses::internal::EPixelStorageFormat::RGB565: + case ramses::internal::EPixelStorageFormat::RGBA5551: + case ramses::internal::EPixelStorageFormat::ETC2RGB: + case ramses::internal::EPixelStorageFormat::ETC2RGBA: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_4x4: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_5x4: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_5x5: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_6x5: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_6x6: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x5: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x6: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_8x8: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x5: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x6: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x8: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_10x10: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_12x10: + case ramses::internal::EPixelStorageFormat::ASTC_RGBA_12x12: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_4x4: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_5x4: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_5x5: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_6x5: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_6x6: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x5: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x6: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_8x8: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x5: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x6: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x8: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_10x10: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_12x10: + case ramses::internal::EPixelStorageFormat::ASTC_SRGBA_12x12: + case ramses::internal::EPixelStorageFormat::SRGB8: + case ramses::internal::EPixelStorageFormat::SRGB8_ALPHA8: + break; + } + assert(false); + return ERenderBufferFormat::RGBA8; + } + + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static void FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const MipLevelData mipLevelData[]); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static void FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static void FillMipData(std::byte* dest, uint32_t mipMapCount, const MipLevelData mipLevelData[]); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static void FillMipData(std::byte* dest, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const MipLevelData mipLevelData[], ETextureFormat format); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format); + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + static bool MipDataValid(uint32_t size, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format); + static bool TextureParametersValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount); + }; +} diff --git a/src/client/impl/UniformInput.cpp b/src/client/impl/UniformInput.cpp new file mode 100644 index 000000000..f51d95e62 --- /dev/null +++ b/src/client/impl/UniformInput.cpp @@ -0,0 +1,28 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/UniformInput.h" +#include "impl/EffectInputImpl.h" + +namespace ramses +{ + UniformInput::UniformInput() + : EffectInput{ std::make_unique() } + { + } + + EEffectUniformSemantic UniformInput::getSemantics() const + { + return m_impl->getUniformSemantics(); + } + + size_t UniformInput::getElementCount() const + { + return m_impl->getElementCount(); + } +} diff --git a/client/logic/lib/impl/AnchorPoint.cpp b/src/client/impl/logic/AnchorPoint.cpp similarity index 57% rename from client/logic/lib/impl/AnchorPoint.cpp rename to src/client/impl/logic/AnchorPoint.cpp index 739c6c1bd..25d57c69a 100644 --- a/client/logic/lib/impl/AnchorPoint.cpp +++ b/src/client/impl/logic/AnchorPoint.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/AnchorPoint.h" -#include "impl/AnchorPointImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "impl/logic/AnchorPointImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" namespace ramses { @@ -20,13 +20,23 @@ namespace ramses { } - const ramses::Node& AnchorPoint::getRamsesNode() const + const Node& AnchorPoint::getRamsesNode() const { - return m_anchorPointImpl.getRamsesNodeBinding().getRamsesNode(); + return m_anchorPointImpl.getNodeBinding().getRamsesNode(); } - const ramses::Camera& AnchorPoint::getRamsesCamera() const + const Camera& AnchorPoint::getRamsesCamera() const { - return m_anchorPointImpl.getRamsesCameraBinding().getRamsesCamera(); + return m_anchorPointImpl.getCameraBinding().getRamsesCamera(); + } + + internal::AnchorPointImpl& AnchorPoint::impl() + { + return m_anchorPointImpl; + } + + const internal::AnchorPointImpl& AnchorPoint::impl() const + { + return m_anchorPointImpl; } } diff --git a/client/logic/lib/impl/AnchorPointImpl.cpp b/src/client/impl/logic/AnchorPointImpl.cpp similarity index 67% rename from client/logic/lib/impl/AnchorPointImpl.cpp rename to src/client/impl/logic/AnchorPointImpl.cpp index 83fb8a1df..c56de6d01 100644 --- a/client/logic/lib/impl/AnchorPointImpl.cpp +++ b/src/client/impl/logic/AnchorPointImpl.cpp @@ -6,26 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/AnchorPointImpl.h" +#include "impl/logic/AnchorPointImpl.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Camera.h" +#include "ramses/client/Node.h" +#include "ramses/client/Camera.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" -#include "internals/ErrorReporting.h" +#include "impl/ErrorReporting.h" -#include "generated/AnchorPointGen.h" +#include "internal/logic/flatbuffers/generated/AnchorPointGen.h" #include "glm/gtc/type_ptr.hpp" namespace ramses::internal { - AnchorPointImpl::AnchorPointImpl(RamsesNodeBindingImpl& nodeBinding, RamsesCameraBindingImpl& cameraBinding, std::string_view name, uint64_t id) - : LogicNodeImpl{ name, id } + AnchorPointImpl::AnchorPointImpl(SceneImpl& scene, NodeBindingImpl& nodeBinding, CameraBindingImpl& cameraBinding, std::string_view name, sceneObjectId_t id) + : LogicNodeImpl{ scene, name, id } , m_nodeBinding{ nodeBinding } , m_cameraBinding{ cameraBinding } { @@ -48,11 +48,11 @@ namespace ramses::internal SerializationMap& serializationMap) { const auto fbLogicObject = LogicObjectImpl::Serialize(anchorPoint, builder); - const auto fbOutputs = PropertyImpl::Serialize(*anchorPoint.getOutputs()->m_impl, builder, serializationMap); + const auto fbOutputs = PropertyImpl::Serialize(anchorPoint.getOutputs()->impl(), builder, serializationMap); auto fbAnchorPoint = rlogic_serialization::CreateAnchorPoint(builder, fbLogicObject, - anchorPoint.m_nodeBinding.getId(), - anchorPoint.m_cameraBinding.getId(), + anchorPoint.m_nodeBinding.getSceneObjectId().getValue(), + anchorPoint.m_cameraBinding.getSceneObjectId().getValue(), 0, // no inputs fbOutputs); @@ -67,18 +67,18 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(anchorPoint.base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of AnchorPoint from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AnchorPoint from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!anchorPoint.rootOutput()) { - errorReporting.add("Fatal error during loading of AnchorPoint from serialized data: missing root output!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AnchorPoint from serialized data: missing root output!", nullptr); return nullptr; } @@ -88,7 +88,7 @@ namespace ramses::internal if (deserializedRootOutput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of AnchorPoint from serialized data: root output has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AnchorPoint from serialized data: root output has unexpected type!", nullptr); return nullptr; } @@ -96,19 +96,19 @@ namespace ramses::internal !deserializedRootOutput->getChild("viewportCoords") || deserializedRootOutput->getChild("viewportCoords")->getType() != EPropertyType::Vec2f || !deserializedRootOutput->getChild("depth") || deserializedRootOutput->getChild("depth")->getType() != EPropertyType::Float) { - errorReporting.add("Fatal error during loading of AnchorPoint: missing or invalid properties!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AnchorPoint: missing or invalid properties!", nullptr); return nullptr; } - auto* nodeBinding = deserializationMap.resolveLogicObject(anchorPoint.nodeBindingId()); - auto* cameraBinding = deserializationMap.resolveLogicObject(anchorPoint.cameraBindingId()); + auto* nodeBinding = deserializationMap.resolveLogicObject(sceneObjectId_t{ anchorPoint.nodeBindingId() }); + auto* cameraBinding = deserializationMap.resolveLogicObject(sceneObjectId_t{ anchorPoint.cameraBindingId() }); if (!nodeBinding || !cameraBinding) { - errorReporting.add("Fatal error during loading of AnchorPoint: could not resolve NodeBinding and/or CameraBinding!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AnchorPoint: could not resolve NodeBinding and/or CameraBinding!", nullptr); return nullptr; } - auto binding = std::make_unique(*nodeBinding, *cameraBinding, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *nodeBinding, *cameraBinding, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootProperties({}, std::move(deserializedRootOutput)); @@ -122,13 +122,13 @@ namespace ramses::internal matrix44f modelMatrix; const auto& ramsesCam = m_cameraBinding.getRamsesCamera(); - if (ramsesCam.getProjectionMatrix(projectionMatrix) != ramses::StatusOK) + if (!ramsesCam.getProjectionMatrix(projectionMatrix)) return LogicNodeRuntimeError{"Failed to retrieve projection matrix from Ramses camera!"}; - if (ramsesCam.getInverseModelMatrix(cameraViewMatrix) != ramses::StatusOK) + if (!ramsesCam.getInverseModelMatrix(cameraViewMatrix)) return LogicNodeRuntimeError{ "Failed to retrieve view matrix from Ramses camera!" }; - if (m_nodeBinding.getRamsesNode().getModelMatrix(modelMatrix) != ramses::StatusOK) + if (!m_nodeBinding.getRamsesNode().getModelMatrix(modelMatrix)) return LogicNodeRuntimeError{ "Failed to retrieve model matrix from Ramses node!" }; const vec4f localOrigin{ 0, 0, 0, 1 }; @@ -137,18 +137,18 @@ namespace ramses::internal const vec4f pointNormalized = (pointInNDS + 1.f) / 2.f; const vec4f pointViewport = pointNormalized * vec4f{ float(ramsesCam.getViewportWidth()), float(ramsesCam.getViewportHeight()), 1.f, 1.f }; - getOutputs()->getChild(0u)->m_impl->setValue(vec2f{ pointViewport.x, pointViewport.y }); // NOLINT(cppcoreguidelines-pro-type-union-access) - getOutputs()->getChild(1u)->m_impl->setValue(pointViewport.z); // NOLINT(cppcoreguidelines-pro-type-union-access) + getOutputs()->getChild(0u)->impl().setValue(vec2f{ pointViewport.x, pointViewport.y }); // NOLINT(cppcoreguidelines-pro-type-union-access) + getOutputs()->getChild(1u)->impl().setValue(pointViewport.z); // NOLINT(cppcoreguidelines-pro-type-union-access) return std::nullopt; } - RamsesNodeBindingImpl& AnchorPointImpl::getRamsesNodeBinding() + NodeBindingImpl& AnchorPointImpl::getNodeBinding() { return m_nodeBinding; } - RamsesCameraBindingImpl& AnchorPointImpl::getRamsesCameraBinding() + CameraBindingImpl& AnchorPointImpl::getCameraBinding() { return m_cameraBinding; } diff --git a/client/logic/lib/impl/AnchorPointImpl.h b/src/client/impl/logic/AnchorPointImpl.h similarity index 77% rename from client/logic/lib/impl/AnchorPointImpl.h rename to src/client/impl/logic/AnchorPointImpl.h index 0fc0057f0..9afec550d 100644 --- a/client/logic/lib/impl/AnchorPointImpl.h +++ b/src/client/impl/logic/AnchorPointImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "impl/LogicNodeImpl.h" +#include "impl/logic/LogicNodeImpl.h" #include namespace rlogic_serialization @@ -24,8 +24,8 @@ namespace flatbuffers namespace ramses::internal { - class RamsesNodeBindingImpl; - class RamsesCameraBindingImpl; + class NodeBindingImpl; + class CameraBindingImpl; class ErrorReporting; class SerializationMap; class DeserializationMap; @@ -34,7 +34,7 @@ namespace ramses::internal { public: // Move-able (noexcept); Not copy-able - explicit AnchorPointImpl(RamsesNodeBindingImpl& nodeBinding, RamsesCameraBindingImpl& cameraBinding, std::string_view name, uint64_t id); + explicit AnchorPointImpl(SceneImpl& scene, NodeBindingImpl& nodeBinding, CameraBindingImpl& cameraBinding, std::string_view name, sceneObjectId_t id); ~AnchorPointImpl() noexcept override = default; AnchorPointImpl(const AnchorPointImpl& other) = delete; AnchorPointImpl& operator=(const AnchorPointImpl& other) = delete; @@ -49,15 +49,15 @@ namespace ramses::internal ErrorReporting& errorReporting, DeserializationMap& deserializationMap); - [[nodiscard]] RamsesNodeBindingImpl& getRamsesNodeBinding(); - [[nodiscard]] RamsesCameraBindingImpl& getRamsesCameraBinding(); + [[nodiscard]] NodeBindingImpl& getNodeBinding(); + [[nodiscard]] CameraBindingImpl& getCameraBinding(); std::optional update() override; void createRootProperties() final; private: - RamsesNodeBindingImpl& m_nodeBinding; - RamsesCameraBindingImpl& m_cameraBinding; + NodeBindingImpl& m_nodeBinding; + CameraBindingImpl& m_cameraBinding; }; } diff --git a/client/logic/lib/impl/AnimationNode.cpp b/src/client/impl/logic/AnimationNode.cpp similarity index 75% rename from client/logic/lib/impl/AnimationNode.cpp rename to src/client/impl/logic/AnimationNode.cpp index a693416ba..0c3332785 100644 --- a/client/logic/lib/impl/AnimationNode.cpp +++ b/src/client/impl/logic/AnimationNode.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/AnimationNode.h" -#include "impl/AnimationNodeImpl.h" +#include "ramses/client/logic/AnimationNode.h" +#include "impl/logic/AnimationNodeImpl.h" namespace ramses { @@ -22,4 +22,14 @@ namespace ramses { return m_animationNodeImpl.getChannels(); } + + internal::AnimationNodeImpl& AnimationNode::impl() + { + return m_animationNodeImpl; + } + + const internal::AnimationNodeImpl& AnimationNode::impl() const + { + return m_animationNodeImpl; + } } diff --git a/client/logic/lib/impl/AnimationNodeConfig.cpp b/src/client/impl/logic/AnimationNodeConfig.cpp similarity index 84% rename from client/logic/lib/impl/AnimationNodeConfig.cpp rename to src/client/impl/logic/AnimationNodeConfig.cpp index 8111fda5f..63078a280 100644 --- a/client/logic/lib/impl/AnimationNodeConfig.cpp +++ b/src/client/impl/logic/AnimationNodeConfig.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/AnimationNodeConfig.h" -#include "impl/AnimationNodeConfigImpl.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "impl/logic/AnimationNodeConfigImpl.h" namespace ramses { @@ -51,4 +51,14 @@ namespace ramses { return m_impl->getExposingOfChannelDataAsProperties(); } + + internal::AnimationNodeConfigImpl& AnimationNodeConfig::impl() + { + return *m_impl; + } + + const internal::AnimationNodeConfigImpl& AnimationNodeConfig::impl() const + { + return *m_impl; + } } diff --git a/client/logic/lib/impl/AnimationNodeConfigImpl.cpp b/src/client/impl/logic/AnimationNodeConfigImpl.cpp similarity index 63% rename from client/logic/lib/impl/AnimationNodeConfigImpl.cpp rename to src/client/impl/logic/AnimationNodeConfigImpl.cpp index fc4ac6969..b3ee6fec1 100644 --- a/client/logic/lib/impl/AnimationNodeConfigImpl.cpp +++ b/src/client/impl/logic/AnimationNodeConfigImpl.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/AnimationNodeConfigImpl.h" +#include "impl/logic/AnimationNodeConfigImpl.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/DataArray.h" -#include "impl/LoggerImpl.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/DataArray.h" +#include "internal/Core/Utils/LogMacros.h" namespace ramses::internal { @@ -18,32 +18,32 @@ namespace ramses::internal { if (!channelData.timeStamps || !channelData.keyframes) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', missing timestamps and/or keyframes.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', missing timestamps and/or keyframes.", channelData.name); return false; } if (!CanPropertyTypeBeAnimated(channelData.keyframes->getDataType())) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', keyframes data type cannot be animated.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', keyframes data type cannot be animated.", channelData.name); return false; } if (channelData.timeStamps->getDataType() != EPropertyType::Float) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', timestamps must be of type Float.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', timestamps must be of type Float.", channelData.name); return false; } if (channelData.timeStamps->getNumElements() != channelData.keyframes->getNumElements()) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', number of keyframes must be same as number of timestamps.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', number of keyframes must be same as number of timestamps.", channelData.name); return false; } const auto& timestamps = *channelData.timeStamps->getData(); if (std::adjacent_find(timestamps.cbegin(), timestamps.cend(), std::greater_equal()) != timestamps.cend()) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', timestamps have to be strictly in ascending order.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', timestamps have to be strictly in ascending order.", channelData.name); return false; } @@ -52,7 +52,7 @@ namespace ramses::internal const size_t elementArraySize = channelData.keyframes->getData>()->front().size(); if (elementArraySize > MaxArrayPropertySize) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}'," + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}'," " when using elements of data type float array, the float array size ({}) cannot exceed {}.", channelData.name, elementArraySize, MaxArrayPropertySize); return false; @@ -62,7 +62,7 @@ namespace ramses::internal if ((channelData.interpolationType == EInterpolationType::Linear_Quaternions || channelData.interpolationType == EInterpolationType::Cubic_Quaternions) && channelData.keyframes->getDataType() != EPropertyType::Vec4f) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', quaternion animation requires the keyframes to be of type vec4f.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', quaternion animation requires the keyframes to be of type vec4f.", channelData.name); return false; } @@ -70,19 +70,19 @@ namespace ramses::internal { if (!channelData.tangentsIn || !channelData.tangentsOut) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', cubic interpolation requires tangents to be provided.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', cubic interpolation requires tangents to be provided.", channelData.name); return false; } if (channelData.tangentsIn->getDataType() != channelData.keyframes->getDataType() || channelData.tangentsOut->getDataType() != channelData.keyframes->getDataType()) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', tangents must be of same data type as keyframes.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', tangents must be of same data type as keyframes.", channelData.name); return false; } if (channelData.tangentsIn->getNumElements() != channelData.keyframes->getNumElements() || channelData.tangentsOut->getNumElements() != channelData.keyframes->getNumElements()) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', number of tangents in/out must be same as number of keyframes.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', number of tangents in/out must be same as number of keyframes.", channelData.name); return false; } if (channelData.keyframes->getDataType() == EPropertyType::Array) @@ -91,14 +91,14 @@ namespace ramses::internal if (channelData.tangentsIn->getData>()->front().size() != elementArraySize || channelData.tangentsOut->getData>()->front().size() != elementArraySize) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', tangents must have same array element size as keyframes.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', tangents must have same array element size as keyframes.", channelData.name); return false; } } } else if (channelData.tangentsIn || channelData.tangentsOut) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', tangents were provided for other than cubic interpolation type.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', tangents were provided for other than cubic interpolation type.", channelData.name); return false; } @@ -106,14 +106,14 @@ namespace ramses::internal { if (channelData.keyframes->getNumElements() > MaxArrayPropertySize) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', number of keyframes ({}) cannot exceed {} when animation data exposed as properties.", + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', number of keyframes ({}) cannot exceed {} when animation data exposed as properties.", channelData.name, channelData.keyframes->getNumElements(), MaxArrayPropertySize); return false; } if (channelData.keyframes->getDataType() == EPropertyType::Array) { - LOG_ERROR("AnimationNodeConfig::addChannel: Cannot add channelData data '{}', elements of data type float arrays cannot be exposed as properties.", channelData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::addChannel: Cannot add channelData data '{}', elements of data type float arrays cannot be exposed as properties.", channelData.name); return false; } } @@ -139,7 +139,7 @@ namespace ramses::internal { if (channelData.keyframes->getNumElements() > MaxArrayPropertySize) { - LOG_ERROR("AnimationNodeConfig::setExposingOfChannelDataAsProperties: Cannot enable channel data properties for channel '{}'," + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::setExposingOfChannelDataAsProperties: Cannot enable channel data properties for channel '{}'," " number of keyframes ({}) cannot exceed {} when animation data exposed as properties.", channelData.name, channelData.keyframes->getNumElements(), MaxArrayPropertySize); return false; @@ -147,7 +147,7 @@ namespace ramses::internal if (channelData.keyframes->getDataType() == EPropertyType::Array) { - LOG_ERROR("AnimationNodeConfig::setExposingOfChannelDataAsProperties: Cannot enable channel data properties for channel '{}'," + LOG_ERROR_P(CONTEXT_CLIENT, "AnimationNodeConfig::setExposingOfChannelDataAsProperties: Cannot enable channel data properties for channel '{}'," " elements of data type float arrays cannot be exposed as properties.", channelData.name); return false; } diff --git a/client/logic/lib/impl/AnimationNodeConfigImpl.h b/src/client/impl/logic/AnimationNodeConfigImpl.h similarity index 95% rename from client/logic/lib/impl/AnimationNodeConfigImpl.h rename to src/client/impl/logic/AnimationNodeConfigImpl.h index 7cd1cb13a..f51527b94 100644 --- a/client/logic/lib/impl/AnimationNodeConfigImpl.h +++ b/src/client/impl/logic/AnimationNodeConfigImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/AnimationTypes.h" +#include "ramses/client/logic/AnimationTypes.h" namespace ramses::internal { diff --git a/client/logic/lib/impl/AnimationNodeImpl.cpp b/src/client/impl/logic/AnimationNodeImpl.cpp similarity index 92% rename from client/logic/lib/impl/AnimationNodeImpl.cpp rename to src/client/impl/logic/AnimationNodeImpl.cpp index e9158b61b..4f455d270 100644 --- a/client/logic/lib/impl/AnimationNodeImpl.cpp +++ b/src/client/impl/logic/AnimationNodeImpl.cpp @@ -6,23 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/AnimationNodeImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/DataArrayImpl.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/DataArray.h" -#include "internals/EPropertySemantics.h" -#include "internals/ErrorReporting.h" -#include "generated/AnimationNodeGen.h" +#include "impl/logic/AnimationNodeImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/ErrorReporting.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/DataArray.h" +#include "internal/logic/EPropertySemantics.h" +#include "internal/logic/flatbuffers/generated/AnimationNodeGen.h" #include "fmt/format.h" #include "glm/gtx/range.hpp" #include namespace ramses::internal { - AnimationNodeImpl::AnimationNodeImpl(AnimationChannels channels, bool exposeDataAsProperties, std::string_view name, uint64_t id) noexcept - : LogicNodeImpl(name, id) + AnimationNodeImpl::AnimationNodeImpl(SceneImpl& scene, AnimationChannels channels, bool exposeDataAsProperties, std::string_view name, sceneObjectId_t id) noexcept + : LogicNodeImpl(scene, name, id) , m_channels{ std::move(channels) } , m_hasChannelDataExposedViaProperties{ exposeDataAsProperties } { @@ -40,7 +40,7 @@ namespace ramses::internal // (for timestamps and keyframes at least), it also makes it possible to modify this data in runtime while keeping original data constant assert(m_channels[i].timeStamps->getDataType() == EPropertyType::Float && m_channels[i].timeStamps->getNumElements() > 0); m_channelsWorkData[i].timestamps = *m_channels[i].timeStamps->getData(); - m_channelsWorkData[i].keyframes = m_channels[i].keyframes->m_impl.getDataVariant(); + m_channelsWorkData[i].keyframes = m_channels[i].keyframes->impl().getDataVariant(); // overall duration equals longest channel in animation m_maxChannelDuration = std::max(m_maxChannelDuration, channel.timeStamps->getData()->back()); @@ -206,11 +206,11 @@ namespace ramses::internal { // array data type requires each array element to be set to individual output property for (size_t arrayIdx = 0u; arrayIdx < v.size(); ++arrayIdx) - outputValueProp->getChild(arrayIdx)->m_impl->setValue(v[arrayIdx]); + outputValueProp->getChild(arrayIdx)->impl().setValue(v[arrayIdx]); } else { - outputValueProp->m_impl->setValue(PropertyValue{ v }); + outputValueProp->impl().setValue(PropertyValue{ v }); } }, interpolatedValue); } @@ -325,17 +325,17 @@ namespace ramses::internal channelsFB.push_back(rlogic_serialization::CreateChannel( builder, builder.CreateString(channel.name), - serializationMap.resolveDataArrayOffset(channel.timeStamps->getId()), - serializationMap.resolveDataArrayOffset(channel.keyframes->getId()), + serializationMap.resolveDataArrayOffset(channel.timeStamps->getSceneObjectId()), + serializationMap.resolveDataArrayOffset(channel.keyframes->getSceneObjectId()), interpTypeFB, - channel.tangentsIn ? serializationMap.resolveDataArrayOffset(channel.tangentsIn->getId()) : 0, - channel.tangentsOut ? serializationMap.resolveDataArrayOffset(channel.tangentsOut->getId()) : 0 + channel.tangentsIn ? serializationMap.resolveDataArrayOffset(channel.tangentsIn->getSceneObjectId()) : 0, + channel.tangentsOut ? serializationMap.resolveDataArrayOffset(channel.tangentsOut->getSceneObjectId()) : 0 )); } const auto logicObject = LogicObjectImpl::Serialize(animNode, builder); - const auto inputPropertyObject = PropertyImpl::Serialize(*animNode.getInputs()->m_impl, builder, serializationMap); - const auto ouputPropertyObject = PropertyImpl::Serialize(*animNode.getOutputs()->m_impl, builder, serializationMap); + const auto inputPropertyObject = PropertyImpl::Serialize(animNode.getInputs()->impl(), builder, serializationMap); + const auto ouputPropertyObject = PropertyImpl::Serialize(animNode.getOutputs()->impl(), builder, serializationMap); return rlogic_serialization::CreateAnimationNode( builder, logicObject, @@ -352,12 +352,12 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(animNodeFB.base(), name, id, userIdHigh, userIdLow, errorReporting) || !animNodeFB.channels() || !animNodeFB.rootInput() || !animNodeFB.rootOutput()) { - errorReporting.add("Fatal error during loading of AnimationNode from serialized data: missing name, id, channels or in/out property data!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AnimationNode from serialized data: missing name, id, channels or in/out property data!", nullptr); return nullptr; } @@ -369,7 +369,7 @@ namespace ramses::internal !channelFB->timestamps() || !channelFB->keyframes()) { - errorReporting.add(fmt::format("Fatal error during loading of AnimationNode '{}' channel data: missing name, timestamps or keyframes!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of AnimationNode '{}' channel data: missing name, timestamps or keyframes!", name), nullptr); return nullptr; } @@ -396,7 +396,7 @@ namespace ramses::internal channel.interpolationType = EInterpolationType::Cubic_Quaternions; break; default: - errorReporting.add(fmt::format("Fatal error during loading of AnimationNode '{}' channel '{}' data: missing or invalid interpolation type!", name, channel.name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of AnimationNode '{}' channel '{}' data: missing or invalid interpolation type!", name, channel.name), nullptr); return nullptr; } @@ -405,7 +405,7 @@ namespace ramses::internal if (!channelFB->tangentsIn() || !channelFB->tangentsOut()) { - errorReporting.add(fmt::format("Fatal error during loading of AnimationNode '{}' channel '{}' data: missing tangents!", name, channel.name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of AnimationNode '{}' channel '{}' data: missing tangents!", name, channel.name), nullptr); return nullptr; } @@ -422,20 +422,20 @@ namespace ramses::internal auto rootInProperty = PropertyImpl::Deserialize(*animNodeFB.rootInput(), EPropertySemantics::AnimationInput, errorReporting, deserializationMap); auto rootOutProperty = PropertyImpl::Deserialize(*animNodeFB.rootOutput(), EPropertySemantics::AnimationOutput, errorReporting, deserializationMap); - auto deserialized = std::make_unique(std::move(channels), hasChannelDataProperties, name, id); + auto deserialized = std::make_unique(deserializationMap.getScene(), std::move(channels), hasChannelDataProperties, name, id); deserialized->setUserId(userIdHigh, userIdLow); if (!rootInProperty->getChild(EInputIdx_Progress) || rootInProperty->getChild(EInputIdx_Progress)->getName() != "progress" || rootOutProperty->getChildCount() != deserialized->getChannels().size() + EOutputIdx_ChannelsBegin || !rootOutProperty->getChild(EOutputIdx_Duration) || rootOutProperty->getChild(EOutputIdx_Duration)->getName() != "duration") { - errorReporting.add(fmt::format("Fatal error during loading of AnimationNode '{}': missing or invalid properties!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of AnimationNode '{}': missing or invalid properties!", name), nullptr); return nullptr; } if (hasChannelDataProperties && (!rootInProperty->getChild(EInputIdx_ChannelsData) || rootInProperty->getChild(EInputIdx_ChannelsData)->getName() != "channelsData")) { - errorReporting.add(fmt::format("Fatal error during loading of AnimationNode '{}': missing or invalid channels data property!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of AnimationNode '{}': missing or invalid channels data property!", name), nullptr); return nullptr; } @@ -464,7 +464,7 @@ namespace ramses::internal const auto& timestamps = channelData.timestamps; for (size_t i = 0u; i < timestamps.size(); ++i) - timestampsProp->getChild(i)->m_impl->setValue(timestamps[i]); + timestampsProp->getChild(i)->impl().setValue(timestamps[i]); std::visit([&](const auto& keyframes) { using ValueType = std::remove_const_t>; @@ -476,7 +476,7 @@ namespace ramses::internal else { for (size_t i = 0u; i < keyframes.size(); ++i) - keyframesProp->getChild(i)->m_impl->setValue(keyframes[i]); + keyframesProp->getChild(i)->impl().setValue(keyframes[i]); } }, channelData.keyframes); } diff --git a/client/logic/lib/impl/AnimationNodeImpl.h b/src/client/impl/logic/AnimationNodeImpl.h similarity index 91% rename from client/logic/lib/impl/AnimationNodeImpl.h rename to src/client/impl/logic/AnimationNodeImpl.h index f471eac51..51be78300 100644 --- a/client/logic/lib/impl/AnimationNodeImpl.h +++ b/src/client/impl/logic/AnimationNodeImpl.h @@ -8,10 +8,10 @@ #pragma once -#include "ramses-logic/AnimationTypes.h" +#include "ramses/client/logic/AnimationTypes.h" -#include "impl/LogicNodeImpl.h" -#include "impl/DataArrayImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/DataArrayImpl.h" #include namespace rlogic_serialization @@ -34,7 +34,7 @@ namespace ramses::internal class AnimationNodeImpl : public LogicNodeImpl { public: - AnimationNodeImpl(AnimationChannels channels, bool exposeDataAsProperties, std::string_view name, uint64_t id) noexcept; + AnimationNodeImpl(SceneImpl& scene, AnimationChannels channels, bool exposeDataAsProperties, std::string_view name, sceneObjectId_t id) noexcept; [[nodiscard]] float getMaximumChannelDuration() const; [[nodiscard]] const AnimationChannels& getChannels() const; diff --git a/client/logic/lib/impl/RamsesAppearanceBinding.cpp b/src/client/impl/logic/AppearanceBinding.cpp similarity index 53% rename from client/logic/lib/impl/RamsesAppearanceBinding.cpp rename to src/client/impl/logic/AppearanceBinding.cpp index f9722f419..f7dc9a451 100644 --- a/client/logic/lib/impl/RamsesAppearanceBinding.cpp +++ b/src/client/impl/logic/AppearanceBinding.cpp @@ -7,20 +7,30 @@ // ------------------------------------------------------------------------- -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "impl/RamsesAppearanceBindingImpl.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "impl/logic/AppearanceBindingImpl.h" namespace ramses { - RamsesAppearanceBinding::RamsesAppearanceBinding(std::unique_ptr impl) noexcept + AppearanceBinding::AppearanceBinding(std::unique_ptr impl) noexcept : RamsesBinding(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_appearanceBinding{ static_cast(RamsesBinding::m_impl) } + , m_appearanceBinding{ static_cast(RamsesBinding::m_impl) } { } - ramses::Appearance& RamsesAppearanceBinding::getRamsesAppearance() const + ramses::Appearance& AppearanceBinding::getRamsesAppearance() const { return m_appearanceBinding.getRamsesAppearance(); } + + internal::AppearanceBindingImpl& AppearanceBinding::impl() + { + return m_appearanceBinding; + } + + const internal::AppearanceBindingImpl& AppearanceBinding::impl() const + { + return m_appearanceBinding; + } } diff --git a/client/logic/lib/impl/RamsesAppearanceBindingImpl.cpp b/src/client/impl/logic/AppearanceBindingImpl.cpp similarity index 54% rename from client/logic/lib/impl/RamsesAppearanceBindingImpl.cpp rename to src/client/impl/logic/AppearanceBindingImpl.cpp index 10cf47cfd..5155ca3b3 100644 --- a/client/logic/lib/impl/RamsesAppearanceBindingImpl.cpp +++ b/src/client/impl/logic/AppearanceBindingImpl.cpp @@ -6,64 +6,51 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesAppearanceBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" -#include "impl/LoggerImpl.h" +#include "impl/logic/PropertyImpl.h" -#include "internals/RamsesHelper.h" -#include "internals/ErrorReporting.h" -#include "internals/TypeUtils.h" -#include "internals/RamsesObjectResolver.h" +#include "internal/logic/RamsesHelper.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/TypeUtils.h" +#include "internal/logic/RamsesObjectResolver.h" -#include "generated/RamsesAppearanceBindingGen.h" +#include "internal/logic/flatbuffers/generated/AppearanceBindingGen.h" namespace ramses::internal { - RamsesAppearanceBindingImpl::RamsesAppearanceBindingImpl(ramses::Appearance& ramsesAppearance, std::string_view name, uint64_t id) - : RamsesBindingImpl(name, id) + AppearanceBindingImpl::AppearanceBindingImpl(SceneImpl& scene, ramses::Appearance& ramsesAppearance, std::string_view name, sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_ramsesAppearance(ramsesAppearance) { const auto& effect = m_ramsesAppearance.get().getEffect(); const size_t uniformCount = effect.getUniformInputCount(); - m_uniformIndices.reserve(uniformCount); + m_uniforms.reserve(uniformCount); - // create mapping from property children indices to uniform inputs, this must match properties (either created or deserialized) + // store uniforms that will be exposed as properties, these must match properties (either created or deserialized) for (size_t i = 0; i < uniformCount; ++i) { - ramses::UniformInput uniformInput; - ramses::status_t result = effect.getUniformInput(i, uniformInput); - assert(result == ramses::StatusOK); - assert(uniformInput.isValid()); - (void)result; - - if (GetPropertyTypeForUniform(uniformInput)) - m_uniformIndices.push_back(i); + std::optional uniformInput = effect.getUniformInput(i); + assert(uniformInput.has_value()); + if (GetPropertyTypeForUniform(*uniformInput)) + m_uniforms.push_back(std::move(*uniformInput)); } } - void RamsesAppearanceBindingImpl::createRootProperties() + void AppearanceBindingImpl::createRootProperties() { - const auto& effect = m_ramsesAppearance.get().getEffect(); - const size_t uniformCount = effect.getUniformInputCount(); - std::vector bindingInputs; - bindingInputs.reserve(uniformCount); - - for (size_t i = 0; i < uniformCount; ++i) + bindingInputs.reserve(m_uniforms.size()); + for (const auto& uniformInput : m_uniforms) { - ramses::UniformInput uniformInput; - effect.getUniformInput(i, uniformInput); const std::optional convertedType = GetPropertyTypeForUniform(uniformInput); - - // TODO Violin handle all types eventually (need some more breaking ramses features for that) if (convertedType) { // Non-array case @@ -78,21 +65,22 @@ namespace ramses::internal } } } + assert(bindingInputs.size() == m_uniforms.size()); HierarchicalTypeData bindingInputsType(TypeData{ "", EPropertyType::Struct }, bindingInputs); setRootInputs(std::make_unique(bindingInputsType, EPropertySemantics::BindingInput)); } - flatbuffers::Offset RamsesAppearanceBindingImpl::Serialize( - const RamsesAppearanceBindingImpl& binding, + flatbuffers::Offset AppearanceBindingImpl::Serialize( + const AppearanceBindingImpl& binding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap) { auto ramsesReference = RamsesBindingImpl::SerializeRamsesReference(binding.m_ramsesAppearance, builder); const auto logicObject = LogicObjectImpl::Serialize(binding, builder); - const auto propertyObject = PropertyImpl::Serialize(*binding.getInputs()->m_impl, builder, serializationMap); + const auto propertyObject = PropertyImpl::Serialize(binding.getInputs()->impl(), builder, serializationMap); auto ramsesBinding = rlogic_serialization::CreateRamsesBinding(builder, logicObject, ramsesReference, @@ -102,40 +90,40 @@ namespace ramses::internal rlogic_serialization::ResourceId parentEffectResourceId; parentEffectResourceId = rlogic_serialization::ResourceId(binding.m_ramsesAppearance.get().getEffect().getResourceId().lowPart, binding.m_ramsesAppearance.get().getEffect().getResourceId().highPart); - auto ramsesAppearanceBinding = rlogic_serialization::CreateRamsesAppearanceBinding(builder, + auto appearanceBinding = rlogic_serialization::CreateAppearanceBinding(builder, ramsesBinding, &parentEffectResourceId ); - builder.Finish(ramsesAppearanceBinding); + builder.Finish(appearanceBinding); - return ramsesAppearanceBinding; + return appearanceBinding; } - std::unique_ptr RamsesAppearanceBindingImpl::Deserialize( - const rlogic_serialization::RamsesAppearanceBinding& appearanceBinding, + std::unique_ptr AppearanceBindingImpl::Deserialize( + const rlogic_serialization::AppearanceBinding& appearanceBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { if (!appearanceBinding.base()) { - errorReporting.add("Fatal error during loading of RamsesAppearanceBinding from serialized data: missing base class info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AppearanceBinding from serialized data: missing base class info!", nullptr); return nullptr; } std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(appearanceBinding.base()->base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of RamsesAppearanceBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AppearanceBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!appearanceBinding.base()->rootInput()) { - errorReporting.add("Fatal error during loading of RamsesAppearanceBinding from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AppearanceBinding from serialized data: missing root input!", nullptr); return nullptr; } @@ -148,14 +136,14 @@ namespace ramses::internal if (deserializedRootInput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of RamsesAppearanceBinding from serialized data: root input has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AppearanceBinding from serialized data: root input has unexpected type!", nullptr); return nullptr; } const auto* boundObject = appearanceBinding.base()->boundRamsesObject(); if (!boundObject) { - errorReporting.add("Fatal error during loading of RamsesAppearanceBinding from serialized data: no reference to appearance!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AppearanceBinding from serialized data: no reference to appearance!", nullptr); return nullptr; } @@ -171,18 +159,18 @@ namespace ramses::internal const ramses::resourceId_t effectResourceId = effect.getResourceId(); if (effectResourceId.lowPart != appearanceBinding.parentEffectId()->resourceIdLow() || effectResourceId.highPart != appearanceBinding.parentEffectId()->resourceIdHigh()) { - errorReporting.add("Fatal error during loading of RamsesAppearanceBinding from serialized data: effect signature doesn't match after loading!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of AppearanceBinding from serialized data: effect signature doesn't match after loading!", nullptr); return nullptr; } - auto binding = std::make_unique(*resolvedAppearance, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *resolvedAppearance, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs(std::move(deserializedRootInput)); return binding; } - std::optional RamsesAppearanceBindingImpl::update() + std::optional AppearanceBindingImpl::update() { const size_t childCount = getInputs()->getChildCount(); for (size_t i = 0; i < childCount; ++i) @@ -193,22 +181,21 @@ namespace ramses::internal return std::nullopt; } - void RamsesAppearanceBindingImpl::setInputValueToUniform(size_t inputIndex) + void AppearanceBindingImpl::setInputValueToUniform(size_t inputIndex) { - PropertyImpl& inputProperty = *getInputs()->getChild(inputIndex)->m_impl; + assert(inputIndex < m_uniforms.size()); + PropertyImpl& inputProperty = getInputs()->getChild(inputIndex)->impl(); const EPropertyType propertyType = inputProperty.getType(); if (TypeUtils::IsPrimitiveType(propertyType)) { if (inputProperty.checkForBindingInputNewValueAndReset()) { - ramses::UniformInput uniform; - m_ramsesAppearance.get().getEffect().getUniformInput(m_uniformIndices[inputIndex], uniform); std::visit([&](auto v) { - using RamsesValueType = typename RlogicTypeToRamsesType>>::TYPE; - if constexpr (ramses::IsUniformInputDataType()) + using ValueType = std::remove_const_t>; + if constexpr (ramses::IsUniformInputDataType()) { - m_ramsesAppearance.get().setInputValue(uniform, RamsesValueType{ std::move(v) }); + m_ramsesAppearance.get().setInputValue(m_uniforms[inputIndex], ValueType{ std::move(v) }); } else { @@ -227,45 +214,53 @@ namespace ramses::internal const size_t arraySize = inputProperty.getChildCount(); for (size_t i = 0; i < arraySize; ++i) { - if (inputProperty.getChild(i)->m_impl->checkForBindingInputNewValueAndReset()) + if (inputProperty.getChild(i)->impl().checkForBindingInputNewValueAndReset()) anyArrayElementWasSet = true; } if (anyArrayElementWasSet) { - ramses::UniformInput uniform; - m_ramsesAppearance.get().getEffect().getUniformInput(m_uniformIndices[inputIndex], uniform); - std::visit([&](const auto& v) { using ValueType = std::remove_const_t>; - using RamsesValueType = typename RlogicTypeToRamsesType>>::TYPE; - if constexpr (ramses::IsUniformInputDataType()) + if constexpr (ramses::IsUniformInputDataType()) { - std::vector values; - values.reserve(inputProperty.getChildCount()); - for (size_t i = 0u; i < inputProperty.getChildCount(); ++i) - values.push_back(RamsesValueType{ *inputProperty.getChild(i)->get() }); - m_ramsesAppearance.get().setInputValue(uniform, values.size(), values.data()); + if constexpr (std::is_same_v) // special handling for bool array, cannot use vector + { + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + auto values = std::make_unique(inputProperty.getChildCount()); + for (size_t i = 0u; i < inputProperty.getChildCount(); ++i) + values[i] = *inputProperty.getChild(i)->get(); + + m_ramsesAppearance.get().setInputValue(m_uniforms[inputIndex], inputProperty.getChildCount(), values.get()); + } + else + { + std::vector values; + values.reserve(inputProperty.getChildCount()); + for (size_t i = 0u; i < inputProperty.getChildCount(); ++i) + values.push_back(ValueType{ *inputProperty.getChild(i)->get() }); + + m_ramsesAppearance.get().setInputValue(m_uniforms[inputIndex], values.size(), values.data()); + } } else assert(false && "This should never happen"); - }, inputProperty.getChild(0u)->m_impl->getValue()); // small trick to determine the element type so that the flattened array can be declared in templated code + }, inputProperty.getChild(0u)->impl().getValue()); // small trick to determine the element type so that the flattened array can be declared in templated code } } } - ramses::Appearance& RamsesAppearanceBindingImpl::getRamsesAppearance() const + ramses::Appearance& AppearanceBindingImpl::getRamsesAppearance() const { return m_ramsesAppearance; } - std::optional RamsesAppearanceBindingImpl::GetPropertyTypeForUniform(const ramses::UniformInput& uniform) + std::optional AppearanceBindingImpl::GetPropertyTypeForUniform(const ramses::UniformInput& uniform) { - assert(uniform.isValid()); // Can't bind semantic uniforms if (uniform.getSemantics() != ramses::EEffectUniformSemantic::Invalid) return std::nullopt; - return ConvertRamsesUniformTypeToPropertyType(*uniform.getDataType()); + return ConvertRamsesUniformTypeToPropertyType(uniform.getDataType()); } } diff --git a/client/logic/lib/impl/RamsesAppearanceBindingImpl.h b/src/client/impl/logic/AppearanceBindingImpl.h similarity index 68% rename from client/logic/lib/impl/RamsesAppearanceBindingImpl.h rename to src/client/impl/logic/AppearanceBindingImpl.h index 64d36dfed..dfca3a80d 100644 --- a/client/logic/lib/impl/RamsesAppearanceBindingImpl.h +++ b/src/client/impl/logic/AppearanceBindingImpl.h @@ -8,12 +8,12 @@ #pragma once -#include "impl/RamsesBindingImpl.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" +#include "impl/logic/RamsesBindingImpl.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-client-api/UniformInput.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/UniformInput.h" #include #include @@ -27,7 +27,7 @@ namespace ramses namespace rlogic_serialization { - struct RamsesAppearanceBinding; + struct AppearanceBinding; } namespace flatbuffers @@ -42,18 +42,18 @@ namespace ramses::internal class ErrorReporting; class IRamsesObjectResolver; - class RamsesAppearanceBindingImpl : public RamsesBindingImpl + class AppearanceBindingImpl : public RamsesBindingImpl { public: - explicit RamsesAppearanceBindingImpl(ramses::Appearance& ramsesAppearance, std::string_view name, uint64_t id); + explicit AppearanceBindingImpl(SceneImpl& scene, ramses::Appearance& ramsesAppearance, std::string_view name, sceneObjectId_t id); - [[nodiscard]] static flatbuffers::Offset Serialize( - const RamsesAppearanceBindingImpl& binding, + [[nodiscard]] static flatbuffers::Offset Serialize( + const AppearanceBindingImpl& binding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap); - [[nodiscard]] static std::unique_ptr Deserialize( - const rlogic_serialization::RamsesAppearanceBinding& appearanceBinding, + [[nodiscard]] static std::unique_ptr Deserialize( + const rlogic_serialization::AppearanceBinding& appearanceBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap); @@ -66,7 +66,7 @@ namespace ramses::internal private: std::reference_wrapper m_ramsesAppearance; - std::vector m_uniformIndices; + std::vector m_uniforms; void setInputValueToUniform(size_t inputIndex); diff --git a/client/logic/lib/impl/RamsesCameraBinding.cpp b/src/client/impl/logic/CameraBinding.cpp similarity index 55% rename from client/logic/lib/impl/RamsesCameraBinding.cpp rename to src/client/impl/logic/CameraBinding.cpp index ed0bf8535..5604520f9 100644 --- a/client/logic/lib/impl/RamsesCameraBinding.cpp +++ b/src/client/impl/logic/CameraBinding.cpp @@ -6,20 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/RamsesCameraBinding.h" -#include "impl/RamsesCameraBindingImpl.h" +#include "ramses/client/logic/CameraBinding.h" +#include "impl/logic/CameraBindingImpl.h" namespace ramses { - RamsesCameraBinding::RamsesCameraBinding(std::unique_ptr impl) noexcept + CameraBinding::CameraBinding(std::unique_ptr impl) noexcept : RamsesBinding(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_cameraBinding{ static_cast(RamsesBinding::m_impl) } + , m_cameraBinding{ static_cast(RamsesBinding::m_impl) } { } - ramses::Camera& RamsesCameraBinding::getRamsesCamera() const + ramses::Camera& CameraBinding::getRamsesCamera() const { return m_cameraBinding.getRamsesCamera(); } + + internal::CameraBindingImpl& CameraBinding::impl() + { + return m_cameraBinding; + } + + const internal::CameraBindingImpl& CameraBinding::impl() const + { + return m_cameraBinding; + } } diff --git a/client/logic/lib/impl/RamsesCameraBindingImpl.cpp b/src/client/impl/logic/CameraBindingImpl.cpp similarity index 52% rename from client/logic/lib/impl/RamsesCameraBindingImpl.cpp rename to src/client/impl/logic/CameraBindingImpl.cpp index a1b9221e5..f399e2e81 100644 --- a/client/logic/lib/impl/RamsesCameraBindingImpl.cpp +++ b/src/client/impl/logic/CameraBindingImpl.cpp @@ -6,34 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesCameraBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" -#include "ramses-utils.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/PerspectiveCamera.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Camera.h" +#include "ramses/client/PerspectiveCamera.h" -#include "ramses-logic/EPropertyType.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" -#include "impl/LoggerImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "fmt/format.h" -#include "internals/RamsesHelper.h" -#include "internals/ErrorReporting.h" -#include "internals/RamsesObjectResolver.h" +#include "internal/logic/RamsesHelper.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesObjectResolver.h" -#include "generated/RamsesCameraBindingGen.h" +#include "internal/logic/flatbuffers/generated/CameraBindingGen.h" namespace ramses::internal { - RamsesCameraBindingImpl::RamsesCameraBindingImpl(ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name, uint64_t id) - : RamsesBindingImpl(name, id) + CameraBindingImpl::CameraBindingImpl(SceneImpl& scene, ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name, sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_ramsesCamera(ramsesCamera) , m_hasFrustumPlanesProperties{ ramsesCamera.isOfType(ramses::ERamsesObjectType::OrthographicCamera) || withFrustumPlanes } { } - void RamsesCameraBindingImpl::createRootProperties() + void CameraBindingImpl::createRootProperties() { std::vector frustumPlanes = { TypeData{ "nearPlane", EPropertyType::Float }, @@ -76,52 +76,52 @@ namespace ramses::internal ApplyRamsesValuesToInputProperties(*this); } - flatbuffers::Offset RamsesCameraBindingImpl::Serialize( - const RamsesCameraBindingImpl& cameraBinding, + flatbuffers::Offset CameraBindingImpl::Serialize( + const CameraBindingImpl& binding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap) { - auto ramsesReference = RamsesBindingImpl::SerializeRamsesReference(cameraBinding.m_ramsesCamera, builder); + auto ramsesReference = RamsesBindingImpl::SerializeRamsesReference(binding.m_ramsesCamera, builder); - const auto logicObject = LogicObjectImpl::Serialize(cameraBinding, builder); - const auto propertyObject = PropertyImpl::Serialize(*cameraBinding.getInputs()->m_impl, builder, serializationMap); + const auto logicObject = LogicObjectImpl::Serialize(binding, builder); + const auto propertyObject = PropertyImpl::Serialize(binding.getInputs()->impl(), builder, serializationMap); auto ramsesBinding = rlogic_serialization::CreateRamsesBinding(builder, logicObject, ramsesReference, propertyObject); builder.Finish(ramsesBinding); - auto ramsesCameraBinding = rlogic_serialization::CreateRamsesCameraBinding(builder, ramsesBinding); - builder.Finish(ramsesCameraBinding); + auto cameraBinding = rlogic_serialization::CreateCameraBinding(builder, ramsesBinding); + builder.Finish(cameraBinding); - return ramsesCameraBinding; + return cameraBinding; } - std::unique_ptr RamsesCameraBindingImpl::Deserialize( - const rlogic_serialization::RamsesCameraBinding& cameraBinding, + std::unique_ptr CameraBindingImpl::Deserialize( + const rlogic_serialization::CameraBinding& cameraBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { if (!cameraBinding.base()) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: missing base class info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: missing base class info!", nullptr); return nullptr; } std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(cameraBinding.base()->base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!cameraBinding.base()->rootInput()) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: missing root input!", nullptr); return nullptr; } @@ -131,14 +131,14 @@ namespace ramses::internal if (deserializedRootInput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: root input has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: root input has unexpected type!", nullptr); return nullptr; } const auto frustumInputProp = deserializedRootInput->getChild("frustum"); if (!frustumInputProp || !(frustumInputProp->getChildCount() == 4u || frustumInputProp->getChildCount() == 6u)) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: missing or invalid input properties!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: missing or invalid input properties!", nullptr); return nullptr; } const bool hasFrustumPlanesProperties = (frustumInputProp->getChildCount() == 6u); @@ -146,7 +146,7 @@ namespace ramses::internal const auto* boundObject = cameraBinding.base()->boundRamsesObject(); if (!boundObject) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: no reference to ramses camera!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: no reference to ramses camera!", nullptr); return nullptr; } @@ -158,11 +158,11 @@ namespace ramses::internal if (static_cast(resolvedCamera->getType()) != boundObject->objectType()) { - errorReporting.add("Fatal error during loading of RamsesCameraBinding from serialized data: loaded type does not match referenced camera type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of CameraBinding from serialized data: loaded type does not match referenced camera type!", nullptr); return nullptr; } - auto binding = std::make_unique(*resolvedCamera, hasFrustumPlanesProperties, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *resolvedCamera, hasFrustumPlanesProperties, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs(std::move(deserializedRootInput)); @@ -171,15 +171,13 @@ namespace ramses::internal return binding; } - std::optional RamsesCameraBindingImpl::update() + std::optional CameraBindingImpl::update() { - ramses::status_t status = ramses::StatusOK; - PropertyImpl& vpProperties = *getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))->m_impl; - - PropertyImpl& vpOffsetX = *vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))->m_impl; - PropertyImpl& vpOffsetY = *vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))->m_impl; - PropertyImpl& vpWidth = *vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))->m_impl; - PropertyImpl& vpHeight = *vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))->m_impl; + PropertyImpl& vpProperties = getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))->impl(); + PropertyImpl& vpOffsetX = vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))->impl(); + PropertyImpl& vpOffsetY = vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))->impl(); + PropertyImpl& vpWidth = vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))->impl(); + PropertyImpl& vpHeight = vpProperties.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))->impl(); if (vpOffsetX.checkForBindingInputNewValueAndReset() || vpOffsetY.checkForBindingInputNewValueAndReset() || vpWidth.checkForBindingInputNewValueAndReset() @@ -195,26 +193,22 @@ namespace ramses::internal return LogicNodeRuntimeError{ fmt::format("Camera viewport size must be positive! (width: {}; height: {})", vpW, vpH) }; } - status = m_ramsesCamera.get().setViewport(vpX, vpY, vpW, vpH); - - if (status != ramses::StatusOK) - { - return LogicNodeRuntimeError{m_ramsesCamera.get().getStatusMessage(status)}; - } + if (!m_ramsesCamera.get().setViewport(vpX, vpY, vpW, vpH)) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - PropertyImpl& frustum = *getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))->m_impl; + PropertyImpl& frustum = getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))->impl(); // Index of Perspective Frustum Properties is used, but wouldn't matter as Ortho Camera indeces are the same for these two properties - PropertyImpl& nearPlane = *frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::NearPlane))->m_impl; - PropertyImpl& farPlane = *frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FarPlane))->m_impl; + PropertyImpl& nearPlane = frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::NearPlane))->impl(); + PropertyImpl& farPlane = frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FarPlane))->impl(); if (m_hasFrustumPlanesProperties) { - PropertyImpl& leftPlane = *frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::LeftPlane))->m_impl; - PropertyImpl& rightPlane = *frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::RightPlane))->m_impl; - PropertyImpl& bottomPlane = *frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::BottomPlane))->m_impl; - PropertyImpl& topPlane = *frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::TopPlane))->m_impl; + PropertyImpl& leftPlane = frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::LeftPlane))->impl(); + PropertyImpl& rightPlane = frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::RightPlane))->impl(); + PropertyImpl& bottomPlane = frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::BottomPlane))->impl(); + PropertyImpl& topPlane = frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::TopPlane))->impl(); if (nearPlane.checkForBindingInputNewValueAndReset() || farPlane.checkForBindingInputNewValueAndReset() @@ -223,22 +217,22 @@ namespace ramses::internal || bottomPlane.checkForBindingInputNewValueAndReset() || topPlane.checkForBindingInputNewValueAndReset()) { - status = m_ramsesCamera.get().setFrustum( + if (!m_ramsesCamera.get().setFrustum( leftPlane.getValueAs(), rightPlane.getValueAs(), bottomPlane.getValueAs(), topPlane.getValueAs(), nearPlane.getValueAs(), - farPlane.getValueAs()); - - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesCamera.get().getStatusMessage(status) }; + farPlane.getValueAs())) + { + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; + } } } else { - PropertyImpl& fov = *frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))->m_impl; - PropertyImpl& aR = *frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))->m_impl; + PropertyImpl& fov = frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))->impl(); + PropertyImpl& aR = frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))->impl(); if (nearPlane.checkForBindingInputNewValueAndReset() || farPlane.checkForBindingInputNewValueAndReset() @@ -246,54 +240,52 @@ namespace ramses::internal || aR.checkForBindingInputNewValueAndReset()) { assert(m_ramsesCamera.get().isOfType(ramses::ERamsesObjectType::PerspectiveCamera)); - auto* perspectiveCam = ramses::RamsesUtils::TryConvert(m_ramsesCamera.get()); - status = perspectiveCam->setFrustum(fov.getValueAs(), aR.getValueAs(), nearPlane.getValueAs(), farPlane.getValueAs()); - - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesCamera.get().getStatusMessage(status) }; + auto* perspectiveCam = m_ramsesCamera.get().as(); + if (!perspectiveCam->setFrustum(fov.getValueAs(), aR.getValueAs(), nearPlane.getValueAs(), farPlane.getValueAs())) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } } return std::nullopt; } - bool RamsesCameraBindingImpl::hasFrustumPlanesProperties() const + bool CameraBindingImpl::hasFrustumPlanesProperties() const { return m_hasFrustumPlanesProperties; } - ramses::Camera& RamsesCameraBindingImpl::getRamsesCamera() const + ramses::Camera& CameraBindingImpl::getRamsesCamera() const { return m_ramsesCamera; } - void RamsesCameraBindingImpl::ApplyRamsesValuesToInputProperties(RamsesCameraBindingImpl& binding) + void CameraBindingImpl::ApplyRamsesValuesToInputProperties(CameraBindingImpl& binding) { const auto& ramsesCamera = binding.getRamsesCamera(); // Initializes input values with values from ramses camera silently (no dirty mechanism triggered) - PropertyImpl& viewport = *binding.getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))->m_impl; - viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getViewportX() }); - viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getViewportY() }); - viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))->m_impl->initializeBindingInputValue(PropertyValue{ static_cast(ramsesCamera.getViewportWidth()) }); - viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))->m_impl->initializeBindingInputValue(PropertyValue{ static_cast(ramsesCamera.getViewportHeight()) }); - - PropertyImpl& frustum = *binding.getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))->m_impl; - frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::NearPlane))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getNearPlane() }); - frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::FarPlane))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getFarPlane() }); + PropertyImpl& viewport = binding.getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))->impl(); + viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getViewportX() }); + viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getViewportY() }); + viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))->impl().initializeBindingInputValue(PropertyValue{ static_cast(ramsesCamera.getViewportWidth()) }); + viewport.getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))->impl().initializeBindingInputValue(PropertyValue{ static_cast(ramsesCamera.getViewportHeight()) }); + + PropertyImpl& frustum = binding.getInputs()->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))->impl(); + frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::NearPlane))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getNearPlane() }); + frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::FarPlane))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getFarPlane() }); if (binding.hasFrustumPlanesProperties()) { - frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::LeftPlane))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getLeftPlane() }); - frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::RightPlane))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getRightPlane() }); - frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::BottomPlane))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getBottomPlane() }); - frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::TopPlane))->m_impl->initializeBindingInputValue(PropertyValue{ ramsesCamera.getTopPlane() }); + frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::LeftPlane))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getLeftPlane() }); + frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::RightPlane))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getRightPlane() }); + frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::BottomPlane))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getBottomPlane() }); + frustum.getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::TopPlane))->impl().initializeBindingInputValue(PropertyValue{ ramsesCamera.getTopPlane() }); } else { assert(ramsesCamera.isOfType(ramses::ERamsesObjectType::PerspectiveCamera)); - const auto* perspectiveCam = ramses::RamsesUtils::TryConvert(ramsesCamera); - frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))->m_impl->initializeBindingInputValue(PropertyValue{ perspectiveCam->getVerticalFieldOfView() }); - frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))->m_impl->initializeBindingInputValue(PropertyValue{ perspectiveCam->getAspectRatio() }); + const auto* perspectiveCam = ramsesCamera.as(); + frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))->impl().initializeBindingInputValue(PropertyValue{ perspectiveCam->getVerticalFieldOfView() }); + frustum.getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))->impl().initializeBindingInputValue(PropertyValue{ perspectiveCam->getAspectRatio() }); } } } diff --git a/client/logic/lib/impl/RamsesCameraBindingImpl.h b/src/client/impl/logic/CameraBindingImpl.h similarity index 74% rename from client/logic/lib/impl/RamsesCameraBindingImpl.h rename to src/client/impl/logic/CameraBindingImpl.h index e5d5ecfab..87ee6fd11 100644 --- a/client/logic/lib/impl/RamsesCameraBindingImpl.h +++ b/src/client/impl/logic/CameraBindingImpl.h @@ -8,11 +8,11 @@ #pragma once -#include "impl/RamsesBindingImpl.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" +#include "impl/logic/RamsesBindingImpl.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" -#include "ramses-client-api/RamsesObjectTypes.h" +#include "ramses/framework/RamsesObjectTypes.h" #include @@ -23,7 +23,7 @@ namespace ramses namespace rlogic_serialization { - struct RamsesCameraBinding; + struct CameraBinding; } namespace flatbuffers @@ -70,18 +70,18 @@ namespace ramses::internal TopPlane = 5, }; - class RamsesCameraBindingImpl : public RamsesBindingImpl + class CameraBindingImpl : public RamsesBindingImpl { public: - explicit RamsesCameraBindingImpl(ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name, uint64_t id); + explicit CameraBindingImpl(SceneImpl& scene, ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name, sceneObjectId_t id); - [[nodiscard]] static flatbuffers::Offset Serialize( - const RamsesCameraBindingImpl& cameraBinding, + [[nodiscard]] static flatbuffers::Offset Serialize( + const CameraBindingImpl& binding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap); - [[nodiscard]] static std::unique_ptr Deserialize( - const rlogic_serialization::RamsesCameraBinding& cameraBinding, + [[nodiscard]] static std::unique_ptr Deserialize( + const rlogic_serialization::CameraBinding& cameraBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap); @@ -97,6 +97,6 @@ namespace ramses::internal std::reference_wrapper m_ramsesCamera; bool m_hasFrustumPlanesProperties; - static void ApplyRamsesValuesToInputProperties(RamsesCameraBindingImpl& binding); + static void ApplyRamsesValuesToInputProperties(CameraBindingImpl& binding); }; } diff --git a/client/logic/lib/impl/Collection.cpp b/src/client/impl/logic/Collection.cpp similarity index 92% rename from client/logic/lib/impl/Collection.cpp rename to src/client/impl/logic/Collection.cpp index a3a8576b6..ee49cc3e1 100644 --- a/client/logic/lib/impl/Collection.cpp +++ b/src/client/impl/logic/Collection.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/Collection.h" -#include "ramses-logic/LuaScript.h" +#include "ramses/client/logic/Collection.h" +#include "ramses/client/logic/LuaScript.h" #include namespace ramses diff --git a/client/logic/lib/impl/DataArray.cpp b/src/client/impl/logic/DataArray.cpp similarity index 84% rename from client/logic/lib/impl/DataArray.cpp rename to src/client/impl/logic/DataArray.cpp index 70995fc6a..b2f4efc27 100644 --- a/client/logic/lib/impl/DataArray.cpp +++ b/src/client/impl/logic/DataArray.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/DataArray.h" -#include "impl/DataArrayImpl.h" +#include "ramses/client/logic/DataArray.h" +#include "impl/logic/DataArrayImpl.h" namespace ramses { DataArray::DataArray(std::unique_ptr impl) noexcept : LogicObject(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_impl{ static_cast(*LogicObject::m_impl) } + , m_impl{ static_cast(LogicObject::m_impl) } { } @@ -34,6 +34,16 @@ namespace ramses return m_impl.getNumElements(); } + internal::DataArrayImpl& DataArray::impl() + { + return m_impl; + } + + const internal::DataArrayImpl& DataArray::impl() const + { + return m_impl; + } + template RAMSES_API const std::vector* DataArray::getDataInternal() const; template RAMSES_API const std::vector* DataArray::getDataInternal() const; template RAMSES_API const std::vector* DataArray::getDataInternal() const; diff --git a/client/logic/lib/impl/DataArrayImpl.cpp b/src/client/impl/logic/DataArrayImpl.cpp similarity index 81% rename from client/logic/lib/impl/DataArrayImpl.cpp rename to src/client/impl/logic/DataArrayImpl.cpp index 1de2a2a4e..551a5ae5a 100644 --- a/client/logic/lib/impl/DataArrayImpl.cpp +++ b/src/client/impl/logic/DataArrayImpl.cpp @@ -6,19 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/DataArrayImpl.h" -#include "generated/DataArrayGen.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/flatbuffers/generated/DataArrayGen.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/logic/DeserializationMap.h" #include "flatbuffers/flatbuffers.h" -#include "internals/ErrorReporting.h" -#include "LoggerImpl.h" -#include #include "glm/gtx/range.hpp" +#include namespace ramses::internal { template - DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id) - : LogicObjectImpl(name, id) + DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id) + : LogicObjectImpl(scene, name, id) , m_dataType{ PropertyTypeToEnum::TYPE } , m_data{ std::move(data) } { @@ -29,7 +30,7 @@ namespace ramses::internal { if (PropertyTypeToEnum::TYPE != m_dataType) { - LOG_ERROR("DataArray::getData failed for '{}', correct template that matches stored data type must be used.", getIdentificationString()); + LOG_ERROR_P(CONTEXT_CLIENT, "DataArray::getData failed for '{}', correct template that matches stored data type must be used.", getIdentificationString()); return nullptr; } @@ -150,14 +151,14 @@ namespace ramses::internal { if (!data.data_as() || !data.data_as()->data()) { - errorReporting.add("Fatal error during loading of DataArray from serialized data: unexpected data type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of DataArray from serialized data: unexpected data type!", nullptr); return false; } const auto fbVec = data.data_as()->data(); static_assert(std::is_arithmetic_v, "wrong base type used"); if (numComponents == 0u || fbVec->size() == 0u || (fbVec->size() % numComponents != 0)) { - errorReporting.add("Fatal error during loading of DataArray from serialized data: unexpected data size!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of DataArray from serialized data: unexpected data size!", nullptr); return false; } @@ -211,15 +212,15 @@ namespace ramses::internal } } - std::unique_ptr DataArrayImpl::Deserialize(const rlogic_serialization::DataArray& data, ErrorReporting& errorReporting) + std::unique_ptr DataArrayImpl::Deserialize(const rlogic_serialization::DataArray& data, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(data.base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of DataArray from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of DataArray from serialized data: missing name and/or ID!", nullptr); return nullptr; } @@ -233,7 +234,7 @@ namespace ramses::internal return nullptr; const auto& fbData = *data.data_as()->data(); auto dataVec = std::vector{ fbData.cbegin(), fbData.cend() }; - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::Vec2f: @@ -242,7 +243,7 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec(*data.data_as()->data(), numComponents); - return std::make_unique(std::move(dataVec), name, id); + return std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); } case rlogic_serialization::EDataArrayType::Vec3f: { @@ -250,7 +251,7 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec(*data.data_as()->data(), numComponents); - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::Vec4f: @@ -259,7 +260,7 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec(*data.data_as()->data(), numComponents); - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::Int32: @@ -269,7 +270,7 @@ namespace ramses::internal return nullptr; const auto& fbData = *data.data_as()->data(); auto dataVec = std::vector{ fbData.cbegin(), fbData.cend() }; - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::Vec2i: @@ -278,7 +279,7 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec(*data.data_as()->data(), numComponents); - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::Vec3i: @@ -287,7 +288,7 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec(*data.data_as()->data(), numComponents); - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::Vec4i: @@ -296,7 +297,7 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec(*data.data_as()->data(), numComponents); - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } case rlogic_serialization::EDataArrayType::FloatArray: @@ -305,11 +306,11 @@ namespace ramses::internal if (!checkFlatbufferVectorValidity, float, rlogic_serialization::floatArr>(data, errorReporting, numComponents)) return nullptr; auto dataVec = unflattenIntoArrayOfVec, float>(*data.data_as()->data(), numComponents); - deserialized = std::make_unique(std::move(dataVec), name, id); + deserialized = std::make_unique(deserializationMap.getScene(), std::move(dataVec), name, id); break; } default: - errorReporting.add(fmt::format("Fatal error during loading of DataArray from serialized data: unsupported or corrupt data type '{}'!", data.type()), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of DataArray from serialized data: unsupported or corrupt data type '{}'!", fmt::underlying(data.type())), nullptr); return nullptr; } @@ -330,15 +331,15 @@ namespace ramses::internal return m_data; } - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); - template DataArrayImpl::DataArrayImpl(std::vector>&& data, std::string_view name, uint64_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); + template DataArrayImpl::DataArrayImpl(SceneImpl& scene, std::vector>&& data, std::string_view name, sceneObjectId_t id); template const std::vector* DataArrayImpl::getData() const; template const std::vector* DataArrayImpl::getData() const; diff --git a/client/logic/lib/impl/DataArrayImpl.h b/src/client/impl/logic/DataArrayImpl.h similarity index 86% rename from client/logic/lib/impl/DataArrayImpl.h rename to src/client/impl/logic/DataArrayImpl.h index c88a004e9..49e1a0f59 100644 --- a/client/logic/lib/impl/DataArrayImpl.h +++ b/src/client/impl/logic/DataArrayImpl.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-logic/EPropertyType.h" -#include "impl/LogicObjectImpl.h" +#include "ramses/client/logic/EPropertyType.h" +#include "impl/logic/LogicObjectImpl.h" #include #include #include @@ -30,12 +30,13 @@ namespace ramses::internal { class ErrorReporting; class SerializationMap; + class DeserializationMap; class DataArrayImpl : public LogicObjectImpl { public: template - DataArrayImpl(std::vector&& data, std::string_view name, uint64_t id); + DataArrayImpl(SceneImpl& scene, std::vector&& data, std::string_view name, sceneObjectId_t id); template [[nodiscard]] const std::vector* getData() const; @@ -49,7 +50,8 @@ namespace ramses::internal [[nodiscard]] static std::unique_ptr Deserialize( const rlogic_serialization::DataArray& data, - ErrorReporting& errorReporting); + ErrorReporting& errorReporting, + DeserializationMap& deserializationMap); using DataArrayVariant = std::variant< std::vector, diff --git a/client/logic/lib/impl/Iterator.cpp b/src/client/impl/logic/Iterator.cpp similarity index 95% rename from client/logic/lib/impl/Iterator.cpp rename to src/client/impl/logic/Iterator.cpp index 34c93bfa9..5a2789b2c 100644 --- a/client/logic/lib/impl/Iterator.cpp +++ b/src/client/impl/logic/Iterator.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/Iterator.h" -#include "ramses-logic/LuaScript.h" +#include "ramses/client/logic/Iterator.h" +#include "ramses/client/logic/LuaScript.h" #include diff --git a/src/client/impl/logic/LogicEngine.cpp b/src/client/impl/logic/LogicEngine.cpp new file mode 100644 index 000000000..b51db95d3 --- /dev/null +++ b/src/client/impl/logic/LogicEngine.cpp @@ -0,0 +1,348 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/logic/LogicEngine.h" + +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" + +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/LuaConfigImpl.h" +#include "internal/logic/ApiObjects.h" + +#include + +namespace ramses +{ + LogicEngine::LogicEngine(std::unique_ptr impl) + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + internal::LogicEngineImpl& LogicEngine::impl() + { + return m_impl; + } + + const internal::LogicEngineImpl& LogicEngine::impl() const + { + return m_impl; + } + + template + Collection LogicEngine::getLogicObjectsInternal() const + { + return Collection(m_impl.getApiObjects().getApiObjectContainer()); + } + + template + const T* LogicEngine::findLogicObjectInternal(std::string_view name) const + { + // const version of findLogicObjectInternal cast to its non-const version to avoid duplicating code + return (const_cast(*this)).findLogicObjectInternal(name); + } + + template + T* LogicEngine::findLogicObjectInternal(std::string_view name) + { + auto& container = m_impl.getApiObjects().getApiObjectContainer(); + const auto it = std::find_if(container.begin(), container.end(), [name](const auto& o) { + return o->getName() == name; }); + + return (it == container.end() ? nullptr : *it); + } + + template + const T* LogicEngine::findLogicObjectInternal(sceneObjectId_t id) const + { + // const version of findLogicObjectInternal cast to its non-const version to avoid duplicating code + return (const_cast(*this)).findLogicObjectInternal(id); + } + + template + T* LogicEngine::findLogicObjectInternal(sceneObjectId_t id) + { + auto& container = m_impl.getApiObjects().getApiObjectContainer(); + const auto it = std::find_if(container.begin(), container.end(), [id](const auto& o) { + return o->getSceneObjectId() == id; }); + + return (it == container.end() ? nullptr : *it); + } + + LuaScript* LogicEngine::createLuaScript(std::string_view source, const LuaConfig& config, std::string_view scriptName) + { + return m_impl.createLuaScript(source, config.impl(), scriptName); + } + + LuaInterface* LogicEngine::createLuaInterface(std::string_view source, std::string_view interfaceName, const LuaConfig& config) + { + return m_impl.createLuaInterface(source, config.impl(), interfaceName); + } + + LuaModule* LogicEngine::createLuaModule(std::string_view source, const LuaConfig& config, std::string_view moduleName) + { + return m_impl.createLuaModule(source, config.impl(), moduleName); + } + + bool LogicEngine::extractLuaDependencies(std::string_view source, const std::function& callbackFunc) + { + return m_impl.extractLuaDependencies(source, callbackFunc); + } + + NodeBinding* LogicEngine::createNodeBinding(Node& ramsesNode, ERotationType rotationType /* = ramses::ERotationType::Euler_XYZ*/, std::string_view name) + { + return m_impl.createNodeBinding(ramsesNode, rotationType, name); + } + + bool LogicEngine::destroy(LogicObject& object) + { + return m_impl.destroy(object); + } + + AppearanceBinding* LogicEngine::createAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name) + { + return m_impl.createAppearanceBinding(ramsesAppearance, name); + } + + CameraBinding* LogicEngine::createCameraBinding(ramses::Camera& ramsesCamera, std::string_view name) + { + return m_impl.createCameraBinding(ramsesCamera, name); + } + + CameraBinding* LogicEngine::createCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name) + { + return m_impl.createCameraBindingWithFrustumPlanes(ramsesCamera, name); + } + + RenderPassBinding* LogicEngine::createRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name) + { + return m_impl.createRenderPassBinding(ramsesRenderPass, name); + } + + RenderGroupBinding* LogicEngine::createRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElements& elements, std::string_view name) + { + return m_impl.createRenderGroupBinding(ramsesRenderGroup, elements, name); + } + + MeshNodeBinding* LogicEngine::createMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name) + { + return m_impl.createMeshNodeBinding(ramsesMeshNode, name); + } + + SkinBinding* LogicEngine::createSkinBinding( + const std::vector& joints, + const std::vector& inverseBindMatrices, + AppearanceBinding& appearanceBinding, + const UniformInput& jointMatInput, + std::string_view name) + { + return m_impl.createSkinBinding(joints, inverseBindMatrices, appearanceBinding, jointMatInput, name); + } + + template + DataArray* LogicEngine::createDataArrayInternal(const std::vector& data, std::string_view name) + { + static_assert(CanPropertyTypeBeStoredInDataArray(PropertyTypeToEnum::TYPE)); + return m_impl.createDataArray(data, name); + } + + AnimationNode* LogicEngine::createAnimationNode(const AnimationNodeConfig& config, std::string_view name) + { + return m_impl.createAnimationNode(config, name); + } + + TimerNode* LogicEngine::createTimerNode(std::string_view name) + { + return m_impl.createTimerNode(name); + } + + AnchorPoint* LogicEngine::createAnchorPoint(NodeBinding& nodeBinding, CameraBinding& cameraBinding, std::string_view name) + { + return m_impl.createAnchorPoint(nodeBinding, cameraBinding, name); + } + + bool LogicEngine::update() + { + return m_impl.update(); + } + + void LogicEngine::enableUpdateReport(bool enable) + { + m_impl.enableUpdateReport(enable); + } + + LogicEngineReport LogicEngine::getLastUpdateReport() const + { + return m_impl.getLastUpdateReport(); + } + + void LogicEngine::setStatisticsLoggingRate(size_t loggingRate, EStatisticsLogMode mode) + { + m_impl.setStatisticsLoggingRate(loggingRate, mode); + } + + bool LogicEngine::link(Property& sourceProperty, Property& targetProperty) + { + return m_impl.link(sourceProperty, targetProperty); + } + + bool LogicEngine::linkWeak(Property& sourceProperty, Property& targetProperty) + { + return m_impl.linkWeak(sourceProperty, targetProperty); + } + + bool LogicEngine::unlink(Property& sourceProperty, Property& targetProperty) + { + return m_impl.unlink(sourceProperty, targetProperty); + } + + bool LogicEngine::isLinked(const LogicNode& logicNode) const + { + return m_impl.isLinked(logicNode); + } + + size_t LogicEngine::getTotalSerializedSize(ELuaSavingMode luaSavingMode) const + { + return m_impl.getTotalSerializedSize(luaSavingMode); + } + + template + size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode luaSavingMode) const + { + return m_impl.getSerializedSize(luaSavingMode); + } + + const std::vector& LogicEngine::getPropertyLinks() const + { + return impl().getApiObjects().getAllPropertyLinks(); + } + + const std::vector& LogicEngine::getPropertyLinks() + { + return m_impl.getApiObjects().getAllPropertyLinks(); + } + + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + template RAMSES_API Collection LogicEngine::getLogicObjectsInternal() const; + + template RAMSES_API const LogicObject* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const LuaScript* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const LuaModule* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const LuaInterface* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const NodeBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const AppearanceBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const CameraBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const RenderPassBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const RenderGroupBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const MeshNodeBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const SkinBinding* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const DataArray* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const AnimationNode* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const TimerNode* LogicEngine::findLogicObjectInternal(std::string_view) const; + template RAMSES_API const AnchorPoint* LogicEngine::findLogicObjectInternal(std::string_view) const; + + template RAMSES_API LogicObject* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API LuaScript* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API LuaModule* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API LuaInterface* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API NodeBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API AppearanceBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API CameraBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API RenderPassBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API RenderGroupBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API MeshNodeBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API SkinBinding* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API DataArray* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API AnimationNode* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API TimerNode* LogicEngine::findLogicObjectInternal(std::string_view); + template RAMSES_API AnchorPoint* LogicEngine::findLogicObjectInternal(std::string_view); + + template RAMSES_API const LogicObject* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const LuaScript* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const LuaModule* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const LuaInterface* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const NodeBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const AppearanceBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const CameraBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const RenderPassBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const RenderGroupBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const MeshNodeBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const SkinBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const DataArray* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const AnimationNode* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const TimerNode* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + template RAMSES_API const AnchorPoint* LogicEngine::findLogicObjectInternal(sceneObjectId_t) const; + + template RAMSES_API LogicObject* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API LuaScript* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API LuaModule* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API LuaInterface* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API NodeBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API AppearanceBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API CameraBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API RenderPassBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API RenderGroupBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API MeshNodeBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API SkinBinding* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API DataArray* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API AnimationNode* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API TimerNode* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + template RAMSES_API AnchorPoint* LogicEngine::findLogicObjectInternal(sceneObjectId_t); + + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal(const std::vector&, std::string_view); + template RAMSES_API DataArray* LogicEngine::createDataArrayInternal>(const std::vector>&, std::string_view); + + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; + template RAMSES_API size_t LogicEngine::getSerializedSizeInternal(ELuaSavingMode) const; +} diff --git a/src/client/impl/logic/LogicEngineImpl.cpp b/src/client/impl/logic/LogicEngineImpl.cpp new file mode 100644 index 000000000..ce4eb4e69 --- /dev/null +++ b/src/client/impl/logic/LogicEngineImpl.cpp @@ -0,0 +1,706 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/logic/LogicEngineImpl.h" +#include "impl/ValidationReportImpl.h" + +#include "ramses/framework/RamsesVersion.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" + +#include "impl/logic/AnchorPointImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LuaModuleImpl.h" +#include "impl/logic/LuaConfigImpl.h" +#include "impl/SaveFileConfigImpl.h" +#include "impl/logic/TimerNodeImpl.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/LogicEngineReportImpl.h" +#include "impl/logic/RenderGroupBindingElementsImpl.h" +#include "impl/SceneImpl.h" +#include "impl/NodeImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/EFeatureLevelImpl.h" +#include "impl/ErrorReporting.h" +#include "impl/SerializationContext.h" + +#include "internal/logic/FileUtils.h" +#include "internal/logic/TypeUtils.h" +#include "internal/logic/RamsesObjectResolver.h" + +#include "ramses/client/Node.h" +#include "ramses/client/Camera.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/ramses-utils.h" +#include "internal/Core/Utils/LogMacros.h" + +#include "internal/logic/flatbuffers/generated/LogicEngineGen.h" +#include "ramses-sdk-build-config.h" + +#include "fmt/format.h" + +#include +#include +#include + +namespace ramses::internal +{ + LogicEngineImpl::LogicEngineImpl(SceneImpl& sceneImpl, std::string_view name) + : SceneObjectImpl{ sceneImpl, ERamsesObjectType::LogicEngine, name } + , m_featureLevel{ sceneImpl.getClientImpl().getFramework().getFeatureLevel() } + , m_apiObjects{ std::make_unique(m_featureLevel, sceneImpl) } + { + } + + LuaScript* LogicEngineImpl::createLuaScript(std::string_view source, const LuaConfigImpl& config, std::string_view scriptName) + { + return m_apiObjects->createLuaScript(source, config, scriptName, getErrorReporting()); + } + + LuaInterface* LogicEngineImpl::createLuaInterface(std::string_view source, const LuaConfigImpl& config, std::string_view interfaceName) + { + return m_apiObjects->createLuaInterface(source, config, interfaceName, getErrorReporting()); + } + + LuaModule* LogicEngineImpl::createLuaModule(std::string_view source, const LuaConfigImpl& config, std::string_view moduleName) + { + return m_apiObjects->createLuaModule(source, config, moduleName, getErrorReporting()); + } + + bool LogicEngineImpl::extractLuaDependencies(std::string_view source, const std::function& callbackFunc) + { + const std::optional> extractedDependencies = LuaCompilationUtils::ExtractModuleDependencies(source, getErrorReporting()); + if (!extractedDependencies) + return false; + + for (const auto& dep : *extractedDependencies) + callbackFunc(dep); + + return true; + } + + NodeBinding* LogicEngineImpl::createNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesNode.impl())) + { + getErrorReporting().set(fmt::format("Failed to create NodeBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesNode.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + return m_apiObjects->createNodeBinding(ramsesNode, rotationType, name); + } + + AppearanceBinding* LogicEngineImpl::createAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesAppearance.impl())) + { + getErrorReporting().set(fmt::format("Failed to create AppearanceBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesAppearance.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + return m_apiObjects->createAppearanceBinding(ramsesAppearance, name); + } + + CameraBinding* LogicEngineImpl::createCameraBinding(ramses::Camera& ramsesCamera, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesCamera.impl())) + { + getErrorReporting().set(fmt::format("Failed to create CameraBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesCamera.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + return m_apiObjects->createCameraBinding(ramsesCamera, false, name); + } + + CameraBinding* LogicEngineImpl::createCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesCamera.impl())) + { + getErrorReporting().set(fmt::format("Failed to create CameraBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesCamera.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + return m_apiObjects->createCameraBinding(ramsesCamera, true, name); + } + + RenderPassBinding* LogicEngineImpl::createRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesRenderPass.impl())) + { + getErrorReporting().set(fmt::format("Failed to create RenderPassBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesRenderPass.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + return m_apiObjects->createRenderPassBinding(ramsesRenderPass, name); + } + + RenderGroupBinding* LogicEngineImpl::createRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElements& elements, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesRenderGroup.impl())) + { + getErrorReporting().set(fmt::format("Failed to create RenderGroupBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesRenderGroup.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + if (elements.impl().getElements().empty()) + { + getErrorReporting().set("Cannot create RenderGroupBinding, there were no elements provided.", *this); + return nullptr; + } + + if (!isFromTheSameSceneAs(elements.impl().getElements().front().second->impl())) + { + getErrorReporting().set(fmt::format("Failed to create RenderGroupBinding, elements are from sceneId={} but LogicEngine is from sceneId={}", + elements.impl().getElements().front().second->impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + for (const auto& element : elements.impl().getElements()) + { + bool isContained = false; + if (element.second->isOfType(ramses::ERamsesObjectType::MeshNode)) + { + isContained = ramsesRenderGroup.containsMeshNode(*element.second->as()); + } + else if (element.second->isOfType(ramses::ERamsesObjectType::RenderGroup)) + { + isContained = ramsesRenderGroup.containsRenderGroup(*element.second->as()); + } + + if (!isContained) + { + getErrorReporting().set("Cannot create RenderGroupBinding, one or more of the provided elements is not contained in the RenderGroup to bind.", *this); + return nullptr; + } + } + + return m_apiObjects->createRenderGroupBinding(ramsesRenderGroup, elements, name); + } + + MeshNodeBinding* LogicEngineImpl::createMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name) + { + if (!isFromTheSameSceneAs(ramsesMeshNode.impl())) + { + getErrorReporting().set(fmt::format("Failed to create MeshNodeBinding, object is from sceneId={} but LogicEngine is from sceneId={}", ramsesMeshNode.impl().getSceneImpl().getSceneId(), getSceneImpl().getSceneId()), *this); + return nullptr; + } + + return m_apiObjects->createMeshNodeBinding(ramsesMeshNode, name); + } + + SkinBinding* LogicEngineImpl::createSkinBinding( + const std::vector& joints, + const std::vector& inverseBindMatrices, + AppearanceBinding& appearanceBinding, + const UniformInput& jointMatInput, + std::string_view name) + { + if (joints.empty() || std::find(joints.cbegin(), joints.cend(), nullptr) != joints.cend()) + { + getErrorReporting().set("Cannot create SkinBinding, no or null joint node bindings provided.", *this); + return nullptr; + } + + if (joints.size() != inverseBindMatrices.size()) + { + getErrorReporting().set("Cannot create SkinBinding, number of inverse matrices must match the number of joints.", *this); + return nullptr; + } + + for (const auto nodeBinding : joints) + { + const auto& nodeBindings = m_apiObjects->getApiObjectContainer(); + if (std::find(nodeBindings.cbegin(), nodeBindings.cend(), nodeBinding) == nodeBindings.cend()) + { + getErrorReporting().set(fmt::format("Failed to create SkinBinding '{}': one or more of the provided Ramses node bindings was not found in this logic instance.", name), *this); + return nullptr; + } + } + + const auto& appearanceBindings = m_apiObjects->getApiObjectContainer(); + if (std::find(appearanceBindings.cbegin(), appearanceBindings.cend(), &appearanceBinding) == appearanceBindings.cend()) + { + getErrorReporting().set(fmt::format("Failed to create SkinBinding '{}': provided Ramses appearance binding was not found in this logic instance.", name), *this); + return nullptr; + } + + const auto actualUniformInputOpt = appearanceBinding.getRamsesAppearance().getEffect().findUniformInput(jointMatInput.getName()); + if (!actualUniformInputOpt.has_value() || appearanceBinding.getRamsesAppearance().isInputBound(*actualUniformInputOpt)) + { + getErrorReporting().set("Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound.", *this); + return nullptr; + } + + if (actualUniformInputOpt->getDataType() != ramses::EDataType::Matrix44F + || actualUniformInputOpt->getElementCount() != joints.size()) + { + getErrorReporting().set("Cannot create SkinBinding, provided uniform input must be of type array of Matrix4x4 with element count matching number of joints.", *this); + return nullptr; + } + + std::vector jointsAsImpls; + jointsAsImpls.reserve(joints.size()); + for (const auto j : joints) + jointsAsImpls.push_back(&j->impl()); + + return m_apiObjects->createSkinBinding(std::move(jointsAsImpls), inverseBindMatrices, appearanceBinding.impl(), *actualUniformInputOpt, name); + } + + template + DataArray* LogicEngineImpl::createDataArray(const std::vector& data, std::string_view name) + { + static_assert(CanPropertyTypeBeStoredInDataArray(PropertyTypeToEnum::TYPE)); + + if (data.empty()) + { + getErrorReporting().set(fmt::format("Cannot create DataArray '{}' with empty data.", name), *this); + return nullptr; + } + + if constexpr (std::is_same_v>) + { + for (const auto& vec : data) + { + if (vec.size() != data.front().size()) + { + getErrorReporting().set("Failed to create DataArray of float arrays: all arrays must be of same size.", *this); + return nullptr; + } + } + } + + // NOLINTNEXTLINE(readability-misleading-indentation) for some reason clang is confused about constexpr branch above + return m_apiObjects->createDataArray(data, name); + } + + ramses::AnimationNode* LogicEngineImpl::createAnimationNode(const AnimationNodeConfig& config, std::string_view name) + { + auto containsDataArray = [this](const DataArray* da) { + const auto& dataArrays = m_apiObjects->getApiObjectContainer(); + const auto it = std::find_if(dataArrays.cbegin(), dataArrays.cend(), + [da](const auto& d) { return d == da; }); + return it != dataArrays.cend(); + }; + + if (config.getChannels().empty()) + { + getErrorReporting().set(fmt::format("Failed to create AnimationNode '{}': must provide at least one channel.", name), *this); + return nullptr; + } + + for (const auto& channel : config.getChannels()) + { + if (!containsDataArray(channel.timeStamps) || + !containsDataArray(channel.keyframes)) + { + getErrorReporting().set(fmt::format("Failed to create AnimationNode '{}': timestamps or keyframes were not found in this logic instance.", name), *this); + return nullptr; + } + + if ((channel.tangentsIn && !containsDataArray(channel.tangentsIn)) || + (channel.tangentsOut && !containsDataArray(channel.tangentsOut))) + { + getErrorReporting().set(fmt::format("Failed to create AnimationNode '{}': tangents were not found in this logic instance.", name), *this); + return nullptr; + } + } + + return m_apiObjects->createAnimationNode(config.impl(), name); + } + + TimerNode* LogicEngineImpl::createTimerNode(std::string_view name) + { + return m_apiObjects->createTimerNode(name); + } + + AnchorPoint* LogicEngineImpl::createAnchorPoint(NodeBinding& nodeBinding, CameraBinding& cameraBinding, std::string_view name) + { + const auto& nodeBindings = m_apiObjects->getApiObjectContainer(); + const auto& cameraBindings = m_apiObjects->getApiObjectContainer(); + if (std::find(nodeBindings.cbegin(), nodeBindings.cend(), &nodeBinding) == nodeBindings.cend() || + std::find(cameraBindings.cbegin(), cameraBindings.cend(), &cameraBinding) == cameraBindings.cend()) + { + getErrorReporting().set(fmt::format("Failed to create AnchorPoint '{}': provided Ramses node binding and/or camera binding were not found in this logic instance.", name), *this); + return nullptr; + } + + return m_apiObjects->createAnchorPoint(nodeBinding.impl(), cameraBinding.impl(), name); + } + + bool LogicEngineImpl::destroy(LogicObject& object) + { + return m_apiObjects->destroy(object, getErrorReporting()); + } + + bool LogicEngineImpl::isLinked(const LogicNode& logicNode) const + { + return m_apiObjects->getLogicNodeDependencies().isLinked(logicNode.impl()); + } + + size_t LogicEngineImpl::activateLinksRecursive(PropertyImpl& output) + { + size_t activatedLinks = 0u; + + const auto childCount = output.getChildCount(); + for (size_t i = 0; i < childCount; ++i) + { + PropertyImpl& child = output.getChild(i)->impl(); + + if (TypeUtils::CanHaveChildren(child.getType())) + { + activatedLinks += activateLinksRecursive(child); + } + else + { + const auto& outgoingLinks = child.getOutgoingLinks(); + for (const auto& outLink : outgoingLinks) + { + PropertyImpl* linkedProp = outLink.property; + const bool valueChanged = linkedProp->setValue(child.getValue()); + if (valueChanged || linkedProp->getPropertySemantics() == EPropertySemantics::AnimationInput) + { + linkedProp->getLogicNode().setDirty(true); + ++activatedLinks; + } + } + } + } + + return activatedLinks; + } + + bool LogicEngineImpl::update() + { + if (m_statisticsEnabled || m_updateReportEnabled) + { + m_updateReport.clear(); + m_updateReport.sectionStarted(UpdateReport::ETimingSection::TotalUpdate); + } + if (m_updateReportEnabled) + { + m_updateReport.sectionStarted(UpdateReport::ETimingSection::TopologySort); + } + + const std::optional& sortedNodes = m_apiObjects->getLogicNodeDependencies().getTopologicallySortedNodes(); + if (!sortedNodes) + { + getErrorReporting().set("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling update()!", *this); + return false; + } + + if (m_updateReportEnabled) + m_updateReport.sectionFinished(UpdateReport::ETimingSection::TopologySort); + + // force dirty all timer nodes, anchor points and skinbindings + setNodeToBeAlwaysUpdatedDirty(); + + const bool success = updateNodes(*sortedNodes); + + if (m_statisticsEnabled || m_updateReportEnabled) + { + m_updateReport.sectionFinished(UpdateReport::ETimingSection::TotalUpdate); + m_statistics.collect(m_updateReport, sortedNodes->size()); + if (m_statistics.checkUpdateFrameFinished()) + m_statistics.calculateAndLog(); + } + + return success; + } + + bool LogicEngineImpl::updateNodes(const NodeVector& sortedNodes) + { + for (LogicNodeImpl* nodeIter : sortedNodes) + { + LogicNodeImpl& node = *nodeIter; + + if (!node.isDirty()) + { + if (m_updateReportEnabled) + m_updateReport.nodeSkippedExecution(node); + + if(m_nodeDirtyMechanismEnabled) + continue; + } + + if (m_updateReportEnabled) + m_updateReport.nodeExecutionStarted(node); + if (m_statisticsEnabled) + m_statistics.nodeExecuted(); + + const std::optional potentialError = node.update(); + if (potentialError) + { + getErrorReporting().set(potentialError->message, &node.getLogicObject()); + return false; + } + + Property* outputs = node.getOutputs(); + if (outputs != nullptr) + { + const size_t activatedLinks = activateLinksRecursive(outputs->impl()); + + if (m_statisticsEnabled || m_updateReportEnabled) + m_updateReport.linksActivated(activatedLinks); + } + + if (m_updateReportEnabled) + m_updateReport.nodeExecutionFinished(); + + node.setDirty(false); + } + + return true; + } + + void LogicEngineImpl::setNodeToBeAlwaysUpdatedDirty() + { + // force timer nodes dirty so they can update their ticker + for (TimerNode* timerNode : m_apiObjects->getApiObjectContainer()) + timerNode->impl().setDirty(true); + // force anchor points dirty because they depend on set of ramses states which cannot be monitored + for (AnchorPoint* anchorPoint : m_apiObjects->getApiObjectContainer()) + anchorPoint->impl().setDirty(true); + // force skinbindings dirty because they depend on set of ramses states which cannot be monitored + for (SkinBinding* skinBinding : m_apiObjects->getApiObjectContainer()) + skinBinding->impl().setDirty(true); + } + + void LogicEngineImpl::onValidate(ValidationReportImpl& report) const + { + if (m_apiObjects->bindingsDirty()) + report.add(EIssueType::Warning, "Saving logic engine content with manually updated binding values without calling update() will result in those values being lost!", nullptr); + + m_apiObjects->validateInterfaces(report); + m_apiObjects->validateDanglingNodes(report); + } + + bool LogicEngineImpl::loadFromByteData(const void* byteData, size_t byteSize, bool enableMemoryVerification, const std::string& dataSourceDescription) + { + if (byteSize < 8) + { + getErrorReporting().set(fmt::format("{} contains corrupted data! Data should be at least 8 bytes", dataSourceDescription), *this); + return false; + } + + auto* uint8Data(static_cast(byteData)); + if (enableMemoryVerification) + { + flatbuffers::Verifier bufferVerifier(uint8Data, byteSize); + const bool bufferOK = bufferVerifier.VerifyBuffer(); + + if (!bufferOK) + { + getErrorReporting().set(fmt::format("{} contains corrupted data!", dataSourceDescription), *this); + return false; + } + } + + const auto* logicEngine = rlogic_serialization::GetLogicEngine(byteData); + + if (nullptr == logicEngine) + { + getErrorReporting().set(fmt::format("{} doesn't contain logic engine data with readable version specifiers", dataSourceDescription), *this); + return false; + } + + LOG_INFO_P(CONTEXT_CLIENT, "Loading logic engine content from '{}'", dataSourceDescription); + + if (nullptr == logicEngine->apiObjects()) + { + getErrorReporting().set(fmt::format("Fatal error while loading {}: doesn't contain API objects!", dataSourceDescription), *this); + return false; + } + + RamsesObjectResolver ramsesResolver{ getErrorReporting(), getSceneImpl() }; + std::unique_ptr deserializedObjects = ApiObjects::Deserialize(getSceneImpl(), *logicEngine->apiObjects(), ramsesResolver, dataSourceDescription, getErrorReporting(), m_featureLevel); + + if (!deserializedObjects) + { + return false; + } + + // No errors -> move data into member + m_apiObjects = std::move(deserializedObjects); + + return true; + } + + bool LogicEngineImpl::save(flatbuffers::FlatBufferBuilder& builder, const SaveFileConfigImpl& config) + { + // Refuse save() if logic graph has loops + if (!m_apiObjects->getLogicNodeDependencies().getTopologicallySortedNodes()) + { + getErrorReporting().set("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling saveToFile()!", *this); + return false; + } + + const auto& scripts = m_apiObjects->getApiObjectContainer(); + const auto sIt = std::find_if(scripts.cbegin(), scripts.cend(), [](const LuaScript* s) { return s->impl().hasDebugLogFunctions(); }); + if (sIt != scripts.cend()) + { + getErrorReporting().set(fmt::format("Cannot save to file, Lua script '{}' has enabled debug log functions, remove this script before saving.", (*sIt)->impl().getIdentificationString()), *sIt); + return false; + } + const auto& modules = m_apiObjects->getApiObjectContainer(); + const auto mIt = std::find_if(modules.cbegin(), modules.cend(), [](const LuaModule* m) { return m->impl().hasDebugLogFunctions(); }); + if (mIt != modules.cend()) + { + getErrorReporting().set(fmt::format("Cannot save to file, Lua module '{}' has enabled debug log functions, remove this module before saving.", (*mIt)->impl().getIdentificationString()), *mIt); + return false; + } + + const auto logicEngine = rlogic_serialization::CreateLogicEngine(builder, + ApiObjects::Serialize(*m_apiObjects, builder, config.getLuaSavingMode())); + + builder.Finish(logicEngine); + + return true; + } + + bool LogicEngineImpl::link(Property& sourceProperty, Property& targetProperty) + { + return m_apiObjects->getLogicNodeDependencies().link(sourceProperty.impl(), targetProperty.impl(), false, getErrorReporting()); + } + + bool LogicEngineImpl::linkWeak(Property& sourceProperty, Property& targetProperty) + { + return m_apiObjects->getLogicNodeDependencies().link(sourceProperty.impl(), targetProperty.impl(), true, getErrorReporting()); + } + + bool LogicEngineImpl::unlink(Property& sourceProperty, Property& targetProperty) + { + return m_apiObjects->getLogicNodeDependencies().unlink(sourceProperty.impl(), targetProperty.impl(), getErrorReporting()); + } + + const ApiObjects& LogicEngineImpl::getApiObjects() const + { + return *m_apiObjects; + } + + ApiObjects& LogicEngineImpl::getApiObjects() + { + return *m_apiObjects; + } + + void LogicEngineImpl::disableTrackingDirtyNodes() + { + m_nodeDirtyMechanismEnabled = false; + } + + void LogicEngineImpl::enableUpdateReport(bool enable) + { + m_updateReportEnabled = enable; + if (!m_updateReportEnabled) + m_updateReport.clear(); + } + + LogicEngineReport LogicEngineImpl::getLastUpdateReport() const + { + return LogicEngineReport{ std::make_unique(m_updateReport) }; + } + + void LogicEngineImpl::setStatisticsLoggingRate(size_t loggingRate, EStatisticsLogMode mode) + { + m_statistics.setLoggingRate(loggingRate); + if (loggingRate == 0u) + { + m_statisticsEnabled = false; + enableUpdateReport(false); + return; + } + + m_statisticsEnabled = true; + switch (mode) + { + case EStatisticsLogMode::Compact: + enableUpdateReport(false); + break; + case EStatisticsLogMode::Detailed: + enableUpdateReport(true); + break; + } + } + + size_t LogicEngineImpl::getTotalSerializedSize(ELuaSavingMode luaSavingMode) const + { + return ApiObjectsSerializedSize::GetTotalSerializedSize(*m_apiObjects, luaSavingMode); + } + + bool LogicEngineImpl::serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const + { + if (!SceneObjectImpl::serialize(outStream, serializationContext)) + return false; + + flatbuffers::FlatBufferBuilder builder; + + if (!const_cast(this)->save(builder, serializationContext.getSaveConfig())) + return false; + + uint32_t size = builder.GetSize(); + outStream << size; + outStream.write(builder.GetBufferPointer(), builder.GetSize()); + return true; + } + + bool LogicEngineImpl::deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) + { + if (!SceneObjectImpl::deserialize(inStream, serializationContext)) + return false; + + uint32_t size = 0u; + inStream >> size; + m_byteBuffer.resize(size); + inStream.read(m_byteBuffer.data(), size); + // we need to parse the byte buffer later when all scene objects are available + serializationContext.addForDependencyResolve(this); + return true; + } + + bool LogicEngineImpl::resolveDeserializationDependencies(DeserializationContext& serializationContext) + { + const bool enableMemoryVerification = serializationContext.getLoadConfig().getMemoryVerificationEnabled(); + if (!loadFromByteData(m_byteBuffer.data(), m_byteBuffer.size(), enableMemoryVerification, fmt::format("data buffer '{}' (size: {})", m_byteBuffer.data(), m_byteBuffer.size()))) + return false; + + std::vector().swap(m_byteBuffer); + return true; + } + + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray(const std::vector&, std::string_view name); + template DataArray* LogicEngineImpl::createDataArray>(const std::vector>&, std::string_view name); +} diff --git a/src/client/impl/logic/LogicEngineImpl.h b/src/client/impl/logic/LogicEngineImpl.h new file mode 100644 index 000000000..2e2f62dc9 --- /dev/null +++ b/src/client/impl/logic/LogicEngineImpl.h @@ -0,0 +1,160 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "impl/SceneObjectImpl.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicEngineReport.h" +#include "ramses/framework/DataTypes.h" +#include "ramses/framework/EFeatureLevel.h" +#include "internal/logic/ApiObjects.h" +#include "internal/logic/LogicNodeDependencies.h" +#include "internal/logic/UpdateReport.h" +#include "internal/logic/LogicNodeUpdateStatistics.h" +#include "internal/logic/ApiObjectsSerializedSize.h" + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/ERotationType.h" + +#include +#include +#include +#include + +namespace ramses +{ + class SceneObject; + class Node; + class Appearance; + class Camera; + class RenderPass; + class RenderGroup; + class UniformInput; + class MeshNode; + + class NodeBinding; + class AppearanceBinding; + class CameraBinding; + class RenderPassBinding; + class RenderGroupBinding; + class RenderGroupBindingElements; + class MeshNodeBinding; + class SkinBinding; + class DataArray; + class AnimationNode; + class AnimationNodeConfig; + class TimerNode; + class AnchorPoint; + class LuaScript; + class LuaInterface; + class LuaModule; + class LogicNode; + class Property; +} + +namespace ramses::internal +{ + class SceneImpl; + class LuaConfigImpl; + class SaveFileConfigImpl; + class LogicNodeImpl; + class RamsesBindingImpl; + class ValidationReportImpl; + class ApiObjects; + + class LogicEngineImpl : public SceneObjectImpl + { + public: + LogicEngineImpl(SceneImpl& sceneImpl, std::string_view name); + + // Public API + LuaScript* createLuaScript(std::string_view source, const LuaConfigImpl& config, std::string_view scriptName); + LuaInterface* createLuaInterface(std::string_view source, const LuaConfigImpl& config, std::string_view interfaceName); + LuaModule* createLuaModule(std::string_view source, const LuaConfigImpl& config, std::string_view moduleName); + bool extractLuaDependencies(std::string_view source, const std::function& callbackFunc); + NodeBinding* createNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name); + AppearanceBinding* createAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name); + CameraBinding* createCameraBinding(ramses::Camera& ramsesCamera, std::string_view name); + CameraBinding* createCameraBindingWithFrustumPlanes(ramses::Camera& ramsesCamera, std::string_view name); + RenderPassBinding* createRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name); + RenderGroupBinding* createRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElements& elements, std::string_view name); + MeshNodeBinding* createMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name); + SkinBinding* createSkinBinding( + const std::vector& joints, + const std::vector& inverseBindMatrices, + AppearanceBinding& appearanceBinding, + const UniformInput& jointMatInput, + std::string_view name); + template + DataArray* createDataArray(const std::vector& data, std::string_view name); + AnimationNode* createAnimationNode(const AnimationNodeConfig& config, std::string_view name); + TimerNode* createTimerNode(std::string_view name); + AnchorPoint* createAnchorPoint(NodeBinding& nodeBinding, CameraBinding& cameraBinding, std::string_view name); + + bool destroy(LogicObject& object); + + bool update(); + + void onValidate(ValidationReportImpl& report) const override; + + bool link(Property& sourceProperty, Property& targetProperty); + bool linkWeak(Property& sourceProperty, Property& targetProperty); + bool unlink(Property& sourceProperty, Property& targetProperty); + + [[nodiscard]] bool isLinked(const LogicNode& logicNode) const; + + [[nodiscard]] const ApiObjects& getApiObjects() const; + [[nodiscard]] ApiObjects& getApiObjects(); + + // for benchmarking purposes only + void disableTrackingDirtyNodes(); + + void enableUpdateReport(bool enable); + [[nodiscard]] LogicEngineReport getLastUpdateReport() const; + + void setStatisticsLoggingRate(size_t loggingRate, EStatisticsLogMode mode = EStatisticsLogMode::Compact); + + [[nodiscard]] size_t getTotalSerializedSize(ELuaSavingMode luaSavingMode) const; + template + [[nodiscard]] size_t getSerializedSize(ELuaSavingMode luaSavingMode) const; + + // RamsesObject implementation + bool serialize(ramses::internal::IOutputStream& outStream, SerializationContext& serializationContext) const override; + bool deserialize(ramses::internal::IInputStream& inStream, DeserializationContext& serializationContext) override; + bool resolveDeserializationDependencies(DeserializationContext& serializationContext) override; + void deinitializeFrameworkData() override { /*logic has no internal framework data*/ } + + private: + bool save(flatbuffers::FlatBufferBuilder& builder, const SaveFileConfigImpl& config); + size_t activateLinksRecursive(PropertyImpl& output); + void setNodeToBeAlwaysUpdatedDirty(); + + [[nodiscard]] bool updateNodes(const NodeVector& nodes); + + [[nodiscard]] bool loadFromByteData(const void* byteData, size_t byteSize, bool enableMemoryVerification, const std::string& dataSourceDescription); + + EFeatureLevel m_featureLevel; + + std::unique_ptr m_apiObjects; + bool m_nodeDirtyMechanismEnabled = true; + + bool m_updateReportEnabled = false; + bool m_statisticsEnabled = true; + UpdateReport m_updateReport; + LogicNodeUpdateStatistics m_statistics; + std::vector m_byteBuffer; + }; + + template + size_t LogicEngineImpl::getSerializedSize(ELuaSavingMode luaSavingMode) const + { + return ApiObjectsSerializedSize::GetSerializedSize(*m_apiObjects, luaSavingMode); + } +} diff --git a/client/logic/lib/impl/LogicEngineReport.cpp b/src/client/impl/logic/LogicEngineReport.cpp similarity index 94% rename from client/logic/lib/impl/LogicEngineReport.cpp rename to src/client/impl/logic/LogicEngineReport.cpp index a9a37d00d..b6155ecf4 100644 --- a/client/logic/lib/impl/LogicEngineReport.cpp +++ b/src/client/impl/logic/LogicEngineReport.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngineReport.h" -#include "impl/LogicEngineReportImpl.h" +#include "ramses/client/logic/LogicEngineReport.h" +#include "impl/logic/LogicEngineReportImpl.h" namespace ramses { diff --git a/client/logic/lib/impl/LogicEngineReportImpl.cpp b/src/client/impl/logic/LogicEngineReportImpl.cpp similarity index 96% rename from client/logic/lib/impl/LogicEngineReportImpl.cpp rename to src/client/impl/logic/LogicEngineReportImpl.cpp index 128bec5f1..c3c07246a 100644 --- a/client/logic/lib/impl/LogicEngineReportImpl.cpp +++ b/src/client/impl/logic/LogicEngineReportImpl.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LogicEngineReportImpl.h" -#include "LogicNodeImpl.h" +#include "impl/logic/LogicEngineReportImpl.h" +#include "impl/logic/LogicNodeImpl.h" namespace ramses::internal { diff --git a/client/logic/lib/impl/LogicEngineReportImpl.h b/src/client/impl/logic/LogicEngineReportImpl.h similarity index 94% rename from client/logic/lib/impl/LogicEngineReportImpl.h rename to src/client/impl/logic/LogicEngineReportImpl.h index feb7fe674..285a5f258 100644 --- a/client/logic/lib/impl/LogicEngineReportImpl.h +++ b/src/client/impl/logic/LogicEngineReportImpl.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-logic/LogicNode.h" -#include "internals/UpdateReport.h" +#include "ramses/client/logic/LogicNode.h" +#include "internal/logic/UpdateReport.h" namespace ramses::internal { diff --git a/client/logic/lib/impl/LogicNode.cpp b/src/client/impl/logic/LogicNode.cpp similarity index 69% rename from client/logic/lib/impl/LogicNode.cpp rename to src/client/impl/logic/LogicNode.cpp index 8350627c5..786dd688e 100644 --- a/client/logic/lib/impl/LogicNode.cpp +++ b/src/client/impl/logic/LogicNode.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicNode.h" -#include "impl/LogicNodeImpl.h" +#include "ramses/client/logic/LogicNode.h" +#include "impl/logic/LogicNodeImpl.h" namespace ramses { LogicNode::LogicNode(std::unique_ptr impl) noexcept : LogicObject(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_impl{ static_cast(*LogicObject::m_impl) } + , m_impl{ static_cast(LogicObject::m_impl) } { } @@ -28,8 +28,23 @@ namespace ramses return m_impl.getInputs(); } + Property* LogicNode::getOutputs() + { + return m_impl.getOutputs(); + } + const Property* LogicNode::getOutputs() const { return m_impl.getOutputs(); } + + internal::LogicNodeImpl& LogicNode::impl() + { + return m_impl; + } + + const internal::LogicNodeImpl& LogicNode::impl() const + { + return m_impl; + } } diff --git a/client/logic/lib/impl/LogicNodeImpl.cpp b/src/client/impl/logic/LogicNodeImpl.cpp similarity index 80% rename from client/logic/lib/impl/LogicNodeImpl.cpp rename to src/client/impl/logic/LogicNodeImpl.cpp index c7a2390b3..b1c99c75d 100644 --- a/client/logic/lib/impl/LogicNodeImpl.cpp +++ b/src/client/impl/logic/LogicNodeImpl.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LogicNodeImpl.h" +#include "impl/logic/LogicNodeImpl.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" +#include "impl/logic/PropertyImpl.h" namespace ramses::internal { - LogicNodeImpl::LogicNodeImpl(std::string_view name, uint64_t id) noexcept - : LogicObjectImpl(name, id) + LogicNodeImpl::LogicNodeImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept + : LogicObjectImpl(scene, name, id) { } @@ -59,13 +59,13 @@ namespace ramses::internal if (rootInput) { m_inputs = PropertyImpl::CreateProperty(std::move(rootInput)); - m_inputs->m_impl->setLogicNode(*this); + m_inputs->impl().setLogicNode(*this); } if (rootOutput) { m_outputs = PropertyImpl::CreateProperty(std::move(rootOutput)); - m_outputs->m_impl->setLogicNode(*this); + m_outputs->impl().setLogicNode(*this); } } } diff --git a/client/logic/lib/impl/LogicNodeImpl.h b/src/client/impl/logic/LogicNodeImpl.h similarity index 85% rename from client/logic/lib/impl/LogicNodeImpl.h rename to src/client/impl/logic/LogicNodeImpl.h index bfaf7c0b7..a3211dd6e 100644 --- a/client/logic/lib/impl/LogicNodeImpl.h +++ b/src/client/impl/logic/LogicNodeImpl.h @@ -9,8 +9,8 @@ #pragma once -#include "impl/LogicObjectImpl.h" -#include "impl/PropertyImpl.h" +#include "impl/logic/LogicObjectImpl.h" +#include "impl/logic/PropertyImpl.h" #include #include #include @@ -23,9 +23,7 @@ namespace ramses::internal class LogicNodeImpl : public LogicObjectImpl { public: - explicit LogicNodeImpl(std::string_view name, uint64_t id) noexcept; - LogicNodeImpl(const LogicNodeImpl& other) = delete; - LogicNodeImpl& operator=(const LogicNodeImpl& other) = delete; + explicit LogicNodeImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept; ~LogicNodeImpl() noexcept override; [[nodiscard]] Property* getInputs(); diff --git a/src/client/impl/logic/LogicObject.cpp b/src/client/impl/logic/LogicObject.cpp new file mode 100644 index 000000000..e3e89eb64 --- /dev/null +++ b/src/client/impl/logic/LogicObject.cpp @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/logic/LogicObject.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "impl/logic/LogicObjectImpl.h" + +namespace ramses +{ + LogicObject::LogicObject(std::unique_ptr impl) noexcept + : SceneObject{ std::move(impl) } + , m_impl{ static_cast(SceneObject::m_impl) } + { + } + + internal::LogicObjectImpl& LogicObject::impl() + { + return m_impl; + } + + const internal::LogicObjectImpl& LogicObject::impl() const + { + return m_impl; + } + +} diff --git a/client/logic/lib/impl/LogicObjectImpl.cpp b/src/client/impl/logic/LogicObjectImpl.cpp similarity index 50% rename from client/logic/lib/impl/LogicObjectImpl.cpp rename to src/client/impl/logic/LogicObjectImpl.cpp index c9fb140f8..f01c2f1e0 100644 --- a/client/logic/lib/impl/LogicObjectImpl.cpp +++ b/src/client/impl/logic/LogicObjectImpl.cpp @@ -6,95 +6,59 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LogicObjectImpl.h" -#include "ramses-logic/LuaInterface.h" -#include "impl/LoggerImpl.h" -#include "internals/ErrorReporting.h" +#include "impl/logic/LogicObjectImpl.h" +#include "ramses/client/logic/LuaInterface.h" +#include "fmt/format.h" +#include "impl/ErrorReporting.h" #include "flatbuffers/flatbuffers.h" -#include "generated/LogicObjectGen.h" +#include "internal/logic/flatbuffers/generated/LogicObjectGen.h" namespace ramses::internal { - LogicObjectImpl::LogicObjectImpl(std::string_view name, uint64_t id) noexcept - : m_name(name) - , m_id(id) + LogicObjectImpl::LogicObjectImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept + : SceneObjectImpl{ scene, ERamsesObjectType::LogicObject, name } { - } - - LogicObjectImpl::~LogicObjectImpl() noexcept = default; - - std::string_view LogicObjectImpl::getName() const - { - return m_name; - } - - bool LogicObjectImpl::setName(std::string_view name) - { - m_name = name; - return true; - } - - uint64_t LogicObjectImpl::getId() const - { - return m_id; - } - - bool LogicObjectImpl::setUserId(uint64_t highId, uint64_t lowId) - { - m_userId = { highId, lowId }; - return true; - } - - std::pair LogicObjectImpl::getUserId() const - { - return m_userId; - } - - std::string LogicObjectImpl::getIdentificationString() const - { - if (m_userId.first != 0u || m_userId.second != 0u) - return fmt::format("{} [Id={} UserId={:016X}{:016X}]", m_name, m_id, m_userId.first, m_userId.second); - - return fmt::format("{} [Id={}]", m_name, m_id); + if (id.isValid()) // set ID from deserialization if created object during loading + m_sceneObjectId = id; } flatbuffers::Offset LogicObjectImpl::Serialize(const LogicObjectImpl& object, flatbuffers::FlatBufferBuilder& builder) { return rlogic_serialization::CreateLogicObject(builder, - builder.CreateString(object.m_name), - object.m_id, - object.m_userId.first, - object.m_userId.second + builder.CreateString(object.getName()), + object.getSceneObjectId().getValue(), + object.getUserId().first, + object.getUserId().second ); } bool LogicObjectImpl::Deserialize(const rlogic_serialization::LogicObject* object, std::string& name, - uint64_t& id, + sceneObjectId_t& id, uint64_t& userIdHigh, uint64_t& userIdLow, ErrorReporting& errorReporting) { if (!object) { - errorReporting.add("Fatal error during loading of LogicObject base from serialized data: missing base table!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LogicObject base from serialized data: missing base table!", nullptr); return false; } if (!object->name()) { - errorReporting.add("Fatal error during loading of LogicObject base from serialized data: missing name!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LogicObject base from serialized data: missing name!", nullptr); return false; } if (object->id() == 0u) { - errorReporting.add("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LogicObject base from serialized data: missing or invalid logic object ID!", nullptr); return false; } name = object->name()->string_view(); - id = object->id(); + id = sceneObjectId_t{ object->id() }; userIdHigh = object->userIdHigh(); userIdLow = object->userIdLow(); diff --git a/client/logic/lib/impl/LogicObjectImpl.h b/src/client/impl/logic/LogicObjectImpl.h similarity index 65% rename from client/logic/lib/impl/LogicObjectImpl.h rename to src/client/impl/logic/LogicObjectImpl.h index a4f82fa17..a0f89be69 100644 --- a/client/logic/lib/impl/LogicObjectImpl.h +++ b/src/client/impl/logic/LogicObjectImpl.h @@ -8,6 +8,7 @@ #pragma once +#include "impl/SceneObjectImpl.h" #include namespace flatbuffers @@ -29,40 +30,33 @@ namespace ramses namespace ramses::internal { class ErrorReporting; + class SceneImpl; - class LogicObjectImpl + class LogicObjectImpl : public SceneObjectImpl { public: - explicit LogicObjectImpl(std::string_view name, uint64_t id) noexcept; - virtual ~LogicObjectImpl() noexcept; - LogicObjectImpl(const LogicObjectImpl&) = delete; - LogicObjectImpl& operator=(const LogicObjectImpl&) = delete; - - [[nodiscard]] std::string_view getName() const; - [[nodiscard]] uint64_t getId() const; - bool setName(std::string_view name); - bool setUserId(uint64_t highId, uint64_t lowId); - [[nodiscard]] std::pair getUserId() const; - - [[nodiscard]] std::string getIdentificationString() const; + explicit LogicObjectImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept; void setLogicObject(LogicObject& obj); [[nodiscard]] const LogicObject& getLogicObject() const; [[nodiscard]] LogicObject& getLogicObject(); + // RamsesObject implementation + void deinitializeFrameworkData() final override { /*logic has no internal framework data*/ } + protected: + // Logic objects are validated by LogicEngineImpl + void onValidate(ValidationReportImpl& /*report*/) const final override {} + static flatbuffers::Offset Serialize(const LogicObjectImpl& object, flatbuffers::FlatBufferBuilder& builder); static bool Deserialize(const rlogic_serialization::LogicObject* object, std::string& name, - uint64_t& id, + sceneObjectId_t& id, uint64_t& userIdHigh, uint64_t& userIdLow, ErrorReporting& errorReporting); private: - std::string m_name; - uint64_t m_id; - std::pair m_userId{ 0u, 0u }; LogicObject* m_logicObject = nullptr; }; } diff --git a/client/logic/lib/impl/LuaConfig.cpp b/src/client/impl/logic/LuaConfig.cpp similarity index 84% rename from client/logic/lib/impl/LuaConfig.cpp rename to src/client/impl/logic/LuaConfig.cpp index 3d51ad228..0ecf039d5 100644 --- a/client/logic/lib/impl/LuaConfig.cpp +++ b/src/client/impl/logic/LuaConfig.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LuaConfig.h" +#include "ramses/client/logic/LuaConfig.h" -#include "impl/LuaConfigImpl.h" +#include "impl/logic/LuaConfigImpl.h" namespace ramses { @@ -47,4 +47,14 @@ namespace ramses { return m_impl->addStandardModuleDependency(stdModule); } + + internal::LuaConfigImpl& LuaConfig::impl() + { + return *m_impl; + } + + const internal::LuaConfigImpl& LuaConfig::impl() const + { + return *m_impl; + } } diff --git a/client/logic/lib/impl/LuaConfigImpl.cpp b/src/client/impl/logic/LuaConfigImpl.cpp similarity index 76% rename from client/logic/lib/impl/LuaConfigImpl.cpp rename to src/client/impl/logic/LuaConfigImpl.cpp index 291cc7986..71997b44b 100644 --- a/client/logic/lib/impl/LuaConfigImpl.cpp +++ b/src/client/impl/logic/LuaConfigImpl.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LuaConfigImpl.h" -#include "impl/LoggerImpl.h" +#include "impl/logic/LuaConfigImpl.h" +#include "internal/Core/Utils/LogMacros.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/SolState.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "internal/logic/SolState.h" namespace ramses::internal { @@ -18,13 +18,13 @@ namespace ramses::internal { if (!LuaCompilationUtils::CheckModuleName(aliasName)) { - LOG_ERROR("Failed to add dependency '{}'! The alias name should be a valid Lua label.", aliasName); + LOG_ERROR_P(CONTEXT_CLIENT, "Failed to add dependency '{}'! The alias name should be a valid Lua label.", aliasName); return false; } if (SolState::IsReservedModuleName(aliasName)) { - LOG_ERROR("Failed to add dependency '{}'! The alias collides with a standard library name!", aliasName); + LOG_ERROR_P(CONTEXT_CLIENT, "Failed to add dependency '{}'! The alias collides with a standard library name!", aliasName); return false; } @@ -32,7 +32,7 @@ namespace ramses::internal if (m_modulesMapping.cend() != m_modulesMapping.find(aliasNameStr)) { - LOG_ERROR("Module dependencies must be uniquely aliased! Alias '{}' is already used!", aliasName); + LOG_ERROR_P(CONTEXT_CLIENT, "Module dependencies must be uniquely aliased! Alias '{}' is already used!", aliasName); return false; } @@ -49,7 +49,7 @@ namespace ramses::internal { if (std::find(m_stdModules.cbegin(), m_stdModules.cend(), stdModule) != m_stdModules.cend()) { - LOG_ERROR("Standard module {} already added, can't add twice!", stdModule); + LOG_ERROR_P(CONTEXT_CLIENT, "Standard module {} already added, can't add twice!", stdModule); return false; } diff --git a/client/logic/lib/impl/LuaConfigImpl.h b/src/client/impl/logic/LuaConfigImpl.h similarity index 96% rename from client/logic/lib/impl/LuaConfigImpl.h rename to src/client/impl/logic/LuaConfigImpl.h index a6ec0ee12..e01b188a7 100644 --- a/client/logic/lib/impl/LuaConfigImpl.h +++ b/src/client/impl/logic/LuaConfigImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/EStandardModule.h" +#include "ramses/client/logic/EStandardModule.h" #include #include diff --git a/client/logic/lib/impl/LuaInterface.cpp b/src/client/impl/logic/LuaInterface.cpp similarity index 73% rename from client/logic/lib/impl/LuaInterface.cpp rename to src/client/impl/logic/LuaInterface.cpp index efa36438b..c4a56bc29 100644 --- a/client/logic/lib/impl/LuaInterface.cpp +++ b/src/client/impl/logic/LuaInterface.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LuaInterface.h" -#include "impl/LuaInterfaceImpl.h" +#include "ramses/client/logic/LuaInterface.h" +#include "impl/logic/LuaInterfaceImpl.h" namespace ramses { @@ -17,4 +17,14 @@ namespace ramses , m_interface{ static_cast(LogicNode::m_impl) } { } + + internal::LuaInterfaceImpl& LuaInterface::impl() + { + return m_interface; + } + + const internal::LuaInterfaceImpl& LuaInterface::impl() const + { + return m_interface; + } } diff --git a/client/logic/lib/impl/LuaInterfaceImpl.cpp b/src/client/impl/logic/LuaInterfaceImpl.cpp similarity index 79% rename from client/logic/lib/impl/LuaInterfaceImpl.cpp rename to src/client/impl/logic/LuaInterfaceImpl.cpp index dc5b3ec81..45ff82527 100644 --- a/client/logic/lib/impl/LuaInterfaceImpl.cpp +++ b/src/client/impl/logic/LuaInterfaceImpl.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LuaInterfaceImpl.h" +#include "impl/logic/LuaInterfaceImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/TypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/TypeUtils.h" -#include "generated/LuaInterfaceGen.h" +#include "internal/logic/flatbuffers/generated/LuaInterfaceGen.h" namespace ramses::internal { - LuaInterfaceImpl::LuaInterfaceImpl(LuaCompiledInterface compiledInterface, std::string_view name, uint64_t id) - : LogicNodeImpl(name, id) + LuaInterfaceImpl::LuaInterfaceImpl(SceneImpl& scene, LuaCompiledInterface compiledInterface, std::string_view name, sceneObjectId_t id) + : LogicNodeImpl{ scene, name, id } { setRootProperties(std::move(compiledInterface.rootProperty), nullptr); } @@ -27,7 +27,7 @@ namespace ramses::internal SerializationMap& serializationMap) { const auto logicObject = LogicObjectImpl::Serialize(luaInterface, builder); - const auto propertyObject = PropertyImpl::Serialize(*luaInterface.getInputs()->m_impl, builder, serializationMap); + const auto propertyObject = PropertyImpl::Serialize(luaInterface.getInputs()->impl(), builder, serializationMap); auto intf = rlogic_serialization::CreateLuaInterface(builder, logicObject, propertyObject); builder.Finish(intf); @@ -40,24 +40,24 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(luaInterface.base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of LuaInterface from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaInterface from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (name.length() == 0) { - errorReporting.add("Fatal error during loading of LuaInterface from serialized data: empty name!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaInterface from serialized data: empty name!", nullptr); return nullptr; } if (!luaInterface.rootProperty()) { - errorReporting.add("Fatal error during loading of LuaInterface from serialized data: missing root property!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaInterface from serialized data: missing root property!", nullptr); return nullptr; } @@ -69,11 +69,12 @@ namespace ramses::internal if (rootProperty->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: root property has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: root property has unexpected type!", nullptr); return nullptr; } auto deserialized = std::make_unique( + deserializationMap.getScene(), LuaCompiledInterface{ std::move(rootProperty) }, name, id); deserialized->setUserId(userIdHigh, userIdLow); @@ -83,7 +84,7 @@ namespace ramses::internal std::vector LuaInterfaceImpl::collectUnlinkedProperties() const { - std::vector outputProperties = getOutputs()->m_impl->collectLeafChildren(); + std::vector outputProperties = getOutputs()->impl().collectLeafChildren(); // filter for unlinked properties by removing linked ones auto it = std::remove_if(outputProperties.begin(), outputProperties.end(), [](const auto* node) { return node->isLinked(); }); diff --git a/client/logic/lib/impl/LuaInterfaceImpl.h b/src/client/impl/logic/LuaInterfaceImpl.h similarity index 80% rename from client/logic/lib/impl/LuaInterfaceImpl.h rename to src/client/impl/logic/LuaInterfaceImpl.h index 40bc7667a..eb8a8c423 100644 --- a/client/logic/lib/impl/LuaInterfaceImpl.h +++ b/src/client/impl/logic/LuaInterfaceImpl.h @@ -8,15 +8,15 @@ #pragma once -#include "impl/LogicNodeImpl.h" +#include "impl/logic/LogicNodeImpl.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" -#include "internals/WrappedLuaProperty.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/WrappedLuaProperty.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LuaInterface.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LuaInterface.h" #include #include @@ -42,7 +42,7 @@ namespace ramses::internal class LuaInterfaceImpl : public LogicNodeImpl { public: - explicit LuaInterfaceImpl(LuaCompiledInterface compiledInterface, std::string_view name, uint64_t id); + explicit LuaInterfaceImpl(SceneImpl& scene, LuaCompiledInterface compiledInterface, std::string_view name, sceneObjectId_t id); ~LuaInterfaceImpl() noexcept override = default; LuaInterfaceImpl(const LuaInterfaceImpl & other) = delete; LuaInterfaceImpl& operator=(const LuaInterfaceImpl & other) = delete; diff --git a/client/logic/lib/impl/LuaModule.cpp b/src/client/impl/logic/LuaModule.cpp similarity index 67% rename from client/logic/lib/impl/LuaModule.cpp rename to src/client/impl/logic/LuaModule.cpp index 56c1b46f1..8dadb026f 100644 --- a/client/logic/lib/impl/LuaModule.cpp +++ b/src/client/impl/logic/LuaModule.cpp @@ -6,15 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LuaModule.h" -#include "impl/LuaModuleImpl.h" +#include "ramses/client/logic/LuaModule.h" +#include "impl/logic/LuaModuleImpl.h" namespace ramses { LuaModule::LuaModule(std::unique_ptr impl) noexcept : LogicObject(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_impl{ static_cast(*LogicObject::m_impl) } + , m_impl{ static_cast(LogicObject::m_impl) } { } + + internal::LuaModuleImpl& LuaModule::impl() + { + return m_impl; + } + + const internal::LuaModuleImpl& LuaModule::impl() const + { + return m_impl; + } } diff --git a/client/logic/lib/impl/LuaModuleImpl.cpp b/src/client/impl/logic/LuaModuleImpl.cpp similarity index 81% rename from client/logic/lib/impl/LuaModuleImpl.cpp rename to src/client/impl/logic/LuaModuleImpl.cpp index 3ceb0f6b3..41fa463aa 100644 --- a/client/logic/lib/impl/LuaModuleImpl.cpp +++ b/src/client/impl/logic/LuaModuleImpl.cpp @@ -6,25 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LuaModuleImpl.h" +#include "impl/logic/LuaModuleImpl.h" -#include "ramses-logic/LuaModule.h" +#include "ramses/client/logic/LuaModule.h" -#include "internals/LuaCompilationUtils.h" -#include "generated/LuaModuleGen.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "internal/logic/flatbuffers/generated/LuaModuleGen.h" #include "flatbuffers/flatbuffers.h" -#include "internals/SolState.h" -#include "internals/ErrorReporting.h" -#include "internals/DeserializationMap.h" -#include "internals/SerializationMap.h" -#include "internals/EnvironmentProtection.h" -#include "internals/PropertyTypeExtractor.h" +#include "internal/logic/SolState.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/EnvironmentProtection.h" +#include "internal/logic/PropertyTypeExtractor.h" #include namespace ramses::internal { - LuaModuleImpl::LuaModuleImpl(LuaCompiledModule module, std::string_view name, uint64_t id) - : LogicObjectImpl(name, id) + LuaModuleImpl::LuaModuleImpl(SceneImpl& scene, LuaCompiledModule module, std::string_view name, sceneObjectId_t id) + : LogicObjectImpl{ scene, name, id } , m_sourceCode{ std::move(module.source.sourceCode) } , m_byteCode{ std::move(module.source.byteCode) } , m_module{ std::move(module.moduleTable) } @@ -56,7 +56,7 @@ namespace ramses::internal modulesFB.push_back( rlogic_serialization::CreateLuaModuleUsage(builder, builder.CreateString(dependency.first), - dependency.second->getId())); + dependency.second->getSceneObjectId().getValue())); } std::vector stdModules; @@ -110,12 +110,12 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(module.base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of LuaModule from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaModule from serialized data: missing name and/or ID!", nullptr); return nullptr; } @@ -123,13 +123,13 @@ namespace ramses::internal const bool hasBytecode = (module.luaByteCode() != nullptr && module.luaByteCode()->size() > 0); if (!hasSourceCode && !hasBytecode) { - errorReporting.add("Fatal error during loading of LuaModule from serialized data: has neither Lua source code nor bytecode!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaModule from serialized data: has neither Lua source code nor bytecode!", nullptr); return nullptr; } if (!module.dependencies()) { - errorReporting.add("Fatal error during loading of LuaModule from serialized data: missing dependencies!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaModule from serialized data: missing dependencies!", nullptr); return nullptr; } @@ -146,13 +146,13 @@ namespace ramses::internal { if (!mod->name()) { - errorReporting.add(fmt::format("Fatal error during loading of LuaModule '{}' module data: missing name!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of LuaModule '{}' module data: missing name!", name), nullptr); return nullptr; } - const auto* moduleUsed = deserializationMap.resolveLogicObject(mod->moduleId()); + const auto* moduleUsed = deserializationMap.resolveLogicObject(sceneObjectId_t{ mod->moduleId() }); if (!moduleUsed) { - errorReporting.add(fmt::format("Fatal error during loading of LuaModule '{}' module data: could not resolve dependent module with id={}!", name, mod->moduleId()), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of LuaModule '{}' module data: could not resolve dependent module with id={}!", name, mod->moduleId()), nullptr); return nullptr; } @@ -168,14 +168,11 @@ namespace ramses::internal } auto compiledModule = LuaCompilationUtils::CompileModuleOrImportPrecompiled(solState, modulesUsed, stdModules, std::move(source), name, errorReporting, std::move(byteCode), false); - if (!compiledModule) - { - errorReporting.add(fmt::format("Fatal error during loading of LuaModule '{}' from serialized data!", name), nullptr, EErrorType::BinaryVersionMismatch); return nullptr; - } auto deserialized = std::make_unique( + deserializationMap.getScene(), std::move(*compiledModule), name, id); diff --git a/client/logic/lib/impl/LuaModuleImpl.h b/src/client/impl/logic/LuaModuleImpl.h similarity index 86% rename from client/logic/lib/impl/LuaModuleImpl.h rename to src/client/impl/logic/LuaModuleImpl.h index ffe2f4810..596ce76fb 100644 --- a/client/logic/lib/impl/LuaModuleImpl.h +++ b/src/client/impl/logic/LuaModuleImpl.h @@ -8,10 +8,10 @@ #pragma once -#include "impl/LogicObjectImpl.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/SolWrapper.h" -#include "ramses-logic/ELuaSavingMode.h" +#include "impl/logic/LogicObjectImpl.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "internal/logic/SolWrapper.h" +#include "ramses/client/logic/ELuaSavingMode.h" #include namespace rlogic_serialization @@ -40,7 +40,7 @@ namespace ramses::internal class LuaModuleImpl : public LogicObjectImpl { public: - LuaModuleImpl(LuaCompiledModule module, std::string_view name, uint64_t id); + LuaModuleImpl(SceneImpl& scene, LuaCompiledModule module, std::string_view name, sceneObjectId_t id); [[nodiscard]] const sol::table& getModule() const; [[nodiscard]] const ModuleMapping& getDependencies() const; diff --git a/client/logic/lib/impl/LuaScript.cpp b/src/client/impl/logic/LuaScript.cpp similarity index 74% rename from client/logic/lib/impl/LuaScript.cpp rename to src/client/impl/logic/LuaScript.cpp index f88e2f5a3..8b7523fd4 100644 --- a/client/logic/lib/impl/LuaScript.cpp +++ b/src/client/impl/logic/LuaScript.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LuaScript.h" -#include "impl/LuaScriptImpl.h" +#include "ramses/client/logic/LuaScript.h" +#include "impl/logic/LuaScriptImpl.h" namespace ramses { @@ -17,4 +17,14 @@ namespace ramses , m_script{ static_cast(LogicNode::m_impl) } { } + + internal::LuaScriptImpl& LuaScript::impl() + { + return m_script; + } + + const internal::LuaScriptImpl& LuaScript::impl() const + { + return m_script; + } } diff --git a/client/logic/lib/impl/LuaScriptImpl.cpp b/src/client/impl/logic/LuaScriptImpl.cpp similarity index 79% rename from client/logic/lib/impl/LuaScriptImpl.cpp rename to src/client/impl/logic/LuaScriptImpl.cpp index 7df68aa84..340908772 100644 --- a/client/logic/lib/impl/LuaScriptImpl.cpp +++ b/src/client/impl/logic/LuaScriptImpl.cpp @@ -6,28 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/LuaScriptImpl.h" +#include "impl/logic/LuaScriptImpl.h" -#include "ramses-logic/LuaModule.h" -#include "internals/SolState.h" -#include "impl/PropertyImpl.h" -#include "impl/LuaModuleImpl.h" +#include "ramses/client/logic/LuaModule.h" +#include "internal/logic/SolState.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LuaModuleImpl.h" -#include "internals/WrappedLuaProperty.h" -#include "internals/SolHelper.h" -#include "internals/ErrorReporting.h" -#include "internals/PropertyTypeExtractor.h" -#include "internals/EnvironmentProtection.h" -#include "internals/SerializationMap.h" +#include "internal/logic/WrappedLuaProperty.h" +#include "internal/logic/SolHelper.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/PropertyTypeExtractor.h" +#include "internal/logic/EnvironmentProtection.h" +#include "internal/logic/SerializationMap.h" -#include "generated/LuaScriptGen.h" +#include "internal/logic/flatbuffers/generated/LuaScriptGen.h" #include namespace ramses::internal { - LuaScriptImpl::LuaScriptImpl(LuaCompiledScript compiledScript, std::string_view name, uint64_t id) - : LogicNodeImpl(name, id) + LuaScriptImpl::LuaScriptImpl(SceneImpl& scene, LuaCompiledScript compiledScript, std::string_view name, sceneObjectId_t id) + : LogicNodeImpl{ scene, name, id } , m_source(std::move(compiledScript.source.sourceCode)) , m_byteCode(std::move(compiledScript.source.byteCode)) , m_wrappedRootInput(*compiledScript.rootInput) @@ -61,7 +61,7 @@ namespace ramses::internal userModules.push_back( rlogic_serialization::CreateLuaModuleUsage(builder, builder.CreateString(module.first), - module.second->getId())); + module.second->getSceneObjectId().getValue())); } std::vector stdModules; @@ -74,8 +74,8 @@ namespace ramses::internal const auto fbLogicObject = LogicObjectImpl::Serialize(luaScript, builder); const auto fbModulesVec = builder.CreateVector(userModules); const auto fbStdModulesVec = builder.CreateVector(stdModules); - const auto fbInputPropertyObject = PropertyImpl::Serialize(*luaScript.getInputs()->m_impl, builder, serializationMap); - const auto fbOuputPropertyObject = PropertyImpl::Serialize(*luaScript.getOutputs()->m_impl, builder, serializationMap); + const auto fbInputPropertyObject = PropertyImpl::Serialize(luaScript.getInputs()->impl(), builder, serializationMap); + const auto fbOuputPropertyObject = PropertyImpl::Serialize(luaScript.getOutputs()->impl(), builder, serializationMap); const bool hasSourceCode = !luaScript.m_source.empty(); const bool hasByteCode = !luaScript.m_byteCode.empty(); @@ -123,12 +123,12 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(luaScript.base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: missing name and/or ID!", nullptr); return nullptr; } @@ -136,13 +136,13 @@ namespace ramses::internal const bool hasBytecode = (luaScript.luaByteCode() != nullptr && luaScript.luaByteCode()->size() > 0); if (!hasSourceCode && !hasBytecode) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: has neither Lua source code nor bytecode!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: has neither Lua source code nor bytecode!", nullptr); return nullptr; } if (!luaScript.rootInput()) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: missing root input!", nullptr); return nullptr; } @@ -154,7 +154,7 @@ namespace ramses::internal if (!luaScript.rootOutput()) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: missing root output!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: missing root output!", nullptr); return nullptr; } @@ -166,19 +166,19 @@ namespace ramses::internal if (rootInput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: root input has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: root input has unexpected type!", nullptr); return nullptr; } if (rootOutput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: root output has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: root output has unexpected type!", nullptr); return nullptr; } if (!luaScript.userModules()) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: missing user module dependencies!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: missing user module dependencies!", nullptr); return nullptr; } ModuleMapping userModules; @@ -187,13 +187,13 @@ namespace ramses::internal { if (!module->name()) { - errorReporting.add(fmt::format("Fatal error during loading of LuaScript '{}' module data: missing name!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of LuaScript '{}' module data: missing name!", name), nullptr); return nullptr; } - const auto* moduleUsed = deserializationMap.resolveLogicObject(module->moduleId()); + const auto* moduleUsed = deserializationMap.resolveLogicObject(sceneObjectId_t{ module->moduleId() }); if (!moduleUsed) { - errorReporting.add(fmt::format("Fatal error during loading of LuaScript '{}' module data: could not resolve dependent module with id={}!", name, module->moduleId()), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of LuaScript '{}' module data: could not resolve dependent module with id={}!", name, module->moduleId()), nullptr); return nullptr; } @@ -202,7 +202,7 @@ namespace ramses::internal if (!luaScript.standardModules()) { - errorReporting.add("Fatal error during loading of LuaScript from serialized data: missing standard module dependencies!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of LuaScript from serialized data: missing standard module dependencies!", nullptr); return nullptr; } StandardModules stdModules; @@ -231,12 +231,10 @@ namespace ramses::internal false); if (!compiledScript) - { - errorReporting.add(fmt::format("Fatal error during loading of LuaScript '{}' from serialized data!", name), nullptr, EErrorType::BinaryVersionMismatch); return nullptr; - } auto deserialized = std::make_unique( + deserializationMap.getScene(), std::move(*compiledScript), name, id); diff --git a/client/logic/lib/impl/LuaScriptImpl.h b/src/client/impl/logic/LuaScriptImpl.h similarity index 83% rename from client/logic/lib/impl/LuaScriptImpl.h rename to src/client/impl/logic/LuaScriptImpl.h index 4ff1a26a5..3a42c6e67 100644 --- a/client/logic/lib/impl/LuaScriptImpl.h +++ b/src/client/impl/logic/LuaScriptImpl.h @@ -8,15 +8,15 @@ #pragma once -#include "impl/LogicNodeImpl.h" +#include "impl/logic/LogicNodeImpl.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/DeserializationMap.h" -#include "internals/WrappedLuaProperty.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/WrappedLuaProperty.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/ELuaSavingMode.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/ELuaSavingMode.h" #include #include @@ -48,7 +48,7 @@ namespace ramses::internal class LuaScriptImpl : public LogicNodeImpl { public: - explicit LuaScriptImpl(LuaCompiledScript compiledScript, std::string_view name, uint64_t id); + explicit LuaScriptImpl(SceneImpl& scene, LuaCompiledScript compiledScript, std::string_view name, sceneObjectId_t id); ~LuaScriptImpl() noexcept override = default; LuaScriptImpl(const LuaScriptImpl & other) = delete; LuaScriptImpl& operator=(const LuaScriptImpl & other) = delete; diff --git a/client/logic/lib/impl/RamsesMeshNodeBinding.cpp b/src/client/impl/logic/MeshNodeBinding.cpp similarity index 54% rename from client/logic/lib/impl/RamsesMeshNodeBinding.cpp rename to src/client/impl/logic/MeshNodeBinding.cpp index 4d814ddff..3e4a15615 100644 --- a/client/logic/lib/impl/RamsesMeshNodeBinding.cpp +++ b/src/client/impl/logic/MeshNodeBinding.cpp @@ -6,25 +6,35 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "impl/RamsesMeshNodeBindingImpl.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "impl/logic/MeshNodeBindingImpl.h" namespace ramses { - RamsesMeshNodeBinding::RamsesMeshNodeBinding(std::unique_ptr impl) noexcept + MeshNodeBinding::MeshNodeBinding(std::unique_ptr impl) noexcept : RamsesBinding(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_meshNodeBinding{ static_cast(RamsesBinding::m_impl) } + , m_meshNodeBinding{ static_cast(RamsesBinding::m_impl) } { } - const ramses::MeshNode& RamsesMeshNodeBinding::getRamsesMeshNode() const + const ramses::MeshNode& MeshNodeBinding::getRamsesMeshNode() const { return m_meshNodeBinding.getRamsesMeshNode(); } - ramses::MeshNode& RamsesMeshNodeBinding::getRamsesMeshNode() + ramses::MeshNode& MeshNodeBinding::getRamsesMeshNode() { return m_meshNodeBinding.getRamsesMeshNode(); } + + internal::MeshNodeBindingImpl& MeshNodeBinding::impl() + { + return m_meshNodeBinding; + } + + const internal::MeshNodeBindingImpl& MeshNodeBinding::impl() const + { + return m_meshNodeBinding; + } } diff --git a/client/logic/lib/impl/RamsesMeshNodeBindingImpl.cpp b/src/client/impl/logic/MeshNodeBindingImpl.cpp similarity index 51% rename from client/logic/lib/impl/RamsesMeshNodeBindingImpl.cpp rename to src/client/impl/logic/MeshNodeBindingImpl.cpp index 7ba844d4e..d35c1201f 100644 --- a/client/logic/lib/impl/RamsesMeshNodeBindingImpl.cpp +++ b/src/client/impl/logic/MeshNodeBindingImpl.cpp @@ -6,25 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesMeshNodeBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-logic/Property.h" -#include "ramses-utils.h" -#include "internals/ErrorReporting.h" -#include "internals/RamsesObjectResolver.h" -#include "generated/RamsesMeshNodeBindingGen.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-utils.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesObjectResolver.h" +#include "internal/logic/flatbuffers/generated/MeshNodeBindingGen.h" #include "fmt/format.h" namespace ramses::internal { - RamsesMeshNodeBindingImpl::RamsesMeshNodeBindingImpl(ramses::MeshNode& ramsesMeshNode, std::string_view name, uint64_t id) - : RamsesBindingImpl{ name, id } + MeshNodeBindingImpl::MeshNodeBindingImpl(SceneImpl& scene, ramses::MeshNode& ramsesMeshNode, std::string_view name, sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_ramsesMeshNode{ ramsesMeshNode } { } - void RamsesMeshNodeBindingImpl::createRootProperties() + void MeshNodeBindingImpl::createRootProperties() { HierarchicalTypeData inputsType = MakeStruct("", { TypeData{"vertexOffset", EPropertyType::Int32}, //EInputProperty::VertexOffset @@ -39,50 +39,50 @@ namespace ramses::internal ApplyRamsesValuesToInputProperties(*this, m_ramsesMeshNode); } - flatbuffers::Offset RamsesMeshNodeBindingImpl::Serialize( - const RamsesMeshNodeBindingImpl& meshNodeBinding, + flatbuffers::Offset MeshNodeBindingImpl::Serialize( + const MeshNodeBindingImpl& meshNodeBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap) { const auto logicObject = LogicObjectImpl::Serialize(meshNodeBinding, builder); const auto fbRamsesRef = RamsesBindingImpl::SerializeRamsesReference(meshNodeBinding.m_ramsesMeshNode, builder); - const auto propertyObject = PropertyImpl::Serialize(*meshNodeBinding.getInputs()->m_impl, builder, serializationMap); + const auto propertyObject = PropertyImpl::Serialize(meshNodeBinding.getInputs()->impl(), builder, serializationMap); auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(builder, logicObject, fbRamsesRef, propertyObject); - auto fbMeshNodeBinding = rlogic_serialization::CreateRamsesMeshNodeBinding(builder, fbRamsesBinding); + auto fbMeshNodeBinding = rlogic_serialization::CreateMeshNodeBinding(builder, fbRamsesBinding); builder.Finish(fbMeshNodeBinding); return fbMeshNodeBinding; } - std::unique_ptr RamsesMeshNodeBindingImpl::Deserialize( - const rlogic_serialization::RamsesMeshNodeBinding& meshNodeBinding, + std::unique_ptr MeshNodeBindingImpl::Deserialize( + const rlogic_serialization::MeshNodeBinding& meshNodeBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { if (!meshNodeBinding.base()) { - errorReporting.add("Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing base class info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of MeshNodeBinding from serialized data: missing base class info!", nullptr); return nullptr; } std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(meshNodeBinding.base()->base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of MeshNodeBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!meshNodeBinding.base()->rootInput()) { - errorReporting.add("Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of MeshNodeBinding from serialized data: missing root input!", nullptr); return nullptr; } @@ -97,14 +97,14 @@ namespace ramses::internal deserializedRootInput->getChild(size_t(EInputProperty::IndexCount))->getName() != "indexCount" || deserializedRootInput->getChild(size_t(EInputProperty::InstanceCount))->getName() != "instanceCount") { - errorReporting.add("Fatal error during loading of RamsesMeshNodeBinding from serialized data: corrupted root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of MeshNodeBinding from serialized data: corrupted root input!", nullptr); return nullptr; } const auto* boundObject = meshNodeBinding.base()->boundRamsesObject(); if (!boundObject) { - errorReporting.add("Fatal error during loading of RamsesMeshNodeBinding from serialized data: missing ramses object reference!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of MeshNodeBinding from serialized data: missing ramses object reference!", nullptr); return nullptr; } @@ -115,13 +115,13 @@ namespace ramses::internal if (ramsesObject->getType() != ramses::ERamsesObjectType::MeshNode || ramsesObject->getType() != static_cast(boundObject->objectType())) { - errorReporting.add("Fatal error during loading of RamsesMeshNodeBinding from serialized data: loaded object type does not match referenced object type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of MeshNodeBinding from serialized data: loaded object type does not match referenced object type!", nullptr); return nullptr; } - auto* ramsesMeshNode = ramses::RamsesUtils::TryConvert(*ramsesObject); + auto* ramsesMeshNode = ramsesObject->as(); assert(ramsesMeshNode); - auto binding = std::make_unique(*ramsesMeshNode, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *ramsesMeshNode, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs(std::move(deserializedRootInput)); @@ -130,60 +130,72 @@ namespace ramses::internal return binding; } - std::optional RamsesMeshNodeBindingImpl::update() + std::optional MeshNodeBindingImpl::update() { - auto prop = getInputs()->getChild(size_t(EInputProperty::VertexOffset))->m_impl.get(); + auto prop = &getInputs()->getChild(size_t(EInputProperty::VertexOffset))->impl(); if (prop->checkForBindingInputNewValueAndReset()) { - const auto status = m_ramsesMeshNode.get().setStartVertex(prop->getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesMeshNode.get().getStatusMessage(status) }; + const auto vtxOffset = prop->getValueAs(); + if (vtxOffset < 0) + return LogicNodeRuntimeError{ "MeshNodeBinding vertex offset cannot be negative" }; + + if (!m_ramsesMeshNode.get().setStartVertex(static_cast(vtxOffset))) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - prop = getInputs()->getChild(size_t(EInputProperty::IndexOffset))->m_impl.get(); + prop = &getInputs()->getChild(size_t(EInputProperty::IndexOffset))->impl(); if (prop->checkForBindingInputNewValueAndReset()) { - const auto status = m_ramsesMeshNode.get().setStartIndex(prop->getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesMeshNode.get().getStatusMessage(status) }; + const auto idxOffset = prop->getValueAs(); + if (idxOffset < 0) + return LogicNodeRuntimeError{ "MeshNodeBinding index offset cannot be negative" }; + + if (!m_ramsesMeshNode.get().setStartIndex(static_cast(idxOffset))) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - prop = getInputs()->getChild(size_t(EInputProperty::IndexCount))->m_impl.get(); + prop = &getInputs()->getChild(size_t(EInputProperty::IndexCount))->impl(); if (prop->checkForBindingInputNewValueAndReset()) { - const auto status = m_ramsesMeshNode.get().setIndexCount(prop->getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesMeshNode.get().getStatusMessage(status) }; + const auto idxCount = prop->getValueAs(); + if (idxCount < 0) + return LogicNodeRuntimeError{ "MeshNodeBinding index count cannot be negative" }; + + if (!m_ramsesMeshNode.get().setIndexCount(static_cast(idxCount))) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - prop = getInputs()->getChild(size_t(EInputProperty::InstanceCount))->m_impl.get(); + prop = &getInputs()->getChild(size_t(EInputProperty::InstanceCount))->impl(); if (prop->checkForBindingInputNewValueAndReset()) { - const auto status = m_ramsesMeshNode.get().setInstanceCount(prop->getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesMeshNode.get().getStatusMessage(status) }; + const auto instCount = prop->getValueAs(); + if (instCount < 0) + return LogicNodeRuntimeError{ "MeshNodeBinding instance count cannot be negative" }; + + if (!m_ramsesMeshNode.get().setInstanceCount(static_cast(instCount))) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } return std::nullopt; } - const ramses::MeshNode& RamsesMeshNodeBindingImpl::getRamsesMeshNode() const + const ramses::MeshNode& MeshNodeBindingImpl::getRamsesMeshNode() const { return m_ramsesMeshNode; } - ramses::MeshNode& RamsesMeshNodeBindingImpl::getRamsesMeshNode() + ramses::MeshNode& MeshNodeBindingImpl::getRamsesMeshNode() { return m_ramsesMeshNode; } - // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at initialization, + // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at creation or deserialization, // should not overwrite values unless set() or link explicitly called - void RamsesMeshNodeBindingImpl::ApplyRamsesValuesToInputProperties(RamsesMeshNodeBindingImpl& binding, ramses::MeshNode& ramsesMeshNode) + void MeshNodeBindingImpl::ApplyRamsesValuesToInputProperties(MeshNodeBindingImpl& binding, ramses::MeshNode& ramsesMeshNode) { - binding.getInputs()->getChild(size_t(EInputProperty::VertexOffset))->m_impl->initializeBindingInputValue(static_cast(ramsesMeshNode.getStartVertex())); - binding.getInputs()->getChild(size_t(EInputProperty::IndexOffset))->m_impl->initializeBindingInputValue(static_cast(ramsesMeshNode.getStartIndex())); - binding.getInputs()->getChild(size_t(EInputProperty::IndexCount))->m_impl->initializeBindingInputValue(static_cast(ramsesMeshNode.getIndexCount())); - binding.getInputs()->getChild(size_t(EInputProperty::InstanceCount))->m_impl->initializeBindingInputValue(static_cast(ramsesMeshNode.getInstanceCount())); + binding.getInputs()->getChild(size_t(EInputProperty::VertexOffset))->impl().initializeBindingInputValue(static_cast(ramsesMeshNode.getStartVertex())); + binding.getInputs()->getChild(size_t(EInputProperty::IndexOffset))->impl().initializeBindingInputValue(static_cast(ramsesMeshNode.getStartIndex())); + binding.getInputs()->getChild(size_t(EInputProperty::IndexCount))->impl().initializeBindingInputValue(static_cast(ramsesMeshNode.getIndexCount())); + binding.getInputs()->getChild(size_t(EInputProperty::InstanceCount))->impl().initializeBindingInputValue(static_cast(ramsesMeshNode.getInstanceCount())); } } diff --git a/client/logic/lib/impl/RamsesMeshNodeBindingImpl.h b/src/client/impl/logic/MeshNodeBindingImpl.h similarity index 66% rename from client/logic/lib/impl/RamsesMeshNodeBindingImpl.h rename to src/client/impl/logic/MeshNodeBindingImpl.h index 541aff979..9e3096958 100644 --- a/client/logic/lib/impl/RamsesMeshNodeBindingImpl.h +++ b/src/client/impl/logic/MeshNodeBindingImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "impl/RamsesBindingImpl.h" +#include "impl/logic/RamsesBindingImpl.h" #include namespace ramses @@ -18,7 +18,7 @@ namespace ramses namespace rlogic_serialization { - struct RamsesMeshNodeBinding; + struct MeshNodeBinding; } namespace flatbuffers @@ -34,22 +34,20 @@ namespace ramses::internal class SerializationMap; class DeserializationMap; - class RamsesMeshNodeBindingImpl : public RamsesBindingImpl + class MeshNodeBindingImpl : public RamsesBindingImpl { public: // Move-able (noexcept); Not copy-able - explicit RamsesMeshNodeBindingImpl(ramses::MeshNode& ramsesMeshNode, std::string_view name, uint64_t id); - ~RamsesMeshNodeBindingImpl() noexcept override = default; - RamsesMeshNodeBindingImpl(const RamsesMeshNodeBindingImpl& other) = delete; - RamsesMeshNodeBindingImpl& operator=(const RamsesMeshNodeBindingImpl& other) = delete; + explicit MeshNodeBindingImpl(SceneImpl& scene, ramses::MeshNode& ramsesMeshNode, std::string_view name, sceneObjectId_t id); + ~MeshNodeBindingImpl() noexcept override = default; - [[nodiscard]] static flatbuffers::Offset Serialize( - const RamsesMeshNodeBindingImpl& meshNodeBinding, + [[nodiscard]] static flatbuffers::Offset Serialize( + const MeshNodeBindingImpl& meshNodeBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap); - [[nodiscard]] static std::unique_ptr Deserialize( - const rlogic_serialization::RamsesMeshNodeBinding& meshNodeBinding, + [[nodiscard]] static std::unique_ptr Deserialize( + const rlogic_serialization::MeshNodeBinding& meshNodeBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap); @@ -72,7 +70,7 @@ namespace ramses::internal }; private: - static void ApplyRamsesValuesToInputProperties(RamsesMeshNodeBindingImpl& binding, ramses::MeshNode& ramsesMeshNode); + static void ApplyRamsesValuesToInputProperties(MeshNodeBindingImpl& binding, ramses::MeshNode& ramsesMeshNode); std::reference_wrapper m_ramsesMeshNode; }; diff --git a/client/logic/lib/impl/RamsesNodeBinding.cpp b/src/client/impl/logic/NodeBinding.cpp similarity index 56% rename from client/logic/lib/impl/RamsesNodeBinding.cpp rename to src/client/impl/logic/NodeBinding.cpp index 8696819c1..f41b23e84 100644 --- a/client/logic/lib/impl/RamsesNodeBinding.cpp +++ b/src/client/impl/logic/NodeBinding.cpp @@ -6,26 +6,36 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesNodeBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" -#include "ramses-logic/RamsesNodeBinding.h" +#include "ramses/client/logic/NodeBinding.h" namespace ramses { - RamsesNodeBinding::RamsesNodeBinding(std::unique_ptr impl) noexcept + NodeBinding::NodeBinding(std::unique_ptr impl) noexcept : RamsesBinding(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_nodeBinding{ static_cast(RamsesBinding::m_impl) } + , m_nodeBinding{ static_cast(RamsesBinding::m_impl) } { } - ramses::Node& RamsesNodeBinding::getRamsesNode() const + ramses::Node& NodeBinding::getRamsesNode() const { return m_nodeBinding.getRamsesNode(); } - ramses::ERotationType RamsesNodeBinding::getRotationType() const + ramses::ERotationType NodeBinding::getRotationType() const { return m_nodeBinding.getRotationType(); } + + internal::NodeBindingImpl& NodeBinding::impl() + { + return m_nodeBinding; + } + + const internal::NodeBindingImpl& NodeBinding::impl() const + { + return m_nodeBinding; + } } diff --git a/client/logic/lib/impl/RamsesNodeBindingImpl.cpp b/src/client/impl/logic/NodeBindingImpl.cpp similarity index 59% rename from client/logic/lib/impl/RamsesNodeBindingImpl.cpp rename to src/client/impl/logic/NodeBindingImpl.cpp index 8aa1e5fda..4bec100ae 100644 --- a/client/logic/lib/impl/RamsesNodeBindingImpl.cpp +++ b/src/client/impl/logic/NodeBindingImpl.cpp @@ -6,31 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesNodeBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" -#include "ramses-client-api/Node.h" +#include "ramses/client/Node.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" -#include "impl/LoggerImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "internal/Core/Utils/LogMacros.h" -#include "internals/ErrorReporting.h" -#include "internals/RamsesObjectResolver.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesObjectResolver.h" -#include "generated/RamsesNodeBindingGen.h" +#include "internal/logic/flatbuffers/generated/NodeBindingGen.h" #include "glm/gtc/type_ptr.hpp" namespace ramses::internal { - RamsesNodeBindingImpl::RamsesNodeBindingImpl(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name, uint64_t id) - : RamsesBindingImpl{ name, id } + NodeBindingImpl::NodeBindingImpl(SceneImpl& scene, ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name, sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_ramsesNode{ ramsesNode } , m_rotationType{ rotationType } { } - void RamsesNodeBindingImpl::createRootProperties() + void NodeBindingImpl::createRootProperties() { // Attention! This order is important - it has to match the indices in ENodePropertyStaticIndex! auto inputsType = MakeStruct("", { @@ -47,55 +47,55 @@ namespace ramses::internal ApplyRamsesValuesToInputProperties(*this, m_ramsesNode); } - flatbuffers::Offset RamsesNodeBindingImpl::Serialize( - const RamsesNodeBindingImpl& nodeBinding, + flatbuffers::Offset NodeBindingImpl::Serialize( + const NodeBindingImpl& binding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap) { - auto ramsesReference = RamsesBindingImpl::SerializeRamsesReference(nodeBinding.m_ramsesNode, builder); + auto ramsesReference = RamsesBindingImpl::SerializeRamsesReference(binding.m_ramsesNode, builder); - const auto logicObject = LogicObjectImpl::Serialize(nodeBinding, builder); - const auto propertyObject = PropertyImpl::Serialize(*nodeBinding.getInputs()->m_impl, builder, serializationMap); + const auto logicObject = LogicObjectImpl::Serialize(binding, builder); + const auto propertyObject = PropertyImpl::Serialize(binding.getInputs()->impl(), builder, serializationMap); auto ramsesBinding = rlogic_serialization::CreateRamsesBinding(builder, logicObject, ramsesReference, propertyObject); builder.Finish(ramsesBinding); - auto ramsesNodeBinding = rlogic_serialization::CreateRamsesNodeBinding(builder, + auto nodeBinding = rlogic_serialization::CreateNodeBinding(builder, ramsesBinding, - static_cast(nodeBinding.m_rotationType) + static_cast(binding.m_rotationType) ); - builder.Finish(ramsesNodeBinding); + builder.Finish(nodeBinding); - return ramsesNodeBinding; + return nodeBinding; } - std::unique_ptr RamsesNodeBindingImpl::Deserialize( - const rlogic_serialization::RamsesNodeBinding& nodeBinding, + std::unique_ptr NodeBindingImpl::Deserialize( + const rlogic_serialization::NodeBinding& nodeBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { if (!nodeBinding.base()) { - errorReporting.add("Fatal error during loading of RamsesNodeBinding from serialized data: missing base class info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of NodeBinding from serialized data: missing base class info!", nullptr); return nullptr; } std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(nodeBinding.base()->base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of RamsesNodeBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of NodeBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!nodeBinding.base()->rootInput()) { - errorReporting.add("Fatal error during loading of RamsesNodeBinding from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of NodeBinding from serialized data: missing root input!", nullptr); return nullptr; } @@ -108,14 +108,14 @@ namespace ramses::internal if (deserializedRootInput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of RamsesNodeBinding from serialized data: root input has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of NodeBinding from serialized data: root input has unexpected type!", nullptr); return nullptr; } const auto* boundObject = nodeBinding.base()->boundRamsesObject(); if (!boundObject) { - errorReporting.add("Fatal error during loading of RamsesNodeBinding from serialized data: missing ramses object reference!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of NodeBinding from serialized data: missing ramses object reference!", nullptr); return nullptr; } @@ -130,13 +130,13 @@ namespace ramses::internal if (ramsesNode->getType() != static_cast(boundObject->objectType())) { - errorReporting.add("Fatal error during loading of RamsesNodeBinding from serialized data: loaded node type does not match referenced node type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of NodeBinding from serialized data: loaded node type does not match referenced node type!", nullptr); return nullptr; } const auto rotationType (static_cast(nodeBinding.rotationType())); - auto binding = std::make_unique(*ramsesNode, rotationType, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *ramsesNode, rotationType, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs(std::move(deserializedRootInput)); @@ -145,12 +145,10 @@ namespace ramses::internal return binding; } - std::optional RamsesNodeBindingImpl::update() + std::optional NodeBindingImpl::update() { - ramses::status_t status = ramses::StatusOK; - - PropertyImpl& visibility = *getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Visibility))->m_impl; - PropertyImpl& enabled = *getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Enabled))->m_impl; + PropertyImpl& visibility = getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Visibility))->impl(); + PropertyImpl& enabled = getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Enabled))->impl(); // Ramses uses 3-state visibility mode, transform the 2 bool properties 'visibility' and 'enabled' into 3-state const bool visibilityChanged = visibility.checkForBindingInputNewValueAndReset(); @@ -162,14 +160,14 @@ namespace ramses::internal if (!enabled.getValueAs()) visibilityMode = ramses::EVisibilityMode::Off; - status = m_ramsesNode.get().setVisibility(visibilityMode); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesNode.get().getStatusMessage(status) }; + if (!m_ramsesNode.get().setVisibility(visibilityMode)) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - PropertyImpl& rotation = *getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Rotation))->m_impl; + PropertyImpl& rotation = getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Rotation))->impl(); if (rotation.checkForBindingInputNewValueAndReset()) { + bool status = false; if (m_rotationType == ramses::ERotationType::Quaternion) { const auto& value = rotation.getValueAs(); @@ -181,67 +179,62 @@ namespace ramses::internal status = m_ramsesNode.get().setRotation(valuesEuler, m_rotationType); } - if (status != ramses::StatusOK) - { - return LogicNodeRuntimeError{m_ramsesNode.get().getStatusMessage(status)}; - } + if (!status) + return LogicNodeRuntimeError{getErrorReporting().getError()->message}; } - PropertyImpl& translation = *getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Translation))->m_impl; + PropertyImpl& translation = getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Translation))->impl(); if (translation.checkForBindingInputNewValueAndReset()) { const auto& value = translation.getValueAs(); - status = m_ramsesNode.get().setTranslation(value); - - if (status != ramses::StatusOK) - { - return LogicNodeRuntimeError{ m_ramsesNode.get().getStatusMessage(status) }; - } + if (!m_ramsesNode.get().setTranslation(value)) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - PropertyImpl& scaling = *getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Scaling))->m_impl; + PropertyImpl& scaling = getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Scaling))->impl(); if (scaling.checkForBindingInputNewValueAndReset()) { const auto& value = scaling.getValueAs(); - status = m_ramsesNode.get().setScaling(value); - - if (status != ramses::StatusOK) - { - return LogicNodeRuntimeError{ m_ramsesNode.get().getStatusMessage(status) }; - } + if (!m_ramsesNode.get().setScaling(value)) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } return std::nullopt; } - ramses::Node& RamsesNodeBindingImpl::getRamsesNode() const + ramses::Node& NodeBindingImpl::getRamsesNode() const { return m_ramsesNode; } - ramses::ERotationType RamsesNodeBindingImpl::getRotationType() const + ramses::ERotationType NodeBindingImpl::getRotationType() const { return m_rotationType; } - // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at initialization, + // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at creation or deserialization, // should not overwrite values unless set() or link explicitly called - void RamsesNodeBindingImpl::ApplyRamsesValuesToInputProperties(RamsesNodeBindingImpl& binding, ramses::Node& ramsesNode) + void NodeBindingImpl::ApplyRamsesValuesToInputProperties(NodeBindingImpl& binding, ramses::Node& ramsesNode) { // The 3-state ramses visibility mode is transformed into 2 bool properties in node binding - 'visibility' and 'enabled'. const ramses::EVisibilityMode visibilityMode = ramsesNode.getVisibility(); - const bool visible = (visibilityMode == ramses::EVisibilityMode::Visible); - binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Visibility))->m_impl->initializeBindingInputValue(PropertyValue{ visible }); const bool enabled = (visibilityMode == ramses::EVisibilityMode::Visible || visibilityMode == ramses::EVisibilityMode::Invisible); - binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Enabled))->m_impl->initializeBindingInputValue(PropertyValue{ enabled }); + binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Enabled))->impl().initializeBindingInputValue(PropertyValue{ enabled }); + // converting 3-state into 2 bools (4 states) will always be ambiguous, for the ambiguous combination (enabled=false, visibility=false/true?) + // we leave the visibility property untouched so it holds the state that was serialized to file + if (enabled) + { + const bool visible = (visibilityMode == ramses::EVisibilityMode::Visible); + binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Visibility))->impl().initializeBindingInputValue(PropertyValue{ visible }); + } vec3f translationValue; ramsesNode.getTranslation(translationValue); - binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Translation))->m_impl->initializeBindingInputValue(PropertyValue{ translationValue }); + binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Translation))->impl().initializeBindingInputValue(PropertyValue{ translationValue }); vec3f scalingValue; ramsesNode.getScaling(scalingValue); - binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Scaling))->m_impl->initializeBindingInputValue(PropertyValue{ scalingValue }); + binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Scaling))->impl().initializeBindingInputValue(PropertyValue{ scalingValue }); const ramses::ERotationType rotationType = ramsesNode.getRotationType(); if (binding.m_rotationType == ramses::ERotationType::Quaternion) @@ -261,11 +254,11 @@ namespace ramses::internal // Otherwise issue a warning if (euler[0] != 0.f || euler[1] != 0.f || euler[2] != 0.f) { - LOG_WARN("Initial rotation values for RamsesNodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type. Expected Quaternion, got Euler.", + LOG_WARN_P(CONTEXT_CLIENT, "Initial rotation values for NodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type. Expected Quaternion, got Euler.", binding.getIdentificationString()); } } - binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Rotation))->m_impl->initializeBindingInputValue(rotationValue); + binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Rotation))->impl().initializeBindingInputValue(rotationValue); } else { @@ -278,12 +271,12 @@ namespace ramses::internal // Otherwise issue a warning if (rotationValue[0] != 0.f || rotationValue[1] != 0.f || rotationValue[2] != 0.f) { - LOG_WARN("Initial rotation values for RamsesNodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type.", binding.getIdentificationString()); + LOG_WARN_P(CONTEXT_CLIENT, "Initial rotation values for NodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type.", binding.getIdentificationString()); } } else { - binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Rotation))->m_impl->initializeBindingInputValue(PropertyValue{ rotationValue }); + binding.getInputs()->getChild(static_cast(ENodePropertyStaticIndex::Rotation))->impl().initializeBindingInputValue(PropertyValue{ rotationValue }); } } } diff --git a/client/logic/lib/impl/RamsesNodeBindingImpl.h b/src/client/impl/logic/NodeBindingImpl.h similarity index 62% rename from client/logic/lib/impl/RamsesNodeBindingImpl.h rename to src/client/impl/logic/NodeBindingImpl.h index 9b82e24bc..47032ff23 100644 --- a/client/logic/lib/impl/RamsesNodeBindingImpl.h +++ b/src/client/impl/logic/NodeBindingImpl.h @@ -8,11 +8,11 @@ #pragma once -#include "impl/RamsesBindingImpl.h" -#include "ramses-logic/EPropertyType.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" -#include "ramses-client-api/ERotationType.h" +#include "impl/logic/RamsesBindingImpl.h" +#include "ramses/client/logic/EPropertyType.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" +#include "ramses/framework/ERotationType.h" #include @@ -24,7 +24,7 @@ namespace ramses namespace rlogic_serialization { - struct RamsesNodeBinding; + struct NodeBinding; } namespace flatbuffers @@ -47,22 +47,20 @@ namespace ramses::internal Enabled = 4 }; - class RamsesNodeBindingImpl : public RamsesBindingImpl + class NodeBindingImpl : public RamsesBindingImpl { public: // Move-able (noexcept); Not copy-able - explicit RamsesNodeBindingImpl(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name, uint64_t id); - ~RamsesNodeBindingImpl() noexcept override = default; - RamsesNodeBindingImpl(const RamsesNodeBindingImpl& other) = delete; - RamsesNodeBindingImpl& operator=(const RamsesNodeBindingImpl& other) = delete; + explicit NodeBindingImpl(SceneImpl& scene, ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name, sceneObjectId_t id); + ~NodeBindingImpl() noexcept override = default; - [[nodiscard]] static flatbuffers::Offset Serialize( - const RamsesNodeBindingImpl& nodeBinding, + [[nodiscard]] static flatbuffers::Offset Serialize( + const NodeBindingImpl& nodeBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap); - [[nodiscard]] static std::unique_ptr Deserialize( - const rlogic_serialization::RamsesNodeBinding& nodeBinding, + [[nodiscard]] static std::unique_ptr Deserialize( + const rlogic_serialization::NodeBinding& nodeBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap); @@ -76,7 +74,7 @@ namespace ramses::internal void createRootProperties() final; private: - static void ApplyRamsesValuesToInputProperties(RamsesNodeBindingImpl& binding, ramses::Node& ramsesNode); + static void ApplyRamsesValuesToInputProperties(NodeBindingImpl& binding, ramses::Node& ramsesNode); std::reference_wrapper m_ramsesNode; ramses::ERotationType m_rotationType; diff --git a/client/logic/lib/impl/Property.cpp b/src/client/impl/logic/Property.cpp similarity index 78% rename from client/logic/lib/impl/Property.cpp rename to src/client/impl/logic/Property.cpp index 353f39552..8c8690d20 100644 --- a/client/logic/lib/impl/Property.cpp +++ b/src/client/impl/logic/Property.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicNode.h" -#include "impl/PropertyImpl.h" -#include "impl/LogicNodeImpl.h" -#include "impl/LoggerImpl.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicNode.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "internal/Core/Utils/LogMacros.h" namespace ramses { @@ -115,12 +115,21 @@ namespace ramses return m_impl->hasOutgoingLink(); } - std::optional Property::getIncomingLink() const + std::optional Property::getIncomingLink() const { if (!m_impl->hasIncomingLink()) return std::nullopt; const auto& link = m_impl->getIncomingLink(); + return PropertyLinkConst{ &link.property->getPropertyInstance(), this, link.isWeakLink }; + } + + std::optional Property::getIncomingLink() + { + if (!m_impl->hasIncomingLink()) + return std::nullopt; + + auto& link = m_impl->getIncomingLink(); return PropertyLink{ &link.property->getPropertyInstance(), this, link.isWeakLink }; } @@ -129,15 +138,27 @@ namespace ramses return m_impl->hasOutgoingLink() ? m_impl->getOutgoingLinks().size() : 0u; } - std::optional Property::getOutgoingLink(size_t index) const + std::optional Property::getOutgoingLink(size_t index) const { if (index >= getOutgoingLinksCount()) { - LOG_ERROR("Failed to get outgoing link: zero-based index #{} exceeds the total count of outgoing links which is {}.", index, getOutgoingLinksCount()); + LOG_ERROR_P(CONTEXT_CLIENT, "Failed to get outgoing link: zero-based index #{} exceeds the total count of outgoing links which is {}.", index, getOutgoingLinksCount()); return std::nullopt; } const auto& link = m_impl->getOutgoingLinks()[index]; + return PropertyLinkConst{ this, &link.property->getPropertyInstance(), link.isWeakLink }; + } + + std::optional Property::getOutgoingLink(size_t index) + { + if (index >= getOutgoingLinksCount()) + { + LOG_ERROR_P(CONTEXT_CLIENT, "Failed to get outgoing link: zero-based index #{} exceeds the total count of outgoing links which is {}.", index, getOutgoingLinksCount()); + return std::nullopt; + } + + auto& link = m_impl->getOutgoingLinks()[index]; return PropertyLink{ this, &link.property->getPropertyInstance(), link.isWeakLink }; } @@ -150,4 +171,14 @@ namespace ramses { return *m_impl->getLogicNode().getLogicObject().as(); } + + internal::PropertyImpl& Property::impl() + { + return *m_impl; + } + + const internal::PropertyImpl& Property::impl() const + { + return *m_impl; + } } diff --git a/client/logic/lib/impl/PropertyImpl.cpp b/src/client/impl/logic/PropertyImpl.cpp similarity index 87% rename from client/logic/lib/impl/PropertyImpl.cpp rename to src/client/impl/logic/PropertyImpl.cpp index c1890c66a..6fe41c5a5 100644 --- a/client/logic/lib/impl/PropertyImpl.cpp +++ b/src/client/impl/logic/PropertyImpl.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" -#include "impl/LogicNodeImpl.h" -#include "impl/LoggerImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "internal/Core/Utils/LogMacros.h" -#include "internals/SerializationHelper.h" -#include "internals/TypeUtils.h" -#include "internals/ErrorReporting.h" -#include "internals/TypeUtils.h" +#include "internal/logic/SerializationHelper.h" +#include "internal/logic/TypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/TypeUtils.h" -#include "generated/PropertyGen.h" +#include "internal/logic/flatbuffers/generated/PropertyGen.h" #include #include @@ -243,7 +243,7 @@ namespace ramses::internal // TODO Violin we can make name optional - e.g. array fields don't need a name, no need to serialize empty strings if (!prop.name()) { - errorReporting.add("Fatal error during loading of Property from serialized data: missing name!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: missing name!", nullptr); return nullptr; } @@ -251,7 +251,7 @@ namespace ramses::internal if (!convertedType) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid type!", nullptr); return nullptr; } @@ -267,7 +267,7 @@ namespace ramses::internal case rlogic_serialization::PropertyValue::float_s: if (!prop.value_as_float_s()) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = prop.value_as_float_s()->v(); @@ -277,7 +277,7 @@ namespace ramses::internal auto vec2fValue = prop.value_as_vec2f_s(); if (!vec2fValue) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = vec2f{vec2fValue->x(), vec2fValue->y()}; @@ -288,7 +288,7 @@ namespace ramses::internal auto vec3fValue = prop.value_as_vec3f_s(); if (!vec3fValue) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = vec3f{vec3fValue->x(), vec3fValue->y(), vec3fValue->z()}; @@ -299,7 +299,7 @@ namespace ramses::internal auto vec4fValue = prop.value_as_vec4f_s(); if (!vec4fValue) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = vec4f{vec4fValue->x(), vec4fValue->y(), vec4fValue->z(), vec4fValue->w()}; @@ -308,7 +308,7 @@ namespace ramses::internal case rlogic_serialization::PropertyValue::int32_s: if (!prop.value_as_int32_s()) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = prop.value_as_int32_s()->v(); @@ -316,7 +316,7 @@ namespace ramses::internal case rlogic_serialization::PropertyValue::int64_s: if (!prop.value_as_int64_s()) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = prop.value_as_int64_s()->v(); @@ -326,7 +326,7 @@ namespace ramses::internal auto vec2iValue = prop.value_as_vec2i_s(); if (!vec2iValue) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = vec2i{vec2iValue->x(), vec2iValue->y()}; @@ -337,7 +337,7 @@ namespace ramses::internal auto vec3iValue = prop.value_as_vec3i_s(); if (!vec3iValue) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = vec3i{vec3iValue->x(), vec3iValue->y(), vec3iValue->z()}; @@ -348,7 +348,7 @@ namespace ramses::internal auto vec4iValue = prop.value_as_vec4i_s(); if (!vec4iValue) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = vec4i{vec4iValue->x(), vec4iValue->y(), vec4iValue->z(), vec4iValue->w()}; @@ -357,7 +357,7 @@ namespace ramses::internal case rlogic_serialization::PropertyValue::string_s: if (!prop.value_as_string_s()) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = prop.value_as_string_s()->v()->str(); @@ -365,7 +365,7 @@ namespace ramses::internal case rlogic_serialization::PropertyValue::bool_s: if (!prop.value_as_bool_s()) { - errorReporting.add("Fatal error during loading of Property from serialized data: invalid union!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: invalid union!", nullptr); return nullptr; } impl->m_value = prop.value_as_bool_s()->v(); @@ -383,7 +383,7 @@ namespace ramses::internal if (!prop.children()) { - errorReporting.add("Fatal error during loading of Property from serialized data: complex type has no child type info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: complex type has no child type info!", nullptr); return nullptr; } @@ -392,7 +392,7 @@ namespace ramses::internal if (!child) { // TODO Violin find ways to unit-test this case - errorReporting.add("Fatal error during loading of Property from serialized data: corrupt child data!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of Property from serialized data: corrupt child data!", nullptr); return nullptr; } @@ -434,7 +434,7 @@ namespace ramses::internal return m_children[index].get(); } - LOG_ERROR("No child property with index '{}' found in '{}'", index, m_typeData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "No child property with index '{}' found in '{}'", index, m_typeData.name); return nullptr; } @@ -445,7 +445,7 @@ namespace ramses::internal return m_children[index].get(); } - LOG_ERROR("No child property with index '{}' found in '{}'", index, m_typeData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "No child property with index '{}' found in '{}'", index, m_typeData.name); return nullptr; } @@ -464,7 +464,7 @@ namespace ramses::internal { return it->get(); } - LOG_ERROR("No child property with name '{}' found in '{}'", name, m_typeData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "No child property with name '{}' found in '{}'", name, m_typeData.name); return nullptr; } @@ -509,7 +509,7 @@ namespace ramses::internal assert(std::holds_alternative(m_value)); return std::get(m_value); } - LOG_ERROR("Invalid type '{}' when accessing property '{}', correct type is '{}'", + LOG_ERROR_P(CONTEXT_CLIENT, "Invalid type '{}' when accessing property '{}', correct type is '{}'", GetLuaPrimitiveTypeName(PropertyTypeToEnum::TYPE), m_typeData.name, GetLuaPrimitiveTypeName(m_typeData.type)); return std::nullopt; } @@ -530,25 +530,25 @@ namespace ramses::internal { if (m_semantics == EPropertySemantics::ScriptOutput) { - LOG_ERROR("Cannot set property '{}' which is an output.", m_typeData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "Cannot set property '{}' which is an output.", m_typeData.name); return false; } if (m_incomingLink.property != nullptr) { - LOG_ERROR("Property '{}' is currently linked (to property '{}'). Unlink it first before setting its value!", m_typeData.name, m_incomingLink.property->getName()); + LOG_ERROR_P(CONTEXT_CLIENT, "Property '{}' is currently linked (to property '{}'). Unlink it first before setting its value!", m_typeData.name, m_incomingLink.property->getName()); return false; } if (!TypeUtils::IsPrimitiveType(m_typeData.type)) { - LOG_ERROR("Property '{}' is not a primitive type, can't set its value directly!", m_typeData.name); + LOG_ERROR_P(CONTEXT_CLIENT, "Property '{}' is not a primitive type, can't set its value directly!", m_typeData.name); return false; } if (value.index() != m_value.index()) { - LOG_ERROR("Invalid type when setting property '{}', correct type is '{}'", m_typeData.name, GetLuaPrimitiveTypeName(m_typeData.type)); + LOG_ERROR_P(CONTEXT_CLIENT, "Invalid type when setting property '{}', correct type is '{}'", m_typeData.name, GetLuaPrimitiveTypeName(m_typeData.type)); return false; } @@ -561,7 +561,7 @@ namespace ramses::internal const auto int64Value = std::get(value); if (int64Value > maxIntegerAsDouble || int64Value < -maxIntegerAsDouble) { - LOG_ERROR("Invalid value when setting property '{}', Lua cannot handle full range of 64-bit integer, trying to set '{}' which is out of this range!", + LOG_ERROR_P(CONTEXT_CLIENT, "Invalid value when setting property '{}', Lua cannot handle full range of 64-bit integer, trying to set '{}' which is out of this range!", m_typeData.name, int64Value); return false; } diff --git a/client/logic/lib/impl/PropertyImpl.h b/src/client/impl/logic/PropertyImpl.h similarity index 96% rename from client/logic/lib/impl/PropertyImpl.h rename to src/client/impl/logic/PropertyImpl.h index 687e0f958..7f5184961 100644 --- a/client/logic/lib/impl/PropertyImpl.h +++ b/src/client/impl/logic/PropertyImpl.h @@ -8,11 +8,11 @@ #pragma once -#include "ramses-logic/EPropertyType.h" -#include "internals/EPropertySemantics.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" -#include "internals/TypeData.h" +#include "ramses/client/logic/EPropertyType.h" +#include "internal/logic/EPropertySemantics.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/TypeData.h" #include #include diff --git a/client/logic/lib/impl/RamsesBinding.cpp b/src/client/impl/logic/RamsesBinding.cpp similarity index 87% rename from client/logic/lib/impl/RamsesBinding.cpp rename to src/client/impl/logic/RamsesBinding.cpp index 1fb02dcb1..6c414e40f 100644 --- a/client/logic/lib/impl/RamsesBinding.cpp +++ b/src/client/impl/logic/RamsesBinding.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/RamsesBinding.h" +#include "ramses/client/logic/RamsesBinding.h" -#include "impl/RamsesBindingImpl.h" +#include "impl/logic/RamsesBindingImpl.h" namespace ramses { diff --git a/client/logic/lib/impl/RamsesBindingImpl.cpp b/src/client/impl/logic/RamsesBindingImpl.cpp similarity index 80% rename from client/logic/lib/impl/RamsesBindingImpl.cpp rename to src/client/impl/logic/RamsesBindingImpl.cpp index 4c7319a31..5eb3ad622 100644 --- a/client/logic/lib/impl/RamsesBindingImpl.cpp +++ b/src/client/impl/logic/RamsesBindingImpl.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesBindingImpl.h" -#include "ramses-logic/Property.h" +#include "impl/logic/RamsesBindingImpl.h" +#include "ramses/client/logic/Property.h" -#include "ramses-client-api/SceneObject.h" +#include "ramses/client/SceneObject.h" -#include "generated/RamsesReferenceGen.h" +#include "internal/logic/flatbuffers/generated/RamsesReferenceGen.h" namespace ramses::internal { - RamsesBindingImpl::RamsesBindingImpl(std::string_view name, uint64_t id) noexcept - : LogicNodeImpl(name, id) + RamsesBindingImpl::RamsesBindingImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept + : LogicNodeImpl{ scene, name, id } { // Bindings are not supposed to do anything unless user set an actual value to them // Thus, they are not dirty by default! diff --git a/client/logic/lib/impl/RamsesBindingImpl.h b/src/client/impl/logic/RamsesBindingImpl.h similarity index 89% rename from client/logic/lib/impl/RamsesBindingImpl.h rename to src/client/impl/logic/RamsesBindingImpl.h index 085093f1c..a07200f1f 100644 --- a/client/logic/lib/impl/RamsesBindingImpl.h +++ b/src/client/impl/logic/RamsesBindingImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "impl/LogicNodeImpl.h" +#include "impl/logic/LogicNodeImpl.h" namespace ramses { @@ -31,7 +31,7 @@ namespace ramses::internal class RamsesBindingImpl : public LogicNodeImpl { public: - explicit RamsesBindingImpl(std::string_view name, uint64_t id) noexcept; + explicit RamsesBindingImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept; protected: // Used by subclasses to handle serialization diff --git a/client/logic/lib/impl/RamsesRenderGroupBinding.cpp b/src/client/impl/logic/RenderGroupBinding.cpp similarity index 52% rename from client/logic/lib/impl/RamsesRenderGroupBinding.cpp rename to src/client/impl/logic/RenderGroupBinding.cpp index 97532263e..5a0dbab0f 100644 --- a/client/logic/lib/impl/RamsesRenderGroupBinding.cpp +++ b/src/client/impl/logic/RenderGroupBinding.cpp @@ -6,25 +6,35 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "impl/RamsesRenderGroupBindingImpl.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "impl/logic/RenderGroupBindingImpl.h" namespace ramses { - RamsesRenderGroupBinding::RamsesRenderGroupBinding(std::unique_ptr impl) noexcept + RenderGroupBinding::RenderGroupBinding(std::unique_ptr impl) noexcept : RamsesBinding(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_renderGroupBinding{ static_cast(RamsesBinding::m_impl) } + , m_renderGroupBinding{ static_cast(RamsesBinding::m_impl) } { } - const ramses::RenderGroup& RamsesRenderGroupBinding::getRamsesRenderGroup() const + const ramses::RenderGroup& RenderGroupBinding::getRamsesRenderGroup() const { return m_renderGroupBinding.getRamsesRenderGroup(); } - ramses::RenderGroup& RamsesRenderGroupBinding::getRamsesRenderGroup() + ramses::RenderGroup& RenderGroupBinding::getRamsesRenderGroup() { return m_renderGroupBinding.getRamsesRenderGroup(); } + + internal::RenderGroupBindingImpl& RenderGroupBinding::impl() + { + return m_renderGroupBinding; + } + + const internal::RenderGroupBindingImpl& RenderGroupBinding::impl() const + { + return m_renderGroupBinding; + } } diff --git a/src/client/impl/logic/RenderGroupBindingElements.cpp b/src/client/impl/logic/RenderGroupBindingElements.cpp new file mode 100644 index 000000000..925801e5a --- /dev/null +++ b/src/client/impl/logic/RenderGroupBindingElements.cpp @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "impl/logic/RenderGroupBindingElementsImpl.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/RenderGroup.h" + +namespace ramses +{ + RenderGroupBindingElements::RenderGroupBindingElements() noexcept + : m_impl{ std::make_unique() } + { + } + RenderGroupBindingElements::~RenderGroupBindingElements() noexcept = default; + + RenderGroupBindingElements::RenderGroupBindingElements(const RenderGroupBindingElements& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + RenderGroupBindingElements::RenderGroupBindingElements(RenderGroupBindingElements&& other) noexcept = default; + + RenderGroupBindingElements& RenderGroupBindingElements::operator=(const RenderGroupBindingElements& other) + { + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + RenderGroupBindingElements& RenderGroupBindingElements::operator=(RenderGroupBindingElements&& other) noexcept = default; + + bool RenderGroupBindingElements::addElement(const ramses::MeshNode& meshNode, std::string_view elementName) + { + return m_impl->addElement(meshNode, elementName); + } + + bool RenderGroupBindingElements::addElement(const ramses::RenderGroup& nestedRenderGroup, std::string_view elementName) + { + return m_impl->addElement(nestedRenderGroup, elementName); + } + + internal::RenderGroupBindingElementsImpl& RenderGroupBindingElements::impl() + { + return *m_impl; + } + + const internal::RenderGroupBindingElementsImpl& RenderGroupBindingElements::impl() const + { + return *m_impl; + } +} diff --git a/src/client/impl/logic/RenderGroupBindingElementsImpl.cpp b/src/client/impl/logic/RenderGroupBindingElementsImpl.cpp new file mode 100644 index 000000000..9bdc1fd1c --- /dev/null +++ b/src/client/impl/logic/RenderGroupBindingElementsImpl.cpp @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/logic/RenderGroupBindingElementsImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "ramses/client/SceneObject.h" +#include "impl/SceneObjectImpl.h" +#include + +namespace ramses::internal +{ + bool RenderGroupBindingElementsImpl::addElement(const ramses::SceneObject& ramsesObject, std::string_view elementName) + { + assert(ramsesObject.isOfType(ramses::ERamsesObjectType::MeshNode) || ramsesObject.isOfType(ramses::ERamsesObjectType::RenderGroup)); + + if (!m_elements.empty() && !m_elements.front().second->impl().isFromTheSameSceneAs(ramsesObject.impl())) + { + LOG_ERROR_P(CONTEXT_CLIENT, "RenderGroupBindingElements: Failed to add element, element is from different Scene than those already added."); + return false; + } + + std::string name{ elementName.empty() ? ramsesObject.getName() : elementName }; + if (name.empty()) + { + LOG_ERROR_P(CONTEXT_CLIENT, "RenderGroupBindingElements: Failed to add element, object has no name and provided element name is empty."); + return false; + } + + const auto it = std::find_if(m_elements.cbegin(), m_elements.cend(), [&ramsesObject](const auto& e) { return e.second == &ramsesObject; }); + if (it != m_elements.cend()) + { + LOG_ERROR_P(CONTEXT_CLIENT, "RenderGroupBindingElements: Failed to add element '{}', it is already contained under name '{}'.", name, it->first); + return false; + } + + m_elements.push_back({ std::move(name), &ramsesObject }); + + return true; + } + + const RenderGroupBindingElementsImpl::Elements& RenderGroupBindingElementsImpl::getElements() const + { + return m_elements; + } +} diff --git a/client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.h b/src/client/impl/logic/RenderGroupBindingElementsImpl.h similarity index 95% rename from client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.h rename to src/client/impl/logic/RenderGroupBindingElementsImpl.h index 52aad54f1..64733e72c 100644 --- a/client/logic/lib/impl/RamsesRenderGroupBindingElementsImpl.h +++ b/src/client/impl/logic/RenderGroupBindingElementsImpl.h @@ -18,7 +18,7 @@ namespace ramses namespace ramses::internal { - class RamsesRenderGroupBindingElementsImpl + class RenderGroupBindingElementsImpl { public: [[nodiscard]] bool addElement(const ramses::SceneObject& ramsesObject, std::string_view elementName); diff --git a/client/logic/lib/impl/RamsesRenderGroupBindingImpl.cpp b/src/client/impl/logic/RenderGroupBindingImpl.cpp similarity index 64% rename from client/logic/lib/impl/RamsesRenderGroupBindingImpl.cpp rename to src/client/impl/logic/RenderGroupBindingImpl.cpp index bd7f245ad..248d27d5e 100644 --- a/client/logic/lib/impl/RamsesRenderGroupBindingImpl.cpp +++ b/src/client/impl/logic/RenderGroupBindingImpl.cpp @@ -6,20 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesRenderGroupBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-logic/Property.h" -#include "ramses-utils.h" -#include "internals/ErrorReporting.h" -#include "internals/RamsesObjectResolver.h" -#include "generated/RamsesRenderGroupBindingGen.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/ramses-utils.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesObjectResolver.h" +#include "internal/logic/flatbuffers/generated/RenderGroupBindingGen.h" #include "fmt/format.h" namespace ramses::internal { - RamsesRenderGroupBindingImpl::RamsesRenderGroupBindingImpl(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElementsImpl& elements, std::string_view name, uint64_t id) - : RamsesBindingImpl{ name, id } + RenderGroupBindingImpl::RenderGroupBindingImpl(SceneImpl& scene, ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElementsImpl& elements, std::string_view name, sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_ramsesRenderGroup{ ramsesRenderGroup } , m_elements{ elements.getElements() } { @@ -27,7 +28,7 @@ namespace ramses::internal assert(std::none_of(m_elements.cbegin(), m_elements.cend(), [](const auto& e) { return e.second == nullptr; })); } - void RamsesRenderGroupBindingImpl::createRootProperties() + void RenderGroupBindingImpl::createRootProperties() { HierarchicalTypeData inputsType = MakeStruct("", { TypeData{"renderOrders", EPropertyType::Struct} @@ -45,14 +46,14 @@ namespace ramses::internal ApplyRamsesValuesToInputProperties(*this, m_ramsesRenderGroup); } - flatbuffers::Offset RamsesRenderGroupBindingImpl::Serialize( - const RamsesRenderGroupBindingImpl& renderGroupBinding, + flatbuffers::Offset RenderGroupBindingImpl::Serialize( + const RenderGroupBindingImpl& renderGroupBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap) { const auto logicObject = LogicObjectImpl::Serialize(renderGroupBinding, builder); const auto fbRamsesRef = RamsesBindingImpl::SerializeRamsesReference(renderGroupBinding.m_ramsesRenderGroup, builder); - const auto propertyObject = PropertyImpl::Serialize(*renderGroupBinding.getInputs()->m_impl, builder, serializationMap); + const auto propertyObject = PropertyImpl::Serialize(renderGroupBinding.getInputs()->impl(), builder, serializationMap); auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(builder, logicObject, fbRamsesRef, @@ -69,37 +70,37 @@ namespace ramses::internal ramsesReferenceFB)); } - auto fbRenderGroupBinding = rlogic_serialization::CreateRamsesRenderGroupBinding(builder, fbRamsesBinding, builder.CreateVector(elementsFB)); + auto fbRenderGroupBinding = rlogic_serialization::CreateRenderGroupBinding(builder, fbRamsesBinding, builder.CreateVector(elementsFB)); builder.Finish(fbRenderGroupBinding); return fbRenderGroupBinding; } - std::unique_ptr RamsesRenderGroupBindingImpl::Deserialize( - const rlogic_serialization::RamsesRenderGroupBinding& renderGroupBinding, + std::unique_ptr RenderGroupBindingImpl::Deserialize( + const rlogic_serialization::RenderGroupBinding& renderGroupBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { if (!renderGroupBinding.base()) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing base class info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: missing base class info!", nullptr); return nullptr; } std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(renderGroupBinding.base()->base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!renderGroupBinding.base()->rootInput()) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: missing root input!", nullptr); return nullptr; } @@ -109,14 +110,14 @@ namespace ramses::internal if (deserializedRootInput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: root input has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: root input has unexpected type!", nullptr); return nullptr; } const auto* boundObject = renderGroupBinding.base()->boundRamsesObject(); if (!boundObject) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing ramses object reference!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: missing ramses object reference!", nullptr); return nullptr; } @@ -127,22 +128,22 @@ namespace ramses::internal if (ramsesRenderGroup->getType() != static_cast(boundObject->objectType())) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: loaded object type does not match referenced object type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: loaded object type does not match referenced object type!", nullptr); return nullptr; } if (!deserializedRootInput->getChild("renderOrders") || deserializedRootInput->getChild("renderOrders")->getChildCount() != renderGroupBinding.elements()->size()) { - errorReporting.add("Fatal error during loading of RamsesRenderGroupBinding from serialized data: input properties do not match RenderGroup's elements!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderGroupBinding from serialized data: input properties do not match RenderGroup's elements!", nullptr); return nullptr; } - RamsesRenderGroupBindingElementsImpl elements; + RenderGroupBindingElementsImpl elements; for (const auto* elementFB : *renderGroupBinding.elements()) { if (!elementFB->name() || !elementFB->ramsesObject()) { - errorReporting.add(fmt::format("Fatal error during loading of RamsesRenderGroupBinding '{}' elements data: missing name or Ramses reference!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of RenderGroupBinding '{}' elements data: missing name or Ramses reference!", name), nullptr); return nullptr; } @@ -152,19 +153,19 @@ namespace ramses::internal if (elementObject->getType() != static_cast(elementFB->ramsesObject()->objectType())) { - errorReporting.add(fmt::format("Fatal error during loading of RamsesRenderGroupBinding '{}' elements data: loaded element object type does not match referenced object type!", name), - nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of RenderGroupBinding '{}' elements data: loaded element object type does not match referenced object type!", name), + nullptr); return nullptr; } if (!elements.addElement(*elementObject, elementFB->name()->string_view())) { - errorReporting.add(fmt::format("Fatal error during loading of RamsesRenderGroupBinding '{}' elements data '{}' corrupted!", name, elementFB->name()->string_view()), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of RenderGroupBinding '{}' elements data '{}' corrupted!", name, elementFB->name()->string_view()), nullptr); return nullptr; } } - auto binding = std::make_unique(*ramsesRenderGroup, elements, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *ramsesRenderGroup, elements, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs(std::move(deserializedRootInput)); @@ -173,7 +174,7 @@ namespace ramses::internal return binding; } - std::optional RamsesRenderGroupBindingImpl::update() + std::optional RenderGroupBindingImpl::update() { // The input properties for render order must match the ramses object references stored in m_elements. // This is asserted in ApplyRamsesValuesToInputProperties which is always executed once at creation/deserialization, @@ -182,50 +183,50 @@ namespace ramses::internal auto renderOrdersProps = getInputs()->getChild(0u); for (std::size_t i = 0u; i < renderOrdersProps->getChildCount(); ++i) { - auto& elementPropImpl = *renderOrdersProps->getChild(i)->m_impl; + auto& elementPropImpl = renderOrdersProps->getChild(i)->impl(); if (elementPropImpl.checkForBindingInputNewValueAndReset()) { const int32_t renderOrder = elementPropImpl.getValueAs(); const auto& obj = *m_elements[i].second; assert(obj.isOfType(ramses::ERamsesObjectType::MeshNode) || obj.isOfType(ramses::ERamsesObjectType::RenderGroup)); - ramses::status_t status = ramses::StatusOK; + bool status = false; if (obj.isOfType(ramses::ERamsesObjectType::MeshNode)) { - const auto meshNode = ramses::RamsesUtils::TryConvert(obj); + const auto meshNode = obj.as(); if (!m_ramsesRenderGroup.get().containsMeshNode(*meshNode)) return LogicNodeRuntimeError{ "Cannot set render order of MeshNode which is not contained in bound RenderGroup." }; status = m_ramsesRenderGroup.get().addMeshNode(*meshNode, renderOrder); // we are not adding it, this is ramses way to change render order of already contained element } else { - const auto rg = ramses::RamsesUtils::TryConvert(obj); + const auto rg = obj.as(); if (!m_ramsesRenderGroup.get().containsRenderGroup(*rg)) return LogicNodeRuntimeError{ "Cannot set render order of RenderGroup which is not contained in bound RenderGroup." }; status = m_ramsesRenderGroup.get().addRenderGroup(*rg, renderOrder); // we are not adding it, this is ramses way to change render order of already contained element } - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesRenderGroup.get().getStatusMessage(status) }; + if (!status) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } } return std::nullopt; } - const ramses::RenderGroup& RamsesRenderGroupBindingImpl::getRamsesRenderGroup() const + const ramses::RenderGroup& RenderGroupBindingImpl::getRamsesRenderGroup() const { return m_ramsesRenderGroup; } - ramses::RenderGroup& RamsesRenderGroupBindingImpl::getRamsesRenderGroup() + ramses::RenderGroup& RenderGroupBindingImpl::getRamsesRenderGroup() { return m_ramsesRenderGroup; } - // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at initialization, + // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at creation or deserialization, // should not overwrite values unless set() or link explicitly called - void RamsesRenderGroupBindingImpl::ApplyRamsesValuesToInputProperties(RamsesRenderGroupBindingImpl& binding, ramses::RenderGroup& ramsesRenderGroup) + void RenderGroupBindingImpl::ApplyRamsesValuesToInputProperties(RenderGroupBindingImpl& binding, ramses::RenderGroup& ramsesRenderGroup) { auto renderOrdersProps = binding.getInputs()->getChild(0u); assert(renderOrdersProps && binding.m_elements.size() == renderOrdersProps->getChildCount()); @@ -235,25 +236,25 @@ namespace ramses::internal int32_t renderOrder = 0; const auto& obj = *binding.m_elements[i].second; - ramses::status_t status = ramses::StatusOK; + bool status = false; if (obj.isOfType(ramses::ERamsesObjectType::MeshNode)) { - const auto meshNode = ramses::RamsesUtils::TryConvert(obj); + const auto meshNode = obj.as(); assert(meshNode); assert(ramsesRenderGroup.containsMeshNode(*meshNode)); status = ramsesRenderGroup.getMeshNodeOrder(*meshNode, renderOrder); } else { - const auto rg = ramses::RamsesUtils::TryConvert(obj); + const auto rg = obj.as(); assert(rg); assert(ramsesRenderGroup.containsRenderGroup(*rg)); status = ramsesRenderGroup.getRenderGroupOrder(*rg, renderOrder); } - assert(status == ramses::StatusOK); + assert(status); (void)status; - auto& elementPropImpl = *renderOrdersProps->getChild(i)->m_impl; + auto& elementPropImpl = renderOrdersProps->getChild(i)->impl(); assert(elementPropImpl.getName() == binding.m_elements[i].first); elementPropImpl.initializeBindingInputValue(PropertyValue{ renderOrder }); } diff --git a/client/logic/lib/impl/RamsesRenderGroupBindingImpl.h b/src/client/impl/logic/RenderGroupBindingImpl.h similarity index 58% rename from client/logic/lib/impl/RamsesRenderGroupBindingImpl.h rename to src/client/impl/logic/RenderGroupBindingImpl.h index 2116ee8b5..a14a6c92a 100644 --- a/client/logic/lib/impl/RamsesRenderGroupBindingImpl.h +++ b/src/client/impl/logic/RenderGroupBindingImpl.h @@ -8,8 +8,8 @@ #pragma once -#include "impl/RamsesBindingImpl.h" -#include "impl/RamsesRenderGroupBindingElementsImpl.h" +#include "impl/logic/RamsesBindingImpl.h" +#include "impl/logic/RenderGroupBindingElementsImpl.h" #include namespace ramses @@ -19,7 +19,7 @@ namespace ramses namespace rlogic_serialization { - struct RamsesRenderGroupBinding; + struct RenderGroupBinding; } namespace flatbuffers @@ -35,22 +35,20 @@ namespace ramses::internal class SerializationMap; class DeserializationMap; - class RamsesRenderGroupBindingImpl : public RamsesBindingImpl + class RenderGroupBindingImpl : public RamsesBindingImpl { public: // Move-able (noexcept); Not copy-able - explicit RamsesRenderGroupBindingImpl(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElementsImpl& elements, std::string_view name, uint64_t id); - ~RamsesRenderGroupBindingImpl() noexcept override = default; - RamsesRenderGroupBindingImpl(const RamsesRenderGroupBindingImpl& other) = delete; - RamsesRenderGroupBindingImpl& operator=(const RamsesRenderGroupBindingImpl& other) = delete; + explicit RenderGroupBindingImpl(SceneImpl& scene, ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElementsImpl& elements, std::string_view name, sceneObjectId_t id); + ~RenderGroupBindingImpl() noexcept override = default; - [[nodiscard]] static flatbuffers::Offset Serialize( - const RamsesRenderGroupBindingImpl& renderGroupBinding, + [[nodiscard]] static flatbuffers::Offset Serialize( + const RenderGroupBindingImpl& renderGroupBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap); - [[nodiscard]] static std::unique_ptr Deserialize( - const rlogic_serialization::RamsesRenderGroupBinding& renderGroupBinding, + [[nodiscard]] static std::unique_ptr Deserialize( + const rlogic_serialization::RenderGroupBinding& renderGroupBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap); @@ -63,9 +61,9 @@ namespace ramses::internal void createRootProperties() final; private: - static void ApplyRamsesValuesToInputProperties(RamsesRenderGroupBindingImpl& binding, ramses::RenderGroup& ramsesRenderGroup); + static void ApplyRamsesValuesToInputProperties(RenderGroupBindingImpl& binding, ramses::RenderGroup& ramsesRenderGroup); std::reference_wrapper m_ramsesRenderGroup; - RamsesRenderGroupBindingElementsImpl::Elements m_elements; + RenderGroupBindingElementsImpl::Elements m_elements; }; } diff --git a/client/logic/lib/impl/RamsesRenderPassBinding.cpp b/src/client/impl/logic/RenderPassBinding.cpp similarity index 52% rename from client/logic/lib/impl/RamsesRenderPassBinding.cpp rename to src/client/impl/logic/RenderPassBinding.cpp index 062adf8e9..dc16b1af3 100644 --- a/client/logic/lib/impl/RamsesRenderPassBinding.cpp +++ b/src/client/impl/logic/RenderPassBinding.cpp @@ -6,25 +6,35 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "impl/RamsesRenderPassBindingImpl.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "impl/logic/RenderPassBindingImpl.h" namespace ramses { - RamsesRenderPassBinding::RamsesRenderPassBinding(std::unique_ptr impl) noexcept + RenderPassBinding::RenderPassBinding(std::unique_ptr impl) noexcept : RamsesBinding(std::move(impl)) /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) */ - , m_renderPassBinding{ static_cast(RamsesBinding::m_impl) } + , m_renderPassBinding{ static_cast(RamsesBinding::m_impl) } { } - const ramses::RenderPass& RamsesRenderPassBinding::getRamsesRenderPass() const + const ramses::RenderPass& RenderPassBinding::getRamsesRenderPass() const { return m_renderPassBinding.getRamsesRenderPass(); } - ramses::RenderPass& RamsesRenderPassBinding::getRamsesRenderPass() + ramses::RenderPass& RenderPassBinding::getRamsesRenderPass() { return m_renderPassBinding.getRamsesRenderPass(); } + + internal::RenderPassBindingImpl& RenderPassBinding::impl() + { + return m_renderPassBinding; + } + + const internal::RenderPassBindingImpl& RenderPassBinding::impl() const + { + return m_renderPassBinding; + } } diff --git a/client/logic/lib/impl/RamsesRenderPassBindingImpl.cpp b/src/client/impl/logic/RenderPassBindingImpl.cpp similarity index 50% rename from client/logic/lib/impl/RamsesRenderPassBindingImpl.cpp rename to src/client/impl/logic/RenderPassBindingImpl.cpp index ffaf16f4f..9caafba34 100644 --- a/client/logic/lib/impl/RamsesRenderPassBindingImpl.cpp +++ b/src/client/impl/logic/RenderPassBindingImpl.cpp @@ -6,28 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/RamsesRenderPassBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" -#include "ramses-client-api/RenderPass.h" +#include "ramses/client/RenderPass.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "impl/PropertyImpl.h" +#include "impl/logic/PropertyImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/RamsesObjectResolver.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesObjectResolver.h" -#include "generated/RamsesRenderPassBindingGen.h" +#include "internal/logic/flatbuffers/generated/RenderPassBindingGen.h" namespace ramses::internal { - RamsesRenderPassBindingImpl::RamsesRenderPassBindingImpl(ramses::RenderPass& ramsesRenderPass, std::string_view name, uint64_t id) - : RamsesBindingImpl{ name, id } + RenderPassBindingImpl::RenderPassBindingImpl(SceneImpl& scene, ramses::RenderPass& ramsesRenderPass, std::string_view name, sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_ramsesRenderPass{ ramsesRenderPass } { } - void RamsesRenderPassBindingImpl::createRootProperties() + void RenderPassBindingImpl::createRootProperties() { // Attention! This order is important - it has to match the indices in EPropertyIndex! auto inputsType = MakeStruct("", { @@ -43,50 +43,50 @@ namespace ramses::internal ApplyRamsesValuesToInputProperties(*this, m_ramsesRenderPass); } - flatbuffers::Offset RamsesRenderPassBindingImpl::Serialize( - const RamsesRenderPassBindingImpl& renderPassBinding, + flatbuffers::Offset RenderPassBindingImpl::Serialize( + const RenderPassBindingImpl& renderPassBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap) { const auto logicObject = LogicObjectImpl::Serialize(renderPassBinding, builder); const auto fbRamsesRef = RamsesBindingImpl::SerializeRamsesReference(renderPassBinding.m_ramsesRenderPass, builder); - const auto propertyObject = PropertyImpl::Serialize(*renderPassBinding.getInputs()->m_impl, builder, serializationMap); + const auto propertyObject = PropertyImpl::Serialize(renderPassBinding.getInputs()->impl(), builder, serializationMap); auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(builder, logicObject, fbRamsesRef, propertyObject); - auto fbRenderPassBinding = rlogic_serialization::CreateRamsesRenderPassBinding(builder, fbRamsesBinding); + auto fbRenderPassBinding = rlogic_serialization::CreateRenderPassBinding(builder, fbRamsesBinding); builder.Finish(fbRenderPassBinding); return fbRenderPassBinding; } - std::unique_ptr RamsesRenderPassBindingImpl::Deserialize( - const rlogic_serialization::RamsesRenderPassBinding& renderPassBinding, + std::unique_ptr RenderPassBindingImpl::Deserialize( + const rlogic_serialization::RenderPassBinding& renderPassBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap) { if (!renderPassBinding.base()) { - errorReporting.add("Fatal error during loading of RamsesRenderPassBinding from serialized data: missing base class info!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderPassBinding from serialized data: missing base class info!", nullptr); return nullptr; } std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(renderPassBinding.base()->base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of RamsesRenderPassBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderPassBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!renderPassBinding.base()->rootInput()) { - errorReporting.add("Fatal error during loading of RamsesRenderPassBinding from serialized data: missing root input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderPassBinding from serialized data: missing root input!", nullptr); return nullptr; } @@ -96,14 +96,14 @@ namespace ramses::internal if (deserializedRootInput->getType() != EPropertyType::Struct) { - errorReporting.add("Fatal error during loading of RamsesRenderPassBinding from serialized data: root input has unexpected type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderPassBinding from serialized data: root input has unexpected type!", nullptr); return nullptr; } const auto* boundObject = renderPassBinding.base()->boundRamsesObject(); if (!boundObject) { - errorReporting.add("Fatal error during loading of RamsesRenderPassBinding from serialized data: missing ramses object reference!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderPassBinding from serialized data: missing ramses object reference!", nullptr); return nullptr; } @@ -114,11 +114,11 @@ namespace ramses::internal if (ramsesRenderPass->getType() != static_cast(boundObject->objectType())) { - errorReporting.add("Fatal error during loading of RamsesRenderPassBinding from serialized data: loaded object type does not match referenced object type!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of RenderPassBinding from serialized data: loaded object type does not match referenced object type!", nullptr); return nullptr; } - auto binding = std::make_unique(*ramsesRenderPass, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), *ramsesRenderPass, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs(std::move(deserializedRootInput)); @@ -127,66 +127,60 @@ namespace ramses::internal return binding; } - std::optional RamsesRenderPassBindingImpl::update() + std::optional RenderPassBindingImpl::update() { - ramses::status_t status = ramses::StatusOK; - - PropertyImpl& enabled = *getInputs()->getChild(EPropertyIndex_Enabled)->m_impl; + PropertyImpl& enabled = getInputs()->getChild(EPropertyIndex_Enabled)->impl(); if (enabled.checkForBindingInputNewValueAndReset()) { - status = m_ramsesRenderPass.get().setEnabled(enabled.getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesRenderPass.get().getStatusMessage(status) }; + if (!m_ramsesRenderPass.get().setEnabled(enabled.getValueAs())) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - PropertyImpl& renderOrder = *getInputs()->getChild(EPropertyIndex_RenderOrder)->m_impl; + PropertyImpl& renderOrder = getInputs()->getChild(EPropertyIndex_RenderOrder)->impl(); if (renderOrder.checkForBindingInputNewValueAndReset()) { - status = m_ramsesRenderPass.get().setRenderOrder(renderOrder.getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesRenderPass.get().getStatusMessage(status) }; + if (!m_ramsesRenderPass.get().setRenderOrder(renderOrder.getValueAs())) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - PropertyImpl& clearColor = *getInputs()->getChild(EPropertyIndex_ClearColor)->m_impl; + PropertyImpl& clearColor = getInputs()->getChild(EPropertyIndex_ClearColor)->impl(); if (clearColor.checkForBindingInputNewValueAndReset()) { - status = m_ramsesRenderPass.get().setClearColor(clearColor.getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesRenderPass.get().getStatusMessage(status) }; + if (!m_ramsesRenderPass.get().setClearColor(clearColor.getValueAs())) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } - PropertyImpl& renderOnce = *getInputs()->getChild(EPropertyIndex_RenderOnce)->m_impl; + PropertyImpl& renderOnce = getInputs()->getChild(EPropertyIndex_RenderOnce)->impl(); if (renderOnce.checkForBindingInputNewValueAndReset()) { - status = m_ramsesRenderPass.get().setRenderOnce(renderOnce.getValueAs()); - if (status != ramses::StatusOK) - return LogicNodeRuntimeError{ m_ramsesRenderPass.get().getStatusMessage(status) }; + if (!m_ramsesRenderPass.get().setRenderOnce(renderOnce.getValueAs())) + return LogicNodeRuntimeError{ getErrorReporting().getError()->message }; } return std::nullopt; } - const ramses::RenderPass& RamsesRenderPassBindingImpl::getRamsesRenderPass() const + const ramses::RenderPass& RenderPassBindingImpl::getRamsesRenderPass() const { return m_ramsesRenderPass; } - ramses::RenderPass& RamsesRenderPassBindingImpl::getRamsesRenderPass() + ramses::RenderPass& RenderPassBindingImpl::getRamsesRenderPass() { return m_ramsesRenderPass; } - // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at initialization, + // Overwrites binding value cache silently (without triggering dirty check) - this code is only executed at creation or deserialization, // should not overwrite values unless set() or link explicitly called - void RamsesRenderPassBindingImpl::ApplyRamsesValuesToInputProperties(RamsesRenderPassBindingImpl& binding, ramses::RenderPass& ramsesRenderPass) + void RenderPassBindingImpl::ApplyRamsesValuesToInputProperties(RenderPassBindingImpl& binding, ramses::RenderPass& ramsesRenderPass) { - binding.getInputs()->getChild(EPropertyIndex_Enabled)->m_impl->initializeBindingInputValue(PropertyValue{ ramsesRenderPass.isEnabled() }); + binding.getInputs()->getChild(EPropertyIndex_Enabled)->impl().initializeBindingInputValue(PropertyValue{ ramsesRenderPass.isEnabled() }); - binding.getInputs()->getChild(EPropertyIndex_RenderOrder)->m_impl->initializeBindingInputValue(PropertyValue{ ramsesRenderPass.getRenderOrder() }); + binding.getInputs()->getChild(EPropertyIndex_RenderOrder)->impl().initializeBindingInputValue(PropertyValue{ ramsesRenderPass.getRenderOrder() }); vec4f clearColor = ramsesRenderPass.getClearColor(); - binding.getInputs()->getChild(EPropertyIndex_ClearColor)->m_impl->initializeBindingInputValue(PropertyValue{ clearColor }); + binding.getInputs()->getChild(EPropertyIndex_ClearColor)->impl().initializeBindingInputValue(PropertyValue{ clearColor }); - binding.getInputs()->getChild(EPropertyIndex_RenderOnce)->m_impl->initializeBindingInputValue(PropertyValue{ ramsesRenderPass.isRenderOnce() }); + binding.getInputs()->getChild(EPropertyIndex_RenderOnce)->impl().initializeBindingInputValue(PropertyValue{ ramsesRenderPass.isRenderOnce() }); } } diff --git a/client/logic/lib/impl/RamsesRenderPassBindingImpl.h b/src/client/impl/logic/RenderPassBindingImpl.h similarity index 66% rename from client/logic/lib/impl/RamsesRenderPassBindingImpl.h rename to src/client/impl/logic/RenderPassBindingImpl.h index 605bb0817..49f4e5261 100644 --- a/client/logic/lib/impl/RamsesRenderPassBindingImpl.h +++ b/src/client/impl/logic/RenderPassBindingImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "impl/RamsesBindingImpl.h" +#include "impl/logic/RamsesBindingImpl.h" #include namespace ramses @@ -18,7 +18,7 @@ namespace ramses namespace rlogic_serialization { - struct RamsesRenderPassBinding; + struct RenderPassBinding; } namespace flatbuffers @@ -34,7 +34,7 @@ namespace ramses::internal class SerializationMap; class DeserializationMap; - class RamsesRenderPassBindingImpl : public RamsesBindingImpl + class RenderPassBindingImpl : public RamsesBindingImpl { public: enum EPropertyIndex @@ -48,18 +48,16 @@ namespace ramses::internal }; // Move-able (noexcept); Not copy-able - explicit RamsesRenderPassBindingImpl(ramses::RenderPass& ramsesRenderPass, std::string_view name, uint64_t id); - ~RamsesRenderPassBindingImpl() noexcept override = default; - RamsesRenderPassBindingImpl(const RamsesRenderPassBindingImpl& other) = delete; - RamsesRenderPassBindingImpl& operator=(const RamsesRenderPassBindingImpl& other) = delete; + explicit RenderPassBindingImpl(SceneImpl& scene, ramses::RenderPass& ramsesRenderPass, std::string_view name, sceneObjectId_t id); + ~RenderPassBindingImpl() noexcept override = default; - [[nodiscard]] static flatbuffers::Offset Serialize( - const RamsesRenderPassBindingImpl& renderPassBinding, + [[nodiscard]] static flatbuffers::Offset Serialize( + const RenderPassBindingImpl& renderPassBinding, flatbuffers::FlatBufferBuilder& builder, SerializationMap& serializationMap); - [[nodiscard]] static std::unique_ptr Deserialize( - const rlogic_serialization::RamsesRenderPassBinding& renderPassBinding, + [[nodiscard]] static std::unique_ptr Deserialize( + const rlogic_serialization::RenderPassBinding& renderPassBinding, const IRamsesObjectResolver& ramsesResolver, ErrorReporting& errorReporting, DeserializationMap& deserializationMap); @@ -72,7 +70,7 @@ namespace ramses::internal void createRootProperties() final; private: - static void ApplyRamsesValuesToInputProperties(RamsesRenderPassBindingImpl& binding, ramses::RenderPass& ramsesRenderPass); + static void ApplyRamsesValuesToInputProperties(RenderPassBindingImpl& binding, ramses::RenderPass& ramsesRenderPass); std::reference_wrapper m_ramsesRenderPass; }; diff --git a/client/logic/lib/impl/SkinBinding.cpp b/src/client/impl/logic/SkinBinding.cpp similarity index 67% rename from client/logic/lib/impl/SkinBinding.cpp rename to src/client/impl/logic/SkinBinding.cpp index f3e4164fb..fbad8c2f9 100644 --- a/client/logic/lib/impl/SkinBinding.cpp +++ b/src/client/impl/logic/SkinBinding.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "impl/SkinBindingImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" namespace ramses { @@ -20,13 +20,23 @@ namespace ramses { } - const RamsesAppearanceBinding& SkinBinding::getAppearanceBinding() const + const AppearanceBinding& SkinBinding::getAppearanceBinding() const { - return *m_skinBinding.getAppearanceBinding().getLogicObject().as(); + return *m_skinBinding.getAppearanceBinding().getLogicObject().as(); } const ramses::UniformInput& SkinBinding::getAppearanceUniformInput() const { return m_skinBinding.getAppearanceUniformInput(); } + + internal::SkinBindingImpl& SkinBinding::impl() + { + return m_skinBinding; + } + + const internal::SkinBindingImpl& SkinBinding::impl() const + { + return m_skinBinding; + } } diff --git a/client/logic/lib/impl/SkinBindingImpl.cpp b/src/client/impl/logic/SkinBindingImpl.cpp similarity index 67% rename from client/logic/lib/impl/SkinBindingImpl.cpp rename to src/client/impl/logic/SkinBindingImpl.cpp index bbab66176..848551bff 100644 --- a/client/logic/lib/impl/SkinBindingImpl.cpp +++ b/src/client/impl/logic/SkinBindingImpl.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/SkinBindingImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "internals/ErrorReporting.h" -#include "internals/DeserializationMap.h" -#include "generated/SkinBindingGen.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "ramses/client/Node.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/flatbuffers/generated/SkinBindingGen.h" #include "fmt/format.h" #include "glm/gtc/type_ptr.hpp" #include "glm/gtx/range.hpp" @@ -22,28 +22,25 @@ namespace ramses::internal { SkinBindingImpl::SkinBindingImpl( - std::vector joints, + SceneImpl& scene, + std::vector joints, const std::vector& inverseBindMatrices, - RamsesAppearanceBindingImpl& appearanceBinding, + AppearanceBindingImpl& appearanceBinding, const ramses::UniformInput& jointMatInput, std::string_view name, - uint64_t id) - : RamsesBindingImpl{ name, id } + sceneObjectId_t id) + : RamsesBindingImpl{ scene, name, id } , m_joints{ std::move(joints) } , m_inverseBindMatrices(inverseBindMatrices) , m_appearanceBinding{ appearanceBinding } + , m_jointMatInput { *appearanceBinding.getRamsesAppearance().getEffect().findUniformInput(jointMatInput.getName()) } { assert(!m_joints.empty()); assert(m_joints.size() == inverseBindMatrices.size()); assert(std::find(m_joints.cbegin(), m_joints.cend(), nullptr) == m_joints.cend()); - // ramses::UniformInput cannot be copied, to avoid referencing user provided instance, get it from effect again - assert(jointMatInput.isValid()); - appearanceBinding.getRamsesAppearance().getEffect().findUniformInput(jointMatInput.getName(), m_jointMatInput); - assert(m_jointMatInput.isValid()); - assert(!m_appearanceBinding.getRamsesAppearance().isInputBound(m_jointMatInput)); - assert(*m_jointMatInput.getDataType() == ramses::EDataType::Matrix44F); + assert(m_jointMatInput.getDataType() == ramses::EDataType::Matrix44F); assert(m_jointMatInput.getElementCount() == m_joints.size()); } @@ -63,7 +60,7 @@ namespace ramses::internal std::vector jointIds; jointIds.reserve(skinBinding.m_joints.size()); for (const auto* joint : skinBinding.m_joints) - jointIds.push_back(joint->getId()); + jointIds.push_back(joint->getSceneObjectId().getValue()); const auto fbJoints = builder.CreateVector(jointIds); std::vector inverseBindMatData; @@ -78,7 +75,7 @@ namespace ramses::internal fbLogicObject, fbJoints, fbInverseBindMatData, - skinBinding.m_appearanceBinding.getId(), + skinBinding.m_appearanceBinding.getSceneObjectId().getValue(), builder.CreateString(skinBinding.m_jointMatInput.getName())); builder.Finish(fbSkinBinding); @@ -91,30 +88,30 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(skinBinding.base(), name, id, userIdHigh, userIdLow, errorReporting)) { - errorReporting.add("Fatal error during loading of SkinBinding from serialized data: missing name and/or ID!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of SkinBinding from serialized data: missing name and/or ID!", nullptr); return nullptr; } if (!skinBinding.jointNodeBindingIds() || !skinBinding.inverseBindingMatricesData() || skinBinding.jointNodeBindingIds()->size() == 0u || skinBinding.jointNodeBindingIds()->size() * 16u != skinBinding.inverseBindingMatricesData()->size()) { - errorReporting.add("Fatal error during loading of SkinBinding from serialized data: missing or corrupted joints and/or inverse matrices data!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of SkinBinding from serialized data: missing or corrupted joints and/or inverse matrices data!", nullptr); return nullptr; } - std::vector joints; + std::vector joints; joints.reserve(skinBinding.jointNodeBindingIds()->size()); for (const uint64_t nodeId : *skinBinding.jointNodeBindingIds()) { - const auto nodeBinding = deserializationMap.resolveLogicObject(nodeId); + const auto nodeBinding = deserializationMap.resolveLogicObject(sceneObjectId_t{ nodeId }); if (!nodeBinding) { - errorReporting.add("Fatal error during loading of SkinBinding from serialized data: could not resolve referenced node binding!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of SkinBinding from serialized data: could not resolve referenced node binding!", nullptr); return nullptr; } joints.push_back(nodeBinding); @@ -130,23 +127,23 @@ namespace ramses::internal fbDataBegin = fbDataEnd; } - auto appearanceBinding = deserializationMap.resolveLogicObject(skinBinding.appearanceBindingId()); + auto appearanceBinding = deserializationMap.resolveLogicObject(sceneObjectId_t{ skinBinding.appearanceBindingId() }); if (!appearanceBinding) { - errorReporting.add("Fatal error during loading of SkinBinding from serialized data: could not resolve referenced appearance binding!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of SkinBinding from serialized data: could not resolve referenced appearance binding!", nullptr); return nullptr; } - ramses::UniformInput jointMatInput; + std::optional jointMatInput; if (skinBinding.jointMatUniformInputName()) - appearanceBinding->getRamsesAppearance().getEffect().findUniformInput(skinBinding.jointMatUniformInputName()->c_str(), jointMatInput); - if (!jointMatInput.isValid() || *jointMatInput.getDataType() != ramses::EDataType::Matrix44F || jointMatInput.getElementCount() != joints.size()) + jointMatInput = appearanceBinding->getRamsesAppearance().getEffect().findUniformInput(skinBinding.jointMatUniformInputName()->c_str()); + if (!jointMatInput.has_value() || jointMatInput->getDataType() != ramses::EDataType::Matrix44F || jointMatInput->getElementCount() != joints.size()) { - errorReporting.add("Fatal error during loading of SkinBinding from serialized data: invalid or mismatching uniform input!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of SkinBinding from serialized data: invalid or mismatching uniform input!", nullptr); return nullptr; } - auto binding = std::make_unique(joints, inverseMats, *appearanceBinding, jointMatInput, name, id); + auto binding = std::make_unique(deserializationMap.getScene(), joints, inverseMats, *appearanceBinding, *jointMatInput, name, id); binding->setUserId(userIdHigh, userIdLow); binding->setRootInputs({}); @@ -159,7 +156,7 @@ namespace ramses::internal for (size_t i = 0u; i < m_joints.size(); ++i) { matrix44f jointNodeWorld; - if (m_joints[i]->getRamsesNode().getModelMatrix(jointNodeWorld) != ramses::StatusOK) + if (!m_joints[i]->getRamsesNode().getModelMatrix(jointNodeWorld)) return LogicNodeRuntimeError{ "Failed to retrieve model matrix from Ramses node!" }; const auto& inverseBindMatForJoint = m_inverseBindMatrices[i]; const auto jointMat = jointNodeWorld * inverseBindMatForJoint; @@ -167,18 +164,18 @@ namespace ramses::internal m_jointMatricesArray.emplace_back(jointMat); } - if (m_appearanceBinding.getRamsesAppearance().setInputValue(m_jointMatInput, uint32_t(m_jointMatricesArray.size()), m_jointMatricesArray.data()) != ramses::StatusOK) + if (!m_appearanceBinding.getRamsesAppearance().setInputValue(m_jointMatInput, uint32_t(m_jointMatricesArray.size()), m_jointMatricesArray.data())) return LogicNodeRuntimeError{ "Failed to set matrix array uniform to Ramses appearance!" }; return std::nullopt; } - const std::vector& SkinBindingImpl::getJoints() const + const std::vector& SkinBindingImpl::getJoints() const { return m_joints; } - const RamsesAppearanceBindingImpl& SkinBindingImpl::getAppearanceBinding() const + const AppearanceBindingImpl& SkinBindingImpl::getAppearanceBinding() const { return m_appearanceBinding; } diff --git a/client/logic/lib/impl/SkinBindingImpl.h b/src/client/impl/logic/SkinBindingImpl.h similarity index 76% rename from client/logic/lib/impl/SkinBindingImpl.h rename to src/client/impl/logic/SkinBindingImpl.h index 6f39c8118..108584f0f 100644 --- a/client/logic/lib/impl/SkinBindingImpl.h +++ b/src/client/impl/logic/SkinBindingImpl.h @@ -8,10 +8,10 @@ #pragma once -#include "impl/RamsesBindingImpl.h" -#include "ramses-logic/Property.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-framework-api/DataTypes.h" +#include "impl/logic/RamsesBindingImpl.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/DataTypes.h" #include namespace rlogic_serialization @@ -27,8 +27,8 @@ namespace flatbuffers namespace ramses::internal { - class RamsesNodeBindingImpl; - class RamsesAppearanceBindingImpl; + class NodeBindingImpl; + class AppearanceBindingImpl; class ErrorReporting; class SerializationMap; class DeserializationMap; @@ -38,12 +38,13 @@ namespace ramses::internal public: // Move-able (noexcept); Not copy-able explicit SkinBindingImpl( - std::vector joints, + SceneImpl& scene, + std::vector joints, const std::vector& inverseBindMatrices, - RamsesAppearanceBindingImpl& appearanceBinding, + AppearanceBindingImpl& appearanceBinding, const ramses::UniformInput& jointMatInput, std::string_view name, - uint64_t id); + sceneObjectId_t id); ~SkinBindingImpl() noexcept override = default; SkinBindingImpl(const SkinBindingImpl& other) = delete; SkinBindingImpl& operator=(const SkinBindingImpl& other) = delete; @@ -58,8 +59,8 @@ namespace ramses::internal ErrorReporting& errorReporting, DeserializationMap& deserializationMap); - [[nodiscard]] const std::vector& getJoints() const; - [[nodiscard]] const RamsesAppearanceBindingImpl& getAppearanceBinding() const; + [[nodiscard]] const std::vector& getJoints() const; + [[nodiscard]] const AppearanceBindingImpl& getAppearanceBinding() const; [[nodiscard]] const ramses::UniformInput& getAppearanceUniformInput() const; std::optional update() override; @@ -67,9 +68,9 @@ namespace ramses::internal void createRootProperties() final; private: - std::vector m_joints; + std::vector m_joints; std::vector m_inverseBindMatrices; - RamsesAppearanceBindingImpl& m_appearanceBinding; + AppearanceBindingImpl& m_appearanceBinding; ramses::UniformInput m_jointMatInput; // temp variable used only in update kept as member to avoid reallocs every update call diff --git a/client/logic/lib/impl/TimerNode.cpp b/src/client/impl/logic/TimerNode.cpp similarity index 73% rename from client/logic/lib/impl/TimerNode.cpp rename to src/client/impl/logic/TimerNode.cpp index a1b1c7689..8582efd79 100644 --- a/client/logic/lib/impl/TimerNode.cpp +++ b/src/client/impl/logic/TimerNode.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/TimerNode.h" -#include "impl/TimerNodeImpl.h" +#include "ramses/client/logic/TimerNode.h" +#include "impl/logic/TimerNodeImpl.h" namespace ramses { @@ -17,4 +17,14 @@ namespace ramses , m_timerNodeImpl{ static_cast(LogicNode::m_impl) } { } + + internal::TimerNodeImpl& TimerNode::impl() + { + return m_timerNodeImpl; + } + + const internal::TimerNodeImpl& TimerNode::impl() const + { + return m_timerNodeImpl; + } } diff --git a/client/logic/lib/impl/TimerNodeImpl.cpp b/src/client/impl/logic/TimerNodeImpl.cpp similarity index 77% rename from client/logic/lib/impl/TimerNodeImpl.cpp rename to src/client/impl/logic/TimerNodeImpl.cpp index 5328de04b..4ddfaf98b 100644 --- a/client/logic/lib/impl/TimerNodeImpl.cpp +++ b/src/client/impl/logic/TimerNodeImpl.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "impl/TimerNodeImpl.h" -#include "ramses-logic/Property.h" -#include "impl/PropertyImpl.h" -#include "internals/ErrorReporting.h" -#include "generated/TimerNodeGen.h" +#include "impl/logic/TimerNodeImpl.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/flatbuffers/generated/TimerNodeGen.h" #include "flatbuffers/flatbuffers.h" #include "fmt/format.h" namespace ramses::internal { - TimerNodeImpl::TimerNodeImpl(std::string_view name, uint64_t id) noexcept - : LogicNodeImpl(name, id) + TimerNodeImpl::TimerNodeImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept + : LogicNodeImpl{ scene, name, id } { } @@ -50,7 +50,7 @@ namespace ramses::internal outTicker_us = ticker; } - getOutputs()->getChild(0u)->m_impl->setValue(outTicker_us); + getOutputs()->getChild(0u)->impl().setValue(outTicker_us); return std::nullopt; } @@ -64,16 +64,19 @@ namespace ramses::internal // in the files (this makes their content undeterministic). Instead, we write zeroes, and // cache the current values and restore them again after serialization is done + const PropertyImpl& property = timerNode.getOutputs()->getChild(0u)->impl(); + // 1. Cache current output values - const PropertyValue outTickerTmp = timerNode.getOutputs()->getChild(0u)->m_impl->getValue(); + const PropertyValue outTickerTmp = property.getValue(); // 2. Set to 0 - timerNode.getOutputs()->getChild(0u)->m_impl->setValue(int64_t(0)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + const_cast(property).setValue(int64_t(0)); // 3. Serialize const auto logicObject = LogicObjectImpl::Serialize(timerNode, builder); - const auto inputPropertyObject = PropertyImpl::Serialize(*timerNode.getInputs()->m_impl, builder, serializationMap); - const auto outputPropertyObject = PropertyImpl::Serialize(*timerNode.getOutputs()->m_impl, builder, serializationMap); + const auto inputPropertyObject = PropertyImpl::Serialize(timerNode.getInputs()->impl(), builder, serializationMap); + const auto outputPropertyObject = PropertyImpl::Serialize(timerNode.getOutputs()->impl(), builder, serializationMap); auto timerNodeOffset = rlogic_serialization::CreateTimerNode( builder, logicObject, @@ -82,7 +85,8 @@ namespace ramses::internal ); // 4. Restore values - timerNode.getOutputs()->getChild(0u)->m_impl->setValue(outTickerTmp); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + const_cast(property).setValue(outTickerTmp); return timerNodeOffset; } @@ -93,16 +97,16 @@ namespace ramses::internal DeserializationMap& deserializationMap) { std::string name; - uint64_t id = 0u; + sceneObjectId_t id{}; uint64_t userIdHigh = 0u; uint64_t userIdLow = 0u; if (!LogicObjectImpl::Deserialize(timerNodeFB.base(), name, id, userIdHigh, userIdLow, errorReporting) || !timerNodeFB.rootInput() || !timerNodeFB.rootOutput()) { - errorReporting.add("Fatal error during loading of TimerNode from serialized data: missing name, id or in/out property data!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading of TimerNode from serialized data: missing name, id or in/out property data!", nullptr); return nullptr; } - auto deserialized = std::make_unique(name, id); + auto deserialized = std::make_unique(deserializationMap.getScene(), name, id); deserialized->setUserId(userIdHigh, userIdLow); // deserialize and overwrite constructor generated properties @@ -113,7 +117,7 @@ namespace ramses::internal !rootInProperty->getChild(0u) || rootInProperty->getChild(0u)->getName() != "ticker_us" || !rootOutProperty->getChild(0u) || rootOutProperty->getChild(0u)->getName() != "ticker_us") { - errorReporting.add(fmt::format("Fatal error during loading of TimerNode '{}': missing or invalid properties!", name), nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of TimerNode '{}': missing or invalid properties!", name), nullptr); return nullptr; } deserialized->setRootProperties(std::move(rootInProperty), std::move(rootOutProperty)); diff --git a/client/logic/lib/impl/TimerNodeImpl.h b/src/client/impl/logic/TimerNodeImpl.h similarity index 91% rename from client/logic/lib/impl/TimerNodeImpl.h rename to src/client/impl/logic/TimerNodeImpl.h index d1b4dcb50..3b18b7d6e 100644 --- a/client/logic/lib/impl/TimerNodeImpl.h +++ b/src/client/impl/logic/TimerNodeImpl.h @@ -8,7 +8,7 @@ #pragma once -#include "impl/LogicNodeImpl.h" +#include "impl/logic/LogicNodeImpl.h" #include #include #include @@ -33,7 +33,7 @@ namespace ramses::internal class TimerNodeImpl : public LogicNodeImpl { public: - TimerNodeImpl(std::string_view name, uint64_t id) noexcept; + TimerNodeImpl(SceneImpl& scene, std::string_view name, sceneObjectId_t id) noexcept; std::optional update() override; diff --git a/client/ramses-client/impl/ramses-utils.cpp b/src/client/impl/ramses-utils.cpp similarity index 53% rename from client/ramses-client/impl/ramses-utils.cpp rename to src/client/impl/ramses-utils.cpp index cf1dd0167..483399c83 100644 --- a/client/ramses-client/impl/ramses-utils.cpp +++ b/src/client/impl/ramses-utils.cpp @@ -6,65 +6,68 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-utils.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-client-api/DataObject.h" - -#include "EffectImpl.h" -#include "MeshNodeImpl.h" -#include "RamsesClientImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "PickableObjectImpl.h" - -#include "Math3d/ProjectionParams.h" -#include "Utils/File.h" -#include "Utils/LogMacros.h" -#include "SceneDumper.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Node.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/client/DataObject.h" + +#include "impl/EffectImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/PickableObjectImpl.h" + +#include "internal/Core/Math3d/ProjectionParams.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/LogMacros.h" +#include "impl/SceneDumper.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/PlatformMath.h" #include "lodepng.h" #include #include namespace ramses { - template - const T* RamsesUtils::TryConvert(const RamsesObject& obj) + namespace { - if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(obj.getType(), TYPE_ID_OF_RAMSES_OBJECT::ID)) + bool IsPowerOfTwo(uint32_t val) { - return &RamsesObjectTypeUtils::ConvertTo(obj); - } + while (((val & 1u) == 0u) && val > 1u) + { + val >>= 1u; + } - return nullptr; - } + return (val == 1u); + } - template - T* RamsesUtils::TryConvert(RamsesObject& obj) - { - if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(obj.getType(), TYPE_ID_OF_RAMSES_OBJECT::ID)) + uint32_t Log2(uint32_t val) { - return &RamsesObjectTypeUtils::ConvertTo(obj); - } + uint32_t pow = 0; + while (((val & 1u) == 0u) && val > 1u) + { + pow++; + val >>= 1u; + } - return nullptr; + return pow; + } } Texture2D* RamsesUtils::CreateTextureResourceFromPng(const char* pngFilePath, Scene& scene, const TextureSwizzle& swizzle, std::string_view name/* = 0*/) { if (!pngFilePath) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::CreateTextureResourceFromPng: file path is nullptr"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::CreateTextureResourceFromPng: file path is nullptr"); return nullptr; } @@ -75,56 +78,56 @@ namespace ramses const unsigned int ret = lodepng::decode(data, width, height, pngFilePath); if (ret != 0) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::CreateTextureResourceFromPng: Could not load PNG. File not found or invalid format: " << + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::CreateTextureResourceFromPng: Could not load PNG. File not found or invalid format: " << pngFilePath << " (error " << ret << ": " << lodepng_error_text(ret) << ")"); return nullptr; } - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - return scene.createTexture2D(ETextureFormat::RGBA8, width, height, 1, &mipLevelData, false, swizzle, ResourceCacheFlag_DoNotCache, name); + MipLevelData mipLevelData{static_cast(data.size()), data.data()}; + return scene.createTexture2D(ETextureFormat::RGBA8, width, height, 1, &mipLevelData, false, swizzle, name); } - Texture2D* RamsesUtils::CreateTextureResourceFromPngBuffer(const std::vector& pngData, Scene& scene, const TextureSwizzle& swizzle, std::string_view name) + Texture2D* RamsesUtils::CreateTextureResourceFromPngBuffer(const std::vector& pngData, Scene& scene, const TextureSwizzle& swizzle, std::string_view name) { unsigned int width = 0; unsigned int height = 0; std::vector data; - const unsigned int ret = lodepng::decode(data, width, height, pngData); + const unsigned int ret = lodepng::decode(data, width, height, pngData.data(), pngData.size()); if (ret != 0) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::CreateTextureResourceFromPngBuffer: Could not load PNG. Invalid format (error " << ret << ": " << lodepng_error_text(ret) << ")"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::CreateTextureResourceFromPngBuffer: Could not load PNG. Invalid format (error " << ret << ": " << lodepng_error_text(ret) << ")"); return nullptr; } - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - return scene.createTexture2D(ETextureFormat::RGBA8, width, height, 1, &mipLevelData, false, swizzle, ResourceCacheFlag_DoNotCache, name); + MipLevelData mipLevelData{static_cast(data.size()), data.data()}; + return scene.createTexture2D(ETextureFormat::RGBA8, width, height, 1, &mipLevelData, false, swizzle, name); } bool RamsesUtils::SaveImageBufferToPng(const std::string& filePath, const std::vector& imageData, uint32_t width, uint32_t height) { if (width <= 0 || height <= 0) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Given width and height cannot be 0"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Given width and height cannot be 0"); return false; } if (width * height > 268435455u) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height cannot exceed the size of 268435455"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height cannot exceed the size of 268435455"); return false; } if (width * height * 4u != imageData.size()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height * 4 (rgba8) must exactly match the size of the image buffer"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height * 4 (rgba8) must exactly match the size of the image buffer"); return false; } - - const unsigned int ret = lodepng::encode(filePath, imageData, width, height); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const unsigned int ret = lodepng::encode(filePath, reinterpret_cast(imageData.data()), width, height); if (ret != 0) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Error while saving PNG file: " << filePath << " (error " << ret << ": " << lodepng_error_text(ret) << ")"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Error while saving PNG file: " << filePath << " (error " << ret << ": " << lodepng_error_text(ret) << ")"); return false; } return true; @@ -134,33 +137,33 @@ namespace ramses { if (width <= 0 || height <= 0) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Given width and height cannot be 0"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Given width and height cannot be 0"); return false; } if (width * height > 268435455u) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height cannot exceed the size of 268435455"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height cannot exceed the size of 268435455"); return false; } if (width * height * 4u != imageData.size()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height * 4 (rgba8) must exactly match the size of the image buffer"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SaveImageBufferToPng: Width * height * 4 (rgba8) must exactly match the size of the image buffer"); return false; } if (flipImageBufferVertically) { const size_t lineSize = width * 4u; - std::vector lineBufferV(lineSize); - unsigned char* lineBuffer = lineBufferV.data(); + std::vector lineBufferV(lineSize); + uint8_t* lineBuffer = lineBufferV.data(); for (size_t i = 0u; i < height / 2u; i++) { - unsigned char* endOfImageData = imageData.data() + height * lineSize; - unsigned char* beginOfRowNumberI = imageData.data() + i * lineSize; - unsigned char* beginOfLastRowMinusIrows = endOfImageData - ((i + 1) * lineSize); + uint8_t* endOfImageData = imageData.data() + height * lineSize; + uint8_t* beginOfRowNumberI = imageData.data() + i * lineSize; + uint8_t* beginOfLastRowMinusIrows = endOfImageData - ((i + 1) * lineSize); std::memcpy(lineBuffer, beginOfRowNumberI, lineSize); //1. fill line buffer std::memcpy(beginOfRowNumberI, beginOfLastRowMinusIrows, lineSize); //overwrite copied line @@ -174,34 +177,12 @@ namespace ramses return true; } - bool IsPowerOfTwo(uint32_t val) - { - while (((val & 1u) == 0u) && val > 1u) - { - val >>= 1u; - } - - return (val == 1u); - } - - uint32_t Log2(uint32_t val) - { - uint32_t pow = 0; - while (((val & 1u) == 0u) && val > 1u) - { - pow++; - val >>= 1u; - } - - return pow; - } - - MipLevelData* RamsesUtils::GenerateMipMapsTexture2D(uint32_t originalWidth, uint32_t originalHeight, uint8_t bytesPerPixel, uint8_t* data, size_t& mipMapCount) + MipLevelData* RamsesUtils::GenerateMipMapsTexture2D(uint32_t originalWidth, uint32_t originalHeight, uint8_t bytesPerPixel, std::byte* data, size_t& mipMapCount) { // copy original data const uint32_t originalSize = originalWidth * originalHeight * bytesPerPixel; - uint8_t* originalData = new uint8_t[originalSize]; - ramses_internal::PlatformMemory::Copy(originalData, data, originalSize); + auto* originalData = new std::byte[originalSize]; + ramses::internal::PlatformMemory::Copy(originalData, data, originalSize); if (!IsPowerOfTwo(originalWidth) || !IsPowerOfTwo(originalHeight)) { @@ -214,7 +195,7 @@ namespace ramses } // prepare mip data generation - ramses::MipLevelData* mipLevelData = new ramses::MipLevelData[mipMapCount]; + auto* mipLevelData = new ramses::MipLevelData[mipMapCount]; mipLevelData[0].m_size = originalSize; mipLevelData[0].m_data = originalData; @@ -225,7 +206,7 @@ namespace ramses uint32_t nextWidth = std::max(originalWidth >> 1, 1u); uint32_t nextHeight = std::max(originalHeight >> 1, 1u); uint32_t nextSize = nextWidth * nextHeight * bytesPerPixel; - uint8_t* nextData = new uint8_t[nextSize]; + auto* nextData = new std::byte[nextSize]; uint32_t originalRowSize = originalWidth * bytesPerPixel; uint32_t nextRowSize = nextWidth * bytesPerPixel; @@ -264,7 +245,7 @@ namespace ramses } } - nextData[nextIndex + i] = static_cast(tmp); + nextData[nextIndex + i] = std::byte{static_cast(tmp)}; } } } @@ -282,11 +263,11 @@ namespace ramses return mipLevelData; } - CubeMipLevelData* RamsesUtils::GenerateMipMapsTextureCube(uint32_t faceWidth, uint32_t faceHeight, uint8_t bytesPerPixel, uint8_t* data, size_t& mipMapCount) + CubeMipLevelData* RamsesUtils::GenerateMipMapsTextureCube(uint32_t faceWidth, uint32_t faceHeight, uint8_t bytesPerPixel, std::byte* data, size_t& mipMapCount) { const uint32_t faceSize = faceWidth * faceHeight * bytesPerPixel; mipMapCount = 0u; - std::array faceMips; + std::array faceMips{}; faceMips[0] = GenerateMipMapsTexture2D(faceWidth, faceHeight, bytesPerPixel, &data[faceSize * 0], mipMapCount); faceMips[1] = GenerateMipMapsTexture2D(faceWidth, faceHeight, bytesPerPixel, &data[faceSize * 1], mipMapCount); faceMips[2] = GenerateMipMapsTexture2D(faceWidth, faceHeight, bytesPerPixel, &data[faceSize * 2], mipMapCount); @@ -294,17 +275,17 @@ namespace ramses faceMips[4] = GenerateMipMapsTexture2D(faceWidth, faceHeight, bytesPerPixel, &data[faceSize * 4], mipMapCount); faceMips[5] = GenerateMipMapsTexture2D(faceWidth, faceHeight, bytesPerPixel, &data[faceSize * 5], mipMapCount); - CubeMipLevelData* cubeMipMaps = new CubeMipLevelData[mipMapCount]; + auto* cubeMipMaps = new CubeMipLevelData[mipMapCount]; for (size_t level = 0; level < mipMapCount; level++) { - new (&cubeMipMaps[level]) CubeMipLevelData( + new (&cubeMipMaps[level]) CubeMipLevelData{ faceMips[0][level].m_size, faceMips[0][level].m_data, faceMips[1][level].m_data, faceMips[2][level].m_data, faceMips[3][level].m_data, faceMips[4][level].m_data, - faceMips[5][level].m_data); + faceMips[5][level].m_data}; } for (uint8_t level = 0; level < 6u; level++) @@ -342,20 +323,20 @@ namespace ramses nodeId_t RamsesUtils::GetNodeId(const Node& node) { - return nodeId_t(node.m_impl.getNodeHandle().asMemoryHandle()); + return nodeId_t(node.impl().getNodeHandle().asMemoryHandle()); } bool RamsesUtils::SetPerspectiveCameraFrustumToDataObjects(float fov, float aspectRatio, float nearPlane, float farPlane, DataObject& frustumPlanesData, DataObject& nearFarPlanesData) { - const auto params = ramses_internal::ProjectionParams::Perspective(fov, aspectRatio, nearPlane, farPlane); + const auto params = ramses::internal::ProjectionParams::Perspective(fov, aspectRatio, nearPlane, farPlane); if (!params.isValid()) { - LOG_ERROR(ramses_internal::CONTEXT_CLIENT, "RamsesUtils::SetPerspectiveCameraFrustumToDataObjects failed: invalid frustum planes"); + LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesUtils::SetPerspectiveCameraFrustumToDataObjects failed: invalid frustum planes"); return false; } - if (frustumPlanesData.setValue(ramses::vec4f{ params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }) != StatusOK || - nearFarPlanesData.setValue(ramses::vec2f{ params.nearPlane, params.farPlane }) != StatusOK) + if (!frustumPlanesData.setValue(ramses::vec4f{ params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }) || + !nearFarPlanesData.setValue(ramses::vec2f{ params.nearPlane, params.farPlane })) return false; return true; @@ -363,80 +344,18 @@ namespace ramses void RamsesUtils::DumpUnrequiredSceneObjects(const Scene& scene) { - LOG_INFO_F(ramses_internal::CONTEXT_CLIENT, [&](ramses_internal::StringOutputStream& output) { - SceneDumper sceneDumper{ scene.m_impl }; + LOG_INFO_F(ramses::internal::CONTEXT_CLIENT, [&](ramses::internal::StringOutputStream& output) { + internal::SceneDumper sceneDumper{ scene.impl() }; sceneDumper.dumpUnrequiredObjects(output); }); } void RamsesUtils::DumpUnrequiredSceneObjectsToFile(const Scene& scene, std::ostream& out) { - ramses_internal::StringOutputStream output; - SceneDumper sceneDumper{ scene.m_impl }; + ramses::internal::StringOutputStream output; + internal::SceneDumper sceneDumper{ scene.impl() }; sceneDumper.dumpUnrequiredObjects(output); out << output.release(); } } -// include all RamsesObject to instantiate conversion templates -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/SceneReference.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/ArrayResource.h" - -#define INSTANTIATE_CONVERT_TEMPLATE(_objType) \ - template RAMSES_API const ramses::_objType* ramses::RamsesUtils::TryConvert(const ramses::RamsesObject& obj); \ - template RAMSES_API ramses::_objType* ramses::RamsesUtils::TryConvert(ramses::RamsesObject& obj); - -INSTANTIATE_CONVERT_TEMPLATE(ClientObject) -INSTANTIATE_CONVERT_TEMPLATE(RamsesObject) -INSTANTIATE_CONVERT_TEMPLATE(SceneObject) -INSTANTIATE_CONVERT_TEMPLATE(RamsesClient) -INSTANTIATE_CONVERT_TEMPLATE(Scene) -INSTANTIATE_CONVERT_TEMPLATE(Node) -INSTANTIATE_CONVERT_TEMPLATE(MeshNode) -INSTANTIATE_CONVERT_TEMPLATE(Camera) -INSTANTIATE_CONVERT_TEMPLATE(PerspectiveCamera) -INSTANTIATE_CONVERT_TEMPLATE(OrthographicCamera) -INSTANTIATE_CONVERT_TEMPLATE(Effect) -INSTANTIATE_CONVERT_TEMPLATE(Appearance) -INSTANTIATE_CONVERT_TEMPLATE(GeometryBinding) -INSTANTIATE_CONVERT_TEMPLATE(PickableObject) -INSTANTIATE_CONVERT_TEMPLATE(Resource) -INSTANTIATE_CONVERT_TEMPLATE(Texture2D) -INSTANTIATE_CONVERT_TEMPLATE(Texture3D) -INSTANTIATE_CONVERT_TEMPLATE(TextureCube) -INSTANTIATE_CONVERT_TEMPLATE(ArrayResource) -INSTANTIATE_CONVERT_TEMPLATE(RenderGroup) -INSTANTIATE_CONVERT_TEMPLATE(RenderPass) -INSTANTIATE_CONVERT_TEMPLATE(BlitPass) -INSTANTIATE_CONVERT_TEMPLATE(TextureSampler) -INSTANTIATE_CONVERT_TEMPLATE(TextureSamplerMS) -INSTANTIATE_CONVERT_TEMPLATE(TextureSamplerExternal) -INSTANTIATE_CONVERT_TEMPLATE(RenderBuffer) -INSTANTIATE_CONVERT_TEMPLATE(RenderTarget) -INSTANTIATE_CONVERT_TEMPLATE(DataObject) -INSTANTIATE_CONVERT_TEMPLATE(ArrayBuffer) -INSTANTIATE_CONVERT_TEMPLATE(Texture2DBuffer) -INSTANTIATE_CONVERT_TEMPLATE(SceneReference) diff --git a/client/ramses-client/impl/ramses-text/FontCascade.cpp b/src/client/impl/text/FontCascade.cpp similarity index 93% rename from client/ramses-client/impl/ramses-text/FontCascade.cpp rename to src/client/impl/text/FontCascade.cpp index 3d68a3f5e..83d952533 100644 --- a/client/ramses-client/impl/ramses-text/FontCascade.cpp +++ b/src/client/impl/text/FontCascade.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/IFontAccessor.h" -#include "ramses-text-api/IFontInstance.h" -#include "ramses-text-api/FontCascade.h" +#include "ramses/client/text/IFontAccessor.h" +#include "ramses/client/text/IFontInstance.h" +#include "ramses/client/text/FontCascade.h" #include namespace ramses diff --git a/client/ramses-text-api/FontRegistry.cpp b/src/client/impl/text/FontRegistry.cpp similarity index 93% rename from client/ramses-text-api/FontRegistry.cpp rename to src/client/impl/text/FontRegistry.cpp index 784570d5c..1d0699b22 100644 --- a/client/ramses-text-api/FontRegistry.cpp +++ b/src/client/impl/text/FontRegistry.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text/FontRegistryImpl.h" +#include "ramses/client/text/FontRegistry.h" +#include "impl/text/FontRegistryImpl.h" namespace ramses { FontRegistry::FontRegistry() - : impl(*new FontRegistryImpl) + : impl(*new internal::FontRegistryImpl) { } diff --git a/client/ramses-client/impl/ramses-text/FontRegistryImpl.cpp b/src/client/impl/text/FontRegistryImpl.cpp similarity index 89% rename from client/ramses-client/impl/ramses-text/FontRegistryImpl.cpp rename to src/client/impl/text/FontRegistryImpl.cpp index 168d6f358..627994540 100644 --- a/client/ramses-client/impl/ramses-text/FontRegistryImpl.cpp +++ b/src/client/impl/text/FontRegistryImpl.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/FontRegistryImpl.h" -#include "ramses-text/Freetype2FontInstance.h" -#include "ramses-text/HarfbuzzFontInstance.h" -#include "ramses-text/TextTypesImpl.h" -#include "Utils/LogMacros.h" -#include "RamsesFrameworkTypesImpl.h" -#include "Utils/File.h" +#include "impl/text/FontRegistryImpl.h" +#include "impl/text/Freetype2FontInstance.h" +#include "impl/text/HarfbuzzFontInstance.h" +#include "impl/text/TextTypesImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "internal/Core/Utils/File.h" #include #include -namespace ramses +namespace ramses::internal { FT_Library SharedFTLibrary::Ft2Library = nullptr; size_t SharedFTLibrary::NumRefs = 0u; @@ -26,7 +26,7 @@ namespace ramses if (Ft2Library == nullptr) { const int32_t error = FT_Init_FreeType(&Ft2Library); - if (error) + if (error != 0) { Ft2Library = nullptr; LOG_ERROR(CONTEXT_TEXT, "SharedFTLibrary: Failed to initialize FreeType with error " << error); @@ -46,7 +46,7 @@ namespace ramses } } - FT_Library SharedFTLibrary::get() + FT_Library SharedFTLibrary::Get() { return Ft2Library; } @@ -60,14 +60,14 @@ namespace ramses FontId FontRegistryImpl::createFreetype2Font(std::string_view fontPath) { // basic checks - if (fontPath.empty() || !ramses_internal::File(fontPath).exists()) + if (fontPath.empty() || !ramses::internal::File(fontPath).exists()) { LOG_ERROR(CONTEXT_TEXT, "FontRegistry::createFreetype2Font: Font file not found " << fontPath); return {}; } LOG_INFO_P(CONTEXT_CLIENT, "FontRegistry::createFreetype2Font: path {}", fontPath); - return createFreetype2FontCommon(std::make_unique(fontPath, m_ft2Library.get())); + return createFreetype2FontCommon(std::make_unique(fontPath, SharedFTLibrary::Get())); } FontId FontRegistryImpl::createFreetype2FontFromFileDescriptor(int fd, size_t offset, size_t length) @@ -85,10 +85,10 @@ namespace ramses } LOG_INFO_P(CONTEXT_CLIENT, "FontRegistry::createFreetype2FontFromFileDescriptor: fd {}, offset {}, length {}", fd, offset, length); - return createFreetype2FontCommon(std::make_unique(fd, offset, length, m_ft2Library.get())); + return createFreetype2FontCommon(std::make_unique(fd, offset, length, SharedFTLibrary::Get())); } - FontId FontRegistryImpl::createFreetype2FontCommon(std::unique_ptr face) + FontId FontRegistryImpl::createFreetype2FontCommon(std::unique_ptr face) { if (!face->init()) return {}; diff --git a/client/ramses-client/impl/ramses-text/FontRegistryImpl.h b/src/client/impl/text/FontRegistryImpl.h similarity index 85% rename from client/ramses-client/impl/ramses-text/FontRegistryImpl.h rename to src/client/impl/text/FontRegistryImpl.h index c3a9661e3..72e4c93f4 100644 --- a/client/ramses-client/impl/ramses-text/FontRegistryImpl.h +++ b/src/client/impl/text/FontRegistryImpl.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FONTREGISTRYIMPL_H -#define RAMSES_FONTREGISTRYIMPL_H +#pragma once -#include "ramses-text-api/IFontInstance.h" -#include "ramses-text/Freetype2Wrapper.h" -#include "ramses-text/FreetypeFontFace.h" +#include "ramses/client/text/IFontInstance.h" +#include "impl/text/Freetype2Wrapper.h" +#include "impl/text/FreetypeFontFace.h" #include #include #include -namespace ramses +namespace ramses::internal { class SharedFTLibrary { @@ -25,7 +24,7 @@ namespace ramses SharedFTLibrary(); ~SharedFTLibrary(); - FT_Library get(); + static FT_Library Get(); private: static FT_Library Ft2Library; @@ -56,11 +55,11 @@ namespace ramses private: FontInstanceId reserveFontInstanceId(); void registerFontInstance(FontInstanceId fontInstanceId, std::unique_ptr fontInstance); - FontId createFreetype2FontCommon(std::unique_ptr face); + FontId createFreetype2FontCommon(std::unique_ptr face); SharedFTLibrary m_ft2Library; - std::unordered_map> m_fonts; + std::unordered_map> m_fonts; FontId m_lastFontId{ 0u }; using FontInstances = std::unordered_map>; @@ -68,5 +67,3 @@ namespace ramses FontInstanceId m_lastFontInstanceId{ 0u }; }; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/Freetype2FontInstance.cpp b/src/client/impl/text/Freetype2FontInstance.cpp similarity index 90% rename from client/ramses-client/impl/ramses-text/Freetype2FontInstance.cpp rename to src/client/impl/text/Freetype2FontInstance.cpp index 3203e7ba4..caf473ed9 100644 --- a/client/ramses-client/impl/ramses-text/Freetype2FontInstance.cpp +++ b/src/client/impl/text/Freetype2FontInstance.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/Freetype2FontInstance.h" -#include "ramses-text/Quad.h" -#include "Utils/LogMacros.h" -#include "RamsesFrameworkTypesImpl.h" -#include "ramses-text/TextTypesImpl.h" +#include "impl/text/Freetype2FontInstance.h" +#include "impl/text/Quad.h" +#include "internal/Core/Utils/LogMacros.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/text/TextTypesImpl.h" #include -namespace ramses +namespace ramses::internal { Freetype2FontInstance::Freetype2FontInstance(FontInstanceId id, FT_Face fontFace, uint32_t pixelSize, bool forceAutohinting) : m_id(id) @@ -120,9 +120,9 @@ namespace ramses metrics.key = GlyphKey(glyphId, m_id); metrics.width = static_cast(glyphMetrics.width / 64); metrics.height = static_cast(glyphMetrics.height / 64); - metrics.posX = glyphMetrics.horiBearingX / 64; - metrics.posY = (glyphMetrics.horiBearingY - glyphMetrics.height) / 64; - metrics.advance = glyphMetrics.horiAdvance / 64; + metrics.posX = static_cast(glyphMetrics.horiBearingX / 64); + metrics.posY = static_cast((glyphMetrics.horiBearingY - glyphMetrics.height) / 64); + metrics.advance = static_cast(glyphMetrics.horiAdvance / 64); return &m_glyphMetricsCache.insert({ glyphId, std::move(metrics) }).first->second; } @@ -140,7 +140,7 @@ namespace ramses FT_Glyph ftGlyph = nullptr; auto error = FT_Get_Glyph(m_face->glyph, &ftGlyph); { - if (error) + if (error != 0) { LOG_ERROR(CONTEXT_TEXT, "Freetype2FontInstance::extractGlyphBitmapData: FT_Get_Glyph failed - error: " << error); assert(ftGlyph == nullptr); @@ -151,7 +151,7 @@ namespace ramses if (ftGlyph->format != FT_GLYPH_FORMAT_BITMAP) { error = FT_Glyph_To_Bitmap(&ftGlyph, FT_RENDER_MODE_NORMAL, nullptr, 1); - if (error) + if (error != 0) { LOG_ERROR(CONTEXT_TEXT, "Freetype2FontInstance::extractGlyphBitmapData: FT_Glyph_To_Bitmap failed - error: " << error); FT_Done_Glyph(ftGlyph); @@ -160,7 +160,7 @@ namespace ramses } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) safe because C-style inheritance, valid if ftGlyph->format == FT_GLYPH_FORMAT_BITMAP - const FT_BitmapGlyph bitmapGlyph = reinterpret_cast(ftGlyph); + const auto bitmapGlyph = reinterpret_cast(ftGlyph); const QuadSize glyphBitmapSize(bitmapGlyph->bitmap.width, bitmapGlyph->bitmap.rows); data.width = glyphBitmapSize.x; data.height = glyphBitmapSize.y; @@ -201,7 +201,7 @@ namespace ramses void Freetype2FontInstance::activateSize() const { const uint32_t error = FT_Activate_Size(m_size); - if (error) + if (error != 0u) { LOG_ERROR(CONTEXT_TEXT, "Freetype2FontInstance::activateSize: FT_Activate_Size failed - error: " << error); assert(false); @@ -217,10 +217,9 @@ namespace ramses { FT_Vector delta; FT_Get_Kerning(m_face, glyphIdentifier1.getValue(), glyphIdentifier2.getValue(), FT_KERNING_DEFAULT, &delta); - return delta.x / 64; + return static_cast(delta.x / 64); } - else - LOG_ERROR(CONTEXT_TEXT, "Freetype2FontInstance::getKerningAdvance: Character not found for kerning; Character codes: " << glyphIdentifier1 << ", " << glyphIdentifier2); + LOG_ERROR(CONTEXT_TEXT, "Freetype2FontInstance::getKerningAdvance: Character not found for kerning; Character codes: " << glyphIdentifier1 << ", " << glyphIdentifier2); } return 0; @@ -282,14 +281,14 @@ namespace ramses } } - std::unordered_set Freetype2FontInstance::getAllSupportedCharacters() + std::unordered_set Freetype2FontInstance::getAllSupportedCharacters() { if (!m_allSupportedCharactersCached) { cacheAllSupportedCharacters(); m_allSupportedCharactersCached = true; } - std::unordered_set allSupportedCharactersSet; + std::unordered_set allSupportedCharactersSet; for (const auto character : m_supportedCharacters) { diff --git a/client/ramses-client/impl/ramses-text/Freetype2FontInstance.h b/src/client/impl/text/Freetype2FontInstance.h similarity index 85% rename from client/ramses-client/impl/ramses-text/Freetype2FontInstance.h rename to src/client/impl/text/Freetype2FontInstance.h index ce3bef69d..111f7c160 100644 --- a/client/ramses-client/impl/ramses-text/Freetype2FontInstance.h +++ b/src/client/impl/text/Freetype2FontInstance.h @@ -6,19 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FREETYPE2FONTINSTANCE_H -#define RAMSES_FREETYPE2FONTINSTANCE_H +#pragma once -#include "ramses-text-api/IFontInstance.h" -#include "ramses-text/Freetype2Wrapper.h" -#include "ramses-text-api/Glyph.h" +#include "ramses/client/text/IFontInstance.h" +#include "impl/text/Freetype2Wrapper.h" +#include "ramses/client/text/Glyph.h" #include #include -namespace ramses +namespace ramses::internal { - class Freetype2Font; - class Freetype2FontInstance : public IFontInstance { public: @@ -32,7 +29,7 @@ namespace ramses void loadAndAppendGlyphMetrics(std::u32string::const_iterator charsBegin, std::u32string::const_iterator charsEnd, GlyphMetricsVector& positionedGlyphs) override; GlyphData loadGlyphBitmapData(GlyphId glyphId, uint32_t& sizeX, uint32_t& sizeY) final override; - std::unordered_set getAllSupportedCharacters() override; + std::unordered_set getAllSupportedCharacters() override; [[nodiscard]] GlyphId getGlyphId(char32_t character) const; @@ -58,8 +55,8 @@ namespace ramses struct GlyphBitmapData { GlyphData data; - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; }; // The reason for separation of the metrics and bitmap data cache @@ -67,11 +64,9 @@ namespace ramses // which needs metrics only. std::unordered_map m_glyphMetricsCache; std::unordered_map m_glyphBitmapCache; - mutable std::unordered_map m_supportedCharacters; + mutable std::unordered_map m_supportedCharacters; private: static constexpr bool IsBidiMarker(char32_t character); }; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/Freetype2Wrapper.h b/src/client/impl/text/Freetype2Wrapper.h similarity index 85% rename from client/ramses-client/impl/ramses-text/Freetype2Wrapper.h rename to src/client/impl/text/Freetype2Wrapper.h index 2a068af6b..f2442af5b 100644 --- a/client/ramses-client/impl/ramses-text/Freetype2Wrapper.h +++ b/src/client/impl/text/Freetype2Wrapper.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FREETYPE2WRAPPER_H -#define RAMSES_FREETYPE2WRAPPER_H +#pragma once -#include "Utils/Warnings.h" +#include "internal/Core/Utils/Warnings.h" PUSH_DISABLE_C_STYLE_CAST_WARNING #include "ft2build.h" @@ -18,5 +17,3 @@ PUSH_DISABLE_C_STYLE_CAST_WARNING #include FT_SIZES_H #include FT_GLYPH_H POP_DISABLE_C_STYLE_CAST_WARNING - -#endif diff --git a/client/ramses-client/impl/ramses-text/FreetypeFontFace.cpp b/src/client/impl/text/FreetypeFontFace.cpp similarity index 90% rename from client/ramses-client/impl/ramses-text/FreetypeFontFace.cpp rename to src/client/impl/text/FreetypeFontFace.cpp index dfd6d304a..de91237a1 100644 --- a/client/ramses-client/impl/ramses-text/FreetypeFontFace.cpp +++ b/src/client/impl/text/FreetypeFontFace.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "FreetypeFontFace.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses_internal +namespace ramses::internal { FreetypeFontFace::FreetypeFontFace(FT_Library freetypeLib) : m_freetypeLib(freetypeLib) @@ -34,7 +34,7 @@ namespace ramses_internal FT_Done_Face(localFaceForQuery); return false; } - else if (localFaceForQuery->num_faces > 1) + if (localFaceForQuery->num_faces > 1) LOG_INFO(CONTEXT_TEXT, "FreetypeFontFace::initFromOpenArgs: current implementation does not support multiple faces, face with index 0 will be used (" << localFaceForQuery->num_faces << " faces found in file)"); // close temporary face @@ -92,13 +92,14 @@ namespace ramses_internal namespace { + // NOLINTNEXTLINE(google-runtime-int): see FT_Stream_IoFunc unsigned long ReadFTStream(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) { assert(stream); assert(stream->descriptor.pointer); - BinaryOffsetFileInputStream* bofis = static_cast(stream->descriptor.pointer); - bofis->seek(offset, IInputStream::Seek::FromBeginning); + auto* bofis = static_cast(stream->descriptor.pointer); + bofis->seek(static_cast(offset), IInputStream::Seek::FromBeginning); if (count > 0) bofis->read(buffer, count); return count; @@ -122,7 +123,7 @@ namespace ramses_internal { // see https://www.freetype.org/freetype2/docs/reference/ft2-system_interface.html#ft_streamrec std::memset(&m_fontStream, 0, sizeof(m_fontStream)); - m_fontStream.size = static_cast(length); + m_fontStream.size = static_cast(length); // NOLINT(google-runtime-int): see FT_StreamRec m_fontStream.read = ReadFTStream; m_fontStream.close = CloseFTStream; m_fontStream.descriptor.pointer = &m_fileStream; diff --git a/client/ramses-client/impl/ramses-text/FreetypeFontFace.h b/src/client/impl/text/FreetypeFontFace.h similarity index 88% rename from client/ramses-client/impl/ramses-text/FreetypeFontFace.h rename to src/client/impl/text/FreetypeFontFace.h index 5071b968c..e62792360 100644 --- a/client/ramses-client/impl/ramses-text/FreetypeFontFace.h +++ b/src/client/impl/text/FreetypeFontFace.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FREETYPEFONTFACE_H -#define RAMSES_FREETYPEFONTFACE_H +#pragma once -#include "ramses-text/Freetype2Wrapper.h" -#include "Utils/BinaryOffsetFileInputStream.h" +#include "impl/text/Freetype2Wrapper.h" +#include "internal/Core/Utils/BinaryOffsetFileInputStream.h" #include #include -namespace ramses_internal +namespace ramses::internal { class FreetypeFontFace { @@ -59,8 +58,6 @@ namespace ramses_internal private: BinaryOffsetFileInputStream m_fileStream; - FT_StreamRec m_fontStream; + FT_StreamRec m_fontStream{}; }; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/GlyphGeometry.h b/src/client/impl/text/GlyphGeometry.h similarity index 84% rename from client/ramses-client/impl/ramses-text/GlyphGeometry.h rename to src/client/impl/text/GlyphGeometry.h index 54eea4144..965394767 100644 --- a/client/ramses-client/impl/ramses-text/GlyphGeometry.h +++ b/src/client/impl/text/GlyphGeometry.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLYPHGEOMETRY_H -#define RAMSES_GLYPHGEOMETRY_H +#pragma once -#include "ramses-framework-api/DataTypes.h" -#include "Utils/AssertMovable.h" +#include "ramses/framework/DataTypes.h" +#include "internal/Core/Utils/AssertMovable.h" #include #include @@ -28,5 +27,3 @@ namespace ramses ASSERT_MOVABLE(GlyphGeometry) } - -#endif diff --git a/client/ramses-client/impl/ramses-text/GlyphMapping.h b/src/client/impl/text/GlyphMapping.h similarity index 89% rename from client/ramses-client/impl/ramses-text/GlyphMapping.h rename to src/client/impl/text/GlyphMapping.h index a8a6e0208..7217de1f1 100644 --- a/client/ramses-client/impl/ramses-text/GlyphMapping.h +++ b/src/client/impl/text/GlyphMapping.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXT_GLYPHMAPPING_H -#define RAMSES_TEXT_GLYPHMAPPING_H +#pragma once -#include "ramses-text/Quad.h" +#include "impl/text/Quad.h" #include namespace ramses @@ -26,5 +25,3 @@ namespace ramses using GlyphMappings = std::unordered_map; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/GlyphTextureAtlas.cpp b/src/client/impl/text/GlyphTextureAtlas.cpp similarity index 82% rename from client/ramses-client/impl/ramses-text/GlyphTextureAtlas.cpp rename to src/client/impl/text/GlyphTextureAtlas.cpp index 4c48eef23..155c8bc08 100644 --- a/client/ramses-client/impl/ramses-text/GlyphTextureAtlas.cpp +++ b/src/client/impl/text/GlyphTextureAtlas.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/GlyphTextureAtlas.h" -#include "Utils/LogMacros.h" +#include "impl/text/GlyphTextureAtlas.h" +#include "internal/Core/Utils/LogMacros.h" #include #include -namespace ramses +namespace ramses::internal { - GlyphTextureAtlas::GlyphTextureAtlas(Scene& scene, QuadSize const& pageSize) + GlyphTextureAtlas::GlyphTextureAtlas(ramses::Scene& scene, QuadSize const& pageSize) : m_scene(scene) , m_pageSize(pageSize) { @@ -185,35 +185,35 @@ namespace ramses // Origin - 0.5, Size + 1: The geometry extends 0.5 texel in each direction. const float minX = static_cast(totalAdvance + glyph.posX) - 0.5f; const float minY = static_cast(glyph.posY) - 0.5f; - const float maxX = minX + glyph.width + 1.0f; - const float maxY = minY + glyph.height + 1.0f; + const float maxX = minX + static_cast(glyph.width) + 1.0f; + const float maxY = minY + static_cast(glyph.height) + 1.0f; - geometry.positions.push_back(vec2f{ minX, minY }); // LowerLeft - geometry.positions.push_back(vec2f{ minX, maxY }); // UpperLeft - geometry.positions.push_back(vec2f{ maxX, maxY }); // UpperRight - geometry.positions.push_back(vec2f{ maxX, minY }); // LowerRight + geometry.positions.emplace_back(minX, minY); // LowerLeft + geometry.positions.emplace_back(minX, maxY); // UpperLeft + geometry.positions.emplace_back(maxX, maxY); // UpperRight + geometry.positions.emplace_back(maxX, minY); // LowerRight const Quad& glyphQuad = m_glyphInfoMap.at(glyphKey).glyphMapping.at(atlasPage).quad; - const float lowerLeftX = glyphQuad.getOrigin().x + 0.5f; - const float lowerLeftY = glyphQuad.getOrigin().y + 0.5f; + const auto lowerLeftX = static_cast(glyphQuad.getOrigin().x) + 0.5f; + const auto lowerLeftY = static_cast(glyphQuad.getOrigin().y) + 0.5f; const QuadSize glyphSize = glyphQuad.getSize(); - const float sizeX = glyphSize.x - 1.0f; - const float sizeY = glyphSize.y - 1.0f; + const auto sizeX = static_cast(glyphSize.x) - 1.0f; + const auto sizeY = static_cast(glyphSize.y) - 1.0f; const float upperRightX = lowerLeftX + sizeX; const float upperRightY = lowerLeftY + sizeY; const uint32_t textureWidth = m_pageSize.x; const uint32_t textureHeight = m_pageSize.y; - const float lowerLeftX_textureSpace = lowerLeftX / textureWidth; - const float lowerLeftY_textureSpace = lowerLeftY / textureHeight; - const float upperRightX_textureSpace = upperRightX / textureWidth; - const float upperRightY_textureSpace = upperRightY / textureHeight; + const auto lowerLeftX_textureSpace = lowerLeftX / static_cast(textureWidth); + const auto lowerLeftY_textureSpace = lowerLeftY / static_cast(textureHeight); + const auto upperRightX_textureSpace = upperRightX / static_cast(textureWidth); + const auto upperRightY_textureSpace = upperRightY / static_cast(textureHeight); - geometry.texcoords.push_back(vec2f{ lowerLeftX_textureSpace, upperRightY_textureSpace }); - geometry.texcoords.push_back(vec2f{ lowerLeftX_textureSpace, lowerLeftY_textureSpace }); - geometry.texcoords.push_back(vec2f{ upperRightX_textureSpace, lowerLeftY_textureSpace }); - geometry.texcoords.push_back(vec2f{ upperRightX_textureSpace, upperRightY_textureSpace }); + geometry.texcoords.emplace_back(lowerLeftX_textureSpace, upperRightY_textureSpace); + geometry.texcoords.emplace_back(lowerLeftX_textureSpace, lowerLeftY_textureSpace); + geometry.texcoords.emplace_back(upperRightX_textureSpace, lowerLeftY_textureSpace); + geometry.texcoords.emplace_back(upperRightX_textureSpace, upperRightY_textureSpace); geometry.indices.push_back(indicesCounter + 2); geometry.indices.push_back(indicesCounter + 1); @@ -246,7 +246,7 @@ namespace ramses } } - const TextureSampler& GlyphTextureAtlas::getTextureSampler(size_t atlasPage) const + const ramses::TextureSampler& GlyphTextureAtlas::getTextureSampler(size_t atlasPage) const { return getPage(atlasPage).getSampler(); } diff --git a/client/ramses-client/impl/ramses-text/GlyphTextureAtlas.h b/src/client/impl/text/GlyphTextureAtlas.h similarity index 90% rename from client/ramses-client/impl/ramses-text/GlyphTextureAtlas.h rename to src/client/impl/text/GlyphTextureAtlas.h index 0585c9dfc..fe6e44c7f 100644 --- a/client/ramses-client/impl/ramses-text/GlyphTextureAtlas.h +++ b/src/client/impl/text/GlyphTextureAtlas.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXT_GLYPHTEXTUREATLAS_H -#define RAMSES_TEXT_GLYPHTEXTUREATLAS_H +#pragma once -#include "ramses-text/GlyphMapping.h" -#include "ramses-text-api/GlyphMetrics.h" -#include "ramses-text/GlyphGeometry.h" -#include "ramses-text/GlyphTexturePage.h" +#include "impl/text/GlyphMapping.h" +#include "ramses/client/text/GlyphMetrics.h" +#include "impl/text/GlyphGeometry.h" +#include "impl/text/GlyphTexturePage.h" #include #include @@ -21,7 +20,10 @@ namespace ramses { class Scene; class TextureSampler; +} +namespace ramses::internal +{ class GlyphTextureAtlas { public: @@ -71,4 +73,4 @@ namespace ramses GlyphInfoMap m_glyphInfoMap; }; } -#endif + diff --git a/client/ramses-client/impl/ramses-text/GlyphTexturePage.cpp b/src/client/impl/text/GlyphTexturePage.cpp similarity index 91% rename from client/ramses-client/impl/ramses-text/GlyphTexturePage.cpp rename to src/client/impl/text/GlyphTexturePage.cpp index d3180f335..4f50d07c8 100644 --- a/client/ramses-client/impl/ramses-text/GlyphTexturePage.cpp +++ b/src/client/impl/text/GlyphTexturePage.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/GlyphTexturePage.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/Texture2DBuffer.h" +#include "impl/text/GlyphTexturePage.h" +#include "ramses/client/Scene.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/Texture2DBuffer.h" #include @@ -22,15 +22,12 @@ namespace { return false; } - else - { - remainingSpaceOut = freeSpace.getArea() - quadToFit.getArea(); - return true; - } + remainingSpaceOut = freeSpace.getArea() - quadToFit.getArea(); + return true; } } -namespace ramses +namespace ramses::internal { GlyphTexturePage::GlyphTexturePage(Scene& scene, const QuadSize& size) : m_size(size) @@ -74,8 +71,8 @@ namespace ramses cacheForDataUpdate.resize(targetQuadArea); } - copyPaddingToCache(targetQuad, cacheForDataUpdate); - copyUpdateDataWithoutPaddingToCache(targetQuad, sourceData, cacheForDataUpdate); + CopyPaddingToCache(targetQuad, cacheForDataUpdate); + CopyUpdateDataWithoutPaddingToCache(targetQuad, sourceData, cacheForDataUpdate); updateTextureResource(targetQuad, cacheForDataUpdate); } @@ -99,7 +96,7 @@ namespace ramses const Quad box = m_freeQuads[freeQuadIndex]; assert(subportionSize.y <= box.getSize().y && subportionSize.x <= box.getSize().x); - m_freeQuads.erase(m_freeQuads.begin() + freeQuadIndex); + m_freeQuads.erase(m_freeQuads.begin() + static_cast(freeQuadIndex)); const uint32_t px = box.getOrigin().x; const uint32_t py = box.getOrigin().y; @@ -150,7 +147,7 @@ namespace ramses // TODO Violin fix this, make it not have an "in and out" parameter bool GlyphTexturePage::mergeFreeQuad(Quad& freeQuadInAndOut) { - const uint32_t n = uint32_t(m_freeQuads.size()); + const auto n = uint32_t(m_freeQuads.size()); for (uint32_t i = 0; i < n; i++) { Quad& box2 = m_freeQuads[i]; @@ -163,7 +160,7 @@ namespace ramses return false; } - void GlyphTexturePage::copyPaddingToCache(const Quad& updateQuad, GlyphPageData& cacheForDataUpdate) + void GlyphTexturePage::CopyPaddingToCache(const Quad& updateQuad, GlyphPageData& cacheForDataUpdate) { const uint32_t targetRowCount = updateQuad.getSize().y; const uint32_t targetColumnCount = updateQuad.getSize().x; @@ -181,7 +178,7 @@ namespace ramses } } - void GlyphTexturePage::copyUpdateDataWithoutPaddingToCache(const Quad& updateQuad, const uint8_t* data, GlyphPageData& cacheForDataUpdate) + void GlyphTexturePage::CopyUpdateDataWithoutPaddingToCache(const Quad& updateQuad, const uint8_t* data, GlyphPageData& cacheForDataUpdate) { const uint32_t targetRowCount = updateQuad.getSize().y; const uint32_t targetColumnCount = updateQuad.getSize().x; @@ -216,7 +213,7 @@ namespace ramses for (GlyphTexturePage::QuadIndex i = 0; i < m_freeQuads.size(); i++) { - uint32_t newScore; + uint32_t newScore = 0; const bool canFit = CanFitQuadInFreeSpace(m_freeQuads[i].getSize(), size, newScore); if (canFit) { diff --git a/client/ramses-client/impl/ramses-text/GlyphTexturePage.h b/src/client/impl/text/GlyphTexturePage.h similarity index 85% rename from client/ramses-client/impl/ramses-text/GlyphTexturePage.h rename to src/client/impl/text/GlyphTexturePage.h index 67dcc1333..ef6d761cb 100644 --- a/client/ramses-client/impl/ramses-text/GlyphTexturePage.h +++ b/src/client/impl/text/GlyphTexturePage.h @@ -6,17 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLYPHTEXTUREPAGE_H -#define RAMSES_GLYPHTEXTUREPAGE_H +#pragma once -#include "ramses-text/Quad.h" +#include "impl/text/Quad.h" namespace ramses { class Scene; class Texture2DBuffer; class TextureSampler; +} +namespace ramses::internal +{ class GlyphTexturePage { public: @@ -44,8 +46,8 @@ namespace ramses private: bool mergeFreeQuad(Quad& freeQuadInAndOut); - void copyPaddingToCache(const Quad& updateQuad, GlyphPageData& cacheForDataUpdate); - void copyUpdateDataWithoutPaddingToCache(const Quad& updateQuad, const uint8_t* data, GlyphPageData& cacheForDataUpdate); + static void CopyPaddingToCache(const Quad& updateQuad, GlyphPageData& cacheForDataUpdate); + static void CopyUpdateDataWithoutPaddingToCache(const Quad& updateQuad, const uint8_t* data, GlyphPageData& cacheForDataUpdate); void updateTextureResource(const Quad& updateQuade, const GlyphPageData& pageData); const QuadSize m_size; @@ -55,5 +57,3 @@ namespace ramses TextureSampler& m_textureSampler; }; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/HarfbuzzFontInstance.cpp b/src/client/impl/text/HarfbuzzFontInstance.cpp similarity index 71% rename from client/ramses-client/impl/ramses-text/HarfbuzzFontInstance.cpp rename to src/client/impl/text/HarfbuzzFontInstance.cpp index ad8b6d4e2..4d017fe04 100644 --- a/client/ramses-client/impl/ramses-text/HarfbuzzFontInstance.cpp +++ b/src/client/impl/text/HarfbuzzFontInstance.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/HarfbuzzFontInstance.h" -#include "Utils/Warnings.h" -#include "Utils/LogMacros.h" +#include "impl/text/HarfbuzzFontInstance.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/Core/Utils/LogMacros.h" #include #include @@ -16,16 +16,35 @@ PUSH_DISABLE_C_STYLE_CAST_WARNING #include POP_DISABLE_C_STYLE_CAST_WARNING -namespace ramses +namespace ramses::internal { static int32_t RoundHBFixedToInt(int32_t fixed) { if (fixed >= 0) return (fixed + 32) / 64; - else - return (fixed - 32) / 64; + return (fixed - 32) / 64; } +#if !defined(USE_HARFBUZZ_LEGACY_SHAPING) + static hb_script_t guessScript(hb_unicode_funcs_t* unicodeFuncs, std::u32string::const_iterator charIt, std::u32string::const_iterator charsEnd) + { + hb_script_t result = HB_SCRIPT_COMMON; + + while (charIt != charsEnd) + { + const hb_script_t hbScript = hb_unicode_script(unicodeFuncs, *charIt); + if ((HB_SCRIPT_COMMON != hbScript) && (HB_SCRIPT_INHERITED != hbScript) && (HB_SCRIPT_UNKNOWN != hbScript)) + { + result = hbScript; + break; + } + ++charIt; + } + + return result; + } +#endif + HarfbuzzFontInstance::HarfbuzzFontInstance(FontInstanceId id, FT_Face fontFace, uint32_t pixelSize, bool forceAutohinting) : Freetype2FontInstance(id, fontFace, pixelSize, forceAutohinting) { @@ -73,6 +92,21 @@ namespace ramses // Always take LTR direction. With that Arabic reshaping works, but char codes must already come in the swapped order. hb_buffer_set_direction(m_hbBuffer, HB_DIRECTION_LTR); +#if !defined(USE_HARFBUZZ_LEGACY_SHAPING) + // leading INHERITED, COMMON or UNKNOWN scripts have to be replaced by the first dedicated script (main script) + // to allow contextual shaping: otherwise the main script characters created a buffer containing 1 character + const hb_script_t hbScript = guessScript(unicodeFuncs, charIt, charsEnd); + hb_buffer_set_script(m_hbBuffer, hbScript); + + // add chars using same script to buffer + // - include chars with COMMON property to kern spaces, solidus, minus, etc. correct (8000+ characters) + // - include chars with INHERITED property to shape diacritics correct (600+ characters) + // - include chars with UNKNOWN property (private use area) + while (charIt != charsEnd) + { + const hb_script_t cpScript = hb_unicode_script(unicodeFuncs, *charIt); + if ((cpScript == hbScript) || (HB_SCRIPT_COMMON == cpScript) || (HB_SCRIPT_INHERITED == cpScript) || (HB_SCRIPT_UNKNOWN == cpScript)) +#else const hb_script_t hbScript = hb_unicode_script(unicodeFuncs, *charIt); hb_buffer_set_script(m_hbBuffer, hbScript); @@ -80,6 +114,7 @@ namespace ramses while (charIt != charsEnd) { if (hb_unicode_script(unicodeFuncs, *charIt) == hbScript) +#endif { hb_buffer_add(m_hbBuffer, *charIt, clusterIdx); charIt++; @@ -90,15 +125,15 @@ namespace ramses hb_shape(m_hbFont, m_hbBuffer, nullptr, 0); - uint32_t glyphCount; + uint32_t glyphCount = 0; hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(m_hbBuffer, &glyphCount); - uint32_t glyphPosCount; + uint32_t glyphPosCount = 0; hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(m_hbBuffer, &glyphPosCount); assert(glyphPosCount == glyphCount); for (uint32_t i = 0; i < glyphCount; i++) { - HBGlyphInfo glyphInfo; + HBGlyphInfo glyphInfo{}; glyphInfo.glyphIndex = glyph_info[i].codepoint; glyphInfo.advance = RoundHBFixedToInt(glyph_pos[i].x_advance); glyphInfo.offsetX = RoundHBFixedToInt(glyph_pos[i].x_offset); @@ -145,9 +180,9 @@ namespace ramses void HarfbuzzFontInstance::activateHBFontSize() { assert(m_hbFont != nullptr); - const uint64_t x_scale = static_cast(m_size->metrics.x_scale); - const uint64_t y_scale = static_cast(m_size->metrics.y_scale); - const uint64_t units_per_EM = static_cast(m_face->units_per_EM); + const auto x_scale = static_cast(m_size->metrics.x_scale); + const auto y_scale = static_cast(m_size->metrics.y_scale); + const auto units_per_EM = static_cast(m_face->units_per_EM); // Setting the font size to the hb font, see hb_ft_font_create(). // Needed for correct scaled offset values. diff --git a/client/ramses-client/impl/ramses-text/HarfbuzzFontInstance.h b/src/client/impl/text/HarfbuzzFontInstance.h similarity index 90% rename from client/ramses-client/impl/ramses-text/HarfbuzzFontInstance.h rename to src/client/impl/text/HarfbuzzFontInstance.h index 41c3768d3..720e50729 100644 --- a/client/ramses-client/impl/ramses-text/HarfbuzzFontInstance.h +++ b/src/client/impl/text/HarfbuzzFontInstance.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_HARFBUZZFONTINSTANCE_H -#define RAMSES_HARFBUZZFONTINSTANCE_H +#pragma once -#include "ramses-text/Freetype2FontInstance.h" +#include "impl/text/Freetype2FontInstance.h" struct hb_font_t; struct hb_buffer_t; -namespace ramses +namespace ramses::internal { class HarfbuzzFontInstance final : public Freetype2FontInstance { @@ -36,5 +35,3 @@ namespace ramses hb_buffer_t* m_hbBuffer = nullptr; }; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/LayoutUtils.cpp b/src/client/impl/text/LayoutUtils.cpp similarity index 98% rename from client/ramses-client/impl/ramses-text/LayoutUtils.cpp rename to src/client/impl/text/LayoutUtils.cpp index da8e797ac..f8330bdca 100644 --- a/client/ramses-client/impl/ramses-text/LayoutUtils.cpp +++ b/src/client/impl/text/LayoutUtils.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/LayoutUtils.h" +#include "ramses/client/text/LayoutUtils.h" #include namespace ramses diff --git a/client/ramses-client/impl/ramses-text/Quad.h b/src/client/impl/text/Quad.h similarity index 98% rename from client/ramses-client/impl/ramses-text/Quad.h rename to src/client/impl/text/Quad.h index 0cec8b3e6..0a86178d9 100644 --- a/client/ramses-client/impl/ramses-text/Quad.h +++ b/src/client/impl/text/Quad.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXT_QUAD_H -#define RAMSES_TEXT_QUAD_H +#pragma once #include #include @@ -141,5 +140,3 @@ namespace ramses using Quads = std::vector; } - -#endif diff --git a/client/ramses-text-api/TextCache.cpp b/src/client/impl/text/TextCache.cpp similarity index 92% rename from client/ramses-text-api/TextCache.cpp rename to src/client/impl/text/TextCache.cpp index 92662bd19..3237b79f1 100644 --- a/client/ramses-text-api/TextCache.cpp +++ b/src/client/impl/text/TextCache.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/TextCache.h" -#include "ramses-text/TextCacheImpl.h" +#include "ramses/client/text/TextCache.h" +#include "impl/text/TextCacheImpl.h" namespace ramses { TextCache::TextCache(Scene& scene, IFontAccessor& fontAccessor, uint32_t atlasTextureWidth, uint32_t atlasTextureHeight) - : impl(new TextCacheImpl(scene, fontAccessor, atlasTextureWidth, atlasTextureHeight)) + : impl(new internal::TextCacheImpl(scene, fontAccessor, atlasTextureWidth, atlasTextureHeight)) { } diff --git a/client/ramses-client/impl/ramses-text/TextCacheImpl.cpp b/src/client/impl/text/TextCacheImpl.cpp similarity index 77% rename from client/ramses-client/impl/ramses-text/TextCacheImpl.cpp rename to src/client/impl/text/TextCacheImpl.cpp index c7621f1cb..6b7eaf922 100644 --- a/client/ramses-client/impl/ramses-text/TextCacheImpl.cpp +++ b/src/client/impl/text/TextCacheImpl.cpp @@ -6,27 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/TextCacheImpl.h" -#include "ramses-text-api/TextLine.h" -#include "ramses-text-api/TextCache.h" -#include "ramses-text-api/IFontAccessor.h" -#include "ramses-text-api/IFontInstance.h" - -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" - -#include "Utils/LogMacros.h" -#include "RamsesFrameworkTypesImpl.h" -#include "ramses-text/TextTypesImpl.h" +#include "impl/text/TextCacheImpl.h" +#include "ramses/client/text/TextLine.h" +#include "ramses/client/text/TextCache.h" +#include "ramses/client/text/IFontAccessor.h" +#include "ramses/client/text/IFontInstance.h" + +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" + +#include "internal/Core/Utils/LogMacros.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/text/TextTypesImpl.h" #include -namespace ramses +namespace ramses::internal { TextCacheImpl::TextCacheImpl(Scene& scene, IFontAccessor& fontAccessor, uint32_t atlasTextureWidth, uint32_t atlasTextureHeight) : m_scene(scene) @@ -42,16 +42,20 @@ namespace ramses for (auto fontIt = fontOffsets.cbegin(); fontIt != fontOffsets.cend(); ++fontIt) { - const auto substrBeginIt = std::min(str.cbegin() + fontIt->beginOffset, str.cend()); + const auto substrBeginIt = std::min(str.cbegin() + static_cast(fontIt->beginOffset), str.cend()); const auto nextFontIt = std::next(fontIt); - auto substrEndIt = (nextFontIt != fontOffsets.cend() ? str.cbegin() + nextFontIt->beginOffset : str.cend()); + auto substrEndIt = (nextFontIt != fontOffsets.cend() ? str.cbegin() + static_cast(nextFontIt->beginOffset) : str.cend()); substrEndIt = std::min(substrEndIt, str.cend()); IFontInstance* fontInstance = m_fontAccessor.getFontInstance(fontIt->fontInstance); if (fontInstance != nullptr) + { fontInstance->loadAndAppendGlyphMetrics(substrBeginIt, substrEndIt, positionedGlyphs); + } else + { LOG_ERROR(CONTEXT_TEXT, "TextCache::getPositionedGlyphs: Could not find font instance " << fontIt->fontInstance); + } } return positionedGlyphs; @@ -70,13 +74,11 @@ namespace ramses return {}; } - UniformInput texInput; - AttributeInput posInput; - AttributeInput texCoordInput; - effect.findUniformInput(EEffectUniformSemantic::TextTexture, texInput); - effect.findAttributeInput(EEffectAttributeSemantic::TextPositions, posInput); - effect.findAttributeInput(EEffectAttributeSemantic::TextTextureCoordinates, texCoordInput); - if (!texInput.isValid() || !posInput.isValid() || !texCoordInput.isValid()) + const auto texInput = effect.findUniformInput(EEffectUniformSemantic::TextTexture); + const auto posInput = effect.findAttributeInput(EEffectAttributeSemantic::TextPositions); + const auto texCoordInput = effect.findAttributeInput(EEffectAttributeSemantic::TextTextureCoordinates); + + if (!texInput.has_value() || !posInput.has_value() || !texCoordInput.has_value()) { LOG_ERROR(CONTEXT_TEXT, "TextCache::createTextLine failed - text appearance effect must provide inputs for positions and coordinates attributes and a texture uniform"); return {}; @@ -113,7 +115,7 @@ namespace ramses return {}; } - GeometryBinding* geometryBinding = m_scene.createGeometryBinding(effect); + Geometry* geometryBinding = m_scene.createGeometry(effect); Appearance* appearance = m_scene.createAppearance(effect); if (geometryBinding == nullptr || appearance == nullptr) { @@ -130,11 +132,11 @@ namespace ramses textLine.glyphs = glyphs; textLine.meshNode = m_scene.createMeshNode(); - const uint32_t numIndices = static_cast(geometry.indices.size()); + const auto numIndices = static_cast(geometry.indices.size()); textLine.indices = m_scene.createArrayBuffer(ramses::EDataType::UInt16, numIndices, ""); textLine.indices->updateData(0u, numIndices, geometry.indices.data()); - const uint32_t numVertexElements = static_cast(geometry.positions.size()); + const auto numVertexElements = static_cast(geometry.positions.size()); textLine.positions = m_scene.createArrayBuffer(ramses::EDataType::Vector2F, numVertexElements, ""); textLine.positions->updateData(0u, numVertexElements, geometry.positions.data()); @@ -145,13 +147,13 @@ namespace ramses textLine.meshNode->setIndexCount(numIndices); geometryBinding->setIndices(*textLine.indices); - geometryBinding->setInputBuffer(posInput, *textLine.positions); - geometryBinding->setInputBuffer(texCoordInput, *textLine.textureCoordinates); + geometryBinding->setInputBuffer(*posInput, *textLine.positions); + geometryBinding->setInputBuffer(*texCoordInput, *textLine.textureCoordinates); - appearance->setInputTexture(texInput, m_textureAtlas.getTextureSampler(geometry.atlasPage)); + appearance->setInputTexture(*texInput, m_textureAtlas.getTextureSampler(geometry.atlasPage)); textLine.meshNode->setAppearance(*appearance); - textLine.meshNode->setGeometryBinding(*geometryBinding); + textLine.meshNode->setGeometry(*geometryBinding); return textLineId; } @@ -177,7 +179,7 @@ namespace ramses } TextLine& textLine = m_textLines[textId]; - auto geometry = textLine.meshNode->getGeometryBinding(); + auto geometry = textLine.meshNode->getGeometry(); auto appearance = textLine.meshNode->getAppearance(); m_scene.destroy(*textLine.meshNode); m_scene.destroy(*geometry); diff --git a/client/ramses-client/impl/ramses-text/TextCacheImpl.h b/src/client/impl/text/TextCacheImpl.h similarity index 83% rename from client/ramses-client/impl/ramses-text/TextCacheImpl.h rename to src/client/impl/text/TextCacheImpl.h index 3c81e355d..24533060c 100644 --- a/client/ramses-client/impl/ramses-text/TextCacheImpl.h +++ b/src/client/impl/text/TextCacheImpl.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTCACHEIMPL_H -#define RAMSES_TEXTCACHEIMPL_H +#pragma once -#include "ramses-text/GlyphTextureAtlas.h" -#include "ramses-text-api/TextLine.h" -#include "ramses-text-api/FontInstanceOffsets.h" +#include "impl/text/GlyphTextureAtlas.h" +#include "ramses/client/text/TextLine.h" +#include "ramses/client/text/FontInstanceOffsets.h" #include #include @@ -21,11 +20,14 @@ namespace ramses class MeshNode; class Effect; class IFontAccessor; +} +namespace ramses::internal +{ class TextCacheImpl { public: - TextCacheImpl(Scene& scene, IFontAccessor& fontAccessor, uint32_t atlasTextureWidth, uint32_t atlasTextureHeight); + TextCacheImpl(ramses::Scene& scene, IFontAccessor& fontAccessor, uint32_t atlasTextureWidth, uint32_t atlasTextureHeight); ~TextCacheImpl() = default; GlyphMetricsVector getPositionedGlyphs(const std::u32string& str, FontInstanceId font); @@ -42,7 +44,7 @@ namespace ramses TextCacheImpl& operator=(TextCacheImpl&&) = delete; private: - Scene& m_scene; + ramses::Scene& m_scene; IFontAccessor& m_fontAccessor; GlyphTextureAtlas m_textureAtlas; @@ -52,5 +54,3 @@ namespace ramses TextLineId m_textIdCounter{ 0u }; }; } - -#endif diff --git a/client/ramses-client/impl/ramses-text/TextTypesImpl.h b/src/client/impl/text/TextTypesImpl.h similarity index 74% rename from client/ramses-client/impl/ramses-text/TextTypesImpl.h rename to src/client/impl/text/TextTypesImpl.h index 0f4ddb55f..af7b89101 100644 --- a/client/ramses-client/impl/ramses-text/TextTypesImpl.h +++ b/src/client/impl/text/TextTypesImpl.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXT_TEXTTYPESIMPL_H -#define RAMSES_TEXT_TEXTTYPESIMPL_H +#pragma once -#include "ramses-text-api/FontInstanceId.h" -#include "ramses-text-api/Glyph.h" -#include "ramses-text-api/TextLine.h" -#include "Common/StronglyTypedValue.h" +#include "ramses/client/text/FontInstanceId.h" +#include "ramses/client/text/Glyph.h" +#include "ramses/client/text/TextLine.h" +#include "internal/Core/Common/StronglyTypedValue.h" MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::FontInstanceId); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::GlyphId); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::TextLineId); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::FontId); - -#endif diff --git a/client/ramses-text-api/UtfUtils.cpp b/src/client/impl/text/UtfUtils.cpp similarity index 85% rename from client/ramses-text-api/UtfUtils.cpp rename to src/client/impl/text/UtfUtils.cpp index 0e9be1bb5..f5ed91676 100644 --- a/client/ramses-text-api/UtfUtils.cpp +++ b/src/client/impl/text/UtfUtils.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/UtfUtils.h" -#include "Utils/LogMacros.h" +#include "ramses/client/text/UtfUtils.h" +#include "internal/Core/Utils/LogMacros.h" #include #include @@ -51,6 +51,8 @@ namespace ramses case 2: return codepoint < 0x0080; case 3: return codepoint < 0x0800; case 4: return codepoint < 0x10000; + default: + break; } return false; } @@ -87,7 +89,7 @@ namespace ramses if (extractedCodepoint.extractionSuccessful) utf32String.push_back(extractedCodepoint.codePoint); - strIt += extractedCodepoint.inputCharsConsumed; + strIt += static_cast(extractedCodepoint.inputCharsConsumed); } return utf32String; @@ -188,13 +190,10 @@ namespace ramses result.codePoint = currentByte; break; } - else - { - //2 bytes -> last 5 bits are relevant; 3 bytes -> last 4 bits, etc. - const uint8_t mask = (1u << (7u - trailingBytesRemaining)) - 1u; - result.codePoint = currentByte & mask; - currentCharacterNumBytes = trailingBytesRemaining + 1; - } + //2 bytes -> last 5 bits are relevant; 3 bytes -> last 4 bits, etc. + const uint8_t mask = (1u << (7u - trailingBytesRemaining)) - 1u; + result.codePoint = currentByte & mask; + currentCharacterNumBytes = trailingBytesRemaining + 1; } else { @@ -208,9 +207,13 @@ namespace ramses if (UtfUtils::IsCodePointValid(result.codePoint)) { if (!UtfUtils::IsCodePointOverlongUTF8Encoded(result.codePoint, currentCharacterNumBytes)) + { bSuccess = true; + } else + { LOG_ERROR(CONTEXT_TEXT, "UtfUtils::GetNextUTF8CharacterFromStream: overlong encoded UTF-8 character " << result.codePoint); + } } else LOG_ERROR(CONTEXT_TEXT, "UtfUtils::GetNextUTF8CharacterFromStream invalid UTF-8 code point " << result.codePoint); @@ -263,35 +266,30 @@ namespace ramses } break; } - else + + if (UtfUtils::IsUTF16HighSurrogate(currentWord)) { - if (UtfUtils::IsUTF16HighSurrogate(currentWord)) + if (lowSurrogateExpected) { - if (lowSurrogateExpected) - { - LOG_ERROR(CONTEXT_TEXT, "UtfUtils::ExtractUnicodePointFromUTF16Array: unexpected UTF-16 high surrogate " << currentWord << " in string, low surrogate was expected"); - // TODO Violin this looks quite weird, check if it is correct to "unread" one word - --strBegin; - result.extractionSuccessful = false; - break; - } - result.codePoint = (currentWord - UtfUtils::cUniSurHighStart) << 10; - lowSurrogateExpected = true; + LOG_ERROR(CONTEXT_TEXT, "UtfUtils::ExtractUnicodePointFromUTF16Array: unexpected UTF-16 high surrogate " << currentWord << " in string, low surrogate was expected"); + // TODO Violin this looks quite weird, check if it is correct to "unread" one word + --strBegin; + result.extractionSuccessful = false; + break; } - else + result.codePoint = (currentWord - UtfUtils::cUniSurHighStart) << 10; + lowSurrogateExpected = true; + } + else + { + if (!lowSurrogateExpected) { - if (!lowSurrogateExpected) - { - LOG_ERROR(CONTEXT_TEXT, "UtfUtils::ExtractUnicodePointFromUTF16Array: unexpected UTF-16 low surrogate " << currentWord << " in string, no high surrogate"); - result.extractionSuccessful = false; - break; - } - else - { - result.codePoint += currentWord - UtfUtils::cUniSurLowStart + UtfUtils::cHalfBase; - break; - } + LOG_ERROR(CONTEXT_TEXT, "UtfUtils::ExtractUnicodePointFromUTF16Array: unexpected UTF-16 low surrogate " << currentWord << " in string, no high surrogate"); + result.extractionSuccessful = false; + break; } + result.codePoint += currentWord - UtfUtils::cUniSurLowStart + UtfUtils::cHalfBase; + break; } } result.inputCharsConsumed = std::distance(origBegin, strBegin); diff --git a/client/ramses-client/impl/ClientApplicationLogic.cpp b/src/client/internal/ClientApplicationLogic.cpp similarity index 85% rename from client/ramses-client/impl/ClientApplicationLogic.cpp rename to src/client/internal/ClientApplicationLogic.cpp index ce454b042..bddb0897b 100644 --- a/client/ramses-client/impl/ClientApplicationLogic.cpp +++ b/src/client/internal/ClientApplicationLogic.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ClientApplicationLogic.h" -#include "Components/IResourceProviderComponent.h" -#include "Components/ISceneGraphProviderComponent.h" -#include "Components/ResourceTableOfContents.h" -#include "Scene/ClientScene.h" -#include "RamsesFrameworkImpl.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/PlatformLock.h" - -namespace ramses_internal +#include "internal/ClientApplicationLogic.h" +#include "internal/Components/IResourceProviderComponent.h" +#include "internal/Components/ISceneGraphProviderComponent.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "impl/RamsesFrameworkImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformLock.h" + +namespace ramses::internal { ClientApplicationLogic::ClientApplicationLogic(const Guid& myId, PlatformLock& frameworkLock) : m_frameworkLock(frameworkLock) @@ -25,9 +25,7 @@ namespace ramses_internal { } - ClientApplicationLogic::~ClientApplicationLogic() - { - } + ClientApplicationLogic::~ClientApplicationLogic() = default; void ClientApplicationLogic::init(IResourceProviderComponent& resources, ISceneGraphProviderComponent& scenegraph) { @@ -87,19 +85,19 @@ namespace ramses_internal m_sceneReferenceEventVec.push_back(event); } - ramses_internal::ManagedResource ClientApplicationLogic::addResource(const IResource* resource) + ManagedResource ClientApplicationLogic::addResource(const IResource* resource) { PlatformGuard guard(m_frameworkLock); return m_resourceComponent->manageResource(*resource); } - ramses_internal::ManagedResource ClientApplicationLogic::getResource(ResourceContentHash hash) const + ManagedResource ClientApplicationLogic::getResource(ResourceContentHash hash) const { PlatformGuard guard(m_frameworkLock); return m_resourceComponent->getResource(hash); } - ramses_internal::ResourceHashUsage ClientApplicationLogic::getHashUsage(const ResourceContentHash& hash) const + ResourceHashUsage ClientApplicationLogic::getHashUsage(const ResourceContentHash& hash) const { PlatformGuard guard(m_frameworkLock); return m_resourceComponent->getResourceHashUsage(hash); @@ -134,10 +132,10 @@ namespace ramses_internal m_resourceComponent->reserveResourceCount(totalCount); } - std::vector ClientApplicationLogic::popSceneReferenceEvents() + std::vector ClientApplicationLogic::popSceneReferenceEvents() { PlatformGuard guard(m_frameworkLock); - std::vector ret; + std::vector ret; m_sceneReferenceEventVec.swap(ret); return ret; } diff --git a/client/ramses-client/impl/ClientApplicationLogic.h b/src/client/internal/ClientApplicationLogic.h similarity index 78% rename from client/ramses-client/impl/ClientApplicationLogic.h rename to src/client/internal/ClientApplicationLogic.h index 2c07ebacd..675076885 100644 --- a/client/ramses-client/impl/ClientApplicationLogic.h +++ b/src/client/internal/ClientApplicationLogic.h @@ -6,28 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTAPPLICATIONLOGIC_H -#define RAMSES_CLIENTAPPLICATIONLOGIC_H - -#include "Components/ManagedResource.h" -#include "Components/ResourceHashUsage.h" -#include "Components/InputStreamContainer.h" -#include "Components/ISceneProviderEventConsumer.h" -#include "Components/SceneFileHandle.h" - -#include "SceneAPI/SceneVersionTag.h" -#include "Scene/EScenePublicationMode.h" -#include "Collections/HashSet.h" -#include "Collections/Guid.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "SceneReferencing/SceneReferenceEvent.h" - -namespace ramses -{ - class RamsesFrameworkImpl; -} - -namespace ramses_internal +#pragma once + +#include "internal/Components/ManagedResource.h" +#include "internal/Components/ResourceHashUsage.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Components/ISceneProviderEventConsumer.h" +#include "internal/Components/SceneFileHandle.h" + +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" + +namespace ramses::internal { class ClientScene; class ResourceTableOfContents; @@ -35,6 +29,7 @@ namespace ramses_internal class IResourceProviderComponent; class ISceneGraphProviderComponent; struct FlushTimeInformation; + class RamsesFrameworkImpl; class ClientApplicationLogic : public ISceneProviderEventConsumer { @@ -68,7 +63,7 @@ namespace ramses_internal void reserveResourceCount(uint32_t totalCount); [[nodiscard]] bool hasResourceFile(SceneFileHandle handle) const; - std::vector popSceneReferenceEvents(); + std::vector popSceneReferenceEvents(); private: PlatformLock& m_frameworkLock; @@ -79,8 +74,6 @@ namespace ramses_internal HashSet m_publishedScenes; - std::vector m_sceneReferenceEventVec; + std::vector m_sceneReferenceEventVec; }; } - -#endif diff --git a/src/client/internal/ClientCommands/DumpSceneToFile.cpp b/src/client/internal/ClientCommands/DumpSceneToFile.cpp new file mode 100644 index 000000000..5c408eca1 --- /dev/null +++ b/src/client/internal/ClientCommands/DumpSceneToFile.cpp @@ -0,0 +1,83 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "DumpSceneToFile.h" +#include "impl/RamsesClientImpl.h" +#include "SceneCommandBuffer.h" + +namespace ramses::internal +{ + DumpSceneToFile::DumpSceneToFile(ramses::internal::RamsesClientImpl& client) + : m_client(client) + { + description = "Usage: [-sendViaDLT] [-flush] sceneId [filename] - dump scene to file or DLT"; + registerKeyword("dumpSceneToFile"); + registerKeyword("dumpScene"); + } + + bool DumpSceneToFile::executeInput(const std::vector& input) + { + std::vector arguments; + SceneCommandDumpSceneToFile command{}; + bool flush = false; + + for (auto& str : input) + { + if (str == "-sendViaDLT") + { + command.sendViaDLT = true; + } + else if (str == "-flush") + { + flush = true; + } + else + { + arguments.push_back(str); + } + } + + switch (arguments.size()) + { + case 3: + command.fileName = arguments[2]; + break; + case 2: + if (!command.sendViaDLT) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Expected [filename] or -sendViaDlt"); + return false; + } + break; + default: + LOG_ERROR_P(CONTEXT_RAMSH, "None or too many arguments provided: {}", arguments.size() - 1); + return false; + } + + ramses::sceneId_t sceneId; + ramses::internal::ArgumentConverter::tryConvert(arguments[1], sceneId.getReference()); + if (!sceneId.isValid()) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Invalid SceneId: {}", arguments[0]); + return false; + } + + m_client.enqueueSceneCommand(ramses::sceneId_t(sceneId), std::move(command)); + if (flush) + { + auto* scene = m_client.getScene(sceneId); + if (!scene) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Scene not available: {}", sceneId); + return false; + } + scene->flush(); + } + return true; + } +} diff --git a/client/logic/lib/internals/ValidationResults.h b/src/client/internal/ClientCommands/DumpSceneToFile.h similarity index 58% rename from client/logic/lib/internals/ValidationResults.h rename to src/client/internal/ClientCommands/DumpSceneToFile.h index 08e0248d4..bc5e93fe0 100644 --- a/client/logic/lib/internals/ValidationResults.h +++ b/src/client/internal/ClientCommands/DumpSceneToFile.h @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------- -// Copyright (C) 2022 BMW AG +// Copyright (C) 2017 BMW Car IT GmbH // ------------------------------------------------------------------------- // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,21 +8,22 @@ #pragma once -#include "ramses-logic/WarningData.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -#include #include namespace ramses::internal { - class ValidationResults + class RamsesClientImpl; + + class DumpSceneToFile : public RamshCommand { public: - void clear(); - void add(std::string warningMessage, const LogicObject* logicObject, EWarningType type); - [[nodiscard]] const std::vector& getWarnings() const; + explicit DumpSceneToFile(RamsesClientImpl& client); + bool executeInput(const std::vector& input) override; private: - std::vector m_warnings; + RamsesClientImpl& m_client; }; } diff --git a/client/ramses-client/impl/ClientCommands/FlushSceneVersion.cpp b/src/client/internal/ClientCommands/FlushSceneVersion.cpp similarity index 74% rename from client/ramses-client/impl/ClientCommands/FlushSceneVersion.cpp rename to src/client/internal/ClientCommands/FlushSceneVersion.cpp index 34b815c16..c0aadb0e2 100644 --- a/client/ramses-client/impl/ClientCommands/FlushSceneVersion.cpp +++ b/src/client/internal/ClientCommands/FlushSceneVersion.cpp @@ -7,12 +7,12 @@ // ------------------------------------------------------------------------- #include "FlushSceneVersion.h" -#include "RamsesClientImpl.h" +#include "impl/RamsesClientImpl.h" #include "SceneCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { - FlushSceneVersion::FlushSceneVersion(ramses::RamsesClientImpl& client) + FlushSceneVersion::FlushSceneVersion(RamsesClientImpl& client) : m_client(client) { description = "add scene version to next flush"; @@ -21,12 +21,12 @@ namespace ramses_internal getArgument<1>().setDescription("scene id"); } - bool FlushSceneVersion::execute(ramses::sceneVersionTag_t& sceneVersion, uint64_t& sceneId) const + bool FlushSceneVersion::execute(sceneVersionTag_t& sceneVersion, uint64_t& sceneId) const { SceneCommandFlushSceneVersion command; command.sceneVersion = sceneVersion; - m_client.enqueueSceneCommand(ramses::sceneId_t(sceneId), std::move(command)); + m_client.enqueueSceneCommand(sceneId_t(sceneId), std::move(command)); return true; } } diff --git a/src/client/internal/ClientCommands/FlushSceneVersion.h b/src/client/internal/ClientCommands/FlushSceneVersion.h new file mode 100644 index 000000000..18ae9c0b4 --- /dev/null +++ b/src/client/internal/ClientCommands/FlushSceneVersion.h @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Ramsh/RamshCommandArguments.h" +#include "ramses/framework/RamsesFrameworkTypes.h" + +namespace ramses::internal +{ + class RamsesClientImpl; + + class FlushSceneVersion : public RamshCommandArgs + { + public: + explicit FlushSceneVersion(RamsesClientImpl& client); + bool execute(sceneVersionTag_t& sceneVersion, uint64_t& sceneId) const override; + + private: + RamsesClientImpl& m_client; + }; +} diff --git a/client/ramses-client/impl/ClientCommands/LogMemoryUtils.cpp b/src/client/internal/ClientCommands/LogMemoryUtils.cpp similarity index 72% rename from client/ramses-client/impl/ClientCommands/LogMemoryUtils.cpp rename to src/client/internal/ClientCommands/LogMemoryUtils.cpp index c0b9fe452..96cba9e96 100644 --- a/client/ramses-client/impl/ClientCommands/LogMemoryUtils.cpp +++ b/src/client/internal/ClientCommands/LogMemoryUtils.cpp @@ -7,39 +7,39 @@ // ------------------------------------------------------------------------- #include "LogMemoryUtils.h" -#include "ResourceImpl.h" -#include "RamsesClientImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "ramses-client-api/Resource.h" -#include "SceneAPI/Camera.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/RenderPass.h" -#include "SceneAPI/RenderGroup.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/TextureBuffer.h" -#include "SceneAPI/GeometryDataBuffer.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/RenderTarget.h" -#include "SceneAPI/BlitPass.h" -#include "Scene/ClientScene.h" -#include "Scene/TopologyNode.h" -#include "Scene/TopologyTransform.h" -#include "Scene/DataLayout.h" -#include "Scene/DataInstance.h" +#include "impl/ResourceImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "ramses/client/Resource.h" +#include "internal/SceneGraph/SceneAPI/Camera.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/RenderPass.h" +#include "internal/SceneGraph/SceneAPI/RenderGroup.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/TextureBuffer.h" +#include "internal/SceneGraph/SceneAPI/GeometryDataBuffer.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/RenderTarget.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/TopologyNode.h" +#include "internal/SceneGraph/Scene/TopologyTransform.h" +#include "internal/SceneGraph/Scene/DataLayout.h" +#include "internal/SceneGraph/Scene/DataInstance.h" -namespace ramses_internal +namespace ramses::internal { - MemoryInfoVector GetMemoryInfoFromScene(const ramses::SceneImpl& scene) + MemoryInfoVector GetMemoryInfoFromScene(const SceneImpl& scene) { MemoryInfoVector memoryInfos; - ramses::RamsesObjectVector resources; - scene.getObjectRegistry().getObjectsOfType(resources, ramses::ERamsesObjectType::Resource); + SceneObjectVector resources; + scene.getObjectRegistry().getObjectsOfType(resources, ERamsesObjectType::Resource); for (const auto it : resources) { - const ramses::Resource& resource = ramses::RamsesObjectTypeUtils::ConvertTo(*it); - const IResource* resourceObject = scene.getClientImpl().getResource(resource.m_impl.getLowlevelResourceHash()).get(); + const Resource& resource = RamsesObjectTypeUtils::ConvertTo(*it); + const IResource* resourceObject = scene.getClientImpl().getResource(resource.impl().getLowlevelResourceHash()).get(); if (nullptr != resourceObject) { @@ -72,14 +72,14 @@ namespace ramses_internal const ClientScene& iscene = scene.getIScene(); - memoryInfos.push_back(createMemInfo("Cameras", iscene.getCameraCount(), [](uint32_t){return sizeof(Camera);})); - memoryInfos.push_back(createMemInfo("Renderables", iscene.getRenderableCount(), [](uint32_t){return sizeof(Renderable);})); - memoryInfos.push_back(createMemInfo("RenderStates", iscene.getRenderStateCount(), [](uint32_t){return sizeof(RenderState);})); - memoryInfos.push_back(createMemInfo("Transforms", iscene.getTransformCount(), [](uint32_t){return sizeof(TopologyTransform);})); - memoryInfos.push_back(createMemInfo("BlitPasses", iscene.getBlitPassCount(), [](uint32_t){return sizeof(BlitPass);})); - memoryInfos.push_back(createMemInfo("RenderBuffers", iscene.getRenderBufferCount(), [](uint32_t){return sizeof(RenderBuffer);})); - memoryInfos.push_back(createMemInfo("TextureSamplers", iscene.getTextureSamplerCount(), [](uint32_t){return sizeof(TextureSampler);})); - memoryInfos.push_back(createMemInfo("DataSlots", iscene.getDataSlotCount(), [](uint32_t){return sizeof(DataSlot);})); + memoryInfos.push_back(createMemInfo("Cameras", iscene.getCameraCount(), [](uint32_t /*unused*/){return sizeof(Camera);})); + memoryInfos.push_back(createMemInfo("Renderables", iscene.getRenderableCount(), [](uint32_t /*unused*/){return sizeof(Renderable);})); + memoryInfos.push_back(createMemInfo("RenderStates", iscene.getRenderStateCount(), [](uint32_t /*unused*/){return sizeof(RenderState);})); + memoryInfos.push_back(createMemInfo("Transforms", iscene.getTransformCount(), [](uint32_t /*unused*/){return sizeof(TopologyTransform);})); + memoryInfos.push_back(createMemInfo("BlitPasses", iscene.getBlitPassCount(), [](uint32_t /*unused*/){return sizeof(BlitPass);})); + memoryInfos.push_back(createMemInfo("RenderBuffers", iscene.getRenderBufferCount(), [](uint32_t /*unused*/){return sizeof(RenderBuffer);})); + memoryInfos.push_back(createMemInfo("TextureSamplers", iscene.getTextureSamplerCount(), [](uint32_t /*unused*/){return sizeof(TextureSampler);})); + memoryInfos.push_back(createMemInfo("DataSlots", iscene.getDataSlotCount(), [](uint32_t /*unused*/){return sizeof(DataSlot);})); memoryInfos.push_back(createMemInfo("Nodes", iscene.getNodeCount(), [&iscene](uint32_t h){ return sizeof(TopologyNode) + (iscene.isNodeAllocated(NodeHandle(h)) ? iscene.getChildCount(NodeHandle(h)) * sizeof(NodeHandle) : 0u); diff --git a/client/ramses-client/impl/ClientCommands/LogMemoryUtils.h b/src/client/internal/ClientCommands/LogMemoryUtils.h similarity index 74% rename from client/ramses-client/impl/ClientCommands/LogMemoryUtils.h rename to src/client/internal/ClientCommands/LogMemoryUtils.h index 75487ef50..7e7e4d880 100644 --- a/client/ramses-client/impl/ClientCommands/LogMemoryUtils.h +++ b/src/client/internal/ClientCommands/LogMemoryUtils.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGMEMORYUTILS_H -#define RAMSES_LOGMEMORYUTILS_H +#pragma once -#include "SceneImpl.h" -#include "Collections/Vector.h" +#include "impl/SceneImpl.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include -namespace ramses_internal +namespace ramses::internal { struct MemoryInfo { @@ -23,7 +22,5 @@ namespace ramses_internal }; using MemoryInfoVector = std::vector; - MemoryInfoVector GetMemoryInfoFromScene(const ramses::SceneImpl& scene); + MemoryInfoVector GetMemoryInfoFromScene(const ramses::internal::SceneImpl& scene); } - -#endif diff --git a/client/ramses-client/impl/ClientCommands/LogResourceMemoryUsage.cpp b/src/client/internal/ClientCommands/LogResourceMemoryUsage.cpp similarity index 78% rename from client/ramses-client/impl/ClientCommands/LogResourceMemoryUsage.cpp rename to src/client/internal/ClientCommands/LogResourceMemoryUsage.cpp index 6f35fd16e..8eea60c2a 100644 --- a/client/ramses-client/impl/ClientCommands/LogResourceMemoryUsage.cpp +++ b/src/client/internal/ClientCommands/LogResourceMemoryUsage.cpp @@ -8,12 +8,12 @@ #include "LogResourceMemoryUsage.h" #include "SceneCommandBuffer.h" -#include "RamsesClientImpl.h" -#include "Utils/LogMacros.h" +#include "impl/RamsesClientImpl.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { - LogResourceMemoryUsage::LogResourceMemoryUsage(ramses::RamsesClientImpl& client) + LogResourceMemoryUsage::LogResourceMemoryUsage(RamsesClientImpl& client) : m_client(client) { description = "Log memory usage of resources in scene"; @@ -25,7 +25,7 @@ namespace ramses_internal { LOG_INFO(CONTEXT_CLIENT,"LogResourceMemoryUsage"); SceneCommandLogResourceMemoryUsage command; - m_client.enqueueSceneCommand(ramses::sceneId_t(sceneId), std::move(command)); + m_client.enqueueSceneCommand(sceneId_t(sceneId), std::move(command)); return true; } } diff --git a/client/ramses-client/impl/ClientCommands/LogResourceMemoryUsage.h b/src/client/internal/ClientCommands/LogResourceMemoryUsage.h similarity index 65% rename from client/ramses-client/impl/ClientCommands/LogResourceMemoryUsage.h rename to src/client/internal/ClientCommands/LogResourceMemoryUsage.h index 97fb7492d..0a4f490ff 100644 --- a/client/ramses-client/impl/ClientCommands/LogResourceMemoryUsage.h +++ b/src/client/internal/ClientCommands/LogResourceMemoryUsage.h @@ -6,27 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGRESOURCEMEMORYUSAGE_H -#define RAMSES_LOGRESOURCEMEMORYUSAGE_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses +namespace ramses::internal { class RamsesClientImpl; -} -namespace ramses_internal -{ class LogResourceMemoryUsage : public RamshCommandArgs { public: - explicit LogResourceMemoryUsage(ramses::RamsesClientImpl& client); + explicit LogResourceMemoryUsage(RamsesClientImpl& client); bool execute(uint64_t& sceneId) const override; private: - ramses::RamsesClientImpl& m_client; + RamsesClientImpl& m_client; }; } - -#endif diff --git a/client/ramses-client/impl/ClientCommands/PrintSceneList.cpp b/src/client/internal/ClientCommands/PrintSceneList.cpp similarity index 78% rename from client/ramses-client/impl/ClientCommands/PrintSceneList.cpp rename to src/client/internal/ClientCommands/PrintSceneList.cpp index 4f9769eaa..2482a43f4 100644 --- a/client/ramses-client/impl/ClientCommands/PrintSceneList.cpp +++ b/src/client/internal/ClientCommands/PrintSceneList.cpp @@ -7,23 +7,21 @@ // ------------------------------------------------------------------------- #include "PrintSceneList.h" -#include "ramses-client-api/Scene.h" -#include "RamsesClientImpl.h" -#include "Utils/LogMacros.h" +#include "ramses/client/Scene.h" +#include "impl/RamsesClientImpl.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { - PrintSceneList::PrintSceneList(const ramses::RamsesClientImpl& client) + PrintSceneList::PrintSceneList(const RamsesClientImpl& client) : m_client(client) { description = "print scene list"; registerKeyword("printSceneList"); } - bool PrintSceneList::executeInput(const std::vector& input) + bool PrintSceneList::executeInput([[maybe_unused]] const std::vector& input) { - UNUSED(input); - LOG_INFO_F(CONTEXT_CLIENT, ([&](StringOutputStream& sos) { sos << "PrintSceneList::executeInput: "; sos << "Scenes: \n\r"; diff --git a/client/ramses-client/impl/ClientCommands/PrintSceneList.h b/src/client/internal/ClientCommands/PrintSceneList.h similarity index 69% rename from client/ramses-client/impl/ClientCommands/PrintSceneList.h rename to src/client/internal/ClientCommands/PrintSceneList.h index bb0ec6da1..09919e559 100644 --- a/client/ramses-client/impl/ClientCommands/PrintSceneList.h +++ b/src/client/internal/ClientCommands/PrintSceneList.h @@ -6,26 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PRINTSCENELIST_H -#define RAMSES_PRINTSCENELIST_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses +namespace ramses::internal { class RamsesClientImpl; -} -namespace ramses_internal -{ class PrintSceneList : public RamshCommand { public: - explicit PrintSceneList(const ramses::RamsesClientImpl& client); + explicit PrintSceneList(const RamsesClientImpl& client); bool executeInput(const std::vector& input) override; private: - const ramses::RamsesClientImpl& m_client; + const RamsesClientImpl& m_client; }; } - -#endif //RAMSES_PRINTSCENELIST_H diff --git a/client/ramses-client/impl/ClientCommands/SceneCommandBuffer.cpp b/src/client/internal/ClientCommands/SceneCommandBuffer.cpp similarity index 100% rename from client/ramses-client/impl/ClientCommands/SceneCommandBuffer.cpp rename to src/client/internal/ClientCommands/SceneCommandBuffer.cpp diff --git a/client/ramses-client/impl/ClientCommands/SceneCommandBuffer.h b/src/client/internal/ClientCommands/SceneCommandBuffer.h similarity index 83% rename from client/ramses-client/impl/ClientCommands/SceneCommandBuffer.h rename to src/client/internal/ClientCommands/SceneCommandBuffer.h index 1d593d75a..7007b899f 100644 --- a/client/ramses-client/impl/ClientCommands/SceneCommandBuffer.h +++ b/src/client/internal/ClientCommands/SceneCommandBuffer.h @@ -6,35 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENECOMMANDBUFFER_H -#define RAMSES_SCENECOMMANDBUFFER_H +#pragma once -#include "ramses-framework-api/EValidationSeverity.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "PlatformAbstraction/VariantWrapper.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/Issue.h" +#include "internal/PlatformAbstraction/VariantWrapper.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { // Commands struct SceneCommandFlushSceneVersion { - ramses::sceneVersionTag_t sceneVersion; + sceneVersionTag_t sceneVersion = 0u; }; struct SceneCommandValidationRequest { - ramses::EValidationSeverity severity = ramses::EValidationSeverity::Info; + EIssueType verbosity = EIssueType::Warning; std::string optionalObjectName; }; struct SceneCommandDumpSceneToFile { std::string fileName; - bool sendViaDLT; + bool sendViaDLT = false; }; struct SceneCommandLogResourceMemoryUsage @@ -84,4 +83,4 @@ namespace ramses_internal std::visit(visitor, v); } } -#endif + diff --git a/src/client/internal/ClientCommands/SceneCommandVisitor.cpp b/src/client/internal/ClientCommands/SceneCommandVisitor.cpp new file mode 100644 index 000000000..2ac09fb5d --- /dev/null +++ b/src/client/internal/ClientCommands/SceneCommandVisitor.cpp @@ -0,0 +1,111 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "SceneCommandVisitor.h" +#include "SceneCommandBuffer.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SceneImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/SaveFileConfigImpl.h" +#include "LogMemoryUtils.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/File.h" +#include + +namespace ramses::internal +{ + + void SceneCommandVisitor::operator()(const SceneCommandFlushSceneVersion& cmd) + { + LOG_INFO(CONTEXT_CLIENT, "SceneCommandVisitor::execute: set scene version in next flush to \"" << cmd.sceneVersion << "\""); + m_scene.setSceneVersionForNextFlush(cmd.sceneVersion); + } + + void SceneCommandVisitor::operator()(const SceneCommandValidationRequest& cmd) + { + if (cmd.optionalObjectName.empty()) + { + // no object, validate whole scene + ValidationReportImpl report; + m_scene.validate(report); + LOG_INFO(CONTEXT_CLIENT, "Validation: " << report.toString()); + } + else + { + if (const auto* ro = m_scene.findObjectByName(cmd.optionalObjectName.c_str())) + { + ValidationReportImpl report; + ro->impl().validate(report); + LOG_INFO(CONTEXT_CLIENT, "Validation: " << report.toString()); + } + else + LOG_ERROR(CONTEXT_CLIENT, "Validation could not find requested object with name: " << cmd.optionalObjectName); + } + } + + void SceneCommandVisitor::operator()(const SceneCommandDumpSceneToFile& cmd) const + { + std::vector sceneData; + SaveFileConfigImpl config; + config.setValidationEnabled(false); + config.setCompressionEnabled(false); + if (!m_scene.serialize(sceneData, config)) + { + LOG_ERROR_P(CONTEXT_CLIENT, "SceneCommandVisitor::execute: failed dump scene: {} (file:{} dlt:{})", m_scene.getSceneId(), cmd.fileName, cmd.sendViaDLT); + return; + } + + const auto filename = cmd.fileName.empty() ? fmt::format("S_{}.ramses", m_scene.getSceneId()) : cmd.fileName + ".ramses"; + if (!cmd.fileName.empty()) + { + File file(filename); + if (file.open(File::Mode::WriteOverWriteOldBinary)) + { + if (!file.write(sceneData.data(), sceneData.size())) + { + LOG_ERROR_P(CONTEXT_CLIENT, "SceneCommandVisitor::execute: failed to write scene dump: {}", file.getPath()); + } + } + else + { + LOG_ERROR_P(CONTEXT_CLIENT, "SceneCommandVisitor::execute: failed to open: {}", file.getPath()); + } + } + + if (cmd.sendViaDLT) + { + if (GetRamsesLogger().transmit(std::move(sceneData), filename)) + { + LOG_INFO_P(CONTEXT_CLIENT, "SceneCommandVisitor::execute: started dlt file transfer: {}", filename); + } + else + { + LOG_ERROR_P(CONTEXT_CLIENT, "SceneCommandVisitor::execute: failed to send scene dump file via dlt: {}", filename); + } + } + } + + void SceneCommandVisitor::operator()(const SceneCommandLogResourceMemoryUsage& /*cmd*/) const + { + MemoryInfoVector memoryInfos = GetMemoryInfoFromScene(m_scene); + std::sort(memoryInfos.begin(), memoryInfos.end(),[](const MemoryInfo& lhs, const MemoryInfo& rhs){return lhs.memoryUsage > rhs.memoryUsage;}); + + LOG_INFO_F(CONTEXT_CLIENT,([&](StringOutputStream& out){ + const uint32_t memTotal = std::accumulate(memoryInfos.begin(), memoryInfos.end(), 0u, + [](uint32_t v, const MemoryInfo& info){return v + info.memoryUsage;}); + out << "\n\rTotal memory usage: " << memTotal; + out << "\n\rDetailed memory usage:"; + for (const auto info : memoryInfos) + { + out << "\n\r"; + out << info.memoryUsage << "\t"; + out << info.logInfoMesage; + } + })); + } +} diff --git a/client/ramses-client/impl/ClientCommands/SceneCommandVisitor.h b/src/client/internal/ClientCommands/SceneCommandVisitor.h similarity index 76% rename from client/ramses-client/impl/ClientCommands/SceneCommandVisitor.h rename to src/client/internal/ClientCommands/SceneCommandVisitor.h index 3f5a4af82..dfe7bfbf6 100644 --- a/client/ramses-client/impl/ClientCommands/SceneCommandVisitor.h +++ b/src/client/internal/ClientCommands/SceneCommandVisitor.h @@ -7,19 +7,14 @@ // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENECOMMANDVISITOR_H -#define RAMSES_SCENECOMMANDVISITOR_H +#pragma once #include -namespace ramses +namespace ramses::internal { class SceneImpl; - class ResourceFileDescriptionSet; -} -namespace ramses_internal -{ struct SceneCommandFlushSceneVersion; struct SceneCommandValidationRequest; struct SceneCommandDumpSceneToFile; @@ -28,7 +23,7 @@ namespace ramses_internal class SceneCommandVisitor { public: - explicit SceneCommandVisitor(ramses::SceneImpl& scene) + explicit SceneCommandVisitor(SceneImpl& scene) : m_scene(scene) {} @@ -38,11 +33,7 @@ namespace ramses_internal void operator()(const SceneCommandLogResourceMemoryUsage& cmd) const; private: - static void SendSceneViaDLT(const std::string& sceneDumpFileName); - - ramses::SceneImpl& m_scene; + SceneImpl& m_scene; }; } - -#endif diff --git a/client/ramses-client/impl/ClientCommands/ValidateCommand.cpp b/src/client/internal/ClientCommands/ValidateCommand.cpp similarity index 66% rename from client/ramses-client/impl/ClientCommands/ValidateCommand.cpp rename to src/client/internal/ClientCommands/ValidateCommand.cpp index b1490d2fe..e66985181 100644 --- a/client/ramses-client/impl/ClientCommands/ValidateCommand.cpp +++ b/src/client/internal/ClientCommands/ValidateCommand.cpp @@ -7,15 +7,14 @@ // ------------------------------------------------------------------------- #include "ValidateCommand.h" -#include "ramses-client-api/Scene.h" -#include "SceneImpl.h" -#include "RamsesClientImpl.h" -#include "Utils/LogMacros.h" -#include "ramses-framework-api/EValidationSeverity.h" +#include "ramses/client/Scene.h" +#include "impl/SceneImpl.h" +#include "impl/RamsesClientImpl.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { - ValidateCommand::ValidateCommand(ramses::RamsesClientImpl& client) + ValidateCommand::ValidateCommand(RamsesClientImpl& client) : m_client(client) { description = "validate a scene, or object within scene"; @@ -31,27 +30,23 @@ namespace ramses_internal { SceneCommandValidationRequest command; - if (severity == "info") + if (severity == "warning") { - command.severity = ramses::EValidationSeverity::Info; - } - else if (severity == "warning") - { - command.severity = ramses::EValidationSeverity::Warning; + command.verbosity = EIssueType::Warning; } else if (severity == "error") { - command.severity = ramses::EValidationSeverity::Error; + command.verbosity = EIssueType::Error; } else { - LOG_ERROR(CONTEXT_CLIENT, "Wrong value for severity parameter, must be info, warning or error"); + LOG_ERROR(CONTEXT_CLIENT, "Wrong value for severity parameter, must be 'warning' or 'error'"); return false; } command.optionalObjectName = objectName; - m_client.enqueueSceneCommand(ramses::sceneId_t(sceneId), std::move(command)); + m_client.enqueueSceneCommand(sceneId_t(sceneId), std::move(command)); return true; } } diff --git a/client/ramses-client/impl/ClientCommands/ValidateCommand.h b/src/client/internal/ClientCommands/ValidateCommand.h similarity index 69% rename from client/ramses-client/impl/ClientCommands/ValidateCommand.h rename to src/client/internal/ClientCommands/ValidateCommand.h index 1fba1e6d0..888c23e13 100644 --- a/client/ramses-client/impl/ClientCommands/ValidateCommand.h +++ b/src/client/internal/ClientCommands/ValidateCommand.h @@ -6,29 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_VALIDATECOMMAND_H -#define RAMSES_VALIDATECOMMAND_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include -namespace ramses +namespace ramses::internal { class RamsesClientImpl; -} -namespace ramses_internal -{ class ValidateCommand : public RamshCommandArgs { public: - explicit ValidateCommand(ramses::RamsesClientImpl& client); + explicit ValidateCommand(RamsesClientImpl& client); bool execute(uint64_t& sceneId, std::string& severity, std::string& objectName) const override; private: - ramses::RamsesClientImpl& m_client; + RamsesClientImpl& m_client; }; } - -#endif diff --git a/client/ramses-client/impl/DataSlotUtils.cpp b/src/client/internal/DataSlotUtils.cpp similarity index 96% rename from client/ramses-client/impl/DataSlotUtils.cpp rename to src/client/internal/DataSlotUtils.cpp index efd7a50e2..4f714a4c1 100644 --- a/client/ramses-client/impl/DataSlotUtils.cpp +++ b/src/client/internal/DataSlotUtils.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "DataSlotUtils.h" -#include "Scene/ClientScene.h" +#include "internal/DataSlotUtils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" -namespace ramses_internal +namespace ramses::internal { namespace DataSlotUtils { diff --git a/client/ramses-client/impl/DataSlotUtils.h b/src/client/internal/DataSlotUtils.h similarity index 83% rename from client/ramses-client/impl/DataSlotUtils.h rename to src/client/internal/DataSlotUtils.h index 35500aad5..7d4a103f7 100644 --- a/client/ramses-client/impl/DataSlotUtils.h +++ b/src/client/internal/DataSlotUtils.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATASLOTUTILS_H -#define RAMSES_DATASLOTUTILS_H +#pragma once -#include "SceneAPI/Handles.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" -namespace ramses_internal +namespace ramses::internal { class ClientScene; @@ -26,5 +25,3 @@ namespace ramses_internal bool HasDataSlotIdForTexture(const ClientScene& scene, const ResourceContentHash& texture); } } - -#endif diff --git a/client/ramses-client/impl/RamsesVersion.cpp b/src/client/internal/RamsesVersion.cpp similarity index 87% rename from client/ramses-client/impl/RamsesVersion.cpp rename to src/client/internal/RamsesVersion.cpp index a71ec694d..af461bc94 100644 --- a/client/ramses-client/impl/RamsesVersion.cpp +++ b/src/client/internal/RamsesVersion.cpp @@ -6,21 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RamsesVersion.h" -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "Collections/StringOutputStream.h" -#include "PlatformAbstraction/PlatformStringUtils.h" -#include "Utils/LogMacros.h" +#include "internal/RamsesVersion.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/PlatformStringUtils.h" +#include "internal/Core/Utils/LogMacros.h" #include "ramses-sdk-build-config.h" +#include "impl/EFeatureLevelImpl.h" #include -namespace ramses_internal +namespace ramses::internal { namespace RamsesVersion { - void WriteToStream(IOutputStream& stream, std::string_view versionString, std::string_view gitHash, ramses::EFeatureLevel featureLevel) + void WriteToStream(IOutputStream& stream, std::string_view versionString, std::string_view gitHash, EFeatureLevel featureLevel) { LOG_INFO(CONTEXT_CLIENT, "RamsesVersion::WriteToStream: Version: " << versionString << " Git Hash: " << gitHash << " Feature Level: " << featureLevel); StringOutputStream out; @@ -66,8 +67,9 @@ namespace ramses_internal static bool ExpectAndGetNumber(const std::string& inputString, size_t& matchIndexInOut, uint32_t& numberOut) { const char* startptr = inputString.data() + matchIndexInOut; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive char* endptr = nullptr; - const long readNumber = std::strtol(startptr, &endptr, 10); + const auto readNumber = std::strtol(startptr, &endptr, 10); if (startptr == endptr || readNumber < 0) return false; matchIndexInOut += (endptr - startptr); @@ -78,7 +80,7 @@ namespace ramses_internal static bool ExpectHexString(const std::string& inputString, size_t& matchIndexInOut) { const size_t startIdx = matchIndexInOut; - while (matchIndexInOut < inputString.size() && std::isxdigit(inputString[matchIndexInOut])) + while (matchIndexInOut < inputString.size() && (std::isxdigit(inputString[matchIndexInOut]) != 0)) ++matchIndexInOut; return startIdx != matchIndexInOut; } @@ -127,7 +129,7 @@ namespace ramses_internal return true; } - static bool ExpectFeatureLevel(const std::string& featureLevelString, ramses::EFeatureLevel& outFeatureLevel) + static bool ExpectFeatureLevel(const std::string& featureLevelString, EFeatureLevel& outFeatureLevel) { size_t idx = 0; if (!ExpectString(featureLevelString, idx, "[FeatureLevel:")) @@ -137,12 +139,12 @@ namespace ramses_internal if (!ExpectAndGetNumber(featureLevelString, idx, num)) return false; - if (std::find(ramses::AllFeatureLevels.cbegin(), ramses::AllFeatureLevels.cend(), num) == ramses::AllFeatureLevels.cend()) + if (!IsFeatureLevel(num)) { LOG_ERROR(CONTEXT_CLIENT, "RamsesVersion::ReadFromStream: Unknown feature level " << num << " in file, either file corrupt or file exported with future version"); return false; } - outFeatureLevel = static_cast(num); + outFeatureLevel = static_cast(num); if (!ExpectString(featureLevelString, idx, "]") || idx != featureLevelString.size()) return false; @@ -150,7 +152,7 @@ namespace ramses_internal return true; } - bool ReadFromStream(IInputStream& stream, VersionInfo& outVersion, ramses::EFeatureLevel& outFeatureLevel) + bool ReadFromStream(IInputStream& stream, VersionInfo& outVersion, EFeatureLevel& outFeatureLevel) { std::string versionString; std::string gitHashString; diff --git a/client/ramses-client/impl/RamsesVersion.h b/src/client/internal/RamsesVersion.h similarity index 70% rename from client/ramses-client/impl/RamsesVersion.h rename to src/client/internal/RamsesVersion.h index 57c43b82a..4c1427570 100644 --- a/client/ramses-client/impl/RamsesVersion.h +++ b/src/client/internal/RamsesVersion.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RAMSESVERSION_H -#define RAMSES_INTERNAL_RAMSESVERSION_H +#pragma once -#include "ramses-framework-api/EFeatureLevel.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "ramses/framework/EFeatureLevel.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { class IOutputStream; class IInputStream; @@ -26,14 +25,12 @@ namespace ramses_internal { std::string gitHash; std::string versionString; - uint32_t major; - uint32_t minor; + uint32_t major = 0u; + uint32_t minor = 0u; }; - void WriteToStream(IOutputStream& stream, std::string_view versionString, std::string_view gitHash, ramses::EFeatureLevel featureLevel); - bool ReadFromStream(IInputStream& stream, VersionInfo& outVersion, ramses::EFeatureLevel& outFeatureLevel); + void WriteToStream(IOutputStream& stream, std::string_view versionString, std::string_view gitHash, EFeatureLevel featureLevel); + bool ReadFromStream(IInputStream& stream, VersionInfo& outVersion, EFeatureLevel& outFeatureLevel); bool MatchesMajorMinor(uint32_t currentMajor, uint32_t currentMinor, const VersionInfo& in); } } - -#endif diff --git a/framework/FrameworkTestUtils/include/TestPngHeader.h b/src/client/internal/VisibilityModeUtils.h similarity index 55% rename from framework/FrameworkTestUtils/include/TestPngHeader.h rename to src/client/internal/VisibilityModeUtils.h index 2ce57a530..288bd9596 100644 --- a/framework/FrameworkTestUtils/include/TestPngHeader.h +++ b/src/client/internal/VisibilityModeUtils.h @@ -6,20 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORKTESTUTILS_TESTPNGHEADER_H -#define RAMSES_FRAMEWORKTESTUTILS_TESTPNGHEADER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include +#include "ramses/framework/EVisibilityMode.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { - namespace TestPngHeader - { - std::vector GetValidHeader(); - std::vector GetValidHeaderWithFakeData(); - std::vector GetInvalidHeader(); - } + const std::array VisibilityModeNames = {"Off", "Invisible", "Visible"}; + ENUM_TO_STRING(EVisibilityMode, VisibilityModeNames, EVisibilityMode::Visible); } - -#endif diff --git a/client/ramses-client/impl/glslEffectBlock/GLSlang.h b/src/client/internal/glslEffectBlock/GLSlang.h similarity index 88% rename from client/ramses-client/impl/glslEffectBlock/GLSlang.h rename to src/client/internal/glslEffectBlock/GLSlang.h index 44a39b268..26ed7d60c 100644 --- a/client/ramses-client/impl/glslEffectBlock/GLSlang.h +++ b/src/client/internal/glslEffectBlock/GLSlang.h @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLSLANG_H -#define RAMSES_GLSLANG_H +#pragma once -#include "Utils/Warnings.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/Core/Utils/Warnings.h" + +#include WARNINGS_PUSH @@ -35,6 +35,3 @@ WARNINGS_POP #include "Public/ShaderLang.h" - - -#endif diff --git a/src/client/internal/glslEffectBlock/GlslEffect.cpp b/src/client/internal/glslEffectBlock/GlslEffect.cpp new file mode 100644 index 000000000..7b79f8b8d --- /dev/null +++ b/src/client/internal/glslEffectBlock/GlslEffect.cpp @@ -0,0 +1,188 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "GlslEffect.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/glslEffectBlock/GlslToEffectConverter.h" +#include "internal/glslEffectBlock/GlslParser.h" +#include "internal/glslEffectBlock/GlslLimits.h" + +#include + +namespace ramses::internal +{ + /* + wrapper for glslang process wide initializer and finalizer. + constructed once in this file. it is global and lives longer + than main. will not work if global static objects start using + effects but this should never happen. + */ + class GlslangInitAndFinalizeOnceHelper + { + public: + GlslangInitAndFinalizeOnceHelper() + { + glslang::InitializeProcess(); + glslang::InitProcess(); + } + + ~GlslangInitAndFinalizeOnceHelper() + { + glslang::DetachProcess(); + glslang::FinalizeProcess(); + } + }; + static GlslangInitAndFinalizeOnceHelper glslangInitializer; + + + GlslEffect::GlslEffect(std::string_view vertexShader, + std::string_view fragmentShader, + std::string_view geometryShader, + std::vector compilerDefines, + const HashMap& semanticInputs, + std::string_view name) + : m_vertexShader(vertexShader) + , m_fragmentShader(fragmentShader) + , m_geometryShader(geometryShader) + , m_compilerDefines(std::move(compilerDefines)) + , m_semanticInputs(semanticInputs) + , m_name(name) + { + } + + GlslEffect::~GlslEffect() = default; + + EffectResource* GlslEffect::createEffectResource() + { + if (m_effectResource) + { + return m_effectResource; + } + + GlslParser parser{m_vertexShader, m_fragmentShader, m_geometryShader, m_compilerDefines}; + if (!parser.valid()) + { + m_errorMessages << parser.getErrors(); + return nullptr; + } + + GlslToEffectConverter glslToEffectConverter(m_semanticInputs); + if (!glslToEffectConverter.parseShaderProgram(parser.getProgram())) + { + m_errorMessages << "[GLSL Input Parser] " << glslToEffectConverter.getStatusMessage(); + return nullptr; + } + + if (!extractAndCheckShaderVersions(parser.getProgram())) + { + return nullptr; + } + + if (!extractAndCheckExtensions(parser.getProgram())) + { + return nullptr; + } + + const EffectInputInformationVector& uniformInputs = glslToEffectConverter.getUniformInputs(); + const EffectInputInformationVector& attributeInputs = glslToEffectConverter.getAttributeInputs(); + const auto geomInputType = glslToEffectConverter.getGeometryShaderInputType(); + + m_effectResource = new EffectResource(parser.getVertexShader(), parser.getFragmentShader(), parser.getGeometryShader(), geomInputType, uniformInputs, attributeInputs, m_name); + return m_effectResource; + } + + bool GlslEffect::extractAndCheckShaderVersions(const glslang::TProgram* program) + { + // profile check + const EProfile vertexShaderProfile = program->getIntermediate(EShLangVertex)->getProfile(); + const EProfile fragmentShaderProfile = program->getIntermediate(EShLangFragment)->getProfile(); + const auto geometryShaderIntermediate = program->getIntermediate(EShLangGeometry); + const bool hasGeometryShader = (nullptr != geometryShaderIntermediate); + const EProfile geometryShaderProfile = hasGeometryShader ? geometryShaderIntermediate->getProfile() : ENoProfile; + + if (vertexShaderProfile != EEsProfile || fragmentShaderProfile != EEsProfile || (hasGeometryShader && geometryShaderProfile != EEsProfile)) + { + m_errorMessages << "[GLSL Compiler] " << m_name << " unsupported profile (supported profile: es)\n"; + return false; + } + + const uint32_t vertexShaderVersion = program->getIntermediate(EShLangVertex)->getVersion(); + const uint32_t fragmentShaderVersion = program->getIntermediate(EShLangFragment)->getVersion(); + const uint32_t geometryShaderVersion = hasGeometryShader ? geometryShaderIntermediate->getVersion() : 0u; + + const uint32_t maximumSupportedVersion = 320u; + + if (vertexShaderVersion > maximumSupportedVersion || + fragmentShaderVersion > maximumSupportedVersion || + (hasGeometryShader && geometryShaderVersion > maximumSupportedVersion)) + { + m_errorMessages << "[GLSL Compiler] " << m_name << " unsupported version (maximum supported version: 320 es)\n"; + return false; + } + + if (vertexShaderVersion != fragmentShaderVersion || + (hasGeometryShader && vertexShaderVersion != geometryShaderVersion)) + { + m_errorMessages << "[GLSL Compiler] " << m_name << " version of vertex, fragment and geometry shaders must be same\n"; + return false; + } + + m_shadingLanguageVersion = vertexShaderVersion; + return true; + } + + bool GlslEffect::extractAndCheckExtensions(const glslang::TProgram* program) + { + bool success = true; + const std::set& vertexExtensions = program->getIntermediate(EShLangVertex)->getRequestedExtensions(); + for (const auto& it : vertexExtensions) + { + const char* extensionName = it.c_str(); + if (!IsSupportedExtension(extensionName)) + { + m_errorMessages << "[GLSL Compiler] " << m_name << " extension not supported in vertex shader: " << extensionName << "\n"; + success = false; + } + } + + const std::set& fragmentExtensions = program->getIntermediate(EShLangFragment)->getRequestedExtensions(); + for (const auto& it : fragmentExtensions) + { + const char* extensionName = it.c_str(); + if (!IsSupportedExtension(extensionName)) + { + m_errorMessages << "[GLSL Compiler] " << m_name << " extension not supported in fragment shader: " << extensionName << "\n"; + success = false; + } + } + + return success; + } + + uint32_t GlslEffect::getShadingLanguageVersion() const + { + assert(m_effectResource); + return m_shadingLanguageVersion; + } + + std::string GlslEffect::getEffectErrorMessages() const + { + return m_errorMessages.data(); + } + + bool GlslEffect::IsSupportedExtension(const std::string& extension) + { + if (extension == "GL_OES_EGL_image_external" + || extension == "GL_OES_EGL_image_external_essl3") + { + return true; + } + + return false; + } +} diff --git a/client/ramses-client/impl/glslEffectBlock/GlslEffect.h b/src/client/internal/glslEffectBlock/GlslEffect.h similarity index 51% rename from client/ramses-client/impl/glslEffectBlock/GlslEffect.h rename to src/client/internal/glslEffectBlock/GlslEffect.h index b86868b3e..de7be7b90 100644 --- a/client/ramses-client/impl/glslEffectBlock/GlslEffect.h +++ b/src/client/internal/glslEffectBlock/GlslEffect.h @@ -6,18 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLSLEFFECT_H -#define RAMSES_GLSLEFFECT_H +#pragma once -#include "Collections/StringOutputStream.h" -#include "Collections/Vector.h" -#include "Collections/HashMap.h" -#include "SceneAPI/EFixedSemantics.h" -#include "SceneAPI/RenderState.h" -#include "Resource/IResource.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" +#include "internal/SceneGraph/Resource/EffectResource.h" #include #include +#include struct TBuiltInResource; namespace glslang @@ -27,7 +25,7 @@ namespace glslang class TPoolAllocator; } -namespace ramses_internal +namespace ramses::internal { class EffectResource; @@ -37,25 +35,18 @@ namespace ramses_internal GlslEffect(std::string_view vertexShader, std::string_view fragmentShader, std::string_view geometryShader, - const std::vector& compilerDefines, + std::vector compilerDefines, const HashMap& semanticInputs, std::string_view name); ~GlslEffect(); - EffectResource* createEffectResource(ResourceCacheFlag cacheFlag); + [[nodiscard]] EffectResource* createEffectResource(); - uint32_t getShadingLanguageVersion() const; - std::string getEffectErrorMessages() const; + [[nodiscard]] uint32_t getShadingLanguageVersion() const; + [[nodiscard]] std::string getEffectErrorMessages() const; private: - struct ShaderParts - { - std::string version; - std::string defines; - std::string userCode; - }; - const std::string m_vertexShader; const std::string m_fragmentShader; const std::string m_geometryShader; @@ -64,19 +55,12 @@ namespace ramses_internal const std::string m_name; mutable StringOutputStream m_errorMessages; - EffectResource* m_effectResource; - uint32_t m_shadingLanguageVersion; + EffectResource* m_effectResource{nullptr}; + uint32_t m_shadingLanguageVersion{0}; - std::string createDefineString() const; - bool createShaderParts(ShaderParts& outParts, const std::string& defineString, const std::string& userShader) const; - std::string mergeShaderParts(const ShaderParts& shaderParts) const; - bool parseShader(glslang::TShader& tShader, const TBuiltInResource& glslCompilationResources, const ShaderParts& shaderParts, const std::string& shaderName); - glslang::TProgram* linkProgram(glslang::TShader* vertexShader, glslang::TShader* fragmentShader, glslang::TShader* geometryShader) const; bool extractAndCheckShaderVersions(const glslang::TProgram* program); bool extractAndCheckExtensions(const glslang::TProgram* program); - bool isSupportedExtension(const std::string& extension) const; + static bool IsSupportedExtension(const std::string& extension); }; } - -#endif diff --git a/client/ramses-client/impl/glslEffectBlock/GlslLimits.h b/src/client/internal/glslEffectBlock/GlslLimits.h similarity index 96% rename from client/ramses-client/impl/glslEffectBlock/GlslLimits.h rename to src/client/internal/glslEffectBlock/GlslLimits.h index cc1de272c..89d5e762c 100644 --- a/client/ramses-client/impl/glslEffectBlock/GlslLimits.h +++ b/src/client/internal/glslEffectBlock/GlslLimits.h @@ -6,17 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLSLLIMITS_H -#define RAMSES_GLSLLIMITS_H - -#include "PlatformAbstraction/PlatformTypes.h" +#pragma once +#include #include #include struct TBuiltInResource; -namespace ramses_internal +namespace ramses::internal { class GlslLimits { @@ -56,14 +54,12 @@ namespace ramses_internal static uint32_t GetVersionFromString(std::string_view glslVersion) { - std::array buffer; + std::array buffer{}; uint32_t n = 0; - for (size_t i = 0; i < glslVersion.size(); i++) + for (char c : glslVersion) { - const char& c = glslVersion[i]; - - if (isdigit(c)) + if (isdigit(c) != 0) { buffer[n++] = c; } @@ -76,7 +72,7 @@ namespace ramses_internal } buffer[n] = 0; - return std::atoi(buffer.data()); + return std::strtoul(buffer.data(), nullptr, 0); } static bool IsESVersion(std::string_view glslVersion) @@ -206,5 +202,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/src/client/internal/glslEffectBlock/GlslParser.cpp b/src/client/internal/glslEffectBlock/GlslParser.cpp new file mode 100644 index 000000000..f1b14cbdf --- /dev/null +++ b/src/client/internal/glslEffectBlock/GlslParser.cpp @@ -0,0 +1,390 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/glslEffectBlock/GlslParser.h" +#include "GlslLimits.h" +#include "fmt/format.h" +#include "internal/PlatformAbstraction/FmtBase.h" +#include "impl/EffectDescriptionImpl.h" + +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase +{ + template constexpr auto format(const glslang::TIntermSymbol& symbol, FormatContext& ctx) + { + const auto& qualifier = symbol.getQualifier(); + if (qualifier.hasLayout() && qualifier.hasAnyLocation()) + { + return fmt::format_to(ctx.out(), "layout(location = {}) {}", qualifier.layoutLocation, symbol.getName()); + } + return fmt::format_to(ctx.out(), "{}", symbol.getName()); + } +}; + +namespace +{ + bool isInput(glslang::TIntermSymbol* symbol) + { + const auto storageQualifier = symbol->getType().getQualifier().storage; + return (storageQualifier == glslang::EvqVaryingIn); + } + + bool isOutput(glslang::TIntermSymbol* symbol) + { + const auto storageQualifier = symbol->getType().getQualifier().storage; + return (storageQualifier == glslang::EvqVaryingOut); + } + + bool isUniform(glslang::TIntermSymbol* symbol) + { + return (glslang::EvqUniform == symbol->getType().getQualifier().storage); + } + + using SymbolMap = std::unordered_map; + + class ShaderInterface + { + public: + explicit ShaderInterface(const SymbolMap& symbolMap) + : m_symbols(symbolMap) + { + } + + /** + * Finds a linker match for the given symbol + * + * @param symbol symbol to find a match for + */ + [[nodiscard]] auto findMatch(const glslang::TIntermSymbol* symbol) const + { + const auto& qualifier = symbol->getQualifier(); + // symbol name is irrelevant if symbol has a layout location + if (qualifier.hasLayout() && qualifier.hasAnyLocation()) + { + auto pred = [&](const SymbolMap::value_type& it) { + const auto& q = it.second->getQualifier(); + if (q.hasLayout() && q.hasAnyLocation()) + { + return qualifier.layoutLocation == q.layoutLocation; // NOLINT(cppcoreguidelines-narrowing-conversions): false positive + } + return false; + }; + return std::find_if(m_symbols.begin(), m_symbols.end(), pred); + } + return m_symbols.find(symbol->getName()); + } + + [[nodiscard]] auto begin() const + { + return m_symbols.begin(); + } + + [[nodiscard]] auto end() const + { + return m_symbols.end(); + } + + private: + const SymbolMap& m_symbols; + }; + + class UnusedVariableTraverser : public glslang::TIntermTraverser + { + public: + using LoggerFunc = std::function; + + explicit UnusedVariableTraverser(LoggerFunc logger) + : m_logger(std::move(logger)) + , m_interface(m_interfaceSymbols) + { + } + + [[nodiscard]] const ShaderInterface& getInterface() const + { + return m_interface; + } + + private: + bool visitAggregate(glslang::TVisit /*unused*/, glslang::TIntermAggregate* node) override + { + if (node->getOp() == glslang::EOpLinkerObjects) + { + assert(!m_link); // only expected once per shader + m_link = true; + } + return true; + } + + void visitSymbol(glslang::TIntermSymbol* symbol) override + { + const auto storageQualifier = symbol->getType().getQualifier().storage; + switch (storageQualifier) + { + case glslang::EvqConst: // constants are replaced by values in the AST + case glslang::EvqVertexId: + case glslang::EvqInstanceId: + return; + default: + break; + } + + if (m_link) + { + if (m_usedSymbols.find(symbol->getName()) == m_usedSymbols.end()) + { + const auto msg = fmt::format("Unused [{}]: '{}' ({})", glslang::GetStorageQualifierString(storageQualifier), symbol->getName(), symbol->getCompleteString()); + auto category = ramses::internal::EShaderWarningCategory::UnusedVariable; + switch (storageQualifier) + { + case glslang::EvqUniform: + category = ramses::internal::EShaderWarningCategory::UnusedUniform; + break; + case glslang::EvqVaryingIn: + case glslang::EvqVaryingOut: + category = ramses::internal::EShaderWarningCategory::UnusedVarying; + break; + default: + break; + } + m_logger(category, msg); + } + m_interfaceSymbols[symbol->getName()] = symbol; + } + else + { + m_usedSymbols[symbol->getName()] = symbol; + } + } + + LoggerFunc m_logger; + bool m_link = false; + SymbolMap m_usedSymbols; + SymbolMap m_interfaceSymbols; + ShaderInterface m_interface; + }; +} + + +namespace ramses::internal +{ + GlslParser::GlslParser(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader, const std::vector& compilerDefines) + { + m_vertexShader = std::make_unique(EShLangVertex); + m_fragmentShader = std::make_unique(EShLangFragment); + if (!geometryShader.empty()) + { + m_geometryShader = std::make_unique(EShLangGeometry); + } + + const auto defineString = CreateDefineString(compilerDefines); + const bool hasGeometryShader = (m_geometryShader != nullptr); + + ShaderParts vertexShaderParts; + ShaderParts fragmentShaderParts; + ShaderParts geometryShaderParts; + + if (!createShaderParts(vertexShaderParts, defineString, vertexShader, "vertex shader") || + !createShaderParts(fragmentShaderParts, defineString, fragmentShader, "fragment shader") || + (hasGeometryShader && !createShaderParts(geometryShaderParts, defineString, geometryShader, "geometry shader"))) + { + return; + } + + TBuiltInResource glslCompilationResources{}; + // Use the GLSL version to determine the resource limits in the shader. The version used in vertex + // and fragment shader must be the same (checked later), so we just pass one of them for now. + GlslLimits::InitCompilationResources(glslCompilationResources, vertexShaderParts.version); + if (!parseShader(*m_vertexShader, glslCompilationResources, vertexShaderParts, "vertex shader") || + !parseShader(*m_fragmentShader, glslCompilationResources, fragmentShaderParts, "fragment shader") || + (hasGeometryShader && !parseShader(*m_geometryShader, glslCompilationResources, geometryShaderParts, "geometry shader"))) + { + return; + } + + if (!linkProgram()) + { + return; + } + + { // merge shaders from parts + m_vertexShaderFromParts = MergeShaderParts(vertexShaderParts); + m_fragmentShaderFromParts = MergeShaderParts(fragmentShaderParts); + m_geometryShaderFromParts = MergeShaderParts(geometryShaderParts); + } + } + + bool GlslParser::linkProgram() + { + auto program = std::make_unique(); + program->addShader(m_vertexShader.get()); + program->addShader(m_fragmentShader.get()); + if (m_geometryShader) + program->addShader(m_geometryShader.get()); + + if (!program->link(EShMsgDefault)) + { + m_errorMessages << "[GLSL Compiler] Shader Program Linker Error:\n" << program->getInfoLog() << "\n"; + return false; + } + + if (!program->buildReflection()) + { + m_errorMessages << "[GLSL Compiler] Shader program error in buildReflection\n"; + return false; + } + m_program = std::move(program); + return true; + } + + std::string GlslParser::getErrors() const + { + return m_errorMessages.c_str(); + } + + const glslang::TProgram* GlslParser::getProgram() const + { + return m_program.get(); + } + + bool GlslParser::valid() const + { + return m_program != nullptr; + } + + GlslParser::Warnings GlslParser::generateWarnings() const + { + Warnings warnings; + if (!m_program) + { + return warnings; + } + + // find unused variables + UnusedVariableTraverser tVertex([&](EShaderWarningCategory category, const std::string& msg) { warnings.push_back({EShaderStage::Vertex, category, msg}); }); + m_program->getIntermediate(EShLangVertex)->getTreeRoot()->traverse(&tVertex); + UnusedVariableTraverser tFragment([&](EShaderWarningCategory category, const std::string& msg) { warnings.push_back({EShaderStage::Fragment, category, msg}); }); + m_program->getIntermediate(EShLangFragment)->getTreeRoot()->traverse(&tFragment); + + // check interface between vertex and fragment shader + const auto& fInterface = tFragment.getInterface(); + for (const auto& v : tVertex.getInterface()) + { + if (!isOutput(v.second) && !isUniform(v.second)) + continue; + auto it = fInterface.findMatch(v.second); + if (it == fInterface.end()) + { + if (isOutput(v.second)) + { + warnings.push_back({EShaderStage::Vertex, EShaderWarningCategory::InterfaceMismatch, + fmt::format("Vertex shader output '{}' is not input in fragment shader", *v.second)}); + } + } + else + { + const auto& vType = v.second->getType(); + const auto& fType = it->second->getType(); + // operator!= only compares the type, but not the qualifier + if (vType != fType) + { + warnings.push_back({EShaderStage::Vertex, EShaderWarningCategory::InterfaceMismatch, + fmt::format("Type mismatch: '{}'. (Vertex:{}, Fragment:{})", *v.second, v.second->getCompleteString(), it->second->getCompleteString())}); + } + else if (fType.getQualifier().precision != vType.getQualifier().precision) + { + warnings.push_back( {EShaderStage::Vertex, EShaderWarningCategory::PrecisionMismatch, + fmt::format("Precision mismatch: '{}'. (Vertex:{}, Fragment:{})", *v.second, v.second->getCompleteString(), it->second->getCompleteString())}); + } + } + } + + const auto& vInterface = tVertex.getInterface(); + for (const auto& f : fInterface) + { + if (!isInput(f.second)) + continue; + auto it = vInterface.findMatch(f.second); + if (it == vInterface.end()) + { + warnings.push_back({EShaderStage::Fragment, EShaderWarningCategory::InterfaceMismatch, + fmt::format("Fragment shader input '{}' is not output in vertex shader", *f.second)}); + } + } + return warnings; + } + + std::string GlslParser::CreateDefineString(const std::vector& compilerDefines) + { + StringOutputStream result; + for (const auto& defineString : compilerDefines) + { + result << "#define " << defineString << "\n"; + } + return std::string(result.release()); + } + + bool GlslParser::createShaderParts(ShaderParts& outParts, const std::string& defineString, const std::string& userShader, const std::string& shaderName) const + { + size_t versionStringStart = 0; + std::string versionString; + if ((versionStringStart = userShader.find("#version")) != std::string::npos) + { + size_t versionStringEnd = userShader.find('\n', versionStringStart); + if (versionStringEnd == std::string::npos) + { + m_errorMessages << "[GLSL Compiler] " << shaderName << " Shader contains #version without newline \n"; + return false; + } + + outParts.version = userShader.substr(versionStringStart, versionStringEnd + 1 - versionStringStart); + outParts.userCode = userShader.substr(versionStringEnd + 1, userShader.size() - versionStringEnd - 1); + } + else + { + outParts.version = "#version 100\n"; + outParts.userCode = userShader; + } + + outParts.defines = defineString; + return true; + } + + bool GlslParser::parseShader(glslang::TShader& tShader, const TBuiltInResource& glslCompilationResources, const ShaderParts& shaderParts, const std::string& shaderName) + { + std::array fragmentShaderCodeCString = {shaderParts.version.c_str(), shaderParts.defines.c_str(), shaderParts.userCode.c_str()}; + tShader.setStrings(fragmentShaderCodeCString.data(), 3); + bool parsingSuccessful = tShader.parse(&glslCompilationResources, 100, false, EShMsgDefault); + if (!parsingSuccessful) + { + m_errorMessages << "[GLSL Compiler] " << shaderName << " Shader Parsing Error:\n" << tShader.getInfoLog() << "\n"; + return false; + } + return true; + } + + const std::string& GlslParser::getVertexShader() const + { + return m_vertexShaderFromParts; + } + + const std::string& GlslParser::getFragmentShader() const + { + return m_fragmentShaderFromParts; + } + + const std::string& GlslParser::getGeometryShader() const + { + return m_geometryShaderFromParts; + } + + std::string GlslParser::MergeShaderParts(const ShaderParts& shaderParts) + { + StringOutputStream str; + str << shaderParts.version << shaderParts.defines << shaderParts.userCode; + return str.release(); + } +} diff --git a/src/client/internal/glslEffectBlock/GlslParser.h b/src/client/internal/glslEffectBlock/GlslParser.h new file mode 100644 index 000000000..da3350fa4 --- /dev/null +++ b/src/client/internal/glslEffectBlock/GlslParser.h @@ -0,0 +1,72 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/glslEffectBlock/GLSlang.h" +#include "internal/SceneGraph/SceneAPI/EShaderStage.h" +#include "internal/SceneGraph/SceneAPI/EShaderWarningCategory.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" + +#include +#include +#include +#include + +namespace ramses::internal +{ + class GlslParser + { + public: + struct Warning + { + EShaderStage stage; + EShaderWarningCategory category; + std::string msg; + }; + using Warnings = std::vector; + + GlslParser(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader = {}, const std::vector& compilerDefines = {}); + + [[nodiscard]] bool valid() const; + + [[nodiscard]] const std::string& getVertexShader() const; + [[nodiscard]] const std::string& getFragmentShader() const; + [[nodiscard]] const std::string& getGeometryShader() const; + + [[nodiscard]] Warnings generateWarnings() const; + + [[nodiscard]] std::string getErrors() const; + + [[nodiscard]] const glslang::TProgram* getProgram() const; + + private: + struct ShaderParts + { + std::string version; + std::string defines; + std::string userCode; + }; + + static std::string CreateDefineString(const std::vector& compilerDefines); + bool createShaderParts(ShaderParts& outParts, const std::string& defineString, const std::string& userShader, const std::string& shaderName) const; + bool parseShader(glslang::TShader& tShader, const TBuiltInResource& glslCompilationResources, const ShaderParts& shaderParts, const std::string& shaderName); + inline bool linkProgram(); + static std::string MergeShaderParts(const ShaderParts& shaderParts); + + mutable StringOutputStream m_errorMessages; + std::unique_ptr m_vertexShader; + std::unique_ptr m_fragmentShader; + std::unique_ptr m_geometryShader; + std::unique_ptr m_program; + + std::string m_vertexShaderFromParts; + std::string m_fragmentShaderFromParts; + std::string m_geometryShaderFromParts; + }; +} diff --git a/client/ramses-client/impl/glslEffectBlock/GlslToEffectConverter.cpp b/src/client/internal/glslEffectBlock/GlslToEffectConverter.cpp similarity index 92% rename from client/ramses-client/impl/glslEffectBlock/GlslToEffectConverter.cpp rename to src/client/internal/glslEffectBlock/GlslToEffectConverter.cpp index 182c680ce..0766478ec 100644 --- a/client/ramses-client/impl/glslEffectBlock/GlslToEffectConverter.cpp +++ b/src/client/internal/glslEffectBlock/GlslToEffectConverter.cpp @@ -6,22 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "glslEffectBlock/GlslToEffectConverter.h" +#include "internal/glslEffectBlock/GlslToEffectConverter.h" #define CHECK_RETURN_ERR(expr) if (!(expr)) return false -namespace ramses_internal +namespace ramses::internal { GlslToEffectConverter::GlslToEffectConverter(const HashMap& semanticInputs) : m_semanticInputs(semanticInputs) { } - GlslToEffectConverter::~GlslToEffectConverter() - { - } + GlslToEffectConverter::~GlslToEffectConverter() = default; - bool GlslToEffectConverter::parseShaderProgram(glslang::TProgram* program) + bool GlslToEffectConverter::parseShaderProgram(const glslang::TProgram* program) { CHECK_RETURN_ERR(parseLinkerObjectsForStage(program->getIntermediate(EShLangVertex)->getTreeRoot(), EShaderStage::Vertex)); // Parse data for vertex stage CHECK_RETURN_ERR(parseLinkerObjectsForStage(program->getIntermediate(EShLangFragment)->getTreeRoot(), EShaderStage::Fragment)); // Parse data for fragment stage @@ -132,7 +130,7 @@ namespace ramses_internal { return setInputTypeFromType(symbol->getType(), symbol->getName(), m_attributeInputs); } - else if (storageQualifier == glslang::EvqUniform) + if (storageQualifier == glslang::EvqUniform) { return setInputTypeFromType(symbol->getType(), symbol->getName(), m_uniformInputs); } @@ -162,11 +160,8 @@ namespace ramses_internal add = false; break; } - else - { - m_message << temp[i].inputName << ": uniform with same name but different data type declared in multiple stages"; - return false; - } + m_message << temp[i].inputName << ": uniform with same name but different data type declared in multiple stages"; + return false; } } @@ -181,10 +176,10 @@ namespace ramses_internal bool GlslToEffectConverter::setInputTypeFromType(const glslang::TType& type, std::string_view inputName, EffectInputInformationVector& outputVector) const { - uint32_t elementCount; + uint32_t elementCount = 0; CHECK_RETURN_ERR(getElementCountFromType(type, inputName, elementCount)); - assert(inputName.size() != 0); + assert(!inputName.empty()); assert(elementCount > 0); if (!type.isStruct()) @@ -200,10 +195,10 @@ namespace ramses_internal for(const auto& structField : *structFields) { const glslang::TType& fieldType = *structField.type; - const auto subName = getStructFieldIdentifier(inputName, fieldType.getFieldName(), type.isArray() ? static_cast(i) : -1); + const auto subName = GetStructFieldIdentifier(inputName, fieldType.getFieldName(), type.isArray() ? static_cast(i) : -1); // Get the element count for the field - uint32_t newElementCount; + uint32_t newElementCount = 0; CHECK_RETURN_ERR(getElementCountFromType(fieldType, inputName, newElementCount)); if (fieldType.isStruct()) @@ -222,7 +217,7 @@ namespace ramses_internal return true; } - std::string GlslToEffectConverter::getStructFieldIdentifier(std::string_view baseName, std::string_view fieldName, const int32_t arrayIndex) const + std::string GlslToEffectConverter::GetStructFieldIdentifier(std::string_view baseName, std::string_view fieldName, const int32_t arrayIndex) { StringOutputStream stream; stream << baseName; @@ -285,7 +280,7 @@ namespace ramses_internal bool GlslToEffectConverter::setInputTypeFromType(const glslang::TType& type, EffectInputInformation& input) const { - assert(input.inputName.size() != 0); + assert(!input.inputName.empty()); assert(!type.isStruct()); const glslang::TBasicType basicType = type.getBasicType(); @@ -296,11 +291,17 @@ namespace ramses_internal { case glslang::Esd2D: if (type.getSampler().isMultiSample()) + { input.dataType = EDataType::TextureSampler2DMS; - else if(type.getSampler().isExternal()) + } + else if (type.getSampler().isExternal()) + { input.dataType = EDataType::TextureSamplerExternal; + } else + { input.dataType = EDataType::TextureSampler2D; + } return true; case glslang::Esd3D: input.dataType = EDataType::TextureSampler3D; @@ -331,6 +332,8 @@ namespace ramses_internal case 4: input.dataType = EDataType::Vector4F; return true; + default: + break; } break; case glslang::EbtInt: @@ -345,6 +348,8 @@ namespace ramses_internal case 4: input.dataType = EDataType::Vector4I; return true; + default: + break; } break; default: @@ -391,7 +396,7 @@ namespace ramses_internal input.dataType = EDataType::Float; return true; case glslang::EbtBool: - input.dataType = EDataType::Int32; + input.dataType = EDataType::Bool; return true; case glslang::EbtInt: input.dataType = EDataType::Int32; @@ -409,8 +414,7 @@ namespace ramses_internal bool GlslToEffectConverter::setSemanticsOnInput(EffectInputInformation& input) const { - assert(input.inputName.size() != 0); - assert(input.dataType != EDataType::NUMBER_OF_ELEMENTS); + assert(!input.inputName.empty()); if (const EFixedSemantics* semantic = m_semanticInputs.get(input.inputName)) { if (!IsSemanticCompatibleWithDataType(*semantic, input.dataType)) diff --git a/client/ramses-client/impl/glslEffectBlock/GlslToEffectConverter.h b/src/client/internal/glslEffectBlock/GlslToEffectConverter.h similarity index 67% rename from client/ramses-client/impl/glslEffectBlock/GlslToEffectConverter.h rename to src/client/internal/glslEffectBlock/GlslToEffectConverter.h index 872e20f3d..429a506fd 100644 --- a/client/ramses-client/impl/glslEffectBlock/GlslToEffectConverter.h +++ b/src/client/internal/glslEffectBlock/GlslToEffectConverter.h @@ -6,21 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GLSLTOEFFECTCONVERTER_H -#define RAMSES_GLSLTOEFFECTCONVERTER_H +#pragma once -#include "glslEffectBlock/GLSlang.h" -#include "Collections/StringOutputStream.h" -#include "Collections/HashMap.h" -#include "SceneAPI/EFixedSemantics.h" -#include "Resource/EffectInputInformation.h" -#include "SceneAPI/RenderState.h" -#include +#include "ramses/framework/AppearanceEnums.h" + +#include "internal/glslEffectBlock/GLSlang.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" +#include "internal/SceneGraph/SceneAPI/EShaderStage.h" +#include "internal/SceneGraph/Resource/EffectInputInformation.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { class GlslToEffectConverter { @@ -28,23 +29,15 @@ namespace ramses_internal explicit GlslToEffectConverter(const HashMap& semanticInputs); ~GlslToEffectConverter(); - bool parseShaderProgram(glslang::TProgram* program); - std::string getStatusMessage() const; + [[nodiscard]] bool parseShaderProgram(const glslang::TProgram* program); + [[nodiscard]] std::string getStatusMessage() const; - const EffectInputInformationVector& getUniformInputs() const; - const EffectInputInformationVector& getAttributeInputs() const; + [[nodiscard]] const EffectInputInformationVector& getUniformInputs() const; + [[nodiscard]] const EffectInputInformationVector& getAttributeInputs() const; - std::optional getGeometryShaderInputType() const; + [[nodiscard]] std::optional getGeometryShaderInputType() const; private: - enum class EShaderStage - { - Vertex = 1, - Geometry = 2, - Fragment = 4, - Invalid - }; - HashMap m_semanticInputs; mutable StringOutputStream m_message; @@ -64,8 +57,6 @@ namespace ramses_internal bool setSemanticsOnInput(EffectInputInformation& input) const; bool makeUniformsUnique(); bool createEffectInputType(const glslang::TType& type, std::string_view inputName, uint32_t elementCount, EffectInputInformationVector& outputVector) const; - std::string getStructFieldIdentifier(std::string_view baseName, std::string_view fieldName, const int32_t arrayIndex) const; + static std::string GetStructFieldIdentifier(std::string_view baseName, std::string_view fieldName, const int32_t arrayIndex); }; } - -#endif diff --git a/client/logic/lib/internals/ApiObjects.cpp b/src/client/internal/logic/ApiObjects.cpp similarity index 52% rename from client/logic/lib/internals/ApiObjects.cpp rename to src/client/internal/logic/ApiObjects.cpp index a0136a9e6..4684bbeb6 100644 --- a/client/logic/lib/internals/ApiObjects.cpp +++ b/src/client/internal/logic/ApiObjects.cpp @@ -6,72 +6,74 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/ApiObjects.h" -#include "internals/ErrorReporting.h" +#include "internal/logic/ApiObjects.h" +#include "impl/ErrorReporting.h" #include "ramses-sdk-build-config.h" -#include "ramses-logic/LogicObject.h" -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" - -#include "impl/PropertyImpl.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LuaInterfaceImpl.h" -#include "impl/LuaModuleImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" -#include "impl/RamsesRenderPassBindingImpl.h" -#include "impl/RamsesRenderGroupBindingImpl.h" -#include "impl/RamsesMeshNodeBindingImpl.h" -#include "impl/SkinBindingImpl.h" -#include "impl/DataArrayImpl.h" -#include "impl/AnimationNodeImpl.h" -#include "impl/AnimationNodeConfigImpl.h" -#include "impl/TimerNodeImpl.h" -#include "impl/AnchorPointImpl.h" - -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/MeshNode.h" - -#include "generated/ApiObjectsGen.h" -#include "generated/RamsesAppearanceBindingGen.h" -#include "generated/RamsesBindingGen.h" -#include "generated/RamsesCameraBindingGen.h" -#include "generated/RamsesNodeBindingGen.h" -#include "generated/LinkGen.h" -#include "generated/DataArrayGen.h" -#include "generated/AnimationNodeGen.h" -#include "generated/TimerNodeGen.h" +#include "ramses/client/logic/LogicObject.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" + +#include "impl/ValidationReportImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LuaInterfaceImpl.h" +#include "impl/logic/LuaModuleImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/logic/AnimationNodeImpl.h" +#include "impl/logic/AnimationNodeConfigImpl.h" +#include "impl/logic/TimerNodeImpl.h" +#include "impl/logic/AnchorPointImpl.h" + +#include "ramses/client/Scene.h" +#include "ramses/client/Node.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Camera.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" + +#include "internal/logic/flatbuffers/generated/ApiObjectsGen.h" +#include "internal/logic/flatbuffers/generated/AppearanceBindingGen.h" +#include "internal/logic/flatbuffers/generated/RamsesBindingGen.h" +#include "internal/logic/flatbuffers/generated/CameraBindingGen.h" +#include "internal/logic/flatbuffers/generated/NodeBindingGen.h" +#include "internal/logic/flatbuffers/generated/LinkGen.h" +#include "internal/logic/flatbuffers/generated/DataArrayGen.h" +#include "internal/logic/flatbuffers/generated/AnimationNodeGen.h" +#include "internal/logic/flatbuffers/generated/TimerNodeGen.h" #include "fmt/format.h" #include "TypeUtils.h" -#include "ValidationResults.h" #include namespace ramses::internal { - ApiObjects::ApiObjects(ramses::EFeatureLevel featureLevel) + ApiObjects::ApiObjects(ramses::EFeatureLevel featureLevel, SceneImpl& scene) : m_featureLevel{ featureLevel } + , m_scene{ scene } { (void)m_featureLevel; // maybe unused if not affecting any internal objects but kept for future levels } @@ -85,7 +87,7 @@ namespace ramses::internal if (m_luaModules.cend() == std::find_if(m_luaModules.cbegin(), m_luaModules.cend(), [&module](const auto& m) { return m == module.second; })) { - errorReporting.add(fmt::format("Failed to map Lua module '{}'! It was created on a different instance of LogicEngine.", module.first), module.second, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Failed to map Lua module '{}'! It was created on a different instance of LogicEngine.", module.first), module.second); return false; } } @@ -116,7 +118,7 @@ namespace ramses::internal if (!compiledScript) return nullptr; - auto impl = std::make_unique(std::move(*compiledScript), scriptName, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, std::move(*compiledScript), scriptName, sceneObjectId_t{}); impl->createRootProperties(); return &createAndRegisterObject(std::move(impl)); @@ -126,12 +128,11 @@ namespace ramses::internal std::string_view source, const LuaConfigImpl& config, std::string_view interfaceName, - ErrorReporting& errorReporting, - bool verifyModules) + ErrorReporting& errorReporting) { if (interfaceName.empty()) { - errorReporting.add("Can't create interface with empty name!", nullptr, EErrorType::IllegalArgument); + errorReporting.set("Can't create interface with empty name!", nullptr); return nullptr; } @@ -143,7 +144,6 @@ namespace ramses::internal *m_solState, modules, config.getStandardModules(), - verifyModules, std::string{ source }, interfaceName, errorReporting); @@ -151,7 +151,7 @@ namespace ramses::internal if (!compiledInterface) return nullptr; - auto impl = std::make_unique(std::move(*compiledInterface), interfaceName, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, std::move(*compiledInterface), interfaceName, sceneObjectId_t{}); impl->createRootProperties(); return &createAndRegisterObject(std::move(impl)); @@ -180,60 +180,60 @@ namespace ramses::internal if (!compiledModule) return nullptr; - auto impl = std::make_unique(std::move(*compiledModule), moduleName, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, std::move(*compiledModule), moduleName, sceneObjectId_t{}); return &createAndRegisterObject(std::move(impl)); } - RamsesNodeBinding* ApiObjects::createRamsesNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name) + NodeBinding* ApiObjects::createNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name) { - auto impl = std::make_unique(ramsesNode, rotationType, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, ramsesNode, rotationType, name, sceneObjectId_t{}); impl->createRootProperties(); - return &createAndRegisterObject(std::move(impl)); + return &createAndRegisterObject(std::move(impl)); } - RamsesAppearanceBinding* ApiObjects::createRamsesAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name) + AppearanceBinding* ApiObjects::createAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name) { - auto impl = std::make_unique(ramsesAppearance, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, ramsesAppearance, name, sceneObjectId_t{}); impl->createRootProperties(); - return &createAndRegisterObject(std::move(impl)); + return &createAndRegisterObject(std::move(impl)); } - RamsesCameraBinding* ApiObjects::createRamsesCameraBinding(ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name) + CameraBinding* ApiObjects::createCameraBinding(ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name) { - auto impl = std::make_unique(ramsesCamera, withFrustumPlanes, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, ramsesCamera, withFrustumPlanes, name, sceneObjectId_t{}); impl->createRootProperties(); - return &createAndRegisterObject(std::move(impl)); + return &createAndRegisterObject(std::move(impl)); } - RamsesRenderPassBinding* ApiObjects::createRamsesRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name) + RenderPassBinding* ApiObjects::createRenderPassBinding(ramses::RenderPass& ramsesRenderPass, std::string_view name) { - auto impl = std::make_unique(ramsesRenderPass, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, ramsesRenderPass, name, sceneObjectId_t{}); impl->createRootProperties(); - return &createAndRegisterObject(std::move(impl)); + return &createAndRegisterObject(std::move(impl)); } - RamsesRenderGroupBinding* ApiObjects::createRamsesRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElements& elements, std::string_view name) + RenderGroupBinding* ApiObjects::createRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElements& elements, std::string_view name) { - auto impl = std::make_unique(ramsesRenderGroup, *elements.m_impl, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, ramsesRenderGroup, elements.impl(), name, sceneObjectId_t{}); impl->createRootProperties(); - return &createAndRegisterObject(std::move(impl)); + return &createAndRegisterObject(std::move(impl)); } - RamsesMeshNodeBinding* ApiObjects::createRamsesMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name) + MeshNodeBinding* ApiObjects::createMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name) { - auto impl = std::make_unique(ramsesMeshNode, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, ramsesMeshNode, name, sceneObjectId_t{}); impl->createRootProperties(); - return &createAndRegisterObject(std::move(impl)); + return &createAndRegisterObject(std::move(impl)); } SkinBinding* ApiObjects::createSkinBinding( - std::vector joints, + std::vector joints, const std::vector& inverseBindMatrices, - RamsesAppearanceBindingImpl& appearanceBinding, + AppearanceBindingImpl& appearanceBinding, const ramses::UniformInput& jointMatInput, std::string_view name) { - auto impl = std::make_unique(std::move(joints), inverseBindMatrices, appearanceBinding, jointMatInput, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, std::move(joints), inverseBindMatrices, appearanceBinding, jointMatInput, name, sceneObjectId_t{}); impl->createRootProperties(); return &createAndRegisterObject(std::move(impl)); } @@ -244,27 +244,27 @@ namespace ramses::internal static_assert(CanPropertyTypeBeStoredInDataArray(PropertyTypeToEnum::TYPE)); // make copy of users data and move into data array std::vector dataCopy = data; - auto impl = std::make_unique(std::move(dataCopy), name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, std::move(dataCopy), name, sceneObjectId_t{}); return &createAndRegisterObject(std::move(impl)); } AnimationNode* ApiObjects::createAnimationNode(const AnimationNodeConfigImpl& config, std::string_view name) { - auto impl = std::make_unique(config.getChannels(), config.getExposingOfChannelDataAsProperties(), name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, config.getChannels(), config.getExposingOfChannelDataAsProperties(), name, sceneObjectId_t{}); impl->createRootProperties(); return &createAndRegisterObject(std::move(impl)); } TimerNode* ApiObjects::createTimerNode(std::string_view name) { - auto impl = std::make_unique(name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, name, sceneObjectId_t{}); impl->createRootProperties(); return &createAndRegisterObject(std::move(impl)); } - AnchorPoint* ApiObjects::createAnchorPoint(RamsesNodeBindingImpl& nodeBinding, RamsesCameraBindingImpl& cameraBinding, std::string_view name) + AnchorPoint* ApiObjects::createAnchorPoint(NodeBindingImpl& nodeBinding, CameraBindingImpl& cameraBinding, std::string_view name) { - auto impl = std::make_unique(nodeBinding, cameraBinding, name, getNextLogicObjectId()); + auto impl = std::make_unique(m_scene, nodeBinding, cameraBinding, name, sceneObjectId_t{}); impl->createRootProperties(); auto& anchor = createAndRegisterObject(std::move(impl)); @@ -288,29 +288,29 @@ namespace ramses::internal if (luaModule) return destroyInternal(*luaModule, errorReporting); - auto ramsesNodeBinding = dynamic_cast(&object); - if (ramsesNodeBinding) - return destroyInternal(*ramsesNodeBinding, errorReporting); + auto nodeBinding = dynamic_cast(&object); + if (nodeBinding) + return destroyInternal(*nodeBinding, errorReporting); - auto ramsesAppearanceBinding = dynamic_cast(&object); - if (ramsesAppearanceBinding) - return destroyInternal(*ramsesAppearanceBinding, errorReporting); + auto appearanceBinding = dynamic_cast(&object); + if (appearanceBinding) + return destroyInternal(*appearanceBinding, errorReporting); - auto ramsesCameraBinding = dynamic_cast(&object); - if (ramsesCameraBinding) - return destroyInternal(*ramsesCameraBinding, errorReporting); + auto cameraBinding = dynamic_cast(&object); + if (cameraBinding) + return destroyInternal(*cameraBinding, errorReporting); - auto ramsesRenderPassBinding = dynamic_cast(&object); - if (ramsesRenderPassBinding) - return destroyAndUnregisterObject(*ramsesRenderPassBinding, errorReporting); + auto renderPassBinding = dynamic_cast(&object); + if (renderPassBinding) + return destroyAndUnregisterObject(*renderPassBinding, errorReporting); - auto ramsesRenderGroupBinding = dynamic_cast(&object); - if (ramsesRenderGroupBinding) - return destroyAndUnregisterObject(*ramsesRenderGroupBinding, errorReporting); + auto renderGroupBinding = dynamic_cast(&object); + if (renderGroupBinding) + return destroyAndUnregisterObject(*renderGroupBinding, errorReporting); - auto ramsesMeshNodeBinding = dynamic_cast(&object); - if (ramsesMeshNodeBinding) - return destroyAndUnregisterObject(*ramsesMeshNodeBinding, errorReporting); + auto meshNodeBinding = dynamic_cast(&object); + if (meshNodeBinding) + return destroyAndUnregisterObject(*meshNodeBinding, errorReporting); auto skinBinding = dynamic_cast(&object); if (skinBinding) @@ -332,7 +332,7 @@ namespace ramses::internal if (anchor) return destroyInternal(*anchor, errorReporting); - errorReporting.add(fmt::format("Tried to destroy object '{}' with unknown type", object.getName()), &object, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Tried to destroy object '{}' with unknown type", object.getName()), &object); return false; } @@ -348,7 +348,7 @@ namespace ramses::internal channel.tangentsIn == &dataArray || channel.tangentsOut == &dataArray) { - errorReporting.add(fmt::format("Failed to destroy data array '{}', it is used in animation node '{}' channel '{}'", dataArray.getName(), animNode->getName(), channel.name), &dataArray, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Failed to destroy data array '{}', it is used in animation node '{}' channel '{}'", dataArray.getName(), animNode->getName(), channel.name), &dataArray); return false; } } @@ -361,11 +361,11 @@ namespace ramses::internal { for (const auto& script : m_scripts) { - for (const auto& moduleInUse : script->m_script.getModules()) + for (const auto& moduleInUse : script->impl().getModules()) { if (moduleInUse.second == &luaModule) { - errorReporting.add(fmt::format("Failed to destroy LuaModule '{}', it is used in LuaScript '{}'", luaModule.getName(), script->getName()), &luaModule, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Failed to destroy LuaModule '{}', it is used in LuaScript '{}'", luaModule.getName(), script->getName()), &luaModule); return false; } } @@ -374,66 +374,66 @@ namespace ramses::internal return destroyAndUnregisterObject(luaModule, errorReporting); } - bool ApiObjects::destroyInternal(RamsesNodeBinding& ramsesNodeBinding, ErrorReporting& errorReporting) + bool ApiObjects::destroyInternal(NodeBinding& nodeBinding, ErrorReporting& errorReporting) { for (const auto& anchor : m_anchorPoints) { - if (anchor->m_anchorPointImpl.getRamsesNodeBinding().getId() == ramsesNodeBinding.getId()) + if (anchor->impl().getNodeBinding().getSceneObjectId() == nodeBinding.getSceneObjectId()) { - errorReporting.add(fmt::format("Failed to destroy Ramses node binding '{}', it is used in anchor point '{}'", ramsesNodeBinding.getName(), anchor->getName()), &ramsesNodeBinding, EErrorType::Other); + errorReporting.set(fmt::format("Failed to destroy Ramses node binding '{}', it is used in anchor point '{}'", nodeBinding.getName(), anchor->getName()), &nodeBinding); return false; } } for (const auto& skin : m_skinBindings) { - for (const auto node : skin->m_skinBinding.getJoints()) + for (const auto node : skin->impl().getJoints()) { - if (node->getId() == ramsesNodeBinding.getId()) + if (node->getSceneObjectId() == nodeBinding.getSceneObjectId()) { - errorReporting.add(fmt::format("Failed to destroy Ramses node binding '{}', it is used in skin binding '{}'", ramsesNodeBinding.getName(), skin->getName()), &ramsesNodeBinding, EErrorType::Other); + errorReporting.set(fmt::format("Failed to destroy Ramses node binding '{}', it is used in skin binding '{}'", nodeBinding.getName(), skin->getName()), &nodeBinding); return false; } } } - return destroyAndUnregisterObject(ramsesNodeBinding, errorReporting); + return destroyAndUnregisterObject(nodeBinding, errorReporting); } - bool ApiObjects::destroyInternal(RamsesAppearanceBinding& ramsesAppearanceBinding, ErrorReporting& errorReporting) + bool ApiObjects::destroyInternal(AppearanceBinding& appearanceBinding, ErrorReporting& errorReporting) { for (const auto& skin : m_skinBindings) { - if (skin->m_skinBinding.getAppearanceBinding().getId() == ramsesAppearanceBinding.getId()) + if (skin->impl().getAppearanceBinding().getSceneObjectId() == appearanceBinding.getSceneObjectId()) { - errorReporting.add(fmt::format("Failed to destroy Ramses appearance binding '{}', it is used in skin binding '{}'", ramsesAppearanceBinding.getName(), skin->getName()), &ramsesAppearanceBinding, EErrorType::Other); + errorReporting.set(fmt::format("Failed to destroy Ramses appearance binding '{}', it is used in skin binding '{}'", appearanceBinding.getName(), skin->getName()), &appearanceBinding); return false; } } - return destroyAndUnregisterObject(ramsesAppearanceBinding, errorReporting); + return destroyAndUnregisterObject(appearanceBinding, errorReporting); } - bool ApiObjects::destroyInternal(RamsesCameraBinding& ramsesCameraBinding, ErrorReporting& errorReporting) + bool ApiObjects::destroyInternal(CameraBinding& cameraBinding, ErrorReporting& errorReporting) { for (const auto& anchor : m_anchorPoints) { - if (anchor->m_anchorPointImpl.getRamsesCameraBinding().getId() == ramsesCameraBinding.getId()) + if (anchor->impl().getCameraBinding().getSceneObjectId() == cameraBinding.getSceneObjectId()) { - errorReporting.add(fmt::format("Failed to destroy Ramses camera binding '{}', it is used in anchor point '{}'", ramsesCameraBinding.getName(), anchor->getName()), &ramsesCameraBinding, EErrorType::Other); + errorReporting.set(fmt::format("Failed to destroy Ramses camera binding '{}', it is used in anchor point '{}'", cameraBinding.getName(), anchor->getName()), &cameraBinding); return false; } } - return destroyAndUnregisterObject(ramsesCameraBinding, errorReporting); + return destroyAndUnregisterObject(cameraBinding, errorReporting); } bool ApiObjects::destroyInternal(AnchorPoint& node, ErrorReporting& errorReporting) { if (std::find(m_anchorPoints.cbegin(), m_anchorPoints.cend(), &node) != m_anchorPoints.end()) { - m_logicNodeDependencies.removeBindingDependency(node.m_anchorPointImpl.getRamsesNodeBinding(), node.m_impl); - m_logicNodeDependencies.removeBindingDependency(node.m_anchorPointImpl.getRamsesCameraBinding(), node.m_impl); + m_logicNodeDependencies.removeBindingDependency(node.impl().getNodeBinding(), node.m_impl); + m_logicNodeDependencies.removeBindingDependency(node.impl().getCameraBinding(), node.m_impl); } return destroyAndUnregisterObject(node, errorReporting); @@ -452,14 +452,12 @@ namespace ramses::internal m_objectsOwningContainer.push_back(std::move(object)); // set reference to HL object so impl can access its HL instance - objRaw.LogicObject::m_impl->setLogicObject(objRaw); + objRaw.impl().setLogicObject(objRaw); // store in general pool of logic objects assert(std::find(m_logicObjects.cbegin(), m_logicObjects.cend(), &objRaw) == m_logicObjects.cend()); + assert(std::find_if(m_logicObjects.cbegin(), m_logicObjects.cend(), [&objRaw](const auto o) { return o->getSceneObjectId() == objRaw.getSceneObjectId(); }) == m_logicObjects.cend()); m_logicObjects.push_back(&objRaw); - // store in map to retrieve object by ID - assert(m_logicObjectIdMapping.count(objRaw.getId()) == 0u); - m_logicObjectIdMapping.emplace(objRaw.getId(), &objRaw); // put into own scope as clang-tidy is confused with constexpr branches and reports indentation warning { @@ -476,29 +474,29 @@ namespace ramses::internal { this->m_luaModules.push_back(&objRaw); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - this->m_ramsesNodeBindings.push_back(&objRaw); + this->m_nodeBindings.push_back(&objRaw); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - this->m_ramsesAppearanceBindings.push_back(&objRaw); + this->m_appearanceBindings.push_back(&objRaw); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - this->m_ramsesCameraBindings.push_back(&objRaw); + this->m_cameraBindings.push_back(&objRaw); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - this->m_ramsesRenderPassBindings.push_back(&objRaw); + this->m_renderPassBindings.push_back(&objRaw); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - this->m_ramsesRenderGroupBindings.push_back(&objRaw); + this->m_renderGroupBindings.push_back(&objRaw); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - this->m_ramsesMeshNodeBindings.push_back(&objRaw); + this->m_meshNodeBindings.push_back(&objRaw); } else if constexpr (std::is_same_v) { @@ -541,7 +539,7 @@ namespace ramses::internal const auto findOwnedObj = std::find_if(m_objectsOwningContainer.cbegin(), m_objectsOwningContainer.cend(), [&](const auto& obj) { return obj.get() == &objToDelete; }); if (findOwnedObj == m_objectsOwningContainer.cend()) { - errorReporting.add(fmt::format("Failed to destroy object '{}', cannot find it in this LogicEngine instance.", objToDelete.LogicObject::m_impl->getIdentificationString()), &objToDelete, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Failed to destroy object '{}', cannot find it in this LogicEngine instance.", objToDelete.impl().getIdentificationString()), &objToDelete); return false; } @@ -570,29 +568,29 @@ namespace ramses::internal { eraseFromPool(objToDelete, this->m_luaModules); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - eraseFromPool(objToDelete, this->m_ramsesNodeBindings); + eraseFromPool(objToDelete, this->m_nodeBindings); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - eraseFromPool(objToDelete, this->m_ramsesAppearanceBindings); + eraseFromPool(objToDelete, this->m_appearanceBindings); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - eraseFromPool(objToDelete, this->m_ramsesCameraBindings); + eraseFromPool(objToDelete, this->m_cameraBindings); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - eraseFromPool(objToDelete, this->m_ramsesRenderPassBindings); + eraseFromPool(objToDelete, this->m_renderPassBindings); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - eraseFromPool(objToDelete, this->m_ramsesRenderGroupBindings); + eraseFromPool(objToDelete, this->m_renderGroupBindings); } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - eraseFromPool(objToDelete, this->m_ramsesMeshNodeBindings); + eraseFromPool(objToDelete, this->m_meshNodeBindings); } else if constexpr (std::is_same_v) { @@ -620,9 +618,6 @@ namespace ramses::internal } } - assert(m_logicObjectIdMapping.count(objToDelete.getId()) != 0u); - m_logicObjectIdMapping.erase(objToDelete.getId()); - const auto findLogicObj = std::find(m_logicObjects.cbegin(), m_logicObjects.cend(), &objToDelete); assert(findLogicObj != m_logicObjects.cend() && "Can't find LogicObject in logic objects!"); m_logicObjects.erase(findLogicObj); @@ -632,111 +627,7 @@ namespace ramses::internal return true; } - bool ApiObjects::checkBindingsReferToSameRamsesScene(ErrorReporting& errorReporting) const - { - // Optional because it's OK that no Ramses object is referenced at all (and thus no ramses scene) - std::optional sceneId; - - for (const auto& binding : m_ramsesNodeBindings) - { - const ramses::Node& node = binding->m_nodeBinding.getRamsesNode(); - const ramses::sceneId_t nodeSceneId = node.getSceneId(); - if (!sceneId) - { - sceneId = nodeSceneId; - } - - if (*sceneId != nodeSceneId) - { - errorReporting.add(fmt::format("Ramses node '{}' is from scene with id:{} but other objects are from scene with id:{}!", - node.getName(), nodeSceneId.getValue(), sceneId->getValue()), binding, EErrorType::IllegalArgument); - return false; - } - } - - for (const auto& binding : m_ramsesAppearanceBindings) - { - const ramses::Appearance& appearance = binding->m_appearanceBinding.getRamsesAppearance(); - const ramses::sceneId_t appearanceSceneId = appearance.getSceneId(); - if (!sceneId) - { - sceneId = appearanceSceneId; - } - - if (*sceneId != appearanceSceneId) - { - errorReporting.add(fmt::format("Ramses appearance '{}' is from scene with id:{} but other objects are from scene with id:{}!", - appearance.getName(), appearanceSceneId.getValue(), sceneId->getValue()), binding, EErrorType::IllegalArgument); - return false; - } - } - - for (const auto& binding : m_ramsesCameraBindings) - { - const ramses::Camera& camera = binding->m_cameraBinding.getRamsesCamera(); - const ramses::sceneId_t cameraSceneId = camera.getSceneId(); - if (!sceneId) - { - sceneId = cameraSceneId; - } - - if (*sceneId != cameraSceneId) - { - errorReporting.add(fmt::format("Ramses camera '{}' is from scene with id:{} but other objects are from scene with id:{}!", - camera.getName(), cameraSceneId.getValue(), sceneId->getValue()), binding, EErrorType::IllegalArgument); - return false; - } - } - - for (const auto& binding : m_ramsesRenderPassBindings) - { - const ramses::RenderPass& rp = binding->m_renderPassBinding.getRamsesRenderPass(); - const ramses::sceneId_t rpSceneId = rp.getSceneId(); - if (!sceneId) - sceneId = rpSceneId; - - if (*sceneId != rpSceneId) - { - errorReporting.add(fmt::format("Ramses render pass '{}' is from scene with id:{} but other objects are from scene with id:{}!", - rp.getName(), rpSceneId.getValue(), sceneId->getValue()), binding, EErrorType::IllegalArgument); - return false; - } - } - - for (const auto& binding : m_ramsesRenderGroupBindings) - { - const auto& rg = binding->m_renderGroupBinding.getRamsesRenderGroup(); - const ramses::sceneId_t rgSceneId = rg.getSceneId(); - if (!sceneId) - sceneId = rgSceneId; - - if (*sceneId != rgSceneId) - { - errorReporting.add(fmt::format("Ramses render group '{}' is from scene with id:{} but other objects are from scene with id:{}!", - rg.getName(), rgSceneId.getValue(), sceneId->getValue()), binding, EErrorType::IllegalArgument); - return false; - } - } - - for (const auto& binding : m_ramsesMeshNodeBindings) - { - const auto& mn = binding->m_meshNodeBinding.getRamsesMeshNode(); - const ramses::sceneId_t mnSceneId = mn.getSceneId(); - if (!sceneId) - sceneId = mnSceneId; - - if (*sceneId != mnSceneId) - { - errorReporting.add(fmt::format("Ramses mesh node '{}' is from scene with id:{} but other objects are from scene with id:{}!", - mn.getName(), mnSceneId.getValue(), sceneId->getValue()), binding, EErrorType::IllegalArgument); - return false; - } - } - - return true; - } - - void ApiObjects::validateInterfaces(ValidationResults& validationResults) const + void ApiObjects::validateInterfaces(ValidationReportImpl& report) const { // check if there are any outputs without link // Note: this is different from the check for dangling nodes/content, where nodes are checked if they have ANY @@ -747,7 +638,7 @@ namespace ramses::internal { std::vector unlinkedProperties = intf->m_interface.collectUnlinkedProperties(); for (const auto* output : unlinkedProperties) - validationResults.add(::fmt::format("Interface [{}] has unlinked output [{}]", intf->getName(), output->getName()), intf, EWarningType::UnusedContent); + report.add(EIssueType::Warning, ::fmt::format("Interface [{}] has unlinked output [{}]", intf->getName(), output->getName()), intf); } // check if there are any name conflicts @@ -755,10 +646,10 @@ namespace ramses::internal std::sort(interfacesByName.begin(), interfacesByName.end(), [](const auto i1, const auto i2) { return i1->getName() < i2->getName(); }); const auto duplicateIt = std::adjacent_find(interfacesByName.cbegin(), interfacesByName.cend(), [](const auto i1, const auto i2) { return i1->getName() == i2->getName(); }); if (duplicateIt != interfacesByName.cend()) - validationResults.add(fmt::format("Interface [{}] does not have a unique name", (*duplicateIt)->getName()), *duplicateIt, EWarningType::Other); + report.add(EIssueType::Error, fmt::format("Interface [{}] does not have a unique name", (*duplicateIt)->getName()), *duplicateIt); } - void ApiObjects::validateDanglingNodes(ValidationResults& validationResults) const + void ApiObjects::validateDanglingNodes(ValidationReportImpl& report) const { //nodes with no outputs linked for (const auto* logicObj : m_logicObjects) @@ -776,11 +667,11 @@ namespace ramses::internal if (objAsNode->getOutputs()->getChildCount() != 0u) { bool anyOutputLinked = false; - for (const auto* output : objAsNode->getOutputs()->m_impl->collectLeafChildren()) + for (const auto* output : objAsNode->getOutputs()->impl().collectLeafChildren()) anyOutputLinked |= (output->hasOutgoingLink()); if (!anyOutputLinked) - validationResults.add(fmt::format("Node [{}] has no outgoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode, EWarningType::UnusedContent); + report.add(EIssueType::Warning, fmt::format("Node [{}] has no outgoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode); } } } @@ -788,15 +679,15 @@ namespace ramses::internal // collect node bindings used in anchor points and skin bindings // - these are allowed to have no incoming links because they are being read from std::unordered_set bindingsInUse; - for (const auto* anchor : m_anchorPoints) + for (auto* anchor : m_anchorPoints) { - bindingsInUse.insert(anchor->m_anchorPointImpl.getRamsesNodeBinding().getLogicObject().as()); - bindingsInUse.insert(anchor->m_anchorPointImpl.getRamsesCameraBinding().getLogicObject().as()); + bindingsInUse.insert(anchor->impl().getNodeBinding().getLogicObject().as()); + bindingsInUse.insert(anchor->impl().getCameraBinding().getLogicObject().as()); } for (const auto* skin : m_skinBindings) { - bindingsInUse.insert(skin->m_skinBinding.getAppearanceBinding().getLogicObject().as()); - for (const auto* joint : skin->m_skinBinding.getJoints()) + bindingsInUse.insert(skin->impl().getAppearanceBinding().getLogicObject().as()); + for (const auto* joint : skin->impl().getJoints()) bindingsInUse.insert(joint->getLogicObject().as()); } @@ -820,11 +711,11 @@ namespace ramses::internal if (objAsNode->getInputs()->getChildCount() != 0u) { bool anyInputLinked = false; - for (const auto* input : objAsNode->getInputs()->m_impl->collectLeafChildren()) + for (const auto* input : objAsNode->getInputs()->impl().collectLeafChildren()) anyInputLinked |= (input->hasIncomingLink()); if (!anyInputLinked) - validationResults.add(fmt::format("Node [{}] has no ingoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode, EWarningType::UnusedContent); + report.add(EIssueType::Warning, fmt::format("Node [{}] has no ingoing links! Node should be deleted or properly linked!", objAsNode->getName()), objAsNode); } } } @@ -849,29 +740,29 @@ namespace ramses::internal { return m_luaModules; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return m_ramsesNodeBindings; + return m_nodeBindings; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return m_ramsesAppearanceBindings; + return m_appearanceBindings; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return m_ramsesCameraBindings; + return m_cameraBindings; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return m_ramsesRenderPassBindings; + return m_renderPassBindings; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return m_ramsesRenderGroupBindings; + return m_renderGroupBindings; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { - return m_ramsesMeshNodeBindings; + return m_meshNodeBindings; } else if constexpr (std::is_same_v) { @@ -917,17 +808,6 @@ namespace ramses::internal return m_logicNodeDependencies; } - LogicObject* ApiObjects::getApiObjectById(uint64_t id) const - { - auto apiObjectIter = m_logicObjectIdMapping.find(id); - if (apiObjectIter != m_logicObjectIdMapping.end()) - { - assert(apiObjectIter->second->getId() == id); - return apiObjectIter->second; - } - return nullptr; - } - flatbuffers::Offset ApiObjects::Serialize(const ApiObjects& apiObjects, flatbuffers::FlatBufferBuilder& builder, ELuaSavingMode luaSavingMode) { SerializationMap serializationMap; @@ -941,7 +821,7 @@ namespace ramses::internal luascripts.reserve(apiObjects.m_scripts.size()); std::transform(apiObjects.m_scripts.begin(), apiObjects.m_scripts.end(), std::back_inserter(luascripts), [&builder, &serializationMap, luaSavingMode](const std::vector::value_type& it) { - return LuaScriptImpl::Serialize(it->m_script, builder, serializationMap, luaSavingMode); + return LuaScriptImpl::Serialize(it->impl(), builder, serializationMap, luaSavingMode); }); std::vector> luaInterfaces; @@ -951,40 +831,40 @@ namespace ramses::internal return LuaInterfaceImpl::Serialize(it->m_interface, builder, serializationMap); }); - std::vector> ramsesnodebindings; - ramsesnodebindings.reserve(apiObjects.m_ramsesNodeBindings.size()); - std::transform(apiObjects.m_ramsesNodeBindings.begin(), - apiObjects.m_ramsesNodeBindings.end(), - std::back_inserter(ramsesnodebindings), - [&builder, &serializationMap](const std::vector::value_type& it) { - return RamsesNodeBindingImpl::Serialize(it->m_nodeBinding, builder, serializationMap); + std::vector> nodebindings; + nodebindings.reserve(apiObjects.m_nodeBindings.size()); + std::transform(apiObjects.m_nodeBindings.begin(), + apiObjects.m_nodeBindings.end(), + std::back_inserter(nodebindings), + [&builder, &serializationMap](const std::vector::value_type& it) { + return NodeBindingImpl::Serialize(it->impl(), builder, serializationMap); }); - std::vector> ramsesappearancebindings; - ramsesappearancebindings.reserve(apiObjects.m_ramsesAppearanceBindings.size()); - std::transform(apiObjects.m_ramsesAppearanceBindings.begin(), - apiObjects.m_ramsesAppearanceBindings.end(), - std::back_inserter(ramsesappearancebindings), - [&builder, &serializationMap](const std::vector::value_type& it) { - return RamsesAppearanceBindingImpl::Serialize(it->m_appearanceBinding, builder, serializationMap); + std::vector> appearancebindings; + appearancebindings.reserve(apiObjects.m_appearanceBindings.size()); + std::transform(apiObjects.m_appearanceBindings.begin(), + apiObjects.m_appearanceBindings.end(), + std::back_inserter(appearancebindings), + [&builder, &serializationMap](const std::vector::value_type& it) { + return AppearanceBindingImpl::Serialize(it->impl(), builder, serializationMap); }); - std::vector> ramsescamerabindings; - ramsescamerabindings.reserve(apiObjects.m_ramsesCameraBindings.size()); - std::transform(apiObjects.m_ramsesCameraBindings.begin(), - apiObjects.m_ramsesCameraBindings.end(), - std::back_inserter(ramsescamerabindings), - [&builder, &serializationMap](const std::vector::value_type& it) { - return RamsesCameraBindingImpl::Serialize(it->m_cameraBinding, builder, serializationMap); + std::vector> camerabindings; + camerabindings.reserve(apiObjects.m_cameraBindings.size()); + std::transform(apiObjects.m_cameraBindings.begin(), + apiObjects.m_cameraBindings.end(), + std::back_inserter(camerabindings), + [&builder, &serializationMap](const std::vector::value_type& it) { + return CameraBindingImpl::Serialize(it->impl(), builder, serializationMap); }); - std::vector> ramsesrenderpassbindings; - ramsesrenderpassbindings.reserve(apiObjects.m_ramsesRenderPassBindings.size()); - std::transform(apiObjects.m_ramsesRenderPassBindings.begin(), - apiObjects.m_ramsesRenderPassBindings.end(), - std::back_inserter(ramsesrenderpassbindings), - [&builder, &serializationMap](const std::vector::value_type& it) { - return RamsesRenderPassBindingImpl::Serialize(it->m_renderPassBinding, builder, serializationMap); + std::vector> renderpassbindings; + renderpassbindings.reserve(apiObjects.m_renderPassBindings.size()); + std::transform(apiObjects.m_renderPassBindings.begin(), + apiObjects.m_renderPassBindings.end(), + std::back_inserter(renderpassbindings), + [&builder, &serializationMap](const std::vector::value_type& it) { + return RenderPassBindingImpl::Serialize(it->impl(), builder, serializationMap); }); std::vector> dataArrays; @@ -992,7 +872,7 @@ namespace ramses::internal for (const auto& da : apiObjects.m_dataArrays) { dataArrays.push_back(DataArrayImpl::Serialize(da->m_impl, builder, serializationMap)); - serializationMap.storeDataArray(da->getId(), dataArrays.back()); + serializationMap.storeDataArray(da->getSceneObjectId(), dataArrays.back()); } // animation nodes must go after data arrays because they reference them @@ -1004,28 +884,28 @@ namespace ramses::internal std::vector> timerNodes; timerNodes.reserve(apiObjects.m_timerNodes.size()); for (const auto& timerNode : apiObjects.m_timerNodes) - timerNodes.push_back(TimerNodeImpl::Serialize(timerNode->m_timerNodeImpl, builder, serializationMap)); + timerNodes.push_back(TimerNodeImpl::Serialize(timerNode->impl(), builder, serializationMap)); // anchor points must go after node and camera bindings because they reference them std::vector> anchorPoints; anchorPoints.reserve(apiObjects.m_anchorPoints.size()); for (const auto& anchorPoint : apiObjects.m_anchorPoints) - anchorPoints.push_back(AnchorPointImpl::Serialize(anchorPoint->m_anchorPointImpl, builder, serializationMap)); + anchorPoints.push_back(AnchorPointImpl::Serialize(anchorPoint->impl(), builder, serializationMap)); - std::vector> ramsesRenderGroupBindings; - ramsesRenderGroupBindings.reserve(apiObjects.m_ramsesRenderGroupBindings.size()); - for (const auto& rgBinding : apiObjects.m_ramsesRenderGroupBindings) - ramsesRenderGroupBindings.push_back(RamsesRenderGroupBindingImpl::Serialize(rgBinding->m_renderGroupBinding, builder, serializationMap)); + std::vector> renderGroupBindings; + renderGroupBindings.reserve(apiObjects.m_renderGroupBindings.size()); + for (const auto& rgBinding : apiObjects.m_renderGroupBindings) + renderGroupBindings.push_back(RenderGroupBindingImpl::Serialize(rgBinding->impl(), builder, serializationMap)); - std::vector> ramsesMeshNodeBindings; - ramsesMeshNodeBindings.reserve(apiObjects.m_ramsesMeshNodeBindings.size()); - for (const auto& mnBinding : apiObjects.m_ramsesMeshNodeBindings) - ramsesMeshNodeBindings.push_back(RamsesMeshNodeBindingImpl::Serialize(mnBinding->m_meshNodeBinding, builder, serializationMap)); + std::vector> meshNodeBindings; + meshNodeBindings.reserve(apiObjects.m_meshNodeBindings.size()); + for (const auto& mnBinding : apiObjects.m_meshNodeBindings) + meshNodeBindings.push_back(MeshNodeBindingImpl::Serialize(mnBinding->impl(), builder, serializationMap)); std::vector> skinBindings; skinBindings.reserve(apiObjects.m_skinBindings.size()); for (const auto& skinBinding : apiObjects.m_skinBindings) - skinBindings.push_back(SkinBindingImpl::Serialize(skinBinding->m_skinBinding, builder, serializationMap)); + skinBindings.push_back(SkinBindingImpl::Serialize(skinBinding->impl(), builder, serializationMap)); // links must go last due to dependency on serialized properties const auto collectedLinks = apiObjects.collectPropertyLinks(); @@ -1034,25 +914,25 @@ namespace ramses::internal for (const auto& link : collectedLinks) { links.push_back(rlogic_serialization::CreateLink(builder, - serializationMap.resolvePropertyOffset(*link.source->m_impl), - serializationMap.resolvePropertyOffset(*link.target->m_impl), + serializationMap.resolvePropertyOffset(link.source->impl()), + serializationMap.resolvePropertyOffset(link.target->impl()), link.isWeakLink)); } const auto fbModules = builder.CreateVector(luaModules); const auto fbScripts = builder.CreateVector(luascripts); const auto fbInterfaces = builder.CreateVector(luaInterfaces); - const auto fbNodeBindings = builder.CreateVector(ramsesnodebindings); - const auto fbAppearanceBindings = builder.CreateVector(ramsesappearancebindings); - const auto fbCameraBindings = builder.CreateVector(ramsescamerabindings); + const auto fbNodeBindings = builder.CreateVector(nodebindings); + const auto fbAppearanceBindings = builder.CreateVector(appearancebindings); + const auto fbCameraBindings = builder.CreateVector(camerabindings); const auto fbDataArrays = builder.CreateVector(dataArrays); const auto fbAnimations = builder.CreateVector(animationNodes); const auto fbTimers = builder.CreateVector(timerNodes); const auto fbLinks = builder.CreateVector(links); - const auto fbRenderPasses = builder.CreateVector(ramsesrenderpassbindings); + const auto fbRenderPasses = builder.CreateVector(renderpassbindings); const auto fbAnchorPoints = builder.CreateVector(anchorPoints); - const auto fbRenderGroupBindings = builder.CreateVector(ramsesRenderGroupBindings); - const auto fbMeshNodeBindings = builder.CreateVector(ramsesMeshNodeBindings); + const auto fbRenderGroupBindings = builder.CreateVector(renderGroupBindings); + const auto fbMeshNodeBindings = builder.CreateVector(meshNodeBindings); const auto fbSkinBindings = builder.CreateVector(skinBindings); const auto logicEngine = rlogic_serialization::CreateApiObjects( @@ -1067,7 +947,6 @@ namespace ramses::internal fbAnimations, fbTimers, fbLinks, - apiObjects.m_lastObjectId, fbRenderPasses, fbAnchorPoints, fbRenderGroupBindings, @@ -1081,110 +960,109 @@ namespace ramses::internal } std::unique_ptr ApiObjects::Deserialize( + SceneImpl& scene, const rlogic_serialization::ApiObjects& apiObjects, - const IRamsesObjectResolver* ramsesResolver, + const IRamsesObjectResolver& ramsesResolver, const std::string& dataSourceDescription, ErrorReporting& errorReporting, ramses::EFeatureLevel featureLevel) { // Collect data here, only return if no error occurred - auto deserialized = std::make_unique(featureLevel); + auto deserialized = std::make_unique(featureLevel, scene); // Collect deserialized object mappings to resolve dependencies - DeserializationMap deserializationMap; + DeserializationMap deserializationMap{ scene }; if (!apiObjects.luaModules()) { - errorReporting.add("Fatal error during loading from serialized data: missing Lua modules container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing Lua modules container!", nullptr); return nullptr; } if (!apiObjects.luaScripts()) { - errorReporting.add("Fatal error during loading from serialized data: missing Lua scripts container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing Lua scripts container!", nullptr); return nullptr; } if (!apiObjects.luaInterfaces()) { - errorReporting.add("Fatal error during loading from serialized data: missing Lua interfaces container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing Lua interfaces container!", nullptr); return nullptr; } if (!apiObjects.nodeBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing node bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing node bindings container!", nullptr); return nullptr; } if (!apiObjects.appearanceBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing appearance bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing appearance bindings container!", nullptr); return nullptr; } if (!apiObjects.cameraBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing camera bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing camera bindings container!", nullptr); return nullptr; } if (!apiObjects.renderPassBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing renderpass bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing renderpass bindings container!", nullptr); return nullptr; } if (!apiObjects.links()) { - errorReporting.add("Fatal error during loading from serialized data: missing links container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing links container!", nullptr); return nullptr; } if (!apiObjects.dataArrays()) { - errorReporting.add("Fatal error during loading from serialized data: missing data arrays container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing data arrays container!", nullptr); return nullptr; } if (!apiObjects.animationNodes()) { - errorReporting.add("Fatal error during loading from serialized data: missing animation nodes container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing animation nodes container!", nullptr); return nullptr; } if (!apiObjects.timerNodes()) { - errorReporting.add("Fatal error during loading from serialized data: missing timer nodes container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing timer nodes container!", nullptr); return nullptr; } if (!apiObjects.anchorPoints()) { - errorReporting.add("Fatal error during loading from serialized data: missing anchor points container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing anchor points container!", nullptr); return nullptr; } if (!apiObjects.renderGroupBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing rendergroup bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing rendergroup bindings container!", nullptr); return nullptr; } if (!apiObjects.meshNodeBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing meshnode bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing meshnode bindings container!", nullptr); return nullptr; } if (!apiObjects.skinBindings()) { - errorReporting.add("Fatal error during loading from serialized data: missing skin bindings container!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing skin bindings container!", nullptr); return nullptr; } - deserialized->m_lastObjectId = apiObjects.lastObjectId(); - const size_t logicObjectsTotalSize = static_cast(apiObjects.luaModules()->size()) + static_cast(apiObjects.luaScripts()->size()) + @@ -1214,7 +1092,7 @@ namespace ramses::internal return nullptr; auto& obj = deserialized->createAndRegisterObject(std::move(deserializedModule)); - deserializationMap.storeLogicObject(obj.getId(), obj.m_impl); + deserializationMap.storeLogicObject(obj.getSceneObjectId(), obj.m_impl); } const auto& luascripts = *apiObjects.luaScripts(); @@ -1241,74 +1119,56 @@ namespace ramses::internal deserialized->createAndRegisterObject(std::move(deserializedInterface)); } - if (apiObjects.nodeBindings()->size() != 0u || - apiObjects.appearanceBindings()->size() != 0u || - apiObjects.cameraBindings()->size() != 0u || - apiObjects.renderPassBindings()->size() != 0u || - apiObjects.renderGroupBindings()->size() != 0u || - apiObjects.meshNodeBindings()->size() != 0u) - { - if (ramsesResolver == nullptr) - { - errorReporting.add("Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!", nullptr, EErrorType::BinaryVersionMismatch); - return nullptr; - } - } - - const auto& ramsesNodeBindings = *apiObjects.nodeBindings(); - deserialized->m_ramsesNodeBindings.reserve(ramsesNodeBindings.size()); - for (const auto* binding : ramsesNodeBindings) + const auto& nodeBindings = *apiObjects.nodeBindings(); + deserialized->m_nodeBindings.reserve(nodeBindings.size()); + for (const auto* binding : nodeBindings) { assert(binding); - assert(ramsesResolver); - std::unique_ptr deserializedBinding = RamsesNodeBindingImpl::Deserialize(*binding, *ramsesResolver, errorReporting, deserializationMap); + std::unique_ptr deserializedBinding = NodeBindingImpl::Deserialize(*binding, ramsesResolver, errorReporting, deserializationMap); if (!deserializedBinding) return nullptr; - auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); - deserializationMap.storeLogicObject(obj.getId(), obj.m_impl); + auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); + deserializationMap.storeLogicObject(obj.getSceneObjectId(), obj.m_impl); } - const auto& ramsesAppearanceBindings = *apiObjects.appearanceBindings(); - deserialized->m_ramsesAppearanceBindings.reserve(ramsesAppearanceBindings.size()); - for (const auto* binding : ramsesAppearanceBindings) + const auto& appearanceBindings = *apiObjects.appearanceBindings(); + deserialized->m_appearanceBindings.reserve(appearanceBindings.size()); + for (const auto* binding : appearanceBindings) { assert(binding); - assert(ramsesResolver); - std::unique_ptr deserializedBinding = RamsesAppearanceBindingImpl::Deserialize(*binding, *ramsesResolver, errorReporting, deserializationMap); + std::unique_ptr deserializedBinding = AppearanceBindingImpl::Deserialize(*binding, ramsesResolver, errorReporting, deserializationMap); if (!deserializedBinding) return nullptr; - auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); - deserializationMap.storeLogicObject(obj.getId(), obj.m_impl); + auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); + deserializationMap.storeLogicObject(obj.getSceneObjectId(), obj.m_impl); } - const auto& ramsesCameraBindings = *apiObjects.cameraBindings(); - deserialized->m_ramsesCameraBindings.reserve(ramsesCameraBindings.size()); - for (const auto* binding : ramsesCameraBindings) + const auto& cameraBindings = *apiObjects.cameraBindings(); + deserialized->m_cameraBindings.reserve(cameraBindings.size()); + for (const auto* binding : cameraBindings) { assert(binding); - assert(ramsesResolver); - std::unique_ptr deserializedBinding = RamsesCameraBindingImpl::Deserialize(*binding, *ramsesResolver, errorReporting, deserializationMap); + std::unique_ptr deserializedBinding = CameraBindingImpl::Deserialize(*binding, ramsesResolver, errorReporting, deserializationMap); if (!deserializedBinding) return nullptr; - auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); - deserializationMap.storeLogicObject(obj.getId(), obj.m_impl); + auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); + deserializationMap.storeLogicObject(obj.getSceneObjectId(), obj.m_impl); } - const auto& ramsesRenderPassBindings = *apiObjects.renderPassBindings(); - deserialized->m_ramsesRenderPassBindings.reserve(ramsesRenderPassBindings.size()); - for (const auto* binding : ramsesRenderPassBindings) + const auto& renderPassBindings = *apiObjects.renderPassBindings(); + deserialized->m_renderPassBindings.reserve(renderPassBindings.size()); + for (const auto* binding : renderPassBindings) { assert(binding); - assert(ramsesResolver); - std::unique_ptr deserializedBinding = RamsesRenderPassBindingImpl::Deserialize(*binding, *ramsesResolver, errorReporting, deserializationMap); + std::unique_ptr deserializedBinding = RenderPassBindingImpl::Deserialize(*binding, ramsesResolver, errorReporting, deserializationMap); if (!deserializedBinding) return nullptr; - auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); - deserializationMap.storeLogicObject(obj.getId(), obj.m_impl); + auto& obj = deserialized->createAndRegisterObject(std::move(deserializedBinding)); + deserializationMap.storeLogicObject(obj.getSceneObjectId(), obj.m_impl); } const auto& dataArrays = *apiObjects.dataArrays(); @@ -1316,7 +1176,7 @@ namespace ramses::internal for (const auto* fbData : dataArrays) { assert(fbData); - auto deserializedDataArray = DataArrayImpl::Deserialize(*fbData, errorReporting); + auto deserializedDataArray = DataArrayImpl::Deserialize(*fbData, errorReporting, deserializationMap); if (!deserializedDataArray) return nullptr; @@ -1335,7 +1195,7 @@ namespace ramses::internal return nullptr; auto& obj = deserialized->createAndRegisterObject(std::move(deserializedAnimNode)); - deserializationMap.storeLogicObject(obj.getId(), obj.m_impl); + deserializationMap.storeLogicObject(obj.getSceneObjectId(), obj.m_impl); } const auto& timerNodes = *apiObjects.timerNodes(); @@ -1356,7 +1216,6 @@ namespace ramses::internal for (const auto* fbAnchor : anchorPoints) { assert(fbAnchor); - assert(ramsesResolver); std::unique_ptr deserializedAnchor = AnchorPointImpl::Deserialize(*fbAnchor, errorReporting, deserializationMap); if (!deserializedAnchor) return nullptr; @@ -1364,17 +1223,16 @@ namespace ramses::internal deserialized->createAndRegisterObject(std::move(deserializedAnchor)); } - const auto& ramsesRenderGroupBindings = *apiObjects.renderGroupBindings(); - deserialized->m_ramsesRenderGroupBindings.reserve(ramsesRenderGroupBindings.size()); - for (const auto* binding : ramsesRenderGroupBindings) + const auto& renderGroupBindings = *apiObjects.renderGroupBindings(); + deserialized->m_renderGroupBindings.reserve(renderGroupBindings.size()); + for (const auto* binding : renderGroupBindings) { assert(binding); - assert(ramsesResolver); - std::unique_ptr deserializedBinding = RamsesRenderGroupBindingImpl::Deserialize(*binding, *ramsesResolver, errorReporting, deserializationMap); + std::unique_ptr deserializedBinding = RenderGroupBindingImpl::Deserialize(*binding, ramsesResolver, errorReporting, deserializationMap); if (!deserializedBinding) return nullptr; - deserialized->createAndRegisterObject(std::move(deserializedBinding)); + deserialized->createAndRegisterObject(std::move(deserializedBinding)); } // skin bindings must go after node and appearance bindings because they need to resolve references @@ -1390,17 +1248,16 @@ namespace ramses::internal deserialized->createAndRegisterObject(std::move(deserializedBinding)); } - const auto& ramsesMeshNodeBindings = *apiObjects.meshNodeBindings(); - deserialized->m_ramsesMeshNodeBindings.reserve(ramsesMeshNodeBindings.size()); - for (const auto* binding : ramsesMeshNodeBindings) + const auto& meshNodeBindings = *apiObjects.meshNodeBindings(); + deserialized->m_meshNodeBindings.reserve(meshNodeBindings.size()); + for (const auto* binding : meshNodeBindings) { assert(binding); - assert(ramsesResolver); - std::unique_ptr deserializedBinding = RamsesMeshNodeBindingImpl::Deserialize(*binding, *ramsesResolver, errorReporting, deserializationMap); + std::unique_ptr deserializedBinding = MeshNodeBindingImpl::Deserialize(*binding, ramsesResolver, errorReporting, deserializationMap); if (!deserializedBinding) return nullptr; - deserialized->createAndRegisterObject(std::move(deserializedBinding)); + deserialized->createAndRegisterObject(std::move(deserializedBinding)); } // links must go last due to dependency on deserialized properties @@ -1412,13 +1269,13 @@ namespace ramses::internal if (!rLink->sourceProperty()) { - errorReporting.add("Fatal error during loading from serialized data: missing link source property!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing link source property!", nullptr); return nullptr; } if (!rLink->targetProperty()) { - errorReporting.add("Fatal error during loading from serialized data: missing link target property!", nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set("Fatal error during loading from serialized data: missing link target property!", nullptr); return nullptr; } @@ -1432,12 +1289,12 @@ namespace ramses::internal errorReporting); if (!success) { - errorReporting.add( + errorReporting.set( fmt::format("Fatal error during loading from {}! Could not link property '{}' to property '{}'!", dataSourceDescription, sourceProp->name()->string_view(), targetProp->name()->string_view() - ), nullptr, EErrorType::BinaryVersionMismatch); + ), nullptr); return nullptr; } } @@ -1448,26 +1305,32 @@ namespace ramses::internal bool ApiObjects::bindingsDirty() const { return - std::any_of(m_ramsesNodeBindings.cbegin(), m_ramsesNodeBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || - std::any_of(m_ramsesAppearanceBindings.cbegin(), m_ramsesAppearanceBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || - std::any_of(m_ramsesCameraBindings.cbegin(), m_ramsesCameraBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || - std::any_of(m_ramsesRenderPassBindings.cbegin(), m_ramsesRenderPassBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || - std::any_of(m_ramsesRenderGroupBindings.cbegin(), m_ramsesRenderGroupBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || - std::any_of(m_ramsesMeshNodeBindings.cbegin(), m_ramsesMeshNodeBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || + std::any_of(m_nodeBindings.cbegin(), m_nodeBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || + std::any_of(m_appearanceBindings.cbegin(), m_appearanceBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || + std::any_of(m_cameraBindings.cbegin(), m_cameraBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || + std::any_of(m_renderPassBindings.cbegin(), m_renderPassBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || + std::any_of(m_renderGroupBindings.cbegin(), m_renderGroupBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || + std::any_of(m_meshNodeBindings.cbegin(), m_meshNodeBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }) || std::any_of(m_skinBindings.cbegin(), m_skinBindings.cend(), [](const auto& b) { return b->m_impl.isDirty(); }); } - uint64_t ApiObjects::getNextLogicObjectId() + int ApiObjects::getNumElementsInLuaStack() const { - return ++m_lastObjectId; + return m_solState->getNumElementsInLuaStack(); } - int ApiObjects::getNumElementsInLuaStack() const + const std::vector& ApiObjects::getAllPropertyLinks() const { - return m_solState->getNumElementsInLuaStack(); + const std::vector links = collectPropertyLinks(); + m_collectedLinksConst.clear(); + m_collectedLinksConst.reserve(links.size()); + for (const auto& link : links) + m_collectedLinksConst.push_back({link.source, link.target, link.isWeakLink}); + + return m_collectedLinksConst; } - const std::vector& ApiObjects::getAllPropertyLinks() const + const std::vector& ApiObjects::getAllPropertyLinks() { m_collectedLinks = collectPropertyLinks(); return m_collectedLinks; @@ -1477,10 +1340,10 @@ namespace ramses::internal { std::vector links; - std::deque propsStack; - for (const auto& obj : m_logicObjects) + std::deque propsStack; + for (auto& obj : m_logicObjects) { - const auto logicNode = obj->as(); + auto logicNode = obj->as(); if (!logicNode) continue; @@ -1489,12 +1352,12 @@ namespace ramses::internal propsStack.push_back(logicNode->getOutputs()); while (!propsStack.empty()) { - const auto prop = propsStack.back(); + auto prop = propsStack.back(); propsStack.pop_back(); if (prop == nullptr) continue; - const auto incomingLink = prop->getIncomingLink(); + auto incomingLink = prop->getIncomingLink(); if (incomingLink) links.push_back(*incomingLink); @@ -1516,35 +1379,35 @@ namespace ramses::internal template DataArray* ApiObjects::createDataArray(const std::vector&, std::string_view); template DataArray* ApiObjects::createDataArray>(const std::vector>&, std::string_view); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - template ApiObjectContainer& ApiObjects::getApiObjectContainer(); - - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; - template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + template ApiObjectContainer& ApiObjects::getApiObjectContainer(); + + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; + template const ApiObjectContainer& ApiObjects::getApiObjectContainer() const; } diff --git a/client/logic/lib/internals/ApiObjects.h b/src/client/internal/logic/ApiObjects.h similarity index 64% rename from client/logic/lib/internals/ApiObjects.h rename to src/client/internal/logic/ApiObjects.h index 3100043fa..a2b03c5be 100644 --- a/client/logic/lib/internals/ApiObjects.h +++ b/src/client/internal/logic/ApiObjects.h @@ -8,36 +8,24 @@ #pragma once -#include "ramses-logic/AnimationTypes.h" -#include "ramses-logic/PropertyLink.h" -#include "ramses-framework-api/DataTypes.h" -#include "ramses-logic/ELuaSavingMode.h" -#include "ramses-framework-api/EFeatureLevel.h" +#include "ramses/client/logic/AnimationTypes.h" +#include "ramses/client/logic/PropertyLink.h" +#include "ramses/framework/DataTypes.h" +#include "ramses/client/logic/ELuaSavingMode.h" +#include "ramses/framework/EFeatureLevel.h" -#include "impl/LuaConfigImpl.h" +#include "impl/logic/LuaConfigImpl.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/SolState.h" -#include "internals/LogicNodeDependencies.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "internal/logic/SolState.h" +#include "internal/logic/LogicNodeDependencies.h" -#include "ramses-client-api/ERotationType.h" +#include "ramses/framework/ERotationType.h" #include #include #include -namespace ramses -{ - class Scene; - class Node; - class Appearance; - class Camera; - class RenderPass; - class RenderGroup; - class UniformInput; - class MeshNode; -} - namespace rlogic_serialization { struct ApiObjects; @@ -51,18 +39,26 @@ namespace flatbuffers namespace ramses { + class Node; + class Appearance; + class Camera; + class RenderPass; + class RenderGroup; + class UniformInput; + class MeshNode; + class LogicObject; class LogicNode; class LuaScript; class LuaInterface; class LuaModule; - class RamsesNodeBinding; - class RamsesAppearanceBinding; - class RamsesCameraBinding; - class RamsesRenderPassBinding; - class RamsesRenderGroupBinding; - class RamsesRenderGroupBindingElements; - class RamsesMeshNodeBinding; + class NodeBinding; + class AppearanceBinding; + class CameraBinding; + class RenderPassBinding; + class RenderGroupBinding; + class RenderGroupBindingElements; + class MeshNodeBinding; class SkinBinding; class DataArray; class AnimationNode; @@ -75,11 +71,12 @@ namespace ramses::internal class SolState; class IRamsesObjectResolver; class AnimationNodeConfigImpl; - class ValidationResults; class SerializationMap; - class RamsesNodeBindingImpl; - class RamsesCameraBindingImpl; - class RamsesAppearanceBindingImpl; + class NodeBindingImpl; + class CameraBindingImpl; + class AppearanceBindingImpl; + class SceneImpl; + class ValidationReportImpl; template using ApiObjectContainer = std::vector; @@ -89,7 +86,7 @@ namespace ramses::internal { public: // Not move-able and non-copyable - explicit ApiObjects(ramses::EFeatureLevel featureLevel); + explicit ApiObjects(ramses::EFeatureLevel featureLevel, SceneImpl& scene); ~ApiObjects() noexcept; // Not move-able because of the dependency between sol objects and their parent sol state // Moving those would require a custom move assignment operator which keeps both sol states alive @@ -106,8 +103,9 @@ namespace ramses::internal flatbuffers::FlatBufferBuilder& builder, ELuaSavingMode luaSavingMode); static std::unique_ptr Deserialize( + SceneImpl& scene, const rlogic_serialization::ApiObjects& apiObjects, - const IRamsesObjectResolver* ramsesResolver, + const IRamsesObjectResolver& ramsesResolver, const std::string& dataSourceDescription, ErrorReporting& errorReporting, ramses::EFeatureLevel featureLevel); @@ -122,36 +120,34 @@ namespace ramses::internal std::string_view source, const LuaConfigImpl& config, std::string_view interfaceName, - ErrorReporting& errorReporting, - bool verifyModules); + ErrorReporting& errorReporting); LuaModule* createLuaModule( std::string_view source, const LuaConfigImpl& config, std::string_view moduleName, ErrorReporting& errorReporting); - RamsesNodeBinding* createRamsesNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name); - RamsesAppearanceBinding* createRamsesAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name); - RamsesCameraBinding* createRamsesCameraBinding(ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name); - RamsesRenderPassBinding* createRamsesRenderPassBinding(ramses::RenderPass& renderPass, std::string_view name); - RamsesRenderGroupBinding* createRamsesRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RamsesRenderGroupBindingElements& elements, std::string_view name); - RamsesMeshNodeBinding* createRamsesMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name); + NodeBinding* createNodeBinding(ramses::Node& ramsesNode, ramses::ERotationType rotationType, std::string_view name); + AppearanceBinding* createAppearanceBinding(ramses::Appearance& ramsesAppearance, std::string_view name); + CameraBinding* createCameraBinding(ramses::Camera& ramsesCamera, bool withFrustumPlanes, std::string_view name); + RenderPassBinding* createRenderPassBinding(ramses::RenderPass& renderPass, std::string_view name); + RenderGroupBinding* createRenderGroupBinding(ramses::RenderGroup& ramsesRenderGroup, const RenderGroupBindingElements& elements, std::string_view name); + MeshNodeBinding* createMeshNodeBinding(ramses::MeshNode& ramsesMeshNode, std::string_view name); SkinBinding* createSkinBinding( - std::vector joints, + std::vector joints, const std::vector& inverseBindMatrices, - RamsesAppearanceBindingImpl& appearanceBinding, + AppearanceBindingImpl& appearanceBinding, const ramses::UniformInput& jointMatInput, std::string_view name); template DataArray* createDataArray(const std::vector& data, std::string_view name); AnimationNode* createAnimationNode(const AnimationNodeConfigImpl& config, std::string_view name); TimerNode* createTimerNode(std::string_view name); - AnchorPoint* createAnchorPoint(RamsesNodeBindingImpl& nodeBinding, RamsesCameraBindingImpl& cameraBinding, std::string_view name); + AnchorPoint* createAnchorPoint(NodeBindingImpl& nodeBinding, CameraBindingImpl& cameraBinding, std::string_view name); bool destroy(LogicObject& object, ErrorReporting& errorReporting); // Invariance checks - [[nodiscard]] bool checkBindingsReferToSameRamsesScene(ErrorReporting& errorReporting) const; - void validateInterfaces(ValidationResults& validationResults) const; - void validateDanglingNodes(ValidationResults& validationResults) const; + void validateInterfaces(ValidationReportImpl& report) const; + void validateDanglingNodes(ValidationReportImpl& report) const; // Getters template @@ -162,15 +158,13 @@ namespace ramses::internal [[nodiscard]] const LogicNodeDependencies& getLogicNodeDependencies() const; [[nodiscard]] LogicNodeDependencies& getLogicNodeDependencies(); - [[nodiscard]] LogicObject* getApiObjectById(uint64_t id) const; - // Internally used [[nodiscard]] bool bindingsDirty() const; - [[nodiscard]] uint64_t getNextLogicObjectId(); [[nodiscard]] int getNumElementsInLuaStack() const; - [[nodiscard]] const std::vector& getAllPropertyLinks() const; + [[nodiscard]] const std::vector& getAllPropertyLinks() const; + [[nodiscard]] const std::vector& getAllPropertyLinks(); private: template @@ -179,10 +173,10 @@ namespace ramses::internal [[nodiscard]] bool destroyAndUnregisterObject(T& objToDelete, ErrorReporting& errorReporting); // Type-specific destruction logic - [[nodiscard]] bool destroyInternal(RamsesNodeBinding& ramsesNodeBinding, ErrorReporting& errorReporting); + [[nodiscard]] bool destroyInternal(NodeBinding& nodeBinding, ErrorReporting& errorReporting); [[nodiscard]] bool destroyInternal(LuaModule& luaModule, ErrorReporting& errorReporting); - [[nodiscard]] bool destroyInternal(RamsesAppearanceBinding& ramsesAppearanceBinding, ErrorReporting& errorReporting); - [[nodiscard]] bool destroyInternal(RamsesCameraBinding& ramsesCameraBinding, ErrorReporting& errorReporting); + [[nodiscard]] bool destroyInternal(AppearanceBinding& appearanceBinding, ErrorReporting& errorReporting); + [[nodiscard]] bool destroyInternal(CameraBinding& cameraBinding, ErrorReporting& errorReporting); [[nodiscard]] bool destroyInternal(DataArray& dataArray, ErrorReporting& errorReporting); [[nodiscard]] bool destroyInternal(AnchorPoint& node, ErrorReporting& errorReporting); @@ -194,12 +188,12 @@ namespace ramses::internal ApiObjectContainer m_scripts; ApiObjectContainer m_interfaces; ApiObjectContainer m_luaModules; - ApiObjectContainer m_ramsesNodeBindings; - ApiObjectContainer m_ramsesAppearanceBindings; - ApiObjectContainer m_ramsesCameraBindings; - ApiObjectContainer m_ramsesRenderPassBindings; - ApiObjectContainer m_ramsesRenderGroupBindings; - ApiObjectContainer m_ramsesMeshNodeBindings; + ApiObjectContainer m_nodeBindings; + ApiObjectContainer m_appearanceBindings; + ApiObjectContainer m_cameraBindings; + ApiObjectContainer m_renderPassBindings; + ApiObjectContainer m_renderGroupBindings; + ApiObjectContainer m_meshNodeBindings; ApiObjectContainer m_skinBindings; ApiObjectContainer m_dataArrays; ApiObjectContainer m_animationNodes; @@ -209,12 +203,12 @@ namespace ramses::internal ApiObjectOwningContainer m_objectsOwningContainer; LogicNodeDependencies m_logicNodeDependencies; - uint64_t m_lastObjectId = 0; - std::unordered_map m_logicObjectIdMapping; // persistent storage for links to be given out via public API getPropertyLinks() mutable std::vector m_collectedLinks; + mutable std::vector m_collectedLinksConst; ramses::EFeatureLevel m_featureLevel; + SceneImpl& m_scene; }; } diff --git a/client/logic/lib/internals/ApiObjectsSerializedSize.cpp b/src/client/internal/logic/ApiObjectsSerializedSize.cpp similarity index 63% rename from client/logic/lib/internals/ApiObjectsSerializedSize.cpp rename to src/client/internal/logic/ApiObjectsSerializedSize.cpp index ff61c9b82..7169b9a29 100644 --- a/client/logic/lib/internals/ApiObjectsSerializedSize.cpp +++ b/src/client/internal/logic/ApiObjectsSerializedSize.cpp @@ -6,40 +6,40 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/ApiObjects.h" -#include "internals/ApiObjectsSerializedSize.h" - -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/TimerNode.h" - -#include "generated/ApiObjectsGen.h" - -#include "impl/AnchorPointImpl.h" -#include "impl/AnimationNodeImpl.h" -#include "impl/DataArrayImpl.h" -#include "impl/LuaInterfaceImpl.h" -#include "impl/LuaModuleImpl.h" -#include "impl/LuaScriptImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesRenderPassBindingImpl.h" -#include "impl/RamsesRenderGroupBindingImpl.h" -#include "impl/RamsesMeshNodeBindingImpl.h" -#include "impl/SkinBindingImpl.h" -#include "impl/TimerNodeImpl.h" +#include "internal/logic/ApiObjects.h" +#include "internal/logic/ApiObjectsSerializedSize.h" + +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/TimerNode.h" + +#include "internal/logic/flatbuffers/generated/ApiObjectsGen.h" + +#include "impl/logic/AnchorPointImpl.h" +#include "impl/logic/AnimationNodeImpl.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/logic/LuaInterfaceImpl.h" +#include "impl/logic/LuaModuleImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/TimerNodeImpl.h" namespace ramses::internal { @@ -51,7 +51,7 @@ namespace ramses::internal SerializationMap serializationMap{}; for (const auto& element : container) { - (void)I::Serialize(static_cast(element->m_impl), builder, serializationMap); + (void)I::Serialize(static_cast(element->impl()), builder, serializationMap); } return static_cast(builder.GetSize()); } @@ -60,18 +60,18 @@ namespace ramses::internal template<> size_t calculateSerializedSize(const ApiObjectContainer& container, ELuaSavingMode /*unused*/) { - auto insertIds = [](const DataArray* data, std::unordered_set& ids) + auto insertIds = [](const DataArray* data, std::unordered_set& ids) { if (data) { - ids.insert(data->getId()); + ids.insert(data->getSceneObjectId()); } }; flatbuffers::FlatBufferBuilder builder{}; for (const auto& element : container) { - std::unordered_set ids{}; + std::unordered_set ids{}; for (const auto& channel : element->getChannels()) { insertIds(channel.timeStamps, ids); @@ -84,7 +84,7 @@ namespace ramses::internal { serializationMap.storeDataArray(id, 0u); } - (void)AnimationNodeImpl::Serialize(element->m_animationNodeImpl, builder, serializationMap); + (void)AnimationNodeImpl::Serialize(element->impl(), builder, serializationMap); } return static_cast(builder.GetSize()); } @@ -96,7 +96,7 @@ namespace ramses::internal SerializationMap serializationMap{}; for (const auto& element : container) { - (void)LuaScriptImpl::Serialize(element->m_script, builder, serializationMap, luaSavingMode); + (void)LuaScriptImpl::Serialize(element->impl(), builder, serializationMap, luaSavingMode); } return static_cast(builder.GetSize()); } @@ -108,7 +108,7 @@ namespace ramses::internal SerializationMap serializationMap{}; for (const auto& element : container) { - (void)LuaModuleImpl::Serialize(element->m_impl, builder, serializationMap, luaSavingMode); + (void)LuaModuleImpl::Serialize(element->impl(), builder, serializationMap, luaSavingMode); } return static_cast(builder.GetSize()); } @@ -121,33 +121,33 @@ namespace ramses::internal } template<> - size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) + size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) { - return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); + return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); } template<> - size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) + size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) { - return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); + return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); } template<> - size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) + size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) { - return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); + return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); } template<> - size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) + size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) { - return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); + return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); } template<> - size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) + size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) { - return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); + return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); } template<> @@ -157,9 +157,9 @@ namespace ramses::internal } template<> - size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) + size_t ApiObjectsSerializedSize::GetSerializedSize(const ApiObjects& apiObjects, ELuaSavingMode luaSavingMode) { - return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); + return calculateSerializedSize(apiObjects.getApiObjectContainer(), luaSavingMode); } template<> diff --git a/client/logic/lib/internals/ApiObjectsSerializedSize.h b/src/client/internal/logic/ApiObjectsSerializedSize.h similarity index 95% rename from client/logic/lib/internals/ApiObjectsSerializedSize.h rename to src/client/internal/logic/ApiObjectsSerializedSize.h index 85a7758c3..6613e9e7c 100644 --- a/client/logic/lib/internals/ApiObjectsSerializedSize.h +++ b/src/client/internal/logic/ApiObjectsSerializedSize.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/ELuaSavingMode.h" +#include "ramses/client/logic/ELuaSavingMode.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/DeserializationMap.h b/src/client/internal/logic/DeserializationMap.h similarity index 86% rename from client/logic/lib/internals/DeserializationMap.h rename to src/client/internal/logic/DeserializationMap.h index 96b7da850..242c947d3 100644 --- a/client/logic/lib/internals/DeserializationMap.h +++ b/src/client/internal/logic/DeserializationMap.h @@ -23,6 +23,7 @@ namespace ramses namespace ramses::internal { + class SceneImpl; class PropertyImpl; class LogicObjectImpl; @@ -30,6 +31,16 @@ namespace ramses::internal class DeserializationMap { public: + explicit DeserializationMap(SceneImpl& scene) + : m_scene{ scene } + { + } + + SceneImpl& getScene() const + { + return m_scene; + } + void storePropertyImpl(const rlogic_serialization::Property& flatbufferObject, PropertyImpl& impl) { Store(&flatbufferObject, &impl, m_properties); @@ -50,13 +61,14 @@ namespace ramses::internal return *Get(&flatbufferObject, m_dataArrays); } - void storeLogicObject(uint64_t id, LogicObjectImpl& obj) + void storeLogicObject(sceneObjectId_t id, LogicObjectImpl& obj) { + assert(id.isValid()); Store(id, &obj, m_logicObjects); } template - ImplT* resolveLogicObject(uint64_t id) const + ImplT* resolveLogicObject(sceneObjectId_t id) const { // fail queries using IDs gracefully if given ID not found // file can be OK on flatbuffer schema level but might still contain corrupted ID value @@ -86,7 +98,8 @@ namespace ramses::internal std::unordered_map m_properties; std::unordered_map m_dataArrays; - std::unordered_map m_logicObjects; + std::unordered_map m_logicObjects; + SceneImpl& m_scene; }; } diff --git a/client/logic/lib/internals/DirectedAcyclicGraph.cpp b/src/client/internal/logic/DirectedAcyclicGraph.cpp similarity index 99% rename from client/logic/lib/internals/DirectedAcyclicGraph.cpp rename to src/client/internal/logic/DirectedAcyclicGraph.cpp index b977d2254..f4625fa40 100644 --- a/client/logic/lib/internals/DirectedAcyclicGraph.cpp +++ b/src/client/internal/logic/DirectedAcyclicGraph.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/DirectedAcyclicGraph.h" +#include "internal/logic/DirectedAcyclicGraph.h" #include #include diff --git a/client/logic/lib/internals/DirectedAcyclicGraph.h b/src/client/internal/logic/DirectedAcyclicGraph.h similarity index 100% rename from client/logic/lib/internals/DirectedAcyclicGraph.h rename to src/client/internal/logic/DirectedAcyclicGraph.h diff --git a/client/logic/lib/internals/EPropertySemantics.h b/src/client/internal/logic/EPropertySemantics.h similarity index 100% rename from client/logic/lib/internals/EPropertySemantics.h rename to src/client/internal/logic/EPropertySemantics.h diff --git a/client/logic/lib/internals/EnvironmentProtection.cpp b/src/client/internal/logic/EnvironmentProtection.cpp similarity index 99% rename from client/logic/lib/internals/EnvironmentProtection.cpp rename to src/client/internal/logic/EnvironmentProtection.cpp index dd2b55c7d..63767d29e 100644 --- a/client/logic/lib/internals/EnvironmentProtection.cpp +++ b/src/client/internal/logic/EnvironmentProtection.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/EnvironmentProtection.h" +#include "internal/logic/EnvironmentProtection.h" -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/EPropertyType.h" -#include "internals/SolHelper.h" +#include "internal/logic/SolHelper.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/EnvironmentProtection.h b/src/client/internal/logic/EnvironmentProtection.h similarity index 98% rename from client/logic/lib/internals/EnvironmentProtection.h rename to src/client/internal/logic/EnvironmentProtection.h index 6f8071528..1a0f10e79 100644 --- a/client/logic/lib/internals/EnvironmentProtection.h +++ b/src/client/internal/logic/EnvironmentProtection.h @@ -8,7 +8,7 @@ #pragma once -#include "internals/SolWrapper.h" +#include "internal/logic/SolWrapper.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/FileUtils.cpp b/src/client/internal/logic/FileUtils.cpp similarity index 100% rename from client/logic/lib/internals/FileUtils.cpp rename to src/client/internal/logic/FileUtils.cpp diff --git a/client/logic/lib/internals/FileUtils.h b/src/client/internal/logic/FileUtils.h similarity index 100% rename from client/logic/lib/internals/FileUtils.h rename to src/client/internal/logic/FileUtils.h diff --git a/client/logic/lib/internals/InterfaceTypeFunctions.cpp b/src/client/internal/logic/InterfaceTypeFunctions.cpp similarity index 96% rename from client/logic/lib/internals/InterfaceTypeFunctions.cpp rename to src/client/internal/logic/InterfaceTypeFunctions.cpp index b62520e0c..00d030c10 100644 --- a/client/logic/lib/internals/InterfaceTypeFunctions.cpp +++ b/src/client/internal/logic/InterfaceTypeFunctions.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/InterfaceTypeFunctions.h" -#include "internals/SolHelper.h" +#include "internal/logic/InterfaceTypeFunctions.h" +#include "internal/logic/SolHelper.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/InterfaceTypeFunctions.h b/src/client/internal/logic/InterfaceTypeFunctions.h similarity index 94% rename from client/logic/lib/internals/InterfaceTypeFunctions.h rename to src/client/internal/logic/InterfaceTypeFunctions.h index 34640343d..31192e6ca 100644 --- a/client/logic/lib/internals/InterfaceTypeFunctions.h +++ b/src/client/internal/logic/InterfaceTypeFunctions.h @@ -8,8 +8,8 @@ #pragma once -#include "internals/InterfaceTypeInfo.h" -#include "internals/LuaTypeConversions.h" +#include "internal/logic/InterfaceTypeInfo.h" +#include "internal/logic/LuaTypeConversions.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/InterfaceTypeInfo.h b/src/client/internal/logic/InterfaceTypeInfo.h similarity index 89% rename from client/logic/lib/internals/InterfaceTypeInfo.h rename to src/client/internal/logic/InterfaceTypeInfo.h index 8751e617f..6c1b3d68a 100644 --- a/client/logic/lib/internals/InterfaceTypeInfo.h +++ b/src/client/internal/logic/InterfaceTypeInfo.h @@ -8,8 +8,8 @@ #pragma once -#include "internals/SolWrapper.h" -#include "ramses-logic/EPropertyType.h" +#include "internal/logic/SolWrapper.h" +#include "ramses/client/logic/EPropertyType.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/LogicNodeDependencies.cpp b/src/client/internal/logic/LogicNodeDependencies.cpp similarity index 80% rename from client/logic/lib/internals/LogicNodeDependencies.cpp rename to src/client/internal/logic/LogicNodeDependencies.cpp index 55efaa1e9..82bbe4362 100644 --- a/client/logic/lib/internals/LogicNodeDependencies.cpp +++ b/src/client/internal/logic/LogicNodeDependencies.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/LogicNodeDependencies.h" +#include "internal/logic/LogicNodeDependencies.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "impl/LogicNodeImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/RamsesBindingImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/RamsesBindingImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/TypeUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/TypeUtils.h" #include #include "fmt/format.h" @@ -48,7 +48,7 @@ namespace ramses::internal bool LogicNodeDependencies::isLinked(const LogicNodeImpl& logicNode) const { auto inputs = logicNode.getInputs(); - if (isLinked(*inputs->m_impl)) + if (isLinked(inputs->impl())) { return true; } @@ -56,12 +56,12 @@ namespace ramses::internal const auto outputs = logicNode.getOutputs(); if (nullptr != outputs) { - return isLinked(*outputs->m_impl); + return isLinked(outputs->impl()); } return false; } - bool LogicNodeDependencies::isLinked(PropertyImpl& input) const + bool LogicNodeDependencies::isLinked(const PropertyImpl& input) const { const auto inputCount = input.getChildCount(); // check if an input of this node is a target of another node @@ -70,7 +70,7 @@ namespace ramses::internal const auto child = input.getChild(i); if (TypeUtils::CanHaveChildren(child->getType())) { - if (isLinked(*child->m_impl)) + if (isLinked(child->impl())) { return true; } @@ -78,7 +78,7 @@ namespace ramses::internal else { assert(TypeUtils::IsPrimitiveType(child->getType())); - if (child->m_impl->isLinked()) + if (child->impl().isLinked()) { return true; } @@ -102,19 +102,19 @@ namespace ramses::internal { if (!m_logicNodeDAG.containsNode(output.getLogicNode())) { - errorReporting.add(fmt::format("LogicNode '{}' is not an instance of this LogicEngine", output.getLogicNode().getName()), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("LogicNode '{}' is not an instance of this LogicEngine", output.getLogicNode().getName()), nullptr); return false; } if (!m_logicNodeDAG.containsNode(input.getLogicNode())) { - errorReporting.add(fmt::format("LogicNode '{}' is not an instance of this LogicEngine", input.getLogicNode().getName()), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("LogicNode '{}' is not an instance of this LogicEngine", input.getLogicNode().getName()), nullptr); return false; } if (&output.getLogicNode() == &input.getLogicNode()) { - errorReporting.add(fmt::format("Link source and target can't belong to the same node! ('{}')", input.getLogicNode().getName()), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Link source and target can't belong to the same node! ('{}')", input.getLogicNode().getName()), nullptr); return false; } @@ -122,36 +122,36 @@ namespace ramses::internal { std::string_view lhsType = output.isOutput() ? "output" : "input"; std::string_view rhsType = input.isOutput() ? "output" : "input"; - errorReporting.add(fmt::format("Failed to link {} property '{}' to {} property '{}'. Only outputs can be linked to inputs", lhsType, output.getName(), rhsType, input.getName()), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Failed to link {} property '{}' to {} property '{}'. Only outputs can be linked to inputs", lhsType, output.getName(), rhsType, input.getName()), nullptr); return false; } if (output.getType() != input.getType()) { - errorReporting.add(fmt::format("Types of source property '{}:{}' does not match target property '{}:{}'", + errorReporting.set(fmt::format("Types of source property '{}:{}' does not match target property '{}:{}'", output.getName(), GetLuaPrimitiveTypeName(output.getType()), input.getName(), - GetLuaPrimitiveTypeName(input.getType())), nullptr, EErrorType::IllegalArgument); + GetLuaPrimitiveTypeName(input.getType())), nullptr); return false; } // No need to also test input type, above check already makes sure output and input are of the same type if (!TypeUtils::IsPrimitiveType(output.getType())) { - errorReporting.add(fmt::format("Can't link properties of complex types directly, currently only primitive properties can be linked"), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Can't link properties of complex types directly, currently only primitive properties can be linked"), nullptr); return false; } const PropertyImpl* linkedIncomingProperty = input.getIncomingLink().property; if (linkedIncomingProperty != nullptr) { - errorReporting.add(fmt::format("The property '{}' of LogicNode '{}' is already linked (to property '{}' of LogicNode '{}')", + errorReporting.set(fmt::format("The property '{}' of LogicNode '{}' is already linked (to property '{}' of LogicNode '{}')", input.getName(), input.getLogicNode().getName(), linkedIncomingProperty->getName(), linkedIncomingProperty->getLogicNode().getName() - ), nullptr, EErrorType::IllegalArgument); + ), nullptr); return false; } @@ -177,20 +177,20 @@ namespace ramses::internal { if (TypeUtils::CanHaveChildren(input.getType())) { - errorReporting.add(fmt::format("Can't unlink properties of complex types directly!"), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Can't unlink properties of complex types directly!"), nullptr); return false; } const PropertyImpl* linkedIncomingProperty = input.getIncomingLink().property; if (linkedIncomingProperty == nullptr) { - errorReporting.add(fmt::format("Input property '{}' is not currently linked!", input.getName()), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Input property '{}' is not currently linked!", input.getName()), nullptr); return false; } if (linkedIncomingProperty != &output) { - errorReporting.add(fmt::format("Input property '{}' is currently linked to another property '{}'", linkedIncomingProperty->getName(), input.getName()), nullptr, EErrorType::IllegalArgument); + errorReporting.set(fmt::format("Input property '{}' is currently linked to another property '{}'", linkedIncomingProperty->getName(), input.getName()), nullptr); return false; } diff --git a/client/logic/lib/internals/LogicNodeDependencies.h b/src/client/internal/logic/LogicNodeDependencies.h similarity index 94% rename from client/logic/lib/internals/LogicNodeDependencies.h rename to src/client/internal/logic/LogicNodeDependencies.h index d90b52840..2ca85facd 100644 --- a/client/logic/lib/internals/LogicNodeDependencies.h +++ b/src/client/internal/logic/LogicNodeDependencies.h @@ -8,7 +8,7 @@ #pragma once -#include "internals/DirectedAcyclicGraph.h" +#include "internal/logic/DirectedAcyclicGraph.h" #include @@ -45,7 +45,7 @@ namespace ramses::internal private: DirectedAcyclicGraph m_logicNodeDAG; - [[nodiscard]] bool isLinked(PropertyImpl& input) const; + [[nodiscard]] bool isLinked(const PropertyImpl& input) const; // Initial state: no nodes and no need to re-compute node topology std::optional m_cachedTopologicallySortedNodes = NodeVector{}; diff --git a/client/logic/lib/internals/LogicNodeUpdateStatistics.cpp b/src/client/internal/logic/LogicNodeUpdateStatistics.cpp similarity index 58% rename from client/logic/lib/internals/LogicNodeUpdateStatistics.cpp rename to src/client/internal/logic/LogicNodeUpdateStatistics.cpp index c360ebde8..49e1dc1e4 100644 --- a/client/logic/lib/internals/LogicNodeUpdateStatistics.cpp +++ b/src/client/internal/logic/LogicNodeUpdateStatistics.cpp @@ -6,13 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicNode.h" -#include "internals/UpdateReport.h" -#include "internals/LogicNodeUpdateStatistics.h" -#include "impl/LoggerImpl.h" +#include "internal/logic/UpdateReport.h" +#include "internal/logic/LogicNodeUpdateStatistics.h" +#include "impl/RamsesObjectImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include +#include namespace ramses::internal { + LogicNodeUpdateStatistics::LogicNodeUpdateStatistics() + { + m_slowestNodes.fill({ nullptr, std::chrono::microseconds(-1) }); + } + void LogicNodeUpdateStatistics::clear() { m_updateExecutionTime.clear(); @@ -22,11 +29,7 @@ namespace ramses::internal m_currentStatisticsFrame = 0u; m_totalNodesCount = 0u; m_lastTimeUpdateDataAdded = std::nullopt; - } - - void LogicNodeUpdateStatistics::setLogLevel(ELogLevel logLevel) - { - m_logLevel = logLevel; + m_slowestNodes.fill({nullptr, std::chrono::microseconds(-1)}); } void LogicNodeUpdateStatistics::collect(const UpdateReport& report, size_t totalNodesCount) @@ -39,6 +42,25 @@ namespace ramses::internal m_nodesExecutedCurrentUpdate = 0u; m_activatedLinks.add(static_cast(report.getLinkActivations())); + auto isLongerTime = [](const LogicNodeTimed& a, const LogicNodeTimed& b) { return a.second > b.second; }; + for (auto& newNode : report.getNodesExecuted()) + { + if (isLongerTime(newNode, m_slowestNodes.back())) + { + auto it = std::find_if(m_slowestNodes.begin(), m_slowestNodes.end(), [&newNode](const auto& slowNode) { return newNode.first == slowNode.first; }); + if (it == m_slowestNodes.end()) + { + m_slowestNodes.back() = newNode; + } + else + { + it->second = std::max(newNode.second, it->second); + } + + std::sort(m_slowestNodes.begin(), m_slowestNodes.end(), isLongerTime); + } + } + m_currentStatisticsFrame++; } @@ -73,19 +95,19 @@ namespace ramses::internal const auto now = Clock::now(); if (!m_lastTimeLogged.has_value()) { - log("First Statistics Log"); + LOG_INFO(CONTEXT_PERIODIC, "First Statistics Log"); } else { const auto timeSinceLastLog = std::chrono::duration_cast(now - m_lastTimeLogged.value()); - log("Time since last log: {:.2f} sec", static_cast(timeSinceLastLog.count()) / 1000.f); + LOG_INFO_P(CONTEXT_PERIODIC, "Time since last log: {:.2f} sec", static_cast(timeSinceLastLog.count()) / 1000.f); } m_lastTimeLogged = now; } void LogicNodeUpdateStatistics::logUpdateExecutionTime() { - log("Update Execution time (min/max/avg): {}/{}/{} [u]sec", + LOG_INFO_P(CONTEXT_PERIODIC, "Update Execution time (min/max/avg): {}/{}/{} [u]sec", m_updateExecutionTime.min, m_updateExecutionTime.max, m_updateExecutionTime.acc / m_currentStatisticsFrame); @@ -95,11 +117,11 @@ namespace ramses::internal { if (m_currentStatisticsFrame == 1) { - log("Time between Update calls cannot be measured with loggingRate = 1"); + LOG_INFO_P(CONTEXT_PERIODIC, "Time between Update calls cannot be measured with loggingRate = 1"); } else { - log("Time between Update calls (min/max/avg): {:.2f}/{:.2f}/{:.2f} [m]sec", + LOG_INFO_P(CONTEXT_PERIODIC, "Time between Update calls (min/max/avg): {:.2f}/{:.2f}/{:.2f} [m]sec", static_cast(m_timeSinceLastUpdate.min) / 1000.f, static_cast(m_timeSinceLastUpdate.max) / 1000.f, (static_cast(m_timeSinceLastUpdate.acc) / static_cast(m_currentStatisticsFrame - 1)) / 1000.f); @@ -109,21 +131,38 @@ namespace ramses::internal void LogicNodeUpdateStatistics::logNodesExecuted() { const size_t totalNodesNotZero = std::max(size_t(1), m_totalNodesCount); - log("Nodes Executed (min/max/avg): {}%/{}%/{}% of {} nodes total", - (m_nodesExecuted.min / totalNodesNotZero) * 100, - (m_nodesExecuted.max / totalNodesNotZero) * 100, - ((m_nodesExecuted.acc / m_currentStatisticsFrame) / totalNodesNotZero) * 100, + LOG_INFO_P(CONTEXT_PERIODIC, "Nodes Executed (min/max/avg): {}%/{}%/{}% ({}/{}/{}) of {} nodes total", + static_cast(static_cast(m_nodesExecuted.min) / totalNodesNotZero * 100.f), + static_cast(static_cast(m_nodesExecuted.max) / totalNodesNotZero * 100.f), + static_cast(static_cast(m_nodesExecuted.acc) / m_currentStatisticsFrame / totalNodesNotZero * 100.f), + m_nodesExecuted.min, + m_nodesExecuted.max, + m_nodesExecuted.acc / m_currentStatisticsFrame, m_totalNodesCount); } void LogicNodeUpdateStatistics::logActivatedLinks() { - log("Activated links (min/max/avg): {}/{}/{}", + LOG_INFO_P(CONTEXT_PERIODIC, "Activated links (min/max/avg): {}/{}/{}", m_activatedLinks.min, m_activatedLinks.max, m_activatedLinks.acc / m_currentStatisticsFrame); } + void LogicNodeUpdateStatistics::logSlowestNodes() + { + if (m_slowestNodes[0].first == nullptr) + return; + + std::string nodes; + for (auto& node : m_slowestNodes) + { + if (node.first != nullptr) + nodes += fmt::format(" [{}:{}]", node.first->getName(), node.second.count()); + } + LOG_INFO_P(CONTEXT_PERIODIC, "Slowest nodes [name:time_us]:{}", nodes); + } + void LogicNodeUpdateStatistics::calculateAndLog() { assert(m_currentStatisticsFrame != 0u); @@ -138,6 +177,8 @@ namespace ramses::internal logActivatedLinks(); + logSlowestNodes(); + clear(); } } diff --git a/client/logic/lib/internals/LogicNodeUpdateStatistics.h b/src/client/internal/logic/LogicNodeUpdateStatistics.h similarity index 87% rename from client/logic/lib/internals/LogicNodeUpdateStatistics.h rename to src/client/internal/logic/LogicNodeUpdateStatistics.h index 69a644308..4e3ec3dd3 100644 --- a/client/logic/lib/internals/LogicNodeUpdateStatistics.h +++ b/src/client/internal/logic/LogicNodeUpdateStatistics.h @@ -7,8 +7,8 @@ // ------------------------------------------------------------------------- #pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "impl/LoggerImpl.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/Core/Utils/LogMacros.h" namespace ramses::internal { @@ -20,15 +20,16 @@ namespace ramses::internal class LogicNodeUpdateStatistics { public: - + LogicNodeUpdateStatistics(); void nodeExecuted(); void collect(const UpdateReport& report, size_t totalNodesCount); void calculateAndLog(); - void setLogLevel(ELogLevel logLevel); void setLoggingRate(size_t loggingRate); [[nodiscard]] bool checkUpdateFrameFinished() const; private: + using LogicNodeTimed = UpdateReport::LogicNodeTimed; + struct StatisticProperty { int64_t min = std::numeric_limits::max(); @@ -50,10 +51,6 @@ namespace ramses::internal } }; - template void log(const ARGS&... args) - { - LoggerImpl::GetInstance().log(m_logLevel, args...); - } void clear(); void collectTimeSinceLastUpdate(); @@ -62,6 +59,7 @@ namespace ramses::internal void logTimeBetweenUpdates(); void logNodesExecuted(); void logActivatedLinks(); + void logSlowestNodes(); size_t m_loggingRate = 60u; size_t m_currentStatisticsFrame = 0u; @@ -69,11 +67,12 @@ namespace ramses::internal size_t m_totalNodesCount = 0u; std::optional m_lastTimeLogged = std::nullopt; std::optional m_lastTimeUpdateDataAdded = std::nullopt; - ELogLevel m_logLevel = ELogLevel::Debug; StatisticProperty m_timeSinceLastUpdate; StatisticProperty m_updateExecutionTime; StatisticProperty m_nodesExecuted; StatisticProperty m_activatedLinks; + + std::array m_slowestNodes; }; } diff --git a/client/logic/lib/internals/LuaCompilationUtils.cpp b/src/client/internal/logic/LuaCompilationUtils.cpp similarity index 84% rename from client/logic/lib/internals/LuaCompilationUtils.cpp rename to src/client/internal/logic/LuaCompilationUtils.cpp index b37bf2ef0..adfee36a0 100644 --- a/client/logic/lib/internals/LuaCompilationUtils.cpp +++ b/src/client/internal/logic/LuaCompilationUtils.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/LuaCompilationUtils.h" - -#include "ramses-logic/LuaModule.h" -#include "impl/PropertyImpl.h" -#include "impl/LuaModuleImpl.h" -#include "impl/LoggerImpl.h" -#include "internals/SolState.h" -#include "internals/InterfaceTypeInfo.h" -#include "internals/ErrorReporting.h" -#include "internals/PropertyTypeExtractor.h" -#include "internals/EPropertySemantics.h" -#include "internals/EnvironmentProtection.h" +#include "internal/logic/LuaCompilationUtils.h" + +#include "ramses/client/logic/LuaModule.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LuaModuleImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/logic/SolState.h" +#include "internal/logic/InterfaceTypeInfo.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/PropertyTypeExtractor.h" +#include "internal/logic/EPropertySemantics.h" +#include "internal/logic/EnvironmentProtection.h" #include "fmt/format.h" #include "SolHelper.h" @@ -54,12 +54,12 @@ namespace ramses::internal sol::error error = main_result; if (source.empty()) { - errorReporting.add(fmt::format("Fatal error during loading of LuaScript '{}': failed loading pre-compiled byte code and no source available to recompile:\n{}!", name, error.what()), - nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of LuaScript '{}': failed loading pre-compiled byte code and no source available to recompile:\n{}!", name, error.what()), + nullptr); return std::nullopt; } - LOG_WARN("Performance warning! Error during loading of LuaScript '{}' from pre-compiled byte code, will try to recompile script from source code. Error:\n{}!", name, error.what()); + LOG_WARN_P(CONTEXT_CLIENT, "Performance warning! Error during loading of LuaScript '{}' from pre-compiled byte code, will try to recompile script from source code. Error:\n{}!", name, error.what()); byteCodeFromPrecompiledScript.clear(); } } @@ -70,7 +70,7 @@ namespace ramses::internal if (!load_result.valid()) { sol::error error = load_result; - errorReporting.add(fmt::format("[{}] Error while loading script. Lua stack trace:\n{}", name, error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while loading script. Lua stack trace:\n{}", name, error.what()), nullptr); return std::nullopt; } @@ -88,15 +88,15 @@ namespace ramses::internal if (!main_result.valid()) { sol::error error = main_result; - errorReporting.add(error.what(), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(error.what(), nullptr); return std::nullopt; } } if (main_result.get_type() != sol::type::none) { - errorReporting.add(fmt::format("[{}] Expected no return value in script source, but a value of type '{}' was returned!", - name, sol_helper::GetSolTypeName(main_result.get_type())), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Expected no return value in script source, but a value of type '{}' was returned!", + name, sol_helper::GetSolTypeName(main_result.get_type())), nullptr); return std::nullopt; } @@ -115,7 +115,7 @@ namespace ramses::internal if (!initResult.valid()) { sol::error error = initResult; - errorReporting.add(fmt::format("[{}] Error while initializing script. Lua stack trace:\n{}", name, error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while initializing script. Lua stack trace:\n{}", name, error.what()), nullptr); return std::nullopt; } } @@ -124,7 +124,7 @@ namespace ramses::internal if (!run.valid()) { - errorReporting.add(fmt::format("[{}] No 'run' function defined!", name), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] No 'run' function defined!", name), nullptr); return std::nullopt; } @@ -142,7 +142,7 @@ namespace ramses::internal sol::protected_function intf = internalEnv["interface"]; if (!intf.valid()) { - errorReporting.add(fmt::format("[{}] No 'interface' function defined!", name), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] No 'interface' function defined!", name), nullptr); return std::nullopt; } @@ -170,7 +170,7 @@ namespace ramses::internal if (!intfResult.valid()) { sol::error error = intfResult; - errorReporting.add(fmt::format("[{}] Error while loading script. Lua stack trace:\n{}", name, error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while loading script. Lua stack trace:\n{}", name, error.what()), nullptr); return std::nullopt; } @@ -207,7 +207,6 @@ namespace ramses::internal SolState& solState, const ModuleMapping& userModules, const StandardModules& stdModules, - bool verifyModules, const std::string& source, std::string_view name, ErrorReporting& errorReporting) @@ -216,11 +215,11 @@ namespace ramses::internal if (!load_result.valid()) { sol::error error = load_result; - errorReporting.add(fmt::format("[{}] Error while loading interface. Lua stack trace:\n{}", name, error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while loading interface. Lua stack trace:\n{}", name, error.what()), nullptr); return std::nullopt; } - if (verifyModules && !CrossCheckDeclaredAndProvidedModules(source, userModules, name, errorReporting)) + if (!CrossCheckDeclaredAndProvidedModules(source, userModules, name, errorReporting)) return std::nullopt; sol::environment env = solState.createEnvironment(stdModules, userModules, false); @@ -238,21 +237,21 @@ namespace ramses::internal if (!main_result.valid()) { sol::error error = main_result; - errorReporting.add(error.what(), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(error.what(), nullptr); return std::nullopt; } if (main_result.get_type() != sol::type::none) { - errorReporting.add(fmt::format("[{}] Expected no return value in interface source, but a value of type '{}' was returned!", - name, sol_helper::GetSolTypeName(main_result.get_type())), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Expected no return value in interface source, but a value of type '{}' was returned!", + name, sol_helper::GetSolTypeName(main_result.get_type())), nullptr); return std::nullopt; } sol::protected_function intf = internalEnv["interface"]; if (!intf.valid()) { - errorReporting.add(fmt::format("[{}] No 'interface' function defined!", name), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] No 'interface' function defined!", name), nullptr); return std::nullopt; } @@ -273,7 +272,7 @@ namespace ramses::internal if (!interfaceResult.valid()) { sol::error error = interfaceResult; - errorReporting.add(fmt::format("[{}] Error while loading interface. Lua stack trace:\n{}", name, error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while loading interface. Lua stack trace:\n{}", name, error.what()), nullptr); return std::nullopt; } @@ -322,12 +321,12 @@ namespace ramses::internal sol::error error = main_result; if (source.empty()) { - errorReporting.add(fmt::format("Fatal error during loading of LuaModule '{}': failed loading pre-compiled byte code and no source available to recompile:\n{}!", name, error.what()), - nullptr, EErrorType::BinaryVersionMismatch); + errorReporting.set(fmt::format("Fatal error during loading of LuaModule '{}': failed loading pre-compiled byte code and no source available to recompile:\n{}!", name, error.what()), + nullptr); return std::nullopt; } - LOG_WARN("Performance warning! Error during loading of LuaScript '{}' from pre-compiled byte code, will try to recompile script from source code. Error:\n{}!", name, error.what()); + LOG_WARN_P(CONTEXT_CLIENT, "Performance warning! Error during loading of LuaScript '{}' from pre-compiled byte code, will try to recompile script from source code. Error:\n{}!", name, error.what()); byteCodeFromPrecompiledModule.clear(); } } @@ -338,7 +337,7 @@ namespace ramses::internal if (!load_result.valid()) { sol::error error = load_result; - errorReporting.add(fmt::format("[{}] Error while loading module. Lua stack trace:\n{}", name, error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while loading module. Lua stack trace:\n{}", name, error.what()), nullptr); return std::nullopt; } @@ -356,7 +355,7 @@ namespace ramses::internal if (!main_result.valid()) { sol::error error = main_result; - errorReporting.add(error.what(), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(error.what(), nullptr); return std::nullopt; } } @@ -367,7 +366,7 @@ namespace ramses::internal // TODO Violin check and test for abuse: yield, more than one result if (!resultObj.is()) { - errorReporting.add(fmt::format("[{}] Error while loading module. Module script must return a table!", name), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("[{}] Error while loading module. Module script must return a table!", name), nullptr); return std::nullopt; } @@ -466,7 +465,7 @@ namespace ramses::internal std::string errMsg = fmt::format("[{}] Error while loading script/module. Module dependencies declared in source code do not match those provided by LuaConfig.\n", name); errMsg += fmt::format(" Module dependencies declared in source code: {}\n", fmt::join(*declaredModules, ", ")); errMsg += fmt::format(" Module dependencies provided on create API: {}", fmt::join(providedModules, ", ")); - errorReporting.add(errMsg, nullptr, EErrorType::IllegalArgument); + errorReporting.set(errMsg, nullptr); return false; } @@ -492,9 +491,9 @@ namespace ramses::internal else { const auto argTypeName = sol::type_name(v.lua_state(), v.get_type()); - errorReporting.add( + errorReporting.set( fmt::format(R"(Error while extracting module dependencies: argument {} is of type '{}', string must be provided: ex. 'modules("moduleA", "moduleB")')", - argIdx, argTypeName), nullptr, EErrorType::LuaSyntaxError); + argIdx, argTypeName), nullptr); success = false; } ++argIdx; @@ -505,7 +504,7 @@ namespace ramses::internal if (!load_result.valid()) { sol::error error = load_result; - errorReporting.add(fmt::format("Error while extracting module dependencies:\n{}", error.what()), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("Error while extracting module dependencies:\n{}", error.what()), nullptr); return std::nullopt; } @@ -514,7 +513,7 @@ namespace ramses::internal if (!scriptFuncResult.valid()) { const sol::error error = scriptFuncResult; - LOG_DEBUG("Lua runtime error while extracting module dependencies, this is ignored for the actual extraction but might affect its result:\n{}", error.what()); + LOG_DEBUG_P(CONTEXT_CLIENT, "Lua runtime error while extracting module dependencies, this is ignored for the actual extraction but might affect its result:\n{}", error.what()); } if (!success) @@ -522,7 +521,7 @@ namespace ramses::internal if (timesCalled > 1) { - errorReporting.add("Error while extracting module dependencies: 'modules' function was executed more than once", nullptr, EErrorType::LuaSyntaxError); + errorReporting.set("Error while extracting module dependencies: 'modules' function was executed more than once", nullptr); return std::nullopt; } @@ -531,7 +530,7 @@ namespace ramses::internal const auto duplicateIt = std::adjacent_find(sortedDependencies.begin(), sortedDependencies.end()); if (duplicateIt != sortedDependencies.end()) { - errorReporting.add(fmt::format("Error while extracting module dependencies: '{}' appears more than once in dependency list", *duplicateIt), nullptr, EErrorType::LuaSyntaxError); + errorReporting.set(fmt::format("Error while extracting module dependencies: '{}' appears more than once in dependency list", *duplicateIt), nullptr); return std::nullopt; } diff --git a/client/logic/lib/internals/LuaCompilationUtils.h b/src/client/internal/logic/LuaCompilationUtils.h similarity index 97% rename from client/logic/lib/internals/LuaCompilationUtils.h rename to src/client/internal/logic/LuaCompilationUtils.h index 18ac11c0f..585e7140a 100644 --- a/client/logic/lib/internals/LuaCompilationUtils.h +++ b/src/client/internal/logic/LuaCompilationUtils.h @@ -8,8 +8,8 @@ #pragma once -#include "impl/LuaConfigImpl.h" -#include "internals/SolWrapper.h" +#include "impl/logic/LuaConfigImpl.h" +#include "internal/logic/SolWrapper.h" #include #include @@ -79,7 +79,6 @@ namespace ramses::internal SolState& solState, const ModuleMapping& userModules, const StandardModules& stdModules, - bool verifyModules, const std::string& source, std::string_view name, ErrorReporting& errorReporting); diff --git a/client/logic/lib/internals/LuaCustomizations.cpp b/src/client/internal/logic/LuaCustomizations.cpp similarity index 96% rename from client/logic/lib/internals/LuaCustomizations.cpp rename to src/client/internal/logic/LuaCustomizations.cpp index 22dbd629b..16d3f7244 100644 --- a/client/logic/lib/internals/LuaCustomizations.cpp +++ b/src/client/internal/logic/LuaCustomizations.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/LuaCustomizations.h" +#include "internal/logic/LuaCustomizations.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/EPropertyType.h" -#include "impl/LoggerImpl.h" +#include "internal/Core/Utils/LogMacros.h" -#include "internals/SolHelper.h" -#include "internals/WrappedLuaProperty.h" -#include "internals/PropertyTypeExtractor.h" -#include "internals/LuaTypeConversions.h" -#include "internals/TypeUtils.h" +#include "internal/logic/SolHelper.h" +#include "internal/logic/WrappedLuaProperty.h" +#include "internal/logic/PropertyTypeExtractor.h" +#include "internal/logic/LuaTypeConversions.h" +#include "internal/logic/TypeUtils.h" namespace ramses::internal { @@ -41,15 +41,15 @@ namespace ramses::internal { auto rl_logInfo = [](const std::string& msg) { - LOG_INFO("LuaDebugLog: {}", msg); + LOG_INFO_P(CONTEXT_CLIENT, "LuaDebugLog: {}", msg); }; auto rl_logWarn = [](const std::string& msg) { - LOG_WARN("LuaDebugLog: {}", msg); + LOG_WARN_P(CONTEXT_CLIENT, "LuaDebugLog: {}", msg); }; auto rl_logError = [](const std::string& msg) { - LOG_ERROR("LuaDebugLog: {}", msg); + LOG_ERROR_P(CONTEXT_CLIENT, "LuaDebugLog: {}", msg); }; env["rl_logInfo"] = rl_logInfo; diff --git a/client/logic/lib/internals/LuaCustomizations.h b/src/client/internal/logic/LuaCustomizations.h similarity index 98% rename from client/logic/lib/internals/LuaCustomizations.h rename to src/client/internal/logic/LuaCustomizations.h index d29c538de..6cac59e21 100644 --- a/client/logic/lib/internals/LuaCustomizations.h +++ b/src/client/internal/logic/LuaCustomizations.h @@ -8,7 +8,7 @@ #pragma once -#include "internals/SolWrapper.h" +#include "internal/logic/SolWrapper.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/LuaTypeConversions.cpp b/src/client/internal/logic/LuaTypeConversions.cpp similarity index 98% rename from client/logic/lib/internals/LuaTypeConversions.cpp rename to src/client/internal/logic/LuaTypeConversions.cpp index 01e3b24d5..f600158a1 100644 --- a/client/logic/lib/internals/LuaTypeConversions.cpp +++ b/src/client/internal/logic/LuaTypeConversions.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/LuaTypeConversions.h" -#include "internals/SolHelper.h" +#include "internal/logic/LuaTypeConversions.h" +#include "internal/logic/SolHelper.h" #include @@ -50,7 +50,7 @@ namespace ramses::internal return potentialTable; } - size_t LuaTypeConversions::GetMaxIndexForVectorType(ramses::EPropertyType type) + size_t LuaTypeConversions::GetMaxIndexForVectorType(EPropertyType type) { switch (type) { diff --git a/client/logic/lib/internals/LuaTypeConversions.h b/src/client/internal/logic/LuaTypeConversions.h similarity index 95% rename from client/logic/lib/internals/LuaTypeConversions.h rename to src/client/internal/logic/LuaTypeConversions.h index 1d076960a..482a9a12f 100644 --- a/client/logic/lib/internals/LuaTypeConversions.h +++ b/src/client/internal/logic/LuaTypeConversions.h @@ -8,8 +8,8 @@ #pragma once -#include "internals/SolWrapper.h" -#include "ramses-logic/EPropertyType.h" +#include "internal/logic/SolWrapper.h" +#include "ramses/client/logic/EPropertyType.h" #include #include @@ -66,7 +66,7 @@ namespace ramses::internal template [[nodiscard]] static DataOrError ExtractSpecificType(const sol::object& solObject); - [[nodiscard]] static size_t GetMaxIndexForVectorType(ramses::EPropertyType type); + [[nodiscard]] static size_t GetMaxIndexForVectorType(EPropertyType type); template [[nodiscard]] static DataOrError> ExtractArray(const sol::object& solObject); diff --git a/client/logic/lib/internals/PropertyTypeExtractor.cpp b/src/client/internal/logic/PropertyTypeExtractor.cpp similarity index 97% rename from client/logic/lib/internals/PropertyTypeExtractor.cpp rename to src/client/internal/logic/PropertyTypeExtractor.cpp index 92eb11f4d..cfa89b390 100644 --- a/client/logic/lib/internals/PropertyTypeExtractor.cpp +++ b/src/client/internal/logic/PropertyTypeExtractor.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/PropertyTypeExtractor.h" -#include "internals/SolHelper.h" -#include "internals/TypeUtils.h" -#include "internals/InterfaceTypeInfo.h" -#include "internals/InterfaceTypeFunctions.h" -#include "internals/LuaTypeConversions.h" +#include "internal/logic/PropertyTypeExtractor.h" +#include "internal/logic/SolHelper.h" +#include "internal/logic/TypeUtils.h" +#include "internal/logic/InterfaceTypeInfo.h" +#include "internal/logic/InterfaceTypeFunctions.h" +#include "internal/logic/LuaTypeConversions.h" #include "fmt/format.h" #include diff --git a/client/logic/lib/internals/PropertyTypeExtractor.h b/src/client/internal/logic/PropertyTypeExtractor.h similarity index 93% rename from client/logic/lib/internals/PropertyTypeExtractor.h rename to src/client/internal/logic/PropertyTypeExtractor.h index 56b93dc0d..c28ed7eb1 100644 --- a/client/logic/lib/internals/PropertyTypeExtractor.h +++ b/src/client/internal/logic/PropertyTypeExtractor.h @@ -8,9 +8,9 @@ #pragma once -#include "internals/SolWrapper.h" -#include "ramses-logic/EPropertyType.h" -#include "internals/TypeData.h" +#include "internal/logic/SolWrapper.h" +#include "ramses/client/logic/EPropertyType.h" +#include "internal/logic/TypeData.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/RamsesHelper.h b/src/client/internal/logic/RamsesHelper.h similarity index 92% rename from client/logic/lib/internals/RamsesHelper.h rename to src/client/internal/logic/RamsesHelper.h index 87806cce1..3c50cce09 100644 --- a/client/logic/lib/internals/RamsesHelper.h +++ b/src/client/internal/logic/RamsesHelper.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-logic/EPropertyType.h" -#include "ramses-framework-api/EDataType.h" +#include "ramses/client/logic/EPropertyType.h" +#include "ramses/framework/EDataType.h" #include namespace ramses::internal @@ -18,6 +18,8 @@ namespace ramses::internal { switch (type) { + case ramses::EDataType::Bool: + return EPropertyType::Bool; case ramses::EDataType::Int32: return EPropertyType::Int32; case ramses::EDataType::Float: diff --git a/client/logic/lib/internals/RamsesObjectResolver.cpp b/src/client/internal/logic/RamsesObjectResolver.cpp similarity index 84% rename from client/logic/lib/internals/RamsesObjectResolver.cpp rename to src/client/internal/logic/RamsesObjectResolver.cpp index 82d75310d..953cd7316 100644 --- a/client/logic/lib/internals/RamsesObjectResolver.cpp +++ b/src/client/internal/logic/RamsesObjectResolver.cpp @@ -6,19 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/RamsesObjectResolver.h" +#include "internal/logic/RamsesObjectResolver.h" -#include "internals/ErrorReporting.h" +#include "impl/ErrorReporting.h" -#include "ramses-client-api/SceneObject.h" -#include "ramses-client-api/Scene.h" -#include "ramses-utils.h" +#include "ramses/client/Node.h" +#include "ramses/client/Camera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/SceneObject.h" +#include "ramses/client/ramses-utils.h" + +#include "impl/SceneImpl.h" #include "fmt/format.h" namespace ramses::internal { - RamsesObjectResolver::RamsesObjectResolver(ErrorReporting& errorReporting, ramses::Scene& scene) + RamsesObjectResolver::RamsesObjectResolver(ErrorReporting& errorReporting, SceneImpl& scene) : m_errors(errorReporting) , m_scene(scene) { @@ -61,19 +67,17 @@ namespace ramses::internal if (sceneObject == nullptr) { - m_errors.add( + m_errors.set( fmt::format("Fatal error during loading from file! Serialized Ramses Logic object '{}' points to a Ramses object (id: {}) which couldn't be found in the provided scene!", - logicNodeName, objectId.getValue()), - nullptr, EErrorType::ContentStateError); + logicNodeName, objectId.getValue()), nullptr); return nullptr; } - T* concreteObject = ramses::RamsesUtils::TryConvert(*sceneObject); + T* concreteObject = sceneObject->as(); if (concreteObject == nullptr) { - m_errors.add(fmt::format("Fatal error during loading from file! Ramses binding '{}' points to a Ramses scene object (id: {}) which is not of the same type!", - logicNodeName, objectId.getValue()), - nullptr, EErrorType::ContentStateError); + m_errors.set(fmt::format("Fatal error during loading from file! Ramses binding '{}' points to a Ramses scene object (id: {}) which is not of the same type!", + logicNodeName, objectId.getValue()), nullptr); return nullptr; } diff --git a/client/logic/lib/internals/RamsesObjectResolver.h b/src/client/internal/logic/RamsesObjectResolver.h similarity index 95% rename from client/logic/lib/internals/RamsesObjectResolver.h rename to src/client/internal/logic/RamsesObjectResolver.h index f755bb1c5..367e7d0c9 100644 --- a/client/logic/lib/internals/RamsesObjectResolver.h +++ b/src/client/internal/logic/RamsesObjectResolver.h @@ -8,13 +8,12 @@ #pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include namespace ramses { class SceneObject; - class Scene; class Node; class Appearance; class Camera; @@ -25,6 +24,7 @@ namespace ramses namespace ramses::internal { class ErrorReporting; + class SceneImpl; class IRamsesObjectResolver { @@ -42,7 +42,7 @@ namespace ramses::internal class RamsesObjectResolver final : public IRamsesObjectResolver { public: - explicit RamsesObjectResolver(ErrorReporting& errorReporting, ramses::Scene& scene); + explicit RamsesObjectResolver(ErrorReporting& errorReporting, SceneImpl& scene); [[nodiscard]] ramses::Node* findRamsesNodeInScene(std::string_view logicNodeName, ramses::sceneObjectId_t objectId) const override; [[nodiscard]] ramses::Appearance* findRamsesAppearanceInScene(std::string_view logicNodeName, ramses::sceneObjectId_t objectId) const override; @@ -56,6 +56,6 @@ namespace ramses::internal [[nodiscard]] T* findRamsesObjectInScene(std::string_view logicNodeName, ramses::sceneObjectId_t objectId) const; ErrorReporting& m_errors; - ramses::Scene& m_scene; + SceneImpl& m_scene; }; } diff --git a/client/logic/lib/internals/SerializationHelper.h b/src/client/internal/logic/SerializationHelper.h similarity index 95% rename from client/logic/lib/internals/SerializationHelper.h rename to src/client/internal/logic/SerializationHelper.h index bdd2795e9..bc10ad770 100644 --- a/client/logic/lib/internals/SerializationHelper.h +++ b/src/client/internal/logic/SerializationHelper.h @@ -8,9 +8,9 @@ #pragma once -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/EPropertyType.h" -#include "generated/PropertyGen.h" +#include "internal/logic/flatbuffers/generated/PropertyGen.h" #include diff --git a/client/logic/lib/internals/SerializationMap.h b/src/client/internal/logic/SerializationMap.h similarity index 84% rename from client/logic/lib/internals/SerializationMap.h rename to src/client/internal/logic/SerializationMap.h index 6772a7ab5..b59e64be1 100644 --- a/client/logic/lib/internals/SerializationMap.h +++ b/src/client/internal/logic/SerializationMap.h @@ -8,8 +8,9 @@ #pragma once -#include "generated/PropertyGen.h" -#include "generated/DataArrayGen.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/logic/flatbuffers/generated/PropertyGen.h" +#include "internal/logic/flatbuffers/generated/DataArrayGen.h" #include #include @@ -31,12 +32,13 @@ namespace ramses::internal return Get(&prop, m_properties); } - void storeDataArray(uint64_t id, flatbuffers::Offset offset) + void storeDataArray(sceneObjectId_t id, flatbuffers::Offset offset) { + assert(id.isValid()); Store(id, offset, m_dataArrays); } - [[nodiscard]] flatbuffers::Offset resolveDataArrayOffset(uint64_t dataArrayId) const + [[nodiscard]] flatbuffers::Offset resolveDataArrayOffset(sceneObjectId_t dataArrayId) const { return Get(dataArrayId, m_dataArrays); } @@ -74,7 +76,7 @@ namespace ramses::internal } std::unordered_map> m_properties; - std::unordered_map> m_dataArrays; + std::unordered_map> m_dataArrays; std::unordered_map>> m_byteCodeOffsets; }; } diff --git a/client/logic/lib/internals/SolHelper.h b/src/client/internal/logic/SolHelper.h similarity index 94% rename from client/logic/lib/internals/SolHelper.h rename to src/client/internal/logic/SolHelper.h index 046e4674e..459308a95 100644 --- a/client/logic/lib/internals/SolHelper.h +++ b/src/client/internal/logic/SolHelper.h @@ -9,8 +9,8 @@ #pragma once #include "fmt/format.h" -#include "ramses-logic/EStandardModule.h" -#include "internals/SolWrapper.h" +#include "ramses/client/logic/EStandardModule.h" +#include "internal/logic/SolWrapper.h" #include diff --git a/client/logic/lib/internals/SolState.cpp b/src/client/internal/logic/SolState.cpp similarity index 92% rename from client/logic/lib/internals/SolState.cpp rename to src/client/internal/logic/SolState.cpp index c65f3be4d..415039f4f 100644 --- a/client/logic/lib/internals/SolState.cpp +++ b/src/client/internal/logic/SolState.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/SolState.h" +#include "internal/logic/SolState.h" -#include "ramses-logic/LuaModule.h" -#include "impl/LuaModuleImpl.h" +#include "ramses/client/logic/LuaModule.h" +#include "impl/logic/LuaModuleImpl.h" -#include "internals/LuaCustomizations.h" -#include "internals/WrappedLuaProperty.h" -#include "internals/SolHelper.h" -#include "internals/LuaTypeConversions.h" -#include "internals/EnvironmentProtection.h" +#include "internal/logic/LuaCustomizations.h" +#include "internal/logic/WrappedLuaProperty.h" +#include "internal/logic/SolHelper.h" +#include "internal/logic/LuaTypeConversions.h" +#include "internal/logic/EnvironmentProtection.h" #include @@ -92,7 +92,7 @@ namespace ramses::internal for (const auto& module : userModules) { assert(!SolState::IsReservedModuleName(module.first)); - protectedEnv[module.first] = module.second->m_impl.getModule(); + protectedEnv[module.first] = module.second->impl().getModule(); } // TODO Violin take a closer look at this, should not be needed @@ -145,7 +145,7 @@ namespace ramses::internal env[name] = std::move(copy); } - std::optional SolState::GetStdModuleName(ramses::EStandardModule m) + std::optional SolState::GetStdModuleName(EStandardModule m) { switch (m) { diff --git a/client/logic/lib/internals/SolState.h b/src/client/internal/logic/SolState.h similarity index 86% rename from client/logic/lib/internals/SolState.h rename to src/client/internal/logic/SolState.h index 28e9f8a23..ddb5db29e 100644 --- a/client/logic/lib/internals/SolState.h +++ b/src/client/internal/logic/SolState.h @@ -8,20 +8,20 @@ #pragma once -#include "impl/LuaConfigImpl.h" -#include "internals/SolWrapper.h" +#include "impl/logic/LuaConfigImpl.h" +#include "internal/logic/SolWrapper.h" #include #include namespace ramses::internal { - constexpr std::array StdModules = { - ramses::EStandardModule::Base, - ramses::EStandardModule::String, - ramses::EStandardModule::Table, - ramses::EStandardModule::Math, - ramses::EStandardModule::Debug + constexpr std::array StdModules = { + EStandardModule::Base, + EStandardModule::String, + EStandardModule::Table, + EStandardModule::Math, + EStandardModule::Debug }; constexpr std::array SolLibs = { sol::lib::base, @@ -65,6 +65,6 @@ namespace ramses::internal std::vector m_safeBaselibSymbols; void mapStandardModules(const StandardModules& stdModules, sol::environment& env); - [[nodiscard]] static std::optional GetStdModuleName(ramses::EStandardModule m); + [[nodiscard]] static std::optional GetStdModuleName(EStandardModule m); }; } diff --git a/client/logic/lib/internals/SolWrapper.h b/src/client/internal/logic/SolWrapper.h similarity index 98% rename from client/logic/lib/internals/SolWrapper.h rename to src/client/internal/logic/SolWrapper.h index ca77c7024..06133409e 100644 --- a/client/logic/lib/internals/SolWrapper.h +++ b/src/client/internal/logic/SolWrapper.h @@ -89,10 +89,6 @@ __pragma(warning(pop)) #error "LuaJIT is not tested yet, thus we expect that SOL_USE_CXX_LUAJIT is off currently" #endif -#if SOL_IS_ON(SOL_USE_LUA_HPP_I_) -#error "We use the default setting (OFF)" -#endif - // Configured with SOL_EXCEPTIONS_ALWAYS_UNSAFE=1 #if SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS_I_) #error "We want to catch and redirect exceptions and pass to to user handler func instead of prapagating them directly through Lua" @@ -139,5 +135,3 @@ __pragma(warning(pop)) #error "We use the default setting (OFF)" #endif - - diff --git a/client/logic/lib/internals/StdFilesystemWrapper.h b/src/client/internal/logic/StdFilesystemWrapper.h similarity index 100% rename from client/logic/lib/internals/StdFilesystemWrapper.h rename to src/client/internal/logic/StdFilesystemWrapper.h diff --git a/client/logic/lib/internals/TypeData.cpp b/src/client/internal/logic/TypeData.cpp similarity index 97% rename from client/logic/lib/internals/TypeData.cpp rename to src/client/internal/logic/TypeData.cpp index d782077c6..37dbc7eb6 100644 --- a/client/logic/lib/internals/TypeData.cpp +++ b/src/client/internal/logic/TypeData.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/TypeData.h" +#include "internal/logic/TypeData.h" namespace ramses::internal { diff --git a/client/logic/lib/internals/TypeData.h b/src/client/internal/logic/TypeData.h similarity index 98% rename from client/logic/lib/internals/TypeData.h rename to src/client/internal/logic/TypeData.h index 8f319d7d6..43d73d201 100644 --- a/client/logic/lib/internals/TypeData.h +++ b/src/client/internal/logic/TypeData.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/EPropertyType.h" #include #include diff --git a/client/logic/lib/internals/TypeUtils.h b/src/client/internal/logic/TypeUtils.h similarity index 84% rename from client/logic/lib/internals/TypeUtils.h rename to src/client/internal/logic/TypeUtils.h index 7a19afc45..9919cbbee 100644 --- a/client/logic/lib/internals/TypeUtils.h +++ b/src/client/internal/logic/TypeUtils.h @@ -8,9 +8,9 @@ #pragma once -#include "ramses-logic/Property.h" -#include "impl/PropertyImpl.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/PropertyImpl.h" +#include "ramses/framework/DataTypes.h" #include #include @@ -126,13 +126,4 @@ namespace ramses::internal return 0u; } }; - - /// TODO vaclav temporary till these are unified - template struct RlogicTypeToRamsesType { using TYPE = T; }; - template <> struct RlogicTypeToRamsesType { using TYPE = ramses::vec2f; }; - template <> struct RlogicTypeToRamsesType { using TYPE = ramses::vec3f; }; - template <> struct RlogicTypeToRamsesType { using TYPE = ramses::vec4f; }; - template <> struct RlogicTypeToRamsesType { using TYPE = ramses::vec2i; }; - template <> struct RlogicTypeToRamsesType { using TYPE = ramses::vec3i; }; - template <> struct RlogicTypeToRamsesType { using TYPE = ramses::vec4i; }; } diff --git a/client/logic/lib/internals/UpdateReport.cpp b/src/client/internal/logic/UpdateReport.cpp similarity index 98% rename from client/logic/lib/internals/UpdateReport.cpp rename to src/client/internal/logic/UpdateReport.cpp index ffe12b3f3..625248fef 100644 --- a/client/logic/lib/internals/UpdateReport.cpp +++ b/src/client/internal/logic/UpdateReport.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/UpdateReport.h" +#include "internal/logic/UpdateReport.h" #include namespace ramses::internal @@ -79,5 +79,4 @@ namespace ramses::internal { return m_activatedLinks; } - } diff --git a/client/logic/lib/internals/UpdateReport.h b/src/client/internal/logic/UpdateReport.h similarity index 93% rename from client/logic/lib/internals/UpdateReport.h rename to src/client/internal/logic/UpdateReport.h index 726e30b86..17f7fc5ff 100644 --- a/client/logic/lib/internals/UpdateReport.h +++ b/src/client/internal/logic/UpdateReport.h @@ -21,7 +21,8 @@ namespace ramses::internal { public: using ReportTimeUnits = std::chrono::microseconds; - using LogicNodesTimed = std::vector>; + using LogicNodeTimed = std::pair; + using LogicNodesTimed = std::vector; using LogicNodes = std::vector; enum class ETimingSection diff --git a/client/logic/lib/internals/WrappedLuaProperty.cpp b/src/client/internal/logic/WrappedLuaProperty.cpp similarity index 98% rename from client/logic/lib/internals/WrappedLuaProperty.cpp rename to src/client/internal/logic/WrappedLuaProperty.cpp index 2976911a5..e8e40c655 100644 --- a/client/logic/lib/internals/WrappedLuaProperty.cpp +++ b/src/client/internal/logic/WrappedLuaProperty.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/WrappedLuaProperty.h" -#include "internals/SolHelper.h" -#include "internals/LuaTypeConversions.h" -#include "internals/TypeUtils.h" +#include "internal/logic/WrappedLuaProperty.h" +#include "internal/logic/SolHelper.h" +#include "internal/logic/LuaTypeConversions.h" +#include "internal/logic/TypeUtils.h" #include "glm/gtc/type_ptr.hpp" -#include "impl/PropertyImpl.h" +#include "impl/logic/PropertyImpl.h" namespace ramses::internal { @@ -35,7 +35,7 @@ namespace ramses::internal for (size_t i = 0; i < propertyToWrap.getChildCount(); ++i) { - m_wrappedChildProperties.emplace_back(*propertyToWrap.getChild(i)->m_impl); + m_wrappedChildProperties.emplace_back(propertyToWrap.getChild(i)->impl()); } } diff --git a/client/logic/lib/internals/WrappedLuaProperty.h b/src/client/internal/logic/WrappedLuaProperty.h similarity index 96% rename from client/logic/lib/internals/WrappedLuaProperty.h rename to src/client/internal/logic/WrappedLuaProperty.h index f58efffae..467e69ba8 100644 --- a/client/logic/lib/internals/WrappedLuaProperty.h +++ b/src/client/internal/logic/WrappedLuaProperty.h @@ -8,10 +8,10 @@ #pragma once -#include "internals/SolWrapper.h" +#include "internal/logic/SolWrapper.h" -#include "impl/PropertyImpl.h" -#include "internals/SolState.h" +#include "impl/logic/PropertyImpl.h" +#include "internal/logic/SolState.h" namespace ramses { diff --git a/client/logic/lib/flatbuffers/generated/AnchorPointGen.h b/src/client/internal/logic/flatbuffers/generated/AnchorPointGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/AnchorPointGen.h rename to src/client/internal/logic/flatbuffers/generated/AnchorPointGen.h diff --git a/client/logic/lib/flatbuffers/generated/AnimationNodeGen.h b/src/client/internal/logic/flatbuffers/generated/AnimationNodeGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/AnimationNodeGen.h rename to src/client/internal/logic/flatbuffers/generated/AnimationNodeGen.h diff --git a/client/logic/lib/flatbuffers/generated/ApiObjectsGen.h b/src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h similarity index 81% rename from client/logic/lib/flatbuffers/generated/ApiObjectsGen.h rename to src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h index ba562fd29..996013b4c 100644 --- a/client/logic/lib/flatbuffers/generated/ApiObjectsGen.h +++ b/src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h @@ -8,21 +8,21 @@ #include "AnchorPointGen.h" #include "AnimationNodeGen.h" +#include "AppearanceBindingGen.h" +#include "CameraBindingGen.h" #include "DataArrayGen.h" #include "LinkGen.h" #include "LogicObjectGen.h" #include "LuaInterfaceGen.h" #include "LuaModuleGen.h" #include "LuaScriptGen.h" +#include "MeshNodeBindingGen.h" +#include "NodeBindingGen.h" #include "PropertyGen.h" -#include "RamsesAppearanceBindingGen.h" #include "RamsesBindingGen.h" -#include "RamsesCameraBindingGen.h" -#include "RamsesMeshNodeBindingGen.h" -#include "RamsesNodeBindingGen.h" #include "RamsesReferenceGen.h" -#include "RamsesRenderGroupBindingGen.h" -#include "RamsesRenderPassBindingGen.h" +#include "RenderGroupBindingGen.h" +#include "RenderPassBindingGen.h" #include "SkinBindingGen.h" #include "TimerNodeGen.h" @@ -50,12 +50,11 @@ struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_ANIMATIONNODES = 18, VT_TIMERNODES = 20, VT_LINKS = 22, - VT_LASTOBJECTID = 24, - VT_RENDERPASSBINDINGS = 26, - VT_ANCHORPOINTS = 28, - VT_RENDERGROUPBINDINGS = 30, - VT_SKINBINDINGS = 32, - VT_MESHNODEBINDINGS = 34 + VT_RENDERPASSBINDINGS = 24, + VT_ANCHORPOINTS = 26, + VT_RENDERGROUPBINDINGS = 28, + VT_SKINBINDINGS = 30, + VT_MESHNODEBINDINGS = 32 }; const flatbuffers::Vector> *luaModules() const { return GetPointer> *>(VT_LUAMODULES); @@ -66,14 +65,14 @@ struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> *luaInterfaces() const { return GetPointer> *>(VT_LUAINTERFACES); } - const flatbuffers::Vector> *nodeBindings() const { - return GetPointer> *>(VT_NODEBINDINGS); + const flatbuffers::Vector> *nodeBindings() const { + return GetPointer> *>(VT_NODEBINDINGS); } - const flatbuffers::Vector> *appearanceBindings() const { - return GetPointer> *>(VT_APPEARANCEBINDINGS); + const flatbuffers::Vector> *appearanceBindings() const { + return GetPointer> *>(VT_APPEARANCEBINDINGS); } - const flatbuffers::Vector> *cameraBindings() const { - return GetPointer> *>(VT_CAMERABINDINGS); + const flatbuffers::Vector> *cameraBindings() const { + return GetPointer> *>(VT_CAMERABINDINGS); } const flatbuffers::Vector> *dataArrays() const { return GetPointer> *>(VT_DATAARRAYS); @@ -87,23 +86,20 @@ struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::Vector> *links() const { return GetPointer> *>(VT_LINKS); } - uint64_t lastObjectId() const { - return GetField(VT_LASTOBJECTID, 0); - } - const flatbuffers::Vector> *renderPassBindings() const { - return GetPointer> *>(VT_RENDERPASSBINDINGS); + const flatbuffers::Vector> *renderPassBindings() const { + return GetPointer> *>(VT_RENDERPASSBINDINGS); } const flatbuffers::Vector> *anchorPoints() const { return GetPointer> *>(VT_ANCHORPOINTS); } - const flatbuffers::Vector> *renderGroupBindings() const { - return GetPointer> *>(VT_RENDERGROUPBINDINGS); + const flatbuffers::Vector> *renderGroupBindings() const { + return GetPointer> *>(VT_RENDERGROUPBINDINGS); } const flatbuffers::Vector> *skinBindings() const { return GetPointer> *>(VT_SKINBINDINGS); } - const flatbuffers::Vector> *meshNodeBindings() const { - return GetPointer> *>(VT_MESHNODEBINDINGS); + const flatbuffers::Vector> *meshNodeBindings() const { + return GetPointer> *>(VT_MESHNODEBINDINGS); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && @@ -137,7 +133,6 @@ struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyOffset(verifier, VT_LINKS) && verifier.VerifyVector(links()) && verifier.VerifyVectorOfTables(links()) && - VerifyField(verifier, VT_LASTOBJECTID) && VerifyOffset(verifier, VT_RENDERPASSBINDINGS) && verifier.VerifyVector(renderPassBindings()) && verifier.VerifyVectorOfTables(renderPassBindings()) && @@ -170,13 +165,13 @@ struct ApiObjectsBuilder { void add_luaInterfaces(flatbuffers::Offset>> luaInterfaces) { fbb_.AddOffset(ApiObjects::VT_LUAINTERFACES, luaInterfaces); } - void add_nodeBindings(flatbuffers::Offset>> nodeBindings) { + void add_nodeBindings(flatbuffers::Offset>> nodeBindings) { fbb_.AddOffset(ApiObjects::VT_NODEBINDINGS, nodeBindings); } - void add_appearanceBindings(flatbuffers::Offset>> appearanceBindings) { + void add_appearanceBindings(flatbuffers::Offset>> appearanceBindings) { fbb_.AddOffset(ApiObjects::VT_APPEARANCEBINDINGS, appearanceBindings); } - void add_cameraBindings(flatbuffers::Offset>> cameraBindings) { + void add_cameraBindings(flatbuffers::Offset>> cameraBindings) { fbb_.AddOffset(ApiObjects::VT_CAMERABINDINGS, cameraBindings); } void add_dataArrays(flatbuffers::Offset>> dataArrays) { @@ -191,22 +186,19 @@ struct ApiObjectsBuilder { void add_links(flatbuffers::Offset>> links) { fbb_.AddOffset(ApiObjects::VT_LINKS, links); } - void add_lastObjectId(uint64_t lastObjectId) { - fbb_.AddElement(ApiObjects::VT_LASTOBJECTID, lastObjectId, 0); - } - void add_renderPassBindings(flatbuffers::Offset>> renderPassBindings) { + void add_renderPassBindings(flatbuffers::Offset>> renderPassBindings) { fbb_.AddOffset(ApiObjects::VT_RENDERPASSBINDINGS, renderPassBindings); } void add_anchorPoints(flatbuffers::Offset>> anchorPoints) { fbb_.AddOffset(ApiObjects::VT_ANCHORPOINTS, anchorPoints); } - void add_renderGroupBindings(flatbuffers::Offset>> renderGroupBindings) { + void add_renderGroupBindings(flatbuffers::Offset>> renderGroupBindings) { fbb_.AddOffset(ApiObjects::VT_RENDERGROUPBINDINGS, renderGroupBindings); } void add_skinBindings(flatbuffers::Offset>> skinBindings) { fbb_.AddOffset(ApiObjects::VT_SKINBINDINGS, skinBindings); } - void add_meshNodeBindings(flatbuffers::Offset>> meshNodeBindings) { + void add_meshNodeBindings(flatbuffers::Offset>> meshNodeBindings) { fbb_.AddOffset(ApiObjects::VT_MESHNODEBINDINGS, meshNodeBindings); } explicit ApiObjectsBuilder(flatbuffers::FlatBufferBuilder &_fbb) @@ -226,21 +218,19 @@ inline flatbuffers::Offset CreateApiObjects( flatbuffers::Offset>> luaModules = 0, flatbuffers::Offset>> luaScripts = 0, flatbuffers::Offset>> luaInterfaces = 0, - flatbuffers::Offset>> nodeBindings = 0, - flatbuffers::Offset>> appearanceBindings = 0, - flatbuffers::Offset>> cameraBindings = 0, + flatbuffers::Offset>> nodeBindings = 0, + flatbuffers::Offset>> appearanceBindings = 0, + flatbuffers::Offset>> cameraBindings = 0, flatbuffers::Offset>> dataArrays = 0, flatbuffers::Offset>> animationNodes = 0, flatbuffers::Offset>> timerNodes = 0, flatbuffers::Offset>> links = 0, - uint64_t lastObjectId = 0, - flatbuffers::Offset>> renderPassBindings = 0, + flatbuffers::Offset>> renderPassBindings = 0, flatbuffers::Offset>> anchorPoints = 0, - flatbuffers::Offset>> renderGroupBindings = 0, + flatbuffers::Offset>> renderGroupBindings = 0, flatbuffers::Offset>> skinBindings = 0, - flatbuffers::Offset>> meshNodeBindings = 0) { + flatbuffers::Offset>> meshNodeBindings = 0) { ApiObjectsBuilder builder_(_fbb); - builder_.add_lastObjectId(lastObjectId); builder_.add_meshNodeBindings(meshNodeBindings); builder_.add_skinBindings(skinBindings); builder_.add_renderGroupBindings(renderGroupBindings); @@ -269,34 +259,33 @@ inline flatbuffers::Offset CreateApiObjectsDirect( const std::vector> *luaModules = nullptr, const std::vector> *luaScripts = nullptr, const std::vector> *luaInterfaces = nullptr, - const std::vector> *nodeBindings = nullptr, - const std::vector> *appearanceBindings = nullptr, - const std::vector> *cameraBindings = nullptr, + const std::vector> *nodeBindings = nullptr, + const std::vector> *appearanceBindings = nullptr, + const std::vector> *cameraBindings = nullptr, const std::vector> *dataArrays = nullptr, const std::vector> *animationNodes = nullptr, const std::vector> *timerNodes = nullptr, const std::vector> *links = nullptr, - uint64_t lastObjectId = 0, - const std::vector> *renderPassBindings = nullptr, + const std::vector> *renderPassBindings = nullptr, const std::vector> *anchorPoints = nullptr, - const std::vector> *renderGroupBindings = nullptr, + const std::vector> *renderGroupBindings = nullptr, const std::vector> *skinBindings = nullptr, - const std::vector> *meshNodeBindings = nullptr) { + const std::vector> *meshNodeBindings = nullptr) { auto luaModules__ = luaModules ? _fbb.CreateVector>(*luaModules) : 0; auto luaScripts__ = luaScripts ? _fbb.CreateVector>(*luaScripts) : 0; auto luaInterfaces__ = luaInterfaces ? _fbb.CreateVector>(*luaInterfaces) : 0; - auto nodeBindings__ = nodeBindings ? _fbb.CreateVector>(*nodeBindings) : 0; - auto appearanceBindings__ = appearanceBindings ? _fbb.CreateVector>(*appearanceBindings) : 0; - auto cameraBindings__ = cameraBindings ? _fbb.CreateVector>(*cameraBindings) : 0; + auto nodeBindings__ = nodeBindings ? _fbb.CreateVector>(*nodeBindings) : 0; + auto appearanceBindings__ = appearanceBindings ? _fbb.CreateVector>(*appearanceBindings) : 0; + auto cameraBindings__ = cameraBindings ? _fbb.CreateVector>(*cameraBindings) : 0; auto dataArrays__ = dataArrays ? _fbb.CreateVector>(*dataArrays) : 0; auto animationNodes__ = animationNodes ? _fbb.CreateVector>(*animationNodes) : 0; auto timerNodes__ = timerNodes ? _fbb.CreateVector>(*timerNodes) : 0; auto links__ = links ? _fbb.CreateVector>(*links) : 0; - auto renderPassBindings__ = renderPassBindings ? _fbb.CreateVector>(*renderPassBindings) : 0; + auto renderPassBindings__ = renderPassBindings ? _fbb.CreateVector>(*renderPassBindings) : 0; auto anchorPoints__ = anchorPoints ? _fbb.CreateVector>(*anchorPoints) : 0; - auto renderGroupBindings__ = renderGroupBindings ? _fbb.CreateVector>(*renderGroupBindings) : 0; + auto renderGroupBindings__ = renderGroupBindings ? _fbb.CreateVector>(*renderGroupBindings) : 0; auto skinBindings__ = skinBindings ? _fbb.CreateVector>(*skinBindings) : 0; - auto meshNodeBindings__ = meshNodeBindings ? _fbb.CreateVector>(*meshNodeBindings) : 0; + auto meshNodeBindings__ = meshNodeBindings ? _fbb.CreateVector>(*meshNodeBindings) : 0; return rlogic_serialization::CreateApiObjects( _fbb, luaModules__, @@ -309,7 +298,6 @@ inline flatbuffers::Offset CreateApiObjectsDirect( animationNodes__, timerNodes__, links__, - lastObjectId, renderPassBindings__, anchorPoints__, renderGroupBindings__, @@ -329,7 +317,6 @@ inline const flatbuffers::TypeTable *ApiObjectsTypeTable() { { flatbuffers::ET_SEQUENCE, 1, 7 }, { flatbuffers::ET_SEQUENCE, 1, 8 }, { flatbuffers::ET_SEQUENCE, 1, 9 }, - { flatbuffers::ET_ULONG, 0, -1 }, { flatbuffers::ET_SEQUENCE, 1, 10 }, { flatbuffers::ET_SEQUENCE, 1, 11 }, { flatbuffers::ET_SEQUENCE, 1, 12 }, @@ -340,18 +327,18 @@ inline const flatbuffers::TypeTable *ApiObjectsTypeTable() { rlogic_serialization::LuaModuleTypeTable, rlogic_serialization::LuaScriptTypeTable, rlogic_serialization::LuaInterfaceTypeTable, - rlogic_serialization::RamsesNodeBindingTypeTable, - rlogic_serialization::RamsesAppearanceBindingTypeTable, - rlogic_serialization::RamsesCameraBindingTypeTable, + rlogic_serialization::NodeBindingTypeTable, + rlogic_serialization::AppearanceBindingTypeTable, + rlogic_serialization::CameraBindingTypeTable, rlogic_serialization::DataArrayTypeTable, rlogic_serialization::AnimationNodeTypeTable, rlogic_serialization::TimerNodeTypeTable, rlogic_serialization::LinkTypeTable, - rlogic_serialization::RamsesRenderPassBindingTypeTable, + rlogic_serialization::RenderPassBindingTypeTable, rlogic_serialization::AnchorPointTypeTable, - rlogic_serialization::RamsesRenderGroupBindingTypeTable, + rlogic_serialization::RenderGroupBindingTypeTable, rlogic_serialization::SkinBindingTypeTable, - rlogic_serialization::RamsesMeshNodeBindingTypeTable + rlogic_serialization::MeshNodeBindingTypeTable }; static const char * const names[] = { "luaModules", @@ -364,7 +351,6 @@ inline const flatbuffers::TypeTable *ApiObjectsTypeTable() { "animationNodes", "timerNodes", "links", - "lastObjectId", "renderPassBindings", "anchorPoints", "renderGroupBindings", @@ -372,7 +358,7 @@ inline const flatbuffers::TypeTable *ApiObjectsTypeTable() { "meshNodeBindings" }; static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 16, type_codes, type_refs, nullptr, names + flatbuffers::ST_TABLE, 15, type_codes, type_refs, nullptr, names }; return &tt; } diff --git a/client/logic/lib/flatbuffers/generated/RamsesAppearanceBindingGen.h b/src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h similarity index 71% rename from client/logic/lib/flatbuffers/generated/RamsesAppearanceBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h index 9b8de6814..17ed55924 100644 --- a/client/logic/lib/flatbuffers/generated/RamsesAppearanceBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h @@ -1,8 +1,8 @@ // automatically generated by the FlatBuffers compiler, do not modify -#ifndef FLATBUFFERS_GENERATED_RAMSESAPPEARANCEBINDING_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_RAMSESAPPEARANCEBINDING_RLOGIC_SERIALIZATION_H_ +#ifndef FLATBUFFERS_GENERATED_APPEARANCEBINDING_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_APPEARANCEBINDING_RLOGIC_SERIALIZATION_H_ #include "flatbuffers/flatbuffers.h" @@ -15,12 +15,12 @@ namespace rlogic_serialization { struct ResourceId; -struct RamsesAppearanceBinding; -struct RamsesAppearanceBindingBuilder; +struct AppearanceBinding; +struct AppearanceBindingBuilder; inline const flatbuffers::TypeTable *ResourceIdTypeTable(); -inline const flatbuffers::TypeTable *RamsesAppearanceBindingTypeTable(); +inline const flatbuffers::TypeTable *AppearanceBindingTypeTable(); FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ResourceId FLATBUFFERS_FINAL_CLASS { private: @@ -47,11 +47,11 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ResourceId FLATBUFFERS_FINAL_CLASS { }; FLATBUFFERS_STRUCT_END(ResourceId, 16); -struct RamsesAppearanceBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef RamsesAppearanceBindingBuilder Builder; +struct AppearanceBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef AppearanceBindingBuilder Builder; struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return RamsesAppearanceBindingTypeTable(); + return AppearanceBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE = 4, @@ -72,41 +72,41 @@ struct RamsesAppearanceBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta } }; -struct RamsesAppearanceBindingBuilder { - typedef RamsesAppearanceBinding Table; +struct AppearanceBindingBuilder { + typedef AppearanceBinding Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; void add_base(flatbuffers::Offset base) { - fbb_.AddOffset(RamsesAppearanceBinding::VT_BASE, base); + fbb_.AddOffset(AppearanceBinding::VT_BASE, base); } void add_parentEffectId(const rlogic_serialization::ResourceId *parentEffectId) { - fbb_.AddStruct(RamsesAppearanceBinding::VT_PARENTEFFECTID, parentEffectId); + fbb_.AddStruct(AppearanceBinding::VT_PARENTEFFECTID, parentEffectId); } - explicit RamsesAppearanceBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit AppearanceBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesAppearanceBindingBuilder &operator=(const RamsesAppearanceBindingBuilder &); - flatbuffers::Offset Finish() { + AppearanceBindingBuilder &operator=(const AppearanceBindingBuilder &); + flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesAppearanceBinding( +inline flatbuffers::Offset CreateAppearanceBinding( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0, const rlogic_serialization::ResourceId *parentEffectId = 0) { - RamsesAppearanceBindingBuilder builder_(_fbb); + AppearanceBindingBuilder builder_(_fbb); builder_.add_parentEffectId(parentEffectId); builder_.add_base(base); return builder_.Finish(); } -struct RamsesAppearanceBinding::Traits { - using type = RamsesAppearanceBinding; - static auto constexpr Create = CreateRamsesAppearanceBinding; +struct AppearanceBinding::Traits { + using type = AppearanceBinding; + static auto constexpr Create = CreateAppearanceBinding; }; inline const flatbuffers::TypeTable *ResourceIdTypeTable() { @@ -125,7 +125,7 @@ inline const flatbuffers::TypeTable *ResourceIdTypeTable() { return &tt; } -inline const flatbuffers::TypeTable *RamsesAppearanceBindingTypeTable() { +inline const flatbuffers::TypeTable *AppearanceBindingTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_SEQUENCE, 0, 0 }, { flatbuffers::ET_SEQUENCE, 0, 1 } @@ -146,4 +146,4 @@ inline const flatbuffers::TypeTable *RamsesAppearanceBindingTypeTable() { } // namespace rlogic_serialization -#endif // FLATBUFFERS_GENERATED_RAMSESAPPEARANCEBINDING_RLOGIC_SERIALIZATION_H_ +#endif // FLATBUFFERS_GENERATED_APPEARANCEBINDING_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/RamsesMeshNodeBindingGen.h b/src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h similarity index 57% rename from client/logic/lib/flatbuffers/generated/RamsesMeshNodeBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h index 006704cd1..e8f5270cd 100644 --- a/client/logic/lib/flatbuffers/generated/RamsesMeshNodeBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h @@ -1,8 +1,8 @@ // automatically generated by the FlatBuffers compiler, do not modify -#ifndef FLATBUFFERS_GENERATED_RAMSESMESHNODEBINDING_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_RAMSESMESHNODEBINDING_RLOGIC_SERIALIZATION_H_ +#ifndef FLATBUFFERS_GENERATED_CAMERABINDING_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_CAMERABINDING_RLOGIC_SERIALIZATION_H_ #include "flatbuffers/flatbuffers.h" @@ -13,16 +13,16 @@ namespace rlogic_serialization { -struct RamsesMeshNodeBinding; -struct RamsesMeshNodeBindingBuilder; +struct CameraBinding; +struct CameraBindingBuilder; -inline const flatbuffers::TypeTable *RamsesMeshNodeBindingTypeTable(); +inline const flatbuffers::TypeTable *CameraBindingTypeTable(); -struct RamsesMeshNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef RamsesMeshNodeBindingBuilder Builder; +struct CameraBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef CameraBindingBuilder Builder; struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return RamsesMeshNodeBindingTypeTable(); + return CameraBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE = 4 @@ -38,39 +38,39 @@ struct RamsesMeshNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Tabl } }; -struct RamsesMeshNodeBindingBuilder { - typedef RamsesMeshNodeBinding Table; +struct CameraBindingBuilder { + typedef CameraBinding Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; void add_base(flatbuffers::Offset base) { - fbb_.AddOffset(RamsesMeshNodeBinding::VT_BASE, base); + fbb_.AddOffset(CameraBinding::VT_BASE, base); } - explicit RamsesMeshNodeBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit CameraBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesMeshNodeBindingBuilder &operator=(const RamsesMeshNodeBindingBuilder &); - flatbuffers::Offset Finish() { + CameraBindingBuilder &operator=(const CameraBindingBuilder &); + flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesMeshNodeBinding( +inline flatbuffers::Offset CreateCameraBinding( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0) { - RamsesMeshNodeBindingBuilder builder_(_fbb); + CameraBindingBuilder builder_(_fbb); builder_.add_base(base); return builder_.Finish(); } -struct RamsesMeshNodeBinding::Traits { - using type = RamsesMeshNodeBinding; - static auto constexpr Create = CreateRamsesMeshNodeBinding; +struct CameraBinding::Traits { + using type = CameraBinding; + static auto constexpr Create = CreateCameraBinding; }; -inline const flatbuffers::TypeTable *RamsesMeshNodeBindingTypeTable() { +inline const flatbuffers::TypeTable *CameraBindingTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_SEQUENCE, 0, 0 } }; @@ -88,4 +88,4 @@ inline const flatbuffers::TypeTable *RamsesMeshNodeBindingTypeTable() { } // namespace rlogic_serialization -#endif // FLATBUFFERS_GENERATED_RAMSESMESHNODEBINDING_RLOGIC_SERIALIZATION_H_ +#endif // FLATBUFFERS_GENERATED_CAMERABINDING_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/DataArrayGen.h b/src/client/internal/logic/flatbuffers/generated/DataArrayGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/DataArrayGen.h rename to src/client/internal/logic/flatbuffers/generated/DataArrayGen.h diff --git a/client/logic/lib/flatbuffers/generated/LinkGen.h b/src/client/internal/logic/flatbuffers/generated/LinkGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/LinkGen.h rename to src/client/internal/logic/flatbuffers/generated/LinkGen.h diff --git a/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h b/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h new file mode 100644 index 000000000..1dbd21f45 --- /dev/null +++ b/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h @@ -0,0 +1,137 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_LOGICENGINE_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_LOGICENGINE_RLOGIC_SERIALIZATION_H_ + +#include "flatbuffers/flatbuffers.h" + +#include "AnchorPointGen.h" +#include "AnimationNodeGen.h" +#include "ApiObjectsGen.h" +#include "AppearanceBindingGen.h" +#include "CameraBindingGen.h" +#include "DataArrayGen.h" +#include "LinkGen.h" +#include "LogicObjectGen.h" +#include "LuaInterfaceGen.h" +#include "LuaModuleGen.h" +#include "LuaScriptGen.h" +#include "MeshNodeBindingGen.h" +#include "NodeBindingGen.h" +#include "PropertyGen.h" +#include "RamsesBindingGen.h" +#include "RamsesReferenceGen.h" +#include "RenderGroupBindingGen.h" +#include "RenderPassBindingGen.h" +#include "SkinBindingGen.h" +#include "TimerNodeGen.h" + +namespace rlogic_serialization { + +struct LogicEngine; +struct LogicEngineBuilder; + +inline const flatbuffers::TypeTable *LogicEngineTypeTable(); + +struct LogicEngine FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef LogicEngineBuilder Builder; + struct Traits; + static const flatbuffers::TypeTable *MiniReflectTypeTable() { + return LogicEngineTypeTable(); + } + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_APIOBJECTS = 4 + }; + const rlogic_serialization::ApiObjects *apiObjects() const { + return GetPointer(VT_APIOBJECTS); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_APIOBJECTS) && + verifier.VerifyTable(apiObjects()) && + verifier.EndTable(); + } +}; + +struct LogicEngineBuilder { + typedef LogicEngine Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_apiObjects(flatbuffers::Offset apiObjects) { + fbb_.AddOffset(LogicEngine::VT_APIOBJECTS, apiObjects); + } + explicit LogicEngineBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + LogicEngineBuilder &operator=(const LogicEngineBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateLogicEngine( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset apiObjects = 0) { + LogicEngineBuilder builder_(_fbb); + builder_.add_apiObjects(apiObjects); + return builder_.Finish(); +} + +struct LogicEngine::Traits { + using type = LogicEngine; + static auto constexpr Create = CreateLogicEngine; +}; + +inline const flatbuffers::TypeTable *LogicEngineTypeTable() { + static const flatbuffers::TypeCode type_codes[] = { + { flatbuffers::ET_SEQUENCE, 0, 0 } + }; + static const flatbuffers::TypeFunction type_refs[] = { + rlogic_serialization::ApiObjectsTypeTable + }; + static const char * const names[] = { + "apiObjects" + }; + static const flatbuffers::TypeTable tt = { + flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + }; + return &tt; +} + +inline const rlogic_serialization::LogicEngine *GetLogicEngine(const void *buf) { + return flatbuffers::GetRoot(buf); +} + +inline const rlogic_serialization::LogicEngine *GetSizePrefixedLogicEngine(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyLogicEngineBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedLogicEngineBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishLogicEngineBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedLogicEngineBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace rlogic_serialization + +#endif // FLATBUFFERS_GENERATED_LOGICENGINE_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/LogicObjectGen.h b/src/client/internal/logic/flatbuffers/generated/LogicObjectGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/LogicObjectGen.h rename to src/client/internal/logic/flatbuffers/generated/LogicObjectGen.h diff --git a/client/logic/lib/flatbuffers/generated/LuaInterfaceGen.h b/src/client/internal/logic/flatbuffers/generated/LuaInterfaceGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/LuaInterfaceGen.h rename to src/client/internal/logic/flatbuffers/generated/LuaInterfaceGen.h diff --git a/client/logic/lib/flatbuffers/generated/LuaModuleGen.h b/src/client/internal/logic/flatbuffers/generated/LuaModuleGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/LuaModuleGen.h rename to src/client/internal/logic/flatbuffers/generated/LuaModuleGen.h diff --git a/client/logic/lib/flatbuffers/generated/LuaScriptGen.h b/src/client/internal/logic/flatbuffers/generated/LuaScriptGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/LuaScriptGen.h rename to src/client/internal/logic/flatbuffers/generated/LuaScriptGen.h diff --git a/client/logic/lib/flatbuffers/generated/RamsesCameraBindingGen.h b/src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h similarity index 57% rename from client/logic/lib/flatbuffers/generated/RamsesCameraBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h index 743673d04..499a4bc83 100644 --- a/client/logic/lib/flatbuffers/generated/RamsesCameraBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h @@ -1,8 +1,8 @@ // automatically generated by the FlatBuffers compiler, do not modify -#ifndef FLATBUFFERS_GENERATED_RAMSESCAMERABINDING_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_RAMSESCAMERABINDING_RLOGIC_SERIALIZATION_H_ +#ifndef FLATBUFFERS_GENERATED_MESHNODEBINDING_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_MESHNODEBINDING_RLOGIC_SERIALIZATION_H_ #include "flatbuffers/flatbuffers.h" @@ -13,16 +13,16 @@ namespace rlogic_serialization { -struct RamsesCameraBinding; -struct RamsesCameraBindingBuilder; +struct MeshNodeBinding; +struct MeshNodeBindingBuilder; -inline const flatbuffers::TypeTable *RamsesCameraBindingTypeTable(); +inline const flatbuffers::TypeTable *MeshNodeBindingTypeTable(); -struct RamsesCameraBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef RamsesCameraBindingBuilder Builder; +struct MeshNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef MeshNodeBindingBuilder Builder; struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return RamsesCameraBindingTypeTable(); + return MeshNodeBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE = 4 @@ -38,39 +38,39 @@ struct RamsesCameraBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table } }; -struct RamsesCameraBindingBuilder { - typedef RamsesCameraBinding Table; +struct MeshNodeBindingBuilder { + typedef MeshNodeBinding Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; void add_base(flatbuffers::Offset base) { - fbb_.AddOffset(RamsesCameraBinding::VT_BASE, base); + fbb_.AddOffset(MeshNodeBinding::VT_BASE, base); } - explicit RamsesCameraBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit MeshNodeBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesCameraBindingBuilder &operator=(const RamsesCameraBindingBuilder &); - flatbuffers::Offset Finish() { + MeshNodeBindingBuilder &operator=(const MeshNodeBindingBuilder &); + flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesCameraBinding( +inline flatbuffers::Offset CreateMeshNodeBinding( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0) { - RamsesCameraBindingBuilder builder_(_fbb); + MeshNodeBindingBuilder builder_(_fbb); builder_.add_base(base); return builder_.Finish(); } -struct RamsesCameraBinding::Traits { - using type = RamsesCameraBinding; - static auto constexpr Create = CreateRamsesCameraBinding; +struct MeshNodeBinding::Traits { + using type = MeshNodeBinding; + static auto constexpr Create = CreateMeshNodeBinding; }; -inline const flatbuffers::TypeTable *RamsesCameraBindingTypeTable() { +inline const flatbuffers::TypeTable *MeshNodeBindingTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_SEQUENCE, 0, 0 } }; @@ -88,4 +88,4 @@ inline const flatbuffers::TypeTable *RamsesCameraBindingTypeTable() { } // namespace rlogic_serialization -#endif // FLATBUFFERS_GENERATED_RAMSESCAMERABINDING_RLOGIC_SERIALIZATION_H_ +#endif // FLATBUFFERS_GENERATED_MESHNODEBINDING_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/RamsesNodeBindingGen.h b/src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h similarity index 61% rename from client/logic/lib/flatbuffers/generated/RamsesNodeBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h index abeee4901..5f5fe512c 100644 --- a/client/logic/lib/flatbuffers/generated/RamsesNodeBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h @@ -1,8 +1,8 @@ // automatically generated by the FlatBuffers compiler, do not modify -#ifndef FLATBUFFERS_GENERATED_RAMSESNODEBINDING_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_RAMSESNODEBINDING_RLOGIC_SERIALIZATION_H_ +#ifndef FLATBUFFERS_GENERATED_NODEBINDING_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_NODEBINDING_RLOGIC_SERIALIZATION_H_ #include "flatbuffers/flatbuffers.h" @@ -13,16 +13,16 @@ namespace rlogic_serialization { -struct RamsesNodeBinding; -struct RamsesNodeBindingBuilder; +struct NodeBinding; +struct NodeBindingBuilder; -inline const flatbuffers::TypeTable *RamsesNodeBindingTypeTable(); +inline const flatbuffers::TypeTable *NodeBindingTypeTable(); -struct RamsesNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef RamsesNodeBindingBuilder Builder; +struct NodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef NodeBindingBuilder Builder; struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return RamsesNodeBindingTypeTable(); + return NodeBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE = 4, @@ -43,44 +43,44 @@ struct RamsesNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { } }; -struct RamsesNodeBindingBuilder { - typedef RamsesNodeBinding Table; +struct NodeBindingBuilder { + typedef NodeBinding Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; void add_base(flatbuffers::Offset base) { - fbb_.AddOffset(RamsesNodeBinding::VT_BASE, base); + fbb_.AddOffset(NodeBinding::VT_BASE, base); } void add_rotationType(uint8_t rotationType) { - fbb_.AddElement(RamsesNodeBinding::VT_ROTATIONTYPE, rotationType, 0); + fbb_.AddElement(NodeBinding::VT_ROTATIONTYPE, rotationType, 0); } - explicit RamsesNodeBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit NodeBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesNodeBindingBuilder &operator=(const RamsesNodeBindingBuilder &); - flatbuffers::Offset Finish() { + NodeBindingBuilder &operator=(const NodeBindingBuilder &); + flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesNodeBinding( +inline flatbuffers::Offset CreateNodeBinding( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0, uint8_t rotationType = 0) { - RamsesNodeBindingBuilder builder_(_fbb); + NodeBindingBuilder builder_(_fbb); builder_.add_base(base); builder_.add_rotationType(rotationType); return builder_.Finish(); } -struct RamsesNodeBinding::Traits { - using type = RamsesNodeBinding; - static auto constexpr Create = CreateRamsesNodeBinding; +struct NodeBinding::Traits { + using type = NodeBinding; + static auto constexpr Create = CreateNodeBinding; }; -inline const flatbuffers::TypeTable *RamsesNodeBindingTypeTable() { +inline const flatbuffers::TypeTable *NodeBindingTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_SEQUENCE, 0, 0 }, { flatbuffers::ET_UCHAR, 0, -1 } @@ -100,4 +100,4 @@ inline const flatbuffers::TypeTable *RamsesNodeBindingTypeTable() { } // namespace rlogic_serialization -#endif // FLATBUFFERS_GENERATED_RAMSESNODEBINDING_RLOGIC_SERIALIZATION_H_ +#endif // FLATBUFFERS_GENERATED_NODEBINDING_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/PropertyGen.h b/src/client/internal/logic/flatbuffers/generated/PropertyGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/PropertyGen.h rename to src/client/internal/logic/flatbuffers/generated/PropertyGen.h diff --git a/client/logic/lib/flatbuffers/generated/RamsesBindingGen.h b/src/client/internal/logic/flatbuffers/generated/RamsesBindingGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/RamsesBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/RamsesBindingGen.h diff --git a/client/logic/lib/flatbuffers/generated/RamsesReferenceGen.h b/src/client/internal/logic/flatbuffers/generated/RamsesReferenceGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/RamsesReferenceGen.h rename to src/client/internal/logic/flatbuffers/generated/RamsesReferenceGen.h diff --git a/client/logic/lib/flatbuffers/generated/RamsesRenderGroupBindingGen.h b/src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h similarity index 78% rename from client/logic/lib/flatbuffers/generated/RamsesRenderGroupBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h index 7b53bfc1a..2efc9d60e 100644 --- a/client/logic/lib/flatbuffers/generated/RamsesRenderGroupBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h @@ -1,8 +1,8 @@ // automatically generated by the FlatBuffers compiler, do not modify -#ifndef FLATBUFFERS_GENERATED_RAMSESRENDERGROUPBINDING_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_RAMSESRENDERGROUPBINDING_RLOGIC_SERIALIZATION_H_ +#ifndef FLATBUFFERS_GENERATED_RENDERGROUPBINDING_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_RENDERGROUPBINDING_RLOGIC_SERIALIZATION_H_ #include "flatbuffers/flatbuffers.h" @@ -16,12 +16,12 @@ namespace rlogic_serialization { struct Element; struct ElementBuilder; -struct RamsesRenderGroupBinding; -struct RamsesRenderGroupBindingBuilder; +struct RenderGroupBinding; +struct RenderGroupBindingBuilder; inline const flatbuffers::TypeTable *ElementTypeTable(); -inline const flatbuffers::TypeTable *RamsesRenderGroupBindingTypeTable(); +inline const flatbuffers::TypeTable *RenderGroupBindingTypeTable(); struct Element FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef ElementBuilder Builder; @@ -97,11 +97,11 @@ inline flatbuffers::Offset CreateElementDirect( ramsesObject); } -struct RamsesRenderGroupBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef RamsesRenderGroupBindingBuilder Builder; +struct RenderGroupBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef RenderGroupBindingBuilder Builder; struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return RamsesRenderGroupBindingTypeTable(); + return RenderGroupBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE = 4, @@ -124,49 +124,49 @@ struct RamsesRenderGroupBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::T } }; -struct RamsesRenderGroupBindingBuilder { - typedef RamsesRenderGroupBinding Table; +struct RenderGroupBindingBuilder { + typedef RenderGroupBinding Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; void add_base(flatbuffers::Offset base) { - fbb_.AddOffset(RamsesRenderGroupBinding::VT_BASE, base); + fbb_.AddOffset(RenderGroupBinding::VT_BASE, base); } void add_elements(flatbuffers::Offset>> elements) { - fbb_.AddOffset(RamsesRenderGroupBinding::VT_ELEMENTS, elements); + fbb_.AddOffset(RenderGroupBinding::VT_ELEMENTS, elements); } - explicit RamsesRenderGroupBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit RenderGroupBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesRenderGroupBindingBuilder &operator=(const RamsesRenderGroupBindingBuilder &); - flatbuffers::Offset Finish() { + RenderGroupBindingBuilder &operator=(const RenderGroupBindingBuilder &); + flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesRenderGroupBinding( +inline flatbuffers::Offset CreateRenderGroupBinding( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0, flatbuffers::Offset>> elements = 0) { - RamsesRenderGroupBindingBuilder builder_(_fbb); + RenderGroupBindingBuilder builder_(_fbb); builder_.add_elements(elements); builder_.add_base(base); return builder_.Finish(); } -struct RamsesRenderGroupBinding::Traits { - using type = RamsesRenderGroupBinding; - static auto constexpr Create = CreateRamsesRenderGroupBinding; +struct RenderGroupBinding::Traits { + using type = RenderGroupBinding; + static auto constexpr Create = CreateRenderGroupBinding; }; -inline flatbuffers::Offset CreateRamsesRenderGroupBindingDirect( +inline flatbuffers::Offset CreateRenderGroupBindingDirect( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0, const std::vector> *elements = nullptr) { auto elements__ = elements ? _fbb.CreateVector>(*elements) : 0; - return rlogic_serialization::CreateRamsesRenderGroupBinding( + return rlogic_serialization::CreateRenderGroupBinding( _fbb, base, elements__); @@ -190,7 +190,7 @@ inline const flatbuffers::TypeTable *ElementTypeTable() { return &tt; } -inline const flatbuffers::TypeTable *RamsesRenderGroupBindingTypeTable() { +inline const flatbuffers::TypeTable *RenderGroupBindingTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_SEQUENCE, 0, 0 }, { flatbuffers::ET_SEQUENCE, 1, 1 } @@ -211,4 +211,4 @@ inline const flatbuffers::TypeTable *RamsesRenderGroupBindingTypeTable() { } // namespace rlogic_serialization -#endif // FLATBUFFERS_GENERATED_RAMSESRENDERGROUPBINDING_RLOGIC_SERIALIZATION_H_ +#endif // FLATBUFFERS_GENERATED_RENDERGROUPBINDING_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/RamsesRenderPassBindingGen.h b/src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h similarity index 55% rename from client/logic/lib/flatbuffers/generated/RamsesRenderPassBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h index 43151a07f..7564e7bd8 100644 --- a/client/logic/lib/flatbuffers/generated/RamsesRenderPassBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h @@ -1,8 +1,8 @@ // automatically generated by the FlatBuffers compiler, do not modify -#ifndef FLATBUFFERS_GENERATED_RAMSESRENDERPASSBINDING_RLOGIC_SERIALIZATION_H_ -#define FLATBUFFERS_GENERATED_RAMSESRENDERPASSBINDING_RLOGIC_SERIALIZATION_H_ +#ifndef FLATBUFFERS_GENERATED_RENDERPASSBINDING_RLOGIC_SERIALIZATION_H_ +#define FLATBUFFERS_GENERATED_RENDERPASSBINDING_RLOGIC_SERIALIZATION_H_ #include "flatbuffers/flatbuffers.h" @@ -13,16 +13,16 @@ namespace rlogic_serialization { -struct RamsesRenderPassBinding; -struct RamsesRenderPassBindingBuilder; +struct RenderPassBinding; +struct RenderPassBindingBuilder; -inline const flatbuffers::TypeTable *RamsesRenderPassBindingTypeTable(); +inline const flatbuffers::TypeTable *RenderPassBindingTypeTable(); -struct RamsesRenderPassBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { - typedef RamsesRenderPassBindingBuilder Builder; +struct RenderPassBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef RenderPassBindingBuilder Builder; struct Traits; static const flatbuffers::TypeTable *MiniReflectTypeTable() { - return RamsesRenderPassBindingTypeTable(); + return RenderPassBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_BASE = 4 @@ -38,39 +38,39 @@ struct RamsesRenderPassBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta } }; -struct RamsesRenderPassBindingBuilder { - typedef RamsesRenderPassBinding Table; +struct RenderPassBindingBuilder { + typedef RenderPassBinding Table; flatbuffers::FlatBufferBuilder &fbb_; flatbuffers::uoffset_t start_; void add_base(flatbuffers::Offset base) { - fbb_.AddOffset(RamsesRenderPassBinding::VT_BASE, base); + fbb_.AddOffset(RenderPassBinding::VT_BASE, base); } - explicit RamsesRenderPassBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit RenderPassBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesRenderPassBindingBuilder &operator=(const RamsesRenderPassBindingBuilder &); - flatbuffers::Offset Finish() { + RenderPassBindingBuilder &operator=(const RenderPassBindingBuilder &); + flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesRenderPassBinding( +inline flatbuffers::Offset CreateRenderPassBinding( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset base = 0) { - RamsesRenderPassBindingBuilder builder_(_fbb); + RenderPassBindingBuilder builder_(_fbb); builder_.add_base(base); return builder_.Finish(); } -struct RamsesRenderPassBinding::Traits { - using type = RamsesRenderPassBinding; - static auto constexpr Create = CreateRamsesRenderPassBinding; +struct RenderPassBinding::Traits { + using type = RenderPassBinding; + static auto constexpr Create = CreateRenderPassBinding; }; -inline const flatbuffers::TypeTable *RamsesRenderPassBindingTypeTable() { +inline const flatbuffers::TypeTable *RenderPassBindingTypeTable() { static const flatbuffers::TypeCode type_codes[] = { { flatbuffers::ET_SEQUENCE, 0, 0 } }; @@ -88,4 +88,4 @@ inline const flatbuffers::TypeTable *RamsesRenderPassBindingTypeTable() { } // namespace rlogic_serialization -#endif // FLATBUFFERS_GENERATED_RAMSESRENDERPASSBINDING_RLOGIC_SERIALIZATION_H_ +#endif // FLATBUFFERS_GENERATED_RENDERPASSBINDING_RLOGIC_SERIALIZATION_H_ diff --git a/client/logic/lib/flatbuffers/generated/SkinBindingGen.h b/src/client/internal/logic/flatbuffers/generated/SkinBindingGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/SkinBindingGen.h rename to src/client/internal/logic/flatbuffers/generated/SkinBindingGen.h diff --git a/client/logic/lib/flatbuffers/generated/TimerNodeGen.h b/src/client/internal/logic/flatbuffers/generated/TimerNodeGen.h similarity index 100% rename from client/logic/lib/flatbuffers/generated/TimerNodeGen.h rename to src/client/internal/logic/flatbuffers/generated/TimerNodeGen.h diff --git a/client/logic/lib/flatbuffers/schemas/AnchorPoint.fbs b/src/client/internal/logic/flatbuffers/schemas/AnchorPoint.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/AnchorPoint.fbs rename to src/client/internal/logic/flatbuffers/schemas/AnchorPoint.fbs diff --git a/client/logic/lib/flatbuffers/schemas/AnimationNode.fbs b/src/client/internal/logic/flatbuffers/schemas/AnimationNode.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/AnimationNode.fbs rename to src/client/internal/logic/flatbuffers/schemas/AnimationNode.fbs diff --git a/client/logic/lib/flatbuffers/schemas/ApiObjects.fbs b/src/client/internal/logic/flatbuffers/schemas/ApiObjects.fbs similarity index 65% rename from client/logic/lib/flatbuffers/schemas/ApiObjects.fbs rename to src/client/internal/logic/flatbuffers/schemas/ApiObjects.fbs index 812d53f10..6533eb1d0 100644 --- a/client/logic/lib/flatbuffers/schemas/ApiObjects.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/ApiObjects.fbs @@ -9,12 +9,12 @@ include "LuaModule.fbs"; include "LuaScript.fbs"; include "LuaInterface.fbs"; -include "RamsesNodeBinding.fbs"; -include "RamsesAppearanceBinding.fbs"; -include "RamsesCameraBinding.fbs"; -include "RamsesRenderPassBinding.fbs"; -include "RamsesRenderGroupBinding.fbs"; -include "RamsesMeshNodeBinding.fbs"; +include "NodeBinding.fbs"; +include "AppearanceBinding.fbs"; +include "CameraBinding.fbs"; +include "RenderPassBinding.fbs"; +include "RenderGroupBinding.fbs"; +include "MeshNodeBinding.fbs"; include "SkinBinding.fbs"; include "Link.fbs"; include "DataArray.fbs"; @@ -29,17 +29,16 @@ table ApiObjects luaModules:[LuaModule]; luaScripts:[LuaScript]; luaInterfaces:[LuaInterface]; - nodeBindings:[RamsesNodeBinding]; - appearanceBindings:[RamsesAppearanceBinding]; - cameraBindings:[RamsesCameraBinding]; + nodeBindings:[NodeBinding]; + appearanceBindings:[AppearanceBinding]; + cameraBindings:[CameraBinding]; dataArrays:[DataArray]; animationNodes:[AnimationNode]; timerNodes:[TimerNode]; links:[Link]; - lastObjectId:uint64 = 0; - renderPassBindings:[RamsesRenderPassBinding]; + renderPassBindings:[RenderPassBinding]; anchorPoints:[AnchorPoint]; - renderGroupBindings:[RamsesRenderGroupBinding]; + renderGroupBindings:[RenderGroupBinding]; skinBindings:[SkinBinding]; - meshNodeBindings:[RamsesMeshNodeBinding]; + meshNodeBindings:[MeshNodeBinding]; } diff --git a/client/logic/lib/flatbuffers/schemas/RamsesAppearanceBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/AppearanceBinding.fbs similarity index 95% rename from client/logic/lib/flatbuffers/schemas/RamsesAppearanceBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/AppearanceBinding.fbs index e6aecbedf..9c0382e99 100644 --- a/client/logic/lib/flatbuffers/schemas/RamsesAppearanceBinding.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/AppearanceBinding.fbs @@ -16,7 +16,7 @@ struct ResourceId resourceIdHigh:uint64 = 0; } -table RamsesAppearanceBinding +table AppearanceBinding { base:RamsesBinding; parentEffectId:ResourceId; diff --git a/client/logic/lib/flatbuffers/schemas/RamsesCameraBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/CameraBinding.fbs similarity index 95% rename from client/logic/lib/flatbuffers/schemas/RamsesCameraBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/CameraBinding.fbs index b1904b627..1559e8883 100644 --- a/client/logic/lib/flatbuffers/schemas/RamsesCameraBinding.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/CameraBinding.fbs @@ -10,7 +10,7 @@ include "RamsesBinding.fbs"; namespace rlogic_serialization; -table RamsesCameraBinding +table CameraBinding { base:RamsesBinding; } diff --git a/client/logic/lib/flatbuffers/schemas/DataArray.fbs b/src/client/internal/logic/flatbuffers/schemas/DataArray.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/DataArray.fbs rename to src/client/internal/logic/flatbuffers/schemas/DataArray.fbs diff --git a/client/logic/lib/flatbuffers/schemas/Link.fbs b/src/client/internal/logic/flatbuffers/schemas/Link.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/Link.fbs rename to src/client/internal/logic/flatbuffers/schemas/Link.fbs diff --git a/src/client/internal/logic/flatbuffers/schemas/LogicEngine.fbs b/src/client/internal/logic/flatbuffers/schemas/LogicEngine.fbs new file mode 100644 index 000000000..5e18cd631 --- /dev/null +++ b/src/client/internal/logic/flatbuffers/schemas/LogicEngine.fbs @@ -0,0 +1,19 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +include "ApiObjects.fbs"; + +namespace rlogic_serialization; + +table LogicEngine +{ + // Data objects + apiObjects:ApiObjects; +} + +root_type LogicEngine; diff --git a/client/logic/lib/flatbuffers/schemas/LogicObject.fbs b/src/client/internal/logic/flatbuffers/schemas/LogicObject.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/LogicObject.fbs rename to src/client/internal/logic/flatbuffers/schemas/LogicObject.fbs diff --git a/client/logic/lib/flatbuffers/schemas/LuaInterface.fbs b/src/client/internal/logic/flatbuffers/schemas/LuaInterface.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/LuaInterface.fbs rename to src/client/internal/logic/flatbuffers/schemas/LuaInterface.fbs diff --git a/client/logic/lib/flatbuffers/schemas/LuaModule.fbs b/src/client/internal/logic/flatbuffers/schemas/LuaModule.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/LuaModule.fbs rename to src/client/internal/logic/flatbuffers/schemas/LuaModule.fbs diff --git a/client/logic/lib/flatbuffers/schemas/LuaScript.fbs b/src/client/internal/logic/flatbuffers/schemas/LuaScript.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/LuaScript.fbs rename to src/client/internal/logic/flatbuffers/schemas/LuaScript.fbs diff --git a/client/logic/lib/flatbuffers/schemas/RamsesMeshNodeBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/MeshNodeBinding.fbs similarity index 95% rename from client/logic/lib/flatbuffers/schemas/RamsesMeshNodeBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/MeshNodeBinding.fbs index 9985c2655..60c08b2e1 100644 --- a/client/logic/lib/flatbuffers/schemas/RamsesMeshNodeBinding.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/MeshNodeBinding.fbs @@ -10,7 +10,7 @@ include "RamsesBinding.fbs"; namespace rlogic_serialization; -table RamsesMeshNodeBinding +table MeshNodeBinding { base:RamsesBinding; } diff --git a/client/logic/lib/flatbuffers/schemas/RamsesNodeBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/NodeBinding.fbs similarity index 96% rename from client/logic/lib/flatbuffers/schemas/RamsesNodeBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/NodeBinding.fbs index 8e34e43d1..108b0c6fc 100644 --- a/client/logic/lib/flatbuffers/schemas/RamsesNodeBinding.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/NodeBinding.fbs @@ -10,7 +10,7 @@ include "RamsesBinding.fbs"; namespace rlogic_serialization; -table RamsesNodeBinding +table NodeBinding { base:RamsesBinding; rotationType:uint8; diff --git a/client/logic/lib/flatbuffers/schemas/Property.fbs b/src/client/internal/logic/flatbuffers/schemas/Property.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/Property.fbs rename to src/client/internal/logic/flatbuffers/schemas/Property.fbs diff --git a/client/logic/lib/flatbuffers/schemas/RamsesBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/RamsesBinding.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/RamsesBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/RamsesBinding.fbs diff --git a/client/logic/lib/flatbuffers/schemas/RamsesReference.fbs b/src/client/internal/logic/flatbuffers/schemas/RamsesReference.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/RamsesReference.fbs rename to src/client/internal/logic/flatbuffers/schemas/RamsesReference.fbs diff --git a/client/logic/lib/flatbuffers/schemas/RamsesRenderGroupBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/RenderGroupBinding.fbs similarity index 95% rename from client/logic/lib/flatbuffers/schemas/RamsesRenderGroupBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/RenderGroupBinding.fbs index f7ee157ab..52be64f92 100644 --- a/client/logic/lib/flatbuffers/schemas/RamsesRenderGroupBinding.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/RenderGroupBinding.fbs @@ -16,7 +16,7 @@ table Element ramsesObject:RamsesReference; } -table RamsesRenderGroupBinding +table RenderGroupBinding { base:RamsesBinding; elements:[Element]; diff --git a/client/logic/lib/flatbuffers/schemas/RamsesRenderPassBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/RenderPassBinding.fbs similarity index 94% rename from client/logic/lib/flatbuffers/schemas/RamsesRenderPassBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/RenderPassBinding.fbs index 3e694c325..f0fdda4e2 100644 --- a/client/logic/lib/flatbuffers/schemas/RamsesRenderPassBinding.fbs +++ b/src/client/internal/logic/flatbuffers/schemas/RenderPassBinding.fbs @@ -10,7 +10,7 @@ include "RamsesBinding.fbs"; namespace rlogic_serialization; -table RamsesRenderPassBinding +table RenderPassBinding { base:RamsesBinding; } diff --git a/client/logic/lib/flatbuffers/schemas/SkinBinding.fbs b/src/client/internal/logic/flatbuffers/schemas/SkinBinding.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/SkinBinding.fbs rename to src/client/internal/logic/flatbuffers/schemas/SkinBinding.fbs diff --git a/client/logic/lib/flatbuffers/schemas/TimerNode.fbs b/src/client/internal/logic/flatbuffers/schemas/TimerNode.fbs similarity index 100% rename from client/logic/lib/flatbuffers/schemas/TimerNode.fbs rename to src/client/internal/logic/flatbuffers/schemas/TimerNode.fbs diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt new file mode 100644 index 000000000..86cc393d1 --- /dev/null +++ b/src/framework/CMakeLists.txt @@ -0,0 +1,119 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2016 BMW Car IT GmbH +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +if (ramses-sdk_ENABLE_TCP_SUPPORT) + list(APPEND FRAMEWORK_SOURCES internal/Communication/TransportTCP/*.h + internal/Communication/TransportTCP/*.cpp) + list(APPEND FRAMEWORK_LIBS asio) +endif() + +if(ramses-sdk_HAS_DLT) + list(APPEND FRAMEWORK_SOURCES internal/DltLogAppender/DltAdapterImpl/*.h + internal/DltLogAppender/DltAdapterImpl/*.cpp) + list(APPEND FRAMEWORK_LIBS automotive-dlt) +endif() + +find_package(AndroidSDK) +if(AndroidSDK_FOUND) + list(APPEND FRAMEWORK_SOURCES internal/Core/Utils/AndroidLogger/*.h + internal/Core/Utils/AndroidLogger/*.cpp) + list(APPEND FRAMEWORK_LIBS AndroidSDK) +endif() + +createModule( + NAME ramses-framework + TYPE STATIC_LIBRARY + ENABLE_INSTALL OFF + + INCLUDE_PATHS . + + # impl + SRC_FILES impl/*.h + impl/*.cpp + # internal + SRC_FILES ${FRAMEWORK_SOURCES} + SRC_FILES internal/Watchdog/*.h + internal/Watchdog/*.cpp + SRC_FILES internal/PlatformAbstraction/*.h + internal/PlatformAbstraction/Collections/*.h + internal/PlatformAbstraction/internal/*.h + internal/PlatformAbstraction/*.cpp + SRC_FILES internal/Core/Math3d/*.h + internal/Core/Math3d/*.cpp + SRC_FILES internal/Core/Utils/*.h + internal/Core/Utils/*.cpp + SRC_FILES internal/Core/Common/*.h + internal/Core/Common/*.cpp + SRC_FILES internal/Core/TaskFramework/*.h + internal/Core/TaskFramework/*.cpp + SRC_FILES internal/SceneGraph/SceneAPI/*.h + internal/SceneGraph/SceneAPI/*.cpp + SRC_FILES internal/SceneGraph/Scene/*.h + internal/SceneGraph/Scene/*.cpp + SRC_FILES internal/SceneGraph/Resource/*.h + internal/SceneGraph/Resource/*.cpp + SRC_FILES internal/SceneGraph/SceneUtils/*.h + internal/SceneGraph/SceneUtils/*.cpp + SRC_FILES internal/Communication/TransportCommon/*.h + internal/Communication/TransportCommon/*.cpp + SRC_FILES internal/Ramsh/*.h + internal/Ramsh/*.cpp + SRC_FILES internal/Components/*.h + internal/Components/*.cpp + SRC_FILES internal/DltLogAppender/*.h + internal/DltLogAppender/*.cpp + internal/DltLogAppender/src/*.cpp + SRC_FILES internal/SceneReferencing/*.h + internal/SceneReferencing/*.cpp + + DEPENDENCIES ramses-api + ramses-common-base + lz4 + fmt::fmt + ramses-abseil + lodepng + cityhash + ${FRAMEWORK_LIBS} + ) + +if (ramses-sdk_ENABLE_TCP_SUPPORT) + message(STATUS "+ TCP communication system support enabled") + target_compile_definitions(ramses-framework PUBLIC "-DHAS_TCP_COMM=1") +else() + message(STATUS "- TCP communication system support disabled") +endif() + +if (ramses-sdk_HAS_DLT) + target_compile_definitions(ramses-framework PUBLIC "-DDLT_ENABLED") + + if (automotive-dlt_HAS_FILETRANSFER) + target_compile_definitions(ramses-framework PUBLIC "-DDLT_HAS_FILETRANSFER") + endif() +endif() + +if(ramses-sdk_USE_LINUX_DEV_PTP) + target_compile_definitions(ramses-framework PUBLIC "-DRAMSES_LINUX_USE_DEV_PTP=1") +endif() + +# Thread priority and binding for worker threads +if (DEFINED ramses-sdk_WORKER_THREAD_PRIORITY AND NOT ramses-sdk_WORKER_THREAD_PRIORITY STREQUAL "") + target_compile_definitions(ramses-framework PRIVATE "-DRAMSES_WORKER_THREAD_PRIORITY=${ramses-sdk_WORKER_THREAD_PRIORITY}") +endif() + +if (DEFINED ramses-sdk_WORKER_THREAD_CORE_BINDING AND NOT ramses-sdk_WORKER_THREAD_CORE_BINDING STREQUAL "") + target_compile_definitions(ramses-framework PRIVATE "-DRAMSES_WORKER_THREAD_CORE_BINDING=${ramses-sdk_WORKER_THREAD_CORE_BINDING}") +endif() + +if (DEFINED ramses-sdk_CONN_KEEPALIVE_THREAD_CORE_BINDING AND NOT ramses-sdk_CONN_KEEPALIVE_THREAD_CORE_BINDING STREQUAL "") + target_compile_definitions(ramses-framework PRIVATE "-DRAMSES_CONN_KEEPALIVE_THREAD_CORE_BINDING=${ramses-sdk_CONN_KEEPALIVE_THREAD_CORE_BINDING}") +endif() + + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + target_compile_options(ramses-framework PRIVATE "-Wsuggest-override") +endif() diff --git a/framework/ramses-framework/include/APILoggingHelper.h b/src/framework/impl/APILoggingHelper.h similarity index 87% rename from framework/ramses-framework/include/APILoggingHelper.h rename to src/framework/impl/APILoggingHelper.h index 5926a7290..58edfffdb 100644 --- a/framework/ramses-framework/include/APILoggingHelper.h +++ b/src/framework/impl/APILoggingHelper.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_APILOGGINGHELPER_H -#define RAMSES_APILOGGINGHELPER_H +#pragma once -#include "Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" -namespace ramses_internal +namespace ramses::internal { class APILoggingHelper { @@ -29,4 +28,4 @@ namespace ramses_internal } }; } -#endif + diff --git a/framework/ramses-framework/include/APILoggingMacros.h b/src/framework/impl/APILoggingMacros.h similarity index 95% rename from framework/ramses-framework/include/APILoggingMacros.h rename to src/framework/impl/APILoggingMacros.h index 4bc59d9da..be73c5451 100644 --- a/framework/ramses-framework/include/APILoggingMacros.h +++ b/src/framework/impl/APILoggingMacros.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_APILOGGINGMACROS_H -#define RAMSES_APILOGGINGMACROS_H +#pragma once // API -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" //utils -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #define LOG_API_LOGLEVEL LOG_TRACE @@ -46,11 +45,11 @@ #define LOG_STATIC_API_BASE(retval) \ CLASS_FUNCTION_NAME << " ret " << retval #define LOG_API_ARGUMENTS(arguments) \ - " ( args: " << arguments << " )" + " ( args: " << arguments << " )" // NOLINT(bugprone-macro-parentheses) // Client side macros #define LOG_HL_CLIENT_API_STR(str) \ - LOG_API_LOGLEVEL(ramses_internal::CONTEXT_HLAPI_CLIENT, str) + LOG_API_LOGLEVEL(ramses::internal::CONTEXT_HLAPI_CLIENT, str) #define LOG_HL_CLIENT_API_NOARG(retval) \ LOG_HL_CLIENT_API_STR(LOG_API_BASE(retval)) @@ -90,7 +89,7 @@ // Renderer side macros #define LOG_HL_RENDERER_API_STR(str) \ - LOG_API_LOGLEVEL(ramses_internal::CONTEXT_HLAPI_RENDERER, str) + LOG_API_LOGLEVEL(ramses::internal::CONTEXT_HLAPI_RENDERER, str) #define LOG_HL_RENDERER_API_NOARG(retval) \ LOG_HL_RENDERER_API_STR(LOG_API_BASE(retval)) @@ -123,5 +122,3 @@ LOG_HL_RENDERER_STATIC_API3(retval, arg1, arg2, arg3 LOG_API_SEPERATOR arg4) #define LOG_HL_RENDERER_STATIC_API5(retval, arg1, arg2, arg3, arg4, arg5) \ LOG_HL_RENDERER_STATIC_API4(retval, arg1, arg2, arg3, arg4 LOG_API_SEPERATOR arg5) - -#endif diff --git a/framework/ramses-framework/src/AppearanceEnums.cpp b/src/framework/impl/AppearanceEnums.cpp similarity index 97% rename from framework/ramses-framework/src/AppearanceEnums.cpp rename to src/framework/impl/AppearanceEnums.cpp index b044038db..750287eff 100644 --- a/framework/ramses-framework/src/AppearanceEnums.cpp +++ b/src/framework/impl/AppearanceEnums.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "AppearanceEnumsImpl.h" +#include "impl/AppearanceEnumsImpl.h" namespace ramses { diff --git a/framework/ramses-framework/include/AppearanceEnumsImpl.h b/src/framework/impl/AppearanceEnumsImpl.h similarity index 71% rename from framework/ramses-framework/include/AppearanceEnumsImpl.h rename to src/framework/impl/AppearanceEnumsImpl.h index 40a73ef6a..c3692d045 100644 --- a/framework/ramses-framework/include/AppearanceEnumsImpl.h +++ b/src/framework/impl/AppearanceEnumsImpl.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-framework-api/AppearanceEnums.h" -#include "Utils/LoggingUtils.h" +#include "ramses/framework/AppearanceEnums.h" +#include "internal/Core/Utils/LoggingUtils.h" namespace ramses { @@ -111,13 +111,13 @@ namespace ramses "LineStrip", }; - ENUM_TO_STRING_NO_EXTRA_LAST(EBlendOperation, BlendOperationNames, EBlendOperation::Max); - ENUM_TO_STRING_NO_EXTRA_LAST(EBlendFactor, BlendFactorNames, EBlendFactor::AlphaSaturate); - ENUM_TO_STRING_NO_EXTRA_LAST(ECullMode, CullModeNames, ECullMode::FrontAndBackFacing); - ENUM_TO_STRING_NO_EXTRA_LAST(EDepthWrite, DepthWriteNames, EDepthWrite::Enabled); - ENUM_TO_STRING_NO_EXTRA_LAST(EScissorTest, ScissorTestNames, EScissorTest::Enabled); - ENUM_TO_STRING_NO_EXTRA_LAST(EDepthFunc, DepthFuncNames, EDepthFunc::Never); - ENUM_TO_STRING_NO_EXTRA_LAST(EStencilFunc, StencilFuncNames, EStencilFunc::GreaterEqual); - ENUM_TO_STRING_NO_EXTRA_LAST(EStencilOperation, StencilOperationNames, EStencilOperation::Invert); - ENUM_TO_STRING_NO_EXTRA_LAST(EDrawMode, DrawModeNames, EDrawMode::LineStrip); + ENUM_TO_STRING(EBlendOperation, BlendOperationNames, EBlendOperation::Max); + ENUM_TO_STRING(EBlendFactor, BlendFactorNames, EBlendFactor::AlphaSaturate); + ENUM_TO_STRING(ECullMode, CullModeNames, ECullMode::FrontAndBackFacing); + ENUM_TO_STRING(EDepthWrite, DepthWriteNames, EDepthWrite::Enabled); + ENUM_TO_STRING(EScissorTest, ScissorTestNames, EScissorTest::Enabled); + ENUM_TO_STRING(EDepthFunc, DepthFuncNames, EDepthFunc::Never); + ENUM_TO_STRING(EStencilFunc, StencilFuncNames, EStencilFunc::GreaterEqual); + ENUM_TO_STRING(EStencilOperation, StencilOperationNames, EStencilOperation::Invert); + ENUM_TO_STRING(EDrawMode, DrawModeNames, EDrawMode::LineStrip); } diff --git a/framework/ramses-framework/include/CommandT.h b/src/framework/impl/CommandT.h similarity index 90% rename from framework/ramses-framework/include/CommandT.h rename to src/framework/impl/CommandT.h index d850041fc..bc87e35f4 100644 --- a/framework/ramses-framework/include/CommandT.h +++ b/src/framework/impl/CommandT.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMANDT_H -#define RAMSES_COMMANDT_H +#pragma once #define DEFINE_COMMAND_TYPE(CommandClassType, CommandTypeValue) \ CommandClassType() : Command< typename CommandClassType::CommandTypeInfo >(CommandTypeValue) \ @@ -17,7 +16,7 @@ #include -namespace ramses_internal +namespace ramses::internal { template< typename COMMAND_TYPE_INFO > struct Command @@ -29,9 +28,7 @@ namespace ramses_internal { } - virtual ~Command() - { - } + virtual ~Command() = default; template [[nodiscard]] const COMMAND_TYPE& convertTo() const @@ -51,4 +48,3 @@ namespace ramses_internal }; } -#endif // RAMSES_COMMANDT_H diff --git a/src/framework/impl/DataTypeUtils.h b/src/framework/impl/DataTypeUtils.h new file mode 100644 index 000000000..13bc7e664 --- /dev/null +++ b/src/framework/impl/DataTypeUtils.h @@ -0,0 +1,174 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" +#include + +namespace ramses +{ + class DataTypeUtils + { + public: + static ramses::EDataType ConvertDataTypeFromInternal(ramses::internal::EDataType type) + { + switch (type) + { + case ramses::internal::EDataType::Bool: + return EDataType::Bool; + case ramses::internal::EDataType::Int32: + return EDataType::Int32; + case ramses::internal::EDataType::UInt16: + return EDataType::UInt16; + case ramses::internal::EDataType::UInt32: + return EDataType::UInt32; + case ramses::internal::EDataType::Float: + return EDataType::Float; + case ramses::internal::EDataType::Vector2F: + return EDataType::Vector2F; + case ramses::internal::EDataType::Vector3F: + return EDataType::Vector3F; + case ramses::internal::EDataType::Vector4F: + return EDataType::Vector4F; + case ramses::internal::EDataType::Vector2I: + return EDataType::Vector2I; + case ramses::internal::EDataType::Vector3I: + return EDataType::Vector3I; + case ramses::internal::EDataType::Vector4I: + return EDataType::Vector4I; + case ramses::internal::EDataType::Matrix22F: + return EDataType::Matrix22F; + case ramses::internal::EDataType::Matrix33F: + return EDataType::Matrix33F; + case ramses::internal::EDataType::Matrix44F: + return EDataType::Matrix44F; + case ramses::internal::EDataType::ByteBlob: + return EDataType::ByteBlob; + + // internal attribure array types are converted back to their element type on public API + case ramses::internal::EDataType::UInt16Buffer: + return EDataType::UInt16; + case ramses::internal::EDataType::FloatBuffer: + return EDataType::Float; + case ramses::internal::EDataType::Vector2Buffer: + return EDataType::Vector2F; + case ramses::internal::EDataType::Vector3Buffer: + return EDataType::Vector3F; + case ramses::internal::EDataType::Vector4Buffer: + return EDataType::Vector4F; + + case ramses::internal::EDataType::TextureSampler2D: + return EDataType::TextureSampler2D; + case ramses::internal::EDataType::TextureSampler2DMS: + return EDataType::TextureSampler2DMS; + case ramses::internal::EDataType::TextureSampler3D: + return EDataType::TextureSampler3D; + case ramses::internal::EDataType::TextureSamplerCube: + return EDataType::TextureSamplerCube; + case ramses::internal::EDataType::TextureSamplerExternal: + return EDataType::TextureSamplerExternal; + + default: + assert(false); + return EDataType::ByteBlob; + } + } + + static constexpr ramses::internal::EDataType ConvertDataTypeToInternal(EDataType type) + { + switch (type) + { + case EDataType::Bool: + return ramses::internal::EDataType::Bool; + case EDataType::Int32: + return ramses::internal::EDataType::Int32; + case EDataType::UInt16: + return ramses::internal::EDataType::UInt16; + case EDataType::UInt32: + return ramses::internal::EDataType::UInt32; + case EDataType::Float: + return ramses::internal::EDataType::Float; + case EDataType::Vector2F: + return ramses::internal::EDataType::Vector2F; + case EDataType::Vector3F: + return ramses::internal::EDataType::Vector3F; + case EDataType::Vector4F: + return ramses::internal::EDataType::Vector4F; + case EDataType::Vector2I: + return ramses::internal::EDataType::Vector2I; + case EDataType::Vector3I: + return ramses::internal::EDataType::Vector3I; + case EDataType::Vector4I: + return ramses::internal::EDataType::Vector4I; + case EDataType::Matrix22F: + return ramses::internal::EDataType::Matrix22F; + case EDataType::Matrix33F: + return ramses::internal::EDataType::Matrix33F; + case EDataType::Matrix44F: + return ramses::internal::EDataType::Matrix44F; + case EDataType::ByteBlob: + return ramses::internal::EDataType::ByteBlob; + case EDataType::TextureSampler2D: + return ramses::internal::EDataType::TextureSampler2D; + case EDataType::TextureSampler2DMS: + return ramses::internal::EDataType::TextureSampler2DMS; + case EDataType::TextureSampler3D: + return ramses::internal::EDataType::TextureSampler3D; + case EDataType::TextureSamplerCube: + return ramses::internal::EDataType::TextureSamplerCube; + case EDataType::TextureSamplerExternal: + return ramses::internal::EDataType::TextureSamplerExternal; + } + + assert(false); + return ramses::internal::EDataType::Invalid; + } + + static ramses::internal::EResourceType DeductResourceTypeFromDataType(EDataType type) + { + if (IsValidIndicesType(type)) + return ramses::internal::EResourceType::IndexArray; + if (IsValidVerticesType(type)) + return ramses::internal::EResourceType::VertexArray; + + assert(false); + return ramses::internal::EResourceType::Invalid; + } + + static ramses::internal::EDataBufferType DeductBufferTypeFromDataType(EDataType type) + { + if (IsValidIndicesType(type)) + return ramses::internal::EDataBufferType::IndexBuffer; + if (IsValidVerticesType(type)) + return ramses::internal::EDataBufferType::VertexBuffer; + + assert(false); + return ramses::internal::EDataBufferType::Invalid; + } + + static bool IsValidIndicesType(EDataType type) + { + return + type == EDataType::UInt16 || + type == EDataType::UInt32; + } + + static bool IsValidVerticesType(EDataType type) + { + return + type == EDataType::Float || + type == EDataType::Vector2F || + type == EDataType::Vector3F || + type == EDataType::Vector4F || + type == EDataType::ByteBlob; + } + }; +} diff --git a/framework/ramses-framework/include/DataTypesImpl.h b/src/framework/impl/DataTypesImpl.h similarity index 75% rename from framework/ramses-framework/include/DataTypesImpl.h rename to src/framework/impl/DataTypesImpl.h index fc99217f5..a43869050 100644 --- a/framework/ramses-framework/include/DataTypesImpl.h +++ b/src/framework/impl/DataTypesImpl.h @@ -9,10 +9,10 @@ #pragma once -#include "ramses-framework-api/DataTypes.h" -#include "PlatformAbstraction/FmtBase.h" +#include "ramses/framework/DataTypes.h" +#include "internal/PlatformAbstraction/FmtBase.h" -template <> struct fmt::formatter : public ramses_internal::SimpleFormatterBase +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const glm::vec4& m, FormatContext& ctx) { @@ -20,7 +20,7 @@ template <> struct fmt::formatter : public ramses_internal::SimpleFor } }; -template <> struct fmt::formatter : public ramses_internal::SimpleFormatterBase +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const glm::vec3& m, FormatContext& ctx) { @@ -28,7 +28,7 @@ template <> struct fmt::formatter : public ramses_internal::SimpleFor } }; -template <> struct fmt::formatter : public ramses_internal::SimpleFormatterBase +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const glm::vec2& m, FormatContext& ctx) { @@ -36,7 +36,7 @@ template <> struct fmt::formatter : public ramses_internal::SimpleFor } }; -template <> struct fmt::formatter : public ramses_internal::SimpleFormatterBase +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const glm::ivec4& m, FormatContext& ctx) { @@ -44,7 +44,7 @@ template <> struct fmt::formatter : public ramses_internal::SimpleFo } }; -template <> struct fmt::formatter : public ramses_internal::SimpleFormatterBase +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const glm::ivec3& m, FormatContext& ctx) { @@ -52,7 +52,7 @@ template <> struct fmt::formatter : public ramses_internal::SimpleFo } }; -template <> struct fmt::formatter : public ramses_internal::SimpleFormatterBase +template <> struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const glm::ivec2& m, FormatContext& ctx) { diff --git a/src/framework/impl/EFeatureLevelImpl.h b/src/framework/impl/EFeatureLevelImpl.h new file mode 100644 index 000000000..ae97303f4 --- /dev/null +++ b/src/framework/impl/EFeatureLevelImpl.h @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/EFeatureLevel.h" +#include "internal/Core/Utils/EnumTraits.h" + +namespace ramses +{ + [[nodiscard]] inline constexpr bool IsFeatureLevel(std::underlying_type_t featureLevel) + { +#if defined(_MSC_VER) && _MSC_VER < 1925 + // older versions of MSCV don't generate a proper __FUNCSIG__, if the enum contains duplicate symbols + // for the same numeric value (EFeatureLevel_Latest) +#else + static_assert (ramses::internal::EnumTraits::VerifyElementCountIfSupported(EFeatureLevel_Latest), + "EFeatureLevel_Latest must refer to the latest feature level"); +#endif + return (featureLevel >= 1) && (featureLevel <= EFeatureLevel_Latest); + } +} diff --git a/src/framework/impl/ErrorReporting.cpp b/src/framework/impl/ErrorReporting.cpp new file mode 100644 index 000000000..fe0fa160a --- /dev/null +++ b/src/framework/impl/ErrorReporting.cpp @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/ErrorReporting.h" +#include "impl/RamsesObjectImpl.h" +#include "ramses/framework/RamsesObject.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal +{ + void ErrorReporting::set(std::string errorMessage, const RamsesObject* object) + { + if (object) + { + LOG_ERROR_P(CONTEXT_CLIENT, "[{}] {}", object->getName(), errorMessage); + } + else + { + LOG_ERROR_P(CONTEXT_CLIENT, "{}", errorMessage); + } + + std::lock_guard g{ m_lock }; + m_error = Issue{ ramses::EIssueType::Error, std::move(errorMessage), object }; + } + + void ErrorReporting::set(std::string errorMessage, const RamsesObjectImpl& object) + { + set(std::move(errorMessage), &object.getRamsesObject()); + } + + void ErrorReporting::reset() + { + std::lock_guard g{ m_lock }; + m_error.reset(); + } + + std::optional ErrorReporting::getError() const + { + std::lock_guard g{ m_lock }; + return m_error; + } +} diff --git a/client/ramses-client/impl/IRamsesObjectRegistry.h b/src/framework/impl/ErrorReporting.h similarity index 50% rename from client/ramses-client/impl/IRamsesObjectRegistry.h rename to src/framework/impl/ErrorReporting.h index c27b95a87..0aed8fb51 100644 --- a/client/ramses-client/impl/IRamsesObjectRegistry.h +++ b/src/framework/impl/ErrorReporting.h @@ -1,30 +1,37 @@ // ------------------------------------------------------------------------- -// Copyright (C) 2015 BMW Car IT GmbH +// Copyright (C) 2020 BMW AG // ------------------------------------------------------------------------- // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRAMSESOBJECTREGISTRY_H -#define RAMSES_IRAMSESOBJECTREGISTRY_H +#pragma once -#include "ramses-client-api/RamsesObjectTypes.h" - -#include +#include "ramses/framework/Issue.h" +#include +#include namespace ramses { class RamsesObject; +} + +namespace ramses::internal +{ class RamsesObjectImpl; - class IRamsesObjectRegistry + class ErrorReporting { public: - virtual ~IRamsesObjectRegistry() {}; + void reset(); + void set(std::string errorMessage, const RamsesObject* object = nullptr); + void set(std::string errorMessage, const RamsesObjectImpl& object); + + [[nodiscard]] std::optional getError() const; - virtual void updateName(RamsesObject&, const std::string&) = 0; + private: + std::optional m_error; + mutable std::recursive_mutex m_lock; }; } - -#endif diff --git a/framework/ramses-framework/src/FrameworkFactoryRegistry.cpp b/src/framework/impl/FrameworkFactoryRegistry.cpp similarity index 78% rename from framework/ramses-framework/src/FrameworkFactoryRegistry.cpp rename to src/framework/impl/FrameworkFactoryRegistry.cpp index d6fba35fa..f9e5b209a 100644 --- a/framework/ramses-framework/src/FrameworkFactoryRegistry.cpp +++ b/src/framework/impl/FrameworkFactoryRegistry.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "FrameworkFactoryRegistry.h" -#include "Utils/LogMacros.h" +#include "impl/FrameworkFactoryRegistry.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses +namespace ramses::internal { FrameworkFactoryRegistry& FrameworkFactoryRegistry::GetInstance() { @@ -20,7 +20,7 @@ namespace ramses void FrameworkFactoryRegistry::registerClientFactory(std::unique_ptr factory) { if (m_clientFactory) - LOG_WARN(ramses_internal::CONTEXT_FRAMEWORK, "FrameworkFactoryRegistry::registerClientFactory called more than once"); + LOG_WARN(CONTEXT_FRAMEWORK, "FrameworkFactoryRegistry::registerClientFactory called more than once"); m_clientFactory = std::move(factory); } @@ -28,7 +28,7 @@ namespace ramses void FrameworkFactoryRegistry::registerRendererFactory(std::unique_ptr factory) { if (m_rendererFactory) - LOG_WARN(ramses_internal::CONTEXT_FRAMEWORK, "FrameworkFactoryRegistry::registerRendererFactory called more than once"); + LOG_WARN(CONTEXT_FRAMEWORK, "FrameworkFactoryRegistry::registerRendererFactory called more than once"); m_rendererFactory = std::move(factory); } diff --git a/framework/ramses-framework/include/FrameworkFactoryRegistry.h b/src/framework/impl/FrameworkFactoryRegistry.h similarity index 88% rename from framework/ramses-framework/include/FrameworkFactoryRegistry.h rename to src/framework/impl/FrameworkFactoryRegistry.h index e34a6ea1b..e432f3117 100644 --- a/framework/ramses-framework/include/FrameworkFactoryRegistry.h +++ b/src/framework/impl/FrameworkFactoryRegistry.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORKFACTORYREGISTRY_H -#define RAMSES_FRAMEWORKFACTORYREGISTRY_H +#pragma once -#include "RamsesObjectFactoryInterfaces.h" +#include "impl/RamsesObjectFactoryInterfaces.h" #include -namespace ramses +namespace ramses::internal { class FrameworkFactoryRegistry { @@ -35,4 +34,4 @@ namespace ramses std::unique_ptr m_rendererFactory; }; } -#endif + diff --git a/framework/ramses-framework/src/PublicRamshCommand.cpp b/src/framework/impl/PublicRamshCommand.cpp similarity index 92% rename from framework/ramses-framework/src/PublicRamshCommand.cpp rename to src/framework/impl/PublicRamshCommand.cpp index 511d632ca..03969240a 100644 --- a/framework/ramses-framework/src/PublicRamshCommand.cpp +++ b/src/framework/impl/PublicRamshCommand.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "PublicRamshCommand.h" -#include "ramses-framework-api/IRamshCommand.h" +#include "ramses/framework/IRamshCommand.h" #include -namespace ramses_internal +namespace ramses::internal { PublicRamshCommand::PublicRamshCommand(const std::shared_ptr& command) : m_command(command) diff --git a/framework/ramses-framework/include/PublicRamshCommand.h b/src/framework/impl/PublicRamshCommand.h similarity index 72% rename from framework/ramses-framework/include/PublicRamshCommand.h rename to src/framework/impl/PublicRamshCommand.h index 33afc97f3..5fed268e1 100644 --- a/framework/ramses-framework/include/PublicRamshCommand.h +++ b/src/framework/impl/PublicRamshCommand.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PUBLICRAMSHCOMMAND_H -#define RAMSES_PUBLICRAMSHCOMMAND_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" #include #include #include @@ -19,18 +18,16 @@ namespace ramses class IRamshCommand; } -namespace ramses_internal +namespace ramses::internal { class PublicRamshCommand : public RamshCommand { public: - explicit PublicRamshCommand(const std::shared_ptr& command); + explicit PublicRamshCommand(const std::shared_ptr& command); bool executeInput(const std::vector& input) override; private: - std::weak_ptr m_command; + std::weak_ptr m_command; }; } - -#endif diff --git a/src/framework/impl/RamsesFramework.cpp b/src/framework/impl/RamsesFramework.cpp new file mode 100644 index 000000000..72648e1e9 --- /dev/null +++ b/src/framework/impl/RamsesFramework.cpp @@ -0,0 +1,120 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/framework/RamsesFramework.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/APILoggingMacros.h" +#include "internal/PlatformAbstraction/PlatformTime.h" + +#include "APILoggingHelper.h" + +#include + +namespace ramses +{ + RamsesFramework::RamsesFramework(const RamsesFrameworkConfig& config) + : m_impl{ internal::RamsesFrameworkImpl::CreateImpl(config) } + { + m_impl->setHLFramework(*this); + LOG_HL_CLIENT_API1(LOG_API_VOID, LOG_API_GENERIC_OBJECT_STRING(config)); + } + + RamsesRenderer* RamsesFramework::createRenderer(const RendererConfig& config) + { + auto result = m_impl->createRenderer(config); + LOG_HL_CLIENT_API1(LOG_API_GENERIC_PTR_STRING(result), LOG_API_GENERIC_PTR_STRING(&config)); + return result; + } + + RamsesClient* RamsesFramework::createClient(std::string_view applicationName) + { + auto result = m_impl->createClient(applicationName); + LOG_HL_CLIENT_API1(LOG_API_GENERIC_PTR_STRING(result), applicationName); + return result; + } + + bool RamsesFramework::destroyRenderer(RamsesRenderer& renderer) + { + const bool result = m_impl->destroyRenderer(renderer); + LOG_HL_CLIENT_API1(result, LOG_API_GENERIC_OBJECT_STRING(renderer)); + return result; + } + + bool RamsesFramework::destroyClient(RamsesClient& client) + { + const bool result = m_impl->destroyClient(client); + LOG_HL_CLIENT_API1(result, LOG_API_GENERIC_OBJECT_STRING(client)); + return result; + } + + std::optional RamsesFramework::getLastError() + { + return m_impl->getLastError(); + } + + RamsesFramework::~RamsesFramework() + { + LOG_HL_CLIENT_API_NOARG(LOG_API_VOID); + } + + bool RamsesFramework::connect() + { + const bool result = m_impl->connect(); + LOG_HL_CLIENT_API_NOARG(result); + return result; + } + + bool RamsesFramework::isConnected() const + { + return m_impl->isConnected(); + } + + bool RamsesFramework::disconnect() + { + const bool result = m_impl->disconnect(); + LOG_HL_CLIENT_API_NOARG(result); + return result; + } + + EFeatureLevel RamsesFramework::getFeatureLevel() const + { + return m_impl->getFeatureLevel(); + } + + void RamsesFramework::SetLogHandler(const LogHandlerFunc& logHandlerFunc) + { + internal::RamsesFrameworkImpl::SetLogHandler(logHandlerFunc); + } + + uint64_t RamsesFramework::GetSynchronizedClockMilliseconds() + { + return ramses::internal::PlatformTime::GetMillisecondsSynchronized(); + } + + bool RamsesFramework::addRamshCommand(const std::shared_ptr& command) + { + const bool result = m_impl->addRamshCommand(command); + LOG_HL_CLIENT_API1(result, LOG_API_GENERIC_PTR_STRING(command.get())); + return result; + } + + bool RamsesFramework::executeRamshCommand(const std::string& input) + { + return m_impl->executeRamshCommand(input); + } + + internal::RamsesFrameworkImpl& RamsesFramework::impl() + { + return *m_impl; + } + + const internal::RamsesFrameworkImpl& RamsesFramework::impl() const + { + return *m_impl; + } +} diff --git a/src/framework/impl/RamsesFrameworkConfig.cpp b/src/framework/impl/RamsesFrameworkConfig.cpp new file mode 100644 index 000000000..a11e139b0 --- /dev/null +++ b/src/framework/impl/RamsesFrameworkConfig.cpp @@ -0,0 +1,160 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "impl/RamsesFrameworkConfigImpl.h" + +namespace ramses +{ + RamsesFrameworkConfig::RamsesFrameworkConfig(EFeatureLevel featureLevel) + : m_impl{ std::make_unique(featureLevel) } + { + } + + RamsesFrameworkConfig::~RamsesFrameworkConfig() = default; + + RamsesFrameworkConfig::RamsesFrameworkConfig(const RamsesFrameworkConfig& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + RamsesFrameworkConfig::RamsesFrameworkConfig(RamsesFrameworkConfig&& other) noexcept = default; + + RamsesFrameworkConfig& RamsesFrameworkConfig::operator=(const RamsesFrameworkConfig& other) + { + if (this != &other) + { + m_impl = std::make_unique(*other.m_impl); + } + return *this; + } + + RamsesFrameworkConfig& RamsesFrameworkConfig::operator=(RamsesFrameworkConfig&& other) noexcept = default; + + bool RamsesFrameworkConfig::setFeatureLevel(EFeatureLevel featureLevel) + { + return m_impl->setFeatureLevel(featureLevel); + } + + EFeatureLevel RamsesFrameworkConfig::getFeatureLevel() const + { + return m_impl->getFeatureLevel(); + } + + bool RamsesFrameworkConfig::setRequestedRamsesShellType(ERamsesShellType requestedShellType) + { + return m_impl->setRequestedRamsesShellType(requestedShellType); + } + + bool RamsesFrameworkConfig::setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval) + { + return m_impl->setWatchdogNotificationInterval(thread, interval); + } + + bool RamsesFrameworkConfig::setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback) + { + return m_impl->setWatchdogNotificationCallBack(callback); + } + + bool RamsesFrameworkConfig::disableDLTApplicationRegistration() + { + return m_impl->enableDLTApplicationRegistration(false); + } + + void RamsesFrameworkConfig::setDLTApplicationID(std::string_view id) + { + m_impl->setDLTApplicationID(id); + } + + std::string_view RamsesFrameworkConfig::getDLTApplicationID() const + { + return m_impl->getDLTApplicationID(); + } + + void RamsesFrameworkConfig::setDLTApplicationDescription(std::string_view description) + { + m_impl->setDLTApplicationDescription(description); + } + + std::string_view RamsesFrameworkConfig::getDLTApplicationDescription() const + { + return m_impl->getDLTApplicationDescription(); + } + + void RamsesFrameworkConfig::setLogLevel(ELogLevel logLevel) + { + m_impl->setLogLevel(logLevel); + } + + bool RamsesFrameworkConfig::setLogLevel(std::string_view context, ELogLevel logLevel) + { + return m_impl->setLogLevel(context, logLevel); + } + + void RamsesFrameworkConfig::setLogLevelConsole(ELogLevel logLevel) + { + m_impl->setLogLevelConsole(logLevel); + } + + void RamsesFrameworkConfig::setPeriodicLogInterval(std::chrono::seconds interval) + { + m_impl->setPeriodicLogInterval(interval); + } + + bool RamsesFrameworkConfig::setParticipantGuid(uint64_t guid) + { + return m_impl->setParticipantGuid(guid); + } + + bool RamsesFrameworkConfig::setParticipantName(std::string_view name) + { + return m_impl->setParticipantName(name); + } + + bool RamsesFrameworkConfig::setConnectionSystem(EConnectionSystem connectionSystem) + { + return m_impl->setConnectionSystem(connectionSystem); + } + + void RamsesFrameworkConfig::setInterfaceSelectionIPForTCPCommunication(std::string_view ip) + { + m_impl->m_tcpConfig.setIPAddress(ip); + } + + void RamsesFrameworkConfig::setInterfaceSelectionPortForTCPCommunication(uint16_t port) + { + m_impl->m_tcpConfig.setPort(port); + } + + void RamsesFrameworkConfig::setDaemonIPForTCPCommunication(std::string_view ip) + { + m_impl->m_tcpConfig.setDaemonIPAddress(ip); + } + + void RamsesFrameworkConfig::setDaemonPortForTCPCommunication(uint16_t port) + { + m_impl->m_tcpConfig.setDaemonPort(port); + } + + bool RamsesFrameworkConfig::setConnectionKeepaliveSettings(std::chrono::milliseconds interval, std::chrono::milliseconds timeout) + { + m_impl->m_tcpConfig.setAliveInterval(interval); + m_impl->m_tcpConfig.setAliveTimeout(timeout); + return true; + } + + internal::RamsesFrameworkConfigImpl& RamsesFrameworkConfig::impl() + { + return *m_impl; + } + + const internal::RamsesFrameworkConfigImpl& RamsesFrameworkConfig::impl() const + { + return *m_impl; + } +} diff --git a/framework/ramses-framework/src/RamsesFrameworkConfigImpl.cpp b/src/framework/impl/RamsesFrameworkConfigImpl.cpp similarity index 66% rename from framework/ramses-framework/src/RamsesFrameworkConfigImpl.cpp rename to src/framework/impl/RamsesFrameworkConfigImpl.cpp index 5d9046b47..883556124 100644 --- a/framework/ramses-framework/src/RamsesFrameworkConfigImpl.cpp +++ b/src/framework/impl/RamsesFrameworkConfigImpl.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RamsesFrameworkConfigImpl.h" -#include "Utils/LoggingUtils.h" -#include "Watchdog/PlatformWatchdog.h" -#include "TransportCommon/EConnectionProtocol.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/Watchdog/PlatformWatchdog.h" +#include "internal/Communication/TransportCommon/EConnectionProtocol.h" +#include "internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h" +#include "impl/EFeatureLevelImpl.h" #include -namespace ramses +namespace ramses::internal { - using namespace ramses_internal; - #if defined(HAS_TCP_COMM) static bool gHasTCPComm = true; #else @@ -24,28 +24,30 @@ namespace ramses #endif RamsesFrameworkConfigImpl::RamsesFrameworkConfigImpl(EFeatureLevel featureLevel) - : StatusObjectImpl() - , m_shellType(ERamsesShellType::Default) + : m_shellType(ERamsesShellType::Default) , m_periodicLogsEnabled(true) , m_usedProtocol(gHasTCPComm ? EConnectionProtocol::TCP : EConnectionProtocol::Off) { m_featureLevel = featureLevel; - if (std::find(ramses::AllFeatureLevels.cbegin(), ramses::AllFeatureLevels.cend(), m_featureLevel) == ramses::AllFeatureLevels.cend()) + if (!IsFeatureLevel(featureLevel)) { LOG_ERROR_P(CONTEXT_CLIENT, "Unrecognized feature level '0{}' provided, falling back to feature level 01", featureLevel); - m_featureLevel = ramses::EFeatureLevel_01; + m_featureLevel = EFeatureLevel_01; } } RamsesFrameworkConfigImpl::~RamsesFrameworkConfigImpl() = default; - status_t RamsesFrameworkConfigImpl::setFeatureLevel(EFeatureLevel featureLevel) + bool RamsesFrameworkConfigImpl::setFeatureLevel(EFeatureLevel featureLevel) { - if (std::find(ramses::AllFeatureLevels.cbegin(), ramses::AllFeatureLevels.cend(), featureLevel) == ramses::AllFeatureLevels.cend()) - return addErrorEntry(fmt::format("RamsesFrameworkConfig::setFeatureLevel: Failed to set unsupported feature level '{}'.", featureLevel)); + if (!IsFeatureLevel(featureLevel)) + { + LOG_ERROR_P(CONTEXT_CLIENT, "RamsesFrameworkConfig::setFeatureLevel: Failed to set unsupported feature level '{}'.", featureLevel); + return false; + } m_featureLevel = featureLevel; - return StatusOK; + return true; } EFeatureLevel RamsesFrameworkConfigImpl::getFeatureLevel() const @@ -53,6 +55,7 @@ namespace ramses return m_featureLevel; } + // NOLINTNEXTLINE(readability-convert-member-functions-to-static): design decision uint32_t RamsesFrameworkConfigImpl::getProtocolVersion() const { return RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR; @@ -78,39 +81,39 @@ namespace ramses return m_watchdogConfig.getCallBack(); } - status_t RamsesFrameworkConfigImpl::setRequestedRamsesShellType(ERamsesShellType shellType) + bool RamsesFrameworkConfigImpl::setRequestedRamsesShellType(ERamsesShellType shellType) { m_shellType = shellType; - return StatusOK; + return true; } - status_t RamsesFrameworkConfigImpl::setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval) + bool RamsesFrameworkConfigImpl::setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval) { if (0u == interval) { LOG_ERROR(CONTEXT_CLIENT, "Could not set watchdog notification interval, interval is not valid"); - return addErrorEntry("Could not set watchdog notification interval, interval is not valid"); + return false; } if (ERamsesThreadIdentifier::Unknown == thread) { LOG_ERROR(CONTEXT_CLIENT, "Could not set watchdog notification interval, thread identifier is not valid"); - return addErrorEntry("Could not set watchdog notification interval, thread identifier is not valid"); + return false; } m_watchdogConfig.setWatchdogNotificationInterval(thread, interval); - return StatusOK; + return true; } - status_t RamsesFrameworkConfigImpl::setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback) + bool RamsesFrameworkConfigImpl::setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback) { m_watchdogConfig.setThreadWatchDogCallback(callback); - return StatusOK; + return true; } - status_t RamsesFrameworkConfigImpl::enableDLTApplicationRegistration(bool state) + bool RamsesFrameworkConfigImpl::enableDLTApplicationRegistration(bool state) { m_enableDltApplicationRegistration = state; - return StatusOK; + return true; } bool RamsesFrameworkConfigImpl::getDltApplicationRegistrationEnabled() const @@ -143,10 +146,10 @@ namespace ramses loggerConfig.logLevel = logLevel; } - status_t RamsesFrameworkConfigImpl::setLogLevel(std::string_view context, ELogLevel logLevel) + bool RamsesFrameworkConfigImpl::setLogLevel(std::string_view context, ELogLevel logLevel) { loggerConfig.logLevelContexts[std::string{context}] = logLevel; - return StatusOK; + return true; } void RamsesFrameworkConfigImpl::setLogLevelConsole(ELogLevel logLevel) @@ -160,23 +163,24 @@ namespace ramses m_periodicLogsEnabled = (periodicLogTimeout > 0); } - status_t RamsesFrameworkConfigImpl::setParticipantGuid(uint64_t guid) + bool RamsesFrameworkConfigImpl::setParticipantGuid(uint64_t guid) { m_userProvidedGuid = Guid(guid); if (!m_userProvidedGuid.isValid() || guid < 256) { - return addErrorEntry(fmt::format("RamsesFrameworkConfig::setParticipantGuid: Failed to set invalid id '{}'.", m_userProvidedGuid)); + LOG_ERROR_P(CONTEXT_CLIENT, "RamsesFrameworkConfig::setParticipantGuid: Failed to set invalid id '{}'.", m_userProvidedGuid); + return false; } - return StatusOK; + return true; } - status_t RamsesFrameworkConfigImpl::setParticipantName(std::string_view name) + bool RamsesFrameworkConfigImpl::setParticipantName(std::string_view name) { m_participantName = name; - return StatusOK; + return true; } - status_t RamsesFrameworkConfigImpl::setConnectionSystem(EConnectionSystem connectionSystem) + bool RamsesFrameworkConfigImpl::setConnectionSystem(EConnectionSystem connectionSystem) { switch (connectionSystem) { @@ -187,10 +191,10 @@ namespace ramses m_usedProtocol = EConnectionProtocol::Off; break; } - return StatusOK; + return true; } - ramses_internal::Guid RamsesFrameworkConfigImpl::getUserProvidedGuid() const + Guid RamsesFrameworkConfigImpl::getUserProvidedGuid() const { return m_userProvidedGuid; } diff --git a/src/framework/impl/RamsesFrameworkConfigImpl.h b/src/framework/impl/RamsesFrameworkConfigImpl.h new file mode 100644 index 000000000..6484a2b62 --- /dev/null +++ b/src/framework/impl/RamsesFrameworkConfigImpl.h @@ -0,0 +1,83 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "TCPConfig.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "ramses/framework/IThreadWatchdogNotification.h" +#include "ramses/framework/EFeatureLevel.h" +#include "impl/ThreadWatchdogConfig.h" +#include "internal/Communication/TransportCommon/EConnectionProtocol.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" + +#include + +namespace ramses::internal +{ + class RamsesFrameworkConfigImpl + { + public: + explicit RamsesFrameworkConfigImpl(EFeatureLevel featureLevel); + ~RamsesFrameworkConfigImpl(); + + [[nodiscard]] bool setFeatureLevel(EFeatureLevel featureLevel); + [[nodiscard]] EFeatureLevel getFeatureLevel() const; + + [[nodiscard]] bool enableDLTApplicationRegistration(bool state); + [[nodiscard]] bool getDltApplicationRegistrationEnabled() const; + + void setDLTApplicationID(std::string_view id); + [[nodiscard]] std::string_view getDLTApplicationID() const; + + void setDLTApplicationDescription(std::string_view description); + [[nodiscard]] std::string_view getDLTApplicationDescription() const; + + [[nodiscard]] uint32_t getProtocolVersion() const; + + [[nodiscard]] bool setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval); + [[nodiscard]] bool setWatchdogNotificationCallBack(IThreadWatchdogNotification* callback); + + [[nodiscard]] bool setRequestedRamsesShellType(ERamsesShellType shellType); + + [[nodiscard]] EConnectionProtocol getUsedProtocol() const; + [[nodiscard]] uint32_t getWatchdogNotificationInterval(ERamsesThreadIdentifier thread) const; + [[nodiscard]] IThreadWatchdogNotification* getWatchdogNotificationCallback() const; + + void setLogLevel(ELogLevel logLevel); + [[nodiscard]] bool setLogLevel(std::string_view context, ELogLevel logLevel); + void setLogLevelConsole(ELogLevel logLevel); + + void setPeriodicLogInterval(std::chrono::seconds interval); + + [[nodiscard]] bool setParticipantGuid(uint64_t guid); + [[nodiscard]] Guid getUserProvidedGuid() const; + + [[nodiscard]] bool setParticipantName(std::string_view name); + [[nodiscard]] const std::string& getParticipantName() const; + + [[nodiscard]] bool setConnectionSystem(EConnectionSystem connectionSystem); + + TCPConfig m_tcpConfig; + ERamsesShellType m_shellType; + ThreadWatchdogConfig m_watchdogConfig; + bool m_periodicLogsEnabled; + + RamsesLoggerConfig loggerConfig; + uint32_t periodicLogTimeout = 2u; + + void setFeatureLevelNoCheck(EFeatureLevel featureLevel); + + private: + EFeatureLevel m_featureLevel = EFeatureLevel_01; + EConnectionProtocol m_usedProtocol; + std::string m_participantName; + bool m_enableDltApplicationRegistration = true; + Guid m_userProvidedGuid; + }; +} diff --git a/framework/ramses-framework/src/RamsesFrameworkImpl.cpp b/src/framework/impl/RamsesFrameworkImpl.cpp similarity index 66% rename from framework/ramses-framework/src/RamsesFrameworkImpl.cpp rename to src/framework/impl/RamsesFrameworkImpl.cpp index 8c9fde255..4ee2d4caf 100644 --- a/framework/ramses-framework/src/RamsesFrameworkImpl.cpp +++ b/src/framework/impl/RamsesFrameworkImpl.cpp @@ -6,31 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RamsesFrameworkImpl.h" -#include "Utils/LogMacros.h" -#include "Ramsh/RamshTools.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "impl/RamsesFrameworkImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Ramsh/RamshTools.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" #include "ramses-sdk-build-config.h" -#include "TransportCommon/CommunicationSystemFactory.h" -#include "TransportCommon/ICommunicationSystem.h" -#include "Utils/RamsesLogger.h" -#include "RamsesFrameworkConfigImpl.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "Ramsh/RamshStandardSetup.h" -#include "PlatformAbstraction/synchronized_clock.h" -#include "FrameworkFactoryRegistry.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/Communication/TransportCommon/CommunicationSystemFactory.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "internal/Ramsh/RamshStandardSetup.h" +#include "internal/PlatformAbstraction/synchronized_clock.h" +#include "impl/FrameworkFactoryRegistry.h" +#include "internal/PlatformAbstraction/PlatformTime.h" #include "PublicRamshCommand.h" -#include "ramses-framework-api/IRamshCommand.h" +#include "ramses/framework/IRamshCommand.h" #include -namespace ramses +namespace ramses::internal { - using namespace ramses_internal; - - RamsesFrameworkImpl::RamsesFrameworkImpl(const RamsesFrameworkConfigImpl& config, const ramses_internal::ParticipantIdentifier& participantAddress) - : StatusObjectImpl() - , m_ramsh(new RamshStandardSetup(config.m_shellType)) + RamsesFrameworkImpl::RamsesFrameworkImpl(const RamsesFrameworkConfigImpl& config, const ParticipantIdentifier& participantAddress) + : m_ramsh(new RamshStandardSetup(config.m_shellType)) , m_participantAddress(participantAddress) // NOTE: if you add something here consider using m_frameworkLock for all locking purposes inside this new class , m_connectionProtocol(config.getUsedProtocol()) @@ -48,10 +45,9 @@ namespace ramses m_resourceComponent, m_frameworkLock, config.getFeatureLevel()) - , m_ramshCommandLogConnectionInformation(std::make_shared(*m_communicationSystem)) + , m_ramshCommandLogConnectionInformation(std::make_shared(*m_communicationSystem)) , m_featureLevel{ config.getFeatureLevel() } - , m_ramsesClients() - , m_ramsesRenderer(nullptr, [](RamsesRenderer*) {}) + , m_ramsesRenderer(nullptr, [](RamsesRenderer* /*renderer*/) {}) { m_ramsh->start(); m_ramsh->add(m_ramshCommandLogConnectionInformation); @@ -92,7 +88,7 @@ namespace ramses return nullptr; } - LOG_INFO(ramses_internal::CONTEXT_FRAMEWORK, "RamsesFramework::createRamsesRenderer"); + LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFramework::createRamsesRenderer"); m_ramsesRenderer = FrameworkFactoryRegistry::GetInstance().getRendererFactory()->createRenderer(*this, config); return m_ramsesRenderer.get(); } @@ -110,52 +106,62 @@ namespace ramses return nullptr; } - LOG_INFO(ramses_internal::CONTEXT_FRAMEWORK, "RamsesFramework::createRamsesClient"); + LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFramework::createRamsesClient"); ClientUniquePtr client = FrameworkFactoryRegistry::GetInstance().getClientFactory()->createClient(*this, applicationName); auto clientPtr = client.get(); m_ramsesClients.emplace(std::make_pair(clientPtr, std::move(client))); return clientPtr; } - status_t RamsesFrameworkImpl::destroyRenderer(RamsesRenderer& renderer) + bool RamsesFrameworkImpl::destroyRenderer(RamsesRenderer& renderer) { if (!FrameworkFactoryRegistry::GetInstance().getRendererFactory()) - return addErrorEntry("RamsesFramework::destroyRenderer: renderer destruction failed because ramses was built without renderer support"); + { + m_errorReporting.set("RamsesFramework::destroyRenderer: renderer destruction failed because ramses was built without renderer support"); + return false; + } - LOG_INFO(ramses_internal::CONTEXT_FRAMEWORK, "RamsesFramework::destroyRenderer"); + LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFramework::destroyRenderer"); if (!m_ramsesRenderer || m_ramsesRenderer.get() != &renderer) { - return addErrorEntry("RamsesFramework::destroyRenderer: renderer does not belong to this framework"); + m_errorReporting.set("RamsesFramework::destroyRenderer: renderer does not belong to this framework"); + return false; } if (m_connected) { - return addErrorEntry("RamsesFramework::destroyRenderer: framework may not be connected on renderer destruction"); + m_errorReporting.set("RamsesFramework::destroyRenderer: framework may not be connected on renderer destruction"); + return false; } m_ramsesRenderer.reset(); - return StatusOK; + return true; } - status_t RamsesFrameworkImpl::destroyClient(RamsesClient& client) + bool RamsesFrameworkImpl::destroyClient(RamsesClient& client) { if (!FrameworkFactoryRegistry::GetInstance().getClientFactory()) - return addErrorEntry("RamsesFramework::destroyClient: client destruction failed because ramses was built without client support"); + { + m_errorReporting.set("RamsesFramework::destroyClient: client destruction failed because ramses was built without client support"); + return false; + } - LOG_INFO(ramses_internal::CONTEXT_FRAMEWORK, "RamsesFramework::destroyClient"); + LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFramework::destroyClient"); auto clientIt = m_ramsesClients.find(&client); if (clientIt == m_ramsesClients.end()) { - return addErrorEntry("RamsesFramework::destroyClient: client does not belong to this framework"); + m_errorReporting.set("RamsesFramework::destroyClient: client does not belong to this framework"); + return false; } if (m_connected) { - return addErrorEntry("RamsesFramework::destroyClient: framework may not be connected on client destruction"); + m_errorReporting.set("RamsesFramework::destroyClient: framework may not be connected on client destruction"); + return false; } m_ramsesClients.erase(clientIt); - return StatusOK; + return true; } EFeatureLevel RamsesFrameworkImpl::getFeatureLevel() const @@ -163,91 +169,129 @@ namespace ramses return m_featureLevel; } - ramses_internal::ResourceComponent& RamsesFrameworkImpl::getResourceComponent() + ErrorReporting& RamsesFrameworkImpl::getErrorReporting() + { + return m_errorReporting; + } + + std::optional RamsesFrameworkImpl::getLastError() + { + auto issue = m_errorReporting.getError(); + m_errorReporting.reset(); + return issue; + } + + const RamsesFramework& RamsesFrameworkImpl::getHLRamsesFramework() const + { + assert(m_hlFramework); + return *m_hlFramework; + } + + RamsesFramework& RamsesFrameworkImpl::getHLRamsesFramework() + { + assert(m_hlFramework); + return *m_hlFramework; + } + + ResourceComponent& RamsesFrameworkImpl::getResourceComponent() { return m_resourceComponent; } - ramses_internal::SceneGraphComponent& RamsesFrameworkImpl::getScenegraphComponent() + SceneGraphComponent& RamsesFrameworkImpl::getScenegraphComponent() { return m_scenegraphComponent; } - ramses_internal::ParticipantIdentifier RamsesFrameworkImpl::getParticipantAddress() const + ParticipantIdentifier RamsesFrameworkImpl::getParticipantAddress() const { return m_participantAddress; } - ramses_internal::Ramsh& RamsesFrameworkImpl::getRamsh() + Ramsh& RamsesFrameworkImpl::getRamsh() { return *m_ramsh; } - ramses_internal::PlatformLock& RamsesFrameworkImpl::getFrameworkLock() + PlatformLock& RamsesFrameworkImpl::getFrameworkLock() { return m_frameworkLock; } - const ramses_internal::ThreadWatchdogConfig& RamsesFrameworkImpl::getThreadWatchdogConfig() const + const ThreadWatchdogConfig& RamsesFrameworkImpl::getThreadWatchdogConfig() const { return m_threadWatchdogConfig; } - ramses_internal::ITaskQueue& RamsesFrameworkImpl::getTaskQueue() + ITaskQueue& RamsesFrameworkImpl::getTaskQueue() { return m_threadedTaskExecutor; } - ramses_internal::PeriodicLogger& RamsesFrameworkImpl::getPeriodicLogger() + PeriodicLogger& RamsesFrameworkImpl::getPeriodicLogger() { return m_periodicLogger; } - ramses_internal::StatisticCollectionFramework& RamsesFrameworkImpl::getStatisticCollection() + StatisticCollectionFramework& RamsesFrameworkImpl::getStatisticCollection() { return m_statisticCollection; } - status_t RamsesFrameworkImpl::addRamshCommand(const std::shared_ptr& command) + bool RamsesFrameworkImpl::addRamshCommand(const std::shared_ptr& command) { if (!command) - return addErrorEntry("addRamshCommand: command may not be null"); + { + m_errorReporting.set("addRamshCommand: command may not be null"); + return false; + } LOG_INFO_P(CONTEXT_FRAMEWORK, "RamsesFrameworkImpl::addRamshCommand: keyword '{}'", command->keyword()); - auto commandWrapper = std::make_shared(command); + auto commandWrapper = std::make_shared(command); if (!m_ramsh->add(commandWrapper, false)) - return addErrorEntry("addRamshCommand: command not valid"); + { + m_errorReporting.set("addRamshCommand: command not valid"); + return false; + } m_publicRamshCommands.push_back(commandWrapper); - return StatusOK; + return true; } - status_t RamsesFrameworkImpl::executeRamshCommand(const std::string& input) + bool RamsesFrameworkImpl::executeRamshCommand(const std::string& input) { if (input.empty()) - return addErrorEntry("executeRamshCommand: command may not be empty"); + { + m_errorReporting.set("executeRamshCommand: command may not be empty"); + return false; + } LOG_INFO_P(CONTEXT_FRAMEWORK, "RamsesFrameworkImpl::executeRamshCommand: '{}'", input); if (!m_ramsh->execute(RamshTools::parseCommandString(input))) - return addErrorEntry("executeRamshCommand: executing command failed"); - return StatusOK; + { + m_errorReporting.set("executeRamshCommand: executing command failed"); + return false; + } + return true; } - ramses::status_t RamsesFrameworkImpl::connect() + bool RamsesFrameworkImpl::connect() { LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFrameworkImpl::connect"); if (m_connected) { - return addErrorEntry("Already connected, cannot connect twice"); + m_errorReporting.set("Already connected, cannot connect twice"); + return false; } if (!m_communicationSystem->connectServices()) { - return addErrorEntry("Could not connect to daemon"); + m_errorReporting.set("Could not connect to daemon"); + return false; } m_scenegraphComponent.connectToNetwork(); m_connected = true; - return StatusOK; + return true; } bool RamsesFrameworkImpl::isConnected() const @@ -255,13 +299,14 @@ namespace ramses return m_connected; } - ramses::status_t RamsesFrameworkImpl::disconnect() + bool RamsesFrameworkImpl::disconnect() { LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFrameworkImpl::disconnect"); if (!m_connected) { - return addErrorEntry("Not connected, cannot disconnect"); + m_errorReporting.set("Not connected, cannot disconnect"); + return false; } m_scenegraphComponent.disconnectFromNetwork(); @@ -270,18 +315,23 @@ namespace ramses m_connected = false; LOG_INFO(CONTEXT_FRAMEWORK, "RamsesFrameworkImpl::disconnect: done ok"); - return StatusOK; + return true; + } + + void RamsesFrameworkImpl::setHLFramework(RamsesFramework& framework) + { + m_hlFramework = &framework; } std::unique_ptr RamsesFrameworkImpl::CreateImpl(const RamsesFrameworkConfig& config) { - ramses_internal::GetRamsesLogger().initialize(config.m_impl.get().loggerConfig, false, config.m_impl.get().getDltApplicationRegistrationEnabled()); + GetRamsesLogger().initialize(config.impl().loggerConfig, false, config.impl().getDltApplicationRegistrationEnabled()); - ramses_internal::Guid myGuid = config.m_impl.get().getUserProvidedGuid(); + Guid myGuid = config.impl().getUserProvidedGuid(); if (!myGuid.isValid()) { // check if user provided one - myGuid = config.m_impl.get().getUserProvidedGuid(); + myGuid = config.impl().getUserProvidedGuid(); // generate randomly when invalid or overlappping with reserved values (make sure generated ids do not collide with explicit guids) if (myGuid.isInvalid() || myGuid.get() <= 0xFF) @@ -292,10 +342,10 @@ namespace ramses myGuid = Guid(dis(gen)); } } - ramses_internal::ParticipantIdentifier participantAddress(myGuid, config.m_impl.get().getParticipantName()); + ParticipantIdentifier participantAddress(myGuid, config.impl().getParticipantName()); LOG_INFO(CONTEXT_FRAMEWORK, "Starting Ramses Client Application: " << participantAddress.getParticipantName() << " guid:" << participantAddress.getParticipantId() << - " stack: " << config.m_impl.get().getUsedProtocol()); + " stack: " << config.impl().getUsedProtocol()); LogEnvironmentVariableIfSet("XDG_RUNTIME_DIR"); LogEnvironmentVariableIfSet("LIBGL_DRIVERS_PATH"); @@ -307,11 +357,11 @@ namespace ramses LOG_INFO(CONTEXT_FRAMEWORK, "Ramses synchronized time is using " << synchronized_clock::source() << ". Currrent sync time " << asMicroseconds(currentSyncTime) << " us, system clock is " << systemClockTime << " us"); - std::unique_ptr impl{ new RamsesFrameworkImpl(config.m_impl, participantAddress) }; - if (config.m_impl.get().m_periodicLogsEnabled) + std::unique_ptr impl{ new RamsesFrameworkImpl(config.impl(), participantAddress) }; + if (config.impl().m_periodicLogsEnabled) { - LOG_INFO_P(CONTEXT_FRAMEWORK, "RamsesFramework: periodic logs enabled with period of {}s", config.m_impl.get().periodicLogTimeout); - impl->m_periodicLogger.startLogging(config.m_impl.get().periodicLogTimeout); + LOG_INFO_P(CONTEXT_FRAMEWORK, "RamsesFramework: periodic logs enabled with period of {}s", config.impl().periodicLogTimeout); + impl->m_periodicLogger.startLogging(config.impl().periodicLogTimeout); } else { @@ -330,7 +380,7 @@ namespace ramses { std::string envVarValue; // TODO(tobias) envVarValue.getLength should not be there because empty variable is also set. remove when fixed - if (ramses_internal::PlatformEnvironmentVariables::get(std::string{envVarName}, envVarValue) && envVarValue.size() != 0) + if (PlatformEnvironmentVariables::get(std::string{envVarName}, envVarValue) && !envVarValue.empty()) { LOG_INFO(CONTEXT_FRAMEWORK, "Environment variable set: " << envVarName << "=" << envVarValue); } @@ -339,7 +389,7 @@ namespace ramses void RamsesFrameworkImpl::LogAvailableCommunicationStacks() { // Create log function outside to work around broken MSVC macro in macro behavior - auto fun = [](ramses_internal::StringOutputStream& sos) { + auto fun = [](StringOutputStream& sos) { sos << "Available communication stacks:"; #if defined(HAS_TCP_COMM) sos << " TCP"; @@ -351,7 +401,7 @@ namespace ramses void RamsesFrameworkImpl::LogBuildInformation() { // Create log function outside to work around broken MSVC macro in macro behavior - auto fun = [](ramses_internal::StringOutputStream& sos) { + auto fun = [](StringOutputStream& sos) { sos << "RamsesBuildInfo: Version " << ramses_sdk::RAMSES_SDK_RAMSES_VERSION << ", Compiler " << ramses_sdk::RAMSES_SDK_CMAKE_CXX_COMPILER_ID << ", Config " << ramses_sdk::RAMSES_SDK_CMAKE_BUILD_TYPE diff --git a/src/framework/impl/RamsesFrameworkImpl.h b/src/framework/impl/RamsesFrameworkImpl.h new file mode 100644 index 000000000..8898fe6aa --- /dev/null +++ b/src/framework/impl/RamsesFrameworkImpl.h @@ -0,0 +1,115 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/TaskFramework/ThreadedTaskExecutor.h" +#include "internal/Components/ResourceComponent.h" +#include "internal/Components/SceneGraphComponent.h" +#include "internal/Core/Common/ParticipantIdentifier.h" +#include "internal/Core/Utils/PeriodicLogger.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Communication/TransportCommon/LogConnectionInfo.h" +#include "internal/Communication/TransportCommon/EConnectionProtocol.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/EFeatureLevel.h" +#include "ramses/framework/Issue.h" +#include "impl/RamsesObjectFactoryInterfaces.h" +#include "impl/ErrorReporting.h" + +#include +#include +#include +#include + +namespace ramses +{ + class RamsesFramework; + class RamsesFrameworkConfig; + class RamsesClient; + class RamsesRenderer; + class RendererConfig; + class IRamshCommand; +} + +namespace ramses::internal +{ + class ICommunicationSystem; + class RamshStandardSetup; + class Ramsh; + class PublicRamshCommand; + class RamsesFrameworkConfigImpl; + + class RamsesFrameworkImpl + { + public: + ~RamsesFrameworkImpl(); + + RamsesRenderer* createRenderer(const RendererConfig& config); + RamsesClient* createClient(std::string_view applicationName); + + bool destroyRenderer(RamsesRenderer& renderer); + bool destroyClient(RamsesClient& client); + + bool connect(); + bool isConnected() const; + bool disconnect(); + + const RamsesFramework& getHLRamsesFramework() const; + RamsesFramework& getHLRamsesFramework(); + EFeatureLevel getFeatureLevel() const; + ErrorReporting& getErrorReporting(); + [[nodiscard]] std::optional getLastError(); + + ResourceComponent& getResourceComponent(); + SceneGraphComponent& getScenegraphComponent(); + ParticipantIdentifier getParticipantAddress() const; + Ramsh& getRamsh(); + PlatformLock& getFrameworkLock(); + const ThreadWatchdogConfig& getThreadWatchdogConfig() const; + ITaskQueue& getTaskQueue(); + PeriodicLogger& getPeriodicLogger(); + StatisticCollectionFramework& getStatisticCollection(); + static void SetLogHandler(const LogHandlerFunc& logHandlerFunc); + bool addRamshCommand(const std::shared_ptr& command); + bool executeRamshCommand(const std::string& input); + + void setHLFramework(RamsesFramework& framework); + static std::unique_ptr CreateImpl(const RamsesFrameworkConfig& config); + + private: + RamsesFrameworkImpl(const RamsesFrameworkConfigImpl& config, const ParticipantIdentifier& participantAddress); + static void LogEnvironmentVariableIfSet(std::string_view envVarName); + static void LogAvailableCommunicationStacks(); + static void LogBuildInformation(); + + // the framework-wide mutex that is used by all framework-base classes to synchronize access to shared resource + // has to be used by all logic, component, etc classes + PlatformLock m_frameworkLock; + std::unique_ptr m_ramsh; + std::vector> m_publicRamshCommands; + StatisticCollectionFramework m_statisticCollection; + ParticipantIdentifier m_participantAddress; + EConnectionProtocol m_connectionProtocol; + std::unique_ptr m_communicationSystem; + PeriodicLogger m_periodicLogger; + bool m_connected; + const ThreadWatchdogConfig m_threadWatchdogConfig; + ThreadedTaskExecutor m_threadedTaskExecutor; + ResourceComponent m_resourceComponent; + SceneGraphComponent m_scenegraphComponent; + std::shared_ptr m_ramshCommandLogConnectionInformation; + + EFeatureLevel m_featureLevel; + ErrorReporting m_errorReporting; + + RamsesFramework* m_hlFramework = nullptr; + std::unordered_map m_ramsesClients; + RendererUniquePtr m_ramsesRenderer; + }; +} diff --git a/src/framework/impl/RamsesFrameworkTypesImpl.cpp b/src/framework/impl/RamsesFrameworkTypesImpl.cpp new file mode 100644 index 000000000..8349eb310 --- /dev/null +++ b/src/framework/impl/RamsesFrameworkTypesImpl.cpp @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RamsesFrameworkTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" + +// ensure internal and api types match +static_assert(std::is_same::value, "SceneID type mismatch"); +static_assert(ramses::sceneId_t::Invalid().getValue() == ramses::internal::SceneId::Invalid().getValue(), "SceneID default mismatch"); + +static_assert(std::is_same::value, "SceneVersionTag type mismatch"); +static_assert(ramses::InvalidSceneVersionTag == ramses::internal::SceneVersionTag::Invalid().getValue(), "SceneVersionTag default mismatch"); + +static_assert(std::is_same::value, "dataProviderId_t type mismatch"); +static_assert(ramses::dataProviderId_t::Invalid().getValue() == ramses::internal::DataSlotId::Invalid().getValue(), "dataProviderId_t invalid value mismatch"); + +static_assert(std::is_same::value, "dataConsumerId_t type mismatch"); +static_assert(ramses::dataConsumerId_t::Invalid().getValue() == ramses::internal::DataSlotId::Invalid().getValue(), "dataConsumerId_t invalid value mismatch"); + +static_assert(std::is_same::value, "NodeHandle type mismatch"); +static_assert(ramses::nodeId_t::Invalid().getValue() == ramses::internal::NodeHandle::Invalid().asMemoryHandle(), "NodeHandle default mismatch"); + +static_assert(std::is_same::value, "PickableObjectId type mismatch"); +static_assert(ramses::pickableObjectId_t::Invalid().getValue() == ramses::internal::PickableObjectId::Invalid().getValue(), "PickableObjectId default mismatch"); diff --git a/framework/ramses-framework/include/RamsesFrameworkTypesImpl.h b/src/framework/impl/RamsesFrameworkTypesImpl.h similarity index 65% rename from framework/ramses-framework/include/RamsesFrameworkTypesImpl.h rename to src/framework/impl/RamsesFrameworkTypesImpl.h index 0e66ed8c2..ab219f878 100644 --- a/framework/ramses-framework/include/RamsesFrameworkTypesImpl.h +++ b/src/framework/impl/RamsesFrameworkTypesImpl.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESFRAMEWORKTYPESIMPL_H -#define RAMSES_RAMSESFRAMEWORKTYPESIMPL_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "PlatformAbstraction/Hash.h" -#include "Collections/StringOutputStream.h" -#include "Common/StronglyTypedValue.h" -#include "PlatformAbstraction/FmtBase.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Common/StronglyTypedValue.h" +#include "internal/PlatformAbstraction/FmtBase.h" MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::sceneId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::dataConsumerId_t); @@ -21,15 +20,15 @@ MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::dataProviderId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::pickableObjectId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::displayId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::displayBufferId_t); -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::resourceCacheFlag_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::binaryShaderFormatId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::waylandIviSurfaceId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::waylandIviLayerId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::streamBufferId_t); MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::externalBufferId_t); +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::sceneObjectId_t); template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template constexpr auto format(const ramses::resourceId_t& res, FormatContext& ctx) @@ -38,6 +37,16 @@ struct fmt::formatter : public ramses_internal::SimpleForm } }; +template +struct fmt::formatter> : public ramses::internal::SimpleFormatterBase +{ + template + constexpr auto format(const ramses::Flags& value, FormatContext& ctx) + { + return fmt::format_to(ctx.out(), "{}", value.value()); + } +}; + namespace std { template<> @@ -45,9 +54,7 @@ namespace std { size_t operator()(const ::ramses::resourceId_t& rid) const { - return ramses_internal::HashValue(rid.lowPart, rid.highPart); + return ramses::internal::HashValue(rid.lowPart, rid.highPart); } }; } - -#endif diff --git a/src/framework/impl/RamsesObject.cpp b/src/framework/impl/RamsesObject.cpp new file mode 100644 index 000000000..c5937d165 --- /dev/null +++ b/src/framework/impl/RamsesObject.cpp @@ -0,0 +1,232 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/framework/RamsesObject.h" + +// internal +#include "impl/RamsesObjectImpl.h" + +#include "ramses/client/logic/LogicObject.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" + +#include "ramses/client/Effect.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Camera.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Camera.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Scene.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/SceneReference.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicObject.h" + + +namespace ramses +{ + RamsesObject::RamsesObject(std::unique_ptr impl) + : m_impl{ std::move(impl) } + { + m_impl->setRamsesObject(*this); + } + + RamsesObject::~RamsesObject() = default; + + std::string_view RamsesObject::getName() const + { + return m_impl->getName(); + } + + bool RamsesObject::setName(std::string_view name) + { + const auto status = m_impl->setName(name); + LOG_HL_CLIENT_API1(status, name); + return status; + } + + ERamsesObjectType RamsesObject::getType() const + { + return m_impl->getType(); + } + + bool RamsesObject::isOfType(ERamsesObjectType type) const + { + return m_impl->isOfType(type); + } + + bool RamsesObject::setUserId(uint64_t highId, uint64_t lowId) + { + return m_impl->setUserId(highId, lowId); + } + + std::pair RamsesObject::getUserId() const + { + return m_impl->getUserId(); + } + + void RamsesObject::validate(ValidationReport& report) const + { + return m_impl->validate(report.impl()); + } + + internal::RamsesObjectImpl& RamsesObject::impl() + { + return *m_impl; + } + + const internal::RamsesObjectImpl& RamsesObject::impl() const + { + return *m_impl; + } + + template + const T* RamsesObject::internalCast() const + { + return dynamic_cast(this); + } + + template + T* RamsesObject::internalCast() + { + return dynamic_cast(this); + } + + template RAMSES_API const LogicObject* RamsesObject::internalCast() const; + template RAMSES_API const LogicNode* RamsesObject::internalCast() const; + template RAMSES_API const RamsesBinding* RamsesObject::internalCast() const; + template RAMSES_API const LuaModule* RamsesObject::internalCast() const; + template RAMSES_API const LuaScript* RamsesObject::internalCast() const; + template RAMSES_API const LuaInterface* RamsesObject::internalCast() const; + template RAMSES_API const NodeBinding* RamsesObject::internalCast() const; + template RAMSES_API const AppearanceBinding* RamsesObject::internalCast() const; + template RAMSES_API const CameraBinding* RamsesObject::internalCast() const; + template RAMSES_API const RenderPassBinding* RamsesObject::internalCast() const; + template RAMSES_API const RenderGroupBinding* RamsesObject::internalCast() const; + template RAMSES_API const MeshNodeBinding* RamsesObject::internalCast() const; + template RAMSES_API const SkinBinding* RamsesObject::internalCast() const; + template RAMSES_API const DataArray* RamsesObject::internalCast() const; + template RAMSES_API const AnimationNode* RamsesObject::internalCast() const; + template RAMSES_API const TimerNode* RamsesObject::internalCast() const; + template RAMSES_API const AnchorPoint* RamsesObject::internalCast() const; + + template RAMSES_API LogicObject* RamsesObject::internalCast(); + template RAMSES_API LogicNode* RamsesObject::internalCast(); + template RAMSES_API RamsesBinding* RamsesObject::internalCast(); + template RAMSES_API LuaModule* RamsesObject::internalCast(); + template RAMSES_API LuaScript* RamsesObject::internalCast(); + template RAMSES_API LuaInterface* RamsesObject::internalCast(); + template RAMSES_API NodeBinding* RamsesObject::internalCast(); + template RAMSES_API AppearanceBinding* RamsesObject::internalCast(); + template RAMSES_API CameraBinding* RamsesObject::internalCast(); + template RAMSES_API RenderPassBinding* RamsesObject::internalCast(); + template RAMSES_API RenderGroupBinding* RamsesObject::internalCast(); + template RAMSES_API MeshNodeBinding* RamsesObject::internalCast(); + template RAMSES_API SkinBinding* RamsesObject::internalCast(); + template RAMSES_API DataArray* RamsesObject::internalCast(); + template RAMSES_API AnimationNode* RamsesObject::internalCast(); + template RAMSES_API TimerNode* RamsesObject::internalCast(); + template RAMSES_API AnchorPoint* RamsesObject::internalCast(); + + template RAMSES_API const ClientObject* RamsesObject::internalCast() const; + template RAMSES_API const RamsesObject* RamsesObject::internalCast() const; + template RAMSES_API const SceneObject* RamsesObject::internalCast() const; + template RAMSES_API const RamsesClient* RamsesObject::internalCast() const; + template RAMSES_API const Scene* RamsesObject::internalCast() const; + template RAMSES_API const LogicEngine* RamsesObject::internalCast() const; + template RAMSES_API const Node* RamsesObject::internalCast() const; + template RAMSES_API const MeshNode* RamsesObject::internalCast() const; + template RAMSES_API const Camera* RamsesObject::internalCast() const; + template RAMSES_API const PerspectiveCamera* RamsesObject::internalCast() const; + template RAMSES_API const OrthographicCamera* RamsesObject::internalCast() const; + template RAMSES_API const Effect* RamsesObject::internalCast() const; + template RAMSES_API const Appearance* RamsesObject::internalCast() const; + template RAMSES_API const Geometry* RamsesObject::internalCast() const; + template RAMSES_API const PickableObject* RamsesObject::internalCast() const; + template RAMSES_API const Resource* RamsesObject::internalCast() const; + template RAMSES_API const Texture2D* RamsesObject::internalCast() const; + template RAMSES_API const Texture3D* RamsesObject::internalCast() const; + template RAMSES_API const TextureCube* RamsesObject::internalCast() const; + template RAMSES_API const ArrayResource* RamsesObject::internalCast() const; + template RAMSES_API const RenderGroup* RamsesObject::internalCast() const; + template RAMSES_API const RenderPass* RamsesObject::internalCast() const; + template RAMSES_API const BlitPass* RamsesObject::internalCast() const; + template RAMSES_API const TextureSampler* RamsesObject::internalCast() const; + template RAMSES_API const TextureSamplerMS* RamsesObject::internalCast() const; + template RAMSES_API const TextureSamplerExternal* RamsesObject::internalCast() const; + template RAMSES_API const RenderBuffer* RamsesObject::internalCast() const; + template RAMSES_API const RenderTarget* RamsesObject::internalCast() const; + template RAMSES_API const DataObject* RamsesObject::internalCast() const; + template RAMSES_API const ArrayBuffer* RamsesObject::internalCast() const; + template RAMSES_API const Texture2DBuffer* RamsesObject::internalCast() const; + template RAMSES_API const SceneReference* RamsesObject::internalCast() const; + + template RAMSES_API ClientObject* RamsesObject::internalCast(); + template RAMSES_API RamsesObject* RamsesObject::internalCast(); + template RAMSES_API SceneObject* RamsesObject::internalCast(); + template RAMSES_API RamsesClient* RamsesObject::internalCast(); + template RAMSES_API Scene* RamsesObject::internalCast(); + template RAMSES_API LogicEngine* RamsesObject::internalCast(); + template RAMSES_API Node* RamsesObject::internalCast(); + template RAMSES_API MeshNode* RamsesObject::internalCast(); + template RAMSES_API Camera* RamsesObject::internalCast(); + template RAMSES_API PerspectiveCamera* RamsesObject::internalCast(); + template RAMSES_API OrthographicCamera* RamsesObject::internalCast(); + template RAMSES_API Effect* RamsesObject::internalCast(); + template RAMSES_API Appearance* RamsesObject::internalCast(); + template RAMSES_API Geometry* RamsesObject::internalCast(); + template RAMSES_API PickableObject* RamsesObject::internalCast(); + template RAMSES_API Resource* RamsesObject::internalCast(); + template RAMSES_API Texture2D* RamsesObject::internalCast(); + template RAMSES_API Texture3D* RamsesObject::internalCast(); + template RAMSES_API TextureCube* RamsesObject::internalCast(); + template RAMSES_API ArrayResource* RamsesObject::internalCast(); + template RAMSES_API RenderGroup* RamsesObject::internalCast(); + template RAMSES_API RenderPass* RamsesObject::internalCast(); + template RAMSES_API BlitPass* RamsesObject::internalCast(); + template RAMSES_API TextureSampler* RamsesObject::internalCast(); + template RAMSES_API TextureSamplerMS* RamsesObject::internalCast(); + template RAMSES_API TextureSamplerExternal* RamsesObject::internalCast(); + template RAMSES_API RenderBuffer* RamsesObject::internalCast(); + template RAMSES_API RenderTarget* RamsesObject::internalCast(); + template RAMSES_API DataObject* RamsesObject::internalCast(); + template RAMSES_API ArrayBuffer* RamsesObject::internalCast(); + template RAMSES_API Texture2DBuffer* RamsesObject::internalCast(); + template RAMSES_API SceneReference* RamsesObject::internalCast(); +} diff --git a/framework/ramses-framework/include/RamsesObjectFactoryInterfaces.h b/src/framework/impl/RamsesObjectFactoryInterfaces.h similarity index 74% rename from framework/ramses-framework/include/RamsesObjectFactoryInterfaces.h rename to src/framework/impl/RamsesObjectFactoryInterfaces.h index 6c4da96cc..013e638ca 100644 --- a/framework/ramses-framework/include/RamsesObjectFactoryInterfaces.h +++ b/src/framework/impl/RamsesObjectFactoryInterfaces.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESOBJECTFACTORYINTERFACES_H -#define RAMSES_RAMSESOBJECTFACTORYINTERFACES_H +#pragma once #include #include @@ -15,10 +14,14 @@ namespace ramses { + namespace internal + { + class RamsesFrameworkImpl; + } + class RamsesClient; class RamsesRenderer; class RendererConfig; - class RamsesFrameworkImpl; template using UniquePtrWithDeleter = std::unique_ptr>; @@ -30,7 +33,7 @@ namespace ramses public: virtual ~IClientFactory() = default; - virtual ClientUniquePtr createClient(RamsesFrameworkImpl& framework, std::string_view applicationName) const = 0; + virtual ClientUniquePtr createClient(internal::RamsesFrameworkImpl& framework, std::string_view applicationName) const = 0; }; class IRendererFactory @@ -38,7 +41,7 @@ namespace ramses public: virtual ~IRendererFactory() = default; - virtual RendererUniquePtr createRenderer(RamsesFrameworkImpl& framework, const RendererConfig& config) const = 0; + virtual RendererUniquePtr createRenderer(internal::RamsesFrameworkImpl& framework, const RendererConfig& config) const = 0; }; } -#endif + diff --git a/src/framework/impl/RamsesObjectImpl.cpp b/src/framework/impl/RamsesObjectImpl.cpp new file mode 100644 index 000000000..873430852 --- /dev/null +++ b/src/framework/impl/RamsesObjectImpl.cpp @@ -0,0 +1,121 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RamsesObjectImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SerializationContext.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" + +namespace ramses::internal +{ + RamsesObjectImpl::RamsesObjectImpl(ERamsesObjectType type, std::string_view name) + : m_type(type) + , m_name(name) + { + } + + RamsesObjectImpl::~RamsesObjectImpl() = default; + + ERamsesObjectType RamsesObjectImpl::getType() const + { + return m_type; + } + + bool RamsesObjectImpl::isOfType(ERamsesObjectType type) const + { + return RamsesObjectTypeUtils::IsTypeMatchingBaseType(m_type, type); + } + + const std::string& RamsesObjectImpl::getName() const + { + return m_name; + } + + bool RamsesObjectImpl::setName(std::string_view name) + { + m_name = name; + return true; + } + + bool RamsesObjectImpl::setUserId(uint64_t highId, uint64_t lowId) + { + m_userId = { highId, lowId }; + return true; + } + + std::pair RamsesObjectImpl::getUserId() const + { + return m_userId; + } + + std::string RamsesObjectImpl::getIdentificationString() const + { + if (m_userId.first != 0u || m_userId.second != 0u) + return fmt::format("{} [{} UserId={:016X}{:016X}]", getName(), RamsesObjectTypeUtils::GetRamsesObjectTypeName(m_type), m_userId.first, m_userId.second); + + return fmt::format("{} [{}]", getName(), RamsesObjectTypeUtils::GetRamsesObjectTypeName(m_type)); + } + + const RamsesObject& RamsesObjectImpl::getRamsesObject() const + { + assert(m_ramsesObject != nullptr); + return *m_ramsesObject; + } + + RamsesObject& RamsesObjectImpl::getRamsesObject() + { + // non-const version of getRamsesObject cast to its const version to avoid duplicating code + return const_cast((const_cast(*this)).getRamsesObject()); + } + + void RamsesObjectImpl::setRamsesObject(RamsesObject& ramsesObject) + { + assert(m_ramsesObject == nullptr); + m_ramsesObject = &ramsesObject; + } + + bool RamsesObjectImpl::serialize(IOutputStream& outStream, SerializationContext& serializationContext) const + { + outStream << serializationContext.getIDForObject(this); + outStream << m_name; + outStream << m_userId.first; + outStream << m_userId.second; + + return true; + } + + bool RamsesObjectImpl::deserialize(IInputStream& inStream, [[maybe_unused]] DeserializationContext& serializationContext) + { + inStream >> m_name; + inStream >> m_userId.first; + inStream >> m_userId.second; + + return true; + } + + bool RamsesObjectImpl::resolveDeserializationDependencies([[maybe_unused]] DeserializationContext& serializationContext) + { + return true; + } + + void RamsesObjectImpl::validate(ValidationReportImpl& report) const + { + // avoid double report + if (report.addVisit(this)) + { + onValidate(report); + // validate dependencies + for (auto* obj : report.getDependentObjects(this)) + { + obj->validate(report); + } + } + } +} diff --git a/src/framework/impl/RamsesObjectImpl.h b/src/framework/impl/RamsesObjectImpl.h new file mode 100644 index 000000000..6fdac5e8f --- /dev/null +++ b/src/framework/impl/RamsesObjectImpl.h @@ -0,0 +1,68 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesObjectTypes.h" +#include "ramses/framework/Issue.h" +#include "impl/APILoggingMacros.h" +#include "impl/ValidationReportImpl.h" + +#include +#include + +namespace ramses +{ + class RamsesObject; +} + +namespace ramses::internal +{ + class IOutputStream; + class IInputStream; + class SerializationContext; + class DeserializationContext; + + class RamsesObjectImpl + { + public: + explicit RamsesObjectImpl(ERamsesObjectType type, std::string_view name); + virtual ~RamsesObjectImpl(); + + [[nodiscard]] ERamsesObjectType getType() const; + [[nodiscard]] bool isOfType(ERamsesObjectType type) const; + [[nodiscard]] const std::string& getName() const; + [[nodiscard]] virtual bool setName(std::string_view name); + [[nodiscard]] const RamsesObject& getRamsesObject() const; + [[nodiscard]] RamsesObject& getRamsesObject(); + + bool setUserId(uint64_t highId, uint64_t lowId); + [[nodiscard]] std::pair getUserId() const; + [[nodiscard]] virtual std::string getIdentificationString() const; + + void setRamsesObject(RamsesObject& ramsesObject); + + virtual bool serialize(IOutputStream& outStream, SerializationContext& serializationContext) const; + virtual bool deserialize(IInputStream& inStream, DeserializationContext& serializationContext); + virtual bool resolveDeserializationDependencies(DeserializationContext& serializationContext); + + virtual void deinitializeFrameworkData() = 0; + + void validate(ValidationReportImpl& report) const; + + protected: + virtual void onValidate(ValidationReportImpl& /*report*/) const {}; + + private: + ERamsesObjectType m_type; + std::string m_name; + std::pair m_userId{ 0u, 0u }; + + RamsesObject* m_ramsesObject = nullptr; + }; +} diff --git a/client/ramses-client/impl/RamsesObjectTypeTraits.h b/src/framework/impl/RamsesObjectTypeTraits.h similarity index 86% rename from client/ramses-client/impl/RamsesObjectTypeTraits.h rename to src/framework/impl/RamsesObjectTypeTraits.h index a9728bd6e..397286725 100644 --- a/client/ramses-client/impl/RamsesObjectTypeTraits.h +++ b/src/framework/impl/RamsesObjectTypeTraits.h @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESOBJECTTYPETRAITS_H -#define RAMSES_RAMSESOBJECTTYPETRAITS_H +#pragma once -#include "ramses-client-api/RamsesObjectTypes.h" +#include "ramses/framework/RamsesObjectTypes.h" +#include "internal/Core/Utils/EnumTraits.h" #include namespace ramses @@ -29,12 +29,12 @@ namespace ramses struct CLASS_OF_RAMSES_OBJECT_TYPE; #define FORWARD_DECLARE_CLASS(_className) \ - class _className; + class _className; // NOLINT(bugprone-macro-parentheses) #define FORWARD_DECLARE_CLASS_NAMESPACE(_className, _namespace) \ namespace _namespace \ { \ - class _className; \ + class _className; /* NOLINT(bugprone-macro-parentheses) */ \ } #define DEFINE_TYPEID_OF_RAMSES_OBJECT(_className, _id) \ @@ -73,12 +73,14 @@ namespace ramses // - provide info whether type is concrete (can be instantiated) or a pure base type DEFINE_RAMSES_OBJECT_TRAITS(RamsesClient, ERamsesObjectType::Client, ERamsesObjectType::RamsesObject, true); DEFINE_RAMSES_OBJECT_TRAITS(Scene, ERamsesObjectType::Scene, ERamsesObjectType::ClientObject, true); + DEFINE_RAMSES_OBJECT_TRAITS(LogicEngine, ERamsesObjectType::LogicEngine, ERamsesObjectType::SceneObject, true); + DEFINE_RAMSES_OBJECT_TRAITS(LogicObject, ERamsesObjectType::LogicObject, ERamsesObjectType::SceneObject, false); DEFINE_RAMSES_OBJECT_TRAITS(MeshNode, ERamsesObjectType::MeshNode, ERamsesObjectType::Node, true); DEFINE_RAMSES_OBJECT_TRAITS(PerspectiveCamera, ERamsesObjectType::PerspectiveCamera, ERamsesObjectType::Camera, true); DEFINE_RAMSES_OBJECT_TRAITS(OrthographicCamera, ERamsesObjectType::OrthographicCamera, ERamsesObjectType::Camera, true); DEFINE_RAMSES_OBJECT_TRAITS(Effect, ERamsesObjectType::Effect, ERamsesObjectType::Resource, true); DEFINE_RAMSES_OBJECT_TRAITS(Appearance, ERamsesObjectType::Appearance, ERamsesObjectType::SceneObject, true); - DEFINE_RAMSES_OBJECT_TRAITS(GeometryBinding, ERamsesObjectType::GeometryBinding, ERamsesObjectType::SceneObject, true); + DEFINE_RAMSES_OBJECT_TRAITS(Geometry, ERamsesObjectType::Geometry, ERamsesObjectType::SceneObject, true); DEFINE_RAMSES_OBJECT_TRAITS(PickableObject, ERamsesObjectType::PickableObject, ERamsesObjectType::Node, true); DEFINE_RAMSES_OBJECT_TRAITS(Texture2D, ERamsesObjectType::Texture2D, ERamsesObjectType::Resource, true); DEFINE_RAMSES_OBJECT_TRAITS(Texture3D, ERamsesObjectType::Texture3D, ERamsesObjectType::Resource, true); @@ -98,7 +100,7 @@ namespace ramses DEFINE_RAMSES_OBJECT_TRAITS(Camera, ERamsesObjectType::Camera, ERamsesObjectType::Node, false); DEFINE_RAMSES_OBJECT_TRAITS(Resource, ERamsesObjectType::Resource, ERamsesObjectType::SceneObject, false); DEFINE_RAMSES_OBJECT_TRAITS(DataObject, ERamsesObjectType::DataObject, ERamsesObjectType::SceneObject, true); - DEFINE_RAMSES_OBJECT_TRAITS(ArrayBuffer, ERamsesObjectType::ArrayBufferObject, ERamsesObjectType::SceneObject, true); + DEFINE_RAMSES_OBJECT_TRAITS(ArrayBuffer, ERamsesObjectType::ArrayBuffer, ERamsesObjectType::SceneObject, true); DEFINE_RAMSES_OBJECT_TRAITS(Texture2DBuffer, ERamsesObjectType::Texture2DBuffer, ERamsesObjectType::SceneObject, true); DEFINE_RAMSES_OBJECT_TRAITS(SceneReference, ERamsesObjectType::SceneReference, ERamsesObjectType::SceneObject, true); DEFINE_RAMSES_OBJECT_TRAITS(TextureSamplerExternal, ERamsesObjectType::TextureSamplerExternal, ERamsesObjectType::SceneObject, true); @@ -111,7 +113,7 @@ namespace ramses }; #define DEFINE_RAMSES_OBJECT_TRAITS_LIST_BEGIN() \ - const std::array RamsesObjectTraits = \ + constexpr std::array RamsesObjectTraits = \ { \ RamsesObjectTraitsEntry{ ERamsesObjectType::Invalid, ERamsesObjectType::Invalid, false }, #define DEFINE_RAMSES_OBJECT_TRAITS_LIST(_typeId) \ @@ -129,6 +131,8 @@ namespace ramses DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::SceneObject) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Client) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Scene) + DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::LogicEngine) + DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::LogicObject) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Node) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::MeshNode) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Camera) @@ -136,7 +140,7 @@ namespace ramses DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::OrthographicCamera) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Effect) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Appearance) - DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::GeometryBinding) + DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Geometry) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::PickableObject) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Resource) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Texture2D) @@ -150,14 +154,19 @@ namespace ramses DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::TextureSamplerMS) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::RenderBuffer) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::RenderTarget) - DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::ArrayBufferObject) + DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::ArrayBuffer) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::Texture2DBuffer) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::DataObject) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::SceneReference) DEFINE_RAMSES_OBJECT_TRAITS_LIST(ERamsesObjectType::TextureSamplerExternal) DEFINE_RAMSES_OBJECT_TRAITS_LIST_END() - static_assert(static_cast(ERamsesObjectType::NUMBER_OF_TYPES) == RamsesObjectTraits.size(), "Every RamsesObject type must register its traits!"); -} + const size_t RamsesObjectTypeCount = static_cast(ERamsesObjectType::TextureSamplerExternal) + 1u; -#endif + // Whenever new type of object is added + // its traits must be registered in RamsesObjectTypeTraits.h using helper macros + // and added to appropriate test type list(s) in RamsesObjectTestTypes.h + // and added a conversion template instantiation in RamsesObjectTypeUtils.cpp + static_assert(RamsesObjectTypeCount == RamsesObjectTraits.size(), "Every RamsesObject type must register its traits!"); + static_assert(ramses::internal::EnumTraits::VerifyElementCountIfSupported(RamsesObjectTypeCount)); +} diff --git a/src/framework/impl/RamsesObjectTypeUtils.cpp b/src/framework/impl/RamsesObjectTypeUtils.cpp new file mode 100644 index 000000000..c359b995f --- /dev/null +++ b/src/framework/impl/RamsesObjectTypeUtils.cpp @@ -0,0 +1,58 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RamsesObjectTypeUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" + +namespace ramses::internal +{ + const std::array RamsesObjectTypeNames = + { + "Invalid", + "ClientObject", + "RamsesObject", + "SceneObject", + "Client", + "Scene", + "LogicEngine", + "LogicObject", + "Node", + "MeshNode", + "Camera", + "PerspectiveCamera", + "OrthographicCamera", + "Effect", + "Appearance", + "Geometry", + "PickableObject", + "Resource", + "Texture2D", + "Texture3D", + "TextureCube", + "ArrayResource", + "RenderGroup", + "RenderPass", + "BlitPass", + "TextureSampler", + "TextureSamplerMS", + "RenderBuffer", + "RenderTarget", + "ArrayBuffer", + "Texture2DBuffer", + "DataObject", + "SceneReference", + "TextureSamplerExternal" + }; + + ENUM_TO_STRING(ERamsesObjectType, RamsesObjectTypeNames, ERamsesObjectType::TextureSamplerExternal); + + const char* RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType type) + { + return EnumToString(type); + } +} diff --git a/src/framework/impl/RamsesObjectTypeUtils.h b/src/framework/impl/RamsesObjectTypeUtils.h new file mode 100644 index 000000000..3d65aaee7 --- /dev/null +++ b/src/framework/impl/RamsesObjectTypeUtils.h @@ -0,0 +1,60 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesObjectTypes.h" +#include "ramses/framework/RamsesObject.h" +#include "impl/RamsesObjectTypeTraits.h" +#include + +namespace ramses::internal +{ + class RamsesObjectTypeUtils final + { + public: + static const char* GetRamsesObjectTypeName(ERamsesObjectType type); + + static constexpr bool IsTypeMatchingBaseType(ERamsesObjectType type, ERamsesObjectType baseType) + { + while (type != ERamsesObjectType::Invalid) + { + if (type == baseType) + { + return true; + } + const auto index = static_cast(type); + assert(RamsesObjectTraits[index].typeID == type && "Wrong order of RamsesObject traits!"); + type = RamsesObjectTraits[index].baseClassTypeID; + } + + return false; + } + + static constexpr bool IsConcreteType(ERamsesObjectType type) + { + const auto index = static_cast(type); + assert(RamsesObjectTraits[index].typeID == type && "Wrong order of RamsesObject traits!"); + return RamsesObjectTraits[index].isConcreteType; + } + + template + static const T& ConvertTo(const RamsesObject& obj) + { + assert(IsTypeMatchingBaseType(obj.getType(), TYPE_ID_OF_RAMSES_OBJECT::ID)); + return static_cast(obj); + } + + template + static T& ConvertTo(RamsesObject& obj) + { + assert(IsTypeMatchingBaseType(obj.getType(), TYPE_ID_OF_RAMSES_OBJECT::ID)); + return static_cast(obj); + } + }; +} diff --git a/framework/ramses-framework/src/RamsesVersion.cpp b/src/framework/impl/RamsesVersion.cpp similarity index 94% rename from framework/ramses-framework/src/RamsesVersion.cpp rename to src/framework/impl/RamsesVersion.cpp index e09323a2d..fcb2cddf7 100644 --- a/framework/ramses-framework/src/RamsesVersion.cpp +++ b/src/framework/impl/RamsesVersion.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-framework-api/RamsesVersion.h" +#include "ramses/framework/RamsesVersion.h" #include "ramses-sdk-build-config.h" namespace ramses diff --git a/client/ramses-client/impl/SerializationContext.cpp b/src/framework/impl/SerializationContext.cpp similarity index 72% rename from client/ramses-client/impl/SerializationContext.cpp rename to src/framework/impl/SerializationContext.cpp index 7ba4c948b..69d7aba05 100644 --- a/client/ramses-client/impl/SerializationContext.cpp +++ b/src/framework/impl/SerializationContext.cpp @@ -6,23 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SerializationContext.h" +#include "impl/SerializationContext.h" #include "RamsesObjectImpl.h" -#include "ramses-client-api/RamsesObject.h" +#include "ramses/framework/RamsesObject.h" -namespace ramses +namespace ramses::internal { - SerializationContext::SerializationContext() + SerializationContext::SerializationContext(const SaveFileConfigImpl& saveConfig) : m_lastID(GetObjectIDNull() + 1u) + , m_saveConfig(saveConfig) { } + const SaveFileConfigImpl& SerializationContext::getSaveConfig() const + { + return m_saveConfig; + } + ObjectIDType DeserializationContext::GetObjectIDNull() { return ObjectIDType(0u); } - DeserializationContext::DeserializationContext() + DeserializationContext::DeserializationContext(const SceneConfigImpl& loadConfig) + : m_loadConfig(loadConfig) { } @@ -70,17 +77,18 @@ namespace ramses m_dependingObjects.put(obj); } - status_t DeserializationContext::resolveDependencies() + bool DeserializationContext::resolveDependencies() { for (auto obj : m_dependingObjects) { - CHECK_RETURN_ERR(obj->resolveDeserializationDependencies(*this)); + if (!obj->resolveDeserializationDependencies(*this)) + return false; } - return StatusOK; + return true; } - void DeserializationContext::addNodeHandleToNodeImplMapping(ramses_internal::NodeHandle nodeHandle, NodeImpl* node) + void DeserializationContext::addNodeHandleToNodeImplMapping(NodeHandle nodeHandle, NodeImpl* node) { assert(nodeHandle < m_nodeMap.size()); m_nodeMap[nodeHandle.asMemoryHandle()] = node; @@ -93,9 +101,14 @@ namespace ramses m_dependingObjects.reserve(totalObjects); } - NodeImpl* DeserializationContext::getNodeImplForHandle(ramses_internal::NodeHandle nodeHandle) const + NodeImpl* DeserializationContext::getNodeImplForHandle(NodeHandle nodeHandle) const { assert(nodeHandle.asMemoryHandle() < m_nodeMap.size()); return m_nodeMap[nodeHandle.asMemoryHandle()]; } + + const SceneConfigImpl& DeserializationContext::getLoadConfig() const + { + return m_loadConfig; + } } diff --git a/client/ramses-client/impl/SerializationContext.h b/src/framework/impl/SerializationContext.h similarity index 63% rename from client/ramses-client/impl/SerializationContext.h rename to src/framework/impl/SerializationContext.h index ad591d40d..67b394d2f 100644 --- a/client/ramses-client/impl/SerializationContext.h +++ b/src/framework/impl/SerializationContext.h @@ -6,61 +6,65 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SERIALIZATIONCONTEXT_H -#define RAMSES_SERIALIZATIONCONTEXT_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "Collections/HashMap.h" -#include "Collections/HashSet.h" -#include "Collections/IInputStream.h" -#include "Components/ManagedResource.h" -#include "SceneAPI/Handles.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/Components/ManagedResource.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" -namespace ramses +namespace ramses::internal { class RamsesObjectImpl; class NodeImpl; + class SaveFileConfigImpl; + class SceneConfigImpl; using ObjectIDType = uint32_t; class DeserializationContext { - using RamsesObjectImplSet = ramses_internal::HashSet; + using RamsesObjectImplSet = HashSet; public: - explicit DeserializationContext(); + explicit DeserializationContext(const SceneConfigImpl& loadConfig); void resize(uint32_t totalObjects, uint32_t nodeCount); static ObjectIDType GetObjectIDNull(); // phase 1: deserialize void registerObjectImpl(RamsesObjectImpl* obj, ObjectIDType id); - void addNodeHandleToNodeImplMapping(ramses_internal::NodeHandle nodeHandle, NodeImpl* node); + void addNodeHandleToNodeImplMapping(NodeHandle nodeHandle, NodeImpl* node); template - static void ReadDependentPointerAndStoreAsID(ramses_internal::IInputStream& inStream, PTR_TYPE*& ptr); + static void ReadDependentPointerAndStoreAsID(IInputStream& inStream, PTR_TYPE*& ptr); void addForDependencyResolve(RamsesObjectImpl* obj); // phase 2: resolve dependencies - status_t resolveDependencies(); + bool resolveDependencies(); template void resolveDependencyIDImplAndStoreAsPointer(OBJECT_TYPE*& ptrId) const; - [[nodiscard]] NodeImpl* getNodeImplForHandle(ramses_internal::NodeHandle) const; + [[nodiscard]] NodeImpl* getNodeImplForHandle(NodeHandle /*nodeHandle*/) const; + + [[nodiscard]] const SceneConfigImpl& getLoadConfig() const; private: std::vector m_objectImpls; std::vector m_nodeMap; RamsesObjectImplSet m_dependingObjects; + const SceneConfigImpl& m_loadConfig; }; class SerializationContext { - using IdMap = ramses_internal::HashMap; + using IdMap = HashMap; public: - SerializationContext(); + explicit SerializationContext(const SaveFileConfigImpl& saveConfig); ObjectIDType getIDForObject(const RamsesObjectImpl* obj); static ObjectIDType GetObjectIDNull(); @@ -68,23 +72,26 @@ namespace ramses void serializeSceneObjectIds(bool flag); [[nodiscard]] bool getSerializeSceneObjectIds() const; + [[nodiscard]] const SaveFileConfigImpl& getSaveConfig() const; + private: bool m_serializeSceneObjectIds = true; IdMap m_ids; ObjectIDType m_lastID; + const SaveFileConfigImpl& m_saveConfig; }; template void DeserializationContext::resolveDependencyIDImplAndStoreAsPointer(OBJECT_TYPE*& ptrId) const { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) ptr really stores an id here - const ObjectIDType id = static_cast(reinterpret_cast(ptrId)); + const auto id = static_cast(reinterpret_cast(ptrId)); assert(id < m_objectImpls.size()); ptrId = static_cast(m_objectImpls[id]); } template - void DeserializationContext::ReadDependentPointerAndStoreAsID(ramses_internal::IInputStream& inStream, PTR_TYPE*& ptr) + void DeserializationContext::ReadDependentPointerAndStoreAsID(IInputStream& inStream, PTR_TYPE*& ptr) { ObjectIDType objID = GetObjectIDNull(); inStream >> objID; @@ -92,5 +99,3 @@ namespace ramses ptr = reinterpret_cast(size_t(objID)); } } - -#endif diff --git a/framework/ramses-framework/src/TCPConfig.cpp b/src/framework/impl/TCPConfig.cpp similarity index 96% rename from framework/ramses-framework/src/TCPConfig.cpp rename to src/framework/impl/TCPConfig.cpp index 64683c13a..de58541d7 100644 --- a/framework/ramses-framework/src/TCPConfig.cpp +++ b/src/framework/impl/TCPConfig.cpp @@ -8,14 +8,13 @@ #include "TCPConfig.h" -namespace ramses +namespace ramses::internal { const uint16_t TCPConfig::DefaultPort(0); const uint16_t TCPConfig::DefaultDaemonPort(5999); TCPConfig::TCPConfig() - : m_portHasBeenSet(false) - , m_port(DefaultPort) + : m_port(DefaultPort) , m_ipAddress("127.0.0.1") , m_daemonPort(DefaultDaemonPort) , m_daemonIP("127.0.0.1") diff --git a/framework/ramses-framework/include/TCPConfig.h b/src/framework/impl/TCPConfig.h similarity index 90% rename from framework/ramses-framework/include/TCPConfig.h rename to src/framework/impl/TCPConfig.h index 43ba6e75c..2c7b7a532 100644 --- a/framework/ramses-framework/include/TCPConfig.h +++ b/src/framework/impl/TCPConfig.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TCPCONFIG_H -#define RAMSES_TCPCONFIG_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include #include -namespace ramses +namespace ramses::internal { class TCPConfig { @@ -40,7 +39,7 @@ namespace ramses static const uint16_t DefaultPort; static const uint16_t DefaultDaemonPort; - bool m_portHasBeenSet; + bool m_portHasBeenSet{false}; uint16_t m_port; std::string m_ipAddress; uint16_t m_daemonPort; @@ -49,5 +48,3 @@ namespace ramses std::chrono::milliseconds m_aliveTimeout; }; } - -#endif diff --git a/src/framework/impl/TextureEnums.cpp b/src/framework/impl/TextureEnums.cpp new file mode 100644 index 000000000..8430167c4 --- /dev/null +++ b/src/framework/impl/TextureEnums.cpp @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/TextureEnumsImpl.h" + +namespace ramses +{ + const char* toString(ETextureSamplingMethod samplingMethod) + { + return EnumToString(samplingMethod); + } + + const char* toString(ETextureAddressMode addressMode) + { + return EnumToString(addressMode); + } + + const char* toString(ETextureFormat format) + { + return EnumToString(format); + } + + const char* toString(ETextureCubeFace face) + { + return EnumToString(face); + } +} diff --git a/client/ramses-client-api/TextureEnums.cpp b/src/framework/impl/TextureEnumsImpl.h similarity index 76% rename from client/ramses-client-api/TextureEnums.cpp rename to src/framework/impl/TextureEnumsImpl.h index 92aec73d0..b13c94d58 100644 --- a/client/ramses-client-api/TextureEnums.cpp +++ b/src/framework/impl/TextureEnumsImpl.h @@ -6,8 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/TextureEnums.h" -#include "Utils/LoggingUtils.h" +#pragma once + +#include "ramses/framework/TextureEnums.h" +#include "internal/Core/Utils/LoggingUtils.h" namespace ramses { @@ -28,7 +30,6 @@ namespace ramses }; const std::array TextureFormatNames = { - "ETextureFormat_Invalid", "ETextureFormat_R8", "ETextureFormat_RG8", "ETextureFormat_RGB8", @@ -87,28 +88,26 @@ namespace ramses "ETextureCubeFace_NegativeZ" }; - ENUM_TO_STRING_NO_EXTRA_LAST(ETextureSamplingMethod, TextureSamplingMethodNames, ETextureSamplingMethod::Linear_MipMapLinear); - ENUM_TO_STRING_NO_EXTRA_LAST(ETextureAddressMode, TextureAddressModeNames, ETextureAddressMode::Mirror); - ENUM_TO_STRING_NO_EXTRA_LAST(ETextureFormat, TextureFormatNames, ETextureFormat::ASTC_SRGBA_12x12); - ENUM_TO_STRING_NO_EXTRA_LAST(ETextureCubeFace, TextureCubeFaceNames, ETextureCubeFace::NegativeZ); - - const char* toString(ETextureSamplingMethod samplingMethod) - { - return EnumToString(samplingMethod); - } - - const char* toString(ETextureAddressMode addressMode) + const std::array TextureChannelColorNames = { - return EnumToString(addressMode); - } + "Red", + "Green", + "Blue", + "Alpha", + "One", + "Zero" + }; - const char* toString(ETextureFormat format) + const std::array RenderBufferAccessModeNames = { - return EnumToString(format); - } + "WriteOnly", + "ReadWrite" + }; - const char* toString(ETextureCubeFace face) - { - return EnumToString(face); - } + ENUM_TO_STRING(ETextureSamplingMethod, TextureSamplingMethodNames, ETextureSamplingMethod::Linear_MipMapLinear); + ENUM_TO_STRING(ETextureAddressMode, TextureAddressModeNames, ETextureAddressMode::Mirror); + ENUM_TO_STRING(ETextureFormat, TextureFormatNames, ETextureFormat::ASTC_SRGBA_12x12); + ENUM_TO_STRING(ETextureCubeFace, TextureCubeFaceNames, ETextureCubeFace::NegativeZ); + ENUM_TO_STRING(ETextureChannelColor, TextureChannelColorNames, ETextureChannelColor::Zero); + ENUM_TO_STRING(ERenderBufferAccessMode, RenderBufferAccessModeNames, ERenderBufferAccessMode::ReadWrite); } diff --git a/src/framework/impl/ThreadWatchdogConfig.h b/src/framework/impl/ThreadWatchdogConfig.h new file mode 100644 index 000000000..2b0a18f47 --- /dev/null +++ b/src/framework/impl/ThreadWatchdogConfig.h @@ -0,0 +1,64 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/IThreadWatchdogNotification.h" + +namespace ramses::internal +{ + class ThreadWatchdogConfig + { + public: + ThreadWatchdogConfig() = default; + + void setWatchdogNotificationInterval(ERamsesThreadIdentifier thread, uint32_t interval) + { + switch(thread) + { + case ERamsesThreadIdentifier::Workers: + m_workerThreadsWatchdogInterval = interval; + break; + case ERamsesThreadIdentifier::Renderer: + m_rendererThreadWatchdogInterval = interval; + break; + case ERamsesThreadIdentifier::Unknown: + break; + } + } + + void setThreadWatchDogCallback(IThreadWatchdogNotification* callback) + { + m_callback = callback; + } + + [[nodiscard]] uint32_t getWatchdogNotificationInterval(ERamsesThreadIdentifier thread) const + { + switch (thread) + { + case ERamsesThreadIdentifier::Workers: + return m_workerThreadsWatchdogInterval; + case ERamsesThreadIdentifier::Renderer: + return m_rendererThreadWatchdogInterval; + case ERamsesThreadIdentifier::Unknown: + return 0; + } + return 0; + } + + [[nodiscard]] IThreadWatchdogNotification* getCallBack() const + { + return m_callback; + } + private: + IThreadWatchdogNotification* m_callback{nullptr}; + uint32_t m_workerThreadsWatchdogInterval{1000u}; + uint32_t m_rendererThreadWatchdogInterval{1000u}; + }; +} diff --git a/src/framework/impl/ValidationReport.cpp b/src/framework/impl/ValidationReport.cpp new file mode 100644 index 000000000..b6939db79 --- /dev/null +++ b/src/framework/impl/ValidationReport.cpp @@ -0,0 +1,68 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/framework/ValidationReport.h" +// impl +#include "impl/ValidationReportImpl.h" + +namespace ramses +{ + ValidationReport::ValidationReport() + : m_impl(std::make_unique()) + { + } + + ValidationReport::~ValidationReport() = default; + + ValidationReport::ValidationReport(ValidationReport&& other) noexcept = default; + + ValidationReport& ValidationReport::operator=(ValidationReport&& other) noexcept = default; + + ValidationReport::ValidationReport(const ValidationReport& other) + : m_impl(std::make_unique(*other.m_impl)) + { + } + + ValidationReport& ValidationReport::operator=(const ValidationReport& other) + { + if (this != &other) + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + void ValidationReport::clear() + { + m_impl->clear(); + } + + bool ValidationReport::hasError() const + { + return m_impl->hasError(); + } + + bool ValidationReport::hasIssue() const + { + return m_impl->hasIssue(); + } + + const std::vector& ValidationReport::getIssues() const + { + return m_impl->getIssues(); + } + + internal::ValidationReportImpl& ValidationReport::impl() + { + return *m_impl; + } + + const internal::ValidationReportImpl& ValidationReport::impl() const + { + return *m_impl; + } +} diff --git a/src/framework/impl/ValidationReportImpl.cpp b/src/framework/impl/ValidationReportImpl.cpp new file mode 100644 index 000000000..8e64f2695 --- /dev/null +++ b/src/framework/impl/ValidationReportImpl.cpp @@ -0,0 +1,53 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// impl +#include "impl/ValidationReportImpl.h" +// internal +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "ramses/framework/RamsesObject.h" +#include "impl/RamsesObjectImpl.h" +#include "impl/RamsesObjectTypeUtils.h" + +namespace ramses::internal +{ + std::string ValidationReportImpl::toString() const + { + StringOutputStream stringStream; + for (auto& m: m_messages) + { + switch (m.type) + { + case EIssueType::Error: + stringStream << "ERROR: "; + break; + case EIssueType::Warning: + stringStream << "WARNING: "; + break; + + } + + if (m.object) + stringStream << m.object->impl().getIdentificationString() << ": "; + + stringStream << m.message << "\n"; + } + return stringStream.release(); + } + + const std::vector& ValidationReportImpl::getDependentObjects(const RamsesObjectImpl* object) const + { + auto it = m_dependencies.find(object); + if (it != m_dependencies.end()) + { + return it->second; + } + static std::vector emptyResult; + return emptyResult; + } +} diff --git a/src/framework/impl/ValidationReportImpl.h b/src/framework/impl/ValidationReportImpl.h new file mode 100644 index 000000000..2a59a20b9 --- /dev/null +++ b/src/framework/impl/ValidationReportImpl.h @@ -0,0 +1,78 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/APIExport.h" +#include "ramses/framework/Issue.h" +#include +#include +#include +#include + +namespace ramses::internal +{ + class RamsesObjectImpl; + + class ValidationReportImpl + { + public: + void add(EIssueType type, const std::string& text, const RamsesObject* obj) + { + m_messages.push_back({type, text, obj}); + m_resultingType = std::min(m_resultingType, type); + } + + void addDependentObject(const RamsesObjectImpl& object, const RamsesObjectImpl& dependency) + { + m_dependencies[&object].push_back(&dependency); + } + + [[nodiscard]] const std::vector& getDependentObjects(const RamsesObjectImpl* object) const; + + void clear() + { + m_messages.clear(); + m_resultingType = EIssueType::Warning; + m_dependencies.clear(); + m_visited.clear(); + } + + [[nodiscard]] bool hasError() const + { + return !m_messages.empty() && (m_resultingType == EIssueType::Error); + } + + [[nodiscard]] bool hasIssue() const + { + return !m_messages.empty(); + } + + [[nodiscard]] const std::vector& getIssues() const + { + return m_messages; + } + + [[nodiscard]] bool addVisit(const RamsesObjectImpl* obj) + { + const auto res = m_visited.insert(obj); + return res.second; + } + + [[nodiscard]] std::string toString() const; + + private: + using DependencyMap = std::unordered_map>; + + std::vector m_messages; + EIssueType m_resultingType = EIssueType::Warning; + DependencyMap m_dependencies; + + std::unordered_set m_visited; + }; +} diff --git a/framework/Communication/TransportCommon/src/CommunicationSystemFactory.cpp b/src/framework/internal/Communication/TransportCommon/CommunicationSystemFactory.cpp similarity index 65% rename from framework/Communication/TransportCommon/src/CommunicationSystemFactory.cpp rename to src/framework/internal/Communication/TransportCommon/CommunicationSystemFactory.cpp index 990e5acab..5663559a4 100644 --- a/framework/Communication/TransportCommon/src/CommunicationSystemFactory.cpp +++ b/src/framework/internal/Communication/TransportCommon/CommunicationSystemFactory.cpp @@ -6,33 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/CommunicationSystemFactory.h" +#include "internal/Communication/TransportCommon/CommunicationSystemFactory.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" -#include "TransportCommon/FakeConnectionSystem.h" -#include "TransportCommon/FakeDiscoveryDaemon.h" -#include "TransportCommon/IDiscoveryDaemon.h" -#include "TransportCommon/ICommunicationSystem.h" +#include "internal/Communication/TransportCommon/FakeConnectionSystem.h" +#include "internal/Communication/TransportCommon/FakeDiscoveryDaemon.h" +#include "internal/Communication/TransportCommon/IDiscoveryDaemon.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" #if defined(HAS_TCP_COMM) -#include "TransportTCP/TCPConnectionSystem.h" -#include "TransportTCP/TcpDiscoveryDaemon.h" +#include "internal/Communication/TransportTCP/TCPConnectionSystem.h" +#include "internal/Communication/TransportTCP/TcpDiscoveryDaemon.h" #endif -#include "RamsesFrameworkConfigImpl.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include -namespace ramses_internal +namespace ramses::internal { namespace { #if defined(HAS_TCP_COMM) // Construct TCPConnectionSystem - auto ConstructTCPConnectionManager(const ramses::RamsesFrameworkConfigImpl& config, const ParticipantIdentifier& participantIdentifier, + auto ConstructTCPConnectionManager(const RamsesFrameworkConfigImpl& config, const ParticipantIdentifier& participantIdentifier, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection) { LOG_INFO(CONTEXT_COMMUNICATION, "Use TCPConnectionSystem"); @@ -54,13 +54,11 @@ namespace ramses_internal #endif } - std::unique_ptr CommunicationSystemFactory::ConstructDiscoveryDaemon(const ramses::RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, Ramsh* optionalRamsh) + std::unique_ptr CommunicationSystemFactory::ConstructDiscoveryDaemon([[maybe_unused]] const RamsesFrameworkConfigImpl& config, + [[maybe_unused]] PlatformLock& frameworkLock, + [[maybe_unused]] StatisticCollectionFramework& statisticCollection, + [[maybe_unused]] Ramsh* optionalRamsh) { - UNUSED(config); - UNUSED(frameworkLock); - UNUSED(statisticCollection); - UNUSED(optionalRamsh); - std::unique_ptr constructedDaemon; switch(config.getUsedProtocol()) { @@ -82,13 +80,11 @@ namespace ramses_internal return constructedDaemon; } - std::unique_ptr CommunicationSystemFactory::ConstructCommunicationSystem(const ramses::RamsesFrameworkConfigImpl& config, const ParticipantIdentifier& participantIdentifier, - PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection) + std::unique_ptr CommunicationSystemFactory::ConstructCommunicationSystem(const RamsesFrameworkConfigImpl& config, + [[maybe_unused]] const ParticipantIdentifier& participantIdentifier, + [[maybe_unused]] PlatformLock& frameworkLock, + [[maybe_unused]] StatisticCollectionFramework& statisticCollection) { - UNUSED(participantIdentifier); - UNUSED(frameworkLock); - UNUSED(statisticCollection); - switch (config.getUsedProtocol()) { #if defined(HAS_TCP_COMM) diff --git a/framework/Communication/TransportCommon/include/TransportCommon/CommunicationSystemFactory.h b/src/framework/internal/Communication/TransportCommon/CommunicationSystemFactory.h similarity index 75% rename from framework/Communication/TransportCommon/include/TransportCommon/CommunicationSystemFactory.h rename to src/framework/internal/Communication/TransportCommon/CommunicationSystemFactory.h index 64e488a3d..ea6190483 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/CommunicationSystemFactory.h +++ b/src/framework/internal/Communication/TransportCommon/CommunicationSystemFactory.h @@ -6,35 +6,29 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMUNICATIONSYSTEMFACTORY_H -#define RAMSES_COMMUNICATIONSYSTEMFACTORY_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformLock.h" -#include +#include "internal/PlatformAbstraction/PlatformLock.h" -namespace ramses -{ - class RamsesFrameworkConfigImpl; -} +#include +#include -namespace ramses_internal +namespace ramses::internal { class ParticipantIdentifier; class IDiscoveryDaemon; class ICommunicationSystem; class StatisticCollectionFramework; class Ramsh; + class RamsesFrameworkConfigImpl; class CommunicationSystemFactory { public: - static std::unique_ptr ConstructCommunicationSystem(const ramses::RamsesFrameworkConfigImpl& config, + static std::unique_ptr ConstructCommunicationSystem(const RamsesFrameworkConfigImpl& config, const ParticipantIdentifier& participantIdentifier, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection); - static std::unique_ptr ConstructDiscoveryDaemon(const ramses::RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLoc, + static std::unique_ptr ConstructDiscoveryDaemon(const RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLoc, StatisticCollectionFramework& statisticCollectionk, Ramsh* optionalRamsh = nullptr); }; } - -#endif diff --git a/framework/Communication/TransportCommon/src/ConnectionStatusUpdateNotifier.cpp b/src/framework/internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.cpp similarity index 80% rename from framework/Communication/TransportCommon/src/ConnectionStatusUpdateNotifier.cpp rename to src/framework/internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.cpp index ec54f405e..70f591900 100644 --- a/framework/Communication/TransportCommon/src/ConnectionStatusUpdateNotifier.cpp +++ b/src/framework/internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/ConnectionStatusUpdateNotifier.h" -#include "TaskFramework/ITask.h" -#include "TransportCommon/IConnectionStatusListener.h" -#include "Utils/LogMacros.h" +#include -namespace ramses_internal +#include "internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.h" +#include "internal/Core/TaskFramework/ITask.h" +#include "internal/Communication/TransportCommon/IConnectionStatusListener.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal { - ConnectionStatusUpdateNotifier::ConnectionStatusUpdateNotifier(const std::string& participantName, const LogContext& logContext, const std::string& usage, PlatformLock& frameworkLock) - : m_participantName(participantName) + ConnectionStatusUpdateNotifier::ConnectionStatusUpdateNotifier(std::string participantName, const LogContext& logContext, std::string usage, PlatformLock& frameworkLock) + : m_participantName(std::move(participantName)) , m_logContext(logContext) - , m_usage(usage) + , m_usage(std::move(usage)) , m_lock(frameworkLock) { } - ConnectionStatusUpdateNotifier::~ConnectionStatusUpdateNotifier() - { - } + ConnectionStatusUpdateNotifier::~ConnectionStatusUpdateNotifier() = default; void ConnectionStatusUpdateNotifier::registerForConnectionUpdates(IConnectionStatusListener* listener) { @@ -38,7 +38,7 @@ namespace ramses_internal void ConnectionStatusUpdateNotifier::unregisterForConnectionUpdates(IConnectionStatusListener* listener) { PlatformGuard g(m_lock); - std::vector::iterator positionToDelete = find_c(m_listeners, listener); + auto positionToDelete = find_c(m_listeners, listener); if (positionToDelete != m_listeners.end()) { m_listeners.erase(positionToDelete); diff --git a/framework/Communication/TransportCommon/include/TransportCommon/ConnectionStatusUpdateNotifier.h b/src/framework/internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.h similarity index 65% rename from framework/Communication/TransportCommon/include/TransportCommon/ConnectionStatusUpdateNotifier.h rename to src/framework/internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.h index b7c983533..99c05ff4a 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/ConnectionStatusUpdateNotifier.h +++ b/src/framework/internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.h @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONNECTIONSTATUSUPDATENOTIFIER_H -#define RAMSES_CONNECTIONSTATUSUPDATENOTIFIER_H +#pragma once -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" -#include "TransportCommon/EConnectionStatus.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/EConnectionStatus.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "Collections/Guid.h" -#include "Collections/Vector.h" -#include "Collections/HashSet.h" -#include "Utils/LogContext.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/Core/Utils/LogContext.h" -namespace ramses_internal +namespace ramses::internal { class ConnectionStatusUpdateNotifier : public IConnectionStatusUpdateNotifier { public: - ConnectionStatusUpdateNotifier(const std::string& participantName, const LogContext& logContext, const std::string& usage, PlatformLock& frameworkLock); + ConnectionStatusUpdateNotifier(std::string participantName, const LogContext& logContext, std::string usage, PlatformLock& frameworkLock); ~ConnectionStatusUpdateNotifier() override; void registerForConnectionUpdates(IConnectionStatusListener* listener) override; @@ -41,5 +40,3 @@ namespace ramses_internal HashSet m_currentState; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/EConnectionProtocol.h b/src/framework/internal/Communication/TransportCommon/EConnectionProtocol.h similarity index 66% rename from framework/Communication/TransportCommon/include/TransportCommon/EConnectionProtocol.h rename to src/framework/internal/Communication/TransportCommon/EConnectionProtocol.h index 6ec701fcd..1d171fc9f 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/EConnectionProtocol.h +++ b/src/framework/internal/Communication/TransportCommon/EConnectionProtocol.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ECONNECTIONPROTOCOL_H -#define RAMSES_ECONNECTIONPROTOCOL_H +#pragma once -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { enum class EConnectionProtocol { @@ -28,9 +27,7 @@ namespace ramses_internal }; } -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::EConnectionProtocol, +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EConnectionProtocol, "EConnectionProtocol", - ramses_internal::ConectionProtocolNames, - ramses_internal::EConnectionProtocol::Invalid); - -#endif + ramses::internal::ConectionProtocolNames, + ramses::internal::EConnectionProtocol::Invalid); diff --git a/framework/Communication/TransportCommon/include/TransportCommon/EConnectionStatus.h b/src/framework/internal/Communication/TransportCommon/EConnectionStatus.h similarity index 87% rename from framework/Communication/TransportCommon/include/TransportCommon/EConnectionStatus.h rename to src/framework/internal/Communication/TransportCommon/EConnectionStatus.h index 0e6196c87..3793e24df 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/EConnectionStatus.h +++ b/src/framework/internal/Communication/TransportCommon/EConnectionStatus.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ECONNECTIONSTATUS_H -#define RAMSES_ECONNECTIONSTATUS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include #include -namespace ramses_internal +namespace ramses::internal { enum EConnectionStatus { @@ -34,4 +33,4 @@ namespace ramses_internal } } } -#endif + diff --git a/framework/Communication/TransportCommon/include/TransportCommon/FakeConnectionStatusUpdateNotifier.h b/src/framework/internal/Communication/TransportCommon/FakeConnectionStatusUpdateNotifier.h similarity index 72% rename from framework/Communication/TransportCommon/include/TransportCommon/FakeConnectionStatusUpdateNotifier.h rename to src/framework/internal/Communication/TransportCommon/FakeConnectionStatusUpdateNotifier.h index b6304f734..65a6386d7 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/FakeConnectionStatusUpdateNotifier.h +++ b/src/framework/internal/Communication/TransportCommon/FakeConnectionStatusUpdateNotifier.h @@ -6,31 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FAKECONNECTIONSTATUSUPDATENOTIFIER_H -#define RAMSES_FAKECONNECTIONSTATUSUPDATENOTIFIER_H +#pragma once -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" -namespace ramses_internal +namespace ramses::internal { class IConnectionStatusListener; class FakeConnectionStatusUpdateNotifier : public IConnectionStatusUpdateNotifier { public: - ~FakeConnectionStatusUpdateNotifier() override - { - } + ~FakeConnectionStatusUpdateNotifier() override = default; - void registerForConnectionUpdates(IConnectionStatusListener*) override + void registerForConnectionUpdates(IConnectionStatusListener* /*listener*/) override { } - void unregisterForConnectionUpdates(IConnectionStatusListener*) override + void unregisterForConnectionUpdates(IConnectionStatusListener* /*listener*/) override { } }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/FakeConnectionSystem.h b/src/framework/internal/Communication/TransportCommon/FakeConnectionSystem.h similarity index 86% rename from framework/Communication/TransportCommon/include/TransportCommon/FakeConnectionSystem.h rename to src/framework/internal/Communication/TransportCommon/FakeConnectionSystem.h index 312a12787..2214dee9d 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/FakeConnectionSystem.h +++ b/src/framework/internal/Communication/TransportCommon/FakeConnectionSystem.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FAKECONNECTIONSYSTEM_H -#define RAMSES_FAKECONNECTIONSYSTEM_H +#pragma once -#include "TransportCommon/FakeConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/FakeConnectionStatusUpdateNotifier.h" #include "ICommunicationSystem.h" -#include "Components/ManagedResource.h" +#include "internal/Components/ManagedResource.h" -namespace ramses_internal +namespace ramses::internal { class SceneActionCollection; @@ -36,7 +35,7 @@ namespace ramses_internal return fake; } - bool broadcastNewScenesAvailable(const SceneInfoVector& /*newScenes*/, ramses::EFeatureLevel) override + bool broadcastNewScenesAvailable(const SceneInfoVector& /*newScenes*/, EFeatureLevel /*unused*/) override { return true; } @@ -46,7 +45,7 @@ namespace ramses_internal return true; } - bool sendScenesAvailable(const Guid& /*to*/, const SceneInfoVector& /*availableScenes*/, ramses::EFeatureLevel) override + bool sendScenesAvailable(const Guid& /*to*/, const SceneInfoVector& /*availableScenes*/, EFeatureLevel /*unused*/) override { return true; } @@ -71,7 +70,7 @@ namespace ramses_internal return true; } - bool sendRendererEvent(const Guid& /*to*/, const SceneId& /*sceneId*/, const std::vector& /*data*/) override + bool sendRendererEvent(const Guid& /*to*/, const SceneId& /*sceneId*/, const std::vector& /*data*/) override { return true; } @@ -93,5 +92,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/FakeDiscoveryDaemon.h b/src/framework/internal/Communication/TransportCommon/FakeDiscoveryDaemon.h similarity index 82% rename from framework/Communication/TransportCommon/include/TransportCommon/FakeDiscoveryDaemon.h rename to src/framework/internal/Communication/TransportCommon/FakeDiscoveryDaemon.h index 94ad97052..fa017f328 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/FakeDiscoveryDaemon.h +++ b/src/framework/internal/Communication/TransportCommon/FakeDiscoveryDaemon.h @@ -6,22 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FAKEDISCOVERYDAEMON_H -#define RAMSES_FAKEDISCOVERYDAEMON_H +#pragma once #include "IDiscoveryDaemon.h" -namespace ramses_internal +namespace ramses::internal { class FakeDiscoveryDaemon final : public IDiscoveryDaemon { public: - FakeDiscoveryDaemon() - : m_started(false) - { - } - bool start() override { if (m_started) @@ -43,8 +37,6 @@ namespace ramses_internal } private: - bool m_started; + bool m_started{false}; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/ICommunicationSystem.h b/src/framework/internal/Communication/TransportCommon/ICommunicationSystem.h similarity index 76% rename from framework/Communication/TransportCommon/include/TransportCommon/ICommunicationSystem.h rename to src/framework/internal/Communication/TransportCommon/ICommunicationSystem.h index 57dd50331..9b3290542 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/ICommunicationSystem.h +++ b/src/framework/internal/Communication/TransportCommon/ICommunicationSystem.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ICOMMUNICATIONSYSTEM_H -#define RAMSES_ICOMMUNICATIONSYSTEM_H +#pragma once -#include "TransportCommon/ServiceHandlerInterfaces.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneTypes.h" -#include "Components/ManagedResource.h" -#include "Utils/IPeriodicLogSupplier.h" -#include "ramses-framework-api/EFeatureLevel.h" +#include "internal/Communication/TransportCommon/ServiceHandlerInterfaces.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Core/Utils/IPeriodicLogSupplier.h" +#include "ramses/framework/EFeatureLevel.h" -namespace ramses_internal +namespace ramses::internal { class Guid; class IConnectionStatusUpdateNotifier; @@ -26,7 +25,7 @@ namespace ramses_internal class ICommunicationSystem : public IPeriodicLogSupplier { public: - ~ICommunicationSystem() override {} + ~ICommunicationSystem() override = default; // connection management virtual bool connectServices() = 0; @@ -35,9 +34,9 @@ namespace ramses_internal virtual IConnectionStatusUpdateNotifier& getRamsesConnectionStatusUpdateNotifier() = 0; // scene - virtual bool broadcastNewScenesAvailable(const SceneInfoVector& newScenes, ramses::EFeatureLevel featureLevel) = 0; + virtual bool broadcastNewScenesAvailable(const SceneInfoVector& newScenes, EFeatureLevel featureLevel) = 0; virtual bool broadcastScenesBecameUnavailable(const SceneInfoVector& unavailableScenes) = 0; - virtual bool sendScenesAvailable(const Guid& to, const SceneInfoVector& availableScenes, ramses::EFeatureLevel featureLevel) = 0; + virtual bool sendScenesAvailable(const Guid& to, const SceneInfoVector& availableScenes, EFeatureLevel featureLevel) = 0; virtual bool sendSubscribeScene(const Guid& to, const SceneId& sceneId) = 0; virtual bool sendUnsubscribeScene(const Guid& to, const SceneId& sceneId) = 0; @@ -45,7 +44,7 @@ namespace ramses_internal virtual bool sendInitializeScene(const Guid& to, const SceneId& sceneId) = 0; virtual bool sendSceneUpdate(const Guid& to, const SceneId& sceneId, const ISceneUpdateSerializer& serializer) = 0; - virtual bool sendRendererEvent(const Guid& to, const SceneId& sceneId, const std::vector& data) = 0; + virtual bool sendRendererEvent(const Guid& to, const SceneId& sceneId, const std::vector& data) = 0; // set service handlers virtual void setSceneProviderServiceHandler(ISceneProviderServiceHandler* handler) = 0; @@ -56,5 +55,3 @@ namespace ramses_internal void triggerLogMessageForPeriodicLog() override = 0; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/IConnectionStatusListener.h b/src/framework/internal/Communication/TransportCommon/IConnectionStatusListener.h similarity index 80% rename from framework/Communication/TransportCommon/include/TransportCommon/IConnectionStatusListener.h rename to src/framework/internal/Communication/TransportCommon/IConnectionStatusListener.h index fa6892984..8a8b76bef 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/IConnectionStatusListener.h +++ b/src/framework/internal/Communication/TransportCommon/IConnectionStatusListener.h @@ -6,20 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ICONNECTIONSTATUSLISTENER_H -#define RAMSES_ICONNECTIONSTATUSLISTENER_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class Guid; class IConnectionStatusListener { public: - virtual ~IConnectionStatusListener() {}; + virtual ~IConnectionStatusListener() = default; + virtual void newParticipantHasConnected(const Guid& guid) = 0; virtual void participantHasDisconnected(const Guid& guid) = 0; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/IConnectionStatusUpdateNotifier.h b/src/framework/internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h similarity index 80% rename from framework/Communication/TransportCommon/include/TransportCommon/IConnectionStatusUpdateNotifier.h rename to src/framework/internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h index f5188a5ca..9edf6f70b 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/IConnectionStatusUpdateNotifier.h +++ b/src/framework/internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h @@ -6,21 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ICONNECTIONSTATUSUPDATENOTIFIER_H -#define RAMSES_ICONNECTIONSTATUSUPDATENOTIFIER_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IConnectionStatusListener; class IConnectionStatusUpdateNotifier { public: - virtual ~IConnectionStatusUpdateNotifier() {} + virtual ~IConnectionStatusUpdateNotifier() = default; virtual void registerForConnectionUpdates(IConnectionStatusListener* listener) = 0; virtual void unregisterForConnectionUpdates(IConnectionStatusListener* listener) = 0; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/IDiscoveryDaemon.h b/src/framework/internal/Communication/TransportCommon/IDiscoveryDaemon.h similarity index 81% rename from framework/Communication/TransportCommon/include/TransportCommon/IDiscoveryDaemon.h rename to src/framework/internal/Communication/TransportCommon/IDiscoveryDaemon.h index 184709b03..f010208f5 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/IDiscoveryDaemon.h +++ b/src/framework/internal/Communication/TransportCommon/IDiscoveryDaemon.h @@ -6,19 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IDISCOVERYDAEMON_H -#define RAMSES_IDISCOVERYDAEMON_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IDiscoveryDaemon { public: - virtual ~IDiscoveryDaemon() {} + virtual ~IDiscoveryDaemon() = default; virtual bool start() = 0; virtual bool stop() = 0; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/ISceneUpdateSerializer.h b/src/framework/internal/Communication/TransportCommon/ISceneUpdateSerializer.h similarity index 68% rename from framework/Communication/TransportCommon/include/TransportCommon/ISceneUpdateSerializer.h rename to src/framework/internal/Communication/TransportCommon/ISceneUpdateSerializer.h index 7dffa0662..714281647 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/ISceneUpdateSerializer.h +++ b/src/framework/internal/Communication/TransportCommon/ISceneUpdateSerializer.h @@ -6,20 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENEUPDATESERIALIZER_H -#define RAMSES_ISCENEUPDATESERIALIZER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" #include "absl/types/span.h" -namespace ramses_internal +#include + +namespace ramses::internal { class ISceneUpdateSerializer { public: virtual ~ISceneUpdateSerializer() = default; - virtual bool writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const = 0; + virtual bool writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const = 0; }; } - -#endif diff --git a/framework/Communication/TransportCommon/src/LogConnectionInfo.cpp b/src/framework/internal/Communication/TransportCommon/LogConnectionInfo.cpp similarity index 84% rename from framework/Communication/TransportCommon/src/LogConnectionInfo.cpp rename to src/framework/internal/Communication/TransportCommon/LogConnectionInfo.cpp index 24c417a11..3288acb69 100644 --- a/framework/Communication/TransportCommon/src/LogConnectionInfo.cpp +++ b/src/framework/internal/Communication/TransportCommon/LogConnectionInfo.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- -#include "TransportCommon/LogConnectionInfo.h" -#include "TransportCommon/ICommunicationSystem.h" +#include "internal/Communication/TransportCommon/LogConnectionInfo.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" -namespace ramses_internal +namespace ramses::internal { LogConnectionInfo::LogConnectionInfo(ICommunicationSystem& communicationSystem) : m_communicationSystem(communicationSystem) diff --git a/framework/Communication/TransportCommon/include/TransportCommon/LogConnectionInfo.h b/src/framework/internal/Communication/TransportCommon/LogConnectionInfo.h similarity index 85% rename from framework/Communication/TransportCommon/include/TransportCommon/LogConnectionInfo.h rename to src/framework/internal/Communication/TransportCommon/LogConnectionInfo.h index 22fa75824..cf3d25113 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/LogConnectionInfo.h +++ b/src/framework/internal/Communication/TransportCommon/LogConnectionInfo.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGCONNECTIONINFO_H -#define RAMSES_LOGCONNECTIONINFO_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class ICommunicationSystem; @@ -25,5 +24,3 @@ namespace ramses_internal ICommunicationSystem& m_communicationSystem; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/RamsesTransportProtocolVersion.h b/src/framework/internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h similarity index 75% rename from framework/Communication/TransportCommon/include/TransportCommon/RamsesTransportProtocolVersion.h rename to src/framework/internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h index 1a99cce77..21fbbe14c 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/RamsesTransportProtocolVersion.h +++ b/src/framework/internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h @@ -6,9 +6,6 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESTRANSPORTPROTOCOLVERSION_H -#define RAMSES_RAMSESTRANSPORTPROTOCOLVERSION_H +#pragma once -#define RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR 121 - -#endif +#define RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR 124 diff --git a/framework/Communication/TransportCommon/src/SceneUpdateSerializationHelper.cpp b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.cpp similarity index 85% rename from framework/Communication/TransportCommon/src/SceneUpdateSerializationHelper.cpp rename to src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.cpp index 9de5fc860..283513172 100644 --- a/framework/Communication/TransportCommon/src/SceneUpdateSerializationHelper.cpp +++ b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.cpp @@ -6,21 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SceneUpdateSerializationHelper.h" -#include "Utils/VectorBinaryOutputStream.h" -#include "Scene/SceneActionCollection.h" -#include "Utils/LogMacros.h" -#include "Utils/BinaryInputStream.h" -#include "Components/ResourceSerializationHelper.h" -#include "Components/FlushInformation.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h" +#include "internal/Core/Utils/VectorBinaryOutputStream.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Components/ResourceSerializationHelper.h" +#include "internal/Components/FlushInformation.h" namespace { template - void putDataArray(ramses_internal::VectorBinaryOutputStream& stream, const std::vector& dataArray) + void putDataArray(ramses::internal::VectorBinaryOutputStream& stream, const std::vector& dataArray) { - const uint32_t numElements = static_cast(dataArray.size()); + const auto numElements = static_cast(dataArray.size()); stream << numElements; if (numElements > 0u) { @@ -31,7 +31,7 @@ namespace } template - void getDataArray(ramses_internal::BinaryInputStream& in, std::vector& dataArray) + void getDataArray(ramses::internal::BinaryInputStream& in, std::vector& dataArray) { assert(dataArray.empty()); uint32_t numElements = 0u; @@ -47,26 +47,27 @@ namespace } } -namespace ramses_internal +namespace ramses::internal { namespace SceneActionSerialization { - absl::Span SerializeDescription(const SceneActionCollection& actions, std::vector& workingMemory) + absl::Span SerializeDescription(const SceneActionCollection& actions, std::vector& workingMemory) { VectorBinaryOutputStream os(workingMemory); os << static_cast(actions.numberOfActions()); for (const auto& sa : actions) - os << static_cast(sa.type()) - << static_cast(sa.offsetInCollection()); + { + os << static_cast(sa.type()) << static_cast(sa.offsetInCollection()); + } return workingMemory; } - absl::Span SerializeData(const SceneActionCollection& actions) + absl::Span SerializeData(const SceneActionCollection& actions) { return actions.collectionData(); } - SceneActionCollection Deserialize(absl::Span description, absl::Span data) + SceneActionCollection Deserialize(absl::Span description, absl::Span data) { BinaryInputStream is(description.data()); uint32_t numActions = 0; @@ -95,7 +96,7 @@ namespace ramses_internal HasEffectTimeSync = 2u }; - absl::Span SerializeInfos(const FlushInformation& flushInfos, std::vector& workingMemory) + absl::Span SerializeInfos(const FlushInformation& flushInfos, std::vector& workingMemory) { const size_t estimatedDataSize = sizeof(flushInfos.containsValidInformation) + @@ -166,7 +167,7 @@ namespace ramses_internal return workingMemory; } - FlushInformation Deserialize(absl::Span flushInfoBlob) + FlushInformation Deserialize(absl::Span flushInfoBlob) { assert(flushInfoBlob.size() >= FlushInformation::getMinimumSize()); BinaryInputStream is(flushInfoBlob.data()); @@ -232,7 +233,7 @@ namespace ramses_internal namespace ResourceSerialization { - absl::Span SerializeDescription(const IResource& resource, std::vector& workingMemory) + absl::Span SerializeDescription(const IResource& resource, std::vector& workingMemory) { VectorBinaryOutputStream os(workingMemory); os << resource.getHash(); // hash must be outside because metadata and blob is used to calculate hash @@ -240,18 +241,17 @@ namespace ramses_internal return workingMemory; } - absl::Span SerializeData(const IResource& resource) + absl::Span SerializeData(const IResource& resource) { // prefer compressed if available if (resource.isCompressedAvailable()) return resource.getCompressedResourceData(); - else if (resource.isDeCompressedAvailable()) + if (resource.isDeCompressedAvailable()) return resource.getResourceData(); - else - return {}; + return {}; } - std::unique_ptr Deserialize(absl::Span description, absl::Span data) + std::unique_ptr Deserialize(absl::Span description, absl::Span data) { BinaryInputStream is(description.data()); ResourceContentHash hash; @@ -259,7 +259,7 @@ namespace ramses_internal ResourceSerializationHelper::DeserializedResourceHeader header = ResourceSerializationHelper::ResourceFromMetadataStream(is); - const size_t expectedDataSize = header.compressionStatus == EResourceCompressionStatus_Compressed ? + const size_t expectedDataSize = header.compressionStatus == EResourceCompressionStatus::Compressed ? header.compressedSize : header.decompressedSize; if (data.size() != expectedDataSize) @@ -268,16 +268,20 @@ namespace ramses_internal expectedDataSize, data.size(), header.compressionStatus); return nullptr; } - if (data.size() > 0) + if (!data.empty()) { // TODO(Carsten): We need to set a compression level here, but we simply don't know which one it is. // We just set offline for now to avoid any potential recompressing, but there shouldn't be // any compressing on renderer side anyway.To implement correctly, we need to break network/file // compatibility by serializing the IResource::CompressionLevel instead of EResourceCompressionStatus - if (header.compressionStatus == EResourceCompressionStatus_Compressed) + if (header.compressionStatus == EResourceCompressionStatus::Compressed) + { header.resource->setCompressedResourceData(CompressedResourceBlob(data.size(), data.data()), IResource::CompressionLevel::Offline, header.decompressedSize, hash); + } else + { header.resource->setResourceData(ResourceBlob(data.size(), data.data()), hash); + } } return std::move(header.resource); } diff --git a/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h new file mode 100644 index 000000000..9520ddea0 --- /dev/null +++ b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "absl/types/span.h" + +#include +#include +#include + +namespace ramses::internal +{ + class SceneActionCollection; + class IResource; + struct FlushInformation; + + namespace SceneActionSerialization + { + absl::Span SerializeDescription(const SceneActionCollection& actions, std::vector& workingMemory); + absl::Span SerializeData(const SceneActionCollection& actions); + + SceneActionCollection Deserialize(absl::Span description, absl::Span data); + }; + + namespace ResourceSerialization + { + absl::Span SerializeDescription(const IResource& resource, std::vector& workingMemory); + absl::Span SerializeData(const IResource& resource); + + std::unique_ptr Deserialize(absl::Span description, absl::Span data); + } + + namespace FlushInformationSerialization + { + absl::Span SerializeInfos(const FlushInformation& flushInfos, std::vector& workingMemory); + FlushInformation Deserialize(absl::Span flushInfoBlob); + } +} diff --git a/framework/Communication/TransportCommon/src/SceneUpdateSerializer.cpp b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializer.cpp similarity index 77% rename from framework/Communication/TransportCommon/src/SceneUpdateSerializer.cpp rename to src/framework/internal/Communication/TransportCommon/SceneUpdateSerializer.cpp index 86f4f89c2..a6b9c3161 100644 --- a/framework/Communication/TransportCommon/src/SceneUpdateSerializer.cpp +++ b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializer.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SceneUpdateSerializer.h" -#include "TransportCommon/SingleSceneUpdateWriter.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializer.h" +#include "internal/Communication/TransportCommon/SingleSceneUpdateWriter.h" -namespace ramses_internal +namespace ramses::internal { SceneUpdateSerializer::SceneUpdateSerializer(const SceneUpdate& update, StatisticCollectionScene& sceneStatistics) : m_update(update) @@ -17,7 +17,7 @@ namespace ramses_internal { } - bool SceneUpdateSerializer::writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const + bool SceneUpdateSerializer::writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const { SingleSceneUpdateWriter writer(m_update, packetMem, writeDoneFunc, m_sceneStatistics); return writer.write(); diff --git a/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializer.h b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializer.h similarity index 77% rename from framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializer.h rename to src/framework/internal/Communication/TransportCommon/SceneUpdateSerializer.h index 4fcd11d14..fdc33d01e 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateSerializer.h +++ b/src/framework/internal/Communication/TransportCommon/SceneUpdateSerializer.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEUPDATESERIALIZER_H -#define RAMSES_SCENEUPDATESERIALIZER_H +#pragma once -#include "TransportCommon/ISceneUpdateSerializer.h" +#include "internal/Communication/TransportCommon/ISceneUpdateSerializer.h" -namespace ramses_internal +namespace ramses::internal { struct SceneUpdate; class StatisticCollectionScene; @@ -20,7 +19,7 @@ namespace ramses_internal { public: explicit SceneUpdateSerializer(const SceneUpdate& update, StatisticCollectionScene& sceneStatistics); - bool writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const override; + bool writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const override; [[nodiscard]] const SceneUpdate& getUpdate() const; [[nodiscard]] const StatisticCollectionScene& getStatisticCollection() const; @@ -29,5 +28,3 @@ namespace ramses_internal StatisticCollectionScene& m_sceneStatistics; }; } - -#endif diff --git a/framework/Communication/TransportCommon/src/SceneUpdateStreamDeserializer.cpp b/src/framework/internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.cpp similarity index 88% rename from framework/Communication/TransportCommon/src/SceneUpdateStreamDeserializer.cpp rename to src/framework/internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.cpp index 44b1c12ed..760a7c658 100644 --- a/framework/Communication/TransportCommon/src/SceneUpdateStreamDeserializer.cpp +++ b/src/framework/internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SceneUpdateStreamDeserializer.h" -#include "TransportCommon/SingleSceneUpdateWriter.h" -#include "TransportCommon/SceneUpdateSerializationHelper.h" -#include "Utils/LogMacros.h" -#include "Utils/BinaryInputStream.h" +#include "internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h" +#include "internal/Communication/TransportCommon/SingleSceneUpdateWriter.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/BinaryInputStream.h" -namespace ramses_internal +namespace ramses::internal { - SceneUpdateStreamDeserializer::Result SceneUpdateStreamDeserializer::processData(absl::Span data) + SceneUpdateStreamDeserializer::Result SceneUpdateStreamDeserializer::processData(absl::Span data) { // check state + input if (m_hasFailed) @@ -43,9 +43,13 @@ namespace ramses_internal } if (hasMorePackets == SingleSceneUpdateWriter::hasMorePacketsFlag) + { ++m_nextExpectedPacketNum; + } else if (hasMorePackets == SingleSceneUpdateWriter::lastPacketFlag) + { m_nextExpectedPacketNum = 1; + } else { LOG_ERROR_P(CONTEXT_FRAMEWORK, "SceneUpdateStreamDeserializer::processData: Corrupted packet, more flag is {}", hasMorePackets); @@ -56,7 +60,9 @@ namespace ramses_internal while (is.getCurrentReadBytes() < data.size()) { if (m_currentBlockSize != 0) + { continueReadingBlock(is, data.size()); + } else { if (!startReadingNewBlock(is, data.size())) @@ -99,7 +105,7 @@ namespace ramses_internal const size_t readBytes = std::min(remainingDatInPacket, remainingBytesToReadForBlock); m_currentBlock.insert(m_currentBlock.end(), is.readPosition(), is.readPosition() + readBytes); - is.skip(readBytes); + is.skip(static_cast(readBytes)); } bool SceneUpdateStreamDeserializer::startReadingNewBlock(BinaryInputStream& is, size_t dataSize) @@ -121,7 +127,7 @@ namespace ramses_internal bool SceneUpdateStreamDeserializer::finalizeBlock() { - SingleSceneUpdateWriter::BlockType blockType = static_cast(m_blockType); + auto blockType = static_cast(m_blockType); if (blockType == SingleSceneUpdateWriter::BlockType::SceneActionCollection) { @@ -174,8 +180,8 @@ namespace ramses_internal is >> descSize >> dataSize; - m_currentResult.actions = SceneActionSerialization::Deserialize(absl::Span(is.readPosition(), descSize), - absl::Span(is.readPosition() + descSize, dataSize)); + m_currentResult.actions = SceneActionSerialization::Deserialize(absl::Span(is.readPosition(), descSize), + absl::Span(is.readPosition() + descSize, dataSize)); return true; } @@ -193,8 +199,8 @@ namespace ramses_internal is >> descSize >> dataSize; - m_currentResult.resources.push_back(ResourceSerialization::Deserialize(absl::Span(is.readPosition(), descSize), - absl::Span(is.readPosition() + descSize, dataSize))); + m_currentResult.resources.push_back(ResourceSerialization::Deserialize(absl::Span(is.readPosition(), descSize), + absl::Span(is.readPosition() + descSize, dataSize))); return true; } @@ -217,7 +223,7 @@ namespace ramses_internal return false; } - m_currentResult.flushInfos = FlushInformationSerialization::Deserialize(absl::Span(is.readPosition(), dataSize)); + m_currentResult.flushInfos = FlushInformationSerialization::Deserialize(absl::Span(is.readPosition(), dataSize)); return true; } diff --git a/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateStreamDeserializer.h b/src/framework/internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h similarity index 82% rename from framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateStreamDeserializer.h rename to src/framework/internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h index 5b0bd2d71..741a20739 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/SceneUpdateStreamDeserializer.h +++ b/src/framework/internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEUPDATESTREAMDESERIALIZER_H -#define RAMSES_SCENEUPDATESTREAMDESERIALIZER_H +#pragma once -#include "Scene/SceneActionCollection.h" -#include "Components/FlushInformation.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/Components/FlushInformation.h" #include "absl/types/span.h" -namespace ramses_internal +namespace ramses::internal { class IResource; class BinaryInputStream; @@ -37,13 +36,13 @@ namespace ramses_internal Result(Result const&) = delete; Result& operator=(Result const& rhs) = delete; - ResultType result; + ResultType result{ResultType::Failed}; SceneActionCollection actions; FlushInformation flushInfos; std::vector> resources; }; - Result processData(absl::Span data); + Result processData(absl::Span data); private: void continueReadingBlock(BinaryInputStream& is, size_t dataSize); @@ -59,9 +58,7 @@ namespace ramses_internal bool m_hasFailed = false; uint32_t m_currentBlockSize = 0; uint32_t m_blockType = 0; - std::vector m_currentBlock; + std::vector m_currentBlock; Result m_currentResult; }; } - -#endif diff --git a/framework/Communication/TransportCommon/include/TransportCommon/ServiceHandlerInterfaces.h b/src/framework/internal/Communication/TransportCommon/ServiceHandlerInterfaces.h similarity index 71% rename from framework/Communication/TransportCommon/include/TransportCommon/ServiceHandlerInterfaces.h rename to src/framework/internal/Communication/TransportCommon/ServiceHandlerInterfaces.h index fc89ff78c..11130157e 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/ServiceHandlerInterfaces.h +++ b/src/framework/internal/Communication/TransportCommon/ServiceHandlerInterfaces.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SERVICEHANDLERINTERFACES_H -#define RAMSES_SERVICEHANDLERINTERFACES_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" #include "absl/types/span.h" -#include "ramses-framework-api/EFeatureLevel.h" +#include "ramses/framework/EFeatureLevel.h" -namespace ramses_internal +namespace ramses::internal { class Guid; class SceneActionCollection; @@ -25,26 +24,24 @@ namespace ramses_internal class ISceneProviderServiceHandler { public: - virtual ~ISceneProviderServiceHandler() {} + virtual ~ISceneProviderServiceHandler() = default; virtual void handleSubscribeScene(const SceneId& sceneId, const Guid& consumerID) = 0; virtual void handleUnsubscribeScene(const SceneId& sceneId, const Guid& consumerID) = 0; - virtual void handleRendererEvent(const SceneId& sceneId, const std::vector& data, const Guid& rendererId) = 0; + virtual void handleRendererEvent(const SceneId& sceneId, const std::vector& data, const Guid& rendererId) = 0; }; class ISceneRendererServiceHandler { public: - virtual ~ISceneRendererServiceHandler() {} + virtual ~ISceneRendererServiceHandler() = default; - virtual void handleNewScenesAvailable(const SceneInfoVector& newScenes, const Guid& providerID, ramses::EFeatureLevel featureLevel) = 0; + virtual void handleNewScenesAvailable(const SceneInfoVector& newScenes, const Guid& providerID, EFeatureLevel featureLevel) = 0; virtual void handleScenesBecameUnavailable(const SceneInfoVector& unavailableScenes, const Guid& providerID) = 0; virtual void handleSceneNotAvailable(const SceneId& sceneId, const Guid& providerID) = 0; virtual void handleInitializeScene(const SceneId& sceneId, const Guid& providerID) = 0; - virtual void handleSceneUpdate(const SceneId& sceneId, absl::Span actionData, const Guid& providerID) = 0; + virtual void handleSceneUpdate(const SceneId& sceneId, absl::Span actionData, const Guid& providerID) = 0; }; } - -#endif diff --git a/framework/Communication/TransportCommon/src/SingleSceneUpdateWriter.cpp b/src/framework/internal/Communication/TransportCommon/SingleSceneUpdateWriter.cpp similarity index 89% rename from framework/Communication/TransportCommon/src/SingleSceneUpdateWriter.cpp rename to src/framework/internal/Communication/TransportCommon/SingleSceneUpdateWriter.cpp index e39247ba3..d33e22fe4 100644 --- a/framework/Communication/TransportCommon/src/SingleSceneUpdateWriter.cpp +++ b/src/framework/internal/Communication/TransportCommon/SingleSceneUpdateWriter.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SingleSceneUpdateWriter.h" -#include "TransportCommon/SceneUpdateSerializationHelper.h" -#include "Utils/StatisticCollection.h" -#include "Utils/LogMacros.h" +#include "internal/Communication/TransportCommon/SingleSceneUpdateWriter.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses_internal +namespace ramses::internal { - SingleSceneUpdateWriter::SingleSceneUpdateWriter(const SceneUpdate& update, absl::Span packetMem, const std::function& writeDoneFunc, StatisticCollectionScene& sceneStatistics) + SingleSceneUpdateWriter::SingleSceneUpdateWriter(const SceneUpdate& update, absl::Span packetMem, const std::function& writeDoneFunc, StatisticCollectionScene& sceneStatistics) : m_update(update) , m_packetMem(packetMem) , m_writeDoneFunc(writeDoneFunc) @@ -69,8 +69,10 @@ namespace ramses_internal } if (m_update.flushInfos.containsValidInformation) + { if (!writeFlushInfos(m_update.flushInfos)) return false; + } if (m_packetWriter.getBytesWritten() > 0) { @@ -89,7 +91,7 @@ namespace ramses_internal const auto descSpan = SceneActionSerialization::SerializeDescription(m_update.actions, m_temporaryMemToSerializeDescription); const auto dataSpan = SceneActionSerialization::SerializeData(m_update.actions); - std::array header; + std::array header{}; RawBinaryOutputStream os(header.data(), header.size()); os << static_cast(descSpan.size()) << static_cast(dataSpan.size()); @@ -102,7 +104,7 @@ namespace ramses_internal const auto descSpan = ResourceSerialization::SerializeDescription(res, m_temporaryMemToSerializeDescription); const auto dataSpan = ResourceSerialization::SerializeData(res); - std::array header; + std::array header{}; RawBinaryOutputStream os(header.data(), header.size()); os << static_cast(descSpan.size()) << static_cast(dataSpan.size()); @@ -114,7 +116,7 @@ namespace ramses_internal m_temporaryMemToSerializeDescription.clear(); const auto descSpan = FlushInformationSerialization::SerializeInfos(infos, m_temporaryMemToSerializeDescription); - std::array header; + std::array header{}; RawBinaryOutputStream os(header.data(), header.size()); os << static_cast(descSpan.size()); return writeBlock(BlockType::FlushInfos, { {os.getData(), os.getSize()}, descSpan }); @@ -128,12 +130,12 @@ namespace ramses_internal << static_cast(0); // placeholders } - bool SingleSceneUpdateWriter::writeBlock(BlockType type, std::initializer_list> spans) + bool SingleSceneUpdateWriter::writeBlock(BlockType type, std::initializer_list> spans) { size_t blockSize = 0; for (const auto s : spans) blockSize += s.size(); - std::array header; + std::array header{}; RawBinaryOutputStream os(header.data(), header.size()); os << static_cast(type) << static_cast(blockSize); @@ -147,9 +149,9 @@ namespace ramses_internal return true; } - bool SingleSceneUpdateWriter::writeDataToPackets(absl::Span data, bool writeContinuous) + bool SingleSceneUpdateWriter::writeDataToPackets(absl::Span data, bool writeContinuous) { - while (data.size() > 0) + while (!data.empty()) { const bool startNewPacket = writeContinuous ? (m_packetMem.size() < m_packetWriter.getBytesWritten() + data.size()) : diff --git a/framework/Communication/TransportCommon/include/TransportCommon/SingleSceneUpdateWriter.h b/src/framework/internal/Communication/TransportCommon/SingleSceneUpdateWriter.h similarity index 75% rename from framework/Communication/TransportCommon/include/TransportCommon/SingleSceneUpdateWriter.h rename to src/framework/internal/Communication/TransportCommon/SingleSceneUpdateWriter.h index 850febb01..24d0deda8 100644 --- a/framework/Communication/TransportCommon/include/TransportCommon/SingleSceneUpdateWriter.h +++ b/src/framework/internal/Communication/TransportCommon/SingleSceneUpdateWriter.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SINGLESCENEUPDATEWRITER_H -#define RAMSES_SINGLESCENEUPDATEWRITER_H +#pragma once -#include "Components/SceneUpdate.h" -#include "Utils/RawBinaryOutputStream.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Core/Utils/RawBinaryOutputStream.h" #include "absl/types/span.h" -namespace ramses_internal +namespace ramses::internal { class StatisticCollectionScene; class SingleSceneUpdateWriter { public: - SingleSceneUpdateWriter(const SceneUpdate& update, absl::Span packetMem, const std::function& writeDoneFunc, StatisticCollectionScene& sceneStatistics); + SingleSceneUpdateWriter(const SceneUpdate& update, absl::Span packetMem, const std::function& writeDoneFunc, StatisticCollectionScene& sceneStatistics); bool write(); @@ -42,18 +41,16 @@ namespace ramses_internal bool writeResource(const IResource& resource); bool writeFlushInfos(const FlushInformation& infos); - bool writeBlock(BlockType type, std::initializer_list> spans); - bool writeDataToPackets(absl::Span data, bool writeContinuous = false); + bool writeBlock(BlockType type, std::initializer_list> spans); + bool writeDataToPackets(absl::Span data, bool writeContinuous = false); const SceneUpdate& m_update; - const absl::Span m_packetMem; + const absl::Span m_packetMem; const std::function& m_writeDoneFunc; RawBinaryOutputStream m_packetWriter; uint32_t m_packetNum = 1; - std::vector m_temporaryMemToSerializeDescription; // optimization to avoid allocations + std::vector m_temporaryMemToSerializeDescription; // optimization to avoid allocations StatisticCollectionScene& m_sceneStatistics; uint64_t m_overallSize{0}; }; } - -#endif diff --git a/framework/Communication/TransportTCP/include/TransportTCP/AsioWrapper.h b/src/framework/internal/Communication/TransportTCP/AsioWrapper.h similarity index 96% rename from framework/Communication/TransportTCP/include/TransportTCP/AsioWrapper.h rename to src/framework/internal/Communication/TransportTCP/AsioWrapper.h index 59f29db29..98d4b8afa 100644 --- a/framework/Communication/TransportTCP/include/TransportTCP/AsioWrapper.h +++ b/src/framework/internal/Communication/TransportTCP/AsioWrapper.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ASIOWRAPPER_H -#define RAMSES_ASIOWRAPPER_H +#pragma once -#include "Utils/Warnings.h" +#include "internal/Core/Utils/Warnings.h" // disable warnings temporarily and set configuration defines WARNINGS_PUSH @@ -77,5 +76,3 @@ namespace asio } } } - -#endif diff --git a/framework/Communication/TransportTCP/include/TransportTCP/EMessageId.h b/src/framework/internal/Communication/TransportTCP/EMessageId.h similarity index 74% rename from framework/Communication/TransportTCP/include/TransportTCP/EMessageId.h rename to src/framework/internal/Communication/TransportTCP/EMessageId.h index 1ab8260d8..4889d8c80 100644 --- a/framework/Communication/TransportTCP/include/TransportTCP/EMessageId.h +++ b/src/framework/internal/Communication/TransportTCP/EMessageId.h @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMESSAGEID_H -#define RAMSES_EMESSAGEID_H +#pragma once -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "Utils/LoggingUtils.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/Core/Utils/LoggingUtils.h" #include -namespace ramses_internal +namespace ramses::internal { enum class EMessageId : uint32_t { - PublishScene = 1, + Invalid, + PublishScene, UnpublishScene, SubscribeScene, UnsubscribeScene, @@ -34,13 +34,10 @@ namespace ramses_internal // scene CreateScene, - - NUM_ELEMENTS }; const std::array EMessageIdNames = { - "INVALID_0", // needed because EMessageId starts at 1 - + "Invalid", "PublishScene", "UnpublishScene", "SubscribeScene", @@ -61,16 +58,14 @@ namespace ramses_internal inline IInputStream& operator>>(IInputStream& inputStream, EMessageId& messageId) { - std::underlying_type_t val; + std::underlying_type_t val = 0; inputStream >> val; messageId = static_cast(val); return inputStream; } } -MAKE_ENUM_CLASS_PRINTABLE(ramses_internal::EMessageId, +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EMessageId, "EMessageId", - ramses_internal::EMessageIdNames, - ramses_internal::EMessageId::NUM_ELEMENTS); - -#endif + ramses::internal::EMessageIdNames, + ramses::internal::EMessageId::CreateScene); diff --git a/framework/Communication/TransportTCP/include/TransportTCP/NetworkParticipantAddress.h b/src/framework/internal/Communication/TransportTCP/NetworkParticipantAddress.h similarity index 79% rename from framework/Communication/TransportTCP/include/TransportTCP/NetworkParticipantAddress.h rename to src/framework/internal/Communication/TransportTCP/NetworkParticipantAddress.h index fd026b257..3e95df5a9 100644 --- a/framework/Communication/TransportTCP/include/TransportTCP/NetworkParticipantAddress.h +++ b/src/framework/internal/Communication/TransportTCP/NetworkParticipantAddress.h @@ -6,25 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_NETWORKPARTICIPANTADDRESS_H -#define RAMSES_NETWORKPARTICIPANTADDRESS_H +#pragma once -#include "Common/ParticipantIdentifier.h" +#include "internal/Core/Common/ParticipantIdentifier.h" #include #include -namespace ramses_internal +namespace ramses::internal { class NetworkParticipantAddress : public ParticipantIdentifier { public: - NetworkParticipantAddress() - : ParticipantIdentifier() - , m_ip("127.0.0.1") - , m_port(0) - { - }; + NetworkParticipantAddress() = default; NetworkParticipantAddress(const Guid& id, std::string_view name, std::string_view ip, uint16_t port) : ParticipantIdentifier(id, name) @@ -56,10 +50,8 @@ namespace ramses_internal } private: - std::string m_ip; - uint16_t m_port; + std::string m_ip{"127.0.0.1"}; + uint16_t m_port{0}; }; } - -#endif diff --git a/framework/Communication/TransportTCP/src/TCPConnectionSystem.cpp b/src/framework/internal/Communication/TransportTCP/TCPConnectionSystem.cpp similarity index 95% rename from framework/Communication/TransportTCP/src/TCPConnectionSystem.cpp rename to src/framework/internal/Communication/TransportTCP/TCPConnectionSystem.cpp index cc080aeb8..c0633bcd8 100644 --- a/framework/Communication/TransportTCP/src/TCPConnectionSystem.cpp +++ b/src/framework/internal/Communication/TransportTCP/TCPConnectionSystem.cpp @@ -6,32 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportTCP/TCPConnectionSystem.h" +#include "internal/Communication/TransportTCP/TCPConnectionSystem.h" -#include "Scene/SceneActionCollection.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/RawBinaryOutputStream.h" -#include "Utils/StatisticCollection.h" -#include "Utils/LogMacros.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/RawBinaryOutputStream.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Core/Utils/LogMacros.h" #include -#include "TransportCommon/ISceneUpdateSerializer.h" +#include +#include "internal/Communication/TransportCommon/ISceneUpdateSerializer.h" -namespace ramses_internal +namespace ramses::internal { static const constexpr uint32_t ResourceDataSize = 300000; static const constexpr uint32_t SceneActionDataSize = 300000; - TCPConnectionSystem::TCPConnectionSystem(const NetworkParticipantAddress& participantAddress, + TCPConnectionSystem::TCPConnectionSystem(NetworkParticipantAddress participantAddress, uint32_t protocolVersion, - const NetworkParticipantAddress& daemonAddress, + NetworkParticipantAddress daemonAddress, bool pureDaemon, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, std::chrono::milliseconds aliveInterval, std::chrono::milliseconds aliveTimeout) - : m_participantAddress(participantAddress) + : m_participantAddress(std::move(participantAddress)) , m_protocolVersion(protocolVersion) - , m_daemonAddress(daemonAddress) + , m_daemonAddress(std::move(daemonAddress)) , m_actAsDaemon(m_participantAddress.getPort() != 0) , m_hasOtherDaemon(!(m_daemonAddress.getPort() == m_participantAddress.getPort() && m_daemonAddress.getIp() == m_participantAddress.getIp()) && @@ -65,7 +66,7 @@ namespace ramses_internal bool TCPConnectionSystem::connectServices() { - LOG_INFO_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_INFO_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { sos << "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::connectServices: " << m_participantAddress.getParticipantId() << "/" << m_participantAddress.getParticipantName() << " at " << m_participantAddress.getIp() << ":" << m_participantAddress.getPort() @@ -300,7 +301,7 @@ namespace ramses_internal assert(pp->currentOutBuffer.empty()); pp->currentOutBuffer = msg.stream.release(); - const uint32_t fullSize = static_cast(pp->currentOutBuffer.size()); + const auto fullSize = static_cast(pp->currentOutBuffer.size()); LOG_DEBUG(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::sendMessageToParticipant: To " << pp->address.getParticipantId() << ", MsgType " << msg.messageType << ", Size " << fullSize); @@ -420,7 +421,7 @@ namespace ramses_internal pp->checkReceivedAliveTimer.async_wait([this, pp, originalLastReceived = pp->lastReceived](asio::error_code e) { if (!e) { - LOG_WARN_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_WARN_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { const auto now = std::chrono::steady_clock::now(); const auto lastRecvMs = std::chrono::duration_cast>(now - originalLastReceived).count(); const auto expectedMs = std::chrono::duration_cast>(now - originalLastReceived - m_aliveInterval).count(); @@ -470,9 +471,13 @@ namespace ramses_internal pp->connectTimer.expires_after(backoffTime); pp->connectTimer.async_wait([this, pp](asio::error_code ee) { if (ee) + { LOG_DEBUG(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::removeParticipant: Backoff timer got canceled."); + } else + { addNewParticipantByAddress(pp->address); + } }); } else @@ -581,7 +586,7 @@ namespace ramses_internal void TCPConnectionSystem::handleReceivedMessage(const ParticipantPtr& pp) { - assert(pp->receiveBuffer.size() > 0); + assert(!pp->receiveBuffer.empty()); BinaryInputStream stream(pp->receiveBuffer.data()); uint32_t recvProtocolVersion = 0; @@ -597,7 +602,7 @@ namespace ramses_internal uint32_t messageTypeTmp = 0; stream >> messageTypeTmp; - EMessageId messageType = static_cast(messageTypeTmp); + auto messageType = static_cast(messageTypeTmp); LOG_TRACE(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handleReceivedMessage: From " << pp->address.getParticipantId() << ", type " << messageType); @@ -660,7 +665,7 @@ namespace ramses_internal LOG_WARN(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handleConnectionDescriptionMessage: Duplicate connection description while established from " << pp->address.getParticipantId()); return; } - else if (pp->state != EParticipantState::WaitingForHello) + if (pp->state != EParticipantState::WaitingForHello) { LOG_ERROR(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handleConnectionDescriptionMessage: Unexpected connection description from " << pp->address.getParticipantId() << " in state " << EnumToString(pp->state)); @@ -673,7 +678,7 @@ namespace ramses_internal Guid guid; std::string name; std::string ip; - uint16_t port; + uint16_t port = 0u; EParticipantType participantType; stream >> guid >> name @@ -769,7 +774,7 @@ namespace ramses_internal Guid guid; std::string name; std::string ip; - uint16_t port; + uint16_t port = 0u; EParticipantType participantType; stream >> guid >> name @@ -899,10 +904,10 @@ namespace ramses_internal static_assert(SceneActionDataSize < 1000000, "SceneActionDataSize too big"); - std::vector buffer(SceneActionDataSize); + std::vector buffer(SceneActionDataSize); return serializer.writeToPackets({buffer.data(), buffer.size()}, [&](size_t size) { - const uint32_t usedSize = static_cast(size); + const auto usedSize = static_cast(size); OutMessage msg(to, EMessageId::SendSceneUpdate); msg.stream << sceneId.getValue() << usedSize; @@ -922,20 +927,20 @@ namespace ramses_internal uint32_t dataSize = 0; stream >> dataSize; - std::vector data(dataSize); + std::vector data(dataSize); stream.read(data.data(), dataSize); LOG_TRACE(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handleSceneActionList: from " << pp->address.getParticipantId()); PlatformGuard guard(m_frameworkLock); - m_sceneRendererHandler->handleSceneUpdate(sceneId, std::move(data), pp->address.getParticipantId()); + m_sceneRendererHandler->handleSceneUpdate(sceneId, data, pp->address.getParticipantId()); } } // -- - bool TCPConnectionSystem::broadcastNewScenesAvailable(const SceneInfoVector& newScenes, ramses::EFeatureLevel featureLevel) + bool TCPConnectionSystem::broadcastNewScenesAvailable(const SceneInfoVector& newScenes, EFeatureLevel featureLevel) { - LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { sos << "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::sendScenesAvailable: to all ["; for (const auto& s : newScenes) sos << s.sceneID << "/" << s.friendlyName << "; "; @@ -953,9 +958,9 @@ namespace ramses_internal return postMessageForSending(std::move(msg)); } - bool TCPConnectionSystem::sendScenesAvailable(const Guid& to, const SceneInfoVector& availableScenes, ramses::EFeatureLevel featureLevel) + bool TCPConnectionSystem::sendScenesAvailable(const Guid& to, const SceneInfoVector& availableScenes, EFeatureLevel featureLevel) { - LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { sos << "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::sendScenesAvailable: to " << to << " ["; for (const auto& s : availableScenes) sos << s.sceneID << "/" << s.friendlyName << "; "; @@ -977,7 +982,7 @@ namespace ramses_internal { if (m_sceneRendererHandler) { - uint32_t numScenes; + uint32_t numScenes = 0u; stream >> numScenes; SceneInfoVector newScenes; @@ -993,9 +998,9 @@ namespace ramses_internal uint32_t featureLevelInt = 0u; stream >> featureLevelInt; - const ramses::EFeatureLevel featureLevel = static_cast(featureLevelInt); + const auto featureLevel = static_cast(featureLevelInt); - LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { sos << "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handlePublishScene: from " << pp->address.getParticipantId() << " ["; for (const auto& s : newScenes) sos << s.sceneID << "/" << s.friendlyName << "; "; @@ -1010,7 +1015,7 @@ namespace ramses_internal // -- bool TCPConnectionSystem::broadcastScenesBecameUnavailable(const SceneInfoVector& unavailableScenes) { - LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { sos << "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::broadcastScenesBecameUnavailable: to all ["; for (const auto& s : unavailableScenes) sos << s.sceneID << "/" << s.friendlyName << "; "; @@ -1031,7 +1036,7 @@ namespace ramses_internal { if (m_sceneRendererHandler) { - uint32_t numScenes; + uint32_t numScenes = 0u; stream >> numScenes; SceneInfoVector unavailableScenes; @@ -1045,7 +1050,7 @@ namespace ramses_internal unavailableScenes.push_back(sceneInfo); } - LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(CONTEXT_COMMUNICATION, ([&](StringOutputStream& sos) { sos << "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handleUnpublishScene: from " << pp->address.getParticipantId() << " ["; for (const auto& s : unavailableScenes) sos << s.sceneID << "/" << s.friendlyName << "; "; @@ -1058,7 +1063,7 @@ namespace ramses_internal } // -- - bool TCPConnectionSystem::sendRendererEvent(const Guid& to, const SceneId& sceneId, const std::vector& data) + bool TCPConnectionSystem::sendRendererEvent(const Guid& to, const SceneId& sceneId, const std::vector& data) { LOG_DEBUG(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::sendRendererEvent: to " << to << ", size " << data.size()); if (data.size() > 32000) // really 32768 @@ -1083,13 +1088,13 @@ namespace ramses_internal uint32_t dataSize = 0; stream >> dataSize; - std::vector data(dataSize); + std::vector data(dataSize); stream.read(data.data(), dataSize); LOG_DEBUG(CONTEXT_COMMUNICATION, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << ")::handleRendererEvent: from " << pp->address.getParticipantId() << ", size " << dataSize); PlatformGuard guard(m_frameworkLock); - m_sceneProviderHandler->handleRendererEvent(sceneId, std::move(data), pp->address.getParticipantId()); + m_sceneProviderHandler->handleRendererEvent(sceneId, data, pp->address.getParticipantId()); } } @@ -1136,15 +1141,20 @@ namespace ramses_internal }; if (m_runState) + { asio::post(m_runState->m_io, logFunction); + } else + { logFunction(); + } } void TCPConnectionSystem::triggerLogMessageForPeriodicLog() { // expect framework lock to be held if (m_runState) + { asio::post(m_runState->m_io, [this]() { LOG_INFO_F(CONTEXT_PERIODIC, ([&](StringOutputStream& sos) @@ -1163,8 +1173,11 @@ namespace ramses_internal } })); }); + } else + { LOG_INFO(CONTEXT_PERIODIC, "TCPConnectionSystem(" << m_participantAddress.getParticipantName() << "): Not connected"); + } } void TCPConnectionSystem::triggerConnectionUpdateNotification(Guid participant, EConnectionStatus status) @@ -1187,9 +1200,9 @@ namespace ramses_internal // --- TCPConnectionSystem::Participant --- - TCPConnectionSystem::Participant::Participant(const NetworkParticipantAddress& address_, asio::io_service& io_, + TCPConnectionSystem::Participant::Participant(NetworkParticipantAddress address_, asio::io_service& io_, EParticipantType type_, EParticipantState state_) - : address(address_) + : address(std::move(address_)) , socket(io_) , connectTimer(io_) , lengthReceiveBuffer(0) diff --git a/framework/Communication/TransportTCP/include/TransportTCP/TCPConnectionSystem.h b/src/framework/internal/Communication/TransportTCP/TCPConnectionSystem.h similarity index 85% rename from framework/Communication/TransportTCP/include/TransportTCP/TCPConnectionSystem.h rename to src/framework/internal/Communication/TransportTCP/TCPConnectionSystem.h index 8158e98e1..9ec7d9dd1 100644 --- a/framework/Communication/TransportTCP/include/TransportTCP/TCPConnectionSystem.h +++ b/src/framework/internal/Communication/TransportTCP/TCPConnectionSystem.h @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMUNICATION_TCPCONNECTIONSYSTEM_H -#define RAMSES_COMMUNICATION_TCPCONNECTIONSYSTEM_H - -#include "TransportCommon/ICommunicationSystem.h" -#include "TransportCommon/ConnectionStatusUpdateNotifier.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "TransportTCP/NetworkParticipantAddress.h" -#include "TransportTCP/EMessageId.h" -#include "Utils/BinaryOutputStream.h" -#include "Collections/HashSet.h" -#include "Collections/HashMap.h" -#include "TransportTCP/AsioWrapper.h" +#pragma once + +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" +#include "internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Communication/TransportTCP/NetworkParticipantAddress.h" +#include "internal/Communication/TransportTCP/EMessageId.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/Communication/TransportTCP/AsioWrapper.h" #include +#include -namespace ramses_internal +namespace ramses::internal { class StatisticCollectionFramework; class BinaryInputStream; @@ -29,7 +29,7 @@ namespace ramses_internal class TCPConnectionSystem final : public Runnable, public ICommunicationSystem { public: - TCPConnectionSystem(const NetworkParticipantAddress& participantAddress, uint32_t protocolVersion, const NetworkParticipantAddress& daemonAddress, bool pureDaemon, + TCPConnectionSystem(NetworkParticipantAddress participantAddress, uint32_t protocolVersion, NetworkParticipantAddress daemonAddress, bool pureDaemon, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, std::chrono::milliseconds aliveInterval, std::chrono::milliseconds aliveTimeout); ~TCPConnectionSystem() override; @@ -42,9 +42,9 @@ namespace ramses_internal IConnectionStatusUpdateNotifier& getRamsesConnectionStatusUpdateNotifier() override; // scene - bool broadcastNewScenesAvailable(const SceneInfoVector& newScenes, ramses::EFeatureLevel featureLevel) override; + bool broadcastNewScenesAvailable(const SceneInfoVector& newScenes, EFeatureLevel featureLevel) override; bool broadcastScenesBecameUnavailable(const SceneInfoVector& unavailableScenes) override; - bool sendScenesAvailable(const Guid& to, const SceneInfoVector& availableScenes, ramses::EFeatureLevel featureLevel) override; + bool sendScenesAvailable(const Guid& to, const SceneInfoVector& availableScenes, EFeatureLevel featureLevel) override; bool sendSubscribeScene(const Guid& to, const SceneId& sceneId) override; bool sendUnsubscribeScene(const Guid& to, const SceneId& sceneId) override; @@ -52,7 +52,7 @@ namespace ramses_internal bool sendInitializeScene(const Guid& to, const SceneId& sceneId) override; bool sendSceneUpdate(const Guid& to, const SceneId& sceneId, const ISceneUpdateSerializer& serializer) override; - bool sendRendererEvent(const Guid& to, const SceneId& sceneId, const std::vector& data) override; + bool sendRendererEvent(const Guid& to, const SceneId& sceneId, const std::vector& data) override; // set service handlers void setSceneProviderServiceHandler(ISceneProviderServiceHandler* handler) override; @@ -86,8 +86,8 @@ namespace ramses_internal assert(to_.isValid()); } - OutMessage(const std::vector& to_, EMessageId messageType_) - : to(to_) + OutMessage(std::vector to_, EMessageId messageType_) + : to(std::move(to_)) , messageType(messageType_) { stream << static_cast(0) // fill in size later @@ -107,7 +107,7 @@ namespace ramses_internal struct Participant { - Participant(const NetworkParticipantAddress& address_, asio::io_service& io_, + Participant(NetworkParticipantAddress address_, asio::io_service& io_, EParticipantType type_, EParticipantState state_); ~Participant(); @@ -116,10 +116,10 @@ namespace ramses_internal asio::steady_timer connectTimer; std::deque outQueue; - std::vector currentOutBuffer; + std::vector currentOutBuffer; uint32_t lengthReceiveBuffer; - std::vector receiveBuffer; + std::vector receiveBuffer; std::chrono::steady_clock::time_point lastSent; asio::steady_timer sendAliveTimer; @@ -201,5 +201,3 @@ namespace ramses_internal HashMap m_establishedParticipants; }; } - -#endif diff --git a/framework/Communication/TransportTCP/src/TcpDiscoveryDaemon.cpp b/src/framework/internal/Communication/TransportTCP/TcpDiscoveryDaemon.cpp similarity index 79% rename from framework/Communication/TransportTCP/src/TcpDiscoveryDaemon.cpp rename to src/framework/internal/Communication/TransportTCP/TcpDiscoveryDaemon.cpp index ff2ea98ce..f84fceca0 100644 --- a/framework/Communication/TransportTCP/src/TcpDiscoveryDaemon.cpp +++ b/src/framework/internal/Communication/TransportTCP/TcpDiscoveryDaemon.cpp @@ -6,16 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportTCP//TcpDiscoveryDaemon.h" -#include "TransportTCP/NetworkParticipantAddress.h" -#include "TransportTCP/TCPConnectionSystem.h" -#include "RamsesFrameworkConfigImpl.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" -#include "Ramsh/Ramsh.h" +#include "internal/Communication/TransportTCP//TcpDiscoveryDaemon.h" +#include "internal/Communication/TransportTCP/NetworkParticipantAddress.h" +#include "internal/Communication/TransportTCP/TCPConnectionSystem.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Ramsh/Ramsh.h" -namespace ramses_internal +namespace ramses::internal { - TcpDiscoveryDaemon::TcpDiscoveryDaemon(const ramses::RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, Ramsh* optionalRamsh) + TcpDiscoveryDaemon::TcpDiscoveryDaemon(const RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, Ramsh* optionalRamsh) : m_started(false) { // own address diff --git a/framework/Communication/TransportTCP/include/TransportTCP/TcpDiscoveryDaemon.h b/src/framework/internal/Communication/TransportTCP/TcpDiscoveryDaemon.h similarity index 66% rename from framework/Communication/TransportTCP/include/TransportTCP/TcpDiscoveryDaemon.h rename to src/framework/internal/Communication/TransportTCP/TcpDiscoveryDaemon.h index 886bb5ca6..47deef9a7 100644 --- a/framework/Communication/TransportTCP/include/TransportTCP/TcpDiscoveryDaemon.h +++ b/src/framework/internal/Communication/TransportTCP/TcpDiscoveryDaemon.h @@ -6,28 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TCPDISCOVERYDAEMON_H -#define RAMSES_TCPDISCOVERYDAEMON_H +#pragma once -#include "TransportCommon/IDiscoveryDaemon.h" -#include "TransportCommon/ICommunicationSystem.h" -#include "TransportCommon/LogConnectionInfo.h" -#include "PlatformAbstraction/PlatformLock.h" +#include "internal/Communication/TransportCommon/IDiscoveryDaemon.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" +#include "internal/Communication/TransportCommon/LogConnectionInfo.h" +#include "internal/PlatformAbstraction/PlatformLock.h" #include -namespace ramses -{ - class RamsesFrameworkConfigImpl; -} -namespace ramses_internal +namespace ramses::internal { class StatisticCollectionFramework; class Ramsh; + class RamsesFrameworkConfigImpl; class TcpDiscoveryDaemon final : public IDiscoveryDaemon { public: - TcpDiscoveryDaemon(const ramses::RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, Ramsh* optionalRamsh); + TcpDiscoveryDaemon(const RamsesFrameworkConfigImpl& config, PlatformLock& frameworkLock, StatisticCollectionFramework& statisticCollection, Ramsh* optionalRamsh); ~TcpDiscoveryDaemon() override; bool start() override; @@ -39,5 +35,3 @@ namespace ramses_internal std::shared_ptr m_commandLogConnectionInformation; }; } - -#endif diff --git a/framework/Components/src/ClientSceneLogicBase.cpp b/src/framework/internal/Components/ClientSceneLogicBase.cpp similarity index 78% rename from framework/Components/src/ClientSceneLogicBase.cpp rename to src/framework/internal/Components/ClientSceneLogicBase.cpp index 8e31dfcf4..452e473d6 100644 --- a/framework/Components/src/ClientSceneLogicBase.cpp +++ b/src/framework/internal/Components/ClientSceneLogicBase.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ClientSceneLogicBase.h" -#include "Components/ISceneGraphSender.h" -#include "Scene/ClientScene.h" -#include "Scene/SceneDescriber.h" -#include "Scene/SceneActionApplier.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "SceneUtils/ResourceUtils.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Utils/LogMacros.h" -#include "Utils/StatisticCollection.h" -#include "Components/IResourceProviderComponent.h" -#include "Components/SceneUpdate.h" - -namespace ramses_internal +#include "internal/Components/ClientSceneLogicBase.h" +#include "internal/Components/ISceneGraphSender.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/SceneDescriber.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Components/IResourceProviderComponent.h" +#include "internal/Components/SceneUpdate.h" + +namespace ramses::internal { ClientSceneLogicBase::ClientSceneLogicBase(ISceneGraphSender& sceneGraphSender, ClientScene& scene, IResourceProviderComponent& res, const Guid& clientAddress) : m_scenegraphSender(sceneGraphSender) @@ -27,7 +27,6 @@ namespace ramses_internal , m_myID(clientAddress) , m_sceneId(scene.getSceneId()) , m_scene(scene) - , m_scenePublicationMode(EScenePublicationMode_Unpublished) { std::fill(m_resourceCount.begin(), m_resourceCount.end(), 0); std::fill(m_resourceMaxSize.begin(), m_resourceMaxSize.end(), 0); @@ -41,7 +40,7 @@ namespace ramses_internal void ClientSceneLogicBase::publish(EScenePublicationMode publicationMode) { - if (m_scenePublicationMode == EScenePublicationMode_Unpublished) + if (!m_scenePublicationMode.has_value()) { m_scenePublicationMode = publicationMode; m_scenegraphSender.sendPublishScene(m_sceneId, publicationMode, m_scene.getName()); @@ -50,11 +49,11 @@ namespace ramses_internal void ClientSceneLogicBase::unpublish() { - if (m_scenePublicationMode != EScenePublicationMode_Unpublished) + if (m_scenePublicationMode.has_value()) { - m_scenegraphSender.sendUnpublishScene(m_sceneId, m_scenePublicationMode); + m_scenegraphSender.sendUnpublishScene(m_sceneId, *m_scenePublicationMode); - m_scenePublicationMode = EScenePublicationMode_Unpublished; + m_scenePublicationMode.reset(); } // reset to initial state @@ -64,7 +63,7 @@ namespace ramses_internal bool ClientSceneLogicBase::isPublished() const { - return m_scenePublicationMode != EScenePublicationMode_Unpublished; + return m_scenePublicationMode.has_value(); } void ClientSceneLogicBase::addSubscriber(const Guid& newSubscriber) @@ -134,12 +133,13 @@ namespace ramses_internal m_resourceChangesSinceLastFlush.m_resourcesAdded.size() << " client resources, " << m_resourceChangesSinceLastFlush.m_sceneResourceActions.size() << " scene resource actions (" << sceneResourcesSize << " bytes in total used by scene resources)"); + assert(m_scenePublicationMode.has_value()); for(const auto& subscriber : m_subscribersWaitingForScene) { - m_scenegraphSender.sendCreateScene(subscriber, m_sceneId, m_scenePublicationMode); + m_scenegraphSender.sendCreateScene(subscriber, m_sceneId, *m_scenePublicationMode); } m_scene.getStatisticCollection().statSceneActionsSent.incCounter(sceneUpdate.actions.numberOfActions()*static_cast(m_subscribersWaitingForScene.size())); - m_scenegraphSender.sendSceneUpdate(m_subscribersWaitingForScene, std::move(sceneUpdate), m_sceneId, m_scenePublicationMode, m_scene.getStatisticCollection()); + m_scenegraphSender.sendSceneUpdate(m_subscribersWaitingForScene, std::move(sceneUpdate), m_sceneId, *m_scenePublicationMode, m_scene.getStatisticCollection()); m_subscribersActive.insert(m_subscribersActive.end(), m_subscribersWaitingForScene.begin(), m_subscribersWaitingForScene.end()); m_subscribersWaitingForScene.clear(); @@ -153,9 +153,8 @@ namespace ramses_internal " ; ActionCountPerType:\n"; struct ActionInfo { - ActionInfo() : count(0), size(0) {} - uint32_t count; - uint32_t size; + uint32_t count{0}; + uint32_t size{0}; }; std::vector sceneActionCountPerType(NumOfSceneActionTypes); for (const auto& action : update.actions) @@ -177,24 +176,21 @@ namespace ramses_internal const char* ClientSceneLogicBase::getSceneStateString() const { - if (m_subscribersActive.size() > 0) + if (!m_subscribersActive.empty()) { return "Subscribed"; } - else + if (m_scenePublicationMode.has_value()) { - switch (m_scenePublicationMode) + switch (*m_scenePublicationMode) { - case ramses_internal::EScenePublicationMode_Unpublished: - return "Unpublished"; - case ramses_internal::EScenePublicationMode_LocalAndRemote: + case EScenePublicationMode::LocalAndRemote: return "Published"; - case ramses_internal::EScenePublicationMode_LocalOnly: + case EScenePublicationMode::LocalOnly: return "Published_LocalOnly"; - default: - return "Invalid"; } } + return "Unpublished"; } void ClientSceneLogicBase::updateResourceStatistics() @@ -213,20 +209,19 @@ namespace ramses_internal EResourceStatisticIndex index = EResourceStatisticIndex_ArrayResource; switch (info.type) { - case EResourceType_VertexArray: - case EResourceType_IndexArray: + case EResourceType::VertexArray: + case EResourceType::IndexArray: index = EResourceStatisticIndex_ArrayResource; break; - case EResourceType_Effect: + case EResourceType::Effect: index = EResourceStatisticIndex_Effect; break; - case EResourceType_Texture2D: - case EResourceType_Texture3D: - case EResourceType_TextureCube: + case EResourceType::Texture2D: + case EResourceType::Texture3D: + case EResourceType::TextureCube: index = EResourceStatisticIndex_Texture; break; - case EResourceType_Invalid: - case EResourceType_NUMBER_OF_ELEMENTS: + case EResourceType::Invalid: assert(0); } const auto size = info.decompressedSize; @@ -249,6 +244,26 @@ namespace ramses_internal } } + bool ClientSceneLogicBase::updateExpirationAndCheckIfChanged(const FlushTimeInformation& flushTimeInfo) + { + const bool hasExpirationTSChange = (flushTimeInfo.expirationTimestamp != m_lastFlushedExpirationTimestamp); + m_lastFlushedExpirationTimestamp = flushTimeInfo.expirationTimestamp; + + return hasExpirationTSChange; + } + + bool ClientSceneLogicBase::canSkipSceneActionSend(uint32_t numSceneActions, SceneVersionTag versionTag, bool expirationChanged, bool isEffectTimeSync) const + { + return + m_flushCounter != 0 && // never skip first flush (might block renderer side transition subscription pending -> subscibed) + !isEffectTimeSync && // no effect time synchronization + m_resourceChangesSinceLastFlush.empty() && // no resource changes (client+scene) + numSceneActions == 0u && // no other sceneactions yet + m_scene.getSceneReferenceActions().empty() && // no scenereference updates + !expirationChanged && // no expiration monitoring change + versionTag == SceneVersionTag::Invalid(); // no scene version + } + ClientSceneLogicBase::ResourceChangeState ClientSceneLogicBase::verifyAndGetResourceChanges(SceneUpdate& sceneUpdate, bool hasNewActions) { m_resourceChangesSinceLastFlush.clear(); diff --git a/framework/Components/include/Components/ClientSceneLogicBase.h b/src/framework/internal/Components/ClientSceneLogicBase.h similarity index 80% rename from framework/Components/include/Components/ClientSceneLogicBase.h rename to src/framework/internal/Components/ClientSceneLogicBase.h index d88888177..e1d5acc80 100644 --- a/framework/Components/include/Components/ClientSceneLogicBase.h +++ b/src/framework/internal/Components/ClientSceneLogicBase.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTSCENELOGICBASE_H -#define RAMSES_CLIENTSCENELOGICBASE_H +#pragma once -#include "Scene/EScenePublicationMode.h" -#include "Scene/ClientScene.h" -#include "Scene/Scene.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/Scene.h" +#include -namespace ramses_internal +namespace ramses::internal { class ISceneGraphSender; class StatisticCollectionScene; @@ -52,6 +52,8 @@ namespace ramses_internal ResourceChangeState verifyAndGetResourceChanges(SceneUpdate& sceneUpdate, bool hasNewActions); void updateResourceStatistics(); void fillStatisticsCollection(); + bool updateExpirationAndCheckIfChanged(const FlushTimeInformation& flushTimeInfo); + [[nodiscard]] bool canSkipSceneActionSend(uint32_t numSceneActions, SceneVersionTag versionTag, bool expirationChanged, bool isEffectTimeSync) const; ISceneGraphSender& m_scenegraphSender; IResourceProviderComponent& m_resourceComponent; @@ -62,19 +64,18 @@ namespace ramses_internal using AddressVector = std::vector; AddressVector m_subscribersActive; AddressVector m_subscribersWaitingForScene; - EScenePublicationMode m_scenePublicationMode; + std::optional m_scenePublicationMode; uint64_t m_flushCounter = 0u; ResourceContentHashVector m_lastFlushResourcesInUse; + FlushTime::Clock::time_point m_lastFlushedExpirationTimestamp{ FlushTime::InvalidTimestamp }; ResourceChanges m_resourceChangesSinceLastFlush; // keep container memory allocated ResourceContentHashVector m_currentFlushResourcesInUse; // keep container memory allocated // resource statistics gathered while flushing the last time - std::array m_resourceCount; - std::array m_resourceDataSize; - std::array m_resourceMaxSize; + std::array m_resourceCount{}; + std::array m_resourceDataSize{}; + std::array m_resourceMaxSize{}; }; } - -#endif diff --git a/framework/Components/src/ClientSceneLogicDirect.cpp b/src/framework/internal/Components/ClientSceneLogicDirect.cpp similarity index 72% rename from framework/Components/src/ClientSceneLogicDirect.cpp rename to src/framework/internal/Components/ClientSceneLogicDirect.cpp index df61f616b..ab77125e4 100644 --- a/framework/Components/src/ClientSceneLogicDirect.cpp +++ b/src/framework/internal/Components/ClientSceneLogicDirect.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ClientSceneLogicDirect.h" -#include "Components/ISceneGraphSender.h" -#include "Scene/ClientScene.h" -#include "Scene/SceneDescriber.h" -#include "Scene/SceneActionApplier.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Utils/LogMacros.h" -#include "Utils/StatisticCollection.h" -#include "Components/FlushTimeInformation.h" -#include "Components/SceneUpdate.h" -#include "Components/ClientSceneLogicBase.h" -#include "Components/IResourceProviderComponent.h" - -namespace ramses_internal +#include "internal/Components/ClientSceneLogicDirect.h" +#include "internal/Components/ISceneGraphSender.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/SceneDescriber.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Components/ClientSceneLogicBase.h" +#include "internal/Components/IResourceProviderComponent.h" + +namespace ramses::internal { ClientSceneLogicDirect::ClientSceneLogicDirect(ISceneGraphSender& sceneGraphSender, ClientScene& scene, IResourceProviderComponent& res, const Guid& clientAddress) : ClientSceneLogicBase(sceneGraphSender, scene, res, clientAddress) @@ -62,6 +62,9 @@ namespace ramses_internal })); } + const bool expirationChanged = updateExpirationAndCheckIfChanged(flushTimeInfo); + const bool skipSceneActionSend = canSkipSceneActionSend(sceneUpdate.actions.numberOfActions(), versionTag, expirationChanged, flushTimeInfo.isEffectTimeSync); + ++m_flushCounter; if (isPublished()) @@ -84,8 +87,17 @@ namespace ramses_internal if (isPublished() && !m_subscribersActive.empty()) { - m_scene.getStatisticCollection().statSceneActionsSent.incCounter(sceneUpdate.actions.numberOfActions()*static_cast(m_subscribersActive.size())); - m_scenegraphSender.sendSceneUpdate(m_subscribersActive, std::move(sceneUpdate), m_sceneId, m_scenePublicationMode, m_scene.getStatisticCollection()); + if (skipSceneActionSend) + { + LOG_DEBUG(CONTEXT_CLIENT, "ClientSceneLogicDirect::flushSceneActions: skip flush for sceneId " << m_sceneId << ", cnt " << m_flushCounter << " because empty"); + m_scene.getStatisticCollection().statSceneActionsSentSkipped.incCounter(1); + } + else + { + assert(m_scenePublicationMode.has_value()); + m_scene.getStatisticCollection().statSceneActionsSent.incCounter(sceneUpdate.actions.numberOfActions() * static_cast(m_subscribersActive.size())); + m_scenegraphSender.sendSceneUpdate(m_subscribersActive, std::move(sceneUpdate), m_sceneId, *m_scenePublicationMode, m_scene.getStatisticCollection()); + } } m_scene.resetResourceChanges(); diff --git a/framework/Components/include/Components/ClientSceneLogicDirect.h b/src/framework/internal/Components/ClientSceneLogicDirect.h similarity index 83% rename from framework/Components/include/Components/ClientSceneLogicDirect.h rename to src/framework/internal/Components/ClientSceneLogicDirect.h index a5c4acd70..7f26b2ee0 100644 --- a/framework/Components/include/Components/ClientSceneLogicDirect.h +++ b/src/framework/internal/Components/ClientSceneLogicDirect.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTSCENELOGICDIRECT_H -#define RAMSES_CLIENTSCENELOGICDIRECT_H +#pragma once -#include "Components/ClientSceneLogicBase.h" -#include "SceneAPI/SceneSizeInformation.h" +#include "internal/Components/ClientSceneLogicBase.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" -namespace ramses_internal +namespace ramses::internal { class ClientSceneLogicDirect final : public ClientSceneLogicBase { @@ -26,5 +25,3 @@ namespace ramses_internal FlushTime::Clock::time_point m_effectTimeSync{FlushTime::InvalidTimestamp}; }; } - -#endif diff --git a/framework/Components/src/ClientSceneLogicShadowCopy.cpp b/src/framework/internal/Components/ClientSceneLogicShadowCopy.cpp similarity index 81% rename from framework/Components/src/ClientSceneLogicShadowCopy.cpp rename to src/framework/internal/Components/ClientSceneLogicShadowCopy.cpp index 66bebf900..a3cfb1d2e 100644 --- a/framework/Components/src/ClientSceneLogicShadowCopy.cpp +++ b/src/framework/internal/Components/ClientSceneLogicShadowCopy.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ClientSceneLogicShadowCopy.h" -#include "Components/ISceneGraphSender.h" -#include "Scene/ClientScene.h" -#include "Scene/SceneDescriber.h" -#include "Scene/SceneActionApplier.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Utils/LogMacros.h" -#include "Utils/StatisticCollection.h" -#include "Components/FlushTimeInformation.h" -#include "Components/IResourceProviderComponent.h" -#include "Components/SceneUpdate.h" - -namespace ramses_internal +#include "internal/Components/ClientSceneLogicShadowCopy.h" +#include "internal/Components/ISceneGraphSender.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/SceneDescriber.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/IResourceProviderComponent.h" +#include "internal/Components/SceneUpdate.h" + +namespace ramses::internal { ClientSceneLogicShadowCopy::ClientSceneLogicShadowCopy(ISceneGraphSender& sceneGraphSender, ClientScene& scene, IResourceProviderComponent& res, const Guid& clientAddress) : ClientSceneLogicBase(sceneGraphSender, scene, res, clientAddress) @@ -68,17 +68,8 @@ namespace ramses_internal })); } - const bool hasExpirationTSChange = (flushTimeInfo.expirationTimestamp != m_lastFlushedExpirationTimestamp); - m_lastFlushedExpirationTimestamp = flushTimeInfo.expirationTimestamp; - - const bool skipSceneActionSend = - m_flushCounter != 0 && // never skip first flush (might block renderer side transition subscription pending -> subscibed) - !flushTimeInfo.isEffectTimeSync && // no effect time synchronization - m_resourceChangesSinceLastFlush.empty() && // no resource changes (client+scene) - sceneUpdate.actions.empty() && // no other sceneactions yet - m_scene.getSceneReferenceActions().empty() && // no scenereference updates - !hasExpirationTSChange && // no expiration monitoring change - versionTag == SceneVersionTag::Invalid(); // no scene version + const bool expirationChanged = updateExpirationAndCheckIfChanged(flushTimeInfo); + const bool skipSceneActionSend = canSkipSceneActionSend(sceneUpdate.actions.numberOfActions(), versionTag, expirationChanged, flushTimeInfo.isEffectTimeSync); ++m_flushCounter; @@ -107,8 +98,9 @@ namespace ramses_internal } else { + assert(m_scenePublicationMode.has_value()); m_scene.getStatisticCollection().statSceneActionsSent.incCounter(sceneUpdate.actions.numberOfActions() * static_cast(m_subscribersActive.size())); - m_scenegraphSender.sendSceneUpdate(m_subscribersActive, std::move(sceneUpdate), m_sceneId, m_scenePublicationMode, m_scene.getStatisticCollection()); + m_scenegraphSender.sendSceneUpdate(m_subscribersActive, std::move(sceneUpdate), m_sceneId, *m_scenePublicationMode, m_scene.getStatisticCollection()); } } diff --git a/framework/Components/include/Components/ClientSceneLogicShadowCopy.h b/src/framework/internal/Components/ClientSceneLogicShadowCopy.h similarity index 76% rename from framework/Components/include/Components/ClientSceneLogicShadowCopy.h rename to src/framework/internal/Components/ClientSceneLogicShadowCopy.h index 8772f34c6..2cc779d20 100644 --- a/framework/Components/include/Components/ClientSceneLogicShadowCopy.h +++ b/src/framework/internal/Components/ClientSceneLogicShadowCopy.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTSCENELOGICSHADOWCOPY_H -#define RAMSES_CLIENTSCENELOGICSHADOWCOPY_H +#pragma once -#include "Components/ClientSceneLogicBase.h" -#include "Components/FlushTimeInformation.h" -#include "Components/ManagedResource.h" +#include "internal/Components/ClientSceneLogicBase.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/ManagedResource.h" -namespace ramses_internal +namespace ramses::internal { class ClientSceneLogicShadowCopy final : public ClientSceneLogicBase { @@ -31,8 +30,5 @@ namespace ramses_internal FlushTime::Clock::time_point m_effectTimeSync{FlushTime::InvalidTimestamp}; SceneVersionTag m_lastVersionTag; ManagedResourceVector m_lastFlushUsedResources; - ramses_internal::FlushTime::Clock::time_point m_lastFlushedExpirationTimestamp{ ramses_internal::FlushTime::InvalidTimestamp }; }; } - -#endif diff --git a/framework/Components/include/Components/ERendererToClientEventType.h b/src/framework/internal/Components/ERendererToClientEventType.h similarity index 82% rename from framework/Components/include/Components/ERendererToClientEventType.h rename to src/framework/internal/Components/ERendererToClientEventType.h index d2675a72e..98dbb50a4 100644 --- a/framework/Components/include/Components/ERendererToClientEventType.h +++ b/src/framework/internal/Components/ERendererToClientEventType.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMPONENT_ERENDERERTOCLIENTEVENTTYPE_H -#define RAMSES_COMPONENT_ERENDERERTOCLIENTEVENTTYPE_H +#pragma once #include "fmt/format.h" -namespace ramses_internal +namespace ramses::internal { enum class ERendererToClientEventType : uint32_t { @@ -32,15 +31,15 @@ namespace ramses_internal } template <> - struct fmt::formatter { + struct fmt::formatter { template constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template - constexpr auto format(const ramses_internal::ERendererToClientEventType& thetype, FormatContext& ctx) { + constexpr auto format(const ramses::internal::ERendererToClientEventType& thetype, FormatContext& ctx) { return fmt::format_to(ctx.out(), "{}", EnumToString(thetype)); } }; -#endif + diff --git a/framework/Components/src/EffectUniformTime.cpp b/src/framework/internal/Components/EffectUniformTime.cpp similarity index 92% rename from framework/Components/src/EffectUniformTime.cpp rename to src/framework/internal/Components/EffectUniformTime.cpp index b6b163056..5b56d0cd6 100644 --- a/framework/Components/src/EffectUniformTime.cpp +++ b/src/framework/internal/Components/EffectUniformTime.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/EffectUniformTime.h" +#include "internal/Components/EffectUniformTime.h" -namespace ramses_internal +namespace ramses::internal { int32_t EffectUniformTime::GetMilliseconds(FlushTime::Clock::time_point epochBeginning) { diff --git a/framework/Components/include/Components/EffectUniformTime.h b/src/framework/internal/Components/EffectUniformTime.h similarity index 84% rename from framework/Components/include/Components/EffectUniformTime.h rename to src/framework/internal/Components/EffectUniformTime.h index e3a58333c..f76b9489f 100644 --- a/framework/Components/include/Components/EffectUniformTime.h +++ b/src/framework/internal/Components/EffectUniformTime.h @@ -8,10 +8,11 @@ #pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Components/FlushTimeInformation.h" +#include "internal/Components/FlushTimeInformation.h" -namespace ramses_internal +#include + +namespace ramses::internal { class EffectUniformTime { diff --git a/framework/Components/include/Components/FileInputStreamContainer.h b/src/framework/internal/Components/FileInputStreamContainer.h similarity index 78% rename from framework/Components/include/Components/FileInputStreamContainer.h rename to src/framework/internal/Components/FileInputStreamContainer.h index f3f5b225f..8cae5e1fb 100644 --- a/framework/Components/include/Components/FileInputStreamContainer.h +++ b/src/framework/internal/Components/FileInputStreamContainer.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_FILEINPUTSTREAMCONTAINER_H -#define RAMSES_FRAMEWORK_FILEINPUTSTREAMCONTAINER_H +#pragma once -#include "Components/InputStreamContainer.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/File.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/File.h" #include -namespace ramses_internal +namespace ramses::internal { class FileInputStreamContainer : public IInputStreamContainer { @@ -35,5 +34,3 @@ namespace ramses_internal BinaryFileInputStream m_stream; }; } - -#endif diff --git a/framework/Components/include/Components/FlushInformation.h b/src/framework/internal/Components/FlushInformation.h similarity index 87% rename from framework/Components/include/Components/FlushInformation.h rename to src/framework/internal/Components/FlushInformation.h index cbe1cedaa..94ad45382 100644 --- a/framework/Components/include/Components/FlushInformation.h +++ b/src/framework/internal/Components/FlushInformation.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FLUSHINFORMATION_H -#define RAMSES_FLUSHINFORMATION_H +#pragma once -#include "SceneAPI/SceneSizeInformation.h" -#include "Scene/ResourceChanges.h" -#include "SceneReferencing/SceneReferenceAction.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" #include "FlushTimeInformation.h" -#include "SceneAPI/SceneVersionTag.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" -namespace ramses_internal +#include + +namespace ramses::internal { struct FlushInformation { @@ -82,10 +82,10 @@ namespace ramses_internal } template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::FlushInformation& fi, FormatContext& ctx) + constexpr auto format(const ramses::internal::FlushInformation& fi, FormatContext& ctx) { fmt::format_to(ctx.out(), "FlushInformation:[valid:{};flushcounter:{};version:{};resChanges[+:{};-:{};resActions:{}];refActions:{};time[{};sync:{};exp:{};int:{}];sizeInfo:", @@ -94,15 +94,19 @@ struct fmt::formatter : public ramses_interna fi.versionTag, fi.resourceChanges.m_resourcesAdded.size(), fi.resourceChanges.m_resourcesRemoved.size(), fi.resourceChanges.m_sceneResourceActions.size(), fi.sceneReferences.size(), - fi.flushTimeInfo.clock_type, + fmt::underlying(fi.flushTimeInfo.clock_type), (fi.flushTimeInfo.isEffectTimeSync ? 1 : 0), static_cast(std::chrono::time_point_cast(fi.flushTimeInfo.expirationTimestamp).time_since_epoch().count()), static_cast(std::chrono::time_point_cast(fi.flushTimeInfo.internalTimestamp).time_since_epoch().count())); if (fi.hasSizeInfo) + { fmt::format_to(ctx.out(), "{}", fi.sizeInfo); + } else + { fmt::format_to(ctx.out(), "none"); + } return fmt::format_to(ctx.out(), "]"); } }; -#endif + diff --git a/framework/Components/include/Components/FlushTimeInformation.h b/src/framework/internal/Components/FlushTimeInformation.h similarity index 89% rename from framework/Components/include/Components/FlushTimeInformation.h rename to src/framework/internal/Components/FlushTimeInformation.h index 43e761cc7..0f94e07b1 100644 --- a/framework/Components/include/Components/FlushTimeInformation.h +++ b/src/framework/internal/Components/FlushTimeInformation.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FLUSHTIMEINFORMATION_H -#define RAMSES_FLUSHTIMEINFORMATION_H +#pragma once -#include "PlatformAbstraction/synchronized_clock.h" +#include "internal/PlatformAbstraction/synchronized_clock.h" -namespace ramses_internal +namespace ramses::internal { namespace FlushTime { @@ -37,7 +36,7 @@ namespace ramses_internal /** * if set to true, the internalTimestamp is used to update the IScene's effect time - * #ramses_internal::IScene::setEffectTimeSync + * #ramses::internal::IScene::setEffectTimeSync */ bool isEffectTimeSync = false; }; @@ -55,5 +54,3 @@ namespace ramses_internal return !(a == b); } } - -#endif diff --git a/framework/Components/include/Components/IManagedResourceDeleterCallback.h b/src/framework/internal/Components/IManagedResourceDeleterCallback.h similarity index 80% rename from framework/Components/include/Components/IManagedResourceDeleterCallback.h rename to src/framework/internal/Components/IManagedResourceDeleterCallback.h index b2524eb95..48cc2a928 100644 --- a/framework/Components/include/Components/IManagedResourceDeleterCallback.h +++ b/src/framework/internal/Components/IManagedResourceDeleterCallback.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IMANAGEDRESOURCEDELETERCALLBACK_H -#define RAMSES_IMANAGEDRESOURCEDELETERCALLBACK_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "Resource/IResource.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/Resource/IResource.h" -namespace ramses_internal +namespace ramses::internal { class IManagedResourceDeleterCallback { public: - virtual ~IManagedResourceDeleterCallback(){} + virtual ~IManagedResourceDeleterCallback() = default; virtual void managedResourceDeleted(const IResource& resource) = 0; }; @@ -37,5 +36,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/Components/include/Components/IResourceHashUsageCallback.h b/src/framework/internal/Components/IResourceHashUsageCallback.h similarity index 74% rename from framework/Components/include/Components/IResourceHashUsageCallback.h rename to src/framework/internal/Components/IResourceHashUsageCallback.h index 4f667290a..a5d5f1032 100644 --- a/framework/Components/include/Components/IResourceHashUsageCallback.h +++ b/src/framework/internal/Components/IResourceHashUsageCallback.h @@ -6,20 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRESOURCEHASHUSAGECALLBACK_H -#define RAMSES_IRESOURCEHASHUSAGECALLBACK_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" -namespace ramses_internal +namespace ramses::internal { class IResourceHashUsageCallback { public: - virtual ~IResourceHashUsageCallback() {} + virtual ~IResourceHashUsageCallback() = default; virtual void resourceHashUsageZero(const ResourceContentHash& hash) = 0; }; } - -#endif diff --git a/framework/Components/include/Components/IResourceProviderComponent.h b/src/framework/internal/Components/IResourceProviderComponent.h similarity index 81% rename from framework/Components/include/Components/IResourceProviderComponent.h rename to src/framework/internal/Components/IResourceProviderComponent.h index 6d6b33627..5740a7520 100644 --- a/framework/Components/include/Components/IResourceProviderComponent.h +++ b/src/framework/internal/Components/IResourceProviderComponent.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRESOURCEPROVIDERCOMPONENT_H -#define RAMSES_IRESOURCEPROVIDERCOMPONENT_H +#pragma once -#include "Components/ResourceHashUsage.h" -#include "Components/InputStreamContainer.h" -#include "Components/SceneFileHandle.h" +#include "internal/Components/ResourceHashUsage.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Components/SceneFileHandle.h" #include "ManagedResource.h" -#include "Resource/ResourceInfo.h" +#include "internal/SceneGraph/Resource/ResourceInfo.h" -namespace ramses_internal +namespace ramses::internal { class Guid; class ResourceTableOfContents; @@ -23,9 +22,9 @@ namespace ramses_internal class IResourceProviderComponent { public: - virtual ~IResourceProviderComponent() {} + virtual ~IResourceProviderComponent() = default; - virtual ManagedResource manageResource(const IResource& resource, bool deletionAllowed = false) = 0; + virtual ManagedResource manageResource(const IResource& resource) = 0; virtual ManagedResource getResource(ResourceContentHash hash) = 0; virtual ManagedResource loadResource(const ResourceContentHash& hash) = 0; @@ -43,5 +42,3 @@ namespace ramses_internal [[nodiscard]] virtual bool knowsResource(const ResourceContentHash& hash) const = 0; }; } - -#endif diff --git a/framework/Components/include/Components/ISceneGraphConsumerComponent.h b/src/framework/internal/Components/ISceneGraphConsumerComponent.h similarity index 80% rename from framework/Components/include/Components/ISceneGraphConsumerComponent.h rename to src/framework/internal/Components/ISceneGraphConsumerComponent.h index 911f46cc1..c6a73f9e7 100644 --- a/framework/Components/include/Components/ISceneGraphConsumerComponent.h +++ b/src/framework/internal/Components/ISceneGraphConsumerComponent.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENEGRAPHCONSUMERCOMPONENT_H -#define RAMSES_ISCENEGRAPHCONSUMERCOMPONENT_H +#pragma once -#include "Collections/Guid.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneTypes.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" -namespace ramses_internal +namespace ramses::internal { class ISceneRendererHandler; struct SceneReferenceEvent; @@ -22,7 +21,7 @@ namespace ramses_internal class ISceneGraphConsumerComponent { public: - virtual ~ISceneGraphConsumerComponent() {} + virtual ~ISceneGraphConsumerComponent() = default; virtual void setSceneRendererHandler(ISceneRendererHandler* sceneRendererHandler) = 0; virtual void subscribeScene(const Guid& to, SceneId sceneId) = 0; @@ -31,5 +30,3 @@ namespace ramses_internal virtual void sendResourceAvailabilityEvent(const Guid& to, ResourceAvailabilityEvent const& event) = 0; }; } - -#endif diff --git a/framework/Components/include/Components/ISceneGraphProviderComponent.h b/src/framework/internal/Components/ISceneGraphProviderComponent.h similarity index 76% rename from framework/Components/include/Components/ISceneGraphProviderComponent.h rename to src/framework/internal/Components/ISceneGraphProviderComponent.h index f84978e31..ba944fd6d 100644 --- a/framework/Components/include/Components/ISceneGraphProviderComponent.h +++ b/src/framework/internal/Components/ISceneGraphProviderComponent.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENEGRAPHPROVIDERCOMPONENT_H -#define RAMSES_ISCENEGRAPHPROVIDERCOMPONENT_H +#pragma once -#include "Scene/EScenePublicationMode.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneVersionTag.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" -namespace ramses_internal +namespace ramses::internal { class Guid; class ClientScene; @@ -26,7 +25,7 @@ namespace ramses_internal class ISceneGraphProviderComponent { public: - virtual ~ISceneGraphProviderComponent() {} + virtual ~ISceneGraphProviderComponent() = default; virtual void handleCreateScene(ClientScene& scene, bool enableLocalOnlyOptimization, ISceneProviderEventConsumer& eventInterface) = 0; virtual void handlePublishScene(SceneId sceneId, EScenePublicationMode publicationMode) = 0; virtual void handleUnpublishScene(SceneId sceneId) = 0; @@ -34,5 +33,3 @@ namespace ramses_internal virtual void handleRemoveScene(SceneId sceneId) = 0; }; } - -#endif diff --git a/framework/Components/include/Components/ISceneGraphSender.h b/src/framework/internal/Components/ISceneGraphSender.h similarity index 87% rename from framework/Components/include/Components/ISceneGraphSender.h rename to src/framework/internal/Components/ISceneGraphSender.h index 79966df85..61defd7d5 100644 --- a/framework/Components/include/Components/ISceneGraphSender.h +++ b/src/framework/internal/Components/ISceneGraphSender.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENEGRAPHSENDER_H -#define RAMSES_ISCENEGRAPHSENDER_H +#pragma once -#include "SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include -namespace ramses_internal +namespace ramses::internal { class Guid; struct SceneUpdate; @@ -22,12 +21,10 @@ namespace ramses_internal class ISceneGraphSender { public: - virtual ~ISceneGraphSender() {} + virtual ~ISceneGraphSender() = default; virtual void sendPublishScene (SceneId sceneId, EScenePublicationMode publicationMode, std::string_view name) = 0; virtual void sendUnpublishScene (SceneId sceneId, EScenePublicationMode publicationMode) = 0; virtual void sendCreateScene (const Guid& to, const SceneId& sceneId, EScenePublicationMode publicationMode) = 0; virtual void sendSceneUpdate (const std::vector& to, SceneUpdate&& sceneUpdate, SceneId sceneId, EScenePublicationMode mode, StatisticCollectionScene& sceneStatistics) = 0; }; } - -#endif diff --git a/framework/Components/include/Components/ISceneProviderEventConsumer.h b/src/framework/internal/Components/ISceneProviderEventConsumer.h similarity index 88% rename from framework/Components/include/Components/ISceneProviderEventConsumer.h rename to src/framework/internal/Components/ISceneProviderEventConsumer.h index d6f4b5680..6a37161ae 100644 --- a/framework/Components/include/Components/ISceneProviderEventConsumer.h +++ b/src/framework/internal/Components/ISceneProviderEventConsumer.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENEPROVIDEREVENTCONSUMER_H -#define RAMSES_ISCENEPROVIDEREVENTCONSUMER_H +#pragma once -namespace ramses_internal +namespace ramses::internal { struct SceneReferenceEvent; struct ResourceAvailabilityEvent; @@ -24,5 +23,3 @@ namespace ramses_internal virtual void handleResourceAvailabilityEvent(ResourceAvailabilityEvent const& event, const Guid& rendererId) = 0; }; } - -#endif diff --git a/framework/Components/include/Components/ISceneRendererHandler.h b/src/framework/internal/Components/ISceneRendererHandler.h similarity index 88% rename from framework/Components/include/Components/ISceneRendererHandler.h rename to src/framework/internal/Components/ISceneRendererHandler.h index 529cf009c..7c93e3ba9 100644 --- a/framework/Components/include/Components/ISceneRendererHandler.h +++ b/src/framework/internal/Components/ISceneRendererHandler.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENERENDERERHANDLER_H -#define RAMSES_ISCENERENDERERHANDLER_H +#pragma once -#include "SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" -namespace ramses_internal +namespace ramses::internal { class Guid; struct SceneUpdate; @@ -27,5 +26,3 @@ namespace ramses_internal virtual void handleSceneBecameUnavailable(const SceneId& unavailableScene, const Guid& providerID) = 0; }; } - -#endif diff --git a/framework/Components/include/Components/InputStreamContainer.h b/src/framework/internal/Components/InputStreamContainer.h similarity index 84% rename from framework/Components/include/Components/InputStreamContainer.h rename to src/framework/internal/Components/InputStreamContainer.h index e8583defa..60467c80f 100644 --- a/framework/Components/include/Components/InputStreamContainer.h +++ b/src/framework/internal/Components/InputStreamContainer.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_INPUTSTREAMCONTAINER_H -#define RAMSES_FRAMEWORK_INPUTSTREAMCONTAINER_H +#pragma once -#include "Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" -namespace ramses_internal +namespace ramses::internal { class IInputStreamContainer { @@ -28,5 +27,3 @@ namespace ramses_internal using InputStreamContainerSPtr = std::shared_ptr; } - -#endif diff --git a/framework/Components/include/Components/ManagedResource.h b/src/framework/internal/Components/ManagedResource.h similarity index 79% rename from framework/Components/include/Components/ManagedResource.h rename to src/framework/internal/Components/ManagedResource.h index 6423e814d..99714e30e 100644 --- a/framework/Components/include/Components/ManagedResource.h +++ b/src/framework/internal/Components/ManagedResource.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MANAGEDRESOURCE_H -#define RAMSES_MANAGEDRESOURCE_H +#pragma once -#include "Resource/IResource.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include -namespace ramses_internal +namespace ramses::internal { using ManagedResource = std::shared_ptr; using ManagedResourceVector = std::vector; } - -#endif diff --git a/framework/Components/include/Components/MemoryInputStreamContainer.h b/src/framework/internal/Components/MemoryInputStreamContainer.h similarity index 68% rename from framework/Components/include/Components/MemoryInputStreamContainer.h rename to src/framework/internal/Components/MemoryInputStreamContainer.h index 5fa1b1e23..c37608782 100644 --- a/framework/Components/include/Components/MemoryInputStreamContainer.h +++ b/src/framework/internal/Components/MemoryInputStreamContainer.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_MEMORYINPUTSTREAMCONTAINER_H -#define RAMSES_FRAMEWORK_MEMORYINPUTSTREAMCONTAINER_H +#pragma once -#include "Components/InputStreamContainer.h" -#include "Utils/BinaryInputStream.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Core/Utils/BinaryInputStream.h" #include -namespace ramses_internal +namespace ramses::internal { class MemoryInputStreamContainer : public IInputStreamContainer { public: // NOLINTNEXTLINE(modernize-avoid-c-arrays) - explicit MemoryInputStreamContainer(std::unique_ptr data) + explicit MemoryInputStreamContainer(std::unique_ptr data) : m_data(std::move(data)) , m_stream(m_data.get()) { @@ -32,9 +31,7 @@ namespace ramses_internal private: // NOLINTNEXTLINE(modernize-avoid-c-arrays) - const std::unique_ptr m_data; + const std::unique_ptr m_data; BinaryInputStream m_stream; }; } - -#endif diff --git a/framework/Components/include/Components/OffsetFileInputStreamContainer.h b/src/framework/internal/Components/OffsetFileInputStreamContainer.h similarity index 78% rename from framework/Components/include/Components/OffsetFileInputStreamContainer.h rename to src/framework/internal/Components/OffsetFileInputStreamContainer.h index 6cdff1517..5c2d2813b 100644 --- a/framework/Components/include/Components/OffsetFileInputStreamContainer.h +++ b/src/framework/internal/Components/OffsetFileInputStreamContainer.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_OFFSETFILEINPUTSTREAMCONTAINER_H -#define RAMSES_FRAMEWORK_OFFSETFILEINPUTSTREAMCONTAINER_H +#pragma once -#include "Components/InputStreamContainer.h" -#include "Utils/BinaryOffsetFileInputStream.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Core/Utils/BinaryOffsetFileInputStream.h" -namespace ramses_internal +namespace ramses::internal { class OffsetFileInputStreamContainer : public IInputStreamContainer { @@ -30,5 +29,3 @@ namespace ramses_internal BinaryOffsetFileInputStream m_stream; }; } - -#endif diff --git a/framework/Components/src/ResourceAvailabilityEvent.cpp b/src/framework/internal/Components/ResourceAvailabilityEvent.cpp similarity index 71% rename from framework/Components/src/ResourceAvailabilityEvent.cpp rename to src/framework/internal/Components/ResourceAvailabilityEvent.cpp index a7b41c2a1..46c9acdae 100644 --- a/framework/Components/src/ResourceAvailabilityEvent.cpp +++ b/src/framework/internal/Components/ResourceAvailabilityEvent.cpp @@ -7,28 +7,29 @@ // ------------------------------------------------------------------------- #include -#include "Utils/BinaryInputStream.h" -#include "Utils/BinaryOutputStream.h" -#include "Components/ResourceAvailabilityEvent.h" -#include "Components/ERendererToClientEventType.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Components/ResourceAvailabilityEvent.h" +#include "internal/Components/ERendererToClientEventType.h" -namespace ramses_internal +namespace ramses::internal { template - inline void write(std::vector::iterator& where, T const& value) + inline void write(std::vector::iterator& where, T const& value) { std::memcpy(&(*where), &value, sizeof(T)); where += sizeof(T); } template - inline void read(std::vector::const_iterator& where, T& value) + inline void read(std::vector::const_iterator& where, T& value) { - std::memcpy(&value, &(*where), sizeof(T)); + // TODO static_cast is only needed to fix GCC bug until version 11.1 + std::memcpy(&value, static_cast(&(*where)), sizeof(T)); where += sizeof(T); } - void ResourceAvailabilityEvent::readFromBlob(std::vector const& blob) + void ResourceAvailabilityEvent::readFromBlob(std::vector const& blob) { auto it = blob.cbegin(); ERendererToClientEventType eventType; @@ -48,7 +49,7 @@ namespace ramses_internal assert(it == blob.cend()); } - void ResourceAvailabilityEvent::writeToBlob(std::vector& blob) const + void ResourceAvailabilityEvent::writeToBlob(std::vector& blob) const { assert(blob.empty()); diff --git a/framework/Components/include/Components/ResourceAvailabilityEvent.h b/src/framework/internal/Components/ResourceAvailabilityEvent.h similarity index 67% rename from framework/Components/include/Components/ResourceAvailabilityEvent.h rename to src/framework/internal/Components/ResourceAvailabilityEvent.h index 46ebf02dd..5b131b4ad 100644 --- a/framework/Components/include/Components/ResourceAvailabilityEvent.h +++ b/src/framework/internal/Components/ResourceAvailabilityEvent.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEAVAILABILITYEVENT_H -#define RAMSES_RESOURCEAVAILABILITYEVENT_H +#pragma once -#include "SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include -#include "SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" -namespace ramses_internal +namespace ramses::internal { struct ResourceAvailabilityEvent { SceneId sceneid; std::vector availableResources; - void readFromBlob(std::vector const& blob); - void writeToBlob(std::vector& blob) const; + void readFromBlob(std::vector const& blob); + void writeToBlob(std::vector& blob) const; }; } - -#endif diff --git a/framework/Components/src/ResourceComponent.cpp b/src/framework/internal/Components/ResourceComponent.cpp similarity index 84% rename from framework/Components/src/ResourceComponent.cpp rename to src/framework/internal/Components/ResourceComponent.cpp index 77bfb2304..67e31f26b 100644 --- a/framework/Components/src/ResourceComponent.cpp +++ b/src/framework/internal/Components/ResourceComponent.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourceComponent.h" -#include "Components/ResourceTableOfContents.h" -#include "Components/ResourceFilesRegistry.h" -#include "Components/SceneFileHandle.h" -#include "Utils/LogMacros.h" +#include "internal/Components/ResourceComponent.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/Components/ResourceFilesRegistry.h" +#include "internal/Components/SceneFileHandle.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { ResourceComponent::ResourceComponent(StatisticCollectionFramework& statistics, PlatformLock& frameworkLock) : m_resourceStorage(frameworkLock, statistics) @@ -20,16 +20,14 @@ namespace ramses_internal { } - ResourceComponent::~ResourceComponent() - { - } + ResourceComponent::~ResourceComponent() = default; - ramses_internal::ManagedResource ResourceComponent::getResource(ResourceContentHash hash) + ManagedResource ResourceComponent::getResource(ResourceContentHash hash) { return m_resourceStorage.getResource(hash); } - ramses_internal::ResourceHashUsage ResourceComponent::getResourceHashUsage(const ResourceContentHash& hash) + ResourceHashUsage ResourceComponent::getResourceHashUsage(const ResourceContentHash& hash) { return m_resourceStorage.getResourceHashUsage(hash); } @@ -44,12 +42,17 @@ namespace ramses_internal return m_resourceStorage.knowsResource(hash); } - ramses_internal::ManagedResource ResourceComponent::manageResource(const IResource& resource, bool deletionAllowed) + ManagedResource ResourceComponent::manageResource(const IResource& resource) + { + return m_resourceStorage.manageResource(resource, false); + } + + ManagedResource ResourceComponent::manageResourceDeletionAllowed(const IResource& resource) { - return m_resourceStorage.manageResource(resource, deletionAllowed); + return m_resourceStorage.manageResource(resource, true); } - SceneFileHandle ResourceComponent::addResourceFile(InputStreamContainerSPtr resourceFileInputStream, const ramses_internal::ResourceTableOfContents& toc) + SceneFileHandle ResourceComponent::addResourceFile(InputStreamContainerSPtr resourceFileInputStream, const ResourceTableOfContents& toc) { for (const auto& item : toc.getFileContents()) { @@ -139,7 +142,7 @@ namespace ramses_internal m_resourceStorage.reserveResourceCount(totalCount); } - ramses_internal::ManagedResourceVector ResourceComponent::resolveResources(ResourceContentHashVector& hashes) + ManagedResourceVector ResourceComponent::resolveResources(ResourceContentHashVector& hashes) { ManagedResourceVector result; result.reserve(hashes.size()); @@ -151,9 +154,13 @@ namespace ramses_internal mr = loadResource(hash); if (mr) + { result.push_back(mr); + } else + { failed.push_back(hash); + } } if (!failed.empty()) diff --git a/framework/Components/include/Components/ResourceComponent.h b/src/framework/internal/Components/ResourceComponent.h similarity index 86% rename from framework/Components/include/Components/ResourceComponent.h rename to src/framework/internal/Components/ResourceComponent.h index 66af231a3..75f814f82 100644 --- a/framework/Components/include/Components/ResourceComponent.h +++ b/src/framework/internal/Components/ResourceComponent.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCECOMPONENT_H -#define RAMSES_RESOURCECOMPONENT_H +#pragma once #include "ResourceStorage.h" -#include "Components/ResourceHashUsage.h" +#include "internal/Components/ResourceHashUsage.h" #include "ResourceFilesRegistry.h" #include "IResourceProviderComponent.h" -#include "Utils/StatisticCollection.h" +#include "internal/Core/Utils/StatisticCollection.h" -namespace ramses_internal +namespace ramses::internal { class ResourceComponent : public IResourceProviderComponent { @@ -25,12 +24,12 @@ namespace ramses_internal ~ResourceComponent() override; // implement IResourceProviderComponent - ManagedResource manageResource(const IResource& resource, bool deletionAllowed = false) override; + ManagedResource manageResource(const IResource& resource) override; ManagedResource getResource(ResourceContentHash hash) override; ManagedResource loadResource(const ResourceContentHash& hash) override; ResourceHashUsage getResourceHashUsage(const ResourceContentHash& hash) override; - SceneFileHandle addResourceFile(InputStreamContainerSPtr resourceFileInputStream, const ramses_internal::ResourceTableOfContents& toc) override; + SceneFileHandle addResourceFile(InputStreamContainerSPtr resourceFileInputStream, const ResourceTableOfContents& toc) override; void loadResourceFromFile(SceneFileHandle handle) override; void removeResourceFile(SceneFileHandle handle) override; [[nodiscard]] bool hasResourceFile(SceneFileHandle handle) const override; @@ -43,6 +42,7 @@ namespace ramses_internal ManagedResourceVector getResources(); + ManagedResource manageResourceDeletionAllowed(const IResource& resource); private: ResourceStorage m_resourceStorage; @@ -51,5 +51,3 @@ namespace ramses_internal StatisticCollectionFramework& m_statistics; }; } - -#endif diff --git a/framework/Components/include/Components/ResourceDeleterCallingCallback.h b/src/framework/internal/Components/ResourceDeleterCallingCallback.h similarity index 88% rename from framework/Components/include/Components/ResourceDeleterCallingCallback.h rename to src/framework/internal/Components/ResourceDeleterCallingCallback.h index a51091fd6..db4e07f8f 100644 --- a/framework/Components/include/Components/ResourceDeleterCallingCallback.h +++ b/src/framework/internal/Components/ResourceDeleterCallingCallback.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEDELETERCALLINGCALLBACK_H -#define RAMSES_RESOURCEDELETERCALLINGCALLBACK_H +#pragma once #include "IManagedResourceDeleterCallback.h" -namespace ramses_internal +namespace ramses::internal { class ResourceDeleterCallingCallback { @@ -31,5 +30,3 @@ namespace ramses_internal IManagedResourceDeleterCallback& m_callback; }; } - -#endif diff --git a/framework/Components/include/Components/ResourceFilesRegistry.h b/src/framework/internal/Components/ResourceFilesRegistry.h similarity index 90% rename from framework/Components/include/Components/ResourceFilesRegistry.h rename to src/framework/internal/Components/ResourceFilesRegistry.h index eff323ab2..403cccd52 100644 --- a/framework/Components/include/Components/ResourceFilesRegistry.h +++ b/src/framework/internal/Components/ResourceFilesRegistry.h @@ -6,31 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEFILESREGISTRY_H -#define RAMSES_RESOURCEFILESREGISTRY_H +#pragma once #include "ResourceStorage.h" #include "ManagedResource.h" -#include "Components/ResourceHashUsage.h" +#include "internal/Components/ResourceHashUsage.h" #include "ResourceTableOfContents.h" -#include "Components/InputStreamContainer.h" -#include "Utils/File.h" -#include "Collections/Pair.h" -#include "Components/SceneFileHandle.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Core/Utils/File.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "internal/Components/SceneFileHandle.h" #include #include -namespace ramses_internal +namespace ramses::internal { struct ResourceRegistryEntry { ResourceFileEntry fileEntry; ResourceHashUsage hashUsage; - ResourceRegistryEntry(const ResourceFileEntry& fileEntry_, const ResourceHashUsage& hashUsage_) + ResourceRegistryEntry(const ResourceFileEntry& fileEntry_, ResourceHashUsage hashUsage_) : fileEntry(fileEntry_) - , hashUsage(hashUsage_) + , hashUsage(std::move(hashUsage_)) {} }; @@ -103,5 +102,3 @@ namespace ramses_internal return EStatus::NotExist; } } - -#endif diff --git a/framework/Components/include/Components/ResourceHashUsage.h b/src/framework/internal/Components/ResourceHashUsage.h similarity index 88% rename from framework/Components/include/Components/ResourceHashUsage.h rename to src/framework/internal/Components/ResourceHashUsage.h index 756c2a751..be2e0a827 100644 --- a/framework/Components/include/Components/ResourceHashUsage.h +++ b/src/framework/internal/Components/ResourceHashUsage.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEHASHUSAGE_H -#define RAMSES_RESOURCEHASHUSAGE_H +#pragma once -#include "Resource/IResource.h" +#include "internal/SceneGraph/Resource/IResource.h" #include "ResourceHashUsageCallback.h" #include -namespace ramses_internal +namespace ramses::internal { class ResourceHashUsage { public: - ResourceHashUsage() - {} + ResourceHashUsage() = default; + ResourceHashUsage(const ResourceContentHash& hash, ResourceHashUsageCallback& deleter) : m_resource(&hash, deleter) { @@ -49,5 +48,3 @@ namespace ramses_internal std::shared_ptr m_resource; }; } - -#endif diff --git a/framework/Components/include/Components/ResourceHashUsageCallback.h b/src/framework/internal/Components/ResourceHashUsageCallback.h similarity index 88% rename from framework/Components/include/Components/ResourceHashUsageCallback.h rename to src/framework/internal/Components/ResourceHashUsageCallback.h index 7324c0701..23d984765 100644 --- a/framework/Components/include/Components/ResourceHashUsageCallback.h +++ b/src/framework/internal/Components/ResourceHashUsageCallback.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEHASHUSAGECALLBACK_H -#define RAMSES_RESOURCEHASHUSAGECALLBACK_H +#pragma once #include "IResourceHashUsageCallback.h" -namespace ramses_internal +namespace ramses::internal { class ResourceHashUsageCallback { @@ -31,5 +30,3 @@ namespace ramses_internal IResourceHashUsageCallback& m_callback; }; } - -#endif diff --git a/framework/Components/src/ResourcePersistation.cpp b/src/framework/internal/Components/ResourcePersistation.cpp similarity index 85% rename from framework/Components/src/ResourcePersistation.cpp rename to src/framework/internal/Components/ResourcePersistation.cpp index 6f76c4cf6..dc024f7d7 100644 --- a/framework/Components/src/ResourcePersistation.cpp +++ b/src/framework/internal/Components/ResourcePersistation.cpp @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourcePersistation.h" - -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "Utils/File.h" -#include "Utils/VoidOutputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Components/ManagedResource.h" -#include "Components/ResourceTableOfContents.h" -#include "Resource/ResourceInfo.h" -#include "Resource/IResource.h" -#include "Components/SingleResourceSerialization.h" -#include "Utils/LogMacros.h" - -namespace ramses_internal +#include "internal/Components/ResourcePersistation.h" + +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/VoidOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/SceneGraph/Resource/ResourceInfo.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/Components/SingleResourceSerialization.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal { void ResourcePersistation::WriteOneResourceToStream(IOutputStream& outStream, const ManagedResource& resource) { @@ -33,7 +33,7 @@ namespace ramses_internal return SingleResourceSerialization::DeserializeResource(inStream, hash); } - void ResourcePersistation::WriteNamedResourcesWithTOCToStream(BinaryFileOutputStream& outStream, const ManagedResourceVector& resourcesForFile, bool compress) + void ResourcePersistation::WriteNamedResourcesWithTOCToStream(IOutputStream& outStream, const ManagedResourceVector& resourcesForFile, bool compress) { // achieve maximum resource file loading speed by reading in increasing file position order // so store TOC first followed by all resources, as the toc is read before the resources diff --git a/framework/Components/include/Components/ResourcePersistation.h b/src/framework/internal/Components/ResourcePersistation.h similarity index 76% rename from framework/Components/include/Components/ResourcePersistation.h rename to src/framework/internal/Components/ResourcePersistation.h index 0b11168cb..8390035f1 100644 --- a/framework/Components/include/Components/ResourcePersistation.h +++ b/src/framework/internal/Components/ResourcePersistation.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEPERSISTATION_H -#define RAMSES_RESOURCEPERSISTATION_H +#pragma once -#include "Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include "ManagedResource.h" -#include "Collections/Pair.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" #include -namespace ramses_internal +namespace ramses::internal { class IOutputStream; class IInputStream; @@ -24,12 +23,10 @@ namespace ramses_internal class ResourcePersistation { public: - static void WriteNamedResourcesWithTOCToStream(BinaryFileOutputStream& outStream, const ManagedResourceVector& resourcesForFile, bool compress); + static void WriteNamedResourcesWithTOCToStream(IOutputStream& outStream, const ManagedResourceVector& resourcesForFile, bool compress); static void WriteOneResourceToStream(IOutputStream& outStream, const ManagedResource& resource); static std::unique_ptr ReadOneResourceFromStream(IInputStream& inStream, const ResourceContentHash& hash); static std::unique_ptr RetrieveResourceFromStream(IInputStream& inStream, const ResourceFileEntry& entry); }; } - -#endif diff --git a/framework/Components/src/ResourceSerializationHelper.cpp b/src/framework/internal/Components/ResourceSerializationHelper.cpp similarity index 69% rename from framework/Components/src/ResourceSerializationHelper.cpp rename to src/framework/internal/Components/ResourceSerializationHelper.cpp index 51278e574..f022b1321 100644 --- a/framework/Components/src/ResourceSerializationHelper.cpp +++ b/src/framework/internal/Components/ResourceSerializationHelper.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourceSerializationHelper.h" -#include "Utils/VoidOutputStream.h" -#include "Resource/TextureResource.h" -#include "Resource/ArrayResource.h" -#include "Resource/EffectResource.h" -#include "Utils/LogMacros.h" +#include "internal/Components/ResourceSerializationHelper.h" +#include "internal/Core/Utils/VoidOutputStream.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses_internal +namespace ramses::internal { namespace ResourceSerializationHelper { @@ -25,10 +25,9 @@ namespace ramses_internal output << resource.getName(); // prefer compressed if available - output << static_cast(resource.isCompressedAvailable() ? EResourceCompressionStatus_Compressed : EResourceCompressionStatus_Uncompressed); + output << static_cast(resource.isCompressedAvailable() ? EResourceCompressionStatus::Compressed : EResourceCompressionStatus::Uncompressed); output << resource.getCompressedDataSize(); output << resource.getDecompressedDataSize(); - output << resource.getCacheFlag().getValue(); resource.serializeResourceMetadataToStream(output); } @@ -47,33 +46,30 @@ namespace ramses_internal uint32_t compressionStatusValue = 0; uint32_t compressedSize = 0; uint32_t decompressedSize = 0; - uint32_t cacheFlagValue = 0; input >> resourceTypeValue; input >> name; input >> compressionStatusValue; input >> compressedSize; input >> decompressedSize; - input >> cacheFlagValue; - const ResourceCacheFlag cacheFlag(cacheFlagValue); - const EResourceType resourceType = static_cast(resourceTypeValue); - const EResourceCompressionStatus compressionStatus = static_cast(compressionStatusValue); + const auto resourceType = static_cast(resourceTypeValue); + const auto compressionStatus = static_cast(compressionStatusValue); std::unique_ptr resource; switch (resourceType) { - case EResourceType_Texture2D: - case EResourceType_Texture3D: - case EResourceType_TextureCube: - resource = TextureResource::CreateResourceFromMetadataStream(input, resourceType, cacheFlag, name); + case EResourceType::Texture2D: + case EResourceType::Texture3D: + case EResourceType::TextureCube: + resource = TextureResource::CreateResourceFromMetadataStream(input, resourceType, name); break; - case EResourceType_VertexArray: - case EResourceType_IndexArray: - resource = ArrayResource::CreateResourceFromMetadataStream(input, cacheFlag, resourceType, name); + case EResourceType::VertexArray: + case EResourceType::IndexArray: + resource = ArrayResource::CreateResourceFromMetadataStream(input, resourceType, name); break; - case EResourceType_Effect: - resource = EffectResource::CreateResourceFromMetadataStream(input, cacheFlag, name); + case EResourceType::Effect: + resource = EffectResource::CreateResourceFromMetadataStream(input, name); break; default: LOG_ERROR_P(CONTEXT_FRAMEWORK, "ResourceSerializationHelper::ResourceFromMetadataStream: Failed for unknown resource type {}", resourceType); diff --git a/framework/Components/include/Components/ResourceSerializationHelper.h b/src/framework/internal/Components/ResourceSerializationHelper.h similarity index 72% rename from framework/Components/include/Components/ResourceSerializationHelper.h rename to src/framework/internal/Components/ResourceSerializationHelper.h index 7d7bfe4a6..53c68fcf3 100644 --- a/framework/Components/include/Components/ResourceSerializationHelper.h +++ b/src/framework/internal/Components/ResourceSerializationHelper.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCESERIALIZATIONHELPER_H -#define RAMSES_RESOURCESERIALIZATIONHELPER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Resource/EResourceCompressionStatus.h" -#include "Resource/IResource.h" +#include "internal/SceneGraph/Resource/EResourceCompressionStatus.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include -namespace ramses_internal +namespace ramses::internal { class IOutputStream; class IInputStream; @@ -25,9 +24,9 @@ namespace ramses_internal struct DeserializedResourceHeader { std::unique_ptr resource; - EResourceCompressionStatus compressionStatus; - uint32_t decompressedSize; - uint32_t compressedSize; + EResourceCompressionStatus compressionStatus = EResourceCompressionStatus::Uncompressed; + uint32_t decompressedSize = 0; + uint32_t compressedSize = 0; }; void SerializeResourceMetadata(IOutputStream& output, const IResource& resource); @@ -36,5 +35,3 @@ namespace ramses_internal DeserializedResourceHeader ResourceFromMetadataStream(IInputStream& input); } } - -#endif diff --git a/framework/Components/src/ResourceStorage.cpp b/src/framework/internal/Components/ResourceStorage.cpp similarity index 95% rename from framework/Components/src/ResourceStorage.cpp rename to src/framework/internal/Components/ResourceStorage.cpp index 473d7c5a9..94453e703 100644 --- a/framework/Components/src/ResourceStorage.cpp +++ b/src/framework/internal/Components/ResourceStorage.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourceStorage.h" -#include "Components/ResourceDeleterCallingCallback.h" -#include "Utils/LogMacros.h" +#include "internal/Components/ResourceStorage.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { ResourceStorage::ResourceStorage(PlatformLock& lockToUse, StatisticCollectionFramework& statistics) : m_resourceMapLock(lockToUse) @@ -50,7 +50,7 @@ namespace ramses_internal return result; } - ramses_internal::ManagedResource ResourceStorage::getResource(ResourceContentHash hash) + ManagedResource ResourceStorage::getResource(ResourceContentHash hash) { PlatformGuard lock(m_resourceMapLock); RefCntResource* entry = m_resourceMap.get(hash); @@ -79,7 +79,7 @@ namespace ramses_internal } - ramses_internal::ResourceHashUsage ResourceStorage::getResourceHashUsage(const ResourceContentHash& hash) + ResourceHashUsage ResourceStorage::getResourceHashUsage(const ResourceContentHash& hash) { ResourceHashUsageCallback deleter(*this); ResourceContentHash* hashObjectToUse = nullptr; @@ -138,7 +138,7 @@ namespace ramses_internal return entry->resourceInfo; } - ramses_internal::ManagedResource ResourceStorage::manageResource(const IResource& resource, bool deletionAllowed) + ManagedResource ResourceStorage::manageResource(const IResource& resource, bool deletionAllowed) { ResourceDeleterCallingCallback deleter(*this); diff --git a/framework/Components/include/Components/ResourceStorage.h b/src/framework/internal/Components/ResourceStorage.h similarity index 72% rename from framework/Components/include/Components/ResourceStorage.h rename to src/framework/internal/Components/ResourceStorage.h index d80a5c158..e7dbe52db 100644 --- a/framework/Components/include/Components/ResourceStorage.h +++ b/src/framework/internal/Components/ResourceStorage.h @@ -6,31 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCESTORAGE_H -#define RAMSES_RESOURCESTORAGE_H +#pragma once -#include "PlatformAbstraction/PlatformLock.h" -#include "Components/ManagedResource.h" -#include "Components/IManagedResourceDeleterCallback.h" -#include "Components/ResourceHashUsage.h" -#include "Components/IResourceHashUsageCallback.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Collections/HashMap.h" -#include "Resource/ResourceInfo.h" -#include "Utils/StatisticCollection.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/IManagedResourceDeleterCallback.h" +#include "internal/Components/ResourceHashUsage.h" +#include "internal/Components/IResourceHashUsageCallback.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/Resource/ResourceInfo.h" +#include "internal/Core/Utils/StatisticCollection.h" -namespace ramses_internal +namespace ramses::internal { class ResourceStorage: public IManagedResourceDeleterCallback, public IResourceHashUsageCallback { struct RefCntResource { - int hashUsages; - int refCount; - ResourceContentHash* hash; - const IResource* resource; - ResourceInfo resourceInfo; - bool deletionAllowed; + int hashUsages{1}; + int refCount{0}; + ResourceContentHash* hash{nullptr}; + const IResource* resource{nullptr}; + ResourceInfo resourceInfo; + bool deletionAllowed{false}; }; public: explicit ResourceStorage(PlatformLock& lockToUse, StatisticCollectionFramework& statistics); @@ -64,5 +63,3 @@ namespace ramses_internal ResourceMap m_resourceMap; }; } - -#endif diff --git a/framework/Components/src/ResourceTableOfContents.cpp b/src/framework/internal/Components/ResourceTableOfContents.cpp similarity index 92% rename from framework/Components/src/ResourceTableOfContents.cpp rename to src/framework/internal/Components/ResourceTableOfContents.cpp index b2bdb8465..d161f0f9c 100644 --- a/framework/Components/src/ResourceTableOfContents.cpp +++ b/src/framework/internal/Components/ResourceTableOfContents.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourceTableOfContents.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Utils/LogMacros.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/Core/Utils/LogMacros.h" #include #include -namespace ramses_internal +namespace ramses::internal { bool ResourceTableOfContents::containsResource(const ResourceContentHash& hash) const @@ -36,7 +36,7 @@ namespace ramses_internal void ResourceTableOfContents::writeTOCToStream(IOutputStream& outstream) { - const uint32_t numberOfEntries = static_cast(m_fileContents.size()); + const auto numberOfEntries = static_cast(m_fileContents.size()); outstream << numberOfEntries; // sort resources to get deterministic file @@ -88,7 +88,7 @@ namespace ramses_internal uint32_t sizeInBytes = 0; instream >> sizeInBytes; registerContents(info, offsetInBytes, sizeInBytes); - ++objectCounts[info.type]; + ++objectCounts[static_cast(info.type)]; const uint32_t resourceSizeToWarnAboutInBytes = 100 * 1024 * 1024; if (info.decompressedSize > resourceSizeToWarnAboutInBytes) diff --git a/framework/Components/include/Components/ResourceTableOfContents.h b/src/framework/internal/Components/ResourceTableOfContents.h similarity index 74% rename from framework/Components/include/Components/ResourceTableOfContents.h rename to src/framework/internal/Components/ResourceTableOfContents.h index 39cd93707..7bf5890be 100644 --- a/framework/Components/include/Components/ResourceTableOfContents.h +++ b/src/framework/internal/Components/ResourceTableOfContents.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCETABLEOFCONTENTS_H -#define RAMSES_RESOURCETABLEOFCONTENTS_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "Collections/HashMap.h" -#include "Collections/IInputStream.h" -#include "Collections/IOutputStream.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" #include "ResourcePersistation.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "Resource/ResourceInfo.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/SceneGraph/Resource/ResourceInfo.h" -namespace ramses_internal +namespace ramses::internal { struct ResourceFileEntry { @@ -43,5 +42,3 @@ namespace ramses_internal TableOfContentsMap m_fileContents; }; } - -#endif diff --git a/framework/Components/include/Components/SceneFileHandle.h b/src/framework/internal/Components/SceneFileHandle.h similarity index 72% rename from framework/Components/include/Components/SceneFileHandle.h rename to src/framework/internal/Components/SceneFileHandle.h index 46f2afb33..b4425b99f 100644 --- a/framework/Components/include/Components/SceneFileHandle.h +++ b/src/framework/internal/Components/SceneFileHandle.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_SCENEFILEHANDLE_H -#define RAMSES_FRAMEWORK_SCENEFILEHANDLE_H +#pragma once -#include "Common/StronglyTypedValue.h" +#include "internal/Core/Common/StronglyTypedValue.h" -namespace ramses_internal +namespace ramses::internal { struct SceneFileHandleTag; using SceneFileHandle = StronglyTypedValue; } -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::SceneFileHandle); - -#endif +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::SceneFileHandle); diff --git a/framework/Components/src/SceneGraphComponent.cpp b/src/framework/internal/Components/SceneGraphComponent.cpp similarity index 91% rename from framework/Components/src/SceneGraphComponent.cpp rename to src/framework/internal/Components/SceneGraphComponent.cpp index 68916b41f..950f25c68 100644 --- a/framework/Components/src/SceneGraphComponent.cpp +++ b/src/framework/internal/Components/SceneGraphComponent.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/SceneGraphComponent.h" -#include "Components/ClientSceneLogicShadowCopy.h" -#include "Components/ClientSceneLogicDirect.h" -#include "Scene/ClientScene.h" -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" -#include "TransportCommon/ICommunicationSystem.h" -#include "Utils/LogMacros.h" -#include "Scene/SceneActionCollection.h" -#include "SceneReferencing/SceneReferenceEvent.h" -#include "Components/ISceneRendererHandler.h" -#include "TransportCommon/SceneUpdateStreamDeserializer.h" -#include "Components/SceneUpdate.h" -#include "TransportCommon/SceneUpdateSerializer.h" -#include "Components/ResourceAvailabilityEvent.h" -#include "Components/IResourceProviderComponent.h" -#include "Components/SceneUpdate.h" - -namespace ramses_internal +#include "internal/Components/SceneGraphComponent.h" +#include "internal/Components/ClientSceneLogicShadowCopy.h" +#include "internal/Components/ClientSceneLogicDirect.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" +#include "internal/Components/ISceneRendererHandler.h" +#include "internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializer.h" +#include "internal/Components/ResourceAvailabilityEvent.h" +#include "internal/Components/IResourceProviderComponent.h" +#include "internal/Components/SceneUpdate.h" + +namespace ramses::internal { SceneGraphComponent::SceneGraphComponent( const Guid& myID, @@ -31,7 +31,7 @@ namespace ramses_internal IConnectionStatusUpdateNotifier& connectionStatusUpdateNotifier, IResourceProviderComponent& res, PlatformLock& frameworkLock, - ramses::EFeatureLevel featureLevel) + EFeatureLevel featureLevel) : m_sceneRendererHandler(nullptr) , m_myID(myID) , m_communicationSystem(communicationSystem) @@ -90,7 +90,7 @@ namespace ramses_internal } // TODO(tobias) remove mode, already given with publish - void SceneGraphComponent::sendCreateScene(const Guid& to, const SceneId& sceneId, EScenePublicationMode mode) + void SceneGraphComponent::sendCreateScene(const Guid& to, const SceneId& sceneId, [[maybe_unused]] EScenePublicationMode mode) { LOG_INFO(CONTEXT_FRAMEWORK, "SceneGraphComponent::sendCreateScene: sceneId " << sceneId << ", to " << to); @@ -106,15 +106,18 @@ namespace ramses_internal if (m_sceneRendererHandler) { if (info) + { m_sceneRendererHandler->handleInitializeScene(*info, m_myID); + } else + { m_sceneRendererHandler->handleInitializeScene(SceneInfo(sceneId, "", mode), m_myID); + } } } else { - assert(mode != EScenePublicationMode_LocalOnly); - UNUSED(mode); + assert(mode != EScenePublicationMode::LocalOnly); m_communicationSystem.sendInitializeScene(to, sceneId); } } @@ -158,7 +161,7 @@ namespace ramses_internal if (m_sceneRendererHandler) m_sceneRendererHandler->handleNewSceneAvailable(info, m_myID); - if (mode != EScenePublicationMode_LocalOnly && m_connected) + if (mode != EScenePublicationMode::LocalOnly && m_connected) m_communicationSystem.broadcastNewScenesAvailable({info}, m_featureLevel); m_locallyPublishedScenes.put(sceneId, info); @@ -175,7 +178,7 @@ namespace ramses_internal if (m_sceneRendererHandler) m_sceneRendererHandler->handleSceneBecameUnavailable(sceneId, m_myID); - if (mode != EScenePublicationMode_LocalOnly && m_connected) + if (mode != EScenePublicationMode::LocalOnly && m_connected) m_communicationSystem.broadcastScenesBecameUnavailable({info}); } @@ -196,9 +199,13 @@ namespace ramses_internal void SceneGraphComponent::unsubscribeScene(const Guid& to, SceneId sceneId) { if (m_myID == to) + { handleUnsubscribeScene(sceneId, m_myID); + } else + { m_communicationSystem.sendUnsubscribeScene(to, sceneId); + } } void SceneGraphComponent::connectToNetwork() @@ -217,7 +224,7 @@ namespace ramses_internal SceneInfoVector scenesToUnpublish; for (const auto& p : m_locallyPublishedScenes) { - if (p.value.publicationMode != EScenePublicationMode_LocalOnly) + if (p.value.publicationMode != EScenePublicationMode::LocalOnly) scenesToUnpublish.push_back(p.value); } if (!scenesToUnpublish.empty()) @@ -249,15 +256,17 @@ namespace ramses_internal SceneInfoVector availableScenes; for(const auto& p : m_locallyPublishedScenes) { - if (p.value.publicationMode != EScenePublicationMode_LocalOnly) + if (p.value.publicationMode != EScenePublicationMode::LocalOnly) { LOG_INFO(CONTEXT_FRAMEWORK, "SceneGraphComponent::newParticipantHasConnected: publishing scene to new particpant: " << connnectedParticipant << " scene is: " << p.key << " mode: " << EnumToString(p.value.publicationMode) << " from: " << m_myID); availableScenes.push_back(p.value); } } - if (availableScenes.size() > 0) + if (!availableScenes.empty()) + { m_communicationSystem.sendScenesAvailable(connnectedParticipant, availableScenes, m_featureLevel); + } } void SceneGraphComponent::participantHasDisconnected(const Guid& disconnnectedParticipant) @@ -392,10 +401,12 @@ namespace ramses_internal void SceneGraphComponent::sendSceneReferenceEvent(const Guid& to, SceneReferenceEvent const& event) { if (m_myID == to) + { forwardToSceneProviderEventConsumer(event); + } else { - std::vector dataBuffer; + std::vector dataBuffer; event.writeToBlob(dataBuffer); m_communicationSystem.sendRendererEvent(to, event.masterSceneId, dataBuffer); } @@ -404,16 +415,18 @@ namespace ramses_internal void SceneGraphComponent::sendResourceAvailabilityEvent(const Guid& to, ResourceAvailabilityEvent const& event) { if (m_myID == to) + { forwardToSceneProviderEventConsumer(event); + } else { - std::vector dataBuffer; + std::vector dataBuffer; event.writeToBlob(dataBuffer); - m_communicationSystem.sendRendererEvent(to, event.sceneid, std::move(dataBuffer)); + m_communicationSystem.sendRendererEvent(to, event.sceneid, dataBuffer); } } - void SceneGraphComponent::handleRendererEvent(const SceneId& sceneId, const std::vector& data, const Guid& /*rendererID*/) + void SceneGraphComponent::handleRendererEvent(const SceneId& sceneId, const std::vector& data, const Guid& /*rendererID*/) { // First extract type of event, it is at the beginning // TODO(jonathan): check if we can improve type handling, handle in better framing format e.g. @@ -451,18 +464,30 @@ namespace ramses_internal { auto it = m_sceneEventConsumers.find(event.masterSceneId); if (it != m_sceneEventConsumers.end()) + { it->value->handleSceneReferenceEvent(event, m_myID); + } else - LOG_WARN(CONTEXT_CLIENT, "SceneGraphComponent::forwardToSceneProviderEventConsumer: trying to send event to local client, but no event handler registered for sceneId " << event.masterSceneId); + { + LOG_WARN(CONTEXT_CLIENT, + "SceneGraphComponent::forwardToSceneProviderEventConsumer: trying to send event to local client, but no event handler registered for sceneId " + << event.masterSceneId); + } } void SceneGraphComponent::forwardToSceneProviderEventConsumer(ResourceAvailabilityEvent const& event) { auto it = m_sceneEventConsumers.find(event.sceneid); if (it != m_sceneEventConsumers.end()) + { it->value->handleResourceAvailabilityEvent(event, m_myID); + } else - LOG_WARN(CONTEXT_CLIENT, "SceneGraphComponent::forwardToSceneProviderEventConsumer: trying to send event to local client, but no event handler registered for sceneId " << event.sceneid); + { + LOG_WARN(CONTEXT_CLIENT, + "SceneGraphComponent::forwardToSceneProviderEventConsumer: trying to send event to local client, but no event handler registered for sceneId " + << event.sceneid); + } } void SceneGraphComponent::handleInitializeScene(const SceneId& sceneId, const Guid& providerID) @@ -495,7 +520,7 @@ namespace ramses_internal m_sceneRendererHandler->handleInitializeScene(it->second.info, providerID); } - void SceneGraphComponent::handleSceneUpdate(const SceneId& sceneId, absl::Span actionData, const Guid& providerID) + void SceneGraphComponent::handleSceneUpdate(const SceneId& sceneId, absl::Span actionData, const Guid& providerID) { if (!m_sceneRendererHandler) { @@ -548,7 +573,7 @@ namespace ramses_internal } } - void SceneGraphComponent::handleNewScenesAvailable(const SceneInfoVector& newScenes, const Guid& providerID, ramses::EFeatureLevel featureLevel) + void SceneGraphComponent::handleNewScenesAvailable(const SceneInfoVector& newScenes, const Guid& providerID, EFeatureLevel featureLevel) { // TODO(tobias) also cross-check with locally published scenes (+ published by someone else?) and warn/ignore if exists @@ -574,7 +599,7 @@ namespace ramses_internal m_remoteScenes[newScene.sceneID] = ReceivedScene{ newScene, providerID, nullptr }; if (m_sceneRendererHandler) - m_sceneRendererHandler->handleNewSceneAvailable(SceneInfo(newScene.sceneID, newScene.friendlyName, EScenePublicationMode_LocalAndRemote), providerID); + m_sceneRendererHandler->handleNewSceneAvailable(SceneInfo(newScene.sceneID, newScene.friendlyName, EScenePublicationMode::LocalAndRemote), providerID); } else { diff --git a/framework/Components/include/Components/SceneGraphComponent.h b/src/framework/internal/Components/SceneGraphComponent.h similarity index 85% rename from framework/Components/include/Components/SceneGraphComponent.h rename to src/framework/internal/Components/SceneGraphComponent.h index 7090b8dba..2d4dccf56 100644 --- a/framework/Components/include/Components/SceneGraphComponent.h +++ b/src/framework/internal/Components/SceneGraphComponent.h @@ -6,28 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEGRAPHCOMPONENT_H -#define RAMSES_SCENEGRAPHCOMPONENT_H +#pragma once #include "ISceneGraphProviderComponent.h" #include "ISceneGraphConsumerComponent.h" #include "ISceneGraphSender.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneSizeInformation.h" -#include "TransportCommon/IConnectionStatusListener.h" -#include "TransportCommon/ServiceHandlerInterfaces.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/Communication/TransportCommon/IConnectionStatusListener.h" +#include "internal/Communication/TransportCommon/ServiceHandlerInterfaces.h" #include "ISceneProviderEventConsumer.h" #include "ERendererToClientEventType.h" -#include "ramses-framework-api/EFeatureLevel.h" -#include "Utils/IPeriodicLogSupplier.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "Collections/HashMap.h" -#include "Collections/HashSet.h" -#include "Collections/Pair.h" +#include "ramses/framework/EFeatureLevel.h" +#include "internal/Core/Utils/IPeriodicLogSupplier.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" #include -namespace ramses_internal +namespace ramses::internal { class Guid; class ClientSceneLogicBase; @@ -53,7 +52,7 @@ namespace ramses_internal IConnectionStatusUpdateNotifier& connectionStatusUpdateNotifier, IResourceProviderComponent& res, PlatformLock& frameworkLock, - ramses::EFeatureLevel featureLevel); + EFeatureLevel featureLevel); ~SceneGraphComponent() override; void setSceneRendererHandler(ISceneRendererHandler* sceneRendererHandler) override; @@ -84,12 +83,12 @@ namespace ramses_internal // ISceneProviderServiceHandler void handleSubscribeScene(const SceneId& sceneId, const Guid& consumerID) override; void handleUnsubscribeScene(const SceneId& sceneId, const Guid& consumerID) override; - void handleRendererEvent(const SceneId& sceneId, const std::vector& data, const Guid& rendererID) override; + void handleRendererEvent(const SceneId& sceneId, const std::vector& data, const Guid& rendererID) override; // ISceneRendererServiceHandler void handleInitializeScene(const SceneId& sceneId, const Guid& providerID) override; - void handleSceneUpdate(const SceneId& sceneId, absl::Span actionData, const Guid& providerID) override; - void handleNewScenesAvailable(const SceneInfoVector& newScenes, const Guid& providerID, ramses::EFeatureLevel featureLevel) override; + void handleSceneUpdate(const SceneId& sceneId, absl::Span actionData, const Guid& providerID) override; + void handleNewScenesAvailable(const SceneInfoVector& newScenes, const Guid& providerID, EFeatureLevel featureLevel) override; void handleScenesBecameUnavailable(const SceneInfoVector& unavailableScenes, const Guid& providerID) override; void handleSceneNotAvailable(const SceneId& sceneId, const Guid& providerID) override; @@ -123,7 +122,7 @@ namespace ramses_internal IResourceProviderComponent& m_resourceComponent; - ramses::EFeatureLevel m_featureLevel = ramses::EFeatureLevel_01; + EFeatureLevel m_featureLevel = EFeatureLevel_01; struct ReceivedScene { @@ -137,5 +136,3 @@ namespace ramses_internal bool m_connected = false; }; } - -#endif diff --git a/framework/Components/include/Components/SceneUpdate.h b/src/framework/internal/Components/SceneUpdate.h similarity index 74% rename from framework/Components/include/Components/SceneUpdate.h rename to src/framework/internal/Components/SceneUpdate.h index 8dc536874..53022e03f 100644 --- a/framework/Components/include/Components/SceneUpdate.h +++ b/src/framework/internal/Components/SceneUpdate.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEUPDATE_H -#define RAMSES_SCENEUPDATE_H +#pragma once -#include "Components/ManagedResource.h" -#include "Scene/SceneActionCollection.h" -#include "Components/FlushInformation.h" +#include "internal/Components/ManagedResource.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/Components/FlushInformation.h" -namespace ramses_internal +namespace ramses::internal { struct SceneUpdate { @@ -22,5 +21,3 @@ namespace ramses_internal FlushInformation flushInfos; }; } - -#endif diff --git a/framework/Components/src/SingleResourceSerialization.cpp b/src/framework/internal/Components/SingleResourceSerialization.cpp similarity index 85% rename from framework/Components/src/SingleResourceSerialization.cpp rename to src/framework/internal/Components/SingleResourceSerialization.cpp index 9f0b56b89..46fc72094 100644 --- a/framework/Components/src/SingleResourceSerialization.cpp +++ b/src/framework/internal/Components/SingleResourceSerialization.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/SingleResourceSerialization.h" -#include "Components/ResourceSerializationHelper.h" -#include "Resource/IResource.h" -#include "Collections/IOutputStream.h" -#include "Resource/EResourceCompressionStatus.h" -#include "Utils/VoidOutputStream.h" -#include "Collections/IInputStream.h" +#include "internal/Components/SingleResourceSerialization.h" +#include "internal/Components/ResourceSerializationHelper.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/SceneGraph/Resource/EResourceCompressionStatus.h" +#include "internal/Core/Utils/VoidOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" -namespace ramses_internal +namespace ramses::internal { uint32_t SingleResourceSerialization::SizeOfSerializedResource(const IResource& resource) { @@ -52,7 +52,7 @@ namespace ramses_internal return {}; // data blob - if (header.compressionStatus == EResourceCompressionStatus_Compressed) + if (header.compressionStatus == EResourceCompressionStatus::Compressed) { // read compressed data from stream CompressedResourceBlob compressedData(header.compressedSize); diff --git a/framework/Components/include/Components/SingleResourceSerialization.h b/src/framework/internal/Components/SingleResourceSerialization.h similarity index 81% rename from framework/Components/include/Components/SingleResourceSerialization.h rename to src/framework/internal/Components/SingleResourceSerialization.h index 98347d303..d3aaa5f4f 100644 --- a/framework/Components/include/Components/SingleResourceSerialization.h +++ b/src/framework/internal/Components/SingleResourceSerialization.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SINGLERESOURCESERIALIZATION_H -#define RAMSES_SINGLERESOURCESERIALIZATION_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { class IOutputStream; class IResource; @@ -28,5 +28,3 @@ namespace ramses_internal static std::unique_ptr DeserializeResource(IInputStream& input, ResourceContentHash hash); }; } - -#endif diff --git a/framework/Core/Common/include/Common/BitForgeMacro.h b/src/framework/internal/Core/Common/BitForgeMacro.h similarity index 85% rename from framework/Core/Common/include/Common/BitForgeMacro.h rename to src/framework/internal/Core/Common/BitForgeMacro.h index 3a9fb360c..b777fe922 100644 --- a/framework/Core/Common/include/Common/BitForgeMacro.h +++ b/src/framework/internal/Core/Common/BitForgeMacro.h @@ -6,9 +6,6 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_BITFORGEMACRO_H -#define RAMSES_SCENEAPI_BITFORGEMACRO_H +#pragma once #define BIT(x) (1u << (x)) - -#endif diff --git a/framework/Core/Common/include/Common/MemoryHandle.h b/src/framework/internal/Core/Common/MemoryHandle.h similarity index 81% rename from framework/Core/Common/include/Common/MemoryHandle.h rename to src/framework/internal/Core/Common/MemoryHandle.h index 98c3c1a07..42534730c 100644 --- a/framework/Core/Common/include/Common/MemoryHandle.h +++ b/src/framework/internal/Core/Common/MemoryHandle.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MEMORYHANDLE_H -#define RAMSES_MEMORYHANDLE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include #include -namespace ramses_internal +namespace ramses::internal { using MemoryHandle = uint32_t; constexpr MemoryHandle InvalidMemoryHandle = std::numeric_limits::max(); } - -#endif diff --git a/framework/Core/Common/src/ParticipantIdentifier.cpp b/src/framework/internal/Core/Common/ParticipantIdentifier.cpp similarity index 84% rename from framework/Core/Common/src/ParticipantIdentifier.cpp rename to src/framework/internal/Core/Common/ParticipantIdentifier.cpp index 421347d14..1ce6e4d7d 100644 --- a/framework/Core/Common/src/ParticipantIdentifier.cpp +++ b/src/framework/internal/Core/Common/ParticipantIdentifier.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Common/ParticipantIdentifier.h" +#include "internal/Core/Common/ParticipantIdentifier.h" -namespace ramses_internal +namespace ramses::internal { ParticipantIdentifier::ParticipantIdentifier(const Guid& participantId, std::string_view participantName) : m_participantId(participantId) @@ -21,7 +21,5 @@ namespace ramses_internal { } - ParticipantIdentifier::~ParticipantIdentifier() - { - } + ParticipantIdentifier::~ParticipantIdentifier() = default; } diff --git a/framework/Core/Common/include/Common/ParticipantIdentifier.h b/src/framework/internal/Core/Common/ParticipantIdentifier.h similarity index 91% rename from framework/Core/Common/include/Common/ParticipantIdentifier.h rename to src/framework/internal/Core/Common/ParticipantIdentifier.h index 79ee287ec..1ded9cd09 100644 --- a/framework/Core/Common/include/Common/ParticipantIdentifier.h +++ b/src/framework/internal/Core/Common/ParticipantIdentifier.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PARTICIPANTIDENTIFIER_H -#define RAMSES_PARTICIPANTIDENTIFIER_H +#pragma once -#include "Collections/Guid.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ParticipantIdentifier { @@ -49,5 +48,3 @@ namespace ramses_internal return m_participantId == other.m_participantId; } } - -#endif diff --git a/src/framework/internal/Core/Common/StronglyTypedValue.h b/src/framework/internal/Core/Common/StronglyTypedValue.h new file mode 100644 index 000000000..31bce8ee5 --- /dev/null +++ b/src/framework/internal/Core/Common/StronglyTypedValue.h @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/StronglyTypedValue.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/Macros.h" +#include +#include + +#define MAKE_STRONGLYTYPEDVALUE_PRINTABLE(stronglyType) \ + template <> \ + struct fmt::formatter<::stronglyType>: formatter { \ + template \ + constexpr auto parse(ParseContext& ctx) { \ + return ctx.begin(); \ + } \ + template \ + constexpr auto format(const ::stronglyType& str, FormatContext& ctx) { \ + return fmt::format_to(ctx.out(), "{}", str.getValue()); \ + } \ + }; + diff --git a/framework/Core/Common/include/Common/TypedMemoryHandle.h b/src/framework/internal/Core/Common/TypedMemoryHandle.h similarity index 81% rename from framework/Core/Common/include/Common/TypedMemoryHandle.h rename to src/framework/internal/Core/Common/TypedMemoryHandle.h index f6b4eacd1..01d8e9b79 100644 --- a/framework/Core/Common/include/Common/TypedMemoryHandle.h +++ b/src/framework/internal/Core/Common/TypedMemoryHandle.h @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TYPEDMEMORYHANDLE_H -#define RAMSES_TYPEDMEMORYHANDLE_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/Hash.h" -#include "Collections/IInputStream.h" -#include "Collections/IOutputStream.h" -#include "Collections/StringOutputStream.h" -#include "Common/MemoryHandle.h" -#include "PlatformAbstraction/FmtBase.h" -#include "PlatformAbstraction/Macros.h" - -namespace ramses_internal +#pragma once + +#include "internal/PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Common/MemoryHandle.h" +#include "internal/PlatformAbstraction/FmtBase.h" +#include "internal/PlatformAbstraction/Macros.h" + +#include + +namespace ramses::internal { template class TypedMemoryHandle final @@ -62,6 +62,7 @@ namespace ramses_internal return *this; } + // NOLINTNEXTLINE(readability-const-return-type): required by cert-dcl21-cpp constexpr inline const TypedMemoryHandle operator++(int) { TypedMemoryHandle res(m_handle); @@ -75,6 +76,7 @@ namespace ramses_internal return *this; } + // NOLINTNEXTLINE(readability-const-return-type): required by cert-dcl21-cpp constexpr inline const TypedMemoryHandle operator--(int) { TypedMemoryHandle res(m_handle); @@ -243,10 +245,10 @@ namespace ramses_internal } template -struct fmt::formatter> : public ramses_internal::SimpleFormatterBase +struct fmt::formatter> : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::TypedMemoryHandle& str, FormatContext& ctx) + constexpr auto format(const ramses::internal::TypedMemoryHandle& str, FormatContext& ctx) { return fmt::format_to(ctx.out(), "{}", str.asMemoryHandle()); } @@ -255,27 +257,25 @@ struct fmt::formatter> : public ram namespace std { template - struct hash<::ramses_internal::TypedMemoryHandle> + struct hash<::ramses::internal::TypedMemoryHandle> { - size_t operator()(const ::ramses_internal::TypedMemoryHandle& key) const + size_t operator()(const ::ramses::internal::TypedMemoryHandle& key) const { - return static_cast(hash()(key.asMemoryHandle())); + return static_cast(hash()(key.asMemoryHandle())); } }; template - struct numeric_limits < ramses_internal::TypedMemoryHandle > + struct numeric_limits < ramses::internal::TypedMemoryHandle > { - static inline ramses_internal::TypedMemoryHandle max() + static inline ramses::internal::TypedMemoryHandle max() { - return ramses_internal::TypedMemoryHandle(std::numeric_limits::max()); + return ramses::internal::TypedMemoryHandle(std::numeric_limits::max()); } - static inline ramses_internal::TypedMemoryHandle min() + static inline ramses::internal::TypedMemoryHandle min() { - return ramses_internal::TypedMemoryHandle(std::numeric_limits::min()); + return ramses::internal::TypedMemoryHandle(std::numeric_limits::min()); } }; } - -#endif diff --git a/framework/Core/Math3d/include/Math3d/CameraMatrixHelper.h b/src/framework/internal/Core/Math3d/CameraMatrixHelper.h similarity index 95% rename from framework/Core/Math3d/include/Math3d/CameraMatrixHelper.h rename to src/framework/internal/Core/Math3d/CameraMatrixHelper.h index fd65fd1d7..0807c9db9 100644 --- a/framework/Core/Math3d/include/Math3d/CameraMatrixHelper.h +++ b/src/framework/internal/Core/Math3d/CameraMatrixHelper.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CAMERAMATRIXHELPER_H -#define RAMSES_CAMERAMATRIXHELPER_H +#pragma once #include "ProjectionParams.h" #include -namespace ramses_internal +namespace ramses::internal { class CameraMatrixHelper { @@ -54,5 +53,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/Core/Math3d/src/ProjectionParams.cpp b/src/framework/internal/Core/Math3d/ProjectionParams.cpp similarity index 94% rename from framework/Core/Math3d/src/ProjectionParams.cpp rename to src/framework/internal/Core/Math3d/ProjectionParams.cpp index 313fb75d4..49b04b3ec 100644 --- a/framework/Core/Math3d/src/ProjectionParams.cpp +++ b/src/framework/internal/Core/Math3d/ProjectionParams.cpp @@ -6,17 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Math3d/ProjectionParams.h" -#include "Common/TypedMemoryHandle.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Math3d/ProjectionParams.h" +#include "internal/Core/Common/TypedMemoryHandle.h" +#include "internal/PlatformAbstraction/PlatformMath.h" #include -namespace ramses_internal +namespace ramses::internal { - ProjectionParams::ProjectionParams() - { - } - bool ProjectionParams::operator==(const ProjectionParams& other) const { return m_projectionType == other.getProjectionType() diff --git a/framework/Core/Math3d/include/Math3d/ProjectionParams.h b/src/framework/internal/Core/Math3d/ProjectionParams.h similarity index 87% rename from framework/Core/Math3d/include/Math3d/ProjectionParams.h rename to src/framework/internal/Core/Math3d/ProjectionParams.h index 99b40f480..6cc3677ac 100644 --- a/framework/Core/Math3d/include/Math3d/ProjectionParams.h +++ b/src/framework/internal/Core/Math3d/ProjectionParams.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PROJECTIONPARAMS_H -#define RAMSES_PROJECTIONPARAMS_H +#pragma once -#include "DataTypesImpl.h" -#include "SceneAPI/ECameraProjectionType.h" +#include "impl/DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/ECameraProjectionType.h" -namespace ramses_internal +namespace ramses::internal { class ProjectionParams { @@ -64,19 +63,17 @@ namespace ramses_internal [[nodiscard]] bool isValid() const; // frustum planes - float leftPlane; - float rightPlane; - float bottomPlane; - float topPlane; - float nearPlane; - float farPlane; + float leftPlane = NAN; + float rightPlane = NAN; + float bottomPlane = NAN; + float topPlane = NAN; + float nearPlane = NAN; + float farPlane = NAN; private: // Don't construct projection params yourself, use static creation methods - ProjectionParams(); + ProjectionParams() = default; - ECameraProjectionType m_projectionType; + ECameraProjectionType m_projectionType = ECameraProjectionType::Perspective; }; } - -#endif diff --git a/framework/Core/Math3d/src/Quad.cpp b/src/framework/internal/Core/Math3d/Quad.cpp similarity index 93% rename from framework/Core/Math3d/src/Quad.cpp rename to src/framework/internal/Core/Math3d/Quad.cpp index d83e4f7f9..f18a34b55 100644 --- a/framework/Core/Math3d/src/Quad.cpp +++ b/src/framework/internal/Core/Math3d/Quad.cpp @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Math3d/Quad.h" +#include "internal/Core/Math3d/Quad.h" -namespace ramses_internal +namespace ramses::internal { - Quad::Quad() - { - } + Quad::Quad() = default; Quad::Quad(int32_t _x, int32_t _y, int32_t _width, int32_t _height) : x(_x) diff --git a/framework/Core/Math3d/include/Math3d/Quad.h b/src/framework/internal/Core/Math3d/Quad.h similarity index 88% rename from framework/Core/Math3d/include/Math3d/Quad.h rename to src/framework/internal/Core/Math3d/Quad.h index 62817db99..d3fd604ee 100644 --- a/framework/Core/Math3d/include/Math3d/Quad.h +++ b/src/framework/internal/Core/Math3d/Quad.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_QUAD_H -#define RAMSES_QUAD_H +#pragma once +#include #include -#include "PlatformAbstraction/PlatformTypes.h" -namespace ramses_internal +namespace ramses::internal { class Quad { @@ -32,5 +31,3 @@ namespace ramses_internal int32_t height = 0; }; } - -#endif diff --git a/framework/Core/Math3d/src/Rotation.cpp b/src/framework/internal/Core/Math3d/Rotation.cpp similarity index 94% rename from framework/Core/Math3d/src/Rotation.cpp rename to src/framework/internal/Core/Math3d/Rotation.cpp index e1b8cfeb5..90fa7c89f 100644 --- a/framework/Core/Math3d/src/Rotation.cpp +++ b/src/framework/internal/Core/Math3d/Rotation.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include +#include "internal/Core/Math3d/Rotation.h" #include "glm/gtx/euler_angles.hpp" -namespace ramses_internal::Math3d +namespace ramses::internal::Math3d { namespace { @@ -65,9 +65,6 @@ namespace ramses_internal::Math3d { return RotationQuaternion(rotation); } - else - { - return RotationEuler(glm::vec3(rotation), rotationType); - } + return RotationEuler(glm::vec3(rotation), rotationType); } } diff --git a/framework/Core/Math3d/include/Math3d/Rotation.h b/src/framework/internal/Core/Math3d/Rotation.h similarity index 84% rename from framework/Core/Math3d/include/Math3d/Rotation.h rename to src/framework/internal/Core/Math3d/Rotation.h index 1e4842200..376f89b41 100644 --- a/framework/Core/Math3d/include/Math3d/Rotation.h +++ b/src/framework/internal/Core/Math3d/Rotation.h @@ -8,10 +8,10 @@ #pragma once -#include "SceneAPI/ERotationType.h" -#include "DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +namespace ramses::internal { namespace Math3d { diff --git a/framework/Core/TaskFramework/src/EnqueueOnlyOneAtATimeQueue.cpp b/src/framework/internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.cpp similarity index 81% rename from framework/Core/TaskFramework/src/EnqueueOnlyOneAtATimeQueue.cpp rename to src/framework/internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.cpp index 9c22301e9..baaf77b1a 100644 --- a/framework/Core/TaskFramework/src/EnqueueOnlyOneAtATimeQueue.cpp +++ b/src/framework/internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/EnqueueOnlyOneAtATimeQueue.h" -#include "TaskFramework/TaskFinishHandlerDecorator.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h" +#include "internal/Core/TaskFramework/TaskFinishHandlerDecorator.h" +#include "internal/PlatformAbstraction/PlatformThread.h" -namespace ramses_internal +namespace ramses::internal { EnqueueOnlyOneAtATimeQueue::EnqueueOnlyOneAtATimeQueue(ITaskQueue& nextQueue) : m_nextQueue(nextQueue) @@ -34,17 +34,14 @@ namespace ramses_internal PlatformGuard guard(mlock); if (m_acceptingTasks) { - TaskFinishHandlerDecorator* decorator = new TaskFinishHandlerDecorator(*this, Task); + auto* decorator = new TaskFinishHandlerDecorator(*this, Task); if (!m_hasOutstandingTask) { moveTaskToNextQueue(*decorator); return true; } - else - { - m_waitingTasks.push_back(decorator); - return true; - } + m_waitingTasks.push_back(decorator); + return true; } return false; } @@ -55,7 +52,7 @@ namespace ramses_internal task.release(); } - void EnqueueOnlyOneAtATimeQueue::TaskFinished(ITask& ) + void EnqueueOnlyOneAtATimeQueue::TaskFinished(ITask& /*Task*/) { PlatformGuard guard(mlock); m_hasOutstandingTask = false; diff --git a/framework/Core/TaskFramework/include/TaskFramework/EnqueueOnlyOneAtATimeQueue.h b/src/framework/internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h similarity index 89% rename from framework/Core/TaskFramework/include/TaskFramework/EnqueueOnlyOneAtATimeQueue.h rename to src/framework/internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h index 6485d2cbb..fad8a0046 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/EnqueueOnlyOneAtATimeQueue.h +++ b/src/framework/internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h @@ -7,15 +7,14 @@ // ------------------------------------------------------------------------- -#ifndef RAMSES_ENQUEUEONLYONEATATIMEQUEUE_H -#define RAMSES_ENQUEUEONLYONEATATIMEQUEUE_H +#pragma once -#include "PlatformAbstraction/PlatformLock.h" +#include "internal/PlatformAbstraction/PlatformLock.h" #include "ITaskQueue.h" #include "ITaskFinishHandler.h" #include -namespace ramses_internal +namespace ramses::internal { class TaskFinishHandlerDecorator; @@ -41,5 +40,3 @@ namespace ramses_internal void moveTaskToNextQueue(ITask& task); }; } - -#endif diff --git a/framework/Core/TaskFramework/include/TaskFramework/ITask.h b/src/framework/internal/Core/TaskFramework/ITask.h similarity index 86% rename from framework/Core/TaskFramework/include/TaskFramework/ITask.h rename to src/framework/internal/Core/TaskFramework/ITask.h index 8c54a097c..2bb140160 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/ITask.h +++ b/src/framework/internal/Core/TaskFramework/ITask.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITASK_H -#define RAMSES_ITASK_H +#pragma once -#include "TaskFramework/RefCounted.h" +#include "internal/Core/TaskFramework/RefCounted.h" -namespace ramses_internal +namespace ramses::internal { /** * Interface for a Task which executable. @@ -27,5 +26,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/Core/TaskFramework/include/TaskFramework/ITaskFinishHandler.h b/src/framework/internal/Core/TaskFramework/ITaskFinishHandler.h similarity index 86% rename from framework/Core/TaskFramework/include/TaskFramework/ITaskFinishHandler.h rename to src/framework/internal/Core/TaskFramework/ITaskFinishHandler.h index 7704af481..2a5384a20 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/ITaskFinishHandler.h +++ b/src/framework/internal/Core/TaskFramework/ITaskFinishHandler.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITASKFINISHHANDLER_H -#define RAMSES_ITASKFINISHHANDLER_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class ITask; @@ -21,5 +20,3 @@ namespace ramses_internal virtual void TaskFinished(ITask& Task) = 0; }; } - -#endif diff --git a/framework/Core/TaskFramework/include/TaskFramework/ITaskQueue.h b/src/framework/internal/Core/TaskFramework/ITaskQueue.h similarity index 80% rename from framework/Core/TaskFramework/include/TaskFramework/ITaskQueue.h rename to src/framework/internal/Core/TaskFramework/ITaskQueue.h index 39ae53997..871b7d4af 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/ITaskQueue.h +++ b/src/framework/internal/Core/TaskFramework/ITaskQueue.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITASKQUEUE_H -#define RAMSES_ITASKQUEUE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include -namespace ramses_internal +namespace ramses::internal { class ITask; class ITaskExecutionObserver; @@ -19,11 +18,9 @@ namespace ramses_internal class ITaskQueue { public: - virtual ~ITaskQueue(){}; + virtual ~ITaskQueue() = default; virtual bool enqueue(ITask& Task) = 0; virtual void disableAcceptingTasksAfterExecutingCurrentQueue() = 0; }; } - -#endif diff --git a/framework/Core/TaskFramework/include/TaskFramework/ProcessingTaskQueue.h b/src/framework/internal/Core/TaskFramework/ProcessingTaskQueue.h similarity index 90% rename from framework/Core/TaskFramework/include/TaskFramework/ProcessingTaskQueue.h rename to src/framework/internal/Core/TaskFramework/ProcessingTaskQueue.h index 4a8e2d7b5..5ad88f8f9 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/ProcessingTaskQueue.h +++ b/src/framework/internal/Core/TaskFramework/ProcessingTaskQueue.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PROCESSINGTASKQUEUE_H -#define RAMSES_PROCESSINGTASKQUEUE_H +#pragma once -#include "Collections/BlockingQueue.h" +#include "internal/PlatformAbstraction/Collections/BlockingQueue.h" #include "ITask.h" -namespace ramses_internal +namespace ramses::internal { /** * The processing task queue stores task which should be executed according with their execution observer. @@ -50,5 +49,3 @@ namespace ramses_internal return m_taskQueue.empty(); } } - -#endif diff --git a/framework/Core/TaskFramework/include/TaskFramework/RefCounted.h b/src/framework/internal/Core/TaskFramework/RefCounted.h similarity index 93% rename from framework/Core/TaskFramework/include/TaskFramework/RefCounted.h rename to src/framework/internal/Core/TaskFramework/RefCounted.h index f6b6bd789..8132480a7 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/RefCounted.h +++ b/src/framework/internal/Core/TaskFramework/RefCounted.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_REFCOUNTED_H -#define RAMSES_REFCOUNTED_H +#pragma once #include #include #include -namespace ramses_internal +namespace ramses::internal { class RefCounted { @@ -52,5 +51,3 @@ namespace ramses_internal return m_refCount; } } - -#endif diff --git a/framework/Core/TaskFramework/src/TaskExecutingThread.cpp b/src/framework/internal/Core/TaskFramework/TaskExecutingThread.cpp similarity index 93% rename from framework/Core/TaskFramework/src/TaskExecutingThread.cpp rename to src/framework/internal/Core/TaskFramework/TaskExecutingThread.cpp index eed4ae50d..6f1f7cc00 100644 --- a/framework/Core/TaskFramework/src/TaskExecutingThread.cpp +++ b/src/framework/internal/Core/TaskFramework/TaskExecutingThread.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/TaskExecutingThread.h" -#include "TaskFramework/ITask.h" -#include "TaskFramework/ProcessingTaskQueue.h" -#include "Utils/LogMacros.h" +#include "internal/Core/TaskFramework/TaskExecutingThread.h" +#include "internal/Core/TaskFramework/ITask.h" +#include "internal/Core/TaskFramework/ProcessingTaskQueue.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { TaskExecutingThread::TaskExecutingThread(IThreadAliveNotifier& aliveHandler) : m_pBlockingTaskQueue(nullptr) diff --git a/framework/Core/TaskFramework/include/TaskFramework/TaskExecutingThread.h b/src/framework/internal/Core/TaskFramework/TaskExecutingThread.h similarity index 92% rename from framework/Core/TaskFramework/include/TaskFramework/TaskExecutingThread.h rename to src/framework/internal/Core/TaskFramework/TaskExecutingThread.h index 8dbc296ed..b77ba8a99 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/TaskExecutingThread.h +++ b/src/framework/internal/Core/TaskFramework/TaskExecutingThread.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TASKEXECUTINGTHREAD_H -#define RAMSES_TASKEXECUTINGTHREAD_H +#pragma once -#include "PlatformAbstraction/PlatformThread.h" -#include "Watchdog/IThreadAliveNotifier.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" #include -namespace ramses_internal +namespace ramses::internal { // class forward declarations class ITask; @@ -95,5 +94,3 @@ namespace ramses_internal } - -#endif diff --git a/framework/Core/TaskFramework/src/TaskExecutingThreadPool.cpp b/src/framework/internal/Core/TaskFramework/TaskExecutingThreadPool.cpp similarity index 89% rename from framework/Core/TaskFramework/src/TaskExecutingThreadPool.cpp rename to src/framework/internal/Core/TaskFramework/TaskExecutingThreadPool.cpp index c6c1129bc..07530f4fb 100644 --- a/framework/Core/TaskFramework/src/TaskExecutingThreadPool.cpp +++ b/src/framework/internal/Core/TaskFramework/TaskExecutingThreadPool.cpp @@ -6,18 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/TaskExecutingThreadPool.h" -#include "TaskFramework/TaskExecutingThread.h" -#include "Utils/LogMacros.h" +#include "internal/Core/TaskFramework/TaskExecutingThreadPool.h" +#include "internal/Core/TaskFramework/TaskExecutingThread.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { - TaskExecutingThreadPool::TaskExecutingThreadPool() - : m_mutex() - , m_bInitialized(false) - , m_bStarted(false) - { - } + TaskExecutingThreadPool::TaskExecutingThreadPool() = default; TaskExecutingThreadPool::~TaskExecutingThreadPool() { diff --git a/framework/Core/TaskFramework/include/TaskFramework/TaskExecutingThreadPool.h b/src/framework/internal/Core/TaskFramework/TaskExecutingThreadPool.h similarity index 89% rename from framework/Core/TaskFramework/include/TaskFramework/TaskExecutingThreadPool.h rename to src/framework/internal/Core/TaskFramework/TaskExecutingThreadPool.h index 66c3112fa..20cdd0a2f 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/TaskExecutingThreadPool.h +++ b/src/framework/internal/Core/TaskFramework/TaskExecutingThreadPool.h @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TASKEXECUTINGTHREADPOOL_H -#define RAMSES_TASKEXECUTINGTHREADPOOL_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Collections/Vector.h" -#include "TaskFramework/TaskExecutingThread.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Core/TaskFramework/TaskExecutingThread.h" + +#include #include #include -namespace ramses_internal +namespace ramses::internal { class ProcessingTaskQueue; @@ -71,11 +71,11 @@ namespace ramses_internal /** * Flag whether the pool is initialized. */ - bool m_bInitialized; + bool m_bInitialized{false}; /** * Flag whether the threads within the pool are started. */ - bool m_bStarted; + bool m_bStarted{false}; /** * Anonymous enumeration for defining constants. @@ -89,5 +89,3 @@ namespace ramses_internal }; }; } - -#endif diff --git a/framework/Core/TaskFramework/src/TaskFinishHandlerDecorator.cpp b/src/framework/internal/Core/TaskFramework/TaskFinishHandlerDecorator.cpp similarity index 86% rename from framework/Core/TaskFramework/src/TaskFinishHandlerDecorator.cpp rename to src/framework/internal/Core/TaskFramework/TaskFinishHandlerDecorator.cpp index 12dcdd1e3..f925091eb 100644 --- a/framework/Core/TaskFramework/src/TaskFinishHandlerDecorator.cpp +++ b/src/framework/internal/Core/TaskFramework/TaskFinishHandlerDecorator.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/TaskFinishHandlerDecorator.h" -#include "TaskFramework/ITaskFinishHandler.h" +#include "internal/Core/TaskFramework/TaskFinishHandlerDecorator.h" +#include "internal/Core/TaskFramework/ITaskFinishHandler.h" -namespace ramses_internal +namespace ramses::internal { TaskFinishHandlerDecorator::TaskFinishHandlerDecorator(ITaskFinishHandler& TaskFinishHandler, ITask& TaskToWatch) diff --git a/framework/Core/TaskFramework/include/TaskFramework/TaskFinishHandlerDecorator.h b/src/framework/internal/Core/TaskFramework/TaskFinishHandlerDecorator.h similarity index 84% rename from framework/Core/TaskFramework/include/TaskFramework/TaskFinishHandlerDecorator.h rename to src/framework/internal/Core/TaskFramework/TaskFinishHandlerDecorator.h index 5d616fa0b..af437afff 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/TaskFinishHandlerDecorator.h +++ b/src/framework/internal/Core/TaskFramework/TaskFinishHandlerDecorator.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TASKFINISHHANDLERDECORATOR_H -#define RAMSES_TASKFINISHHANDLERDECORATOR_H +#pragma once -#include "TaskFramework/ITask.h" +#include "internal/Core/TaskFramework/ITask.h" -namespace ramses_internal +namespace ramses::internal { class ITaskFinishHandler; @@ -30,5 +29,3 @@ namespace ramses_internal ITask& m_watchedTask; }; } - -#endif diff --git a/framework/Core/TaskFramework/src/TaskForwardingQueue.cpp b/src/framework/internal/Core/TaskFramework/TaskForwardingQueue.cpp similarity index 87% rename from framework/Core/TaskFramework/src/TaskForwardingQueue.cpp rename to src/framework/internal/Core/TaskFramework/TaskForwardingQueue.cpp index 288e367fe..b1c9df589 100644 --- a/framework/Core/TaskFramework/src/TaskForwardingQueue.cpp +++ b/src/framework/internal/Core/TaskFramework/TaskForwardingQueue.cpp @@ -6,21 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/TaskForwardingQueue.h" -#include "TaskFramework/TaskFinishHandlerDecorator.h" +#include "internal/Core/TaskFramework/TaskForwardingQueue.h" +#include "internal/Core/TaskFramework/TaskFinishHandlerDecorator.h" -namespace ramses_internal +namespace ramses::internal { TaskForwardingQueue::TaskForwardingQueue(ITaskQueue& nextQueue) : m_nextQueue(nextQueue) , m_acceptNewTasks(true) - , m_ongoingTasks() { } - TaskForwardingQueue::~TaskForwardingQueue() - { - } + TaskForwardingQueue::~TaskForwardingQueue() = default; bool TaskForwardingQueue::enqueue(ITask& task) { diff --git a/framework/Core/TaskFramework/include/TaskFramework/TaskForwardingQueue.h b/src/framework/internal/Core/TaskFramework/TaskForwardingQueue.h similarity index 90% rename from framework/Core/TaskFramework/include/TaskFramework/TaskForwardingQueue.h rename to src/framework/internal/Core/TaskFramework/TaskForwardingQueue.h index c4896ca87..dd2855ee0 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/TaskForwardingQueue.h +++ b/src/framework/internal/Core/TaskFramework/TaskForwardingQueue.h @@ -7,16 +7,15 @@ // ------------------------------------------------------------------------- -#ifndef RAMSES_TASKFORWARDINGQUEUE_H -#define RAMSES_TASKFORWARDINGQUEUE_H +#pragma once #include "ITaskQueue.h" #include "ITaskFinishHandler.h" -#include "Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" #include #include -namespace ramses_internal +namespace ramses::internal { class TaskForwardingQueue : public ITaskQueue, public ITaskFinishHandler { @@ -40,5 +39,3 @@ namespace ramses_internal HashSet m_ongoingTasks; }; } - -#endif diff --git a/framework/Core/TaskFramework/src/ThreadedTaskExecutor.cpp b/src/framework/internal/Core/TaskFramework/ThreadedTaskExecutor.cpp similarity index 91% rename from framework/Core/TaskFramework/src/ThreadedTaskExecutor.cpp rename to src/framework/internal/Core/TaskFramework/ThreadedTaskExecutor.cpp index 7fc762575..3cdfe182b 100644 --- a/framework/Core/TaskFramework/src/ThreadedTaskExecutor.cpp +++ b/src/framework/internal/Core/TaskFramework/ThreadedTaskExecutor.cpp @@ -6,15 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/ThreadedTaskExecutor.h" +#include "internal/Core/TaskFramework/ThreadedTaskExecutor.h" #include -namespace ramses_internal +namespace ramses::internal { ThreadedTaskExecutor::ThreadedTaskExecutor(uint16_t threadCount, const ThreadWatchdogConfig& watchdogConfig) - : ThreadWatchdog(watchdogConfig, ramses::ERamsesThreadIdentifier::Workers) - , m_taskQueue() - , m_threadPool() + : ThreadWatchdog(watchdogConfig, ERamsesThreadIdentifier::Workers) , m_acceptingNewTasks(true) { m_threadPool.init(threadCount, *this); diff --git a/framework/Core/TaskFramework/include/TaskFramework/ThreadedTaskExecutor.h b/src/framework/internal/Core/TaskFramework/ThreadedTaskExecutor.h similarity index 88% rename from framework/Core/TaskFramework/include/TaskFramework/ThreadedTaskExecutor.h rename to src/framework/internal/Core/TaskFramework/ThreadedTaskExecutor.h index aa17b1c09..679967930 100644 --- a/framework/Core/TaskFramework/include/TaskFramework/ThreadedTaskExecutor.h +++ b/src/framework/internal/Core/TaskFramework/ThreadedTaskExecutor.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_THREADEDTASKEXECUTOR_H -#define RAMSES_THREADEDTASKEXECUTOR_H +#pragma once #include "ITaskQueue.h" #include "ITask.h" #include "TaskExecutingThreadPool.h" -#include "TaskFramework/ProcessingTaskQueue.h" -#include "ThreadWatchdogConfig.h" -#include "Watchdog/ThreadWatchdog.h" -#include "Watchdog/PlatformWatchdog.h" -#include "Collections/Vector.h" +#include "internal/Core/TaskFramework/ProcessingTaskQueue.h" +#include "impl/ThreadWatchdogConfig.h" +#include "internal/Watchdog/ThreadWatchdog.h" +#include "internal/Watchdog/PlatformWatchdog.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { /** * The (central) task executor class which puts execute request in the processing queue and uses the thread pool @@ -79,5 +78,3 @@ namespace ramses_internal mutable std::mutex m_lock; }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/Adler32Checksum.h b/src/framework/internal/Core/Utils/Adler32Checksum.h similarity index 78% rename from framework/Core/Utils/include/Utils/Adler32Checksum.h rename to src/framework/internal/Core/Utils/Adler32Checksum.h index 6b30f50bb..cd53d39c8 100644 --- a/framework/Core/Utils/include/Utils/Adler32Checksum.h +++ b/src/framework/internal/Core/Utils/Adler32Checksum.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ADLER32CHECKSUM_H -#define RAMSES_ADLER32CHECKSUM_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include -namespace ramses_internal +namespace ramses::internal { class Adler32Checksum { @@ -22,11 +21,11 @@ namespace ramses_internal return (m_b << 16) | m_a; } - void addData(const Byte* ptr, uint32_t size) + void addData(const std::byte* ptr, uint32_t size) { for (uint32_t i = 0; i < size; i++) { - m_a = (m_a + ptr[i]) % MOD_ADLER; + m_a = (m_a + std::to_integer(ptr[i])) % MOD_ADLER; m_b = (m_b + m_a) % MOD_ADLER; } } @@ -38,4 +37,4 @@ namespace ramses_internal uint32_t m_b = 0; }; } -#endif + diff --git a/framework/Core/Utils/AndroidLogger/src/AndroidLogAppender.cpp b/src/framework/internal/Core/Utils/AndroidLogger/AndroidLogAppender.cpp similarity index 87% rename from framework/Core/Utils/AndroidLogger/src/AndroidLogAppender.cpp rename to src/framework/internal/Core/Utils/AndroidLogger/AndroidLogAppender.cpp index 3a64a98a1..7a39b6b67 100644 --- a/framework/Core/Utils/AndroidLogger/src/AndroidLogAppender.cpp +++ b/src/framework/internal/Core/Utils/AndroidLogger/AndroidLogAppender.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "AndroidLogger/AndroidLogAppender.h" -#include "Utils/LogMessage.h" -#include "Utils/LogContext.h" -#include "Utils/RamsesLogger.h" -#include "Utils/InplaceStringTokenizer.h" +#include "internal/Core/Utils/AndroidLogger/AndroidLogAppender.h" +#include "internal/Core/Utils/LogMessage.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/Core/Utils/InplaceStringTokenizer.h" #include #include -namespace ramses_internal +namespace ramses::internal { void AndroidLogAppender::log(const LogMessage& logMessage) { diff --git a/framework/Core/Utils/AndroidLogger/include/AndroidLogger/AndroidLogAppender.h b/src/framework/internal/Core/Utils/AndroidLogger/AndroidLogAppender.h similarity index 81% rename from framework/Core/Utils/AndroidLogger/include/AndroidLogger/AndroidLogAppender.h rename to src/framework/internal/Core/Utils/AndroidLogger/AndroidLogAppender.h index 7e9484b5e..08276cbb6 100644 --- a/framework/Core/Utils/AndroidLogger/include/AndroidLogger/AndroidLogAppender.h +++ b/src/framework/internal/Core/Utils/AndroidLogger/AndroidLogAppender.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ANDROIDLOGAPPENDER_H -#define RAMSES_ANDROIDLOGAPPENDER_H +#pragma once -#include "Utils/LogAppenderBase.h" +#include "internal/Core/Utils/LogAppenderBase.h" -namespace ramses_internal +namespace ramses::internal { class AndroidLogAppender : public LogAppenderBase { @@ -19,5 +18,3 @@ namespace ramses_internal void log(const LogMessage& logMessage) override; }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/AssertMovable.h b/src/framework/internal/Core/Utils/AssertMovable.h similarity index 92% rename from framework/Core/Utils/include/Utils/AssertMovable.h rename to src/framework/internal/Core/Utils/AssertMovable.h index 7d1965150..4e4b23671 100644 --- a/framework/Core/Utils/include/Utils/AssertMovable.h +++ b/src/framework/internal/Core/Utils/AssertMovable.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UTILS_ASSERTMOVABLE_H -#define RAMSES_UTILS_ASSERTMOVABLE_H +#pragma once #define INT_RAMSES_ASSERT_NOTHROW_MOVABLE(name) \ static_assert(std::is_nothrow_move_constructible::value, #name " must be movable"); \ @@ -17,5 +16,3 @@ // but all std libs of our supported compilers guarantee noexcept for this, // so we can assert no throw movable for our objects #define ASSERT_MOVABLE(x) INT_RAMSES_ASSERT_NOTHROW_MOVABLE(x) - -#endif diff --git a/framework/Core/Utils/include/Utils/BinaryFileInputStream.h b/src/framework/internal/Core/Utils/BinaryFileInputStream.h similarity index 82% rename from framework/Core/Utils/include/Utils/BinaryFileInputStream.h rename to src/framework/internal/Core/Utils/BinaryFileInputStream.h index 12ae973e0..51b90be07 100644 --- a/framework/Core/Utils/include/Utils/BinaryFileInputStream.h +++ b/src/framework/internal/Core/Utils/BinaryFileInputStream.h @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BINARYFILEINPUTSTREAM_H -#define RAMSES_BINARYFILEINPUTSTREAM_H +#pragma once -#include "Collections/IInputStream.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformError.h" -#include "Utils/File.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/PlatformError.h" +#include "internal/Core/Utils/File.h" -namespace ramses_internal +#include + +namespace ramses::internal { class BinaryFileInputStream: public IInputStream { @@ -27,7 +27,7 @@ namespace ramses_internal IInputStream& read(void* buffer, size_t size) override; - EStatus seek(Int numberOfBytesToSeek, Seek origin) override; + EStatus seek(int64_t numberOfBytesToSeek, Seek origin) override; EStatus getPos(size_t& position) const override; [[nodiscard]] EStatus getState() const override; @@ -62,30 +62,32 @@ namespace ramses_internal } inline - ramses_internal::EStatus - BinaryFileInputStream::seek(Int numberOfBytesToSeek, Seek origin) + ramses::internal::EStatus + BinaryFileInputStream::seek(int64_t numberOfBytesToSeek, Seek origin) { File::SeekOrigin fileOrigin = File::SeekOrigin::BeginningOfFile; if (origin == Seek::FromBeginning) + { fileOrigin = File::SeekOrigin::BeginningOfFile; + } else if (origin == Seek::Relative) + { fileOrigin = File::SeekOrigin::RelativeToCurrentPosition; + } return m_file.seek(numberOfBytesToSeek, fileOrigin) ? EStatus::Ok : EStatus::Error; } inline - ramses_internal::EStatus + ramses::internal::EStatus BinaryFileInputStream::getPos(size_t& position) const { return m_file.getPos(position) ? EStatus::Ok : EStatus::Error; } inline - ramses_internal::EStatus + ramses::internal::EStatus BinaryFileInputStream::getState() const { return m_state; } } - -#endif diff --git a/framework/Core/Utils/include/Utils/BinaryFileOutputStream.h b/src/framework/internal/Core/Utils/BinaryFileOutputStream.h similarity index 82% rename from framework/Core/Utils/include/Utils/BinaryFileOutputStream.h rename to src/framework/internal/Core/Utils/BinaryFileOutputStream.h index b2af0192d..c1acf43a6 100644 --- a/framework/Core/Utils/include/Utils/BinaryFileOutputStream.h +++ b/src/framework/internal/Core/Utils/BinaryFileOutputStream.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BINARYFILEOUTPUTSTREAM_H -#define RAMSES_BINARYFILEOUTPUTSTREAM_H +#pragma once -#include "Collections/IOutputStream.h" -#include "Utils/File.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/Core/Utils/File.h" -namespace ramses_internal +namespace ramses::internal { class BinaryFileOutputStream: public IOutputStream { @@ -25,7 +24,7 @@ namespace ramses_internal IOutputStream& write(const void* data, size_t size) override; - EStatus getPos(size_t& position); + EStatus getPos(size_t& position) const override; [[nodiscard]] EStatus getState() const; private: @@ -59,9 +58,9 @@ namespace ramses_internal } inline - ramses_internal::EStatus BinaryFileOutputStream::getPos(size_t& position) + ramses::internal::EStatus BinaryFileOutputStream::getPos(size_t& position) const { - m_state = m_file.getPos(position) ? EStatus::Ok : EStatus::Error; + const_cast(m_state) = m_file.getPos(position) ? EStatus::Ok : EStatus::Error; return m_state; } @@ -71,5 +70,3 @@ namespace ramses_internal return m_state; } } - -#endif diff --git a/framework/Core/Utils/include/Utils/BinaryInputStream.h b/src/framework/internal/Core/Utils/BinaryInputStream.h similarity index 73% rename from framework/Core/Utils/include/Utils/BinaryInputStream.h rename to src/framework/internal/Core/Utils/BinaryInputStream.h index 62dabf783..388994778 100644 --- a/framework/Core/Utils/include/Utils/BinaryInputStream.h +++ b/src/framework/internal/Core/Utils/BinaryInputStream.h @@ -6,35 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BINARYINPUTSTREAM_H -#define RAMSES_BINARYINPUTSTREAM_H +#pragma once -#include "Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" -namespace ramses_internal +namespace ramses::internal { class BinaryInputStream: public IInputStream { public: - explicit BinaryInputStream(const Byte* input); + explicit BinaryInputStream(const std::byte* input); IInputStream& read(void* buffer, size_t size) override; [[nodiscard]] EStatus getState() const override; - [[nodiscard]] const Byte* readPosition() const; + [[nodiscard]] const std::byte* readPosition() const; [[nodiscard]] size_t getCurrentReadBytes() const; void skip(int64_t offset); - EStatus seek(Int numberOfBytesToSeek, Seek origin) override; + EStatus seek(int64_t numberOfBytesToSeek, Seek origin) override; EStatus getPos(size_t& position) const override; private: - const Byte* m_current; - const Byte* m_start; + const std::byte* m_current; + const std::byte* m_start; }; - inline BinaryInputStream::BinaryInputStream(const Byte* input) + inline BinaryInputStream::BinaryInputStream(const std::byte* input) : m_current(input) , m_start(input) { @@ -42,7 +41,7 @@ namespace ramses_internal inline IInputStream& BinaryInputStream::read(void* buffer, size_t size) { - if (size) + if (size != 0u) std::memcpy(buffer, m_current, size); m_current += size; return *this; @@ -58,7 +57,7 @@ namespace ramses_internal return m_current - m_start; } - inline const Byte* BinaryInputStream::readPosition() const + inline const std::byte* BinaryInputStream::readPosition() const { return m_current; } @@ -68,12 +67,16 @@ namespace ramses_internal m_current += offset; } - inline EStatus BinaryInputStream::seek(Int numberOfBytesToSeek, Seek origin) + inline EStatus BinaryInputStream::seek(int64_t numberOfBytesToSeek, Seek origin) { if (origin == Seek::FromBeginning) + { m_current = m_start + numberOfBytesToSeek; + } else if (origin == Seek::Relative) + { m_current += numberOfBytesToSeek; + } return EStatus::Ok; } @@ -83,5 +86,3 @@ namespace ramses_internal return EStatus::Ok; } } - -#endif diff --git a/framework/Core/Utils/src/BinaryOffsetFileInputStream.cpp b/src/framework/internal/Core/Utils/BinaryOffsetFileInputStream.cpp similarity index 78% rename from framework/Core/Utils/src/BinaryOffsetFileInputStream.cpp rename to src/framework/internal/Core/Utils/BinaryOffsetFileInputStream.cpp index 513c1e2e9..3ffdc45ba 100644 --- a/framework/Core/Utils/src/BinaryOffsetFileInputStream.cpp +++ b/src/framework/internal/Core/Utils/BinaryOffsetFileInputStream.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/BinaryOffsetFileInputStream.h" +#include "internal/Core/Utils/BinaryOffsetFileInputStream.h" -namespace ramses_internal +namespace ramses::internal { BinaryOffsetFileInputStream::BinaryOffsetFileInputStream(int fd, size_t offset, size_t length) @@ -52,37 +52,42 @@ namespace ramses_internal if (bytesRead != size) { - if (feof(m_file)) + if (feof(m_file) != 0) + { m_state = EStatus::Eof; + } else + { m_state = EStatus::Error; + } } return *this; } - EStatus BinaryOffsetFileInputStream::seek(Int numberOfBytesToSeek, Seek origin) + EStatus BinaryOffsetFileInputStream::seek(int64_t numberOfBytesToSeek, Seek origin) { if (m_state != EStatus::Ok) return EStatus::Error; - Int newPos = 0; + int64_t newPos = 0; switch (origin) { case Seek::FromBeginning: - newPos = static_cast(m_startPos + numberOfBytesToSeek); + newPos = static_cast(m_startPos + numberOfBytesToSeek); break; case Seek::Relative: - newPos = static_cast(m_pos + numberOfBytesToSeek); + newPos = static_cast(m_pos + numberOfBytesToSeek); break; } - if (newPos < static_cast(m_startPos) || newPos > static_cast(m_startPos + m_length)) + if (newPos < static_cast(m_startPos) || newPos > static_cast(m_startPos + m_length)) return EStatus::Error; + // NOLINTNEXTLINE(google-runtime-int): long is the API type if (fseek(m_file, static_cast(newPos), SEEK_SET) != 0) return EStatus::Error; - m_pos = newPos; + m_pos = static_cast(newPos); return EStatus::Ok; } diff --git a/framework/Core/Utils/include/Utils/BinaryOffsetFileInputStream.h b/src/framework/internal/Core/Utils/BinaryOffsetFileInputStream.h similarity index 82% rename from framework/Core/Utils/include/Utils/BinaryOffsetFileInputStream.h rename to src/framework/internal/Core/Utils/BinaryOffsetFileInputStream.h index 0377516b4..f2fb270ef 100644 --- a/framework/Core/Utils/include/Utils/BinaryOffsetFileInputStream.h +++ b/src/framework/internal/Core/Utils/BinaryOffsetFileInputStream.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_BINARYOFFSETFILEINPUTSTREAM_H -#define RAMSES_FRAMEWORK_BINARYOFFSETFILEINPUTSTREAM_H +#pragma once -#include "Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" #include -namespace ramses_internal +namespace ramses::internal { /* * Special input stream that works on an already open filedescriptor. The relevant @@ -21,7 +20,7 @@ namespace ramses_internal * * The class takes ownership if the passed filedescriptor. */ - class BinaryOffsetFileInputStream : public IInputStream + class BinaryOffsetFileInputStream final : public IInputStream { public: explicit BinaryOffsetFileInputStream(int fd, size_t offset, size_t length); @@ -32,7 +31,7 @@ namespace ramses_internal IInputStream& read(void* buffer, size_t size) override; - EStatus seek(Int numberOfBytesToSeek, Seek origin) override; + EStatus seek(int64_t numberOfBytesToSeek, Seek origin) override; EStatus getPos(size_t& position) const override; [[nodiscard]] EStatus getState() const override; @@ -45,5 +44,3 @@ namespace ramses_internal size_t m_pos; }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/BinaryOutputStream.h b/src/framework/internal/Core/Utils/BinaryOutputStream.h similarity index 67% rename from framework/Core/Utils/include/Utils/BinaryOutputStream.h rename to src/framework/internal/Core/Utils/BinaryOutputStream.h index 2ab615093..5e6ad1556 100644 --- a/framework/Core/Utils/include/Utils/BinaryOutputStream.h +++ b/src/framework/internal/Core/Utils/BinaryOutputStream.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BINARYOUTPUTSTREAM_H -#define RAMSES_BINARYOUTPUTSTREAM_H +#pragma once -#include "Collections/IOutputStream.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { class BinaryOutputStream: public IOutputStream { @@ -21,31 +21,37 @@ namespace ramses_internal explicit BinaryOutputStream(size_t startSize = 16); IOutputStream& write(const void* data, size_t size) override; + EStatus getPos(size_t& position) const override; - [[nodiscard]] const Byte* getData() const; + [[nodiscard]] const std::byte* getData() const; [[nodiscard]] size_t getSize() const; [[nodiscard]] size_t getCapacity() const; - std::vector release(); + std::vector release(); private: - std::vector m_buffer; + std::vector m_buffer; }; inline BinaryOutputStream::BinaryOutputStream(size_t startSize) - : m_buffer() { m_buffer.reserve(startSize); } inline IOutputStream& BinaryOutputStream::write(const void* data, size_t size) { - const Byte* dataCharptr = static_cast(data); + const auto* dataCharptr = static_cast(data); m_buffer.insert(m_buffer.end(), dataCharptr, dataCharptr+size); return *this; } - inline const Byte* BinaryOutputStream::getData() const + inline EStatus BinaryOutputStream::getPos(size_t& position) const + { + position = m_buffer.size(); + return EStatus::Ok; + } + + inline const std::byte* BinaryOutputStream::getData() const { return m_buffer.data(); } @@ -60,12 +66,10 @@ namespace ramses_internal return static_cast(m_buffer.capacity()); } - inline std::vector BinaryOutputStream::release() + inline std::vector BinaryOutputStream::release() { - std::vector result; + std::vector result; result.swap(m_buffer); return result; } } - -#endif diff --git a/framework/Core/Utils/src/ConsoleLogAppender.cpp b/src/framework/internal/Core/Utils/ConsoleLogAppender.cpp similarity index 84% rename from framework/Core/Utils/src/ConsoleLogAppender.cpp rename to src/framework/internal/Core/Utils/ConsoleLogAppender.cpp index 4196765d6..ad7239003 100644 --- a/framework/Core/Utils/src/ConsoleLogAppender.cpp +++ b/src/framework/internal/Core/Utils/ConsoleLogAppender.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/ConsoleLogAppender.h" -#include "Utils/LogMessage.h" -#include "Utils/LogContext.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" -#include "PlatformAbstraction/PlatformConsole.h" +#include "internal/Core/Utils/ConsoleLogAppender.h" +#include "internal/Core/Utils/LogMessage.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/PlatformAbstraction/PlatformConsole.h" #include "fmt/format.h" #include "fmt/chrono.h" -namespace ramses_internal +namespace ramses::internal { ConsoleLogAppender::ConsoleLogAppender() : m_callback([]() {}) @@ -67,8 +67,8 @@ namespace ramses_internal break; } - struct tm posix_tm; - time_t posix_time = static_cast(now / 1000); + struct tm posix_tm{}; + auto posix_time = static_cast(now / 1000); // use thread-safe localtime instead of std::localtime that returns internal shared buffer #ifdef _MSC_VER localtime_s(&posix_tm, &posix_time); @@ -77,14 +77,17 @@ namespace ramses_internal #endif if (m_colorsEnabled) + { fmt::print("{}{:%Y%m%d-%H:%M:%S}.{:03}{} | {}{}{} | {}{}{} | {}\n", Console::White(), posix_tm, now % 1000, Console::Default(), logLevelColor, logLevelStr, Console::Default(), Console::Cyan(), logMessage.getContext().getContextId(), Console::Default(), logMessage.getStream().data()); + } else - fmt::print("{:%Y%m%d-%H:%M:%S}.{:03} | {} | {} | {}\n", - posix_tm, now % 1000, logLevelStr,logMessage.getContext().getContextId(), logMessage.getStream().data()); + { + fmt::print("{:%Y%m%d-%H:%M:%S}.{:03} | {} | {} | {}\n", posix_tm, now % 1000, logLevelStr, logMessage.getContext().getContextId(), logMessage.getStream().data()); + } std::fflush(stdout); m_callback(); diff --git a/framework/Core/Utils/include/Utils/ConsoleLogAppender.h b/src/framework/internal/Core/Utils/ConsoleLogAppender.h similarity index 85% rename from framework/Core/Utils/include/Utils/ConsoleLogAppender.h rename to src/framework/internal/Core/Utils/ConsoleLogAppender.h index ea47ff6a0..11c790642 100644 --- a/framework/Core/Utils/include/Utils/ConsoleLogAppender.h +++ b/src/framework/internal/Core/Utils/ConsoleLogAppender.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONSOLELOGAPPENDER_H -#define RAMSES_CONSOLELOGAPPENDER_H +#pragma once -#include "Utils/LogAppenderBase.h" -#include "Utils/LogLevel.h" -#include "Collections/StringOutputStream.h" +#include "internal/Core/Utils/LogAppenderBase.h" +#include "internal/Core/Utils/LogLevel.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ConsoleLogAppender : public LogAppenderBase { @@ -47,5 +46,3 @@ namespace ramses_internal m_logLevel = level; } } - -#endif diff --git a/framework/Core/Utils/include/Utils/DataTypeUtils.h b/src/framework/internal/Core/Utils/DataTypeUtils.h similarity index 96% rename from framework/Core/Utils/include/Utils/DataTypeUtils.h rename to src/framework/internal/Core/Utils/DataTypeUtils.h index 0e890f8ce..77c4f0dd6 100644 --- a/framework/Core/Utils/include/Utils/DataTypeUtils.h +++ b/src/framework/internal/Core/Utils/DataTypeUtils.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATATYPEUTILS_H -#define RAMSES_DATATYPEUTILS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "DataTypesImpl.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +#include + +namespace ramses::internal { enum EDataTypeID { @@ -188,5 +188,3 @@ namespace ramses_internal using DataType = glm::ivec4; }; } - -#endif diff --git a/src/framework/internal/Core/Utils/EnumTraits.h b/src/framework/internal/Core/Utils/EnumTraits.h new file mode 100644 index 000000000..7295196e2 --- /dev/null +++ b/src/framework/internal/Core/Utils/EnumTraits.h @@ -0,0 +1,123 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include + +// EnumTraits implementation relies on compiler macros __FUNCSIG__ / __PRETTY_FUNCTION__ +// These are non-standard and only supported for recent compilers +// Sometimes older compilers do support __PRETTY_FUNCTION__, but they do not distinguish between known and unknown enum values. +// Those compilers are not supported either (e.g. gcc-7). +#if defined(_MSC_VER) && _MSC_VER >= 1910 || defined(__GNUC__) && __GNUC__ >= 9 || defined(__clang__) && __clang_major__ >= 5 +#undef RAMSES_HAS_ENUMTRAITS +#define RAMSES_HAS_ENUMTRAITS 1 // prefer ramses::internal::EnumTraits::IsSupported() where possible +#endif + +namespace ramses::internal +{ + namespace EnumTraits + { + namespace internal + { + const size_t Limit = 128; + + template constexpr bool IsValid() + { + // __FUNCSIG__/__PRETTY_FUNCTION__ will generate different strings for known and unknown enum values. + // - known enum values will be printed as a symbol name: "ramses::EFoo::Value1" + // - unknown enum values will be printed as a number: "15" or with preceding type: "(enum ramses::EFoo)15" + // + // Exact format may vary (compiler dependent): + // + // Examples: + // Possible __FUNCSIG__/__PRETTY_FUNCTION__ for a known (valid) enum value: + // "bool ramses::internal::EnumTraits::internal::IsValid(void)" + // ^-- separatorIndex + // "bool ramses::internal::EnumTraits::internal::IsValid() [E = ramses::EFoo, V = ramses::EFoo::Value1] + // ^-- separatorIndex + // + // Possible __FUNCSIG__/__PRETTY_FUNCTION__ for an unknown (invalid) enum value: + // "bool ramses::internal::EnumTraits::internal::IsValid(void)" + // ^-- separatorIndex + // "bool ramses::internal::EnumTraits::internal::IsValid() [E = ramses::EFoo, V = 15] + // ^-- separatorIndex + // "bool ramses::internal::EnumTraits::internal::IsValid() [E = ramses::EFoo, V = -3] + // ^-- separatorIndex + // + // separatorIndex identifies the position of the enum value (template parameter V) +#if defined(__GNUC__) + const std::string_view name = __PRETTY_FUNCTION__; + const auto separatorIndex = name.rfind(' '); +#elif defined (_MSC_VER) + const std::string_view name = __FUNCSIG__; + const auto separatorIndex = name.rfind(','); +#else + // invalid for unknown compilers + const std::string_view name = " ("; + const auto separatorIndex = 0; +#endif + // expect a symbol name after the separatorIndex, e.g.: "ramses::EFoo::Value1" + const auto c = name[separatorIndex + 1]; + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == ':' || c == '_') + { + return true; + } + return false; + } + + template constexpr size_t CountValid() + { + return 0; + } + + template constexpr size_t CountValid() + { + return CountValid() + (IsValid() ? 1u : 0u); + } + + template constexpr size_t InternalElementCount(std::integer_sequence /* unused */) + { + return CountValid(I)...>(); + } + + template struct ElementCount + { + // calculates the number of known enum values for the value range: 0..Limit + static const size_t value = InternalElementCount(std::make_integer_sequence()); + }; + } + + constexpr bool IsSupported() + { +#if defined(RAMSES_HAS_ENUMTRAITS) + return true; +#else + return false; +#endif + } + + /** + * Verifies if the enum has the expected number of elements. + * This is only supported for most common compilers. + * Unsupported compilers will always pass the check. + */ + template [[nodiscard]] constexpr bool VerifyElementCountIfSupported(size_t elementCount) + { + if (!IsSupported()) + { + return true; + } + assert(elementCount < internal::Limit); + return elementCount == internal::ElementCount::value; + } + } +} diff --git a/framework/Core/Utils/src/File.cpp b/src/framework/internal/Core/Utils/File.cpp similarity index 51% rename from framework/Core/Utils/src/File.cpp rename to src/framework/internal/Core/Utils/File.cpp index 309ba4f3e..b2295e46a 100644 --- a/framework/Core/Utils/src/File.cpp +++ b/src/framework/internal/Core/Utils/File.cpp @@ -6,33 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/File.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/LogMacros.h" + #include -namespace ramses_internal +namespace ramses::internal { - namespace - { - // path may not have trailing backslash on windows but remove it on all platforms for consistency - std::string RemoveTrailingBackslash(std::string path) - { - const auto len = path.size(); - if (len > 0 && (path[len-1] == '\\' || path[len-1] == '/')) - { - if (len < 2 || path[len-2] != ':') - { - path.resize(len - 1); - } - } - return path; - } - } - File::File(std::string_view path) - : m_isOpen(false) - , m_path(RemoveTrailingBackslash(std::string{path})) - , m_handle(nullptr) + : m_path(path) { } @@ -63,16 +45,16 @@ namespace ramses_internal return *this; } - bool File::isOpen() + bool File::isOpen() const { return m_isOpen; } - bool File::isEof() + bool File::isEof() const { if (m_handle == nullptr) return false; - return feof(m_handle) != 0; + return std::feof(m_handle) != 0; } @@ -109,10 +91,10 @@ namespace ramses_internal default: flags = ""; } - FILE* handle = fopen(m_path.c_str(), flags); + std::FILE* handle = std::fopen(m_path.string().c_str(), flags); if (handle == nullptr) { - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::open: fopen {} with flags {} failed, errno is {}", m_path, flags, errno); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::open: fopen {} with flags {} failed, errno is {}", m_path.string(), flags, errno); return false; } @@ -125,9 +107,9 @@ namespace ramses_internal { if (m_handle == nullptr) return false; - if (fflush(m_handle) != 0) + if (std::fflush(m_handle) != 0) { - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::flush: fflush failed for {}, errno is {}", m_path, errno); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::flush: fflush failed for {}, errno is {}", m_path.string(), errno); return false; } return true; @@ -138,9 +120,9 @@ namespace ramses_internal if (m_handle == nullptr) return false; - if (fclose(m_handle) != 0) + if (std::fclose(m_handle) != 0) { - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::close: fclose failed for {}, errno is {}", m_path, errno); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::close: fclose failed for {}, errno is {}", m_path.string(), errno); return false; } m_handle = nullptr; @@ -150,7 +132,7 @@ namespace ramses_internal std::string File::getPath() const { - return m_path; + return m_path.string(); } EStatus File::read(void* buffer, size_t length, size_t& numBytes) @@ -160,7 +142,7 @@ namespace ramses_internal if (m_handle == nullptr) return EStatus::Error; - size_t result = fread(buffer, 1, length, m_handle); + size_t result = std::fread(buffer, 1, length, m_handle); if (result == length) { @@ -168,14 +150,14 @@ namespace ramses_internal return EStatus::Ok; } - if (feof(m_handle)) + if (std::feof(m_handle) != 0) { - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::read: fread attempted to read {} bytes from {}, got only {} due to eof", length, m_path, result); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::read: fread attempted to read {} bytes from {}, got only {} due to eof", length, m_path.string(), result); numBytes = result; return EStatus::Eof; } - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::read: fread attempted to read {} bytes from {}, got only {} due to read error", length, m_path, result); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::read: fread attempted to read {} bytes from {}, got only {} due to read error", length, m_path.string(), result); return EStatus::Error; } @@ -189,17 +171,17 @@ namespace ramses_internal if (length == 0) return true; - size_t result = fwrite(buffer, length, 1, m_handle); - if (result != 1u) + const size_t result = std::fwrite(buffer, 1, length, m_handle); + if (result != length) { - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::write: fwrite attempted to write {} bytes to {} and failed", length, m_path); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::write: fwrite attempted to write {} bytes to {} and failed, {} bytes written", length, m_path.string(), result); return false; } return true; } - bool File::seek(std::intptr_t offset, SeekOrigin origin) + bool File::seek(int64_t offset, SeekOrigin origin) { if (m_handle == nullptr) return false; @@ -214,11 +196,11 @@ namespace ramses_internal nativeOrigin = SEEK_CUR; break; } - size_t result = fseek(m_handle, static_cast(offset), nativeOrigin); - if (0 != result) + // NOLINTNEXTLINE(google-runtime-int): long is the API type + if (std::fseek(m_handle, static_cast(offset), nativeOrigin) != 0) { - LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::seek: fseek attempted to seek to {} bytes from {} of file {} and failed, errno is {}", offset, origin, m_path, errno); + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::seek: fseek attempted to seek to {} bytes from {} of file {} and failed, errno is {}", offset, origin, m_path.string(), errno); return false; } @@ -227,179 +209,75 @@ namespace ramses_internal std::string File::getExtension() const { - const size_t position = m_path.rfind('.'); - if (position == std::string::npos) - { - // index not found - return {}; - } - return m_path.substr(position + 1); + auto extension{m_path.extension().string()}; + if (!extension.empty()) + extension.erase(extension.begin()); // remove . from extension + return extension; } std::string File::getFileName() const { - size_t lastSeparator = m_path.rfind('/'); - if (lastSeparator == std::string::npos) - lastSeparator = m_path.rfind('\\'); - - if (lastSeparator != std::string::npos) - return m_path.substr(lastSeparator + 1); - - return m_path; + return m_path.filename().string(); } bool File::createFile() { - FILE* handle = fopen(m_path.c_str(), "w"); + std::FILE* handle = std::fopen(m_path.string().c_str(), "w"); if (handle == nullptr) return false; - fclose(handle); - return true; - } -} - -#ifdef _WIN32 - -#include "PlatformAbstraction/MinimalWindowsH.h" - -namespace ramses_internal -{ - bool File::createDirectory() - { - BOOL status = CreateDirectoryA(m_path.c_str(), 0) == TRUE; - if (status != TRUE) - return false; - return true; - } - - bool File::remove() - { - BOOL status = FALSE; - if (isDirectory()) - { - status = RemoveDirectoryA(m_path.c_str()); - } - else - { - status = DeleteFileA(m_path.c_str()); - } - if (status == TRUE) - return true; - return false; + return std::fclose(handle) == 0; } bool File::isDirectory() const { - DWORD dwAttributes = GetFileAttributesA(m_path.c_str()); - return (dwAttributes != INVALID_FILE_ATTRIBUTES) && ((dwAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + std::error_code ec; + return std::filesystem::is_directory(m_path, ec); } bool File::exists() const { - DWORD dwAttributes = GetFileAttributesA(m_path.c_str()); - return (dwAttributes != INVALID_FILE_ATTRIBUTES); - } - - bool File::getPos(size_t& position) const - { - __int64 pos = ftell(m_handle); - if (pos >= 0) - { - position = static_cast(pos); - return true; - } - return false; - } - - // TODO(tobias) does not work on write opened file, should be reworked to use m_handle if m_isOpen - bool File::getSizeInBytes(size_t& size) const - { - HANDLE fileHandle = CreateFileA(m_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (fileHandle == nullptr) - { - return false; - } - LARGE_INTEGER tempSize; - BOOL status = GetFileSizeEx(fileHandle, &tempSize); - CloseHandle(fileHandle); - if (status != TRUE) - { - return false; - } - size = static_cast(tempSize.QuadPart); - return true; - } -} - - -#else // posix -#include -#include - -namespace ramses_internal -{ - bool File::getSizeInBytes(size_t& size) const - { - struct stat tmp; - if (stat(m_path.c_str(), &tmp) != 0) - return false; - - size = tmp.st_size; - return true; - } - - bool File::isDirectory() const - { - struct stat tmp; - return stat(m_path.c_str(), &tmp) == 0 && S_ISDIR(tmp.st_mode); // NOLINT(hicpp-signed-bitwise) ignore bad stuff in macro + std::error_code ec; + return std::filesystem::exists(m_path, ec); } bool File::createDirectory() { - // NOLINTNEXTLINE(hicpp-signed-bitwise) - if (mkdir(m_path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO ) != 0) // mode 0777 - return false; - return true; + std::error_code ec; + return std::filesystem::create_directory(m_path, ec); } bool File::remove() { - int status = -1; - if (isDirectory()) - { - status = ::rmdir(m_path.c_str()); - } - else - { - status = ::remove(m_path.c_str()); - } - - if (status == 0) - return true; - - return false; + std::error_code ec; + return std::filesystem::remove(m_path, ec); } - bool File::exists() const + bool File::getSizeInBytes(size_t& size) const { - struct stat fileStats; - if (stat(m_path.c_str(), &fileStats) != 0) + std::error_code ec; + const auto fileSize = std::filesystem::file_size(m_path, ec); + if (ec) + { + LOG_ERROR_P(CONTEXT_FRAMEWORK, "File::getSizeInBytes: can't get file size for {}, error code is {}", m_path.string(), ec.message()); return false; - + } + size = static_cast(fileSize); return true; } bool File::getPos(size_t& position) const { - const off_t pos = ftello(m_handle); - if (pos >= 0) + if (m_handle == nullptr) + return false; + + if (const auto pos = std::ftell(m_handle); pos >= 0) { - position = pos; + position = static_cast(pos); return true; } return false; } } -#endif + diff --git a/framework/Core/Utils/include/Utils/File.h b/src/framework/internal/Core/Utils/File.h similarity index 58% rename from framework/Core/Utils/include/Utils/File.h rename to src/framework/internal/Core/Utils/File.h index b2d109a54..190fd2826 100644 --- a/framework/Core/Utils/include/Utils/File.h +++ b/src/framework/internal/Core/Utils/File.h @@ -6,18 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FILE_H -#define RAMSES_FILE_H +#pragma once -#include "PlatformAbstraction/PlatformError.h" -#include "PlatformAbstraction/Macros.h" +#include "internal/PlatformAbstraction/PlatformError.h" #include #include +#include #include #include -namespace ramses_internal +#include + +namespace ramses::internal { class File final { @@ -40,43 +41,52 @@ namespace ramses_internal RelativeToCurrentPosition // seeks relative to current position within the file }; + static constexpr std::array SeekOriginNames + { + "BeginningOfFile", + "RelativeToCurrentPosition", + }; + explicit File(std::string_view filepath); ~File(); File(File&& other) noexcept; File& operator=(File&& other) noexcept; - RNODISCARD bool exists() const; - RNODISCARD bool isDirectory() const; + [[nodiscard]] bool exists() const; + [[nodiscard]] bool isDirectory() const; bool createFile(); bool createDirectory(); bool remove(); - RNODISCARD bool open(const Mode& mode); + [[nodiscard]] bool open(const Mode& mode); bool close(); - RNODISCARD bool isOpen(); - RNODISCARD bool isEof(); + [[nodiscard]] bool isOpen() const; + [[nodiscard]] bool isEof() const; - RNODISCARD bool getSizeInBytes(size_t& size) const; + [[nodiscard]] bool getSizeInBytes(size_t& size) const; - RNODISCARD EStatus read(void* buffer, size_t length, size_t& numBytes); - RNODISCARD bool write(const void* buffer, size_t length); - RNODISCARD bool seek(std::intptr_t numberOfBytesToSeek, SeekOrigin origin); - RNODISCARD bool getPos(size_t& position) const; + [[nodiscard]] EStatus read(void* buffer, size_t length, size_t& numBytes); + [[nodiscard]] bool write(const void* buffer, size_t length); + [[nodiscard]] bool seek(int64_t offset, SeekOrigin origin); + [[nodiscard]] bool getPos(size_t& position) const; bool flush(); - RNODISCARD std::string getFileName() const; - RNODISCARD std::string getPath() const; - RNODISCARD std::string getExtension() const; + [[nodiscard]] std::string getFileName() const; + [[nodiscard]] std::string getPath() const; + [[nodiscard]] std::string getExtension() const; File(const File&) = delete; File& operator=(File&) = delete; private: - bool m_isOpen; - std::string m_path; - FILE* m_handle; + bool m_isOpen{false}; + std::filesystem::path m_path{}; + std::FILE* m_handle{nullptr}; }; } -#endif +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::File::SeekOrigin, + "SeekOrigin", + ramses::internal::File::SeekOriginNames, + ramses::internal::File::SeekOrigin::RelativeToCurrentPosition); diff --git a/framework/Core/Utils/include/Utils/HandlePool.h b/src/framework/internal/Core/Utils/HandlePool.h similarity index 89% rename from framework/Core/Utils/include/Utils/HandlePool.h rename to src/framework/internal/Core/Utils/HandlePool.h index c728b62af..34a0ea126 100644 --- a/framework/Core/Utils/include/Utils/HandlePool.h +++ b/src/framework/internal/Core/Utils/HandlePool.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_HANDLEPOOL_H -#define RAMSES_HANDLEPOOL_H +#pragma once -#include "Common/TypedMemoryHandle.h" -#include "Collections/Vector.h" +#include "internal/Core/Common/TypedMemoryHandle.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include #include -namespace ramses_internal +namespace ramses::internal { template class HandlePool @@ -39,15 +38,13 @@ namespace ramses_internal HANDLE acquireInternal(MemoryHandle handle); std::vector m_handlePool; - MemoryHandle m_nextAvailableHint; - uint32_t m_numberOfAcquired; + MemoryHandle m_nextAvailableHint{0u}; + uint32_t m_numberOfAcquired{0u}; }; template HandlePool::HandlePool(uint32_t size) : m_handlePool(size) - , m_nextAvailableHint(0u) - , m_numberOfAcquired(0u) { } @@ -77,7 +74,7 @@ namespace ramses_internal } // allocate and acquire new handle - const MemoryHandle newHandle = static_cast(m_handlePool.size()); + const auto newHandle = static_cast(m_handlePool.size()); m_handlePool.resize(newHandle + 1u); return acquireInternal(newHandle); } @@ -123,7 +120,7 @@ namespace ramses_internal template - uint32_t ramses_internal::HandlePool::getNumberOfAcquired() const + uint32_t ramses::internal::HandlePool::getNumberOfAcquired() const { return m_numberOfAcquired; } @@ -146,5 +143,3 @@ namespace ramses_internal return std::numeric_limits::max(); } } - -#endif diff --git a/framework/Core/Utils/include/Utils/IPeriodicLogSupplier.h b/src/framework/internal/Core/Utils/IPeriodicLogSupplier.h similarity index 79% rename from framework/Core/Utils/include/Utils/IPeriodicLogSupplier.h rename to src/framework/internal/Core/Utils/IPeriodicLogSupplier.h index 4003600b4..a90a63c69 100644 --- a/framework/Core/Utils/include/Utils/IPeriodicLogSupplier.h +++ b/src/framework/internal/Core/Utils/IPeriodicLogSupplier.h @@ -6,18 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IPERIODICLOGSUPPLIER_H -#define RAMSES_IPERIODICLOGSUPPLIER_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IPeriodicLogSupplier { public: - virtual ~IPeriodicLogSupplier() {}; + virtual ~IPeriodicLogSupplier() = default; virtual void triggerLogMessageForPeriodicLog() = 0; }; } -#endif - diff --git a/framework/Core/Utils/src/Image.cpp b/src/framework/internal/Core/Utils/Image.cpp similarity index 88% rename from framework/Core/Utils/src/Image.cpp rename to src/framework/internal/Core/Utils/Image.cpp index 13a4e6463..362e53c9e 100644 --- a/framework/Core/Utils/src/Image.cpp +++ b/src/framework/internal/Core/Utils/Image.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/Image.h" -#include "Utils/File.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/Image.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" #include "lodepng.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" #include -namespace ramses_internal +namespace ramses::internal { Image::Image(uint32_t width, uint32_t height, std::vector&& data) : m_width(width) @@ -101,8 +101,8 @@ namespace ramses_internal std::vector enlargedData(width * height * 4u); - const size_t srcRowSize = m_width * 4u; - const size_t dstRowSize = width * 4u; + const auto srcRowSize = static_cast(m_width) * 4; + const auto dstRowSize = static_cast(width) * 4; // copy source row and fill rest of row auto srcRowBegin = m_data.cbegin(); @@ -150,9 +150,13 @@ namespace ramses_internal const bool overflow = (dest >= maximumValueBeforeOverflow); if (overflow) + { dest = std::numeric_limits::max(); + } else + { dest += value; + } return overflow; }; @@ -169,8 +173,11 @@ namespace ramses_internal } if (overflow) - LOG_WARN(CONTEXT_FRAMEWORK, "Image::getSumOfPixelValues: Overflow of sum of pixel values! The overflown values saturate to max possible positive value for int32_t [=" - << std::numeric_limits::max() << "]"); + { + LOG_WARN(CONTEXT_FRAMEWORK, + "Image::getSumOfPixelValues: Overflow of sum of pixel values! The overflown values saturate to max possible positive value for int32_t [=" + << std::numeric_limits::max() << "]"); + } return result; } diff --git a/framework/Core/Utils/include/Utils/Image.h b/src/framework/internal/Core/Utils/Image.h similarity index 91% rename from framework/Core/Utils/include/Utils/Image.h rename to src/framework/internal/Core/Utils/Image.h index 0929e2f73..2d600a35e 100644 --- a/framework/Core/Utils/include/Utils/Image.h +++ b/src/framework/internal/Core/Utils/Image.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IMAGE_H -#define RAMSES_IMAGE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/Macros.h" -#include "Utils/AssertMovable.h" -#include "DataTypesImpl.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/Core/Utils/AssertMovable.h" +#include "impl/DataTypesImpl.h" +#include #include #include #include #include -namespace ramses_internal +namespace ramses::internal { class Image { @@ -76,7 +75,7 @@ namespace ramses_internal auto dst = m_data.end(); for (size_t row = 0u; row < m_height; ++row) { - dst -= rowSize; + dst -= static_cast(rowSize); const Iter srcRowEnd = srcRowBegin + rowSize; std::copy(srcRowBegin, srcRowEnd, dst); srcRowBegin = srcRowEnd; @@ -86,5 +85,3 @@ namespace ramses_internal m_data.assign(begin, end); } } - -#endif diff --git a/framework/Core/Utils/include/Utils/InplaceStringTokenizer.h b/src/framework/internal/Core/Utils/InplaceStringTokenizer.h similarity index 93% rename from framework/Core/Utils/include/Utils/InplaceStringTokenizer.h rename to src/framework/internal/Core/Utils/InplaceStringTokenizer.h index 52ae1c919..b6956c20a 100644 --- a/framework/Core/Utils/include/Utils/InplaceStringTokenizer.h +++ b/src/framework/internal/Core/Utils/InplaceStringTokenizer.h @@ -6,14 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UTILS_INPLACESTRINGTOKENIZER_H -#define RAMSES_UTILS_INPLACESTRINGTOKENIZER_H - -#include +#pragma once +#include #include -namespace ramses_internal +namespace ramses::internal { namespace InplaceStringTokenizer { @@ -68,7 +66,7 @@ namespace ramses_internal else { const size_t splitPos = s.rfind(splitToken, nextEnd); - size_t nextStart; + size_t nextStart = 0; if (splitPos != std::string::npos && splitPos > currentStart) { nextEnd = splitPos; @@ -93,5 +91,3 @@ namespace ramses_internal } } } - -#endif diff --git a/framework/Core/Utils/include/Utils/LogAppenderBase.h b/src/framework/internal/Core/Utils/LogAppenderBase.h similarity index 86% rename from framework/Core/Utils/include/Utils/LogAppenderBase.h rename to src/framework/internal/Core/Utils/LogAppenderBase.h index 9f6eb00ae..7a3cbdb1c 100644 --- a/framework/Core/Utils/include/Utils/LogAppenderBase.h +++ b/src/framework/internal/Core/Utils/LogAppenderBase.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGAPPENDERBASE_H -#define RAMSES_LOGAPPENDERBASE_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class LogMessage; @@ -20,5 +19,3 @@ namespace ramses_internal virtual void log(const LogMessage& logMessage) = 0; }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/LogContext.h b/src/framework/internal/Core/Utils/LogContext.h similarity index 93% rename from framework/Core/Utils/include/Utils/LogContext.h rename to src/framework/internal/Core/Utils/LogContext.h index 8ff7e8842..41dc23bcf 100644 --- a/framework/Core/Utils/include/Utils/LogContext.h +++ b/src/framework/internal/Core/Utils/LogContext.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGCONTEXT_H -#define RAMSES_LOGCONTEXT_H +#pragma once -#include "Utils/LogLevel.h" +#include "internal/Core/Utils/LogLevel.h" #include -namespace ramses_internal +namespace ramses::internal { class LogContext { @@ -72,5 +71,3 @@ namespace ramses_internal return m_data; } } - -#endif diff --git a/framework/Core/Utils/src/LogHelper.cpp b/src/framework/internal/Core/Utils/LogHelper.cpp similarity index 84% rename from framework/Core/Utils/src/LogHelper.cpp rename to src/framework/internal/Core/Utils/LogHelper.cpp index 0a81cd1e9..387465d28 100644 --- a/framework/Core/Utils/src/LogHelper.cpp +++ b/src/framework/internal/Core/Utils/LogHelper.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/LogHelper.h" -#include "Utils/LogMacros.h" -#include "Utils/StringUtils.h" +#include "internal/Core/Utils/LogHelper.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/StringUtils.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { namespace LogHelper { @@ -30,32 +30,32 @@ namespace ramses_internal logLevel = ELogLevel::Trace; return true; } - else if (lowered == "debug" || lowered == "5") + if (lowered == "debug" || lowered == "5") { logLevel = ELogLevel::Debug; return true; } - else if (lowered == "info" || lowered == "4") + if (lowered == "info" || lowered == "4") { logLevel = ELogLevel::Info; return true; } - else if (lowered == "warn" || lowered == "3") + if (lowered == "warn" || lowered == "3") { logLevel = ELogLevel::Warn; return true; } - else if (lowered == "error" || lowered == "2") + if (lowered == "error" || lowered == "2") { logLevel = ELogLevel::Error; return true; } - else if (lowered == "fatal" || lowered == "1") + if (lowered == "fatal" || lowered == "1") { logLevel = ELogLevel::Fatal; return true; } - else if (lowered == "off" || lowered == "0") + if (lowered == "off" || lowered == "0") { logLevel = ELogLevel::Off; return true; @@ -79,16 +79,16 @@ namespace ramses_internal const size_t positionOfColon = filterCommand.find(':', currentCommandStart); if (positionOfColon != std::string::npos && currentCommandStart < positionOfColon) { - const Int lengthOfLogLevelString = positionOfColon - currentCommandStart; + const auto lengthOfLogLevelString = positionOfColon - currentCommandStart; const std::string logLevelStr = filterCommand.substr(currentCommandStart, lengthOfLogLevelString); ELogLevel logLevel; if (StringToLogLevel(logLevelStr, logLevel)) { - const Int offsetOfLogContextPattern = lengthOfLogLevelString + 1; + const auto offsetOfLogContextPattern = lengthOfLogLevelString + 1; const std::string contextPattern = filterCommand.substr(currentCommandStart + offsetOfLogContextPattern, currentCommandEnd - currentCommandStart - offsetOfLogContextPattern); - if (contextPattern.size() > 0) + if (!contextPattern.empty()) { - returnValue.push_back(ContextFilter(logLevel, contextPattern)); + returnValue.emplace_back(logLevel, contextPattern); } } else diff --git a/framework/Core/Utils/include/Utils/LogHelper.h b/src/framework/internal/Core/Utils/LogHelper.h similarity index 82% rename from framework/Core/Utils/include/Utils/LogHelper.h rename to src/framework/internal/Core/Utils/LogHelper.h index a02d7e6e5..f618762c9 100644 --- a/framework/Core/Utils/include/Utils/LogHelper.h +++ b/src/framework/internal/Core/Utils/LogHelper.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UTILS_LOGHELPER_H -#define RAMSES_UTILS_LOGHELPER_H +#pragma once -#include "Utils/LogLevel.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/Core/Utils/LogLevel.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { namespace LogHelper { @@ -27,5 +26,3 @@ namespace ramses_internal std::vector ParseContextFilters(const std::string& filterCommand); } } - -#endif diff --git a/framework/Core/Utils/include/Utils/LogLevel.h b/src/framework/internal/Core/Utils/LogLevel.h similarity index 78% rename from framework/Core/Utils/include/Utils/LogLevel.h rename to src/framework/internal/Core/Utils/LogLevel.h index 79272a0e8..162198671 100644 --- a/framework/Core/Utils/include/Utils/LogLevel.h +++ b/src/framework/internal/Core/Utils/LogLevel.h @@ -6,14 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGLEVEL_H -#define RAMSES_LOGLEVEL_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses_internal +namespace ramses::internal { using ramses::ELogLevel; } - -#endif diff --git a/framework/Core/Utils/src/LogMacros.cpp b/src/framework/internal/Core/Utils/LogMacros.cpp similarity index 93% rename from framework/Core/Utils/src/LogMacros.cpp rename to src/framework/internal/Core/Utils/LogMacros.cpp index 32b0b881c..5c6c2e2d6 100644 --- a/framework/Core/Utils/src/LogMacros.cpp +++ b/src/framework/internal/Core/Utils/LogMacros.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" -namespace ramses_internal +namespace ramses::internal { LogContext& CONTEXT_FRAMEWORK = GetRamsesLogger().createContext("ramses.Framework ", "RFRA"); LogContext& CONTEXT_CLIENT = GetRamsesLogger().createContext("ramses.Client ", "RCLI"); diff --git a/framework/Core/Utils/include/Utils/LogMacros.h b/src/framework/internal/Core/Utils/LogMacros.h similarity index 58% rename from framework/Core/Utils/include/Utils/LogMacros.h rename to src/framework/internal/Core/Utils/LogMacros.h index cc3a58df7..30817acf4 100644 --- a/framework/Core/Utils/include/Utils/LogMacros.h +++ b/src/framework/internal/Core/Utils/LogMacros.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGMACROS_H -#define RAMSES_LOGMACROS_H +#pragma once -#include "Utils/LogMessage.h" -#include "Utils/LogContext.h" -#include "Utils/RamsesLogger.h" -#include "fmt/format.h" +#include "internal/Core/Utils/LogMessage.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/PlatformAbstraction/FmtBase.h" -namespace ramses_internal +namespace ramses::internal { extern LogContext& CONTEXT_FRAMEWORK; extern LogContext& CONTEXT_CLIENT; @@ -34,17 +33,17 @@ namespace ramses_internal namespace ramses { - using ::ramses_internal::CONTEXT_FRAMEWORK; - using ::ramses_internal::CONTEXT_CLIENT; - using ::ramses_internal::CONTEXT_RENDERER; - using ::ramses_internal::CONTEXT_PERIODIC; - using ::ramses_internal::CONTEXT_TEXT; - using ::ramses_internal::CONTEXT_COMMUNICATION; - using ::ramses_internal::CONTEXT_PROFILING; - using ::ramses_internal::CONTEXT_HLAPI_CLIENT; - using ::ramses_internal::CONTEXT_HLAPI_RENDERER; - using ::ramses_internal::CONTEXT_RAMSH; - using ::ramses_internal::CONTEXT_SMOKETEST; + using ramses::internal::CONTEXT_FRAMEWORK; + using ramses::internal::CONTEXT_CLIENT; + using ramses::internal::CONTEXT_RENDERER; + using ramses::internal::CONTEXT_PERIODIC; + using ramses::internal::CONTEXT_TEXT; + using ramses::internal::CONTEXT_COMMUNICATION; + using ramses::internal::CONTEXT_PROFILING; + using ramses::internal::CONTEXT_HLAPI_CLIENT; + using ramses::internal::CONTEXT_HLAPI_RENDERER; + using ramses::internal::CONTEXT_RAMSH; + using ramses::internal::CONTEXT_SMOKETEST; } // LOG_* macros for log message in macro @@ -52,83 +51,83 @@ namespace ramses do { \ if((logLevel) <= (context).getLogLevel()) \ { \ - ::ramses_internal::StringOutputStream ramses_log_stream(80); \ - ramses_log_stream << message; \ - ::ramses_internal::GetRamsesLogger().log(::ramses_internal::LogMessage((context), (logLevel), ramses_log_stream)); \ + ramses::internal::StringOutputStream ramses_log_stream(80); \ + ramses_log_stream << message; /* NOLINT(bugprone-macro-parentheses) */ \ + ramses::internal::GetRamsesLogger().log(ramses::internal::LogMessage((context), (logLevel), ramses_log_stream)); \ } \ } while (0) #define LOG_TRACE(context, message) \ - LOG_COMMON((context), ::ramses_internal::ELogLevel::Trace, message) + LOG_COMMON((context), ramses::ELogLevel::Trace, message) #define LOG_INFO(context, message) \ - LOG_COMMON((context), ::ramses_internal::ELogLevel::Info, message) + LOG_COMMON((context), ramses::ELogLevel::Info, message) #define LOG_DEBUG(context, message) \ - LOG_COMMON((context), ::ramses_internal::ELogLevel::Debug, message) + LOG_COMMON((context), ramses::ELogLevel::Debug, message) #define LOG_WARN(context, message) \ - LOG_COMMON((context), ::ramses_internal::ELogLevel::Warn, message) + LOG_COMMON((context), ramses::ELogLevel::Warn, message) #define LOG_ERROR(context, message) \ - LOG_COMMON((context), ::ramses_internal::ELogLevel::Error, message) + LOG_COMMON((context), ramses::ELogLevel::Error, message) #define LOG_FATAL(context, message) \ - LOG_COMMON((context), ::ramses_internal::ELogLevel::Fatal, message) + LOG_COMMON((context), ramses::ELogLevel::Fatal, message) // LOG_* macros for log message via printf syntax #define LOG_COMMON_P(context, logLevel, ...) \ do { \ if((logLevel) <= (context).getLogLevel()) \ - ::ramses_internal::GetRamsesLogger().log(::ramses_internal::LogMessage((context), (logLevel), ::ramses_internal::StringOutputStream(::fmt::format(__VA_ARGS__)))); \ + ramses::internal::GetRamsesLogger().log(ramses::internal::LogMessage((context), (logLevel), ramses::internal::StringOutputStream(::fmt::format(__VA_ARGS__)))); \ } while (0) #define LOG_TRACE_P(context, ...) \ - LOG_COMMON_P((context), ::ramses_internal::ELogLevel::Trace, __VA_ARGS__) + LOG_COMMON_P((context), ramses::ELogLevel::Trace, __VA_ARGS__) #define LOG_INFO_P(context, ...) \ - LOG_COMMON_P((context), ::ramses_internal::ELogLevel::Info, __VA_ARGS__) + LOG_COMMON_P((context), ramses::ELogLevel::Info, __VA_ARGS__) #define LOG_DEBUG_P(context, ...) \ - LOG_COMMON_P((context), ::ramses_internal::ELogLevel::Debug, __VA_ARGS__) + LOG_COMMON_P((context), ramses::ELogLevel::Debug, __VA_ARGS__) #define LOG_WARN_P(context, ...) \ - LOG_COMMON_P((context), ::ramses_internal::ELogLevel::Warn, __VA_ARGS__) + LOG_COMMON_P((context), ramses::ELogLevel::Warn, __VA_ARGS__) #define LOG_ERROR_P(context, ...) \ - LOG_COMMON_P((context), ::ramses_internal::ELogLevel::Error, __VA_ARGS__) + LOG_COMMON_P((context), ramses::ELogLevel::Error, __VA_ARGS__) #define LOG_FATAL_P(context, ...) \ - LOG_COMMON_P((context), ::ramses_internal::ELogLevel::Fatal, __VA_ARGS__) + LOG_COMMON_P((context), ramses::ELogLevel::Fatal, __VA_ARGS__) // LOG_* macros for log message via callable #define LOG_COMMON_F(context, logLevel, callable) \ do { \ if((logLevel) <= (context).getLogLevel()) \ { \ - ::ramses_internal::StringOutputStream ramses_log_stream(160); \ + ramses::internal::StringOutputStream ramses_log_stream(160); \ callable(ramses_log_stream); \ - ::ramses_internal::GetRamsesLogger().log(::ramses_internal::LogMessage((context), (logLevel), ramses_log_stream)); \ + ramses::internal::GetRamsesLogger().log(ramses::internal::LogMessage((context), (logLevel), ramses_log_stream)); \ } \ } while (0) #define LOG_TRACE_F(context, callable) \ - LOG_COMMON_F((context), ::ramses_internal::ELogLevel::Trace, callable) + LOG_COMMON_F((context), ramses::ELogLevel::Trace, callable) #define LOG_INFO_F(context, callable) \ - LOG_COMMON_F((context), ::ramses_internal::ELogLevel::Info, callable) + LOG_COMMON_F((context), ramses::ELogLevel::Info, callable) #define LOG_DEBUG_F(context, callable) \ - LOG_COMMON_F((context), ::ramses_internal::ELogLevel::Debug, callable) + LOG_COMMON_F((context), ramses::ELogLevel::Debug, callable) #define LOG_WARN_F(context, callable) \ - LOG_COMMON_F((context), ::ramses_internal::ELogLevel::Warn, callable) + LOG_COMMON_F((context), ramses::ELogLevel::Warn, callable) #define LOG_ERROR_F(context, callable) \ - LOG_COMMON_F((context), ::ramses_internal::ELogLevel::Error, callable) + LOG_COMMON_F((context), ramses::ELogLevel::Error, callable) #define LOG_FATAL_F(context, callable) \ - LOG_COMMON_F((context), ::ramses_internal::ELogLevel::Fatal, callable) + LOG_COMMON_F((context), ramses::ELogLevel::Fatal, callable) // LOG_* macros for log message via printf syntax with callable #define LOG_COMMON_PF(context, logLevel, callable) \ @@ -138,27 +137,25 @@ namespace ramses { \ fmt::memory_buffer ramses_fmtlib_buffer; \ callable(ramses_fmtlib_buffer); \ - ::ramses_internal::GetRamsesLogger().log(::ramses_internal::LogMessage((context), (logLevel), ::ramses_internal::StringOutputStream(fmt::to_string(ramses_fmtlib_buffer)))); \ + ramses::internal::GetRamsesLogger().log(ramses::internal::LogMessage((context), (logLevel), ramses::internal::StringOutputStream(fmt::to_string(ramses_fmtlib_buffer)))); \ } \ } while (0) #define LOG_TRACE_PF(context, callable) \ - LOG_COMMON_PF((context), ::ramses_internal::ELogLevel::Trace, callable) + LOG_COMMON_PF((context), ramses::ELogLevel::Trace, callable) #define LOG_INFO_PF(context, callable) \ - LOG_COMMON_PF((context), ::ramses_internal::ELogLevel::Info, callable) + LOG_COMMON_PF((context), ramses::ELogLevel::Info, callable) #define LOG_DEBUG_PF(context, callable) \ - LOG_COMMON_PF((context), ::ramses_internal::ELogLevel::Debug, callable) + LOG_COMMON_PF((context), ramses::ELogLevel::Debug, callable) #define LOG_WARN_PF(context, callable) \ - LOG_COMMON_PF((context), ::ramses_internal::ELogLevel::Warn, callable) + LOG_COMMON_PF((context), ramses::ELogLevel::Warn, callable) #define LOG_ERROR_PF(context, callable) \ - LOG_COMMON_PF((context), ::ramses_internal::ELogLevel::Error, callable) + LOG_COMMON_PF((context), ramses::ELogLevel::Error, callable) #define LOG_FATAL_PF(context, callable) \ - LOG_COMMON_PF((context), ::ramses_internal::ELogLevel::Fatal, callable) + LOG_COMMON_PF((context), ramses::ELogLevel::Fatal, callable) - -#endif diff --git a/framework/Core/Utils/include/Utils/LogMessage.h b/src/framework/internal/Core/Utils/LogMessage.h similarity index 90% rename from framework/Core/Utils/include/Utils/LogMessage.h rename to src/framework/internal/Core/Utils/LogMessage.h index 35544fd3c..3e7cfa5ee 100644 --- a/framework/Core/Utils/include/Utils/LogMessage.h +++ b/src/framework/internal/Core/Utils/LogMessage.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGMESSAGE_H -#define RAMSES_LOGMESSAGE_H +#pragma once -#include "Utils/LogLevel.h" -#include "Collections/StringOutputStream.h" +#include "internal/Core/Utils/LogLevel.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" -namespace ramses_internal +namespace ramses::internal { class LogContext; @@ -53,5 +52,3 @@ namespace ramses_internal return m_logLevel; } } - -#endif diff --git a/src/framework/internal/Core/Utils/LoggingUtils.h b/src/framework/internal/Core/Utils/LoggingUtils.h new file mode 100644 index 000000000..2eb3609c3 --- /dev/null +++ b/src/framework/internal/Core/Utils/LoggingUtils.h @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/PlatformAbstraction/FmtBase.h" +#include "EnumTraits.h" +#include +#include +#include +#include + +// declares mapping function from enum to strings +#define ENUM_TO_STRING(type, elements, lastElement) \ + /* ensure that elements have the same number of elements as the enum types */ \ + static_assert(ramses::internal::EnumTraits::VerifyElementCountIfSupported(elements.size())); /* NOLINT(bugprone-macro-parentheses) */ \ + static_assert(static_cast(lastElement) + 1 == elements.size(), "number of elements does not match"); /* NOLINT(bugprone-macro-parentheses) */ \ + static inline const char* EnumToString(type index) \ + { \ + const std::size_t value = static_cast(index); \ + if (value > static_cast(lastElement)) \ + { \ + assert(false && "EnumToString called with invalid value"); /* NOLINT(misc-static-assert)*/ \ + return ""; \ + } \ + return elements[value]; /* NOLINT(bugprone-macro-parentheses) */ \ + }; + +#define MAKE_ENUM_CLASS_PRINTABLE(type, enumName, elementNameArray, lastEnumElement) \ + /* ensure that elementNameArray have the same number of elements as the enum types */ \ + static_assert(ramses::internal::EnumTraits::VerifyElementCountIfSupported(elementNameArray.size())); /* NOLINT(bugprone-macro-parentheses) */ \ + static_assert(static_cast(lastEnumElement) < std::numeric_limits::max()); \ + static_assert(static_cast(lastEnumElement) + 1 == static_cast(elementNameArray.size()), "number of elements does not match"); /* NOLINT(bugprone-macro-parentheses) */ \ + static_assert(std::is_enum::value && !std::is_convertible::value, "Must use with enum class"); \ + template <> struct fmt::formatter: formatter \ + { \ + static constexpr const auto lastValue = static_cast(lastEnumElement); \ + bool shortLog = false; \ + template constexpr auto parse(ParseContext& ctx) \ + { \ + auto it = ctx.begin(); \ + const auto end = ctx.end(); \ + if (it != end && *it == 's') \ + { \ + ++it; \ + shortLog = true; \ + } \ + if (it != end && *it != '}') \ + assert(false && "invalid format for enum class"); /* NOLINT(misc-static-assert) */ \ + return it; \ + } \ + template constexpr auto format(type index, FormatContext& ctx) \ + { \ + const auto value = static_cast(index); \ + if (value > lastValue) \ + return fmt::format_to(ctx.out(), "", value); \ + return shortLog ? fmt::format_to(ctx.out(), "{}", elementNameArray[value]) /* NOLINT(bugprone-macro-parentheses) */ \ + : fmt::format_to(ctx.out(), "{}::{}", enumName, elementNameArray[value]); /* NOLINT(bugprone-macro-parentheses) */ \ + } \ + }; diff --git a/framework/Core/Utils/include/Utils/MemoryPool.h b/src/framework/internal/Core/Utils/MemoryPool.h similarity index 97% rename from framework/Core/Utils/include/Utils/MemoryPool.h rename to src/framework/internal/Core/Utils/MemoryPool.h index 11aeb15ef..8c278076a 100644 --- a/framework/Core/Utils/include/Utils/MemoryPool.h +++ b/src/framework/internal/Core/Utils/MemoryPool.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MEMORYPOOL_H -#define RAMSES_MEMORYPOOL_H +#pragma once -#include "Utils/HandlePool.h" -#include "Utils/MemoryPoolIterator.h" +#include "internal/Core/Utils/HandlePool.h" +#include "internal/Core/Utils/MemoryPoolIterator.h" #include #include -namespace ramses_internal +namespace ramses::internal { template class MemoryPool final @@ -192,5 +191,3 @@ namespace ramses_internal return const_iterator(*this, HANDLE(getTotalCount())); } } - -#endif diff --git a/framework/Core/Utils/include/Utils/MemoryPoolExplicit.h b/src/framework/internal/Core/Utils/MemoryPoolExplicit.h similarity index 96% rename from framework/Core/Utils/include/Utils/MemoryPoolExplicit.h rename to src/framework/internal/Core/Utils/MemoryPoolExplicit.h index c17f58e05..2a64f552e 100644 --- a/framework/Core/Utils/include/Utils/MemoryPoolExplicit.h +++ b/src/framework/internal/Core/Utils/MemoryPoolExplicit.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MEMORYPOOLEXPLICIT_H -#define RAMSES_MEMORYPOOLEXPLICIT_H +#pragma once -#include "Common/TypedMemoryHandle.h" -#include "Utils/MemoryPoolIterator.h" -#include "Collections/Vector.h" +#include "internal/Core/Common/TypedMemoryHandle.h" +#include "internal/Core/Utils/MemoryPoolIterator.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include -namespace ramses_internal +namespace ramses::internal { template class MemoryPoolExplicit final @@ -172,5 +171,3 @@ namespace ramses_internal return const_iterator(*this, HANDLE(getTotalCount())); } } - -#endif diff --git a/framework/Core/Utils/include/Utils/MemoryPoolIterator.h b/src/framework/internal/Core/Utils/MemoryPoolIterator.h similarity index 95% rename from framework/Core/Utils/include/Utils/MemoryPoolIterator.h rename to src/framework/internal/Core/Utils/MemoryPoolIterator.h index 65ad1434b..c4105eb43 100644 --- a/framework/Core/Utils/include/Utils/MemoryPoolIterator.h +++ b/src/framework/internal/Core/Utils/MemoryPoolIterator.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MEMORYPOOLITERATOR_H -#define RAMSES_MEMORYPOOLITERATOR_H +#pragma once #include #include #include -namespace ramses_internal +namespace ramses::internal { template class memory_pool_iterator_t @@ -54,6 +53,7 @@ namespace ramses_internal return *this; } + // NOLINTNEXTLINE(readability-const-return-type): required by cert-dcl21-cpp const memory_pool_iterator_t operator++(int) { auto currentIt = *this; @@ -70,9 +70,13 @@ namespace ramses_internal if (m_memPool && start < m_memPool->getTotalCount()) { if (m_memPool->isAllocated(start)) + { m_current.second = m_memPool->getMemory(start); + } else + { seekNextAllocated(); + } } } @@ -137,5 +141,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/MemoryUtils.h b/src/framework/internal/Core/Utils/MemoryUtils.h similarity index 70% rename from framework/Core/Utils/include/Utils/MemoryUtils.h rename to src/framework/internal/Core/Utils/MemoryUtils.h index fd1e00246..caab85c6a 100644 --- a/framework/Core/Utils/include/Utils/MemoryUtils.h +++ b/src/framework/internal/Core/Utils/MemoryUtils.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MEMORYUTILS_H -#define RAMSES_MEMORYUTILS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include -namespace ramses_internal +namespace ramses::internal { class MemoryUtils { @@ -21,13 +20,11 @@ namespace ramses_internal { assert(elementCount > 0u); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) safe to get Byte* - const Byte* data = reinterpret_cast(elements); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) safe to get std::byte* + const auto* data = reinterpret_cast(elements); const uint32_t dataSize = sizeof(T) * elementCount; - return (*data == 0) && (PlatformMemory::Compare(data, data + 1u, dataSize - 1u) == 0); + return (*data == std::byte{0}) && (PlatformMemory::Compare(data, data + 1u, dataSize - 1u) == 0); } }; } - -#endif diff --git a/framework/Core/Utils/src/PeriodicLogger.cpp b/src/framework/internal/Core/Utils/PeriodicLogger.cpp similarity index 95% rename from framework/Core/Utils/src/PeriodicLogger.cpp rename to src/framework/internal/Core/Utils/PeriodicLogger.cpp index fe1f43c8c..c9d3ba70c 100644 --- a/framework/Core/Utils/src/PeriodicLogger.cpp +++ b/src/framework/internal/Core/Utils/PeriodicLogger.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/PeriodicLogger.h" -#include "Utils/PeriodicLoggerHelper.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/PeriodicLogger.h" +#include "internal/Core/Utils/PeriodicLoggerHelper.h" +#include "internal/Core/Utils/LogMacros.h" #include "ramses-sdk-build-config.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/PlatformAbstraction/PlatformTime.h" -namespace ramses_internal +namespace ramses::internal { const uint64_t PeriodicLogger::m_processStartupTime = PlatformTime::GetMillisecondsMonotonic(); std::atomic PeriodicLogger::m_numberOfRamsesInstancesStartedInProcess(0); @@ -133,7 +133,7 @@ namespace ramses_internal << " Env:" << ::ramses_sdk::RAMSES_SDK_BUILD_ENV_VERSION_INFO_FULL << " SyncT:" << asMilliseconds(syncNow) << "ms (dtSteady:" << steadyDiff << " - dtSync:" << syncDiff << " -> " << (steadyDiff - syncDiff) << ")" << " PUp:" << pUp << " RUp:" << rUp - << " RInit:" << m_numberOfRamsesInstancesStartedInProcess << " RParallel:" << m_numberOfRamsesInstancesCurrentlyActive); + << " RInit:" << m_numberOfRamsesInstancesStartedInProcess.load() << " RParallel:" << m_numberOfRamsesInstancesCurrentlyActive.load()); m_previousSyncTime = syncNow; m_previousSteadyTime = steadyNow; @@ -141,7 +141,7 @@ namespace ramses_internal void PeriodicLogger::printStatistic() { - LOG_INFO_F(CONTEXT_PERIODIC, ([&](ramses_internal::StringOutputStream& output) { + LOG_INFO_F(CONTEXT_PERIODIC, ([&](ramses::internal::StringOutputStream& output) { uint32_t numberTimeIntervals = m_statisticCollection.getNumberTimeIntervalsSinceLastSummaryReset(); output << "msgIn "; logStatisticSummaryEntry(output, m_statisticCollection.statMessagesReceived.getSummary(), numberTimeIntervals); @@ -163,7 +163,7 @@ namespace ramses_internal if (m_statisticCollectionScenes.size() > 0) { - LOG_INFO_F(CONTEXT_PERIODIC, ([&](ramses_internal::StringOutputStream& output) { + LOG_INFO_F(CONTEXT_PERIODIC, ([&](ramses::internal::StringOutputStream& output) { for (auto entry : m_statisticCollectionScenes) { uint32_t numberTimeIntervals = entry.value->getNumberTimeIntervalsSinceLastSummaryReset(); diff --git a/framework/Core/Utils/include/Utils/PeriodicLogger.h b/src/framework/internal/Core/Utils/PeriodicLogger.h similarity index 78% rename from framework/Core/Utils/include/Utils/PeriodicLogger.h rename to src/framework/internal/Core/Utils/PeriodicLogger.h index d0fe22fae..fb49ccd74 100644 --- a/framework/Core/Utils/include/Utils/PeriodicLogger.h +++ b/src/framework/internal/Core/Utils/PeriodicLogger.h @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PERIODICLOGGER_H -#define RAMSES_PERIODICLOGGER_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "PlatformAbstraction/PlatformEvent.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "PlatformAbstraction/synchronized_clock.h" -#include "Collections/Vector.h" -#include "Utils/IPeriodicLogSupplier.h" -#include "Utils/StatisticCollection.h" -#include "Collections/HashMap.h" -#include "SceneAPI/SceneId.h" +#pragma once + +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/PlatformEvent.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/PlatformAbstraction/synchronized_clock.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Core/Utils/IPeriodicLogSupplier.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { class PeriodicLogger : public Runnable { @@ -61,5 +61,3 @@ namespace ramses_internal static std::atomic m_numberOfRamsesInstancesStartedInProcess; }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/PeriodicLoggerHelper.h b/src/framework/internal/Core/Utils/PeriodicLoggerHelper.h similarity index 88% rename from framework/Core/Utils/include/Utils/PeriodicLoggerHelper.h rename to src/framework/internal/Core/Utils/PeriodicLoggerHelper.h index 355f39d95..fb484912b 100644 --- a/framework/Core/Utils/include/Utils/PeriodicLoggerHelper.h +++ b/src/framework/internal/Core/Utils/PeriodicLoggerHelper.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PERIODICLOGGERHELPER_H -#define RAMSES_PERIODICLOGGERHELPER_H +#pragma once -#include "Collections/StringOutputStream.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/StatisticCollection.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Utils/StatisticCollection.h" -namespace ramses_internal +#include + +namespace ramses::internal { template void logStatisticSummaryEntry(StringOutputStream& stream, const SummaryEntry& summary, uint32_t numberTimeIntervals) @@ -21,9 +21,13 @@ namespace ramses_internal if (numberTimeIntervals > 0) { if (summary.minValue == summary.maxValue) + { stream << "(" << summary.minValue << ")"; + } else + { stream << "(" << summary.minValue << "/" << summary.maxValue << "/" << summary.sum / numberTimeIntervals << ")"; + } } else { @@ -58,5 +62,3 @@ namespace ramses_internal } } } - -#endif diff --git a/framework/Core/Utils/src/RamsesLogger.cpp b/src/framework/internal/Core/Utils/RamsesLogger.cpp similarity index 89% rename from framework/Core/Utils/src/RamsesLogger.cpp rename to src/framework/internal/Core/Utils/RamsesLogger.cpp index 2f1b38e30..1948fdcfb 100644 --- a/framework/Core/Utils/src/RamsesLogger.cpp +++ b/src/framework/internal/Core/Utils/RamsesLogger.cpp @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/RamsesLogger.h" -#include "Utils/ConsoleLogAppender.h" -#include "Utils/LogContext.h" -#include "Utils/LogHelper.h" -#include "Utils/LogMacros.h" -#include "DltLogAppender/DltLogAppender.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/Core/Utils/ConsoleLogAppender.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/LogHelper.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/DltLogAppender/DltLogAppender.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" #include #ifdef __ANDROID__ - #include + #include "internal/Core/Utils/AndroidLogger/AndroidLogAppender.h" #endif -namespace ramses_internal +namespace ramses::internal { RamsesLogger::RamsesLogger() : m_isInitialized(false) - , m_consoleLogAppender() , m_fileTransferContext(createContext("File Transfer Context", "FILE")) { m_consoleLogAppender.setLogLevel(LogLevelDefault_Console); @@ -35,21 +34,10 @@ namespace ramses_internal #endif } - RamsesLogger::~RamsesLogger() - { - for (auto& ctx : m_logContexts) - { - delete ctx; - } - } + RamsesLogger::~RamsesLogger() = default; void RamsesLogger::initialize(const RamsesLoggerConfig& config, bool disableDLT, bool enableDLTApplicationRegistration) { - if (m_isInitialized) - { - // should not be called multiple times but happens when more than one RamsesFramework is created - LOG_WARN(CONTEXT_FRAMEWORK, "RamsesLogger::initialize called more than once"); - } m_isInitialized = true; bool pushLogLevelToDltDaemon = false; @@ -101,12 +89,15 @@ namespace ramses_internal if (!alreadyHadDltAppender) { + std::vector logContextPtrs; + for (auto& ctx : m_logContexts) + logContextPtrs.push_back(ctx.get()); DltAdapter* dltAdapter = DltAdapter::getDltAdapter(); if (dltAdapter->initialize(config.dltAppId, config.dltAppDescription, enableDLTApplicationRegistration, [this](const std::string& contextId_, int logLevel_) { dltLogLevelChangeCallback(contextId_, logLevel_); }, - m_logContexts, pushLogLevelToDltDaemon)) + logContextPtrs, pushLogLevelToDltDaemon)) { m_dltLogAppender = std::make_unique(); m_logAppenders.push_back(m_dltLogAppender.get()); @@ -214,6 +205,16 @@ namespace ramses_internal return DltAdapter::getDltAdapter()->transmitFile(m_fileTransferContext, path, deleteFile); } + bool RamsesLogger::transmit(std::vector && data, const std::string &filename) const + { + if (!m_dltLogAppender) + { + return false; + } + + return DltAdapter::getDltAdapter()->transmit(m_fileTransferContext, std::move(data), filename); + } + bool RamsesLogger::registerInjectionCallback(LogContext& ctx, uint32_t serviceId, int (*callback)(uint32_t serviceId, void* data, uint32_t length)) { if (!m_dltLogAppender) @@ -236,9 +237,9 @@ namespace ramses_internal LOG_ERROR(CONTEXT_FRAMEWORK, "RamsesLogger::createContext: Context creation not allowed after initialize"); assert(false); } - LogContext* ctx = new LogContext(name, id); - m_logContexts.push_back(ctx); - return *ctx; + m_logContexts.push_back(std::make_unique(name, id)); + + return *m_logContexts.back(); } void RamsesLogger::dltLogLevelChangeCallback(const std::string& contextId, int logLevelAsInt) @@ -271,8 +272,10 @@ namespace ramses_internal LogContext* RamsesLogger::getLogContextById(const std::string& contextId) { for (const auto& ctx : m_logContexts) + { if (contextId == ctx->getContextId()) - return ctx; + return ctx.get(); + } return nullptr; } @@ -305,7 +308,7 @@ namespace ramses_internal } } - void RamsesLogger::setLogHandler(const ramses::LogHandlerFunc& logHandlerFunc) + void RamsesLogger::setLogHandler(const LogHandlerFunc& logHandlerFunc) { if (logHandlerFunc) { diff --git a/framework/Core/Utils/include/Utils/RamsesLogger.h b/src/framework/internal/Core/Utils/RamsesLogger.h similarity index 80% rename from framework/Core/Utils/include/Utils/RamsesLogger.h rename to src/framework/internal/Core/Utils/RamsesLogger.h index 23edea2d0..9e01e446b 100644 --- a/framework/Core/Utils/include/Utils/RamsesLogger.h +++ b/src/framework/internal/Core/Utils/RamsesLogger.h @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UTILS_RAMSESLOGGER_H -#define RAMSES_UTILS_RAMSESLOGGER_H +#pragma once -#include "Utils/LogLevel.h" -#include "Utils/LogContext.h" -#include "Utils/ConsoleLogAppender.h" -#include "Utils/LogAppenderBase.h" -#include "Utils/UserLogAppender.h" -#include "Collections/Vector.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/Core/Utils/LogLevel.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/ConsoleLogAppender.h" +#include "internal/Core/Utils/LogAppenderBase.h" +#include "internal/Core/Utils/UserLogAppender.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include #include #include #include #include #include -namespace ramses_internal +namespace ramses::internal { class DltLogAppender; @@ -33,7 +32,7 @@ namespace ramses_internal std::optional logLevel; std::optional logLevelConsole; // output specific loglevels can overwrite generic argument - std::map logLevelContexts; // TODO: std::unordered_map + std::map logLevelContexts{}; // TODO: std::unordered_map std::string dltAppId = "RAMS"; std::string dltAppDescription = "RAMS-DESC"; }; @@ -71,11 +70,12 @@ namespace ramses_internal void removeAfterConsoleLogCallback(); [[nodiscard]] bool transmitFile(const std::string& path, bool deleteFile) const; + bool transmit(std::vector&& data, const std::string& filename) const; bool registerInjectionCallback(LogContext& ctx, uint32_t serviceId, int (*callback)(uint32_t serviceId, void* data, uint32_t length)); static const char* GetLogLevelText(ELogLevel logLevel); - void setLogHandler(const ramses::LogHandlerFunc& logHandlerFunc); + void setLogHandler(const LogHandlerFunc& logHandlerFunc); private: static const ELogLevel LogLevelDefault_Contexts = ELogLevel::Info; @@ -87,12 +87,12 @@ namespace ramses_internal LogContext* getLogContextById(const std::string& contextId); std::mutex m_appenderLock; - bool m_isInitialized; + std::atomic_bool m_isInitialized; ConsoleLogAppender m_consoleLogAppender; std::unique_ptr m_dltLogAppender; std::unique_ptr m_platformLogAppender; std::unique_ptr m_userLogAppender; - std::vector m_logContexts; + std::vector> m_logContexts; std::vector m_logAppenders; LogContext& m_fileTransferContext; }; @@ -103,5 +103,3 @@ namespace ramses_internal return logger; } } - -#endif diff --git a/framework/Core/Utils/include/Utils/RawBinaryOutputStream.h b/src/framework/internal/Core/Utils/RawBinaryOutputStream.h similarity index 67% rename from framework/Core/Utils/include/Utils/RawBinaryOutputStream.h rename to src/framework/internal/Core/Utils/RawBinaryOutputStream.h index e320ea31d..599211e8e 100644 --- a/framework/Core/Utils/include/Utils/RawBinaryOutputStream.h +++ b/src/framework/internal/Core/Utils/RawBinaryOutputStream.h @@ -6,35 +6,36 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAWBINARYOUTPUTSTREAM_H -#define RAMSES_RAWBINARYOUTPUTSTREAM_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" + +#include #include #include -namespace ramses_internal +namespace ramses::internal { class RawBinaryOutputStream: public IOutputStream { public: - explicit RawBinaryOutputStream(Byte* data, size_t size); + explicit RawBinaryOutputStream(std::byte* data, size_t size); IOutputStream& write(const void* data, size_t size) override; + EStatus getPos(size_t& position) const override; - [[nodiscard]] const Byte* getData() const; + [[nodiscard]] const std::byte* getData() const; [[nodiscard]] size_t getSize() const; [[nodiscard]] size_t getBytesWritten() const; private: - Byte* m_dataBase; - Byte* m_data; + std::byte* m_dataBase; + std::byte* m_data; size_t m_size; }; inline - RawBinaryOutputStream::RawBinaryOutputStream(Byte* data, size_t size) + RawBinaryOutputStream::RawBinaryOutputStream(std::byte* data, size_t size) : m_dataBase(data) , m_data(data) , m_size(size) @@ -45,13 +46,19 @@ namespace ramses_internal inline IOutputStream& RawBinaryOutputStream::write(const void* data, const size_t size) { assert(m_data + size <= m_dataBase + m_size); - if (size) + if (size != 0u) std::memcpy(m_data, data, size); m_data += size; return *this; } - inline const Byte* RawBinaryOutputStream::getData() const + inline EStatus RawBinaryOutputStream::getPos(size_t& position) const + { + position = m_data - m_dataBase; + return EStatus::Ok; + } + + inline const std::byte* RawBinaryOutputStream::getData() const { return m_dataBase; } @@ -66,5 +73,3 @@ namespace ramses_internal return m_data - m_dataBase; } } - -#endif diff --git a/framework/Core/Utils/src/StatisticCollection.cpp b/src/framework/internal/Core/Utils/StatisticCollection.cpp similarity index 96% rename from framework/Core/Utils/src/StatisticCollection.cpp rename to src/framework/internal/Core/Utils/StatisticCollection.cpp index 04d83f9d5..fe023f6ec 100644 --- a/framework/Core/Utils/src/StatisticCollection.cpp +++ b/src/framework/internal/Core/Utils/StatisticCollection.cpp @@ -6,17 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/StatisticCollection.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { - - StatisticCollection::StatisticCollection() - : m_numberTimeIntervalsSinceLastSummaryReset(0) - { - } - void StatisticCollection::reset() { m_numberTimeIntervalsSinceLastSummaryReset = 0; diff --git a/framework/Core/Utils/include/Utils/StatisticCollection.h b/src/framework/internal/Core/Utils/StatisticCollection.h similarity index 94% rename from framework/Core/Utils/include/Utils/StatisticCollection.h rename to src/framework/internal/Core/Utils/StatisticCollection.h index 448404866..c2a44374e 100644 --- a/framework/Core/Utils/include/Utils/StatisticCollection.h +++ b/src/framework/internal/Core/Utils/StatisticCollection.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STATISTICCOLLECTION_H -#define RAMSES_STATISTICCOLLECTION_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include #include #include #include #include #include -namespace ramses_internal +namespace ramses::internal { template struct SummaryEntry final @@ -32,7 +31,7 @@ namespace ramses_internal }; template - ramses_internal::SummaryEntry::SummaryEntry() + ramses::internal::SummaryEntry::SummaryEntry() { reset(); } @@ -152,10 +151,7 @@ namespace ramses_internal class StatisticCollection { public: - StatisticCollection(); - - virtual ~StatisticCollection() - {} + virtual ~StatisticCollection() = default; virtual void reset(); virtual void resetSummaries(); @@ -164,7 +160,7 @@ namespace ramses_internal [[nodiscard]] uint32_t getNumberTimeIntervalsSinceLastSummaryReset() const; protected: - uint32_t m_numberTimeIntervalsSinceLastSummaryReset; + uint32_t m_numberTimeIntervalsSinceLastSummaryReset{0u}; }; class StatisticCollectionFramework : public StatisticCollection @@ -220,5 +216,3 @@ namespace ramses_internal }; } -#endif - diff --git a/framework/Core/Utils/src/StringUtils.cpp b/src/framework/internal/Core/Utils/StringUtils.cpp similarity index 87% rename from framework/Core/Utils/src/StringUtils.cpp rename to src/framework/internal/Core/Utils/StringUtils.cpp index 7dc3b3775..354df0a8f 100644 --- a/framework/Core/Utils/src/StringUtils.cpp +++ b/src/framework/internal/Core/Utils/StringUtils.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/StringUtils.h" +#include "internal/Core/Utils/StringUtils.h" namespace { @@ -16,12 +16,12 @@ namespace while (!string.empty()) { const auto pos = string.find(splitChar); - if (pos == string.npos) + if (pos == std::string_view::npos) { f(string); break; } - if (const auto s = string.substr(0, pos); s.size() != 0 && s != " ") + if (const auto s = string.substr(0, pos); !s.empty() && s != " ") { f(s); } @@ -30,7 +30,7 @@ namespace } } // namespace -namespace ramses_internal +namespace ramses::internal { std::string StringUtils::Trim(std::string_view string) { @@ -39,7 +39,7 @@ namespace ramses_internal std::string_view StringUtils::TrimView(std::string_view string) { - if (const auto pos = string.find_first_not_of("\t\n\v\f\r "); pos != string.npos) + if (const auto pos = string.find_first_not_of("\t\n\v\f\r "); pos != std::string_view::npos) { string.remove_prefix(pos); } @@ -48,7 +48,7 @@ namespace ramses_internal return {}; } - if (const auto pos = string.find_last_not_of("\t\n\v\f\r "); pos != string.npos) + if (const auto pos = string.find_last_not_of("\t\n\v\f\r "); pos != std::string_view::npos) { string.remove_suffix(string.size() - pos - 1); } @@ -71,4 +71,4 @@ namespace ramses_internal tokenizeTo(string, split, f); return result; } -} // namespace ramses_internal +} // namespace ramses::internal diff --git a/framework/Core/Utils/include/Utils/StringUtils.h b/src/framework/internal/Core/Utils/StringUtils.h similarity index 91% rename from framework/Core/Utils/include/Utils/StringUtils.h rename to src/framework/internal/Core/Utils/StringUtils.h index 903633630..7f2060ae9 100644 --- a/framework/Core/Utils/include/Utils/StringUtils.h +++ b/src/framework/internal/Core/Utils/StringUtils.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STRINGUTILS_H -#define RAMSES_STRINGUTILS_H +#pragma once -#include "Collections/HashSet.h" -#include "Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include #include -namespace ramses_internal +namespace ramses::internal { class StringUtils { @@ -51,5 +50,3 @@ namespace ramses_internal static HashSet TokenizeToSet(std::string_view string, const char split = ' '); }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/TextureMathUtils.h b/src/framework/internal/Core/Utils/TextureMathUtils.h similarity index 94% rename from framework/Core/Utils/include/Utils/TextureMathUtils.h rename to src/framework/internal/Core/Utils/TextureMathUtils.h index ed248a89a..e5af707eb 100644 --- a/framework/Core/Utils/include/Utils/TextureMathUtils.h +++ b/src/framework/internal/Core/Utils/TextureMathUtils.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTUREMATHUTILS_H -#define RAMSES_TEXTUREMATHUTILS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { class TextureMathUtils { @@ -60,5 +59,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/Core/Utils/include/Utils/ThreadBarrier.h b/src/framework/internal/Core/Utils/ThreadBarrier.h similarity index 80% rename from framework/Core/Utils/include/Utils/ThreadBarrier.h rename to src/framework/internal/Core/Utils/ThreadBarrier.h index 8fc95cbda..a1dbd7361 100644 --- a/framework/Core/Utils/include/Utils/ThreadBarrier.h +++ b/src/framework/internal/Core/Utils/ThreadBarrier.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_THREADBARRIER_H -#define RAMSES_THREADBARRIER_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class ThreadBarrier { @@ -26,13 +25,11 @@ namespace ramses_internal private: std::mutex m_lock; std::condition_variable m_condVar; - int m_requiredWaiters; - int m_currentWaiters; + int m_requiredWaiters{0}; + int m_currentWaiters{0}; }; inline ThreadBarrier::ThreadBarrier(int num) - : m_requiredWaiters(0) - , m_currentWaiters(0) { init_wait_for_num(num); } @@ -48,10 +45,12 @@ namespace ramses_internal { std::unique_lock l(m_lock); if (++m_currentWaiters == m_requiredWaiters) + { m_condVar.notify_all(); + } else - m_condVar.wait(l, [&](){ return m_currentWaiters >= m_requiredWaiters; }); + { + m_condVar.wait(l, [&]() { return m_currentWaiters >= m_requiredWaiters; }); + } } } - -#endif diff --git a/framework/Core/Utils/src/ThreadLocalLog.cpp b/src/framework/internal/Core/Utils/ThreadLocalLog.cpp similarity index 93% rename from framework/Core/Utils/src/ThreadLocalLog.cpp rename to src/framework/internal/Core/Utils/ThreadLocalLog.cpp index a938d4949..d523bff02 100644 --- a/framework/Core/Utils/src/ThreadLocalLog.cpp +++ b/src/framework/internal/Core/Utils/ThreadLocalLog.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #include -namespace ramses_internal +namespace ramses::internal { namespace TLSPrefix { diff --git a/framework/Core/Utils/include/Utils/ThreadLocalLog.h b/src/framework/internal/Core/Utils/ThreadLocalLog.h similarity index 51% rename from framework/Core/Utils/include/Utils/ThreadLocalLog.h rename to src/framework/internal/Core/Utils/ThreadLocalLog.h index e2a074633..9e8aab4ec 100644 --- a/framework/Core/Utils/include/Utils/ThreadLocalLog.h +++ b/src/framework/internal/Core/Utils/ThreadLocalLog.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_THREADLOCALLOG_H -#define RAMSES_THREADLOCALLOG_H +#pragma once -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { class ThreadLocalLog { @@ -23,77 +22,77 @@ namespace ramses_internal } #define LOG_COMMON_R(context, logLevel, message) \ - LOG_COMMON((context), logLevel, ::ramses_internal::ThreadLocalLog::GetPrefix() << ": " << message) + LOG_COMMON((context), logLevel, ramses::internal::ThreadLocalLog::GetPrefix() << ": " << message) // NOLINT(bugprone-macro-parentheses) #define LOG_TRACE_R(context, message) \ - LOG_COMMON_R(context, ::ramses_internal::ELogLevel::Trace, message) + LOG_COMMON_R(context, ramses::ELogLevel::Trace, message) #define LOG_DEBUG_R(context, message) \ - LOG_COMMON_R(context, ::ramses_internal::ELogLevel::Debug, message) + LOG_COMMON_R(context, ramses::ELogLevel::Debug, message) #define LOG_INFO_R(context, message) \ - LOG_COMMON_R(context, ::ramses_internal::ELogLevel::Info, message) + LOG_COMMON_R(context, ramses::ELogLevel::Info, message) #define LOG_WARN_R(context, message) \ - LOG_COMMON_R(context, ::ramses_internal::ELogLevel::Warn, message) + LOG_COMMON_R(context, ramses::ELogLevel::Warn, message) #define LOG_ERROR_R(context, message) \ - LOG_COMMON_R(context, ::ramses_internal::ELogLevel::Error, message) + LOG_COMMON_R(context, ramses::ELogLevel::Error, message) #define LOG_FATAL_R(context, message) \ - LOG_COMMON_R(context, ::ramses_internal::ELogLevel::Fatal, message) + LOG_COMMON_R(context, ramses::ELogLevel::Fatal, message) #define LOG_COMMON_RP(context, logLevel, ...) \ - LOG_COMMON_P((context), logLevel, ::fmt::format("{}: {}", ::ramses_internal::ThreadLocalLog::GetPrefix(), ::fmt::format(__VA_ARGS__))) + LOG_COMMON_P((context), logLevel, ::fmt::format("{}: {}", ramses::internal::ThreadLocalLog::GetPrefix(), ::fmt::format(__VA_ARGS__))) #define LOG_TRACE_RP(context, ...) \ - LOG_COMMON_RP(context, ::ramses_internal::ELogLevel::Trace, __VA_ARGS__) + LOG_COMMON_RP(context, ramses::ELogLevel::Trace, __VA_ARGS__) #define LOG_DEBUG_RP(context, ...) \ - LOG_COMMON_RP(context, ::ramses_internal::ELogLevel::Debug, __VA_ARGS__) + LOG_COMMON_RP(context, ramses::ELogLevel::Debug, __VA_ARGS__) #define LOG_INFO_RP(context, ...) \ - LOG_COMMON_RP(context, ::ramses_internal::ELogLevel::Info, __VA_ARGS__) + LOG_COMMON_RP(context, ramses::ELogLevel::Info, __VA_ARGS__) #define LOG_WARN_RP(context, ...) \ - LOG_COMMON_RP(context, ::ramses_internal::ELogLevel::Warn, __VA_ARGS__) + LOG_COMMON_RP(context, ramses::ELogLevel::Warn, __VA_ARGS__) #define LOG_ERROR_RP(context, ...) \ - LOG_COMMON_RP(context, ::ramses_internal::ELogLevel::Error, __VA_ARGS__) + LOG_COMMON_RP(context, ramses::ELogLevel::Error, __VA_ARGS__) #define LOG_FATAL_RP(context, ...) \ - LOG_COMMON_RP(context, ::ramses_internal::ELogLevel::Fatal, __VA_ARGS__) + LOG_COMMON_RP(context, ramses::ELogLevel::Fatal, __VA_ARGS__) #define LOG_COMMON_RF(context, logLevel, callable) \ do { \ if((logLevel) <= (context).getLogLevel()) \ { \ - ::ramses_internal::StringOutputStream ramses_log_stream(160); \ - ramses_log_stream << ::ramses_internal::ThreadLocalLog::GetPrefix() << ": "; \ + ramses::internal::StringOutputStream ramses_log_stream(160); \ + ramses_log_stream << ramses::internal::ThreadLocalLog::GetPrefix() << ": "; \ callable(ramses_log_stream); \ - ::ramses_internal::GetRamsesLogger().log(::ramses_internal::LogMessage((context), (logLevel), ramses_log_stream)); \ + ramses::internal::GetRamsesLogger().log(ramses::internal::LogMessage((context), (logLevel), ramses_log_stream)); \ } \ } while (0) #define LOG_TRACE_RF(context, callable) \ - LOG_COMMON_RF((context), ::ramses_internal::ELogLevel::Trace, callable) + LOG_COMMON_RF((context), ramses::ELogLevel::Trace, callable) #define LOG_INFO_RF(context, callable) \ - LOG_COMMON_RF((context), ::ramses_internal::ELogLevel::Info, callable) + LOG_COMMON_RF((context), ramses::ELogLevel::Info, callable) #define LOG_DEBUG_RF(context, callable) \ - LOG_COMMON_RF((context), ::ramses_internal::ELogLevel::Debug, callable) + LOG_COMMON_RF((context), ramses::ELogLevel::Debug, callable) #define LOG_WARN_RF(context, callable) \ - LOG_COMMON_RF((context), ::ramses_internal::ELogLevel::Warn, callable) + LOG_COMMON_RF((context), ramses::ELogLevel::Warn, callable) #define LOG_ERROR_RF(context, callable) \ - LOG_COMMON_RF((context), ::ramses_internal::ELogLevel::Error, callable) + LOG_COMMON_RF((context), ramses::ELogLevel::Error, callable) #define LOG_FATAL_RF(context, callable) \ - LOG_COMMON_RF((context), ::ramses_internal::ELogLevel::Fatal, callable) + LOG_COMMON_RF((context), ramses::ELogLevel::Fatal, callable) #define LOG_COMMON_RPF(context, logLevel, callable) \ @@ -101,28 +100,26 @@ namespace ramses_internal if ((logLevel) <= (context).getLogLevel()) \ { \ fmt::memory_buffer ramses_fmtlib_buffer; \ - fmt::format_to(ramses_fmtlib_buffer, "{}: ", ::ramses_internal::ThreadLocalLog::GetPrefix()); \ + fmt::format_to(std::back_inserter(ramses_fmtlib_buffer), "{}: ", ramses::internal::ThreadLocalLog::GetPrefix()); \ callable(ramses_fmtlib_buffer); \ - ::ramses_internal::GetRamsesLogger().log(::ramses_internal::LogMessage((context), (logLevel), ::ramses_internal::StringOutputStream(fmt::to_string(ramses_fmtlib_buffer)))); \ + ramses::internal::GetRamsesLogger().log(ramses::internal::LogMessage((context), (logLevel), ramses::internal::StringOutputStream(fmt::to_string(ramses_fmtlib_buffer)))); \ } \ } while (0) #define LOG_TRACE_RPF(context, callable) \ - LOG_COMMON_RPF((context), ::ramses_internal::ELogLevel::Trace, callable) + LOG_COMMON_RPF((context), ramses::ELogLevel::Trace, callable) #define LOG_INFO_RPF(context, callable) \ - LOG_COMMON_RPF((context), ::ramses_internal::ELogLevel::Info, callable) + LOG_COMMON_RPF((context), ramses::ELogLevel::Info, callable) #define LOG_DEBUG_RPF(context, callable) \ - LOG_COMMON_RPF((context), ::ramses_internal::ELogLevel::Debug, callable) + LOG_COMMON_RPF((context), ramses::ELogLevel::Debug, callable) #define LOG_WARN_RPF(context, callable) \ - LOG_COMMON_RPF((context), ::ramses_internal::ELogLevel::Warn, callable) + LOG_COMMON_RPF((context), ramses::ELogLevel::Warn, callable) #define LOG_ERROR_RPF(context, callable) \ - LOG_COMMON_RPF((context), ::ramses_internal::ELogLevel::Error, callable) + LOG_COMMON_RPF((context), ramses::ELogLevel::Error, callable) #define LOG_FATAL_RPF(context, callable) \ - LOG_COMMON_RPF((context), ::ramses_internal::ELogLevel::Fatal, callable) - -#endif + LOG_COMMON_RPF((context), ramses::ELogLevel::Fatal, callable) diff --git a/framework/Core/Utils/include/Utils/ThreadLocalLogForced.h b/src/framework/internal/Core/Utils/ThreadLocalLogForced.h similarity index 94% rename from framework/Core/Utils/include/Utils/ThreadLocalLogForced.h rename to src/framework/internal/Core/Utils/ThreadLocalLogForced.h index dcb12b3f6..12175af6b 100644 --- a/framework/Core/Utils/include/Utils/ThreadLocalLogForced.h +++ b/src/framework/internal/Core/Utils/ThreadLocalLogForced.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_THREADLOCALLOGFORCED_H -#define RAMSES_THREADLOCALLOGFORCED_H +#pragma once // Include this file to CPP files where a thread local log (log with prefixed thread ID) must always be used. // The standard LOG macros are redefined to use the thread local versions. -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #undef LOG_TRACE #undef LOG_INFO @@ -63,5 +62,3 @@ #define LOG_WARN_PF LOG_WARN_RPF #define LOG_ERROR_PF LOG_ERROR_RPF #define LOG_FATAL_PF LOG_FATAL_RPF - -#endif diff --git a/src/framework/internal/Core/Utils/UserLogAppender.cpp b/src/framework/internal/Core/Utils/UserLogAppender.cpp new file mode 100644 index 000000000..f69d205bf --- /dev/null +++ b/src/framework/internal/Core/Utils/UserLogAppender.cpp @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "internal/Core/Utils/UserLogAppender.h" +#include "internal/Core/Utils/LogMessage.h" +#include "internal/Core/Utils/LogContext.h" + +namespace ramses::internal +{ + UserLogAppender::UserLogAppender(LogHandlerFunc f) + : m_func(std::move(f)) + { + } + + void UserLogAppender::log(const LogMessage& logMessage) + { + m_func(logMessage.getLogLevel(), logMessage.getContext().getContextId(), logMessage.getStream().data()); + } +} diff --git a/framework/Core/Utils/include/Utils/UserLogAppender.h b/src/framework/internal/Core/Utils/UserLogAppender.h similarity index 71% rename from framework/Core/Utils/include/Utils/UserLogAppender.h rename to src/framework/internal/Core/Utils/UserLogAppender.h index b92c305b8..180305b75 100644 --- a/framework/Core/Utils/include/Utils/UserLogAppender.h +++ b/src/framework/internal/Core/Utils/UserLogAppender.h @@ -8,21 +8,21 @@ #pragma once -#include "Utils/LogAppenderBase.h" -#include "Utils/LogLevel.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/Core/Utils/LogAppenderBase.h" +#include "internal/Core/Utils/LogLevel.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses_internal +namespace ramses::internal { class UserLogAppender : public LogAppenderBase { public: - explicit UserLogAppender(const ramses::LogHandlerFunc& f); + explicit UserLogAppender(LogHandlerFunc f); void log(const LogMessage& logMessage) override; private: - ramses::LogHandlerFunc m_func; + LogHandlerFunc m_func; }; } diff --git a/framework/Core/Utils/include/Utils/VectorBinaryOutputStream.h b/src/framework/internal/Core/Utils/VectorBinaryOutputStream.h similarity index 63% rename from framework/Core/Utils/include/Utils/VectorBinaryOutputStream.h rename to src/framework/internal/Core/Utils/VectorBinaryOutputStream.h index f6d01515b..30b7165b9 100644 --- a/framework/Core/Utils/include/Utils/VectorBinaryOutputStream.h +++ b/src/framework/internal/Core/Utils/VectorBinaryOutputStream.h @@ -6,30 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_VECTORBINARYOUTPUTSTREAM_H -#define RAMSES_VECTORBINARYOUTPUTSTREAM_H +#pragma once -#include "Collections/IOutputStream.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" #include "absl/types/span.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { class VectorBinaryOutputStream: public IOutputStream { public: - explicit VectorBinaryOutputStream(std::vector& vecRef); + explicit VectorBinaryOutputStream(std::vector& vecRef); - [[nodiscard]] absl::Span asSpan() const; + [[nodiscard]] absl::Span asSpan() const; IOutputStream& write(const void* data, size_t size) override; + EStatus getPos(size_t& position) const override; private: - std::vector& m_vecRef; + std::vector& m_vecRef; const size_t m_startSize; }; - inline VectorBinaryOutputStream::VectorBinaryOutputStream(std::vector& vecRef) + inline VectorBinaryOutputStream::VectorBinaryOutputStream(std::vector& vecRef) : m_vecRef(vecRef) , m_startSize(m_vecRef.size()) { @@ -39,18 +40,22 @@ namespace ramses_internal { if (size > 0) { - const Byte* dataByte = static_cast(data); + const auto* dataByte = static_cast(data); m_vecRef.insert(m_vecRef.end(), dataByte, dataByte + size); } return *this; } - inline absl::Span VectorBinaryOutputStream::asSpan() const + inline EStatus VectorBinaryOutputStream::getPos(size_t& position) const + { + position = m_vecRef.size() - m_startSize; + return EStatus::Ok; + } + + inline absl::Span VectorBinaryOutputStream::asSpan() const { if (m_vecRef.empty()) return {}; return {m_vecRef.data() + m_startSize, m_vecRef.size() - m_startSize}; } } - -#endif diff --git a/framework/Core/Utils/include/Utils/VoidOutputStream.h b/src/framework/internal/Core/Utils/VoidOutputStream.h similarity index 67% rename from framework/Core/Utils/include/Utils/VoidOutputStream.h rename to src/framework/internal/Core/Utils/VoidOutputStream.h index d23e1f76e..13a6cb94c 100644 --- a/framework/Core/Utils/include/Utils/VoidOutputStream.h +++ b/src/framework/internal/Core/Utils/VoidOutputStream.h @@ -6,43 +6,41 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_VOIDOUTPUTSTREAM_H -#define RAMSES_VOIDOUTPUTSTREAM_H +#pragma once -#include -#include "Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" -namespace ramses_internal +#include + +namespace ramses::internal { class VoidOutputStream: public IOutputStream { public: - explicit VoidOutputStream(); - IOutputStream& write(const void* data, size_t size) override; + EStatus getPos(size_t& position) const override; [[nodiscard]] size_t getSize() const; private: - size_t m_size; + size_t m_size{0u}; }; - inline - VoidOutputStream::VoidOutputStream() - : m_size(0u) - { - } - inline IOutputStream& VoidOutputStream::write(const void*, size_t size) + inline IOutputStream& VoidOutputStream::write(const void* /*data*/, size_t size) { m_size += size; return *this; } + inline EStatus VoidOutputStream::getPos(size_t& position) const + { + position = m_size; + return EStatus::Ok; + } + inline size_t VoidOutputStream::getSize() const { return m_size; } } - -#endif diff --git a/framework/Core/Utils/include/Utils/Warnings.h b/src/framework/internal/Core/Utils/Warnings.h similarity index 97% rename from framework/Core/Utils/include/Utils/Warnings.h rename to src/framework/internal/Core/Utils/Warnings.h index 15d9c470b..8bff9e5a0 100644 --- a/framework/Core/Utils/include/Utils/Warnings.h +++ b/src/framework/internal/Core/Utils/Warnings.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WARNINGS_H -#define RAMSES_WARNINGS_H +#pragma once #define STRINGIFY(x) #x @@ -37,8 +36,6 @@ # define WARNING_DISABLE_VC(warning) #endif - - #if defined(__GNUC__) // GCC and clang # define WARNING_DISABLE_LINUX(warning) \ _Pragma(STRINGIFY(GCC diagnostic ignored #warning)) @@ -88,6 +85,3 @@ #else # define POP_DISABLE_C_STYLE_CAST_WARNING #endif - - -#endif diff --git a/framework/ramses-framework-api/include/ramses-framework-api/EValidationSeverity.h b/src/framework/internal/DltLogAppender/DltAdapter.h similarity index 63% rename from framework/ramses-framework-api/include/ramses-framework-api/EValidationSeverity.h rename to src/framework/internal/DltLogAppender/DltAdapter.h index 1291c8ac7..95b37c32a 100644 --- a/framework/ramses-framework-api/include/ramses-framework-api/EValidationSeverity.h +++ b/src/framework/internal/DltLogAppender/DltAdapter.h @@ -6,20 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EVALIDATIONSEVERITY_H -#define RAMSES_EVALIDATIONSEVERITY_H - -namespace ramses -{ - /** - * @brief The EValidationSeverity defines severity of validation results - */ - enum class EValidationSeverity - { - Info, - Warning, - Error - }; -} +#pragma once +#ifdef DLT_ENABLED +#include "internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h" +using DltAdapter = ramses::internal::DltAdapterImpl; +#else +#include "internal/DltLogAppender/DltAdapterDummy.h" +using DltAdapter = ramses::internal::DltAdapterDummy; #endif diff --git a/framework/DltLogAppender/src/DltAdapterDummy/DltAdapterDummy.cpp b/src/framework/internal/DltLogAppender/DltAdapterDummy.cpp similarity index 88% rename from framework/DltLogAppender/src/DltAdapterDummy/DltAdapterDummy.cpp rename to src/framework/internal/DltLogAppender/DltAdapterDummy.cpp index 451e1d7f6..14653c3fc 100644 --- a/framework/DltLogAppender/src/DltAdapterDummy/DltAdapterDummy.cpp +++ b/src/framework/internal/DltLogAppender/DltAdapterDummy.cpp @@ -6,4 +6,4 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "DltLogAppender/DltAdapterDummy/DltAdapterDummy.h" +#include "internal/DltLogAppender/DltAdapterDummy.h" diff --git a/framework/DltLogAppender/include/DltLogAppender/DltAdapterDummy/DltAdapterDummy.h b/src/framework/internal/DltLogAppender/DltAdapterDummy.h similarity index 71% rename from framework/DltLogAppender/include/DltLogAppender/DltAdapterDummy/DltAdapterDummy.h rename to src/framework/internal/DltLogAppender/DltAdapterDummy.h index f64cf60af..fb753f5e6 100644 --- a/framework/DltLogAppender/include/DltLogAppender/DltAdapterDummy/DltAdapterDummy.h +++ b/src/framework/internal/DltLogAppender/DltAdapterDummy.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DLTADAPTERDUMMY_H -#define RAMSES_DLTADAPTERDUMMY_H +#pragma once -#include "DltLogAppender/IDltAdapter.h" +#include "internal/DltLogAppender/IDltAdapter.h" -namespace ramses_internal +namespace ramses::internal { /** * Implementation for a dummy DltAdapter doing nothing @@ -34,7 +33,7 @@ namespace ramses_internal return true; } - bool logMessage(const LogMessage&) override + bool logMessage(const LogMessage& /*msg*/) override { return true; } @@ -48,12 +47,17 @@ namespace ramses_internal void uninitialize() override {} - bool registerInjectionCallback(LogContext*, uint32_t, int(*)(uint32_t, void*, uint32_t)) override + bool registerInjectionCallback(LogContext* /*ctx*/, uint32_t /*sid*/, int(* /*dltInjectionCallback*/)(uint32_t, void*, uint32_t)) override { return true; } - bool transmitFile(LogContext&, const std::string&, bool) override + bool transmitFile(LogContext& /*ctx*/, const std::string& /*uri*/, bool /*deleteFile*/) override + { + return true; + } + + bool transmit(LogContext& /*ctx*/, std::vector&& /*data*/, const std::string& /*filename*/) override { return true; } @@ -64,8 +68,7 @@ namespace ramses_internal } private: - DltAdapterDummy() {}; - ~DltAdapterDummy() override {}; + DltAdapterDummy() = default;; + ~DltAdapterDummy() override = default;; }; } -#endif // RAMSES_DLTADAPTERDUMMY_H diff --git a/framework/DltLogAppender/src/DltAdapterImpl/DltAdapterImpl.cpp b/src/framework/internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.cpp similarity index 62% rename from framework/DltLogAppender/src/DltAdapterImpl/DltAdapterImpl.cpp rename to src/framework/internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.cpp index be67698fa..cace0d445 100644 --- a/framework/DltLogAppender/src/DltAdapterImpl/DltAdapterImpl.cpp +++ b/src/framework/internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.cpp @@ -6,26 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "DltLogAppender/DltAdapterImpl/DltAdapterImpl.h" -#include "Utils/Warnings.h" - -#ifdef DLT_HAS_FILETRANSFER -extern "C" -{ -#include -} -#endif -#include "Utils/LogLevel.h" -#include "Utils/LogMessage.h" -#include "Utils/InplaceStringTokenizer.h" - +#include "internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/Core/Utils/LogLevel.h" +#include "internal/Core/Utils/LogMessage.h" +#include "internal/Core/Utils/InplaceStringTokenizer.h" +#include "internal/Core/Utils/File.h" #include #include -namespace ramses_internal +namespace ramses::internal { DltAdapterImpl::DltAdapterImpl() - : m_logLevelChangeCallback([](const std::string&, int) {}) + : m_logLevelChangeCallback([](const std::string& /*unused*/, int /*unused*/) {}) { } @@ -67,10 +60,10 @@ namespace ramses_internal return false; } - DltContext* dltContext = static_cast(msg.getContext().getUserData()); + auto* dltContext = static_cast(msg.getContext().getUserData()); if (dltContext == nullptr) { - fprintf(stderr, "DltAdapterImpl::logMessage: missing dlt context\n"); + fmt::print(stderr, "DltAdapterImpl::logMessage: missing dlt context\n"); return false; } @@ -150,19 +143,19 @@ namespace ramses_internal { if (m_initialized) { - fprintf(stderr, "DltAdapterImpl::initialize: already initialized\n"); + fmt::print(stderr, "DltAdapterImpl::initialize: already initialized\n"); return false; } - if (registerApplication && (appId.size() < 1 || appId.size() > 4)) + if (registerApplication && (appId.empty() || appId.size() > 4)) { - fprintf(stderr, "DltAdapterImpl::initialize: dlt app id must be set\n"); + fmt::print(stderr, "DltAdapterImpl::initialize: dlt app id must be set\n"); return false; } if (contexts.empty()) { - fprintf(stderr, "DltAdapterImpl::initialize: contexts may not be empty\n"); + fmt::print(stderr, "DltAdapterImpl::initialize: contexts may not be empty\n"); return false; } @@ -180,7 +173,7 @@ namespace ramses_internal for (auto ctx : contexts) { assert(!ctx->getUserData()); - DltContext* dltContext = new DltContext; + auto* dltContext = new DltContext; ctx->setUserData(dltContext); if (pushLogLevelsToDaemon) @@ -198,7 +191,7 @@ namespace ramses_internal if (dlt_register_log_level_changed_callback(dltContext, &DltAdapterImpl::DltLogLevelChangedCallback) < 0) { - fprintf(stderr, "DltAdapterImpl::initialize: set loglevel changed callback failure\n"); + fmt::print(stderr, "DltAdapterImpl::initialize: set loglevel changed callback failure\n"); } } @@ -216,7 +209,7 @@ namespace ramses_internal for (auto ctx : m_contexts) { - DltContext* dltContext = static_cast(ctx->getUserData()); + auto* dltContext = static_cast(ctx->getUserData()); ctx->setUserData(nullptr); DLT_UNREGISTER_CONTEXT((*dltContext)); @@ -229,7 +222,7 @@ namespace ramses_internal DLT_UNREGISTER_APP(); } - m_logLevelChangeCallback = [](const std::string&, int) {}; + m_logLevelChangeCallback = [](const std::string& /*unused*/, int /*unused*/) {}; m_appRegistered = false; m_initialized = false; } @@ -240,15 +233,15 @@ namespace ramses_internal { return false; } - DltContext* dltContext = static_cast(ctx->getUserData()); + auto* dltContext = static_cast(ctx->getUserData()); if (!dltContext) { - fprintf(stderr, "DltAdapterImpl::registerInjectionCallback: no dlt context\n"); + fmt::print(stderr, "DltAdapterImpl::registerInjectionCallback: no dlt context\n"); return false; } if (dlt_register_injection_callback(dltContext, sid, dltInjectionCallback) < 0) { - fprintf(stderr, "DltAdapterImpl::registerInjectionCallback: failed\n"); + fmt::print(stderr, "DltAdapterImpl::registerInjectionCallback: failed\n"); return false; } return true; @@ -260,7 +253,38 @@ namespace ramses_internal { return false; } - return m_fileTransfer.transmitFile(ctx, uri, deleteFile); + File f(uri); + size_t size = 0u; + if (!f.getSizeInBytes(size)) + { + return false; + } + if (!f.open(File::Mode::ReadOnlyBinary)) + { + return false; + } + std::vector data; + data.resize(size); + if (f.read(data.data(), data.size(), size) != EStatus::Ok) + { + return false; + } + f.close(); + if (deleteFile) + { + f.remove(); + } + + return m_fileTransfer.transmit(ctx, std::move(data), uri); + } + + bool DltAdapterImpl::transmit(LogContext& ctx, std::vector&& data, const std::string& filename) + { + if (!m_initialized) + { + return false; + } + return m_fileTransfer.transmit(ctx, std::move(data), filename); } bool DltAdapterImpl::isInitialized() @@ -279,44 +303,22 @@ namespace ramses_internal m_thread.join(); } - bool DltAdapterImpl::FileTransferWorker::transmitFile(LogContext& ctx, const std::string& uri, bool deleteFile) + bool DltAdapterImpl::FileTransferWorker::transmit(LogContext& ctx, std::vector&& data, const std::string& filename) { - if (uri.size() == 0 || ctx.getUserData() == nullptr) - { - return false; - } -#ifndef DLT_HAS_FILETRANSFER - UNUSED(deleteFile); - return false; -#else - DltContext* fileContext = static_cast(ctx.getUserData()); - - // send header to catch early errors - // if a file is modified during transfer it will be incomplete - // (dlt-filetransfer will assign a new serial) - if (dlt_user_log_file_header(fileContext, uri.c_str()) != 0) + if (filename.empty() || ctx.getUserData() == nullptr) { return false; } FileTransfer ft; - ft.filename = uri; - ft.ctx = fileContext; - ft.deleteFlag = deleteFile ? 1 : 0; + ft.filename = filename; + ft.ctx = static_cast(ctx.getUserData()); + ft.data = data; + ft.serial = ++m_fileTransferSerial; bool startThread = false; { PlatformGuard guard(m_lock); startThread = m_files.empty(); - if (!m_files.empty() && m_files.front().filename == uri) - { - // file is resent -> cancel running transfer - WARNINGS_PUSH - WARNING_DISABLE_LINUX(-Wold-style-cast) - DLT_LOG2((*fileContext), DLT_LOG_WARN, DLT_STRING("file is resent -> cancel running transfer:"), DLT_STRING(uri.c_str())); - WARNINGS_POP - m_thread.cancel(); - startThread = true; - } m_files.push_back(ft); } @@ -327,7 +329,6 @@ namespace ramses_internal m_thread.start(*this); } return true; -#endif } void DltAdapterImpl::FileTransferWorker::get(DltAdapterImpl::FileTransferWorker::FileTransfer& ft) @@ -352,44 +353,90 @@ namespace ramses_internal void DltAdapterImpl::FileTransferWorker::run() { -#ifdef DLT_HAS_FILETRANSFER - // Sending files per dlt is done by chunking the binary file data in very small chunks (~1024 bytes) and send these per regular DLT_LOG(..) - // after each chunk dlt_user_log_file_data() should sleep for the timeoutInMs so the FIFO of dlt will not be flooded with to many messages in a short period of time. - // The dlt implementation recommends a timeout of 20 ms, unfortunately due the very small chunk size a huge number of messages has to be sent and - // this will cause a transfer time of ~30 seconds for a file of 1.5 MB. + // Sending files per DLT is done by chunking the binary file data in very small chunks (~1024 bytes) and send these per regular DLT_LOG(..) + // After each chunk the file transfer should wait for some time, so the DLT-FIFO will not be flooded with too many messages. + // The DLT implementation recommends a timeout of 20 ms, but this would mean long transfer durations for typical scene dumps. // To prevent these long delays we simply use a smaller timeout and check for dlt buffer overflows (dlt_user_check_buffer()) - const int timeoutInMS = 1; +#ifdef DLT_HAS_FILETRANSFER + const auto uptime = std::chrono::seconds(dlt_uptime() / 10000); +#else + // no dlt_uptime() available - do not wait + const auto uptime = std::chrono::seconds(60); +#endif + const auto startupDelay = std::chrono::seconds(60); + if (uptime < startupDelay) + { + // do not transfer files at system startup (reduces risk of losing messages) + std::this_thread::sleep_for(startupDelay - uptime); + } FileTransfer ft; get(ft); do { - const auto filename = ft.filename.c_str(); - const auto total = dlt_user_log_file_packagesCount(ft.ctx, filename); - int i = 1; - for (; ((i <= total) && !isCancelRequested()); ++i) - { - auto canWriteToDLT = [](){ - int dltTotal = 0; - int dltUsed = 0; - // file transfer should not use more than 50% of the dlt buffer - dlt_user_check_buffer(&dltTotal, &dltUsed); - return ((dltTotal - dltUsed) > (dltTotal / 2)); - }; - while (!canWriteToDLT()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(timeoutInMS)); - } - if (dlt_user_log_file_data(ft.ctx, filename, i, timeoutInMS) != 0) - { - // usually this means the file was modified during transfer - break; - } - } - if (i > total) - { - dlt_user_log_file_end(ft.ctx, filename, ft.deleteFlag); - } + send(ft); } while(pop(ft) && !isCancelRequested()); + } + + void DltAdapterImpl::FileTransferWorker::send(const FileTransfer &ft) + { + const int timeoutInMS = 1; + const size_t bufferSize = 1024; + const auto filename = ft.filename.c_str(); + const auto packages = (ft.data.size() % bufferSize == 0) ? (ft.data.size() / bufferSize) : (ft.data.size() / bufferSize + 1); + +#ifdef DLT_HAS_FILETRANSFER + auto canWriteToDLT = [](){ + int dltTotal = 0; + int dltUsed = 0; + // file transfer should not use more than 50% of the dlt buffer + dlt_user_check_buffer(&dltTotal, &dltUsed); + const auto dltFree = dltTotal - dltUsed; + return (dltFree > (dltTotal / 2) && dltFree > static_cast(5 * bufferSize)); + }; +#else + auto canWriteToDLT = [](){ + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + return true; + }; #endif + + while (!canWriteToDLT()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + + DLT_LOG8(*ft.ctx, DLT_LOG_INFO, + DLT_STRING("FLST"), + DLT_UINT(ft.serial), + DLT_STRING(filename), + DLT_UINT(ft.data.size()), + DLT_STRING(""), + DLT_UINT(packages), + DLT_UINT(bufferSize), + DLT_STRING("FLST") + ); + + for (uint32_t i = 0; ((i < packages) && !isCancelRequested()); ++i) + { + while (!canWriteToDLT()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(timeoutInMS)); + } + const auto it = ft.data.begin() + static_cast(i * bufferSize); + const auto chunkSize = std::min(static_cast(ft.data.end() - it), bufferSize); + DLT_LOG5(*ft.ctx, DLT_LOG_INFO, + DLT_STRING("FLDA"), + DLT_UINT(ft.serial), + DLT_UINT(i + 1), + DLT_RAW(const_cast(&(*it)), static_cast(chunkSize)), + DLT_STRING("FLDA") + ); + } + + DLT_LOG3(*ft.ctx, DLT_LOG_INFO, + DLT_STRING("FLFI"), + DLT_UINT(ft.serial), + DLT_STRING("FLFI") + ); } } diff --git a/framework/DltLogAppender/include/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h b/src/framework/internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h similarity index 77% rename from framework/DltLogAppender/include/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h rename to src/framework/internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h index e85fce793..c820253e8 100644 --- a/framework/DltLogAppender/include/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h +++ b/src/framework/internal/DltLogAppender/DltAdapterImpl/DltAdapterImpl.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DLTADAPTERIMPL_H -#define RAMSES_DLTADAPTERIMPL_H +#pragma once -#include "DltLogAppender/IDltAdapter.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "PlatformAbstraction/PlatformLock.h" +#include "internal/DltLogAppender/IDltAdapter.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/PlatformLock.h" -#include "Utils/LogContext.h" -#include "Collections/Vector.h" -#include "Collections/Pair.h" -#include "Utils/Warnings.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "internal/Core/Utils/Warnings.h" #include @@ -29,7 +28,7 @@ WARNING_DISABLE_LINUX(-Wold-style-cast) WARNINGS_POP -namespace ramses_internal +namespace ramses::internal { /** * Implementation for DltAdapter @@ -51,6 +50,7 @@ namespace ramses_internal bool logMessage(const LogMessage& msg) override; bool registerInjectionCallback(LogContext* ctx, uint32_t sid, int (*dltInjectionCallback)(uint32_t service_id, void *data, uint32_t length)) override; bool transmitFile(LogContext& ctx, const std::string& uri, bool deleteFile) override; + bool transmit(LogContext& ctx, std::vector&& data, const std::string& filename) override; bool isInitialized() override; @@ -78,7 +78,7 @@ namespace ramses_internal FileTransferWorker(); ~FileTransferWorker() override; - bool transmitFile(LogContext& ctx, const std::string& uri, bool deleteFile); + bool transmit(LogContext& ctx, std::vector&& data, const std::string& filename); private: void run() override; @@ -86,19 +86,22 @@ namespace ramses_internal struct FileTransfer { std::string filename; + std::vector data; DltContext* ctx = nullptr; - int deleteFlag = 0; + uint32_t serial = 0; }; void get(FileTransfer& ft); bool pop(FileTransfer& ft); + void send(const FileTransfer& ft); + PlatformThread m_thread; PlatformLock m_lock; std::list m_files; + uint32_t m_fileTransferSerial = 0; }; FileTransferWorker m_fileTransfer; }; } -#endif // RAMSES_DLTADAPTERIMPL_H diff --git a/framework/DltLogAppender/src/DltLogAppender.cpp b/src/framework/internal/DltLogAppender/DltLogAppender.cpp similarity index 89% rename from framework/DltLogAppender/src/DltLogAppender.cpp rename to src/framework/internal/DltLogAppender/DltLogAppender.cpp index 6c61b53bd..78eb496ed 100644 --- a/framework/DltLogAppender/src/DltLogAppender.cpp +++ b/src/framework/internal/DltLogAppender/DltLogAppender.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "DltLogAppender/DltLogAppender.h" +#include "internal/DltLogAppender/DltLogAppender.h" -namespace ramses_internal +namespace ramses::internal { DltLogAppender::DltLogAppender() : m_dltAdapter(DltAdapter::getDltAdapter()) diff --git a/framework/DltLogAppender/include/DltLogAppender/DltLogAppender.h b/src/framework/internal/DltLogAppender/DltLogAppender.h similarity index 80% rename from framework/DltLogAppender/include/DltLogAppender/DltLogAppender.h rename to src/framework/internal/DltLogAppender/DltLogAppender.h index b499ca08c..23af2e092 100644 --- a/framework/DltLogAppender/include/DltLogAppender/DltLogAppender.h +++ b/src/framework/internal/DltLogAppender/DltLogAppender.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DLTLOGAPPENDER_H -#define RAMSES_DLTLOGAPPENDER_H +#pragma once -#include "Utils/LogAppenderBase.h" -#include "DltLogAppender/DltAdapter.h" +#include "internal/Core/Utils/LogAppenderBase.h" +#include "internal/DltLogAppender/DltAdapter.h" -namespace ramses_internal +namespace ramses::internal { /** * Logs messages via dlt @@ -27,5 +26,3 @@ namespace ramses_internal DltAdapter* m_dltAdapter; }; } - -#endif diff --git a/framework/DltLogAppender/include/DltLogAppender/IDltAdapter.h b/src/framework/internal/DltLogAppender/IDltAdapter.h similarity index 86% rename from framework/DltLogAppender/include/DltLogAppender/IDltAdapter.h rename to src/framework/internal/DltLogAppender/IDltAdapter.h index 1c2f30684..637a7a575 100644 --- a/framework/DltLogAppender/include/DltLogAppender/IDltAdapter.h +++ b/src/framework/internal/DltLogAppender/IDltAdapter.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IDLTADAPTER_H -#define RAMSES_IDLTADAPTER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LogLevel.h" -#include "Utils/LogMessage.h" -#include "Utils/LogContext.h" +#include "internal/Core/Utils/LogLevel.h" +#include "internal/Core/Utils/LogMessage.h" +#include "internal/Core/Utils/LogContext.h" +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { /** * Interface for DltAdapter @@ -30,7 +29,7 @@ namespace ramses_internal /** * Destructor */ - virtual ~IDltAdapter() {}; + virtual ~IDltAdapter() = default;; /** * Send log message to dlt @@ -59,6 +58,8 @@ namespace ramses_internal */ virtual bool transmitFile(LogContext& ctx, const std::string& uri, bool deleteFile) = 0; + virtual bool transmit(LogContext& ctx, std::vector&& data, const std::string& filename) = 0; + /** * Return the state if dlt was found at runtime * @return if dlt was successfully initialized @@ -66,4 +67,3 @@ namespace ramses_internal virtual bool isInitialized() = 0; }; } -#endif // RAMSES_DLTADAPTERIMPL_H diff --git a/framework/PlatformAbstraction/include/Collections/BlockingQueue.h b/src/framework/internal/PlatformAbstraction/Collections/BlockingQueue.h similarity index 94% rename from framework/PlatformAbstraction/include/Collections/BlockingQueue.h rename to src/framework/internal/PlatformAbstraction/Collections/BlockingQueue.h index 602be20c6..3849c2aaf 100644 --- a/framework/PlatformAbstraction/include/Collections/BlockingQueue.h +++ b/src/framework/internal/PlatformAbstraction/Collections/BlockingQueue.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTAINER_BLOCKINGQUEUE_H -#define RAMSES_CONTAINER_BLOCKINGQUEUE_H +#pragma once #include #include @@ -15,7 +14,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { template class BlockingQueue final @@ -45,7 +44,9 @@ namespace ramses_internal assert(pElement); std::unique_lock l(m_mutex); if (timeout == std::chrono::milliseconds{0}) + { m_condvar.wait(l, [&](){ return !m_queue.empty(); }); + } else { if (!m_condvar.wait_for(l, timeout, [&](){ return !m_queue.empty(); })) @@ -63,5 +64,3 @@ namespace ramses_internal return m_queue.empty(); } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/Guid.h b/src/framework/internal/PlatformAbstraction/Collections/Guid.h similarity index 81% rename from framework/PlatformAbstraction/include/Collections/Guid.h rename to src/framework/internal/PlatformAbstraction/Collections/Guid.h index 53d2784e9..708d2a231 100644 --- a/framework/PlatformAbstraction/include/Collections/Guid.h +++ b/src/framework/internal/PlatformAbstraction/Collections/Guid.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GUID_H -#define RAMSES_GUID_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/Macros.h" -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" #include "fmt/format.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { class Guid final { @@ -105,7 +104,7 @@ namespace ramses_internal } template <> -struct fmt::formatter +struct fmt::formatter { template constexpr auto parse(ParseContext& ctx) @@ -114,32 +113,27 @@ struct fmt::formatter } template - constexpr auto format(const ramses_internal::Guid& guid, FormatContext& ctx) + constexpr auto format(const ramses::internal::Guid& guid, FormatContext& ctx) { const uint64_t v = guid.get(); if (v < 256) { return fmt::format_to(ctx.out(), "00{:02X}", v); } - else - { - const uint64_t upper = (v >> 48u) & 0xFFFFu; - const uint64_t lower = v & 0xFFFFFFFFFFFFu; - return fmt::format_to(ctx.out(), "{:04X}-{:012X}", upper, lower); - } + const uint64_t upper = (v >> 48u) & 0xFFFFu; + const uint64_t lower = v & 0xFFFFFFFFFFFFu; + return fmt::format_to(ctx.out(), "{:04X}-{:012X}", upper, lower); } }; namespace std { template<> - struct hash + struct hash { - size_t operator()(const ramses_internal::Guid& key) const + size_t operator()(const ramses::internal::Guid& key) const { return std::hash()(key.get()); } }; } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/HashMap.h b/src/framework/internal/PlatformAbstraction/Collections/HashMap.h similarity index 94% rename from framework/PlatformAbstraction/include/Collections/HashMap.h rename to src/framework/internal/PlatformAbstraction/Collections/HashMap.h index 12165fee8..47470ea9c 100644 --- a/framework/PlatformAbstraction/include/Collections/HashMap.h +++ b/src/framework/internal/PlatformAbstraction/Collections/HashMap.h @@ -6,21 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COLLECTIONS_HASHMAP_H -#define RAMSES_COLLECTIONS_HASHMAP_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformError.h" -#include "PlatformAbstraction/Hash.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "PlatformAbstraction/Macros.h" -#include "Utils/AssertMovable.h" +#pragma once + +#include "internal/PlatformAbstraction/PlatformError.h" +#include "internal/PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/Core/Utils/AssertMovable.h" + +#include #include #include #include #include -namespace ramses_internal +namespace ramses::internal { /** * Table object container where keys are found and retrieved via hashs. @@ -38,9 +38,9 @@ namespace ramses_internal class Pair final { public: - Pair(const Key& key_, const T& value_) - : key(key_) - , value(value_) + Pair(Key key_, T value_) + : key(std::move(key_)) + , value(std::move(value_)) { } @@ -56,9 +56,6 @@ namespace ramses_internal { public: HashMapEntry() - : next(nullptr) - , previous(nullptr) - , isChainElement(false) { // 'preconnect' free entries next = this + 1; @@ -93,11 +90,11 @@ namespace ramses_internal HashMapEntry& operator=(const HashMapEntry& other) = delete; private: - HashMapEntry* next; // pointer to the next entry (chaining) - HashMapEntry* previous; // pointer to the previous entry (chaining) + HashMapEntry* next{nullptr}; // pointer to the next entry (chaining) + HashMapEntry* previous{nullptr}; // pointer to the previous entry (chaining) // NOLINTNEXTLINE(modernize-avoid-c-arrays) - alignas(Pair) char keyValuePairMemory[sizeof(Pair)]; // properly aligned memory for key and value - bool isChainElement; // true if the element is not the first element in a chain + alignas(Pair) char keyValuePairMemory[sizeof(Pair)]{0}; // properly aligned memory for key and value + bool isChainElement{false}; // true if the element is not the first element in a chain friend class HashMap; }; @@ -108,7 +105,6 @@ namespace ramses_internal public: friend class HashMap; - friend class Iterator; /** * Constructor. @@ -181,6 +177,7 @@ namespace ramses_internal * Step the iterator forward to the next element (postfix operator) * @return the next iterator */ + // NOLINTNEXTLINE(readability-const-return-type): required by cert-dcl21-cpp const ConstIterator operator++(int32_t) { ConstIterator oldValue(*this); @@ -290,6 +287,7 @@ namespace ramses_internal * Step the iterator forward to the next element (postfix operator) * @return the next iterator */ + // NOLINTNEXTLINE(readability-const-return-type): required by cert-dcl21-cpp const Iterator operator++(int32_t) { Iterator oldValue(*this); @@ -453,14 +451,14 @@ namespace ramses_internal private: static uint8_t BitSizeFromElementCount(size_t count); - uint8_t mBitCount; // bit size for the hash function - size_t mSize; // the size of the data list - size_t mThreshold; // defines when a rehashing may occur - HashMapEntry** mBuckets; // bucket list - HashMapEntry* mData; // placeholder for the data - HashMapEntry* mLastHashMapEntry; // pointer to the last entry in the map - HashMapEntry* mFirstFreeHashMapEntry; // start of pointer list of free entries - size_t mCount; // the current entry count + uint8_t mBitCount{0}; // bit size for the hash function + size_t mSize{0}; // the size of the data list + size_t mThreshold{0}; // defines when a rehashing may occur + HashMapEntry** mBuckets{nullptr}; // bucket list + HashMapEntry* mData{nullptr}; // placeholder for the data + HashMapEntry* mLastHashMapEntry{nullptr}; // pointer to the last entry in the map + HashMapEntry* mFirstFreeHashMapEntry{nullptr}; // start of pointer list of free entries + size_t mCount{0u}; // the current entry count void initializeLastEntry(); void destructAll(); @@ -497,7 +495,6 @@ namespace ramses_internal , mData(new HashMapEntry[mThreshold + 1]) // One dummy for the end , mLastHashMapEntry(mData + mThreshold) , mFirstFreeHashMapEntry(mData) - , mCount(0) { PlatformMemory::Set(mBuckets, 0, sizeof(HashMapEntry*) * mSize); //NOLINT(bugprone-sizeof-expression) sizeof is really a pointer type here initializeLastEntry(); @@ -512,7 +509,6 @@ namespace ramses_internal , mData(new HashMapEntry[mThreshold + 1]) //One dummy for the end , mLastHashMapEntry(mData + mThreshold) , mFirstFreeHashMapEntry(mData) - , mCount(0) // will get increased by internalPut { PlatformMemory::Set(mBuckets, 0, sizeof(HashMapEntry*) * mSize); //NOLINT(bugprone-sizeof-expression) sizeof is really a pointer type here initializeLastEntry(); @@ -550,7 +546,6 @@ namespace ramses_internal , mData(new HashMapEntry[mThreshold + 1]) //One dummy for the end , mLastHashMapEntry(mData + mThreshold) , mFirstFreeHashMapEntry(mData) - , mCount(0) { PlatformMemory::Set(mBuckets, 0, sizeof(HashMapEntry*) * mSize); //NOLINT(bugprone-sizeof-expression) sizeof is really a pointer type here initializeLastEntry(); @@ -965,9 +960,7 @@ namespace ramses_internal // never go below default capacity (also count==0 would fail in calculation) if (count < DefaultHashMapCapacity) count = DefaultHashMapCapacity; - const size_t countRespectingLoadFactor = static_cast(std::ceil(count / DefaultHashMapMaxLoadFactor)); + const auto countRespectingLoadFactor = static_cast(std::ceil(static_cast(count) / DefaultHashMapMaxLoadFactor)); return static_cast(std::ceil(std::log2(countRespectingLoadFactor))); } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/HashSet.h b/src/framework/internal/PlatformAbstraction/Collections/HashSet.h similarity index 96% rename from framework/PlatformAbstraction/include/Collections/HashSet.h rename to src/framework/internal/PlatformAbstraction/Collections/HashSet.h index 10a5fb77f..1ede387f0 100644 --- a/framework/PlatformAbstraction/include/Collections/HashSet.h +++ b/src/framework/internal/PlatformAbstraction/Collections/HashSet.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COLLECTIONS_HASHSET_H -#define RAMSES_COLLECTIONS_HASHSET_H +#pragma once -#include "PlatformAbstraction/Hash.h" -#include "PlatformAbstraction/Macros.h" -#include "Collections/HashMap.h" -#include "Utils/AssertMovable.h" +#include "internal/PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +namespace ramses::internal { /** * Unordered set of objects with fast lookup and retrieval. @@ -101,6 +100,7 @@ namespace ramses_internal * Step the iterator forward to the next element (postfix operator) * @return the next iterator */ + // NOLINTNEXTLINE(readability-const-return-type): required by cert-dcl21-cpp const HashSetIterator operator++(int32_t) { HashSetIterator oldValue(*this); @@ -302,9 +302,7 @@ namespace ramses_internal } template - HashSet::~HashSet() - { - } + HashSet::~HashSet() = default; template HashSet::HashSet(const size_t initialCapacity) @@ -442,5 +440,3 @@ namespace ramses_internal return *this; } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/HeapArray.h b/src/framework/internal/PlatformAbstraction/Collections/HeapArray.h similarity index 87% rename from framework/PlatformAbstraction/include/Collections/HeapArray.h rename to src/framework/internal/PlatformAbstraction/Collections/HeapArray.h index 4ca9c7f57..4eb51bdaf 100644 --- a/framework/PlatformAbstraction/include/Collections/HeapArray.h +++ b/src/framework/internal/PlatformAbstraction/Collections/HeapArray.h @@ -6,22 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTAINER_HEAPARRAY_H -#define RAMSES_CONTAINER_HEAPARRAY_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "PlatformAbstraction/Macros.h" -#include "Utils/AssertMovable.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/Core/Utils/AssertMovable.h" #include "absl/types/span.h" + +#include #include +#include -namespace ramses_internal +namespace ramses::internal { template class HeapArray final { - static_assert(std::is_integral::value, "only integral types allowed"); + static_assert(std::is_same_v || std::is_integral_v, "only integral types or std::byte allowed"); public: explicit HeapArray(size_t size = 0, const T* data = nullptr); @@ -30,8 +31,8 @@ namespace ramses_internal HeapArray(const HeapArray&) = delete; HeapArray& operator=(const HeapArray&) = delete; - HeapArray(HeapArray&&) noexcept; - HeapArray& operator=(HeapArray&&) noexcept; + HeapArray(HeapArray&& o) noexcept; + HeapArray& operator=(HeapArray&& o) noexcept; RNODISCARD size_t size() const; RNODISCARD T* data(); @@ -129,5 +130,3 @@ namespace ramses_internal } } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/IInputStream.h b/src/framework/internal/PlatformAbstraction/Collections/IInputStream.h similarity index 87% rename from framework/PlatformAbstraction/include/Collections/IInputStream.h rename to src/framework/internal/PlatformAbstraction/Collections/IInputStream.h index acf8d4136..6b8a191d7 100644 --- a/framework/PlatformAbstraction/include/Collections/IInputStream.h +++ b/src/framework/internal/PlatformAbstraction/Collections/IInputStream.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IINPUTSTREAM_H -#define RAMSES_IINPUTSTREAM_H +#pragma once -#include "PlatformAbstraction/PlatformError.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/PlatformAbstraction/PlatformError.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { class IInputStream { @@ -29,7 +29,7 @@ namespace ramses_internal virtual IInputStream& read(void* data, size_t size) = 0; [[nodiscard]] virtual EStatus getState() const = 0; - virtual EStatus seek(Int numberOfBytesToSeek, Seek origin) = 0; + virtual EStatus seek(int64_t numberOfBytesToSeek, Seek origin) = 0; virtual EStatus getPos(size_t& position) const = 0; }; @@ -62,5 +62,3 @@ namespace ramses_internal return stream; } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/IOutputStream.h b/src/framework/internal/PlatformAbstraction/Collections/IOutputStream.h similarity index 92% rename from framework/PlatformAbstraction/include/Collections/IOutputStream.h rename to src/framework/internal/PlatformAbstraction/Collections/IOutputStream.h index dd64ee1f3..0b3d5ca2d 100644 --- a/framework/PlatformAbstraction/include/Collections/IOutputStream.h +++ b/src/framework/internal/PlatformAbstraction/Collections/IOutputStream.h @@ -6,21 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UTILS_IOUTPUTSTREAM_H -#define RAMSES_UTILS_IOUTPUTSTREAM_H +#pragma once #include #include #include #include +#include "internal/PlatformAbstraction/PlatformError.h" -namespace ramses_internal +namespace ramses::internal { class IOutputStream { public: virtual ~IOutputStream() = default; virtual IOutputStream& write(const void* data, size_t size) = 0; + virtual EStatus getPos(size_t& position) const = 0; IOutputStream& operator<<(const void*) = delete; }; @@ -88,10 +89,8 @@ namespace ramses_internal inline IOutputStream& operator<<(IOutputStream& stream, std::string_view value) { - const uint32_t len = static_cast(value.size()); + const auto len = static_cast(value.size()); stream << len; return stream.write(value.data(), len); } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/Pair.h b/src/framework/internal/PlatformAbstraction/Collections/Pair.h similarity index 80% rename from framework/PlatformAbstraction/include/Collections/Pair.h rename to src/framework/internal/PlatformAbstraction/Collections/Pair.h index e0d8790db..523ed089c 100644 --- a/framework/PlatformAbstraction/include/Collections/Pair.h +++ b/src/framework/internal/PlatformAbstraction/Collections/Pair.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PAIR_H -#define RAMSES_PAIR_H +#pragma once -#include "PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Hash.h" #include template @@ -17,8 +16,6 @@ struct std::hash> { size_t operator()(const std::pair& data) { - return ramses_internal::HashValue(data.first, data.second); + return ramses::internal::HashValue(data.first, data.second); } }; - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/StringOutputStream.h b/src/framework/internal/PlatformAbstraction/Collections/StringOutputStream.h similarity index 78% rename from framework/PlatformAbstraction/include/Collections/StringOutputStream.h rename to src/framework/internal/PlatformAbstraction/Collections/StringOutputStream.h index 468646b64..a78739b6a 100644 --- a/framework/PlatformAbstraction/include/Collections/StringOutputStream.h +++ b/src/framework/internal/PlatformAbstraction/Collections/StringOutputStream.h @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSES_STRINGOUTPUTSTREAM_H -#define RAMSES_RAMSES_STRINGOUTPUTSTREAM_H +#pragma once -#include "PlatformAbstraction/Macros.h" +#include "internal/PlatformAbstraction/Macros.h" #include "fmt/format.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { class StringOutputStream final { @@ -27,7 +27,8 @@ namespace ramses_internal template ()))> StringOutputStream& operator<<(const T& value) { - m_buffer.reserve(m_buffer.size() + fmt::formatted_size("{}", value)); + const auto size = std::min(fmt::formatted_size("{}", value), SanityMaxReserveSize); + m_buffer.reserve(m_buffer.size() + size); fmt::format_to(std::back_inserter(m_buffer), "{}", value); return *this; } @@ -35,7 +36,8 @@ namespace ramses_internal template void formatTo(const char* fmtSpec, const Args& ... values) { - m_buffer.reserve(m_buffer.size() + fmt::formatted_size(fmtSpec, values...)); + const auto size = std::min(fmt::formatted_size(fmtSpec, values...), SanityMaxReserveSize); + m_buffer.reserve(m_buffer.size() + size); fmt::format_to(std::back_inserter(m_buffer), fmtSpec, values...); } @@ -49,7 +51,11 @@ namespace ramses_internal RNODISCARD const std::string& data() const; private: - std::string m_buffer; + std::string m_buffer{}; + + // fmt (at least some versions on some platforms) seems to have a bug when determining formatted_size of certain longer string_views + // this is only affecting reserved size when streaming, NOT the actual data streamed + static constexpr size_t SanityMaxReserveSize = 8192u; }; inline @@ -108,5 +114,3 @@ namespace ramses_internal return tmp; } } - -#endif diff --git a/framework/PlatformAbstraction/include/Collections/Vector.h b/src/framework/internal/PlatformAbstraction/Collections/Vector.h similarity index 92% rename from framework/PlatformAbstraction/include/Collections/Vector.h rename to src/framework/internal/PlatformAbstraction/Collections/Vector.h index e60e426b0..40011019d 100644 --- a/framework/PlatformAbstraction/include/Collections/Vector.h +++ b/src/framework/internal/PlatformAbstraction/Collections/Vector.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTAINER_VECTOR_H -#define RAMSES_CONTAINER_VECTOR_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { template inline @@ -35,5 +34,3 @@ namespace ramses_internal return std::find(vec.begin(), vec.end(), value) != vec.end(); } } - -#endif diff --git a/framework/PlatformAbstraction/src/ConsoleInput.cpp b/src/framework/internal/PlatformAbstraction/ConsoleInput.cpp similarity index 93% rename from framework/PlatformAbstraction/src/ConsoleInput.cpp rename to src/framework/internal/PlatformAbstraction/ConsoleInput.cpp index 7b5978100..bf15c2ca1 100644 --- a/framework/PlatformAbstraction/src/ConsoleInput.cpp +++ b/src/framework/internal/PlatformAbstraction/ConsoleInput.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/ConsoleInput.h" +#include "internal/PlatformAbstraction/ConsoleInput.h" #include #include #ifdef _MSC_VER -#include "PlatformAbstraction/MinimalWindowsH.h" +#include "internal/PlatformAbstraction/MinimalWindowsH.h" #include #include @@ -166,7 +166,7 @@ namespace return false; // create interrupt pipes - std::array tmpPipes; + std::array tmpPipes{}; if (::pipe(tmpPipes.data()) != 0) return false; m_readPipe = tmpPipes[0]; @@ -177,9 +177,14 @@ namespace if (::tcgetattr(stdinFd, &m_oldTerminalSettings) != 0) { if (errno == ENOTTY) + { m_ttyConnected = false; - else // filedescriptor closed or broken + } + else + { + // filedescriptor closed or broken return false; + } } return true; } @@ -195,9 +200,9 @@ namespace tcsetattr(stdinFd, TCSANOW, &m_oldTerminalSettings); } } - if (m_readPipe) + if (m_readPipe != 0) ::close(m_readPipe); - if (m_WritePipe) + if (m_WritePipe != 0) ::close(m_WritePipe); } @@ -210,7 +215,7 @@ namespace if (m_ttyConnected) { // disable echo - struct termios temporaryWithoutEcho; + struct termios temporaryWithoutEcho{}; std::memcpy(&temporaryWithoutEcho, &m_oldTerminalSettings, sizeof(struct termios)); // NOLINTNEXTLINE(hicpp-signed-bitwise) temporaryWithoutEcho.c_lflag &= ~(ECHO | ICANON); @@ -250,12 +255,9 @@ namespace } break; } - else - { - // ignore interrupted by signal, otherwise fail - if (errno != EINTR) - break; - } + // ignore interrupted by signal, otherwise fail + if (errno != EINTR) + break; } if (m_ttyConnected) @@ -278,13 +280,13 @@ namespace private: int m_readPipe = 0; int m_WritePipe = 0; - struct termios m_oldTerminalSettings; + struct termios m_oldTerminalSettings{}; bool m_ttyConnected = false; }; } #endif - namespace ramses_internal + namespace ramses::internal { namespace { @@ -292,11 +294,13 @@ namespace std::unique_ptr impl; } + // NOLINTNEXTLINE(readability-convert-member-functions-to-static): design decision bool ConsoleInput::readChar(char& c) { return impl->readChar(c); } + // NOLINTNEXTLINE(readability-convert-member-functions-to-static): design decision void ConsoleInput::interruptReadChar() { impl->interruptReadChar(); diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/ConsoleInput.h b/src/framework/internal/PlatformAbstraction/ConsoleInput.h similarity index 84% rename from framework/PlatformAbstraction/include/PlatformAbstraction/ConsoleInput.h rename to src/framework/internal/PlatformAbstraction/ConsoleInput.h index 92b02e6b5..bb8895e20 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/ConsoleInput.h +++ b/src/framework/internal/PlatformAbstraction/ConsoleInput.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_CONSOLEINPUT_H -#define RAMSES_PLATFORMABSTRACTION_CONSOLEINPUT_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class ConsoleInput final { @@ -25,5 +24,3 @@ namespace ramses_internal ConsoleInput(); }; } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/FmtBase.h b/src/framework/internal/PlatformAbstraction/FmtBase.h similarity index 74% rename from framework/PlatformAbstraction/include/PlatformAbstraction/FmtBase.h rename to src/framework/internal/PlatformAbstraction/FmtBase.h index a8498a035..5c28e6aad 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/FmtBase.h +++ b/src/framework/internal/PlatformAbstraction/FmtBase.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_FMTBASE_H -#define RAMSES_PLATFORMABSTRACTION_FMTBASE_H +#pragma once #include "fmt/format.h" -namespace ramses_internal +namespace ramses::internal { struct SimpleFormatterBase { @@ -23,4 +22,10 @@ namespace ramses_internal }; } -#endif +namespace ramses { + template ::value, void>::type> + auto format_as(T f) + { + return fmt::underlying(f); + } +} diff --git a/framework/PlatformAbstraction/src/Guid.cpp b/src/framework/internal/PlatformAbstraction/Guid.cpp similarity index 86% rename from framework/PlatformAbstraction/src/Guid.cpp rename to src/framework/internal/PlatformAbstraction/Guid.cpp index 5eac91c02..1cab40c77 100644 --- a/framework/PlatformAbstraction/src/Guid.cpp +++ b/src/framework/internal/PlatformAbstraction/Guid.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Collections/Guid.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" #include -namespace ramses_internal +namespace ramses::internal { uint64_t Guid::GetFromString(const char* guid, size_t len) { @@ -22,6 +22,7 @@ namespace ramses_internal uint64_t data4 = 0; uint64_t data5 = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cert-err34-c) const int retVal = sscanf(guid, "%08x-%04x-%04x-%04" SCNx64 "-%012" SCNx64, &data1, &data2, @@ -37,6 +38,7 @@ namespace ramses_internal uint64_t data4 = 0; uint64_t data5 = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cert-err34-c) const int retVal = sscanf(guid, "%04" SCNx64 "-%012" SCNx64, &data4, &data5); if (retVal == 2 && data4 <= 0xFFFF && data5 <= 0xFFFFFFFFFFFF) return (data4 << 48) + data5; @@ -45,6 +47,7 @@ namespace ramses_internal { // shortened format parsing (to always allow e.g. Guid(fmt::to_string(Guid(1)))) uint64_t data5 = 0; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, cert-err34-c) const int retVal = sscanf(guid, "%04" SCNx64, &data5); if (retVal == 1 && data5 <= 0xff) return data5; diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/Hash.h b/src/framework/internal/PlatformAbstraction/Hash.h similarity index 85% rename from framework/PlatformAbstraction/include/PlatformAbstraction/Hash.h rename to src/framework/internal/PlatformAbstraction/Hash.h index 44a131e53..43407c0a5 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/Hash.h +++ b/src/framework/internal/PlatformAbstraction/Hash.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_HASH_H -#define RAMSES_PLATFORMABSTRACTION_HASH_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include #include +#include -namespace ramses_internal +namespace ramses::internal { // HashCombine and HashVal(ue) (modeled according to standard proposal n3876) template @@ -50,10 +50,10 @@ namespace ramses_internal constexpr const uint32_t prime = 16777619UL; constexpr const uint32_t offset_base = 2166136261UL; - const Byte* ptr = static_cast(key); + const auto* ptr = static_cast(key); uint32_t result = offset_base; for (std::size_t i = 0; i < len; ++i) - result = (result ^ ptr[i]) * prime; + result = (result ^ std::to_integer(ptr[i])) * prime; return result; } }; @@ -67,10 +67,10 @@ namespace ramses_internal constexpr const uint64_t prime = 1099511628211ULL; constexpr const uint64_t offset_base = 14695981039346656037ULL; - const Byte* ptr = static_cast(key); + const auto* ptr = static_cast(key); uint64_t result = offset_base; for (std::size_t i = 0; i < len; ++i) - result = (result ^ ptr[i]) * prime; + result = (result ^ std::to_integer(ptr[i])) * prime; return result; } }; @@ -82,5 +82,3 @@ namespace ramses_internal return internal::FnvHash()(ptr, size); } } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/Macros.h b/src/framework/internal/PlatformAbstraction/Macros.h similarity index 90% rename from framework/PlatformAbstraction/include/PlatformAbstraction/Macros.h rename to src/framework/internal/PlatformAbstraction/Macros.h index 3c395daf3..b3ddbc82f 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/Macros.h +++ b/src/framework/internal/PlatformAbstraction/Macros.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_MACROS_H -#define RAMSES_PLATFORMABSTRACTION_MACROS_H +#pragma once #if defined(__clang__) # define RFALLTHROUGH [[clang::fallthrough]] @@ -28,5 +27,3 @@ #else # define RFORMATCHECK(fmtstr_idx, vararg_idx) #endif - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/MinimalWindowsH.h b/src/framework/internal/PlatformAbstraction/MinimalWindowsH.h similarity index 94% rename from framework/PlatformAbstraction/include/PlatformAbstraction/MinimalWindowsH.h rename to src/framework/internal/PlatformAbstraction/MinimalWindowsH.h index f90316490..245c3fbe7 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/MinimalWindowsH.h +++ b/src/framework/internal/PlatformAbstraction/MinimalWindowsH.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_MINIMALWINDOWSH_H -#define RAMSES_PLATFORMABSTRACTION_MINIMALWINDOWSH_H +#pragma once #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -125,5 +124,3 @@ #endif #undef min #undef max - -#endif diff --git a/framework/PlatformAbstraction/src/PlatformConsole.cpp b/src/framework/internal/PlatformAbstraction/PlatformConsole.cpp similarity index 80% rename from framework/PlatformAbstraction/src/PlatformConsole.cpp rename to src/framework/internal/PlatformAbstraction/PlatformConsole.cpp index e3d54b6c7..296b02bda 100644 --- a/framework/PlatformAbstraction/src/PlatformConsole.cpp +++ b/src/framework/internal/PlatformAbstraction/PlatformConsole.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformConsole.h" +#include "internal/PlatformAbstraction/PlatformConsole.h" #ifdef _WIN32 #include @@ -15,7 +15,7 @@ #endif #endif -namespace ramses_internal +namespace ramses::internal { #ifdef _WIN32 void Console::Initialize() @@ -27,14 +27,14 @@ namespace ramses_internal DWORD dwMode = 0; if (!GetConsoleMode(hOut, &dwMode)) { - printf("GetConsoleMode failed: color output might be broken\n"); + fmt::print("GetConsoleMode failed: color output might be broken\n"); return; } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; if (!SetConsoleMode(hOut, dwMode)) { - printf("SetConsoleMode failed: color output might be broken\n"); + fmt::print("SetConsoleMode failed: color output might be broken\n"); } } #endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformConsole.h b/src/framework/internal/PlatformAbstraction/PlatformConsole.h similarity index 96% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformConsole.h rename to src/framework/internal/PlatformAbstraction/PlatformConsole.h index 7662c5b58..872f5e078 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformConsole.h +++ b/src/framework/internal/PlatformAbstraction/PlatformConsole.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMCONSOLE_H -#define RAMSES_PLATFORMCONSOLE_H +#pragma once #include "fmt/format.h" -namespace ramses_internal +namespace ramses::internal { enum class ConsoleColor { @@ -120,5 +119,3 @@ namespace ramses_internal #endif } } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformEnvironmentVariables.h b/src/framework/internal/PlatformAbstraction/PlatformEnvironmentVariables.h similarity index 90% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformEnvironmentVariables.h rename to src/framework/internal/PlatformAbstraction/PlatformEnvironmentVariables.h index 324a5599a..fcde1bb51 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformEnvironmentVariables.h +++ b/src/framework/internal/PlatformAbstraction/PlatformEnvironmentVariables.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMENVIRONMENTVARIABLES_H -#define RAMSES_PLATFORMENVIRONMENTVARIABLES_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { namespace PlatformEnvironmentVariables { @@ -30,7 +29,7 @@ namespace ramses_internal free(envValue); return found; #else - char * env = getenv(key.c_str()); + auto * env = getenv(key.c_str()); if (nullptr != env) { value = env; @@ -65,5 +64,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformError.h b/src/framework/internal/PlatformAbstraction/PlatformError.h similarity index 73% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformError.h rename to src/framework/internal/PlatformAbstraction/PlatformError.h index c1fca454f..84084462d 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformError.h +++ b/src/framework/internal/PlatformAbstraction/PlatformError.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMERROR_H -#define RAMSES_PLATFORMERROR_H +#pragma once -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { enum class EStatus { @@ -29,6 +28,4 @@ namespace ramses_internal }; } -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::EStatus, "EStatus", ramses_internal::EStatusNames, ramses_internal::EStatus::Eof); - -#endif +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EStatus, "EStatus", ramses::internal::EStatusNames, ramses::internal::EStatus::Eof); diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformEvent.h b/src/framework/internal/PlatformAbstraction/PlatformEvent.h similarity index 91% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformEvent.h rename to src/framework/internal/PlatformAbstraction/PlatformEvent.h index 741e5f855..e6ce53184 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformEvent.h +++ b/src/framework/internal/PlatformAbstraction/PlatformEvent.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMEVENT_H -#define RAMSES_PLATFORMEVENT_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class PlatformEvent final { @@ -43,12 +42,14 @@ namespace ramses_internal std::unique_lock l(m_mutex); bool result = true; if (millisec == 0) + { m_condVar.wait(l, [&](){ return m_triggered; }); + } else - result = m_condVar.wait_for(l, std::chrono::milliseconds{millisec}, [&](){ return m_triggered; }); + { + result = m_condVar.wait_for(l, std::chrono::milliseconds{millisec}, [&]() { return m_triggered; }); + } m_triggered = false; return result; } } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformLock.h b/src/framework/internal/PlatformAbstraction/PlatformLock.h similarity index 86% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformLock.h rename to src/framework/internal/PlatformAbstraction/PlatformLock.h index 133d990f9..d0fa77098 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformLock.h +++ b/src/framework/internal/PlatformAbstraction/PlatformLock.h @@ -6,15 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMLOCK_H -#define RAMSES_PLATFORMLOCK_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { using PlatformLock = std::recursive_mutex; using PlatformGuard = std::lock_guard; } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformMath.h b/src/framework/internal/PlatformAbstraction/PlatformMath.h similarity index 92% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformMath.h rename to src/framework/internal/PlatformAbstraction/PlatformMath.h index 3d754b2e7..d0034dba1 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformMath.h +++ b/src/framework/internal/PlatformAbstraction/PlatformMath.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMMATH_H -#define RAMSES_PLATFORMMATH_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { namespace PlatformMath { @@ -39,5 +38,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformMemory.h b/src/framework/internal/PlatformAbstraction/PlatformMemory.h similarity index 88% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformMemory.h rename to src/framework/internal/PlatformAbstraction/PlatformMemory.h index ea3bc963a..0e9097fd8 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformMemory.h +++ b/src/framework/internal/PlatformAbstraction/PlatformMemory.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMMEMORY_H -#define RAMSES_PLATFORMMEMORY_H +#pragma once -#include +#include #include -namespace ramses_internal +namespace ramses::internal { namespace PlatformMemory { @@ -39,5 +38,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/PlatformAbstraction/src/PlatformSignal.cpp b/src/framework/internal/PlatformAbstraction/PlatformSignal.cpp similarity index 89% rename from framework/PlatformAbstraction/src/PlatformSignal.cpp rename to src/framework/internal/PlatformAbstraction/PlatformSignal.cpp index 7520ecf02..2a15e37cb 100644 --- a/framework/PlatformAbstraction/src/PlatformSignal.cpp +++ b/src/framework/internal/PlatformAbstraction/PlatformSignal.cpp @@ -6,26 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformSignal.h" -#include "Collections/HashMap.h" +#include "internal/PlatformAbstraction/PlatformSignal.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" #include -namespace ramses_internal +namespace ramses::internal { namespace { struct HandlerInfo { - PlatformSignal::SignalHandlerFunction handler; - PlatformSignal::SignalHandlerFunction previousHandler; - bool chainPrevious; - bool active; + PlatformSignal::SignalHandlerFunction handler{nullptr}; + PlatformSignal::SignalHandlerFunction previousHandler{nullptr}; + bool chainPrevious{false}; + bool active{false}; }; HashMap gSignalMap; void SignalHandlerDispatcher(int sig) { - ESignal enumSig = static_cast(sig); + auto enumSig = static_cast(sig); HandlerInfo info; if (EStatus::Ok == gSignalMap.get(enumSig, info)) { diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformSignal.h b/src/framework/internal/PlatformAbstraction/PlatformSignal.h similarity index 88% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformSignal.h rename to src/framework/internal/PlatformAbstraction/PlatformSignal.h index 2ab5c5e32..1a973f479 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformSignal.h +++ b/src/framework/internal/PlatformAbstraction/PlatformSignal.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_PLATFORMSIGNAL_H -#define RAMSES_PLATFORMABSTRACTION_PLATFORMSIGNAL_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { enum class ESignal { @@ -34,5 +33,3 @@ namespace ramses_internal static const char* SignalToString(ESignal sig); }; } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformStringUtils.h b/src/framework/internal/PlatformAbstraction/PlatformStringUtils.h similarity index 91% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformStringUtils.h rename to src/framework/internal/PlatformAbstraction/PlatformStringUtils.h index 7c775fc36..a57885696 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformStringUtils.h +++ b/src/framework/internal/PlatformAbstraction/PlatformStringUtils.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMSTRINGUTILS_H -#define RAMSES_PLATFORMSTRINGUTILS_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { namespace PlatformStringUtils { @@ -33,5 +32,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformThread.h b/src/framework/internal/PlatformAbstraction/PlatformThread.h similarity index 91% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformThread.h rename to src/framework/internal/PlatformAbstraction/PlatformThread.h index 1283e715c..7ddaa7eb5 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformThread.h +++ b/src/framework/internal/PlatformAbstraction/PlatformThread.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMTHREAD_H -#define RAMSES_PLATFORMTHREAD_H +#pragma once -#include "PlatformAbstraction/Runnable.h" +#include "internal/PlatformAbstraction/Runnable.h" #include #include @@ -17,12 +16,12 @@ #include #ifdef _WIN32 -#include "PlatformAbstraction/internal/Thread_std.h" +#include "internal/PlatformAbstraction/internal/Thread_std.h" #else -#include "PlatformAbstraction/internal/Thread_Posix.h" +#include "internal/PlatformAbstraction/internal/Thread_Posix.h" #endif -namespace ramses_internal +namespace ramses::internal { class PlatformThread final { @@ -112,5 +111,3 @@ namespace ramses_internal std::this_thread::sleep_for(std::chrono::milliseconds{msec}); } } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTime.h b/src/framework/internal/PlatformAbstraction/PlatformTime.h similarity index 96% rename from framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTime.h rename to src/framework/internal/PlatformAbstraction/PlatformTime.h index a4bc0ac1c..9974fcb1c 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/PlatformTime.h +++ b/src/framework/internal/PlatformAbstraction/PlatformTime.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMTIME_H -#define RAMSES_PLATFORMTIME_H +#pragma once -#include "PlatformTypes.h" #include "synchronized_clock.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { namespace PlatformTime { @@ -93,5 +93,3 @@ namespace ramses_internal return std::chrono::time_point_cast(now).time_since_epoch().count(); } } - -#endif diff --git a/framework/PlatformAbstraction/src/PlatformTypes.cpp b/src/framework/internal/PlatformAbstraction/PlatformTypes.cpp similarity index 86% rename from framework/PlatformAbstraction/src/PlatformTypes.cpp rename to src/framework/internal/PlatformAbstraction/PlatformTypes.cpp index 9f146775f..0ac0bd571 100644 --- a/framework/PlatformAbstraction/src/PlatformTypes.cpp +++ b/src/framework/internal/PlatformAbstraction/PlatformTypes.cpp @@ -6,9 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformTypes.h" +#include +#include -namespace ramses_internal +namespace ramses::internal { static_assert(1u == sizeof(int8_t), "Unexpected sizeof(int8_t)"); static_assert(1u == sizeof(int8_t), "Unexpected sizeof(int8_t)"); @@ -23,8 +24,8 @@ namespace ramses_internal static_assert(8u == sizeof(double), "Unexpected sizeof(double)"); static_assert(1u == sizeof(bool), "Unexpected sizeof(bool)"); static_assert(1u == sizeof(char), "Unexpected sizeof(char)"); - static_assert(1u == sizeof(Byte), "Unexpected sizeof(Byte)"); + static_assert(1u == sizeof(std::byte), "Unexpected sizeof(std::byte)"); - static_assert(sizeof(void*) == sizeof(Int), "Unexpected sizeof(Int)"); + static_assert(sizeof(void*) == sizeof(intptr_t), "Unexpected sizeof(intptr_t)"); static_assert(sizeof(void*) == sizeof(uintptr_t), "Unexpected sizeof(uintptr_t)"); } diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/Runnable.h b/src/framework/internal/PlatformAbstraction/Runnable.h similarity index 90% rename from framework/PlatformAbstraction/include/PlatformAbstraction/Runnable.h rename to src/framework/internal/PlatformAbstraction/Runnable.h index fd85d0c5d..c9a5fe433 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/Runnable.h +++ b/src/framework/internal/PlatformAbstraction/Runnable.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_RUNNABLE_H -#define RAMSES_FRAMEWORK_RUNNABLE_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class Runnable { @@ -43,5 +42,3 @@ namespace ramses_internal std::atomic mCancel{false}; }; } - -#endif diff --git a/framework/PlatformAbstraction/src/StringOutputStream.cpp b/src/framework/internal/PlatformAbstraction/StringOutputStream.cpp similarity index 86% rename from framework/PlatformAbstraction/src/StringOutputStream.cpp rename to src/framework/internal/PlatformAbstraction/StringOutputStream.cpp index 2f8df0ae7..792193c28 100644 --- a/framework/PlatformAbstraction/src/StringOutputStream.cpp +++ b/src/framework/internal/PlatformAbstraction/StringOutputStream.cpp @@ -6,4 +6,4 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/VariantWrapper.h b/src/framework/internal/PlatformAbstraction/VariantWrapper.h similarity index 77% rename from framework/PlatformAbstraction/include/PlatformAbstraction/VariantWrapper.h rename to src/framework/internal/PlatformAbstraction/VariantWrapper.h index 530ef4488..ca386f3f9 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/VariantWrapper.h +++ b/src/framework/internal/PlatformAbstraction/VariantWrapper.h @@ -6,11 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_VARIANTWRAPPER_H -#define RAMSES_PLATFORMABSTRACTION_VARIANTWRAPPER_H +#pragma once -#include "Utils/Warnings.h" +#include "internal/Core/Utils/Warnings.h" #include - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/internal/Thread_Posix.h b/src/framework/internal/PlatformAbstraction/internal/Thread_Posix.h similarity index 91% rename from framework/PlatformAbstraction/include/PlatformAbstraction/internal/Thread_Posix.h rename to src/framework/internal/PlatformAbstraction/internal/Thread_Posix.h index 08a385fb2..22868077b 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/internal/Thread_Posix.h +++ b/src/framework/internal/PlatformAbstraction/internal/Thread_Posix.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_THREAD_POSIX_H -#define RAMSES_PLATFORMABSTRACTION_THREAD_POSIX_H +#pragma once -#include "PlatformAbstraction/Macros.h" -#include "Utils/AssertMovable.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/Core/Utils/AssertMovable.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { namespace internal { @@ -40,7 +39,7 @@ namespace internal Thread& operator=(const Thread&) = delete; private: - static void* Run(void*); + static void* Run(void* arg); bool m_running = false; pthread_t m_threadId{}; @@ -73,9 +72,13 @@ namespace internal pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); if (pthread_create(&m_threadId, &attr, Thread::Run, m_fun.get()) == 0) + { m_running = true; + } else + { m_fun = nullptr; + } pthread_attr_destroy(&attr); } @@ -83,7 +86,7 @@ namespace internal { assert(arg); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) must restore original type after going through C - Fun_t* fun = reinterpret_cast(arg); + auto* fun = reinterpret_cast(arg); (*fun)(); return nullptr; } @@ -119,5 +122,3 @@ namespace internal } } } - -#endif diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/internal/Thread_std.h b/src/framework/internal/PlatformAbstraction/internal/Thread_std.h similarity index 86% rename from framework/PlatformAbstraction/include/PlatformAbstraction/internal/Thread_std.h rename to src/framework/internal/PlatformAbstraction/internal/Thread_std.h index 63a15f5b4..2427a8af7 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/internal/Thread_std.h +++ b/src/framework/internal/PlatformAbstraction/internal/Thread_std.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_THREAD_STD_H -#define RAMSES_PLATFORMABSTRACTION_THREAD_STD_H +#pragma once -#include "PlatformAbstraction/Macros.h" -#include "Utils/AssertMovable.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/Core/Utils/AssertMovable.h" #include #include -namespace ramses_internal +namespace ramses::internal { namespace internal { @@ -57,5 +56,3 @@ namespace internal } } } - -#endif diff --git a/framework/PlatformAbstraction/src/synchronized_clock.cpp b/src/framework/internal/PlatformAbstraction/synchronized_clock.cpp similarity index 91% rename from framework/PlatformAbstraction/src/synchronized_clock.cpp rename to src/framework/internal/PlatformAbstraction/synchronized_clock.cpp index 870522963..7ccc2032d 100644 --- a/framework/PlatformAbstraction/src/synchronized_clock.cpp +++ b/src/framework/internal/PlatformAbstraction/synchronized_clock.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/synchronized_clock.h" +#include "internal/PlatformAbstraction/synchronized_clock.h" #ifdef RAMSES_LINUX_USE_DEV_PTP -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { namespace synchronized_clock_impl { diff --git a/framework/PlatformAbstraction/include/PlatformAbstraction/synchronized_clock.h b/src/framework/internal/PlatformAbstraction/synchronized_clock.h similarity index 94% rename from framework/PlatformAbstraction/include/PlatformAbstraction/synchronized_clock.h rename to src/framework/internal/PlatformAbstraction/synchronized_clock.h index 01003f859..e51f28ddf 100644 --- a/framework/PlatformAbstraction/include/PlatformAbstraction/synchronized_clock.h +++ b/src/framework/internal/PlatformAbstraction/synchronized_clock.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYNCHRONIZED_CLOCK_H -#define RAMSES_SYNCHRONIZED_CLOCK_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { enum class synchronized_clock_type { @@ -48,7 +47,7 @@ namespace ramses_internal #if defined(RAMSES_LINUX_USE_DEV_PTP) #include -namespace ramses_internal +namespace ramses::internal { namespace synchronized_clock_impl { @@ -80,7 +79,7 @@ namespace ramses_internal } #else -namespace ramses_internal +namespace ramses::internal { inline const char* synchronized_clock::source() { @@ -98,5 +97,3 @@ namespace ramses_internal } } #endif - -#endif diff --git a/framework/Ramsh/src/Ramsh.cpp b/src/framework/internal/Ramsh/Ramsh.cpp similarity index 86% rename from framework/Ramsh/src/Ramsh.cpp rename to src/framework/internal/Ramsh/Ramsh.cpp index b61891f39..090337e77 100644 --- a/framework/Ramsh/src/Ramsh.cpp +++ b/src/framework/internal/Ramsh/Ramsh.cpp @@ -6,21 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/Ramsh.h" -#include "Collections/StringOutputStream.h" -#include "Collections/HashSet.h" -#include "Utils/LogMacros.h" -#include "Ramsh/RamshCommandSetContextLogLevelFilter.h" -#include "Ramsh/RamshCommandPrintHelp.h" -#include "Ramsh/RamshCommandPrintBuildConfig.h" -#include "Ramsh/RamshCommandPrintRamsesVersion.h" -#include "Ramsh/RamshCommandSetConsoleLogLevel.h" -#include "Ramsh/RamshCommandSetContextLogLevel.h" -#include "Ramsh/RamshCommandSetContextLogLevelFilter.h" -#include "Ramsh/RamshCommandPrintLogLevels.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommandSetContextLogLevelFilter.h" +#include "internal/Ramsh/RamshCommandPrintHelp.h" +#include "internal/Ramsh/RamshCommandPrintBuildConfig.h" +#include "internal/Ramsh/RamshCommandPrintRamsesVersion.h" +#include "internal/Ramsh/RamshCommandSetConsoleLogLevel.h" +#include "internal/Ramsh/RamshCommandSetContextLogLevel.h" +#include "internal/Ramsh/RamshCommandSetContextLogLevelFilter.h" +#include "internal/Ramsh/RamshCommandPrintLogLevels.h" #include -namespace ramses_internal +namespace ramses::internal { Ramsh::Ramsh() { diff --git a/framework/Ramsh/include/Ramsh/Ramsh.h b/src/framework/internal/Ramsh/Ramsh.h similarity index 96% rename from framework/Ramsh/include/Ramsh/Ramsh.h rename to src/framework/internal/Ramsh/Ramsh.h index fb3d04a2b..3689ba9fc 100644 --- a/framework/Ramsh/include/Ramsh/Ramsh.h +++ b/src/framework/internal/Ramsh/Ramsh.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSH_H -#define RAMSES_RAMSH_H +#pragma once #include #include @@ -15,7 +14,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { class RamshCommand; class RamshCommandPrintHelp; @@ -53,5 +52,3 @@ namespace ramses_internal std::shared_ptr m_pCmdPrintLogLevels; }; } - -#endif diff --git a/framework/Ramsh/src/RamshCommand.cpp b/src/framework/internal/Ramsh/RamshCommand.cpp similarity index 93% rename from framework/Ramsh/src/RamshCommand.cpp rename to src/framework/internal/Ramsh/RamshCommand.cpp index 4cead3962..f885a5a8a 100644 --- a/framework/Ramsh/src/RamshCommand.cpp +++ b/src/framework/internal/Ramsh/RamshCommand.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" #include "fmt/format.h" -namespace ramses_internal +namespace ramses::internal { void RamshCommand::registerKeyword(const std::string& keyword) diff --git a/framework/Ramsh/include/Ramsh/RamshCommand.h b/src/framework/internal/Ramsh/RamshCommand.h similarity index 89% rename from framework/Ramsh/include/Ramsh/RamshCommand.h rename to src/framework/internal/Ramsh/RamshCommand.h index 3037f0254..91b5e18f5 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommand.h +++ b/src/framework/internal/Ramsh/RamshCommand.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMAND_H -#define RAMSES_RAMSHCOMMAND_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class RamshCommand { @@ -30,6 +29,4 @@ namespace ramses_internal std::vector m_keywords; std::string description; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/include/Ramsh/RamshCommandArguments.h b/src/framework/internal/Ramsh/RamshCommandArguments.h similarity index 92% rename from framework/Ramsh/include/Ramsh/RamshCommandArguments.h rename to src/framework/internal/Ramsh/RamshCommandArguments.h index 42a8aa5bc..bda4d5f66 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandArguments.h +++ b/src/framework/internal/Ramsh/RamshCommandArguments.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDARGUMENTS_H -#define RAMSES_RAMSHCOMMANDARGUMENTS_H +#pragma once -#include "Ramsh/RamshCommand.h" -#include "Ramsh/RamshCommandArgumentsDataProvider.h" -#include "Ramsh/RamshCommandArgumentsUtils.h" +#include "internal/Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommandArgumentsDataProvider.h" +#include "internal/Ramsh/RamshCommandArgumentsUtils.h" -namespace ramses_internal +namespace ramses::internal { // base class for easy-to-use argument definitions @@ -41,15 +40,13 @@ namespace ramses_internal bool executeInput(const std::vector& in) override; [[nodiscard]] std::string descriptionString() const override; - ~RamshCommandArgsBase() override - { - } + ~RamshCommandArgsBase() override = default; protected: // gets implemented by a deriving class, which either tries to get the value of the next argument or executes the command implementation virtual bool executeInternal(const RamshArgumentDataProvider&,Unused&,Unused&,Unused&,Unused&,Unused&) const = 0; // initialize the argument definitions - virtual void init(); + void init(); }; template @@ -64,13 +61,13 @@ namespace ramses_internal using RamshCommandArgsBase::executeInternal; // gets the value of the current (N-th) argument and calls the deriving class' implementation to either get the next argument or execute the command implementation - bool executeInternal(const RamshArgumentDataProvider&,T1&,T2&,T3&,T4&,Unused&) const override; + bool executeInternal(const RamshArgumentDataProvider& args,T1& a1,T2& a2,T3& a3,T4& a4,Unused& unused) const override; // gets implemented by a deriving class, which either tries to get the value of the next argument or executes the command implementation virtual bool executeInternal(const RamshArgumentDataProvider&,T0&,T1&,T2&,T3&,T4&) const = 0; // initializes the current (N-th) argument and call the base class' implementation to initialize the next argument - void init() override; + void init(); }; // ------------------------------------------------------- @@ -184,7 +181,7 @@ struct RamshCommandArgs \ } template - inline bool RamshCommandArgsBase::executeInternal(const RamshArgumentDataProvider& args,T1& a1,T2& a2,T3& a3,T4& a4,Unused&) const + inline bool RamshCommandArgsBase::executeInternal(const RamshArgumentDataProvider& args,T1& a1,T2& a2,T3& a3,T4& a4,Unused& /*unused*/) const { T0 val; @@ -205,9 +202,7 @@ struct RamshCommandArgs \ } template - inline RamshCommandArgsBase::RamshCommandArgsBase() - { - } + inline RamshCommandArgsBase::RamshCommandArgsBase() = default; template inline bool RamshCommandArgsBase::executeInput(const std::vector& in) @@ -231,6 +226,4 @@ struct RamshCommandArgs \ { } -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/include/Ramsh/RamshCommandArgumentsConverter.h b/src/framework/internal/Ramsh/RamshCommandArgumentsConverter.h similarity index 68% rename from framework/Ramsh/include/Ramsh/RamshCommandArgumentsConverter.h rename to src/framework/internal/Ramsh/RamshCommandArgumentsConverter.h index fa081ee39..99a7d8e23 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandArgumentsConverter.h +++ b/src/framework/internal/Ramsh/RamshCommandArgumentsConverter.h @@ -6,49 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDARGUMENTSCONVERTER_H -#define RAMSES_RAMSHCOMMANDARGUMENTSCONVERTER_H +#pragma once -#include "Collections/Vector.h" -#include "Collections/Pair.h" -#include "Collections/StringOutputStream.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" #include -namespace ramses_internal +#include + +namespace ramses::internal { using RamshArgumentData = std::string; -} - -#define DEFINE_INT_CONVERTER(TYPE) \ - template<> \ - struct ArgumentConverter \ - { \ - static inline bool tryConvert(const RamshArgumentData& data, TYPE& value) \ - { \ - int64_t val = 0; \ - bool result = ArgumentConverter::tryConvert(data,val); \ - value = static_cast(val); \ - return result; \ - } \ - }; -namespace ramses_internal -{ // converts raw binary data - template + template struct ArgumentConverter { - static inline bool tryConvert(const RamshArgumentData& data, T& value) - { - if(data.size() < sizeof(T)) - { - return false; - } - - PlatformMemory::Copy(&value,data.c_str(),sizeof(T)); - return true; - } }; // converts data and adds it to a Vector (raw data separated by ',') @@ -61,9 +36,9 @@ namespace ramses_internal bool result = true; - for(size_t i = 0; i < data.size(); i++) + for(auto ch : data) { - if(',' == data.at(i)) + if(',' == ch) { T val = T(); result = result && ArgumentConverter::tryConvert(curr,val); @@ -73,7 +48,7 @@ namespace ramses_internal } else { - curr+=data.at(i); + curr += ch; } } @@ -96,16 +71,16 @@ namespace ramses_internal bool result = false; std::string curr; - for(size_t i = 0; i < data.size(); i++) + for (auto ch : data) { - if(';' == data.at(i)) + if(';' == ch) { result = ArgumentConverter::tryConvert(curr,val.first); curr.clear(); } else { - curr+=data.at(i); + curr += ch; } } @@ -116,31 +91,36 @@ namespace ramses_internal }; // standard converter for int-types - template<> - struct ArgumentConverter + template + struct ArgumentConverter>> { - static inline bool tryConvert(const RamshArgumentData& data, int64_t& value) + static inline bool tryConvert(const RamshArgumentData& data, T& value) { - char* endptr; - value = static_cast(strtol(data.c_str(),&endptr,10)); + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg): false positive + value = static_cast(strtoll(data.c_str(),&endptr,10)); // conversion is only successful if anything was converted return endptr != data.c_str(); } }; - // convertes which use the standard converter for int-types and just cast the value to the right type - DEFINE_INT_CONVERTER(uint64_t) - DEFINE_INT_CONVERTER(uint32_t) - DEFINE_INT_CONVERTER(int32_t) - DEFINE_INT_CONVERTER(uint16_t) - DEFINE_INT_CONVERTER(int16_t) + template + struct ArgumentConverter>> + { + static inline bool tryConvert(const RamshArgumentData& data, T& value) + { + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg): false positive + value = static_cast(strtoull(data.c_str(), &endptr, 10)); + // conversion is only successful if anything was converted + return endptr != data.c_str(); + } + }; template<> struct ArgumentConverter { static inline bool tryConvert(const RamshArgumentData& data, double& value) { - char* endptr; + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg): false positive value = static_cast(strtod(data.c_str(),&endptr)); // conversion is only successful if anything was converted return endptr != data.c_str(); @@ -171,6 +151,4 @@ namespace ramses_internal } }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/include/Ramsh/RamshCommandArgumentsDataProvider.h b/src/framework/internal/Ramsh/RamshCommandArgumentsDataProvider.h similarity index 93% rename from framework/Ramsh/include/Ramsh/RamshCommandArgumentsDataProvider.h rename to src/framework/internal/Ramsh/RamshCommandArgumentsDataProvider.h index e470fad80..56afeba8f 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandArgumentsDataProvider.h +++ b/src/framework/internal/Ramsh/RamshCommandArgumentsDataProvider.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDARGUMENTSDATAPROVIDER_H -#define RAMSES_RAMSHCOMMANDARGUMENTSDATAPROVIDER_H +#pragma once -#include "Ramsh/RamshCommandArgumentsConverter.h" -#include "Collections/HashSet.h" -#include "Ramsh/RamshTypeInfo.h" -#include "Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommandArgumentsConverter.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/Ramsh/RamshTypeInfo.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { class RamshArgumentBase; using ArgumentVector = std::vector; @@ -39,9 +38,7 @@ namespace ramses_internal protected: explicit RamshArgumentBase(const RamshTypeInfo& typeinfo, void* defaultValue = nullptr); - virtual ~RamshArgumentBase() - { - } + virtual ~RamshArgumentBase() = default; // amount of data consumed by the argument - usually 2 for data arguments (parameter name + data) [[nodiscard]] virtual uint32_t amountConsumed() const; @@ -49,8 +46,6 @@ namespace ramses_internal // try to set the argument's data with a given keyword (doesn't modify the argument itself, just returns a data object or nullptr if keyword doesn't match) [[nodiscard]] const RamshArgumentData* set(const std::string& keyword, const std::string& data) const; - [[nodiscard]] const RamshArgumentData* forceSet(const std::string& data) const; - // tries to get the value of the argument (converts the data) with a data object previously retrieved by calling set/forceSet template bool getValue(const RamshArgumentData* data, T& value) const; @@ -143,9 +138,7 @@ namespace ramses_internal : RamshArgument(defaultValue) {} - inline TypedRamshArgument() - : RamshArgument() - {} + TypedRamshArgument() = default; [[nodiscard]] uint32_t amountConsumed() const override; }; @@ -226,12 +219,6 @@ namespace ramses_internal return nullptr; } - inline const RamshArgumentData* RamshArgumentBase::forceSet(const std::string& data) const - { - return &data; - } - - template inline void RamshArgumentBase::setDefaultValueInternal(const T& defaultValue) { @@ -239,10 +226,14 @@ namespace ramses_internal if(m_typeInfo == RamshTypeId::id()) { // either allocates new memory for default value or copy-assigns new default value - if(m_defaultValue) + if (m_defaultValue) + { *static_cast(m_defaultValue) = defaultValue; + } else + { m_defaultValue = new T(defaultValue); + } } else { @@ -422,16 +413,18 @@ namespace ramses_internal } --pos; } - else if(m_args[j]->amountConsumed() == 1) + else if (m_args[j]->amountConsumed() == 1) + { // only the flag is consumed by the argument - m_data[j] = m_args[j]->set(in[pos]->substr(1,in[pos]->size()-1),""); + m_data[j] = m_args[j]->set(in[pos]->substr(1, in[pos]->size() - 1), ""); + } if(m_data[j]) { // if an argument value was found, remove the consumed data - in.erase(in.begin() + pos); + in.erase(in.begin() + static_cast(pos)); if(m_args[j]->amountConsumed() > 1) - in.erase(in.begin() + pos); + in.erase(in.begin() + static_cast(pos)); break; } @@ -445,7 +438,7 @@ namespace ramses_internal // only set the data if an argument has no previously found value, if any data is left and if the argument actually consumes any data if(!m_data[j] && !in.empty() && m_args[j]->amountConsumed() > 1) { - m_data[j] = m_args[j]->forceSet(**in.begin()); + m_data[j] = *in.begin(); in.erase(in.begin()); } } @@ -491,7 +484,7 @@ namespace ramses_internal template inline RamshArgument& RamshArgumentProvider::addArgument(const T& defaultValue) { - TypedRamshArgument* arg = new TypedRamshArgument(defaultValue); + auto* arg = new TypedRamshArgument(defaultValue); m_arguments.push_back(arg); return *arg; } @@ -499,7 +492,7 @@ namespace ramses_internal template inline RamshArgument& RamshArgumentProvider::addArgument() { - TypedRamshArgument* arg = new TypedRamshArgument(); + auto* arg = new TypedRamshArgument(); m_arguments.push_back(arg); return *arg; } @@ -522,7 +515,7 @@ namespace ramses_internal template<> struct ArgumentConverterProxy { - static inline bool tryConvert(const void* defaultValue, const RamshArgumentData&, bool& value) + static inline bool tryConvert(const void* defaultValue, const RamshArgumentData& /*unused*/, bool& value) { // either flip default value or return true if flag was found value = defaultValue ? !(*static_cast(defaultValue)) : true; @@ -533,10 +526,10 @@ namespace ramses_internal template struct ArgumentConverterProxy { - static inline bool tryConvert(const void*, const RamshArgumentData& data, T& value) + static inline bool tryConvert(const void* /*unused*/, const RamshArgumentData& data, T& value) { return ArgumentConverter::tryConvert(data,value); } }; } -#endif + diff --git a/framework/Ramsh/include/Ramsh/RamshCommandArgumentsUtils.h b/src/framework/internal/Ramsh/RamshCommandArgumentsUtils.h similarity index 94% rename from framework/Ramsh/include/Ramsh/RamshCommandArgumentsUtils.h rename to src/framework/internal/Ramsh/RamshCommandArgumentsUtils.h index 26f4cea36..30a54e2d5 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandArgumentsUtils.h +++ b/src/framework/internal/Ramsh/RamshCommandArgumentsUtils.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDARGUMENTSUTILS_H -#define RAMSES_RAMSHCOMMANDARGUMENTSUTILS_H +#pragma once // helper macros for class generation #define RAMSH_REPEAT_0(WHAT) @@ -29,7 +28,7 @@ #define RAMSH_COND_CALL2(CONDITION,WHAT) WHAT##_##CONDITION #define RAMSH_COND_CALL(CONDITION,WHAT) RAMSH_COND_CALL2(CONDITION,WHAT) -namespace ramses_internal +namespace ramses::internal { namespace ramsh_utils { @@ -51,5 +50,3 @@ namespace ramses_internal } } -#endif - diff --git a/framework/Ramsh/src/RamshCommandExit.cpp b/src/framework/internal/Ramsh/RamshCommandExit.cpp similarity index 80% rename from framework/Ramsh/src/RamshCommandExit.cpp rename to src/framework/internal/Ramsh/RamshCommandExit.cpp index 41b2ed386..f91f46085 100644 --- a/framework/Ramsh/src/RamshCommandExit.cpp +++ b/src/framework/internal/Ramsh/RamshCommandExit.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandExit.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommandExit.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandExit::RamshCommandExit() : m_exitRequested(false) @@ -19,9 +19,8 @@ namespace ramses_internal description = "exit program"; } - bool RamshCommandExit::executeInput(const std::vector& input) + bool RamshCommandExit::executeInput([[maybe_unused]] const std::vector& input) { - UNUSED(input); LOG_INFO(CONTEXT_RAMSH, "Received ramsh command exit"); m_exitRequested = true; m_exitEvent.signal(); diff --git a/framework/Ramsh/include/Ramsh/RamshCommandExit.h b/src/framework/internal/Ramsh/RamshCommandExit.h similarity index 83% rename from framework/Ramsh/include/Ramsh/RamshCommandExit.h rename to src/framework/internal/Ramsh/RamshCommandExit.h index ff9572961..b5b2ba3a7 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandExit.h +++ b/src/framework/internal/Ramsh/RamshCommandExit.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDEXIT_H -#define RAMSES_RAMSHCOMMANDEXIT_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -#include "PlatformAbstraction/PlatformEvent.h" +#include "internal/PlatformAbstraction/PlatformEvent.h" #include -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -33,5 +32,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/Ramsh/src/RamshCommandPrintBuildConfig.cpp b/src/framework/internal/Ramsh/RamshCommandPrintBuildConfig.cpp similarity index 91% rename from framework/Ramsh/src/RamshCommandPrintBuildConfig.cpp rename to src/framework/internal/Ramsh/RamshCommandPrintBuildConfig.cpp index 03ea995ee..94931b75b 100644 --- a/framework/Ramsh/src/RamshCommandPrintBuildConfig.cpp +++ b/src/framework/internal/Ramsh/RamshCommandPrintBuildConfig.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandPrintBuildConfig.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommandPrintBuildConfig.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" #include "ramses-sdk-build-config.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandPrintBuildConfig::RamshCommandPrintBuildConfig() { @@ -19,7 +19,7 @@ namespace ramses_internal description = "print build configuration"; } - bool RamshCommandPrintBuildConfig::executeInput(const std::vector&) + bool RamshCommandPrintBuildConfig::executeInput(const std::vector& /*unused*/) { LOG_INFO(CONTEXT_RAMSH, "VERSION_STRING = " << ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION << "\n" << diff --git a/framework/Ramsh/include/Ramsh/RamshCommandPrintBuildConfig.h b/src/framework/internal/Ramsh/RamshCommandPrintBuildConfig.h similarity index 78% rename from framework/Ramsh/include/Ramsh/RamshCommandPrintBuildConfig.h rename to src/framework/internal/Ramsh/RamshCommandPrintBuildConfig.h index d13bbe72d..6ce08e5c1 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandPrintBuildConfig.h +++ b/src/framework/internal/Ramsh/RamshCommandPrintBuildConfig.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDPRINTBUILDCONFIG_H -#define RAMSES_RAMSHCOMMANDPRINTBUILDCONFIG_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -22,6 +21,4 @@ namespace ramses_internal bool executeInput(const std::vector& input) override; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommandPrintHelp.cpp b/src/framework/internal/Ramsh/RamshCommandPrintHelp.cpp similarity index 80% rename from framework/Ramsh/src/RamshCommandPrintHelp.cpp rename to src/framework/internal/Ramsh/RamshCommandPrintHelp.cpp index c477cf43f..aa8eca28e 100644 --- a/framework/Ramsh/src/RamshCommandPrintHelp.cpp +++ b/src/framework/internal/Ramsh/RamshCommandPrintHelp.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandPrintHelp.h" -#include "Ramsh/Ramsh.h" -#include "Collections/HashSet.h" -#include "Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommandPrintHelp.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandPrintHelp::RamshCommandPrintHelp(const Ramsh& ramsh) : m_ramsh(ramsh) { diff --git a/framework/Ramsh/include/Ramsh/RamshCommandPrintHelp.h b/src/framework/internal/Ramsh/RamshCommandPrintHelp.h similarity index 81% rename from framework/Ramsh/include/Ramsh/RamshCommandPrintHelp.h rename to src/framework/internal/Ramsh/RamshCommandPrintHelp.h index 16fff99ba..6128d7b2e 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandPrintHelp.h +++ b/src/framework/internal/Ramsh/RamshCommandPrintHelp.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDPRINTHELP_H -#define RAMSES_RAMSHCOMMANDPRINTHELP_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -25,6 +24,4 @@ namespace ramses_internal const Ramsh& m_ramsh; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommandPrintLogLevels.cpp b/src/framework/internal/Ramsh/RamshCommandPrintLogLevels.cpp similarity index 85% rename from framework/Ramsh/src/RamshCommandPrintLogLevels.cpp rename to src/framework/internal/Ramsh/RamshCommandPrintLogLevels.cpp index 3183d7879..52a44d657 100644 --- a/framework/Ramsh/src/RamshCommandPrintLogLevels.cpp +++ b/src/framework/internal/Ramsh/RamshCommandPrintLogLevels.cpp @@ -7,12 +7,12 @@ // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandPrintLogLevels.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" +#include "internal/Ramsh/RamshCommandPrintLogLevels.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandPrintLogLevels::RamshCommandPrintLogLevels(const Ramsh& ramsh) : m_ramsh(ramsh) { @@ -20,10 +20,8 @@ namespace ramses_internal description = "print Log Levels"; } - bool RamshCommandPrintLogLevels::executeInput(const std::vector& input) + bool RamshCommandPrintLogLevels::executeInput([[maybe_unused]] const std::vector& input) { - UNUSED(input); - const RamsesLogger& logger = GetRamsesLogger(); const ELogLevel consoleLogLevel = logger.getConsoleLogLevel(); diff --git a/framework/Ramsh/include/Ramsh/RamshCommandPrintLogLevels.h b/src/framework/internal/Ramsh/RamshCommandPrintLogLevels.h similarity index 80% rename from framework/Ramsh/include/Ramsh/RamshCommandPrintLogLevels.h rename to src/framework/internal/Ramsh/RamshCommandPrintLogLevels.h index e7c5aaa5c..8ce4d5574 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandPrintLogLevels.h +++ b/src/framework/internal/Ramsh/RamshCommandPrintLogLevels.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDPRINTLOGLEVELS_H -#define RAMSES_RAMSHCOMMANDPRINTLOGLEVELS_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -25,6 +24,4 @@ namespace ramses_internal const Ramsh& m_ramsh; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommandPrintRamsesVersion.cpp b/src/framework/internal/Ramsh/RamshCommandPrintRamsesVersion.cpp similarity index 84% rename from framework/Ramsh/src/RamshCommandPrintRamsesVersion.cpp rename to src/framework/internal/Ramsh/RamshCommandPrintRamsesVersion.cpp index f98daa07d..f199cee7f 100644 --- a/framework/Ramsh/src/RamshCommandPrintRamsesVersion.cpp +++ b/src/framework/internal/Ramsh/RamshCommandPrintRamsesVersion.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandPrintRamsesVersion.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommandPrintRamsesVersion.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" #include "ramses-sdk-build-config.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandPrintRamsesVersion::RamshCommandPrintRamsesVersion() { diff --git a/framework/Ramsh/include/Ramsh/RamshCommandPrintRamsesVersion.h b/src/framework/internal/Ramsh/RamshCommandPrintRamsesVersion.h similarity index 78% rename from framework/Ramsh/include/Ramsh/RamshCommandPrintRamsesVersion.h rename to src/framework/internal/Ramsh/RamshCommandPrintRamsesVersion.h index 05cff5b12..e3e63122d 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandPrintRamsesVersion.h +++ b/src/framework/internal/Ramsh/RamshCommandPrintRamsesVersion.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDPRINTRAMSESVERSION_H -#define RAMSES_RAMSHCOMMANDPRINTRAMSESVERSION_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -22,6 +21,4 @@ namespace ramses_internal bool executeInput(const std::vector& input) override; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommandSetConsoleLogLevel.cpp b/src/framework/internal/Ramsh/RamshCommandSetConsoleLogLevel.cpp similarity index 84% rename from framework/Ramsh/src/RamshCommandSetConsoleLogLevel.cpp rename to src/framework/internal/Ramsh/RamshCommandSetConsoleLogLevel.cpp index 320334e2d..5938e0d70 100644 --- a/framework/Ramsh/src/RamshCommandSetConsoleLogLevel.cpp +++ b/src/framework/internal/Ramsh/RamshCommandSetConsoleLogLevel.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandSetConsoleLogLevel.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" -#include "Utils/LogHelper.h" +#include "internal/Ramsh/RamshCommandSetConsoleLogLevel.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/Core/Utils/LogHelper.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandSetConsoleLogLevel::RamshCommandSetConsoleLogLevel(const Ramsh& ramsh) : m_ramsh(ramsh) { diff --git a/framework/Ramsh/include/Ramsh/RamshCommandSetConsoleLogLevel.h b/src/framework/internal/Ramsh/RamshCommandSetConsoleLogLevel.h similarity index 80% rename from framework/Ramsh/include/Ramsh/RamshCommandSetConsoleLogLevel.h rename to src/framework/internal/Ramsh/RamshCommandSetConsoleLogLevel.h index c0a88320a..edee3a180 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandSetConsoleLogLevel.h +++ b/src/framework/internal/Ramsh/RamshCommandSetConsoleLogLevel.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDSETCONSOLELOGLEVEL_H -#define RAMSES_RAMSHCOMMANDSETCONSOLELOGLEVEL_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -26,6 +25,4 @@ namespace ramses_internal const Ramsh& m_ramsh; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommandSetContextLogLevel.cpp b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevel.cpp similarity index 86% rename from framework/Ramsh/src/RamshCommandSetContextLogLevel.cpp rename to src/framework/internal/Ramsh/RamshCommandSetContextLogLevel.cpp index 4ded6fab1..b8a62c268 100644 --- a/framework/Ramsh/src/RamshCommandSetContextLogLevel.cpp +++ b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevel.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandSetContextLogLevel.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" -#include "Utils/LogHelper.h" +#include "internal/Ramsh/RamshCommandSetContextLogLevel.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/Core/Utils/LogHelper.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandSetContextLogLevel::RamshCommandSetContextLogLevel(const Ramsh& ramsh) : m_ramsh(ramsh) { diff --git a/framework/Ramsh/include/Ramsh/RamshCommandSetContextLogLevel.h b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevel.h similarity index 80% rename from framework/Ramsh/include/Ramsh/RamshCommandSetContextLogLevel.h rename to src/framework/internal/Ramsh/RamshCommandSetContextLogLevel.h index e068b387d..f93848e51 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandSetContextLogLevel.h +++ b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevel.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDSETCONTEXTLOGLEVEL_H -#define RAMSES_RAMSHCOMMANDSETCONTEXTLOGLEVEL_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -26,6 +25,4 @@ namespace ramses_internal const Ramsh& m_ramsh; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommandSetContextLogLevelFilter.cpp b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevelFilter.cpp similarity index 86% rename from framework/Ramsh/src/RamshCommandSetContextLogLevelFilter.cpp rename to src/framework/internal/Ramsh/RamshCommandSetContextLogLevelFilter.cpp index 770a357e4..fbd75960a 100644 --- a/framework/Ramsh/src/RamshCommandSetContextLogLevelFilter.cpp +++ b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevelFilter.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommandSetContextLogLevelFilter.h" -#include "Ramsh/Ramsh.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" +#include "internal/Ramsh/RamshCommandSetContextLogLevelFilter.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" -namespace ramses_internal +namespace ramses::internal { RamshCommandSetContextLogLevelFilter::RamshCommandSetContextLogLevelFilter(const Ramsh& ramsh) : m_ramsh(ramsh) diff --git a/framework/Ramsh/include/Ramsh/RamshCommandSetContextLogLevelFilter.h b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevelFilter.h similarity index 79% rename from framework/Ramsh/include/Ramsh/RamshCommandSetContextLogLevelFilter.h rename to src/framework/internal/Ramsh/RamshCommandSetContextLogLevelFilter.h index 563bebf5f..e63f20a81 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommandSetContextLogLevelFilter.h +++ b/src/framework/internal/Ramsh/RamshCommandSetContextLogLevelFilter.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMANDSETCONTEXTLOGLEVELFILTER_H -#define RAMSES_RAMSHCOMMANDSETCONTEXTLOGLEVELFILTER_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -26,6 +25,4 @@ namespace ramses_internal const Ramsh& m_ramsh; }; -}// namespace ramses_internal - -#endif +}// namespace ramses::internal diff --git a/framework/Ramsh/src/RamshCommunicationChannelConsole.cpp b/src/framework/internal/Ramsh/RamshCommunicationChannelConsole.cpp similarity index 91% rename from framework/Ramsh/src/RamshCommunicationChannelConsole.cpp rename to src/framework/internal/Ramsh/RamshCommunicationChannelConsole.cpp index c4515cee4..790dabcfa 100644 --- a/framework/Ramsh/src/RamshCommunicationChannelConsole.cpp +++ b/src/framework/internal/Ramsh/RamshCommunicationChannelConsole.cpp @@ -6,16 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommunicationChannelConsole.h" -#include "Ramsh/RamshCommunicationChannelConsoleSignalHandler.h" -#include "Ramsh/Ramsh.h" -#include "Ramsh/RamshTools.h" -#include "Utils/RamsesLogger.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/ConsoleInput.h" - -namespace ramses_internal +#include + +#include "internal/Ramsh/RamshCommunicationChannelConsole.h" +#include "internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Ramsh/RamshTools.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/ConsoleInput.h" + +namespace ramses::internal { std::unique_ptr RamshCommunicationChannelConsole::Construct(Ramsh& ramsh, const std::string& prompt, bool startThread) { @@ -29,12 +31,11 @@ namespace ramses_internal return std::unique_ptr(new RamshCommunicationChannelConsole(ramsh, prompt, std::move(consoleInput), startThread)); } - RamshCommunicationChannelConsole::RamshCommunicationChannelConsole(Ramsh& ramsh, const std::string& prompt, std::unique_ptr consoleInput, bool startThread) + RamshCommunicationChannelConsole::RamshCommunicationChannelConsole(Ramsh& ramsh, std::string prompt, std::unique_ptr consoleInput, bool startThread) : m_ramsh(ramsh) - , m_prompt(prompt) + , m_prompt(std::move(prompt)) , m_pausePrompt(false) , m_checkInputThread("R_Ramsh_Console") - , m_commandHistory() , m_nextCommandFromHistory(0) , m_interactiveMode(!PlatformEnvironmentVariables::HasEnvVar("DISABLE_RAMSH_INTERACTIVE_MODE")) , m_console(std::move(consoleInput)) @@ -143,7 +144,7 @@ namespace ramses_internal bool inputEmpty = true; { PlatformGuard g(m_lock); - inputEmpty = m_input.size() == 0; + inputEmpty = m_input.empty(); if(!inputEmpty) { diff --git a/framework/Ramsh/include/Ramsh/RamshCommunicationChannelConsole.h b/src/framework/internal/Ramsh/RamshCommunicationChannelConsole.h similarity index 79% rename from framework/Ramsh/include/Ramsh/RamshCommunicationChannelConsole.h rename to src/framework/internal/Ramsh/RamshCommunicationChannelConsole.h index 3203d5659..d57b612b3 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommunicationChannelConsole.h +++ b/src/framework/internal/Ramsh/RamshCommunicationChannelConsole.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMUNICATIONCHANNELCONSOLE_H -#define RAMSES_RAMSHCOMMUNICATIONCHANNELCONSOLE_H +#pragma once -#include "PlatformAbstraction/PlatformThread.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { class ConsoleInput; class Ramsh; @@ -35,7 +34,7 @@ namespace ramses_internal void processInput(char c); private: - RamshCommunicationChannelConsole(Ramsh& ramsh, const std::string& prompt, std::unique_ptr consoleInput, bool startThread); + RamshCommunicationChannelConsole(Ramsh& ramsh, std::string prompt, std::unique_ptr consoleInput, bool startThread); void cancel() override; void run() override; @@ -57,5 +56,3 @@ namespace ramses_internal std::unique_ptr m_console; }; } - -#endif diff --git a/framework/Ramsh/src/RamshCommunicationChannelConsoleSignalHandler.cpp b/src/framework/internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.cpp similarity index 84% rename from framework/Ramsh/src/RamshCommunicationChannelConsoleSignalHandler.cpp rename to src/framework/internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.cpp index 446290690..e90481764 100644 --- a/framework/Ramsh/src/RamshCommunicationChannelConsoleSignalHandler.cpp +++ b/src/framework/internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommunicationChannelConsoleSignalHandler.h" +#include "internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h" -#include "Ramsh/RamshCommunicationChannelConsole.h" -#include "PlatformAbstraction/PlatformSignal.h" -#include "Utils/LogMacros.h" +#include "internal/Ramsh/RamshCommunicationChannelConsole.h" +#include "internal/PlatformAbstraction/PlatformSignal.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { static void handleSignalCallback(int sig) { @@ -40,7 +40,7 @@ namespace ramses_internal void RamshCommunicationChannelConsoleSignalHandler::remove(RamshCommunicationChannelConsole* console) { - std::vector::iterator i = find_c(m_consoles, console); + auto i = find_c(m_consoles, console); if (i != m_consoles.end()) { m_consoles.erase(i); @@ -49,7 +49,7 @@ namespace ramses_internal void RamshCommunicationChannelConsoleSignalHandler::handleSignal(int sig) { - ESignal enumSignal = static_cast(sig); + auto enumSignal = static_cast(sig); const auto signal = PlatformSignal::SignalToString(enumSignal); LOG_WARN_P(CONTEXT_RAMSH, "Received signal {}", signal); diff --git a/framework/Ramsh/include/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h b/src/framework/internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h similarity index 91% rename from framework/Ramsh/include/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h rename to src/framework/internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h index 21225274f..cd41aa2d4 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h +++ b/src/framework/internal/Ramsh/RamshCommunicationChannelConsoleSignalHandler.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMUNICATIONCHANNELCONSOLESIGNALHANDLER_H -#define RAMSES_RAMSHCOMMUNICATIONCHANNELCONSOLESIGNALHANDLER_H +#pragma once -#include "Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { class RamshCommunicationChannelConsole; @@ -47,5 +46,3 @@ namespace ramses_internal std::vector m_consoles; }; } - -#endif diff --git a/framework/Ramsh/src/RamshCommunicationChannelDLT.cpp b/src/framework/internal/Ramsh/RamshCommunicationChannelDLT.cpp similarity index 54% rename from framework/Ramsh/src/RamshCommunicationChannelDLT.cpp rename to src/framework/internal/Ramsh/RamshCommunicationChannelDLT.cpp index 6fb49ec05..7ed5fa8b3 100644 --- a/framework/Ramsh/src/RamshCommunicationChannelDLT.cpp +++ b/src/framework/internal/Ramsh/RamshCommunicationChannelDLT.cpp @@ -6,18 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshCommunicationChannelDLT.h" -#include "Ramsh/Ramsh.h" -#include "Ramsh/RamshTools.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" +#include "internal/Ramsh/RamshCommunicationChannelDLT.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Ramsh/RamshTools.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" #include -namespace ramses_internal +namespace ramses::internal { - RamshCommunicationChannelDLT* RamshCommunicationChannelDLT::m_instance = nullptr; - int RamshCommunicationChannelDLT::dltInjectionCallbackF(uint32_t sid, void* data, uint32_t length) { std::string incoming{static_cast(data), 0, length - 1};//use length to avoid unterminated strings copied to target buffer @@ -25,31 +23,36 @@ namespace ramses_internal LOG_DEBUG(CONTEXT_RAMSH, "Received dlt injection with service id " << sid << ", length is " << length); LOG_INFO(CONTEXT_RAMSH, "Calling command '" << incoming << "' received from dlt injection"); - if (RamshCommunicationChannelDLT::m_instance) - { - RamshCommunicationChannelDLT::m_instance->processInput(incoming); - } + RamshCommunicationChannelDLT::GetInstance().processInput(incoming); + return 0; } - RamshCommunicationChannelDLT::RamshCommunicationChannelDLT(Ramsh& ramsh) - : m_ramsh(ramsh) + RamshCommunicationChannelDLT::RamshCommunicationChannelDLT() { - m_instance = this; const uint32_t serviceId = 5000u; GetRamsesLogger().registerInjectionCallback(CONTEXT_RAMSH, serviceId, &RamshCommunicationChannelDLT::dltInjectionCallbackF); } - RamshCommunicationChannelDLT::~RamshCommunicationChannelDLT() + void RamshCommunicationChannelDLT::registerRamsh(Ramsh& ramsh) + { + // if used in parallel with multiple instances, the last registered ramsh is used + std::lock_guard guard(m_ramshLock); + m_ramsh = &ramsh; + } + + void RamshCommunicationChannelDLT::unregisterRamsh(Ramsh& ramsh) { - if (this == m_instance) - { - m_instance = nullptr; - } + // if used in parallel with multiple instances, the currently used ramsh might not be the one to unregister + std::lock_guard guard(m_ramshLock); + if (m_ramsh == &ramsh) + m_ramsh = nullptr; } void RamshCommunicationChannelDLT::processInput(const std::string& s) { - m_ramsh.execute(RamshTools::parseCommandString(s)); + std::lock_guard guard(m_ramshLock); + if (m_ramsh) + m_ramsh->execute(RamshTools::parseCommandString(s)); } } diff --git a/framework/Ramsh/include/Ramsh/RamshCommunicationChannelDLT.h b/src/framework/internal/Ramsh/RamshCommunicationChannelDLT.h similarity index 66% rename from framework/Ramsh/include/Ramsh/RamshCommunicationChannelDLT.h rename to src/framework/internal/Ramsh/RamshCommunicationChannelDLT.h index e4b4694fb..46d4346fb 100644 --- a/framework/Ramsh/include/Ramsh/RamshCommunicationChannelDLT.h +++ b/src/framework/internal/Ramsh/RamshCommunicationChannelDLT.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHCOMMUNICATIONCHANNELDLT_H -#define RAMSES_RAMSHCOMMUNICATIONCHANNELDLT_H +#pragma once #include #include +#include -namespace ramses_internal +namespace ramses::internal { class Ramsh; @@ -22,21 +22,23 @@ namespace ramses_internal class RamshCommunicationChannelDLT final { public: - explicit RamshCommunicationChannelDLT(Ramsh& ramsh); - ~RamshCommunicationChannelDLT(); + static RamshCommunicationChannelDLT& GetInstance() + { + static RamshCommunicationChannelDLT instance; + return instance; + } + + void registerRamsh(Ramsh& ramsh); + void unregisterRamsh(Ramsh& ramsh); private: + RamshCommunicationChannelDLT(); + static int dltInjectionCallbackF(uint32_t sid, void* data, uint32_t length); void processInput(const std::string& s); - Ramsh& m_ramsh; - - /** - * Static communication Channel instance for DLT - */ - static RamshCommunicationChannelDLT* m_instance; + std::mutex m_ramshLock; + Ramsh* m_ramsh = nullptr; }; } - -#endif diff --git a/framework/Ramsh/src/RamshStandardSetup.cpp b/src/framework/internal/Ramsh/RamshStandardSetup.cpp similarity index 58% rename from framework/Ramsh/src/RamshStandardSetup.cpp rename to src/framework/internal/Ramsh/RamshStandardSetup.cpp index 913b29944..e70c33d4e 100644 --- a/framework/Ramsh/src/RamshStandardSetup.cpp +++ b/src/framework/internal/Ramsh/RamshStandardSetup.cpp @@ -6,19 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshStandardSetup.h" -#include "Ramsh/RamshCommunicationChannelDLT.h" -#include "Ramsh/RamshCommunicationChannelConsole.h" +#include "internal/Ramsh/RamshStandardSetup.h" +#include "internal/Ramsh/RamshCommunicationChannelDLT.h" +#include "internal/Ramsh/RamshCommunicationChannelConsole.h" -namespace ramses_internal +namespace ramses::internal { - RamshStandardSetup::RamshStandardSetup(ramses::ERamsesShellType type, std::string prompt) + RamshStandardSetup::RamshStandardSetup(ERamsesShellType type, std::string prompt) : m_type(type) , m_prompt(std::move(prompt)) { } - RamshStandardSetup::~RamshStandardSetup() = default; + RamshStandardSetup::~RamshStandardSetup() + { + RamshCommunicationChannelDLT::GetInstance().unregisterRamsh(*this); + } bool RamshStandardSetup::start() { @@ -26,10 +29,10 @@ namespace ramses_internal return false; m_started = true; - if (m_type == ramses::ERamsesShellType::Console) + if (m_type == ERamsesShellType::Console) m_consoleChannel = RamshCommunicationChannelConsole::Construct(*this, m_prompt); - if (m_type == ramses::ERamsesShellType::Console || m_type == ramses::ERamsesShellType::Default) - m_dltChannel = std::make_unique(*this); + if (m_type == ERamsesShellType::Console || m_type == ERamsesShellType::Default) + RamshCommunicationChannelDLT::GetInstance().registerRamsh(*this); return true; } @@ -37,8 +40,8 @@ namespace ramses_internal { if (!m_started) return false; - m_dltChannel.reset(); m_consoleChannel.reset(); + RamshCommunicationChannelDLT::GetInstance().unregisterRamsh(*this); m_started = false; return true; } diff --git a/framework/Ramsh/include/Ramsh/RamshStandardSetup.h b/src/framework/internal/Ramsh/RamshStandardSetup.h similarity index 66% rename from framework/Ramsh/include/Ramsh/RamshStandardSetup.h rename to src/framework/internal/Ramsh/RamshStandardSetup.h index e10646bdb..f0a57d908 100644 --- a/framework/Ramsh/include/Ramsh/RamshStandardSetup.h +++ b/src/framework/internal/Ramsh/RamshStandardSetup.h @@ -6,36 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHSTANDARDSETUP_H -#define RAMSES_RAMSHSTANDARDSETUP_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "Ramsh/Ramsh.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/Ramsh/Ramsh.h" #include -namespace ramses_internal +namespace ramses::internal { class RamshCommunicationChannelConsole; - class RamshCommunicationChannelDLT; class RamshStandardSetup : public Ramsh { public: - explicit RamshStandardSetup(ramses::ERamsesShellType type, std::string prompt = "ramses"); + explicit RamshStandardSetup(ERamsesShellType type, std::string prompt = "ramses"); ~RamshStandardSetup() override; bool start(); bool stop(); private: - const ramses::ERamsesShellType m_type; + const ERamsesShellType m_type; const std::string m_prompt; bool m_started = false; std::unique_ptr m_consoleChannel; - std::unique_ptr m_dltChannel; }; } - -#endif diff --git a/framework/Ramsh/src/RamshTools.cpp b/src/framework/internal/Ramsh/RamshTools.cpp similarity index 93% rename from framework/Ramsh/src/RamshTools.cpp rename to src/framework/internal/Ramsh/RamshTools.cpp index 44efabb6c..d440d012e 100644 --- a/framework/Ramsh/src/RamshTools.cpp +++ b/src/framework/internal/Ramsh/RamshTools.cpp @@ -6,10 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/RamshTools.h" -#include +#include "internal/Ramsh/RamshTools.h" -namespace ramses_internal +#include + +namespace ramses::internal { size_t RamshTools::delimiterPosition(const std::string& msg, const std::string& delimiter) { @@ -22,10 +23,14 @@ namespace ramses_internal for(; pos != inputLength; pos++) { - if(delimiter.at(delimPos) == msg.at(pos)) + if (delimiter.at(delimPos) == msg.at(pos)) + { delimPos++; + } else + { delimPos = 0; + } } return delimPos; @@ -107,4 +112,4 @@ namespace ramses_internal return result; } -} // namespace ramses_internal +} // namespace ramses::internal diff --git a/framework/Ramsh/include/Ramsh/RamshTools.h b/src/framework/internal/Ramsh/RamshTools.h similarity index 91% rename from framework/Ramsh/include/Ramsh/RamshTools.h rename to src/framework/internal/Ramsh/RamshTools.h index 12f1c465e..228dcccc6 100644 --- a/framework/Ramsh/include/Ramsh/RamshTools.h +++ b/src/framework/internal/Ramsh/RamshTools.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHTOOLS_H -#define RAMSES_RAMSHTOOLS_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class RamshTools { @@ -24,5 +23,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/Ramsh/include/Ramsh/RamshTypeInfo.h b/src/framework/internal/Ramsh/RamshTypeInfo.h similarity index 92% rename from framework/Ramsh/include/Ramsh/RamshTypeInfo.h rename to src/framework/internal/Ramsh/RamshTypeInfo.h index b7e1265e4..e5d440b9b 100644 --- a/framework/Ramsh/include/Ramsh/RamshTypeInfo.h +++ b/src/framework/internal/Ramsh/RamshTypeInfo.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSHTYPEINFO_H -#define RAMSES_RAMSHTYPEINFO_H +#pragma once -namespace ramses_internal +namespace ramses::internal { using RamshTypeInfo = const void *; @@ -163,18 +162,4 @@ namespace ramses_internal return "char"; } }; - -#ifdef __APPLE__ - template<> - struct TypeName - { - inline explicit operator std::string() const - { - return "int"; - } - }; -#endif - } - -#endif diff --git a/framework/SceneGraph/Resource/include/Resource/ArrayResource.h b/src/framework/internal/SceneGraph/Resource/ArrayResource.h similarity index 73% rename from framework/SceneGraph/Resource/include/Resource/ArrayResource.h rename to src/framework/internal/SceneGraph/Resource/ArrayResource.h index bf701fa9f..a382f0426 100644 --- a/framework/SceneGraph/Resource/include/Resource/ArrayResource.h +++ b/src/framework/internal/SceneGraph/Resource/ArrayResource.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_ARRAYRESOURCE_H -#define RAMSES_INTERNAL_ARRAYRESOURCE_H +#pragma once #include "BufferResource.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include #include -namespace ramses_internal +namespace ramses::internal { class ArrayResource : public BufferResource { public: - ArrayResource(EResourceType arrayType, uint32_t elementCount, EDataType elementType, const void* arrayData, ResourceCacheFlag cacheFlag, std::string_view name) - : BufferResource(arrayType, elementCount * EnumToSize(elementType), arrayData, cacheFlag, name) + ArrayResource(EResourceType arrayType, uint32_t elementCount, EDataType elementType, const void* arrayData, std::string_view name) + : BufferResource(arrayType, elementCount * EnumToSize(elementType), arrayData, name) , m_elementCount(elementCount) , m_elementType(elementType) { @@ -42,7 +41,7 @@ namespace ramses_internal output << static_cast(getElementType()); } - static std::unique_ptr CreateResourceFromMetadataStream(IInputStream& input, ResourceCacheFlag cacheFlag, EResourceType arrayType, std::string_view name) + static std::unique_ptr CreateResourceFromMetadataStream(IInputStream& input, EResourceType arrayType, std::string_view name) { uint32_t elementCount = 0; uint32_t elementTypeAsUInt = 0; @@ -50,16 +49,14 @@ namespace ramses_internal input >> elementCount; input >> elementTypeAsUInt; - const EDataType indexElementType = static_cast(elementTypeAsUInt); + const auto indexElementType = static_cast(elementTypeAsUInt); // Data for resource will be filled later - return std::make_unique(arrayType, elementCount, indexElementType, nullptr, cacheFlag, name); + return std::make_unique(arrayType, elementCount, indexElementType, nullptr, name); } private: - uint32_t m_elementCount; + uint32_t m_elementCount; EDataType m_elementType; }; } - -#endif diff --git a/framework/SceneGraph/Resource/include/Resource/BufferResource.h b/src/framework/internal/SceneGraph/Resource/BufferResource.h similarity index 73% rename from framework/SceneGraph/Resource/include/Resource/BufferResource.h rename to src/framework/internal/SceneGraph/Resource/BufferResource.h index 090473b7e..4ccff7233 100644 --- a/framework/SceneGraph/Resource/include/Resource/BufferResource.h +++ b/src/framework/internal/SceneGraph/Resource/BufferResource.h @@ -6,25 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BUFFERRESOURCE_H -#define RAMSES_BUFFERRESOURCE_H +#pragma once -#include "Resource/ResourceBase.h" +#include "internal/SceneGraph/Resource/ResourceBase.h" -namespace ramses_internal +namespace ramses::internal { class BufferResource : public ResourceBase { public: - BufferResource(EResourceType typeID, uint32_t dataSize, const void* data, ResourceCacheFlag cacheFlag, std::string_view name) - : ResourceBase(typeID, cacheFlag, name) + BufferResource(EResourceType typeID, uint32_t dataSize, const void* data, std::string_view name) + : ResourceBase(typeID, name) { // TODO(tobias) this might create an empty resource blob that will be thrown away later. // needs more refactoring to solve properly - if (dataSize) - setResourceData(ResourceBlob(dataSize, static_cast(data))); + if (dataSize != 0u) + setResourceData(ResourceBlob(dataSize, static_cast(data))); } }; } - -#endif diff --git a/src/framework/internal/SceneGraph/Resource/EResourceCompressionStatus.h b/src/framework/internal/SceneGraph/Resource/EResourceCompressionStatus.h new file mode 100644 index 000000000..92af66573 --- /dev/null +++ b/src/framework/internal/SceneGraph/Resource/EResourceCompressionStatus.h @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/Utils/LoggingUtils.h" + +namespace ramses::internal +{ + enum class EResourceCompressionStatus //enum to support multiple compression formats in the future + { + Uncompressed = 0, + Compressed + }; + + const std::array EResourceCompressionStatusNames = + { + "Uncompressed", + "Compressed", + }; +} + +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EResourceCompressionStatus, + "EResourceCompressionStatus", + ramses::internal::EResourceCompressionStatusNames, + ramses::internal::EResourceCompressionStatus::Compressed); diff --git a/framework/SceneGraph/Resource/include/Resource/EffectInputInformation.h b/src/framework/internal/SceneGraph/Resource/EffectInputInformation.h similarity index 83% rename from framework/SceneGraph/Resource/include/Resource/EffectInputInformation.h rename to src/framework/internal/SceneGraph/Resource/EffectInputInformation.h index 86d250a78..eaa2e55c3 100644 --- a/framework/SceneGraph/Resource/include/Resource/EffectInputInformation.h +++ b/src/framework/internal/SceneGraph/Resource/EffectInputInformation.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECTINPUTINFORMATION_H -#define RAMSES_EFFECTINPUTINFORMATION_H +#pragma once -#include "Collections/Vector.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EFixedSemantics.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" #include #include -namespace ramses_internal +namespace ramses::internal { enum EEffectInputTextureType { @@ -45,28 +44,20 @@ namespace ramses_internal struct EffectInputInformation { - inline EffectInputInformation(); + EffectInputInformation() = default; inline EffectInputInformation(std::string_view inputName_, uint32_t elementCount_, EDataType dataType_, EFixedSemantics semantics_); inline friend bool operator==(const EffectInputInformation& a, const EffectInputInformation& b); inline friend bool operator!=(const EffectInputInformation& a, const EffectInputInformation& b); std::string inputName; - uint32_t elementCount; - EDataType dataType; - EFixedSemantics semantics; + uint32_t elementCount{1u}; + EDataType dataType{EDataType::Invalid}; + EFixedSemantics semantics{EFixedSemantics::Invalid}; }; using EffectInputInformationVector = std::vector; - - EffectInputInformation::EffectInputInformation() - : elementCount(1) - , dataType(EDataType::Invalid) - , semantics(EFixedSemantics::Invalid) - { - } - EffectInputInformation::EffectInputInformation(std::string_view inputName_, uint32_t elementCount_, EDataType dataType_, EFixedSemantics semantics_) : inputName(inputName_) , elementCount(elementCount_) @@ -88,5 +79,3 @@ namespace ramses_internal return !(a == b); } } - -#endif diff --git a/framework/SceneGraph/Resource/src/EffectResource.cpp b/src/framework/internal/SceneGraph/Resource/EffectResource.cpp similarity index 80% rename from framework/SceneGraph/Resource/src/EffectResource.cpp rename to src/framework/internal/SceneGraph/Resource/EffectResource.cpp index 74b647ffd..775008211 100644 --- a/framework/SceneGraph/Resource/src/EffectResource.cpp +++ b/src/framework/internal/SceneGraph/Resource/EffectResource.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Resource/EffectResource.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "Utils/BinaryOutputStream.h" -#include "Utils/BinaryInputStream.h" -#include "AppearanceEnumsImpl.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "impl/AppearanceEnumsImpl.h" -namespace ramses_internal +namespace ramses::internal { namespace { @@ -34,17 +34,17 @@ namespace ramses_internal } EffectResource::EffectResource(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader, - std::optional geometryShaderInputType, const EffectInputInformationVector& uniformInputs, - const EffectInputInformationVector& attributeInputs, std::string_view name, ResourceCacheFlag cacheFlag) - : ResourceBase(EResourceType_Effect, cacheFlag, name) - , m_uniformInputs(uniformInputs) - , m_attributeInputs(attributeInputs) + std::optional geometryShaderInputType, EffectInputInformationVector uniformInputs, + EffectInputInformationVector attributeInputs, std::string_view name) + : ResourceBase(EResourceType::Effect, name) + , m_uniformInputs(std::move(uniformInputs)) + , m_attributeInputs(std::move(attributeInputs)) , m_fragmentShaderOffset(static_cast(vertexShader.size() + 1)) , m_geometryShaderOffset(m_fragmentShaderOffset+static_cast(fragmentShader.size() + 1)) , m_geometryShaderInputType(geometryShaderInputType) { assert((geometryShader.empty() != geometryShaderInputType.has_value())); - const uint32_t dataLength = static_cast(vertexShader.size() + 1 + fragmentShader.size() + 1 + geometryShader.size() + 1); // including 3x terminating '0' + const auto dataLength = static_cast(vertexShader.size() + 1 + fragmentShader.size() + 1 + geometryShader.size() + 1); // including 3x terminating '0' ResourceBlob blob(dataLength); std::memcpy(blob.data(), vertexShader.c_str(), vertexShader.size() + 1); std::memcpy(blob.data() + m_fragmentShaderOffset, fragmentShader.c_str(), fragmentShader.size() + 1); @@ -52,11 +52,11 @@ namespace ramses_internal setResourceData(std::move(blob)); } - EffectResource::EffectResource(const EffectInputInformationVector& uniformInputs, const EffectInputInformationVector& attributeInputs, std::optional geometryShaderInputType, - std::string_view name, uint32_t fragmentShaderOffset, uint32_t geometryShaderOffset, ResourceCacheFlag cacheFlag) - : ResourceBase(EResourceType_Effect, cacheFlag, name) - , m_uniformInputs(uniformInputs) - , m_attributeInputs(attributeInputs) + EffectResource::EffectResource(EffectInputInformationVector&& uniformInputs, EffectInputInformationVector&& attributeInputs, std::optional geometryShaderInputType, + std::string_view name, uint32_t fragmentShaderOffset, uint32_t geometryShaderOffset) + : ResourceBase(EResourceType::Effect, name) + , m_uniformInputs(std::move(uniformInputs)) + , m_attributeInputs(std::move(attributeInputs)) , m_fragmentShaderOffset(fragmentShaderOffset) , m_geometryShaderOffset(geometryShaderOffset) , m_geometryShaderInputType(geometryShaderInputType) @@ -136,12 +136,12 @@ namespace ramses_internal else { constexpr auto maxValue = std::numeric_limits>::max(); - static_assert(ramses::DrawModeNames.size() < maxValue, "Last enum value is reserved"); + static_assert(DrawModeNames.size() < maxValue, "Last enum value is reserved"); output << maxValue; } } - std::unique_ptr EffectResource::CreateResourceFromMetadataStream(IInputStream& input, ResourceCacheFlag cacheFlag, std::string_view name) + std::unique_ptr EffectResource::CreateResourceFromMetadataStream(IInputStream& input, std::string_view name) { EffectInputInformationVector uniformInputs; ReadInputVector(input, uniformInputs); @@ -160,12 +160,12 @@ namespace ramses_internal { gsInputType = static_cast(gsInputTypeValue); } - return std::unique_ptr(new EffectResource(uniformInputs, attributeInputs, gsInputType, name, fragementShaderOffset, geometryShaderOffset, cacheFlag)); + return std::unique_ptr(new EffectResource(std::move(uniformInputs), std::move(attributeInputs), gsInputType, name, fragementShaderOffset, geometryShaderOffset)); } void EffectResource::WriteInputVector(IOutputStream& stream, const EffectInputInformationVector& inputVector) { - uint32_t length = static_cast(inputVector.size()); + const auto length = static_cast(inputVector.size()); stream << length; for (uint32_t i = 0; i < length; ++i) { @@ -176,14 +176,14 @@ namespace ramses_internal void EffectResource::ReadInputVector(IInputStream& stream, EffectInputInformationVector& inputVector) { - uint32_t length; + uint32_t length = 0; stream >> length; inputVector.reserve(length); for (uint32_t i = 0; i < length; ++i) { EffectInputInformation input; - uint32_t typeTmp; - uint32_t semanticTmp; + uint32_t typeTmp = 0; + uint32_t semanticTmp = 0; stream >> input.inputName >> input.elementCount >> typeTmp >> semanticTmp; input.dataType = static_cast(typeTmp); input.semantics = static_cast(semanticTmp); diff --git a/framework/SceneGraph/Resource/include/Resource/EffectResource.h b/src/framework/internal/SceneGraph/Resource/EffectResource.h similarity index 73% rename from framework/SceneGraph/Resource/include/Resource/EffectResource.h rename to src/framework/internal/SceneGraph/Resource/EffectResource.h index e10b2cbe5..28ef11aed 100644 --- a/framework/SceneGraph/Resource/include/Resource/EffectResource.h +++ b/src/framework/internal/SceneGraph/Resource/EffectResource.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECTRESOURCE_H -#define RAMSES_EFFECTRESOURCE_H +#pragma once -#include "Resource/ResourceBase.h" -#include "Resource/EffectInputInformation.h" +#include "internal/SceneGraph/Resource/ResourceBase.h" +#include "internal/SceneGraph/Resource/EffectInputInformation.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { class IInputStream; class IOutputStream; @@ -25,8 +24,8 @@ namespace ramses_internal { public: EffectResource(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader, - std::optional geometryShaderInputType, const EffectInputInformationVector& uniformInputs, - const EffectInputInformationVector& attributeInputs, std::string_view name, ResourceCacheFlag cacheFlag); + std::optional geometryShaderInputType, EffectInputInformationVector uniformInputs, + EffectInputInformationVector attributeInputs, std::string_view name); const char* getVertexShader() const; const char* getFragmentShader() const; @@ -41,11 +40,11 @@ namespace ramses_internal DataFieldHandle getAttributeDataFieldHandleByName(std::string_view name) const; void serializeResourceMetadataToStream(IOutputStream& output) const override; - static std::unique_ptr CreateResourceFromMetadataStream(IInputStream& input, ResourceCacheFlag cacheFlag, std::string_view name); + static std::unique_ptr CreateResourceFromMetadataStream(IInputStream& input, std::string_view name); private: - EffectResource(const EffectInputInformationVector& uniformInputs, const EffectInputInformationVector& attributeInputs, std::optional geometryShaderInputType, - std::string_view name, uint32_t fragmentShaderOffset, uint32_t geometryShaderOffset, ResourceCacheFlag cacheFlag); + EffectResource(EffectInputInformationVector&& uniformInputs, EffectInputInformationVector&& attributeInputs, std::optional geometryShaderInputType, + std::string_view name, uint32_t fragmentShaderOffset, uint32_t geometryShaderOffset); static void WriteInputVector(IOutputStream& stream, const EffectInputInformationVector& inputVector); static void ReadInputVector(IInputStream& stream, EffectInputInformationVector& inputVector); @@ -58,5 +57,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/SceneGraph/Resource/include/Resource/IResource.h b/src/framework/internal/SceneGraph/Resource/IResource.h similarity index 89% rename from framework/SceneGraph/Resource/include/Resource/IResource.h rename to src/framework/internal/SceneGraph/Resource/IResource.h index 2fc1590cf..23887b60f 100644 --- a/framework/SceneGraph/Resource/include/Resource/IResource.h +++ b/src/framework/internal/SceneGraph/Resource/IResource.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRESOURCE_H -#define RAMSES_IRESOURCE_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "Resource/ResourceTypes.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" #include -namespace ramses_internal +namespace ramses::internal { class IOutputStream; @@ -30,14 +29,14 @@ namespace ramses_internal Offline, }; - IResource() {} + IResource() = default; IResource(const IResource&) = delete; IResource(IResource&&) = delete; IResource& operator=(const IResource&) = delete; IResource& operator=(IResource&&) = delete; - virtual ~IResource(){}; + virtual ~IResource() = default; [[nodiscard]] virtual const ResourceBlob& getResourceData() const = 0; [[nodiscard]] virtual const CompressedResourceBlob& getCompressedResourceData() const = 0; [[nodiscard]] virtual uint32_t getDecompressedDataSize() const = 0; @@ -51,7 +50,6 @@ namespace ramses_internal virtual void decompress() const = 0; [[nodiscard]] virtual bool isCompressedAvailable() const = 0; [[nodiscard]] virtual bool isDeCompressedAvailable() const = 0; - [[nodiscard]] virtual ResourceCacheFlag getCacheFlag() const = 0; [[nodiscard]] virtual const std::string& getName() const = 0; virtual void serializeResourceMetadataToStream(IOutputStream& output) const = 0; @@ -69,5 +67,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/SceneGraph/Resource/src/LZ4CompressionUtils.cpp b/src/framework/internal/SceneGraph/Resource/LZ4CompressionUtils.cpp similarity index 94% rename from framework/SceneGraph/Resource/src/LZ4CompressionUtils.cpp rename to src/framework/internal/SceneGraph/Resource/LZ4CompressionUtils.cpp index 6b50c2bce..1643c0e77 100644 --- a/framework/SceneGraph/Resource/src/LZ4CompressionUtils.cpp +++ b/src/framework/internal/SceneGraph/Resource/LZ4CompressionUtils.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Resource/LZ4CompressionUtils.h" +#include "internal/SceneGraph/Resource/LZ4CompressionUtils.h" #include "lz4.h" #include "lz4hc.h" -namespace ramses_internal +namespace ramses::internal { namespace LZ4CompressionUtils { CompressedResourceBlob compress(const ResourceBlob& plainBuffer, CompressionLevel level) { const int plainSize = static_cast(plainBuffer.size()); - if (!plainSize) + if (plainSize == 0) return CompressedResourceBlob(); CompressedResourceBlob compressedBuffer(LZ4_compressBound(plainSize)); @@ -53,7 +53,7 @@ namespace ramses_internal ResourceBlob decompress(const CompressedResourceBlob& compressedData, uint32_t uncompressedSize) { - if (!compressedData.size() || !uncompressedSize) + if ((compressedData.size() == 0u) || (uncompressedSize == 0u)) return ResourceBlob(); ResourceBlob plainBuffer(uncompressedSize); diff --git a/framework/SceneGraph/Resource/include/Resource/LZ4CompressionUtils.h b/src/framework/internal/SceneGraph/Resource/LZ4CompressionUtils.h similarity index 82% rename from framework/SceneGraph/Resource/include/Resource/LZ4CompressionUtils.h rename to src/framework/internal/SceneGraph/Resource/LZ4CompressionUtils.h index 0129d791e..820ca115d 100644 --- a/framework/SceneGraph/Resource/include/Resource/LZ4CompressionUtils.h +++ b/src/framework/internal/SceneGraph/Resource/LZ4CompressionUtils.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LZ4COMPRESSIONUTILS_H -#define RAMSES_LZ4COMPRESSIONUTILS_H +#pragma once -#include "Collections/HeapArray.h" -#include "Resource/ResourceTypes.h" +#include "internal/PlatformAbstraction/Collections/HeapArray.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" -namespace ramses_internal +namespace ramses::internal { namespace LZ4CompressionUtils { @@ -26,4 +25,4 @@ namespace ramses_internal ResourceBlob decompress(const CompressedResourceBlob& compressedData, uint32_t uncompressedSize); } } -#endif + diff --git a/framework/SceneGraph/Resource/src/ResourceBase.cpp b/src/framework/internal/SceneGraph/Resource/ResourceBase.cpp similarity index 94% rename from framework/SceneGraph/Resource/src/ResourceBase.cpp rename to src/framework/internal/SceneGraph/Resource/ResourceBase.cpp index cc2f7068c..46cece3c0 100644 --- a/framework/SceneGraph/Resource/src/ResourceBase.cpp +++ b/src/framework/internal/SceneGraph/Resource/ResourceBase.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Resource/ResourceBase.h" -#include "Resource/LZ4CompressionUtils.h" -#include "Utils/BinaryOutputStream.h" +#include "internal/SceneGraph/Resource/ResourceBase.h" +#include "internal/SceneGraph/Resource/LZ4CompressionUtils.h" +#include "internal/Core/Utils/BinaryOutputStream.h" #include -namespace ramses_internal +namespace ramses::internal { void ResourceBase::updateHash() const { diff --git a/framework/SceneGraph/Resource/include/Resource/ResourceBase.h b/src/framework/internal/SceneGraph/Resource/ResourceBase.h similarity index 90% rename from framework/SceneGraph/Resource/include/Resource/ResourceBase.h rename to src/framework/internal/SceneGraph/Resource/ResourceBase.h index c369168a9..5c5fa9c78 100644 --- a/framework/SceneGraph/Resource/include/Resource/ResourceBase.h +++ b/src/framework/internal/SceneGraph/Resource/ResourceBase.h @@ -6,24 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEBASE_H -#define RAMSES_RESOURCEBASE_H +#pragma once #include "IResource.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { class ResourceBase : public IResource { public: - explicit ResourceBase(EResourceType typeID, ResourceCacheFlag cacheFlag, std::string_view name) + explicit ResourceBase(EResourceType typeID, std::string_view name) : m_typeID(typeID) - , m_cacheFlag(cacheFlag) , m_name(name) { } @@ -113,11 +111,6 @@ namespace ramses_internal return m_data.data() != nullptr; } - ResourceCacheFlag getCacheFlag() const final override - { - return m_cacheFlag; - } - const std::string& getName() const final override { return m_name; @@ -138,10 +131,7 @@ namespace ramses_internal mutable CompressionLevel m_currentCompression = CompressionLevel::None; mutable ResourceContentHash m_hash; uint32_t m_uncompressedSize = 0; - ResourceCacheFlag m_cacheFlag; std::string m_name; mutable std::mutex m_compressionLock; }; } - -#endif diff --git a/framework/SceneGraph/Resource/include/Resource/ResourceInfo.h b/src/framework/internal/SceneGraph/Resource/ResourceInfo.h similarity index 80% rename from framework/SceneGraph/Resource/include/Resource/ResourceInfo.h rename to src/framework/internal/SceneGraph/Resource/ResourceInfo.h index c812ecc39..8b15b6179 100644 --- a/framework/SceneGraph/Resource/include/Resource/ResourceInfo.h +++ b/src/framework/internal/SceneGraph/Resource/ResourceInfo.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEINFO_H -#define RAMSES_RESOURCEINFO_H +#pragma once -#include "Resource/ResourceTypes.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" #include "IResource.h" -namespace ramses_internal +namespace ramses::internal { struct ResourceInfo { - EResourceType type; + EResourceType type{EResourceType::Invalid}; ResourceContentHash hash; - uint32_t decompressedSize; - uint32_t compressedSize; + uint32_t decompressedSize{0u}; + uint32_t compressedSize{0u}; ResourceInfo(const EResourceType type_, const ResourceContentHash& hash_, const uint32_t decompressedSize_, const uint32_t compressedSize_) : type(type_) @@ -30,12 +29,7 @@ namespace ramses_internal { } - ResourceInfo() - : type(EResourceType_Invalid) - , decompressedSize(0) - , compressedSize(0) - { - } + ResourceInfo() = default; explicit ResourceInfo(const IResource* resourceToGetDataFrom) : type(resourceToGetDataFrom->getTypeID()) @@ -61,5 +55,3 @@ namespace ramses_internal using ResourceInfoVector = std::vector; } - -#endif diff --git a/src/framework/internal/SceneGraph/Resource/ResourceTypes.h b/src/framework/internal/SceneGraph/Resource/ResourceTypes.h new file mode 100644 index 000000000..0908014ea --- /dev/null +++ b/src/framework/internal/SceneGraph/Resource/ResourceTypes.h @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/PlatformAbstraction/Collections/HeapArray.h" +#include "internal/Core/Common/StronglyTypedValue.h" +#include "internal/Core/Utils/LoggingUtils.h" + +namespace ramses::internal +{ + using ResourceBlob = HeapArray; + using CompressedResourceBlob = HeapArray; + + enum class EResourceType + { + Invalid, + VertexArray, + IndexArray, + Texture2D, + Texture3D, + TextureCube, + Effect, + }; + + const std::array EResourceTypeNames = + { + "Invalid", + "VertexArray", + "IndexArray", + "Texture2D", + "Texture3D", + "TextureCube", + "Effect" + }; + + const size_t EResourceType_NUMBER_OF_ELEMENTS = EResourceTypeNames.size(); + static_assert(EnumTraits::VerifyElementCountIfSupported(EResourceType_NUMBER_OF_ELEMENTS)); + + ENUM_TO_STRING(EResourceType, EResourceTypeNames, EResourceType::Effect); +} + +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EResourceType, + "EResourceType", + ramses::internal::EResourceTypeNames, + ramses::internal::EResourceType::Effect); diff --git a/framework/SceneGraph/Resource/include/Resource/TextureMetaInfo.h b/src/framework/internal/SceneGraph/Resource/TextureMetaInfo.h similarity index 65% rename from framework/SceneGraph/Resource/include/Resource/TextureMetaInfo.h rename to src/framework/internal/SceneGraph/Resource/TextureMetaInfo.h index 62dd68ea5..2059781d7 100644 --- a/framework/SceneGraph/Resource/include/Resource/TextureMetaInfo.h +++ b/src/framework/internal/SceneGraph/Resource/TextureMetaInfo.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_TEXTUREMETAINFO_H -#define RAMSES_FRAMEWORK_TEXTUREMETAINFO_H +#pragma once +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" + +#include #include -#include "PlatformAbstraction/PlatformTypes.h" -#include "Collections/Vector.h" -#include "SceneAPI/TextureEnums.h" -#include "Collections/Pair.h" #include -namespace ramses_internal +namespace ramses::internal { using MipDataSizeVector = std::vector; using TextureSwizzleArray = std::array; @@ -24,25 +24,23 @@ namespace ramses_internal struct TextureMetaInfo { - explicit TextureMetaInfo(uint32_t width = 0u, uint32_t height = 0u, uint32_t depth = 0u, ETextureFormat format = ETextureFormat::Invalid, bool generateMipChain = false, const TextureSwizzleArray swizzle = {}, const MipDataSizeVector& mipDataSizes = {}) + explicit TextureMetaInfo(uint32_t width = 0u, uint32_t height = 0u, uint32_t depth = 0u, EPixelStorageFormat format = EPixelStorageFormat::Invalid, bool generateMipChain = false, const TextureSwizzleArray swizzle = {}, MipDataSizeVector mipDataSizes = {}) : m_width(width) , m_height(height) , m_depth(depth) , m_format(format) , m_generateMipChain(generateMipChain) - , m_dataSizes(mipDataSizes) + , m_dataSizes(std::move(mipDataSizes)) , m_swizzle(swizzle) { } - uint32_t m_width; - uint32_t m_height; - uint32_t m_depth; - ETextureFormat m_format; + uint32_t m_width; + uint32_t m_height; + uint32_t m_depth; + EPixelStorageFormat m_format; bool m_generateMipChain; MipDataSizeVector m_dataSizes; TextureSwizzleArray m_swizzle; }; } - -#endif diff --git a/framework/SceneGraph/Resource/include/Resource/TextureResource.h b/src/framework/internal/SceneGraph/Resource/TextureResource.h similarity index 79% rename from framework/SceneGraph/Resource/include/Resource/TextureResource.h rename to src/framework/internal/SceneGraph/Resource/TextureResource.h index 71e712945..f3eab2a23 100644 --- a/framework/SceneGraph/Resource/include/Resource/TextureResource.h +++ b/src/framework/internal/SceneGraph/Resource/TextureResource.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURERESOURCE_H -#define RAMSES_TEXTURERESOURCE_H +#pragma once -#include "Resource/TextureMetaInfo.h" -#include "Resource/BufferResource.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" +#include "internal/SceneGraph/Resource/BufferResource.h" #include #include -namespace ramses_internal +namespace ramses::internal { class TextureResource : public BufferResource { public: - TextureResource(EResourceType typeID, const TextureMetaInfo& texDesc, ResourceCacheFlag cacheFlag, std::string_view name) - : BufferResource(typeID, GetTotalDataSizeFromMipSizes(texDesc.m_dataSizes, typeID), nullptr, cacheFlag, name) + TextureResource(EResourceType typeID, const TextureMetaInfo& texDesc, std::string_view name) + : BufferResource(typeID, GetTotalDataSizeFromMipSizes(texDesc.m_dataSizes, typeID), nullptr, name) , m_width(texDesc.m_width) , m_height(texDesc.m_height) , m_depth(texDesc.m_depth) @@ -36,10 +35,6 @@ namespace ramses_internal assert((texDesc.m_dataSizes.size() == 1) || !texDesc.m_generateMipChain); }; - ~TextureResource() override - { - }; - uint32_t getWidth() const { return m_width; @@ -60,7 +55,7 @@ namespace ramses_internal return m_mipDataSizes; } - ETextureFormat getTextureFormat() const + EPixelStorageFormat getTextureFormat() const { return m_format; } @@ -79,16 +74,16 @@ namespace ramses_internal { switch (getTypeID()) { - case EResourceType_Texture2D: + case EResourceType::Texture2D: output << m_width; output << m_height; break; - case EResourceType_Texture3D: + case EResourceType::Texture3D: output << m_width; output << m_height; output << m_depth; break; - case EResourceType_TextureCube: + case EResourceType::TextureCube: output << m_width; break; default: @@ -108,7 +103,7 @@ namespace ramses_internal output << m_generateMipChain; } - static std::unique_ptr CreateResourceFromMetadataStream(IInputStream& input, EResourceType typeID, ResourceCacheFlag cacheFlag, std::string_view name) + static std::unique_ptr CreateResourceFromMetadataStream(IInputStream& input, EResourceType typeID, std::string_view name) { TextureMetaInfo texDesc(1u, 1u, 1u); uint32_t texelFormat = 0; @@ -117,16 +112,16 @@ namespace ramses_internal switch (typeID) { - case EResourceType_Texture2D: + case EResourceType::Texture2D: input >> texDesc.m_width; input >> texDesc.m_height; break; - case EResourceType_Texture3D: + case EResourceType::Texture3D: input >> texDesc.m_width; input >> texDesc.m_height; input >> texDesc.m_depth; break; - case EResourceType_TextureCube: + case EResourceType::TextureCube: input >> texDesc.m_width; break; default: @@ -134,7 +129,7 @@ namespace ramses_internal } input >> texelFormat; - texDesc.m_format = static_cast(texelFormat); + texDesc.m_format = static_cast(texelFormat); static_assert(texDesc.m_swizzle.size() == 4, "Wrong size of texture swizzle array"); for (uint32_t ii = 0; ii < 4; ++ii) { @@ -150,24 +145,22 @@ namespace ramses_internal input >> texDesc.m_generateMipChain; - return std::make_unique(typeID, texDesc, cacheFlag, name); + return std::make_unique(typeID, texDesc, name); } private: static uint32_t GetTotalDataSizeFromMipSizes(const MipDataSizeVector& mipSizes, EResourceType typeID) { const uint32_t totalMipChainDataSize = std::accumulate(mipSizes.begin(), mipSizes.end(), 0u); - return (typeID == EResourceType_TextureCube ? 6u * totalMipChainDataSize : totalMipChainDataSize); + return (typeID == EResourceType::TextureCube ? 6u * totalMipChainDataSize : totalMipChainDataSize); } - uint32_t m_width; - uint32_t m_height; - uint32_t m_depth; + uint32_t m_width; + uint32_t m_height; + uint32_t m_depth; MipDataSizeVector m_mipDataSizes; - ETextureFormat m_format; + EPixelStorageFormat m_format; TextureSwizzleArray m_swizzle; bool m_generateMipChain; }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/ActionCollectingScene.cpp b/src/framework/internal/SceneGraph/Scene/ActionCollectingScene.cpp similarity index 97% rename from framework/SceneGraph/Scene/src/ActionCollectingScene.cpp rename to src/framework/internal/SceneGraph/Scene/ActionCollectingScene.cpp index 8faac049b..7d316923e 100644 --- a/framework/SceneGraph/Scene/src/ActionCollectingScene.cpp +++ b/src/framework/internal/SceneGraph/Scene/ActionCollectingScene.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/ActionCollectingScene.h" +#include "internal/SceneGraph/Scene/ActionCollectingScene.h" -namespace ramses_internal +namespace ramses::internal { ActionCollectingScene::ActionCollectingScene(const SceneInfo& sceneInfo) : ResourceChangeCollectingScene(sceneInfo) @@ -77,6 +77,12 @@ namespace ramses_internal m_creator.setDataVector2iArray(containerHandle, field, elementCount, data); } + void ActionCollectingScene::setDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) + { + ResourceChangeCollectingScene::setDataBooleanArray(containerHandle, field, elementCount, data); + m_creator.setDataBooleanArray(containerHandle, field, elementCount, data); + } + void ActionCollectingScene::setDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) { ResourceChangeCollectingScene::setDataIntegerArray(containerHandle, field, elementCount, data); @@ -415,7 +421,7 @@ namespace ramses_internal m_creator.removeRenderGroupFromRenderGroup(groupHandleParent, groupHandleChild); } - ramses_internal::RenderPassHandle ActionCollectingScene::allocateRenderPass(uint32_t renderGroupCount, RenderPassHandle handle /*= InvalidRenderPassHandle*/) + RenderPassHandle ActionCollectingScene::allocateRenderPass(uint32_t renderGroupCount, RenderPassHandle handle /*= InvalidRenderPassHandle*/) { const RenderPassHandle handleActual = ResourceChangeCollectingScene::allocateRenderPass(renderGroupCount, handle); m_creator.allocateRenderPass(renderGroupCount, handleActual); @@ -609,7 +615,7 @@ namespace ramses_internal m_creator.setRenderPassClearColor(pass, clearColor); } - void ActionCollectingScene::setRenderPassClearFlag(RenderPassHandle pass, uint32_t clearFlag) + void ActionCollectingScene::setRenderPassClearFlag(RenderPassHandle pass, ClearFlags clearFlag) { ResourceChangeCollectingScene::setRenderPassClearFlag(pass, clearFlag); m_creator.setRenderPassClearFlag(pass, clearFlag); @@ -629,13 +635,13 @@ namespace ramses_internal m_creator.releaseDataBuffer(handle); } - void ActionCollectingScene::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) + void ActionCollectingScene::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) { ResourceChangeCollectingScene::updateDataBuffer(handle, offsetInBytes, dataSizeInBytes, data); m_creator.updateDataBuffer(handle, offsetInBytes, dataSizeInBytes, data); } - TextureBufferHandle ActionCollectingScene::allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) + TextureBufferHandle ActionCollectingScene::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) { const TextureBufferHandle allocatedHandle = ResourceChangeCollectingScene::allocateTextureBuffer(textureFormat, mipMapDimensions, handle); m_creator.allocateTextureBuffer(textureFormat, mipMapDimensions, allocatedHandle); @@ -649,7 +655,7 @@ namespace ramses_internal m_creator.releaseTextureBuffer(handle); } - void ActionCollectingScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) + void ActionCollectingScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) { ResourceChangeCollectingScene::updateTextureBuffer(handle, mipLevel, x, y, width, height, data); const uint32_t dataSize = width * height * GetTexelSizeFromFormat(getTextureBuffer(handle).textureFormat); diff --git a/framework/SceneGraph/Scene/include/Scene/ActionCollectingScene.h b/src/framework/internal/SceneGraph/Scene/ActionCollectingScene.h similarity index 86% rename from framework/SceneGraph/Scene/include/Scene/ActionCollectingScene.h rename to src/framework/internal/SceneGraph/Scene/ActionCollectingScene.h index 523068818..005caa36b 100644 --- a/framework/SceneGraph/Scene/include/Scene/ActionCollectingScene.h +++ b/src/framework/internal/SceneGraph/Scene/ActionCollectingScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ACTIONCOLLECTINGSCENE_H -#define RAMSES_ACTIONCOLLECTINGSCENE_H +#pragma once -#include "Scene/ResourceChangeCollectingScene.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "SceneReferencing/SceneReferenceAction.h" +#include "internal/SceneGraph/Scene/ResourceChangeCollectingScene.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" -namespace ramses_internal +namespace ramses::internal { class ActionCollectingScene : public ResourceChangeCollectingScene { @@ -23,7 +22,7 @@ namespace ramses_internal void preallocateSceneSize (const SceneSizeInformation& sizeInfo) override; // Renderable allocation - RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle = RenderableHandle::Invalid()) override; + RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle) override; void releaseRenderable (RenderableHandle renderableHandle) override; // Renderable data (stuff required for rendering) @@ -34,10 +33,10 @@ namespace ramses_internal void setRenderableRenderState (RenderableHandle renderableHandle, RenderStateHandle stateHandle) override; void setRenderableInstanceCount (RenderableHandle renderableHandle, uint32_t instanceCount) override; void setRenderableStartVertex (RenderableHandle renderableHandle, uint32_t startVertex) override; - void setRenderableUniformsDataInstanceAndState (RenderableHandle renderableHandle, DataInstanceHandle newDataInstance, RenderStateHandle stateHandle); + void setRenderableUniformsDataInstanceAndState (RenderableHandle renderableHandle, DataInstanceHandle newDataInstance, RenderStateHandle stateHandle); // Render state - RenderStateHandle allocateRenderState (RenderStateHandle stateHandle = RenderStateHandle::Invalid()) override; + RenderStateHandle allocateRenderState (RenderStateHandle stateHandle) override; void releaseRenderState (RenderStateHandle stateHandle) override; void setRenderStateBlendFactors (RenderStateHandle stateHandle, EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) override; void setRenderStateBlendOperations (RenderStateHandle stateHandle, EBlendOperation operationColor, EBlendOperation operationAlpha) override; @@ -52,14 +51,14 @@ namespace ramses_internal void setRenderStateColorWriteMask (RenderStateHandle stateHandle, ColorWriteMask colorMask) override; // Camera - CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle = CameraHandle::Invalid()) override; + CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle) override; void releaseCamera (CameraHandle cameraHandle) override; // Creation/Deletion - NodeHandle allocateNode (uint32_t childrenCount = 0u, NodeHandle handle = NodeHandle::Invalid()) override; + NodeHandle allocateNode (uint32_t childrenCount, NodeHandle handle) override; void releaseNode (NodeHandle nodeHandle) override; - TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle = TransformHandle::Invalid()) override; + TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle) override; void releaseTransform (TransformHandle transform) override; // Parent-child relationship @@ -72,16 +71,17 @@ namespace ramses_internal void setScaling (TransformHandle handle, const glm::vec3& scaling) override; - DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle = DataLayoutHandle::Invalid()) override; + DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle) override; void releaseDataLayout (DataLayoutHandle layoutHandle) override; - DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle = DataInstanceHandle::Invalid()) override; + DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle) override; void releaseDataInstance (DataInstanceHandle containerHandle) override; void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) override; void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) override; void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) override; void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) override; + void setDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) override; void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) override; void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) override; void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) override; @@ -94,21 +94,21 @@ namespace ramses_internal void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) override; // Texture sampler description - TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle = TextureSamplerHandle::Invalid()) override; + TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle) override; void releaseTextureSampler (TextureSamplerHandle handle) override; // Render groups - RenderGroupHandle allocateRenderGroup (uint32_t renderableCount = 0u, uint32_t nestedGroupCount = 0u, RenderGroupHandle groupHandle = RenderGroupHandle::Invalid()) override; + RenderGroupHandle allocateRenderGroup (uint32_t renderableCount, uint32_t nestedGroupCount, RenderGroupHandle groupHandle) override; void releaseRenderGroup (RenderGroupHandle groupHandle) override; void addRenderableToRenderGroup (RenderGroupHandle groupHandle, RenderableHandle renderableHandle, int32_t order) override; void removeRenderableFromRenderGroup (RenderGroupHandle groupHandle, RenderableHandle renderableHandle) override; void addRenderGroupToRenderGroup (RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild, int32_t order) override; - void removeRenderGroupFromRenderGroup(RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild) override; + void removeRenderGroupFromRenderGroup(RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild) override; - RenderPassHandle allocateRenderPass (uint32_t renderGroupCount = 0u, RenderPassHandle handle = RenderPassHandle::Invalid()) override; + RenderPassHandle allocateRenderPass (uint32_t renderGroupCount, RenderPassHandle handle) override; void releaseRenderPass (RenderPassHandle handle) override; void setRenderPassClearColor (RenderPassHandle passHandle, const glm::vec4& clearColor) override; - void setRenderPassClearFlag (RenderPassHandle passHandle, uint32_t clearFlag) override; + void setRenderPassClearFlag (RenderPassHandle passHandle, ClearFlags clearFlag) override; void setRenderPassCamera (RenderPassHandle passHandle, CameraHandle cameraHandle) override; void setRenderPassRenderTarget (RenderPassHandle passHandle, RenderTargetHandle targetHandle) override; void setRenderPassRenderOrder (RenderPassHandle passHandle, int32_t renderOrder) override; @@ -118,41 +118,41 @@ namespace ramses_internal void addRenderGroupToRenderPass (RenderPassHandle passHandle, RenderGroupHandle groupHandle, int32_t order) override; void removeRenderGroupFromRenderPass (RenderPassHandle passHandle, RenderGroupHandle groupHandle) override; - BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) override; + BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) override; void releaseBlitPass (BlitPassHandle passHandle) override; void setBlitPassRenderOrder (BlitPassHandle passHandle, int32_t renderOrder) override; void setBlitPassEnabled (BlitPassHandle passHandle, bool isEnabled) override; void setBlitPassRegions (BlitPassHandle passHandle, const PixelRectangle& sourceRegion, const PixelRectangle& destinationRegion) override; - PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle = PickableObjectHandle::Invalid()) override; + PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle) override; void releasePickableObject (PickableObjectHandle pickableHandle) override; void setPickableObjectId (PickableObjectHandle pickableHandle, PickableObjectId id) override; void setPickableObjectCamera (PickableObjectHandle pickableHandle, CameraHandle cameraHandle) override; void setPickableObjectEnabled (PickableObjectHandle pickableHandle, bool isEnabled) override; - DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle) override; void setDataSlotTexture (DataSlotHandle handle, const ResourceContentHash& texture) override; void releaseDataSlot (DataSlotHandle handle) override; // Render targets - RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle = RenderTargetHandle::Invalid()) override; + RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle) override; void releaseRenderTarget (RenderTargetHandle targetHandle) override; // Render buffers - RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle = RenderBufferHandle::Invalid()) override; + RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle) override; void releaseRenderBuffer (RenderBufferHandle handle) override; void addRenderTargetRenderBuffer (RenderTargetHandle targetHandle, RenderBufferHandle bufferHandle) override; // Data buffers - DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle = DataBufferHandle::Invalid()) override; + DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle) override; void releaseDataBuffer (DataBufferHandle handle) override; - void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) override; + void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) override; - TextureBufferHandle allocateTextureBuffer (ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()) override; + TextureBufferHandle allocateTextureBuffer (EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) override; void releaseTextureBuffer (TextureBufferHandle handle) override; - void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) override; + void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) override; - SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle = {}) override; + SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle) override; void releaseSceneReference (SceneReferenceHandle handle) override; void requestSceneReferenceState (SceneReferenceHandle handle, RendererSceneState state) override; void requestSceneReferenceFlushNotifications(SceneReferenceHandle handle, bool enable) override; @@ -173,5 +173,3 @@ namespace ramses_internal SceneReferenceActionVector m_sceneReferenceActions; }; } - -#endif diff --git a/framework/SceneGraph/Scene/include/Scene/ClientScene.h b/src/framework/internal/SceneGraph/Scene/ClientScene.h similarity index 82% rename from framework/SceneGraph/Scene/include/Scene/ClientScene.h rename to src/framework/internal/SceneGraph/Scene/ClientScene.h index 77dc43517..a7ca48739 100644 --- a/framework/SceneGraph/Scene/include/Scene/ClientScene.h +++ b/src/framework/internal/SceneGraph/Scene/ClientScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTSCENE_H -#define RAMSES_CLIENTSCENE_H +#pragma once -#include "Scene/DataLayoutCachedScene.h" -#include "SceneReferencing/SceneReferenceAction.h" -#include "Utils/StatisticCollection.h" +#include "internal/SceneGraph/Scene/DataLayoutCachedScene.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" +#include "internal/Core/Utils/StatisticCollection.h" -namespace ramses_internal +namespace ramses::internal { // The client scene is just a wrapper for concrete implementation of a low level scene // together with some additional data used in client side logic @@ -34,5 +33,3 @@ namespace ramses_internal StatisticCollectionScene m_statisticCollection; }; } - -#endif diff --git a/framework/SceneGraph/Scene/include/Scene/DataInstance.h b/src/framework/internal/SceneGraph/Scene/DataInstance.h similarity index 89% rename from framework/SceneGraph/Scene/include/Scene/DataInstance.h rename to src/framework/internal/SceneGraph/Scene/DataInstance.h index 3b6e70c8c..05d63516e 100644 --- a/framework/SceneGraph/Scene/include/Scene/DataInstance.h +++ b/src/framework/internal/SceneGraph/Scene/DataInstance.h @@ -6,20 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAINSTANCE_H -#define RAMSES_DATAINSTANCE_H +#pragma once -#include "Scene/DataLayout.h" -#include "Utils/AssertMovable.h" +#include "internal/SceneGraph/Scene/DataLayout.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +namespace ramses::internal { class DataInstance { public: - DataInstance() - { - } + DataInstance() = default; DataInstance(DataLayoutHandle dataLayoutHandle, uint32_t size) : m_dataLayoutHandle(dataLayoutHandle) @@ -58,10 +55,8 @@ namespace ramses_internal private: DataLayoutHandle m_dataLayoutHandle; - std::vector m_data; + std::vector m_data; }; ASSERT_MOVABLE(DataInstance) } - -#endif diff --git a/framework/SceneGraph/Scene/include/Scene/DataLayout.h b/src/framework/internal/SceneGraph/Scene/DataLayout.h similarity index 89% rename from framework/SceneGraph/Scene/include/Scene/DataLayout.h rename to src/framework/internal/SceneGraph/Scene/DataLayout.h index b97d876d9..d4a2102c4 100644 --- a/framework/SceneGraph/Scene/include/Scene/DataLayout.h +++ b/src/framework/internal/SceneGraph/Scene/DataLayout.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATALAYOUT_H -#define RAMSES_DATALAYOUT_H +#pragma once -#include "SceneAPI/DataFieldInfo.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/DataFieldInfo.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { class DataLayout { @@ -26,7 +25,7 @@ namespace ramses_internal m_fieldOffsets.reserve(fields.size()); for (const auto& field : fields) { - const uint32_t alignmentRequirement = static_cast(EnumToAlignment(field.dataType)); + const auto alignmentRequirement = static_cast(EnumToAlignment(field.dataType)); if (m_totalSize % alignmentRequirement != 0) m_totalSize += alignmentRequirement - (m_totalSize % alignmentRequirement); @@ -79,5 +78,3 @@ namespace ramses_internal ResourceContentHash effectHash; }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/DataLayoutCachedScene.cpp b/src/framework/internal/SceneGraph/Scene/DataLayoutCachedScene.cpp similarity index 97% rename from framework/SceneGraph/Scene/src/DataLayoutCachedScene.cpp rename to src/framework/internal/SceneGraph/Scene/DataLayoutCachedScene.cpp index 1a1fb4a34..745e6f967 100644 --- a/framework/SceneGraph/Scene/src/DataLayoutCachedScene.cpp +++ b/src/framework/internal/SceneGraph/Scene/DataLayoutCachedScene.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/DataLayoutCachedScene.h" +#include "internal/SceneGraph/Scene/DataLayoutCachedScene.h" -namespace ramses_internal +namespace ramses::internal { DataLayoutCachedScene::DataLayoutCachedScene(const SceneInfo& sceneInfo) : ActionCollectingScene(sceneInfo) diff --git a/framework/SceneGraph/Scene/include/Scene/DataLayoutCachedScene.h b/src/framework/internal/SceneGraph/Scene/DataLayoutCachedScene.h similarity index 80% rename from framework/SceneGraph/Scene/include/Scene/DataLayoutCachedScene.h rename to src/framework/internal/SceneGraph/Scene/DataLayoutCachedScene.h index 070939123..4812c8ec6 100644 --- a/framework/SceneGraph/Scene/include/Scene/DataLayoutCachedScene.h +++ b/src/framework/internal/SceneGraph/Scene/DataLayoutCachedScene.h @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATALAYOUTCACHEDSCENE_H -#define RAMSES_DATALAYOUTCACHEDSCENE_H +#pragma once -#include "Scene/ActionCollectingScene.h" -#include "Collections/Vector.h" -#include "Collections/HashMap.h" +#include "internal/SceneGraph/Scene/ActionCollectingScene.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" -namespace ramses_internal +namespace ramses::internal { class DataLayoutCachedScene : public ActionCollectingScene { public: explicit DataLayoutCachedScene(const SceneInfo& sceneInfo = SceneInfo()); - DataLayoutHandle allocateDataLayout(const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle = DataLayoutHandle::Invalid()) override; + DataLayoutHandle allocateDataLayout(const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle) override; void releaseDataLayout(DataLayoutHandle handle) override; - [[nodiscard]] uint32_t getNumDataLayoutReferences(DataLayoutHandle handle) const; + [[nodiscard]] uint32_t getNumDataLayoutReferences(DataLayoutHandle handle) const; private: struct DataLayoutCacheEntry @@ -41,5 +40,3 @@ namespace ramses_internal std::vector m_dataLayoutCache; }; } - -#endif diff --git a/framework/SceneGraph/Scene/include/Scene/ESceneActionId.h b/src/framework/internal/SceneGraph/Scene/ESceneActionId.h similarity index 98% rename from framework/SceneGraph/Scene/include/Scene/ESceneActionId.h rename to src/framework/internal/SceneGraph/Scene/ESceneActionId.h index d15af92f1..839360990 100644 --- a/framework/SceneGraph/Scene/include/Scene/ESceneActionId.h +++ b/src/framework/internal/SceneGraph/Scene/ESceneActionId.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ESCENEACTIONID_H -#define RAMSES_ESCENEACTIONID_H +#pragma once -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "Common/BitForgeMacro.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/Core/Common/BitForgeMacro.h" -namespace ramses_internal +namespace ramses::internal { enum class ESceneActionId : uint32_t { @@ -33,6 +32,7 @@ namespace ramses_internal ReleaseDataInstance, AllocateDataLayout, ReleaseDataLayout, + SetDataBooleanArray, SetDataIntegerArray, SetDataFloatArray, SetDataVector2fArray, @@ -222,6 +222,7 @@ case ENUMVALUE: return #ENUMVALUE CreateNameForEnumID(ESceneActionId::ReleaseDataInstance); CreateNameForEnumID(ESceneActionId::AllocateDataLayout); CreateNameForEnumID(ESceneActionId::ReleaseDataLayout); + CreateNameForEnumID(ESceneActionId::SetDataBooleanArray); CreateNameForEnumID(ESceneActionId::SetDataIntegerArray); CreateNameForEnumID(ESceneActionId::SetDataFloatArray); CreateNameForEnumID(ESceneActionId::SetDataVector2fArray); @@ -385,5 +386,3 @@ case ENUMVALUE: return #ENUMVALUE return "Unknown Message Type"; } } - -#endif diff --git a/src/framework/internal/SceneGraph/Scene/EScenePublicationMode.h b/src/framework/internal/SceneGraph/Scene/EScenePublicationMode.h new file mode 100644 index 000000000..17e347a13 --- /dev/null +++ b/src/framework/internal/SceneGraph/Scene/EScenePublicationMode.h @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include "ramses/framework/EScenePublicationMode.h" + +namespace ramses::internal +{ + using ramses::EScenePublicationMode; + + inline const char* EnumToString(EScenePublicationMode publicationMode) + { + switch (publicationMode) + { + case EScenePublicationMode::LocalAndRemote: + return "EScenePublicationMode::LocalAndRemote"; + case EScenePublicationMode::LocalOnly: + return "EScenePublicationMode::LocalOnly"; + } + assert(false); + return ""; + } +} diff --git a/framework/SceneGraph/Scene/include/Scene/ETransformMatrixType.h b/src/framework/internal/SceneGraph/Scene/ETransformMatrixType.h similarity index 85% rename from framework/SceneGraph/Scene/include/Scene/ETransformMatrixType.h rename to src/framework/internal/SceneGraph/Scene/ETransformMatrixType.h index 404eecabd..80b6d7a1b 100644 --- a/framework/SceneGraph/Scene/include/Scene/ETransformMatrixType.h +++ b/src/framework/internal/SceneGraph/Scene/ETransformMatrixType.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ETRANSFORMMATRIXTYPE_H -#define RAMSES_ETRANSFORMMATRIXTYPE_H +#pragma once -namespace ramses_internal +namespace ramses::internal { enum ETransformationMatrixType { @@ -19,5 +18,3 @@ namespace ramses_internal ETransformationMatrixType_COUNT }; } -#endif - diff --git a/framework/SceneGraph/Scene/include/Scene/MatrixCacheEntry.h b/src/framework/internal/SceneGraph/Scene/MatrixCacheEntry.h similarity index 79% rename from framework/SceneGraph/Scene/include/Scene/MatrixCacheEntry.h rename to src/framework/internal/SceneGraph/Scene/MatrixCacheEntry.h index f7672c5b3..a4bef619f 100644 --- a/framework/SceneGraph/Scene/include/Scene/MatrixCacheEntry.h +++ b/src/framework/internal/SceneGraph/Scene/MatrixCacheEntry.h @@ -6,20 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MATRIXCACHEENTRY_H -#define RAMSES_MATRIXCACHEENTRY_H +#pragma once #include "ETransformMatrixType.h" -#include "DataTypesImpl.h" -#include "SceneAPI/Handles.h" +#include "impl/DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" #include -namespace ramses_internal +namespace ramses::internal { struct MatrixCacheEntry { MatrixCacheEntry() - : m_isIdentity(true) { setDirty(); } @@ -30,10 +28,8 @@ namespace ramses_internal m_matrixDirty[ETransformationMatrixType_Object] = true; } - std::array m_matrix; - std::array m_matrixDirty; - bool m_isIdentity; + std::array m_matrix{}; + std::array m_matrixDirty{}; + bool m_isIdentity{true}; }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/ResourceChangeCollectingScene.cpp b/src/framework/internal/SceneGraph/Scene/ResourceChangeCollectingScene.cpp similarity index 92% rename from framework/SceneGraph/Scene/src/ResourceChangeCollectingScene.cpp rename to src/framework/internal/SceneGraph/Scene/ResourceChangeCollectingScene.cpp index 244fdfcc4..a56ffc46c 100644 --- a/framework/SceneGraph/Scene/src/ResourceChangeCollectingScene.cpp +++ b/src/framework/internal/SceneGraph/Scene/ResourceChangeCollectingScene.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/ResourceChangeCollectingScene.h" -#include "Utils/MemoryPoolExplicit.h" +#include "internal/SceneGraph/Scene/ResourceChangeCollectingScene.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" -namespace ramses_internal +namespace ramses::internal { ResourceChangeCollectingScene::ResourceChangeCollectingScene(const SceneInfo& sceneInfo) : TransformationCachedScene(sceneInfo) @@ -67,7 +67,7 @@ namespace ramses_internal TransformationCachedScene::setDataTextureSamplerHandle(containerHandle, field, samplerHandle); } - ramses_internal::TextureSamplerHandle ResourceChangeCollectingScene::allocateTextureSampler(const TextureSampler& sampler, TextureSamplerHandle handle /*= TextureSamplerHandle::Invalid()*/) + TextureSamplerHandle ResourceChangeCollectingScene::allocateTextureSampler(const TextureSampler& sampler, TextureSamplerHandle handle /*= TextureSamplerHandle::Invalid()*/) { if (sampler.textureResource.isValid()) m_resourcesChanged = true; @@ -83,7 +83,7 @@ namespace ramses_internal TransformationCachedScene::releaseTextureSampler(handle); } - ramses_internal::DataSlotHandle ResourceChangeCollectingScene::allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle /*= DataSlotHandle::Invalid()*/) + DataSlotHandle ResourceChangeCollectingScene::allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle /*= DataSlotHandle::Invalid()*/) { if (dataSlot.attachedTexture.isValid()) m_resourcesChanged = true; @@ -157,13 +157,13 @@ namespace ramses_internal m_sceneResourceActions.push_back({ handle.asMemoryHandle(), ESceneResourceAction_DestroyDataBuffer }); } - void ResourceChangeCollectingScene::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) + void ResourceChangeCollectingScene::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) { TransformationCachedScene::updateDataBuffer(handle, offsetInBytes, dataSizeInBytes, data); m_sceneResourceActions.push_back({ handle.asMemoryHandle(), ESceneResourceAction_UpdateDataBuffer }); } - TextureBufferHandle ResourceChangeCollectingScene::allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) + TextureBufferHandle ResourceChangeCollectingScene::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) { const TextureBufferHandle newHandle = TransformationCachedScene::allocateTextureBuffer(textureFormat, mipMapDimensions, handle); m_sceneResourceActions.push_back({ newHandle.asMemoryHandle(), ESceneResourceAction_CreateTextureBuffer}); @@ -176,7 +176,7 @@ namespace ramses_internal m_sceneResourceActions.push_back({ handle.asMemoryHandle(), ESceneResourceAction_DestroyTextureBuffer }); } - void ResourceChangeCollectingScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) + void ResourceChangeCollectingScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) { TransformationCachedScene::updateTextureBuffer(handle, mipLevel, x, y, width, height, data); m_sceneResourceActions.push_back({ handle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer }); diff --git a/framework/SceneGraph/Scene/include/Scene/ResourceChangeCollectingScene.h b/src/framework/internal/SceneGraph/Scene/ResourceChangeCollectingScene.h similarity index 78% rename from framework/SceneGraph/Scene/include/Scene/ResourceChangeCollectingScene.h rename to src/framework/internal/SceneGraph/Scene/ResourceChangeCollectingScene.h index d9ca11df7..3804a7050 100644 --- a/framework/SceneGraph/Scene/include/Scene/ResourceChangeCollectingScene.h +++ b/src/framework/internal/SceneGraph/Scene/ResourceChangeCollectingScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCECHANGECOLLECTINGSCENE_H -#define RAMSES_RESOURCECHANGECOLLECTINGSCENE_H +#pragma once -#include "Scene/TransformationCachedScene.h" -#include "Scene/ResourceChanges.h" +#include "internal/SceneGraph/Scene/TransformationCachedScene.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" -namespace ramses_internal +namespace ramses::internal { class ResourceChangeCollectingScene : public TransformationCachedScene { @@ -31,30 +30,30 @@ namespace ramses_internal void setDataResource(DataInstanceHandle dataInstanceHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) override; void setDataTextureSamplerHandle(DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) override; - TextureSamplerHandle allocateTextureSampler(const TextureSampler& sampler, TextureSamplerHandle handle = TextureSamplerHandle::Invalid()) override; + TextureSamplerHandle allocateTextureSampler(const TextureSampler& sampler, TextureSamplerHandle handle) override; void releaseTextureSampler(TextureSamplerHandle handle) override; - DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle) override; void setDataSlotTexture(DataSlotHandle providerHandle, const ResourceContentHash& texture) override; void releaseDataSlot(DataSlotHandle handle) override; // functions which affect scene resources - RenderTargetHandle allocateRenderTarget(RenderTargetHandle targetHandle = RenderTargetHandle::Invalid()) override; + RenderTargetHandle allocateRenderTarget(RenderTargetHandle targetHandle) override; void releaseRenderTarget(RenderTargetHandle handle) override; - RenderBufferHandle allocateRenderBuffer(const RenderBuffer& renderBuffer, RenderBufferHandle handle = RenderBufferHandle::Invalid()) override; + RenderBufferHandle allocateRenderBuffer(const RenderBuffer& renderBuffer, RenderBufferHandle handle) override; void releaseRenderBuffer(RenderBufferHandle handle) override; - BlitPassHandle allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) override; + BlitPassHandle allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) override; void releaseBlitPass(BlitPassHandle handle) override; - DataBufferHandle allocateDataBuffer(EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle = DataBufferHandle::Invalid()) override; + DataBufferHandle allocateDataBuffer(EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle) override; void releaseDataBuffer(DataBufferHandle handle) override; - void updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) override; + void updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) override; - TextureBufferHandle allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()) override; + TextureBufferHandle allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) override; void releaseTextureBuffer(TextureBufferHandle handle) override; - void updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) override; + void updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) override; private: @@ -62,5 +61,3 @@ namespace ramses_internal bool m_resourcesChanged = false; }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/ResourceChanges.cpp b/src/framework/internal/SceneGraph/Scene/ResourceChanges.cpp similarity index 88% rename from framework/SceneGraph/Scene/src/ResourceChanges.cpp rename to src/framework/internal/SceneGraph/Scene/ResourceChanges.cpp index 813536ed4..9e0519f32 100644 --- a/framework/SceneGraph/Scene/src/ResourceChanges.cpp +++ b/src/framework/internal/SceneGraph/Scene/ResourceChanges.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/ResourceChanges.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" -namespace ramses_internal +namespace ramses::internal { void ResourceChanges::clear() { @@ -28,12 +28,12 @@ namespace ramses_internal template void putDataArray(SceneActionCollection& action, const std::vector& dataArray) { - const uint32_t numElements = static_cast(dataArray.size()); + const auto numElements = static_cast(dataArray.size()); action.write(numElements); if (numElements > 0u) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) TODO(tobias) questionable if correct because ELEMENTTYPE not POD - const Byte* rawData = reinterpret_cast(dataArray.data()); + const auto* rawData = reinterpret_cast(dataArray.data()); const uint32_t size = numElements * sizeof(ELEMENTTYPE); action.write(rawData, size); } @@ -47,7 +47,7 @@ namespace ramses_internal if (numElements > 0u) { - const Byte* rawData = nullptr; + const std::byte* rawData = nullptr; uint32_t size = 0u; action.readWithoutCopy(rawData, size); diff --git a/framework/SceneGraph/Scene/include/Scene/ResourceChanges.h b/src/framework/internal/SceneGraph/Scene/ResourceChanges.h similarity index 89% rename from framework/SceneGraph/Scene/include/Scene/ResourceChanges.h rename to src/framework/internal/SceneGraph/Scene/ResourceChanges.h index 701398a3e..fd62bbafe 100644 --- a/framework/SceneGraph/Scene/include/Scene/ResourceChanges.h +++ b/src/framework/internal/SceneGraph/Scene/ResourceChanges.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCECHANGES_H -#define RAMSES_RESOURCECHANGES_H +#pragma once -#include "SceneAPI/SceneTypes.h" -#include "Scene/SceneActionCollection.h" -#include "PlatformAbstraction/FmtBase.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/PlatformAbstraction/FmtBase.h" -namespace ramses_internal +namespace ramses::internal { class SceneAction; @@ -37,8 +36,6 @@ namespace ramses_internal ESceneResourceAction_CreateTextureBuffer, ESceneResourceAction_UpdateTextureBuffer, ESceneResourceAction_DestroyTextureBuffer, - - ESceneResourceAction_NUMBER_OF_ELEMENTS }; struct SceneResourceAction @@ -112,14 +109,14 @@ namespace ramses_internal "UpdateTextureBuffer", "DestroyTextureBuffer" }; - ENUM_TO_STRING(ESceneResourceAction, SceneResourceActionNames, ESceneResourceAction_NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(ESceneResourceAction, SceneResourceActionNames, ESceneResourceAction_DestroyTextureBuffer); } template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::ResourceChanges& rc, FormatContext& ctx) + constexpr auto format(const ramses::internal::ResourceChanges& rc, FormatContext& ctx) { fmt::format_to(ctx.out(), "\n[ new client resources:"); for (const auto& res : rc.m_resourcesAdded) @@ -138,5 +135,3 @@ struct fmt::formatter : public ramses_internal } }; - -#endif diff --git a/framework/SceneGraph/Scene/src/Scene.cpp b/src/framework/internal/SceneGraph/Scene/Scene.cpp similarity index 96% rename from framework/SceneGraph/Scene/src/Scene.cpp rename to src/framework/internal/SceneGraph/Scene/Scene.cpp index d50d8de9b..4f29c2235 100644 --- a/framework/SceneGraph/Scene/src/Scene.cpp +++ b/src/framework/internal/SceneGraph/Scene/Scene.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/Scene.h" +#include "internal/SceneGraph/Scene/Scene.h" -#include "Utils/MemoryPoolExplicit.h" -#include "Utils/MemoryPool.h" -#include "Utils/LogMacros.h" -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/RenderGroupUtils.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" +#include "internal/Core/Utils/MemoryPool.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" +#include "internal/PlatformAbstraction/PlatformMath.h" -namespace ramses_internal +namespace ramses::internal { template class MEMORYPOOL> SceneT::SceneT(const SceneInfo& sceneInfo) @@ -31,11 +31,6 @@ namespace ramses_internal m_effectTimeSync = t; } - template class MEMORYPOOL> - SceneT::~SceneT() - { - } - template class MEMORYPOOL> RenderPassHandle SceneT::allocateRenderPass(uint32_t renderGroupCount, RenderPassHandle handle) { @@ -57,7 +52,7 @@ namespace ramses_internal } template class MEMORYPOOL> - void SceneT::setRenderPassClearFlag(RenderPassHandle passHandle, uint32_t clearFlag) + void SceneT::setRenderPassClearFlag(RenderPassHandle passHandle, ClearFlags clearFlag) { m_renderPasses.getMemory(passHandle)->clearFlags = clearFlag; } @@ -99,9 +94,8 @@ namespace ramses_internal } template class MEMORYPOOL> - void SceneT::retriggerRenderPassRenderOnce(RenderPassHandle passHandle) + void SceneT::retriggerRenderPassRenderOnce([[maybe_unused]] RenderPassHandle passHandle) { - UNUSED(passHandle); assert(m_renderPasses.getMemory(passHandle)->isRenderOnce); // implemented on renderer side only in a derived scene } @@ -209,7 +203,7 @@ namespace ramses_internal } template class MEMORYPOOL> - void ramses_internal::SceneT::releaseDataBuffer(DataBufferHandle handle) + void ramses::internal::SceneT::releaseDataBuffer(DataBufferHandle handle) { assert(m_dataBuffers.isAllocated(handle)); m_dataBuffers.release(handle); @@ -222,11 +216,11 @@ namespace ramses_internal } template class MEMORYPOOL> - void SceneT::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) + void SceneT::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) { GeometryDataBuffer& dataBuffer = *m_dataBuffers.getMemory(handle); assert(dataSizeInBytes + offsetInBytes <= dataBuffer.data.size()); - Byte* const copyDestination = dataBuffer.data.data() + offsetInBytes; + std::byte* const copyDestination = dataBuffer.data.data() + offsetInBytes; PlatformMemory::Copy(copyDestination, data, dataSizeInBytes); dataBuffer.usedSize = std::max(dataBuffer.usedSize, dataSizeInBytes + offsetInBytes); } @@ -238,7 +232,7 @@ namespace ramses_internal } template class MEMORYPOOL> - TextureBufferHandle SceneT::allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) + TextureBufferHandle SceneT::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) { const TextureBufferHandle allocatedHandle = m_textureBuffers.allocate(handle); TextureBuffer* textureBuffer = m_textureBuffers.getMemory(allocatedHandle); @@ -274,7 +268,7 @@ namespace ramses_internal } template class MEMORYPOOL> - void SceneT::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) + void SceneT::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) { TextureBuffer& textureBuffer = *m_textureBuffers.getMemory(handle); assert(mipLevel < textureBuffer.mipMaps.size()); @@ -289,7 +283,7 @@ namespace ramses_internal assert(y + height <= mipLevelHeight); //copy updated part of the texture data in row-major order - Byte* const mipLevelDataPtr = mip.data.data(); + std::byte* const mipLevelDataPtr = mip.data.data(); if (0u == x && 0u == y && width == mipLevelWidth && height == mipLevelHeight) { PlatformMemory::Copy(mipLevelDataPtr, data, dataSize); @@ -299,7 +293,7 @@ namespace ramses_internal const uint32_t dataRowSize = width * texelSize; const uint32_t mipLevelRowSize = mipLevelWidth * texelSize; - Byte* destinationPtr = mipLevelDataPtr + x * texelSize + y * mipLevelRowSize; + std::byte* destinationPtr = mipLevelDataPtr + x * texelSize + y * mipLevelRowSize; for (uint32_t i = 0u; i < height; ++i) { PlatformMemory::Copy(destinationPtr, data, dataRowSize); @@ -322,9 +316,9 @@ namespace ramses_internal } template class MEMORYPOOL> - DataSlotHandle SceneT::allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle /*= DataSlotHandle::Invalid()*/) + DataSlotHandle SceneT::allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle) { - assert(dataSlot.type != EDataSlotType_Undefined); + assert(dataSlot.type != EDataSlotType::Undefined); const DataSlotHandle allocatedHandle = m_dataSlots.allocate(handle); *m_dataSlots.getMemory(allocatedHandle) = dataSlot; return allocatedHandle; @@ -405,7 +399,7 @@ namespace ramses_internal } template class MEMORYPOOL> - BlitPassHandle SceneT::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle /*= BlitPassHandle::Invalid()*/) + BlitPassHandle SceneT::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) { const BlitPassHandle blitPassHandle = m_blitPasses.allocate(passHandle); BlitPass& blitPass = *m_blitPasses.getMemory(blitPassHandle); @@ -527,7 +521,7 @@ namespace ramses_internal } template class MEMORYPOOL> - void ramses_internal::SceneT::setRenderStateScissorTest(RenderStateHandle stateHandle, EScissorTest flag, const RenderState::ScissorRegion& region) + void ramses::internal::SceneT::setRenderStateScissorTest(RenderStateHandle stateHandle, EScissorTest flag, const RenderState::ScissorRegion& region) { m_states.getMemory(stateHandle)->scissorTest = flag; m_states.getMemory(stateHandle)->scissorRegion = region; @@ -815,6 +809,12 @@ namespace ramses_internal setInstanceDataInternal(containerHandle, fieldId, elementCount, data); } + template class MEMORYPOOL> + void SceneT::setDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle fieldId, uint32_t elementCount, const bool* data) + { + setInstanceDataInternal(containerHandle, fieldId, elementCount, data); + } + template class MEMORYPOOL> void SceneT::setDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle fieldId, uint32_t elementCount, const int32_t* data) { @@ -1216,6 +1216,13 @@ namespace ramses_internal return getDataVector4fArray(containerHandle, field)[0]; } + template class MEMORYPOOL> + bool SceneT::getDataSingleBoolean(DataInstanceHandle containerHandle, DataFieldHandle field) const + { + assert(getDataLayout(getLayoutOfDataInstance(containerHandle)).getField(field).elementCount == 1); + return getDataBooleanArray(containerHandle, field)[0]; + } + template class MEMORYPOOL> int32_t SceneT::getDataSingleInteger(DataInstanceHandle containerHandle, DataFieldHandle field) const { @@ -1297,6 +1304,14 @@ namespace ramses_internal setDataVector4fArray(containerHandle, field, elementCount, &data); } + template class MEMORYPOOL> + void SceneT::setDataSingleBoolean(DataInstanceHandle containerHandle, DataFieldHandle field, bool data) + { + uint32_t elementCount = getDataLayout(getLayoutOfDataInstance(containerHandle)).getField(field).elementCount; + assert(elementCount == 1); + setDataBooleanArray(containerHandle, field, elementCount, &data); + } + template class MEMORYPOOL> void SceneT::setDataSingleInteger(DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) { diff --git a/framework/SceneGraph/Scene/include/Scene/Scene.h b/src/framework/internal/SceneGraph/Scene/Scene.h similarity index 77% rename from framework/SceneGraph/Scene/include/Scene/Scene.h rename to src/framework/internal/SceneGraph/Scene/Scene.h index 10ffee939..f29f02c28 100644 --- a/framework/SceneGraph/Scene/include/Scene/Scene.h +++ b/src/framework/internal/SceneGraph/Scene/Scene.h @@ -6,34 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_SCENE_H -#define RAMSES_INTERNAL_SCENE_H - -#include "SceneAPI/IScene.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/ResourceField.h" -#include "SceneAPI/Camera.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/RenderPass.h" -#include "SceneAPI/RenderGroup.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/TextureBuffer.h" -#include "SceneAPI/GeometryDataBuffer.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/RenderTarget.h" -#include "SceneAPI/BlitPass.h" -#include "SceneAPI/PickableObject.h" -#include "SceneAPI/SceneReference.h" - -#include "Scene/TopologyNode.h" -#include "Scene/TopologyTransform.h" -#include "Scene/DataLayout.h" -#include "Scene/DataInstance.h" - -#include "Utils/MemoryPool.h" -#include "Utils/MemoryPoolExplicit.h" - -namespace ramses_internal +#pragma once + +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ResourceField.h" +#include "internal/SceneGraph/SceneAPI/Camera.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/RenderPass.h" +#include "internal/SceneGraph/SceneAPI/RenderGroup.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/TextureBuffer.h" +#include "internal/SceneGraph/SceneAPI/GeometryDataBuffer.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/RenderTarget.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "internal/SceneGraph/SceneAPI/PickableObject.h" +#include "internal/SceneGraph/SceneAPI/SceneReference.h" + +#include "internal/SceneGraph/Scene/TopologyNode.h" +#include "internal/SceneGraph/Scene/TopologyTransform.h" +#include "internal/SceneGraph/Scene/DataLayout.h" +#include "internal/SceneGraph/Scene/DataInstance.h" + +#include "internal/Core/Utils/MemoryPool.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" + +namespace ramses::internal { template class MEMORYPOOL> class SceneT; @@ -65,7 +64,6 @@ namespace ramses_internal using SceneReferenceMemoryPool = MEMORYPOOL; explicit SceneT(const SceneInfo& sceneInfo = SceneInfo()); - ~SceneT() override; void preallocateSceneSize (const SceneSizeInformation& sizeInfo) override; @@ -76,7 +74,7 @@ namespace ramses_internal [[nodiscard]] FlushTime::Clock::time_point getEffectTimeSync() const override; // Renderables - RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle = RenderableHandle::Invalid()) override; + RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle) override; void releaseRenderable (RenderableHandle renderableHandle) override; [[nodiscard]] bool isRenderableAllocated (RenderableHandle renderableHandle) const final override; [[nodiscard]] uint32_t getRenderableCount () const final override; @@ -91,7 +89,7 @@ namespace ramses_internal [[nodiscard]] const RenderableMemoryPool& getRenderables () const; // Render state - RenderStateHandle allocateRenderState (RenderStateHandle stateHandle = RenderStateHandle::Invalid()) override; + RenderStateHandle allocateRenderState (RenderStateHandle stateHandle) override; void releaseRenderState (RenderStateHandle stateHandle) override; [[nodiscard]] bool isRenderStateAllocated (RenderStateHandle stateHandle) const final override; [[nodiscard]] uint32_t getRenderStateCount () const final override; @@ -110,7 +108,7 @@ namespace ramses_internal [[nodiscard]] const RenderStateMemoryPool& getRenderStates () const; // Camera - CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle = CameraHandle::Invalid()) override; + CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle) override; void releaseCamera (CameraHandle cameraHandle) override; [[nodiscard]] bool isCameraAllocated (CameraHandle handle) const final override; [[nodiscard]] uint32_t getCameraCount () const final override; @@ -118,7 +116,7 @@ namespace ramses_internal [[nodiscard]] const CameraMemoryPool& getCameras () const; // Nodes - NodeHandle allocateNode (uint32_t childrenCount = 0u, NodeHandle handle = NodeHandle::Invalid()) override; + NodeHandle allocateNode (uint32_t childrenCount, NodeHandle handle) override; void releaseNode (NodeHandle nodeHandle) override; [[nodiscard]] bool isNodeAllocated (NodeHandle node) const final override; [[nodiscard]] uint32_t getNodeCount () const final override; @@ -130,7 +128,7 @@ namespace ramses_internal [[nodiscard]] const NodeMemoryPool& getNodes () const; // Transformation - TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle = TransformHandle::Invalid()) override; + TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle) override; void releaseTransform (TransformHandle transform) override; [[nodiscard]] uint32_t getTransformCount () const final override; [[nodiscard]] bool isTransformAllocated (TransformHandle transformHandle) const final override; @@ -144,77 +142,81 @@ namespace ramses_internal void setScaling (TransformHandle handle, const glm::vec3& scaling) override; [[nodiscard]] const TransformMemoryPool& getTransforms () const; - DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle = DataLayoutHandle::Invalid()) override; + DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle) override; void releaseDataLayout (DataLayoutHandle layoutHandle) override; [[nodiscard]] bool isDataLayoutAllocated (DataLayoutHandle layoutHandle) const final override; [[nodiscard]] uint32_t getDataLayoutCount () const final override; [[nodiscard]] const DataLayout& getDataLayout (DataLayoutHandle layoutHandle) const final override; [[nodiscard]] const DataLayoutMemoryPool& getDataLayouts () const; - DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle = DataInstanceHandle::Invalid()) override; + DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle) override; void releaseDataInstance (DataInstanceHandle containerHandle) override; [[nodiscard]] bool isDataInstanceAllocated (DataInstanceHandle containerHandle) const final override; [[nodiscard]] uint32_t getDataInstanceCount () const final override; [[nodiscard]] DataLayoutHandle getLayoutOfDataInstance (DataInstanceHandle containerHandle) const final override; [[nodiscard]] const DataInstanceMemoryPool& getDataInstances () const; - [[nodiscard]] const float* getDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::vec2* getDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::vec3* getDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::vec4* getDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const int32_t* getDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::mat2* getDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::mat3* getDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::mat4* getDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::ivec2* getDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::ivec3* getDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::ivec4* getDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const ResourceField& getDataResource (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] TextureSamplerHandle getDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] DataInstanceHandle getDataReference (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - - void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) override; - void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) override; - void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) override; - void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) override; - void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) override; - void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) override; - void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) override; - void setDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec4* data) override; - void setDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat2* data) override; - void setDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat3* data) override; - void setDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat4* data) override; - void setDataResource (DataInstanceHandle containerHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) override; - void setDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) override; - void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) override; + [[nodiscard]] const float* getDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::vec2* getDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::vec3* getDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::vec4* getDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const bool* getDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const int32_t* getDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::mat2* getDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::mat3* getDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::mat4* getDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::ivec2* getDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::ivec4* getDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::ivec3* getDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const ResourceField& getDataResource (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] TextureSamplerHandle getDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] DataInstanceHandle getDataReference (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + + void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) override; + void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) override; + void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) override; + void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) override; + void setDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) override; + void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) override; + void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) override; + void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) override; + void setDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec4* data) override; + void setDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat2* data) override; + void setDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat3* data) override; + void setDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat4* data) override; + void setDataResource (DataInstanceHandle containerHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) override; + void setDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) override; + void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) override; // get/setData*Array wrappers for elementCount == 1 - [[nodiscard]] float getDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::vec2& getDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::vec3& getDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::vec4& getDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] int32_t getDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::mat2& getDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::mat3& getDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::mat4& getDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::ivec2& getDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::ivec3& getDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - [[nodiscard]] const glm::ivec4& getDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; - - void setDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field, float data) override; - void setDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec2& data) override; - void setDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec3& data) override; - void setDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec4& data) override; - void setDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) override; - void setDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec2& data) override; - void setDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec3& data) override; - void setDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec4& data) override; - void setDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat2& data) override; - void setDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat3& data) override; - void setDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat4& data) override; + [[nodiscard]] float getDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::vec2& getDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::vec3& getDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::vec4& getDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] bool getDataSingleBoolean (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] int32_t getDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::mat2& getDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::mat3& getDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::mat4& getDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::ivec2& getDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::ivec3& getDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + [[nodiscard]] const glm::ivec4& getDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field) const final override; + + void setDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field, float data) override; + void setDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec2& data) override; + void setDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec3& data) override; + void setDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec4& data) override; + void setDataSingleBoolean (DataInstanceHandle containerHandle, DataFieldHandle field, bool data) override; + void setDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) override; + void setDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec2& data) override; + void setDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec3& data) override; + void setDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec4& data) override; + void setDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat2& data) override; + void setDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat3& data) override; + void setDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat4& data) override; // Texture sampler - TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle = TextureSamplerHandle::Invalid()) override; + TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle) override; void releaseTextureSampler (TextureSamplerHandle handle) override; [[nodiscard]] bool isTextureSamplerAllocated (TextureSamplerHandle handle) const final override; [[nodiscard]] uint32_t getTextureSamplerCount () const final override; @@ -222,7 +224,7 @@ namespace ramses_internal [[nodiscard]] const TextureSamplerMemoryPool& getTextureSamplers () const; // Render groups - RenderGroupHandle allocateRenderGroup (uint32_t renderableCount = 0u, uint32_t nestedGroupCount = 0u, RenderGroupHandle groupHandle = RenderGroupHandle::Invalid()) override; + RenderGroupHandle allocateRenderGroup (uint32_t renderableCount, uint32_t nestedGroupCount, RenderGroupHandle groupHandle) override; void releaseRenderGroup (RenderGroupHandle groupHandle) override; [[nodiscard]] bool isRenderGroupAllocated (RenderGroupHandle groupHandle) const final override; [[nodiscard]] uint32_t getRenderGroupCount () const final override; @@ -234,12 +236,12 @@ namespace ramses_internal [[nodiscard]] const RenderGroupMemoryPool& getRenderGroups () const; //Render pass - RenderPassHandle allocateRenderPass (uint32_t renderGroupCount = 0u, RenderPassHandle passHandle = RenderPassHandle::Invalid()) override; + RenderPassHandle allocateRenderPass (uint32_t renderGroupCount, RenderPassHandle passHandle) override; void releaseRenderPass (RenderPassHandle passHandle) override; [[nodiscard]] bool isRenderPassAllocated (RenderPassHandle pass) const final override; - [[nodiscard]] uint32_t getRenderPassCount () const final override; + [[nodiscard]] uint32_t getRenderPassCount () const final override; void setRenderPassClearColor (RenderPassHandle passHandle, const glm::vec4& clearColor) override; - void setRenderPassClearFlag (RenderPassHandle passHandle, uint32_t clearFlag) override; + void setRenderPassClearFlag (RenderPassHandle passHandle, ClearFlags clearFlag) override; void setRenderPassCamera (RenderPassHandle passHandle, CameraHandle cameraHandle) override; void setRenderPassRenderTarget (RenderPassHandle passHandle, RenderTargetHandle targetHandle) override; void setRenderPassRenderOrder (RenderPassHandle passHandle, int32_t renderOrder) override; @@ -252,7 +254,7 @@ namespace ramses_internal [[nodiscard]] const RenderPassMemoryPool& getRenderPasses () const; //Blit pass - BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) override; + BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) override; void releaseBlitPass (BlitPassHandle passHandle) override; [[nodiscard]] bool isBlitPassAllocated (BlitPassHandle passHandle) const final override; [[nodiscard]] uint32_t getBlitPassCount () const final override; @@ -263,7 +265,7 @@ namespace ramses_internal [[nodiscard]] const BlitPassMemoryPool& getBlitPasses () const; //Pickable object - PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle = PickableObjectHandle::Invalid()) override; + PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle) override; void releasePickableObject (PickableObjectHandle pickableHandle) override; [[nodiscard]] bool isPickableObjectAllocated (PickableObjectHandle pickableHandle) const final override; [[nodiscard]] uint32_t getPickableObjectCount () const final override; @@ -274,7 +276,7 @@ namespace ramses_internal [[nodiscard]] const PickableObjectMemoryPool& getPickableObjects () const; // Render targets - RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle = RenderTargetHandle::Invalid()) override; + RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle) override; void releaseRenderTarget (RenderTargetHandle targetHandle) override; [[nodiscard]] bool isRenderTargetAllocated (RenderTargetHandle targetHandle) const final override; [[nodiscard]] uint32_t getRenderTargetCount () const final override; @@ -284,7 +286,7 @@ namespace ramses_internal [[nodiscard]] const RenderTargetMemoryPool& getRenderTargets () const; // Render buffers - RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle = RenderBufferHandle::Invalid()) override; + RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle) override; void releaseRenderBuffer (RenderBufferHandle handle) override; [[nodiscard]] bool isRenderBufferAllocated (RenderBufferHandle handle) const final override; [[nodiscard]] uint32_t getRenderBufferCount () const final override; @@ -292,24 +294,24 @@ namespace ramses_internal [[nodiscard]] const RenderBufferMemoryPool& getRenderBuffers () const; // Data buffers - DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle = DataBufferHandle::Invalid()) override; + DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle) override; void releaseDataBuffer (DataBufferHandle handle) override; [[nodiscard]] uint32_t getDataBufferCount () const final override; - void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) override; + void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) override; [[nodiscard]] bool isDataBufferAllocated (DataBufferHandle handle) const final override; [[nodiscard]] const GeometryDataBuffer& getDataBuffer (DataBufferHandle handle) const final override; [[nodiscard]] const DataBufferMemoryPool& getDataBuffers () const; //Texture buffers - TextureBufferHandle allocateTextureBuffer (ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()) override; + TextureBufferHandle allocateTextureBuffer (EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) override; void releaseTextureBuffer (TextureBufferHandle handle) override; [[nodiscard]] bool isTextureBufferAllocated (TextureBufferHandle handle) const final override; [[nodiscard]] uint32_t getTextureBufferCount () const final override; - void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) override; + void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) override; [[nodiscard]] const TextureBuffer& getTextureBuffer (TextureBufferHandle handle) const final override; [[nodiscard]] const TextureBufferMemoryPool& getTextureBuffers () const; - DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle) override; void releaseDataSlot (DataSlotHandle handle) override; void setDataSlotTexture (DataSlotHandle handle, const ResourceContentHash& texture) override; [[nodiscard]] bool isDataSlotAllocated (DataSlotHandle handle) const final override; @@ -317,7 +319,7 @@ namespace ramses_internal [[nodiscard]] const DataSlot& getDataSlot (DataSlotHandle handle) const final override; [[nodiscard]] const DataSlotMemoryPool& getDataSlots () const; - SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle = {}) override; + SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle) override; void releaseSceneReference (SceneReferenceHandle handle) override; void requestSceneReferenceState (SceneReferenceHandle handle, RendererSceneState state) override; void requestSceneReferenceFlushNotifications(SceneReferenceHandle handle, bool enable) override; @@ -698,6 +700,12 @@ namespace ramses_internal return getInstanceDataInternal(containerHandle, fieldId); } + template class MEMORYPOOL> + inline const bool* SceneT::getDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle fieldId) const + { + return getInstanceDataInternal(containerHandle, fieldId); + } + template class MEMORYPOOL> inline const int32_t* SceneT::getDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle fieldId) const { @@ -740,5 +748,3 @@ namespace ramses_internal return *getInstanceDataInternal(containerHandle, fieldId); } } - -#endif diff --git a/framework/SceneGraph/Scene/src/SceneActionApplier.cpp b/src/framework/internal/SceneGraph/Scene/SceneActionApplier.cpp similarity index 87% rename from framework/SceneGraph/Scene/src/SceneActionApplier.cpp rename to src/framework/internal/SceneGraph/Scene/SceneActionApplier.cpp index 643afa390..a85668acd 100644 --- a/framework/SceneGraph/Scene/src/SceneActionApplier.cpp +++ b/src/framework/internal/SceneGraph/Scene/SceneActionApplier.cpp @@ -6,35 +6,35 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneActionApplier.h" -#include "Scene/ResourceChanges.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/PixelRectangle.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/Viewport.h" -#include "SceneAPI/Camera.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/ERotationType.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" -#include "Components/SingleResourceSerialization.h" -#include "Components/FlushTimeInformation.h" -#include "Resource/IResource.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/LogMacros.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/PixelRectangle.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/Viewport.h" +#include "internal/SceneGraph/SceneAPI/Camera.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" +#include "internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h" +#include "internal/Components/SingleResourceSerialization.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/LogMacros.h" #include "glm/gtx/range.hpp" #include -#define ALLOCATE_AND_ASSERT_HANDLE(_allocateExpr, _handleToCheck) \ -{ \ - assert(_handleToCheck.isValid()); \ - const auto _actualHandle = _allocateExpr; \ - assert(_handleToCheck == _actualHandle); \ - UNUSED(_actualHandle); \ -} -namespace ramses_internal +namespace ramses::internal { + template + inline void AssertHandle([[maybe_unused]] const TypedMemoryHandle& actualHandle, [[maybe_unused]] const TypedMemoryHandle& handleToCheck) + { + assert(handleToCheck.isValid()); + assert(handleToCheck == actualHandle); + } + void SceneActionApplier::ApplySingleActionOnScene(IScene& scene, SceneActionCollection::SceneActionReader& action) { switch (action.type()) @@ -44,14 +44,12 @@ namespace ramses_internal uint32_t childrenCount = 0u; NodeHandle nodeHandle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(childrenCount); action.read(nodeHandle); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateNode(childrenCount, nodeHandle), nodeHandle); + AssertHandle(scene.allocateNode(childrenCount, nodeHandle), nodeHandle); break; } case ESceneActionId::AddChildToNode: @@ -69,7 +67,7 @@ namespace ramses_internal TransformHandle transformHandle; action.read(nodeHandle); action.read(transformHandle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateTransform(nodeHandle, transformHandle), transformHandle); + AssertHandle(scene.allocateTransform(nodeHandle, transformHandle), transformHandle); break; } case ESceneActionId::SetTranslation: @@ -106,14 +104,12 @@ namespace ramses_internal DataLayoutHandle dataLayout; DataInstanceHandle diHandle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(dataLayout); action.read(diHandle); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateDataInstance(dataLayout, diHandle), diHandle); + AssertHandle(scene.allocateDataInstance(dataLayout, diHandle), diHandle); break; } case ESceneActionId::SetDataFloatArray: @@ -123,7 +119,7 @@ namespace ramses_internal uint32_t elementCount = 0; action.read(handle); action.read(field); - float* const array = const_cast(scene.getDataFloatArray(handle, field)); + auto* array = const_cast(scene.getDataFloatArray(handle, field)); action.read(array, elementCount); scene.setDataFloatArray(handle, field, elementCount, array); break; @@ -212,6 +208,17 @@ namespace ramses_internal scene.setDataMatrix44fArray(handle, field, elementCount, array); break; } + case ESceneActionId::SetDataBooleanArray: { + DataInstanceHandle handle; + DataFieldHandle field; + uint32_t elementCount = 0; + action.read(handle); + action.read(field); + auto* array = const_cast(scene.getDataBooleanArray(handle, field)); + action.read(array, elementCount); + scene.setDataBooleanArray(handle, field, elementCount, array); + break; + } case ESceneActionId::SetDataIntegerArray: { DataInstanceHandle handle; @@ -219,7 +226,7 @@ namespace ramses_internal uint32_t elementCount = 0; action.read(handle); action.read(field); - int32_t* const array = const_cast(scene.getDataIntegerArray(handle, field)); + auto* array = const_cast(scene.getDataIntegerArray(handle, field)); action.read(array, elementCount); scene.setDataIntegerArray(handle, field, elementCount, array); break; @@ -272,9 +279,9 @@ namespace ramses_internal DataFieldHandle field; ResourceContentHash hash; DataBufferHandle dataBuffer; - uint32_t instancingDivisor; - uint16_t offset; - uint16_t stride; + uint32_t instancingDivisor = 0; + uint16_t offset = 0; + uint16_t stride = 0; action.read(handle); action.read(field); action.read(hash); @@ -313,7 +320,7 @@ namespace ramses_internal RenderableHandle handle; action.read(node); action.read(handle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderable(node, handle), handle); + AssertHandle(scene.allocateRenderable(node, handle), handle); break; } case ESceneActionId::RemoveChildFromNode: @@ -344,7 +351,7 @@ namespace ramses_internal } ResourceContentHash effectHash; action.read(effectHash); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateDataLayout(dataFields, effectHash, layoutHandle), layoutHandle); + AssertHandle(scene.allocateDataLayout(dataFields, effectHash, layoutHandle), layoutHandle); break; } case ESceneActionId::ReleaseDataLayout: @@ -391,7 +398,7 @@ namespace ramses_internal case ESceneActionId::SetRenderableDataInstance: { RenderableHandle renderable; - uint32_t slot; + uint32_t slot = 0; DataInstanceHandle diHandle; action.read(renderable); action.read(slot); @@ -422,16 +429,14 @@ namespace ramses_internal uint32_t renderableCount = 0u; uint32_t nestedGroupCount = 0u; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; RenderGroupHandle renderGroup; action.read(renderableCount); action.read(nestedGroupCount); action.read(renderGroup); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderGroup(renderableCount, nestedGroupCount, renderGroup), renderGroup); + AssertHandle(scene.allocateRenderGroup(renderableCount, nestedGroupCount, renderGroup), renderGroup); break; } case ESceneActionId::ReleaseRenderGroup: @@ -506,7 +511,7 @@ namespace ramses_internal { RenderStateHandle stateHandle; action.read(stateHandle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderState(stateHandle), stateHandle); + AssertHandle(scene.allocateRenderState(stateHandle), stateHandle); break; } case ESceneActionId::ReleaseState: @@ -642,7 +647,7 @@ namespace ramses_internal case ESceneActionId::SetStateColorWriteMask: { RenderStateHandle stateHandle; - ColorWriteMask colorMask; + ColorWriteMask colorMask = 0; action.read(stateHandle); action.read(colorMask); scene.setRenderStateColorWriteMask(stateHandle, colorMask); @@ -650,7 +655,7 @@ namespace ramses_internal } case ESceneActionId::AllocateCamera: { - uint32_t projType; + uint32_t projType = 0; NodeHandle nodeHandle; DataInstanceHandle dataInstHandle; CameraHandle cameraHandle; @@ -658,7 +663,7 @@ namespace ramses_internal action.read(nodeHandle); action.read(dataInstHandle); action.read(cameraHandle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateCamera(static_cast(projType), nodeHandle, dataInstHandle, cameraHandle), cameraHandle); + AssertHandle(scene.allocateCamera(static_cast(projType), nodeHandle, dataInstHandle, cameraHandle), cameraHandle); break; } case ESceneActionId::ReleaseCamera: @@ -673,14 +678,12 @@ namespace ramses_internal uint32_t renderGroupCount = 0u; RenderPassHandle passHandle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(renderGroupCount); action.read(passHandle); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderPass(renderGroupCount, passHandle), passHandle); + AssertHandle(scene.allocateRenderPass(renderGroupCount, passHandle), passHandle); break; } case ESceneActionId::ReleaseRenderPass: @@ -711,7 +714,7 @@ namespace ramses_internal case ESceneActionId::SetRenderPassRenderOrder: { RenderPassHandle passHandle; - int32_t renderOrder; + int32_t renderOrder = 0; action.read(passHandle); action.read(renderOrder); scene.setRenderPassRenderOrder(passHandle, renderOrder); @@ -720,7 +723,7 @@ namespace ramses_internal case ESceneActionId::SetRenderPassEnabled: { RenderPassHandle passHandle; - bool isEnabled; + bool isEnabled = false; action.read(passHandle); action.read(isEnabled); scene.setRenderPassEnabled(passHandle, isEnabled); @@ -729,7 +732,7 @@ namespace ramses_internal case ESceneActionId::SetRenderPassRenderOnce: { RenderPassHandle passHandle; - bool enabled; + bool enabled = false; action.read(passHandle); action.read(enabled); scene.setRenderPassRenderOnce(passHandle, enabled); @@ -772,7 +775,7 @@ namespace ramses_internal action.read(nodeHandle); action.read(id); action.read(pickableHandle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocatePickableObject(geometryHandle, nodeHandle, id, pickableHandle), pickableHandle); + AssertHandle(scene.allocatePickableObject(geometryHandle, nodeHandle, id, pickableHandle), pickableHandle); break; } case ESceneActionId::ReleasePickableObject: @@ -803,7 +806,7 @@ namespace ramses_internal case ESceneActionId::SetPickableObjectEnabled: { PickableObjectHandle pickableHandle; - bool isEnabled; + bool isEnabled = false; action.read(pickableHandle); action.read(isEnabled); scene.setPickableObjectEnabled(pickableHandle, isEnabled); @@ -815,15 +818,13 @@ namespace ramses_internal RenderBufferHandle sourceRenderbufferHandle; RenderBufferHandle destinationRenderbufferHandle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(sourceRenderbufferHandle); action.read(destinationRenderbufferHandle); action.read(passHandle); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateBlitPass(sourceRenderbufferHandle, destinationRenderbufferHandle, passHandle), passHandle); + AssertHandle(scene.allocateBlitPass(sourceRenderbufferHandle, destinationRenderbufferHandle, passHandle), passHandle); break; } case ESceneActionId::ReleaseBlitPass: @@ -836,7 +837,7 @@ namespace ramses_internal case ESceneActionId::SetBlitPassRenderOrder: { BlitPassHandle passHandle; - int32_t renderOrder; + int32_t renderOrder = 0; action.read(passHandle); action.read(renderOrder); scene.setBlitPassRenderOrder(passHandle, renderOrder); @@ -845,7 +846,7 @@ namespace ramses_internal case ESceneActionId::SetBlitPassEnabled: { BlitPassHandle passHandle; - bool isEnabled; + bool isEnabled = false; action.read(passHandle); action.read(isEnabled); scene.setBlitPassEnabled(passHandle, isEnabled); @@ -873,7 +874,7 @@ namespace ramses_internal TextureSamplerHandle samplerHandle; TextureSampler sampler; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(samplerHandle); action.read(sampler.states.m_addressModeU); @@ -885,16 +886,18 @@ namespace ramses_internal action.read(sampler.contentType); if (sampler.contentType == TextureSampler::ContentType::ClientTexture) + { action.read(sampler.textureResource); + } else + { action.read(sampler.contentHandle); + } action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateTextureSampler(sampler, samplerHandle), samplerHandle); + AssertHandle(scene.allocateTextureSampler(sampler, samplerHandle), samplerHandle); break; } case ESceneActionId::ReleaseTextureSampler: @@ -908,13 +911,11 @@ namespace ramses_internal { RenderTargetHandle renderTargetHandle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(renderTargetHandle); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderTarget(renderTargetHandle), renderTargetHandle); + AssertHandle(scene.allocateRenderTarget(renderTargetHandle), renderTargetHandle); break; } case ESceneActionId::ReleaseRenderTarget: @@ -937,25 +938,21 @@ namespace ramses_internal { RenderBufferHandle handle; RenderBuffer renderBuffer; - uint32_t enumInt; + uint32_t enumInt = 0; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(renderBuffer.width); action.read(renderBuffer.height); action.read(handle); action.read(enumInt); - renderBuffer.type = static_cast(enumInt); - action.read(enumInt); - renderBuffer.format = static_cast(enumInt); + renderBuffer.format = static_cast(enumInt); action.read(enumInt); renderBuffer.accessMode = static_cast(enumInt); action.read(renderBuffer.sampleCount); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderBuffer(renderBuffer, handle), handle); + AssertHandle(scene.allocateRenderBuffer(renderBuffer, handle), handle); break; } case ESceneActionId::ReleaseRenderBuffer: @@ -979,7 +976,7 @@ namespace ramses_internal action.read(dataSlot.attachedTexture); action.read(dataSlot.attachedTextureSampler); action.read(dataSlotHandle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateDataSlot(dataSlot, dataSlotHandle), dataSlotHandle); + AssertHandle(scene.allocateDataSlot(dataSlot, dataSlotHandle), dataSlotHandle); break; } case ESceneActionId::SetDataSlotTexture: @@ -1010,30 +1007,28 @@ namespace ramses_internal } case ESceneActionId::SetRenderPassClearFlag: { - uint32_t clearFlag; + ClearFlags::value_type clearFlag = 0; RenderPassHandle renderPassHandle; action.read(renderPassHandle); action.read(clearFlag); - scene.setRenderPassClearFlag(renderPassHandle, clearFlag); + scene.setRenderPassClearFlag(renderPassHandle, ClearFlags(clearFlag)); break; } case ESceneActionId::AllocateDataBuffer: { - uint32_t dataBufferType; - uint32_t dataType; - uint32_t maximumSizeInBytes; + uint32_t dataBufferType = 0; + uint32_t dataType = 0; + uint32_t maximumSizeInBytes = 0; DataBufferHandle handle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(dataBufferType); action.read(dataType); action.read(maximumSizeInBytes); action.read(handle.asMemoryHandleReference()); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateDataBuffer(static_cast(dataBufferType), static_cast(dataType), maximumSizeInBytes, handle), handle); + AssertHandle(scene.allocateDataBuffer(static_cast(dataBufferType), static_cast(dataType), maximumSizeInBytes, handle), handle); break; } case ESceneActionId::ReleaseDataBuffer: @@ -1046,9 +1041,9 @@ namespace ramses_internal case ESceneActionId::UpdateDataBuffer: { DataBufferHandle handle; - uint32_t offsetInBytes; - uint32_t dataSizeInBytes; - const Byte* data = nullptr; + uint32_t offsetInBytes = 0; + uint32_t dataSizeInBytes = 0; + const std::byte* data = nullptr; action.read(handle.asMemoryHandleReference()); action.read(offsetInBytes); @@ -1058,20 +1053,20 @@ namespace ramses_internal } case ESceneActionId::AllocateTextureBuffer: { - uint32_t textureFormat; - uint32_t mipLevelCount; + uint32_t textureFormat = 0; + uint32_t mipLevelCount = 0; MipMapDimensions mipMapDimensions; TextureBufferHandle handle; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(textureFormat); action.read(mipLevelCount); mipMapDimensions.reserve(mipLevelCount); for (uint32_t i = 0u; i < mipLevelCount; ++i) { - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; action.read(width); action.read(height); mipMapDimensions.push_back({ width, height }); @@ -1079,10 +1074,8 @@ namespace ramses_internal action.read(handle); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateTextureBuffer(static_cast(textureFormat), mipMapDimensions, handle), handle); + AssertHandle(scene.allocateTextureBuffer(static_cast(textureFormat), mipMapDimensions, handle), handle); break; } case ESceneActionId::ReleaseTextureBuffer: @@ -1096,13 +1089,13 @@ namespace ramses_internal case ESceneActionId::UpdateTextureBuffer: { TextureBufferHandle handle; - uint32_t mipLevel; - uint32_t x; - uint32_t y; - uint32_t width; - uint32_t height; - uint32_t dataSizeInBytes; - const Byte* data = nullptr; + uint32_t mipLevel = 0; + uint32_t x = 0; + uint32_t y = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t dataSizeInBytes = 0; + const std::byte* data = nullptr; action.read(handle); action.read(mipLevel); @@ -1120,14 +1113,12 @@ namespace ramses_internal SceneReferenceHandle handle; SceneId sceneId; std::string objectName; - uint64_t objectId; + uint64_t objectId = 0; action.read(handle); action.read(sceneId); action.read(objectName); action.read(objectId); - UNUSED(objectName); - UNUSED(objectId); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateSceneReference(sceneId, handle), handle); + AssertHandle(scene.allocateSceneReference(sceneId, handle), handle); break; } case ESceneActionId::ReleaseSceneReference: @@ -1149,7 +1140,7 @@ namespace ramses_internal case ESceneActionId::SetSceneReferenceRenderOrder: { SceneReferenceHandle handle; - int32_t renderOrder; + int32_t renderOrder = 0; action.read(handle); action.read(renderOrder); scene.setSceneReferenceRenderOrder(handle, renderOrder); @@ -1158,7 +1149,7 @@ namespace ramses_internal case ESceneActionId::RequestSceneReferenceFlushNotifications: { SceneReferenceHandle handle; - bool enable; + bool enable = false; action.read(handle); action.read(enable); scene.requestSceneReferenceFlushNotifications(handle, enable); @@ -1197,12 +1188,12 @@ namespace ramses_internal // effectAttributeLayout RenderableHandle renderable; NodeHandle node; - uint32_t startIndex; - uint32_t indexCount; + uint32_t startIndex = 0; + uint32_t indexCount = 0; RenderStateHandle stateHandle; - EVisibilityMode visible; - uint32_t instanceCount; - uint32_t startVertex; + EVisibilityMode visible = EVisibilityMode::Off; + uint32_t instanceCount = 0; + uint32_t startVertex = 0; DataInstanceHandle geoInstanceHandle; DataInstanceHandle uniformInstanceHandle; @@ -1217,7 +1208,7 @@ namespace ramses_internal action.read(geoInstanceHandle); action.read(uniformInstanceHandle); - ALLOCATE_AND_ASSERT_HANDLE(scene.allocateRenderable(node, renderable), renderable); + AssertHandle(scene.allocateRenderable(node, renderable), renderable); if(startIndex != 0u) { @@ -1266,12 +1257,12 @@ namespace ramses_internal EDepthFunc depthFunc; EScissorTest scissorTest; EStencilFunc stencilFunc; - uint8_t stencilRefValue; - uint8_t stencilMask; + uint8_t stencilRefValue = 0; + uint8_t stencilMask = 0; EStencilOp stencilOpFail; EStencilOp stencilOpDepthFail; EStencilOp stencilOpDepthPass; - ColorWriteMask colorWriteMask; + ColorWriteMask colorWriteMask = 0; action.read(stateHandle); action.read(scissorRegion.x); @@ -1301,9 +1292,8 @@ namespace ramses_internal action.read(stencilOpDepthPass); action.read(colorWriteMask); - const RenderStateHandle stateHandleNew = scene.allocateRenderState(stateHandle); + [[maybe_unused]] const RenderStateHandle stateHandleNew = scene.allocateRenderState(stateHandle); assert(stateHandle == stateHandleNew); - UNUSED(stateHandleNew); scene.setRenderStateBlendFactors( stateHandle, bfSrcColor, bfDstColor, bfSrcAlpha, bfDstAlpha); scene.setRenderStateBlendOperations(stateHandle, boColor, boAlpha); diff --git a/framework/SceneGraph/Scene/include/Scene/SceneActionApplier.h b/src/framework/internal/SceneGraph/Scene/SceneActionApplier.h similarity index 83% rename from framework/SceneGraph/Scene/include/Scene/SceneActionApplier.h rename to src/framework/internal/SceneGraph/Scene/SceneActionApplier.h index 7ae939cf5..3857eae60 100644 --- a/framework/SceneGraph/Scene/include/Scene/SceneActionApplier.h +++ b/src/framework/internal/SceneGraph/Scene/SceneActionApplier.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEACTIONAPPLIER_H -#define RAMSES_SCENEACTIONAPPLIER_H +#pragma once #include "SceneActionCollection.h" -#include "SceneAPI/SceneVersionTag.h" -#include "Collections/Vector.h" -#include "SceneReferencing/SceneReferenceAction.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" #include -namespace ramses_internal +namespace ramses::internal { class IScene; struct ResourceChanges; @@ -35,5 +34,3 @@ namespace ramses_internal static void ApplySingleActionOnScene(IScene& scene, SceneActionCollection::SceneActionReader& action); }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/SceneActionCollection.cpp b/src/framework/internal/SceneGraph/Scene/SceneActionCollection.cpp similarity index 86% rename from framework/SceneGraph/Scene/src/SceneActionCollection.cpp rename to src/framework/internal/SceneGraph/Scene/SceneActionCollection.cpp index e4a7ccf4c..422209f11 100644 --- a/framework/SceneGraph/Scene/src/SceneActionCollection.cpp +++ b/src/framework/internal/SceneGraph/Scene/SceneActionCollection.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneActionCollection.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" -namespace ramses_internal +namespace ramses::internal { const uint8_t SceneActionCollection::MaxStringLength; } diff --git a/framework/SceneGraph/Scene/include/Scene/SceneActionCollection.h b/src/framework/internal/SceneGraph/Scene/SceneActionCollection.h similarity index 90% rename from framework/SceneGraph/Scene/include/Scene/SceneActionCollection.h rename to src/framework/internal/SceneGraph/Scene/SceneActionCollection.h index 6af0f1083..00226dc59 100644 --- a/framework/SceneGraph/Scene/include/Scene/SceneActionCollection.h +++ b/src/framework/internal/SceneGraph/Scene/SceneActionCollection.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEACTIONCOLLECTION_H -#define RAMSES_SCENEACTIONCOLLECTION_H - -#include "Scene/ESceneActionId.h" -#include "Common/TypedMemoryHandle.h" -#include "Common/StronglyTypedValue.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Collections/Guid.h" -#include "Collections/Vector.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "PlatformAbstraction/Macros.h" -#include "Utils/AssertMovable.h" +#pragma once + +#include "internal/SceneGraph/Scene/ESceneActionId.h" +#include "internal/Core/Common/TypedMemoryHandle.h" +#include "internal/Core/Common/StronglyTypedValue.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/Core/Utils/AssertMovable.h" #include "glm/gtx/range.hpp" +#include #include #include #include @@ -28,7 +27,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { class SceneActionCollection { @@ -74,24 +73,24 @@ namespace ramses_internal template ::value>::type> void write(E enumValue); // fixed size array - template::value>::type> + template||std::is_same_v>> void write(const T(&data)[N]); // NOLINT(modernize-avoid-c-arrays) // generic - template::value>::type> + template||std::is_same_v>> void write(const T& value); // generic array - template::value>::type> + template||std::is_same_v>> void write(const T* data, uint32_t numElements); static const uint8_t MaxStringLength = std::numeric_limits::max(); // blob write access - void appendRawData(const Byte* data, size_t dataSize); - std::vector& getRawDataForDirectWriting(); + void appendRawData(const std::byte* data, size_t dataSize); + std::vector& getRawDataForDirectWriting(); void addRawSceneActionInformation(ESceneActionId type, uint32_t offset); // blob read access - [[nodiscard]] const std::vector& collectionData() const; + [[nodiscard]] const std::vector& collectionData() const; // reading class Iterator; @@ -112,7 +111,7 @@ namespace ramses_internal public: [[nodiscard]] ESceneActionId type() const; [[nodiscard]] uint32_t size() const; - [[nodiscard]] const Byte* data() const; + [[nodiscard]] const std::byte* data() const; [[nodiscard]] uint32_t offsetInCollection() const; // concrete types @@ -124,9 +123,9 @@ namespace ramses_internal template void read(StronglyTypedValue& value); template - void read(glm::vec&); + void read(glm::vec& value); template - void read(glm::mat&); + void read(glm::mat& value); template::value, int>::type = 0> void read(E& value); // fixed size array @@ -139,7 +138,7 @@ namespace ramses_internal template::value>::type> void read(T* data, uint32_t& numElements); // get pointer to written array of bytes and increment reader position - void readWithoutCopy(const Byte*& data, uint32_t& size); + void readWithoutCopy(const std::byte*& data, uint32_t& size); [[nodiscard]] bool isFullyRead() const; @@ -197,8 +196,8 @@ namespace ramses_internal ESceneActionId type; uint32_t offset; - bool operator!=(const ActionInfo&) const; - bool operator==(const ActionInfo&) const; + bool operator!=(const ActionInfo& rhs) const; + bool operator==(const ActionInfo& rhs) const; }; template::value>::type> @@ -206,15 +205,13 @@ namespace ramses_internal void writeAsByteBlob(const void* value, size_t size); void reserveAdditionalDataCapacity(size_t additionalCapacity); - std::vector m_data; + std::vector m_data; std::vector m_actionInfo; }; ASSERT_MOVABLE(SceneActionCollection) - inline SceneActionCollection::SceneActionCollection() - { - } + inline SceneActionCollection::SceneActionCollection() = default; inline SceneActionCollection::SceneActionCollection(size_t initialDataCapacity, size_t initialNumberOfSceneActionsInformationCapacity) { @@ -276,7 +273,7 @@ namespace ramses_internal else { // normal append - const uint32_t offsetBase = static_cast(m_data.size()); + const auto offsetBase = static_cast(m_data.size()); m_actionInfo.reserve(m_actionInfo.size() + other.m_actionInfo.size()); for (const auto info : other.m_actionInfo) { @@ -317,7 +314,7 @@ namespace ramses_internal inline void SceneActionCollection::write(std::string_view str) { // check for MaxStringLength - const uint8_t truncatedLength = static_cast(std::min(static_cast(MaxStringLength), str.size())); + const auto truncatedLength = static_cast(std::min(static_cast(MaxStringLength), str.size())); reserveAdditionalDataCapacity(sizeof(uint8_t) + truncatedLength); writeAsByteBlob(truncatedLength); @@ -377,7 +374,7 @@ namespace ramses_internal const uint32_t byteSize = numElements * sizeof(T); reserveAdditionalDataCapacity(sizeof(uint32_t) + byteSize); writeAsByteBlob(numElements); - writeAsByteBlob(data, byteSize); + writeAsByteBlob(static_cast(data), byteSize); } template @@ -394,17 +391,17 @@ namespace ramses_internal inline void SceneActionCollection::writeAsByteBlob(const void* value, size_t size) { - const Byte* valuePtr = static_cast(value); + const auto* valuePtr = static_cast(value); m_data.insert(m_data.end(), valuePtr, valuePtr + size); } // blob write access - inline void SceneActionCollection::appendRawData(const Byte* data, size_t dataSize) + inline void SceneActionCollection::appendRawData(const std::byte* data, size_t dataSize) { writeAsByteBlob(data, dataSize); } - inline std::vector& SceneActionCollection::getRawDataForDirectWriting() + inline std::vector& SceneActionCollection::getRawDataForDirectWriting() { return m_data; } @@ -415,7 +412,7 @@ namespace ramses_internal } // blob read access - inline const std::vector& SceneActionCollection::collectionData() const + inline const std::vector& SceneActionCollection::collectionData() const { return m_data; } @@ -480,7 +477,7 @@ namespace ramses_internal return offsetForIndex(m_actionIndex + 1) - m_collection->m_actionInfo[m_actionIndex].offset; } - inline const Byte* SceneActionCollection::SceneActionReader::data() const + inline const std::byte* SceneActionCollection::SceneActionReader::data() const { assert(m_collection && m_actionIndex < m_collection->m_actionInfo.size()); return m_collection->m_data.data() + m_collection->m_actionInfo[m_actionIndex].offset; @@ -519,7 +516,7 @@ namespace ramses_internal m_readPosition += byteSize; } - inline void SceneActionCollection::SceneActionReader::readWithoutCopy(const Byte*& data, uint32_t& size) + inline void SceneActionCollection::SceneActionReader::readWithoutCopy(const std::byte*& data, uint32_t& size) { // read array size readFromByteBlob(size); @@ -534,7 +531,7 @@ namespace ramses_internal inline void SceneActionCollection::SceneActionReader::read(Guid& guid) { - uint64_t data; + uint64_t data = 0u; readFromByteBlob(data); guid = Guid(data); } @@ -590,7 +587,7 @@ namespace ramses_internal inline void SceneActionCollection::SceneActionReader::readFromByteBlob(void* data, size_t size) { - PlatformMemory::Copy(static_cast(data), m_collection->m_data.data() + m_readPosition, size); + PlatformMemory::Copy(static_cast(data), m_collection->m_data.data() + m_readPosition, size); m_readPosition += size; } @@ -669,5 +666,3 @@ namespace ramses_internal return !(*this != rhs); } } - -#endif diff --git a/framework/SceneGraph/Scene/src/SceneActionCollectionCreator.cpp b/src/framework/internal/SceneGraph/Scene/SceneActionCollectionCreator.cpp similarity index 96% rename from framework/SceneGraph/Scene/src/SceneActionCollectionCreator.cpp rename to src/framework/internal/SceneGraph/Scene/SceneActionCollectionCreator.cpp index 0151506a8..ee53e2694 100644 --- a/framework/SceneGraph/Scene/src/SceneActionCollectionCreator.cpp +++ b/src/framework/internal/SceneGraph/Scene/SceneActionCollectionCreator.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneActionCollectionCreator.h" - -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/PixelRectangle.h" -#include "SceneAPI/Camera.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/Viewport.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/ERotationType.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" -#include "Resource/IResource.h" -#include "Components/SingleResourceSerialization.h" -#include "Components/FlushTimeInformation.h" -#include "Utils/RawBinaryOutputStream.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" + +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/PixelRectangle.h" +#include "internal/SceneGraph/SceneAPI/Camera.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/Viewport.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" +#include "internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/Components/SingleResourceSerialization.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Core/Utils/RawBinaryOutputStream.h" #include "glm/gtx/range.hpp" -namespace ramses_internal +namespace ramses::internal { SceneActionCollectionCreator::SceneActionCollectionCreator(SceneActionCollection& collection_) : collection(collection_) @@ -181,6 +181,14 @@ namespace ramses_internal collection.write(data, elementCount); } + void SceneActionCollectionCreator::setDataBooleanArray(DataInstanceHandle handle, DataFieldHandle field, uint32_t elementCount, const bool* data) + { + collection.beginWriteSceneAction(ESceneActionId::SetDataBooleanArray); + collection.write(handle); + collection.write(field); + collection.write(data, elementCount); + } + void SceneActionCollectionCreator::setDataIntegerArray(DataInstanceHandle handle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) { collection.beginWriteSceneAction(ESceneActionId::SetDataIntegerArray); @@ -661,9 +669,13 @@ namespace ramses_internal collection.write(sampler.states.m_anisotropyLevel); collection.write(sampler.contentType); if (sampler.contentType == TextureSampler::ContentType::ClientTexture) + { collection.write(sampler.textureResource); + } else + { collection.write(sampler.contentHandle); + } collection.write(std::string{}); collection.write(uint64_t{}); } @@ -701,7 +713,6 @@ namespace ramses_internal collection.write(renderBuffer.width); collection.write(renderBuffer.height); collection.write(handle); - collection.write(static_cast(renderBuffer.type)); collection.write(static_cast(renderBuffer.format)); collection.write(static_cast(renderBuffer.accessMode)); collection.write(renderBuffer.sampleCount); @@ -732,7 +743,7 @@ namespace ramses_internal collection.write(handle); } - void SceneActionCollectionCreator::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) + void SceneActionCollectionCreator::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) { collection.beginWriteSceneAction(ESceneActionId::UpdateDataBuffer); collection.write(handle); @@ -740,7 +751,7 @@ namespace ramses_internal collection.write(data, dataSizeInBytes); } - void SceneActionCollectionCreator::allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) + void SceneActionCollectionCreator::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) { collection.beginWriteSceneAction(ESceneActionId::AllocateTextureBuffer); collection.write(static_cast(textureFormat)); @@ -761,7 +772,7 @@ namespace ramses_internal collection.write(handle); } - void SceneActionCollectionCreator::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data, uint32_t dataSize) + void SceneActionCollectionCreator::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data, uint32_t dataSize) { collection.beginWriteSceneAction(ESceneActionId::UpdateTextureBuffer); collection.write(handle); @@ -841,11 +852,11 @@ namespace ramses_internal collection.write(clearColor); } - void SceneActionCollectionCreator::setRenderPassClearFlag(RenderPassHandle handle, uint32_t clearFlag) + void SceneActionCollectionCreator::setRenderPassClearFlag(RenderPassHandle handle, ClearFlags clearFlag) { collection.beginWriteSceneAction(ESceneActionId::SetRenderPassClearFlag); collection.write(handle); - collection.write(clearFlag); + collection.write(clearFlag.value()); } void SceneActionCollectionCreator::compoundRenderableData( diff --git a/framework/SceneGraph/Scene/include/Scene/SceneActionCollectionCreator.h b/src/framework/internal/SceneGraph/Scene/SceneActionCollectionCreator.h similarity index 87% rename from framework/SceneGraph/Scene/include/Scene/SceneActionCollectionCreator.h rename to src/framework/internal/SceneGraph/Scene/SceneActionCollectionCreator.h index e45ba1b43..303c1e6f2 100644 --- a/framework/SceneGraph/Scene/include/Scene/SceneActionCollectionCreator.h +++ b/src/framework/internal/SceneGraph/Scene/SceneActionCollectionCreator.h @@ -6,34 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEACTIONCOLLECTIONCREATOR_H -#define RAMSES_SCENEACTIONCOLLECTIONCREATOR_H - -#include "Scene/SceneActionCollection.h" -#include "SceneAPI/ERenderableDataSlotType.h" -#include "SceneAPI/RenderState.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EFixedSemantics.h" -#include "SceneAPI/ECameraProjectionType.h" -#include "SceneAPI/TextureEnums.h" -#include "SceneAPI/EDataSlotType.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/SceneVersionTag.h" -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/DataFieldInfo.h" -#include "SceneAPI/MipMapSize.h" -#include "SceneAPI/PickableObject.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/RendererSceneState.h" -#include "SceneAPI/ERotationType.h" -#include "Scene/ResourceChanges.h" -#include "Resource/TextureMetaInfo.h" -#include "Components/FlushTimeInformation.h" -#include "SceneReferencing/SceneReferenceAction.h" - - -namespace ramses_internal +#pragma once + +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/SceneGraph/SceneAPI/ERenderableDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" +#include "internal/SceneGraph/SceneAPI/ECameraProjectionType.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/EDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/DataFieldInfo.h" +#include "internal/SceneGraph/SceneAPI/MipMapSize.h" +#include "internal/SceneGraph/SceneAPI/PickableObject.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" + + +namespace ramses::internal { struct PixelRectangle; class IResource; @@ -111,6 +110,7 @@ namespace ramses_internal void setDataVector2fArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data); void setDataVector3fArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data); void setDataVector4fArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data); + void setDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data); void setDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data); void setDataVector2iArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data); void setDataVector3iArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data); @@ -138,7 +138,7 @@ namespace ramses_internal void allocateRenderPass(uint32_t renderGroupCount, RenderPassHandle passHandle); void releaseRenderPass(RenderPassHandle passHandle); void setRenderPassClearColor(RenderPassHandle passHandle, const glm::vec4& clearColor); - void setRenderPassClearFlag(RenderPassHandle passHandle, uint32_t clearFlag); + void setRenderPassClearFlag(RenderPassHandle passHandle, ClearFlags clearFlag); void setRenderPassCamera(RenderPassHandle passHandle, CameraHandle cameraHandle); void setRenderPassRenderTarget(RenderPassHandle passHandle, RenderTargetHandle targetHandle); void setRenderPassRenderOrder(RenderPassHandle passHandle, int32_t renderOrder); @@ -174,12 +174,12 @@ namespace ramses_internal // Data buffers void allocateDataBuffer(EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle); void releaseDataBuffer(DataBufferHandle handle); - void updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data); + void updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data); // Texture buffers - void allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle); + void allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle); void releaseTextureBuffer(TextureBufferHandle handle); - void updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data, uint32_t dataSize); + void updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data, uint32_t dataSize); void allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle); void setDataSlotTexture(DataSlotHandle handle, const ResourceContentHash& texture); @@ -206,5 +206,3 @@ namespace ramses_internal void putSceneSizeInformation(const SceneSizeInformation& sizeInfo); }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/SceneDescriber.cpp b/src/framework/internal/SceneGraph/Scene/SceneDescriber.cpp similarity index 95% rename from framework/SceneGraph/Scene/src/SceneDescriber.cpp rename to src/framework/internal/SceneGraph/Scene/SceneDescriber.cpp index ef5f78e86..0245d3cad 100644 --- a/framework/SceneGraph/Scene/src/SceneDescriber.cpp +++ b/src/framework/internal/SceneGraph/Scene/SceneDescriber.cpp @@ -6,17 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneDescriber.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "Scene/ClientScene.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/RenderGroup.h" -#include "SceneAPI/PixelRectangle.h" -#include "SceneAPI/TextureSamplerStates.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/MemoryUtils.h" - -namespace ramses_internal +#include "internal/SceneGraph/Scene/SceneDescriber.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/RenderGroup.h" +#include "internal/SceneGraph/SceneAPI/PixelRectangle.h" +#include "internal/SceneGraph/SceneAPI/TextureSamplerStates.h" +#include "internal/Core/Utils/MemoryUtils.h" + +#include + +namespace ramses::internal { template void SceneDescriber::describeScene(const T& source, SceneActionCollectionCreator& collector) @@ -263,6 +264,15 @@ namespace ramses_internal } break; } + case EDataType::Bool: + { + const bool* value = source.getDataBooleanArray(i, f); + if (!MemoryUtils::AreAllBytesZero(value, elementCount)) + { + collector.setDataBooleanArray(i, f, elementCount, value); + } + break; + } case EDataType::Int32: { const int32_t* value = source.getDataIntegerArray(i, f); @@ -427,7 +437,7 @@ namespace ramses_internal void SceneDescriber::RecreateTextureBuffers(const IScene& source, SceneActionCollectionCreator& collector) { - std::vector tempForCopyingUsedTextureDataBuffer; + std::vector tempForCopyingUsedTextureDataBuffer; const uint32_t textureBufferTotalCount = source.getTextureBufferCount(); for (TextureBufferHandle textureBufferHandle(0u); textureBufferHandle < textureBufferTotalCount; ++textureBufferHandle) @@ -459,9 +469,9 @@ namespace ramses_internal const auto& mipMapSize = mipMapDimensions[mipMapLevel]; const uint32_t usedDataSize = usedRegion.width * usedRegion.height * texelSize; - const Byte* mipMapData = mip.data.data(); + const std::byte* mipMapData = mip.data.data(); - const Byte* updatedData = mipMapData; + const std::byte* updatedData = mipMapData; //Iff the width of used region is different from (smaller than) the width of the mip map level //then the data of the used region must be copied from the mip map data into an intermediate (temp) @@ -481,8 +491,8 @@ namespace ramses_internal const uint32_t dataRowSize = usedRegion.width * texelSize; const uint32_t mipLevelRowSize = mipMapSize.width * texelSize; - const Byte* sourcePtr = mipMapData + usedRegion.x * texelSize + usedRegion.y * mipLevelRowSize; - Byte* destinationPtr = tempForCopyingUsedTextureDataBuffer.data(); + const std::byte* sourcePtr = mipMapData + usedRegion.x * texelSize + usedRegion.y * mipLevelRowSize; + std::byte* destinationPtr = tempForCopyingUsedTextureDataBuffer.data(); for (int32_t i = 0; i < usedRegion.height; ++i) { PlatformMemory::Copy(destinationPtr, sourcePtr, dataRowSize); diff --git a/framework/SceneGraph/Scene/include/Scene/SceneDescriber.h b/src/framework/internal/SceneGraph/Scene/SceneDescriber.h similarity index 95% rename from framework/SceneGraph/Scene/include/Scene/SceneDescriber.h rename to src/framework/internal/SceneGraph/Scene/SceneDescriber.h index 02f207d86..c0b902dc5 100644 --- a/framework/SceneGraph/Scene/include/Scene/SceneDescriber.h +++ b/src/framework/internal/SceneGraph/Scene/SceneDescriber.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEDESCRIBER_H -#define RAMSES_SCENEDESCRIBER_H +#pragma once -#include "Scene/Scene.h" +#include "internal/SceneGraph/Scene/Scene.h" -namespace ramses_internal +namespace ramses::internal { class ClientScene; class SceneActionCollectionCreator; @@ -48,5 +47,3 @@ namespace ramses_internal static void RecreateSceneReferences(const IScene& source, SceneActionCollectionCreator& collector); }; } - -#endif diff --git a/framework/SceneGraph/Scene/src/ScenePersistation.cpp b/src/framework/internal/SceneGraph/Scene/ScenePersistation.cpp similarity index 88% rename from framework/SceneGraph/Scene/src/ScenePersistation.cpp rename to src/framework/internal/SceneGraph/Scene/ScenePersistation.cpp index 26d5cbaaa..db6dc2c29 100644 --- a/framework/SceneGraph/Scene/src/ScenePersistation.cpp +++ b/src/framework/internal/SceneGraph/Scene/ScenePersistation.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/ScenePersistation.h" -#include "SceneAPI/SceneCreationInformation.h" -#include "Scene/SceneActionApplier.h" -#include "Scene/SceneDescriber.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "Utils/File.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/LogMacros.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/ScenePersistation.h" +#include "internal/SceneGraph/SceneAPI/SceneCreationInformation.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Scene/SceneDescriber.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include -namespace ramses_internal +namespace ramses::internal { static const uint32_t gSceneMarker = 0x534d4152; // {'R', 'A', 'M', 'S'} @@ -79,7 +79,7 @@ namespace ramses_internal creator.preallocateSceneSize(scene.getSceneSizeInformation()); SceneDescriber::describeScene(scene, creator); - const std::vector& actionData = collection.collectionData(); + const std::vector& actionData = collection.collectionData(); outStream << static_cast(gSceneMarker); outStream << static_cast(collection.numberOfActions()); @@ -129,7 +129,7 @@ namespace ramses_internal SceneActionCollection actions(0, numberOfSceneActionsToRead); // read data - std::vector& rawActionData = actions.getRawDataForDirectWriting(); + std::vector& rawActionData = actions.getRawDataForDirectWriting(); rawActionData.resize(sizeOfAllSceneActions); inStream.read(rawActionData.data(), rawActionData.size()); @@ -146,7 +146,7 @@ namespace ramses_internal ++objectCounts[actionType]; } - LOG_DEBUG_F(ramses_internal::CONTEXT_PROFILING, ([&](ramses_internal::StringOutputStream& sos) { + LOG_DEBUG_F(CONTEXT_PROFILING, ([&](StringOutputStream& sos) { sos << "ScenePersistation::ReadSceneFromStream: SceneAction type counts for SceneID " << scene.getSceneId() << " (total: " << numberOfSceneActionsToRead << ")\n"; for (uint32_t i = 0; i < NumOfSceneActionTypes; i++) { diff --git a/framework/SceneGraph/Scene/include/Scene/ScenePersistation.h b/src/framework/internal/SceneGraph/Scene/ScenePersistation.h similarity index 87% rename from framework/SceneGraph/Scene/include/Scene/ScenePersistation.h rename to src/framework/internal/SceneGraph/Scene/ScenePersistation.h index c9f32b489..3ad01fde1 100644 --- a/framework/SceneGraph/Scene/include/Scene/ScenePersistation.h +++ b/src/framework/internal/SceneGraph/Scene/ScenePersistation.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEPERSISTATION_H -#define RAMSES_SCENEPERSISTATION_H +#pragma once -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" -namespace ramses_internal +namespace ramses::internal { class ClientScene; class IOutputStream; @@ -31,5 +30,3 @@ namespace ramses_internal static void ReadSceneFromFile(std::string_view filename, IScene& scene); }; } - -#endif diff --git a/framework/SceneGraph/Scene/include/Scene/TopologyNode.h b/src/framework/internal/SceneGraph/Scene/TopologyNode.h similarity index 73% rename from framework/SceneGraph/Scene/include/Scene/TopologyNode.h rename to src/framework/internal/SceneGraph/Scene/TopologyNode.h index 59747a802..e663a6bd2 100644 --- a/framework/SceneGraph/Scene/include/Scene/TopologyNode.h +++ b/src/framework/internal/SceneGraph/Scene/TopologyNode.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_TOPOLOGYNODE_H -#define RAMSES_INTERNAL_TOPOLOGYNODE_H +#pragma once -#include "SceneAPI/SceneTypes.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/AssertMovable.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +#include + +namespace ramses::internal { struct TopologyNode { @@ -23,5 +23,3 @@ namespace ramses_internal ASSERT_MOVABLE(TopologyNode) } - -#endif diff --git a/framework/SceneGraph/Scene/include/Scene/TopologyTransform.h b/src/framework/internal/SceneGraph/Scene/TopologyTransform.h similarity index 77% rename from framework/SceneGraph/Scene/include/Scene/TopologyTransform.h rename to src/framework/internal/SceneGraph/Scene/TopologyTransform.h index cc92c84a1..8d7064e1c 100644 --- a/framework/SceneGraph/Scene/include/Scene/TopologyTransform.h +++ b/src/framework/internal/SceneGraph/Scene/TopologyTransform.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TOPOLOGYTRANSFORM_H -#define RAMSES_TOPOLOGYTRANSFORM_H +#pragma once -#include "SceneAPI/Handles.h" -#include "SceneAPI/ERotationType.h" -#include "SceneAPI/IScene.h" -#include "Utils/AssertMovable.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +namespace ramses::internal { struct TopologyTransform { @@ -28,5 +27,3 @@ namespace ramses_internal ASSERT_MOVABLE(TopologyTransform) } - -#endif diff --git a/framework/SceneGraph/Scene/src/TransformationCachedScene.cpp b/src/framework/internal/SceneGraph/Scene/TransformationCachedScene.cpp similarity index 95% rename from framework/SceneGraph/Scene/src/TransformationCachedScene.cpp rename to src/framework/internal/SceneGraph/Scene/TransformationCachedScene.cpp index 790e9757e..cee98ccd9 100644 --- a/framework/SceneGraph/Scene/src/TransformationCachedScene.cpp +++ b/src/framework/internal/SceneGraph/Scene/TransformationCachedScene.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/TransformationCachedScene.h" -#include "Utils/MemoryPoolExplicit.h" -#include "Utils/MemoryPool.h" -#include "Math3d/Rotation.h" +#include "internal/SceneGraph/Scene/TransformationCachedScene.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" +#include "internal/Core/Utils/MemoryPool.h" +#include "internal/Core/Math3d/Rotation.h" #include "glm/gtx/transform.hpp" namespace @@ -17,7 +17,7 @@ namespace const auto Identity = glm::identity(); } -namespace ramses_internal +namespace ramses::internal { template class MEMORYPOOL> TransformationCachedSceneT::TransformationCachedSceneT(const SceneInfo& sceneInfo) @@ -132,11 +132,8 @@ namespace ramses_internal { return cacheEntry.m_matrix[matrixType]; } - else - { - dirtyNodes.push_back(currentNode); - currentNode = SceneT::getParent(currentNode); - } + dirtyNodes.push_back(currentNode); + currentNode = SceneT::getParent(currentNode); } return Identity; @@ -221,7 +218,7 @@ namespace ramses_internal } template class MEMORYPOOL> - bool ramses_internal::TransformationCachedSceneT::isMatrixCacheDirty(ETransformationMatrixType matrixType, NodeHandle node) const + bool ramses::internal::TransformationCachedSceneT::isMatrixCacheDirty(ETransformationMatrixType matrixType, NodeHandle node) const { return getMatrixCacheEntry(node).m_matrixDirty[matrixType]; } diff --git a/framework/SceneGraph/Scene/include/Scene/TransformationCachedScene.h b/src/framework/internal/SceneGraph/Scene/TransformationCachedScene.h similarity index 90% rename from framework/SceneGraph/Scene/include/Scene/TransformationCachedScene.h rename to src/framework/internal/SceneGraph/Scene/TransformationCachedScene.h index 91819dd24..5c774c3e2 100644 --- a/framework/SceneGraph/Scene/include/Scene/TransformationCachedScene.h +++ b/src/framework/internal/SceneGraph/Scene/TransformationCachedScene.h @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRANSFORMATIONCACHEDSCENE_H -#define RAMSES_TRANSFORMATIONCACHEDSCENE_H +#pragma once -#include "Scene/Scene.h" -#include "Scene/MatrixCacheEntry.h" -#include "Utils/MemoryPool.h" -#include "Utils/MemoryPoolExplicit.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/SceneGraph/Scene/Scene.h" +#include "internal/SceneGraph/Scene/MatrixCacheEntry.h" +#include "internal/Core/Utils/MemoryPool.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" -namespace ramses_internal +#include + +namespace ramses::internal { template class MEMORYPOOL> class TransformationCachedSceneT; @@ -32,13 +32,13 @@ namespace ramses_internal void preallocateSceneSize(const SceneSizeInformation& sizeInfo) override; // From IScene - NodeHandle allocateNode(uint32_t childrenCount = 0u, NodeHandle node = NodeHandle::Invalid()) override; + NodeHandle allocateNode(uint32_t childrenCount, NodeHandle node) override; void releaseNode(NodeHandle node) override; void addChildToNode(NodeHandle parent, NodeHandle child) override; void removeChildFromNode(NodeHandle parent, NodeHandle child) override; - TransformHandle allocateTransform(NodeHandle nodeHandle, TransformHandle handle = TransformHandle::Invalid()) override; + TransformHandle allocateTransform(NodeHandle nodeHandle, TransformHandle handle) override; void releaseTransform(TransformHandle transform) override; void setTranslation(TransformHandle transform, const glm::vec3& translation) override; @@ -78,5 +78,3 @@ namespace ramses_internal mutable NodeHandleVector m_dirtyNodes; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/BlitPass.h b/src/framework/internal/SceneGraph/SceneAPI/BlitPass.h similarity index 77% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/BlitPass.h rename to src/framework/internal/SceneGraph/SceneAPI/BlitPass.h index 4ba54dca2..f5fb5c585 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/BlitPass.h +++ b/src/framework/internal/SceneGraph/SceneAPI/BlitPass.h @@ -6,25 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_BLITPASS_H -#define RAMSES_INTERNAL_BLITPASS_H +#pragma once -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/PixelRectangle.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/PixelRectangle.h" -namespace ramses_internal +namespace ramses::internal { struct BlitPass { bool isEnabled = true; - int32_t renderOrder = 0; + int32_t renderOrder = 0; RenderBufferHandle sourceRenderBuffer; RenderBufferHandle destinationRenderBuffer; PixelRectangle sourceRegion; PixelRectangle destinationRegion; }; } - -#endif - - diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/Camera.h b/src/framework/internal/SceneGraph/SceneAPI/Camera.h similarity index 82% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/Camera.h rename to src/framework/internal/SceneGraph/SceneAPI/Camera.h index 52527ecb2..cf94693b7 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/Camera.h +++ b/src/framework/internal/SceneGraph/SceneAPI/Camera.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_CAMERA_H -#define RAMSES_SCENEAPI_CAMERA_H +#pragma once -#include "SceneAPI/Handles.h" -#include "SceneAPI/Viewport.h" -#include "SceneAPI/ECameraProjectionType.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Viewport.h" +#include "internal/SceneGraph/SceneAPI/ECameraProjectionType.h" -namespace ramses_internal +namespace ramses::internal { struct Camera { @@ -27,5 +26,3 @@ namespace ramses_internal static constexpr DataFieldHandle FrustumNearFarPlanesField{ 3 }; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/DataFieldInfo.h b/src/framework/internal/SceneGraph/SceneAPI/DataFieldInfo.h similarity index 86% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/DataFieldInfo.h rename to src/framework/internal/SceneGraph/SceneAPI/DataFieldInfo.h index 0fdbee3e7..def09b4d8 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/DataFieldInfo.h +++ b/src/framework/internal/SceneGraph/SceneAPI/DataFieldInfo.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAFIELDINFO_H -#define RAMSES_DATAFIELDINFO_H +#pragma once -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EFixedSemantics.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { struct DataFieldInfo { @@ -43,5 +42,3 @@ namespace ramses_internal using DataFieldInfoVector = std::vector; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/DataSlot.h b/src/framework/internal/SceneGraph/SceneAPI/DataSlot.h similarity index 68% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/DataSlot.h rename to src/framework/internal/SceneGraph/SceneAPI/DataSlot.h index fe00458dd..a2ef78768 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/DataSlot.h +++ b/src/framework/internal/SceneGraph/SceneAPI/DataSlot.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATASLOT_H -#define RAMSES_DATASLOT_H +#pragma once -#include "SceneAPI/EDataSlotType.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Common/StronglyTypedValue.h" +#include "internal/SceneGraph/SceneAPI/EDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/Core/Common/StronglyTypedValue.h" -namespace ramses_internal +namespace ramses::internal { struct DataSlotIdTag {}; using DataSlotId = StronglyTypedValue; struct DataSlot { - EDataSlotType type; + EDataSlotType type{EDataSlotType::Undefined}; DataSlotId id; NodeHandle attachedNode; DataInstanceHandle attachedDataReference; @@ -30,6 +29,4 @@ namespace ramses_internal }; } -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::DataSlotId) - -#endif +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::DataSlotId) diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/ECameraProjectionType.h b/src/framework/internal/SceneGraph/SceneAPI/ECameraProjectionType.h similarity index 62% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/ECameraProjectionType.h rename to src/framework/internal/SceneGraph/SceneAPI/ECameraProjectionType.h index e988b3acc..dbf166ad9 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/ECameraProjectionType.h +++ b/src/framework/internal/SceneGraph/SceneAPI/ECameraProjectionType.h @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ECAMERAPROJECTIONTYPE_H -#define RAMSES_ECAMERAPROJECTIONTYPE_H +#pragma once -#include "Utils/LoggingUtils.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { enum class ECameraProjectionType { @@ -26,9 +24,7 @@ namespace ramses_internal }; } -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::ECameraProjectionType, +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::ECameraProjectionType, "ECameraProjectionType", - ramses_internal::ECameraProjectionTypeNames, - ramses_internal::ECameraProjectionType::Orthographic); - -#endif + ramses::internal::ECameraProjectionTypeNames, + ramses::internal::ECameraProjectionType::Orthographic); diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataBufferType.h b/src/framework/internal/SceneGraph/SceneAPI/EDataBufferType.h similarity index 76% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/EDataBufferType.h rename to src/framework/internal/SceneGraph/SceneAPI/EDataBufferType.h index 174bcb9f3..d4179d6c3 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataBufferType.h +++ b/src/framework/internal/SceneGraph/SceneAPI/EDataBufferType.h @@ -6,21 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_EDATABUFFERTYPE_H -#define RAMSES_FRAMEWORK_EDATABUFFERTYPE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +#include + +namespace ramses::internal { enum class EDataBufferType : uint8_t { Invalid = 0, IndexBuffer, VertexBuffer, - - NUMBER_OF_ELEMENTS }; const std::array DataBufferTypeNames = @@ -30,7 +28,5 @@ namespace ramses_internal "EDataBufferType::VertexBuffer", }; - ENUM_TO_STRING(EDataBufferType, DataBufferTypeNames, EDataBufferType::NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(EDataBufferType, DataBufferTypeNames, EDataBufferType::VertexBuffer); } - -#endif diff --git a/src/framework/internal/SceneGraph/SceneAPI/EDataSlotType.h b/src/framework/internal/SceneGraph/SceneAPI/EDataSlotType.h new file mode 100644 index 000000000..2be5e0b47 --- /dev/null +++ b/src/framework/internal/SceneGraph/SceneAPI/EDataSlotType.h @@ -0,0 +1,40 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/Utils/LoggingUtils.h" + +#include + +namespace ramses::internal +{ + enum class EDataSlotType + { + TransformationProvider = 0, + TransformationConsumer, + DataProvider, + DataConsumer, + TextureProvider, + TextureConsumer, + Undefined, + }; + + const std::array DataSlotTypeNames = + { + "TransformationProvider", + "TransformationConsumer", + "DataProvider", + "DataConsumer", + "TextureProvider", + "TextureConsumer", + "Undefined" + }; + + ENUM_TO_STRING(EDataSlotType, DataSlotTypeNames, EDataSlotType::Undefined); +} diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataType.h b/src/framework/internal/SceneGraph/SceneAPI/EDataType.h similarity index 94% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/EDataType.h rename to src/framework/internal/SceneGraph/SceneAPI/EDataType.h index 768c39347..fddcac88e 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/EDataType.h +++ b/src/framework/internal/SceneGraph/SceneAPI/EDataType.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_EDATATYPE_H -#define RAMSES_SCENEAPI_EDATATYPE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/ResourceField.h" -#include "SceneAPI/Handles.h" -#include "Utils/LoggingUtils.h" -#include "Utils/Warnings.h" -#include "DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/ResourceField.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/Core/Utils/Warnings.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +#include + +namespace ramses::internal { WARNINGS_PUSH @@ -26,6 +26,7 @@ WARNING_DISABLE_GCC(-Wshadow) { Invalid = 0, + Bool, Int32, UInt16, UInt32, @@ -60,8 +61,6 @@ WARNING_DISABLE_GCC(-Wshadow) ByteBlob, TextureSamplerExternal, - - NUMBER_OF_ELEMENTS // must be last, used for checking }; WARNINGS_POP @@ -69,6 +68,7 @@ WARNINGS_POP const std::array DataTypeNames = { "DATATYPE_INVALID", + "DATATYPE_BOOL", "DATATYPE_INT32", "DATATYPE_UINT16", "DATATYPE_UINT32", @@ -97,12 +97,13 @@ WARNINGS_POP "DATATYPE_TEXTURESAMPLEREXTERNAL" }; - ENUM_TO_STRING(EDataType, DataTypeNames, EDataType::NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(EDataType, DataTypeNames, EDataType::TextureSamplerExternal); inline constexpr uint32_t EnumToNumComponents(EDataType type) { switch (type) { + case EDataType::Bool: case EDataType::Int32: case EDataType::UInt16: case EDataType::UInt32: @@ -152,6 +153,7 @@ WARNINGS_POP { switch (type) { + case EDataType::Bool : return sizeof(bool); case EDataType::Int32 : return sizeof(int32_t); case EDataType::UInt16 : return sizeof(uint16_t); case EDataType::UInt32 : return sizeof(uint32_t); @@ -181,7 +183,6 @@ WARNINGS_POP case EDataType::Vector4Buffer : return sizeof(ResourceField); case EDataType::Invalid: - case EDataType::NUMBER_OF_ELEMENTS: break; }; @@ -193,6 +194,7 @@ WARNINGS_POP { switch (type) { + case EDataType::Bool : return alignof(bool); case EDataType::Int32 : return alignof(int32_t); case EDataType::UInt16 : return alignof(uint16_t); case EDataType::UInt32 : return alignof(uint32_t); @@ -249,6 +251,12 @@ WARNINGS_POP { }; + template <> + struct TypeToEDataTypeTraits < bool > + { + static const EDataType DataType = EDataType::Bool; + }; + template <> struct TypeToEDataTypeTraits < int32_t > { @@ -351,5 +359,3 @@ WARNINGS_POP return IsBufferDataType(dataType); } } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/EFixedSemantics.h b/src/framework/internal/SceneGraph/SceneAPI/EFixedSemantics.h similarity index 86% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/EFixedSemantics.h rename to src/framework/internal/SceneGraph/SceneAPI/EFixedSemantics.h index f42fc2514..e7af23b3c 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/EFixedSemantics.h +++ b/src/framework/internal/SceneGraph/SceneAPI/EFixedSemantics.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EFFECT_EFIXEDSEMANTICS_H -#define RAMSES_EFFECT_EFIXEDSEMANTICS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/EDataType.h" -#include "Utils/LoggingUtils.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +#include + +namespace ramses::internal { enum class EFixedSemantics { @@ -94,9 +94,7 @@ namespace ramses_internal } } -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::EFixedSemantics, +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EFixedSemantics, "EFixedSemantics", - ramses_internal::EFixedSemanticsNames, - ramses_internal::EFixedSemantics::TimeMs); - -#endif + ramses::internal::EFixedSemanticsNames, + ramses::internal::EFixedSemantics::TimeMs); diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/ERenderableDataSlotType.h b/src/framework/internal/SceneGraph/SceneAPI/ERenderableDataSlotType.h similarity index 71% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/ERenderableDataSlotType.h rename to src/framework/internal/SceneGraph/SceneAPI/ERenderableDataSlotType.h index b8b38c5aa..88211d781 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/ERenderableDataSlotType.h +++ b/src/framework/internal/SceneGraph/SceneAPI/ERenderableDataSlotType.h @@ -6,19 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_ERENDERABLEDATASLOTTYPE_H -#define RAMSES_SCENEAPI_ERENDERABLEDATASLOTTYPE_H +#pragma once -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { enum ERenderableDataSlotType { ERenderableDataSlotType_Geometry = 0, ERenderableDataSlotType_Uniforms, - - ERenderableDataSlotType_MAX_SLOTS // must be last, used for checking }; @@ -28,8 +25,9 @@ namespace ramses_internal "Uniforms", }; - ENUM_TO_STRING(ERenderableDataSlotType, RenderableDataSlotTypeNames, ERenderableDataSlotType_MAX_SLOTS); + const size_t ERenderableDataSlotType_MAX_SLOTS = 2u; + static_assert(EnumTraits::VerifyElementCountIfSupported(ERenderableDataSlotType_MAX_SLOTS)); -} + ENUM_TO_STRING(ERenderableDataSlotType, RenderableDataSlotTypeNames, ERenderableDataSlotType_Uniforms); -#endif +} diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/ERotationType.h b/src/framework/internal/SceneGraph/SceneAPI/ERotationType.h similarity index 57% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/ERotationType.h rename to src/framework/internal/SceneGraph/SceneAPI/ERotationType.h index 6bc485aa2..4071e5ca8 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/ERotationType.h +++ b/src/framework/internal/SceneGraph/SceneAPI/ERotationType.h @@ -8,27 +8,14 @@ #pragma once -#include "Utils/LoggingUtils.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "ramses/framework/ERotationType.h" -namespace ramses_internal +#include + +namespace ramses::internal { - enum class ERotationType : uint8_t - { - Euler_XYZ, - Euler_XZY, - Euler_YXZ, - Euler_YZX, - Euler_ZXY, - Euler_ZYX, - Euler_XYX, - Euler_XZX, - Euler_YXY, - Euler_YZY, - Euler_ZXZ, - Euler_ZYZ, - Quaternion, - }; + using ramses::ERotationType; const std::array ERotationTypeNames = { "Euler_XYZ", @@ -47,8 +34,8 @@ namespace ramses_internal }; } -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::ERotationType, +MAKE_ENUM_CLASS_PRINTABLE(ramses::ERotationType, "ERotationType", - ramses_internal::ERotationTypeNames, - ramses_internal::ERotationType::Quaternion); + ramses::internal::ERotationTypeNames, + ramses::ERotationType::Quaternion); diff --git a/src/framework/internal/SceneGraph/SceneAPI/EShaderStage.h b/src/framework/internal/SceneGraph/SceneAPI/EShaderStage.h new file mode 100644 index 000000000..e9b462c1f --- /dev/null +++ b/src/framework/internal/SceneGraph/SceneAPI/EShaderStage.h @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/Core/Utils/LoggingUtils.h" + +#include + +namespace ramses::internal +{ + enum class EShaderStage + { + Vertex = 0, + Fragment, + Geometry, + }; + + const std::array EShaderStageNames = + { + "Vertex", + "Fragment", + "Geometry", + }; +} + +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EShaderStage, + "EShaderStage", + ramses::internal::EShaderStageNames, + ramses::internal::EShaderStage::Geometry); + diff --git a/src/framework/internal/SceneGraph/SceneAPI/EShaderWarningCategory.h b/src/framework/internal/SceneGraph/SceneAPI/EShaderWarningCategory.h new file mode 100644 index 000000000..42c37659d --- /dev/null +++ b/src/framework/internal/SceneGraph/SceneAPI/EShaderWarningCategory.h @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/Utils/LoggingUtils.h" + +namespace ramses::internal +{ + enum class EShaderWarningCategory + { + Unknown, + UnusedVarying, + UnusedUniform, + UnusedVariable, + InterfaceMismatch, + PrecisionMismatch, + }; + + const std::array EShaderWarningCategoryNames = { + "Unknown", + "UnusedVarying", + "UnusedUniform", + "UnusedVariable", + "InterfaceMismatch", + "PrecisionMismatch", + }; +} + +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EShaderWarningCategory, + "EShaderWarningCategory", + ramses::internal::EShaderWarningCategoryNames, + ramses::internal::EShaderWarningCategory::PrecisionMismatch); + diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/GeometryDataBuffer.h b/src/framework/internal/SceneGraph/SceneAPI/GeometryDataBuffer.h similarity index 70% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/GeometryDataBuffer.h rename to src/framework/internal/SceneGraph/SceneAPI/GeometryDataBuffer.h index 18c139ea5..196fcf4cc 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/GeometryDataBuffer.h +++ b/src/framework/internal/SceneGraph/SceneAPI/GeometryDataBuffer.h @@ -6,25 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_GEOMETRYDATABUFFER_H -#define RAMSES_INTERNAL_GEOMETRYDATABUFFER_H +#pragma once -#include "SceneAPI/EDataBufferType.h" -#include "SceneAPI/EDataType.h" -#include "Collections/Vector.h" -#include "Utils/AssertMovable.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +namespace ramses::internal { struct GeometryDataBuffer { EDataBufferType bufferType = EDataBufferType::Invalid; EDataType dataType = EDataType::Invalid; uint32_t usedSize = 0u; - std::vector data; + std::vector data; }; ASSERT_MOVABLE(GeometryDataBuffer) } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/Handles.h b/src/framework/internal/SceneGraph/SceneAPI/Handles.h similarity index 94% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/Handles.h rename to src/framework/internal/SceneGraph/SceneAPI/Handles.h index 7f8918bfc..690cff708 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/Handles.h +++ b/src/framework/internal/SceneGraph/SceneAPI/Handles.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_HANDLES_H -#define RAMSES_SCENEAPI_HANDLES_H +#pragma once -#include "Common/TypedMemoryHandle.h" +#include "internal/Core/Common/TypedMemoryHandle.h" -namespace ramses_internal +namespace ramses::internal { // Topology handles struct NodeHandleTag {}; @@ -71,5 +70,3 @@ namespace ramses_internal struct SceneReferenceHandleTag {}; using SceneReferenceHandle = TypedMemoryHandle; } - -#endif diff --git a/src/framework/internal/SceneGraph/SceneAPI/IScene.h b/src/framework/internal/SceneGraph/SceneAPI/IScene.h new file mode 100644 index 000000000..9d1e8c482 --- /dev/null +++ b/src/framework/internal/SceneGraph/SceneAPI/IScene.h @@ -0,0 +1,317 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/ERenderableDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/ECameraProjectionType.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/DataFieldInfo.h" +#include "internal/SceneGraph/SceneAPI/MipMapSize.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/SceneAPI/ERotationType.h" + +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Components/FlushTimeInformation.h" +#include "impl/DataTypesImpl.h" + +#include + +namespace ramses::internal +{ + class DataLayout; + struct SceneSizeInformation; + struct PixelRectangle; + struct Camera; + struct RenderPass; + struct RenderGroup; + struct TextureSampler; + struct TextureSamplerStates; + struct TextureBuffer; + struct GeometryDataBuffer; + struct RenderBuffer; + struct BlitPass; + struct PickableObject; + struct SceneReference; + struct TopologyTransform; + + class IScene + { + public: + IScene() = default; + virtual ~IScene() = default; + + // scene should never be copied or moved + IScene(const IScene&) = delete; + IScene& operator=(const IScene&) = delete; + IScene(IScene&&) = delete; + IScene& operator=(IScene&&) = delete; + + [[nodiscard]] virtual const std::string& getName () const = 0; + [[nodiscard]] virtual SceneId getSceneId () const = 0; + + virtual void setEffectTimeSync(FlushTime::Clock::time_point t) = 0; + [[nodiscard]] virtual FlushTime::Clock::time_point getEffectTimeSync() const = 0; + + virtual void preallocateSceneSize (const SceneSizeInformation& sizeInfo) = 0; + + // Renderable + virtual RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle) = 0; + virtual void releaseRenderable (RenderableHandle renderableHandle) = 0; + [[nodiscard]] virtual bool isRenderableAllocated (RenderableHandle renderableHandle) const = 0; + [[nodiscard]] virtual uint32_t getRenderableCount () const = 0; + virtual void setRenderableDataInstance (RenderableHandle renderableHandle, ERenderableDataSlotType slot, DataInstanceHandle newDataInstance) = 0; + virtual void setRenderableStartIndex (RenderableHandle renderableHandle, uint32_t startIndex) = 0; + virtual void setRenderableIndexCount (RenderableHandle renderableHandle, uint32_t indexCount) = 0; + virtual void setRenderableRenderState (RenderableHandle renderableHandle, RenderStateHandle stateHandle) = 0; + virtual void setRenderableVisibility (RenderableHandle renderableHandle, EVisibilityMode visibility) = 0; + virtual void setRenderableInstanceCount (RenderableHandle renderableHandle, uint32_t instanceCount) = 0; + virtual void setRenderableStartVertex (RenderableHandle renderableHandle, uint32_t startVertex) = 0; + [[nodiscard]] virtual const Renderable& getRenderable (RenderableHandle renderableHandle) const = 0; + + // Render state + virtual RenderStateHandle allocateRenderState (RenderStateHandle stateHandle) = 0; + virtual void releaseRenderState (RenderStateHandle stateHandle) = 0; + [[nodiscard]] virtual bool isRenderStateAllocated (RenderStateHandle stateHandle) const = 0; + [[nodiscard]] virtual uint32_t getRenderStateCount () const = 0; + virtual void setRenderStateBlendFactors (RenderStateHandle stateHandle, EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) = 0; + virtual void setRenderStateBlendOperations (RenderStateHandle stateHandle, EBlendOperation operationColor, EBlendOperation operationAlpha) = 0; + virtual void setRenderStateBlendColor (RenderStateHandle stateHandle, const glm::vec4& color) = 0; + virtual void setRenderStateCullMode (RenderStateHandle stateHandle, ECullMode cullMode) = 0; + virtual void setRenderStateDrawMode (RenderStateHandle stateHandle, EDrawMode drawMode) = 0; + virtual void setRenderStateDepthFunc (RenderStateHandle stateHandle, EDepthFunc func) = 0; + virtual void setRenderStateDepthWrite (RenderStateHandle stateHandle, EDepthWrite flag) = 0; + virtual void setRenderStateScissorTest (RenderStateHandle stateHandle, EScissorTest flag, const RenderState::ScissorRegion& region) = 0; + virtual void setRenderStateStencilFunc (RenderStateHandle stateHandle, EStencilFunc func, uint8_t ref, uint8_t mask) = 0; + virtual void setRenderStateStencilOps (RenderStateHandle stateHandle, EStencilOp sfail, EStencilOp dpfail, EStencilOp dppass) = 0; + virtual void setRenderStateColorWriteMask (RenderStateHandle stateHandle, ColorWriteMask colorMask) = 0; + [[nodiscard]] virtual const RenderState& getRenderState (RenderStateHandle stateHandle) const = 0; + + // Camera + virtual CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle) = 0; + virtual void releaseCamera (CameraHandle cameraHandle) = 0; + [[nodiscard]] virtual bool isCameraAllocated (CameraHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getCameraCount () const = 0; + [[nodiscard]] virtual const Camera& getCamera (CameraHandle cameraHandle) const = 0; + + // Nodes + virtual NodeHandle allocateNode (uint32_t childrenCount, NodeHandle handle) = 0; + virtual void releaseNode (NodeHandle nodeHandle) = 0; + [[nodiscard]] virtual bool isNodeAllocated (NodeHandle node) const = 0; + [[nodiscard]] virtual uint32_t getNodeCount () const = 0; + [[nodiscard]] virtual NodeHandle getParent (NodeHandle nodeHandle) const = 0; + virtual void addChildToNode (NodeHandle parent, NodeHandle child) = 0; + virtual void removeChildFromNode (NodeHandle parent, NodeHandle child) = 0; + [[nodiscard]] virtual uint32_t getChildCount (NodeHandle parent) const = 0; + [[nodiscard]] virtual NodeHandle getChild (NodeHandle parent, uint32_t childNumber) const = 0; + + // Transformation + constexpr static glm::vec3 IdentityTranslation = {0.f, 0.f, 0.f}; + constexpr static glm::vec4 IdentityRotation = {0.f, 0.f, 0.f, 1.f}; // zero rotation for both euler and quaternion + constexpr static glm::vec3 IdentityScaling = {1.f, 1.f, 1.f}; + + virtual TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle) = 0; + virtual void releaseTransform (TransformHandle transform) = 0; + [[nodiscard]] virtual bool isTransformAllocated (TransformHandle transformHandle) const = 0; + [[nodiscard]] virtual uint32_t getTransformCount () const = 0; + [[nodiscard]] virtual NodeHandle getTransformNode (TransformHandle handle) const = 0; + [[nodiscard]] virtual const glm::vec3& getTranslation (TransformHandle handle) const = 0; + [[nodiscard]] virtual const glm::vec4& getRotation (TransformHandle handle) const = 0; + [[nodiscard]] virtual ERotationType getRotationType (TransformHandle handle) const = 0; + [[nodiscard]] virtual const glm::vec3& getScaling (TransformHandle handle) const = 0; + virtual void setTranslation (TransformHandle handle, const glm::vec3& translation) = 0; + virtual void setRotation (TransformHandle handle, const glm::vec4& rotation, ERotationType rotationType) = 0; + virtual void setScaling (TransformHandle handle, const glm::vec3& scaling) = 0; + + virtual DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle) = 0; + virtual void releaseDataLayout (DataLayoutHandle layoutHandle) = 0; + [[nodiscard]] virtual bool isDataLayoutAllocated (DataLayoutHandle layoutHandle) const = 0; + [[nodiscard]] virtual uint32_t getDataLayoutCount () const = 0; + [[nodiscard]] virtual const DataLayout& getDataLayout (DataLayoutHandle layoutHandle) const = 0; + + virtual DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle) = 0; + virtual void releaseDataInstance (DataInstanceHandle containerHandle) = 0; + [[nodiscard]] virtual bool isDataInstanceAllocated (DataInstanceHandle containerHandle) const = 0; + [[nodiscard]] virtual uint32_t getDataInstanceCount () const = 0; + [[nodiscard]] virtual DataLayoutHandle getLayoutOfDataInstance (DataInstanceHandle containerHandle) const = 0; + + [[nodiscard]] virtual const float* getDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::vec2* getDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::vec3* getDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::vec4* getDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const bool* getDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const int32_t* getDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::mat2* getDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::mat3* getDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::mat4* getDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::ivec2* getDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::ivec3* getDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::ivec4* getDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const ResourceField& getDataResource (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual TextureSamplerHandle getDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual DataInstanceHandle getDataReference (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + + virtual void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) = 0; + virtual void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) = 0; + virtual void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) = 0; + virtual void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) = 0; + virtual void setDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) = 0; + virtual void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) = 0; + virtual void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) = 0; + virtual void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) = 0; + virtual void setDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec4* data) = 0; + virtual void setDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat2* data) = 0; + virtual void setDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat3* data) = 0; + virtual void setDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat4* data) = 0; + virtual void setDataResource (DataInstanceHandle containerHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) = 0; + virtual void setDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) = 0; + virtual void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) = 0; + + // get/setData*Array wrappers for elementCount == 1 + [[nodiscard]] virtual float getDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::vec2& getDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::vec3& getDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::vec4& getDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual bool getDataSingleBoolean (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual int32_t getDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::mat2& getDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::mat3& getDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::mat4& getDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::ivec2& getDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::ivec3& getDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + [[nodiscard]] virtual const glm::ivec4& getDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field) const = 0; + + virtual void setDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field, float data) = 0; + virtual void setDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec2& data) = 0; + virtual void setDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec3& data) = 0; + virtual void setDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec4& data) = 0; + virtual void setDataSingleBoolean (DataInstanceHandle containerHandle, DataFieldHandle field, bool data) = 0; + virtual void setDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) = 0; + virtual void setDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec2& data) = 0; + virtual void setDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec3& data) = 0; + virtual void setDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec4& data) = 0; + virtual void setDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat2& data) = 0; + virtual void setDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat3& data) = 0; + virtual void setDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat4& data) = 0; + + // Texture sampler description + virtual TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle) = 0; + virtual void releaseTextureSampler (TextureSamplerHandle handle) = 0; + [[nodiscard]] virtual bool isTextureSamplerAllocated (TextureSamplerHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getTextureSamplerCount () const = 0; + [[nodiscard]] virtual const TextureSampler& getTextureSampler (TextureSamplerHandle handle) const = 0; + + // Render groups + virtual RenderGroupHandle allocateRenderGroup (uint32_t renderableCount, uint32_t nestedGroupCount, RenderGroupHandle groupHandle) = 0; + virtual void releaseRenderGroup (RenderGroupHandle groupHandle) = 0; + [[nodiscard]] virtual bool isRenderGroupAllocated (RenderGroupHandle groupHandle) const = 0; + [[nodiscard]] virtual uint32_t getRenderGroupCount () const = 0; + virtual void addRenderableToRenderGroup (RenderGroupHandle groupHandle, RenderableHandle renderableHandle, int32_t order) = 0; + virtual void removeRenderableFromRenderGroup (RenderGroupHandle groupHandle, RenderableHandle renderableHandle) = 0; + virtual void addRenderGroupToRenderGroup (RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild, int32_t order) = 0; + virtual void removeRenderGroupFromRenderGroup(RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild) = 0; + [[nodiscard]] virtual const RenderGroup& getRenderGroup (RenderGroupHandle groupHandle) const = 0; + + // Render passes + virtual RenderPassHandle allocateRenderPass (uint32_t renderGroupCount, RenderPassHandle passHandle) = 0; + virtual void releaseRenderPass (RenderPassHandle passHandle) = 0; + [[nodiscard]] virtual bool isRenderPassAllocated (RenderPassHandle passHandle) const = 0; + [[nodiscard]] virtual uint32_t getRenderPassCount () const = 0; + virtual void setRenderPassClearColor (RenderPassHandle passHandle, const glm::vec4& clearColor) = 0; + virtual void setRenderPassClearFlag (RenderPassHandle passHandle, ClearFlags clearFlag) = 0; + virtual void setRenderPassCamera (RenderPassHandle passHandle, CameraHandle cameraHandle) = 0; + virtual void setRenderPassRenderTarget (RenderPassHandle passHandle, RenderTargetHandle targetHandle) = 0; + virtual void setRenderPassRenderOrder (RenderPassHandle passHandle, int32_t renderOrder) = 0; + virtual void setRenderPassEnabled (RenderPassHandle passHandle, bool isEnabled) = 0; + virtual void setRenderPassRenderOnce (RenderPassHandle passHandle, bool enable) = 0; + virtual void retriggerRenderPassRenderOnce (RenderPassHandle passHandle) = 0; + virtual void addRenderGroupToRenderPass (RenderPassHandle passHandle, RenderGroupHandle groupHandle, int32_t order) = 0; + virtual void removeRenderGroupFromRenderPass (RenderPassHandle passHandle, RenderGroupHandle groupHandle) = 0; + [[nodiscard]] virtual const RenderPass& getRenderPass (RenderPassHandle passHandle) const = 0; + + //Blit pass + virtual BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) = 0; + virtual void releaseBlitPass (BlitPassHandle passHandle) = 0; + [[nodiscard]] virtual bool isBlitPassAllocated (BlitPassHandle passHandle) const = 0; + [[nodiscard]] virtual uint32_t getBlitPassCount () const = 0; + virtual void setBlitPassRenderOrder (BlitPassHandle passHandle, int32_t renderOrder) = 0; + virtual void setBlitPassEnabled (BlitPassHandle passHandle, bool isEnabled) = 0; + virtual void setBlitPassRegions (BlitPassHandle passHandle, const PixelRectangle& sourceRegion, const PixelRectangle& destinationRegion) = 0; + [[nodiscard]] virtual const BlitPass& getBlitPass (BlitPassHandle passHandle) const = 0; + + [[nodiscard]] virtual SceneSizeInformation getSceneSizeInformation() const = 0; + + //Pickable object + virtual PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle) = 0; + virtual void releasePickableObject (PickableObjectHandle pickableHandle) = 0; + [[nodiscard]] virtual bool isPickableObjectAllocated (PickableObjectHandle pickableHandle) const = 0; + [[nodiscard]] virtual uint32_t getPickableObjectCount () const = 0; + virtual void setPickableObjectId (PickableObjectHandle pickableHandle, PickableObjectId id) = 0; + virtual void setPickableObjectCamera (PickableObjectHandle pickableHandle, CameraHandle cameraHandle) = 0; + virtual void setPickableObjectEnabled(PickableObjectHandle pickableHandel, bool isEnabled) = 0; + [[nodiscard]] virtual const PickableObject& getPickableObject (PickableObjectHandle pickableHandle) const = 0; + + // Render targets + virtual RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle) = 0; + virtual void releaseRenderTarget (RenderTargetHandle targetHandle) = 0; + [[nodiscard]] virtual bool isRenderTargetAllocated (RenderTargetHandle targetHandle) const = 0; + [[nodiscard]] virtual uint32_t getRenderTargetCount () const = 0; + virtual void addRenderTargetRenderBuffer (RenderTargetHandle targetHandle, RenderBufferHandle bufferHandle) = 0; + [[nodiscard]] virtual uint32_t getRenderTargetRenderBufferCount(RenderTargetHandle targetHandle) const = 0; + [[nodiscard]] virtual RenderBufferHandle getRenderTargetRenderBuffer (RenderTargetHandle targetHandle, uint32_t bufferIndex) const = 0; + + // Render buffers + virtual RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle) = 0; + virtual void releaseRenderBuffer (RenderBufferHandle handle) = 0; + [[nodiscard]] virtual bool isRenderBufferAllocated (RenderBufferHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getRenderBufferCount () const = 0; + [[nodiscard]] virtual const RenderBuffer& getRenderBuffer (RenderBufferHandle handle) const = 0; + + // Data buffers + virtual DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle) = 0; + virtual void releaseDataBuffer (DataBufferHandle handle) = 0; + [[nodiscard]] virtual bool isDataBufferAllocated (DataBufferHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getDataBufferCount () const = 0; + virtual void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) = 0; + [[nodiscard]] virtual const GeometryDataBuffer& getDataBuffer (DataBufferHandle handle) const = 0; + + //Texture buffers + virtual TextureBufferHandle allocateTextureBuffer (EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) = 0; + virtual void releaseTextureBuffer (TextureBufferHandle handle) = 0; + [[nodiscard]] virtual bool isTextureBufferAllocated (TextureBufferHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getTextureBufferCount () const = 0; + virtual void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) = 0; + [[nodiscard]] virtual const TextureBuffer& getTextureBuffer (TextureBufferHandle handle) const = 0; + + // Data slots + virtual DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle) = 0; + virtual void releaseDataSlot (DataSlotHandle handle) = 0; + virtual void setDataSlotTexture (DataSlotHandle handle, const ResourceContentHash& texture) = 0; + [[nodiscard]] virtual bool isDataSlotAllocated (DataSlotHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getDataSlotCount () const = 0; + [[nodiscard]] virtual const DataSlot& getDataSlot (DataSlotHandle handle) const = 0; + + // Scene references + virtual SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle) = 0; + virtual void releaseSceneReference (SceneReferenceHandle handle) = 0; + virtual void requestSceneReferenceState (SceneReferenceHandle handle, RendererSceneState state) = 0; + virtual void requestSceneReferenceFlushNotifications(SceneReferenceHandle handle, bool enable) = 0; + virtual void setSceneReferenceRenderOrder (SceneReferenceHandle handle, int32_t renderOrder) = 0; + [[nodiscard]] virtual bool isSceneReferenceAllocated(SceneReferenceHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getSceneReferenceCount () const = 0; + [[nodiscard]] virtual const SceneReference& getSceneReference (SceneReferenceHandle handle) const = 0; + }; +} diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/MipMapSize.h b/src/framework/internal/SceneGraph/SceneAPI/MipMapSize.h similarity index 83% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/MipMapSize.h rename to src/framework/internal/SceneGraph/SceneAPI/MipMapSize.h index 3f1f50d53..eb88e4627 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/MipMapSize.h +++ b/src/framework/internal/SceneGraph/SceneAPI/MipMapSize.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MIPMAPSIZE_H -#define RAMSES_MIPMAPSIZE_H +#pragma once -#include "Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { struct MipMapSize { @@ -21,5 +20,3 @@ namespace ramses_internal using MipMapDimensions = std::vector; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/PickableObject.h b/src/framework/internal/SceneGraph/SceneAPI/PickableObject.h similarity index 79% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/PickableObject.h rename to src/framework/internal/SceneGraph/SceneAPI/PickableObject.h index 33197e6cb..63f2b2d80 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/PickableObject.h +++ b/src/framework/internal/SceneGraph/SceneAPI/PickableObject.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_PICKABLEOBJECT_H -#define RAMSES_INTERNAL_PICKABLEOBJECT_H +#pragma once -#include "SceneAPI/SceneTypes.h" -#include "Utils/AssertMovable.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +namespace ramses::internal { struct PickableObject { @@ -25,5 +24,3 @@ namespace ramses_internal ASSERT_MOVABLE(PickableObject) } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/PixelRectangle.h b/src/framework/internal/SceneGraph/SceneAPI/PixelRectangle.h similarity index 74% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/PixelRectangle.h rename to src/framework/internal/SceneGraph/SceneAPI/PixelRectangle.h index 7798bb12d..7dddad7dc 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/PixelRectangle.h +++ b/src/framework/internal/SceneGraph/SceneAPI/PixelRectangle.h @@ -6,20 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PIXELRECTANGLE_H -#define RAMSES_PIXELRECTANGLE_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { struct PixelRectangle { - uint32_t x; - uint32_t y; - int32_t width; - int32_t height; + uint32_t x = 0u; + uint32_t y = 0u; + int32_t width = 0; + int32_t height = 0; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderBuffer.h b/src/framework/internal/SceneGraph/SceneAPI/RenderBuffer.h similarity index 62% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RenderBuffer.h rename to src/framework/internal/SceneGraph/SceneAPI/RenderBuffer.h index f01dcde62..010e5d550 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderBuffer.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RenderBuffer.h @@ -6,20 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RENDERBUFFER_H -#define RAMSES_INTERNAL_RENDERBUFFER_H +#pragma once -#include "SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" -namespace ramses_internal +namespace ramses::internal { struct RenderBuffer { RenderBuffer() = default; - RenderBuffer(uint32_t _width, uint32_t _height, ERenderBufferType _type, ETextureFormat _format, ERenderBufferAccessMode _accessMode, uint32_t _sampleCount) + RenderBuffer(uint32_t _width, uint32_t _height, EPixelStorageFormat _format, ERenderBufferAccessMode _accessMode, uint32_t _sampleCount) : width(_width) , height(_height) - , type(_type) , format(_format) , accessMode(_accessMode) , sampleCount(_sampleCount) @@ -28,8 +26,7 @@ namespace ramses_internal bool operator==(const RenderBuffer& other) const { - return type == other.type - && format == other.format + return format == other.format && width == other.width && height == other.height && accessMode == other.accessMode @@ -41,13 +38,10 @@ namespace ramses_internal return !this->operator==(other); } - uint32_t width = 0u; - uint32_t height = 0u; - ERenderBufferType type = ERenderBufferType_InvalidBuffer; - ETextureFormat format = ETextureFormat::Invalid; - ERenderBufferAccessMode accessMode = ERenderBufferAccessMode_Invalid; - uint32_t sampleCount = 0u; + uint32_t width = 0u; + uint32_t height = 0u; + EPixelStorageFormat format = EPixelStorageFormat::Invalid; + ERenderBufferAccessMode accessMode = ERenderBufferAccessMode::ReadWrite; + uint32_t sampleCount = 0u; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderGroup.h b/src/framework/internal/SceneGraph/SceneAPI/RenderGroup.h similarity index 76% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RenderGroup.h rename to src/framework/internal/SceneGraph/SceneAPI/RenderGroup.h index 27c974c30..2baf64249 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderGroup.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RenderGroup.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RENDERGROUP_H -#define RAMSES_INTERNAL_RENDERGROUP_H +#pragma once -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneTypes.h" -#include "Utils/AssertMovable.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/Core/Utils/AssertMovable.h" -namespace ramses_internal +namespace ramses::internal { struct RenderableOrderEntry { RenderableHandle renderable; - int32_t order; + int32_t order{0}; }; using RenderableOrderVector = std::vector; @@ -30,5 +29,3 @@ namespace ramses_internal ASSERT_MOVABLE(RenderGroup) } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderGroupUtils.h b/src/framework/internal/SceneGraph/SceneAPI/RenderGroupUtils.h similarity index 94% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RenderGroupUtils.h rename to src/framework/internal/SceneGraph/SceneAPI/RenderGroupUtils.h index a2f78d620..6c262e531 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderGroupUtils.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RenderGroupUtils.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RENDERGROUPUTILS_H -#define RAMSES_INTERNAL_RENDERGROUPUTILS_H +#pragma once -#include "SceneAPI/RenderGroup.h" -#include "SceneAPI/RenderPass.h" +#include "internal/SceneGraph/SceneAPI/RenderGroup.h" +#include "internal/SceneGraph/SceneAPI/RenderPass.h" -namespace ramses_internal +namespace ramses::internal { class RenderGroupUtils { @@ -63,5 +62,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderPass.h b/src/framework/internal/SceneGraph/SceneAPI/RenderPass.h similarity index 73% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RenderPass.h rename to src/framework/internal/SceneGraph/SceneAPI/RenderPass.h index 110f7ec5d..5d6fc479d 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderPass.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RenderPass.h @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RENDERPASS_H -#define RAMSES_INTERNAL_RENDERPASS_H +#pragma once -#include "SceneAPI/SceneTypes.h" -#include "Utils/AssertMovable.h" -#include "DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/Core/Utils/AssertMovable.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +namespace ramses::internal { struct RenderPass { bool isEnabled = true; CameraHandle camera; RenderTargetHandle renderTarget; - int32_t renderOrder = 0; + int32_t renderOrder = 0; glm::vec4 clearColor{ 0.f, 0.f, 0.f, 1.f }; - uint32_t clearFlags = EClearFlags_All; + ClearFlags clearFlags = EClearFlag::All; bool isRenderOnce = false; RenderGroupOrderVector renderGroups; @@ -30,5 +29,3 @@ namespace ramses_internal ASSERT_MOVABLE(RenderPass) } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderState.h b/src/framework/internal/SceneGraph/SceneAPI/RenderState.h similarity index 76% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RenderState.h rename to src/framework/internal/SceneGraph/SceneAPI/RenderState.h index 82b5b97ed..8ed9af433 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderState.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RenderState.h @@ -6,26 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_RENDERSTATE_H -#define RAMSES_SCENEAPI_RENDERSTATE_H +#pragma once -#include "Common/BitForgeMacro.h" -#include "Utils/LoggingUtils.h" -#include "Math3d/Quad.h" -#include "DataTypesImpl.h" -#include "ramses-framework-api/AppearanceEnums.h" +#include "internal/Core/Common/BitForgeMacro.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/Core/Math3d/Quad.h" +#include "impl/DataTypesImpl.h" +#include "ramses/framework/AppearanceEnums.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses_internal +namespace ramses::internal { - using ramses::EBlendOperation; - using ramses::EBlendFactor; - using ramses::ECullMode; - using ramses::EDepthWrite; - using ramses::EDepthFunc; - using ramses::EScissorTest; - using ramses::EStencilFunc; using EStencilOp = ramses::EStencilOperation; - using ramses::EDrawMode; enum EColorWriteFlag : uint32_t { @@ -37,20 +29,6 @@ namespace ramses_internal }; using ColorWriteMask = uint8_t; - enum EClearFlags : uint32_t - { - EClearFlags_None = 0, - EClearFlags_Color = BIT(0u), - EClearFlags_Depth = BIT(1u), - EClearFlags_Stencil = BIT(2u), - EClearFlags_All = EClearFlags_Color | EClearFlags_Depth | EClearFlags_Stencil - }; - - constexpr bool IsClearFlagSet(uint32_t clearFlags, EClearFlags clearFlagToCheckIfSet) - { - return (clearFlags & clearFlagToCheckIfSet) == clearFlagToCheckIfSet; - } - struct RenderState { struct ScissorRegion @@ -96,5 +74,3 @@ namespace ramses_internal static_assert(sizeof(RenderState) == ExpectedSize, "Avoid padding in this struct, add padding explicitly as member"); } -#endif - diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderTarget.h b/src/framework/internal/SceneGraph/SceneAPI/RenderTarget.h similarity index 79% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RenderTarget.h rename to src/framework/internal/SceneGraph/SceneAPI/RenderTarget.h index a76a14ddb..92cd4df16 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RenderTarget.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RenderTarget.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RENDERTARGET_H -#define RAMSES_INTERNAL_RENDERTARGET_H +#pragma once -#include "SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" -namespace ramses_internal +namespace ramses::internal { struct RenderTarget { RenderBufferHandleVector renderBuffers; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/Renderable.h b/src/framework/internal/SceneGraph/SceneAPI/Renderable.h similarity index 73% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/Renderable.h rename to src/framework/internal/SceneGraph/SceneAPI/Renderable.h index 9721a47f6..8b07d7be7 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/Renderable.h +++ b/src/framework/internal/SceneGraph/SceneAPI/Renderable.h @@ -6,22 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_RENDERABLE_H -#define RAMSES_INTERNAL_RENDERABLE_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/ERenderableDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ERenderableDataSlotType.h" +#include "ramses/framework/EVisibilityMode.h" #include -namespace ramses_internal +namespace ramses::internal { - enum class EVisibilityMode : int8_t - { - Off = 0, - Invisible, - Visible - }; + using ramses::EVisibilityMode; struct Renderable { @@ -37,5 +32,3 @@ namespace ramses_internal RenderStateHandle renderState; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/RendererSceneState.h b/src/framework/internal/SceneGraph/SceneAPI/RendererSceneState.h similarity index 66% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/RendererSceneState.h rename to src/framework/internal/SceneGraph/SceneAPI/RendererSceneState.h index 29c0473e4..d0df24df1 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/RendererSceneState.h +++ b/src/framework/internal/SceneGraph/SceneAPI/RendererSceneState.h @@ -6,21 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_RENDERERSCENESTATE_H -#define RAMSES_FRAMEWORK_RENDERERSCENESTATE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "ramses/framework/RendererSceneState.h" +#include -namespace ramses_internal +namespace ramses::internal { - enum class RendererSceneState : uint32_t - { - Unavailable, - Available, - Ready, - Rendered, - }; + using ramses::RendererSceneState; const std::array RendererSceneStateNames = { @@ -29,7 +23,5 @@ namespace ramses_internal "Ready", "Rendered", }; - ENUM_TO_STRING(RendererSceneState, RendererSceneStateNames, 4); + ENUM_TO_STRING(RendererSceneState, RendererSceneStateNames, RendererSceneState::Rendered); } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/ResourceContentHash.h b/src/framework/internal/SceneGraph/SceneAPI/ResourceContentHash.h similarity index 63% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/ResourceContentHash.h rename to src/framework/internal/SceneGraph/SceneAPI/ResourceContentHash.h index 2ea077a5b..98f11b276 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/ResourceContentHash.h +++ b/src/framework/internal/SceneGraph/SceneAPI/ResourceContentHash.h @@ -6,28 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCECONTENTHASH_H -#define RAMSES_RESOURCECONTENTHASH_H - -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/Hash.h" -#include "Collections/IOutputStream.h" -#include "Collections/IInputStream.h" -#include "PlatformAbstraction/FmtBase.h" -#include "Resource/ResourceTypes.h" +#pragma once + +#include "internal/PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/FmtBase.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" + +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { struct ResourceContentHash { - constexpr ResourceContentHash() - : lowPart(0) - , highPart(0) - { - } + constexpr ResourceContentHash() = default; constexpr ResourceContentHash(uint64_t low, uint64_t high) : lowPart(low) @@ -55,8 +51,8 @@ namespace ramses_internal return !(*this == rhs); } - uint64_t lowPart; - uint64_t highPart; + uint64_t lowPart{0u}; + uint64_t highPart{0u}; }; constexpr inline bool operator<(ResourceContentHash const& lhs, ResourceContentHash const& rhs) @@ -78,31 +74,31 @@ namespace ramses_internal } template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::ResourceContentHash& res, FormatContext& ctx) + constexpr auto format(const ramses::internal::ResourceContentHash& res, FormatContext& ctx) { const char* typeShortString = nullptr; const auto type = static_cast((res.highPart >> 60LU) & 0xFu); switch (type) { - case static_cast(ramses_internal::EResourceType_VertexArray): + case static_cast(ramses::internal::EResourceType::VertexArray): typeShortString = "vtx"; break; - case static_cast(ramses_internal::EResourceType_IndexArray): + case static_cast(ramses::internal::EResourceType::IndexArray): typeShortString = "idx"; break; - case static_cast(ramses_internal::EResourceType_Texture2D): + case static_cast(ramses::internal::EResourceType::Texture2D): typeShortString = "tx2"; break; - case static_cast(ramses_internal::EResourceType_Texture3D): + case static_cast(ramses::internal::EResourceType::Texture3D): typeShortString = "tx3"; break; - case static_cast(ramses_internal::EResourceType_TextureCube): + case static_cast(ramses::internal::EResourceType::TextureCube): typeShortString = "txc"; break; - case static_cast(ramses_internal::EResourceType_Effect): + case static_cast(ramses::internal::EResourceType::Effect): typeShortString = "eff"; break; default: @@ -113,10 +109,10 @@ struct fmt::formatter : public ramses_inte }; template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::ResourceContentHashVector& hashes, FormatContext& ctx) + constexpr auto format(const ramses::internal::ResourceContentHashVector& hashes, FormatContext& ctx) { fmt::format_to(ctx.out(), "[{} resources:", hashes.size()); for (auto const& hash : hashes) @@ -130,16 +126,14 @@ struct fmt::formatter : public ramse namespace std { template <> - struct hash + struct hash { public: - size_t operator()(const ramses_internal::ResourceContentHash& v) const + size_t operator()(const ramses::internal::ResourceContentHash& v) const { - static_assert(sizeof(ramses_internal::ResourceContentHash) == 2*sizeof(uint64_t), "make sure resourceontenthash is just 2 64 values"); - return ramses_internal::HashMemoryRange(&v, 2 * sizeof(uint64_t)); + static_assert(sizeof(ramses::internal::ResourceContentHash) == 2*sizeof(uint64_t), "make sure resourceontenthash is just 2 64 values"); + return ramses::internal::HashMemoryRange(&v, 2 * sizeof(uint64_t)); } }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/ResourceField.h b/src/framework/internal/SceneGraph/SceneAPI/ResourceField.h similarity index 64% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/ResourceField.h rename to src/framework/internal/SceneGraph/SceneAPI/ResourceField.h index a2af66fe7..0c5a9382a 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/ResourceField.h +++ b/src/framework/internal/SceneGraph/SceneAPI/ResourceField.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_RESOURCEFIELD_H -#define RAMSES_SCENEAPI_RESOURCEFIELD_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" -namespace ramses_internal +#include + +namespace ramses::internal { enum class EDataType; @@ -21,10 +21,8 @@ namespace ramses_internal { ResourceContentHash hash; DataBufferHandle dataBuffer; - uint32_t instancingDivisor; - uint16_t offsetWithinElementInBytes; - uint16_t stride; + uint32_t instancingDivisor{0u}; + uint16_t offsetWithinElementInBytes{0u}; + uint16_t stride{0u}; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneCreationInformation.h b/src/framework/internal/SceneGraph/SceneAPI/SceneCreationInformation.h similarity index 83% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/SceneCreationInformation.h rename to src/framework/internal/SceneGraph/SceneAPI/SceneCreationInformation.h index 3714a7354..f15adaaa0 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneCreationInformation.h +++ b/src/framework/internal/SceneGraph/SceneAPI/SceneCreationInformation.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENECREATIONINFORMATION_H -#define RAMSES_SCENECREATIONINFORMATION_H +#pragma once -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include #include -namespace ramses_internal +namespace ramses::internal { struct SceneCreationInformation { @@ -34,5 +33,3 @@ namespace ramses_internal SceneSizeInformation m_sizeInfo; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneId.h b/src/framework/internal/SceneGraph/SceneAPI/SceneId.h similarity index 76% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/SceneId.h rename to src/framework/internal/SceneGraph/SceneAPI/SceneId.h index 6d71c12b5..107f11d6d 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneId.h +++ b/src/framework/internal/SceneGraph/SceneAPI/SceneId.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_SCENEID_H -#define RAMSES_SCENEAPI_SCENEID_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Common/StronglyTypedValue.h" -#include "Collections/Vector.h" -#include "Scene/EScenePublicationMode.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/Core/Common/StronglyTypedValue.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include #include #include -namespace ramses_internal +namespace ramses::internal { struct SceneIdTag {}; using SceneId = StronglyTypedValue; @@ -27,8 +26,9 @@ namespace ramses_internal struct SceneInfo { - SceneInfo() {} - explicit SceneInfo(const SceneId& sceneID_, std::string_view friendlyName_ = {}, EScenePublicationMode mode = EScenePublicationMode_LocalAndRemote) + SceneInfo() = default; + + explicit SceneInfo(const SceneId& sceneID_, std::string_view friendlyName_ = {}, EScenePublicationMode mode = EScenePublicationMode::LocalAndRemote) : sceneID(sceneID_) , friendlyName(friendlyName_) , publicationMode(mode) @@ -46,11 +46,9 @@ namespace ramses_internal SceneId sceneID; std::string friendlyName; - EScenePublicationMode publicationMode = EScenePublicationMode_Unpublished; + EScenePublicationMode publicationMode = EScenePublicationMode::LocalAndRemote; }; using SceneInfoVector = std::vector; } -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::SceneId) - -#endif +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::SceneId) diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneReference.h b/src/framework/internal/SceneGraph/SceneAPI/SceneReference.h similarity index 78% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/SceneReference.h rename to src/framework/internal/SceneGraph/SceneAPI/SceneReference.h index 5bb59b359..830e97222 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneReference.h +++ b/src/framework/internal/SceneGraph/SceneAPI/SceneReference.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_SCENEREFERENCE_H -#define RAMSES_INTERNAL_SCENEREFERENCE_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" -namespace ramses_internal +namespace ramses::internal { struct SceneReference { @@ -22,5 +21,3 @@ namespace ramses_internal bool flushNotifications = false; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneSizeInformation.h b/src/framework/internal/SceneGraph/SceneAPI/SceneSizeInformation.h similarity index 94% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/SceneSizeInformation.h rename to src/framework/internal/SceneGraph/SceneAPI/SceneSizeInformation.h index ef1ee05b4..2d5f66595 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneSizeInformation.h +++ b/src/framework/internal/SceneGraph/SceneAPI/SceneSizeInformation.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENESIZEINFORMATION_H -#define RAMSES_SCENESIZEINFORMATION_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "PlatformAbstraction/FmtBase.h" +#include "internal/PlatformAbstraction/FmtBase.h" -namespace ramses_internal +#include + +namespace ramses::internal { struct SceneSizeInformation { @@ -125,10 +125,10 @@ namespace ramses_internal } template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::SceneSizeInformation& si, FormatContext& ctx) + constexpr auto format(const ramses::internal::SceneSizeInformation& si, FormatContext& ctx) { return fmt::format_to(ctx.out(), "[node={} camera={} transform={} renderable={} state={} datalayout={} datainstance={} renderGroup={} renderPass={} blitPass={} " @@ -155,5 +155,3 @@ struct fmt::formatter : public ramses_int } }; - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneTypes.h b/src/framework/internal/SceneGraph/SceneAPI/SceneTypes.h similarity index 87% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/SceneTypes.h rename to src/framework/internal/SceneGraph/SceneAPI/SceneTypes.h index 7d8c6b302..0590a5b48 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneTypes.h +++ b/src/framework/internal/SceneGraph/SceneAPI/SceneTypes.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_SCENETYPES_H -#define RAMSES_SCENEAPI_SCENETYPES_H +#pragma once -#include "SceneAPI/Handles.h" -#include "SceneAPI/RenderState.h" -#include "Common/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" #include -namespace ramses_internal +namespace ramses::internal { struct RenderGroupOrderEntry { RenderGroupHandle renderGroup; - int32_t order; + int32_t order{0}; bool operator<(const RenderGroupOrderEntry& other) const { @@ -44,5 +43,3 @@ namespace ramses_internal using PickableObjectId = StronglyTypedValue::max(), PickableObjectIdTag>; using PickableObjectIds = std::vector; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneVersionTag.h b/src/framework/internal/SceneGraph/SceneAPI/SceneVersionTag.h similarity index 70% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/SceneVersionTag.h rename to src/framework/internal/SceneGraph/SceneAPI/SceneVersionTag.h index 43f429aee..fbbf52529 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/SceneVersionTag.h +++ b/src/framework/internal/SceneGraph/SceneAPI/SceneVersionTag.h @@ -6,18 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_SCENEVERSIONTAG_H -#define RAMSES_SCENEAPI_SCENEVERSIONTAG_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Common/StronglyTypedValue.h" +#include "internal/Core/Common/StronglyTypedValue.h" -namespace ramses_internal +#include + +namespace ramses::internal { struct SceneVersionTagTag {}; using SceneVersionTag = StronglyTypedValue::max(), SceneVersionTagTag>; } -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::SceneVersionTag) - -#endif +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::SceneVersionTag) diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureBuffer.h b/src/framework/internal/SceneGraph/SceneAPI/TextureBuffer.h similarity index 72% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/TextureBuffer.h rename to src/framework/internal/SceneGraph/SceneAPI/TextureBuffer.h index b297450cc..29731fa61 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureBuffer.h +++ b/src/framework/internal/SceneGraph/SceneAPI/TextureBuffer.h @@ -6,31 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNAL_TEXTUREBUFFER_H -#define RAMSES_INTERNAL_TEXTUREBUFFER_H +#pragma once -#include "Collections/Vector.h" -#include "SceneAPI/TextureEnums.h" -#include "Resource/TextureMetaInfo.h" -#include "Math3d/Quad.h" -#include "Utils/AssertMovable.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" +#include "internal/Core/Math3d/Quad.h" +#include "internal/Core/Utils/AssertMovable.h" #include #include -namespace ramses_internal +namespace ramses::internal { struct MipMap { uint32_t width = 0u; uint32_t height = 0u; Quad usedRegion; - std::vector data; + std::vector data; }; using MipMaps = std::vector; struct TextureBuffer { - ETextureFormat textureFormat = ETextureFormat::Invalid; + EPixelStorageFormat textureFormat = EPixelStorageFormat::Invalid; MipMaps mipMaps; static uint32_t GetMipMapDataSizeInBytes(const TextureBuffer& buffer) @@ -41,5 +40,3 @@ namespace ramses_internal ASSERT_MOVABLE(TextureBuffer) } - -#endif diff --git a/src/framework/internal/SceneGraph/SceneAPI/TextureEnums.h b/src/framework/internal/SceneGraph/SceneAPI/TextureEnums.h new file mode 100644 index 000000000..ec4ca8bd8 --- /dev/null +++ b/src/framework/internal/SceneGraph/SceneAPI/TextureEnums.h @@ -0,0 +1,256 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/Utils/LoggingUtils.h" +#include "ramses/framework/TextureEnums.h" +#include "impl/TextureEnumsImpl.h" + +#include +#include + +namespace ramses::internal +{ + using ramses::ETextureSamplingMethod; + using ramses::ETextureAddressMode; + using ramses::ETextureChannelColor; + using ramses::ETextureCubeFace; + using ramses::ERenderBufferAccessMode; + + enum class EPixelStorageFormat : uint8_t + { + Invalid = 0, + + R8, + RG8, + RGB8, + RGB565, + + RGBA8, + RGBA4, + RGBA5551, + + ETC2RGB, // ericsson texture compression 2 + ETC2RGBA, // ericsson texture compression 2 with alpha + + // Floating point formats + R16F, + R32F, + RG16F, + RG32F, + RGB16F, + RGB32F, + RGBA16F, + RGBA32F, + + // sRGB formats + SRGB8, + SRGB8_ALPHA8, + + // ASTC formats + ASTC_RGBA_4x4, + ASTC_RGBA_5x4, + ASTC_RGBA_5x5, + ASTC_RGBA_6x5, + ASTC_RGBA_6x6, + ASTC_RGBA_8x5, + ASTC_RGBA_8x6, + ASTC_RGBA_8x8, + ASTC_RGBA_10x5, + ASTC_RGBA_10x6, + ASTC_RGBA_10x8, + ASTC_RGBA_10x10, + ASTC_RGBA_12x10, + ASTC_RGBA_12x12, + ASTC_SRGBA_4x4, + ASTC_SRGBA_5x4, + ASTC_SRGBA_5x5, + ASTC_SRGBA_6x5, + ASTC_SRGBA_6x6, + ASTC_SRGBA_8x5, + ASTC_SRGBA_8x6, + ASTC_SRGBA_8x8, + ASTC_SRGBA_10x5, + ASTC_SRGBA_10x6, + ASTC_SRGBA_10x8, + ASTC_SRGBA_10x10, + ASTC_SRGBA_12x10, + ASTC_SRGBA_12x12, + + // Depth + Depth16, + Depth24, + Depth32, + Depth24_Stencil8, + }; + + static inline bool IsFormatCompressed(EPixelStorageFormat textureformat) + { + switch (textureformat) + { + case EPixelStorageFormat::ETC2RGB: + case EPixelStorageFormat::ETC2RGBA: + case EPixelStorageFormat::ASTC_RGBA_4x4: + case EPixelStorageFormat::ASTC_RGBA_5x4: + case EPixelStorageFormat::ASTC_RGBA_5x5: + case EPixelStorageFormat::ASTC_RGBA_6x5: + case EPixelStorageFormat::ASTC_RGBA_6x6: + case EPixelStorageFormat::ASTC_RGBA_8x5: + case EPixelStorageFormat::ASTC_RGBA_8x6: + case EPixelStorageFormat::ASTC_RGBA_8x8: + case EPixelStorageFormat::ASTC_RGBA_10x5: + case EPixelStorageFormat::ASTC_RGBA_10x6: + case EPixelStorageFormat::ASTC_RGBA_10x8: + case EPixelStorageFormat::ASTC_RGBA_10x10: + case EPixelStorageFormat::ASTC_RGBA_12x10: + case EPixelStorageFormat::ASTC_RGBA_12x12: + case EPixelStorageFormat::ASTC_SRGBA_4x4: + case EPixelStorageFormat::ASTC_SRGBA_5x4: + case EPixelStorageFormat::ASTC_SRGBA_5x5: + case EPixelStorageFormat::ASTC_SRGBA_6x5: + case EPixelStorageFormat::ASTC_SRGBA_6x6: + case EPixelStorageFormat::ASTC_SRGBA_8x5: + case EPixelStorageFormat::ASTC_SRGBA_8x6: + case EPixelStorageFormat::ASTC_SRGBA_8x8: + case EPixelStorageFormat::ASTC_SRGBA_10x5: + case EPixelStorageFormat::ASTC_SRGBA_10x6: + case EPixelStorageFormat::ASTC_SRGBA_10x8: + case EPixelStorageFormat::ASTC_SRGBA_10x10: + case EPixelStorageFormat::ASTC_SRGBA_12x10: + case EPixelStorageFormat::ASTC_SRGBA_12x12: + return true; + default: + return false; + } + } + + static inline uint32_t GetTexelSizeFromFormat(EPixelStorageFormat texelFormat) + { + switch (texelFormat) + { + case EPixelStorageFormat::R8: + return 1u; + case EPixelStorageFormat::R16F: + case EPixelStorageFormat::RG8: + case EPixelStorageFormat::RGB565: + case EPixelStorageFormat::RGBA4: + case EPixelStorageFormat::RGBA5551: + case EPixelStorageFormat::Depth16: + return 2u; + case EPixelStorageFormat::RGB8: + case EPixelStorageFormat::Depth24: + case EPixelStorageFormat::SRGB8: + return 3u; + case EPixelStorageFormat::RG16F: + case EPixelStorageFormat::RGBA8: + case EPixelStorageFormat::R32F: + case EPixelStorageFormat::SRGB8_ALPHA8: + case EPixelStorageFormat::Depth32: + case EPixelStorageFormat::Depth24_Stencil8: + return 4u; + case EPixelStorageFormat::RGB16F: + return 6u; + case EPixelStorageFormat::RG32F: + case EPixelStorageFormat::RGBA16F: + return 8u; + case EPixelStorageFormat::RGB32F: + return 12u; + case EPixelStorageFormat::RGBA32F: + return 16u; + + default: + assert(false && "Unknown texel size"); + return 1u; + } + } + + static inline bool IsDepthOrStencilFormat(EPixelStorageFormat textureformat) + { + switch (textureformat) + { + case EPixelStorageFormat::Depth16: + case EPixelStorageFormat::Depth24: + case EPixelStorageFormat::Depth32: + case EPixelStorageFormat::Depth24_Stencil8: + return true; + default: + return false; + } + } + + static inline bool IsDepthOnlyFormat(EPixelStorageFormat textureformat) + { + switch (textureformat) + { + case EPixelStorageFormat::Depth16: + case EPixelStorageFormat::Depth24: + case EPixelStorageFormat::Depth32: + return true; + default: + return false; + } + } + + const std::array TextureFormatNames = { + "Invalid", + "R8", + "RG8", + "RGB8", + "RGB565", + "RGBA8", + "RGBA4", + "RGBA5551", + "ETC2RGB", + "ETC2RGBA", + "R16F", + "R32F", + "RG16F", + "RG32F", + "RGB16F", + "RGB32F", + "RGBA16F", + "RGBA32F", + "SRGB8", + "SRGB8_ALPHA8", + "ASTC_RGBA_4x4", + "ASTC_RGBA_5x4", + "ASTC_RGBA_5x5", + "ASTC_RGBA_6x5", + "ASTC_RGBA_6x6", + "ASTC_RGBA_8x5", + "ASTC_RGBA_8x6", + "ASTC_RGBA_8x8", + "ASTC_RGBA_10x5", + "ASTC_RGBA_10x6", + "ASTC_RGBA_10x8", + "ASTC_RGBA_10x10", + "ASTC_RGBA_12x10", + "ASTC_RGBA_12x12", + "ASTC_SRGBA_4x4", + "ASTC_SRGBA_5x4", + "ASTC_SRGBA_5x5", + "ASTC_SRGBA_6x5", + "ASTC_SRGBA_6x6", + "ASTC_SRGBA_8x5", + "ASTC_SRGBA_8x6", + "ASTC_SRGBA_8x8", + "ASTC_SRGBA_10x5", + "ASTC_SRGBA_10x6", + "ASTC_SRGBA_10x8", + "ASTC_SRGBA_10x10", + "ASTC_SRGBA_12x10", + "ASTC_SRGBA_12x12", + "Depth16", + "Depth24", + "Depth32", + "Depth24_Stencil8", + }; + + ENUM_TO_STRING(EPixelStorageFormat, TextureFormatNames, EPixelStorageFormat::Depth24_Stencil8); +} diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureSampler.h b/src/framework/internal/SceneGraph/SceneAPI/TextureSampler.h similarity index 71% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/TextureSampler.h rename to src/framework/internal/SceneGraph/SceneAPI/TextureSampler.h index 8035fbb7d..1a275c7a3 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureSampler.h +++ b/src/framework/internal/SceneGraph/SceneAPI/TextureSampler.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_TEXTURESAMPLER_H -#define RAMSES_SCENEAPI_TEXTURESAMPLER_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/TextureSamplerStates.h" -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/TextureSamplerStates.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" -namespace ramses_internal +namespace ramses::internal { struct TextureSampler { @@ -63,20 +62,18 @@ namespace ramses_internal { switch (contentType) { - case ramses_internal::TextureSampler::ContentType::RenderBuffer: - case ramses_internal::TextureSampler::ContentType::RenderBufferMS: + case ramses::internal::TextureSampler::ContentType::RenderBuffer: + case ramses::internal::TextureSampler::ContentType::RenderBufferMS: return true; - case ramses_internal::TextureSampler::ContentType::None: - case ramses_internal::TextureSampler::ContentType::ClientTexture: - case ramses_internal::TextureSampler::ContentType::TextureBuffer: - case ramses_internal::TextureSampler::ContentType::OffscreenBuffer: - case ramses_internal::TextureSampler::ContentType::StreamBuffer: - case ramses_internal::TextureSampler::ContentType::ExternalTexture: + case ramses::internal::TextureSampler::ContentType::None: + case ramses::internal::TextureSampler::ContentType::ClientTexture: + case ramses::internal::TextureSampler::ContentType::TextureBuffer: + case ramses::internal::TextureSampler::ContentType::OffscreenBuffer: + case ramses::internal::TextureSampler::ContentType::StreamBuffer: + case ramses::internal::TextureSampler::ContentType::ExternalTexture: break; } return false; } }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureSamplerStates.h b/src/framework/internal/SceneGraph/SceneAPI/TextureSamplerStates.h similarity index 56% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/TextureSamplerStates.h rename to src/framework/internal/SceneGraph/SceneAPI/TextureSamplerStates.h index 1442c0656..a96c03c4d 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/TextureSamplerStates.h +++ b/src/framework/internal/SceneGraph/SceneAPI/TextureSamplerStates.h @@ -6,26 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURESAMPLERSTATES_H -#define RAMSES_TEXTURESAMPLERSTATES_H +#pragma once -#include "SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" #include -namespace ramses_internal +namespace ramses::internal { struct TextureSamplerStates { TextureSamplerStates() - : TextureSamplerStates(EWrapMethod::Clamp, EWrapMethod::Clamp, EWrapMethod::Clamp, - ESamplingMethod::Linear_MipMapLinear, ESamplingMethod::Linear_MipMapLinear) + : TextureSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, + ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Linear_MipMapLinear) {} - TextureSamplerStates(EWrapMethod addressModeU, - EWrapMethod addressModeV, - EWrapMethod addressModeR, - ESamplingMethod minSamplingMode, - ESamplingMethod magSamplingMode, + TextureSamplerStates(ETextureAddressMode addressModeU, + ETextureAddressMode addressModeV, + ETextureAddressMode addressModeR, + ETextureSamplingMethod minSamplingMode, + ETextureSamplingMethod magSamplingMode, uint32_t anisotropyLevel = 1u) : m_addressModeU(addressModeU) , m_addressModeV(addressModeV) @@ -38,8 +37,8 @@ namespace ramses_internal [[nodiscard]] uint64_t hash() const { - static_assert(sizeof(EWrapMethod) == 1u, "Unexpected size for enum used for shift operations"); - static_assert(sizeof(ESamplingMethod) == 1u, "Unexpected size for enum used for shift operations"); + static_assert(sizeof(ETextureAddressMode) == 1u, "Unexpected size for enum used for shift operations"); + static_assert(sizeof(ETextureSamplingMethod) == 1u, "Unexpected size for enum used for shift operations"); return static_cast(m_addressModeU) | (static_cast(m_addressModeV) << 8u) @@ -50,13 +49,11 @@ namespace ramses_internal } - EWrapMethod m_addressModeU; - EWrapMethod m_addressModeV; - EWrapMethod m_addressModeR; - ESamplingMethod m_minSamplingMode; - ESamplingMethod m_magSamplingMode; + ETextureAddressMode m_addressModeU; + ETextureAddressMode m_addressModeV; + ETextureAddressMode m_addressModeR; + ETextureSamplingMethod m_minSamplingMode; + ETextureSamplingMethod m_magSamplingMode; uint32_t m_anisotropyLevel; }; } - -#endif diff --git a/framework/SceneGraph/SceneAPI/include/SceneAPI/Viewport.h b/src/framework/internal/SceneGraph/SceneAPI/Viewport.h similarity index 88% rename from framework/SceneGraph/SceneAPI/include/SceneAPI/Viewport.h rename to src/framework/internal/SceneGraph/SceneAPI/Viewport.h index d7f6d3677..a2a3566a4 100644 --- a/framework/SceneGraph/SceneAPI/include/SceneAPI/Viewport.h +++ b/src/framework/internal/SceneGraph/SceneAPI/Viewport.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEAPI_VIEWPORT_H -#define RAMSES_SCENEAPI_VIEWPORT_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include -namespace ramses_internal +namespace ramses::internal { struct Viewport { @@ -46,5 +45,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/SceneGraph/SceneUtils/src/DataInstanceHelper.cpp b/src/framework/internal/SceneGraph/SceneUtils/DataInstanceHelper.cpp similarity index 64% rename from framework/SceneGraph/SceneUtils/src/DataInstanceHelper.cpp rename to src/framework/internal/SceneGraph/SceneUtils/DataInstanceHelper.cpp index 05e6596cd..c6a0d5d65 100644 --- a/framework/SceneGraph/SceneUtils/src/DataInstanceHelper.cpp +++ b/src/framework/internal/SceneGraph/SceneUtils/DataInstanceHelper.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneUtils/DataInstanceHelper.h" -#include "SceneAPI/IScene.h" -#include "SceneUtils/ISceneDataArrayAccessor.h" -#include "Scene/DataLayout.h" +#include "internal/SceneGraph/SceneUtils/DataInstanceHelper.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h" +#include "internal/SceneGraph/Scene/DataLayout.h" -namespace ramses_internal +namespace ramses::internal { template void getAndStoreInstanceFieldData(const IScene& scene, DataInstanceHandle dataInstance, DataFieldHandle dataField, DataInstanceValueVariant& value) @@ -22,44 +22,47 @@ namespace ramses_internal void DataInstanceHelper::GetInstanceFieldData(const IScene& scene, DataInstanceHandle dataInstance, DataFieldHandle dataField, DataInstanceValueVariant& value) { - const ramses_internal::DataLayoutHandle dataLayoutHandle = scene.getLayoutOfDataInstance(dataInstance); - const ramses_internal::DataLayout& layout = scene.getDataLayout(dataLayoutHandle); - const ramses_internal::EDataType dataType = layout.getField(dataField).dataType; + const ramses::internal::DataLayoutHandle dataLayoutHandle = scene.getLayoutOfDataInstance(dataInstance); + const ramses::internal::DataLayout& layout = scene.getDataLayout(dataLayoutHandle); + const ramses::internal::EDataType dataType = layout.getField(dataField).dataType; assert(layout.getField(dataField).elementCount == 1u); switch (dataType) { - case ramses_internal::EDataType::Float: + case ramses::internal::EDataType::Float: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector2F: + case ramses::internal::EDataType::Vector2F: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector3F: + case ramses::internal::EDataType::Vector3F: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector4F: + case ramses::internal::EDataType::Vector4F: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Int32: + case ramses::internal::EDataType::Bool: + getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); + break; + case ramses::internal::EDataType::Int32: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector2I: + case ramses::internal::EDataType::Vector2I: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector3I: + case ramses::internal::EDataType::Vector3I: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector4I: + case ramses::internal::EDataType::Vector4I: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Matrix22F: + case ramses::internal::EDataType::Matrix22F: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Matrix33F: + case ramses::internal::EDataType::Matrix33F: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Matrix44F: + case ramses::internal::EDataType::Matrix44F: getAndStoreInstanceFieldData(scene, dataInstance, dataField, value); break; default: @@ -77,44 +80,47 @@ namespace ramses_internal void DataInstanceHelper::SetInstanceFieldData(IScene& scene, DataInstanceHandle dataInstance, DataFieldHandle dataField, const DataInstanceValueVariant& value) { - const ramses_internal::DataLayoutHandle dataLayoutHandle = scene.getLayoutOfDataInstance(dataInstance); - const ramses_internal::DataLayout& layout = scene.getDataLayout(dataLayoutHandle); - const ramses_internal::EDataType dataType = layout.getField(dataField).dataType; + const ramses::internal::DataLayoutHandle dataLayoutHandle = scene.getLayoutOfDataInstance(dataInstance); + const ramses::internal::DataLayout& layout = scene.getDataLayout(dataLayoutHandle); + const ramses::internal::EDataType dataType = layout.getField(dataField).dataType; assert(layout.getField(dataField).elementCount == 1u); switch (dataType) { - case ramses_internal::EDataType::Float: + case ramses::internal::EDataType::Float: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector2F: + case ramses::internal::EDataType::Vector2F: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector3F: + case ramses::internal::EDataType::Vector3F: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector4F: + case ramses::internal::EDataType::Vector4F: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Int32: + case ramses::internal::EDataType::Bool: + setInstanceFieldData(scene, dataInstance, dataField, value); + break; + case ramses::internal::EDataType::Int32: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector2I: + case ramses::internal::EDataType::Vector2I: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector3I: + case ramses::internal::EDataType::Vector3I: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Vector4I: + case ramses::internal::EDataType::Vector4I: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Matrix22F: + case ramses::internal::EDataType::Matrix22F: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Matrix33F: + case ramses::internal::EDataType::Matrix33F: setInstanceFieldData(scene, dataInstance, dataField, value); break; - case ramses_internal::EDataType::Matrix44F: + case ramses::internal::EDataType::Matrix44F: setInstanceFieldData(scene, dataInstance, dataField, value); break; default: diff --git a/framework/SceneGraph/SceneUtils/include/SceneUtils/DataInstanceHelper.h b/src/framework/internal/SceneGraph/SceneUtils/DataInstanceHelper.h similarity index 84% rename from framework/SceneGraph/SceneUtils/include/SceneUtils/DataInstanceHelper.h rename to src/framework/internal/SceneGraph/SceneUtils/DataInstanceHelper.h index c5046a486..5ceb47d39 100644 --- a/framework/SceneGraph/SceneUtils/include/SceneUtils/DataInstanceHelper.h +++ b/src/framework/internal/SceneGraph/SceneUtils/DataInstanceHelper.h @@ -6,21 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAINSTANCEHELPER_H -#define RAMSES_DATAINSTANCEHELPER_H +#pragma once -#include "SceneAPI/Handles.h" -#include "DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "impl/DataTypesImpl.h" -#include "PlatformAbstraction/VariantWrapper.h" +#include "internal/PlatformAbstraction/VariantWrapper.h" -namespace ramses_internal +namespace ramses::internal { class IScene; using DataInstanceValueVariant = std::variant< std::monostate, float, + bool, int32_t, glm::vec2, glm::vec3, @@ -39,5 +39,3 @@ namespace ramses_internal static void SetInstanceFieldData(IScene& scene, DataInstanceHandle dataInstance, DataFieldHandle dataField, const DataInstanceValueVariant& value); }; } - -#endif diff --git a/framework/SceneGraph/SceneUtils/src/DataLayoutCreationHelper.cpp b/src/framework/internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.cpp similarity index 93% rename from framework/SceneGraph/SceneUtils/src/DataLayoutCreationHelper.cpp rename to src/framework/internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.cpp index fd6ac1db0..788700d5c 100644 --- a/framework/SceneGraph/SceneUtils/src/DataLayoutCreationHelper.cpp +++ b/src/framework/internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneUtils/DataLayoutCreationHelper.h" -#include "SceneAPI/IScene.h" -#include "Scene/DataLayout.h" +#include "internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/Scene/DataLayout.h" -namespace ramses_internal +namespace ramses::internal { DataLayoutHandle DataLayoutCreationHelper::CreateUniformDataLayoutMatchingEffectInputs(IScene& scene, const EffectInputInformationVector& uniformsInputInfo, InputIndexVector& referencedInputs, const ResourceContentHash& effectHash, DataLayoutHandle handle) { diff --git a/framework/SceneGraph/SceneUtils/include/SceneUtils/DataLayoutCreationHelper.h b/src/framework/internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h similarity index 82% rename from framework/SceneGraph/SceneUtils/include/SceneUtils/DataLayoutCreationHelper.h rename to src/framework/internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h index 9a308f029..e97cfaf39 100644 --- a/framework/SceneGraph/SceneUtils/include/SceneUtils/DataLayoutCreationHelper.h +++ b/src/framework/internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATALAYOUTCREATIONHELPER_H -#define RAMSES_DATALAYOUTCREATIONHELPER_H +#pragma once -#include "SceneAPI/Handles.h" -#include "SceneAPI/EDataType.h" -#include "Collections/Vector.h" -#include "Resource/EffectInputInformation.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/SceneGraph/Resource/EffectInputInformation.h" -namespace ramses_internal +namespace ramses::internal { class IScene; @@ -30,5 +29,3 @@ namespace ramses_internal static bool IsBindableInput(const EffectInputInformation& inputInfo); }; } - -#endif diff --git a/framework/SceneGraph/SceneUtils/include/SceneUtils/ISceneDataArrayAccessor.h b/src/framework/internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h similarity index 91% rename from framework/SceneGraph/SceneUtils/include/SceneUtils/ISceneDataArrayAccessor.h rename to src/framework/internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h index 0a88d4b9c..6304fabcc 100644 --- a/framework/SceneGraph/SceneUtils/include/SceneUtils/ISceneDataArrayAccessor.h +++ b/src/framework/internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISCENEDATAARRAYACCESSOR_H -#define RAMSES_ISCENEDATAARRAYACCESSOR_H +#pragma once -#include "SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" -namespace ramses_internal +namespace ramses::internal { class ISceneDataArrayAccessor { @@ -48,6 +47,12 @@ namespace ramses_internal return scene->getDataVector4fArray(containerHandle, field); } + template <> + inline const bool* ISceneDataArrayAccessor::GetDataArray(const IScene* scene, DataInstanceHandle containerHandle, DataFieldHandle field) + { + return scene->getDataBooleanArray(containerHandle, field); + } + template <> inline const int32_t* ISceneDataArrayAccessor::GetDataArray(const IScene* scene, DataInstanceHandle containerHandle, DataFieldHandle field) { @@ -115,6 +120,12 @@ namespace ramses_internal scene->setDataVector4fArray(containerHandle, field, elementCount, data); } + template <> + inline void ISceneDataArrayAccessor::SetDataArray(IScene* scene, DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) + { + scene->setDataBooleanArray(containerHandle, field, elementCount, data); + } + template <> inline void ISceneDataArrayAccessor::SetDataArray(IScene* scene, DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) { @@ -157,5 +168,3 @@ namespace ramses_internal scene->setDataMatrix44fArray(containerHandle, field, elementCount, data); } } - -#endif diff --git a/framework/SceneGraph/SceneUtils/src/ResourceUtils.cpp b/src/framework/internal/SceneGraph/SceneUtils/ResourceUtils.cpp similarity index 69% rename from framework/SceneGraph/SceneUtils/src/ResourceUtils.cpp rename to src/framework/internal/SceneGraph/SceneUtils/ResourceUtils.cpp index 4ad87b51b..dabb22ba7 100644 --- a/framework/SceneGraph/SceneUtils/src/ResourceUtils.cpp +++ b/src/framework/internal/SceneGraph/SceneUtils/ResourceUtils.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneUtils/ResourceUtils.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/GeometryDataBuffer.h" -#include "SceneAPI/TextureBuffer.h" -#include "Scene/DataLayout.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/GeometryDataBuffer.h" +#include "internal/SceneGraph/SceneAPI/TextureBuffer.h" +#include "internal/SceneGraph/Scene/DataLayout.h" +#include "internal/SceneGraph/Scene/ClientScene.h" -namespace ramses_internal +namespace ramses::internal { namespace ResourceUtils { diff --git a/framework/SceneGraph/SceneUtils/include/SceneUtils/ResourceUtils.h b/src/framework/internal/SceneGraph/SceneUtils/ResourceUtils.h similarity index 94% rename from framework/SceneGraph/SceneUtils/include/SceneUtils/ResourceUtils.h rename to src/framework/internal/SceneGraph/SceneUtils/ResourceUtils.h index 7cbf4b770..549d98ef0 100644 --- a/framework/SceneGraph/SceneUtils/include/SceneUtils/ResourceUtils.h +++ b/src/framework/internal/SceneGraph/SceneUtils/ResourceUtils.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEUTILS_H -#define RAMSES_RESOURCEUTILS_H +#pragma once -#include "Scene/ResourceChanges.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/TextureSampler.h" -#include "SceneAPI/GeometryDataBuffer.h" -#include "SceneAPI/TextureBuffer.h" -#include "Scene/DataLayout.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/GeometryDataBuffer.h" +#include "internal/SceneGraph/SceneAPI/TextureBuffer.h" +#include "internal/SceneGraph/Scene/DataLayout.h" -namespace ramses_internal +namespace ramses::internal { class IScene; @@ -130,5 +129,3 @@ namespace ramses_internal void DiffResources(ResourceContentHashVector const& old, ResourceContentHashVector const& curr, ResourceChanges& changes); } } - -#endif diff --git a/framework/SceneReferencing/src/SceneReferenceAction.cpp b/src/framework/internal/SceneReferencing/SceneReferenceAction.cpp similarity index 95% rename from framework/SceneReferencing/src/SceneReferenceAction.cpp rename to src/framework/internal/SceneReferencing/SceneReferenceAction.cpp index dca725b51..cb6485c11 100644 --- a/framework/SceneReferencing/src/SceneReferenceAction.cpp +++ b/src/framework/internal/SceneReferencing/SceneReferenceAction.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneReferencing/SceneReferenceAction.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" -namespace ramses_internal +namespace ramses::internal { void SceneReferenceActionUtils::WriteToCollection(SceneReferenceActionVector const& actions, SceneActionCollection& collection) { diff --git a/framework/SceneReferencing/include/SceneReferencing/SceneReferenceAction.h b/src/framework/internal/SceneReferencing/SceneReferenceAction.h similarity index 64% rename from framework/SceneReferencing/include/SceneReferencing/SceneReferenceAction.h rename to src/framework/internal/SceneReferencing/SceneReferenceAction.h index f32bc0c48..f4c52b3b7 100644 --- a/framework/SceneReferencing/include/SceneReferencing/SceneReferenceAction.h +++ b/src/framework/internal/SceneReferencing/SceneReferenceAction.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEREFERENCEACTION_H -#define RAMSES_SCENEREFERENCEACTION_H - -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/RendererSceneState.h" -#include "SceneAPI/DataSlot.h" -#include "Scene/SceneActionCollection.h" +#pragma once + +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/Core/Utils/LoggingUtils.h" #include -namespace ramses_internal +namespace ramses::internal { enum class SceneReferenceActionType : uint8_t { @@ -24,9 +24,15 @@ namespace ramses_internal UnlinkData }; + const std::array SceneReferenceActionTypeNames = + { + "LinkData", + "UnlinkData", + }; + struct SceneReferenceAction { - SceneReferenceActionType type; + SceneReferenceActionType type = SceneReferenceActionType::LinkData; SceneReferenceHandle consumerScene; DataSlotId consumerId; SceneReferenceHandle providerScene; @@ -53,4 +59,7 @@ namespace ramses_internal }; } -#endif +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::SceneReferenceActionType, + "SceneReferenceActionType", + ramses::internal::SceneReferenceActionTypeNames, + ramses::internal::SceneReferenceActionType::UnlinkData); diff --git a/framework/SceneReferencing/src/SceneReferenceEvent.cpp b/src/framework/internal/SceneReferencing/SceneReferenceEvent.cpp similarity index 73% rename from framework/SceneReferencing/src/SceneReferenceEvent.cpp rename to src/framework/internal/SceneReferencing/SceneReferenceEvent.cpp index 1cb9c6db9..8c9e5391c 100644 --- a/framework/SceneReferencing/src/SceneReferenceEvent.cpp +++ b/src/framework/internal/SceneReferencing/SceneReferenceEvent.cpp @@ -6,26 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneReferencing/SceneReferenceEvent.h" -#include "Components/ERendererToClientEventType.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" +#include "internal/Components/ERendererToClientEventType.h" -namespace ramses_internal +namespace ramses::internal { template - inline void write(std::vector::iterator& where, T const& value) + inline void write(std::vector::iterator& where, T const& value) { std::memcpy(&(*where), &value, sizeof(T)); where += sizeof(T); } template - inline void read(std::vector::const_iterator& where, T& value) + inline void read(std::vector::const_iterator& where, T& value) { - std::memcpy(&value, &(*where), sizeof(T)); + // TODO static_cast is only needed to fix GCC bug until version 11.1 + std::memcpy(&value, static_cast(&(*where)), sizeof(T)); where += sizeof(T); } - void SceneReferenceEvent::readFromBlob(std::vector const& blob) + void SceneReferenceEvent::readFromBlob(std::vector const& blob) { assert(blob.size() == serializedSize); @@ -47,7 +48,7 @@ namespace ramses_internal assert(it == blob.cend()); } - void SceneReferenceEvent::writeToBlob(std::vector& blob) const + void SceneReferenceEvent::writeToBlob(std::vector& blob) const { assert(blob.empty()); diff --git a/framework/SceneReferencing/include/SceneReferencing/SceneReferenceEvent.h b/src/framework/internal/SceneReferencing/SceneReferenceEvent.h similarity index 78% rename from framework/SceneReferencing/include/SceneReferencing/SceneReferenceEvent.h rename to src/framework/internal/SceneReferencing/SceneReferenceEvent.h index 2181e5b8c..250a54788 100644 --- a/framework/SceneReferencing/include/SceneReferencing/SceneReferenceEvent.h +++ b/src/framework/internal/SceneReferencing/SceneReferenceEvent.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEREFERENCEEVENT_H -#define RAMSES_SCENEREFERENCEEVENT_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneVersionTag.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/RendererSceneState.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" #include -#include "Components/ERendererToClientEventType.h" +#include "internal/Components/ERendererToClientEventType.h" -namespace ramses_internal +namespace ramses::internal { enum class SceneReferenceEventType : uint8_t { @@ -57,9 +56,7 @@ namespace ramses_internal sizeof(SceneVersionTag) + sizeof(bool); - void readFromBlob(std::vector const& blob); - void writeToBlob(std::vector& blob) const; + void readFromBlob(std::vector const& blob); + void writeToBlob(std::vector& blob) const; }; } - -#endif diff --git a/framework/Watchdog/include/Watchdog/IThreadAliveNotifier.h b/src/framework/internal/Watchdog/IThreadAliveNotifier.h similarity index 84% rename from framework/Watchdog/include/Watchdog/IThreadAliveNotifier.h rename to src/framework/internal/Watchdog/IThreadAliveNotifier.h index 3cfe33fbd..6e9acd831 100644 --- a/framework/Watchdog/include/Watchdog/IThreadAliveNotifier.h +++ b/src/framework/internal/Watchdog/IThreadAliveNotifier.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITHREADALIVENOTIFIER_H -#define RAMSES_ITHREADALIVENOTIFIER_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class IThreadAliveNotifier { public: - virtual ~IThreadAliveNotifier() {} + virtual ~IThreadAliveNotifier() = default; virtual uint64_t registerThread() = 0; virtual void unregisterThread(uint64_t identifier) = 0; @@ -26,5 +25,3 @@ namespace ramses_internal [[nodiscard]] virtual std::chrono::milliseconds calculateTimeout() const = 0; }; } - -#endif diff --git a/framework/Watchdog/src/PlatformWatchdog.cpp b/src/framework/internal/Watchdog/PlatformWatchdog.cpp similarity index 88% rename from framework/Watchdog/src/PlatformWatchdog.cpp rename to src/framework/internal/Watchdog/PlatformWatchdog.cpp index 2e660d5fa..9858ca5e0 100644 --- a/framework/Watchdog/src/PlatformWatchdog.cpp +++ b/src/framework/internal/Watchdog/PlatformWatchdog.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Watchdog/PlatformWatchdog.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/Watchdog/PlatformWatchdog.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformTime.h" using namespace std::chrono_literals; -namespace ramses_internal +namespace ramses::internal { - PlatformWatchdog::PlatformWatchdog(std::chrono::milliseconds notificationInterval, ramses::ERamsesThreadIdentifier thread, ramses::IThreadWatchdogNotification* callback) + PlatformWatchdog::PlatformWatchdog(std::chrono::milliseconds notificationInterval, ERamsesThreadIdentifier thread, IThreadWatchdogNotification* callback) : m_interval(notificationInterval / 2) , m_thread(thread) , m_watchdogCallback(callback) diff --git a/framework/Watchdog/include/Watchdog/PlatformWatchdog.h b/src/framework/internal/Watchdog/PlatformWatchdog.h similarity index 56% rename from framework/Watchdog/include/Watchdog/PlatformWatchdog.h rename to src/framework/internal/Watchdog/PlatformWatchdog.h index 77d2bb918..fad242706 100644 --- a/framework/Watchdog/include/Watchdog/PlatformWatchdog.h +++ b/src/framework/internal/Watchdog/PlatformWatchdog.h @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMABSTRACTION_PLATFORMWATCHDOG_H -#define RAMSES_PLATFORMABSTRACTION_PLATFORMWATCHDOG_H +#pragma once -#include "ramses-framework-api/IThreadWatchdogNotification.h" +#include "ramses/framework/IThreadWatchdogNotification.h" #include #include -namespace ramses_internal +namespace ramses::internal { inline - static constexpr std::string_view EnumToString(const ramses::ERamsesThreadIdentifier& thread) + static constexpr std::string_view EnumToString(const ERamsesThreadIdentifier& thread) { switch (thread) { - case ramses::ERamsesThreadIdentifier::Workers: return "ERamsesThread_Workers"; - case ramses::ERamsesThreadIdentifier::Renderer: return "ERamsesThread_Renderer"; - case ramses::ERamsesThreadIdentifier::Unknown: return "ERamsesThread_Unknown"; + case ERamsesThreadIdentifier::Workers: return "ERamsesThread_Workers"; + case ERamsesThreadIdentifier::Renderer: return "ERamsesThread_Renderer"; + case ERamsesThreadIdentifier::Unknown: return "ERamsesThread_Unknown"; } return ""; } @@ -31,7 +30,7 @@ namespace ramses_internal class PlatformWatchdog { public: - PlatformWatchdog(std::chrono::milliseconds notificationInterval, ramses::ERamsesThreadIdentifier thread, ramses::IThreadWatchdogNotification* callback); + PlatformWatchdog(std::chrono::milliseconds notificationInterval, ERamsesThreadIdentifier thread, IThreadWatchdogNotification* callback); virtual ~PlatformWatchdog(); void notifyWatchdog(); @@ -39,10 +38,8 @@ namespace ramses_internal private: const std::chrono::milliseconds m_interval; - ramses::ERamsesThreadIdentifier m_thread; - ramses::IThreadWatchdogNotification* m_watchdogCallback; + ERamsesThreadIdentifier m_thread; + IThreadWatchdogNotification* m_watchdogCallback; std::chrono::milliseconds m_lastNotificationTime; }; } - -#endif diff --git a/framework/Watchdog/include/Watchdog/ThreadAliveNotifierMock.h b/src/framework/internal/Watchdog/ThreadAliveNotifierMock.h similarity index 85% rename from framework/Watchdog/include/Watchdog/ThreadAliveNotifierMock.h rename to src/framework/internal/Watchdog/ThreadAliveNotifierMock.h index b183860b0..624040beb 100644 --- a/framework/Watchdog/include/Watchdog/ThreadAliveNotifierMock.h +++ b/src/framework/internal/Watchdog/ThreadAliveNotifierMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_THREADALIVENOTIFIERMOCK_H -#define RAMSES_THREADALIVENOTIFIERMOCK_H +#pragma once #include "gmock/gmock.h" -#include "Watchdog/IThreadAliveNotifier.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" -namespace ramses_internal +namespace ramses::internal { class ThreadAliveNotifierMock : public IThreadAliveNotifier { @@ -24,7 +23,6 @@ namespace ramses_internal ON_CALL(*this, registerThread()).WillByDefault(testing::Return(dummyThreadId)); ON_CALL(*this, calculateTimeout()).WillByDefault(testing::Return(std::chrono::milliseconds{ 10 })); } - ~ThreadAliveNotifierMock() override {} MOCK_METHOD(uint64_t, registerThread, (), (override)); MOCK_METHOD(void, unregisterThread, (uint64_t identifier), (override)); @@ -32,5 +30,3 @@ namespace ramses_internal MOCK_METHOD(std::chrono::milliseconds, calculateTimeout, (), (const, override)); }; } - -#endif diff --git a/framework/Watchdog/src/ThreadWatchdog.cpp b/src/framework/internal/Watchdog/ThreadWatchdog.cpp similarity index 94% rename from framework/Watchdog/src/ThreadWatchdog.cpp rename to src/framework/internal/Watchdog/ThreadWatchdog.cpp index c652eaebc..78ed40875 100644 --- a/framework/Watchdog/src/ThreadWatchdog.cpp +++ b/src/framework/internal/Watchdog/ThreadWatchdog.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Watchdog/ThreadWatchdog.h" +#include "internal/Watchdog/ThreadWatchdog.h" #include #include -namespace ramses_internal +namespace ramses::internal { - ThreadWatchdog::ThreadWatchdog(const ThreadWatchdogConfig& watchdogConfig, ramses::ERamsesThreadIdentifier identifier) + ThreadWatchdog::ThreadWatchdog(const ThreadWatchdogConfig& watchdogConfig, ERamsesThreadIdentifier identifier) : m_watchdogNotifier(std::chrono::milliseconds{ watchdogConfig.getWatchdogNotificationInterval(identifier) }, identifier, watchdogConfig.getCallBack()) { } diff --git a/framework/Watchdog/include/Watchdog/ThreadWatchdog.h b/src/framework/internal/Watchdog/ThreadWatchdog.h similarity index 85% rename from framework/Watchdog/include/Watchdog/ThreadWatchdog.h rename to src/framework/internal/Watchdog/ThreadWatchdog.h index 8e779f055..8d0ac1e4f 100644 --- a/framework/Watchdog/include/Watchdog/ThreadWatchdog.h +++ b/src/framework/internal/Watchdog/ThreadWatchdog.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_THREADWATCHDOG_H -#define RAMSES_THREADWATCHDOG_H +#pragma once -#include "ThreadWatchdogConfig.h" +#include "impl/ThreadWatchdogConfig.h" #include "IThreadAliveNotifier.h" -#include "Watchdog/PlatformWatchdog.h" +#include "internal/Watchdog/PlatformWatchdog.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ThreadWatchdog : public IThreadAliveNotifier { public: - ThreadWatchdog(const ThreadWatchdogConfig& watchdogConfig, ramses::ERamsesThreadIdentifier identifier); + ThreadWatchdog(const ThreadWatchdogConfig& watchdogConfig, ERamsesThreadIdentifier identifier); uint64_t registerThread() final override; void unregisterThread(uint64_t identifier) final override; @@ -37,5 +36,3 @@ namespace ramses_internal PlatformWatchdog m_watchdogNotifier; }; } - -#endif diff --git a/ramses-cli/CMakeLists.txt b/src/ramses-cli/CMakeLists.txt similarity index 58% rename from ramses-cli/CMakeLists.txt rename to src/ramses-cli/CMakeLists.txt index 74802d953..887259089 100644 --- a/ramses-cli/CMakeLists.txt +++ b/src/ramses-cli/CMakeLists.txt @@ -9,31 +9,13 @@ add_library(ramses-framework-cli INTERFACE) target_include_directories(ramses-framework-cli INTERFACE include) target_link_libraries(ramses-framework-cli INTERFACE - ramses-framework-api + ramses-api CLI11::CLI11 ) -if(TARGET ramses-renderer-api) +if(TARGET ramses-renderer) add_library(ramses-cli INTERFACE) target_link_libraries(ramses-cli INTERFACE ramses-framework-cli - ramses-renderer-api ) - - if (${ramses-sdk_BUILD_TESTS}) - add_executable(ramses-cli-test - test/ramses-cli-test.cpp - include/ramses-cli.h - ) - target_link_libraries(ramses-cli-test PRIVATE - ramses-cli - ramses-gmock-main - ramses-framework - ramses-renderer-impl - ) - makeTestFromTarget( - TARGET ramses-cli-test - SUFFIX UNITTEST) - folderizeTarget(ramses-cli-test) - endif() endif() diff --git a/ramses-cli/include/ramses-cli.h b/src/ramses-cli/include/ramses-cli.h similarity index 94% rename from ramses-cli/include/ramses-cli.h rename to src/ramses-cli/include/ramses-cli.h index f58b49ff5..7307f7500 100644 --- a/ramses-cli/include/ramses-cli.h +++ b/src/ramses-cli/include/ramses-cli.h @@ -8,8 +8,8 @@ #pragma once #include "ramses-framework-cli.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" namespace ramses { @@ -108,15 +108,11 @@ namespace ramses }, "set window height"); grp->add_flag_function( "-f,--fullscreen", [&](auto /*unused*/) { config.setWindowFullscreen(true); }, "enable fullscreen mode"); - grp->add_flag_function( - "--borderless", [&](auto /*unused*/) { config.setWindowBorderless(true); }, "disable window borders"); grp->add_flag_function( "--resizable", [&](auto /*unused*/) { config.setResizable(true); }, "enables resizable renderer window"); grp->add_option_function( "--msaa", [&](auto value) { config.setMultiSampling(value); }, "set msaa (antialiasing) sample count") ->check(CLI::IsMember({1, 2, 4, 8})); - grp->add_flag_function( - "--delete-effects", [&](auto /*unused*/) { config.keepEffectsUploaded(false); }, "do not keep effects uploaded"); grp->add_flag_function( "--ivi-visible,!--no-ivi-visible", [&](auto /*unused*/) { config.setWindowIviVisible(); }, "set IVI surface visible when created"); grp->add_option_function( diff --git a/ramses-cli/include/ramses-framework-cli.h b/src/ramses-cli/include/ramses-framework-cli.h similarity index 99% rename from ramses-cli/include/ramses-framework-cli.h rename to src/ramses-cli/include/ramses-framework-cli.h index 89132db93..1ff9b98f8 100644 --- a/ramses-cli/include/ramses-framework-cli.h +++ b/src/ramses-cli/include/ramses-framework-cli.h @@ -8,7 +8,7 @@ #pragma once #include "CLI/CLI.hpp" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" namespace ramses { diff --git a/renderer/Platform/Platform_Wayland_Shell_EGL/CMakeLists.txt b/src/renderer/CMakeLists.txt similarity index 61% rename from renderer/Platform/Platform_Wayland_Shell_EGL/CMakeLists.txt rename to src/renderer/CMakeLists.txt index 853211073..396dc980c 100644 --- a/renderer/Platform/Platform_Wayland_Shell_EGL/CMakeLists.txt +++ b/src/renderer/CMakeLists.txt @@ -1,18 +1,21 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- +add_subdirectory(internal/RendererLib) +add_subdirectory(internal/Platform) + createModule( - NAME Platform_Wayland_Shell_EGL + NAME ramses-renderer TYPE STATIC_LIBRARY ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Platform_Wayland_Shell_EGL/*.h - src/*.cpp - DEPENDENCIES Platform_Wayland_EGL - Window_Wayland_Shell + + SRC_FILES impl/*.h + impl/*.cpp + + DEPENDENCIES Platform ) diff --git a/src/renderer/impl/BinaryShaderCache.cpp b/src/renderer/impl/BinaryShaderCache.cpp new file mode 100644 index 000000000..0654b810b --- /dev/null +++ b/src/renderer/impl/BinaryShaderCache.cpp @@ -0,0 +1,91 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/renderer/BinaryShaderCache.h" +#include "impl/BinaryShaderCacheImpl.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "ramses/framework/RamsesFrameworkTypes.h" + +namespace ramses +{ + static ramses::internal::ResourceContentHash EffectIdToResourceContentHash(const effectId_t& effectId) + { + return ramses::internal::ResourceContentHash(effectId.lowPart, effectId.highPart); + } + + BinaryShaderCache::BinaryShaderCache() + : m_impl(*new internal::BinaryShaderCacheImpl()) + { + + } + + BinaryShaderCache::~BinaryShaderCache() + { + delete &m_impl; + } + + void BinaryShaderCache::deviceSupportsBinaryShaderFormats(const binaryShaderFormatId_t* supportedFormats, uint32_t numSupportedFormats) + { + m_impl.deviceSupportsBinaryShaderFormats(supportedFormats, numSupportedFormats); + } + + bool BinaryShaderCache::hasBinaryShader(effectId_t effectId) const + { + return m_impl.hasBinaryShader(EffectIdToResourceContentHash(effectId)); + } + + uint32_t BinaryShaderCache::getBinaryShaderSize(effectId_t effectId) const + { + return m_impl.getBinaryShaderSize(EffectIdToResourceContentHash(effectId)); + } + + binaryShaderFormatId_t BinaryShaderCache::getBinaryShaderFormat(effectId_t effectId) const + { + return m_impl.getBinaryShaderFormat(EffectIdToResourceContentHash(effectId)); + } + + bool BinaryShaderCache::shouldBinaryShaderBeCached(effectId_t effectId, sceneId_t sceneId) const + { + return m_impl.shouldBinaryShaderBeCached(EffectIdToResourceContentHash(effectId), ramses::internal::SceneId{ sceneId.getValue() }); + } + + void BinaryShaderCache::getBinaryShaderData(effectId_t effectId, std::byte* buffer, uint32_t bufferSize) const + { + m_impl.getBinaryShaderData(EffectIdToResourceContentHash(effectId), buffer, bufferSize); + } + + void BinaryShaderCache::storeBinaryShader(effectId_t effectId, sceneId_t sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) + { + m_impl.storeBinaryShader(EffectIdToResourceContentHash(effectId), ramses::internal::SceneId{ sceneId.getValue() }, binaryShaderData, binaryShaderDataSize, binaryShaderFormat); + } + + void BinaryShaderCache::saveToFile(std::string_view filePath) const + { + m_impl.saveToFile(filePath); + } + + bool BinaryShaderCache::loadFromFile(std::string_view filePath) + { + return m_impl.loadFromFile(filePath); + } + + void BinaryShaderCache::binaryShaderUploaded(effectId_t effectId, bool success) const + { + m_impl.binaryShaderUploaded(ramses::internal::ResourceContentHash(effectId.lowPart, effectId.highPart), success); + } + + internal::BinaryShaderCacheImpl& BinaryShaderCache::impl() + { + return m_impl; + } + + const internal::BinaryShaderCacheImpl& BinaryShaderCache::impl() const + { + return m_impl; + } +} diff --git a/renderer/ramses-renderer-impl/src/BinaryShaderCacheImpl.cpp b/src/renderer/impl/BinaryShaderCacheImpl.cpp similarity index 60% rename from renderer/ramses-renderer-impl/src/BinaryShaderCacheImpl.cpp rename to src/renderer/impl/BinaryShaderCacheImpl.cpp index 71d12545c..22c2bd0db 100644 --- a/renderer/ramses-renderer-impl/src/BinaryShaderCacheImpl.cpp +++ b/src/renderer/impl/BinaryShaderCacheImpl.cpp @@ -6,61 +6,59 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "BinaryShaderCacheImpl.h" -#include "Utils/File.h" -#include "Utils/LogMacros.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/BinaryOutputStream.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "impl/BinaryShaderCacheImpl.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" #include "city.h" -#include "TransportCommon/RamsesTransportProtocolVersion.h" +#include "internal/Communication/TransportCommon/RamsesTransportProtocolVersion.h" namespace { const uint32_t NUM_RESERVED_BYTES = 32; } -namespace ramses +namespace ramses::internal { void BinaryShaderCacheImpl::deviceSupportsBinaryShaderFormats(const binaryShaderFormatId_t* supportedFormats, uint32_t numSupportedFormats) { m_supportedFormats = {supportedFormats, supportedFormats + numSupportedFormats }; } - bool BinaryShaderCacheImpl::hasBinaryShader(const ramses_internal::ResourceContentHash& effectId) const + bool BinaryShaderCacheImpl::hasBinaryShader(const ResourceContentHash& effectId) const { if (!m_binaryShaders.contains(effectId)) return false; // do not report shader as available if not matching any supported format by device - return ramses_internal::contains_c(m_supportedFormats, getBinaryShaderFormat(effectId)); + return contains_c(m_supportedFormats, getBinaryShaderFormat(effectId)); } - uint32_t BinaryShaderCacheImpl::getBinaryShaderSize(const ramses_internal::ResourceContentHash& effectId) const + uint32_t BinaryShaderCacheImpl::getBinaryShaderSize(const ResourceContentHash& effectId) const { const auto iter = m_binaryShaders.find(effectId); return (iter != m_binaryShaders.end() ? uint32_t(iter->value.data.size()) : 0u); } - binaryShaderFormatId_t BinaryShaderCacheImpl::getBinaryShaderFormat(const ramses_internal::ResourceContentHash& effectId) const + binaryShaderFormatId_t BinaryShaderCacheImpl::getBinaryShaderFormat(const ResourceContentHash& effectId) const { const auto iter = m_binaryShaders.find(effectId); return (iter != m_binaryShaders.end() ? binaryShaderFormatId_t{ iter->value.format.getValue() } : binaryShaderFormatId_t{ 0 }); } - bool BinaryShaderCacheImpl::shouldBinaryShaderBeCached(const ramses_internal::ResourceContentHash& effectId, ramses_internal::SceneId sceneId) const + // NOLINTNEXTLINE(readability-convert-member-functions-to-static): design decision + bool BinaryShaderCacheImpl::shouldBinaryShaderBeCached([[maybe_unused]] const ResourceContentHash& effectId, + [[maybe_unused]] SceneId sceneId) const { - UNUSED(effectId); - UNUSED(sceneId); return true; } - void BinaryShaderCacheImpl::getBinaryShaderData(const ramses_internal::ResourceContentHash& effectId, uint8_t* buffer, uint32_t bufferSize) const + void BinaryShaderCacheImpl::getBinaryShaderData(const ResourceContentHash& effectId, std::byte* buffer, [[maybe_unused]] uint32_t bufferSize) const { - UNUSED(bufferSize); - assert(nullptr != buffer); assert(bufferSize > 0); @@ -70,14 +68,17 @@ namespace ramses return; } - const uint32_t dataSize = static_cast(iter->value.data.size()); + const auto dataSize = static_cast(iter->value.data.size()); assert(bufferSize >= dataSize); - ramses_internal::PlatformMemory::Copy(buffer, iter->value.data.data(), dataSize); + PlatformMemory::Copy(buffer, iter->value.data.data(), dataSize); } - void BinaryShaderCacheImpl::storeBinaryShader(const ramses_internal::ResourceContentHash& effectId, ramses_internal::SceneId sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat) + void BinaryShaderCacheImpl::storeBinaryShader(const ResourceContentHash& effectId, + [[maybe_unused]] SceneId sceneId, + const std::byte* binaryShaderData, + uint32_t binaryShaderDataSize, + binaryShaderFormatId_t binaryShaderFormat) { - UNUSED(sceneId); assert(nullptr != binaryShaderData); assert(binaryShaderDataSize > 0); @@ -85,32 +86,32 @@ namespace ramses if (m_binaryShaders.contains(effectId)) return; - BinaryShader binaryShader = { {binaryShaderData, binaryShaderData + binaryShaderDataSize}, ramses_internal::BinaryShaderFormatID{ binaryShaderFormat.getValue() } }; - m_binaryShaders.put(effectId, std::move(binaryShader)); + BinaryShader binaryShader = { {binaryShaderData, binaryShaderData + binaryShaderDataSize}, BinaryShaderFormatID{ binaryShaderFormat.getValue() } }; + m_binaryShaders.put(effectId, binaryShader); } bool BinaryShaderCacheImpl::loadFromFile(std::string_view filePath) { - ramses_internal::File file(filePath); + ramses::internal::File file(filePath); if (!file.exists()) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: file does not exist: " << filePath); + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: file does not exist: " << filePath); return false; } - ramses_internal::BinaryFileInputStream fileInputStream(file); - if (ramses_internal::EStatus::Ok != fileInputStream.getState()) + BinaryFileInputStream fileInputStream(file); + if (EStatus::Ok != fileInputStream.getState()) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: failed to load file: " << filePath << " errorstate: " << fileInputStream.getState()); + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: failed to load file: " << filePath << " errorstate: " << fileInputStream.getState()); return false; } - FileHeader fileHeader; + FileHeader fileHeader{}; size_t actualSize = 0; if (!file.getSizeInBytes(actualSize) || actualSize < sizeof(FileHeader)) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: Invalid file size - cache needs to be repopulated and saved again"); return false; } @@ -119,7 +120,7 @@ namespace ramses if (actualSize != fileHeader.fileSize) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: File did not match the size stored in the file header, file is corrupt - cache needs to be repopulated and saved again"); return false; } @@ -128,7 +129,7 @@ namespace ramses if (fileHeader.transportVersion != RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: File version " << fileHeader.transportVersion << " did not match the program version " << RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR << " - cache needs to be repopulated and saved again"); return false; } @@ -137,8 +138,8 @@ namespace ramses const uint32_t contentSize = fileHeader.fileSize - sizeof(fileHeader); - using ramses_internal::Byte; - std::vector content(contentSize); + using std::byte; + std::vector content(contentSize); fileInputStream.read(content.data(), contentSize); @@ -146,12 +147,12 @@ namespace ramses if (checksum != fileHeader.checksum) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: Checksum was wrong, file is corrupt - cache needs to be repopulated and saved again"); return false; } - ramses_internal::BinaryInputStream inputStream(content.data()); + BinaryInputStream inputStream(content.data()); uint32_t numBinaryShaders = 0; inputStream >> numBinaryShaders; @@ -160,14 +161,14 @@ namespace ramses for (uint32_t index = 0; index < numBinaryShaders; index++) { BinaryShader binaryShader; - ramses_internal::ResourceContentHash effectId; + ResourceContentHash effectId; if (!deserializeBinaryShader(inputStream, effectId, binaryShader.data, binaryShader.format)) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: Deserialization failed, abort loading at " << index << " of " << numBinaryShaders); + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::loadFromFile: Deserialization failed, abort loading at " << index << " of " << numBinaryShaders); return false; } - m_binaryShaders.put(effectId, std::move(binaryShader)); + m_binaryShaders.put(effectId, binaryShader); } return true; @@ -175,7 +176,7 @@ namespace ramses void BinaryShaderCacheImpl::saveToFile(std::string_view filePath) const { - ramses_internal::BinaryOutputStream outputStream; + BinaryOutputStream outputStream; { std::lock_guard g(m_hashMapLock); @@ -185,7 +186,7 @@ namespace ramses serializeBinaryShader(outputStream, binaryShader.key, binaryShader.value.data, binaryShader.value.format); } - const uint32_t contentSize = static_cast(outputStream.getSize()); + const auto contentSize = static_cast(outputStream.getSize()); const uint64_t checksum = cityhash::CityHash64(reinterpret_cast(outputStream.getData()), contentSize); FileHeader fileHeader = {}; @@ -193,30 +194,31 @@ namespace ramses fileHeader.transportVersion = RAMSES_TRANSPORT_PROTOCOL_VERSION_MAJOR; fileHeader.checksum = checksum; - ramses_internal::File file(filePath); - ramses_internal::BinaryFileOutputStream outputFileStream(file); + ramses::internal::File file(filePath); + ramses::internal::BinaryFileOutputStream outputFileStream(file); - if (outputFileStream.getState() == ramses_internal::EStatus::Ok) + if (outputFileStream.getState() == EStatus::Ok) { outputFileStream << fileHeader.fileSize << fileHeader.transportVersion << fileHeader.checksum; outputFileStream.write(outputStream.getData(), contentSize); } else { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCacheImpl::saveToFile: failed to open " << filePath); } } - void BinaryShaderCacheImpl::binaryShaderUploaded(ramses_internal::ResourceContentHash effectHash, bool success) const + // NOLINTNEXTLINE(readability-convert-member-functions-to-static): design decision + void BinaryShaderCacheImpl::binaryShaderUploaded(ResourceContentHash effectHash, bool success) const { if (!success) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "BinaryShaderCache: Failed to upload binary shader from cache for effect id: " << effectHash); + LOG_WARN(CONTEXT_RENDERER, "BinaryShaderCache: Failed to upload binary shader from cache for effect id: " << effectHash); } } - bool BinaryShaderCacheImpl::deserializeBinaryShader(ramses_internal::IInputStream& inputStream, ramses_internal::ResourceContentHash& effectId, ramses_internal::UInt8Vector& binaryShaderData, ramses_internal::BinaryShaderFormatID& binaryShaderFormat) + bool BinaryShaderCacheImpl::deserializeBinaryShader(IInputStream& inputStream, ResourceContentHash& effectId, std::vector& binaryShaderData, BinaryShaderFormatID& binaryShaderFormat) { binaryShaderData.clear(); binaryShaderFormat = {}; @@ -243,11 +245,11 @@ namespace ramses return true; } - void BinaryShaderCacheImpl::serializeBinaryShader(ramses_internal::IOutputStream& outputStream, const ramses_internal::ResourceContentHash& effectId, const ramses_internal::UInt8Vector& binaryShaderData, ramses_internal::BinaryShaderFormatID binaryShaderFormat) + void BinaryShaderCacheImpl::serializeBinaryShader(IOutputStream& outputStream, const ResourceContentHash& effectId, const std::vector& binaryShaderData, BinaryShaderFormatID binaryShaderFormat) { outputStream << effectId; - const uint32_t binaryShaderDataSize = static_cast(binaryShaderData.size()); + const auto binaryShaderDataSize = static_cast(binaryShaderData.size()); outputStream << binaryShaderDataSize; outputStream << binaryShaderFormat.getValue(); diff --git a/src/renderer/impl/BinaryShaderCacheImpl.h b/src/renderer/impl/BinaryShaderCacheImpl.h new file mode 100644 index 000000000..22673180e --- /dev/null +++ b/src/renderer/impl/BinaryShaderCacheImpl.h @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/RendererLib/Types.h" +#include "ramses/renderer/Types.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" + +#include +#include + +namespace ramses::internal +{ + class IOutputStream; + class IInputStream; + + class BinaryShaderCacheImpl + { + public: + void deviceSupportsBinaryShaderFormats(const binaryShaderFormatId_t* supportedFormats, uint32_t numSupportedFormats); + bool hasBinaryShader(const ResourceContentHash& effectId) const; + uint32_t getBinaryShaderSize(const ResourceContentHash& effectId) const; + binaryShaderFormatId_t getBinaryShaderFormat(const ResourceContentHash& effectId) const; + bool shouldBinaryShaderBeCached(const ResourceContentHash& effectId, SceneId sceneId) const; + void getBinaryShaderData(const ResourceContentHash& effectId, std::byte* buffer, uint32_t bufferSize) const; + void storeBinaryShader(const ResourceContentHash& effectId, SceneId sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat); + void binaryShaderUploaded(ResourceContentHash effectHash, bool success) const; + + void saveToFile(std::string_view filePath) const; + bool loadFromFile(std::string_view filePath); + + struct FileHeader + { + uint32_t fileSize; + uint32_t transportVersion; + uint64_t checksum; + }; + + private: + static void serializeBinaryShader(IOutputStream& outputStream, const ResourceContentHash& effectId, const std::vector& binaryShaderData, BinaryShaderFormatID binaryShaderFormat); + static bool deserializeBinaryShader(IInputStream& inputStream, ResourceContentHash& effectId, std::vector& binaryShaderData, BinaryShaderFormatID& binaryShaderFormat); + + struct BinaryShader + { + std::vector data; + BinaryShaderFormatID format; + }; + using BinaryShaderTable = HashMap; + + BinaryShaderTable m_binaryShaders; + std::vector m_supportedFormats; + // protects HashMap write of new shaders concurrently with saving of file + // WARNING: Does not protect loading from file concurrently with querying for shaders! + // Reason: Avoid performance degradation because unknown if Integrity mutexes are cheap without congestion. + mutable std::mutex m_hashMapLock; + }; +} diff --git a/renderer/ramses-renderer-impl/src/BinaryShaderCacheProxy.cpp b/src/renderer/impl/BinaryShaderCacheProxy.cpp similarity index 61% rename from renderer/ramses-renderer-impl/src/BinaryShaderCacheProxy.cpp rename to src/renderer/impl/BinaryShaderCacheProxy.cpp index 842954a4a..58bc5d3e1 100644 --- a/renderer/ramses-renderer-impl/src/BinaryShaderCacheProxy.cpp +++ b/src/renderer/impl/BinaryShaderCacheProxy.cpp @@ -6,63 +6,63 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "BinaryShaderCacheProxy.h" -#include "ramses-renderer-api/IBinaryShaderCache.h" +#include "impl/BinaryShaderCacheProxy.h" +#include "ramses/renderer/IBinaryShaderCache.h" -namespace ramses +namespace ramses::internal { BinaryShaderCacheProxy::BinaryShaderCacheProxy(ramses::IBinaryShaderCache& cache) : m_cache(cache) { } - void BinaryShaderCacheProxy::deviceSupportsBinaryShaderFormats(const std::vector& supportedFormats) + void BinaryShaderCacheProxy::deviceSupportsBinaryShaderFormats(const std::vector& supportedFormats) { // called only once before any other call to binary shader cache, no need to lock std::vector formats; formats.reserve(supportedFormats.size()); std::transform(supportedFormats.cbegin(), supportedFormats.cend(), std::back_inserter(formats), - [](ramses_internal::BinaryShaderFormatID id) { return binaryShaderFormatId_t{ id.getValue() }; }); + [](BinaryShaderFormatID id) { return binaryShaderFormatId_t{ id.getValue() }; }); m_cache.deviceSupportsBinaryShaderFormats(formats.data(), uint32_t(formats.size())); } - bool BinaryShaderCacheProxy::hasBinaryShader(ramses_internal::ResourceContentHash effectHash) const + bool BinaryShaderCacheProxy::hasBinaryShader(ResourceContentHash effectHash) const { std::lock_guard lock(m_mutex); return m_cache.hasBinaryShader({ effectHash.lowPart, effectHash.highPart }); } - uint32_t BinaryShaderCacheProxy::getBinaryShaderSize(ramses_internal::ResourceContentHash effectHash) const + uint32_t BinaryShaderCacheProxy::getBinaryShaderSize(ResourceContentHash effectHash) const { std::lock_guard lock(m_mutex); return m_cache.getBinaryShaderSize({ effectHash.lowPart, effectHash.highPart }); } - ramses_internal::BinaryShaderFormatID BinaryShaderCacheProxy::getBinaryShaderFormat(ramses_internal::ResourceContentHash effectHash) const + BinaryShaderFormatID BinaryShaderCacheProxy::getBinaryShaderFormat(ResourceContentHash effectHash) const { std::lock_guard lock(m_mutex); - return ramses_internal::BinaryShaderFormatID{ m_cache.getBinaryShaderFormat({ effectHash.lowPart, effectHash.highPart }).getValue() }; + return BinaryShaderFormatID{ m_cache.getBinaryShaderFormat({ effectHash.lowPart, effectHash.highPart }).getValue() }; } - bool BinaryShaderCacheProxy::shouldBinaryShaderBeCached(ramses_internal::ResourceContentHash effectHash, ramses_internal::SceneId sceneId) const + bool BinaryShaderCacheProxy::shouldBinaryShaderBeCached(ResourceContentHash effectHash, SceneId sceneId) const { std::lock_guard lock(m_mutex); return m_cache.shouldBinaryShaderBeCached({ effectHash.lowPart, effectHash.highPart }, sceneId_t(sceneId.getValue())); } - void BinaryShaderCacheProxy::getBinaryShaderData(ramses_internal::ResourceContentHash effectHash, uint8_t* buffer, uint32_t bufferSize) const + void BinaryShaderCacheProxy::getBinaryShaderData(ResourceContentHash effectHash, std::byte* buffer, uint32_t bufferSize) const { std::lock_guard lock(m_mutex); m_cache.getBinaryShaderData({ effectHash.lowPart, effectHash.highPart }, buffer, bufferSize); } - void BinaryShaderCacheProxy::storeBinaryShader(ramses_internal::ResourceContentHash effectHash, ramses_internal::SceneId sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, ramses_internal::BinaryShaderFormatID binaryShaderFormat) + void BinaryShaderCacheProxy::storeBinaryShader(ResourceContentHash effectHash, SceneId sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) { std::lock_guard lock(m_mutex); m_cache.storeBinaryShader({ effectHash.lowPart, effectHash.highPart }, sceneId_t{ sceneId.getValue() }, binaryShaderData, binaryShaderDataSize, binaryShaderFormatId_t{ binaryShaderFormat.getValue() }); } - void BinaryShaderCacheProxy::binaryShaderUploaded(ramses_internal::ResourceContentHash effectHash, bool success) const + void BinaryShaderCacheProxy::binaryShaderUploaded(ResourceContentHash effectHash, bool success) const { std::lock_guard lock(m_mutex); m_cache.binaryShaderUploaded({ effectHash.lowPart, effectHash.highPart }, success); diff --git a/src/renderer/impl/BinaryShaderCacheProxy.h b/src/renderer/impl/BinaryShaderCacheProxy.h new file mode 100644 index 000000000..18797e13d --- /dev/null +++ b/src/renderer/impl/BinaryShaderCacheProxy.h @@ -0,0 +1,45 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/RendererLib/PlatformInterface/IBinaryShaderCache.h" +#include "ramses/renderer/Types.h" +#include + +namespace ramses +{ + class IBinaryShaderCache; +} + +namespace ramses::internal +{ + class BinaryShaderCacheProxy final : public ramses::internal::IBinaryShaderCache + { + public: + explicit BinaryShaderCacheProxy(ramses::IBinaryShaderCache& cache); + ~BinaryShaderCacheProxy() override = default; + + void deviceSupportsBinaryShaderFormats(const std::vector& supportedFormats) override; + + bool hasBinaryShader(ResourceContentHash effectHash) const override; + uint32_t getBinaryShaderSize(ResourceContentHash effectHash) const override; + BinaryShaderFormatID getBinaryShaderFormat(ResourceContentHash effectHash) const override; + void getBinaryShaderData(ResourceContentHash effectHash, std::byte* buffer, uint32_t bufferSize) const override; + + bool shouldBinaryShaderBeCached(ResourceContentHash effectHash, SceneId sceneId) const override; + void storeBinaryShader(ResourceContentHash effectHash, SceneId sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) override; + void binaryShaderUploaded(ResourceContentHash effectHash, bool success) const override; + std::once_flag& binaryShaderFormatsReported() override; + + private: + ramses::IBinaryShaderCache& m_cache; + mutable std::mutex m_mutex; + std::once_flag m_supportedFormatsReported; + }; +} diff --git a/renderer/ramses-renderer-impl/src/CommandDispatchingThread.cpp b/src/renderer/impl/CommandDispatchingThread.cpp similarity index 89% rename from renderer/ramses-renderer-impl/src/CommandDispatchingThread.cpp rename to src/renderer/impl/CommandDispatchingThread.cpp index 4667734ea..a9655cc59 100644 --- a/renderer/ramses-renderer-impl/src/CommandDispatchingThread.cpp +++ b/src/renderer/impl/CommandDispatchingThread.cpp @@ -7,13 +7,12 @@ // ------------------------------------------------------------------------- #include "CommandDispatchingThread.h" -#include "RamsesRendererUtils.h" -#include "RendererLib/DisplayDispatcher.h" -#include "Utils/LogMacros.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Watchdog/PlatformWatchdog.h" +#include "internal/RendererLib/DisplayDispatcher.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Watchdog/PlatformWatchdog.h" -namespace ramses_internal +namespace ramses::internal { CommandDispatchingThread::CommandDispatchingThread(DisplayDispatcher& displayDispatcher, RendererCommandBuffer& commandBuffer, IThreadAliveNotifier& watchdog) : m_displayDispatcher{ displayDispatcher } diff --git a/renderer/ramses-renderer-impl/include/CommandDispatchingThread.h b/src/renderer/impl/CommandDispatchingThread.h similarity index 80% rename from renderer/ramses-renderer-impl/include/CommandDispatchingThread.h rename to src/renderer/impl/CommandDispatchingThread.h index 2da726f38..c878f38dd 100644 --- a/renderer/ramses-renderer-impl/include/CommandDispatchingThread.h +++ b/src/renderer/impl/CommandDispatchingThread.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMANDDISPATCHINGTHREAD_H -#define RAMSES_COMMANDDISPATCHINGTHREAD_H +#pragma once -#include "RendererAPI/ELoopMode.h" -#include "RendererLib/RendererCommands.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Watchdog/IThreadAliveNotifier.h" +#include "internal/RendererLib/Enums/ELoopMode.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" #include #include -namespace ramses_internal +namespace ramses::internal { class DisplayDispatcher; class RendererCommandBuffer; @@ -41,5 +40,3 @@ namespace ramses_internal RendererCommands m_tmpCommands; }; } - -#endif diff --git a/src/renderer/impl/DisplayConfig.cpp b/src/renderer/impl/DisplayConfig.cpp new file mode 100644 index 000000000..14970ecb2 --- /dev/null +++ b/src/renderer/impl/DisplayConfig.cpp @@ -0,0 +1,277 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/framework/ValidationReport.h" + +// internal +#include "impl/DisplayConfigImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/APILoggingMacros.h" + +namespace ramses +{ + DisplayConfig::DisplayConfig() + : m_impl{ std::make_unique() } + { + } + + DisplayConfig::~DisplayConfig() = default; + + DisplayConfig::DisplayConfig(const DisplayConfig& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + DisplayConfig::DisplayConfig(DisplayConfig&& other) noexcept = default; + + DisplayConfig& DisplayConfig::operator=(const DisplayConfig& other) + { + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + DisplayConfig& DisplayConfig::operator=(DisplayConfig&& other) noexcept = default; + + bool DisplayConfig::setDeviceType(EDeviceType deviceType) + { + return m_impl->setDeviceType(deviceType); + } + + EDeviceType DisplayConfig::getDeviceType() const + { + return m_impl->getDeviceType(); + } + + bool DisplayConfig::setWindowType(EWindowType windowType) + { + return m_impl->setWindowType(windowType); + } + + EWindowType DisplayConfig::getWindowType() const + { + return m_impl->getWindowType(); + } + + bool DisplayConfig::setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height) + { + const auto status = m_impl->setWindowRectangle(x, y, width, height); + LOG_HL_RENDERER_API4(status, x, y, width, height); + return status; + } + + bool DisplayConfig::getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const + { + return m_impl->getWindowRectangle(x, y, width, height); + } + + bool DisplayConfig::setWindowFullscreen(bool fullscreen) + { + const auto status = m_impl->setFullscreen(fullscreen); + LOG_HL_RENDERER_API1(status, fullscreen); + return status; + } + + bool DisplayConfig::isWindowFullscreen() const + { + return m_impl->isFullscreen(); + } + + bool DisplayConfig::setMultiSampling(uint32_t numSamples) + { + const auto status = m_impl->setMultiSampling(numSamples); + LOG_HL_RENDERER_API1(status, numSamples); + return status; + } + + bool DisplayConfig::getMultiSamplingSamples(uint32_t& numSamples) const + { + return m_impl->getMultiSamplingSamples(numSamples); + } + + bool DisplayConfig::setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID) + { + const auto status = m_impl->setWaylandIviLayerID(waylandIviLayerID); + LOG_HL_RENDERER_API1(status, waylandIviLayerID); + return status; + } + + waylandIviLayerId_t DisplayConfig::getWaylandIviLayerID() const + { + return m_impl->getWaylandIviLayerID(); + } + + bool DisplayConfig::setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID) + { + const auto status = m_impl->setWaylandIviSurfaceID(waylandIviSurfaceID); + LOG_HL_RENDERER_API1(status, waylandIviSurfaceID.getValue()); + return status; + } + + waylandIviSurfaceId_t DisplayConfig::getWaylandIviSurfaceID() const + { + return m_impl->getWaylandIviSurfaceID(); + } + + bool DisplayConfig::setWaylandDisplay(std::string_view waylandDisplay) + { + return m_impl->setWaylandDisplay(waylandDisplay); + } + + std::string_view DisplayConfig::getWaylandDisplay() const + { + return m_impl->getWaylandDisplay(); + } + + bool DisplayConfig::setAsyncEffectUploadEnabled(bool enabled) + { + const auto status = m_impl->setAsyncEffectUploadEnabled(enabled); + LOG_HL_RENDERER_API1(status, enabled); + return status; + } + + void* DisplayConfig::getAndroidNativeWindow() const + { + return m_impl->getAndroidNativeWindow(); + } + + bool DisplayConfig::setAndroidNativeWindow(void* nativeWindowPtr) + { + return m_impl->setAndroidNativeWindow(nativeWindowPtr); + } + + IOSNativeWindowPtr DisplayConfig::getIOSNativeWindow() const + { + return m_impl->getIOSNativeWindow(); + } + + bool DisplayConfig::setIOSNativeWindow(IOSNativeWindowPtr nativeWindowPtr) + { + return m_impl->setIOSNativeWindow(nativeWindowPtr); + } + + bool DisplayConfig::setWindowIviVisible() + { + const auto status = m_impl->setWindowIviVisible(true); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool DisplayConfig::setGPUMemoryCacheSize(uint64_t size) + { + const auto status = m_impl->setGPUMemoryCacheSize(size); + LOG_HL_RENDERER_API1(status, size); + return status; + } + + bool DisplayConfig::setResizable(bool resizable) + { + const auto status = m_impl->setResizable(resizable); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool DisplayConfig::setClearColor(const vec4f& color) + { + const auto status = m_impl->setClearColor(color); + LOG_HL_RENDERER_API4(status, color.r, color.g, color.b, color.a); + return status; + } + + bool DisplayConfig::setDepthStencilBufferType(EDepthBufferType depthBufferType) + { + const auto status = m_impl->setDepthStencilBufferType(depthBufferType); + LOG_HL_RENDERER_API1(status, depthBufferType); + return status; + } + + bool DisplayConfig::setX11WindowHandle(X11WindowHandle x11WindowHandle) + { + const auto status = m_impl->setX11WindowHandle(x11WindowHandle); + LOG_HL_RENDERER_API1(status, x11WindowHandle.getValue()); + return status; + } + + X11WindowHandle DisplayConfig::getX11WindowHandle() const + { + return m_impl->getX11WindowHandle(); + } + + bool DisplayConfig::setWindowsWindowHandle(void* hwnd) + { + const auto status = m_impl->setWindowsWindowHandle(hwnd); + LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_PTR_STRING(hwnd)); + return status; + } + + void* DisplayConfig::getWindowsWindowHandle() const + { + return m_impl->getWindowsWindowHandle(); + } + + bool DisplayConfig::setWaylandEmbeddedCompositingSocketName(std::string_view socketname) + { + return m_impl->setWaylandEmbeddedCompositingSocketName(socketname); + } + + std::string_view DisplayConfig::getWaylandEmbeddedCompositingSocketName() const + { + return m_impl->getWaylandEmbeddedCompositingSocketName(); + } + + bool DisplayConfig::setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname) + { + return m_impl->setWaylandEmbeddedCompositingSocketGroup(groupname); + } + + bool DisplayConfig::setWaylandEmbeddedCompositingSocketFD(int socketFileDescriptor) + { + return m_impl->setWaylandEmbeddedCompositingSocketFD(socketFileDescriptor); + } + + bool DisplayConfig::setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions) + { + return m_impl->setWaylandEmbeddedCompositingSocketPermissions(permissions); + } + + bool DisplayConfig::setPlatformRenderNode(std::string_view renderNode) + { + return m_impl->setPlatformRenderNode(renderNode); + } + + bool DisplayConfig::setSwapInterval(int32_t interval) + { + return m_impl->setSwapInterval(interval); + } + + bool DisplayConfig::setScenePriority(sceneId_t sceneId, int32_t priority) + { + return m_impl->setScenePriority(sceneId, priority); + } + + bool DisplayConfig::setResourceUploadBatchSize(uint32_t batchSize) + { + return m_impl->setResourceUploadBatchSize(batchSize); + } + + void DisplayConfig::validate(ValidationReport& report) const + { + m_impl->validate(report.impl()); + } + + internal::DisplayConfigImpl& DisplayConfig::impl() + { + return *m_impl; + } + + const internal::DisplayConfigImpl& DisplayConfig::impl() const + { + return *m_impl; + } +} diff --git a/src/renderer/impl/DisplayConfigImpl.cpp b/src/renderer/impl/DisplayConfigImpl.cpp new file mode 100644 index 000000000..8623bb68c --- /dev/null +++ b/src/renderer/impl/DisplayConfigImpl.cpp @@ -0,0 +1,334 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/Core/Utils/LogMacros.h" +#include "impl/DisplayConfigImpl.h" +#include "impl/ValidationReportImpl.h" + +namespace ramses::internal +{ + DisplayConfigImpl::DisplayConfigImpl() = default; + + bool DisplayConfigImpl::setDeviceType(EDeviceType deviceType) + { + m_internalConfig.setDeviceType(deviceType); + return true; + } + + EDeviceType DisplayConfigImpl::getDeviceType() const + { + return m_internalConfig.getDeviceType(); + } + + bool DisplayConfigImpl::setWindowType(EWindowType windowType) + { + m_internalConfig.setWindowType(windowType); + return true; + } + + EWindowType DisplayConfigImpl::getWindowType() const + { + return m_internalConfig.getWindowType(); + } + + bool DisplayConfigImpl::setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height) + { + if (width == 0u || height == 0u) + { + LOG_ERROR(CONTEXT_CLIENT, "DisplayConfig::setWindowRectangle failed - width and/or height cannot be 0!"); + return false; + } + + m_internalConfig.setWindowPositionX(x); + m_internalConfig.setWindowPositionY(y); + m_internalConfig.setDesiredWindowWidth(width); + m_internalConfig.setDesiredWindowHeight(height); + + return true; + } + + bool DisplayConfigImpl::getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const + { + x = m_internalConfig.getWindowPositionX(); + y = m_internalConfig.getWindowPositionY(); + width = m_internalConfig.getDesiredWindowWidth(); + height = m_internalConfig.getDesiredWindowHeight(); + return true; + } + + bool DisplayConfigImpl::setFullscreen(bool fullscreen) + { + m_internalConfig.setFullscreenState(fullscreen); + return true; + } + + bool DisplayConfigImpl::isFullscreen() const + { + return m_internalConfig.getFullscreenState(); + } + + const ramses::internal::DisplayConfig& DisplayConfigImpl::getInternalDisplayConfig() const + { + return m_internalConfig; + } + + bool DisplayConfigImpl::setMultiSampling(uint32_t numSamples) + { + if (!ramses::internal::contains_c({ 1u, 2u, 4u, 8u }, numSamples)) + { + LOG_ERROR(CONTEXT_CLIENT, "DisplayConfigImpl::setMultiSampling failed - sample count must be 1, 2, 4 or 8!"); + return false; + } + + m_internalConfig.setAntialiasingSampleCount(numSamples); + + return true; + } + + bool DisplayConfigImpl::getMultiSamplingSamples(uint32_t& numSamples) const + { + const uint32_t sampleCount = m_internalConfig.getAntialiasingSampleCount(); + numSamples = sampleCount; + + return true; + } + + bool DisplayConfigImpl::setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID) + { + m_internalConfig.setWaylandIviLayerID(WaylandIviLayerId(waylandIviLayerID.getValue())); + return true; + } + + waylandIviLayerId_t DisplayConfigImpl::getWaylandIviLayerID() const + { + return waylandIviLayerId_t(m_internalConfig.getWaylandIviLayerID().getValue()); + } + + bool DisplayConfigImpl::setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID) + { + m_internalConfig.setWaylandIviSurfaceID(WaylandIviSurfaceId(waylandIviSurfaceID.getValue())); + return true; + } + + waylandIviSurfaceId_t DisplayConfigImpl::getWaylandIviSurfaceID() const + { + return waylandIviSurfaceId_t(m_internalConfig.getWaylandIviSurfaceID().getValue()); + } + + void* DisplayConfigImpl::getAndroidNativeWindow() const + { + return m_internalConfig.getAndroidNativeWindow().getValue(); + } + + bool DisplayConfigImpl::setAndroidNativeWindow(void * nativeWindowPtr) + { + m_internalConfig.setAndroidNativeWindow(ramses::internal::AndroidNativeWindowPtr(nativeWindowPtr)); + return true; + } + + IOSNativeWindowPtr DisplayConfigImpl::getIOSNativeWindow() const + { + return m_internalConfig.getIOSNativeWindow(); + } + + bool DisplayConfigImpl::setIOSNativeWindow(IOSNativeWindowPtr nativeWindowPtr) + { + m_internalConfig.setIOSNativeWindow(nativeWindowPtr); + return true; + } + + bool DisplayConfigImpl::setWindowIviVisible(bool visible) + { + m_internalConfig.setStartVisibleIvi(visible); + return true; + } + + bool DisplayConfigImpl::setResizable(bool resizable) + { + m_internalConfig.setResizable(resizable); + return true; + } + + bool DisplayConfigImpl::setGPUMemoryCacheSize(uint64_t size) + { + m_internalConfig.setGPUMemoryCacheSize(size); + return true; + } + + bool DisplayConfigImpl::setClearColor(const vec4f& color) + { + m_internalConfig.setClearColor(color); + return true; + } + + bool DisplayConfigImpl::setDepthStencilBufferType(EDepthBufferType depthBufferType) + { + m_internalConfig.setDepthStencilBufferType(depthBufferType); + return true; + } + + bool DisplayConfigImpl::setX11WindowHandle(X11WindowHandle x11WindowHandle) + { + m_internalConfig.setX11WindowHandle(x11WindowHandle); + return true; + } + + X11WindowHandle DisplayConfigImpl::getX11WindowHandle() const + { + return m_internalConfig.getX11WindowHandle(); + } + + bool DisplayConfigImpl::setWindowsWindowHandle(void* hwnd) + { + m_internalConfig.setWindowsWindowHandle(ramses::internal::WindowsWindowHandle(hwnd)); + return true; + } + + void* DisplayConfigImpl::getWindowsWindowHandle() const + { + return m_internalConfig.getWindowsWindowHandle().getValue(); + } + + bool DisplayConfigImpl::setWaylandDisplay(std::string_view waylandDisplay) + { + m_internalConfig.setWaylandDisplay(waylandDisplay); + return true; + } + + std::string_view DisplayConfigImpl::getWaylandDisplay() const + { + return m_internalConfig.getWaylandDisplay(); + } + + bool DisplayConfigImpl::setAsyncEffectUploadEnabled(bool enabled) + { + m_internalConfig.setAsyncEffectUploadEnabled(enabled); + return true; + } + + bool DisplayConfigImpl::setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname) + { + m_internalConfig.setWaylandEmbeddedCompositingSocketGroup(groupname); + return true; + } + + std::string_view DisplayConfigImpl::getWaylandSocketEmbeddedGroup() const + { + return m_internalConfig.getWaylandSocketEmbeddedGroup(); + } + + bool DisplayConfigImpl::setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions) + { + if (m_internalConfig.setWaylandEmbeddedCompositingSocketPermissions(permissions)) + return true; + LOG_ERROR(CONTEXT_CLIENT, "DisplayConfig::setWaylandEmbeddedCompositingSocketPermissions failed"); + return false; + } + + uint32_t DisplayConfigImpl::getWaylandSocketEmbeddedPermissions() const + { + return m_internalConfig.getWaylandSocketEmbeddedPermissions(); + } + + bool DisplayConfigImpl::setWaylandEmbeddedCompositingSocketName(std::string_view socketname) + { + m_internalConfig.setWaylandEmbeddedCompositingSocketName(socketname); + return true; + } + + std::string_view DisplayConfigImpl::getWaylandEmbeddedCompositingSocketName() const + { + return m_internalConfig.getWaylandSocketEmbedded(); + } + + bool DisplayConfigImpl::setWaylandEmbeddedCompositingSocketFD(int fd) + { + m_internalConfig.setWaylandEmbeddedCompositingSocketFD(fd); + return true; + } + + int DisplayConfigImpl::getWaylandSocketEmbeddedFD() const + { + return m_internalConfig.getWaylandSocketEmbeddedFD(); + } + + bool DisplayConfigImpl::setPlatformRenderNode(std::string_view renderNode) + { + m_internalConfig.setPlatformRenderNode(renderNode); + return true; + } + + std::string_view DisplayConfigImpl::getPlatformRenderNode() const + { + return m_internalConfig.getPlatformRenderNode(); + } + + bool DisplayConfigImpl::setSwapInterval(int32_t interval) + { + m_internalConfig.setSwapInterval(interval); + return true; + } + + int32_t DisplayConfigImpl::getSwapInterval() const + { + return m_internalConfig.getSwapInterval(); + } + + bool DisplayConfigImpl::setScenePriority(sceneId_t sceneId, int32_t priority) + { + m_internalConfig.setScenePriority(SceneId(sceneId.getValue()), priority); + return true; + } + + int32_t DisplayConfigImpl::getScenePriority(sceneId_t sceneId) const + { + return m_internalConfig.getScenePriority(SceneId(sceneId.getValue())); + } + + bool DisplayConfigImpl::setResourceUploadBatchSize(uint32_t batchSize) + { + if (batchSize == 0) + { + LOG_ERROR(CONTEXT_CLIENT, "DisplayConfig::setResourceUploadBatchSize failed - batchSize cannot be 0!"); + return false; + } + m_internalConfig.setResourceUploadBatchSize(batchSize); + return true; + } + + uint32_t DisplayConfigImpl::getResourceUploadBatchSize() const + { + return m_internalConfig.getResourceUploadBatchSize(); + } + + void DisplayConfigImpl::validate(ValidationReportImpl& report) const + { + const auto embeddedCompositorFilename = m_internalConfig.getWaylandSocketEmbedded(); + int embeddedCompositorFileDescriptor = m_internalConfig.getWaylandSocketEmbeddedFD(); + + if (!embeddedCompositorFilename.empty() && embeddedCompositorFileDescriptor >= 0) + { + report.add(EIssueType::Warning, "Competing settings for EmbeddedCompositor are set (file descriptor and file name). File descriptor setting will be preferred.", nullptr); + } + + if (m_internalConfig.getWindowType() != EWindowType::Windows && m_internalConfig.getDeviceType() != EDeviceType::GLES_3_0) + report.add(EIssueType::Error, "Selected window type supports only GL ES 3.0 device type", nullptr); + + if(m_internalConfig.getWindowsWindowHandle().isValid() && m_internalConfig.getWindowType() != EWindowType::Windows) + report.add(EIssueType::Error, "External Windows window handle is set and selected window type is not Windows", nullptr); + + if (m_internalConfig.getX11WindowHandle().isValid() && m_internalConfig.getWindowType() != EWindowType::X11) + report.add(EIssueType::Error, "External X11 window handle is set and selected window type is not X11", nullptr); + + if (m_internalConfig.getAndroidNativeWindow().isValid() && m_internalConfig.getWindowType() != EWindowType::Android) + report.add(EIssueType::Error, "External Android window handle is set and selected window type is not Android", nullptr); + + if (m_internalConfig.getIOSNativeWindow().isValid() && m_internalConfig.getWindowType() != EWindowType::iOS) + report.add(EIssueType::Error, "External iOS window handle is set and selected window type is not iOS", nullptr); + } +} diff --git a/src/renderer/impl/DisplayConfigImpl.h b/src/renderer/impl/DisplayConfigImpl.h new file mode 100644 index 000000000..90187005b --- /dev/null +++ b/src/renderer/impl/DisplayConfigImpl.h @@ -0,0 +1,92 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/Types.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "impl/DataTypesImpl.h" + +namespace CLI +{ + class App; +} + +namespace ramses::internal +{ + class ValidationReportImpl; + + class DisplayConfigImpl + { + public: + DisplayConfigImpl(); + + [[nodiscard]] bool setDeviceType(EDeviceType deviceType); + [[nodiscard]] EDeviceType getDeviceType() const; + [[nodiscard]] bool setWindowType(EWindowType windowType); + [[nodiscard]] EWindowType getWindowType() const; + [[nodiscard]] bool setWindowRectangle(int32_t x, int32_t y, uint32_t width, uint32_t height); + [[nodiscard]] bool getWindowRectangle(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const; + [[nodiscard]] bool setFullscreen(bool fullscreen); + [[nodiscard]] bool isFullscreen() const; + [[nodiscard]] bool setMultiSampling(uint32_t numSamples); + [[nodiscard]] bool getMultiSamplingSamples(uint32_t& numSamples) const; + [[nodiscard]] bool setWaylandIviSurfaceID(waylandIviSurfaceId_t waylandIviSurfaceID); + [[nodiscard]] waylandIviSurfaceId_t getWaylandIviSurfaceID() const; + [[nodiscard]] bool setWaylandIviLayerID(waylandIviLayerId_t waylandIviLayerID); + [[nodiscard]] waylandIviLayerId_t getWaylandIviLayerID() const; + [[nodiscard]] bool setWaylandDisplay(std::string_view waylandDisplay); + [[nodiscard]] std::string_view getWaylandDisplay() const; + [[nodiscard]] void* getAndroidNativeWindow() const; + [[nodiscard]] bool setAndroidNativeWindow(void * nativeWindowPtr); + [[nodiscard]] IOSNativeWindowPtr getIOSNativeWindow() const; + [[nodiscard]] bool setIOSNativeWindow(IOSNativeWindowPtr nativeWindowPtr); + [[nodiscard]] bool setWindowIviVisible(bool visible); + [[nodiscard]] bool setResizable(bool resizable); + [[nodiscard]] bool setGPUMemoryCacheSize(uint64_t size); + [[nodiscard]] bool setClearColor(const vec4f& color); + [[nodiscard]] bool setDepthStencilBufferType(EDepthBufferType depthBufferType); + [[nodiscard]] bool setX11WindowHandle(X11WindowHandle x11WindowHandle); + [[nodiscard]] X11WindowHandle getX11WindowHandle() const; + [[nodiscard]] bool setWindowsWindowHandle(void* hwnd); + [[nodiscard]] void* getWindowsWindowHandle() const; + [[nodiscard]] bool setAsyncEffectUploadEnabled(bool enabled); + + [[nodiscard]] bool setWaylandEmbeddedCompositingSocketGroup(std::string_view groupname); + [[nodiscard]] std::string_view getWaylandSocketEmbeddedGroup() const; + + [[nodiscard]] bool setWaylandEmbeddedCompositingSocketPermissions(uint32_t permissions); + [[nodiscard]] uint32_t getWaylandSocketEmbeddedPermissions() const; + + [[nodiscard]] bool setWaylandEmbeddedCompositingSocketName(std::string_view socketname); + [[nodiscard]] std::string_view getWaylandEmbeddedCompositingSocketName() const; + + [[nodiscard]] bool setWaylandEmbeddedCompositingSocketFD(int fd); + [[nodiscard]] int getWaylandSocketEmbeddedFD() const; + + [[nodiscard]] bool setPlatformRenderNode(std::string_view renderNode); + [[nodiscard]] std::string_view getPlatformRenderNode() const; + + [[nodiscard]] bool setSwapInterval(int32_t interval); + [[nodiscard]] int32_t getSwapInterval() const; + + [[nodiscard]] bool setScenePriority(sceneId_t sceneId, int32_t priority); + [[nodiscard]] int32_t getScenePriority(sceneId_t sceneId) const; + + [[nodiscard]] bool setResourceUploadBatchSize(uint32_t batchSize); + [[nodiscard]] uint32_t getResourceUploadBatchSize() const; + + void validate(ValidationReportImpl& report) const; + + //impl methods + [[nodiscard]] const ramses::internal::DisplayConfig& getInternalDisplayConfig() const; + + private: + ramses::internal::DisplayConfig m_internalConfig; + }; +} diff --git a/src/renderer/impl/RamsesRenderer.cpp b/src/renderer/impl/RamsesRenderer.cpp new file mode 100644 index 000000000..9d1ff87ee --- /dev/null +++ b/src/renderer/impl/RamsesRenderer.cpp @@ -0,0 +1,272 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/renderer/RamsesRenderer.h" +#include "impl/RamsesRendererImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/APILoggingMacros.h" + +namespace ramses +{ + RamsesRenderer::RamsesRenderer(std::unique_ptr impl) + : m_impl{ std::move(impl) } + { + } + + RamsesRenderer::~RamsesRenderer() = default; + + bool RamsesRenderer::doOneLoop() + { + const bool status = m_impl->doOneLoop(); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool RamsesRenderer::startThread() + { + const bool status = m_impl->startThread(); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool RamsesRenderer::stopThread() + { + const bool status = m_impl->stopThread(); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool RamsesRenderer::isThreadRunning() const + { + return m_impl->isThreadRunning(); + } + + bool RamsesRenderer::setFramerateLimit(displayId_t displayId, float fpsLimit) + { + const bool status = m_impl->setFramerateLimit(displayId, fpsLimit); + LOG_HL_RENDERER_API2(status, displayId, fpsLimit); + return status; + } + + float RamsesRenderer::getFramerateLimit(displayId_t displayId) const + { + return m_impl->getFramerateLimit(displayId); + } + + bool RamsesRenderer::setLoopMode(ELoopMode loopMode) + { + const bool status = m_impl->setLoopMode(loopMode); + LOG_HL_RENDERER_API1(status, loopMode); + return status; + } + + ELoopMode RamsesRenderer::getLoopMode() const + { + return m_impl->getLoopMode(); + } + + displayId_t RamsesRenderer::createDisplay(const DisplayConfig& config) + { + const displayId_t displayId = m_impl->createDisplay(config); + LOG_HL_RENDERER_API1(displayId, LOG_API_GENERIC_OBJECT_STRING(config)); + return displayId; + } + + bool RamsesRenderer::destroyDisplay(displayId_t displayId) + { + const bool status = m_impl->destroyDisplay(displayId); + LOG_HL_RENDERER_API1(status, displayId); + return status; + } + + displayBufferId_t RamsesRenderer::getDisplayFramebuffer(displayId_t displayId) const + { + return m_impl->getDisplayFramebuffer(displayId); + } + + RendererSceneControl* RamsesRenderer::getSceneControlAPI() + { + return m_impl->getSceneControlAPI(); + } + + bool RamsesRenderer::dispatchEvents(IRendererEventHandler& rendererEventHandler) + { + const bool status = m_impl->dispatchEvents(rendererEventHandler); + LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(rendererEventHandler)); + return status; + } + + bool RamsesRenderer::setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height) + { + const bool status = m_impl->setExternallyOwnedWindowSize(display, width, height); + LOG_HL_RENDERER_API3(status, display, width, height); + return status; + } + + displayBufferId_t RamsesRenderer::createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount, EDepthBufferType depthBufferType) + { + const displayBufferId_t bufferId = m_impl->createOffscreenBuffer(display, width, height, sampleCount, false, depthBufferType); + LOG_HL_RENDERER_API5(bufferId, display, width, height, sampleCount, depthBufferType); + return bufferId; + } + + bool RamsesRenderer::destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer) + { + const bool status = m_impl->destroyOffscreenBuffer(display, offscreenBuffer); + LOG_HL_RENDERER_API2(status, display, offscreenBuffer); + return status; + } + + externalBufferId_t RamsesRenderer::createExternalBuffer(displayId_t display) + { + const auto bufferId = m_impl->createExternalBuffer(display); + LOG_HL_RENDERER_API1(bufferId, display); + return bufferId; + } + + bool RamsesRenderer::destroyExternalBuffer(displayId_t display, externalBufferId_t externalBuffer) + { + const auto status = m_impl->destroyExternalBuffer(display, externalBuffer); + LOG_HL_RENDERER_API2(status, display, externalBuffer); + return status; + } + + streamBufferId_t RamsesRenderer::createStreamBuffer(displayId_t display, waylandIviSurfaceId_t surfaceId) + { + return m_impl->createStreamBuffer(display, surfaceId); + } + + bool RamsesRenderer::destroyStreamBuffer(displayId_t display, streamBufferId_t bufferId) + { + return m_impl->destroyStreamBuffer(display, bufferId); + } + + bool RamsesRenderer::setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, ClearFlags clearFlags) + { + const bool status = m_impl->setDisplayBufferClearFlags(display, displayBuffer, clearFlags); + LOG_HL_RENDERER_API3(status, display, displayBuffer, clearFlags); + return status; + } + + bool RamsesRenderer::setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color) + { + const bool status = m_impl->setDisplayBufferClearColor(display, displayBuffer, color); + LOG_HL_RENDERER_API6(status, display, displayBuffer, color.r, color.g, color.b, color.a); + return status; + } + + bool RamsesRenderer::readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + const bool status = m_impl->readPixels(displayId, displayBuffer, x, y, width, height); + LOG_HL_RENDERER_API6(status, displayId, displayBuffer, x, y, width, height); + return status; + } + + bool RamsesRenderer::flush() + { + const bool result = m_impl->flush(); + LOG_HL_RENDERER_API_NOARG(result); + return result; + } + + bool RamsesRenderer::setSurfaceVisibility(uint32_t surfaceId, bool visibility) + { + const bool status = m_impl->systemCompositorSetIviSurfaceVisibility(surfaceId, visibility); + LOG_HL_RENDERER_API2(status, surfaceId, visibility); + return status; + } + + bool RamsesRenderer::setSurfaceOpacity(uint32_t surfaceId, float opacity) + { + const bool status = m_impl->systemCompositorSetIviSurfaceOpacity(surfaceId, opacity); + LOG_HL_RENDERER_API2(status, surfaceId, opacity); + return status; + } + + bool RamsesRenderer::setSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height) + { + const bool status = m_impl->systemCompositorSetIviSurfaceRectangle(surfaceId, x, y, width, height); + LOG_HL_RENDERER_API5(status, surfaceId, x, y, width, height); + return status; + } + + bool RamsesRenderer::setLayerVisibility(uint32_t layerId, bool visibility) + { + const bool status = m_impl->systemCompositorSetIviLayerVisibility(layerId, visibility); + LOG_HL_RENDERER_API2(status, layerId, visibility); + return status; + } + + bool RamsesRenderer::takeSystemCompositorScreenshot(std::string_view fileName, int32_t screenIviId) + { + const bool status = m_impl->systemCompositorTakeScreenshot(fileName, screenIviId); + LOG_HL_RENDERER_API2(status, fileName, screenIviId); + return status; + } + + bool RamsesRenderer::logRendererInfo() + { + const bool status = m_impl->logRendererInfo(); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool RamsesRenderer::setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender) + { + const bool status = m_impl->setFrameTimerLimits(limitForSceneResourcesUpload, limitForClientResourcesUpload, limitForOffscreenBufferRender); + LOG_HL_RENDERER_API3(status, limitForSceneResourcesUpload, limitForClientResourcesUpload, limitForOffscreenBufferRender); + return status; + } + + bool RamsesRenderer::setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit) + { + const bool status = m_impl->setPendingFlushLimits(forceApplyFlushLimit, forceUnsubscribeSceneLimit); + LOG_HL_RENDERER_API2(status, forceApplyFlushLimit, forceUnsubscribeSceneLimit); + return status; + } + + displayBufferId_t RamsesRenderer::createInterruptibleOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, EDepthBufferType depthBufferType) + { + const auto bufferId = m_impl->createOffscreenBuffer(display, width, height, 0u, true, depthBufferType); + LOG_HL_RENDERER_API4(bufferId, display, width, height, depthBufferType); + return bufferId; + } + + displayBufferId_t RamsesRenderer::createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t usageFlags, uint64_t modifier) + { + ramses::internal::DmaBufferFourccFormat bufferFormat { bufferFourccFormat }; + ramses::internal::DmaBufferUsageFlags bufferUsage { usageFlags }; + ramses::internal::DmaBufferModifiers bufferModifer { modifier }; + const auto bufferId = m_impl->createDmaOffscreenBuffer(display, width, height, bufferFormat, bufferUsage, bufferModifer); + LOG_HL_RENDERER_API6(bufferId, display, width, height, bufferFourccFormat, usageFlags, modifier); + return bufferId; + } + + bool RamsesRenderer::getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const + { + return m_impl->getDmaOffscreenBufferFDAndStride(display, displayBufferId, fd, stride); + } + + bool RamsesRenderer::setSkippingOfUnmodifiedBuffers(bool enable) + { + const bool status = m_impl->setSkippingOfUnmodifiedBuffers(enable); + LOG_HL_RENDERER_API1(status, enable); + return status; + } + + internal::RamsesRendererImpl& RamsesRenderer::impl() + { + return *m_impl; + } + + const internal::RamsesRendererImpl& RamsesRenderer::impl() const + { + return *m_impl; + } +} diff --git a/src/renderer/impl/RamsesRendererImpl.cpp b/src/renderer/impl/RamsesRendererImpl.cpp new file mode 100644 index 000000000..ce4f7db74 --- /dev/null +++ b/src/renderer/impl/RamsesRendererImpl.cpp @@ -0,0 +1,717 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RamsesRendererImpl.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RendererConfigImpl.h" +#include "impl/DisplayConfigImpl.h" +#include "impl/RendererSceneControlImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/BinaryShaderCacheProxy.h" +#include "impl/FrameworkFactoryRegistry.h" +#include "impl/RendererFactory.h" +#include "impl/ErrorReporting.h" +#include "impl/ValidationReportImpl.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Platform/PlatformFactory.h" +#include "internal/RendererLib/PlatformInterface/ISystemCompositorController.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/Ramsh/Ramsh.h" +#include + +namespace ramses::internal +{ + static const bool rendererRegisterSuccess = RendererFactory::RegisterRendererFactory(); + + RamsesRendererImpl::RamsesRendererImpl(RamsesFrameworkImpl& framework, const ramses::RendererConfig& config) + : m_framework(framework) + , m_binaryShaderCache(config.impl().getBinaryShaderCache() ? new BinaryShaderCacheProxy(*(config.impl().getBinaryShaderCache())) : nullptr) + , m_rendererFrameworkLogic(framework.getScenegraphComponent(), m_rendererCommandBuffer, framework.getFrameworkLock()) + , m_threadWatchdog(framework.getThreadWatchdogConfig(), ERamsesThreadIdentifier::Renderer) + , m_displayDispatcher{ std::make_unique(std::make_unique(), config.impl().getInternalRendererConfig(), m_rendererFrameworkLogic, m_threadWatchdog) } + , m_systemCompositorEnabled(config.impl().getInternalRendererConfig().getSystemCompositorControlEnabled()) + , m_loopMode(ELoopMode::UpdateAndRender) + , m_rendererLoopThreadType(ERendererLoopThreadType_Undefined) + , m_periodicLogSupplier(framework.getPeriodicLogger(), m_rendererCommandBuffer) + { + assert(!framework.isConnected()); + + { //Add ramsh commands to ramsh, independent of whether it is enabled or not. + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + m_ramshCommands.push_back(std::make_shared(m_rendererCommandBuffer)); + for (const auto& cmd : m_ramshCommands) + m_framework.getRamsh().add(cmd); + LOG_DEBUG(CONTEXT_SMOKETEST, "Ramsh commands registered from RamsesRenderer"); + } + + LOG_TRACE(CONTEXT_PROFILING, "RamsesRenderer::RamsesRenderer finished initializing renderer"); + } + + RamsesRendererImpl::~RamsesRendererImpl() = default; + + bool RamsesRendererImpl::doOneLoop() + { + if (ERendererLoopThreadType_InRendererOwnThread == m_rendererLoopThreadType) + { + getErrorReporting().set("Can not call doOneLoop explicitly if renderer is (or was) running in its own thread!"); + return false; + } + + m_rendererLoopThreadType = ERendererLoopThreadType_UsingDoOneLoop; + m_displayDispatcher->dispatchCommands(m_rendererCommandBuffer); + m_displayDispatcher->doOneLoop(); + return true; + } + + bool RamsesRendererImpl::flush() + { + pushAndConsumeRendererCommands(m_pendingRendererCommands); + return true; + } + + displayId_t RamsesRendererImpl::createDisplay(const ramses::DisplayConfig& config) + { + ValidationReportImpl configReport; + config.impl().validate(configReport); + if (configReport.hasError()) + { + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::createDisplay: failed to create display, using invalid display configuration - use validate method on object!"); + return displayId_t::Invalid(); + } + + const displayId_t displayId = m_nextDisplayId; + m_nextDisplayId.getReference() = m_nextDisplayId.getValue() + 1; + // display's framebuffer is also counted as display buffer together with offscreen buffers + assert(m_displayFramebuffers.count(displayId) == 0); + m_displayFramebuffers.insert({ displayId, m_nextDisplayBufferId }); + m_nextDisplayBufferId.getReference() = m_nextDisplayBufferId.getValue() + 1; + + RendererCommand::CreateDisplay cmd{ DisplayHandle(displayId.getValue()), config.impl().getInternalDisplayConfig(), m_binaryShaderCache.get() }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return displayId; + } + + bool RamsesRendererImpl::destroyDisplay(displayId_t displayId) + { + RendererCommand::DestroyDisplay cmd{ DisplayHandle(displayId.getValue()) }; + m_pendingRendererCommands.push_back(std::move(cmd)); + m_displayFramebuffers.erase(displayId); + + return true; + } + + displayBufferId_t RamsesRendererImpl::getDisplayFramebuffer(displayId_t displayId) const + { + const auto it = m_displayFramebuffers.find(displayId); + if (it == m_displayFramebuffers.cend()) + { + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::getDisplayFramebuffer: there is no display with ID " << displayId); + return displayBufferId_t::Invalid(); + } + return it->second; + } + + const DisplayDispatcher& RamsesRendererImpl::getDisplayDispatcher() const + { + return *m_displayDispatcher; + } + + DisplayDispatcher& RamsesRendererImpl::getDisplayDispatcher() + { + return *m_displayDispatcher; + } + + RendererSceneControl* RamsesRendererImpl::getSceneControlAPI() + { + if (!m_sceneControlAPI) + { + LOG_INFO(CONTEXT_CLIENT, "RamsesRenderer: instantiating RendererSceneControl"); + auto m_impl = std::make_unique(*this); + m_sceneControlAPI = UniquePtrWithDeleter{ new RendererSceneControl{ std::move(m_impl) }, [](RendererSceneControl* api) { delete api; } }; + } + + return m_sceneControlAPI.get(); + } + + void RamsesRendererImpl::logConfirmationEcho(displayId_t display, const std::string& text) + { + m_pendingRendererCommands.push_back(RendererCommand::ConfirmationEcho{ DisplayHandle{ display.getValue() }, text }); + } + + const RendererCommands& RamsesRendererImpl::getPendingCommands() const + { + return m_pendingRendererCommands; + } + + displayBufferId_t RamsesRendererImpl::createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount, bool interruptible, EDepthBufferType depthBufferType) + { + if (width < 1u || height < 1u) + { + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::createOffscreenBuffer: failed to create offscreen buffer, resolution must be higher than 0x0!"); + return {}; + } + + const DisplayHandle displayHandle(display.getValue()); + const displayBufferId_t bufferId = m_nextDisplayBufferId; + const OffscreenBufferHandle bufferHandle(bufferId.getValue()); + m_nextDisplayBufferId.getReference() = m_nextDisplayBufferId.getValue() + 1; + + RendererCommand::CreateOffscreenBuffer cmd{displayHandle, bufferHandle, width, height, sampleCount, interruptible, depthBufferType}; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return bufferId; + } + + displayBufferId_t RamsesRendererImpl::createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers) + { + if(isThreaded()) + { + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::createDmaOffscreenBuffer: failed to create offscreen buffer, renderer must be used only with doOneLoop (not running the renderer thread)!"); + return {}; + } + + if (width < 1u || height < 1u) + { + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::createDmaOffscreenBuffer: failed to create offscreen buffer, resolution must be higher than 0x0!"); + return {}; + } + + const DisplayHandle displayHandle(display.getValue()); + const displayBufferId_t bufferId = m_nextDisplayBufferId; + const OffscreenBufferHandle bufferHandle(bufferId.getValue()); + m_nextDisplayBufferId.getReference() = m_nextDisplayBufferId.getValue() + 1; + + RendererCommand::CreateDmaOffscreenBuffer cmd{ displayHandle, bufferHandle, width, height, dmaBufferFourccFormat, dmaBufferUsageFlags, dmaBufferModifiers }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return bufferId; + } + + bool RamsesRendererImpl::destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer) + { + const DisplayHandle displayHandle(display.getValue()); + const OffscreenBufferHandle bufferHandle(offscreenBuffer.getValue()); + m_pendingRendererCommands.push_back(RendererCommand::DestroyOffscreenBuffer{ displayHandle, bufferHandle }); + + return true; + } + + bool RamsesRendererImpl::setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, ClearFlags clearFlags) + { + const auto it = m_displayFramebuffers.find(display); + if (it == m_displayFramebuffers.cend()) + { + getErrorReporting().set("RendererSceneControl::setDisplayBufferClearFlags failed: display does not exist."); + return false; + } + + OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; + // if buffer to clear is display's framebuffer pass invalid OB to internal renderer + if (displayBuffer == it->second) + bufferHandle = OffscreenBufferHandle::Invalid(); + + const DisplayHandle displayHandle{ display.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::SetClearFlags{ displayHandle, bufferHandle, clearFlags }); + + return true; + } + + bool RamsesRendererImpl::setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color) + { + const auto it = m_displayFramebuffers.find(display); + if (it == m_displayFramebuffers.cend()) + { + getErrorReporting().set("RendererSceneControl::setDisplayBufferClearColor failed: display does not exist."); + return false; + } + + OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; + // if buffer to clear is display's framebuffer pass invalid OB to internal renderer + if (displayBuffer == it->second) + bufferHandle = OffscreenBufferHandle::Invalid(); + + const DisplayHandle displayHandle{ display.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::SetClearColor{ displayHandle, bufferHandle, color }); + + return true; + } + + bool RamsesRendererImpl::getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const + { + const auto it = std::find_if(m_offscreenDmaBufferInfos.cbegin(), m_offscreenDmaBufferInfos.cend(), [&](const auto& dmaBufInfo){ return dmaBufInfo.display == display && dmaBufInfo.displayBuffer == displayBufferId;}); + + if (it == m_offscreenDmaBufferInfos.cend()) + { + getErrorReporting().set(::fmt::format("RamsesRenderer::getDmaOffscreenBufferFDAndStride: no DMA buffer created for buffer {} on display {}", displayBufferId, display)); + return false; + } + + fd = it->fd; + stride = it->stride; + + return true; + } + + streamBufferId_t RamsesRendererImpl::allocateStreamBuffer() + { + const streamBufferId_t bufferId = m_nextStreamBufferId; + m_nextStreamBufferId.getReference() = m_nextStreamBufferId.getValue() + 1; + return bufferId; + } + + streamBufferId_t RamsesRendererImpl::createStreamBuffer(displayId_t display, waylandIviSurfaceId_t source) + { + streamBufferId_t bufferId = allocateStreamBuffer(); + const DisplayHandle displayHandle{ display.getValue() }; + const StreamBufferHandle bufferHandle{ bufferId.getValue() }; + const WaylandIviSurfaceId sourceLL{ source.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::CreateStreamBuffer{ displayHandle, bufferHandle, sourceLL }); + + return bufferId; + } + + bool RamsesRendererImpl::destroyStreamBuffer(displayId_t display, streamBufferId_t streamBuffer) + { + const DisplayHandle displayHandle{ display.getValue() }; + const StreamBufferHandle bufferHandle{ streamBuffer.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::DestroyStreamBuffer{ displayHandle, bufferHandle }); + + return true; + } + + externalBufferId_t RamsesRendererImpl::createExternalBuffer(displayId_t display) + { + if (isThreaded()) + { + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::createExternalBuffer: can not create external buffers unless renderer is using doOneLoop"); + return {}; + } + + externalBufferId_t bufferId{ m_nextExternalBufferId }; + m_nextExternalBufferId.getReference() = m_nextExternalBufferId.getValue() + 1; + + const DisplayHandle displayHandle{ display.getValue() }; + const ExternalBufferHandle bufferHandle{ bufferId.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::CreateExternalBuffer{ displayHandle, bufferHandle }); + + return bufferId; + } + + bool RamsesRendererImpl::destroyExternalBuffer(displayId_t display, externalBufferId_t externalTexture) + { + const DisplayHandle displayHandle{ display.getValue() }; + const ExternalBufferHandle bufferHandle{ externalTexture.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::DestroyExternalBuffer{ displayHandle, bufferHandle }); + + return true; + } + + bool RamsesRendererImpl::readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + if (width == 0u || height == 0u) + { + getErrorReporting().set("RamsesRenderer::readPixels failed: width and height must be greater than Zero"); + return false; + } + const auto it = m_displayFramebuffers.find(displayId); + if (it == m_displayFramebuffers.cend()) + { + getErrorReporting().set("RamsesRenderer::readPixels failed: display does not exist."); + return false; + } + + OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; + // if buffer to read from is display's framebuffer pass invalid OB to internal renderer + if (displayBuffer == it->second) + bufferHandle = OffscreenBufferHandle::Invalid(); + + RendererCommand::ReadPixels cmd{ DisplayHandle{ displayId.getValue() }, bufferHandle, x, y, width, height, false, false, {} }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RamsesRendererImpl::systemCompositorSetIviSurfaceVisibility(uint32_t surfaceId, bool visibility) + { + if (!m_systemCompositorEnabled) + { + getErrorReporting().set("RamsesRenderer::setSurfaceVisibility failed: system compositor was not enabled when creating the renderer."); + return false; + } + + const WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); + m_pendingRendererCommands.push_back(RendererCommand::SCSetIviSurfaceVisibility{ waylandIviSurfaceId, visibility }); + return true; + } + + bool RamsesRendererImpl::systemCompositorSetIviSurfaceOpacity(uint32_t surfaceId, float opacity) + { + if (!m_systemCompositorEnabled) + { + getErrorReporting().set("RamsesRenderer::setSurfaceOpacity failed: system compositor was not enabled when creating the renderer."); + return false; + } + + const WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); + m_pendingRendererCommands.push_back(RendererCommand::SCSetIviSurfaceOpacity{ waylandIviSurfaceId, opacity }); + return true; + } + + bool RamsesRendererImpl::systemCompositorSetIviSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height) + { + if (!m_systemCompositorEnabled) + { + getErrorReporting().set("RamsesRenderer::setSurfaceRectangle failed: system compositor was not enabled when creating the renderer."); + return false; + } + + const WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); + m_pendingRendererCommands.push_back(RendererCommand::SCSetIviSurfaceDestRectangle{ waylandIviSurfaceId, x, y, width, height }); + return true; + } + + bool RamsesRendererImpl::systemCompositorSetIviLayerVisibility(uint32_t layerId, bool visibility) + { + if (!m_systemCompositorEnabled) + { + getErrorReporting().set("RamsesRenderer::setLayerVisibility failed: system compositor was not enabled when creating the renderer."); + return false; + } + + const WaylandIviLayerId waylandIviLayerId(layerId); + m_pendingRendererCommands.push_back(RendererCommand::SCSetIviLayerVisibility{ waylandIviLayerId, visibility }); + return true; + } + + bool RamsesRendererImpl::systemCompositorTakeScreenshot(std::string_view fileName, int32_t screenIviId) + { + if (!m_systemCompositorEnabled) + { + getErrorReporting().set("RamsesRenderer::takeSystemCompositorScreenshot failed: system compositor was not enabled when creating the renderer."); + return false; + } + + m_pendingRendererCommands.push_back(RendererCommand::SCScreenshot{ screenIviId, std::string{fileName} }); + return true; + } + + bool RamsesRendererImpl::systemCompositorAddIviSurfaceToIviLayer(uint32_t surfaceId, uint32_t layerId) + { + if (!m_systemCompositorEnabled) + { + getErrorReporting().set("RamsesRenderer::addSurfaceToLayer failed: system compositor was not enabled when creating the renderer."); + return false; + } + + const WaylandIviSurfaceId waylandIviSurfaceId(surfaceId); + const WaylandIviLayerId waylandIviLayerId(layerId); + m_pendingRendererCommands.push_back(RendererCommand::SCAddIviSurfaceToIviLayer{ waylandIviSurfaceId, waylandIviLayerId }); + return true; + } + + bool RamsesRendererImpl::dispatchEvents(IRendererEventHandler& rendererEventHandler) + { + m_tempRendererEvents.clear(); + m_displayDispatcher->dispatchRendererEvents(m_tempRendererEvents); + + for(const auto& event : m_tempRendererEvents) + { + switch (event.eventType) + { + case ERendererEventType::DisplayCreated: + rendererEventHandler.displayCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Ok); + break; + case ERendererEventType::DisplayCreateFailed: + rendererEventHandler.displayCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Failed); + break; + case ERendererEventType::DisplayDestroyed: + rendererEventHandler.displayDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Ok); + break; + case ERendererEventType::DisplayDestroyFailed: + rendererEventHandler.displayDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, ERendererEventResult::Failed); + break; + case ERendererEventType::ReadPixelsFromFramebuffer: + case ERendererEventType::ReadPixelsFromFramebufferFailed: + { + const auto& pixelData = event.pixelData; + const displayId_t displayId{ event.displayHandle.asMemoryHandle() }; + const OffscreenBufferHandle obHandle = event.offscreenBuffer; + const displayBufferId_t displayBuffer(obHandle.isValid() ? obHandle.asMemoryHandle() : getDisplayFramebuffer(displayId).getValue()); + const auto eventResult = (event.eventType == ERendererEventType::ReadPixelsFromFramebuffer ? ERendererEventResult::Ok : ERendererEventResult::Failed); + assert((event.eventType == ERendererEventType::ReadPixelsFromFramebuffer) ^ pixelData.empty()); + rendererEventHandler.framebufferPixelsRead(pixelData.data(), static_cast(pixelData.size()), displayId, displayBuffer, eventResult); + break; + } + case ERendererEventType::OffscreenBufferCreated: + { + const displayId_t display{ event.displayHandle.asMemoryHandle() }; + const displayBufferId_t displayBuffer{ event.offscreenBuffer.asMemoryHandle() }; + assert(std::find_if(m_offscreenDmaBufferInfos.cbegin(), m_offscreenDmaBufferInfos.cend(), [&](const auto& dmaBufInfo){ return dmaBufInfo.display == display && dmaBufInfo.displayBuffer == displayBuffer;}) == m_offscreenDmaBufferInfos.cend()); + m_offscreenDmaBufferInfos.push_back({ display, displayBuffer, event.dmaBufferFD, event.dmaBufferStride }); + + rendererEventHandler.offscreenBufferCreated(display, displayBuffer, ERendererEventResult::Ok); + break; + } + case ERendererEventType::OffscreenBufferCreateFailed: + rendererEventHandler.offscreenBufferCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, displayBufferId_t{ event.offscreenBuffer.asMemoryHandle() }, ERendererEventResult::Failed); + break; + case ERendererEventType::OffscreenBufferDestroyed: + { + const displayId_t display{ event.displayHandle.asMemoryHandle() }; + const displayBufferId_t displayBuffer{ event.offscreenBuffer.asMemoryHandle() }; + rendererEventHandler.offscreenBufferDestroyed(display, displayBuffer, ERendererEventResult::Ok); + + const auto it = std::find_if(m_offscreenDmaBufferInfos.cbegin(), m_offscreenDmaBufferInfos.cend(), [&](const auto& dmaBufInfo){ return dmaBufInfo.display == display && dmaBufInfo.displayBuffer == displayBuffer;}); + if(it != m_offscreenDmaBufferInfos.cend()) + m_offscreenDmaBufferInfos.erase(it); + break; + } + case ERendererEventType::OffscreenBufferDestroyFailed: + rendererEventHandler.offscreenBufferDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, displayBufferId_t{ event.offscreenBuffer.asMemoryHandle() }, ERendererEventResult::Failed); + break; + case ERendererEventType::ExternalBufferCreated: + rendererEventHandler.externalBufferCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, event.textureGlId, ERendererEventResult::Ok); + break; + case ERendererEventType::ExternalBufferCreateFailed: + rendererEventHandler.externalBufferCreated(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, 0u, ERendererEventResult::Failed); + break; + case ERendererEventType::ExternalBufferDestroyed: + rendererEventHandler.externalBufferDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, ERendererEventResult::Ok); + break; + case ERendererEventType::ExternalBufferDestroyFailed: + rendererEventHandler.externalBufferDestroyed(displayId_t{ event.displayHandle.asMemoryHandle() }, externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, ERendererEventResult::Failed); + break; + case ERendererEventType::WindowClosed: + rendererEventHandler.windowClosed(displayId_t{ event.displayHandle.asMemoryHandle() }); + break; + case ERendererEventType::WindowKeyEvent: + rendererEventHandler.keyEvent(displayId_t{ event.displayHandle.asMemoryHandle() }, + event.keyEvent.type, event.keyEvent.modifier, event.keyEvent.keyCode); + break; + case ERendererEventType::WindowMouseEvent: + rendererEventHandler.mouseEvent(displayId_t{ event.displayHandle.asMemoryHandle() }, + event.mouseEvent.type, + event.mouseEvent.pos.x, event.mouseEvent.pos.y); + break; + case ERendererEventType::WindowResizeEvent: + rendererEventHandler.windowResized(displayId_t{ event.displayHandle.asMemoryHandle() }, event.resizeEvent.width, event.resizeEvent.height); + break; + case ERendererEventType::WindowMoveEvent: + rendererEventHandler.windowMoved(displayId_t{ event.displayHandle.asMemoryHandle() }, event.moveEvent.posX, event.moveEvent.posY); + break; + case ERendererEventType::FrameTimingReport: + rendererEventHandler.renderThreadLoopTimings(displayId_t{ event.displayHandle.asMemoryHandle() }, event.frameTimings.maximumLoopTimeWithinPeriod, event.frameTimings.averageLoopTimeWithinPeriod); + break; + case ERendererEventType::Invalid: + case ERendererEventType::ScenePublished: + case ERendererEventType::SceneStateChanged: + case ERendererEventType::SceneUnpublished: + case ERendererEventType::SceneSubscribed: + case ERendererEventType::SceneSubscribeFailed: + case ERendererEventType::SceneUnsubscribed: + case ERendererEventType::SceneUnsubscribedIndirect: + case ERendererEventType::SceneUnsubscribeFailed: + case ERendererEventType::SceneMapped: + case ERendererEventType::SceneMapFailed: + case ERendererEventType::SceneUnmapped: + case ERendererEventType::SceneUnmappedIndirect: + case ERendererEventType::SceneUnmapFailed: + case ERendererEventType::SceneShown: + case ERendererEventType::SceneShowFailed: + case ERendererEventType::SceneHidden: + case ERendererEventType::SceneHiddenIndirect: + case ERendererEventType::SceneHideFailed: + case ERendererEventType::SceneFlushed: + case ERendererEventType::SceneExpirationMonitoringEnabled: + case ERendererEventType::SceneExpirationMonitoringDisabled: + case ERendererEventType::SceneExpired: + case ERendererEventType::SceneRecoveredFromExpiration: + case ERendererEventType::SceneDataLinked: + case ERendererEventType::SceneDataLinkFailed: + case ERendererEventType::SceneDataBufferLinked: + case ERendererEventType::SceneDataBufferLinkFailed: + case ERendererEventType::SceneDataUnlinked: + case ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange: + case ERendererEventType::SceneDataUnlinkFailed: + case ERendererEventType::SceneDataSlotProviderCreated: + case ERendererEventType::SceneDataSlotProviderDestroyed: + case ERendererEventType::SceneDataSlotConsumerCreated: + case ERendererEventType::SceneDataSlotConsumerDestroyed: + case ERendererEventType::StreamSurfaceAvailable: + case ERendererEventType::StreamSurfaceUnavailable: + case ERendererEventType::ObjectsPicked: + getErrorReporting().set("RamsesRenderer::dispatchEvents failed - unknown renderer event type!"); + assert(false); + return false; + } + } + + return true; + } + + bool RamsesRendererImpl::logRendererInfo() + { + m_pendingRendererCommands.push_back(RendererCommand::LogInfo{ ERendererLogTopic::All, true, {} }); + return true; + } + + bool RamsesRendererImpl::startThread() + { + if (m_rendererLoopThreadType == ERendererLoopThreadType_UsingDoOneLoop) + { + getErrorReporting().set("RamsesRenderer::startThread Can not call startThread if doOneLoop is called before!"); + return false; + } + + m_displayDispatcher->startDisplayThreadsUpdating(); + m_diplayThreadUpdating = true; + + // First time starting thread, create dispatching thread. + // Dispatching thread must be created after dispatcher startDisplayThreadsUpdating above which enables display threaded mode + // and any existing queued up commands will be processed in threaded mode. + if (m_rendererLoopThreadType == ERendererLoopThreadType_Undefined) + m_commandDispatchingThread = std::make_unique(*m_displayDispatcher, m_rendererCommandBuffer, m_threadWatchdog); + m_rendererLoopThreadType = ERendererLoopThreadType_InRendererOwnThread; + + return true; + } + + bool RamsesRendererImpl::stopThread() + { + if (ERendererLoopThreadType_InRendererOwnThread != m_rendererLoopThreadType) + { + getErrorReporting().set("RamsesRenderer::stopThread Can not call stopThread if startThread was not called before!"); + return false; + } + + m_displayDispatcher->stopDisplayThreadsUpdating(); + m_diplayThreadUpdating = false; + + return true; + } + + bool RamsesRendererImpl::isThreadRunning() const + { + return m_diplayThreadUpdating; + } + + bool RamsesRendererImpl::isThreaded() const + { + return m_rendererLoopThreadType == ERendererLoopThreadType_InRendererOwnThread; + } + + bool RamsesRendererImpl::setFramerateLimit(displayId_t displayId, float fpsLimit) + { + if (fpsLimit <= 0.0f) + { + getErrorReporting().set("RamsesRenderer::setFramerateLimit must specify a positive fpsLimit!"); + return false; + } + if (ERendererLoopThreadType_UsingDoOneLoop == m_rendererLoopThreadType) + { + getErrorReporting().set("RamsesRenderer::setFramerateLimit cannot call setFramerateLimit if doOneLoop is called before because it can only control framerate when using rendering thread!"); + return false; + } + + m_displayDispatcher->setMinFrameDuration(std::chrono::microseconds{ std::lround(1000000 / fpsLimit) }, DisplayHandle{ displayId.getValue() }); + + return true; + } + + float RamsesRendererImpl::getFramerateLimit(displayId_t displayId) const + { + const auto minFrameDuration = m_displayDispatcher->getMinFrameDuration(DisplayHandle{ displayId.getValue() }); + return 1000000.f / static_cast(minFrameDuration.count()); + } + + bool RamsesRendererImpl::setLoopMode(ELoopMode loopMode) + { + m_loopMode = loopMode; + m_displayDispatcher->setLoopMode(m_loopMode); + return true; + } + + ELoopMode RamsesRendererImpl::getLoopMode() const + { + return m_loopMode; + } + + bool RamsesRendererImpl::setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender) + { + m_pendingRendererCommands.push_back(RendererCommand::SetLimits_FrameBudgets{ limitForSceneResourcesUpload, limitForClientResourcesUpload, limitForOffscreenBufferRender }); + return true; + } + + bool RamsesRendererImpl::setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height) + { + const auto it = m_displayFramebuffers.find(display); + if (it == m_displayFramebuffers.cend()) + { + getErrorReporting().set("RamsesRenderer::setExternallyOwnedWindowSize failed: display does not exist."); + return false; + } + + m_pendingRendererCommands.push_back(RendererCommand::SetExterallyOwnedWindowSize{ DisplayHandle{ display.getValue() }, width, height }); + return true; + } + + bool RamsesRendererImpl::setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit) + { + m_pendingRendererCommands.push_back(RendererCommand::SetLimits_FlushesForceApply{ forceApplyFlushLimit }); + m_pendingRendererCommands.push_back(RendererCommand::SetLimits_FlushesForceUnsubscribe{ forceUnsubscribeSceneLimit }); + return true; + } + + bool RamsesRendererImpl::setSkippingOfUnmodifiedBuffers(bool enable) + { + m_pendingRendererCommands.push_back(RendererCommand::SetSkippingOfUnmodifiedBuffers{ enable }); + return true; + } + + void RamsesRendererImpl::pushAndConsumeRendererCommands(RendererCommands& cmds) + { + m_rendererCommandBuffer.addAndConsumeCommandsFrom(cmds); + } + + const RamsesRendererImpl::DisplayFrameBufferMap& RamsesRendererImpl::getDisplayFrameBuffers() const + { + return m_displayFramebuffers; + } + + ErrorReporting& RamsesRendererImpl::getErrorReporting() const + { + return m_framework.getErrorReporting(); + } +} diff --git a/src/renderer/impl/RamsesRendererImpl.h b/src/renderer/impl/RamsesRendererImpl.h new file mode 100644 index 000000000..b4f6d011b --- /dev/null +++ b/src/renderer/impl/RamsesRendererImpl.h @@ -0,0 +1,173 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/Types.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "impl/DataTypesImpl.h" +#include "impl/CommandDispatchingThread.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/DisplayDispatcher.h" +#include "internal/RendererLib/RendererPeriodicLogSupplier.h" +#include "internal/RendererLib/Enums/ELoopMode.h" +#include "internal/RendererLib/RendererFrameworkLogic.h" +#include "internal/Watchdog/ThreadWatchdog.h" +#include "internal/RendererLib/RamshCommands/Screenshot.h" +#include "internal/RendererLib/RamshCommands/LogRendererInfo.h" +#include "internal/RendererLib/RamshCommands/PrintStatistics.h" +#include "internal/RendererLib/RamshCommands/TriggerPickEvent.h" +#include "internal/RendererLib/RamshCommands/SetClearColor.h" +#include "internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.h" +#include "internal/RendererLib/RamshCommands/SetFrameTimeLimits.h" +#include "internal/RendererLib/RamshCommands/SetSceneState.h" +#include "internal/RendererLib/RamshCommands/LinkUnlink.h" +#include "internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h" +#include "internal/RendererLib/RamshCommands/AssignScene.h" +#include +#include +#include + +namespace ramses +{ + class RendererConfig; + class DisplayConfig; + class IRendererEventHandler; +} + +namespace ramses::internal +{ + class IBinaryShaderCache; + class RamsesFrameworkImpl; + class ErrorReporting; + + class RamsesRendererImpl + { + public: + RamsesRendererImpl(RamsesFrameworkImpl& framework, const ramses::RendererConfig& config); + ~RamsesRendererImpl(); + + bool doOneLoop(); + bool flush(); + + displayId_t createDisplay(const ramses::DisplayConfig& config); + bool destroyDisplay(displayId_t displayId); + displayBufferId_t getDisplayFramebuffer(displayId_t displayId) const; + const DisplayDispatcher& getDisplayDispatcher() const; + DisplayDispatcher& getDisplayDispatcher(); + + RendererSceneControl* getSceneControlAPI(); + + displayBufferId_t createOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, uint32_t sampleCount, bool interruptible, EDepthBufferType depthBufferType); + displayBufferId_t createDmaOffscreenBuffer(displayId_t display, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers); + bool destroyOffscreenBuffer(displayId_t display, displayBufferId_t offscreenBuffer); + bool setDisplayBufferClearFlags(displayId_t display, displayBufferId_t displayBuffer, ClearFlags clearFlags); + bool setDisplayBufferClearColor(displayId_t display, displayBufferId_t displayBuffer, const vec4f& color); + bool getDmaOffscreenBufferFDAndStride(displayId_t display, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; + + streamBufferId_t allocateStreamBuffer(); + streamBufferId_t createStreamBuffer(displayId_t display, waylandIviSurfaceId_t source); + bool destroyStreamBuffer(displayId_t display, streamBufferId_t streamBuffer); + + externalBufferId_t createExternalBuffer(displayId_t display); + bool destroyExternalBuffer(displayId_t display, externalBufferId_t externalTexture); + bool readPixels(displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height); + + bool systemCompositorSetIviSurfaceVisibility(uint32_t surfaceId, bool visibility); + bool systemCompositorSetIviSurfaceOpacity(uint32_t surfaceId, float opacity); + bool systemCompositorSetIviSurfaceRectangle(uint32_t surfaceId, int32_t x, int32_t y, int32_t width, int32_t height); + bool systemCompositorSetIviLayerVisibility(uint32_t layerId, bool visibility); + bool systemCompositorTakeScreenshot(std::string_view fileName, int32_t screenIviId); + bool systemCompositorAddIviSurfaceToIviLayer(uint32_t surfaceId, uint32_t layerId); + + bool dispatchEvents(IRendererEventHandler& rendererEventHandler); + + void logConfirmationEcho(displayId_t display, const std::string& text); + bool logRendererInfo(); + + bool startThread(); + bool stopThread(); + bool isThreadRunning() const; + bool isThreaded() const; + bool setFramerateLimit(displayId_t displayId, float fpsLimit); + float getFramerateLimit(displayId_t displayId) const; + bool setLoopMode(ELoopMode loopMode); + ELoopMode getLoopMode() const; + bool setFrameTimerLimits(uint64_t limitForSceneResourcesUpload, uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); + bool setExternallyOwnedWindowSize(displayId_t display, uint32_t width, uint32_t height); + + bool setPendingFlushLimits(uint32_t forceApplyFlushLimit, uint32_t forceUnsubscribeSceneLimit); + bool setSkippingOfUnmodifiedBuffers(bool enable); + + const RendererCommands& getPendingCommands() const; + void pushAndConsumeRendererCommands(RendererCommands& cmds); + + using DisplayFrameBufferMap = std::unordered_map; + const DisplayFrameBufferMap& getDisplayFrameBuffers() const; + + ErrorReporting& getErrorReporting() const; + + private: + RamsesFrameworkImpl& m_framework; + std::unique_ptr m_binaryShaderCache; + + RendererCommands m_pendingRendererCommands; + RendererCommandBuffer m_rendererCommandBuffer; + RendererFrameworkLogic m_rendererFrameworkLogic; + ThreadWatchdog m_threadWatchdog; + std::unique_ptr m_displayDispatcher; + + displayId_t m_nextDisplayId{0u}; + displayBufferId_t m_nextDisplayBufferId{0u}; + streamBufferId_t m_nextStreamBufferId{0u}; + externalBufferId_t m_nextExternalBufferId{0u}; + DisplayFrameBufferMap m_displayFramebuffers; + bool m_systemCompositorEnabled; + + struct OffscreenDmaBufferInfo + { + displayId_t display; + displayBufferId_t displayBuffer; + int fd; + uint32_t stride; + }; + std::vector m_offscreenDmaBufferInfos; + + ELoopMode m_loopMode; + std::unique_ptr m_commandDispatchingThread; + bool m_diplayThreadUpdating = false; + + enum ERendererLoopThreadType + { + ERendererLoopThreadType_Undefined = 0, + ERendererLoopThreadType_InRendererOwnThread, + ERendererLoopThreadType_UsingDoOneLoop + }; + ERendererLoopThreadType m_rendererLoopThreadType; + RendererPeriodicLogSupplier m_periodicLogSupplier; //must be destructed before the RendererCommandBuffer! + + // scene control APIs can only be destructed within their friend RamsesRendererImpl class, + // use custom deleter to achieve that with unique ptr + template using UniquePtrWithDeleter = std::unique_ptr>; + UniquePtrWithDeleter m_sceneControlAPI; + + // keep allocated containers which are used to swap internal data + RendererEventVector m_tempRendererEvents; + + std::vector < std::shared_ptr > m_ramshCommands; + }; +} diff --git a/src/renderer/impl/RendererConfig.cpp b/src/renderer/impl/RendererConfig.cpp new file mode 100644 index 000000000..51cd73c28 --- /dev/null +++ b/src/renderer/impl/RendererConfig.cpp @@ -0,0 +1,93 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +// API +#include "ramses/renderer/RendererConfig.h" + +// internal +#include "impl/RendererConfigImpl.h" +#include "impl/APILoggingMacros.h" +#include + +namespace ramses +{ + RendererConfig::RendererConfig() + : m_impl{ std::make_unique() } + { + } + + RendererConfig::~RendererConfig() = default; + + RendererConfig::RendererConfig(const RendererConfig& other) + : m_impl{ std::make_unique(*other.m_impl) } + { + } + + RendererConfig::RendererConfig(RendererConfig&& other) noexcept = default; + + RendererConfig& RendererConfig::operator=(const RendererConfig& other) + { + m_impl = std::make_unique(*other.m_impl); + return *this; + } + + RendererConfig& RendererConfig::operator=(RendererConfig&& other) noexcept = default; + + bool RendererConfig::setBinaryShaderCache(IBinaryShaderCache& cache) + { + const auto status = m_impl->setBinaryShaderCache(cache); + LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(cache)); + return status; + } + + bool RendererConfig::enableSystemCompositorControl() + { + const auto status = m_impl->enableSystemCompositorControl(); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool RendererConfig::setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec) + { + const auto status = m_impl->setFrameCallbackMaxPollTime(waitTimeInUsec); + LOG_HL_RENDERER_API1(status, waitTimeInUsec); + return status; + } + + bool RendererConfig::setSystemCompositorWaylandDisplay(std::string_view waylandDisplay) + { + return m_impl->setSystemCompositorWaylandDisplay(waylandDisplay); + } + + std::string_view RendererConfig::getSystemCompositorWaylandDisplay() const + { + return m_impl->getSystemCompositorWaylandDisplay(); + } + + bool RendererConfig::setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period) + { + const auto status = m_impl->setRenderThreadLoopTimingReportingPeriod(period); + LOG_HL_RENDERER_API1(status, period.count()); + return status; + } + + std::chrono::milliseconds RendererConfig::getRenderThreadLoopTimingReportingPeriod() const + { + return m_impl->getRenderThreadLoopTimingReportingPeriod(); + } + + internal::RendererConfigImpl& RendererConfig::impl() + { + return *m_impl; + } + + const internal::RendererConfigImpl& RendererConfig::impl() const + { + return *m_impl; + } +} diff --git a/renderer/ramses-renderer-impl/src/RendererConfigImpl.cpp b/src/renderer/impl/RendererConfigImpl.cpp similarity index 52% rename from renderer/ramses-renderer-impl/src/RendererConfigImpl.cpp rename to src/renderer/impl/RendererConfigImpl.cpp index 279e7b6fd..c64f2f684 100644 --- a/renderer/ramses-renderer-impl/src/RendererConfigImpl.cpp +++ b/src/renderer/impl/RendererConfigImpl.cpp @@ -6,39 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererConfigImpl.h" +#include "impl/RendererConfigImpl.h" -namespace ramses +namespace ramses::internal { - RendererConfigImpl::RendererConfigImpl() - : StatusObjectImpl() - , m_binaryShaderCache(nullptr) - , m_rendererResourceCache(nullptr) - { - } + RendererConfigImpl::RendererConfigImpl() = default; - status_t RendererConfigImpl::enableSystemCompositorControl() + bool RendererConfigImpl::enableSystemCompositorControl() { m_internalConfig.enableSystemCompositorControl(); - return StatusOK; + return true; } - status_t RendererConfigImpl::setBinaryShaderCache(IBinaryShaderCache& cache) + bool RendererConfigImpl::setBinaryShaderCache(IBinaryShaderCache& cache) { m_binaryShaderCache = &cache; - return StatusOK; - } - - status_t RendererConfigImpl::setRendererResourceCache(IRendererResourceCache& cache) - { - m_rendererResourceCache = &cache; - return StatusOK; + return true; } - status_t RendererConfigImpl::setSystemCompositorWaylandDisplay(std::string_view waylandDisplay) + bool RendererConfigImpl::setSystemCompositorWaylandDisplay(std::string_view waylandDisplay) { m_internalConfig.setWaylandDisplayForSystemCompositorController(waylandDisplay); - return StatusOK; + return true; } std::string_view RendererConfigImpl::getSystemCompositorWaylandDisplay() const @@ -46,26 +35,21 @@ namespace ramses return m_internalConfig.getWaylandDisplayForSystemCompositorController(); } - status_t RendererConfigImpl::setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec) + bool RendererConfigImpl::setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec) { m_internalConfig.setFrameCallbackMaxPollTime(std::chrono::microseconds{waitTimeInUsec}); - return StatusOK; + return true; } - IBinaryShaderCache* RendererConfigImpl::getBinaryShaderCache() const + ramses::IBinaryShaderCache* RendererConfigImpl::getBinaryShaderCache() const { return m_binaryShaderCache; } - IRendererResourceCache* RendererConfigImpl::getRendererResourceCache() const - { - return m_rendererResourceCache; - } - - status_t RendererConfigImpl::setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period) + bool RendererConfigImpl::setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period) { m_internalConfig.setRenderthreadLooptimingReportingPeriod(period); - return StatusOK; + return true; } std::chrono::milliseconds RendererConfigImpl::getRenderThreadLoopTimingReportingPeriod() const @@ -73,7 +57,7 @@ namespace ramses return m_internalConfig.getRenderThreadLoopTimingReportingPeriod(); } - const ramses_internal::RendererConfig& RendererConfigImpl::getInternalRendererConfig() const + const ramses::internal::RendererConfig& RendererConfigImpl::getInternalRendererConfig() const { return m_internalConfig; } diff --git a/src/renderer/impl/RendererConfigImpl.h b/src/renderer/impl/RendererConfigImpl.h new file mode 100644 index 000000000..7d5dff636 --- /dev/null +++ b/src/renderer/impl/RendererConfigImpl.h @@ -0,0 +1,45 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/RendererLib/RendererConfig.h" + +namespace ramses +{ + class IBinaryShaderCache; +} + +namespace ramses::internal +{ + class RendererConfigImpl + { + public: + RendererConfigImpl(); + + [[nodiscard]] bool enableSystemCompositorControl(); + + [[nodiscard]] bool setSystemCompositorWaylandDisplay(std::string_view waylandDisplay); + [[nodiscard]] std::string_view getSystemCompositorWaylandDisplay() const; + + [[nodiscard]] bool setFrameCallbackMaxPollTime(uint64_t waitTimeInUsec); + + [[nodiscard]] bool setBinaryShaderCache(IBinaryShaderCache& cache); + [[nodiscard]] ramses::IBinaryShaderCache* getBinaryShaderCache() const; + + [[nodiscard]] bool setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds period); + [[nodiscard]] std::chrono::milliseconds getRenderThreadLoopTimingReportingPeriod() const; + + //impl methods + [[nodiscard]] const ramses::internal::RendererConfig& getInternalRendererConfig() const; + + private: + ramses::internal::RendererConfig m_internalConfig; + IBinaryShaderCache* m_binaryShaderCache{nullptr}; + }; +} diff --git a/renderer/ramses-renderer-impl/include/RendererEventChainer.h b/src/renderer/impl/RendererEventChainer.h similarity index 97% rename from renderer/ramses-renderer-impl/include/RendererEventChainer.h rename to src/renderer/impl/RendererEventChainer.h index 71871e5dd..b6a0205f0 100644 --- a/renderer/ramses-renderer-impl/include/RendererEventChainer.h +++ b/src/renderer/impl/RendererEventChainer.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREREVENTCHAINER_H -#define RAMSES_RENDEREREVENTCHAINER_H +#pragma once -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" -namespace ramses +namespace ramses::internal { class RendererEventChainer final : public IRendererEventHandler { @@ -53,7 +52,7 @@ namespace ramses m_handler2.displayDestroyed(displayId, result); } - void keyEvent(displayId_t displayId, EKeyEvent eventType, uint32_t keyModifiers, EKeyCode keyCode) override + void keyEvent(displayId_t displayId, EKeyEvent eventType, KeyModifiers keyModifiers, EKeyCode keyCode) override { m_handler1.keyEvent(displayId, eventType, keyModifiers, keyCode); m_handler2.keyEvent(displayId, eventType, keyModifiers, keyCode); @@ -222,5 +221,3 @@ namespace ramses IRendererSceneControlEventHandler& m_handler2; }; } - -#endif diff --git a/renderer/ramses-renderer-impl/src/RendererFactory.cpp b/src/renderer/impl/RendererFactory.cpp similarity index 85% rename from renderer/ramses-renderer-impl/src/RendererFactory.cpp rename to src/renderer/impl/RendererFactory.cpp index fd7479c3c..6b904b0d3 100644 --- a/renderer/ramses-renderer-impl/src/RendererFactory.cpp +++ b/src/renderer/impl/RendererFactory.cpp @@ -7,12 +7,12 @@ // ------------------------------------------------------------------------- #include "RendererFactory.h" -#include "FrameworkFactoryRegistry.h" -#include "RamsesRendererImpl.h" +#include "impl/FrameworkFactoryRegistry.h" +#include "impl/RamsesRendererImpl.h" -namespace ramses +namespace ramses::internal { - RendererUniquePtr RendererFactory::createRenderer(RamsesFrameworkImpl& framework, const RendererConfig& config) const + RendererUniquePtr RendererFactory::createRenderer(RamsesFrameworkImpl& framework, const ramses::RendererConfig& config) const { auto impl = std::make_unique(framework, config); RendererUniquePtr renderer{ new RamsesRenderer{ std::move(impl) }, diff --git a/renderer/ramses-renderer-impl/include/RendererFactory.h b/src/renderer/impl/RendererFactory.h similarity index 73% rename from renderer/ramses-renderer-impl/include/RendererFactory.h rename to src/renderer/impl/RendererFactory.h index f6da8f752..69e4374d3 100644 --- a/renderer/ramses-renderer-impl/include/RendererFactory.h +++ b/src/renderer/impl/RendererFactory.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERFACTORY_H -#define RAMSES_RENDERERFACTORY_H +#pragma once -#include "ramses-renderer-api/RamsesRenderer.h" -#include "RamsesObjectFactoryInterfaces.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "impl/RamsesObjectFactoryInterfaces.h" -namespace ramses +namespace ramses::internal { class RendererFactory : public IRendererFactory { public: static bool RegisterRendererFactory(); - RendererUniquePtr createRenderer(RamsesFrameworkImpl& framework, const RendererConfig& config) const override; + RendererUniquePtr createRenderer(RamsesFrameworkImpl& framework, const ramses::RendererConfig& config) const override; }; } -#endif + diff --git a/renderer/ramses-renderer-impl/src/RendererMate.cpp b/src/renderer/impl/RendererMate.cpp similarity index 80% rename from renderer/ramses-renderer-impl/src/RendererMate.cpp rename to src/renderer/impl/RendererMate.cpp index 6209d23f4..55672ac32 100644 --- a/renderer/ramses-renderer-impl/src/RendererMate.cpp +++ b/src/renderer/impl/RendererMate.cpp @@ -6,30 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererMate.h" +#include "impl/RendererMate.h" #include "RendererEventChainer.h" #include "RendererMateRamshCommands.h" -#include "RamsesRendererImpl.h" -#include "RamsesFrameworkImpl.h" -#include "RamsesFrameworkTypesImpl.h" -#include "Ramsh/Ramsh.h" +#include "impl/RamsesRendererImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "internal/Ramsh/Ramsh.h" -namespace ramses +namespace ramses::internal { RendererMate::RendererMate(RamsesRendererImpl& renderer, RamsesFrameworkImpl& framework) : m_ramsesRenderer(renderer) , m_rendererSceneControl(*renderer.getSceneControlAPI()) - , m_exitCommand{ std::make_shared() } + , m_exitCommand{ std::make_shared() } { framework.getRamsh().add(m_exitCommand); - m_ramshCommands.push_back(std::make_shared(*this)); - m_ramshCommands.push_back(std::make_shared(*this)); - m_ramshCommands.push_back(std::make_shared(*this)); - m_ramshCommands.push_back(std::make_shared(*this)); - m_ramshCommands.push_back(std::make_shared(*this)); + m_ramshCommands.push_back(std::make_shared(*this)); + m_ramshCommands.push_back(std::make_shared(*this)); + m_ramshCommands.push_back(std::make_shared(*this)); + m_ramshCommands.push_back(std::make_shared(*this)); + m_ramshCommands.push_back(std::make_shared(*this)); for (auto& cmd : m_ramshCommands) framework.getRamsh().add(cmd); - LOG_DEBUG(ramses_internal::CONTEXT_SMOKETEST, "Ramsh commands registered from RendererMate"); + LOG_DEBUG(CONTEXT_SMOKETEST, "Ramsh commands registered from RendererMate"); } bool RendererMate::setSceneState(sceneId_t sceneId, RendererSceneState state, const std::string& confirmationText) @@ -42,22 +42,22 @@ namespace ramses m_scenesInfo[sceneId].renderedStateConfirmationText.clear(); } else - m_scenesInfo[sceneId].renderedStateConfirmationText = std::move(confirmationText); + m_scenesInfo[sceneId].renderedStateConfirmationText = confirmationText; - return m_rendererSceneControl.setSceneState(sceneId, state) == StatusOK; + return m_rendererSceneControl.setSceneState(sceneId, state); } bool RendererMate::setSceneMapping(sceneId_t sceneId, displayId_t displayId) { std::lock_guard guard(m_lock); m_scenesInfo[sceneId].displayMapped = displayId; - return m_rendererSceneControl.setSceneMapping(sceneId, displayId) == StatusOK; + return m_rendererSceneControl.setSceneMapping(sceneId, displayId); } bool RendererMate::setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) { std::lock_guard guard(m_lock); - return m_rendererSceneControl.setSceneDisplayBufferAssignment(sceneId, displayBuffer, sceneRenderOrder) == StatusOK; + return m_rendererSceneControl.setSceneDisplayBufferAssignment(sceneId, displayBuffer, sceneRenderOrder); } void RendererMate::processConfirmationEchoCommand(displayId_t display, const std::string& confirmationText) @@ -124,34 +124,31 @@ namespace ramses } } - void RendererMate::displayCreated(displayId_t displayId, ERendererEventResult result) + void RendererMate::displayCreated([[maybe_unused]] displayId_t displayId, ERendererEventResult result) { - UNUSED(displayId); if (result == ERendererEventResult::Failed) { m_isRunning = false; } } - void RendererMate::keyEvent(displayId_t displayId, EKeyEvent keyEvent, uint32_t keyModifiers, EKeyCode keyCode) + void RendererMate::keyEvent([[maybe_unused]] displayId_t displayId, EKeyEvent keyEvent, KeyModifiers keyModifiers, EKeyCode keyCode) { if (!m_keysHandling) return; - UNUSED(displayId); if (keyEvent != EKeyEvent::Pressed) return; - if (keyCode == EKeyCode_Escape && keyModifiers == EKeyModifier_NoModifier) + if (keyCode == EKeyCode_Escape && keyModifiers == EKeyModifier()) { m_isRunning = false; return; } }; - void RendererMate::windowClosed(displayId_t displayId) + void RendererMate::windowClosed([[maybe_unused]] displayId_t displayId) { - UNUSED(displayId); m_isRunning = false; } diff --git a/renderer/ramses-renderer-impl/include/RendererMate.h b/src/renderer/impl/RendererMate.h similarity index 88% rename from renderer/ramses-renderer-impl/include/RendererMate.h rename to src/renderer/impl/RendererMate.h index b21d37b7a..aaec5b857 100644 --- a/renderer/ramses-renderer-impl/include/RendererMate.h +++ b/src/renderer/impl/RendererMate.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERMATE_H -#define RAMSES_RENDERERMATE_H +#pragma once -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "Ramsh/RamshCommandExit.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/Ramsh/RamshCommandExit.h" #include #include @@ -20,10 +19,14 @@ #include namespace ramses +{ + class RendererSceneControl; +} + +namespace ramses::internal { class RamsesRendererImpl; class RamsesFrameworkImpl; - class RendererSceneControl; class RendererMate final : public RendererSceneControlEventHandlerEmpty, public RendererEventHandlerEmpty { @@ -50,7 +53,7 @@ namespace ramses // IRendererEventHandler void displayCreated(displayId_t displayId, ERendererEventResult result) override; - void keyEvent(displayId_t displayId, EKeyEvent keyEvent, uint32_t keyModifiers, EKeyCode keyCode) override; + void keyEvent(displayId_t displayId, EKeyEvent keyEvent, KeyModifiers keyModifiers, EKeyCode keyCode) override; void windowClosed(displayId_t displayId) override; struct SceneInfo @@ -65,8 +68,8 @@ namespace ramses std::unordered_map m_scenesInfo; - std::shared_ptr m_exitCommand; - std::vector> m_ramshCommands; + std::shared_ptr m_exitCommand; + std::vector> m_ramshCommands; bool m_isRunning = true; bool m_keysHandling = false; @@ -105,5 +108,3 @@ namespace ramses std::unordered_map m_oldState; }; } - -#endif diff --git a/renderer/ramses-renderer-impl/src/RendererMateRamshCommands.cpp b/src/renderer/impl/RendererMateRamshCommands.cpp similarity index 71% rename from renderer/ramses-renderer-impl/src/RendererMateRamshCommands.cpp rename to src/renderer/impl/RendererMateRamshCommands.cpp index b8c5875d5..964b68dfb 100644 --- a/renderer/ramses-renderer-impl/src/RendererMateRamshCommands.cpp +++ b/src/renderer/impl/RendererMateRamshCommands.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- #include "RendererMateRamshCommands.h" -#include "RendererMate.h" +#include "impl/RendererMate.h" -namespace ramses_internal +namespace ramses::internal { - RendererMateRamshCommand::RendererMateRamshCommand(ramses::RendererMate& rendererMate) + RendererMateRamshCommand::RendererMateRamshCommand(RendererMate& rendererMate) : m_rendererMate(rendererMate) { } - ShowSceneOnDisplay::ShowSceneOnDisplay(ramses::RendererMate& rendererMate) + ShowSceneOnDisplay::ShowSceneOnDisplay(RendererMate& rendererMate) : RendererMateRamshCommand(rendererMate) { description = "Subscribe to scene and map it on a display with translation offset (-sceneId # -displayId # -order # [-confirm ])"; @@ -25,14 +25,9 @@ namespace ramses_internal uint32_t parseIntArg(const std::vector& input, uint32_t idx) { - const auto argVal(input[idx]); - return atoi(argVal.c_str()); - } - - float parseFloatArg(const std::vector& input, uint32_t idx) - { - const auto argVal(input[idx]); - return static_cast(atof(argVal.c_str())); + uint32_t value = 0u; + ArgumentConverter::tryConvert(input[idx], value); + return value; } std::string parseStringArg(const std::vector& input, uint32_t idx) @@ -42,21 +37,21 @@ namespace ramses_internal bool ShowSceneOnDisplay::executeInput(const std::vector& input) { - ramses::sceneId_t sceneId(0xffff); - ramses::displayId_t displayId{ 0xffff }; + sceneId_t sceneId(0xffff); + displayId_t displayId{ 0xffff }; std::string confirmationText; int sceneRenderOrder = 0; bool sceneDefined = false; bool displayDefined = false; - const uint32_t numArgStrings = static_cast(input.size()); + const auto numArgStrings = static_cast(input.size()); for (uint32_t argStrIdx = 0u; argStrIdx < numArgStrings - 1; ++argStrIdx) { const std::string argStr(input[argStrIdx]); if (argStr == std::string("-sceneId")) { - sceneId = ramses::sceneId_t(parseIntArg(input, ++argStrIdx)); + sceneId = sceneId_t(parseIntArg(input, ++argStrIdx)); sceneDefined = true; } else if (argStr == std::string("-displayId")) @@ -66,7 +61,7 @@ namespace ramses_internal } else if (argStr == std::string("-order")) { - sceneRenderOrder = parseIntArg(input, ++argStrIdx); + sceneRenderOrder = static_cast(parseIntArg(input, ++argStrIdx)); } else if (argStr == std::string("-confirm")) { @@ -79,12 +74,12 @@ namespace ramses_internal m_rendererMate.setSceneMapping(sceneId, displayId); m_rendererMate.setSceneDisplayBufferAssignment(sceneId, {}, sceneRenderOrder); - m_rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, confirmationText); + m_rendererMate.setSceneState(sceneId, RendererSceneState::Rendered, confirmationText); return true; } - HideScene::HideScene(ramses::RendererMate& rendererMate) + HideScene::HideScene(RendererMate& rendererMate) : RendererMateRamshCommand(rendererMate) { description = "Hide a rendered scene"; @@ -97,11 +92,11 @@ namespace ramses_internal bool HideScene::execute(uint64_t& sceneId) const { - m_rendererMate.setSceneState(ramses::sceneId_t{ sceneId }, ramses::RendererSceneState::Ready); + m_rendererMate.setSceneState(sceneId_t{ sceneId }, RendererSceneState::Ready); return true; } - ReleaseScene::ReleaseScene(ramses::RendererMate& rendererMate) + ReleaseScene::ReleaseScene(RendererMate& rendererMate) : RendererMateRamshCommand(rendererMate) { description = "Release (Unmap and Unsubscribe) a scene from renderer"; @@ -114,11 +109,11 @@ namespace ramses_internal bool ReleaseScene::execute(uint64_t& sceneId) const { - m_rendererMate.setSceneState(ramses::sceneId_t{ sceneId }, ramses::RendererSceneState::Available); + m_rendererMate.setSceneState(sceneId_t{ sceneId }, RendererSceneState::Available); return true; } - LinkData::LinkData(ramses::RendererMate& rendererMate) + LinkData::LinkData(RendererMate& rendererMate) : RendererMateRamshCommand(rendererMate) { description = "Link scene data on renderer"; @@ -143,11 +138,11 @@ namespace ramses_internal bool LinkData::execute(uint64_t& providerSceneId, uint32_t& providerId, uint64_t& consumerSceneId, uint32_t& consumerId) const { - m_rendererMate.linkData(ramses::sceneId_t(providerSceneId), ramses::dataProviderId_t{ providerId }, ramses::sceneId_t(consumerSceneId), ramses::dataConsumerId_t{ consumerId }); + m_rendererMate.linkData(sceneId_t(providerSceneId), dataProviderId_t{ providerId }, sceneId_t(consumerSceneId), dataConsumerId_t{ consumerId }); return true; } - ConfirmationEcho::ConfirmationEcho(ramses::RendererMate& rendererMate) + ConfirmationEcho::ConfirmationEcho(RendererMate& rendererMate) : RendererMateRamshCommand(rendererMate) { description = "echos given text when command is executed (used for automated tests)"; @@ -159,7 +154,7 @@ namespace ramses_internal bool ConfirmationEcho::execute(uint32_t& displayId, std::string& text) const { - m_rendererMate.processConfirmationEchoCommand(ramses::displayId_t{ displayId }, text); + m_rendererMate.processConfirmationEchoCommand(displayId_t{ displayId }, text); return true; } } diff --git a/renderer/ramses-renderer-impl/include/RendererMateRamshCommands.h b/src/renderer/impl/RendererMateRamshCommands.h similarity index 71% rename from renderer/ramses-renderer-impl/include/RendererMateRamshCommands.h rename to src/renderer/impl/RendererMateRamshCommands.h index 0efaeaef7..172924a87 100644 --- a/renderer/ramses-renderer-impl/include/RendererMateRamshCommands.h +++ b/src/renderer/impl/RendererMateRamshCommands.h @@ -6,64 +6,58 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERMATERAMSHCOMMANDS_H -#define RAMSES_RENDERERMATERAMSHCOMMANDS_H +#pragma once -#include "Ramsh/RamshCommand.h" -#include "Ramsh/RamshCommandArguments.h" +#include "internal/Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommandArguments.h" #include -namespace ramses +namespace ramses::internal { class RendererMate; -} -namespace ramses_internal -{ class RendererMateRamshCommand { public: - explicit RendererMateRamshCommand(ramses::RendererMate& rendererMate); + explicit RendererMateRamshCommand(RendererMate& rendererMate); virtual ~RendererMateRamshCommand() = default; protected: - ramses::RendererMate& m_rendererMate; + RendererMate& m_rendererMate; }; class ShowSceneOnDisplay final : public RamshCommand, public RendererMateRamshCommand { public: - explicit ShowSceneOnDisplay(ramses::RendererMate& rendererMate); + explicit ShowSceneOnDisplay(RendererMate& rendererMate); bool executeInput(const std::vector& input) override; }; class HideScene final : public RamshCommandArgs < uint64_t >, public RendererMateRamshCommand { public: - explicit HideScene(ramses::RendererMate& rendererMate); + explicit HideScene(RendererMate& rendererMate); bool execute(uint64_t& sceneId) const override; }; class ReleaseScene final : public RamshCommandArgs < uint64_t >, public RendererMateRamshCommand { public: - explicit ReleaseScene(ramses::RendererMate& rendererMate); + explicit ReleaseScene(RendererMate& rendererMate); bool execute(uint64_t& sceneId) const override; }; class LinkData final : public RamshCommandArgs < uint64_t, uint32_t, uint64_t, uint32_t >, public RendererMateRamshCommand { public: - explicit LinkData(ramses::RendererMate& rendererMate); + explicit LinkData(RendererMate& rendererMate); bool execute(uint64_t& providerSceneId, uint32_t& providerId, uint64_t& consumerSceneId, uint32_t& consumerId) const override; }; class ConfirmationEcho final : public RamshCommandArgs, public RendererMateRamshCommand { public: - explicit ConfirmationEcho(ramses::RendererMate& rendererMate); + explicit ConfirmationEcho(RendererMate& rendererMate); bool execute(uint32_t& displayId, std::string& text) const override; }; } - -#endif diff --git a/src/renderer/impl/RendererSceneControl.cpp b/src/renderer/impl/RendererSceneControl.cpp new file mode 100644 index 000000000..62df9ccbf --- /dev/null +++ b/src/renderer/impl/RendererSceneControl.cpp @@ -0,0 +1,107 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/renderer/RendererSceneControl.h" +#include "impl/RendererSceneControlImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/APILoggingMacros.h" + +namespace ramses +{ + RendererSceneControl::RendererSceneControl(std::unique_ptr impl) + : m_impl{ std::move(impl) } + { + } + + RendererSceneControl::~RendererSceneControl() = default; + + bool RendererSceneControl::setSceneState(sceneId_t sceneId, RendererSceneState state) + { + const bool status = m_impl->setSceneState(sceneId, state); + LOG_HL_RENDERER_API2(status, sceneId, internal::EnumToString(state)); + return status; + } + + bool RendererSceneControl::setSceneMapping(sceneId_t sceneId, displayId_t displayId) + { + const bool status = m_impl->setSceneMapping(sceneId, displayId); + LOG_HL_RENDERER_API2(status, sceneId, displayId); + return status; + } + + bool RendererSceneControl::setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) + { + const bool status = m_impl->setSceneDisplayBufferAssignment(sceneId, displayBuffer, sceneRenderOrder); + LOG_HL_RENDERER_API3(status, sceneId, displayBuffer, sceneRenderOrder); + return status; + } + + bool RendererSceneControl::linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) + { + const bool status = m_impl->linkOffscreenBuffer(offscreenBufferId, consumerSceneId, consumerDataSlotId); + LOG_HL_RENDERER_API3(status, offscreenBufferId, consumerSceneId, consumerDataSlotId); + return status; + } + + bool RendererSceneControl::linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) + { + return m_impl->linkStreamBuffer(streamBufferId, consumerSceneId, consumerDataSlotId); + } + + bool RendererSceneControl::linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) + { + const bool status = m_impl->linkExternalBuffer(externalBufferId, consumerSceneId, consumerDataSlotId); + LOG_HL_RENDERER_API3(status, externalBufferId, consumerSceneId, consumerDataSlotId); + return status; + } + + bool RendererSceneControl::linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) + { + const bool status = m_impl->linkData(providerSceneId, providerId, consumerSceneId, consumerId); + LOG_HL_RENDERER_API4(status, providerSceneId, providerId, consumerSceneId, consumerId); + return status; + } + + bool RendererSceneControl::unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) + { + const bool status = m_impl->unlinkData(consumerSceneId, consumerId); + LOG_HL_RENDERER_API2(status, consumerSceneId, consumerId); + return status; + } + + bool RendererSceneControl::handlePickEvent(sceneId_t sceneId, float bufferNormalizedCoordX, float bufferNormalizedCoordY) + { + const bool status = m_impl->handlePickEvent(sceneId, bufferNormalizedCoordX, bufferNormalizedCoordY); + LOG_HL_RENDERER_API3(status, sceneId, bufferNormalizedCoordX, bufferNormalizedCoordY); + return status; + } + + bool RendererSceneControl::flush() + { + const bool status = m_impl->flush(); + LOG_HL_RENDERER_API_NOARG(status); + return status; + } + + bool RendererSceneControl::dispatchEvents(IRendererSceneControlEventHandler& eventHandler) + { + const bool status = m_impl->dispatchEvents(eventHandler); + LOG_HL_RENDERER_API1(status, LOG_API_GENERIC_OBJECT_STRING(eventHandler)); + return status; + } + + internal::RendererSceneControlImpl& RendererSceneControl::impl() + { + return *m_impl; + } + + const internal::RendererSceneControlImpl& RendererSceneControl::impl() const + { + return *m_impl; + } +} diff --git a/src/renderer/impl/RendererSceneControlImpl.cpp b/src/renderer/impl/RendererSceneControlImpl.cpp new file mode 100644 index 000000000..cc67907bb --- /dev/null +++ b/src/renderer/impl/RendererSceneControlImpl.cpp @@ -0,0 +1,273 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/RendererSceneControlImpl.h" +#include "impl/RamsesRendererImpl.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "impl/ErrorReporting.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "internal/RendererLib/Types.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal +{ + RendererSceneControlImpl::RendererSceneControlImpl(RamsesRendererImpl& renderer) + : m_renderer(renderer) + { + } + + RendererSceneControlImpl::~RendererSceneControlImpl() = default; + + bool RendererSceneControlImpl::setSceneState(sceneId_t sceneId, RendererSceneState state) + { + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControl::setSceneState: scene " << sceneId << " " << EnumToString(state)); + + if (state == RendererSceneState::Unavailable) + { + m_renderer.getErrorReporting().set("RendererSceneControl::setSceneState: Can not set scene state Unavailable. In order to release the scene from renderer set Available state."); + return false; + } + + auto& sceneInfo = m_sceneInfos[sceneId]; + if (state >= RendererSceneState::Ready && !sceneInfo.mappingSet) + { + m_renderer.getErrorReporting().set("RendererSceneControl::setSceneState: cannot get scene to ready/rendered without mapping info, set mapping info via RendererSceneControl::setSceneMapping first!"); + return false; + } + sceneInfo.targetState = state; + + RendererCommand::SetSceneState cmd{ SceneId{ sceneId.getValue() }, state }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::setSceneMapping(sceneId_t sceneId, displayId_t displayId) + { + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControl::setSceneMapping: scene " << sceneId << " display " << displayId); + + auto& sceneInfo = m_sceneInfos[sceneId]; + if (sceneInfo.currState >= RendererSceneState::Ready || sceneInfo.targetState >= RendererSceneState::Ready) + { + m_renderer.getErrorReporting().set("RendererSceneControl::setSceneMapping: cannot change mapping properties, scene's current or desired state already set to READY/RENDERED." + " Set scene state to AVAILABLE first, adjust mapping properties and then it can be made READY/RENDERED with new mapping properties."); + return false; + } + + sceneInfo.mappingSet = true; + + RendererCommand::SetSceneMapping cmd{ SceneId{ sceneId.getValue() }, DisplayHandle{ displayId.getValue() } }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) + { + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControl::setSceneDisplayBufferAssignment: scene " << sceneId << " displayBuffer " << displayBuffer << " renderOrder " << sceneRenderOrder); + + if (!m_sceneInfos[sceneId].mappingSet) + { + m_renderer.getErrorReporting().set("RendererSceneControl::setSceneDisplayBufferAssignment: scene does not have valid mapping information, set its mapping first."); + return false; + } + + OffscreenBufferHandle bufferHandle{ displayBuffer.getValue() }; + // if buffer to assign to is display's framebuffer pass invalid OB to internal renderer + const auto& frameBuffers = m_renderer.getDisplayFrameBuffers(); + if (std::any_of(frameBuffers.cbegin(), frameBuffers.cend(), [displayBuffer](const auto& d) { return d.second == displayBuffer; })) + bufferHandle = OffscreenBufferHandle::Invalid(); + + RendererCommand::SetSceneDisplayBufferAssignment cmd{ SceneId{ sceneId.getValue() }, bufferHandle, sceneRenderOrder }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) + { + const OffscreenBufferHandle providerBuffer{ offscreenBufferId.getValue() }; + const SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; + RendererCommand::LinkOffscreenBuffer cmd{ providerBuffer, internalConsumerSceneId, DataSlotId{ consumerDataSlotId.getValue() } }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) + { + const StreamBufferHandle providerBuffer{ streamBufferId.getValue() }; + const SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; + RendererCommand::LinkStreamBuffer cmd{ providerBuffer, internalConsumerSceneId, DataSlotId{ consumerDataSlotId.getValue() } }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) + { + const ExternalBufferHandle externalTexHandle{externalBufferId.getValue()}; + const SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; + RendererCommand::LinkExternalBuffer cmd{ externalTexHandle, internalConsumerSceneId, DataSlotId{ consumerDataSlotId.getValue() } }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) + { + if (providerSceneId == consumerSceneId) + { + m_renderer.getErrorReporting().set("RendererSceneControl::linkData failed: provider and consumer scene must not be identical"); + return false; + } + + const SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; + const SceneId internalProviderSceneId{ providerSceneId.getValue() }; + const DataSlotId providerSlot{ providerId.getValue() }; + const DataSlotId consumerSlot{ consumerId.getValue() }; + RendererCommand::LinkData cmd{ internalProviderSceneId, providerSlot, internalConsumerSceneId, consumerSlot }; + m_pendingRendererCommands.push_back(std::move(cmd)); + + return true; + } + + bool RendererSceneControlImpl::unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) + { + const SceneId internalConsumerSceneId{ consumerSceneId.getValue() }; + const DataSlotId consumerSlot{ consumerId.getValue() }; + m_pendingRendererCommands.push_back(RendererCommand::UnlinkData{ internalConsumerSceneId, consumerSlot }); + return true; + } + + bool RendererSceneControlImpl::handlePickEvent(sceneId_t scene, float bufferNormalizedCoordX, float bufferNormalizedCoordY) + { + const glm::vec2 coords(bufferNormalizedCoordX, bufferNormalizedCoordY); + const SceneId sceneId(scene.getValue()); + m_pendingRendererCommands.push_back(RendererCommand::PickEvent{ sceneId, coords }); + return true; + } + + bool RendererSceneControlImpl::flush() + { + m_renderer.pushAndConsumeRendererCommands(m_pendingRendererCommands); + return true; + } + + bool RendererSceneControlImpl::dispatchEvents(IRendererSceneControlEventHandler& eventHandler) + { + m_tempRendererEvents.clear(); + m_renderer.getDisplayDispatcher().dispatchSceneControlEvents(m_tempRendererEvents); + + for (const auto& event : m_tempRendererEvents) + { + switch (event.eventType) + { + case ERendererEventType::SceneStateChanged: + { + const sceneId_t sceneId{ event.sceneId.getValue() }; + const auto state = static_cast(event.state); + m_sceneInfos[sceneId].currState = state; + if (state == RendererSceneState::Unavailable) + { + // reset stored target state if scene became unavailable + m_sceneInfos[sceneId].targetState = RendererSceneState::Unavailable; + } + eventHandler.sceneStateChanged(sceneId, state); + break; + } + case ERendererEventType::SceneDataBufferLinked: + assert(event.offscreenBuffer.isValid()? (!event.streamBuffer.isValid() && !event.externalBuffer.isValid()) + : (event.streamBuffer.isValid() != event.externalBuffer.isValid())); + + if (event.offscreenBuffer.isValid()) + eventHandler.offscreenBufferLinked(displayBufferId_t{ event.offscreenBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, true); + if (event.streamBuffer.isValid()) + eventHandler.streamBufferLinked(streamBufferId_t(event.streamBuffer.asMemoryHandle()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), true); + if (event.externalBuffer.isValid()) + eventHandler.externalBufferLinked(externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, true); + + break; + case ERendererEventType::SceneDataBufferLinkFailed: + assert(event.offscreenBuffer.isValid() ? (!event.streamBuffer.isValid() && !event.externalBuffer.isValid()) + : (event.streamBuffer.isValid() != event.externalBuffer.isValid())); + + if (event.offscreenBuffer.isValid()) + eventHandler.offscreenBufferLinked(displayBufferId_t { event.offscreenBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, false); + if (event.streamBuffer.isValid()) + eventHandler.streamBufferLinked(streamBufferId_t(event.streamBuffer.asMemoryHandle()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), false); + if (event.externalBuffer.isValid()) + eventHandler.externalBufferLinked(externalBufferId_t{ event.externalBuffer.asMemoryHandle() }, sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }, false); + + break; + case ERendererEventType::SceneDataLinked: + eventHandler.dataLinked(sceneId_t(event.providerSceneId.getValue()), dataProviderId_t(event.providerdataId.getValue()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), true); + break; + case ERendererEventType::SceneDataLinkFailed: + eventHandler.dataLinked(sceneId_t(event.providerSceneId.getValue()), dataProviderId_t(event.providerdataId.getValue()), sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), false); + break; + case ERendererEventType::SceneDataUnlinked: + eventHandler.dataUnlinked(sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), true); + break; + case ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange: + // TODO vaclav remove this event, not useful + break; + case ERendererEventType::SceneDataUnlinkFailed: + eventHandler.dataUnlinked(sceneId_t(event.consumerSceneId.getValue()), dataConsumerId_t(event.consumerdataId.getValue()), false); + break; + case ERendererEventType::SceneDataSlotProviderCreated: + eventHandler.dataProviderCreated(sceneId_t{ event.providerSceneId.getValue() }, dataProviderId_t{ event.providerdataId.getValue() }); + break; + case ERendererEventType::SceneDataSlotProviderDestroyed: + eventHandler.dataProviderDestroyed(sceneId_t{ event.providerSceneId.getValue() }, dataProviderId_t{ event.providerdataId.getValue() }); + break; + case ERendererEventType::SceneDataSlotConsumerCreated: + eventHandler.dataConsumerCreated(sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }); + break; + case ERendererEventType::SceneDataSlotConsumerDestroyed: + eventHandler.dataConsumerDestroyed(sceneId_t{ event.consumerSceneId.getValue() }, dataConsumerId_t{ event.consumerdataId.getValue() }); + break; + case ERendererEventType::ObjectsPicked: + static_assert(sizeof(pickableObjectId_t) == sizeof(std::remove_pointer::type)); + eventHandler.objectsPicked(sceneId_t(event.sceneId.getValue()), reinterpret_cast(event.pickedObjectIds.data()), event.pickedObjectIds.size()); + break; + case ERendererEventType::SceneFlushed: + eventHandler.sceneFlushed(sceneId_t(event.sceneId.getValue()), event.sceneVersionTag.getValue()); + break; + case ERendererEventType::SceneExpirationMonitoringEnabled: + eventHandler.sceneExpirationMonitoringEnabled(sceneId_t(event.sceneId.getValue())); + break; + case ERendererEventType::SceneExpirationMonitoringDisabled: + eventHandler.sceneExpirationMonitoringDisabled(sceneId_t(event.sceneId.getValue())); + break; + case ERendererEventType::SceneExpired: + eventHandler.sceneExpired(sceneId_t(event.sceneId.getValue())); + break; + case ERendererEventType::SceneRecoveredFromExpiration: + eventHandler.sceneRecoveredFromExpiration(sceneId_t(event.sceneId.getValue())); + break; + case ERendererEventType::StreamSurfaceAvailable: + eventHandler.streamAvailabilityChanged(waylandIviSurfaceId_t(event.streamSourceId.getValue()), true); + break; + case ERendererEventType::StreamSurfaceUnavailable: + eventHandler.streamAvailabilityChanged(waylandIviSurfaceId_t(event.streamSourceId.getValue()), false); + break; + default: + assert(false); + break; + } + } + + return true; + } + + const RendererCommands& RendererSceneControlImpl::getPendingCommands() const + { + return m_pendingRendererCommands; + } +} diff --git a/src/renderer/impl/RendererSceneControlImpl.h b/src/renderer/impl/RendererSceneControlImpl.h new file mode 100644 index 000000000..cd5e157f2 --- /dev/null +++ b/src/renderer/impl/RendererSceneControlImpl.h @@ -0,0 +1,73 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/RendererSceneControl.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/RendererLib/RendererEvent.h" +#include + +namespace ramses::internal +{ + class RamsesRendererImpl; + + class IRendererSceneControl + { + public: + virtual bool setSceneState(sceneId_t sceneId, RendererSceneState state) = 0; + virtual bool setSceneMapping(sceneId_t sceneId, displayId_t displayId) = 0; + virtual bool setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) = 0; + virtual bool linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) = 0; + virtual bool linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) = 0; + virtual bool linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) = 0; + virtual bool unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) = 0; + virtual bool handlePickEvent(sceneId_t scene, float bufferNormalizedCoordX, float bufferNormalizedCoordY) = 0; + virtual bool flush() = 0; + virtual bool dispatchEvents(IRendererSceneControlEventHandler& eventHandler) = 0; + + virtual ~IRendererSceneControl() = default; + }; + + class RendererSceneControlImpl final : public IRendererSceneControl + { + public: + explicit RendererSceneControlImpl(RamsesRendererImpl& renderer); + ~RendererSceneControlImpl() override; + + bool setSceneState(sceneId_t sceneId, RendererSceneState state) override; + bool setSceneMapping(sceneId_t sceneId, displayId_t displayId) override; + bool setSceneDisplayBufferAssignment(sceneId_t sceneId, displayBufferId_t displayBuffer, int32_t sceneRenderOrder) override; + bool linkOffscreenBuffer(displayBufferId_t offscreenBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) override; + bool linkStreamBuffer(streamBufferId_t streamBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId) override; + virtual bool linkExternalBuffer(externalBufferId_t externalBufferId, sceneId_t consumerSceneId, dataConsumerId_t consumerDataSlotId); + bool linkData(sceneId_t providerSceneId, dataProviderId_t providerId, sceneId_t consumerSceneId, dataConsumerId_t consumerId) override; + bool unlinkData(sceneId_t consumerSceneId, dataConsumerId_t consumerId) override; + bool handlePickEvent(sceneId_t scene, float bufferNormalizedCoordX, float bufferNormalizedCoordY) override; + + bool flush() override; + bool dispatchEvents(IRendererSceneControlEventHandler& eventHandler) override; + + const RendererCommands& getPendingCommands() const; + + private: + RamsesRendererImpl& m_renderer; + RendererCommands m_pendingRendererCommands; + + struct SceneInfo + { + bool mappingSet = false; + RendererSceneState currState = RendererSceneState::Unavailable; + RendererSceneState targetState = RendererSceneState::Unavailable; + }; + std::unordered_map m_sceneInfos; + + // keep allocated containers which are used to swap internal data + RendererEventVector m_tempRendererEvents; + }; +} diff --git a/renderer/Platform/Platform_Android_EGL/src/Platform_Android_EGL.cpp b/src/renderer/internal/Platform/Android/Platform_Android_EGL.cpp similarity index 81% rename from renderer/Platform/Platform_Android_EGL/src/Platform_Android_EGL.cpp rename to src/renderer/internal/Platform/Android/Platform_Android_EGL.cpp index 9ec262570..2a2b27307 100644 --- a/renderer/Platform/Platform_Android_EGL/src/Platform_Android_EGL.cpp +++ b/src/renderer/internal/Platform/Android/Platform_Android_EGL.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Android_EGL/Platform_Android_EGL.h" -#include "RendererLib/RendererConfig.h" -#include "Context_EGL/Context_EGL.h" -#include "Platform_Base/EmbeddedCompositor_Dummy.h" +#include "internal/Platform/Android/Platform_Android_EGL.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/Platform/EGL/Context_EGL.h" +#include "internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h" -namespace ramses_internal +namespace ramses::internal { Platform_Android_EGL::Platform_Android_EGL(const RendererConfig& rendererConfig) : Platform_EGL(rendererConfig) diff --git a/renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL/Platform_Android_EGL.h b/src/renderer/internal/Platform/Android/Platform_Android_EGL.h similarity index 78% rename from renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL/Platform_Android_EGL.h rename to src/renderer/internal/Platform/Android/Platform_Android_EGL.h index a571a47bd..12ef30733 100644 --- a/renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL/Platform_Android_EGL.h +++ b/src/renderer/internal/Platform/Android/Platform_Android_EGL.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_ANDROID_EGL_H -#define RAMSES_PLATFORM_ANDROID_EGL_H +#pragma once -#include "Platform_Android_EGL/Window_Android.h" -#include "Platform_EGL/Platform_EGL.h" -#include "Context_EGL/Context_EGL.h" +#include "internal/Platform/Android/Window_Android.h" +#include "internal/Platform/EGL/Platform_EGL.h" +#include "internal/Platform/EGL/Context_EGL.h" -namespace ramses_internal +namespace ramses::internal { class Platform_Android_EGL : public Platform_EGL { @@ -26,5 +25,3 @@ namespace ramses_internal virtual uint32_t getSwapInterval() const override; }; } - -#endif diff --git a/renderer/Platform/Platform_Android_EGL/src/Window_Android.cpp b/src/renderer/internal/Platform/Android/Window_Android.cpp similarity index 80% rename from renderer/Platform/Platform_Android_EGL/src/Window_Android.cpp rename to src/renderer/internal/Platform/Android/Window_Android.cpp index d5884926c..726543734 100644 --- a/renderer/Platform/Platform_Android_EGL/src/Window_Android.cpp +++ b/src/renderer/internal/Platform/Android/Window_Android.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayConfig.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "Platform_Android_EGL/Window_Android.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" +#include "internal/Platform/Android/Window_Android.h" -namespace ramses_internal +namespace ramses::internal { Window_Android::Window_Android(const DisplayConfig& displayConfig, IWindowEventHandler &windowEventHandler, uint32_t id) : Window_Base(displayConfig, windowEventHandler, id) @@ -42,9 +42,8 @@ namespace ramses_internal return m_nativeWindow; } - bool Window_Android::setFullscreen(bool fullscreen) + bool Window_Android::setFullscreen([[maybe_unused]] bool fullscreen) { - UNUSED(fullscreen); return true; } diff --git a/renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL/Window_Android.h b/src/renderer/internal/Platform/Android/Window_Android.h similarity index 87% rename from renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL/Window_Android.h rename to src/renderer/internal/Platform/Android/Window_Android.h index fa15321c7..f28e72997 100644 --- a/renderer/Platform/Platform_Android_EGL/include/Platform_Android_EGL/Window_Android.h +++ b/src/renderer/internal/Platform/Android/Window_Android.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_ANDROID_H -#define RAMSES_WINDOW_ANDROID_H +#pragma once -#include "Platform_Base/Window_Base.h" -#include "RendererAPI/IWindowEventHandler.h" +#include "internal/RendererLib/PlatformBase/Window_Base.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" #include #include -namespace ramses_internal +namespace ramses::internal { class Window_Android : public Window_Base { @@ -41,5 +40,3 @@ namespace ramses_internal ANativeWindow* m_nativeWindow; }; } - -#endif diff --git a/src/renderer/internal/Platform/CMakeLists.txt b/src/renderer/internal/Platform/CMakeLists.txt new file mode 100644 index 000000000..a5d5a2c4b --- /dev/null +++ b/src/renderer/internal/Platform/CMakeLists.txt @@ -0,0 +1,107 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS) + list(APPEND PLATFORM_SOURCES Windows/*.h + Windows/*.cpp) + message("+ Windows Window") +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL OR ramses-sdk_ENABLE_WINDOW_TYPE_X11 OR ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID OR ramses-sdk_ENABLE_WINDOW_TYPE_IOS) + list(APPEND PLATFORM_SOURCES EGL/*.h + EGL/*.cpp) + list(APPEND PLATFORM_LIBS EGL) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) + list(APPEND PLATFORM_SOURCES X11/*.h + X11/*.cpp) + list(APPEND PLATFORM_LIBS X11) + message("+ X11 Window") +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID) + list(APPEND PLATFORM_SOURCES Android/*.h + Android/*.cpp) + list(APPEND PLATFORM_LIBS AndroidSDK) + message("+ Android Window") +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_IOS) + list(APPEND PLATFORM_SOURCES iOS/*.h + iOS/*.cpp + iOS/*.mm) + list(APPEND PLATFORM_LIBS QuartzCore) + message("+ iOS Window") +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) + list(APPEND PLATFORM_SOURCES Wayland/*.h + Wayland/*.cpp + Wayland/EmbeddedCompositor/*.h + Wayland/EmbeddedCompositor/*.cpp) + list(APPEND PLATFORM_LIBS wayland-zwp-linux-dmabuf-v1-extension + wayland-client + wayland-server + wayland-egl + LinuxInput) + + # Optional extension that's dependant on gbm and libdrm for support of DMA offscreen buffers + find_package(gbm) + find_package(libdrm) + if(gbm_FOUND AND libdrm_FOUND) + list(APPEND PLATFORM_SOURCES Device_EGL_Extension/*.h + Device_EGL_Extension/*.cpp) + list(APPEND PLATFORM_LIBS gbm libdrm) + set(DEVICE_EGL_EXTENSION_SUPPORTED true) + endif() +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) + list(APPEND PLATFORM_SOURCES Wayland/WlShell/*.h + Wayland/WlShell/*.cpp) + message("+ Wayland wl_shell Window") +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) + list(APPEND PLATFORM_SOURCES Wayland/IVI/*.h + Wayland/IVI/*.cpp + Wayland/IVI/SystemCompositorController/*.h + Wayland/IVI/SystemCompositorController/*.cpp) + list(APPEND PLATFORM_LIBS libdrm + wayland-ivi-extension) + message("+ Wayland ivi Window") +endif() + +createModule( + NAME Platform + TYPE STATIC_LIBRARY + ENABLE_INSTALL OFF + SRC_FILES *.h + *.cpp + OpenGL/*.h + OpenGL/*.cpp + ${PLATFORM_SOURCES} + DEPENDENCIES ramses-renderer-lib + OpenGL + ${PLATFORM_LIBS} + + # TODO move this to the OpenGL target where it belongs + # This tech debt is inherited from Device_GL + PUBLIC_DEFINES ${OpenGL_DEFINITIONS} +) + +# check for eglmesaext header and ensure it's included when available +find_file(ramses-sdk_HAS_EGLMESAEXT "EGL/eglmesaext.h") +if (ramses-sdk_HAS_EGLMESAEXT) + target_compile_definitions(Platform PUBLIC "RAMSES_HAS_EGLMESAEXT=1") +endif() + +if(${DEVICE_EGL_EXTENSION_SUPPORTED}) + target_compile_definitions(Platform PUBLIC DEVICE_EGL_EXTENSION_SUPPORTED) +endif() diff --git a/renderer/Platform/Device_EGL_Extension/src/Device_EGL_Extension.cpp b/src/renderer/internal/Platform/Device_EGL_Extension/Device_EGL_Extension.cpp similarity index 94% rename from renderer/Platform/Device_EGL_Extension/src/Device_EGL_Extension.cpp rename to src/renderer/internal/Platform/Device_EGL_Extension/Device_EGL_Extension.cpp index ddc860543..05b1eda2d 100644 --- a/renderer/Platform/Device_EGL_Extension/src/Device_EGL_Extension.cpp +++ b/src/renderer/internal/Platform/Device_EGL_Extension/Device_EGL_Extension.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_EGL_Extension/Device_EGL_Extension.h" -#include "Utils/LogMacros.h" +#include "internal/Platform/Device_EGL_Extension/Device_EGL_Extension.h" +#include "internal/Core/Utils/LogMacros.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { Device_EGL_Extension::Device_EGL_Extension(Context_EGL& context, std::string_view renderNode) : m_resourceMapper(context.getResources()) @@ -45,6 +45,7 @@ namespace ramses_internal return false; } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive m_drmRenderNodeFD = open(m_renderNode.c_str(), O_RDWR); if (m_drmRenderNodeFD < 0) { @@ -112,8 +113,8 @@ namespace ramses_internal if(modifiers.isValid() && modifiers.getValue() != DRM_FORMAT_MOD_INVALID) { const uint64_t bufferModifiers = modifiers.getValue(); - const EGLint bufferModifiersLowBytes = static_cast(bufferModifiers & 0xffffffff); - const EGLint bufferModifiersHighBytes = static_cast(bufferModifiers >> 32); + const auto bufferModifiersLowBytes = static_cast(bufferModifiers & 0xffffffff); + const auto bufferModifiersHighBytes = static_cast(bufferModifiers >> 32); eglImageCreationAttribs.push_back(EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT); eglImageCreationAttribs.push_back(bufferModifiersLowBytes); diff --git a/renderer/Platform/Device_EGL_Extension/include/Device_EGL_Extension/Device_EGL_Extension.h b/src/renderer/internal/Platform/Device_EGL_Extension/Device_EGL_Extension.h similarity index 90% rename from renderer/Platform/Device_EGL_Extension/include/Device_EGL_Extension/Device_EGL_Extension.h rename to src/renderer/internal/Platform/Device_EGL_Extension/Device_EGL_Extension.h index faa9fe3df..7e7cea9df 100644 --- a/renderer/Platform/Device_EGL_Extension/include/Device_EGL_Extension/Device_EGL_Extension.h +++ b/src/renderer/internal/Platform/Device_EGL_Extension/Device_EGL_Extension.h @@ -6,26 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICE_EGL_EXTENSION_H -#define RAMSES_DEVICE_EGL_EXTENSION_H +#pragma once -#include "RendererAPI/IDeviceExtension.h" -#include "Context_EGL/Context_EGL.h" -#include "Platform_Base/RenderBufferGPUResource.h" -#include "WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h" +#include "internal/RendererLib/PlatformInterface/IDeviceExtension.h" +#include "internal/Platform/EGL/Context_EGL.h" +#include "internal/RendererLib/PlatformBase/RenderBufferGPUResource.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" #include struct gbm_device; struct gbm_bo; -namespace ramses_internal +namespace ramses::internal { class DmaRenderBufferGpuResource: public RenderBufferGPUResource { public: DmaRenderBufferGpuResource(uint32_t gpuAddress, uint32_t width, uint32_t height, EGLImage eglImage, gbm_bo* gbmBufferObject, int fd, uint32_t stride) - : RenderBufferGPUResource(gpuAddress, width, height, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, 0u, ERenderBufferAccessMode_ReadWrite) + : RenderBufferGPUResource(gpuAddress, width, height, EPixelStorageFormat::RGBA8, 0u, ERenderBufferAccessMode::ReadWrite) , m_eglImage(eglImage) , m_gbmBufferObject(gbmBufferObject) , m_fd(fd) @@ -101,5 +100,3 @@ namespace ramses_internal gbm_device* m_gbmDevice = nullptr; }; } - -#endif diff --git a/renderer/Platform/Context_EGL/src/Context_EGL.cpp b/src/renderer/internal/Platform/EGL/Context_EGL.cpp similarity index 95% rename from renderer/Platform/Context_EGL/src/Context_EGL.cpp rename to src/renderer/internal/Platform/EGL/Context_EGL.cpp index 22e57ae5b..6b49d60e6 100644 --- a/renderer/Platform/Context_EGL/src/Context_EGL.cpp +++ b/src/renderer/internal/Platform/EGL/Context_EGL.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Context_EGL/Context_EGL.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/EGL/Context_EGL.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include namespace @@ -65,7 +65,7 @@ namespace } } -namespace ramses_internal +namespace ramses::internal { Context_EGL::Context_EGL(Generic_EGLNativeDisplayType eglDisplay, Generic_EGLNativeWindowType eglWindow, const EGLint* contextAttributes, const EGLint* surfaceAttributes, const EGLint* windowSurfaceAttributes, EGLint swapInterval, Context_EGL* sharedContext /*= 0*/) : m_nativeDisplay(eglDisplay) @@ -77,7 +77,7 @@ namespace ramses_internal { if(nullptr != sharedContext) { - const EGLContext contextHandleToShare = sharedContext->m_eglSurfaceData.eglContext; + EGLContext contextHandleToShare = sharedContext->m_eglSurfaceData.eglContext; LOG_DEBUG(CONTEXT_RENDERER, "Context_EGL::Context_EGL Sharing new context with existing context: " << contextHandleToShare); m_eglSurfaceData.eglSharedContext = contextHandleToShare; } @@ -94,7 +94,7 @@ namespace ramses_internal if(!queryEglExtensions()) return false; - if(!bindEglAPI()) + if(!BindEglAPI()) return false; // Due to a bug in the Android emulator, calling eglChooseConfig with nullptr as surface @@ -118,7 +118,6 @@ namespace ramses_internal eglSwapInterval(m_eglSurfaceData.eglDisplay, m_swapInterval); - LOG_INFO_P(CONTEXT_RENDERER, "Context_EGL::init(): EGL context creation succeeded (swap interval:{})", m_swapInterval); return true; } @@ -141,14 +140,14 @@ namespace ramses_internal } #else LOG_INFO(CONTEXT_RENDERER, "Context_EGL::~Context_EGL calling eglDestroySurface if !isSharedContext:" << isSharedContext); - if (!isSharedContext && !eglDestroySurface(m_eglSurfaceData.eglDisplay, m_eglSurfaceData.eglSurface)) + if (!isSharedContext && (eglDestroySurface(m_eglSurfaceData.eglDisplay, m_eglSurfaceData.eglSurface) == EGL_FALSE)) { LOG_ERROR(CONTEXT_RENDERER, "Context_EGL::destroy eglDestroySurface failed. Error code: " << eglGetError()); } #endif LOG_INFO(CONTEXT_RENDERER, "Context_EGL::~Context_EGL calling eglDestroyContext"); - if (!eglDestroyContext(m_eglSurfaceData.eglDisplay, m_eglSurfaceData.eglContext)) + if (eglDestroyContext(m_eglSurfaceData.eglDisplay, m_eglSurfaceData.eglContext) == EGL_FALSE) { LOG_ERROR(CONTEXT_RENDERER, "Context_EGL::destroy eglDestroyContext failed. Error code: " << eglGetError()); } @@ -162,7 +161,7 @@ namespace ramses_internal #endif LOG_DEBUG(CONTEXT_RENDERER, "Context_EGL::~Context_EGL calling eglTerminate"); - if (!isSharedContext && !eglTerminate(m_eglSurfaceData.eglDisplay)) + if (!isSharedContext && (eglTerminate(m_eglSurfaceData.eglDisplay) == EGL_FALSE)) { LOG_ERROR(CONTEXT_RENDERER, "Context_EGL::terminateEGLDisplayIfNotUsedAnymore eglTerminate() failed! Error code: " << eglGetError()); } @@ -252,9 +251,9 @@ namespace ramses_internal return true; #endif - EGLint iMajorVersion; - EGLint iMinorVersion; - if (!eglInitialize(m_eglSurfaceData.eglDisplay, &iMajorVersion, &iMinorVersion)) + EGLint iMajorVersion = 0; + EGLint iMinorVersion = 0; + if (eglInitialize(m_eglSurfaceData.eglDisplay, &iMajorVersion, &iMinorVersion) == EGL_FALSE) { LOG_ERROR(CONTEXT_RENDERER, "Context_EGL initialization failed at eglInitialize() with error code: " << eglGetError()); return false; @@ -280,9 +279,9 @@ namespace ramses_internal return true; } - bool Context_EGL::bindEglAPI() + bool Context_EGL::BindEglAPI() { - if (!eglBindAPI(EGL_OPENGL_ES_API)) + if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { LOG_ERROR(CONTEXT_RENDERER, "Context_EGL initialization failed at eglBindAPI() with error code: " << eglGetError()); return false; @@ -411,8 +410,8 @@ namespace ramses_internal if (m_eglSurfaceData.eglSharedContext) surfaceAttributes = surfaceAttribsForPBuffer; #endif - int iConfigs; - if (!eglChooseConfig(m_eglSurfaceData.eglDisplay, surfaceAttributes, &m_eglSurfaceData.eglConfig, 1, &iConfigs) || (iConfigs == 0)) + int iConfigs = 0; + if ((eglChooseConfig(m_eglSurfaceData.eglDisplay, surfaceAttributes, &m_eglSurfaceData.eglConfig, 1, &iConfigs) == EGL_FALSE) || (iConfigs == 0)) { if (0 == iConfigs) { diff --git a/renderer/Platform/Context_EGL/include/Context_EGL/Context_EGL.h b/src/renderer/internal/Platform/EGL/Context_EGL.h similarity index 83% rename from renderer/Platform/Context_EGL/include/Context_EGL/Context_EGL.h rename to src/renderer/internal/Platform/EGL/Context_EGL.h index 4febedd7e..0cd41c8b5 100644 --- a/renderer/Platform/Context_EGL/include/Context_EGL/Context_EGL.h +++ b/src/renderer/internal/Platform/EGL/Context_EGL.h @@ -6,35 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTEXT_EGL_H -#define RAMSES_CONTEXT_EGL_H +#pragma once #include #undef Status #undef None #undef Always +#undef Bool -#include "Platform_Base/Context_Base.h" +#include "internal/RendererLib/PlatformBase/Context_Base.h" -namespace ramses_internal +namespace ramses::internal { struct EglSurfaceData { - EglSurfaceData() - : eglDisplay(nullptr) - , eglConfig(nullptr) - , eglSurface(EGL_NO_SURFACE) - , eglContext(EGL_NO_CONTEXT) - , eglSharedContext(EGL_NO_CONTEXT) - { - } - - EGLDisplay eglDisplay; - EGLConfig eglConfig; - EGLSurface eglSurface; - EGLContext eglContext; - EGLContext eglSharedContext; + EGLDisplay eglDisplay{nullptr}; + EGLConfig eglConfig{nullptr}; + EGLSurface eglSurface{EGL_NO_SURFACE}; + EGLContext eglContext{EGL_NO_CONTEXT}; + EGLContext eglSharedContext{EGL_NO_CONTEXT}; }; class Context_EGL : public Context_Base @@ -46,7 +37,11 @@ namespace ramses_internal // format and we only treat it as an opque value. // Try to remove this workaround in the future by e.g. refactoring Context usage or making Context templated. // Additionally everone uses pointer types except rgl +#if defined(__APPLE__) + using Generic_EGLNativeDisplayType = int; +#else using Generic_EGLNativeDisplayType = void*; +#endif using Generic_EGLNativeWindowType = void*; Context_EGL(Generic_EGLNativeDisplayType eglDisplay, Generic_EGLNativeWindowType eglWindow, const EGLint* contextAttributes, const EGLint* surfaceAttributes, const EGLint* windowSurfaceAttributes, EGLint swapInterval, Context_EGL* sharedContext = nullptr); @@ -66,7 +61,7 @@ namespace ramses_internal bool getEglDisplayFromNativeHandle(); bool initializeEgl(); bool queryEglExtensions(); - bool bindEglAPI(); + static bool BindEglAPI(); void logAllFoundEglConfigs() const; void logErrorHints(const EGLint* surfaceAttributes) const; bool chooseEglConfig(); @@ -86,5 +81,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/Platform/Platform_EGL/include/Platform_EGL/Platform_EGL.h b/src/renderer/internal/Platform/EGL/Platform_EGL.h similarity index 70% rename from renderer/Platform/Platform_EGL/include/Platform_EGL/Platform_EGL.h rename to src/renderer/internal/Platform/EGL/Platform_EGL.h index fc10dcdd5..9cbdf4cfc 100644 --- a/renderer/Platform/Platform_EGL/include/Platform_EGL/Platform_EGL.h +++ b/src/renderer/internal/Platform/EGL/Platform_EGL.h @@ -6,23 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_EGL_H -#define RAMSES_PLATFORM_EGL_H +#pragma once -#include "Platform_Base/Platform_Base.h" -#include "RendererLib/RendererConfig.h" -#include "RendererLib/DisplayConfig.h" -#include "Context_EGL/Context_EGL.h" -#include "Device_GL/Device_GL.h" -#include "Utils/LogMacros.h" +#include "internal/RendererLib/PlatformBase/Platform_Base.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Platform/EGL/Context_EGL.h" +#include "internal/Platform/OpenGL/Device_GL.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include #ifdef DEVICE_EGL_EXTENSION_SUPPORTED -#include "Device_EGL_Extension/Device_EGL_Extension.h" +#include "internal/Platform/Device_EGL_Extension/Device_EGL_Extension.h" #endif #include -namespace ramses_internal +namespace ramses::internal { template class Platform_EGL : public Platform_Base @@ -35,14 +35,34 @@ namespace ramses_internal bool createContext(const DisplayConfig& displayConfig) override { - m_context = createContextInternal(displayConfig, nullptr); + if (m_glesMinorVersion.has_value()) + { + m_context = createContextInternal(displayConfig, nullptr, *m_glesMinorVersion); + } + else + { + const std::array minorVersions = {2, 1, 0}; + for (auto minor : minorVersions) + { + m_context = createContextInternal(displayConfig, nullptr, minor); + if (m_context) + { + m_glesMinorVersion = minor; + break; + } + LOG_ERROR_RP( + CONTEXT_RENDERER, "Context_EGL::init(): Failed to create GLES 3.{} context. Ramses will crash if any scene uses GLES 3.{} features.", minor, minor); + } + } + return m_context != nullptr; } bool createContextUploading() override { assert(m_context); - m_contextUploading = createContextInternal(DisplayConfig{}, static_cast(m_context.get())); + assert(m_glesMinorVersion.has_value()); + m_contextUploading = createContextInternal(DisplayConfig{}, static_cast(m_context.get()), *m_glesMinorVersion); return m_contextUploading != nullptr; } @@ -67,7 +87,7 @@ namespace ramses_internal return true; #ifdef DEVICE_EGL_EXTENSION_SUPPORTED - Context_EGL& context = static_cast(*m_context); + auto& context = static_cast(*m_context); auto deviceExtension = std::make_unique(context, platformRenderNode); if(deviceExtension->init()) m_deviceExtension = std::move(deviceExtension); @@ -82,7 +102,7 @@ namespace ramses_internal [[nodiscard]] virtual uint32_t getSwapInterval() const = 0; private: - std::unique_ptr createContextInternal(const DisplayConfig& displayConfig, Context_EGL* sharedContext) + std::unique_ptr createContextInternal(const DisplayConfig& displayConfig, Context_EGL* sharedContext, EGLint minorVersion) { if(displayConfig.getDeviceType() != EDeviceType::GLES_3_0) { @@ -91,14 +111,14 @@ namespace ramses_internal } assert(m_window); - WindowT* platformWindow = static_cast(m_window.get()); + auto* platformWindow = static_cast(m_window.get()); EGLint swapInterval = displayConfig.getSwapInterval(); if (swapInterval < 0) { swapInterval = getSwapInterval(); } - const std::vector contextAttributes = GetContextAttributes(); + const std::vector contextAttributes = GetContextAttributes(minorVersion); const std::vector surfaceAttributes = GetSurfaceAttributes(platformWindow->getMSAASampleCount(), displayConfig.getDepthStencilBufferType()); auto context = std::make_unique( @@ -111,7 +131,10 @@ namespace ramses_internal sharedContext); if (context->init()) + { + LOG_INFO_RP(CONTEXT_RENDERER, "Context_EGL::init(): EGL 3.{} context creation succeeded (swap interval:{})", minorVersion, swapInterval); return context; + } return {}; } @@ -125,38 +148,36 @@ namespace ramses_internal return {}; } - static std::vector GetContextAttributes() + static std::vector GetContextAttributes(EGLint minorVersion) { return { EGL_CONTEXT_CLIENT_VERSION, 3, - + EGL_CONTEXT_MINOR_VERSION, + minorVersion, EGL_NONE }; } - static std::vector GetSurfaceAttributes(uint32_t msaaSampleCount, ERenderBufferType depthStencilBufferType) + static std::vector GetSurfaceAttributes(uint32_t msaaSampleCount, EDepthBufferType depthStencilBufferType) { EGLint depthBufferSize = 0; EGLint stencilBufferSize = 0; switch(depthStencilBufferType) { - case ERenderBufferType_DepthStencilBuffer: + case EDepthBufferType::DepthStencil: depthBufferSize = 24; stencilBufferSize = 8; break; - case ERenderBufferType_DepthBuffer: + case EDepthBufferType::Depth: depthBufferSize = 24; stencilBufferSize = 0; break; - case ERenderBufferType_InvalidBuffer: + case EDepthBufferType::None: depthBufferSize = 0; stencilBufferSize = 0; break; - case ERenderBufferType_ColorBuffer: - case ERenderBufferType_NUMBER_OF_ELEMENTS: - assert(false); } return std::vector @@ -197,7 +218,7 @@ namespace ramses_internal EGL_NONE }; } + + std::optional m_glesMinorVersion; }; } - -#endif diff --git a/renderer/Platform/Device_GL/src/DebugOutput.cpp b/src/renderer/internal/Platform/OpenGL/DebugOutput.cpp similarity index 95% rename from renderer/Platform/Device_GL/src/DebugOutput.cpp rename to src/renderer/internal/Platform/OpenGL/DebugOutput.cpp index 4728142a3..3800c76af 100644 --- a/renderer/Platform/Device_GL/src/DebugOutput.cpp +++ b/src/renderer/internal/Platform/OpenGL/DebugOutput.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_GL/DebugOutput.h" +#include "internal/Platform/OpenGL/DebugOutput.h" -#include "RendererAPI/IContext.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include #include -namespace ramses_internal +namespace ramses::internal { -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) #define GL_DEBUG_TYPE_ERROR GL_DEBUG_TYPE_ERROR_KHR #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR @@ -79,7 +79,7 @@ namespace ramses_internal bool DebugOutput::loadExtensionFunctionPointer(const IContext& context) { -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) glDebugMessageCallback = reinterpret_cast(context.getProcAddress("glDebugMessageCallbackKHR")); glDebugMessageControl = diff --git a/renderer/Platform/Device_GL/include/Device_GL/DebugOutput.h b/src/renderer/internal/Platform/OpenGL/DebugOutput.h similarity index 84% rename from renderer/Platform/Device_GL/include/Device_GL/DebugOutput.h rename to src/renderer/internal/Platform/OpenGL/DebugOutput.h index ea2fa12c2..9ae7b091d 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/DebugOutput.h +++ b/src/renderer/internal/Platform/OpenGL/DebugOutput.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEBUGOUTPUT_H -#define RAMSES_DEBUGOUTPUT_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Device_GL/Device_GL_platform.h" +#include "internal/Platform/OpenGL/Device_GL_platform.h" -namespace ramses_internal +#include + +namespace ramses::internal { class IContext; @@ -26,7 +26,7 @@ namespace ramses_internal private: bool loadExtensionFunctionPointer(const IContext& context); -#if defined(__linux__) +#if defined(__linux__) || defined(__APPLE__) PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallback = nullptr; PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControl = nullptr; #else @@ -36,5 +36,3 @@ namespace ramses_internal mutable bool m_errorOccured = false; }; } - -#endif diff --git a/renderer/Platform/Device_GL/src/Device_GL.cpp b/src/renderer/internal/Platform/OpenGL/Device_GL.cpp similarity index 68% rename from renderer/Platform/Device_GL/src/Device_GL.cpp rename to src/renderer/internal/Platform/OpenGL/Device_GL.cpp index 6fae47032..5bd7a42b3 100644 --- a/renderer/Platform/Device_GL/src/Device_GL.cpp +++ b/src/renderer/internal/Platform/OpenGL/Device_GL.cpp @@ -6,31 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_GL/Device_GL.h" - -#include "Platform_Base/RenderTargetGpuResource.h" -#include "Platform_Base/RenderBufferGPUResource.h" -#include "Platform_Base/IndexBufferGPUResource.h" -#include "Platform_Base/VertexArrayGPUResource.h" - -#include "Device_GL/Device_GL_platform.h" -#include "Device_GL/ShaderGPUResource_GL.h" -#include "Device_GL/ShaderUploader_GL.h" -#include "Device_GL/ShaderProgramInfo.h" -#include "Device_GL/TypesConversion_GL.h" - -#include "SceneAPI/PixelRectangle.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IDeviceExtension.h" -#include "Resource/EffectResource.h" - -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/TextureMathUtils.h" -#include "PlatformAbstraction/PlatformStringUtils.h" -#include "PlatformAbstraction/Macros.h" +#include "internal/Platform/OpenGL/Device_GL.h" + +#include "internal/RendererLib/PlatformBase/RenderTargetGpuResource.h" +#include "internal/RendererLib/PlatformBase/RenderBufferGPUResource.h" +#include "internal/RendererLib/PlatformBase/IndexBufferGPUResource.h" +#include "internal/RendererLib/PlatformBase/VertexArrayGPUResource.h" + +#include "internal/Platform/OpenGL/Device_GL_platform.h" +#include "internal/Platform/OpenGL/ShaderGPUResource_GL.h" +#include "internal/Platform/OpenGL/ShaderUploader_GL.h" +#include "internal/Platform/OpenGL/ShaderProgramInfo.h" +#include "internal/Platform/OpenGL/TypesConversion_GL.h" + +#include "internal/SceneGraph/SceneAPI/PixelRectangle.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IDeviceExtension.h" +#include "internal/SceneGraph/Resource/EffectResource.h" + +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/TextureMathUtils.h" +#include "internal/PlatformAbstraction/PlatformStringUtils.h" +#include "internal/PlatformAbstraction/Macros.h" #include "glm/gtc/type_ptr.hpp" -namespace ramses_internal +#include "impl/TextureEnumsImpl.h" + +namespace ramses::internal { static constexpr GLboolean ToGLboolean(bool b) { @@ -41,9 +43,9 @@ namespace ramses_internal struct GLTextureInfo { GLenum target = GL_ZERO; - uint32_t width = 0u; - uint32_t height = 0u; - uint32_t depth = 0u; + GLsizei width = 0u; + GLsizei height = 0u; + GLsizei depth = 0u; TextureUploadParams_GL uploadParams; }; @@ -62,10 +64,8 @@ namespace ramses_internal Device_GL::Device_GL(IContext& context, IDeviceExtension* deviceExtension) : Device_Base(context) - , m_activeShader(nullptr) , m_activePrimitiveDrawMode(EDrawMode::Triangles) , m_activeIndexArrayElementSizeBytes(2u) - , m_debugOutput() , m_deviceExtension(deviceExtension) , m_emptyExternalTextureResource(m_resourceMapper.registerResource(std::make_unique(0u, 0u))) { @@ -73,35 +73,31 @@ namespace ramses_internal m_debugOutput.enable(context); #endif - m_limits.addTextureFormat(ETextureFormat::Depth16); - m_limits.addTextureFormat(ETextureFormat::Depth24); - m_limits.addTextureFormat(ETextureFormat::Depth24_Stencil8); - - m_limits.addTextureFormat(ETextureFormat::RGBA8); - m_limits.addTextureFormat(ETextureFormat::RGB8); - m_limits.addTextureFormat(ETextureFormat::RGBA5551); - m_limits.addTextureFormat(ETextureFormat::RGBA4); - m_limits.addTextureFormat(ETextureFormat::RGB565); - - m_limits.addTextureFormat(ETextureFormat::R8); - m_limits.addTextureFormat(ETextureFormat::R16); - m_limits.addTextureFormat(ETextureFormat::RG8); - m_limits.addTextureFormat(ETextureFormat::RG16); - m_limits.addTextureFormat(ETextureFormat::RGB16); - m_limits.addTextureFormat(ETextureFormat::RGBA16); - - m_limits.addTextureFormat(ETextureFormat::R16F); - m_limits.addTextureFormat(ETextureFormat::R32F); - m_limits.addTextureFormat(ETextureFormat::RG16F); - m_limits.addTextureFormat(ETextureFormat::RG32F); - m_limits.addTextureFormat(ETextureFormat::RGB16F); - m_limits.addTextureFormat(ETextureFormat::RGB32F); - m_limits.addTextureFormat(ETextureFormat::RGBA16F); - m_limits.addTextureFormat(ETextureFormat::RGBA32F); - - m_limits.addTextureFormat(ETextureFormat::SRGB8); - m_limits.addTextureFormat(ETextureFormat::SRGB8_ALPHA8); - m_limits.addTextureFormat(ETextureFormat::DXT3RGBA); + m_limits.addTextureFormat(EPixelStorageFormat::Depth16); + m_limits.addTextureFormat(EPixelStorageFormat::Depth24); + m_limits.addTextureFormat(EPixelStorageFormat::Depth32); + m_limits.addTextureFormat(EPixelStorageFormat::Depth24_Stencil8); + + m_limits.addTextureFormat(EPixelStorageFormat::RGBA8); + m_limits.addTextureFormat(EPixelStorageFormat::RGB8); + m_limits.addTextureFormat(EPixelStorageFormat::RGBA5551); + m_limits.addTextureFormat(EPixelStorageFormat::RGBA4); + m_limits.addTextureFormat(EPixelStorageFormat::RGB565); + + m_limits.addTextureFormat(EPixelStorageFormat::R8); + m_limits.addTextureFormat(EPixelStorageFormat::RG8); + + m_limits.addTextureFormat(EPixelStorageFormat::R16F); + m_limits.addTextureFormat(EPixelStorageFormat::R32F); + m_limits.addTextureFormat(EPixelStorageFormat::RG16F); + m_limits.addTextureFormat(EPixelStorageFormat::RG32F); + m_limits.addTextureFormat(EPixelStorageFormat::RGB16F); + m_limits.addTextureFormat(EPixelStorageFormat::RGB32F); + m_limits.addTextureFormat(EPixelStorageFormat::RGBA16F); + m_limits.addTextureFormat(EPixelStorageFormat::RGBA32F); + + m_limits.addTextureFormat(EPixelStorageFormat::SRGB8); + m_limits.addTextureFormat(EPixelStorageFormat::SRGB8_ALPHA8); } Device_GL::~Device_GL() @@ -165,10 +161,7 @@ namespace ramses_internal const GLenum drawModeGL = TypesConversion_GL::GetDrawMode(m_activePrimitiveDrawMode); const GLenum elementTypeGL = TypesConversion_GL::GetIndexElementType(m_activeIndexArrayElementSizeBytes); - if (instanceCount > 1u) - glDrawElementsInstanced(drawModeGL, elementCount, elementTypeGL, startOffsetAddress, static_cast(instanceCount)); - else - glDrawElements(drawModeGL, elementCount, elementTypeGL, startOffsetAddress); + glDrawElementsInstanced(drawModeGL, elementCount, elementTypeGL, startOffsetAddress, static_cast(instanceCount)); // For profiling/tests Device_Base::drawIndexedTriangles(startOffset, elementCount, instanceCount); @@ -177,33 +170,26 @@ namespace ramses_internal void Device_GL::drawTriangles(int32_t startOffset, int32_t elementCount, uint32_t instanceCount) { const GLenum drawModeGL = TypesConversion_GL::GetDrawMode(m_activePrimitiveDrawMode); - if (instanceCount > 1u) - { - glDrawArraysInstanced(drawModeGL, startOffset, elementCount, static_cast(instanceCount)); - } - else - { - glDrawArrays(drawModeGL, startOffset, elementCount); - } + glDrawArraysInstanced(drawModeGL, startOffset, elementCount, static_cast(instanceCount)); // For profiling/tests Device_Base::drawTriangles(startOffset, elementCount, instanceCount); } - void Device_GL::clear(uint32_t clearFlags) + void Device_GL::clear(ClearFlags clearFlags) { GLbitfield deviceClearFlags = 0; - if (clearFlags & EClearFlags_Color) + if (clearFlags.isSet(EClearFlag::Color)) { // NOLINTNEXTLINE(hicpp-signed-bitwise) deviceClearFlags |= GL_COLOR_BUFFER_BIT; } - if (clearFlags & EClearFlags_Depth) + if (clearFlags.isSet(EClearFlag::Depth)) { // NOLINTNEXTLINE(hicpp-signed-bitwise) deviceClearFlags |= GL_DEPTH_BUFFER_BIT; } - if (clearFlags & EClearFlags_Stencil) + if (clearFlags.isSet(EClearFlag::Stencil)) { // NOLINTNEXTLINE(hicpp-signed-bitwise) deviceClearFlags |= GL_STENCIL_BUFFER_BIT; @@ -349,22 +335,28 @@ namespace ramses_internal width, height, m_limits.getMaxViewportWidth(), m_limits.getMaxViewportHeight()); } - glViewport(x, y, std::min(width, m_limits.getMaxViewportWidth()), std::min(height, m_limits.getMaxViewportHeight())); + glViewport(x, y, static_cast(std::min(width, m_limits.getMaxViewportWidth())), static_cast(std::min(height, m_limits.getMaxViewportHeight()))); } - GLHandle Device_GL::createTexture(uint32_t width, uint32_t height, ETextureFormat storageFormat, uint32_t sampleCount) const + GLHandle Device_GL::createTexture(uint32_t width, uint32_t height, EPixelStorageFormat storageFormat, uint32_t sampleCount) const { LOG_DEBUG(CONTEXT_RENDERER, "Device_GL::createTexture: creating a new texture (texture render target)"); - const GLHandle texID = generateAndBindTexture((sampleCount) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D); + const GLHandle texID = GenerateAndBindTexture((sampleCount) != 0u ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D); GLTextureInfo texInfo; - fillGLInternalTextureInfo((sampleCount) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, width, height, 1u, storageFormat, {ETextureChannelColor::Red, ETextureChannelColor::Green, ETextureChannelColor::Blue, ETextureChannelColor::Alpha}, texInfo); - allocateTextureStorage(texInfo, 1u, sampleCount); + fillGLInternalTextureInfo((sampleCount) != 0u ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, + width, + height, + 1u, + storageFormat, + {ETextureChannelColor::Red, ETextureChannelColor::Green, ETextureChannelColor::Blue, ETextureChannelColor::Alpha}, + texInfo); + AllocateTextureStorage(texInfo, 1u, sampleCount); return texID; } - GLHandle Device_GL::createRenderBuffer(uint32_t width, uint32_t height, ETextureFormat format, uint32_t sampleCount) + GLHandle Device_GL::CreateRenderBuffer(uint32_t width, uint32_t height, EPixelStorageFormat format, uint32_t sampleCount) { LOG_TRACE(CONTEXT_RENDERER, "Creating a new render buffer"); @@ -375,176 +367,162 @@ namespace ramses_internal GLenum internalFormat(0); switch (format) { - case ETextureFormat::Depth24: + case EPixelStorageFormat::Depth16: + internalFormat = GL_DEPTH_COMPONENT16; + break; + case EPixelStorageFormat::Depth24: internalFormat = GL_DEPTH_COMPONENT24; break; - case ETextureFormat::Depth24_Stencil8: + case EPixelStorageFormat::Depth32: + internalFormat = GL_DEPTH_COMPONENT32F; + break; + case EPixelStorageFormat::Depth24_Stencil8: internalFormat = GL_DEPTH24_STENCIL8; break; - case ETextureFormat::RGBA8: + case EPixelStorageFormat::RGBA8: internalFormat = GL_RGBA8; break; default: assert(false && "Unknown render buffer format"); } - sampleCount = checkAndClampNumberOfSamples(internalFormat, sampleCount); + sampleCount = CheckAndClampNumberOfSamples(internalFormat, sampleCount); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, sampleCount, internalFormat, width, height); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, static_cast(sampleCount), internalFormat, static_cast(width), static_cast(height)); return renderbufferHandle; } - bool Device_GL::getUniformLocation(DataFieldHandle field, GLInputLocation& location) const + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const float* value) { - assert(nullptr != m_activeShader); - location = m_activeShader->getUniformLocation(field); - return location != GLInputLocationInvalid; + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform1fv(uniformLocation.getValue(), static_cast(count), value); + return uniformLocation.isValid(); } - bool Device_GL::getAttributeLocation(DataFieldHandle field, GLInputLocation& location) const + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) { - assert(nullptr != m_activeShader); - location = m_activeShader->getAttributeLocation(field); - return location != GLInputLocationInvalid; + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform2fv(uniformLocation.getValue(), static_cast(count), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const float* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform1fv(uniformLocation.getValue(), count, value); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform3fv(uniformLocation.getValue(), static_cast(count), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform2fv(uniformLocation.getValue(), count, glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform4fv(uniformLocation.getValue(), static_cast(count), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const bool* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) + // GL does not provide native bool on its API but it is a common practice to use integer for setting bool uniforms + m_containerForBoolValues.assign(count, 0); + for (uint32_t i = 0; i < count; ++i) { - assert(nullptr != value); - glUniform3fv(uniformLocation.getValue(), count, glm::value_ptr(value[0])); + if (value[i]) + m_containerForBoolValues[i] = 1; } - } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) - { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform4fv(uniformLocation.getValue(), count, glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform1iv(uniformLocation.getValue(), static_cast(count), m_containerForBoolValues.data()); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform1iv(uniformLocation.getValue(), count, value); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform1iv(uniformLocation.getValue(), static_cast(count), value); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform2iv(uniformLocation.getValue(), count, glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform2iv(uniformLocation.getValue(), static_cast(count), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform3iv(uniformLocation.getValue(), count, glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform3iv(uniformLocation.getValue(), static_cast(count), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniform4iv(uniformLocation.getValue(), count, glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniform4iv(uniformLocation.getValue(), static_cast(count), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniformMatrix2fv(uniformLocation.getValue(), count, ToGLboolean(false), glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniformMatrix2fv(uniformLocation.getValue(), static_cast(count), ToGLboolean(false), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniformMatrix3fv(uniformLocation.getValue(), count, ToGLboolean(false), glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniformMatrix3fv(uniformLocation.getValue(), static_cast(count), ToGLboolean(false), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - void Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) + bool Device_GL::setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) - { - assert(nullptr != value); - glUniformMatrix4fv(uniformLocation.getValue(), count, ToGLboolean(false), glm::value_ptr(value[0])); - } + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) + glUniformMatrix4fv(uniformLocation.getValue(), static_cast(count), ToGLboolean(false), glm::value_ptr(value[0])); + return uniformLocation.isValid(); } - DeviceResourceHandle Device_GL::allocateTexture2D(uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) + DeviceResourceHandle Device_GL::allocateTexture2D(uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) { - const GLHandle texID = generateAndBindTexture(GL_TEXTURE_2D); + const GLHandle texID = GenerateAndBindTexture(GL_TEXTURE_2D); GLTextureInfo texInfo; fillGLInternalTextureInfo(GL_TEXTURE_2D, width, height, 1u, textureFormat, swizzle, texInfo); - allocateTextureStorage(texInfo, mipLevelCount); + AllocateTextureStorage(texInfo, mipLevelCount); return m_resourceMapper.registerResource(std::make_unique(texInfo, texID, totalSizeInBytes)); } - DeviceResourceHandle Device_GL::allocateTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes) + DeviceResourceHandle Device_GL::allocateTexture3D(uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes) { - const GLHandle texID = generateAndBindTexture(GL_TEXTURE_3D); + const GLHandle texID = GenerateAndBindTexture(GL_TEXTURE_3D); GLTextureInfo texInfo; fillGLInternalTextureInfo(GL_TEXTURE_3D, width, height, depth, textureFormat, DefaultTextureSwizzleArray, texInfo); - allocateTextureStorage(texInfo, mipLevelCount); + AllocateTextureStorage(texInfo, mipLevelCount); return m_resourceMapper.registerResource(std::make_unique(texInfo, texID, totalSizeInBytes)); } - DeviceResourceHandle Device_GL::allocateTextureCube(uint32_t faceSize, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) + DeviceResourceHandle Device_GL::allocateTextureCube(uint32_t faceSize, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) { - const GLHandle texID = generateAndBindTexture(GL_TEXTURE_CUBE_MAP); + const GLHandle texID = GenerateAndBindTexture(GL_TEXTURE_CUBE_MAP); GLTextureInfo texInfo; fillGLInternalTextureInfo(GL_TEXTURE_CUBE_MAP, faceSize, faceSize, 1u, textureFormat, swizzle, texInfo); - allocateTextureStorage(texInfo, mipLevelCount); + AllocateTextureStorage(texInfo, mipLevelCount); return m_resourceMapper.registerResource(std::make_unique(texInfo, texID, totalSizeInBytes)); } @@ -554,9 +532,9 @@ namespace ramses_internal if (m_limits.isExternalTextureExtensionSupported()) { const auto textureTarget = GL_TEXTURE_EXTERNAL_OES; - const GLHandle texID = generateAndBindTexture(textureTarget); + const GLHandle texID = GenerateAndBindTexture(textureTarget); GLTextureInfo texInfo; - fillGLInternalTextureInfo(textureTarget, 0u, 0u, 1u, ETextureFormat::RGBA8, {}, texInfo); + fillGLInternalTextureInfo(textureTarget, 0u, 0u, 1u, EPixelStorageFormat::RGBA8, {}, texInfo); return m_resourceMapper.registerResource(std::make_unique(texInfo, texID, 0u)); } @@ -571,19 +549,19 @@ namespace ramses_internal void Device_GL::bindTexture(DeviceResourceHandle handle) { - const TextureGPUResource_GL& gpuResource = m_resourceMapper.getResourceAs(handle); + const auto& gpuResource = m_resourceMapper.getResourceAs(handle); glBindTexture(gpuResource.m_textureInfo.target, gpuResource.getGPUAddress()); } void Device_GL::generateMipmaps(DeviceResourceHandle handle) { - const TextureGPUResource_GL& gpuResource = m_resourceMapper.getResourceAs(handle); + const auto& gpuResource = m_resourceMapper.getResourceAs(handle); glGenerateMipmap(gpuResource.m_textureInfo.target); } - void Device_GL::uploadTextureData(DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const Byte* data, uint32_t dataSize) + void Device_GL::uploadTextureData(DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const std::byte* data, uint32_t dataSize, uint32_t stride) { - const TextureGPUResource_GL& gpuResource = m_resourceMapper.getResourceAs(handle); + const auto& gpuResource = m_resourceMapper.getResourceAs(handle); GLTextureInfo texInfo = gpuResource.m_textureInfo; // in case of cube texture faceID is encoded in Z offset @@ -592,10 +570,10 @@ namespace ramses_internal texInfo.target = TypesConversion_GL::GetCubemapFaceSpecifier(static_cast(z)); z = 0u; } - uploadTextureMipMapData(mipLevel, x, y, z, width, height, depth, texInfo, data, dataSize); + UploadTextureMipMapData(mipLevel, x, y, z, width, height, depth, texInfo, data, dataSize, stride); } - DeviceResourceHandle Device_GL::uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) + DeviceResourceHandle Device_GL::uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) { if (!handle.isValid()) { @@ -606,38 +584,35 @@ namespace ramses_internal return m_resourceMapper.registerResource(std::make_unique(texID, 0u)); } - else - { - // upload data to registered texture resource - const GLHandle texID = getTextureAddress(handle); - assert(texID != InvalidGLHandle); - assert(data != nullptr); - LOG_DEBUG(CONTEXT_RENDERER, "Device_GL::uploadStreamTexture2D: texid: " << texID << " width: " << width << " height: " << height << " format: " << EnumToString(format) << " textureSwizzle: " << EnumToString(swizzle[0]) << "," << EnumToString(swizzle[1]) << "," << EnumToString(swizzle[2]) << "," << EnumToString(swizzle[3])); + // upload data to registered texture resource + const GLHandle texID = getTextureAddress(handle); + assert(texID != InvalidGLHandle); + assert(data != nullptr); + LOG_DEBUG(CONTEXT_RENDERER, "Device_GL::uploadStreamTexture2D: texid: " << texID << " width: " << width << " height: " << height << " format: " << EnumToString(format) << " textureSwizzle: " << EnumToString(swizzle[0]) << "," << EnumToString(swizzle[1]) << "," << EnumToString(swizzle[2]) << "," << EnumToString(swizzle[3])); - glBindTexture(GL_TEXTURE_2D, texID); + glBindTexture(GL_TEXTURE_2D, texID); - GLTextureInfo texInfo; - fillGLInternalTextureInfo(GL_TEXTURE_2D, width, height, 1u, format, swizzle, texInfo); + GLTextureInfo texInfo; + fillGLInternalTextureInfo(GL_TEXTURE_2D, width, height, 1u, format, swizzle, texInfo); - assert(4 == swizzle.size()); - glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_R, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[0])); - glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_G, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[1])); - glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_B, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[2])); - glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_A, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[3])); - assert(!texInfo.uploadParams.compressed); - // For now stream texture upload is using glTexImage2D instead of glStore/glSubimage because its size/format cannot be immutable - glTexImage2D(texInfo.target, 0, texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height, 0, texInfo.uploadParams.baseInternalFormat, texInfo.uploadParams.type, data); + assert(4 == swizzle.size()); + glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_R, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[0])); + glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_G, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[1])); + glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_B, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[2])); + glTexParameteri(texInfo.target, GL_TEXTURE_SWIZZLE_A, TypesConversion_GL::GetGlColorFromTextureChannelColor(texInfo.uploadParams.swizzle[3])); + assert(!texInfo.uploadParams.compressed); + // For now stream texture upload is using glTexImage2D instead of glStore/glSubimage because its size/format cannot be immutable + glTexImage2D(texInfo.target, 0, texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height, 0, texInfo.uploadParams.baseInternalFormat, texInfo.uploadParams.type, data); - return handle; - } + return handle; } - void Device_GL::fillGLInternalTextureInfo(GLenum target, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, GLTextureInfo& glTexInfoOut) const + void Device_GL::fillGLInternalTextureInfo(GLenum target, uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, GLTextureInfo& glTexInfoOut) const { glTexInfoOut.target = target; - glTexInfoOut.width = width; - glTexInfoOut.height = height; - glTexInfoOut.depth = depth; + glTexInfoOut.width = static_cast(width); + glTexInfoOut.height = static_cast(height); + glTexInfoOut.depth = static_cast(depth); if (!m_limits.isTextureFormatAvailable(textureFormat)) { @@ -649,13 +624,13 @@ namespace ramses_internal glTexInfoOut.uploadParams.swizzle = swizzle; } - uint32_t Device_GL::checkAndClampNumberOfSamples(GLenum internalFormat, uint32_t numSamples) const + uint32_t Device_GL::CheckAndClampNumberOfSamples(GLenum internalFormat, uint32_t numSamples) { if (numSamples > 1) { GLint maxNumSamplesGL = 0; glGetInternalformativ(GL_RENDERBUFFER, internalFormat, GL_SAMPLES, 1, &maxNumSamplesGL); - uint32_t maxNumSamples = static_cast(maxNumSamplesGL); + auto maxNumSamples = static_cast(maxNumSamplesGL); if (numSamples > maxNumSamples) { LOG_WARN_P(CONTEXT_RENDERER, "Device_GL: clamping requested MSAA sample count {} " @@ -667,7 +642,7 @@ namespace ramses_internal return numSamples; } - void Device_GL::allocateTextureStorage(const GLTextureInfo& texInfo, uint32_t mipLevels, uint32_t sampleCount) const + void Device_GL::AllocateTextureStorage(const GLTextureInfo& texInfo, uint32_t mipLevels, uint32_t sampleCount) { assert(!(sampleCount && mipLevels > 1)); glPixelStorei(GL_UNPACK_ALIGNMENT, texInfo.uploadParams.byteAlignment); @@ -683,14 +658,14 @@ namespace ramses_internal { case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP: - glTexStorage2D(texInfo.target, mipLevels, texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height); + glTexStorage2D(texInfo.target, static_cast(mipLevels), texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height); break; case GL_TEXTURE_2D_MULTISAMPLE: - sampleCount = checkAndClampNumberOfSamples(texInfo.uploadParams.sizedInternalFormat, sampleCount); - glTexStorage2DMultisample(texInfo.target, sampleCount, texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height, ToGLboolean(true)); + sampleCount = CheckAndClampNumberOfSamples(texInfo.uploadParams.sizedInternalFormat, sampleCount); + glTexStorage2DMultisample(texInfo.target, static_cast(sampleCount), texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height, ToGLboolean(true)); break; case GL_TEXTURE_3D: - glTexStorage3D(texInfo.target, mipLevels, texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height, texInfo.depth); + glTexStorage3D(texInfo.target, static_cast(mipLevels), texInfo.uploadParams.sizedInternalFormat, texInfo.width, texInfo.height, texInfo.depth); break; default: assert(false); @@ -698,7 +673,7 @@ namespace ramses_internal } } - void Device_GL::uploadTextureMipMapData(uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const GLTextureInfo& texInfo, const uint8_t *pData, uint32_t dataSize) const + void Device_GL::UploadTextureMipMapData(uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const GLTextureInfo& texInfo, const std::byte *pData, uint32_t dataSize, uint32_t stride) { assert(width > 0 && height > 0 && depth > 0 && "trying to upload texture with 0 width and/or height and/or depth!"); assert(x + width <= TextureMathUtils::GetMipSize(mipLevel, texInfo.width)); @@ -706,6 +681,10 @@ namespace ramses_internal assert(z + depth <= TextureMathUtils::GetMipSize(mipLevel, texInfo.depth)); assert(dataSize > 0u || !texInfo.uploadParams.compressed); + glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast(stride)); + glPixelStorei(GL_UNPACK_SKIP_ROWS, static_cast(y)); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, static_cast(x)); + switch (texInfo.target) { case GL_TEXTURE_2D: @@ -717,46 +696,87 @@ namespace ramses_internal case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: if (texInfo.uploadParams.compressed) { - glCompressedTexSubImage2D(texInfo.target, mipLevel, x, y, width, height, texInfo.uploadParams.sizedInternalFormat, dataSize, pData); + glCompressedTexSubImage2D(texInfo.target, + static_cast(mipLevel), + static_cast(x), + static_cast(y), + static_cast(width), + static_cast(height), + texInfo.uploadParams.sizedInternalFormat, + static_cast(dataSize), + pData); } else { - glTexSubImage2D(texInfo.target, mipLevel, x, y, width, height, texInfo.uploadParams.baseInternalFormat, texInfo.uploadParams.type, pData); + glTexSubImage2D(texInfo.target, + static_cast(mipLevel), + static_cast(x), + static_cast(y), + static_cast(width), + static_cast(height), + texInfo.uploadParams.baseInternalFormat, + texInfo.uploadParams.type, + pData); } break; case GL_TEXTURE_3D: if (texInfo.uploadParams.compressed) { - glCompressedTexSubImage3D(texInfo.target, mipLevel, x, y, z, width, height, depth, texInfo.uploadParams.sizedInternalFormat, dataSize, pData); + glCompressedTexSubImage3D(texInfo.target, + static_cast(mipLevel), + static_cast(x), + static_cast(y), + static_cast(z), + static_cast(width), + static_cast(height), + static_cast(depth), + texInfo.uploadParams.sizedInternalFormat, + static_cast(dataSize), + pData); } else { - glTexSubImage3D(texInfo.target, mipLevel, x, y, z, width, height, depth, texInfo.uploadParams.baseInternalFormat, texInfo.uploadParams.type, pData); + glTexSubImage3D(texInfo.target, + static_cast(mipLevel), + static_cast(x), + static_cast(y), + static_cast(z), + static_cast(width), + static_cast(height), + static_cast(depth), + texInfo.uploadParams.baseInternalFormat, + texInfo.uploadParams.type, + pData); } break; default: assert(false); break; } + + // restore default values + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); } - DeviceResourceHandle Device_GL::uploadRenderBuffer(uint32_t width, uint32_t height, ERenderBufferType type, ETextureFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) + DeviceResourceHandle Device_GL::uploadRenderBuffer(uint32_t width, uint32_t height, EPixelStorageFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) { GLHandle bufferGLHandle = InvalidGLHandle; switch (accessMode) { - case ERenderBufferAccessMode_ReadWrite: + case ERenderBufferAccessMode::ReadWrite: bufferGLHandle = createTexture(width, height, format, sampleCount); break; - case ERenderBufferAccessMode_WriteOnly: - bufferGLHandle = createRenderBuffer(width, height, format, sampleCount); + case ERenderBufferAccessMode::WriteOnly: + bufferGLHandle = CreateRenderBuffer(width, height, format, sampleCount); break; default: assert(false); } if (bufferGLHandle != InvalidGLHandle) - return m_resourceMapper.registerResource(std::make_unique(bufferGLHandle, width, height, type, format, sampleCount, accessMode)); + return m_resourceMapper.registerResource(std::make_unique(bufferGLHandle, width, height, format, sampleCount, accessMode)); return DeviceResourceHandle::Invalid(); } @@ -794,13 +814,13 @@ namespace ramses_internal void Device_GL::deleteRenderBuffer(DeviceResourceHandle bufferHandle) { - const RenderBufferGPUResource& resource = m_resourceMapper.getResourceAs(bufferHandle); + const auto& resource = m_resourceMapper.getResourceAs(bufferHandle); const GLHandle glAddress = resource.getGPUAddress(); - if (ERenderBufferAccessMode_ReadWrite == resource.getAccessMode()) + if (ERenderBufferAccessMode::ReadWrite == resource.getAccessMode()) { glDeleteTextures(1, &glAddress); } - else if (ERenderBufferAccessMode_WriteOnly == resource.getAccessMode()) + else if (ERenderBufferAccessMode::WriteOnly == resource.getAccessMode()) { glDeleteRenderbuffers(1, &glAddress); } @@ -814,12 +834,12 @@ namespace ramses_internal DeviceResourceHandle Device_GL::uploadTextureSampler(const TextureSamplerStates& samplerStates) { - GLuint sampler; + GLuint sampler = 0; glGenSamplers(1, &sampler); - const GLenum wrappingModeR = TypesConversion_GL::GetWrapMode(samplerStates.m_addressModeR); - const GLenum wrappingModeU = TypesConversion_GL::GetWrapMode(samplerStates.m_addressModeU); - const GLenum wrappingModeV = TypesConversion_GL::GetWrapMode(samplerStates.m_addressModeV); + const auto wrappingModeR = TypesConversion_GL::GetWrapMode(samplerStates.m_addressModeR); + const auto wrappingModeU = TypesConversion_GL::GetWrapMode(samplerStates.m_addressModeU); + const auto wrappingModeV = TypesConversion_GL::GetWrapMode(samplerStates.m_addressModeV); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, wrappingModeU); glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, wrappingModeV); @@ -827,41 +847,38 @@ namespace ramses_internal switch (samplerStates.m_minSamplingMode) { - case ESamplingMethod::Nearest: + case ETextureSamplingMethod::Nearest: glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); break; - case ESamplingMethod::Linear: + case ETextureSamplingMethod::Linear: glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; - case ESamplingMethod::Nearest_MipMapNearest: + case ETextureSamplingMethod::Nearest_MipMapNearest: glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); break; - case ESamplingMethod::Nearest_MipMapLinear: + case ETextureSamplingMethod::Nearest_MipMapLinear: glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); break; - case ESamplingMethod::Linear_MipMapNearest: + case ETextureSamplingMethod::Linear_MipMapNearest: glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); break; - case ESamplingMethod::Linear_MipMapLinear: + case ETextureSamplingMethod::Linear_MipMapLinear: glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); break; - case ESamplingMethod::NUMBER_OF_ELEMENTS: - assert(false); } switch (samplerStates.m_magSamplingMode) { - case ESamplingMethod::Nearest: + case ETextureSamplingMethod::Nearest: glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; - case ESamplingMethod::Linear: + case ETextureSamplingMethod::Linear: glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); break; - case ESamplingMethod::Nearest_MipMapNearest: - case ESamplingMethod::Nearest_MipMapLinear: - case ESamplingMethod::Linear_MipMapNearest: - case ESamplingMethod::Linear_MipMapLinear: - case ESamplingMethod::NUMBER_OF_ELEMENTS: + case ETextureSamplingMethod::Nearest_MipMapNearest: + case ETextureSamplingMethod::Nearest_MipMapLinear: + case ETextureSamplingMethod::Linear_MipMapNearest: + case ETextureSamplingMethod::Linear_MipMapLinear: assert(false); } @@ -870,7 +887,7 @@ namespace ramses_internal { // clamp anisotropy value to max supported range const auto anisotropyLevel = std::min(samplerStates.m_anisotropyLevel, m_limits.getMaximumAnisotropy()); - glSamplerParameteri(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropyLevel); + glSamplerParameteri(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast(anisotropyLevel)); } return m_resourceMapper.registerResource(std::make_unique(sampler, 0u)); @@ -878,7 +895,7 @@ namespace ramses_internal void Device_GL::deleteTextureSampler(DeviceResourceHandle handle) { - const GPUResource& resource = m_resourceMapper.getResourceAs(handle); + const auto& resource = m_resourceMapper.getResourceAs(handle); const GLHandle glAddress = resource.getGPUAddress(); glDeleteSamplers(1, &glAddress); @@ -917,7 +934,7 @@ namespace ramses_internal for (size_t i = 0; i < renderBuffers.size(); ++i) { - const RenderBufferGPUResource& bufferGPUResource = m_resourceMapper.getResourceAs(renderBuffers[i]); + const auto& bufferGPUResource = m_resourceMapper.getResourceAs(renderBuffers[i]); if (0 == i) { @@ -954,10 +971,10 @@ namespace ramses_internal colorBuffers.reserve(renderBuffers.size()); for (const DeviceResourceHandle rbHandle : renderBuffers) { - const RenderBufferGPUResource& bufferGPUResource = m_resourceMapper.getResourceAs(rbHandle); - bindRenderBufferToRenderTarget(bufferGPUResource, colorBuffers.size()); + const auto& bufferGPUResource = m_resourceMapper.getResourceAs(rbHandle); + BindRenderBufferToRenderTarget(bufferGPUResource, colorBuffers.size()); - if (ERenderBufferType_ColorBuffer == bufferGPUResource.getType()) + if (!IsDepthOrStencilFormat(bufferGPUResource.getStorageFormat())) colorBuffers.push_back(GL_COLOR_ATTACHMENT0 + static_cast(colorBuffers.size())); } @@ -976,7 +993,7 @@ namespace ramses_internal void Device_GL::deleteRenderTarget(DeviceResourceHandle handle) { - const RenderTargetGPUResource& rtResource = m_resourceMapper.getResourceAs(handle); + const auto& rtResource = m_resourceMapper.getResourceAs(handle); const GLHandle rtGlAddress = rtResource.getGPUAddress(); assert(rtGlAddress != InvalidGLHandle); glDeleteFramebuffers(1, &rtGlAddress); @@ -992,15 +1009,15 @@ namespace ramses_internal glInvalidateFramebuffer(GL_FRAMEBUFFER, static_cast(depthStencilAttachments.size()), depthStencilAttachments.data()); } - void Device_GL::bindRenderBufferToRenderTarget(const RenderBufferGPUResource& renderBufferGpuResource, size_t colorBufferSlot) + void Device_GL::BindRenderBufferToRenderTarget(const RenderBufferGPUResource& renderBufferGpuResource, size_t colorBufferSlot) { switch (renderBufferGpuResource.getAccessMode()) { - case ramses_internal::ERenderBufferAccessMode_WriteOnly: - bindWriteOnlyRenderBufferToRenderTarget(renderBufferGpuResource.getType(), colorBufferSlot, renderBufferGpuResource.getGPUAddress()); + case ERenderBufferAccessMode::WriteOnly: + BindWriteOnlyRenderBufferToRenderTarget(renderBufferGpuResource.getStorageFormat(), colorBufferSlot, renderBufferGpuResource.getGPUAddress()); break; - case ramses_internal::ERenderBufferAccessMode_ReadWrite: - bindReadWriteRenderBufferToRenderTarget(renderBufferGpuResource.getType(), colorBufferSlot, renderBufferGpuResource.getGPUAddress(), renderBufferGpuResource.getSampleCount() != 0); + case ERenderBufferAccessMode::ReadWrite: + BindReadWriteRenderBufferToRenderTarget(renderBufferGpuResource.getStorageFormat(), colorBufferSlot, renderBufferGpuResource.getGPUAddress(), renderBufferGpuResource.getSampleCount() != 0); break; default: assert(false && "invalid render buffer access mode"); @@ -1008,46 +1025,38 @@ namespace ramses_internal } } - void Device_GL::bindReadWriteRenderBufferToRenderTarget(ERenderBufferType bufferType, size_t colorBufferSlot, GLHandle bufferGLHandle, const bool multiSample) + void Device_GL::BindReadWriteRenderBufferToRenderTarget(EPixelStorageFormat bufferFormat, size_t colorBufferSlot, GLHandle bufferGLHandle, const bool multiSample) { const int texTarget = (multiSample) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; - switch (bufferType) + if (IsDepthOnlyFormat(bufferFormat)) { - case ERenderBufferType_DepthBuffer: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texTarget, bufferGLHandle, 0); - break; - case ERenderBufferType_DepthStencilBuffer: + } + else if (IsDepthOrStencilFormat(bufferFormat)) + { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texTarget, bufferGLHandle, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, texTarget, bufferGLHandle, 0); - break; - case ERenderBufferType_ColorBuffer: + } + else + { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast(colorBufferSlot), texTarget, bufferGLHandle, 0); - break; - case ERenderBufferType_InvalidBuffer: - default: - assert(false && "invalid render buffer type"); - break; } } - void Device_GL::bindWriteOnlyRenderBufferToRenderTarget(ERenderBufferType bufferType, size_t colorBufferSlot, GLHandle bufferGLHandle) + void Device_GL::BindWriteOnlyRenderBufferToRenderTarget(EPixelStorageFormat bufferFormat, size_t colorBufferSlot, GLHandle bufferGLHandle) { - switch (bufferType) + if (IsDepthOnlyFormat(bufferFormat)) { - case ERenderBufferType_DepthBuffer: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bufferGLHandle); - break; - case ERenderBufferType_DepthStencilBuffer: + } + else if (IsDepthOrStencilFormat(bufferFormat)) + { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bufferGLHandle); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, bufferGLHandle); - break; - case ERenderBufferType_ColorBuffer: + } + else + { glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast(colorBufferSlot), GL_RENDERBUFFER, bufferGLHandle); - break; - case ERenderBufferType_InvalidBuffer: - default: - assert(false && "invalid render buffer type"); - break; } } @@ -1089,7 +1098,7 @@ namespace ramses_internal renderTargetPair->readingIndex = (renderTargetPair->readingIndex + 1) % 2; } - GLHandle Device_GL::generateAndBindTexture(GLenum target) const + GLHandle Device_GL::GenerateAndBindTexture(GLenum target) { GLHandle texID = InvalidGLHandle; glGenTextures(1, &texID); @@ -1108,7 +1117,7 @@ namespace ramses_internal return m_resourceMapper.registerResource(std::make_unique(glAddress, totalSizeInBytes)); } - void Device_GL::uploadVertexBufferData(DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) + void Device_GL::uploadVertexBufferData(DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) { const auto& vertexBuffer = m_resourceMapper.getResource(handle); assert(dataSize <= vertexBuffer.getTotalSizeInBytes()); @@ -1135,7 +1144,7 @@ namespace ramses_internal if (vertexArrayInfo.indexBuffer.isValid()) { - const IndexBufferGPUResource& indexBufferGPUResource = m_resourceMapper.getResourceAs(vertexArrayInfo.indexBuffer); + const auto& indexBufferGPUResource = m_resourceMapper.getResourceAs(vertexArrayInfo.indexBuffer); const GLHandle resourceAddress = indexBufferGPUResource.getGPUAddress(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, resourceAddress); } @@ -1159,7 +1168,7 @@ namespace ramses_internal const std::intptr_t offsetInBytes = vb.startVertex * elementSize + vb.offsetWithinElement; const void* offsetAsPointer = reinterpret_cast(offsetInBytes); - const auto attributeNumComponents = EnumToNumComponents(attributeDataType); + const auto attributeNumComponents = static_cast(EnumToNumComponents(attributeDataType)); glBindBuffer(GL_ARRAY_BUFFER, arrayResource.getGPUAddress()); glEnableVertexAttribArray(vertexInputAddress.getValue()); @@ -1178,13 +1187,13 @@ namespace ramses_internal void Device_GL::activateVertexArray(DeviceResourceHandle handle) { assert(handle.isValid()); - const VertexArrayGPUResource& vertexArrayResource = m_resourceMapper.getResourceAs(handle); + const auto& vertexArrayResource = m_resourceMapper.getResourceAs(handle); glBindVertexArray(vertexArrayResource.getGPUAddress()); const auto indexBuffer = vertexArrayResource.getIndexBufferHandle(); if (indexBuffer.isValid()) { - const IndexBufferGPUResource& indexBufferGPUResource = m_resourceMapper.getResourceAs(indexBuffer); + const auto& indexBufferGPUResource = m_resourceMapper.getResourceAs(indexBuffer); m_activeIndexArrayElementSizeBytes = indexBufferGPUResource.getElementSizeInBytes(); assert(m_activeIndexArrayElementSizeBytes == 2 || m_activeIndexArrayElementSizeBytes == 4); m_activeIndexArraySizeBytes = indexBufferGPUResource.getTotalSizeInBytes(); @@ -1215,7 +1224,7 @@ namespace ramses_internal return m_resourceMapper.registerResource(std::make_unique(glAddress, sizeInBytes, dataType == EDataType::UInt16 ? 2 : 4)); } - void Device_GL::uploadIndexBufferData(DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) + void Device_GL::uploadIndexBufferData(DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) { const auto& indexBuffer = m_resourceMapper.getResource(handle); assert(dataSize <= indexBuffer.getTotalSizeInBytes()); @@ -1239,12 +1248,11 @@ namespace ramses_internal const bool uploadSuccessful = ShaderUploader_GL::UploadShaderProgramFromSource(shader, programInfo, debugErrorLog); if (uploadSuccessful) - return std::make_unique(shader, programInfo); - else { - LOG_ERROR(CONTEXT_RENDERER, "Device_GL::uploadShader: shader upload failed: " << debugErrorLog); - return nullptr; + return std::make_unique(shader, programInfo); } + LOG_ERROR(CONTEXT_RENDERER, "Device_GL::uploadShader: shader upload failed: " << debugErrorLog); + return nullptr; } DeviceResourceHandle Device_GL::registerShader(std::unique_ptr shaderResource) @@ -1252,7 +1260,7 @@ namespace ramses_internal return m_resourceMapper.registerResource(std::move(shaderResource)); } - DeviceResourceHandle Device_GL::uploadBinaryShader(const EffectResource& shader, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) + DeviceResourceHandle Device_GL::uploadBinaryShader(const EffectResource& shader, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) { ShaderProgramInfo programInfo; std::string debugErrorLog; @@ -1263,25 +1271,22 @@ namespace ramses_internal LOG_DEBUG(CONTEXT_SMOKETEST, "Device_GL::uploadShader: renderer successfully uploaded binary shader for effect " << shader.getName()); return m_resourceMapper.registerResource(std::make_unique(shader, programInfo)); } - else - { - LOG_INFO(CONTEXT_RENDERER, "Device_GL::uploadShader: renderer failed to upload binary shader for effect " << shader.getName() << ". Error was: " << debugErrorLog); - return DeviceResourceHandle::Invalid(); - } + LOG_INFO(CONTEXT_RENDERER, "Device_GL::uploadShader: renderer failed to upload binary shader for effect " << shader.getName() << ". Error was: " << debugErrorLog); + return DeviceResourceHandle::Invalid(); } - bool Device_GL::getBinaryShader(DeviceResourceHandle handle, UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) + bool Device_GL::getBinaryShader(DeviceResourceHandle handle, std::vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) { binaryShader.clear(); - const ShaderGPUResource_GL& shaderProgramGL = m_resourceMapper.getResourceAs(handle); + const auto& shaderProgramGL = m_resourceMapper.getResourceAs(handle); LOG_TRACE(CONTEXT_RENDERER, "Device_GL::getBinaryShader: retrieving shader binary for effect with handle " << handle.asMemoryHandle()); return shaderProgramGL.getBinaryInfo(binaryShader, binaryShaderFormat); } void Device_GL::deleteShader(DeviceResourceHandle handle) { - const ShaderGPUResource_GL& shaderProgramGL = m_resourceMapper.getResourceAs(handle); + const auto& shaderProgramGL = m_resourceMapper.getResourceAs(handle); if (m_activeShader == &shaderProgramGL) { m_activeShader = nullptr; @@ -1292,7 +1297,7 @@ namespace ramses_internal void Device_GL::activateShader(DeviceResourceHandle handle) { - const ShaderGPUResource_GL& shaderProgramGL = m_resourceMapper.getResourceAs(handle); + const auto& shaderProgramGL = m_resourceMapper.getResourceAs(handle); glUseProgram(shaderProgramGL.getGPUAddress()); m_activeShader = &shaderProgramGL; } @@ -1307,8 +1312,8 @@ namespace ramses_internal void Device_GL::activateTexture(DeviceResourceHandle handle, DataFieldHandle field) { - GLInputLocation uniformLocation; - if (getUniformLocation(field, uniformLocation)) + const auto uniformLocation = m_activeShader->getUniformLocation(field); + if (uniformLocation.isValid()) { const TextureSlotInfo textureSlot = m_activeShader->getTextureSlot(field); @@ -1337,7 +1342,7 @@ namespace ramses_internal } } - int Device_GL::getTextureAddress(DeviceResourceHandle handle) const + uint32_t Device_GL::getTextureAddress(DeviceResourceHandle handle) const { return m_resourceMapper.getResource(handle).getGPUAddress(); } @@ -1359,15 +1364,15 @@ namespace ramses_internal const GLenum blittingMask = (colorOnly ? GL_COLOR_BUFFER_BIT : (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); glBindFramebuffer(GL_READ_FRAMEBUFFER, blittingSourceFrameBuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, blittingDestinationFrameBuffer); - glBlitFramebuffer(srcRect.x, - srcRect.y, - srcRect.x + srcRect.width, - srcRect.y + srcRect.height, + glBlitFramebuffer(static_cast(srcRect.x), + static_cast(srcRect.y), + static_cast(srcRect.x + srcRect.width), + static_cast(srcRect.y + srcRect.height), - dstRect.x, - dstRect.y, - dstRect.x + dstRect.width, - dstRect.y + dstRect.height, + static_cast(dstRect.x), + static_cast(dstRect.y), + static_cast(dstRect.x + dstRect.width), + static_cast(dstRect.y + dstRect.height), blittingMask, GL_NEAREST); @@ -1421,8 +1426,8 @@ namespace ramses_internal for (GLint compressedGLTextureFormat : compressedTextureFormats) { - const ETextureFormat textureFormat = TypesConversion_GL::GetTextureFormatFromCompressedGLTextureFormat(compressedGLTextureFormat); - if (ETextureFormat::Invalid != textureFormat) + const EPixelStorageFormat textureFormat = TypesConversion_GL::GetTextureFormatFromCompressedGLTextureFormat(compressedGLTextureFormat); + if (EPixelStorageFormat::Invalid != textureFormat) { m_limits.addTextureFormat(textureFormat); } @@ -1433,7 +1438,7 @@ namespace ramses_internal glGetIntegerv(GL_MAX_SAMPLES, &maxMSAASamples); m_limits.setMaximumSamples(maxMSAASamples); - std::array maxViewport; + std::array maxViewport{}; glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewport.data()); m_limits.setMaxViewport(maxViewport[0], maxViewport[1]); @@ -1491,7 +1496,7 @@ namespace ramses_internal void Device_GL::readPixels(uint8_t* buffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, static_cast(buffer)); + glReadPixels(static_cast(x), static_cast(y), static_cast(width), static_cast(height), GL_RGBA, GL_UNSIGNED_BYTE, static_cast(buffer)); } uint32_t Device_GL::getTotalGpuMemoryUsageInKB() const diff --git a/renderer/Platform/Device_GL/include/Device_GL/Device_GL.h b/src/renderer/internal/Platform/OpenGL/Device_GL.h similarity index 69% rename from renderer/Platform/Device_GL/include/Device_GL/Device_GL.h rename to src/renderer/internal/Platform/OpenGL/Device_GL.h index bab7b2323..9588ed83e 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/Device_GL.h +++ b/src/renderer/internal/Platform/OpenGL/Device_GL.h @@ -6,26 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICE_GL_H -#define RAMSES_DEVICE_GL_H +#pragma once -#include "Platform_Base/Device_Base.h" -#include "Platform_Base/DeviceResourceMapper.h" +#include "internal/RendererLib/PlatformBase/Device_Base.h" +#include "internal/RendererLib/PlatformBase/DeviceResourceMapper.h" #include "Types_GL.h" #include "DebugOutput.h" -#include "SceneAPI/TextureSamplerStates.h" +#include "internal/SceneGraph/SceneAPI/TextureSamplerStates.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ShaderGPUResource_GL; class RenderBufferGPUResource; class IDeviceExtension; struct GLTextureInfo; - class Device_GL : public Device_Base + class Device_GL final : public Device_Base { public: explicit Device_GL(IContext& context, IDeviceExtension* deviceExtension); @@ -36,7 +35,7 @@ namespace ramses_internal void drawIndexedTriangles(int32_t startOffset, int32_t elementCount, uint32_t instanceCount) override; void drawTriangles (int32_t startOffset, int32_t elementCount, uint32_t instanceCount) override; - void clear (uint32_t clearFlags) override; + void clear (ClearFlags clearFlags) override; void colorMask (bool r, bool g, bool b, bool a) override; void clearColor (const glm::vec4& clearColor) override; void clearDepth (float d) override; @@ -53,22 +52,23 @@ namespace ramses_internal void drawMode (EDrawMode mode) override; void setViewport (int32_t x, int32_t y, uint32_t width, uint32_t height) override; - void setConstant(DataFieldHandle field, uint32_t count, const float* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const float* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const bool* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) override; void readPixels(uint8_t* buffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; DeviceResourceHandle allocateVertexBuffer (uint32_t totalSizeInBytes) override; - void uploadVertexBufferData(DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) override; + void uploadVertexBufferData(DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) override; void deleteVertexBuffer (DeviceResourceHandle handle) override; DeviceResourceHandle allocateVertexArray (const VertexArrayInfo& vertexArrayInfo) override; @@ -76,31 +76,31 @@ namespace ramses_internal void deleteVertexArray (DeviceResourceHandle handle) override; DeviceResourceHandle allocateIndexBuffer (EDataType dataType, uint32_t sizeInBytes) override; - void uploadIndexBufferData (DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) override; + void uploadIndexBufferData (DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) override; void deleteIndexBuffer (DeviceResourceHandle handle) override; std::unique_ptr uploadShader(const EffectResource& shader) override; DeviceResourceHandle registerShader (std::unique_ptr shaderResource) override; - DeviceResourceHandle uploadBinaryShader (const EffectResource& shader, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) override; - bool getBinaryShader (DeviceResourceHandle handleconst, UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) override; + DeviceResourceHandle uploadBinaryShader (const EffectResource& shader, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) override; + bool getBinaryShader (DeviceResourceHandle handleconst, std::vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) override; void deleteShader (DeviceResourceHandle handle) override; void activateShader (DeviceResourceHandle handle) override; - DeviceResourceHandle allocateTexture2D (uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; - DeviceResourceHandle allocateTexture3D (uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; - DeviceResourceHandle allocateTextureCube (uint32_t faceSize, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; + DeviceResourceHandle allocateTexture2D (uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; + DeviceResourceHandle allocateTexture3D (uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; + DeviceResourceHandle allocateTextureCube (uint32_t faceSize, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; DeviceResourceHandle allocateExternalTexture() override; DeviceResourceHandle getEmptyExternalTexture() const override; void bindTexture (DeviceResourceHandle handle) override; void generateMipmaps (DeviceResourceHandle handle) override; - void uploadTextureData (DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const Byte* data, uint32_t dataSize) override; - DeviceResourceHandle uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) override; + void uploadTextureData (DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const std::byte* data, uint32_t dataSize, uint32_t stride) override; + DeviceResourceHandle uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) override; void deleteTexture (DeviceResourceHandle handle) override; void activateTexture (DeviceResourceHandle handle, DataFieldHandle field) override; - int getTextureAddress (DeviceResourceHandle handle) const override; + uint32_t getTextureAddress (DeviceResourceHandle handle) const override; - DeviceResourceHandle uploadRenderBuffer (uint32_t width, uint32_t height, ERenderBufferType type, ETextureFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) override; + DeviceResourceHandle uploadRenderBuffer (uint32_t width, uint32_t height, EPixelStorageFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) override; void deleteRenderBuffer (DeviceResourceHandle handle) override; DeviceResourceHandle uploadDmaRenderBuffer (uint32_t width, uint32_t height, DmaBufferFourccFormat fourccFormat, DmaBufferUsageFlags usageFlags, DmaBufferModifiers modifiers) override; @@ -139,7 +139,7 @@ namespace ramses_internal { std::array renderTargets; std::array colorBuffers; - uint8_t readingIndex; + uint8_t readingIndex = 0; }; std::vector m_pairedRenderTargets; @@ -155,35 +155,31 @@ namespace ramses_internal std::vector m_supportedBinaryProgramFormats; IDeviceExtension* m_deviceExtension = nullptr; const DeviceResourceHandle m_emptyExternalTextureResource; + std::vector m_containerForBoolValues; std::unordered_map m_textureSamplerObjectsCache; - bool getUniformLocation(DataFieldHandle field, GLInputLocation& location) const; - bool getAttributeLocation(DataFieldHandle field, GLInputLocation& location) const; - bool allBuffersHaveTheSameSize(const DeviceHandleVector& renderBuffers) const; - void bindRenderBufferToRenderTarget(const RenderBufferGPUResource& renderBufferGpuResource, size_t colorBufferSlot); - void bindReadWriteRenderBufferToRenderTarget(ERenderBufferType bufferType, size_t colorBufferSlot, GLHandle bufferGLHandle, bool multiSample); - void bindWriteOnlyRenderBufferToRenderTarget(ERenderBufferType bufferType, size_t colorBufferSlot, GLHandle bufferGLHandle); - GLHandle createTexture(uint32_t width, uint32_t height, ETextureFormat storageFormat, uint32_t sampleCount) const; - GLHandle createRenderBuffer(uint32_t width, uint32_t height, ETextureFormat format, uint32_t sampleCount); + static void BindRenderBufferToRenderTarget(const RenderBufferGPUResource& renderBufferGpuResource, size_t colorBufferSlot); + static void BindReadWriteRenderBufferToRenderTarget(EPixelStorageFormat bufferFormat, size_t colorBufferSlot, GLHandle bufferGLHandle, bool multiSample); + static void BindWriteOnlyRenderBufferToRenderTarget(EPixelStorageFormat bufferFormat, size_t colorBufferSlot, GLHandle bufferGLHandle); + GLHandle createTexture(uint32_t width, uint32_t height, EPixelStorageFormat storageFormat, uint32_t sampleCount) const; + static GLHandle CreateRenderBuffer(uint32_t width, uint32_t height, EPixelStorageFormat format, uint32_t sampleCount); DeviceResourceHandle uploadTextureSampler(const TextureSamplerStates& samplerStates); void deleteTextureSampler(DeviceResourceHandle handle); void activateTextureSampler(DeviceResourceHandle handle, DataFieldHandle field); - GLHandle generateAndBindTexture(GLenum target) const; + static GLHandle GenerateAndBindTexture(GLenum target); - void fillGLInternalTextureInfo(GLenum target, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, GLTextureInfo& texInfoOut) const; - uint32_t checkAndClampNumberOfSamples(GLenum internalFormat, uint32_t numSamples) const; + void fillGLInternalTextureInfo(GLenum target, uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, GLTextureInfo& texInfoOut) const; + static uint32_t CheckAndClampNumberOfSamples(GLenum internalFormat, uint32_t numSamples); - void allocateTextureStorage(const GLTextureInfo& texInfo, uint32_t mipLevels, uint32_t sampleCount = 0) const; - void uploadTextureMipMapData(uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const GLTextureInfo& texInfo, const uint8_t *pData, uint32_t dataSize) const; + static void AllocateTextureStorage(const GLTextureInfo& texInfo, uint32_t mipLevels, uint32_t sampleCount = 0); + static void UploadTextureMipMapData(uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const GLTextureInfo& texInfo, const std::byte *pData, uint32_t dataSize, uint32_t stride); bool isApiExtensionAvailable(const std::string& extensionName) const; void queryDeviceDependentFeatures(); void loadOpenGLExtensions(); }; } - -#endif diff --git a/renderer/Platform/Device_GL/src/Device_GL_platform.cpp b/src/renderer/internal/Platform/OpenGL/Device_GL_platform.cpp similarity index 85% rename from renderer/Platform/Device_GL/src/Device_GL_platform.cpp rename to src/renderer/internal/Platform/OpenGL/Device_GL_platform.cpp index 857349043..4c089eab9 100644 --- a/renderer/Platform/Device_GL/src/Device_GL_platform.cpp +++ b/src/renderer/internal/Platform/OpenGL/Device_GL_platform.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_GL/Device_GL_platform.h" +#include "internal/Platform/OpenGL/Device_GL_platform.h" -namespace ramses_internal +namespace ramses::internal { DEFINE_ALL_API_PROCS; } diff --git a/renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform.h b/src/renderer/internal/Platform/OpenGL/Device_GL_platform.h similarity index 69% rename from renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform.h rename to src/renderer/internal/Platform/OpenGL/Device_GL_platform.h index 87af5bf12..20d8b28fd 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform.h +++ b/src/renderer/internal/Platform/OpenGL/Device_GL_platform.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICE_GL_PLATFORM_H -#define RAMSES_DEVICE_GL_PLATFORM_H +#pragma once #ifdef _WIN32 -#include "PlatformAbstraction/MinimalWindowsH.h" +#include "internal/PlatformAbstraction/MinimalWindowsH.h" #include #include @@ -22,17 +21,27 @@ // should always include that file as well, since gl3ext doesn't define ANY extensions, see http://www.khronos.org/registry/gles/#headers #include +#elif defined(__APPLE__) +#include +#include +// should always include that file as well, since gl3ext doesn't define ANY extensions, see http://www.khronos.org/registry/gles/#headers +#include + #endif #if defined(__linux__) - #include "Device_GL/Device_GL_platform_linux.h" + #include "internal/Platform/OpenGL/Device_GL_platform_linux.h" #endif // LINUX +#if defined(__APPLE__) + #include "internal/Platform/OpenGL/Device_GL_platform_apple.h" +#endif // APPLE + #ifdef _WIN32 - #include "Device_GL/Device_GL_platform_windows.h" + #include "internal/Platform/OpenGL/Device_GL_platform_windows.h" #endif // WIN32 -namespace ramses_internal +namespace ramses::internal { // OpenGL API DECLARE_ALL_API_PROCS @@ -45,5 +54,3 @@ namespace ramses_internal #define GL_OES_EGL_image_external #define GL_TEXTURE_EXTERNAL_OES 0x8D65 #endif - -#endif diff --git a/src/renderer/internal/Platform/OpenGL/Device_GL_platform_apple.h b/src/renderer/internal/Platform/OpenGL/Device_GL_platform_apple.h new file mode 100644 index 000000000..42fa8a10a --- /dev/null +++ b/src/renderer/internal/Platform/OpenGL/Device_GL_platform_apple.h @@ -0,0 +1,17 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#define LOAD_ALL_API_PROCS(CONTEXT) (void)CONTEXT // does nothing for apple - api procs are already defined in the GL library +#define DECLARE_ALL_API_PROCS // does nothing for apple - api procs are already declared in the GL headers +#define DEFINE_ALL_API_PROCS // does nothing for apple - api procs are already defined in the GL headers + +// extension procs, however, need to be declared and defined +#define DECLARE_EXTENSION_PROC(TYPE, NAME) extern TYPE NAME; +#define DEFINE_EXTENSION_PROC(TYPE, NAME) TYPE NAME = 0; diff --git a/renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform_linux.h b/src/renderer/internal/Platform/OpenGL/Device_GL_platform_linux.h similarity index 91% rename from renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform_linux.h rename to src/renderer/internal/Platform/OpenGL/Device_GL_platform_linux.h index 561f6f8ee..e12eb0026 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform_linux.h +++ b/src/renderer/internal/Platform/OpenGL/Device_GL_platform_linux.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICE_GL_PLATFORM_LINUX_H -#define RAMSES_DEVICE_GL_PLATFORM_LINUX_H +#pragma once #define LOAD_ALL_API_PROCS(CONTEXT) (void)CONTEXT // does nothing for linux - api procs are already defined in the GL library #define DECLARE_ALL_API_PROCS // does nothing for linux - api procs are already declared in the GL headers @@ -16,5 +15,3 @@ // extension procs, however, need to be declared and defined #define DECLARE_EXTENSION_PROC(TYPE, NAME) extern TYPE NAME; #define DEFINE_EXTENSION_PROC(TYPE, NAME) TYPE NAME = 0; - -#endif diff --git a/renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform_windows.h b/src/renderer/internal/Platform/OpenGL/Device_GL_platform_windows.h similarity index 99% rename from renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform_windows.h rename to src/renderer/internal/Platform/OpenGL/Device_GL_platform_windows.h index 3b0a8f463..9cd7f6eaf 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/Device_GL_platform_windows.h +++ b/src/renderer/internal/Platform/OpenGL/Device_GL_platform_windows.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICE_GL_PLATFORM_WINDOWS_H -#define RAMSES_DEVICE_GL_PLATFORM_WINDOWS_H +#pragma once // Required workaround for WGL, because it defines all extension procs // and leaves no possibility to check if an extension is available in the headers @@ -379,5 +378,3 @@ DEFINE_API_PROC(PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC, glCompressedTexSubImage2D); DEFINE_API_PROC(PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC, glCompressedTexSubImage3D); \ DEFINE_API_PROC(PFNGLGETINTERNALFORMATIVPROC, glGetInternalformativ); \ DEFINE_API_PROC(PFNGLINVALIDATEFRAMEBUFFERPROC, glInvalidateFramebuffer); \ - -#endif diff --git a/renderer/Platform/Device_GL/src/ShaderGPUResource_GL.cpp b/src/renderer/internal/Platform/OpenGL/ShaderGPUResource_GL.cpp similarity index 90% rename from renderer/Platform/Device_GL/src/ShaderGPUResource_GL.cpp rename to src/renderer/internal/Platform/OpenGL/ShaderGPUResource_GL.cpp index 7e9af44e9..36822b65e 100644 --- a/renderer/Platform/Device_GL/src/ShaderGPUResource_GL.cpp +++ b/src/renderer/internal/Platform/OpenGL/ShaderGPUResource_GL.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_GL/ShaderGPUResource_GL.h" -#include "Device_GL/Device_GL_platform.h" -#include "Resource/EffectResource.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/OpenGL/ShaderGPUResource_GL.h" +#include "internal/Platform/OpenGL/Device_GL_platform.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { ShaderGPUResource_GL::ShaderGPUResource_GL(const EffectResource& effect, ShaderProgramInfo shaderProgramInfo) : ShaderGPUResource(shaderProgramInfo.shaderProgramHandle) @@ -57,8 +57,7 @@ namespace ramses_internal TextureSlotInfo ShaderGPUResource_GL::getTextureSlot(DataFieldHandle field) const { TextureSlotInfo slot; - const EStatus status = m_bufferSlots.get(field, slot); - UNUSED(status); + [[maybe_unused]] const EStatus status = m_bufferSlots.get(field, slot); assert(status == EStatus::Ok); return slot; } @@ -102,7 +101,7 @@ namespace ramses_internal const char* varName = input.inputName.c_str(); const GLint address = glGetAttribLocation(m_shaderProgramInfo.shaderProgramHandle, varName); const GLInputLocation inputLocation(address); - if (inputLocation == GLInputLocationInvalid) + if (!inputLocation.isValid()) { LOG_WARN(CONTEXT_RENDERER, "ShaderGPUResource_GL::loadAttributeLocation: glGetAttribLocation for effect '" << effect.getName() << "' for attribute '" << varName << "' failed"); } @@ -115,7 +114,7 @@ namespace ramses_internal const char* varName = input.inputName.c_str(); const GLint address = glGetUniformLocation(m_shaderProgramInfo.shaderProgramHandle, varName); const GLInputLocation inputLocation(address); - if (inputLocation == GLInputLocationInvalid) + if (!inputLocation.isValid()) { LOG_WARN(CONTEXT_RENDERER, "ShaderGPUResource_GL::loadUniformLocation: for effect '" << effect.getName() << "' for uniform '" << varName << "' failed"); } @@ -123,7 +122,7 @@ namespace ramses_internal return inputLocation; } - bool ShaderGPUResource_GL::getBinaryInfo(UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) const + bool ShaderGPUResource_GL::getBinaryInfo(std::vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) const { GLint sizeInBytes = -1; glGetProgramiv(m_shaderProgramInfo.shaderProgramHandle, GL_PROGRAM_BINARY_LENGTH, &sizeInBytes); @@ -134,7 +133,7 @@ namespace ramses_internal return false; } - GLenum binaryFormat; + GLenum binaryFormat = 0; GLsizei actualSize = 0; binaryShader.resize(static_cast(sizeInBytes)); diff --git a/renderer/Platform/Device_GL/include/Device_GL/ShaderGPUResource_GL.h b/src/renderer/internal/Platform/OpenGL/ShaderGPUResource_GL.h similarity index 75% rename from renderer/Platform/Device_GL/include/Device_GL/ShaderGPUResource_GL.h rename to src/renderer/internal/Platform/OpenGL/ShaderGPUResource_GL.h index 91d645090..122f3e076 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/ShaderGPUResource_GL.h +++ b/src/renderer/internal/Platform/OpenGL/ShaderGPUResource_GL.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHADERGPURESOURCE_GL_H -#define RAMSES_SHADERGPURESOURCE_GL_H +#pragma once -#include "Platform_Base/ShaderGPUResource.h" -#include "Device_GL/ShaderProgramInfo.h" -#include "Resource/EffectInputInformation.h" +#include "internal/RendererLib/PlatformBase/ShaderGPUResource.h" +#include "internal/Platform/OpenGL/ShaderProgramInfo.h" +#include "internal/SceneGraph/Resource/EffectInputInformation.h" -namespace ramses_internal +namespace ramses::internal { class EffectResource; @@ -21,25 +20,24 @@ namespace ramses_internal { explicit TextureSlotInfo(TextureSlot slotInit = 0u) : slot(slotInit) - , textureType(EEffectInputTextureType_Invalid) { } TextureSlot slot; - EEffectInputTextureType textureType; + EEffectInputTextureType textureType{EEffectInputTextureType_Invalid}; }; - class ShaderGPUResource_GL : public ShaderGPUResource + class ShaderGPUResource_GL final : public ShaderGPUResource { public: ShaderGPUResource_GL(const EffectResource& effect, ShaderProgramInfo shaderProgramInfo); ~ShaderGPUResource_GL() override; - [[nodiscard]] GLInputLocation getUniformLocation(DataFieldHandle) const; - [[nodiscard]] GLInputLocation getAttributeLocation(DataFieldHandle) const; - [[nodiscard]] TextureSlotInfo getTextureSlot(DataFieldHandle) const; + [[nodiscard]] GLInputLocation getUniformLocation(DataFieldHandle field) const; + [[nodiscard]] GLInputLocation getAttributeLocation(DataFieldHandle field) const; + [[nodiscard]] TextureSlotInfo getTextureSlot(DataFieldHandle field) const; - bool getBinaryInfo(UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) const; + bool getBinaryInfo(std::vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) const; private: void preloadVariableLocations(const EffectResource& effect); @@ -56,5 +54,3 @@ namespace ramses_internal InputLocationMap m_attributeLocationMap; }; } - -#endif diff --git a/renderer/Platform/Device_GL/include/Device_GL/ShaderProgramInfo.h b/src/renderer/internal/Platform/OpenGL/ShaderProgramInfo.h similarity index 83% rename from renderer/Platform/Device_GL/include/Device_GL/ShaderProgramInfo.h rename to src/renderer/internal/Platform/OpenGL/ShaderProgramInfo.h index 83601ccb1..950021632 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/ShaderProgramInfo.h +++ b/src/renderer/internal/Platform/OpenGL/ShaderProgramInfo.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHADERPROGRAMINFO_H -#define RAMSES_SHADERPROGRAMINFO_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Device_GL/Types_GL.h" +#include "internal/Platform/OpenGL/Types_GL.h" -namespace ramses_internal +#include + +namespace ramses::internal { struct ShaderProgramInfo { @@ -30,5 +30,3 @@ namespace ramses_internal GLHandle geometryShaderHandle; }; } - -#endif diff --git a/renderer/Platform/Device_GL/src/ShaderUploader_GL.cpp b/src/renderer/internal/Platform/OpenGL/ShaderUploader_GL.cpp similarity index 69% rename from renderer/Platform/Device_GL/src/ShaderUploader_GL.cpp rename to src/renderer/internal/Platform/OpenGL/ShaderUploader_GL.cpp index 066a48067..998759483 100644 --- a/renderer/Platform/Device_GL/src/ShaderUploader_GL.cpp +++ b/src/renderer/internal/Platform/OpenGL/ShaderUploader_GL.cpp @@ -6,16 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_GL/Device_GL_platform.h" -#include "Device_GL/ShaderUploader_GL.h" +#include "internal/Platform/OpenGL/Device_GL_platform.h" +#include "internal/Platform/OpenGL/ShaderUploader_GL.h" -#include "Resource/EffectResource.h" -#include "Device_GL/ShaderProgramInfo.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Platform/OpenGL/ShaderProgramInfo.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "absl/strings/str_split.h" -namespace ramses_internal +namespace ramses::internal { - bool ShaderUploader_GL::UploadShaderProgramFromBinary(const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat, ShaderProgramInfo& programShaderInfoOut, std::string& debugErrorLog) + bool ShaderUploader_GL::UploadShaderProgramFromBinary(const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat, ShaderProgramInfo& programShaderInfoOut, std::string& debugErrorLog) { LOG_TRACE(CONTEXT_RENDERER, "ShaderUploader_GL::UploadShaderProgramFromBinary: uploading binary data"); @@ -26,18 +27,15 @@ namespace ramses_internal return false; } - glProgramBinary(programHandle, binaryShaderFormat.getValue(), binaryShaderData, binaryShaderDataSize); + glProgramBinary(programHandle, binaryShaderFormat.getValue(), binaryShaderData, static_cast(binaryShaderDataSize)); if (CheckShaderProgramLinkStatus(programHandle, debugErrorLog)) { programShaderInfoOut.shaderProgramHandle = programHandle; return true; } - else - { - glDeleteProgram(programHandle); - return false; - } + glDeleteProgram(programHandle); + return false; } @@ -99,9 +97,6 @@ namespace ramses_internal glAttachShader(shaderProgramHandle, geometryShaderHandle); glLinkProgram(shaderProgramHandle); - GLint linkStatus; - glGetProgramiv(shaderProgramHandle, GL_LINK_STATUS, &linkStatus); - if (CheckShaderProgramLinkStatus(shaderProgramHandle, debugErrorLog)) { programShaderInfoOut.vertexShaderHandle = vertexShaderHandle; @@ -110,34 +105,34 @@ namespace ramses_internal programShaderInfoOut.geometryShaderHandle = geometryShaderHandle; return true; } - else - { - LOG_ERROR(CONTEXT_RENDERER, "ShaderUploader_GL::UploadShaderProgramFromSource: CheckShaderProgramLinkStatus failed"); - glDeleteProgram(shaderProgramHandle); - glDeleteShader(vertexShaderHandle); - glDeleteShader(fragmentShaderHandle); - if (hasGeometryShader) - glDeleteShader(geometryShaderHandle); - return false; - } + LOG_ERROR(CONTEXT_RENDERER, "ShaderUploader_GL::UploadShaderProgramFromSource: CheckShaderProgramLinkStatus failed"); + glDeleteProgram(shaderProgramHandle); + glDeleteShader(vertexShaderHandle); + glDeleteShader(fragmentShaderHandle); + if (hasGeometryShader) + glDeleteShader(geometryShaderHandle); + return false; } bool ShaderUploader_GL::CheckShaderProgramLinkStatus(GLHandle shaderProgram, std::string& errorLogOut) { - GLint linkStatus; + GLint linkStatus = GL_FALSE; glGetProgramiv(shaderProgram, GL_LINK_STATUS, &linkStatus); if (GL_FALSE == linkStatus) { - int32_t infoLength; - glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &infoLength); - if (infoLength > 0) + GLint charBufferSize = 0; + glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &charBufferSize); + if (charBufferSize > 0) { std::string str; - str.resize(infoLength); + // charBufferSize includes null termination character and data() returns array which is null-terminated + str.resize(charBufferSize - 1); - int32_t numChars; - glGetProgramInfoLog(shaderProgram, infoLength, &numChars, str.data()); + GLsizei numChars = 0; + glGetProgramInfoLog(shaderProgram, charBufferSize, &numChars, str.data()); + // Might be useful for the case when charBufferSize reported by glGetProgramiv is bigger than the real data + // returned by glGetProgramInfoLog. Does not affect performance, it is a case of error handling. str.resize(numChars); errorLogOut = "Failed to link shader program:\n"; @@ -145,14 +140,13 @@ namespace ramses_internal errorLogOut += "\n"; } else + { errorLogOut = "Failed to link shader program - no info given from compiler\n"; + } return false; } - else - { - return true; - } + return true; } GLHandle ShaderUploader_GL::CompileShaderStage(const char* stageSource, GLenum shaderType, std::string& errorLogOut) @@ -169,15 +163,26 @@ namespace ramses_internal if (compilationResult == GL_FALSE) { - int32_t infoLength; - int32_t numberChars; - glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &infoLength); + std::string info; + + GLint charBufferSize = 0; + glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &charBufferSize); + if (charBufferSize > 0) + { + // charBufferSize includes null termination character and data() returns array which is null-terminated + info.resize(charBufferSize - 1); + GLsizei numberChars = 0; + glGetShaderInfoLog(shaderHandle, charBufferSize, &numberChars, info.data()); + // Might be useful for the case when charBufferSize reported by glGetShaderiv is bigger than the real data + // returned by glGetShaderInfoLog. Does not affect performance, it is a case of error handling. + info.resize(numberChars); + } + else + { + info = "no info given from compiler"; + } - // Allocate Log Space - char* info = new char[infoLength]; - glGetShaderInfoLog(shaderHandle, infoLength, &numberChars, info); errorLogOut = std::string("Unable to compile shader stage: ") + info; - delete[] info; PrintShaderSourceWithLineNumbers(stageSource); glDeleteShader(shaderHandle); @@ -191,15 +196,9 @@ namespace ramses_internal void ShaderUploader_GL::PrintShaderSourceWithLineNumbers(std::string_view source) { uint32_t lineNumber = 1; - Int prevNewLine = 0; - auto nextNewLine = source.find('\n'); - - while (nextNewLine != std::string_view::npos) + for (const auto& line : absl::StrSplit(source, '\n')) { - LOG_ERROR(CONTEXT_RENDERER, "Device_Base::PrintShaderSourceWithLineNumbers: L" << lineNumber << ": " << source.substr(prevNewLine, nextNewLine - prevNewLine)); - - prevNewLine = nextNewLine + 1; - nextNewLine = source.find('\n', prevNewLine); + LOG_ERROR(CONTEXT_RENDERER, "Device_Base::PrintShaderSourceWithLineNumbers: L" << lineNumber << ": " << line); ++lineNumber; } } diff --git a/renderer/Platform/Device_GL/include/Device_GL/ShaderUploader_GL.h b/src/renderer/internal/Platform/OpenGL/ShaderUploader_GL.h similarity index 75% rename from renderer/Platform/Device_GL/include/Device_GL/ShaderUploader_GL.h rename to src/renderer/internal/Platform/OpenGL/ShaderUploader_GL.h index 4a86a2ce8..5c62a9add 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/ShaderUploader_GL.h +++ b/src/renderer/internal/Platform/OpenGL/ShaderUploader_GL.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHADERUPLOADER_GL_H -#define RAMSES_SHADERUPLOADER_GL_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" #include "Types_GL.h" #include #include -namespace ramses_internal +namespace ramses::internal { struct ShaderProgramInfo; class EffectResource; @@ -24,7 +23,7 @@ namespace ramses_internal { public: static bool UploadShaderProgramFromSource(const EffectResource& effect, ShaderProgramInfo& programShaderInfoOut, std::string& debugErrorLog); - static bool UploadShaderProgramFromBinary(const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat, ShaderProgramInfo& programShaderInfoOut, std::string& debugErrorLog); + static bool UploadShaderProgramFromBinary(const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat, ShaderProgramInfo& programShaderInfoOut, std::string& debugErrorLog); private: static GLHandle CompileShaderStage(const char* stageSource, GLenum shaderType, std::string& errorLogOut); @@ -32,5 +31,3 @@ namespace ramses_internal static void PrintShaderSourceWithLineNumbers(std::string_view source); }; } - -#endif diff --git a/renderer/Platform/Device_GL/src/TypesConversion_GL.cpp b/src/renderer/internal/Platform/OpenGL/TypesConversion_GL.cpp similarity index 62% rename from renderer/Platform/Device_GL/src/TypesConversion_GL.cpp rename to src/renderer/internal/Platform/OpenGL/TypesConversion_GL.cpp index f4c263cba..46898999c 100644 --- a/renderer/Platform/Device_GL/src/TypesConversion_GL.cpp +++ b/src/renderer/internal/Platform/OpenGL/TypesConversion_GL.cpp @@ -6,228 +6,190 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Device_GL/Device_GL_platform.h" -#include "Device_GL/TypesConversion_GL.h" -#include "PlatformAbstraction/Macros.h" +#include "internal/Platform/OpenGL/Device_GL_platform.h" +#include "internal/Platform/OpenGL/TypesConversion_GL.h" +#include "internal/PlatformAbstraction/Macros.h" -namespace ramses_internal +namespace ramses::internal { - TextureUploadParams_GL TypesConversion_GL::GetTextureUploadParams(ETextureFormat format) + TextureUploadParams_GL TypesConversion_GL::GetTextureUploadParams(EPixelStorageFormat format) { TextureUploadParams_GL uploadParams; switch (format) { - case ETextureFormat::RGBA4: + case EPixelStorageFormat::RGBA4: uploadParams.sizedInternalFormat = GL_RGBA4; uploadParams.baseInternalFormat = GL_RGBA; uploadParams.type = GL_UNSIGNED_SHORT_4_4_4_4; - uploadParams.byteAlignment = 2u; + uploadParams.byteAlignment = 2; break; - case ETextureFormat::RGB8: + case EPixelStorageFormat::RGB8: uploadParams.sizedInternalFormat = GL_RGB8; uploadParams.baseInternalFormat = GL_RGB; uploadParams.type = GL_UNSIGNED_BYTE; - uploadParams.byteAlignment = 1u; + uploadParams.byteAlignment = 1; break; - case ETextureFormat::RGBA8: + case EPixelStorageFormat::RGBA8: uploadParams.sizedInternalFormat = GL_RGBA8; uploadParams.baseInternalFormat = GL_RGBA; uploadParams.type = GL_UNSIGNED_BYTE; - uploadParams.byteAlignment = 4u; - break; - case ETextureFormat::RGB16: - uploadParams.sizedInternalFormat = GL_RGB16UI; - uploadParams.baseInternalFormat = GL_RGB; - uploadParams.type = GL_UNSIGNED_SHORT; - uploadParams.byteAlignment = 2u; - break; - case ETextureFormat::RGBA16: - uploadParams.sizedInternalFormat = GL_RGBA16UI; - uploadParams.baseInternalFormat = GL_RGBA; - uploadParams.type = GL_UNSIGNED_SHORT; - uploadParams.byteAlignment = 8u; + uploadParams.byteAlignment = 4; break; - case ETextureFormat::RGBA5551: + case EPixelStorageFormat::RGBA5551: uploadParams.sizedInternalFormat = GL_RGB5_A1; uploadParams.baseInternalFormat = GL_RGBA; uploadParams.type = GL_UNSIGNED_SHORT_5_5_5_1; - uploadParams.byteAlignment = 2u; + uploadParams.byteAlignment = 2; break; - case ETextureFormat::RGB565: + case EPixelStorageFormat::RGB565: uploadParams.sizedInternalFormat = GL_RGB565; uploadParams.baseInternalFormat = GL_RGB; uploadParams.type = GL_UNSIGNED_SHORT_5_6_5; - uploadParams.byteAlignment = 2u; - break; - case ETextureFormat::DXT1RGB: // GL_EXT_texture_compression_dxt1 - uploadParams.compressed = true; - uploadParams.sizedInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - uploadParams.baseInternalFormat = GL_RGB; - uploadParams.byteAlignment = 8u; - break; - case ETextureFormat::DXT3RGBA: - // DXT3 (extension) - uploadParams.compressed = true; - uploadParams.sizedInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - uploadParams.baseInternalFormat = GL_RGBA; - uploadParams.byteAlignment = 8u; // actually 16 bytes but glPixelStore accepts maximum 8 bytes alignment - break; - case ETextureFormat::DXT5RGBA: - // DXT5 (extension) - uploadParams.compressed = true; - uploadParams.sizedInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - uploadParams.baseInternalFormat = GL_RGBA; - uploadParams.byteAlignment = 8u; // actually 16 bytes but glPixelStore accepts maximum 8 bytes alignment + uploadParams.byteAlignment = 2; break; - case ETextureFormat::R8: + case EPixelStorageFormat::R8: uploadParams.sizedInternalFormat = GL_R8; uploadParams.baseInternalFormat = GL_RED; uploadParams.type = GL_UNSIGNED_BYTE; - uploadParams.byteAlignment = 1u; + uploadParams.byteAlignment = 1; break; - case ETextureFormat::RG8: + case EPixelStorageFormat::RG8: uploadParams.sizedInternalFormat = GL_RG8; uploadParams.baseInternalFormat = GL_RG; uploadParams.type = GL_UNSIGNED_BYTE; - uploadParams.byteAlignment = 2u; - break; - case ETextureFormat::R16: - uploadParams.sizedInternalFormat = GL_R16UI; - uploadParams.baseInternalFormat = GL_RED; - uploadParams.type = GL_UNSIGNED_SHORT; - uploadParams.byteAlignment = 2u; - break; - case ETextureFormat::RG16: - uploadParams.sizedInternalFormat = GL_RG16UI; - uploadParams.baseInternalFormat = GL_RG; - uploadParams.type = GL_UNSIGNED_SHORT; - uploadParams.byteAlignment = 4u; + uploadParams.byteAlignment = 2; break; - case ETextureFormat::R16F: + case EPixelStorageFormat::R16F: uploadParams.sizedInternalFormat = GL_R16F; uploadParams.baseInternalFormat = GL_RED; uploadParams.type = GL_HALF_FLOAT; - uploadParams.byteAlignment = 2u; + uploadParams.byteAlignment = 2; break; - case ETextureFormat::R32F: + case EPixelStorageFormat::R32F: uploadParams.sizedInternalFormat = GL_R32F; uploadParams.baseInternalFormat = GL_RED; uploadParams.type = GL_FLOAT; - uploadParams.byteAlignment = 4u; + uploadParams.byteAlignment = 4; break; - case ETextureFormat::RG16F: + case EPixelStorageFormat::RG16F: uploadParams.sizedInternalFormat = GL_RG16F; uploadParams.baseInternalFormat = GL_RG; uploadParams.type = GL_HALF_FLOAT; - uploadParams.byteAlignment = 4u; + uploadParams.byteAlignment = 4; break; - case ETextureFormat::RG32F: + case EPixelStorageFormat::RG32F: uploadParams.sizedInternalFormat = GL_RG32F; uploadParams.baseInternalFormat = GL_RG; uploadParams.type = GL_FLOAT; - uploadParams.byteAlignment = 8u; + uploadParams.byteAlignment = 8; break; - case ETextureFormat::RGB16F: + case EPixelStorageFormat::RGB16F: uploadParams.sizedInternalFormat = GL_RGB16F; uploadParams.baseInternalFormat = GL_RGB; uploadParams.type = GL_HALF_FLOAT; - uploadParams.byteAlignment = 2u; + uploadParams.byteAlignment = 2; break; - case ETextureFormat::RGB32F: + case EPixelStorageFormat::RGB32F: uploadParams.sizedInternalFormat = GL_RGB32F; uploadParams.baseInternalFormat = GL_RGB; uploadParams.type = GL_FLOAT; - uploadParams.byteAlignment = 4u; + uploadParams.byteAlignment = 4; break; - case ETextureFormat::RGBA16F: + case EPixelStorageFormat::RGBA16F: uploadParams.sizedInternalFormat = GL_RGBA16F; uploadParams.baseInternalFormat = GL_RGBA; uploadParams.type = GL_HALF_FLOAT; - uploadParams.byteAlignment = 8u; + uploadParams.byteAlignment = 8; break; - case ETextureFormat::RGBA32F: + case EPixelStorageFormat::RGBA32F: uploadParams.sizedInternalFormat = GL_RGBA32F; uploadParams.baseInternalFormat = GL_RGBA; uploadParams.type = GL_FLOAT; - uploadParams.byteAlignment = 8u; // actually 16 bytes but glPixelStore accepts maximum 8 bytes alignment + uploadParams.byteAlignment = 8; // actually 16 bytes but glPixelStore accepts maximum 8 bytes alignment break; - case ETextureFormat::SRGB8: + case EPixelStorageFormat::SRGB8: uploadParams.sizedInternalFormat = GL_SRGB8; uploadParams.baseInternalFormat = GL_RGB; uploadParams.type = GL_UNSIGNED_BYTE; - uploadParams.byteAlignment = 1u; + uploadParams.byteAlignment = 1; break; - case ETextureFormat::SRGB8_ALPHA8: + case EPixelStorageFormat::SRGB8_ALPHA8: uploadParams.sizedInternalFormat = GL_SRGB8_ALPHA8; uploadParams.baseInternalFormat = GL_RGBA; uploadParams.type = GL_UNSIGNED_BYTE; - uploadParams.byteAlignment = 4u; + uploadParams.byteAlignment = 4; break; - case ETextureFormat::ETC2RGB: + case EPixelStorageFormat::ETC2RGB: uploadParams.compressed = true; uploadParams.sizedInternalFormat = GL_COMPRESSED_RGB8_ETC2; uploadParams.baseInternalFormat = GL_RGB; - uploadParams.byteAlignment = 8u; + uploadParams.byteAlignment = 8; break; - case ETextureFormat::ETC2RGBA: + case EPixelStorageFormat::ETC2RGBA: uploadParams.compressed = true; uploadParams.sizedInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; uploadParams.baseInternalFormat = GL_RGBA; - uploadParams.byteAlignment = 8u; - break; - // TODO Violin remove redundant list of ASTC enums from this switch and below GetASTCFormat() method - case ETextureFormat::ASTC_RGBA_4x4: - case ETextureFormat::ASTC_RGBA_5x4: - case ETextureFormat::ASTC_RGBA_5x5: - case ETextureFormat::ASTC_RGBA_6x5: - case ETextureFormat::ASTC_RGBA_6x6: - case ETextureFormat::ASTC_RGBA_8x5: - case ETextureFormat::ASTC_RGBA_8x6: - case ETextureFormat::ASTC_RGBA_8x8: - case ETextureFormat::ASTC_RGBA_10x5: - case ETextureFormat::ASTC_RGBA_10x6: - case ETextureFormat::ASTC_RGBA_10x8: - case ETextureFormat::ASTC_RGBA_10x10: - case ETextureFormat::ASTC_RGBA_12x10: - case ETextureFormat::ASTC_RGBA_12x12: - case ETextureFormat::ASTC_SRGBA_4x4: - case ETextureFormat::ASTC_SRGBA_5x4: - case ETextureFormat::ASTC_SRGBA_5x5: - case ETextureFormat::ASTC_SRGBA_6x5: - case ETextureFormat::ASTC_SRGBA_6x6: - case ETextureFormat::ASTC_SRGBA_8x5: - case ETextureFormat::ASTC_SRGBA_8x6: - case ETextureFormat::ASTC_SRGBA_8x8: - case ETextureFormat::ASTC_SRGBA_10x5: - case ETextureFormat::ASTC_SRGBA_10x6: - case ETextureFormat::ASTC_SRGBA_10x8: - case ETextureFormat::ASTC_SRGBA_10x10: - case ETextureFormat::ASTC_SRGBA_12x10: - case ETextureFormat::ASTC_SRGBA_12x12: + uploadParams.byteAlignment = 8; + break; + case EPixelStorageFormat::ASTC_RGBA_4x4: + case EPixelStorageFormat::ASTC_RGBA_5x4: + case EPixelStorageFormat::ASTC_RGBA_5x5: + case EPixelStorageFormat::ASTC_RGBA_6x5: + case EPixelStorageFormat::ASTC_RGBA_6x6: + case EPixelStorageFormat::ASTC_RGBA_8x5: + case EPixelStorageFormat::ASTC_RGBA_8x6: + case EPixelStorageFormat::ASTC_RGBA_8x8: + case EPixelStorageFormat::ASTC_RGBA_10x5: + case EPixelStorageFormat::ASTC_RGBA_10x6: + case EPixelStorageFormat::ASTC_RGBA_10x8: + case EPixelStorageFormat::ASTC_RGBA_10x10: + case EPixelStorageFormat::ASTC_RGBA_12x10: + case EPixelStorageFormat::ASTC_RGBA_12x12: + case EPixelStorageFormat::ASTC_SRGBA_4x4: + case EPixelStorageFormat::ASTC_SRGBA_5x4: + case EPixelStorageFormat::ASTC_SRGBA_5x5: + case EPixelStorageFormat::ASTC_SRGBA_6x5: + case EPixelStorageFormat::ASTC_SRGBA_6x6: + case EPixelStorageFormat::ASTC_SRGBA_8x5: + case EPixelStorageFormat::ASTC_SRGBA_8x6: + case EPixelStorageFormat::ASTC_SRGBA_8x8: + case EPixelStorageFormat::ASTC_SRGBA_10x5: + case EPixelStorageFormat::ASTC_SRGBA_10x6: + case EPixelStorageFormat::ASTC_SRGBA_10x8: + case EPixelStorageFormat::ASTC_SRGBA_10x10: + case EPixelStorageFormat::ASTC_SRGBA_12x10: + case EPixelStorageFormat::ASTC_SRGBA_12x12: uploadParams.compressed = true; uploadParams.sizedInternalFormat = GetASTCFormat(format); uploadParams.baseInternalFormat = GL_RGBA; // not used, but to be consistent with the other formats - uploadParams.byteAlignment = 8u; // actually 16 bytes but glPixelStore accepts maximum 8 bytes alignment + uploadParams.byteAlignment = 8; // actually 16 bytes but glPixelStore accepts maximum 8 bytes alignment break; - case ETextureFormat::Depth16: + + case EPixelStorageFormat::Depth16: uploadParams.sizedInternalFormat = GL_DEPTH_COMPONENT16; uploadParams.baseInternalFormat = GL_DEPTH_COMPONENT; - uploadParams.byteAlignment = 2u; + uploadParams.byteAlignment = 2; break; - case ETextureFormat::Depth24: + case EPixelStorageFormat::Depth24: uploadParams.sizedInternalFormat = GL_DEPTH_COMPONENT24; uploadParams.baseInternalFormat = GL_DEPTH_COMPONENT; - uploadParams.byteAlignment = 1u; + uploadParams.byteAlignment = 1; + break; + case EPixelStorageFormat::Depth32: + uploadParams.sizedInternalFormat = GL_DEPTH_COMPONENT32F; + uploadParams.baseInternalFormat = GL_DEPTH_COMPONENT; + uploadParams.byteAlignment = 4; break; - case ETextureFormat::Depth24_Stencil8: + case EPixelStorageFormat::Depth24_Stencil8: uploadParams.sizedInternalFormat = GL_DEPTH24_STENCIL8; uploadParams.baseInternalFormat = GL_DEPTH_STENCIL; - uploadParams.byteAlignment = 4u; + uploadParams.byteAlignment = 4; break; - default: + + case EPixelStorageFormat::Invalid: assert(false); break; } @@ -353,15 +315,15 @@ namespace ramses_internal } } - GLenum TypesConversion_GL::GetWrapMode(EWrapMethod wrapMode) + GLint TypesConversion_GL::GetWrapMode(ETextureAddressMode wrapMode) { switch (wrapMode) { - case EWrapMethod::Repeat: + case ETextureAddressMode::Repeat: return GL_REPEAT; - case EWrapMethod::Clamp: + case ETextureAddressMode::Clamp: return GL_CLAMP_TO_EDGE; - case EWrapMethod::RepeatMirrored: + case ETextureAddressMode::Mirror: return GL_MIRRORED_REPEAT; default: assert(false && "Invalid wrap mode"); @@ -464,83 +426,82 @@ namespace ramses_internal { switch (face) { - case ETextureCubeFace_PositiveX: + case ETextureCubeFace::PositiveX: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; - case ETextureCubeFace_NegativeX: + case ETextureCubeFace::NegativeX: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; - case ETextureCubeFace_PositiveY: + case ETextureCubeFace::PositiveY: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; - case ETextureCubeFace_NegativeY: + case ETextureCubeFace::NegativeY: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; - case ETextureCubeFace_PositiveZ: + case ETextureCubeFace::PositiveZ: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; - case ETextureCubeFace_NegativeZ: + case ETextureCubeFace::NegativeZ: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; - default: - assert(false); - return 0; } + assert(false); + return 0; } - GLenum TypesConversion_GL::GetASTCFormat(ETextureFormat ramsesEnum) + GLint TypesConversion_GL::GetASTCFormat(EPixelStorageFormat ramsesEnum) { switch (ramsesEnum) { - case ETextureFormat::ASTC_RGBA_4x4: + case EPixelStorageFormat::ASTC_RGBA_4x4: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; - case ETextureFormat::ASTC_RGBA_5x4: + case EPixelStorageFormat::ASTC_RGBA_5x4: return GL_COMPRESSED_RGBA_ASTC_5x4_KHR; - case ETextureFormat::ASTC_RGBA_5x5: + case EPixelStorageFormat::ASTC_RGBA_5x5: return GL_COMPRESSED_RGBA_ASTC_5x5_KHR; - case ETextureFormat::ASTC_RGBA_6x5: + case EPixelStorageFormat::ASTC_RGBA_6x5: return GL_COMPRESSED_RGBA_ASTC_6x5_KHR; - case ETextureFormat::ASTC_RGBA_6x6: + case EPixelStorageFormat::ASTC_RGBA_6x6: return GL_COMPRESSED_RGBA_ASTC_6x6_KHR; - case ETextureFormat::ASTC_RGBA_8x5: + case EPixelStorageFormat::ASTC_RGBA_8x5: return GL_COMPRESSED_RGBA_ASTC_8x5_KHR; - case ETextureFormat::ASTC_RGBA_8x6: + case EPixelStorageFormat::ASTC_RGBA_8x6: return GL_COMPRESSED_RGBA_ASTC_8x6_KHR; - case ETextureFormat::ASTC_RGBA_8x8: + case EPixelStorageFormat::ASTC_RGBA_8x8: return GL_COMPRESSED_RGBA_ASTC_8x8_KHR; - case ETextureFormat::ASTC_RGBA_10x5: + case EPixelStorageFormat::ASTC_RGBA_10x5: return GL_COMPRESSED_RGBA_ASTC_10x5_KHR; - case ETextureFormat::ASTC_RGBA_10x6: + case EPixelStorageFormat::ASTC_RGBA_10x6: return GL_COMPRESSED_RGBA_ASTC_10x6_KHR; - case ETextureFormat::ASTC_RGBA_10x8: + case EPixelStorageFormat::ASTC_RGBA_10x8: return GL_COMPRESSED_RGBA_ASTC_10x8_KHR; - case ETextureFormat::ASTC_RGBA_10x10: + case EPixelStorageFormat::ASTC_RGBA_10x10: return GL_COMPRESSED_RGBA_ASTC_10x10_KHR; - case ETextureFormat::ASTC_RGBA_12x10: + case EPixelStorageFormat::ASTC_RGBA_12x10: return GL_COMPRESSED_RGBA_ASTC_12x10_KHR; - case ETextureFormat::ASTC_RGBA_12x12: + case EPixelStorageFormat::ASTC_RGBA_12x12: return GL_COMPRESSED_RGBA_ASTC_12x12_KHR; - case ETextureFormat::ASTC_SRGBA_4x4: + case EPixelStorageFormat::ASTC_SRGBA_4x4: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR; - case ETextureFormat::ASTC_SRGBA_5x4: + case EPixelStorageFormat::ASTC_SRGBA_5x4: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR; - case ETextureFormat::ASTC_SRGBA_5x5: + case EPixelStorageFormat::ASTC_SRGBA_5x5: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR; - case ETextureFormat::ASTC_SRGBA_6x5: + case EPixelStorageFormat::ASTC_SRGBA_6x5: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR; - case ETextureFormat::ASTC_SRGBA_6x6: + case EPixelStorageFormat::ASTC_SRGBA_6x6: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR; - case ETextureFormat::ASTC_SRGBA_8x5: + case EPixelStorageFormat::ASTC_SRGBA_8x5: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR; - case ETextureFormat::ASTC_SRGBA_8x6: + case EPixelStorageFormat::ASTC_SRGBA_8x6: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR; - case ETextureFormat::ASTC_SRGBA_8x8: + case EPixelStorageFormat::ASTC_SRGBA_8x8: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR; - case ETextureFormat::ASTC_SRGBA_10x5: + case EPixelStorageFormat::ASTC_SRGBA_10x5: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR; - case ETextureFormat::ASTC_SRGBA_10x6: + case EPixelStorageFormat::ASTC_SRGBA_10x6: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR; - case ETextureFormat::ASTC_SRGBA_10x8: + case EPixelStorageFormat::ASTC_SRGBA_10x8: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR; - case ETextureFormat::ASTC_SRGBA_10x10: + case EPixelStorageFormat::ASTC_SRGBA_10x10: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR; - case ETextureFormat::ASTC_SRGBA_12x10: + case EPixelStorageFormat::ASTC_SRGBA_12x10: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR; - case ETextureFormat::ASTC_SRGBA_12x12: + case EPixelStorageFormat::ASTC_SRGBA_12x12: return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR; default: assert(false && "Using unsupported enum in GetASTCFormat()"); @@ -548,82 +509,80 @@ namespace ramses_internal } } - ETextureFormat TypesConversion_GL::GetTextureFormatFromCompressedGLTextureFormat(GLenum compressedGLTextureFormat) + EPixelStorageFormat TypesConversion_GL::GetTextureFormatFromCompressedGLTextureFormat(GLenum compressedGLTextureFormat) { switch (compressedGLTextureFormat) { - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - return ETextureFormat::DXT1RGB; - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - return ETextureFormat::DXT3RGBA; - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - return ETextureFormat::DXT5RGBA; case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: - return ETextureFormat::ASTC_RGBA_4x4; + return EPixelStorageFormat::ASTC_RGBA_4x4; case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: - return ETextureFormat::ASTC_RGBA_5x4; + return EPixelStorageFormat::ASTC_RGBA_5x4; case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: - return ETextureFormat::ASTC_RGBA_5x5; + return EPixelStorageFormat::ASTC_RGBA_5x5; case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: - return ETextureFormat::ASTC_RGBA_6x5; + return EPixelStorageFormat::ASTC_RGBA_6x5; case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: - return ETextureFormat::ASTC_RGBA_6x6; + return EPixelStorageFormat::ASTC_RGBA_6x6; case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: - return ETextureFormat::ASTC_RGBA_8x5; + return EPixelStorageFormat::ASTC_RGBA_8x5; case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: - return ETextureFormat::ASTC_RGBA_8x6; + return EPixelStorageFormat::ASTC_RGBA_8x6; case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: - return ETextureFormat::ASTC_RGBA_8x8; + return EPixelStorageFormat::ASTC_RGBA_8x8; case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: - return ETextureFormat::ASTC_RGBA_10x5; + return EPixelStorageFormat::ASTC_RGBA_10x5; case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: - return ETextureFormat::ASTC_RGBA_10x6; + return EPixelStorageFormat::ASTC_RGBA_10x6; case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: - return ETextureFormat::ASTC_RGBA_10x8; + return EPixelStorageFormat::ASTC_RGBA_10x8; case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: - return ETextureFormat::ASTC_RGBA_10x10; + return EPixelStorageFormat::ASTC_RGBA_10x10; case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: - return ETextureFormat::ASTC_RGBA_12x10; + return EPixelStorageFormat::ASTC_RGBA_12x10; case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: - return ETextureFormat::ASTC_RGBA_12x12; + return EPixelStorageFormat::ASTC_RGBA_12x12; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: - return ETextureFormat::ASTC_SRGBA_4x4; + return EPixelStorageFormat::ASTC_SRGBA_4x4; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: - return ETextureFormat::ASTC_SRGBA_5x4; + return EPixelStorageFormat::ASTC_SRGBA_5x4; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: - return ETextureFormat::ASTC_SRGBA_5x5; + return EPixelStorageFormat::ASTC_SRGBA_5x5; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: - return ETextureFormat::ASTC_SRGBA_6x5; + return EPixelStorageFormat::ASTC_SRGBA_6x5; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: - return ETextureFormat::ASTC_SRGBA_6x6; + return EPixelStorageFormat::ASTC_SRGBA_6x6; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: - return ETextureFormat::ASTC_SRGBA_8x5; + return EPixelStorageFormat::ASTC_SRGBA_8x5; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: - return ETextureFormat::ASTC_SRGBA_8x6; + return EPixelStorageFormat::ASTC_SRGBA_8x6; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: - return ETextureFormat::ASTC_SRGBA_8x8; + return EPixelStorageFormat::ASTC_SRGBA_8x8; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: - return ETextureFormat::ASTC_SRGBA_10x5; + return EPixelStorageFormat::ASTC_SRGBA_10x5; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: - return ETextureFormat::ASTC_SRGBA_10x6; + return EPixelStorageFormat::ASTC_SRGBA_10x6; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: - return ETextureFormat::ASTC_SRGBA_10x8; + return EPixelStorageFormat::ASTC_SRGBA_10x8; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: - return ETextureFormat::ASTC_SRGBA_10x10; + return EPixelStorageFormat::ASTC_SRGBA_10x10; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: - return ETextureFormat::ASTC_SRGBA_12x10; + return EPixelStorageFormat::ASTC_SRGBA_12x10; case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: - return ETextureFormat::ASTC_SRGBA_12x12; + return EPixelStorageFormat::ASTC_SRGBA_12x12; case GL_COMPRESSED_RGB8_ETC2: - return ETextureFormat::ETC2RGB; + return EPixelStorageFormat::ETC2RGB; case GL_COMPRESSED_RGBA8_ETC2_EAC: - return ETextureFormat::ETC2RGBA; + return EPixelStorageFormat::ETC2RGBA; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + // not supported by ramses default: - return ETextureFormat::Invalid; + return EPixelStorageFormat::Invalid; } } - GLenum TypesConversion_GL::GetGlColorFromTextureChannelColor(ETextureChannelColor color) + GLint TypesConversion_GL::GetGlColorFromTextureChannelColor(ETextureChannelColor color) { switch (color) { @@ -639,8 +598,6 @@ namespace ramses_internal return GL_ONE; case ETextureChannelColor::Zero: return GL_ZERO; - case ETextureChannelColor::NUMBER_OF_ELEMENTS: - break; } assert(false && "Invalid texture channel color"); return GL_RED; diff --git a/renderer/Platform/Device_GL/include/Device_GL/TypesConversion_GL.h b/src/renderer/internal/Platform/OpenGL/TypesConversion_GL.h similarity index 64% rename from renderer/Platform/Device_GL/include/Device_GL/TypesConversion_GL.h rename to src/renderer/internal/Platform/OpenGL/TypesConversion_GL.h index 949a0d442..ff624f40c 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/TypesConversion_GL.h +++ b/src/renderer/internal/Platform/OpenGL/TypesConversion_GL.h @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TYPESCONVERSION_GL_H -#define RAMSES_TYPESCONVERSION_GL_H +#pragma once -#include "Resource/EffectInputInformation.h" -#include "SceneAPI/RenderState.h" -#include "SceneAPI/TextureEnums.h" -#include "Resource/TextureMetaInfo.h" +#include "internal/SceneGraph/Resource/EffectInputInformation.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" -namespace ramses_internal +namespace ramses::internal { struct TextureUploadParams_GL { - GLenum sizedInternalFormat = GL_ZERO; + GLint sizedInternalFormat = GL_ZERO; GLenum baseInternalFormat = GL_ZERO; GLenum type = GL_ZERO; bool compressed = false; - uint32_t byteAlignment = 0u; + GLint byteAlignment = 0; TextureSwizzleArray swizzle = {}; }; @@ -34,17 +33,15 @@ namespace ramses_internal static GLenum GetDepthFunc(EDepthFunc func); static GLenum GetBlendFactor(EBlendFactor factor); static GLenum GetBlendOperation(EBlendOperation operation); - static GLenum GetWrapMode(EWrapMethod wrapMode); + static GLint GetWrapMode(ETextureAddressMode wrapMode); static GLenum GetStencilFunc(EStencilFunc func); static GLenum GetStencilOperation(EStencilOp op); static GLenum GetCullMode(ECullMode mode); static GLenum GetTextureTargetFromTextureInputType(EEffectInputTextureType textureType); static GLenum GetCubemapFaceSpecifier(ETextureCubeFace face); - static GLenum GetASTCFormat(ETextureFormat ramsesEnum); - static ETextureFormat GetTextureFormatFromCompressedGLTextureFormat(GLenum compressedGLTextureFormat); - static GLenum GetGlColorFromTextureChannelColor(ETextureChannelColor color); - static TextureUploadParams_GL GetTextureUploadParams(ETextureFormat format); + static GLint GetASTCFormat(EPixelStorageFormat ramsesEnum); + static EPixelStorageFormat GetTextureFormatFromCompressedGLTextureFormat(GLenum compressedGLTextureFormat); + static GLint GetGlColorFromTextureChannelColor(ETextureChannelColor color); + static TextureUploadParams_GL GetTextureUploadParams(EPixelStorageFormat format); }; } - -#endif diff --git a/renderer/Platform/Device_GL/include/Device_GL/Types_GL.h b/src/renderer/internal/Platform/OpenGL/Types_GL.h similarity index 81% rename from renderer/Platform/Device_GL/include/Device_GL/Types_GL.h rename to src/renderer/internal/Platform/OpenGL/Types_GL.h index 0298ae019..e2ecac4e7 100644 --- a/renderer/Platform/Device_GL/include/Device_GL/Types_GL.h +++ b/src/renderer/internal/Platform/OpenGL/Types_GL.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TYPES_GL_H -#define RAMSES_TYPES_GL_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Common/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" -namespace ramses_internal +#include + +namespace ramses::internal { // GLHandle is not strongly typed because most of the time its scope is limited and involves GL call // and it is stored in GPUResource as platform independent type, thus would require conversion every time it is used @@ -21,12 +21,9 @@ namespace ramses_internal struct GLInputLocationTag {}; using GLInputLocation = StronglyTypedValue; - static const GLInputLocation GLInputLocationInvalid(-1); using TextureSlot = int32_t; // TODO Violin fix this using GLenum = unsigned int; } - -#endif diff --git a/renderer/PlatformFactory/src/PlatformFactory.cpp b/src/renderer/internal/Platform/PlatformFactory.cpp similarity index 71% rename from renderer/PlatformFactory/src/PlatformFactory.cpp rename to src/renderer/internal/Platform/PlatformFactory.cpp index d4f3481a1..af6b0de4d 100644 --- a/renderer/PlatformFactory/src/PlatformFactory.cpp +++ b/src/renderer/internal/Platform/PlatformFactory.cpp @@ -6,31 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformFactory/PlatformFactory.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RendererConfig.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/PlatformFactory.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS) -#include "Platform_Windows_WGL/Platform_Windows_WGL.h" +#include "internal/Platform/Windows/Platform_Windows_WGL.h" #endif #if defined(ramses_sdk_ENABLE_WINDOW_TYPE_X11) -#include "Platform_X11_EGL/Platform_X11_EGL.h" +#include "internal/Platform/X11/Platform_X11_EGL.h" #endif #if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) -#include "Platform_Wayland_IVI_EGL/Platform_Wayland_IVI_EGL_ES_3_0.h" +#include "internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.h" #endif #if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) -#include "Platform_Wayland_Shell_EGL/Platform_Wayland_Shell_EGL_ES_3_0.h" +#include "internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.h" #endif #if defined(ramses_sdk_ENABLE_WINDOW_TYPE_ANDROID) -#include "Platform_Android_EGL/Platform_Android_EGL.h" +#include "internal/Platform/Android/Platform_Android_EGL.h" +#endif +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_IOS) +#include "internal/Platform/iOS/Platform_iOS_EGL.h" #endif -namespace ramses_internal +namespace ramses::internal { std::unique_ptr PlatformFactory::createPlatform(const RendererConfig& rendererConfig, const DisplayConfig& displayConfig) { @@ -49,6 +52,10 @@ namespace ramses_internal case EWindowType::Android: #if defined(ramses_sdk_ENABLE_WINDOW_TYPE_ANDROID) return std::make_unique(rendererConfig); +#endif + case ramses::EWindowType::iOS: +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_IOS) + return std::make_unique(rendererConfig); #endif break; case EWindowType::Wayland_IVI: diff --git a/renderer/PlatformFactory/include/PlatformFactory/PlatformFactory.h b/src/renderer/internal/Platform/PlatformFactory.h similarity index 82% rename from renderer/PlatformFactory/include/PlatformFactory/PlatformFactory.h rename to src/renderer/internal/Platform/PlatformFactory.h index b2eb41702..85d8ac910 100644 --- a/renderer/PlatformFactory/include/PlatformFactory/PlatformFactory.h +++ b/src/renderer/internal/Platform/PlatformFactory.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMFACTORY_H -#define RAMSES_PLATFORMFACTORY_H +#pragma once -#include "RendererAPI/IPlatformFactory.h" +#include "internal/RendererLib/PlatformInterface/IPlatformFactory.h" -namespace ramses_internal +namespace ramses::internal { class PlatformFactory : public IPlatformFactory { @@ -19,5 +18,3 @@ namespace ramses_internal std::unique_ptr createPlatform(const RendererConfig& rendererConfig, const DisplayConfig& displayConfig) override; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/EmbeddedCompositor_Wayland.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/EmbeddedCompositor_Wayland.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.cpp index fd837b93d..c12b6fb6e 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/EmbeddedCompositor_Wayland.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.cpp @@ -6,27 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandBuffer.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufGlobal.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufBuffer.h" -#include "EmbeddedCompositor_Wayland/TextureUploadingAdapter_Wayland.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabuf.h" -#include "EmbeddedCompositor_Wayland/WaylandOutputParams.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RendererLogContext.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputParams.h" +#include "internal/Platform/EGL/Context_EGL.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/PlatformAbstraction/PlatformTime.h" #include #include -namespace ramses_internal +namespace ramses::internal { - EmbeddedCompositor_Wayland::EmbeddedCompositor_Wayland(const DisplayConfig& displayConfig, IContext& context) + EmbeddedCompositor_Wayland::EmbeddedCompositor_Wayland(const DisplayConfig& displayConfig, Context_EGL &context) : m_waylandEmbeddedSocketName(displayConfig.getWaylandSocketEmbedded()) , m_waylandEmbeddedSocketGroup(displayConfig.getWaylandSocketEmbeddedGroup()) , m_waylandEmbeddedSocketPermissions(displayConfig.getWaylandSocketEmbeddedPermissions()) @@ -101,7 +102,7 @@ namespace ramses_internal bool EmbeddedCompositor_Wayland::hasUpdatedStreamTextureSources() const { - return 0u != m_updatedStreamTextureSourceIds.size(); + return !m_updatedStreamTextureSourceIds.empty(); } WaylandIviSurfaceIdSet EmbeddedCompositor_Wayland::dispatchUpdatedStreamTextureSourceIds() @@ -128,13 +129,34 @@ namespace ramses_internal void EmbeddedCompositor_Wayland::logInfos(RendererLogContext& context) const { - context << m_surfaces.size() << " connected wayland client(s)" << RendererLogContext::NewLine; + WaylandEGLExtensionProcs eglExt(m_context.getEglDisplay()); + context << m_surfaces.size() << " wayland surface(s)" << RendererLogContext::NewLine; context.indent(); for (auto surface: m_surfaces) { - surface->logInfos(context); + surface->logInfos(context, eglExt); } context.unindent(); + context << m_waylandBuffers.size() << " wayland buffer(s)" << RendererLogContext::NewLine; + context.indent(); + for (auto* buffer: m_waylandBuffers) + { + buffer->logInfos(context, eglExt); + } + context.unindent(); + context << m_regions.size() << " wayland region(s) - [not supported]" << RendererLogContext::NewLine; + } + + void EmbeddedCompositor_Wayland::logPeriodicInfo(StringOutputStream& sos) const + { + sos << "EC: "; + for (auto surface: m_surfaces) + { + auto* buf = surface->getWaylandBuffer(); + auto* res = (buf != nullptr) ? static_cast(buf->getResource().getLowLevelHandle()) : nullptr; + sos << surface->getIviSurfaceId().getValue() << "[pid:" << surface->getClientCredentials().getProcessId() << " res:" << res << "];"; + } + sos << "\n"; } void EmbeddedCompositor_Wayland::addWaylandSurface(IWaylandSurface& waylandSurface) @@ -158,7 +180,7 @@ namespace ramses_internal } } - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::removeWaylandSurface() Client destroyed surface, showing fallback texture for ivi surface " << waylandSurface.getIviSurfaceId() + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::removeWaylandSurface() Client destroyed surface, showing fallback texture for " << waylandSurface.getIviSurfaceId() << ". Count surfaces :" << m_surfaces.size()); } @@ -185,7 +207,7 @@ namespace ramses_internal if (notifyClients) { LOG_TRACE(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::endFrame(): will send surface frame callbacks to clients"); - const uint32_t time = static_cast(PlatformTime::GetMillisecondsAbsolute()); + const auto time = static_cast(PlatformTime::GetMillisecondsAbsolute()); for (auto surface: m_surfaces) { @@ -205,21 +227,21 @@ namespace ramses_internal IWaylandSurface* waylandClientSurface = findWaylandSurfaceByIviSurfaceId(streamTextureSourceId); assert(nullptr != waylandClientSurface); - LOG_DEBUG(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::uploadCompositingContentForStreamTexture(): Stream texture with source Id " << streamTextureSourceId); + LOG_DEBUG(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::uploadCompositingContentForStreamTexture() " << streamTextureSourceId); LOG_INFO(CONTEXT_SMOKETEST, "embedded-compositing client surface found for existing streamtexture: " << streamTextureSourceId); - uploadCompositingContentForWaylandSurface(waylandClientSurface, textureHandle, textureUploadingAdapter); + UploadCompositingContentForWaylandSurface(waylandClientSurface, textureHandle, textureUploadingAdapter); return waylandClientSurface->getNumberOfCommitedFrames(); } - void EmbeddedCompositor_Wayland::uploadCompositingContentForWaylandSurface(IWaylandSurface* waylandSurface, DeviceResourceHandle textureHandle, ITextureUploadingAdapter& textureUploadingAdapter) + void EmbeddedCompositor_Wayland::UploadCompositingContentForWaylandSurface(IWaylandSurface* waylandSurface, DeviceResourceHandle textureHandle, ITextureUploadingAdapter& textureUploadingAdapter) { IWaylandBuffer* waylandBuffer = waylandSurface->getWaylandBuffer(); assert(nullptr != waylandBuffer); WaylandBufferResource& waylandBufferResource = waylandBuffer->getResource(); - const uint8_t* sharedMemoryBufferData = static_cast(waylandBufferResource.bufferGetSharedMemoryData()); + const auto* sharedMemoryBufferData = static_cast(waylandBufferResource.bufferGetSharedMemoryData()); LinuxDmabufBufferData* linuxDmabufBuffer = LinuxDmabufBuffer::fromWaylandBufferResource(waylandBufferResource); const bool surfaceBufferTypeChanged = waylandSurface->dispatchBufferTypeChanged(); @@ -228,16 +250,16 @@ namespace ramses_internal { //reset swizzle const TextureSwizzleArray swizzle = {ETextureChannelColor::Red, ETextureChannelColor::Green, ETextureChannelColor::Blue, ETextureChannelColor::Alpha}; - uint8_t dummyData { 0u }; - textureUploadingAdapter.uploadTexture2D(textureHandle, 1u, 1u, ETextureFormat::R8, &dummyData, swizzle); + std::byte dummyData { 0u }; + textureUploadingAdapter.uploadTexture2D(textureHandle, 1u, 1u, EPixelStorageFormat::R8, &dummyData, swizzle); - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::uploadCompositingContentForWaylandSurface(): resetting swizzle for stream source id :" << waylandSurface->getIviSurfaceId()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::uploadCompositingContentForWaylandSurface(): resetting swizzle for " << waylandSurface->getIviSurfaceId()); } if (nullptr != sharedMemoryBufferData) { const TextureSwizzleArray swizzle = {ETextureChannelColor::Blue, ETextureChannelColor::Green, ETextureChannelColor::Red, ETextureChannelColor::Alpha}; - textureUploadingAdapter.uploadTexture2D(textureHandle, waylandBufferResource.bufferGetSharedMemoryWidth(), waylandBufferResource.bufferGetSharedMemoryHeight(), ETextureFormat::RGBA8, sharedMemoryBufferData, swizzle); + textureUploadingAdapter.uploadTexture2D(textureHandle, waylandBufferResource.getWidth(), waylandBufferResource.getHeight(), EPixelStorageFormat::RGBA8, sharedMemoryBufferData, swizzle); } else if (nullptr != linuxDmabufBuffer) { @@ -246,7 +268,7 @@ namespace ramses_internal if(!success) { StringOutputStream message; - message << "Failed creating EGL image from dma buffer for stream source id: " << waylandSurface->getIviSurfaceId(); + message << "Failed creating EGL image from dma buffer for " << waylandSurface->getIviSurfaceId(); waylandBufferResource.postError(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, message.release()); } @@ -275,10 +297,7 @@ namespace ramses_internal { return waylandClientSurface->getNumberOfCommitedFramesSinceBeginningOfTime(); } - else - { - return 0; - } + return 0; } bool EmbeddedCompositor_Wayland::isBufferAttachedToWaylandIviSurface(WaylandIviSurfaceId waylandSurfaceId) const @@ -288,10 +307,7 @@ namespace ramses_internal { return waylandClientSurface->hasPendingBuffer() || waylandClientSurface->getWaylandBuffer() != nullptr; } - else - { - return false; - } + return false; } uint32_t EmbeddedCompositor_Wayland::getNumberOfCompositorConnections() const @@ -325,10 +341,7 @@ namespace ramses_internal { return waylandClientSurface->getSurfaceTitle(); } - else - { - return {}; - } + return {}; } IWaylandBuffer* EmbeddedCompositor_Wayland::findWaylandBuffer(WaylandBufferResource& bufferResource) @@ -384,11 +397,11 @@ namespace ramses_internal void EmbeddedCompositor_Wayland::removeFromUpdatedStreamTextureSourceIds(WaylandIviSurfaceId id) { - if (m_newStreamTextureSourceIds.count(id)) + if (m_newStreamTextureSourceIds.count(id) != 0u) { m_newStreamTextureSourceIds.erase(id); } - else if (m_knownStreamTextureSoruceIds.count(id)) + else if (m_knownStreamTextureSoruceIds.count(id) != 0u) { m_obsoleteStreamTextureSourceIds.insert(id); } @@ -399,10 +412,10 @@ namespace ramses_internal void EmbeddedCompositor_Wayland::addToUpdatedStreamTextureSourceIds(WaylandIviSurfaceId id) { - LOG_TRACE(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::addToUpdatedStreamTextureSourceIds: new texture data for stream texture with source id " << id); + LOG_TRACE(CONTEXT_RENDERER, "EmbeddedCompositor_Wayland::addToUpdatedStreamTextureSourceIds: new texture data for stream texture with " << id); m_updatedStreamTextureSourceIds.insert(id); - if(!m_knownStreamTextureSoruceIds.count(id)) + if (m_knownStreamTextureSoruceIds.count(id) == 0u) { m_newStreamTextureSourceIds.insert(id); m_knownStreamTextureSoruceIds.insert(id); @@ -416,8 +429,7 @@ namespace ramses_internal void EmbeddedCompositor_Wayland::removeWaylandRegion(IWaylandRegion& waylandRegion) { - const bool removed = m_regions.remove(&waylandRegion); - UNUSED(removed); + [[maybe_unused]] const bool removed = m_regions.remove(&waylandRegion); assert(removed); } } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h index 2afa5ef08..1bfb5466d 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h @@ -6,26 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITOR_WAYLAND_H -#define RAMSES_EMBEDDEDCOMPOSITOR_WAYLAND_H - -#include "EmbeddedCompositor_Wayland/WaylandCompositorGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandShellGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandOutputGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/WaylandIVIApplicationGlobal.h" -#include "EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufGlobal.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "Collections/HashMap.h" +#pragma once + +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" #include -namespace ramses_internal +namespace ramses::internal { class DisplayConfig; - class IContext; + class Context_EGL; class IWaylandCompositorConnection; class IWaylandSurface; class IWaylandBuffer; @@ -35,7 +34,7 @@ namespace ramses_internal class EmbeddedCompositor_Wayland: public IEmbeddedCompositor, public IEmbeddedCompositor_Wayland { public: - EmbeddedCompositor_Wayland(const DisplayConfig& displayConfig, IContext& context); + EmbeddedCompositor_Wayland(const DisplayConfig& displayConfig, Context_EGL& context); ~EmbeddedCompositor_Wayland() override; bool init(); @@ -58,6 +57,7 @@ namespace ramses_internal [[nodiscard]] const IWaylandSurface& findSurfaceForStreamTexture(WaylandIviSurfaceId streamTextureSourceId) const; [[nodiscard]] std::string getTitleOfWaylandIviSurface(WaylandIviSurfaceId waylandSurfaceId) const override; void logInfos(RendererLogContext& context) const override; + void logPeriodicInfo(StringOutputStream& sos) const override; void addWaylandSurface(IWaylandSurface& waylandSurface) override; void removeWaylandSurface(IWaylandSurface& waylandSurface) override; @@ -80,7 +80,7 @@ namespace ramses_internal private: [[nodiscard]] IWaylandSurface* findWaylandSurfaceByIviSurfaceId(WaylandIviSurfaceId iviSurfaceId) const; - void uploadCompositingContentForWaylandSurface(IWaylandSurface* waylandSurface, DeviceResourceHandle textureHandle, ITextureUploadingAdapter& textureUploadingAdapter); + static void UploadCompositingContentForWaylandSurface(IWaylandSurface* waylandSurface, DeviceResourceHandle textureHandle, ITextureUploadingAdapter& textureUploadingAdapter); bool applyPermissionsGroupToEmbeddedCompositingSocket(const std::string& embeddedSocketName); @@ -93,7 +93,7 @@ namespace ramses_internal const std::string m_waylandEmbeddedSocketGroup; const uint32_t m_waylandEmbeddedSocketPermissions; const int m_waylandEmbeddedSocketFD; - IContext& m_context; + Context_EGL& m_context; WaylandDisplay m_serverDisplay; WaylandCompositorGlobal m_compositorGlobal; @@ -119,5 +119,3 @@ namespace ramses_internal WaylandRegions m_regions; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h index e019eba36..77c0ce4a9 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IEMBEDDEDCOMPOSITOR_WAYLAND_H -#define RAMSES_IEMBEDDEDCOMPOSITOR_WAYLAND_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IWaylandBuffer; class IWaylandSurface; @@ -20,7 +19,7 @@ namespace ramses_internal class IEmbeddedCompositor_Wayland { public: - virtual ~IEmbeddedCompositor_Wayland(){} + virtual ~IEmbeddedCompositor_Wayland() = default; virtual void handleBufferDestroyed(IWaylandBuffer& buffer) = 0; virtual void addWaylandSurface(IWaylandSurface& waylandSurface) = 0; @@ -32,5 +31,3 @@ namespace ramses_internal virtual void removeWaylandRegion(IWaylandRegion& waylandRegion) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/INativeWaylandResource.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/INativeWaylandResource.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h index f2317efbf..c7892d433 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/INativeWaylandResource.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h @@ -5,8 +5,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INATIVEWAYLANDRESOURCE_H -#define RAMSES_INATIVEWAYLANDRESOURCE_H +#pragma once #include #include @@ -16,12 +15,12 @@ using IWaylandResourceDestroyFuncT = void (*)(struct wl_resource *); struct wl_listener; struct wl_resource; -namespace ramses_internal +namespace ramses::internal { class INativeWaylandResource { public: - virtual ~INativeWaylandResource() {} + virtual ~INativeWaylandResource() = default; virtual int getVersion() = 0; virtual void postError(uint32_t code, const std::string& message) = 0; virtual void* getUserData() = 0; @@ -31,5 +30,3 @@ namespace ramses_internal virtual void destroy() = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandBuffer.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h similarity index 75% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandBuffer.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h index aa1767992..7e7afba5c 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandBuffer.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDBUFFER_H -#define RAMSES_IWAYLANDBUFFER_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class WaylandBufferResource; + class RendererLogContext; + class WaylandEGLExtensionProcs; class IWaylandBuffer { public: - virtual ~IWaylandBuffer(){} + virtual ~IWaylandBuffer() = default; [[nodiscard]] virtual WaylandBufferResource& getResource() const = 0; virtual void reference() = 0; virtual void release() = 0; [[nodiscard]] virtual bool isSharedMemoryBuffer() const = 0; + virtual void logInfos(RendererLogContext& context, const WaylandEGLExtensionProcs& eglExt) const = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandClient.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h similarity index 84% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandClient.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h index 8e2ea3e0a..35f668521 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandClient.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h @@ -5,15 +5,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDCLIENT_H -#define RAMSES_IWAYLANDCLIENT_H +#pragma once -#include "EmbeddedCompositor_Wayland/WaylandClientCredentials.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.h" #include struct wl_interface; -namespace ramses_internal +namespace ramses::internal { class INativeWaylandResource; class WaylandCallbackResource; @@ -21,12 +20,10 @@ namespace ramses_internal class IWaylandClient { public: - virtual ~IWaylandClient() {} + virtual ~IWaylandClient() = default; [[nodiscard]] virtual WaylandClientCredentials getCredentials() const = 0; virtual void postNoMemory() = 0; virtual INativeWaylandResource* resourceCreate(const wl_interface* interface, int version, uint32_t id) = 0; virtual WaylandCallbackResource* callbackResourceCreate(const wl_interface* interface, int version, uint32_t id) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h similarity index 82% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h index 296868d36..05e4f75d0 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h @@ -5,24 +5,21 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDCOMPOSITORCONNECTION_H -#define RAMSES_IWAYLANDCOMPOSITORCONNECTION_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class IWaylandCompositorConnection { public: - virtual ~IWaylandCompositorConnection(){} + virtual ~IWaylandCompositorConnection() = default; virtual void resourceDestroyed() = 0; virtual void compositorCreateSurface(IWaylandClient& client, uint32_t id) = 0; virtual void compositorCreateRegion(IWaylandClient& client, uint32_t id) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandCompositorGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorGlobal.h similarity index 80% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandCompositorGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorGlobal.h index 760f0279b..ed077d888 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandCompositorGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorGlobal.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDCOMPOSITORGLOBAL_H -#define RAMSES_IWAYLANDCOMPOSITORGLOBAL_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class IWaylandDisplay; class IWaylandClient; @@ -19,11 +18,9 @@ namespace ramses_internal class IWaylandCompositorGlobal { public: - virtual ~IWaylandCompositorGlobal(){} + virtual ~IWaylandCompositorGlobal() = default; virtual bool init(IWaylandDisplay& serverDisplay) = 0; virtual void compositorBind(IWaylandClient& client, uint32_t version, uint32_t id) = 0; virtual void destroy() = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandDisplay.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandDisplay.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandDisplay.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandDisplay.h index 8c041d01c..733d243d5 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandDisplay.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandDisplay.h @@ -6,26 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDDISPLAY_H -#define RAMSES_IWAYLANDDISPLAY_H +#pragma once #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class IWaylandGlobal; class IWaylandDisplay { public: - virtual ~IWaylandDisplay() {} + virtual ~IWaylandDisplay() = default; virtual bool init(const std::string& socketName, const std::string& socketGroupName, uint32_t socketPermissions, int socketFD) = 0; virtual IWaylandGlobal* createGlobal(const wl_interface* interface, int version, void* data, wl_global_bind_func_t bind) = 0; virtual void dispatchEventLoop() = 0; virtual void flushClients() = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h similarity index 79% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h index 3f2d016d5..b2ea1ed19 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h @@ -5,16 +5,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDGLOBAL_H -#define RAMSES_IWAYLANDGLOBAL_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IWaylandGlobal { public: - virtual ~IWaylandGlobal() {} + virtual ~IWaylandGlobal() = default; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVIApplicationConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationConnection.h similarity index 72% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVIApplicationConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationConnection.h index 7e0242ee4..f909b7b12 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVIApplicationConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationConnection.h @@ -5,12 +5,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDIVIAPPLICATIONCONNECTION_H -#define RAMSES_IWAYLANDIVIAPPLICATIONCONNECTION_H +#pragma once #include +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class INativeWaylandResource; @@ -18,10 +18,8 @@ namespace ramses_internal class IWaylandIVIApplicationConnection { public: - virtual ~IWaylandIVIApplicationConnection(){} + virtual ~IWaylandIVIApplicationConnection() = default; virtual void resourceDestroyed() = 0; - virtual void iviApplicationIVISurfaceCreate(IWaylandClient& client, uint32_t iviId, INativeWaylandResource& surfaceResource, uint32_t id) = 0; + virtual void iviApplicationIVISurfaceCreate(IWaylandClient& client, WaylandIviSurfaceId iviId, INativeWaylandResource& surfaceResource, uint32_t id) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVIApplicationGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationGlobal.h similarity index 80% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVIApplicationGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationGlobal.h index ee70a50d1..abf61ecb8 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVIApplicationGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationGlobal.h @@ -6,23 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDIVIAPPLICATIONGLOBAL_H -#define RAMSES_IWAYLANDIVIAPPLICATIONGLOBAL_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class IWaylandIVIApplicationGlobal { public: - virtual ~IWaylandIVIApplicationGlobal(){} + virtual ~IWaylandIVIApplicationGlobal() = default; protected: virtual void iviApplicationBind(IWaylandClient& client, uint32_t version, uint32_t id) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVISurface.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVISurface.h similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVISurface.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVISurface.h index d0375a3d5..e31b812d4 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandIVISurface.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVISurface.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDIVISURFACE_H -#define RAMSES_IWAYLANDIVISURFACE_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class IWaylandBuffer; class IWaylandIVISurface { public: - virtual ~IWaylandIVISurface(){} + virtual ~IWaylandIVISurface() = default; virtual void resourceDestroyed() = 0; virtual void surfaceWasDeleted() = 0; virtual void bufferWasSetToSurface(IWaylandBuffer* buffer) = 0; [[nodiscard]] virtual WaylandIviSurfaceId getIviId() const = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandRegion.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h similarity index 86% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandRegion.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h index 396b80f47..4a0eb90b0 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandRegion.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDREGION_H -#define RAMSES_IWAYLANDREGION_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class IWaylandRegion { public: - virtual ~IWaylandRegion(){} + virtual ~IWaylandRegion() = default; virtual void resourceDestroyed() = 0; virtual void regionAdd(IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) = 0; virtual void regionSubtract(IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellConnection.h similarity index 83% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellConnection.h index 380efa868..1380f5813 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellConnection.h @@ -5,12 +5,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDSHELLCONNECTION_H -#define RAMSES_IWAYLANDSHELLCONNECTION_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class INativeWaylandResource; @@ -18,10 +17,8 @@ namespace ramses_internal class IWaylandShellConnection { public: - virtual ~IWaylandShellConnection(){} + virtual ~IWaylandShellConnection() = default; virtual void resourceDestroyed() = 0; virtual void shellGetShellSurface(IWaylandClient& client, uint32_t id, INativeWaylandResource& surfaceResource) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellGlobal.h similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellGlobal.h index dcdabd34e..1085d4cad 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellGlobal.h @@ -6,21 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDSHELLGLOBAL_H -#define RAMSES_IWAYLANDSHELLGLOBAL_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class IWaylandShellGlobal { public: - virtual ~IWaylandShellGlobal(){} + virtual ~IWaylandShellGlobal() = default; virtual void shellBind(IWaylandClient& client, uint32_t version, uint32_t id) = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellSurface.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellSurface.h similarity index 92% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellSurface.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellSurface.h index 8ef5749de..b6f3a069e 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandShellSurface.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellSurface.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDSHELLSURFACE_H -#define RAMSES_IWAYLANDSHELLSURFACE_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class INativeWaylandResource; @@ -20,7 +19,7 @@ namespace ramses_internal class IWaylandShellSurface { public: - virtual ~IWaylandShellSurface(){} + virtual ~IWaylandShellSurface() = default; virtual void resourceDestroyed() = 0; virtual void shellSurfacePong(IWaylandClient& client, uint32_t serial) = 0; virtual void shellSurfaceMove(IWaylandClient& client, INativeWaylandResource& seatResource, uint32_t serial) = 0; @@ -37,5 +36,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandSurface.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h similarity index 88% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandSurface.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h index c8851e0be..2cd236904 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/IWaylandSurface.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h @@ -6,26 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWAYLANDSURFACE_H -#define RAMSES_IWAYLANDSURFACE_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "RendererAPI/Types.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/RendererLib/Types.h" #include -namespace ramses_internal +namespace ramses::internal { class IWaylandShellSurface; class WaylandBufferResource; class IWaylandBuffer; class IWaylandIVISurface; class RendererLogContext; + class WaylandEGLExtensionProcs; class IWaylandSurface { public: - virtual ~IWaylandSurface(){} + virtual ~IWaylandSurface() = default; virtual void resourceDestroyed() = 0; virtual void surfaceAttach(IWaylandClient& client, WaylandBufferResource& bufferResource, int x, int y) = 0; @@ -40,7 +40,7 @@ namespace ramses_internal virtual void surfaceDamageBuffer(IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) = 0; virtual void setShellSurface(IWaylandShellSurface* shellSurface) = 0; [[nodiscard]] virtual bool hasShellSurface() const = 0; - virtual void logInfos(RendererLogContext& context) const = 0; + virtual void logInfos(RendererLogContext& context, const WaylandEGLExtensionProcs& eglExt) const = 0; [[nodiscard]] virtual WaylandIviSurfaceId getIviSurfaceId() const = 0; virtual void sendFrameCallbacks(uint32_t time) = 0; [[nodiscard]] virtual IWaylandBuffer* getWaylandBuffer() const = 0; @@ -56,5 +56,3 @@ namespace ramses_internal virtual bool dispatchBufferTypeChanged() = 0; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabuf.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.cpp similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabuf.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.cpp index b34124fe2..02c59a8e8 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabuf.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.cpp @@ -7,20 +7,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/LinuxDmabuf.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/ThreadLocalLogForced.h" -#include +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + #include -namespace ramses_internal +#include +#include + +namespace ramses::internal { const unsigned int LinuxDmabufBufferData::MAX_DMABUF_PLANES; - LinuxDmabufBufferData::PlaneData::PlaneData() - : m_fd(-1) - { - } + LinuxDmabufBufferData::PlaneData::PlaneData() = default; LinuxDmabufBufferData::PlaneData::PlaneData(PlaneData const& other) { @@ -52,7 +51,6 @@ namespace ramses_internal } LinuxDmabufBufferData::LinuxDmabufBufferData() - : m_numPlanes(0) { // Set a valid do-nothing callback clearDestroyCallback(); @@ -63,14 +61,11 @@ namespace ramses_internal m_destroyCallback(this); } - void LinuxDmabufBufferData::noopDestroyCallback(LinuxDmabufBufferData* dmabuf) - { - UNUSED(dmabuf); - } + void LinuxDmabufBufferData::noopDestroyCallback([[maybe_unused]] LinuxDmabufBufferData* dmabuf) {} void LinuxDmabufBufferData::clearDestroyCallback() { - setDestroyCallback(std::bind(&LinuxDmabufBufferData::noopDestroyCallback, this, std::placeholders::_1)); + setDestroyCallback([this](LinuxDmabufBufferData* dmabuf) { noopDestroyCallback(dmabuf); }); } void LinuxDmabufBufferData::setDestroyCallback(const std::function& func) diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabuf.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h similarity index 85% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabuf.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h index d749f3df1..c232a3baa 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabuf.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h @@ -7,13 +7,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINUXDMABUF_H -#define RAMSES_LINUXDMABUF_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class LinuxDmabufParams; @@ -57,10 +56,10 @@ namespace ramses_internal PlaneData(PlaneData const& other); ~PlaneData(); - int m_fd; - uint32_t m_offset; - uint32_t m_stride; - uint64_t m_modifier; + int m_fd{-1}; + uint32_t m_offset{0}; + uint32_t m_stride{0}; + uint64_t m_modifier{0}; }; void noopDestroyCallback(LinuxDmabufBufferData* dmabuf); @@ -69,12 +68,10 @@ namespace ramses_internal std::array m_planeData; - int32_t m_width; - int32_t m_height; - uint32_t m_format; - uint32_t m_flags; - unsigned int m_numPlanes; + int32_t m_width{0}; + int32_t m_height{0}; + uint32_t m_format{0}; + uint32_t m_flags{0}; + unsigned int m_numPlanes{0}; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufBuffer.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h similarity index 90% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufBuffer.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h index 30c7134af..deeb53783 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufBuffer.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h @@ -7,12 +7,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINUXDMABUFBUFFER_H -#define RAMSES_LINUXDMABUFBUFFER_H +#pragma once #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class LinuxDmabufBufferData; class WaylandBufferResource; @@ -27,5 +26,3 @@ namespace ramses_internal static struct wl_buffer_interface const m_bufferInterface; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufConnection.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.cpp similarity index 83% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufConnection.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.cpp index 3c41af567..e34b97ea2 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufConnection.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.cpp @@ -13,16 +13,16 @@ #include "wayland-server.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufConnection.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufParams.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" -namespace ramses_internal +namespace ramses::internal { struct zwp_linux_dmabuf_v1_interface const LinuxDmabufConnection::m_dmabufInterface = { @@ -33,7 +33,7 @@ namespace ramses_internal LinuxDmabufConnection::LinuxDmabufConnection(IWaylandClient& client, uint32_t version, uint32_t id) : m_clientCredentials(client.getCredentials()) { - m_resource = client.resourceCreate(&zwp_linux_dmabuf_v1_interface, version, id); + m_resource = client.resourceCreate(&zwp_linux_dmabuf_v1_interface, static_cast(version), id); if (m_resource) { @@ -68,7 +68,7 @@ namespace ramses_internal void LinuxDmabufConnection::ResourceDestroyedCallback(wl_resource* dmabufConnectionResource) { - LinuxDmabufConnection* dmabufConnection = static_cast(wl_resource_get_user_data(dmabufConnectionResource)); + auto* dmabufConnection = static_cast(wl_resource_get_user_data(dmabufConnectionResource)); dmabufConnection->resourceDestroyed(); delete dmabufConnection; @@ -87,13 +87,13 @@ namespace ramses_internal void LinuxDmabufConnection::DmabufDestroyCallback(wl_client* /*client*/, wl_resource* dmabufConnectionResource) { - LinuxDmabufConnection* connection = static_cast(wl_resource_get_user_data(dmabufConnectionResource)); + auto* connection = static_cast(wl_resource_get_user_data(dmabufConnectionResource)); delete connection; } void LinuxDmabufConnection::DmabufCreateParamsCallback(wl_client* /*client*/, wl_resource* dmabufConnectionResource, uint32_t id) { - LinuxDmabufConnection* connection = static_cast(wl_resource_get_user_data(dmabufConnectionResource)); + auto* connection = static_cast(wl_resource_get_user_data(dmabufConnectionResource)); connection->createParams(id); } @@ -125,8 +125,8 @@ namespace ramses_internal { }; - uint64_t const* modifiers; - size_t numModifiers; + uint64_t const* modifiers = nullptr; + size_t numModifiers = 0; if ((sizeof(fallbackModifiers) / sizeof(fallbackModifiers[0])) == 0) { @@ -160,7 +160,7 @@ namespace ramses_internal wl_resource* resource = m_resource->getLowLevelHandle(); WaylandClient client(wl_resource_get_client(resource)); - LinuxDmabufParams* params = new LinuxDmabufParams(client, m_resource->getVersion(), id); + auto* params = new LinuxDmabufParams(client, m_resource->getVersion(), id); if (!params->wasSuccessfullyInitialized()) { diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.h similarity index 90% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.h index de67388dd..789374436 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.h @@ -7,15 +7,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINUXDMABUFCONNECTION_H -#define RAMSES_LINUXDMABUFCONNECTION_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class LinuxDmabufConnection { @@ -42,4 +41,4 @@ namespace ramses_internal INativeWaylandResource* m_resource = nullptr; }; } -#endif + diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufGlobal.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.cpp similarity index 66% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufGlobal.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.cpp index c25af14dd..96f9d7326 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufGlobal.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.cpp @@ -17,15 +17,15 @@ #include "wayland-server.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" -#include "Context_EGL/Context_EGL.h" -#include "EmbeddedCompositor_Wayland/IWaylandGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufGlobal.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufConnection.h" -#include "WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h" - -namespace ramses_internal +#include "internal/Platform/EGL/Context_EGL.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" + +namespace ramses::internal { LinuxDmabufGlobal::LinuxDmabufGlobal(IEmbeddedCompositor_Wayland& compositor) : m_compositor(compositor) @@ -37,22 +37,9 @@ namespace ramses_internal assert(nullptr == m_waylandGlobal); } - bool LinuxDmabufGlobal::init(WaylandDisplay& serverDisplay, IContext& context) + bool LinuxDmabufGlobal::init(WaylandDisplay& serverDisplay, Context_EGL& context) { - EGLDisplay eglDisplay; - - Context_EGL* contextEgl = dynamic_cast(&context); - - if (!contextEgl) - { - // Running in a test case - return false; - } - - eglDisplay = contextEgl->getEglDisplay(); - assert(eglDisplay != EGL_NO_DISPLAY); - - WaylandEGLExtensionProcs procs(eglDisplay); + WaylandEGLExtensionProcs procs(context.getEglDisplay()); if (!procs.areDmabufExtensionsSupported()) { @@ -76,9 +63,10 @@ namespace ramses_internal } } + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) void LinuxDmabufGlobal::globalBind(IWaylandClient& client, uint32_t version, uint32_t id) { - LinuxDmabufConnection* connection = new LinuxDmabufConnection(client, version, id); + auto* connection = new LinuxDmabufConnection(client, version, id); connection->sendFormats(); @@ -86,10 +74,9 @@ namespace ramses_internal // reaped when the client disconnects or when the client destroys it. } - void LinuxDmabufGlobal::GlobalBindCallback(wl_client *client, void* data, uint32_t version, uint32_t id) + void LinuxDmabufGlobal::GlobalBindCallback(wl_client* client, void* data, [[maybe_unused]] uint32_t version, uint32_t id) { - UNUSED(version); - LinuxDmabufGlobal* global = static_cast(data); + auto* global = static_cast(data); WaylandClient waylandClient(client); global->globalBind(waylandClient, version, id); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.h index f1dff130e..2a8f2b994 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufGlobal.h @@ -7,17 +7,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINUXDMABUFGLOBAL_H -#define RAMSES_LINUXDMABUFGLOBAL_H +#pragma once #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositor_Wayland; - class IContext; + class Context_EGL; class WaylandDisplay; class IWaylandGlobal; class IWaylandClient; @@ -28,7 +27,7 @@ namespace ramses_internal explicit LinuxDmabufGlobal(IEmbeddedCompositor_Wayland& compositor); ~LinuxDmabufGlobal(); - bool init(WaylandDisplay& serverDisplay, IContext& context); + bool init(WaylandDisplay& serverDisplay, Context_EGL& context); void destroy(); void globalBind(IWaylandClient& client, uint32_t version, uint32_t id); @@ -42,5 +41,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufParams.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.cpp similarity index 84% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufParams.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.cpp index 69ce58999..865235f50 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/LinuxDmabufParams.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.cpp @@ -11,18 +11,18 @@ #include "wayland-server.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufParams.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufBuffer.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabuf.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" #include -namespace ramses_internal +namespace ramses::internal { struct zwp_linux_buffer_params_v1_interface const LinuxDmabufParams::m_paramsInterface = { @@ -36,7 +36,7 @@ namespace ramses_internal : m_clientCredentials(client.getCredentials()) { m_data = new LinuxDmabufBufferData(); - m_resource = client.resourceCreate(&zwp_linux_buffer_params_v1_interface, version, id); + m_resource = client.resourceCreate(&zwp_linux_buffer_params_v1_interface, static_cast(version), id); if (nullptr != m_resource) { @@ -73,7 +73,7 @@ namespace ramses_internal void LinuxDmabufParams::ResourceDestroyedCallback(wl_resource* dmabufParamsResource) { - LinuxDmabufParams* dmabufParams = static_cast(wl_resource_get_user_data(dmabufParamsResource)); + auto* dmabufParams = static_cast(wl_resource_get_user_data(dmabufParamsResource)); dmabufParams->resourceDestroyed(); delete dmabufParams; @@ -90,32 +90,29 @@ namespace ramses_internal m_resource = nullptr; } - void LinuxDmabufParams::DmabufParamsDestroyCallback(wl_client* client, wl_resource* dmabufParamsResource) + void LinuxDmabufParams::DmabufParamsDestroyCallback([[maybe_unused]] wl_client* client, wl_resource* dmabufParamsResource) { - UNUSED(client); - - LinuxDmabufParams* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); + auto* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); delete params; } - void LinuxDmabufParams::DmabufParamsAddCallback(wl_client* client, wl_resource* dmabufParamsResource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) + void LinuxDmabufParams::DmabufParamsAddCallback([[maybe_unused]] wl_client* client, wl_resource* dmabufParamsResource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) { - UNUSED(client); - LinuxDmabufParams* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); + auto* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); params->addPlane(fd, plane_idx, offset, stride, modifier_hi, modifier_lo); } void LinuxDmabufParams::DmabufParamsCreateCallback(wl_client* client, wl_resource* dmabufParamsResource, int32_t width, int32_t height, uint32_t format, uint32_t flags) { WaylandClient waylandClient(client); - LinuxDmabufParams* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); + auto* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); params->createBuffer(waylandClient, 0 /* allocate an available wl_resource ID */, width, height, format, flags); } void LinuxDmabufParams::DmabufParamsCreateImmedCallback(wl_client* client, wl_resource* dmabufParamsResource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) { WaylandClient waylandClient(client); - LinuxDmabufParams* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); + auto* params = static_cast(wl_resource_get_user_data(dmabufParamsResource)); params->createBuffer(waylandClient, buffer_id, width, height, format, flags); } @@ -175,17 +172,13 @@ namespace ramses_internal static void BufferDestroyCallback(wl_resource* bufferResource) { - LinuxDmabufBufferData* data = static_cast(wl_resource_get_user_data(bufferResource)); + auto* data = static_cast(wl_resource_get_user_data(bufferResource)); assert(nullptr != data); delete data; } void LinuxDmabufParams::createBuffer(IWaylandClient& client, uint32_t bufferId, int32_t width, int32_t height, uint32_t format, uint32_t flags) { - LOG_INFO(CONTEXT_RENDERER, "LinuxDmabufParams::createBuffer(): buffer id : " << bufferId - << ", width :" << width << ", height :" << height << ", format :" << format << ", flags :" << flags - << " " << m_clientCredentials); - if (m_data->getNumPlanes() < 1) { LOG_ERROR(CONTEXT_RENDERER, "LinuxDmabufParams::createBuffer(): failed to create buffer [id : " << bufferId @@ -223,7 +216,7 @@ namespace ramses_internal if (width < 1 || height < 1) { LOG_ERROR(CONTEXT_RENDERER, "LinuxDmabufParams::createBuffer(): failed to create buffer [id : " << bufferId - << "] because of invalid width or height"); + << "] because of invalid width(" << width << ") or height(" << height << ")"); StringOutputStream message; message << "invalid width " << width << " or height " << height; @@ -236,8 +229,8 @@ namespace ramses_internal for (unsigned int i = 0; i < m_data->getNumPlanes(); ++i) { - uint64_t const offset = static_cast(m_data->getOffset(i)); - uint64_t const stride = static_cast(m_data->getStride(i)); + auto const offset = static_cast(m_data->getOffset(i)); + auto const stride = static_cast(m_data->getStride(i)); // Validate internal consistency of user-supplied geometry info if (offset + stride > UINT32_MAX) @@ -341,6 +334,9 @@ namespace ramses_internal bufferWaylandResource->setImplementation(&LinuxDmabufBuffer::m_bufferInterface, m_data, BufferDestroyCallback); m_data = nullptr; + LOG_INFO_P(CONTEXT_RENDERER, "LinuxDmabufParams::createBuffer(): bufferId:{} w:{} h:{} format:{} flags:{} res:{} {}", + bufferId, width, height, format, flags, static_cast(bufferNativeWaylandResource), m_clientCredentials); + // Announce the resulting buffer to the client if (0 == bufferId) { @@ -354,9 +350,8 @@ namespace ramses_internal delete bufferWaylandResource; } - void LinuxDmabufBuffer::DmabufBufferDestroyCallback(wl_client* client, wl_resource* bufferResource) + void LinuxDmabufBuffer::DmabufBufferDestroyCallback([[maybe_unused]] wl_client* client, wl_resource* bufferResource) { - UNUSED(client); wl_resource_destroy(bufferResource); } @@ -364,12 +359,12 @@ namespace ramses_internal { struct wl_resource* nativeResource = resource.getLowLevelHandle(); - if (!wl_resource_instance_of(nativeResource, &wl_buffer_interface, &m_bufferInterface)) + if (wl_resource_instance_of(nativeResource, &wl_buffer_interface, &m_bufferInterface) == 0) { return nullptr; } - LinuxDmabufBufferData* ret = static_cast(wl_resource_get_user_data(nativeResource)); + auto* ret = static_cast(wl_resource_get_user_data(nativeResource)); assert(nullptr != ret); return ret; } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufParams.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.h similarity index 93% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufParams.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.h index bfed1067a..b92b590a6 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/LinuxDmabufParams.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.h @@ -7,15 +7,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINUXDMABUFPARAMS_H -#define RAMSES_LINUXDMABUFPARAMS_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class IWaylandClient; class INativeWaylandResource; @@ -51,5 +50,3 @@ namespace ramses_internal friend class LinuxDmabuf; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/NativeWaylandResource.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.cpp similarity index 88% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/NativeWaylandResource.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.cpp index 6e51a5d53..c34a55a3f 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/NativeWaylandResource.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.cpp @@ -6,16 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" #include #include -namespace ramses_internal +namespace ramses::internal { - NativeWaylandResource::NativeWaylandResource(): m_resource(nullptr) - { - } + NativeWaylandResource::NativeWaylandResource() = default; NativeWaylandResource::NativeWaylandResource(wl_resource* resource) : m_resource(resource) @@ -30,6 +28,7 @@ namespace ramses_internal void NativeWaylandResource::postError(uint32_t code, const std::string& message) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) wl_resource_post_error(m_resource, code, "%s", message.c_str()); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/NativeWaylandResource.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h similarity index 94% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/NativeWaylandResource.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h index aa7ade6b9..df0af7348 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/NativeWaylandResource.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h @@ -5,13 +5,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_NATIVEWAYLANDRESOURCE_H -#define RAMSES_NATIVEWAYLANDRESOURCE_H +#pragma once -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { /** A word about wayland resource (wl_resource) and their lifecycle. @@ -60,8 +59,6 @@ namespace ramses_internal void destroy() override; protected: - wl_resource* m_resource; + wl_resource* m_resource{nullptr}; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/TextureUploadingAdapter_Wayland.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.cpp similarity index 73% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/TextureUploadingAdapter_Wayland.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.cpp index 604c9307f..e06251b44 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/TextureUploadingAdapter_Wayland.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/TextureUploadingAdapter_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.h" #include #include #include #include "linux-dmabuf-unstable-v1-server-protocol.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabuf.h" -#include "RendererAPI/IDevice.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" -namespace ramses_internal +namespace ramses::internal { TextureUploadingAdapter_Wayland::TextureUploadingAdapter_Wayland(IDevice& device, wl_display* waylandWindowDisplay, wl_display* embeddedCompositingDisplay) : TextureUploadingAdapter_Base(device) @@ -55,14 +55,38 @@ namespace ramses_internal } } - void TextureUploadingAdapter_Wayland::uploadTextureFromWaylandResource(DeviceResourceHandle textureHandle, EGLClientBuffer bufferResource) + void TextureUploadingAdapter_Wayland::uploadTextureFromWaylandResource(DeviceResourceHandle textureHandle, wl_resource *bufferResource) { if (m_waylandEglExtensionProcs.areExtensionsSupported()) { const GLuint texID = m_device.getTextureAddress(textureHandle); glBindTexture(GL_TEXTURE_2D, texID); - const EGLImage eglImage = m_waylandEglExtensionProcs.eglCreateImageKHR(nullptr, EGL_WAYLAND_BUFFER_WL, bufferResource, nullptr); + if (CONTEXT_RENDERER.getLogLevel() >= ELogLevel::Debug) + { + EGLint textureFormat = 0; + EGLint textureWidth = 0; + EGLint textureHeight = 0; + EGLBoolean res = m_waylandEglExtensionProcs.eglQueryWaylandBufferWL(bufferResource, EGL_TEXTURE_FORMAT, &textureFormat); + if (res == EGL_FALSE) + { + LOG_ERROR(CONTEXT_RENDERER, "eglQueryWaylandBufferWL(EGL_TEXTURE_FORMAT) failed"); + } + res = m_waylandEglExtensionProcs.eglQueryWaylandBufferWL(bufferResource, EGL_WIDTH, &textureWidth); + if (res == EGL_FALSE) + { + LOG_ERROR(CONTEXT_RENDERER, "eglQueryWaylandBufferWL(EGL_WIDTH) failed"); + } + res = m_waylandEglExtensionProcs.eglQueryWaylandBufferWL(bufferResource, EGL_HEIGHT, &textureHeight); + if (res == EGL_FALSE) + { + LOG_ERROR(CONTEXT_RENDERER, "eglQueryWaylandBufferWL(EGL_HEIGHT) failed"); + } + LOG_DEBUG_P(CONTEXT_RENDERER, "TextureUploadingAdapter_Wayland::uploadTextureFromWaylandResource: w:{} h:{} format:{} ({})", + textureWidth, textureHeight, textureFormat, m_waylandEglExtensionProcs.getTextureFormatName(textureFormat)); + } + + EGLImage eglImage = m_waylandEglExtensionProcs.eglCreateImageKHR(nullptr, EGL_WAYLAND_BUFFER_WL, bufferResource, nullptr); assert(EGL_NO_IMAGE != eglImage); m_waylandEglExtensionProcs.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage); m_waylandEglExtensionProcs.eglDestroyImageKHR(eglImage); @@ -125,13 +149,13 @@ namespace ramses_internal // So far, the only value of the 'flags' parameter to zwp_linux_buffer_params_v1::create() that // we support is for inverting along the Y axis. // NOLINTNEXTLINE(hicpp-signed-bitwise) - if (dmabuf->getFlags() & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) + if ((dmabuf->getFlags() & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) != 0u) { LOG_ERROR(CONTEXT_RENDERER, "TextureUploadingAdapter_Wayland::importDmabufToEglImage: DMA buf has unsupported flags=" << dmabuf->getFlags() << "! Creating EGL image failed!"); return nullptr; } - std::array attribs; + std::array attribs{}; int atti = 0; attribs[atti++] = EGL_WIDTH; @@ -139,7 +163,7 @@ namespace ramses_internal attribs[atti++] = EGL_HEIGHT; attribs[atti++] = dmabuf->getHeight(); attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; - attribs[atti++] = dmabuf->getFormat(); + attribs[atti++] = static_cast(dmabuf->getFormat()); PUSH_DISABLE_C_STYLE_CAST_WARNING bool hasModifier = dmabuf->getModifier(0) != DRM_FORMAT_MOD_INVALID; @@ -151,16 +175,16 @@ namespace ramses_internal attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; attribs[atti++] = dmabuf->getFd(0); attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; - attribs[atti++] = dmabuf->getOffset(0); + attribs[atti++] = static_cast(dmabuf->getOffset(0)); attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attribs[atti++] = dmabuf->getStride(0); + attribs[atti++] = static_cast(dmabuf->getStride(0)); if (hasModifier) { attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attribs[atti++] = dmabuf->getModifier(0) & 0xffffffff; + attribs[atti++] = static_cast(dmabuf->getModifier(0) & 0xffffffff); attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attribs[atti++] = dmabuf->getModifier(0) >> 32; + attribs[atti++] = static_cast(dmabuf->getModifier(0) >> 32); } } @@ -170,16 +194,16 @@ namespace ramses_internal attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; attribs[atti++] = dmabuf->getFd(1); attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; - attribs[atti++] = dmabuf->getOffset(1); + attribs[atti++] = static_cast(dmabuf->getOffset(1)); attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; - attribs[atti++] = dmabuf->getStride(1); + attribs[atti++] = static_cast(dmabuf->getStride(1)); if (hasModifier) { attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; - attribs[atti++] = dmabuf->getModifier(1) & 0xffffffff; + attribs[atti++] = static_cast(dmabuf->getModifier(1) & 0xffffffff); attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; - attribs[atti++] = dmabuf->getModifier(1) >> 32; + attribs[atti++] = static_cast(dmabuf->getModifier(1) >> 32); } } @@ -189,16 +213,16 @@ namespace ramses_internal attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; attribs[atti++] = dmabuf->getFd(2); attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; - attribs[atti++] = dmabuf->getOffset(2); + attribs[atti++] = static_cast(dmabuf->getOffset(2)); attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; - attribs[atti++] = dmabuf->getStride(2); + attribs[atti++] = static_cast(dmabuf->getStride(2)); if (hasModifier) { attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; - attribs[atti++] = dmabuf->getModifier(2) & 0xffffffff; + attribs[atti++] = static_cast(dmabuf->getModifier(2) & 0xffffffff); attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; - attribs[atti++] = dmabuf->getModifier(2) >> 32; + attribs[atti++] = static_cast(dmabuf->getModifier(2) >> 32); } } @@ -208,22 +232,22 @@ namespace ramses_internal attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT; attribs[atti++] = dmabuf->getFd(3); attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT; - attribs[atti++] = dmabuf->getOffset(3); + attribs[atti++] = static_cast(dmabuf->getOffset(3)); attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT; - attribs[atti++] = dmabuf->getStride(3); + attribs[atti++] = static_cast(dmabuf->getStride(3)); if (hasModifier) { attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT; - attribs[atti++] = dmabuf->getModifier(3) & 0xffffffff; + attribs[atti++] = static_cast(dmabuf->getModifier(3) & 0xffffffff); attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT; - attribs[atti++] = dmabuf->getModifier(3) >> 32; + attribs[atti++] = static_cast(dmabuf->getModifier(3) >> 32); } } attribs[atti++] = EGL_NONE; - const EGLImage eglImage = m_waylandEglExtensionProcs.eglCreateImageKHR(nullptr, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); + EGLImage eglImage = m_waylandEglExtensionProcs.eglCreateImageKHR(nullptr, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs.data()); if (EGL_NO_IMAGE == eglImage) { LOG_ERROR(CONTEXT_RENDERER, "TextureUploadingAdapter_Wayland::importDmabufToEglImage: Creating EGL image failed width egl error code : " << eglGetError()); @@ -239,7 +263,7 @@ namespace ramses_internal bool TextureUploadingAdapter_Wayland::uploadTextureFromLinuxDmabuf(DeviceResourceHandle textureHandle, LinuxDmabufBufferData* dmabuf) { - DmabufEglImage* image; + DmabufEglImage* image = nullptr; auto iter = m_dmabufEglImagesMap.find(dmabuf); // Lazily import it @@ -249,7 +273,8 @@ namespace ramses_internal if(image == nullptr) return false; - dmabuf->setDestroyCallback(std::bind(&TextureUploadingAdapter_Wayland::handleDmabufDestroy, this, std::placeholders::_1)); + dmabuf->setDestroyCallback([this](LinuxDmabufBufferData* buf) { handleDmabufDestroy(buf); }); + m_dmabufEglImagesMap[dmabuf] = image; } else diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/TextureUploadingAdapter_Wayland.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.h similarity index 75% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/TextureUploadingAdapter_Wayland.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.h index 766318405..a3ef4c8de 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/TextureUploadingAdapter_Wayland.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTUREUPLOADINGADAPTER_WAYLAND_H -#define RAMSES_TEXTUREUPLOADINGADAPTER_WAYLAND_H +#pragma once -#include "Platform_Base/TextureUploadingAdapter_Base.h" -#include "WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h" +#include "internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" #include -namespace ramses_internal +namespace ramses::internal { class LinuxDmabufBufferData; @@ -23,9 +22,11 @@ namespace ramses_internal TextureUploadingAdapter_Wayland(IDevice& device, wl_display* waylandWindowDisplay, wl_display* embeddedCompositingDisplay); ~TextureUploadingAdapter_Wayland() override; - void uploadTextureFromWaylandResource(DeviceResourceHandle textureHandle, EGLClientBuffer bufferResource); + void uploadTextureFromWaylandResource(DeviceResourceHandle textureHandle, wl_resource* bufferResource); bool uploadTextureFromLinuxDmabuf(DeviceResourceHandle textureHandle, LinuxDmabufBufferData* dmabuf); + const WaylandEGLExtensionProcs& getEGLExtension() const; + private: class DmabufEglImage { @@ -46,12 +47,15 @@ namespace ramses_internal DmabufEglImage* importDmabufToEglImage(LinuxDmabufBufferData* dmabuf); - const WaylandEGLExtensionProcs m_waylandEglExtensionProcs; - wl_display* const m_embeddedCompositingDisplay; + const WaylandEGLExtensionProcs m_waylandEglExtensionProcs; + wl_display* const m_embeddedCompositingDisplay; std::unordered_map m_dmabufEglImagesMap; friend class DmabufEglImage; }; -} -#endif + inline const WaylandEGLExtensionProcs& TextureUploadingAdapter_Wayland::getEGLExtension() const + { + return m_waylandEglExtensionProcs; + } +} diff --git a/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.cpp new file mode 100644 index 000000000..b70c4fcf3 --- /dev/null +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.cpp @@ -0,0 +1,133 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/RendererLib/RendererLogContext.h" +#include + +namespace ramses::internal +{ + WaylandBuffer::WaylandBuffer(WaylandBufferResource& bufferResource, IEmbeddedCompositor_Wayland& compositor) + : m_bufferResource(*bufferResource.clone()) + , m_compositor(compositor) + { + LOG_DEBUG(CONTEXT_RENDERER, "WaylandBuffer::WaylandBuffer"); + + m_destroyListener.notify = ClientBufferDestroyed; + m_bufferResource.addDestroyListener(&m_destroyListener); + } + + WaylandBuffer::~WaylandBuffer() + { + LOG_DEBUG(CONTEXT_RENDERER, "WaylandBuffer::~WaylandBuffer"); + delete &m_bufferResource; + } + + WaylandBufferResource& WaylandBuffer::getResource() const + { + return m_bufferResource; + } + + void WaylandBuffer::ClientBufferDestroyed(wl_listener* listener, [[maybe_unused]] void* data) + { + LOG_DEBUG(CONTEXT_RENDERER, "WaylandBuffer::ClientBufferDestroyed data: " << data); + + WaylandBuffer* buffer(nullptr); + +WARNINGS_PUSH +WARNING_DISABLE_LINUX(-Winvalid-offsetof) +WARNING_DISABLE_LINUX(-Wcast-align) +WARNING_DISABLE_LINUX(-Wold-style-cast) + + buffer = wl_container_of(listener, buffer, m_destroyListener); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) + +WARNINGS_POP + + assert(buffer != nullptr); + + buffer->m_compositor.handleBufferDestroyed(*buffer); + delete buffer; + } + + /** Increase reference counter of buffer. */ + void WaylandBuffer::reference() + { + m_refCount++; + } + + /** Decrease reference counter of buffer. */ + void WaylandBuffer::release() + { + assert(m_refCount > 0); + + if (--m_refCount == 0) + { + m_bufferResource.bufferSendRelease(); + } + } + + bool WaylandBuffer::isSharedMemoryBuffer() const + { + return m_bufferResource.bufferGetSharedMemoryData() != nullptr; + } + + void WaylandBuffer::logInfos(RendererLogContext& contexti, const WaylandEGLExtensionProcs &eglExt) const + { + auto res = m_bufferResource.getLowLevelHandle(); + contexti << "Buffer: res:" << static_cast(res); + wl_shm_buffer* shm = wl_shm_buffer_get(res); + if (shm != nullptr) + { + contexti << " type:shm w:" << wl_shm_buffer_get_width(shm) << " h:" << wl_shm_buffer_get_height(shm); + contexti << " format:" << wl_shm_buffer_get_format(shm); + } + else if ((res != nullptr) && (wl_resource_instance_of(res, &wl_buffer_interface, &LinuxDmabufBuffer::m_bufferInterface) != 0)) + { + contexti << " type:dma"; + auto dmabuf = static_cast(wl_resource_get_user_data(res)); + if (dmabuf != nullptr) + { + contexti << " w:" << dmabuf->getWidth() << " h:" << dmabuf->getHeight(); + contexti << " planes:" << dmabuf->getNumPlanes() << " format:" << dmabuf->getFormat(); + contexti << " flags:" << dmabuf->getFlags(); + } + } + else + { + EGLint textureFormat = 0; + EGLint textureWidth = 0; + EGLint textureHeight = 0; + auto bufferResource = m_bufferResource.getLowLevelHandle(); + contexti << " type:EGL_WAYLAND_BUFFER_WL"; + EGLBoolean ret = eglExt.eglQueryWaylandBufferWL(bufferResource, EGL_TEXTURE_FORMAT, &textureFormat); + if (ret == EGL_FALSE) + { + contexti << "eglQueryWaylandBufferWL(EGL_TEXTURE_FORMAT) failed" << RendererLogContext::NewLine; + } + ret = eglExt.eglQueryWaylandBufferWL(bufferResource, EGL_WIDTH, &textureWidth); + if (ret == EGL_FALSE) + { + contexti << "eglQueryWaylandBufferWL(EGL_WIDTH) failed" << RendererLogContext::NewLine; + } + ret = eglExt.eglQueryWaylandBufferWL(bufferResource, EGL_HEIGHT, &textureHeight); + if (ret == EGL_FALSE) + { + contexti << "eglQueryWaylandBufferWL(EGL_HEIGHT) failed" << RendererLogContext::NewLine; + } + contexti << " w:" << textureWidth << " h:" << textureHeight + << " format:" << WaylandEGLExtensionProcs::getTextureFormatName(textureFormat) << " (" << textureFormat << ")"; + } + contexti << RendererLogContext::NewLine; + } +} diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandBuffer.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.h similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandBuffer.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.h index eb28bb2fa..17f1e4d8f 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandBuffer.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.h @@ -5,15 +5,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDBUFFER_H -#define RAMSES_WAYLANDBUFFER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" -#include "EmbeddedCompositor_Wayland/IWaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h" #include -namespace ramses_internal +#include + +namespace ramses::internal { class IEmbeddedCompositor_Wayland; @@ -28,6 +28,7 @@ namespace ramses_internal void reference() override; void release() override; [[nodiscard]] bool isSharedMemoryBuffer() const override; + void logInfos(RendererLogContext& context, const WaylandEGLExtensionProcs& eglExt) const override; private: @@ -35,7 +36,7 @@ namespace ramses_internal WaylandBufferResource& m_bufferResource; - wl_listener m_destroyListener; + wl_listener m_destroyListener{}; /** Reference counter. * The counter is increased each time the buffer is attached to a surface @@ -47,11 +48,9 @@ namespace ramses_internal * a surface and then destroy it right after wl_surface.commit. * So we will keep this buffer unitl the reference counter * drops to zero. */ - int m_refCount; + int m_refCount{0}; IEmbeddedCompositor_Wayland& m_compositor; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBufferResource.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.cpp similarity index 58% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBufferResource.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.cpp index 377c33789..073c17a40 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandBufferResource.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabuf.h" -namespace ramses_internal +namespace ramses::internal { - WaylandBufferResource::WaylandBufferResource() - { - } + WaylandBufferResource::WaylandBufferResource() = default; WaylandBufferResource::WaylandBufferResource(wl_resource* resource) : NativeWaylandResource(resource) @@ -24,7 +24,7 @@ namespace ramses_internal wl_buffer_send_release(m_resource); } - int32_t WaylandBufferResource::bufferGetSharedMemoryWidth() const + int32_t WaylandBufferResource::getWidth() const { wl_shm_buffer* sharedMemoryBuffer = wl_shm_buffer_get(m_resource); @@ -32,13 +32,15 @@ namespace ramses_internal { return wl_shm_buffer_get_width(sharedMemoryBuffer); } - else + if ((m_resource != nullptr) && (wl_resource_instance_of(m_resource, &wl_buffer_interface, &LinuxDmabufBuffer::m_bufferInterface) != 0)) { - return 0; + auto dmabuf = static_cast(wl_resource_get_user_data(m_resource)); + return (dmabuf != nullptr) ? dmabuf->getWidth() : 0; } + return 0; } - int32_t WaylandBufferResource::bufferGetSharedMemoryHeight() const + int32_t WaylandBufferResource::getHeight() const { wl_shm_buffer* sharedMemoryBuffer = wl_shm_buffer_get(m_resource); @@ -46,10 +48,12 @@ namespace ramses_internal { return wl_shm_buffer_get_height(sharedMemoryBuffer); } - else + if ((m_resource != nullptr) && (wl_resource_instance_of(m_resource, &wl_buffer_interface, &LinuxDmabufBuffer::m_bufferInterface) != 0)) { - return 0; + auto dmabuf = static_cast(wl_resource_get_user_data(m_resource)); + return (dmabuf != nullptr) ? dmabuf->getHeight() : 0; } + return 0; } const void* WaylandBufferResource::bufferGetSharedMemoryData() const @@ -59,10 +63,7 @@ namespace ramses_internal { return wl_shm_buffer_get_data(sharedMemoryBuffer); } - else - { - return nullptr; - } + return nullptr; } WaylandBufferResource* WaylandBufferResource::clone() const diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandBufferResource.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h similarity index 72% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandBufferResource.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h index 06b4845d7..6c43cd479 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandBufferResource.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h @@ -6,12 +6,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDBUFFERRESOURCE_H -#define RAMSES_WAYLANDBUFFERRESOURCE_H +#pragma once -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" -namespace ramses_internal +namespace ramses::internal { class WaylandBufferResource : public NativeWaylandResource { @@ -19,11 +18,9 @@ namespace ramses_internal WaylandBufferResource(); explicit WaylandBufferResource(wl_resource* resource); virtual void bufferSendRelease(); - [[nodiscard]] virtual int32_t bufferGetSharedMemoryWidth() const; - [[nodiscard]] virtual int32_t bufferGetSharedMemoryHeight() const; + [[nodiscard]] virtual int32_t getWidth() const; + [[nodiscard]] virtual int32_t getHeight() const; [[nodiscard]] virtual const void* bufferGetSharedMemoryData() const; [[nodiscard]] virtual WaylandBufferResource* clone() const; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCallbackResource.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCallbackResource.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.cpp index 309ebbbfa..3e2b6ee56 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCallbackResource.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.cpp @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandCallbackResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h" -namespace ramses_internal +namespace ramses::internal { - WaylandCallbackResource::WaylandCallbackResource() - { - } + WaylandCallbackResource::WaylandCallbackResource() = default; WaylandCallbackResource::WaylandCallbackResource(wl_resource* resource): NativeWaylandResource(resource) { diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCallbackResource.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h similarity index 80% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCallbackResource.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h index f90d46f0e..ebdbe8332 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCallbackResource.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h @@ -5,12 +5,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCALLBACKRESOURCE_H -#define RAMSES_WAYLANDCALLBACKRESOURCE_H +#pragma once -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" -namespace ramses_internal +namespace ramses::internal { class WaylandCallbackResource : public NativeWaylandResource { @@ -20,5 +19,3 @@ namespace ramses_internal virtual void callbackSendDone(uint32_t callbackData); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandClient.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandClient.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.cpp index 53c794d56..87cbf85c4 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandClient.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/WaylandCallbackResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandClient::WaylandClient(wl_client* client) : m_client(client) @@ -23,9 +23,9 @@ namespace ramses_internal { if(m_credentials.getProcessId() == -1) { - pid_t processId; - uid_t userId; - gid_t groupId; + pid_t processId = 0; + uid_t userId = 0; + gid_t groupId = 0; wl_client_get_credentials(m_client, &processId, &userId, &groupId); m_credentials = WaylandClientCredentials(processId, userId, groupId); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandClient.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandClient.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h index 7fae6dcc9..f5ff9c235 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandClient.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCLIENT_H -#define RAMSES_WAYLANDCLIENT_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class WaylandClient: public IWaylandClient { @@ -32,5 +31,3 @@ namespace ramses_internal mutable WaylandClientCredentials m_credentials; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandClientCredentials.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.cpp similarity index 90% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandClientCredentials.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.cpp index 5b26432e0..7639ef640 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandClientCredentials.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandClientCredentials.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.h" #include #include -namespace ramses_internal +namespace ramses::internal { WaylandClientCredentials::WaylandClientCredentials( pid_t processId, diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandClientCredentials.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.h similarity index 77% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandClientCredentials.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.h index 5ea349157..f7255980b 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandClientCredentials.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandClientCredentials.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCLIENTCREDENTIALS_H -#define RAMSES_WAYLANDCLIENTCREDENTIALS_H +#pragma once -#include "PlatformAbstraction/FmtBase.h" +#include "internal/PlatformAbstraction/FmtBase.h" #include -namespace ramses_internal +namespace ramses::internal { class WaylandClientCredentials { @@ -35,10 +34,10 @@ namespace ramses_internal } template <> -struct fmt::formatter : public ramses_internal::SimpleFormatterBase +struct fmt::formatter : public ramses::internal::SimpleFormatterBase { template - constexpr auto format(const ramses_internal::WaylandClientCredentials& cred, FormatContext& ctx) + constexpr auto format(const ramses::internal::WaylandClientCredentials& cred, FormatContext& ctx) { return fmt::format_to(ctx.out(), "[pid:{} uid:{} gid:{}]", cred.getProcessId(), @@ -46,5 +45,3 @@ struct fmt::formatter : public ramses cred.getGroupId()); } }; - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCompositorConnection.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.cpp similarity index 80% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCompositorConnection.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.cpp index a832488c8..c76248175 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCompositorConnection.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/WaylandSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandRegion.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandCompositorConnection::WaylandCompositorConnection(IWaylandClient& client, uint32_t version, uint32_t id, IEmbeddedCompositor_Wayland& embeddedCompositor) : m_clientCredentials(client.getCredentials()) @@ -24,7 +24,7 @@ namespace ramses_internal { LOG_DEBUG(CONTEXT_RENDERER, "WaylandCompositorConnection::WaylandCompositorConnection Connection created"); - m_resource = client.resourceCreate(&wl_compositor_interface, version, id); + m_resource = client.resourceCreate(&wl_compositor_interface, static_cast(version), id); if (nullptr != m_resource) { m_resource->setImplementation(&m_compositorInterface, this, ResourceDestroyedCallback); @@ -71,7 +71,7 @@ namespace ramses_internal { LOG_TRACE(CONTEXT_RENDERER, "WaylandCompositorConnection::compositorCreateSurface"); - new WaylandSurface(m_embeddedCompositor, client, m_version, id); + new WaylandSurface(m_embeddedCompositor, client, static_cast(m_version), id); // Registers callback for destruction, when the corresponding wl_surface is destroyed } @@ -85,7 +85,7 @@ namespace ramses_internal void WaylandCompositorConnection::ResourceDestroyedCallback(wl_resource* clientResource) { - WaylandCompositorConnection* compositorConnection = + auto* compositorConnection = static_cast(wl_resource_get_user_data(clientResource)); compositorConnection->resourceDestroyed(); @@ -95,14 +95,14 @@ namespace ramses_internal void WaylandCompositorConnection::CompositorCreateSurfaceCallback(wl_client* client, wl_resource* clientResource, uint32_t id) { WaylandClient waylandClient(client); - WaylandCompositorConnection* waylandCompositorConnection = static_cast(wl_resource_get_user_data(clientResource)); + auto* waylandCompositorConnection = static_cast(wl_resource_get_user_data(clientResource)); waylandCompositorConnection->compositorCreateSurface(waylandClient, id); } void WaylandCompositorConnection::CompositorCreateRegionCallback(wl_client* client, wl_resource* clientResource, uint32_t id) { WaylandClient waylandClient(client); - WaylandCompositorConnection* waylandCompositorConnection = static_cast(wl_resource_get_user_data(clientResource)); + auto* waylandCompositorConnection = static_cast(wl_resource_get_user_data(clientResource)); waylandCompositorConnection->compositorCreateRegion(waylandClient, id); } } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCompositorConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h similarity index 88% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCompositorConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h index 7e6d93350..3ecbc4fa3 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCompositorConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h @@ -5,14 +5,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCOMPOSITORCONNECTION_H -#define RAMSES_WAYLANDCOMPOSITORCONNECTION_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositor_Wayland; @@ -40,6 +39,7 @@ namespace ramses_internal const struct Compositor_Interface : private wl_compositor_interface { Compositor_Interface() + : wl_compositor_interface() { create_surface = CompositorCreateSurfaceCallback; create_region = CompositorCreateRegionCallback; @@ -47,5 +47,3 @@ namespace ramses_internal } m_compositorInterface; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCompositorGlobal.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.cpp similarity index 76% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCompositorGlobal.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.cpp index 8ea7ffdce..53c795993 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandCompositorGlobal.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandCompositorGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/IWaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/IWaylandGlobal.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandCompositorGlobal::WaylandCompositorGlobal(IEmbeddedCompositor_Wayland& compositor) : m_compositor(compositor) @@ -59,7 +59,7 @@ namespace ramses_internal void WaylandCompositorGlobal::compositorBind(IWaylandClient& client, uint32_t version, uint32_t id) { - WaylandCompositorConnection* waylandCompositorConnection = new WaylandCompositorConnection(client, version, id, m_compositor); + auto* waylandCompositorConnection = new WaylandCompositorConnection(client, version, id, m_compositor); if (waylandCompositorConnection->wasSuccessfullyInitialized()) { m_compositor.addWaylandCompositorConnection(*waylandCompositorConnection); @@ -72,7 +72,7 @@ namespace ramses_internal void WaylandCompositorGlobal::CompositorBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) { - WaylandCompositorGlobal* waylandCompositorGlobal = static_cast(data); + auto* waylandCompositorGlobal = static_cast(data); WaylandClient waylandClient(client); waylandCompositorGlobal->compositorBind(waylandClient, version, id); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCompositorGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCompositorGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.h index b965c161f..771928b18 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandCompositorGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorGlobal.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCOMPOSITORGLOBAL_H -#define RAMSES_WAYLANDCOMPOSITORGLOBAL_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandCompositorGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorGlobal.h" struct wl_client; struct wl_global; -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositor_Wayland; class IWaylandGlobal; @@ -35,5 +34,3 @@ namespace ramses_internal IWaylandGlobal* m_waylandGlobal = nullptr; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandDisplay.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandDisplay.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.cpp index ad9b35a8b..385540edd 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandDisplay.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.cpp @@ -6,21 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/WaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandGlobal.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { - WaylandDisplay::WaylandDisplay() - { - } + WaylandDisplay::WaylandDisplay() = default; bool WaylandDisplay::init(const std::string& socketName, const std::string& socketGroupName, uint32_t socketPermissions, int socketFD) { @@ -55,18 +53,18 @@ namespace ramses_internal bool WaylandDisplay::addSocketToDisplay(const std::string& socketName, const std::string& socketGroupName, uint32_t socketPermissions, int socketFD) { - const bool socketNameProvided = socketName.size() > 0u; + const bool socketNameProvided = !socketName.empty(); const bool socketFDProvided = socketFD >= 0; if (socketFDProvided && !socketNameProvided) { return addSocketToDisplayWithFD(socketFD); } - else if (socketNameProvided && !socketFDProvided) + if (socketNameProvided && !socketFDProvided) { return addSocketToDisplayWithName(socketName, socketGroupName, socketPermissions); } - else if (socketNameProvided && socketFDProvided) + if (socketNameProvided && socketFDProvided) { LOG_ERROR(CONTEXT_RENDERER, "WaylandDisplay::addSocketToDisplay(): Failed to add wayland display because " @@ -96,13 +94,10 @@ namespace ramses_internal "systemd"); return true; } - else - { - LOG_ERROR(CONTEXT_RENDERER, - "WaylandDisplay::addSocketToDisplayWithFD(): Failed to add wayland display on embedded compositor socket " - "provided by RendererConfig::setWaylandEmbeddedCompositingSocketFD()"); - return false; - } + LOG_ERROR(CONTEXT_RENDERER, + "WaylandDisplay::addSocketToDisplayWithFD(): Failed to add wayland display on embedded compositor socket " + "provided by RendererConfig::setWaylandEmbeddedCompositingSocketFD()"); + return false; } bool WaylandDisplay::addSocketToDisplayWithName(const std::string& socketName, const std::string& socketGroupName, uint32_t socketPermissions) @@ -116,8 +111,8 @@ namespace ramses_internal return false; } - const std::string socketFullPath = getSocketFullPath(socketName); - if (!applyGroupToEmbeddedCompositingSocket(socketFullPath, socketGroupName)) + const std::string socketFullPath = GetSocketFullPath(socketName); + if (!ApplyGroupToEmbeddedCompositingSocket(socketFullPath, socketGroupName)) { LOG_ERROR(CONTEXT_RENDERER, "WaylandDisplay::addSocketToDisplayWithName(): Failed to set group '" << socketGroupName << "' on embedded compositor socket :" @@ -125,7 +120,7 @@ namespace ramses_internal return false; } - if (!applyPermissionsToEmbeddedCompositingSocket(socketFullPath, socketPermissions)) + if (!ApplyPermissionsToEmbeddedCompositingSocket(socketFullPath, socketPermissions)) { LOG_ERROR(CONTEXT_RENDERER, "WaylandDisplay::addSocketToDisplayWithName(): Failed to set permissions " << socketPermissions << " on embedded compositor socket :" @@ -140,20 +135,20 @@ namespace ramses_internal return true; } - std::string WaylandDisplay::getSocketFullPath(const std::string& socketName) const + std::string WaylandDisplay::GetSocketFullPath(const std::string& socketName) { std::string XDGRuntimeDir; PlatformEnvironmentVariables::get("XDG_RUNTIME_DIR", XDGRuntimeDir); return std::string(fmt::format("{}/{}", XDGRuntimeDir, socketName)); } - bool WaylandDisplay::applyGroupToEmbeddedCompositingSocket(const std::string& socketFullPath, const std::string& socketGroupName) + bool WaylandDisplay::ApplyGroupToEmbeddedCompositingSocket(const std::string& socketFullPath, const std::string& socketGroupName) { - if (socketGroupName.size() > 0u) + if (!socketGroupName.empty()) { - group permissionGroup; + group permissionGroup{}; group* permissionGroupResult = nullptr; - std::array bufferForStringFields; + std::array bufferForStringFields; // NOLINT(cppcoreguidelines-pro-type-member-init) const int status = getgrnam_r(socketGroupName.c_str(), &permissionGroup, @@ -163,7 +158,7 @@ namespace ramses_internal if (0 != status) { LOG_ERROR(CONTEXT_RENDERER, - "WaylandDisplay::applyGroupToEmbeddedCompositingSocket(): Could not " + "WaylandDisplay::ApplyGroupToEmbeddedCompositingSocket(): Could not " "get group file entry for group name :" << socketGroupName << ". Error :" << status); return false; @@ -171,7 +166,7 @@ namespace ramses_internal if (nullptr == permissionGroupResult) { LOG_ERROR(CONTEXT_RENDERER, - "WaylandDisplay::applyGroupToEmbeddedCompositingSocket(): Could not " + "WaylandDisplay::ApplyGroupToEmbeddedCompositingSocket(): Could not " "find group for socket with group name :" << socketGroupName); return false; @@ -183,14 +178,14 @@ namespace ramses_internal if (0 != chownStatusSocket) { LOG_ERROR(CONTEXT_RENDERER, - "WaylandDisplay::applyGroupToEmbeddedCompositingSocket(): Could not " + "WaylandDisplay::ApplyGroupToEmbeddedCompositingSocket(): Could not " "set group :" << socketGroupName << " on :" << socketFullPath << ". Error:" << strerror(errno)); return false; } LOG_INFO(CONTEXT_RENDERER, - "WaylandDisplay::applyGroupToEmbeddedCompositingSocket(): Successfully set " + "WaylandDisplay::ApplyGroupToEmbeddedCompositingSocket(): Successfully set " "group :" << socketGroupName << " on socket :" << socketFullPath); @@ -199,21 +194,21 @@ namespace ramses_internal if (0 != chownStatusLockfile) { LOG_ERROR(CONTEXT_RENDERER, - "WaylandDisplay::::applyGroupToEmbeddedCompositingSocket(): Could not " + "WaylandDisplay::::ApplyGroupToEmbeddedCompositingSocket(): Could not " "set group :" << socketGroupName << " on :" << fullSocketLockFilePath << ". Error:" << strerror(errno)); return false; } LOG_INFO(CONTEXT_RENDERER, - "WaylandDisplay::::applyGroupToEmbeddedCompositingSocket(): Successfully " + "WaylandDisplay::::ApplyGroupToEmbeddedCompositingSocket(): Successfully " "set group :" << socketGroupName << " on :" << fullSocketLockFilePath); } return true; } - bool WaylandDisplay::applyPermissionsToEmbeddedCompositingSocket(const std::string& socketFullPath, uint32_t socketPermissions) + bool WaylandDisplay::ApplyPermissionsToEmbeddedCompositingSocket(const std::string& socketFullPath, uint32_t socketPermissions) { // if none given use default ug+rw if (socketPermissions == 0) @@ -222,13 +217,13 @@ namespace ramses_internal if (0 != ::chmod(socketFullPath.c_str(), socketPermissions)) { LOG_ERROR(CONTEXT_RENDERER, - "WaylandDisplay::applyPermissionsToEmbeddedCompositingSocket(): Could not set permissions: " + "WaylandDisplay::ApplyPermissionsToEmbeddedCompositingSocket(): Could not set permissions: " << socketPermissions << " on :" << socketFullPath << ". Error:" << strerror(errno)); return false; } LOG_INFO(CONTEXT_RENDERER, - "WaylandDisplay::applyPermissionsToEmbeddedCompositingSocket(): Successfully set permissions: " + "WaylandDisplay::ApplyPermissionsToEmbeddedCompositingSocket(): Successfully set permissions: " << socketPermissions << " on socket :" << socketFullPath); return true; } @@ -245,10 +240,7 @@ namespace ramses_internal { return new WaylandGlobal(global); } - else - { - return nullptr; - } + return nullptr; } void WaylandDisplay::dispatchEventLoop() diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandDisplay.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h similarity index 76% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandDisplay.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h index abd5c2c30..c022edf19 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandDisplay.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDDISPLAY_H -#define RAMSES_WAYLANDDISPLAY_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandDisplay.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class WaylandDisplay: public IWaylandDisplay @@ -32,12 +31,10 @@ namespace ramses_internal bool addSocketToDisplay(const std::string& socketName, const std::string& socketGroupName, uint32_t socketPermissions, int socketFD); bool addSocketToDisplayWithFD(int socketFD); bool addSocketToDisplayWithName(const std::string& socketName, const std::string& socketGroupName, uint32_t socketPermissions); - bool applyGroupToEmbeddedCompositingSocket(const std::string& socketFullPath, const std::string& socketGroupName); - bool applyPermissionsToEmbeddedCompositingSocket(const std::string& socketFullPath, uint32_t socketPermissions); - [[nodiscard]] std::string getSocketFullPath(const std::string& socketName) const; + static bool ApplyGroupToEmbeddedCompositingSocket(const std::string& socketFullPath, const std::string& socketGroupName); + static bool ApplyPermissionsToEmbeddedCompositingSocket(const std::string& socketFullPath, uint32_t socketPermissions); + [[nodiscard]] static std::string GetSocketFullPath(const std::string& socketName); wl_display* m_display = nullptr; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandGlobal.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.cpp similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandGlobal.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.cpp index 9191bbf32..570813ab3 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandGlobal.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandGlobal::WaylandGlobal(wl_global* global) : m_global(global) diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.h similarity index 82% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.h index e03c0ce0b..911622fb9 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.h @@ -5,13 +5,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDGLOBAL_H -#define RAMSES_WAYLANDGLOBAL_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class WaylandGlobal : public IWaylandGlobal { @@ -23,5 +22,3 @@ namespace ramses_internal wl_global* m_global; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVIApplicationConnection.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.cpp similarity index 79% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVIApplicationConnection.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.cpp index 34461e210..9146fe4fc 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVIApplicationConnection.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandIVIApplicationConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandIVISurface.h" -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandIVIApplicationConnection::WaylandIVIApplicationConnection(IWaylandClient& client, uint32_t version, @@ -24,7 +24,7 @@ namespace ramses_internal { LOG_DEBUG(CONTEXT_RENDERER, "WaylandIVIApplicationConnection::WaylandIVIApplicationConnection Connection created"); - m_resource = client.resourceCreate(&ivi_application_interface, version, id); + m_resource = client.resourceCreate(&ivi_application_interface, static_cast(version), id); if (m_resource) { LOG_INFO(CONTEXT_RENDERER, "WaylandIVIApplicationConnection::WaylandIVIApplicationConnection(): ivi application interface is now provided " << m_clientCredentials); @@ -68,19 +68,16 @@ namespace ramses_internal } void WaylandIVIApplicationConnection::iviApplicationIVISurfaceCreate(IWaylandClient& client, - uint32_t iviId, + WaylandIviSurfaceId iviId, INativeWaylandResource& surfaceResource, uint32_t id) { - LOG_INFO(CONTEXT_RENDERER, - "WaylandIVIApplicationConnection::iviApplicationConnectionIVISurfaceCreate New ivi surface created " - "with ivi-id " - << iviId); + LOG_INFO(CONTEXT_RENDERER, "WaylandIVIApplicationConnection::iviApplicationIVISurfaceCreate " << iviId); - WaylandSurface* clientSurface = reinterpret_cast(surfaceResource.getUserData()); + auto* clientSurface = reinterpret_cast(surfaceResource.getUserData()); assert(nullptr != m_resource); - WaylandIVISurface* waylandIVISurface = new WaylandIVISurface(client, *m_resource, WaylandIviSurfaceId(iviId), clientSurface, id, m_compositor); + auto* waylandIVISurface = new WaylandIVISurface(client, *m_resource, WaylandIviSurfaceId(iviId), clientSurface, id, m_compositor); // Registers callback for destruction, when the corresponding wl_ivi_surface is destroyed if (!waylandIVISurface->wasSuccessfullyInitialized()) @@ -91,7 +88,7 @@ namespace ramses_internal void WaylandIVIApplicationConnection::ResourceDestroyedCallback(wl_resource* iviApplicationConnectionResource) { - WaylandIVIApplicationConnection* iviApplicationConnection = + auto* iviApplicationConnection = static_cast(wl_resource_get_user_data(iviApplicationConnectionResource)); iviApplicationConnection->resourceDestroyed(); @@ -105,13 +102,13 @@ namespace ramses_internal wl_resource* surfaceResource, uint32_t id) { + const WaylandIviSurfaceId iviSurfaceId{iviId}; LOG_INFO(CONTEXT_RENDERER, - "WaylandIVIApplicationConnection::IVIApplicationIVISurfaceCreateCallback iviId: " << iviId - << " id: " << id); - WaylandIVIApplicationConnection* iviApplicationConnection = + "WaylandIVIApplicationConnection::IVIApplicationIVISurfaceCreateCallback " << iviSurfaceId << " id:" << id); + auto* iviApplicationConnection = static_cast(wl_resource_get_user_data(iviApplicationConnectionResource)); WaylandClient waylandClient(client); NativeWaylandResource waylandSurfaceResource(surfaceResource); - iviApplicationConnection->iviApplicationIVISurfaceCreate(waylandClient, iviId, waylandSurfaceResource, id); + iviApplicationConnection->iviApplicationIVISurfaceCreate(waylandClient, iviSurfaceId, waylandSurfaceResource, id); } } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVIApplicationConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.h similarity index 84% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVIApplicationConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.h index 8c113fd09..5ff932a00 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVIApplicationConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.h @@ -5,15 +5,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDIVIAPPLICATIONCONNECTION_H -#define RAMSES_WAYLANDIVIAPPLICATIONCONNECTION_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandIVIApplicationConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" #include "ivi-application-server-protocol.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositor_Wayland; @@ -25,7 +24,7 @@ namespace ramses_internal [[nodiscard]] bool wasSuccessfullyInitialized() const; void resourceDestroyed() override; - void iviApplicationIVISurfaceCreate(IWaylandClient& client, uint32_t iviId, INativeWaylandResource& surfaceResource, uint32_t id) override; + void iviApplicationIVISurfaceCreate(IWaylandClient& client, WaylandIviSurfaceId iviId, INativeWaylandResource& surfaceResource, uint32_t id) override; private: static void ResourceDestroyedCallback(wl_resource* iviApplicationConnectionResource); @@ -38,11 +37,10 @@ namespace ramses_internal const struct IVIApplication_Interface : private ivi_application_interface { IVIApplication_Interface() + : ivi_application_interface() { surface_create = IVIApplicationIVISurfaceCreateCallback; } } m_iviApplicationInterface; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVIApplicationGlobal.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.cpp similarity index 76% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVIApplicationGlobal.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.cpp index 9db7b3606..5ef0f3356 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVIApplicationGlobal.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandIVIApplicationGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/WaylandIVIApplicationConnection.h" -#include "EmbeddedCompositor_Wayland/IWaylandGlobal.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandIVIApplicationGlobal::WaylandIVIApplicationGlobal(EmbeddedCompositor_Wayland& compositor) : m_compositor(compositor) @@ -60,7 +60,7 @@ namespace ramses_internal { LOG_INFO(CONTEXT_RENDERER, "WaylandIVIApplicationGlobal::iviApplicationBind"); - WaylandIVIApplicationConnection* waylandIVIApplicationConnection = new WaylandIVIApplicationConnection(client, version, id, m_compositor); + auto* waylandIVIApplicationConnection = new WaylandIVIApplicationConnection(client, version, id, m_compositor); // Registers callback for destruction, when the corresponding resource is destroyed if (!waylandIVIApplicationConnection->wasSuccessfullyInitialized()) @@ -69,10 +69,9 @@ namespace ramses_internal } } - void WaylandIVIApplicationGlobal::IVIApplicationBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) + void WaylandIVIApplicationGlobal::IVIApplicationBindCallback(wl_client* client, void* data, [[maybe_unused]] uint32_t version, uint32_t id) { - UNUSED(version); - WaylandIVIApplicationGlobal* shell = static_cast(data); + auto* shell = static_cast(data); WaylandClient waylandClient(client); shell->iviApplicationBind(waylandClient, version, id); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVIApplicationGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.h similarity index 86% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVIApplicationGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.h index 2e6b91afb..846d35598 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVIApplicationGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationGlobal.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDIVIAPPLICATIONGLOBAL_H -#define RAMSES_WAYLANDIVIAPPLICATIONGLOBAL_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandIVIApplicationGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVIApplicationGlobal.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositor_Wayland; class WaylandDisplay; @@ -37,5 +36,3 @@ namespace ramses_internal EmbeddedCompositor_Wayland& m_compositor; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVISurface.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.cpp similarity index 76% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVISurface.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.cpp index dd390bf2a..32f8d18bb 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandIVISurface.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandIVISurface.h" -#include "EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/IWaylandSurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "Utils/ThreadLocalLogForced.h" - -namespace ramses_internal +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + +namespace ramses::internal { WaylandIVISurface::WaylandIVISurface(IWaylandClient& client, - INativeWaylandResource& iviApplicationConnectionResource, + INativeWaylandResource& iviApplicationConnectionResource, WaylandIviSurfaceId iviSurfaceId, IWaylandSurface* surface, uint32_t id, @@ -26,12 +26,12 @@ namespace ramses_internal , m_compositor(compositor) , m_clientCredentials(client.getCredentials()) { - LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface"); + LOG_TRACE(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface"); if (surface->hasIviSurface()) { - LOG_ERROR(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: failed creating ivi-surface : " << iviSurfaceId - << ". The wayland surface already has an ivi-surface " << surface->getIviSurfaceId() << "attached! " << m_clientCredentials); + LOG_ERROR(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: failed creating: " << iviSurfaceId + << ". The wayland surface already has a surface with " << surface->getIviSurfaceId() << "attached! " << m_clientCredentials); iviApplicationConnectionResource.postError(IVI_APPLICATION_ERROR_IVI_ID, "surface already has a ivi-surface"); } @@ -42,7 +42,7 @@ namespace ramses_internal m_resource = client.resourceCreate(&ivi_surface_interface, iviApplicationConnectionResource.getVersion(), id); if (nullptr != m_resource) { - LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: created succesffully for ivi-surface : " << iviSurfaceId + LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: created successfully: " << iviSurfaceId << " " << m_clientCredentials); m_resource->setImplementation(&m_iviSurfaceInterface, this, ResourceDestroyedCallback); @@ -56,7 +56,7 @@ namespace ramses_internal } else { - LOG_ERROR(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: failed creating ivi-surface : " << iviSurfaceId + LOG_ERROR(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: failed creating " << iviSurfaceId << ". Failed creating wayland resource " << m_clientCredentials); client.postNoMemory(); } @@ -66,9 +66,9 @@ namespace ramses_internal const auto credentialsForOtherClient = m_compositor.findSurfaceForStreamTexture(iviSurfaceId).getClientCredentials(); - LOG_ERROR(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: failed creating ivi-surface : " << iviSurfaceId + LOG_ERROR(CONTEXT_RENDERER, "WaylandIVISurface::WaylandIVISurface: failed creating " << iviSurfaceId << " for " << m_clientCredentials - << ". A wayland surface already eixsts with same ivi-surface id for " << credentialsForOtherClient); + << ". A wayland surface already exists with same ivi-surface id for " << credentialsForOtherClient); iviApplicationConnectionResource.postError(IVI_APPLICATION_ERROR_IVI_ID, "ivi-id is already in use"); } @@ -82,7 +82,7 @@ namespace ramses_internal WaylandIVISurface::~WaylandIVISurface() { - LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::!WaylandIVISurface: wayland ivi sruface destroyed with ivi-id : " << m_iviSurfaceId + LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::~WaylandIVISurface " << m_iviSurfaceId << " " << m_clientCredentials); if (m_surface != nullptr) @@ -120,7 +120,7 @@ namespace ramses_internal void WaylandIVISurface::resourceDestroyed() { - LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::resourceDestroyed"); + LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::resourceDestroyed " << m_iviSurfaceId); // wl_resource is destroyed outside by the Wayland library, so m_resource looses the ownership of the // Wayland resource, so that we don't call wl_resource_destroy. @@ -129,10 +129,9 @@ namespace ramses_internal } - void WaylandIVISurface::IVISurfaceDestroyCallback(wl_client* client, wl_resource* iviSurfaceResource) + void WaylandIVISurface::IVISurfaceDestroyCallback([[maybe_unused]] wl_client* client, wl_resource* iviSurfaceResource) { - UNUSED(client); - WaylandIVISurface* iviSurface = static_cast(wl_resource_get_user_data(iviSurfaceResource)); + auto* iviSurface = static_cast(wl_resource_get_user_data(iviSurfaceResource)); delete iviSurface; } @@ -140,7 +139,7 @@ namespace ramses_internal { LOG_INFO(CONTEXT_RENDERER, "WaylandIVISurface::IVISurfaceDestroyedCallback"); - WaylandIVISurface* iviSurface = static_cast(wl_resource_get_user_data(iviSurfaceResource)); + auto* iviSurface = static_cast(wl_resource_get_user_data(iviSurfaceResource)); iviSurface->resourceDestroyed(); delete iviSurface; } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVISurface.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.h similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVISurface.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.h index 57d63e255..4d7ac8849 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandIVISurface.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDIVISURFACE_H -#define RAMSES_WAYLANDIVISURFACE_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandIVISurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVISurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "ivi-application-server-protocol.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositor_Wayland; class INativeWaylandResource; @@ -38,6 +37,7 @@ namespace ramses_internal const struct IVISurface_Interface : private ivi_surface_interface { IVISurface_Interface() + : ivi_surface_interface() { destroy = IVISurfaceDestroyCallback; } @@ -50,5 +50,3 @@ namespace ramses_internal const WaylandClientCredentials m_clientCredentials; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandOutputConnection.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.cpp similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandOutputConnection.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.cpp index 767efb493..a1139f730 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandOutputConnection.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandOutputConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { WaylandOutputConnection::WaylandOutputConnection(const WaylandOutputParams& waylandOutputParams, IWaylandClient& client, uint32_t version, uint32_t id) : m_waylandOutputParams(waylandOutputParams) @@ -23,7 +23,7 @@ namespace ramses_internal { LOG_DEBUG(CONTEXT_RENDERER, "WaylandOutputConnection::WaylandOutputConnection Connection created"); - m_resource = client.resourceCreate(&wl_output_interface, version, id); + m_resource = client.resourceCreate(&wl_output_interface, static_cast(version), id); if (nullptr != m_resource) { m_resource->setImplementation(&m_outputInterface, this, ResourceDestroyedCallback); @@ -90,17 +90,16 @@ namespace ramses_internal void WaylandOutputConnection::ResourceDestroyedCallback(wl_resource* clientResource) { - WaylandOutputConnection* outputConnection = + auto* outputConnection = static_cast(wl_resource_get_user_data(clientResource)); outputConnection->resourceDestroyed(); delete outputConnection; } - void WaylandOutputConnection::OutputReleaseCallback(wl_client* client, wl_resource *clientResource) + void WaylandOutputConnection::OutputReleaseCallback([[maybe_unused]] wl_client* client, wl_resource *clientResource) { - UNUSED(client); - WaylandOutputConnection* outputConnection = + auto* outputConnection = static_cast(wl_resource_get_user_data(clientResource)); delete outputConnection; } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.h similarity index 86% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.h index fd7857c11..adf14e27e 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.h @@ -5,14 +5,13 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDOUTPUTCONNECTION_H -#define RAMSES_WAYLANDOUTPUTCONNECTION_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "EmbeddedCompositor_Wayland/WaylandOutputParams.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputParams.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class WaylandOutputConnection final { @@ -34,11 +33,10 @@ namespace ramses_internal const struct Output_Interface : public wl_output_interface { Output_Interface() + : wl_output_interface() { release = OutputReleaseCallback; } } m_outputInterface; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandOutputGlobal.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.cpp similarity index 79% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandOutputGlobal.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.cpp index 24c59c27f..4bbbe92b1 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandOutputGlobal.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandOutputGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandOutputConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/IWaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/IWaylandGlobal.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandOutputGlobal::WaylandOutputGlobal(const WaylandOutputParams& waylandOutputParams) : m_waylandOutputParams(waylandOutputParams) @@ -60,7 +60,7 @@ namespace ramses_internal void WaylandOutputGlobal::GlobalBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) { - WaylandOutputGlobal* waylandOutputGlobal = static_cast(data); + auto* waylandOutputGlobal = static_cast(data); WaylandClient waylandClient(client); waylandOutputGlobal->globalBind(waylandClient, version, id); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.h index f182c91ec..aa9fe6904 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputGlobal.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDOUTPUTGLOBAL_H -#define RAMSES_WAYLANDOUTPUTGLOBAL_H +#pragma once -#include "EmbeddedCompositor_Wayland/WaylandOutputParams.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputParams.h" #include struct wl_client; -namespace ramses_internal +namespace ramses::internal { class IWaylandGlobal; class IWaylandClient; @@ -36,5 +35,3 @@ namespace ramses_internal IWaylandGlobal* m_waylandGlobal = nullptr; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputParams.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputParams.h similarity index 84% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputParams.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputParams.h index d240ab08f..b6a58e642 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandOutputParams.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputParams.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDOUTPUTPARAMS_H -#define RAMSES_WAYLANDOUTPUTPARAMS_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { struct WaylandOutputParams { @@ -19,5 +18,3 @@ namespace ramses_internal uint32_t height; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandRegion.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.cpp similarity index 72% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandRegion.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.cpp index 957128f44..97cbce108 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandRegion.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandRegion.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { WaylandRegion::WaylandRegion(IEmbeddedCompositor_Wayland& compositor, IWaylandClient& client, @@ -22,7 +22,7 @@ namespace ramses_internal { LOG_TRACE(CONTEXT_RENDERER, "WaylandRegion::WaylandRegion"); - m_resource = client.resourceCreate(&wl_region_interface, version, id); + m_resource = client.resourceCreate(&wl_region_interface, static_cast(version), id); if (nullptr != m_resource) { m_resource->setImplementation(&m_regionInterface, this, ResourceDestroyedCallback); @@ -60,32 +60,27 @@ namespace ramses_internal m_resource = nullptr; } - void WaylandRegion::regionAdd(IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) + void WaylandRegion::regionAdd([[maybe_unused]] IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) { LOG_TRACE(CONTEXT_RENDERER, "WaylandRegion::regionAdd x: " << x << " y: " << y << " width: " << width << " height: " << height); - - UNUSED(client); } - void WaylandRegion::regionSubtract(IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) + void WaylandRegion::regionSubtract([[maybe_unused]] IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) { LOG_TRACE(CONTEXT_RENDERER, "WaylandRegion::regionSubtract x: " << x << " y: " << y << " width: " << width << " height: " << height); - - UNUSED(client); } - void WaylandRegion::RegionDestroyCallback(wl_client* client, wl_resource* regionResource) + void WaylandRegion::RegionDestroyCallback([[maybe_unused]] wl_client* client, wl_resource* regionResource) { - UNUSED(client); - WaylandRegion* region = static_cast(wl_resource_get_user_data(regionResource)); + auto* region = static_cast(wl_resource_get_user_data(regionResource)); delete region; } void WaylandRegion::RegionAddCallback( wl_client* client, wl_resource* regionResource, int32_t x, int32_t y, int32_t width, int32_t height) { - WaylandRegion* region = static_cast(wl_resource_get_user_data(regionResource)); + auto* region = static_cast(wl_resource_get_user_data(regionResource)); WaylandClient waylandClient(client); region->regionAdd(waylandClient, x, y, width, height); } @@ -93,14 +88,14 @@ namespace ramses_internal void WaylandRegion::RegionSubtractCallback( wl_client* client, wl_resource* regionResource, int32_t x, int32_t y, int32_t width, int32_t height) { - WaylandRegion* region = static_cast(wl_resource_get_user_data(regionResource)); + auto* region = static_cast(wl_resource_get_user_data(regionResource)); WaylandClient waylandClient(client); region->regionSubtract(waylandClient, x, y, width, height); } void WaylandRegion::ResourceDestroyedCallback(wl_resource* regionResource) { - WaylandRegion* region = static_cast(wl_resource_get_user_data(regionResource)); + auto* region = static_cast(wl_resource_get_user_data(regionResource)); region->resourceDestroyed(); delete region; } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandRegion.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.h similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandRegion.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.h index ed95b4c83..8dc5d2549 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandRegion.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDREGION_H -#define RAMSES_WAYLANDREGION_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandRegion.h" -#include "RendererAPI/Types.h" -#include "RendererLib/RendererLogContext.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/RendererLogContext.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositor_Wayland; @@ -46,6 +45,7 @@ namespace ramses_internal const struct Region_Interface : private wl_region_interface { Region_Interface() + : wl_region_interface() { destroy = RegionDestroyCallback; add = RegionAddCallback; @@ -54,5 +54,3 @@ namespace ramses_internal } m_regionInterface; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellConnection.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellConnection.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.cpp index a955c910f..13d287fc9 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellConnection.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.cpp @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandShellConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandShellSurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandShellConnection.h" -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandShellConnection::WaylandShellConnection(IWaylandClient& client, uint32_t version, uint32_t id) : m_clientCredentials(client.getCredentials()) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellConnection::WaylandShellConnection Connection created " << m_clientCredentials); - m_resource = client.resourceCreate(&wl_shell_interface, version, id); + m_resource = client.resourceCreate(&wl_shell_interface, static_cast(version), id); if (m_resource) { m_resource->setImplementation(&m_shellInterface, this, ResourceDestroyedCallback); @@ -64,10 +64,10 @@ namespace ramses_internal void WaylandShellConnection::shellGetShellSurface(IWaylandClient& client, uint32_t id, INativeWaylandResource& surfaceResource) { - IWaylandSurface* clientSurface = reinterpret_cast(surfaceResource.getUserData()); + auto* clientSurface = reinterpret_cast(surfaceResource.getUserData()); assert(nullptr != clientSurface); assert(nullptr != m_resource); - WaylandShellSurface* waylandShellSurface = new WaylandShellSurface(client, *m_resource, id, *clientSurface); + auto* waylandShellSurface = new WaylandShellSurface(client, *m_resource, id, *clientSurface); // Registers callback for destruction, when the corresponding wl_shell_surface is destroyed if (!waylandShellSurface->wasSuccessfullyInitialized()) @@ -78,7 +78,7 @@ namespace ramses_internal void WaylandShellConnection::ResourceDestroyedCallback(wl_resource* shellConnectionResource) { - WaylandShellConnection* shellConnection = + auto* shellConnection = static_cast(wl_resource_get_user_data(shellConnectionResource)); shellConnection->resourceDestroyed(); @@ -87,7 +87,7 @@ namespace ramses_internal void WaylandShellConnection::ShellGetShellSurfaceCallback(wl_client *client, wl_resource* shellConnectionResource, uint32_t id, wl_resource *surfaceResource) { - WaylandShellConnection* shellConnection = + auto* shellConnection = static_cast(wl_resource_get_user_data(shellConnectionResource)); WaylandClient waylandClient(client); diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellConnection.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellConnection.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.h index 31b2139f2..dbc9ca9f8 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellConnection.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.h @@ -5,15 +5,14 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDSHELLCONNECTION_H -#define RAMSES_WAYLANDSHELLCONNECTION_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandShellConnection.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "ivi-application-server-protocol.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class WaylandShellConnection : public IWaylandShellConnection { @@ -35,11 +34,10 @@ namespace ramses_internal const struct Shell_Interface : private wl_shell_interface { Shell_Interface() + : wl_shell_interface() { get_shell_surface = ShellGetShellSurfaceCallback; } } m_shellInterface; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellGlobal.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.cpp similarity index 74% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellGlobal.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.cpp index 0f062987e..f12a992fc 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellGlobal.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.cpp @@ -6,20 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandShellGlobal.h" -#include "EmbeddedCompositor_Wayland/WaylandShellConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/IWaylandGlobal.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandGlobal.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { - WaylandShellGlobal::WaylandShellGlobal() - { - } + WaylandShellGlobal::WaylandShellGlobal() = default; WaylandShellGlobal::~WaylandShellGlobal() { @@ -57,7 +55,7 @@ namespace ramses_internal void WaylandShellGlobal::shellBind(IWaylandClient& client, uint32_t version, uint32_t id) { - WaylandShellConnection* waylandShellConnection = new WaylandShellConnection(client, version, id); + auto* waylandShellConnection = new WaylandShellConnection(client, version, id); // Registers callback for destruction, when the corresponding resource is destroyed if (!waylandShellConnection->wasSuccessfullyInitialized()) @@ -66,10 +64,9 @@ namespace ramses_internal } } - void WaylandShellGlobal::ShellBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) + void WaylandShellGlobal::ShellBindCallback(wl_client* client, void* data, [[maybe_unused]] uint32_t version, uint32_t id) { - UNUSED(version); - WaylandShellGlobal* shell = static_cast(data); + auto* shell = static_cast(data); WaylandClient waylandClient(client); shell->shellBind(waylandClient, version, id); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellGlobal.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.h similarity index 86% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellGlobal.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.h index 5077ab9b2..368ee3d76 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellGlobal.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellGlobal.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDSHELLGLOBAL_H -#define RAMSES_WAYLANDSHELLGLOBAL_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandShellGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellGlobal.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class WaylandDisplay; class IWaylandGlobal; @@ -33,5 +32,3 @@ namespace ramses_internal IWaylandGlobal* m_waylandGlobal = nullptr; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellSurface.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.cpp similarity index 70% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellSurface.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.cpp index a69e8c3c9..090293337 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandShellSurface.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandShellSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandShellSurface::WaylandShellSurface(IWaylandClient& client, INativeWaylandResource& shellConnectionResource, uint32_t id, IWaylandSurface& surface) : m_clientCredentials(client.getCredentials()) @@ -87,11 +87,9 @@ namespace ramses_internal return m_title; } - void WaylandShellSurface::shellSurfacePong(IWaylandClient& client, uint32_t serial) + void WaylandShellSurface::shellSurfacePong([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] uint32_t serial) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfacePong"); - UNUSED(client); - UNUSED(serial); if (m_surface == nullptr) { @@ -99,112 +97,92 @@ namespace ramses_internal } } - void WaylandShellSurface::shellSurfaceMove(IWaylandClient& client, INativeWaylandResource& seatResource, uint32_t serial) + void WaylandShellSurface::shellSurfaceMove([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] INativeWaylandResource& seatResource, [[maybe_unused]] uint32_t serial) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceMove"); - UNUSED(client); - UNUSED(serial); - UNUSED(seatResource); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfacePong Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceResize(IWaylandClient& client, INativeWaylandResource& seatResource, uint32_t serial, uint32_t edges) + void WaylandShellSurface::shellSurfaceResize([[maybe_unused]] IWaylandClient& client, + [[maybe_unused]] INativeWaylandResource& seatResource, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] uint32_t edges) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceResize"); - UNUSED(client); - UNUSED(serial); - UNUSED(seatResource); - UNUSED(edges); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceResize Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceSetToplevel(IWaylandClient& client) + void WaylandShellSurface::shellSurfaceSetToplevel([[maybe_unused]] IWaylandClient& client) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetToplevel"); - UNUSED(client); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetToplevel Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceSetTransient(IWaylandClient& client, INativeWaylandResource& parentSurfaceResource, int32_t x, int32_t y, uint32_t flags) + void WaylandShellSurface::shellSurfaceSetTransient([[maybe_unused]] IWaylandClient& client, + [[maybe_unused]] INativeWaylandResource& parentSurfaceResource, + [[maybe_unused]] int32_t x, + [[maybe_unused]] int32_t y, + [[maybe_unused]] uint32_t flags) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetTransient"); - UNUSED(client); - UNUSED(parentSurfaceResource); - UNUSED(x); - UNUSED(y); - UNUSED(flags); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetTransient Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceSetFullscreen(IWaylandClient& client, uint32_t method, uint32_t framerate) + void WaylandShellSurface::shellSurfaceSetFullscreen([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] uint32_t method, [[maybe_unused]] uint32_t framerate) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetFullscreen"); - UNUSED(client); - UNUSED(method); - UNUSED(framerate); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetFullscreen Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceSetPopup(IWaylandClient& client, INativeWaylandResource& seatResource, uint32_t serial, INativeWaylandResource& parentSurfaceResource, int32_t x, - int32_t y, uint32_t flags) + void WaylandShellSurface::shellSurfaceSetPopup([[maybe_unused]] IWaylandClient& client, + [[maybe_unused]] INativeWaylandResource& seatResource, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] INativeWaylandResource& parentSurfaceResource, + [[maybe_unused]] int32_t x, + [[maybe_unused]] int32_t y, + [[maybe_unused]] uint32_t flags) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetPopup"); - UNUSED(client); - UNUSED(seatResource); - UNUSED(serial); - UNUSED(parentSurfaceResource); - UNUSED(x); - UNUSED(y); - UNUSED(flags); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetPopup Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceSetMaximized(IWaylandClient& client) + void WaylandShellSurface::shellSurfaceSetMaximized([[maybe_unused]] IWaylandClient& client) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetMaximized"); - UNUSED(client); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetMaximized Surface has already been deleted!"); } } - void WaylandShellSurface::shellSurfaceSetTitle(IWaylandClient& client, const char* title) + void WaylandShellSurface::shellSurfaceSetTitle([[maybe_unused]] IWaylandClient& client, const char* title) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetTitle title: " << title); - UNUSED(client); if (m_surface == nullptr) { @@ -216,13 +194,10 @@ namespace ramses_internal } } - void WaylandShellSurface::shellSurfaceSetClass(IWaylandClient& client, const char* className) + void WaylandShellSurface::shellSurfaceSetClass([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] const char* className) { LOG_INFO(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetClass"); - UNUSED(client); - UNUSED(className); - if (m_surface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, "WaylandShellSurface::shellSurfaceSetClass Surface has already been deleted!"); @@ -232,21 +207,21 @@ namespace ramses_internal void WaylandShellSurface::ResourceDestroyedCallback(wl_resource* surfaceResource) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); shellSurface->resourceDestroyed(); delete shellSurface; } void WaylandShellSurface::ShellSurfacePongCallback(wl_client* client, wl_resource* surfaceResource, uint32_t serial) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); shellSurface->shellSurfacePong(waylandClient, serial); } void WaylandShellSurface::ShellSurfaceMoveCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* seatResource, uint32_t serial) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); NativeWaylandResource waylandSeatResource(seatResource); shellSurface->shellSurfaceMove(waylandClient, waylandSeatResource, serial); @@ -254,7 +229,7 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceResizeCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* seatResource, uint32_t serial, uint32_t edges) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); NativeWaylandResource waylandSeatResource(seatResource); shellSurface->shellSurfaceResize(waylandClient, waylandSeatResource, serial, edges); @@ -262,7 +237,7 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceSetToplevelCallback(wl_client* client, wl_resource* surfaceResource) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); shellSurface->shellSurfaceSetToplevel(waylandClient); } @@ -270,7 +245,7 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceSetTransientCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* parentSurfaceResource, int32_t x, int32_t y, uint32_t flags) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); NativeWaylandResource waylandParentSurfaceResource(parentSurfaceResource); shellSurface->shellSurfaceSetTransient(waylandClient, waylandParentSurfaceResource, x, y, flags); @@ -279,7 +254,7 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceSetFullscreenCallback(wl_client* client, wl_resource* surfaceResource, uint32_t method, uint32_t framerate, wl_resource* /*outputResource*/) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); shellSurface->shellSurfaceSetFullscreen(waylandClient, method, framerate); @@ -288,7 +263,7 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceSetPopupCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* seatResource, uint32_t serial, wl_resource* parentSurfaceResource, int32_t x, int32_t y, uint32_t flags) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); NativeWaylandResource waylandSeatResource(seatResource); NativeWaylandResource waylandParentSurfaceResource(parentSurfaceResource); @@ -297,7 +272,7 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceSetMaximizedCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* /*outputResource*/) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); shellSurface->shellSurfaceSetMaximized(waylandClient); @@ -305,14 +280,14 @@ namespace ramses_internal void WaylandShellSurface::ShellSurfaceSetTitleCallback(wl_client* client, wl_resource* surfaceResource, const char* title) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); shellSurface->shellSurfaceSetTitle(waylandClient, title); } void WaylandShellSurface::ShellSurfaceSetClassCallback(wl_client* client, wl_resource* surfaceResource, const char* className) { - WaylandShellSurface* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* shellSurface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); shellSurface->shellSurfaceSetClass(waylandClient, className); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellSurface.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h similarity index 93% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellSurface.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h index 4935861e8..2a5378b23 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandShellSurface.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDSHELLSURFACE_H -#define RAMSES_WAYLANDSHELLSURFACE_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandSurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandShellSurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { class WaylandShellSurface: public IWaylandShellSurface { @@ -59,6 +58,7 @@ namespace ramses_internal const struct ShellSurface_Interface : private wl_shell_surface_interface { ShellSurface_Interface() + : wl_shell_surface_interface() { pong = ShellSurfacePongCallback; move = ShellSurfaceMoveCallback; @@ -79,5 +79,3 @@ namespace ramses_internal std::string m_title; }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandSurface.cpp b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.cpp similarity index 68% rename from renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandSurface.cpp rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.cpp index de0da702b..36e7fd543 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/src/WaylandSurface.cpp +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandSurface.h" -#include "EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/IWaylandIVISurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandShellSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/IWaylandBuffer.h" -#include "EmbeddedCompositor_Wayland/WaylandCallbackResource.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVISurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandSurface::WaylandSurface(IEmbeddedCompositor_Wayland& compositor, IWaylandClient& client, int version, uint32_t id) : m_compositor(compositor) @@ -88,6 +88,20 @@ namespace ramses_internal } } + void WaylandSurface::logSurfaceAttach(ELogLevel level, const char* stage, IWaylandBuffer* buffer, int x, int y) + { + if (level <= CONTEXT_RENDERER.getLogLevel()) + { + auto res = buffer->getResource(); + const auto isShm = (res.bufferGetSharedMemoryData() != nullptr); + const auto width = res.getWidth(); + const auto height = res.getHeight(); + auto* wlres = res.getLowLevelHandle(); + LOG_COMMON_RP(CONTEXT_RENDERER, level, "WaylandSurface::surfaceAttach{}: {} shm:{} x:{} y:{} w:{} h:{} res:{}", + stage, getIviSurfaceId(), isShm, x, y, width, height, static_cast(wlres)); + } + } + IWaylandBuffer* WaylandSurface::getWaylandBuffer() const { return m_buffer; @@ -115,11 +129,8 @@ namespace ramses_internal { return m_shellSurface->getTitle(); } - else - { - static std::string emptyString; - return emptyString; - } + static std::string emptyString; + return emptyString; } void WaylandSurface::setIviSurface(IWaylandIVISurface* iviSurface) @@ -139,10 +150,7 @@ namespace ramses_internal { return m_iviSurface->getIviId(); } - else - { - return {}; - } + return {}; } uint32_t WaylandSurface::getNumberOfCommitedFrames() const @@ -171,65 +179,45 @@ namespace ramses_internal m_surfaceResource = nullptr; } - void WaylandSurface::surfaceAttach(IWaylandClient& client, WaylandBufferResource& bufferResource, int x, int y) + void WaylandSurface::surfaceAttach([[maybe_unused]] IWaylandClient& client, WaylandBufferResource& bufferResource, [[maybe_unused]] int x, [[maybe_unused]] int y) { - LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceAttach"); - - UNUSED(client); - UNUSED(x); - UNUSED(y); - - LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::surfaceAttach: set new pending buffer to surface with title " - << getSurfaceTitle() << ", ivi surface id " << getIviSurfaceId().getValue()); - m_pendingBuffer = &m_compositor.getOrCreateBuffer(bufferResource); m_removeBufferOnNextCommit = false; - //WaylandSurface::surfaceAttach is called iff attached buffer is not null (if null buffer gets attached then WaylandSurface::surfaceDetach gets called instead) + // WaylandSurface::surfaceAttach is called if attached buffer is not null (if null buffer gets attached then WaylandSurface::surfaceDetach gets called instead) assert(m_pendingBuffer); - //if no buffer was attached (e.g. first buffer ever or after detach) const bool pendingBufferIsSharedMemoryBuffer = m_pendingBuffer->isSharedMemoryBuffer(); + logSurfaceAttach(ELogLevel::Trace, "", m_pendingBuffer, x, y); + // if no buffer was attached (e.g. first buffer ever or after detach) if(!m_buffer) { m_bufferTypeChanged = true; - - LOG_INFO(CONTEXT_RENDERER, "WaylandSurface::surfaceAttach: Mark buffer type as changed for surface with ivi-id :" << getIviSurfaceId() - << " due to attach after no buffer attached to surface. New buffer is Shared mem buffer :" << pendingBufferIsSharedMemoryBuffer); + logSurfaceAttach(ELogLevel::Info, "[new]", m_pendingBuffer, x, y); } - //if new buffer (pending buffer) has different type from current buffer + // if new buffer (pending buffer) has different type from current buffer else if(pendingBufferIsSharedMemoryBuffer != m_buffer->isSharedMemoryBuffer()) { m_bufferTypeChanged = true; - - LOG_INFO(CONTEXT_RENDERER, "WaylandSurface::surfaceAttach: Mark buffer type as changed for surface with ivi-id :" << getIviSurfaceId() - << " because newly attached buffer has differen type from current. New buffer is Shared mem buffer :" << pendingBufferIsSharedMemoryBuffer); + logSurfaceAttach(ELogLevel::Info, "[typeChange]", m_pendingBuffer, x, y); } } - void WaylandSurface::surfaceDetach(IWaylandClient& client) + void WaylandSurface::surfaceDetach([[maybe_unused]] IWaylandClient& client) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceDetach"); - UNUSED(client); - LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceAttach: remove current pending buffer from surface with title " - << getSurfaceTitle() << ", ivi surface id " << getIviSurfaceId().getValue()); + << getSurfaceTitle() << ", " << getIviSurfaceId()); m_pendingBuffer = nullptr; m_removeBufferOnNextCommit = true; } - void WaylandSurface::surfaceDamage(IWaylandClient& client, int x, int y, int width, int height) + void + WaylandSurface::surfaceDamage([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] int x, [[maybe_unused]] int y, [[maybe_unused]] int width, [[maybe_unused]] int height) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceDamage"); - - UNUSED(client); - UNUSED(x); - UNUSED(y); - UNUSED(width); - UNUSED(height); } void WaylandSurface::surfaceFrame(IWaylandClient& client, uint32_t id) @@ -250,31 +238,22 @@ namespace ramses_internal } } - void WaylandSurface::surfaceSetOpaqueRegion(IWaylandClient& client, INativeWaylandResource* regionResource) + void WaylandSurface::surfaceSetOpaqueRegion([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] INativeWaylandResource* regionResource) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceSetOpaqueRegion"); - - UNUSED(client); - UNUSED(regionResource); } - void WaylandSurface::surfaceSetInputRegion(IWaylandClient& client, INativeWaylandResource* regionResource) + void WaylandSurface::surfaceSetInputRegion([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] INativeWaylandResource* regionResource) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceSetInputRegion"); - - UNUSED(client); - UNUSED(regionResource); } - void WaylandSurface::surfaceCommit(IWaylandClient& client) + void WaylandSurface::surfaceCommit([[maybe_unused]] IWaylandClient& client) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceCommit"); - UNUSED(client); - LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::surfaceCommit: handling commit message for surface with ivi surface id " - << getIviSurfaceId().getValue()); + "WaylandSurface::surfaceCommit: handling commit message for surface " << getIviSurfaceId()); // Transfers pending callbacks to list of frame_callbacks. for(const auto& callback : m_pendingCallbacks) @@ -286,8 +265,7 @@ namespace ramses_internal if (m_pendingBuffer) { LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::surfaceCommit: new texture data for surface with ivi surface id " - << getIviSurfaceId().getValue()); + "WaylandSurface::surfaceCommit: new texture data for surface " << getIviSurfaceId()); setBufferToSurface(*m_pendingBuffer); m_pendingBuffer = nullptr; } @@ -297,8 +275,7 @@ namespace ramses_internal { LOG_TRACE( CONTEXT_RENDERER, - "WaylandSurface::surfaceCommit: remove buffer from surface with ivi surface id " - << getIviSurfaceId().getValue() + "WaylandSurface::surfaceCommit: remove buffer from surface " << getIviSurfaceId() << " because triggered by earlier empty attachsurface"); unsetBufferFromSurface(); } @@ -308,31 +285,20 @@ namespace ramses_internal m_numberOfCommitedFramesSinceBeginningOfTime++; } - void WaylandSurface::surfaceSetBufferTransform(IWaylandClient& client, int32_t transform) + void WaylandSurface::surfaceSetBufferTransform([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] int32_t transform) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceSetBufferTransform"); - - UNUSED(client); - UNUSED(transform); } - void WaylandSurface::surfaceSetBufferScale(IWaylandClient& client, int32_t scale) + void WaylandSurface::surfaceSetBufferScale([[maybe_unused]] IWaylandClient& client, [[maybe_unused]] int32_t scale) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceSetBufferScale"); - - UNUSED(client); - UNUSED(scale); } - void WaylandSurface::surfaceDamageBuffer(IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height) + void WaylandSurface::surfaceDamageBuffer( + [[maybe_unused]] IWaylandClient& client, [[maybe_unused]] int32_t x, [[maybe_unused]] int32_t y, [[maybe_unused]] int32_t width, [[maybe_unused]] int32_t height) { LOG_TRACE(CONTEXT_RENDERER, "WaylandSurface::surfaceDamageBuffer"); - - UNUSED(client); - UNUSED(x); - UNUSED(y); - UNUSED(width); - UNUSED(height); } WaylandClientCredentials WaylandSurface::getClientCredentials() const @@ -347,16 +313,15 @@ namespace ramses_internal return result; } - void WaylandSurface::SurfaceDestroyCallback(wl_client* client, wl_resource* surfaceResource) + void WaylandSurface::SurfaceDestroyCallback([[maybe_unused]] wl_client* client, wl_resource* surfaceResource) { - UNUSED(client); - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); delete surface; } - void WaylandSurface::SurfaceAttachCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* bufferResource, int x, int y) + void WaylandSurface::SurfaceAttachCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* bufferResource, [[maybe_unused]] int x, [[maybe_unused]] int y) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); if (nullptr != bufferResource) { @@ -365,30 +330,27 @@ namespace ramses_internal } else { - UNUSED(x); - UNUSED(y); surface->surfaceDetach(waylandClient); } - } void WaylandSurface::SurfaceDamageCallback(wl_client* client, wl_resource* surfaceResource, int x, int y, int width, int height) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); surface->surfaceDamage(waylandClient, x, y, width, height); } void WaylandSurface::SurfaceFrameCallback(wl_client* client, wl_resource* surfaceResource, uint32_t id) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); surface->surfaceFrame(waylandClient, id); } void WaylandSurface::SurfaceSetOpaqueRegionCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* regionResource) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); if (nullptr != regionResource) { @@ -403,7 +365,7 @@ namespace ramses_internal void WaylandSurface::SurfaceSetInputRegionCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* regionResource) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); if (nullptr != regionResource) { @@ -418,35 +380,35 @@ namespace ramses_internal void WaylandSurface::SurfaceCommitCallback(wl_client* client, wl_resource* surfaceResource) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); surface->surfaceCommit(waylandClient); } void WaylandSurface::SurfaceSetBufferTransformCallback(wl_client* client, wl_resource* surfaceResource, int32_t transform) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); surface->surfaceSetBufferTransform(waylandClient, transform); } void WaylandSurface::SurfaceSetBufferScaleCallback(wl_client* client, wl_resource* surfaceResource, int32_t scale) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); surface->surfaceSetBufferScale(waylandClient, scale); } void WaylandSurface::SurfaceDamageBufferCallback(wl_client *client, wl_resource* surfaceResource, int32_t x, int32_t y, int32_t width, int32_t height) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandClient waylandClient(client); surface->surfaceDamageBuffer(waylandClient, x, y, width, height); } void WaylandSurface::ResourceDestroyedCallback(wl_resource* surfaceResource) { - WaylandSurface* surface = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* surface = static_cast(wl_resource_get_user_data(surfaceResource)); surface->resourceDestroyed(); delete surface; } @@ -462,8 +424,7 @@ namespace ramses_internal else { LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::setBufferToSurface client provides content for surface " - << getIviSurfaceId().getValue()); + "WaylandSurface::setBufferToSurface client provides content for surface " << getIviSurfaceId()); } setWaylandBuffer(&buffer); @@ -472,8 +433,7 @@ namespace ramses_internal void WaylandSurface::unsetBufferFromSurface() { LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::unsetBufferFromSurface: removing buffer used for surface with ivi surface id " - << getIviSurfaceId().getValue()); + "WaylandSurface::unsetBufferFromSurface: removing buffer used for surface " << getIviSurfaceId()); if (m_buffer) { @@ -495,10 +455,19 @@ namespace ramses_internal m_frameCallbacks.clear(); } - void WaylandSurface::logInfos(RendererLogContext& context) const + void WaylandSurface::logInfos(RendererLogContext& context, const WaylandEGLExtensionProcs &eglExt) const { - context << "[ivi-surface-id: " << getIviSurfaceId().getValue() << "; title: \"" << getSurfaceTitle() << "\"]" + context << getIviSurfaceId() << "; title: \"" << getSurfaceTitle() + << "\"; client" << getClientCredentials() + << "; commitedFrames: " << m_numberOfCommitedFramesSinceBeginningOfTime << RendererLogContext::NewLine; + if (m_buffer != nullptr) + { + context.indent(); + m_buffer->logInfos(context, eglExt); + context.unindent(); + } + } void WaylandSurface::bufferDestroyed(IWaylandBuffer& buffer) @@ -506,9 +475,7 @@ namespace ramses_internal if (m_buffer == &buffer) { LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::bufferDestroyed(): destroying buffer for surface with ivi " - "surface id :" - << getIviSurfaceId().getValue()); + "WaylandSurface::bufferDestroyed(): destroying buffer for surface: " << getIviSurfaceId()); unsetBufferFromSurface(); } @@ -516,9 +483,7 @@ namespace ramses_internal if (m_pendingBuffer == &buffer) { LOG_TRACE(CONTEXT_RENDERER, - "WaylandSurface::bufferDestroyed(): destroying pending buffer for surface with " - "ivi surface id :" - << getIviSurfaceId().getValue()); + "WaylandSurface::bufferDestroyed(): destroying pending buffer for surface: " << getIviSurfaceId()); m_pendingBuffer = nullptr; } } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandSurface.h b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandSurface.h rename to src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h index 00ca95991..20ea70049 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/include/EmbeddedCompositor_Wayland/WaylandSurface.h +++ b/src/renderer/internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDSURFACE_H -#define RAMSES_WAYLANDSURFACE_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandSurface.h" -#include "RendererLib/RendererLogContext.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Core/Utils/LogLevel.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "wayland-server.h" -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositor_Wayland; class IWaylandIVISurface; @@ -35,12 +35,12 @@ namespace ramses_internal [[nodiscard]] const std::string& getSurfaceTitle() const override; void setIviSurface(IWaylandIVISurface* iviSurface) override; [[nodiscard]] bool hasIviSurface() const override; - [[nodiscard]] WaylandIviSurfaceId getIviSurfaceId() const override; + [[nodiscard]] WaylandIviSurfaceId getIviSurfaceId() const final override; [[nodiscard]] uint32_t getNumberOfCommitedFrames() const override; void resetNumberOfCommitedFrames() override; [[nodiscard]] uint64_t getNumberOfCommitedFramesSinceBeginningOfTime() const override; void sendFrameCallbacks(uint32_t time) override; - void logInfos(RendererLogContext& context) const override; + void logInfos(RendererLogContext& context, const WaylandEGLExtensionProcs& eglExt) const override; void bufferDestroyed(IWaylandBuffer& buffer) override; void resourceDestroyed() override; void surfaceAttach(IWaylandClient& client, WaylandBufferResource& bufferResource, int x, int y) override; @@ -60,9 +60,10 @@ namespace ramses_internal void setBufferToSurface(IWaylandBuffer& buffer); void unsetBufferFromSurface(); void setWaylandBuffer(IWaylandBuffer* buffer); + void logSurfaceAttach(ELogLevel level, const char* stage, IWaylandBuffer* buffer, int x, int y); static void SurfaceDestroyCallback(wl_client* client, wl_resource* surfaceResource); - static void SurfaceAttachCallback(wl_client*, wl_resource* surfaceResource, wl_resource* bufferResource, int x, int y); + static void SurfaceAttachCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* bufferResource, int x, int y); static void SurfaceDamageCallback(wl_client* client, wl_resource* surfaceResource, int x, int y, int width, int height); static void SurfaceFrameCallback(wl_client* client, wl_resource* surfaceResource, uint32_t id); static void SurfaceSetOpaqueRegionCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* regionResource); @@ -90,6 +91,7 @@ namespace ramses_internal const struct Surface_Interface : private wl_surface_interface { Surface_Interface() + : wl_surface_interface() { destroy = SurfaceDestroyCallback; attach = SurfaceAttachCallback; @@ -109,5 +111,3 @@ namespace ramses_internal bool m_bufferTypeChanged = false; }; } - -#endif diff --git a/renderer/Platform/Platform_Wayland_IVI_EGL/src/Platform_Wayland_IVI_EGL_ES_3_0.cpp b/src/renderer/internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.cpp similarity index 82% rename from renderer/Platform/Platform_Wayland_IVI_EGL/src/Platform_Wayland_IVI_EGL_ES_3_0.cpp rename to src/renderer/internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.cpp index beb694030..f7aa0e115 100644 --- a/renderer/Platform/Platform_Wayland_IVI_EGL/src/Platform_Wayland_IVI_EGL_ES_3_0.cpp +++ b/src/renderer/internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Wayland_IVI_EGL/Platform_Wayland_IVI_EGL_ES_3_0.h" -#include "Window_Wayland_IVI/Window_Wayland_IVI.h" -#include "SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h" -#include "RendererLib/RendererConfig.h" -#include "RendererLib/DisplayConfig.h" -#include "Utils/ThreadLocalLogForced.h" - -namespace ramses_internal +#include "internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.h" +#include "internal/Platform/Wayland/IVI/Window_Wayland_IVI.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + +namespace ramses::internal { Platform_Wayland_IVI_EGL_ES_3_0::Platform_Wayland_IVI_EGL_ES_3_0(const RendererConfig& rendererConfig) @@ -26,9 +26,13 @@ namespace ramses_internal assert(!m_systemCompositorController); auto systemCompositorController = std::make_unique(m_rendererConfig.getWaylandDisplayForSystemCompositorController()); if (systemCompositorController->init()) + { m_systemCompositorController = std::move(systemCompositorController); + } else + { LOG_ERROR(CONTEXT_RENDERER, "Platform_Wayland_IVI_EGL_ES_3_0:createSystemCompositorController: failed to initialize system compositor controller"); + } return m_systemCompositorController != nullptr; } diff --git a/renderer/Platform/Platform_Wayland_IVI_EGL/include/Platform_Wayland_IVI_EGL/Platform_Wayland_IVI_EGL_ES_3_0.h b/src/renderer/internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.h similarity index 82% rename from renderer/Platform/Platform_Wayland_IVI_EGL/include/Platform_Wayland_IVI_EGL/Platform_Wayland_IVI_EGL_ES_3_0.h rename to src/renderer/internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.h index 7c6640e9f..3ad89dd95 100644 --- a/renderer/Platform/Platform_Wayland_IVI_EGL/include/Platform_Wayland_IVI_EGL/Platform_Wayland_IVI_EGL_ES_3_0.h +++ b/src/renderer/internal/Platform/Wayland/IVI/Platform_Wayland_IVI_EGL_ES_3_0.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_WAYLAND_IVI_EGL_ES_3_0_H -#define RAMSES_PLATFORM_WAYLAND_IVI_EGL_ES_3_0_H +#pragma once -#include "Platform_Wayland_EGL/Platform_Wayland_EGL.h" +#include "internal/Platform/Wayland/Platform_Wayland_EGL.h" -namespace ramses_internal +namespace ramses::internal { class Platform_Wayland_IVI_EGL_ES_3_0 : public Platform_Wayland_EGL { @@ -24,5 +23,3 @@ namespace ramses_internal }; } -#endif - diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/IVIControllerScreen.cpp b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.cpp similarity index 86% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/src/IVIControllerScreen.cpp rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.cpp index f30b5bb02..c7a6111d4 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/IVIControllerScreen.cpp +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SystemCompositorController_Wayland_IVI/IVIControllerScreen.h" -#include "Utils/LogMacros.h" -#include "Utils/Warnings.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/Warnings.h" PUSH_DISABLE_C_STYLE_CAST_WARNING #include "ivi-controller-client-protocol.h" POP_DISABLE_C_STYLE_CAST_WARNING -namespace ramses_internal +namespace ramses::internal { IVIControllerScreen::IVIControllerScreen(ivi_controller_screen& controllerScreen, uint32_t screenId) : m_controllerScreen(controllerScreen) diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/IVIControllerScreen.h b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.h similarity index 85% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/IVIControllerScreen.h rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.h index 239b399e3..43cb27552 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/IVIControllerScreen.h +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.h @@ -6,16 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IVICONTROLLERSCREEN_H -#define RAMSES_IVICONTROLLERSCREEN_H - -#include "PlatformAbstraction/PlatformTypes.h" +#pragma once +#include #include struct ivi_controller_screen; -namespace ramses_internal +namespace ramses::internal { class IVIControllerScreen { @@ -31,5 +29,3 @@ namespace ramses_internal const uint32_t m_screenId; }; } - -#endif diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/IVIControllerSurface.cpp b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.cpp similarity index 66% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/src/IVIControllerSurface.cpp rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.cpp index 32ece2e63..6e40fff55 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/IVIControllerSurface.cpp +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SystemCompositorController_Wayland_IVI/IVIControllerSurface.h" -#include "SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h" -#include "Utils/LogMacros.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses_internal +namespace ramses::internal { IVIControllerSurface::IVIControllerSurface(ivi_controller_surface* controllerSurface, WaylandIviSurfaceId iviId, @@ -20,7 +20,7 @@ namespace ramses_internal , m_iviId(iviId) , m_systemCompositorController(systemCompositorController) { - LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::IVIControllerSurface ivi-id: " << m_iviId); + LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::IVIControllerSurface: " << m_iviId); if (nullptr != m_controllerSurface) { @@ -35,7 +35,7 @@ namespace ramses_internal IVIControllerSurface::~IVIControllerSurface() { - LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::~IVIControllerSurface ivi-id: " << m_iviId); + LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::~IVIControllerSurface: " << m_iviId); if (nullptr != m_controllerSurface) { @@ -116,153 +116,112 @@ namespace ramses_internal return m_controllerSurface; } - const WaylandIviSurfaceId IVIControllerSurface::getIVIId() const + WaylandIviSurfaceId IVIControllerSurface::getIVIId() const { return m_iviId; } - void IVIControllerSurface::HandleVisibilityCallback(void* data, - ivi_controller_surface* iviControllerSurface, - int32_t visibility) + void IVIControllerSurface::HandleVisibilityCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t visibility) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleVisibilityCallback ivi-id: " << controllerSurface.m_iviId << " visibility: " << visibility); + "IVIControllerSurface::HandleVisibilityCallback " << controllerSurface.m_iviId << " visibility: " << visibility); } - void IVIControllerSurface::HandleOpacityCallBack(void* data, - ivi_controller_surface* iviControllerSurface, - wl_fixed_t opacity) + void IVIControllerSurface::HandleOpacityCallBack(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, wl_fixed_t opacity) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleOpacityCallBack ivi-id: " << controllerSurface.m_iviId << " opacity: " << opacity); - + "IVIControllerSurface::HandleOpacityCallBack " << controllerSurface.m_iviId << " opacity: " << opacity); } void IVIControllerSurface::HandleSourceRectangleCallback( - void* data, ivi_controller_surface* iviControllerSurface, int32_t x, int32_t y, int32_t width, int32_t height) + void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t x, int32_t y, int32_t width, int32_t height) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleSourceRectangleCallback ivi-id: " + "IVIControllerSurface::HandleSourceRectangleCallback " << controllerSurface.m_iviId << " x: " << x << " y: " << y << " width: " << width << " height: " << height); - } void IVIControllerSurface::HandleDestinationRectangleCallback( - void* data, ivi_controller_surface* iviControllerSurface, int32_t x, int32_t y, int32_t width, int32_t height) + void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t x, int32_t y, int32_t width, int32_t height) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleDestinationRectangleCallback ivi-id: " + "IVIControllerSurface::HandleDestinationRectangleCallback " << controllerSurface.m_iviId << " x: " << x << " y: " << y << " width: " << width << " height: " << height); - } - void IVIControllerSurface::HandleConfigurationCallback(void* data, - ivi_controller_surface* iviControllerSurface, - int32_t width, - int32_t height) + void IVIControllerSurface::HandleConfigurationCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t width, int32_t height) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleConfigurationCallback ivi-id: " << controllerSurface.m_iviId << " width: " + "IVIControllerSurface::HandleConfigurationCallback " << controllerSurface.m_iviId << " width: " << width << " height: " << height); } - void IVIControllerSurface::HandleOrientationCallback(void* data, - ivi_controller_surface* iviControllerSurface, - int32_t orientation) + void IVIControllerSurface::HandleOrientationCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t orientation) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleOrientationCallback ivi-id: " << controllerSurface.m_iviId + "IVIControllerSurface::HandleOrientationCallback " << controllerSurface.m_iviId << " orientation: " << orientation); } - void IVIControllerSurface::HandlePixelformatCallback(void* data, - ivi_controller_surface* iviControllerSurface, - int32_t pixelformat) + void IVIControllerSurface::HandlePixelformatCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t pixelformat) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandlePixelformatCallback ivi-id: " << controllerSurface.m_iviId + "IVIControllerSurface::HandlePixelformatCallback " << controllerSurface.m_iviId << " pixelformat: " << pixelformat); } - void IVIControllerSurface::HandleLayerCallback(void* data, - ivi_controller_surface* iviControllerSurface, - ivi_controller_layer* layer) + void IVIControllerSurface::HandleLayerCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, [[maybe_unused]] ivi_controller_layer* layer) { - UNUSED(iviControllerSurface); - UNUSED(layer); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::HandleLayerCallback: surface " << controllerSurface.m_iviId << " added to layer"); } - void IVIControllerSurface::HandleStatsCallback(void* data, - ivi_controller_surface* iviControllerSurface, - uint32_t redrawCount, - uint32_t frameCount, - uint32_t updateCount, - uint32_t pid, - const char* processName) + void IVIControllerSurface::HandleStatsCallback(void* data, + [[maybe_unused]] ivi_controller_surface* iviControllerSurface, + uint32_t redrawCount, + uint32_t frameCount, + uint32_t updateCount, + uint32_t pid, + const char* processName) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleStatsCallback ivi-id: " + "IVIControllerSurface::HandleStatsCallback " << controllerSurface.m_iviId << " redrawCount: " << redrawCount << " frameCount: " << frameCount << " updateCount: " << updateCount << " pid: " << pid << " processName: " << processName); } - void IVIControllerSurface::HandleDestroyedCallback(void* data, ivi_controller_surface* iviControllerSurface) + void IVIControllerSurface::HandleDestroyedCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); - LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::HandleDestroyedCallback ivi-id: " << controllerSurface.m_iviId); + LOG_INFO(CONTEXT_RENDERER, "IVIControllerSurface::HandleDestroyedCallback " << controllerSurface.m_iviId); SystemCompositorController_Wayland_IVI& systemCompositorController = controllerSurface.m_systemCompositorController; systemCompositorController.deleteControllerSurface(controllerSurface); } - void IVIControllerSurface::HandleContentCallback(void* data, - ivi_controller_surface* iviControllerSurface, - int32_t contentState) + void IVIControllerSurface::HandleContentCallback(void* data, [[maybe_unused]] ivi_controller_surface* iviControllerSurface, int32_t contentState) { - UNUSED(iviControllerSurface); - IVIControllerSurface& controllerSurface = *static_cast(data); LOG_INFO(CONTEXT_RENDERER, - "IVIControllerSurface::HandleContentCallback ivi-id: " << controllerSurface.m_iviId + "IVIControllerSurface::HandleContentCallback " << controllerSurface.m_iviId << " contentState: " << contentState); } } diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/IVIControllerSurface.h b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.h similarity index 94% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/IVIControllerSurface.h rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.h index 6ba1c9857..576c43467 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/IVIControllerSurface.h +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IVICONTROLLERSURFACE_H -#define RAMSES_IVICONTROLLERSURFACE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "RendererAPI/Types.h" -#include "Utils/Warnings.h" +#include "internal/RendererLib/Types.h" +#include "internal/Core/Utils/Warnings.h" PUSH_DISABLE_C_STYLE_CAST_WARNING #include "ivi-controller-client-protocol.h" POP_DISABLE_C_STYLE_CAST_WARNING -namespace ramses_internal +#include + +namespace ramses::internal { class SystemCompositorController_Wayland_IVI; @@ -34,7 +34,7 @@ namespace ramses_internal void sendStats(); void destroy(); ivi_controller_surface* getNativeWaylandControllerSurface(); - [[nodiscard]] const WaylandIviSurfaceId getIVIId() const; + [[nodiscard]] WaylandIviSurfaceId getIVIId() const; private: static void HandleVisibilityCallback(void* data, @@ -96,7 +96,7 @@ namespace ramses_internal const struct IVIControllerSurface_Listener : public ivi_controller_surface_listener { - IVIControllerSurface_Listener() + IVIControllerSurface_Listener() : ivi_controller_surface_listener() { visibility = HandleVisibilityCallback; opacity = HandleOpacityCallBack; @@ -113,5 +113,3 @@ namespace ramses_internal } m_iviControllerSurfaceListener; }; } - -#endif diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/SystemCompositorController_Wayland_IVI.cpp b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.cpp similarity index 83% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/src/SystemCompositorController_Wayland_IVI.cpp rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.cpp index 77414cdfa..5c8a9d49d 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/SystemCompositorController_Wayland_IVI.cpp +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h" -#include "SystemCompositorController_Wayland_IVI/IVIControllerSurface.h" -#include "SystemCompositorController_Wayland_IVI/IVIControllerScreen.h" -#include "SystemCompositorController_Wayland_IVI/WaylandOutput.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "Collections/StringOutputStream.h" -#include "Utils/LogMacros.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerSurface.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/IVIControllerScreen.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Utils/LogMacros.h" #include #include "poll.h" @@ -21,7 +21,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { SystemCompositorController_Wayland_IVI::SystemCompositorController_Wayland_IVI(std::string_view waylandDisplay) : m_waylandDisplay(waylandDisplay) @@ -86,7 +86,7 @@ namespace ramses_internal void SystemCompositorController_Wayland_IVI::update() { - pollfd pfd; + pollfd pfd{}; pfd.fd = wl_display_get_fd(m_display); pfd.events = POLLIN; pfd.revents = 0; @@ -99,7 +99,7 @@ namespace ramses_internal } // NOLINTNEXTLINE(hicpp-signed-bitwise) - if (pfd.revents & POLLIN) + if ((pfd.revents & POLLIN) != 0) { wl_display_dispatch(m_display); } @@ -117,7 +117,7 @@ namespace ramses_internal // This log message is checked by test_testclient_system_compositor_controller.py, so be aware of changing it. LOG_INFO_F(CONTEXT_RENDERER, ([&](StringOutputStream& sos) { - sos << "SystemCompositorController_Wayland_IVI::listIVISurfaces Known ivi-ids are:"; + sos << "SystemCompositorController_Wayland_IVI::listIVISurfaces Known ivi-surface ids:"; for (auto id : sortedList) { sos << ' ' << id; @@ -136,8 +136,8 @@ namespace ramses_internal bool SystemCompositorController_Wayland_IVI::setSurfaceVisibility(WaylandIviSurfaceId surfaceId, bool visibility) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::setSurfaceVisibility surfaceId: " - << surfaceId.getValue() << " visibility: " << visibility); + "SystemCompositorController_Wayland_IVI::setSurfaceVisibility " + << surfaceId << " visibility: " << visibility); IVIControllerSurface& controllerSurface = getOrCreateControllerSurface(surfaceId); controllerSurface.setVisibility(visibility); @@ -149,8 +149,7 @@ namespace ramses_internal bool SystemCompositorController_Wayland_IVI::setSurfaceOpacity(WaylandIviSurfaceId surfaceId, float opacity) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::setOpacity surfaceId: " << surfaceId.getValue() - << " opacity: " << opacity); + "SystemCompositorController_Wayland_IVI::setOpacity " << surfaceId << " opacity: " << opacity); IVIControllerSurface& controllerSurface = getOrCreateControllerSurface(surfaceId); @@ -166,8 +165,8 @@ namespace ramses_internal WaylandIviSurfaceId surfaceId, int32_t x, int32_t y, int32_t width, int32_t height) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::setSurfaceDestinationRectangle surfaceId: " - << surfaceId.getValue() << " position: (" << x << ", " << y << ", " << width << ", " << height + "SystemCompositorController_Wayland_IVI::setSurfaceDestinationRectangle " + << surfaceId << " position: (" << x << ", " << y << ", " << width << ", " << height << ")"); IVIControllerSurface& controllerSurface = getOrCreateControllerSurface(surfaceId); @@ -225,8 +224,7 @@ namespace ramses_internal WaylandIviLayerId layerId) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::addSurfaceToLayer surfaceId: " - << surfaceId.getValue() << "layerId: " << layerId.getValue()); + "SystemCompositorController_Wayland_IVI::addSurfaceToLayer " << surfaceId << " " << layerId); // Workaround for bug in compositor, create a new ivi_controller_layer here, otherwise the surface list of the // layer can get wrong, when another application has also changed it in the meantime. @@ -234,9 +232,7 @@ namespace ramses_internal if (nullptr == controllerLayer) { LOG_ERROR(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::addSurfaceToLayer ivi_controller_layer_create failed, " - "layer-id: " - << layerId.getValue()); + "SystemCompositorController_Wayland_IVI::addSurfaceToLayer ivi_controller_layer_create failed, " << layerId); return false; } @@ -264,8 +260,7 @@ namespace ramses_internal WaylandIviLayerId layerId) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::removeSurfaceFromLayer surfaceId: " - << surfaceId.getValue() << " layerId: " << layerId.getValue()); + "SystemCompositorController_Wayland_IVI::removeSurfaceFromLayer " << surfaceId << " " << layerId); // Workaround for bug in compositor, create a new ivi_controller_layer here, otherwise the surface list of the // layer can get wrong, when another application has also changed it in the meantime. @@ -274,8 +269,7 @@ namespace ramses_internal { LOG_ERROR(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::removeSurfaceFromLayer ivi_controller_layer_create " - "failed, layer-id: " - << layerId.getValue()); + "failed " << layerId); return false; } @@ -283,8 +277,7 @@ namespace ramses_internal if (nullptr == controllerSurface) { LOG_ERROR(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::removeSurfaceFromLayer Surface " << surfaceId.getValue() - << " does not exist!"); + "SystemCompositorController_Wayland_IVI::removeSurfaceFromLayer " << surfaceId << " does not exist!"); return false; } @@ -309,15 +302,14 @@ namespace ramses_internal bool SystemCompositorController_Wayland_IVI::destroySurface(WaylandIviSurfaceId surfaceId) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::destroySurface surfaceId: " << surfaceId.getValue()); + "SystemCompositorController_Wayland_IVI::destroySurface " << surfaceId); IVIControllerSurface* controllerSurface = getControllerSurface(surfaceId); if (nullptr == controllerSurface) { LOG_ERROR(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::destroySurface Surface " << surfaceId.getValue() - << " does not exist!"); + "SystemCompositorController_Wayland_IVI::destroySurface " << surfaceId << " does not exist!"); return false; } controllerSurface->destroy(); @@ -330,8 +322,8 @@ namespace ramses_internal bool SystemCompositorController_Wayland_IVI::setLayerVisibility(WaylandIviLayerId layerId, bool visibility) { LOG_INFO(CONTEXT_RENDERER, - "SystemCompositorController_Wayland_IVI::setLayerVisibility layerId: " - << layerId.getValue() << " visibility: " << visibility); + "SystemCompositorController_Wayland_IVI::setLayerVisibility " + << layerId << " visibility: " << visibility); // Workaround for bug in compositor, create a new ivi_controller_layer here, otherwise the surface list of the // layer can get wrong, when another application has also changed it in the meantime. @@ -340,8 +332,7 @@ namespace ramses_internal { LOG_ERROR(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::setLayerVisibility ivi_controller_layer_create " - "failed, layer-id: " - << layerId.getValue()); + "failed, " << layerId); return false; } @@ -396,7 +387,7 @@ namespace ramses_internal if (nullptr == nativeControllerSurface) { - LOG_ERROR(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::getOrCreateControllerSurface"); + LOG_ERROR(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::getOrCreateControllerSurface " << iviId); } controllerSurface = new IVIControllerSurface(nativeControllerSurface, iviId, *this); @@ -412,13 +403,8 @@ namespace ramses_internal wl_display_flush(m_display); } - void SystemCompositorController_Wayland_IVI::registryHandleGlobal(wl_registry* registry, - uint32_t name, - const char* interface, - uint32_t version) + void SystemCompositorController_Wayland_IVI::registryHandleGlobal(wl_registry* registry, uint32_t name, const char* interface, [[maybe_unused]] uint32_t version) { - UNUSED(version); - // Binding the wl_output is needed, otherwise the controller screens don't come in. if (std::string_view("wl_output") == interface) { @@ -433,11 +419,10 @@ namespace ramses_internal } } - void SystemCompositorController_Wayland_IVI::iviControllerHandleScreen(ivi_controller* controller, - uint32_t id_screen, - ivi_controller_screen* nativeControllerScreen) + void SystemCompositorController_Wayland_IVI::iviControllerHandleScreen([[maybe_unused]] ivi_controller* controller, + uint32_t id_screen, + ivi_controller_screen* nativeControllerScreen) { - UNUSED(controller); LOG_INFO(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::iviControllerHandleScreen Detected ivi-screen: " << id_screen); if (nullptr != getControllerScreen(id_screen)) @@ -457,36 +442,32 @@ namespace ramses_internal m_controllerScreens.emplace_back(std::make_unique(*nativeControllerScreen, id_screen)); } - void SystemCompositorController_Wayland_IVI::iviControllerHandleLayer(ivi_controller* controller, uint32_t id_layer) + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) + void SystemCompositorController_Wayland_IVI::iviControllerHandleLayer([[maybe_unused]] ivi_controller* controller, [[maybe_unused]] uint32_t id_layer) { - UNUSED(controller); - UNUSED(id_layer); - LOG_INFO(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::iviControllerHandleLayer Detected ivi-layer: " << id_layer); + LOG_INFO(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::iviControllerHandleLayer Detected " << WaylandIviLayerId(id_layer)); } - void SystemCompositorController_Wayland_IVI::iviControllerHandleSurface(ivi_controller* controller, uint32_t iviID) + void SystemCompositorController_Wayland_IVI::iviControllerHandleSurface([[maybe_unused]] ivi_controller* controller, uint32_t iviID) { - UNUSED(controller); - LOG_INFO(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::iviControllerHandleSurface Detected ivi-surface: " << iviID); + const WaylandIviSurfaceId surfaceId{iviID}; + LOG_INFO(CONTEXT_RENDERER, "SystemCompositorController_Wayland_IVI::iviControllerHandleSurface Detected " << surfaceId); - getOrCreateControllerSurface(WaylandIviSurfaceId(iviID)); + getOrCreateControllerSurface(surfaceId); } void SystemCompositorController_Wayland_IVI::RegistryHandleGlobalCallback( void* data, wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { - SystemCompositorController_Wayland_IVI* systemCompositorController = + auto* systemCompositorController = static_cast(data); systemCompositorController->registryHandleGlobal(registry, name, interface, version); } - void SystemCompositorController_Wayland_IVI::RegistryHandleGlobalRemoveCallback(void* data, - wl_registry* wl_registry, - uint32_t name) + void SystemCompositorController_Wayland_IVI::RegistryHandleGlobalRemoveCallback([[maybe_unused]] void* data, + [[maybe_unused]] wl_registry* wl_registry, + [[maybe_unused]] uint32_t name) { - UNUSED(data); - UNUSED(wl_registry); - UNUSED(name); } void SystemCompositorController_Wayland_IVI::IVIControllerHandleScreenCallback(void* data, @@ -494,7 +475,7 @@ namespace ramses_internal uint32_t id_screen, ivi_controller_screen* screen) { - SystemCompositorController_Wayland_IVI* systemCompositorController = + auto* systemCompositorController = static_cast(data); systemCompositorController->iviControllerHandleScreen(controller, id_screen, screen); } @@ -503,7 +484,7 @@ namespace ramses_internal struct ivi_controller* controller, uint32_t id_layer) { - SystemCompositorController_Wayland_IVI* systemCompositorController = + auto* systemCompositorController = static_cast(data); systemCompositorController->iviControllerHandleLayer(controller, id_layer); } @@ -512,23 +493,17 @@ namespace ramses_internal ivi_controller* controller, uint32_t id_surface) { - SystemCompositorController_Wayland_IVI* systemCompositorController = + auto* systemCompositorController = static_cast(data); systemCompositorController->iviControllerHandleSurface(controller, id_surface); } - void SystemCompositorController_Wayland_IVI::IVIControllerHandleErrorCallback(void* data, - ivi_controller* controller, - int32_t objectId, - int32_t objectType, - int32_t errorCode, - const char* errorText) + void SystemCompositorController_Wayland_IVI::IVIControllerHandleErrorCallback([[maybe_unused]] void* data, + [[maybe_unused]] ivi_controller* controller, + [[maybe_unused]] int32_t objectId, + [[maybe_unused]] int32_t objectType, + [[maybe_unused]] int32_t errorCode, + [[maybe_unused]] const char* errorText) { - UNUSED(data); - UNUSED(controller); - UNUSED(objectId); - UNUSED(objectType); - UNUSED(errorCode); - UNUSED(errorText); } } diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h similarity index 94% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h index 7557ff26e..f53ffaa63 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLER_WAYLAND_IVI_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLER_WAYLAND_IVI_H +#pragma once -#include "RendererAPI/ISystemCompositorController.h" +#include "internal/RendererLib/PlatformInterface/ISystemCompositorController.h" #include "wayland-client-protocol.h" -#include "Utils/Warnings.h" +#include "internal/Core/Utils/Warnings.h" PUSH_DISABLE_C_STYLE_CAST_WARNING #include "ivi-controller-client-protocol.h" POP_DISABLE_C_STYLE_CAST_WARNING #include #include -namespace ramses_internal +namespace ramses::internal { class IVIControllerSurface; class IVIControllerScreen; @@ -82,7 +81,7 @@ namespace ramses_internal const struct Registry_Listener : public wl_registry_listener { - Registry_Listener() + Registry_Listener() : wl_registry_listener() { global = RegistryHandleGlobalCallback; global_remove = RegistryHandleGlobalRemoveCallback; @@ -91,7 +90,7 @@ namespace ramses_internal const struct IVIController_Listener : public ivi_controller_listener { - IVIController_Listener() + IVIController_Listener() : ivi_controller_listener() { screen = IVIControllerHandleScreenCallback; layer = IVIControllerHandleLayerCallback; @@ -101,5 +100,3 @@ namespace ramses_internal } m_iviControllerListener; }; } - -#endif diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/WaylandOutput.cpp b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.cpp similarity index 62% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/src/WaylandOutput.cpp rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.cpp index ff8df182b..24222174a 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/src/WaylandOutput.cpp +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SystemCompositorController_Wayland_IVI/WaylandOutput.h" -#include "Utils/LogMacros.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses_internal +namespace ramses::internal { WaylandOutput::WaylandOutput(wl_registry* registry, uint32_t name) { @@ -54,34 +54,23 @@ namespace ramses_internal } } - void WaylandOutput::OutputHandleGeometryCallback(void* data, - wl_output* wl_output, - int32_t x, - int32_t y, - int32_t physical_width, - int32_t physical_height, - int32_t subpixel, - const char* make, - const char* model, - int32_t transform) + void WaylandOutput::OutputHandleGeometryCallback([[maybe_unused]] void* data, + [[maybe_unused]] wl_output* wl_output, + [[maybe_unused]] int32_t x, + [[maybe_unused]] int32_t y, + [[maybe_unused]] int32_t physical_width, + [[maybe_unused]] int32_t physical_height, + [[maybe_unused]] int32_t subpixel, + [[maybe_unused]] const char* make, + [[maybe_unused]] const char* model, + [[maybe_unused]] int32_t transform) { - UNUSED(data); - UNUSED(wl_output); - UNUSED(x); - UNUSED(y); - UNUSED(physical_width); - UNUSED(physical_height); - UNUSED(subpixel); - UNUSED(make); - UNUSED(model); - UNUSED(transform); } void WaylandOutput::OutputHandleModeCallback( - void* data, wl_output* wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) + void* data, [[maybe_unused]] wl_output* wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - UNUSED(wl_output); - WaylandOutput* waylandOutput = static_cast(data); + auto* waylandOutput = static_cast(data); waylandOutput->outputHandleMode(flags, width, height, refresh); } } diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/WaylandOutput.h b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.h similarity index 91% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/WaylandOutput.h rename to src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.h index 4bc9f0796..e0afc092a 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/include/SystemCompositorController_Wayland_IVI/WaylandOutput.h +++ b/src/renderer/internal/Platform/Wayland/IVI/SystemCompositorController/WaylandOutput.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDOUTPUT_H -#define RAMSES_WAYLANDOUTPUT_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" #include "wayland-client-protocol.h" -namespace ramses_internal +#include + +namespace ramses::internal { class WaylandOutput { @@ -40,7 +40,7 @@ namespace ramses_internal uint32_t m_screenId = 0; const struct Output_Listener : public wl_output_listener { - Output_Listener() + Output_Listener() : wl_output_listener() { geometry = OutputHandleGeometryCallback; mode = OutputHandleModeCallback; @@ -48,5 +48,3 @@ namespace ramses_internal } m_outputListener; }; } - -#endif diff --git a/renderer/Platform/Window_Wayland_IVI/src/Window_Wayland_IVI.cpp b/src/renderer/internal/Platform/Wayland/IVI/Window_Wayland_IVI.cpp similarity index 93% rename from renderer/Platform/Window_Wayland_IVI/src/Window_Wayland_IVI.cpp rename to src/renderer/internal/Platform/Wayland/IVI/Window_Wayland_IVI.cpp index 11f3220ff..385a33619 100644 --- a/renderer/Platform/Window_Wayland_IVI/src/Window_Wayland_IVI.cpp +++ b/src/renderer/internal/Platform/Wayland/IVI/Window_Wayland_IVI.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Wayland_IVI/Window_Wayland_IVI.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/IVI/Window_Wayland_IVI.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include "ivi-application-client-protocol.h" -namespace ramses_internal +namespace ramses::internal { Window_Wayland_IVI::Window_Wayland_IVI(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler, @@ -67,7 +67,7 @@ namespace ramses_internal void Window_Wayland_IVI::configureCallback(void* userData, ivi_surface* /*surface*/, int32_t width, int32_t height) { - Window_Wayland_IVI* window = static_cast(userData); + auto* window = static_cast(userData); (window->m_eventHandler).onResize(width, height); } diff --git a/renderer/Platform/Window_Wayland_IVI/include/Window_Wayland_IVI/Window_Wayland_IVI.h b/src/renderer/internal/Platform/Wayland/IVI/Window_Wayland_IVI.h similarity index 89% rename from renderer/Platform/Window_Wayland_IVI/include/Window_Wayland_IVI/Window_Wayland_IVI.h rename to src/renderer/internal/Platform/Wayland/IVI/Window_Wayland_IVI.h index d36539b1d..ead0afd18 100644 --- a/renderer/Platform/Window_Wayland_IVI/include/Window_Wayland_IVI/Window_Wayland_IVI.h +++ b/src/renderer/internal/Platform/Wayland/IVI/Window_Wayland_IVI.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_WAYLAND_IVI_H -#define RAMSES_WINDOW_WAYLAND_IVI_H +#pragma once -#include "Window_Wayland/Window_Wayland.h" +#include "internal/Platform/Wayland/Window_Wayland.h" #include "ivi-application-client-protocol.h" struct ivi_application; struct ivi_surface; -namespace ramses_internal +namespace ramses::internal { class Window_Wayland_IVI : public Window_Wayland { @@ -36,7 +35,7 @@ namespace ramses_internal const struct IVI_Surface_Listener : public ivi_surface_listener { - IVI_Surface_Listener() + IVI_Surface_Listener() : ivi_surface_listener() { configure = configureCallback; } @@ -44,5 +43,3 @@ namespace ramses_internal } m_IVISurfaceListener; }; } - -#endif diff --git a/renderer/Platform/Window_Wayland/src/InputHandling_Wayland.cpp b/src/renderer/internal/Platform/Wayland/InputHandling_Wayland.cpp similarity index 68% rename from renderer/Platform/Window_Wayland/src/InputHandling_Wayland.cpp rename to src/renderer/internal/Platform/Wayland/InputHandling_Wayland.cpp index fd33c927e..aebafdabe 100644 --- a/renderer/Platform/Window_Wayland/src/InputHandling_Wayland.cpp +++ b/src/renderer/internal/Platform/Wayland/InputHandling_Wayland.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Wayland/InputHandling_Wayland.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" +#include "internal/Platform/Wayland/InputHandling_Wayland.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" #include -namespace ramses_internal +namespace ramses::internal { InputHandling_Wayland::InputHandling_Wayland(IWindowEventHandler& windowEventHandler) : m_windowEventHandler(windowEventHandler) @@ -83,41 +83,33 @@ namespace ramses_internal } - void InputHandling_Wayland::PointerHandleEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy) + void InputHandling_Wayland::PointerHandleEnter([[maybe_unused]] void* data, + [[maybe_unused]] wl_pointer* pointer, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] wl_surface* surface, + [[maybe_unused]] wl_fixed_t sx, + [[maybe_unused]] wl_fixed_t sy) { - UNUSED(data); - UNUSED(pointer); - UNUSED(serial); - UNUSED(surface); - UNUSED(sx); - UNUSED(sy); } - void InputHandling_Wayland::PointerHandleLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface) + void InputHandling_Wayland::PointerHandleLeave([[maybe_unused]] void* data, + [[maybe_unused]] wl_pointer* pointer, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] wl_surface* surface) { - UNUSED(data); - UNUSED(pointer); - UNUSED(serial); - UNUSED(surface); } - void InputHandling_Wayland::PointerHandleMotion(void *data, wl_pointer* pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) + void InputHandling_Wayland::PointerHandleMotion(void* data, [[maybe_unused]] wl_pointer* pointer, [[maybe_unused]] uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { - UNUSED(pointer); - UNUSED(time); - InputHandling_Wayland& inputHandling = *static_cast(data); - inputHandling.m_cursorPosX = wl_fixed_to_double(sx); - inputHandling.m_cursorPosY = wl_fixed_to_double(sy); - inputHandling.m_windowEventHandler.onMouseEvent(EMouseEventType_Move, inputHandling.m_cursorPosX, inputHandling.m_cursorPosY); + inputHandling.m_cursorPosX = wl_fixed_to_int(sx); + inputHandling.m_cursorPosY = wl_fixed_to_int(sy); + inputHandling.m_windowEventHandler.onMouseEvent(EMouseEvent::Move, inputHandling.m_cursorPosX, inputHandling.m_cursorPosY); } - void InputHandling_Wayland::PointerHandleButton(void *data, wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) + void InputHandling_Wayland::PointerHandleButton( + void* data, [[maybe_unused]] wl_pointer* pointer, [[maybe_unused]] uint32_t serial, [[maybe_unused]] uint32_t time, uint32_t button, uint32_t state) { - UNUSED(pointer); - UNUSED(serial); - UNUSED(time); - InputHandling_Wayland& inputHandling = *static_cast(data); const bool buttonPressed = (state == WL_POINTER_BUTTON_STATE_PRESSED); @@ -127,36 +119,33 @@ namespace ramses_internal { inputHandling.m_leftMouseButtonDown = buttonPressed; inputHandling.m_windowEventHandler.onMouseEvent( - buttonPressed ? EMouseEventType_LeftButtonDown : EMouseEventType_LeftButtonUp, + buttonPressed ? EMouseEvent::LeftButtonDown : EMouseEvent::LeftButtonUp, inputHandling.m_cursorPosX, inputHandling.m_cursorPosY); + break; } - break; case BTN_MIDDLE: { inputHandling.m_middleMouseButtonDown = buttonPressed; inputHandling.m_windowEventHandler.onMouseEvent( - buttonPressed ? EMouseEventType_MiddleButtonDown : EMouseEventType_MiddleButtonUp, + buttonPressed ? EMouseEvent::MiddleButtonDown : EMouseEvent::MiddleButtonUp, inputHandling.m_cursorPosX, inputHandling.m_cursorPosY); - + break; } - break; case BTN_RIGHT: { inputHandling.m_rightMouseButtonDown = buttonPressed; inputHandling.m_windowEventHandler.onMouseEvent( - buttonPressed ? EMouseEventType_RightButtonDown : EMouseEventType_RightButtonUp, + buttonPressed ? EMouseEvent::RightButtonDown : EMouseEvent::RightButtonUp, inputHandling.m_cursorPosX, inputHandling.m_cursorPosY); + break; } - break; - + default: + break; } } - void InputHandling_Wayland::PointerHandleAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) + void InputHandling_Wayland::PointerHandleAxis(void* data, [[maybe_unused]] wl_pointer* pointer, [[maybe_unused]] uint32_t time, uint32_t axis, wl_fixed_t value) { - UNUSED(pointer); - UNUSED(time); - InputHandling_Wayland& inputHandling = *static_cast(data); switch (axis) @@ -167,7 +156,7 @@ namespace ramses_internal case WL_POINTER_AXIS_VERTICAL_SCROLL: { const double delta = wl_fixed_to_double(value); - const EMouseEventType event = (delta < 0) ? EMouseEventType_WheelUp : EMouseEventType_WheelDown; + const EMouseEvent event = (delta < 0) ? EMouseEvent::WheelUp : EMouseEvent::WheelDown; inputHandling.m_windowEventHandler.onMouseEvent(event, inputHandling.m_cursorPosX, inputHandling.m_cursorPosY); } break; @@ -176,56 +165,46 @@ namespace ramses_internal } } - void InputHandling_Wayland::KeyboardHandleKeymap(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) + void InputHandling_Wayland::KeyboardHandleKeymap( + [[maybe_unused]] void* data, [[maybe_unused]] wl_keyboard* keyboard, [[maybe_unused]] uint32_t format, [[maybe_unused]] int fd, [[maybe_unused]] uint32_t size) { - UNUSED(data); - UNUSED(keyboard); - UNUSED(format); - UNUSED(fd); - UNUSED(size); } - void InputHandling_Wayland::KeyboardHandleEnter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) + void InputHandling_Wayland::KeyboardHandleEnter([[maybe_unused]] void* data, + [[maybe_unused]] wl_keyboard* keyboard, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] wl_surface* surface, + [[maybe_unused]] wl_array* keys) { LOG_TRACE(CONTEXT_RENDERER, "keyboard handle enter"); - UNUSED(data); - UNUSED(keyboard); - UNUSED(serial); - UNUSED(surface); - UNUSED(keys); } - void InputHandling_Wayland::KeyboardHandleLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) + void InputHandling_Wayland::KeyboardHandleLeave([[maybe_unused]] void* data, + [[maybe_unused]] wl_keyboard* keyboard, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] wl_surface* surface) { LOG_TRACE(CONTEXT_RENDERER, "keyboard handle leave"); - UNUSED(data); - UNUSED(keyboard); - UNUSED(serial); - UNUSED(surface); } - void InputHandling_Wayland::KeyboardHandleKey(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) + void InputHandling_Wayland::KeyboardHandleKey( + [[maybe_unused]] void* data, [[maybe_unused]] wl_keyboard* keyboard, [[maybe_unused]] uint32_t serial, [[maybe_unused]] uint32_t time, uint32_t key, uint32_t state_w) { - UNUSED(data); - UNUSED(keyboard); - UNUSED(serial); - UNUSED(time); - const EKeyCode ramsesKeyCode = RamsesKeyCodeFromWaylandKey(key); - EKeyModifier keyModifier = EKeyModifier_NoModifier; + EKeyModifier keyModifier = EKeyModifier::NoModifier; switch (ramsesKeyCode) { case EKeyCode_ShiftLeft: case EKeyCode_ShiftRight: - keyModifier = EKeyModifier_Shift; + keyModifier = EKeyModifier::Shift; break; case EKeyCode_ControlLeft: case EKeyCode_ControlRight: - keyModifier = EKeyModifier_Ctrl; + keyModifier = EKeyModifier::Ctrl; break; case EKeyCode_AltRight: - keyModifier = EKeyModifier_Alt; + keyModifier = EKeyModifier::Alt; break; default: break; @@ -236,33 +215,32 @@ namespace ramses_internal switch (state_w) { case WL_KEYBOARD_KEY_STATE_PRESSED: - inputHandling.m_keyModifiers |= keyModifier; - inputHandling.m_windowEventHandler.onKeyEvent(EKeyEventType_Pressed, inputHandling.m_keyModifiers, ramsesKeyCode); + inputHandling.m_keyModifiers.setFlag(keyModifier, true); + inputHandling.m_windowEventHandler.onKeyEvent(EKeyEvent::Pressed, inputHandling.m_keyModifiers, ramsesKeyCode); break; case WL_KEYBOARD_KEY_STATE_RELEASED: - inputHandling.m_keyModifiers &= ~keyModifier; - inputHandling.m_windowEventHandler.onKeyEvent(EKeyEventType_Released, inputHandling.m_keyModifiers, ramsesKeyCode); + inputHandling.m_keyModifiers.setFlag(keyModifier, false); + inputHandling.m_windowEventHandler.onKeyEvent(EKeyEvent::Released, inputHandling.m_keyModifiers, ramsesKeyCode); break; case 2://WL_KEYBOARD_KEY_STATE_REPEAT: // not availabe in current wayland header but send by winston compositor - inputHandling.m_windowEventHandler.onKeyEvent(EKeyEventType_Pressed, inputHandling.m_keyModifiers, ramsesKeyCode); + inputHandling.m_windowEventHandler.onKeyEvent(EKeyEvent::Pressed, inputHandling.m_keyModifiers, ramsesKeyCode); break; default: LOG_WARN(CONTEXT_RENDERER, "unhandled wayland keyboard handle key event: " << state_w); } } - void InputHandling_Wayland::KeyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) + void InputHandling_Wayland::KeyboardHandleModifiers([[maybe_unused]] void* data, + [[maybe_unused]] wl_keyboard* keyboard, + [[maybe_unused]] uint32_t serial, + [[maybe_unused]] uint32_t mods_depressed, + [[maybe_unused]] uint32_t mods_latched, + [[maybe_unused]] uint32_t mods_locked, + [[maybe_unused]] uint32_t group) { - UNUSED(data); - UNUSED(keyboard); - UNUSED(serial); - UNUSED(mods_depressed); - UNUSED(mods_latched); - UNUSED(mods_locked); - UNUSED(group); } - void InputHandling_Wayland::SeatHandleCapabilities(void *data, wl_seat *seat, unsigned int caps) + void InputHandling_Wayland::SeatHandleCapabilities(void* data, wl_seat* seat, unsigned int caps) { InputHandling_Wayland& inputHandling = *static_cast(data); diff --git a/renderer/Platform/Window_Wayland/include/Window_Wayland/InputHandling_Wayland.h b/src/renderer/internal/Platform/Wayland/InputHandling_Wayland.h similarity index 87% rename from renderer/Platform/Window_Wayland/include/Window_Wayland/InputHandling_Wayland.h rename to src/renderer/internal/Platform/Wayland/InputHandling_Wayland.h index ba3f90355..c467e59f5 100644 --- a/renderer/Platform/Window_Wayland/include/Window_Wayland/InputHandling_Wayland.h +++ b/src/renderer/internal/Platform/Wayland/InputHandling_Wayland.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INPUTHANDLING_WAYLAND_H -#define RAMSES_INPUTHANDLING_WAYLAND_H +#pragma once -#include "RendererAPI/IWindowEventHandler.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" -#include "RendererLib/EKeyCode.h" -#include "RendererLib/EKeyModifier.h" +#include "internal/RendererLib/Enums/EKeyCode.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" #include "wayland-client-protocol.h" -namespace ramses_internal +namespace ramses::internal { class InputHandling_Wayland { @@ -47,16 +46,16 @@ namespace ramses_internal wl_keyboard* m_keyboard = nullptr; wl_pointer* m_pointer = nullptr; - uint32_t m_keyModifiers = EKeyModifier_NoModifier; - double m_cursorPosX = 0.0; - double m_cursorPosY = 0.0; + KeyModifiers m_keyModifiers; + int m_cursorPosX = 0; + int m_cursorPosY = 0; bool m_leftMouseButtonDown = false; bool m_rightMouseButtonDown = false; bool m_middleMouseButtonDown = false; const struct Seat_Listener : public wl_seat_listener { - Seat_Listener() + Seat_Listener() : wl_seat_listener() { capabilities = SeatHandleCapabilities; } @@ -64,7 +63,7 @@ namespace ramses_internal const struct Pointer_Listener : public wl_pointer_listener { - Pointer_Listener() + Pointer_Listener() : wl_pointer_listener() { enter = PointerHandleEnter; leave = PointerHandleLeave; @@ -76,7 +75,7 @@ namespace ramses_internal const struct Keyboard_Listener : public wl_keyboard_listener { - Keyboard_Listener() + Keyboard_Listener() : wl_keyboard_listener() { keymap = KeyboardHandleKeymap; enter = KeyboardHandleEnter; @@ -87,5 +86,3 @@ namespace ramses_internal } m_keyboardListener; }; } - -#endif diff --git a/renderer/Platform/Platform_Wayland_EGL/src/Logger_Wayland.cpp b/src/renderer/internal/Platform/Wayland/Logger_Wayland.cpp similarity index 90% rename from renderer/Platform/Platform_Wayland_EGL/src/Logger_Wayland.cpp rename to src/renderer/internal/Platform/Wayland/Logger_Wayland.cpp index a828fb274..2da049085 100644 --- a/renderer/Platform/Platform_Wayland_EGL/src/Logger_Wayland.cpp +++ b/src/renderer/internal/Platform/Wayland/Logger_Wayland.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Wayland_EGL/Logger_Wayland.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/Logger_Wayland.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { namespace { diff --git a/renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL/Logger_Wayland.h b/src/renderer/internal/Platform/Wayland/Logger_Wayland.h similarity index 85% rename from renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL/Logger_Wayland.h rename to src/renderer/internal/Platform/Wayland/Logger_Wayland.h index ad2a3b33f..5408769ca 100644 --- a/renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL/Logger_Wayland.h +++ b/src/renderer/internal/Platform/Wayland/Logger_Wayland.h @@ -6,15 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGGER_WAYLAND_H -#define RAMSES_LOGGER_WAYLAND_H +#pragma once -namespace ramses_internal +namespace ramses::internal { namespace Logger_Wayland { void RedirectToRamsesLogger(); } } - -#endif diff --git a/renderer/Platform/Platform_Wayland_EGL/src/Platform_Wayland_EGL.cpp b/src/renderer/internal/Platform/Wayland/Platform_Wayland_EGL.cpp similarity index 64% rename from renderer/Platform/Platform_Wayland_EGL/src/Platform_Wayland_EGL.cpp rename to src/renderer/internal/Platform/Wayland/Platform_Wayland_EGL.cpp index 4008015c9..f32be18b4 100644 --- a/renderer/Platform/Platform_Wayland_EGL/src/Platform_Wayland_EGL.cpp +++ b/src/renderer/internal/Platform/Wayland/Platform_Wayland_EGL.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Wayland_EGL/Platform_Wayland_EGL.h" -#include "Platform_Wayland_EGL/Logger_Wayland.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RendererConfig.h" -#include "Platform_Base/EmbeddedCompositor_Dummy.h" -#include "EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/TextureUploadingAdapter_Wayland.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/Platform_Wayland_EGL.h" +#include "internal/Platform/Wayland/Logger_Wayland.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/TextureUploadingAdapter_Wayland.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { Platform_Wayland_EGL::Platform_Wayland_EGL(const RendererConfig& rendererConfig) : Platform_EGL(rendererConfig) @@ -24,11 +24,9 @@ namespace ramses_internal Logger_Wayland::RedirectToRamsesLogger(); } - Platform_Wayland_EGL::~Platform_Wayland_EGL() - { - } + Platform_Wayland_EGL::~Platform_Wayland_EGL() = default; - bool Platform_Wayland_EGL::isCreatingWaylandEmbeddedCompositorRequired(const DisplayConfig& displayConfig) const + bool Platform_Wayland_EGL::IsCreatingWaylandEmbeddedCompositorRequired(const DisplayConfig& displayConfig) { //EC should be created if (any of) display config params are set const bool areConfigParametersForEmbeddedCompositorSet = !displayConfig.getWaylandSocketEmbedded().empty() @@ -40,33 +38,30 @@ namespace ramses_internal bool Platform_Wayland_EGL::createEmbeddedCompositor(const DisplayConfig& displayConfig) { //TODO Mohamed: remove use of EC dummy as soon as it is possible to create multiple displays on wayland - if (!isCreatingWaylandEmbeddedCompositorRequired(displayConfig)) + if (!IsCreatingWaylandEmbeddedCompositorRequired(displayConfig)) { LOG_INFO(CONTEXT_RENDERER, "Embedded compositor not created because RendererConfig parameters were not set"); return Platform_EGL::createEmbeddedCompositor(displayConfig); } - else - { - auto compositor = std::make_unique(displayConfig, *m_context); - if (compositor->init()) - m_embeddedCompositor = std::move(compositor); + auto compositor = std::make_unique(displayConfig, static_cast(*m_context)); + if (compositor->init()) + m_embeddedCompositor = std::move(compositor); - return m_embeddedCompositor != nullptr; - } + return m_embeddedCompositor != nullptr; } void Platform_Wayland_EGL::createTextureUploadingAdapter(const DisplayConfig& displayConfig) { assert(m_device); //TODO Mohamed: remove use of EC dummy as soon as it is possible to create multiple displays on wayland - if (!isCreatingWaylandEmbeddedCompositorRequired(displayConfig)) + if (!IsCreatingWaylandEmbeddedCompositorRequired(displayConfig)) { Platform_EGL::createTextureUploadingAdapter(displayConfig); } else { - const Window_Wayland* platformWindow = static_cast(m_window.get()); - const EmbeddedCompositor_Wayland* platformEmbeddedCompositor = static_cast(m_embeddedCompositor.get()); + const auto* platformWindow = static_cast(m_window.get()); + const auto* platformEmbeddedCompositor = static_cast(m_embeddedCompositor.get()); wl_display* windowWaylandDisplay = platformWindow->getNativeDisplayHandle(); wl_display* embeddedCompositingDisplay = platformEmbeddedCompositor->getEmbeddedCompositingDisplay(); diff --git a/renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL/Platform_Wayland_EGL.h b/src/renderer/internal/Platform/Wayland/Platform_Wayland_EGL.h similarity index 78% rename from renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL/Platform_Wayland_EGL.h rename to src/renderer/internal/Platform/Wayland/Platform_Wayland_EGL.h index 301ab96bf..5381d2af2 100644 --- a/renderer/Platform/Platform_Wayland_EGL/include/Platform_Wayland_EGL/Platform_Wayland_EGL.h +++ b/src/renderer/internal/Platform/Wayland/Platform_Wayland_EGL.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_WAYLAND_EGL_H -#define RAMSES_PLATFORM_WAYLAND_EGL_H +#pragma once -#include "Window_Wayland/Window_Wayland.h" -#include "Platform_EGL/Platform_EGL.h" +#include "internal/Platform/Wayland/Window_Wayland.h" +#include "internal/Platform/EGL/Platform_EGL.h" -namespace ramses_internal +namespace ramses::internal { class Platform_Wayland_EGL : public Platform_EGL { @@ -26,11 +25,9 @@ namespace ramses_internal [[nodiscard]] uint32_t getSwapInterval() const override; //TODO Mohamed: remove use of EC dummy as soon as it is possible to create multiple displays on wayland - [[nodiscard]] bool isCreatingWaylandEmbeddedCompositorRequired(const DisplayConfig& displayConfig) const; + [[nodiscard]] static bool IsCreatingWaylandEmbeddedCompositorRequired(const DisplayConfig& displayConfig); const std::chrono::microseconds m_frameCallbackMaxPollTime; }; } -#endif - diff --git a/renderer/Platform/WaylandUtilities/src/UnixDomainSocket.cpp b/src/renderer/internal/Platform/Wayland/UnixDomainSocket.cpp similarity index 91% rename from renderer/Platform/WaylandUtilities/src/UnixDomainSocket.cpp rename to src/renderer/internal/Platform/Wayland/UnixDomainSocket.cpp index 1290969ac..ceb5a3c12 100644 --- a/renderer/Platform/WaylandUtilities/src/UnixDomainSocket.cpp +++ b/src/renderer/internal/Platform/Wayland/UnixDomainSocket.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "WaylandUtilities/UnixDomainSocket.h" -#include "PlatformAbstraction/PlatformStringUtils.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/PlatformAbstraction/PlatformStringUtils.h" #include #include #include @@ -15,11 +15,12 @@ #include #include #include +#include -namespace ramses_internal +namespace ramses::internal { - UnixDomainSocket::UnixDomainSocket(const std::string& _socketFilename, const std::string& xdgRuntimeDir) - : m_xdgRuntimeDir(xdgRuntimeDir) + UnixDomainSocket::UnixDomainSocket(const std::string& _socketFilename, std::string xdgRuntimeDir) + : m_xdgRuntimeDir(std::move(xdgRuntimeDir)) , m_socketFilename(m_xdgRuntimeDir + "/" + _socketFilename) , m_socketFileLock(m_socketFilename + ".lock") { @@ -119,7 +120,7 @@ namespace ramses_internal return false; } - struct stat buf; + struct stat buf{}; if (fstat(fileDescriptor, &buf) < 0) { return false; @@ -131,7 +132,7 @@ namespace ramses_internal bool UnixDomainSocket::checkSocketFilePath() const { - struct stat socket_stat; + struct stat socket_stat{}; if (stat(m_socketFilename.c_str(), &socket_stat) < 0 ) { if (errno != ENOENT) @@ -145,7 +146,7 @@ namespace ramses_internal int UnixDomainSocket::createSocketLockFile() const { // create lock file, with close-on-exec and usr and grp RW rights - // NOLINTNEXTLINE(hicpp-signed-bitwise) + // NOLINTNEXTLINE(hicpp-signed-bitwise, cppcoreguidelines-pro-type-vararg) int lockFileFileDescriptor = open(m_socketFileLock.c_str(), O_CREAT | O_CLOEXEC, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); if (lockFileFileDescriptor < 0) @@ -206,14 +207,15 @@ namespace ramses_internal assert(socketFileDescriptor >= 0); // get the file descriptor flags - long flags = fcntl(socketFileDescriptor, F_GETFD); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive + const auto flags = fcntl(socketFileDescriptor, F_GETFD); if (flags == -1) { return -1; } // now add the close-on-exec flag to the file descriptor's flags - // NOLINTNEXTLINE(hicpp-signed-bitwise) + // NOLINTNEXTLINE(hicpp-signed-bitwise, cppcoreguidelines-pro-type-vararg) if (fcntl(socketFileDescriptor, F_SETFD, flags | FD_CLOEXEC) == -1) { return -1; @@ -223,7 +225,7 @@ namespace ramses_internal bool UnixDomainSocket::bindSocketToFile() const { - sockaddr_un addr; + sockaddr_un addr{}; const size_t sizeOfSockAddr = fillSockaddrForUnixDomain(addr); if (sizeOfSockAddr == 0) @@ -267,7 +269,7 @@ namespace ramses_internal bool UnixDomainSocket::connectSocketToFile(int socketFileDescriptor) const { - sockaddr_un addr; + sockaddr_un addr{}; const size_t sizeOfSockAddr = fillSockaddrForUnixDomain(addr); if (sizeOfSockAddr == 0) diff --git a/renderer/Platform/WaylandUtilities/include/WaylandUtilities/UnixDomainSocket.h b/src/renderer/internal/Platform/Wayland/UnixDomainSocket.h similarity index 86% rename from renderer/Platform/WaylandUtilities/include/WaylandUtilities/UnixDomainSocket.h rename to src/renderer/internal/Platform/Wayland/UnixDomainSocket.h index fbabcac72..259b8f571 100644 --- a/renderer/Platform/WaylandUtilities/include/WaylandUtilities/UnixDomainSocket.h +++ b/src/renderer/internal/Platform/Wayland/UnixDomainSocket.h @@ -6,22 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_UNIXDOMAINSOCKET_H -#define RAMSES_UNIXDOMAINSOCKET_H +#pragma once - -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { class UnixDomainSocket { public: - UnixDomainSocket(const std::string& socketFilename, const std::string& xdgRuntimeDir); + UnixDomainSocket(const std::string& socketFilename, std::string xdgRuntimeDir); ~UnixDomainSocket(); int createBoundFileDescriptor(); @@ -49,5 +47,3 @@ namespace ramses_internal std::vector m_connectedFileDescriptors; }; } - -#endif //RAMSES_UNIXDOMAINSOCKET_H diff --git a/renderer/Platform/WaylandEGLExtensionProcs/src/WaylandEGLExtensionProcs.cpp b/src/renderer/internal/Platform/Wayland/WaylandEGLExtensionProcs.cpp similarity index 66% rename from renderer/Platform/WaylandEGLExtensionProcs/src/WaylandEGLExtensionProcs.cpp rename to src/renderer/internal/Platform/Wayland/WaylandEGLExtensionProcs.cpp index 64e63dc26..b5d60ac13 100644 --- a/renderer/Platform/WaylandEGLExtensionProcs/src/WaylandEGLExtensionProcs.cpp +++ b/src/renderer/internal/Platform/Wayland/WaylandEGLExtensionProcs.cpp @@ -6,15 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/StringUtils.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/StringUtils.h" #include -namespace ramses_internal - +namespace ramses::internal { + inline std::string getString(const char* str) + { + return str != nullptr ? str : ""; + } + WaylandEGLExtensionProcs::WaylandEGLExtensionProcs(wl_display* waylandWindowDisplay) : m_eglDisplay(eglGetDisplay(waylandWindowDisplay)) , m_eglCreateImageKHR(nullptr) @@ -22,6 +26,7 @@ namespace ramses_internal , m_glEGLImageTargetTexture2DOES(nullptr) , m_eglBindWaylandDisplayWL(nullptr) , m_eglUnbindWaylandDisplayWL(nullptr) + , m_eglQueryWaylandBufferWL(nullptr) , m_extensionsSupported(false) , m_dmabufExtensionsSupported(false) { @@ -35,6 +40,7 @@ namespace ramses_internal , m_glEGLImageTargetTexture2DOES(nullptr) , m_eglBindWaylandDisplayWL(nullptr) , m_eglUnbindWaylandDisplayWL(nullptr) + , m_eglQueryWaylandBufferWL(nullptr) , m_extensionsSupported(false) , m_dmabufExtensionsSupported(false) { @@ -43,8 +49,12 @@ namespace ramses_internal void WaylandEGLExtensionProcs::Init() { - std::string eglExtensionsString(eglQueryString(m_eglDisplay, EGL_EXTENSIONS)); - std::string glExtensionsString(reinterpret_cast(glGetString(GL_EXTENSIONS))); + if (m_eglDisplay == EGL_NO_DISPLAY) + { + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::Init EGL_NO_DISPLAY"); + } + const auto eglExtensionsString = getString(eglQueryString(m_eglDisplay, EGL_EXTENSIONS)); + const auto glExtensionsString = getString(reinterpret_cast(glGetString(GL_EXTENSIONS))); const auto eglExtensions = StringUtils::TokenizeToSet(eglExtensionsString); const auto glExtensions = StringUtils::TokenizeToSet(glExtensionsString); @@ -68,6 +78,9 @@ namespace ramses_internal m_eglUnbindWaylandDisplayWL = reinterpret_cast(eglGetProcAddress("eglUnbindWaylandDisplayWL")); assert(m_eglUnbindWaylandDisplayWL != nullptr); + m_eglQueryWaylandBufferWL = reinterpret_cast(eglGetProcAddress("eglQueryWaylandBufferWL")); + assert(m_eglQueryWaylandBufferWL != nullptr); + m_extensionsSupported = true; } @@ -79,17 +92,14 @@ namespace ramses_internal } } - bool WaylandEGLExtensionProcs::CheckExtensionAvailable(const ramses_internal::HashSet& eglExtensions, const std::string& extensionName) + bool WaylandEGLExtensionProcs::CheckExtensionAvailable(const HashSet& eglExtensions, const std::string& extensionName) { if (eglExtensions.contains(extensionName)) { return true; } - else - { - LOG_INFO(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::CheckExtensionAvailable Extension " << extensionName << " not supported!"); - return false; - } + LOG_INFO(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::CheckExtensionAvailable Extension " << extensionName << " not supported!"); + return false; } EGLImageKHR WaylandEGLExtensionProcs::eglCreateImageKHR(EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint* attributeList) const @@ -98,12 +108,9 @@ namespace ramses_internal { return m_eglCreateImageKHR(m_eglDisplay, context, target, buffer, attributeList); } - else - { - LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglCreateImageKHR Extension not bound!"); - assert(false); - return EGL_NO_IMAGE; - } + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglCreateImageKHR Extension not bound!"); + assert(false); + return EGL_NO_IMAGE; } EGLBoolean WaylandEGLExtensionProcs::eglDestroyImageKHR(EGLImageKHR image) const @@ -112,12 +119,9 @@ namespace ramses_internal { return m_eglDestroyImageKHR(m_eglDisplay, image); } - else - { - LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglDestroyImageKHR Extension not bound!"); - assert(false); - return EGL_FALSE; - } + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglDestroyImageKHR Extension not bound!"); + assert(false); + return EGL_FALSE; } void WaylandEGLExtensionProcs::glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) const @@ -126,11 +130,8 @@ namespace ramses_internal { return m_glEGLImageTargetTexture2DOES(target, image); } - else - { - LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::glEGLImageTargetTexture2DOES Extension not bound!"); - assert(false); - } + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::glEGLImageTargetTexture2DOES Extension not bound!"); + assert(false); } EGLBoolean WaylandEGLExtensionProcs::eglBindWaylandDisplayWL(wl_display* waylandDisplay) const @@ -139,12 +140,9 @@ namespace ramses_internal { return m_eglBindWaylandDisplayWL(m_eglDisplay, waylandDisplay); } - else - { - LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglBindWaylandDisplayWL Extension not bound!"); - assert(false); - return EGL_FALSE; - } + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglBindWaylandDisplayWL Extension not bound!"); + assert(false); + return EGL_FALSE; } EGLBoolean WaylandEGLExtensionProcs::eglUnbindWaylandDisplayWL(wl_display* waylandDisplay) const @@ -153,12 +151,20 @@ namespace ramses_internal { return m_eglUnbindWaylandDisplayWL(m_eglDisplay, waylandDisplay); } - else + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglUnbindWaylandDisplayWL Extension not bound!"); + assert(false); + return EGL_FALSE; + } + + EGLBoolean WaylandEGLExtensionProcs::eglQueryWaylandBufferWL(wl_resource* buffer, EGLint attribute, EGLint* value) const + { + if (m_eglQueryWaylandBufferWL) { - LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglUnbindWaylandDisplayWL Extension not bound!"); - assert(false); - return EGL_FALSE; + return m_eglQueryWaylandBufferWL(m_eglDisplay, buffer, attribute, value); } + LOG_ERROR(CONTEXT_RENDERER, "WaylandEGLExtensionProcs::eglQueryWaylandBufferWL Extension not bound!"); + assert(false); + return EGL_FALSE; } bool WaylandEGLExtensionProcs::areExtensionsSupported()const @@ -170,4 +176,14 @@ namespace ramses_internal { return m_dmabufExtensionsSupported; } + + const char* WaylandEGLExtensionProcs::getTextureFormatName(EGLint textureFormat) + { + switch (textureFormat) + { + case EGL_TEXTURE_RGB: return "EGL_TEXTURE_RGB"; + case EGL_TEXTURE_RGBA: return "EGL_TEXTURE_RGBA"; + default: return ""; + } + } } diff --git a/renderer/Platform/WaylandEGLExtensionProcs/include/WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h b/src/renderer/internal/Platform/Wayland/WaylandEGLExtensionProcs.h similarity index 78% rename from renderer/Platform/WaylandEGLExtensionProcs/include/WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h rename to src/renderer/internal/Platform/Wayland/WaylandEGLExtensionProcs.h index ad33ef38b..30ce04960 100644 --- a/renderer/Platform/WaylandEGLExtensionProcs/include/WaylandEGLExtensionProcs/WaylandEGLExtensionProcs.h +++ b/src/renderer/internal/Platform/Wayland/WaylandEGLExtensionProcs.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDEGLEXTENSIONPROCS_H -#define RAMSES_WAYLANDEGLEXTENSIONPROCS_H +#pragma once -#include "Utils/Warnings.h" -#include "Collections/HashSet.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" WARNINGS_PUSH WARNING_DISABLE_LINUX(-Wdeprecated-declarations) @@ -27,7 +26,7 @@ WARNINGS_POP #include -namespace ramses_internal +namespace ramses::internal { class WaylandEGLExtensionProcs { @@ -40,27 +39,29 @@ namespace ramses_internal void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) const; EGLBoolean eglBindWaylandDisplayWL(wl_display* waylandDisplay) const; EGLBoolean eglUnbindWaylandDisplayWL(wl_display* waylandDisplay) const; + EGLBoolean eglQueryWaylandBufferWL(wl_resource* buffer, EGLint attribute, EGLint* value) const; [[nodiscard]] bool areExtensionsSupported()const; [[nodiscard]] bool areDmabufExtensionsSupported()const; + static const char* getTextureFormatName(EGLint textureFormat); + private: void Init(); - static bool CheckExtensionAvailable(const ramses_internal::HashSet& eglExtensions, const std::string& extensionName); + static bool CheckExtensionAvailable(const HashSet& eglExtensions, const std::string& extensionName); - const EGLDisplay m_eglDisplay; + EGLDisplay m_eglDisplay; PFNEGLCREATEIMAGEKHRPROC m_eglCreateImageKHR; PFNEGLDESTROYIMAGEKHRPROC m_eglDestroyImageKHR; PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES; PFNEGLBINDWAYLANDDISPLAYWL m_eglBindWaylandDisplayWL; PFNEGLUNBINDWAYLANDDISPLAYWL m_eglUnbindWaylandDisplayWL; + PFNEGLQUERYWAYLANDBUFFERWL m_eglQueryWaylandBufferWL; bool m_extensionsSupported; bool m_dmabufExtensionsSupported; }; } - -#endif diff --git a/renderer/Platform/WaylandUtilities/src/WaylandEnvironmentUtils.cpp b/src/renderer/internal/Platform/Wayland/WaylandEnvironmentUtils.cpp similarity index 86% rename from renderer/Platform/WaylandUtilities/src/WaylandEnvironmentUtils.cpp rename to src/renderer/internal/Platform/Wayland/WaylandEnvironmentUtils.cpp index 28bb71e8c..4d3949f6e 100644 --- a/renderer/Platform/WaylandUtilities/src/WaylandEnvironmentUtils.cpp +++ b/src/renderer/internal/Platform/Wayland/WaylandEnvironmentUtils.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" -#include "Utils/File.h" -#include "Utils/LoggingUtils.h" -#include "Utils/LogMacros.h" - -namespace ramses_internal +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal { namespace { @@ -24,7 +24,7 @@ namespace ramses_internal "WAYLAND_DISPLAY" }; - ENUM_TO_STRING(WaylandEnvironmentVariable, EnvironmentVariableNames, WaylandEnvironmentVariable::NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(WaylandEnvironmentVariable, EnvironmentVariableNames, WaylandEnvironmentVariable::WaylandDisplay); bool CheckXDGRuntimeDir() { @@ -53,23 +53,33 @@ namespace ramses_internal const bool socketIsAbsolute = (!socketFilename.empty()) && socketFilename[0] == '/'; const auto socketFullPath = (socketIsAbsolute ? socketFilename : (xdgRuntimeDir + "/" + socketFilename)); - if(!socketIsAbsolute) + if (!socketIsAbsolute) + { if(!CheckXDGRuntimeDir()) return; + } const File socketFile(socketFullPath); if (socketFile.exists()) + { LOG_INFO(CONTEXT_RENDERER, "WaylandEnvironmentUtils::LogEnvironmentState: Socket file " << socketFullPath << " exists."); + } else + { LOG_WARN(CONTEXT_RENDERER, "WaylandEnvironmentUtils::LogEnvironmentState: Socket file " << socketFullPath << " does not exist."); + } } bool CheckSocketFileDescritorExists(const std::string& socketFD) { - if (UnixDomainSocket::IsFileDescriptorForValidSocket(atoi(socketFD.c_str()))) + if (UnixDomainSocket::IsFileDescriptorForValidSocket(static_cast(strtol(socketFD.c_str(), nullptr, 0)))) + { LOG_INFO(CONTEXT_RENDERER, "WaylandEnvironmentUtils::LogEnvironmentState: Environment variable WAYLAND_SOCKET contains a valid socket file descriptor."); + } else + { LOG_WARN(CONTEXT_RENDERER, "WaylandEnvironmentUtils::LogEnvironmentState: Environment variable WAYLAND_SOCKET does not contain a valid socket file descriptor."); + } return true; } @@ -108,16 +118,26 @@ namespace ramses_internal << ", WAYLAND_DISPLAY=" << waylandDisplayEnvironmentVar << ", WAYLAND_SOCKET=" << waylandSocketEnvironmentVar); - if(!waylandDisplayName.empty()) + if (!waylandDisplayName.empty()) + { CheckSocketFileExists(xdgPathEnvironmentVar, waylandDisplayName); + } else if (waylandSocketFoundInEnvironmentVars && waylandDisplayFoundInEnvironmentVars) + { LOG_ERROR(CONTEXT_RENDERER, "WaylandEnvironmentUtils::LogEnvironmentState: Environment variables WAYLAND_DISPLAY and WAYLAND_SOCKET are both set."); + } else if (waylandSocketFoundInEnvironmentVars) + { CheckSocketFileDescritorExists(waylandSocketEnvironmentVar); + } else if (waylandDisplayFoundInEnvironmentVars) + { CheckSocketFileExists(xdgPathEnvironmentVar, waylandDisplayEnvironmentVar); + } else + { CheckXDGRuntimeDir(); + } } } } diff --git a/renderer/Platform/WaylandUtilities/include/WaylandUtilities/WaylandEnvironmentUtils.h b/src/renderer/internal/Platform/Wayland/WaylandEnvironmentUtils.h similarity index 84% rename from renderer/Platform/WaylandUtilities/include/WaylandUtilities/WaylandEnvironmentUtils.h rename to src/renderer/internal/Platform/Wayland/WaylandEnvironmentUtils.h index 03dfbb902..1a3fa019a 100644 --- a/renderer/Platform/WaylandUtilities/include/WaylandUtilities/WaylandEnvironmentUtils.h +++ b/src/renderer/internal/Platform/Wayland/WaylandEnvironmentUtils.h @@ -6,21 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDENVIRONMENTUTILS_H -#define RAMSES_WAYLANDENVIRONMENTUTILS_H - -#include +#pragma once +#include #include -namespace ramses_internal +namespace ramses::internal { enum class WaylandEnvironmentVariable : uint8_t { XDGRuntimeDir = 0, WaylandSocket, WaylandDisplay, - NUMBER_OF_ELEMENTS }; @@ -32,5 +29,3 @@ namespace ramses_internal void LogEnvironmentState(const std::string& waylandDisplayName); }; } - -#endif diff --git a/renderer/Platform/Window_Wayland/src/Window_Wayland.cpp b/src/renderer/internal/Platform/Wayland/Window_Wayland.cpp similarity index 90% rename from renderer/Platform/Window_Wayland/src/Window_Wayland.cpp rename to src/renderer/internal/Platform/Wayland/Window_Wayland.cpp index 2b7544e32..7c89e5793 100644 --- a/renderer/Platform/Window_Wayland/src/Window_Wayland.cpp +++ b/src/renderer/internal/Platform/Wayland/Window_Wayland.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Wayland/Window_Wayland.h" -#include "Utils/ThreadLocalLogForced.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "RendererLib/DisplayConfig.h" +#include "internal/Platform/Wayland/Window_Wayland.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/RendererLib/DisplayConfig.h" #include -namespace ramses_internal +namespace ramses::internal { Window_Wayland::Window_Wayland(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler, @@ -61,7 +61,7 @@ namespace ramses_internal return false; } - m_wlContext.native_window = wl_egl_window_create(m_wlContext.surface, m_width, m_height); + m_wlContext.native_window = wl_egl_window_create(m_wlContext.surface, static_cast(m_width), static_cast(m_height)); if (!m_wlContext.native_window) { LOG_ERROR(CONTEXT_RENDERER, "Window_Wayland::init Error: wl_egl_window_create() failed"); @@ -177,24 +177,20 @@ namespace ramses_internal return m_wlContext.native_window; } - void Window_Wayland::RegistryGlobalCreated(void* data, wl_registry* wl_registry, uint32_t name, const char* interface, uint32_t version) + void Window_Wayland::RegistryGlobalCreated(void* data, wl_registry* wl_registry, uint32_t name, const char* interface, [[maybe_unused]] uint32_t version) { - UNUSED(version); Window_Wayland& window = *static_cast(data); window.registryGlobalCreated(wl_registry, name, interface, version); } - void Window_Wayland::RegistryGlobalRemoved(void* data, wl_registry* wl_registry, uint32_t name) + void Window_Wayland::RegistryGlobalRemoved([[maybe_unused]] void* data, [[maybe_unused]] wl_registry* wl_registry, [[maybe_unused]] uint32_t name) { - UNUSED(data); - UNUSED(wl_registry); - UNUSED(name); - //callback to destroy globals not implemented in neither winston or weston, globals are destroyed in destructor. + // callback to destroy globals not implemented in neither winston or weston, globals are destroyed in destructor. } - void Window_Wayland::FrameRenderingDoneCallback(void* userData, wl_callback* callbackWaylandObject, uint32_t) + void Window_Wayland::FrameRenderingDoneCallback(void* userData, wl_callback* callbackWaylandObject, uint32_t /*unused*/) { - Window_Wayland* window = static_cast(userData); + auto* window = static_cast(userData); //assert that it is a "done" event for the last registered callback assert(callbackWaylandObject == window->m_wlContext.frameRenderingDoneWaylandCallbacObject); @@ -215,9 +211,8 @@ namespace ramses_internal wl_callback_add_listener(m_wlContext.frameRenderingDoneWaylandCallbacObject, &m_frameRenderingDoneCallbackListener, this); } - bool Window_Wayland::setFullscreen(bool fullscreen) + bool Window_Wayland::setFullscreen([[maybe_unused]] bool fullscreen) { - UNUSED(fullscreen); return true; } @@ -243,9 +238,8 @@ namespace ramses_internal wl_display_dispatch(m_wlContext.display); } - void Window_Wayland::registryGlobalCreated(wl_registry* wl_registry, uint32_t name, const char* interface, uint32_t version) + void Window_Wayland::registryGlobalCreated(wl_registry* wl_registry, uint32_t name, const char* interface, [[maybe_unused]] uint32_t version) { - UNUSED(version); if (0 == strcmp(interface, "wl_compositor")) { m_wlContext.compositor = diff --git a/renderer/Platform/Window_Wayland/include/Window_Wayland/Window_Wayland.h b/src/renderer/internal/Platform/Wayland/Window_Wayland.h similarity index 90% rename from renderer/Platform/Window_Wayland/include/Window_Wayland/Window_Wayland.h rename to src/renderer/internal/Platform/Wayland/Window_Wayland.h index eddcf56b3..899867a87 100644 --- a/renderer/Platform/Window_Wayland/include/Window_Wayland/Window_Wayland.h +++ b/src/renderer/internal/Platform/Wayland/Window_Wayland.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_WAYLAND_H -#define RAMSES_WINDOW_WAYLAND_H +#pragma once -#include "Platform_Base/Window_Base.h" -#include "Window_Wayland/WlContext.h" +#include "internal/RendererLib/PlatformBase/Window_Base.h" +#include "internal/Platform/Wayland/WlContext.h" #include "InputHandling_Wayland.h" #include #include -namespace ramses_internal +namespace ramses::internal { class Window_Wayland : public Window_Base { @@ -62,7 +61,7 @@ namespace ramses_internal const struct FrameRenderingDoneCallback_Listener : public wl_callback_listener { - FrameRenderingDoneCallback_Listener() + FrameRenderingDoneCallback_Listener() : wl_callback_listener() { done = FrameRenderingDoneCallback; } @@ -70,7 +69,7 @@ namespace ramses_internal const struct Registry_Listener : public wl_registry_listener { - Registry_Listener() + Registry_Listener() : wl_registry_listener() { global = RegistryGlobalCreated; global_remove = RegistryGlobalRemoved; @@ -80,5 +79,3 @@ namespace ramses_internal const std::chrono::microseconds m_frameCallbackMaxPollTime; }; } - -#endif diff --git a/renderer/Platform/Window_Wayland/include/Window_Wayland/WlContext.h b/src/renderer/internal/Platform/Wayland/WlContext.h similarity index 86% rename from renderer/Platform/Window_Wayland/include/Window_Wayland/WlContext.h rename to src/renderer/internal/Platform/Wayland/WlContext.h index 08cee9e59..921312fe4 100644 --- a/renderer/Platform/Window_Wayland/include/Window_Wayland/WlContext.h +++ b/src/renderer/internal/Platform/Wayland/WlContext.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WLCONTEXT_H -#define RAMSES_WLCONTEXT_H +#pragma once -#include "RendererAPI/IWindowEventHandler.h" -#include "RendererLib/EKeyModifier.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" #include struct wl_display; @@ -23,14 +22,10 @@ struct wl_keyboard; struct wl_pointer; struct wl_egl_window; -namespace ramses_internal +namespace ramses::internal { struct WlContext { - WlContext() - { - } - wl_display* display = nullptr; int displayFD = -1; wl_registry* registry = nullptr; @@ -46,5 +41,3 @@ namespace ramses_internal bool previousFrameRenderingDone = true; }; } - -#endif diff --git a/renderer/Platform/Platform_Wayland_Shell_EGL/src/Platform_Wayland_Shell_EGL_ES_3_0.cpp b/src/renderer/internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.cpp similarity index 79% rename from renderer/Platform/Platform_Wayland_Shell_EGL/src/Platform_Wayland_Shell_EGL_ES_3_0.cpp rename to src/renderer/internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.cpp index 5a269122c..ac12c9220 100644 --- a/renderer/Platform/Platform_Wayland_Shell_EGL/src/Platform_Wayland_Shell_EGL_ES_3_0.cpp +++ b/src/renderer/internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Wayland_Shell_EGL/Platform_Wayland_Shell_EGL_ES_3_0.h" -#include "Window_Wayland_Shell/Window_Wayland_Shell.h" -#include "RendererLib/RendererConfig.h" -#include "RendererLib/DisplayConfig.h" +#include "internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.h" +#include "internal/Platform/Wayland/WlShell/Window_Wayland_Shell.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/DisplayConfig.h" -namespace ramses_internal +namespace ramses::internal { Platform_Wayland_Shell_EGL_ES_3_0::Platform_Wayland_Shell_EGL_ES_3_0(const RendererConfig& rendererConfig) : Platform_Wayland_EGL(rendererConfig) diff --git a/renderer/Platform/Platform_Wayland_Shell_EGL/include/Platform_Wayland_Shell_EGL/Platform_Wayland_Shell_EGL_ES_3_0.h b/src/renderer/internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.h similarity index 81% rename from renderer/Platform/Platform_Wayland_Shell_EGL/include/Platform_Wayland_Shell_EGL/Platform_Wayland_Shell_EGL_ES_3_0.h rename to src/renderer/internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.h index 2fce6402e..0b1628244 100644 --- a/renderer/Platform/Platform_Wayland_Shell_EGL/include/Platform_Wayland_Shell_EGL/Platform_Wayland_Shell_EGL_ES_3_0.h +++ b/src/renderer/internal/Platform/Wayland/WlShell/Platform_Wayland_Shell_EGL_ES_3_0.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_WAYLAND_SHELL_EGL_ES_3_0_H -#define RAMSES_PLATFORM_WAYLAND_SHELL_EGL_ES_3_0_H +#pragma once -#include "Platform_Wayland_EGL/Platform_Wayland_EGL.h" +#include "internal/Platform/Wayland/Platform_Wayland_EGL.h" -namespace ramses_internal +namespace ramses::internal { class Platform_Wayland_Shell_EGL_ES_3_0 : public Platform_Wayland_EGL { @@ -23,5 +22,3 @@ namespace ramses_internal }; } -#endif - diff --git a/renderer/Platform/Window_Wayland_Shell/src/Window_Wayland_Shell.cpp b/src/renderer/internal/Platform/Wayland/WlShell/Window_Wayland_Shell.cpp similarity index 84% rename from renderer/Platform/Window_Wayland_Shell/src/Window_Wayland_Shell.cpp rename to src/renderer/internal/Platform/Wayland/WlShell/Window_Wayland_Shell.cpp index 92efdecde..d590ba0c5 100644 --- a/renderer/Platform/Window_Wayland_Shell/src/Window_Wayland_Shell.cpp +++ b/src/renderer/internal/Platform/Wayland/WlShell/Window_Wayland_Shell.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Wayland_Shell/Window_Wayland_Shell.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Wayland/WlShell/Window_Wayland_Shell.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { Window_Wayland_Shell::Window_Wayland_Shell(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler, @@ -73,24 +73,19 @@ namespace ramses_internal } } - void Window_Wayland_Shell::configureCallback(void* userData, wl_shell_surface* surface, uint32_t edges, int32_t width, int32_t height) + void Window_Wayland_Shell::configureCallback(void* userData, [[maybe_unused]] wl_shell_surface* surface, [[maybe_unused]] uint32_t edges, int32_t width, int32_t height) { - UNUSED(surface); - UNUSED(edges); - Window_Wayland_Shell* window = static_cast(userData); + auto* window = static_cast(userData); window->m_eventHandler.onResize(width, height); } - void Window_Wayland_Shell::pingCallback(void* userData, wl_shell_surface* surface, uint32_t serial) + void Window_Wayland_Shell::pingCallback([[maybe_unused]] void* userData, wl_shell_surface* surface, uint32_t serial) { - UNUSED(userData); wl_shell_surface_pong(surface, serial); } - void Window_Wayland_Shell::popupDoneCallback(void* userData, wl_shell_surface* surface) + void Window_Wayland_Shell::popupDoneCallback([[maybe_unused]] void* userData, [[maybe_unused]] wl_shell_surface* surface) { - UNUSED(userData); - UNUSED(surface); LOG_DEBUG(CONTEXT_RENDERER, "Window_Wayland::popupDoneCallback called"); } diff --git a/renderer/Platform/Window_Wayland_Shell/include/Window_Wayland_Shell/Window_Wayland_Shell.h b/src/renderer/internal/Platform/Wayland/WlShell/Window_Wayland_Shell.h similarity index 90% rename from renderer/Platform/Window_Wayland_Shell/include/Window_Wayland_Shell/Window_Wayland_Shell.h rename to src/renderer/internal/Platform/Wayland/WlShell/Window_Wayland_Shell.h index 41ec5813c..a41afc5d5 100644 --- a/renderer/Platform/Window_Wayland_Shell/include/Window_Wayland_Shell/Window_Wayland_Shell.h +++ b/src/renderer/internal/Platform/Wayland/WlShell/Window_Wayland_Shell.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_WAYLAND_SHELL_H -#define RAMSES_WINDOW_WAYLAND_SHELL_H +#pragma once -#include "Window_Wayland/Window_Wayland.h" +#include "internal/Platform/Wayland/Window_Wayland.h" -namespace ramses_internal +namespace ramses::internal { class Window_Wayland_Shell : public Window_Wayland { @@ -34,7 +33,7 @@ namespace ramses_internal const struct Shell_Surface_Listener : public wl_shell_surface_listener { - Shell_Surface_Listener() + Shell_Surface_Listener() : wl_shell_surface_listener() { ping = pingCallback; configure = configureCallback; @@ -43,5 +42,3 @@ namespace ramses_internal } m_shellSurfaceListener; }; } - -#endif diff --git a/renderer/Platform/Context_WGL/src/Context_WGL.cpp b/src/renderer/internal/Platform/Windows/Context_WGL.cpp similarity index 81% rename from renderer/Platform/Context_WGL/src/Context_WGL.cpp rename to src/renderer/internal/Platform/Windows/Context_WGL.cpp index 3206ede2a..1348788e2 100644 --- a/renderer/Platform/Context_WGL/src/Context_WGL.cpp +++ b/src/renderer/internal/Platform/Windows/Context_WGL.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Context_WGL/Context_WGL.h" -#include "Window_Windows/HiddenWindow.h" -#include "Window_Windows/Window_Windows.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Windows/Context_WGL.h" +#include "internal/Platform/Windows/HiddenWindow.h" +#include "internal/Platform/Windows/Window_Windows.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { - Context_WGL::Context_WGL(ERenderBufferType depthStencilBufferType, HDC displayHandle, WglExtensions wglExtensions, EDeviceType deviceType, uint32_t msaaSampleCount) + Context_WGL::Context_WGL(EDepthBufferType depthStencilBufferType, HDC displayHandle, WglExtensions wglExtensions, const Config& config, uint32_t msaaSampleCount) : m_displayHandle(displayHandle) , m_ext(wglExtensions) - , m_contextAttributes(createContextAttributes(deviceType)) + , m_contextAttributes(createContextAttributes(config)) , m_msaaSampleCount(msaaSampleCount) , m_depthStencilBufferType(depthStencilBufferType) { @@ -55,7 +55,7 @@ namespace ramses_internal if (0 == m_wglContextHandle) { uint32_t error = GetLastError(); - LOG_FATAL(CONTEXT_RENDERER, "wglCreateContextAttribsARB failed, returned context handle is 0. GetLastError returned error code " << error); + LOG_ERROR(CONTEXT_RENDERER, "wglCreateContextAttribsARB failed, returned context handle is 0. GetLastError returned error code " << error); return false; } @@ -132,21 +132,16 @@ namespace ramses_internal switch (m_depthStencilBufferType) { - case ERenderBufferType_DepthStencilBuffer: - depthBits = 24u; + case EDepthBufferType::DepthStencil: + depthBits = 24u; stencilBits = 8u; break; - case ERenderBufferType_DepthBuffer: - depthBits = 24u; + case EDepthBufferType::Depth: + depthBits = 24u; stencilBits = 0u; break; - case ERenderBufferType_InvalidBuffer: - depthBits = 0u; - stencilBits = 0u; + case EDepthBufferType::None: break; - case ERenderBufferType_ColorBuffer: - case ERenderBufferType_NUMBER_OF_ELEMENTS: - assert(false); } int iAttributes[] = @@ -246,38 +241,27 @@ namespace ramses_internal return true; } - std::vector Context_WGL::createContextAttributes(EDeviceType deviceType) + std::vector Context_WGL::createContextAttributes(const Config& config) { if (m_ext.isExtensionAvailable("create_context_profile")) { - switch (deviceType) + if (config.gles) { - case EDeviceType::GLES_3_0: return std::vector{ - WGL_CONTEXT_MAJOR_VERSION_ARB, 3, - WGL_CONTEXT_MINOR_VERSION_ARB, 0, + WGL_CONTEXT_MAJOR_VERSION_ARB, config.majorVersion, + WGL_CONTEXT_MINOR_VERSION_ARB, config.minorVersion, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_ES_PROFILE_BIT_EXT, 0, 0 }; - case EDeviceType::GL_4_2: - return std::vector{ - WGL_CONTEXT_MAJOR_VERSION_ARB, 4, - WGL_CONTEXT_MINOR_VERSION_ARB, 2, - //TODO: fix profile. Tracked in seprate subtask - //WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - 0 - }; - case EDeviceType::GL_4_5: - return std::vector{ - WGL_CONTEXT_MAJOR_VERSION_ARB, 4, - WGL_CONTEXT_MINOR_VERSION_ARB, 5, - WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - 0 - }; } - - assert(false && "Unhandled device type"); + return std::vector{ + WGL_CONTEXT_MAJOR_VERSION_ARB, config.majorVersion, + WGL_CONTEXT_MINOR_VERSION_ARB, config.minorVersion, + //TODO: fix profile. Tracked in seprate subtask + //WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0 + }; } else { diff --git a/renderer/Platform/Context_WGL/include/Context_WGL/Context_WGL.h b/src/renderer/internal/Platform/Windows/Context_WGL.h similarity index 65% rename from renderer/Platform/Context_WGL/include/Context_WGL/Context_WGL.h rename to src/renderer/internal/Platform/Windows/Context_WGL.h index 8afb17a33..8eeba2582 100644 --- a/renderer/Platform/Context_WGL/include/Context_WGL/Context_WGL.h +++ b/src/renderer/internal/Platform/Windows/Context_WGL.h @@ -6,22 +6,29 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTEXT_WGL_H -#define RAMSES_CONTEXT_WGL_H +#pragma once -#include "Platform_Base/Context_Base.h" -#include "Context_WGL/WglExtensions.h" +#include "internal/RendererLib/PlatformBase/Context_Base.h" +#include "internal/Platform/Windows/WglExtensions.h" -#include "PlatformAbstraction/MinimalWindowsH.h" -#include "SceneAPI/TextureEnums.h" -#include "RendererAPI/EDeviceType.h" +#include "ramses/renderer/Types.h" -namespace ramses_internal +#include "internal/PlatformAbstraction/MinimalWindowsH.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" + +namespace ramses::internal { class Context_WGL : public Context_Base { public: - Context_WGL(ERenderBufferType depthStencilBufferType, HDC displayHandle, WglExtensions procs, EDeviceType deviceType, uint32_t msaaSampleCount); + struct Config + { + uint8_t majorVersion = 4; + uint8_t minorVersion = 2; + bool gles = false; + }; + + Context_WGL(EDepthBufferType depthStencilBufferType, HDC displayHandle, WglExtensions procs, const Config& config, uint32_t msaaSampleCount); Context_WGL(Context_WGL& sharedContext, HDC displayHandle, WglExtensions procs, uint32_t msaaSampleCount); ~Context_WGL() override; @@ -38,7 +45,7 @@ namespace ramses_internal private: bool initCustomPixelFormat(); - std::vector createContextAttributes(EDeviceType deviceType); + std::vector createContextAttributes(const Config& config); HDC m_displayHandle; WglExtensions m_ext; @@ -48,9 +55,7 @@ namespace ramses_internal const HGLRC m_wglSharedContextHandle = 0; HGLRC m_wglContextHandle = 0; - const ERenderBufferType m_depthStencilBufferType; + const EDepthBufferType m_depthStencilBufferType; }; } - -#endif diff --git a/renderer/Platform/Window_Windows/src/HiddenWindow.cpp b/src/renderer/internal/Platform/Windows/HiddenWindow.cpp similarity index 96% rename from renderer/Platform/Window_Windows/src/HiddenWindow.cpp rename to src/renderer/internal/Platform/Windows/HiddenWindow.cpp index ef645c503..0af616565 100644 --- a/renderer/Platform/Window_Windows/src/HiddenWindow.cpp +++ b/src/renderer/internal/Platform/Windows/HiddenWindow.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "windows.h" -#include "Window_Windows/HiddenWindow.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/Platform/Windows/HiddenWindow.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { HiddenWindow::HiddenWindow() diff --git a/renderer/Platform/Window_Windows/include/Window_Windows/HiddenWindow.h b/src/renderer/internal/Platform/Windows/HiddenWindow.h similarity index 82% rename from renderer/Platform/Window_Windows/include/Window_Windows/HiddenWindow.h rename to src/renderer/internal/Platform/Windows/HiddenWindow.h index 6528c7486..74c486dcc 100644 --- a/renderer/Platform/Window_Windows/include/Window_Windows/HiddenWindow.h +++ b/src/renderer/internal/Platform/Windows/HiddenWindow.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_HIDDENWINDOW_H -#define RAMSES_HIDDENWINDOW_H +#pragma once -#include "PlatformAbstraction/MinimalWindowsH.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/PlatformAbstraction/MinimalWindowsH.h" -namespace ramses_internal +#include + +namespace ramses::internal { class HiddenWindow { @@ -30,5 +30,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/Platform/Platform_Windows_WGL/src/Platform_Windows_WGL.cpp b/src/renderer/internal/Platform/Windows/Platform_Windows_WGL.cpp similarity index 52% rename from renderer/Platform/Platform_Windows_WGL/src/Platform_Windows_WGL.cpp rename to src/renderer/internal/Platform/Windows/Platform_Windows_WGL.cpp index 17d6b3f8b..37b804145 100644 --- a/renderer/Platform/Platform_Windows_WGL/src/Platform_Windows_WGL.cpp +++ b/src/renderer/internal/Platform/Windows/Platform_Windows_WGL.cpp @@ -6,14 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Windows_WGL/Platform_Windows_WGL.h" -#include "Context_WGL/Context_WGL.h" -#include "Device_GL/Device_GL.h" -#include "Window_Windows/Window_Windows.h" -#include "Platform_Base/EmbeddedCompositor_Dummy.h" -#include "RendererLib/DisplayConfig.h" - -namespace ramses_internal +#include "internal/Platform/Windows/Platform_Windows_WGL.h" +#include "internal/Platform/Windows/Context_WGL.h" +#include "internal/Platform/OpenGL/Device_GL.h" +#include "internal/Platform/Windows/Window_Windows.h" +#include "internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + +namespace ramses::internal { Platform_Windows_WGL::Platform_Windows_WGL(const RendererConfig& rendererConfig) : Platform_Base(rendererConfig) @@ -35,31 +36,51 @@ namespace ramses_internal bool Platform_Windows_WGL::createContext(const DisplayConfig& displayConfig) { - assert(m_window); - Window_Windows* platformWindow = static_cast(m_window.get()); - - auto context = std::make_unique( - displayConfig.getDepthStencilBufferType(), - platformWindow->getNativeDisplayHandle(), - m_wglExtensions, - displayConfig.getDeviceType(), - platformWindow->getMSAASampleCount()); + if (m_contextConfig.has_value()) + { + m_context = createContextInternal(displayConfig, *m_contextConfig); + return m_context != nullptr; + } - if (context->init()) + std::vector minorVersions; + Context_WGL::Config contextConfig; + switch (displayConfig.getDeviceType()) { - m_context = std::move(context); - return true; + case EDeviceType::GLES_3_0: + contextConfig.majorVersion = 3; + minorVersions = {2, 1, 0}; + contextConfig.gles = true; + break; + case EDeviceType::GL_4_2: + contextConfig.majorVersion = 4; + minorVersions = {2}; + break; + case EDeviceType::GL_4_5: + contextConfig.majorVersion = 4; + minorVersions = {5}; + break; } - return false; + for (auto minor : minorVersions) + { + contextConfig.minorVersion = minor; + m_context = createContextInternal(displayConfig, contextConfig); + if (m_context) + { + m_contextConfig = contextConfig; + break; + } + LOG_ERROR_P(CONTEXT_RENDERER, "Windows_WGL::createContext failed: {}. Ramses will crash if any scene uses features of this version.", GetVersionString(contextConfig)); + } + return m_context != nullptr; } bool Platform_Windows_WGL::createContextUploading() { assert(m_window); assert(m_context); + assert(m_contextConfig.has_value()); Window_Windows* platformWindow = static_cast(m_window.get()); - auto context = std::make_unique( static_cast(*m_context), platformWindow->getNativeDisplayHandle(), @@ -78,6 +99,7 @@ namespace ramses_internal bool Platform_Windows_WGL::createDevice() { assert(m_context); + assert(m_contextConfig.has_value()); auto device = std::make_unique(*m_context, nullptr); if (device->init()) m_device = std::move(device); @@ -88,10 +110,35 @@ namespace ramses_internal bool Platform_Windows_WGL::createDeviceUploading() { assert(m_contextUploading); + assert(m_contextConfig.has_value()); auto device = std::make_unique(*m_contextUploading, nullptr); if (device->init()) m_deviceUploading = std::move(device); return m_deviceUploading.get() != nullptr; } + + std::unique_ptr Platform_Windows_WGL::createContextInternal(const DisplayConfig& displayConfig, const Context_WGL::Config& contextConfig) + { + assert(m_window); + Window_Windows* platformWindow = static_cast(m_window.get()); + auto context = std::make_unique( + displayConfig.getDepthStencilBufferType(), + platformWindow->getNativeDisplayHandle(), + m_wglExtensions, contextConfig, + platformWindow->getMSAASampleCount()); + + if (context->init()) + { + LOG_INFO_P(CONTEXT_RENDERER, "Windows_WGL::createContext: {}", GetVersionString(contextConfig)); + return context; + } + + return {}; + } + + std::string Platform_Windows_WGL::GetVersionString(const Context_WGL::Config& config) + { + return fmt::format("WGL{} {}.{}", config.gles ? " ES" : "", config.majorVersion, config.minorVersion); + } } diff --git a/src/renderer/internal/Platform/Windows/Platform_Windows_WGL.h b/src/renderer/internal/Platform/Windows/Platform_Windows_WGL.h new file mode 100644 index 000000000..1d7c8407c --- /dev/null +++ b/src/renderer/internal/Platform/Windows/Platform_Windows_WGL.h @@ -0,0 +1,38 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/Types.h" +#include "internal/RendererLib/PlatformBase/Platform_Base.h" +#include "internal/Platform/Windows/Context_WGL.h" +#include "internal/Platform/Windows/WglExtensions.h" +#include + +namespace ramses::internal +{ + class Platform_Windows_WGL final : public Platform_Base + { + public: + explicit Platform_Windows_WGL(const RendererConfig& rendererConfig); + + private: + virtual bool createWindow(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler) override; + virtual bool createContext(const DisplayConfig& displayConfig) override; + virtual bool createContextUploading() override; + virtual bool createDevice() override; + virtual bool createDeviceUploading() override; + + std::unique_ptr createContextInternal(const DisplayConfig& displayConfig, const Context_WGL::Config& contextConfig); + + static std::string GetVersionString(const Context_WGL::Config& config); + + WglExtensions m_wglExtensions; + std::optional m_contextConfig; + }; +} diff --git a/renderer/Platform/Context_WGL/src/WglExtensions.cpp b/src/renderer/internal/Platform/Windows/WglExtensions.cpp similarity index 95% rename from renderer/Platform/Context_WGL/src/WglExtensions.cpp rename to src/renderer/internal/Platform/Windows/WglExtensions.cpp index adba64084..1391003f5 100644 --- a/renderer/Platform/Context_WGL/src/WglExtensions.cpp +++ b/src/renderer/internal/Platform/Windows/WglExtensions.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Context_WGL/WglExtensions.h" -#include "Window_Windows/HiddenWindow.h" -#include "Platform_Base/Context_Base.h" -#include "Utils/LogMacros.h" +#include "internal/Platform/Windows/WglExtensions.h" +#include "internal/Platform/Windows/HiddenWindow.h" +#include "internal/RendererLib/PlatformBase/Context_Base.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { Procs::Procs() : wglChoosePixelFormatARB(0) diff --git a/renderer/Platform/Context_WGL/include/Context_WGL/WglExtensions.h b/src/renderer/internal/Platform/Windows/WglExtensions.h similarity index 85% rename from renderer/Platform/Context_WGL/include/Context_WGL/WglExtensions.h rename to src/renderer/internal/Platform/Windows/WglExtensions.h index 40f7f5b97..029f64136 100644 --- a/renderer/Platform/Context_WGL/include/Context_WGL/WglExtensions.h +++ b/src/renderer/internal/Platform/Windows/WglExtensions.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WGLEXTENSIONS_H -#define RAMSES_WGLEXTENSIONS_H +#pragma once -#include "RendererAPI/Types.h" -#include "Collections/HashSet.h" +#include "internal/RendererLib/Types.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" -#include "PlatformAbstraction/MinimalWindowsH.h" +#include "internal/PlatformAbstraction/MinimalWindowsH.h" #include "GL/GL.h" #include "GL/wgl.h" #include -namespace ramses_internal +namespace ramses::internal { struct Procs { @@ -46,5 +45,3 @@ namespace ramses_internal bool m_loaded; }; } - -#endif diff --git a/renderer/Platform/Window_Windows/src/Window_Windows.cpp b/src/renderer/internal/Platform/Windows/Window_Windows.cpp similarity index 89% rename from renderer/Platform/Window_Windows/src/Window_Windows.cpp rename to src/renderer/internal/Platform/Windows/Window_Windows.cpp index 9ec35b3c5..ca258e992 100644 --- a/renderer/Platform/Window_Windows/src/Window_Windows.cpp +++ b/src/renderer/internal/Platform/Windows/Window_Windows.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Windows/Window_Windows.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/EKeyModifier.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Collections/Guid.h" +#include "internal/Platform/Windows/Window_Windows.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" #include "fmt/format.h" #include -namespace ramses_internal +namespace ramses::internal { EKeyCode Window_Windows::convertVirtualKeyCodeIntoRamsesKeyCode(WPARAM virtualKeyCode, LPARAM lParam) { @@ -194,26 +194,17 @@ namespace ramses_internal } else { - if( m_borderless ) + m_windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + if (m_resizable) { - m_windowStyle = WS_POPUP ; - m_windowEXStyle = WS_EX_TOOLWINDOW; + m_windowStyle |= WS_OVERLAPPEDWINDOW; } else { - m_windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - - if (m_resizable) - { - m_windowStyle |= WS_OVERLAPPEDWINDOW; - } - else - { - m_windowStyle |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; - } - m_windowEXStyle = 0; + m_windowStyle |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; } - + m_windowEXStyle = 0; } RECT windowRect; @@ -383,31 +374,31 @@ namespace ramses_internal return; } - EKeyModifier keyModifier = EKeyModifier_NoModifier; + EKeyModifier keyModifier = EKeyModifier::NoModifier; switch (wParam) { case VK_SHIFT: - keyModifier = EKeyModifier_Shift; + keyModifier = EKeyModifier::Shift; break; case VK_CONTROL: - keyModifier = EKeyModifier_Ctrl; + keyModifier = EKeyModifier::Ctrl; break; case VK_MENU: - keyModifier = EKeyModifier_Alt; + keyModifier = EKeyModifier::Alt; break; } if (keyPressed) { - m_keyModifiers |= keyModifier; + m_keyModifiers.setFlag(keyModifier, true); } else if (keyReleased) { - m_keyModifiers &= ~keyModifier; + m_keyModifiers.setFlag(keyModifier, false); } const EKeyCode keyCode = convertVirtualKeyCodeIntoRamsesKeyCode(wParam, lParam); - m_eventHandler.onKeyEvent(keyPressed ? EKeyEventType_Pressed : EKeyEventType_Released, m_keyModifiers, keyCode); + m_eventHandler.onKeyEvent(keyPressed ? EKeyEvent::Pressed : EKeyEvent::Released, m_keyModifiers, keyCode); } LRESULT WINAPI Window_Windows::WindowProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -427,7 +418,7 @@ namespace ramses_internal break; window->m_isMouseTracked = false; - window->handleMouseEvent(EMouseEventType_WindowLeave, window->m_mousePosX, window->m_mousePosY); + window->handleMouseEvent(EMouseEvent::WindowLeave, window->m_mousePosX, window->m_mousePosY); break; case WM_MOUSEMOVE: if (NULL == window) @@ -436,12 +427,12 @@ namespace ramses_internal if (!window->m_isMouseTracked) { window->m_isMouseTracked = TrackMouse(hWnd); - window->handleMouseEvent(EMouseEventType_WindowEnter, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::WindowEnter, pt.x, pt.y); } window->m_mousePosX = pt.x; window->m_mousePosY = pt.y; - window->handleMouseEvent(EMouseEventType_Move, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::Move, pt.x, pt.y); break; case WM_LBUTTONDOWN: @@ -449,7 +440,7 @@ namespace ramses_internal if (NULL != window) { window->m_bLButtonDown = true; - window->handleMouseEvent(EMouseEventType_LeftButtonDown, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::LeftButtonDown, pt.x, pt.y); } } break; @@ -458,7 +449,7 @@ namespace ramses_internal if (NULL != window) { window->m_bLButtonDown = false; - window->handleMouseEvent(EMouseEventType_LeftButtonUp, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::LeftButtonUp, pt.x, pt.y); } } break; @@ -467,7 +458,7 @@ namespace ramses_internal if (NULL != window) { window->m_bRButtonDown = true; - window->handleMouseEvent(EMouseEventType_RightButtonDown, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::RightButtonDown, pt.x, pt.y); } } break; @@ -476,7 +467,7 @@ namespace ramses_internal if (NULL != window) { window->m_bRButtonDown = false; - window->handleMouseEvent(EMouseEventType_RightButtonUp, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::RightButtonUp, pt.x, pt.y); } } break; @@ -485,7 +476,7 @@ namespace ramses_internal if (NULL != window) { window->m_bMButtonDown = true; - window->handleMouseEvent(EMouseEventType_MiddleButtonDown, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::MiddleButtonDown, pt.x, pt.y); } } break; @@ -494,7 +485,7 @@ namespace ramses_internal if (NULL != window) { window->m_bMButtonDown = false; - window->handleMouseEvent(EMouseEventType_MiddleButtonUp, pt.x, pt.y); + window->handleMouseEvent(EMouseEvent::MiddleButtonUp, pt.x, pt.y); } } break; @@ -504,7 +495,7 @@ namespace ramses_internal { ScreenToClient(hWnd, &pt); short totalDelta = GET_WHEEL_DELTA_WPARAM(wParam); - EMouseEventType event = (totalDelta > 0) ? EMouseEventType_WheelUp : EMouseEventType_WheelDown; + EMouseEvent event = (totalDelta > 0) ? EMouseEvent::WheelUp : EMouseEvent::WheelDown; totalDelta = static_cast(abs(totalDelta)); totalDelta -= totalDelta % WHEEL_DELTA; // just in case.. @@ -576,7 +567,7 @@ namespace ramses_internal return lRet; } - void Window_Windows::handleMouseEvent(EMouseEventType type, int32_t posX, int32_t posY) + void Window_Windows::handleMouseEvent(EMouseEvent type, int32_t posX, int32_t posY) { if (m_bLButtonDown || m_bMButtonDown || m_bRButtonDown) { diff --git a/renderer/Platform/Window_Windows/include/Window_Windows/Window_Windows.h b/src/renderer/internal/Platform/Windows/Window_Windows.h similarity index 89% rename from renderer/Platform/Window_Windows/include/Window_Windows/Window_Windows.h rename to src/renderer/internal/Platform/Windows/Window_Windows.h index 662681fd4..5ab60ecf2 100644 --- a/renderer/Platform/Window_Windows/include/Window_Windows/Window_Windows.h +++ b/src/renderer/internal/Platform/Windows/Window_Windows.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_WINDOWS_H -#define RAMSES_WINDOW_WINDOWS_H +#pragma once #include #undef min // revert windows breaking stuff #undef max -#include "Platform_Base/Window_Base.h" -#include "RendererAPI/IWindowEventHandler.h" +#include "internal/RendererLib/PlatformBase/Window_Base.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" #include -namespace ramses_internal +namespace ramses::internal { class Window_Windows : public Window_Base { @@ -51,7 +50,7 @@ namespace ramses_internal WNDCLASSA m_windowClass; DWORD m_windowStyle; DWORD m_windowEXStyle; - uint32_t m_keyModifiers; + KeyModifiers m_keyModifiers; bool m_bLButtonDown; bool m_bRButtonDown; @@ -71,7 +70,7 @@ namespace ramses_internal void handleSysCommand(WPARAM wParam); void handleKeyEvent(uint32_t windowsMsg, WPARAM wParam, LPARAM lParam); - void handleMouseEvent(EMouseEventType type, int32_t posX, int32_t posY); + void handleMouseEvent(EMouseEvent type, int32_t posX, int32_t posY); void handleWindowCloseEvent(); void handleWindowMoveEvent(int32_t posX, int32_t posY); @@ -81,5 +80,3 @@ namespace ramses_internal static void PrintError(); }; } - -#endif diff --git a/renderer/Platform/Platform_X11_EGL/src/Platform_X11_EGL.cpp b/src/renderer/internal/Platform/X11/Platform_X11_EGL.cpp similarity index 84% rename from renderer/Platform/Platform_X11_EGL/src/Platform_X11_EGL.cpp rename to src/renderer/internal/Platform/X11/Platform_X11_EGL.cpp index 92950da4f..744adb082 100644 --- a/renderer/Platform/Platform_X11_EGL/src/Platform_X11_EGL.cpp +++ b/src/renderer/internal/Platform/X11/Platform_X11_EGL.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_X11_EGL/Platform_X11_EGL.h" -#include "RendererLib/RendererConfig.h" -#include "Platform_Base/EmbeddedCompositor_Dummy.h" +#include "internal/Platform/X11/Platform_X11_EGL.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h" -namespace ramses_internal +namespace ramses::internal { Platform_X11_EGL::Platform_X11_EGL(const RendererConfig& rendererConfig) : Platform_EGL(rendererConfig) diff --git a/renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL/Platform_X11_EGL.h b/src/renderer/internal/Platform/X11/Platform_X11_EGL.h similarity index 82% rename from renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL/Platform_X11_EGL.h rename to src/renderer/internal/Platform/X11/Platform_X11_EGL.h index 48b148f89..b7954ec62 100644 --- a/renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL/Platform_X11_EGL.h +++ b/src/renderer/internal/Platform/X11/Platform_X11_EGL.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_X11_EGL_H -#define RAMSES_PLATFORM_X11_EGL_H +#pragma once -#include "Platform_EGL/Platform_EGL.h" -#include "Platform_X11_EGL/Window_X11.h" +#include "internal/Platform/EGL/Platform_EGL.h" +#include "internal/Platform/X11/Window_X11.h" -namespace ramses_internal +namespace ramses::internal { class Platform_X11_EGL : public Platform_EGL { @@ -24,5 +23,3 @@ namespace ramses_internal [[nodiscard]] uint32_t getSwapInterval() const override; }; } - -#endif diff --git a/renderer/Platform/Platform_X11_EGL/src/Window_X11.cpp b/src/renderer/internal/Platform/X11/Window_X11.cpp similarity index 84% rename from renderer/Platform/Platform_X11_EGL/src/Window_X11.cpp rename to src/renderer/internal/Platform/X11/Window_X11.cpp index e8aa7dca2..a63ddf85b 100644 --- a/renderer/Platform/Platform_X11_EGL/src/Window_X11.cpp +++ b/src/renderer/internal/Platform/X11/Window_X11.cpp @@ -6,15 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_X11_EGL/Window_X11.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/EKeyModifier.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Warnings.h" +#include "internal/Platform/X11/Window_X11.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" #include -namespace ramses_internal +namespace ramses::internal { EKeyCode Window_X11::convertKeySymbolIntoRamsesKeyCode(uint32_t virtualKeyCode) { @@ -189,7 +187,7 @@ namespace ramses_internal } else { - if (m_X11WindowData.window) + if (m_X11WindowData.window != 0u) { XDestroyWindow(m_X11WindowData.display, m_X11WindowData.window); XSync(m_X11WindowData.display, static_cast(true)); @@ -255,8 +253,8 @@ namespace ramses_internal // Get the root window parameters PUSH_DISABLE_C_STYLE_CAST_WARNING - rootWindow = RootWindow(m_X11WindowData.display, m_X11WindowData.screen); - colorDepth = DefaultDepth(m_X11WindowData.display, m_X11WindowData.screen); + rootWindow = RootWindow(m_X11WindowData.display, m_X11WindowData.screen); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) + colorDepth = DefaultDepth(m_X11WindowData.display, m_X11WindowData.screen); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) POP_DISABLE_C_STYLE_CAST_WARNING // update window size in global config, if required @@ -281,7 +279,7 @@ namespace ramses_internal x11_event.xclient.message_type = x11_state_atom; x11_event.xclient.format = 32; x11_event.xclient.data.l[0] = 1; - x11_event.xclient.data.l[1] = x11_fs_atom; + x11_event.xclient.data.l[1] = x11_fs_atom; // NOLINT(cppcoreguidelines-narrowing-conversions) x11_event.xclient.data.l[2] = 0; XSendEvent(m_X11WindowData.display, @@ -292,11 +290,7 @@ namespace ramses_internal } // Try to find a visual which is matching the needed parameters - if (!XMatchVisualInfo(m_X11WindowData.display, - m_X11WindowData.screen, - colorDepth, - TrueColor, - &m_X11WindowData.visual)) + if (XMatchVisualInfo(m_X11WindowData.display, m_X11WindowData.screen, colorDepth, TrueColor, &m_X11WindowData.visual) == 0) { LOG_ERROR(CONTEXT_RENDERER, "Error: Unable to acquire visual\n"); return false; @@ -356,37 +350,15 @@ namespace ramses_internal { // NOLINTNEXTLINE(hicpp-signed-bitwise) sizeHints->flags |= PMinSize | PMaxSize; - sizeHints->min_width = m_width; - sizeHints->max_width = m_width; - sizeHints->min_height = m_height; - sizeHints->max_height = m_height; + sizeHints->min_width = static_cast(m_width); + sizeHints->max_width = static_cast(m_width); + sizeHints->min_height = static_cast(m_height); + sizeHints->max_height = static_cast(m_height); } XSetWMNormalHints(m_X11WindowData.display, m_X11WindowData.window, sizeHints); XFree(sizeHints); sizeHints = nullptr; - // disable window decorations - if( m_borderless ) - { - struct MotifHints - { - unsigned long flags; - unsigned long functions; - unsigned long decorations; - long inputMode; - unsigned long status; - }; - - Atom motifHintsProperty = XInternAtom(m_X11WindowData.display, "_MOTIF_WM_HINTS", 0); - struct MotifHints hints; - hints.flags = (1L << 1); // 2 = change window decorations - hints.decorations = 0; // false, ask the window manager to not decorate this window (NB. "ask" not "tell" - since the WM might not handle this atom) - XChangeProperty(m_X11WindowData.display, - m_X11WindowData.window, - motifHintsProperty, motifHintsProperty, 32, - PropModeReplace, reinterpret_cast(&hints), 5); - } - // map the window XMapWindow(m_X11WindowData.display, m_X11WindowData.window); XFlush(m_X11WindowData.display); @@ -422,10 +394,10 @@ namespace ramses_internal void Window_X11::handleEvents() { LOG_TRACE(CONTEXT_RENDERER, "Updating X11 window"); - while (XPending(m_X11WindowData.display)) + while (XPending(m_X11WindowData.display) != 0) { XEvent event; - std::array text; + std::array text{}; XNextEvent(m_X11WindowData.display, &event); if (m_X11WindowData.window != event.xany.window) @@ -436,10 +408,10 @@ namespace ramses_internal switch (event.type) { case EnterNotify: - m_eventHandler.onMouseEvent(EMouseEventType_WindowEnter, event.xcrossing.x, event.xcrossing.y); + m_eventHandler.onMouseEvent(EMouseEvent::WindowEnter, event.xcrossing.x, event.xcrossing.y); break; case LeaveNotify: - m_eventHandler.onMouseEvent(EMouseEventType_WindowLeave, event.xcrossing.x, event.xcrossing.y); + m_eventHandler.onMouseEvent(EMouseEvent::WindowLeave, event.xcrossing.x, event.xcrossing.y); break; case ConfigureNotify: { @@ -458,35 +430,35 @@ namespace ramses_internal } break; case MotionNotify: - m_eventHandler.onMouseEvent(EMouseEventType_Move, event.xmotion.x, event.xmotion.y); + m_eventHandler.onMouseEvent(EMouseEvent::Move, event.xmotion.x, event.xmotion.y); break; case ButtonPress: { - EMouseEventType eventType = EMouseEventType_Invalid; + EMouseEvent eventType = EMouseEvent::Invalid; switch (event.xbutton.button) { case Button1: - eventType = EMouseEventType_LeftButtonDown; + eventType = EMouseEvent::LeftButtonDown; m_bLButtonDown = true; break; case Button3: - eventType = EMouseEventType_RightButtonDown; + eventType = EMouseEvent::RightButtonDown; m_bRButtonDown = true; break; case Button2: - eventType = EMouseEventType_MiddleButtonDown; + eventType = EMouseEvent::MiddleButtonDown; m_bMButtonDown = true; break; case Button4: // scroll up - eventType = EMouseEventType_WheelUp; + eventType = EMouseEvent::WheelUp; break; case Button5: // scroll down - eventType = EMouseEventType_WheelDown; + eventType = EMouseEvent::WheelDown; break; default: break; } - if (EMouseEventType_Invalid != eventType) + if (EMouseEvent::Invalid != eventType) { m_eventHandler.onMouseEvent(eventType, event.xbutton.x, event.xbutton.y); } @@ -494,25 +466,25 @@ namespace ramses_internal break; case ButtonRelease: { - EMouseEventType eventType = EMouseEventType_Invalid; + EMouseEvent eventType = EMouseEvent::Invalid; switch (event.xbutton.button) { case Button1: - eventType = EMouseEventType_LeftButtonUp; + eventType = EMouseEvent::LeftButtonUp; m_bLButtonDown = false; break; case Button3: - eventType = EMouseEventType_RightButtonUp; + eventType = EMouseEvent::RightButtonUp; m_bRButtonDown = false; break; case Button2: - eventType = EMouseEventType_MiddleButtonUp; + eventType = EMouseEvent::MiddleButtonUp; m_bMButtonDown = false; break; default: break; } - if (EMouseEventType_Invalid != eventType) + if (EMouseEvent::Invalid != eventType) { m_eventHandler.onMouseEvent(eventType, event.xbutton.x, event.xbutton.y); } @@ -531,15 +503,15 @@ namespace ramses_internal { case 50: //XK_Shift_L: case 62: //XK_Shift_R: - m_keyModifiers |= EKeyModifier_Shift; + m_keyModifiers.setFlag(EKeyModifier::Shift, true); break; case 37: //XK_Control_L: case 105: //XK_Control_R: - m_keyModifiers |= EKeyModifier_Ctrl; + m_keyModifiers.setFlag(EKeyModifier::Ctrl, true); break; case 64: //XK_Alt_L: case 108: //XK_Alt_R: - m_keyModifiers |= EKeyModifier_Alt; + m_keyModifiers.setFlag(EKeyModifier::Alt, true); break; } @@ -547,7 +519,7 @@ namespace ramses_internal if (XLookupString(&event.xkey, text.data(), text.size(), &keySym, nullptr) != -1) { const EKeyCode keyCode = convertKeySymbolIntoRamsesKeyCode(keySym); - m_eventHandler.onKeyEvent(EKeyEventType_Pressed, m_keyModifiers, keyCode); + m_eventHandler.onKeyEvent(EKeyEvent::Pressed, m_keyModifiers, keyCode); } } break; @@ -555,7 +527,7 @@ namespace ramses_internal { // Even when a key is hold the x server sends KeyPress and KeyRelease events // We skip these obsolete KeyRelease events by checking the following KeyPress event - if (XPending(m_X11WindowData.display)) + if (XPending(m_X11WindowData.display) != 0) { XEvent nextEvent; XPeekEvent(m_X11WindowData.display, &nextEvent); @@ -572,15 +544,15 @@ namespace ramses_internal { case 50: //XK_Shift_L: case 62: //XK_Shift_R: - m_keyModifiers &= ~EKeyModifier_Shift; + m_keyModifiers .setFlag(EKeyModifier::Shift, false); break; case 37: //XK_Control_L: case 105: //XK_Control_R: - m_keyModifiers &= ~EKeyModifier_Ctrl; + m_keyModifiers.setFlag(EKeyModifier::Ctrl, false); break; case 64: //XK_Alt_L: case 108: //XK_Alt_R: - m_keyModifiers &= ~EKeyModifier_Alt; + m_keyModifiers.setFlag(EKeyModifier::Alt, false); break; default: break; @@ -590,7 +562,7 @@ namespace ramses_internal if (XLookupString(&event.xkey, text.data(), text.size(), &keySym, nullptr) != -1) { const EKeyCode keyCode = convertKeySymbolIntoRamsesKeyCode(keySym); - m_eventHandler.onKeyEvent(EKeyEventType_Released, m_keyModifiers, keyCode); + m_eventHandler.onKeyEvent(EKeyEvent::Released, m_keyModifiers, keyCode); } break; } diff --git a/renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL/Window_X11.h b/src/renderer/internal/Platform/X11/Window_X11.h similarity index 78% rename from renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL/Window_X11.h rename to src/renderer/internal/Platform/X11/Window_X11.h index f0432c3c1..f0f0b5584 100644 --- a/renderer/Platform/Platform_X11_EGL/include/Platform_X11_EGL/Window_X11.h +++ b/src/renderer/internal/Platform/X11/Window_X11.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_X11_H -#define RAMSES_WINDOW_X11_H +#pragma once -#include "Platform_Base/Window_Base.h" -#include "RendererAPI/IWindowEventHandler.h" +#include "internal/RendererLib/PlatformBase/Window_Base.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" // This dependency is needed due to the broken window abstraction of EGL // EGL acts as if it does not care about the window type (X11, wayland etc), @@ -21,26 +20,20 @@ #include "X11/Xlib.h" #include "X11/Xutil.h" #undef Status +#undef Bool #undef None -namespace ramses_internal +namespace ramses::internal { struct X11WindowData { - X11WindowData() - : window(0) - , display(nullptr) - , screen(0) - , delWindow(0) - { - } - ::Window window; - ::Display* display; - long screen; - XVisualInfo visual; - Colormap colormap; - Atom delWindow; + ::Window window{0}; + ::Display* display{nullptr}; + int screen{0}; + XVisualInfo visual{}; + Colormap colormap{}; + Atom delWindow{0}; }; class Window_X11 : public Window_Base @@ -70,7 +63,7 @@ namespace ramses_internal X11WindowData m_X11WindowData; - uint32_t m_keyModifiers; + KeyModifiers m_keyModifiers; bool m_bLButtonDown; bool m_bRButtonDown; @@ -78,5 +71,3 @@ namespace ramses_internal X11WindowHandle m_userProvidedWindowHandle; }; } - -#endif diff --git a/src/renderer/internal/Platform/iOS/Platform_iOS_EGL.h b/src/renderer/internal/Platform/iOS/Platform_iOS_EGL.h new file mode 100644 index 000000000..59ab8f039 --- /dev/null +++ b/src/renderer/internal/Platform/iOS/Platform_iOS_EGL.h @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Platform/iOS/Window_iOS.h" +#include "internal/Platform/EGL/Platform_EGL.h" + +namespace ramses::internal +{ + class Platform_iOS_EGL : public Platform_EGL + { + public: + explicit Platform_iOS_EGL(const RendererConfig& rendererConfig); + + protected: + virtual bool createWindow(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler) override; + virtual uint32_t getSwapInterval() const override; + }; +} diff --git a/src/renderer/internal/Platform/iOS/Platform_iOS_EGL.mm b/src/renderer/internal/Platform/iOS/Platform_iOS_EGL.mm new file mode 100644 index 000000000..870c1ead4 --- /dev/null +++ b/src/renderer/internal/Platform/iOS/Platform_iOS_EGL.mm @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/Platform/iOS/Platform_iOS_EGL.h" + +namespace ramses::internal +{ + Platform_iOS_EGL::Platform_iOS_EGL(const RendererConfig& rendererConfig) + : Platform_EGL(rendererConfig) + { + } + + bool Platform_iOS_EGL::createWindow(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler) + { + auto window = std::make_unique(displayConfig, windowEventHandler, 0u); + if (window->init()) + { + m_window = std::move(window); + return true; + } + + return false; + } + + uint32_t Platform_iOS_EGL::getSwapInterval() const + { + return 1u; + } +} diff --git a/src/renderer/internal/Platform/iOS/Window_iOS.h b/src/renderer/internal/Platform/iOS/Window_iOS.h new file mode 100644 index 000000000..d79478eb2 --- /dev/null +++ b/src/renderer/internal/Platform/iOS/Window_iOS.h @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/RendererLib/PlatformBase/Window_Base.h" + +namespace ramses::internal +{ + class Window_iOS : public Window_Base + { + public: + Window_iOS(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler, uint32_t id); + ~Window_iOS() override; + + bool init() override; + + void handleEvents() override; + + int getNativeDisplayHandle() const; + void* getNativeWindowHandle() const; + + bool hasTitle() const override + { + return false; + } + + bool setFullscreen(bool fullscreen) override; + bool setExternallyOwnedWindowSize(uint32_t width, uint32_t height) override; + + private: + void* m_metalLayer; + }; +} diff --git a/src/renderer/internal/Platform/iOS/Window_iOS.mm b/src/renderer/internal/Platform/iOS/Window_iOS.mm new file mode 100644 index 000000000..bfdb0fd08 --- /dev/null +++ b/src/renderer/internal/Platform/iOS/Window_iOS.mm @@ -0,0 +1,62 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/Platform/iOS/Window_iOS.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Warnings.h" + +#include "QuartzCore/CAMetalLayer.h" +#include "EGL/egl.h" + +namespace ramses::internal +{ + Window_iOS::Window_iOS(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler, UInt32 id) + : Window_Base(displayConfig, windowEventHandler, id) + , m_metalLayer(static_cast(displayConfig.getIOSNativeWindow().getValue())) + { + LOG_INFO(CONTEXT_RENDERER, "Window_iOS::Window_iOS"); + } + + Window_iOS::~Window_iOS() + { + LOG_INFO(CONTEXT_RENDERER, "Window_iOS::~Window_iOS"); + } + + bool Window_iOS::init() + { + return true; + } + + int Window_iOS::getNativeDisplayHandle() const + { + return EGL_DEFAULT_DISPLAY; + } + + void* Window_iOS::getNativeWindowHandle() const + { + return m_metalLayer; + } + + bool Window_iOS::setFullscreen(bool fullscreen) + { + (void)fullscreen; + return true; + } + + bool Window_iOS::setExternallyOwnedWindowSize(uint32_t width, uint32_t height) + { + m_width = width; + m_height = height; + return true; + } + + void Window_iOS::handleEvents() + { + } +} diff --git a/renderer/RendererLib/RendererLib/src/AsyncEffectUploader.cpp b/src/renderer/internal/RendererLib/AsyncEffectUploader.cpp similarity index 90% rename from renderer/RendererLib/RendererLib/src/AsyncEffectUploader.cpp rename to src/renderer/internal/RendererLib/AsyncEffectUploader.cpp index 1ddf5c629..83093c16c 100644 --- a/renderer/RendererLib/RendererLib/src/AsyncEffectUploader.cpp +++ b/src/renderer/internal/RendererLib/AsyncEffectUploader.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/AsyncEffectUploader.h" -#include "Platform_Base/Context_Base.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IResourceUploadRenderBackend.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IPlatform.h" -#include "Resource/EffectResource.h" -#include "Watchdog/IThreadAliveNotifier.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/AsyncEffectUploader.h" +#include "internal/RendererLib/PlatformBase/Context_Base.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IResourceUploadRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IPlatform.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { AsyncEffectUploader::AsyncEffectUploader(IPlatform& platform, IRenderBackend& renderBackend, IThreadAliveNotifier& notifier, int logPrefixID) : m_platform(platform) @@ -75,9 +75,10 @@ namespace ramses_internal { std::unique_lock guard(m_mutex); do + { m_notifier.notifyAlive(m_aliveIdentifier); - while (!m_sleepConditionVar.wait_for(guard, m_notifier.calculateTimeout(), - [&]() { return !m_effectsToUpload.empty() || !m_effectsUploadedCache.empty() || isCancelRequested(); })); + } while (!m_sleepConditionVar.wait_for( + guard, m_notifier.calculateTimeout(), [&]() { return !m_effectsToUpload.empty() || !m_effectsUploadedCache.empty() || isCancelRequested(); })); m_effectsUploaded.insert(m_effectsUploaded.end(), std::make_move_iterator(m_effectsUploadedCache.begin()), std::make_move_iterator(m_effectsUploadedCache.end())); m_effectsUploadedCache.clear(); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/AsyncEffectUploader.h b/src/renderer/internal/RendererLib/AsyncEffectUploader.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/AsyncEffectUploader.h rename to src/renderer/internal/RendererLib/AsyncEffectUploader.h index cdf87c40b..cb1169db7 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/AsyncEffectUploader.h +++ b/src/renderer/internal/RendererLib/AsyncEffectUploader.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ASYNCEFFECTUPLOADER_H -#define RAMSES_ASYNCEFFECTUPLOADER_H +#pragma once -#include "Platform_Base/GpuResource.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { class IPlatform; class IRenderBackend; @@ -64,5 +63,3 @@ namespace ramses_internal const int m_logPrefixID; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/BufferLinks.cpp b/src/renderer/internal/RendererLib/BufferLinks.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/src/BufferLinks.cpp rename to src/renderer/internal/RendererLib/BufferLinks.cpp index 6e73d5b4c..3e91ddb81 100644 --- a/renderer/RendererLib/RendererLib/src/BufferLinks.cpp +++ b/src/renderer/internal/RendererLib/BufferLinks.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/BufferLinks.h" +#include "internal/RendererLib/BufferLinks.h" -namespace ramses_internal +namespace ramses::internal { template void BufferLinks::addLink(BUFFERHANDLE providerBuffer, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/BufferLinks.h b/src/renderer/internal/RendererLib/BufferLinks.h similarity index 91% rename from renderer/RendererLib/RendererLib/include/RendererLib/BufferLinks.h rename to src/renderer/internal/RendererLib/BufferLinks.h index 756c48792..83b88811c 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/BufferLinks.h +++ b/src/renderer/internal/RendererLib/BufferLinks.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BUFFERLINKS_H -#define RAMSES_BUFFERLINKS_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "SceneAPI/Handles.h" -#include "RendererAPI/Types.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/RendererLib/Types.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { template struct BufferLink @@ -57,5 +56,3 @@ namespace ramses_internal using ExternalBufferLinks = BufferLinks; using ExternalBufferLinkVector = BufferLinkVector; } - -#endif diff --git a/src/renderer/internal/RendererLib/CMakeLists.txt b/src/renderer/internal/RendererLib/CMakeLists.txt new file mode 100644 index 000000000..4614a5a5e --- /dev/null +++ b/src/renderer/internal/RendererLib/CMakeLists.txt @@ -0,0 +1,47 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +file(GLOB_RECURSE + RAMSES_RENDERER_LIB_FILES + *.h + *.cpp) + +createModule( + NAME ramses-renderer-lib + TYPE STATIC_LIBRARY + ENABLE_INSTALL OFF + + INCLUDE_PATHS ${PROJECT_SOURCE_DIR}/src/renderer + SRC_FILES ${RAMSES_RENDERER_LIB_FILES} + DEPENDENCIES ramses-api + ramses-framework +) + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS) + target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) + target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_X11) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_ANDROID) + target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_ANDROID) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_IOS) + target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_IOS) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) + target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) + target_compile_definitions(ramses-renderer-lib PUBLIC ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) +endif() diff --git a/renderer/RendererLib/RendererLib/src/ConstantLogger.cpp b/src/renderer/internal/RendererLib/ConstantLogger.cpp similarity index 93% rename from renderer/RendererLib/RendererLib/src/ConstantLogger.cpp rename to src/renderer/internal/RendererLib/ConstantLogger.cpp index f2d591190..03b5e7d57 100644 --- a/renderer/RendererLib/RendererLib/src/ConstantLogger.cpp +++ b/src/renderer/internal/RendererLib/ConstantLogger.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/ConstantLogger.h" +#include "internal/RendererLib/ConstantLogger.h" -namespace ramses_internal +namespace ramses::internal { template void ConstantLogger::LogValue(DataFieldHandle field, const DATATYPE& value, RendererLogContext& context) @@ -46,6 +46,12 @@ namespace ramses_internal context << "[" << field << "] Load int(" << value << ")" << RendererLogContext::NewLine; } + template <> + void ConstantLogger::LogValue(DataFieldHandle field, const bool& value, RendererLogContext& context) + { + context << "[" << field << "] Load bool(" << value << ")" << RendererLogContext::NewLine; + } + template <> void ConstantLogger::LogValue(DataFieldHandle field, const glm::ivec2& value, RendererLogContext& context) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ConstantLogger.h b/src/renderer/internal/RendererLib/ConstantLogger.h similarity index 91% rename from renderer/RendererLib/RendererLib/include/RendererLib/ConstantLogger.h rename to src/renderer/internal/RendererLib/ConstantLogger.h index c3d8678aa..6309c48c9 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ConstantLogger.h +++ b/src/renderer/internal/RendererLib/ConstantLogger.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONSTANTLOGGER_H -#define RAMSES_CONSTANTLOGGER_H +#pragma once #include "RendererLogContext.h" -#include "SceneAPI/Handles.h" -#include "DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +namespace ramses::internal { class RendererLogContext; @@ -41,6 +40,7 @@ namespace ramses_internal template <> void ConstantLogger::LogValue (DataFieldHandle field, const glm::vec2 & value, RendererLogContext& context); template <> void ConstantLogger::LogValue (DataFieldHandle field, const glm::vec3 & value, RendererLogContext& context); template <> void ConstantLogger::LogValue (DataFieldHandle field, const glm::vec4 & value, RendererLogContext& context); + template <> void ConstantLogger::LogValue (DataFieldHandle field, const bool & value, RendererLogContext& context); template <> void ConstantLogger::LogValue (DataFieldHandle field, const int32_t & value, RendererLogContext& context); template <> void ConstantLogger::LogValue (DataFieldHandle field, const glm::ivec2 & value, RendererLogContext& context); template <> void ConstantLogger::LogValue (DataFieldHandle field, const glm::ivec3 & value, RendererLogContext& context); @@ -49,5 +49,3 @@ namespace ramses_internal template <> void ConstantLogger::LogValue(DataFieldHandle field, const glm::mat3& value, RendererLogContext& context); template <> void ConstantLogger::LogValue(DataFieldHandle field, const glm::mat4& value, RendererLogContext& context); } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DataLinkUtils.h b/src/renderer/internal/RendererLib/DataLinkUtils.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RendererLib/DataLinkUtils.h rename to src/renderer/internal/RendererLib/DataLinkUtils.h index bf3527fb9..8a085d41f 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DataLinkUtils.h +++ b/src/renderer/internal/RendererLib/DataLinkUtils.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATALINKUTILS_H -#define RAMSES_DATALINKUTILS_H +#pragma once -#include "RendererLib/RendererScenes.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/DataSlot.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" -namespace ramses_internal +namespace ramses::internal { class DataLinkUtils { @@ -57,5 +56,3 @@ namespace ramses_internal return scene.getDataLayout(dataLayout).getField(DataFieldHandle(0u)).dataType; } } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DataReferenceLinkCachedScene.cpp b/src/renderer/internal/RendererLib/DataReferenceLinkCachedScene.cpp similarity index 92% rename from renderer/RendererLib/RendererLib/src/DataReferenceLinkCachedScene.cpp rename to src/renderer/internal/RendererLib/DataReferenceLinkCachedScene.cpp index ec0f54da4..dee97c7e8 100644 --- a/renderer/RendererLib/RendererLib/src/DataReferenceLinkCachedScene.cpp +++ b/src/renderer/internal/RendererLib/DataReferenceLinkCachedScene.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DataReferenceLinkCachedScene.h" -#include "RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/DataReferenceLinkCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" -namespace ramses_internal +namespace ramses::internal { DataReferenceLinkCachedScene::DataReferenceLinkCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo) : TransformationLinkCachedScene(sceneLinksManager, sceneInfo) @@ -20,7 +20,7 @@ namespace ramses_internal { const DataSlotHandle actualHandle = TransformationLinkCachedScene::allocateDataSlot(dataSlot, handle); - if (dataSlot.type == EDataSlotType_DataConsumer) + if (dataSlot.type == EDataSlotType::DataConsumer) { assert(dataSlot.attachedDataReference.isValid()); m_fallbackValues.allocate(dataSlot.attachedDataReference); @@ -64,6 +64,12 @@ namespace ramses_internal updateFallbackValue(containerHandle, data); } + void DataReferenceLinkCachedScene::setDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) + { + TransformationLinkCachedScene::setDataBooleanArray(containerHandle, field, elementCount, data); + updateFallbackValue(containerHandle, data); + } + void DataReferenceLinkCachedScene::setDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) { TransformationLinkCachedScene::setDataIntegerArray(containerHandle, field, elementCount, data); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DataReferenceLinkCachedScene.h b/src/renderer/internal/RendererLib/DataReferenceLinkCachedScene.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/DataReferenceLinkCachedScene.h rename to src/renderer/internal/RendererLib/DataReferenceLinkCachedScene.h index 2117db84c..260a62dfd 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DataReferenceLinkCachedScene.h +++ b/src/renderer/internal/RendererLib/DataReferenceLinkCachedScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAREFERENCELINKCACHEDSCENE_H -#define RAMSES_DATAREFERENCELINKCACHEDSCENE_H +#pragma once -#include "RendererLib/TransformationLinkCachedScene.h" -#include "SceneUtils/DataInstanceHelper.h" +#include "internal/RendererLib/TransformationLinkCachedScene.h" +#include "internal/SceneGraph/SceneUtils/DataInstanceHelper.h" -namespace ramses_internal +namespace ramses::internal { class DataReferenceLinkCachedScene : public TransformationLinkCachedScene { @@ -20,7 +19,7 @@ namespace ramses_internal explicit DataReferenceLinkCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo = SceneInfo()); // From IScene - DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle) override; void releaseDataSlot(DataSlotHandle handle) override; // Listen on data changes in order to handle fallback values @@ -28,6 +27,7 @@ namespace ramses_internal void setDataVector2fArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) override; void setDataVector3fArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) override; void setDataVector4fArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) override; + void setDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) override; void setDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) override; void setDataVector2iArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) override; void setDataVector3iArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) override; @@ -47,5 +47,3 @@ namespace ramses_internal FallbackValuePool m_fallbackValues; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DataReferenceLinkManager.cpp b/src/renderer/internal/RendererLib/DataReferenceLinkManager.cpp similarity index 84% rename from renderer/RendererLib/RendererLib/src/DataReferenceLinkManager.cpp rename to src/renderer/internal/RendererLib/DataReferenceLinkManager.cpp index fc06f1a02..b5999dd2b 100644 --- a/renderer/RendererLib/RendererLib/src/DataReferenceLinkManager.cpp +++ b/src/renderer/internal/RendererLib/DataReferenceLinkManager.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DataReferenceLinkManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DataLinkUtils.h" -#include "SceneUtils/DataInstanceHelper.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/DataReferenceLinkManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DataLinkUtils.h" +#include "internal/SceneGraph/SceneUtils/DataInstanceHelper.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { DataReferenceLinkManager::DataReferenceLinkManager(RendererScenes& rendererScenes) : LinkManagerBase(rendererScenes) @@ -34,8 +34,8 @@ namespace ramses_internal bool DataReferenceLinkManager::createDataLink(SceneId providerSceneId, DataSlotHandle providerSlotHandle, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) { - assert(EDataSlotType_DataProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); - assert(EDataSlotType_DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::DataProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); + assert(EDataSlotType::DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); const EDataType providerDataType = DataLinkUtils::GetSlotDataReferenceType(providerSceneId, providerSlotHandle, m_scenes); const EDataType consumerDataType = DataLinkUtils::GetSlotDataReferenceType(consumerSceneId, consumerSlotHandle, m_scenes); @@ -50,7 +50,7 @@ namespace ramses_internal bool DataReferenceLinkManager::removeDataLink(SceneId consumerSceneId, DataSlotHandle consumerSlotHandle, SceneId* providerSceneIdOut) { - assert(EDataSlotType_DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); if (providerSceneIdOut && getSceneLinks().hasLinkedProvider(consumerSceneId, consumerSlotHandle)) *providerSceneIdOut = getSceneLinks().getLinkedProvider(consumerSceneId, consumerSlotHandle).providerSceneId; diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DataReferenceLinkManager.h b/src/renderer/internal/RendererLib/DataReferenceLinkManager.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/DataReferenceLinkManager.h rename to src/renderer/internal/RendererLib/DataReferenceLinkManager.h index 6e4abdcb4..33151ef12 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DataReferenceLinkManager.h +++ b/src/renderer/internal/RendererLib/DataReferenceLinkManager.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATAREFERENCELINKMANAGER_H -#define RAMSES_DATAREFERENCELINKMANAGER_H +#pragma once -#include "RendererLib/LinkManagerBase.h" +#include "internal/RendererLib/LinkManagerBase.h" -namespace ramses_internal +namespace ramses::internal { class RendererScenes; class DataReferenceLinkCachedScene; @@ -33,5 +32,3 @@ namespace ramses_internal using LinkManagerBase::getSceneLinks; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplayBundle.cpp b/src/renderer/internal/RendererLib/DisplayBundle.cpp similarity index 94% rename from renderer/RendererLib/RendererLib/src/DisplayBundle.cpp rename to src/renderer/internal/RendererLib/DisplayBundle.cpp index d2fa79a78..bc0f5e557 100644 --- a/renderer/RendererLib/RendererLib/src/DisplayBundle.cpp +++ b/src/renderer/internal/RendererLib/DisplayBundle.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayBundle.h" -#include "RendererLib/RenderBackend.h" -#include "RendererAPI/IPlatform.h" -#include "RendererAPI/IDisplayController.h" -#include "RendererAPI/IDevice.h" -#include "Watchdog/IThreadAliveNotifier.h" - -namespace ramses_internal +#include "internal/RendererLib/DisplayBundle.h" +#include "internal/RendererLib/RenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IPlatform.h" +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" + +namespace ramses::internal { DisplayBundle::DisplayBundle( DisplayHandle display, diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayBundle.h b/src/renderer/internal/RendererLib/DisplayBundle.h similarity index 83% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplayBundle.h rename to src/renderer/internal/RendererLib/DisplayBundle.h index b9ac432e3..32bc17854 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayBundle.h +++ b/src/renderer/internal/RendererLib/DisplayBundle.h @@ -6,25 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYBUNDLE_H -#define RAMSES_DISPLAYBUNDLE_H - -#include "RendererLib/Renderer.h" -#include "RendererLib/RendererCommandExecutor.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/RendererSceneControlLogic.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererLib/SceneReferenceOwnership.h" -#include "RendererLib/SceneReferenceLogic.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererAPI/ELoopMode.h" -#include "RendererEventCollector.h" - -namespace ramses_internal +#pragma once + +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/RendererCommandExecutor.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/RendererSceneUpdater.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/SceneReferenceOwnership.h" +#include "internal/RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/Enums/ELoopMode.h" +#include "internal/RendererLib/RendererEventCollector.h" + +namespace ramses::internal { class IPlatform; class IRendererSceneEventSender; @@ -113,5 +112,3 @@ namespace ramses_internal size_t m_loopsWithinMeasurePeriod{ 0u }; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplayConfig.cpp b/src/renderer/internal/RendererLib/DisplayConfig.cpp similarity index 92% rename from renderer/RendererLib/RendererLib/src/DisplayConfig.cpp rename to src/renderer/internal/RendererLib/DisplayConfig.cpp index f6cbf8d73..2e037e269 100644 --- a/renderer/RendererLib/RendererLib/src/DisplayConfig.cpp +++ b/src/renderer/internal/RendererLib/DisplayConfig.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayConfig.h" +#include "internal/RendererLib/DisplayConfig.h" #include -namespace ramses_internal +namespace ramses::internal { EDeviceType DisplayConfig::getDeviceType() const { @@ -33,7 +33,7 @@ namespace ramses_internal void DisplayConfig::setAntialiasingSampleCount(uint32_t samples) { - assert(ramses_internal::contains_c({ 1u, 2u, 4u, 8u }, samples)); + assert(contains_c({ 1u, 2u, 4u, 8u }, samples)); m_antiAliasingSamples = samples; } @@ -67,6 +67,16 @@ namespace ramses_internal m_androidNativeWindowPtr = nativeWindowPtr; } + IOSNativeWindowPtr DisplayConfig::getIOSNativeWindow() const + { + return m_iOSNativeWindowPtr; + } + + void DisplayConfig::setIOSNativeWindow(IOSNativeWindowPtr nativeWindowPtr) + { + m_iOSNativeWindowPtr = nativeWindowPtr; + } + bool DisplayConfig::getStartVisibleIvi() const { return m_startVisibleIvi; @@ -87,16 +97,6 @@ namespace ramses_internal m_fullscreen = state; } - bool DisplayConfig::getBorderlessState() const - { - return m_borderless; - } - - void DisplayConfig::setBorderlessState(bool state) - { - m_borderless = state; - } - uint32_t DisplayConfig::getAntialiasingSampleCount() const { return m_antiAliasingSamples; @@ -144,16 +144,6 @@ namespace ramses_internal m_windowPositionY = posy; } - void DisplayConfig::setKeepEffectsUploaded(bool enabled) - { - m_keepEffectsUploaded = enabled; - } - - bool DisplayConfig::getKeepEffectsUploaded() const - { - return m_keepEffectsUploaded; - } - bool DisplayConfig::isResizable() const { return m_resizable; @@ -184,12 +174,12 @@ namespace ramses_internal return m_clearColor; } - void DisplayConfig::setDepthStencilBufferType(ERenderBufferType depthStencilBufferType) + void DisplayConfig::setDepthStencilBufferType(EDepthBufferType depthStencilBufferType) { m_depthStencilBufferType = depthStencilBufferType; } - ERenderBufferType DisplayConfig::getDepthStencilBufferType() const + EDepthBufferType DisplayConfig::getDepthStencilBufferType() const { return m_depthStencilBufferType; } @@ -332,7 +322,6 @@ namespace ramses_internal m_deviceType == other.m_deviceType && m_windowType == other.m_windowType && m_fullscreen == other.m_fullscreen && - m_borderless == other.m_borderless && m_antiAliasingSamples == other.m_antiAliasingSamples && m_desiredWindowWidth == other.m_desiredWindowWidth && m_desiredWindowHeight == other.m_desiredWindowHeight && diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayConfig.h b/src/renderer/internal/RendererLib/DisplayConfig.h similarity index 79% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplayConfig.h rename to src/renderer/internal/RendererLib/DisplayConfig.h index 86818c084..d8556d091 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayConfig.h +++ b/src/renderer/internal/RendererLib/DisplayConfig.h @@ -6,26 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYCONFIG_H -#define RAMSES_DISPLAYCONFIG_H +#pragma once -#include "RendererAPI/Types.h" -#include "RendererAPI/EDeviceType.h" -#include "RendererAPI/EWindowType.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/TextureEnums.h" -#include "DataTypesImpl.h" +#include "ramses/renderer/Types.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "impl/DataTypesImpl.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { class DisplayConfig { public: - DisplayConfig() {} + DisplayConfig() = default; [[nodiscard]] EDeviceType getDeviceType() const; void setDeviceType(EDeviceType deviceType); @@ -36,10 +34,6 @@ namespace ramses_internal [[nodiscard]] bool getFullscreenState() const; void setFullscreenState(bool state); - - [[nodiscard]] bool getBorderlessState() const ; - void setBorderlessState(bool state); - [[nodiscard]] uint32_t getAntialiasingSampleCount() const; void setAntialiasingSampleCount(uint32_t samples); @@ -73,12 +67,12 @@ namespace ramses_internal [[nodiscard]] AndroidNativeWindowPtr getAndroidNativeWindow() const; void setAndroidNativeWindow(AndroidNativeWindowPtr nativeWindowPtr); + [[nodiscard]] IOSNativeWindowPtr getIOSNativeWindow() const; + void setIOSNativeWindow(IOSNativeWindowPtr nativeWindowPtr); + [[nodiscard]] bool getStartVisibleIvi() const; void setStartVisibleIvi(bool startVisible); - [[nodiscard]] bool getKeepEffectsUploaded() const; - void setKeepEffectsUploaded(bool enable); - [[nodiscard]] bool isResizable() const; void setResizable(bool resizable); @@ -88,8 +82,8 @@ namespace ramses_internal void setClearColor(const glm::vec4& clearColor); [[nodiscard]] const glm::vec4& getClearColor() const; - void setDepthStencilBufferType(ERenderBufferType depthStencilBufferType); - [[nodiscard]] ERenderBufferType getDepthStencilBufferType() const; + void setDepthStencilBufferType(EDepthBufferType depthStencilBufferType); + [[nodiscard]] EDepthBufferType getDepthStencilBufferType() const; void setAsyncEffectUploadEnabled(bool enabled); [[nodiscard]] bool isAsyncEffectUploadEnabled() const; @@ -125,11 +119,30 @@ namespace ramses_internal private: EDeviceType m_deviceType = EDeviceType::GLES_3_0; - static_assert(SupportedWindowTypes.size() > 0, "No window types supported for build configuration"); + constexpr static std::array SupportedWindowTypes{ +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS) + EWindowType::Windows, +#endif +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_X11) + EWindowType::X11, +#endif +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) + EWindowType::Wayland_IVI, +#endif +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) + EWindowType::Wayland_Shell, +#endif +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_ANDROID) + EWindowType::Android, +#endif +#if defined(ramses_sdk_ENABLE_WINDOW_TYPE_IOS) + EWindowType::iOS +#endif + }; + static_assert(!SupportedWindowTypes.empty(), "No window types supported for build configuration"); EWindowType m_windowType = *SupportedWindowTypes.begin(); bool m_fullscreen = false; - bool m_borderless = false; bool m_resizable = false; uint32_t m_desiredWindowWidth = 1280; @@ -142,15 +155,15 @@ namespace ramses_internal WindowsWindowHandle m_windowsWindowHandle; X11WindowHandle m_x11WindowHandle; AndroidNativeWindowPtr m_androidNativeWindowPtr; + IOSNativeWindowPtr m_iOSNativeWindowPtr; bool m_startVisibleIvi = false; std::string m_waylandDisplay; uint32_t m_antiAliasingSamples = 1; - bool m_keepEffectsUploaded = true; uint64_t m_gpuMemoryCacheSize = 0u; glm::vec4 m_clearColor{ 0.f, 0.f, 0.f, 1.0f }; - ERenderBufferType m_depthStencilBufferType = ERenderBufferType_DepthStencilBuffer; + EDepthBufferType m_depthStencilBufferType = EDepthBufferType::DepthStencil; bool m_asyncEffectUploadEnabled = true; std::string m_waylandSocketEmbedded; @@ -164,5 +177,3 @@ namespace ramses_internal uint32_t m_resourceUploadBatchSize = 10u; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplayController.cpp b/src/renderer/internal/RendererLib/DisplayController.cpp similarity index 80% rename from renderer/RendererLib/RendererLib/src/DisplayController.cpp rename to src/renderer/internal/RendererLib/DisplayController.cpp index ffbb06001..b020b3c62 100644 --- a/renderer/RendererLib/RendererLib/src/DisplayController.cpp +++ b/src/renderer/internal/RendererLib/DisplayController.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayController.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IWindow.h" -#include "RendererAPI/IContext.h" -#include "RendererLib/RendererConfig.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/LoggingDevice.h" -#include "RendererLib/RendererCachedScene.h" -#include "Math3d/CameraMatrixHelper.h" -#include "RenderExecutor.h" - -namespace ramses_internal +#include "internal/RendererLib/DisplayController.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/LoggingDevice.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "internal/RendererLib/RenderExecutor.h" + +namespace ramses::internal { DisplayController::DisplayController(IRenderBackend& renderer, uint32_t /*samples*/) : m_renderBackend(renderer) @@ -59,18 +59,18 @@ namespace ramses_internal return executor.executeScene(scene); } - void DisplayController::clearBuffer(DeviceResourceHandle buffer, uint32_t clearFlags, const glm::vec4& clearColor) + void DisplayController::clearBuffer(DeviceResourceHandle buffer, ClearFlags clearFlags, const glm::vec4& clearColor) { - if (clearFlags != EClearFlags_None) + if (clearFlags != EClearFlag::None) { m_device.activateRenderTarget(buffer); - if (clearFlags & EClearFlags_Color) + if (clearFlags.isSet(EClearFlag::Color)) { m_device.colorMask(true, true, true, true); m_device.clearColor(clearColor); } - if (clearFlags & EClearFlags_Depth) + if (clearFlags.isSet(EClearFlag::Depth)) m_device.depthWrite(EDepthWrite::Enabled); m_device.scissorTest(EScissorTest::Disabled, {}); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayController.h b/src/renderer/internal/RendererLib/DisplayController.h similarity index 81% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplayController.h rename to src/renderer/internal/RendererLib/DisplayController.h index ed4110ccb..06df50957 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayController.h +++ b/src/renderer/internal/RendererLib/DisplayController.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYCONTROLLER_H -#define RAMSES_DISPLAYCONTROLLER_H +#pragma once -#include "RendererAPI/IDisplayController.h" -#include "Math3d/CameraMatrixHelper.h" +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" #include "EmbeddedCompositingManager.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererConfig; class IRenderBackend; @@ -34,8 +33,8 @@ namespace ramses_internal [[nodiscard]] bool canRenderNewFrame() const override; void enableContext() override; void swapBuffers() override; - SceneRenderExecutionIterator renderScene(const RendererCachedScene& scene, RenderingContext& renderContext, const FrameTimer* frameTimer = nullptr) override; - void clearBuffer(DeviceResourceHandle buffer, uint32_t clearFlags, const glm::vec4& clearColor) override; + SceneRenderExecutionIterator renderScene(const RendererCachedScene& scene, RenderingContext& renderContext, const FrameTimer* frameTimer) override; + void clearBuffer(DeviceResourceHandle buffer, ClearFlags clearFlags, const glm::vec4& clearColor) override; [[nodiscard]] DeviceResourceHandle getDisplayBuffer() const final override; [[nodiscard]] IRenderBackend& getRenderBackend() const override; @@ -43,7 +42,7 @@ namespace ramses_internal [[nodiscard]] uint32_t getDisplayWidth() const override; [[nodiscard]] uint32_t getDisplayHeight() const override; - void readPixels(DeviceResourceHandle renderTargetHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, std::vector& dataOut) override; + void readPixels(DeviceResourceHandle renderTargetHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, std::vector& dataOut) override; void validateRenderingStatusHealthy() const override; @@ -56,5 +55,3 @@ namespace ramses_internal const uint32_t m_displayHeight; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplayDispatcher.cpp b/src/renderer/internal/RendererLib/DisplayDispatcher.cpp similarity index 94% rename from renderer/RendererLib/RendererLib/src/DisplayDispatcher.cpp rename to src/renderer/internal/RendererLib/DisplayDispatcher.cpp index 7d285cef3..b6bf57a60 100644 --- a/renderer/RendererLib/RendererLib/src/DisplayDispatcher.cpp +++ b/src/renderer/internal/RendererLib/DisplayDispatcher.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayDispatcher.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererLib/RendererCommandUtils.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/DisplayDispatcher.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RendererCommandUtils.h" +#include "internal/Core/Utils/ThreadLocalLog.h" -namespace ramses_internal +namespace ramses::internal { DisplayDispatcher::DisplayDispatcher( std::unique_ptr platformFactory, - const RendererConfig& config, + RendererConfig config, IRendererSceneEventSender& rendererSceneSender, IThreadAliveNotifier& notifier) : m_platformFactory(std::move(platformFactory)) - , m_rendererConfig{ config } + , m_rendererConfig{ std::move(config) } , m_rendererSceneSender{ rendererSceneSender } , m_notifier{ notifier } { @@ -75,7 +75,7 @@ namespace ramses_internal { const auto frameCounter = display.second.displayThread->getFrameCounter(); if (display.second.lastFrameCounter == frameCounter) - LOG_WARN_P(CONTEXT_RENDERER, "Display {} potentially stuck at trace ID {}", display.first, display.second.displayBundle->traceId()); + LOG_WARN_P(CONTEXT_RENDERER, "Display {} potentially stuck at trace ID {}", display.first, display.second.displayBundle->traceId().load()); display.second.lastFrameCounter = frameCounter; } } @@ -114,7 +114,8 @@ namespace ramses_internal if (displayBundle.displayThread) { displayBundle.displayThread->setLoopMode(m_loopMode); - const auto minFrameDuration = (m_minFrameDurationsPerDisplay.count(displayHandle) ? m_minFrameDurationsPerDisplay[displayHandle] : DefaultMinFrameDuration); + const auto minFrameDuration = + (m_minFrameDurationsPerDisplay.count(displayHandle) != 0u ? m_minFrameDurationsPerDisplay[displayHandle] : DefaultMinFrameDuration); displayBundle.displayThread->setMinFrameDuration(minFrameDuration); if (m_displayThreadsUpdating) displayBundle.displayThread->startUpdating(); @@ -244,9 +245,10 @@ namespace ramses_internal const bool isFirstDisplay = (m_displays.cbegin()->first == emittingDisplay); const auto owningDisplay = m_sceneDisplayTrackerForEvents.getSceneOwnership(sceneId); if (owningDisplay.isValid()) + { return owningDisplay == emittingDisplay; - else - return isFirstDisplay; + } + return isFirstDisplay; } void DisplayDispatcher::dispatchRendererEvents(RendererEventVector& events) @@ -308,10 +310,17 @@ namespace ramses_internal if (evt.state == RendererSceneState::Ready) m_sceneDisplayTrackerForEvents.setSceneOwnership(evt.sceneId, display.first); if (isSceneStateChangeEmittedFromOwningDisplay(evt.sceneId, display.first)) + { events.push_back(std::move(evt)); + } else - LOG_INFO_P(CONTEXT_RENDERER, "DisplayDispatcher::dispatchSceneControlEvents: filtering scene state change event from non-owner display {}, scene {} change state to {}.", - display.first, evt.sceneId, EnumToString(evt.state)); + { + LOG_INFO_P(CONTEXT_RENDERER, + "DisplayDispatcher::dispatchSceneControlEvents: filtering scene state change event from non-owner display {}, scene {} change state to {}.", + display.first, + evt.sceneId, + EnumToString(evt.state)); + } } else events.push_back(std::move(evt)); @@ -368,8 +377,10 @@ namespace ramses_internal std::lock_guard lock{ m_displaysAccessLock }; m_loopMode = loopMode; for (auto& display : m_displays) + { if (display.second.displayThread) display.second.displayThread->setLoopMode(loopMode); + } } void DisplayDispatcher::setMinFrameDuration(std::chrono::microseconds minFrameDuration, DisplayHandle display) @@ -410,7 +421,7 @@ namespace ramses_internal bool DisplayDispatcher::hasSystemCompositorController() const { assert(!m_threadedDisplays); - assert(m_displays.size() != 0); + assert(!m_displays.empty()); const IDisplayBundle& displayBundle = *m_displays.cbegin()->second.displayBundle; return displayBundle.hasSystemCompositorController(); } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayDispatcher.h b/src/renderer/internal/RendererLib/DisplayDispatcher.h similarity index 91% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplayDispatcher.h rename to src/renderer/internal/RendererLib/DisplayDispatcher.h index c1262683a..284a3c1d0 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayDispatcher.h +++ b/src/renderer/internal/RendererLib/DisplayDispatcher.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYDISPATCHER_H -#define RAMSES_DISPLAYDISPATCHER_H - -#include "RendererLib/DisplayBundle.h" -#include "RendererLib/RendererConfig.h" -#include "RendererLib/SceneDisplayTracker.h" -#include "RendererLib/DisplayThread.h" -#include "RendererAPI/ELoopMode.h" -#include "RendererAPI/IPlatformFactory.h" -#include "Watchdog/IThreadAliveNotifier.h" +#pragma once + +#include "internal/RendererLib/DisplayBundle.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/SceneDisplayTracker.h" +#include "internal/RendererLib/DisplayThread.h" +#include "internal/RendererLib/Enums/ELoopMode.h" +#include "internal/RendererLib/PlatformInterface/IPlatformFactory.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; class IRendererSceneEventSender; @@ -32,7 +31,7 @@ namespace ramses_internal public: DisplayDispatcher( std::unique_ptr platformFactory, - const RendererConfig& config, + RendererConfig config, IRendererSceneEventSender& rendererSceneSender, IThreadAliveNotifier& notifier); virtual ~DisplayDispatcher() = default; @@ -119,5 +118,3 @@ namespace ramses_internal int m_loopCounter = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplayEventHandler.cpp b/src/renderer/internal/RendererLib/DisplayEventHandler.cpp similarity index 79% rename from renderer/RendererLib/RendererLib/src/DisplayEventHandler.cpp rename to src/renderer/internal/RendererLib/DisplayEventHandler.cpp index 21bb1abf2..569690b2a 100644 --- a/renderer/RendererLib/RendererLib/src/DisplayEventHandler.cpp +++ b/src/renderer/internal/RendererLib/DisplayEventHandler.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayEventHandler.h" -#include "RendererAPI/IWindow.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "RendererLib/EKeyModifier.h" -#include "RendererEventCollector.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/DisplayEventHandler.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { DisplayEventHandler::DisplayEventHandler(DisplayHandle displayHandle, RendererEventCollector& eventCollector) : m_displayHandle(displayHandle) @@ -21,13 +21,11 @@ namespace ramses_internal { } - DisplayEventHandler::~DisplayEventHandler() - { - } + DisplayEventHandler::~DisplayEventHandler() = default; /* WindowEvent handlers */ - void DisplayEventHandler::onKeyEvent(EKeyEventType event, uint32_t modifiers, EKeyCode keyCode) + void DisplayEventHandler::onKeyEvent(EKeyEvent event, KeyModifiers modifiers, EKeyCode keyCode) { LOG_TRACE(CONTEXT_RENDERER, "DisplayController::onKeyEvent: [display: " << m_displayHandle.asMemoryHandle() << "; eventType: " << EnumToString(event) << "; modifiers: " << KeyModifierToString(modifiers) << "; key: " << EnumToString(keyCode) << "]"); @@ -40,7 +38,7 @@ namespace ramses_internal m_eventCollector.addWindowEvent(ERendererEventType::WindowKeyEvent, m_displayHandle, keyEvent); } - void DisplayEventHandler::onMouseEvent(EMouseEventType event, int32_t posX, int32_t posY) + void DisplayEventHandler::onMouseEvent(EMouseEvent event, int32_t posX, int32_t posY) { LOG_TRACE(CONTEXT_RENDERER, "DisplayController::onMouseEvent: [display: " << m_displayHandle.asMemoryHandle() << "; eventType: " << EnumToString(event) << "; posX: " << posX << "; posY: " << posY << "]"); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayEventHandler.h b/src/renderer/internal/RendererLib/DisplayEventHandler.h similarity index 76% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplayEventHandler.h rename to src/renderer/internal/RendererLib/DisplayEventHandler.h index 02d8529bc..ef4a6e68f 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayEventHandler.h +++ b/src/renderer/internal/RendererLib/DisplayEventHandler.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYEVENTHANDLER_H -#define RAMSES_DISPLAYEVENTHANDLER_H +#pragma once -#include "RendererAPI/Types.h" -#include "RendererAPI/IWindowEventHandler.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" -namespace ramses_internal +namespace ramses::internal { class RendererEventCollector; @@ -24,8 +23,8 @@ namespace ramses_internal ~DisplayEventHandler() override; /* Inherited from IWindowEventHandler */ - void onKeyEvent(EKeyEventType event, uint32_t modifiers, EKeyCode keyCode) override; - void onMouseEvent(EMouseEventType event, int32_t posX, int32_t posY) override; + void onKeyEvent(EKeyEvent event, KeyModifiers modifiers, EKeyCode keyCode) override; + void onMouseEvent(EMouseEvent event, int32_t posX, int32_t posY) override; void onClose() override; void onResize(uint32_t width, uint32_t height) override; void onWindowMove(int32_t posX, int32_t posY) override; @@ -35,5 +34,3 @@ namespace ramses_internal RendererEventCollector& m_eventCollector; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplaySetup.cpp b/src/renderer/internal/RendererLib/DisplaySetup.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/src/DisplaySetup.cpp rename to src/renderer/internal/RendererLib/DisplaySetup.cpp index cdab970d3..067eb1c80 100644 --- a/renderer/RendererLib/RendererLib/src/DisplaySetup.cpp +++ b/src/renderer/internal/RendererLib/DisplaySetup.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplaySetup.h" -#include "SceneAPI/RenderState.h" +#include "internal/RendererLib/DisplaySetup.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" -namespace ramses_internal +namespace ramses::internal { void DisplaySetup::registerDisplayBuffer(DeviceResourceHandle displayBuffer, const Viewport& viewport, const glm::vec4& clearColor, bool isOffscreenBuffer, bool isInterruptible) { assert(!isInterruptible || isOffscreenBuffer); assert(m_displayBuffers.find(displayBuffer) == m_displayBuffers.cend()); - DisplayBufferInfo bufferInfo{ isOffscreenBuffer, isInterruptible, viewport, EClearFlags_All, clearColor, {}, true }; + DisplayBufferInfo bufferInfo{ isOffscreenBuffer, isInterruptible, viewport, EClearFlag::All, clearColor, {}, true }; m_displayBuffers.emplace(displayBuffer, std::move(bufferInfo)); } @@ -96,7 +96,7 @@ namespace ramses_internal getDisplayBufferInternal(displayBuffer).needsRerender = true; } - void DisplaySetup::setClearFlags(DeviceResourceHandle displayBuffer, uint32_t clearFlags) + void DisplaySetup::setClearFlags(DeviceResourceHandle displayBuffer, ClearFlags clearFlags) { getDisplayBufferInternal(displayBuffer).clearFlags = clearFlags; } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplaySetup.h b/src/renderer/internal/RendererLib/DisplaySetup.h similarity index 79% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplaySetup.h rename to src/renderer/internal/RendererLib/DisplaySetup.h index a74dea9c1..84d270371 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplaySetup.h +++ b/src/renderer/internal/RendererLib/DisplaySetup.h @@ -6,35 +6,35 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYSETUP_H -#define RAMSES_DISPLAYSETUP_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/Viewport.h" -#include "Collections/Vector.h" -#include "DataTypesImpl.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/Viewport.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "impl/DataTypesImpl.h" #include -namespace ramses_internal +namespace ramses::internal { struct AssignedSceneInfo { SceneId sceneId; - int32_t globalSceneOrder; - bool shown; + int32_t globalSceneOrder{0}; + bool shown{false}; }; using AssignedScenes = std::vector; struct DisplayBufferInfo { - bool isOffscreenBuffer; - bool isInterruptible; - Viewport viewport; - uint32_t clearFlags; - glm::vec4 clearColor; + bool isOffscreenBuffer{false}; + bool isInterruptible{false}; + Viewport viewport; + ClearFlags clearFlags; + glm::vec4 clearColor; AssignedScenes scenes; - bool needsRerender; + bool needsRerender{false}; }; using DisplayBuffersMap = std::map; @@ -52,7 +52,7 @@ namespace ramses_internal void unassignScene(SceneId sceneId); [[nodiscard]] DeviceResourceHandle findDisplayBufferSceneIsAssignedTo(SceneId sceneId) const; void setSceneShown(SceneId sceneId, bool show); - void setClearFlags(DeviceResourceHandle displayBuffer, uint32_t clearFlags); + void setClearFlags(DeviceResourceHandle displayBuffer, ClearFlags clearFlags); void setClearColor(DeviceResourceHandle displayBuffer, const glm::vec4& clearColor); void setDisplayBufferSize(DeviceResourceHandle displayBuffer, uint32_t width, uint32_t height); @@ -71,5 +71,3 @@ namespace ramses_internal mutable DeviceHandleVector m_buffersToRender; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/DisplayThread.cpp b/src/renderer/internal/RendererLib/DisplayThread.cpp similarity index 94% rename from renderer/RendererLib/RendererLib/src/DisplayThread.cpp rename to src/renderer/internal/RendererLib/DisplayThread.cpp index 8b8fb117a..d8d52adf6 100644 --- a/renderer/RendererLib/RendererLib/src/DisplayThread.cpp +++ b/src/renderer/internal/RendererLib/DisplayThread.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/DisplayThread.h" -#include "RendererLib/DisplayBundle.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/DisplayThread.h" +#include "internal/RendererLib/DisplayBundle.h" +#include "internal/Core/Utils/ThreadLocalLog.h" -namespace ramses_internal +namespace ramses::internal { DisplayThread::DisplayThread(DisplayBundleShared displayBundle, DisplayHandle displayHandle, IThreadAliveNotifier& notifier) : m_displayHandle{ displayHandle } @@ -112,7 +112,7 @@ namespace ramses_internal m_display->traceId() = 10005; const auto currentLoopDuration = std::chrono::duration_cast(loopEndTime - loopStartTime); - lastLoopSleepTime = sleepToControlFramerate(currentLoopDuration, minimumFrameDuration); + lastLoopSleepTime = SleepToControlFramerate(currentLoopDuration, minimumFrameDuration); m_display->traceId() = 10006; } @@ -124,7 +124,7 @@ namespace ramses_internal m_display.destroy(); } - std::chrono::milliseconds DisplayThread::sleepToControlFramerate(std::chrono::microseconds loopDuration, std::chrono::microseconds minimumFrameDuration) + std::chrono::milliseconds DisplayThread::SleepToControlFramerate(std::chrono::microseconds loopDuration, std::chrono::microseconds minimumFrameDuration) { std::chrono::milliseconds sleepTime{ 0 }; if (loopDuration < minimumFrameDuration) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayThread.h b/src/renderer/internal/RendererLib/DisplayThread.h similarity index 88% rename from renderer/RendererLib/RendererLib/include/RendererLib/DisplayThread.h rename to src/renderer/internal/RendererLib/DisplayThread.h index 7d6d79aee..bb0978b20 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/DisplayThread.h +++ b/src/renderer/internal/RendererLib/DisplayThread.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYTHREAD_H -#define RAMSES_DISPLAYTHREAD_H +#pragma once -#include "RendererAPI/ELoopMode.h" -#include "RendererLib/DisplayBundle.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Watchdog/IThreadAliveNotifier.h" +#include "internal/RendererLib/Enums/ELoopMode.h" +#include "internal/RendererLib/DisplayBundle.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Watchdog/IThreadAliveNotifier.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { constexpr static std::chrono::microseconds DefaultMinFrameDuration{ 1000000 / 60 }; @@ -83,7 +82,7 @@ namespace ramses_internal private: void run() override; - std::chrono::milliseconds sleepToControlFramerate(std::chrono::microseconds loopDuration, std::chrono::microseconds minimumFrameDuration); + static std::chrono::milliseconds SleepToControlFramerate(std::chrono::microseconds loopDuration, std::chrono::microseconds minimumFrameDuration); const DisplayHandle m_displayHandle; DisplayBundleShared m_display; @@ -101,5 +100,3 @@ namespace ramses_internal std::atomic_uint32_t m_frameCounter{ 0u }; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/EmbeddedCompositingManager.cpp b/src/renderer/internal/RendererLib/EmbeddedCompositingManager.cpp similarity index 92% rename from renderer/RendererLib/RendererLib/src/EmbeddedCompositingManager.cpp rename to src/renderer/internal/RendererLib/EmbeddedCompositingManager.cpp index 4ae4ed495..38875e3aa 100644 --- a/renderer/RendererLib/RendererLib/src/EmbeddedCompositingManager.cpp +++ b/src/renderer/internal/RendererLib/EmbeddedCompositingManager.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/EmbeddedCompositingManager.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RendererAPI/ITextureUploadingAdapter.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/EmbeddedCompositingManager.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/RendererLib/PlatformInterface/ITextureUploadingAdapter.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { EmbeddedCompositingManager::EmbeddedCompositingManager(IDevice &device, IEmbeddedCompositor &embeddedCompositor, ITextureUploadingAdapter& textureUploadingAdapter) : m_device(device) @@ -26,7 +26,7 @@ namespace ramses_internal auto streamTextureInfoIt = m_streamTextureSourceInfoMap.find(source); if (m_streamTextureSourceInfoMap.end() == streamTextureInfoIt) { - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingManager::refStream: Creating new stream texture with source id: " << source); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingManager::refStream: Creating new stream texture with: " << source); createStreamTexture(source); streamTextureInfoIt = m_streamTextureSourceInfoMap.find(source); } @@ -47,7 +47,7 @@ namespace ramses_internal if (streamTextureSourceInfo->refs == 0) { - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingManager::unrefStream: Destroying no more referenced stream texture with source id: " << source); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingManager::unrefStream: Destroying no more referenced stream texture with " << source); destroyStreamTexture(source); } } @@ -131,7 +131,7 @@ namespace ramses_internal { // register a new texture (with dummy description and no data) to device and put entry in hashmap // actual data is uploaded later when source content available - const DeviceResourceHandle compositedTextureDeviceHandle = m_device.uploadStreamTexture2D(DeviceResourceHandle::Invalid(), 1u, 1u, ETextureFormat::RGBA8, nullptr, {ETextureChannelColor::Blue, ETextureChannelColor::Green, ETextureChannelColor::Red, ETextureChannelColor::Alpha}); + const DeviceResourceHandle compositedTextureDeviceHandle = m_device.uploadStreamTexture2D(DeviceResourceHandle::Invalid(), 1u, 1u, EPixelStorageFormat::RGBA8, nullptr, {ETextureChannelColor::Blue, ETextureChannelColor::Green, ETextureChannelColor::Red, ETextureChannelColor::Alpha}); StreamTextureSourceInfo streamTextureSourceInfo; streamTextureSourceInfo.compositedTextureHandle = compositedTextureDeviceHandle; diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/EmbeddedCompositingManager.h b/src/renderer/internal/RendererLib/EmbeddedCompositingManager.h similarity index 91% rename from renderer/RendererLib/RendererLib/include/RendererLib/EmbeddedCompositingManager.h rename to src/renderer/internal/RendererLib/EmbeddedCompositingManager.h index 18de15667..ac6c0ebce 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/EmbeddedCompositingManager.h +++ b/src/renderer/internal/RendererLib/EmbeddedCompositingManager.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITINGMANAGER_H -#define RAMSES_EMBEDDEDCOMPOSITINGMANAGER_H +#pragma once -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "Collections/HashMap.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" -namespace ramses_internal +namespace ramses::internal { class IDevice; class IEmbeddedCompositor; @@ -51,5 +50,3 @@ namespace ramses_internal ITextureUploadingAdapter& m_textureUploadingAdapter; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/EKeyCode.h b/src/renderer/internal/RendererLib/Enums/EKeyCode.h similarity index 54% rename from renderer/RendererLib/RendererLib/include/RendererLib/EKeyCode.h rename to src/renderer/internal/RendererLib/Enums/EKeyCode.h index f388fdc3c..b28309ce7 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/EKeyCode.h +++ b/src/renderer/internal/RendererLib/Enums/EKeyCode.h @@ -6,134 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EKEYCODE_H -#define RAMSES_EKEYCODE_H +#pragma once -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { - - enum EKeyCode - { - EKeyCode_Unknown = 0, - - EKeyCode_A, - EKeyCode_B, - EKeyCode_C, - EKeyCode_D, - EKeyCode_E, - EKeyCode_F, - EKeyCode_G, - EKeyCode_H, - EKeyCode_I, - EKeyCode_J, - EKeyCode_K, - EKeyCode_L, - EKeyCode_M, - EKeyCode_N, - EKeyCode_O, - EKeyCode_P, - EKeyCode_Q, - EKeyCode_R, - EKeyCode_S, - EKeyCode_T, - EKeyCode_U, - EKeyCode_V, - EKeyCode_W, - EKeyCode_X, - EKeyCode_Y, - EKeyCode_Z, - - EKeyCode_0, - EKeyCode_1, - EKeyCode_2, - EKeyCode_3, - EKeyCode_4, - EKeyCode_5, - EKeyCode_6, - EKeyCode_7, - EKeyCode_8, - EKeyCode_9, - - EKeyCode_NumLock, - EKeyCode_Numpad_Add, - EKeyCode_Numpad_Subtract, - EKeyCode_Numpad_Multiply, - EKeyCode_Numpad_Divide, - EKeyCode_Numpad_Enter, - EKeyCode_Numpad_Decimal, - EKeyCode_Numpad_0, - EKeyCode_Numpad_1, - EKeyCode_Numpad_2, - EKeyCode_Numpad_3, - EKeyCode_Numpad_4, - EKeyCode_Numpad_5, - EKeyCode_Numpad_6, - EKeyCode_Numpad_7, - EKeyCode_Numpad_8, - EKeyCode_Numpad_9, - - EKeyCode_Return, - EKeyCode_Escape, - EKeyCode_Backspace, - EKeyCode_Tab, - EKeyCode_Space, - EKeyCode_Menu, - EKeyCode_CapsLock, - EKeyCode_ShiftLeft, - EKeyCode_ShiftRight, - EKeyCode_AltLeft, - EKeyCode_AltRight, - EKeyCode_ControlLeft, - EKeyCode_ControlRight, - EKeyCode_WindowsLeft, - EKeyCode_WindowsRight, - - EKeyCode_F1, - EKeyCode_F2, - EKeyCode_F3, - EKeyCode_F4, - EKeyCode_F5, - EKeyCode_F6, - EKeyCode_F7, - EKeyCode_F8, - EKeyCode_F9, - EKeyCode_F10, - EKeyCode_F11, - EKeyCode_F12, - - EKeyCode_PrintScreen, - EKeyCode_ScrollLock, - EKeyCode_Pause, - - EKeyCode_Insert, - EKeyCode_Home, - EKeyCode_PageUp, - EKeyCode_Delete, - EKeyCode_End, - EKeyCode_PageDown, - - EKeyCode_Right, - EKeyCode_Left, - EKeyCode_Up, - EKeyCode_Down, - - EKeyCode_Minus, - EKeyCode_Equals, - EKeyCode_LeftBracket, - EKeyCode_RightBracket, - EKeyCode_Backslash, - EKeyCode_Semicolon, - EKeyCode_Comma, - EKeyCode_Period, - EKeyCode_Slash, - EKeyCode_Apostrophe, - EKeyCode_Grave, - EKeyCode_NumberSign, - - EKeyCode_NUMBER_OF_ELEMENTS - }; + using ramses::EKeyCode; const std::array KeyCodeNames = { @@ -254,8 +133,6 @@ namespace ramses_internal "EKeyCode_NumberSign" }; - ENUM_TO_STRING(EKeyCode, KeyCodeNames, EKeyCode_NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(ramses::EKeyCode, KeyCodeNames, ramses::EKeyCode_NumberSign); } - -#endif diff --git a/client/ramses-client/impl/SceneConfigImpl.cpp b/src/renderer/internal/RendererLib/Enums/EKeyEvent.h similarity index 60% rename from client/ramses-client/impl/SceneConfigImpl.cpp rename to src/renderer/internal/RendererLib/Enums/EKeyEvent.h index a505ed41b..c005d8655 100644 --- a/client/ramses-client/impl/SceneConfigImpl.cpp +++ b/src/renderer/internal/RendererLib/Enums/EKeyEvent.h @@ -6,18 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneConfigImpl.h" +#pragma once -namespace ramses +#include "internal/Core/Utils/LoggingUtils.h" +#include "ramses/renderer/Types.h" + +namespace ramses::internal { - status_t SceneConfigImpl::setPublicationMode(EScenePublicationMode publicationMode) - { - m_publicationMode = publicationMode; - return StatusOK; - } + using ramses::EKeyEvent; - EScenePublicationMode SceneConfigImpl::getPublicationMode() const + const std::array KeyEventNames = { - return m_publicationMode; - } + "Invalid", + "Pressed", + "Released" + }; + + ENUM_TO_STRING(EKeyEvent, KeyEventNames, EKeyEvent::Released); } + diff --git a/src/renderer/internal/RendererLib/Enums/EKeyModifier.h b/src/renderer/internal/RendererLib/Enums/EKeyModifier.h new file mode 100644 index 000000000..181a324b6 --- /dev/null +++ b/src/renderer/internal/RendererLib/Enums/EKeyModifier.h @@ -0,0 +1,57 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/Common/BitForgeMacro.h" +#include "ramses/renderer/Types.h" + +#include + +namespace ramses::internal +{ + using ramses::EKeyModifier; + using ramses::KeyModifiers; + + static inline std::string KeyModifierToString(KeyModifiers flags) + { + std::string s = "("; + if (flags == EKeyModifier::NoModifier) + { + s.append("EKeyModifier::NoModifier"); + } + else + { + if (flags.isSet(EKeyModifier::Ctrl)) + { + s.append("EKeyModifier::Ctrl | "); + } + if (flags.isSet(EKeyModifier::Shift)) + { + s.append("EKeyModifier::Shift | "); + } + if (flags.isSet(EKeyModifier::Alt)) + { + s.append("EKeyModifier::Alt | "); + } + if (flags.isSet(EKeyModifier::Function)) + { + s.append("EKeyModifier::Function | "); + } + if (flags.isSet(EKeyModifier::Numpad)) + { + s.append("EKeyModifier::Numpad | "); + } + } + s.append(")"); + + return s; + }; + +} + diff --git a/renderer/Platform/Platform_EGL/src/dummy.cpp b/src/renderer/internal/RendererLib/Enums/ELoopMode.h similarity index 75% rename from renderer/Platform/Platform_EGL/src/dummy.cpp rename to src/renderer/internal/RendererLib/Enums/ELoopMode.h index 34aec4474..6b21891f2 100644 --- a/renderer/Platform/Platform_EGL/src/dummy.cpp +++ b/src/renderer/internal/RendererLib/Enums/ELoopMode.h @@ -1,11 +1,16 @@ // ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG +// Copyright (C) 2017 BMW Car IT GmbH // ------------------------------------------------------------------------- // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -namespace ramses_internal +#pragma once + +#include "ramses/renderer/Types.h" + +namespace ramses::internal { + using ramses::ELoopMode; } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/EKeyEventType.h b/src/renderer/internal/RendererLib/Enums/EMouseEvent.h similarity index 50% rename from renderer/RendererLib/RendererLib/include/RendererLib/EKeyEventType.h rename to src/renderer/internal/RendererLib/Enums/EMouseEvent.h index 92f456a9b..d0d8f2971 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/EKeyEventType.h +++ b/src/renderer/internal/RendererLib/Enums/EMouseEvent.h @@ -6,30 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EKEYEVENTTYPE_H -#define RAMSES_EKEYEVENTTYPE_H +#pragma once -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "ramses/renderer/Types.h" -namespace ramses_internal +namespace ramses::internal { - enum EKeyEventType - { - EKeyEventType_Invalid = 0, - - EKeyEventType_Pressed, - EKeyEventType_Released, + using ramses::EMouseEvent; - EKeyEventType_NUMBER_OF_ELEMENTS - }; - - const std::array KeyEventTypeNames = + const std::array MouseEventNames = { - "EKeyEventType_Invalid", - "EKeyEventType_Pressed", - "EKeyEventType_Released" + "Invalid", + "LeftButtonDown", + "LeftButtonUp", + "RightButtonDown", + "RightButtonUp", + "MiddleButtonDown", + "MiddleButtonUp", + "WheelUp", + "WheelDown", + "Move", + "WindowEnter", + "WindowLeave" }; - ENUM_TO_STRING(EKeyEventType, KeyEventTypeNames, EKeyEventType_NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(EMouseEvent, MouseEventNames, EMouseEvent::WindowLeave); } -#endif + diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/EResourceStatus.h b/src/renderer/internal/RendererLib/Enums/EResourceStatus.h similarity index 77% rename from renderer/RendererLib/RendererLib/include/RendererLib/EResourceStatus.h rename to src/renderer/internal/RendererLib/Enums/EResourceStatus.h index 015c42719..8dc3625c2 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/EResourceStatus.h +++ b/src/renderer/internal/RendererLib/Enums/EResourceStatus.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ERESOURCESTATUS_H -#define RAMSES_ERESOURCESTATUS_H +#pragma once -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { enum class EResourceStatus { @@ -32,9 +31,7 @@ namespace ramses_internal }; } -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::EResourceStatus, +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::EResourceStatus, "EResourceStatus", - ramses_internal::ResourceStatusNames, - ramses_internal::EResourceStatus::Broken); - -#endif + ramses::internal::ResourceStatusNames, + ramses::internal::EResourceStatus::Broken); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ESceneState.h b/src/renderer/internal/RendererLib/Enums/ESceneState.h similarity index 87% rename from renderer/RendererLib/RendererLib/include/RendererLib/ESceneState.h rename to src/renderer/internal/RendererLib/Enums/ESceneState.h index 2c8452d4a..da137e564 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ESceneState.h +++ b/src/renderer/internal/RendererLib/Enums/ESceneState.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ESCENESTATE_H -#define RAMSES_ESCENESTATE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" + +#include #include -namespace ramses_internal +namespace ramses::internal { enum class ESceneState { @@ -27,7 +27,6 @@ namespace ramses_internal Mapped, // Scene is ready to be rendered RenderRequested, // (renderer internal state) Scene is requested to be rendered Rendered, // Scene is rendered - NUMBER_OF_ELEMENTS }; const std::array SceneStateNames = @@ -44,12 +43,10 @@ namespace ramses_internal "Rendered" }; - ENUM_TO_STRING(ESceneState, SceneStateNames, ESceneState::NUMBER_OF_ELEMENTS); + ENUM_TO_STRING(ESceneState, SceneStateNames, ESceneState::Rendered); static inline bool SceneStateIsAtLeast(ESceneState state, ESceneState minState) { return state >= minState; } } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/FrameProfilerStatistics.cpp b/src/renderer/internal/RendererLib/FrameProfilerStatistics.cpp similarity index 88% rename from renderer/RendererLib/RendererLib/src/FrameProfilerStatistics.cpp rename to src/renderer/internal/RendererLib/FrameProfilerStatistics.cpp index 5bde70282..d42b45757 100644 --- a/renderer/RendererLib/RendererLib/src/FrameProfilerStatistics.cpp +++ b/src/renderer/internal/RendererLib/FrameProfilerStatistics.cpp @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/FrameProfilerStatistics.h" -#include "Collections/StringOutputStream.h" -#include "Utils/LoggingUtils.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/RendererLib/FrameProfilerStatistics.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/PlatformAbstraction/PlatformMath.h" -namespace ramses_internal +namespace ramses::internal { FrameProfilerStatistics::FrameProfilerStatistics() : m_regionStartTimes(NumberOfRegions) - , m_currentRegionId{ 0 } { m_frameTimings.reserve(NumberOfFrames * NumberOfRegions); initNextFrameTimings(); @@ -23,7 +22,7 @@ namespace ramses_internal void FrameProfilerStatistics::startRegion(ERegion region) { - const size_t regionId = static_cast(region); + const auto regionId = static_cast(region); assert(regionId < m_regionStartTimes.size()); assert((regionId > m_currentRegionId || m_currentRegionId == 0u) && "Re-entering regions within frame is not supported!"); assert(region != ERegion::MaxFramerateSleep && "Do not call startRegion() with MaxFramerateSleep, it is handled internally."); @@ -34,11 +33,11 @@ namespace ramses_internal void FrameProfilerStatistics::endRegion(ERegion region) { - const size_t regionId = static_cast(region); + const auto regionId = static_cast(region); assert(m_currentRegionId == regionId); assert(region != ERegion::MaxFramerateSleep && "Do not call endRegion() with MaxFramerateSleep, it is handled internally."); - const size_t totalRegionTime = static_cast(PlatformTime::GetMicrosecondsMonotonic() - m_regionStartTimes[regionId]); + const auto totalRegionTime = static_cast(PlatformTime::GetMicrosecondsMonotonic() - m_regionStartTimes[regionId]); m_frameTimings[m_frameTimings.size() - NumberOfRegions + regionId] = totalRegionTime; } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/FrameProfilerStatistics.h b/src/renderer/internal/RendererLib/FrameProfilerStatistics.h similarity index 88% rename from renderer/RendererLib/RendererLib/include/RendererLib/FrameProfilerStatistics.h rename to src/renderer/internal/RendererLib/FrameProfilerStatistics.h index c7d6f7cb5..9e64d31fa 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/FrameProfilerStatistics.h +++ b/src/renderer/internal/RendererLib/FrameProfilerStatistics.h @@ -6,17 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEPROFILERSTATISTICS_H -#define RAMSES_FRAMEPROFILERSTATISTICS_H +#pragma once -#include "PlatformAbstraction/PlatformTime.h" -#include "Collections/Vector.h" -#include "Utils/LoggingUtils.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Core/Utils/LoggingUtils.h" -namespace ramses_internal +namespace ramses::internal { class StringOutputStream; + const std::array RegionNames = + { + "RendererCommands", + "UpdateClientResources", + "ApplySceneActions", + "UpdateSceneResources", + "UpdateEmbeddedCompositingResources", + "UpdateStreamTextures", + "UpdateScenesToBeMapped", + "UpdateResourceCache", + "UpdateAnimations", + "UpdateDataLinks", + "HandleDisplayEvents", + "DrawScenes", + "SwapBuffersNotifyClients", + "MaxFramerateSleep" + }; + class FrameProfilerStatistics { public: @@ -36,7 +53,6 @@ namespace ramses_internal DrawScenes, SwapBuffersAndNotifyClients, MaxFramerateSleep, // do not use this directly with startRegion/endRegion, it is handled internally - Count }; FrameProfilerStatistics(); @@ -60,9 +76,10 @@ namespace ramses_internal // these are reset every period std::vector m_frameTimings; - size_t m_currentRegionId; + size_t m_currentRegionId{0}; - static const uint32_t NumberOfRegions = static_cast(ERegion::Count); + static const uint32_t NumberOfRegions = static_cast(RegionNames.size()); + static_assert(EnumTraits::VerifyElementCountIfSupported(NumberOfRegions)); static const uint32_t NumberOfFrames = 600u; }; @@ -89,25 +106,5 @@ namespace ramses_internal #define FRAME_PROFILER_REGION(Region) \ ScopedFrameProfilerRegion region(m_renderer.getProfilerStatistics(), Region) - const std::array RegionNames = - { - "RendererCommands", - "UpdateClientResources", - "ApplySceneActions", - "UpdateSceneResources", - "UpdateEmbeddedCompositingResources", - "UpdateStreamTextures", - "UpdateScenesToBeMapped", - "UpdateResourceCache", - "UpdateAnimations", - "UpdateDataLinks", - "HandleDisplayEvents", - "DrawScenes", - "SwapBuffersNotifyClients", - "MaxFramerateSleep" - }; - - ENUM_TO_STRING(FrameProfilerStatistics::ERegion, RegionNames, FrameProfilerStatistics::ERegion::Count); + ENUM_TO_STRING(FrameProfilerStatistics::ERegion, RegionNames, FrameProfilerStatistics::ERegion::MaxFramerateSleep); } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/FrameTimer.h b/src/renderer/internal/RendererLib/FrameTimer.h similarity index 93% rename from renderer/RendererLib/RendererLib/include/RendererLib/FrameTimer.h rename to src/renderer/internal/RendererLib/FrameTimer.h index adfcee808..3c7eead36 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/FrameTimer.h +++ b/src/renderer/internal/RendererLib/FrameTimer.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMETIMER_H -#define RAMSES_FRAMETIMER_H +#pragma once -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/PlatformTime.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { enum class EFrameTimerSectionBudget { @@ -70,8 +69,6 @@ namespace ramses_internal using Duration = std::chrono::microseconds; Clock::time_point m_frameStartTimeStamp; - std::array(EFrameTimerSectionBudget::COUNT)> m_sectionBudgets; + std::array(EFrameTimerSectionBudget::COUNT)> m_sectionBudgets{}; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/IRendererResourceManager.h b/src/renderer/internal/RendererLib/IRendererResourceManager.h similarity index 85% rename from renderer/RendererLib/RendererLib/include/RendererLib/IRendererResourceManager.h rename to src/renderer/internal/RendererLib/IRendererResourceManager.h index 06f93af59..d00f2db8d 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/IRendererResourceManager.h +++ b/src/renderer/internal/RendererLib/IRendererResourceManager.h @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERERRESOURCEMANAGER_H -#define RAMSES_IRENDERERRESOURCEMANAGER_H +#pragma once #include "IResourceDeviceHandleAccessor.h" -#include "RendererLib/EResourceStatus.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/TextureSamplerStates.h" -#include "SceneAPI/EDataType.h" -#include "Resource/ResourceTypes.h" -#include "Components/ManagedResource.h" +#include "internal/RendererLib/Enums/EResourceStatus.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/TextureSamplerStates.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" +#include "internal/Components/ManagedResource.h" +#include "ramses/renderer/Types.h" #include #include -namespace ramses_internal +namespace ramses::internal { struct RenderTarget; - class IRendererResourceCache; enum class EDataBufferType : uint8_t; using StreamUsage = std::vector; @@ -53,11 +52,11 @@ namespace ramses_internal virtual void uploadDataBuffer(DataBufferHandle dataBufferHandle, EDataBufferType dataBufferType, EDataType dataType, uint32_t dataSizeInBytes, SceneId sceneId) = 0; virtual void unloadDataBuffer(DataBufferHandle dataBufferHandle, SceneId sceneId) = 0; - virtual void updateDataBuffer(DataBufferHandle handle, uint32_t dataSizeInBytes, const Byte* data, SceneId sceneId) = 0; + virtual void updateDataBuffer(DataBufferHandle handle, uint32_t dataSizeInBytes, const std::byte* data, SceneId sceneId) = 0; - virtual void uploadTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, ETextureFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId) = 0; + virtual void uploadTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId) = 0; virtual void unloadTextureBuffer(TextureBufferHandle textureBufferHandle, SceneId sceneId) = 0; - virtual void updateTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data, SceneId sceneId) = 0; + virtual void updateTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t mipLevel, const Quad& area, uint32_t stride, const std::byte* data, SceneId sceneId) = 0; virtual void uploadVertexArray(RenderableHandle renderableHandle, const VertexArrayInfo& vertexArrayInfo, SceneId sceneId) = 0; virtual void unloadVertexArray(RenderableHandle renderableHandle, SceneId sceneId) = 0; @@ -67,7 +66,7 @@ namespace ramses_internal [[nodiscard]] virtual const ResourceContentHashVector* getResourcesInUseByScene(SceneId sceneId) const = 0; // Renderer resources - virtual void uploadOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType) = 0; + virtual void uploadOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType) = 0; virtual void uploadDmaOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers) = 0; virtual void unloadOffscreenBuffer(OffscreenBufferHandle bufferHandle) = 0; @@ -80,4 +79,4 @@ namespace ramses_internal [[nodiscard]] virtual const StreamUsage& getStreamUsage(WaylandIviSurfaceId source) const = 0; }; } -#endif + diff --git a/renderer/RendererLib/RendererFramework/include/RendererFramework/IRendererSceneEventSender.h b/src/renderer/internal/RendererLib/IRendererSceneEventSender.h similarity index 78% rename from renderer/RendererLib/RendererFramework/include/RendererFramework/IRendererSceneEventSender.h rename to src/renderer/internal/RendererLib/IRendererSceneEventSender.h index 6d795a51f..ebcd375d2 100644 --- a/renderer/RendererLib/RendererFramework/include/RendererFramework/IRendererSceneEventSender.h +++ b/src/renderer/internal/RendererLib/IRendererSceneEventSender.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERERSCENEEVENTSENDER_H -#define RAMSES_IRENDERERSCENEEVENTSENDER_H +#pragma once -#include "SceneAPI/RendererSceneState.h" -#include "SceneAPI/SceneVersionTag.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" -namespace ramses_internal +namespace ramses::internal { class IRendererSceneEventSender { public: - virtual ~IRendererSceneEventSender() {} + virtual ~IRendererSceneEventSender() = default; virtual void sendSubscribeScene(SceneId sceneId) = 0; virtual void sendUnsubscribeScene(SceneId sceneId) = 0; @@ -30,4 +29,4 @@ namespace ramses_internal virtual void sendDataUnlinked(SceneId masterScene, SceneId consumerScene, DataSlotId consumer, bool success) = 0; }; } -#endif + diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/IRendererSceneStateControl.h b/src/renderer/internal/RendererLib/IRendererSceneStateControl.h similarity index 84% rename from renderer/RendererLib/RendererLib/include/RendererLib/IRendererSceneStateControl.h rename to src/renderer/internal/RendererLib/IRendererSceneStateControl.h index 8e27c2084..3829f9578 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/IRendererSceneStateControl.h +++ b/src/renderer/internal/RendererLib/IRendererSceneStateControl.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERERSCENESTATECONTROL_H -#define RAMSES_IRENDERERSCENESTATECONTROL_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/DataSlot.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" -namespace ramses_internal +namespace ramses::internal { class IRendererSceneStateControl { @@ -29,5 +28,3 @@ namespace ramses_internal virtual ~IRendererSceneStateControl() = default; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/IRendererSceneUpdater.h b/src/renderer/internal/RendererLib/IRendererSceneUpdater.h similarity index 88% rename from renderer/RendererLib/RendererLib/include/RendererLib/IRendererSceneUpdater.h rename to src/renderer/internal/RendererLib/IRendererSceneUpdater.h index 487b3edc1..f0cbe0f6c 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/IRendererSceneUpdater.h +++ b/src/renderer/internal/RendererLib/IRendererSceneUpdater.h @@ -6,16 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERERSCENEUPDATER_H -#define RAMSES_IRENDERERSCENEUPDATER_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/TextureEnums.h" -#include "SceneAPI/DataSlot.h" -#include "DataTypesImpl.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "impl/DataTypesImpl.h" +#include "ramses/renderer/Types.h" -namespace ramses_internal +namespace ramses::internal { struct SceneUpdate; class DisplayConfig; @@ -30,14 +31,14 @@ namespace ramses_internal virtual void handleScenePublished(SceneId sceneId, EScenePublicationMode mode) = 0; virtual void handleSceneUnpublished(SceneId sceneId) = 0; virtual void handleSceneReceived(const SceneInfo& sceneInfo) = 0; - virtual bool handleBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType) = 0; + virtual bool handleBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType) = 0; virtual bool handleDmaBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers) = 0; virtual bool handleBufferDestroyRequest(OffscreenBufferHandle buffer) = 0; virtual bool handleBufferCreateRequest(StreamBufferHandle buffer, WaylandIviSurfaceId source) = 0; virtual bool handleBufferDestroyRequest(StreamBufferHandle buffer) = 0; virtual bool handleExternalBufferCreateRequest(ExternalBufferHandle buffer) = 0; virtual bool handleExternalBufferDestroyRequest(ExternalBufferHandle buffer) = 0; - virtual void handleSetClearFlags(OffscreenBufferHandle buffer, uint32_t clearFlags) = 0; + virtual void handleSetClearFlags(OffscreenBufferHandle buffer, ClearFlags clearFlags) = 0; virtual void handleSetClearColor(OffscreenBufferHandle buffer, const glm::vec4& clearColor) = 0; virtual void handleSetExternallyOwnedWindowSize(uint32_t width, uint32_t height) = 0; virtual void handleReadPixels(OffscreenBufferHandle buffer, ScreenshotInfo&& screenshotInfo) = 0; @@ -55,5 +56,3 @@ namespace ramses_internal virtual ~IRendererSceneUpdater() = default; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/IResourceDeviceHandleAccessor.h b/src/renderer/internal/RendererLib/IResourceDeviceHandleAccessor.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/IResourceDeviceHandleAccessor.h rename to src/renderer/internal/RendererLib/IResourceDeviceHandleAccessor.h index 9a24d70e4..57cefae7e 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/IResourceDeviceHandleAccessor.h +++ b/src/renderer/internal/RendererLib/IResourceDeviceHandleAccessor.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRESOURCEDEVICEHANDLEACCESSOR_H -#define RAMSES_IRESOURCEDEVICEHANDLEACCESSOR_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class IResourceDeviceHandleAccessor { public: - virtual ~IResourceDeviceHandleAccessor() {} + virtual ~IResourceDeviceHandleAccessor() = default; [[nodiscard]] virtual DeviceResourceHandle getResourceDeviceHandle(const ResourceContentHash& resourceHash) const = 0; [[nodiscard]] virtual DeviceResourceHandle getRenderTargetDeviceHandle(RenderTargetHandle targetHandle, SceneId sceneId) const = 0; @@ -39,4 +38,4 @@ namespace ramses_internal [[nodiscard]] virtual DeviceResourceHandle getVertexArrayDeviceHandle(RenderableHandle renderableHandle, SceneId sceneId) const = 0; }; } -#endif + diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/IResourceUploader.h b/src/renderer/internal/RendererLib/IResourceUploader.h similarity index 77% rename from renderer/RendererLib/RendererLib/include/RendererLib/IResourceUploader.h rename to src/renderer/internal/RendererLib/IResourceUploader.h index 07b27e908..932b99244 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/IResourceUploader.h +++ b/src/renderer/internal/RendererLib/IResourceUploader.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRESOURCEUPLOADER_H -#define RAMSES_IRESOURCEUPLOADER_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Resource/ResourceTypes.h" -#include "Components/ManagedResource.h" -#include "SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" +#include "internal/Components/ManagedResource.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include -namespace ramses_internal +namespace ramses::internal { class IResource; class IRenderBackend; @@ -25,12 +24,10 @@ namespace ramses_internal class IResourceUploader { public: - virtual ~IResourceUploader() {} + virtual ~IResourceUploader() = default; virtual std::optional uploadResource(IRenderBackend& renderBackend, const ResourceDescriptor& resourceObject, uint32_t& outVRAMSize) = 0; virtual void unloadResource(IRenderBackend& renderBackend, EResourceType type, ResourceContentHash hash, DeviceResourceHandle handle) = 0; virtual void storeShaderInBinaryShaderCache(IRenderBackend& renderBackend, DeviceResourceHandle deviceHandle, const ResourceContentHash& hash, SceneId sceneid) = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/IntersectionUtils.cpp b/src/renderer/internal/RendererLib/IntersectionUtils.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/src/IntersectionUtils.cpp rename to src/renderer/internal/RendererLib/IntersectionUtils.cpp index 4a89c270c..5431552e4 100644 --- a/renderer/RendererLib/RendererLib/src/IntersectionUtils.cpp +++ b/src/renderer/internal/RendererLib/IntersectionUtils.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/IntersectionUtils.h" -#include "Math3d/CameraMatrixHelper.h" -#include "RendererAPI/IDisplayController.h" -#include "RendererAPI/Types.h" +#include "internal/RendererLib/IntersectionUtils.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/RendererLib/Types.h" #include "glm/gtc/type_ptr.hpp" -namespace ramses_internal +namespace ramses::internal { glm::vec3 IntersectionUtils::CalculatePlaneNormal(const Triangle& triangle) { @@ -122,7 +122,7 @@ namespace ramses_internal for (size_t fltIdx = 0u; fltIdx < geometrySize; fltIdx += 9) { const float* triData = &geometry[fltIdx]; - Triangle triangle; + Triangle triangle{}; std::copy(triData + 0, triData + 3, glm::value_ptr(triangle.v0)); std::copy(triData + 3, triData + 6, glm::value_ptr(triangle.v1)); std::copy(triData + 6, triData + 9, glm::value_ptr(triangle.v2)); @@ -174,6 +174,7 @@ namespace ramses_internal if (coordsInViewportSpace.x < 0 || coordsInViewportSpace.y < 0 || coordsInViewportSpace.x > vpSize.x || coordsInViewportSpace.y > vpSize.y) continue; + // NOLINTNEXTLINE(cppcoreguidelines-narrowing-conversions): implicit conversion from int to float const glm::vec2 coordsNDS = { 2.f * coordsInViewportSpace.x / vpSize.x - 1.f, 2.f * coordsInViewportSpace.y / vpSize.y - 1.f }; const auto cameraViewMatrix = scene.updateMatrixCacheWithLinks( @@ -193,8 +194,7 @@ namespace ramses_internal scene.getDataBuffer(pickableObject.geometryHandle); assert(geometryBuffer.bufferType == EDataBufferType::VertexBuffer); assert(geometryBuffer.dataType == EDataType::Vector3F); - const float* geometryBufferFloat = - reinterpret_cast(geometryBuffer.data.data()); + const auto* geometryBufferFloat = reinterpret_cast(geometryBuffer.data.data()); const uint32_t geometrySize = geometryBuffer.usedSize / sizeof(float); assert(0 == geometrySize % 9); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/IntersectionUtils.h b/src/renderer/internal/RendererLib/IntersectionUtils.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RendererLib/IntersectionUtils.h rename to src/renderer/internal/RendererLib/IntersectionUtils.h index bb456df0f..016a27a3c 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/IntersectionUtils.h +++ b/src/renderer/internal/RendererLib/IntersectionUtils.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERSECTIONUTILS_H -#define RAMSES_INTERSECTIONUTILS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" #include "TransformationLinkCachedScene.h" -namespace ramses_internal +#include + +namespace ramses::internal { class IntersectionUtils { @@ -39,4 +39,4 @@ namespace ramses_internal float& distanceRayOriginToIntersection); }; } -#endif + diff --git a/renderer/RendererLib/RendererLib/src/LinkManagerBase.cpp b/src/renderer/internal/RendererLib/LinkManagerBase.cpp similarity index 69% rename from renderer/RendererLib/RendererLib/src/LinkManagerBase.cpp rename to src/renderer/internal/RendererLib/LinkManagerBase.cpp index 7227c2ee3..e7cc21bcc 100644 --- a/renderer/RendererLib/RendererLib/src/LinkManagerBase.cpp +++ b/src/renderer/internal/RendererLib/LinkManagerBase.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/LinkManagerBase.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DataLinkUtils.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/LinkManagerBase.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DataLinkUtils.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { LinkManagerBase::LinkManagerBase(RendererScenes& rendererScenes) : m_scenes(rendererScenes) @@ -41,12 +41,12 @@ namespace ramses_internal bool LinkManagerBase::createDataLink(SceneId providerSceneId, DataSlotHandle providerSlotHandle, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) { - assert(EDataSlotType_DataProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type - || EDataSlotType_TransformationProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type - || EDataSlotType_TextureProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); - assert(EDataSlotType_DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type - || EDataSlotType_TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type - || EDataSlotType_TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::DataProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type + || EDataSlotType::TransformationProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type + || EDataSlotType::TextureProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); + assert(EDataSlotType::DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type + || EDataSlotType::TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type + || EDataSlotType::TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); if (m_sceneLinks.hasLinkedProvider(consumerSceneId, consumerSlotHandle)) { @@ -67,9 +67,9 @@ namespace ramses_internal bool LinkManagerBase::removeDataLink(SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) { - assert(EDataSlotType_DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type - || EDataSlotType_TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type - || EDataSlotType_TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::DataConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type + || EDataSlotType::TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type + || EDataSlotType::TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); if (!m_sceneLinks.hasLinkedProvider(consumerSceneId, consumerSlotHandle)) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/LinkManagerBase.h b/src/renderer/internal/RendererLib/LinkManagerBase.h similarity index 88% rename from renderer/RendererLib/RendererLib/include/RendererLib/LinkManagerBase.h rename to src/renderer/internal/RendererLib/LinkManagerBase.h index 50b80dd21..9db4c38f2 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/LinkManagerBase.h +++ b/src/renderer/internal/RendererLib/LinkManagerBase.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINKMANAGERBASE_H -#define RAMSES_LINKMANAGERBASE_H +#pragma once -#include "RendererLib/SceneLinks.h" -#include "SceneDependencyChecker.h" +#include "internal/RendererLib/SceneLinks.h" +#include "internal/RendererLib/SceneDependencyChecker.h" -namespace ramses_internal +namespace ramses::internal { class RendererScenes; @@ -37,5 +36,3 @@ namespace ramses_internal SceneDependencyChecker m_dependencyChecker; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/LoggingDevice.cpp b/src/renderer/internal/RendererLib/LoggingDevice.cpp similarity index 84% rename from renderer/RendererLib/RendererLib/src/LoggingDevice.cpp rename to src/renderer/internal/RendererLib/LoggingDevice.cpp index e3c797d6a..82c03816e 100644 --- a/renderer/RendererLib/RendererLib/src/LoggingDevice.cpp +++ b/src/renderer/internal/RendererLib/LoggingDevice.cpp @@ -6,14 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/LoggingDevice.h" -#include "RendererLib/ConstantLogger.h" -#include "SceneAPI/RenderBuffer.h" -#include "Resource/EffectResource.h" -#include "SceneAPI/TextureSamplerStates.h" -#include "AppearanceEnumsImpl.h" - -namespace ramses_internal +#include "internal/RendererLib/LoggingDevice.h" +#include "internal/RendererLib/ConstantLogger.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/SceneAPI/TextureSamplerStates.h" +#include "impl/AppearanceEnumsImpl.h" +#include "impl/TextureEnumsImpl.h" + +namespace ramses::internal { LoggingDevice::LoggingDevice(const IDevice& deviceDelegate, RendererLogContext& context) : m_deviceDelegate(deviceDelegate) @@ -21,59 +22,76 @@ namespace ramses_internal { } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; + } + + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) + { + ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const bool* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } - void LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const float* value) + bool LoggingDevice::setConstant(DataFieldHandle field, uint32_t count, const float* value) { ConstantLogger::LogValueArray(field, value, count, m_logContext); + return true; } void LoggingDevice::colorMask(bool r, bool g, bool b, bool a) @@ -181,7 +199,7 @@ namespace ramses_internal return DeviceResourceHandle::Invalid(); } - void LoggingDevice::uploadVertexBufferData(DeviceResourceHandle handle, const Byte*, uint32_t dataSize) + void LoggingDevice::uploadVertexBufferData(DeviceResourceHandle handle, const std::byte* /*data*/, uint32_t dataSize) { m_logContext << "upload vertex buffer data [device handle: " << handle << " size: " << dataSize << "]" << RendererLogContext::NewLine; } @@ -191,7 +209,7 @@ namespace ramses_internal m_logContext << "delete vertex buffer [handle: " << handle << "]" << RendererLogContext::NewLine; } - DeviceResourceHandle LoggingDevice::allocateVertexArray(const VertexArrayInfo&) + DeviceResourceHandle LoggingDevice::allocateVertexArray(const VertexArrayInfo& /*vertexArrayInfo*/) { m_logContext << "allocate vertex array" << RendererLogContext::NewLine; return {}; @@ -213,7 +231,7 @@ namespace ramses_internal return DeviceResourceHandle::Invalid(); } - void LoggingDevice::uploadIndexBufferData(DeviceResourceHandle handle, const Byte*, uint32_t dataSize) + void LoggingDevice::uploadIndexBufferData(DeviceResourceHandle handle, const std::byte* /*data*/, uint32_t dataSize) { m_logContext << "upload index buffer data [device handle: " << handle << " size: " << dataSize << "]" << RendererLogContext::NewLine; } @@ -235,19 +253,18 @@ namespace ramses_internal return {}; } - DeviceResourceHandle LoggingDevice::uploadBinaryShader(const EffectResource& effect, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) + DeviceResourceHandle LoggingDevice::uploadBinaryShader(const EffectResource& effect, + [[maybe_unused]] const std::byte* binaryShaderData, + uint32_t binaryShaderDataSize, + BinaryShaderFormatID binaryShaderFormat) { - UNUSED(binaryShaderData); - m_logContext << "upload binary shader " << effect.getName() << " size: " << binaryShaderDataSize << " format: " << binaryShaderFormat << RendererLogContext::NewLine; return DeviceResourceHandle::Invalid(); } - bool LoggingDevice::getBinaryShader(DeviceResourceHandle handle, UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) + bool + LoggingDevice::getBinaryShader(DeviceResourceHandle handle, [[maybe_unused]] std::vector& binaryShader, [[maybe_unused]] BinaryShaderFormatID& binaryShaderFormat) { - UNUSED(binaryShader); - UNUSED(binaryShaderFormat); - m_logContext << "get shader binary for shader [handle: " << handle << "]" << RendererLogContext::NewLine; return false; } @@ -262,7 +279,7 @@ namespace ramses_internal m_logContext << "activate shader [handle: " << handle << "]" << RendererLogContext::NewLine; } - DeviceResourceHandle LoggingDevice::allocateTexture2D(uint32_t width, uint32_t height, ETextureFormat format, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) + DeviceResourceHandle LoggingDevice::allocateTexture2D(uint32_t width, uint32_t height, EPixelStorageFormat format, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) { m_logContext << "allocate texture2d [ (w,h):(" << width << "," << height << ") mipLevelCount:" << mipLevelCount << " format:" << EnumToString(format) << "textureSwizzle:"<< EnumToString(swizzle[0]) << ";" << EnumToString(swizzle[1]) << ";" << EnumToString(swizzle[2]) << ";" << EnumToString(swizzle[3]) @@ -270,13 +287,13 @@ namespace ramses_internal return DeviceResourceHandle::Invalid(); } - DeviceResourceHandle LoggingDevice::allocateTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, uint32_t mipLevelCount, uint32_t) + DeviceResourceHandle LoggingDevice::allocateTexture3D(uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat format, uint32_t mipLevelCount, uint32_t /*totalSizeInBytes*/) { m_logContext << "allocate texture3d [ (w,h,d):(" << width << "," << height << "," << depth << ") mipLevelCount:" << mipLevelCount << " format:" << EnumToString(format) << "]" << RendererLogContext::NewLine; return DeviceResourceHandle::Invalid(); } - DeviceResourceHandle LoggingDevice::allocateTextureCube(uint32_t faceSize, ETextureFormat format, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t) + DeviceResourceHandle LoggingDevice::allocateTextureCube(uint32_t faceSize, EPixelStorageFormat format, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t /*totalSizeInBytes*/) { m_logContext << "allocate textureCube [ faceSize:" << faceSize << " mipLevelCount:" << mipLevelCount << " format:" << EnumToString(format) << "textureSwizzle:"<< EnumToString(swizzle[0]) << ";" << EnumToString(swizzle[1]) << ";" << EnumToString(swizzle[2]) << ";" << EnumToString(swizzle[3]) << "]" << RendererLogContext::NewLine; @@ -305,12 +322,12 @@ namespace ramses_internal m_logContext << "generate mipmaps for texture [handle:" << handle << "]" << RendererLogContext::NewLine; } - void LoggingDevice::uploadTextureData(DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const Byte*, uint32_t dataSize) + void LoggingDevice::uploadTextureData(DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const std::byte* /*data*/, uint32_t dataSize, uint32_t /*stride*/) { m_logContext << "update texture data [handle:" << handle << " mipLevel:" << mipLevel << " (x,y,z):(" << x << "," << y << "," << z << ") (w,h,d):(" << width << "," << height << "," << depth << ") dataSize:" << dataSize << "]" << RendererLogContext::NewLine; } - DeviceResourceHandle LoggingDevice::uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, ETextureFormat, const uint8_t*, const TextureSwizzleArray& swizzle) + DeviceResourceHandle LoggingDevice::uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, EPixelStorageFormat /*format*/, const std::byte* /*data*/, const TextureSwizzleArray& swizzle) { m_logContext << "upload stream texture2d [textureHandle: " << handle << " (w,h):(" << width << "," << height << ") " << "textureSwizzle: " << EnumToString(swizzle[0]) << "," << EnumToString(swizzle[1]) << "," << EnumToString(swizzle[2]) << "," << EnumToString(swizzle[3]) << "]" << RendererLogContext::NewLine; return DeviceResourceHandle::Invalid(); @@ -326,9 +343,9 @@ namespace ramses_internal logResourceActivation("texture", handle, field); } - DeviceResourceHandle LoggingDevice::uploadRenderBuffer(uint32_t, uint32_t, ERenderBufferType type, ETextureFormat, ERenderBufferAccessMode, uint32_t) + DeviceResourceHandle LoggingDevice::uploadRenderBuffer(uint32_t /*width*/, uint32_t /*height*/, EPixelStorageFormat format, ERenderBufferAccessMode /*accessMode*/, uint32_t /*sampleCount*/) { - m_logContext << "upload render buffer [type: " << EnumToString(type) << "]" << RendererLogContext::NewLine; + m_logContext << "upload render buffer [type: " << EnumToString(format) << "]" << RendererLogContext::NewLine; return DeviceResourceHandle::Invalid(); } @@ -414,9 +431,9 @@ namespace ramses_internal m_logContext << "draw " << instanceCount << " instances with " << elementCount << " vertices starting from " << startOffset << RendererLogContext::NewLine; } - void LoggingDevice::clear(uint32_t clearFlags) + void LoggingDevice::clear(ClearFlags clearFlags) { - m_logContext << "clear buffer [flags: " << clearFlags << "]" << RendererLogContext::NewLine; + m_logContext << "clear buffer [flags: " << clearFlags.value() << "]" << RendererLogContext::NewLine; } void LoggingDevice::pairRenderTargetsForDoubleBuffering(const std::array& renderTargets, const std::array& colorBuffers) @@ -468,7 +485,7 @@ namespace ramses_internal m_logContext << "clear stencil to [ " << s << "]" << RendererLogContext::NewLine; } - int32_t LoggingDevice::getTextureAddress(DeviceResourceHandle) const + uint32_t LoggingDevice::getTextureAddress(DeviceResourceHandle /*handle*/) const { return 0; } @@ -482,7 +499,7 @@ namespace ramses_internal return true; } - void LoggingDevice::getSupportedBinaryProgramFormats(std::vector&) const + void LoggingDevice::getSupportedBinaryProgramFormats(std::vector& /*unused*/) const { } @@ -500,7 +517,7 @@ namespace ramses_internal { } - uint32_t LoggingDevice::getGPUHandle(DeviceResourceHandle) const + uint32_t LoggingDevice::getGPUHandle(DeviceResourceHandle /*deviceHandle*/) const { return 0u; } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/LoggingDevice.h b/src/renderer/internal/RendererLib/LoggingDevice.h similarity index 76% rename from renderer/RendererLib/RendererLib/include/RendererLib/LoggingDevice.h rename to src/renderer/internal/RendererLib/LoggingDevice.h index ec5320091..1ba3119b2 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/LoggingDevice.h +++ b/src/renderer/internal/RendererLib/LoggingDevice.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGGINGDEVICE_H -#define RAMSES_LOGGINGDEVICE_H +#pragma once -#include "RendererAPI/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererLogContext; @@ -22,17 +21,18 @@ namespace ramses_internal public: LoggingDevice(const IDevice& deviceDelegate, RendererLogContext& context); - void setConstant(DataFieldHandle field, uint32_t count, const float* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) override; - void setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const float* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const bool* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) override; + bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) override; void colorMask(bool r, bool g, bool b, bool a) override; void clearColor(const glm::vec4& clearColor) override; @@ -49,32 +49,32 @@ namespace ramses_internal void setViewport(int32_t x, int32_t y, uint32_t width, uint32_t height) override; DeviceResourceHandle allocateVertexBuffer(uint32_t totalSizeInBytes) override; - void uploadVertexBufferData(DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) override; + void uploadVertexBufferData(DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) override; void deleteVertexBuffer(DeviceResourceHandle handle) override; DeviceResourceHandle allocateVertexArray(const VertexArrayInfo& vertexArrayInfo) override; void activateVertexArray(DeviceResourceHandle handle) override; void deleteVertexArray(DeviceResourceHandle handle) override; DeviceResourceHandle allocateIndexBuffer(EDataType dataType, uint32_t sizeInBytes) override; - void uploadIndexBufferData(DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) override; + void uploadIndexBufferData(DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) override; void deleteIndexBuffer(DeviceResourceHandle handle) override; std::unique_ptr uploadShader(const EffectResource& effect) override; DeviceResourceHandle registerShader(std::unique_ptr shaderResource) override; - DeviceResourceHandle uploadBinaryShader(const EffectResource& effect, const uint8_t* binaryShaderData = nullptr, uint32_t binaryShaderDataSize = 0, BinaryShaderFormatID binaryShaderFormat = {}) override; - bool getBinaryShader(DeviceResourceHandle handle, UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) override; + DeviceResourceHandle uploadBinaryShader(const EffectResource& effect, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) override; + bool getBinaryShader(DeviceResourceHandle handle, std::vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) override; void deleteShader(DeviceResourceHandle handle) override; void activateShader(DeviceResourceHandle handle) override; - DeviceResourceHandle allocateTexture2D(uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; - DeviceResourceHandle allocateTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, uint32_t mipLevelCount, uint32_t dataSize) override; - DeviceResourceHandle allocateTextureCube(uint32_t faceSize, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t dataSize) override; + DeviceResourceHandle allocateTexture2D(uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) override; + DeviceResourceHandle allocateTexture3D(uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, uint32_t dataSize) override; + DeviceResourceHandle allocateTextureCube(uint32_t faceSize, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t dataSize) override; DeviceResourceHandle allocateExternalTexture() override; [[nodiscard]] DeviceResourceHandle getEmptyExternalTexture() const override; void bindTexture(DeviceResourceHandle handle) override; void generateMipmaps(DeviceResourceHandle handle) override; - void uploadTextureData(DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const Byte* data, uint32_t dataSize) override; - DeviceResourceHandle uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) override; + void uploadTextureData(DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const std::byte* data, uint32_t dataSize, uint32_t stride) override; + DeviceResourceHandle uploadStreamTexture2D(DeviceResourceHandle handle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) override; void deleteTexture(DeviceResourceHandle handle) override; void activateTexture(DeviceResourceHandle handle, DataFieldHandle field) override; - DeviceResourceHandle uploadRenderBuffer(uint32_t width, uint32_t height, ERenderBufferType type, ETextureFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) override; + DeviceResourceHandle uploadRenderBuffer(uint32_t width, uint32_t height, EPixelStorageFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) override; void deleteRenderBuffer(DeviceResourceHandle handle) override; void activateTextureSamplerObject(const TextureSamplerStates& samplerStates, DataFieldHandle field) override; @@ -92,7 +92,7 @@ namespace ramses_internal void drawIndexedTriangles(int32_t startOffset, int32_t elementCount, uint32_t instanceCount) override; void drawTriangles(int32_t startOffset, int32_t elementCount, uint32_t instanceCount) override; - void clear(uint32_t clearFlags) override; + void clear(ClearFlags clearFlags) override; void pairRenderTargetsForDoubleBuffering(const std::array& renderTargets, const std::array& colorBuffers) override; void unpairRenderTargets(DeviceResourceHandle renderTarget) override; @@ -106,7 +106,7 @@ namespace ramses_internal void clearDepth(float d) override; void clearStencil(int32_t s) override; - [[nodiscard]] int32_t getTextureAddress(DeviceResourceHandle handle) const override; + [[nodiscard]] uint32_t getTextureAddress(DeviceResourceHandle handle) const override; void validateDeviceStatusHealthy() const override; [[nodiscard]] bool isDeviceStatusHealthy() const override; @@ -126,5 +126,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/PendingSceneResourcesUtils.cpp b/src/renderer/internal/RendererLib/PendingSceneResourcesUtils.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/PendingSceneResourcesUtils.cpp rename to src/renderer/internal/RendererLib/PendingSceneResourcesUtils.cpp index 7adefef2d..da874faeb 100644 --- a/renderer/RendererLib/RendererLib/src/PendingSceneResourcesUtils.cpp +++ b/src/renderer/internal/RendererLib/PendingSceneResourcesUtils.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/PendingSceneResourcesUtils.h" -#include "RendererLib/IRendererResourceManager.h" -#include "RendererLib/SceneResourceUploader.h" -#include "RendererLib/FrameTimer.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/GeometryDataBuffer.h" +#include "internal/RendererLib/PendingSceneResourcesUtils.h" +#include "internal/RendererLib/IRendererResourceManager.h" +#include "internal/RendererLib/SceneResourceUploader.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/SceneGraph/SceneAPI/GeometryDataBuffer.h" -namespace ramses_internal +namespace ramses::internal { void PendingSceneResourcesUtils::ConsolidateSceneResourceActions(const SceneResourceActionVector& newActions, SceneResourceActionVector& currentActionsInOut) { @@ -71,7 +71,10 @@ namespace ramses_internal } } - bool PendingSceneResourcesUtils::ApplySceneResourceActions(const SceneResourceActionVector& actions, const IScene& scene, IRendererResourceManager& resourceManager, const FrameTimer* frameTimer) + bool PendingSceneResourcesUtils::ApplySceneResourceActions(const SceneResourceActionVector& actions, + const RendererCachedScene& scene, + IRendererResourceManager& resourceManager, + const FrameTimer* frameTimer) { constexpr size_t TimeCheckPeriod = 20u; constexpr size_t ThresholdForTimeChecking = 100u; diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/PendingSceneResourcesUtils.h b/src/renderer/internal/RendererLib/PendingSceneResourcesUtils.h similarity index 78% rename from renderer/RendererLib/RendererLib/include/RendererLib/PendingSceneResourcesUtils.h rename to src/renderer/internal/RendererLib/PendingSceneResourcesUtils.h index 66020e874..b8d6a32ce 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/PendingSceneResourcesUtils.h +++ b/src/renderer/internal/RendererLib/PendingSceneResourcesUtils.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PENDINGSCENERESOURCESUTILS_H -#define RAMSES_PENDINGSCENERESOURCESUTILS_H +#pragma once -#include "Scene/ResourceChanges.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" -namespace ramses_internal +namespace ramses::internal { - class IScene; + class RendererCachedScene; class IRendererResourceManager; class FrameTimer; @@ -21,12 +20,10 @@ namespace ramses_internal { public: static void ConsolidateSceneResourceActions(const SceneResourceActionVector& newActions, SceneResourceActionVector& currentActionsInOut); - static bool ApplySceneResourceActions(const SceneResourceActionVector& actions, const IScene& scene, IRendererResourceManager& resourceManager, const FrameTimer* frameTimer = nullptr); + static bool ApplySceneResourceActions(const SceneResourceActionVector& actions, const RendererCachedScene& scene, IRendererResourceManager& resourceManager, const FrameTimer* frameTimer = nullptr); private: static bool RemoveSceneResourceActionIfContained(SceneResourceActionVector& actions, MemoryHandle handle, ESceneResourceAction action); static bool ContainsSceneResourceAction(const SceneResourceActionVector& actions, MemoryHandle handle, ESceneResourceAction action); }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/Context_Base.cpp b/src/renderer/internal/RendererLib/PlatformBase/Context_Base.cpp similarity index 88% rename from renderer/RendererLib/Platform_Base/src/Context_Base.cpp rename to src/renderer/internal/RendererLib/PlatformBase/Context_Base.cpp index ba545a205..33f57640c 100644 --- a/renderer/RendererLib/Platform_Base/src/Context_Base.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/Context_Base.cpp @@ -6,14 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/Context_Base.h" -#include "Utils/StringUtils.h" +#include "internal/RendererLib/PlatformBase/Context_Base.h" +#include "internal/Core/Utils/StringUtils.h" -namespace ramses_internal +namespace ramses::internal { - Context_Base::Context_Base() - { - } + Context_Base::Context_Base() = default; DeviceResourceMapper& Context_Base::getResources() { diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/Context_Base.h b/src/renderer/internal/RendererLib/PlatformBase/Context_Base.h similarity index 85% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/Context_Base.h rename to src/renderer/internal/RendererLib/PlatformBase/Context_Base.h index 7330741e8..cba2def82 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/Context_Base.h +++ b/src/renderer/internal/RendererLib/PlatformBase/Context_Base.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTEXT_BASE_H -#define RAMSES_CONTEXT_BASE_H +#pragma once -#include "RendererAPI/IContext.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" -#include "Collections/HashSet.h" -#include "Platform_Base/DeviceResourceMapper.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/RendererLib/PlatformBase/DeviceResourceMapper.h" #include -namespace ramses_internal +namespace ramses::internal { class DisplayConfig; @@ -42,5 +41,3 @@ namespace ramses_internal HashSet m_apiExtensions; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/DeviceResourceMapper.cpp b/src/renderer/internal/RendererLib/PlatformBase/DeviceResourceMapper.cpp similarity index 92% rename from renderer/RendererLib/Platform_Base/src/DeviceResourceMapper.cpp rename to src/renderer/internal/RendererLib/PlatformBase/DeviceResourceMapper.cpp index dc88072fe..811beb226 100644 --- a/renderer/RendererLib/Platform_Base/src/DeviceResourceMapper.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/DeviceResourceMapper.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/DeviceResourceMapper.h" -#include "Platform_Base/GpuResource.h" +#include "internal/RendererLib/PlatformBase/DeviceResourceMapper.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" -namespace ramses_internal +namespace ramses::internal { DeviceResourceMapper::DeviceResourceMapper() { diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/DeviceResourceMapper.h b/src/renderer/internal/RendererLib/PlatformBase/DeviceResourceMapper.h similarity index 88% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/DeviceResourceMapper.h rename to src/renderer/internal/RendererLib/PlatformBase/DeviceResourceMapper.h index 3984e9dab..108f0042a 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/DeviceResourceMapper.h +++ b/src/renderer/internal/RendererLib/PlatformBase/DeviceResourceMapper.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICERESOURCEMAPPER_H -#define RAMSES_DEVICERESOURCEMAPPER_H +#pragma once -#include "RendererAPI/Types.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Platform_Base/GpuResource.h" -#include "Utils/MemoryPool.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" +#include "internal/Core/Utils/MemoryPool.h" + +#include #include #include -namespace ramses_internal +namespace ramses::internal { class DeviceResourceMapper { @@ -56,5 +56,3 @@ namespace ramses_internal return **m_resources.getMemory(resourceHandle); } } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/Device_Base.cpp b/src/renderer/internal/RendererLib/PlatformBase/Device_Base.cpp similarity index 69% rename from renderer/RendererLib/Platform_Base/src/Device_Base.cpp rename to src/renderer/internal/RendererLib/PlatformBase/Device_Base.cpp index f9f794336..cd03cfdba 100644 --- a/renderer/RendererLib/Platform_Base/src/Device_Base.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/Device_Base.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/Device_Base.h" -#include "Platform_Base/DeviceResourceMapper.h" -#include "RendererAPI/IContext.h" +#include "internal/RendererLib/PlatformBase/Device_Base.h" +#include "internal/RendererLib/PlatformBase/DeviceResourceMapper.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" -namespace ramses_internal +namespace ramses::internal { Device_Base::Device_Base(IContext& context) : m_context(context) @@ -23,16 +23,13 @@ namespace ramses_internal return m_limits; } - void Device_Base::drawIndexedTriangles(int32_t, int32_t, uint32_t) + void Device_Base::drawIndexedTriangles(int32_t /*startOffset*/, int32_t /*elementCount*/, uint32_t /*instanceCount*/) { ++m_drawCalls; } - void Device_Base::drawTriangles(int32_t startOffset, int32_t elementCount, uint32_t instanceCount) + void Device_Base::drawTriangles([[maybe_unused]] int32_t startOffset, [[maybe_unused]] int32_t elementCount, [[maybe_unused]] uint32_t instanceCount) { - UNUSED(startOffset); - UNUSED(elementCount); - UNUSED(instanceCount); ++m_drawCalls; } diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/Device_Base.h b/src/renderer/internal/RendererLib/PlatformBase/Device_Base.h similarity index 91% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/Device_Base.h rename to src/renderer/internal/RendererLib/PlatformBase/Device_Base.h index 12ccc81d6..4bd9253f7 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/Device_Base.h +++ b/src/renderer/internal/RendererLib/PlatformBase/Device_Base.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICE_BASE_H -#define RAMSES_DEVICE_BASE_H +#pragma once -#include "RendererAPI/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" #include "RendererLimits.h" -namespace ramses_internal +namespace ramses::internal { class IContext; class DeviceResourceMapper; @@ -38,5 +37,3 @@ namespace ramses_internal uint32_t m_drawCalls = 0u; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/EmbeddedCompositor_Dummy.cpp b/src/renderer/internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.cpp similarity index 85% rename from renderer/RendererLib/Platform_Base/src/EmbeddedCompositor_Dummy.cpp rename to src/renderer/internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.cpp index b15e9bd85..d1eead518 100644 --- a/renderer/RendererLib/Platform_Base/src/EmbeddedCompositor_Dummy.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.cpp @@ -6,34 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/EmbeddedCompositor_Dummy.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererAPI/ITextureUploadingAdapter.h" -#include "RendererAPI/Types.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/PlatformInterface/ITextureUploadingAdapter.h" +#include "internal/RendererLib/Types.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { EmbeddedCompositor_Dummy::EmbeddedCompositor_Dummy() { LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositor_Dummy::EmbeddedCompositor_Dummy: created EmbeddedCompositor_Dummy"); } - bool EmbeddedCompositor_Dummy::init() - { - LOG_DEBUG(CONTEXT_RENDERER, "EmbeddedCompositor_Dummy::EmbeddedCompositor_Dummy:"); - return true; - } - - void EmbeddedCompositor_Dummy::endFrame(bool) + void EmbeddedCompositor_Dummy::endFrame(bool /*notifyClients*/) { LOG_TRACE(CONTEXT_RENDERER, "EmbeddedCompositor_Dummy::endFrame"); } - uint32_t EmbeddedCompositor_Dummy::uploadCompositingContentForStreamTexture(WaylandIviSurfaceId streamTextureSourceId, DeviceResourceHandle textureHandle, ITextureUploadingAdapter& textureUploadingAdapter) + uint32_t EmbeddedCompositor_Dummy::uploadCompositingContentForStreamTexture(WaylandIviSurfaceId streamTextureSourceId, [[maybe_unused]] DeviceResourceHandle textureHandle, [[maybe_unused]] ITextureUploadingAdapter& textureUploadingAdapter) { - UNUSED(textureUploadingAdapter); - UNUSED(textureHandle); LOG_TRACE(CONTEXT_RENDERER, "EmbeddedCompositor_Dummy::uploadCompositingContentForStreamTexture: " << streamTextureSourceId.getValue()); return 0; } @@ -88,6 +80,10 @@ namespace ramses_internal context << "No embedded compositor information available." << RendererLogContext::NewLine; } + void EmbeddedCompositor_Dummy::logPeriodicInfo(StringOutputStream& /*sos*/) const + { + } + bool EmbeddedCompositor_Dummy::isRealCompositor() const { return false; diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/EmbeddedCompositor_Dummy.h b/src/renderer/internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h similarity index 91% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/EmbeddedCompositor_Dummy.h rename to src/renderer/internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h index 7eb4c650d..d3a9c50bc 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/EmbeddedCompositor_Dummy.h +++ b/src/renderer/internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h @@ -6,20 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITOR_DUMMY_H -#define RAMSES_EMBEDDEDCOMPOSITOR_DUMMY_H +#pragma once -#include "RendererAPI/IEmbeddedCompositor.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositor_Dummy : public IEmbeddedCompositor { public: EmbeddedCompositor_Dummy(); - bool init(); - void handleRequestsFromClients() override; [[nodiscard]] bool hasUpdatedStreamTextureSources() const override; WaylandIviSurfaceIdSet dispatchUpdatedStreamTextureSourceIds() override; @@ -35,9 +32,8 @@ namespace ramses_internal [[nodiscard]] bool hasSurfaceForStreamTexture(WaylandIviSurfaceId streamTextureSourceId) const override; [[nodiscard]] std::string getTitleOfWaylandIviSurface(WaylandIviSurfaceId waylandSurfaceId) const override; void logInfos(RendererLogContext& context) const override; + void logPeriodicInfo(StringOutputStream& sos) const override; [[nodiscard]] bool isRealCompositor() const override; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/GpuResource.h b/src/renderer/internal/RendererLib/PlatformBase/GpuResource.h similarity index 86% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/GpuResource.h rename to src/renderer/internal/RendererLib/PlatformBase/GpuResource.h index 6aa54bb56..c5f645cac 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/GpuResource.h +++ b/src/renderer/internal/RendererLib/PlatformBase/GpuResource.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GPURESOURCE_H -#define RAMSES_GPURESOURCE_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class GPUResource { @@ -22,7 +21,7 @@ namespace ramses_internal { } - virtual ~GPUResource() {} + virtual ~GPUResource() = default; [[nodiscard]] inline uint32_t getGPUAddress() const { @@ -39,5 +38,3 @@ namespace ramses_internal const uint32_t m_sizeInBytes; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/IndexBufferGPUResource.h b/src/renderer/internal/RendererLib/PlatformBase/IndexBufferGPUResource.h similarity index 86% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/IndexBufferGPUResource.h rename to src/renderer/internal/RendererLib/PlatformBase/IndexBufferGPUResource.h index 6a7cb5b0d..b760dbef9 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/IndexBufferGPUResource.h +++ b/src/renderer/internal/RendererLib/PlatformBase/IndexBufferGPUResource.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INDEXBUFFERGPURESOURCE_H -#define RAMSES_INDEXBUFFERGPURESOURCE_H +#pragma once -#include "Platform_Base/GpuResource.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" -namespace ramses_internal +namespace ramses::internal { class IndexBufferGPUResource : public GPUResource { @@ -31,5 +30,3 @@ namespace ramses_internal const uint32_t m_elementSizeInBytes; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/Platform_Base.cpp b/src/renderer/internal/RendererLib/PlatformBase/Platform_Base.cpp similarity index 85% rename from renderer/RendererLib/Platform_Base/src/Platform_Base.cpp rename to src/renderer/internal/RendererLib/PlatformBase/Platform_Base.cpp index dc37272a1..223153be8 100644 --- a/renderer/RendererLib/Platform_Base/src/Platform_Base.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/Platform_Base.cpp @@ -6,25 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/Platform_Base.h" -#include "RendererAPI/IWindow.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IDeviceExtension.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RendererAPI/ISystemCompositorController.h" -#include "RendererLib/RenderBackend.h" -#include "RendererLib/ResourceUploadRenderBackend.h" -#include "RendererLib/RendererConfig.h" -#include "RendererLib/DisplayConfig.h" -#include "Platform_Base/TextureUploadingAdapter_Base.h" -#include "Platform_Base/EmbeddedCompositor_Dummy.h" -#include "Utils/ThreadLocalLog.h" - -namespace ramses_internal +#include "internal/RendererLib/PlatformBase/Platform_Base.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IDeviceExtension.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/RendererLib/PlatformInterface/ISystemCompositorController.h" +#include "internal/RendererLib/RenderBackend.h" +#include "internal/RendererLib/ResourceUploadRenderBackend.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h" +#include "internal/RendererLib/PlatformBase/EmbeddedCompositor_Dummy.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal { - Platform_Base::Platform_Base(const RendererConfig& rendererConfig) - : m_rendererConfig(rendererConfig) + Platform_Base::Platform_Base(RendererConfig rendererConfig) + : m_rendererConfig(std::move(rendererConfig)) { } @@ -177,18 +177,15 @@ namespace ramses_internal return m_systemCompositorController.get(); } - void Platform_Base::createTextureUploadingAdapter(const DisplayConfig&) + void Platform_Base::createTextureUploadingAdapter(const DisplayConfig& /*unused*/) { assert(!m_textureUploadingAdapter); m_textureUploadingAdapter = std::make_unique(*m_device); } - bool Platform_Base::createEmbeddedCompositor(const DisplayConfig&) + bool Platform_Base::createEmbeddedCompositor(const DisplayConfig& /*unused*/) { - auto embeddedCompositor = std::make_unique(); - if (embeddedCompositor->init()) - m_embeddedCompositor = std::move(embeddedCompositor); - + m_embeddedCompositor = std::make_unique(); return m_embeddedCompositor != nullptr; } diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/Platform_Base.h b/src/renderer/internal/RendererLib/PlatformBase/Platform_Base.h similarity index 90% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/Platform_Base.h rename to src/renderer/internal/RendererLib/PlatformBase/Platform_Base.h index add363aa8..173053709 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/Platform_Base.h +++ b/src/renderer/internal/RendererLib/PlatformBase/Platform_Base.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_BASE_H -#define RAMSES_PLATFORM_BASE_H +#pragma once -#include "RendererAPI/IPlatform.h" -#include "RendererLib/RendererConfig.h" -#include "Collections/Vector.h" +#include "internal/RendererLib/PlatformInterface/IPlatform.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ITextureUploadingAdapter; class IDevice; @@ -36,7 +35,7 @@ namespace ramses_internal ISystemCompositorController* getSystemCompositorController() override; protected: - explicit Platform_Base(const RendererConfig& rendererConfig); + explicit Platform_Base(RendererConfig rendererConfig); ~Platform_Base() override; virtual bool createWindow(const DisplayConfig& displayConfig, IWindowEventHandler& windowEventHandler) = 0; @@ -64,5 +63,3 @@ namespace ramses_internal std::unique_ptr m_textureUploadingAdapter; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/RenderBufferGPUResource.h b/src/renderer/internal/RendererLib/PlatformBase/RenderBufferGPUResource.h similarity index 65% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/RenderBufferGPUResource.h rename to src/renderer/internal/RendererLib/PlatformBase/RenderBufferGPUResource.h index 68209c415..bb26753ba 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/RenderBufferGPUResource.h +++ b/src/renderer/internal/RendererLib/PlatformBase/RenderBufferGPUResource.h @@ -6,20 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERBUFFERGPURESOURCE_H -#define RAMSES_RENDERBUFFERGPURESOURCE_H +#pragma once -#include "Platform_Base/GpuResource.h" -#include "SceneAPI/TextureEnums.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" -namespace ramses_internal +namespace ramses::internal { class RenderBufferGPUResource : public GPUResource { public: - RenderBufferGPUResource(uint32_t gpuAddress, uint32_t width, uint32_t height, ERenderBufferType type, ETextureFormat format, uint32_t sampleCount, ERenderBufferAccessMode accessMode) + RenderBufferGPUResource(uint32_t gpuAddress, uint32_t width, uint32_t height, EPixelStorageFormat format, uint32_t sampleCount, ERenderBufferAccessMode accessMode) : GPUResource(gpuAddress, width * height * GetTexelSizeFromFormat(format) * std::max(1u, sampleCount)) - , m_type(type) , m_format(format) , m_sampleCount(sampleCount) , m_width(width) @@ -28,12 +26,7 @@ namespace ramses_internal { } - [[nodiscard]] ERenderBufferType getType() const - { - return m_type; - } - - [[nodiscard]] ETextureFormat getStorageFormat() const + [[nodiscard]] EPixelStorageFormat getStorageFormat() const { return m_format; } @@ -59,13 +52,10 @@ namespace ramses_internal } private: - const ERenderBufferType m_type; - const ETextureFormat m_format; - const uint32_t m_sampleCount; - const uint32_t m_width; - const uint32_t m_height; - const ERenderBufferAccessMode m_accessMode; + EPixelStorageFormat m_format; + uint32_t m_sampleCount; + uint32_t m_width; + uint32_t m_height; + ERenderBufferAccessMode m_accessMode; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/RenderTargetGpuResource.cpp b/src/renderer/internal/RendererLib/PlatformBase/RenderTargetGpuResource.cpp similarity index 86% rename from renderer/RendererLib/Platform_Base/src/RenderTargetGpuResource.cpp rename to src/renderer/internal/RendererLib/PlatformBase/RenderTargetGpuResource.cpp index d939639bc..132c21dfc 100644 --- a/renderer/RendererLib/Platform_Base/src/RenderTargetGpuResource.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/RenderTargetGpuResource.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/RenderTargetGpuResource.h" +#include "internal/RendererLib/PlatformBase/RenderTargetGpuResource.h" -namespace ramses_internal +namespace ramses::internal { RenderTargetGPUResource::RenderTargetGPUResource(uint32_t gpuAddress) : GPUResource(gpuAddress, 0u) diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/RenderTargetGpuResource.h b/src/renderer/internal/RendererLib/PlatformBase/RenderTargetGpuResource.h similarity index 80% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/RenderTargetGpuResource.h rename to src/renderer/internal/RendererLib/PlatformBase/RenderTargetGpuResource.h index 844743291..84c4af3d6 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/RenderTargetGpuResource.h +++ b/src/renderer/internal/RendererLib/PlatformBase/RenderTargetGpuResource.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERTARGETGPURESOURCE_H -#define RAMSES_RENDERTARGETGPURESOURCE_H +#pragma once -#include "Platform_Base/GpuResource.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" -namespace ramses_internal +namespace ramses::internal { class RenderTargetGPUResource : public GPUResource { @@ -19,5 +18,3 @@ namespace ramses_internal explicit RenderTargetGPUResource(uint32_t gpuAddress); }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/RendererLimits.cpp b/src/renderer/internal/RendererLib/PlatformBase/RendererLimits.cpp similarity index 91% rename from renderer/RendererLib/Platform_Base/src/RendererLimits.cpp rename to src/renderer/internal/RendererLib/PlatformBase/RendererLimits.cpp index 484a60653..8d50eaefa 100644 --- a/renderer/RendererLib/Platform_Base/src/RendererLimits.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/RendererLimits.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/RendererLimits.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/PlatformBase/RendererLimits.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { uint32_t RendererLimits::getMaximumTextureUnits() const { @@ -67,12 +67,12 @@ namespace ramses_internal m_maximumDrawBuffers = drawBuffers; } - bool RendererLimits::isTextureFormatAvailable(ETextureFormat format) const + bool RendererLimits::isTextureFormatAvailable(EPixelStorageFormat format) const { return m_availableTextureFormats.contains(format); } - void RendererLimits::addTextureFormat(ETextureFormat format) + void RendererLimits::addTextureFormat(EPixelStorageFormat format) { m_availableTextureFormats.put(format); } diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/RendererLimits.h b/src/renderer/internal/RendererLib/PlatformBase/RendererLimits.h similarity index 81% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/RendererLimits.h rename to src/renderer/internal/RendererLib/PlatformBase/RendererLimits.h index 9e57dd059..ac5094077 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/RendererLimits.h +++ b/src/renderer/internal/RendererLib/PlatformBase/RendererLimits.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERLIMITS_H -#define RAMSES_RENDERERLIMITS_H +#pragma once -#include "SceneAPI/TextureEnums.h" -#include "RendererAPI/Types.h" -#include "Collections/HashSet.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/RendererLib/Types.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" -namespace ramses_internal +namespace ramses::internal { class RendererLimits { @@ -36,8 +35,8 @@ namespace ramses_internal void setMaximumDrawBuffers(uint32_t drawBuffers); // Texture formats - [[nodiscard]] bool isTextureFormatAvailable(ETextureFormat format) const; - void addTextureFormat(ETextureFormat format); + [[nodiscard]] bool isTextureFormatAvailable(EPixelStorageFormat format) const; + void addTextureFormat(EPixelStorageFormat format); [[nodiscard]] bool isExternalTextureExtensionSupported() const; void setExternalTextureExtensionSupported(bool supported); @@ -52,8 +51,6 @@ namespace ramses_internal uint32_t m_maxViewportHeight = 16u; uint32_t m_maximumDrawBuffers = 4u; bool m_externalTextureExtensionSupported = false; - HashSet m_availableTextureFormats; + HashSet m_availableTextureFormats; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/ShaderGPUResource.h b/src/renderer/internal/RendererLib/PlatformBase/ShaderGPUResource.h similarity index 85% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/ShaderGPUResource.h rename to src/renderer/internal/RendererLib/PlatformBase/ShaderGPUResource.h index 545b8431f..8ce94a0e4 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/ShaderGPUResource.h +++ b/src/renderer/internal/RendererLib/PlatformBase/ShaderGPUResource.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHADERGPURESOURCE_H -#define RAMSES_SHADERGPURESOURCE_H +#pragma once -#include "Platform_Base/GpuResource.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" -namespace ramses_internal +namespace ramses::internal { class ShaderGPUResource : public GPUResource { @@ -22,5 +21,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/TextureUploadingAdapter_Base.cpp b/src/renderer/internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.cpp similarity index 71% rename from renderer/RendererLib/Platform_Base/src/TextureUploadingAdapter_Base.cpp rename to src/renderer/internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.cpp index 3534d0e47..d7e1bfde6 100644 --- a/renderer/RendererLib/Platform_Base/src/TextureUploadingAdapter_Base.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/TextureUploadingAdapter_Base.h" -#include "RendererAPI/IDevice.h" +#include "internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" -namespace ramses_internal +namespace ramses::internal { TextureUploadingAdapter_Base::TextureUploadingAdapter_Base(IDevice& device) : m_device(device) { } - void TextureUploadingAdapter_Base::uploadTexture2D(DeviceResourceHandle textureHandle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) + void TextureUploadingAdapter_Base::uploadTexture2D(DeviceResourceHandle textureHandle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) { m_device.uploadStreamTexture2D(textureHandle, width, height, format, data, swizzle); } diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/TextureUploadingAdapter_Base.h b/src/renderer/internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h similarity index 69% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/TextureUploadingAdapter_Base.h rename to src/renderer/internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h index ff5dfa07b..55a63a2c5 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/TextureUploadingAdapter_Base.h +++ b/src/renderer/internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTUREUPLOADINGADAPTER_BASE_H -#define RAMSES_TEXTUREUPLOADINGADAPTER_BASE_H +#pragma once -#include "RendererAPI/ITextureUploadingAdapter.h" -#include "Resource/TextureMetaInfo.h" +#include "internal/RendererLib/PlatformInterface/ITextureUploadingAdapter.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" -namespace ramses_internal +namespace ramses::internal { class IDevice; @@ -20,11 +19,9 @@ namespace ramses_internal { public: explicit TextureUploadingAdapter_Base(IDevice& device); - void uploadTexture2D(DeviceResourceHandle textureHandle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) override; + void uploadTexture2D(DeviceResourceHandle textureHandle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) override; protected: IDevice& m_device; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/VertexArrayGPUResource.h b/src/renderer/internal/RendererLib/PlatformBase/VertexArrayGPUResource.h similarity index 86% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/VertexArrayGPUResource.h rename to src/renderer/internal/RendererLib/PlatformBase/VertexArrayGPUResource.h index 6d596edb4..10804f183 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/VertexArrayGPUResource.h +++ b/src/renderer/internal/RendererLib/PlatformBase/VertexArrayGPUResource.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_VERTEXARRAYGPURESOURCE_H -#define RAMSES_VERTEXARRAYGPURESOURCE_H +#pragma once -#include "Platform_Base/GpuResource.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" -namespace ramses_internal +namespace ramses::internal { class VertexArrayGPUResource : public GPUResource { @@ -31,5 +30,3 @@ namespace ramses_internal DeviceResourceHandle m_indexBufferHandle; }; } - -#endif diff --git a/renderer/RendererLib/Platform_Base/src/Window_Base.cpp b/src/renderer/internal/RendererLib/PlatformBase/Window_Base.cpp similarity index 81% rename from renderer/RendererLib/Platform_Base/src/Window_Base.cpp rename to src/renderer/internal/RendererLib/PlatformBase/Window_Base.cpp index 7a6a12264..0997a8c3a 100644 --- a/renderer/RendererLib/Platform_Base/src/Window_Base.cpp +++ b/src/renderer/internal/RendererLib/PlatformBase/Window_Base.cpp @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Platform_Base/Window_Base.h" -#include "Collections/StringOutputStream.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "Utils/LogMacros.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/PlatformBase/Window_Base.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { Window_Base::Window_Base(const DisplayConfig& displayConfig, IWindowEventHandler& eventHandler, uint32_t id) : m_windowName((StringOutputStream() << "RAMSES Renderer " << id).release()) , m_eventHandler(eventHandler) , m_fullscreen(displayConfig.getFullscreenState()) - , m_borderless(displayConfig.getBorderlessState()) , m_msaaSampleCount(1) , m_width(displayConfig.getDesiredWindowWidth()) , m_height(displayConfig.getDesiredWindowHeight()) @@ -60,7 +59,7 @@ namespace ramses_internal return m_waylandIviSurfaceID; } - bool Window_Base::setExternallyOwnedWindowSize(uint32_t, uint32_t) + bool Window_Base::setExternallyOwnedWindowSize(uint32_t /*width*/, uint32_t /*height*/) { LOG_ERROR(CONTEXT_RENDERER, "Window_Base::setExternallyOwnedWindowSize: platform does not support externally owned windows!"); return false; @@ -78,7 +77,7 @@ namespace ramses_internal float Window_Base::getAspectRatio() const { - return static_cast(m_width) / m_height; + return static_cast(m_width) / static_cast(m_height); } int32_t Window_Base::getPosX() const diff --git a/renderer/RendererLib/Platform_Base/include/Platform_Base/Window_Base.h b/src/renderer/internal/RendererLib/PlatformBase/Window_Base.h similarity index 92% rename from renderer/RendererLib/Platform_Base/include/Platform_Base/Window_Base.h rename to src/renderer/internal/RendererLib/PlatformBase/Window_Base.h index bace8ad56..2858a2d16 100644 --- a/renderer/RendererLib/Platform_Base/include/Platform_Base/Window_Base.h +++ b/src/renderer/internal/RendererLib/PlatformBase/Window_Base.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOW_BASE_H -#define RAMSES_WINDOW_BASE_H +#pragma once -#include "RendererAPI/IWindow.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" -namespace ramses_internal +namespace ramses::internal { class DisplayConfig; class IWindowEventHandler; @@ -43,7 +42,6 @@ namespace ramses_internal std::string m_windowName; IWindowEventHandler& m_eventHandler; const bool m_fullscreen; - const bool m_borderless; uint32_t m_msaaSampleCount; uint32_t m_width; @@ -55,5 +53,3 @@ namespace ramses_internal const bool m_resizable; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IBinaryShaderCache.h b/src/renderer/internal/RendererLib/PlatformInterface/IBinaryShaderCache.h similarity index 76% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IBinaryShaderCache.h rename to src/renderer/internal/RendererLib/PlatformInterface/IBinaryShaderCache.h index b0814f9da..9fd07a830 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IBinaryShaderCache.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IBinaryShaderCache.h @@ -6,34 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IBINARYSHADERCACHE_H -#define RAMSES_IBINARYSHADERCACHE_H +#pragma once -#include "SceneAPI/ResourceContentHash.h" -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" #include -namespace ramses_internal +namespace ramses::internal { class IBinaryShaderCache { public: - virtual ~IBinaryShaderCache() {}; + virtual ~IBinaryShaderCache() = default; virtual void deviceSupportsBinaryShaderFormats(const std::vector& supportedFormats) = 0; [[nodiscard]] virtual bool hasBinaryShader(ResourceContentHash effectHash) const = 0; [[nodiscard]] virtual uint32_t getBinaryShaderSize(ResourceContentHash effectHash) const = 0; [[nodiscard]] virtual BinaryShaderFormatID getBinaryShaderFormat(ResourceContentHash effectHash) const = 0; - virtual void getBinaryShaderData(ResourceContentHash effectHash, uint8_t* buffer, uint32_t bufferSize) const = 0; + virtual void getBinaryShaderData(ResourceContentHash effectHash, std::byte* buffer, uint32_t bufferSize) const = 0; [[nodiscard]] virtual bool shouldBinaryShaderBeCached(ResourceContentHash effectHash, SceneId sceneId) const = 0; - virtual void storeBinaryShader(ResourceContentHash effectHash, SceneId sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) = 0; + virtual void storeBinaryShader(ResourceContentHash effectHash, SceneId sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) = 0; virtual void binaryShaderUploaded(ResourceContentHash effectHash, bool success) const = 0; virtual std::once_flag& binaryShaderFormatsReported() = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IContext.h b/src/renderer/internal/RendererLib/PlatformInterface/IContext.h similarity index 86% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IContext.h rename to src/renderer/internal/RendererLib/PlatformInterface/IContext.h index 4909fb92b..0e8ef22ec 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IContext.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IContext.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ICONTEXT_H -#define RAMSES_ICONTEXT_H +#pragma once -#include "Types.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class DeviceResourceMapper; class IContext { public: - virtual ~IContext(){} + virtual ~IContext() = default; virtual bool swapBuffers() = 0; virtual bool enable() = 0; @@ -29,5 +28,3 @@ namespace ramses_internal virtual void* getProcAddress(const char* name) const = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IDevice.h b/src/renderer/internal/RendererLib/PlatformInterface/IDevice.h similarity index 75% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IDevice.h rename to src/renderer/internal/RendererLib/PlatformInterface/IDevice.h index 471b6b8de..e2c5cc1d8 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IDevice.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IDevice.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IDEVICE_H -#define RAMSES_IDEVICE_H - -#include "RendererAPI/Types.h" -#include "SceneAPI/TextureEnums.h" -#include "SceneAPI/RenderState.h" -#include "SceneAPI/EDataType.h" -#include "Resource/TextureMetaInfo.h" -#include "Platform_Base/GpuResource.h" +#pragma once + +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" +#include "internal/RendererLib/PlatformBase/GpuResource.h" #include #include -namespace ramses_internal +namespace ramses::internal { class EffectResource; @@ -28,23 +27,24 @@ namespace ramses_internal class IDevice { public: - virtual ~IDevice() {} + virtual ~IDevice() = default; // data - virtual void setConstant(DataFieldHandle field, uint32_t count, const float* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) = 0; - virtual void setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const float* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec2* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec3* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::vec4* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const bool* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const int32_t* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec2* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec3* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::ivec4* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat2* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat3* value) = 0; + virtual bool setConstant(DataFieldHandle field, uint32_t count, const glm::mat4* value) = 0; //draw calls - virtual void clear (uint32_t clearFlags) = 0; + virtual void clear (ClearFlags clearFlags) = 0; virtual void drawIndexedTriangles(int32_t startOffset, int32_t elementCount, uint32_t instanceCount) = 0; virtual void drawTriangles (int32_t startOffset, int32_t elementCount, uint32_t instanceCount) = 0; virtual void flush () = 0; @@ -68,11 +68,11 @@ namespace ramses_internal // resources virtual DeviceResourceHandle allocateVertexBuffer (uint32_t totalSizeInBytes) = 0; - virtual void uploadVertexBufferData (DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) = 0; + virtual void uploadVertexBufferData (DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) = 0; virtual void deleteVertexBuffer (DeviceResourceHandle handle) = 0; virtual DeviceResourceHandle allocateIndexBuffer (EDataType dataType, uint32_t sizeInBytes) = 0; - virtual void uploadIndexBufferData (DeviceResourceHandle handle, const Byte* data, uint32_t dataSize) = 0; + virtual void uploadIndexBufferData (DeviceResourceHandle handle, const std::byte* data, uint32_t dataSize) = 0; virtual void deleteIndexBuffer (DeviceResourceHandle handle) = 0; virtual DeviceResourceHandle allocateVertexArray (const VertexArrayInfo& vertexArrayInfo) = 0; @@ -81,27 +81,27 @@ namespace ramses_internal virtual std::unique_ptr uploadShader (const EffectResource& effect) = 0; virtual DeviceResourceHandle registerShader (std::unique_ptr shaderResource) = 0; - virtual DeviceResourceHandle uploadBinaryShader (const EffectResource& effect, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) = 0; - virtual bool getBinaryShader (DeviceResourceHandle handle, UInt8Vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) = 0; + virtual DeviceResourceHandle uploadBinaryShader (const EffectResource& effect, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat) = 0; + virtual bool getBinaryShader (DeviceResourceHandle handle, std::vector& binaryShader, BinaryShaderFormatID& binaryShaderFormat) = 0; virtual void deleteShader (DeviceResourceHandle handle) = 0; virtual void activateShader (DeviceResourceHandle handle) = 0; - virtual DeviceResourceHandle allocateTexture2D (uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) = 0; - virtual DeviceResourceHandle allocateTexture3D (uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes) = 0; - virtual DeviceResourceHandle allocateTextureCube (uint32_t faceSize, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) = 0; + virtual DeviceResourceHandle allocateTexture2D (uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) = 0; + virtual DeviceResourceHandle allocateTexture3D (uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes) = 0; + virtual DeviceResourceHandle allocateTextureCube (uint32_t faceSize, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes) = 0; virtual DeviceResourceHandle allocateExternalTexture () = 0; [[nodiscard]] virtual DeviceResourceHandle getEmptyExternalTexture () const = 0; virtual void bindTexture (DeviceResourceHandle handle) = 0; virtual void generateMipmaps (DeviceResourceHandle handle) = 0; - virtual void uploadTextureData (DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const Byte* data, uint32_t dataSize) = 0; - virtual DeviceResourceHandle uploadStreamTexture2D (DeviceResourceHandle handle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) = 0; + virtual void uploadTextureData (DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const std::byte* data, uint32_t dataSize, uint32_t stride) = 0; + virtual DeviceResourceHandle uploadStreamTexture2D (DeviceResourceHandle handle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) = 0; virtual void deleteTexture (DeviceResourceHandle handle) = 0; virtual void activateTexture (DeviceResourceHandle handle, DataFieldHandle field) = 0; - [[nodiscard]] virtual int getTextureAddress (DeviceResourceHandle handle) const = 0; + [[nodiscard]] virtual uint32_t getTextureAddress (DeviceResourceHandle handle) const = 0; // Render buffers/targets - virtual DeviceResourceHandle uploadRenderBuffer (uint32_t width, uint32_t height, ERenderBufferType type, ETextureFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) = 0; + virtual DeviceResourceHandle uploadRenderBuffer (uint32_t width, uint32_t height, EPixelStorageFormat format, ERenderBufferAccessMode accessMode, uint32_t sampleCount) = 0; virtual void deleteRenderBuffer (DeviceResourceHandle handle) = 0; virtual DeviceResourceHandle uploadDmaRenderBuffer (uint32_t width, uint32_t height, DmaBufferFourccFormat fourccFormat, DmaBufferUsageFlags usageFlags, DmaBufferModifiers modifiers) = 0; @@ -138,5 +138,3 @@ namespace ramses_internal [[nodiscard]] virtual uint32_t getGPUHandle(DeviceResourceHandle deviceHandle) const = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IDeviceExtension.h b/src/renderer/internal/RendererLib/PlatformInterface/IDeviceExtension.h similarity index 89% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IDeviceExtension.h rename to src/renderer/internal/RendererLib/PlatformInterface/IDeviceExtension.h index 57204c9e2..b094e128f 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IDeviceExtension.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IDeviceExtension.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IDEVICEEXTENSION_H -#define RAMSES_IDEVICEEXTENSION_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class IDeviceExtension { @@ -24,5 +23,3 @@ namespace ramses_internal virtual void destroyDmaRenderBuffer (DeviceResourceHandle handle) = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IDisplayController.h b/src/renderer/internal/RendererLib/PlatformInterface/IDisplayController.h similarity index 75% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IDisplayController.h rename to src/renderer/internal/RendererLib/PlatformInterface/IDisplayController.h index dd2a546ba..8f0eb85fa 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IDisplayController.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IDisplayController.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IDISPLAYCONTROLLER_H -#define RAMSES_IDISPLAYCONTROLLER_H +#pragma once -#include "RendererAPI/Types.h" -#include "RendererAPI/SceneRenderExecutionIterator.h" -#include "DataTypesImpl.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/SceneRenderExecutionIterator.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +namespace ramses::internal { class IRenderBackend; class IEmbeddedCompositingManager; @@ -25,14 +25,14 @@ namespace ramses_internal class IDisplayController { public: - virtual ~IDisplayController() {}; + virtual ~IDisplayController() = default; virtual void handleWindowEvents() = 0; [[nodiscard]] virtual bool canRenderNewFrame() const = 0; virtual void enableContext() = 0; virtual void swapBuffers() = 0; - virtual SceneRenderExecutionIterator renderScene(const RendererCachedScene& scene, RenderingContext& renderContext, const FrameTimer* frameTimer = nullptr) = 0; - virtual void clearBuffer(DeviceResourceHandle buffer, uint32_t clearFlags, const glm::vec4& clearColor) = 0; + virtual SceneRenderExecutionIterator renderScene(const RendererCachedScene& scene, RenderingContext& renderContext, const FrameTimer* frameTimer) = 0; + virtual void clearBuffer(DeviceResourceHandle buffer, ClearFlags clearFlags, const glm::vec4& clearColor) = 0; [[nodiscard]] virtual DeviceResourceHandle getDisplayBuffer() const = 0; [[nodiscard]] virtual IRenderBackend& getRenderBackend() const = 0; @@ -40,10 +40,8 @@ namespace ramses_internal [[nodiscard]] virtual uint32_t getDisplayWidth() const = 0; [[nodiscard]] virtual uint32_t getDisplayHeight() const = 0; - virtual void readPixels(DeviceResourceHandle renderTargetHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, std::vector& dataOut) = 0; + virtual void readPixels(DeviceResourceHandle renderTargetHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, std::vector& dataOut) = 0; virtual void validateRenderingStatusHealthy() const = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IEmbeddedCompositingManager.h b/src/renderer/internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h similarity index 88% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IEmbeddedCompositingManager.h rename to src/renderer/internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h index 71d0929cc..214207e2b 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IEmbeddedCompositingManager.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IEMBEDDEDCOMPOSITINGMANAGER_H -#define RAMSES_IEMBEDDEDCOMPOSITINGMANAGER_H +#pragma once -#include "Types.h" +#include "internal/RendererLib/Types.h" #include -namespace ramses_internal +namespace ramses::internal { using StreamSourceUpdates = std::vector< std::pair >; class IEmbeddedCompositingManager { public: - virtual ~IEmbeddedCompositingManager(){} + virtual ~IEmbeddedCompositingManager() = default; virtual void refStream(WaylandIviSurfaceId source) = 0; virtual void unrefStream(WaylandIviSurfaceId source) = 0; @@ -34,5 +33,3 @@ namespace ramses_internal [[nodiscard]] virtual bool hasRealCompositor() const = 0; //TODO Mohamed: remove this as soon as EC dummy is removed }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IEmbeddedCompositor.h b/src/renderer/internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h similarity index 92% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IEmbeddedCompositor.h rename to src/renderer/internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h index 195a30848..2ee2779c5 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IEmbeddedCompositor.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IEMBEDDEDCOMPOSITOR_H -#define RAMSES_IEMBEDDEDCOMPOSITOR_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class RendererLogContext; class ITextureUploadingAdapter; + class StringOutputStream; class IEmbeddedCompositor { @@ -36,9 +36,8 @@ namespace ramses_internal [[nodiscard]] virtual bool hasSurfaceForStreamTexture(WaylandIviSurfaceId streamTextureSourceId) const = 0; [[nodiscard]] virtual std::string getTitleOfWaylandIviSurface(WaylandIviSurfaceId waylandSurfaceId) const = 0; virtual void logInfos(RendererLogContext& context) const = 0; + virtual void logPeriodicInfo(StringOutputStream& sos) const = 0; [[nodiscard]] virtual bool isRealCompositor() const = 0; //TODO Mohamed: remove this when dummy EC is removed }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IPlatform.h b/src/renderer/internal/RendererLib/PlatformInterface/IPlatform.h similarity index 93% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IPlatform.h rename to src/renderer/internal/RendererLib/PlatformInterface/IPlatform.h index 78b9cdf46..c3574c925 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IPlatform.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IPlatform.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IPLATFORM_H -#define RAMSES_IPLATFORM_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IRenderBackend; class DisplayConfig; @@ -30,5 +29,3 @@ namespace ramses_internal virtual ~IPlatform() = default; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IPlatformFactory.h b/src/renderer/internal/RendererLib/PlatformInterface/IPlatformFactory.h similarity index 85% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IPlatformFactory.h rename to src/renderer/internal/RendererLib/PlatformInterface/IPlatformFactory.h index 88c3ca5ea..803f410d1 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IPlatformFactory.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IPlatformFactory.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IPLATFORMFACTORY_H -#define RAMSES_IPLATFORMFACTORY_H +#pragma once -#include "RendererAPI/IPlatform.h" +#include "internal/RendererLib/PlatformInterface/IPlatform.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererConfig; class DisplayConfig; @@ -24,5 +23,3 @@ namespace ramses_internal virtual std::unique_ptr createPlatform(const RendererConfig& rendererConfig, const DisplayConfig& displayConfig) = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IRenderBackend.h b/src/renderer/internal/RendererLib/PlatformInterface/IRenderBackend.h similarity index 92% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IRenderBackend.h rename to src/renderer/internal/RendererLib/PlatformInterface/IRenderBackend.h index 8f16b6ecb..6eb8f0ccb 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IRenderBackend.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IRenderBackend.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERBACKEND_H -#define RAMSES_IRENDERBACKEND_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IWindow; class IContext; @@ -30,5 +29,3 @@ namespace ramses_internal [[nodiscard]] virtual ITextureUploadingAdapter& getTextureUploadingAdapter() const = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IResourceUploadRenderBackend.h b/src/renderer/internal/RendererLib/PlatformInterface/IResourceUploadRenderBackend.h similarity index 82% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IResourceUploadRenderBackend.h rename to src/renderer/internal/RendererLib/PlatformInterface/IResourceUploadRenderBackend.h index a40e771e8..8abf12b77 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IResourceUploadRenderBackend.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IResourceUploadRenderBackend.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRESOURCEUPLOADRENDERBACKEND_H -#define RAMSES_IRESOURCEUPLOADRENDERBACKEND_H +#pragma once -namespace ramses_internal +namespace ramses::internal { class IDevice; class IContext; @@ -18,11 +17,9 @@ namespace ramses_internal class IResourceUploadRenderBackend { public: - virtual ~IResourceUploadRenderBackend() {}; + virtual ~IResourceUploadRenderBackend() = default; [[nodiscard]] virtual IContext& getContext() const = 0; [[nodiscard]] virtual IDevice& getDevice() const = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/ISystemCompositorController.h b/src/renderer/internal/RendererLib/PlatformInterface/ISystemCompositorController.h similarity index 93% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/ISystemCompositorController.h rename to src/renderer/internal/RendererLib/PlatformInterface/ISystemCompositorController.h index 5a9049a5b..ed2df8f64 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/ISystemCompositorController.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/ISystemCompositorController.h @@ -6,22 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ISYSTEMCOMPOSITORCONTROLLER_H -#define RAMSES_ISYSTEMCOMPOSITORCONTROLLER_H +#pragma once -#include "RendererAPI/Types.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/RendererLib/Types.h" +#include #include -namespace ramses_internal +namespace ramses::internal { class ISystemCompositorController { public: - virtual ~ISystemCompositorController() - { - } + virtual ~ISystemCompositorController() = default; /** * Update internal state and do event processing. @@ -110,5 +107,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/ITextureUploadingAdapter.h b/src/renderer/internal/RendererLib/PlatformInterface/ITextureUploadingAdapter.h similarity index 57% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/ITextureUploadingAdapter.h rename to src/renderer/internal/RendererLib/PlatformInterface/ITextureUploadingAdapter.h index f0391017d..aedb5bfad 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/ITextureUploadingAdapter.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/ITextureUploadingAdapter.h @@ -6,22 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ITEXTUREUPLOADINGADAPTER_H -#define RAMSES_ITEXTUREUPLOADINGADAPTER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/TextureEnums.h" -#include "RendererAPI/Types.h" -#include "Resource/TextureMetaInfo.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" -namespace ramses_internal +#include + +namespace ramses::internal { class ITextureUploadingAdapter { public: - virtual ~ITextureUploadingAdapter() {} - virtual void uploadTexture2D(DeviceResourceHandle textureHandle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle) = 0; + virtual ~ITextureUploadingAdapter() = default; + virtual void uploadTexture2D(DeviceResourceHandle textureHandle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle) = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IWindow.h b/src/renderer/internal/RendererLib/PlatformInterface/IWindow.h similarity index 91% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IWindow.h rename to src/renderer/internal/RendererLib/PlatformInterface/IWindow.h index 6ff1e409a..dc7d68af5 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IWindow.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IWindow.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWINDOW_H -#define RAMSES_IWINDOW_H +#pragma once -#include "Types.h" +#include "internal/RendererLib/Types.h" #include #include -namespace ramses_internal +namespace ramses::internal { class IWindow { public: - virtual ~IWindow(){} + virtual ~IWindow() = default; virtual bool init() = 0; virtual bool setFullscreen(bool fullscreen) = 0; @@ -38,5 +37,3 @@ namespace ramses_internal [[nodiscard]] virtual WaylandIviSurfaceId getWaylandIviSurfaceID() const = 0; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/IWindowEventHandler.h b/src/renderer/internal/RendererLib/PlatformInterface/IWindowEventHandler.h similarity index 60% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/IWindowEventHandler.h rename to src/renderer/internal/RendererLib/PlatformInterface/IWindowEventHandler.h index 388fae6a8..9f921dd8b 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/IWindowEventHandler.h +++ b/src/renderer/internal/RendererLib/PlatformInterface/IWindowEventHandler.h @@ -6,28 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IWINDOWEVENTHANDLER_H -#define RAMSES_IWINDOWEVENTHANDLER_H +#pragma once -#include "RendererLib/EMouseEventType.h" -#include "RendererLib/EKeyEventType.h" -#include "RendererLib/EKeyCode.h" +#include "internal/RendererLib/Enums/EMouseEvent.h" +#include "internal/RendererLib/Enums/EKeyEvent.h" +#include "internal/RendererLib/Enums/EKeyCode.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" -namespace ramses_internal +namespace ramses::internal { class IWindow; class IWindowEventHandler { public: - virtual ~IWindowEventHandler() {}; + virtual ~IWindowEventHandler() = default; - virtual void onKeyEvent(EKeyEventType event, uint32_t modifiers, EKeyCode keyCode) = 0; - virtual void onMouseEvent(EMouseEventType event, int32_t posX, int32_t posY) = 0; + virtual void onKeyEvent(EKeyEvent event, KeyModifiers modifiers, ramses::EKeyCode keyCode) = 0; + virtual void onMouseEvent(EMouseEvent event, int32_t posX, int32_t posY) = 0; virtual void onClose() = 0; virtual void onResize(uint32_t width, uint32_t height) = 0; virtual void onWindowMove(int32_t posX, int32_t posY) = 0; }; } - -#endif diff --git a/src/renderer/internal/RendererLib/RamshCommands/AssignScene.cpp b/src/renderer/internal/RendererLib/RamshCommands/AssignScene.cpp new file mode 100644 index 000000000..6f15ad247 --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/AssignScene.cpp @@ -0,0 +1,100 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RamshCommands/AssignScene.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal +{ + AssignScene::AssignScene(RendererCommandBuffer& rendererCommandBuffer) + : m_rendererCommandBuffer(rendererCommandBuffer) + { + description = "Usage: [-ob bufferID] [-display displayID] sceneId - assign scene to display / offscreen buffer"; + registerKeyword("assign"); + } + + bool AssignScene::executeInput(const std::vector& input) + { + std::vector positionals; + ramses::internal::RendererCommand::SetSceneMapping cmdMap{}; + ramses::internal::RendererCommand::SetSceneDisplayBufferAssignment cmdOb{}; + + enum class EOption + { + None, + Display, + OffscreenBuffer, + }; + + EOption lastOption = EOption::None; + + for (const auto& arg : input) + { + if (arg == "-displayId") + { + lastOption = EOption::Display; + } + else if (arg == "-ob") + { + lastOption = EOption::OffscreenBuffer; + } + else + { + switch( lastOption ) + { + case EOption::Display: + ArgumentConverter::tryConvert(arg, cmdMap.display.asMemoryHandleReference()); + lastOption = EOption::None; + break; + case EOption::OffscreenBuffer: + ArgumentConverter::tryConvert(arg, cmdOb.buffer.asMemoryHandleReference()); + lastOption = EOption::None; + break; + case EOption::None: + if (!contains_c(m_keywords, arg)) + { + positionals.push_back(arg); + } + break; + } + } + } + + if (positionals.size() != 1) + { + LOG_ERROR_P(CONTEXT_RAMSH, "None or too many arguments provided: {}", positionals.size()); + return false; + } + + SceneId sceneId; + ArgumentConverter::tryConvert(positionals[0], sceneId.getReference()); + if (!sceneId.isValid()) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Invalid SceneId: {}", positionals[0]); + return false; + } + cmdMap.scene = sceneId; + cmdOb.scene = sceneId; + if (!cmdMap.display.isValid() && !cmdOb.buffer.isValid()) + { + LOG_ERROR(CONTEXT_RAMSH, "Expected '-ob ID' or '-displayId ID' option"); + return false; + } + if (cmdMap.display.isValid()) + { + m_rendererCommandBuffer.enqueueCommand(std::move(cmdMap)); + } + if (cmdOb.buffer.isValid()) + { + m_rendererCommandBuffer.enqueueCommand(std::move(cmdOb)); + } + + return true; + } +} diff --git a/client/logic/lib/internals/ErrorReporting.h b/src/renderer/internal/RendererLib/RamshCommands/AssignScene.h similarity index 58% rename from client/logic/lib/internals/ErrorReporting.h rename to src/renderer/internal/RendererLib/RamshCommands/AssignScene.h index 71bd1a82f..df5f787f9 100644 --- a/client/logic/lib/internals/ErrorReporting.h +++ b/src/renderer/internal/RendererLib/RamshCommands/AssignScene.h @@ -1,5 +1,5 @@ // ------------------------------------------------------------------------- -// Copyright (C) 2020 BMW AG +// Copyright (C) 2023 BMW AG // ------------------------------------------------------------------------- // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,22 +8,20 @@ #pragma once -#include -#include -#include "ramses-logic/ErrorData.h" +#include "internal/Ramsh/RamshCommandArguments.h" namespace ramses::internal { - class ErrorReporting + class RendererCommandBuffer; + + class AssignScene : public RamshCommand { public: - - void clear(); - void add(std::string errorMessage, const LogicObject* logicObject, EErrorType type); - - [[nodiscard]] const std::vector& getErrors() const; + explicit AssignScene(RendererCommandBuffer& rendererCommandBuffer); + bool executeInput(const std::vector& input) override; private: - std::vector m_errors; + RendererCommandBuffer& m_rendererCommandBuffer; }; } + diff --git a/src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.cpp b/src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.cpp new file mode 100644 index 000000000..4af50774f --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.cpp @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal +{ + CreateOffscreenBuffer::CreateOffscreenBuffer(RendererCommandBuffer& rendererCommandBuffer) + : m_rendererCommandBuffer(rendererCommandBuffer) + { + description = "Usage: displayId bufferId width height - create an offscreen buffer"; + registerKeyword("obCreate"); + } + + bool CreateOffscreenBuffer::executeInput(const std::vector& input) + { + if (input.size() != 5) + { + LOG_ERROR_P(CONTEXT_RAMSH, "None or too many arguments provided: {}", input.size() - 1); + return false; + } + ramses::internal::RendererCommand::CreateOffscreenBuffer cmd{}; + cmd.depthStencilBufferType = EDepthBufferType::DepthStencil; + cmd.interruptible = false; + cmd.sampleCount = 0; + if (!ArgumentConverter::tryConvert(input[1], cmd.display.asMemoryHandleReference())) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Invalid display id: {}", input[1]); + return false; + } + if (!ArgumentConverter::tryConvert(input[2], cmd.offscreenBuffer.asMemoryHandleReference())) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Invalid buffer id: {}", input[2]); + return false; + } + + ArgumentConverter::tryConvert(input[3], cmd.width); + ArgumentConverter::tryConvert(input[4], cmd.height); + if (cmd.width == 0) + { + LOG_ERROR(CONTEXT_RAMSH, "Invalid width: must be larger than 0"); + return false; + } + if (cmd.height == 0) + { + LOG_ERROR(CONTEXT_RAMSH, "Invalid height: must be larger than 0"); + return false; + } + m_rendererCommandBuffer.enqueueCommand(std::move(cmd)); + return true; + } +} diff --git a/src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h b/src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h new file mode 100644 index 000000000..e3543c7d7 --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Ramsh/RamshCommandArguments.h" + +namespace ramses::internal +{ + class RendererCommandBuffer; + + class CreateOffscreenBuffer : public RamshCommand + { + public: + explicit CreateOffscreenBuffer(RendererCommandBuffer& rendererCommandBuffer); + bool executeInput(const std::vector& input) override; + + private: + RendererCommandBuffer& m_rendererCommandBuffer; + }; +} + diff --git a/src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.cpp b/src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.cpp new file mode 100644 index 000000000..d8cc5d047 --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.cpp @@ -0,0 +1,48 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RamshCommands/LinkUnlink.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal +{ + LinkBuffer::LinkBuffer(RendererCommandBuffer& rendererCommandBuffer) + : m_rendererCommandBuffer(rendererCommandBuffer) + { + description = "Links an offscreen buffer to a scene"; + registerKeyword("link"); + getArgument<0>().setDescription("scene id"); + getArgument<1>().setDescription("data slot"); + getArgument<2>().setDescription("buffer id"); + } + + bool LinkBuffer::execute(uint64_t& sceneId, uint32_t& dataSlot, uint32_t& bufferId) const + { + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::LinkOffscreenBuffer{ OffscreenBufferHandle{bufferId}, SceneId{sceneId}, DataSlotId{dataSlot} }); + return true; + } +} + +namespace ramses::internal +{ + UnlinkBuffer::UnlinkBuffer(RendererCommandBuffer& rendererCommandBuffer) + : m_rendererCommandBuffer(rendererCommandBuffer) + { + description = "Unlinks data from a scene"; + registerKeyword("unlink"); + getArgument<0>().setDescription("scene id"); + getArgument<1>().setDescription("data slot"); + } + + bool UnlinkBuffer::execute(uint64_t& sceneId, uint32_t& dataSlot) const + { + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::UnlinkData{ SceneId{sceneId}, DataSlotId{dataSlot} }); + return true; + } +} diff --git a/src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.h b/src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.h new file mode 100644 index 000000000..c0328dde3 --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/LinkUnlink.h @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Ramsh/RamshCommandArguments.h" + +namespace ramses::internal +{ + class RendererCommandBuffer; + + class LinkBuffer : public RamshCommandArgs + { + public: + explicit LinkBuffer(RendererCommandBuffer& rendererCommandBuffer); + bool execute(uint64_t& sceneId, uint32_t& dataSlot, uint32_t& bufferId) const override; + + private: + RendererCommandBuffer& m_rendererCommandBuffer; + }; + + class UnlinkBuffer : public RamshCommandArgs + { + public: + explicit UnlinkBuffer(RendererCommandBuffer& rendererCommandBuffer); + bool execute(uint64_t& sceneId, uint32_t& dataSlot) const override; + + private: + RendererCommandBuffer& m_rendererCommandBuffer; + }; +} + diff --git a/renderer/RendererLib/RendererCommands/src/LogRendererInfo.cpp b/src/renderer/internal/RendererLib/RamshCommands/LogRendererInfo.cpp similarity index 80% rename from renderer/RendererLib/RendererCommands/src/LogRendererInfo.cpp rename to src/renderer/internal/RendererLib/RamshCommands/LogRendererInfo.cpp index 0b7b97941..545c4219a 100644 --- a/renderer/RendererLib/RendererCommands/src/LogRendererInfo.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/LogRendererInfo.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- -#include "RendererCommands/LogRendererInfo.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RamshCommands/LogRendererInfo.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { LogRendererInfo::LogRendererInfo(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -33,15 +33,15 @@ namespace ramses_internal bool LogRendererInfo::execute(std::string& topic, bool& verbose, MemoryHandle& nodeHandleFilter) const { - const ERendererLogTopic etopic = GetRendererTopic(topic); - if (etopic == ERendererLogTopic::COUNT) + const auto etopic = GetRendererTopic(topic); + if (!etopic.has_value()) return false; - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::LogInfo{ etopic, verbose, NodeHandle(nodeHandleFilter) }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::LogInfo{ *etopic, verbose, NodeHandle(nodeHandleFilter) }); return true; } - ERendererLogTopic LogRendererInfo::GetRendererTopic(const std::string& topicName) + std::optional LogRendererInfo::GetRendererTopic(const std::string& topicName) { if (topicName == "display") return ERendererLogTopic::Displays; @@ -60,6 +60,6 @@ namespace ramses_internal if (topicName == "all") return ERendererLogTopic::All; - return ERendererLogTopic::COUNT; + return std::nullopt; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/LogRendererInfo.h b/src/renderer/internal/RendererLib/RamshCommands/LogRendererInfo.h similarity index 72% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/LogRendererInfo.h rename to src/renderer/internal/RendererLib/RamshCommands/LogRendererInfo.h index 656c61521..41246641b 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/LogRendererInfo.h +++ b/src/renderer/internal/RendererLib/RamshCommands/LogRendererInfo.h @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LOGRENDERERINFO_H -#define RAMSES_LOGRENDERERINFO_H +#pragma once -#include "Ramsh/RamshCommand.h" -#include "RendererLib/RendererLogger.h" -#include "Ramsh/RamshCommandArguments.h" -#include "SceneAPI/Handles.h" +#include "internal/Ramsh/RamshCommand.h" +#include "internal/RendererLib/RendererLogger.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" #include +#include -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; @@ -29,8 +29,6 @@ namespace ramses_internal private: RendererCommandBuffer& m_rendererCommandBuffer; - static ERendererLogTopic GetRendererTopic(const std::string& topicName); + static std::optional GetRendererTopic(const std::string& topicName); }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/PrintStatistics.cpp b/src/renderer/internal/RendererLib/RamshCommands/PrintStatistics.cpp similarity index 78% rename from renderer/RendererLib/RendererCommands/src/PrintStatistics.cpp rename to src/renderer/internal/RendererLib/RamshCommands/PrintStatistics.cpp index 168ef73a6..17b461710 100644 --- a/renderer/RendererLib/RendererCommands/src/PrintStatistics.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/PrintStatistics.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- -#include "RendererCommands/PrintStatistics.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RamshCommands/PrintStatistics.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { PrintStatistics::PrintStatistics(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -22,7 +22,7 @@ namespace ramses_internal bool PrintStatistics::executeInput(const std::vector& /*input*/) { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::LogStatistics{}); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::LogStatistics{}); return true; } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/PrintStatistics.h b/src/renderer/internal/RendererLib/RamshCommands/PrintStatistics.h similarity index 82% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/PrintStatistics.h rename to src/renderer/internal/RendererLib/RamshCommands/PrintStatistics.h index 535f7e7ab..13f469722 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/PrintStatistics.h +++ b/src/renderer/internal/RendererLib/RamshCommands/PrintStatistics.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PRINTSTATISTICS_H -#define RAMSES_PRINTSTATISTICS_H +#pragma once -#include "Ramsh/RamshCommand.h" -#include "RendererLib/RendererStatistics.h" +#include "internal/Ramsh/RamshCommand.h" +#include "internal/RendererLib/RendererStatistics.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; @@ -26,5 +25,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/Screenshot.cpp b/src/renderer/internal/RendererLib/RamshCommands/Screenshot.cpp similarity index 66% rename from renderer/RendererLib/RendererCommands/src/Screenshot.cpp rename to src/renderer/internal/RendererLib/RamshCommands/Screenshot.cpp index ccfec78be..6fa2bd7fb 100644 --- a/renderer/RendererLib/RendererCommands/src/Screenshot.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/Screenshot.cpp @@ -6,15 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/Screenshot.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RamshCommands/Screenshot.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { Screenshot::Screenshot(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) { - description = "save a screenshot"; + description = "saves a screenshot. Options:[-filename FILENAME -displayId DISPLAYID -sendViaDLT -ob BUFFERID]"; registerKeyword("p"); registerKeyword("screenshot"); } @@ -25,13 +26,15 @@ namespace ramses_internal { EOption_None = 0, EOption_Filename, - EOption_Display + EOption_Display, + EOption_OffscreenBuffer, }; EOption lastOption = EOption_None; std::string filename = "unnamed.png"; - DisplayHandle display = DisplayHandle(0); + auto display = DisplayHandle(0); + OffscreenBufferHandle offscreenBuffer; bool sendViaDLT = false; for (const auto& arg : input) @@ -48,33 +51,38 @@ namespace ramses_internal { sendViaDLT = true; } + else if (arg == "-ob") + { + lastOption = EOption_OffscreenBuffer; + } else { switch( lastOption ) { case EOption_Display: - display = DisplayHandle(atoi(arg.c_str())); + display = DisplayHandle(strtoul(arg.c_str(), nullptr, 0)); lastOption = EOption_None; break; case EOption_Filename: filename = arg; lastOption = EOption_None; break; - + case EOption_OffscreenBuffer: + offscreenBuffer = OffscreenBufferHandle(strtoul(arg.c_str(), nullptr, 0)); + lastOption = EOption_None; + break; case EOption_None: if( contains_c(m_keywords, arg) ) // check whether a keyword is the current argument { continue; } - return false; - - default: + LOG_ERROR_P(CONTEXT_RAMSH, "Unknown option: {}", arg); return false; } } } - m_rendererCommandBuffer.enqueueCommand(RendererCommand::ReadPixels{ display, {}, 0u, 0u, 0u, 0u, true, sendViaDLT, std::move(filename) }); + m_rendererCommandBuffer.enqueueCommand(RendererCommand::ReadPixels{ display, offscreenBuffer, 0u, 0u, 0u, 0u, true, sendViaDLT, std::move(filename) }); return true; } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/Screenshot.h b/src/renderer/internal/RendererLib/RamshCommands/Screenshot.h similarity index 86% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/Screenshot.h rename to src/renderer/internal/RendererLib/RamshCommands/Screenshot.h index 43785b6b1..b2458bc66 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/Screenshot.h +++ b/src/renderer/internal/RendererLib/RamshCommands/Screenshot.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCREENSHOT_H -#define RAMSES_SCREENSHOT_H +#pragma once -#include "Ramsh/RamshCommand.h" +#include "internal/Ramsh/RamshCommand.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/src/renderer/internal/RendererLib/RamshCommands/SetClearColor.cpp b/src/renderer/internal/RendererLib/RamshCommands/SetClearColor.cpp new file mode 100644 index 000000000..7ebb91677 --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/SetClearColor.cpp @@ -0,0 +1,73 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + + +#include "internal/RendererLib/RamshCommands/SetClearColor.h" +#include "internal/RendererLib/RendererCommandBuffer.h" + +namespace ramses::internal +{ + SetClearColor::SetClearColor(RendererCommandBuffer& rendererCommandBuffer) + : m_rendererCommandBuffer(rendererCommandBuffer) + { + description = "Usage: [-ob bufferID] displayID red green blue alpha - set clear color for display or offscreen buffer"; + registerKeyword("clc"); + } + + bool SetClearColor::executeInput(const std::vector& input) + { + std::vector positionals; + ramses::internal::RendererCommand::SetClearColor cmd{}; + + enum class EOption + { + None, + OffscreenBuffer, + }; + + EOption lastOption = EOption::None; + + for (const auto& arg : input) + { + if (arg == "-ob") + { + lastOption = EOption::OffscreenBuffer; + } + else + { + switch( lastOption ) + { + case EOption::OffscreenBuffer: + ArgumentConverter::tryConvert(arg, cmd.offscreenBuffer.asMemoryHandleReference()); + lastOption = EOption::None; + break; + case EOption::None: + if (!contains_c(m_keywords, arg)) + { + positionals.push_back(arg); + } + break; + } + } + } + + if (positionals.size() != 5) + { + LOG_ERROR_P(CONTEXT_RAMSH, "None or too many arguments provided: {}", positionals.size()); + return false; + } + + ArgumentConverter::tryConvert(positionals[0], cmd.display.asMemoryHandleReference()); + ArgumentConverter::tryConvert(positionals[1], cmd.clearColor.r); + ArgumentConverter::tryConvert(positionals[2], cmd.clearColor.g); + ArgumentConverter::tryConvert(positionals[3], cmd.clearColor.b); + ArgumentConverter::tryConvert(positionals[4], cmd.clearColor.a); + m_rendererCommandBuffer.enqueueCommand(std::move(cmd)); + return true; + } +} diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SetClearColor.h b/src/renderer/internal/RendererLib/RamshCommands/SetClearColor.h similarity index 67% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SetClearColor.h rename to src/renderer/internal/RendererLib/RamshCommands/SetClearColor.h index 5023571b6..7635fb40e 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SetClearColor.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SetClearColor.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SETCLEARCOLOR_H -#define RAMSES_SETCLEARCOLOR_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" +#include "internal/Ramsh/RamshCommandArguments.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; - class SetClearColor : public RamshCommandArgs + class SetClearColor : public RamshCommand { public: explicit SetClearColor(RendererCommandBuffer& rendererCommandBuffer); - bool execute(uint32_t& displayId, float& red, float& green, float& blue, float& alpha) const override; + bool executeInput(const std::vector& input) override; private: RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SetFrameTimeLimits.cpp b/src/renderer/internal/RendererLib/RamshCommands/SetFrameTimeLimits.cpp similarity index 89% rename from renderer/RendererLib/RendererCommands/src/SetFrameTimeLimits.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SetFrameTimeLimits.cpp index f10cb15f2..6a366236a 100644 --- a/renderer/RendererLib/RendererCommands/src/SetFrameTimeLimits.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SetFrameTimeLimits.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SetFrameTimeLimits.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RamshCommands/SetFrameTimeLimits.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { SetFrameTimeLimits::SetFrameTimeLimits(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SetFrameTimeLimits.h b/src/renderer/internal/RendererLib/RamshCommands/SetFrameTimeLimits.h similarity index 86% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SetFrameTimeLimits.h rename to src/renderer/internal/RendererLib/RamshCommands/SetFrameTimeLimits.h index 10307ed9c..04a95b543 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SetFrameTimeLimits.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SetFrameTimeLimits.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SETFRAMETIMELIMITS_H -#define RAMSES_SETFRAMETIMELIMITS_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" +#include "internal/Ramsh/RamshCommandArguments.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/src/renderer/internal/RendererLib/RamshCommands/SetSceneState.cpp b/src/renderer/internal/RendererLib/RamshCommands/SetSceneState.cpp new file mode 100644 index 000000000..bc67af98c --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/SetSceneState.cpp @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + + +#include "internal/RendererLib/RamshCommands/SetSceneState.h" +#include "internal/RendererLib/RendererCommandBuffer.h" + +namespace ramses::internal +{ + SetSceneState::SetSceneState(RendererCommandBuffer& rendererCommandBuffer) + : m_rendererCommandBuffer(rendererCommandBuffer) + { + description = "Sets the scene state"; + registerKeyword("scenestate"); + getArgument<0>().setDescription("scene id"); + getArgument<1>().setDescription("3:Rendered 2:Ready 1:Available 0:Unavailable"); + } + + bool SetSceneState::execute(uint64_t& sceneId, uint32_t& sceneState) const + { + if (sceneState >= 4) + { + LOG_ERROR_P(CONTEXT_RAMSH, "Invalid scene state: {}", sceneState); + return false; + } + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SetSceneState{ SceneId{sceneId}, static_cast(sceneState) }); + return true; + } +} diff --git a/src/renderer/internal/RendererLib/RamshCommands/SetSceneState.h b/src/renderer/internal/RendererLib/RamshCommands/SetSceneState.h new file mode 100644 index 000000000..d769b9b1f --- /dev/null +++ b/src/renderer/internal/RendererLib/RamshCommands/SetSceneState.h @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Ramsh/RamshCommandArguments.h" + +namespace ramses::internal +{ + class RendererCommandBuffer; + + class SetSceneState : public RamshCommandArgs + { + public: + explicit SetSceneState(RendererCommandBuffer& rendererCommandBuffer); + bool execute(uint64_t& sceneId, uint32_t& sceneState) const override; + + private: + RendererCommandBuffer& m_rendererCommandBuffer; + }; +} diff --git a/renderer/RendererLib/RendererCommands/src/SetSkippingOfUnmodifiedBuffers.cpp b/src/renderer/internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.cpp similarity index 75% rename from renderer/RendererLib/RendererCommands/src/SetSkippingOfUnmodifiedBuffers.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.cpp index 6479ed91a..fccbdcb88 100644 --- a/renderer/RendererLib/RendererCommands/src/SetSkippingOfUnmodifiedBuffers.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SetSkippingOfUnmodifiedBuffers.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -using namespace ramses_internal; +using namespace ramses::internal; SetSkippingOfUnmodifiedBuffers::SetSkippingOfUnmodifiedBuffers(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -23,6 +23,6 @@ SetSkippingOfUnmodifiedBuffers::SetSkippingOfUnmodifiedBuffers(RendererCommandBu bool SetSkippingOfUnmodifiedBuffers::execute(uint32_t& enableSkipping) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SetSkippingOfUnmodifiedBuffers{ enableSkipping > 0u }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SetSkippingOfUnmodifiedBuffers{ enableSkipping > 0u }); return true; } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SetSkippingOfUnmodifiedBuffers.h b/src/renderer/internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.h similarity index 83% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SetSkippingOfUnmodifiedBuffers.h rename to src/renderer/internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.h index d5852a8d2..0363fe9e3 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SetSkippingOfUnmodifiedBuffers.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SetSkippingOfUnmodifiedBuffers.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SETSKIPPINGOFUNMODIFIEDBUFFERS_H -#define RAMSES_SETSKIPPINGOFUNMODIFIEDBUFFERS_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" +#include "internal/Ramsh/RamshCommandArguments.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerAddSurfaceToLayer.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.cpp similarity index 74% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerAddSurfaceToLayer.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.cpp index 783543c17..7dc15eccb 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerAddSurfaceToLayer.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerAddSurfaceToLayer.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerAddSurfaceToLayer::SystemCompositorControllerAddSurfaceToLayer(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) { - description = "adds an ivi surface to an ivi layer"; + description = "adds an ivi-surface to an ivi-layer"; registerKeyword("scAddSurfaceToLayer"); registerKeyword("scastl"); @@ -23,7 +23,7 @@ namespace ramses_internal bool SystemCompositorControllerAddSurfaceToLayer::execute(uint32_t& surfaceId, uint32_t& layerId) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCAddIviSurfaceToIviLayer{ WaylandIviSurfaceId(surfaceId), WaylandIviLayerId(layerId) }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCAddIviSurfaceToIviLayer{ WaylandIviSurfaceId(surfaceId), WaylandIviLayerId(layerId) }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerAddSurfaceToLayer.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.h similarity index 78% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerAddSurfaceToLayer.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.h index 0647413e6..41166afb1 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerAddSurfaceToLayer.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerAddSurfaceToLayer.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERADDSURFACETOLAYER_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERADDSURFACETOLAYER_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerAddSurfaceToLayer : public RamshCommandArgs @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerDestroySurface.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.cpp similarity index 75% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerDestroySurface.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.cpp index 8a911c586..d2788c033 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerDestroySurface.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerDestroySurface.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerDestroySurface::SystemCompositorControllerDestroySurface(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) { - description = "destroy an ivi surface"; + description = "destroy an ivi-surface"; registerKeyword("scDestroySurface"); registerKeyword("scds"); @@ -22,7 +22,7 @@ namespace ramses_internal bool SystemCompositorControllerDestroySurface::execute(uint32_t& surfaceId) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCDestroyIviSurface{ WaylandIviSurfaceId(surfaceId) }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCDestroyIviSurface{ WaylandIviSurfaceId(surfaceId) }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerDestroySurface.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.h similarity index 77% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerDestroySurface.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.h index 43597f671..e9e00e7d1 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerDestroySurface.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerDestroySurface.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERDESTROYSURFACE_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERDESTROYSURFACE_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerDestroySurface : public RamshCommandArgs { @@ -24,5 +23,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerListIviSurfaces.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.cpp similarity index 77% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerListIviSurfaces.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.cpp index 46add70fd..3172fb939 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerListIviSurfaces.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerListIviSurfaces.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerListIviSurfaces::SystemCompositorControllerListIviSurfaces(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -18,10 +18,9 @@ namespace ramses_internal registerKeyword("scl"); } - bool SystemCompositorControllerListIviSurfaces::executeInput(const std::vector& input) + bool SystemCompositorControllerListIviSurfaces::executeInput([[maybe_unused]] const std::vector& input) { - UNUSED(input); - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCListIviSurfaces{}); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCListIviSurfaces{}); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerListIviSurfaces.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.h similarity index 77% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerListIviSurfaces.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.h index a2c9a3b43..a9c140e41 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerListIviSurfaces.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerListIviSurfaces.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERLISTIVISURFACES_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERLISTIVISURFACES_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerListIviSurfaces : public RamshCommand @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerRemoveSurfaceFromLayer.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.cpp similarity index 74% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerRemoveSurfaceFromLayer.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.cpp index 9ab4afe57..702728fc9 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerRemoveSurfaceFromLayer.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerRemoveSurfaceFromLayer::SystemCompositorControllerRemoveSurfaceFromLayer(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) { - description = "remove an ivi surface from an ivi layer"; + description = "remove an ivi-surface from an ivi-layer"; registerKeyword("scRemoveSurfaceFromLayer"); registerKeyword("scrsfl"); @@ -23,7 +23,7 @@ namespace ramses_internal bool SystemCompositorControllerRemoveSurfaceFromLayer::execute(uint32_t& surfaceId, uint32_t& layerId) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCRemoveIviSurfaceFromIviLayer{ WaylandIviSurfaceId(surfaceId), WaylandIviLayerId(layerId) }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCRemoveIviSurfaceFromIviLayer{ WaylandIviSurfaceId(surfaceId), WaylandIviLayerId(layerId) }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h similarity index 77% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h index 4f686b106..a44a861f6 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerRemoveSurfaceFromLayer.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERREMOVESURFACEFROMLAYER_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERREMOVESURFACEFROMLAYER_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerRemoveSurfaceFromLayer : public RamshCommandArgs @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerScreenshot.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.cpp similarity index 82% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerScreenshot.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.cpp index 59d7cdba4..157aa7896 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerScreenshot.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerScreenshot.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerScreenshot::SystemCompositorControllerScreenshot(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -23,7 +23,7 @@ namespace ramses_internal bool SystemCompositorControllerScreenshot::execute(std::string& fileName, int32_t& screenIviId) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCScreenshot{ screenIviId, fileName }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCScreenshot{ screenIviId, fileName }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerScreenshot.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.h similarity index 79% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerScreenshot.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.h index 67027cc32..061bff2e1 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerScreenshot.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerScreenshot.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERSCREENSHOT_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERSCREENSHOT_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" #include -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerScreenshot : public RamshCommandArgs @@ -27,5 +26,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetLayerVisibility.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.cpp similarity index 80% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetLayerVisibility.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.cpp index 181c9ec7f..3eaf408b0 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetLayerVisibility.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerSetLayerVisibility.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerSetLayerVisibility::SystemCompositorControllerSetLayerVisibility(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -23,7 +23,7 @@ namespace ramses_internal bool SystemCompositorControllerSetLayerVisibility::execute(int32_t& layerId, int32_t& visibility) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCSetIviLayerVisibility{ WaylandIviLayerId(layerId), visibility != 0 }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCSetIviLayerVisibility{ WaylandIviLayerId(layerId), visibility != 0 }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetLayerVisibility.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.h similarity index 77% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetLayerVisibility.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.h index 1eb009385..dd1a235e1 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetLayerVisibility.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetLayerVisibility.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERSETLAYERVISIBILITY_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERSETLAYERVISIBILITY_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerSetLayerVisibility : public RamshCommandArgs @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceDestRectangle.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.cpp similarity index 81% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceDestRectangle.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.cpp index 247587a20..0cb5af39e 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceDestRectangle.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerSetSurfaceDestRectangle.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerSetSurfaceDestRectangle::SystemCompositorControllerSetSurfaceDestRectangle(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -26,7 +26,7 @@ namespace ramses_internal bool SystemCompositorControllerSetSurfaceDestRectangle::execute(int32_t& surfaceId, int32_t& x, int32_t& y, int32_t& width, int32_t& height) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCSetIviSurfaceDestRectangle{ WaylandIviSurfaceId(surfaceId), x, y, width ,height }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCSetIviSurfaceDestRectangle{ WaylandIviSurfaceId(surfaceId), x, y, width ,height }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceDestRectangle.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.h similarity index 78% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceDestRectangle.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.h index 03a136cfb..42b47d42c 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceDestRectangle.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceDestRectangle.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERSETSURFACEDESTRECTANGLE_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERSETSURFACEDESTRECTANGLE_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerSetSurfaceDestRectangle : public RamshCommandArgs @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceOpacity.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.cpp similarity index 80% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceOpacity.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.cpp index 05270d952..de82a5ccf 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceOpacity.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerSetSurfaceOpacity.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerSetSurfaceOpacity::SystemCompositorControllerSetSurfaceOpacity(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -23,7 +23,7 @@ namespace ramses_internal bool SystemCompositorControllerSetSurfaceOpacity::execute(int32_t& surfaceId, float& opacity) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCSetIviSurfaceOpacity{ WaylandIviSurfaceId(surfaceId), opacity }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCSetIviSurfaceOpacity{ WaylandIviSurfaceId(surfaceId), opacity }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceOpacity.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.h similarity index 77% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceOpacity.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.h index f84ae1db0..543053f66 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceOpacity.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceOpacity.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERSETSURFACEOPACITY_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERSETSURFACEOPACITY_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerSetSurfaceOpacity : public RamshCommandArgs @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceVisibility.cpp b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.cpp similarity index 79% rename from renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceVisibility.cpp rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.cpp index 08ff62036..62e8b274a 100644 --- a/renderer/RendererLib/RendererCommands/src/SystemCompositorControllerSetSurfaceVisibility.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererCommands/SystemCompositorControllerSetSurfaceVisibility.h" +#include "internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.h" -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerSetSurfaceVisibility::SystemCompositorControllerSetSurfaceVisibility(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -23,7 +23,7 @@ namespace ramses_internal bool SystemCompositorControllerSetSurfaceVisibility::execute(int32_t& surfaceId, int32_t& visibility) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::SCSetIviSurfaceVisibility{ WaylandIviSurfaceId(surfaceId), visibility != 0 }); + m_rendererCommandBuffer.enqueueCommand(ramses::internal::RendererCommand::SCSetIviSurfaceVisibility{ WaylandIviSurfaceId(surfaceId), visibility != 0 }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceVisibility.h b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.h similarity index 77% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceVisibility.h rename to src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.h index b22441f37..9d0bbbe9e 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/SystemCompositorControllerSetSurfaceVisibility.h +++ b/src/renderer/internal/RendererLib/RamshCommands/SystemCompositorControllerSetSurfaceVisibility.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERSETSURFACEVISIBILITY_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERSETSURFACEVISIBILITY_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerSetSurfaceVisibility : public RamshCommandArgs @@ -25,5 +24,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererCommands/src/TriggerPickEvent.cpp b/src/renderer/internal/RendererLib/RamshCommands/TriggerPickEvent.cpp similarity index 79% rename from renderer/RendererLib/RendererCommands/src/TriggerPickEvent.cpp rename to src/renderer/internal/RendererLib/RamshCommands/TriggerPickEvent.cpp index 8f72c336a..91755692d 100644 --- a/renderer/RendererLib/RendererCommands/src/TriggerPickEvent.cpp +++ b/src/renderer/internal/RendererLib/RamshCommands/TriggerPickEvent.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- -#include "RendererCommands/TriggerPickEvent.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RamshCommands/TriggerPickEvent.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { TriggerPickEvent::TriggerPickEvent(RendererCommandBuffer& rendererCommandBuffer) : m_rendererCommandBuffer(rendererCommandBuffer) @@ -24,7 +24,7 @@ namespace ramses_internal bool TriggerPickEvent::execute(uint64_t& sceneId, float& pickCoordX, float& pickCoordY) const { - m_rendererCommandBuffer.enqueueCommand(ramses_internal::RendererCommand::PickEvent{ SceneId{sceneId}, glm::vec2{pickCoordX, pickCoordY} }); + m_rendererCommandBuffer.enqueueCommand(RendererCommand::PickEvent{ SceneId{sceneId}, glm::vec2{pickCoordX, pickCoordY} }); return true; } } diff --git a/renderer/RendererLib/RendererCommands/include/RendererCommands/TriggerPickEvent.h b/src/renderer/internal/RendererLib/RamshCommands/TriggerPickEvent.h similarity index 85% rename from renderer/RendererLib/RendererCommands/include/RendererCommands/TriggerPickEvent.h rename to src/renderer/internal/RendererLib/RamshCommands/TriggerPickEvent.h index 2d3987fed..3a975f9fa 100644 --- a/renderer/RendererLib/RendererCommands/include/RendererCommands/TriggerPickEvent.h +++ b/src/renderer/internal/RendererLib/RamshCommands/TriggerPickEvent.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRIGGERPICKEVENT_H -#define RAMSES_TRIGGERPICKEVENT_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" +#include "internal/Ramsh/RamshCommandArguments.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; @@ -24,5 +23,3 @@ namespace ramses_internal RendererCommandBuffer& m_rendererCommandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RenderBackend.cpp b/src/renderer/internal/RendererLib/RenderBackend.cpp similarity index 86% rename from renderer/RendererLib/RendererLib/src/RenderBackend.cpp rename to src/renderer/internal/RendererLib/RenderBackend.cpp index d5365d819..56b960d18 100644 --- a/renderer/RendererLib/RendererLib/src/RenderBackend.cpp +++ b/src/renderer/internal/RendererLib/RenderBackend.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RenderBackend.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IEmbeddedCompositor.h" +#include "internal/RendererLib/RenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" -namespace ramses_internal +namespace ramses::internal { RenderBackend::RenderBackend(IWindow& window, IContext& context, IDevice& device, IEmbeddedCompositor& embeddedCompositor, ITextureUploadingAdapter& textureUploadingAdapter) : m_window(window) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RenderBackend.h b/src/renderer/internal/RendererLib/RenderBackend.h similarity index 90% rename from renderer/RendererLib/RendererLib/include/RendererLib/RenderBackend.h rename to src/renderer/internal/RendererLib/RenderBackend.h index a21147f08..baa47ef9f 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RenderBackend.h +++ b/src/renderer/internal/RendererLib/RenderBackend.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERBACKEND_H -#define RAMSES_RENDERBACKEND_H +#pragma once -#include "RendererAPI/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" -namespace ramses_internal +namespace ramses::internal { class RenderBackend : public IRenderBackend { @@ -33,5 +32,3 @@ namespace ramses_internal ITextureUploadingAdapter& m_textureUploadingAdapter; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RenderExecutor.cpp b/src/renderer/internal/RendererLib/RenderExecutor.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/src/RenderExecutor.cpp rename to src/renderer/internal/RendererLib/RenderExecutor.cpp index 52d1d4c19..5e7e72630 100644 --- a/renderer/RendererLib/RendererLib/src/RenderExecutor.cpp +++ b/src/renderer/internal/RendererLib/RenderExecutor.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RenderExecutor.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererAPI/IDevice.h" -#include "SceneAPI/BlitPass.h" -#include "Components/EffectUniformTime.h" +#include "internal/RendererLib/RenderExecutor.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "internal/Components/EffectUniformTime.h" -namespace ramses_internal +namespace ramses::internal { uint32_t RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = RenderExecutor::DefaultNumRenderablesToRenderInBetweenTimeBudgetChecks; @@ -60,7 +60,7 @@ namespace ramses_internal m_state.renderPassState.setState(pass); if (m_state.renderPassState.hasChanged() && renderPassIsExecutedFromBeginning) { - uint32_t clearFlags = EClearFlags_None; + ClearFlags clearFlags = EClearFlag::None; glm::vec4 clearColor; // clear only if rendering into render target or display buffer clear pending @@ -71,21 +71,21 @@ namespace ramses_internal clearFlags = renderPass.clearFlags; clearColor = renderPass.clearColor; } - else if (renderContext.displayBufferClearPending != EClearFlags_None) + else if (renderContext.displayBufferClearPending != EClearFlag::None) { clearFlags = renderContext.displayBufferClearPending; clearColor = renderContext.displayBufferClearColor; - renderContext.displayBufferClearPending = EClearFlags_None; + renderContext.displayBufferClearPending = EClearFlag::None; } - if (clearFlags != EClearFlags_None) + if (clearFlags != EClearFlag::None) { - if (clearFlags & EClearFlags_Color) + if (clearFlags.isSet(EClearFlag::Color)) { m_state.getDevice().colorMask(true, true, true, true); m_state.getDevice().clearColor(clearColor); } - if (clearFlags & EClearFlags_Depth) + if (clearFlags.isSet(EClearFlag::Depth)) m_state.getDevice().depthWrite(EDepthWrite::Enabled); m_state.getDevice().scissorTest(EScissorTest::Disabled, {}); @@ -299,6 +299,13 @@ namespace ramses_internal break; } + case EDataType::Bool: + { + const bool* value = renderScene.getDataBooleanArray(dataInstance, dataInstancefield); + device.setConstant(uniformInputField, elementCount, value); + break; + } + case EDataType::Int32: { const int32_t* value = renderScene.getDataIntegerArray(dataInstance, dataInstancefield); @@ -372,9 +379,13 @@ namespace ramses_internal const Renderable& renderable = renderScene.getRenderable(m_state.getRenderable()); if (m_state.vertexArrayUsesIndices) - device.drawIndexedTriangles(renderable.startIndex, renderable.indexCount, renderable.instanceCount); + { + device.drawIndexedTriangles(static_cast(renderable.startIndex), static_cast(renderable.indexCount), renderable.instanceCount); + } else - device.drawTriangles(renderable.startIndex, renderable.indexCount, renderable.instanceCount); + { + device.drawTriangles(static_cast(renderable.startIndex), static_cast(renderable.indexCount), renderable.instanceCount); + } } void RenderExecutor::setGlobalInternalStates(const RendererCachedScene& scene) const @@ -607,7 +618,7 @@ namespace ramses_internal // next pass using this depth buffer decides if can discard based on clear const auto& rpData = scene.getRenderPass(rpInfo.getRenderPassHandle()); if (FindDepthRenderBufferInRenderTarget(scene, rpData.renderTarget) == depthRB) - return IsClearFlagSet(rpData.clearFlags, EClearFlags_Depth) && IsClearFlagSet(rpData.clearFlags, EClearFlags_Stencil); + return rpData.clearFlags.isSet(EClearFlag::Depth) && rpData.clearFlags.isSet(EClearFlag::Stencil); } else { @@ -631,8 +642,7 @@ namespace ramses_internal for (uint32_t rbIdx = 0u; rbIdx < rbCount; ++rbIdx) { const auto rb = scene.getRenderTargetRenderBuffer(renderTarget, rbIdx); - const auto rbType = scene.getRenderBuffer(rb).type; - if (rbType == ERenderBufferType_DepthBuffer || rbType == ERenderBufferType_DepthStencilBuffer) + if (IsDepthOrStencilFormat(scene.getRenderBuffer(rb).format)) return rb; } diff --git a/renderer/RendererLib/RendererLib/include/RenderExecutor.h b/src/renderer/internal/RendererLib/RenderExecutor.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RenderExecutor.h rename to src/renderer/internal/RendererLib/RenderExecutor.h index c7140daf6..60abbfda5 100644 --- a/renderer/RendererLib/RendererLib/include/RenderExecutor.h +++ b/src/renderer/internal/RendererLib/RenderExecutor.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREXECUTOR_H -#define RAMSES_RENDEREXECUTOR_H +#pragma once -#include "RenderExecutorInternalState.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EFixedSemantics.h" +#include "internal/RendererLib/RenderExecutorInternalState.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EFixedSemantics.h" -namespace ramses_internal +namespace ramses::internal { class IDevice; struct RenderingContext; @@ -59,5 +58,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RenderExecutorInternalRenderStates.h b/src/renderer/internal/RendererLib/RenderExecutorInternalRenderStates.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RenderExecutorInternalRenderStates.h rename to src/renderer/internal/RendererLib/RenderExecutorInternalRenderStates.h index 96a854519..224546bd1 100644 --- a/renderer/RendererLib/RendererLib/include/RenderExecutorInternalRenderStates.h +++ b/src/renderer/internal/RendererLib/RenderExecutorInternalRenderStates.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREXECUTORINTERNALRENDERSTATES_H -#define RAMSES_RENDEREXECUTORINTERNALRENDERSTATES_H +#pragma once -#include "SceneAPI/RenderState.h" -#include "PlatformAbstraction/PlatformTypes.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" -namespace ramses_internal +#include + +namespace ramses::internal { // Initial values of these states must be invalid in the sense that whatever values come // from scene's render states set by user must compare NOT equal @@ -79,5 +79,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RenderExecutorInternalState.cpp b/src/renderer/internal/RendererLib/RenderExecutorInternalState.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/RenderExecutorInternalState.cpp rename to src/renderer/internal/RendererLib/RenderExecutorInternalState.cpp index 567d5bf56..43a3222e6 100644 --- a/renderer/RendererLib/RendererLib/src/RenderExecutorInternalState.cpp +++ b/src/renderer/internal/RendererLib/RenderExecutorInternalState.cpp @@ -6,20 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RenderExecutorInternalState.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererAPI/RenderingContext.h" +#include "internal/RendererLib/RenderExecutorInternalState.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/RenderingContext.h" -namespace ramses_internal +namespace ramses::internal { RenderExecutorInternalState::RenderExecutorInternalState(IDevice& device, RenderingContext& renderContext, const FrameTimer* frameTimer) - : viewportState() - , m_currentRenderIterator(renderContext.renderFrom) + : m_currentRenderIterator(renderContext.renderFrom) , m_device(device) - , m_scene(nullptr) , m_renderContext(renderContext) - , m_projectionMatrix(1.f) - , m_cameraWorldPosition(0.0f) , m_frameTimer(frameTimer) { // Currently framebuffer is default render target and invalid RT handle is used to refer to it. diff --git a/renderer/RendererLib/RendererLib/include/RenderExecutorInternalState.h b/src/renderer/internal/RendererLib/RenderExecutorInternalState.h similarity index 87% rename from renderer/RendererLib/RendererLib/include/RenderExecutorInternalState.h rename to src/renderer/internal/RendererLib/RenderExecutorInternalState.h index 943798159..097dfccf0 100644 --- a/renderer/RendererLib/RendererLib/include/RenderExecutorInternalState.h +++ b/src/renderer/internal/RendererLib/RenderExecutorInternalState.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREXECUTORINTERNALSTATE_H -#define RAMSES_RENDEREXECUTORINTERNALSTATE_H - -#include "Math3d/CameraMatrixHelper.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/Viewport.h" -#include "RendererAPI/SceneRenderExecutionIterator.h" -#include "RendererAPI/RenderingContext.h" -#include "RendererLib/FrameTimer.h" -#include "RenderExecutorInternalRenderStates.h" +#pragma once + +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Viewport.h" +#include "internal/RendererLib/SceneRenderExecutionIterator.h" +#include "internal/RendererLib/RenderingContext.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/RenderExecutorInternalRenderStates.h" #include -namespace ramses_internal +namespace ramses::internal { class IDevice; class RendererCachedScene; @@ -109,17 +108,17 @@ namespace ramses_internal private: IDevice& m_device; - const RendererCachedScene* m_scene; + const RendererCachedScene* m_scene{nullptr}; RenderingContext& m_renderContext; RenderableHandle m_renderable; - glm::mat4 m_projectionMatrix; - glm::mat4 m_viewMatrix; - glm::mat4 m_modelMatrix; - glm::mat4 m_modelViewMatrix; - glm::mat4 m_modelViewProjectionMatrix; - glm::vec3 m_cameraWorldPosition; + glm::mat4 m_projectionMatrix{1.f}; + glm::mat4 m_viewMatrix{}; + glm::mat4 m_modelMatrix{}; + glm::mat4 m_modelViewMatrix{}; + glm::mat4 m_modelViewProjectionMatrix{}; + glm::vec3 m_cameraWorldPosition{0.f}; CachedState < CameraHandle > m_camera; @@ -187,5 +186,3 @@ namespace ramses_internal return m_frameTimer != nullptr ? m_frameTimer->isTimeBudgetExceededForSection(EFrameTimerSectionBudget::OffscreenBufferRender) : false; } } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RenderExecutorLogger.cpp b/src/renderer/internal/RendererLib/RenderExecutorLogger.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/RenderExecutorLogger.cpp rename to src/renderer/internal/RendererLib/RenderExecutorLogger.cpp index 704b40ec0..0ce16cf55 100644 --- a/renderer/RendererLib/RendererLib/src/RenderExecutorLogger.cpp +++ b/src/renderer/internal/RendererLib/RenderExecutorLogger.cpp @@ -6,13 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RenderExecutorLogger.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/RendererCachedScene.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/BlitPass.h" - -namespace ramses_internal +#include "internal/RendererLib/RenderExecutorLogger.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "impl/TextureEnumsImpl.h" + +namespace ramses::internal { RenderExecutorLogger::RenderExecutorLogger(IDevice& device, RenderingContext& renderContext, RendererLogContext& context) : RenderExecutor(device, renderContext) @@ -68,18 +69,18 @@ namespace ramses_internal m_logContext << "--> Render target " << renderTarget << " [Device handle: " << rtHandle << "]" << RendererLogContext::NewLine; m_logContext << " Size: " << renderBuffer.width << " x " << renderBuffer.height << RendererLogContext::NewLine; - m_logContext << " Access mode: " << RenderBufferAccessModeNames[renderBuffer.accessMode] << RendererLogContext::NewLine; + m_logContext << " Access mode: " << EnumToString(renderBuffer.accessMode) << RendererLogContext::NewLine; m_logContext << " Sample count: " << renderBuffer.sampleCount << RendererLogContext::NewLine; - const uint32_t clearFlags = rp.clearFlags; + const auto clearFlags = rp.clearFlags; m_logContext << " Clear flags: " - << ((clearFlags & EClearFlags_Color) ? "Color|" : "") - << ((clearFlags & EClearFlags_Depth) ? "Depth|" : "") - << ((clearFlags & EClearFlags_Stencil) ? "Stencil" : "") - << ((clearFlags == EClearFlags_None) ? "None" : "") + << (clearFlags.isSet(EClearFlag::Color) ? "Color|" : "") + << (clearFlags.isSet(EClearFlag::Depth) ? "Depth|" : "") + << (clearFlags.isSet(EClearFlag::Stencil) ? "Stencil" : "") + << ((clearFlags == EClearFlag::None) ? "None" : "") << RendererLogContext::NewLine; - if (clearFlags & EClearFlags_Color) + if (clearFlags.isSet(EClearFlag::Color)) { const auto& clearColor = rp.clearColor; m_logContext << " Clear color: [" @@ -98,7 +99,6 @@ namespace ramses_internal const RenderBuffer& rbData = scene.getRenderBuffer(rbHandle); m_logContext << " Render buffer (index " << i << ")" << RendererLogContext::NewLine; - m_logContext << " Type: " << RenderBufferTypeNames[rbData.type] << RendererLogContext::NewLine; m_logContext << " Format: " << EnumToString(rbData.format) << RendererLogContext::NewLine; m_logContext << " [RenderBuffer handle: " << rbHandle << "]" << RendererLogContext::NewLine; m_logContext << " [Device handle: " << deviceHandle << "]" << RendererLogContext::NewLine; diff --git a/renderer/RendererLib/RendererLib/include/RenderExecutorLogger.h b/src/renderer/internal/RendererLib/RenderExecutorLogger.h similarity index 90% rename from renderer/RendererLib/RendererLib/include/RenderExecutorLogger.h rename to src/renderer/internal/RendererLib/RenderExecutorLogger.h index 55efb5ef0..4a237c869 100644 --- a/renderer/RendererLib/RendererLib/include/RenderExecutorLogger.h +++ b/src/renderer/internal/RendererLib/RenderExecutorLogger.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREXECUTORLOGGER_H -#define RAMSES_RENDEREXECUTORLOGGER_H +#pragma once -#include "RenderExecutor.h" +#include "internal/RendererLib/RenderExecutor.h" -namespace ramses_internal +namespace ramses::internal { class RendererLogContext; struct RenderingContext; @@ -34,5 +33,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RenderableComparator.h b/src/renderer/internal/RendererLib/RenderableComparator.h similarity index 94% rename from renderer/RendererLib/RendererLib/include/RendererLib/RenderableComparator.h rename to src/renderer/internal/RendererLib/RenderableComparator.h index 606a9cc1f..2d95b1cff 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RenderableComparator.h +++ b/src/renderer/internal/RendererLib/RenderableComparator.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERABLECOMPARATOR_H -#define RAMSES_RENDERABLECOMPARATOR_H +#pragma once -#include "RendererLib/ResourceCachedScene.h" +#include "internal/RendererLib/ResourceCachedScene.h" -namespace ramses_internal +namespace ramses::internal { class RenderableComparator { @@ -57,5 +56,3 @@ namespace ramses_internal const ResourceCachedScene& m_scene; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/Renderer.cpp b/src/renderer/internal/RendererLib/Renderer.cpp similarity index 92% rename from renderer/RendererLib/RendererLib/src/Renderer.cpp rename to src/renderer/internal/RendererLib/Renderer.cpp index 67475bda2..b05f125d6 100644 --- a/renderer/RendererLib/RendererLib/src/Renderer.cpp +++ b/src/renderer/internal/RendererLib/Renderer.cpp @@ -6,26 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/Renderer.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/ISystemCompositorController.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RendererAPI/IWindow.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/RenderingContext.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/DisplayController.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DisplayEventHandler.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "Platform_Base/Platform_Base.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/ISystemCompositorController.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/RenderingContext.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/DisplayController.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DisplayEventHandler.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/PlatformBase/Platform_Base.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { const glm::vec4 Renderer::DefaultClearColor = { 0.f, 0.f, 0.f, 1.f }; @@ -132,57 +132,82 @@ namespace ramses_internal void Renderer::systemCompositorListIviSurfaces() const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->listIVISurfaces(); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorListIviSurfaces: system compositor is not available"); + } } void Renderer::systemCompositorSetIviSurfaceVisibility(WaylandIviSurfaceId surfaceId, bool visibility) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->setSurfaceVisibility(surfaceId, visibility); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorSetSurfaceVisibility: system compositor is not available"); + } } void Renderer::systemCompositorSetIviSurfaceOpacity(WaylandIviSurfaceId surfaceId, float opacity) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->setSurfaceOpacity(surfaceId, opacity); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorSetOpacity: system compositor is not available"); + } } void Renderer::systemCompositorSetIviSurfaceDestRectangle(WaylandIviSurfaceId surfaceId, int32_t x, int32_t y, int32_t width, int32_t height) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->setSurfaceDestinationRectangle(surfaceId, x, y, width, height); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorSetRectangle: system compositor is not available"); + } } void Renderer::systemCompositorSetIviLayerVisibility(WaylandIviLayerId layerId, bool visibility) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->setLayerVisibility(layerId, visibility); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorSetLayerVisibility: system compositor is not available"); + } } void Renderer::systemCompositorScreenshot(std::string_view fileName, int32_t screenIviId) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->doScreenshot(fileName, screenIviId); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorScreenshot: system compositor is not available"); + } } bool Renderer::systemCompositorAddIviSurfaceToIviLayer(WaylandIviSurfaceId surfaceId, WaylandIviLayerId layerId) const { if (m_platform.getSystemCompositorController()) + { return m_platform.getSystemCompositorController()->addSurfaceToLayer(surfaceId, layerId); - else - LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorAddSurfaceToLayer: system compositor is not available"); + } + LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorAddSurfaceToLayer: system compositor is not available"); return true; } @@ -190,17 +215,25 @@ namespace ramses_internal void Renderer::systemCompositorRemoveIviSurfaceFromIviLayer(WaylandIviSurfaceId surfaceId, WaylandIviLayerId layerId) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->removeSurfaceFromLayer(surfaceId, layerId); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorRemoveSurfaceFromLayer: system compositor is not available"); + } } void Renderer::systemCompositorDestroyIviSurface(WaylandIviSurfaceId surfaceId) const { if (m_platform.getSystemCompositorController()) + { m_platform.getSystemCompositorController()->destroySurface(surfaceId); + } else + { LOG_ERROR(CONTEXT_RENDERER, "RamsesRenderer::systemCompositorDestroySurface: system compositor is not available"); + } } void Renderer::handleDisplayEvents() @@ -211,9 +244,13 @@ namespace ramses_internal { m_canRenderFrame = canRenderFrame; if (canRenderFrame) + { LOG_INFO(CONTEXT_RENDERER, "Renderer::doOneRenderLoop start rendering frames on display"); + } else + { LOG_INFO(CONTEXT_RENDERER, "Renderer::doOneRenderLoop will skip frames because window is not ready for rendering on display"); + } } } @@ -248,7 +285,7 @@ namespace ramses_internal if (sceneInfo.shown) { const RendererCachedScene& scene = m_rendererScenes.getScene(sceneInfo.sceneId); - m_displayController->renderScene(scene, renderContext); + m_displayController->renderScene(scene, renderContext, nullptr); onSceneWasRendered(scene); } } @@ -281,8 +318,10 @@ namespace ramses_internal m_tempScenesToRender.clear(); for (const auto& assignedScene : displayBufferInfo.scenes) + { if (assignedScene.shown) m_tempScenesToRender.push_back(assignedScene.sceneId); + } // OB was marked for re-render but has no shown scenes -> clear it if (m_tempScenesToRender.empty()) @@ -292,11 +331,11 @@ namespace ramses_internal { // offscreen buffer depth component may be discarded after last scene rendered into it and it is not kept for next frame (clear is enabled) if (sceneId == m_tempScenesToRender.back() - && IsClearFlagSet(displayBufferInfo.clearFlags, EClearFlags_Depth) && IsClearFlagSet(displayBufferInfo.clearFlags, EClearFlags_Stencil)) + && displayBufferInfo.clearFlags.isSet(EClearFlag::Depth) && displayBufferInfo.clearFlags.isSet(EClearFlag::Stencil)) renderContext.displayBufferDepthDiscard = true; const RendererCachedScene& scene = m_rendererScenes.getScene(sceneId); - m_displayController->renderScene(scene, renderContext); + m_displayController->renderScene(scene, renderContext, nullptr); onSceneWasRendered(scene); } @@ -323,7 +362,7 @@ namespace ramses_internal renderContext.displayBufferDeviceHandle = displayBuffer; renderContext.viewportWidth = displayBufferInfo.viewport.width; renderContext.viewportHeight = displayBufferInfo.viewport.height; - renderContext.displayBufferClearPending = EClearFlags_None; // set clear flags accordingly below only if not in interrupted state + renderContext.displayBufferClearPending = EClearFlag::None; // set clear flags accordingly below only if not in interrupted state const auto& assignedScenes = displayBufferInfo.scenes; if (!m_rendererInterruptState.isInterrupted(displayBuffer)) @@ -333,7 +372,9 @@ namespace ramses_internal // clear buffer only if we just started rendering into it and no scene shown to keep expected behavior (no scene -> clear buffer) const bool hasAnyShownScene = std::any_of(std::cbegin(assignedScenes), std::cend(assignedScenes), [](const auto& s) { return s.shown; }); if (!hasAnyShownScene) + { m_displayController->clearBuffer(displayBuffer, displayBufferInfo.clearFlags, displayBufferInfo.clearColor); + } else { renderContext.displayBufferClearPending = displayBufferInfo.clearFlags; @@ -526,7 +567,11 @@ namespace ramses_internal return nullptr; systemCompositorSetIviSurfaceVisibility(rendererSurfaceIVIID, config.getStartVisibleIvi()); - systemCompositorSetIviSurfaceDestRectangle(rendererSurfaceIVIID, config.getWindowPositionX(), config.getWindowPositionY(), config.getDesiredWindowWidth(), config.getDesiredWindowHeight()); + systemCompositorSetIviSurfaceDestRectangle(rendererSurfaceIVIID, + config.getWindowPositionX(), + config.getWindowPositionY(), + static_cast(config.getDesiredWindowWidth()), + static_cast(config.getDesiredWindowHeight())); } // Enable the context of the render backend that was just created @@ -538,7 +583,7 @@ namespace ramses_internal return new DisplayController(*renderBackend, numSamples); } - void Renderer::setClearFlags(DeviceResourceHandle bufferDeviceHandle, uint32_t clearFlags) + void Renderer::setClearFlags(DeviceResourceHandle bufferDeviceHandle, ClearFlags clearFlags) { assert(hasDisplayController()); m_displayBuffersSetup.setClearFlags(bufferDeviceHandle, clearFlags); @@ -566,7 +611,7 @@ namespace ramses_internal void Renderer::scheduleScreenshot(DeviceResourceHandle renderTargetHandle, ScreenshotInfo&& screenshot) { assert(hasDisplayController()); - if (m_screenshots.count(renderTargetHandle)) + if (m_screenshots.count(renderTargetHandle) != 0u) LOG_WARN(CONTEXT_RENDERER, "Renderer::scheduleScreenshot: will overwrite previous screenshot request that was not executed yet (buffer=" << renderTargetHandle << ")"); m_screenshots[renderTargetHandle] = std::move(screenshot); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/Renderer.h b/src/renderer/internal/RendererLib/Renderer.h similarity index 91% rename from renderer/RendererLib/RendererLib/include/RendererLib/Renderer.h rename to src/renderer/internal/RendererLib/Renderer.h index 0a9b3f33c..2971dc1d4 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/Renderer.h +++ b/src/renderer/internal/RendererLib/Renderer.h @@ -6,24 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERER_H -#define RAMSES_RENDERER_H - -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererLib/FrameProfilerStatistics.h" -#include "RendererLib/RendererInterruptState.h" -#include "RendererLib/DisplaySetup.h" -#include "RendererLib/DisplayEventHandler.h" -#include "Collections/Vector.h" -#include "Collections/HashMap.h" +#pragma once + +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/FrameProfilerStatistics.h" +#include "internal/RendererLib/RendererInterruptState.h" +#include "internal/RendererLib/DisplaySetup.h" +#include "internal/RendererLib/DisplayEventHandler.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { class IDisplayController; class RendererCachedScene; @@ -73,7 +72,7 @@ namespace ramses_internal DisplayEventHandler& getDisplayEventHandler(); - virtual void setClearFlags(DeviceResourceHandle bufferDeviceHandle, uint32_t clearFlags); + virtual void setClearFlags(DeviceResourceHandle bufferDeviceHandle, ClearFlags clearFlags); virtual void setClearColor(DeviceResourceHandle bufferDeviceHandle, const glm::vec4& clearColor); virtual bool setExternallyOwnedWindowSize(uint32_t width, uint32_t height); void scheduleScreenshot(DeviceResourceHandle renderTargetHandle, ScreenshotInfo&& screenshot); @@ -137,5 +136,3 @@ namespace ramses_internal std::vector m_tempScenesToRender; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererCachedScene.cpp b/src/renderer/internal/RendererLib/RendererCachedScene.cpp similarity index 90% rename from renderer/RendererLib/RendererLib/src/RendererCachedScene.cpp rename to src/renderer/internal/RendererLib/RendererCachedScene.cpp index 36ad27a53..831b627c9 100644 --- a/renderer/RendererLib/RendererLib/src/RendererCachedScene.cpp +++ b/src/renderer/internal/RendererLib/RendererCachedScene.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/RenderableComparator.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/RenderableComparator.h" #include "RenderingPassOrderComparator.h" #include -namespace ramses_internal +namespace ramses::internal { RendererCachedScene::RendererCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo) : ResourceCachedScene(sceneLinksManager, sceneInfo) @@ -56,7 +56,7 @@ namespace ramses_internal m_renderableOrderingDirty = true; } - BlitPassHandle RendererCachedScene::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle /*= BlitPassHandle::Invalid()*/) + BlitPassHandle RendererCachedScene::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) { const BlitPassHandle blitPass = ResourceCachedScene::allocateBlitPass(sourceRenderBufferHandle, destinationRenderBufferHandle, passHandle); m_renderableOrderingDirty = true; @@ -82,6 +82,25 @@ namespace ramses_internal m_renderableOrderingDirty = true; } + TextureBufferHandle RendererCachedScene::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) + { + auto resultHandle = TextureLinkCachedScene::allocateTextureBuffer(textureFormat, mipMapDimensions, handle); + m_textureBufferUpdates.resize(getTextureBufferCount()); + auto& update = m_textureBufferUpdates[resultHandle.asMemoryHandle()]; + update.resize(mipMapDimensions.size()); + return resultHandle; + } + + void RendererCachedScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) + { + TextureLinkCachedScene::updateTextureBuffer(handle, mipLevel, x, y, width, height, data); + assert(handle.asMemoryHandle() < m_textureBufferUpdates.size()); + auto& update = m_textureBufferUpdates[handle.asMemoryHandle()]; + assert(mipLevel < update.size()); + auto& area = update[mipLevel]; + area = area.getBoundingQuad(Quad{static_cast(x), static_cast(y), static_cast(width), static_cast(height)}); + } + void RendererCachedScene::setRenderPassEnabled(RenderPassHandle passHandle, bool isEnabled) { ResourceCachedScene::setRenderPassEnabled(passHandle, isEnabled); @@ -251,8 +270,8 @@ namespace ramses_internal std::sort(orderedGroupRenderables.begin(), orderedGroupRenderables.end(), renderableComp); std::sort(orderedRenderGroups.begin(), orderedRenderGroups.end()); - RenderableOrderVector::iterator renderablesIterator = orderedGroupRenderables.begin(); - RenderGroupOrderVector::iterator renderGroupIterator = orderedRenderGroups.begin(); + auto renderablesIterator = orderedGroupRenderables.begin(); + auto renderGroupIterator = orderedRenderGroups.begin(); while (renderablesIterator != orderedGroupRenderables.end() || renderGroupIterator != orderedRenderGroups.end()) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCachedScene.h b/src/renderer/internal/RendererLib/RendererCachedScene.h similarity index 82% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererCachedScene.h rename to src/renderer/internal/RendererLib/RendererCachedScene.h index 329c8513f..e0008aa9c 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCachedScene.h +++ b/src/renderer/internal/RendererLib/RendererCachedScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCACHEDSCENE_H -#define RAMSES_RENDERERCACHEDSCENE_H +#pragma once -#include "RendererLib/ResourceCachedScene.h" +#include "internal/RendererLib/ResourceCachedScene.h" #include "RenderingPassInfo.h" -namespace ramses_internal +namespace ramses::internal { class IResourceDeviceHandleAccessor; @@ -63,15 +62,33 @@ namespace ramses_internal void addRenderGroupToRenderGroup (RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild, int32_t order) override; void removeRenderGroupFromRenderGroup(RenderGroupHandle groupHandleParent, RenderGroupHandle groupHandleChild) override; - BlitPassHandle allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) override; + BlitPassHandle allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) override; void releaseBlitPass(BlitPassHandle passHandle) override; void setBlitPassRenderOrder(BlitPassHandle passHandle, int32_t renderOrder) override; void setBlitPassEnabled(BlitPassHandle passHandle, bool isEnabled) override; + TextureBufferHandle allocateTextureBuffer (EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) override; + void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) override; + const RenderingPassInfoVector& getSortedRenderingPasses () const; const RenderableVector& getOrderedRenderablesForPass (RenderPassHandle pass) const; const glm::mat4& getRenderableWorldMatrix (RenderableHandle renderable) const; + using TextureBufferUpdate = std::vector; + + const TextureBufferUpdate& getTextureBufferUpdate(TextureBufferHandle handle) const + { + assert(handle.asMemoryHandle() < getTextureBufferCount()); + return m_textureBufferUpdates[handle.asMemoryHandle()]; + } + + void popTextureBufferUpdate(TextureBufferHandle handle) const + { + assert(handle.asMemoryHandle() < getTextureBufferCount()); + for (auto& mip : m_textureBufferUpdates[handle.asMemoryHandle()]) + mip = {}; + } + private: void updatePassRenderableSorting(); void updateRenderablesInPass(RenderPassHandle passHandle); @@ -89,6 +106,8 @@ namespace ramses_internal using RenderPasses = HashSet; mutable RenderPasses m_renderOncePassesToRender; + mutable std::vector m_textureBufferUpdates; + bool m_hasActiveShaderAnimation = false; }; @@ -102,5 +121,3 @@ namespace ramses_internal return m_hasActiveShaderAnimation; } } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererCommandBuffer.cpp b/src/renderer/internal/RendererLib/RendererCommandBuffer.cpp similarity index 94% rename from renderer/RendererLib/RendererLib/src/RendererCommandBuffer.cpp rename to src/renderer/internal/RendererLib/RendererCommandBuffer.cpp index 2443c0a92..3b6cc2a67 100644 --- a/renderer/RendererLib/RendererLib/src/RendererCommandBuffer.cpp +++ b/src/renderer/internal/RendererLib/RendererCommandBuffer.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { void RendererCommandBuffer::addAndConsumeCommandsFrom(RendererCommands& cmds) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandBuffer.h b/src/renderer/internal/RendererLib/RendererCommandBuffer.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandBuffer.h rename to src/renderer/internal/RendererLib/RendererCommandBuffer.h index ef921c61a..1f0386606 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandBuffer.h +++ b/src/renderer/internal/RendererLib/RendererCommandBuffer.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCOMMANDBUFFER_H -#define RAMSES_RENDERERCOMMANDBUFFER_H +#pragma once -#include "RendererLib/RendererCommands.h" +#include "internal/RendererLib/RendererCommands.h" #include #include -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer { @@ -42,5 +41,3 @@ namespace ramses_internal m_newCommandsCvar.notify_all(); } } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererCommandExecutor.cpp b/src/renderer/internal/RendererLib/RendererCommandExecutor.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/src/RendererCommandExecutor.cpp rename to src/renderer/internal/RendererLib/RendererCommandExecutor.cpp index b33e22f77..66f67c1ac 100644 --- a/renderer/RendererLib/RendererLib/src/RendererCommandExecutor.cpp +++ b/src/renderer/internal/RendererLib/RendererCommandExecutor.cpp @@ -6,21 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererCommandExecutor.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "RendererLib/Renderer.h" -#include "RendererLib/IRendererSceneUpdater.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererSceneControlLogic.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/RendererCommandUtils.h" -#include "RendererAPI/IDisplayController.h" -#include "RendererEventCollector.h" -#include "Utils/Image.h" -#include "Utils/ThreadLocalLogForced.h" - -namespace ramses_internal +#include "internal/RendererLib/RendererCommandExecutor.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/IRendererSceneUpdater.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/RendererSceneUpdater.h" +#include "internal/RendererLib/RendererCommandUtils.h" +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/Core/Utils/Image.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + +namespace ramses::internal { RendererCommandExecutor::RendererCommandExecutor(Renderer& renderer, RendererCommandBuffer& rendererCommandBuffer, IRendererSceneUpdater& sceneUpdater, IRendererSceneControlLogic& sceneControlLogic, RendererEventCollector& rendererEventCollector, FrameTimer& frameTimer) : m_renderer(renderer) @@ -80,7 +80,7 @@ namespace ramses_internal m_sceneControlLogic.setSceneState(cmd.scene, cmd.state); } - void RendererCommandExecutor::operator()(const RendererCommand::SetSceneMapping&) + void RendererCommandExecutor::operator()(const RendererCommand::SetSceneMapping& /*unused*/) { // command sets scene ownership on dispatcher level, noop here } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandExecutor.h b/src/renderer/internal/RendererLib/RendererCommandExecutor.h similarity index 96% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandExecutor.h rename to src/renderer/internal/RendererLib/RendererCommandExecutor.h index fbc7eecf9..cab239a56 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandExecutor.h +++ b/src/renderer/internal/RendererLib/RendererCommandExecutor.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCOMMANDEXECUTOR_H -#define RAMSES_RENDERERCOMMANDEXECUTOR_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "RendererLib/RendererCommands.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/RendererCommands.h" -namespace ramses_internal +namespace ramses::internal { class Renderer; class RendererCommandBuffer; @@ -83,5 +82,3 @@ namespace ramses_internal RendererCommands m_tmpCommands; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandUtils.h b/src/renderer/internal/RendererLib/RendererCommandUtils.h similarity index 96% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandUtils.h rename to src/renderer/internal/RendererLib/RendererCommandUtils.h index a2ddeae31..1686a1630 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommandUtils.h +++ b/src/renderer/internal/RendererLib/RendererCommandUtils.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCOMMANDUTILS_H -#define RAMSES_RENDERERCOMMANDUTILS_H +#pragma once -#include "RendererLib/RendererCommands.h" -#include "RendererLib/RendererEvent.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/RendererLib/RendererEvent.h" +#include "impl/RamsesFrameworkTypesImpl.h" #include -namespace ramses_internal +namespace ramses::internal { namespace RendererCommandUtils { @@ -45,9 +45,9 @@ namespace ramses_internal inline std::string ToString(const RendererCommand::SetExterallyOwnedWindowSize& cmd) { return fmt::format("SetExterallyOwnedWindowSize (displayId={} width={} height={})", cmd.display, cmd.width, cmd.height); } inline std::string ToString(const RendererCommand::ReadPixels& cmd) { return fmt::format("ReadPixels (displayId={} OB={})", cmd.display, cmd.offscreenBuffer); } inline std::string ToString(const RendererCommand::SetSkippingOfUnmodifiedBuffers& cmd) { return fmt::format("SetSkippingOfUnmodifiedBuffers (enable={})", cmd.enable); } - inline std::string ToString(const RendererCommand::LogStatistics&) { return "LogStatistics"; } - inline std::string ToString(const RendererCommand::LogInfo&) { return "LogInfo"; } - inline std::string ToString(const RendererCommand::SCListIviSurfaces&) { return "SCListIviSurfaces"; } + inline std::string ToString(const RendererCommand::LogStatistics& /*unused*/) { return "LogStatistics"; } + inline std::string ToString(const RendererCommand::LogInfo& /*unused*/) { return "LogInfo"; } + inline std::string ToString(const RendererCommand::SCListIviSurfaces& /*unused*/) { return "SCListIviSurfaces"; } inline std::string ToString(const RendererCommand::SCSetIviSurfaceVisibility& cmd) { return fmt::format("SCSetIviSurfaceVisibility (surfaceId={} visibility={})", cmd.surface, cmd.visibility); } inline std::string ToString(const RendererCommand::SCSetIviSurfaceOpacity& cmd) { return fmt::format("SCSetIviSurfaceOpacity (surfaceId={} opacity={})", cmd.surface, cmd.opacity); } inline std::string ToString(const RendererCommand::SCSetIviSurfaceDestRectangle& cmd) { return fmt::format("SCSetIviSurfaceDestRectangle (surfaceId={} x={} y={} w={} h={})", cmd.surface, cmd.x, cmd.y, cmd.width, cmd.height); } @@ -72,7 +72,7 @@ namespace ramses_internal return RendererCommand::Variant{ std::move(copy) }; } template <> - inline RendererCommand::Variant CreateVariantFrom(const RendererCommand::UpdateScene&) + inline RendererCommand::Variant CreateVariantFrom(const RendererCommand::UpdateScene& /*unused*/) { // implementation needed for visiting commands but this should never be executed assert(!"this command is not supposed to be copied"); @@ -84,7 +84,7 @@ namespace ramses_internal } template - RendererEvent GenerateFailEventForCommand(const T&) + RendererEvent GenerateFailEventForCommand(const T& /*unused*/) { return RendererEvent{ ERendererEventType::Invalid }; } @@ -163,9 +163,13 @@ namespace ramses_internal return std::holds_alternative(c) && std::get(c).scene == sceneId; }); if (it != commands.end()) + { commands.erase(it); + } else + { commands.push_back(std::move(cmd)); + } } // do not stash single time commands and frame profiler else if ( @@ -186,5 +190,3 @@ namespace ramses_internal } } } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommands.h b/src/renderer/internal/RendererLib/RendererCommands.h similarity index 81% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererCommands.h rename to src/renderer/internal/RendererLib/RendererCommands.h index 40303f3ec..7ec1ca037 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererCommands.h +++ b/src/renderer/internal/RendererLib/RendererCommands.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCOMMANDS_H -#define RAMSES_RENDERERCOMMANDS_H - -#include "SceneAPI/Handles.h" -#include "SceneAPI/TextureEnums.h" -#include "RendererAPI/Types.h" -#include "Scene/EScenePublicationMode.h" -#include "Components/SceneUpdate.h" -#include "RendererLib/DisplayConfig.h" -#include "PlatformAbstraction/VariantWrapper.h" -#include "DataTypesImpl.h" +#pragma once + +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/PlatformAbstraction/VariantWrapper.h" +#include "impl/DataTypesImpl.h" #include #include -namespace ramses_internal +namespace ramses::internal { class IBinaryShaderCache; @@ -30,7 +29,7 @@ namespace ramses_internal struct ScenePublished { SceneId scene; - EScenePublicationMode publicationMode; + EScenePublicationMode publicationMode = EScenePublicationMode::LocalAndRemote; }; struct SceneUnpublished @@ -52,7 +51,7 @@ namespace ramses_internal struct SetSceneState { SceneId scene; - RendererSceneState state; + RendererSceneState state = RendererSceneState::Unavailable; }; struct SetSceneMapping @@ -65,7 +64,7 @@ namespace ramses_internal { SceneId scene; OffscreenBufferHandle buffer; - int32_t renderOrder; + int32_t renderOrder = 0; }; struct LinkData @@ -113,7 +112,7 @@ namespace ramses_internal { DisplayHandle display; DisplayConfig config; - IBinaryShaderCache* binaryShaderCache; + IBinaryShaderCache* binaryShaderCache{nullptr}; }; struct DestroyDisplay @@ -125,19 +124,19 @@ namespace ramses_internal { DisplayHandle display; OffscreenBufferHandle offscreenBuffer; - uint32_t width; - uint32_t height; - uint32_t sampleCount; - bool interruptible; - ERenderBufferType depthStencilBufferType; + uint32_t width = 0; + uint32_t height = 0; + uint32_t sampleCount = 0; + bool interruptible = false; + EDepthBufferType depthStencilBufferType = EDepthBufferType::None; }; struct CreateDmaOffscreenBuffer { DisplayHandle display; OffscreenBufferHandle offscreenBuffer; - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; DmaBufferFourccFormat dmaBufferFourccFormat; DmaBufferUsageFlags dmaBufferUsageFlags; DmaBufferModifiers dmaBufferModifiers; @@ -178,7 +177,7 @@ namespace ramses_internal { DisplayHandle display; OffscreenBufferHandle offscreenBuffer; - uint32_t clearFlags; + ClearFlags clearFlags; }; struct SetClearColor @@ -191,8 +190,8 @@ namespace ramses_internal struct SetExterallyOwnedWindowSize { DisplayHandle display; - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; }; struct ReadPixels @@ -220,8 +219,8 @@ namespace ramses_internal struct LogInfo { - ERendererLogTopic topic; - bool verbose; + ERendererLogTopic topic = ERendererLogTopic::All; + bool verbose = false; NodeHandle nodeFilter; }; @@ -233,27 +232,27 @@ namespace ramses_internal struct SCSetIviSurfaceVisibility { WaylandIviSurfaceId surface; - bool visibility; + bool visibility = false; }; struct SCSetIviSurfaceOpacity { WaylandIviSurfaceId surface; - float opacity; + float opacity = NAN; }; struct SCSetIviSurfaceDestRectangle { WaylandIviSurfaceId surface; - int32_t x; - int32_t y; - int32_t width; - int32_t height; + int32_t x = 0; + int32_t y = 0; + int32_t width = 0; + int32_t height = 0; }; struct SCScreenshot { - int32_t screenId; + int32_t screenId = 0; std::string filename; }; @@ -266,7 +265,7 @@ namespace ramses_internal struct SCSetIviLayerVisibility { WaylandIviLayerId layer; - bool visibility; + bool visibility = false; }; struct SCRemoveIviSurfaceFromIviLayer @@ -282,19 +281,19 @@ namespace ramses_internal struct SetLimits_FrameBudgets { - uint64_t limitForSceneResourcesUploadMicrosec; - uint64_t limitForResourcesUploadMicrosec; - uint64_t limitForOffscreenBufferRenderMicrosec; + uint64_t limitForSceneResourcesUploadMicrosec = 0; + uint64_t limitForResourcesUploadMicrosec = 0; + uint64_t limitForOffscreenBufferRenderMicrosec = 0; }; struct SetLimits_FlushesForceApply { - uint32_t limitForPendingFlushesForceApply; + uint32_t limitForPendingFlushesForceApply = 0; }; struct SetLimits_FlushesForceUnsubscribe { - uint32_t limitForPendingFlushesForceUnsubscribe; + uint32_t limitForPendingFlushesForceUnsubscribe = 0; }; struct ConfirmationEcho @@ -351,5 +350,3 @@ namespace ramses_internal using RendererCommands = std::vector; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererConfig.cpp b/src/renderer/internal/RendererLib/RendererConfig.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/RendererConfig.cpp rename to src/renderer/internal/RendererLib/RendererConfig.cpp index d4e36bd97..d36bd53e2 100644 --- a/renderer/RendererLib/RendererLib/src/RendererConfig.cpp +++ b/src/renderer/internal/RendererLib/RendererConfig.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererConfig.h" -#include "Collections/StringOutputStream.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" -namespace ramses_internal +namespace ramses::internal { void RendererConfig::enableSystemCompositorControl() { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererConfig.h b/src/renderer/internal/RendererLib/RendererConfig.h similarity index 90% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererConfig.h rename to src/renderer/internal/RendererLib/RendererConfig.h index 91a3551aa..624a91cba 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererConfig.h +++ b/src/renderer/internal/RendererLib/RendererConfig.h @@ -6,21 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCONFIG_H -#define RAMSES_RENDERERCONFIG_H +#pragma once -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" #include #include -namespace ramses_internal +namespace ramses::internal { class RendererConfig { public: - RendererConfig() {} - void setWaylandDisplayForSystemCompositorController(std::string_view wd); [[nodiscard]] std::string_view getWaylandDisplayForSystemCompositorController() const; @@ -39,5 +36,3 @@ namespace ramses_internal std::chrono::milliseconds m_renderThreadLoopTimingReportingPeriod { 0 }; // zero deactivates reporting }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererEvent.h b/src/renderer/internal/RendererLib/RendererEvent.h similarity index 81% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererEvent.h rename to src/renderer/internal/RendererLib/RendererEvent.h index 19fe95d18..023ff110f 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererEvent.h +++ b/src/renderer/internal/RendererLib/RendererEvent.h @@ -6,23 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREREVENT_H -#define RAMSES_RENDEREREVENT_H - -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/RendererSceneState.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/SceneVersionTag.h" -#include "RendererLib/EMouseEventType.h" -#include "RendererLib/EKeyEventType.h" -#include "RendererLib/EKeyCode.h" -#include "RendererLib/DisplayConfig.h" -#include "Utils/LoggingUtils.h" +#pragma once + +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/RendererLib/Enums/EMouseEvent.h" +#include "internal/RendererLib/Enums/EKeyEvent.h" +#include "internal/RendererLib/Enums/EKeyCode.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/LoggingUtils.h" #include -namespace ramses_internal +namespace ramses::internal { enum class ERendererEventType { @@ -87,7 +87,6 @@ namespace ramses_internal StreamSurfaceUnavailable, ObjectsPicked, FrameTimingReport, - NUMBER_OF_ELEMENTS }; const std::array RendererEventTypeNames = @@ -154,27 +153,27 @@ namespace ramses_internal struct MouseEvent { - EMouseEventType type = EMouseEventType_Invalid; - glm::ivec2 pos; + EMouseEvent type = EMouseEvent::Invalid; + glm::ivec2 pos{}; }; struct WindowMoveEvent { - int32_t posX; - int32_t posY; + int32_t posX = 0; + int32_t posY = 0; }; struct KeyEvent { - EKeyEventType type = EKeyEventType_Invalid; - uint32_t modifier; - EKeyCode keyCode; + EKeyEvent type = EKeyEvent::Invalid; + KeyModifiers modifier; + EKeyCode keyCode{ramses::EKeyCode_Unknown}; }; struct ResizeEvent { - uint32_t width; - uint32_t height; + uint32_t width{0u}; + uint32_t height{0u}; }; struct FrameTimings @@ -191,12 +190,12 @@ namespace ramses_internal { } - ERendererEventType eventType; + ERendererEventType eventType = ERendererEventType::Invalid; SceneId sceneId; RendererSceneState state = RendererSceneState::Unavailable; DisplayHandle displayHandle; DisplayConfig displayConfig; - UInt8Vector pixelData; + std::vector pixelData; SceneId providerSceneId; SceneId consumerSceneId; DataSlotId providerdataId; @@ -211,7 +210,7 @@ namespace ramses_internal WindowMoveEvent moveEvent; WaylandIviSurfaceId streamSourceId; PickableObjectIds pickedObjectIds; - FrameTimings frameTimings; + FrameTimings frameTimings{}; int dmaBufferFD = -1; uint32_t dmaBufferStride = 0u; uint32_t textureGlId = 0u; @@ -220,12 +219,10 @@ namespace ramses_internal struct InternalSceneStateEvent { - ERendererEventType type; + ERendererEventType type = ERendererEventType::Invalid; SceneId sceneId; }; using InternalSceneStateEvents = std::vector; } -MAKE_ENUM_CLASS_PRINTABLE(ramses_internal::ERendererEventType, "ERendererEventType", ramses_internal::RendererEventTypeNames, ramses_internal::ERendererEventType::NUMBER_OF_ELEMENTS); - -#endif +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::ERendererEventType, "ERendererEventType", ramses::internal::RendererEventTypeNames, ramses::internal::ERendererEventType::FrameTimingReport); diff --git a/renderer/RendererLib/RendererLib/src/RendererEventCollector.cpp b/src/renderer/internal/RendererLib/RendererEventCollector.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/src/RendererEventCollector.cpp rename to src/renderer/internal/RendererLib/RendererEventCollector.cpp index c03210177..b1299ce6e 100644 --- a/renderer/RendererLib/RendererLib/src/RendererEventCollector.cpp +++ b/src/renderer/internal/RendererLib/RendererEventCollector.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererEventCollector.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { void RendererEventCollector::appendAndConsumePendingEvents(RendererEventVector& rendererEvents, RendererEventVector& sceneControlEvents) { @@ -46,7 +46,7 @@ namespace ramses_internal pushToRendererEventQueue(std::move(event)); } - void RendererEventCollector::addReadPixelsEvent(ERendererEventType eventType, DisplayHandle displayHandle, OffscreenBufferHandle offscreenBufferHandle, UInt8Vector&& pixelData) + void RendererEventCollector::addReadPixelsEvent(ERendererEventType eventType, DisplayHandle displayHandle, OffscreenBufferHandle offscreenBufferHandle, std::vector&& pixelData) { LOG_INFO(CONTEXT_RENDERER, eventType << " display=" << displayHandle); @@ -190,7 +190,7 @@ namespace ramses_internal void RendererEventCollector::addStreamSourceEvent(ERendererEventType eventType, WaylandIviSurfaceId streamSourceId) { - LOG_INFO(CONTEXT_RENDERER, eventType << " streamSourceId=" << streamSourceId); + LOG_INFO(CONTEXT_RENDERER, eventType << " streamSource=" << streamSourceId); RendererEvent event(eventType); event.streamSourceId = streamSourceId; diff --git a/renderer/RendererLib/RendererLib/include/RendererEventCollector.h b/src/renderer/internal/RendererLib/RendererEventCollector.h similarity index 95% rename from renderer/RendererLib/RendererLib/include/RendererEventCollector.h rename to src/renderer/internal/RendererLib/RendererEventCollector.h index b9f99c5d0..6b3a4c046 100644 --- a/renderer/RendererLib/RendererLib/include/RendererEventCollector.h +++ b/src/renderer/internal/RendererLib/RendererEventCollector.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDEREREVENTCOLLECTOR_H -#define RAMSES_RENDEREREVENTCOLLECTOR_H +#pragma once -#include "RendererLib/RendererEvent.h" +#include "internal/RendererLib/RendererEvent.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererEventCollector { @@ -23,7 +22,7 @@ namespace ramses_internal [[nodiscard]] RendererEventVector getSceneControlEvents() const; void addDisplayEvent(ERendererEventType eventType, DisplayHandle displayHandle, const DisplayConfig& config = {}); - void addReadPixelsEvent(ERendererEventType eventType, DisplayHandle displayHandle, OffscreenBufferHandle offscreenBufferHandle, UInt8Vector&& pixelData); + void addReadPixelsEvent(ERendererEventType eventType, DisplayHandle displayHandle, OffscreenBufferHandle offscreenBufferHandle, std::vector&& pixelData); void addInternalSceneEvent(ERendererEventType eventType, SceneId sceneId); void addSceneEvent(ERendererEventType eventType, SceneId sceneId, RendererSceneState state); void addSceneExpirationEvent(ERendererEventType eventType, SceneId sceneId); @@ -52,5 +51,3 @@ namespace ramses_internal InternalSceneStateEvents m_internalSceneStateEvents; }; } - -#endif diff --git a/renderer/RendererLib/RendererFramework/src/RendererFrameworkLogic.cpp b/src/renderer/internal/RendererLib/RendererFrameworkLogic.cpp similarity index 93% rename from renderer/RendererLib/RendererFramework/src/RendererFrameworkLogic.cpp rename to src/renderer/internal/RendererLib/RendererFrameworkLogic.cpp index d5f2c1a20..489b8a0c2 100644 --- a/renderer/RendererLib/RendererFramework/src/RendererFrameworkLogic.cpp +++ b/src/renderer/internal/RendererLib/RendererFrameworkLogic.cpp @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererFramework/RendererFrameworkLogic.h" -#include "Components/ISceneGraphConsumerComponent.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "Math3d/CameraMatrixHelper.h" -#include "Utils/LogMacros.h" -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" -#include "Components/ManagedResource.h" -#include "Components/SceneGraphComponent.h" -#include "SceneReferencing/SceneReferenceEvent.h" -#include "Components/SceneUpdate.h" - -namespace ramses_internal +#include "internal/RendererLib/RendererFrameworkLogic.h" +#include "internal/Components/ISceneGraphConsumerComponent.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/SceneGraphComponent.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" +#include "internal/Components/SceneUpdate.h" + +namespace ramses::internal { RendererFrameworkLogic::RendererFrameworkLogic( ISceneGraphConsumerComponent& sgc, diff --git a/renderer/RendererLib/RendererFramework/include/RendererFramework/RendererFrameworkLogic.h b/src/renderer/internal/RendererLib/RendererFrameworkLogic.h similarity index 84% rename from renderer/RendererLib/RendererFramework/include/RendererFramework/RendererFrameworkLogic.h rename to src/renderer/internal/RendererLib/RendererFrameworkLogic.h index a3db613d1..1aa773f6e 100644 --- a/renderer/RendererLib/RendererFramework/include/RendererFramework/RendererFrameworkLogic.h +++ b/src/renderer/internal/RendererLib/RendererFrameworkLogic.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERFRAMEWORKLOGIC_H -#define RAMSES_RENDERERFRAMEWORKLOGIC_H +#pragma once -#include "RendererFramework/IRendererSceneEventSender.h" -#include "Scene/SceneActionCollection.h" -#include "Collections/HashMap.h" -#include "Collections/Pair.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "Utils/Warnings.h" -#include "Components/ISceneRendererHandler.h" +#include "internal/RendererLib/IRendererSceneEventSender.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/Core/Utils/Warnings.h" +#include "internal/Components/ISceneRendererHandler.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ISceneGraphConsumerComponent; class IResourceConsumerComponent; @@ -60,5 +59,3 @@ namespace ramses_internal HashMap > m_sceneClients; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererInterruptState.h b/src/renderer/internal/RendererLib/RendererInterruptState.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererInterruptState.h rename to src/renderer/internal/RendererLib/RendererInterruptState.h index 7d5c2b4f3..08664f007 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererInterruptState.h +++ b/src/renderer/internal/RendererLib/RendererInterruptState.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERINTERRUPTSTATE_H -#define RAMSES_RENDERERINTERRUPTSTATE_H +#pragma once -#include "RendererAPI/Types.h" -#include "RendererAPI/SceneRenderExecutionIterator.h" -#include "SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/SceneRenderExecutionIterator.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" -namespace ramses_internal +namespace ramses::internal { class RendererInterruptState { @@ -61,5 +60,3 @@ namespace ramses_internal SceneRenderExecutionIterator m_executorState; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererLogContext.cpp b/src/renderer/internal/RendererLib/RendererLogContext.cpp similarity index 90% rename from renderer/RendererLib/RendererLib/src/RendererLogContext.cpp rename to src/renderer/internal/RendererLib/RendererLogContext.cpp index d53449c32..7cccdcf3c 100644 --- a/renderer/RendererLib/RendererLib/src/RendererLogContext.cpp +++ b/src/renderer/internal/RendererLib/RendererLogContext.cpp @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererLogContext.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "PlatformAbstraction/Macros.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/PlatformAbstraction/Macros.h" #include -namespace ramses_internal +namespace ramses::internal { const RendererLogContext::NewLineType RendererLogContext::NewLine; RendererLogContext::RendererLogContext(ERendererLogLevelFlag logLevel) : m_activeLogLevelFlags(GetActiveLogLevelFlags(logLevel)) - , m_nodeHandleFilter() , m_lineActive(false) { } @@ -62,7 +61,7 @@ namespace ramses_internal { auto resultString = filter.substr(1u, filter.size()); - const size_t lastCharIndex = std::max(0, resultString.size() - 1); + const size_t lastCharIndex{resultString.size() > 1 ? resultString.size() - 1 : 0}; if (resultString[lastCharIndex] == '*') { resultString = resultString.substr(0, lastCharIndex); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererLogContext.h b/src/renderer/internal/RendererLib/RendererLogContext.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererLogContext.h rename to src/renderer/internal/RendererLib/RendererLogContext.h index 4e3ce1cb3..996f1d152 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererLogContext.h +++ b/src/renderer/internal/RendererLib/RendererLogContext.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERLOGCONTEXT_H -#define RAMSES_RENDERERLOGCONTEXT_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Collections/StringOutputStream.h" -#include "Common/BitForgeMacro.h" -#include "SceneAPI/Handles.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Common/BitForgeMacro.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include #include -namespace ramses_internal +namespace ramses::internal { enum ERendererLogLevelFlag : uint32_t { @@ -56,7 +55,6 @@ namespace ramses_internal struct NewLineType { - NewLineType() {} }; public: @@ -76,12 +74,10 @@ namespace ramses_internal } template <> - inline RendererLogContext& RendererLogContext::operator << (const RendererLogContext::NewLineType&) + inline RendererLogContext& RendererLogContext::operator << (const RendererLogContext::NewLineType& /*unused*/) { m_stream << "\n"; m_lineActive = false; return *this; } } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererLogger.cpp b/src/renderer/internal/RendererLib/RendererLogger.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/RendererLogger.cpp rename to src/renderer/internal/RendererLib/RendererLogger.cpp index 04aae3c98..5604fb56f 100644 --- a/renderer/RendererLib/RendererLib/src/RendererLogger.cpp +++ b/src/renderer/internal/RendererLib/RendererLogger.cpp @@ -6,36 +6,36 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IWindow.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RendererAPI/IContext.h" -#include "RendererLib/RendererLogger.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/Renderer.h" -#include "RendererLib/DisplayController.h" -#include "RendererLib/SceneStateInfo.h" -#include "RendererLib/EResourceStatus.h" -#include "RendererLib/ResourceDescriptor.h" -#include "RendererLib/RendererResourceManager.h" -#include "RendererLib/LoggingDevice.h" -#include "RendererLib/TransformationLinkManager.h" -#include "RendererLib/DataReferenceLinkManager.h" -#include "RendererLib/SceneLinks.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/DisplaySetup.h" -#include "RendererLib/StagingInfo.h" -#include "RendererLib/SceneReferenceLogic.h" -#include "RenderExecutor.h" -#include "RenderExecutorLogger.h" -#include "RendererEventCollector.h" -#include "Platform_Base/DeviceResourceMapper.h" -#include "Utils/ThreadLocalLogForced.h" - -namespace ramses_internal +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/RendererLogger.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/RendererSceneUpdater.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/DisplayController.h" +#include "internal/RendererLib/SceneStateInfo.h" +#include "internal/RendererLib/Enums/EResourceStatus.h" +#include "internal/RendererLib/ResourceDescriptor.h" +#include "internal/RendererLib/RendererResourceManager.h" +#include "internal/RendererLib/LoggingDevice.h" +#include "internal/RendererLib/TransformationLinkManager.h" +#include "internal/RendererLib/DataReferenceLinkManager.h" +#include "internal/RendererLib/SceneLinks.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/DisplaySetup.h" +#include "internal/RendererLib/StagingInfo.h" +#include "internal/RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/RenderExecutor.h" +#include "internal/RendererLib/RenderExecutorLogger.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/RendererLib/PlatformBase/DeviceResourceMapper.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + +namespace ramses::internal { void RendererLogger::StartSection(std::string_view name, RendererLogContext& context) { @@ -149,9 +149,13 @@ namespace ramses_internal const auto& stagingInfo = updater.m_rendererScenes.getStagingInfo(sceneId); context << "Version tag: "; if (stagingInfo.lastAppliedVersionTag.isValid()) + { context << stagingInfo.lastAppliedVersionTag; + } else + { context << ""; + } context << RendererLogContext::NewLine; if (context.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)) @@ -205,7 +209,7 @@ namespace ramses_internal { context << "Width: " << width << RendererLogContext::NewLine; context << "Height: " << height << RendererLogContext::NewLine; - context << "IviSurfaceId: " << surfaceId.getValue() << RendererLogContext::NewLine; + context << surfaceId << RendererLogContext::NewLine; if (context.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)) { const auto& renderer = updater.m_renderer; @@ -272,7 +276,7 @@ namespace ramses_internal StartSection("RENDERER CLIENT RESOURCES", context); - const RendererResourceManager* resourceManager = static_cast(updater.m_displayResourceManager.get()); + const auto* resourceManager = static_cast(updater.m_displayResourceManager.get()); context.indent(); context << resourceManager->m_resourceRegistry.getAllResourceDescriptors().size() << " Resources" << RendererLogContext::NewLine << RendererLogContext::NewLine; @@ -281,7 +285,7 @@ namespace ramses_internal context.indent(); for (uint32_t i = 0; i < static_cast(EResourceStatus::Broken) + 1u; i++) { - const EResourceStatus status = static_cast(i); + const auto status = static_cast(i); uint32_t count = 0; for(const auto& descriptorIt : resourceManager->m_resourceRegistry.getAllResourceDescriptors()) @@ -308,7 +312,7 @@ namespace ramses_internal //Infos by resource type for (uint32_t i = 0; i < EResourceType_NUMBER_OF_ELEMENTS; i++) { - const EResourceType type = static_cast(i); + const auto type = static_cast(i); uint32_t count = 0; uint32_t compressedSize = 0; @@ -318,8 +322,10 @@ namespace ramses_internal ResourceContentHashVector resources; for (const auto& resourceIt : resourceManager->m_resourceRegistry.getAllResourceDescriptors()) + { if (resourceIt.value.type == type) resources.push_back(resourceIt.value.hash); + } std::sort(resources.begin(), resources.end(), [&](ResourceContentHash r1, ResourceContentHash r2) { @@ -382,7 +388,7 @@ namespace ramses_internal StartSection("RENDERER SCENE RESOURCES", context); context << "Resources for " << updater.m_rendererScenes.size() << " Scene(s)" << RendererLogContext::NewLine << RendererLogContext::NewLine; - const RendererResourceManager& resourceManager = static_cast(*updater.m_displayResourceManager); + const auto& resourceManager = static_cast(*updater.m_displayResourceManager); for (const auto& sceneResRegistryIt : resourceManager.m_sceneResourceRegistryMap) { context.indent(); @@ -394,7 +400,7 @@ namespace ramses_internal context.indent(); const auto& renderBuffers = scene.getRenderBuffers(); - const auto renderBufferCount = std::count_if(renderBuffers.cbegin(), renderBuffers.cend(), [](const auto&) {return true; }); + const auto renderBufferCount = std::count_if(renderBuffers.cbegin(), renderBuffers.cend(), [](const auto& /*unused*/) {return true; }); context << "Render buffers: " << renderBufferCount << RendererLogContext::NewLine; { uint32_t size = 0u; @@ -406,7 +412,7 @@ namespace ramses_internal { const auto deviceHandle = resRegistry.getRenderBufferDeviceHandle(rb.first); context << rb.first << " (deviceHandle=" << deviceHandle << " GPUhandle=" << device.getGPUHandle(deviceHandle) << ") "; - context << "[" << rbDesc.width << "x" << rbDesc.height << "; " << EnumToString(rbDesc.type) << "; " << EnumToString(rbDesc.format) << "; " << EnumToString(rbDesc.accessMode) << "; " << rbDesc.sampleCount << " samples] "; + context << "[" << rbDesc.width << "x" << rbDesc.height << "; " << EnumToString(rbDesc.format) << "; " << EnumToString(rbDesc.accessMode) << "; " << rbDesc.sampleCount << " samples] "; context << resRegistry.getRenderBufferByteSize(rb.first) / 1024 << " KB"; context << RendererLogContext::NewLine; } @@ -417,7 +423,7 @@ namespace ramses_internal } const auto& renderTargets = scene.getRenderTargets(); - const auto renderTargetCount = std::count_if(renderTargets.cbegin(), renderTargets.cend(), [](const auto&) {return true; }); + const auto renderTargetCount = std::count_if(renderTargets.cbegin(), renderTargets.cend(), [](const auto& /*unused*/) {return true; }); context << "Render targets: " << renderTargetCount << RendererLogContext::NewLine; if (context.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)) { @@ -435,7 +441,7 @@ namespace ramses_internal } const auto& blitPasses = scene.getBlitPasses(); - const auto blitPassCount = std::count_if(blitPasses.cbegin(), blitPasses.cend(), [](const auto&) {return true; }); + const auto blitPassCount = std::count_if(blitPasses.cbegin(), blitPasses.cend(), [](const auto& /*unused*/) {return true; }); context << "Blit passes: " << blitPassCount << RendererLogContext::NewLine; if (context.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)) { @@ -452,7 +458,7 @@ namespace ramses_internal } const auto& textureBuffers = scene.getTextureBuffers(); - const auto textureBufferCount = std::count_if(textureBuffers.cbegin(), textureBuffers.cend(), [](const auto&) {return true; }); + const auto textureBufferCount = std::count_if(textureBuffers.cbegin(), textureBuffers.cend(), [](const auto& /*unused*/) {return true; }); context << "Texture buffers: " << textureBufferCount << RendererLogContext::NewLine; { uint32_t size = 0u; @@ -476,7 +482,7 @@ namespace ramses_internal } const auto& dataBuffers = scene.getDataBuffers(); - const auto dataBufferCount = std::count_if(dataBuffers.cbegin(), dataBuffers.cend(), [](const auto&) {return true; }); + const auto dataBufferCount = std::count_if(dataBuffers.cbegin(), dataBuffers.cend(), [](const auto& /*unused*/) {return true; }); context << "Data buffers: " << dataBufferCount << RendererLogContext::NewLine; { uint32_t size = 0u; @@ -541,7 +547,7 @@ namespace ramses_internal StartSection("RENDERER MISSING RESOURCES", context); HashMap> missingResourcesPerScene; - const RendererResourceManager* resourceManager = static_cast(updater.m_displayResourceManager.get()); + const auto* resourceManager = static_cast(updater.m_displayResourceManager.get()); const auto& resourceDescriptors = resourceManager->m_resourceRegistry.getAllResourceDescriptors(); for (const auto& descriptorIt : resourceDescriptors) { @@ -667,7 +673,7 @@ namespace ramses_internal { context << "Display Buffer device handle: " << buffer << RendererLogContext::NewLine; - const DisplayController& displayController = static_cast(*updater.m_renderer.m_displayController); + const auto& displayController = static_cast(*updater.m_renderer.m_displayController); const auto& displayBuffers = updater.m_renderer.m_displayBuffersSetup.getDisplayBuffers(); const DisplayBufferInfo& bufferInfo = displayBuffers.find(buffer)->second; @@ -682,7 +688,7 @@ namespace ramses_internal displayController.m_device.getFramebufferRenderTarget(), displayController.getDisplayWidth(), displayController.getDisplayHeight(), SceneRenderExecutionIterator{}, - EClearFlags_All, + EClearFlag::All, glm::vec4{ 0.f }, false }; @@ -743,12 +749,12 @@ namespace ramses_internal { context << " [ "; for (const auto& scene : masterInfo.expirationStates) - context << scene.first << "/" << scene.second << " "; + context << scene.first << "/" << fmt::underlying(scene.second) << " "; context << "]"; } context << RendererLogContext::NewLine; - context << "- consolidated expiration state: " << masterInfo.consolidatedExpirationState << RendererLogContext::NewLine; + context << "- consolidated expiration state: " << fmt::underlying(masterInfo.consolidatedExpirationState) << RendererLogContext::NewLine; context << "- master destroyed: " << masterInfo.destroyed << RendererLogContext::NewLine; context.unindent(); @@ -780,7 +786,7 @@ namespace ramses_internal for (const auto& slot : scene.getDataSlots()) { const EDataSlotType slotType = slot.second->type; - if (slotType == EDataSlotType_TransformationProvider || slotType == EDataSlotType_DataProvider || slotType == EDataSlotType_TextureProvider) + if (slotType == EDataSlotType::TransformationProvider || slotType == EDataSlotType::DataProvider || slotType == EDataSlotType::TextureProvider) { LogProvider(scenes, context, scene, slot.first); slotCount++; @@ -795,7 +801,7 @@ namespace ramses_internal for (const auto& slot : scene.getDataSlots()) { const EDataSlotType slotType = slot.second->type; - if (slotType == EDataSlotType_TransformationConsumer || slotType == EDataSlotType_DataConsumer || slotType == EDataSlotType_TextureConsumer) + if (slotType == EDataSlotType::TransformationConsumer || slotType == EDataSlotType::DataConsumer || slotType == EDataSlotType::TextureConsumer) { LogConsumer(scenes, context, scene, slot.first); slotCount++; @@ -897,29 +903,29 @@ namespace ramses_internal const DataSlot& dataSlot = scene.getDataSlot(slotHandle); switch (dataSlot.type) { - case EDataSlotType_TransformationProvider: - case EDataSlotType_TransformationConsumer: + case EDataSlotType::TransformationProvider: + case EDataSlotType::TransformationConsumer: { context << "type: transformation; "; context << "node: \"" << dataSlot.attachedNode.asMemoryHandle() << "\"; "; } break; - case EDataSlotType_DataProvider: - case EDataSlotType_DataConsumer: + case EDataSlotType::DataProvider: + case EDataSlotType::DataConsumer: { const EDataType datatype = GetDataTypeForSlot(scene, slotHandle); context << "type: data; "; context << "datatype: " << EnumToString(datatype) << "; "; } break; - case EDataSlotType_TextureProvider: + case EDataSlotType::TextureProvider: { const ResourceContentHash& hash = dataSlot.attachedTexture; context << "type: texture; "; context << "hash: " << hash << "; "; } break; - case EDataSlotType_TextureConsumer: + case EDataSlotType::TextureConsumer: { const TextureSamplerHandle& handle = dataSlot.attachedTextureSampler; context << "type: texture; "; @@ -935,14 +941,14 @@ namespace ramses_internal { switch (type) { - case EDataSlotType_TransformationProvider: - case EDataSlotType_TransformationConsumer: + case EDataSlotType::TransformationProvider: + case EDataSlotType::TransformationConsumer: return linkManager.getTransformationLinkManager().getSceneLinks(); - case EDataSlotType_DataProvider: - case EDataSlotType_DataConsumer: + case EDataSlotType::DataProvider: + case EDataSlotType::DataConsumer: return linkManager.getDataReferenceLinkManager().getSceneLinks(); - case EDataSlotType_TextureProvider: - case EDataSlotType_TextureConsumer: + case EDataSlotType::TextureProvider: + case EDataSlotType::TextureConsumer: return linkManager.getTextureLinkManager().getSceneLinks(); default: assert(false); @@ -1072,6 +1078,12 @@ namespace ramses_internal } sos << "\n"; + if (updater.m_renderer.hasDisplayController()) + { + const auto& ec = updater.m_renderer.getDisplayController().getRenderBackend().getEmbeddedCompositor(); + ec.logPeriodicInfo(sos); + } + updater.m_renderer.getStatistics().writeStatsToStream(sos); sos << "\nTime budgets:" << " sceneResourceUpload " << int64_t(updater.m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::SceneResourcesUpload).count()) << "us" diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererLogger.h b/src/renderer/internal/RendererLib/RendererLogger.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererLogger.h rename to src/renderer/internal/RendererLib/RendererLogger.h index 65c90af9f..f260ba7ec 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererLogger.h +++ b/src/renderer/internal/RendererLib/RendererLogger.h @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERLOGGER_H -#define RAMSES_RENDERERLOGGER_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LogContext.h" -#include "Utils/LoggingUtils.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/EDataType.h" -#include "SceneAPI/EDataSlotType.h" -#include "RendererAPI/Types.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EDataSlotType.h" +#include "internal/RendererLib/Types.h" +#include #include -namespace ramses_internal + +namespace ramses::internal { class RendererSceneUpdater; class RendererLogContext; @@ -32,11 +32,10 @@ namespace ramses_internal class RendererLogger { public: + RendererLogger() = delete; static void LogTopic(const RendererSceneUpdater& updater, ERendererLogTopic topic, bool verbose, NodeHandle nodeHandleFilter = NodeHandle::Invalid()); private: - RendererLogger(); - static void LogSceneStates(const RendererSceneUpdater& updater, RendererLogContext& context); static void LogDisplays(const RendererSceneUpdater& updater, RendererLogContext& context); static void LogDisplayBuffer(const RendererSceneUpdater& updater, DeviceResourceHandle bufferHandle, const DisplayBufferInfo& dispBufferInfo, RendererLogContext& context); @@ -66,5 +65,3 @@ namespace ramses_internal static void Log(const LogContext& logContext, const RendererLogContext& context); }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererPeriodicLogSupplier.cpp b/src/renderer/internal/RendererLib/RendererPeriodicLogSupplier.cpp similarity index 85% rename from renderer/RendererLib/RendererLib/src/RendererPeriodicLogSupplier.cpp rename to src/renderer/internal/RendererLib/RendererPeriodicLogSupplier.cpp index 102e2b825..4b76630c6 100644 --- a/renderer/RendererLib/RendererLib/src/RendererPeriodicLogSupplier.cpp +++ b/src/renderer/internal/RendererLib/RendererPeriodicLogSupplier.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererPeriodicLogSupplier.h" -#include "RendererLib/RendererCommandBuffer.h" -#include "Utils/PeriodicLogger.h" +#include "internal/RendererLib/RendererPeriodicLogSupplier.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/Core/Utils/PeriodicLogger.h" -namespace ramses_internal +namespace ramses::internal { RendererPeriodicLogSupplier::RendererPeriodicLogSupplier(PeriodicLogger& periodicLogger, RendererCommandBuffer& commandBuffer) : m_periodicLogger(periodicLogger) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererPeriodicLogSupplier.h b/src/renderer/internal/RendererLib/RendererPeriodicLogSupplier.h similarity index 85% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererPeriodicLogSupplier.h rename to src/renderer/internal/RendererLib/RendererPeriodicLogSupplier.h index 8e53e0a78..035a6d11a 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererPeriodicLogSupplier.h +++ b/src/renderer/internal/RendererLib/RendererPeriodicLogSupplier.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERPERIODICLOGSUPPLIER_H -#define RAMSES_RENDERERPERIODICLOGSUPPLIER_H +#pragma once -#include "Utils/IPeriodicLogSupplier.h" +#include "internal/Core/Utils/IPeriodicLogSupplier.h" -namespace ramses_internal +namespace ramses::internal { class PeriodicLogger; class RendererCommandBuffer; @@ -29,5 +28,3 @@ namespace ramses_internal RendererCommandBuffer& m_commandBuffer; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererResourceManager.cpp b/src/renderer/internal/RendererLib/RendererResourceManager.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/RendererResourceManager.cpp rename to src/renderer/internal/RendererLib/RendererResourceManager.cpp index dcfe72a6f..3dacfcac8 100644 --- a/renderer/RendererLib/RendererLib/src/RendererResourceManager.cpp +++ b/src/renderer/internal/RendererLib/RendererResourceManager.cpp @@ -6,25 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererResourceManager.h" -#include "RendererLib/RendererResourceManagerUtils.h" -#include "RendererLib/IResourceUploader.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "RendererAPI/IRendererResourceCache.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/EDataBufferType.h" -#include "Components/ManagedResource.h" -#include "Resource/ResourceInfo.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/TextureMathUtils.h" -#include "Utils/LogMacros.h" +#include "internal/RendererLib/RendererResourceManager.h" +#include "internal/RendererLib/IResourceUploader.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" +#include "internal/Components/ManagedResource.h" +#include "internal/SceneGraph/Resource/ResourceInfo.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/TextureMathUtils.h" +#include "internal/Core/Utils/LogMacros.h" #include -namespace ramses_internal +namespace ramses::internal { RendererResourceManager::RendererResourceManager( IRenderBackend& renderBackend, @@ -65,9 +63,8 @@ namespace ramses_internal unloadExternalBuffer(handle); } - for (const auto& resDesc : m_resourceRegistry.getAllResourceDescriptors()) + for (const auto& resDesc [[maybe_unused]] : m_resourceRegistry.getAllResourceDescriptors()) { - UNUSED(resDesc); assert(resDesc.value.sceneUsage.empty()); } } @@ -107,7 +104,7 @@ namespace ramses_internal device.clearColor({ 0.f, 0.f, 0.f, 1.f }); device.depthWrite(EDepthWrite::Enabled); device.scissorTest(EScissorTest::Disabled, {}); - device.clear(EClearFlags_All); + device.clear(EClearFlag::All); } void RendererResourceManager::unloadAllSceneResourcesForScene(SceneId sceneId) @@ -309,12 +306,12 @@ namespace ramses_internal void RendererResourceManager::uploadRenderTargetBuffer(RenderBufferHandle renderBufferHandle, SceneId sceneId, const RenderBuffer& renderBuffer) { const uint32_t memSize = renderBuffer.width * renderBuffer.height * GetTexelSizeFromFormat(renderBuffer.format) * std::max(1u, renderBuffer.sampleCount); - LOG_INFO_P(CONTEXT_RENDERER, "RendererResourceManager::uploadRenderTargetBuffer sceneId={} handle={} {}x{} {} {} samples={} estimatedSize={}KB", sceneId, renderBufferHandle, - renderBuffer.width, renderBuffer.height, EnumToString(renderBuffer.type), EnumToString(renderBuffer.format), renderBuffer.sampleCount, memSize/1024); + LOG_INFO_P(CONTEXT_RENDERER, "RendererResourceManager::uploadRenderTargetBuffer sceneId={} handle={} {}x{} {} samples={} estimatedSize={}KB", sceneId, renderBufferHandle, + renderBuffer.width, renderBuffer.height, EnumToString(renderBuffer.format), renderBuffer.sampleCount, memSize/1024); RendererSceneResourceRegistry& sceneResources = getSceneResourceRegistry(sceneId); IDevice& device = m_renderBackend.getDevice(); - const DeviceResourceHandle deviceHandle = device.uploadRenderBuffer(renderBuffer.width, renderBuffer.height, renderBuffer.type, renderBuffer.format, renderBuffer.accessMode, renderBuffer.sampleCount); + const DeviceResourceHandle deviceHandle = device.uploadRenderBuffer(renderBuffer.width, renderBuffer.height, renderBuffer.format, renderBuffer.accessMode, renderBuffer.sampleCount); assert(deviceHandle.isValid()); if (!deviceHandle.isValid()) { @@ -322,7 +319,7 @@ namespace ramses_internal std::ignore = device.isDeviceStatusHealthy(); } - sceneResources.addRenderBuffer(renderBufferHandle, deviceHandle, memSize, ERenderBufferAccessMode_WriteOnly == renderBuffer.accessMode); + sceneResources.addRenderBuffer(renderBufferHandle, deviceHandle, memSize, ERenderBufferAccessMode::WriteOnly == renderBuffer.accessMode); m_stats.sceneResourceUploaded(sceneId, memSize); } @@ -341,9 +338,9 @@ namespace ramses_internal void RendererResourceManager::uploadRenderTarget(RenderTargetHandle renderTarget, const RenderBufferHandleVector& rtBufferHandles, SceneId sceneId) { LOG_INFO_PF(CONTEXT_RENDERER, ([&](auto& out) { - fmt::format_to(out, "RendererResourceManager::uploadRenderTarget sceneId={} handle={} renderBufferHandles:", sceneId, renderTarget); + fmt::format_to(std::back_inserter(out), "RendererResourceManager::uploadRenderTarget sceneId={} handle={} renderBufferHandles:", sceneId, renderTarget); for (const auto rb : rtBufferHandles) - fmt::format_to(out, " {}", rb); + fmt::format_to(std::back_inserter(out), " {}", rb); })); assert(!rtBufferHandles.empty()); @@ -388,7 +385,7 @@ namespace ramses_internal offscreenBufferDesc.m_colorBufferHandle[0] = device.uploadDmaRenderBuffer(width, height, dmaBufferFourccFormat, dmaBufferUsageFlags, dmaBufferModifiers); - offscreenBufferDesc.m_estimatedVRAMUsage = width * height * GetTexelSizeFromFormat(ETextureFormat::RGBA8); + offscreenBufferDesc.m_estimatedVRAMUsage = width * height * GetTexelSizeFromFormat(EPixelStorageFormat::RGBA8); LOG_INFO_P(CONTEXT_RENDERER, "RendererResourceManager::uploadDmaOffscreenBuffer handle={} {}x{} fourccFormat={} usage={} modifier={} estimatedSize(RGBA8)={}KB", bufferHandle, width, height, dmaBufferFourccFormat.getValue(), dmaBufferUsageFlags.getValue(), dmaBufferModifiers.getValue(), offscreenBufferDesc.m_estimatedVRAMUsage / 1024); @@ -397,7 +394,7 @@ namespace ramses_internal clearRenderTarget(offscreenBufferDesc.m_renderTargetHandle[0]); } - void RendererResourceManager::uploadOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType) + void RendererResourceManager::uploadOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType) { assert(!m_offscreenBuffers.isAllocated(bufferHandle)); IDevice& device = m_renderBackend.getDevice(); @@ -406,30 +403,29 @@ namespace ramses_internal OffscreenBufferDescriptor& offscreenBufferDesc = *m_offscreenBuffers.getMemory(bufferHandle); offscreenBufferDesc.isDmaBuffer = false; - offscreenBufferDesc.m_colorBufferHandle[0] = device.uploadRenderBuffer(width, height, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, sampleCount); + offscreenBufferDesc.m_colorBufferHandle[0] = device.uploadRenderBuffer(width, height, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, sampleCount); - uint32_t texelSize = GetTexelSizeFromFormat(ETextureFormat::RGBA8) * (isDoubleBuffered ? 2u : 1u); // only color buffer is double + uint32_t texelSize = GetTexelSizeFromFormat(EPixelStorageFormat::RGBA8) * (isDoubleBuffered ? 2u : 1u); // only color buffer is double switch (depthStencilBufferType) { - case ERenderBufferType_InvalidBuffer: + case EDepthBufferType::None: offscreenBufferDesc.m_depthBufferHandle = {}; break; - case ERenderBufferType_DepthBuffer: - offscreenBufferDesc.m_depthBufferHandle = device.uploadRenderBuffer(width, height, ERenderBufferType_DepthBuffer, ETextureFormat::Depth24, ERenderBufferAccessMode_WriteOnly, sampleCount); - texelSize += 4; // using 32bits for memory estimation which is probably what driver will allocate anyway + case EDepthBufferType::Depth: + offscreenBufferDesc.m_depthBufferHandle = + device.uploadRenderBuffer(width, height, EPixelStorageFormat::Depth32, ERenderBufferAccessMode::WriteOnly, sampleCount); + texelSize += GetTexelSizeFromFormat(EPixelStorageFormat::Depth32); break; - case ERenderBufferType_DepthStencilBuffer: - offscreenBufferDesc.m_depthBufferHandle = device.uploadRenderBuffer(width, height, ERenderBufferType_DepthStencilBuffer, ETextureFormat::Depth24_Stencil8, ERenderBufferAccessMode_WriteOnly, sampleCount); - texelSize += 4; + case EDepthBufferType::DepthStencil: + offscreenBufferDesc.m_depthBufferHandle = + device.uploadRenderBuffer(width, height, EPixelStorageFormat::Depth24_Stencil8, ERenderBufferAccessMode::WriteOnly, sampleCount); + texelSize += GetTexelSizeFromFormat(EPixelStorageFormat::Depth24_Stencil8); break; - case ERenderBufferType_ColorBuffer: - case ERenderBufferType_NUMBER_OF_ELEMENTS: - assert(false); } offscreenBufferDesc.m_estimatedVRAMUsage = width * height * std::max(1u, sampleCount) * texelSize; LOG_INFO_P(CONTEXT_RENDERER, "RendererResourceManager::uploadOffscreenBuffer handle={} {}x{} samples={} interruptible={} depthType={} estimatedSize={}KB", bufferHandle, - width, height, sampleCount, isDoubleBuffered, EnumToString(depthStencilBufferType), offscreenBufferDesc.m_estimatedVRAMUsage / 1024); + width, height, sampleCount, isDoubleBuffered, depthStencilBufferType, offscreenBufferDesc.m_estimatedVRAMUsage / 1024); DeviceHandleVector bufferDeviceHandles; bufferDeviceHandles.push_back(offscreenBufferDesc.m_colorBufferHandle[0]); @@ -439,7 +435,7 @@ namespace ramses_internal if (isDoubleBuffered) { - offscreenBufferDesc.m_colorBufferHandle[1] = device.uploadRenderBuffer(width, height, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, sampleCount); + offscreenBufferDesc.m_colorBufferHandle[1] = device.uploadRenderBuffer(width, height, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, sampleCount); DeviceHandleVector bufferDeviceHandles2; bufferDeviceHandles2.push_back(offscreenBufferDesc.m_colorBufferHandle[1]); @@ -468,8 +464,10 @@ namespace ramses_internal device.deleteRenderTarget(offscreenBufferDesc.m_renderTargetHandle[0]); - if(offscreenBufferDesc.isDmaBuffer) + if (offscreenBufferDesc.isDmaBuffer) + { device.destroyDmaRenderBuffer(offscreenBufferDesc.m_colorBufferHandle[0]); + } else { if (offscreenBufferDesc.m_renderTargetHandle[1].isValid()) @@ -655,7 +653,7 @@ namespace ramses_internal sceneResources.removeDataBuffer(dataBufferHandle); } - void RendererResourceManager::updateDataBuffer(DataBufferHandle handle, uint32_t dataSizeInBytes, const Byte* data, SceneId sceneId) + void RendererResourceManager::updateDataBuffer(DataBufferHandle handle, uint32_t dataSizeInBytes, const std::byte* data, SceneId sceneId) { assert(m_sceneResourceRegistryMap.contains(sceneId)); const RendererSceneResourceRegistry& sceneResources = *m_sceneResourceRegistryMap.get(sceneId); @@ -687,10 +685,10 @@ namespace ramses_internal return sceneResources.getDataBufferDeviceHandle(dataBufferHandle); } - void RendererResourceManager::uploadTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, ETextureFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId) + void RendererResourceManager::uploadTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId) { assert(textureBufferHandle.isValid()); - assert(ETextureFormat::Invalid != textureFormat); + assert(EPixelStorageFormat::Invalid != textureFormat); assert(!IsFormatCompressed(textureFormat)); //can not be a compressed format RendererSceneResourceRegistry& sceneResources = getSceneResourceRegistry(sceneId); @@ -724,7 +722,7 @@ namespace ramses_internal sceneResources.removeTextureBuffer(textureBufferHandle); } - void RendererResourceManager::updateTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data, SceneId sceneId) + void RendererResourceManager::updateTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t mipLevel, const Quad& area, uint32_t stride, const std::byte* data, SceneId sceneId) { assert(m_sceneResourceRegistryMap.contains(sceneId)); const RendererSceneResourceRegistry& sceneResources = *m_sceneResourceRegistryMap.get(sceneId); @@ -734,9 +732,9 @@ namespace ramses_internal IDevice& device = m_renderBackend.getDevice(); device.bindTexture(deviceHandle); - device.uploadTextureData(deviceHandle, mipLevel, x, y, 0u, width, height, 1u, data, 0u); + device.uploadTextureData(deviceHandle, mipLevel, area.x, area.y, 0u, area.width, area.height, 1u, data, 0u, stride); - const uint32_t updateDataSizeInBytes = TextureMathUtils::GetTotalMemoryUsedByMipmappedTexture(GetTexelSizeFromFormat(sceneResources.getTextureBufferFormat(textureBufferHandle)), width, height, 1u, 1u); + const uint32_t updateDataSizeInBytes = TextureMathUtils::GetTotalMemoryUsedByMipmappedTexture(GetTexelSizeFromFormat(sceneResources.getTextureBufferFormat(textureBufferHandle)), area.width, area.height, 1u, 1u); m_stats.sceneResourceUploaded(sceneId, updateDataSizeInBytes); } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManager.h b/src/renderer/internal/RendererLib/RendererResourceManager.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManager.h rename to src/renderer/internal/RendererLib/RendererResourceManager.h index 4f39f38ec..ced296267 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceManager.h +++ b/src/renderer/internal/RendererLib/RendererResourceManager.h @@ -6,32 +6,29 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERRESOURCEMANAGER_H -#define RAMSES_RENDERERRESOURCEMANAGER_H +#pragma once #include "IRendererResourceManager.h" -#include "RendererLib/RendererResourceRegistry.h" -#include "RendererLib/RendererSceneResourceRegistry.h" -#include "RendererLib/ResourceUploadingManager.h" -#include "RendererResourceManagerUtils.h" -#include "Collections/HashMap.h" -#include "Collections/Vector.h" -#include "Utils/MemoryPool.h" +#include "internal/RendererLib/RendererResourceRegistry.h" +#include "internal/RendererLib/RendererSceneResourceRegistry.h" +#include "internal/RendererLib/ResourceUploadingManager.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Core/Utils/MemoryPool.h" #include -namespace ramses_internal +namespace ramses::internal { class IRenderBackend; class AsyncEffectUploader; class IEmbeddedCompositingManager; - class IRendererResourceCache; class FrameTimer; class RendererStatistics; class IBinaryShaderCache; class IResourceUploader; class DisplayConfig; - class RendererResourceManager : public IRendererResourceManager + class RendererResourceManager final : public IRendererResourceManager { public: RendererResourceManager( @@ -57,7 +54,7 @@ namespace ramses_internal [[nodiscard]] EResourceType getResourceType(const ResourceContentHash& hash) const override; // Scene resources - [[nodiscard]] DeviceResourceHandle getRenderTargetDeviceHandle(RenderTargetHandle, SceneId sceneId) const override; + [[nodiscard]] DeviceResourceHandle getRenderTargetDeviceHandle(RenderTargetHandle targetHandle, SceneId sceneId) const override; [[nodiscard]] DeviceResourceHandle getRenderTargetBufferDeviceHandle(RenderBufferHandle bufferHandle, SceneId sceneId) const override; void getBlitPassRenderTargetsDeviceHandle(BlitPassHandle blitPassHandle, SceneId sceneId, DeviceResourceHandle& srcRT, DeviceResourceHandle& dstRT) const override; @@ -67,7 +64,7 @@ namespace ramses_internal void uploadRenderTarget(RenderTargetHandle renderTarget, const RenderBufferHandleVector& rtBufferHandles, SceneId sceneId) override; void unloadRenderTarget(RenderTargetHandle renderTarget, SceneId sceneId) override; - void uploadOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType) override; + void uploadOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType) override; void uploadDmaOffscreenBuffer(OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers) override; void unloadOffscreenBuffer(OffscreenBufferHandle bufferHandle) override; @@ -82,12 +79,12 @@ namespace ramses_internal void uploadDataBuffer(DataBufferHandle dataBufferHandle, EDataBufferType dataBufferType, EDataType dataType, uint32_t dataSizeInBytes, SceneId sceneId) override; void unloadDataBuffer(DataBufferHandle dataBufferHandle, SceneId sceneId) override; - void updateDataBuffer(DataBufferHandle handle, uint32_t dataSizeInBytes, const Byte* data, SceneId sceneId) override; + void updateDataBuffer(DataBufferHandle handle, uint32_t dataSizeInBytes, const std::byte* data, SceneId sceneId) override; [[nodiscard]] DeviceResourceHandle getDataBufferDeviceHandle(DataBufferHandle dataBufferHandle, SceneId sceneId) const override; - void uploadTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, ETextureFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId) override; + void uploadTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId) override; void unloadTextureBuffer(TextureBufferHandle textureBufferHandle, SceneId sceneId) override; - void updateTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data, SceneId sceneId) override; + void updateTextureBuffer(TextureBufferHandle textureBufferHandle, uint32_t mipLevel, const Quad& area, uint32_t stride, const std::byte* data, SceneId sceneId) override; [[nodiscard]] DeviceResourceHandle getTextureBufferDeviceHandle(TextureBufferHandle textureBufferHandle, SceneId sceneId) const override; void uploadVertexArray(RenderableHandle renderableHandle, const VertexArrayInfo& vertexArrayInfo, SceneId sceneId) override; @@ -122,10 +119,10 @@ namespace ramses_internal struct OffscreenBufferDescriptor { // Second render target and color buffer are only used for double-buffered offscreen buffers, otherwise just the first - std::array m_renderTargetHandle; - std::array m_colorBufferHandle; + std::array m_renderTargetHandle{}; + std::array m_colorBufferHandle{}; DeviceResourceHandle m_depthBufferHandle; - uint32_t m_estimatedVRAMUsage; + uint32_t m_estimatedVRAMUsage = 0; bool isDmaBuffer = false; }; using OffscreenBufferMap = MemoryPool; @@ -148,5 +145,3 @@ namespace ramses_internal friend class RendererLogger; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererResourceRegistry.cpp b/src/renderer/internal/RendererLib/RendererResourceRegistry.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/src/RendererResourceRegistry.cpp rename to src/renderer/internal/RendererLib/RendererResourceRegistry.cpp index aa00127c4..4abe604d1 100644 --- a/renderer/RendererLib/RendererLib/src/RendererResourceRegistry.cpp +++ b/src/renderer/internal/RendererLib/RendererResourceRegistry.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererResourceRegistry.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/RendererResourceRegistry.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { void RendererResourceRegistry::registerResource(const ResourceContentHash& hash) { @@ -235,7 +235,7 @@ namespace ramses_internal const bool isUnused = ((rd != nullptr) && rd->sceneUsage.empty()); { - ResourceContentHashVector::iterator it = find_c(m_resourcesNotInUseByScenes, hash); + auto it = find_c(m_resourcesNotInUseByScenes, hash); const bool isContained = (it != m_resourcesNotInUseByScenes.end()); if (isUnused) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceRegistry.h b/src/renderer/internal/RendererLib/RendererResourceRegistry.h similarity index 95% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceRegistry.h rename to src/renderer/internal/RendererLib/RendererResourceRegistry.h index 0bfb33120..780d259c5 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererResourceRegistry.h +++ b/src/renderer/internal/RendererLib/RendererResourceRegistry.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERRESOURCEREGISTRY_H -#define RAMSES_RENDERERRESOURCEREGISTRY_H +#pragma once -#include "RendererLib/ResourceDescriptor.h" +#include "internal/RendererLib/ResourceDescriptor.h" #include #include -namespace ramses_internal +namespace ramses::internal { class RendererResourceRegistry { @@ -57,5 +56,3 @@ namespace ramses_internal friend class RendererLogger; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererSceneControlLogic.cpp b/src/renderer/internal/RendererLib/RendererSceneControlLogic.cpp similarity index 83% rename from renderer/RendererLib/RendererLib/src/RendererSceneControlLogic.cpp rename to src/renderer/internal/RendererLib/RendererSceneControlLogic.cpp index 27adfecc7..352068cb2 100644 --- a/renderer/RendererLib/RendererLib/src/RendererSceneControlLogic.cpp +++ b/src/renderer/internal/RendererLib/RendererSceneControlLogic.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererSceneControlLogic.h" -#include "RendererLib/IRendererSceneStateControl.h" -#include "RendererLib/RendererEvent.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" +#include "internal/RendererLib/IRendererSceneStateControl.h" +#include "internal/RendererLib/RendererEvent.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { RendererSceneControlLogic::RendererSceneControlLogic(IRendererSceneStateControl& sceneStateControl) : m_sceneStateControl(sceneStateControl) @@ -98,7 +98,7 @@ namespace ramses_internal case ESceneStateInternal::Mapped: case ESceneStateInternal::MappedAndAssigned: case ESceneStateInternal::Rendered: - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating subscribe to scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating subscribe to scene with id: " << sceneId); m_sceneStateControl.handleSceneSubscriptionRequest(sceneId); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Subscribe; break; @@ -116,13 +116,13 @@ namespace ramses_internal case ESceneStateInternal::Rendered: { assert(m_scenesInfo.count(sceneId) > 0); - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating mapping of scene " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating mapping of scene " << sceneId); m_sceneStateControl.handleSceneMappingRequest(sceneId); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Map; break; } case ESceneStateInternal::Published: - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating unsubscribe of scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating unsubscribe of scene with id: " << sceneId); m_sceneStateControl.handleSceneUnsubscriptionRequest(sceneId, false); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Unsubscribe; break; @@ -140,7 +140,7 @@ namespace ramses_internal break; case ESceneStateInternal::Subscribed: case ESceneStateInternal::Published: - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating unmap of scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating unmap of scene with id: " << sceneId); m_sceneStateControl.handleSceneUnmappingRequest(sceneId); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Unmap; break; @@ -154,13 +154,13 @@ namespace ramses_internal switch (targetSceneState) { case ESceneStateInternal::Rendered: - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating show of scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating show of scene with id: " << sceneId); m_sceneStateControl.handleSceneShowRequest(sceneId); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Show; break; case ESceneStateInternal::Subscribed: case ESceneStateInternal::Published: - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating unmap of scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating unmap of scene with id: " << sceneId); m_sceneStateControl.handleSceneUnmappingRequest(sceneId); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Unmap; break; @@ -176,7 +176,7 @@ namespace ramses_internal case ESceneStateInternal::MappedAndAssigned: case ESceneStateInternal::Subscribed: case ESceneStateInternal::Published: - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic initiating hide of scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic initiating hide of scene with id: " << sceneId); m_sceneStateControl.handleSceneHideRequest(sceneId); sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Hide; break; @@ -211,10 +211,10 @@ namespace ramses_internal const RendererSceneState newState = GetSceneStateFromInternal(state); sceneInfo.currentState = state; - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic received scene state change: scene " << sceneId << " is in state " << EnumToString(newState) << " (internal " << int(state) << ")"); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic received scene state change: scene " << sceneId << " is in state " << EnumToString(newState) << " (internal " << int(state) << ")"); if (currState != newState) { - LOG_INFO_F(ramses_internal::CONTEXT_RENDERER, ([&](ramses_internal::StringOutputStream& sos) + LOG_INFO_F(CONTEXT_RENDERER, ([&](StringOutputStream& sos) { sos << "RendererSceneControlLogic generated event: scene state change"; sos << " (scene " << sceneId; @@ -245,7 +245,7 @@ namespace ramses_internal //another request, other than display manager, or broken state if (getLastSceneStateCommandWaitingForReply(sceneId) != ESceneStateCommand::Subscribe) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic - received subscription event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); + LOG_WARN(CONTEXT_RENDERER, "RendererSceneControlLogic - received subscription event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); return; } m_scenesInfo[sceneId].lastCommandWaitigForReply = ESceneStateCommand::None; @@ -256,7 +256,7 @@ namespace ramses_internal setCurrentSceneState(sceneId, ESceneStateInternal::Subscribed); break; case EventResult::Failed: - LOG_ERROR_P(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic::sceneSubscribed failed for scene {}, will retry.", sceneId); + LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneControlLogic::sceneSubscribed failed for scene {}, will retry.", sceneId); break; case EventResult::Indirect: default: @@ -273,7 +273,7 @@ namespace ramses_internal { if (getLastSceneStateCommandWaitingForReply(sceneId) != ESceneStateCommand::Unsubscribe) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic - received unsubscription event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); + LOG_WARN(CONTEXT_RENDERER, "RendererSceneControlLogic - received unsubscription event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); return; } m_scenesInfo[sceneId].lastCommandWaitigForReply = ESceneStateCommand::None; @@ -289,7 +289,7 @@ namespace ramses_internal setCurrentSceneState(sceneId, ESceneStateInternal::Published); break; case EventResult::Failed: - LOG_ERROR_P(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic::sceneUnsubscribed failed for scene {}, will retry.", sceneId); + LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneControlLogic::sceneUnsubscribed failed for scene {}, will retry.", sceneId); goToTargetState(sceneId); break; default: @@ -302,7 +302,7 @@ namespace ramses_internal //another request, other than display manager, or broken state if (getLastSceneStateCommandWaitingForReply(sceneId) != ESceneStateCommand::Map) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic - received map event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); + LOG_WARN(CONTEXT_RENDERER, "RendererSceneControlLogic - received map event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); return; } m_scenesInfo[sceneId].lastCommandWaitigForReply = ESceneStateCommand::None; @@ -313,7 +313,7 @@ namespace ramses_internal setCurrentSceneState(sceneId, ESceneStateInternal::Mapped); break; case EventResult::Failed: - LOG_ERROR_P(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic::sceneMapped failed for scene {}, will retry.", sceneId); + LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneControlLogic::sceneMapped failed for scene {}, will retry.", sceneId); break; case EventResult::Indirect: default: @@ -330,7 +330,7 @@ namespace ramses_internal { if (getLastSceneStateCommandWaitingForReply(sceneId) != ESceneStateCommand::Unmap) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic - received unmap event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); + LOG_WARN(CONTEXT_RENDERER, "RendererSceneControlLogic - received unmap event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); return; } m_scenesInfo[sceneId].lastCommandWaitigForReply = ESceneStateCommand::None; @@ -343,7 +343,7 @@ namespace ramses_internal // after unmap, we always need to continue to unsubscribed, before we can continue to a potentially different target state if (getLastSceneStateCommandWaitingForReply(sceneId) == ESceneStateCommand::None) { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic continuing to unsubscribe after unmap regardless of target state of scene with id: " << sceneId); + LOG_INFO(CONTEXT_RENDERER, "RendererSceneControlLogic continuing to unsubscribe after unmap regardless of target state of scene with id: " << sceneId); m_sceneStateControl.handleSceneUnsubscriptionRequest(sceneId, false); SceneInfo& sceneInfo = m_scenesInfo[sceneId]; sceneInfo.lastCommandWaitigForReply = ESceneStateCommand::Unsubscribe; @@ -353,7 +353,7 @@ namespace ramses_internal setCurrentSceneState(sceneId, ESceneStateInternal::Subscribed); break; case EventResult::Failed: - LOG_ERROR_P(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic::sceneUnmapped failed for scene {}, will retry.", sceneId); + LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneControlLogic::sceneUnmapped failed for scene {}, will retry.", sceneId); goToTargetState(sceneId); break; default: @@ -366,7 +366,7 @@ namespace ramses_internal //another request, other than display manager, or broken state if (getLastSceneStateCommandWaitingForReply(sceneId) != ESceneStateCommand::Show) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic - received show event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); + LOG_WARN(CONTEXT_RENDERER, "RendererSceneControlLogic - received show event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); return; } m_scenesInfo[sceneId].lastCommandWaitigForReply = ESceneStateCommand::None; @@ -377,7 +377,7 @@ namespace ramses_internal setCurrentSceneState(sceneId, ESceneStateInternal::Rendered); break; case EventResult::Failed: - LOG_ERROR_P(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic::sceneShown failed for scene {}, will retry.", sceneId); + LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneControlLogic::sceneShown failed for scene {}, will retry.", sceneId); break; case EventResult::Indirect: default: @@ -394,7 +394,7 @@ namespace ramses_internal { if (getLastSceneStateCommandWaitingForReply(sceneId) != ESceneStateCommand::Hide) { - LOG_WARN(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic - received hide event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); + LOG_WARN(CONTEXT_RENDERER, "RendererSceneControlLogic - received hide event but did not ask for one, ignoring unexpected state change event for scene " << sceneId); return; } m_scenesInfo[sceneId].lastCommandWaitigForReply = ESceneStateCommand::None; @@ -410,7 +410,7 @@ namespace ramses_internal setCurrentSceneState(sceneId, ESceneStateInternal::MappedAndAssigned); break; case EventResult::Failed: - LOG_ERROR_P(ramses_internal::CONTEXT_RENDERER, "RendererSceneControlLogic::sceneHidden failed for scene {}, will retry.", sceneId); + LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneControlLogic::sceneHidden failed for scene {}, will retry.", sceneId); goToTargetState(sceneId); break; default: diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneControlLogic.h b/src/renderer/internal/RendererLib/RendererSceneControlLogic.h similarity index 95% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneControlLogic.h rename to src/renderer/internal/RendererLib/RendererSceneControlLogic.h index 71b7be543..613fee9a5 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneControlLogic.h +++ b/src/renderer/internal/RendererLib/RendererSceneControlLogic.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENECONTROLLOGIC_H -#define RAMSES_RENDERERSCENECONTROLLOGIC_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include -namespace ramses_internal +namespace ramses::internal { class IRendererSceneStateControl; struct InternalSceneStateEvent; @@ -53,7 +52,7 @@ namespace ramses_internal struct Event { SceneId sceneId; - RendererSceneState state; + RendererSceneState state = RendererSceneState::Unavailable; }; using Events = std::vector; void consumeEvents(Events& eventsOut); @@ -110,5 +109,3 @@ namespace ramses_internal Events m_pendingEvents; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererSceneResourceRegistry.cpp b/src/renderer/internal/RendererLib/RendererSceneResourceRegistry.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/src/RendererSceneResourceRegistry.cpp rename to src/renderer/internal/RendererLib/RendererSceneResourceRegistry.cpp index f7d588ac7..9ee30ae94 100644 --- a/renderer/RendererLib/RendererLib/src/RendererSceneResourceRegistry.cpp +++ b/src/renderer/internal/RendererLib/RendererSceneResourceRegistry.cpp @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererSceneResourceRegistry.h" +#include "internal/RendererLib/RendererSceneResourceRegistry.h" -namespace ramses_internal +namespace ramses::internal { - RendererSceneResourceRegistry::RendererSceneResourceRegistry() - { - } + RendererSceneResourceRegistry::RendererSceneResourceRegistry() = default; RendererSceneResourceRegistry::~RendererSceneResourceRegistry() { @@ -152,7 +150,7 @@ namespace ramses_internal } } - void RendererSceneResourceRegistry::addTextureBuffer(TextureBufferHandle handle, DeviceResourceHandle deviceHandle, ETextureFormat format, uint32_t size) + void RendererSceneResourceRegistry::addTextureBuffer(TextureBufferHandle handle, DeviceResourceHandle deviceHandle, EPixelStorageFormat format, uint32_t size) { assert(!m_textureBuffers.contains(handle)); m_textureBuffers.put(handle, { deviceHandle, size, format }); @@ -170,7 +168,7 @@ namespace ramses_internal return m_textureBuffers.get(handle)->deviceHandle; } - ETextureFormat RendererSceneResourceRegistry::getTextureBufferFormat(TextureBufferHandle handle) const + EPixelStorageFormat RendererSceneResourceRegistry::getTextureBufferFormat(TextureBufferHandle handle) const { assert(m_textureBuffers.contains(handle)); return m_textureBuffers.get(handle)->format; diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneResourceRegistry.h b/src/renderer/internal/RendererLib/RendererSceneResourceRegistry.h similarity index 79% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneResourceRegistry.h rename to src/renderer/internal/RendererLib/RendererSceneResourceRegistry.h index 98056b74f..4ee4f36ec 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneResourceRegistry.h +++ b/src/renderer/internal/RendererLib/RendererSceneResourceRegistry.h @@ -6,19 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENERESOURCEREGISTRY_H -#define RAMSES_RENDERERSCENERESOURCEREGISTRY_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/TextureEnums.h" -#include "Collections/HashMap.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" -namespace ramses_internal +namespace ramses::internal { - enum class EDataBufferType : uint8_t; - enum ESceneResourceType { ESceneResourceType_RenderBuffer_WriteOnly = 0, @@ -56,12 +54,12 @@ namespace ramses_internal [[nodiscard]] EDataBufferType getDataBufferType (DataBufferHandle handle) const; void getAllDataBuffers (DataBufferHandleVector& dataBuffers) const; - void addTextureBuffer (TextureBufferHandle handle, DeviceResourceHandle deviceHandle, ETextureFormat format, uint32_t size); - void removeTextureBuffer (TextureBufferHandle handle); - [[nodiscard]] DeviceResourceHandle getTextureBufferDeviceHandle(TextureBufferHandle handle) const; - [[nodiscard]] ETextureFormat getTextureBufferFormat (TextureBufferHandle handle) const; - [[nodiscard]] uint32_t getTextureBufferByteSize (TextureBufferHandle handle) const; - void getAllTextureBuffers (TextureBufferHandleVector& textureBuffers) const; + void addTextureBuffer (TextureBufferHandle handle, DeviceResourceHandle deviceHandle, EPixelStorageFormat format, uint32_t size); + void removeTextureBuffer (TextureBufferHandle handle); + [[nodiscard]] DeviceResourceHandle getTextureBufferDeviceHandle(TextureBufferHandle handle) const; + [[nodiscard]] EPixelStorageFormat getTextureBufferFormat (TextureBufferHandle handle) const; + [[nodiscard]] uint32_t getTextureBufferByteSize (TextureBufferHandle handle) const; + void getAllTextureBuffers (TextureBufferHandleVector& textureBuffers) const; void addVertexArray(RenderableHandle renderableHandle, DeviceResourceHandle deviceHandle); void removeVertexArray(RenderableHandle renderableHandle); @@ -74,15 +72,15 @@ namespace ramses_internal struct TextureBufferEntry { DeviceResourceHandle deviceHandle; - uint32_t size; - ETextureFormat format; + uint32_t size = 0u; + EPixelStorageFormat format = EPixelStorageFormat::Invalid; }; struct RenderBufferEntry { DeviceResourceHandle deviceHandle; - uint32_t size; - bool writeOnly; + uint32_t size = 0u; + bool writeOnly = false; }; struct BlitPassEntry @@ -94,8 +92,8 @@ namespace ramses_internal struct DataBufferEntry { DeviceResourceHandle deviceHandle; - uint32_t size; - EDataBufferType dataBufferType; + uint32_t size = 0u; + EDataBufferType dataBufferType = EDataBufferType::Invalid; }; using RenderBufferMap = HashMap; @@ -114,5 +112,3 @@ namespace ramses_internal VertexArrayMap m_vertexArrays; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererSceneUpdater.cpp b/src/renderer/internal/RendererLib/RendererSceneUpdater.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/src/RendererSceneUpdater.cpp rename to src/renderer/internal/RendererLib/RendererSceneUpdater.cpp index 62248e1de..6f8d13fef 100644 --- a/renderer/RendererLib/RendererLib/src/RendererSceneUpdater.cpp +++ b/src/renderer/internal/RendererLib/RendererSceneUpdater.cpp @@ -6,40 +6,39 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneActionApplier.h" -#include "Scene/ResourceChanges.h" -#include "SceneUtils/ResourceUtils.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IDisplayController.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RendererAPI/IRendererResourceCache.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/Renderer.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/RendererResourceManager.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DataLinkUtils.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererLib/EmbeddedCompositingManager.h" -#include "RendererLib/PendingSceneResourcesUtils.h" -#include "RendererLib/RendererLogger.h" -#include "RendererLib/IntersectionUtils.h" -#include "RendererLib/SceneReferenceLogic.h" -#include "RendererLib/ResourceUploader.h" -#include "RendererEventCollector.h" -#include "Components/FlushTimeInformation.h" -#include "Components/SceneUpdate.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/Image.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "PlatformAbstraction/Macros.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/RendererSceneUpdater.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/RendererResourceManager.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DataLinkUtils.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/EmbeddedCompositingManager.h" +#include "internal/RendererLib/PendingSceneResourcesUtils.h" +#include "internal/RendererLib/RendererLogger.h" +#include "internal/RendererLib/IntersectionUtils.h" +#include "internal/RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/ResourceUploader.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/Image.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/Macros.h" #include -#include "RendererLib/SceneResourceUploader.h" +#include "internal/RendererLib/SceneResourceUploader.h" -namespace ramses_internal +namespace ramses::internal { RendererSceneUpdater::RendererSceneUpdater( DisplayHandle display, @@ -50,8 +49,7 @@ namespace ramses_internal RendererEventCollector& eventCollector, FrameTimer& frameTimer, SceneExpirationMonitor& expirationMonitor, - IThreadAliveNotifier& notifier, - IRendererResourceCache* rendererResourceCache + IThreadAliveNotifier& notifier ) : m_display{ display } , m_platform(platform) @@ -61,7 +59,6 @@ namespace ramses_internal , m_rendererEventCollector(eventCollector) , m_frameTimer(frameTimer) , m_expirationMonitor(expirationMonitor) - , m_rendererResourceCache(rendererResourceCache) , m_notifier(notifier) { } @@ -73,7 +70,7 @@ namespace ramses_internal const SceneId sceneId = m_rendererScenes.begin()->key; destroyScene(sceneId); } - assert(m_scenesToBeMapped.size() == 0u); + assert(m_scenesToBeMapped.empty()); if (m_displayResourceManager) destroyDisplayContext(); @@ -98,9 +95,13 @@ namespace ramses_internal } if (SceneStateIsAtLeast(sceneState, ESceneState::Subscribed)) + { consolidatePendingSceneActions(sceneId, std::move(sceneUpdate)); + } else + { LOG_ERROR_P(CONTEXT_RENDERER, "RendererSceneUpdater::handleSceneActions could not apply scene actions because scene {} is neither subscribed nor mapped", sceneId); + } } void RendererSceneUpdater::createDisplayContext(const DisplayConfig& displayConfig, IBinaryShaderCache* binaryShaderCache) @@ -155,9 +156,9 @@ namespace ramses_internal m_renderer.getStatistics()); } - void RendererSceneUpdater::destroyResourceManager() + bool RendererSceneUpdater::hasResourceManager() const { - m_displayResourceManager.reset(); + return m_displayResourceManager != nullptr; } void RendererSceneUpdater::destroyDisplayContext() @@ -186,7 +187,7 @@ namespace ramses_internal m_asyncEffectUploader->destroyResourceUploadRenderBackendAndStopThread(); m_asyncEffectUploader.reset(); - destroyResourceManager(); + m_displayResourceManager.reset(); m_renderer.resetRenderInterruptState(); m_renderer.destroyDisplayContext(); @@ -299,7 +300,7 @@ namespace ramses_internal << " Possible causes: too many flushes queued and couldn't be applied (even force-applied); or renderer thread was stopped or stalled," << " e.g. because of taking screenshots, and couldn't process the flushes."); - if (m_sceneStateExecutor.getScenePublicationMode(sceneId) != EScenePublicationMode_LocalOnly) + if (m_sceneStateExecutor.getScenePublicationMode(sceneId) != EScenePublicationMode::LocalOnly) { LOG_ERROR(CONTEXT_RENDERER, "Force unsubscribing scene " << sceneId << " to avoid risk of running out of memory!" << " Any incoming data for the scene will be ignored till the scene is re-subscribed."); @@ -450,9 +451,6 @@ namespace ramses_internal for (const auto& mr : *resList) { m_displayResourceManager->provideResourceData(mr); - uint32_t dummySize = 0; - if (m_rendererResourceCache && !m_rendererResourceCache->hasResource(mr->getHash(), dummySize)) - RendererResourceManagerUtils::StoreResource(m_rendererResourceCache, mr.get(), sceneID); } // data was provided, clear shared_ptr references from list resList->clear(); @@ -468,9 +466,13 @@ namespace ramses_internal { const SceneId sceneID = rendererScene.key; if (m_sceneStateExecutor.getSceneState(sceneID) < ESceneState::MappingAndUploading) + { consolidateResourceDataForMapping(sceneID); + } else + { referenceAndProvidePendingResourceData(sceneID); + } } // if there are resources to upload, unload and upload pending resources @@ -579,7 +581,7 @@ namespace ramses_internal rendererScene.getSceneId()); rendererScene.setEffectTimeSync(pendingFlush.timeInfo.internalTimestamp); } - applySceneActions(rendererScene, pendingFlush); + ApplySceneActions(rendererScene, pendingFlush); if (pendingFlush.versionTag.isValid()) { @@ -595,12 +597,16 @@ namespace ramses_internal // also mark scene as modified if it had an active shader animation before (to not stop the animation with an empty flush) const bool isFlushWithChanges = !pendingFlush.sceneActions.empty() || pendingFlush.timeInfo.isEffectTimeSync || hadActiveShaderAnimation; if (isFlushWithChanges) + { // there are changes to scene -> mark it as modified to be re-rendered m_modifiedScenesToRerender.put(sceneID); + } else if (m_sceneStateExecutor.getSceneState(sceneID) == ESceneState::Rendered) + { // there are no changes to scene and it might not be rendered due to skipping of frames optimization, // mark it as if rendered for expiration monitor so that it does not expire m_expirationMonitor.onRendered(sceneID); + } } if (!pendingData.sceneReferenceActions.empty()) @@ -656,15 +662,16 @@ namespace ramses_internal WaylandIviSurfaceIdVector obsoleteStreams; embeddedCompositingManager.dispatchStateChangesOfSources(streamsWithAvailabilityChanged, newStreams, obsoleteStreams); - for (const auto stream : newStreams) - { - m_rendererEventCollector.addStreamSourceEvent(ERendererEventType::StreamSurfaceAvailable, stream); - } + // stream might be in both obsoleteStreams and newStreams -> dispatch unavailable first for (const auto stream : obsoleteStreams) { m_rendererEventCollector.addStreamSourceEvent(ERendererEventType::StreamSurfaceUnavailable, stream); m_renderer.getStatistics().untrackStreamTexture(stream); } + for (const auto stream : newStreams) + { + m_rendererEventCollector.addStreamSourceEvent(ERendererEventType::StreamSurfaceAvailable, stream); + } auto& texLinks = m_rendererScenes.getSceneLinksManager().getTextureLinkManager(); StreamBufferLinkVector links; @@ -863,7 +870,7 @@ namespace ramses_internal // log missing resources every period if (currentFrameTime - mapRequest.lastLogTimeStamp > MappingLogPeriod) { - LOG_WARN_F(CONTEXT_RENDERER, [&](ramses_internal::StringOutputStream& logger) + LOG_WARN_F(CONTEXT_RENDERER, [&](StringOutputStream& logger) { logger << "Scene " << sceneId << " waiting " << totalWaitingTime.count() << " ms for resources in order to be mapped: "; size_t numResourcesWaiting = 0u; @@ -912,7 +919,7 @@ namespace ramses_internal return false; } - void RendererSceneUpdater::applySceneActions(RendererCachedScene& scene, PendingFlush& flushInfo) + void RendererSceneUpdater::ApplySceneActions(RendererCachedScene& scene, PendingFlush& flushInfo) { const SceneActionCollection& actionsForScene = flushInfo.sceneActions; const uint32_t numActions = actionsForScene.numberOfActions(); @@ -987,7 +994,7 @@ namespace ramses_internal LOG_INFO(CONTEXT_RENDERER, "Applying scene resources gathered from scene " << sceneId << ", " << sceneResourceActions.size() << " actions, " << sceneResourcesByteSize << " bytes"); // enable time measuring and interrupting of upload only if scene is remote - const bool sceneIsRemote = (m_sceneStateExecutor.getScenePublicationMode(sceneId) != EScenePublicationMode_LocalOnly); + const bool sceneIsRemote = (m_sceneStateExecutor.getScenePublicationMode(sceneId) != EScenePublicationMode::LocalOnly); if (!PendingSceneResourcesUtils::ApplySceneResourceActions(sceneResourceActions, scene, resourceManager, sceneIsRemote ? &m_frameTimer : nullptr)) return false; } @@ -1142,7 +1149,7 @@ namespace ramses_internal } } - bool RendererSceneUpdater::handleBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType) + bool RendererSceneUpdater::handleBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType) { bool success = false; if (!m_renderer.hasDisplayController()) @@ -1315,6 +1322,12 @@ namespace ramses_internal assert(m_displayResourceManager); IRendererResourceManager& resourceManager = *m_displayResourceManager; + if (resourceManager.getStreamBufferDeviceHandle(buffer).isValid()) + { + LOG_ERROR(CONTEXT_RENDERER, "RendererSceneUpdater::handleBufferCreateRequest a stream buffer with the same ID (" << buffer << ") already exists!"); + return false; + } + resourceManager.uploadStreamBuffer(buffer, source); const DeviceResourceHandle deviceHandle = resourceManager.getStreamBufferDeviceHandle(buffer); @@ -1333,13 +1346,19 @@ namespace ramses_internal assert(m_displayResourceManager); IRendererResourceManager& resourceManager = *m_displayResourceManager; + if (!resourceManager.getStreamBufferDeviceHandle(buffer).isValid()) + { + LOG_ERROR(CONTEXT_RENDERER, "RendererSceneUpdater::handleBufferDestroyRequest could not find stream buffer with ID " << buffer); + return false; + } + resourceManager.unloadStreamBuffer(buffer); m_rendererScenes.getSceneLinksManager().handleBufferDestroyedOrSourceUnavailable(buffer); return true; } - void RendererSceneUpdater::handleSetClearFlags(OffscreenBufferHandle buffer, uint32_t clearFlags) + void RendererSceneUpdater::handleSetClearFlags(OffscreenBufferHandle buffer, ClearFlags clearFlags) { if (!m_renderer.hasDisplayController()) { @@ -1413,17 +1432,23 @@ namespace ramses_internal const auto& displayController = m_renderer.getDisplayController(); if (buffer.isValid()) + { renderTargetHandle = displayResourceManager.getOffscreenBufferDeviceHandle(buffer); + } else + { renderTargetHandle = displayController.getDisplayBuffer(); + } if (renderTargetHandle.isValid()) { const auto& bufferViewport = m_renderer.getDisplaySetup().getDisplayBuffer(renderTargetHandle).viewport; if (screenshotInfo.fullScreen) + { screenshotInfo.rectangle = { 0u, 0u, bufferViewport.width, bufferViewport.height }; - else if (screenshotInfo.rectangle.x + screenshotInfo.rectangle.width > bufferViewport.width - || screenshotInfo.rectangle.y + screenshotInfo.rectangle.height > bufferViewport.height) + } + else if (screenshotInfo.rectangle.x + screenshotInfo.rectangle.width > bufferViewport.width || + screenshotInfo.rectangle.y + screenshotInfo.rectangle.height > bufferViewport.height) { LOG_ERROR(CONTEXT_RENDERER, "RendererSceneUpdater::readPixels failed, requested area is out of offscreen display/buffer size boundaries!"); readPixelsFailed = true; @@ -1439,8 +1464,10 @@ namespace ramses_internal if (readPixelsFailed) { if (screenshotInfo.filename.empty()) + { // only generate event when not saving pixels to file! m_rendererEventCollector.addReadPixelsEvent(ERendererEventType::ReadPixelsFromFramebufferFailed, m_display, buffer, {}); + } } else m_renderer.scheduleScreenshot(renderTargetHandle, std::move(screenshotInfo)); @@ -1480,7 +1507,7 @@ namespace ramses_internal if (providerSlotHandle.isValid() && consumerSlotHandle.isValid()) { const EDataSlotType providerSlotType = DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_rendererScenes).type; - if (providerSlotType == EDataSlotType_TextureProvider) + if (providerSlotType == EDataSlotType::TextureProvider) { if (m_sceneStateExecutor.getSceneState(providerSceneId) < ESceneState::Mapped) { @@ -1574,8 +1601,10 @@ namespace ramses_internal } const auto& buffer = m_renderer.getDisplaySetup().getDisplayBuffer(bufferHandle); - const glm::ivec2 coordsInBufferSpace = { static_cast(std::lroundf((coordsNormalizedToBufferSize.x + 1.f) * buffer.viewport.width / 2.f)) , - static_cast(std::lroundf((coordsNormalizedToBufferSize.y + 1.f) * buffer.viewport.height / 2.f)) }; + const auto width = static_cast(buffer.viewport.width); + const auto height = static_cast(buffer.viewport.height); + const glm::ivec2 coordsInBufferSpace = { static_cast(std::lroundf((coordsNormalizedToBufferSize.x + 1.f) * width / 2.f)) , + static_cast(std::lroundf((coordsNormalizedToBufferSize.y + 1.f) * height / 2.f)) }; PickableObjectIds pickedObjects; const TransformationLinkCachedScene& scene = m_rendererScenes.getScene(sceneId); @@ -1621,9 +1650,13 @@ namespace ramses_internal { const auto& pendingData = m_rendererScenes.getStagingInfo(sceneId).pendingData; for (const auto& pendingFlush : pendingData.pendingFlushes) + { for (const auto& res : pendingFlush.resourcesAdded) + { if (m_displayResourceManager->getResourceStatus(res) != EResourceStatus::Uploaded) return false; + } + } return true; } @@ -1750,8 +1783,10 @@ namespace ramses_internal texLinkManager.getOffscreenBufferLinks().getLinkedConsumers(bufferHandle, m_offscreenBufferConsumerSceneLinksCache); for (const auto& link : m_offscreenBufferConsumerSceneLinksCache) + { if (!m_modifiedScenesToRerender.contains(link.consumerSceneId)) m_offscreeenBufferModifiedScenesVisitingCache.push_back(link.consumerSceneId); + } } } } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneUpdater.h b/src/renderer/internal/RendererLib/RendererSceneUpdater.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneUpdater.h rename to src/renderer/internal/RendererLib/RendererSceneUpdater.h index bcdc61fe8..fb3e2cbea 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererSceneUpdater.h +++ b/src/renderer/internal/RendererLib/RendererSceneUpdater.h @@ -6,29 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENEUPDATER_H -#define RAMSES_RENDERERSCENEUPDATER_H - -#include "RendererAPI/Types.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "SceneAPI/SceneId.h" -#include "RendererLib/StagingInfo.h" -#include "RendererLib/BufferLinks.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/IRendererSceneStateControl.h" -#include "RendererLib/IRendererSceneUpdater.h" -#include "RendererLib/IRendererResourceManager.h" -#include "Scene/EScenePublicationMode.h" +#pragma once + +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/StagingInfo.h" +#include "internal/RendererLib/BufferLinks.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/IRendererSceneStateControl.h" +#include "internal/RendererLib/IRendererSceneUpdater.h" +#include "internal/RendererLib/IRendererResourceManager.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" #include "AsyncEffectUploader.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererCachedScene; class Renderer; class SceneStateExecutor; class IBinaryShaderCache; - class IRendererResourceCache; class RendererEventCollector; class RendererScenes; class DisplayConfig; @@ -56,25 +54,24 @@ namespace ramses_internal RendererEventCollector& eventCollector, FrameTimer& frameTimer, SceneExpirationMonitor& expirationMonitor, - IThreadAliveNotifier& notifier, - IRendererResourceCache* rendererResourceCache = nullptr); + IThreadAliveNotifier& notifier); ~RendererSceneUpdater() override; // IRendererSceneUpdater void handleSceneUpdate(SceneId sceneId, SceneUpdate&& sceneUpdate) override; void createDisplayContext(const DisplayConfig& displayConfig, IBinaryShaderCache* binaryShaderCache) override; - void destroyDisplayContext() override; + void destroyDisplayContext() final override; void handleScenePublished(SceneId sceneId, EScenePublicationMode mode) override; void handleSceneUnpublished(SceneId sceneId) override; void handleSceneReceived(const SceneInfo& sceneInfo) override; - bool handleBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType) override; + bool handleBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType) override; bool handleDmaBufferCreateRequest(OffscreenBufferHandle buffer, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers) override; bool handleBufferDestroyRequest(OffscreenBufferHandle buffer) override; bool handleBufferCreateRequest(StreamBufferHandle buffer, WaylandIviSurfaceId source) override; bool handleBufferDestroyRequest(StreamBufferHandle buffer) override; bool handleExternalBufferCreateRequest(ExternalBufferHandle buffer) override; bool handleExternalBufferDestroyRequest(ExternalBufferHandle buffer) override; - void handleSetClearFlags(OffscreenBufferHandle buffer, uint32_t clearFlags) override; + void handleSetClearFlags(OffscreenBufferHandle buffer, ClearFlags clearFlags) override; void handleSetClearColor(OffscreenBufferHandle buffer, const glm::vec4& clearColor) override; void handleSetExternallyOwnedWindowSize(uint32_t width, uint32_t height) override; void handleReadPixels(OffscreenBufferHandle buffer, ScreenshotInfo&& screenshotInfo) override; @@ -111,7 +108,7 @@ namespace ramses_internal const DisplayConfig& displayConfig, IBinaryShaderCache* binaryShaderCache); - virtual void destroyResourceManager(); + [[nodiscard]] bool hasResourceManager() const; std::chrono::milliseconds m_maximumWaitingTimeToForceMap{ 2000 }; @@ -121,7 +118,7 @@ namespace ramses_internal bool markClientAndSceneResourcesForReupload(SceneId sceneId); void updateScenePendingFlushes(SceneId sceneID, StagingInfo& stagingInfo); - void applySceneActions(RendererCachedScene& scene, PendingFlush& flushInfo); + static void ApplySceneActions(RendererCachedScene& scene, PendingFlush& flushInfo); void applyPendingFlushes(SceneId sceneID, StagingInfo& stagingInfo); void processStagedResourceChanges(SceneId sceneID, StagingInfo& stagingInfo); @@ -162,7 +159,6 @@ namespace ramses_internal FrameTimer& m_frameTimer; SceneExpirationMonitor& m_expirationMonitor; ISceneReferenceLogic* m_sceneReferenceLogic = nullptr; - IRendererResourceCache* m_rendererResourceCache = nullptr; std::unique_ptr m_displayResourceManager; std::unique_ptr m_asyncEffectUploader; @@ -193,5 +189,3 @@ namespace ramses_internal RenderableVector m_tempRenderablesWithUpdatedVertexArrays; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererScenes.cpp b/src/renderer/internal/RendererLib/RendererScenes.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/src/RendererScenes.cpp rename to src/renderer/internal/RendererLib/RendererScenes.cpp index fd79b3edd..362756384 100644 --- a/renderer/RendererLib/RendererLib/src/RendererScenes.cpp +++ b/src/renderer/internal/RendererLib/RendererScenes.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererScenes.h" -namespace ramses_internal +namespace ramses::internal { RendererScenes::RendererScenes(RendererEventCollector& eventCollector) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererScenes.h b/src/renderer/internal/RendererLib/RendererScenes.h similarity index 86% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererScenes.h rename to src/renderer/internal/RendererLib/RendererScenes.h index b5aa339ac..335a07ee6 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererScenes.h +++ b/src/renderer/internal/RendererLib/RendererScenes.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENES_H -#define RAMSES_RENDERERSCENES_H +#pragma once -#include "RendererLib/StagingInfo.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/StagingInfo.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererEventCollector; struct RendererSceneInfo { - RendererCachedScene* scene; - StagingInfo* stagingInfo; + RendererCachedScene* scene{nullptr}; + StagingInfo* stagingInfo{nullptr}; }; class RendererScenes @@ -53,5 +52,3 @@ namespace ramses_internal std::unique_ptr m_sceneLinksManager; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/RendererStatistics.cpp b/src/renderer/internal/RendererLib/RendererStatistics.cpp similarity index 92% rename from renderer/RendererLib/RendererLib/src/RendererStatistics.cpp rename to src/renderer/internal/RendererLib/RendererStatistics.cpp index 7694c9f0f..f7e3f0fae 100644 --- a/renderer/RendererLib/RendererLib/src/RendererStatistics.cpp +++ b/src/renderer/internal/RendererLib/RendererStatistics.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/RendererStatistics.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Collections/StringOutputStream.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" -namespace ramses_internal +namespace ramses::internal { float RendererStatistics::getFps() const { const uint64_t reportTimeInterval = PlatformTime::GetMillisecondsMonotonic() - m_timeBase; if (reportTimeInterval > 0u) - return (1000.f * m_frameNumber) / reportTimeInterval; + return (1000.f * static_cast(m_frameNumber)) / static_cast(reportTimeInterval); return 0.f; } @@ -63,9 +63,9 @@ namespace ramses_internal sceneStats.sceneResourcesBytesUploaded += byteSize; } - void RendererStatistics::streamTextureUpdated(WaylandIviSurfaceId sourceId, size_t numUpdates) + void RendererStatistics::streamTextureUpdated(WaylandIviSurfaceId iviSurface, size_t numUpdates) { - auto& strTexStat = m_streamTextureStatistics[sourceId]; + auto& strTexStat = m_streamTextureStatistics[iviSurface]; strTexStat.numUpdates += numUpdates; if (strTexStat.lastFrameUpdated != m_frameNumber) { @@ -128,9 +128,13 @@ namespace ramses_internal { sceneStats.numFramesWhereFlushBlocked++; if (sceneStats.lastFrameFlushBlocked == m_frameNumber - 1) + { sceneStats.currentConsecutiveFramesBlocked++; // keeps being blocked + } else + { sceneStats.currentConsecutiveFramesBlocked = 1u; // starts being blocked + } sceneStats.maxConsecutiveFramesBlocked = std::max(sceneStats.maxConsecutiveFramesBlocked, sceneStats.currentConsecutiveFramesBlocked); sceneStats.lastFrameFlushBlocked = m_frameNumber; } @@ -146,19 +150,23 @@ namespace ramses_internal m_displayStatistics.offscreenBufferStatistics.erase(offscreenBuffer); } - void RendererStatistics::untrackStreamTexture(WaylandIviSurfaceId sourceId) + void RendererStatistics::untrackStreamTexture(WaylandIviSurfaceId iviSurface) { - m_streamTextureStatistics.erase(sourceId); + m_streamTextureStatistics.erase(iviSurface); } void RendererStatistics::frameFinished(uint32_t drawCalls) { const uint64_t currTick = PlatformTime::GetMicrosecondsMonotonic(); - const uint32_t frameDuration = static_cast(currTick - m_lastFrameTick); + const auto frameDuration = static_cast(currTick - m_lastFrameTick); if (frameDuration < m_frameDurationMin) + { m_frameDurationMin = frameDuration; + } else if (frameDuration > m_frameDurationMax) + { m_frameDurationMax = frameDuration; + } // update 'gap' measurements - max number of consecutive frames with some action happening or not for (auto& sceneStat : m_sceneStatistics) @@ -296,14 +304,14 @@ namespace ramses_internal str << ", FApplied " << sceneStats.numFlushesApplied; if (sceneStats.numFlushesArrived > 0u) { - str << ", actions/F (" << numSceneActionsPerFlush.minValue << "/" << numSceneActionsPerFlush.maxValue << "/" << static_cast(numSceneActionsPerFlush.sum) / sceneStats.numFlushesArrived << ")"; - str << ", dt/F (" << flushLatency.minValue << "/" << flushLatency.maxValue << "/" << static_cast(flushLatency.sum) / sceneStats.numFlushesArrived << ")"; - str << ", RC+/F (" << numResourcesAddedPerFlush.minValue << "/" << numResourcesAddedPerFlush.maxValue << "/" << static_cast(numResourcesAddedPerFlush.sum) / sceneStats.numFlushesArrived << ")"; - str << ", RC-/F (" << numResourcesRemovedPerFlush.minValue << "/" << numResourcesRemovedPerFlush.maxValue << "/" << static_cast(numResourcesRemovedPerFlush.sum) / sceneStats.numFlushesArrived << ")"; - str << ", RS/F (" << numSceneResourceActionsPerFlush.minValue << "/" << numSceneResourceActionsPerFlush.maxValue << "/" << static_cast(numSceneResourceActionsPerFlush.sum) / sceneStats.numFlushesArrived << ")"; + str << ", actions/F (" << numSceneActionsPerFlush.minValue << "/" << numSceneActionsPerFlush.maxValue << "/" << static_cast(numSceneActionsPerFlush.sum) / static_cast(sceneStats.numFlushesArrived) << ")"; + str << ", dt/F (" << flushLatency.minValue << "/" << flushLatency.maxValue << "/" << static_cast(flushLatency.sum) / static_cast(sceneStats.numFlushesArrived) << ")"; + str << ", RC+/F (" << numResourcesAddedPerFlush.minValue << "/" << numResourcesAddedPerFlush.maxValue << "/" << static_cast(numResourcesAddedPerFlush.sum) / static_cast(sceneStats.numFlushesArrived) << ")"; + str << ", RC-/F (" << numResourcesRemovedPerFlush.minValue << "/" << numResourcesRemovedPerFlush.maxValue << "/" << static_cast(numResourcesRemovedPerFlush.sum) / static_cast(sceneStats.numFlushesArrived) << ")"; + str << ", RS/F (" << numSceneResourceActionsPerFlush.minValue << "/" << numSceneResourceActionsPerFlush.maxValue << "/" << static_cast(numSceneResourceActionsPerFlush.sum) / static_cast(sceneStats.numFlushesArrived) << ")"; } if (sceneStats.numExpirationOffsets > 0u) - str << ", Exp (" << sceneStats.numExpiredOffsets << "/" << sceneStats.numExpirationOffsets << ":" << expirationOffset.minValue << "/" << expirationOffset.maxValue << "/" << static_cast(expirationOffset.sum) / sceneStats.numExpirationOffsets << ")"; + str << ", Exp (" << sceneStats.numExpiredOffsets << "/" << sceneStats.numExpirationOffsets << ":" << expirationOffset.minValue << "/" << expirationOffset.maxValue << "/" << static_cast(expirationOffset.sum) / static_cast(sceneStats.numExpirationOffsets) << ")"; if (sceneStats.sceneResourcesUploaded > 0u) str << ", RSUploaded " << sceneStats.sceneResourcesUploaded << " (" << sceneStats.sceneResourcesBytesUploaded << " B)"; diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RendererStatistics.h b/src/renderer/internal/RendererLib/RendererStatistics.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/RendererStatistics.h rename to src/renderer/internal/RendererLib/RendererStatistics.h index 0709eda0b..7b2b4a9e9 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RendererStatistics.h +++ b/src/renderer/internal/RendererLib/RendererStatistics.h @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSTATISTICS_H -#define RAMSES_RENDERERSTATISTICS_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" -#include "Utils/StatisticCollection.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "Components/FlushTimeInformation.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Components/FlushTimeInformation.h" +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { class StringOutputStream; @@ -41,13 +40,13 @@ namespace ramses_internal void resourceUploaded(size_t byteSize); void sceneResourceUploaded(SceneId sceneId, size_t byteSize); - void streamTextureUpdated(WaylandIviSurfaceId sourceId, size_t numUpdates); + void streamTextureUpdated(WaylandIviSurfaceId iviSurface, size_t numUpdates); void shaderCompiled(std::chrono::microseconds microsecondsUsed, std::string_view name, SceneId sceneid); void setVRAMUsage(uint64_t totalUploaded, uint64_t gpuCacheSize); void untrackScene(SceneId sceneId); void untrackOffscreenBuffer(DeviceResourceHandle offscreenBuffer); - void untrackStreamTexture(WaylandIviSurfaceId sourceId); + void untrackStreamTexture(WaylandIviSurfaceId iviSurface); void addExpirationOffset(SceneId sceneId, int64_t expirationOffset); @@ -95,8 +94,8 @@ namespace ramses_internal // expiration offset in milliseconds, can be negative and zero (=healthy) or positive (=expired) SummaryEntry expirationOffset; - size_t numExpirationOffsets; - size_t numExpiredOffsets; + size_t numExpirationOffsets = 0u; + size_t numExpiredOffsets = 0u; size_t sceneResourcesUploaded = 0u; size_t sceneResourcesBytesUploaded = 0u; @@ -141,5 +140,3 @@ namespace ramses_internal std::map< WaylandIviSurfaceId, StreamTextureStatistics, StronglyTypedValueComparator > m_streamTextureStatistics; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/RenderingContext.h b/src/renderer/internal/RendererLib/RenderingContext.h similarity index 67% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/RenderingContext.h rename to src/renderer/internal/RendererLib/RenderingContext.h index 6a8ab9832..3669b353a 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/RenderingContext.h +++ b/src/renderer/internal/RendererLib/RenderingContext.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERINGCONTEXT_H -#define RAMSES_RENDERINGCONTEXT_H +#pragma once -#include "RendererAPI/Types.h" -#include "RendererAPI/SceneRenderExecutionIterator.h" -#include "SceneAPI/Viewport.h" -#include "SceneAPI/RenderState.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/SceneRenderExecutionIterator.h" +#include "internal/SceneGraph/SceneAPI/Viewport.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" -namespace ramses_internal +namespace ramses::internal { struct RenderingContext { @@ -23,10 +22,8 @@ namespace ramses_internal uint32_t viewportHeight = 0u; SceneRenderExecutionIterator renderFrom; - uint32_t displayBufferClearPending = EClearFlags_None; - glm::vec4 displayBufferClearColor; + ClearFlags displayBufferClearPending = EClearFlag::None; + glm::vec4 displayBufferClearColor{}; bool displayBufferDepthDiscard = false; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/RenderingPassInfo.h b/src/renderer/internal/RendererLib/RenderingPassInfo.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RendererLib/RenderingPassInfo.h rename to src/renderer/internal/RendererLib/RenderingPassInfo.h index 69dedf1a3..20c35e3c1 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/RenderingPassInfo.h +++ b/src/renderer/internal/RendererLib/RenderingPassInfo.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERINGPASSINFO_H -#define RAMSES_RENDERINGPASSINFO_H +#pragma once -#include "SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" -namespace ramses_internal +namespace ramses::internal { enum class ERenderingPassType : uint8_t { @@ -57,5 +56,3 @@ namespace ramses_internal using RenderingPassInfoVector = std::vector; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RenderingPassOrderComparator.h b/src/renderer/internal/RendererLib/RenderingPassOrderComparator.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RenderingPassOrderComparator.h rename to src/renderer/internal/RendererLib/RenderingPassOrderComparator.h index e06bfa0dd..951384f14 100644 --- a/renderer/RendererLib/RendererLib/include/RenderingPassOrderComparator.h +++ b/src/renderer/internal/RendererLib/RenderingPassOrderComparator.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERINGPASSORDERCOMPARATOR_H -#define RAMSES_RENDERINGPASSORDERCOMPARATOR_H +#pragma once -#include "SceneAPI/IScene.h" -#include "RendererLib/RenderingPassInfo.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/RendererLib/RenderingPassInfo.h" -namespace ramses_internal +namespace ramses::internal { class RenderingPassOrderComparator { @@ -40,5 +39,3 @@ namespace ramses_internal const IScene& m_scene; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/ResourceCachedScene.cpp b/src/renderer/internal/RendererLib/ResourceCachedScene.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/src/ResourceCachedScene.cpp rename to src/renderer/internal/RendererLib/ResourceCachedScene.cpp index 105f51c20..a1911dfce 100644 --- a/renderer/RendererLib/RendererLib/src/ResourceCachedScene.cpp +++ b/src/renderer/internal/RendererLib/ResourceCachedScene.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/ResourceCachedScene.h" -#include "RendererLib/IResourceDeviceHandleAccessor.h" -#include "RendererLib/TextureLinkCachedScene.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/ResourceCachedScene.h" +#include "internal/RendererLib/IResourceDeviceHandleAccessor.h" +#include "internal/RendererLib/TextureLinkCachedScene.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { ResourceCachedScene::ResourceCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo) : TextureLinkCachedScene(sceneLinksManager, sceneInfo) @@ -146,7 +146,7 @@ namespace ramses_internal return rtHandle; } - BlitPassHandle ResourceCachedScene::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle /*= BlitPassHandle::Invalid()*/) + BlitPassHandle ResourceCachedScene::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) { const BlitPassHandle blitPassHandle = TextureLinkCachedScene::allocateBlitPass(sourceRenderBufferHandle, destinationRenderBufferHandle, passHandle); @@ -291,9 +291,13 @@ namespace ramses_internal DeviceResourceHandle deviceHandle; if (dataResource.hash.isValid()) + { deviceHandle = resourceAccessor.getResourceDeviceHandle(dataResource.hash); + } else if (dataResource.dataBuffer.isValid()) + { deviceHandle = resourceAccessor.getDataBufferDeviceHandle(dataResource.dataBuffer, sceneId); + } if (!deviceHandle.isValid()) return false; @@ -311,7 +315,7 @@ namespace ramses_internal const SceneId sceneId = getSceneId(); - const uint32_t rtCount = static_cast(m_renderTargetCache.size()); + const auto rtCount = static_cast(m_renderTargetCache.size()); for (RenderTargetHandle rtHandle(0u); rtHandle < rtCount; ++rtHandle) { DeviceResourceHandle& deviceHandle = m_renderTargetCache[rtHandle.asMemoryHandle()]; @@ -374,9 +378,13 @@ namespace ramses_internal case TextureSampler::ContentType::ExternalTexture: { if (samplerData.contentHandle == InvalidMemoryHandle) + { m_deviceHandleCacheForTextures[sampler.asMemoryHandle()] = resourceAccessor.getEmptyExternalBufferDeviceHandle(); + } else - m_deviceHandleCacheForTextures[sampler.asMemoryHandle()] = resourceAccessor.getExternalBufferDeviceHandle(ExternalBufferHandle{ samplerData.contentHandle }); + { + m_deviceHandleCacheForTextures[sampler.asMemoryHandle()] = resourceAccessor.getExternalBufferDeviceHandle(ExternalBufferHandle{samplerData.contentHandle}); + } assert(m_deviceHandleCacheForTextures[sampler.asMemoryHandle()].isValid()); return true; @@ -391,7 +399,6 @@ namespace ramses_internal bool ResourceCachedScene::updateTextureSamplerResourceAsRenderBuffer(const IResourceDeviceHandleAccessor& resourceAccessor, const RenderBufferHandle bufferHandle, DeviceResourceHandle& deviceHandleOut) { - assert(getRenderBuffer(bufferHandle).type != ERenderBufferType_InvalidBuffer); const DeviceResourceHandle textureDeviceHandle = resourceAccessor.getRenderTargetBufferDeviceHandle(bufferHandle, getSceneId()); deviceHandleOut = textureDeviceHandle; @@ -569,7 +576,7 @@ namespace ramses_internal return m_textureSamplersDirty[indexIntoCache]; } - bool ResourceCachedScene::isGeometryDataLayout(const DataLayout& layout) const + bool ResourceCachedScene::IsGeometryDataLayout(const DataLayout& layout) { // TODO vaclav mark data layout explicitly if used for geometry or uniforms when network protocol can change // For now we check if indices field is contained to determine geometry data instance - this field exists even if no indices are actually used @@ -607,7 +614,9 @@ namespace ramses_internal m_vertexArrayCache[renderableAsIndex].deviceHandle = {}; if (!isRenderableAllocated(renderableHandle)) + { setRenderableVertexArrayDirtyFlag(renderableHandle, false); + } else if (!m_renderableResourcesDirty[renderableAsIndex]) { assert(getRenderable(renderableHandle).visibilityMode != EVisibilityMode::Off); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceCachedScene.h b/src/renderer/internal/RendererLib/ResourceCachedScene.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RendererLib/ResourceCachedScene.h rename to src/renderer/internal/RendererLib/ResourceCachedScene.h index 4835c9894..a65524025 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceCachedScene.h +++ b/src/renderer/internal/RendererLib/ResourceCachedScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCECACHEDSCENE_H -#define RAMSES_RESOURCECACHEDSCENE_H +#pragma once -#include "RendererLib/TextureLinkCachedScene.h" +#include "internal/RendererLib/TextureLinkCachedScene.h" -namespace ramses_internal +namespace ramses::internal { class IResourceDeviceHandleAccessor; @@ -29,11 +28,11 @@ namespace ramses_internal void preallocateSceneSize(const SceneSizeInformation& sizeInfo) override; // Renderable allocation - RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle = RenderableHandle::Invalid()) override; + RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle) override; void releaseRenderable (RenderableHandle renderableHandle) override; void setRenderableVisibility (RenderableHandle renderableHandle, EVisibilityMode visibility) override; void setRenderableStartVertex (RenderableHandle renderableHandle, uint32_t startVertex) override; - DataInstanceHandle allocateDataInstance (DataLayoutHandle handle, DataInstanceHandle instanceHandle = DataInstanceHandle::Invalid()) override; + DataInstanceHandle allocateDataInstance (DataLayoutHandle handle, DataInstanceHandle instanceHandle) override; void releaseDataInstance (DataInstanceHandle dataInstanceHandle) override; TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle) override; void releaseTextureSampler (TextureSamplerHandle handle) override; @@ -43,8 +42,8 @@ namespace ramses_internal void setDataResource (DataInstanceHandle dataInstanceHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) override; void setDataTextureSamplerHandle (DataInstanceHandle dataInstanceHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) override; - RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle = RenderTargetHandle::Invalid()) override; - BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) override; + RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle) override; + BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) override; void resetResourceCache(); @@ -80,7 +79,7 @@ namespace ramses_internal bool doesDataInstanceReferToDirtyTextureSampler(DataInstanceHandle handle) const; bool isDataInstanceDirty(DataInstanceHandle handle) const; bool isTextureSamplerDirty(TextureSamplerHandle handle) const; - bool isGeometryDataLayout(const DataLayout& layout) const; + static bool IsGeometryDataLayout(const DataLayout& layout); static bool CheckAndUpdateDeviceHandle(const IResourceDeviceHandleAccessor& resourceAccessor, DeviceResourceHandle& deviceHandleInOut, const ResourceContentHash& resourceHash); bool checkAndUpdateEffectResource(const IResourceDeviceHandleAccessor& resourceAccessor, RenderableHandle renderable); @@ -111,5 +110,3 @@ namespace ramses_internal bool m_blitPassesDirty = false; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceDescriptor.h b/src/renderer/internal/RendererLib/ResourceDescriptor.h similarity index 66% rename from renderer/RendererLib/RendererLib/include/RendererLib/ResourceDescriptor.h rename to src/renderer/internal/RendererLib/ResourceDescriptor.h index 8530888e4..ec20bfd91 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceDescriptor.h +++ b/src/renderer/internal/RendererLib/ResourceDescriptor.h @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEDESCRIPTOR_H -#define RAMSES_RESOURCEDESCRIPTOR_H +#pragma once -#include "RendererLib/EResourceStatus.h" -#include "Resource/ResourceTypes.h" -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Components/ManagedResource.h" -#include "Collections/HashMap.h" +#include "internal/RendererLib/Enums/EResourceStatus.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/Components/ManagedResource.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" -namespace ramses_internal +namespace ramses::internal { struct ResourceDescriptor { EResourceStatus status = EResourceStatus::Registered; - EResourceType type = EResourceType_Invalid; + EResourceType type = EResourceType::Invalid; DeviceResourceHandle deviceHandle; ResourceContentHash hash; SceneIdVector sceneUsage; @@ -34,5 +33,3 @@ namespace ramses_internal using ResourceDescriptors = HashMap; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/ResourceUploadRenderBackend.cpp b/src/renderer/internal/RendererLib/ResourceUploadRenderBackend.cpp similarity index 80% rename from renderer/RendererLib/RendererLib/src/ResourceUploadRenderBackend.cpp rename to src/renderer/internal/RendererLib/ResourceUploadRenderBackend.cpp index 159bacddc..dfe8f89e1 100644 --- a/renderer/RendererLib/RendererLib/src/ResourceUploadRenderBackend.cpp +++ b/src/renderer/internal/RendererLib/ResourceUploadRenderBackend.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/ResourceUploadRenderBackend.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IDevice.h" +#include "internal/RendererLib/ResourceUploadRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" -namespace ramses_internal +namespace ramses::internal { ResourceUploadRenderBackend::ResourceUploadRenderBackend(IContext& context, IDevice& device) : m_context(context) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploadRenderBackend.h b/src/renderer/internal/RendererLib/ResourceUploadRenderBackend.h similarity index 84% rename from renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploadRenderBackend.h rename to src/renderer/internal/RendererLib/ResourceUploadRenderBackend.h index 6c5974f57..428260f7e 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploadRenderBackend.h +++ b/src/renderer/internal/RendererLib/ResourceUploadRenderBackend.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEUPLOADRENDERBACKEND_H -#define RAMSES_RESOURCEUPLOADRENDERBACKEND_H +#pragma once -#include "RendererAPI/IResourceUploadRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IResourceUploadRenderBackend.h" -namespace ramses_internal +namespace ramses::internal { // This class uses a shared context for upload of resources in a parallel thread class ResourceUploadRenderBackend : public IResourceUploadRenderBackend @@ -30,5 +29,3 @@ namespace ramses_internal IDevice& m_device; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/ResourceUploader.cpp b/src/renderer/internal/RendererLib/ResourceUploader.cpp similarity index 76% rename from renderer/RendererLib/RendererLib/src/ResourceUploader.cpp rename to src/renderer/internal/RendererLib/ResourceUploader.cpp index beeb92d66..5ce89fc3e 100644 --- a/renderer/RendererLib/RendererLib/src/ResourceUploader.cpp +++ b/src/renderer/internal/RendererLib/ResourceUploader.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/ResourceUploader.h" -#include "Resource/IResource.h" -#include "Resource/ArrayResource.h" -#include "Resource/TextureResource.h" -#include "Resource/EffectResource.h" -#include "RendererAPI/IBinaryShaderCache.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IRenderBackend.h" -#include "Utils/ThreadLocalLogForced.h" -#include "Utils/TextureMathUtils.h" -#include "Components/ManagedResource.h" -#include "RendererLib/ResourceDescriptor.h" - -namespace ramses_internal +#include "internal/RendererLib/ResourceUploader.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/RendererLib/PlatformInterface/IBinaryShaderCache.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/Core/Utils/TextureMathUtils.h" +#include "internal/Components/ManagedResource.h" +#include "internal/RendererLib/ResourceDescriptor.h" + +namespace ramses::internal { ResourceUploader::ResourceUploader(bool asyncEffectUploadEnabled, IBinaryShaderCache* binaryShaderCache) : m_asyncEffectUploadEnabled(asyncEffectUploadEnabled) @@ -36,27 +36,27 @@ namespace ramses_internal switch (resourceObject.getTypeID()) { - case EResourceType_VertexArray: + case EResourceType::VertexArray: { - const ArrayResource* vertArray = resourceObject.convertTo(); + const auto* vertArray = resourceObject.convertTo(); const DeviceResourceHandle deviceHandle = device.allocateVertexBuffer(vertArray->getDecompressedDataSize()); device.uploadVertexBufferData(deviceHandle, vertArray->getResourceData().data(), vertArray->getDecompressedDataSize()); return deviceHandle; } - case EResourceType_IndexArray: + case EResourceType::IndexArray: { - const ArrayResource* indexArray = resourceObject.convertTo(); + const auto* indexArray = resourceObject.convertTo(); const DeviceResourceHandle deviceHandle = device.allocateIndexBuffer(indexArray->getElementType(), indexArray->getDecompressedDataSize()); device.uploadIndexBufferData(deviceHandle, indexArray->getResourceData().data(), indexArray->getDecompressedDataSize()); return deviceHandle; } - case EResourceType_Texture2D: - case EResourceType_Texture3D: - case EResourceType_TextureCube: - return uploadTexture(device, *resourceObject.convertTo(), outVRAMSize); - case EResourceType_Effect: + case EResourceType::Texture2D: + case EResourceType::Texture3D: + case EResourceType::TextureCube: + return UploadTexture(device, *resourceObject.convertTo(), outVRAMSize); + case EResourceType::Effect: { - const EffectResource* effectRes = resourceObject.convertTo(); + const auto* effectRes = resourceObject.convertTo(); const ResourceContentHash hash = effectRes->getHash(); const auto binaryShaderDeviceHandle = queryBinaryShaderCache(renderBackend, *effectRes, hash); if(binaryShaderDeviceHandle.isValid()) @@ -78,18 +78,18 @@ namespace ramses_internal { switch (type) { - case EResourceType_VertexArray: + case EResourceType::VertexArray: renderBackend.getDevice().deleteVertexBuffer(handle); break; - case EResourceType_IndexArray: + case EResourceType::IndexArray: renderBackend.getDevice().deleteIndexBuffer(handle); break; - case EResourceType_Texture2D: - case EResourceType_Texture3D: - case EResourceType_TextureCube: + case EResourceType::Texture2D: + case EResourceType::Texture3D: + case EResourceType::TextureCube: renderBackend.getDevice().deleteTexture(handle); break; - case EResourceType_Effect: + case EResourceType::Effect: renderBackend.getDevice().deleteShader(handle); break; default: @@ -97,26 +97,27 @@ namespace ramses_internal } } - DeviceResourceHandle ResourceUploader::uploadTexture(IDevice& device, const TextureResource& texture, uint32_t& vramSize) + DeviceResourceHandle ResourceUploader::UploadTexture(IDevice& device, const TextureResource& texture, uint32_t& vramSize) { const bool generateMipsFlag = texture.getGenerateMipChainFlag(); const auto& mipDataSizes = texture.getMipDataSizes(); - const uint32_t numProvidedMipLevels = static_cast(mipDataSizes.size()); + const auto numProvidedMipLevels = static_cast(mipDataSizes.size()); assert(numProvidedMipLevels == 1u || !generateMipsFlag); const uint32_t numMipLevelsToAllocate = generateMipsFlag ? TextureMathUtils::GetMipLevelCount(texture.getWidth(), texture.getHeight(), texture.getDepth()) : numProvidedMipLevels; vramSize = EstimateGPUAllocatedSizeOfTexture(texture, numMipLevelsToAllocate); + const uint32_t cubeFaces = 6u; // allocate texture DeviceResourceHandle textureDeviceHandle; switch (texture.getTypeID()) { - case EResourceType_Texture2D: + case EResourceType::Texture2D: textureDeviceHandle = device.allocateTexture2D(texture.getWidth(), texture.getHeight(), texture.getTextureFormat(), texture.getTextureSwizzle(), numMipLevelsToAllocate, vramSize); break; - case EResourceType_Texture3D: + case EResourceType::Texture3D: textureDeviceHandle = device.allocateTexture3D(texture.getWidth(), texture.getHeight(), texture.getDepth(), texture.getTextureFormat(), numMipLevelsToAllocate, vramSize); break; - case EResourceType_TextureCube: + case EResourceType::TextureCube: textureDeviceHandle = device.allocateTextureCube(texture.getWidth(), texture.getTextureFormat(), texture.getTextureSwizzle(), numMipLevelsToAllocate, vramSize); break; default: @@ -125,29 +126,29 @@ namespace ramses_internal assert(textureDeviceHandle.isValid()); // upload texture data - const Byte* pData = texture.getResourceData().data(); + const std::byte* pData = texture.getResourceData().data(); switch (texture.getTypeID()) { - case EResourceType_Texture2D: - case EResourceType_Texture3D: + case EResourceType::Texture2D: + case EResourceType::Texture3D: for (uint32_t mipLevel = 0u; mipLevel < static_cast(mipDataSizes.size()); ++mipLevel) { const uint32_t width = TextureMathUtils::GetMipSize(mipLevel, texture.getWidth()); const uint32_t height = TextureMathUtils::GetMipSize(mipLevel, texture.getHeight()); const uint32_t depth = TextureMathUtils::GetMipSize(mipLevel, texture.getDepth()); - device.uploadTextureData(textureDeviceHandle, mipLevel, 0u, 0u, 0u, width, height, depth, pData, mipDataSizes[mipLevel]); + device.uploadTextureData(textureDeviceHandle, mipLevel, 0u, 0u, 0u, width, height, depth, pData, mipDataSizes[mipLevel], 0); pData += mipDataSizes[mipLevel]; } break; - case EResourceType_TextureCube: - for (uint32_t i = 0; i < 6u; ++i) + case EResourceType::TextureCube: + static_assert(EnumTraits::VerifyElementCountIfSupported(cubeFaces)); + for (uint32_t faceId = 0; faceId < cubeFaces; ++faceId) { - const ETextureCubeFace faceId = static_cast(i); for (uint32_t mipLevel = 0u; mipLevel < static_cast(mipDataSizes.size()); ++mipLevel) { const uint32_t faceSize = TextureMathUtils::GetMipSize(mipLevel, texture.getWidth()); // texture faceID is encoded in Z offset - device.uploadTextureData(textureDeviceHandle, mipLevel, 0u, 0u, faceId, faceSize, faceSize, 1u, pData, mipDataSizes[mipLevel]); + device.uploadTextureData(textureDeviceHandle, mipLevel, 0u, 0u, faceId, faceSize, faceSize, 1u, pData, mipDataSizes[mipLevel], 0); pData += mipDataSizes[mipLevel]; } } @@ -184,7 +185,7 @@ namespace ramses_internal const uint32_t binaryShaderSize = m_binaryShaderCache->getBinaryShaderSize(hash); const BinaryShaderFormatID binaryShaderFormat = m_binaryShaderCache->getBinaryShaderFormat(hash); - UInt8Vector buffer(binaryShaderSize); + std::vector buffer(binaryShaderSize); m_binaryShaderCache->getBinaryShaderData(hash, &buffer.front(), binaryShaderSize); const DeviceResourceHandle binaryShaderHandle = device.uploadBinaryShader(effect, &buffer.front(), binaryShaderSize, binaryShaderFormat); @@ -210,11 +211,11 @@ namespace ramses_internal { IDevice& device = renderBackend.getDevice(); - UInt8Vector binaryShader; + std::vector binaryShader; BinaryShaderFormatID format; if (device.getBinaryShader(deviceHandle, binaryShader, format)) { - assert(binaryShader.size() != 0u); + assert(!binaryShader.empty()); m_binaryShaderCache->storeBinaryShader(hash, sceneid, &binaryShader.front(), static_cast(binaryShader.size()), format); } else @@ -228,12 +229,10 @@ namespace ramses_internal { return texture.getDecompressedDataSize(); } - else + if (texture.getTypeID() == EResourceType::TextureCube) { - if (texture.getTypeID() == EResourceType_TextureCube) - return 6u * TextureMathUtils::GetTotalMemoryUsedByMipmappedTexture(GetTexelSizeFromFormat(texture.getTextureFormat()), texture.getWidth(), texture.getWidth(), 1u, numMipLevelsToAllocate); - else - return TextureMathUtils::GetTotalMemoryUsedByMipmappedTexture(GetTexelSizeFromFormat(texture.getTextureFormat()), texture.getWidth(), texture.getHeight(), texture.getDepth(), numMipLevelsToAllocate); + return 6u * TextureMathUtils::GetTotalMemoryUsedByMipmappedTexture(GetTexelSizeFromFormat(texture.getTextureFormat()), texture.getWidth(), texture.getWidth(), 1u, numMipLevelsToAllocate); } + return TextureMathUtils::GetTotalMemoryUsedByMipmappedTexture(GetTexelSizeFromFormat(texture.getTextureFormat()), texture.getWidth(), texture.getHeight(), texture.getDepth(), numMipLevelsToAllocate); } } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploader.h b/src/renderer/internal/RendererLib/ResourceUploader.h similarity index 85% rename from renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploader.h rename to src/renderer/internal/RendererLib/ResourceUploader.h index ee8651cfd..98f07e967 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploader.h +++ b/src/renderer/internal/RendererLib/ResourceUploader.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEUPLOADER_H -#define RAMSES_RESOURCEUPLOADER_H +#pragma once -#include "RendererLib/IResourceUploader.h" -#include "Components/ManagedResource.h" +#include "internal/RendererLib/IResourceUploader.h" +#include "internal/Components/ManagedResource.h" -namespace ramses_internal +namespace ramses::internal { class IBinaryShaderCache; class TextureResource; @@ -30,7 +29,7 @@ namespace ramses_internal void storeShaderInBinaryShaderCache(IRenderBackend& renderBackend, DeviceResourceHandle deviceHandle, const ResourceContentHash& hash, SceneId sceneid) override; private: - DeviceResourceHandle uploadTexture(IDevice& device, const TextureResource& texture, uint32_t& vramSize); + static DeviceResourceHandle UploadTexture(IDevice& device, const TextureResource& texture, uint32_t& vramSize); DeviceResourceHandle queryBinaryShaderCache(IRenderBackend& renderBackend, const EffectResource& effect, ResourceContentHash hash); static uint32_t EstimateGPUAllocatedSizeOfTexture(const TextureResource& texture, uint32_t numMipLevelsToAllocate); @@ -39,5 +38,3 @@ namespace ramses_internal IBinaryShaderCache* const m_binaryShaderCache; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/ResourceUploadingManager.cpp b/src/renderer/internal/RendererLib/ResourceUploadingManager.cpp similarity index 87% rename from renderer/RendererLib/RendererLib/src/ResourceUploadingManager.cpp rename to src/renderer/internal/RendererLib/ResourceUploadingManager.cpp index 72e4be52f..26a4159d0 100644 --- a/renderer/RendererLib/RendererLib/src/ResourceUploadingManager.cpp +++ b/src/renderer/internal/RendererLib/ResourceUploadingManager.cpp @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/ResourceUploadingManager.h" -#include "RendererLib/RendererResourceRegistry.h" -#include "RendererLib/IResourceUploader.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "RendererAPI/IDevice.h" -#include "Utils/ThreadLocalLogForced.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Resource/EffectResource.h" +#include "internal/RendererLib/ResourceUploadingManager.h" +#include "internal/RendererLib/RendererResourceRegistry.h" +#include "internal/RendererLib/IResourceUploader.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/SceneGraph/Resource/EffectResource.h" #include #include -namespace ramses_internal +namespace ramses::internal { ResourceUploadingManager::ResourceUploadingManager( RendererResourceRegistry& resources, @@ -35,7 +35,6 @@ namespace ramses_internal , m_uploader{ std::move(uploader) } , m_renderBackend(renderBackend) , m_asyncEffectUploader(asyncEffectUploader) - , m_keepEffects(displayConfig.getKeepEffectsUploaded()) , m_frameTimer(frameTimer) , m_resourceCacheSize(displayConfig.getGPUMemoryCacheSize()) , m_resourceUploadBatchSize(displayConfig.getResourceUploadBatchSize()) @@ -52,12 +51,11 @@ namespace ramses_internal // Or in case display is being destructed together with scenes and there is no more rendering, // i.e. no more deferred upload/unloads ResourceContentHashVector resourcesToUnload; - getResourcesToUnloadNext(resourcesToUnload, false, std::numeric_limits::max()); + getResourcesToUnloadNext(resourcesToUnload, std::numeric_limits::max(), false); unloadResources(resourcesToUnload); - for(const auto& resource : m_resources.getAllResourceDescriptors()) + for(const auto& resource [[maybe_unused]] : m_resources.getAllResourceDescriptors()) { - UNUSED(resource); assert(resource.value.status != EResourceStatus::Uploaded); } } @@ -75,7 +73,7 @@ namespace ramses_internal const uint64_t sizeToBeFreed = getAmountOfMemoryToBeFreedForNewResources(sizeToUpload); ResourceContentHashVector resourcesToUnload; - getResourcesToUnloadNext(resourcesToUnload, m_keepEffects, sizeToBeFreed); + getResourcesToUnloadNext(resourcesToUnload, sizeToBeFreed); unloadResources(resourcesToUnload); uploadResources(resourcesToUpload); @@ -158,7 +156,7 @@ namespace ramses_internal const auto numRemaining = resourcesToUpload.size() - numUploaded; LOG_INFO(CONTEXT_RENDERER, "ResourceUploadingManager::uploadResources: Interrupt: Exceeded time for resource upload (uploaded " << numUploaded << " resources of size " << sizeUploaded << " B, remaining " << numRemaining << " resources to upload). dt " << sectionDuration.count() << "ms"); - LOG_INFO_F(CONTEXT_RENDERER, [&](ramses_internal::StringOutputStream& logger) + LOG_INFO_F(CONTEXT_RENDERER, [&](StringOutputStream& logger) { logger << "Remaining resources in queue to upload:"; for (size_t j = numUploaded; j < resourcesToUpload.size() && j < numUploaded + 10; ++j) @@ -208,7 +206,7 @@ namespace ramses_internal else { // effect not found in cache, schedule for upload in uploader thread - assert(rd.type == EResourceType_Effect); + assert(rd.type == EResourceType::Effect); assert(std::find_if(std::cbegin(m_effectsToUpload), std::cend(m_effectsToUpload), [&](const auto& e){ return e->getHash() == rd.hash;}) == m_effectsToUpload.cend()); m_effectsToUpload.push_back(pResource->convertTo()); m_resources.setResourceScheduledForUpload(rd.hash); @@ -234,7 +232,7 @@ namespace ramses_internal m_resources.unregisterResource(rd.hash); } - void ResourceUploadingManager::getResourcesToUnloadNext(ResourceContentHashVector& resourcesToUnload, bool keepEffects, uint64_t sizeToBeFreed) const + void ResourceUploadingManager::getResourcesToUnloadNext(ResourceContentHashVector& resourcesToUnload, uint64_t sizeToBeFreed, bool keepEffects) const { assert(resourcesToUnload.empty()); const ResourceContentHashVector& unusedResources = m_resources.getAllResourcesNotInUseByScenes(); @@ -251,15 +249,11 @@ namespace ramses_internal } const ResourceDescriptor& rd = m_resources.getResourceDescriptor(hash); - if (rd.status == EResourceStatus::Uploaded) + if (rd.status == EResourceStatus::Uploaded && !(keepEffects && rd.type == EResourceType::Effect)) { - const bool keepEffectCached = keepEffects && (rd.type == EResourceType_Effect); - if (!keepEffectCached) - { - resourcesToUnload.push_back(hash); - assert(m_resourceSizes.contains(hash)); - sizeToUnload += *m_resourceSizes.get(hash); - } + resourcesToUnload.push_back(hash); + assert(m_resourceSizes.contains(hash)); + sizeToUnload += *m_resourceSizes.get(hash); } } } @@ -334,15 +328,9 @@ namespace ramses_internal { return sizeToUpload - remainingCacheSize; } - else - { - return 0u; - } - } - else - { - // cache already exceeded, try unloading all that is above cache limit plus size for new resources to be uploaded - return sizeToUpload + m_resourceTotalUploadedSize - m_resourceCacheSize; + return 0u; } + // cache already exceeded, try unloading all that is above cache limit plus size for new resources to be uploaded + return sizeToUpload + m_resourceTotalUploadedSize - m_resourceCacheSize; } } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploadingManager.h b/src/renderer/internal/RendererLib/ResourceUploadingManager.h similarity index 88% rename from renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploadingManager.h rename to src/renderer/internal/RendererLib/ResourceUploadingManager.h index 0cbfa2458..8b8e256b4 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/ResourceUploadingManager.h +++ b/src/renderer/internal/RendererLib/ResourceUploadingManager.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEUPLOADINGMANAGER_H -#define RAMSES_RESOURCEUPLOADINGMANAGER_H +#pragma once -#include "RendererLib/ResourceDescriptor.h" -#include "RendererLib/IResourceUploader.h" -#include "RendererLib/AsyncEffectUploader.h" -#include "Collections/HashMap.h" +#include "internal/RendererLib/ResourceDescriptor.h" +#include "internal/RendererLib/IResourceUploader.h" +#include "internal/RendererLib/AsyncEffectUploader.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" #include -namespace ramses_internal +namespace ramses::internal { class RendererResourceRegistry; class IRenderBackend; @@ -53,7 +52,7 @@ namespace ramses_internal void syncEffects(); void uploadResource(const ResourceDescriptor& rd); void unloadResource(const ResourceDescriptor& rd); - void getResourcesToUnloadNext(ResourceContentHashVector& resourcesToUnload, bool keepEffects, uint64_t sizeToBeFreed) const; + void getResourcesToUnloadNext(ResourceContentHashVector& resourcesToUnload, uint64_t sizeToBeFreed, bool keepEffects = true) const; void getAndPrepareResourcesToUploadNext(ResourceContentHashVector& resourcesToUpload, uint64_t& totalSize) const; [[nodiscard]] int32_t getScenePriority(const ResourceDescriptor& rd) const; [[nodiscard]] uint64_t getAmountOfMemoryToBeFreedForNewResources(uint64_t sizeToUpload) const; @@ -65,7 +64,6 @@ namespace ramses_internal EffectsRawResources m_effectsToUpload; EffectsGpuResources m_effectsUploadedTemp; //to avoid re-allocation each frame - const bool m_keepEffects; const FrameTimer& m_frameTimer; using SizeMap = HashMap; @@ -80,5 +78,3 @@ namespace ramses_internal mutable std::map m_buckets; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneDependencyChecker.cpp b/src/renderer/internal/RendererLib/SceneDependencyChecker.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/src/SceneDependencyChecker.cpp rename to src/renderer/internal/RendererLib/SceneDependencyChecker.cpp index 469347822..f1e99911a 100644 --- a/renderer/RendererLib/RendererLib/src/SceneDependencyChecker.cpp +++ b/src/renderer/internal/RendererLib/SceneDependencyChecker.cpp @@ -6,15 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneDependencyChecker.h" -#include "RendererAPI/Types.h" +#include "internal/RendererLib/SceneDependencyChecker.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { - SceneDependencyChecker::SceneDependencyChecker() - : m_dirty(false) - { - } + SceneDependencyChecker::SceneDependencyChecker() = default; bool SceneDependencyChecker::addDependency(SceneId providerScene, SceneId consumerScene) { diff --git a/renderer/RendererLib/RendererLib/include/SceneDependencyChecker.h b/src/renderer/internal/RendererLib/SceneDependencyChecker.h similarity index 84% rename from renderer/RendererLib/RendererLib/include/SceneDependencyChecker.h rename to src/renderer/internal/RendererLib/SceneDependencyChecker.h index 9c5188c1b..7b0afc2d1 100644 --- a/renderer/RendererLib/RendererLib/include/SceneDependencyChecker.h +++ b/src/renderer/internal/RendererLib/SceneDependencyChecker.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEDEPENDENCYCHECKER_H -#define RAMSES_SCENEDEPENDENCYCHECKER_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" -#include "Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" -namespace ramses_internal +namespace ramses::internal { class SceneDependencyChecker @@ -36,8 +35,6 @@ namespace ramses_internal ConsumerToProvidersMap m_consumerToProvidersMap; mutable SceneIdVector m_sceneOrderList; - mutable bool m_dirty; + mutable bool m_dirty{false}; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneDisplayTracker.cpp b/src/renderer/internal/RendererLib/SceneDisplayTracker.cpp similarity index 89% rename from renderer/RendererLib/RendererLib/src/SceneDisplayTracker.cpp rename to src/renderer/internal/RendererLib/SceneDisplayTracker.cpp index 0d217cc7a..ceffdd3fa 100644 --- a/renderer/RendererLib/RendererLib/src/SceneDisplayTracker.cpp +++ b/src/renderer/internal/RendererLib/SceneDisplayTracker.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneDisplayTracker.h" +#include "internal/RendererLib/SceneDisplayTracker.h" #include -namespace ramses_internal +namespace ramses::internal { void SceneDisplayTracker::setSceneOwnership(SceneId scene, DisplayHandle display) { @@ -28,9 +28,13 @@ namespace ramses_internal for (auto it = m_sceneToDisplay.begin(); it != m_sceneToDisplay.end(); ) { if (it->second == display) + { it = m_sceneToDisplay.erase(it); + } else + { ++it; + } } } } diff --git a/src/renderer/internal/RendererLib/SceneDisplayTracker.h b/src/renderer/internal/RendererLib/SceneDisplayTracker.h new file mode 100644 index 000000000..e9c2dfb71 --- /dev/null +++ b/src/renderer/internal/RendererLib/SceneDisplayTracker.h @@ -0,0 +1,86 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/RendererLib/RendererEvent.h" +#include +#include + +namespace ramses::internal +{ + class SceneDisplayTracker + { + public: + void setSceneOwnership(SceneId scene, DisplayHandle display); + [[nodiscard]] DisplayHandle getSceneOwnership(SceneId scene) const; + void unregisterDisplay(DisplayHandle display); + + // 3 return types - valid display, invalid display (error), no value (broadcast) + [[nodiscard]] std::optional determineDisplayFromRendererCommand(const RendererCommand::Variant& cmd) const; + + private: + // scene commands + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::ReceiveScene& cmd) const { return getSceneOwnership(cmd.info.sceneID); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::UpdateScene& cmd) const { return getSceneOwnership(cmd.scene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetSceneState& cmd) const { return getSceneOwnership(cmd.scene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::SetSceneDisplayBufferAssignment& cmd) const { return getSceneOwnership(cmd.scene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::PickEvent& cmd) const { return getSceneOwnership(cmd.scene); } + // link commands + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkData& cmd) const { return getSceneOwnership(cmd.consumerScene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkOffscreenBuffer& cmd) const { return getSceneOwnership(cmd.consumerScene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkStreamBuffer& cmd) const { return getSceneOwnership(cmd.consumerScene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::LinkExternalBuffer& cmd) const { return getSceneOwnership(cmd.consumerScene); } + [[nodiscard]] std::optional getDisplayOf(const RendererCommand::UnlinkData& cmd) const { return getSceneOwnership(cmd.consumerScene); } + // display commands + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetSceneMapping& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::CreateDisplay& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::DestroyDisplay& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::CreateOffscreenBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::CreateDmaOffscreenBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::DestroyOffscreenBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::CreateStreamBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::DestroyStreamBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::CreateExternalBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::DestroyExternalBuffer& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetClearFlags& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetClearColor& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetExterallyOwnedWindowSize& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::ReadPixels& cmd) { return cmd.display; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::ConfirmationEcho& cmd) { return cmd.display; } + // broadcast commands + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::ScenePublished& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SceneUnpublished& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetSkippingOfUnmodifiedBuffers& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::LogStatistics& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::LogInfo& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCListIviSurfaces& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCSetIviSurfaceVisibility& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCSetIviSurfaceOpacity& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCSetIviSurfaceDestRectangle& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCScreenshot& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCAddIviSurfaceToIviLayer& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCSetIviLayerVisibility& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCRemoveIviSurfaceFromIviLayer& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SCDestroyIviSurface& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetLimits_FrameBudgets& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetLimits_FlushesForceApply& /*unused*/) { return {}; } + [[nodiscard]] static std::optional getDisplayOf(const RendererCommand::SetLimits_FlushesForceUnsubscribe& /*unused*/) { return {}; } + + std::unordered_map m_sceneToDisplay; + }; + + // NOLINTNEXTLINE(readability-convert-member-functions-to-static): false positive + inline std::optional SceneDisplayTracker::determineDisplayFromRendererCommand(const RendererCommand::Variant& cmd) const + { + return std::visit([&](const auto& c) { return getDisplayOf(c); }, cmd); + } +} diff --git a/renderer/RendererLib/RendererLib/src/SceneExpirationMonitor.cpp b/src/renderer/internal/RendererLib/SceneExpirationMonitor.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/src/SceneExpirationMonitor.cpp rename to src/renderer/internal/RendererLib/SceneExpirationMonitor.cpp index 1ffa37d38..1d4ff8cc7 100644 --- a/renderer/RendererLib/RendererLib/src/SceneExpirationMonitor.cpp +++ b/src/renderer/internal/RendererLib/SceneExpirationMonitor.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/RendererStatistics.h" -#include "RendererEventCollector.h" -#include "Scene/SceneActionApplier.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "Utils/ThreadLocalLogForced.h" - -namespace ramses_internal +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" + +namespace ramses::internal { SceneExpirationMonitor::SceneExpirationMonitor(const RendererScenes& scenes, RendererEventCollector& eventCollector, RendererStatistics& statistics) : m_scenes(scenes) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneExpirationMonitor.h b/src/renderer/internal/RendererLib/SceneExpirationMonitor.h similarity index 88% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneExpirationMonitor.h rename to src/renderer/internal/RendererLib/SceneExpirationMonitor.h index df5edfd2f..ee6f92192 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneExpirationMonitor.h +++ b/src/renderer/internal/RendererLib/SceneExpirationMonitor.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEEXPIRATIONMONITOR_H -#define RAMSES_SCENEEXPIRATIONMONITOR_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "SceneAPI/SceneVersionTag.h" -#include "RendererLib/StagingInfo.h" -#include "Components/FlushTimeInformation.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/RendererLib/StagingInfo.h" +#include "internal/Components/FlushTimeInformation.h" #include #include -namespace ramses_internal +namespace ramses::internal { class RendererScenes; class RendererEventCollector; @@ -60,5 +59,3 @@ namespace ramses_internal RendererStatistics& m_statistics; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneLinkScene.cpp b/src/renderer/internal/RendererLib/SceneLinkScene.cpp similarity index 91% rename from renderer/RendererLib/RendererLib/src/SceneLinkScene.cpp rename to src/renderer/internal/RendererLib/SceneLinkScene.cpp index ab594c2f0..55f96e71d 100644 --- a/renderer/RendererLib/RendererLib/src/SceneLinkScene.cpp +++ b/src/renderer/internal/RendererLib/SceneLinkScene.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneLinkScene.h" -#include "RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/SceneLinkScene.h" +#include "internal/RendererLib/SceneLinksManager.h" -namespace ramses_internal +namespace ramses::internal { SceneLinkScene::SceneLinkScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo) : TransformationCachedSceneWithExplicitMemory(sceneInfo) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneLinkScene.h b/src/renderer/internal/RendererLib/SceneLinkScene.h similarity index 81% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneLinkScene.h rename to src/renderer/internal/RendererLib/SceneLinkScene.h index ce9303fa0..d3933ecf9 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneLinkScene.h +++ b/src/renderer/internal/RendererLib/SceneLinkScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENELINKSCENE_H -#define RAMSES_SCENELINKSCENE_H +#pragma once -#include "Scene/TransformationCachedScene.h" +#include "internal/SceneGraph/Scene/TransformationCachedScene.h" -namespace ramses_internal +namespace ramses::internal { class SceneLinksManager; @@ -21,12 +20,10 @@ namespace ramses_internal explicit SceneLinkScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo = SceneInfo()); // From IScene - DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle) override; void releaseDataSlot(DataSlotHandle handle) override; protected: SceneLinksManager& m_sceneLinksManager; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneLinks.cpp b/src/renderer/internal/RendererLib/SceneLinks.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/src/SceneLinks.cpp rename to src/renderer/internal/RendererLib/SceneLinks.cpp index 2b1b3c488..a24d62256 100644 --- a/renderer/RendererLib/RendererLib/src/SceneLinks.cpp +++ b/src/renderer/internal/RendererLib/SceneLinks.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneLinks.h" +#include "internal/RendererLib/SceneLinks.h" -namespace ramses_internal +namespace ramses::internal { void SceneLinks::addLink(SceneId providerSceneId, DataSlotHandle providerSlotHandle, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneLinks.h b/src/renderer/internal/RendererLib/SceneLinks.h similarity index 91% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneLinks.h rename to src/renderer/internal/RendererLib/SceneLinks.h index 74c333be0..84493f074 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneLinks.h +++ b/src/renderer/internal/RendererLib/SceneLinks.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENELINKS_H -#define RAMSES_SCENELINKS_H +#pragma once -#include "SceneAPI/SceneId.h" -#include "SceneAPI/Handles.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { struct SceneLink { @@ -47,5 +46,3 @@ namespace ramses_internal SceneLinkVector m_links; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneLinksManager.cpp b/src/renderer/internal/RendererLib/SceneLinksManager.cpp similarity index 92% rename from renderer/RendererLib/RendererLib/src/SceneLinksManager.cpp rename to src/renderer/internal/RendererLib/SceneLinksManager.cpp index 2c0eaef9b..564bbc50e 100644 --- a/renderer/RendererLib/RendererLib/src/SceneLinksManager.cpp +++ b/src/renderer/internal/RendererLib/SceneLinksManager.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DataLinkUtils.h" -#include "RendererEventCollector.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DataLinkUtils.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" -namespace ramses_internal +namespace ramses::internal { SceneLinksManager::SceneLinksManager(RendererScenes& rendererScenes, RendererEventCollector& rendererEventCollector) : m_rendererScenes(rendererScenes) @@ -63,15 +63,15 @@ namespace ramses_internal const EDataSlotType consumerSlotType = DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_rendererScenes).type; bool linkSuccess = false; - if (providerSlotType == EDataSlotType_TransformationProvider && consumerSlotType == EDataSlotType_TransformationConsumer) + if (providerSlotType == EDataSlotType::TransformationProvider && consumerSlotType == EDataSlotType::TransformationConsumer) { linkSuccess = m_transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle); } - else if (providerSlotType == EDataSlotType_DataProvider && consumerSlotType == EDataSlotType_DataConsumer) + else if (providerSlotType == EDataSlotType::DataProvider && consumerSlotType == EDataSlotType::DataConsumer) { linkSuccess = m_dataReferenceLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle); } - else if (providerSlotType == EDataSlotType_TextureProvider && consumerSlotType == EDataSlotType_TextureConsumer) + else if (providerSlotType == EDataSlotType::TextureProvider && consumerSlotType == EDataSlotType::TextureConsumer) { linkSuccess = m_textureLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle); } @@ -112,7 +112,7 @@ namespace ramses_internal } const EDataSlotType consumerSlotType = DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_rendererScenes).type; - if (consumerSlotType != EDataSlotType_TextureConsumer) + if (consumerSlotType != EDataSlotType::TextureConsumer) { LOG_ERROR(CONTEXT_RENDERER, "SceneLinksManager::createBufferLink failed: consumer data " << consumerId << " refers to a slot that is not of texture type! (consumer scene: " << consumerSceneId << ")"); m_rendererEventCollector.addBufferEvent(ERendererEventType::SceneDataBufferLinkFailed, providerBuffer, consumerSceneId, consumerId); @@ -148,15 +148,15 @@ namespace ramses_internal bool unlinkSuccess = false; SceneId providerSceneId; - if (consumerSlotType == EDataSlotType_TransformationConsumer) + if (consumerSlotType == EDataSlotType::TransformationConsumer) { unlinkSuccess = m_transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle, &providerSceneId); } - else if (consumerSlotType == EDataSlotType_DataConsumer) + else if (consumerSlotType == EDataSlotType::DataConsumer) { unlinkSuccess = m_dataReferenceLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle, &providerSceneId); } - else if (consumerSlotType == EDataSlotType_TextureConsumer) + else if (consumerSlotType == EDataSlotType::TextureConsumer) { unlinkSuccess = m_textureLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle, &providerSceneId); } @@ -189,14 +189,14 @@ namespace ramses_internal switch (dataSlot.type) { - case EDataSlotType_DataProvider: - case EDataSlotType_TransformationProvider: - case EDataSlotType_TextureProvider: + case EDataSlotType::DataProvider: + case EDataSlotType::TransformationProvider: + case EDataSlotType::TextureProvider: m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotProviderCreated, sceneId, SceneId(0), dataSlot.id, DataSlotId(0)); break; - case EDataSlotType_DataConsumer: - case EDataSlotType_TransformationConsumer: - case EDataSlotType_TextureConsumer: + case EDataSlotType::DataConsumer: + case EDataSlotType::TransformationConsumer: + case EDataSlotType::TextureConsumer: m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0), sceneId, DataSlotId(0), dataSlot.id); break; default: @@ -212,12 +212,12 @@ namespace ramses_internal switch (dataSlot.type) { - case EDataSlotType_TransformationProvider: + case EDataSlotType::TransformationProvider: removeLinksToProvider(sceneId, dataSlotHandle, m_transformationLinkManager); m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotProviderDestroyed, sceneId, SceneId(0), dataSlot.id, DataSlotId(0)); break; - case EDataSlotType_TransformationConsumer: + case EDataSlotType::TransformationConsumer: if (m_transformationLinkManager.getSceneLinks().hasLinkedProvider(sceneId, dataSlotHandle)) { if (m_transformationLinkManager.removeDataLink(sceneId, dataSlotHandle)) @@ -228,12 +228,12 @@ namespace ramses_internal m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotConsumerDestroyed, SceneId(0), sceneId, DataSlotId(0), dataSlot.id); break; - case EDataSlotType_DataProvider: + case EDataSlotType::DataProvider: removeLinksToProvider(sceneId, dataSlotHandle, m_dataReferenceLinkManager); m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotProviderDestroyed, sceneId, SceneId(0), dataSlot.id, DataSlotId(0)); break; - case EDataSlotType_DataConsumer: + case EDataSlotType::DataConsumer: if (m_dataReferenceLinkManager.getSceneLinks().hasLinkedProvider(sceneId, dataSlotHandle)) { if (m_dataReferenceLinkManager.removeDataLink(sceneId, dataSlotHandle)) @@ -244,12 +244,12 @@ namespace ramses_internal m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotConsumerDestroyed, SceneId(0), sceneId, DataSlotId(0), dataSlot.id); break; - case EDataSlotType_TextureProvider: + case EDataSlotType::TextureProvider: removeLinksToProvider(sceneId, dataSlotHandle, m_textureLinkManager); m_rendererEventCollector.addDataLinkEvent(ERendererEventType::SceneDataSlotProviderDestroyed, sceneId, SceneId(0), dataSlot.id, DataSlotId(0)); break; - case EDataSlotType_TextureConsumer: + case EDataSlotType::TextureConsumer: if (m_textureLinkManager.getSceneLinks().hasLinkedProvider(sceneId, dataSlotHandle) || m_textureLinkManager.getOffscreenBufferLinks().hasLinkedProvider(sceneId, dataSlotHandle) || m_textureLinkManager.getStreamBufferLinks().hasLinkedProvider(sceneId, dataSlotHandle) || diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneLinksManager.h b/src/renderer/internal/RendererLib/SceneLinksManager.h similarity index 90% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneLinksManager.h rename to src/renderer/internal/RendererLib/SceneLinksManager.h index f48fb59f0..9626991d7 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneLinksManager.h +++ b/src/renderer/internal/RendererLib/SceneLinksManager.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENELINKSMANAGER_H -#define RAMSES_SCENELINKSMANAGER_H +#pragma once -#include "RendererLib/TransformationLinkManager.h" -#include "RendererLib/DataReferenceLinkManager.h" -#include "RendererLib/TextureLinkManager.h" -#include "SceneAPI/DataSlot.h" +#include "internal/RendererLib/TransformationLinkManager.h" +#include "internal/RendererLib/DataReferenceLinkManager.h" +#include "internal/RendererLib/TextureLinkManager.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" -namespace ramses_internal +namespace ramses::internal { class RendererScenes; class RendererEventCollector; @@ -57,5 +56,3 @@ namespace ramses_internal TextureLinkManager m_textureLinkManager; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneReferenceLogic.cpp b/src/renderer/internal/RendererLib/SceneReferenceLogic.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/src/SceneReferenceLogic.cpp rename to src/renderer/internal/RendererLib/SceneReferenceLogic.cpp index 7bf1df859..7a58fca91 100644 --- a/renderer/RendererLib/RendererLib/src/SceneReferenceLogic.cpp +++ b/src/renderer/internal/RendererLib/SceneReferenceLogic.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneReferenceLogic.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/RendererSceneControlLogic.h" -#include "RendererLib/IRendererSceneUpdater.h" -#include "RendererLib/SceneReferenceOwnership.h" -#include "RendererFramework/IRendererSceneEventSender.h" -#include "Utils/ThreadLocalLogForced.h" +#include "internal/RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" +#include "internal/RendererLib/IRendererSceneUpdater.h" +#include "internal/RendererLib/SceneReferenceOwnership.h" +#include "internal/RendererLib/IRendererSceneEventSender.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" #include -namespace ramses_internal +namespace ramses::internal { SceneReferenceLogic::SceneReferenceLogic(const RendererScenes& scenes, IRendererSceneControlLogic& sceneLogic, IRendererSceneUpdater& sceneUpdater, IRendererSceneEventSender& sender, SceneReferenceOwnership& sharedOwnership) : m_rendererScenes(scenes) @@ -108,12 +108,15 @@ namespace ramses_internal case ERendererEventType::SceneExpired: case ERendererEventType::SceneRecoveredFromExpiration: { - auto logExpirationMsg = [](SceneId master, SceneId evtScene, const char* msg) - { + auto logExpirationMsg = [](SceneId master, SceneId evtScene, const char* msg) { if (master != evtScene) + { LOG_INFO_P(CONTEXT_RENDERER, "SceneReferenceLogic: received event scene reference (master {} / ref {}) {}", master, evtScene, msg); + } else + { LOG_INFO_P(CONTEXT_RENDERER, "SceneReferenceLogic: received event master scene {} {}", master, msg); + } }; auto masterSceneId = findMasterSceneForReferencedScene(evt.sceneId); // check if scene from event is master itself @@ -301,8 +304,10 @@ namespace ramses_internal const auto& masterScene = m_rendererScenes.getScene(masterSceneId); std::vector> releasedRefs; for (const auto& sceneRefIt : masterInfo.sceneReferences) + { if (!masterScene.isSceneReferenceAllocated(sceneRefIt.second)) - releasedRefs.push_back({ sceneRefIt.first, sceneRefIt.second }); + releasedRefs.emplace_back(sceneRefIt.first, sceneRefIt.second); + } if (!releasedRefs.empty()) { @@ -426,8 +431,10 @@ namespace ramses_internal { assert(std::count_if(m_masterScenes.cbegin(), m_masterScenes.cend(), [sceneId](const auto& s) { return s.second.sceneReferences.count(sceneId) != 0; }) <= 1); for (const auto& master : m_masterScenes) - if (master.second.sceneReferences.count(sceneId)) + { + if (master.second.sceneReferences.count(sceneId) != 0u) return master.first; + } return SceneId::Invalid(); } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneReferenceLogic.h b/src/renderer/internal/RendererLib/SceneReferenceLogic.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneReferenceLogic.h rename to src/renderer/internal/RendererLib/SceneReferenceLogic.h index ed5562c43..21e36ae10 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneReferenceLogic.h +++ b/src/renderer/internal/RendererLib/SceneReferenceLogic.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEREFERENCELOGIC_H -#define RAMSES_SCENEREFERENCELOGIC_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" -#include "SceneReferencing/SceneReferenceAction.h" -#include "RendererLib/RendererEvent.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" +#include "internal/RendererLib/RendererEvent.h" #include #include -namespace ramses_internal +namespace ramses::internal { class RendererScenes; class IRendererSceneControlLogic; @@ -90,5 +89,3 @@ namespace ramses_internal friend class RendererLogger; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneReferenceOwnership.h b/src/renderer/internal/RendererLib/SceneReferenceOwnership.h similarity index 87% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneReferenceOwnership.h rename to src/renderer/internal/RendererLib/SceneReferenceOwnership.h index fb7f1d583..bb6c67901 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneReferenceOwnership.h +++ b/src/renderer/internal/RendererLib/SceneReferenceOwnership.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEREFERENCEOWNERSHIP_H -#define RAMSES_SCENEREFERENCEOWNERSHIP_H +#pragma once -#include "RendererAPI/Types.h" -#include "SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include #include -namespace ramses_internal +namespace ramses::internal { class SceneReferenceOwnership { @@ -39,5 +38,3 @@ namespace ramses_internal std::unordered_map m_refSceneToOwner; }; } - -#endif diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/SceneRenderExecutionIterator.h b/src/renderer/internal/RendererLib/SceneRenderExecutionIterator.h similarity index 90% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/SceneRenderExecutionIterator.h rename to src/renderer/internal/RendererLib/SceneRenderExecutionIterator.h index 1b86437e9..01369fa42 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/SceneRenderExecutionIterator.h +++ b/src/renderer/internal/RendererLib/SceneRenderExecutionIterator.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENERENDEREXECUTIONITERATOR_H -#define RAMSES_SCENERENDEREXECUTIONITERATOR_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include -namespace ramses_internal +namespace ramses::internal { class SceneRenderExecutionIterator { @@ -61,5 +60,3 @@ namespace ramses_internal uint32_t m_flattenedRenderableIdx = 0u; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneResourceUploader.cpp b/src/renderer/internal/RendererLib/SceneResourceUploader.cpp similarity index 73% rename from renderer/RendererLib/RendererLib/src/SceneResourceUploader.cpp rename to src/renderer/internal/RendererLib/SceneResourceUploader.cpp index 3bfc850e1..77fcf7e3c 100644 --- a/renderer/RendererLib/RendererLib/src/SceneResourceUploader.cpp +++ b/src/renderer/internal/RendererLib/SceneResourceUploader.cpp @@ -6,15 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneResourceUploader.h" -#include "RendererLib/IRendererResourceManager.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/TextureBuffer.h" -#include "SceneAPI/BlitPass.h" -#include "Scene/DataLayout.h" - -namespace ramses_internal +#include "internal/RendererLib/SceneResourceUploader.h" +#include "internal/RendererLib/IRendererResourceManager.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/TextureBuffer.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "internal/SceneGraph/Scene/DataLayout.h" + +namespace ramses::internal { void SceneResourceUploader::UploadRenderTarget(const IScene& scene, RenderTargetHandle renderTarget, IRendererResourceManager& resourceManager) { @@ -53,15 +54,21 @@ namespace ramses_internal resourceManager.uploadTextureBuffer(textureBuffer, mip0.width, mip0.height, texBuffer.textureFormat, static_cast(mipMaps.size()), scene.getSceneId()); } - void SceneResourceUploader::UpdateTextureBuffer(const IScene& scene, TextureBufferHandle textureBuffer, IRendererResourceManager& resourceManager) + void SceneResourceUploader::UpdateTextureBuffer(const RendererCachedScene& scene, TextureBufferHandle textureBuffer, IRendererResourceManager& resourceManager) { const TextureBuffer& texBuffer = scene.getTextureBuffer(textureBuffer); + + const auto& update = scene.getTextureBufferUpdate(textureBuffer); const auto& mipMaps = texBuffer.mipMaps; + assert(update.size() == mipMaps.size()); for (uint32_t mipLevel = 0u; mipLevel < static_cast(mipMaps.size()); ++mipLevel) { const auto& mip = mipMaps[mipLevel]; - resourceManager.updateTextureBuffer(textureBuffer, mipLevel, 0u, 0u, mip.width, mip.height, texBuffer.mipMaps[mipLevel].data.data(), scene.getSceneId()); + const auto& quad = update[mipLevel]; + if (quad.getArea() != 0) + resourceManager.updateTextureBuffer(textureBuffer, mipLevel, quad, mip.width, mip.data.data(), scene.getSceneId()); } + scene.popTextureBufferUpdate(textureBuffer); } void SceneResourceUploader::UploadVertexArray(const IScene& scene, RenderableHandle renderableHandle, IRendererResourceManager& resourceManager) @@ -82,9 +89,13 @@ namespace ramses_internal DeviceResourceHandle bufferHandle; if (dataResource.dataBuffer.isValid()) + { bufferHandle = resourceManager.getDataBufferDeviceHandle(dataResource.dataBuffer, scene.getSceneId()); + } else if (dataResource.hash.isValid()) + { bufferHandle = resourceManager.getResourceDeviceHandle(dataResource.hash); + } //all buffers must have valid device handles, except for index buffer since it's possible to have vertex array //without index buffer @@ -92,9 +103,19 @@ namespace ramses_internal //indices are always in the 1st field (field Zero) if (attributeField == 0u) + { vertexArrayInfo.indexBuffer = bufferHandle; + } else - vertexArrayInfo.vertexBuffers.push_back({ bufferHandle, attributeField - 1, dataResource.instancingDivisor, renderable.startVertex, dataField.dataType, dataResource.offsetWithinElementInBytes, dataResource.stride }); + { + vertexArrayInfo.vertexBuffers.push_back({bufferHandle, + attributeField - 1, + dataResource.instancingDivisor, + renderable.startVertex, + dataField.dataType, + dataResource.offsetWithinElementInBytes, + dataResource.stride}); + } } resourceManager.uploadVertexArray(renderableHandle, vertexArrayInfo, scene.getSceneId()); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneResourceUploader.h b/src/renderer/internal/RendererLib/SceneResourceUploader.h similarity index 82% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneResourceUploader.h rename to src/renderer/internal/RendererLib/SceneResourceUploader.h index 813da16e0..80d500861 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneResourceUploader.h +++ b/src/renderer/internal/RendererLib/SceneResourceUploader.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENERESOURCEUPLOADER_H -#define RAMSES_SCENERESOURCEUPLOADER_H +#pragma once -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" -namespace ramses_internal +namespace ramses::internal { class IScene; + class RendererCachedScene; class IRendererResourceManager; class SceneResourceUploader @@ -23,9 +23,7 @@ namespace ramses_internal static void UploadRenderBuffer(const IScene& scene, RenderBufferHandle renderBuffer, IRendererResourceManager& resourceManager); static void UploadBlitPassRenderTargets(const IScene& scene, BlitPassHandle blitPass, IRendererResourceManager& resourceManager); static void UploadTextureBuffer(const IScene& scene, TextureBufferHandle textureBuffer, IRendererResourceManager& resourceManager); - static void UpdateTextureBuffer(const IScene& scene, TextureBufferHandle textureBuffer, IRendererResourceManager& resourceManager); + static void UpdateTextureBuffer(const RendererCachedScene& scene, TextureBufferHandle textureBuffer, IRendererResourceManager& resourceManager); static void UploadVertexArray(const IScene& scene, RenderableHandle renderableHandle, IRendererResourceManager& resourceManager); }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneStateExecutor.cpp b/src/renderer/internal/RendererLib/SceneStateExecutor.cpp similarity index 94% rename from renderer/RendererLib/RendererLib/src/SceneStateExecutor.cpp rename to src/renderer/internal/RendererLib/SceneStateExecutor.cpp index 9391cd6aa..c6aa539d7 100644 --- a/renderer/RendererLib/RendererLib/src/SceneStateExecutor.cpp +++ b/src/renderer/internal/RendererLib/SceneStateExecutor.cpp @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/Renderer.h" -#include "RendererLib/RendererScenes.h" -#include "Components/ISceneGraphConsumerComponent.h" -#include "RendererEventCollector.h" -#include "Utils/ThreadLocalLogForced.h" -#include "PlatformAbstraction/Macros.h" -#include "RendererFramework/IRendererSceneEventSender.h" - -namespace ramses_internal +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/Components/ISceneGraphConsumerComponent.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/Core/Utils/ThreadLocalLogForced.h" +#include "internal/PlatformAbstraction/Macros.h" +#include "internal/RendererLib/IRendererSceneEventSender.h" + +namespace ramses::internal { SceneStateExecutor::SceneStateExecutor(const Renderer& renderer, IRendererSceneEventSender& rendererSceneSender, RendererEventCollector& rendererEventCollector) : m_renderer(renderer) @@ -198,14 +198,14 @@ namespace ramses_internal return ESceneState::Unknown; } - EScenePublicationMode SceneStateExecutor::getScenePublicationMode(SceneId sceneId) const + std::optional SceneStateExecutor::getScenePublicationMode(SceneId sceneId) const { if (m_scenesStateInfo.hasScene(sceneId)) { return m_scenesStateInfo.getScenePublicationMode(sceneId); } - return EScenePublicationMode_Unpublished; + return {}; } bool SceneStateExecutor::checkIfCanBePublished(SceneId sceneId) const @@ -350,10 +350,7 @@ namespace ramses_internal { return true; } - else - { - LOG_ERROR(CONTEXT_RENDERER, "Failed to receive scene with id " << sceneId << " because scene arrived before!"); - } + LOG_ERROR(CONTEXT_RENDERER, "Failed to receive scene with id " << sceneId << " because scene arrived before!"); } else { @@ -371,10 +368,7 @@ namespace ramses_internal { return true; } - else - { - LOG_ERROR(CONTEXT_RENDERER, "Failed to subscribe scene with id " << sceneId << " because scene is in wrong state " << EnumToString(m_scenesStateInfo.getSceneState(sceneId)) << "!"); - } + LOG_ERROR(CONTEXT_RENDERER, "Failed to subscribe scene with id " << sceneId << " because scene is in wrong state " << EnumToString(m_scenesStateInfo.getSceneState(sceneId)) << "!"); } else { @@ -425,11 +419,10 @@ namespace ramses_internal if (m_scenesStateInfo.hasScene(sceneId) && ESceneState::Subscribed == m_scenesStateInfo.getSceneState(sceneId)) { if (m_renderer.hasDisplayController()) - return true; - else { - LOG_WARN(CONTEXT_RENDERER, "Map scene with id " << sceneId << " might fail because the display seems invalid!"); + return true; } + LOG_WARN(CONTEXT_RENDERER, "Map scene with id " << sceneId << " might fail because the display seems invalid!"); } else { diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneStateExecutor.h b/src/renderer/internal/RendererLib/SceneStateExecutor.h similarity index 93% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneStateExecutor.h rename to src/renderer/internal/RendererLib/SceneStateExecutor.h index d1286aabf..eb83fe7c3 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneStateExecutor.h +++ b/src/renderer/internal/RendererLib/SceneStateExecutor.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENESTATEEXECUTOR_H -#define RAMSES_SCENESTATEEXECUTOR_H +#pragma once -#include "RendererLib/SceneStateInfo.h" -#include "Scene/EScenePublicationMode.h" +#include "internal/RendererLib/SceneStateInfo.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" -namespace ramses_internal +namespace ramses::internal { class Renderer; class ISceneGraphConsumerComponent; @@ -53,7 +52,7 @@ namespace ramses_internal [[nodiscard]] bool checkIfCanBeHidden (SceneId sceneId) const; [[nodiscard]] ESceneState getSceneState(SceneId sceneId) const; - [[nodiscard]] EScenePublicationMode getScenePublicationMode(SceneId sceneId) const; + [[nodiscard]] std::optional getScenePublicationMode(SceneId sceneId) const; private: void rollBackToUnsubscribedAndTriggerIndirectEvents(ESceneState sceneState, SceneId sceneId); @@ -77,5 +76,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/SceneStateInfo.cpp b/src/renderer/internal/RendererLib/SceneStateInfo.cpp similarity index 90% rename from renderer/RendererLib/RendererLib/src/SceneStateInfo.cpp rename to src/renderer/internal/RendererLib/SceneStateInfo.cpp index a7f28da4c..9a39a3410 100644 --- a/renderer/RendererLib/RendererLib/src/SceneStateInfo.cpp +++ b/src/renderer/internal/RendererLib/SceneStateInfo.cpp @@ -6,9 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/SceneStateInfo.h" +#include "internal/RendererLib/SceneStateInfo.h" -namespace ramses_internal +namespace ramses::internal { void SceneStateInfo::addScene(SceneId sceneId, EScenePublicationMode mode) { @@ -43,11 +43,11 @@ namespace ramses_internal return sceneInfo->state; } - EScenePublicationMode SceneStateInfo::getScenePublicationMode(SceneId sceneId) const + std::optional SceneStateInfo::getScenePublicationMode(SceneId sceneId) const { SceneInfo* sceneInfo = m_scenesInfo.get(sceneId); if (!sceneInfo) - return EScenePublicationMode_Unpublished; + return {}; return sceneInfo->publicationMode; } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/SceneStateInfo.h b/src/renderer/internal/RendererLib/SceneStateInfo.h similarity index 71% rename from renderer/RendererLib/RendererLib/include/RendererLib/SceneStateInfo.h rename to src/renderer/internal/RendererLib/SceneStateInfo.h index 644349419..352a61439 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/SceneStateInfo.h +++ b/src/renderer/internal/RendererLib/SceneStateInfo.h @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENESTATEINFO_H -#define RAMSES_SCENESTATEINFO_H +#pragma once -#include "Collections/HashMap.h" -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" -#include "RendererLib/ESceneState.h" -#include "Scene/EScenePublicationMode.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/Enums/ESceneState.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include -namespace ramses_internal +namespace ramses::internal { class SceneStateInfo @@ -26,7 +26,7 @@ namespace ramses_internal void removeScene(SceneId sceneId); void setSceneState(SceneId sceneId, ESceneState sceneState); [[nodiscard]] ESceneState getSceneState(SceneId sceneId) const; - [[nodiscard]] EScenePublicationMode getScenePublicationMode(SceneId sceneId) const; + [[nodiscard]] std::optional getScenePublicationMode(SceneId sceneId) const; void getKnownSceneIds(SceneIdVector& knownIds) const; private: @@ -39,5 +39,3 @@ namespace ramses_internal HashMap m_scenesInfo; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/StagingInfo.h b/src/renderer/internal/RendererLib/StagingInfo.h similarity index 81% rename from renderer/RendererLib/RendererLib/include/RendererLib/StagingInfo.h rename to src/renderer/internal/RendererLib/StagingInfo.h index 5b0eb1db1..3c4ebe495 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/StagingInfo.h +++ b/src/renderer/internal/RendererLib/StagingInfo.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STAGINGINFO_H -#define RAMSES_STAGINGINFO_H +#pragma once -#include "SceneAPI/SceneSizeInformation.h" -#include "SceneAPI/SceneVersionTag.h" -#include "Scene/SceneActionCollection.h" -#include "Scene/ResourceChanges.h" -#include "SceneReferencing/SceneReferenceAction.h" -#include "Components/FlushTimeInformation.h" -#include "Components/ManagedResource.h" +#include "internal/SceneGraph/SceneAPI/SceneSizeInformation.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneReferencing/SceneReferenceAction.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/Components/ManagedResource.h" -namespace ramses_internal +namespace ramses::internal { class ResourceCachedScene; @@ -61,5 +60,3 @@ namespace ramses_internal ManagedResourceVector resourcesToUploadOnceMapping; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/TextureLinkCachedScene.cpp b/src/renderer/internal/RendererLib/TextureLinkCachedScene.cpp similarity index 93% rename from renderer/RendererLib/RendererLib/src/TextureLinkCachedScene.cpp rename to src/renderer/internal/RendererLib/TextureLinkCachedScene.cpp index 7df4c1648..e826956e5 100644 --- a/renderer/RendererLib/RendererLib/src/TextureLinkCachedScene.cpp +++ b/src/renderer/internal/RendererLib/TextureLinkCachedScene.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/TextureLinkCachedScene.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/IResourceDeviceHandleAccessor.h" -#include "SceneAPI/TextureSampler.h" +#include "internal/RendererLib/TextureLinkCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/IResourceDeviceHandleAccessor.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" -namespace ramses_internal +namespace ramses::internal { TextureLinkCachedScene::TextureLinkCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo) : DataReferenceLinkCachedScene(sceneLinksManager, sceneInfo) @@ -22,7 +22,7 @@ namespace ramses_internal { const DataSlotHandle actualHandle = DataReferenceLinkCachedScene::allocateDataSlot(dataSlot, handle); - if (dataSlot.type == EDataSlotType_TextureConsumer) + if (dataSlot.type == EDataSlotType::TextureConsumer) { const auto sampler = dataSlot.attachedTextureSampler; assert(sampler.isValid() && isTextureSamplerAllocated(sampler)); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/TextureLinkCachedScene.h b/src/renderer/internal/RendererLib/TextureLinkCachedScene.h similarity index 86% rename from renderer/RendererLib/RendererLib/include/RendererLib/TextureLinkCachedScene.h rename to src/renderer/internal/RendererLib/TextureLinkCachedScene.h index 6a8e1a74a..36fbebb97 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/TextureLinkCachedScene.h +++ b/src/renderer/internal/RendererLib/TextureLinkCachedScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURELINKCACHEDSCENE_H -#define RAMSES_TEXTURELINKCACHEDSCENE_H +#pragma once -#include "RendererLib/DataReferenceLinkCachedScene.h" -#include "RendererAPI/Types.h" +#include "internal/RendererLib/DataReferenceLinkCachedScene.h" +#include "internal/RendererLib/Types.h" -namespace ramses_internal +namespace ramses::internal { class TextureLinkCachedScene : public DataReferenceLinkCachedScene { @@ -20,7 +19,7 @@ namespace ramses_internal explicit TextureLinkCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo = SceneInfo()); // From IScene - DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle) override; void releaseDataSlot(DataSlotHandle handle) override; void setDataSlotTexture(DataSlotHandle handle, const ResourceContentHash& texture) override; @@ -36,5 +35,3 @@ namespace ramses_internal HashMap m_fallbackTextureSamplers; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/TextureLinkManager.cpp b/src/renderer/internal/RendererLib/TextureLinkManager.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/src/TextureLinkManager.cpp rename to src/renderer/internal/RendererLib/TextureLinkManager.cpp index 1b5de2158..a80ec7c21 100644 --- a/renderer/RendererLib/RendererLib/src/TextureLinkManager.cpp +++ b/src/renderer/internal/RendererLib/TextureLinkManager.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/TextureLinkManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DataLinkUtils.h" +#include "internal/RendererLib/TextureLinkManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DataLinkUtils.h" -namespace ramses_internal +namespace ramses::internal { TextureLinkManager::TextureLinkManager(RendererScenes& rendererScenes) : LinkManagerBase(rendererScenes) @@ -65,8 +65,8 @@ namespace ramses_internal bool TextureLinkManager::createDataLink(SceneId providerSceneId, DataSlotHandle providerSlotHandle, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) { - assert(EDataSlotType_TextureProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); - assert(EDataSlotType_TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::TextureProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); + assert(EDataSlotType::TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); assert(!m_offscreenBufferLinks.hasLinkedProvider(consumerSceneId, consumerSlotHandle)); assert(!m_streamBufferLinks.hasLinkedProvider(consumerSceneId, consumerSlotHandle)); @@ -98,7 +98,7 @@ namespace ramses_internal template void TextureLinkManager::createBufferLinkInternal(BUFFERHANDLE providerBuffer, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle, BufferLinks& links) { - assert(EDataSlotType_TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::TextureConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); assert(!getSceneLinks().hasLinkedProvider(consumerSceneId, consumerSlotHandle)); assert(!links.hasLinkedProvider(consumerSceneId, consumerSlotHandle)); diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/TextureLinkManager.h b/src/renderer/internal/RendererLib/TextureLinkManager.h similarity index 94% rename from renderer/RendererLib/RendererLib/include/RendererLib/TextureLinkManager.h rename to src/renderer/internal/RendererLib/TextureLinkManager.h index 4ee3b21e6..aac9cdfe3 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/TextureLinkManager.h +++ b/src/renderer/internal/RendererLib/TextureLinkManager.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURELINKMANAGER_H -#define RAMSES_TEXTURELINKMANAGER_H +#pragma once -#include "RendererLib/LinkManagerBase.h" -#include "RendererLib/BufferLinks.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/RendererLib/LinkManagerBase.h" +#include "internal/RendererLib/BufferLinks.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" -namespace ramses_internal +namespace ramses::internal { class RendererScenes; @@ -65,5 +64,3 @@ namespace ramses_internal ExternalBufferLinks m_externalBufferLinks; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/TransformationLinkCachedScene.cpp b/src/renderer/internal/RendererLib/TransformationLinkCachedScene.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/src/TransformationLinkCachedScene.cpp rename to src/renderer/internal/RendererLib/TransformationLinkCachedScene.cpp index db418b916..e19dd0c6e 100644 --- a/renderer/RendererLib/RendererLib/src/TransformationLinkCachedScene.cpp +++ b/src/renderer/internal/RendererLib/TransformationLinkCachedScene.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/TransformationLinkCachedScene.h" -#include "RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/TransformationLinkCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" -namespace ramses_internal +namespace ramses::internal { TransformationLinkCachedScene::TransformationLinkCachedScene(SceneLinksManager& sceneLinksManager, const SceneInfo& sceneInfo) : SceneLinkScene(sceneLinksManager, sceneInfo) @@ -55,7 +55,7 @@ namespace ramses_internal void TransformationLinkCachedScene::releaseDataSlot(DataSlotHandle handle) { const DataSlot& dataSlot = getDataSlot(handle); - if (dataSlot.type == EDataSlotType_TransformationProvider || dataSlot.type == EDataSlotType_TransformationConsumer) + if (dataSlot.type == EDataSlotType::TransformationProvider || dataSlot.type == EDataSlotType::TransformationConsumer) { propagateDirtyToConsumers(dataSlot.attachedNode); } diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/TransformationLinkCachedScene.h b/src/renderer/internal/RendererLib/TransformationLinkCachedScene.h similarity index 92% rename from renderer/RendererLib/RendererLib/include/RendererLib/TransformationLinkCachedScene.h rename to src/renderer/internal/RendererLib/TransformationLinkCachedScene.h index 0edea4716..42e854b2b 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/TransformationLinkCachedScene.h +++ b/src/renderer/internal/RendererLib/TransformationLinkCachedScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRANSFORMATIONLINKCACHEDSCENE_H -#define RAMSES_TRANSFORMATIONLINKCACHEDSCENE_H +#pragma once -#include "RendererLib/SceneLinkScene.h" +#include "internal/RendererLib/SceneLinkScene.h" -namespace ramses_internal +namespace ramses::internal { class TransformationLinkCachedScene : public SceneLinkScene { @@ -39,5 +38,3 @@ namespace ramses_internal mutable NodeHandleVector m_dirtyNodes; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/src/TransformationLinkManager.cpp b/src/renderer/internal/RendererLib/TransformationLinkManager.cpp similarity index 90% rename from renderer/RendererLib/RendererLib/src/TransformationLinkManager.cpp rename to src/renderer/internal/RendererLib/TransformationLinkManager.cpp index c12f24020..44dd89dfb 100644 --- a/renderer/RendererLib/RendererLib/src/TransformationLinkManager.cpp +++ b/src/renderer/internal/RendererLib/TransformationLinkManager.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/TransformationLinkManager.h" -#include "RendererLib/TransformationLinkCachedScene.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/DataLinkUtils.h" +#include "internal/RendererLib/TransformationLinkManager.h" +#include "internal/RendererLib/TransformationLinkCachedScene.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/DataLinkUtils.h" -namespace ramses_internal +namespace ramses::internal { TransformationLinkManager::TransformationLinkManager(RendererScenes& rendererScenes) : LinkManagerBase(rendererScenes) @@ -34,8 +34,8 @@ namespace ramses_internal bool TransformationLinkManager::createDataLink(SceneId providerSceneId, DataSlotHandle providerSlotHandle, SceneId consumerSceneId, DataSlotHandle consumerSlotHandle) { - assert(EDataSlotType_TransformationProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); - assert(EDataSlotType_TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::TransformationProvider == DataLinkUtils::GetDataSlot(providerSceneId, providerSlotHandle, m_scenes).type); + assert(EDataSlotType::TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); if (!LinkManagerBase::createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)) { @@ -67,7 +67,7 @@ namespace ramses_internal bool TransformationLinkManager::removeDataLink(SceneId consumerSceneId, DataSlotHandle consumerSlotHandle, SceneId* providerSceneIdOut) { - assert(EDataSlotType_TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); + assert(EDataSlotType::TransformationConsumer == DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).type); const NodeHandle consumerNodeHandle = DataLinkUtils::GetDataSlot(consumerSceneId, consumerSlotHandle, m_scenes).attachedNode; if (getSceneLinks().hasLinkedProvider(consumerSceneId, consumerSlotHandle)) diff --git a/renderer/RendererLib/RendererLib/include/RendererLib/TransformationLinkManager.h b/src/renderer/internal/RendererLib/TransformationLinkManager.h similarity index 89% rename from renderer/RendererLib/RendererLib/include/RendererLib/TransformationLinkManager.h rename to src/renderer/internal/RendererLib/TransformationLinkManager.h index 19ac40c01..ab89728dc 100644 --- a/renderer/RendererLib/RendererLib/include/RendererLib/TransformationLinkManager.h +++ b/src/renderer/internal/RendererLib/TransformationLinkManager.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRANSFORMATIONLINKMANAGER_H -#define RAMSES_TRANSFORMATIONLINKMANAGER_H +#pragma once -#include "RendererLib/LinkManagerBase.h" -#include "Scene/ETransformMatrixType.h" -#include "DataTypesImpl.h" +#include "internal/RendererLib/LinkManagerBase.h" +#include "internal/SceneGraph/Scene/ETransformMatrixType.h" +#include "impl/DataTypesImpl.h" -namespace ramses_internal +namespace ramses::internal { class RendererScenes; @@ -43,5 +42,3 @@ namespace ramses_internal SceneToNodeSlotMap m_nodesToDataSlots; }; } - -#endif diff --git a/src/renderer/internal/RendererLib/Types.cpp b/src/renderer/internal/RendererLib/Types.cpp new file mode 100644 index 000000000..64fa11366 --- /dev/null +++ b/src/renderer/internal/RendererLib/Types.cpp @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/Types.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "impl/RamsesFrameworkTypesImpl.h" +#include "ramses/renderer/Types.h" + +static_assert(std::is_same::value, "DisplayHandle type mismatch"); +static_assert(ramses::displayId_t::Invalid().getValue() == ramses::internal::DisplayHandle::Invalid().asMemoryHandle(), "DisplayHandle default mismatch"); + +static_assert(std::is_same::value, "OffscreenBufferHandle type mismatch"); +static_assert(ramses::displayBufferId_t::Invalid().getValue() == ramses::internal::OffscreenBufferHandle::Invalid().asMemoryHandle(), "OffscreenBufferHandle default mismatch"); + +static_assert(std::is_same::value, "WaylandIviSurfaceId type mismatch"); +static_assert(ramses::waylandIviSurfaceId_t::Invalid().getValue() == ramses::internal::WaylandIviSurfaceId::Invalid().getValue(), "WaylandIviSurfaceId default mismatch"); +static_assert(std::is_same::value, "WaylandIviSurfaceId type mismatch"); +static_assert(ramses::waylandIviSurfaceId_t::Invalid().getValue() == ramses::internal::WaylandIviSurfaceId::Invalid().getValue(), "WaylandIviSurfaceId default mismatch"); + +static_assert(std::is_same::value, "WaylandIviLayerId type mismatch"); +static_assert(ramses::waylandIviLayerId_t::Invalid().getValue() == ramses::internal::WaylandIviLayerId::Invalid().getValue(), "WaylandIviLayerId default mismatch"); + +static_assert(std::is_same::value, "BinaryShaderFormatID type mismatch"); +static_assert(ramses::binaryShaderFormatId_t::Invalid().getValue() == ramses::internal::BinaryShaderFormatID::Invalid().getValue(), "BinaryShaderFormatID default mismatch"); diff --git a/renderer/RendererLib/RendererAPI/include/RendererAPI/Types.h b/src/renderer/internal/RendererLib/Types.h similarity index 59% rename from renderer/RendererLib/RendererAPI/include/RendererAPI/Types.h rename to src/renderer/internal/RendererLib/Types.h index f51f130cf..afe728f70 100644 --- a/renderer/RendererLib/RendererAPI/include/RendererAPI/Types.h +++ b/src/renderer/internal/RendererLib/Types.h @@ -6,26 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTERNALRENDERERAPI_TYPES_H -#define RAMSES_INTERNALRENDERERAPI_TYPES_H - -#include "SceneAPI/Handles.h" -#include "Common/StronglyTypedValue.h" -#include "SceneAPI/RendererSceneState.h" -#include "Collections/Vector.h" -#include "Collections/HashSet.h" -#include "Common/StronglyTypedValue.h" -#include "Common/TypedMemoryHandle.h" -#include "Common/BitForgeMacro.h" -#include "Utils/LoggingUtils.h" +#pragma once + +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/Core/Common/StronglyTypedValue.h" +#include "internal/SceneGraph/SceneAPI/RendererSceneState.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/Core/Common/TypedMemoryHandle.h" +#include "internal/Core/Common/BitForgeMacro.h" +#include "internal/Core/Utils/LoggingUtils.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include #include -namespace ramses_internal +namespace ramses::internal { - enum class EDataType; - struct DisplayHandleTag {}; using DisplayHandle = TypedMemoryHandle; using DisplayHandleVector = std::vector; @@ -58,7 +56,6 @@ namespace ramses_internal using RenderableSet = HashSet; using BoolVector = std::vector; - using UInt8Vector = std::vector; using RenderTargetHandleVector = std::vector; struct DeviceResourceHandleTag {}; @@ -68,8 +65,7 @@ namespace ramses_internal struct WaylandIviLayerIdTag {}; using WaylandIviLayerId = StronglyTypedValue::max(), WaylandIviLayerIdTag>; - struct X11WindowHandleTag {}; - using X11WindowHandle = StronglyTypedValue::max(), X11WindowHandleTag>; + using ramses::X11WindowHandle; struct WindowsWindowHandleTag {}; using WindowsWindowHandle = StronglyTypedValue; @@ -77,20 +73,22 @@ namespace ramses_internal struct AndroidNativeWindowPtrTag {}; using AndroidNativeWindowPtr = StronglyTypedValue; + using ramses::IOSNativeWindowPtr; + struct ScreenshotInfo { struct Rectangle { - uint32_t x; - uint32_t y; - uint32_t width; - uint32_t height; + uint32_t x{0}; + uint32_t y{0}; + uint32_t width{0}; + uint32_t height{0}; }; - Rectangle rectangle; - std::string filename; - bool fullScreen; - bool sendViaDLT; - UInt8Vector pixelData; + Rectangle rectangle; + std::string filename; + bool fullScreen{false}; + bool sendViaDLT{false}; + std::vector pixelData; }; using ScreenshotInfoVector = std::vector; @@ -99,12 +97,12 @@ namespace ramses_internal struct VertexBufferInfo { DeviceResourceHandle deviceHandle; - DataFieldHandle field; - uint32_t instancingDivisor; - uint32_t startVertex; - EDataType bufferDataType; - uint16_t offsetWithinElement; - uint16_t stride; + DataFieldHandle field; + uint32_t instancingDivisor{0u}; + uint32_t startVertex{0u}; + EDataType bufferDataType{EDataType::Invalid}; + uint16_t offsetWithinElement{0u}; + uint16_t stride{0u}; }; struct VertexArrayInfo @@ -126,7 +124,6 @@ namespace ramses_internal EventQueue, All, PeriodicLog, - COUNT }; const std::array RendererLogTopicNames = @@ -144,11 +141,32 @@ namespace ramses_internal }; } -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::WaylandIviLayerId) -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::WindowsWindowHandle) -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::AndroidNativeWindowPtr) -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::BinaryShaderFormatID) -MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses_internal::WaylandIviSurfaceId) -MAKE_ENUM_CLASS_PRINTABLE(ramses_internal::ERendererLogTopic, "ERendererLogTopic", ramses_internal::RendererLogTopicNames, ramses_internal::ERendererLogTopic::COUNT); - -#endif +template <> +struct fmt::formatter { + template + constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + template + constexpr auto format(const ramses::internal::WaylandIviLayerId& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "ivi-layer:{}", value.getValue()); + } +}; + +template <> +struct fmt::formatter { + template + constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + template + constexpr auto format(const ramses::internal::WaylandIviSurfaceId& value, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "ivi-surface:{}", value.getValue()); + } +}; + +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::WindowsWindowHandle) +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::AndroidNativeWindowPtr) +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::IOSNativeWindowPtr) +MAKE_STRONGLYTYPEDVALUE_PRINTABLE(ramses::internal::BinaryShaderFormatID) +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::ERendererLogTopic, "ERendererLogTopic", ramses::internal::RendererLogTopicNames, ramses::internal::ERendererLogTopic::PeriodicLog); diff --git a/src/shared-lib/CMakeLists.txt b/src/shared-lib/CMakeLists.txt new file mode 100644 index 000000000..d675128c2 --- /dev/null +++ b/src/shared-lib/CMakeLists.txt @@ -0,0 +1,76 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2018 BMW Car IT GmbH +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + + +# RAMSES compiles the .cpp for its API for framework, client and renderer into static libs. +# +# For shared libs, the .cpp for API need to be re-compiled with API EXPORT enabled, +# which changes the API headers effectively, by declaring API functions and classes +# to be exported (visible on linux systems, or available in DLL) into the shared libs. +# +# To accomplish that, this modules 1st collects all the relevant .cpp files declared +# in impl folders in framework, client and renderer (depending on configured cmake options) +# and compiles them in the modules of the shared libs. + +set(LIB_TYPE "SHARED_LIBRARY") + +# Sanitizers can not handles shared libs properly, so in case of building for a sanitizer (for testing/debugging) +# force override lib type to be a static lib +if (ramses-sdk_ENABLE_SANITIZER STREQUAL "asan" OR + ramses-sdk_ENABLE_SANITIZER STREQUAL "ubsan" OR + ramses-sdk_ENABLE_SANITIZER STREQUAL "asan+ubsan") + message(WARNING "Static library type not supported for ramses-shared-lib in general, switching to static lib for address sanitizer to work correctly.") + set(LIB_TYPE "STATIC_LIBRARY") +endif() + +# Collect impl.cpp for framework and client depending on configured cmake options +list(APPEND HEADLESS_SOURCES ${PROJECT_SOURCE_DIR}/src/framework/impl/*.cpp) +list(APPEND HEADLESS_SOURCES ${PROJECT_SOURCE_DIR}/src/client/impl/*.cpp) +if(ramses-sdk_TEXT_SUPPORT) + list(APPEND HEADLESS_SOURCES ${PROJECT_SOURCE_DIR}/src/client/impl/text/*.cpp) +endif() + +if(ramses-sdk_ENABLE_LOGIC) + list(APPEND HEADLESS_SOURCES ${PROJECT_SOURCE_DIR}/src/client/impl/logic/*.cpp) +endif() + +set(RENDERER_SOURCES ${PROJECT_SOURCE_DIR}/src/renderer/impl/*.cpp) + +if (ramses-sdk_BUILD_FULL_SHARED_LIB) + createModuleWithRenderer( + NAME ramses-shared-lib + TYPE ${LIB_TYPE} + ENABLE_INSTALL ON + SRC_FILES ${HEADLESS_SOURCES} + ${RENDERER_SOURCES} + DEPENDENCIES ramses-client + ) + + target_link_libraries(ramses-shared-lib PUBLIC glm::glm) + if(${LIB_TYPE} STREQUAL "SHARED_LIBRARY") + target_compile_definitions(ramses-shared-lib PUBLIC RAMSES_LINK_SHARED_EXPORT) + endif() +endif() + +if (ramses-sdk_BUILD_HEADLESS_SHARED_LIB) + + # build shared library without renderer + createModule( + NAME ramses-shared-lib-headless + TYPE ${LIB_TYPE} + ENABLE_INSTALL ON + SRC_FILES ${HEADLESS_SOURCES} + DEPENDENCIES ramses-client + ) + + target_link_libraries(ramses-shared-lib-headless PUBLIC glm::glm) + target_link_libraries(ramses-shared-lib-headless INTERFACE ramses-api) + if(${LIB_TYPE} STREQUAL "SHARED_LIBRARY") + target_compile_definitions(ramses-shared-lib-headless PUBLIC RAMSES_LINK_SHARED_EXPORT) + endif() +endif() diff --git a/client/logic/tools/CMakeLists.txt b/tests/CMakeLists.txt similarity index 70% rename from client/logic/tools/CMakeLists.txt rename to tests/CMakeLists.txt index 278665d1c..b2b6f19b0 100644 --- a/client/logic/tools/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,13 +1,17 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2021 BMW AG +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -add_subdirectory(ramses-logic-viewer) +add_subdirectory(unittests) -if(ramses-sdk_BUILD_TESTS) - add_subdirectory(ramses-logic-viewer-test) +if(ANY_WINDOW_TYPE_ENABLED) + add_subdirectory(integration) +endif() + +if(ramses-sdk_ENABLE_LOGIC) + add_subdirectory(benchmarks) endif() diff --git a/integration/SandwichTests/CMakeLists.txt b/tests/benchmarks/CMakeLists.txt similarity index 86% rename from integration/SandwichTests/CMakeLists.txt rename to tests/benchmarks/CMakeLists.txt index fb673ce04..42ca08dc0 100644 --- a/integration/SandwichTests/CMakeLists.txt +++ b/tests/benchmarks/CMakeLists.txt @@ -1,9 +1,9 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -ADD_SUBDIRECTORY(RendererTests) +add_subdirectory(logic) diff --git a/tests/benchmarks/logic/CMakeLists.txt b/tests/benchmarks/logic/CMakeLists.txt new file mode 100644 index 000000000..96d04304a --- /dev/null +++ b/tests/benchmarks/logic/CMakeLists.txt @@ -0,0 +1,19 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME ramses-logic-benchmarks + TYPE BINARY + ENABLE_INSTALL OFF + + SRC_FILES *.cpp + *.h + + DEPENDENCIES ramses-client + ramses::google-benchmark-main +) diff --git a/benchmarks/animation.cpp b/tests/benchmarks/logic/animation.cpp similarity index 91% rename from benchmarks/animation.cpp rename to tests/benchmarks/logic/animation.cpp index a20843e27..7c99f63fc 100644 --- a/benchmarks/animation.cpp +++ b/tests/benchmarks/logic/animation.cpp @@ -6,14 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/AnimationTypes.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/AnimationTypes.h" #include "fmt/format.h" namespace ramses @@ -35,7 +33,9 @@ namespace ramses static void RunScript(benchmark::State& state, const std::string& src) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; + LuaConfig config; config.addStandardModuleDependency(EStandardModule::Base); auto* script = logicEngine.createLuaScript(src, config); @@ -107,7 +107,9 @@ namespace ramses static void BM_AnimationLinear(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; + const auto* animTimestamps = logicEngine.createDataArray(std::vector{ 0.f, 1.5f }); // will be interpreted as seconds const auto* animKeyframes = logicEngine.createDataArray(std::vector{ {0.f, 0.f, 0.f}, {0.f, 0.f, 360.f} }); const ramses::AnimationChannel channelLinear { "rotationZ", animTimestamps, animKeyframes, ramses::EInterpolationType::Linear }; @@ -123,7 +125,9 @@ namespace ramses static void BM_AnimationKeyframes(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; + const auto* animTimestamps = logicEngine.createDataArray(std::vector{ 0.f, 0.5f, 1.f, 1.5f }); // will be interpreted as seconds const auto* animKeyframes = logicEngine.createDataArray(std::vector{ {0.f, 0.f, 0.f}, {0.f, 0.f, 180.f}, {0.f, 0.f, 100.f}, {0.f, 0.f, 360.f} }); const ramses::AnimationChannel channelLinear { "rotationZ", animTimestamps, animKeyframes, ramses::EInterpolationType::Linear }; @@ -139,7 +143,9 @@ namespace ramses static void BM_AnimationKeyframesCubic(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; + const auto* animTimestamps = logicEngine.createDataArray(std::vector{ 0.f, 0.5f, 1.f, 1.5f }); // will be interpreted as seconds const auto* animKeyframes = logicEngine.createDataArray(std::vector{ {0.f, 0.f, 0.f}, {0.f, 0.f, 180.f}, {0.f, 0.f, 100.f}, {0.f, 0.f, 360.f} }); const auto* cubicAnimTangentsIn = logicEngine.createDataArray(std::vector{ {0.f, 0.f, 0.f}, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f } }); diff --git a/tests/benchmarks/logic/benchmarksetup.h b/tests/benchmarks/logic/benchmarksetup.h new file mode 100644 index 000000000..fcec0efcb --- /dev/null +++ b/tests/benchmarks/logic/benchmarksetup.h @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "benchmark/benchmark.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/logic/LogicEngine.h" + +namespace ramses +{ + class BenchmarkSetUp + { + public: + RamsesFramework m_framework{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; + RamsesClient& m_client{ *m_framework.createClient("benchmarkClient") }; + Scene& m_scene{ *m_client.createScene(SceneConfig(sceneId_t{ 123u })) }; + LogicEngine& m_logicEngine{ *m_scene.createLogicEngine() }; + }; +} diff --git a/benchmarks/compile_lua.cpp b/tests/benchmarks/logic/compile_lua.cpp similarity index 91% rename from benchmarks/compile_lua.cpp rename to tests/benchmarks/logic/compile_lua.cpp index 7a8c4bf18..b2e32b15f 100644 --- a/benchmarks/compile_lua.cpp +++ b/tests/benchmarks/logic/compile_lua.cpp @@ -6,10 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" #include "fmt/format.h" namespace ramses @@ -22,7 +20,8 @@ namespace ramses static void BM_CompileLua_Interface(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; LuaConfig config; config.addStandardModuleDependency(EStandardModule::Base); diff --git a/benchmarks/getproperty.cpp b/tests/benchmarks/logic/getproperty.cpp similarity index 97% rename from benchmarks/getproperty.cpp rename to tests/benchmarks/logic/getproperty.cpp index ed6cb881d..fdfd911f2 100644 --- a/benchmarks/getproperty.cpp +++ b/tests/benchmarks/logic/getproperty.cpp @@ -6,26 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "impl/LogicEngineImpl.h" -#include "internals/SolWrapper.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/LogicEngineImpl.h" +#include "internal/logic/SolWrapper.h" #include "fmt/format.h" namespace ramses { static void Run(benchmark::State& state, const std::string& src) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; LuaConfig config; config.addStandardModuleDependency(EStandardModule::Base); logicEngine.createLuaScript(src, config); - logicEngine.m_impl->disableTrackingDirtyNodes(); + logicEngine.impl().disableTrackingDirtyNodes(); while (state.KeepRunning()) { diff --git a/benchmarks/links.cpp b/tests/benchmarks/logic/links.cpp similarity index 83% rename from benchmarks/links.cpp rename to tests/benchmarks/logic/links.cpp index fbd0bf9b6..3e6df35eb 100644 --- a/benchmarks/links.cpp +++ b/tests/benchmarks/logic/links.cpp @@ -6,12 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "impl/LogicEngineImpl.h" -#include "ramses-logic/Property.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" +#include "impl/logic/LogicEngineImpl.h" +#include "ramses/client/logic/Property.h" #include "fmt/format.h" @@ -19,7 +17,8 @@ namespace ramses { static void BM_Links_CreateDestroyLink(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; const int64_t propertyCount = state.range(0); @@ -39,10 +38,10 @@ namespace ramses LuaScript* srcScript = logicEngine.createLuaScript(scriptSrc, config); LuaScript* destScript = logicEngine.createLuaScript(scriptSrc, config); - const Property* from = srcScript->getOutputs()->getChild("src0"); + Property* from = srcScript->getOutputs()->getChild("src0"); Property* to = destScript->getInputs()->getChild("target0"); - logicEngine.m_impl->disableTrackingDirtyNodes(); + logicEngine.impl().disableTrackingDirtyNodes(); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { logicEngine.link(*from, *to); @@ -59,9 +58,10 @@ namespace ramses static void BM_Links_CreateDestroyLink_ManyScripts(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; - const std::size_t scriptCount = static_cast(state.range(0)); + const auto scriptCount = static_cast(state.range(0)); const std::string scriptSrc = R"( function interface(IN,OUT) @@ -94,10 +94,10 @@ namespace ramses } const std::size_t halfwayIndex = scriptCount/2; - const Property* srcPropertyInTheMiddle = scripts[halfwayIndex]->getOutputs()->getChild("src10"); - const Property* destPropertyInTheMiddle = scripts[halfwayIndex+1]->getInputs()->getChild("dest10"); + Property* srcPropertyInTheMiddle = scripts[halfwayIndex]->getOutputs()->getChild("src10"); + Property* destPropertyInTheMiddle = scripts[halfwayIndex+1]->getInputs()->getChild("dest10"); - logicEngine.m_impl->disableTrackingDirtyNodes(); + logicEngine.impl().disableTrackingDirtyNodes(); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { logicEngine.unlink(*srcPropertyInTheMiddle, *destPropertyInTheMiddle); diff --git a/benchmarks/property.cpp b/tests/benchmarks/logic/property.cpp similarity index 88% rename from benchmarks/property.cpp rename to tests/benchmarks/logic/property.cpp index 0c189f254..d22885301 100644 --- a/benchmarks/property.cpp +++ b/tests/benchmarks/logic/property.cpp @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" - -#include "impl/LogicEngineImpl.h" +#include "impl/logic/LogicEngineImpl.h" #include "fmt/format.h" namespace ramses { static void BM_Property_SetIntValue(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; const int64_t propertyCount = state.range(0); diff --git a/benchmarks/serialization.cpp b/tests/benchmarks/logic/serialization.cpp similarity index 59% rename from benchmarks/serialization.cpp rename to tests/benchmarks/logic/serialization.cpp index b0726a392..88641a2d2 100644 --- a/benchmarks/serialization.cpp +++ b/tests/benchmarks/logic/serialization.cpp @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/Logger.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "internal/Core/Utils/RamsesLogger.h" #include "fmt/format.h" #include namespace ramses { - static std::vector CreateLargeLogicEngineBuffer(std::string_view fileName, std::size_t scriptCount) + static auto CreateLargeLogicEngineBuffer(std::string_view fileName, std::size_t scriptCount) { - Logger::SetLogVerbosityLimit(ELogLevel::Off); + ramses::internal::GetRamsesLogger().setConsoleLogLevel(ELogLevel::Off); - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; const std::string scriptSrc = R"( function interface(IN,OUT) @@ -56,28 +55,32 @@ namespace ramses SaveFileConfig configNoValidation; configNoValidation.setValidationEnabled(false); - logicEngine.saveToFile(fileName, configNoValidation); + std::ignore = setup.m_scene.saveToFile(fileName, configNoValidation); std::ifstream fileStream(std::string(fileName), std::ifstream::binary); fileStream.seekg(0, std::ios::end); - std::vector byteBuffer(static_cast(fileStream.tellg())); + + const auto size = static_cast(fileStream.tellg()); + std::unique_ptr byteBuffer(new std::byte[size], [](const auto* ptr) { delete[] ptr; }); fileStream.seekg(0, std::ios::beg); - fileStream.read(byteBuffer.data(), static_cast(byteBuffer.size())); - return byteBuffer; + fileStream.read(reinterpret_cast(byteBuffer.get()), static_cast(size)); + return std::pair{std::move(byteBuffer), size}; } static void BM_LoadFromBuffer_WithVerifier(benchmark::State& state) { - Logger::SetLogVerbosityLimit(ELogLevel::Off); + ramses::internal::GetRamsesLogger().setConsoleLogLevel(ELogLevel::Off); - const std::size_t scriptCount = static_cast(state.range(0)); + const auto scriptCount = static_cast(state.range(0)); - const std::vector buffer = CreateLargeLogicEngineBuffer("largeFile.bin", scriptCount); + auto buffer = CreateLargeLogicEngineBuffer("largeFile.bin", scriptCount); + BenchmarkSetUp setup; + SceneConfig config; + config.setMemoryVerificationEnabled(true); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { - LogicEngine logicEngine; - logicEngine.loadFromBuffer(buffer.data(), buffer.size(), nullptr, true); + setup.m_client.loadSceneFromMemory(std::move(buffer.first), buffer.second, config); } } @@ -86,16 +89,19 @@ namespace ramses static void BM_LoadFromBuffer_WithoutVerifier(benchmark::State& state) { - Logger::SetLogVerbosityLimit(ELogLevel::Off); + ramses::internal::GetRamsesLogger().setConsoleLogLevel(ELogLevel::Off); + + const auto scriptCount = static_cast(state.range(0)); - const std::size_t scriptCount = static_cast(state.range(0)); + auto buffer = CreateLargeLogicEngineBuffer("largeFile.bin", scriptCount); - const std::vector buffer = CreateLargeLogicEngineBuffer("largeFile.bin", scriptCount); + BenchmarkSetUp setup; + SceneConfig config; + config.setMemoryVerificationEnabled(false); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { - LogicEngine logicEngine; - logicEngine.loadFromBuffer(buffer.data(), buffer.size(), nullptr, false); + setup.m_client.loadSceneFromMemory(std::move(buffer.first), buffer.second, config); } } diff --git a/benchmarks/update.cpp b/tests/benchmarks/logic/update.cpp similarity index 90% rename from benchmarks/update.cpp rename to tests/benchmarks/logic/update.cpp index 2fb1ad4f9..8aef7d2ae 100644 --- a/benchmarks/update.cpp +++ b/tests/benchmarks/logic/update.cpp @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "benchmark/benchmark.h" +#include "benchmarksetup.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" - -#include "impl/LogicEngineImpl.h" +#include "impl/logic/LogicEngineImpl.h" #include "fmt/format.h" namespace ramses { static void BM_Update_AssignProperty(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; const int64_t loopCount = state.range(0); @@ -37,7 +36,7 @@ namespace ramses logicEngine.createLuaScript(scriptSrc); - logicEngine.m_impl->disableTrackingDirtyNodes(); + logicEngine.impl().disableTrackingDirtyNodes(); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { logicEngine.update(); @@ -51,7 +50,8 @@ namespace ramses static void BM_Update_AssignStruct(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; const int64_t loopCount = state.range(0); @@ -87,7 +87,7 @@ namespace ramses logicEngine.createLuaScript(scriptSrc); - logicEngine.m_impl->disableTrackingDirtyNodes(); + logicEngine.impl().disableTrackingDirtyNodes(); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { logicEngine.update(); @@ -99,7 +99,8 @@ namespace ramses static void BM_Update_AssignArray(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; const int64_t loopCount = state.range(0); @@ -117,7 +118,7 @@ namespace ramses logicEngine.createLuaScript(scriptSrc); - logicEngine.m_impl->disableTrackingDirtyNodes(); + logicEngine.impl().disableTrackingDirtyNodes(); for (auto _ : state) // NOLINT(clang-analyzer-deadcode.DeadStores) False positive { logicEngine.update(); @@ -136,9 +137,10 @@ namespace ramses // when the arg is close to 99, the time to update should be close to zero static void BM_Update_IsFasterWithFewerDirtyScripts(benchmark::State& state) { - LogicEngine logicEngine; + BenchmarkSetUp setup; + auto& logicEngine = setup.m_logicEngine; - const std::size_t scriptToSetDirty = static_cast(state.range(0)); + const auto scriptToSetDirty = static_cast(state.range(0)); constexpr int64_t scriptCount = 100; const std::string scriptSrc = R"( diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt new file mode 100644 index 000000000..a8bffde58 --- /dev/null +++ b/tests/integration/CMakeLists.txt @@ -0,0 +1,18 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2018 BMW Car IT GmbH +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +add_subdirectory(test-content) +add_subdirectory(renderer-test-utils) +add_subdirectory(renderer-tests) +add_subdirectory(smoke-tests) +add_subdirectory(resource-stress-tests) +add_subdirectory(render-backend-tests) + +if(ramses-sdk_ENABLE_LOGIC AND ramses-sdk_USE_IMAGEMAGICK AND ramses-sdk_BUILD_FULL_SHARED_LIB) + add_subdirectory(logic-viewer-tests) +endif() diff --git a/tests/integration/logic-viewer-tests/CMakeLists.txt b/tests/integration/logic-viewer-tests/CMakeLists.txt new file mode 100644 index 000000000..fff4f956e --- /dev/null +++ b/tests/integration/logic-viewer-tests/CMakeLists.txt @@ -0,0 +1,34 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +find_package(ImageMagick COMPONENTS compare) + +if(NOT ImageMagick_FOUND) + message(FATAL_ERROR "ImageMagick compare not found, but required (ramses-sdk_USE_IMAGEMAGICK=ON)") +endif() + +createModule( + NAME ramses-logic-viewer-gui-test + TYPE BINARY + ENABLE_INSTALL ${ramses-sdk_ENABLE_INSTALL} + INCLUDE_PATHS ${PROJECT_SOURCE_DIR}/tools/ramses-logic-viewer + ${PROJECT_SOURCE_DIR}/tests/unittests/client/logic/shared + SRC_FILES LogicViewerAppTest.cpp + RESOURCE_FOLDERS res + + PUBLIC_DEFINES MAGICK_COMPARE="${ImageMagick_compare_EXECUTABLE}" + + DEPENDENCIES ramses-logic-viewer-gui-lib + ramses-shared-lib + ramses-gmock + ramses-gmock-main +) + +MakeTestFromTarget( + TARGET ramses-logic-viewer-gui-test + SUFFIX RNDSANDWICHTEST_SWRAST) diff --git a/client/logic/tools/ramses-logic-viewer-test/LogicViewerAppTest.cpp b/tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp similarity index 91% rename from client/logic/tools/ramses-logic-viewer-test/LogicViewerAppTest.cpp rename to tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp index 86bf40b1f..630f26830 100644 --- a/client/logic/tools/ramses-logic-viewer-test/LogicViewerAppTest.cpp +++ b/tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp @@ -14,22 +14,22 @@ #include "gmock/gmock.h" #include "RamsesTestUtils.h" #include "WithTempDirectory.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-client.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/ramses-client.h" #include "ImguiClientHelper.h" #include "fmt/format.h" #include "CLI/CLI.hpp" @@ -239,17 +239,16 @@ std::string rtrim(const std::string& str) namespace ramses::internal { - const char* const logicFile = "ALogicViewerAppTest.rlogic"; const char* const ramsesFile = "ALogicViewerAppTest.ramses"; const char* const luaFile = "ALogicViewerAppTest.lua"; class LogHandler { public: - void add(ramses::ELogLevel level, const std::string& context, const std::string& msg) + void add(ELogLevel level, std::string_view context, std::string_view msg) { std::lock_guard guard(m_mutex); - m_log.push_back({level, context, msg}); + m_log.push_back({level, std::string(context), std::string(msg)}); } void clear() @@ -275,7 +274,7 @@ namespace ramses::internal private: struct LogEntry { - ramses::ELogLevel level; + ELogLevel level; std::string context; std::string msg; }; @@ -290,9 +289,9 @@ namespace ramses::internal public: ALogicViewerAppBase() { - createLogicFile(); - [[maybe_unused]] auto status = m_scene.scene->saveToFile(ramsesFile, true); - auto handler = [&](ramses::ELogLevel level, const std::string& context, const std::string& msg) { m_log.add(level, context, msg); }; + setupLogic(); + [[maybe_unused]] auto status = m_scene.scene->saveToFile(ramsesFile, {}); + auto handler = [&](ELogLevel level, std::string_view context, std::string_view msg) { m_log.add(level, context, msg); }; ramses::RamsesFramework::SetLogHandler(handler); } @@ -301,9 +300,9 @@ namespace ramses::internal ramses::RamsesFramework::SetLogHandler(ramses::LogHandlerFunc()); } - void createLogicFile() + void setupLogic() { - LogicEngine engine{ ramses::EFeatureLevel_Latest }; + LogicEngine& engine{ *m_scene.scene->createLogicEngine() }; auto* interface = engine.createLuaInterface(R"( function interface(IN,OUT) @@ -378,15 +377,15 @@ namespace ramses::internal end )", {}, "allTypesScript"); - auto* nodeBinding = engine.createRamsesNodeBinding(*m_scene.meshNode, ramses::ERotationType::Euler_XYZ, "myNode"); + auto* nodeBinding = engine.createNodeBinding(*m_scene.meshNode, ramses::ERotationType::Euler_XYZ, "myNode"); - engine.createRamsesAppearanceBinding(*m_scene.appearance, "myAppearance"); - engine.createRamsesCameraBinding(*m_scene.camera, "myCamera"); - engine.createRamsesRenderPassBinding(*m_scene.renderPass, "myRenderPass"); - ramses::RamsesRenderGroupBindingElements rgElements; + engine.createAppearanceBinding(*m_scene.appearance, "myAppearance"); + engine.createCameraBinding(*m_scene.camera, "myCamera"); + engine.createRenderPassBinding(*m_scene.renderPass, "myRenderPass"); + ramses::RenderGroupBindingElements rgElements; rgElements.addElement(*m_scene.meshNode, "myMeshNode"); - engine.createRamsesRenderGroupBinding(*m_scene.renderGroup, rgElements, "myRenderGroup"); - engine.createRamsesMeshNodeBinding(*m_scene.meshNode, "myMeshNode"); + engine.createRenderGroupBinding(*m_scene.renderGroup, rgElements, "myRenderGroup"); + engine.createMeshNodeBinding(*m_scene.meshNode, "myMeshNode"); engine.createTimerNode("myTimer"); @@ -401,10 +400,6 @@ namespace ramses::internal engine.link(*script->getOutputs()->getChild("paramVec3f"), *nodeBinding->getInputs()->getChild("rotation")); engine.update(); - - ramses::SaveFileConfig noValidationConfig; - noValidationConfig.setValidationEnabled(false); - engine.saveToFile(logicFile, noValidationConfig); } void createApp(const std::vector& argsList = {}) @@ -449,8 +444,8 @@ namespace ramses::internal [[nodiscard]] static testing::AssertionResult CompareImage(std::string_view actual, std::string_view expected, const float tolerance = 0.1f) { - const std::string diffDir = "../res/unittests/diff/"; - const std::string resourceDir = "../res/unittests/"; + const std::string diffDir = "../res/diff/"; + const std::string resourceDir = "../res/"; const std::string expectedPath = resourceDir + expected.data(); const std::string filenameOut = diffDir + "OUT_" + expected.data(); const std::string filenameDiff = diffDir + "DIFF_" + expected.data(); @@ -563,9 +558,9 @@ namespace ramses::internal [[nodiscard]] bool keyPress(ramses::EKeyCode keyCode) { auto imgui = m_app->getImguiClientHelper(); - imgui->keyEvent(ramses::displayId_t(0), ramses::EKeyEvent::Pressed, 0, keyCode); + imgui->keyEvent(ramses::displayId_t(0), ramses::EKeyEvent::Pressed, {}, keyCode); EXPECT_TRUE(m_app->doOneLoop()); - imgui->keyEvent(ramses::displayId_t(0), ramses::EKeyEvent::Released, 0, keyCode); + imgui->keyEvent(ramses::displayId_t(0), ramses::EKeyEvent::Released, {}, keyCode); return m_app->doOneLoop(); } @@ -619,10 +614,10 @@ namespace ramses::internal TEST_F(ALogicViewerGuiApp, ImageCompareSelfTest) { - EXPECT_TRUE(CompareImage("../res/unittests/ALogicViewerApp_red.png", "ALogicViewerApp_red.png")); - EXPECT_TRUE(CompareImage("../res/unittests/ALogicViewerApp_red.png", "ALogicViewerApp_white.png", 100)); - EXPECT_FALSE(CompareImage("../res/unittests/ALogicViewerApp_red.png", "ALogicViewerApp_white.png")); - const std::string diffDir = "../res/unittests/diff/"; + EXPECT_TRUE(CompareImage("../res/ALogicViewerApp_red.png", "ALogicViewerApp_red.png")); + EXPECT_TRUE(CompareImage("../res/ALogicViewerApp_red.png", "ALogicViewerApp_white.png", 100)); + EXPECT_FALSE(CompareImage("../res/ALogicViewerApp_red.png", "ALogicViewerApp_white.png")); + const std::string diffDir = "../res/diff/"; EXPECT_TRUE(fs::exists(diffDir + "OUT_ALogicViewerApp_white.png")); EXPECT_TRUE(fs::exists(diffDir + "DIFF_ALogicViewerApp_white.png")); EXPECT_TRUE(fs::exists(diffDir + "STDERR_ALogicViewerApp_white.png")); @@ -661,18 +656,10 @@ namespace ramses::internal EXPECT_THAT(testing::internal::GetCapturedStderr(), testing::HasSubstr("File does not exist: notExisting.ramses")); } - TYPED_TEST(ALogicViewerApp_T, logicFileDoesNotExist) - { - testing::internal::CaptureStderr(); - this->createApp({ ramsesFile, "notExisting.rlogic" }); - EXPECT_EQ(static_cast(CLI::ExitCodes::ValidationError), this->m_app->run()); - EXPECT_THAT(testing::internal::GetCapturedStderr(), testing::HasSubstr("File does not exist: notExisting.rlogic")); - } - TYPED_TEST(ALogicViewerApp_T, luaFileDoesNotExist) { testing::internal::CaptureStderr(); - this->createApp({ ramsesFile, logicFile, "notExisting.lua" }); + this->createApp({ ramsesFile, "notExisting.lua" }); EXPECT_EQ(static_cast(CLI::ExitCodes::ValidationError), this->m_app->run()); EXPECT_THAT(testing::internal::GetCapturedStderr(), testing::HasSubstr("File does not exist: notExisting.lua")); } @@ -789,7 +776,7 @@ namespace ramses::internal )"); createApp({ R"(--exec-lua=test_default('almost_yellow.png', 0.9))", ramsesFile }); EXPECT_EQ(0, m_app->run()); - auto appearance = m_app->getViewer()->getEngine().findByName("myAppearance"); + auto appearance = m_app->getViewer()->getLogic().findObject("myAppearance"); ASSERT_TRUE(appearance != nullptr); auto prop = appearance->getInputs()->getChild("green"); ASSERT_TRUE(prop != nullptr); @@ -801,7 +788,7 @@ namespace ramses::internal { this->createApp({ R"(--exec-lua=rlogic.appearanceBindings.myAppearance.IN.green.value = 0.44)", ramsesFile }); EXPECT_EQ(0, this->m_app->run()); - auto appearance = this->m_app->getViewer()->getEngine().template findByName("myAppearance"); + auto appearance = this->m_app->getViewer()->getLogic().template findObject("myAppearance"); ASSERT_TRUE(appearance != nullptr); auto prop = appearance->getInputs()->getChild("green"); ASSERT_TRUE(prop != nullptr); @@ -820,7 +807,7 @@ namespace ramses::internal { createApp({ R"(--exec-lua=rlogic.appearanceBindings.myAppearance.IN.green.value = 0.24)", "--headless", ramsesFile }); ASSERT_EQ(0, m_app->run()); - auto appearance = m_app->getViewer()->getEngine().findByName("myAppearance"); + auto appearance = m_app->getViewer()->getLogic().findObject("myAppearance"); ASSERT_TRUE(appearance != nullptr); auto prop = appearance->getInputs()->getChild("green"); ASSERT_TRUE(prop != nullptr); @@ -885,7 +872,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.interfaces() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 30, ui.interfaces() + ui.buttonHeight + 2 * ui.smallButtonHeight)); - auto* script = m_app->getViewer()->getEngine().findByName("myScript"); + auto* script = m_app->getViewer()->getLogic().findObject("myScript"); ASSERT_TRUE(script != nullptr); auto* prop = script->getOutputs()->getChild("paramVec3f"); ASSERT_TRUE(prop != nullptr); @@ -902,7 +889,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.scripts() + 2 * ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 30, ui.animationNodes() + ui.buttonHeight + 4 * ui.smallButtonHeight)); - auto* script = m_app->getViewer()->getEngine().findByName("allTypesScript"); + auto* script = m_app->getViewer()->getLogic().findObject("allTypesScript"); ASSERT_TRUE(script != nullptr); auto* prop = script->getOutputs()->getChild("paramFloat"); ASSERT_TRUE(prop != nullptr); @@ -916,7 +903,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.animationNodes() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 10, ui.animationNodes() + ui.buttonHeight + 4 * ui.smallButtonHeight)); - auto* animation = m_app->getViewer()->getEngine().findByName("myAnimation"); + auto* animation = m_app->getViewer()->getLogic().findObject("myAnimation"); ASSERT_TRUE(animation != nullptr); auto* prop = animation->getInputs()->getChild("progress"); ASSERT_TRUE(prop != nullptr); @@ -930,7 +917,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.timerNodes() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 30, ui.timerNodes() + ui.buttonHeight + 2 * ui.smallButtonHeight)); - auto* timer = m_app->getViewer()->getEngine().findByName("myTimer"); + auto* timer = m_app->getViewer()->getLogic().findObject("myTimer"); ASSERT_TRUE(timer != nullptr); auto* prop = timer->getInputs()->getChild("ticker_us"); ASSERT_TRUE(prop != nullptr); @@ -944,7 +931,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.appearanceBindings() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 10, ui.appearanceBindings() + ui.buttonHeight + 3 * ui.smallButtonHeight)); - auto* appearance = m_app->getViewer()->getEngine().findByName("myAppearance"); + auto* appearance = m_app->getViewer()->getLogic().findObject("myAppearance"); ASSERT_TRUE(appearance != nullptr); auto* prop = appearance->getInputs()->getChild("green"); ASSERT_TRUE(prop != nullptr); @@ -958,7 +945,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.nodeBindings() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 20, ui.nodeBindings() + 2 * ui.buttonHeight + 5 * ui.smallButtonHeight)); - auto* node = m_app->getViewer()->getEngine().findByName("myNode"); + auto* node = m_app->getViewer()->getLogic().findObject("myNode"); ASSERT_TRUE(node != nullptr); auto* prop = node->getInputs()->getChild("translation"); ASSERT_TRUE(prop != nullptr); @@ -976,7 +963,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.cameraBindings() + 4 * ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX - 30, ui.cameraBindings() + 3 * ui.buttonHeight + 4 * ui.smallButtonHeight)); - auto* camera = m_app->getViewer()->getEngine().findByName("myCamera"); + auto* camera = m_app->getViewer()->getLogic().findObject("myCamera"); ASSERT_TRUE(camera != nullptr); auto* prop = camera->getInputs()->getChild("viewport"); ASSERT_TRUE(prop != nullptr); @@ -992,7 +979,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.renderPassBindings() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 30, ui.renderPassBindings() + 2 * ui.buttonHeight + 3 * ui.smallButtonHeight)); - auto* renderPass = m_app->getViewer()->getEngine().findByName("myRenderPass"); + auto* renderPass = m_app->getViewer()->getLogic().findObject("myRenderPass"); ASSERT_TRUE(renderPass != nullptr); auto* prop = renderPass->getInputs()->getChild("renderOrder"); ASSERT_TRUE(prop != nullptr); @@ -1007,7 +994,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.renderGroupBindings() + 4 * ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 30, ui.renderGroupBindings() + ui.buttonHeight + 4 * ui.smallButtonHeight)); - auto* renderGroup = m_app->getViewer()->getEngine().findByName("myRenderGroup"); + auto* renderGroup = m_app->getViewer()->getLogic().findObject("myRenderGroup"); ASSERT_TRUE(renderGroup != nullptr); auto* prop = renderGroup->getInputs()->getChild("renderOrders")->getChild("myMeshNode"); ASSERT_TRUE(prop != nullptr); @@ -1021,7 +1008,7 @@ namespace ramses::internal EXPECT_TRUE(click(mouseX, ui.meshNodeBindings() + ui.smallButtonHeight)); EXPECT_TRUE(dragX(mouseX, mouseX + 30, ui.meshNodeBindings() + 4 * ui.smallButtonHeight)); - auto* meshNode = m_app->getViewer()->getEngine().findByName("myMeshNode"); + auto* meshNode = m_app->getViewer()->getLogic().findObject("myMeshNode"); ASSERT_TRUE(meshNode != nullptr); auto* prop = meshNode->getInputs()->getChild("vertexOffset"); ASSERT_TRUE(prop != nullptr); @@ -1031,7 +1018,7 @@ namespace ramses::internal TEST_F(ALogicViewerAppUI, reloadConfiguration) { const int32_t xFile = 25; - auto* script = m_app->getViewer()->getEngine().findByName("allTypesScript"); + auto* script = m_app->getViewer()->getLogic().findObject("allTypesScript"); ASSERT_TRUE(script != nullptr); auto* prop = script->getOutputs()->getChild("paramString"); ASSERT_TRUE(prop != nullptr); @@ -1346,7 +1333,7 @@ ShowDisplaySettings=0)"); EXPECT_EQ(1, report.getNodesExecuted().size()); EXPECT_EQ(10, report.getNodesSkippedExecution().size()); - auto* interface = m_app->getViewer()->getEngine().findByName("myInterface"); + auto* interface = m_app->getViewer()->getLogic().findObject("myInterface"); ASSERT_TRUE(interface != nullptr); auto* prop = interface->getInputs()->getChild("paramFloat"); ASSERT_TRUE(prop != nullptr); diff --git a/client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_clearColor.png b/tests/integration/logic-viewer-tests/res/ALogicViewerApp_clearColor.png similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_clearColor.png rename to tests/integration/logic-viewer-tests/res/ALogicViewerApp_clearColor.png diff --git a/client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_clearColorCmdLine.png b/tests/integration/logic-viewer-tests/res/ALogicViewerApp_clearColorCmdLine.png similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_clearColorCmdLine.png rename to tests/integration/logic-viewer-tests/res/ALogicViewerApp_clearColorCmdLine.png diff --git a/client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_red.png b/tests/integration/logic-viewer-tests/res/ALogicViewerApp_red.png similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_red.png rename to tests/integration/logic-viewer-tests/res/ALogicViewerApp_red.png diff --git a/client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_red_500x700.png b/tests/integration/logic-viewer-tests/res/ALogicViewerApp_red_500x700.png similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_red_500x700.png rename to tests/integration/logic-viewer-tests/res/ALogicViewerApp_red_500x700.png diff --git a/client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_white.png b/tests/integration/logic-viewer-tests/res/ALogicViewerApp_white.png similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_white.png rename to tests/integration/logic-viewer-tests/res/ALogicViewerApp_white.png diff --git a/client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_yellow.png b/tests/integration/logic-viewer-tests/res/ALogicViewerApp_yellow.png similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/res/ALogicViewerApp_yellow.png rename to tests/integration/logic-viewer-tests/res/ALogicViewerApp_yellow.png diff --git a/integration/PlatformTests/RenderBackendTests/CMakeLists.txt b/tests/integration/render-backend-tests/CMakeLists.txt similarity index 86% rename from integration/PlatformTests/RenderBackendTests/CMakeLists.txt rename to tests/integration/render-backend-tests/CMakeLists.txt index fdc9b6f84..27dc8b758 100644 --- a/integration/PlatformTests/RenderBackendTests/CMakeLists.txt +++ b/tests/integration/render-backend-tests/CMakeLists.txt @@ -7,11 +7,11 @@ # ------------------------------------------------------------------------- createModuleWithRenderer( - NAME RenderBackendTests + NAME render-backend-tests TYPE BINARY ENABLE_INSTALL ON - SRC_FILES src/*.h - src/*.cpp + SRC_FILES *.h + *.cpp DEPENDENCIES RendererTestUtils ramses-cli ) @@ -23,20 +23,18 @@ SET(GATE_WINDOW_PLATFORMS IF (ramses-sdk_BUILD_TESTS) makeTestPerWindowTypeFromTarget( - TARGET RenderBackendTests + TARGET render-backend-tests SUFFIX RNDSANDWICHTEST EXTRA_ARGS --ivi-layer 3 - SKIPPABLE ) # These tests don't work specifically on the build slaves, because binary shaders are not supported there SET(GATE_FILTER "-ADeviceSupportingBinaryShaders*:ADeviceSupportingGeometryShaders*") makeTestPerWindowTypeFromTarget( - TARGET RenderBackendTests + TARGET render-backend-tests SUFFIX RNDSANDWICHTEST_SWRAST EXTRA_ARGS --gtest_filter=${GATE_FILTER} --ivi-layer 3 WINDOW_TYPE_FILTER ${GATE_WINDOW_PLATFORMS} - SKIPPABLE ) ENDIF() diff --git a/integration/PlatformTests/RenderBackendTests/src/PlatformTest.cpp b/tests/integration/render-backend-tests/PlatformTest.cpp similarity index 92% rename from integration/PlatformTests/RenderBackendTests/src/PlatformTest.cpp rename to tests/integration/render-backend-tests/PlatformTest.cpp index 6019688c0..fffb20ced 100644 --- a/integration/PlatformTests/RenderBackendTests/src/PlatformTest.cpp +++ b/tests/integration/render-backend-tests/PlatformTest.cpp @@ -7,24 +7,24 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "RendererLib/RendererConfig.h" +#include "internal/RendererLib/RendererConfig.h" #include "WindowEventHandlerMock.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/RenderBackend.h" -#include "RendererLib/ResourceUploadRenderBackend.h" -#include "RendererAPI/IContext.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "PlatformFactory/PlatformFactory.h" -#include "RendererAPI/IPlatform.h" -#include "Resource/EffectResource.h" -#include "Utils/ThreadLocalLog.h" -#include "DisplayConfigImpl.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/RenderBackend.h" +#include "internal/RendererLib/ResourceUploadRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/Platform/PlatformFactory.h" +#include "internal/RendererLib/PlatformInterface/IPlatform.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "impl/DisplayConfigImpl.h" #include "RendererTestUtils.h" #include #include -namespace ramses_internal +namespace ramses::internal { namespace { @@ -44,7 +44,7 @@ namespace ramses_internal { PlatformFactory platformFactory; auto dispConfig = RendererTestUtils::CreateTestDisplayConfig(0u); - platform = platformFactory.createPlatform(rendererConfig, dispConfig.m_impl.get().getInternalDisplayConfig()); + platform = platformFactory.createPlatform(rendererConfig, dispConfig.impl().getInternalDisplayConfig()); assert(platform); // caller is expected to have a display prefix for logs @@ -75,7 +75,7 @@ namespace ramses_internal return platform->createResourceUploadRenderBackend(); } - std::unique_ptr uploadEffectAndExpectSuccess(IDevice& device) + static std::unique_ptr UploadEffectAndExpectSuccess(IDevice& device) { const char* vertexShader = R"SHADER( #version 100 @@ -93,7 +93,7 @@ namespace ramses_internal } )SHADER"; - EffectResource effect(vertexShader, fragmentShader, "", {}, {}, {}, "", ResourceCacheFlag_DoNotCache); + EffectResource effect(vertexShader, fragmentShader, "", {}, {}, {}, ""); EXPECT_TRUE(device.isDeviceStatusHealthy()); auto resource = device.uploadShader(effect); EXPECT_NE(nullptr, resource); @@ -101,7 +101,7 @@ namespace ramses_internal return resource; } - void activateShaderAndExpectSucces(IDevice& device, DeviceResourceHandle deviceHandle) + static void ActivateShaderAndExpectSucces(IDevice& device, DeviceResourceHandle deviceHandle) { EXPECT_TRUE(device.isDeviceStatusHealthy()); device.activateShader(deviceHandle); @@ -343,7 +343,7 @@ namespace ramses_internal return; } - const auto shaderResource = uploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); + const auto shaderResource = UploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); success.set_value(shaderResource != nullptr); platform->destroyResourceUploadRenderBackend(); @@ -352,7 +352,7 @@ namespace ramses_internal EXPECT_TRUE(success.get_future().get()); EXPECT_TRUE(mainRenderBackend->getContext().enable()); - uploadEffectAndExpectSuccess(mainRenderBackend->getDevice()); + UploadEffectAndExpectSuccess(mainRenderBackend->getDevice()); resourceUploadThread.join(); platform->destroyRenderBackend(); @@ -385,7 +385,7 @@ namespace ramses_internal //block till main thread enables context EXPECT_TRUE(successMainThread.get_future().get()); - const auto shaderResource = uploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); + const auto shaderResource = UploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); successResourceUpload.set_value(shaderResource != nullptr); platform->destroyResourceUploadRenderBackend(); @@ -426,7 +426,7 @@ namespace ramses_internal return; } - auto resource = uploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); + auto resource = UploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); shaderResource.set_value(std::move(resource)); //block till resource is used to make sure behavior is deterministic @@ -445,7 +445,7 @@ namespace ramses_internal EXPECT_TRUE(deviceHandle.isValid()); EXPECT_TRUE(mainRenderBackend->getContext().enable()); - activateShaderAndExpectSucces(mainDevice, deviceHandle); + ActivateShaderAndExpectSucces(mainDevice, deviceHandle); resourceUsedSuccess.set_value(true); resourceUploadThread.join(); @@ -472,7 +472,7 @@ namespace ramses_internal return; } - auto resource= uploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); + auto resource= UploadEffectAndExpectSuccess(resourceUploadRenderBackend->getDevice()); shaderResource.set_value(std::move(resource)); platform->destroyResourceUploadRenderBackend(); @@ -487,7 +487,7 @@ namespace ramses_internal //use resource uploaded in other context/thread auto& mainDevice = mainRenderBackend->getDevice(); const auto deviceHandle = mainDevice.registerShader(std::move(resource)); - activateShaderAndExpectSucces(mainDevice, deviceHandle); + ActivateShaderAndExpectSucces(mainDevice, deviceHandle); platform->destroyRenderBackend(); } diff --git a/integration/PlatformTests/RenderBackendTests/src/ShaderUploadTest.cpp b/tests/integration/render-backend-tests/ShaderUploadTest.cpp similarity index 67% rename from integration/PlatformTests/RenderBackendTests/src/ShaderUploadTest.cpp rename to tests/integration/render-backend-tests/ShaderUploadTest.cpp index 663119edf..3fe3805b1 100644 --- a/integration/PlatformTests/RenderBackendTests/src/ShaderUploadTest.cpp +++ b/tests/integration/render-backend-tests/ShaderUploadTest.cpp @@ -6,21 +6,25 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-renderer-api/DisplayConfig.h" -#include "DisplayConfigImpl.h" -#include "RendererConfigImpl.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/RamsesFrameworkTypes.h" + +#include "impl/DisplayConfigImpl.h" +#include "impl/RendererConfigImpl.h" #include "RendererTestUtils.h" #include "WindowEventHandlerMock.h" -#include "Resource/EffectResource.h" -#include "Resource/ResourceTypes.h" -#include "RendererAPI/IRenderBackend.h" -#include "Platform_Base/Device_Base.h" -#include "PlatformFactory/PlatformFactory.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/ResourceTypes.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "internal/RendererLib/PlatformBase/Device_Base.h" +#include "internal/Platform/PlatformFactory.h" #include +#include using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ADevice : public Test { @@ -68,10 +72,10 @@ namespace ramses_internal EffectInputInformationVector attributeInputs; attributeInputs.push_back(EffectInputInformation("a_position", 1, EDataType::Vector3F, EFixedSemantics::Invalid)); - return new EffectResource(vertexShader, fragmentShader, "", {}, uniformInputs, attributeInputs, "test effect", ResourceCacheFlag_DoNotCache); + return new EffectResource(vertexShader, fragmentShader, "", {}, uniformInputs, attributeInputs, "test effect"); } - static void SetUpTestCase() + static void SetUpTestSuite() { assert(nullptr == platform); @@ -79,20 +83,20 @@ namespace ramses_internal ramses::DisplayConfig dispConfig = RendererTestUtils::CreateTestDisplayConfig(0u); PlatformFactory platformFactory; - platform = platformFactory.createPlatform(rendererConfig.m_impl.get().getInternalRendererConfig(), dispConfig.m_impl.get().getInternalDisplayConfig()); + platform = platformFactory.createPlatform(rendererConfig.impl().getInternalRendererConfig(), dispConfig.impl().getInternalDisplayConfig()); assert(nullptr != platform); eventHandler = new NiceMock(); ramses::DisplayConfig displayConfig = RendererTestUtils::CreateTestDisplayConfig(0); displayConfig.setWindowRectangle(0, 0, 16u, 16u); - renderBackend = platform->createRenderBackend(displayConfig.m_impl.get().getInternalDisplayConfig(), *eventHandler); + renderBackend = platform->createRenderBackend(displayConfig.impl().getInternalDisplayConfig(), *eventHandler); assert(nullptr != renderBackend); testDevice = &renderBackend->getDevice(); } - static void TearDownTestCase() + static void TearDownTestSuite() { testDevice = nullptr; @@ -185,7 +189,7 @@ namespace ramses_internal EffectInputInformationVector attributeInputs; attributeInputs.push_back(EffectInputInformation("a_position", 1, EDataType::Vector3F, EFixedSemantics::Invalid)); - return new EffectResource(vertexShader, fragmentShader, geometryShader, EDrawMode::Points, uniformInputs, attributeInputs, "test effect", ResourceCacheFlag_DoNotCache); + return new EffectResource(vertexShader, fragmentShader, geometryShader, EDrawMode::Points, uniformInputs, attributeInputs, "test effect"); } }; @@ -210,7 +214,7 @@ namespace ramses_internal "", {}, templateEffect->getUniformInputs(), templateEffect->getAttributeInputs(), - "invalid effect", ResourceCacheFlag_DoNotCache); + "invalid effect"); const auto shaderGpuResource = testDevice->uploadShader(invalidEffect); EXPECT_EQ(nullptr, shaderGpuResource); } @@ -224,7 +228,7 @@ namespace ramses_internal "", {}, templateEffect->getUniformInputs(), templateEffect->getAttributeInputs(), - "invalid effect", ResourceCacheFlag_DoNotCache); + "invalid effect"); const auto shaderGpuResource = testDevice->uploadShader(invalidEffect); EXPECT_EQ(nullptr, shaderGpuResource); } @@ -251,7 +255,7 @@ namespace ramses_internal "", {}, templateEffect->getUniformInputs(), templateEffect->getAttributeInputs(), - "invalid effect", ResourceCacheFlag_DoNotCache); + "invalid effect"); const auto shaderGpuResource = testDevice->uploadShader(invalidEffect); EXPECT_EQ(nullptr, shaderGpuResource); } @@ -261,43 +265,47 @@ namespace ramses_internal ASSERT_TRUE(testDevice != nullptr); const std::unique_ptr templateEffect(CreateTestEffectResource()); - const std::string fragmentShader( - "#version 100 \n" - " \n" - "varying highp vec3 v_position; \n" - " \n" - "uniform highp int u_int; \n" - "uniform highp float u_float; \n" - "uniform highp vec2 u_vec2; \n" - "uniform highp vec3 u_vec3; \n" - "uniform highp vec4 u_vec4; \n" - "uniform highp ivec2 u_ivec2; \n" - "uniform highp ivec3 u_ivec3; \n" - "uniform highp ivec4 u_ivec4; \n" - "uniform highp mat2 u_mat2; \n" - "uniform highp mat3 u_mat3; \n" - "uniform highp mat4 u_mat4; \n" - " \n" - "void main(void) \n" - "{ \n" - " gl_FragColor = vec4(v_position + vec3(u_vec2, 1.0), u_float);\n" - "} \n" - ); + const std::string fragmentShader(R"( + #version 100 + + uniform bool u_bool[2]; + uniform highp int u_int[2]; + uniform highp float u_float[2]; + uniform highp vec2 u_vec2[2]; + uniform highp vec3 u_vec3[2]; + uniform highp vec4 u_vec4[2]; + uniform highp ivec2 u_ivec2[2]; + uniform highp ivec3 u_ivec3[2]; + uniform highp ivec4 u_ivec4[2]; + uniform highp mat2 u_mat2[2]; + uniform highp mat3 u_mat3[2]; + uniform highp mat4 u_mat4[2]; + + void main(void) + { + gl_FragColor.r = u_float[1] * float(u_int[1]); + gl_FragColor.g = u_mat2[1][0][0] * u_vec2[1].x * float(u_ivec2[1].x); + gl_FragColor.b = u_mat3[1][0][0] * u_vec3[1].x * float(u_ivec3[1].x); + if (u_bool[0]) + gl_FragColor.g = u_mat4[1][0][0] * u_vec4[1].x * float(u_ivec4[1].x); + } + )"); EffectInputInformationVector uniformInputs; - uniformInputs.push_back(EffectInputInformation("u_int", 1, EDataType::Int32, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_float", 1, EDataType::Float, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_bool", 2, EDataType::Bool, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_int", 2, EDataType::Int32, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_float", 2, EDataType::Float, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_vec2", 1, EDataType::Vector2F, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_vec3", 1, EDataType::Vector3F, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_vec4", 1, EDataType::Vector4F, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_vec2", 2, EDataType::Vector2F, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_vec3", 2, EDataType::Vector3F, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_vec4", 2, EDataType::Vector4F, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_ivec2", 1, EDataType::Vector2I, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_ivec3", 1, EDataType::Vector3I, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_ivec4", 1, EDataType::Vector4I, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_ivec2", 2, EDataType::Vector2I, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_ivec3", 2, EDataType::Vector3I, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_ivec4", 2, EDataType::Vector4I, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_mat2", 1, EDataType::Matrix22F, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_mat3", 1, EDataType::Matrix33F, EFixedSemantics::Invalid)); - uniformInputs.push_back(EffectInputInformation("u_mat4", 1, EDataType::Matrix44F, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_mat2", 2, EDataType::Matrix22F, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_mat3", 2, EDataType::Matrix33F, EFixedSemantics::Invalid)); + uniformInputs.push_back(EffectInputInformation("u_mat4", 2, EDataType::Matrix44F, EFixedSemantics::Invalid)); const EffectResource testEffect( templateEffect->getVertexShader(), @@ -305,7 +313,7 @@ namespace ramses_internal "", {}, uniformInputs, templateEffect->getAttributeInputs(), - "uniform test effect", ResourceCacheFlag_DoNotCache); + "uniform test effect"); auto shaderGpuResource = testDevice->uploadShader(testEffect); ASSERT_NE(nullptr, shaderGpuResource); const DeviceResourceHandle handle = testDevice->registerShader(std::move(shaderGpuResource)); @@ -313,43 +321,46 @@ namespace ramses_internal testDevice->activateShader(handle); - const int32_t intValue = 5; - const float floatValue = 5.0f; - const glm::vec2 vec2Value(5.0f); - const glm::vec3 vec3Value(5.0f); - const glm::vec4 vec4Value(5.0f); - const glm::ivec2 vec2iValue(5); - const glm::ivec3 vec3iValue(5); - const glm::ivec4 vec4iValue(5); - const glm::mat2 mat22Value(1.0f, 2.0f, 3.0f, 4.0f); - const glm::mat3 mat33Value(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f); - const glm::mat4 mat44Value(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f); + const std::array boolValues{true, true}; + const std::array intValues{ 5, 5 }; + const std::array floatValues{ 5.f, 5.f }; + const std::array vec2Values{ glm::vec2{5.f}, glm::vec2{5.f} }; + const std::array vec3Values{ glm::vec3{5.f}, glm::vec3{5.f} }; + const std::array vec4Values{ glm::vec4{5.f}, glm::vec4{5.f} }; + const std::array vec2iValues{ glm::ivec2{5}, glm::ivec2{5} }; + const std::array vec3iValues{ glm::ivec3{5}, glm::ivec3{5} }; + const std::array vec4iValues{ glm::ivec4{5}, glm::ivec4{5} }; + const std::array mat22Values{ glm::mat2{1.0f, 2.0f, 3.0f, 4.0f}, glm::mat2{1.0f, 2.0f, 3.0f, 4.0f} }; + const std::array mat33Values{ glm::mat3{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}, glm::mat3{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f} }; + const std::array mat44Values{ glm::mat4{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f}, glm::mat4{} }; EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); // check that there were no errors (GL_NO_ERROR in case of OpenGL) - triggers an internal assert otherwise - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_int")), 1, &intValue); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_bool")), 2, boolValues.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_float")), 1, &floatValue); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_int")), 2, intValues.data())); + EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_float")), 2, floatValues.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_vec2")), 1, &vec2Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_vec2")), 2, vec2Values.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_vec3")), 1, &vec3Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_vec3")), 2, vec3Values.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_vec4")), 1, &vec4Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_vec4")), 2, vec4Values.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_ivec2")), 1, &vec2iValue); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_ivec2")), 2, vec2iValues.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_ivec3")), 1, &vec3iValue); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_ivec3")), 2, vec3iValues.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_ivec4")), 1, &vec4iValue); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_ivec4")), 2, vec4iValues.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_mat2")), 1, &mat22Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_mat2")), 2, mat22Values.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_mat3")), 1, &mat33Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_mat3")), 2, mat33Values.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_mat4")), 1, &mat44Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_mat4")), 2, mat44Values.data())); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); testDevice->deleteShader(handle); @@ -368,7 +379,7 @@ namespace ramses_internal "", {}, uniformInputs, templateEffect->getAttributeInputs(), - "test effect", ResourceCacheFlag_DoNotCache); + "test effect"); auto shaderGpuResource = testDevice->uploadShader(testEffect); ASSERT_NE(nullptr, shaderGpuResource); @@ -380,7 +391,7 @@ namespace ramses_internal const float floatValue = 5.0f; EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); - testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_NonExistingFloat")), 1, &floatValue); + EXPECT_FALSE(testDevice->setConstant(DataFieldHandle(testEffect.getUniformDataFieldHandleByName("u_NonExistingFloat")), 1, &floatValue)); // Currently, RAMSES silently ignores (== does not try to set) uniforms which are not declared in the shader // This test verifies that behavior (i.e. device does not crash or report error) @@ -398,10 +409,10 @@ namespace ramses_internal const DeviceResourceHandle handle = testDevice->registerShader(std::move(shaderGpuResource)); EXPECT_TRUE(handle.isValid()); - UInt8Vector binaryShader; + std::vector binaryShader; BinaryShaderFormatID binaryShaderFormat; EXPECT_TRUE(testDevice->getBinaryShader(handle, binaryShader, binaryShaderFormat)); - EXPECT_TRUE(binaryShader.size() != 0); + EXPECT_FALSE(binaryShader.empty()); EXPECT_TRUE(binaryShaderFormat != BinaryShaderFormatID::Invalid()); testDevice->deleteShader(handle); @@ -415,10 +426,10 @@ namespace ramses_internal const DeviceResourceHandle handle = testDevice->registerShader(std::move(shaderGpuResource)); EXPECT_TRUE(handle.isValid()); - UInt8Vector binaryShader; + std::vector binaryShader; BinaryShaderFormatID binaryShaderFormat; EXPECT_TRUE(testDevice->getBinaryShader(handle, binaryShader, binaryShaderFormat)); - EXPECT_TRUE(binaryShader.size() != 0); + EXPECT_FALSE(binaryShader.empty()); EXPECT_TRUE(binaryShaderFormat != BinaryShaderFormatID::Invalid()); const DeviceResourceHandle handleBinary = testDevice->uploadBinaryShader(*testEffect, &binaryShader.front(), static_cast(binaryShader.size()), binaryShaderFormat); @@ -447,10 +458,10 @@ namespace ramses_internal const DeviceResourceHandle handle = testDevice->registerShader(std::move(shaderGpuResource)); ASSERT_TRUE(handle.isValid()); - UInt8Vector binaryShader; + std::vector binaryShader; BinaryShaderFormatID binaryShaderFormat; EXPECT_TRUE(testDevice->getBinaryShader(handle, binaryShader, binaryShaderFormat)); - EXPECT_TRUE(binaryShader.size() != 0); + EXPECT_FALSE(binaryShader.empty()); EXPECT_TRUE(binaryShaderFormat != BinaryShaderFormatID::Invalid()); const DeviceResourceHandle handleBinary = testDevice->uploadBinaryShader(*testEffect, &binaryShader.front(), static_cast(binaryShader.size()), binaryShaderFormat); @@ -462,8 +473,8 @@ namespace ramses_internal const glm::vec2 vec2Value(5.0f); // This will trigger internal asserts if the shader is not correct/loaded properly - testDevice->setConstant(DataFieldHandle(testEffect->getUniformDataFieldHandleByName("u_float")), 1, &floatValue); - testDevice->setConstant(DataFieldHandle(testEffect->getUniformDataFieldHandleByName("u_vec2")), 1, &vec2Value); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect->getUniformDataFieldHandleByName("u_float")), 1, &floatValue)); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect->getUniformDataFieldHandleByName("u_vec2")), 1, &vec2Value)); testDevice->deleteShader(handle); testDevice->deleteShader(handleBinary); @@ -492,7 +503,7 @@ namespace ramses_internal EDrawMode::Lines, templateEffect->getUniformInputs(), templateEffect->getAttributeInputs(), - "invalid effect", ResourceCacheFlag_DoNotCache); + "invalid effect"); const auto shaderGpuResource = testDevice->uploadShader(invalidEffect); EXPECT_EQ(nullptr, shaderGpuResource); } @@ -524,7 +535,7 @@ namespace ramses_internal EDrawMode::Points, templateEffect->getUniformInputs(), templateEffect->getAttributeInputs(), - "invalid effect", ResourceCacheFlag_DoNotCache); + "invalid effect"); const auto shaderGpuResource = testDevice->uploadShader(invalidEffect); EXPECT_EQ(nullptr, shaderGpuResource); } @@ -542,9 +553,87 @@ namespace ramses_internal testDevice->activateShader(handle); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); // check that there were no errors (GL_NO_ERROR in case of OpenGL) - triggers an internal assert otherwise float floatValue = 10.0; - testDevice->setConstant(DataFieldHandle(testEffect->getUniformDataFieldHandleByName("u_geomFloat")), 1, &floatValue); + EXPECT_TRUE(testDevice->setConstant(DataFieldHandle(testEffect->getUniformDataFieldHandleByName("u_geomFloat")), 1, &floatValue)); EXPECT_TRUE(testDevice->isDeviceStatusHealthy()); testDevice->deleteShader(handle); } + + TEST_F(ADeviceSupportingGeometryShaders, PrintInvalidShaderToLogger) + { + std::vector collectedErrors; + + ramses::RamsesFramework::SetLogHandler([&collectedErrors](auto level, auto /*unused*/, auto message) { + if (level == ramses::ELogLevel::Error) + { + collectedErrors.push_back(std::string(message)); + } + }); + + const auto invalidShader{"--this is some invalide shader source code--"}; + const std::unique_ptr templateEffect(CreateTestEffectResource()); + { + const EffectResource invalidEffect(invalidShader, + templateEffect->getFragmentShader(), + templateEffect->getGeometryShader(), + std::nullopt, + templateEffect->getUniformInputs(), + templateEffect->getAttributeInputs(), + "invalid effect"); + (void)testDevice->uploadShader(invalidEffect); + + EXPECT_THAT(collectedErrors, ::testing::Contains(std::string{"1: Device_Base::PrintShaderSourceWithLineNumbers: L1: "} + invalidShader)); + } + + auto shader = { + "#version 320 es", + "", + "layout(points) in;", + "layout(points, max_vertices = 1) out;", + "uniform highp float u_geomFloat;", + "", + "uniform highp vec4 u_float; //interface mismatch", + "", + "void main() {", + "", + " gl_Position = vec4(u_geomFloat * u_float);", + " EmitVertex();", + " EndPrimitive() // missing ;", + "}", + }; + std::string invalidGeometryShader{}; + for (auto& line : shader) + { + invalidGeometryShader += line; + invalidGeometryShader += '\n'; + } + collectedErrors.clear(); + { + const EffectResource invalidEffect(templateEffect->getVertexShader(), + templateEffect->getFragmentShader(), + invalidGeometryShader, + EDrawMode::Points, + templateEffect->getUniformInputs(), + templateEffect->getAttributeInputs(), + "invalid effect"); + (void)testDevice->uploadShader(invalidEffect); + + std::vector expectedResult; + std::size_t l = 1; + for (const auto& line : shader) + { + expectedResult.push_back(std::string{"1: Device_Base::PrintShaderSourceWithLineNumbers: L"} + std::to_string(l) + ": " + line); + l++; + } + + auto it = std::find(collectedErrors.begin(), collectedErrors.end(), expectedResult[0]); + for (const auto& line : expectedResult) + { + ASSERT_NE(it, collectedErrors.end()); + EXPECT_EQ(*it, line); + it++; + } + } + ramses::RamsesFramework::SetLogHandler(nullptr); + } } diff --git a/integration/PlatformTests/RenderBackendTests/src/main.cpp b/tests/integration/render-backend-tests/main.cpp similarity index 83% rename from integration/PlatformTests/RenderBackendTests/src/main.cpp rename to tests/integration/render-backend-tests/main.cpp index ebf8569c9..00615eaa5 100644 --- a/integration/PlatformTests/RenderBackendTests/src/main.cpp +++ b/tests/integration/render-backend-tests/main.cpp @@ -8,9 +8,9 @@ #include "gmock/gmock.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include "RendererTestUtils.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #include "ramses-cli.h" int main(int argc, char* argv[]) @@ -34,10 +34,10 @@ int main(int argc, char* argv[]) return -1; } CLI11_PARSE(cli, argc, argv); - RendererTestUtils::SetDefaultConfigForAllTests(rendererConfig, displayConfig); + ramses::internal::RendererTestUtils::SetDefaultConfigForAllTests(rendererConfig, displayConfig); // set log prefix for all tests - ramses_internal::ThreadLocalLog::SetPrefix(1); + ramses::internal::ThreadLocalLog::SetPrefix(1); return RUN_ALL_TESTS(); } diff --git a/renderer/RendererTestUtils/CMakeLists.txt b/tests/integration/renderer-test-utils/CMakeLists.txt similarity index 71% rename from renderer/RendererTestUtils/CMakeLists.txt rename to tests/integration/renderer-test-utils/CMakeLists.txt index ca6070cf1..d80f008bf 100644 --- a/renderer/RendererTestUtils/CMakeLists.txt +++ b/tests/integration/renderer-test-utils/CMakeLists.txt @@ -11,10 +11,11 @@ IF (${ramses-sdk_BUILD_TESTS}) NAME RendererTestUtils TYPE STATIC_LIBRARY - INCLUDE_PATHS include - SRC_FILES include/*.h - src/*.cpp - DEPENDENCIES ramses-renderer-impl - RendererTestCommon + INCLUDE_PATHS . + SRC_FILES *.h + *.cpp + DEPENDENCIES ramses-renderer + ramses-client + renderer-test-common ) endif() diff --git a/tests/integration/renderer-test-utils/ReadPixelCallbackHandler.h b/tests/integration/renderer-test-utils/ReadPixelCallbackHandler.h new file mode 100644 index 000000000..614c41ea4 --- /dev/null +++ b/tests/integration/renderer-test-utils/ReadPixelCallbackHandler.h @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/IRendererEventHandler.h" + +namespace ramses::internal +{ + class ReadPixelCallbackHandler : public RendererEventHandlerEmpty + { + public: + void framebufferPixelsRead(const uint8_t* pixelData, const uint32_t pixelDataSize, displayId_t /*displayId*/, displayBufferId_t /*displayBuffer*/, [[maybe_unused]] ERendererEventResult result) override + { + m_pixelData.clear(); + + assert(result == ERendererEventResult::Ok); + m_pixelData.resize(pixelDataSize); + PlatformMemory::Copy(&m_pixelData[0], pixelData, pixelDataSize * sizeof(uint8_t)); + + m_pixelDataRead = true; + } + + std::vector m_pixelData; + bool m_pixelDataRead = false; + }; +} diff --git a/renderer/RendererTestUtils/include/RendererAndSceneTestEventHandler.h b/tests/integration/renderer-test-utils/RendererAndSceneTestEventHandler.h similarity index 84% rename from renderer/RendererTestUtils/include/RendererAndSceneTestEventHandler.h rename to tests/integration/renderer-test-utils/RendererAndSceneTestEventHandler.h index 4e4f612e1..88ffd21c4 100644 --- a/renderer/RendererTestUtils/include/RendererAndSceneTestEventHandler.h +++ b/tests/integration/renderer-test-utils/RendererAndSceneTestEventHandler.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERANDSCENETESTEVENTHANDLER_H -#define RAMSES_RENDERERANDSCENETESTEVENTHANDLER_H - -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "RamsesRendererImpl.h" -#include "RamsesRendererUtils.h" -#include "PlatformAbstraction/PlatformTime.h" +#pragma once + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/client/Scene.h" +#include "impl/RamsesRendererImpl.h" +#include "internal/PlatformAbstraction/PlatformTime.h" #include -namespace ramses +namespace ramses::internal { class RendererAndSceneTestEventHandler final : public RendererEventHandlerEmpty, public RendererSceneControlEventHandlerEmpty { @@ -60,19 +59,19 @@ namespace ramses m_displays.erase(displayId); } - void offscreenBufferCreated(displayId_t, displayBufferId_t offscreenBufferId, ERendererEventResult result) override + void offscreenBufferCreated(displayId_t /*displayId*/, displayBufferId_t offscreenBufferId, ERendererEventResult result) override { if (result == ERendererEventResult::Ok) m_offscreenBuffers.insert({ offscreenBufferId, false }); } - void offscreenBufferDestroyed(displayId_t, displayBufferId_t offscreenBufferId, ERendererEventResult result) override + void offscreenBufferDestroyed(displayId_t /*displayId*/, displayBufferId_t offscreenBufferId, ERendererEventResult result) override { if (result == ERendererEventResult::Ok) m_offscreenBuffers.erase(offscreenBufferId); } - void offscreenBufferLinked(displayBufferId_t offscreenBufferId, sceneId_t, dataConsumerId_t, bool success) override + void offscreenBufferLinked(displayBufferId_t offscreenBufferId, sceneId_t /*consumerScene*/, dataConsumerId_t /*consumerId*/, bool success) override { if (success) m_offscreenBuffers[offscreenBufferId] = true; @@ -81,14 +80,19 @@ namespace ramses void streamAvailabilityChanged(waylandIviSurfaceId_t streamId, bool available) override { if (available) + { m_availableStreams.insert(streamId); + } else + { m_availableStreams.erase(streamId); + } } - bool waitForSceneState(sceneId_t sceneId, RendererSceneState state) + bool waitForSceneState(ramses::Scene& scene, RendererSceneState state) { - return waitUntilOrTimeout([&] { return m_scenes.count(sceneId) > 0 && m_scenes.find(sceneId)->second.state == state; }); + const auto sceneId = scene.getSceneId(); + return waitUntilOrTimeout([&] { return m_scenes.count(sceneId) > 0 && m_scenes.find(sceneId)->second.state == state; }, &scene); } bool waitForDisplayCreation(displayId_t displayId) @@ -147,12 +151,15 @@ namespace ramses } private: - bool waitUntilOrTimeout(const std::function& conditionFunction) + bool waitUntilOrTimeout(const std::function& conditionFunction, ramses::Scene* scene = nullptr) { const std::chrono::steady_clock::time_point timeoutTS = std::chrono::steady_clock::now() + m_timeout; while (!conditionFunction() && std::chrono::steady_clock::now() < timeoutTS) { - if (!m_renderer.m_impl.isThreaded()) + if (scene) + scene->flush(); // flush periodically to not block subscribing in local publish mode + + if (!m_renderer.impl().isThreaded()) m_renderer.doOneLoop(); std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); // will give the renderer time to process changes @@ -180,5 +187,3 @@ namespace ramses std::chrono::milliseconds m_timeout; }; } - -#endif diff --git a/tests/integration/renderer-test-utils/RendererTestUtils.cpp b/tests/integration/renderer-test-utils/RendererTestUtils.cpp new file mode 100644 index 000000000..21cd57c7a --- /dev/null +++ b/tests/integration/renderer-test-utils/RendererTestUtils.cpp @@ -0,0 +1,223 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererTestUtils.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "impl/RamsesRendererImpl.h" +#include "impl/DisplayConfigImpl.h" +#include "impl/RendererConfigImpl.h" +#include "internal/Core/Utils/Image.h" +#include "internal/Core/Utils/File.h" +#include "RendererAndSceneTestEventHandler.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "ReadPixelCallbackHandler.h" + +namespace ramses::internal +{ + const float RendererTestUtils::DefaultMaxAveragePercentPerPixel = 0.2f; + std::optional RendererTestUtils::MaxFrameCallbackPollingTime; + std::optional RendererTestUtils::WaylandDisplayForSystemCompositorController; + std::unique_ptr RendererTestUtils::defaultRendererConfig = std::make_unique(); + std::unique_ptr RendererTestUtils::defaultDisplayConfig = std::make_unique(); + + namespace + { + // If multiple platform backends are created at the same time, they have to have different id's + // The numbers have special meaning, therefore the test uses 100, 101, 102, ... to make sure they + // don't clash with existing applications + const uint32_t firstIviSurfaceId = 10010; + } + + void RendererTestUtils::SaveScreenshotForDisplay(RamsesRenderer& renderer, displayId_t displayId, displayBufferId_t displayBuffer, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& screenshotFileName) + { + const Image screenshotBitmap = ReadPixelData(renderer, displayId, displayBuffer, x, y, width, height); + screenshotBitmap.saveToFilePNG("./res/" + screenshotFileName + ".PNG"); + } + + bool RendererTestUtils::PerformScreenshotTestForDisplay( + RamsesRenderer& renderer, + displayId_t displayId, + displayBufferId_t displayBuffer, + uint32_t x, uint32_t y, uint32_t width, uint32_t height, + const std::string& screenshotFileName, + float maxAveragePercentErrorPerPixel, + bool saveDiffOnError) + { + const Image screenshotBitmap = ReadPixelData(renderer, displayId, displayBuffer, x, y, width, height); + return CompareBitmapToImageInFile(screenshotBitmap, screenshotFileName, maxAveragePercentErrorPerPixel, saveDiffOnError); + } + + bool RendererTestUtils::CompareBitmapToImageInFile(const Image& actualBitmap, const std::string& expectedScreenshotFileName, float maxAveragePercentErrorPerPixel, bool saveDiffOnError) + { + Image expectedBitmap; + expectedBitmap.loadFromFilePNG("./res/" + expectedScreenshotFileName + ".PNG"); + if (expectedBitmap.getWidth() != actualBitmap.getWidth() || + expectedBitmap.getHeight() != actualBitmap.getHeight()) + { + fmt::print("Screenshot comparison failed: size of expected image {}/{} does not match size of actual image {}/{}\n", + expectedBitmap.getWidth(), expectedBitmap.getHeight(), actualBitmap.getWidth(), actualBitmap.getHeight()); + + return false; + } + const Image diff = expectedBitmap.createDiffTo(actualBitmap); + assert(diff.getNumberOfPixels() == actualBitmap.getNumberOfPixels()); + + const auto sumOfPixelDiff = diff.getSumOfPixelValues(); + const uint32_t totalNumberOfPixels = diff.getNumberOfPixels(); + + if (totalNumberOfPixels == 0) + { + assert(false); + fmt::print("Screenshot comparison failed: difference bitmap has no pixels!\n"); + return false; + } + + const float averagePercentErrorPerPixel = (100.0f * (sumOfPixelDiff.x + sumOfPixelDiff.y + sumOfPixelDiff.z + sumOfPixelDiff.w) / 256.0f) / totalNumberOfPixels; + + if (averagePercentErrorPerPixel > maxAveragePercentErrorPerPixel) + { + if (saveDiffOnError) + { + fmt::print("Screenshot comparison failed: {} ({}x{})\n", expectedScreenshotFileName.c_str(), diff.getWidth(), diff.getHeight()); + fmt::print(" - avg error per pixel {}, maximum allowed error is {}\n", averagePercentErrorPerPixel, maxAveragePercentErrorPerPixel); + fmt::print(" - total error R={}, G={}, B={}, A={}n", sumOfPixelDiff.x, sumOfPixelDiff.y, sumOfPixelDiff.z, sumOfPixelDiff.w); + fmt::print(" - number of pixels different by more than 1 (one or more color channels): {}\n", diff.getNumberOfNonBlackPixels(1)); + fmt::print(" - number of pixels different by more than 64 (one or more color channels): {}\n", diff.getNumberOfNonBlackPixels(64)); + fmt::print(" - number of pixels different by more than 128 (one or more color channels): {}\n", diff.getNumberOfNonBlackPixels(128)); + fmt::print(" - number of pixels different by 255 (one or more color channels): {}\n", diff.getNumberOfNonBlackPixels(254)); + + const std::string screenShotPath{"./res/diffs"}; + File diffDirectory(screenShotPath); + diffDirectory.createDirectory(); + + actualBitmap.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_ACTUAL.PNG"); + diff.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_DIFF.PNG"); + + //Create separate RGB and Alpha diffs + std::pair colorAndAlphaDiffImages = diff.createSeparateColorAndAlphaImages(); + colorAndAlphaDiffImages.first.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_DIFF_RGB.PNG"); + colorAndAlphaDiffImages.second.saveToFilePNG(screenShotPath + "/" + expectedScreenshotFileName + "_DIFF_ALPHA.PNG"); + } + + return false; + } + + return true; + } + + displayId_t RendererTestUtils::CreateDisplayImmediate(RamsesRenderer& renderer, const ramses::DisplayConfig& displayConfig) + { + const displayId_t displayId = renderer.createDisplay(displayConfig); + renderer.flush(); + RendererAndSceneTestEventHandler eventHandler(renderer); + if (eventHandler.waitForDisplayCreation(displayId)) + { + return displayId; + } + + assert(false && "Display construction failed or timed out!"); + return displayId_t::Invalid(); + } + + void RendererTestUtils::DestroyDisplayImmediate(RamsesRenderer& renderer, displayId_t displayId) + { + renderer.destroyDisplay(displayId); + renderer.flush(); + RendererAndSceneTestEventHandler eventHandler(renderer); + eventHandler.registerAlreadyCreatedDisplay(displayId); + if (!eventHandler.waitForDisplayDestruction(displayId)) + { + assert(false && "Display destruction failed or timed out!"); + } + } + + ramses::RendererConfig RendererTestUtils::CreateTestRendererConfig() + { + ramses::RendererConfig rendererConfig(*defaultRendererConfig); + auto& internalRendererConfig = const_cast(rendererConfig.impl().getInternalRendererConfig()); + + if (WaylandDisplayForSystemCompositorController.has_value()) + { + internalRendererConfig.enableSystemCompositorControl(); + internalRendererConfig.setWaylandDisplayForSystemCompositorController(WaylandDisplayForSystemCompositorController.value()); + } + + if(MaxFrameCallbackPollingTime.has_value()) + internalRendererConfig.setFrameCallbackMaxPollTime(MaxFrameCallbackPollingTime.value()); + + return rendererConfig; + } + + void RendererTestUtils::SetWaylandDisplayForSystemCompositorControllerForAllTests(const std::string& wd) + { + WaylandDisplayForSystemCompositorController = wd; + } + + void RendererTestUtils::SetDefaultConfigForAllTests(const ramses::RendererConfig& rendererConfig, const ramses::DisplayConfig& displayConfig) + { + defaultRendererConfig = std::make_unique(rendererConfig); + defaultDisplayConfig = std::make_unique(displayConfig); + } + + void RendererTestUtils::SetMaxFrameCallbackPollingTimeForAllTests(std::chrono::microseconds time) + { + MaxFrameCallbackPollingTime = time; + } + + ramses::DisplayConfig RendererTestUtils::CreateTestDisplayConfig(uint32_t iviSurfaceIdOffset, bool iviWindowStartVisible) + { + ramses::DisplayConfig displayConfig(*defaultDisplayConfig); + displayConfig.setWaylandIviSurfaceID(waylandIviSurfaceId_t(firstIviSurfaceId + iviSurfaceIdOffset)); + + if(iviWindowStartVisible) + displayConfig.setWindowIviVisible(); + + return displayConfig; + } + + Image RendererTestUtils::ReadPixelData( + RamsesRenderer& renderer, + displayId_t displayId, + displayBufferId_t displayBuffer, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height) + { + [[maybe_unused]] const bool status = renderer.readPixels(displayId, displayBuffer, x, y, width, height); + assert(status); + renderer.flush(); + + constexpr std::chrono::seconds timeoutTime{ 10u }; + const auto startTime = std::chrono::steady_clock::now(); + ReadPixelCallbackHandler callbackHandler; + while (true) + { + if (!renderer.impl().isThreaded()) + renderer.doOneLoop(); + + renderer.dispatchEvents(callbackHandler); + if (callbackHandler.m_pixelDataRead) + break; + + if ((std::chrono::steady_clock::now() - startTime) > timeoutTime) + { + LOG_ERROR(CONTEXT_RENDERER, "RendererTestUtils::ReadPixelData: ran out of time!"); + return Image{}; + } + std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); + } + + assert(callbackHandler.m_pixelData.size() == width * height * 4u); + + // flip image vertically so that the layout read from frame buffer (bottom-up) + // is converted to layout normally used in image files (top-down) + return Image(width, height, callbackHandler.m_pixelData.cbegin(), callbackHandler.m_pixelData.cend(), true); + } +} diff --git a/tests/integration/renderer-test-utils/RendererTestUtils.h b/tests/integration/renderer-test-utils/RendererTestUtils.h new file mode 100644 index 000000000..3e5fcd9a8 --- /dev/null +++ b/tests/integration/renderer-test-utils/RendererTestUtils.h @@ -0,0 +1,90 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" +#include "internal/Core/Utils/Image.h" + +#include +#include +#include + +namespace ramses +{ + class RamsesRenderer; + class RamsesDisplay; +} + +namespace ramses::internal +{ + class RendererTestUtils + { + public: + static void SaveScreenshotForDisplay( + RamsesRenderer& renderer, + displayId_t displayId, + displayBufferId_t displayBuffer, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + const std::string& screenshotFileName); + + static bool PerformScreenshotTestForDisplay( + RamsesRenderer& renderer, + displayId_t displayId, + displayBufferId_t displayBuffer, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + const std::string& screenshotFileName, + float maxAveragePercentErrorPerPixel = DefaultMaxAveragePercentPerPixel, + bool saveDiffOnError = true + ); + + static ramses::internal::Image ReadPixelData( + RamsesRenderer& renderer, + displayId_t displayId, + displayBufferId_t displayBuffer, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height); + + static bool CompareBitmapToImageInFile( + const ramses::internal::Image& actualBitmap, + const std::string& expectedScreenshotFileName, + float maxAveragePercentErrorPerPixel, + bool saveDiffOnError); + + static displayId_t CreateDisplayImmediate(RamsesRenderer& renderer, const ramses::DisplayConfig& displayConfig); + static void DestroyDisplayImmediate(RamsesRenderer& renderer, displayId_t displayId); + + // All renderer tests should use this renderer config which adds dummy embedded compositor + static ramses::RendererConfig CreateTestRendererConfig(); + // All renderer tests should use this display config which makes window visible by default for Wayland + static ramses::DisplayConfig CreateTestDisplayConfig(uint32_t iviSurfaceIdOffset, bool iviWindowStartVisible = true); + static void SetMaxFrameCallbackPollingTimeForAllTests(std::chrono::microseconds time); + static void SetDefaultConfigForAllTests(const ramses::RendererConfig& rendererConfig, const ramses::DisplayConfig& displayConfig); + + static void SetWaylandDisplayForSystemCompositorControllerForAllTests(const std::string& wd); + + static const float DefaultMaxAveragePercentPerPixel; + + private: + static std::optional MaxFrameCallbackPollingTime; + static std::optional WaylandDisplayForSystemCompositorController; + + // config objects are not copyable.. + static std::unique_ptr defaultRendererConfig; + static std::unique_ptr defaultDisplayConfig; + }; +} diff --git a/integration/SandwichTests/RendererTests/CMakeLists.txt b/tests/integration/renderer-tests/CMakeLists.txt similarity index 52% rename from integration/SandwichTests/RendererTests/CMakeLists.txt rename to tests/integration/renderer-tests/CMakeLists.txt index 5c5926847..90143f7b4 100644 --- a/integration/SandwichTests/RendererTests/CMakeLists.txt +++ b/tests/integration/renderer-tests/CMakeLists.txt @@ -7,112 +7,113 @@ # ------------------------------------------------------------------------- set(RendererTests-resources_MIXIN - RESOURCE_FOLDERS ../../TestContent/res - res + RESOURCE_FOLDERS res + ../test-content/res ) +function(disable_leak_detection_for_test test_name) + message(STATUS "Disable leak detection for ${test_name}") + + get_test_property(${test_name} ENVIRONMENT CURRENT_ENV) + set_tests_properties(${test_name} + PROPERTIES ENVIRONMENT "ASAN_OPTIONS=$ENV{ASAN_OPTIONS}:detect_leaks=0;${CURRENT_ENV}") +endfunction() + createModuleWithRenderer( - NAME RenderingTests + NAME rendering-tests TYPE BINARY ENABLE_INSTALL ON - INCLUDE_PATHS RendererTestFramework - RenderingTests - SRC_FILES RenderingTests/*.h - RendererTestFramework/*.h - RenderingTests/*.cpp - RendererTestFramework/*.cpp + INCLUDE_PATHS renderer-test-framework + rendering-tests + SRC_FILES rendering-tests/*.h + renderer-test-framework/*.h + rendering-tests/*.cpp + renderer-test-framework/*.cpp ${RendererTests-resources_MIXIN} - DEPENDENCIES TestContent FrameworkTestUtils RendererTestUtils ramses-cli + DEPENDENCIES test-content framework-test-utils RendererTestUtils ramses-cli ) createModuleWithRenderer( - NAME RendererLifecycleTests + NAME renderer-lifecycle-tests TYPE BINARY ENABLE_INSTALL ON - INCLUDE_PATHS RendererLifecycleTests - RendererTestFramework + INCLUDE_PATHS renderer-lifecycle-tests + renderer-test-framework - SRC_FILES RendererLifecycleTests/*.h - RendererTestFramework/*.h + SRC_FILES renderer-lifecycle-tests/*.h + renderer-test-framework/*.h - RendererLifecycleTests/main.cpp - RendererLifecycleTests/RendererLifecycleTests.cpp - RendererTestFramework/*.cpp + renderer-lifecycle-tests/*.cpp + renderer-test-framework/*.cpp ${RendererTests-resources_MIXIN} - DEPENDENCIES TestContent FrameworkTestUtils RendererTestUtils + DEPENDENCIES test-content framework-test-utils RendererTestUtils ramses-gmock-main ramses-cli ) -if(ramses-sdk-ENABLE_WINDOW_TYPE_WINDOWS OR ramses-sdk-ENABLE_WINDOW_TYPE_X11) - target_sources(RendererLifecycleTests PRIVATE "RendererLifecycleTests/ExternalWindowTests.cpp") - folderizeTarget(RendererLifecycleTests) -endif() - if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) createModule( - NAME EmbeddedCompositingTests + NAME embedded-compositing-rendering-tests TYPE BINARY ENABLE_INSTALL ON - INCLUDE_PATHS RendererTestFramework - EmbeddedCompositingTests - EmbeddedCompositingTests/EmbeddedCompositingTestFramework - EmbeddedCompositingTests/TestWaylandApplication - EmbeddedCompositingTests/TestCases + INCLUDE_PATHS renderer-test-framework + embedded-compositing-rendering-tests + embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework + embedded-compositing-rendering-tests/TestWaylandApplication + embedded-compositing-rendering-tests/TestCases - SRC_FILES EmbeddedCompositingTests/EmbeddedCompositingTestFramework/*.h - EmbeddedCompositingTests/TestWaylandApplication/*.h - EmbeddedCompositingTests/TestCases/*.h + SRC_FILES embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/*.h + embedded-compositing-rendering-tests/TestWaylandApplication/*.h + embedded-compositing-rendering-tests/TestCases/*.h - RendererTestFramework/*.cpp - EmbeddedCompositingTests/EmbeddedCompositingTestFramework/*.cpp - EmbeddedCompositingTests/TestWaylandApplication/*.cpp - EmbeddedCompositingTests/TestCases/*.cpp + renderer-test-framework/*.cpp + embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/*.cpp + embedded-compositing-rendering-tests/TestWaylandApplication/*.cpp + embedded-compositing-rendering-tests/TestCases/*.cpp ${RendererTests-resources_MIXIN} - DEPENDENCIES TestContent - FrameworkTestUtils + DEPENDENCIES test-content + framework-test-utils RendererTestUtils - WaylandUtilities ramses-cli ) # TODO(tobias/mohamed) Disable test broken by newer ubuntu 18.04 packages - makeTestFromTarget(TARGET EmbeddedCompositingTests + makeTestFromTarget(TARGET embedded-compositing-rendering-tests SUFFIX RNDSANDWICHTEST_SWRAST EXTRA_ARGS --ivi-layer 3 --ivi-control --filter-out ShowStreamTextureAfterChangingSurfaceSize --window-type wayland-ivi - SKIPPABLE ) - createModuleWithRenderer( - NAME DmaOffscreenBufferRenderingTests - TYPE BINARY - ENABLE_INSTALL ON + if(libdrm_FOUND AND gbm_FOUND) + createModuleWithRenderer( + NAME dma-offscreen-buffer-rendering-tests + TYPE BINARY + ENABLE_INSTALL ON - INCLUDE_PATHS RendererTestFramework - DmaOffscreenBufferRenderingTests + INCLUDE_PATHS renderer-test-framework + dma-offscreen-buffer-rendering-tests - SRC_FILES DmaOffscreenBufferRenderingTests/*.h - RendererTestFramework/*.h + SRC_FILES dma-offscreen-buffer-rendering-tests/*.h + renderer-test-framework/*.h - DmaOffscreenBufferRenderingTests/*.cpp - RendererTestFramework/*.cpp + dma-offscreen-buffer-rendering-tests/*.cpp + renderer-test-framework/*.cpp - ${RendererTests-resources_MIXIN} + ${RendererTests-resources_MIXIN} - DEPENDENCIES TestContent - FrameworkTestUtils - RendererTestUtils - Device_EGL_Extension - ramses-cli - ) + DEPENDENCIES test-content + framework-test-utils + RendererTestUtils + ramses-cli + ) + endif() endif() # Tests which are supposed to be run with valgrind @@ -128,13 +129,13 @@ SET(GATE_WINDOW_PLATFORMS if (ramses-sdk_BUILD_TESTS) makeTestPerWindowTypeFromTarget( - TARGET RenderingTests + TARGET rendering-tests SUFFIX RNDSANDWICHTEST EXTRA_ARGS --ivi-control --ivi-layer 3 ) makeTestPerWindowTypeFromTarget( - TARGET RendererLifecycleTests + TARGET renderer-lifecycle-tests SUFFIX RNDSANDWICHTEST_SWRAST EXTRA_ARGS --ivi-control --ivi-layer 3 ) @@ -147,41 +148,40 @@ if (ramses-sdk_BUILD_TESTS) # A limited set of rendering tests executed in the gate (executing all tests takes too long) makeTestPerWindowTypeFromTarget( - TARGET RenderingTests + TARGET rendering-tests SUFFIX RNDSANDWICHTEST_VALGRINDGATE WINDOW_TYPE_FILTER ${VALGRIND_WINDOW_PLATFORMS} EXTRA_ARGS --ivi-control --ivi-layer 3 --filter-in TextTest_AccurateText:TextureTest_Texture2D_AddressMode ) # A limited set of renderer lifecycle tests executed in the gate (executing all tests takes too long) makeTestPerWindowTypeFromTarget( - TARGET RendererLifecycleTests + TARGET renderer-lifecycle-tests SUFFIX RNDSANDWICHTEST_VALGRINDGATE WINDOW_TYPE_FILTER ${VALGRIND_WINDOW_PLATFORMS} EXTRA_ARGS --ivi-control --ivi-layer 3 --gtest_filter=*DestroyRenderer_ChangeScene_ThenRecreateRenderer*:*RecreateSceneWithSameId* ) # A limited set of EC tests executed in the gate (executing all tests takes too long) makeTestPerWindowTypeFromTarget( - TARGET EmbeddedCompositingTests + TARGET embedded-compositing-rendering-tests SUFFIX RNDSANDWICHTEST_VALGRINDGATE WINDOW_TYPE_FILTER ${VALGRIND_WINDOW_PLATFORMS} EXTRA_ARGS --ivi-control --ivi-layer 3 --filter-in ShowStreamTextureAfterRecreatingScene:Recreated:ClientCanBindMultipleTimes ) makeTestPerWindowTypeFromTarget( - TARGET RenderingTests + TARGET rendering-tests SUFFIX RNDSANDWICHTEST_SWRAST WINDOW_TYPE_FILTER ${GATE_WINDOW_PLATFORMS} EXTRA_ARGS --ivi-control --ivi-layer 3 --filter-out ${SW_EMULATION_FILTER} ) + # Disable LSAN leak detection inside ASAN for rendering tests where lsan is unstable and sporadically crashes. if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) - # Disable LSAN leak detection inside ASAN because it segfaults on destruction for this test only. - # Crash seems to be caused when traversing DTLS info for a potentially unmapped thread stack. - message(STATUS "Disable leak detection for RenderingTests-wayland-ivi-egl-es-3-0_RNDSANDWICHTEST_SWRAST") - - get_test_property(RenderingTests_wayland-ivi-gles30_RNDSANDWICHTEST_SWRAST ENVIRONMENT CURRENT_ENV) - set_tests_properties(RenderingTests_wayland-ivi-gles30_RNDSANDWICHTEST_SWRAST - PROPERTIES ENVIRONMENT "ASAN_OPTIONS=$ENV{ASAN_OPTIONS}:detect_leaks=0;${CURRENT_ENV}") + disable_leak_detection_for_test("rendering-tests_wayland-ivi-gles30_RNDSANDWICHTEST_SWRAST") + endif() + if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) + disable_leak_detection_for_test("rendering-tests_x11-gles30_RNDSANDWICHTEST_SWRAST") + disable_leak_detection_for_test("renderer-lifecycle-tests_x11-gles30_RNDSANDWICHTEST_SWRAST") endif() ENDIF() diff --git a/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferRenderingTests.h b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferRenderingTests.h new file mode 100644 index 000000000..fdd7547b1 --- /dev/null +++ b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferRenderingTests.h @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "RendererTestsFramework.h" +#include "DmaOffscreenBufferTests.h" +#include "internal/Core/Utils/LogMacros.h" + +#include +#include + +namespace ramses::internal +{ + class DmaOffscreenBufferRenderingTests + { + public: + DmaOffscreenBufferRenderingTests(const std::vector& filterIn, const std::vector& filterOut, bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) + : m_testFramework(generateScreenshots, config) + { + m_dmaOffscreenBufferTests.setUpTestCases(m_testFramework); + m_testFramework.filterTestCases(filterIn, filterOut); + } + + bool runTests() + { + return m_testFramework.runAllTests(); + } + + void logReport() + { + fmt::print("{}\n", m_testFramework.generateReport()); + } + + protected: + RendererTestsFramework m_testFramework; + DmaOffscreenBufferTests m_dmaOffscreenBufferTests; + }; +} diff --git a/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.cpp b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.cpp new file mode 100644 index 000000000..b4ab96768 --- /dev/null +++ b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.cpp @@ -0,0 +1,574 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "DmaOffscreenBufferTests.h" +#include "TestScenes/TextureLinkScene.h" +#include "TestScenes/MultipleTrianglesScene.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/Utils/LogMacros.h" +#include +#include +#include +#include +#include + +namespace ramses::internal +{ + void DmaOffscreenBufferTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferRead, *this, "DmaOffscreenBufferTest_BufferRead"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferWrite, *this, "DmaOffscreenBufferTest_BufferWrite"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferReadWrite, *this, "DmaOffscreenBufferTest_BufferReadWrite"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_ReadContentWithAlphaBlending, *this, "DmaOffscreenBufferTest_ContentWithAlphaBlending"); + + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_XBGR8888, *this, "DmaOffscreenBufferTest_Format_XBGR8888"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_XRGB8888, *this, "DmaOffscreenBufferTest_Format_XRGB8888"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_ARGB8888, *this, "DmaOffscreenBufferTest_Format_ARGB8888"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_Format_BGR888, *this, "DmaOffscreenBufferTest_Format_BGR888"); + + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_UsageFlagRendering, *this, "DmaOffscreenBufferTest_UsageFlagRendering"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut, *this, "DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut"); + + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_200x200, *this, "DmaOffscreenBufferTest_BufferSize_200x200"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_256x256, *this, "DmaOffscreenBufferTest_BufferSize_256x256"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_128x64, *this, "DmaOffscreenBufferTest_BufferSize_128x64"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_BufferSize_128x256, *this, "DmaOffscreenBufferTest_BufferSize_128x256"); + + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead, *this, "DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead"); + testFramework.createTestCaseWithoutRenderer(DmaOffscreenBufferTest_TwoDisplays, *this, "DmaOffscreenBufferTest_TwoDisplays"); + } + + bool DmaOffscreenBufferTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + //Set big frame callback poll time, to make sure no frames are skipped due to late frame callbacks + auto rendererConfig = RendererTestUtils::CreateTestRendererConfig(); + rendererConfig.setFrameCallbackMaxPollTime(1000000u); + testFramework.initializeRenderer(rendererConfig); + + ramses::DisplayConfig dmaDisplayConfig = RendererTestUtils::CreateTestDisplayConfig(1u); + dmaDisplayConfig.setWindowRectangle(0, 0, IntegrationScene::DefaultViewportWidth, IntegrationScene::DefaultViewportHeight); + dmaDisplayConfig.setPlatformRenderNode("/dev/dri/renderD128"); + testFramework.createDisplay(dmaDisplayConfig); + + if(testCase.m_id == DmaOffscreenBufferTest_TwoDisplays) + { + ramses::DisplayConfig dmaDisplayConfig2 = RendererTestUtils::CreateTestDisplayConfig(2u); + dmaDisplayConfig2.setWindowRectangle(IntegrationScene::DefaultViewportWidth, 0, IntegrationScene::DefaultViewportWidth, IntegrationScene::DefaultViewportHeight); + dmaDisplayConfig2.setPlatformRenderNode("/dev/dri/renderD128"); + testFramework.createDisplay(dmaDisplayConfig2); + } + + switch (testCase.m_id) + { + case DmaOffscreenBufferTest_BufferRead: + return runBufferReadTest(testFramework); + case DmaOffscreenBufferTest_BufferWrite: + return runBufferWriteTest(testFramework); + case DmaOffscreenBufferTest_BufferReadWrite: + return runBufferReadWriteTest(testFramework); + case DmaOffscreenBufferTest_ReadContentWithAlphaBlending: + return runContentWithAlphaBlendingTest(testFramework); + + case DmaOffscreenBufferTest_Format_ARGB8888: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_ARGB8888); + case DmaOffscreenBufferTest_Format_XRGB8888: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_XRGB8888); + case DmaOffscreenBufferTest_Format_XBGR8888: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_XBGR8888); + case DmaOffscreenBufferTest_Format_BGR888: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DRM_FORMAT_BGR888); + + case DmaOffscreenBufferTest_UsageFlagRendering: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, GBM_BO_USE_RENDERING); + case DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT); + + case DmaOffscreenBufferTest_BufferSize_200x200: + return runBufferReadWriteTest(testFramework,0u, 200u, 200u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferReadWrite"); + case DmaOffscreenBufferTest_BufferSize_256x256: + return runBufferReadWriteTest(testFramework,0u, 256u, 256u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferWrite_256x256"); + case DmaOffscreenBufferTest_BufferSize_128x64: + return runBufferReadWriteTest(testFramework,0u, 128u, 64u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferWrite_128x64"); + case DmaOffscreenBufferTest_BufferSize_128x256: + return runBufferReadWriteTest(testFramework,0u, 128u, 256u, DefaultFourccBufferFormat, DefaultUsageFlags, "DmaOffscreenBufferTest_BufferWrite_128x256"); + + case DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead: + return runDoubleBufferedReadTest(testFramework); + case DmaOffscreenBufferTest_TwoDisplays: + return runBufferReadWriteTest(testFramework,0u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, DefaultUsageFlags) + && runBufferReadWriteTest(testFramework,1u, DefaultBufferWidth, DefaultBufferHeight, DefaultFourccBufferFormat, DefaultUsageFlags); + default: + assert(false && "undefined test case"); + } + + return false; + } + + template + ramses::sceneId_t DmaOffscreenBufferTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth, uint32_t vpHeight) + { + const auto sceneId = testFramework.getScenesRegistry().createScene(sceneState, camPos, vpWidth, vpHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId, displayIdx); + + return sceneId; + } + + template + bool DmaOffscreenBufferTests::renderSceneToBufferAndExpectScreenshot(RendererTestsFramework& testFramework, const TestBufferInfo& testBuffer, uint32_t sceneState, const std::string& screenshotImage) + { + //render a scene into OB, then read mapped memory for that OB and compare it with screenshot image + const auto sceneId = createAndShowScene(testFramework, testBuffer.displayIdx, sceneState, glm::vec3{0.f}, testBuffer.width, testBuffer.height); + testFramework.assignSceneToDisplayBuffer(sceneId, testBuffer.displaBufferId); + + //make two loops to make sure swap buffers was called twice after scene was assigned to OB + testFramework.flushRendererAndDoOneLoop(); + testFramework.flushRendererAndDoOneLoop(); + + //check content rendered into OB from two loops ago + const Image obMemImageResult = ReadMappedMemoryContentToImage(testBuffer); + return RendererTestUtils::CompareBitmapToImageInFile(obMemImageResult, screenshotImage, RendererTestUtils::DefaultMaxAveragePercentPerPixel, true); + } + + bool DmaOffscreenBufferTests::StartSyncBuffer(const TestBufferInfo& bufferInfo) + { + dma_buf_sync syncStartFlags = { 0 }; + switch (bufferInfo.accessRights) + { + case EMappingBufferAccessRight::ReadOnly: + syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ; + break; + case EMappingBufferAccessRight::WriteOnly: + syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE; + break; + case EMappingBufferAccessRight::ReadWrite: + syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW; + break; + } + assert(syncStartFlags.flags != 0); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive + const auto syncStartResult = ioctl(bufferInfo.fd, DMA_BUF_IOCTL_SYNC, &syncStartFlags); + if(syncStartResult != 0) + { + LOG_ERROR(CONTEXT_RENDERER, "Failed to start sync!"); + return false; + } + + return true; + } + + bool DmaOffscreenBufferTests::EndSyncBuffer(const TestBufferInfo& bufferInfo) + { + dma_buf_sync syncEndFlags = { 0 }; + switch (bufferInfo.accessRights) + { + case EMappingBufferAccessRight::ReadOnly: + syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ; + break; + case EMappingBufferAccessRight::WriteOnly: + syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE; + break; + case EMappingBufferAccessRight::ReadWrite: + syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW; + break; + } + assert(syncEndFlags.flags != 0); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive + const auto syncEndResult = ioctl(bufferInfo.fd, DMA_BUF_IOCTL_SYNC, &syncEndFlags); + if(syncEndResult != 0) + { + LOG_ERROR(CONTEXT_RENDERER, "Failed to end sync!"); + return false; + } + + return true; + } + + Image DmaOffscreenBufferTests::ReadMappedMemoryContentToImage(const TestBufferInfo& bufferInfo) + { + if(!StartSyncBuffer(bufferInfo)) + return {}; + + std::vector mappedMemImageData; + mappedMemImageData.reserve(bufferInfo.height * bufferInfo.width * 4u); + + auto readPixelARGB8888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 4u * col; + const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //R=p[2], G=p[1], B=p[0], A=p[3] + mappedMemImageData.push_back(pixelData[2]); + mappedMemImageData.push_back(pixelData[1]); + mappedMemImageData.push_back(pixelData[0]); + mappedMemImageData.push_back(pixelData[3]); + }; + + auto readPixelXRGB8888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 4u * col; + const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //R=p[2], G=p[1], B=p[0], A=255 + mappedMemImageData.push_back(pixelData[2]); + mappedMemImageData.push_back(pixelData[1]); + mappedMemImageData.push_back(pixelData[0]); + mappedMemImageData.push_back(255u); + }; + + auto readPixelXBGR8888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 4u * col; + const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //R=p[0], G=p[1], B=p[2], A=255 + mappedMemImageData.push_back(pixelData[0]); + mappedMemImageData.push_back(pixelData[1]); + mappedMemImageData.push_back(pixelData[2]); + mappedMemImageData.push_back(255u); + }; + + auto readPixelBGR888 = [&bufferInfo, &mappedMemImageData](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * (bufferInfo.height - row - 1) + 3u * col; + const uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //R=p[0], G=p[1], B=p[2], A=255 + mappedMemImageData.push_back(pixelData[0]); + mappedMemImageData.push_back(pixelData[1]); + mappedMemImageData.push_back(pixelData[2]); + mappedMemImageData.push_back(255u); + }; + + switch(bufferInfo.fourccFormat) + { + case DRM_FORMAT_ARGB8888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + readPixelARGB8888(row, col); + } + break; + case DRM_FORMAT_XRGB8888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + readPixelXRGB8888(row, col); + } + break; + case DRM_FORMAT_XBGR8888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + readPixelXBGR8888(row, col); + } + break; + case DRM_FORMAT_BGR888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + readPixelBGR888(row, col); + } + break; + default: + LOG_ERROR(CONTEXT_RENDERER, "Trying to read content from unknown buffer format!"); + assert(false); + return {}; + } + + if(!EndSyncBuffer(bufferInfo)) + return {}; + + return Image(bufferInfo.width, bufferInfo.height, std::move(mappedMemImageData)); + } + + bool DmaOffscreenBufferTests::WriteTestContentToMappedMemory(const DmaOffscreenBufferTests::TestBufferInfo& bufferInfo) + { + if(!StartSyncBuffer(bufferInfo)) + return false; + + auto calculatePixelMagicValue = [&bufferInfo](uint32_t row, uint32_t col) + { + const auto r = static_cast((1.f * col / bufferInfo.width) * 255); + const auto g = static_cast((1.f * row / bufferInfo.height) * 255); + const uint8_t b = 128u; + const uint8_t a = 255u; + + return std::array{r, g, b, a}; + }; + + auto writePixelARGB8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * row + 4u * col; + uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //p[0]=B, p[1]=G, p[2]=R, p[3]=A + const auto val = calculatePixelMagicValue(row, col); + pixelData[0] = val[2]; + pixelData[1] = val[1]; + pixelData[2] = val[0]; + pixelData[3] = val[3]; + }; + + auto writePixelXRGB8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * row + 4u * col; + uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //p[0]=B, p[1]=G, p[2]=R, p[3]=X + const auto val = calculatePixelMagicValue(row, col); + pixelData[0] = val[2]; + pixelData[1] = val[1]; + pixelData[2] = val[0]; + pixelData[3] = 0u; + }; + + auto writePixelXBGR8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * row + 4u * col; + uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //p[0]=R, p[1]=G, p[2]=B, p[3]=X + const auto val = calculatePixelMagicValue(row, col); + pixelData[0] = val[0]; + pixelData[1] = val[1]; + pixelData[2] = val[2]; + pixelData[3] = 0u; + }; + + auto writePixelBGR8888 = [&bufferInfo, &calculatePixelMagicValue](uint32_t row, uint32_t col) + { + const std::size_t pixelDataOffset = bufferInfo.stride * row + 3u * col; + uint8_t* pixelData = reinterpret_cast(bufferInfo.mappedMemory) + pixelDataOffset; + + //p[0]=R, p[1]=G, p[2]=B + const auto val = calculatePixelMagicValue(row, col); + pixelData[0] = val[0]; + pixelData[1] = val[1]; + pixelData[2] = val[2]; + }; + + switch(bufferInfo.fourccFormat) + { + case DRM_FORMAT_ARGB8888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + writePixelARGB8888(row, col); + } + break; + case DRM_FORMAT_XRGB8888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + writePixelXRGB8888(row, col); + } + break; + case DRM_FORMAT_XBGR8888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + writePixelXBGR8888(row, col); + } + break; + case DRM_FORMAT_BGR888: + for (uint32_t row = 0u; row < bufferInfo.height; ++row) + { + for(uint32_t col = 0u; col < bufferInfo.width; ++col) + writePixelBGR8888(row, col); + } + break; + default: + LOG_ERROR(CONTEXT_RENDERER, "Trying to write test content to unknown buffer format!"); + assert(false); + return false; + } + + if(!EndSyncBuffer(bufferInfo)) + return false; + + return true; + } + + bool DmaOffscreenBufferTests::CreateAndMapDmaOffscreenBuffer(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t bufferWidth, uint32_t bufferHeight, uint32_t fourccFormat, uint32_t bufferUsageFlags, EMappingBufferAccessRight accessRights, TestBufferInfo& bufferInfo, bool disableClearing) + { + bufferInfo.displayIdx = displayIdx; + bufferInfo.width = bufferWidth; + bufferInfo.height = bufferHeight; + bufferInfo.fourccFormat = fourccFormat; + + bufferInfo.displaBufferId = testFramework.createDmaOffscreenBuffer(displayIdx, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, DRM_FORMAT_MOD_INVALID); + if(!bufferInfo.displaBufferId.isValid()) + { + LOG_ERROR(CONTEXT_RENDERER, "Invalid offscreen buffer handle!"); + return false; + } + + if(disableClearing) + { + testFramework.setClearFlags(displayIdx, bufferInfo.displaBufferId, ramses::EClearFlag::None); + testFramework.flushRendererAndDoOneLoop(); + } + + if(!testFramework.getDmaOffscreenBufferFDAndStride(displayIdx, bufferInfo.displaBufferId, bufferInfo.fd, bufferInfo.stride)) + { + LOG_ERROR(CONTEXT_RENDERER, "Could not get FD and stride for DMA offscreen buffer!"); + return false; + } + + bufferInfo.accessRights = accessRights; + int accessRightsFlags = 0; + switch(accessRights) + { + case EMappingBufferAccessRight::ReadOnly: + accessRightsFlags = PROT_READ; + break; + case EMappingBufferAccessRight::WriteOnly: + accessRightsFlags = PROT_WRITE; + break; + case EMappingBufferAccessRight::ReadWrite: + accessRightsFlags = PROT_READ | PROT_WRITE; + break; + } + assert(accessRightsFlags != 0); + + const auto fileSize = lseek(bufferInfo.fd, 0u, SEEK_END); + bufferInfo.mappedMemory = mmap(nullptr, fileSize, accessRightsFlags, MAP_SHARED, bufferInfo.fd, 0u); + if (bufferInfo.mappedMemory == MAP_FAILED) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) + { + LOG_ERROR(CONTEXT_RENDERER, "Failed to map memory!"); + return false; + } + + return true; + } + + bool DmaOffscreenBufferTests::runBufferWriteTest(RendererTestsFramework& testFramework) + { + constexpr uint32_t bufferWidth = 256u; + constexpr uint32_t bufferHeight = 256u; + constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; + constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; + + //write some content into mapped memory of DMA OB, then link to tex consumer and make screenshot test + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 0u, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::WriteOnly, testBuffer1)) + return false; + + if(!WriteTestContentToMappedMemory(testBuffer1)) + return false; + + const auto sceneIdConsumer = createAndShowScene(testFramework, 0u, TextureLinkScene::DATA_CONSUMER, m_cameraTranslation); + testFramework.createBufferDataLink(testBuffer1.displaBufferId, sceneIdConsumer, TextureLinkScene::DataConsumerId); + + return testFramework.renderAndCompareScreenshot("DmaOffscreenBufferTest_BufferWrite"); + } + + bool DmaOffscreenBufferTests::runBufferReadTest(RendererTestsFramework &testFramework) + { + constexpr uint32_t bufferWidth = 200u; + constexpr uint32_t bufferHeight = 200u; + constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; + constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; + + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 0u, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer1, false)) + return false; + + return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::THREE_TRIANGLES, "MultipleTrianglesScene_ThreeTriangles"); + } + + bool DmaOffscreenBufferTests::runContentWithAlphaBlendingTest(RendererTestsFramework &testFramework) + { + constexpr uint32_t bufferWidth = 200u; + constexpr uint32_t bufferHeight = 200u; + constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; + constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; + + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 0u, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer1, false)) + return false; + + return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::ALPHA_BLENDING, "MultipleTrianglesScene_AlphaBlending"); + } + + bool DmaOffscreenBufferTests::runBufferReadWriteTest(RendererTestsFramework &testFramework, uint32_t displayIdx, uint32_t bufferWidth, uint32_t bufferHeight, uint32_t fourccFormat, uint32_t bufferUsageFlags, const std::string& screenshotImage) + { + if(!CreateAndMapDmaOffscreenBuffer(testFramework, displayIdx, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadWrite, testBuffer1)) + return false; + + //write color gradient to OB + if(!WriteTestContentToMappedMemory(testBuffer1)) + return false; + + //render triangles scene above (since clearing is disabled) the written color gradient + return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::THREE_TRIANGLES, screenshotImage); + } + + bool DmaOffscreenBufferTests::runDoubleBufferedReadTest(RendererTestsFramework& testFramework) + { + //The test creates a scene and 2 DMA OBs, then keeps alternating the scene between both OBs each frame + //The contents of the DMA OB are checked against a screenshot from 2 frames ago to make sure the content was already rendered + constexpr uint32_t bufferWidth = 200u; + constexpr uint32_t bufferHeight = 200u; + constexpr uint32_t fourccFormat = DefaultFourccBufferFormat; + constexpr uint32_t bufferUsageFlags = DefaultUsageFlags; + const float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel; + + //Create a dummy scene and assign to FB to make sure swap buffers is called every frame + createAndShowScene(testFramework, 0u, MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3{0.f}, bufferWidth, bufferHeight); + + const auto sceneIdProvider = createAndShowScene(testFramework, 0u, MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3{0.f}, bufferWidth, bufferHeight); + + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 0, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer1)) + return false; + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 0, bufferWidth, bufferHeight, fourccFormat, bufferUsageFlags, EMappingBufferAccessRight::ReadOnly, testBuffer2)) + return false; + + testFramework.assignSceneToDisplayBuffer(sceneIdProvider, testBuffer1.displaBufferId); + testFramework.flushRendererAndDoOneLoop(); + + //change scene state and render to OB2 + testFramework.getScenesRegistry().setSceneState(sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(sceneIdProvider).flush(); + testFramework.assignSceneToDisplayBuffer(sceneIdProvider, testBuffer2.displaBufferId); + testFramework.flushRendererAndDoOneLoop(); + + //check result of OB1 from two loops ago + const Image ob1MemImageResult = ReadMappedMemoryContentToImage(testBuffer1); + if(!RendererTestUtils::CompareBitmapToImageInFile(ob1MemImageResult, "MultipleTrianglesScene_ThreeTriangles", expectedPixelError, true)) + return false; + + testFramework.getScenesRegistry().destroyScenes(); + testFramework.flushRendererAndDoOneLoop(); + + //check result of OB2 from two loops ago + const Image ob2MemImageResult = ReadMappedMemoryContentToImage(testBuffer2); + if(!RendererTestUtils::CompareBitmapToImageInFile(ob2MemImageResult, "MultipleTrianglesScene_RenderingOrderChanged", expectedPixelError, true)) + return false; + + return true; + } + + bool DmaOffscreenBufferTests::runTwoDisplaysTest(RendererTestsFramework &testFramework) + { + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 0u, 256u, 256u, DefaultFourccBufferFormat, DefaultUsageFlags, EMappingBufferAccessRight::ReadWrite, testBuffer1)) + return false; + if(!CreateAndMapDmaOffscreenBuffer(testFramework, 1u, 128u, 64u, DefaultFourccBufferFormat, DefaultUsageFlags, EMappingBufferAccessRight::ReadWrite, testBuffer2)) + return false; + + //write color gradient to OBs + if(!WriteTestContentToMappedMemory(testBuffer1)) + return false; + if(!WriteTestContentToMappedMemory(testBuffer2)) + return false; + + if(!renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer1, MultipleTrianglesScene::THREE_TRIANGLES, "DmaOffscreenBufferTest_BufferWrite_256x256")) + return false; + return renderSceneToBufferAndExpectScreenshot(testFramework, testBuffer2, MultipleTrianglesScene::THREE_TRIANGLES, "DmaOffscreenBufferTest_BufferWrite_128x64"); + } +} diff --git a/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.h b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.h new file mode 100644 index 000000000..369fac718 --- /dev/null +++ b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/DmaOffscreenBufferTests.h @@ -0,0 +1,106 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include +#include +#include + +namespace ramses::internal +{ + class DmaOffscreenBufferTests : public IRendererTest + { + public: + explicit DmaOffscreenBufferTests() = default; + + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + enum + { + DmaOffscreenBufferTest_BufferRead, + DmaOffscreenBufferTest_BufferWrite, + DmaOffscreenBufferTest_BufferReadWrite, + DmaOffscreenBufferTest_ReadContentWithAlphaBlending, + + DmaOffscreenBufferTest_Format_ARGB8888, + DmaOffscreenBufferTest_Format_XRGB8888, + DmaOffscreenBufferTest_Format_XBGR8888, + DmaOffscreenBufferTest_Format_BGR888, + + DmaOffscreenBufferTest_UsageFlagRendering, + DmaOffscreenBufferTest_UsageFlagRenderingAndScanOut, + + DmaOffscreenBufferTest_BufferSize_200x200, + DmaOffscreenBufferTest_BufferSize_256x256, + DmaOffscreenBufferTest_BufferSize_128x64, + DmaOffscreenBufferTest_BufferSize_128x256, + + DmaOffscreenBufferTest_TwoBuffers_DoubleBufferedRead, + DmaOffscreenBufferTest_TwoDisplays, + }; + + enum class EMappingBufferAccessRight + { + ReadOnly, + WriteOnly, + ReadWrite + }; + + struct TestBufferInfo + { + uint32_t displayIdx = 0u; + ramses::displayBufferId_t displaBufferId; + int fd = -1; + uint32_t stride = 0u; + void* mappedMemory = nullptr; + EMappingBufferAccessRight accessRights = EMappingBufferAccessRight::ReadOnly; + uint32_t width = 0; + uint32_t height = 0; + uint32_t fourccFormat = 0; + }; + + template + ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth = ramses::internal::IntegrationScene::DefaultViewportWidth, uint32_t vpHeight = ramses::internal::IntegrationScene::DefaultViewportHeight); + template + bool renderSceneToBufferAndExpectScreenshot(RendererTestsFramework& testFramework, const TestBufferInfo& testBuffer, uint32_t sceneState, const std::string& screenshotImage); + + static ramses::internal::Image ReadMappedMemoryContentToImage(const TestBufferInfo& bufferInfo); + static bool WriteTestContentToMappedMemory(const TestBufferInfo& bufferInfo); + + static bool CreateAndMapDmaOffscreenBuffer(RendererTestsFramework& testFramework, uint32_t displayIdx, uint32_t bufferWidth, uint32_t bufferHeight, uint32_t fourccFormat, uint32_t bufferUsageFlags, EMappingBufferAccessRight accessRights, TestBufferInfo& bufferInfo, bool disableClearing = true); + static bool StartSyncBuffer(const TestBufferInfo& bufferInfo); + static bool EndSyncBuffer(const TestBufferInfo& bufferInfo); + + bool runBufferWriteTest(RendererTestsFramework& testFramework); + bool runBufferReadTest(RendererTestsFramework& testFramework); + bool runContentWithAlphaBlendingTest(RendererTestsFramework& testFramework); + bool runBufferReadWriteTest(RendererTestsFramework& testFramework, + uint32_t displayIdx = 0u, + uint32_t bufferWidth = DefaultBufferWidth, + uint32_t bufferHeight = DefaultBufferHeight, + uint32_t fourccFormat = DefaultFourccBufferFormat, + uint32_t bufferUsageFlags = DefaultUsageFlags, + const std::string& screenshotImage = "DmaOffscreenBufferTest_BufferReadWrite"); + bool runDoubleBufferedReadTest(RendererTestsFramework& testFramework); + bool runTwoDisplaysTest(RendererTestsFramework& testFramework); + + static constexpr uint32_t DefaultBufferWidth = 200u; + static constexpr uint32_t DefaultBufferHeight = 200u; + static constexpr uint32_t DefaultUsageFlags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; + static constexpr uint32_t DefaultFourccBufferFormat = DRM_FORMAT_ARGB8888; + + const glm::vec3 m_cameraTranslation{ 0.f, 0.f, 8.f }; + TestBufferInfo testBuffer1; + TestBufferInfo testBuffer2; + }; +} diff --git a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/main.cpp b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/main.cpp similarity index 90% rename from integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/main.cpp rename to tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/main.cpp index fd152b6bc..3272bcab3 100644 --- a/integration/SandwichTests/RendererTests/DmaOffscreenBufferRenderingTests/main.cpp +++ b/tests/integration/renderer-tests/dma-offscreen-buffer-rendering-tests/main.cpp @@ -7,12 +7,12 @@ // ------------------------------------------------------------------------- #include "RendererTestUtils.h" -#include "Utils/StringUtils.h" +#include "internal/Core/Utils/StringUtils.h" #include "DmaOffscreenBufferRenderingTests.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include "ramses-cli.h" -using namespace ramses_internal; +using namespace ramses::internal; using namespace ramses; int main(int argc, const char *argv[]) @@ -57,7 +57,7 @@ int main(int argc, const char *argv[]) if (!renderingTestsSuccess) { - printf("Some rendering tests failed! Look above for more detailed info.\n"); + fmt::print("Some rendering tests failed! Look above for more detailed info.\n"); return 1; } } diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h similarity index 91% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h index 1c1725399..0ea3a62fa 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestMessages.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITINGTESTMESSAGES_H -#define RAMSES_EMBEDDEDCOMPOSITINGTESTMESSAGES_H +#pragma once -namespace ramses_internal +namespace ramses::internal { enum class ETestForkerApplicationMessage { @@ -34,6 +33,7 @@ namespace ramses_internal RenderOneFrame_ToEGLBuffer, RenderOneFrame_ToSharedMemoryBuffer, AttachBuffer, + ReAttachBuffer, DestroyBuffers, SetSurfaceSize, SetTriangleColor, @@ -47,5 +47,3 @@ namespace ramses_internal StartRamsesRendererAndRunRenderingTest, }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp similarity index 87% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp index 2da2fd9d0..5d3770fd8 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.cpp @@ -7,23 +7,23 @@ // ------------------------------------------------------------------------- #include "EmbeddedCompositingTestsFramework.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RamsesRendererImpl.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "impl/RamsesRendererImpl.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include "EmbeddedCompositingTestMessages.h" #include "TestSignalHandler.h" #include "ETriangleColor.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/PlatformTime.h" #include "TestForkingController.h" -#include "Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" -namespace ramses_internal +namespace ramses::internal { const std::string EmbeddedCompositingTestsFramework::TestEmbeddedCompositingDisplayName = "ramses-ec-display"; const std::string EmbeddedCompositingTestsFramework::TestAlternateEmbeddedCompositingDisplayName = "ramses-ec-alternate-display"; - EmbeddedCompositingTestsFramework::EmbeddedCompositingTestsFramework(bool generateScreenshots, TestForkingController& testForkingController, const ramses::RamsesFrameworkConfig& config) + EmbeddedCompositingTestsFramework::EmbeddedCompositingTestsFramework(bool generateScreenshots, TestForkingController& testForkingController, const RamsesFrameworkConfig& config) : RendererTestsFramework(generateScreenshots, config) , m_testForkingController(testForkingController) { @@ -75,63 +75,63 @@ namespace ramses_internal LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::stopTestApplicationAndWaitUntilDisconnected stop confirmation received"); } - void EmbeddedCompositingTestsFramework::waitForContentOnStreamTexture(WaylandIviSurfaceId sourceId, uint32_t displayIdx) + void EmbeddedCompositingTestsFramework::waitForContentOnStreamTexture(WaylandIviSurfaceId iviSurface, uint32_t displayIdx) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(displayIdx); IEmbeddedCompositingManager& embeddedCompositorManager = getEmbeddedCompositorManager(displayIdx); - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForContentOnStreamTexture(): waiting for content on stream source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForContentOnStreamTexture() " << iviSurface); - while (!embeddedCompositor.isContentAvailableForStreamTexture(sourceId)) + while (!embeddedCompositor.isContentAvailableForStreamTexture(iviSurface)) { embeddedCompositorManager.processClientRequests(); } - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForContentOnStreamTexture(): content found on stream source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForContentOnStreamTexture(): content found on " << iviSurface); } - void EmbeddedCompositingTestsFramework::waitForUnavailablilityOfContentOnStreamTexture(WaylandIviSurfaceId sourceId) + void EmbeddedCompositingTestsFramework::waitForUnavailablilityOfContentOnStreamTexture(WaylandIviSurfaceId iviSurface) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); IEmbeddedCompositingManager& embeddedCompositorManager = getEmbeddedCompositorManager(); - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForUnavailablilityOfContentFromStreamTexture(): waiting for unavailability of content on stream source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForUnavailablilityOfContentFromStreamTexture() " << iviSurface); - while (embeddedCompositor.isContentAvailableForStreamTexture(sourceId)) + while (embeddedCompositor.isContentAvailableForStreamTexture(iviSurface)) { embeddedCompositorManager.processClientRequests(); } - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForUnavailablilityOfContentFromStreamTexture(): no content on stream source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForUnavailablilityOfContentFromStreamTexture(): no content on " << iviSurface); } - bool EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedFramesForIviSurface(WaylandIviSurfaceId waylandSurfaceId, uint64_t numberOfComittedBuffers, uint32_t timeoutMilliseconds) + bool EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedFramesForIviSurface(WaylandIviSurfaceId iviSurface, uint64_t numberOfComittedBuffers, uint32_t timeoutMilliseconds) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); IEmbeddedCompositingManager& embeddedCompositingManager = getEmbeddedCompositorManager(); - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedBuffersForIviSurface(): waiting for number of commited buffers for ivi surface " << waylandSurfaceId.getValue() << " reaching " << numberOfComittedBuffers); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedBuffersForIviSurface(): waiting for number of commited buffers for " << iviSurface << " reaching " << numberOfComittedBuffers); const uint64_t startTime = PlatformTime::GetMillisecondsMonotonic(); - while (embeddedCompositor.getNumberOfCommitedFramesForWaylandIviSurfaceSinceBeginningOfTime(waylandSurfaceId) != numberOfComittedBuffers) + while (embeddedCompositor.getNumberOfCommitedFramesForWaylandIviSurfaceSinceBeginningOfTime(iviSurface) != numberOfComittedBuffers) { embeddedCompositingManager.processClientRequests(); - const uint32_t timeElapsed = static_cast(PlatformTime::GetMillisecondsMonotonic() - startTime); + const auto timeElapsed = static_cast(PlatformTime::GetMillisecondsMonotonic() - startTime); if(timeElapsed > timeoutMilliseconds) { LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedFramesForIviSurfaceWithTimeout(): timed out after " << timeElapsed << " ms"); return false; } } - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedBuffersForIviSurface(): number of commited buffers for ivi surface " << waylandSurfaceId.getValue() << " is " << embeddedCompositor.getNumberOfCommitedFramesForWaylandIviSurfaceSinceBeginningOfTime(waylandSurfaceId)); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitUntilNumberOfCommitedBuffersForIviSurface(): number of commited buffers for " << iviSurface << " is " << embeddedCompositor.getNumberOfCommitedFramesForWaylandIviSurfaceSinceBeginningOfTime(iviSurface)); return true; } - std::string EmbeddedCompositingTestsFramework::getTitleOfIviSurface(WaylandIviSurfaceId waylandSurfaceId) + std::string EmbeddedCompositingTestsFramework::getTitleOfIviSurface(WaylandIviSurfaceId iviSurface) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); - return embeddedCompositor.getTitleOfWaylandIviSurface(waylandSurfaceId); + return embeddedCompositor.getTitleOfWaylandIviSurface(iviSurface); } void EmbeddedCompositingTestsFramework::logEmbeddedCompositor(RendererLogContext& logContext) @@ -160,17 +160,16 @@ namespace ramses_internal LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitUntilNumberOfCompositorConnections(): number of compositor connections is " << numberOfConnections); } - void EmbeddedCompositingTestsFramework::waitForBufferAttachedToIviSurface(WaylandIviSurfaceId waylandSurfaceId) + void EmbeddedCompositingTestsFramework::waitForBufferAttachedToIviSurface(WaylandIviSurfaceId iviSurface) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); IEmbeddedCompositingManager& embeddedCompositingManager = getEmbeddedCompositorManager(); LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForBufferAttachedToIviSurface(): waiting for " - "buffer attached to ivi surface " - << waylandSurfaceId.getValue()); + "buffer attached to " << iviSurface); - while (!embeddedCompositor.isBufferAttachedToWaylandIviSurface(waylandSurfaceId)) + while (!embeddedCompositor.isBufferAttachedToWaylandIviSurface(iviSurface)) { embeddedCompositingManager.processClientRequests(); } @@ -178,17 +177,16 @@ namespace ramses_internal "EmbeddedCompositingTestsFramework::waitForBufferAttachedToIviSurface(): buffer is attached"); } - void EmbeddedCompositingTestsFramework::waitForNoBufferAttachedToIviSurface(WaylandIviSurfaceId waylandSurfaceId) + void EmbeddedCompositingTestsFramework::waitForNoBufferAttachedToIviSurface(WaylandIviSurfaceId iviSurface) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); IEmbeddedCompositingManager& embeddedCompositingManager = getEmbeddedCompositorManager(); LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForNoBufferAttachedToIviSurface(): waiting for " - "no buffer attached to ivi surface " - << waylandSurfaceId.getValue()); + "no buffer attached to " << iviSurface); - while (embeddedCompositor.isBufferAttachedToWaylandIviSurface(waylandSurfaceId)) + while (embeddedCompositor.isBufferAttachedToWaylandIviSurface(iviSurface)) { embeddedCompositingManager.processClientRequests(); } @@ -202,37 +200,37 @@ namespace ramses_internal getTestRenderer().doOneLoop(); } - void EmbeddedCompositingTestsFramework::waitForSurfaceAvailableForStreamTexture(WaylandIviSurfaceId sourceId) + void EmbeddedCompositingTestsFramework::waitForSurfaceAvailableForStreamTexture(WaylandIviSurfaceId iviSurface) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceAvailableForStreamTexture(): waiting for surface available for stream source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceAvailableForStreamTexture() " << iviSurface); - while (!embeddedCompositor.hasSurfaceForStreamTexture(sourceId)) + while (!embeddedCompositor.hasSurfaceForStreamTexture(iviSurface)) { getTestRenderer().doOneLoop(); } - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceAvailableForStreamTexture(): surface available for source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceAvailableForStreamTexture(): surface available for " << iviSurface); } - void EmbeddedCompositingTestsFramework::waitForSurfaceUnavailableForStreamTexture(WaylandIviSurfaceId sourceId) + void EmbeddedCompositingTestsFramework::waitForSurfaceUnavailableForStreamTexture(WaylandIviSurfaceId iviSurface) { const IEmbeddedCompositor& embeddedCompositor = getEmbeddedCompositor(); - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceUnavailableForStreamTexture(): waiting for surface unavailable for stream source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceUnavailableForStreamTexture() " << iviSurface); - while (embeddedCompositor.hasSurfaceForStreamTexture(sourceId)) + while (embeddedCompositor.hasSurfaceForStreamTexture(iviSurface)) { getTestRenderer().doOneLoop(); } - LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceUnavailableForStreamTexture(): surface unavailable for source id :" << sourceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "EmbeddedCompositingTestsFramework::waitForSurfaceUnavailableForStreamTexture(): surface unavailable for " << iviSurface); } - bool EmbeddedCompositingTestsFramework::waitForStreamSurfaceAvailabilityChange(WaylandIviSurfaceId streamSource, bool available) + bool EmbeddedCompositingTestsFramework::waitForStreamSurfaceAvailabilityChange(WaylandIviSurfaceId iviSurface, bool available) { - return getTestRenderer().waitForStreamSurfaceAvailabilityChange(ramses::waylandIviSurfaceId_t(streamSource.getValue()), available); + return getTestRenderer().waitForStreamSurfaceAvailabilityChange(waylandIviSurfaceId_t(iviSurface.getValue()), available); } void EmbeddedCompositingTestsFramework::sendStopToTestApplication(uint32_t testAppIdx) @@ -313,10 +311,10 @@ namespace ramses_internal m_testForkingController.sendMessageToTestApplication(bos, testAppIdx); } - void EmbeddedCompositingTestsFramework::sendCreateIVISurfaceToTestApplication(TestApplicationSurfaceId surfaceId, WaylandIviSurfaceId surfaceIviId, uint32_t testAppIdx) + void EmbeddedCompositingTestsFramework::sendCreateIVISurfaceToTestApplication(TestApplicationSurfaceId surfaceId, WaylandIviSurfaceId iviSurface, uint32_t testAppIdx) { BinaryOutputStream bos; - bos << ETestWaylandApplicationMessage::CreateIVISurface << surfaceId.getValue() << surfaceIviId.getValue(); + bos << ETestWaylandApplicationMessage::CreateIVISurface << surfaceId.getValue() << iviSurface.getValue(); m_testForkingController.sendMessageToTestApplication(bos, testAppIdx); } @@ -341,6 +339,13 @@ namespace ramses_internal m_testForkingController.sendMessageToTestApplication(bos, testAppIdx); } + void EmbeddedCompositingTestsFramework::sendReAttachBufferToTestApplication(TestApplicationSurfaceId surfaceId, uint32_t count, uint32_t testAppIdx) + { + BinaryOutputStream bos; + bos << ETestWaylandApplicationMessage::ReAttachBuffer << surfaceId.getValue() << count; + m_testForkingController.sendMessageToTestApplication(bos, testAppIdx); + } + void EmbeddedCompositingTestsFramework::sendDestroyBuffersToTestApplication(uint32_t testAppIdx) { BinaryOutputStream bos; @@ -383,9 +388,9 @@ namespace ramses_internal return getTestRenderer().getEmbeddedCompositorManager(displays[displayIdx].displayId); } - void EmbeddedCompositingTestsFramework::setSurfaceVisibility(WaylandIviSurfaceId surfaceId, bool visibility) + void EmbeddedCompositingTestsFramework::setSurfaceVisibility(WaylandIviSurfaceId iviSurface, bool visibility) { - getTestRenderer().setSurfaceVisibility(surfaceId, visibility); + getTestRenderer().setSurfaceVisibility(iviSurface, visibility); } void EmbeddedCompositingTestsFramework::sendSetSurfaceSizeToTestApplicaton(TestApplicationSurfaceId surfaceId, uint32_t width, uint32_t height, uint32_t testAppIdx) @@ -406,7 +411,7 @@ namespace ramses_internal { const auto& displays = getDisplays(); - const ramses::waylandIviLayerId_t waylandIviLayerId = displays[0].config.getWaylandIviLayerID(); + const waylandIviLayerId_t waylandIviLayerId = displays[0].config.getWaylandIviLayerID(); const uint32_t iviSurfaceOffset = 10u; BinaryOutputStream bos; diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h similarity index 86% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h index b73d6824c..47044ecca 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITINGTESTSFRAMEWORK_H -#define RAMSES_EMBEDDEDCOMPOSITINGTESTSFRAMEWORK_H +#pragma once #include "RendererTestsFramework.h" -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" #include "ETriangleColor.h" #include "TestApplicationSurfaceId.h" #include "TestApplicationShellSurfaceId.h" @@ -19,7 +18,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositor; class IEmbeddedCompositingManager; @@ -36,7 +35,7 @@ namespace ramses_internal class EmbeddedCompositingTestsFramework: public RendererTestsFramework { public: - EmbeddedCompositingTestsFramework(bool generateScreenshots, TestForkingController& testForkingController, const ramses::RamsesFrameworkConfig& config); + EmbeddedCompositingTestsFramework(bool generateScreenshots, TestForkingController& testForkingController, const RamsesFrameworkConfig& config); //control test app lifecycle void startTestApplication(uint32_t testAppIdx = 0u); @@ -46,29 +45,30 @@ namespace ramses_internal void killTestApplication(uint32_t testAppIdx = 0u); //wait for events - void waitForContentOnStreamTexture(WaylandIviSurfaceId sourceId, uint32_t displayIdx = 0u); - void waitForUnavailablilityOfContentOnStreamTexture(WaylandIviSurfaceId sourceId); - void waitForSurfaceAvailableForStreamTexture(WaylandIviSurfaceId sourceId); - void waitForSurfaceUnavailableForStreamTexture(WaylandIviSurfaceId sourceId); - bool waitForStreamSurfaceAvailabilityChange(WaylandIviSurfaceId streamSource, bool available); - bool waitUntilNumberOfCommitedFramesForIviSurface(WaylandIviSurfaceId waylandSurfaceId, uint64_t numberOfComittedBuffers, uint32_t timeoutMilliseconds = std::numeric_limits::max()); - void waitForBufferAttachedToIviSurface(WaylandIviSurfaceId waylandSurfaceId); - void waitForNoBufferAttachedToIviSurface(WaylandIviSurfaceId waylandSurfaceId); + void waitForContentOnStreamTexture(WaylandIviSurfaceId iviSurface, uint32_t displayIdx = 0u); + void waitForUnavailablilityOfContentOnStreamTexture(WaylandIviSurfaceId iviSurface); + void waitForSurfaceAvailableForStreamTexture(WaylandIviSurfaceId iviSurface); + void waitForSurfaceUnavailableForStreamTexture(WaylandIviSurfaceId iviSurface); + bool waitForStreamSurfaceAvailabilityChange(WaylandIviSurfaceId iviSurface, bool available); + bool waitUntilNumberOfCommitedFramesForIviSurface(WaylandIviSurfaceId iviSurface, uint64_t numberOfComittedBuffers, uint32_t timeoutMilliseconds = std::numeric_limits::max()); + void waitForBufferAttachedToIviSurface(WaylandIviSurfaceId iviSurface); + void waitForNoBufferAttachedToIviSurface(WaylandIviSurfaceId iviSurface); void waitUntilNumberOfCompositorConnections(uint32_t numberOfConnections, bool doResourceUpdate = false, uint32_t displayIdx = 0u); //send message to test app TestApplicationSurfaceId sendCreateSurfaceWithEGLContextToTestApplication(uint32_t width, uint32_t height, uint32_t swapInterval, uint32_t testAppIdx = 0u); TestApplicationShellSurfaceId sendCreateShellSurfaceToTestApplication(TestApplicationSurfaceId surfaceId, uint32_t testAppIdx = 0u); - void sendSetShellSurfaceTitleToTestApplication(TestApplicationShellSurfaceId, std::string_view title, uint32_t testAppIdx = 0u); + void sendSetShellSurfaceTitleToTestApplication(TestApplicationShellSurfaceId shellSurfaceId, std::string_view title, uint32_t testAppIdx = 0u); void sendSetShellSurfaceDummyValuesToTestApplication(TestApplicationSurfaceId surfaceId, TestApplicationShellSurfaceId shellSurfaceId, uint32_t testAppIdx = 0u); void sendDestroyShellSurfaceToTestApplication(TestApplicationShellSurfaceId shellSurfaceId, uint32_t testAppIdx = 0u); TestApplicationSurfaceId sendCreateSurfaceWithoutEGLContextToTestApplication(uint32_t width, uint32_t height, uint32_t testAppIdx = 0u); void sendDestroySurfaceToTestApplication(TestApplicationSurfaceId surfaceId, uint32_t testAppIdx = 0u); - void sendCreateIVISurfaceToTestApplication(TestApplicationSurfaceId surfaceId, WaylandIviSurfaceId newSurfaceIviId, uint32_t testAppIdx = 0u); + void sendCreateIVISurfaceToTestApplication(TestApplicationSurfaceId surfaceId, WaylandIviSurfaceId iviSurface, uint32_t testAppIdx = 0u); void sendDestroyIVISurfaceToTestApplication(TestApplicationSurfaceId surfaceId, uint32_t testAppIdx = 0u); void sendRenderOneFrameToEGLBufferToTestApplication(TestApplicationSurfaceId surfaceId, bool waitOnFramecallback = false, uint32_t testAppIdx = 0u); void sendRenderOneFrameToSharedMemoryBufferToTestApplication(TestApplicationSurfaceId surfaceId, bool waitOnFramecallback = false, uint32_t testAppIdx = 0u); void sendAttachBufferToTestApplication(TestApplicationSurfaceId surfaceId, bool commit = false, uint32_t testAppIdx = 0u); + void sendReAttachBufferToTestApplication(TestApplicationSurfaceId surfaceId, uint32_t count, uint32_t testAppIdx = 0u); void sendDestroyBuffersToTestApplication(uint32_t testAppIdx = 0u); void sendRenderOneFrameToTwoSurfacesAndWaitOnFrameCallbackToTestApplication(TestApplicationSurfaceId surfaceId1, TestApplicationSurfaceId surfaceId2, uint32_t testAppIdx = 0u); void sendAdditionalConnectToEmbeddedCompositorToTestApplication(uint32_t testAppIdx = 0u); @@ -85,8 +85,8 @@ namespace ramses_internal //local renderer void renderOneFrame(); - void setSurfaceVisibility(WaylandIviSurfaceId surfaceId, bool visibility); - std::string getTitleOfIviSurface(WaylandIviSurfaceId waylandSurfaceId); + void setSurfaceVisibility(WaylandIviSurfaceId iviSurface, bool visibility); + std::string getTitleOfIviSurface(WaylandIviSurfaceId iviSurface); void logEmbeddedCompositor(RendererLogContext& logContext); const static std::string TestEmbeddedCompositingDisplayName; @@ -104,5 +104,3 @@ namespace ramses_internal TestApplicationShellSurfaceId m_nextShellSurfaceId = TestApplicationShellSurfaceId(0); }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h similarity index 89% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h index e2a40499a..e155b62d7 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/IEmbeddedCompositingTest.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IEMBEDDEDCOMPOSITINGTEST_H -#define RAMSES_IEMBEDDEDCOMPOSITINGTEST_H +#pragma once #include "IRendererTest.h" #include "EmbeddedCompositingTestsFramework.h" -namespace ramses_internal +namespace ramses::internal { class IEmbeddedCompositingTest: public IRendererTest { public: - ~IEmbeddedCompositingTest() override {} + ~IEmbeddedCompositingTest() override = default; virtual void setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) = 0; virtual bool runEmbeddedCompositingTestCase(EmbeddedCompositingTestsFramework& testFramework, const RenderingTestCase& testCase) = 0; @@ -34,5 +33,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/NamedPipe.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/NamedPipe.h similarity index 85% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/NamedPipe.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/NamedPipe.h index eab0a3a99..55d33b0ea 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/NamedPipe.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/NamedPipe.h @@ -6,11 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_NAMEDPIPE_H -#define RAMSES_NAMEDPIPE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include #include @@ -18,12 +16,13 @@ #include #include +#include #include #include #include #include -namespace ramses_internal +namespace ramses::internal { enum EReadFromPipeStatus { @@ -69,6 +68,7 @@ namespace ramses_internal void open() { assert(-1 == m_pipeFileDescriptor); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): false positive m_pipeFileDescriptor = ::open(m_pipeName.c_str(), O_RDWR); assert(-1 != m_pipeFileDescriptor); @@ -82,7 +82,7 @@ namespace ramses_internal { assert(-1 != m_pipeFileDescriptor); ssize_t bytesLeftToWrite = totalBytesToWrite; - const uint8_t* writingLocation = static_cast(sourcePointer); + const auto* writingLocation = static_cast(sourcePointer); while(bytesLeftToWrite > 0) { @@ -119,7 +119,7 @@ namespace ramses_internal bool waitOnData(uint32_t timeoutInMilliSeconds) { - pollfd pfd; + pollfd pfd{}; pfd.fd = m_pipeFileDescriptor; pfd.events = POLLIN; pfd.revents = 0; @@ -131,7 +131,7 @@ namespace ramses_internal EReadFromPipeStatus readExactBytesFromPipe(void *writingLocation, ssize_t totalBytesToRead) const { ssize_t bytesRemaining = totalBytesToRead; - uint8_t* currentWritingLocation = static_cast(writingLocation); + auto* currentWritingLocation = static_cast(writingLocation); while (true) { @@ -150,10 +150,7 @@ namespace ramses_internal { return EReadFromPipeStatus_Success; } - else - { - currentWritingLocation += bytesRead; - } + currentWritingLocation += bytesRead; } else { @@ -164,24 +161,18 @@ namespace ramses_internal const bool isPipeEmpty = (readErrorStatus == EAGAIN || readErrorStatus == EWOULDBLOCK); const bool isInTheMiddleOfReadingOperation = totalBytesToRead != bytesRemaining; - if(isPipeEmpty) + if (isPipeEmpty) { if(isInTheMiddleOfReadingOperation) { continue; } - else - { - LOG_ERROR(CONTEXT_RENDERER, "NamedPipe::ReadExactBytesFromPipe read failed for pipe " << m_pipeName << " bytesRead: " << bytesRead << " with errno: " << readErrorStatus << "!"); - return EReadFromPipeStatus_Empty; - } - - } - else - { LOG_ERROR(CONTEXT_RENDERER, "NamedPipe::ReadExactBytesFromPipe read failed for pipe " << m_pipeName << " bytesRead: " << bytesRead << " with errno: " << readErrorStatus << "!"); - return EReadFromPipeStatus_Failure; + return EReadFromPipeStatus_Empty; + } + LOG_ERROR(CONTEXT_RENDERER, "NamedPipe::ReadExactBytesFromPipe read failed for pipe " << m_pipeName << " bytesRead: " << bytesRead << " with errno: " << readErrorStatus << "!"); + return EReadFromPipeStatus_Failure; } } @@ -196,5 +187,3 @@ namespace ramses_internal }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkerApplication.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkerApplication.cpp similarity index 98% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkerApplication.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkerApplication.cpp index 2e03defa4..03123959c 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkerApplication.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkerApplication.cpp @@ -8,12 +8,12 @@ #include "TestForkerApplication.h" #include "EmbeddedCompositingTestFramework/EmbeddedCompositingTestsFramework.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include "TestWaylandApplication.h" #include "TestSignalHandler.h" #include -namespace ramses_internal +namespace ramses::internal { TestForkerApplication::TestForkerApplication(std::string_view testToForkerPipeName, const std::vector>& testPipeNames) : m_testToForkerPipe(testToForkerPipeName, false) diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkerApplication.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkerApplication.h similarity index 92% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkerApplication.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkerApplication.h index 314cff604..f187013d6 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkerApplication.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkerApplication.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTFORKERAPPLICATION_H -#define RAMSES_TESTFORKERAPPLICATION_H +#pragma once #include "NamedPipe.h" #include #include -namespace ramses_internal +namespace ramses::internal { class TestForkerApplication { @@ -39,5 +38,3 @@ namespace ramses_internal std::vector m_testApplicationInfo; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkingController.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkingController.cpp similarity index 95% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkingController.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkingController.cpp index a5156ada3..9a2e7d74b 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkingController.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkingController.cpp @@ -8,12 +8,14 @@ #include "TestForkingController.h" #include "TestForkerApplication.h" -#include "PlatformAbstraction/PlatformTypes.h" #include "EmbeddedCompositingTestMessages.h" -#include "Utils/RamsesLogger.h" +#include "internal/Core/Utils/RamsesLogger.h" + #include -namespace ramses_internal +#include + +namespace ramses::internal { TestForkingController::TestForkingController() { @@ -30,8 +32,7 @@ namespace ramses_internal TestForkingController::~TestForkingController() { stopForkerApplication(); - pid_t processEndStatus = ::waitpid(m_testForkerApplicationProcessId, nullptr, 0); - UNUSED(processEndStatus); + [[maybe_unused]] pid_t processEndStatus = ::waitpid(m_testForkerApplicationProcessId, nullptr, 0); assert(m_testForkerApplicationProcessId == processEndStatus); } @@ -54,7 +55,7 @@ namespace ramses_internal LOG_INFO(CONTEXT_RENDERER, "TestForkingController::sendMessageToTestApplication :" << testAppIdx); assert(testAppIdx < m_testPipes.size()); - const uint32_t dataSize = static_cast(os.getSize()); + const auto dataSize = static_cast(os.getSize()); if (!m_testPipes[testAppIdx].testToWaylandClientPipe->write(&dataSize, sizeof(dataSize))) { LOG_ERROR(CONTEXT_RENDERER, "TestForkingController::sendMessageToTestApplication failed to write data size to pipe!"); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkingController.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkingController.h similarity index 94% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkingController.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkingController.h index b1198ddec..d42612168 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestForkingController.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestForkingController.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTFORKINGCONTROLLER_H -#define RAMSES_TESTFORKINGCONTROLLER_H +#pragma once #include "NamedPipe.h" #include "EmbeddedCompositingTestMessages.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "Utils/LogMacros.h" -#include "Utils/BinaryOutputStream.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/BinaryOutputStream.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { class TestForkingController { @@ -87,5 +86,3 @@ namespace ramses_internal } } } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestSignalHandler.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestSignalHandler.cpp similarity index 89% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestSignalHandler.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestSignalHandler.cpp index e8720a5c6..d2e6aefa8 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestSignalHandler.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestSignalHandler.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "TestSignalHandler.h" -#include "PlatformAbstraction/PlatformSignal.h" -#include "Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformSignal.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { std::string TestSignalHandler::ProcessName; @@ -26,9 +26,7 @@ namespace ramses_internal PlatformSignal::SetSignalHandler(ESignal::TERM, HandleSignalCallback, true); } - TestSignalHandler::TestSignalHandler() - { - } + TestSignalHandler::TestSignalHandler() = default; void TestSignalHandler::HandleSignalCallback(int32_t signal) { diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestSignalHandler.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestSignalHandler.h similarity index 88% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestSignalHandler.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestSignalHandler.h index 0e8b526ac..6c7768584 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/TestSignalHandler.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/TestSignalHandler.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTSIGNALHANDLER_H -#define RAMSES_TESTSIGNALHANDLER_H +#pragma once #include #include -namespace ramses_internal +namespace ramses::internal { class TestSignalHandler { @@ -26,5 +25,3 @@ namespace ramses_internal static std::string ProcessName; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h similarity index 91% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h index 12bde25b4..0bc207c31 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/EmbeddedCompositingTestFramework/WaylandOutputTestParams.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDOUTPUTTESTPARAMS_H -#define RAMSES_WAYLANDOUTPUTTESTPARAMS_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { struct WaylandOutputTestParams { @@ -42,5 +41,3 @@ namespace ramses_internal int32_t factor = 0; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTests.h similarity index 94% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTests.h index 9ea7e63b0..b51f61271 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTests.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITINGTESTS_H -#define RAMSES_EMBEDDEDCOMPOSITINGTESTS_H +#pragma once #include "EmbeddedCompositingTestsFramework.h" #include "SingleStreamTextureTests.h" @@ -25,16 +24,15 @@ #include #include -namespace ramses_internal +namespace ramses::internal { class TestForkingController; class EmbeddedCompositingTests { public: - EmbeddedCompositingTests(TestForkingController& testForkingController, const std::vector& filterIn, const std::vector& filterOut, bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) + EmbeddedCompositingTests(TestForkingController& testForkingController, const std::vector& filterIn, const std::vector& filterOut, bool generateScreenshots, const RamsesFrameworkConfig& config) : m_testFramework(generateScreenshots, testForkingController, config) - , m_embeddedCompositingTestsWithFD() { m_singleStreamTextureTests.setUpEmbeddedCompositingTestCases(m_testFramework); m_multiStreamTextureTests.setUpEmbeddedCompositingTestCases(m_testFramework); @@ -77,5 +75,3 @@ namespace ramses_internal MultiDisplayStreamTextureTests m_multiDisplayStreamTextureTests; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTestsWithFD.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTestsWithFD.cpp similarity index 93% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTestsWithFD.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTestsWithFD.cpp index ef7866a44..803364590 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTestsWithFD.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTestsWithFD.cpp @@ -8,13 +8,13 @@ #include "EmbeddedCompositingTestsWithFD.h" #include "EmbeddedCompositingTestsFramework.h" -#include "RendererLib/DisplayConfig.h" -#include "DisplayConfigImpl.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "impl/DisplayConfigImpl.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" #include "RendererTestUtils.h" -namespace ramses_internal +namespace ramses::internal { void EmbeddedCompositingTestsWithFD::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -31,8 +31,8 @@ namespace ramses_internal ramses::RendererConfig rendererConfig = RendererTestUtils::CreateTestRendererConfig(); - const uint32_t displayWidth = ramses_internal::IntegrationScene::DefaultViewportWidth; - const uint32_t displayHeight = ramses_internal::IntegrationScene::DefaultViewportHeight; + const uint32_t displayWidth = IntegrationScene::DefaultViewportWidth; + const uint32_t displayHeight = IntegrationScene::DefaultViewportHeight; ramses::DisplayConfig displayConfig = RendererTestUtils::CreateTestDisplayConfig(0, true); displayConfig.setWindowRectangle(0, 0, displayWidth, displayHeight); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTestsWithFD.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTestsWithFD.h similarity index 87% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTestsWithFD.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTestsWithFD.h index a7e9ba367..e30d6d109 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/EmbeddedCompositingTestsWithFD.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/EmbeddedCompositingTestsWithFD.h @@ -6,20 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITINGTESTSWITHFD_H -#define RAMSES_EMBEDDEDCOMPOSITINGTESTSWITHFD_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositingTestsWithFD : public IEmbeddedCompositingTest { public: - explicit EmbeddedCompositingTestsWithFD() - { - } - void setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) final; bool runEmbeddedCompositingTestCase(EmbeddedCompositingTestsFramework& testFramework, const RenderingTestCase& testCase) final; @@ -37,5 +32,3 @@ namespace ramses_internal const std::string m_waylandSocket = EmbeddedCompositingTestsFramework::TestEmbeddedCompositingDisplayName; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiDisplayStreamTextureTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiDisplayStreamTextureTests.cpp similarity index 95% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiDisplayStreamTextureTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiDisplayStreamTextureTests.cpp index 3e2c1a54f..fd191acfc 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiDisplayStreamTextureTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiDisplayStreamTextureTests.cpp @@ -8,11 +8,11 @@ #include "MultiDisplayStreamTextureTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "RendererLib/DisplayConfig.h" +#include "internal/RendererLib/DisplayConfig.h" #include "ETriangleColor.h" -#include "DisplayConfigImpl.h" +#include "impl/DisplayConfigImpl.h" -namespace ramses_internal +namespace ramses::internal { void MultiDisplayStreamTextureTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -58,8 +58,8 @@ namespace ramses_internal const auto sceneId1 = createAndShowScene(testFramework, EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight, 0u); const auto sceneId2 = createAndShowScene(testFramework, EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight, 1u); - const auto streamBufferDisp1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); - const auto streamBufferDisp2 = testFramework.createStreamBuffer(1u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBufferDisp1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBufferDisp2 = testFramework.createStreamBuffer(1u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); testFramework.createBufferDataLink(streamBufferDisp1, sceneId1, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBufferDisp2, sceneId2, EmbeddedCompositorScene::SamplerConsumerId1); @@ -91,8 +91,8 @@ namespace ramses_internal const auto sceneId1 = createAndShowScene(testFramework, EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight, 0u); const auto sceneId2 = createAndShowScene(testFramework, EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight, 1u); - const auto streamBufferDisp1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); - const auto streamBufferDisp2 = testFramework.createStreamBuffer(1u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBufferDisp1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBufferDisp2 = testFramework.createStreamBuffer(1u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); testFramework.createBufferDataLink(streamBufferDisp1, sceneId1, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBufferDisp2, sceneId2, EmbeddedCompositorScene::SamplerConsumerId1); @@ -124,8 +124,8 @@ namespace ramses_internal const auto sceneId1 = createAndShowScene(testFramework, EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight, 0u); const auto sceneId2 = createAndShowScene(testFramework, EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight, 1u); - const auto streamBufferDisp1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); - const auto streamBufferDisp2 = testFramework.createStreamBuffer(1u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBufferDisp1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBufferDisp2 = testFramework.createStreamBuffer(1u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); testFramework.createBufferDataLink(streamBufferDisp1, sceneId1, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBufferDisp2, sceneId2, EmbeddedCompositorScene::SamplerConsumerId1); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiDisplayStreamTextureTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiDisplayStreamTextureTests.h similarity index 76% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiDisplayStreamTextureTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiDisplayStreamTextureTests.h index f4444b5e3..567e32918 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiDisplayStreamTextureTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiDisplayStreamTextureTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTIDISPLAYSTREAMTEXTURETESTS_H -#define RAMSES_MULTIDISPLAYSTREAMTEXTURETESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class MultiDisplayStreamTextureTests : public IEmbeddedCompositingTest { @@ -29,9 +28,9 @@ namespace ramses_internal template - ramses::sceneId_t createAndShowScene(EmbeddedCompositingTestsFramework& testFramework, uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight, uint32_t displayIdx) + sceneId_t createAndShowScene(EmbeddedCompositingTestsFramework& testFramework, uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight, uint32_t displayIdx) { - const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState, { 0, 0, 0 }, vpWidth, vpHeight); + const sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState, { 0, 0, 0 }, vpWidth, vpHeight); testFramework.publishAndFlushScene(sceneId); testFramework.getSceneToRendered(sceneId, displayIdx); return sceneId; @@ -41,5 +40,3 @@ namespace ramses_internal static constexpr uint32_t DisplayHeight = IntegrationScene::DefaultViewportHeight; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiSceneStreamTextureTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiSceneStreamTextureTests.cpp similarity index 96% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiSceneStreamTextureTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiSceneStreamTextureTests.cpp index 0ca263d70..90b7ab54b 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiSceneStreamTextureTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiSceneStreamTextureTests.cpp @@ -8,11 +8,12 @@ #include "MultiSceneStreamTextureTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "RendererLib/DisplayConfig.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/LogMacros.h" #include "ETriangleColor.h" -#include "DisplayConfigImpl.h" +#include "impl/DisplayConfigImpl.h" -namespace ramses_internal +namespace ramses::internal { void MultiSceneStreamTextureTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -32,8 +33,8 @@ namespace ramses_internal constexpr WaylandIviSurfaceId waylandSurfaceIviId1{57u}; constexpr WaylandIviSurfaceId waylandSurfaceIviId2{788u}; - const auto streamBuffer1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); - const auto streamBuffer2 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId2.getValue()}); + const auto streamBuffer1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBuffer2 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId2.getValue()}); switch(testCase.m_id) { diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiSceneStreamTextureTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiSceneStreamTextureTests.h similarity index 91% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiSceneStreamTextureTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiSceneStreamTextureTests.h index 637dacf41..1c8c06c59 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiSceneStreamTextureTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiSceneStreamTextureTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTISCENESTREAMTEXTURETESTS_H -#define RAMSES_MULTISCENESTREAMTEXTURETESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class MultiSceneStreamTextureTests : public IEmbeddedCompositingTest { @@ -31,5 +30,3 @@ namespace ramses_internal static constexpr uint32_t DisplayHeight = IntegrationScene::DefaultViewportHeight; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiStreamTextureTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiStreamTextureTests.cpp similarity index 96% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiStreamTextureTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiStreamTextureTests.cpp index 01d5debb6..ec18bfebe 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiStreamTextureTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiStreamTextureTests.cpp @@ -8,10 +8,11 @@ #include "MultiStreamTextureTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "RendererLib/DisplayConfig.h" -#include "DisplayConfigImpl.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/LogMacros.h" +#include "impl/DisplayConfigImpl.h" -namespace ramses_internal +namespace ramses::internal { void MultiStreamTextureTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -34,9 +35,9 @@ namespace ramses_internal constexpr WaylandIviSurfaceId waylandSurfaceIviId2{7889u}; constexpr WaylandIviSurfaceId waylandSurfaceIviId3{4709u}; - const auto streamBuffer1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); - const auto streamBuffer2 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId2.getValue()}); - const auto streamBuffer3 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId3.getValue()}); + const auto streamBuffer1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBuffer2 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId2.getValue()}); + const auto streamBuffer3 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId3.getValue()}); switch(testCase.m_id) { diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiStreamTextureTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiStreamTextureTests.h similarity index 92% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiStreamTextureTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiStreamTextureTests.h index fd41ad88c..ba49d9f75 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/MultiStreamTextureTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/MultiStreamTextureTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTISTREAMTEXTURETESTS_H -#define RAMSES_MULTISTREAMTEXTURETESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class MultiStreamTextureTests : public IEmbeddedCompositingTest { @@ -33,5 +32,3 @@ namespace ramses_internal static constexpr uint32_t DisplayHeight = IntegrationScene::DefaultViewportHeight; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp similarity index 96% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp index 23cd9e064..3c7c0c056 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/OffscreenBuffersWithStreamTexturesTests.cpp @@ -9,9 +9,9 @@ #include "OffscreenBuffersWithStreamTexturesTests.h" #include "TestScenes/EmbeddedCompositorScene.h" #include "TestScenes/TextureLinkScene.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { void OffscreenBuffersWithStreamTexturesTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -27,7 +27,7 @@ namespace ramses_internal bool testResultValue = true; constexpr WaylandIviSurfaceId waylandSurfaceIviId1{409u}; - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); switch(testCase.m_id) { diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/OffscreenBuffersWithStreamTexturesTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/OffscreenBuffersWithStreamTexturesTests.h similarity index 86% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/OffscreenBuffersWithStreamTexturesTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/OffscreenBuffersWithStreamTexturesTests.h index af58a4ca5..48d8fbdd6 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/OffscreenBuffersWithStreamTexturesTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/OffscreenBuffersWithStreamTexturesTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_OFFSCREENBUFFERSWITHSTREAMTEXTURESTESTS_H -#define RAMSES_OFFSCREENBUFFERSWITHSTREAMTEXTURESTESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class OffscreenBuffersWithStreamTexturesTests : public IEmbeddedCompositingTest { @@ -26,5 +25,3 @@ namespace ramses_internal }; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SharedMemoryBufferTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SharedMemoryBufferTests.cpp similarity index 90% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SharedMemoryBufferTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SharedMemoryBufferTests.cpp index de50bb0d6..7c95a0cd5 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SharedMemoryBufferTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SharedMemoryBufferTests.cpp @@ -6,14 +6,39 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" #include "SharedMemoryBufferTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include +#include -namespace ramses_internal +namespace ramses::internal { + class StreamAvailabilityHandler : public ramses::RendererEventHandlerEmpty, public ramses::RendererSceneControlEventHandlerEmpty + { + public: + void streamAvailabilityChanged(ramses::waylandIviSurfaceId_t streamId, bool available) override + { + ++callbacks; + if (available) + { + LOG_INFO_P(CONTEXT_RENDERER, "stream available: {}", streamId.getValue()); + availableStreams.insert(streamId); + } + else + { + LOG_INFO_P(CONTEXT_RENDERER, "stream unavailable: {}", streamId.getValue()); + availableStreams.erase(streamId); + } + } + + std::unordered_set availableStreams; + int callbacks = 0; + }; + void SharedMemoryBufferTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { ramses::DisplayConfig displayConfig = RendererTestUtils::CreateTestDisplayConfig(0u, true); @@ -24,6 +49,7 @@ namespace ramses_internal testFramework.createTestCase(ShowFallbackTextureWhenBufferIsDetachedFromSurface, *this, "ShowFallbackTextureWhenBufferIsDetachedFromSurface").m_displayConfigs.push_back(displayConfig); testFramework.createTestCase(ShowFallbackTextureWhenBufferIsDetachedFromSurfaceAndLastFrameNotUsedForRendering, *this, "ShowFallbackTextureWhenBufferIsDetachedFromSurfaceAndLastFrameNotUsedForRendering").m_displayConfigs.push_back(displayConfig); testFramework.createTestCase(ClientAttachesAndDestroysBufferWithoutCommit, *this, "ClientAttachesAndDestroysBufferWithoutCommit").m_displayConfigs.push_back(displayConfig); + testFramework.createTestCase(QuickBufferReAttach, *this, "QuickBufferReAttach").m_displayConfigs.push_back(displayConfig); testFramework.createTestCase(SwitchBetweenBufferTypes_ShmThenEgl, *this, "SwitchBetweenBufferTypes_ShmThenEgl").m_displayConfigs.push_back(displayConfig); testFramework.createTestCase(SwitchBetweenBufferTypes_EglThenShmThenEgl, *this, "SwitchBetweenBufferTypes_EglThenShmThenEgl").m_displayConfigs.push_back(displayConfig); testFramework.createTestCase(SwitchBetweenBufferTypes_EglThenShmThenDestroyShmThenEgl, *this, "SwitchBetweenBufferTypes_EglThenShmThenDestroyShmThenEgl").m_displayConfigs.push_back(displayConfig); @@ -49,8 +75,8 @@ namespace ramses_internal constexpr WaylandIviSurfaceId waylandSurfaceIviId1{517u}; constexpr WaylandIviSurfaceId waylandSurfaceIviId2{718u}; - const auto streamBuffer1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); - const auto streamBuffer2 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId2.getValue()}); + const auto streamBuffer1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBuffer2 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId2.getValue()}); switch(testCase.m_id) { @@ -74,14 +100,47 @@ namespace ramses_internal for (uint32_t i = 0; i < cycles; i++) { - testResultValue &= renderAndCheckOneSharedMemoryFrame(testFramework, surfaceId, ETriangleColor::Red, waylandSurfaceIviId1, frameCounter, "EC_ShMemRedTriangle"); - testResultValue &= renderAndCheckOneSharedMemoryFrame(testFramework, surfaceId, ETriangleColor::Blue, waylandSurfaceIviId1, frameCounter, "EC_ShMemBlueTriangle"); - testResultValue &= renderAndCheckOneSharedMemoryFrame(testFramework, surfaceId, ETriangleColor::White, waylandSurfaceIviId1, frameCounter, "EC_ShMemWhiteTriangle"); + testResultValue &= RenderAndCheckOneSharedMemoryFrame(testFramework, surfaceId, ETriangleColor::Red, waylandSurfaceIviId1, frameCounter, "EC_ShMemRedTriangle"); + testResultValue &= RenderAndCheckOneSharedMemoryFrame(testFramework, surfaceId, ETriangleColor::Blue, waylandSurfaceIviId1, frameCounter, "EC_ShMemBlueTriangle"); + testResultValue &= RenderAndCheckOneSharedMemoryFrame(testFramework, surfaceId, ETriangleColor::White, waylandSurfaceIviId1, frameCounter, "EC_ShMemWhiteTriangle"); } testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; } + case QuickBufferReAttach: + { + testFramework.startTestApplicationAndWaitUntilConnected(); + testFramework.createAndShowScene(EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, DisplayWidth, DisplayHeight); + const TestApplicationSurfaceId surfaceId = testFramework.sendCreateSurfaceWithoutEGLContextToTestApplication(255, 255); + StreamAvailabilityHandler handler; + testFramework.sendCreateIVISurfaceToTestApplication(surfaceId, waylandSurfaceIviId1); + testFramework.sendRenderOneFrameToSharedMemoryBufferToTestApplication(surfaceId); + testFramework.waitForContentOnStreamTexture(waylandSurfaceIviId1); + testFramework.renderOneFrame(); + testFramework.dispatchRendererEvents(handler, handler); + if (handler.availableStreams.empty()) + { + LOG_ERROR_P(CONTEXT_RENDERER, "Test surface {} is unavailable", surfaceId.getValue()); + testResultValue = false; + } + else + { + const auto iviSurface = *handler.availableStreams.begin(); + const auto callbacks = handler.callbacks; + testFramework.sendReAttachBufferToTestApplication(surfaceId, 1); + testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId1, 3); + testFramework.renderOneFrame(); + testFramework.dispatchRendererEvents(handler, handler); + if (handler.availableStreams.count(iviSurface) != 1 || handler.callbacks == callbacks) + { + LOG_ERROR_P(CONTEXT_RENDERER, "Stream surface {} is unavailable after reattach", surfaceId.getValue()); + testResultValue = false; + } + } + testFramework.stopTestApplicationAndWaitUntilDisconnected(); + break; + } case ShowFallbackTextureWhenBufferIsDetachedFromSurface: { testFramework.startTestApplicationAndWaitUntilConnected(); @@ -508,7 +567,7 @@ namespace ramses_internal testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId2, 1); testResultValue &= testFramework.renderAndCompareScreenshot("EC_ShMemRedTriangle_Left_ShMemRedTriangle_Right"); // 0 | 0 | - - testResultValue &= checkFreeBufferState(testFramework, "0"); // Buffer 0 not free + testResultValue &= CheckFreeBufferState(testFramework, "0"); // Buffer 0 not free // Render frame to first surface: testFramework.sendSetTriangleColorToTestApplication(ETriangleColor::Blue); @@ -516,7 +575,7 @@ namespace ramses_internal testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId1, 2); testResultValue &= testFramework.renderAndCompareScreenshot("EC_ShMemBlueTriangle_Left_ShMemRedTriangle_Right"); // 1 | 0 | - - testResultValue &= checkFreeBufferState(testFramework, "00"); // Buffer 0 and 1 not free + testResultValue &= CheckFreeBufferState(testFramework, "00"); // Buffer 0 and 1 not free // Render frame to second surface: testFramework.sendSetTriangleColorToTestApplication(ETriangleColor::White); @@ -524,7 +583,7 @@ namespace ramses_internal testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId2, 2); testResultValue &= testFramework.renderAndCompareScreenshot("EC_ShMemBlueTriangle_Left_ShMemWhiteTriangle_Right"); // 1 | 2 | 0 - testResultValue &= checkFreeBufferState(testFramework, "100"); // Buffer 0 free, buffer 1 and 2 not free + testResultValue &= CheckFreeBufferState(testFramework, "100"); // Buffer 0 free, buffer 1 and 2 not free // Render frame to first surface: testFramework.sendSetTriangleColorToTestApplication(ETriangleColor::Grey); @@ -532,21 +591,21 @@ namespace ramses_internal testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId1, 3); testResultValue &= testFramework.renderAndCompareScreenshot("EC_ShMemGreyTriangle_Left_ShMemWhiteTriangle_Right"); // 0 | 2 | 1 - testResultValue &= checkFreeBufferState(testFramework, "010"); // Buffer 0 not free, buffer 1 free, and buffer 2 not free + testResultValue &= CheckFreeBufferState(testFramework, "010"); // Buffer 0 not free, buffer 1 free, and buffer 2 not free // Detach buffer from first surface: testFramework.sendDetachBufferFromSurfaceToTestApplication(surfaceId1); testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId1, 4); testResultValue &= testFramework.renderAndCompareScreenshot("EC_Fallback1_Left_WhiteTriangle_Right"); // - | 2 | 0, 1 - testResultValue &= checkFreeBufferState(testFramework, "110"); // Buffer 0 and 1 free, buffer 2 not free + testResultValue &= CheckFreeBufferState(testFramework, "110"); // Buffer 0 and 1 free, buffer 2 not free // Detach buffer from second surface: testFramework.sendDetachBufferFromSurfaceToTestApplication(surfaceId2); testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId2, 3); testResultValue &= testFramework.renderAndCompareScreenshot("EC_Fallback1_Left_Fallback1_Right"); // - | - | 0, 1, 2 - testResultValue &= checkFreeBufferState(testFramework, "111"); // Bufer 0, 1 and 2 free + testResultValue &= CheckFreeBufferState(testFramework, "111"); // Bufer 0, 1 and 2 free testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; } @@ -560,7 +619,7 @@ namespace ramses_internal return testResultValue; } - bool SharedMemoryBufferTests::renderAndCheckOneSharedMemoryFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, ETriangleColor color, WaylandIviSurfaceId waylandSurfaceIviId, uint32_t& frameCount, const std::string& expectedImageName) + bool SharedMemoryBufferTests::RenderAndCheckOneSharedMemoryFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, ETriangleColor color, WaylandIviSurfaceId waylandSurfaceIviId, uint32_t& frameCount, const std::string& expectedImageName) { testFramework.sendSetTriangleColorToTestApplication(color); testFramework.sendRenderOneFrameToSharedMemoryBufferToTestApplication(testSurfaceId, true); @@ -575,13 +634,13 @@ namespace ramses_internal if (numberOfAllocatedSHMBuffer != expectedNumberOfAllocatedSHMBuffer) { - LOG_ERROR(CONTEXT_RENDERER, "SharedMemoryBufferTests::renderAndCheckOneSharedMemoryFrame Number of allocated SHM buffers " << numberOfAllocatedSHMBuffer << " does not match expected value " << expectedNumberOfAllocatedSHMBuffer <<"!"); + LOG_ERROR(CONTEXT_RENDERER, "SharedMemoryBufferTests::RenderAndCheckOneSharedMemoryFrame Number of allocated SHM buffers " << numberOfAllocatedSHMBuffer << " does not match expected value " << expectedNumberOfAllocatedSHMBuffer <<"!"); return false; } return true; } - bool SharedMemoryBufferTests::checkFreeBufferState(EmbeddedCompositingTestsFramework& testFramework, std::string_view expectedBufferFreeState) + bool SharedMemoryBufferTests::CheckFreeBufferState(EmbeddedCompositingTestsFramework& testFramework, std::string_view expectedBufferFreeState) { std::string bufferFreeState; uint32_t numberOfAllocatedBuffers = testFramework.getNumberOfAllocatedSHMBufferFromTestApplication(); @@ -589,14 +648,18 @@ namespace ramses_internal for (uint32_t i = 0; i < numberOfAllocatedBuffers; i++) { if (testFramework.getIsBufferFreeFromTestApplication(i)) + { bufferFreeState += "1"; + } else + { bufferFreeState += "0"; + } } if (bufferFreeState != expectedBufferFreeState) { - LOG_ERROR(CONTEXT_RENDERER, "SharedMemoryBufferTests::checkFreeBufferState Expected buffer free state: " << expectedBufferFreeState << " does not match actual buffer free state: " << bufferFreeState); + LOG_ERROR(CONTEXT_RENDERER, "SharedMemoryBufferTests::CheckFreeBufferState Expected buffer free state: " << expectedBufferFreeState << " does not match actual buffer free state: " << bufferFreeState); return false; } return true; diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SharedMemoryBufferTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SharedMemoryBufferTests.h similarity index 81% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SharedMemoryBufferTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SharedMemoryBufferTests.h index 9c45b52b8..5d147ab49 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SharedMemoryBufferTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SharedMemoryBufferTests.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHAREDMEMORYBUFFERTESTS_H -#define RAMSES_SHAREDMEMORYBUFFERTESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" #include -namespace ramses_internal +namespace ramses::internal { class SharedMemoryBufferTests : public IEmbeddedCompositingTest { @@ -29,6 +28,7 @@ namespace ramses_internal ShowFallbackTextureWhenBufferIsDetachedFromSurface, ShowFallbackTextureWhenBufferIsDetachedFromSurfaceAndLastFrameNotUsedForRendering, ClientAttachesAndDestroysBufferWithoutCommit, + QuickBufferReAttach, //switchting between EGL and shared memory buffers SwitchBetweenBufferTypes_ShmThenDestroyShmThenEgl, @@ -46,13 +46,11 @@ namespace ramses_internal TestCorrectBufferRelease, }; - bool renderAndCheckOneSharedMemoryFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, ETriangleColor color, WaylandIviSurfaceId waylandSurfaceIviId, uint32_t& frameCount, const std::string& expectedImageName); - bool checkFreeBufferState(EmbeddedCompositingTestsFramework& testFramework, std::string_view bufferFreeState); + static bool RenderAndCheckOneSharedMemoryFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, ETriangleColor color, WaylandIviSurfaceId waylandSurfaceIviId, uint32_t& frameCount, const std::string& expectedImageName); + static bool CheckFreeBufferState(EmbeddedCompositingTestsFramework& testFramework, std::string_view bufferFreeState); static constexpr uint32_t DisplayWidth = IntegrationScene::DefaultViewportWidth; static constexpr uint32_t DisplayWidthTwoStreams = IntegrationScene::DefaultViewportWidth * 2; static constexpr uint32_t DisplayHeight = IntegrationScene::DefaultViewportHeight; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SingleStreamTextureTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SingleStreamTextureTests.cpp similarity index 97% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SingleStreamTextureTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SingleStreamTextureTests.cpp index 03def7f78..32dccb537 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SingleStreamTextureTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SingleStreamTextureTests.cpp @@ -8,18 +8,16 @@ #include "SingleStreamTextureTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "Utils/LogMacros.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" +#include "internal/PlatformAbstraction/PlatformMath.h" #include "wayland-server-protocol.h" #include -namespace ramses_internal +namespace ramses::internal { - SingleStreamTextureTests::SingleStreamTextureTests() - { - } + SingleStreamTextureTests::SingleStreamTextureTests() = default; void SingleStreamTextureTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -65,7 +63,7 @@ namespace ramses_internal constexpr WaylandIviSurfaceId waylandSurfaceIviId1{574u}; constexpr WaylandIviSurfaceId waylandSurfaceIviId2{7889u}; - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); switch(testCase.m_id) { @@ -197,8 +195,8 @@ namespace ramses_internal testResultValue &= testFramework.waitUntilNumberOfCommitedFramesForIviSurface(WaylandIviSurfaceId(waylandSurfaceIviId1.getValue()), 1); testFramework.renderOneFrame(); - testResultValue &= resizeSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 8, 8, waylandSurfaceIviId1, 2, "EC_RedTriangleStreamTexture_SmallRes"); - testResultValue &= resizeSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 384, 384, waylandSurfaceIviId1, 3, "EC_RedTriangleStreamTexture"); + testResultValue &= ResizeSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 8, 8, waylandSurfaceIviId1, 2, "EC_RedTriangleStreamTexture_SmallRes"); + testResultValue &= ResizeSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 384, 384, waylandSurfaceIviId1, 3, "EC_RedTriangleStreamTexture"); testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; @@ -214,8 +212,8 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(waylandSurfaceIviId1); - testResultValue &= recreateSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 8, 8, waylandSurfaceIviId1, "EC_RedTriangleStreamTexture_SmallRes"); - testResultValue &= recreateSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 384, 384, waylandSurfaceIviId1, "EC_RedTriangleStreamTexture"); + testResultValue &= RecreateSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 8, 8, waylandSurfaceIviId1, "EC_RedTriangleStreamTexture_SmallRes"); + testResultValue &= RecreateSurfaceRenderAndCheckOneFrame(testFramework, surfaceId, 384, 384, waylandSurfaceIviId1, "EC_RedTriangleStreamTexture"); testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; @@ -278,7 +276,7 @@ namespace ramses_internal "EC_GrayTriangleStreamTexture", "EC_WhiteTriangleStreamTexture"}; - testResultValue = renderFramesOnTestApplicationAndTakeScreenshots(testFramework, surfaceId, waylandSurfaceIviId1, 30u, colors, screenshotFiles, 4u); + testResultValue = RenderFramesOnTestApplicationAndTakeScreenshots(testFramework, surfaceId, waylandSurfaceIviId1, 30u, colors, screenshotFiles, 4u); testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; @@ -479,7 +477,7 @@ namespace ramses_internal testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId1, 1); testFramework.renderOneFrame(); - if (testFramework.getTitleOfIviSurface(waylandSurfaceIviId1) != "") + if (!testFramework.getTitleOfIviSurface(waylandSurfaceIviId1).empty()) { LOG_ERROR(CONTEXT_RENDERER, "SingleStreamTextureTests::runEmbeddedCompositingTestCase Title is not empty!"); @@ -680,7 +678,7 @@ namespace ramses_internal testFramework.stopTestApplicationAndWaitUntilDisconnected(); - if (testFramework.getTitleOfIviSurface(waylandSurfaceIviId1) != "") + if (!testFramework.getTitleOfIviSurface(waylandSurfaceIviId1).empty()) { LOG_ERROR(CONTEXT_RENDERER, "SingleStreamTextureTests::runEmbeddedCompositingTestCase Title is not empty!"); @@ -707,12 +705,12 @@ namespace ramses_internal const std::string outputString = context.getStream().data(); std::stringstream expectedLogBuf; - expectedLogBuf << "1 connected wayland client(s)\n" - << " [ivi-surface-id: " << waylandSurfaceIviId1.getValue() << "; title: \"TestWaylandApplication\"]\n"; + expectedLogBuf << "1 wayland surface(s)\n" + << " ivi-surface:" << waylandSurfaceIviId1.getValue() << "; title: \"TestWaylandApplication\";"; - if (outputString != expectedLogBuf.str()) + if (outputString.rfind(expectedLogBuf.str(), 0) != 0) { - LOG_ERROR(CONTEXT_RENDERER, "SingleStreamTextureTests::runEmbeddedCompositingTestCase Wrong log output: " << outputString); + LOG_ERROR(CONTEXT_RENDERER, "SingleStreamTextureTests::runEmbeddedCompositingTestCase Wrong log output: " << outputString.c_str()); testResultValue = false; } @@ -729,7 +727,7 @@ namespace ramses_internal return testResultValue; } - bool SingleStreamTextureTests::recreateSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, + bool SingleStreamTextureTests::RecreateSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId& testSurfaceIdOut, uint32_t surfaceWidth, uint32_t surfaceHeight, @@ -748,7 +746,7 @@ namespace ramses_internal return testFramework.renderAndCompareScreenshot(expectedImageName); } - bool SingleStreamTextureTests::resizeSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, + bool SingleStreamTextureTests::ResizeSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, uint32_t surfaceWidth, uint32_t surfaceHeight, @@ -762,7 +760,7 @@ namespace ramses_internal return frameCountReached && testFramework.renderAndCompareScreenshot(expectedImageName); } - bool SingleStreamTextureTests::renderFramesOnTestApplicationAndTakeScreenshots(EmbeddedCompositingTestsFramework& testFramework, + bool SingleStreamTextureTests::RenderFramesOnTestApplicationAndTakeScreenshots(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, WaylandIviSurfaceId waylandSurfaceIviId, const uint32_t frameCountToRender, @@ -783,12 +781,12 @@ namespace ramses_internal { testResult &= testFramework.waitUntilNumberOfCommitedFramesForIviSurface(waylandSurfaceIviId, frameIndex + 1); - LOG_INFO(CONTEXT_RENDERER, "SingleStreamTextureTests::renderFramesOnTestApplicationAndTakeScreenshots(): taking screenshot for frame with index: " << frameIndex); + LOG_INFO(CONTEXT_RENDERER, "SingleStreamTextureTests::RenderFramesOnTestApplicationAndTakeScreenshots(): taking screenshot for frame with index: " << frameIndex); const std::string& screenshotName = screenshotFiles[frameIndex % triangleColorCount]; if(!testFramework.renderAndCompareScreenshot(screenshotName)) { - LOG_ERROR(CONTEXT_RENDERER, "SingleStreamTextureTests::renderFramesOnTestApplicationAndTakeScreenshots(): test failed for screenshot for frame with index: " << frameIndex); + LOG_ERROR(CONTEXT_RENDERER, "SingleStreamTextureTests::RenderFramesOnTestApplicationAndTakeScreenshots(): test failed for screenshot for frame with index: " << frameIndex); testResult = false; } } diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SingleStreamTextureTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SingleStreamTextureTests.h similarity index 71% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SingleStreamTextureTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SingleStreamTextureTests.h index c70950d6c..9a843b97a 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/SingleStreamTextureTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/SingleStreamTextureTests.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SINGLESTREAMTEXTURETESTS_H -#define RAMSES_SINGLESTREAMTEXTURETESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" #include -namespace ramses_internal +namespace ramses::internal { class SingleStreamTextureTests : public IEmbeddedCompositingTest { @@ -57,10 +56,8 @@ namespace ramses_internal CompositorLogsInfo, }; - bool recreateSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId& testSurfaceIdOut, uint32_t surfaceWidth, uint32_t surfaceHeight, WaylandIviSurfaceId waylandSurfaceIviId, const std::string& expectedImageName); - bool resizeSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, uint32_t surfaceWidth, uint32_t surfaceHeight, WaylandIviSurfaceId waylandSurfaceIviId, uint32_t frameCount, const std::string& expectedImageName); - bool renderFramesOnTestApplicationAndTakeScreenshots(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, WaylandIviSurfaceId waylandSurfaceIviId, const uint32_t frameCountToRender, const ETriangleColor triangleColors[], const std::string screenshotFiles[], const uint32_t triangleColorCount); + static bool RecreateSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId& testSurfaceIdOut, uint32_t surfaceWidth, uint32_t surfaceHeight, WaylandIviSurfaceId waylandSurfaceIviId, const std::string& expectedImageName); + static bool ResizeSurfaceRenderAndCheckOneFrame(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, uint32_t surfaceWidth, uint32_t surfaceHeight, WaylandIviSurfaceId waylandSurfaceIviId, uint32_t frameCount, const std::string& expectedImageName); + static bool RenderFramesOnTestApplicationAndTakeScreenshots(EmbeddedCompositingTestsFramework& testFramework, TestApplicationSurfaceId testSurfaceId, WaylandIviSurfaceId waylandSurfaceIviId, const uint32_t frameCountToRender, const ETriangleColor triangleColors[], const std::string screenshotFiles[], const uint32_t triangleColorCount); }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamBufferTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamBufferTests.cpp similarity index 94% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamBufferTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamBufferTests.cpp index 7c1c00f4f..35140ea9d 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamBufferTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamBufferTests.cpp @@ -8,10 +8,10 @@ #include "StreamBufferTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "RendererLib/DisplayConfig.h" -#include "Utils/LogMacros.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { void StreamBufferTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -48,7 +48,7 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId); - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId2); @@ -67,7 +67,7 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId); - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); testFramework.createBufferDataLink(streamBuffer, sceneId1, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer, sceneId2, EmbeddedCompositorScene::SamplerConsumerId1); @@ -85,8 +85,8 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId); - const auto streamBuffer1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); - const auto streamBuffer2 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer2 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); testFramework.createBufferDataLink(streamBuffer1, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer2, sceneId, EmbeddedCompositorScene::SamplerConsumerId2); @@ -110,8 +110,8 @@ namespace ramses_internal testFramework.waitForContentOnStreamTexture(streamBufferSourceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId2); - const auto streamBuffer1 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); - const auto streamBuffer2 = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId2.getValue() }); + const auto streamBuffer1 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer2 = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId2.getValue() }); testFramework.createBufferDataLink(streamBuffer1, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer2, sceneId, EmbeddedCompositorScene::SamplerConsumerId2); @@ -129,7 +129,7 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId); - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId2); testResultValue &= testFramework.renderAndCompareScreenshot("EC_RedTriangle_Left_RedTriangle_Right"); @@ -151,7 +151,7 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId); - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId2); testResultValue &= testFramework.renderAndCompareScreenshot("EC_RedTriangle_Left_RedTriangle_Right"); @@ -172,7 +172,7 @@ namespace ramses_internal testFramework.sendRenderOneFrameToEGLBufferToTestApplication(surfaceId); testFramework.waitForContentOnStreamTexture(streamBufferSourceId); - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ streamBufferSourceId.getValue() }); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId2); testResultValue &= testFramework.renderAndCompareScreenshot("EC_RedTriangle_Left_RedTriangle_Right"); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamBufferTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamBufferTests.h similarity index 93% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamBufferTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamBufferTests.h index a91a2db28..bcdbeeb0a 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamBufferTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamBufferTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STREAMBUFFERTESTS_H -#define RAMSES_STREAMBUFFERTESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class StreamBufferTests : public IEmbeddedCompositingTest { @@ -35,5 +34,3 @@ namespace ramses_internal static constexpr uint32_t DisplayHeight = IntegrationScene::DefaultViewportHeight; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamTextureRendererEventTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamTextureRendererEventTests.cpp similarity index 97% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamTextureRendererEventTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamTextureRendererEventTests.cpp index 7d2add635..00f0e264e 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamTextureRendererEventTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamTextureRendererEventTests.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" #include "StreamTextureRendererEventTests.h" #include "TestScenes/EmbeddedCompositorScene.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { void StreamTextureRendererEventTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -265,12 +265,12 @@ namespace ramses_internal bool StreamTextureRendererEventTests::renderAndExpectNoStreamSurfaceAvailabilityChanged(EmbeddedCompositingTestsFramework& testFramework, WaylandIviSurfaceId streamSourceId) { - return 0u == renderAndGetSurfaceAvailabilityChangeEvents(testFramework, streamSourceId).size(); + return renderAndGetSurfaceAvailabilityChangeEvents(testFramework, streamSourceId).empty(); } BoolVector StreamTextureRendererEventTests::renderAndGetSurfaceAvailabilityChangeEvents(EmbeddedCompositingTestsFramework& testFramework, WaylandIviSurfaceId streamSourceId) { - class StreamAvailabilityChangeEventHandler : public ramses::RendererSceneControlEventHandlerEmpty + class StreamAvailabilityChangeEventHandler : public RendererSceneControlEventHandlerEmpty { public: explicit StreamAvailabilityChangeEventHandler(WaylandIviSurfaceId targetStreamSourceId) @@ -278,7 +278,7 @@ namespace ramses_internal { } - void streamAvailabilityChanged(ramses::waylandIviSurfaceId_t eventStreamSourceId, bool state) override + void streamAvailabilityChanged(waylandIviSurfaceId_t eventStreamSourceId, bool state) override { if(eventStreamSourceId.getValue() == m_targetStreamSourceId.getValue()) { @@ -298,7 +298,7 @@ namespace ramses_internal testFramework.renderOneFrame(); - ramses::RendererEventHandlerEmpty dummy; + RendererEventHandlerEmpty dummy; StreamAvailabilityChangeEventHandler handler(streamSourceId); testFramework.dispatchRendererEvents(dummy, handler); return handler.getStreamAvailabilityChanged(); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamTextureRendererEventTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamTextureRendererEventTests.h similarity index 93% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamTextureRendererEventTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamTextureRendererEventTests.h index 7918f123e..686387589 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/StreamTextureRendererEventTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/StreamTextureRendererEventTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STREAMTEXTURERENDEREREVENTTESTS_H -#define RAMSES_STREAMTEXTURERENDEREREVENTTESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class StreamTextureRendererEventTests : public IEmbeddedCompositingTest { @@ -40,5 +39,3 @@ namespace ramses_internal BoolVector renderAndGetSurfaceAvailabilityChangeEvents(EmbeddedCompositingTestsFramework& testFramework, WaylandIviSurfaceId streamSourceId); }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandApplicationWithRamsesRendererTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandApplicationWithRamsesRendererTests.cpp similarity index 96% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandApplicationWithRamsesRendererTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandApplicationWithRamsesRendererTests.cpp index 46fd5d6a3..4b169ff9a 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandApplicationWithRamsesRendererTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandApplicationWithRamsesRendererTests.cpp @@ -9,9 +9,9 @@ #include "WaylandApplicationWithRamsesRendererTests.h" #include "TestScenes/EmbeddedCompositorScene.h" #include "TestScenes/TextureLinkScene.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { void WaylandApplicationWithRamsesRendererTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -32,7 +32,7 @@ namespace ramses_internal { constexpr WaylandIviSurfaceId waylandSurfaceIviId1{157u}; const auto sceneId = testFramework.createAndShowScene(EmbeddedCompositorScene::SINGLE_STREAM_TEXTURE, IntegrationScene::DefaultViewportWidth, IntegrationScene::DefaultViewportHeight); - const auto streamBuffer = testFramework.createStreamBuffer(0u, ramses::waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); + const auto streamBuffer = testFramework.createStreamBuffer(0u, waylandIviSurfaceId_t{ waylandSurfaceIviId1.getValue()}); testFramework.createBufferDataLink(streamBuffer, sceneId, EmbeddedCompositorScene::SamplerConsumerId1); //start test application diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandApplicationWithRamsesRendererTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandApplicationWithRamsesRendererTests.h similarity index 86% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandApplicationWithRamsesRendererTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandApplicationWithRamsesRendererTests.h index 2ad7f0661..bcf97261e 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandApplicationWithRamsesRendererTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandApplicationWithRamsesRendererTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDAPPLICATIONWITHRAMSESRENDERERTESTS_H -#define RAMSES_WAYLANDAPPLICATIONWITHRAMSESRENDERERTESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class WaylandApplicationWithRamsesRendererTests : public IEmbeddedCompositingTest { @@ -26,5 +25,3 @@ namespace ramses_internal }; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandOutputTests.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandOutputTests.cpp similarity index 84% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandOutputTests.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandOutputTests.cpp index 968626997..24a177064 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandOutputTests.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandOutputTests.cpp @@ -9,16 +9,14 @@ #include "WaylandOutputTests.h" #include "TestScenes/EmbeddedCompositorScene.h" #include "WaylandOutputTestParams.h" -#include "Utils/LogMacros.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererAPI/IEmbeddedCompositor.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" #include "wayland-server-protocol.h" -namespace ramses_internal +namespace ramses::internal { - WaylandOutputTests::WaylandOutputTests() - { - } + WaylandOutputTests::WaylandOutputTests() = default; void WaylandOutputTests::setUpEmbeddedCompositingTestCases(EmbeddedCompositingTestsFramework& testFramework) { @@ -52,7 +50,7 @@ namespace ramses_internal WaylandOutputTestParams resultWaylandOutputParams = {}; const bool errorsFoundInWaylandOutput = testFramework.getWaylandOutputParamsFromTestApplication(resultWaylandOutputParams); testResultValue = !errorsFoundInWaylandOutput; - testResultValue &= checkWaylandOutputParams(resultWaylandOutputParams, displayWidth, displayHeight, true, false, false); + testResultValue &= CheckWaylandOutputParams(resultWaylandOutputParams, displayWidth, displayHeight, true, false, false); testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; @@ -76,7 +74,7 @@ namespace ramses_internal WaylandOutputTestParams resultWaylandOutputParams = {}; const bool errorsFoundInWaylandOutput = testFramework.getWaylandOutputParamsFromTestApplication(resultWaylandOutputParams); testResultValue = !errorsFoundInWaylandOutput; - testResultValue &= checkWaylandOutputParams(resultWaylandOutputParams, displayWidth, displayHeight, true, true, true); + testResultValue &= CheckWaylandOutputParams(resultWaylandOutputParams, displayWidth, displayHeight, true, true, true); testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; @@ -100,7 +98,7 @@ namespace ramses_internal WaylandOutputTestParams resultWaylandOutputParams = {}; const bool errorsFoundInWaylandOutput = testFramework.getWaylandOutputParamsFromTestApplication(resultWaylandOutputParams); testResultValue = !errorsFoundInWaylandOutput; - testResultValue &= checkWaylandOutputParams(resultWaylandOutputParams, displayWidth, displayHeight, true, true, true); + testResultValue &= CheckWaylandOutputParams(resultWaylandOutputParams, displayWidth, displayHeight, true, true, true); testFramework.stopTestApplicationAndWaitUntilDisconnected(); break; @@ -113,7 +111,7 @@ namespace ramses_internal return testResultValue; } - bool WaylandOutputTests::checkWaylandOutputParams(const WaylandOutputTestParams& waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight, bool expectMode, bool expectScale, bool expectDone) + bool WaylandOutputTests::CheckWaylandOutputParams(const WaylandOutputTestParams& waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight, bool expectMode, bool expectScale, bool expectDone) { bool success = true; @@ -124,44 +122,44 @@ namespace ramses_internal if(!geometryActuallyReceived) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: geometry was not received!"); + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: geometry was not received!"); success = false; } if(modeActuallyReceived != expectMode) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: expected mode received :" << expectMode + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: expected mode received :" << expectMode << ", actual mode received :" << modeActuallyReceived); success = false; } if(scaleActuallyReceived != expectScale) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: expected scale received :" << expectScale + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: expected scale received :" << expectScale << ", actual scale received :" << scaleActuallyReceived); success = false; } if(doneActuallyReceived != expectDone) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: expected done received :" << expectDone + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: expected done received :" << expectDone << ", actual done received :" << doneActuallyReceived); success = false; } - if(!checkWaylandOutputGeometry(waylandOutputParams)) + if(!CheckWaylandOutputGeometry(waylandOutputParams)) success = false; - if(expectMode && !checkWaylandOutputMode(waylandOutputParams, expectedWidth, expectedHeight)) + if(expectMode && !CheckWaylandOutputMode(waylandOutputParams, expectedWidth, expectedHeight)) success = false; - if(expectScale && !checkWaylandOutputScale(waylandOutputParams)) + if(expectScale && !CheckWaylandOutputScale(waylandOutputParams)) success = false; return success; } - bool WaylandOutputTests::checkWaylandOutputGeometry(const WaylandOutputTestParams& waylandOutputParams) + bool WaylandOutputTests::CheckWaylandOutputGeometry(const WaylandOutputTestParams& waylandOutputParams) { const int32_t xExpected = 0; @@ -175,7 +173,7 @@ namespace ramses_internal || waylandOutputParams.physicalWidth != physicalWidthExpected || waylandOutputParams.physicalHeight != physicalHeightExpected || waylandOutputParams.subpixel != subpixelExpected || waylandOutputParams.transform != transformExpected) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: wrong values received" + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: wrong values received" << ": x = " << waylandOutputParams.x << " (expected = " << xExpected << ")" << ", y = " << waylandOutputParams.y << " (expected = " << yExpected << ")" << ", physical_width = " << waylandOutputParams.physicalWidth << " (expected = " << physicalWidthExpected << ")" @@ -190,7 +188,7 @@ namespace ramses_internal return true; } - bool WaylandOutputTests::checkWaylandOutputMode(const WaylandOutputTestParams &waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight) + bool WaylandOutputTests::CheckWaylandOutputMode(const WaylandOutputTestParams &waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight) { const uint32_t flagsExpected = WL_OUTPUT_MODE_CURRENT; const int32_t refreshExpected= 0; @@ -200,7 +198,7 @@ namespace ramses_internal || waylandOutputParams.height != static_cast(expectedHeight) || waylandOutputParams.refreshRate != refreshExpected) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: wrong values received" + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: wrong values received" << ": flags = " << waylandOutputParams.modeFlags << " (expected = " << flagsExpected << ")" << ": width = " << waylandOutputParams.width << " (expected = " << expectedWidth << ")" << ": height = " << waylandOutputParams.height << " (expected = " << expectedHeight << ")" @@ -213,13 +211,13 @@ namespace ramses_internal return true; } - bool WaylandOutputTests::checkWaylandOutputScale(const WaylandOutputTestParams &waylandOutputParams) + bool WaylandOutputTests::CheckWaylandOutputScale(const WaylandOutputTestParams &waylandOutputParams) { const int32_t factorExpected = 1; if(waylandOutputParams.factor != factorExpected) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandOutputTests::checkWaylandOutputParams: wrong values received" + LOG_ERROR(CONTEXT_RENDERER, "WaylandOutputTests::CheckWaylandOutputParams: wrong values received" << ": factor = " << waylandOutputParams.factor << " (expected = " << factorExpected << ")" ); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandOutputTests.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandOutputTests.h similarity index 62% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandOutputTests.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandOutputTests.h index 530ab21e8..e37255b03 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/WaylandOutputTests.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/WaylandOutputTests.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDOUTPUTTESTS_H -#define RAMSES_WAYLANDOUTPUTTESTS_H +#pragma once #include "IEmbeddedCompositingTest.h" -namespace ramses_internal +namespace ramses::internal { class WaylandOutputTests : public IEmbeddedCompositingTest { @@ -28,11 +27,9 @@ namespace ramses_internal WaylandOutput_Version3, }; - bool checkWaylandOutputParams(const WaylandOutputTestParams& waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight, bool expectMode, bool expectScale, bool expectDone); - bool checkWaylandOutputGeometry(const WaylandOutputTestParams& waylandOutputParams); - bool checkWaylandOutputMode(const WaylandOutputTestParams& waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight); - bool checkWaylandOutputScale(const WaylandOutputTestParams& waylandOutputParams); + static bool CheckWaylandOutputParams(const WaylandOutputTestParams& waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight, bool expectMode, bool expectScale, bool expectDone); + static bool CheckWaylandOutputGeometry(const WaylandOutputTestParams& waylandOutputParams); + static bool CheckWaylandOutputMode(const WaylandOutputTestParams& waylandOutputParams, uint32_t expectedWidth, uint32_t expectedHeight); + static bool CheckWaylandOutputScale(const WaylandOutputTestParams& waylandOutputParams); }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/main.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/main.cpp similarity index 70% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/main.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/main.cpp index ee4fb276b..3cab1a3a6 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestCases/main.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestCases/main.cpp @@ -7,11 +7,11 @@ // ------------------------------------------------------------------------- #include "RendererTestUtils.h" -#include "Utils/StringUtils.h" +#include "internal/Core/Utils/StringUtils.h" #include "EmbeddedCompositingTests.h" -#include "Utils/RamsesLogger.h" +#include "internal/Core/Utils/RamsesLogger.h" #include "EmbeddedCompositingTestFramework/TestForkingController.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include #include #include "ramses-cli.h" @@ -48,21 +48,21 @@ int main(int argc, const char *argv[]) // It is not allowed to call fork after DLT_REGISTER_APP. // For the compositing tests, we don't need DLT at all, so just disable DLT. - ramses_internal::RamsesLoggerConfig loggerConfig; - ramses_internal::GetRamsesLogger().initialize(loggerConfig, true, true); + ramses::internal::RamsesLoggerConfig loggerConfig; + ramses::internal::GetRamsesLogger().initialize(loggerConfig, true, true); //The creation of the forking controller MUST happen before doing anything!!! //Do not move this from here, and do not do anything meaningful before it!!! - ramses_internal::TestForkingController forkingController; + ramses::internal::TestForkingController forkingController; - RendererTestUtils::SetDefaultConfigForAllTests(rendererConfig, displayConfig); + ramses::internal::RendererTestUtils::SetDefaultConfigForAllTests(rendererConfig, displayConfig); - auto filterInTestStrings = ramses_internal::StringUtils::Tokenize(filterIn, ':'); - auto filterOutTestStrings = ramses_internal::StringUtils::Tokenize(filterOut, ':'); + auto filterInTestStrings = ramses::internal::StringUtils::Tokenize(filterIn, ':'); + auto filterOutTestStrings = ramses::internal::StringUtils::Tokenize(filterOut, ':'); - RendererTestUtils::SetMaxFrameCallbackPollingTimeForAllTests(std::chrono::microseconds{10000000}); + ramses::internal::RendererTestUtils::SetMaxFrameCallbackPollingTimeForAllTests(std::chrono::microseconds{10000000}); - ramses_internal::EmbeddedCompositingTests embeddedCompositingTests(forkingController, filterInTestStrings, filterOutTestStrings, generateBitmaps, config); + ramses::internal::EmbeddedCompositingTests embeddedCompositingTests(forkingController, filterInTestStrings, filterOutTestStrings, generateBitmaps, config); for (uint32_t i = 0; i < repeatCount; ++i) { diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/ETriangleColor.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/ETriangleColor.h similarity index 86% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/ETriangleColor.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/ETriangleColor.h index 171fa971b..2838af0b9 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/ETriangleColor.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/ETriangleColor.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ETRIANGLECOLOR_H -#define RAMSES_ETRIANGLECOLOR_H +#pragma once -namespace ramses_internal +namespace ramses::internal { enum class ETriangleColor { @@ -21,5 +20,3 @@ namespace ramses_internal White, }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/OpenGLTriangleDrawer.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/OpenGLTriangleDrawer.cpp similarity index 94% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/OpenGLTriangleDrawer.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/OpenGLTriangleDrawer.cpp index ef182934d..ab5245b78 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/OpenGLTriangleDrawer.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/OpenGLTriangleDrawer.cpp @@ -9,7 +9,7 @@ #include "OpenGLTriangleDrawer.h" #include -namespace ramses_internal +namespace ramses::internal { OpenGLTriangleDrawer::OpenGLTriangleDrawer(ETriangleColor triangleColor) { @@ -25,8 +25,8 @@ namespace ramses_internal glGenBuffers(1, &m_indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer); - unsigned short indices[] = { 0,1,2}; - glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3*sizeof(unsigned short), indices, GL_STATIC_DRAW); + uint16_t indices[] = { 0,1,2}; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3*sizeof(uint16_t), indices, GL_STATIC_DRAW); const GLuint vertexAttributeLocation = glGetAttribLocation(m_shaderProgramReference, "Vertex"); glEnableVertexAttribArray(vertexAttributeLocation); @@ -86,13 +86,13 @@ namespace ramses_internal m_shaderProgramReference = glCreateProgram(); - GLuint vertexShaderReferemce; + GLuint vertexShaderReferemce = 0; vertexShaderReferemce = glCreateShader(GL_VERTEX_SHADER); glAttachShader(m_shaderProgramReference, vertexShaderReferemce); glShaderSource(vertexShaderReferemce, 1, static_cast(&vertexShaderText), nullptr); glCompileShader(vertexShaderReferemce); - GLuint fragmentShaderReference; + GLuint fragmentShaderReference = 0; fragmentShaderReference = glCreateShader(GL_FRAGMENT_SHADER); glAttachShader(m_shaderProgramReference, fragmentShaderReference); glShaderSource(fragmentShaderReference, 1, static_cast(&fragmentShaderText), nullptr); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/OpenGLTriangleDrawer.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/OpenGLTriangleDrawer.h similarity index 78% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/OpenGLTriangleDrawer.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/OpenGLTriangleDrawer.h index 2e1c76a7c..5d5ddfc36 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/OpenGLTriangleDrawer.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/OpenGLTriangleDrawer.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_OPENGLTRIANGLEDRAWER_H -#define RAMSES_OPENGLTRIANGLEDRAWER_H +#pragma once #include #include "ETriangleColor.h" -namespace ramses_internal +namespace ramses::internal { class OpenGLTriangleDrawer { @@ -25,10 +24,8 @@ namespace ramses_internal private: void loadShaderProgram(); - GLuint m_shaderProgramReference; - GLuint m_vertexBuffer; - GLuint m_indexBuffer; + GLuint m_shaderProgramReference = 0; + GLuint m_vertexBuffer = 0; + GLuint m_indexBuffer = 0; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMBuffer.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMBuffer.cpp similarity index 93% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMBuffer.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMBuffer.cpp index 9fad55db3..3f9f0e800 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMBuffer.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMBuffer.cpp @@ -12,9 +12,9 @@ #include #include #include -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { SHMBuffer::SHMBuffer(wl_shm* shm, uint32_t width, uint32_t height, uint32_t id) : m_width(width) @@ -37,7 +37,7 @@ namespace ramses_internal } m_pixelData = static_cast(mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); - if (m_pixelData == MAP_FAILED) + if (m_pixelData == MAP_FAILED) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast) { LOG_ERROR(CONTEXT_RENDERER, "SHMBuffer::SHMBuffer mmap failed!"); close(fd); @@ -101,9 +101,8 @@ namespace ramses_internal wl_surface_commit(surface); } - void SHMBuffer::ReleaseCallback(void* data, wl_buffer* buffer) + void SHMBuffer::ReleaseCallback(void* data, [[maybe_unused]] wl_buffer* buffer) { - UNUSED(buffer); static_cast(data)->release(); } @@ -123,7 +122,7 @@ namespace ramses_internal int SHMBuffer::CreateAnonymousFile(off_t size) { - int ret; + int ret = 0; const char* path = getenv("XDG_RUNTIME_DIR"); if (!path) diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMBuffer.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMBuffer.h similarity index 93% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMBuffer.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMBuffer.h index 94017b650..1866d7a2e 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMBuffer.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMBuffer.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHMBUFFER_H -#define RAMSES_SHMBUFFER_H +#pragma once #include "wayland-client.h" #include -namespace ramses_internal +namespace ramses::internal { class SHMBuffer { @@ -38,7 +37,7 @@ namespace ramses_internal const struct Buffer_Listener : public wl_buffer_listener { - Buffer_Listener() + Buffer_Listener() : wl_buffer_listener() { release = ReleaseCallback; } @@ -53,4 +52,4 @@ namespace ramses_internal bool m_isFree; }; } -#endif + diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMTriangleDrawer.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMTriangleDrawer.cpp similarity index 97% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMTriangleDrawer.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMTriangleDrawer.cpp index 08cf6ab22..1f3fcb40f 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMTriangleDrawer.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMTriangleDrawer.cpp @@ -10,7 +10,7 @@ #include "SHMBuffer.h" #include -namespace ramses_internal +namespace ramses::internal { SHMTriangleDrawer::SHMTriangleDrawer(ETriangleColor triangleColor) { @@ -54,10 +54,6 @@ namespace ramses_internal m_triangleColorBGRA[3] = 0xff; } - SHMTriangleDrawer::~SHMTriangleDrawer() - { - } - void SHMTriangleDrawer::draw(SHMBuffer* buffer) { const uint32_t width = buffer->getWidth(); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMTriangleDrawer.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMTriangleDrawer.h similarity index 80% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMTriangleDrawer.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMTriangleDrawer.h index c984f75bd..e05d3e880 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/SHMTriangleDrawer.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/SHMTriangleDrawer.h @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHMTRIANGLEDRAWER_H -#define RAMSES_SHMTRIANGLEDRAWER_H +#pragma once #include #include "ETriangleColor.h" +#include -namespace ramses_internal +namespace ramses::internal { class SHMBuffer; @@ -20,13 +20,10 @@ namespace ramses_internal { public: explicit SHMTriangleDrawer(ETriangleColor triangleColor); - ~SHMTriangleDrawer(); void draw(SHMBuffer* buffer); private: - uint8_t m_triangleColorBGRA[4]; + std::array m_triangleColorBGRA{}; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestApplicationShellSurfaceId.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestApplicationShellSurfaceId.h similarity index 79% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestApplicationShellSurfaceId.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestApplicationShellSurfaceId.h index 4bde6e0ea..9d5c7091c 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestApplicationShellSurfaceId.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestApplicationShellSurfaceId.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTAPPLICATIONSHELLSURFACEID_H -#define RAMSES_TESTAPPLICATIONSHELLSURFACEID_H +#pragma once -#include "Common/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" -namespace ramses_internal +namespace ramses::internal { struct TestApplicationShellSurfaceIdTag { }; using TestApplicationShellSurfaceId = StronglyTypedValue; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestApplicationSurfaceId.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestApplicationSurfaceId.h similarity index 80% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestApplicationSurfaceId.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestApplicationSurfaceId.h index e0669e7bb..676c87d5e 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestApplicationSurfaceId.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestApplicationSurfaceId.h @@ -6,17 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTAPPLICATIONSURFACEID_H -#define RAMSES_TESTAPPLICATIONSURFACEID_H +#pragma once -#include "Common/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" -namespace ramses_internal +namespace ramses::internal { struct TestApplicationSurfaceIdTag { }; using TestApplicationSurfaceId = StronglyTypedValue; } - -#endif diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestWaylandApplication.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestWaylandApplication.cpp similarity index 84% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestWaylandApplication.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestWaylandApplication.cpp index 5098fd5ff..dc3614901 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestWaylandApplication.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestWaylandApplication.cpp @@ -11,19 +11,19 @@ #include "TestSignalHandler.h" #include "SHMTriangleDrawer.h" #include "TestScenes/MultipleTrianglesScene.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" #include "TestScenesAndRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/framework/RamsesFrameworkConfig.h" -#include "DisplayConfigImpl.h" -#include "PlatformAbstraction/PlatformSignal.h" -#include "RendererLib/DisplayConfig.h" -#include "Utils/BinaryInputStream.h" +#include "impl/DisplayConfigImpl.h" +#include "internal/PlatformAbstraction/PlatformSignal.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/BinaryInputStream.h" -namespace ramses_internal +namespace ramses::internal { TestWaylandApplication::TestWaylandApplication(std::string_view testToWaylandClientPipeName, std::string_view waylandClientToTestPipeName) : m_testToWaylandClientPipe(testToWaylandClientPipeName, false) @@ -84,7 +84,7 @@ namespace ramses_internal bool TestWaylandApplication::readBufferFromTestFramework() { - uint32_t bufferSize; + uint32_t bufferSize = 0; if (!readFromTestFramework(bufferSize)) return false; @@ -111,7 +111,7 @@ namespace ramses_internal case ETestWaylandApplicationMessage::InitializeTestApplication: { std::string displayName; - bool useSocketFD; + bool useSocketFD = false; bis >> displayName >> useSocketFD; @@ -130,10 +130,10 @@ namespace ramses_internal case ETestWaylandApplicationMessage::CreateSurface: { TestApplicationSurfaceId surfaceId; - uint32_t width; - uint32_t height; - uint32_t swapInterval; - bool useEGL; + uint32_t width = 0; + uint32_t height = 0; + uint32_t swapInterval = 0; + bool useEGL = false; bis >> surfaceId.getReference() >> width >> height >> swapInterval >> useEGL; @@ -210,25 +210,25 @@ namespace ramses_internal bis >> surfaceId.getReference(); - LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::handleIncomingMessages(): received message destroy ivi surface for surface with id " << surfaceId.getValue()); + LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::handleIncomingMessages(): received message destroy ivi-surface for surface with id " << surfaceId.getValue()); m_waylandHandler.destroyIVISurface(surfaceId); return true; } case ETestWaylandApplicationMessage::CreateIVISurface: { TestApplicationSurfaceId surfaceId; - uint32_t surfaceIviId; + WaylandIviSurfaceId surfaceIviId; - bis >> surfaceId.getReference() >> surfaceIviId; + bis >> surfaceId.getReference() >> surfaceIviId.getReference(); - LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::handleIncomingMessages(): received message create ivi surface for surface with id " << surfaceId.getValue() << " ivi-id: " << surfaceIviId); + LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::handleIncomingMessages(): received message create ivi-surface for surface with id " << surfaceId.getValue() << " " << surfaceIviId); m_waylandHandler.createIVISurface(surfaceId, surfaceIviId); return true; } case ETestWaylandApplicationMessage::RenderOneFrame_ToEGLBuffer: { TestApplicationSurfaceId surfaceId; - bool useCallback; + bool useCallback = false; bis >> surfaceId.getReference() >> useCallback; @@ -239,7 +239,7 @@ namespace ramses_internal case ETestWaylandApplicationMessage::RenderOneFrame_ToSharedMemoryBuffer: { TestApplicationSurfaceId surfaceId; - bool useCallback; + bool useCallback = false; bis >> surfaceId.getReference() >> useCallback; @@ -250,7 +250,7 @@ namespace ramses_internal case ETestWaylandApplicationMessage::AttachBuffer: { TestApplicationSurfaceId surfaceId; - bool commit; + bool commit = false; bis >> surfaceId.getReference() >> commit; @@ -261,6 +261,20 @@ namespace ramses_internal attachBuffer(surfaceId, commit); return true; } + case ETestWaylandApplicationMessage::ReAttachBuffer: + { + TestApplicationSurfaceId surfaceId; + uint32_t count = 0; + + bis >> surfaceId.getReference() >> count; + + LOG_INFO(CONTEXT_RENDERER, + "TestWaylandApplication::handleIncomingMessages(): received message reattach buffer " + "with id " + << surfaceId.getValue()); + reattachBuffer(surfaceId, count); + return true; + } case ETestWaylandApplicationMessage::DestroyBuffers: { LOG_INFO(CONTEXT_RENDERER, @@ -271,8 +285,8 @@ namespace ramses_internal case ETestWaylandApplicationMessage::SetSurfaceSize: { TestApplicationSurfaceId surfaceId; - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; bis >> surfaceId.getReference() >> width >> height; @@ -282,7 +296,7 @@ namespace ramses_internal } case ETestWaylandApplicationMessage::SetTriangleColor: { - uint32_t triangleColor; + uint32_t triangleColor = 0; bis >> triangleColor; @@ -320,7 +334,7 @@ namespace ramses_internal { TestApplicationSurfaceId surfaceId1; TestApplicationSurfaceId surfaceId2; - bool useCallback; + bool useCallback = false; bis >> surfaceId1.getReference() >> surfaceId2.getReference() >> useCallback; @@ -330,7 +344,7 @@ namespace ramses_internal } case ETestWaylandApplicationMessage::GetIsBufferFree: { - uint32_t buffer; + uint32_t buffer = 0; bis >> buffer; @@ -340,7 +354,7 @@ namespace ramses_internal sendAnswerToTestFramework(isBufferFree); if (buffer >= m_waylandHandler.getNumberOfAllocatedSHMBuffer()) { - LOG_ERROR(ramses_internal::CONTEXT_RENDERER, "WaylandHandler::getIsSHMBufferFree buffer: " << buffer << " does not exist !"); + LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::getIsSHMBufferFree buffer: " << buffer << " does not exist !"); return false; } @@ -372,14 +386,14 @@ namespace ramses_internal case ETestWaylandApplicationMessage::StartRamsesRendererAndRunRenderingTest: { - uint32_t waylandIviLayerId; - uint32_t iviSurfaceOffset; + uint32_t waylandIviLayerId = 0; + uint32_t iviSurfaceOffset = 0; bis >> waylandIviLayerId >> iviSurfaceOffset; LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::handleIncomingMessages(): received message start RAMSES renderer and run rendering test"); - const bool testResult = startRamsesRendererAndRunRenderingTest(WaylandIviLayerId{waylandIviLayerId}, iviSurfaceOffset); + const bool testResult = StartRamsesRendererAndRunRenderingTest(WaylandIviLayerId{waylandIviLayerId}, iviSurfaceOffset); sendAnswerToTestFramework(testResult); return true; } @@ -396,7 +410,7 @@ namespace ramses_internal int socketFD = -1; if(connectUsingFD) { - m_socket = std::make_unique(displayName, WaylandEnvironmentUtils::GetVariable(WaylandEnvironmentVariable::XDGRuntimeDir)); + m_socket = std::make_unique(displayName, WaylandEnvironmentUtils::GetVariable(WaylandEnvironmentVariable::XDGRuntimeDir)); socketFD = m_socket->createConnectedFileDescriptor(false); if(socketFD == -1) @@ -433,8 +447,8 @@ namespace ramses_internal void TestWaylandApplication::renderFrameToSharedMemoryBuffer(TestApplicationSurfaceId surfaceId, bool useCallback) { - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; m_waylandHandler.getWindowSize(surfaceId, width, height); SHMBuffer* buffer = m_waylandHandler.getFreeSHMBuffer(width, height); LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::renderFrameToSharedMemoryBuffer render to SHMBuffer with id " << buffer->getId()); @@ -452,8 +466,8 @@ namespace ramses_internal void TestWaylandApplication::attachBuffer(TestApplicationSurfaceId surfaceId, bool commit) { - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; m_waylandHandler.getWindowSize(surfaceId, width, height); SHMBuffer* buffer = m_waylandHandler.getFreeSHMBuffer(width, height); LOG_INFO(CONTEXT_RENDERER, @@ -462,14 +476,26 @@ namespace ramses_internal m_waylandHandler.attachBuffer(surfaceId, *buffer, commit); } + void TestWaylandApplication::reattachBuffer(TestApplicationSurfaceId surfaceId, uint32_t count) + { + uint32_t width = 0; + uint32_t height = 0; + m_waylandHandler.getWindowSize(surfaceId, width, height); + SHMBuffer* buffer = m_waylandHandler.getFreeSHMBuffer(width, height); + LOG_INFO(CONTEXT_RENDERER, + "TestWaylandApplication::reattachBuffer: attaching buffer" << buffer->getId()); + + m_waylandHandler.reattachBuffer(surfaceId, *buffer, count); + } + void TestWaylandApplication::renderFrameToTwoSurfaces(TestApplicationSurfaceId surfaceId1, TestApplicationSurfaceId surfaceId2, bool useCallback) { - uint32_t width1; - uint32_t height1; + uint32_t width1 = 0; + uint32_t height1 = 0; m_waylandHandler.getWindowSize(surfaceId1, width1, height1); - uint32_t width2; - uint32_t height2; + uint32_t width2 = 0; + uint32_t height2 = 0; m_waylandHandler.getWindowSize(surfaceId2, width2, height2); if (width1 != width2 || height1 != height2) @@ -505,49 +531,49 @@ namespace ramses_internal LOG_INFO(CONTEXT_RENDERER, "TestWaylandApplication::detachBufferFromSurface buffer detached"); } - bool TestWaylandApplication::startRamsesRendererAndRunRenderingTest(WaylandIviLayerId waylandIviLayerId, uint32_t iviSurfaceIdOffset) + bool TestWaylandApplication::StartRamsesRendererAndRunRenderingTest(WaylandIviLayerId waylandIviLayerId, uint32_t iviSurfaceIdOffset) { const uint32_t windowWidth = 128u; const uint32_t windowHeight = 64u; const char* systemCompositorDisplay = "wayland-0"; //start renderer with two displays that show content on the system compositor (not the RAMSES renderer's EC) - ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest}; + RamsesFrameworkConfig config{EFeatureLevel_Latest}; TestScenesAndRenderer testScenesAndRenderer(config); TestRenderer& testRenderer = testScenesAndRenderer.getTestRenderer(); RendererTestUtils::SetWaylandDisplayForSystemCompositorControllerForAllTests(systemCompositorDisplay); testScenesAndRenderer.initializeRenderer(); ramses::DisplayConfig displayConfig1 = RendererTestUtils::CreateTestDisplayConfig(iviSurfaceIdOffset); - displayConfig1.setWindowType(ramses::EWindowType::Wayland_IVI); + displayConfig1.setWindowType(EWindowType::Wayland_IVI); displayConfig1.setWindowRectangle(0, 0, windowWidth, windowHeight); - displayConfig1.m_impl.get().setWaylandDisplay(systemCompositorDisplay); - displayConfig1.setWaylandIviLayerID(ramses::waylandIviLayerId_t(waylandIviLayerId.getValue())); + displayConfig1.setWaylandDisplay(systemCompositorDisplay); + displayConfig1.setWaylandIviLayerID(waylandIviLayerId_t(waylandIviLayerId.getValue())); displayConfig1.setWaylandEmbeddedCompositingSocketName(""); ramses::DisplayConfig displayConfig2 = RendererTestUtils::CreateTestDisplayConfig(iviSurfaceIdOffset + 1); - displayConfig2.setWindowType(ramses::EWindowType::Wayland_IVI); + displayConfig2.setWindowType(EWindowType::Wayland_IVI); displayConfig2.setWindowRectangle(windowWidth, 0, windowWidth, windowHeight); - displayConfig2.m_impl.get().setWaylandDisplay(systemCompositorDisplay); - displayConfig2.setWaylandIviLayerID(ramses::waylandIviLayerId_t(waylandIviLayerId.getValue())); + displayConfig2.setWaylandDisplay(systemCompositorDisplay); + displayConfig2.setWaylandIviLayerID(waylandIviLayerId_t(waylandIviLayerId.getValue())); displayConfig2.setWaylandEmbeddedCompositingSocketName(""); const auto displayHandle1 = testRenderer.createDisplay(displayConfig1); const auto displayHandle2 = testRenderer.createDisplay(displayConfig2); //create two scenes and map a scene to each display - const ramses::sceneId_t sceneId1 = testScenesAndRenderer.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f), windowWidth, windowHeight); + const sceneId_t sceneId1 = testScenesAndRenderer.getScenesRegistry().createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f), windowWidth, windowHeight); testScenesAndRenderer.publish(sceneId1); testScenesAndRenderer.flush(sceneId1); testRenderer.setSceneMapping(sceneId1, displayHandle1); - testRenderer.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); + testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered); - const ramses::sceneId_t sceneId2 = testScenesAndRenderer.getScenesRegistry().createScene(ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f), windowWidth, windowHeight); + const sceneId_t sceneId2 = testScenesAndRenderer.getScenesRegistry().createScene(MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f), windowWidth, windowHeight); testScenesAndRenderer.publish(sceneId2); testScenesAndRenderer.flush(sceneId2); testRenderer.setSceneMapping(sceneId2, displayHandle2); - testRenderer.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); + testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered); //take screenshots and perform check to make sure that the renderer created here does render the scenes on the system compositor's surfaces bool testResult = testRenderer.performScreenshotCheck(displayHandle1, {}, 0u, 0u, windowWidth, windowHeight, "ARendererInstance_Three_Triangles"); diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestWaylandApplication.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestWaylandApplication.h similarity index 80% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestWaylandApplication.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestWaylandApplication.h index bd416fb8d..b067ab7fd 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/TestWaylandApplication.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/TestWaylandApplication.h @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTWAYLANDAPPLICATION_H -#define RAMSES_TESTWAYLANDAPPLICATION_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "RendererAPI/Types.h" +#include "internal/RendererLib/Types.h" #include "EmbeddedCompositingTestMessages.h" #include "NamedPipe.h" #include "WaylandHandler.h" #include "OpenGLTriangleDrawer.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "Utils/LogMacros.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Core/Utils/LogMacros.h" +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { class TestWaylandApplication { @@ -43,10 +42,11 @@ namespace ramses_internal void renderFrameToEGLBuffer(TestApplicationSurfaceId surfaceId, bool useCallback); void renderFrameToSharedMemoryBuffer(TestApplicationSurfaceId surfaceId, bool useCallback); void attachBuffer(TestApplicationSurfaceId surfaceId, bool commit); + void reattachBuffer(TestApplicationSurfaceId surfaceId, uint32_t count); void renderFrameToTwoSurfaces(TestApplicationSurfaceId surfaceId1, TestApplicationSurfaceId surfaceId2, bool useCallback); void setTriangleColor(ETriangleColor color); void detachBufferFromSurface(TestApplicationSurfaceId surfaceId); - bool startRamsesRendererAndRunRenderingTest(WaylandIviLayerId waylandIviLayerId, uint32_t iviSurfaceIdOffset); + static bool StartRamsesRendererAndRunRenderingTest(WaylandIviLayerId waylandIviLayerId, uint32_t iviSurfaceIdOffset); template void sendAnswerToTestFramework(const T& value); @@ -56,8 +56,8 @@ namespace ramses_internal WaylandHandler m_waylandHandler2; ETriangleColor m_triangleColor; - std::vector m_readingBuffer; - std::unique_ptr m_socket; + std::vector m_readingBuffer; + std::unique_ptr m_socket; }; } -#endif + diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/WaylandHandler.cpp b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/WaylandHandler.cpp similarity index 89% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/WaylandHandler.cpp rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/WaylandHandler.cpp index 326e21b92..33ca6c808 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/WaylandHandler.cpp +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/WaylandHandler.cpp @@ -8,12 +8,12 @@ #include "WaylandHandler.h" #include "SHMBuffer.h" -#include "Utils/LogMacros.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" #include #include -namespace ramses_internal +namespace ramses::internal { bool WaylandHandler::init(const std::string& displayName, int displayFD) { @@ -111,7 +111,7 @@ namespace ramses_internal bool WaylandHandler::createWindow(TestApplicationSurfaceId surfaceId, uint32_t windowWidth, uint32_t windowHeight, uint32_t swapInterval, bool useEGL) { - TestWaylandWindow* window = new TestWaylandWindow; + auto* window = new TestWaylandWindow; m_windows.put(surfaceId, window); window->width = windowWidth; @@ -173,6 +173,19 @@ namespace ramses_internal wl_display_flush(wayland.display); } + void WaylandHandler::reattachBuffer(TestApplicationSurfaceId surfaceId, const SHMBuffer& buffer, uint32_t count) + { + TestWaylandWindow& window = getWindow(surfaceId); + while(count-- != 0) + { + wl_surface_attach(window.surface, nullptr, 0, 0); + wl_surface_commit(window.surface); + wl_surface_attach(window.surface, buffer.getWaylandBuffer(), 0, 0); + wl_surface_commit(window.surface); + } + wl_display_flush(wayland.display); + } + void WaylandHandler::detachBuffer(TestApplicationSurfaceId surfaceId) { TestWaylandWindow& window = getWindow(surfaceId); @@ -197,7 +210,7 @@ namespace ramses_internal bool WaylandHandler::checkAndHandleEvents() { - pollfd pfd; + pollfd pfd{}; pfd.fd = wayland.fd; pfd.events = POLLIN; @@ -212,7 +225,7 @@ namespace ramses_internal } // NOLINTNEXTLINE(hicpp-signed-bitwise) - if (pfd.revents & POLLIN) + if ((pfd.revents & POLLIN) != 0) { return wl_display_dispatch(wayland.display) != 0; } @@ -239,8 +252,8 @@ namespace ramses_internal if (!m_usesSharedDisplay) { - EGLint major; - EGLint minor; + EGLint major = 0; + EGLint minor = 0; if (eglInitialize(egldisplay, &major, &minor) != EGL_TRUE) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::setupEGL(): eglInitialize failed with code :" << eglGetError()); @@ -254,7 +267,7 @@ namespace ramses_internal } } - EGLint numconfigs; + EGLint numconfigs = 0; if (eglChooseConfig(egldisplay, ConfigAttribs, &eglconfig, 1, &numconfigs) != EGL_TRUE) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::setupEGL(): eglChooseConfig failed !"); @@ -274,7 +287,7 @@ namespace ramses_internal { LOG_INFO(CONTEXT_RENDERER, "WaylandHandler::registry_handle_global(): binding interface: " << interface << " version: " << versionProvidedByCompositor); - WaylandHandler* waylandHandler = static_cast(data); + auto* waylandHandler = static_cast(data); if (strcmp(interface, "wl_compositor") == 0) { @@ -334,7 +347,7 @@ namespace ramses_internal abort(); } - if(waylandHandler->m_requiredWaylandOutputVersion) + if (waylandHandler->m_requiredWaylandOutputVersion != 0u) { const uint32_t maximumExpectedProtocolVersion = 3u; if(versionProvidedByCompositor > maximumExpectedProtocolVersion) @@ -361,22 +374,21 @@ namespace ramses_internal } } - void WaylandHandler::registry_handle_global_remove(void* data, wl_registry* wl_registry, uint32_t name) - { - UNUSED(data); - UNUSED(wl_registry); - UNUSED(name); - } + void WaylandHandler::registry_handle_global_remove([[maybe_unused]] void* data, [[maybe_unused]] wl_registry* wl_registry, [[maybe_unused]] uint32_t name) {} bool WaylandHandler::setupWayland(const std::string& displayName, int displayFD) { LOG_INFO(CONTEXT_RENDERER, "WaylandHandler::setupWayland(): will connect to display"); WaylandEnvironmentUtils::LogEnvironmentState(""); - if(displayFD != -1) + if (displayFD != -1) + { wayland.display = wl_display_connect_to_fd(displayFD); + } else + { wayland.display = wl_display_connect(displayName.c_str()); + } if (wayland.display == nullptr) { @@ -565,17 +577,17 @@ namespace ramses_internal wl_display_flush(wayland.display); } - void WaylandHandler::createIVISurface(TestApplicationSurfaceId surfaceId, uint32_t iviSurfaceId) + void WaylandHandler::createIVISurface(TestApplicationSurfaceId surfaceId, WaylandIviSurfaceId iviSurfaceId) { TestWaylandWindow& window = getWindow(surfaceId); if (wayland.ivi_app != nullptr) { - ivi_surface* iviSurface = ivi_application_surface_create(wayland.ivi_app, iviSurfaceId, window.surface); + ivi_surface* iviSurface = ivi_application_surface_create(wayland.ivi_app, iviSurfaceId.getValue(), window.surface); if (iviSurface == nullptr) { LOG_ERROR(CONTEXT_RENDERER, - "WaylandHandler::createIVISurface(): Failed to create ivi-surface"); + "WaylandHandler::createIVISurface(): Failed to create " << iviSurfaceId); assert(false); } else @@ -660,23 +672,23 @@ namespace ramses_internal } - void WaylandHandler::FrameCallback(void* userData, wl_callback* callback, uint32_t) + void WaylandHandler::FrameCallback(void* userData, wl_callback* callback, uint32_t /*unused*/) { - WaylandHandler* waylandHandler = static_cast(userData); + auto* waylandHandler = static_cast(userData); waylandHandler->frameCallback(callback); } - void WaylandHandler::wayland_output_handle_geometry(void* data, wl_output*, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char*, const char*, int32_t transform) + void WaylandHandler::wayland_output_handle_geometry(void* data, wl_output* /*unused*/, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* /*unused*/, const char* /*unused*/, int32_t transform) { - WaylandHandler* waylandHandler = static_cast(data); + auto* waylandHandler = static_cast(data); - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_geometry: geometry is received AFTER done callback!"); waylandHandler->m_errorFoundInWaylandOutput = true; } - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_GeometryReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_GeometryReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_geometry: geometry was already received!"); waylandHandler->m_errorFoundInWaylandOutput = true; @@ -692,17 +704,17 @@ namespace ramses_internal waylandHandler->m_waylandOutputParams.transform = transform; } - void WaylandHandler::wayland_output_handle_mode(void* data, wl_output*, uint32_t flags, int32_t width, int32_t height, int32_t refresh) + void WaylandHandler::wayland_output_handle_mode(void* data, wl_output* /*unused*/, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - WaylandHandler* waylandHandler = static_cast(data); + auto* waylandHandler = static_cast(data); - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_mode: mode is received AFTER done callback!"); waylandHandler->m_errorFoundInWaylandOutput = true; } - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ModeReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ModeReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_mode: mode was already received!"); waylandHandler->m_errorFoundInWaylandOutput = true; @@ -716,29 +728,29 @@ namespace ramses_internal waylandHandler->m_waylandOutputParams.refreshRate = refresh; } - void WaylandHandler::wayland_output_handle_done(void *data, wl_output*) + void WaylandHandler::wayland_output_handle_done(void *data, wl_output* /*unused*/) { - WaylandHandler* waylandHandler = static_cast(data); + auto* waylandHandler = static_cast(data); - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_done: done was already received!"); waylandHandler->m_errorFoundInWaylandOutput = true; } - if(!(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_GeometryReceived)) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_GeometryReceived) == 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_done: geometry was NOT received!"); waylandHandler->m_errorFoundInWaylandOutput = true; } - if(!(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ModeReceived)) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ModeReceived) == 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_done: mode was NOT received!"); waylandHandler->m_errorFoundInWaylandOutput = true; } - if(!(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ScaleReceived)) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ScaleReceived) == 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_done: scale was NOT received!"); waylandHandler->m_errorFoundInWaylandOutput = true; @@ -747,17 +759,17 @@ namespace ramses_internal waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags |= WaylandOutputTestParams::WaylandOutput_DoneReceived; } - void WaylandHandler::wayland_output_handle_scale(void* data, wl_output*, int32_t factor) + void WaylandHandler::wayland_output_handle_scale(void* data, wl_output* /*unused*/, int32_t factor) { - WaylandHandler* waylandHandler = static_cast(data); + auto* waylandHandler = static_cast(data); - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_DoneReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_scale: scale is received AFTER done callback!"); waylandHandler->m_errorFoundInWaylandOutput = true; } - if(waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ScaleReceived) + if ((waylandHandler->m_waylandOutputParams.m_waylandOutputReceivedFlags & WaylandOutputTestParams::WaylandOutput_ScaleReceived) != 0) { LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::wayland_output_handle_scale: scale was already received!"); waylandHandler->m_errorFoundInWaylandOutput = true; @@ -785,7 +797,7 @@ namespace ramses_internal } } - SHMBuffer* buffer = new SHMBuffer(wayland.shm, width, height, m_shmBuffer.size()); + auto* buffer = new SHMBuffer(wayland.shm, width, height, m_shmBuffer.size()); m_shmBuffer.push_back(buffer); return buffer; } @@ -839,10 +851,7 @@ namespace ramses_internal { return m_shmBuffer[buffer]->isFree(); } - else - { - return false; - } + return false; } void WaylandHandler::getWaylandOutputTestParams(bool& errorsFound, WaylandOutputTestParams& waylandOutputParams) const @@ -858,13 +867,10 @@ namespace ramses_internal { return *i->value; } - else - { - LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::getWindow Window with id: " << surfaceId.getValue() << " not found !"); - assert(false); - static TestWaylandWindow dummyWindow; - return dummyWindow; - } + LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::getWindow Window with id: " << surfaceId.getValue() << " not found !"); + assert(false); + static TestWaylandWindow dummyWindow; + return dummyWindow; } wl_shell_surface& WaylandHandler::getShellSurface(TestApplicationShellSurfaceId shellSurfaceId) const @@ -874,12 +880,8 @@ namespace ramses_internal { return *(i->value); } - else - { - LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::getShellSurface Shell surface with id: " << shellSurfaceId.getValue() << " not found !"); - assert(false); - - } + LOG_ERROR(CONTEXT_RENDERER, "WaylandHandler::getShellSurface Shell surface with id: " << shellSurfaceId.getValue() << " not found !"); + assert(false); wl_shell_surface* nullShellSurface(nullptr); return static_cast(*nullShellSurface); } diff --git a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/WaylandHandler.h b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/WaylandHandler.h similarity index 92% rename from integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/WaylandHandler.h rename to tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/WaylandHandler.h index 3d5ada468..33ab1c53f 100644 --- a/integration/SandwichTests/RendererTests/EmbeddedCompositingTests/TestWaylandApplication/WaylandHandler.h +++ b/tests/integration/renderer-tests/embedded-compositing-rendering-tests/TestWaylandApplication/WaylandHandler.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDHANDLER_H -#define RAMSES_WAYLANDHANDLER_H +#pragma once #define GL_GLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES @@ -16,8 +15,9 @@ #include "TestApplicationSurfaceId.h" #include "TestApplicationShellSurfaceId.h" #include "WaylandOutputTestParams.h" -#include "RendererAPI/Types.h" -#include "Collections/HashMap.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/Types.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" #include #include @@ -38,7 +38,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { class TestWaylandDisplay { @@ -86,13 +86,14 @@ namespace ramses_internal void destroyShellSurface(TestApplicationShellSurfaceId shellSurfaceId); void destroyWindow(TestApplicationSurfaceId surfaceId); void destroyIVISurface(TestApplicationSurfaceId surfaceId); - void createIVISurface(TestApplicationSurfaceId surfaceId, uint32_t iviSurfaceId); + void createIVISurface(TestApplicationSurfaceId surfaceId, WaylandIviSurfaceId iviSurfaceId); void enableContextForSurface(TestApplicationSurfaceId surfaceId); void disableContextForSurface(); void resizeWindow(TestApplicationSurfaceId surfaceId, uint32_t width, uint32_t height); void swapBuffersAndProcessEvents(TestApplicationSurfaceId surfaceId, bool useCallback); void swapBuffersAndProcessEvents(TestApplicationSurfaceId surfaceId, SHMBuffer& buffer, bool useCallback); void attachBuffer(TestApplicationSurfaceId surfaceId, const SHMBuffer& buffer, bool commit); + void reattachBuffer(TestApplicationSurfaceId surfaceId, const SHMBuffer& buffer, uint32_t count); void detachBuffer(TestApplicationSurfaceId surfaceId); void deleteSHMBuffers(); void waitOnFrameCallback(TestApplicationSurfaceId surfaceId); @@ -118,7 +119,7 @@ namespace ramses_internal static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps); void frameCallback(wl_callback* callback); - static void FrameCallback(void* userData, wl_callback* callback, uint32_t); + static void FrameCallback(void* userData, wl_callback* callback, uint32_t /*unused*/); static void wayland_output_handle_geometry(void *data, struct wl_output *wl_output, @@ -159,7 +160,7 @@ namespace ramses_internal using ShellSurfacesHashMap = HashMap; EGLDisplay egldisplay = EGL_NO_DISPLAY; - EGLConfig eglconfig; + EGLConfig eglconfig{}; TestWaylandDisplay wayland; @@ -171,7 +172,7 @@ namespace ramses_internal const struct Registry_Listener : public wl_registry_listener { - Registry_Listener() + Registry_Listener() : wl_registry_listener() { global = registry_handle_global; global_remove = registry_handle_global_remove; @@ -180,14 +181,14 @@ namespace ramses_internal const struct FrameRenderingDoneCallback_Listener : public wl_callback_listener { - FrameRenderingDoneCallback_Listener() + FrameRenderingDoneCallback_Listener() : wl_callback_listener() { done = FrameCallback; } } m_frameRenderingDoneCallbackListener; const struct WaylandOutput_ListenerV1 : public wl_output_listener { - WaylandOutput_ListenerV1() + WaylandOutput_ListenerV1() : wl_output_listener() { geometry = wayland_output_handle_geometry; mode = wayland_output_handle_mode; @@ -197,7 +198,7 @@ namespace ramses_internal } m_waylandOutputListenerV1; const struct WaylandOutput_ListenerV2 : public wl_output_listener { - WaylandOutput_ListenerV2() + WaylandOutput_ListenerV2() : wl_output_listener() { geometry = wayland_output_handle_geometry; mode = wayland_output_handle_mode; @@ -207,7 +208,7 @@ namespace ramses_internal } m_waylandOutputListenerV2; const struct WaylandOutput_ListenerV3 : public wl_output_listener { - WaylandOutput_ListenerV3() + WaylandOutput_ListenerV3() : wl_output_listener() { geometry = wayland_output_handle_geometry; mode = wayland_output_handle_mode; @@ -221,5 +222,3 @@ namespace ramses_internal WaylandOutputTestParams m_waylandOutputParams; }; } - -#endif diff --git a/tests/integration/renderer-tests/renderer-lifecycle-tests/ExternalWindowTests.cpp b/tests/integration/renderer-tests/renderer-lifecycle-tests/ExternalWindowTests.cpp new file mode 100644 index 000000000..4c8194f1d --- /dev/null +++ b/tests/integration/renderer-tests/renderer-lifecycle-tests/ExternalWindowTests.cpp @@ -0,0 +1,103 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererLifecycleTests.h" +#include "TestScenes/MultipleTrianglesScene.h" + +#include "ramses/renderer/DisplayConfig.h" +#include "impl/DisplayConfigImpl.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +#ifdef ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS +#include "internal/Platform/Windows/Window_Windows.h" +#elif ramses_sdk_ENABLE_WINDOW_TYPE_X11 +#include "internal/Platform/X11/Window_X11.h" +#endif + +namespace ramses::internal +{ + TEST_F(ARendererLifecycleTest, CanRenderSceneToExternallyOwnedWindow) + { + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + testScenesAndRenderer.initializeRenderer(); + + class DummyEventHandler : public IWindowEventHandler + { + public: + ~DummyEventHandler() override = default; + + void onKeyEvent(EKeyEvent event, KeyModifiers mod, EKeyCode code) override + { + (void)event; + (void)mod; + (void)code; + } + + void onMouseEvent(EMouseEvent event, int32_t param1, int32_t param2) override + { + (void)event; + (void)param1; + (void)param2; + } + void onClose() override + { + } + void onResize(uint32_t x, uint32_t y) override + { + (void)x; + (void)y; + } + void onWindowMove(int32_t x, int32_t y) override + { + (void)x; + (void)y; + } + }; + + ramses::DisplayConfig dispConfigExternalWindow = RendererTestUtils::CreateTestDisplayConfig(0u, false); + if(dispConfigExternalWindow.getWindowType() != EWindowType::X11 + && dispConfigExternalWindow.getWindowType() != EWindowType::Windows) + { + GTEST_SKIP(); + } + + dispConfigExternalWindow.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); + DummyEventHandler dummyEventHandler; + + auto dispConfig = RendererTestUtils::CreateTestDisplayConfig(0u, false); + dispConfig.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); + +#ifdef ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS + ThreadLocalLog::SetPrefix(1); + Window_Windows window(dispConfigExternalWindow.impl().getInternalDisplayConfig(), dummyEventHandler, 1); + ASSERT_TRUE(window.init()); + dispConfig.setWindowsWindowHandle(window.getNativeWindowHandle()); +#elif ramses_sdk_ENABLE_WINDOW_TYPE_X11 + ThreadLocalLog::SetPrefix(1); + Window_X11 window(dispConfigExternalWindow.impl().getInternalDisplayConfig(), dummyEventHandler, 1); + ASSERT_TRUE(window.init()); + dispConfig.setX11WindowHandle(X11WindowHandle{window.getNativeWindowHandle()}); +#endif + + const displayId_t display = testRenderer.createDisplay(dispConfig); + ASSERT_TRUE(display != displayId_t::Invalid()); + + testScenesAndRenderer.publish(sceneId); + testScenesAndRenderer.flush(sceneId); + testRenderer.setSceneMapping(sceneId, display); + + auto& scene = testScenesAndRenderer.getScenesRegistry().getScene(sceneId); + ASSERT_TRUE(testRenderer.getSceneToState(scene, RendererSceneState::Rendered)); + + ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); + + testScenesAndRenderer.unpublish(sceneId); + testScenesAndRenderer.destroyRenderer(); + } +} diff --git a/integration/SandwichTests/RendererTests/RendererLifecycleTests/RendererLifecycleTests.cpp b/tests/integration/renderer-tests/renderer-lifecycle-tests/RendererLifecycleTests.cpp similarity index 64% rename from integration/SandwichTests/RendererTests/RendererLifecycleTests/RendererLifecycleTests.cpp rename to tests/integration/renderer-tests/renderer-lifecycle-tests/RendererLifecycleTests.cpp index 2902d322c..7714ae737 100644 --- a/integration/SandwichTests/RendererTests/RendererLifecycleTests/RendererLifecycleTests.cpp +++ b/tests/integration/renderer-tests/renderer-lifecycle-tests/RendererLifecycleTests.cpp @@ -9,7 +9,7 @@ #include "RendererLifecycleTests.h" // These includes are needed by the tests -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include "ReadPixelCallbackHandler.h" #include "TestScenes/MultipleTrianglesScene.h" #include "TestScenes/TextureBufferScene.h" @@ -22,29 +22,30 @@ #include "TestScenes/VisibilityScene.h" // These includes are needed because of ramses API usage -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/SceneReference.h" -#include "RamsesObjectTypeUtils.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/SceneReference.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/ValidationReportImpl.h" #include #include -namespace ramses_internal +namespace ramses::internal { TEST_F(ARendererLifecycleTest, RenderScene) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(display != displayId_t::Invalid()); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); @@ -54,14 +55,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, RecreateSceneWithSameId) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); @@ -72,7 +73,7 @@ namespace ramses_internal testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); testScenesAndRenderer.unpublish(sceneId); @@ -81,19 +82,19 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SaveLoadSceneFromFileThenRender) { - const ramses::sceneId_t sceneId(1234u); + const sceneId_t sceneId(1234u); testScenesAndRenderer.getScenesRegistry().createFileLoadingScene(sceneId, glm::vec3(0.0f, 0.0f, 5.0f), frameworkConfig, FileLoadingScene::CREATE_SAVE_DESTROY_LOAD_USING_SEPARATE_CLIENT, WindowWidth, WindowHeight); const auto validateResult = testScenesAndRenderer.validateScene(sceneId); - ASSERT_EQ(ramses::StatusOK, validateResult) << testScenesAndRenderer.getValidationReport(sceneId); + ASSERT_FALSE(validateResult.hasIssue()) << validateResult.impl().toString(); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_AfterLoadSave")); @@ -102,19 +103,19 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SaveLoadSceneFromFileThenRender_Threaded) { - const ramses::sceneId_t sceneId(1234u); + const sceneId_t sceneId(1234u); testScenesAndRenderer.getScenesRegistry().createFileLoadingScene(sceneId, glm::vec3(0.0f, 0.0f, 5.0f), frameworkConfig, FileLoadingScene::CREATE_SAVE_DESTROY_LOAD_USING_SEPARATE_CLIENT, WindowWidth, WindowHeight); const auto validateResult = testScenesAndRenderer.validateScene(sceneId); - ASSERT_EQ(ramses::StatusOK, validateResult) << testScenesAndRenderer.getValidationReport(sceneId); + ASSERT_FALSE(validateResult.hasIssue()) << validateResult.impl().toString(); testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); testScenesAndRenderer.publish(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.flush(sceneId, 1); testRenderer.waitForFlush(sceneId, 1); @@ -128,29 +129,29 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, createDestroyDisplayMultipleTimesThreaded) { - const ramses::sceneId_t sceneId(1234u); + const sceneId_t sceneId(1234u); testScenesAndRenderer.getScenesRegistry().createFileLoadingScene(sceneId, glm::vec3(0.0f, 0.0f, 5.0f), frameworkConfig, FileLoadingScene::CREATE_SAVE_DESTROY_LOAD_USING_SEPARATE_CLIENT, WindowWidth, WindowHeight); const auto validateResult = testScenesAndRenderer.validateScene(sceneId); - ASSERT_EQ(ramses::StatusOK, validateResult) << testScenesAndRenderer.getValidationReport(sceneId); + ASSERT_FALSE(validateResult.hasIssue()) << validateResult.impl().toString(); testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); for (uint32_t i = 0; i < 10; ++i) { - const ramses::displayId_t display = createDisplayForWindow(i); + const displayId_t display = createDisplayForWindow(i); testScenesAndRenderer.publish(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.flush(sceneId, 1); testRenderer.waitForFlush(sceneId, 1); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_AfterLoadSave")); testScenesAndRenderer.unpublish(sceneId); - ASSERT_TRUE(testScenesAndRenderer.getTestRenderer().waitForSceneStateChange(sceneId, ramses::RendererSceneState::Unavailable)); + ASSERT_TRUE(testScenesAndRenderer.waitForSceneStateChange(sceneId, RendererSceneState::Unavailable)); testRenderer.destroyDisplay(display); } @@ -160,14 +161,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, DestroyAndRecreateRenderer) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.unpublish(sceneId); testScenesAndRenderer.destroyRenderer(); @@ -177,7 +178,7 @@ namespace ramses_internal testScenesAndRenderer.publish(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); @@ -188,14 +189,14 @@ namespace ramses_internal #if defined(RAMSES_TEXT_ENABLED) TEST_F(ARendererLifecycleTest, DestroyRenderer_ChangeScene_ThenRecreateRenderer) { - const ramses::sceneId_t sceneId = createScene(TextScene::EState_INITIAL); + const sceneId_t sceneId = createScene(TextScene::EState_INITIAL); testScenesAndRenderer.initializeRenderer(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.unpublish(sceneId); testScenesAndRenderer.destroyRenderer(); @@ -204,12 +205,12 @@ namespace ramses_internal testScenesAndRenderer.initializeRenderer(); display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + ASSERT_TRUE(displayId_t::Invalid() != display); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_SimpleText")); @@ -220,24 +221,24 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, UnsubscribeRenderer_ChangeScene_ThenResubscribeRenderer) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Available)); ASSERT_TRUE(checkScreenshot(display, "ARendererDisplays_Black")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, MultipleTrianglesScene::TRIANGLES_REORDERED); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Triangles_reordered")); testScenesAndRenderer.unpublish(sceneId); @@ -246,24 +247,24 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, ChangeScene_UnsubscribeRenderer_Flush_ThenResubscribeRenderer) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(display != displayId_t::Invalid()); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, MultipleTrianglesScene::TRIANGLES_REORDERED); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Available)); ASSERT_TRUE(checkScreenshot(display, "ARendererDisplays_Black")); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Triangles_reordered")); @@ -273,16 +274,16 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, RAMSES2881_CreateRendererAfterScene) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); @@ -292,24 +293,24 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, DestroyDisplayAndRemapSceneToOtherDisplay) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display1 = createDisplayForWindow(0u); - const ramses::displayId_t display2 = createDisplayForWindow(1u); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display1); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display2); + const displayId_t display1 = createDisplayForWindow(0u); + const displayId_t display2 = createDisplayForWindow(1u); + ASSERT_TRUE(displayId_t::Invalid() != display1); + ASSERT_TRUE(displayId_t::Invalid() != display2); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display1); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Available)); testRenderer.destroyDisplay(display1); testRenderer.setSceneMapping(sceneId, display2); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display2, "ARendererInstance_Three_Triangles")); @@ -321,15 +322,15 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.flush(sceneId, 1u); testRenderer.waitForFlush(sceneId, 1u); @@ -346,10 +347,10 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); // create data to change auto data = testScenesAndRenderer.getScenesRegistry().getScene(sceneId).createDataObject(ramses::EDataType::Int32); @@ -358,7 +359,7 @@ namespace ramses_internal testScenesAndRenderer.publish(sceneId); //do not wait for subscription - testRenderer.setSceneState(sceneId, ramses::RendererSceneState::Available); + testRenderer.setSceneState(sceneId, RendererSceneState::Available); // change scene while subscription is ongoing for (int i = 0; i < 80; i++) @@ -366,10 +367,10 @@ namespace ramses_internal data->setValue(i); testScenesAndRenderer.flush(sceneId); } - ASSERT_TRUE(testRenderer.waitForSceneStateChange(sceneId, ramses::RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.waitForSceneStateChange(sceneId, RendererSceneState::Available)); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.flush(sceneId, 1u); testRenderer.waitForFlush(sceneId, 1u); @@ -386,14 +387,14 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); @@ -417,14 +418,14 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.destroyRenderer(); } @@ -433,14 +434,14 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(0u, false); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(0u, false); + ASSERT_TRUE(display != displayId_t::Invalid()); - const ramses::sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); // this would time out if resources for the scene could not be uploaded testScenesAndRenderer.unpublish(sceneId); @@ -452,16 +453,16 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, RendererUploadsResourcesIfIviSurfaceInvisibleInLoopModeUpdateOnly) { testScenesAndRenderer.initializeRenderer(); - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); testRenderer.startRendererThread(); - const ramses::displayId_t display = createDisplayForWindow(0u, false); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(0u, false); + ASSERT_TRUE(display != displayId_t::Invalid()); - const ramses::sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); // this would time out if resources for the scene could not be uploaded testScenesAndRenderer.unpublish(sceneId); @@ -472,13 +473,13 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, RemapScenesWithDynamicResourcesToOtherDisplay) { - const ramses::sceneId_t sceneId1 = createScene(TextureBufferScene::EState_RGBA8_OneMip_ScaledDown, glm::vec3(-0.1f, -0.1f, 15.0f), 200u, 200u); // stretch a bit by using bigger viewport - const ramses::sceneId_t sceneId2 = createScene(ArrayBufferScene::INDEX_DATA_BUFFER_UINT16, glm::vec3(-2.0f, -2.0f, 15.0f)); + const sceneId_t sceneId1 = createScene(TextureBufferScene::EState_RGBA8_OneMip_ScaledDown, glm::vec3(-0.1f, -0.1f, 15.0f), 200u, 200u); // stretch a bit by using bigger viewport + const sceneId_t sceneId2 = createScene(ArrayBufferScene::INDEX_DATA_BUFFER_UINT16, glm::vec3(-2.0f, -2.0f, 15.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display1 = createDisplayForWindow(0u); - const ramses::displayId_t display2 = createDisplayForWindow(1u); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display1); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display2); + const displayId_t display1 = createDisplayForWindow(0u); + const displayId_t display2 = createDisplayForWindow(1u); + ASSERT_TRUE(displayId_t::Invalid() != display1); + ASSERT_TRUE(displayId_t::Invalid() != display2); testScenesAndRenderer.publish(sceneId1); testScenesAndRenderer.publish(sceneId2); @@ -486,18 +487,18 @@ namespace ramses_internal testScenesAndRenderer.flush(sceneId2); testRenderer.setSceneMapping(sceneId1, display1); testRenderer.setSceneMapping(sceneId2, display1); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered)); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered)); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId1, ramses::RendererSceneState::Available)); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId2, ramses::RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Available)); ASSERT_TRUE(checkScreenshot(display1, "ARendererDisplays_Black")); testRenderer.setSceneMapping(sceneId1, display2); testRenderer.setSceneMapping(sceneId2, display2); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered)); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display2, "ARendererInstance_DynamicResources")); @@ -508,17 +509,17 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneCanReachShownStateWithLoopModeUpdateOnly_UsingDoOneLoop) { - const ramses::sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(display != displayId_t::Invalid()); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.unpublish(sceneId); testScenesAndRenderer.destroyRenderer(); @@ -528,16 +529,16 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.unpublish(sceneId); testRenderer.destroyDisplay(display); @@ -549,16 +550,16 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); - const ramses::displayId_t display = createDisplayForWindow(0u, false); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(0u, false); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(Texture2DFormatScene::EState_R8, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.unpublish(sceneId); testRenderer.destroyDisplay(display); @@ -569,16 +570,16 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, DoesNotRenderToFramebufferInLoopModeUpdateOnly) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(0u); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(0u); + ASSERT_TRUE(display != displayId_t::Invalid()); - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); testRenderer.readPixels(display, 0u, 0u, WindowWidth, WindowHeight); testRenderer.flushRenderer(); testRenderer.doOneLoop(); ReadPixelCallbackHandler callbackHandler; - ramses::RendererSceneControlEventHandlerEmpty dummy; + RendererSceneControlEventHandlerEmpty dummy; testRenderer.dispatchEvents(callbackHandler, dummy); ASSERT_FALSE(callbackHandler.m_pixelDataRead); @@ -587,25 +588,25 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, Republish_ThenChangeScene) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Three_Triangles")); testScenesAndRenderer.unpublish(sceneId); - ASSERT_TRUE(testScenesAndRenderer.getTestRenderer().waitForSceneStateChange(sceneId, ramses::RendererSceneState::Unavailable)); + ASSERT_TRUE(testScenesAndRenderer.waitForSceneStateChange(sceneId, RendererSceneState::Unavailable)); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, MultipleTrianglesScene::TRIANGLES_REORDERED); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererInstance_Triangles_reordered")); testScenesAndRenderer.unpublish(sceneId); @@ -615,11 +616,14 @@ namespace ramses_internal class ReferencedSceneStateEventHandler final : public TestClientEventHandler { public: - using ReferenceStateMap = std::unordered_map; + using ReferenceStateMap = std::unordered_map; - explicit ReferencedSceneStateEventHandler(ReferenceStateMap&& targetStateMap) : m_targetStateMap{ std::move(targetStateMap) } {} + explicit ReferencedSceneStateEventHandler(ReferenceStateMap&& targetStateMap, TestScenes& scenes) + : m_targetStateMap{ std::move(targetStateMap) } + , m_scenes{ scenes } + {} - void sceneReferenceStateChanged(ramses::SceneReference& sceneRef, ramses::RendererSceneState state) override + void sceneReferenceStateChanged(SceneReference& sceneRef, RendererSceneState state) override { m_currentStateMap[sceneRef.getReferencedSceneId()] = state; } @@ -627,30 +631,37 @@ namespace ramses_internal { return m_currentStateMap == m_targetStateMap; } + void onUpdate() override + { + for (const auto& s : m_targetStateMap) + m_scenes.getScene(s.first).flush(); + } + private: const ReferenceStateMap m_targetStateMap; ReferenceStateMap m_currentStateMap; + TestScenes& m_scenes; }; TEST_F(ARendererLifecycleTest, ReferencedScenesWithDataLinkAndRenderOrder) { testScenesAndRenderer.initializeRenderer(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); // simulate referenced content published - const ramses::sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); - const ramses::sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); + const sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneRefId1); testScenesAndRenderer.publish(sceneRefId2); testScenesAndRenderer.flush(sceneRefId1); testScenesAndRenderer.flush(sceneRefId2); - const ramses::sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); auto sceneRef1 = masterScene.createSceneReference(sceneRefId1); auto sceneRef2 = masterScene.createSceneReference(sceneRefId2); - sceneRef1->requestState(ramses::RendererSceneState::Rendered); - sceneRef2->requestState(ramses::RendererSceneState::Rendered); + sceneRef1->requestState(RendererSceneState::Rendered); + sceneRef2->requestState(RendererSceneState::Rendered); sceneRef1->setRenderOrder(1); sceneRef2->setRenderOrder(2); testScenesAndRenderer.publish(sceneMasterId); @@ -658,10 +669,10 @@ namespace ramses_internal // get master to ready testRenderer.setSceneMapping(sceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Ready)); // get ref scenes to ready (requested rendered but blocked by master) - ReferencedSceneStateEventHandler handler({ { sceneRefId1, ramses::RendererSceneState::Ready }, { sceneRefId2, ramses::RendererSceneState::Ready } }); + ReferencedSceneStateEventHandler handler({ { sceneRefId1, RendererSceneState::Ready }, { sceneRefId2, RendererSceneState::Ready } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(handler)); // nothing rendered @@ -672,7 +683,7 @@ namespace ramses_internal testScenesAndRenderer.flush(sceneMasterId); // now all rendered - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ReferencedScenes")); // kill master, nothing rendered @@ -688,22 +699,22 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); testRenderer.startRendererThread(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); // simulate referenced content published - const ramses::sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); - const ramses::sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); + const sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneRefId1); testScenesAndRenderer.publish(sceneRefId2); testScenesAndRenderer.flush(sceneRefId1); testScenesAndRenderer.flush(sceneRefId2); - const ramses::sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); auto sceneRef1 = masterScene.createSceneReference(sceneRefId1); auto sceneRef2 = masterScene.createSceneReference(sceneRefId2); - sceneRef1->requestState(ramses::RendererSceneState::Ready); - sceneRef2->requestState(ramses::RendererSceneState::Ready); + sceneRef1->requestState(RendererSceneState::Ready); + sceneRef2->requestState(RendererSceneState::Ready); sceneRef1->setRenderOrder(1); sceneRef2->setRenderOrder(2); testScenesAndRenderer.publish(sceneMasterId); @@ -711,7 +722,7 @@ namespace ramses_internal // get master to rendered so refs can request also rendered testRenderer.setSceneMapping(sceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Rendered)); testScenesAndRenderer.flush(sceneMasterId); // get ref scenes to rendered @@ -721,24 +732,38 @@ namespace ramses_internal class EventHandler final : public TestClientEventHandler { public: - EventHandler(TestScenesAndRenderer& tester_, ramses::sceneId_t sceneMasterId_) : m_tester(tester_), m_sceneMasterId(sceneMasterId_) {} - void sceneReferenceStateChanged(ramses::SceneReference& sceneRef, ramses::RendererSceneState state) override + EventHandler(TestScenesAndRenderer& tester_, sceneId_t sceneMasterId_, std::initializer_list refScenes) + : m_tester(tester_) + , m_sceneMasterId(sceneMasterId_) + { + for (const auto s : refScenes) + m_states[s] = RendererSceneState::Available; + } + + void sceneReferenceStateChanged(SceneReference& sceneRef, RendererSceneState state) override { - if (state == ramses::RendererSceneState::Ready) - sceneRef.requestState(ramses::RendererSceneState::Rendered); + if (state == RendererSceneState::Ready) + sceneRef.requestState(RendererSceneState::Rendered); m_tester.flush(m_sceneMasterId); m_states[sceneRef.getReferencedSceneId()] = state; } bool waitCondition() const override { return 2u == m_states.size() - && std::all_of(m_states.cbegin(), m_states.cend(), [](const auto& it) { return it.second == ramses::RendererSceneState::Rendered; }); + && std::all_of(m_states.cbegin(), m_states.cend(), [](const auto& it) { return it.second == RendererSceneState::Rendered; }); } + void onUpdate() override + { + m_tester.flush(m_sceneMasterId); + for (const auto& s : m_states) + m_tester.flush(s.first); + } + private: TestScenesAndRenderer& m_tester; - ramses::sceneId_t m_sceneMasterId; - std::unordered_map m_states; - } handler(testScenesAndRenderer, sceneMasterId); + sceneId_t m_sceneMasterId; + std::unordered_map m_states; + } handler(testScenesAndRenderer, sceneMasterId, { sceneRefId1, sceneRefId2 }); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(handler)); // link data @@ -761,31 +786,31 @@ namespace ramses_internal class ReferencedSceneFlushEventHandler : public TestClientEventHandler { public: - void sceneReferenceFlushed(ramses::SceneReference&, ramses::sceneVersionTag_t versionTag) override + void sceneReferenceFlushed(SceneReference& /*unused*/, sceneVersionTag_t versionTag) override { m_lastReportedVersion = versionTag; } [[nodiscard]] bool waitCondition() const override { - return m_lastReportedVersion != ramses::InvalidSceneVersionTag; + return m_lastReportedVersion != InvalidSceneVersionTag; } - ramses::sceneVersionTag_t m_lastReportedVersion = ramses::InvalidSceneVersionTag; + sceneVersionTag_t m_lastReportedVersion = InvalidSceneVersionTag; }; TEST_F(ARendererLifecycleTest, ReferencedSceneReportsVersionedFlush) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); // simulate referenced content published - const ramses::sceneId_t sceneRefId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneRefId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneRefId); testScenesAndRenderer.flush(sceneRefId); - const ramses::sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); auto sceneRef = masterScene.createSceneReference(sceneRefId); - sceneRef->requestState(ramses::RendererSceneState::Ready); + sceneRef->requestState(RendererSceneState::Ready); testScenesAndRenderer.publish(sceneMasterId); testScenesAndRenderer.flush(sceneMasterId); @@ -795,13 +820,13 @@ namespace ramses_internal // get master to ready testRenderer.setSceneMapping(sceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Ready)); // get ref scenes to ready - ReferencedSceneStateEventHandler stateHandler({ { sceneRefId, ramses::RendererSceneState::Ready } }); + ReferencedSceneStateEventHandler stateHandler({ { sceneRefId, RendererSceneState::Ready } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(stateHandler)); - constexpr ramses::sceneVersionTag_t version1{ 123u }; + constexpr sceneVersionTag_t version1{ 123u }; testScenesAndRenderer.flush(sceneRefId, version1); // wait for versioned flush reported back to client @@ -810,9 +835,9 @@ namespace ramses_internal EXPECT_EQ(version1, flushHandler.m_lastReportedVersion); // another version - constexpr ramses::sceneVersionTag_t version2{ 666u }; + constexpr sceneVersionTag_t version2{ 666u }; testScenesAndRenderer.flush(sceneRefId, version2); - flushHandler.m_lastReportedVersion = ramses::InvalidSceneVersionTag; + flushHandler.m_lastReportedVersion = InvalidSceneVersionTag; EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(flushHandler)); EXPECT_EQ(version2, flushHandler.m_lastReportedVersion); @@ -824,29 +849,29 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, ReferencedSceneReportsLastAppliedVersionWhenEnablingNotifications) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); // simulate referenced content published - const ramses::sceneId_t sceneRefId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneRefId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneRefId); testScenesAndRenderer.flush(sceneRefId); - const ramses::sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); auto sceneRef = masterScene.createSceneReference(sceneRefId); - sceneRef->requestState(ramses::RendererSceneState::Ready); + sceneRef->requestState(RendererSceneState::Ready); testScenesAndRenderer.publish(sceneMasterId); testScenesAndRenderer.flush(sceneMasterId); // get master to ready testRenderer.setSceneMapping(sceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Ready)); // get ref scenes to ready - ReferencedSceneStateEventHandler stateHandler({ { sceneRefId, ramses::RendererSceneState::Ready } }); + ReferencedSceneStateEventHandler stateHandler({ { sceneRefId, RendererSceneState::Ready } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(stateHandler)); - constexpr ramses::sceneVersionTag_t version{ 123u }; + constexpr sceneVersionTag_t version{ 123u }; testScenesAndRenderer.flush(sceneRefId, version); // loop few times to make sure flush was applied // Sceneref version flushes are not reported to renderer so there is no way to check, @@ -858,7 +883,7 @@ namespace ramses_internal // loop few times, expect no flush version reported to client side -> wait condition not met ReferencedSceneFlushEventHandler flushHandler; EXPECT_FALSE(testScenesAndRenderer.loopTillClientEvent(flushHandler)); - EXPECT_EQ(ramses::InvalidSceneVersionTag, flushHandler.m_lastReportedVersion); + EXPECT_EQ(InvalidSceneVersionTag, flushHandler.m_lastReportedVersion); // only now enable receiving of applied versioned flushes sceneRef->requestNotificationsForSceneVersionTags(true); @@ -876,11 +901,11 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, ReferencedSceneDestroyedAndReusedInAnotherMasterScene) { testScenesAndRenderer.initializeRenderer(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); - const ramses::sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); - const ramses::sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); - const ramses::sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); + const sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); // get master and referenced scenes rendered { @@ -893,8 +918,8 @@ namespace ramses_internal auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); auto sceneRef1 = masterScene.createSceneReference(sceneRefId1, "REF1"); auto sceneRef2 = masterScene.createSceneReference(sceneRefId2, "REF2"); - sceneRef1->requestState(ramses::RendererSceneState::Rendered); - sceneRef2->requestState(ramses::RendererSceneState::Rendered); + sceneRef1->requestState(RendererSceneState::Rendered); + sceneRef2->requestState(RendererSceneState::Rendered); sceneRef1->setRenderOrder(1); sceneRef2->setRenderOrder(2); testScenesAndRenderer.publish(sceneMasterId); @@ -902,10 +927,10 @@ namespace ramses_internal // get master to ready testRenderer.setSceneMapping(sceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Ready)); // get ref scenes to ready (requested rendered but blocked by master) - ReferencedSceneStateEventHandler handler({ { sceneRefId1, ramses::RendererSceneState::Ready }, { sceneRefId2, ramses::RendererSceneState::Ready } }); + ReferencedSceneStateEventHandler handler({ { sceneRefId1, RendererSceneState::Ready }, { sceneRefId2, RendererSceneState::Ready } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(handler)); // nothing rendered @@ -916,7 +941,7 @@ namespace ramses_internal testScenesAndRenderer.flush(sceneMasterId); // now all rendered - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ReferencedScenes")); } @@ -924,12 +949,12 @@ namespace ramses_internal { // first ramp down the scenes' states auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); - auto& sceneRef1 = ramses::RamsesObjectTypeUtils::ConvertTo(*masterScene.findObjectByName("REF1")); - auto& sceneRef2 = ramses::RamsesObjectTypeUtils::ConvertTo(*masterScene.findObjectByName("REF2")); - sceneRef1.requestState(ramses::RendererSceneState::Available); - sceneRef2.requestState(ramses::RendererSceneState::Available); + auto& sceneRef1 = *masterScene.findObject("REF1"); + auto& sceneRef2 = *masterScene.findObject("REF2"); + sceneRef1.requestState(RendererSceneState::Available); + sceneRef2.requestState(RendererSceneState::Available); testScenesAndRenderer.flush(sceneMasterId); - ReferencedSceneStateEventHandler stateHandler({ { sceneRefId1, ramses::RendererSceneState::Available }, { sceneRefId2, ramses::RendererSceneState::Available } }); + ReferencedSceneStateEventHandler stateHandler({ { sceneRefId1, RendererSceneState::Available }, { sceneRefId2, RendererSceneState::Available } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(stateHandler)); masterScene.destroy(sceneRef1); @@ -939,15 +964,15 @@ namespace ramses_internal } // create another master (same content as original master) - const ramses::sceneId_t otherSceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t otherSceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); // reference existing scenes by new master and get all rendered { auto& otherMasterScene = testScenesAndRenderer.getScenesRegistry().getScene(otherSceneMasterId); auto sceneRef1 = otherMasterScene.createSceneReference(sceneRefId1); auto sceneRef2 = otherMasterScene.createSceneReference(sceneRefId2); - sceneRef1->requestState(ramses::RendererSceneState::Rendered); - sceneRef2->requestState(ramses::RendererSceneState::Rendered); + sceneRef1->requestState(RendererSceneState::Rendered); + sceneRef2->requestState(RendererSceneState::Rendered); sceneRef1->setRenderOrder(1); sceneRef2->setRenderOrder(2); testScenesAndRenderer.publish(otherSceneMasterId); @@ -955,10 +980,10 @@ namespace ramses_internal // get master to ready testRenderer.setSceneMapping(otherSceneMasterId, display); - testRenderer.getSceneToState(otherSceneMasterId, ramses::RendererSceneState::Ready); + testScenesAndRenderer.getSceneToState(otherSceneMasterId, RendererSceneState::Ready); // get ref scenes to ready (requested rendered but blocked by master) - ReferencedSceneStateEventHandler handler({ { sceneRefId1, ramses::RendererSceneState::Ready }, { sceneRefId2, ramses::RendererSceneState::Ready } }); + ReferencedSceneStateEventHandler handler({ { sceneRefId1, RendererSceneState::Ready }, { sceneRefId2, RendererSceneState::Ready } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(handler)); // nothing rendered @@ -969,7 +994,7 @@ namespace ramses_internal testScenesAndRenderer.flush(otherSceneMasterId); // now all rendered - testRenderer.getSceneToState(otherSceneMasterId, ramses::RendererSceneState::Rendered); + testScenesAndRenderer.getSceneToState(otherSceneMasterId, RendererSceneState::Rendered); ASSERT_TRUE(checkScreenshot(display, "ReferencedScenes")); } @@ -983,11 +1008,11 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, ReferencedSceneDestroyedAndReusedInAnotherMasterScene_whileRendered) { testScenesAndRenderer.initializeRenderer(); - ramses::displayId_t display = createDisplayForWindow(); + displayId_t display = createDisplayForWindow(); - const ramses::sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); - const ramses::sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); - const ramses::sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); + const sceneId_t sceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t sceneRefId1 = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneRefId2 = createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, glm::vec3(-1.5f, 0.0f, 5.0f)); // get master and referenced scenes rendered { @@ -1000,8 +1025,8 @@ namespace ramses_internal auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); auto sceneRef1 = masterScene.createSceneReference(sceneRefId1, "REF1"); auto sceneRef2 = masterScene.createSceneReference(sceneRefId2, "REF2"); - sceneRef1->requestState(ramses::RendererSceneState::Rendered); - sceneRef2->requestState(ramses::RendererSceneState::Rendered); + sceneRef1->requestState(RendererSceneState::Rendered); + sceneRef2->requestState(RendererSceneState::Rendered); sceneRef1->setRenderOrder(1); sceneRef2->setRenderOrder(2); testScenesAndRenderer.publish(sceneMasterId); @@ -1009,10 +1034,10 @@ namespace ramses_internal // get master to ready testRenderer.setSceneMapping(sceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Ready)); // get ref scenes to ready (requested rendered but blocked by master) - ReferencedSceneStateEventHandler handler({ { sceneRefId1, ramses::RendererSceneState::Ready }, { sceneRefId2, ramses::RendererSceneState::Ready } }); + ReferencedSceneStateEventHandler handler({ { sceneRefId1, RendererSceneState::Ready }, { sceneRefId2, RendererSceneState::Ready } }, testScenesAndRenderer.getScenesRegistry()); EXPECT_TRUE(testScenesAndRenderer.loopTillClientEvent(handler)); // nothing rendered @@ -1023,7 +1048,7 @@ namespace ramses_internal testScenesAndRenderer.flush(sceneMasterId); // now all rendered - ASSERT_TRUE(testRenderer.getSceneToState(sceneMasterId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneMasterId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ReferencedScenes")); } @@ -1031,8 +1056,8 @@ namespace ramses_internal { // just unref, dont ramp down any of the rendering states auto& masterScene = testScenesAndRenderer.getScenesRegistry().getScene(sceneMasterId); - auto& sceneRef1 = ramses::RamsesObjectTypeUtils::ConvertTo(*masterScene.findObjectByName("REF1")); - auto& sceneRef2 = ramses::RamsesObjectTypeUtils::ConvertTo(*masterScene.findObjectByName("REF2")); + auto& sceneRef1 = *masterScene.findObject("REF1"); + auto& sceneRef2 = *masterScene.findObject("REF2"); masterScene.destroy(sceneRef1); masterScene.destroy(sceneRef2); testScenesAndRenderer.flush(sceneMasterId); @@ -1041,7 +1066,7 @@ namespace ramses_internal } // create another master (same content as original master) - const ramses::sceneId_t otherSceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); + const sceneId_t otherSceneMasterId = createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT); // reference existing scenes by new master and get all rendered { @@ -1049,14 +1074,14 @@ namespace ramses_internal testScenesAndRenderer.publish(otherSceneMasterId); testScenesAndRenderer.flush(otherSceneMasterId); testRenderer.setSceneMapping(otherSceneMasterId, display); - ASSERT_TRUE(testRenderer.getSceneToState(otherSceneMasterId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(otherSceneMasterId, RendererSceneState::Rendered)); // reference scenes again, now from new master auto& otherMasterScene = testScenesAndRenderer.getScenesRegistry().getScene(otherSceneMasterId); auto sceneRef1 = otherMasterScene.createSceneReference(sceneRefId1); auto sceneRef2 = otherMasterScene.createSceneReference(sceneRefId2); - sceneRef1->requestState(ramses::RendererSceneState::Rendered); - sceneRef2->requestState(ramses::RendererSceneState::Rendered); + sceneRef1->requestState(RendererSceneState::Rendered); + sceneRef2->requestState(RendererSceneState::Rendered); sceneRef1->setRenderOrder(1); sceneRef2->setRenderOrder(2); testScenesAndRenderer.flush(otherSceneMasterId); @@ -1065,8 +1090,8 @@ namespace ramses_internal ASSERT_TRUE(checkScreenshot(display, "ReferencedScenes")); // now change state of ref scenes while referenced by new master - sceneRef1->requestState(ramses::RendererSceneState::Ready); - sceneRef2->requestState(ramses::RendererSceneState::Ready); + sceneRef1->requestState(RendererSceneState::Ready); + sceneRef2->requestState(RendererSceneState::Ready); testScenesAndRenderer.flush(otherSceneMasterId); // nothing rendered @@ -1167,19 +1192,19 @@ namespace ramses_internal testScenesAndRenderer.initializeRenderer(rendererConfig); - const ramses::displayId_t display1 = createDisplayForWindow(0u, true); + const displayId_t display1 = createDisplayForWindow(0u, true); createDisplayForWindow(1u, true); //nothing gets rendered on it, so it is ALWAYS ready (except right after clearing) if (testRenderer.hasSystemCompositorController()) { ASSERT_TRUE(checkScreenshot(display1, "ARendererDisplays_Black")); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display1); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display1, "ARendererInstance_Three_Triangles")); @@ -1202,18 +1227,18 @@ namespace ramses_internal testScenesAndRenderer.initializeRenderer(rendererConfig); createDisplayForWindow(0u, true); //nothing gets rendered on it, so it is ALWAYS ready (except right after clearing) - const ramses::displayId_t display2 = createDisplayForWindow(1u, true); + const displayId_t display2 = createDisplayForWindow(1u, true); if (testRenderer.hasSystemCompositorController()) { ASSERT_TRUE(checkScreenshot(display2, "ARendererDisplays_Black")); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display2); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display2, "ARendererInstance_Three_Triangles")); @@ -1230,13 +1255,13 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneNotExpiredWhenUpdatedAndSubscribed) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Available)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Available)); for (int i = 0; i < 5; ++i) { @@ -1253,13 +1278,13 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneExpiredWhenSubscribed) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Ready)); testRenderer.doOneLoop(); // next flush expired already in past to trigger the exceeded event @@ -1275,15 +1300,15 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneExpiredAndRecoveredWhenSubscribed) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); + const displayId_t display = createDisplayForWindow(); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.setExpirationTimestamp(sceneId, FlushTime::Clock::now() + std::chrono::hours(1)); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Ready)); testRenderer.doOneLoop(); // next flush will be in past to trigger the exceeded event @@ -1305,14 +1330,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneNotExpiredWhenUpdatedAndRendered) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testRenderer.doOneLoop(); // send flushes and render within limit @@ -1333,14 +1358,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneNotExpiredWhenUpdatedWithEmptyFlushesAndRendered) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testRenderer.doOneLoop(); // send flushes and render within limit @@ -1360,14 +1385,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneExpiredWhenRendered) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testRenderer.doOneLoop(); // set expiration of content that will be rendered and eventually will expire @@ -1376,7 +1401,7 @@ namespace ramses_internal testRenderer.doOneLoop(); // send flushes within limit but do not render - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); for (int i = 0; i < 5; ++i) { // make modifications to scene @@ -1395,14 +1420,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneExpiredWhenRenderedAndRecoveredAfterHidden) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testRenderer.doOneLoop(); // set expiration of content that will be rendered and eventually will expire @@ -1411,7 +1436,7 @@ namespace ramses_internal testRenderer.doOneLoop(); // send flushes within limit but do not render - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); for (int i = 0; i < 5; ++i) { // make modifications to scene @@ -1430,7 +1455,7 @@ namespace ramses_internal testRenderer.doOneLoop(); // now hide scene so regular flushes are enough to recover - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Ready)); for (int i = 0; i < 5; ++i) { // make modifications to scene @@ -1446,9 +1471,9 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneExpirationCanBeDisabled_ConfidenceTest) { - struct ExpirationCounter final : public ramses::RendererSceneControlEventHandlerEmpty + struct ExpirationCounter final : public RendererSceneControlEventHandlerEmpty { - void sceneExpired(ramses::sceneId_t) override + void sceneExpired(sceneId_t /*sceneId*/) override { numExpirationEvents++; } @@ -1456,14 +1481,14 @@ namespace ramses_internal } expirationCounter; testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testRenderer.doOneLoop(); // set expiration of content that will be rendered @@ -1480,12 +1505,12 @@ namespace ramses_internal testScenesAndRenderer.flush(sceneId); testRenderer.doOneLoop(); } - ramses::RendererEventHandlerEmpty dummy; + RendererEventHandlerEmpty dummy; testRenderer.dispatchEvents(dummy, expirationCounter); ASSERT_TRUE(expirationCounter.numExpirationEvents == 0u); // now hide scene - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Ready)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Ready)); // send few more flushes within limit and no changes for (int i = 0; i < 3; ++i) @@ -1522,14 +1547,14 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, SceneExpiredAndRecoveredWhenRendered) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testRenderer.doOneLoop(); // set expiration of content that will be rendered and eventually will expire @@ -1538,7 +1563,7 @@ namespace ramses_internal testRenderer.doOneLoop(); // send flushes within limit but do not render - testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + testRenderer.setLoopMode(ELoopMode::UpdateOnly); for (int i = 0; i < 5; ++i) { // make modifications to scene @@ -1551,7 +1576,7 @@ namespace ramses_internal ASSERT_TRUE(testRenderer.checkScenesExpired({ sceneId })); // now also render within limit to recover - testRenderer.setLoopMode(ramses::ELoopMode::UpdateAndRender); + testRenderer.setLoopMode(ELoopMode::UpdateAndRender); for (int i = 0; i < 5; ++i) { // make modifications to scene @@ -1568,19 +1593,19 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, ScenesExpireOneAfterAnother) { testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(displayId_t::Invalid() != display); - const ramses::sceneId_t sceneId1 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); - const ramses::sceneId_t sceneId2 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId1 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); + const sceneId_t sceneId2 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(-0.50f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId1); testScenesAndRenderer.publish(sceneId2); testScenesAndRenderer.flush(sceneId1); testScenesAndRenderer.flush(sceneId2); testRenderer.setSceneMapping(sceneId1, display); testRenderer.setSceneMapping(sceneId2, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered)); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered)); testRenderer.doOneLoop(); testScenesAndRenderer.setExpirationTimestamp(sceneId1, FlushTime::Clock::now() + std::chrono::milliseconds(500)); @@ -1645,13 +1670,13 @@ namespace ramses_internal { testScenesAndRenderer.initializeRenderer(); const auto display = createDisplayForWindow(); - ASSERT_TRUE(ramses::displayId_t::Invalid() != display); + ASSERT_TRUE(displayId_t::Invalid() != display); const auto sceneId = createScene(VisibilityScene::VISIBILITY_ALL_OFF, glm::vec3(0.f, 1.0f, 5.0f)); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display, "ARendererDisplays_Black")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_OFF_AND_INVISIBLE); @@ -1660,35 +1685,35 @@ namespace ramses_internal ASSERT_TRUE(checkScreenshot(display, "ARendererDisplays_Black")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_OFF_AND_VISIBLE); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{1u}); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{1u}); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{1u}); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{1u}); testRenderer.doOneLoop(); ASSERT_TRUE(checkScreenshot(display, "VisibilityScene_Partial")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_ALL_VISIBLE); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{2u}); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{2u}); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{2u}); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{2u}); ASSERT_TRUE(checkScreenshot(display, "VisibilityScene_On")); // another cycle of OFF to ON testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_ALL_OFF); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{3u}); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{3u}); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{3u}); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{3u}); ASSERT_TRUE(checkScreenshot(display, "ARendererDisplays_Black")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_OFF_AND_INVISIBLE); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{4u}); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{4u}); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{4u}); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{4u}); ASSERT_TRUE(checkScreenshot(display, "ARendererDisplays_Black")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_OFF_AND_VISIBLE); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{5u}); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{5u}); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{5u}); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{5u}); ASSERT_TRUE(checkScreenshot(display, "VisibilityScene_Partial")); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, VisibilityScene::VISIBILITY_ALL_VISIBLE); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{6u}); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{6u}); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{6u}); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{6u}); ASSERT_TRUE(checkScreenshot(display, "VisibilityScene_On")); testScenesAndRenderer.destroyRenderer(); @@ -1696,15 +1721,15 @@ namespace ramses_internal TEST_F(ARendererLifecycleTest, canDestroyAndRecreateRendererOnSameFramework) { - const ramses::sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display_1 = createDisplayForWindow(); - ASSERT_TRUE(display_1 != ramses::displayId_t::Invalid()); + const displayId_t display_1 = createDisplayForWindow(); + ASSERT_TRUE(display_1 != displayId_t::Invalid()); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display_1); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display_1, "ARendererInstance_Three_Triangles")); @@ -1712,11 +1737,11 @@ namespace ramses_internal testScenesAndRenderer.destroyRenderer(); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display_2 = createDisplayForWindow(); - ASSERT_TRUE(display_2 != ramses::displayId_t::Invalid()); + const displayId_t display_2 = createDisplayForWindow(); + ASSERT_TRUE(display_2 != displayId_t::Invalid()); testRenderer.setSceneMapping(sceneId, display_2); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); ASSERT_TRUE(checkScreenshot(display_2, "ARendererInstance_Three_Triangles")); @@ -1729,19 +1754,19 @@ namespace ramses_internal // Test dirtiness of VAO is handled correctly in case renderable stays dirty after geometry // resources are ready, e.g., in case VAO upload is blocked till other renderable resources // get uploaded - const ramses::sceneId_t sceneId = testScenesAndRenderer.getScenesRegistry().createScene(TextureSamplerScene::EState_NoTextureSampler, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId = testScenesAndRenderer.getScenesRegistry().createScene(TextureSamplerScene::EState_NoTextureSampler, glm::vec3(0.0f, 0.0f, 5.0f)); testScenesAndRenderer.initializeRenderer(); - const ramses::displayId_t display = createDisplayForWindow(); - ASSERT_TRUE(display != ramses::displayId_t::Invalid()); + const displayId_t display = createDisplayForWindow(); + ASSERT_TRUE(display != displayId_t::Invalid()); testScenesAndRenderer.publish(sceneId); testScenesAndRenderer.flush(sceneId); testRenderer.setSceneMapping(sceneId, display); - ASSERT_TRUE(testRenderer.getSceneToState(sceneId, ramses::RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId, RendererSceneState::Rendered)); testScenesAndRenderer.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetTextureSampler); - testScenesAndRenderer.flush(sceneId, ramses::sceneVersionTag_t{ 1u }); - testRenderer.waitForFlush(sceneId, ramses::sceneVersionTag_t{ 1u }); + testScenesAndRenderer.flush(sceneId, sceneVersionTag_t{ 1u }); + testRenderer.waitForFlush(sceneId, sceneVersionTag_t{ 1u }); testScenesAndRenderer.unpublish(sceneId); testScenesAndRenderer.destroyRenderer(); @@ -1776,4 +1801,160 @@ namespace ramses_internal testScenesAndRenderer.destroyRenderer(); } + +#ifdef ramses_sdk_ENABLE_WINDOW_TYPE_WINDOWS + TEST_F(ARendererLifecycleTest, CanCreateMultipleDisplaysWithDifferentDeviceTypes_GLES30_GL42) + { + const sceneId_t sceneId1 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId2 = createScene(MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f)); + testScenesAndRenderer.initializeRenderer(); + + ramses::DisplayConfig displayConfig1; + displayConfig1.setDeviceType(EDeviceType::GLES_3_0); + displayConfig1.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); + ramses::DisplayConfig displayConfig2; + displayConfig2.setDeviceType(EDeviceType::GL_4_2); + displayConfig2.setWindowRectangle(WindowX + WindowWidth, WindowY, WindowWidth, WindowHeight); + + const displayId_t display1 = testRenderer.createDisplay(displayConfig1); + const displayId_t display2 = testRenderer.createDisplay(displayConfig2); + ASSERT_TRUE(displayId_t::Invalid() != display1); + ASSERT_TRUE(displayId_t::Invalid() != display2); + + testScenesAndRenderer.publish(sceneId1); + testScenesAndRenderer.publish(sceneId2); + testScenesAndRenderer.flush(sceneId1); + testScenesAndRenderer.flush(sceneId2); + testRenderer.setSceneMapping(sceneId1, display1); + testRenderer.setSceneMapping(sceneId2, display2); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered)); + + ASSERT_TRUE(checkScreenshot(display1, "ARendererInstance_Three_Triangles")); + ASSERT_TRUE(checkScreenshot(display2, "ARendererInstance_Triangles_reordered")); + + testScenesAndRenderer.unpublish(sceneId1); + testScenesAndRenderer.unpublish(sceneId2); + testScenesAndRenderer.destroyRenderer(); + } + + TEST_F(ARendererLifecycleTest, CanCreateMultipleDisplaysWithDifferentDeviceTypes_GLES_GL42_GL45) + { + const sceneId_t sceneId1 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId2 = createScene(MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId3 = createScene(MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f)); + testScenesAndRenderer.initializeRenderer(); + + ramses::DisplayConfig displayConfig1; + displayConfig1.setDeviceType(EDeviceType::GLES_3_0); + displayConfig1.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); + ramses::DisplayConfig displayConfig2; + displayConfig2.setDeviceType(EDeviceType::GL_4_2); + displayConfig2.setWindowRectangle(WindowX + WindowWidth, WindowY, WindowWidth, WindowHeight); + ramses::DisplayConfig displayConfig3; + displayConfig3.setDeviceType(EDeviceType::GL_4_5); + displayConfig3.setWindowRectangle(WindowX, WindowY + WindowHeight, WindowWidth, WindowHeight); + + const displayId_t display1 = testRenderer.createDisplay(displayConfig1); + const displayId_t display2 = testRenderer.createDisplay(displayConfig2); + const displayId_t display3 = testRenderer.createDisplay(displayConfig3); + ASSERT_TRUE(displayId_t::Invalid() != display1); + ASSERT_TRUE(displayId_t::Invalid() != display2); + ASSERT_TRUE(displayId_t::Invalid() != display3); + + testScenesAndRenderer.publish(sceneId1); + testScenesAndRenderer.publish(sceneId2); + testScenesAndRenderer.publish(sceneId3); + testScenesAndRenderer.flush(sceneId1); + testScenesAndRenderer.flush(sceneId2); + testScenesAndRenderer.flush(sceneId3); + testRenderer.setSceneMapping(sceneId1, display1); + testRenderer.setSceneMapping(sceneId2, display2); + testRenderer.setSceneMapping(sceneId3, display3); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered)); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId3, RendererSceneState::Rendered)); + + ASSERT_TRUE(checkScreenshot(display1, "ARendererInstance_Three_Triangles")); + ASSERT_TRUE(checkScreenshot(display2, "ARendererInstance_Triangles_reordered")); + ASSERT_TRUE(checkScreenshot(display3, "ARendererInstance_Triangles_reordered")); + + testScenesAndRenderer.unpublish(sceneId1); + testScenesAndRenderer.unpublish(sceneId2); + testScenesAndRenderer.unpublish(sceneId3); + testScenesAndRenderer.destroyRenderer(); + } +#endif + + TEST_F(ARendererLifecycleTest, CanCreateMultipleDisplaysWithDifferentWindowTypes) + { + const sceneId_t sceneId1 = createScene(MultipleTrianglesScene::THREE_TRIANGLES, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId2 = createScene(MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f)); + const sceneId_t sceneId3 = createScene(MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.0f, 0.0f, 5.0f)); + testScenesAndRenderer.initializeRenderer(); + + testScenesAndRenderer.publish(sceneId1); + testScenesAndRenderer.publish(sceneId2); + testScenesAndRenderer.publish(sceneId3); + testScenesAndRenderer.flush(sceneId1); + testScenesAndRenderer.flush(sceneId2); + testScenesAndRenderer.flush(sceneId3); + + displayId_t display1; + displayId_t display2; + displayId_t display3; + +#ifdef ramses_sdk_ENABLE_WINDOW_TYPE_X11 + ramses::DisplayConfig displayConfig1; + displayConfig1.setWindowType(EWindowType::X11); + displayConfig1.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); + display1 = testRenderer.createDisplay(displayConfig1); + ASSERT_TRUE(displayId_t::Invalid() != display1); + testRenderer.setSceneMapping(sceneId1, display1); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId1, RendererSceneState::Rendered)); +#endif + +#ifdef ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL + ramses::DisplayConfig displayConfig2; + displayConfig2.setWindowType(EWindowType::Wayland_Shell); + displayConfig2.setWindowRectangle(WindowX + WindowWidth, WindowY, WindowWidth, WindowHeight); + display2 = testRenderer.createDisplay(displayConfig2); + ASSERT_TRUE(displayId_t::Invalid() != display2); + testRenderer.setSceneMapping(sceneId2, display2); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId2, RendererSceneState::Rendered)); +#endif + +#ifdef ramses_sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI + ramses::DisplayConfig displayConfig3 = RendererTestUtils::CreateTestDisplayConfig(0u, true); + // Dont create wayland ivi window, if the test is "originally" running for wayland shell + // since the compositor used for that case does not support ivi_controller + if(displayConfig3.getWindowType() != EWindowType::Wayland_Shell) + { + displayConfig3.setWindowType(EWindowType::Wayland_IVI); + displayConfig3.setWindowRectangle(WindowX, WindowY + WindowHeight, WindowWidth, WindowHeight); + display3 = testRenderer.createDisplay(displayConfig3); + ASSERT_TRUE(displayId_t::Invalid() != display3); + testRenderer.setSceneMapping(sceneId3, display3); + ASSERT_TRUE(testScenesAndRenderer.getSceneToState(sceneId3, RendererSceneState::Rendered)); + } +#endif + + if(display1.isValid()) + { + ASSERT_TRUE(checkScreenshot(display1, "ARendererInstance_Three_Triangles")); + } + if(display2.isValid()) + { + ASSERT_TRUE(checkScreenshot(display2, "ARendererInstance_Triangles_reordered")); + } + if(display3.isValid()) + { + ASSERT_TRUE(checkScreenshot(display3, "ARendererInstance_Triangles_reordered")); + } + + testScenesAndRenderer.unpublish(sceneId1); + testScenesAndRenderer.unpublish(sceneId2); + testScenesAndRenderer.unpublish(sceneId3); + testScenesAndRenderer.destroyRenderer(); + } } diff --git a/integration/SandwichTests/RendererTests/RendererLifecycleTests/RendererLifecycleTests.h b/tests/integration/renderer-tests/renderer-lifecycle-tests/RendererLifecycleTests.h similarity index 74% rename from integration/SandwichTests/RendererTests/RendererLifecycleTests/RendererLifecycleTests.h rename to tests/integration/renderer-tests/renderer-lifecycle-tests/RendererLifecycleTests.h index 42670c42e..11e0e438e 100644 --- a/integration/SandwichTests/RendererTests/RendererLifecycleTests/RendererLifecycleTests.h +++ b/tests/integration/renderer-tests/renderer-lifecycle-tests/RendererLifecycleTests.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERLIFECYCLETESTS_H -#define RAMSES_RENDERERLIFECYCLETESTS_H +#pragma once #include "TestScenesAndRenderer.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class ARendererLifecycleTest : public ::testing::Test { @@ -23,14 +22,14 @@ namespace ramses_internal {} protected: - ramses::displayId_t createDisplayForWindow(uint32_t iviSurfaceIdOffset = 0u, bool iviWindowStartVisible = true) + displayId_t createDisplayForWindow(uint32_t iviSurfaceIdOffset = 0u, bool iviWindowStartVisible = true) { ramses::DisplayConfig displayConfig = RendererTestUtils::CreateTestDisplayConfig(iviSurfaceIdOffset, iviWindowStartVisible); displayConfig.setWindowRectangle(WindowX, WindowY, WindowWidth, WindowHeight); return testRenderer.createDisplay(displayConfig); } - testing::AssertionResult checkScreenshot(ramses::displayId_t display, const char* screenshotFile) + testing::AssertionResult checkScreenshot(displayId_t display, const char* screenshotFile) { if (testRenderer.performScreenshotCheck(display, {}, 0u, 0u, WindowWidth, WindowHeight, screenshotFile)) return testing::AssertionSuccess(); @@ -39,12 +38,12 @@ namespace ramses_internal } template - ramses::sceneId_t createScene(uint32_t state, const glm::vec3& cameraPosition = { 0.f, 0.f, 0.f }, uint32_t vpWidth = WindowWidth, uint32_t vpHeight = WindowHeight) + sceneId_t createScene(uint32_t state, const glm::vec3& cameraPosition = { 0.f, 0.f, 0.f }, uint32_t vpWidth = WindowWidth, uint32_t vpHeight = WindowHeight) { return testScenesAndRenderer.getScenesRegistry().createScene(state, cameraPosition, vpWidth, vpHeight); } template - void createScene(uint32_t state, ramses::sceneId_t sceneId, const glm::vec3& cameraPosition = { 0.f, 0.f, 0.f }) + void createScene(uint32_t state, sceneId_t sceneId, const glm::vec3& cameraPosition = { 0.f, 0.f, 0.f }) { testScenesAndRenderer.getScenesRegistry().createScene(state, sceneId, cameraPosition, WindowWidth, WindowHeight); } @@ -54,10 +53,8 @@ namespace ramses_internal static const uint32_t WindowWidth = 128u; static const uint32_t WindowHeight = 64u; - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + RamsesFrameworkConfig frameworkConfig{EFeatureLevel_Latest}; TestScenesAndRenderer testScenesAndRenderer; TestRenderer& testRenderer; }; } - -#endif diff --git a/integration/SandwichTests/RendererTests/RendererLifecycleTests/main.cpp b/tests/integration/renderer-tests/renderer-lifecycle-tests/main.cpp similarity index 88% rename from integration/SandwichTests/RendererTests/RendererLifecycleTests/main.cpp rename to tests/integration/renderer-tests/renderer-lifecycle-tests/main.cpp index 697a7463c..5b7aef781 100644 --- a/integration/SandwichTests/RendererTests/RendererLifecycleTests/main.cpp +++ b/tests/integration/renderer-tests/renderer-lifecycle-tests/main.cpp @@ -7,7 +7,7 @@ // ------------------------------------------------------------------------- #include "RendererTestUtils.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include "gmock/gmock.h" #include "ramses-cli.h" @@ -32,7 +32,7 @@ int main(int argc, char *argv[]) return -1; } CLI11_PARSE(cli, argc, argv); - RendererTestUtils::SetDefaultConfigForAllTests(rendererConfig, displayConfig); + ramses::internal::RendererTestUtils::SetDefaultConfigForAllTests(rendererConfig, displayConfig); return RUN_ALL_TESTS(); } diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/IRendererTest.h b/tests/integration/renderer-tests/renderer-test-framework/IRendererTest.h similarity index 62% rename from integration/SandwichTests/RendererTests/RendererTestFramework/IRendererTest.h rename to tests/integration/renderer-tests/renderer-test-framework/IRendererTest.h index e9ce335fa..b1a4e1429 100644 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/IRendererTest.h +++ b/tests/integration/renderer-tests/renderer-test-framework/IRendererTest.h @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_IRENDERERTEST_H -#define RAMSES_IRENDERERTEST_H +#pragma once #include "RendererTestsFramework.h" -class IRendererTest +namespace ramses::internal { -public: - virtual ~IRendererTest() {} + class IRendererTest + { + public: + virtual ~IRendererTest() = default; - virtual void setUpTestCases(RendererTestsFramework& testFramework) = 0; - virtual bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) = 0; -}; - -#endif + virtual void setUpTestCases(RendererTestsFramework& testFramework) = 0; + virtual bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) = 0; + }; +} diff --git a/tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.cpp b/tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.cpp new file mode 100644 index 000000000..26afbeb2a --- /dev/null +++ b/tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.cpp @@ -0,0 +1,626 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererTestsFramework.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "impl/DisplayConfigImpl.h" +#include "IRendererTest.h" +#include "impl/DisplayConfigImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "RendererTestUtils.h" + +namespace ramses::internal +{ + RendererTestsFramework::RendererTestsFramework(bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) + : m_generateScreenshots(generateScreenshots) + , m_testScenesAndRenderer(config) + , m_testRenderer(m_testScenesAndRenderer.getTestRenderer()) + , m_activeTestCase(nullptr) + , m_elapsedTime(0u) + { + } + + RendererTestsFramework::~RendererTestsFramework() + { + assert(m_activeTestCase == nullptr); + for(const auto& testCase : m_testCases) + { + delete testCase; + } + + destroyDisplays(); + m_testScenesAndRenderer.destroyRenderer(); + } + + void RendererTestsFramework::initializeRenderer() + { + m_testScenesAndRenderer.initializeRenderer(); + } + + void RendererTestsFramework::initializeRenderer(const ramses::RendererConfig& rendererConfig) + { + m_testScenesAndRenderer.initializeRenderer(rendererConfig); + } + + void RendererTestsFramework::destroyRenderer() + { + m_testScenesAndRenderer.destroyRenderer(); + } + + ramses::displayId_t RendererTestsFramework::createDisplay(const ramses::DisplayConfig& displayConfig) + { + m_testRenderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + const ramses::displayId_t displayId = m_testRenderer.createDisplay(displayConfig); + m_testRenderer.setLoopMode(ramses::ELoopMode::UpdateAndRender); + if (displayId != ramses::displayId_t::Invalid()) + { + m_displays.push_back({ displayId, displayConfig, {}, {} }); + } + + return displayId; + } + + ramses::displayBufferId_t RendererTestsFramework::getDisplayFramebufferId(uint32_t testDisplayIdx) const + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + return m_testRenderer.getDisplayFramebufferId(displayId); + } + + ramses::internal::TestRenderer& RendererTestsFramework::getTestRenderer() + { + return m_testRenderer; + } + + RenderingTestCase& RendererTestsFramework::createTestCase(uint32_t id, IRendererTest& rendererTest, const std::string& name) + { + auto* testCase = new RenderingTestCase(id, rendererTest, name, true); + m_testCases.push_back(testCase); + + return *testCase; + } + + RenderingTestCase& RendererTestsFramework::createTestCaseWithDefaultDisplay(uint32_t id, IRendererTest& rendererTest, const std::string& name, bool iviWindowStartVisible) + { + RenderingTestCase& testCase = createTestCase(id, rendererTest, name); + ramses::DisplayConfig displayConfig = RendererTestUtils::CreateTestDisplayConfig(0, iviWindowStartVisible); + displayConfig.setWindowRectangle(0, 0, ramses::internal::IntegrationScene::DefaultViewportWidth, ramses::internal::IntegrationScene::DefaultViewportHeight); + testCase.m_displayConfigs.push_back(displayConfig); + + return testCase; + } + + RenderingTestCase& RendererTestsFramework::createTestCaseWithoutRenderer(uint32_t id, IRendererTest &rendererTest, const std::string &name) + { + auto* testCase = new RenderingTestCase(id, rendererTest, name, false); + m_testCases.push_back(testCase); + + return *testCase; + } + + TestScenes& RendererTestsFramework::getScenesRegistry() + { + return m_testScenesAndRenderer.getScenesRegistry(); + } + + ramses::RamsesClient& RendererTestsFramework::getClient() + { + return m_testScenesAndRenderer.getClient(); + } + + void RendererTestsFramework::setSceneMapping(ramses::sceneId_t sceneId, uint32_t testDisplayIdx) + { + m_testRenderer.setSceneMapping(sceneId, m_displays[testDisplayIdx].displayId); + } + + bool RendererTestsFramework::getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + { + return m_testScenesAndRenderer.getSceneToState(sceneId, state); + } + + bool RendererTestsFramework::getSceneToRendered(ramses::sceneId_t sceneId, uint32_t testDisplayIdx) + { + setSceneMapping(sceneId, testDisplayIdx); + return getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + } + + void RendererTestsFramework::dispatchRendererEvents(ramses::IRendererEventHandler& eventHandler, ramses::IRendererSceneControlEventHandler& sceneControlEventHandler) + { + m_testRenderer.dispatchEvents(eventHandler, sceneControlEventHandler); + } + + ramses::displayBufferId_t RendererTestsFramework::createOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount, ramses::EDepthBufferType depthBufferType) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + ramses::displayBufferId_t buffer = m_testRenderer.createOffscreenBuffer(displayId, width, height, interruptible, sampleCount, depthBufferType); + m_displays[testDisplayIdx].offscreenBuffers.push_back(buffer); + return buffer; + } + + ramses::displayBufferId_t RendererTestsFramework::createDmaOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + ramses::displayBufferId_t buffer = m_testRenderer.createDmaOffscreenBuffer(displayId, width, height, bufferFourccFormat, bufferUsageFlags, modifier); + m_displays[testDisplayIdx].offscreenBuffers.push_back(buffer); + return buffer; + } + + bool RendererTestsFramework::getDmaOffscreenBufferFDAndStride(uint32_t testDisplayIdx, ramses::displayBufferId_t displayBufferId, int &fd, uint32_t &stride) const + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + return m_testRenderer.getDmaOffscreenBufferFDAndStride(displayId, displayBufferId, fd, stride); + } + + void RendererTestsFramework::destroyOffscreenBuffer(uint32_t testDisplayIdx, ramses::displayBufferId_t buffer) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + auto& offscreenBuffers = m_displays[testDisplayIdx].offscreenBuffers; + + auto bufferIter = ramses::internal::find_c(offscreenBuffers, buffer); + assert(bufferIter != offscreenBuffers.end()); + offscreenBuffers.erase(bufferIter); + + m_testRenderer.destroyOffscreenBuffer(displayId, buffer); + } + + ramses::streamBufferId_t RendererTestsFramework::createStreamBuffer(uint32_t testDisplayIdx, ramses::waylandIviSurfaceId_t source) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + ramses::streamBufferId_t buffer = m_testRenderer.createStreamBuffer(displayId, source); + m_displays[testDisplayIdx].streamBuffers.push_back(buffer); + return buffer; + } + + void RendererTestsFramework::destroyStreamBuffer(uint32_t testDisplayIdx, ramses::streamBufferId_t buffer) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + auto& streamBuffers = m_displays[testDisplayIdx].streamBuffers; + + auto bufferIter = ramses::internal::find_c(streamBuffers, buffer); + assert(bufferIter != streamBuffers.end()); + streamBuffers.erase(bufferIter); + + m_testRenderer.destroyStreamBuffer(displayId, buffer); + } + + void RendererTestsFramework::assignSceneToDisplayBuffer(ramses::sceneId_t sceneId, ramses::displayBufferId_t buffer, int32_t renderOrder) + { + m_testRenderer.assignSceneToDisplayBuffer(sceneId, buffer, renderOrder); + } + + void RendererTestsFramework::createBufferDataLink(ramses::displayBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) + { + m_testRenderer.createBufferDataLink(providerBuffer, consumerScene, consumerTag); + } + + void RendererTestsFramework::createBufferDataLink(ramses::streamBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) + { + m_testRenderer.createBufferDataLink(providerBuffer, consumerScene, consumerTag); + } + + void RendererTestsFramework::createDataLink(ramses::sceneId_t providerScene, ramses::dataProviderId_t providerTag, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) + { + m_testRenderer.createDataLink(providerScene, providerTag, consumerScene, consumerTag); + } + + void RendererTestsFramework::removeDataLink(ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) + { + m_testRenderer.removeDataLink(consumerScene, consumerTag); + } + + void RendererTestsFramework::setClearFlags(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, ramses::ClearFlags clearFlags) + { + assert(testDisplayIdx < m_displays.size()); + m_testRenderer.setClearFlags(m_displays[testDisplayIdx].displayId, ob, clearFlags); + // clearing state is persistent if display kept for next test, force re-init + m_forceDisplaysReinitForNextTestCase = true; + } + + void RendererTestsFramework::setClearColor(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, const glm::vec4& clearColor) + { + assert(testDisplayIdx < m_displays.size()); + m_testRenderer.setClearColor(m_displays[testDisplayIdx].displayId, ob, clearColor); + // clear color change is persistent if display kept for next test, force re-init + m_forceDisplaysReinitForNextTestCase = true; + } + + void RendererTestsFramework::publishAndFlushScene(ramses::sceneId_t sceneId) + { + m_testScenesAndRenderer.flush(sceneId); + m_testScenesAndRenderer.publish(sceneId); + } + + void RendererTestsFramework::flushRendererAndDoOneLoop() + { + m_testRenderer.flushRenderer(); + m_testRenderer.doOneLoop(); + } + + bool RendererTestsFramework::renderAndCompareScreenshot(const std::string& expectedImageName, uint32_t testDisplayIdx, float maxAveragePercentErrorPerPixel, bool readPixelsTwice, bool saveDiffOnError) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + const ramses::DisplayConfig& displayConfig = m_displays[testDisplayIdx].config; + + return compareScreenshotInternal( + expectedImageName, + displayId, + {}, + maxAveragePercentErrorPerPixel, + 0u, + 0u, + displayConfig.impl().getInternalDisplayConfig().getDesiredWindowWidth(), + displayConfig.impl().getInternalDisplayConfig().getDesiredWindowHeight(), + readPixelsTwice, + saveDiffOnError); + } + + bool RendererTestsFramework::renderAndCompareScreenshotOffscreenBuffer(const std::string& expectedImageName, uint32_t testDisplayIdx, ramses::displayBufferId_t displayBuffer, uint32_t width, uint32_t height, float maxAveragePercentErrorPerPixel) + { + assert(testDisplayIdx < m_displays.size()); + const ramses::displayId_t displayId = m_displays[testDisplayIdx].displayId; + + return compareScreenshotInternal( + expectedImageName, + displayId, + displayBuffer, + maxAveragePercentErrorPerPixel, + 0u, + 0u, + width, + height, + false, + true); + } + + bool RendererTestsFramework::renderAndCompareScreenshotSubimage(const std::string& expectedImageName, uint32_t subimageX, uint32_t subimageY, uint32_t subimageWidth, uint32_t subimageHeight, float maxAveragePercentErrorPerPixel, bool readPixelsTwice) + { + return compareScreenshotInternal(expectedImageName, m_displays[0].displayId, {}, maxAveragePercentErrorPerPixel, subimageX, subimageY, subimageWidth, subimageHeight, readPixelsTwice, true); + } + + void RendererTestsFramework::setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender) + { + m_testRenderer.setFrameTimerLimits(limitForClientResourcesUpload, limitForOffscreenBufferRender); + m_testRenderer.flushRenderer(); + } + + bool RendererTestsFramework::compareScreenshotInternal( + const std::string& expectedImageName, + ramses::displayId_t displayId, + ramses::displayBufferId_t bufferId, + float maxAveragePercentErrorPerPixel, + uint32_t subimageX, + uint32_t subimageY, + uint32_t subimageWidth, + uint32_t subimageHeight, + bool readPixelsTwice, + bool saveDiffOnError) + { + if (m_generateScreenshots) + { + m_testRenderer.saveScreenshotForDisplay(displayId, bufferId, subimageX, subimageY, subimageWidth, subimageHeight, expectedImageName); + return true; + } + + const bool comparisonResult = m_testRenderer.performScreenshotCheck(displayId, bufferId, subimageX, subimageY, subimageWidth, subimageHeight, expectedImageName, maxAveragePercentErrorPerPixel, readPixelsTwice, saveDiffOnError); + + if (!comparisonResult && saveDiffOnError) + { + assert(m_activeTestCase != nullptr); + LOG_ERROR(ramses::internal::CONTEXT_RENDERER, "Screenshot comparison failed for rendering test case: " << m_activeTestCase->m_name << " -> expected screenshot: " << expectedImageName); + } + + return comparisonResult; + } + + bool RendererTestsFramework::NameMatchesFilter(const std::string& name, const std::vector& filters) + { + if (ramses::internal::contains_c(filters, "*")) + { + return true; + } + + for(const auto& filter : filters) + { + if (name.find(filter) != std::string::npos) + { + return true; + } + } + + return false; + } + + void RendererTestsFramework::filterTestCases(const std::vector& filterIn, const std::vector& filterOut) + { + const bool processFilterIn = !filterIn.empty(); + const bool processFilterOut = !filterOut.empty(); + + if (!processFilterIn && !processFilterOut) + { + return; + } + + RenderingTestCases testCases; + testCases.swap(m_testCases); + + for(const auto& testCase : testCases) + { + const auto& testCaseName = testCase->m_name; + const bool excludedByFilterIn = processFilterIn && !NameMatchesFilter(testCaseName, filterIn); + const bool excludedByFilterOut = processFilterOut && NameMatchesFilter(testCaseName, filterOut); + + if (excludedByFilterIn || excludedByFilterOut) + { + delete testCase; + } + else + { + m_testCases.push_back(testCase); + } + } + } + + bool areEqual(const DisplayConfigVector& a, const DisplayConfigVector& b) + { + if (a.size() != b.size()) + { + return false; + } + + for (size_t i = 0; i < a.size(); ++i) + { + const ramses::internal::DisplayConfig& displayConfigA = a[i].impl().getInternalDisplayConfig(); + const ramses::internal::DisplayConfig& displayConfigB = b[i].impl().getInternalDisplayConfig(); + + if (displayConfigA != displayConfigB) + { + return false; + } + } + + return true; + } + + void RendererTestsFramework::sortTestCases() + { + // split test cases into groups using the same renderer/displays setup + const size_t numCases = m_testCases.size(); + + RenderingTestCases unsortedCases; + unsortedCases.swap(m_testCases); + std::vector processedFlags(numCases, false); + + while (m_testCases.size() != numCases) + { + uint32_t nextUnprocessed = 0u; + while (processedFlags[nextUnprocessed]) + { + ++nextUnprocessed; + } + m_testCases.push_back(unsortedCases[nextUnprocessed]); + processedFlags[nextUnprocessed] = true; + + const DisplayConfigVector& groupSetup = unsortedCases[nextUnprocessed]->m_displayConfigs; + for (uint32_t i = nextUnprocessed + 1u; i < numCases; ++i) + { + if (processedFlags[i]) + { + continue; + } + + RenderingTestCase* testCase = unsortedCases[i]; + if (areEqual(groupSetup, testCase->m_displayConfigs)) + { + m_testCases.push_back(testCase); + processedFlags[i] = true; + } + } + } + } + + bool RendererTestsFramework::currentDisplaySetupMatchesTestCase(const RenderingTestCase& testCase) const + { + if (testCase.m_displayConfigs.size() != m_displays.size()) + { + return false; + } + + for (size_t i = 0u; i < m_displays.size(); ++i) + { + assert(i < m_displays.size()); + + ramses::internal::DisplayConfig currentDisplayConfig = m_displays[i].config.impl().getInternalDisplayConfig(); + ramses::internal::DisplayConfig requestedDisplayConfig = testCase.m_displayConfigs[i].impl().getInternalDisplayConfig(); + + // ignore wayland ID in comparison as this is different for every test display config + requestedDisplayConfig.setWaylandIviSurfaceID(currentDisplayConfig.getWaylandIviSurfaceID()); + + if (currentDisplayConfig != requestedDisplayConfig) + { + return false; + } + } + + return true; + } + + bool RendererTestsFramework::applyRendererAndDisplaysConfigurationForTest(const RenderingTestCase& testCase) + { + if(!testCase.m_defaultRendererRequired) + { + //destroy renderer and displays if needed + if(m_testRenderer.isRendererInitialized()) + { + destroyDisplays(); + destroyRenderer(); + } + + return true; + } + + //create renderer and displays if needed + if(!m_testRenderer.isRendererInitialized()) + initializeRenderer(); + + if (!m_forceDisplaysReinitForNextTestCase && currentDisplaySetupMatchesTestCase(testCase)) + { + return true; + } + + destroyDisplays(); + + for(const auto& displayConfig : testCase.m_displayConfigs) + { + const ramses::displayId_t displayId = createDisplay(displayConfig); + + if (displayId == ramses::displayId_t::Invalid()) + return false; + } + m_forceDisplaysReinitForNextTestCase = false; + + return true; + } + + void RendererTestsFramework::destroyDisplays() + { + for(const auto& display : m_displays) + { + m_testRenderer.destroyDisplay(display.displayId); + } + m_displays.clear(); + } + + void RendererTestsFramework::destroyScenes() + { + m_testScenesAndRenderer.getScenesRegistry().destroyScenes(); + } + + void RendererTestsFramework::destroyBuffers() + { + for (auto& display : m_displays) + { + for(const auto buffer : display.offscreenBuffers) + m_testRenderer.destroyOffscreenBuffer(display.displayId, buffer); + display.offscreenBuffers.clear(); + for (const auto buffer : display.streamBuffers) + m_testRenderer.destroyStreamBuffer(display.displayId, buffer); + display.streamBuffers.clear(); + } + } + + bool RendererTestsFramework::runAllTests() + { + bool testResult = true; + + const uint64_t startTime = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + + assert(m_activeTestCase == nullptr); + m_passedTestCases.clear(); + m_failedTestCases.clear(); + + sortTestCases(); + + for(const auto& testCase : m_testCases) + { + LOG_INFO(ramses::internal::CONTEXT_RENDERER, "====== Running rendering test case: " << testCase->m_name << " ======"); + fmt::print("======\nRunning rendering test case: {}\n======\n", testCase->m_name); + fflush(stdout); + + if (applyRendererAndDisplaysConfigurationForTest(*testCase)) + { + testResult &= runTestCase(*testCase); + } + else + { + LOG_ERROR(ramses::internal::CONTEXT_RENDERER, "Renderer/display initialization failed for rendering test case: " << testCase->m_name); + testResult = false; + } + + destroyScenes(); + destroyBuffers(); + } + + const uint64_t endTime = ramses::internal::PlatformTime::GetMillisecondsMonotonic(); + m_elapsedTime = endTime - startTime; + + return testResult; + } + + bool RendererTestsFramework::runTestCase(RenderingTestCase& testCase) + { + m_activeTestCase = &testCase; + + const bool testResult = m_activeTestCase->m_rendererTest.run(*this, testCase); + if (testResult) + { + m_passedTestCases.push_back(m_activeTestCase); + } + else + { + m_failedTestCases.push_back(m_activeTestCase); + } + + m_activeTestCase = nullptr; + + return testResult; + } + + std::string RendererTestsFramework::generateReport() const + { + ramses::internal::StringOutputStream str; + + str << "\n\n--- Rendering test report begin ---\n"; + { + str << "\n Passed rendering test cases: " << m_passedTestCases.size(); + for(const auto& testCase : m_passedTestCases) + { + str << "\n " << testCase->m_name; + } + + str << "\n\n Failed rendering test cases: " << m_failedTestCases.size(); + for (const auto& testCase : m_failedTestCases) + { + str << "\n " << testCase->m_name; + } + + if (m_failedTestCases.empty()) + { + str << "\n"; + str << "\n ------------------"; + str << "\n --- ALL PASSED ---"; + str << "\n ------------------"; + } + else + { + str << "\n"; + str << "\n !!!!!!!!!!!!!!!!!!!!"; + str << "\n !!! FAILED TESTS !!!"; + str << "\n !!!!!!!!!!!!!!!!!!!!"; + } + + str << "\n\n Total time elapsed: " << static_cast(m_elapsedTime) * 0.001f << " s"; + } + str << "\n\n--- End of rendering test report ---\n\n"; + + return str.release(); + } + + const RendererTestsFramework::TestDisplays& RendererTestsFramework::getDisplays() const + { + return m_displays; + } +} diff --git a/tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.h b/tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.h new file mode 100644 index 000000000..76710648f --- /dev/null +++ b/tests/integration/renderer-tests/renderer-test-framework/RendererTestsFramework.h @@ -0,0 +1,158 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "TestScenesAndRenderer.h" +#include "RenderingTestCase.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "RendererTestUtils.h" + +#include +#include + +namespace ramses +{ + class IRendererEventHandler; + class IRendererSceneControlEventHandler; +} + +namespace ramses::internal +{ + class IRendererTest; + + using RenderingTestCases = std::vector; + + class RendererTestsFramework + { + public: + explicit RendererTestsFramework(bool generateScreenshots, const ramses::RamsesFrameworkConfig& config); + ~RendererTestsFramework(); + + void initializeRenderer(); + void initializeRenderer(const ramses::RendererConfig& rendererConfig); + void destroyRenderer(); + ramses::displayId_t createDisplay(const ramses::DisplayConfig& displayConfig); + [[nodiscard]] ramses::displayBufferId_t getDisplayFramebufferId(uint32_t testDisplayIdx) const; + void destroyDisplays(); + ramses::internal::TestRenderer& getTestRenderer(); + TestScenes& getScenesRegistry(); + ramses::RamsesClient& getClient(); + + RenderingTestCase& createTestCase(uint32_t id, IRendererTest& rendererTest, const std::string& name); + RenderingTestCase& createTestCaseWithDefaultDisplay(uint32_t id, IRendererTest& rendererTest, const std::string& name, bool iviWindowStartVisible = true); + RenderingTestCase& createTestCaseWithoutRenderer(uint32_t id, IRendererTest& rendererTest, const std::string& name); + + void setSceneMapping(ramses::sceneId_t sceneId, uint32_t testDisplayIdx); + bool getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); + bool getSceneToRendered(ramses::sceneId_t sceneId, uint32_t testDisplayIdx = 0); + + void dispatchRendererEvents(ramses::IRendererEventHandler& eventHandler, ramses::IRendererSceneControlEventHandler& sceneControlEventHandler); + ramses::displayBufferId_t createOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount = 0u, ramses::EDepthBufferType depthBufferType = ramses::EDepthBufferType::DepthStencil); + ramses::displayBufferId_t createDmaOffscreenBuffer(uint32_t testDisplayIdx, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier); + bool getDmaOffscreenBufferFDAndStride(uint32_t testDisplayIdx, ramses::displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; + void destroyOffscreenBuffer(uint32_t testDisplayIdx, ramses::displayBufferId_t buffer); + ramses::streamBufferId_t createStreamBuffer(uint32_t testDisplayIdx, ramses::waylandIviSurfaceId_t source); + void destroyStreamBuffer(uint32_t testDisplayIdx, ramses::streamBufferId_t buffer); + void assignSceneToDisplayBuffer(ramses::sceneId_t sceneId, ramses::displayBufferId_t buffer, int32_t renderOrder = 0); + void createBufferDataLink(ramses::displayBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); + void createBufferDataLink(ramses::streamBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); + void createDataLink(ramses::sceneId_t providerScene, ramses::dataProviderId_t providerTag, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); + void removeDataLink(ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag); + void setClearFlags(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, ramses::ClearFlags clearFlags); + void setClearColor(uint32_t testDisplayIdx, ramses::displayBufferId_t ob, const glm::vec4& clearColor); + void publishAndFlushScene(ramses::sceneId_t sceneId); + void flushRendererAndDoOneLoop(); + bool renderAndCompareScreenshot( + const std::string& expectedImageName, + uint32_t testDisplayIdx = 0u, + float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, + bool readPixelsTwice = false, + bool saveDiffOnError = true); + bool renderAndCompareScreenshotOffscreenBuffer(const std::string& expectedImageName, + uint32_t testDisplayIdx, ramses::displayBufferId_t displayBuffer, uint32_t width, uint32_t height, + float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel); + bool renderAndCompareScreenshotSubimage(const std::string& expectedImageName, + uint32_t subimageX, uint32_t subimageY, uint32_t subimageWidth, uint32_t subimageHeight, + float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, bool readPixelsTwice = false); + void setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); + void filterTestCases(const std::vector& filterIn, const std::vector& filterOut); + + bool runAllTests(); + [[nodiscard]] std::string generateReport() const; + + static bool NameMatchesFilter(const std::string& name, const std::vector& filter); + + template + ramses::sceneId_t createAndShowScene( + uint32_t sceneState, + const glm::vec3& cameraPosition = glm::vec3(0.0f), + const ramses::SceneConfig& sceneConfig = {}) + { + const ramses::sceneId_t sceneId = getScenesRegistry().createScene(sceneState, cameraPosition, sceneConfig); + publishAndFlushScene(sceneId); + getSceneToRendered(sceneId); + return sceneId; + } + + template + ramses::sceneId_t createAndShowScene(uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight) + { + const ramses::sceneId_t sceneId = getScenesRegistry().createScene(sceneState, { 0, 0, 0 }, vpWidth, vpHeight); + publishAndFlushScene(sceneId); + getSceneToRendered(sceneId); + return sceneId; + } + + protected: + struct TestDisplayInfo + { + ramses::displayId_t displayId; + ramses::DisplayConfig config; + std::vector offscreenBuffers; + std::vector streamBuffers; + }; + + using TestDisplays = std::vector; + [[nodiscard]] const TestDisplays& getDisplays() const; + + private: + bool compareScreenshotInternal( + const std::string& expectedImageName, + ramses::displayId_t displayId, + ramses::displayBufferId_t bufferId, + float maxAveragePercentErrorPerPixel, + uint32_t subimageX, + uint32_t subimageY, + uint32_t subimageWidth, + uint32_t subimageHeight, + bool readPixelsTwice, + bool saveDiffOnError); + + void sortTestCases(); + [[nodiscard]] bool currentDisplaySetupMatchesTestCase(const RenderingTestCase& testCase) const; + bool applyRendererAndDisplaysConfigurationForTest(const RenderingTestCase& testCase); + void destroyScenes(); + void destroyBuffers(); + bool runTestCase(RenderingTestCase& testCase); + + const bool m_generateScreenshots; + ramses::internal::TestScenesAndRenderer m_testScenesAndRenderer; + ramses::internal::TestRenderer& m_testRenderer; + TestDisplays m_displays; + bool m_forceDisplaysReinitForNextTestCase = false; + + RenderingTestCases m_testCases; + + // for logging and debugging purpose + RenderingTestCase* m_activeTestCase; + RenderingTestCases m_passedTestCases; + RenderingTestCases m_failedTestCases; + uint64_t m_elapsedTime; + }; +} diff --git a/tests/integration/renderer-tests/renderer-test-framework/RenderingTestCase.h b/tests/integration/renderer-tests/renderer-test-framework/RenderingTestCase.h new file mode 100644 index 000000000..11d760fab --- /dev/null +++ b/tests/integration/renderer-tests/renderer-test-framework/RenderingTestCase.h @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/DisplayConfig.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" + +#include +#include + +namespace ramses::internal +{ + class IRendererTest; + + using DisplayConfigVector = std::vector; + + struct RenderingTestCase + { + RenderingTestCase(uint32_t id, IRendererTest& rendererTest, std::string_view name, bool defaultRendererRequired) + : m_id(id) + , m_name(name) + , m_rendererTest(rendererTest) + , m_defaultRendererRequired(defaultRendererRequired) + { + } + + const uint32_t m_id; + const std::string m_name; + DisplayConfigVector m_displayConfigs; + IRendererTest& m_rendererTest; + bool m_defaultRendererRequired; + }; +} diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.cpp b/tests/integration/renderer-tests/renderer-test-framework/TestRenderer.cpp similarity index 57% rename from integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.cpp rename to tests/integration/renderer-tests/renderer-test-framework/TestRenderer.cpp index 0f45378d7..9ff6cb40d 100644 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/TestRenderer.cpp +++ b/tests/integration/renderer-tests/renderer-test-framework/TestRenderer.cpp @@ -7,15 +7,16 @@ // ------------------------------------------------------------------------- #include "TestRenderer.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "RendererAPI/IRenderBackend.h" -#include "RamsesRendererImpl.h" -#include "RendererSceneControlImpl.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/client/Scene.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" +#include "impl/RamsesRendererImpl.h" +#include "impl/RendererSceneControlImpl.h" #include "RendererAndSceneTestEventHandler.h" -namespace ramses_internal +namespace ramses::internal { - ramses::displayId_t TestRenderer::createDisplay(const ramses::DisplayConfig& displayConfig) + displayId_t TestRenderer::createDisplay(const ramses::DisplayConfig& displayConfig) { return RendererTestUtils::CreateDisplayImmediate(*m_renderer, displayConfig); } @@ -48,49 +49,49 @@ namespace ramses_internal m_renderer->flush(); } - void TestRenderer::destroyDisplay(ramses::displayId_t displayId) + void TestRenderer::destroyDisplay(displayId_t displayId) { RendererTestUtils::DestroyDisplayImmediate(*m_renderer, displayId); } - ramses::displayBufferId_t TestRenderer::getDisplayFramebufferId(ramses::displayId_t displayId) const + displayBufferId_t TestRenderer::getDisplayFramebufferId(displayId_t displayId) const { return m_renderer->getDisplayFramebuffer(displayId); } - void TestRenderer::setSceneMapping(ramses::sceneId_t sceneId, ramses::displayId_t display) + void TestRenderer::setSceneMapping(sceneId_t sceneId, displayId_t display) { m_sceneControlAPI->setSceneMapping(sceneId, display); m_sceneControlAPI->flush(); } - bool TestRenderer::getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + bool TestRenderer::getSceneToState(ramses::Scene& scene, ramses::RendererSceneState state) { - setSceneState(sceneId, state); - return waitForSceneStateChange(sceneId, state); + setSceneState(scene.getSceneId(), state); + return waitForSceneStateChange(scene, state); } - void TestRenderer::setSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + void TestRenderer::setSceneState(sceneId_t sceneId, ramses::RendererSceneState state) { m_sceneControlAPI->setSceneState(sceneId, state); m_sceneControlAPI->flush(); } - bool TestRenderer::waitForSceneStateChange(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + bool TestRenderer::waitForSceneStateChange(ramses::Scene& scene, ramses::RendererSceneState state) { - ramses::RendererAndSceneTestEventHandler eventHandler(*m_renderer); - return eventHandler.waitForSceneState(sceneId, state); + RendererAndSceneTestEventHandler eventHandler(*m_renderer); + return eventHandler.waitForSceneState(scene, state); } - void TestRenderer::waitForFlush(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t sceneVersionTag) + void TestRenderer::waitForFlush(sceneId_t sceneId, sceneVersionTag_t sceneVersionTag) { - ramses::RendererAndSceneTestEventHandler sceneEventHandler(*m_renderer); + RendererAndSceneTestEventHandler sceneEventHandler(*m_renderer); sceneEventHandler.waitForFlush(sceneId, sceneVersionTag); } - bool TestRenderer::checkScenesExpired(std::initializer_list sceneIds) + bool TestRenderer::checkScenesExpired(std::initializer_list sceneIds) { - ramses::RendererAndSceneTestEventHandler sceneEventHandler(*m_renderer); + RendererAndSceneTestEventHandler sceneEventHandler(*m_renderer); for (auto sceneId : sceneIds) { if (!sceneEventHandler.checkExpiredState(sceneId)) @@ -100,9 +101,9 @@ namespace ramses_internal return true; } - bool TestRenderer::checkScenesNotExpired(std::initializer_list sceneIds) + bool TestRenderer::checkScenesNotExpired(std::initializer_list sceneIds) { - ramses::RendererAndSceneTestEventHandler sceneEventHandler(*m_renderer); + RendererAndSceneTestEventHandler sceneEventHandler(*m_renderer); for (auto sceneId : sceneIds) { if (sceneEventHandler.checkExpiredState(sceneId)) @@ -112,19 +113,19 @@ namespace ramses_internal return true; } - bool TestRenderer::waitForStreamSurfaceAvailabilityChange(ramses::waylandIviSurfaceId_t streamSource, bool available) + bool TestRenderer::waitForStreamSurfaceAvailabilityChange(waylandIviSurfaceId_t streamSource, bool available) { - ramses::RendererAndSceneTestEventHandler eventHandler(*m_renderer); + RendererAndSceneTestEventHandler eventHandler(*m_renderer); return eventHandler.waitForStreamSurfaceAvailabilityChange(streamSource, available); } - void TestRenderer::dispatchEvents(ramses::IRendererEventHandler& eventHandler, ramses::IRendererSceneControlEventHandler& sceneControlEventHandler) + void TestRenderer::dispatchEvents(IRendererEventHandler& eventHandler, IRendererSceneControlEventHandler& sceneControlEventHandler) { m_renderer->dispatchEvents(eventHandler); m_sceneControlAPI->dispatchEvents(sceneControlEventHandler); } - void TestRenderer::setLoopMode(ramses::ELoopMode loopMode) + void TestRenderer::setLoopMode(ELoopMode loopMode) { m_renderer->setLoopMode(loopMode); } @@ -150,66 +151,70 @@ namespace ramses_internal m_renderer->doOneLoop(); } - ramses::displayBufferId_t TestRenderer::createOffscreenBuffer(ramses::displayId_t displayId, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount, ramses::EDepthBufferType depthBufferType) + displayBufferId_t TestRenderer::createOffscreenBuffer(displayId_t displayId, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount, EDepthBufferType depthBufferType) { - ramses::displayBufferId_t offscreenBufferId; + displayBufferId_t offscreenBufferId; if (interruptible) + { offscreenBufferId = m_renderer->createInterruptibleOffscreenBuffer(displayId, width, height, depthBufferType); + } else + { offscreenBufferId = m_renderer->createOffscreenBuffer(displayId, width, height, sampleCount, depthBufferType); + } m_renderer->flush(); - ramses::RendererAndSceneTestEventHandler eventHandler(*m_renderer); + RendererAndSceneTestEventHandler eventHandler(*m_renderer); eventHandler.waitForOffscreenBufferCreation(offscreenBufferId); return offscreenBufferId; } - ramses::displayBufferId_t TestRenderer::createDmaOffscreenBuffer(ramses::displayId_t displayId, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier) + displayBufferId_t TestRenderer::createDmaOffscreenBuffer(displayId_t displayId, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier) { - ramses::displayBufferId_t offscreenBufferId; + displayBufferId_t offscreenBufferId; offscreenBufferId = m_renderer->createDmaOffscreenBuffer(displayId, width, height, bufferFourccFormat, bufferUsageFlags, modifier); m_renderer->flush(); - ramses::RendererAndSceneTestEventHandler eventHandler(*m_renderer); + RendererAndSceneTestEventHandler eventHandler(*m_renderer); eventHandler.waitForOffscreenBufferCreation(offscreenBufferId); return offscreenBufferId; } - void TestRenderer::destroyOffscreenBuffer(ramses::displayId_t displayId, ramses::displayBufferId_t buffer) + void TestRenderer::destroyOffscreenBuffer(displayId_t displayId, displayBufferId_t buffer) { m_renderer->destroyOffscreenBuffer(displayId, buffer); m_renderer->flush(); - ramses::RendererAndSceneTestEventHandler eventHandler(*m_renderer); + RendererAndSceneTestEventHandler eventHandler(*m_renderer); eventHandler.waitForOffscreenBufferDestruction(buffer); } - void TestRenderer::assignSceneToDisplayBuffer(ramses::sceneId_t sceneId, ramses::displayBufferId_t buffer, int32_t renderOrder) + void TestRenderer::assignSceneToDisplayBuffer(sceneId_t sceneId, displayBufferId_t buffer, int32_t renderOrder) { m_sceneControlAPI->setSceneDisplayBufferAssignment(sceneId, buffer, renderOrder); m_sceneControlAPI->flush(); } - void TestRenderer::setClearFlags(ramses::displayId_t displayId, ramses::displayBufferId_t buffer, uint32_t clearFlags) + void TestRenderer::setClearFlags(displayId_t displayId, displayBufferId_t buffer, ClearFlags clearFlags) { m_renderer->setDisplayBufferClearFlags(displayId, buffer, clearFlags); m_renderer->flush(); } - void TestRenderer::setClearColor(ramses::displayId_t displayId, ramses::displayBufferId_t buffer, const glm::vec4& clearColor) + void TestRenderer::setClearColor(displayId_t displayId, displayBufferId_t buffer, const glm::vec4& clearColor) { m_renderer->setDisplayBufferClearColor(displayId, buffer, {clearColor.r, clearColor.g, clearColor.b, clearColor.a}); m_renderer->flush(); } - bool TestRenderer::getDmaOffscreenBufferFDAndStride(ramses::displayId_t displayId, ramses::displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const + bool TestRenderer::getDmaOffscreenBufferFDAndStride(displayId_t displayId, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const { - return m_renderer->getDmaOffscreenBufferFDAndStride(displayId, displayBufferId, fd, stride) == ramses::StatusOK; + return m_renderer->getDmaOffscreenBufferFDAndStride(displayId, displayBufferId, fd, stride); } - ramses::streamBufferId_t TestRenderer::createStreamBuffer(ramses::displayId_t displayId, ramses::waylandIviSurfaceId_t source) + streamBufferId_t TestRenderer::createStreamBuffer(displayId_t displayId, waylandIviSurfaceId_t source) { const auto bufferId = m_renderer->createStreamBuffer(displayId, source); m_renderer->flush(); @@ -217,39 +222,39 @@ namespace ramses_internal return bufferId; } - void TestRenderer::destroyStreamBuffer(ramses::displayId_t displayId, ramses::streamBufferId_t buffer) + void TestRenderer::destroyStreamBuffer(displayId_t displayId, streamBufferId_t buffer) { - m_renderer->m_impl.destroyStreamBuffer(displayId, buffer); + m_renderer->impl().destroyStreamBuffer(displayId, buffer); m_renderer->flush(); } - void TestRenderer::createBufferDataLink(ramses::displayBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) + void TestRenderer::createBufferDataLink(displayBufferId_t providerBuffer, sceneId_t consumerScene, dataConsumerId_t consumerTag) { m_sceneControlAPI->linkOffscreenBuffer(providerBuffer, consumerScene, consumerTag); m_sceneControlAPI->flush(); } - void TestRenderer::createBufferDataLink(ramses::streamBufferId_t providerBuffer, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerTag) + void TestRenderer::createBufferDataLink(streamBufferId_t providerBuffer, sceneId_t consumerScene, dataConsumerId_t consumerTag) { - m_sceneControlAPI->m_impl.linkStreamBuffer(providerBuffer, consumerScene, consumerTag); + m_sceneControlAPI->impl().linkStreamBuffer(providerBuffer, consumerScene, consumerTag); m_sceneControlAPI->flush(); } - void TestRenderer::createDataLink(ramses::sceneId_t providerScene, ramses::dataProviderId_t providerId, ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerId) + void TestRenderer::createDataLink(sceneId_t providerScene, dataProviderId_t providerId, sceneId_t consumerScene, dataConsumerId_t consumerId) { m_sceneControlAPI->linkData(providerScene, providerId, consumerScene, consumerId); m_sceneControlAPI->flush(); } - void TestRenderer::removeDataLink(ramses::sceneId_t consumerScene, ramses::dataConsumerId_t consumerId) + void TestRenderer::removeDataLink(sceneId_t consumerScene, dataConsumerId_t consumerId) { m_sceneControlAPI->unlinkData(consumerScene, consumerId); m_sceneControlAPI->flush(); } bool TestRenderer::performScreenshotCheck( - ramses::displayId_t displayId, - ramses::displayBufferId_t bufferId, + displayId_t displayId, + displayBufferId_t bufferId, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& comparisonImageFile, float maxAveragePercentErrorPerPixel, @@ -274,7 +279,7 @@ namespace ramses_internal saveDiffOnError); } - void TestRenderer::saveScreenshotForDisplay(ramses::displayId_t displayId, ramses::displayBufferId_t bufferId, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& imageFile) + void TestRenderer::saveScreenshotForDisplay(displayId_t displayId, displayBufferId_t bufferId, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& imageFile) { RendererTestUtils::SaveScreenshotForDisplay( *m_renderer, @@ -287,7 +292,7 @@ namespace ramses_internal imageFile); } - void TestRenderer::readPixels(ramses::displayId_t displayId, uint32_t x, uint32_t y, uint32_t width, uint32_t height) + void TestRenderer::readPixels(displayId_t displayId, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { m_renderer->readPixels(displayId, {}, x, y, width, height); } @@ -301,21 +306,21 @@ namespace ramses_internal { RendererCommands cmds; cmds.push_back(RendererCommand::SCSetIviSurfaceVisibility{ surfaceId, visibility }); - m_renderer->m_impl.pushAndConsumeRendererCommands(cmds); + m_renderer->impl().pushAndConsumeRendererCommands(cmds); } - IEmbeddedCompositor& TestRenderer::getEmbeddedCompositor(ramses::displayId_t displayId) + IEmbeddedCompositor& TestRenderer::getEmbeddedCompositor(displayId_t displayId) { - return m_renderer->m_impl.getDisplayDispatcher().getEC(ramses_internal::DisplayHandle{ displayId.getValue() }); + return m_renderer->impl().getDisplayDispatcher().getEC(DisplayHandle{ displayId.getValue() }); } - IEmbeddedCompositingManager& TestRenderer::getEmbeddedCompositorManager(ramses::displayId_t displayId) + IEmbeddedCompositingManager& TestRenderer::getEmbeddedCompositorManager(displayId_t displayId) { - return m_renderer->m_impl.getDisplayDispatcher().getECManager(ramses_internal::DisplayHandle{ displayId.getValue() }); + return m_renderer->impl().getDisplayDispatcher().getECManager(DisplayHandle{ displayId.getValue() }); } bool TestRenderer::hasSystemCompositorController() const { - return m_renderer->m_impl.getDisplayDispatcher().hasSystemCompositorController(); + return m_renderer->impl().getDisplayDispatcher().hasSystemCompositorController(); } } diff --git a/tests/integration/renderer-tests/renderer-test-framework/TestRenderer.h b/tests/integration/renderer-tests/renderer-test-framework/TestRenderer.h new file mode 100644 index 000000000..6881efd74 --- /dev/null +++ b/tests/integration/renderer-tests/renderer-test-framework/TestRenderer.h @@ -0,0 +1,103 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/Types.h" +#include "ramses/framework/RendererSceneState.h" +#include "internal/RendererLib/Types.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "RendererTestUtils.h" + +#include +#include + +namespace ramses +{ + class RamsesFramework; + class Scene; + class IRendererEventHandler; + class IRendererSceneControlEventHandler; +} + +namespace ramses::internal +{ + class IEmbeddedCompositingManager; + class IEmbeddedCompositor; + + class TestRenderer + { + public: + virtual ~TestRenderer() = default; + + void initializeRendererWithFramework(ramses::RamsesFramework& ramsesFramework, const ramses::RendererConfig& rendererConfig); + [[nodiscard]] bool isRendererInitialized() const; + void destroyRendererWithFramework(ramses::RamsesFramework& ramsesFramework); + void flushRenderer(); + + displayId_t createDisplay(const ramses::DisplayConfig& displayConfig); + void destroyDisplay(displayId_t displayId); + [[nodiscard]] displayBufferId_t getDisplayFramebufferId(displayId_t displayId) const; + + void setSceneMapping(sceneId_t sceneId, displayId_t display); + bool getSceneToState(ramses::Scene& scene, ramses::RendererSceneState state); + void setSceneState(sceneId_t sceneId, ramses::RendererSceneState state); + bool waitForSceneStateChange(ramses::Scene& scene, ramses::RendererSceneState state); + void waitForFlush(sceneId_t sceneId, ramses::sceneVersionTag_t sceneVersionTag); + bool checkScenesExpired(std::initializer_list sceneIds); + bool checkScenesNotExpired(std::initializer_list sceneIds); + bool waitForStreamSurfaceAvailabilityChange(waylandIviSurfaceId_t streamSource, bool available); + + void dispatchEvents(IRendererEventHandler& eventHandler, IRendererSceneControlEventHandler& sceneControlEventHandler); + + void setLoopMode(ELoopMode loopMode); + void startRendererThread(); + void stopRendererThread(); + [[nodiscard]] bool isRendererThreadEnabled() const; + void doOneLoop(); + + displayBufferId_t createOffscreenBuffer(displayId_t displayId, uint32_t width, uint32_t height, bool interruptible, uint32_t sampleCount = 0u, EDepthBufferType depthBufferType = EDepthBufferType::DepthStencil); + displayBufferId_t createDmaOffscreenBuffer(displayId_t displayId, uint32_t width, uint32_t height, uint32_t bufferFourccFormat, uint32_t bufferUsageFlags, uint64_t modifier); + void destroyOffscreenBuffer(displayId_t displayId, displayBufferId_t buffer); + void assignSceneToDisplayBuffer(sceneId_t sceneId, displayBufferId_t buffer, int32_t renderOrder); + void setClearFlags(displayId_t displayId, displayBufferId_t buffer, ClearFlags clearFlags); + void setClearColor(displayId_t displayId, displayBufferId_t buffer, const glm::vec4& clearColor); + bool getDmaOffscreenBufferFDAndStride(displayId_t displayId, displayBufferId_t displayBufferId, int& fd, uint32_t& stride) const; + + streamBufferId_t createStreamBuffer(displayId_t displayId, waylandIviSurfaceId_t source); + void destroyStreamBuffer(displayId_t displayId, streamBufferId_t buffer); + + void createBufferDataLink(displayBufferId_t providerBuffer, sceneId_t consumerScene, dataConsumerId_t consumerTag); + void createBufferDataLink(streamBufferId_t providerBuffer, sceneId_t consumerScene, dataConsumerId_t consumerTag); + void createDataLink(sceneId_t providerScene, dataProviderId_t providerId, sceneId_t consumerScene, dataConsumerId_t consumerId); + void removeDataLink(sceneId_t consumerScene, dataConsumerId_t consumerId); + + bool performScreenshotCheck( + const displayId_t displayId, + displayBufferId_t bufferId, + uint32_t x, uint32_t y, uint32_t width, uint32_t height, + const std::string& comparisonImageFile, + float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, + bool readPixelsTwice = false, + bool saveDiffOnError = true); + void saveScreenshotForDisplay(const displayId_t displayId, displayBufferId_t bufferId, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::string& imageFile); + void readPixels(displayId_t displayId, uint32_t x, uint32_t y, uint32_t width, uint32_t height); + void setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); + void setSurfaceVisibility(WaylandIviSurfaceId surfaceId, bool visibility); + + IEmbeddedCompositor& getEmbeddedCompositor(displayId_t displayId); + IEmbeddedCompositingManager& getEmbeddedCompositorManager(displayId_t displayId); + + [[nodiscard]] bool hasSystemCompositorController() const; + + private: + RamsesRenderer* m_renderer = nullptr; + RendererSceneControl* m_sceneControlAPI = nullptr; + }; +} diff --git a/tests/integration/renderer-tests/renderer-test-framework/TestScenes.cpp b/tests/integration/renderer-tests/renderer-test-framework/TestScenes.cpp new file mode 100644 index 000000000..d056d46d6 --- /dev/null +++ b/tests/integration/renderer-tests/renderer-test-framework/TestScenes.cpp @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "TestScenes.h" +#include "TestScenes/FileLoadingScene.h" +#include + +namespace ramses::internal +{ + TestScenes::TestScenes(ramses::RamsesClient& client) + : m_client(client) + { + } + + TestScenes::~TestScenes() + { + for (const auto& sceneEntry : m_scenes) + { + delete sceneEntry.value.integrationScene; + } + } + + void TestScenes::createFileLoadingScene(ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const ramses::RamsesFrameworkConfig& config, uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight) + { + // This creates the scene internally inside the instance + ramses::internal::FileLoadingScene fileLoadingScene(m_client, sceneState, sceneId, cameraPosition, ".", config, vpWidth, vpHeight); + SceneData data; + data.clientScene = fileLoadingScene.getCreatedScene(); + m_scenes.put(sceneId, data); + } + + const ramses::Scene& TestScenes::getScene(ramses::sceneId_t sceneId) const + { + assert(m_scenes.contains(sceneId)); + return *m_scenes.get(sceneId)->clientScene; + } + + ramses::Scene& TestScenes::getScene(ramses::sceneId_t sceneId) + { + assert(m_scenes.contains(sceneId)); + return *m_scenes.get(sceneId)->clientScene; + } + + void TestScenes::destroyScenes() + { + while (m_scenes.size() != 0u) + { + destroyScene(m_scenes.begin()->key); + } + } + + void TestScenes::destroyScene(ramses::sceneId_t sceneId) + { + auto it = m_scenes.find(sceneId); + assert(it != m_scenes.end()); + + auto& clientScene = *it->value.clientScene; + delete it->value.integrationScene; + m_client.destroy(clientScene); + m_scenes.remove(it); + } +} diff --git a/tests/integration/renderer-tests/renderer-test-framework/TestScenes.h b/tests/integration/renderer-tests/renderer-test-framework/TestScenes.h new file mode 100644 index 000000000..83ae4f5b3 --- /dev/null +++ b/tests/integration/renderer-tests/renderer-test-framework/TestScenes.h @@ -0,0 +1,108 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "TestScenes/IntegrationScene.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" +#include + +namespace ramses::internal +{ + class TestScenes + { + public: + explicit TestScenes(ramses::RamsesClient& client); + ~TestScenes(); + + template + void createScene( + uint32_t state, + ramses::sceneId_t sceneId, + const glm::vec3& cameraPosition = glm::vec3(0.0f), + const ramses::SceneConfig& sceneConfig = {}) + { + SceneData data; + SceneConfig config = sceneConfig; + config.setSceneId(sceneId); + + data.clientScene = m_client.createScene(config); + data.integrationScene = new INTEGRATION_SCENE(*data.clientScene, state, cameraPosition); + m_scenes.put(sceneId, data); + + } + template + ramses::sceneId_t createScene( + uint32_t state, + const glm::vec3& cameraPosition = glm::vec3(0.0f), + const ramses::SceneConfig& sceneConfig = {}) + { + const ramses::sceneId_t sceneId = m_nextSceneId; + m_nextSceneId.getReference()++; + createScene(state, sceneId, cameraPosition, sceneConfig); + return sceneId; + } + + template + void createScene( + uint32_t state, + ramses::sceneId_t sceneId, + const glm::vec3& cameraPosition, + uint32_t vpWidth, + uint32_t vpHeight) + { + SceneData data; + data.clientScene = m_client.createScene(SceneConfig(sceneId)); + data.integrationScene = new INTEGRATION_SCENE(*data.clientScene, state, cameraPosition, vpWidth, vpHeight); + m_scenes.put(sceneId, data); + } + template + ramses::sceneId_t createScene( + uint32_t state, + const glm::vec3& cameraPosition, + uint32_t vpWidth, + uint32_t vpHeight) + { + const ramses::sceneId_t sceneId = m_nextSceneId; + m_nextSceneId.getReference()++; + createScene(state, sceneId, cameraPosition, vpWidth, vpHeight); + return sceneId; + } + + void createFileLoadingScene(ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const ramses::RamsesFrameworkConfig& config, uint32_t sceneState, uint32_t vpWidth, uint32_t vpHeight); + + [[nodiscard]] const ramses::Scene& getScene(ramses::sceneId_t sceneId) const; + ramses::Scene& getScene(ramses::sceneId_t sceneId); + + template + void setSceneState(ramses::sceneId_t sceneId, uint32_t state) + { + assert(m_scenes.contains(sceneId)); + assert(m_scenes.get(sceneId)->integrationScene); + auto& scene = static_cast(*m_scenes.get(sceneId)->integrationScene); + scene.setState(state); + } + + void destroyScenes(); + void destroyScene(ramses::sceneId_t sceneId); + + private: + struct SceneData + { + ramses::Scene* clientScene = nullptr; + ramses::internal::IntegrationScene* integrationScene = nullptr; + }; + using SceneMap = ramses::internal::HashMap; + + ramses::RamsesClient& m_client; + ramses::sceneId_t m_nextSceneId{1u}; + SceneMap m_scenes; + }; +} diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenesAndRenderer.cpp b/tests/integration/renderer-tests/renderer-test-framework/TestScenesAndRenderer.cpp similarity index 77% rename from integration/SandwichTests/RendererTests/RendererTestFramework/TestScenesAndRenderer.cpp rename to tests/integration/renderer-tests/renderer-test-framework/TestScenesAndRenderer.cpp index 49d1a4c06..a1311e4b0 100644 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenesAndRenderer.cpp +++ b/tests/integration/renderer-tests/renderer-test-framework/TestScenesAndRenderer.cpp @@ -8,10 +8,10 @@ #include "TestScenesAndRenderer.h" #include "RendererTestUtils.h" -#include "DisplayConfigImpl.h" -#include "RamsesRendererImpl.h" +#include "impl/DisplayConfigImpl.h" +#include "impl/RamsesRendererImpl.h" -namespace ramses_internal +namespace ramses::internal { TestScenesAndRenderer::TestScenesAndRenderer(const ramses::RamsesFrameworkConfig& config) : m_ramsesFramework(config) @@ -56,6 +56,7 @@ namespace ramses_internal const auto startTime = std::chrono::steady_clock::now(); while (std::chrono::steady_clock::now() - startTime < std::chrono::seconds{ 20 }) { + handlerWithCondition.onUpdate(); getClient().dispatchEvents(handlerWithCondition); if (!getTestRenderer().isRendererThreadEnabled()) @@ -70,16 +71,22 @@ namespace ramses_internal return false; } - ramses::status_t TestScenesAndRenderer::validateScene(ramses::sceneId_t sceneId) + bool TestScenesAndRenderer::getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) { - ramses::Scene& clientScene = m_scenes.getScene(sceneId); - return clientScene.validate(); + return m_renderer.getSceneToState(m_scenes.getScene(sceneId), state); + } + + bool TestScenesAndRenderer::waitForSceneStateChange(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + { + return m_renderer.waitForSceneStateChange(m_scenes.getScene(sceneId), state); } - const char* TestScenesAndRenderer::getValidationReport(ramses::sceneId_t sceneId) + ramses::ValidationReport TestScenesAndRenderer::validateScene(ramses::sceneId_t sceneId) { ramses::Scene& clientScene = m_scenes.getScene(sceneId); - return clientScene.getValidationReport(); + ramses::ValidationReport report; + clientScene.validate(report); + return report; } TestScenes& TestScenesAndRenderer::getScenesRegistry() @@ -97,12 +104,12 @@ namespace ramses_internal return m_client; } - const ramses_internal::TestRenderer& TestScenesAndRenderer::getTestRenderer() const + const ramses::internal::TestRenderer& TestScenesAndRenderer::getTestRenderer() const { return m_renderer; } - ramses_internal::TestRenderer& TestScenesAndRenderer::getTestRenderer() + ramses::internal::TestRenderer& TestScenesAndRenderer::getTestRenderer() { return m_renderer; } diff --git a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenesAndRenderer.h b/tests/integration/renderer-tests/renderer-test-framework/TestScenesAndRenderer.h similarity index 61% rename from integration/SandwichTests/RendererTests/RendererTestFramework/TestScenesAndRenderer.h rename to tests/integration/renderer-tests/renderer-test-framework/TestScenesAndRenderer.h index 3ed6a6ab8..28e009ec3 100644 --- a/integration/SandwichTests/RendererTests/RendererTestFramework/TestScenesAndRenderer.h +++ b/tests/integration/renderer-tests/renderer-test-framework/TestScenesAndRenderer.h @@ -6,18 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTSCENESANDRENDERER_H -#define RAMSES_TESTSCENESANDRENDERER_H +#pragma once #include "TestRenderer.h" #include "TestScenes.h" -#include "ramses-client-api/IClientEventHandler.h" -#include "Components/FlushTimeInformation.h" +#include "ramses/framework/ValidationReport.h" +#include "ramses/client/IClientEventHandler.h" +#include "internal/Components/FlushTimeInformation.h" #include #include -namespace ramses_internal +namespace ramses::internal { class TestClientEventHandler; @@ -35,8 +35,10 @@ namespace ramses_internal void setExpirationTimestamp(ramses::sceneId_t sceneId, FlushTime::Clock::time_point expirationTS); bool loopTillClientEvent(TestClientEventHandler& handlerWithCondition); - ramses::status_t validateScene(ramses::sceneId_t sceneId); - const char* getValidationReport(ramses::sceneId_t sceneId); + bool getSceneToState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); + bool waitForSceneStateChange(ramses::sceneId_t sceneId, ramses::RendererSceneState state); + + ramses::ValidationReport validateScene(ramses::sceneId_t sceneId); [[nodiscard]] const TestScenes& getScenesRegistry() const; [[nodiscard]] TestScenes& getScenesRegistry(); @@ -57,15 +59,14 @@ namespace ramses_internal class TestClientEventHandler : public ramses::IClientEventHandler { public: - void sceneFileLoadFailed(std::string_view) override {} - void sceneFileLoadSucceeded(std::string_view, ramses::Scene*) override {} - void sceneReferenceFlushed(ramses::SceneReference&, ramses::sceneVersionTag_t) override {} - void dataLinked(ramses::sceneId_t, ramses::dataProviderId_t, ramses::sceneId_t, ramses::dataConsumerId_t, bool) override {} - void dataUnlinked(ramses::sceneId_t, ramses::dataConsumerId_t, bool) override {} - void sceneReferenceStateChanged(ramses::SceneReference&, ramses::RendererSceneState) override {} + void sceneFileLoadFailed(std::string_view /*filename*/) override {} + void sceneFileLoadSucceeded(std::string_view /*filename*/, ramses::Scene* /*loadedScene*/) override {} + void sceneReferenceFlushed(ramses::SceneReference& /*sceneRef*/, ramses::sceneVersionTag_t /*versionTag*/) override {} + void dataLinked(ramses::sceneId_t /*providerScene*/, ramses::dataProviderId_t /*providerId*/, ramses::sceneId_t /*consumerScene*/, ramses::dataConsumerId_t /*consumerId*/, bool /*success*/) override {} + void dataUnlinked(ramses::sceneId_t /*consumerScene*/, ramses::dataConsumerId_t /*consumerId*/, bool /*success*/) override {} + void sceneReferenceStateChanged(ramses::SceneReference& /*sceneRef*/, ramses::RendererSceneState /*state*/) override {} [[nodiscard]] virtual bool waitCondition() const = 0; + virtual void onUpdate() {} }; } - -#endif diff --git a/tests/integration/renderer-tests/rendering-tests/DataLinkingTests.cpp b/tests/integration/renderer-tests/rendering-tests/DataLinkingTests.cpp new file mode 100644 index 000000000..fc5d42ba7 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/DataLinkingTests.cpp @@ -0,0 +1,400 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "DataLinkingTests.h" +#include "TestScenes/TransformationLinkScene.h" +#include "TestScenes/MultiTransformationLinkScene.h" +#include "TestScenes/DataLinkScene.h" +#include "TestScenes/CameraDataLinkScene.h" +#include "TestScenes/TextureLinkScene.h" +#include "TestScenes/MultiTypeLinkScene.h" +#include "TestScenes/MultipleTrianglesScene.h" +#include "ramses/client/Node.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Texture2D.h" +#include "impl/RamsesObjectTypeUtils.h" + +namespace ramses::internal +{ + void DataLinkingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConsumerNotLinkedToProvider, *this, "TransformationLinkTest_ConsumerNotLinkedToProvider"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConsumerLinkedToProvider, *this, "TransformationLinkTest_ConsumerLinkedToProvider"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_LinkRemoved, *this, "TransformationLinkTest_LinkRemoved"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_LinkOverridesComsumerTransform, *this, "TransformationLinkTest_LinkOverridesComsumerTransform"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConsumerLinkedToProviderNested, *this, "TransformationLinkTest_ConsumerLinkedToProviderNested"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_RemovedProviderFromScene, *this, "TransformationLinkTest_RemovedProviderFromScene"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_RemovedConsumerFromScene, *this, "TransformationLinkTest_RemovedConsumerFromScene"); + testFramework.createTestCaseWithDefaultDisplay(TransformationLinkTest_ConfidenceMultiLinkTest, *this, "TransformationLinkTest_ConfidenceMultiLinkTest"); + + testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_NoLinks, *this, "DataLinkTest_NoLinks"); + testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_Linked, *this, "DataLinkTest_Linked"); + testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_LinksRemoved, *this, "DataLinkTest_LinksRemoved"); + testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_ProviderRemoved, *this, "DataLinkTest_ProviderRemoved"); + testFramework.createTestCaseWithDefaultDisplay(DataLinkTest_ConsumerRemoved, *this, "DataLinkTest_ConsumerRemoved"); + + testFramework.createTestCaseWithDefaultDisplay(CameraDataLinkTest_NoLinks, *this, "CameraDataLinkTest_NoLinks"); + testFramework.createTestCaseWithDefaultDisplay(CameraDataLinkTest_ViewportLinked, *this, "CameraDataLinkTest_ViewportLinked"); + testFramework.createTestCaseWithDefaultDisplay(CameraDataLinkTest_FrustumLinked, *this, "CameraDataLinkTest_FrustumLinked"); + + testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_NoLinks, *this, "TextureLinkTest_NoLinks"); + testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_Linked, *this, "TextureLinkTest_Linked"); + testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_LinksRemoved, *this, "TextureLinkTest_LinksRemoved"); + testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_ProviderSceneUnmapped, *this, "TextureLinkTest_ProviderSceneUnmapped"); + // TODO(Carsten): Find a solution for this test with Vaclav + //testFramework.createTestCaseWithDefaultDisplay(TextureLinkTest_ProvidedTextureChanges, *this, "TextureLinkTest_ProvidedTextureChanges"); + + testFramework.createTestCaseWithDefaultDisplay(MultiTypeLinkTest_NoLinks, *this, "MultiTypeLinkTest_NoLinks"); + testFramework.createTestCaseWithDefaultDisplay(MultiTypeLinkTest_Linked, *this, "MultiTypeLinkTest_Linked"); + } + + bool DataLinkingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + std::string expectedImageName("!!unknown!!"); + float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel; + switch (testCase.m_id) + { + case TransformationLinkTest_ConsumerNotLinkedToProvider: + { + const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerOvrSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_OVERRIDEN, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consAndProvSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_AND_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + testFramework.publishAndFlushScene(providerSceneId); + testFramework.publishAndFlushScene(consumerSceneId); + testFramework.publishAndFlushScene(consumerOvrSceneId); + testFramework.publishAndFlushScene(consAndProvSceneId); + testFramework.getSceneToRendered(providerSceneId); + testFramework.getSceneToRendered(consumerSceneId); + testFramework.getSceneToRendered(consumerOvrSceneId); + testFramework.getSceneToRendered(consAndProvSceneId); + expectedImageName = "DataLinkTest_AllLinksDisabled"; + break; + } + case TransformationLinkTest_ConsumerLinkedToProvider: + { + const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, + glm::vec3(0.0f, 0.0f, 12.0f)); + testFramework.publishAndFlushScene(providerSceneId); + testFramework.publishAndFlushScene(consumerSceneId); + testFramework.getSceneToRendered(providerSceneId); + testFramework.getSceneToRendered(consumerSceneId); + testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); + expectedImageName = "DataLinkTest_ConsumerLinkedToProvider"; + break; + } + case TransformationLinkTest_LinkOverridesComsumerTransform: + { + const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerOvrSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_OVERRIDEN, + glm::vec3(0.0f, 0.0f, 12.0f)); + testFramework.publishAndFlushScene(providerSceneId); + testFramework.publishAndFlushScene(consumerOvrSceneId); + testFramework.getSceneToRendered(providerSceneId); + testFramework.getSceneToRendered(consumerOvrSceneId); + testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerOvrSceneId, TransformationLinkScene::transformConsumerDataId); + expectedImageName = "DataLinkTest_LinkOverridesComsumerTransform"; + break; + } + case TransformationLinkTest_LinkRemoved: + { + const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, + glm::vec3(0.0f, 0.0f, 12.0f)); + testFramework.publishAndFlushScene(providerSceneId); + testFramework.publishAndFlushScene(consumerSceneId); + testFramework.getSceneToRendered(providerSceneId); + testFramework.getSceneToRendered(consumerSceneId); + testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); + testFramework.removeDataLink(consumerSceneId, TransformationLinkScene::transformConsumerDataId); + expectedImageName = "DataLinkTest_LinkRemoved"; + break; + } + case TransformationLinkTest_ConsumerLinkedToProviderNested: + { + const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consAndProvSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER_AND_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, + glm::vec3(0.0f, 0.0f, 12.0f)); + testFramework.publishAndFlushScene(providerSceneId); + testFramework.publishAndFlushScene(consAndProvSceneId); + testFramework.publishAndFlushScene(consumerSceneId); + testFramework.getSceneToRendered(providerSceneId); + testFramework.getSceneToRendered(consAndProvSceneId); + testFramework.getSceneToRendered(consumerSceneId); + testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consAndProvSceneId, TransformationLinkScene::transformConsumerDataId); + testFramework.createDataLink(consAndProvSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); + expectedImageName = "DataLinkTest_ConsumerLinkedToProviderNested"; + break; + } + case TransformationLinkTest_RemovedProviderFromScene: + case TransformationLinkTest_RemovedConsumerFromScene: + { + const ramses::sceneId_t providerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_PROVIDER, + glm::vec3(0.0f, 0.0f, 12.0f)); + const ramses::sceneId_t consumerSceneId = testFramework.getScenesRegistry().createScene(TransformationLinkScene::TRANSFORMATION_CONSUMER, + glm::vec3(0.0f, 0.0f, 12.0f)); + testFramework.publishAndFlushScene(providerSceneId); + testFramework.publishAndFlushScene(consumerSceneId); + testFramework.getSceneToRendered(providerSceneId); + testFramework.getSceneToRendered(consumerSceneId); + testFramework.createDataLink(providerSceneId, TransformationLinkScene::transformProviderDataId, consumerSceneId, TransformationLinkScene::transformConsumerDataId); + + if (TransformationLinkTest_RemovedProviderFromScene == testCase.m_id) + { + ramses::Scene& scene = testFramework.getScenesRegistry().getScene(providerSceneId); + ramses::RamsesObject* provider = scene.findObject("transform provider"); + assert(provider != nullptr); + + scene.destroy(RamsesObjectTypeUtils::ConvertTo(*provider)); + scene.flush(); + } + else if (TransformationLinkTest_RemovedConsumerFromScene == testCase.m_id) + { + ramses::Scene& scene = testFramework.getScenesRegistry().getScene(consumerSceneId); + ramses::RamsesObject* consumer = scene.findObject("transform consumer"); + assert(consumer != nullptr); + + scene.destroy(RamsesObjectTypeUtils::ConvertTo(*consumer)); + scene.flush(); + } + + expectedImageName = "DataLinkTest_LinkRemoved"; + break; + } + case TransformationLinkTest_ConfidenceMultiLinkTest: + { + m_sceneIdProvider = testFramework.getScenesRegistry().createScene(MultiTransformationLinkScene::PROVIDER_SCENE); + m_sceneIdProviderConsumer = testFramework.getScenesRegistry().createScene(MultiTransformationLinkScene::PROVIDER_CONSUMER_SCENE); + m_sceneIdConsumer = testFramework.getScenesRegistry().createScene(MultiTransformationLinkScene::CONSUMER_SCENE); + + testFramework.publishAndFlushScene(m_sceneIdProvider); + testFramework.publishAndFlushScene(m_sceneIdProviderConsumer); + testFramework.publishAndFlushScene(m_sceneIdConsumer); + testFramework.getSceneToRendered(m_sceneIdProvider); + testFramework.getSceneToRendered(m_sceneIdProviderConsumer); + testFramework.getSceneToRendered(m_sceneIdConsumer); + + for (uint32_t rowId = MultiTransformationLinkScene::DataIdRowStart; rowId < MultiTransformationLinkScene::DataIdRowStart + 4u; ++rowId) + { + testFramework.createDataLink(m_sceneIdProvider, ramses::dataProviderId_t{rowId}, m_sceneIdProviderConsumer, ramses::dataConsumerId_t{rowId}); + } + + for (uint32_t meshId = MultiTransformationLinkScene::DataIdMeshStart; meshId < MultiTransformationLinkScene::DataIdMeshStart + 16u; ++meshId) + { + testFramework.createDataLink(m_sceneIdProviderConsumer, ramses::dataProviderId_t{meshId}, m_sceneIdConsumer, ramses::dataConsumerId_t{meshId}); + } + + expectedImageName = "DataLinkTest_ConfidenceMultiLink"; + break; + } + + case DataLinkTest_NoLinks: + createAndShowDataLinkScenes(testFramework); + expectedImageName = "DataLinkTest_NoLinks"; + break; + case DataLinkTest_Linked: + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); + expectedImageName = "DataLinkTest_Linked"; + break; + case DataLinkTest_LinksRemoved: + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshot("DataLinkTest_Linked", 0u, 0.0f)) + { + return false; + } + testFramework.removeDataLink(m_sceneIdConsumer, DataLinkScene::DataConsumerId); + testFramework.removeDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); + expectedImageName = "DataLinkTest_NoLinks"; + break; + case DataLinkTest_ProviderRemoved: + { + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshot("DataLinkTest_Linked", 0u, 0.0f)) + { + return false; + } + + ramses::Scene& scene = testFramework.getScenesRegistry().getScene(m_sceneIdProvider); + auto& consumerAppearance1 = *scene.findObject("dataLinkAppearance1"); + auto& consumerAppearance2 = *scene.findObject("dataLinkAppearance2"); + const auto colorInput = consumerAppearance1.getEffect().findUniformInput("color"); + consumerAppearance1.unbindInput(*colorInput); + consumerAppearance2.unbindInput(*colorInput); + + auto& colorData = *scene.findObject("dataLinkColorData"); + scene.destroy(colorData); + scene.flush(); + + expectedImageName = "DataLinkTest_RemovedProvider"; + } + break; + case DataLinkTest_ConsumerRemoved: + { + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, DataLinkScene::DataProviderId, m_sceneIdProviderConsumer, DataLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, DataLinkScene::DataProviderId, m_sceneIdConsumer, DataLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshot("DataLinkTest_Linked", 0u, 0.0f)) + { + return false; + } + + ramses::Scene& scene = testFramework.getScenesRegistry().getScene(m_sceneIdConsumer); + auto& consumerAppearance1 = *scene.findObject("dataLinkAppearance1"); + auto& consumerAppearance2 = *scene.findObject("dataLinkAppearance2"); + const auto colorInput = consumerAppearance1.getEffect().findUniformInput("color"); + consumerAppearance1.unbindInput(*colorInput); + consumerAppearance2.unbindInput(*colorInput); + + auto& colorData = *scene.findObject("dataLinkColorData"); + scene.destroy(colorData); + scene.flush(); + + expectedImageName = "DataLinkTest_RemovedConsumer"; + } + break; + + case CameraDataLinkTest_NoLinks: + testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_CONSUMER, m_cameraMid); + expectedImageName = "CameraData_NoLinks"; + break; + case CameraDataLinkTest_ViewportLinked: + { + const auto providerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_PROVIDER, m_cameraMid); + const auto consumerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_CONSUMER, m_cameraMid); + testFramework.createDataLink(providerSceneId, CameraDataLinkScene::ViewportOffsetProviderId, consumerSceneId, CameraDataLinkScene::ViewportOffsetConsumerId); + testFramework.createDataLink(providerSceneId, CameraDataLinkScene::ViewportSizeProviderId, consumerSceneId, CameraDataLinkScene::ViewportSizeConsumerId); + expectedImageName = "CameraData_ViewportLinked"; + } + break; + case CameraDataLinkTest_FrustumLinked: + { + const auto providerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_PROVIDER, m_cameraMid); + const auto consumerSceneId = testFramework.createAndShowScene(CameraDataLinkScene::CAMERADATA_CONSUMER, m_cameraMid); + testFramework.createDataLink(providerSceneId, CameraDataLinkScene::FrustumPlanesProviderId, consumerSceneId, CameraDataLinkScene::FrustumPlanesConsumerId); + testFramework.createDataLink(providerSceneId, CameraDataLinkScene::FrustumPlanesNearFarProviderId, consumerSceneId, CameraDataLinkScene::FrustumPlanesNearFarConsumerId); + expectedImageName = "CameraData_FrustumLinked"; + } + break; + + case TextureLinkTest_NoLinks: + createAndShowDataLinkScenes(testFramework); + expectedImageName = "TextureLinkTest_NoLinks"; + break; + case TextureLinkTest_Linked: + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + expectedImageName = "TextureLinkTest_Linked"; + break; + case TextureLinkTest_LinksRemoved: + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshot("TextureLinkTest_Linked", 0u)) + { + return false; + } + testFramework.removeDataLink(m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + testFramework.removeDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); + expectedImageName = "TextureLinkTest_NoLinks"; + break; + case TextureLinkTest_ProviderSceneUnmapped: + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshot("TextureLinkTest_Linked", 0u)) + { + return false; + } + + // unmapping scene will remove any links to it + testFramework.getSceneToState(m_sceneIdProviderConsumer, ramses::RendererSceneState::Available); + + // when scene is mapped and shown again it should be in an initial state without links + testFramework.getSceneToState(m_sceneIdProviderConsumer, ramses::RendererSceneState::Rendered); + + expectedImageName = "TextureLinkTest_NoLinks"; + break; + case TextureLinkTest_ProvidedTextureChanges: + createAndShowDataLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, TextureLinkScene::DataProviderId, m_sceneIdProviderConsumer, TextureLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProviderConsumer, TextureLinkScene::DataProviderId, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshot("TextureLinkTest_Linked", 0u)) + { + return false; + } + + { + ramses::Scene& providerConsumerScene = testFramework.getScenesRegistry().getScene(m_sceneIdProviderConsumer); + const auto& otherTexture = *providerConsumerScene.findObject("ConsumerProviderTexture"); + ramses::Scene& providerScene = testFramework.getScenesRegistry().getScene(m_sceneIdProvider); + providerScene.updateTextureProvider(otherTexture, TextureLinkScene::DataProviderId); + providerScene.flush(); + } + + expectedImageName = "TextureLinkTest_ProviderTextureChanged"; + break; + + case MultiTypeLinkTest_NoLinks: + createAndShowMultiTypeLinkScenes(testFramework); + expectedImageName = "MultiTypeLinkTest_NoLinks"; + break; + case MultiTypeLinkTest_Linked: + createAndShowMultiTypeLinkScenes(testFramework); + testFramework.createDataLink(m_sceneIdProvider, MultiTypeLinkScene::DataProviderId, m_sceneIdConsumer, MultiTypeLinkScene::DataConsumerId); + testFramework.createDataLink(m_sceneIdProvider, MultiTypeLinkScene::TextureProviderId, m_sceneIdConsumer, MultiTypeLinkScene::TextureConsumerId); + testFramework.createDataLink(m_sceneIdConsumer, MultiTypeLinkScene::TransformationProviderId, m_sceneIdProvider, MultiTypeLinkScene::TransformationConsumerId); + expectedImageName = "MultiTypeLinkTest_Linked"; + break; + default: + assert(false && "undefined test case"); + } + + return RenderAndCompareScreenshot(testFramework, expectedImageName, 0u, expectedPixelError); + } + + bool DataLinkingTests::RenderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx, float expectedPixelError) + { + // changes to offscreen buffers are delayed by 1 frame, render one frame before taking screenshot + testFramework.flushRendererAndDoOneLoop(); + return testFramework.renderAndCompareScreenshot(expectedImageName, testDisplayIdx, expectedPixelError); + } + + template + void DataLinkingTests::createAndShowDataLinkScenes(RendererTestsFramework& testFramework) + { + m_sceneIdProvider = testFramework.createAndShowScene(LINKSCENE::DATA_PROVIDER , m_cameraHigh); + m_sceneIdProviderConsumer = testFramework.createAndShowScene(LINKSCENE::DATA_CONSUMER_AND_PROVIDER, m_cameraMid); + m_sceneIdConsumer = testFramework.createAndShowScene(LINKSCENE::DATA_CONSUMER , m_cameraLow); + } + + void DataLinkingTests::createAndShowMultiTypeLinkScenes(RendererTestsFramework& testFramework) + { + m_sceneIdConsumer = testFramework.createAndShowScene(MultiTypeLinkScene::TRANSFORMATION_PROVIDER_DATA_AND_TEXTURE_CONSUMER, m_cameraLow); + m_sceneIdProvider = testFramework.createAndShowScene(MultiTypeLinkScene::TRANSFORMATION_CONSUMER_DATA_AND_TEXTURE_PROVIDER, m_cameraHigh); + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/DataLinkingTests.h b/tests/integration/renderer-tests/rendering-tests/DataLinkingTests.h new file mode 100644 index 000000000..a666b49f4 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/DataLinkingTests.h @@ -0,0 +1,71 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" +#include "ramses/framework/RamsesFrameworkTypes.h" + +#include + +namespace ramses::internal +{ + class DataLinkingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + static bool RenderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx = 0u, float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel); + + template + void createAndShowDataLinkScenes(RendererTestsFramework& testFramework); + + void createAndShowMultiTypeLinkScenes(RendererTestsFramework& testFramework); + + enum + { + TransformationLinkTest_ConsumerNotLinkedToProvider, + TransformationLinkTest_ConsumerLinkedToProvider, + TransformationLinkTest_LinkRemoved, + TransformationLinkTest_LinkOverridesComsumerTransform, + TransformationLinkTest_ConsumerLinkedToProviderNested, + TransformationLinkTest_RemovedProviderFromScene, + TransformationLinkTest_RemovedConsumerFromScene, + TransformationLinkTest_ConfidenceMultiLinkTest, + + DataLinkTest_NoLinks, + DataLinkTest_Linked, + DataLinkTest_LinksRemoved, + DataLinkTest_ProviderRemoved, + DataLinkTest_ConsumerRemoved, + + CameraDataLinkTest_NoLinks, + CameraDataLinkTest_ViewportLinked, + CameraDataLinkTest_FrustumLinked, + + TextureLinkTest_NoLinks, + TextureLinkTest_Linked, + TextureLinkTest_LinksRemoved, + TextureLinkTest_ProviderSceneUnmapped, + TextureLinkTest_ProvidedTextureChanges, + + MultiTypeLinkTest_NoLinks, + MultiTypeLinkTest_Linked + }; + + ramses::sceneId_t m_sceneIdProvider; + ramses::sceneId_t m_sceneIdProviderConsumer; + ramses::sceneId_t m_sceneIdConsumer; + + const glm::vec3 m_cameraLow{ -1.f, 2.f, 8.f }; + const glm::vec3 m_cameraHigh{ 1.f, -2.f, 8.f }; + const glm::vec3 m_cameraMid{ 0.f, 0.f, 8.f }; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.cpp b/tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.cpp new file mode 100644 index 000000000..13906fdb8 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.cpp @@ -0,0 +1,393 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "DisplayRenderingTests.h" +#include "TestScenes/MultipleTrianglesScene.h" +#include "TestScenes/RenderTargetScene.h" +#include "TestScenes/TextScene.h" +#include "RendererTestUtils.h" +#include "impl/DisplayConfigImpl.h" + +namespace ramses::internal +{ + void DisplayRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + ramses::DisplayConfig displayConfig1 = RendererTestUtils::CreateTestDisplayConfig(0); + displayConfig1.setWindowRectangle(0, 0, DisplayWidth, DisplayHeight); + + ramses::DisplayConfig displayConfig2 = RendererTestUtils::CreateTestDisplayConfig(1); + displayConfig2.setWindowRectangle(0, 0, DisplayWidth, DisplayHeight); + + ramses::DisplayConfig displayConfigWithoutDepthAndStencil = RendererTestUtils::CreateTestDisplayConfig(3); + displayConfigWithoutDepthAndStencil.setWindowRectangle(0, 0, ramses::internal::IntegrationScene::DefaultViewportWidth, ramses::internal::IntegrationScene::DefaultViewportHeight); + displayConfigWithoutDepthAndStencil.setDepthStencilBufferType(ramses::EDepthBufferType::None); + + ramses::DisplayConfig displayConfigWithoutStencil = RendererTestUtils::CreateTestDisplayConfig(4); + displayConfigWithoutStencil.setWindowRectangle(0, 0, ramses::internal::IntegrationScene::DefaultViewportWidth, ramses::internal::IntegrationScene::DefaultViewportHeight); + displayConfigWithoutStencil.setDepthStencilBufferType(ramses::EDepthBufferType::Depth); + + ramses::DisplayConfig displayConfigWithoutAsyncEffectUpload = RendererTestUtils::CreateTestDisplayConfig(3); + displayConfigWithoutAsyncEffectUpload.setWindowRectangle(0, 0, DisplayWidth, DisplayHeight); + displayConfigWithoutAsyncEffectUpload.setAsyncEffectUploadEnabled(false); + + testFramework.createTestCase(DisplayRenderingTest_TwoScenes, *this, "DisplayRenderingTest_TwoScenes").m_displayConfigs.push_back(displayConfig1); + testFramework.createTestCase(DisplayRenderingTest_UnpublishScene, *this, "DisplayRenderingTest_UnpublishScene").m_displayConfigs.push_back(displayConfig1); + testFramework.createTestCase(DisplayRenderingTest_HideScene, *this, "DisplayRenderingTest_HideScene").m_displayConfigs.push_back(displayConfig1); + testFramework.createTestCase(DisplayRenderingTest_SceneRenderOrder, *this, "DisplayRenderingTest_SceneRenderOrder").m_displayConfigs.push_back(displayConfig1); + testFramework.createTestCase(DisplayRenderingTest_SceneRenderOrderInversed, *this, "DisplayRenderingTest_SceneRenderOrderInversed").m_displayConfigs.push_back(displayConfig1); + + // These tests are using default display config from framework which uses higher resolution than other display tests + testFramework.createTestCaseWithDefaultDisplay(DisplayRenderingTest_Subimage, *this, "DisplayRenderingTest_Subimage"); + + RenderingTestCase& testCaseRemap = testFramework.createTestCase(DisplayRenderingTest_RemapScene, *this, "DisplayRenderingTest_RemapScene"); + testCaseRemap.m_displayConfigs.push_back(displayConfig1); + testCaseRemap.m_displayConfigs.push_back(displayConfig2); + + RenderingTestCase& testCaseSwap = testFramework.createTestCase(DisplayRenderingTest_SwapScenes, *this, "DisplayRenderingTest_SwapScenes"); + testCaseSwap.m_displayConfigs.push_back(displayConfig1); + testCaseSwap.m_displayConfigs.push_back(displayConfig2); + + RenderingTestCase& testCaseRenderTarget = testFramework.createTestCase(DisplayRenderingTest_RemapSceneWithRenderTarget, *this, "DisplayRenderingTest_RemapSceneWithRenderTarget"); + testCaseRenderTarget.m_displayConfigs.push_back(displayConfig1); + testCaseRenderTarget.m_displayConfigs.push_back(displayConfig2); + +#if defined(RAMSES_TEXT_ENABLED) + RenderingTestCase& testCaseText = testFramework.createTestCase(DisplayRenderingTest_RemapSceneWithText, *this, "DisplayRenderingTest_RemapSceneWithText"); + testCaseText.m_displayConfigs.push_back(displayConfig1); + testCaseText.m_displayConfigs.push_back(displayConfig2); +#endif + + RenderingTestCase& testCaseRemapChanged = testFramework.createTestCase(DisplayRenderingTest_RemapSceneWithChangedContent, *this, "DisplayRenderingTest_RemapSceneWithChangedContent"); + testCaseRemapChanged.m_displayConfigs.push_back(displayConfig1); + testCaseRemapChanged.m_displayConfigs.push_back(displayConfig2); + + testFramework.createTestCase(DisplayRenderingTest_ResubscribeScene, *this, "DisplayRenderingTest_ResubscribeScene").m_displayConfigs.push_back(displayConfig1); + + testFramework.createTestCase(DisplayRenderingTest_FramebufferWithoutDepthAndStencil, *this, "DisplayRenderingTest_FramebufferWithoutDepthAndStencil").m_displayConfigs.push_back(displayConfigWithoutDepthAndStencil); + testFramework.createTestCase(DisplayRenderingTest_FramebufferWithoutStencil , *this, "DisplayRenderingTest_FramebufferWithoutStencil").m_displayConfigs.push_back(displayConfigWithoutStencil); + + testFramework.createTestCase(DisplayRenderingTest_AsyncEffectUploadDisabled, *this, "DisplayRenderingTest_AsyncEffectUploadDisabled").m_displayConfigs.push_back(displayConfigWithoutAsyncEffectUpload); + } + + bool DisplayRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + switch (testCase.m_id) + { + case DisplayRenderingTest_AsyncEffectUploadDisabled: + case DisplayRenderingTest_TwoScenes: + return RunTwoScenesTest(testFramework); + case DisplayRenderingTest_UnpublishScene: + return RunUnpublishTest(testFramework); + case DisplayRenderingTest_HideScene: + return RunHideTest(testFramework); + case DisplayRenderingTest_SceneRenderOrder: + return RunSceneRenderOrderTest(testFramework); + case DisplayRenderingTest_SceneRenderOrderInversed: + return RunSceneRenderOrderInversedTest(testFramework); + case DisplayRenderingTest_Subimage: + return RunSubimageTest(testFramework); + case DisplayRenderingTest_RemapScene: + return RunRemapSceneTest(testFramework); + case DisplayRenderingTest_SwapScenes: + return RunSwapScenesTest(testFramework); + case DisplayRenderingTest_RemapSceneWithRenderTarget: + return RunRemapSceneWithRenderTargetTest(testFramework); +#if defined(RAMSES_TEXT_ENABLED) + case DisplayRenderingTest_RemapSceneWithText: + return RunRemapSceneWithTextTest(testFramework); +#endif + case DisplayRenderingTest_RemapSceneWithChangedContent: + return RunRemapSceneWithChangedContentTest(testFramework); + case DisplayRenderingTest_ResubscribeScene: + return RunResubscribeSceneTest(testFramework); + case DisplayRenderingTest_FramebufferWithoutDepthAndStencil: + return RunFramebufferWithoutDepthAndStencilTest(testFramework); + case DisplayRenderingTest_FramebufferWithoutStencil: + return RunFramebufferWithoutStencil(testFramework); + default: + assert(!"Invalid renderer test ID!"); + return false; + } + } + + bool DisplayRenderingTests::RunTwoScenesTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::SUBTRACTIVE_BLENDING, + glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId); + testFramework.publishAndFlushScene(otherId); + testFramework.getSceneToRendered(sceneId); + testFramework.getSceneToRendered(otherId); + + return testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenes"); + } + + bool DisplayRenderingTests::RunUnpublishTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::SUBTRACTIVE_BLENDING, + glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId); + testFramework.publishAndFlushScene(otherId); + testFramework.getSceneToRendered(sceneId); + testFramework.getSceneToRendered(otherId); + + bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenes"); + + testFramework.getScenesRegistry().getScene(otherId).unpublish(); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_UnpublishedScene"); + + return testResult; + } + + bool DisplayRenderingTests::RunHideTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::SUBTRACTIVE_BLENDING, + glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId); + testFramework.publishAndFlushScene(otherId); + testFramework.getSceneToRendered(sceneId); + testFramework.getSceneToRendered(otherId); + + bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenes"); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Ready); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_HiddenScene"); + + return testResult; + } + + bool DisplayRenderingTests::RunSceneRenderOrderTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::ALPHA_BLENDING, + glm::vec3(0.5f, 0.5f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(-0.5f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId); + testFramework.publishAndFlushScene(otherId); + testFramework.getSceneToRendered(sceneId); + testFramework.getSceneToRendered(otherId); + testFramework.assignSceneToDisplayBuffer(sceneId, testFramework.getDisplayFramebufferId(0u), 1); + testFramework.assignSceneToDisplayBuffer(otherId, testFramework.getDisplayFramebufferId(0u), 2); + + return testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenesOrdered"); + } + + bool DisplayRenderingTests::RunSceneRenderOrderInversedTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::ALPHA_BLENDING, + glm::vec3(0.5f, 0.5f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t otherId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(-0.5f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId); + testFramework.publishAndFlushScene(otherId); + testFramework.getSceneToRendered(sceneId); + testFramework.getSceneToRendered(otherId); + testFramework.assignSceneToDisplayBuffer(sceneId, testFramework.getDisplayFramebufferId(0u), 2); + testFramework.assignSceneToDisplayBuffer(otherId, testFramework.getDisplayFramebufferId(0u), 1); + + return testFramework.renderAndCompareScreenshot("ARendererDisplays_TwoScenesInverseOrdered"); + } + + bool DisplayRenderingTests::RunSubimageTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::SUBIMAGES); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + return testFramework.renderAndCompareScreenshotSubimage("MultipleTrianglesScene_Subimages_middle", 89u, 69u, 30u, 102u); + } + + bool DisplayRenderingTests::RunRemapSceneTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId, 0u); + bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); + testFramework.setSceneMapping(sceneId, 1u); + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 1u); + + return testResult; + } + + bool DisplayRenderingTests::RunSwapScenesTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId1 = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t sceneId2 = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::TRIANGLES_REORDERED, + glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId1); + testFramework.publishAndFlushScene(sceneId2); + testFramework.setSceneMapping(sceneId1, 0u); + testFramework.setSceneMapping(sceneId2, 1u); + testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); + testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); + + bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Triangles_reordered", 1u); + + testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Available); + testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Available); + testFramework.setSceneMapping(sceneId1, 1u); + testFramework.setSceneMapping(sceneId2, 0u); + testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); + testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); + + testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Triangles_reordered", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 1u); + + return testResult; + } + + bool DisplayRenderingTests::RunRemapSceneWithRenderTargetTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::RenderTargetScene::PERSPECTIVE_PROJECTION, + glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId, 0u); + bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_RenderTarget", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); + testFramework.setSceneMapping(sceneId, 1u); + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_RenderTarget", 1u); + + return testResult; + } + +#if defined(RAMSES_TEXT_ENABLED) + bool DisplayRenderingTests::RunRemapSceneWithTextTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::TextScene::EState_INITIAL_128_BY_64_VIEWPORT, + glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_SimpleText", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); + testFramework.setSceneMapping(sceneId, 1u); + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_SimpleText", 1u); + + return testResult; + } +#endif + + bool DisplayRenderingTests::RunRemapSceneWithChangedContentTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId1 = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(2.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + const ramses::sceneId_t sceneId2 = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::TRIANGLES_REORDERED, + glm::vec3(-2.5f, -0.5f, 5.0f), DisplayWidth, DisplayHeight); + + testFramework.publishAndFlushScene(sceneId1); + testFramework.publishAndFlushScene(sceneId2); + testFramework.getSceneToRendered(sceneId1, 0u); + testFramework.getSceneToRendered(sceneId2, 0u); + + bool testResult = testFramework.renderAndCompareScreenshot("ARendererDisplays_ModifiedScenes1", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); + + // unmap scenes + testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Available); + testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Available); + + // modify scenes + testFramework.getScenesRegistry().setSceneState(sceneId1, ramses::internal::MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().setSceneState(sceneId2, ramses::internal::MultipleTrianglesScene::ADDITIVE_BLENDING); + testFramework.getScenesRegistry().getScene(sceneId1).flush(); + testFramework.getScenesRegistry().getScene(sceneId2).flush(); + + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 1u); + + // map scenes to second display + testFramework.setSceneMapping(sceneId1, 1u); + testFramework.setSceneMapping(sceneId2, 1u); + testFramework.getSceneToState(sceneId1, ramses::RendererSceneState::Rendered); + testFramework.getSceneToState(sceneId2, ramses::RendererSceneState::Rendered); + + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_ModifiedScenes2", 1u); + + return testResult; + } + + bool DisplayRenderingTests::RunResubscribeSceneTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, + glm::vec3(0.0f, 0.0f, 5.0f), DisplayWidth, DisplayHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + bool testResult = testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); + testResult &= testFramework.renderAndCompareScreenshot("ARendererDisplays_Black", 0u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + testResult &= testFramework.renderAndCompareScreenshot("ARendererInstance_Three_Triangles", 0u); + + return testResult; + } + + bool DisplayRenderingTests::RunFramebufferWithoutDepthAndStencilTest(RendererTestsFramework& testFramework) + { + ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::DEPTH_FUNC); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + bool testResult = testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_DepthFunc_NoDepthBuffer"); + + testFramework.getScenesRegistry().getScene(sceneId).unpublish(); + + sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::STENCIL_TEST_1); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + testResult &= testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer"); + + return testResult; + } + + bool DisplayRenderingTests::RunFramebufferWithoutStencil(RendererTestsFramework& testFramework) + { + ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::DEPTH_FUNC); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + bool testResult = testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_DepthFunc"); + + testFramework.getScenesRegistry().getScene(sceneId).unpublish(); + + sceneId = testFramework.getScenesRegistry().createScene(ramses::internal::MultipleTrianglesScene::STENCIL_TEST_1); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + testResult &= testFramework.renderAndCompareScreenshot("MultipleTrianglesScene_StencilTest_1_NoStencilBuffer"); + + return testResult; + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.h b/tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.h new file mode 100644 index 000000000..2fb180fc6 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/DisplayRenderingTests.h @@ -0,0 +1,61 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +namespace ramses::internal +{ + class DisplayRenderingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + static bool RunTwoScenesTest(RendererTestsFramework& testFramework); + static bool RunUnpublishTest(RendererTestsFramework& testFramework); + static bool RunHideTest(RendererTestsFramework& testFramework); + static bool RunSceneRenderOrderTest(RendererTestsFramework& testFramework); + static bool RunSceneRenderOrderInversedTest(RendererTestsFramework& testFramework); + static bool RunSubimageTest(RendererTestsFramework& testFramework); + static bool RunRemapSceneTest(RendererTestsFramework& testFramework); + static bool RunSwapScenesTest(RendererTestsFramework& testFramework); + static bool RunRemapSceneWithRenderTargetTest(RendererTestsFramework& testFramework); + static bool RunRemapSceneWithTextTest(RendererTestsFramework& testFramework); + static bool RunRemapSceneWithChangedContentTest(RendererTestsFramework& testFramework); + static bool RunResubscribeSceneTest(RendererTestsFramework& testFramework); + static bool RunFramebufferWithoutDepthAndStencilTest(RendererTestsFramework& testFramework); + static bool RunFramebufferWithoutStencil(RendererTestsFramework& testFramework); + + enum + { + DisplayRenderingTest_TwoScenes = 0, + DisplayRenderingTest_UnpublishScene, + DisplayRenderingTest_HideScene, + DisplayRenderingTest_SceneRenderOrder, + DisplayRenderingTest_SceneRenderOrderInversed, + DisplayRenderingTest_Subimage, + DisplayRenderingTest_RemapScene, + DisplayRenderingTest_SwapScenes, + DisplayRenderingTest_RemapSceneWithRenderTarget, +#if defined(RAMSES_TEXT_ENABLED) + DisplayRenderingTest_RemapSceneWithText, +#endif + DisplayRenderingTest_RemapSceneWithChangedContent, + DisplayRenderingTest_ResubscribeScene, + DisplayRenderingTest_FramebufferWithoutDepthAndStencil, + DisplayRenderingTest_FramebufferWithoutStencil, + DisplayRenderingTest_AsyncEffectUploadDisabled + }; + + static constexpr uint32_t DisplayWidth = 128u; + static constexpr uint32_t DisplayHeight = 64u; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.cpp b/tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.cpp new file mode 100644 index 000000000..13b979f9e --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.cpp @@ -0,0 +1,65 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "EffectRenderingTests.h" +#include "TestScenes/ShaderTestScene.h" +#include "TestScenes/SingleAppearanceScene.h" +#include "TestScenes/CustomShaderTestScene.h" + +namespace ramses::internal +{ + void EffectRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(EffectTest_Shaders, *this, "EffectTest_Shaders"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_Discard_Shaders, *this, "EffectTest_Discard_Shaders"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_UniformWithSameNameInBothStages, *this, "EffectTest_UniformWithSameNameInBothStages"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_ExplicitAttributeLocation, *this, "EffectTest_ExplicitAttributeLocation"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_ExplicitAttributeLocationSwapped, *this, "EffectTest_ExplicitAttributeLocationSwapped"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_StructUniform, *this, "EffectTest_StructUniform"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_BoolUniform, *this, "EffectTest_BoolUniform"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_TextureSize, *this, "EffectTest_TextureSize"); + testFramework.createTestCaseWithDefaultDisplay(EffectTest_OptimizedInput, *this, "EffectTest_OptimizedInput"); + } + + bool EffectRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + switch (testCase.m_id) + { + case EffectTest_Shaders: + return runBasicTest(testFramework, SingleAppearanceScene::RED_TRIANGLES, "SingleAppearanceScene_RedTriangles"); + case EffectTest_Discard_Shaders: + return runBasicTest(testFramework, ShaderTestScene::DISCARD, "ShaderTestScene_Discard"); + case EffectTest_OptimizedInput: + return runBasicTest(testFramework, ShaderTestScene::OPTIMIZED_INPUT, "ShaderTestScene_OptimizedInput"); + case EffectTest_UniformWithSameNameInBothStages: + return runBasicTest(testFramework, ShaderTestScene::UNIFORM_WITH_SAME_NAME_IN_BOTH_STAGES, "ShaderTestScene_MultiStageUniform"); + case EffectTest_ExplicitAttributeLocation: + return runBasicTest(testFramework, CustomShaderTestScene::EXPLICIT_ATTRIBUTE_LOCATION, "ShaderTestScene_TexturedQuad"); + case EffectTest_ExplicitAttributeLocationSwapped: + return runBasicTest(testFramework, CustomShaderTestScene::EXPLICIT_ATTRIBUTE_LOCATION_SWAPPED, "ShaderTestScene_TexturedQuad"); + case EffectTest_StructUniform: + return runBasicTest(testFramework, ShaderTestScene::STRUCT_UNIFORM, "ShaderTestScene_StructUniform"); + case EffectTest_BoolUniform: + return runBasicTest(testFramework, ShaderTestScene::BOOL_UNIFORM, "ShaderTestScene_BoolUniform"); + case EffectTest_TextureSize: + return runBasicTest(testFramework, ShaderTestScene::TEXTURE_SIZE, "ShaderTestScene_TextureSize"); + default: + assert(!"Invalid renderer test ID!"); + return false; + } + } + + template + bool EffectRenderingTests::runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + return testFramework.renderAndCompareScreenshot(expectedImageName, 0u); + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.h b/tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.h new file mode 100644 index 000000000..c23006a1f --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/EffectRenderingTests.h @@ -0,0 +1,38 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +namespace ramses::internal +{ + class EffectRenderingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + template + bool runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName); + + enum + { + EffectTest_Shaders = 0, + EffectTest_Discard_Shaders, + EffectTest_OptimizedInput, + EffectTest_UniformWithSameNameInBothStages, + EffectTest_ExplicitAttributeLocation, + EffectTest_ExplicitAttributeLocationSwapped, + EffectTest_StructUniform, + EffectTest_BoolUniform, + EffectTest_TextureSize, + }; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.cpp b/tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.cpp new file mode 100644 index 000000000..373f1334f --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.cpp @@ -0,0 +1,297 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "InterruptibleOffscreenBufferLinkTests.h" +#include "TestScenes/TextureLinkScene.h" +#include "TestScenes/MultipleTrianglesScene.h" +#include "internal/RendererLib/RenderExecutor.h" + +namespace ramses::internal +{ + void InterruptibleOffscreenBufferLinkTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene, *this, "InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes, *this, "InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene, *this, "InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene, *this, "InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted, *this, "InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted, *this, "InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered, *this, "InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer, *this, "InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + testFramework.createTestCaseWithDefaultDisplay(InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer, *this, "InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer") + .m_displayConfigs.front().setClearColor({1.f, 0.f, 0.f, 1.f}); + } + + bool InterruptibleOffscreenBufferLinkTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + testFramework.setFrameTimerLimits(std::numeric_limits::max(), 0u); + // Override the number of meshes rendered between the time budget checks, for the test we need to interrupt at every single renderable + ramses::internal::RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = 1u; + + bool testResultValue = true; + switch (testCase.m_id) + { + case InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + // provider scene has two meshes, there is interruption after each, that means 3 frames to finish the rendering + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 3u); + // once more frame to render FB with the finished OB contents + testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedFinished"); + break; + } + case InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER, m_cameraMid - glm::vec3{ 1.f, 0.f, 0.f }, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER, m_cameraMid + glm::vec3{ 1.f, 0.f, 0.f }, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + // each provider scene has two meshes, there is interruption after each mesh, that means 5 frames to finish the rendering + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 5u); + // once more frame to render FB with the finished OB contents + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes"); + break; + } + case InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene: + { + const ramses::sceneId_t trianglesSceneId = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES); + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState0"); + + testFramework.getScenesRegistry().setSceneState(trianglesSceneId, MultipleTrianglesScene::ALPHA_BLENDING); + testFramework.getScenesRegistry().getScene(trianglesSceneId).flush(); + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState1"); + + testFramework.getScenesRegistry().setSceneState(trianglesSceneId, MultipleTrianglesScene::COLOR_MASK); + testFramework.getScenesRegistry().getScene(trianglesSceneId).flush(); + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState2"); + + testFramework.getScenesRegistry().setSceneState(trianglesSceneId, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(trianglesSceneId).flush(); + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_FBState3Linked"); + break; + } + case InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + const ramses::sceneId_t m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); + testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); + + // each provider scene has two meshes, there is interruption after each mesh, that means 3 frames to finish the 1st provider + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted2", 3u); + // in the 3rd frame 1st provider is finished and 2nd provider has one mesh rendered + + // 1st provider will be visible next frame, another 2 frames needed to finish 2nd provider + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedFinished2", 2u); + + // 2nd provider will be visible next frame + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene"); + + break; + } + case InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + // render 1 mesh and interrupt + testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedInterrupted"); + + // change scene state + testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); + + // render 3 more frames to finish initial scene state (and new state started to be rendered into OB's 'backbuffer') + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 3u); + // next frame initial scene state is shown in FB and the new state is finished after 4 more frames + testResultValue &= RenderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles", 4u); + + // next frame new scene state is shown in FB + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered"); + break; + } + + case InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + const ramses::sceneId_t m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); + testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); + + // render 1 mesh and interrupt + testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedInterrupted2"); + + // change scene state + testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); + + // render 3 more frames to finish initial provider1 state (and provider2 started to be rendered) + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted2", 3u); + // next frame initial provider1 state is shown in FB and provider2 is finished after 2 more frames + testResultValue &= RenderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles2", 2u); + // next frame provider2 is shown in FB and new state provider1 renders and finishes in 4 frames + testResultValue &= RenderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles3", 4u); + // new state provider1 is shown in FB + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered2"); + break; + } + case InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + const ramses::sceneId_t m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid + glm::vec3{ 0.f, 2.f, 0.f }); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); + testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); + + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted2", 4u); + //initial state of 1st provider scene gets visible (provider scene has 3 meshes, so get visible after 5 loops) + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_ThreeTriangles2"); + + //modify state of 1st provider (now 2nd provider is being rendered) + testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); + + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_ThreeTriangles2"); + //2nd provider gets visible (has 2 meshes) + testResultValue &= RenderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles3", 4u); + //modification of 1st provider gets visible + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered2"); + break; + } + case InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid + glm::vec3{5.0f, 0.0f, 0.0f}, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer, 0); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer, -1); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + //render once then update provider + testResultValue &= testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_LinkedInterrupted"); + + testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); + + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 5u); + //both provider get visible (have 5 meshes together, need 7 loops) + testResultValue &= RenderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB", 6u); + //modification of 1st provider gets visible + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB"); + break; + } + case InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer: + { + const ramses::sceneId_t m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, {0,0,0}, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid + glm::vec3{ 5.0f, 0.0f, 0.0f }, OBWidth, OBHeight); + const ramses::sceneId_t m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, true); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer, 0); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer, -1); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 4u); + + //modify state of 1st provider (now 2nd provider is being rendered) + testFramework.getScenesRegistry().setSceneState(m_sceneIdProvider, MultipleTrianglesScene::TRIANGLES_REORDERED); + testFramework.getScenesRegistry().getScene(m_sceneIdProvider).flush(); + + testResultValue &= RenderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedInterrupted", 2u); + //2nd provider gets visible (has 2 meshes) + testResultValue &= RenderAndCompareScreenshot(testFramework, "InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB", 6u); + //modification of 1st provider gets visible + testResultValue &= testFramework.renderAndCompareScreenshot("InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB"); + break; + } + + default: + assert(false && "undefined test case"); + } + + // Restore the default value for time budget checks + ramses::internal::RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = ramses::internal::RenderExecutor::DefaultNumRenderablesToRenderInBetweenTimeBudgetChecks; + + return testResultValue; + } + + template + ramses::sceneId_t InterruptibleOffscreenBufferLinkTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth, uint32_t vpHeight) + { + const auto sceneId = testFramework.getScenesRegistry().createScene(sceneState, camPos, vpWidth, vpHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + + return sceneId; + } + + bool InterruptibleOffscreenBufferLinkTests::RenderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImage, uint32_t numFramesToRender) + { + bool result = true; + + for (uint32_t i = 0u; i < numFramesToRender; ++i) + result &= testFramework.renderAndCompareScreenshot(expectedImage); + + return result; + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.h b/tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.h new file mode 100644 index 000000000..f98d8b7d3 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/InterruptibleOffscreenBufferLinkTests.h @@ -0,0 +1,46 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +#include + +namespace ramses::internal +{ + class InterruptibleOffscreenBufferLinkTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final override; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final override; + + private: + enum + { + InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithOneScene, + InterruptibleOffscreenBufferLinkTest_OneInterruptibleOBWithTwoScenes, + InterruptibleOffscreenBufferLinkTest_InterruptionDoesNotAffectFrameBufferScene, + InterruptibleOffscreenBufferLinkTest_TwoInterruptibleOBsEachWithOneScene, + InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileInterrupted, + InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted, + InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered, + InterruptibleOffscreenBufferLinkTest_RerendersInterruptedSceneIfItGetsModifiedWhileInterrupted_SameBuffer, + InterruptibleOffscreenBufferLinkTest_RerendersSceneIfItGetsModifiedWhileAnotherSceneIsBeingRendered_SameBuffer, + }; + + template + ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos = { 0, 0, 0 }, uint32_t vpWidth = ramses::internal::IntegrationScene::DefaultViewportWidth, uint32_t vpHeight = ramses::internal::IntegrationScene::DefaultViewportHeight); + static bool RenderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImage, uint32_t numFramesToRender = 1u); + + const glm::vec3 m_cameraMid{ 0.f, 0.f, 8.f }; + + static constexpr uint32_t OBWidth = 256u; + static constexpr uint32_t OBHeight = 256u; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.cpp b/tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.cpp new file mode 100644 index 000000000..4ac4b7ebd --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.cpp @@ -0,0 +1,319 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "OffscreenBufferLinkTests.h" +#include "TestScenes/TextureLinkScene.h" +#include "TestScenes/MultipleTrianglesScene.h" +#include "internal/PlatformAbstraction/PlatformThread.h" + +namespace ramses::internal +{ + OffscreenBufferLinkTests::OffscreenBufferLinkTests(bool useInterruptibleBuffers) + : m_interruptibleBuffers(useInterruptibleBuffers) + { + } + + void OffscreenBufferLinkTests::setUpTestCases(RendererTestsFramework& testFramework) + { + const std::string baseTestName = (m_interruptibleBuffers ? "InterruptibleOffscreenBufferLinkTest_" : "OffscreenBufferLinkTest_"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerLinkedToEmptyBuffer, *this, baseTestName + "ConsumerLinkedToEmptyBuffer"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumersLinkedToBufferWithOneScene, *this, baseTestName + "ConsumersLinkedToBufferWithOneScene"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_OneOfTwoLinksRemoved, *this, baseTestName + "OneOfTwoLinksRemoved"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerLinkedToBufferWithTwoScenes, *this, baseTestName + "ConsumerLinkedToBufferWithTwoScenes"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerRelinkedToAnotherBuffer, *this, baseTestName + "ConsumerRelinkedToAnotherBuffer"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ProviderBufferDestroyed, *this, baseTestName + "ProviderBufferDestroyed"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_SourceSceneHidden, *this, baseTestName + "SourceSceneHidden"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_SourceSceneAssignedToFBWhileShown, *this, baseTestName + "SourceSceneAssignedToFBWhileShown"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_OneOfTwoSourceScenesUnmapped, *this, baseTestName + "OneOfTwoSourceScenesUnmapped"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ProviderSceneUsesDepthTest, *this, baseTestName + "ProviderSceneUsesDepthTest"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ProviderSceneUsesStencilTest, *this, baseTestName + "ProviderSceneUsesStencilTest"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_SetClearColor, *this, baseTestName + "OffscreenBufferLinkTest_SetClearColor"); + if (!m_interruptibleBuffers) + { + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerLinkedToMSAABuffer, *this, baseTestName + "OffscreenBufferLinkTest_MSAA"); + testFramework.createTestCaseWithDefaultDisplay(OffscreenBufferLinkTest_ConsumerUnlinkedMSAABuffer, *this, baseTestName + "OffscreenBufferLinkTest_MSAAUnlinked"); + } + } + + bool OffscreenBufferLinkTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + std::string expectedImageName("!!unknown!!"); + float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel; + switch (testCase.m_id) + { + case OffscreenBufferLinkTest_ConsumerLinkedToEmptyBuffer: + { + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + expectedImageName = "OffscreenBufferLinkTest_Black"; + break; + } + case OffscreenBufferLinkTest_ConsumersLinkedToBufferWithOneScene: + { + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked_OB_Content", 0u, offscreenBuffer, OBWidth, OBHeight)) + return false; + + expectedImageName = "OffscreenBufferLinkTest_Linked"; + break; + } + case OffscreenBufferLinkTest_ConsumerLinkedToMSAABuffer: + { + m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.2f, 0.5f, -5.f), 16u, 16u); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_MS, { 0.2f, 0.f, -3.f }); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, 16u, 16u, m_interruptibleBuffers, 4u); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + expectedImageName = "OffscreenBufferLinkTest_MSAABuffer"; + break; + } + case OffscreenBufferLinkTest_ConsumerUnlinkedMSAABuffer: + { + m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::TRIANGLES_REORDERED, glm::vec3(0.2f, 0.5f, -5.f), 16u, 16u); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_MS, { 0.2f, 0.f, -3.f }); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, 16u, 16u, m_interruptibleBuffers, 4u); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_MSAABuffer", 0u)) + { + return false; + } + + testFramework.removeDataLink(m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + expectedImageName = "OffscreenBufferLinkTest_UnlinkedMSAABuffer"; + break; + } + case OffscreenBufferLinkTest_ConsumerLinkedToBufferWithTwoScenes: + { + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_AND_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content", 0u, offscreenBuffer, OBWidth, OBHeight)) + return false; + + expectedImageName = "OffscreenBufferLinkTest_LinkedTwoScenes"; + break; + } + case OffscreenBufferLinkTest_ConsumerRelinkedToAnotherBuffer: + { + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_AND_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); + + const ramses::displayBufferId_t offscreenBuffer1 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + const ramses::displayBufferId_t offscreenBuffer2 = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer1); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer2); + testFramework.createBufferDataLink(offscreenBuffer1, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked_OB_Content", 0u, offscreenBuffer1, OBWidth, OBHeight)) + return false; + if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked2_OB_Content", 0u, offscreenBuffer2, OBWidth, OBHeight)) + return false; + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) + return false; + + // relink to another buffer + testFramework.createBufferDataLink(offscreenBuffer2, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_Linked2_OB_Content", 0u, offscreenBuffer2, OBWidth, OBHeight)) + return false; + + expectedImageName = "OffscreenBufferLinkTest_Linked2"; + break; + } + case OffscreenBufferLinkTest_OneOfTwoLinksRemoved: + { + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraHigh); + m_sceneIdConsumer2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer2, TextureLinkScene::DataConsumerId); + if (!testFramework.renderAndCompareScreenshotOffscreenBuffer("OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content", 0u, offscreenBuffer, OBWidth, OBHeight)) + return false; + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedTwoConsumers", 0u)) + { + return false; + } + + testFramework.removeDataLink(m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + expectedImageName = "OffscreenBufferLinkTest_Unlinked"; + break; + } + case OffscreenBufferLinkTest_ProviderBufferDestroyed: + { + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) + { + return false; + } + + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, testFramework.getDisplayFramebufferId(0)); + testFramework.destroyOffscreenBuffer(0, offscreenBuffer); + + expectedImageName = "OffscreenBufferLinkTest_BufferDestroyed"; + break; + } + case OffscreenBufferLinkTest_SourceSceneHidden: + { + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) + { + return false; + } + + testFramework.getSceneToState(m_sceneIdProvider, ramses::RendererSceneState::Ready); + + expectedImageName = "OffscreenBufferLinkTest_Black"; + break; + } + case OffscreenBufferLinkTest_SourceSceneAssignedToFBWhileShown: + { + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) + { + return false; + } + + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, testFramework.getDisplayFramebufferId(0)); + + expectedImageName = "OffscreenBufferLinkTest_BlackOB"; + break; + } + case OffscreenBufferLinkTest_OneOfTwoSourceScenesUnmapped: + { + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdProvider2 = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER_AND_PROVIDER_LARGE, m_cameraLow, OBWidth, OBHeight); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider2, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_LinkedTwoScenes", 0u)) + { + return false; + } + + testFramework.getSceneToState(m_sceneIdProvider2, ramses::RendererSceneState::Available); + + // Scene unmapped -> buffer is still there, but is not cleared any more + expectedImageName = "OffscreenBufferLinkTest_Linked"; + break; + } + case OffscreenBufferLinkTest_ProviderSceneUsesDepthTest: + { + m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::DEPTH_FUNC, glm::vec3(0.2f, 0.5f, -5.f), OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, glm::vec3(0.f, 0.f, -3.f)); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers, 0u, ramses::EDepthBufferType::Depth); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + expectedImageName = "OffscreenBufferLinkTest_DepthTest"; + break; + } + case OffscreenBufferLinkTest_ProviderSceneUsesStencilTest: + { + m_sceneIdProvider = createAndShowScene(testFramework, MultipleTrianglesScene::STENCIL_TEST_1, glm::vec3(0.f, 0.5f, -5.f), OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, glm::vec3(0.f, 0.f, -4.f)); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers, 0u, ramses::EDepthBufferType::DepthStencil); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + expectedImageName = "OffscreenBufferLinkTest_StencilTest"; + break; + } + case OffscreenBufferLinkTest_SetClearColor: + { + m_sceneIdProvider = createAndShowScene(testFramework, TextureLinkScene::DATA_PROVIDER_LARGE, m_cameraMid, OBWidth, OBHeight); + m_sceneIdConsumer = createAndShowScene(testFramework, TextureLinkScene::DATA_CONSUMER, m_cameraMid); + + const ramses::displayBufferId_t offscreenBuffer = testFramework.createOffscreenBuffer(0, OBWidth, OBHeight, m_interruptibleBuffers); + testFramework.assignSceneToDisplayBuffer(m_sceneIdProvider, offscreenBuffer); + testFramework.createBufferDataLink(offscreenBuffer, m_sceneIdConsumer, TextureLinkScene::DataConsumerId); + + if (!renderAndCompareScreenshot(testFramework, "OffscreenBufferLinkTest_Linked", 0u)) + return false; + + testFramework.setClearColor(0u, ramses::displayBufferId_t::Invalid(), { 0, 1, 0, 1 }); + testFramework.setClearColor(0u, offscreenBuffer, { 0, 0, 1, 1 }); + + expectedImageName = "OffscreenBufferLinkTest_LinkedCustomClearColor"; + break; + } + default: + assert(false && "undefined test case"); + } + + return renderAndCompareScreenshot(testFramework, expectedImageName, 0u, expectedPixelError); + } + + template + ramses::sceneId_t OffscreenBufferLinkTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth, uint32_t vpHeight) + { + const auto sceneId = testFramework.getScenesRegistry().createScene(sceneState, camPos, vpWidth, vpHeight); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + + return sceneId; + } + + bool OffscreenBufferLinkTests::renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx, float expectedPixelError) + { + // Any changes to interruptible offscreen buffers affect the framebuffer (with consumer) only the next frame due to being executed always as last in each frame. + // Adding simply another doOneLoop here might not always work on wayland platforms, where a frame is not guaranteed to be rendered (see DisplayController::canRenderNewFrame). + // There is no way to find out if a frame was rendered and therefore the solution here is to execute reading of pixels twice before checking result + // (argument flag passed in call below), the read pixels command guarantees that a frame was rendered. + // Generally it still cannot be guaranteed if interruptible OB is really finished (not interrupted) but we assume no interruptions due to no rendering time limits used. + + return testFramework.renderAndCompareScreenshot(expectedImageName, testDisplayIdx, expectedPixelError, m_interruptibleBuffers); + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.h b/tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.h new file mode 100644 index 000000000..5320994dd --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/OffscreenBufferLinkTests.h @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" +#include "ramses/framework/RamsesFrameworkTypes.h" + +#include + +namespace ramses::internal +{ + class OffscreenBufferLinkTests : public IRendererTest + { + public: + explicit OffscreenBufferLinkTests(bool useInterruptibleBuffers); + + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + template + ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState, const glm::vec3& camPos, uint32_t vpWidth = ramses::internal::IntegrationScene::DefaultViewportWidth, uint32_t vpHeight = ramses::internal::IntegrationScene::DefaultViewportHeight); + bool renderAndCompareScreenshot(RendererTestsFramework& testFramework, const std::string& expectedImageName, uint32_t testDisplayIdx = 0u, float expectedPixelError = RendererTestUtils::DefaultMaxAveragePercentPerPixel); + + enum + { + OffscreenBufferLinkTest_ConsumerLinkedToEmptyBuffer, + OffscreenBufferLinkTest_ConsumersLinkedToBufferWithOneScene, + OffscreenBufferLinkTest_OneOfTwoLinksRemoved, + OffscreenBufferLinkTest_ConsumerLinkedToBufferWithTwoScenes, + OffscreenBufferLinkTest_ConsumerRelinkedToAnotherBuffer, + OffscreenBufferLinkTest_ProviderBufferDestroyed, + OffscreenBufferLinkTest_SourceSceneHidden, + OffscreenBufferLinkTest_SourceSceneAssignedToFBWhileShown, + OffscreenBufferLinkTest_OneOfTwoSourceScenesUnmapped, + OffscreenBufferLinkTest_ProviderSceneUsesDepthTest, + OffscreenBufferLinkTest_ProviderSceneUsesStencilTest, + OffscreenBufferLinkTest_SetClearColor, + OffscreenBufferLinkTest_ConsumerLinkedToMSAABuffer, + OffscreenBufferLinkTest_ConsumerUnlinkedMSAABuffer + }; + + ramses::sceneId_t m_sceneIdProvider; + ramses::sceneId_t m_sceneIdProvider2; + ramses::sceneId_t m_sceneIdConsumer; + ramses::sceneId_t m_sceneIdConsumer2; + + const glm::vec3 m_cameraLow{ -1.f, 2.f, 8.f }; + const glm::vec3 m_cameraHigh{ 1.f, -2.f, 8.f }; + const glm::vec3 m_cameraMid{ 0.f, 0.f, 8.f }; + + const bool m_interruptibleBuffers; + + static constexpr uint32_t OBWidth = 256u; + static constexpr uint32_t OBHeight = 256u; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.cpp b/tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.cpp new file mode 100644 index 000000000..9351a108b --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.cpp @@ -0,0 +1,112 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RenderPassRenderingTests.h" +#include "TestScenes/RenderPassOnceScene.h" +#include "RendererTestUtils.h" + +namespace ramses::internal +{ + void RenderPassRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RenderOnce, *this, "RenderPassTest_RenderOnce"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RetriggerRenderOnce, *this, "RenderPassTest_RetriggerRenderOnce"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RemapSceneWithRenderOnce, *this, "RenderPassTest_RemapSceneWithRenderOnce"); + } + + bool RenderPassRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + switch (testCase.m_id) + { + case RenderPassTest_RenderOnce: + return RunRenderOnceTest(testFramework); + case RenderPassTest_RetriggerRenderOnce: + return RunRetriggerRenderOnceTest(testFramework); + case RenderPassTest_RemapSceneWithRenderOnce: + return RunRemapSceneWithRenderOnceTest(testFramework); + default: + assert(!"Invalid renderer test ID!"); + return false; + } + } + + ramses::sceneId_t RenderPassRenderingTests::CreateAndSetupInitialRenderOnceScene(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(RenderPassOnceScene::INITIAL_RENDER_ONCE); + + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + + return sceneId; + } + + bool RenderPassRenderingTests::RunRenderOnceTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = CreateAndSetupInitialRenderOnceScene(testFramework); + + // take 2 screenshots to make sure final image stays even with render once pass + if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial") || + !testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) + { + return false; + } + + // change clear color but do not retrigger render once pass, expect same result + testFramework.getScenesRegistry().setSceneState(sceneId, RenderPassOnceScene::CHANGE_CLEAR_COLOR); + testFramework.getScenesRegistry().getScene(sceneId).flush(); + + return testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial"); + } + + bool RenderPassRenderingTests::RunRetriggerRenderOnceTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = CreateAndSetupInitialRenderOnceScene(testFramework); + + if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) + { + return false; + } + + // change clear color but do not retrigger render once pass, expect same result + testFramework.getScenesRegistry().setSceneState(sceneId, RenderPassOnceScene::CHANGE_CLEAR_COLOR); + testFramework.getScenesRegistry().getScene(sceneId).flush(); + + if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) + { + return false; + } + + // retrigger render once pass and expect new result + testFramework.getScenesRegistry().setSceneState(sceneId, RenderPassOnceScene::RETRIGGER_PASS); + testFramework.getScenesRegistry().getScene(sceneId).flush(); + + // take 2 screenshots to make sure final image stays even with render once pass + return testFramework.renderAndCompareScreenshot("RenderPassOnce_Retriggered") + && testFramework.renderAndCompareScreenshot("RenderPassOnce_Retriggered"); + } + + bool RenderPassRenderingTests::RunRemapSceneWithRenderOnceTest(RendererTestsFramework& testFramework) + { + const ramses::sceneId_t sceneId = CreateAndSetupInitialRenderOnceScene(testFramework); + + if (!testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial")) + { + return false; + } + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); + if (!testFramework.renderAndCompareScreenshot("OffscreenBufferLinkTest_Black")) + { + return false; + } + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + + return testFramework.renderAndCompareScreenshot("RenderPassOnce_Initial"); + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.h b/tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.h new file mode 100644 index 000000000..fe835175e --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/RenderPassRenderingTests.h @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +namespace ramses::internal +{ + class RenderPassRenderingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + static ramses::sceneId_t CreateAndSetupInitialRenderOnceScene(RendererTestsFramework& testFramework); + + static bool RunRenderOnceTest(RendererTestsFramework& testFramework); + static bool RunRetriggerRenderOnceTest(RendererTestsFramework& testFramework); + static bool RunRemapSceneWithRenderOnceTest(RendererTestsFramework& testFramework); + + enum + { + RenderPassTest_RenderOnce = 0, + RenderPassTest_RetriggerRenderOnce, + RenderPassTest_RemapSceneWithRenderOnce, + }; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.cpp b/tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.cpp new file mode 100644 index 000000000..e32c1ce29 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.cpp @@ -0,0 +1,142 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RenderTargetRenderingTests.h" +#include "TestScenes/RenderTargetScene.h" +#include "TestScenes/RenderBufferScene.h" +#include "TestScenes/MsaaRenderBufferScene.h" +#include "TestScenes/MultipleRenderTargetScene.h" + +namespace ramses::internal +{ + void RenderTargetRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Perspective, *this, "RenderTarget_Perspective"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Orthographic, *this, "RenderTarget_Orthographic"); + + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGBA4, *this, "RenderTarget_Format_RGBA4"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_R8, *this, "RenderTarget_Format_R8"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RG8, *this, "RenderTarget_Format_RG8"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGB8, *this, "RenderTarget_Format_RGB8"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_R16F, *this, "RenderTarget_Format_R16F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_R32F, *this, "RenderTarget_Format_R32F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RG16F, *this, "RenderTarget_Format_RG16F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RG32F, *this, "RenderTarget_Format_RG32F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGB16F, *this, "RenderTarget_Format_RGB16F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGB32F, *this, "RenderTarget_Format_RGB32F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGBA16F, *this, "RenderTarget_Format_RGBA16F"); + testFramework.createTestCaseWithDefaultDisplay(RenderTarget_Format_RGBA32F, *this, "RenderTarget_Format_RGBA32F"); + + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_TwoColorBuffersCleared, *this, "MultipleRenderTarget_TwoColorBuffersCleared"); + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_TwoColorBuffersWritten, *this, "MultipleRenderTarget_TwoColorBuffersWritten"); + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4, *this, "MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4"); + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne, *this, "MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne"); + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ReuseSameColorBufferInTwoRTs, *this, "MultipleRenderTarget_ReuseSameColorBufferInTwoRTs"); + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs, *this, "MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs"); + testFramework.createTestCaseWithDefaultDisplay(MultipleRenderTarget_ReadFromDepth, *this, "MultipleRenderTarget_ReadFromDepth"); + + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferNoDepthOrStencil, *this, "RenderBuffer_OneColorBufferNoDepthOrStencil"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer, *this, "RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer, *this, "RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferWithWriteOnlyDepth16, *this, "RenderBuffer_OneColorBufferWithWriteOnlyDepth16"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_OneColorBufferWithWriteOnlyDepth24, *this, "RenderBuffer_OneColorBufferWithWriteOnlyDepth24"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_ReadWriteDepth16, *this, "RenderBuffer_ReadWriteDepth16"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_ReadWriteDepth24, *this, "RenderBuffer_ReadWriteDepth24"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_ReadWriteDepth32, *this, "RenderBuffer_ReadWriteDepth32"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount1Blit, *this, "RenderBuffer_MsaaSampleCount1Blit"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount2Blit, *this, "RenderBuffer_MsaaSampleCount2Blit"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount4Blit, *this, "RenderBuffer_MsaaSampleCount4Blit"); + testFramework.createTestCaseWithDefaultDisplay(RenderBuffer_MsaaSampleCount4TexelFetch, *this, "RenderBuffer_MsaaSampleCount4TexelFetch"); + } + + bool RenderTargetRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + switch (testCase.m_id) + { + case RenderTarget_Perspective: + return runBasicTest(testFramework, RenderTargetScene::PERSPECTIVE_PROJECTION, "RenderTargetScene_PerspectiveProjection"); + case RenderTarget_Orthographic: + return runBasicTest(testFramework, RenderTargetScene::ORTHOGRAPHIC_PROJECTION, "RenderTargetScene_OrthographicProjection"); + case RenderTarget_Format_RGBA4: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGBA4, "RenderTargetScene_RedGreenBlue", 3.0f); // higher threshold needed due to conversion imprecision from 4 to 8 bit + case RenderTarget_Format_R8: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_R8, "RenderTargetScene_Red"); + case RenderTarget_Format_RG8: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RG8, "RenderTargetScene_RedGreen"); + case RenderTarget_Format_RGB8: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGB8, "RenderTargetScene_RedGreenBlue_NoAlpha"); + case RenderTarget_Format_R16F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_R16F, "RenderTargetScene_Red"); + case RenderTarget_Format_R32F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_R32F, "RenderTargetScene_Red"); + case RenderTarget_Format_RG16F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RG16F, "RenderTargetScene_RedGreen"); + case RenderTarget_Format_RG32F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RG32F, "RenderTargetScene_RedGreen"); + case RenderTarget_Format_RGB16F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGB16F, "RenderTargetScene_RedGreenBlue_NoAlpha"); + case RenderTarget_Format_RGB32F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGB32F, "RenderTargetScene_RedGreenBlue"); + case RenderTarget_Format_RGBA16F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGBA16F, "RenderTargetScene_RedGreenBlue", 0.35f); // higher threshold needed due to floating formats get sampled differently on different platforms + case RenderTarget_Format_RGBA32F: + return runBasicTest(testFramework, RenderTargetScene::RENDERBUFFER_FORMAT_RGBA32F, "RenderTargetScene_RedGreenBlue", 0.35f); // higher threshold needed due to floating formats get sampled differently on different platforms + case MultipleRenderTarget_TwoColorBuffersCleared: + return runBasicTest(testFramework, MultipleRenderTargetScene::CLEAR_MRT, "MultiRenderTarget_ClearTwoColorBuffers"); + case MultipleRenderTarget_TwoColorBuffersWritten: + return runBasicTest(testFramework, MultipleRenderTargetScene::TWO_COLOR_BUFFERS, "MultiRenderTarget_TwoColorBuffers"); + case MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4: + return runBasicTest(testFramework, MultipleRenderTargetScene::TWO_COLOR_BUFFERS_RGBA8_AND_RGBA4, "MultiRenderTarget_TwoColorBuffers", 0.5f); // higher threshold needed due to conversion imprecision from 4 to 8 bit + case MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne: + return runBasicTest(testFramework, MultipleRenderTargetScene::SHADER_WRITES_TWO_COLOR_BUFFERS_RT_HAS_ONE, "MultiRenderTarget_OneColorBufferWritten"); + case MultipleRenderTarget_ReuseSameColorBufferInTwoRTs: + return runBasicTest(testFramework, MultipleRenderTargetScene::COLOR_WRITTEN_BY_TWO_DIFFERENT_RTS, "MultiRenderTarget_ColorBufferWrittenByTwoPasses"); + case MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs: + return runBasicTest(testFramework, MultipleRenderTargetScene::DEPTH_WRITTEN_AND_USED_BY_DIFFERENT_RT, "MultiRenderTarget_DepthBufferUsedByTwoPasses"); + case MultipleRenderTarget_ReadFromDepth: + return runBasicTest(testFramework, MultipleRenderTargetScene::DEPTH_WRITTEN_AND_READ, "MultiRenderTarget_DepthRead"); + case RenderBuffer_OneColorBufferNoDepthOrStencil: + return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL, "RenderBuffer_OneColorBufferNoDepthOrStencil"); + case RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer: + return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER, "RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer"); + case RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer: + return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER, "RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer"); + case RenderBuffer_OneColorBufferWithWriteOnlyDepth16: + return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH16, "RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer"); + case RenderBuffer_OneColorBufferWithWriteOnlyDepth24: + return runBasicTest(testFramework, RenderBufferScene::ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH24, "RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer"); + case RenderBuffer_ReadWriteDepth16: + return runBasicTest(testFramework, RenderBufferScene::READ_WRITE_DEPTH16, "RenderBuffer_ReadWriteDepthBuffer"); + case RenderBuffer_ReadWriteDepth24: + return runBasicTest(testFramework, RenderBufferScene::READ_WRITE_DEPTH24, "RenderBuffer_ReadWriteDepthBuffer"); + case RenderBuffer_ReadWriteDepth32: + return runBasicTest(testFramework, RenderBufferScene::READ_WRITE_DEPTH32, "RenderBuffer_ReadWriteDepthBuffer"); + case RenderBuffer_MsaaSampleCount1Blit: + return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_1_BLIT, "RenderBuffer_MsaaSampleCount2Blit", 0.33f); // seems most GL implementations still use msaa2 even with only 1 sample specified + case RenderBuffer_MsaaSampleCount2Blit: + return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_2_BLIT, "RenderBuffer_MsaaSampleCount2Blit", 0.33f); + case RenderBuffer_MsaaSampleCount4Blit: + return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_4_BLIT, "RenderBuffer_MsaaSampleCount4Blit", 0.33f); + case RenderBuffer_MsaaSampleCount4TexelFetch: + return runBasicTest(testFramework, MsaaRenderBufferScene::SAMPLE_COUNT_4_TEXEL_FETCH, "RenderBuffer_MsaaSampleCount4TexelFetch"); + + default: + assert(!"Invalid renderer test ID!"); + return false; + } + } + + template + bool RenderTargetRenderingTests::runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + return testFramework.renderAndCompareScreenshot(expectedImageName, 0u, maxAveragePercentErrorPerPixel); + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.h b/tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.h new file mode 100644 index 000000000..285cdeed9 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/RenderTargetRenderingTests.h @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +#include + +namespace ramses::internal +{ + class RenderTargetRenderingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + template + bool runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel); + + enum + { + RenderTarget_Perspective, + RenderTarget_Orthographic, + + RenderTarget_Format_RGBA4, + RenderTarget_Format_R8, + RenderTarget_Format_RG8, + RenderTarget_Format_RGB8, + RenderTarget_Format_R16F, + RenderTarget_Format_R32F, + RenderTarget_Format_RG16F, + RenderTarget_Format_RG32F, + RenderTarget_Format_RGB16F, + RenderTarget_Format_RGB32F, + RenderTarget_Format_RGBA16F, + RenderTarget_Format_RGBA32F, + + MultipleRenderTarget_TwoColorBuffersCleared, + MultipleRenderTarget_TwoColorBuffersWritten, + MultipleRenderTarget_TwoColorBuffersWrittenRGBA8_RGBA4, + MultipleRenderTarget_ShaderWritesTwoColorBuffers_RTHasOnlyOne, + MultipleRenderTarget_ReuseSameColorBufferInTwoRTs, + MultipleRenderTarget_ReuseSameDepthBufferInTwoRTs, + MultipleRenderTarget_ReadFromDepth, + + RenderBuffer_OneColorBufferNoDepthOrStencil, + RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer, + RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer, + RenderBuffer_OneColorBufferWithWriteOnlyDepth16, + RenderBuffer_OneColorBufferWithWriteOnlyDepth24, + RenderBuffer_ReadWriteDepth16, + RenderBuffer_ReadWriteDepth24, + RenderBuffer_ReadWriteDepth32, + RenderBuffer_MsaaSampleCount1Blit, + RenderBuffer_MsaaSampleCount2Blit, + RenderBuffer_MsaaSampleCount4Blit, + RenderBuffer_MsaaSampleCount4TexelFetch + }; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/RenderingTests.h b/tests/integration/renderer-tests/rendering-tests/RenderingTests.h new file mode 100644 index 000000000..c582e0258 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/RenderingTests.h @@ -0,0 +1,74 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "RendererTestsFramework.h" +#include "SceneRenderingTests.h" +#include "RenderTargetRenderingTests.h" +#include "RenderPassRenderingTests.h" +#include "EffectRenderingTests.h" +#include "TextureRenderingTests.h" +#include "DisplayRenderingTests.h" +#include "DataLinkingTests.h" +#include "OffscreenBufferLinkTests.h" +#include "internal/Core/Utils/LogMacros.h" +#include "InterruptibleOffscreenBufferLinkTests.h" + +#include +#include + +namespace ramses::internal +{ + class RenderingTests + { + public: + RenderingTests(const std::vector& filterIn, const std::vector& filterOut, bool generateScreenshots, const ramses::RamsesFrameworkConfig& config) + : m_testFramework(generateScreenshots, config) + , m_offscreenBufferLinkTests(false) + , m_offscreenBufferLinkTestsUsingInterruptible(true) + { + m_sceneRenderingTests.setUpTestCases(m_testFramework); + m_renderTargetRenderingTests.setUpTestCases(m_testFramework); + m_renderPassRenderingTests.setUpTestCases(m_testFramework); + m_effectRendererTests.setUpTestCases(m_testFramework); + m_textureRenderingTests.setUpTestCases(m_testFramework); + m_displayRenderingTests.setUpTestCases(m_testFramework); + m_dataLinkingTests.setUpTestCases(m_testFramework); + m_offscreenBufferLinkTests.setUpTestCases(m_testFramework); + m_offscreenBufferLinkTestsUsingInterruptible.setUpTestCases(m_testFramework); + m_interruptibleOffscreenBufferLinkTests.setUpTestCases(m_testFramework); + + m_testFramework.filterTestCases(filterIn, filterOut); + } + + bool runTests() + { + return m_testFramework.runAllTests(); + } + + void logReport() + { + fmt::print("{}\n", m_testFramework.generateReport()); + } + + protected: + RendererTestsFramework m_testFramework; + + SceneRenderingTests m_sceneRenderingTests; + RenderTargetRenderingTests m_renderTargetRenderingTests; + RenderPassRenderingTests m_renderPassRenderingTests; + EffectRenderingTests m_effectRendererTests; + TextureRenderingTests m_textureRenderingTests; + DisplayRenderingTests m_displayRenderingTests; + DataLinkingTests m_dataLinkingTests; + OffscreenBufferLinkTests m_offscreenBufferLinkTests; + OffscreenBufferLinkTests m_offscreenBufferLinkTestsUsingInterruptible; + InterruptibleOffscreenBufferLinkTests m_interruptibleOffscreenBufferLinkTests; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.cpp b/tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.cpp new file mode 100644 index 000000000..478957d18 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.cpp @@ -0,0 +1,418 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "SceneRenderingTests.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "impl/RamsesRendererImpl.h" +#include "TestScenes/MultipleTrianglesScene.h" +#include "TestScenes/SingleAppearanceScene.h" +#include "TestScenes/HierarchicalRedTrianglesScene.h" +#include "TestScenes/MultipleGeometryScene.h" +#include "TestScenes/IndexArray32BitScene.h" +#include "TestScenes/RenderPassScene.h" +#include "TestScenes/BlitPassScene.h" +#include "TestScenes/RenderPassClearScene.h" +#include "TestScenes/TextScene.h" +#include "TestScenes/MultiLanguageTextScene.h" +#include "TestScenes/AntiAliasingScene.h" +#include "TestScenes/ArrayInputScene.h" +#include "TestScenes/GeometryInstanceScene.h" +#include "TestScenes/RenderTargetScene.h" +#include "TestScenes/ArrayBufferScene.h" +#include "TestScenes/GeometryShaderScene.h" +#include "TestScenes/ArrayResourceScene.h" + +namespace ramses::internal +{ + void SceneRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_Culling, *this, "RenderStateTest_Culling"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_ColorMask, *this, "RenderStateTest_ColorMask"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_DepthFunc, *this, "RenderStateTest_DepthFunc"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_DrawMode, *this, "RenderStateTest_DrawMode"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_StencilTest1, *this, "RenderStateTest_StencilTest1"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_StencilTest2, *this, "RenderStateTest_StencilTest2"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_StencilTest3, *this, "RenderStateTest_StencilTest3"); + testFramework.createTestCaseWithDefaultDisplay(RenderStateTest_ScissorTest, *this, "RenderStateTest_ScissorTest"); + + testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_RedTriangles, *this, "AppearanceTest_RedTriangles"); + testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_GreenTriangles, *this, "AppearanceTest_GreenTriangles"); + testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_ChangeAppearance, *this, "AppearanceTest_ChangeAppearance"); + testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_TrianglesWithSharedColor, *this, "AppearanceTest_TrianglesWithSharedColor"); + testFramework.createTestCaseWithDefaultDisplay(AppearanceTest_TrianglesWithUnsharedColor, *this, "AppearanceTest_TrianglesWithUnsharedColor"); + + testFramework.createTestCaseWithDefaultDisplay(BlendingTest_BlendingDisabled, *this, "BlendingTest_BlendingDisabled"); + testFramework.createTestCaseWithDefaultDisplay(BlendingTest_AlphaBlending, *this, "BlendingTest_AlphaBlending"); + testFramework.createTestCaseWithDefaultDisplay(BlendingTest_SubtractiveBlending, *this, "BlendingTest_SubtractiveBlending"); + testFramework.createTestCaseWithDefaultDisplay(BlendingTest_AdditiveBlending, *this, "BlendingTest_AdditiveBlending"); + testFramework.createTestCaseWithDefaultDisplay(BlendingTest_BlendingConstant, *this, "BlendingTest_BlendingConstant"); + testFramework.createTestCaseWithDefaultDisplay(BlendingTest_BlendingDstColorAndAlpha, *this, "BlendingTest_BlendingDstColorAndAlpha"); + + testFramework.createTestCaseWithDefaultDisplay(CameraTest_Perspective, *this, "CameraTest_Perspective"); + testFramework.createTestCaseWithDefaultDisplay(CameraTest_Orthographic, *this, "CameraTest_Orthographic"); + testFramework.createTestCaseWithDefaultDisplay(CameraTest_Viewport, *this, "CameraTest_Viewport"); + + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_DeleteMeshNode, *this, "SceneModificationTest_DeleteMeshNode"); + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_Invisible, *this, "SceneModificationTest_NoVisibility"); + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_VisibilityOff, *this, "SceneModificationTest_VisibilityOff"); + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_PartialVisibility, *this, "SceneModificationTest_PartialVisibility"); + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_RotateAndScale, *this, "SceneModificationTest_RotateAndScale"); + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_CameraTransformation, *this, "SceneModificationTest_CameraTransformation"); + testFramework.createTestCaseWithDefaultDisplay(SceneModificationTest_MeshRenderOrder, *this, "SceneModificationTest_MeshRenderOrder"); + + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_SharedAppearance, *this, "GeometryTest_SharedAppearance"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_TriangleListWithoutIndexArray, *this, "GeometryTest_TriangleListWithoutIndexArray"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_TriangleStripWithoutIndexArray, *this, "GeometryTest_TriangleStripWithoutIndexArray"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_32bitIndices, *this, "GeometryTest_32bitIndices"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_32bitIndicesWithOffset, *this, "GeometryTest_32bitIndicesWithOffset"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_16bitIndices, *this, "GeometryTest_16bitIndices"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_16bitIndicesWithOffset, *this, "GeometryTest_16bitIndicesWithOffset"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingWithUniform, *this, "GeometryTest_InstancingWithUniform"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingWithVertexArray, *this, "GeometryTest_InstancingWithVertexArray"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingAndNotInstancing, *this, "GeometryTest_InstancingAndNotInstancing"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_InstancingWithZeroInstanceCount, *this, "GeometryTest_InstancingWithZeroInstanceCount"); + testFramework.createTestCaseWithDefaultDisplay(GeometryTest_VertexArraysWithOffset, *this, "GeometryTest_VertexArraysWithOffset"); + + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_MeshesNotInPassNotRendered, *this, "RenderPassTest_MeshesNotInPassNotRendered"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_DifferentCameras, *this, "RenderPassTest_DifferentCameras"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_MeshesInMultiplePasses, *this, "RenderPassTest_MeshesInMultiplePasses"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassTest_RenderOrder, *this, "RenderPassTest_RenderOrder"); + + testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsColorBuffer, *this, "BlitPassTest_BlitsColorBuffer"); + testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsSubregion, *this, "BlitPassTest_BlitsSubregion"); + testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsDepthBuffer, *this, "BlitPassTest_BlitsDepthBuffer"); + testFramework.createTestCaseWithDefaultDisplay(BlitPassTest_BlitsDepthStencilBuffer, *this, "BlitPassTest_BlitsDepthStencilBuffer"); + + testFramework.createTestCaseWithDefaultDisplay(RenderGroupTest_RenderOrder, *this, "RenderGroupTest_RenderOrder"); + testFramework.createTestCaseWithDefaultDisplay(RenderGroupTest_RenderOrderWithNestedGroups, *this, "RenderGroupTest_RenderOrderWithNestedGroups"); + +#if defined(RAMSES_TEXT_ENABLED) + testFramework.createTestCaseWithDefaultDisplay(TextTest_SimpleText, *this, "TextTest_SimpleText"); + testFramework.createTestCaseWithDefaultDisplay(TextTest_DeletedTextsAndNode, *this, "TextTest_DeletedTextsAndNode"); + testFramework.createTestCaseWithDefaultDisplay(TextTest_DifferentLanguages, *this, "TextTest_DifferentLanguages"); + testFramework.createTestCaseWithDefaultDisplay(TextTest_ForceAutoHinting, *this, "TextTest_ForceAutoHinting"); + testFramework.createTestCaseWithDefaultDisplay(TextTest_FontCascade, *this, "TextTest_FontCascade"); + testFramework.createTestCaseWithDefaultDisplay(TextTest_FontCascadeWithVerticalOffset, *this, "TextTest_FontCascadeWithVerticalOffset"); + testFramework.createTestCaseWithDefaultDisplay(TextTest_Shaping, *this, "TextTest_Shaping"); +#endif + + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_None, *this, "RenderPassClear_None"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_Color, *this, "RenderPassClear_Color"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_Depth, *this, "RenderPassClear_Depth"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_Stencil, *this, "RenderPassClear_Stencil"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_ColorStencil, *this, "RenderPassClear_ColorStencil"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_ColorDepth, *this, "RenderPassClear_ColorDepth"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_StencilDepth, *this, "RenderPassClear_StencilDepth"); + testFramework.createTestCaseWithDefaultDisplay(RenderPassClear_ColorStencilDepth, *this, "RenderPassClear_ColorStencilDepth"); + + testFramework.createTestCaseWithDefaultDisplay(ArrayInputTest_ArrayInputVec4, *this, "ArrayInputTest_ArrayInputVec4"); + testFramework.createTestCaseWithDefaultDisplay(ArrayInputTest_ArrayInputInt32, *this, "ArrayInputTest_ArrayInputInt32"); + testFramework.createTestCaseWithDefaultDisplay(ArrayInputTest_ArrayInputInt32DynamicIndex, *this, "ArrayInputTest_ArrayInputInt32DynamicIndex"); + + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_IndexDataBufferUInt16, *this, "DataBuffer_IndexDataBufferUInt16"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_IndexDataBufferUInt32, *this, "DataBuffer_IndexDataBufferUInt32"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferFloat, *this, "DataBuffer_VertexDataBufferFloat"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferVector2f, *this, "DataBuffer_VertexDataBufferVector2f"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferVector3f, *this, "DataBuffer_VertexDataBufferVector3f"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferVector4f, *this, "DataBuffer_VertexDataBufferVector4f"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_IndexDataBufferGetsUpdated, *this, "DataBuffer_IndexDataBufferGetsUpdated"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_VertexDataBufferGetsUpdated, *this, "DataBuffer_VertexDataBufferGetsUpdated"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_SwitchFromClientArrayResourceToDataBuffer, *this, "DataBuffer_SwitchFromClientArrayResourceToDataBuffer"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_SwitchFromDataBufferToClientArrayResource, *this, "DataBuffer_SwitchFromDataBufferToClientArrayResource"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute, *this, "DataBuffer_InterleavedVertexAttribute"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_GetsUpdated, *this, "DataBuffer_InterleavedVertexAttribute_GetsUpdated"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_TwoStrides, *this, "DataBuffer_InterleavedVertexAttribute_TwoStrides"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_SingleAttrib, *this, "DataBuffer_InterleavedVertexAttribute_SingleAttrib"); + testFramework.createTestCaseWithDefaultDisplay(DataBuffer_InterleavedVertexAttribute_StartVertexOffset, *this, "DataBuffer_InterleavedVertexAttribute_StartVertexOffset"); + + testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute, *this, "ArrayResource_InterleavedVertexAttribute"); + testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute_TwoStrides, *this, "ArrayResource_InterleavedVertexAttribute_TwoStrides"); + testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute_SingleAttrib, *this, "ArrayResource_InterleavedVertexAttribute_SingleAttrib"); + testFramework.createTestCaseWithDefaultDisplay(ArrayResource_InterleavedVertexAttribute_StartVertexOffset, *this, "ArrayResource_InterleavedVertexAttribute_StartVertexOffset"); + + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_PointsInTriangleStripOut, *this, "GeometryShaderGlslV320_PointsInTriangleStripOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_PointsInLineStripOut, *this, "GeometryShaderGlslV320_PointsInLineStripOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_PointsInPointsOut, *this, "GeometryShaderGlslV320_PointsInPointsOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_TrianglesInTriangleStripOut, *this, "GeometryShaderGlslV320_TrianglesInTriangleStripOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV320_TrianglesInPointsOut, *this, "GeometryShaderGlslV320_TrianglesInPointsOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_PointsInTriangleStripOut, *this, "GeometryShaderGlslV310Extension_PointsInTriangleStripOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_PointsInLineStripOut, *this, "GeometryShaderGlslV310Extension_PointsInLineStripOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_PointsInPointsOut, *this, "GeometryShaderGlslV310Extension_PointsInPointsOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut, *this, "GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut"); + testFramework.createTestCaseWithDefaultDisplay(GeometryShaderGlslV310Extension_TrianglesInPointsOut, *this, "GeometryShaderGlslV310Extension_TrianglesInPointsOut"); + + testFramework.createTestCaseWithDefaultDisplay(EulerRotationConventions, *this, "EulerRotationConventions"); + + testFramework.createTestCaseWithDefaultDisplay(Display_SetClearColor, *this, "Display_SetClearColor").m_displayConfigs.front().setClearColor({0.5f, 0.25f, 0.75f, 1.f}); + + RenderingTestCase& testCase = testFramework.createTestCaseWithDefaultDisplay(AntiAliasingTest_MSAA4, *this, "AntiAliasingTest_MSAA4"); + testCase.m_displayConfigs.front().setMultiSampling(4u); + } + + bool SceneRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + switch (testCase.m_id) + { + case RenderStateTest_Culling: + return runBasicTest(testFramework, MultipleTrianglesScene::FACE_CULLING, "MultipleTrianglesScene_FaceCulling"); + case RenderStateTest_ColorMask: + return runBasicTest(testFramework, MultipleTrianglesScene::COLOR_MASK, "MultipleTrianglesScene_ColorMask"); + case RenderStateTest_DepthFunc: + return runBasicTest(testFramework, MultipleTrianglesScene::DEPTH_FUNC, "MultipleTrianglesScene_DepthFunc"); + case RenderStateTest_DrawMode: + return runBasicTest(testFramework, MultipleTrianglesScene::DRAW_MODE, "MultipleTrianglesScene_DrawMode", 1.1f); + case RenderStateTest_StencilTest1: + return runBasicTest(testFramework, MultipleTrianglesScene::STENCIL_TEST_1, "MultipleTrianglesScene_StencilTest_1"); + case RenderStateTest_StencilTest2: + return runBasicTest(testFramework, MultipleTrianglesScene::STENCIL_TEST_2, "MultipleTrianglesScene_StencilTest_2"); + case RenderStateTest_StencilTest3: + return runBasicTest(testFramework, MultipleTrianglesScene::STENCIL_TEST_3, "MultipleTrianglesScene_StencilTest_3"); + case RenderStateTest_ScissorTest: + return runBasicTest(testFramework, MultipleTrianglesScene::SCISSOR_TEST, "MultipleTrianglesScene_ScissorTest"); + case AppearanceTest_RedTriangles: + return runBasicTest(testFramework, SingleAppearanceScene::RED_TRIANGLES, "SingleAppearanceScene_RedTriangles"); + case AppearanceTest_GreenTriangles: + return runBasicTest(testFramework, SingleAppearanceScene::GREEN_TRIANGLES, "SingleAppearanceScene_GreenTriangles"); + case AppearanceTest_ChangeAppearance: + return runBasicTest(testFramework, SingleAppearanceScene::CHANGE_APPEARANCE, "SingleAppearanceScene_ChangeAppearance"); + case AppearanceTest_TrianglesWithSharedColor: + return runBasicTest(testFramework, MultipleTrianglesScene::THREE_TRIANGLES_WITH_SHARED_COLOR, "MultipleTrianglesScene_ThreeTrianglesRed"); + case AppearanceTest_TrianglesWithUnsharedColor: + return runBasicTest(testFramework, MultipleTrianglesScene::THREE_TRIANGLES_WITH_UNSHARED_COLOR, "MultipleTrianglesScene_ThreeTriangles"); + + case BlendingTest_BlendingDisabled: + return runBasicTest(testFramework, MultipleTrianglesScene::THREE_TRIANGLES, "MultipleTrianglesScene_ThreeTriangles"); + case BlendingTest_AlphaBlending: + return runBasicTest(testFramework, MultipleTrianglesScene::ALPHA_BLENDING, "MultipleTrianglesScene_AlphaBlending"); + case BlendingTest_SubtractiveBlending: + return runBasicTest(testFramework, MultipleTrianglesScene::SUBTRACTIVE_BLENDING, "MultipleTrianglesScene_SubtractiveBlending"); + case BlendingTest_AdditiveBlending: + return runBasicTest(testFramework, MultipleTrianglesScene::ADDITIVE_BLENDING, "MultipleTrianglesScene_AdditiveBlending"); + case BlendingTest_BlendingConstant: + return runBasicTest(testFramework, MultipleTrianglesScene::BLENDING_CONSTANT, "MultipleTrianglesScene_BlendingConstant"); + case BlendingTest_BlendingDstColorAndAlpha: + return runBasicTest(testFramework, MultipleTrianglesScene::BLENDING_DST_COLOR_AND_ALPHA, "MultipleTrianglesScene_BlendingDstColorAndAlpha"); + + case CameraTest_Perspective: + return runBasicTest(testFramework, MultipleTrianglesScene::PERSPECTIVE_CAMERA, "MultipleTrianglesScene_PerspectiveCamera"); + case CameraTest_Orthographic: + return runBasicTest(testFramework, MultipleTrianglesScene::ORTHOGRAPHIC_CAMERA, "MultipleTrianglesScene_OrthographicCamera"); + case CameraTest_Viewport: + return runBasicTest(testFramework, RenderPassScene::PASSES_WITH_LEFT_AND_RIGHT_VIEWPORT, "RenderPassScene_LeftRightViewport"); + + case SceneModificationTest_DeleteMeshNode: + return runBasicTest(testFramework, HierarchicalRedTrianglesScene::DELETE_MESHNODE, "HierarchicalRedTrianglesScene_DeleteMeshNode"); + case SceneModificationTest_Invisible: + return runBasicTest(testFramework, HierarchicalRedTrianglesScene::INVISIBLE, "HierarchicalRedTrianglesScene_AllTrianglesInvisible"); + case SceneModificationTest_VisibilityOff: + return runBasicTest(testFramework, HierarchicalRedTrianglesScene::VISIBILITY_OFF, "HierarchicalRedTrianglesScene_AllTrianglesInvisible"); + case SceneModificationTest_PartialVisibility: + return runBasicTest(testFramework, HierarchicalRedTrianglesScene::PARTIAL_VISIBILITY, "HierarchicalRedTrianglesScene_SomeTrianglesInvisible"); + case SceneModificationTest_RotateAndScale: + return runBasicTest(testFramework, HierarchicalRedTrianglesScene::ROTATE_AND_SCALE, "HierarchicalRedTrianglesScene_RotateAndScale"); + case SceneModificationTest_CameraTransformation: + return runBasicTest(testFramework, MultipleTrianglesScene::CAMERA_TRANSFORMATION, "MultipleTrianglesScene_CameraTransformation"); + case SceneModificationTest_MeshRenderOrder: + return runBasicTest(testFramework, MultipleTrianglesScene::TRIANGLES_REORDERED, "MultipleTrianglesScene_RenderingOrderChanged"); + + case GeometryTest_SharedAppearance: + return runBasicTest(testFramework, MultipleGeometryScene::MULTI_TRIANGLE_LIST_GEOMETRY_WITH_INDEX_ARRAY, "MultipleGeometryScene_MultipleGeometry"); + case GeometryTest_TriangleListWithoutIndexArray: + return runBasicTest(testFramework, MultipleGeometryScene::MULTI_TRIANGLE_LIST_GEOMETRY_WITHOUT_INDEX_ARRAY, "MultipleGeometryScene_MultipleGeometry"); + case GeometryTest_TriangleStripWithoutIndexArray: + return runBasicTest(testFramework, MultipleGeometryScene::MULTI_TRIANGLE_STRIP_GEOMETRY_WITHOUT_INDEX_ARRAY, "MultipleGeometryScene_MultipleGeometry"); + case GeometryTest_32bitIndices: + return runBasicTest(testFramework, IndexArray32BitScene::NO_OFFSET_32BIT_INDICES, "IndexArray32BitScene_NoOffset32BitIndices"); + case GeometryTest_32bitIndicesWithOffset: + return runBasicTest(testFramework, IndexArray32BitScene::OFFSET_32BIT_INDICES, "IndexArray32BitScene_Offset32BitIndices"); + case GeometryTest_16bitIndices: + return runBasicTest(testFramework, IndexArray32BitScene::NO_OFFSET_16BIT_INDICES, "IndexArray32BitScene_NoOffset16BitIndices"); + case GeometryTest_16bitIndicesWithOffset: + return runBasicTest(testFramework, IndexArray32BitScene::OFFSET_16BIT_INDICES, "IndexArray32BitScene_Offset16BitIndices"); + case GeometryTest_InstancingWithUniform: + return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_UNIFORM, "GeometryInstanceScene_Instancing"); + case GeometryTest_InstancingWithVertexArray: + return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_VERTEX, "GeometryInstanceScene_Instancing"); + case GeometryTest_InstancingAndNotInstancing: + return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_AND_NOT_INSTANCE, "GeometryInstanceScene_InstancingAndNotInstancing"); + case GeometryTest_InstancingWithZeroInstanceCount: + return runBasicTest(testFramework, GeometryInstanceScene::GEOMETRY_INSTANCE_ZERO_INSTANCE_COUNT, "HierarchicalRedTrianglesScene_AllTrianglesInvisible"); + case GeometryTest_VertexArraysWithOffset: + return runBasicTest(testFramework, MultipleGeometryScene::VERTEX_ARRAYS_WITH_OFFSET, "MultipleGeometryScene_MultipleGeometry"); + + case RenderPassTest_MeshesNotInPassNotRendered: + return runBasicTest(testFramework, RenderPassScene::MESHES_NOT_IN_PASS, "RenderPassScene_MeshNotInPass"); + case RenderPassTest_DifferentCameras: + return runBasicTest(testFramework, RenderPassScene::ONE_MESH_PER_PASS, "RenderPassScene_OneMeshPerPass"); + case RenderPassTest_MeshesInMultiplePasses: + return runBasicTest(testFramework, RenderPassScene::MESH_IN_MULTIPLE_PASSES, "RenderPassScene_MeshInMultiplePasses"); + case RenderPassTest_RenderOrder: + return runBasicTest(testFramework, RenderPassScene::PASSES_WITH_DIFFERENT_RENDER_ORDER, "RenderPassScene_PassesWithDifferentRenderOrder"); + + case BlitPassTest_BlitsColorBuffer: + return runBasicTest(testFramework, BlitPassScene::BLITS_COLOR_BUFFER, "BlitPassTest_BlitsColorBuffer"); + case BlitPassTest_BlitsSubregion: + return runBasicTest(testFramework, BlitPassScene::BLITS_SUBREGION, "BlitPassTest_BlitsSubregion", 0.25f); + case BlitPassTest_BlitsDepthBuffer: + return runBasicTest(testFramework, BlitPassScene::BLITS_DEPTH_BUFFER, "BlitPassTest_BlitsDepthBuffer", 0.25f); + case BlitPassTest_BlitsDepthStencilBuffer: + return runBasicTest(testFramework, BlitPassScene::BLITS_DEPTH_STENCIL_BUFFER, "BlitPassTest_BlitsDepthStencilBuffer"); + + case RenderGroupTest_RenderOrder: + return runBasicTest(testFramework, RenderPassScene::GROUPS_WITH_DIFFERENT_RENDER_ORDER, "RenderPassScene_PassesWithDifferentRenderOrder"); + case RenderGroupTest_RenderOrderWithNestedGroups: + return runBasicTest(testFramework, RenderPassScene::NESTED_GROUPS, "RenderPassScene_PassesWithDifferentRenderOrder"); + +#if defined(RAMSES_TEXT_ENABLED) + case TextTest_SimpleText: + return runBasicTest(testFramework, TextScene::EState_INITIAL, "TextScene_SimpleText"); + case TextTest_DeletedTextsAndNode: + return runBasicTest(testFramework, TextScene::EState_DELETED_TEXTS, "TextScene_DeletedTextsAndNode"); + case TextTest_ForceAutoHinting: + return runBasicTest(testFramework, TextScene::EState_FORCE_AUTO_HINTING, "TextScene_ForceAutoHinting"); + case TextTest_FontCascade: + return runBasicTest(testFramework, TextScene::EState_FONT_CASCADE, "TextScene_FontCascade"); + case TextTest_FontCascadeWithVerticalOffset: + return runBasicTest(testFramework, TextScene::EState_FONT_CASCADE_WITH_VERTICAL_OFFSET, "TextScene_VerticalOffset"); + case TextTest_Shaping: + return runBasicTest(testFramework, TextScene::EState_SHAPING, "TextScene_Shaping"); + case TextTest_DifferentLanguages: + return runBasicTest(testFramework, MultiLanguageTextScene::EState_INITIAL, "MultiLanguageScene_MultiLanguageText"); +#endif + + case AntiAliasingTest_MSAA4: + return runBasicTest(testFramework, AntiAliasingScene::MSAA_4_STATE, "AntiAliasingScene_MSAAx4", 2.5f); + + case RenderPassClear_None: + return runBasicTest(testFramework, static_cast(EClearFlag::None), "RenderPassClear_None"); + case RenderPassClear_Color: + return runBasicTest(testFramework, static_cast(EClearFlag::Color), "RenderPassClear_Color"); + case RenderPassClear_Depth: + return runBasicTest(testFramework, static_cast(EClearFlag::Depth), "RenderPassClear_Depth"); + case RenderPassClear_Stencil: + return runBasicTest(testFramework, static_cast(EClearFlag::Stencil), "RenderPassClear_Stencil"); + case RenderPassClear_ColorStencil: + return runBasicTest(testFramework, (EClearFlag::Color | EClearFlag::Stencil).value(), "RenderPassClear_ColorStencil"); + case RenderPassClear_ColorDepth: + return runBasicTest(testFramework, (EClearFlag::Color | EClearFlag::Depth).value(), "RenderPassClear_ColorDepth"); + case RenderPassClear_StencilDepth: + return runBasicTest(testFramework, (EClearFlag::Stencil | EClearFlag::Depth).value(), "RenderPassClear_StencilDepth"); + case RenderPassClear_ColorStencilDepth: + return runBasicTest(testFramework, static_cast(EClearFlag::All), "RenderPassClear_ColorStencilDepth"); + + case ArrayInputTest_ArrayInputVec4: + return runBasicTest(testFramework, ArrayInputScene::ARRAY_INPUT_VEC4, "ArrayInputScene_ArrayInputVec4"); + case ArrayInputTest_ArrayInputInt32: + return runBasicTest(testFramework, ArrayInputScene::ARRAY_INPUT_INT32, "ArrayInputScene_ArrayInputInt32"); + case ArrayInputTest_ArrayInputInt32DynamicIndex: + return runBasicTest(testFramework, ArrayInputScene::ARRAY_INPUT_INT32_DYNAMIC_INDEX, "ArrayInputScene_ArrayInputInt32DynamicIndex"); + + case DataBuffer_IndexDataBufferUInt16: + return runBasicTest(testFramework, ArrayBufferScene::INDEX_DATA_BUFFER_UINT16, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_IndexDataBufferUInt32: + return runBasicTest(testFramework, ArrayBufferScene::INDEX_DATA_BUFFER_UINT32, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_VertexDataBufferFloat: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_FLOAT, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_VertexDataBufferVector2f: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR2F, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_VertexDataBufferVector3f: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR3F, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_VertexDataBufferVector4f: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_IndexDataBufferGetsUpdated: + { + const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::INDEX_DATA_BUFFER_UINT32, glm::vec3(2, -1, 18)); + testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::UPDATE_INDEX_DATA_BUFFER); + return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangleInverted", 0u); + } + case DataBuffer_VertexDataBufferGetsUpdated: + { + const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F, glm::vec3(2, -1, 18)); + testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::UPDATE_VERTEX_DATA_BUFFER); + return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangleInverted", 0u); + } + case DataBuffer_SwitchFromClientArrayResourceToDataBuffer: + { + const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_ARRAY_RESOURCE_VECTOR4F, glm::vec3(2, -1, 18)); + testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F); + return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangle", 0u); + } + case DataBuffer_SwitchFromDataBufferToClientArrayResource: + { + const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_DATA_BUFFER_VECTOR4F, glm::vec3(2, -1, 18)); + testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::VERTEX_ARRAY_RESOURCE_VECTOR4F); + return testFramework.renderAndCompareScreenshot("DataBufferScene_EquilateralTriangle", 0u); + } + case DataBuffer_InterleavedVertexAttribute: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_InterleavedVertexAttribute_GetsUpdated: + { + const ramses::sceneId_t sceneId = testFramework.createAndShowScene(ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED, glm::vec3(2, -1, 18)); + testFramework.getScenesRegistry().setSceneState(sceneId, ArrayBufferScene::UPDATE_INTERLEAVED_VERTEX_DATA_BUFFER); + return testFramework.renderAndCompareScreenshot("DataBufferScene_RedTriangleInverted", 0u); + } + case DataBuffer_InterleavedVertexAttribute_TwoStrides: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED_TWO_STRIDES, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case ArrayResource_InterleavedVertexAttribute: + return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case ArrayResource_InterleavedVertexAttribute_TwoStrides: + return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED_TWO_STRIDES, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case ArrayResource_InterleavedVertexAttribute_SingleAttrib: + return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED_SINGLE_ATTRIB, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case ArrayResource_InterleavedVertexAttribute_StartVertexOffset: + return runBasicTest(testFramework, ArrayResourceScene::ARRAY_RESOURCE_INTERLEAVED_START_VERTEX, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_InterleavedVertexAttribute_SingleAttrib: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED_SINGLE_ATTRIB, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case DataBuffer_InterleavedVertexAttribute_StartVertexOffset: + return runBasicTest(testFramework, ArrayBufferScene::VERTEX_DATA_BUFFER_INTERLEAVED_START_VERTEX, "DataBufferScene_RedTriangle", 0.0f, glm::vec3(2, -1, 18)); + case Display_SetClearColor: + return testFramework.renderAndCompareScreenshot("Display_SetClearColor", 0u, 0.4f); + case GeometryShaderGlslV320_PointsInTriangleStripOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL320_POINTS_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_PointsInTriangleStripOut", 0.f); + case GeometryShaderGlslV320_PointsInLineStripOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL320_POINTS_IN_LINE_STRIP_OUT, "GeometryShaderScene_PointsInLineStripOut", 0.f); + case GeometryShaderGlslV320_PointsInPointsOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL320_POINTS_IN_POINTS_OUT, "GeometryShaderScene_PointsInPointsOut", .1f); + case GeometryShaderGlslV320_TrianglesInTriangleStripOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL320_TRIANGLES_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_TrianglesInTriangleStripOut", 0.f); + case GeometryShaderGlslV320_TrianglesInPointsOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL320_TRIANGLES_IN_POINTS_OUT, "GeometryShaderScene_TrianglesInPointsOut", .1f); + + case GeometryShaderGlslV310Extension_PointsInTriangleStripOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL310_POINTS_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_PointsInTriangleStripOut", 0.f); + case GeometryShaderGlslV310Extension_PointsInLineStripOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL310_POINTS_IN_LINE_STRIP_OUT, "GeometryShaderScene_PointsInLineStripOut", 0.f); + case GeometryShaderGlslV310Extension_PointsInPointsOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL310_POINTS_IN_POINTS_OUT, "GeometryShaderScene_PointsInPointsOut", .1f); + case GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL310_TRIANGLES_IN_TRIANGLE_STRIP_OUT, "GeometryShaderScene_TrianglesInTriangleStripOut", 0.f); + case GeometryShaderGlslV310Extension_TrianglesInPointsOut: + return runBasicTest(testFramework, GeometryShaderScene::GLSL310_TRIANGLES_IN_POINTS_OUT, "GeometryShaderScene_TrianglesInPointsOut", .1f); + + case EulerRotationConventions: + return runBasicTest(testFramework, MultipleTrianglesScene::EULER_ROTATION_CONVENTIONS, "MultipleTriangleScene_EulerRotationConventions"); + + default: + assert(!"Invalid renderer test ID!"); + return false; + } + } + + template + bool SceneRenderingTests::runBasicTest( + RendererTestsFramework& testFramework, + uint32_t sceneState, + const std::string& expectedImageName, + float maxAveragePercentErrorPerPixel, + const glm::vec3& cameraTranslation, + bool saveDiffOnError) + { + testFramework.createAndShowScene(sceneState, cameraTranslation); + return testFramework.renderAndCompareScreenshot(expectedImageName, 0u, maxAveragePercentErrorPerPixel, false, saveDiffOnError); + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.h b/tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.h new file mode 100644 index 000000000..05b86eff5 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/SceneRenderingTests.h @@ -0,0 +1,159 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +#include + +namespace ramses::internal +{ + class SceneRenderingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + template + bool runBasicTest( + RendererTestsFramework& testFramework, + uint32_t sceneState, + const std::string& expectedImageName, + float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel, + const glm::vec3& cameraTranslation = glm::vec3(0.0f), + bool saveDiffOnError = true); + + enum + { + RenderStateTest_Culling = 0, + RenderStateTest_ColorMask, + RenderStateTest_DepthFunc, + RenderStateTest_DrawMode, + RenderStateTest_StencilTest1, + RenderStateTest_StencilTest2, + RenderStateTest_StencilTest3, + RenderStateTest_ScissorTest, + + AppearanceTest_RedTriangles, + AppearanceTest_GreenTriangles, + AppearanceTest_ChangeAppearance, + AppearanceTest_TrianglesWithSharedColor, + AppearanceTest_TrianglesWithUnsharedColor, + + BlendingTest_BlendingDisabled, + BlendingTest_AlphaBlending, + BlendingTest_SubtractiveBlending, + BlendingTest_AdditiveBlending, + BlendingTest_BlendingConstant, + BlendingTest_BlendingDstColorAndAlpha, + + CameraTest_Perspective, + CameraTest_Orthographic, + CameraTest_Viewport, + + SceneModificationTest_DeleteMeshNode, + SceneModificationTest_Invisible, + SceneModificationTest_VisibilityOff, + SceneModificationTest_PartialVisibility, + SceneModificationTest_RotateAndScale, + SceneModificationTest_CameraTransformation, + SceneModificationTest_MeshRenderOrder, + + GeometryTest_SharedAppearance, + GeometryTest_32bitIndices, + GeometryTest_32bitIndicesWithOffset, + GeometryTest_16bitIndices, + GeometryTest_16bitIndicesWithOffset, + GeometryTest_InstancingWithUniform, + GeometryTest_InstancingWithVertexArray, + GeometryTest_InstancingAndNotInstancing, + GeometryTest_InstancingWithZeroInstanceCount, + GeometryTest_TriangleListWithoutIndexArray, + GeometryTest_TriangleStripWithoutIndexArray, + GeometryTest_VertexArraysWithOffset, + + RenderPassTest_MeshesNotInPassNotRendered, + RenderPassTest_DifferentCameras, + RenderPassTest_MeshesInMultiplePasses, + RenderPassTest_RenderOrder, + + BlitPassTest_BlitsColorBuffer, + BlitPassTest_BlitsSubregion, + BlitPassTest_BlitsDepthBuffer, + BlitPassTest_BlitsDepthStencilBuffer, + + RenderGroupTest_RenderOrder, + RenderGroupTest_RenderOrderWithNestedGroups, + +#if defined(RAMSES_TEXT_ENABLED) + TextTest_SimpleText, + TextTest_DeletedTextsAndNode, + TextTest_DifferentLanguages, + TextTest_ForceAutoHinting, + TextTest_FontCascade, + TextTest_FontCascadeWithVerticalOffset, + TextTest_Shaping, +#endif + + AnimationTest_AnimatedScene, + + AntiAliasingTest_MSAA4, + + RenderPassClear_None, + RenderPassClear_Color, + RenderPassClear_Depth, + RenderPassClear_Stencil, + RenderPassClear_ColorStencil, + RenderPassClear_ColorDepth, + RenderPassClear_StencilDepth, + RenderPassClear_ColorStencilDepth, + + ArrayInputTest_ArrayInputVec4, + ArrayInputTest_ArrayInputInt32, + ArrayInputTest_ArrayInputInt32DynamicIndex, + + DataBuffer_IndexDataBufferUInt16, + DataBuffer_IndexDataBufferUInt32, + DataBuffer_VertexDataBufferFloat, + DataBuffer_VertexDataBufferVector2f, + DataBuffer_VertexDataBufferVector3f, + DataBuffer_VertexDataBufferVector4f, + DataBuffer_IndexDataBufferGetsUpdated, + DataBuffer_VertexDataBufferGetsUpdated, + DataBuffer_SwitchFromClientArrayResourceToDataBuffer, + DataBuffer_SwitchFromDataBufferToClientArrayResource, + DataBuffer_InterleavedVertexAttribute, + DataBuffer_InterleavedVertexAttribute_GetsUpdated, + DataBuffer_InterleavedVertexAttribute_TwoStrides, + DataBuffer_InterleavedVertexAttribute_SingleAttrib, + DataBuffer_InterleavedVertexAttribute_StartVertexOffset, + + ArrayResource_InterleavedVertexAttribute, + ArrayResource_InterleavedVertexAttribute_TwoStrides, + ArrayResource_InterleavedVertexAttribute_SingleAttrib, + ArrayResource_InterleavedVertexAttribute_StartVertexOffset, + + Display_SetClearColor, + + GeometryShaderGlslV320_PointsInTriangleStripOut, + GeometryShaderGlslV320_PointsInLineStripOut, + GeometryShaderGlslV320_PointsInPointsOut, + GeometryShaderGlslV320_TrianglesInTriangleStripOut, + GeometryShaderGlslV320_TrianglesInPointsOut, + GeometryShaderGlslV310Extension_PointsInTriangleStripOut, + GeometryShaderGlslV310Extension_PointsInLineStripOut, + GeometryShaderGlslV310Extension_PointsInPointsOut, + GeometryShaderGlslV310Extension_TrianglesInTriangleStripOut, + GeometryShaderGlslV310Extension_TrianglesInPointsOut, + + EulerRotationConventions + }; + }; +} diff --git a/tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.cpp b/tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.cpp new file mode 100644 index 000000000..8770063dc --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.cpp @@ -0,0 +1,310 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "TextureRenderingTests.h" + +#include "TestScenes/Texture2DAnisotropicTextureFilteringScene.h" +#include "TestScenes/Texture2DFormatScene.h" +#include "TestScenes/Texture2DSamplingScene.h" +#include "TestScenes/Texture2DGenerateMipMapScene.h" +#include "TestScenes/Texture2DCompressedMipMapScene.h" +#include "TestScenes/TextureAddressScene.h" +#include "TestScenes/Texture3DScene.h" +#include "TestScenes/CubeTextureScene.h" +#include "TestScenes/TextureCubeAnisotropicTextureFilteringScene.h" +#include "TestScenes/TextureBufferScene.h" +#include "TestScenes/TextureSamplerScene.h" + + +namespace ramses::internal +{ + void TextureRenderingTests::setUpTestCases(RendererTestsFramework& testFramework) + { + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_R8, *this, "TextureTest_Texture2D_Format_R8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RG8, *this, "TextureTest_Texture2D_Format_RG8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha, *this, "TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB8, *this, "TextureTest_Texture2D_Format_RGB8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB565, *this, "TextureTest_Texture2D_Format_RGB565"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA8, *this, "TextureTest_Texture2D_Format_RGBA8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA4, *this, "TextureTest_Texture2D_Format_RGBA4"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA5551, *this, "TextureTest_Texture2D_Format_RGBA5551"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_Swizzled_BGR8, *this, "TextureTest_Texture2D_Format_Swizzled_BGR8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_Swizzled_BGRA8, *this, "TextureTest_Texture2D_Format_Swizzled_BGRA8"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_R16F, *this, "TextureTest_Texture2D_Format_R16F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_R32F, *this, "TextureTest_Texture2D_Format_R32F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RG16F, *this, "TextureTest_Texture2D_Format_RG16F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RG32F, *this, "TextureTest_Texture2D_Format_RG32F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB16F, *this, "TextureTest_Texture2D_Format_RGB16F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGB32F, *this, "TextureTest_Texture2D_Format_RGB32F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA16F, *this, "TextureTest_Texture2D_Format_RGBA16F"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_RGBA32F, *this, "TextureTest_Texture2D_Format_RGBA32F"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_SRGB8, *this, "TextureTest_Texture2D_Format_SRGB8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_SRGB8_ALPHA8, *this, "TextureTest_Texture2D_Format_SRGB8_ALPHA8"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ETC2RGB, *this, "TextureTest_Texture2D_Format_ETC2RGB"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ETC2RGBA, *this, "TextureTest_Texture2D_Format_ETC2RGBA"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ASTC_RGBA_4x4, *this, "TextureTest_Texture2D_Format_ASTC_RGBA_4x4"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4, *this, "TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_Nearest, *this, "TextureTest_Texture2D_Sampling_Nearest"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_NearestWithMipMaps, *this, "TextureTest_Texture2D_Sampling_NearestWithMipMaps"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_Bilinear, *this, "TextureTest_Texture2D_Sampling_Bilinear"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_BilinearWithMipMaps, *this, "TextureTest_Texture2D_Sampling_BilinearWithMipMaps"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_Trilinear, *this, "TextureTest_Texture2D_Sampling_Trilinear"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_MinLinearMagNearest, *this, "TextureTest_Texture2D_Sampling_MinLinearMagNearest"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_Sampling_MinNearestMagLinear, *this, "TextureTest_Texture2D_Sampling_MinNearestMagLinear"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_AddressMode, *this, "TextureTest_Texture2D_AddressMode"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_AnisotropicFilter, *this, "TextureTest_Texture2D_AnisotropicFilter"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_GenerateMipMapSingle, *this, "TextureTest_Texture2D_GenerateSingleMipMap"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_GenerateMipMapMultiple, *this, "TextureTest_Texture2D_GenerateMultipleMipMaps"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture2D_CompressedMipMap, *this, "TextureTest_Texture2D_CompressedMipMap"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_Texture3D_RGBA8, *this, "TextureTest_Texture3D_RGBA8"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_CubeMap_RGBA8, *this, "TextureTest_CubeMap_RGBA8"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_CubeMap_BGRA_Swizzled, *this, "TextureTest_CubeMap_BGRA_Swizzled"); + testFramework.createTestCaseWithDefaultDisplay(TextureTest_CubeMap_Float, *this, "TextureTest_CubeMap_Float"); + + testFramework.createTestCaseWithDefaultDisplay(TextureTest_TextureCube_AnisotropicFilter, *this, "TextureTest_TextureCube_AnisotropicFilter"); + + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_RGBA8_OneMip, *this, "TextureBufferTest_RGBA8_OneMip"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_RGBA8_ThreeMips, *this, "TextureBufferTest_RGBA8_ThreeMips"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdate, *this, "TextureBufferTest_PartialUpdate"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdateMipMap, *this, "TextureBufferTest_PartialUpdateMipMap"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdateMipMap_RG8, *this, "TextureBufferTest_PartialUpdateMipMap_RG8"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_PartialUpdateIncremental, *this, "TextureBufferTest_PartialUpdateIncremental"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchSceneTextureToClientTexture, *this, "TextureBufferTest_SwitchSceneTextureToClientTexture"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchClientTextureToSceneTexture, *this, "TextureBufferTest_SwitchClientTextureToSceneTexture"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchClientTextureToSceneTextureAndBack, *this, "TextureBufferTest_SwitchClientTextureToSceneTextureAndBack"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate, *this, "TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate"); + testFramework.createTestCaseWithDefaultDisplay(TextureBufferTest_ReMapScene, *this, "TextureBufferTest_ReMapScene"); + + testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_ClientTexture, *this, "SamplerTest_ChangeData_ClientTexture"); + testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_TextureBufferToClientTexture, *this, "SamplerTest_ChangeData_TextureBufferToClientTexture"); + testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_ClientTextureToTextureBuffer, *this, "SamplerTest_ChangeData_ClientTextureToTextureBuffer"); + testFramework.createTestCaseWithDefaultDisplay(SamplerTest_ChangeData_ClientTextureToRenderBuffer, *this, "SamplerTest_ChangeData_ClientTextureToRenderBuffer"); + } + + bool TextureRenderingTests::run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) + { + switch (testCase.m_id) + { + case TextureTest_Texture2D_Format_R8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_R8, "Texture2DFormatScene_R8"); + case TextureTest_Texture2D_Format_RG8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RG8, "Texture2DFormatScene_RG8"); + case TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha: + return runBasicTest(testFramework, Texture2DFormatScene::EState_Swizzled_Luminance_Alpha, "Texture2DFormatScene_Swizzled_Luminance_Alpha"); + case TextureTest_Texture2D_Format_RGB8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB8, "Texture2DFormatScene_RGB8"); + case TextureTest_Texture2D_Format_RGB565: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB565, "Texture2DFormatScene_RGB565"); + case TextureTest_Texture2D_Format_RGBA8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA8, "Texture2DFormatScene_RGBA8"); + case TextureTest_Texture2D_Format_RGBA4: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA4, "Texture2DFormatScene_RGBA4"); + case TextureTest_Texture2D_Format_RGBA5551: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA5551, "Texture2DFormatScene_RGBA5551"); + + case TextureTest_Texture2D_Format_R16F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_R16F, "Texture2DFormatScene_FloatRed"); + case TextureTest_Texture2D_Format_R32F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_R32F, "Texture2DFormatScene_FloatRed"); + case TextureTest_Texture2D_Format_RG16F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RG16F, "Texture2DFormatScene_FloatRG"); + case TextureTest_Texture2D_Format_RG32F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RG32F, "Texture2DFormatScene_FloatRG"); + case TextureTest_Texture2D_Format_RGB16F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB16F, "Texture2DFormatScene_FloatRGB"); + case TextureTest_Texture2D_Format_RGB32F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGB32F, "Texture2DFormatScene_FloatRGB"); + case TextureTest_Texture2D_Format_RGBA16F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA16F, "Texture2DFormatScene_FloatRGB"); + case TextureTest_Texture2D_Format_RGBA32F: + return runBasicTest(testFramework, Texture2DFormatScene::EState_RGBA32F, "Texture2DFormatScene_FloatRGB"); + case TextureTest_Texture2D_Format_SRGB8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_SRGB8, "Texture2DFormatScene_SRGB8"); + case TextureTest_Texture2D_Format_SRGB8_ALPHA8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_SRGB8_ALPHA8, "Texture2DFormatScene_SRGB8_ALPHA8"); + case TextureTest_Texture2D_Format_ASTC_RGBA_4x4: + return runBasicTest(testFramework, Texture2DFormatScene::EState_ASTC_RGBA_4x4, "Texture2DFormatScene_ASTC_RGBA_4x4"); + case TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4: + return runBasicTest(testFramework, Texture2DFormatScene::EState_ASTC_SRGB_ALPHA_4x4, "Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4"); + + case TextureTest_Texture2D_Format_Swizzled_BGR8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_Swizzled_BGR8, "Texture2DFormatScene_Swizzled_BGR8"); + case TextureTest_Texture2D_Format_Swizzled_BGRA8: + return runBasicTest(testFramework, Texture2DFormatScene::EState_Swizzled_BGRA8, "Texture2DFormatScene_Swizzled_BGRA8"); + case TextureTest_Texture2D_Format_ETC2RGB: + return runBasicTest(testFramework, Texture2DFormatScene::EState_ETC2RGB, "Texture2DFormatScene_ETC2RGB"); + case TextureTest_Texture2D_Format_ETC2RGBA: + return runBasicTest(testFramework, Texture2DFormatScene::EState_ETC2RGBA, "Texture2DFormatScene_ETC2RGBA"); + case TextureTest_Texture2D_Sampling_Nearest: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_Nearest, "Texture2DSamplingScene_Nearest"); + case TextureTest_Texture2D_Sampling_NearestWithMipMaps: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_NearestWithMipMaps, "Texture2DSamplingScene_NearestWithMipMaps"); + case TextureTest_Texture2D_Sampling_Bilinear: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_Bilinear, "Texture2DSamplingScene_Bilinear", 1.0f); + case TextureTest_Texture2D_Sampling_BilinearWithMipMaps: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_BilinearWithMipMaps, "Texture2DSamplingScene_BilinearWithMipMaps"); + case TextureTest_Texture2D_Sampling_Trilinear: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_Trilinear, "Texture2DSamplingScene_Trilinear", 6.0f); + case TextureTest_Texture2D_Sampling_MinLinearMagNearest: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_MinLinearMagNearest, "Texture2DSamplingScene_MinLinearMagNearest", 1.0f); + case TextureTest_Texture2D_Sampling_MinNearestMagLinear: + return runBasicTest(testFramework, Texture2DSamplingScene::EState_MinNearestMagLinear, "Texture2DSamplingScene_MinNearestMagLinear"); + + case TextureTest_Texture2D_AddressMode: + return runBasicTest(testFramework, TextureAddressScene::ADDRESS_MODE_STATE, "TextureAddressScene_AllAddressModes", 0.4f); + case TextureTest_Texture2D_AnisotropicFilter: + return runBasicTest(testFramework, Texture2DAnisotropicTextureFilteringScene::EState_Anisotropic, "Texture2DAnisotropicTextureFilteringScene_Anisotropic"); + case TextureTest_Texture2D_GenerateMipMapSingle: + return runBasicTest(testFramework, Texture2DGenerateMipMapScene::EState_GenerateMipMapSingle, "Texture2DGenerateMipMapScene_GenerateSingleMipMap", 1.0f); + case TextureTest_Texture2D_GenerateMipMapMultiple: + return runBasicTest(testFramework, Texture2DGenerateMipMapScene::EState_GenerateMipMapMultiple, "Texture2DGenerateMipMapScene_GenerateMultipleMipMaps", 1.0f); + case TextureTest_Texture2D_CompressedMipMap: + return runBasicTest(testFramework, Texture2DCompressedMipMapScene::EState_CompressedMipMap, "Texture2DCompressedMipMapScene_CompressedMipMap"); + case TextureTest_Texture3D_RGBA8: + return runBasicTest(testFramework, Texture3DScene::SLICES_4, "Texture3DScene_4Slices"); + case TextureTest_CubeMap_RGBA8: + return runBasicTest(testFramework, CubeTextureScene::EState_RGBA8, "CubeTextureScene_CubeMap"); + case TextureTest_CubeMap_BGRA_Swizzled: + return runBasicTest(testFramework, CubeTextureScene::EState_BGRA_Swizzled, "CubeTextureScene_CubeMapSwizzled"); + case TextureTest_CubeMap_Float: + return runBasicTest(testFramework, CubeTextureScene::EState_Float, "CubeTextureScene_CubeMapFloat", 0.28f); + case TextureTest_TextureCube_AnisotropicFilter: + return runBasicTest(testFramework, TextureCubeAnisotropicTextureFilteringScene::EState_Anisotropic, "TextureCubeAnisotropicTextureFilteringScene_Anisotropic"); + + case TextureBufferTest_RGBA8_OneMip: + return runBasicTest(testFramework, TextureBufferScene::EState_RGBA8_OneMip, "TextureBuffer_RGBA8_OneMip"); + case TextureBufferTest_RGBA8_ThreeMips: + return runBasicTest(testFramework, TextureBufferScene::EState_RGBA8_ThreeMips, "TextureBuffer_RGBA8_ThreeMips"); + case TextureBufferTest_PartialUpdate: + return runBasicTest(testFramework, TextureBufferScene::EState_PartialUpdate, "TextureBuffer_PartialUpdate"); + case TextureBufferTest_PartialUpdateMipMap: + return runBasicTest(testFramework, TextureBufferScene::EState_PartialUpdateMipMap, "TextureBuffer_PartialUpdateMipMap"); + case TextureBufferTest_PartialUpdateMipMap_RG8: + return runBasicTest(testFramework, TextureBufferScene::EState_PartialUpdateMipMap_RG8, "TextureBuffer_PartialUpdateMipMap_RG8"); + case TextureBufferTest_PartialUpdateIncremental: + { + const auto sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_PartialUpdate); + const auto init = testFramework.renderAndCompareScreenshot("TextureBuffer_PartialUpdate", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_PartialUpdate1); + const auto update1 = testFramework.renderAndCompareScreenshot("TextureBuffer_PartialUpdate1", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_PartialUpdate2); + const auto update2 = testFramework.renderAndCompareScreenshot("TextureBuffer_PartialUpdate2", 0u); + return init && update1 && update2; + } + case TextureBufferTest_SwitchSceneTextureToClientTexture: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_RGBA8_OneMip); + const bool sceneTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_ClientTextureResource_RGBA8); + const bool switchToClientTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); + return sceneTextureCorrect && switchToClientTexture; + } + case TextureBufferTest_SwitchClientTextureToSceneTexture: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_ClientTextureResource_RGBA8); + const bool clientTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_RGBA8_OneMip); + const bool switchToSceneTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); + return clientTextureCorrect && switchToSceneTextureCorrect; + } + case TextureBufferTest_SwitchClientTextureToSceneTextureAndBack: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_ClientTextureResource_RGBA8); + const bool clientTextureCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_RGBA8_OneMip); + const bool switchToSceneTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_SwitchBackToClientTexture); + const bool switchBackToClientTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); + return clientTextureCorrect && switchToSceneTexture && switchBackToClientTexture; + } + case TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_RGBA8_OneMip); + const bool textureBufferCorrect = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_ClientTextureResource_RGBA8); + const bool switchToClientTexture = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipGreen", 0u); + testFramework.getScenesRegistry().setSceneState(sceneId, TextureBufferScene::EState_SwitchBackToExistingTextureBufferAndUpdate); + const bool switchBackToTextureBuffer = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMipRed", 0u); + return textureBufferCorrect && switchToClientTexture && switchBackToTextureBuffer; + } + case TextureBufferTest_ReMapScene: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureBufferScene::EState_RGBA8_OneMip); + const bool beforeRemapping = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Available); + // just for confidence (reuse existing black image) + const bool blackAfterUnmap = testFramework.renderAndCompareScreenshot("DistributedScene_UnpublishedScene", 0u); + + testFramework.getSceneToState(sceneId, ramses::RendererSceneState::Rendered); + const bool afterRemapping = testFramework.renderAndCompareScreenshot("TextureBuffer_RGBA8_OneMip", 0u); + + return beforeRemapping && blackAfterUnmap && afterRemapping; + } + case SamplerTest_ChangeData_ClientTexture: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_ClientTexture); + if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) + return false; + testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetClientTexture); + return testFramework.renderAndCompareScreenshot("TextureSamplerScene_Changed"); + } + case SamplerTest_ChangeData_TextureBufferToClientTexture: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_TextureBuffer); + if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) + return false; + testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetClientTexture); + return testFramework.renderAndCompareScreenshot("TextureSamplerScene_Changed"); + } + case SamplerTest_ChangeData_ClientTextureToTextureBuffer: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_ClientTexture); + if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) + return false; + testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetTextureBuffer); + return testFramework.renderAndCompareScreenshot("TextureSamplerScene_Changed"); + } + case SamplerTest_ChangeData_ClientTextureToRenderBuffer: + { + const ramses::sceneId_t sceneId = createAndShowScene(testFramework, TextureSamplerScene::EState_ClientTexture); + if (!testFramework.renderAndCompareScreenshot("TextureSamplerScene_Initial")) + return false; + testFramework.getScenesRegistry().setSceneState(sceneId, TextureSamplerScene::EState_SetRenderBuffer); + return testFramework.renderAndCompareScreenshot("TextureSamplerScene_ChangedBlue"); + } + default: + assert(!"Invalid texture rendering test ID!"); + return false; + } + } + + template + bool TextureRenderingTests::runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel) + { + createAndShowScene(testFramework, sceneState); + return testFramework.renderAndCompareScreenshot(expectedImageName, 0u, maxAveragePercentErrorPerPixel); + } + + template + ramses::sceneId_t TextureRenderingTests::createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState) + { + const ramses::sceneId_t sceneId = testFramework.getScenesRegistry().createScene(sceneState); + testFramework.publishAndFlushScene(sceneId); + testFramework.getSceneToRendered(sceneId); + + return sceneId; + } +} diff --git a/tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.h b/tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.h new file mode 100644 index 000000000..596378630 --- /dev/null +++ b/tests/integration/renderer-tests/rendering-tests/TextureRenderingTests.h @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "IRendererTest.h" + +#include + +namespace ramses::internal +{ + class TextureRenderingTests : public IRendererTest + { + public: + void setUpTestCases(RendererTestsFramework& testFramework) final; + bool run(RendererTestsFramework& testFramework, const RenderingTestCase& testCase) final; + + private: + template + ramses::sceneId_t createAndShowScene(RendererTestsFramework& testFramework, uint32_t sceneState); + + template + bool runBasicTest(RendererTestsFramework& testFramework, uint32_t sceneState, const std::string& expectedImageName, float maxAveragePercentErrorPerPixel = RendererTestUtils::DefaultMaxAveragePercentPerPixel); + + enum + { + TextureTest_Texture2D_Format_R8 = 0, + TextureTest_Texture2D_Format_RG8, + TextureTest_Texture2D_Format_RGB8, + TextureTest_Texture2D_Format_Swizzled_Luminance_Alpha, + TextureTest_Texture2D_Format_RGB565, + TextureTest_Texture2D_Format_RGBA8, + TextureTest_Texture2D_Format_RGBA4, + TextureTest_Texture2D_Format_RGBA5551, + TextureTest_Texture2D_Format_Swizzled_BGR8, + TextureTest_Texture2D_Format_Swizzled_BGRA8, + TextureTest_Texture2D_Format_ETC2RGB, + TextureTest_Texture2D_Format_ETC2RGBA, + TextureTest_Texture2D_Format_R16F, + TextureTest_Texture2D_Format_R32F, + TextureTest_Texture2D_Format_RG16F, + TextureTest_Texture2D_Format_RG32F, + TextureTest_Texture2D_Format_RGB16F, + TextureTest_Texture2D_Format_RGB32F, + TextureTest_Texture2D_Format_RGBA16F, + TextureTest_Texture2D_Format_RGBA32F, + TextureTest_Texture2D_Format_SRGB8, + TextureTest_Texture2D_Format_SRGB8_ALPHA8, + TextureTest_Texture2D_Format_ASTC_RGBA_4x4, + TextureTest_Texture2D_Format_ASTC_SRGB_ALPHA_4x4, + + TextureTest_Texture2D_Sampling_Nearest, + TextureTest_Texture2D_Sampling_NearestWithMipMaps, + TextureTest_Texture2D_Sampling_Bilinear, + TextureTest_Texture2D_Sampling_BilinearWithMipMaps, + TextureTest_Texture2D_Sampling_Trilinear, + TextureTest_Texture2D_Sampling_MinLinearMagNearest, + TextureTest_Texture2D_Sampling_MinNearestMagLinear, + + TextureTest_Texture2D_AddressMode, + TextureTest_Texture2D_AnisotropicFilter, + TextureTest_Texture2D_GenerateMipMapSingle, + TextureTest_Texture2D_GenerateMipMapMultiple, + TextureTest_Texture2D_CompressedMipMap, + + TextureTest_Texture3D_RGBA8, + TextureTest_CubeMap_RGBA8, + TextureTest_CubeMap_BGRA_Swizzled, + TextureTest_CubeMap_Float, + TextureTest_TextureCube_AnisotropicFilter, + + TextureBufferTest_RGBA8_OneMip, + TextureBufferTest_RGBA8_ThreeMips, + TextureBufferTest_PartialUpdate, + TextureBufferTest_PartialUpdateMipMap, + TextureBufferTest_PartialUpdateMipMap_RG8, + TextureBufferTest_PartialUpdateIncremental, + TextureBufferTest_SwitchSceneTextureToClientTexture, + TextureBufferTest_SwitchClientTextureToSceneTexture, + TextureBufferTest_SwitchClientTextureToSceneTextureAndBack, + TextureBufferTest_SwitchClientTextureToSceneTextureAndUpdate, + TextureBufferTest_ReMapScene, + + SamplerTest_ChangeData_ClientTexture, + SamplerTest_ChangeData_TextureBufferToClientTexture, + SamplerTest_ChangeData_ClientTextureToTextureBuffer, + SamplerTest_ChangeData_ClientTextureToRenderBuffer, + }; + }; +} diff --git a/integration/SandwichTests/RendererTests/RenderingTests/main.cpp b/tests/integration/renderer-tests/rendering-tests/main.cpp similarity index 92% rename from integration/SandwichTests/RendererTests/RenderingTests/main.cpp rename to tests/integration/renderer-tests/rendering-tests/main.cpp index 3120bb069..d012c495f 100644 --- a/integration/SandwichTests/RendererTests/RenderingTests/main.cpp +++ b/tests/integration/renderer-tests/rendering-tests/main.cpp @@ -7,12 +7,12 @@ // ------------------------------------------------------------------------- #include "RendererTestUtils.h" -#include "Utils/StringUtils.h" +#include "internal/Core/Utils/StringUtils.h" #include "RenderingTests.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" #include "ramses-cli.h" -using namespace ramses_internal; +using namespace ramses::internal; using namespace ramses; int main(int argc, const char *argv[]) @@ -70,7 +70,7 @@ int main(int argc, const char *argv[]) if (!renderingTestsSuccess) { - printf("Some rendering tests failed! Look above for more detailed info.\n"); + fmt::print("Some rendering tests failed! Look above for more detailed info.\n"); return 1; } } diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_Black.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_Black.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_Black.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_Black.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_HiddenScene.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_HiddenScene.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_HiddenScene.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_HiddenScene.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_ModifiedScenes1.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_ModifiedScenes1.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_ModifiedScenes1.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_ModifiedScenes1.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_ModifiedScenes2.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_ModifiedScenes2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_ModifiedScenes2.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_ModifiedScenes2.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_RenderTarget.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_RenderTarget.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_RenderTarget.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_RenderTarget.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_TwoScenes.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_TwoScenes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_TwoScenes.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_TwoScenes.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_TwoScenesInverseOrdered.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_TwoScenesInverseOrdered.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_TwoScenesInverseOrdered.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_TwoScenesInverseOrdered.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_TwoScenesOrdered.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_TwoScenesOrdered.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_TwoScenesOrdered.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_TwoScenesOrdered.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererDisplays_UnpublishedScene.PNG b/tests/integration/renderer-tests/res/ARendererDisplays_UnpublishedScene.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererDisplays_UnpublishedScene.PNG rename to tests/integration/renderer-tests/res/ARendererDisplays_UnpublishedScene.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererInstance_AfterLoadSave.PNG b/tests/integration/renderer-tests/res/ARendererInstance_AfterLoadSave.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererInstance_AfterLoadSave.PNG rename to tests/integration/renderer-tests/res/ARendererInstance_AfterLoadSave.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererInstance_DynamicResources.PNG b/tests/integration/renderer-tests/res/ARendererInstance_DynamicResources.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererInstance_DynamicResources.PNG rename to tests/integration/renderer-tests/res/ARendererInstance_DynamicResources.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererInstance_SimpleText.PNG b/tests/integration/renderer-tests/res/ARendererInstance_SimpleText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererInstance_SimpleText.PNG rename to tests/integration/renderer-tests/res/ARendererInstance_SimpleText.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererInstance_Three_Triangles.PNG b/tests/integration/renderer-tests/res/ARendererInstance_Three_Triangles.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererInstance_Three_Triangles.PNG rename to tests/integration/renderer-tests/res/ARendererInstance_Three_Triangles.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererInstance_Triangles_reordered.PNG b/tests/integration/renderer-tests/res/ARendererInstance_Triangles_reordered.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererInstance_Triangles_reordered.PNG rename to tests/integration/renderer-tests/res/ARendererInstance_Triangles_reordered.PNG diff --git a/integration/SandwichTests/RendererTests/res/ARendererInstance_YellowText.PNG b/tests/integration/renderer-tests/res/ARendererInstance_YellowText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ARendererInstance_YellowText.PNG rename to tests/integration/renderer-tests/res/ARendererInstance_YellowText.PNG diff --git a/integration/SandwichTests/RendererTests/res/AnimatedTriangleScene_AnimatedScene.PNG b/tests/integration/renderer-tests/res/AnimatedTriangleScene_AnimatedScene.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/AnimatedTriangleScene_AnimatedScene.PNG rename to tests/integration/renderer-tests/res/AnimatedTriangleScene_AnimatedScene.PNG diff --git a/integration/SandwichTests/RendererTests/res/AntiAliasingScene_MSAAx4.PNG b/tests/integration/renderer-tests/res/AntiAliasingScene_MSAAx4.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/AntiAliasingScene_MSAAx4.PNG rename to tests/integration/renderer-tests/res/AntiAliasingScene_MSAAx4.PNG diff --git a/integration/SandwichTests/RendererTests/res/ArrayInputScene_ArrayInputInt32.PNG b/tests/integration/renderer-tests/res/ArrayInputScene_ArrayInputInt32.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ArrayInputScene_ArrayInputInt32.PNG rename to tests/integration/renderer-tests/res/ArrayInputScene_ArrayInputInt32.PNG diff --git a/integration/SandwichTests/RendererTests/res/ArrayInputScene_ArrayInputInt32DynamicIndex.PNG b/tests/integration/renderer-tests/res/ArrayInputScene_ArrayInputInt32DynamicIndex.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ArrayInputScene_ArrayInputInt32DynamicIndex.PNG rename to tests/integration/renderer-tests/res/ArrayInputScene_ArrayInputInt32DynamicIndex.PNG diff --git a/integration/SandwichTests/RendererTests/res/ArrayInputScene_ArrayInputVec4.PNG b/tests/integration/renderer-tests/res/ArrayInputScene_ArrayInputVec4.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ArrayInputScene_ArrayInputVec4.PNG rename to tests/integration/renderer-tests/res/ArrayInputScene_ArrayInputVec4.PNG diff --git a/integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsColorBuffer.PNG b/tests/integration/renderer-tests/res/BlitPassTest_BlitsColorBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsColorBuffer.PNG rename to tests/integration/renderer-tests/res/BlitPassTest_BlitsColorBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsDepthBuffer.PNG b/tests/integration/renderer-tests/res/BlitPassTest_BlitsDepthBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsDepthBuffer.PNG rename to tests/integration/renderer-tests/res/BlitPassTest_BlitsDepthBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsDepthStencilBuffer.PNG b/tests/integration/renderer-tests/res/BlitPassTest_BlitsDepthStencilBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsDepthStencilBuffer.PNG rename to tests/integration/renderer-tests/res/BlitPassTest_BlitsDepthStencilBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsSubregion.PNG b/tests/integration/renderer-tests/res/BlitPassTest_BlitsSubregion.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/BlitPassTest_BlitsSubregion.PNG rename to tests/integration/renderer-tests/res/BlitPassTest_BlitsSubregion.PNG diff --git a/integration/SandwichTests/RendererTests/res/CameraData_FrustumLinked.PNG b/tests/integration/renderer-tests/res/CameraData_FrustumLinked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/CameraData_FrustumLinked.PNG rename to tests/integration/renderer-tests/res/CameraData_FrustumLinked.PNG diff --git a/integration/SandwichTests/RendererTests/res/CameraData_NoLinks.PNG b/tests/integration/renderer-tests/res/CameraData_NoLinks.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/CameraData_NoLinks.PNG rename to tests/integration/renderer-tests/res/CameraData_NoLinks.PNG diff --git a/integration/SandwichTests/RendererTests/res/CameraData_ViewportLinked.PNG b/tests/integration/renderer-tests/res/CameraData_ViewportLinked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/CameraData_ViewportLinked.PNG rename to tests/integration/renderer-tests/res/CameraData_ViewportLinked.PNG diff --git a/integration/SandwichTests/RendererTests/res/CubeTextureScene_CubeMap.PNG b/tests/integration/renderer-tests/res/CubeTextureScene_CubeMap.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/CubeTextureScene_CubeMap.PNG rename to tests/integration/renderer-tests/res/CubeTextureScene_CubeMap.PNG diff --git a/integration/SandwichTests/RendererTests/res/CubeTextureScene_CubeMapFloat.PNG b/tests/integration/renderer-tests/res/CubeTextureScene_CubeMapFloat.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/CubeTextureScene_CubeMapFloat.PNG rename to tests/integration/renderer-tests/res/CubeTextureScene_CubeMapFloat.PNG diff --git a/integration/SandwichTests/RendererTests/res/CubeTextureScene_CubeMapSwizzled.PNG b/tests/integration/renderer-tests/res/CubeTextureScene_CubeMapSwizzled.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/CubeTextureScene_CubeMapSwizzled.PNG rename to tests/integration/renderer-tests/res/CubeTextureScene_CubeMapSwizzled.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataBufferScene_EquilateralTriangle.PNG b/tests/integration/renderer-tests/res/DataBufferScene_EquilateralTriangle.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataBufferScene_EquilateralTriangle.PNG rename to tests/integration/renderer-tests/res/DataBufferScene_EquilateralTriangle.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataBufferScene_RedTriangle.PNG b/tests/integration/renderer-tests/res/DataBufferScene_RedTriangle.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataBufferScene_RedTriangle.PNG rename to tests/integration/renderer-tests/res/DataBufferScene_RedTriangle.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataBufferScene_RedTriangleInverted.PNG b/tests/integration/renderer-tests/res/DataBufferScene_RedTriangleInverted.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataBufferScene_RedTriangleInverted.PNG rename to tests/integration/renderer-tests/res/DataBufferScene_RedTriangleInverted.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_AllLinksDisabled.PNG b/tests/integration/renderer-tests/res/DataLinkTest_AllLinksDisabled.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_AllLinksDisabled.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_AllLinksDisabled.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_ConfidenceMultiLink.PNG b/tests/integration/renderer-tests/res/DataLinkTest_ConfidenceMultiLink.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_ConfidenceMultiLink.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_ConfidenceMultiLink.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_ConsumerLinkedToProvider.PNG b/tests/integration/renderer-tests/res/DataLinkTest_ConsumerLinkedToProvider.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_ConsumerLinkedToProvider.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_ConsumerLinkedToProvider.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_ConsumerLinkedToProviderNested.PNG b/tests/integration/renderer-tests/res/DataLinkTest_ConsumerLinkedToProviderNested.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_ConsumerLinkedToProviderNested.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_ConsumerLinkedToProviderNested.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_LinkOverridesComsumerTransform.PNG b/tests/integration/renderer-tests/res/DataLinkTest_LinkOverridesComsumerTransform.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_LinkOverridesComsumerTransform.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_LinkOverridesComsumerTransform.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_LinkRemoved.PNG b/tests/integration/renderer-tests/res/DataLinkTest_LinkRemoved.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_LinkRemoved.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_LinkRemoved.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_Linked.PNG b/tests/integration/renderer-tests/res/DataLinkTest_Linked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_Linked.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_Linked.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_NoLinks.PNG b/tests/integration/renderer-tests/res/DataLinkTest_NoLinks.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_NoLinks.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_NoLinks.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_RemovedConsumer.PNG b/tests/integration/renderer-tests/res/DataLinkTest_RemovedConsumer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_RemovedConsumer.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_RemovedConsumer.PNG diff --git a/integration/SandwichTests/RendererTests/res/DataLinkTest_RemovedProvider.PNG b/tests/integration/renderer-tests/res/DataLinkTest_RemovedProvider.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DataLinkTest_RemovedProvider.PNG rename to tests/integration/renderer-tests/res/DataLinkTest_RemovedProvider.PNG diff --git a/integration/SandwichTests/RendererTests/res/Display_SetClearColor.PNG b/tests/integration/renderer-tests/res/Display_SetClearColor.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Display_SetClearColor.PNG rename to tests/integration/renderer-tests/res/Display_SetClearColor.PNG diff --git a/integration/SandwichTests/RendererTests/res/DistributedScene_UnpublishedScene.PNG b/tests/integration/renderer-tests/res/DistributedScene_UnpublishedScene.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DistributedScene_UnpublishedScene.PNG rename to tests/integration/renderer-tests/res/DistributedScene_UnpublishedScene.PNG diff --git a/integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferReadWrite.PNG b/tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferReadWrite.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferReadWrite.PNG rename to tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferReadWrite.PNG diff --git a/integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite.PNG b/tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite.PNG rename to tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite.PNG diff --git a/integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite_128x256.PNG b/tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite_128x256.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite_128x256.PNG rename to tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite_128x256.PNG diff --git a/integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite_128x64.PNG b/tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite_128x64.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite_128x64.PNG rename to tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite_128x64.PNG diff --git a/integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite_256x256.PNG b/tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite_256x256.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/DmaOffscreenBufferTest_BufferWrite_256x256.PNG rename to tests/integration/renderer-tests/res/DmaOffscreenBufferTest_BufferWrite_256x256.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_BlueTriangleStreamTexture.PNG b/tests/integration/renderer-tests/res/EC_BlueTriangleStreamTexture.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_BlueTriangleStreamTexture.PNG rename to tests/integration/renderer-tests/res/EC_BlueTriangleStreamTexture.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_BlueTriangleStreamTexture_SmallRes.PNG b/tests/integration/renderer-tests/res/EC_BlueTriangleStreamTexture_SmallRes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_BlueTriangleStreamTexture_SmallRes.PNG rename to tests/integration/renderer-tests/res/EC_BlueTriangleStreamTexture_SmallRes.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_BlueTriangleStreamTexture_WithTexCoordsOffset.PNG b/tests/integration/renderer-tests/res/EC_BlueTriangleStreamTexture_WithTexCoordsOffset.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_BlueTriangleStreamTexture_WithTexCoordsOffset.PNG rename to tests/integration/renderer-tests/res/EC_BlueTriangleStreamTexture_WithTexCoordsOffset.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_CanUseStreamTextureInASceneMappedToOffscreenBuffer.PNG b/tests/integration/renderer-tests/res/EC_CanUseStreamTextureInASceneMappedToOffscreenBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_CanUseStreamTextureInASceneMappedToOffscreenBuffer.PNG rename to tests/integration/renderer-tests/res/EC_CanUseStreamTextureInASceneMappedToOffscreenBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_Fallback1_Right.PNG b/tests/integration/renderer-tests/res/EC_Fallback1_Left_Fallback1_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_Fallback1_Right.PNG rename to tests/integration/renderer-tests/res/EC_Fallback1_Left_Fallback1_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_Fallback1_Swizzled_Right_Swizzled.PNG b/tests/integration/renderer-tests/res/EC_Fallback1_Left_Fallback1_Swizzled_Right_Swizzled.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_Fallback1_Swizzled_Right_Swizzled.PNG rename to tests/integration/renderer-tests/res/EC_Fallback1_Left_Fallback1_Swizzled_Right_Swizzled.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_Fallback2_Right.PNG b/tests/integration/renderer-tests/res/EC_Fallback1_Left_Fallback2_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_Fallback2_Right.PNG rename to tests/integration/renderer-tests/res/EC_Fallback1_Left_Fallback2_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_RedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_Fallback1_Left_RedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_RedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_Fallback1_Left_RedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_WhiteTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_Fallback1_Left_WhiteTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_Fallback1_Left_WhiteTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_Fallback1_Left_WhiteTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_FallbackTexture_1.PNG b/tests/integration/renderer-tests/res/EC_FallbackTexture_1.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_FallbackTexture_1.PNG rename to tests/integration/renderer-tests/res/EC_FallbackTexture_1.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_FallbackTexture_WithTexCoordsOffset.PNG b/tests/integration/renderer-tests/res/EC_FallbackTexture_WithTexCoordsOffset.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_FallbackTexture_WithTexCoordsOffset.PNG rename to tests/integration/renderer-tests/res/EC_FallbackTexture_WithTexCoordsOffset.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_GrayTriangleStreamTexture.PNG b/tests/integration/renderer-tests/res/EC_GrayTriangleStreamTexture.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_GrayTriangleStreamTexture.PNG rename to tests/integration/renderer-tests/res/EC_GrayTriangleStreamTexture.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangleStreamTexture.PNG b/tests/integration/renderer-tests/res/EC_RedTriangleStreamTexture.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangleStreamTexture.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangleStreamTexture.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangleStreamTexture_SmallRes.PNG b/tests/integration/renderer-tests/res/EC_RedTriangleStreamTexture_SmallRes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangleStreamTexture_SmallRes.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangleStreamTexture_SmallRes.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_BlueTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_RedTriangle_Left_BlueTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_BlueTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangle_Left_BlueTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_Fallback1_Right.PNG b/tests/integration/renderer-tests/res/EC_RedTriangle_Left_Fallback1_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_Fallback1_Right.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangle_Left_Fallback1_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_Fallback1_Swizzled_Right.PNG b/tests/integration/renderer-tests/res/EC_RedTriangle_Left_Fallback1_Swizzled_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_Fallback1_Swizzled_Right.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangle_Left_Fallback1_Swizzled_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_RedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_RedTriangle_Left_RedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_RedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangle_Left_RedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_ShMemRedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_RedTriangle_Left_ShMemRedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_RedTriangle_Left_ShMemRedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_RedTriangle_Left_ShMemRedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle.PNG b/tests/integration/renderer-tests/res/EC_ShMemBlueTriangle.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle.PNG rename to tests/integration/renderer-tests/res/EC_ShMemBlueTriangle.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_Fallback1_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_Fallback1_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_Fallback1_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_Fallback1_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_Fallback1_Swizzled_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_Fallback1_Swizzled_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_Fallback1_Swizzled_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_Fallback1_Swizzled_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_RedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_RedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_RedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_RedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_ShMemRedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_ShMemRedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_ShMemRedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_ShMemRedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_ShMemWhiteTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_ShMemWhiteTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemBlueTriangle_Left_ShMemWhiteTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemBlueTriangle_Left_ShMemWhiteTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemGreyTriangle_Left_ShMemWhiteTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemGreyTriangle_Left_ShMemWhiteTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemGreyTriangle_Left_ShMemWhiteTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemGreyTriangle_Left_ShMemWhiteTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle.PNG b/tests/integration/renderer-tests/res/EC_ShMemRedTriangle.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle.PNG rename to tests/integration/renderer-tests/res/EC_ShMemRedTriangle.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle_Left_Fallback1_Swizzled_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemRedTriangle_Left_Fallback1_Swizzled_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle_Left_Fallback1_Swizzled_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemRedTriangle_Left_Fallback1_Swizzled_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle_Left_RedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemRedTriangle_Left_RedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle_Left_RedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemRedTriangle_Left_RedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle_Left_ShMemRedTriangle_Right.PNG b/tests/integration/renderer-tests/res/EC_ShMemRedTriangle_Left_ShMemRedTriangle_Right.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemRedTriangle_Left_ShMemRedTriangle_Right.PNG rename to tests/integration/renderer-tests/res/EC_ShMemRedTriangle_Left_ShMemRedTriangle_Right.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_ShMemWhiteTriangle.PNG b/tests/integration/renderer-tests/res/EC_ShMemWhiteTriangle.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_ShMemWhiteTriangle.PNG rename to tests/integration/renderer-tests/res/EC_ShMemWhiteTriangle.PNG diff --git a/integration/SandwichTests/RendererTests/res/EC_WhiteTriangleStreamTexture.PNG b/tests/integration/renderer-tests/res/EC_WhiteTriangleStreamTexture.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/EC_WhiteTriangleStreamTexture.PNG rename to tests/integration/renderer-tests/res/EC_WhiteTriangleStreamTexture.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryInstanceScene_Instancing.PNG b/tests/integration/renderer-tests/res/GeometryInstanceScene_Instancing.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryInstanceScene_Instancing.PNG rename to tests/integration/renderer-tests/res/GeometryInstanceScene_Instancing.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryInstanceScene_InstancingAndNotInstancing.PNG b/tests/integration/renderer-tests/res/GeometryInstanceScene_InstancingAndNotInstancing.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryInstanceScene_InstancingAndNotInstancing.PNG rename to tests/integration/renderer-tests/res/GeometryInstanceScene_InstancingAndNotInstancing.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryShaderScene_PointsInLineStripOut.PNG b/tests/integration/renderer-tests/res/GeometryShaderScene_PointsInLineStripOut.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryShaderScene_PointsInLineStripOut.PNG rename to tests/integration/renderer-tests/res/GeometryShaderScene_PointsInLineStripOut.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryShaderScene_PointsInPointsOut.PNG b/tests/integration/renderer-tests/res/GeometryShaderScene_PointsInPointsOut.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryShaderScene_PointsInPointsOut.PNG rename to tests/integration/renderer-tests/res/GeometryShaderScene_PointsInPointsOut.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryShaderScene_PointsInTriangleStripOut.PNG b/tests/integration/renderer-tests/res/GeometryShaderScene_PointsInTriangleStripOut.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryShaderScene_PointsInTriangleStripOut.PNG rename to tests/integration/renderer-tests/res/GeometryShaderScene_PointsInTriangleStripOut.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryShaderScene_TrianglesInPointsOut.PNG b/tests/integration/renderer-tests/res/GeometryShaderScene_TrianglesInPointsOut.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryShaderScene_TrianglesInPointsOut.PNG rename to tests/integration/renderer-tests/res/GeometryShaderScene_TrianglesInPointsOut.PNG diff --git a/integration/SandwichTests/RendererTests/res/GeometryShaderScene_TrianglesInTriangleStripOut.PNG b/tests/integration/renderer-tests/res/GeometryShaderScene_TrianglesInTriangleStripOut.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/GeometryShaderScene_TrianglesInTriangleStripOut.PNG rename to tests/integration/renderer-tests/res/GeometryShaderScene_TrianglesInTriangleStripOut.PNG diff --git a/integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_AllTrianglesInvisible.PNG b/tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_AllTrianglesInvisible.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_AllTrianglesInvisible.PNG rename to tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_AllTrianglesInvisible.PNG diff --git a/integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_DeleteMeshNode.PNG b/tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_DeleteMeshNode.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_DeleteMeshNode.PNG rename to tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_DeleteMeshNode.PNG diff --git a/integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_RotateAndScale.PNG b/tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_RotateAndScale.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_RotateAndScale.PNG rename to tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_RotateAndScale.PNG diff --git a/integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_SomeTrianglesInvisible.PNG b/tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_SomeTrianglesInvisible.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/HierarchicalRedTrianglesScene_SomeTrianglesInvisible.PNG rename to tests/integration/renderer-tests/res/HierarchicalRedTrianglesScene_SomeTrianglesInvisible.PNG diff --git a/integration/SandwichTests/RendererTests/res/IndexArray32BitScene_NoOffset16BitIndices.PNG b/tests/integration/renderer-tests/res/IndexArray32BitScene_NoOffset16BitIndices.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/IndexArray32BitScene_NoOffset16BitIndices.PNG rename to tests/integration/renderer-tests/res/IndexArray32BitScene_NoOffset16BitIndices.PNG diff --git a/integration/SandwichTests/RendererTests/res/IndexArray32BitScene_NoOffset32BitIndices.PNG b/tests/integration/renderer-tests/res/IndexArray32BitScene_NoOffset32BitIndices.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/IndexArray32BitScene_NoOffset32BitIndices.PNG rename to tests/integration/renderer-tests/res/IndexArray32BitScene_NoOffset32BitIndices.PNG diff --git a/integration/SandwichTests/RendererTests/res/IndexArray32BitScene_Offset16BitIndices.PNG b/tests/integration/renderer-tests/res/IndexArray32BitScene_Offset16BitIndices.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/IndexArray32BitScene_Offset16BitIndices.PNG rename to tests/integration/renderer-tests/res/IndexArray32BitScene_Offset16BitIndices.PNG diff --git a/integration/SandwichTests/RendererTests/res/IndexArray32BitScene_Offset32BitIndices.PNG b/tests/integration/renderer-tests/res/IndexArray32BitScene_Offset32BitIndices.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/IndexArray32BitScene_Offset32BitIndices.PNG rename to tests/integration/renderer-tests/res/IndexArray32BitScene_Offset32BitIndices.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState0.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState0.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState0.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState0.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState1.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState1.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState1.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState1.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState2.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState2.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState2.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState3Linked.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState3Linked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_FBState3Linked.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_FBState3Linked.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_OneOBWithTwoScenes.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles2.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles2.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles2.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles3.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles3.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles3.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles3.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_ThreeTriangles_SameOB.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered2.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered2.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered2.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TrianglesReordered_SameOB.PNG diff --git a/integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene.PNG b/tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene.PNG rename to tests/integration/renderer-tests/res/InterruptibleOffscreenBufferLinkTest_TwoOBsEachWithOneScene.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiLanguageScene_MultiLanguageText.PNG b/tests/integration/renderer-tests/res/MultiLanguageScene_MultiLanguageText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiLanguageScene_MultiLanguageText.PNG rename to tests/integration/renderer-tests/res/MultiLanguageScene_MultiLanguageText.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiRenderTarget_ClearTwoColorBuffers.PNG b/tests/integration/renderer-tests/res/MultiRenderTarget_ClearTwoColorBuffers.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiRenderTarget_ClearTwoColorBuffers.PNG rename to tests/integration/renderer-tests/res/MultiRenderTarget_ClearTwoColorBuffers.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiRenderTarget_ColorBufferWrittenByTwoPasses.PNG b/tests/integration/renderer-tests/res/MultiRenderTarget_ColorBufferWrittenByTwoPasses.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiRenderTarget_ColorBufferWrittenByTwoPasses.PNG rename to tests/integration/renderer-tests/res/MultiRenderTarget_ColorBufferWrittenByTwoPasses.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiRenderTarget_DepthBufferUsedByTwoPasses.PNG b/tests/integration/renderer-tests/res/MultiRenderTarget_DepthBufferUsedByTwoPasses.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiRenderTarget_DepthBufferUsedByTwoPasses.PNG rename to tests/integration/renderer-tests/res/MultiRenderTarget_DepthBufferUsedByTwoPasses.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiRenderTarget_DepthRead.PNG b/tests/integration/renderer-tests/res/MultiRenderTarget_DepthRead.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiRenderTarget_DepthRead.PNG rename to tests/integration/renderer-tests/res/MultiRenderTarget_DepthRead.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiRenderTarget_OneColorBufferWritten.PNG b/tests/integration/renderer-tests/res/MultiRenderTarget_OneColorBufferWritten.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiRenderTarget_OneColorBufferWritten.PNG rename to tests/integration/renderer-tests/res/MultiRenderTarget_OneColorBufferWritten.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiRenderTarget_TwoColorBuffers.PNG b/tests/integration/renderer-tests/res/MultiRenderTarget_TwoColorBuffers.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiRenderTarget_TwoColorBuffers.PNG rename to tests/integration/renderer-tests/res/MultiRenderTarget_TwoColorBuffers.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiTypeLinkTest_Linked.PNG b/tests/integration/renderer-tests/res/MultiTypeLinkTest_Linked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiTypeLinkTest_Linked.PNG rename to tests/integration/renderer-tests/res/MultiTypeLinkTest_Linked.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultiTypeLinkTest_NoLinks.PNG b/tests/integration/renderer-tests/res/MultiTypeLinkTest_NoLinks.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultiTypeLinkTest_NoLinks.PNG rename to tests/integration/renderer-tests/res/MultiTypeLinkTest_NoLinks.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleGeometryScene_MultipleGeometry.PNG b/tests/integration/renderer-tests/res/MultipleGeometryScene_MultipleGeometry.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleGeometryScene_MultipleGeometry.PNG rename to tests/integration/renderer-tests/res/MultipleGeometryScene_MultipleGeometry.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTriangleScene_EulerRotationConventions.PNG b/tests/integration/renderer-tests/res/MultipleTriangleScene_EulerRotationConventions.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTriangleScene_EulerRotationConventions.PNG rename to tests/integration/renderer-tests/res/MultipleTriangleScene_EulerRotationConventions.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_AdditiveBlending.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_AdditiveBlending.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_AdditiveBlending.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_AdditiveBlending.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_AlphaBlending.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_AlphaBlending.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_AlphaBlending.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_AlphaBlending.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_BlendingConstant.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_BlendingConstant.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_BlendingConstant.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_BlendingConstant.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_BlendingDstColorAndAlpha.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_BlendingDstColorAndAlpha.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_BlendingDstColorAndAlpha.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_BlendingDstColorAndAlpha.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_CameraTransformation.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_CameraTransformation.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_CameraTransformation.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_CameraTransformation.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ColorMask.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_ColorMask.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ColorMask.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_ColorMask.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_DepthFunc.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_DepthFunc.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_DepthFunc.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_DepthFunc.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_DepthFunc_NoDepthBuffer.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_DepthFunc_NoDepthBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_DepthFunc_NoDepthBuffer.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_DepthFunc_NoDepthBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_DrawMode.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_DrawMode.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_DrawMode.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_DrawMode.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_FaceCulling.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_FaceCulling.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_FaceCulling.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_FaceCulling.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_OrthographicCamera.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_OrthographicCamera.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_OrthographicCamera.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_OrthographicCamera.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_PerspectiveCamera.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_PerspectiveCamera.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_PerspectiveCamera.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_PerspectiveCamera.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_RenderingOrderChanged.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_RenderingOrderChanged.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_RenderingOrderChanged.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_RenderingOrderChanged.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ScissorTest.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_ScissorTest.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ScissorTest.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_ScissorTest.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_1.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_1.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_1.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_1.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_1_NoDepthOrStencilBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_1_NoStencilBuffer.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_1_NoStencilBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_1_NoStencilBuffer.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_1_NoStencilBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_2.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_2.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_2.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_3.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_3.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_StencilTest_3.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_StencilTest_3.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_Subimages_middle.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_Subimages_middle.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_Subimages_middle.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_Subimages_middle.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_SubtractiveBlending.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_SubtractiveBlending.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_SubtractiveBlending.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_SubtractiveBlending.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ThreeTriangles.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_ThreeTriangles.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ThreeTriangles.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_ThreeTriangles.PNG diff --git a/integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ThreeTrianglesRed.PNG b/tests/integration/renderer-tests/res/MultipleTrianglesScene_ThreeTrianglesRed.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/MultipleTrianglesScene_ThreeTrianglesRed.PNG rename to tests/integration/renderer-tests/res/MultipleTrianglesScene_ThreeTrianglesRed.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Black.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Black.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Black.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Black.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_BlackOB.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_BlackOB.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_BlackOB.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_BlackOB.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_BufferDestroyed.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_BufferDestroyed.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_BufferDestroyed.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_BufferDestroyed.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_DepthTest.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_DepthTest.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_DepthTest.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_DepthTest.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked2.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked2.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked2.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked2_OB_Content.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked2_OB_Content.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked2_OB_Content.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked2_OB_Content.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedCustomClearColor.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedCustomClearColor.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedCustomClearColor.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedCustomClearColor.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedFinished.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedFinished.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedFinished.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedFinished.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedFinished2.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedFinished2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedFinished2.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedFinished2.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedInterrupted.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedInterrupted.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedInterrupted.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedInterrupted.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedInterrupted2.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedInterrupted2.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedInterrupted2.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedInterrupted2.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoConsumers.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoConsumers.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoConsumers.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoConsumers.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoConsumers_OB_Content.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoScenes.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoScenes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoScenes.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoScenes.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_LinkedTwoScenes_OB_Content.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked_OB_Content.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked_OB_Content.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Linked_OB_Content.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Linked_OB_Content.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_MSAABuffer.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_MSAABuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_MSAABuffer.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_MSAABuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_StencilTest.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_StencilTest.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_StencilTest.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_StencilTest.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Unlinked.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Unlinked.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_Unlinked.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_Unlinked.PNG diff --git a/integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_UnlinkedMSAABuffer.PNG b/tests/integration/renderer-tests/res/OffscreenBufferLinkTest_UnlinkedMSAABuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/OffscreenBufferLinkTest_UnlinkedMSAABuffer.PNG rename to tests/integration/renderer-tests/res/OffscreenBufferLinkTest_UnlinkedMSAABuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/ReferencedScenes.PNG b/tests/integration/renderer-tests/res/ReferencedScenes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ReferencedScenes.PNG rename to tests/integration/renderer-tests/res/ReferencedScenes.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderBuffer_MsaaSampleCount2Blit.PNG b/tests/integration/renderer-tests/res/RenderBuffer_MsaaSampleCount2Blit.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderBuffer_MsaaSampleCount2Blit.PNG rename to tests/integration/renderer-tests/res/RenderBuffer_MsaaSampleCount2Blit.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderBuffer_MsaaSampleCount4Blit.PNG b/tests/integration/renderer-tests/res/RenderBuffer_MsaaSampleCount4Blit.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderBuffer_MsaaSampleCount4Blit.PNG rename to tests/integration/renderer-tests/res/RenderBuffer_MsaaSampleCount4Blit.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderBuffer_MsaaSampleCount4TexelFetch.PNG b/tests/integration/renderer-tests/res/RenderBuffer_MsaaSampleCount4TexelFetch.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderBuffer_MsaaSampleCount4TexelFetch.PNG rename to tests/integration/renderer-tests/res/RenderBuffer_MsaaSampleCount4TexelFetch.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderBuffer_OneColorBufferNoDepthOrStencil.PNG b/tests/integration/renderer-tests/res/RenderBuffer_OneColorBufferNoDepthOrStencil.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderBuffer_OneColorBufferNoDepthOrStencil.PNG rename to tests/integration/renderer-tests/res/RenderBuffer_OneColorBufferNoDepthOrStencil.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer.PNG b/tests/integration/renderer-tests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer.PNG rename to tests/integration/renderer-tests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthBuffer.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer.PNG b/tests/integration/renderer-tests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer.PNG rename to tests/integration/renderer-tests/res/RenderBuffer_OneColorBufferWithWriteOnlyDepthStencilBuffer.PNG diff --git a/tests/integration/renderer-tests/res/RenderBuffer_ReadWriteDepthBuffer.PNG b/tests/integration/renderer-tests/res/RenderBuffer_ReadWriteDepthBuffer.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b634c9a5819aba432c9e0c20226e1d04628b5c3b GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5q8Ax6*bK?b4JOMr-u0Z-f1H&2yhU*Lrv-X|* z3*>itx;TbZFuuL)$lGKn;F9R`V2udBX(N}-gs;tHWit^|8)z5$SZB|y| zy8Z8iGbd9c$K;%?#xCgTe~DWM4fzCutV literal 0 HcmV?d00001 diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_Color.PNG b/tests/integration/renderer-tests/res/RenderPassClear_Color.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_Color.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_Color.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_ColorDepth.PNG b/tests/integration/renderer-tests/res/RenderPassClear_ColorDepth.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_ColorDepth.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_ColorDepth.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_ColorStencil.PNG b/tests/integration/renderer-tests/res/RenderPassClear_ColorStencil.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_ColorStencil.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_ColorStencil.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_ColorStencilDepth.PNG b/tests/integration/renderer-tests/res/RenderPassClear_ColorStencilDepth.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_ColorStencilDepth.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_ColorStencilDepth.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_Depth.PNG b/tests/integration/renderer-tests/res/RenderPassClear_Depth.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_Depth.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_Depth.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_None.PNG b/tests/integration/renderer-tests/res/RenderPassClear_None.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_None.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_None.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_Stencil.PNG b/tests/integration/renderer-tests/res/RenderPassClear_Stencil.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_Stencil.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_Stencil.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassClear_StencilDepth.PNG b/tests/integration/renderer-tests/res/RenderPassClear_StencilDepth.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassClear_StencilDepth.PNG rename to tests/integration/renderer-tests/res/RenderPassClear_StencilDepth.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassOnce_Initial.PNG b/tests/integration/renderer-tests/res/RenderPassOnce_Initial.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassOnce_Initial.PNG rename to tests/integration/renderer-tests/res/RenderPassOnce_Initial.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassOnce_Retriggered.PNG b/tests/integration/renderer-tests/res/RenderPassOnce_Retriggered.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassOnce_Retriggered.PNG rename to tests/integration/renderer-tests/res/RenderPassOnce_Retriggered.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassScene_LeftRightViewport.PNG b/tests/integration/renderer-tests/res/RenderPassScene_LeftRightViewport.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassScene_LeftRightViewport.PNG rename to tests/integration/renderer-tests/res/RenderPassScene_LeftRightViewport.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassScene_MeshInMultiplePasses.PNG b/tests/integration/renderer-tests/res/RenderPassScene_MeshInMultiplePasses.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassScene_MeshInMultiplePasses.PNG rename to tests/integration/renderer-tests/res/RenderPassScene_MeshInMultiplePasses.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassScene_MeshNotInPass.PNG b/tests/integration/renderer-tests/res/RenderPassScene_MeshNotInPass.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassScene_MeshNotInPass.PNG rename to tests/integration/renderer-tests/res/RenderPassScene_MeshNotInPass.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassScene_OneMeshPerPass.PNG b/tests/integration/renderer-tests/res/RenderPassScene_OneMeshPerPass.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassScene_OneMeshPerPass.PNG rename to tests/integration/renderer-tests/res/RenderPassScene_OneMeshPerPass.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderPassScene_PassesWithDifferentRenderOrder.PNG b/tests/integration/renderer-tests/res/RenderPassScene_PassesWithDifferentRenderOrder.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderPassScene_PassesWithDifferentRenderOrder.PNG rename to tests/integration/renderer-tests/res/RenderPassScene_PassesWithDifferentRenderOrder.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderTargetScene_OrthographicProjection.PNG b/tests/integration/renderer-tests/res/RenderTargetScene_OrthographicProjection.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderTargetScene_OrthographicProjection.PNG rename to tests/integration/renderer-tests/res/RenderTargetScene_OrthographicProjection.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderTargetScene_PerspectiveProjection.PNG b/tests/integration/renderer-tests/res/RenderTargetScene_PerspectiveProjection.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderTargetScene_PerspectiveProjection.PNG rename to tests/integration/renderer-tests/res/RenderTargetScene_PerspectiveProjection.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderTargetScene_Red.PNG b/tests/integration/renderer-tests/res/RenderTargetScene_Red.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderTargetScene_Red.PNG rename to tests/integration/renderer-tests/res/RenderTargetScene_Red.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderTargetScene_RedGreen.PNG b/tests/integration/renderer-tests/res/RenderTargetScene_RedGreen.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderTargetScene_RedGreen.PNG rename to tests/integration/renderer-tests/res/RenderTargetScene_RedGreen.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderTargetScene_RedGreenBlue.PNG b/tests/integration/renderer-tests/res/RenderTargetScene_RedGreenBlue.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderTargetScene_RedGreenBlue.PNG rename to tests/integration/renderer-tests/res/RenderTargetScene_RedGreenBlue.PNG diff --git a/integration/SandwichTests/RendererTests/res/RenderTargetScene_RedGreenBlue_NoAlpha.PNG b/tests/integration/renderer-tests/res/RenderTargetScene_RedGreenBlue_NoAlpha.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/RenderTargetScene_RedGreenBlue_NoAlpha.PNG rename to tests/integration/renderer-tests/res/RenderTargetScene_RedGreenBlue_NoAlpha.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_BoolUniform.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_BoolUniform.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_BoolUniform.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_BoolUniform.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_Discard.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_Discard.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_Discard.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_Discard.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_MultiStageUniform.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_MultiStageUniform.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_MultiStageUniform.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_MultiStageUniform.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_OptimizedInput.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_OptimizedInput.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_OptimizedInput.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_OptimizedInput.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_StructUniform.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_StructUniform.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_StructUniform.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_StructUniform.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_TextureSize.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_TextureSize.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_TextureSize.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_TextureSize.PNG diff --git a/integration/SandwichTests/RendererTests/res/ShaderTestScene_TexturedQuad.PNG b/tests/integration/renderer-tests/res/ShaderTestScene_TexturedQuad.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/ShaderTestScene_TexturedQuad.PNG rename to tests/integration/renderer-tests/res/ShaderTestScene_TexturedQuad.PNG diff --git a/integration/SandwichTests/RendererTests/res/SingleAppearanceScene_ChangeAppearance.PNG b/tests/integration/renderer-tests/res/SingleAppearanceScene_ChangeAppearance.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/SingleAppearanceScene_ChangeAppearance.PNG rename to tests/integration/renderer-tests/res/SingleAppearanceScene_ChangeAppearance.PNG diff --git a/integration/SandwichTests/RendererTests/res/SingleAppearanceScene_GreenTriangles.PNG b/tests/integration/renderer-tests/res/SingleAppearanceScene_GreenTriangles.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/SingleAppearanceScene_GreenTriangles.PNG rename to tests/integration/renderer-tests/res/SingleAppearanceScene_GreenTriangles.PNG diff --git a/integration/SandwichTests/RendererTests/res/SingleAppearanceScene_RedTriangles.PNG b/tests/integration/renderer-tests/res/SingleAppearanceScene_RedTriangles.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/SingleAppearanceScene_RedTriangles.PNG rename to tests/integration/renderer-tests/res/SingleAppearanceScene_RedTriangles.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_ChangedText.PNG b/tests/integration/renderer-tests/res/TextScene_ChangedText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_ChangedText.PNG rename to tests/integration/renderer-tests/res/TextScene_ChangedText.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_DeletedTextsAndNode.PNG b/tests/integration/renderer-tests/res/TextScene_DeletedTextsAndNode.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_DeletedTextsAndNode.PNG rename to tests/integration/renderer-tests/res/TextScene_DeletedTextsAndNode.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_FontCascade.PNG b/tests/integration/renderer-tests/res/TextScene_FontCascade.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_FontCascade.PNG rename to tests/integration/renderer-tests/res/TextScene_FontCascade.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_ForceAutoHinting.PNG b/tests/integration/renderer-tests/res/TextScene_ForceAutoHinting.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_ForceAutoHinting.PNG rename to tests/integration/renderer-tests/res/TextScene_ForceAutoHinting.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_Shaping.PNG b/tests/integration/renderer-tests/res/TextScene_Shaping.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_Shaping.PNG rename to tests/integration/renderer-tests/res/TextScene_Shaping.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_SimpleText.PNG b/tests/integration/renderer-tests/res/TextScene_SimpleText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_SimpleText.PNG rename to tests/integration/renderer-tests/res/TextScene_SimpleText.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_SmallText.PNG b/tests/integration/renderer-tests/res/TextScene_SmallText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_SmallText.PNG rename to tests/integration/renderer-tests/res/TextScene_SmallText.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_VerticalOffset.PNG b/tests/integration/renderer-tests/res/TextScene_VerticalOffset.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_VerticalOffset.PNG rename to tests/integration/renderer-tests/res/TextScene_VerticalOffset.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextScene_YellowText.PNG b/tests/integration/renderer-tests/res/TextScene_YellowText.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextScene_YellowText.PNG rename to tests/integration/renderer-tests/res/TextScene_YellowText.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DAnisotropicTextureFilteringScene_Anisotropic.PNG b/tests/integration/renderer-tests/res/Texture2DAnisotropicTextureFilteringScene_Anisotropic.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DAnisotropicTextureFilteringScene_Anisotropic.PNG rename to tests/integration/renderer-tests/res/Texture2DAnisotropicTextureFilteringScene_Anisotropic.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DCompressedMipMapScene_CompressedMipMap.PNG b/tests/integration/renderer-tests/res/Texture2DCompressedMipMapScene_CompressedMipMap.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DCompressedMipMapScene_CompressedMipMap.PNG rename to tests/integration/renderer-tests/res/Texture2DCompressedMipMapScene_CompressedMipMap.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ASTC_RGBA_4x4.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_ASTC_RGBA_4x4.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ASTC_RGBA_4x4.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_ASTC_RGBA_4x4.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_ASTC_SRGB_ALPHA_4x4.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ETC2RGB.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_ETC2RGB.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ETC2RGB.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_ETC2RGB.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ETC2RGBA.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_ETC2RGBA.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_ETC2RGBA.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_ETC2RGBA.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_FloatRG.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_FloatRG.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_FloatRG.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_FloatRG.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_FloatRGB.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_FloatRGB.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_FloatRGB.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_FloatRGB.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_FloatRed.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_FloatRed.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_FloatRed.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_FloatRed.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_R8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_R8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_R8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_R8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RG8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_RG8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RG8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_RG8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGB565.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_RGB565.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGB565.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_RGB565.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGB8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_RGB8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGB8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_RGB8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGBA4.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_RGBA4.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGBA4.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_RGBA4.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGBA5551.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_RGBA5551.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGBA5551.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_RGBA5551.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGBA8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_RGBA8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_RGBA8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_RGBA8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_SRGB8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_SRGB8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_SRGB8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_SRGB8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_SRGB8_ALPHA8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_SRGB8_ALPHA8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_SRGB8_ALPHA8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_SRGB8_ALPHA8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_Swizzled_BGR8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_Swizzled_BGR8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_Swizzled_BGR8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_Swizzled_BGR8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_Swizzled_BGRA8.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_Swizzled_BGRA8.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_Swizzled_BGRA8.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_Swizzled_BGRA8.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DFormatScene_Swizzled_Luminance_Alpha.PNG b/tests/integration/renderer-tests/res/Texture2DFormatScene_Swizzled_Luminance_Alpha.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DFormatScene_Swizzled_Luminance_Alpha.PNG rename to tests/integration/renderer-tests/res/Texture2DFormatScene_Swizzled_Luminance_Alpha.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DGenerateMipMapScene_GenerateMultipleMipMaps.PNG b/tests/integration/renderer-tests/res/Texture2DGenerateMipMapScene_GenerateMultipleMipMaps.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DGenerateMipMapScene_GenerateMultipleMipMaps.PNG rename to tests/integration/renderer-tests/res/Texture2DGenerateMipMapScene_GenerateMultipleMipMaps.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DGenerateMipMapScene_GenerateSingleMipMap.PNG b/tests/integration/renderer-tests/res/Texture2DGenerateMipMapScene_GenerateSingleMipMap.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DGenerateMipMapScene_GenerateSingleMipMap.PNG rename to tests/integration/renderer-tests/res/Texture2DGenerateMipMapScene_GenerateSingleMipMap.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_Bilinear.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_Bilinear.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_Bilinear.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_Bilinear.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_BilinearWithMipMaps.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_BilinearWithMipMaps.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_BilinearWithMipMaps.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_BilinearWithMipMaps.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_MinLinearMagNearest.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_MinLinearMagNearest.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_MinLinearMagNearest.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_MinLinearMagNearest.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_MinNearestMagLinear.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_MinNearestMagLinear.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_MinNearestMagLinear.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_MinNearestMagLinear.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_Nearest.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_Nearest.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_Nearest.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_Nearest.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_NearestWithMipMaps.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_NearestWithMipMaps.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_NearestWithMipMaps.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_NearestWithMipMaps.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_Trilinear.PNG b/tests/integration/renderer-tests/res/Texture2DSamplingScene_Trilinear.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture2DSamplingScene_Trilinear.PNG rename to tests/integration/renderer-tests/res/Texture2DSamplingScene_Trilinear.PNG diff --git a/integration/SandwichTests/RendererTests/res/Texture3DScene_4Slices.PNG b/tests/integration/renderer-tests/res/Texture3DScene_4Slices.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/Texture3DScene_4Slices.PNG rename to tests/integration/renderer-tests/res/Texture3DScene_4Slices.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextureAddressScene_AllAddressModes.PNG b/tests/integration/renderer-tests/res/TextureAddressScene_AllAddressModes.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextureAddressScene_AllAddressModes.PNG rename to tests/integration/renderer-tests/res/TextureAddressScene_AllAddressModes.PNG diff --git a/integration/SandwichTests/RendererTests/res/TextureBuffer_PartialUpdate.PNG b/tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate.PNG similarity index 100% rename from integration/SandwichTests/RendererTests/res/TextureBuffer_PartialUpdate.PNG rename to tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate.PNG diff --git a/tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate1.PNG b/tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate1.PNG new file mode 100644 index 0000000000000000000000000000000000000000..fd4c47e9f0dd79401cd1172b4f54878a3e1bac35 GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5m8A!&LZC(qc3<7*YTp1WHFfcq|VEDkm@SlNU z!hr(}|A92a0S1Nu2M30R1|S=V1sE6zJzX3_Dj46My~uEgL89TH zNRWqOE0maVNMxBot7Z<%{low3)C;zk==Xop5$beNn&_bdr&4C^Ikmgy)OI8xu!2dM rpS1sPnz9vBeACpuGEXthnGw^m_O-T({^Dw&vlu*G{an^LB{Ts5^t(~q literal 0 HcmV?d00001 diff --git a/tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate2.PNG b/tests/integration/renderer-tests/res/TextureBuffer_PartialUpdate2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..7104a806991c81cd9b1ac8f0a1718f5b0e79da72 GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5m8A!&LZC(qcbOU@sTp1WHFfcq|VEDkm@SlNU z0ucWPVuk|@3;_)dKn4&CFfb@EFc>f}IB*}CGaV?_?djqeQo;E4>_vt{3=$0oMS?sO zTfu~;Pa;?Fj6)*F44U3QtluxZ!@X)+c%^u!i_%076(Klf(TestRandom::Get(5, 10)); + const auto randomDivisor = static_cast(TestRandom::Get(5, 10)); - resources.renderBuffer = m_scene.createRenderBuffer(m_screenspaceQuad.getWidthOfScreen() / randomDivisor, m_screenspaceQuad.getHeightOfScreen()/ randomDivisor, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); + resources.renderBuffer = m_scene.createRenderBuffer(m_screenspaceQuad.getWidthOfScreen() / randomDivisor, m_screenspaceQuad.getHeightOfScreen()/ randomDivisor, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); ramses::RenderTargetDescription renderTargetDescription; renderTargetDescription.addRenderBuffer(*resources.renderBuffer); diff --git a/integration/StressTests/ResourceStressTests/src/DynamicQuad_OffscreenRenderTarget.h b/tests/integration/resource-stress-tests/DynamicQuad_OffscreenRenderTarget.h similarity index 92% rename from integration/StressTests/ResourceStressTests/src/DynamicQuad_OffscreenRenderTarget.h rename to tests/integration/resource-stress-tests/DynamicQuad_OffscreenRenderTarget.h index 48a76428e..a88120b3f 100644 --- a/integration/StressTests/ResourceStressTests/src/DynamicQuad_OffscreenRenderTarget.h +++ b/tests/integration/resource-stress-tests/DynamicQuad_OffscreenRenderTarget.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DYNAMICQUAD_OFFSCREENRENDERTARGET_H -#define RAMSES_DYNAMICQUAD_OFFSCREENRENDERTARGET_H +#pragma once #include "DynamicQuad_Base.h" @@ -17,7 +16,7 @@ namespace ramses class RenderTarget; } -namespace ramses_internal +namespace ramses::internal { struct RenderTargetResources { @@ -46,5 +45,3 @@ namespace ramses_internal RenderTargetResources m_renderTargetSceneObjects; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/DynamicQuad_Resources.cpp b/tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp similarity index 88% rename from integration/StressTests/ResourceStressTests/src/DynamicQuad_Resources.cpp rename to tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp index 4e045c8f5..6e216eea7 100644 --- a/integration/StressTests/ResourceStressTests/src/DynamicQuad_Resources.cpp +++ b/tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp @@ -8,20 +8,20 @@ #include "DynamicQuad_Resources.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/TextureSampler.h" - -#include "PlatformAbstraction/PlatformMath.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/TextureSampler.h" + +#include "internal/PlatformAbstraction/PlatformMath.h" #include "TestRandom.h" #include -namespace ramses_internal +namespace ramses::internal { DynamicQuad_Resources::DynamicQuad_Resources(ramses::Scene& scene, const ScreenspaceQuad& screenspaceQuad) : DynamicQuad_Base(scene, screenspaceQuad) @@ -35,7 +35,7 @@ namespace ramses_internal m_meshNode.setAppearance(m_appearance); m_meshNode.setIndexCount(4); m_appearance.setDrawMode(ramses::EDrawMode::TriangleStrip); - m_meshNode.setGeometryBinding(m_geometryBinding); + m_meshNode.setGeometry(m_geometryBinding); } @@ -91,7 +91,7 @@ namespace ramses_internal } ramses::MipLevelData textureData(textureWidth * textureHeight * 3, rawData.get()); - resources.texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, textureWidth, textureHeight, 1, &textureData, false, {}, ramses::ResourceCacheFlag_DoNotCache); + resources.texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, textureWidth, textureHeight, 1, &textureData, false, {}); resources.textureSampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, diff --git a/integration/StressTests/ResourceStressTests/src/DynamicQuad_Resources.h b/tests/integration/resource-stress-tests/DynamicQuad_Resources.h similarity index 92% rename from integration/StressTests/ResourceStressTests/src/DynamicQuad_Resources.h rename to tests/integration/resource-stress-tests/DynamicQuad_Resources.h index da284770c..17d1adbd4 100644 --- a/integration/StressTests/ResourceStressTests/src/DynamicQuad_Resources.h +++ b/tests/integration/resource-stress-tests/DynamicQuad_Resources.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DYNAMICQUAD_RESOURCES_H -#define RAMSES_DYNAMICQUAD_RESOURCES_H +#pragma once #include "DynamicQuad_Base.h" @@ -17,7 +16,7 @@ namespace ramses class TextureSampler; } -namespace ramses_internal +namespace ramses::internal { struct TextureResources { @@ -45,5 +44,3 @@ namespace ramses_internal TextureResources m_textureResources; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/DynamicQuad_SceneResources.cpp b/tests/integration/resource-stress-tests/DynamicQuad_SceneResources.cpp similarity index 77% rename from integration/StressTests/ResourceStressTests/src/DynamicQuad_SceneResources.cpp rename to tests/integration/resource-stress-tests/DynamicQuad_SceneResources.cpp index 56fe16781..631e0141d 100644 --- a/integration/StressTests/ResourceStressTests/src/DynamicQuad_SceneResources.cpp +++ b/tests/integration/resource-stress-tests/DynamicQuad_SceneResources.cpp @@ -8,24 +8,24 @@ #include "DynamicQuad_SceneResources.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" #include "TestRandom.h" #include -namespace ramses_internal +namespace ramses::internal { DynamicQuad_SceneResources::DynamicQuad_SceneResources(ramses::Scene& scene, const ScreenspaceQuad& screenspaceQuad) : DynamicQuad_Base(scene, screenspaceQuad) @@ -37,25 +37,18 @@ namespace ramses_internal { m_renderGroup.addMeshNode(m_meshNode); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordInput; - ramses::UniformInput textureInput; - m_appearance.getEffect().findUniformInput("u_texture", textureInput); - m_appearance.getEffect().findAttributeInput("a_position", positionsInput); - m_appearance.getEffect().findAttributeInput("a_texcoord", texcoordInput); - m_geometryBinding.setIndices(*m_indices); - m_geometryBinding.setInputBuffer(positionsInput, *m_vertexPos); - m_geometryBinding.setInputBuffer(texcoordInput, *m_texCoords); + m_geometryBinding.setInputBuffer(*m_appearance.getEffect().findAttributeInput("a_position"), *m_vertexPos); + m_geometryBinding.setInputBuffer(*m_appearance.getEffect().findAttributeInput("a_texcoord"), *m_texCoords); - m_appearance.setInputTexture(textureInput, *m_textureSampler); + m_appearance.setInputTexture(*m_appearance.getEffect().findUniformInput("u_texture"), *m_textureSampler); recreate(); m_meshNode.setAppearance(m_appearance); m_meshNode.setIndexCount(4); m_appearance.setDrawMode(ramses::EDrawMode::TriangleStrip); - m_meshNode.setGeometryBinding(m_geometryBinding); + m_meshNode.setGeometry(m_geometryBinding); } DynamicQuad_SceneResources::~DynamicQuad_SceneResources() diff --git a/integration/StressTests/ResourceStressTests/src/DynamicQuad_SceneResources.h b/tests/integration/resource-stress-tests/DynamicQuad_SceneResources.h similarity index 92% rename from integration/StressTests/ResourceStressTests/src/DynamicQuad_SceneResources.h rename to tests/integration/resource-stress-tests/DynamicQuad_SceneResources.h index 96630ed18..f144b5cf6 100644 --- a/integration/StressTests/ResourceStressTests/src/DynamicQuad_SceneResources.h +++ b/tests/integration/resource-stress-tests/DynamicQuad_SceneResources.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DYNAMICQUAD_SCENERESOURCES_H -#define RAMSES_DYNAMICQUAD_SCENERESOURCES_H +#pragma once #include "DynamicQuad_Base.h" @@ -18,7 +17,7 @@ namespace ramses class ArrayBuffer; } -namespace ramses_internal +namespace ramses::internal { class DynamicQuad_SceneResources : public DynamicQuad_Base { @@ -43,5 +42,3 @@ namespace ramses_internal static const uint32_t DynamicTextureHeight = 32u; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/ResourceStressTestScene.cpp b/tests/integration/resource-stress-tests/ResourceStressTestScene.cpp similarity index 82% rename from integration/StressTests/ResourceStressTests/src/ResourceStressTestScene.cpp rename to tests/integration/resource-stress-tests/ResourceStressTestScene.cpp index d18a54f60..48d922843 100644 --- a/integration/StressTests/ResourceStressTests/src/ResourceStressTestScene.cpp +++ b/tests/integration/resource-stress-tests/ResourceStressTestScene.cpp @@ -8,28 +8,28 @@ #include "ResourceStressTestScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/RenderTargetDescription.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/Camera.h" +#include "ramses/client/Effect.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/RenderTargetDescription.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/PlatformAbstraction/PlatformMath.h" -namespace ramses_internal +namespace ramses::internal { ResourceStressTestScene::ResourceStressTestScene(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const TextureConsumerDataIds& texConsumerDataIds, const ScreenspaceQuad& screenspaceQuad) : m_client(client) @@ -59,10 +59,10 @@ namespace ramses_internal } m_offscreenRenderPass.setRenderOrder(0); - m_offscreenRenderPass.setClearFlags(ramses::EClearFlags_All); + m_offscreenRenderPass.setClearFlags(ramses::EClearFlag::All); m_offscreenRenderPass.setClearColor({0.4f, 0.1f, 0.0f, 1.0f}); - m_finalRenderPass.setClearFlags(ramses::EClearFlags_None); + m_finalRenderPass.setClearFlags(ramses::EClearFlag::None); m_finalRenderPass.setRenderOrder(1); // Linking meshes, render groups, and render passes together @@ -107,6 +107,11 @@ namespace ramses_internal m_scene.flush(flushName); } + ramses::Scene& ResourceStressTestScene::getScene() + { + return m_scene; + } + ramses::RenderPass& ResourceStressTestScene::CreateRenderPass(ramses::Scene& scene, ramses::OrthographicCamera& camera) { ramses::RenderPass* renderPass = scene.createRenderPass(); diff --git a/integration/StressTests/ResourceStressTests/src/ResourceStressTestScene.h b/tests/integration/resource-stress-tests/ResourceStressTestScene.h similarity index 90% rename from integration/StressTests/ResourceStressTests/src/ResourceStressTestScene.h rename to tests/integration/resource-stress-tests/ResourceStressTestScene.h index 089764d6e..2f0b6212b 100644 --- a/integration/StressTests/ResourceStressTests/src/ResourceStressTestScene.h +++ b/tests/integration/resource-stress-tests/ResourceStressTestScene.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCESTRESSTESTSCENE_H -#define RAMSES_RESOURCESTRESSTESTSCENE_H +#pragma once -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/EScenePublicationMode.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/framework/EScenePublicationMode.h" #include "DynamicQuad_Resources.h" #include "DynamicQuad_OffscreenRenderTarget.h" #include "DynamicQuad_SceneResources.h" @@ -26,14 +25,14 @@ namespace ramses class Scene; class RamsesClient; class Appearance; - class GeometryBinding; + class Geometry; class OrthographicCamera; class Effect; class MeshNode; class TextureSampler; } -namespace ramses_internal +namespace ramses::internal { using TextureConsumerDataIds = std::vector; @@ -45,6 +44,7 @@ namespace ramses_internal void recreateResources(bool recreateClientResources = true, bool recreateSceneResources = true, bool recreateSceneRenderTargets = true); void flush(ramses::sceneVersionTag_t flushName); + ramses::Scene& getScene(); private: static ramses::RenderPass& CreateRenderPass(ramses::Scene& scene, ramses::OrthographicCamera& camera); @@ -63,5 +63,3 @@ namespace ramses_internal std::vector> m_quadsWithTextureConsumerLinks; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/ResourceStressTestSceneArray.cpp b/tests/integration/resource-stress-tests/ResourceStressTestSceneArray.cpp similarity index 95% rename from integration/StressTests/ResourceStressTests/src/ResourceStressTestSceneArray.cpp rename to tests/integration/resource-stress-tests/ResourceStressTestSceneArray.cpp index d17eed1b2..3109c4d77 100644 --- a/integration/StressTests/ResourceStressTests/src/ResourceStressTestSceneArray.cpp +++ b/tests/integration/resource-stress-tests/ResourceStressTestSceneArray.cpp @@ -10,7 +10,7 @@ #include "StressTestRenderer.h" #include -namespace ramses_internal +namespace ramses::internal { ResourceStressTestSceneArray::ResourceStressTestSceneArray(ramses::RamsesClient& client, StressTestRenderer& renderer, const SceneArrayConfig& sceneArrayConfig) @@ -33,8 +33,8 @@ namespace ramses_internal m_renderer.setSceneState(sceneConfig.sceneId, state); // wait for all scenes to reach their state - for (const auto& sceneConfig : m_sceneConfigs) - m_renderer.waitForSceneState(sceneConfig.sceneId, state); + for (const auto& scene : m_scenes) + m_renderer.waitForSceneState(scene->getScene(), state); // Link OBs to consumers if (state == ramses::RendererSceneState::Rendered) diff --git a/integration/StressTests/ResourceStressTests/src/ResourceStressTestSceneArray.h b/tests/integration/resource-stress-tests/ResourceStressTestSceneArray.h similarity index 89% rename from integration/StressTests/ResourceStressTests/src/ResourceStressTestSceneArray.h rename to tests/integration/resource-stress-tests/ResourceStressTestSceneArray.h index e09f0b052..dbf9ec9db 100644 --- a/integration/StressTests/ResourceStressTests/src/ResourceStressTestSceneArray.h +++ b/tests/integration/resource-stress-tests/ResourceStressTestSceneArray.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCESTRESSTESTSCENEARRAY_H -#define RAMSES_RESOURCESTRESSTESTSCENEARRAY_H +#pragma once #include "ResourceStressTestScene.h" -#include "ramses-framework-api/RendererSceneState.h" +#include "ramses/framework/RendererSceneState.h" #include #include -namespace ramses_internal +namespace ramses::internal { class StressTestRenderer; @@ -46,5 +45,3 @@ namespace ramses_internal std::vector> m_scenes; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/ResourceStressTests.cpp b/tests/integration/resource-stress-tests/ResourceStressTests.cpp similarity index 95% rename from integration/StressTests/ResourceStressTests/src/ResourceStressTests.cpp rename to tests/integration/resource-stress-tests/ResourceStressTests.cpp index 8a2cca52f..2238c6bca 100644 --- a/integration/StressTests/ResourceStressTests/src/ResourceStressTests.cpp +++ b/tests/integration/resource-stress-tests/ResourceStressTests.cpp @@ -8,12 +8,12 @@ #include "ResourceStressTests.h" -#include "ramses-renderer-api/RendererConfig.h" +#include "ramses/renderer/RendererConfig.h" #include "ResourceStressTestSceneArray.h" -#include "RenderExecutor.h" +#include "internal/RendererLib/RenderExecutor.h" -namespace ramses_internal +namespace ramses::internal { const std::array StressTestCaseNames = { "EStressTestCaseId_recreateResourcesEveryFrameWithSyncFlush", @@ -24,7 +24,9 @@ namespace ramses_internal "EStressTestCaseId_recreateResourcesEveryFrameWithSyncFlush_MapSceneAfterAWhile_ExtremelyLowRendererFPS", "EStressTestCaseId_recreateResourcesEveryFrameWithSyncFlush_RemapSceneAllTheTime", }; - ENUM_TO_STRING(EStressTestCaseId, StressTestCaseNames, EStressTestCaseId_NUMBER_OF_ELEMENTS); + const size_t EStressTestCaseId_NUMBER_OF_ELEMENTS = StressTestCaseNames.size(); + static_assert(EnumTraits::VerifyElementCountIfSupported(EStressTestCaseId_NUMBER_OF_ELEMENTS)); + ENUM_TO_STRING(EStressTestCaseId, StressTestCaseNames, EStressTestCaseId_recreateResourcesEveryFrameWithSyncFlush_RemapSceneAllTheTime); ResourceStressTests::ResourceStressTests(const StressTestConfig& config) : m_testConfig(config) @@ -38,7 +40,7 @@ namespace ramses_internal m_framework.connect(); m_testRenderer.startLooping(); - //ramses_internal::RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = config.renderablesBatchSizeForRenderingInterruption; + //ramses::internal::RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks = config.renderablesBatchSizeForRenderingInterruption; m_testRenderer.setSkippingOfUnmodifiedBuffers(!config.disableSkippingOfFrames); @@ -80,7 +82,7 @@ namespace ramses_internal 5, //"EStressTestCaseId_recreateResourcesEveryFrameWithSyncFlush_MapSceneAfterAWhile_ExtremelyLowRendererFPS", 15,//"EStressTestCaseId_recreateResourcesEveryFrameWithSyncFlush_RemapSceneAllTheTime", }; - static_assert(static_cast(EStressTestCaseId_NUMBER_OF_ELEMENTS) == MinDurationPerTestSeconds.size(), "Size mismatch"); + static_assert(EStressTestCaseId_NUMBER_OF_ELEMENTS == MinDurationPerTestSeconds.size(), "Size mismatch"); if (m_testConfig.durationEachTestSeconds < MinDurationPerTestSeconds[testToRun]) { @@ -130,7 +132,7 @@ namespace ramses_internal SceneArrayConfig ResourceStressTests::generateStressSceneConfig() const { // Top level scene == scene which is rendered directly to framebuffer - const uint32_t displayCount = static_cast(m_displays.size()); + const auto displayCount = static_cast(m_displays.size()); const uint32_t topLevelSceneCount = m_sceneSetsPerDisplay * displayCount; ramses::dataConsumerId_t textureConsumerId{0}; @@ -347,7 +349,7 @@ namespace ramses_internal } } - printf("Test results:\n%s", verboseTestResults.c_str()); + fmt::print("Test results:\n{}", verboseTestResults.c_str()); return testResult; } diff --git a/integration/StressTests/ResourceStressTests/src/ResourceStressTests.h b/tests/integration/resource-stress-tests/ResourceStressTests.h similarity index 83% rename from integration/StressTests/ResourceStressTests/src/ResourceStressTests.h rename to tests/integration/resource-stress-tests/ResourceStressTests.h index cce7ae3a8..a52a2a3d9 100644 --- a/integration/StressTests/ResourceStressTests/src/ResourceStressTests.h +++ b/tests/integration/resource-stress-tests/ResourceStressTests.h @@ -6,34 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCESTRESSTESTS_RESOURCESTRESSTESTS_H -#define RAMSES_RESOURCESTRESSTESTS_RESOURCESTRESSTESTS_H +#pragma once -#include "ramses-client-api/RamsesClient.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" #include "StressTestRenderer.h" #include "ResourceStressTestScene.h" -#include "PlatformAbstraction/PlatformTypes.h" #include "ResourceStressTestSceneArray.h" -namespace ramses_internal +#include + +namespace ramses::internal { struct StressTestConfig { ramses::RamsesFrameworkConfig frameworkConfig{ ramses::EFeatureLevel_Latest }; ramses::RendererConfig rendererConfig; ramses::DisplayConfig displayConfig; - uint32_t durationEachTestSeconds; - uint32_t displayCount; - uint32_t sceneSetsPerDisplay; - bool disableSkippingOfFrames; - uint32_t perFrameBudgetMSec_ClientRes; - uint32_t perFrameBudgetMSec_Rendering; - uint32_t renderablesBatchSizeForRenderingInterruption; + uint32_t durationEachTestSeconds = 0; + uint32_t displayCount = 0; + uint32_t sceneSetsPerDisplay = 0; + bool disableSkippingOfFrames = false; + uint32_t perFrameBudgetMSec_ClientRes = 0; + uint32_t perFrameBudgetMSec_Rendering = 0; + uint32_t renderablesBatchSizeForRenderingInterruption = 0; }; enum EStressTestCaseId @@ -61,9 +61,6 @@ namespace ramses_internal EStressTestCaseId_createDestroyDynamicResources, EStressTestCaseId_updateDynamicResourcesLikeCrazy, EStressTestCaseId_switchBetweenClientAndSceneResourcesLikeCrazy,*/ - - //keep this at the end - EStressTestCaseId_NUMBER_OF_ELEMENTS }; class ResourceStressTests @@ -106,8 +103,8 @@ namespace ramses_internal struct DisplayData { ramses::displayId_t displayId; - uint32_t width; - uint32_t height; + uint32_t width = 0; + uint32_t height = 0; OffscreenBuffers offscreenBuffers; }; @@ -119,5 +116,3 @@ namespace ramses_internal mutable uint32_t m_consumeRendererEventsCounter = 1; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/ScreenspaceQuad.h b/tests/integration/resource-stress-tests/ScreenspaceQuad.h similarity index 95% rename from integration/StressTests/ResourceStressTests/src/ScreenspaceQuad.h rename to tests/integration/resource-stress-tests/ScreenspaceQuad.h index 5ce3cbd53..b7f51fb3e 100644 --- a/integration/StressTests/ResourceStressTests/src/ScreenspaceQuad.h +++ b/tests/integration/resource-stress-tests/ScreenspaceQuad.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCREENSPACEQUAD_H -#define RAMSES_SCREENSPACEQUAD_H +#pragma once -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Scene.h" -#include "DataTypesImpl.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Scene.h" +#include "impl/DataTypesImpl.h" #include "TestRandom.h" -namespace ramses_internal +namespace ramses::internal { enum class EScreenspaceQuadVertex { @@ -108,5 +107,3 @@ namespace ramses_internal uint32_t m_quadHeight; }; } - -#endif diff --git a/integration/StressTests/ResourceStressTests/src/StressTestRenderer.cpp b/tests/integration/resource-stress-tests/StressTestRenderer.cpp similarity index 65% rename from integration/StressTests/ResourceStressTests/src/StressTestRenderer.cpp rename to tests/integration/resource-stress-tests/StressTestRenderer.cpp index 894c66ce4..2e878a208 100644 --- a/integration/StressTests/ResourceStressTests/src/StressTestRenderer.cpp +++ b/tests/integration/resource-stress-tests/StressTestRenderer.cpp @@ -7,9 +7,9 @@ // ------------------------------------------------------------------------- #include "StressTestRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" +#include "ramses/renderer/DisplayConfig.h" -namespace ramses_internal +namespace ramses::internal { StressTestRenderer::StressTestRenderer(ramses::RamsesFramework& framework, const ramses::RendererConfig& config) : m_framework(framework) @@ -24,26 +24,26 @@ namespace ramses_internal m_framework.destroyRenderer(m_renderer); } - ramses::displayId_t StressTestRenderer::createDisplay(uint32_t offsetX, uint32_t width, uint32_t height, uint32_t displayIndex, const ramses::DisplayConfig& config) + displayId_t StressTestRenderer::createDisplay(uint32_t offsetX, uint32_t width, uint32_t height, uint32_t displayIndex, const ramses::DisplayConfig& config) { ramses::DisplayConfig displayConfig(config); displayConfig.setWindowRectangle(offsetX + 50, 50, width, height); const auto iviSurfaceId = displayConfig.getWaylandIviSurfaceID(); - displayConfig.setWaylandIviSurfaceID(ramses::waylandIviSurfaceId_t((iviSurfaceId.isValid() ? iviSurfaceId.getValue() : 10000) + displayIndex)); + displayConfig.setWaylandIviSurfaceID(waylandIviSurfaceId_t((iviSurfaceId.isValid() ? iviSurfaceId.getValue() : 10000) + displayIndex)); if(!displayConfig.getWaylandIviLayerID().isValid()) - displayConfig.setWaylandIviLayerID(ramses::waylandIviLayerId_t(2)); + displayConfig.setWaylandIviLayerID(waylandIviLayerId_t(2)); - ramses::displayId_t displayId = m_renderer.createDisplay(displayConfig); + displayId_t displayId = m_renderer.createDisplay(displayConfig); m_renderer.flush(); m_eventHandler.waitForDisplayCreation(displayId); return displayId; } - ramses::displayBufferId_t StressTestRenderer::createOffscreenBuffer(ramses::displayId_t displayId, uint32_t width, uint32_t height, bool interruptable) + displayBufferId_t StressTestRenderer::createOffscreenBuffer(displayId_t displayId, uint32_t width, uint32_t height, bool interruptable) { - ramses::displayBufferId_t offscreenBufferId; + displayBufferId_t offscreenBufferId; if (interruptable) { offscreenBufferId = m_renderer.createInterruptibleOffscreenBuffer(displayId, width, height); @@ -63,9 +63,9 @@ namespace ramses_internal m_renderer.startThread(); } - void StressTestRenderer::setFPS(ramses::displayId_t display, uint32_t fpsAsInteger) + void StressTestRenderer::setFPS(displayId_t display, uint32_t fpsAsInteger) { - const float fpsAsFloatBecauseWhyNot = static_cast(fpsAsInteger); + const auto fpsAsFloatBecauseWhyNot = static_cast(fpsAsInteger); m_renderer.setFramerateLimit(display, fpsAsFloatBecauseWhyNot); } @@ -79,30 +79,30 @@ namespace ramses_internal m_renderer.setSkippingOfUnmodifiedBuffers(enabled); } - void StressTestRenderer::setSceneDisplayAndBuffer(ramses::sceneId_t sceneId, ramses::displayId_t display, ramses::displayBufferId_t displayBuffer) + void StressTestRenderer::setSceneDisplayAndBuffer(sceneId_t sceneId, displayId_t display, displayBufferId_t displayBuffer) { m_sceneControlAPI.setSceneMapping(sceneId, display); m_sceneControlAPI.setSceneDisplayBufferAssignment(sceneId, displayBuffer); m_sceneControlAPI.flush(); } - void StressTestRenderer::setSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + void StressTestRenderer::setSceneState(sceneId_t sceneId, RendererSceneState state) { m_sceneControlAPI.setSceneState(sceneId, state); m_sceneControlAPI.flush(); } - void StressTestRenderer::waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + void StressTestRenderer::waitForSceneState(ramses::Scene& scene, RendererSceneState state) { - m_eventHandler.waitForSceneState(sceneId, state); + m_eventHandler.waitForSceneState(scene, state); } - void StressTestRenderer::waitForFlush(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t flushVersion) + void StressTestRenderer::waitForFlush(sceneId_t sceneId, sceneVersionTag_t flushVersion) { m_eventHandler.waitForFlush(sceneId, flushVersion); } - void StressTestRenderer::linkOffscreenBufferToSceneTexture(ramses::sceneId_t sceneId, ramses::displayBufferId_t offscreenBuffer, ramses::dataConsumerId_t consumerTexture) + void StressTestRenderer::linkOffscreenBufferToSceneTexture(sceneId_t sceneId, displayBufferId_t offscreenBuffer, dataConsumerId_t consumerTexture) { m_sceneControlAPI.linkOffscreenBuffer(offscreenBuffer, sceneId, consumerTexture); m_sceneControlAPI.flush(); diff --git a/tests/integration/resource-stress-tests/StressTestRenderer.h b/tests/integration/resource-stress-tests/StressTestRenderer.h new file mode 100644 index 000000000..4639ff9c6 --- /dev/null +++ b/tests/integration/resource-stress-tests/StressTestRenderer.h @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/RamsesRenderer.h" +#include "RendererAndSceneTestEventHandler.h" + +namespace ramses +{ + class RamsesRenderer; + class RamsesFramework; +} + +namespace ramses::internal +{ + class StressTestRenderer + { + public: + StressTestRenderer(ramses::RamsesFramework& framework, const ramses::RendererConfig& config); + ~StressTestRenderer(); + + displayId_t createDisplay(uint32_t offsetX, uint32_t width, uint32_t height, uint32_t displayIndex, const ramses::DisplayConfig& config); + displayBufferId_t createOffscreenBuffer(displayId_t displayId, uint32_t width, uint32_t height, bool interruptable); + void startLooping(); + void setFPS(displayId_t display, uint32_t fpsAsInteger); + void setFrameTimerLimits(uint64_t limitForClientResourcesUpload, uint64_t limitForOffscreenBufferRender); + void setSkippingOfUnmodifiedBuffers(bool enabled); + + void setSceneDisplayAndBuffer(sceneId_t sceneId, displayId_t display, displayBufferId_t displayBuffer = {}); + void setSceneState(sceneId_t sceneId, RendererSceneState state); + void waitForSceneState(ramses::Scene& scene, RendererSceneState state); + void linkOffscreenBufferToSceneTexture(sceneId_t sceneId, displayBufferId_t offscreenBuffer, dataConsumerId_t consumerTexture); + + void waitForFlush(sceneId_t sceneId, sceneVersionTag_t flushVersion); + void consumePendingEvents(); + + private: + RamsesFramework& m_framework; + RamsesRenderer& m_renderer; + RendererSceneControl& m_sceneControlAPI; + RendererAndSceneTestEventHandler m_eventHandler; + }; +} diff --git a/integration/StressTests/ResourceStressTests/src/main.cpp b/tests/integration/resource-stress-tests/main.cpp similarity index 93% rename from integration/StressTests/ResourceStressTests/src/main.cpp rename to tests/integration/resource-stress-tests/main.cpp index 29a99b764..fbed2aa49 100644 --- a/integration/StressTests/ResourceStressTests/src/main.cpp +++ b/tests/integration/resource-stress-tests/main.cpp @@ -9,7 +9,7 @@ #include "ResourceStressTests.h" #include "ramses-cli.h" -using namespace ramses_internal; +using namespace ramses::internal; int main(int argc, const char *argv[]) { @@ -51,8 +51,5 @@ int main(int argc, const char *argv[]) { return ResourceStressTests::RunAllTests(testConfig); } - else - { - return ResourceStressTests::RunTest(static_cast(testNumber), testConfig); - } + return ResourceStressTests::RunTest(static_cast(testNumber), testConfig); } diff --git a/integration/SmokeTests/CMakeLists.txt b/tests/integration/smoke-tests/CMakeLists.txt similarity index 100% rename from integration/SmokeTests/CMakeLists.txt rename to tests/integration/smoke-tests/CMakeLists.txt diff --git a/integration/SmokeTests/ramses-local-client-test/CMakeLists.txt b/tests/integration/smoke-tests/ramses-local-client-test/CMakeLists.txt similarity index 85% rename from integration/SmokeTests/ramses-local-client-test/CMakeLists.txt rename to tests/integration/smoke-tests/ramses-local-client-test/CMakeLists.txt index 51db5d028..da0a15f4c 100644 --- a/integration/SmokeTests/ramses-local-client-test/CMakeLists.txt +++ b/tests/integration/smoke-tests/ramses-local-client-test/CMakeLists.txt @@ -10,8 +10,7 @@ createModuleWithRenderer( NAME ramses-local-client-test TYPE BINARY ENABLE_INSTALL ON - SRC_FILES src/* - RESOURCE_FOLDERS ../../TestContent/res - DEPENDENCIES TestContent + SRC_FILES *.cpp + DEPENDENCIES test-content ramses-cli ) diff --git a/integration/SmokeTests/ramses-local-client-test/src/main.cpp b/tests/integration/smoke-tests/ramses-local-client-test/main.cpp similarity index 66% rename from integration/SmokeTests/ramses-local-client-test/src/main.cpp rename to tests/integration/smoke-tests/ramses-local-client-test/main.cpp index bce2dbd4c..b966403ec 100644 --- a/integration/SmokeTests/ramses-local-client-test/src/main.cpp +++ b/tests/integration/smoke-tests/ramses-local-client-test/main.cpp @@ -5,127 +5,129 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-framework-api/RamsesFramework.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/framework/RamsesFramework.h" #include "TestScenes/TransformationLinkScene.h" #include "TestScenes/FileLoadingScene.h" #include "TestScenes/StreamTextureScene.h" -#include "RamsesFrameworkImpl.h" +#include "impl/RamsesFrameworkImpl.h" #include "TestStepCommand.h" -#include "RendererMate.h" -#include "Ramsh/Ramsh.h" +#include "impl/RendererMate.h" +#include "internal/Ramsh/Ramsh.h" #include "ramses-cli.h" namespace { void runTranformationLinkingTest(ramses::RamsesClient& client, ramses::RamsesRenderer& renderer, - ramses::RendererMate& rendererMate, - ramses::RendererMateAutoShowHandler dmEventHandler, + ramses::internal::RendererMate& rendererMate, + ramses::internal::RendererMateAutoShowHandler& dmEventHandler, uint32_t displayWidth, uint32_t displayHeight, uint32_t testNr) { // host scene contains provider nodes const ramses::sceneId_t hostSceneId(12u); - ramses::Scene* hostScene = client.createScene(hostSceneId); - ramses_internal::TransformationLinkScene transformationProviderScene(*hostScene, ramses_internal::TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT, { 0.f, 0.f, 0.f }, displayWidth, displayHeight); + const ramses::SceneConfig config{hostSceneId, ramses::EScenePublicationMode::LocalAndRemote}; + ramses::Scene* hostScene = client.createScene(config); + ramses::internal::TransformationLinkScene transformationProviderScene(*hostScene, ramses::internal::TransformationLinkScene::TRANSFORMATION_PROVIDER_WITHOUT_CONTENT, { 0.f, 0.f, 0.f }, displayWidth, displayHeight); hostScene->flush(); hostScene->publish(ramses::EScenePublicationMode::LocalOnly); //client scene - const ramses::sceneId_t localSceneId(42u); - ramses::Scene* clientScene = client.createScene(localSceneId); - ramses_internal::TransformationLinkScene redTriangleScene(*clientScene, ramses_internal::TransformationLinkScene::TRANSFORMATION_CONSUMER, { 0.f, 0.f, 12.f }, displayWidth, displayHeight); + const ramses::SceneConfig localSceneConfig(ramses::sceneId_t(42u), ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene* clientScene = client.createScene(localSceneConfig); + ramses::internal::TransformationLinkScene redTriangleScene(*clientScene, ramses::internal::TransformationLinkScene::TRANSFORMATION_CONSUMER, { 0.f, 0.f, 12.f }, displayWidth, displayHeight); clientScene->flush(); clientScene->publish(ramses::EScenePublicationMode::LocalOnly); - const ramses::sceneId_t remoteSceneId(67u); + const ramses::SceneConfig remoteSceneConfig(ramses::sceneId_t{67u}, ramses::EScenePublicationMode::LocalAndRemote); if (testNr == 1) { // 'sender' has two scenes, one locally shown, and one distributed remotely - printf("sender\n"); + fmt::print("sender\n"); // scene to distribute only - ramses::Scene* remoteScene = client.createScene(remoteSceneId); - ramses_internal::TransformationLinkScene blueTriangleScene(*remoteScene, ramses_internal::TransformationLinkScene::TRANSFORMATION_CONSUMER_OVERRIDEN, { 0.f, 0.f, 12.f }, displayWidth, displayHeight); + ramses::Scene* remoteScene = client.createScene(remoteSceneConfig); + ramses::internal::TransformationLinkScene blueTriangleScene(*remoteScene, ramses::internal::TransformationLinkScene::TRANSFORMATION_CONSUMER_OVERRIDEN, { 0.f, 0.f, 12.f }, displayWidth, displayHeight); remoteScene->flush(); - remoteScene->publish(); + remoteScene->publish(ramses::EScenePublicationMode::LocalAndRemote); } else //if (testNrArgument == 2) { // 'receiver': has one local scene, and maps a remote scene additionally - printf("receiver mode\n"); + fmt::print("receiver mode\n"); } while (rendererMate.isRunning()) { rendererMate.dispatchAndFlush(dmEventHandler); renderer.doOneLoop(); - ramses_internal::PlatformThread::Sleep(16); + ramses::internal::PlatformThread::Sleep(16); } } void runFileLoadingTest(ramses::RamsesClient& client, ramses::RamsesRenderer& renderer, ramses::RamsesFrameworkConfig& frameworkConfig, - ramses::RendererMate& rendererMate, - ramses::RendererMateAutoShowHandler dmEventHandler, + ramses::internal::RendererMate& rendererMate, + ramses::internal::RendererMateAutoShowHandler& dmEventHandler, uint32_t displayWidth, uint32_t displayHeight) { ramses::sceneId_t sceneId(13u); - ramses_internal::FileLoadingScene fileLoadingScene(client, ramses_internal::FileLoadingScene::CREATE_SAVE_DESTROY_LOAD_USING_SAME_CLIENT, sceneId, { 0.f, 0.f, 5.f }, ".", frameworkConfig, displayWidth, displayHeight); + ramses::internal::FileLoadingScene fileLoadingScene(client, ramses::internal::FileLoadingScene::CREATE_SAVE_DESTROY_LOAD_USING_SAME_CLIENT, sceneId, { 0.f, 0.f, 5.f }, ".", frameworkConfig, displayWidth, displayHeight); ramses::Scene* scene = fileLoadingScene.getCreatedScene(); - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); while (rendererMate.getLastReportedSceneState(sceneId) != ramses::RendererSceneState::Rendered) { renderer.doOneLoop(); rendererMate.dispatchAndFlush(dmEventHandler); - ramses_internal::PlatformThread::Sleep(16); + ramses::internal::PlatformThread::Sleep(16); } } void runNoBlackFrameTest(ramses::RamsesClient& client, ramses::RamsesRenderer& renderer, ramses::RamsesFramework& framework, - ramses::RendererMate& rendererMate, - ramses::RendererMateAutoShowHandler dmEventHandler, + ramses::internal::RendererMate& rendererMate, + ramses::internal::RendererMateAutoShowHandler& dmEventHandler, uint32_t displayWidth, uint32_t displayHeight) { renderer.setLoopMode(ramses::ELoopMode::UpdateOnly); - auto testStepCommand = std::make_shared(); - framework.m_impl.getRamsh().add(testStepCommand); + auto testStepCommand = std::make_shared(); + framework.impl().getRamsh().add(testStepCommand); const ramses::sceneId_t sceneId(1u); - ramses::Scene& clientScene = *client.createScene(sceneId); - ramses_internal::TransformationLinkScene redTriangleScene(clientScene, ramses_internal::TransformationLinkScene::TRANSFORMATION_CONSUMER, { 0.f, 0.f, 4.f }, displayWidth, displayHeight); + const ramses::SceneConfig config(sceneId, ramses::EScenePublicationMode::LocalAndRemote); + ramses::Scene& clientScene = *client.createScene(config); + ramses::internal::TransformationLinkScene redTriangleScene(clientScene, ramses::internal::TransformationLinkScene::TRANSFORMATION_CONSUMER, { 0.f, 0.f, 4.f }, displayWidth, displayHeight); - clientScene.publish(); + clientScene.publish(ramses::EScenePublicationMode::LocalAndRemote); clientScene.flush(); while (rendererMate.getLastReportedSceneState(sceneId) != ramses::RendererSceneState::Rendered) { renderer.doOneLoop(); rendererMate.dispatchAndFlush(dmEventHandler); - ramses_internal::PlatformThread::Sleep(16); + ramses::internal::PlatformThread::Sleep(16); } - LOG_DEBUG(ramses_internal::CONTEXT_SMOKETEST, "Surface should be still invisible"); + LOG_DEBUG(ramses::internal::CONTEXT_SMOKETEST, "Surface should be still invisible"); // The integration test "test_run_no_initial_black_frame.py" checks here, if the surface is not yet // visible on the screen by doing a screenshot from the system compositor. It then sends "step 1" to // proceed with the test. while (testStepCommand->getCurrentTestStep() != 1) { renderer.doOneLoop(); - ramses_internal::PlatformThread::Sleep(50); + ramses::internal::PlatformThread::Sleep(50); } // Do only render a single frame, this makes the surface to become visible. @@ -142,30 +144,31 @@ namespace while (true) { renderer.doOneLoop(); - ramses_internal::PlatformThread::Sleep(50); + ramses::internal::PlatformThread::Sleep(50); } } void runEmbeddedCompositingTest(ramses::RamsesClient& client, ramses::RamsesRenderer& renderer, ramses::displayId_t displayId, - ramses::RendererMate& rendererMate, - ramses::RendererMateAutoShowHandler dmEventHandler, + ramses::internal::RendererMate& rendererMate, + ramses::internal::RendererMateAutoShowHandler& dmEventHandler, uint32_t displayWidth, uint32_t displayHeight, uint32_t testState) { constexpr ramses::sceneId_t sceneId(34u); - ramses::Scene& scene = *client.createScene(sceneId); - ramses_internal::StreamTextureScene streamTextureScene(scene, testState, { 0.f, 0.f, 5.f }, displayWidth, displayHeight); + ramses::SceneConfig config{sceneId, ramses::EScenePublicationMode::LocalAndRemote}; + ramses::Scene& scene = *client.createScene(config); + ramses::internal::StreamTextureScene streamTextureScene(scene, testState, { 0.f, 0.f, 5.f }, displayWidth, displayHeight); scene.flush(); - scene.publish(); + scene.publish(ramses::EScenePublicationMode::LocalAndRemote); while (rendererMate.getLastReportedSceneState(sceneId) != ramses::RendererSceneState::Rendered) { renderer.doOneLoop(); rendererMate.dispatchAndFlush(dmEventHandler); - ramses_internal::PlatformThread::Sleep(16); + ramses::internal::PlatformThread::Sleep(16); } std::array streamBuffers; @@ -176,11 +179,11 @@ namespace streamBuffers[4] = renderer.createStreamBuffer(displayId, ramses::waylandIviSurfaceId_t{ 5u }); streamBuffers[5] = renderer.createStreamBuffer(displayId, ramses::waylandIviSurfaceId_t{ 6u }); - const auto linkingIndices = ramses_internal::StreamTextureScene::getConsumersLinkingIndices(testState); + const auto linkingIndices = ramses::internal::StreamTextureScene::getConsumersLinkingIndices(testState); for(uint32_t idx = 0u; idx < 6u; ++idx) { const auto streamBuffer = streamBuffers[linkingIndices[idx]]; - const auto textureConsumer = ramses_internal::StreamTextureScene::TextureConsumers[idx]; + const auto textureConsumer = ramses::internal::StreamTextureScene::TextureConsumers[idx]; renderer.getSceneControlAPI()->linkStreamBuffer(streamBuffer, sceneId, textureConsumer); } @@ -191,7 +194,7 @@ namespace { renderer.doOneLoop(); rendererMate.dispatchAndFlush(dmEventHandler); - ramses_internal::PlatformThread::Sleep(16); + ramses::internal::PlatformThread::Sleep(16); } } } @@ -246,8 +249,8 @@ int main(int argc, const char* argv[]) uint32_t displayHeight = 0; displayConfig.getWindowRectangle(x, x, displayWidth, displayHeight); - ramses::RendererMate rendererMate(renderer.m_impl, framework.m_impl); - ramses::RendererMateAutoShowHandler dmEventHandler(rendererMate, !disableAutoMapping); + ramses::internal::RendererMate rendererMate(renderer.impl(), framework.impl()); + ramses::internal::RendererMateAutoShowHandler dmEventHandler(rendererMate, !disableAutoMapping); switch(testNr) { diff --git a/integration/SmokeTests/ramses-test-client/CMakeLists.txt b/tests/integration/smoke-tests/ramses-test-client/CMakeLists.txt similarity index 80% rename from integration/SmokeTests/ramses-test-client/CMakeLists.txt rename to tests/integration/smoke-tests/ramses-test-client/CMakeLists.txt index 30a8e8b27..5c43a96f9 100644 --- a/integration/SmokeTests/ramses-test-client/CMakeLists.txt +++ b/tests/integration/smoke-tests/ramses-test-client/CMakeLists.txt @@ -10,10 +10,9 @@ createModule( NAME ramses-test-client TYPE BINARY ENABLE_INSTALL ON - SRC_FILES include/*.h - src/*.cpp - RESOURCE_FOLDERS ../../TestContent/res + SRC_FILES *.h + *.cpp DEPENDENCIES ramses-framework - TestContent + test-content ramses-cli ) diff --git a/integration/SmokeTests/ramses-test-client/src/main.cpp b/tests/integration/smoke-tests/ramses-test-client/main.cpp similarity index 62% rename from integration/SmokeTests/ramses-test-client/src/main.cpp rename to tests/integration/smoke-tests/ramses-test-client/main.cpp index a85e450d5..8270917e7 100644 --- a/integration/SmokeTests/ramses-test-client/src/main.cpp +++ b/tests/integration/smoke-tests/ramses-test-client/main.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-client-api/Scene.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/Scene.h" #include "TestScenes/HierarchicalRedTrianglesScene.h" #include "TestScenes/MultipleTrianglesScene.h" #include "TestScenes/TextScene.h" @@ -16,19 +16,20 @@ #include "TestScenes/MultiTypeLinkScene.h" #include "TestScenes/MultipleRenderTargetScene.h" #include "TestScenes/SceneFromPath.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "RamsesFrameworkImpl.h" -#include "Ramsh/RamshCommandExit.h" +#include "ramses/framework/RamsesFramework.h" +#include "impl/RamsesFrameworkImpl.h" +#include "internal/Ramsh/RamshCommandExit.h" #include "TestStepCommand.h" -#include "Ramsh/Ramsh.h" +#include "internal/Ramsh/Ramsh.h" #include "ramses-cli.h" using SceneVector = std::vector; -using IntegrationScenePtr = std::unique_ptr; +using IntegrationScenePtr = std::unique_ptr; ramses::Scene& addAndReturnScene(ramses::RamsesClient& ramses, SceneVector& scenes, ramses::sceneId_t sceneId) { - ramses::Scene* scene = ramses.createScene(sceneId); + const ramses::SceneConfig config{sceneId, ramses::EScenePublicationMode::LocalAndRemote}; + ramses::Scene* scene = ramses.createScene(config); scenes.push_back(scene); @@ -39,7 +40,7 @@ void publishAllScenes(SceneVector& scenes) { for(auto scene : scenes) { - scene->publish(); + scene->publish(ramses::EScenePublicationMode::LocalAndRemote); } } @@ -112,10 +113,10 @@ int main(int argc, const char* argv[]) CLI11_PARSE(cli, argc, argv); ramses::RamsesFramework framework(frameworkConfig); - auto commandExit = std::make_shared(); - framework.m_impl.getRamsh().add(commandExit); - auto testStepCommand = std::make_shared(); - framework.m_impl.getRamsh().add(testStepCommand); + auto commandExit = std::make_shared(); + framework.impl().getRamsh().add(commandExit); + auto testStepCommand = std::make_shared(); + framework.impl().getRamsh().add(testStepCommand); ramses::RamsesClient* ramses(framework.createClient("ramses test client")); if (!ramses) @@ -137,9 +138,9 @@ int main(int argc, const char* argv[]) { case 2: { - auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, ramses::sceneId_t(21u), + auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, ramses::sceneId_t(21u), cameraPosParam + glm::vec3(2.0f, 0.0f, 0.0f)); - auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED, ramses::sceneId_t(22u), + auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleTrianglesScene::TRIANGLES_REORDERED, ramses::sceneId_t(22u), cameraPosParam + glm::vec3(-2.0f, 0.0f, 0.0f)); integrationScenes.push_back(std::move(integrationScene1)); integrationScenes.push_back(std::move(integrationScene2)); @@ -147,43 +148,43 @@ int main(int argc, const char* argv[]) } case 3: { - auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, ramses::sceneId_t(23u), + auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, ramses::sceneId_t(23u), cameraPosParam + glm::vec3(2.0f, 0.0f, 0.0f)); - auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED, ramses::sceneId_t(24u), + auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleTrianglesScene::TRIANGLES_REORDERED, ramses::sceneId_t(24u), cameraPosParam + glm::vec3(-2.0f, 0.0f, 0.0f)); integrationScenes.push_back(std::move(integrationScene1)); integrationScenes.push_back(std::move(integrationScene2)); ramses::Scene* sceneToBeDestroyed = scenes.front(); assert(framework.isConnected()); - sceneToBeDestroyed->publish(); + sceneToBeDestroyed->publish(ramses::EScenePublicationMode::LocalAndRemote); ramses->destroy(*sceneToBeDestroyed); scenes.erase(scenes.begin()); break; } case 4: { - auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(25u), cameraPosParam); + auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(25u), cameraPosParam); integrationScenes.push_back(std::move(integrationScene)); break; } case 5: { - auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(26u), cameraPosParam); + auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(26u), cameraPosParam); integrationScenes.push_back(std::move(integrationScene)); break; } case 7: { - auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleTrianglesScene::TRIANGLES_REORDERED, ramses::sceneId_t(29u), + auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleTrianglesScene::TRIANGLES_REORDERED, ramses::sceneId_t(29u), cameraPosParam + glm::vec3(30.0f, 0.0f, 0.0f)); - auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleTrianglesScene::THREE_TRIANGLES, ramses::sceneId_t(30u), + auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleTrianglesScene::THREE_TRIANGLES, ramses::sceneId_t(30u), cameraPosParam + glm::vec3(10.0f, 0.0f, 0.0f)); - auto integrationScene3 = createSceneAndSetState(*ramses, scenes, ramses_internal::HierarchicalRedTrianglesScene::THREE_ROWS_TRIANGLES, ramses::sceneId_t(31u), + auto integrationScene3 = createSceneAndSetState(*ramses, scenes, ramses::internal::HierarchicalRedTrianglesScene::THREE_ROWS_TRIANGLES, ramses::sceneId_t(31u), cameraPosParam + glm::vec3(-10.0f, 0.0f, 0.0f)); - auto integrationScene4 = createSceneAndSetState(*ramses, scenes, ramses_internal::HierarchicalRedTrianglesScene::THREE_ROWS_TRIANGLES, ramses::sceneId_t(32u), + auto integrationScene4 = createSceneAndSetState(*ramses, scenes, ramses::internal::HierarchicalRedTrianglesScene::THREE_ROWS_TRIANGLES, ramses::sceneId_t(32u), cameraPosParam + glm::vec3(-30.0f, 0.0f, 0.0f)); integrationScenes.push_back(std::move(integrationScene1)); @@ -195,49 +196,49 @@ int main(int argc, const char* argv[]) case 13: { const ramses::sceneId_t sceneId(37u); - ramses_internal::FileLoadingScene fileLoadingScene(*ramses, testState, sceneId, cameraPosParam, folder, frameworkConfig, DefaultViewportWidth, DefaultViewportHeight); + ramses::internal::FileLoadingScene fileLoadingScene(*ramses, testState, sceneId, cameraPosParam, folder, frameworkConfig, DefaultViewportWidth, DefaultViewportHeight); scenes.push_back(fileLoadingScene.getCreatedScene()); break; } case 15: { - auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultiTypeLinkScene::TRANSFORMATION_CONSUMER_DATA_AND_TEXTURE_PROVIDER, ramses::sceneId_t(39u), cameraPosParam + glm::vec3(1.f, -2.f, 0.f)); - auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses_internal::MultiTypeLinkScene::TRANSFORMATION_PROVIDER_DATA_AND_TEXTURE_CONSUMER, ramses::sceneId_t(40u), cameraPosParam + glm::vec3(-1.f, 2.f, 0.f)); + auto integrationScene1 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultiTypeLinkScene::TRANSFORMATION_CONSUMER_DATA_AND_TEXTURE_PROVIDER, ramses::sceneId_t(39u), cameraPosParam + glm::vec3(1.f, -2.f, 0.f)); + auto integrationScene2 = createSceneAndSetState(*ramses, scenes, ramses::internal::MultiTypeLinkScene::TRANSFORMATION_PROVIDER_DATA_AND_TEXTURE_CONSUMER, ramses::sceneId_t(40u), cameraPosParam + glm::vec3(-1.f, 2.f, 0.f)); integrationScenes.push_back(std::move(integrationScene1)); integrationScenes.push_back(std::move(integrationScene2)); break; } case 16: { - auto integrationScene = createSceneAndSetState(*ramses, scenes, ramses_internal::MultipleRenderTargetScene::TWO_COLOR_BUFFERS, ramses::sceneId_t(41u), cameraPosParam); + auto integrationScene = createSceneAndSetState(*ramses, scenes, ramses::internal::MultipleRenderTargetScene::TWO_COLOR_BUFFERS, ramses::sceneId_t(41u), cameraPosParam); integrationScenes.push_back(std::move(integrationScene)); break; } #if defined(RAMSES_TEXT_ENABLED) case 18: { - auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(43u), cameraPosParam); + auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(43u), cameraPosParam); integrationScenes.push_back(std::move(integrationScene)); break; } #endif case 19: { - ramses_internal::SceneFromPath sceneFromPath(*ramses, folder, filename); + ramses::internal::SceneFromPath sceneFromPath(*ramses, folder, filename); scenes.push_back(sceneFromPath.getCreatedScene()); break; } case 20: { // for IVI layer test with custom resolution - auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(25u), cameraPosParam, 640u, 480u); + auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(25u), cameraPosParam, 640u, 480u); integrationScenes.push_back(std::move(integrationScene)); break; } case 21: { // for IVI layer test with custom resolution - auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(26u), cameraPosParam, 640u, 480u); + auto integrationScene = createSceneAndSetState(*ramses, scenes, testState, ramses::sceneId_t(26u), cameraPosParam, 640u, 480u); integrationScenes.push_back(std::move(integrationScene)); break; } diff --git a/integration/TestContent/src/AntiAliasingScene.cpp b/tests/integration/test-content/AntiAliasingScene.cpp similarity index 61% rename from integration/TestContent/src/AntiAliasingScene.cpp rename to tests/integration/test-content/AntiAliasingScene.cpp index 4e44de626..c54319a2f 100644 --- a/integration/TestContent/src/AntiAliasingScene.cpp +++ b/tests/integration/test-content/AntiAliasingScene.cpp @@ -7,19 +7,19 @@ // ------------------------------------------------------------------------- #include "TestScenes/AntiAliasingScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-framework-api/DataTypes.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/framework/DataTypes.h" +#include "internal/PlatformAbstraction/PlatformMath.h" #include -namespace ramses_internal +namespace ramses::internal { AntiAliasingScene::AntiAliasingScene(ramses::Scene& scene, uint32_t /*state*/, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -30,11 +30,11 @@ namespace ramses_internal float zValue = -6.5f; - pos.push_back(ramses::vec3f{ 0.f, 0.f, zValue }); - colors.push_back(ramses::vec3f{ 1.f, 0.f, 0.f }); + pos.emplace_back(0.f, 0.f, zValue); + colors.emplace_back(1.f, 0.f, 0.f); - pos.push_back(ramses::vec3f{ 0.f, 0.f, zValue }); - colors.push_back(ramses::vec3f{ 1.f, 1.f, 1.f }); + pos.emplace_back(0.f, 0.f, zValue); + colors.emplace_back(1.f, 1.f, 1.f); const uint32_t slices = 28; const float sliceAngle = 2.f * PlatformMath::PI_f / slices; @@ -44,11 +44,11 @@ namespace ramses_internal float x = cosf(angle); float y = sinf(angle); - pos.push_back(ramses::vec3f{ x, y, zValue }); - colors.push_back(ramses::vec3f{ 1.f, 0.f, 0.f }); + pos.emplace_back(x, y, zValue); + colors.emplace_back(1.f, 0.f, 0.f); - pos.push_back(ramses::vec3f{ x, y, zValue }); - colors.push_back(ramses::vec3f{ 1.f, 1.f, 1.f }); + pos.emplace_back(x, y, zValue); + colors.emplace_back(1.f, 1.f, 1.f); if (i > 0) { @@ -65,21 +65,16 @@ namespace ramses_internal ramses::Effect* effectTex = getTestEffect("ramses-test-client-simple-color"); ramses::Appearance* appearance = m_scene.createAppearance(*effectTex, "disk appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput colorsInput; - effectTex->findAttributeInput("a_position", positionsInput); - effectTex->findAttributeInput("a_color", colorsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effectTex, "disk geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effectTex, "disk geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(colorsInput, *vertexColors); + geometry->setInputBuffer(*effectTex->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effectTex->findAttributeInput("a_color"), *vertexColors); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = m_scene.createMeshNode("disk mesh node"); addMeshNodeToDefaultRenderGroup(*meshNode); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); } } diff --git a/integration/TestContent/src/ArrayBufferScene.cpp b/tests/integration/test-content/ArrayBufferScene.cpp similarity index 77% rename from integration/TestContent/src/ArrayBufferScene.cpp rename to tests/integration/test-content/ArrayBufferScene.cpp index c718659db..4948aa9e5 100644 --- a/integration/TestContent/src/ArrayBufferScene.cpp +++ b/tests/integration/test-content/ArrayBufferScene.cpp @@ -8,18 +8,18 @@ #include "TestScenes/ArrayBufferScene.h" #include "TestScenes/Triangle.h" -#include "ramses-utils.h" - -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" -#include "SceneAPI/EDataType.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/AttributeInput.h" - -namespace ramses_internal +#include "ramses/client/ramses-utils.h" + +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/AttributeInput.h" + +namespace ramses::internal { ArrayBufferScene::ArrayBufferScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) @@ -78,9 +78,9 @@ namespace ramses_internal //}; const float val3p0 = 3.f; const float valZero = 0.f; - m_vertexDataBufferInterleaved->updateData(0u , sizeof(float), reinterpret_cast(&val3p0)); - m_vertexDataBufferInterleaved->updateData(5 * sizeof(float) , sizeof(float), reinterpret_cast(&valZero)); - m_vertexDataBufferInterleaved->updateData(8 * sizeof(float) , sizeof(float), reinterpret_cast(&val3p0)); + m_vertexDataBufferInterleaved->updateData(0u , sizeof(float), reinterpret_cast(&val3p0)); + m_vertexDataBufferInterleaved->updateData(5 * sizeof(float) , sizeof(float), reinterpret_cast(&valZero)); + m_vertexDataBufferInterleaved->updateData(8 * sizeof(float) , sizeof(float), reinterpret_cast(&val3p0)); m_scene.flush(); break; } @@ -131,7 +131,7 @@ namespace ramses_internal void ArrayBufferScene::createGeometry(ramses::Effect& effect, uint32_t state) { - m_geometry = m_scene.createGeometryBinding(effect); + m_geometry = m_scene.createGeometry(effect); switch (state) { @@ -185,7 +185,7 @@ namespace ramses_internal } - m_meshNode->setGeometryBinding(*m_geometry); + m_meshNode->setGeometry(*m_geometry); m_meshNode->setIndexCount(3); } @@ -239,13 +239,8 @@ namespace ramses_internal ramses::ArrayBuffer* dataBufferY = m_scene.createArrayBuffer(ramses::EDataType::Float, 4u); dataBufferY->updateData(0u, 4u, verticesY); - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - m_geometry->setInputBuffer(inputX, *dataBufferX); - - ramses::AttributeInput inputY; - m_effect.findAttributeInput("a_positionY", inputY); - m_geometry->setInputBuffer(inputY, *dataBufferY); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *dataBufferX); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionY"), *dataBufferY); } void ArrayBufferScene::createVertexDataBufferVector2F() @@ -260,10 +255,7 @@ namespace ramses_internal ramses::ArrayBuffer* dataBuffer = m_scene.createArrayBuffer(ramses::EDataType::Vector2F, 8u); dataBuffer->updateData(0u, 4u, vertices.data()); - - ramses::AttributeInput input; - m_effect.findAttributeInput("a_position", input); - m_geometry->setInputBuffer(input, *dataBuffer); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_position"), *dataBuffer); } void ArrayBufferScene::createVertexDataBufferVector3F() @@ -279,9 +271,7 @@ namespace ramses_internal ramses::ArrayBuffer* dataBuffer = m_scene.createArrayBuffer(ramses::EDataType::Vector3F, 12u); dataBuffer->updateData(0u, 4u, vertices.data()); - ramses::AttributeInput input; - m_effect.findAttributeInput("a_position", input); - m_geometry->setInputBuffer(input, *dataBuffer); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_position"), *dataBuffer); } void ArrayBufferScene::createVertexDataBufferVector4F() @@ -296,10 +286,7 @@ namespace ramses_internal ramses::ArrayBuffer* dataBuffer = m_scene.createArrayBuffer(ramses::EDataType::Vector4F, 16u); dataBuffer->updateData(0u, 4u, vertices.data()); - - ramses::AttributeInput input; - m_effect.findAttributeInput("a_position", input); - m_geometry->setInputBuffer(input, *dataBuffer); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_position"), *dataBuffer); m_vertexDataBufferVec4 = dataBuffer; } @@ -314,10 +301,7 @@ namespace ramses_internal }; const ramses::ArrayResource* arrayBuffer = m_scene.createArrayResource(3u, vertices.data()); - - ramses::AttributeInput input; - m_effect.findAttributeInput("a_position", input); - m_geometry->setInputBuffer(input, *arrayBuffer); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_position"), *arrayBuffer); } void ArrayBufferScene::createVertexArrayBufferInterleaved() @@ -330,18 +314,12 @@ namespace ramses_internal }; ramses::ArrayBuffer* arrayBuffer = m_scene.createArrayBuffer(ramses::EDataType::ByteBlob, sizeof(vertices)); - arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); + arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t stride = 4 * sizeof(float); - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - ramses::AttributeInput inputYZ; - m_effect.findAttributeInput("a_positionYZ", inputYZ); - ramses::AttributeInput inputW; - m_effect.findAttributeInput("a_positionW", inputW); - m_geometry->setInputBuffer(inputX , *arrayBuffer, 0u , stride); - m_geometry->setInputBuffer(inputYZ , *arrayBuffer, sizeof(float) , stride); - m_geometry->setInputBuffer(inputW , *arrayBuffer, 3 * sizeof(float) , stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *arrayBuffer, 0u, stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionYZ"), *arrayBuffer, sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionW"), *arrayBuffer, 3 * sizeof(float), stride); m_vertexDataBufferInterleaved = arrayBuffer; } @@ -363,20 +341,13 @@ namespace ramses_internal }; ramses::ArrayBuffer* arrayBuffer = m_scene.createArrayBuffer(ramses::EDataType::ByteBlob, sizeof(vertices)); - arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); + arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t strideXAndW = 8 * sizeof(float); constexpr uint16_t strideYZ = 4 * sizeof(float); - - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - ramses::AttributeInput inputYZ; - m_effect.findAttributeInput("a_positionYZ", inputYZ); - ramses::AttributeInput inputW; - m_effect.findAttributeInput("a_positionW", inputW); - m_geometry->setInputBuffer(inputX, *arrayBuffer, 0u , strideXAndW); - m_geometry->setInputBuffer(inputYZ, *arrayBuffer, sizeof(float) , strideYZ); - m_geometry->setInputBuffer(inputW, *arrayBuffer, 7 * sizeof(float) , strideXAndW); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *arrayBuffer, 0u, strideXAndW); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionYZ"), *arrayBuffer, sizeof(float), strideYZ); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionW"), *arrayBuffer, 7 * sizeof(float), strideXAndW); } void ArrayBufferScene::createVertexArrayBufferInterleavedSingleAttrib() @@ -389,12 +360,10 @@ namespace ramses_internal }; ramses::ArrayBuffer* arrayBuffer = m_scene.createArrayBuffer(ramses::EDataType::ByteBlob, sizeof(vertices)); - arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); + arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t stride = 4 * sizeof(float); - ramses::AttributeInput input; - m_effect.findAttributeInput("a_position", input); - m_geometry->setInputBuffer(input, *arrayBuffer, sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_position"), *arrayBuffer, sizeof(float), stride); m_vertexDataBufferInterleaved = arrayBuffer; } @@ -410,18 +379,12 @@ namespace ramses_internal }; ramses::ArrayBuffer* arrayBuffer = m_scene.createArrayBuffer(ramses::EDataType::ByteBlob, sizeof(vertices)); - arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); + arrayBuffer->updateData(0u, sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t stride = 4 * sizeof(float); - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - ramses::AttributeInput inputYZ; - m_effect.findAttributeInput("a_positionYZ", inputYZ); - ramses::AttributeInput inputW; - m_effect.findAttributeInput("a_positionW", inputW); - m_geometry->setInputBuffer(inputX, *arrayBuffer, 0u, stride); - m_geometry->setInputBuffer(inputYZ, *arrayBuffer, sizeof(float), stride); - m_geometry->setInputBuffer(inputW, *arrayBuffer, 3 * sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *arrayBuffer, 0u, stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionYZ"), *arrayBuffer, sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionW"), *arrayBuffer, 3 * sizeof(float), stride); m_vertexDataBufferInterleaved = arrayBuffer; } diff --git a/integration/TestContent/src/ArrayInputScene.cpp b/tests/integration/test-content/ArrayInputScene.cpp similarity index 63% rename from integration/TestContent/src/ArrayInputScene.cpp rename to tests/integration/test-content/ArrayInputScene.cpp index dc42a8d94..95c7e0575 100644 --- a/integration/TestContent/src/ArrayInputScene.cpp +++ b/tests/integration/test-content/ArrayInputScene.cpp @@ -8,31 +8,22 @@ #include "TestScenes/ArrayInputScene.h" #include "TestScenes/Triangle.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" -namespace ramses_internal +namespace ramses::internal { ArrayInputScene::ArrayInputScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) { - ramses::UniformInput caseNumber; - ramses::UniformInput colorVec4Array; - ramses::UniformInput colorIntArray; - ramses::UniformInput index; - ramses::Effect* effect = getTestEffect("ramses-test-client-arrays"); - effect->findUniformInput("caseNumber", caseNumber); - effect->findUniformInput("colorVec4Array", colorVec4Array); - effect->findUniformInput("colorIntArray", colorIntArray); - effect->findUniformInput("index", index); - ramses::TriangleGeometry triangleGeometry(m_scene, *effect); + TriangleGeometry triangleGeometry(m_scene, *effect); ramses::Appearance& appearance = *scene.createAppearance(*effect, "appearance"); ramses::Node* trafoNode = m_scene.createNode("transformation node"); @@ -41,10 +32,10 @@ namespace ramses_internal ramses::MeshNode* meshNode = m_scene.createMeshNode("triangle mesh node"); meshNode->setParent(*trafoNode); meshNode->setAppearance(appearance); - meshNode->setGeometryBinding(triangleGeometry.GetGeometry()); + meshNode->setGeometry(triangleGeometry.GetGeometry()); addMeshNodeToDefaultRenderGroup(*meshNode); - appearance.setInputValue(caseNumber, static_cast(state)); + appearance.setInputValue(*effect->findUniformInput("caseNumber"), static_cast(state)); switch (state) { @@ -55,22 +46,24 @@ namespace ramses_internal ramses::vec4f{ 0.0f, 1.0f, 0.0f, 0.0f }, ramses::vec4f{ 0.0f, 0.0f, 1.0f, 0.0f } }; - appearance.setInputValue(colorVec4Array, 3, floatValues.data()); + appearance.setInputValue(*effect->findUniformInput("colorVec4Array"), 3, floatValues.data()); break; } case ARRAY_INPUT_INT32: { const std::array intValues = { 0, 1, 0 }; - appearance.setInputValue(colorIntArray, 3, intValues.data()); + appearance.setInputValue(*effect->findUniformInput("colorIntArray"), 3, intValues.data()); break; } case ARRAY_INPUT_INT32_DYNAMIC_INDEX: { - appearance.setInputValue(index, 1); + appearance.setInputValue(*effect->findUniformInput("index"), 1); const std::array intValues = { 0, 1, 0 }; - appearance.setInputValue(colorIntArray, 3, intValues.data()); + appearance.setInputValue(*effect->findUniformInput("colorIntArray"), 3, intValues.data()); break; } + default: + break; } } } diff --git a/integration/TestContent/src/ArrayResourceScene.cpp b/tests/integration/test-content/ArrayResourceScene.cpp similarity index 65% rename from integration/TestContent/src/ArrayResourceScene.cpp rename to tests/integration/test-content/ArrayResourceScene.cpp index 2dd2a4350..1855b8008 100644 --- a/integration/TestContent/src/ArrayResourceScene.cpp +++ b/tests/integration/test-content/ArrayResourceScene.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "TestScenes/ArrayResourceScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/AttributeInput.h" - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/AttributeInput.h" + +namespace ramses::internal { ArrayResourceScene::ArrayResourceScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -49,7 +49,7 @@ namespace ramses_internal void ArrayResourceScene::createGeometry(ramses::Effect& effect, uint32_t state) { - m_geometry = m_scene.createGeometryBinding(effect); + m_geometry = m_scene.createGeometry(effect); static const uint32_t indicesData[] = { 0, 1, 2 }; auto dataBuffer = m_scene.createArrayResource(3u, indicesData); @@ -74,7 +74,7 @@ namespace ramses_internal assert(false); } - m_meshNode->setGeometryBinding(*m_geometry); + m_meshNode->setGeometry(*m_geometry); m_meshNode->setIndexCount(3); } @@ -87,18 +87,12 @@ namespace ramses_internal 3.f, -3.f, 0.f, 1.0f, }; - auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); + auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t stride = 4 * sizeof(float); - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - ramses::AttributeInput inputYZ; - m_effect.findAttributeInput("a_positionYZ", inputYZ); - ramses::AttributeInput inputW; - m_effect.findAttributeInput("a_positionW", inputW); - m_geometry->setInputBuffer(inputX, *arrayResource, 0u, stride); - m_geometry->setInputBuffer(inputYZ, *arrayResource, sizeof(float), stride); - m_geometry->setInputBuffer(inputW, *arrayResource, 3 * sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *arrayResource, 0u, stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionYZ"), *arrayResource, sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionW"), *arrayResource, 3 * sizeof(float), stride); } static constexpr float UNUSEDVALUE = std::numeric_limits::max(); @@ -117,20 +111,13 @@ namespace ramses_internal UNUSEDVALUE , UNUSEDVALUE , UNUSEDVALUE , 1.0f //_ , _ _ , w2 }; - auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); + auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t strideXAndW = 8 * sizeof(float); constexpr uint16_t strideYZ = 4 * sizeof(float); - - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - ramses::AttributeInput inputYZ; - m_effect.findAttributeInput("a_positionYZ", inputYZ); - ramses::AttributeInput inputW; - m_effect.findAttributeInput("a_positionW", inputW); - m_geometry->setInputBuffer(inputX, *arrayResource, 0u, strideXAndW); - m_geometry->setInputBuffer(inputYZ, *arrayResource, sizeof(float), strideYZ); - m_geometry->setInputBuffer(inputW, *arrayResource, 7 * sizeof(float), strideXAndW); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *arrayResource, 0u, strideXAndW); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionYZ"), *arrayResource, sizeof(float), strideYZ); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionW"), *arrayResource, 7 * sizeof(float), strideXAndW); } void ArrayResourceScene::createVertexArrayInterleavedSingleAttrib() @@ -142,12 +129,10 @@ namespace ramses_internal UNUSEDVALUE, 3.f, -3.f, 0.f, }; - auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); + auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t stride = 4 * sizeof(float); - ramses::AttributeInput input; - m_effect.findAttributeInput("a_position", input); - m_geometry->setInputBuffer(input, *arrayResource, sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_position"), *arrayResource, sizeof(float), stride); } void ArrayResourceScene::createVertexArrayInterleavedStartVertex() @@ -160,17 +145,11 @@ namespace ramses_internal 3.f, -3.f, 0.f, 1.0f, }; - auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); + auto arrayResource = m_scene.createArrayResource(sizeof(vertices), reinterpret_cast(vertices)); constexpr uint16_t stride = 4 * sizeof(float); - ramses::AttributeInput inputX; - m_effect.findAttributeInput("a_positionX", inputX); - ramses::AttributeInput inputYZ; - m_effect.findAttributeInput("a_positionYZ", inputYZ); - ramses::AttributeInput inputW; - m_effect.findAttributeInput("a_positionW", inputW); - m_geometry->setInputBuffer(inputX, *arrayResource, 0u, stride); - m_geometry->setInputBuffer(inputYZ, *arrayResource, sizeof(float), stride); - m_geometry->setInputBuffer(inputW, *arrayResource, 3 * sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionX"), *arrayResource, 0u, stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionYZ"), *arrayResource, sizeof(float), stride); + m_geometry->setInputBuffer(*m_effect.findAttributeInput("a_positionW"), *arrayResource, 3 * sizeof(float), stride); } } diff --git a/integration/TestContent/src/BlitPassScene.cpp b/tests/integration/test-content/BlitPassScene.cpp similarity index 83% rename from integration/TestContent/src/BlitPassScene.cpp rename to tests/integration/test-content/BlitPassScene.cpp index 3c4e28460..973368121 100644 --- a/integration/TestContent/src/BlitPassScene.cpp +++ b/tests/integration/test-content/BlitPassScene.cpp @@ -7,27 +7,27 @@ // ------------------------------------------------------------------------- #include "TestScenes/BlitPassScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/BlitPass.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/BlitPass.h" #include -namespace ramses_internal +namespace ramses::internal { BlitPassScene::BlitPassScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : CommonRenderBufferTestScene(scene, cameraPosition) - , m_colorBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) - , m_depthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Depth, ramses::ERenderBufferFormat::Depth24, ramses::ERenderBufferAccessMode::WriteOnly)) - , m_depthStencilBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::DepthStencil, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::WriteOnly)) - , m_blittingColorBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) - , m_blittingDepthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Depth, ramses::ERenderBufferFormat::Depth24, ramses::ERenderBufferAccessMode::WriteOnly)) - , m_blittingDepthStencilBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::DepthStencil, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_colorBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_depthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth32, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_depthStencilBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_blittingColorBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_blittingDepthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth32, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_blittingDepthStencilBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::WriteOnly)) { initClearPass(state); initClearPassForBlittingBuffers(state); @@ -97,7 +97,7 @@ namespace ramses_internal renderPass->setRenderTarget(&renderTarget); renderPass->setClearColor({1.f, 0.f, 1.f, 0.5f}); - renderPass->setClearFlags(ramses::EClearFlags_All); + renderPass->setClearFlags(ramses::EClearFlag::All); } @@ -124,7 +124,7 @@ namespace ramses_internal ramses::RenderTarget& renderTarget = createRenderTarget(state); renderPass.setRenderTarget(&renderTarget); - renderPass.setClearFlags(ramses::EClearFlags_None); + renderPass.setClearFlags(ramses::EClearFlag::None); } void BlitPassScene::initBlittingPass(uint32_t state) @@ -164,7 +164,7 @@ namespace ramses_internal ramses::RenderTarget& renderTarget = createBlittingRenderTarget(state); renderPass->setRenderTarget(&renderTarget); renderPass->setClearColor({0.f, 0.f, 1.f, 0.5f}); - renderPass->setClearFlags(ramses::EClearFlags_All); + renderPass->setClearFlags(ramses::EClearFlag::All); } void BlitPassScene::initRenderPassFromBlittingResult(uint32_t state) @@ -194,7 +194,7 @@ namespace ramses_internal if (BLITS_DEPTH_STENCIL_BUFFER == state) { //add another mesh that is filtered by stencil - ramses::MeshNode& meshNode2 = createMesh(getEffectRenderOneBuffer(), ramses::TriangleAppearance::EColor_Green); + ramses::MeshNode& meshNode2 = createMesh(getEffectRenderOneBuffer(), TriangleAppearance::EColor_Green); meshNode2.getAppearance()->setStencilFunction(ramses::EStencilFunc::NotEqual, 0u, 0xff); ramses::Node& transNode2 = *m_scene.createNode(); @@ -213,6 +213,6 @@ namespace ramses_internal ramses::RenderTarget& renderTarget = createBlittingRenderTarget(state); renderPass.setRenderTarget(&renderTarget); - renderPass.setClearFlags(ramses::EClearFlags_None); + renderPass.setClearFlags(ramses::EClearFlag::None); } } diff --git a/integration/TestContent/CMakeLists.txt b/tests/integration/test-content/CMakeLists.txt similarity index 66% rename from integration/TestContent/CMakeLists.txt rename to tests/integration/test-content/CMakeLists.txt index e0f0ad309..459cc1c40 100644 --- a/integration/TestContent/CMakeLists.txt +++ b/tests/integration/test-content/CMakeLists.txt @@ -7,16 +7,13 @@ # ------------------------------------------------------------------------- createModule( - NAME TestContent + NAME test-content TYPE STATIC_LIBRARY ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/TestEffects/*.h - include/TestScenes/*.h - include/TestScenes/TextFake/*.h - include/*.h - src/*.cpp - src/TextFake/*.cpp + INCLUDE_PATHS . + SRC_FILES TestScenes/*.h + *.h + *.cpp RESOURCE_FOLDERS res DEPENDENCIES ramses-client ramses-framework diff --git a/integration/TestContent/src/CameraDataLinkScene.cpp b/tests/integration/test-content/CameraDataLinkScene.cpp similarity index 79% rename from integration/TestContent/src/CameraDataLinkScene.cpp rename to tests/integration/test-content/CameraDataLinkScene.cpp index 570ce79af..c9f668800 100644 --- a/integration/TestContent/src/CameraDataLinkScene.cpp +++ b/tests/integration/test-content/CameraDataLinkScene.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- #include "TestScenes/CameraDataLinkScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/ramses-utils.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include "TestScenes/Triangle.h" -namespace ramses_internal +namespace ramses::internal { CameraDataLinkScene::CameraDataLinkScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -38,8 +38,8 @@ namespace ramses_internal void CameraDataLinkScene::setUpConsumerScene() { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); - ramses::Triangle triangle1(m_scene, *effect, ramses::TriangleAppearance::EColor_Red); - ramses::Triangle triangle2(m_scene, *effect, ramses::TriangleAppearance::EColor_Green); + Triangle triangle1(m_scene, *effect, TriangleAppearance::EColor_Red); + Triangle triangle2(m_scene, *effect, TriangleAppearance::EColor_Green); ramses::MeshNode* mesh1 = m_scene.createMeshNode(); ramses::MeshNode* mesh2 = m_scene.createMeshNode(); @@ -52,8 +52,8 @@ namespace ramses_internal mesh1->setAppearance(appearance1); mesh2->setAppearance(appearance2); - mesh1->setGeometryBinding(triangle1.GetGeometry()); - mesh2->setGeometryBinding(triangle2.GetGeometry()); + mesh1->setGeometry(triangle1.GetGeometry()); + mesh2->setGeometry(triangle2.GetGeometry()); addMeshNodeToDefaultRenderGroup(*mesh1); addMeshNodeToDefaultRenderGroup(*mesh2); @@ -65,13 +65,13 @@ namespace ramses_internal auto camera = m_scene.createPerspectiveCamera(); camera->setViewport(0, 0, 16u, 16u); - camera->setFrustum(19.f, static_cast(ramses_internal::IntegrationScene::DefaultViewportWidth) / ramses_internal::IntegrationScene::DefaultViewportHeight, 0.1f, 100.f); + camera->setFrustum(19.f, static_cast(ramses::internal::IntegrationScene::DefaultViewportWidth) / ramses::internal::IntegrationScene::DefaultViewportHeight, 0.1f, 100.f); setCameraToDefaultRenderPass(camera); auto dataVpOffset = m_scene.createDataObject(ramses::EDataType::Vector2I); auto dataVpSize = m_scene.createDataObject(ramses::EDataType::Vector2I); dataVpOffset->setValue(ramses::vec2i{ 0, 0 }); - dataVpSize->setValue(ramses::vec2i{ int32_t(ramses_internal::IntegrationScene::DefaultViewportWidth), int32_t(ramses_internal::IntegrationScene::DefaultViewportHeight) }); + dataVpSize->setValue(ramses::vec2i{ int32_t(ramses::internal::IntegrationScene::DefaultViewportWidth), int32_t(ramses::internal::IntegrationScene::DefaultViewportHeight) }); camera->bindViewportOffset(*dataVpOffset); camera->bindViewportSize(*dataVpSize); auto dataFrustumPlanes = m_scene.createDataObject(ramses::EDataType::Vector4F); @@ -92,7 +92,7 @@ namespace ramses_internal auto dataFrustumPlanes = m_scene.createDataObject(ramses::EDataType::Vector4F); auto dataFrustumNearFarPlanes = m_scene.createDataObject(ramses::EDataType::Vector2F); dataVpOffset->setValue(ramses::vec2i{ 20, 20 }); - dataVpSize->setValue(ramses::vec2i{ int32_t(ramses_internal::IntegrationScene::DefaultViewportWidth / 2), int32_t(ramses_internal::IntegrationScene::DefaultViewportHeight / 2) }); + dataVpSize->setValue(ramses::vec2i{ int32_t(ramses::internal::IntegrationScene::DefaultViewportWidth / 2), int32_t(ramses::internal::IntegrationScene::DefaultViewportHeight / 2) }); ramses::RamsesUtils::SetPerspectiveCameraFrustumToDataObjects( 60.f, // much wider FOV will make content smaller when projected 0.5f, // original aspect ratio is 1:1, this will make content wider diff --git a/integration/TestContent/src/CommonRenderBufferTestScene.cpp b/tests/integration/test-content/CommonRenderBufferTestScene.cpp similarity index 75% rename from integration/TestContent/src/CommonRenderBufferTestScene.cpp rename to tests/integration/test-content/CommonRenderBufferTestScene.cpp index cbdc13666..dee024982 100644 --- a/integration/TestContent/src/CommonRenderBufferTestScene.cpp +++ b/tests/integration/test-content/CommonRenderBufferTestScene.cpp @@ -8,18 +8,18 @@ #include "TestScenes/CommonRenderBufferTestScene.h" #include "TestScenes/Triangle.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" + +namespace ramses::internal { CommonRenderBufferTestScene::CommonRenderBufferTestScene(ramses::Scene& scene, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -67,16 +67,11 @@ namespace ramses_internal ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "quad geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "quad geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -85,13 +80,11 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Nearest, renderBuffer); - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), *sampler); ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.0f, 0.f, -8.f}); @@ -100,13 +93,13 @@ namespace ramses_internal return *meshNode; } - ramses::MeshNode& CommonRenderBufferTestScene::createMesh(const ramses::Effect& effect, ramses::TriangleAppearance::EColor color) + ramses::MeshNode& CommonRenderBufferTestScene::createMesh(const ramses::Effect& effect, TriangleAppearance::EColor color) { - ramses::Triangle triangle(m_scene, effect, color); + Triangle triangle(m_scene, effect, color); ramses::MeshNode& meshNode = *m_scene.createMeshNode(); meshNode.setAppearance(triangle.GetAppearance()); - meshNode.setGeometryBinding(triangle.GetGeometry()); + meshNode.setGeometry(triangle.GetGeometry()); return meshNode; } diff --git a/integration/TestContent/src/CubeTextureScene.cpp b/tests/integration/test-content/CubeTextureScene.cpp similarity index 67% rename from integration/TestContent/src/CubeTextureScene.cpp rename to tests/integration/test-content/CubeTextureScene.cpp index 4a73fc11a..c5f78b90c 100644 --- a/integration/TestContent/src/CubeTextureScene.cpp +++ b/tests/integration/test-content/CubeTextureScene.cpp @@ -8,20 +8,17 @@ #include "TestScenes/CubeTextureScene.h" #include "TestScenes/Triangle.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" -#include "Utils/Image.h" - -#define EDGE_LENGTH 10u -#define FACE_SIZE EDGE_LENGTH * EDGE_LENGTH * 4u - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" +#include "internal/Core/Utils/Image.h" + +namespace ramses::internal { CubeTextureScene::CubeTextureScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -32,7 +29,7 @@ namespace ramses_internal init(static_cast(state)); } - void CubeTextureScene::divideUnitSphereTriangle(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, long depth) + void CubeTextureScene::divideUnitSphereTriangle(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, int depth) { // make sure points are on unit sphere surface p1 = glm::normalize(p1); @@ -42,7 +39,7 @@ namespace ramses_internal if (depth == 0) { // enough recursions, store current positions and indices (no point sharing implemented) - uint16_t currentPositions = static_cast(m_spherePositions.size()); + const auto currentPositions = static_cast(m_spherePositions.size()); m_sphereIndices.push_back(currentPositions); m_sphereIndices.push_back(currentPositions + 1); m_sphereIndices.push_back(currentPositions + 2); @@ -99,9 +96,7 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Linear, *cubeTexture); - ramses::UniformInput cubeTextureinput; - m_effect->findUniformInput("cubeTex", cubeTextureinput); - pAppearance->setInputTexture(cubeTextureinput, *sampler); + pAppearance->setInputTexture(*m_effect->findUniformInput("cubeTex"), *sampler); initializeUnitSphere(); @@ -109,19 +104,14 @@ namespace ramses_internal const ramses::ArrayResource* pVertexNormals = m_scene.createArrayResource(static_cast(m_sphereNormals.size()), m_sphereNormals.data()); const ramses::ArrayResource* pIndices = m_scene.createArrayResource(static_cast(m_sphereIndices.size()), m_sphereIndices.data()); - ramses::AttributeInput positionsInput; - ramses::AttributeInput normalsInput; - m_effect->findAttributeInput("a_position", positionsInput); - m_effect->findAttributeInput("a_normal", normalsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*m_effect, "sphere geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*m_effect, "sphere geometry"); geometry->setIndices(*pIndices); - geometry->setInputBuffer(positionsInput, *pVertexPositions); - geometry->setInputBuffer(normalsInput, *pVertexNormals); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_position"), *pVertexPositions); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_normal"), *pVertexNormals); m_sphereMesh->setAppearance(*pAppearance); - m_sphereMesh->setGeometryBinding(*geometry); + m_sphereMesh->setGeometry(*geometry); } ramses::TextureCube* CubeTextureScene::createTextureCube(EState state) @@ -130,48 +120,66 @@ namespace ramses_internal { case EState_RGBA8: { - ramses_internal::Image imagePX; + ramses::internal::Image imagePX; imagePX.loadFromFilePNG("res/ramses-test-client-cube-px.png"); - ramses_internal::Image imageNX; + ramses::internal::Image imageNX; imageNX.loadFromFilePNG("res/ramses-test-client-cube-nx.png"); - ramses_internal::Image imagePY; + ramses::internal::Image imagePY; imagePY.loadFromFilePNG("res/ramses-test-client-cube-py.png"); - ramses_internal::Image imageNY; + ramses::internal::Image imageNY; imageNY.loadFromFilePNG("res/ramses-test-client-cube-ny.png"); - ramses_internal::Image imagePZ; + ramses::internal::Image imagePZ; imagePZ.loadFromFilePNG("res/ramses-test-client-cube-pz.png"); - ramses_internal::Image imageNZ; + ramses::internal::Image imageNZ; imageNZ.loadFromFilePNG("res/ramses-test-client-cube-nz.png"); ramses::CubeMipLevelData mipLevelData( static_cast(imagePX.getData().size()), - imagePX.getData().data(), imageNX.getData().data(), - imagePY.getData().data(), imageNY.getData().data(), - imagePZ.getData().data(), imageNZ.getData().data()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imagePX.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imageNX.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imagePY.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imageNY.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imagePZ.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imageNZ.getData().data())); return m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, imageNY.getWidth(), 1, &mipLevelData, false); } case EState_BGRA_Swizzled: { ramses::TextureSwizzle bgraSwizzle = {ramses::ETextureChannelColor::Blue, ramses::ETextureChannelColor::Green, ramses::ETextureChannelColor::Red, ramses::ETextureChannelColor::Alpha}; - ramses_internal::Image imagePX; + ramses::internal::Image imagePX; imagePX.loadFromFilePNG("res/ramses-test-client-cube-px.png"); - ramses_internal::Image imageNX; + ramses::internal::Image imageNX; imageNX.loadFromFilePNG("res/ramses-test-client-cube-nx.png"); - ramses_internal::Image imagePY; + ramses::internal::Image imagePY; imagePY.loadFromFilePNG("res/ramses-test-client-cube-py.png"); - ramses_internal::Image imageNY; + ramses::internal::Image imageNY; imageNY.loadFromFilePNG("res/ramses-test-client-cube-ny.png"); - ramses_internal::Image imagePZ; + ramses::internal::Image imagePZ; imagePZ.loadFromFilePNG("res/ramses-test-client-cube-pz.png"); - ramses_internal::Image imageNZ; + ramses::internal::Image imageNZ; imageNZ.loadFromFilePNG("res/ramses-test-client-cube-nz.png"); ramses::CubeMipLevelData mipLevelData( static_cast(imagePX.getData().size()), - imagePX.getData().data(), imageNX.getData().data(), - imagePY.getData().data(), imageNY.getData().data(), - imagePZ.getData().data(), imageNZ.getData().data()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imagePX.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imageNX.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imagePY.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imageNY.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imagePZ.getData().data()), + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + reinterpret_cast(imageNZ.getData().data())); return m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, imageNY.getWidth(), 1, &mipLevelData, false, bgraSwizzle); } @@ -183,7 +191,7 @@ namespace ramses_internal 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}; - const uint8_t* texturePtr = reinterpret_cast(texture); + const auto* texturePtr = reinterpret_cast(texture); ramses::CubeMipLevelData mipLevelData(sizeof(texture), texturePtr, texturePtr, texturePtr, texturePtr, texturePtr, texturePtr); return m_scene.createTextureCube(ramses::ETextureFormat::RGB32F, 2, 1, &mipLevelData, false); diff --git a/integration/TestContent/src/CustomShaderTestScene.cpp b/tests/integration/test-content/CustomShaderTestScene.cpp similarity index 73% rename from integration/TestContent/src/CustomShaderTestScene.cpp rename to tests/integration/test-content/CustomShaderTestScene.cpp index 378662b98..a071efe16 100644 --- a/integration/TestContent/src/CustomShaderTestScene.cpp +++ b/tests/integration/test-content/CustomShaderTestScene.cpp @@ -7,23 +7,23 @@ // ------------------------------------------------------------------------- #include "TestScenes/CustomShaderTestScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" #include -namespace ramses_internal +namespace ramses::internal { CustomShaderTestScene::CustomShaderTestScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) - , m_effect(*getTestEffect(getEffectNameFromState(state))) + , m_effect(*getTestEffect(GetEffectNameFromState(state))) , m_appearance(*scene.createAppearance(m_effect)) - , m_geometryBinding(*scene.createGeometryBinding(m_effect)) + , m_geometryBinding(*scene.createGeometry(m_effect)) { ramses::MeshNode* meshNode = m_scene.createMeshNode("red triangle mesh node"); addMeshNodeToDefaultRenderGroup(*meshNode); @@ -36,12 +36,12 @@ namespace ramses_internal m_appearance.setDrawMode(ramses::EDrawMode::TriangleStrip); createGeometry(); - meshNode->setGeometryBinding(m_geometryBinding); + meshNode->setGeometry(m_geometryBinding); initInputs(); } - std::string CustomShaderTestScene::getEffectNameFromState(uint32_t state) const + std::string CustomShaderTestScene::GetEffectNameFromState(uint32_t state) { switch (state) { @@ -69,10 +69,7 @@ namespace ramses_internal ramses::vec3f{ 1.f, -1.f, -1.f } }; const ramses::ArrayResource& vertexPositions = *m_scene.createArrayResource(4u, vertexPositionsData.data()); - - ramses::AttributeInput positionsInput; - m_effect.findAttributeInput("a_position", positionsInput); - m_geometryBinding.setInputBuffer(positionsInput, vertexPositions); + m_geometryBinding.setInputBuffer(*m_effect.findAttributeInput("a_position"), vertexPositions); const std::array texCoordsData { @@ -82,10 +79,7 @@ namespace ramses_internal ramses::vec2f{ 1.f, 1.f } }; const ramses::ArrayResource& texCoords = *m_scene.createArrayResource(4u, texCoordsData.data()); - - ramses::AttributeInput texCoordInput; - m_effect.findAttributeInput("a_texcoord", texCoordInput); - m_geometryBinding.setInputBuffer(texCoordInput, texCoords); + m_geometryBinding.setInputBuffer(*m_effect.findAttributeInput("a_texcoord"), texCoords); } void CustomShaderTestScene::initInputs() @@ -94,9 +88,6 @@ namespace ramses_internal assert(texture != nullptr); ramses::TextureSampler& texSampler = *m_scene.createTextureSampler(ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Nearest, ramses::ETextureSamplingMethod::Nearest, *texture); - - ramses::UniformInput input; - m_effect.findUniformInput("u_texture", input); - m_appearance.setInputTexture(input, texSampler); + m_appearance.setInputTexture(*m_effect.findUniformInput("u_texture"), texSampler); } } diff --git a/integration/TestContent/src/DataLinkScene.cpp b/tests/integration/test-content/DataLinkScene.cpp similarity index 84% rename from integration/TestContent/src/DataLinkScene.cpp rename to tests/integration/test-content/DataLinkScene.cpp index cf9f9b373..176b97513 100644 --- a/integration/TestContent/src/DataLinkScene.cpp +++ b/tests/integration/test-content/DataLinkScene.cpp @@ -7,22 +7,22 @@ // ------------------------------------------------------------------------- #include "TestScenes/DataLinkScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/Appearance.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/Appearance.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include "TestScenes/Triangle.h" -namespace ramses_internal +namespace ramses::internal { DataLinkScene::DataLinkScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); - ramses::Triangle triangle1(scene, *effect, ramses::TriangleAppearance::EColor_Red); - ramses::Triangle triangle2(scene, *effect, ramses::TriangleAppearance::EColor_Green); + Triangle triangle1(scene, *effect, TriangleAppearance::EColor_Red); + Triangle triangle2(scene, *effect, TriangleAppearance::EColor_Green); ramses::DataObject* colorData = scene.createDataObject(ramses::EDataType::Vector4F, "dataLinkColorData"); @@ -40,8 +40,8 @@ namespace ramses_internal mesh1->setAppearance(appearance1); mesh2->setAppearance(appearance2); - mesh1->setGeometryBinding(triangle1.GetGeometry()); - mesh2->setGeometryBinding(triangle2.GetGeometry()); + mesh1->setGeometry(triangle1.GetGeometry()); + mesh2->setGeometry(triangle2.GetGeometry()); ramses::Node* translate1 = scene.createNode(); ramses::Node* translate2 = scene.createNode(); diff --git a/integration/TestContent/src/EmbeddedCompositorScene.cpp b/tests/integration/test-content/EmbeddedCompositorScene.cpp similarity index 83% rename from integration/TestContent/src/EmbeddedCompositorScene.cpp rename to tests/integration/test-content/EmbeddedCompositorScene.cpp index db2b74f67..6a1a38266 100644 --- a/integration/TestContent/src/EmbeddedCompositorScene.cpp +++ b/tests/integration/test-content/EmbeddedCompositorScene.cpp @@ -7,20 +7,20 @@ // ------------------------------------------------------------------------- #include "TestScenes/EmbeddedCompositorScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-utils.h" -#include "RamsesObjectTypeUtils.h" - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/ramses-utils.h" +#include "impl/RamsesObjectTypeUtils.h" + +namespace ramses::internal { EmbeddedCompositorScene::EmbeddedCompositorScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -86,6 +86,8 @@ namespace ramses_internal createQuadWithTextureConsumer(0.0f, -1.0f, 2.0f, 2.0f, SamplerConsumerId2, *leftSceneFallbackTexture); break; } + default: + break; } } @@ -108,19 +110,15 @@ namespace ramses_internal const ramses::ArrayResource* indices = m_scene.createArrayResource(6, indicesArray); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(appearance.getEffect(), "triangle geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(appearance.getEffect(), "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - appearance.getEffect().findAttributeInput("a_position", positionsInput); - appearance.getEffect().findAttributeInput("a_texcoord", texcoordsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texcoordsInput, m_textureCoords); + geometry->setInputBuffer(*appearance.getEffect().findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*appearance.getEffect().findAttributeInput("a_texcoord"), m_textureCoords); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = m_scene.createMeshNode("textured triangle mesh node"); meshNode->setAppearance(appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); // mesh needs to be added to a render group that belongs to a render pass with camera in order to be rendered addMeshNodeToDefaultRenderGroup(*meshNode); } @@ -135,10 +133,7 @@ namespace ramses_internal fallbackTexture); ramses::Appearance* appearance = scene.createAppearance(m_effect, "triangle appearance"); scene.createTextureConsumer(*sampler, consumerId); - - ramses::UniformInput textureInput; - m_effect.findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*m_effect.findUniformInput("u_texture"), *sampler); return *appearance; } @@ -165,11 +160,13 @@ namespace ramses_internal effectDesc.setVertexShaderFromFile("res/ramses-test-client-textured-with-texel-fetch.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-test-client-textured-with-texel-fetch.frag"); break; + default: + break; } effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + const ramses::Effect* effect = m_scene.createEffect(effectDesc, "glsl shader"); assert(nullptr != effect); return *effect; @@ -177,9 +174,7 @@ namespace ramses_internal const ramses::ArrayResource& EmbeddedCompositorScene::createTextureCoordinates(uint32_t state) { - switch(state) - { - case SINGLE_STREAM_TEXTURE_WITH_TEXCOORDS_OFFSET: + if (state == SINGLE_STREAM_TEXTURE_WITH_TEXCOORDS_OFFSET) { const std::array textureCoordsArray { @@ -190,18 +185,14 @@ namespace ramses_internal }; return *m_scene.createArrayResource(4u, textureCoordsArray.data()); } - default: + const std::array textureCoordsArray { - const std::array textureCoordsArray - { - ramses::vec2f{ 0.f, 1.f }, //A A-----D - ramses::vec2f{ 0.f, 0.f }, //B | | - ramses::vec2f{ 1.f, 0.f }, //C | | - ramses::vec2f{ 1.f, 1.f } //D B-----C - }; - return *m_scene.createArrayResource(4u, textureCoordsArray.data()); - } - } + ramses::vec2f{ 0.f, 1.f }, //A A-----D + ramses::vec2f{ 0.f, 0.f }, //B | | + ramses::vec2f{ 1.f, 0.f }, //C | | + ramses::vec2f{ 1.f, 1.f } //D B-----C + }; + return *m_scene.createArrayResource(4u, textureCoordsArray.data()); } void EmbeddedCompositorScene::createQuadWithTextureConsumer(float xPos, float yPos, float width, float height, ramses::dataConsumerId_t consumerId, const ramses::Texture2D& fallbackTexture) diff --git a/integration/TestContent/src/FileLoadingScene.cpp b/tests/integration/test-content/FileLoadingScene.cpp similarity index 75% rename from integration/TestContent/src/FileLoadingScene.cpp rename to tests/integration/test-content/FileLoadingScene.cpp index bebf88e09..83a33f521 100644 --- a/integration/TestContent/src/FileLoadingScene.cpp +++ b/tests/integration/test-content/FileLoadingScene.cpp @@ -7,25 +7,25 @@ // ------------------------------------------------------------------------- #include "TestScenes/FileLoadingScene.h" -#include "ramses-utils.h" - -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/UniformInput.h" -#include "RamsesObjectTypeUtils.h" -#include "Utils/File.h" - -namespace ramses_internal +#include "ramses/client/ramses-utils.h" + +#include "ramses/client/Scene.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/UniformInput.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "internal/Core/Utils/File.h" + +namespace ramses::internal { FileLoadingScene::FileLoadingScene(ramses::RamsesClient& clientForLoading, uint32_t state, ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const std::string& folder, const ramses::RamsesFrameworkConfig& config, uint32_t vpWidth, uint32_t vpHeight) : m_viewportWidth(vpWidth) @@ -39,14 +39,14 @@ namespace ramses_internal ramses::RamsesClient& separateClient(*separateFramework.createClient("ramses-test-client-fileLoadingScene-createFiles")); createFiles(separateClient, sceneId, cameraPosition, folder); loadFromFiles(clientForLoading, folder); - cleanupFiles(folder); + CleanupFiles(folder); break; } case CREATE_SAVE_DESTROY_LOAD_USING_SAME_CLIENT: { createFiles(clientForLoading, sceneId, cameraPosition, folder); loadFromFiles(clientForLoading, folder); - cleanupFiles(folder); + CleanupFiles(folder); break; } default: @@ -55,9 +55,9 @@ namespace ramses_internal } } - void FileLoadingScene::createFiles(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const std::string& folder, const ramses::SceneConfig& sceneConfig) + void FileLoadingScene::createFiles(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const std::string& folder) { - ramses::Scene* scene = client.createScene(sceneId, sceneConfig); + ramses::Scene* scene = client.createScene(SceneConfig(sceneId)); ramses::Node* cameraTranslation = scene->createNode("cameraPosition"); cameraTranslation->setTranslation({cameraPosition.x, cameraPosition.y, cameraPosition.z}); @@ -66,7 +66,7 @@ namespace ramses_internal camera->setFrustum(19.f, float(m_viewportWidth) / m_viewportHeight, 0.1f, 1500.f); camera->setParent(*cameraTranslation); ramses::RenderPass* renderPass = scene->createRenderPass("my render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene->createRenderGroup("render group"); renderPass->addRenderGroup(*renderGroup); @@ -94,40 +94,35 @@ namespace ramses_internal effectDesc.setFragmentShaderFromFile("res/ramses-test-client-file-loading-texturing.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effectTex = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + ramses::Effect* effectTex = scene->createEffect(effectDesc, "glsl shader"); ramses::Appearance* appearance = scene->createAppearance(*effectTex, "triangle appearance"); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effectTex, "triangle geometry"); + ramses::Geometry* geometry = scene->createGeometry(*effectTex, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texcoordsInput; - effectTex->findAttributeInput("a_position", positionsInput); - effectTex->findAttributeInput("a_texcoord", texcoordsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texcoordsInput, *textureCoords); - - ramses::UniformInput textureInput; - effectTex->findUniformInput("textureSampler", textureInput); - appearance->setInputTexture(textureInput, *sampler); + geometry->setInputBuffer(*effectTex->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effectTex->findAttributeInput("a_texcoord"), *textureCoords); + appearance->setInputTexture(*effectTex->findUniformInput("textureSampler"), *sampler); ramses::Node* scaleNode = scene->createNode("scale node"); ramses::MeshNode* meshNode = scene->createMeshNode("textured triangle mesh node"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); scaleNode->addChild(*meshNode); - addTriangles(*scene, *renderGroup); + AddTriangles(*scene, *renderGroup); - [[maybe_unused]] auto status = scene->saveToFile(folder + "/tempfile.ramses", false); + SaveFileConfig config; + config.setValidationEnabled(false); + [[maybe_unused]] auto status = scene->saveToFile(folder + "/tempfile.ramses", config); client.destroy(*scene); } - void FileLoadingScene::addTriangles(ramses::Scene& scene, ramses::RenderGroup& renderGroup) + void FileLoadingScene::AddTriangles(ramses::Scene& scene, ramses::RenderGroup& renderGroup) { // prepare triangle geometry: vertex position array and index array const std::array vertexPositionsData{ ramses::vec3f{-0.3f, 0.f, -0.3f}, ramses::vec3f{0.3f, 0.f, -0.3f}, ramses::vec3f{0.f, 0.3f, -0.3f} }; @@ -142,26 +137,24 @@ namespace ramses_internal effectDesc.setFragmentShaderFromFile("res/ramses-test-client-file-loading-red.frag"); effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - ramses::Effect* effect = scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader anim"); + ramses::Effect* effect = scene.createEffect(effectDesc, "glsl shader anim"); ramses::Appearance* appearance = scene.createAppearance(*effect, "triangle appearance anim"); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = scene.createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = scene.createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); // create a mesh nodes to define the triangles with chosen appearance ramses::MeshNode* meshNode1 = scene.createMeshNode("red triangle mesh node1"); meshNode1->setAppearance(*appearance); - meshNode1->setGeometryBinding(*geometry); + meshNode1->setGeometry(*geometry); ramses::MeshNode* meshNode2 = scene.createMeshNode("red triangle mesh node2"); meshNode2->setAppearance(*appearance); - meshNode2->setGeometryBinding(*geometry); + meshNode2->setGeometry(*geometry); ramses::MeshNode* meshNode3 = scene.createMeshNode("red triangle mesh node3"); meshNode3->setAppearance(*appearance); - meshNode3->setGeometryBinding(*geometry); + meshNode3->setGeometry(*geometry); // mesh needs to be added to a render pass with camera in order to be rendered renderGroup.addMeshNode(*meshNode1); @@ -183,10 +176,10 @@ namespace ramses_internal void FileLoadingScene::loadFromFiles(ramses::RamsesClient& client, const std::string& folder) { - ramses::Scene* loadedScene = client.loadSceneFromFile(folder + "/tempfile.ramses"); + ramses::Scene* loadedScene = client.loadSceneFromFile(folder + "/tempfile.ramses", SceneConfig({}, EScenePublicationMode::LocalAndRemote)); // make changes to loaded scene - ramses::Node& loadedScaleNode = ramses::RamsesObjectTypeUtils::ConvertTo(*loadedScene->findObjectByName("scale node")); + auto& loadedScaleNode = *loadedScene->findObject("scale node"); loadedScaleNode.setScaling({2, 2, 2}); loadedScene->flush(); @@ -199,7 +192,7 @@ namespace ramses_internal return m_createdScene; } - void FileLoadingScene::cleanupFiles(const std::string& folder) + void FileLoadingScene::CleanupFiles(const std::string& folder) { for (const auto& name : {"/texture.ramres", "/triangle.ramres", "/tempfile.ramses"}) { diff --git a/integration/TestContent/src/GeometryInstanceScene.cpp b/tests/integration/test-content/GeometryInstanceScene.cpp similarity index 70% rename from integration/TestContent/src/GeometryInstanceScene.cpp rename to tests/integration/test-content/GeometryInstanceScene.cpp index 3972dbc78..96b830d1b 100644 --- a/integration/TestContent/src/GeometryInstanceScene.cpp +++ b/tests/integration/test-content/GeometryInstanceScene.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- #include "TestScenes/GeometryInstanceScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" #include -namespace ramses_internal +namespace ramses::internal { GeometryInstanceScene::GeometryInstanceScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -30,6 +30,7 @@ namespace ramses_internal break; case GEOMETRY_INSTANCE_VERTEX: case GEOMETRY_INSTANCE_AND_NOT_INSTANCE: + case GEOMETRY_INSTANCE_ZERO_INSTANCE_COUNT: effect = getTestEffect("ramses-test-instancing-vertex"); break; default: @@ -45,16 +46,19 @@ namespace ramses_internal addMeshNodeToDefaultRenderGroup(*meshNode, 0); meshNode->setAppearance(*appearance); - ramses::GeometryBinding* geometry = createGeometry(*effect); + ramses::Geometry* geometry = createGeometry(*effect); if (GEOMETRY_INSTANCE_UNIFORM == state) { - setInstancedUniforms(*appearance); + SetInstancedUniforms(*appearance); } else { setInstancedAttributes(*effect, geometry, 1); } - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); + + if (state == GEOMETRY_INSTANCE_ZERO_INSTANCE_COUNT) + meshNode->setInstanceCount(0u); } if (GEOMETRY_INSTANCE_AND_NOT_INSTANCE == state) @@ -71,22 +75,16 @@ namespace ramses_internal translateNode->setTranslation({0.f, -1.5f, 0.f}); notInstancedMeshNode->setParent(*translateNode); - ramses::GeometryBinding* notInstancedGeometry = createGeometry(*effect); + ramses::Geometry* notInstancedGeometry = createGeometry(*effect); setInstancedAttributes(*effect, notInstancedGeometry, 0); - notInstancedMeshNode->setGeometryBinding(*notInstancedGeometry); + notInstancedMeshNode->setGeometry(*notInstancedGeometry); } } - void GeometryInstanceScene::setInstancedUniforms(ramses::Appearance& appearance) + void GeometryInstanceScene::SetInstancedUniforms(ramses::Appearance& appearance) { const ramses::Effect& effect = appearance.getEffect(); - ramses::UniformInput translationInput; - effect.findUniformInput("translation", translationInput); - - ramses::UniformInput colorInput; - effect.findUniformInput("color", colorInput); - static const std::array translation = { ramses::vec3f{ -0.5f, 0.5f, 0.0f }, @@ -101,18 +99,12 @@ namespace ramses_internal ramses::vec4f{ 0.1f, 1.0f, 0.1f, 1.0f } }; - appearance.setInputValue(translationInput, NumInstances, translation.data()); - appearance.setInputValue(colorInput, NumInstances, color.data()); + appearance.setInputValue(*effect.findUniformInput("translation"), NumInstances, translation.data()); + appearance.setInputValue(*effect.findUniformInput("color"), NumInstances, color.data()); } - void GeometryInstanceScene::setInstancedAttributes(const ramses::Effect& effect, ramses::GeometryBinding* geometry, uint32_t instancingDivisor) + void GeometryInstanceScene::setInstancedAttributes(const ramses::Effect& effect, ramses::Geometry* geometry, uint32_t instancingDivisor) { - ramses::AttributeInput translationInput; - effect.findAttributeInput("translation", translationInput); - - ramses::AttributeInput colorInput; - effect.findAttributeInput("color", colorInput); - static const std::array translation { ramses::vec3f{ -0.5f, 0.5f, 0.0f }, @@ -128,12 +120,12 @@ namespace ramses_internal }; const ramses::ArrayResource* translationsArray = m_scene.createArrayResource(3u, translation.data()); - geometry->setInputBuffer(translationInput, *translationsArray, instancingDivisor); + geometry->setInputBuffer(*effect.findAttributeInput("translation"), *translationsArray, instancingDivisor); const ramses::ArrayResource* colorArray = m_scene.createArrayResource(3u, color.data()); - geometry->setInputBuffer(colorInput, *colorArray, instancingDivisor); + geometry->setInputBuffer(*effect.findAttributeInput("color"), *colorArray, instancingDivisor); } - ramses::GeometryBinding* GeometryInstanceScene::createGeometry(const ramses::Effect& effect) + ramses::Geometry* GeometryInstanceScene::createGeometry(const ramses::Effect& effect) { static const std::array verticesData { @@ -150,13 +142,10 @@ namespace ramses_internal const ramses::ArrayResource* indices = m_scene.createArrayResource(NumInstances, indicesData); const ramses::ArrayResource* vertices = m_scene.createArrayResource(NumInstances, verticesData.data()); - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); - - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(effect); + ramses::Geometry* geometry = m_scene.createGeometry(effect); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertices); + geometry->setInputBuffer(*effect.findAttributeInput("a_position"), *vertices); return geometry; } diff --git a/integration/TestContent/src/GeometryShaderScene.cpp b/tests/integration/test-content/GeometryShaderScene.cpp similarity index 85% rename from integration/TestContent/src/GeometryShaderScene.cpp rename to tests/integration/test-content/GeometryShaderScene.cpp index 8493fcc0e..b153a394f 100644 --- a/integration/TestContent/src/GeometryShaderScene.cpp +++ b/tests/integration/test-content/GeometryShaderScene.cpp @@ -7,23 +7,23 @@ // ------------------------------------------------------------------------- #include "TestScenes/GeometryShaderScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/EffectDescription.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/EffectDescription.h" #include -namespace ramses_internal +namespace ramses::internal { GeometryShaderScene::GeometryShaderScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -31,8 +31,8 @@ namespace ramses_internal { ramses::Appearance* appearance = m_scene.createAppearance(m_effect); appearance->setCullingMode(ramses::ECullMode::Disabled); - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(m_effect); - appearance->setDrawMode(getDrawMode(state)); + ramses::Geometry* geometry = m_scene.createGeometry(m_effect); + appearance->setDrawMode(GetDrawMode(state)); const std::array vertexPositionsArray{ ramses::vec2f{.3f, -.3f}, @@ -47,19 +47,15 @@ namespace ramses_internal ramses::vec3f{1.f, 1.f, 1.f} }; const ramses::ArrayResource* vertexPositions = m_scene.createArrayResource(4u, vertexPositionsArray.data()); - ramses::AttributeInput positionsInput; - m_effect.findAttributeInput("a_pos", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + geometry->setInputBuffer(*m_effect.findAttributeInput("a_pos"), *vertexPositions); const ramses::ArrayResource* vertexColors = m_scene.createArrayResource(4u, vertexColorsArray.data()); - ramses::AttributeInput colorsInput; - m_effect.findAttributeInput("a_color", colorsInput); - geometry->setInputBuffer(colorsInput, *vertexColors); + geometry->setInputBuffer(*m_effect.findAttributeInput("a_color"), *vertexColors); // create a mesh node to define the triangle with chosen appearance ramses::MeshNode* meshNode = m_scene.createMeshNode(""); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); meshNode->setIndexCount(4); addMeshNodeToDefaultRenderGroup(*meshNode); @@ -67,7 +63,7 @@ namespace ramses_internal const ramses::Effect& GeometryShaderScene::createTestEffect(uint32_t state) { - const std::string shaderVersion = getShaderVersion(state); + const std::string shaderVersion = GetShaderVersion(state); const std::string vertexShaderV320es = shaderVersion + R"SHADER( in highp vec2 a_pos; @@ -93,8 +89,8 @@ namespace ramses_internal effectDesc.setVertexShader(vertexShaderV320es.c_str()); effectDesc.setFragmentShader(fragmentSahderV320es.c_str()); - const std::string geometryShaderExtensions = getGeometryShaderExtensions(state); - const std::string geometryShaderLayour = getGeometryShaderLayout(state); + const std::string geometryShaderExtensions = GetGeometryShaderExtensions(state); + const std::string geometryShaderLayour = GetGeometryShaderLayout(state); switch(state) { @@ -177,7 +173,7 @@ namespace ramses_internal return *m_scene.createEffect(effectDesc); } - std::string GeometryShaderScene::getGeometryShaderLayout(uint32_t state) + std::string GeometryShaderScene::GetGeometryShaderLayout(uint32_t state) { switch (state) { @@ -223,7 +219,7 @@ namespace ramses_internal return ""; } - std::string GeometryShaderScene::getShaderVersion(uint32_t state) + std::string GeometryShaderScene::GetShaderVersion(uint32_t state) { switch (state) { @@ -250,7 +246,7 @@ namespace ramses_internal return ""; } - std::string GeometryShaderScene::getGeometryShaderExtensions(uint32_t state) + std::string GeometryShaderScene::GetGeometryShaderExtensions(uint32_t state) { switch (state) { @@ -275,7 +271,7 @@ namespace ramses_internal return ""; } - ramses::EDrawMode GeometryShaderScene::getDrawMode(uint32_t state) + ramses::EDrawMode GeometryShaderScene::GetDrawMode(uint32_t state) { switch (state) { @@ -292,6 +288,8 @@ namespace ramses_internal case GLSL310_TRIANGLES_IN_TRIANGLE_STRIP_OUT: case GLSL310_TRIANGLES_IN_POINTS_OUT: return ramses::EDrawMode::TriangleStrip; + default: + break; } assert(false); diff --git a/integration/TestContent/src/HierarchicalRedTrianglesScene.cpp b/tests/integration/test-content/HierarchicalRedTrianglesScene.cpp similarity index 92% rename from integration/TestContent/src/HierarchicalRedTrianglesScene.cpp rename to tests/integration/test-content/HierarchicalRedTrianglesScene.cpp index b7ce739b9..d9a814da4 100644 --- a/integration/TestContent/src/HierarchicalRedTrianglesScene.cpp +++ b/tests/integration/test-content/HierarchicalRedTrianglesScene.cpp @@ -8,11 +8,11 @@ #include "TestScenes/HierarchicalRedTrianglesScene.h" #include "TestScenes/Triangle.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" -namespace ramses_internal +namespace ramses::internal { HierarchicalRedTrianglesScene::HierarchicalRedTrianglesScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -26,10 +26,10 @@ namespace ramses_internal , m_scaleNode2(*m_scene.createNode()) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); - ramses::Triangle redTriangle(m_scene, *effect, ramses::TriangleAppearance::EColor_Red); + Triangle redTriangle(m_scene, *effect, TriangleAppearance::EColor_Red); m_groupNode = m_scene.createNode(); - std::array subGroups; + std::array subGroups{}; for (int row = 0; row < 3; ++row) { ramses::Node* subGroupNode = m_scene.createNode(); @@ -39,7 +39,7 @@ namespace ramses_internal ramses::MeshNode* meshNode = m_scene.createMeshNode("red triangle mesh node"); addMeshNodeToDefaultRenderGroup(*meshNode); meshNode->setAppearance(redTriangle.GetAppearance()); - meshNode->setGeometryBinding(redTriangle.GetGeometry()); + meshNode->setGeometry(redTriangle.GetGeometry()); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({-1.f + column * 2, -3.f + row * 2, -30.f}); @@ -103,6 +103,8 @@ namespace ramses_internal case DELETE_MESHNODE: destroySubTree(m_subGroup2Node); break; + default: + break; } } diff --git a/integration/TestContent/src/IndexArray32BitScene.cpp b/tests/integration/test-content/IndexArray32BitScene.cpp similarity index 79% rename from integration/TestContent/src/IndexArray32BitScene.cpp rename to tests/integration/test-content/IndexArray32BitScene.cpp index afaa76509..0148d8b0b 100644 --- a/integration/TestContent/src/IndexArray32BitScene.cpp +++ b/tests/integration/test-content/IndexArray32BitScene.cpp @@ -7,25 +7,23 @@ // ------------------------------------------------------------------------- #include "TestScenes/IndexArray32BitScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "PlatformAbstraction/PlatformMemory.h" - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" + +namespace ramses::internal { IndexArray32BitScene::IndexArray32BitScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); ramses::Appearance* appearance = m_scene.createAppearance(*effect); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.f, 0.f, 1.f, 1.f }); + appearance->setInputValue(*effect->findUniformInput("color"), ramses::vec4f{ 1.f, 0.f, 1.f, 1.f }); const ramses::vec3f translation = { @@ -44,7 +42,7 @@ namespace ramses_internal createGeometry(*effect,state); } - void IndexArray32BitScene::create16BitIndexArray(ramses::GeometryBinding* geometry) + void IndexArray32BitScene::create16BitIndexArray(ramses::Geometry* geometry) { const std::array indicesData = { @@ -56,7 +54,7 @@ namespace ramses_internal geometry->setIndices(*indicesTri); } - void IndexArray32BitScene::create32BitIndexArray(ramses::GeometryBinding* geometry) + void IndexArray32BitScene::create32BitIndexArray(ramses::Geometry* geometry) { const std::array indicesData = { @@ -89,10 +87,7 @@ namespace ramses_internal const ramses::ArrayResource* verticesTri = m_scene.createArrayResource(65540u, vertexPositionsArray.data()); - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); - - ramses::GeometryBinding* geometryTriangle = m_scene.createGeometryBinding(effect); + ramses::Geometry* geometryTriangle = m_scene.createGeometry(effect); switch(state) { @@ -107,9 +102,9 @@ namespace ramses_internal break; } - geometryTriangle->setInputBuffer(positionsInput, *verticesTri); + geometryTriangle->setInputBuffer(*effect.findAttributeInput("a_position"), *verticesTri); - m_meshNode->setGeometryBinding(*geometryTriangle); + m_meshNode->setGeometry(*geometryTriangle); //with offset applied mirrored triangle is drawn if(state == OFFSET_32BIT_INDICES || state == OFFSET_16BIT_INDICES) diff --git a/integration/TestContent/src/IntegrationScene.cpp b/tests/integration/test-content/IntegrationScene.cpp similarity index 90% rename from integration/TestContent/src/IntegrationScene.cpp rename to tests/integration/test-content/IntegrationScene.cpp index 156bf4645..08b8896e8 100644 --- a/integration/TestContent/src/IntegrationScene.cpp +++ b/tests/integration/test-content/IntegrationScene.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "TestScenes/IntegrationScene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" #include -namespace ramses_internal +namespace ramses::internal { IntegrationScene::IntegrationScene(ramses::Scene& scene, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : m_scene(scene) @@ -30,7 +30,7 @@ namespace ramses_internal m_defaultCamera.setParent(m_defaultCameraTranslationNode); m_defaultRenderPass.setCamera(m_defaultCamera); m_defaultRenderPass.addRenderGroup(m_defaultRenderGroup); - m_defaultRenderPass.setClearFlags(ramses::EClearFlags_None); + m_defaultRenderPass.setClearFlags(ramses::EClearFlag::None); } IntegrationScene::~IntegrationScene() = default; @@ -48,7 +48,7 @@ namespace ramses_internal effectDesc.setAttributeSemantic("a_customPosition", ramses::EEffectAttributeSemantic::TextPositions); effectDesc.setAttributeSemantic("a_customTexCoord", ramses::EEffectAttributeSemantic::TextTextureCoordinates); - ramses::Effect* effect = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, nameOrShaderFile.c_str()); + ramses::Effect* effect = m_scene.createEffect(effectDesc, nameOrShaderFile.c_str()); assert(nullptr != effect); return effect; } diff --git a/integration/TestContent/src/Line.cpp b/tests/integration/test-content/Line.cpp similarity index 74% rename from integration/TestContent/src/Line.cpp rename to tests/integration/test-content/Line.cpp index d500c11df..ad62a2a55 100644 --- a/integration/TestContent/src/Line.cpp +++ b/tests/integration/test-content/Line.cpp @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include - -#include "PlatformAbstraction/PlatformTypes.h" - #include "TestScenes/Line.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" -namespace ramses +#include +#include + +namespace ramses::internal { Line::Line(Scene& scene, Effect& effect, enum Line::EColor color, EDrawMode desiredDrawMode, float alpha) : m_appearance(createAppearance(effect, scene)) @@ -28,9 +27,7 @@ namespace ramses , m_geometry(createGeometry(scene, effect, m_indices)) { m_appearance.setDrawMode(desiredDrawMode); - UniformInput colorInput; - effect.findUniformInput("color", colorInput); - setColor(colorInput, color, alpha); + setColor(*effect.findUniformInput("color"), color, alpha); } Appearance& Line::createAppearance(Effect& effect, Scene& scene) @@ -38,12 +35,9 @@ namespace ramses return *scene.createAppearance(effect, "appearance"); } - ramses::GeometryBinding& Line::createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices) + ramses::Geometry& Line::createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices) { - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); - - GeometryBinding* geometry = scene.createGeometryBinding(effect, "triangle geometry"); + Geometry* geometry = scene.createGeometry(effect, "triangle geometry"); geometry->setIndices(indices); const std::array vertexPositionsData @@ -60,7 +54,7 @@ namespace ramses ramses::vec3f{ 1.f, 0.f, -1.f } }; const ArrayResource* vertexPositions = scene.createArrayResource(10u, vertexPositionsData.data()); - geometry->setInputBuffer(positionsInput, *vertexPositions); + geometry->setInputBuffer(*effect.findAttributeInput("a_position"), *vertexPositions); return *geometry; } @@ -87,22 +81,22 @@ namespace ramses void Line::setColor(const UniformInput& colorInput, enum EColor color, float alpha) { - status_t status = StatusOK; + [[maybe_unused]] bool status = false; switch (color) { - case ramses::Line::EColor_Red: + case Line::EColor_Red: status = m_appearance.setInputValue(colorInput, vec4f{ 1.f, 0.f, 0.f, alpha }); break; - case ramses::Line::EColor_Blue: + case Line::EColor_Blue: status = m_appearance.setInputValue(colorInput, vec4f{ 0.f, 0.f, 1.f, alpha }); break; - case ramses::Line::EColor_Green: + case Line::EColor_Green: status = m_appearance.setInputValue(colorInput, vec4f{ 0.f, 1.f, 0.f, alpha }); break; - case ramses::Line::EColor_White: + case Line::EColor_White: status = m_appearance.setInputValue(colorInput, vec4f{ 1.f, 1.f, 1.f, alpha }); break; - case ramses::Line::EColor_Yellow: + case Line::EColor_Yellow: status = m_appearance.setInputValue(colorInput, vec4f{ 1.f, 1.f, 0.f, alpha }); break; default: @@ -110,7 +104,6 @@ namespace ramses break; } - assert(status == StatusOK); - UNUSED(status); + assert(status); } } diff --git a/integration/TestContent/src/MsaaRenderBufferScene.cpp b/tests/integration/test-content/MsaaRenderBufferScene.cpp similarity index 70% rename from integration/TestContent/src/MsaaRenderBufferScene.cpp rename to tests/integration/test-content/MsaaRenderBufferScene.cpp index 52b86517e..5b7689569 100644 --- a/integration/TestContent/src/MsaaRenderBufferScene.cpp +++ b/tests/integration/test-content/MsaaRenderBufferScene.cpp @@ -7,30 +7,36 @@ // ------------------------------------------------------------------------- #include "TestScenes/MsaaRenderBufferScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/EffectDescription.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/EffectDescription.h" #include -namespace ramses_internal +namespace ramses::internal { MsaaRenderBufferScene::MsaaRenderBufferScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : CommonRenderBufferTestScene(scene, cameraPosition) - , m_colorBufferMsaa2(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::WriteOnly, 2u)) - , m_colorBufferMsaa4(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::WriteOnly, 4u)) - , m_colorBufferMsaa4ReadWrite(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite, 4u)) - , m_blittingColorBuffer(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_colorBufferMsaa1(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::WriteOnly, 1u)) + , m_colorBufferMsaa2(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::WriteOnly, 2u)) + , m_colorBufferMsaa4(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::WriteOnly, 4u)) + , m_colorBufferMsaa1ReadWrite(scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite, 1u)) + , m_colorBufferMsaa4ReadWrite(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite, 4u)) + , m_blittingColorBuffer(*scene.createRenderBuffer(2u, 2u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) { + // only checks that can be created but not used in test + // GL implementations vary in what actual sample count to use with 1 sample specified + assert(m_colorBufferMsaa1ReadWrite); + initRenderPass(state); if (state != SAMPLE_COUNT_4_TEXEL_FETCH) { @@ -47,6 +53,9 @@ namespace ramses_internal switch (state) { + case SAMPLE_COUNT_1_BLIT: + rtDesc.addRenderBuffer(m_colorBufferMsaa1); + break; case SAMPLE_COUNT_2_BLIT: rtDesc.addRenderBuffer(m_colorBufferMsaa2); break; @@ -86,7 +95,7 @@ namespace ramses_internal ramses::RenderTarget& renderTarget = createRenderTarget(state); renderPass.setRenderTarget(&renderTarget); renderPass.setClearColor({0.f, 0.f, 0.f, 1.0f}); - renderPass.setClearFlags(ramses::EClearFlags_All); + renderPass.setClearFlags(ramses::EClearFlag::All); } void MsaaRenderBufferScene::initBlittingPass(uint32_t state) @@ -94,6 +103,9 @@ namespace ramses_internal ramses::BlitPass* blitPass = nullptr; switch (state) { + case SAMPLE_COUNT_1_BLIT: + blitPass = m_scene.createBlitPass(m_colorBufferMsaa1, m_blittingColorBuffer); + break; case SAMPLE_COUNT_2_BLIT: blitPass = m_scene.createBlitPass(m_colorBufferMsaa2, m_blittingColorBuffer); break; @@ -113,17 +125,14 @@ namespace ramses_internal ramses::MeshNode& MsaaRenderBufferScene::createMesh() { const ramses::Effect& effect = getEffectRenderOneBuffer(); - ramses::MeshNode& meshNode = CommonRenderBufferTestScene::createMesh(effect, ramses::TriangleAppearance::EColor_White); - - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); + ramses::MeshNode& meshNode = CommonRenderBufferTestScene::createMesh(effect, TriangleAppearance::EColor_White); const std::array vertexPositionsData{ ramses::vec3f{ -1.f, -1.f, 0.f }, ramses::vec3f{ 1.f, -1.f, 0.f }, ramses::vec3f{ -1.f, 1.f, 0.f } }; const ramses::ArrayResource* vertexPositions = m_scene.createArrayResource(3u, vertexPositionsData.data()); - meshNode.getGeometryBinding()->setInputBuffer(positionsInput, *vertexPositions); + meshNode.getGeometry()->setInputBuffer(*effect.findAttributeInput("a_position"), *vertexPositions); return meshNode; } @@ -149,30 +158,19 @@ namespace ramses_internal ramses::Appearance* appearance = m_scene.createAppearance(effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect.findAttributeInput("a_position", positionsInput); - effect.findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(effect, "quad geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(effect, "quad geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect.findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect.findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSamplerMS* sampler = m_scene.createTextureSamplerMS(renderBuffer, "MSAA sampler"); - - ramses::UniformInput textureInput; - effect.findUniformInput("textureSampler", textureInput); - appearance->setInputTexture(textureInput, *sampler); - - ramses::UniformInput sampleCountInput; - effect.findUniformInput("sampleCount", sampleCountInput); - appearance->setInputValue(sampleCountInput, 4); + appearance->setInputTexture(*effect.findUniformInput("textureSampler"), *sampler); + appearance->setInputValue(*effect.findUniformInput("sampleCount"), 4); ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.0f, 0.f, -8.f}); diff --git a/integration/TestContent/src/MultiLanguageTextScene.cpp b/tests/integration/test-content/MultiLanguageTextScene.cpp similarity index 86% rename from integration/TestContent/src/MultiLanguageTextScene.cpp rename to tests/integration/test-content/MultiLanguageTextScene.cpp index 896a7cb0b..35764952f 100644 --- a/integration/TestContent/src/MultiLanguageTextScene.cpp +++ b/tests/integration/test-content/MultiLanguageTextScene.cpp @@ -9,15 +9,15 @@ #if defined(RAMSES_TEXT_ENABLED) #include "TestScenes/MultiLanguageTextScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-utils.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Effect.h" - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Effect.h" + +namespace ramses::internal { MultiLanguageTextScene::MultiLanguageTextScene(ramses::Scene& scene, uint32_t /*state*/, const glm::vec3& cameraPosition) : TextScene_Base(scene, cameraPosition) @@ -32,8 +32,7 @@ namespace ramses_internal effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); ramses::Effect* effect = scene.createEffect(effectDesc); - ramses::UniformInput colorInput; - effect->findUniformInput("u_color", colorInput); + std::optional colorInput = effect->findUniformInput("u_color"); /// Create fonts: const ramses::FontId font = m_fontRegistry.createFreetype2Font("res/ramses-test-client-Roboto-Bold.ttf"); @@ -73,11 +72,11 @@ namespace ramses_internal textMesh->getAppearance()->setBlendingOperations(ramses::EBlendOperation::Add, ramses::EBlendOperation::Add); textMesh->getAppearance()->setBlendingFactors(ramses::EBlendFactor::SrcAlpha, ramses::EBlendFactor::OneMinusSrcAlpha, ramses::EBlendFactor::SrcAlpha, ramses::EBlendFactor::OneMinusSrcAlpha); } - textCyrillic->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); - textChinese->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 0.0f, 0.0f, 1.0f, 1.0f }); - textHebrew->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 0.0f, 1.0f, 1.0f, 1.0f }); - textArabic->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 1.0f, 1.0f }); - textJapanese->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 1.0f, 1.0f, 0.0f, 1.0f }); + textCyrillic->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); + textChinese->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 0.0f, 0.0f, 1.0f, 1.0f }); + textHebrew->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 0.0f, 1.0f, 1.0f, 1.0f }); + textArabic->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 1.0f, 1.0f }); + textJapanese->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 1.0f, 0.0f, 1.0f }); ramses::Node* translateChinese = m_scene.createNode(); ramses::Node* translateJapanese = m_scene.createNode(); diff --git a/integration/TestContent/src/MultiTransformationLinkScene.cpp b/tests/integration/test-content/MultiTransformationLinkScene.cpp similarity index 91% rename from integration/TestContent/src/MultiTransformationLinkScene.cpp rename to tests/integration/test-content/MultiTransformationLinkScene.cpp index 700a4c3eb..82cabd9c3 100644 --- a/integration/TestContent/src/MultiTransformationLinkScene.cpp +++ b/tests/integration/test-content/MultiTransformationLinkScene.cpp @@ -7,19 +7,19 @@ // ------------------------------------------------------------------------- #include "TestScenes/MultiTransformationLinkScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" #include -namespace ramses_internal +namespace ramses::internal { MultiTransformationLinkScene::MultiTransformationLinkScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) , m_scaleFactor(10.f / NumRows) , m_dummyEffect(*getTestEffect("ramses-test-client-basic")) - , m_redTriangle(m_scene, m_dummyEffect, ramses::TriangleAppearance::EColor_Red) - , m_greenTriangle(m_scene, m_dummyEffect, ramses::TriangleAppearance::EColor_Green) - , m_blueTriangle(m_scene, m_dummyEffect, ramses::TriangleAppearance::EColor_Blue) + , m_redTriangle(m_scene, m_dummyEffect, TriangleAppearance::EColor_Red) + , m_greenTriangle(m_scene, m_dummyEffect, TriangleAppearance::EColor_Green) + , m_blueTriangle(m_scene, m_dummyEffect, TriangleAppearance::EColor_Blue) { switch (state) { @@ -67,7 +67,7 @@ namespace ramses_internal ramses::MeshNode* meshNode = m_scene.createMeshNode("red triangle mesh node"); addMeshNodeToDefaultRenderGroup(*meshNode); meshNode->setAppearance(m_redTriangle.GetAppearance()); - meshNode->setGeometryBinding(m_redTriangle.GetGeometry()); + meshNode->setGeometry(m_redTriangle.GetGeometry()); // scale and translate each triangle to fit in the square ramses::Node* transNode = m_scene.createNode(); @@ -110,7 +110,7 @@ namespace ramses_internal ramses::MeshNode* meshNode = m_scene.createMeshNode("red triangle mesh node"); addMeshNodeToDefaultRenderGroup(*meshNode); meshNode->setAppearance(m_greenTriangle.GetAppearance()); - meshNode->setGeometryBinding(m_greenTriangle.GetGeometry()); + meshNode->setGeometry(m_greenTriangle.GetGeometry()); // scale and translate each triangle to fit in the square ramses::Node* transNode = m_scene.createNode(); @@ -149,7 +149,7 @@ namespace ramses_internal ramses::MeshNode* meshNode = m_scene.createMeshNode(); addMeshNodeToDefaultRenderGroup(*meshNode); meshNode->setAppearance(m_blueTriangle.GetAppearance()); - meshNode->setGeometryBinding(m_blueTriangle.GetGeometry()); + meshNode->setGeometry(m_blueTriangle.GetGeometry()); meshNode->setParent(*transNode); transNode->setParent(*meshGroupNode); diff --git a/integration/TestContent/src/MultiTriangleGeometry.cpp b/tests/integration/test-content/MultiTriangleGeometry.cpp similarity index 72% rename from integration/TestContent/src/MultiTriangleGeometry.cpp rename to tests/integration/test-content/MultiTriangleGeometry.cpp index 34e0275d5..701c35870 100644 --- a/integration/TestContent/src/MultiTriangleGeometry.cpp +++ b/tests/integration/test-content/MultiTriangleGeometry.cpp @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include - -#include "PlatformAbstraction/PlatformTypes.h" - #include "TestScenes/MultiTriangleGeometry.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" -namespace ramses +#include +#include + +namespace ramses::internal { MultiTriangleGeometry::MultiTriangleGeometry(Scene& scene, Effect& effect, enum MultiTriangleGeometry::EColor color, float alpha, EGeometryType geometryType, EVerticesOrder vertOrder) : m_appearance(createAppearance(effect, scene)) @@ -35,11 +34,9 @@ namespace ramses { m_appearance.setDrawMode(ramses::EDrawMode::TriangleFan); } - UniformInput colorInput; - if (StatusOK == effect.findUniformInput("color", colorInput)) - { - setColor(colorInput, color, alpha); - } + std::optional colorInput = effect.findUniformInput("color"); + assert(colorInput.has_value()); + setColor(*colorInput, color, alpha); } Appearance& MultiTriangleGeometry::createAppearance(Effect& effect, Scene& scene) @@ -47,23 +44,24 @@ namespace ramses return *scene.createAppearance(effect, "appearance"); } - GeometryBinding& MultiTriangleGeometry::createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices, EGeometryType geometryType) + Geometry& MultiTriangleGeometry::createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices, EGeometryType geometryType) { - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); - - GeometryBinding* geometry = scene.createGeometryBinding(effect, "triangle geometry"); + Geometry* geometry = scene.createGeometry(effect, "triangle geometry"); geometry->setIndices(indices); const ramses::vec3f* vertexPositionsData = nullptr; const std::array vertexPositionsDataTriangleStrip{ ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{-1.f, -1.f, -1.f}, ramses::vec3f{0.f, 0.f, -1.f}, ramses::vec3f{0.f, -1.f, -1.f} }; const std::array vertexPositionsDataTriangleFan{ ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{-1.f, -1.f, -1.f}, ramses::vec3f{-0.2f, -0.7f, -1.f}, ramses::vec3f{0.f, 0.f, -1.f} }; if (geometryType == EGeometryType_TriangleStripQuad) + { vertexPositionsData = vertexPositionsDataTriangleStrip.data(); + } else + { vertexPositionsData = vertexPositionsDataTriangleFan.data(); + } const ArrayResource* vertexPositions = scene.createArrayResource(4u, vertexPositionsData); - geometry->setInputBuffer(positionsInput, *vertexPositions); + geometry->setInputBuffer(*effect.findAttributeInput("a_position"), *vertexPositions); return *geometry; } @@ -78,19 +76,19 @@ namespace ramses void MultiTriangleGeometry::setColor(const UniformInput& colorInput, enum EColor color, float alpha) { - status_t status = StatusOK; + [[maybe_unused]] bool status = false; switch (color) { - case ramses::MultiTriangleGeometry::EColor_Red: + case MultiTriangleGeometry::EColor_Red: status = m_appearance.setInputValue(colorInput, vec4f{ 1.f, 0.f, 0.f, alpha }); break; - case ramses::MultiTriangleGeometry::EColor_Blue: + case MultiTriangleGeometry::EColor_Blue: status = m_appearance.setInputValue(colorInput, vec4f{ 0.f, 0.f, 1.f, alpha }); break; - case ramses::MultiTriangleGeometry::EColor_Green: + case MultiTriangleGeometry::EColor_Green: status = m_appearance.setInputValue(colorInput, vec4f{ 0.f, 1.f, 0.f, alpha }); break; - case ramses::MultiTriangleGeometry::EColor_White: + case MultiTriangleGeometry::EColor_White: status = m_appearance.setInputValue(colorInput, vec4f{ 1.f, 1.f, 1.f, alpha }); break; default: @@ -98,7 +96,6 @@ namespace ramses break; } - assert(status == StatusOK); - UNUSED(status); + assert(status); } } diff --git a/integration/TestContent/src/MultiTypeLinkScene.cpp b/tests/integration/test-content/MultiTypeLinkScene.cpp similarity index 77% rename from integration/TestContent/src/MultiTypeLinkScene.cpp rename to tests/integration/test-content/MultiTypeLinkScene.cpp index d76f4dba0..47c315651 100644 --- a/integration/TestContent/src/MultiTypeLinkScene.cpp +++ b/tests/integration/test-content/MultiTypeLinkScene.cpp @@ -7,27 +7,27 @@ // ------------------------------------------------------------------------- #include "TestScenes/MultiTypeLinkScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Appearance.h" - -#include "Scene/ClientScene.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Appearance.h" + +#include "internal/SceneGraph/Scene/ClientScene.h" #include "TestScenes/Triangle.h" #include -namespace ramses_internal +namespace ramses::internal { MultiTypeLinkScene::MultiTypeLinkScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); ramses::Effect* effectTex = getTestEffect("ramses-test-client-textured"); - ramses::Triangle triangle1(scene, *effect, ramses::TriangleAppearance::EColor_Blue); - ramses::Triangle triangle2(scene, *effectTex, ramses::TriangleAppearance::EColor_Green); + Triangle triangle1(scene, *effect, TriangleAppearance::EColor_Blue); + Triangle triangle2(scene, *effectTex, TriangleAppearance::EColor_Green); ramses::DataObject* colorData = scene.createDataObject(ramses::EDataType::Vector4F); @@ -42,8 +42,8 @@ namespace ramses_internal mesh1->setAppearance(appearance1); mesh2->setAppearance(appearance2); - mesh1->setGeometryBinding(triangle1.GetGeometry()); - mesh2->setGeometryBinding(triangle2.GetGeometry()); + mesh1->setGeometry(triangle1.GetGeometry()); + mesh2->setGeometry(triangle2.GetGeometry()); ramses::Node* translate1 = scene.createNode(); ramses::Node* translate2 = scene.createNode(); @@ -64,9 +64,7 @@ namespace ramses_internal const std::array textureCoordsArray{ ramses::vec2f{0.f, 1.f}, ramses::vec2f{1.f, 1.f}, ramses::vec2f{0.f, 0.f} }; const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(3u, textureCoordsArray.data()); - ramses::AttributeInput texCoordsInput; - effectTex->findAttributeInput("a_texcoord", texCoordsInput); - triangle2.GetGeometry().setInputBuffer(texCoordsInput, *textureCoords); + triangle2.GetGeometry().setInputBuffer(*effectTex->findAttributeInput("a_texcoord"), *textureCoords); switch (state) { @@ -79,9 +77,9 @@ namespace ramses_internal const std::array pxData{ {0xff, 0x0, 0x0, 0xff} }; const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}); const ramses::TextureSampler& sampler = createSampler(texture); - setSampler(appearance2, sampler); + SetSampler(appearance2, sampler); scene.createTextureProvider(texture, TextureProviderId); } @@ -97,9 +95,9 @@ namespace ramses_internal const std::array pxData{ { 0x0, 0xff, 0x0, 0xff } }; const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}); const ramses::TextureSampler& sampler = createSampler(texture); - setSampler(appearance2, sampler); + SetSampler(appearance2, sampler); scene.createTextureConsumer(sampler, TextureConsumerId); } @@ -114,10 +112,8 @@ namespace ramses_internal return *m_scene.createTextureSampler(ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, ramses::ETextureSamplingMethod::Nearest, ramses::ETextureSamplingMethod::Nearest, texture, 1u, "dataLinkSampler"); } - void MultiTypeLinkScene::setSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler) + void MultiTypeLinkScene::SetSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler) { - ramses::UniformInput texInput; - appearance.getEffect().findUniformInput("u_texture", texInput); - appearance.setInputTexture(texInput, sampler); + appearance.setInputTexture(*appearance.getEffect().findUniformInput("u_texture"), sampler); } } diff --git a/integration/TestContent/src/MultipleGeometryScene.cpp b/tests/integration/test-content/MultipleGeometryScene.cpp similarity index 85% rename from integration/TestContent/src/MultipleGeometryScene.cpp rename to tests/integration/test-content/MultipleGeometryScene.cpp index b0b761ff5..59cfd8869 100644 --- a/integration/TestContent/src/MultipleGeometryScene.cpp +++ b/tests/integration/test-content/MultipleGeometryScene.cpp @@ -7,25 +7,23 @@ // ------------------------------------------------------------------------- #include "TestScenes/MultipleGeometryScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" #include -namespace ramses_internal +namespace ramses::internal { MultipleGeometryScene::MultipleGeometryScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); ramses::Appearance* appearance = m_scene.createAppearance(*effect); - ramses::UniformInput colorInput; - effect->findUniformInput("color", colorInput); - appearance->setInputValue(colorInput, ramses::vec4f{ 1.f, 0.f, 1.f, 1.f }); + appearance->setInputValue(*effect->findUniformInput("color"), ramses::vec4f{ 1.f, 0.f, 1.f, 1.f }); if (MULTI_TRIANGLE_STRIP_GEOMETRY_WITHOUT_INDEX_ARRAY == state) { appearance->setDrawMode(ramses::EDrawMode::TriangleStrip); @@ -166,12 +164,11 @@ namespace ramses_internal assert(false && "Invalid state"); } - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); + const std::optional positionsInput = effect.findAttributeInput("a_position"); - ramses::GeometryBinding* geometryTriangle = m_scene.createGeometryBinding(effect); - ramses::GeometryBinding* geometryQuad = m_scene.createGeometryBinding(effect); - ramses::GeometryBinding* geometryPolygon = m_scene.createGeometryBinding(effect); + ramses::Geometry* geometryTriangle = m_scene.createGeometry(effect); + ramses::Geometry* geometryQuad = m_scene.createGeometry(effect); + ramses::Geometry* geometryPolygon = m_scene.createGeometry(effect); if (MULTI_TRIANGLE_LIST_GEOMETRY_WITH_INDEX_ARRAY == state) { @@ -180,14 +177,14 @@ namespace ramses_internal geometryPolygon->setIndices(*indicesPoly); } - geometryTriangle->setInputBuffer(positionsInput, *verticesTri); - geometryQuad->setInputBuffer(positionsInput, *verticesQuad); - geometryPolygon->setInputBuffer(positionsInput, *verticesPoly); + geometryTriangle->setInputBuffer(*positionsInput, *verticesTri); + geometryQuad->setInputBuffer(*positionsInput, *verticesQuad); + geometryPolygon->setInputBuffer(*positionsInput, *verticesPoly); - m_meshNode[0]->setGeometryBinding(*geometryTriangle); - m_meshNode[1]->setGeometryBinding(*geometryQuad); - m_meshNode[2]->setGeometryBinding(*geometryPolygon); - m_meshNode[3]->setGeometryBinding(*geometryPolygon); + m_meshNode[0]->setGeometry(*geometryTriangle); + m_meshNode[1]->setGeometry(*geometryQuad); + m_meshNode[2]->setGeometry(*geometryPolygon); + m_meshNode[3]->setGeometry(*geometryPolygon); switch (state) { @@ -220,6 +217,8 @@ namespace ramses_internal m_meshNode[3]->setStartVertex(9); break; } + default: + break; } } } diff --git a/integration/TestContent/src/MultipleRenderTargetScene.cpp b/tests/integration/test-content/MultipleRenderTargetScene.cpp similarity index 80% rename from integration/TestContent/src/MultipleRenderTargetScene.cpp rename to tests/integration/test-content/MultipleRenderTargetScene.cpp index eca561461..52037f1f0 100644 --- a/integration/TestContent/src/MultipleRenderTargetScene.cpp +++ b/tests/integration/test-content/MultipleRenderTargetScene.cpp @@ -7,26 +7,26 @@ // ------------------------------------------------------------------------- #include "TestScenes/MultipleRenderTargetScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" #include -namespace ramses_internal +namespace ramses::internal { MultipleRenderTargetScene::MultipleRenderTargetScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : CommonRenderBufferTestScene(scene, cameraPosition, vpWidth, vpHeight) - , m_renderBuffer1(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) - , m_renderBuffer2(initRenderBuffer(scene, state)) - , m_depthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Depth, ramses::ERenderBufferFormat::Depth24, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_renderBuffer1(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_renderBuffer2(InitRenderBuffer(scene, state)) + , m_depthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth32, ramses::ERenderBufferAccessMode::ReadWrite)) { initClearPass(); @@ -57,12 +57,13 @@ namespace ramses_internal } } - ramses::RenderBuffer& MultipleRenderTargetScene::initRenderBuffer(ramses::Scene& scene, uint32_t state) + ramses::RenderBuffer& MultipleRenderTargetScene::InitRenderBuffer(ramses::Scene& scene, uint32_t state) { if (state == TWO_COLOR_BUFFERS_RGBA8_AND_RGBA4) - return *scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA4, ramses::ERenderBufferAccessMode::ReadWrite); - else - return *scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); + { + return *scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA4, ramses::ERenderBufferAccessMode::ReadWrite); + } + return *scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); } ramses::RenderTarget& MultipleRenderTargetScene::createMRTRenderTarget(uint32_t state) @@ -116,20 +117,13 @@ namespace ramses_internal const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(4u, textureCoordsArray.data()); ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - ramses::UniformInput colorInput; - effect->findUniformInput("u_color", colorInput); - appearance->setInputValue(colorInput, modulateColor); - - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); + appearance->setInputValue(*effect->findUniformInput("u_color"), modulateColor); // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -138,13 +132,11 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Nearest, renderBuffer); - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), *sampler); ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({translation.x, translation.y, translation.z}); @@ -167,7 +159,7 @@ namespace ramses_internal renderPass->setRenderTarget(&renderTarget); renderPass->setClearColor({1.f, 0.f, 1.f, 0.5f}); - renderPass->setClearFlags(ramses::EClearFlags::EClearFlags_All); + renderPass->setClearFlags(ramses::EClearFlag::All); } void MultipleRenderTargetScene::initMRTPass(uint32_t state) @@ -194,7 +186,7 @@ namespace ramses_internal ramses::RenderTarget& renderTarget = createMRTRenderTarget(state); renderPass.setRenderTarget(&renderTarget); - renderPass.setClearFlags(ramses::EClearFlags::EClearFlags_None); + renderPass.setClearFlags(ramses::EClearFlag::None); if (state == COLOR_WRITTEN_BY_TWO_DIFFERENT_RTS || state == DEPTH_WRITTEN_AND_USED_BY_DIFFERENT_RT) @@ -202,7 +194,7 @@ namespace ramses_internal camera.setViewport(0u, 0u, 8u, 16u); camera.setFrustum(camera.getLeftPlane() * 0.5f, camera.getRightPlane() * 0.5f, camera.getBottomPlane(), camera.getTopPlane(), camera.getNearPlane(), camera.getFarPlane()); - ramses::MeshNode& meshNode2 = createMesh(getMRTEffect(state), ramses::TriangleAppearance::EColor_Blue); + ramses::MeshNode& meshNode2 = createMesh(getMRTEffect(state), TriangleAppearance::EColor_Blue); transNode.addChild(meshNode2); meshNode2.getAppearance()->setDepthFunction(ramses::EDepthFunc::NotEqual); @@ -221,7 +213,7 @@ namespace ramses_internal ramses::RenderTarget& renderTarget2 = createMRTRenderTarget(state); renderPass2.setRenderTarget(&renderTarget2); - renderPass2.setClearFlags(ramses::EClearFlags::EClearFlags_None); + renderPass2.setClearFlags(ramses::EClearFlag::None); } } diff --git a/integration/TestContent/src/MultipleTrianglesScene.cpp b/tests/integration/test-content/MultipleTrianglesScene.cpp similarity index 83% rename from integration/TestContent/src/MultipleTrianglesScene.cpp rename to tests/integration/test-content/MultipleTrianglesScene.cpp index d077831b7..f2e0c5b56 100644 --- a/integration/TestContent/src/MultipleTrianglesScene.cpp +++ b/tests/integration/test-content/MultipleTrianglesScene.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "TestScenes/MultipleTrianglesScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/DataObject.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Camera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/DataObject.h" #include -namespace ramses_internal +namespace ramses::internal { MultipleTrianglesScene::MultipleTrianglesScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -27,24 +27,24 @@ namespace ramses_internal , m_meshNode5(nullptr) , m_meshNode6(nullptr) , m_meshNode7(nullptr) - , m_whiteTriangle(scene, *m_Effect, ramses::TriangleAppearance::EColor_White) - , m_redTriangle(scene, *m_Effect, ramses::TriangleAppearance::EColor_Red) - , m_greenTriangle(scene, *m_Effect, ramses::TriangleAppearance::EColor_Green) - , m_blueTriangle(scene, *m_Effect, ramses::TriangleAppearance::EColor_Blue) - , m_yellowLine(scene, *m_Effect, ramses::Line::EColor_Yellow, ramses::EDrawMode::Lines) - , m_whiteQuad(scene, *m_Effect, ramses::MultiTriangleGeometry::EColor_White) - , m_triangleFan(scene, *m_Effect, ramses::MultiTriangleGeometry::EColor_Red, 1.f, ramses::MultiTriangleGeometry::EGeometryType_TriangleFan) - , m_lineStrip (scene, *m_Effect, ramses::Line::EColor_Red, ramses::EDrawMode::LineStrip) - , m_linePoints (scene, *m_Effect, ramses::Line::EColor_White, ramses::EDrawMode::Points) - , m_redTransparentTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_Red, 0.6f) - , m_greenTransparentTriangle(scene, *m_Effect, ramses::TriangleAppearance::EColor_Green, 0.6f) - , m_blueTransparentTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_Blue, 0.6f) - , m_colorMaskRedTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_White) - , m_colorMaskGreenTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_White) - , m_colorMaskBlueTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_White) - , m_CCWTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_White, 1.f, ramses::TriangleGeometry::EVerticesOrder_CCW) - , m_CWTriangle (scene, *m_Effect, ramses::TriangleAppearance::EColor_White, 1.f, ramses::TriangleGeometry::EVerticesOrder_CW) - , m_CWTriangleCCWIndices (scene, *m_Effect, ramses::TriangleAppearance::EColor_White, 1.f, ramses::TriangleGeometry::EVerticesOrder_CW) + , m_whiteTriangle(scene, *m_Effect, TriangleAppearance::EColor_White) + , m_redTriangle(scene, *m_Effect, TriangleAppearance::EColor_Red) + , m_greenTriangle(scene, *m_Effect, TriangleAppearance::EColor_Green) + , m_blueTriangle(scene, *m_Effect, TriangleAppearance::EColor_Blue) + , m_yellowLine(scene, *m_Effect, Line::EColor_Yellow, ramses::EDrawMode::Lines) + , m_whiteQuad(scene, *m_Effect, MultiTriangleGeometry::EColor_White) + , m_triangleFan(scene, *m_Effect, MultiTriangleGeometry::EColor_Red, 1.f, MultiTriangleGeometry::EGeometryType_TriangleFan) + , m_lineStrip (scene, *m_Effect, Line::EColor_Red, ramses::EDrawMode::LineStrip) + , m_linePoints (scene, *m_Effect, Line::EColor_White, ramses::EDrawMode::Points) + , m_redTransparentTriangle (scene, *m_Effect, TriangleAppearance::EColor_Red, 0.6f) + , m_greenTransparentTriangle(scene, *m_Effect, TriangleAppearance::EColor_Green, 0.6f) + , m_blueTransparentTriangle (scene, *m_Effect, TriangleAppearance::EColor_Blue, 0.6f) + , m_colorMaskRedTriangle (scene, *m_Effect, TriangleAppearance::EColor_White) + , m_colorMaskGreenTriangle (scene, *m_Effect, TriangleAppearance::EColor_White) + , m_colorMaskBlueTriangle (scene, *m_Effect, TriangleAppearance::EColor_White) + , m_CCWTriangle (scene, *m_Effect, TriangleAppearance::EColor_White, 1.f, TriangleGeometry::EVerticesOrder_CCW) + , m_CWTriangle (scene, *m_Effect, TriangleAppearance::EColor_White, 1.f, TriangleGeometry::EVerticesOrder_CW) + , m_CWTriangleCCWIndices (scene, *m_Effect, TriangleAppearance::EColor_White, 1.f, TriangleGeometry::EVerticesOrder_CW) { m_colorMaskRedTriangle.GetAppearance().setColorWriteMask(false, true, true, true); m_colorMaskGreenTriangle.GetAppearance().setColorWriteMask(true, false, true, true); @@ -102,9 +102,9 @@ namespace ramses_internal m_meshNode1->setAppearance(m_redTransparentTriangle.GetAppearance()); m_meshNode2->setAppearance(m_greenTransparentTriangle.GetAppearance()); m_meshNode3->setAppearance(m_blueTransparentTriangle.GetAppearance()); - m_meshNode1->setGeometryBinding(m_redTransparentTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_greenTransparentTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_blueTransparentTriangle.GetGeometry()); + m_meshNode1->setGeometry(m_redTransparentTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_greenTransparentTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_blueTransparentTriangle.GetGeometry()); addMeshNodeToDefaultRenderGroup(*m_meshNode2, 1); addMeshNodeToDefaultRenderGroup(*m_meshNode3, 2); break; @@ -116,9 +116,9 @@ namespace ramses_internal m_meshNode1->setAppearance(m_redTriangle.GetAppearance()); m_meshNode2->setAppearance(m_greenTransparentTriangle.GetAppearance()); m_meshNode3->setAppearance(m_blueTransparentTriangle.GetAppearance()); - m_meshNode1->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_greenTransparentTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_blueTransparentTriangle.GetGeometry()); + m_meshNode1->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_greenTransparentTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_blueTransparentTriangle.GetGeometry()); addMeshNodeToDefaultRenderGroup(*m_meshNode2, 1); addMeshNodeToDefaultRenderGroup(*m_meshNode3, 2); break; @@ -132,9 +132,9 @@ namespace ramses_internal m_meshNode1->setAppearance(m_redTransparentTriangle.GetAppearance()); m_meshNode2->setAppearance(m_greenTransparentTriangle.GetAppearance()); m_meshNode3->setAppearance(m_blueTransparentTriangle.GetAppearance()); - m_meshNode1->setGeometryBinding(m_redTransparentTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_greenTransparentTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_blueTransparentTriangle.GetGeometry()); + m_meshNode1->setGeometry(m_redTransparentTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_greenTransparentTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_blueTransparentTriangle.GetGeometry()); addMeshNodeToDefaultRenderGroup(*m_meshNode2, 1); addMeshNodeToDefaultRenderGroup(*m_meshNode3, 2); break; @@ -145,7 +145,7 @@ namespace ramses_internal m_whiteTriangle.GetAppearance().setBlendingFactors(ramses::EBlendFactor::ConstColor, ramses::EBlendFactor::ConstAlpha, ramses::EBlendFactor::One, ramses::EBlendFactor::Zero); m_whiteTriangle.GetAppearance().setBlendingOperations(ramses::EBlendOperation::Add, ramses::EBlendOperation::Add); m_meshNode2->setAppearance(m_whiteTriangle.GetAppearance()); - m_meshNode2->setGeometryBinding(m_whiteTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_whiteTriangle.GetGeometry()); addMeshNodeToDefaultRenderGroup(*m_meshNode2, 1); break; case BLENDING_DST_COLOR_AND_ALPHA: @@ -156,9 +156,9 @@ namespace ramses_internal m_blueTriangle.GetAppearance().setBlendingOperations(ramses::EBlendOperation::Add, ramses::EBlendOperation::Add); m_blueTriangle.GetAppearance().setBlendingFactors(ramses::EBlendFactor::One, ramses::EBlendFactor::Zero, ramses::EBlendFactor::DstAlpha, ramses::EBlendFactor::Zero); m_meshNode2->setAppearance(m_whiteTriangle.GetAppearance()); - m_meshNode2->setGeometryBinding(m_whiteTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_whiteTriangle.GetGeometry()); m_meshNode3->setAppearance(m_blueTriangle.GetAppearance()); - m_meshNode3->setGeometryBinding(m_blueTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_blueTriangle.GetGeometry()); m_meshNode3->translate({0.f, -1.f, 0.f}); addMeshNodeToDefaultRenderGroup(*m_meshNode2, 1); addMeshNodeToDefaultRenderGroup(*m_meshNode3, 2); @@ -168,9 +168,9 @@ namespace ramses_internal m_meshNode1->setAppearance(m_colorMaskRedTriangle.GetAppearance()); m_meshNode2->setAppearance(m_colorMaskGreenTriangle.GetAppearance()); m_meshNode3->setAppearance(m_colorMaskBlueTriangle.GetAppearance()); - m_meshNode1->setGeometryBinding(m_colorMaskRedTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_colorMaskGreenTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_colorMaskBlueTriangle.GetGeometry()); + m_meshNode1->setGeometry(m_colorMaskRedTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_colorMaskGreenTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_colorMaskBlueTriangle.GetGeometry()); addMeshNodeToDefaultRenderGroup(*m_meshNode1, 2); addMeshNodeToDefaultRenderGroup(*m_meshNode2, 1); addMeshNodeToDefaultRenderGroup(*m_meshNode3, 0); @@ -196,9 +196,9 @@ namespace ramses_internal m_meshNode1->setAppearance(m_CCWTriangle.GetAppearance()); m_meshNode2->setAppearance(m_CWTriangle.GetAppearance()); m_meshNode3->setAppearance(m_CWTriangleCCWIndices.GetAppearance()); - m_meshNode1->setGeometryBinding(m_CCWTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_CWTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_CWTriangleCCWIndices.GetGeometry()); + m_meshNode1->setGeometry(m_CCWTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_CWTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_CWTriangleCCWIndices.GetGeometry()); break; case DEPTH_FUNC: m_redTriangle.GetAppearance().setDepthFunction(ramses::EDepthFunc::Greater); @@ -222,7 +222,7 @@ namespace ramses_internal m_meshNode2->setAppearance(m_greenTriangle.GetAppearance()); m_meshNode3->removeAppearanceAndGeometry(); m_meshNode3->setAppearance(m_linePoints.GetAppearance()); - m_meshNode3->setGeometryBinding(m_linePoints.GetGeometry()); + m_meshNode3->setGeometry(m_linePoints.GetGeometry()); m_meshNode4->setAppearance(m_whiteQuad.GetAppearance()); m_meshNode5->setAppearance(m_yellowLine.GetAppearance()); m_meshNode6->setAppearance(m_triangleFan.GetAppearance()); @@ -350,6 +350,8 @@ namespace ramses_internal addMeshNodeToDefaultRenderGroup(*m_meshNode5); addMeshNodeToDefaultRenderGroup(*m_meshNode6); break; + default: + break; } } @@ -358,23 +360,23 @@ namespace ramses_internal if (state == EULER_ROTATION_CONVENTIONS) { //use same geometry for all meshes - m_meshNode1->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode4->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode5->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode6->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode7->setGeometryBinding(m_redTriangle.GetGeometry()); + m_meshNode1->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode4->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode5->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode6->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode7->setGeometry(m_redTriangle.GetGeometry()); } else { - m_meshNode1->setGeometryBinding(m_redTriangle.GetGeometry()); - m_meshNode2->setGeometryBinding(m_greenTriangle.GetGeometry()); - m_meshNode3->setGeometryBinding(m_blueTriangle.GetGeometry()); - m_meshNode4->setGeometryBinding(m_whiteQuad.GetGeometry()); - m_meshNode5->setGeometryBinding(m_yellowLine.GetGeometry()); - m_meshNode6->setGeometryBinding(m_triangleFan.GetGeometry()); - m_meshNode7->setGeometryBinding(m_lineStrip.GetGeometry()); + m_meshNode1->setGeometry(m_redTriangle.GetGeometry()); + m_meshNode2->setGeometry(m_greenTriangle.GetGeometry()); + m_meshNode3->setGeometry(m_blueTriangle.GetGeometry()); + m_meshNode4->setGeometry(m_whiteQuad.GetGeometry()); + m_meshNode5->setGeometry(m_yellowLine.GetGeometry()); + m_meshNode6->setGeometry(m_triangleFan.GetGeometry()); + m_meshNode7->setGeometry(m_lineStrip.GetGeometry()); } } diff --git a/tests/integration/test-content/RenderBufferScene.cpp b/tests/integration/test-content/RenderBufferScene.cpp new file mode 100644 index 000000000..af0c9423d --- /dev/null +++ b/tests/integration/test-content/RenderBufferScene.cpp @@ -0,0 +1,178 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "TestScenes/RenderBufferScene.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" +#include + +namespace ramses::internal +{ + RenderBufferScene::RenderBufferScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) + : CommonRenderBufferTestScene(scene, cameraPosition) + , m_readWriteColorRenderBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_writeOnlyDepthBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth32, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_writeOnlyDepthStencilBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_writeOnlyDepthBuffer16(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth16, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_writeOnlyDepthBuffer24(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth24, ramses::ERenderBufferAccessMode::WriteOnly)) + , m_readWriteDepthBuffer16(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth16, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_readWriteDepthBuffer24(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth24, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_readWriteDepthBuffer32(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::Depth32, ramses::ERenderBufferAccessMode::ReadWrite)) + { + initClearPass(state); + initRenderingPass(state); + + switch (state) + { + case ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH16: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH24: + addRenderPassUsingRenderBufferAsQuadTexture(createQuadWithTexture(m_readWriteColorRenderBuffer)); + break; + case READ_WRITE_DEPTH16: + addRenderPassUsingRenderBufferAsQuadTexture(createQuadWithTexture(m_readWriteDepthBuffer16)); + break; + case READ_WRITE_DEPTH24: + addRenderPassUsingRenderBufferAsQuadTexture(createQuadWithTexture(m_readWriteDepthBuffer24)); + break; + case READ_WRITE_DEPTH32: + addRenderPassUsingRenderBufferAsQuadTexture(createQuadWithTexture(m_readWriteDepthBuffer32)); + break; + default: + assert(false); + break; + } + } + + ramses::RenderTarget& RenderBufferScene::createRenderTarget(uint32_t state) + { + ramses::RenderTargetDescription rtDesc; + + switch (state) + { + case ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + break; + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_writeOnlyDepthBuffer); + break; + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_writeOnlyDepthStencilBuffer); + break; + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH16: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_writeOnlyDepthBuffer16); + break; + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH24: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_writeOnlyDepthBuffer24); + break; + case READ_WRITE_DEPTH16: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_readWriteDepthBuffer16); + break; + case READ_WRITE_DEPTH24: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_readWriteDepthBuffer24); + break; + case READ_WRITE_DEPTH32: + rtDesc.addRenderBuffer(m_readWriteColorRenderBuffer); + rtDesc.addRenderBuffer(m_readWriteDepthBuffer32); + break; + default: + assert(false); + break; + } + + return *m_scene.createRenderTarget(rtDesc); + } + + void RenderBufferScene::initClearPass(uint32_t state) + { + ramses::RenderPass* renderPass = m_scene.createRenderPass(); + renderPass->setRenderOrder(-100); + renderPass->setCamera(createCamera()); + + ramses::RenderTarget& renderTarget = createRenderTarget(state); + + renderPass->setRenderTarget(&renderTarget); + renderPass->setClearColor({1.f, 0.f, 1.f, 0.5f}); + renderPass->setClearFlags(ramses::EClearFlag::All); + } + + void RenderBufferScene::initRenderingPass(uint32_t state) + { + ramses::MeshNode& meshNode = createMesh(getEffectRenderOneBuffer()); + + //fill stencil buffer with value of 1 for every fragment that gets rendered into + meshNode.getAppearance()->setStencilFunction(ramses::EStencilFunc::Always, 1, 0xff); + meshNode.getAppearance()->setStencilOperation(ramses::EStencilOperation::Replace, ramses::EStencilOperation::Replace, ramses::EStencilOperation::Replace); + + ramses::Node& transNode = *m_scene.createNode(); + transNode.addChild(meshNode); + transNode.translate({0.0f, -0.5f, -5.0f}); + + ramses::RenderGroup& renderGroup = *m_scene.createRenderGroup(); + renderGroup.addMeshNode(meshNode); + + ramses::RenderPass& renderPass = *m_scene.createRenderPass(); + renderPass.setRenderOrder(0); + renderPass.addRenderGroup(renderGroup); + + ramses::PerspectiveCamera& camera = createCamera(); + renderPass.setCamera(camera); + + ramses::RenderTarget& renderTarget = createRenderTarget(state); + renderPass.setRenderTarget(&renderTarget); + renderPass.setClearFlags(ramses::EClearFlag::None); + + float zTranslate = -0.1f; + switch (state) + { + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER: + zTranslate = 0.1f; + break; + case READ_WRITE_DEPTH16: + case READ_WRITE_DEPTH24: + case READ_WRITE_DEPTH32: + zTranslate = 3.f; // move closer to camera for better contrast with far plane (displayed as full red color) + break; + case ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH16: + case ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH24: + break; + default: + assert(false); + break; + } + + ramses::Node& farTriangleTransNode = *m_scene.createNode(); + farTriangleTransNode.translate({ 0.5f, 0.0f, zTranslate }); + + ramses::MeshNode& meshNode2 = createMesh(getEffectRenderOneBuffer(), TriangleAppearance::EColor_Blue); + farTriangleTransNode.addChild(meshNode2); + transNode.addChild(farTriangleTransNode); + + meshNode2.getAppearance()->setDepthFunction(ramses::EDepthFunc::LessEqual); + meshNode2.getAppearance()->setStencilFunction(ramses::EStencilFunc::NotEqual, 0u, 0xff); + renderGroup.addMeshNode(meshNode2); + + renderGroup.addMeshNode(meshNode2); + } +} diff --git a/integration/TestContent/src/RenderPassClearScene.cpp b/tests/integration/test-content/RenderPassClearScene.cpp similarity index 76% rename from integration/TestContent/src/RenderPassClearScene.cpp rename to tests/integration/test-content/RenderPassClearScene.cpp index 11c4c2a0d..ecea31b22 100644 --- a/integration/TestContent/src/RenderPassClearScene.cpp +++ b/tests/integration/test-content/RenderPassClearScene.cpp @@ -6,32 +6,32 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/MeshNode.h" #include "TestScenes/RenderPassClearScene.h" -namespace ramses_internal +namespace ramses::internal { RenderPassClearScene::RenderPassClearScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) , m_effect(*getTestEffect("ramses-test-client-basic")) - , m_blueTriangle(scene, m_effect, ramses::TriangleAppearance::EColor_Blue) - , m_redTriangle(scene, m_effect, ramses::TriangleAppearance::EColor_Red) - , m_greenTriangle(scene, m_effect, ramses::TriangleAppearance::EColor_Green) - , m_colorBuffer(*m_scene.createRenderBuffer(200u, 200u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) - , m_depthStencilBuffer(*m_scene.createRenderBuffer(200u, 200u, ramses::ERenderBufferType::DepthStencil, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_blueTriangle(scene, m_effect, TriangleAppearance::EColor_Blue) + , m_redTriangle(scene, m_effect, TriangleAppearance::EColor_Red) + , m_greenTriangle(scene, m_effect, TriangleAppearance::EColor_Green) + , m_colorBuffer(*m_scene.createRenderBuffer(200u, 200u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_depthStencilBuffer(*m_scene.createRenderBuffer(200u, 200u, ramses::ERenderBufferFormat::Depth24_Stencil8, ramses::ERenderBufferAccessMode::ReadWrite)) { ramses::RenderTarget& renderTarget = createRenderTarget(); const ramses::PerspectiveCamera& camera = createCamera(); @@ -49,7 +49,7 @@ namespace ramses_internal blueRenderPass.setRenderOrder(0); blueRenderPass.setClearColor({1.f, 0.f, 1.f, 1.f}); - blueRenderPass.setClearFlags(ramses::EClearFlags_All); + blueRenderPass.setClearFlags(ramses::EClearFlag::All); blueRenderPass.addRenderGroup(blueRenderGroup); blueRenderPass.setCamera(camera); blueRenderPass.setRenderTarget(&renderTarget); @@ -67,7 +67,7 @@ namespace ramses_internal redRenderPass.setRenderOrder(1); redRenderPass.setClearColor({0.f, 0.f, 0.f, 1.f}); - redRenderPass.setClearFlags(state); // state will contain all combinations of clear flags tested + redRenderPass.setClearFlags(ClearFlags(state)); // state will contain all combinations of clear flags tested redRenderPass.addRenderGroup(redRenderGroup); redRenderPass.setCamera(camera); redRenderPass.setRenderTarget(&renderTarget); @@ -84,7 +84,7 @@ namespace ramses_internal addTriangleMesh(m_greenTriangle, greenRenderGroup, 0.0f, 0.0f, -2.2f); greenRenderPass.setRenderOrder(2); - greenRenderPass.setClearFlags(ramses::EClearFlags_None); + greenRenderPass.setClearFlags(ramses::EClearFlag::None); greenRenderPass.addRenderGroup(greenRenderGroup); greenRenderPass.setCamera(camera); greenRenderPass.setRenderTarget(&renderTarget); @@ -116,11 +116,11 @@ namespace ramses_internal renderGroup.addMeshNode(quad); } - void RenderPassClearScene::addTriangleMesh(ramses::Triangle& triangle, ramses::RenderGroup& targetGroup, float x, float y, float z) + void RenderPassClearScene::addTriangleMesh(Triangle& triangle, ramses::RenderGroup& targetGroup, float x, float y, float z) { ramses::MeshNode& meshNode = *m_scene.createMeshNode(); meshNode.setAppearance(triangle.GetAppearance()); - meshNode.setGeometryBinding(triangle.GetGeometry()); + meshNode.setGeometry(triangle.GetGeometry()); ramses::Node* translateNode = m_scene.createNode(); translateNode->addChild(meshNode); @@ -165,16 +165,11 @@ namespace ramses_internal ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -183,13 +178,11 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Nearest, renderBuffer); - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), *sampler); ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.f, 0.f, -2.5f}); diff --git a/integration/TestContent/src/RenderPassOnceScene.cpp b/tests/integration/test-content/RenderPassOnceScene.cpp similarity index 73% rename from integration/TestContent/src/RenderPassOnceScene.cpp rename to tests/integration/test-content/RenderPassOnceScene.cpp index 43e6c5a27..bdf6395a3 100644 --- a/integration/TestContent/src/RenderPassOnceScene.cpp +++ b/tests/integration/test-content/RenderPassOnceScene.cpp @@ -7,29 +7,29 @@ // ------------------------------------------------------------------------- #include "TestScenes/RenderPassOnceScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" #include -namespace ramses_internal +namespace ramses::internal { RenderPassOnceScene::RenderPassOnceScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) , m_camera(*scene.createOrthographicCamera()) , m_renderPass(*m_scene.createRenderPass()) - , m_renderBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) + , m_renderBuffer(*scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite)) { m_camera.setFrustum(-1.f, 1.f, -1.f, 1.f, 1.f, 10.f); m_camera.setViewport(0u, 0u, 16u, 16u); @@ -64,9 +64,9 @@ namespace ramses_internal void RenderPassOnceScene::initInputRenderPass() { ramses::MeshNode* meshNode = m_scene.createMeshNode(); - ramses::Triangle blueTriangle(m_scene, *getTestEffect("ramses-test-client-basic"), ramses::TriangleAppearance::EColor_Blue); + Triangle blueTriangle(m_scene, *getTestEffect("ramses-test-client-basic"), TriangleAppearance::EColor_Blue); meshNode->setAppearance(blueTriangle.GetAppearance()); - meshNode->setGeometryBinding(blueTriangle.GetGeometry()); + meshNode->setGeometry(blueTriangle.GetGeometry()); ramses::Node* translateNode = m_scene.createNode(); translateNode->addChild(*meshNode); @@ -80,7 +80,7 @@ namespace ramses_internal rtDesc.addRenderBuffer(m_renderBuffer); ramses::RenderTarget* renderTarget = m_scene.createRenderTarget(rtDesc); m_renderPass.setRenderTarget(renderTarget); - m_renderPass.setClearFlags(ramses::EClearFlags::EClearFlags_All); + m_renderPass.setClearFlags(ramses::EClearFlag::All); m_renderPass.setRenderOrder(-1); } @@ -103,16 +103,11 @@ namespace ramses_internal ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -121,13 +116,11 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Nearest, m_renderBuffer); - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), *sampler); ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.f, 0.f, -4.f}); diff --git a/integration/TestContent/src/RenderPassScene.cpp b/tests/integration/test-content/RenderPassScene.cpp similarity index 88% rename from integration/TestContent/src/RenderPassScene.cpp rename to tests/integration/test-content/RenderPassScene.cpp index 90d506a1e..c64cdfa4d 100644 --- a/integration/TestContent/src/RenderPassScene.cpp +++ b/tests/integration/test-content/RenderPassScene.cpp @@ -7,25 +7,25 @@ // ------------------------------------------------------------------------- #include "TestScenes/RenderPassScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/PerspectiveCamera.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/PerspectiveCamera.h" -namespace ramses_internal +namespace ramses::internal { RenderPassScene::RenderPassScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) , m_effect(*getTestEffect("ramses-test-client-basic")) - , m_blueTriangle(scene, m_effect, ramses::TriangleAppearance::EColor_Blue) - , m_whiteTriangle(scene, m_effect, ramses::TriangleAppearance::EColor_White) + , m_blueTriangle(scene, m_effect, TriangleAppearance::EColor_Blue) + , m_whiteTriangle(scene, m_effect, TriangleAppearance::EColor_White) { ramses::MeshNode* meshNode1 = m_scene.createMeshNode(); ramses::MeshNode* meshNode2 = m_scene.createMeshNode(); meshNode1->setAppearance(m_blueTriangle.GetAppearance()); meshNode2->setAppearance(m_whiteTriangle.GetAppearance()); - meshNode1->setGeometryBinding(m_blueTriangle.GetGeometry()); - meshNode2->setGeometryBinding(m_whiteTriangle.GetGeometry()); + meshNode1->setGeometry(m_blueTriangle.GetGeometry()); + meshNode2->setGeometry(m_whiteTriangle.GetGeometry()); ramses::Node* translateNode = m_scene.createNode(); translateNode->addChild(*meshNode1); @@ -41,8 +41,8 @@ namespace ramses_internal ramses::RenderPass* renderPass1 = m_scene.createRenderPass(); ramses::RenderPass* renderPass2 = m_scene.createRenderPass(); - renderPass1->setClearFlags(ramses::EClearFlags_None); - renderPass2->setClearFlags(ramses::EClearFlags_None); + renderPass1->setClearFlags(ramses::EClearFlag::None); + renderPass2->setClearFlags(ramses::EClearFlag::None); ramses::RenderGroup* renderGroup1 = m_scene.createRenderGroup(); ramses::RenderGroup* renderGroup2 = m_scene.createRenderGroup(); @@ -110,6 +110,7 @@ namespace ramses_internal break; case PASSES_WITH_LEFT_AND_RIGHT_VIEWPORT: + { ramses::PerspectiveCamera* cameraLeft = m_scene.createPerspectiveCamera(); cameraLeft->setFrustum(20.f, IntegrationScene::DefaultViewportWidth * 0.5f / IntegrationScene::DefaultViewportHeight, 0.1f, 100.f ); cameraLeft->setViewport(0u, 0u, IntegrationScene::DefaultViewportWidth / 2u, IntegrationScene::DefaultViewportHeight); @@ -124,5 +125,8 @@ namespace ramses_internal renderGroup2->addMeshNode(*meshNode2); break; } + default: + break; + } } } diff --git a/integration/TestContent/src/RenderTargetScene.cpp b/tests/integration/test-content/RenderTargetScene.cpp similarity index 79% rename from integration/TestContent/src/RenderTargetScene.cpp rename to tests/integration/test-content/RenderTargetScene.cpp index 01607c54d..d7f1094ad 100644 --- a/integration/TestContent/src/RenderTargetScene.cpp +++ b/tests/integration/test-content/RenderTargetScene.cpp @@ -7,23 +7,23 @@ // ------------------------------------------------------------------------- #include "TestScenes/RenderTargetScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderTargetDescription.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Effect.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderTargetDescription.h" #include -namespace ramses_internal +namespace ramses::internal { RenderTargetScene::RenderTargetScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -85,7 +85,7 @@ namespace ramses_internal break; } - const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, bufferFormat, ramses::ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(16u, 16u, bufferFormat, ramses::ERenderBufferAccessMode::ReadWrite); assert(renderBuffer != nullptr); return *renderBuffer; } @@ -134,15 +134,15 @@ namespace ramses_internal ramses::MeshNode* meshNode = m_scene.createMeshNode(); if (state == PERSPECTIVE_PROJECTION || state == ORTHOGRAPHIC_PROJECTION) { - ramses::Triangle blueTriangle(m_scene, *getTestEffect("ramses-test-client-basic"), ramses::TriangleAppearance::EColor_Blue); + Triangle blueTriangle(m_scene, *getTestEffect("ramses-test-client-basic"), TriangleAppearance::EColor_Blue); meshNode->setAppearance(blueTriangle.GetAppearance()); - meshNode->setGeometryBinding(blueTriangle.GetGeometry()); + meshNode->setGeometry(blueTriangle.GetGeometry()); } else { - ramses::Triangle greyTriangle(m_scene, *getTestEffect("ramses-test-client-basic"), ramses::TriangleAppearance::EColor_Grey); + Triangle greyTriangle(m_scene, *getTestEffect("ramses-test-client-basic"), TriangleAppearance::EColor_Grey); meshNode->setAppearance(greyTriangle.GetAppearance()); - meshNode->setGeometryBinding(greyTriangle.GetGeometry()); + meshNode->setGeometry(greyTriangle.GetGeometry()); } ramses::Node* translateNode = m_scene.createNode(); @@ -161,7 +161,7 @@ namespace ramses_internal ramses::RenderTarget* renderTarget = m_scene.createRenderTarget(rtDesc); renderPass->setRenderTarget(renderTarget); renderPass->setClearColor({1.f, 0.f, 1.f, 0.5f}); - renderPass->setClearFlags(ramses::EClearFlags::EClearFlags_All); + renderPass->setClearFlags(ramses::EClearFlag::All); } void RenderTargetScene::initFinalRenderPass() @@ -183,16 +183,11 @@ namespace ramses_internal ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "triangle geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -201,13 +196,11 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Nearest, m_renderBuffer); - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), *sampler); ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.f, 0.f, -4.f}); diff --git a/integration/TestContent/src/SceneFromPath.cpp b/tests/integration/test-content/SceneFromPath.cpp similarity index 93% rename from integration/TestContent/src/SceneFromPath.cpp rename to tests/integration/test-content/SceneFromPath.cpp index 43befae91..cf73e0497 100644 --- a/integration/TestContent/src/SceneFromPath.cpp +++ b/tests/integration/test-content/SceneFromPath.cpp @@ -8,11 +8,11 @@ #include "TestScenes/SceneFromPath.h" -#include "ramses-client-api/RamsesClient.h" +#include "ramses/client/RamsesClient.h" #include -namespace ramses_internal +namespace ramses::internal { SceneFromPath::SceneFromPath(ramses::RamsesClient& ramsesClient, const std::string& folder, const std::string& fileName) { diff --git a/integration/TestContent/src/ShaderTestScene.cpp b/tests/integration/test-content/ShaderTestScene.cpp similarity index 52% rename from integration/TestContent/src/ShaderTestScene.cpp rename to tests/integration/test-content/ShaderTestScene.cpp index 0c48010ab..d584672ac 100644 --- a/integration/TestContent/src/ShaderTestScene.cpp +++ b/tests/integration/test-content/ShaderTestScene.cpp @@ -7,28 +7,28 @@ // ------------------------------------------------------------------------- #include "TestScenes/ShaderTestScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Texture2D.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Texture2D.h" #include -namespace ramses_internal +namespace ramses::internal { ShaderTestScene::ShaderTestScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) - , m_effect(*getTestEffect(getEffectNameFromState(state))) - , m_triangle(scene, m_effect, ramses::TriangleAppearance::EColor_Red) + , m_effect(*getTestEffect(GetEffectNameFromState(state))) + , m_triangle(scene, m_effect, TriangleAppearance::EColor_Red) { ramses::MeshNode* meshNode = m_scene.createMeshNode("red triangle mesh node"); addMeshNodeToDefaultRenderGroup(*meshNode); meshNode->setAppearance(m_triangle.GetAppearance()); - meshNode->setGeometryBinding(m_triangle.GetGeometry()); + meshNode->setGeometry(m_triangle.GetGeometry()); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.f, 0.f, -12.f}); @@ -37,7 +37,7 @@ namespace ramses_internal initInputs(state); } - std::string ShaderTestScene::getEffectNameFromState(uint32_t state) const + std::string ShaderTestScene::GetEffectNameFromState(uint32_t state) { switch (state) { @@ -63,26 +63,26 @@ namespace ramses_internal void ShaderTestScene::initInputs(uint32_t state) { ramses::Appearance& appearance = m_triangle.GetAppearance(); - ramses::UniformInput input; + std::optional optInput; if (state == OPTIMIZED_INPUT) { - ramses::status_t status = m_effect.findUniformInput("zombieUniform", input); - assert(status == ramses::StatusOK); - if (status == ramses::StatusOK) + optInput = m_effect.findUniformInput("zombieUniform"); + assert(optInput.has_value()); + if (optInput.has_value()) { - status = appearance.setInputValue(input, 10.f); - assert(status == ramses::StatusOK); + [[maybe_unused]] bool status = appearance.setInputValue(*optInput, 10.f); + assert(status); } } else if (state == UNIFORM_WITH_SAME_NAME_IN_BOTH_STAGES) { - ramses::status_t status = m_effect.findUniformInput("multiStageUniform", input); - assert(status == ramses::StatusOK); - if (status == ramses::StatusOK) + optInput = m_effect.findUniformInput("multiStageUniform"); + assert(optInput.has_value()); + if (optInput.has_value()) { - status = appearance.setInputValue(input, 1.f); - assert(status == ramses::StatusOK); + [[maybe_unused]] bool status = appearance.setInputValue(*optInput, 1.f); + assert(status); } } else if (state == STRUCT_UNIFORM) @@ -91,23 +91,22 @@ namespace ramses_internal // The shader itself is a combination of nested structs and arrays. // Set the specific values for rendering - this will result in a pink-ish color - ramses::status_t status = m_effect.findUniformInput("inputVar.data[0].red", input); - UNUSED(status); // Needed by release build - assert(ramses::StatusOK == status); - status = appearance.setInputValue(input, 0.8f); - assert(ramses::StatusOK == status); - status = m_effect.findUniformInput("inputVar.data[0].green", input); - assert(ramses::StatusOK == status); - status = appearance.setInputValue(input, 0.2f); - assert(ramses::StatusOK == status); - status = m_effect.findUniformInput("inputVar.data[1].blue", input); - assert(ramses::StatusOK == status); - status = appearance.setInputValue(input, 0.75f); - assert(ramses::StatusOK == status); - status = m_effect.findUniformInput("inputVar.data[1].alpha", input); - assert(ramses::StatusOK == status); - status = appearance.setInputValue(input, 0.9f); - assert(ramses::StatusOK == status); + optInput = m_effect.findUniformInput("inputVar.data[0].red"); + assert(optInput.has_value()); + [[maybe_unused]] bool status = appearance.setInputValue(*optInput, 0.8f); + assert(status); + optInput = m_effect.findUniformInput("inputVar.data[0].green"); + assert(optInput.has_value()); + status = appearance.setInputValue(*optInput, 0.2f); + assert(status); + optInput = m_effect.findUniformInput("inputVar.data[1].blue"); + assert(optInput.has_value()); + status = appearance.setInputValue(*optInput, 0.75f); + assert(status); + optInput = m_effect.findUniformInput("inputVar.data[1].alpha"); + assert(optInput.has_value()); + status = appearance.setInputValue(*optInput, 0.9f); + assert(status); } else if (state == TEXTURE_SIZE) { @@ -117,35 +116,37 @@ namespace ramses_internal const std::array textureCoordsArray{ ramses::vec2f{0.f, 1.f}, ramses::vec2f{1.f, 1.f}, ramses::vec2f{0.f, 0.f} }; const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(3u, textureCoordsArray.data()); - ramses::AttributeInput texCoordsInput; - m_effect.findAttributeInput("a_texCoords", texCoordsInput); - m_triangle.GetGeometry().setInputBuffer(texCoordsInput, *textureCoords); + m_triangle.GetGeometry().setInputBuffer(*m_effect.findAttributeInput("a_texCoords"), *textureCoords); // Adjust the vertices, such that the triangle texture looks less skewed const std::array vertexPositionsData{ ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{1.f, 0.f, -1.f}, ramses::vec3f{-1.f, 1.f, -1.f} }; const ramses::ArrayResource* vertexPositions = m_scene.createArrayResource(3u, vertexPositionsData.data()); - ramses::AttributeInput positionsInput; - m_effect.findAttributeInput("a_position", positionsInput); - m_triangle.GetGeometry().setInputBuffer(positionsInput, *vertexPositions); - - ramses::UniformInput texInput; - appearance.getEffect().findUniformInput("u_texture", texInput); - appearance.setInputTexture(texInput, texSampler); + m_triangle.GetGeometry().setInputBuffer(*m_effect.findAttributeInput("a_position"), *vertexPositions); + appearance.setInputTexture(*appearance.getEffect().findUniformInput("u_texture"), texSampler); // Pass in the texture size as a uniform, which allows us to compare against the output of the "textureSize" method in the shader. - appearance.getEffect().findUniformInput("texSizeFromApplication", texInput); - appearance.setInputValue(texInput, ramses::vec2i{ static_cast(texture.getWidth()), static_cast(texture.getHeight()) }); + appearance.setInputValue(*appearance.getEffect().findUniformInput("texSizeFromApplication"), + ramses::vec2i{static_cast(texture.getWidth()), static_cast(texture.getHeight())}); } else if (state == BOOL_UNIFORM) { // Tests that bool uniforms work - ramses::status_t status = m_effect.findUniformInput("makeSemiTransparent", input); - assert(status == ramses::StatusOK); - if (status == ramses::StatusOK) + optInput = m_effect.findUniformInput("makeSemiTransparent"); + assert(optInput.has_value()); + if (optInput.has_value()) + { + [[maybe_unused]] bool status = appearance.setInputValue(*optInput, true); + assert(status); + } + + optInput = m_effect.findUniformInput("colorCh"); + assert(optInput.has_value()); + if (optInput.has_value()) { - status = appearance.setInputValue(input, 1); - assert(status == ramses::StatusOK); + std::array colorCh = {true, false, false}; + [[maybe_unused]] bool status = appearance.setInputValue(*optInput, 3, colorCh.data()); + assert(status); } } } diff --git a/integration/TestContent/src/SingleAppearanceScene.cpp b/tests/integration/test-content/SingleAppearanceScene.cpp similarity index 67% rename from integration/TestContent/src/SingleAppearanceScene.cpp rename to tests/integration/test-content/SingleAppearanceScene.cpp index e3d8a3b6a..710f9d2ce 100644 --- a/integration/TestContent/src/SingleAppearanceScene.cpp +++ b/tests/integration/test-content/SingleAppearanceScene.cpp @@ -8,14 +8,14 @@ #include "TestScenes/SingleAppearanceScene.h" #include "TestScenes/Triangle.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" -namespace ramses_internal +namespace ramses::internal { SingleAppearanceScene::SingleAppearanceScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) @@ -23,7 +23,7 @@ namespace ramses_internal { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); - ramses::Triangle redTriangle(m_scene, *effect, ramses::TriangleAppearance::EColor_Red); + Triangle redTriangle(m_scene, *effect, TriangleAppearance::EColor_Red); const std::array translation = { -1.0f, 0.0f, -12.0f, @@ -48,43 +48,43 @@ namespace ramses_internal switch (state) { case RED_TRIANGLES: - setTriangleColor(redTriangle.GetAppearance(), *effect, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + SetTriangleColor(redTriangle.GetAppearance(), *effect, glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); break; case GREEN_TRIANGLES: - setTriangleColor(redTriangle.GetAppearance(), *effect, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + SetTriangleColor(redTriangle.GetAppearance(), *effect, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); break; case BLUE_TRIANGLES: - setTriangleColor(redTriangle.GetAppearance(), *effect, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + SetTriangleColor(redTriangle.GetAppearance(), *effect, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); break; case CHANGE_APPEARANCE: + { ramses::Effect* effectExt = getTestEffect("ramses-test-client-basic-extended"); - ramses::Triangle blueTriangle(m_scene, *effectExt, ramses::TriangleAppearance::EColor_Blue); + Triangle blueTriangle(m_scene, *effectExt, TriangleAppearance::EColor_Blue); ramses::Appearance& appearance = blueTriangle.GetAppearance(); - ramses::UniformInput redgreenOffset; - effectExt->findUniformInput("redgreen_offset", redgreenOffset); - appearance.setInputValue(redgreenOffset, ramses::vec2f{ 0.5f, 0.5f }); + appearance.setInputValue(*effectExt->findUniformInput("redgreen_offset"), ramses::vec2f{ 0.5f, 0.5f }); setAppearanceAndGeometryToAllMeshNodes(blueTriangle.GetAppearance(), blueTriangle.GetGeometry()); - setTriangleColor(blueTriangle.GetAppearance(), *effectExt, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + SetTriangleColor(blueTriangle.GetAppearance(), *effectExt, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + break; + } + default: break; } } - void SingleAppearanceScene::setAppearanceAndGeometryToAllMeshNodes(ramses::Appearance& appearance, ramses::GeometryBinding& geometry) + void SingleAppearanceScene::setAppearanceAndGeometryToAllMeshNodes(ramses::Appearance& appearance, ramses::Geometry& geometry) { for (auto meshNode : m_meshNodes) { meshNode->removeAppearanceAndGeometry(); meshNode->setAppearance(appearance); - meshNode->setGeometryBinding(geometry); + meshNode->setGeometry(geometry); } } - void SingleAppearanceScene::setTriangleColor(ramses::Appearance& appearance, const ramses::Effect& effect, const glm::vec4& color) + void SingleAppearanceScene::SetTriangleColor(ramses::Appearance& appearance, const ramses::Effect& effect, const glm::vec4& color) { - ramses::UniformInput colorInput; - effect.findUniformInput("color", colorInput); - appearance.setInputValue(colorInput, color); + appearance.setInputValue(*effect.findUniformInput("color"), color); } } diff --git a/tests/integration/test-content/StreamTextureScene.cpp b/tests/integration/test-content/StreamTextureScene.cpp new file mode 100644 index 000000000..26bfe4547 --- /dev/null +++ b/tests/integration/test-content/StreamTextureScene.cpp @@ -0,0 +1,156 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "TestScenes/StreamTextureScene.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" +#include + +namespace ramses::internal +{ + StreamTextureScene::StreamTextureScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) + : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) + , m_effect(nullptr) + { + m_effect = getTestEffect("ramses-test-client-textured"); + m_root = m_scene.createNode(); + + const ramses::vec3f A{ -0.5f, 0.5f, -0.5f }; + const ramses::vec3f B{ -0.5f, -0.5f, -0.5f }; + const ramses::vec3f C{ 0.5f, -0.5f, -0.5f }; + const ramses::vec3f D{ 0.5f, 0.5f, -0.5f }; + const ramses::vec3f E{ -0.5f, 0.5f, 0.5f }; + const ramses::vec3f F{ -0.5f, -0.5f, 0.5f }; + const ramses::vec3f G{ 0.5f, -0.5f, 0.5f }; + const ramses::vec3f H{ 0.5f, 0.5f, 0.5f }; + + using QuadVerts = std::array; + const QuadVerts verts1{{A, B, C, D}}; + const QuadVerts verts2{{C, G, H, D}}; + const QuadVerts verts3{{H, E, A, D}}; + const QuadVerts verts4{{F, G, C, B}}; + const QuadVerts verts5{{F, B, A, E}}; + const QuadVerts verts6{{F, E, H, G}}; + + switch (state) + { + case MULTI_SOURCE_SAME_FALLBACK_TEXTURE: + { + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts1, TextureConsumers[0]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[1]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts3, TextureConsumers[2]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts4, TextureConsumers[3]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts5, TextureConsumers[4]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts6, TextureConsumers[5]); + break; + } + case SAME_SOURCE_MULTI_FALLBACK: + { + addPngQuad("res/ramses-test-client-embedded-compositing-1.png", verts1, TextureConsumers[0]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[1]); + addPngQuad("res/ramses-test-client-embedded-compositing-3.png", verts3, TextureConsumers[2]); + addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[3]); + addPngQuad("res/ramses-test-client-embedded-compositing-5.png", verts5, TextureConsumers[4]); + addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[5]); + break; + } + case MULTI_SOURCE_MULTI_FALLBACK: + { + //Create two separate half cubes which are visible from the camera position + //Each half cube is formed from 3 surfaces that uses vertex arrays 2, 4 and 6 + ramses::Node* translateLeftNode = m_scene.createNode("translate left node"); + translateLeftNode->translate({-1.0f, 0.0f, 0.0f}); + m_root->addChild(*translateLeftNode); + + ramses::Node* translateRightNode = m_scene.createNode("translate right node"); + translateRightNode->translate({1.0f, 0.0f, 0.0f}); + m_root->addChild(*translateRightNode); + + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[0], translateLeftNode); + addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[1], translateLeftNode); + addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[2], translateLeftNode); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[3], translateRightNode); + addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[4], translateRightNode); + addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[5], translateRightNode); + break; + + } + case INITIAL_STATE: + { + addPngQuad("res/ramses-test-client-embedded-compositing-1.png", verts1, TextureConsumers[0]); + addPngQuad("res/ramses-test-client-embedded-compositing-2.png", verts2, TextureConsumers[1]); + addPngQuad("res/ramses-test-client-embedded-compositing-3.png", verts3, TextureConsumers[2]); + addPngQuad("res/ramses-test-client-embedded-compositing-4.png", verts4, TextureConsumers[3]); + addPngQuad("res/ramses-test-client-embedded-compositing-5.png", verts5, TextureConsumers[4]); + addPngQuad("res/ramses-test-client-embedded-compositing-6.png", verts6, TextureConsumers[5]); + break; + } + default: + break; + } + + m_root->setRotation({-35.264385f, -45.000427f, 0.000427f}, ramses::ERotationType::Euler_XYZ); // rotate the cube onto one corner + } + + void StreamTextureScene::addPngQuad(const char* pngFilePath, const std::array& vertexPositionsArray, ramses::dataConsumerId_t consumerId, ramses::Node* parentNode) + { + const ramses::Texture2D* texture = ramses::RamsesUtils::CreateTextureResourceFromPng(pngFilePath, m_scene); + const ramses::TextureSampler* sampler = m_scene.createTextureSampler( + ramses::ETextureAddressMode::Repeat, + ramses::ETextureAddressMode::Repeat, + ramses::ETextureSamplingMethod::Nearest, + ramses::ETextureSamplingMethod::Nearest, + *texture); + + m_scene.createTextureConsumer(*sampler, consumerId); + + ramses::Appearance* appearance = m_scene.createAppearance(*m_effect, "triangle appearance"); + + const ramses::ArrayResource* vertexPositions = m_scene.createArrayResource(4u, vertexPositionsArray.data()); + + const std::array textureCoordsArray + { + ramses::vec2f{ 0.f, 1.f }, //A A-----D + ramses::vec2f{ 0.f, 0.f }, //B | | + ramses::vec2f{ 1.f, 0.f }, //C | | + ramses::vec2f{ 1.f, 1.f } //D B-----C + }; + const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(4u, textureCoordsArray.data()); + + const uint16_t indicesArray[] = + { + 0, 2, 1, //ACB + 0, 3, 2 //ADC + }; + const ramses::ArrayResource* indices = m_scene.createArrayResource(6u, indicesArray); + + ramses::Geometry* geometry = m_scene.createGeometry(*m_effect, "triangle geometry"); + geometry->setIndices(*indices); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_texcoord"), *textureCoords); + appearance->setInputTexture(*m_effect->findUniformInput("u_texture"), *sampler); + + // create a mesh node to define the triangle with chosen appearance + ramses::MeshNode* meshNode = m_scene.createMeshNode("textured triangle mesh node"); + addMeshNodeToDefaultRenderGroup(*meshNode); + + if (parentNode == nullptr) + parentNode = m_root; + parentNode->addChild(*meshNode); + + meshNode->setAppearance(*appearance); + meshNode->setGeometry(*geometry); + } +} diff --git a/integration/TestContent/include/TestScenes/AntiAliasingScene.h b/tests/integration/test-content/TestScenes/AntiAliasingScene.h similarity index 88% rename from integration/TestContent/include/TestScenes/AntiAliasingScene.h rename to tests/integration/test-content/TestScenes/AntiAliasingScene.h index a38d554d2..1ef7b5dbc 100644 --- a/integration/TestContent/include/TestScenes/AntiAliasingScene.h +++ b/tests/integration/test-content/TestScenes/AntiAliasingScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ANTIALIASINGSCENE_H -#define RAMSES_ANTIALIASINGSCENE_H +#pragma once #include "IntegrationScene.h" -namespace ramses_internal +namespace ramses::internal { class AntiAliasingScene : public IntegrationScene { @@ -27,5 +26,3 @@ namespace ramses_internal }; } -#endif - diff --git a/integration/TestContent/include/TestScenes/ArrayBufferScene.h b/tests/integration/test-content/TestScenes/ArrayBufferScene.h similarity index 90% rename from integration/TestContent/include/TestScenes/ArrayBufferScene.h rename to tests/integration/test-content/TestScenes/ArrayBufferScene.h index 6bcf36adf..e6cb593c2 100644 --- a/integration/TestContent/include/TestScenes/ArrayBufferScene.h +++ b/tests/integration/test-content/TestScenes/ArrayBufferScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ARRAYBUFFERSCENE_H -#define RAMSES_ARRAYBUFFERSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" namespace ramses { @@ -20,7 +19,7 @@ namespace ramses class ArrayBuffer; } -namespace ramses_internal +namespace ramses::internal { class ArrayBufferScene : public IntegrationScene { @@ -68,11 +67,9 @@ namespace ramses_internal ramses::MeshNode* m_meshNode; ramses::Effect& m_effect; - ramses::GeometryBinding* m_geometry = nullptr; + ramses::Geometry* m_geometry = nullptr; ramses::ArrayBuffer* m_indexDataBufferUInt32 = nullptr; ramses::ArrayBuffer* m_vertexDataBufferVec4 = nullptr; ramses::ArrayBuffer* m_vertexDataBufferInterleaved = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/ArrayInputScene.h b/tests/integration/test-content/TestScenes/ArrayInputScene.h similarity index 80% rename from integration/TestContent/include/TestScenes/ArrayInputScene.h rename to tests/integration/test-content/TestScenes/ArrayInputScene.h index 34f57f555..fd259a6a8 100644 --- a/integration/TestContent/include/TestScenes/ArrayInputScene.h +++ b/tests/integration/test-content/TestScenes/ArrayInputScene.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ARRAYINPUTSCENE_H -#define RAMSES_ARRAYINPUTSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { class ArrayInputScene : public IntegrationScene { @@ -30,5 +29,3 @@ namespace ramses_internal }; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/ArrayResourceScene.h b/tests/integration/test-content/TestScenes/ArrayResourceScene.h similarity index 86% rename from integration/TestContent/include/TestScenes/ArrayResourceScene.h rename to tests/integration/test-content/TestScenes/ArrayResourceScene.h index ae512ce57..43a0fdc19 100644 --- a/integration/TestContent/include/TestScenes/ArrayResourceScene.h +++ b/tests/integration/test-content/TestScenes/ArrayResourceScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ARRAYRESOURCESCENE_H -#define RAMSES_ARRAYRESOURCESCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" namespace ramses { @@ -19,7 +18,7 @@ namespace ramses class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class ArrayResourceScene : public IntegrationScene { @@ -45,8 +44,6 @@ namespace ramses_internal ramses::MeshNode* m_meshNode = nullptr; ramses::Effect& m_effect; - ramses::GeometryBinding* m_geometry = nullptr; + ramses::Geometry* m_geometry = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/BlitPassScene.h b/tests/integration/test-content/TestScenes/BlitPassScene.h similarity index 94% rename from integration/TestContent/include/TestScenes/BlitPassScene.h rename to tests/integration/test-content/TestScenes/BlitPassScene.h index f98e91992..5abd22fef 100644 --- a/integration/TestContent/include/TestScenes/BlitPassScene.h +++ b/tests/integration/test-content/TestScenes/BlitPassScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_BLITPASSSCENE_H -#define RAMSES_BLITPASSSCENE_H +#pragma once #include "CommonRenderBufferTestScene.h" @@ -20,7 +19,7 @@ namespace ramses class MeshNode; } -namespace ramses_internal +namespace ramses::internal { class BlitPassScene : public CommonRenderBufferTestScene { @@ -54,5 +53,3 @@ namespace ramses_internal ramses::RenderBuffer& m_blittingDepthStencilBuffer; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/CameraDataLinkScene.h b/tests/integration/test-content/TestScenes/CameraDataLinkScene.h similarity index 88% rename from integration/TestContent/include/TestScenes/CameraDataLinkScene.h rename to tests/integration/test-content/TestScenes/CameraDataLinkScene.h index 617c2d275..3f159c7df 100644 --- a/integration/TestContent/include/TestScenes/CameraDataLinkScene.h +++ b/tests/integration/test-content/TestScenes/CameraDataLinkScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CAMERADATALINKSCENE_H -#define RAMSES_CAMERADATALINKSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses_internal +namespace ramses::internal { class CameraDataLinkScene : public IntegrationScene { @@ -40,5 +39,3 @@ namespace ramses_internal void setUpProviderScene(); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/CommonRenderBufferTestScene.h b/tests/integration/test-content/TestScenes/CommonRenderBufferTestScene.h similarity index 87% rename from integration/TestContent/include/TestScenes/CommonRenderBufferTestScene.h rename to tests/integration/test-content/TestScenes/CommonRenderBufferTestScene.h index 38fd92534..57df8f3a5 100644 --- a/integration/TestContent/include/TestScenes/CommonRenderBufferTestScene.h +++ b/tests/integration/test-content/TestScenes/CommonRenderBufferTestScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMONRENDERBUFFERTESTSCENE_H -#define RAMSES_COMMONRENDERBUFFERTESTSCENE_H +#pragma once #include "IntegrationScene.h" #include "TriangleAppearance.h" @@ -21,7 +20,7 @@ namespace ramses class MeshNode; } -namespace ramses_internal +namespace ramses::internal { class CommonRenderBufferTestScene : public IntegrationScene { @@ -33,10 +32,8 @@ namespace ramses_internal const ramses::Effect& getEffectRenderTwoBuffers(); ramses::PerspectiveCamera& createCamera(float nearPlane = 1.0f, float farPlane = 100.0f); const ramses::MeshNode& createQuadWithTexture(const ramses::RenderBuffer& renderBuffer); - ramses::MeshNode& createMesh(const ramses::Effect& effect, ramses::TriangleAppearance::EColor color = ramses::TriangleAppearance::EColor_Red); + ramses::MeshNode& createMesh(const ramses::Effect& effect, TriangleAppearance::EColor color = TriangleAppearance::EColor_Red); ramses::RenderPass* addRenderPassUsingRenderBufferAsQuadTexture(const ramses::MeshNode& quad); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/CubeTextureScene.h b/tests/integration/test-content/TestScenes/CubeTextureScene.h similarity index 86% rename from integration/TestContent/include/TestScenes/CubeTextureScene.h rename to tests/integration/test-content/TestScenes/CubeTextureScene.h index 5b4f52d43..8e7746827 100644 --- a/integration/TestContent/include/TestScenes/CubeTextureScene.h +++ b/tests/integration/test-content/TestScenes/CubeTextureScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CUBETEXTURESCENE_H -#define RAMSES_CUBETEXTURESCENE_H +#pragma once #include "IntegrationScene.h" -#include "DataTypesImpl.h" -#include "SceneAPI/Handles.h" -#include "Collections/Vector.h" +#include "impl/DataTypesImpl.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" namespace ramses { @@ -20,7 +19,7 @@ namespace ramses class Node; } -namespace ramses_internal +namespace ramses::internal { class CubeTextureScene : public IntegrationScene { @@ -37,7 +36,7 @@ namespace ramses_internal private: void init(EState state); void initializeUnitSphere(); - void divideUnitSphereTriangle(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, long depth); + void divideUnitSphereTriangle(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, int depth); ramses::TextureCube* createTextureCube(EState state); ramses::Effect* m_effect; @@ -49,5 +48,3 @@ namespace ramses_internal std::vector m_sphereIndices; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/CustomShaderTestScene.h b/tests/integration/test-content/TestScenes/CustomShaderTestScene.h similarity index 79% rename from integration/TestContent/include/TestScenes/CustomShaderTestScene.h rename to tests/integration/test-content/TestScenes/CustomShaderTestScene.h index 504f0d6d7..a450771fc 100644 --- a/integration/TestContent/include/TestScenes/CustomShaderTestScene.h +++ b/tests/integration/test-content/TestScenes/CustomShaderTestScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CUSTOMSHADERTESTSCENE_H -#define RAMSES_CUSTOMSHADERTESTSCENE_H +#pragma once #include "IntegrationScene.h" @@ -17,10 +16,10 @@ namespace ramses { class Effect; class Appearance; - class GeometryBinding; + class Geometry; } -namespace ramses_internal +namespace ramses::internal { class CustomShaderTestScene : public IntegrationScene { @@ -34,15 +33,13 @@ namespace ramses_internal }; private: - [[nodiscard]] std::string getEffectNameFromState(uint32_t state) const; + [[nodiscard]] static std::string GetEffectNameFromState(uint32_t state); void createGeometry(); void initInputs(); ramses::Effect& m_effect; ramses::Appearance& m_appearance; - ramses::GeometryBinding& m_geometryBinding; + ramses::Geometry& m_geometryBinding; }; } -#endif - diff --git a/integration/TestContent/include/TestScenes/DataLinkScene.h b/tests/integration/test-content/TestScenes/DataLinkScene.h similarity index 84% rename from integration/TestContent/include/TestScenes/DataLinkScene.h rename to tests/integration/test-content/TestScenes/DataLinkScene.h index 2ac97de5b..e0b526ed7 100644 --- a/integration/TestContent/include/TestScenes/DataLinkScene.h +++ b/tests/integration/test-content/TestScenes/DataLinkScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DATALINKSCENE_H -#define RAMSES_DATALINKSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses_internal +namespace ramses::internal { class DataLinkScene : public IntegrationScene { @@ -31,5 +30,3 @@ namespace ramses_internal static constexpr const ramses::dataConsumerId_t DataConsumerId{152u}; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/EmbeddedCompositorScene.h b/tests/integration/test-content/TestScenes/EmbeddedCompositorScene.h similarity index 92% rename from integration/TestContent/include/TestScenes/EmbeddedCompositorScene.h rename to tests/integration/test-content/TestScenes/EmbeddedCompositorScene.h index 5ff1dc7f1..9d7321b03 100644 --- a/integration/TestContent/include/TestScenes/EmbeddedCompositorScene.h +++ b/tests/integration/test-content/TestScenes/EmbeddedCompositorScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITORSCENE_H -#define RAMSES_EMBEDDEDCOMPOSITORSCENE_H +#pragma once #include "IntegrationScene.h" -#include "RendererAPI/Types.h" -#include "ramses-client-api/Scene.h" +#include "internal/RendererLib/Types.h" +#include "ramses/client/Scene.h" namespace ramses { @@ -19,7 +18,7 @@ namespace ramses class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositorScene : public IntegrationScene { @@ -54,5 +53,3 @@ namespace ramses_internal const ramses::ArrayResource& m_textureCoords; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/FileLoadingScene.h b/tests/integration/test-content/TestScenes/FileLoadingScene.h similarity index 79% rename from integration/TestContent/include/TestScenes/FileLoadingScene.h rename to tests/integration/test-content/TestScenes/FileLoadingScene.h index 3ea7091b7..74eb50c6c 100644 --- a/integration/TestContent/include/TestScenes/FileLoadingScene.h +++ b/tests/integration/test-content/TestScenes/FileLoadingScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FILELOADINGSCENE_H -#define RAMSES_FILELOADINGSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/SceneConfig.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/SceneConfig.h" #include @@ -21,7 +20,7 @@ namespace ramses class RamsesFrameworkConfig; } -namespace ramses_internal +namespace ramses::internal { // FileLoadingScene is NO IntegrationScene (IntegrationScene needs scene and initializes it -> not wanted here) class FileLoadingScene @@ -38,15 +37,13 @@ namespace ramses_internal }; private: - void createFiles(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const std::string& folder, const ramses::SceneConfig& sceneConfig = ramses::SceneConfig()); - void addTriangles(ramses::Scene& scene, ramses::RenderGroup& renderGroup); + void createFiles(ramses::RamsesClient& client, ramses::sceneId_t sceneId, const glm::vec3& cameraPosition, const std::string& folder); + static void AddTriangles(ramses::Scene& scene, ramses::RenderGroup& renderGroup); void loadFromFiles(ramses::RamsesClient& client, const std::string& folder); - void cleanupFiles(const std::string& folder); + static void CleanupFiles(const std::string& folder); ramses::Scene* m_createdScene = nullptr; uint32_t m_viewportWidth; uint32_t m_viewportHeight; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/GeometryInstanceScene.h b/tests/integration/test-content/TestScenes/GeometryInstanceScene.h similarity index 68% rename from integration/TestContent/include/TestScenes/GeometryInstanceScene.h rename to tests/integration/test-content/TestScenes/GeometryInstanceScene.h index 3012974e4..878b392a3 100644 --- a/integration/TestContent/include/TestScenes/GeometryInstanceScene.h +++ b/tests/integration/test-content/TestScenes/GeometryInstanceScene.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GEOMETRYINSTANCESCENE_H -#define RAMSES_GEOMETRYINSTANCESCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" -namespace ramses_internal +namespace ramses::internal { class GeometryInstanceScene : public IntegrationScene { @@ -25,16 +24,15 @@ namespace ramses_internal { GEOMETRY_INSTANCE_UNIFORM = 0, GEOMETRY_INSTANCE_VERTEX, - GEOMETRY_INSTANCE_AND_NOT_INSTANCE + GEOMETRY_INSTANCE_AND_NOT_INSTANCE, + GEOMETRY_INSTANCE_ZERO_INSTANCE_COUNT }; private: - void setInstancedUniforms(ramses::Appearance& appearance); - void setInstancedAttributes(const ramses::Effect& effect, ramses::GeometryBinding* geometry, uint32_t instancingDivisor); - ramses::GeometryBinding* createGeometry(const ramses::Effect& effect); + static void SetInstancedUniforms(ramses::Appearance& appearance); + void setInstancedAttributes(const ramses::Effect& effect, ramses::Geometry* geometry, uint32_t instancingDivisor); + ramses::Geometry* createGeometry(const ramses::Effect& effect); static constexpr uint32_t NumInstances = 3u; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/GeometryShaderScene.h b/tests/integration/test-content/TestScenes/GeometryShaderScene.h similarity index 76% rename from integration/TestContent/include/TestScenes/GeometryShaderScene.h rename to tests/integration/test-content/TestScenes/GeometryShaderScene.h index aab900749..153745485 100644 --- a/integration/TestContent/include/TestScenes/GeometryShaderScene.h +++ b/tests/integration/test-content/TestScenes/GeometryShaderScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_GEOMETRYSHADERSCENE_H -#define RAMSES_GEOMETRYSHADERSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-framework-api/AppearanceEnums.h" +#include "ramses/framework/AppearanceEnums.h" -namespace ramses_internal +namespace ramses::internal { class GeometryShaderScene : public IntegrationScene { @@ -35,14 +34,12 @@ namespace ramses_internal private: const ramses::Effect& createTestEffect(uint32_t state); - std::string getGeometryShaderLayout(uint32_t state); - std::string getShaderVersion(uint32_t state); - std::string getGeometryShaderExtensions(uint32_t state); - ramses::EDrawMode getDrawMode(uint32_t state); + static std::string GetGeometryShaderLayout(uint32_t state); + static std::string GetShaderVersion(uint32_t state); + static std::string GetGeometryShaderExtensions(uint32_t state); + static ramses::EDrawMode GetDrawMode(uint32_t state); const ramses::Effect& m_effect; }; } -#endif - diff --git a/integration/TestContent/include/TestScenes/HierarchicalRedTrianglesScene.h b/tests/integration/test-content/TestScenes/HierarchicalRedTrianglesScene.h similarity index 90% rename from integration/TestContent/include/TestScenes/HierarchicalRedTrianglesScene.h rename to tests/integration/test-content/TestScenes/HierarchicalRedTrianglesScene.h index 7abb184f3..ce8ff771a 100644 --- a/integration/TestContent/include/TestScenes/HierarchicalRedTrianglesScene.h +++ b/tests/integration/test-content/TestScenes/HierarchicalRedTrianglesScene.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_HIERARCHICALREDTRIANGLESSCENE_H -#define RAMSES_HIERARCHICALREDTRIANGLESSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" namespace ramses { class Node; } -namespace ramses_internal +namespace ramses::internal { class HierarchicalRedTrianglesScene : public IntegrationScene { @@ -48,5 +47,3 @@ namespace ramses_internal ramses::Node& m_scaleNode2; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/IndexArray32BitScene.h b/tests/integration/test-content/TestScenes/IndexArray32BitScene.h similarity index 75% rename from integration/TestContent/include/TestScenes/IndexArray32BitScene.h rename to tests/integration/test-content/TestScenes/IndexArray32BitScene.h index 18c23a364..d0b927ca0 100644 --- a/integration/TestContent/include/TestScenes/IndexArray32BitScene.h +++ b/tests/integration/test-content/TestScenes/IndexArray32BitScene.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INDEXARRAY32BITSCENE_H -#define RAMSES_INDEXARRAY32BITSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" -namespace ramses_internal +namespace ramses::internal { class IndexArray32BitScene : public IntegrationScene { public: IndexArray32BitScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition); - void create16BitIndexArray(ramses::GeometryBinding* geometry); - void create32BitIndexArray(ramses::GeometryBinding* geometry); + void create16BitIndexArray(ramses::Geometry* geometry); + void create32BitIndexArray(ramses::Geometry* geometry); enum { @@ -37,5 +36,3 @@ namespace ramses_internal ramses::MeshNode* m_meshNode; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/IntegrationScene.h b/tests/integration/test-content/TestScenes/IntegrationScene.h similarity index 90% rename from integration/TestContent/include/TestScenes/IntegrationScene.h rename to tests/integration/test-content/TestScenes/IntegrationScene.h index 39860745b..5125a89d3 100644 --- a/integration/TestContent/include/TestScenes/IntegrationScene.h +++ b/tests/integration/test-content/TestScenes/IntegrationScene.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INTEGRATIONSCENE_H -#define RAMSES_INTEGRATIONSCENE_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" -#include "Collections/Vector.h" -#include "DataTypesImpl.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "impl/DataTypesImpl.h" +#include #include + namespace ramses { class RamsesClient; @@ -27,7 +27,7 @@ namespace ramses class Effect; } -namespace ramses_internal +namespace ramses::internal { class IntegrationScene { @@ -57,5 +57,3 @@ namespace ramses_internal ramses::PerspectiveCamera& m_defaultCamera; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Line.h b/tests/integration/test-content/TestScenes/Line.h similarity index 79% rename from integration/TestContent/include/TestScenes/Line.h rename to tests/integration/test-content/TestScenes/Line.h index 8d99b9db1..2227d5e78 100644 --- a/integration/TestContent/include/TestScenes/Line.h +++ b/tests/integration/test-content/TestScenes/Line.h @@ -6,22 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_LINE_H -#define RAMSES_LINE_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/AppearanceEnums.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/AppearanceEnums.h" namespace ramses { class Scene; class Appearance; - class GeometryBinding; + class Geometry; class ArrayResource; class ArrayResourceInput; class Effect; class UniformInput; +} +namespace ramses::internal +{ class Line { public: @@ -40,22 +42,20 @@ namespace ramses { return m_appearance; } - GeometryBinding& GetGeometry() + Geometry& GetGeometry() { return m_geometry; } protected: static Appearance& createAppearance(Effect& effect, Scene& scene); - static GeometryBinding& createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices); + static Geometry& createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices); static const ArrayResource& createIndices(Scene& scene, EDrawMode desiredDrawMode); void setColor(const UniformInput& colorInput, enum EColor color, float alpha); Appearance& m_appearance; const ArrayResource& m_indices; - GeometryBinding& m_geometry; + Geometry& m_geometry; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MsaaRenderBufferScene.h b/tests/integration/test-content/TestScenes/MsaaRenderBufferScene.h similarity index 85% rename from integration/TestContent/include/TestScenes/MsaaRenderBufferScene.h rename to tests/integration/test-content/TestScenes/MsaaRenderBufferScene.h index f676d73d2..02a14b6b4 100644 --- a/integration/TestContent/include/TestScenes/MsaaRenderBufferScene.h +++ b/tests/integration/test-content/TestScenes/MsaaRenderBufferScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MSAARENDERBUFFERSCENE_H -#define RAMSES_MSAARENDERBUFFERSCENE_H +#pragma once #include "CommonRenderBufferTestScene.h" -namespace ramses_internal +namespace ramses::internal { class MsaaRenderBufferScene : public CommonRenderBufferTestScene { @@ -20,7 +19,8 @@ namespace ramses_internal enum { - SAMPLE_COUNT_2_BLIT = 0, + SAMPLE_COUNT_1_BLIT = 0, + SAMPLE_COUNT_2_BLIT, SAMPLE_COUNT_4_BLIT, SAMPLE_COUNT_4_TEXEL_FETCH }; @@ -32,11 +32,11 @@ namespace ramses_internal ramses::MeshNode& createMesh(); const ramses::MeshNode& createQuadWithTextureMS(const ramses::RenderBuffer& renderBuffer); + ramses::RenderBuffer& m_colorBufferMsaa1; ramses::RenderBuffer& m_colorBufferMsaa2; ramses::RenderBuffer& m_colorBufferMsaa4; + ramses::RenderBuffer* m_colorBufferMsaa1ReadWrite = nullptr; ramses::RenderBuffer& m_colorBufferMsaa4ReadWrite; ramses::RenderBuffer& m_blittingColorBuffer; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MultiLanguageTextScene.h b/tests/integration/test-content/TestScenes/MultiLanguageTextScene.h similarity index 87% rename from integration/TestContent/include/TestScenes/MultiLanguageTextScene.h rename to tests/integration/test-content/TestScenes/MultiLanguageTextScene.h index 3c12f22de..b5cbfb5d8 100644 --- a/integration/TestContent/include/TestScenes/MultiLanguageTextScene.h +++ b/tests/integration/test-content/TestScenes/MultiLanguageTextScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTILANGUAGETEXTSCENE_H -#define RAMSES_MULTILANGUAGETEXTSCENE_H +#pragma once #if defined(RAMSES_TEXT_ENABLED) #include "TextScene_Base.h" -namespace ramses_internal +namespace ramses::internal { class MultiLanguageTextScene : public TextScene_Base { @@ -28,4 +27,3 @@ namespace ramses_internal } #endif -#endif diff --git a/integration/TestContent/include/TestScenes/MultiTransformationLinkScene.h b/tests/integration/test-content/TestScenes/MultiTransformationLinkScene.h similarity index 82% rename from integration/TestContent/include/TestScenes/MultiTransformationLinkScene.h rename to tests/integration/test-content/TestScenes/MultiTransformationLinkScene.h index 2b3e3b943..1c93a2f06 100644 --- a/integration/TestContent/include/TestScenes/MultiTransformationLinkScene.h +++ b/tests/integration/test-content/TestScenes/MultiTransformationLinkScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTITRANSFORMATIONLINKSCENE_H -#define RAMSES_MULTITRANSFORMATIONLINKSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" #include "Triangle.h" -namespace ramses_internal +namespace ramses::internal { class MultiTransformationLinkScene : public IntegrationScene { @@ -41,10 +40,8 @@ namespace ramses_internal const float m_scaleFactor; ramses::Effect& m_dummyEffect; - ramses::Triangle m_redTriangle; - ramses::Triangle m_greenTriangle; - ramses::Triangle m_blueTriangle; + Triangle m_redTriangle; + Triangle m_greenTriangle; + Triangle m_blueTriangle; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MultiTriangleGeometry.h b/tests/integration/test-content/TestScenes/MultiTriangleGeometry.h similarity index 82% rename from integration/TestContent/include/TestScenes/MultiTriangleGeometry.h rename to tests/integration/test-content/TestScenes/MultiTriangleGeometry.h index 187479eca..ab5407e61 100644 --- a/integration/TestContent/include/TestScenes/MultiTriangleGeometry.h +++ b/tests/integration/test-content/TestScenes/MultiTriangleGeometry.h @@ -6,21 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTITRIANGLEGEOMETRY_H -#define RAMSES_MULTITRIANGLEGEOMETRY_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" namespace ramses { class Scene; class Appearance; - class GeometryBinding; + class Geometry; class ArrayResource; class ArrayResourceInput; class Effect; class UniformInput; +} +namespace ramses::internal +{ class MultiTriangleGeometry { public: @@ -50,22 +52,20 @@ namespace ramses { return m_appearance; } - GeometryBinding& GetGeometry() + Geometry& GetGeometry() { return m_geometry; } protected: static Appearance& createAppearance(Effect& effect, Scene& scene); - static GeometryBinding& createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices, EGeometryType geometryType); + static Geometry& createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices, EGeometryType geometryType); static const ArrayResource& createIndices(Scene& scene, EVerticesOrder vertOrder); void setColor(const UniformInput& colorInput, enum EColor color, float alpha); Appearance& m_appearance; const ArrayResource& m_indices; - GeometryBinding& m_geometry; + Geometry& m_geometry; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MultiTypeLinkScene.h b/tests/integration/test-content/TestScenes/MultiTypeLinkScene.h similarity index 87% rename from integration/TestContent/include/TestScenes/MultiTypeLinkScene.h rename to tests/integration/test-content/TestScenes/MultiTypeLinkScene.h index ed1dbc688..e9994ec86 100644 --- a/integration/TestContent/include/TestScenes/MultiTypeLinkScene.h +++ b/tests/integration/test-content/TestScenes/MultiTypeLinkScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTITYPELINKSCENE_H -#define RAMSES_MULTITYPELINKSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/framework/RamsesFrameworkTypes.h" namespace ramses { @@ -21,7 +20,7 @@ namespace ramses class Texture2D; } -namespace ramses_internal +namespace ramses::internal { class MultiTypeLinkScene : public IntegrationScene { @@ -43,8 +42,6 @@ namespace ramses_internal private: const ramses::TextureSampler& createSampler(const ramses::Texture2D& texture); - void setSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler); + static void SetSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MultipleGeometryScene.h b/tests/integration/test-content/TestScenes/MultipleGeometryScene.h similarity index 82% rename from integration/TestContent/include/TestScenes/MultipleGeometryScene.h rename to tests/integration/test-content/TestScenes/MultipleGeometryScene.h index ae55268d9..143501bf5 100644 --- a/integration/TestContent/include/TestScenes/MultipleGeometryScene.h +++ b/tests/integration/test-content/TestScenes/MultipleGeometryScene.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTIPLEGEOMETRYSCENE_H -#define RAMSES_MULTIPLEGEOMETRYSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" +#include - -namespace ramses_internal +namespace ramses::internal { class MultipleGeometryScene : public IntegrationScene { @@ -33,8 +32,6 @@ namespace ramses_internal void createGeometries(ramses::Effect& effect, uint32_t state); static const uint32_t NumMeshes = 4u; - ramses::MeshNode* m_meshNode[NumMeshes]; + std::array m_meshNode{}; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MultipleRenderTargetScene.h b/tests/integration/test-content/TestScenes/MultipleRenderTargetScene.h similarity index 90% rename from integration/TestContent/include/TestScenes/MultipleRenderTargetScene.h rename to tests/integration/test-content/TestScenes/MultipleRenderTargetScene.h index 78ebadcee..010be786c 100644 --- a/integration/TestContent/include/TestScenes/MultipleRenderTargetScene.h +++ b/tests/integration/test-content/TestScenes/MultipleRenderTargetScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTIPLERENDERTARGETSCENE_H -#define RAMSES_MULTIPLERENDERTARGETSCENE_H +#pragma once #include "CommonRenderBufferTestScene.h" @@ -20,7 +19,7 @@ namespace ramses class MeshNode; } -namespace ramses_internal +namespace ramses::internal { class MultipleRenderTargetScene : public CommonRenderBufferTestScene { @@ -42,7 +41,7 @@ namespace ramses_internal private: const ramses::Effect& getMRTEffect(uint32_t state); ramses::RenderTarget& createMRTRenderTarget(uint32_t state); - ramses::RenderBuffer& initRenderBuffer(ramses::Scene& scene, uint32_t state); + static ramses::RenderBuffer& InitRenderBuffer(ramses::Scene& scene, uint32_t state); const ramses::MeshNode& createQuadWithTexture(const ramses::RenderBuffer& renderBuffer, const glm::vec3& translation, const glm::vec4& modulateColor = glm::vec4(1.f, 1.f, 1.f, 1.f)); void initClearPass(); @@ -54,5 +53,3 @@ namespace ramses_internal ramses::RenderBuffer& m_depthBuffer; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/MultipleTrianglesScene.h b/tests/integration/test-content/TestScenes/MultipleTrianglesScene.h similarity index 67% rename from integration/TestContent/include/TestScenes/MultipleTrianglesScene.h rename to tests/integration/test-content/TestScenes/MultipleTrianglesScene.h index 231774992..7bda99636 100644 --- a/integration/TestContent/include/TestScenes/MultipleTrianglesScene.h +++ b/tests/integration/test-content/TestScenes/MultipleTrianglesScene.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MULTIPLETRIANGLESSCENE_H -#define RAMSES_MULTIPLETRIANGLESSCENE_H +#pragma once #include "IntegrationScene.h" #include "Triangle.h" #include "Line.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" #include "MultiTriangleGeometry.h" -namespace ramses_internal +namespace ramses::internal { class MultipleTrianglesScene : public IntegrationScene { @@ -65,25 +64,23 @@ namespace ramses_internal ramses::MeshNode* m_meshNode6; ramses::MeshNode* m_meshNode7; - ramses::Triangle m_whiteTriangle; - ramses::Triangle m_redTriangle; - ramses::Triangle m_greenTriangle; - ramses::Triangle m_blueTriangle; - ramses::Line m_yellowLine; - ramses::MultiTriangleGeometry m_whiteQuad; - ramses::MultiTriangleGeometry m_triangleFan; - ramses::Line m_lineStrip; - ramses::Line m_linePoints; - ramses::Triangle m_redTransparentTriangle; - ramses::Triangle m_greenTransparentTriangle; - ramses::Triangle m_blueTransparentTriangle; - ramses::Triangle m_colorMaskRedTriangle; - ramses::Triangle m_colorMaskGreenTriangle; - ramses::Triangle m_colorMaskBlueTriangle; - ramses::Triangle m_CCWTriangle; - ramses::Triangle m_CWTriangle; - ramses::Triangle m_CWTriangleCCWIndices; + Triangle m_whiteTriangle; + Triangle m_redTriangle; + Triangle m_greenTriangle; + Triangle m_blueTriangle; + Line m_yellowLine; + MultiTriangleGeometry m_whiteQuad; + MultiTriangleGeometry m_triangleFan; + Line m_lineStrip; + Line m_linePoints; + Triangle m_redTransparentTriangle; + Triangle m_greenTransparentTriangle; + Triangle m_blueTransparentTriangle; + Triangle m_colorMaskRedTriangle; + Triangle m_colorMaskGreenTriangle; + Triangle m_colorMaskBlueTriangle; + Triangle m_CCWTriangle; + Triangle m_CWTriangle; + Triangle m_CWTriangleCCWIndices; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/RenderBufferScene.h b/tests/integration/test-content/TestScenes/RenderBufferScene.h similarity index 72% rename from integration/TestContent/include/TestScenes/RenderBufferScene.h rename to tests/integration/test-content/TestScenes/RenderBufferScene.h index 9d2a11f3b..9a3c1b112 100644 --- a/integration/TestContent/include/TestScenes/RenderBufferScene.h +++ b/tests/integration/test-content/TestScenes/RenderBufferScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERBUFFERSCENE_H -#define RAMSES_RENDERBUFFERSCENE_H +#pragma once #include "CommonRenderBufferTestScene.h" @@ -20,7 +19,7 @@ namespace ramses class MeshNode; } -namespace ramses_internal +namespace ramses::internal { class RenderBufferScene : public CommonRenderBufferTestScene { @@ -31,7 +30,12 @@ namespace ramses_internal { ONE_COLOR_BUFFER_NO_DEPTH_OR_STENCIL = 0, ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_BUFFER, - ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER + ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH_STENCIL_BUFFER, + ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH16, + ONE_COLOR_BUFFER_WITH_WRITE_ONLY_DEPTH24, + READ_WRITE_DEPTH16, + READ_WRITE_DEPTH24, + READ_WRITE_DEPTH32, }; private: @@ -43,7 +47,10 @@ namespace ramses_internal ramses::RenderBuffer& m_readWriteColorRenderBuffer; ramses::RenderBuffer& m_writeOnlyDepthBuffer; ramses::RenderBuffer& m_writeOnlyDepthStencilBuffer; + ramses::RenderBuffer& m_writeOnlyDepthBuffer16; + ramses::RenderBuffer& m_writeOnlyDepthBuffer24; + ramses::RenderBuffer& m_readWriteDepthBuffer16; + ramses::RenderBuffer& m_readWriteDepthBuffer24; + ramses::RenderBuffer& m_readWriteDepthBuffer32; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/RenderPassClearScene.h b/tests/integration/test-content/TestScenes/RenderPassClearScene.h similarity index 73% rename from integration/TestContent/include/TestScenes/RenderPassClearScene.h rename to tests/integration/test-content/TestScenes/RenderPassClearScene.h index 718cce5e5..3039dabb1 100644 --- a/integration/TestContent/include/TestScenes/RenderPassClearScene.h +++ b/tests/integration/test-content/TestScenes/RenderPassClearScene.h @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERPASSCLEARSCENE_H -#define RAMSES_RENDERPASSCLEARSCENE_H +#pragma once #include "IntegrationScene.h" #include "Triangle.h" -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" -namespace ramses_internal +namespace ramses::internal { class RenderPassClearScene : public IntegrationScene { @@ -21,7 +21,7 @@ namespace ramses_internal RenderPassClearScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition); private: - void addTriangleMesh(ramses::Triangle& triangle, ramses::RenderGroup& targetGroup, float x, float y, float z); + void addTriangleMesh(Triangle& triangle, ramses::RenderGroup& targetGroup, float x, float y, float z); void renderBufferToScreen(ramses::RenderBuffer& renderBuffer); ramses::RenderTarget& createRenderTarget(); const ramses::MeshNode& createQuadWithTexture(const ramses::RenderBuffer& renderBuffer); @@ -29,14 +29,11 @@ namespace ramses_internal ramses::Effect& m_effect; - ramses::Triangle m_blueTriangle; - ramses::Triangle m_redTriangle; - ramses::Triangle m_greenTriangle; + Triangle m_blueTriangle; + Triangle m_redTriangle; + Triangle m_greenTriangle; ramses::RenderBuffer& m_colorBuffer; ramses::RenderBuffer& m_depthStencilBuffer; }; } - -#endif //RAMSES_RENDERPASSCLEARSCENE_H - diff --git a/integration/TestContent/include/TestScenes/RenderPassOnceScene.h b/tests/integration/test-content/TestScenes/RenderPassOnceScene.h similarity index 91% rename from integration/TestContent/include/TestScenes/RenderPassOnceScene.h rename to tests/integration/test-content/TestScenes/RenderPassOnceScene.h index 615252a00..35fddbcae 100644 --- a/integration/TestContent/include/TestScenes/RenderPassOnceScene.h +++ b/tests/integration/test-content/TestScenes/RenderPassOnceScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERPASSONCESCENE_H -#define RAMSES_RENDERPASSONCESCENE_H +#pragma once #include "IntegrationScene.h" #include "Triangle.h" @@ -18,7 +17,7 @@ namespace ramses class RenderBuffer; } -namespace ramses_internal +namespace ramses::internal { class RenderPassOnceScene : public IntegrationScene { @@ -43,5 +42,3 @@ namespace ramses_internal const ramses::RenderBuffer& m_renderBuffer; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/RenderPassScene.h b/tests/integration/test-content/TestScenes/RenderPassScene.h similarity index 79% rename from integration/TestContent/include/TestScenes/RenderPassScene.h rename to tests/integration/test-content/TestScenes/RenderPassScene.h index 6b212db48..eb231e7bd 100644 --- a/integration/TestContent/include/TestScenes/RenderPassScene.h +++ b/tests/integration/test-content/TestScenes/RenderPassScene.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERPASSSCENE_H -#define RAMSES_RENDERPASSSCENE_H +#pragma once #include "IntegrationScene.h" #include "Triangle.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" -namespace ramses_internal +namespace ramses::internal { class RenderPassScene : public IntegrationScene { @@ -34,10 +33,7 @@ namespace ramses_internal private: ramses::Effect& m_effect; - ramses::Triangle m_blueTriangle; - ramses::Triangle m_whiteTriangle; + Triangle m_blueTriangle; + Triangle m_whiteTriangle; }; } - -#endif //RAMSES_RENDERPASSSCENE_H - diff --git a/integration/TestContent/include/TestScenes/RenderTargetScene.h b/tests/integration/test-content/TestScenes/RenderTargetScene.h similarity index 94% rename from integration/TestContent/include/TestScenes/RenderTargetScene.h rename to tests/integration/test-content/TestScenes/RenderTargetScene.h index 88c851c0e..d5d2d8846 100644 --- a/integration/TestContent/include/TestScenes/RenderTargetScene.h +++ b/tests/integration/test-content/TestScenes/RenderTargetScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERTARGETSCENE_H -#define RAMSES_RENDERTARGETSCENE_H +#pragma once #include "IntegrationScene.h" #include "Triangle.h" @@ -17,7 +16,7 @@ namespace ramses class RenderBuffer; } -namespace ramses_internal +namespace ramses::internal { class RenderTargetScene : public IntegrationScene { @@ -52,5 +51,3 @@ namespace ramses_internal const ramses::RenderBuffer& m_renderBuffer; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/SceneFromPath.h b/tests/integration/test-content/TestScenes/SceneFromPath.h similarity index 85% rename from integration/TestContent/include/TestScenes/SceneFromPath.h rename to tests/integration/test-content/TestScenes/SceneFromPath.h index ec6c47f3d..d0e8bfeaa 100644 --- a/integration/TestContent/include/TestScenes/SceneFromPath.h +++ b/tests/integration/test-content/TestScenes/SceneFromPath.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEFROMPATH_H -#define RAMSES_SCENEFROMPATH_H +#pragma once -#include "ramses-client-api/Scene.h" +#include "ramses/client/Scene.h" #include -namespace ramses_internal +namespace ramses::internal { class SceneFromPath { @@ -24,5 +23,3 @@ namespace ramses_internal ramses::Scene* m_createdScene = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/ShaderTestScene.h b/tests/integration/test-content/TestScenes/ShaderTestScene.h similarity index 82% rename from integration/TestContent/include/TestScenes/ShaderTestScene.h rename to tests/integration/test-content/TestScenes/ShaderTestScene.h index 79989be12..b2e193477 100644 --- a/integration/TestContent/include/TestScenes/ShaderTestScene.h +++ b/tests/integration/test-content/TestScenes/ShaderTestScene.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SHADERTESTSCENE_H -#define RAMSES_SHADERTESTSCENE_H +#pragma once #include "IntegrationScene.h" #include "Triangle.h" #include -namespace ramses_internal +namespace ramses::internal { class ShaderTestScene : public IntegrationScene { @@ -32,13 +31,11 @@ namespace ramses_internal }; private: - [[nodiscard]] std::string getEffectNameFromState(uint32_t state) const; + [[nodiscard]] static std::string GetEffectNameFromState(uint32_t state); void initInputs(uint32_t state); ramses::Effect& m_effect; - ramses::Triangle m_triangle; + Triangle m_triangle; }; } -#endif - diff --git a/integration/TestContent/include/TestScenes/SingleAppearanceScene.h b/tests/integration/test-content/TestScenes/SingleAppearanceScene.h similarity index 71% rename from integration/TestContent/include/TestScenes/SingleAppearanceScene.h rename to tests/integration/test-content/TestScenes/SingleAppearanceScene.h index 8186c6be9..62bf43efa 100644 --- a/integration/TestContent/include/TestScenes/SingleAppearanceScene.h +++ b/tests/integration/test-content/TestScenes/SingleAppearanceScene.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SINGLEAPPEARANCESCENE_H -#define RAMSES_SINGLEAPPEARANCESCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-client-api/MeshNode.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/client/MeshNode.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { class SingleAppearanceScene : public IntegrationScene { @@ -31,11 +30,9 @@ namespace ramses_internal }; private: - void setAppearanceAndGeometryToAllMeshNodes(ramses::Appearance& appearance, ramses::GeometryBinding& geometry); - void setTriangleColor(ramses::Appearance& appearance, const ramses::Effect& effect, const glm::vec4& color); + void setAppearanceAndGeometryToAllMeshNodes(ramses::Appearance& appearance, ramses::Geometry& geometry); + static void SetTriangleColor(ramses::Appearance& appearance, const ramses::Effect& effect, const glm::vec4& color); std::vector m_meshNodes; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/StreamTextureScene.h b/tests/integration/test-content/TestScenes/StreamTextureScene.h similarity index 93% rename from integration/TestContent/include/TestScenes/StreamTextureScene.h rename to tests/integration/test-content/TestScenes/StreamTextureScene.h index 997648225..2ed991b11 100644 --- a/integration/TestContent/include/TestScenes/StreamTextureScene.h +++ b/tests/integration/test-content/TestScenes/StreamTextureScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_STREAMTEXTURESCENE_H -#define RAMSES_STREAMTEXTURESCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "DataTypesImpl.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "impl/DataTypesImpl.h" #include namespace ramses @@ -19,7 +18,7 @@ namespace ramses class Node; } -namespace ramses_internal +namespace ramses::internal { /// Scene, that creates a cube with six stream textures. class StreamTextureScene : public IntegrationScene @@ -54,6 +53,8 @@ namespace ramses_internal return { 1u, 1u, 1u, 1u, 1u, 1u }; case MULTI_SOURCE_MULTI_FALLBACK: return { 1u, 1u, 3u, 3u, 1u, 5u }; + default: + break; } return {}; @@ -66,5 +67,3 @@ namespace ramses_internal ramses::Node* m_root; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TextScene.h b/tests/integration/test-content/TestScenes/TextScene.h similarity index 97% rename from integration/TestContent/include/TestScenes/TextScene.h rename to tests/integration/test-content/TestScenes/TextScene.h index 8a2e80102..ebdf7ce83 100644 --- a/integration/TestContent/include/TestScenes/TextScene.h +++ b/tests/integration/test-content/TestScenes/TextScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTSCENE_H -#define RAMSES_TEXTSCENE_H +#pragma once #if defined(RAMSES_TEXT_ENABLED) #include "TextScene_Base.h" -namespace ramses_internal +namespace ramses::internal { class TextScene : public TextScene_Base { @@ -78,4 +77,3 @@ namespace ramses_internal } #endif -#endif diff --git a/integration/TestContent/include/TestScenes/TextScene_Base.h b/tests/integration/test-content/TestScenes/TextScene_Base.h similarity index 80% rename from integration/TestContent/include/TestScenes/TextScene_Base.h rename to tests/integration/test-content/TestScenes/TextScene_Base.h index ea62aa19a..1e27f4cbb 100644 --- a/integration/TestContent/include/TestScenes/TextScene_Base.h +++ b/tests/integration/test-content/TestScenes/TextScene_Base.h @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTSCENE_BASE_H -#define RAMSES_TEXTSCENE_BASE_H +#pragma once #if defined(RAMSES_TEXT_ENABLED) #include "IntegrationScene.h" -#include "ramses-text-api/FontInstanceId.h" -#include "ramses-text-api/TextLine.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text-api/TextCache.h" +#include "ramses/client/text/FontInstanceId.h" +#include "ramses/client/text/TextLine.h" +#include "ramses/client/text/FontRegistry.h" +#include "ramses/client/text/TextCache.h" namespace ramses { class OrthographicCamera; } -namespace ramses_internal +namespace ramses::internal { class TextScene_Base : public IntegrationScene { @@ -37,4 +36,3 @@ namespace ramses_internal } #endif -#endif diff --git a/integration/TestContent/include/TestScenes/Texture2DAnisotropicTextureFilteringScene.h b/tests/integration/test-content/TestScenes/Texture2DAnisotropicTextureFilteringScene.h similarity index 79% rename from integration/TestContent/include/TestScenes/Texture2DAnisotropicTextureFilteringScene.h rename to tests/integration/test-content/TestScenes/Texture2DAnisotropicTextureFilteringScene.h index 7ef954733..5e5adaad6 100644 --- a/integration/TestContent/include/TestScenes/Texture2DAnisotropicTextureFilteringScene.h +++ b/tests/integration/test-content/TestScenes/Texture2DAnisotropicTextureFilteringScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DANISOTROPICTEXTUREFILTERINGSCENE_H -#define RAMSES_TEXTURE2DANISOTROPICTEXTUREFILTERINGSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" -#include "Collections/Vector.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" namespace ramses { @@ -22,7 +21,7 @@ namespace ramses class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class Texture2DAnisotropicTextureFilteringScene : public IntegrationScene { @@ -46,5 +45,3 @@ namespace ramses_internal float y); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Texture2DCompressedMipMapScene.h b/tests/integration/test-content/TestScenes/Texture2DCompressedMipMapScene.h similarity index 67% rename from integration/TestContent/include/TestScenes/Texture2DCompressedMipMapScene.h rename to tests/integration/test-content/TestScenes/Texture2DCompressedMipMapScene.h index 35730ab6d..365221056 100644 --- a/integration/TestContent/include/TestScenes/Texture2DCompressedMipMapScene.h +++ b/tests/integration/test-content/TestScenes/Texture2DCompressedMipMapScene.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DCOMPRESSEDMIPMAPSCENE_H -#define RAMSES_TEXTURE2DCOMPRESSEDMIPMAPSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" namespace ramses { class TextureSampler; - class GeometryBinding; + class Geometry; class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class Texture2DCompressedMipMapScene : public IntegrationScene @@ -39,14 +38,12 @@ namespace ramses_internal void createMesh(const ramses::TextureSampler& sampler); void createGeometry(); - const ramses::ArrayResource* m_indexArray; - const ramses::ArrayResource* m_vertexPositions; - const ramses::ArrayResource* m_textureCoords; + const ramses::ArrayResource* m_indexArray = nullptr; + const ramses::ArrayResource* m_vertexPositions = nullptr; + const ramses::ArrayResource* m_textureCoords = nullptr; static constexpr uint32_t NumMipMaps = 2u; - uint32_t m_textureWidth; - uint32_t m_textureHeight; + uint32_t m_textureWidth = 8; + uint32_t m_textureHeight = 8; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Texture2DFormatScene.h b/tests/integration/test-content/TestScenes/Texture2DFormatScene.h similarity index 80% rename from integration/TestContent/include/TestScenes/Texture2DFormatScene.h rename to tests/integration/test-content/TestScenes/Texture2DFormatScene.h index 02778fb0b..42ba2307d 100644 --- a/integration/TestContent/include/TestScenes/Texture2DFormatScene.h +++ b/tests/integration/test-content/TestScenes/Texture2DFormatScene.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DFORMATSCENE_H -#define RAMSES_TEXTURE2DFORMATSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" namespace ramses { @@ -20,7 +19,7 @@ namespace ramses struct TextureSwizzle; } -namespace ramses_internal +namespace ramses::internal { class Texture2DFormatScene : public IntegrationScene { @@ -60,8 +59,6 @@ namespace ramses_internal void createOrthoCamera(); void createQuad(const ramses::TextureSampler& sampler); - const ramses::MipLevelData& getTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle) const; + static const ramses::MipLevelData& GetTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Texture2DGenerateMipMapScene.h b/tests/integration/test-content/TestScenes/Texture2DGenerateMipMapScene.h similarity index 73% rename from integration/TestContent/include/TestScenes/Texture2DGenerateMipMapScene.h rename to tests/integration/test-content/TestScenes/Texture2DGenerateMipMapScene.h index 562142d86..7be707ad9 100644 --- a/integration/TestContent/include/TestScenes/Texture2DGenerateMipMapScene.h +++ b/tests/integration/test-content/TestScenes/Texture2DGenerateMipMapScene.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DGENERATEMIPMAPSCENE_H -#define RAMSES_TEXTURE2DGENERATEMIPMAPSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" namespace ramses { class TextureSampler; - class GeometryBinding; + class Geometry; class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class Texture2DGenerateMipMapScene : public IntegrationScene @@ -41,10 +40,8 @@ namespace ramses_internal void createGeometry(); ramses::TextureSampler* createTexture2DSampler(uint32_t width = 2u, uint32_t height = 2u, uint8_t transparency = 0u); - const ramses::ArrayResource* m_indexArray; - const ramses::ArrayResource* m_vertexPositions; - const ramses::ArrayResource* m_textureCoords; + const ramses::ArrayResource* m_indexArray = nullptr; + const ramses::ArrayResource* m_vertexPositions = nullptr; + const ramses::ArrayResource* m_textureCoords = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Texture2DSamplingScene.h b/tests/integration/test-content/TestScenes/Texture2DSamplingScene.h similarity index 71% rename from integration/TestContent/include/TestScenes/Texture2DSamplingScene.h rename to tests/integration/test-content/TestScenes/Texture2DSamplingScene.h index 95e7d2589..2f464ffeb 100644 --- a/integration/TestContent/include/TestScenes/Texture2DSamplingScene.h +++ b/tests/integration/test-content/TestScenes/Texture2DSamplingScene.h @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE2DSAMPLINGSCENE_H -#define RAMSES_TEXTURE2DSAMPLINGSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" namespace ramses { class TextureSampler; - class GeometryBinding; + class Geometry; class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class Texture2DSamplingScene : public IntegrationScene @@ -54,13 +53,11 @@ namespace ramses_internal void createTwoQuads(float x); - [[nodiscard]] ramses::ETextureSamplingMethod getMinSamplingMethod(EState state) const; - [[nodiscard]] ramses::ETextureSamplingMethod getMagSamplingMethod(EState state) const; + [[nodiscard]] static ramses::ETextureSamplingMethod GetMinSamplingMethod(EState state); + [[nodiscard]] static ramses::ETextureSamplingMethod GetMagSamplingMethod(EState state); - const ramses::ArrayResource* m_indexArray; - const ramses::ArrayResource* m_vertexPositions; - const ramses::ArrayResource* m_textureCoords; + const ramses::ArrayResource* m_indexArray = nullptr; + const ramses::ArrayResource* m_vertexPositions = nullptr; + const ramses::ArrayResource* m_textureCoords = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Texture3DScene.h b/tests/integration/test-content/TestScenes/Texture3DScene.h similarity index 92% rename from integration/TestContent/include/TestScenes/Texture3DScene.h rename to tests/integration/test-content/TestScenes/Texture3DScene.h index c37a35be9..f50f08037 100644 --- a/integration/TestContent/include/TestScenes/Texture3DScene.h +++ b/tests/integration/test-content/TestScenes/Texture3DScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURE3DSCENE_H -#define RAMSES_TEXTURE3DSCENE_H +#pragma once #include "IntegrationScene.h" @@ -18,7 +17,7 @@ namespace ramses class Texture3D; } -namespace ramses_internal +namespace ramses::internal { class Texture3DScene : public IntegrationScene { @@ -40,5 +39,3 @@ namespace ramses_internal ramses::Texture3D* m_texture; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TextureAddressScene.h b/tests/integration/test-content/TestScenes/TextureAddressScene.h similarity index 89% rename from integration/TestContent/include/TestScenes/TextureAddressScene.h rename to tests/integration/test-content/TestScenes/TextureAddressScene.h index 6ca9aa7de..f154d1daf 100644 --- a/integration/TestContent/include/TestScenes/TextureAddressScene.h +++ b/tests/integration/test-content/TestScenes/TextureAddressScene.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTUREADDRESSSCENE_H -#define RAMSES_TEXTUREADDRESSSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/TextureEnums.h" +#include "ramses/framework/TextureEnums.h" namespace ramses { @@ -19,7 +18,7 @@ namespace ramses class Texture2D; } -namespace ramses_internal +namespace ramses::internal { class TextureAddressScene : public IntegrationScene { @@ -46,5 +45,3 @@ namespace ramses_internal ramses::Texture2D* m_texture; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TextureBufferScene.h b/tests/integration/test-content/TestScenes/TextureBufferScene.h similarity index 88% rename from integration/TestContent/include/TestScenes/TextureBufferScene.h rename to tests/integration/test-content/TestScenes/TextureBufferScene.h index f906e68b1..3a19ef96a 100644 --- a/integration/TestContent/include/TestScenes/TextureBufferScene.h +++ b/tests/integration/test-content/TestScenes/TextureBufferScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTUREBUFFERSCENE_H -#define RAMSES_TEXTUREBUFFERSCENE_H +#pragma once #include "IntegrationScene.h" @@ -17,10 +16,10 @@ namespace ramses class Appearance; class Texture2D; class Texture2DBuffer; - class GeometryBinding; + class Geometry; } -namespace ramses_internal +namespace ramses::internal { class TextureBufferScene : public IntegrationScene { @@ -33,6 +32,8 @@ namespace ramses_internal EState_RGBA8_OneMip_ScaledDown, EState_RGBA8_ThreeMips, EState_PartialUpdate, + EState_PartialUpdate1, + EState_PartialUpdate2, EState_PartialUpdateMipMap, EState_PartialUpdateMipMap_RG8, EState_ClientTextureResource_RGBA8, @@ -50,11 +51,9 @@ namespace ramses_internal ramses::Effect& m_effectAllMips; ramses::Appearance& m_appearanceSingleMip; ramses::Appearance& m_appearanceAllMips; - ramses::GeometryBinding& m_geometrySingleMip; - ramses::GeometryBinding& m_geometryAllMips; + ramses::Geometry& m_geometrySingleMip; + ramses::Geometry& m_geometryAllMips; ramses::Texture2D* m_clientTexture = nullptr; ramses::Texture2DBuffer* m_textureBuffer = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h b/tests/integration/test-content/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h similarity index 81% rename from integration/TestContent/include/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h rename to tests/integration/test-content/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h index fb742b43e..b6021e432 100644 --- a/integration/TestContent/include/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h +++ b/tests/integration/test-content/TestScenes/TextureCubeAnisotropicTextureFilteringScene.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURECUBEANISOTROPICTEXTUREFILTERINGSCENE_H -#define RAMSES_TEXTURECUBEANISOTROPICTEXTUREFILTERINGSCENE_H +#pragma once #include "IntegrationScene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/MipLevelData.h" -#include "Collections/Vector.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/MipLevelData.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" namespace ramses { @@ -22,7 +21,7 @@ namespace ramses class ArrayResource; } -namespace ramses_internal +namespace ramses::internal { class TextureCubeAnisotropicTextureFilteringScene : public IntegrationScene { @@ -49,5 +48,3 @@ namespace ramses_internal static void FillMipLevelData(uint8_t* data, uint32_t resolution, uint32_t level); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TextureLinkScene.h b/tests/integration/test-content/TestScenes/TextureLinkScene.h similarity index 85% rename from integration/TestContent/include/TestScenes/TextureLinkScene.h rename to tests/integration/test-content/TestScenes/TextureLinkScene.h index 4042178d5..9be7fd37c 100644 --- a/integration/TestContent/include/TestScenes/TextureLinkScene.h +++ b/tests/integration/test-content/TestScenes/TextureLinkScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURELINKSCENE_H -#define RAMSES_TEXTURELINKSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/framework/RamsesFrameworkTypes.h" namespace ramses { @@ -20,7 +19,7 @@ namespace ramses class Texture2D; } -namespace ramses_internal +namespace ramses::internal { class TextureLinkScene : public IntegrationScene { @@ -42,8 +41,6 @@ namespace ramses_internal private: const ramses::TextureSampler& createSampler(const ramses::Texture2D& texture); - void setSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler); + static void SetSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TextureSamplerScene.h b/tests/integration/test-content/TestScenes/TextureSamplerScene.h similarity index 92% rename from integration/TestContent/include/TestScenes/TextureSamplerScene.h rename to tests/integration/test-content/TestScenes/TextureSamplerScene.h index 5169cc0e2..97b74f225 100644 --- a/integration/TestContent/include/TestScenes/TextureSamplerScene.h +++ b/tests/integration/test-content/TestScenes/TextureSamplerScene.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEXTURESAMPLERSCENE_H -#define RAMSES_TEXTURESAMPLERSCENE_H +#pragma once #include "IntegrationScene.h" @@ -18,7 +17,7 @@ namespace ramses class Appearance; } -namespace ramses_internal +namespace ramses::internal { class TextureSamplerScene : public IntegrationScene { @@ -44,5 +43,3 @@ namespace ramses_internal ramses::Appearance* m_appearance = nullptr; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TransformationLinkScene.h b/tests/integration/test-content/TestScenes/TransformationLinkScene.h similarity index 83% rename from integration/TestContent/include/TestScenes/TransformationLinkScene.h rename to tests/integration/test-content/TestScenes/TransformationLinkScene.h index 8da2dd67a..1894266d6 100644 --- a/integration/TestContent/include/TestScenes/TransformationLinkScene.h +++ b/tests/integration/test-content/TestScenes/TransformationLinkScene.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRANSFORMATIONLINKSCENE_H -#define RAMSES_TRANSFORMATIONLINKSCENE_H +#pragma once #include "IntegrationScene.h" -#include "SceneAPI/Handles.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include "TestScenes/Triangle.h" -namespace ramses_internal +namespace ramses::internal { class TransformationLinkScene : public IntegrationScene { @@ -36,8 +35,6 @@ namespace ramses_internal static constexpr ramses::dataConsumerId_t transformConsumerDataId{54u}; private: - ramses::MeshNode* createTriangleMesh(ramses::TriangleAppearance::EColor color); + ramses::MeshNode* createTriangleMesh(TriangleAppearance::EColor color); }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/Triangle.h b/tests/integration/test-content/TestScenes/Triangle.h similarity index 73% rename from integration/TestContent/include/TestScenes/Triangle.h rename to tests/integration/test-content/TestScenes/Triangle.h index c4b3f619e..5a0d1b1f9 100644 --- a/integration/TestContent/include/TestScenes/Triangle.h +++ b/tests/integration/test-content/TestScenes/Triangle.h @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRIANGLE_H -#define RAMSES_TRIANGLE_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/UniformInput.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/UniformInput.h" #include "TestScenes/TriangleAppearance.h" #include "TestScenes/TriangleGeometry.h" @@ -19,23 +18,26 @@ namespace ramses { class Scene; class Appearance; - class GeometryBinding; + class Geometry; class ArrayResource; class ArrayResourceInput; class Effect; class UniformInput; class DataObject; +} +namespace ramses::internal +{ class Triangle { public: - Triangle(Scene& scene, const Effect& effect, enum TriangleAppearance::EColor color, float alpha = 1.f, TriangleGeometry::EVerticesOrder vertOrder = TriangleGeometry::EVerticesOrder_CCW); + Triangle(ramses::Scene& scene, const Effect& effect, enum TriangleAppearance::EColor color, float alpha = 1.f, TriangleGeometry::EVerticesOrder vertOrder = TriangleGeometry::EVerticesOrder_CCW); Appearance& GetAppearance() { return m_appearance.GetAppearance(); } - GeometryBinding& GetGeometry() + Geometry& GetGeometry() { return m_geometry.GetGeometry(); } @@ -48,5 +50,3 @@ namespace ramses TriangleGeometry m_geometry; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TriangleAppearance.h b/tests/integration/test-content/TestScenes/TriangleAppearance.h similarity index 75% rename from integration/TestContent/include/TestScenes/TriangleAppearance.h rename to tests/integration/test-content/TestScenes/TriangleAppearance.h index 1bf378308..54aeba807 100644 --- a/integration/TestContent/include/TestScenes/TriangleAppearance.h +++ b/tests/integration/test-content/TestScenes/TriangleAppearance.h @@ -6,24 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRIANGLEAPPEARANCE_H -#define RAMSES_TRIANGLEAPPEARANCE_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/UniformInput.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/UniformInput.h" namespace ramses { class RamsesClient; class Scene; class Appearance; - class GeometryBinding; + class Geometry; class ArrayResource; class ArrayResourceInput; class Effect; class UniformInput; class DataObject; +} +namespace ramses::internal +{ class TriangleAppearance { public: @@ -36,7 +38,7 @@ namespace ramses EColor_Grey }; - TriangleAppearance(Scene& scene, const Effect& effect, enum EColor color, float alpha = 1.f); + TriangleAppearance(ramses::Scene& scene, const Effect& effect, enum EColor color, float alpha = 1.f); Appearance& GetAppearance() { @@ -48,11 +50,9 @@ namespace ramses void unbindColor(); private: - static Appearance& createAppearance(const Effect& effect, Scene& scene); + static Appearance& createAppearance(const Effect& effect, ramses::Scene& scene); - UniformInput m_colorInput; - Appearance& m_appearance; + std::optional m_colorInput; + Appearance& m_appearance; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/TriangleGeometry.h b/tests/integration/test-content/TestScenes/TriangleGeometry.h similarity index 59% rename from integration/TestContent/include/TestScenes/TriangleGeometry.h rename to tests/integration/test-content/TestScenes/TriangleGeometry.h index 4c967a8ad..a6a0550f0 100644 --- a/integration/TestContent/include/TestScenes/TriangleGeometry.h +++ b/tests/integration/test-content/TestScenes/TriangleGeometry.h @@ -6,21 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TRIANGLEGEOMETRY_H -#define RAMSES_TRIANGLEGEOMETRY_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-client-api/UniformInput.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/UniformInput.h" namespace ramses { class Scene; - class GeometryBinding; + class Geometry; class ArrayResource; class ArrayResourceInput; class Effect; class UniformInput; +} +namespace ramses::internal +{ class TriangleGeometry { public: @@ -30,20 +32,18 @@ namespace ramses EVerticesOrder_CCW }; - TriangleGeometry(Scene& scene, const Effect& effect, EVerticesOrder vertOrder = EVerticesOrder_CCW); + TriangleGeometry(ramses::Scene& scene, const Effect& effect, EVerticesOrder vertOrder = EVerticesOrder_CCW); - GeometryBinding& GetGeometry() + Geometry& GetGeometry() { return m_geometry; } private: - static GeometryBinding& createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices); - static const ArrayResource& createIndices(Scene& scene, EVerticesOrder vertOrder); + static Geometry& createGeometry(ramses::Scene& scene, const Effect& effect, const ArrayResource& indices); + static const ArrayResource& createIndices(ramses::Scene& scene, EVerticesOrder vertOrder); const ArrayResource& m_indices; - GeometryBinding& m_geometry; + Geometry& m_geometry; }; } - -#endif diff --git a/integration/TestContent/include/TestScenes/VisibilityScene.h b/tests/integration/test-content/TestScenes/VisibilityScene.h similarity index 91% rename from integration/TestContent/include/TestScenes/VisibilityScene.h rename to tests/integration/test-content/TestScenes/VisibilityScene.h index 280a11612..f2f63af7a 100644 --- a/integration/TestContent/include/TestScenes/VisibilityScene.h +++ b/tests/integration/test-content/TestScenes/VisibilityScene.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_VISIBILITYSCENE_H -#define RAMSES_VISIBILITYSCENE_H +#pragma once #include "IntegrationScene.h" -namespace ramses_internal +namespace ramses::internal { class VisibilityScene : public IntegrationScene { @@ -29,5 +28,3 @@ namespace ramses_internal }; }; } - -#endif diff --git a/integration/TestContent/src/TestStepCommand.cpp b/tests/integration/test-content/TestStepCommand.cpp similarity index 88% rename from integration/TestContent/src/TestStepCommand.cpp rename to tests/integration/test-content/TestStepCommand.cpp index cb20b892d..1b8d75730 100644 --- a/integration/TestContent/src/TestStepCommand.cpp +++ b/tests/integration/test-content/TestStepCommand.cpp @@ -8,7 +8,7 @@ #include "TestStepCommand.h" -namespace ramses_internal +namespace ramses::internal { TestStepCommand::TestStepCommand(): m_testStep(0) @@ -18,14 +18,14 @@ namespace ramses_internal getArgument<0>().setDescription("test step nr."); } - bool TestStepCommand::execute(Int& testStep) const + bool TestStepCommand::execute(uint8_t& testStep) const { m_testStep = testStep; m_testStepSetEvent.signal(); return true; } - Int TestStepCommand::getCurrentTestStep() + uint8_t TestStepCommand::getCurrentTestStep() { return m_testStep.load(); } diff --git a/integration/TestContent/include/TestStepCommand.h b/tests/integration/test-content/TestStepCommand.h similarity index 64% rename from integration/TestContent/include/TestStepCommand.h rename to tests/integration/test-content/TestStepCommand.h index f500e3083..0db0ac242 100644 --- a/integration/TestContent/include/TestStepCommand.h +++ b/tests/integration/test-content/TestStepCommand.h @@ -6,27 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTSTEPCOMMAND_H -#define RAMSES_TESTSTEPCOMMAND_H +#pragma once -#include "Ramsh/RamshCommandArguments.h" -#include "PlatformAbstraction/PlatformEvent.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/PlatformAbstraction/PlatformEvent.h" -namespace ramses_internal +namespace ramses::internal { - class TestStepCommand : public RamshCommandArgs + class TestStepCommand : public RamshCommandArgs { public: explicit TestStepCommand(); - bool execute(Int& testStep) const override; + bool execute(uint8_t& testStep) const override; - Int getCurrentTestStep(); + uint8_t getCurrentTestStep(); void waitForTestStepSetEvent(uint32_t timeoutMillisec = 0u); private: - mutable std::atomic m_testStep; + mutable std::atomic m_testStep; mutable PlatformEvent m_testStepSetEvent; }; } - -#endif diff --git a/integration/TestContent/src/TextScene.cpp b/tests/integration/test-content/TextScene.cpp similarity index 94% rename from integration/TestContent/src/TextScene.cpp rename to tests/integration/test-content/TextScene.cpp index c7bc8ecaa..bea6d57f8 100644 --- a/integration/TestContent/src/TextScene.cpp +++ b/tests/integration/test-content/TextScene.cpp @@ -9,17 +9,17 @@ #if defined(RAMSES_TEXT_ENABLED) #include "TestScenes/TextScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "PlatformAbstraction/PlatformMath.h" -#include "ramses-utils.h" - -namespace ramses_internal +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/OrthographicCamera.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "ramses/client/ramses-utils.h" + +namespace ramses::internal { TextScene::TextScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : TextScene_Base(scene, cameraPosition, vpWidth, vpHeight) @@ -48,8 +48,7 @@ namespace ramses_internal effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); ramses::Effect* textEffect = m_scene.createEffect(effectDesc); - ramses::UniformInput colorInput; - textEffect->findUniformInput("u_color", colorInput); + std::optional colorInput = textEffect->findUniformInput("u_color"); /// Create text lines //const std::u32string str = U"ÄÖÜß"; @@ -113,12 +112,12 @@ namespace ramses_internal for (auto textMesh : { m_meshUTF, m_meshASCII, m_meshDigits, m_meshChinese, m_meshLight, m_meshLightAutoHinting, m_meshShaping, m_meshShapingAutoHint, m_meshFontCascade, m_meshFontCascadeWithVerticalOffset }) { - textMesh->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); + textMesh->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 1.0f, 0.0f, 0.0f, 1.0f }); textMesh->getAppearance()->setBlendingOperations(ramses::EBlendOperation::Add, ramses::EBlendOperation::Add); textMesh->getAppearance()->setBlendingFactors(ramses::EBlendFactor::SrcAlpha, ramses::EBlendFactor::OneMinusSrcAlpha, ramses::EBlendFactor::SrcAlpha, ramses::EBlendFactor::OneMinusSrcAlpha); } for (auto textMesh : { m_meshChinese, m_meshFontCascade, m_meshFontCascadeWithVerticalOffset }) - textMesh->getAppearance()->setInputValue(colorInput, ramses::vec4f{ 0.0f, 0.0f, 1.0f, 1.0f }); + textMesh->getAppearance()->setInputValue(*colorInput, ramses::vec4f{ 0.0f, 0.0f, 1.0f, 1.0f }); ramses::Node* translateUTF = m_scene.createNode(); ramses::Node* translateASCII = m_scene.createNode(); @@ -206,6 +205,7 @@ namespace ramses_internal break; } case EState_SMOKE_TEST: + { m_textOrthoCamera->setViewport(0, 0, 480, 480); const auto scaling = ramses::vec3f(0.5f); m_meshUTF->setScaling(scaling); @@ -248,6 +248,10 @@ namespace ramses_internal m_meshShapingAutoHint->translate({0.f, -43.f, 0.f}); addMeshNodeToDefaultRenderGroup(*m_meshShapingAutoHint, 0); + break; + } + default: + break; } if (EState_INITIAL_128_BY_64_VIEWPORT == state) diff --git a/integration/TestContent/src/TextScene_Base.cpp b/tests/integration/test-content/TextScene_Base.cpp similarity index 91% rename from integration/TestContent/src/TextScene_Base.cpp rename to tests/integration/test-content/TextScene_Base.cpp index 762623304..d46ec5be7 100644 --- a/integration/TestContent/src/TextScene_Base.cpp +++ b/tests/integration/test-content/TextScene_Base.cpp @@ -7,12 +7,12 @@ // ------------------------------------------------------------------------- #include "TestScenes/TextScene_Base.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/Scene.h" +#include "ramses/client/OrthographicCamera.h" #if defined(RAMSES_TEXT_ENABLED) -namespace ramses_internal +namespace ramses::internal { TextScene_Base::TextScene_Base(ramses::Scene& scene, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) diff --git a/integration/TestContent/src/Texture2DAnisotropicTextureFilteringScene.cpp b/tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp similarity index 82% rename from integration/TestContent/src/Texture2DAnisotropicTextureFilteringScene.cpp rename to tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp index 82a851482..48d37d30c 100644 --- a/integration/TestContent/src/Texture2DAnisotropicTextureFilteringScene.cpp +++ b/tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- #include "TestScenes/Texture2DAnisotropicTextureFilteringScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "ramses/client/OrthographicCamera.h" // This test draws two textured quads, in the top half one with 16x anisotropic filtering, below the same with trilinear sampling without anisotropic filtering. // The used texture in mip-level 0 has size 4x4 and looks as following (B = blue, G = green, . = black): @@ -32,7 +32,7 @@ // With anisotropic filtering mip-level 0 is used and the blue and green pixels are averaged by multiple samples. This gives the checkerboard pattern with // colors (0,127,127) and (0,0,0). -namespace ramses_internal +namespace ramses::internal { Texture2DAnisotropicTextureFilteringScene::Texture2DAnisotropicTextureFilteringScene(ramses::Scene& scene, uint32_t /*state*/, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -68,7 +68,7 @@ namespace ramses_internal const float x = 0.0f; const float y = 0.0f; - const float w = static_cast(IntegrationScene::DefaultViewportWidth); + const auto w = static_cast(IntegrationScene::DefaultViewportWidth); const float h = static_cast(IntegrationScene::DefaultViewportHeight) * 0.5f; const float z = -1.0f; @@ -98,9 +98,7 @@ namespace ramses_internal mipLevelData, false); - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); - + const std::optional optTextureInput = effect->findUniformInput("u_texture"); ramses::Appearance* appearance = createQuad(effect, vertexPositions, textureCoords, indices, 0.0f, 0.0f); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -109,7 +107,7 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Linear, *texture, 16u); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*optTextureInput, *sampler); ramses::Appearance* appearance1 = createQuad(effect, vertexPositions, textureCoords, indices, 0.0f, h); ramses::TextureSampler* sampler1 = m_scene.createTextureSampler( @@ -118,7 +116,7 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Linear_MipMapLinear, ramses::ETextureSamplingMethod::Linear, *texture); - appearance1->setInputTexture(textureInput, *sampler1); + appearance1->setInputTexture(*optTextureInput, *sampler1); } void Texture2DAnisotropicTextureFilteringScene::createOrthoCamera() @@ -138,21 +136,15 @@ namespace ramses_internal float y) { ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::MeshNode* mesh = m_scene.createMeshNode("quad"); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); ramses::Node* translateNode = m_scene.createNode(); translateNode->setTranslation({x, y, 0.0f}); diff --git a/integration/TestContent/src/Texture2DCompressedMipMapScene.cpp b/tests/integration/test-content/Texture2DCompressedMipMapScene.cpp similarity index 67% rename from integration/TestContent/src/Texture2DCompressedMipMapScene.cpp rename to tests/integration/test-content/Texture2DCompressedMipMapScene.cpp index 7bd16c99c..96a870bbb 100644 --- a/integration/TestContent/src/Texture2DCompressedMipMapScene.cpp +++ b/tests/integration/test-content/Texture2DCompressedMipMapScene.cpp @@ -8,31 +8,26 @@ #include "TestScenes/Texture2DCompressedMipMapScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/OrthographicCamera.h" - -#include "Collections/Vector.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/OrthographicCamera.h" + +#include "internal/PlatformAbstraction/Collections/Vector.h" #include // Renders one horizontal stripe for each mip-level. -namespace ramses_internal +namespace ramses::internal { - Texture2DCompressedMipMapScene::Texture2DCompressedMipMapScene(ramses::Scene& scene, - uint32_t state, - const glm::vec3& cameraPosition) + Texture2DCompressedMipMapScene::Texture2DCompressedMipMapScene(ramses::Scene& scene, [[maybe_unused]] uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) - , m_textureWidth(8) - , m_textureHeight(8) { - UNUSED(state); createOrthoCamera(); const uint8_t dataLevel0[] = {0x7e, 0x80, 0x4, 0x7f, 0x0, 0x7, 0xe0, 0x0,0x81, 0x7e, 0x4, 0x2, 0xfe, 0x0, 0x1f, 0xc0, 0x80, 0x81, 0xfb, 0x82, 0x1, 0xf8, 0x0, 0x3f, 0xf8, 0xf8, 0xf8, 0x2, 0x0, 0x0, 0x0, 0x0}; @@ -72,28 +67,21 @@ namespace ramses_internal ramses::Effect* effect = getTestEffect("ramses-test-client-textured"); assert(effect != nullptr); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - ramses::UniformInput textureInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - effect->findUniformInput("u_texture", textureInput); - ramses::Appearance* appearance = m_scene.createAppearance(*effect); - appearance->setInputTexture(textureInput, sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), sampler); - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect); + ramses::Geometry* geometry = m_scene.createGeometry(*effect); createGeometry(); geometry->setIndices(*m_indexArray); - geometry->setInputBuffer(positionsInput, *m_vertexPositions); - geometry->setInputBuffer(texCoordsInput, *m_textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *m_vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *m_textureCoords); ramses::MeshNode* mesh = m_scene.createMeshNode(); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); } void Texture2DCompressedMipMapScene::createGeometry() @@ -107,7 +95,7 @@ namespace ramses_internal std::vector textureCoords; const float x = 0.0f; - const float w = static_cast(IntegrationScene::DefaultViewportWidth); + const auto w = static_cast(IntegrationScene::DefaultViewportWidth); const float h = static_cast(IntegrationScene::DefaultViewportHeight) / numberStripes; float s = w / m_textureWidth; @@ -117,15 +105,15 @@ namespace ramses_internal { const float y = h * i; - vertexPositions.push_back(ramses::vec3f{ x, y, z }); - vertexPositions.push_back(ramses::vec3f{ x+w, y, z }); - vertexPositions.push_back(ramses::vec3f{ x+w, y+h, z }); - vertexPositions.push_back(ramses::vec3f{ x, y+h, z }); + vertexPositions.emplace_back(x, y, z); + vertexPositions.emplace_back(x+w, y, z); + vertexPositions.emplace_back(x+w, y+h, z); + vertexPositions.emplace_back(x, y+h, z); - textureCoords.push_back(ramses::vec2f{ 0.f, 0.f }); - textureCoords.push_back(ramses::vec2f{ s, 0.f }); - textureCoords.push_back(ramses::vec2f{ s, t }); - textureCoords.push_back(ramses::vec2f{ 0.f, t }); + textureCoords.emplace_back(0.f, 0.f); + textureCoords.emplace_back(s, 0.f); + textureCoords.emplace_back(s, t); + textureCoords.emplace_back(0.f, t); indices.push_back(0 + i * 4); indices.push_back(1 + i * 4); diff --git a/integration/TestContent/src/Texture2DFormatScene.cpp b/tests/integration/test-content/Texture2DFormatScene.cpp similarity index 91% rename from integration/TestContent/src/Texture2DFormatScene.cpp rename to tests/integration/test-content/Texture2DFormatScene.cpp index 501f63ce9..0a07adfb5 100644 --- a/integration/TestContent/src/Texture2DFormatScene.cpp +++ b/tests/integration/test-content/Texture2DFormatScene.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- #include "TestScenes/Texture2DFormatScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-client-api/TextureEnums.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/framework/TextureEnums.h" #include /* @@ -224,7 +224,7 @@ const ramses::MipLevelData mipLevelData_srgb8a8Data(sizeof(srgb8a8Data), reinter const ramses::MipLevelData mipLevelData_null; -namespace ramses_internal +namespace ramses::internal { Texture2DFormatScene::Texture2DFormatScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -236,7 +236,7 @@ namespace ramses_internal uint32_t height = 0u; ramses::TextureSwizzle swizzle = {}; - const ramses::MipLevelData& mipLevelData = getTextureFormatAndData(static_cast(state), format, width, height, swizzle); + const ramses::MipLevelData& mipLevelData = GetTextureFormatAndData(static_cast(state), format, width, height, swizzle); ramses::Texture2D* texture = m_scene.createTexture2D( format, @@ -289,30 +289,23 @@ namespace ramses_internal ramses::Effect* effect = getTestEffect("ramses-test-client-textured"); assert(effect != nullptr); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - ramses::UniformInput textureInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - effect->findUniformInput("u_texture", textureInput); - ramses::Appearance* appearance = m_scene.createAppearance(*effect); - appearance->setInputTexture(textureInput, sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), sampler); appearance->setBlendingFactors(ramses::EBlendFactor::SrcAlpha, ramses::EBlendFactor::OneMinusSrcAlpha, ramses::EBlendFactor::One, ramses::EBlendFactor::OneMinusSrcAlpha); appearance->setBlendingOperations(ramses::EBlendOperation::Add, ramses::EBlendOperation::Add); - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect); + ramses::Geometry* geometry = m_scene.createGeometry(*effect); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::MeshNode* mesh = m_scene.createMeshNode(); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); } - const ramses::MipLevelData& Texture2DFormatScene::getTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle) const + const ramses::MipLevelData& Texture2DFormatScene::GetTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle) { width = 2u; height = 2u; diff --git a/integration/TestContent/src/Texture2DGenerateMipMapScene.cpp b/tests/integration/test-content/Texture2DGenerateMipMapScene.cpp similarity index 85% rename from integration/TestContent/src/Texture2DGenerateMipMapScene.cpp rename to tests/integration/test-content/Texture2DGenerateMipMapScene.cpp index 753c5891f..39f739b25 100644 --- a/integration/TestContent/src/Texture2DGenerateMipMapScene.cpp +++ b/tests/integration/test-content/Texture2DGenerateMipMapScene.cpp @@ -7,22 +7,22 @@ // ------------------------------------------------------------------------- #include "TestScenes/Texture2DGenerateMipMapScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/OrthographicCamera.h" #include // Uses mip-map generation to generate LOD level-1 from level-0 data. Draws two quads, for the left one, 2x2 texels // map to one pixel, thus LOD level-1 is used, which is the average of the four level-0 texels (red, green, blue, white). // The right quad shows large magnification, LOD level-0 is taken with red, green, blue, white texels. -namespace ramses_internal +namespace ramses::internal { Texture2DGenerateMipMapScene::Texture2DGenerateMipMapScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) @@ -65,28 +65,21 @@ namespace ramses_internal ramses::Effect* effect = getTestEffect("ramses-test-client-textured"); assert(effect != nullptr); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - ramses::UniformInput textureInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - effect->findUniformInput("u_texture", textureInput); - ramses::Appearance* appearance = m_scene.createAppearance(*effect); - appearance->setInputTexture(textureInput, sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), sampler); - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect); + ramses::Geometry* geometry = m_scene.createGeometry(*effect); createGeometry(); geometry->setIndices(*m_indexArray); - geometry->setInputBuffer(positionsInput, *m_vertexPositions); - geometry->setInputBuffer(texCoordsInput, *m_textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *m_vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *m_textureCoords); ramses::MeshNode* mesh = m_scene.createMeshNode(); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); ramses::Node* transform = m_scene.createNode(); transform->translate({translateXY, translateXY, 0.0f}); @@ -106,7 +99,7 @@ namespace ramses_internal const float x = 0.0f; const float y = 0.0f; const float w = float(IntegrationScene::DefaultViewportWidth) * 0.5f; - const float h = float(IntegrationScene::DefaultViewportHeight); + const auto h = float(IntegrationScene::DefaultViewportHeight); const float x2 = w; const float y2 = 0.0f; @@ -156,7 +149,7 @@ namespace ramses_internal const uint32_t idx_blue = pixelCount * 2; const uint32_t idx_white = pixelCount * 3; - uint8_t* rgba8_level0 = new uint8_t[pixelCount * 4]; + auto* rgba8_level0 = new uint8_t[pixelCount * 4]; for (uint32_t pixel = 0u; pixel < pixelCount; pixel++) { const uint32_t idx = pixel * 4; diff --git a/integration/TestContent/src/Texture2DSamplingScene.cpp b/tests/integration/test-content/Texture2DSamplingScene.cpp similarity index 90% rename from integration/TestContent/src/Texture2DSamplingScene.cpp rename to tests/integration/test-content/Texture2DSamplingScene.cpp index 6f1ee12a7..556cefe57 100644 --- a/integration/TestContent/src/Texture2DSamplingScene.cpp +++ b/tests/integration/test-content/Texture2DSamplingScene.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "TestScenes/Texture2DSamplingScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/OrthographicCamera.h" #include // The magic of these tests is that they must show a significant difference in the output picture, when the wrong sampling mode is @@ -26,7 +26,7 @@ // The output picture of the tests are looking quite synthetic - stripes and dotted pixel patterns - so not trivial to see // why it must look like that, that's why the expected result is explained in the createGeometry methods for the different modes. -namespace ramses_internal +namespace ramses::internal { Texture2DSamplingScene::Texture2DSamplingScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) @@ -34,7 +34,7 @@ namespace ramses_internal { createOrthoCamera(); - const EState enumState = static_cast(state); + const auto enumState = static_cast(state); const uint8_t rgb8_level0[] = { 0xff,0xff,0xff, 0xff,0xff,0xff, 0x00,0x00,0x00, 0x00,0x00,0x00, @@ -68,8 +68,8 @@ namespace ramses_internal ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, - getMinSamplingMethod(enumState), - getMagSamplingMethod(enumState), + GetMinSamplingMethod(enumState), + GetMagSamplingMethod(enumState), *texture); createMesh(*sampler, enumState); @@ -123,31 +123,24 @@ namespace ramses_internal ramses::Effect* effect = getTestEffect("ramses-test-client-textured"); assert(effect != nullptr); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - ramses::UniformInput textureInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_texcoord", texCoordsInput); - effect->findUniformInput("u_texture", textureInput); - ramses::Appearance* appearance = m_scene.createAppearance(*effect); - appearance->setInputTexture(textureInput, sampler); + appearance->setInputTexture(*effect->findUniformInput("u_texture"), sampler); - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect); + ramses::Geometry* geometry = m_scene.createGeometry(*effect); createGeometry(state); geometry->setIndices(*m_indexArray); - geometry->setInputBuffer(positionsInput, *m_vertexPositions); - geometry->setInputBuffer(texCoordsInput, *m_textureCoords); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *m_vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_texcoord"), *m_textureCoords); ramses::MeshNode* mesh = m_scene.createMeshNode(); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); } - ramses::ETextureSamplingMethod Texture2DSamplingScene::getMinSamplingMethod(EState state) const + ramses::ETextureSamplingMethod Texture2DSamplingScene::GetMinSamplingMethod(EState state) { switch (state) { @@ -173,7 +166,7 @@ namespace ramses_internal } } - ramses::ETextureSamplingMethod Texture2DSamplingScene::getMagSamplingMethod(EState state) const + ramses::ETextureSamplingMethod Texture2DSamplingScene::GetMagSamplingMethod(EState state) { switch (state) { @@ -261,7 +254,7 @@ namespace ramses_internal const float y = 0.0f; const float w = static_cast(IntegrationScene::DefaultViewportWidth) / 2.0f; - const float h = static_cast(IntegrationScene::DefaultViewportHeight); + const auto h = static_cast(IntegrationScene::DefaultViewportHeight); const float x2 = w; const float y2 = 0.0f; @@ -340,7 +333,7 @@ namespace ramses_internal const float x = 0.0f; const float y = 0.0f; const float w = static_cast(IntegrationScene::DefaultViewportWidth) * 0.35f; - const float h = static_cast(IntegrationScene::DefaultViewportHeight); + const auto h = static_cast(IntegrationScene::DefaultViewportHeight); const float x2 = static_cast(IntegrationScene::DefaultViewportWidth) - w; const float y2 = 0.0f; diff --git a/integration/TestContent/src/Texture3DScene.cpp b/tests/integration/test-content/Texture3DScene.cpp similarity index 79% rename from integration/TestContent/src/Texture3DScene.cpp rename to tests/integration/test-content/Texture3DScene.cpp index 577485b0e..86e6b7724 100644 --- a/integration/TestContent/src/Texture3DScene.cpp +++ b/tests/integration/test-content/Texture3DScene.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- #include "TestScenes/Texture3DScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" - -namespace ramses_internal +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" + +namespace ramses::internal { Texture3DScene::Texture3DScene(ramses::Scene& scene, uint32_t /*state*/, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -93,16 +93,11 @@ namespace ramses_internal ramses::vec3f{ 2.f * texCoordMagnifier, 2.f * texCoordMagnifier, depth * texCoordMagnifier } }; const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(4u, textureCoordsArray.data()); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - m_effect->findAttributeInput("a_position", positionsInput); - m_effect->findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*m_effect, "quad geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*m_effect, "quad geometry"); geometry->setIndices(*m_indices); - geometry->setInputBuffer(positionsInput, *m_vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_position"), *m_vertexPositions); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_texcoord"), *textureCoords); ramses::TextureSampler* sampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, @@ -112,14 +107,12 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Nearest, *m_texture); - ramses::UniformInput textureInput; - m_effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*m_effect->findUniformInput("u_texture"), *sampler); ramses::MeshNode* mesh = m_scene.createMeshNode("quad"); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({x, y, -12.5f}); diff --git a/integration/TestContent/src/TextureAddressScene.cpp b/tests/integration/test-content/TextureAddressScene.cpp similarity index 74% rename from integration/TestContent/src/TextureAddressScene.cpp rename to tests/integration/test-content/TextureAddressScene.cpp index 8db4dc03d..d580ab08b 100644 --- a/integration/TestContent/src/TextureAddressScene.cpp +++ b/tests/integration/test-content/TextureAddressScene.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- #include "TestScenes/TextureAddressScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" -namespace ramses_internal +namespace ramses::internal { TextureAddressScene::TextureAddressScene(ramses::Scene& scene, uint32_t /*state*/, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -56,21 +56,16 @@ namespace ramses_internal { ramses::Appearance* appearance = m_scene.createAppearance(*m_effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - m_effect->findAttributeInput("a_position", positionsInput); - m_effect->findAttributeInput("a_texcoord", texCoordsInput); - // set vertex positions directly in geometry - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*m_effect, "quad geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*m_effect, "quad geometry"); geometry->setIndices(*m_indices); - geometry->setInputBuffer(positionsInput, *m_vertexPositions); - geometry->setInputBuffer(texCoordsInput, *m_textureCoords); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_position"), *m_vertexPositions); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_texcoord"), *m_textureCoords); ramses::MeshNode* mesh = m_scene.createMeshNode("quad"); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({x, y, -12.5f}); @@ -85,8 +80,6 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Linear, *m_texture); - ramses::UniformInput textureInput; - m_effect->findUniformInput("u_texture", textureInput); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*m_effect->findUniformInput("u_texture"), *sampler); } } diff --git a/integration/TestContent/src/TextureBufferScene.cpp b/tests/integration/test-content/TextureBufferScene.cpp similarity index 80% rename from integration/TestContent/src/TextureBufferScene.cpp rename to tests/integration/test-content/TextureBufferScene.cpp index 73164efc5..1be93ec06 100644 --- a/integration/TestContent/src/TextureBufferScene.cpp +++ b/tests/integration/test-content/TextureBufferScene.cpp @@ -8,19 +8,19 @@ #include "TestScenes/TextureBufferScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PerspectiveCamera.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" #include -namespace ramses_internal +namespace ramses::internal { TextureBufferScene::TextureBufferScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -29,8 +29,8 @@ namespace ramses_internal , m_effectAllMips(*getTestEffect("ramses-test-client-texture-buffer-allmips")) , m_appearanceSingleMip(*scene.createAppearance(m_effectSingleMip)) , m_appearanceAllMips(*scene.createAppearance(m_effectAllMips)) - , m_geometrySingleMip(*m_scene.createGeometryBinding(m_effectSingleMip)) - , m_geometryAllMips(*m_scene.createGeometryBinding(m_effectAllMips)) + , m_geometrySingleMip(*m_scene.createGeometry(m_effectSingleMip)) + , m_geometryAllMips(*m_scene.createGeometry(m_effectAllMips)) { setOrthoCamera(cameraPosition); @@ -62,23 +62,13 @@ namespace ramses_internal m_geometryAllMips.setIndices(*indices); { - ramses::AttributeInput verticesInput; - ramses::AttributeInput texcoordInput; - m_effectSingleMip.findAttributeInput("a_position", verticesInput); - m_effectSingleMip.findAttributeInput("a_texcoord", texcoordInput); - - m_geometrySingleMip.setInputBuffer(verticesInput, *vertices); - m_geometrySingleMip.setInputBuffer(texcoordInput, *texcoords); + m_geometrySingleMip.setInputBuffer(*m_effectSingleMip.findAttributeInput("a_position"), *vertices); + m_geometrySingleMip.setInputBuffer(*m_effectSingleMip.findAttributeInput("a_texcoord"), *texcoords); } { - ramses::AttributeInput verticesInput; - ramses::AttributeInput texcoordInput; - m_effectAllMips.findAttributeInput("a_position", verticesInput); - m_effectAllMips.findAttributeInput("a_texcoord", texcoordInput); - - m_geometryAllMips.setInputBuffer(verticesInput, *vertices); - m_geometryAllMips.setInputBuffer(texcoordInput, *texcoords); + m_geometryAllMips.setInputBuffer(*m_effectAllMips.findAttributeInput("a_position"), *vertices); + m_geometryAllMips.setInputBuffer(*m_effectAllMips.findAttributeInput("a_texcoord"), *texcoords); } setState(state); @@ -99,6 +89,11 @@ namespace ramses_internal { // Gradients of monochrome texels in different colors const uint8_t rgba_1x1_red[] = { 255, 0, 0, 255 }; + const uint8_t rgba_2x2_yellow[] = + { + 64 , 64, 0, 255 , 128, 128, 0, 255, + 192, 192, 0, 255 , 255, 255, 0, 255 + }; const uint8_t rgba_2x2_green[] = { 0, 64 , 0, 255 , 0, 128, 0, 255, @@ -164,6 +159,19 @@ namespace ramses_internal mipToFetch = 0; break; } + case EState_PartialUpdate1: + assert(m_textureBuffer); + m_textureBuffer->updateData(0, 1, 1, 2, 2, rgba_2x2_yellow); + sampler = m_scene.createTextureSampler(ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Nearest_MipMapNearest, ramses::ETextureSamplingMethod::Nearest, *m_textureBuffer); + mipToFetch = 0; + break; + case EState_PartialUpdate2: + assert(m_textureBuffer); + m_textureBuffer->updateData(0, 1, 1, 1, 1, rgba_1x1_red); + m_textureBuffer->updateData(0, 1, 2, 1, 1, rgba_1x1_red); + sampler = m_scene.createTextureSampler(ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Nearest_MipMapNearest, ramses::ETextureSamplingMethod::Nearest, *m_textureBuffer); + mipToFetch = 0; + break; case EState_PartialUpdateMipMap: { assert(m_textureBuffer == nullptr); @@ -234,24 +242,16 @@ namespace ramses_internal if (mipToFetch < 0) { m_quadMesh.setAppearance(m_appearanceAllMips); - m_quadMesh.setGeometryBinding(m_geometryAllMips); - - ramses::UniformInput samplerInput; - m_effectAllMips.findUniformInput("u_texture", samplerInput); - m_appearanceAllMips.setInputTexture(samplerInput, *sampler); + m_quadMesh.setGeometry(m_geometryAllMips); + m_appearanceAllMips.setInputTexture(*m_effectAllMips.findUniformInput("u_texture"), *sampler); } else { m_quadMesh.setAppearance(m_appearanceSingleMip); - m_quadMesh.setGeometryBinding(m_geometrySingleMip); - - ramses::UniformInput samplerInput; - m_effectSingleMip.findUniformInput("u_texture", samplerInput); - m_appearanceSingleMip.setInputTexture(samplerInput, *sampler); + m_quadMesh.setGeometry(m_geometrySingleMip); - ramses::UniformInput mipInput; - m_effectSingleMip.findUniformInput("u_mip", mipInput); - m_appearanceSingleMip.setInputValue(mipInput, mipToFetch); + m_appearanceSingleMip.setInputTexture(*m_effectSingleMip.findUniformInput("u_texture"), *sampler); + m_appearanceSingleMip.setInputValue(*m_effectSingleMip.findUniformInput("u_mip"), mipToFetch); } m_scene.flush(); diff --git a/integration/TestContent/src/TextureCubeAnisotropicTextureFilteringScene.cpp b/tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp similarity index 82% rename from integration/TestContent/src/TextureCubeAnisotropicTextureFilteringScene.cpp rename to tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp index 9e0cdb366..744e5cc43 100644 --- a/integration/TestContent/src/TextureCubeAnisotropicTextureFilteringScene.cpp +++ b/tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp @@ -7,17 +7,17 @@ // ------------------------------------------------------------------------- #include "TestScenes/TextureCubeAnisotropicTextureFilteringScene.h" -#include "ramses-utils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "ramses/client/OrthographicCamera.h" +#include "internal/PlatformAbstraction/PlatformMath.h" // This test draws two textured quads, in the top half one with 16x anisotropic filtering, below the same with trilinear sampling without anisotropic filtering. // The used texture in mip-level 0 has the following repeating 4x4 pattern (B = blue, G = green, . = black): @@ -33,7 +33,7 @@ // With anisotropic filtering mip-level 0 is used and the blue and green pixels are averaged by multiple samples. This gives the checkerboard pattern with // colors (0,127,127) and (0,0,0). -namespace ramses_internal +namespace ramses::internal { void TextureCubeAnisotropicTextureFilteringScene::FillMipLevelData(uint8_t* data, uint32_t resolution, uint32_t level) @@ -121,7 +121,7 @@ namespace ramses_internal /// Vertically, 1 texel map to one pixel => IntegrationScene::DefaultViewportHeight texels needed. /// Cube map textures are of square size, so take larger value. const uint32_t numberOfMipLevels = GetNextLargerPowerOf2Exponent(std::max(IntegrationScene::DefaultViewportWidth * 2, uint32_t(IntegrationScene::DefaultViewportHeight))) + 1; - ramses::CubeMipLevelData* mipLevelData = new ramses::CubeMipLevelData[numberOfMipLevels]; + auto* mipLevelData = new ramses::CubeMipLevelData[numberOfMipLevels]; const uint32_t textureResolution = 1u << (numberOfMipLevels - 1); @@ -129,16 +129,16 @@ namespace ramses_internal { const uint32_t resolution = textureResolution >> i; const uint32_t levelSize = resolution * resolution * 3; - uint8_t* rgb8_data = new uint8_t[levelSize]; + auto* rgb8_data = new uint8_t[levelSize]; FillMipLevelData(rgb8_data, resolution, i); mipLevelData[i].m_faceDataSize = levelSize; - mipLevelData[i].m_dataNX = rgb8_data; - mipLevelData[i].m_dataPX = rgb8_data; - mipLevelData[i].m_dataNY = rgb8_data; - mipLevelData[i].m_dataPY = rgb8_data; - mipLevelData[i].m_dataNZ = rgb8_data; - mipLevelData[i].m_dataPZ = rgb8_data; + mipLevelData[i].m_dataNX = reinterpret_cast(rgb8_data); + mipLevelData[i].m_dataPX = reinterpret_cast(rgb8_data); + mipLevelData[i].m_dataNY = reinterpret_cast(rgb8_data); + mipLevelData[i].m_dataPY = reinterpret_cast(rgb8_data); + mipLevelData[i].m_dataNZ = reinterpret_cast(rgb8_data); + mipLevelData[i].m_dataPZ = reinterpret_cast(rgb8_data); } ramses::Effect* effect(getTestEffect("ramses-test-client-textured-cube")); @@ -148,7 +148,7 @@ namespace ramses_internal const float x = 0.0f; const float y = 0.0f; - const float w = static_cast(IntegrationScene::DefaultViewportWidth); + const auto w = static_cast(IntegrationScene::DefaultViewportWidth); const float h = static_cast(IntegrationScene::DefaultViewportHeight) * 0.5f; const float z = -1.0f; @@ -184,8 +184,8 @@ namespace ramses_internal } delete[] mipLevelData; - ramses::UniformInput textureInput; - effect->findUniformInput("u_texture", textureInput); + std::optional textureInput = effect->findUniformInput("u_texture"); + assert(textureInput.has_value()); ramses::Appearance* appearance = createQuad(effect, vertexPositions, normals, indices, 0.0f, 0.0f); ramses::TextureSampler* sampler = m_scene.createTextureSampler( @@ -195,7 +195,7 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Linear, *texture, 16u); - appearance->setInputTexture(textureInput, *sampler); + appearance->setInputTexture(*textureInput, *sampler); ramses::Appearance* appearance1 = createQuad(effect, vertexPositions, normals, indices, 0.0f, h); ramses::TextureSampler* sampler1 = m_scene.createTextureSampler( @@ -204,7 +204,7 @@ namespace ramses_internal ramses::ETextureSamplingMethod::Linear_MipMapLinear, ramses::ETextureSamplingMethod::Linear, *texture); - appearance1->setInputTexture(textureInput, *sampler1); + appearance1->setInputTexture(*textureInput, *sampler1); } uint32_t TextureCubeAnisotropicTextureFilteringScene::GetNextLargerPowerOf2Exponent(uint32_t value) @@ -234,21 +234,15 @@ namespace ramses_internal float y) { ramses::Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); - - ramses::AttributeInput positionsInput; - ramses::AttributeInput normalsInput; - effect->findAttributeInput("a_position", positionsInput); - effect->findAttributeInput("a_normal", normalsInput); - - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*effect, "geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(normalsInput, *normals); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_normal"), *normals); ramses::MeshNode* mesh = m_scene.createMeshNode("quad"); addMeshNodeToDefaultRenderGroup(*mesh); mesh->setAppearance(*appearance); - mesh->setGeometryBinding(*geometry); + mesh->setGeometry(*geometry); ramses::Node* translateNode = m_scene.createNode(); translateNode->setTranslation({x, y, 0.0f}); diff --git a/integration/TestContent/src/TextureLinkScene.cpp b/tests/integration/test-content/TextureLinkScene.cpp similarity index 69% rename from integration/TestContent/src/TextureLinkScene.cpp rename to tests/integration/test-content/TextureLinkScene.cpp index 524af38b2..3fa6b7770 100644 --- a/integration/TestContent/src/TextureLinkScene.cpp +++ b/tests/integration/test-content/TextureLinkScene.cpp @@ -7,31 +7,31 @@ // ------------------------------------------------------------------------- #include "TestScenes/TextureLinkScene.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderTarget.h" - -#include "Scene/ClientScene.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderTarget.h" + +#include "internal/SceneGraph/Scene/ClientScene.h" #include "TestScenes/Triangle.h" #include -namespace ramses_internal +namespace ramses::internal { TextureLinkScene::TextureLinkScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) { ramses::Effect* effect = (state == DATA_CONSUMER_MS) ? getTestEffect("ramses-test-client-render-one-buffer-ms") : getTestEffect("ramses-test-client-textured"); - ramses::Triangle triangle1(scene, *effect, ramses::TriangleAppearance::EColor_Red); - ramses::Triangle triangle2(scene, *effect, ramses::TriangleAppearance::EColor_Green); + Triangle triangle1(scene, *effect, TriangleAppearance::EColor_Red); + Triangle triangle2(scene, *effect, TriangleAppearance::EColor_Green); ramses::MeshNode* mesh1 = scene.createMeshNode(); ramses::MeshNode* mesh2 = scene.createMeshNode(); @@ -44,8 +44,8 @@ namespace ramses_internal mesh1->setAppearance(appearance1); mesh2->setAppearance(appearance2); - mesh1->setGeometryBinding(triangle1.GetGeometry()); - mesh2->setGeometryBinding(triangle2.GetGeometry()); + mesh1->setGeometry(triangle1.GetGeometry()); + mesh2->setGeometry(triangle2.GetGeometry()); ramses::Node* translate1 = scene.createNode(); ramses::Node* translate2 = scene.createNode(); @@ -79,10 +79,9 @@ namespace ramses_internal const std::array textureCoordsArray{ ramses::vec2f{0.f, 1.f}, ramses::vec2f{1.f, 1.f}, ramses::vec2f{0.f, 0.f} }; const ramses::ArrayResource* textureCoords = m_scene.createArrayResource(3u, textureCoordsArray.data()); - ramses::AttributeInput texCoordsInput; - effect->findAttributeInput("a_texcoord", texCoordsInput); - triangle1.GetGeometry().setInputBuffer(texCoordsInput, *textureCoords); - triangle2.GetGeometry().setInputBuffer(texCoordsInput, *textureCoords); + const std::optional optTexCoordsInput = effect->findAttributeInput("a_texcoord"); + triangle1.GetGeometry().setInputBuffer(*optTexCoordsInput, *textureCoords); + triangle2.GetGeometry().setInputBuffer(*optTexCoordsInput, *textureCoords); switch (state) { @@ -91,10 +90,10 @@ namespace ramses_internal { const std::array pxData{ {0xff, 0x0, 0x0, 0xff} }; const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "ProviderTexture"); + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "ProviderTexture"); const ramses::TextureSampler& sampler = createSampler(texture); - setSampler(appearance1, sampler); - setSampler(appearance2, sampler); + SetSampler(appearance1, sampler); + SetSampler(appearance2, sampler); scene.createTextureProvider(texture, DataProviderId); } @@ -103,17 +102,17 @@ namespace ramses_internal { const std::array pxData{ { 0x0, 0xff, 0x0, 0xff } }; const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "ConsumerTexture"); + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "ConsumerTexture"); const ramses::TextureSampler& sampler = createSampler(texture); - setSampler(appearance1, sampler); - setSampler(appearance2, sampler); + SetSampler(appearance1, sampler); + SetSampler(appearance2, sampler); scene.createTextureConsumer(sampler, DataConsumerId); } break; case DATA_CONSUMER_MS: { - const ramses::RenderBuffer& fallBackBuffer = *m_scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGB8, ramses::ERenderBufferAccessMode::ReadWrite, 4u, "ConsumerBuffer"); + const ramses::RenderBuffer& fallBackBuffer = *m_scene.createRenderBuffer(16u, 16u, ramses::ERenderBufferFormat::RGB8, ramses::ERenderBufferAccessMode::ReadWrite, 4u, "ConsumerBuffer"); // Create a RenderPass that clears the fallBackBuffer to a visible color, so it can be seen in case the fallBackBuffer is shown ramses::RenderPass& renderPassClear = *m_scene.createRenderPass(); ramses::PerspectiveCamera& camera = *m_scene.createPerspectiveCamera(); @@ -131,17 +130,11 @@ namespace ramses_internal const ramses::TextureSamplerMS& sampler = *m_scene.createTextureSamplerMS(fallBackBuffer, "samplerMSConsumer"); - ramses::UniformInput texInput; - appearance1.getEffect().findUniformInput("textureSampler", texInput); - appearance1.setInputTexture(texInput, sampler); - appearance2.getEffect().findUniformInput("textureSampler", texInput); - appearance2.setInputTexture(texInput, sampler); + appearance1.setInputTexture(*appearance1.getEffect().findUniformInput("textureSampler"), sampler); + appearance2.setInputTexture(*appearance2.getEffect().findUniformInput("textureSampler"), sampler); - ramses::UniformInput sampleCountInput; - appearance1.getEffect().findUniformInput("sampleCount", sampleCountInput); - appearance1.setInputValue(sampleCountInput, 4); - appearance2.getEffect().findUniformInput("sampleCount", sampleCountInput); - appearance2.setInputValue(sampleCountInput, 4); + appearance1.setInputValue(*appearance1.getEffect().findUniformInput("sampleCount"), 4); + appearance2.setInputValue(*appearance2.getEffect().findUniformInput("sampleCount"), 4); scene.createTextureConsumer(sampler, DataConsumerId); } @@ -151,11 +144,11 @@ namespace ramses_internal { const std::array pxData{ { 0x0, 0x0, 0xff, 0xff } }; const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "ConsumerProviderTexture"); + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "ConsumerProviderTexture"); const ramses::TextureSampler& sampler1 = createSampler(texture); const ramses::TextureSampler& sampler2 = createSampler(texture); - setSampler(appearance1, sampler1); - setSampler(appearance2, sampler2); + SetSampler(appearance1, sampler1); + SetSampler(appearance2, sampler2); scene.createTextureConsumer(sampler1, DataConsumerId); scene.createTextureProvider(texture, DataProviderId); @@ -171,10 +164,8 @@ namespace ramses_internal return *m_scene.createTextureSampler(ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, ramses::ETextureSamplingMethod::Nearest, ramses::ETextureSamplingMethod::Nearest, texture, 1u, "dataLinkSampler"); } - void TextureLinkScene::setSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler) + void TextureLinkScene::SetSampler(ramses::Appearance& appearance, const ramses::TextureSampler& sampler) { - ramses::UniformInput texInput; - appearance.getEffect().findUniformInput("u_texture", texInput); - appearance.setInputTexture(texInput, sampler); + appearance.setInputTexture(*appearance.getEffect().findUniformInput("u_texture"), sampler); } } diff --git a/integration/TestContent/src/TextureSamplerScene.cpp b/tests/integration/test-content/TextureSamplerScene.cpp similarity index 76% rename from integration/TestContent/src/TextureSamplerScene.cpp rename to tests/integration/test-content/TextureSamplerScene.cpp index 8378b725e..b2e1a95a7 100644 --- a/integration/TestContent/src/TextureSamplerScene.cpp +++ b/tests/integration/test-content/TextureSamplerScene.cpp @@ -7,26 +7,26 @@ // ------------------------------------------------------------------------- #include "TestScenes/TextureSamplerScene.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/client/Scene.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Effect.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/OrthographicCamera.h" namespace ramses { class Texture2D; } -namespace ramses_internal +namespace ramses::internal { TextureSamplerScene::TextureSamplerScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition) : IntegrationScene(scene, cameraPosition) @@ -68,26 +68,19 @@ namespace ramses_internal m_appearance = m_scene.createAppearance(*m_effect, "appearance"); - ramses::AttributeInput positionsInput; - ramses::AttributeInput texCoordsInput; - m_effect->findAttributeInput("a_position", positionsInput); - m_effect->findAttributeInput("a_texcoord", texCoordsInput); - - ramses::GeometryBinding* geometry = m_scene.createGeometryBinding(*m_effect, "triangle geometry"); + ramses::Geometry* geometry = m_scene.createGeometry(*m_effect, "triangle geometry"); geometry->setIndices(*indices); - geometry->setInputBuffer(positionsInput, *vertexPositions); - geometry->setInputBuffer(texCoordsInput, *textureCoords); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_position"), *vertexPositions); + geometry->setInputBuffer(*m_effect->findAttributeInput("a_texcoord"), *textureCoords); if (state != EState_NoTextureSampler) { - ramses::UniformInput textureInput; - m_effect->findUniformInput("u_texture", textureInput); - m_appearance->setInputTexture(textureInput, *m_sampler); + m_appearance->setInputTexture(*m_effect->findUniformInput("u_texture"), *m_sampler); } ramses::MeshNode* meshNode = m_scene.createMeshNode("quad"); meshNode->setAppearance(*m_appearance); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); ramses::Node* transNode = m_scene.createNode(); transNode->setTranslation({0.f, 0.f, -4.f}); @@ -125,13 +118,13 @@ namespace ramses_internal } case EState::EState_SetRenderBuffer: { - const ramses::RenderBuffer* buffer = m_scene.createRenderBuffer(16, 16, ramses::ERenderBufferType::Color, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* buffer = m_scene.createRenderBuffer(16, 16, ramses::ERenderBufferFormat::RGBA8, ramses::ERenderBufferAccessMode::ReadWrite); ramses::RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(*buffer); const auto rt = m_scene.createRenderTarget(rtDesc); auto rp = m_scene.createRenderPass(); rp->setClearColor({0.f, 0.f, 1.f, 1.f}); - rp->setClearFlags(ramses::EClearFlags_All); + rp->setClearFlags(ramses::EClearFlag::All); rp->setRenderOrder(-1); auto camera = m_scene.createOrthographicCamera(); camera->setViewport(0, 0, 16, 16); @@ -148,9 +141,7 @@ namespace ramses_internal const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 3, 3, 1, mipLevelData, false); m_sampler = m_scene.createTextureSampler(ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, ramses::ETextureSamplingMethod::Nearest, ramses::ETextureSamplingMethod::Nearest, *texture); - ramses::UniformInput textureInput; - m_effect->findUniformInput("u_texture", textureInput); - m_appearance->setInputTexture(textureInput, *m_sampler); + m_appearance->setInputTexture(*m_effect->findUniformInput("u_texture"), *m_sampler); break; } default: diff --git a/integration/TestContent/src/TransformationLinkScene.cpp b/tests/integration/test-content/TransformationLinkScene.cpp similarity index 86% rename from integration/TestContent/src/TransformationLinkScene.cpp rename to tests/integration/test-content/TransformationLinkScene.cpp index 6d5b374d7..c60188d9d 100644 --- a/integration/TestContent/src/TransformationLinkScene.cpp +++ b/tests/integration/test-content/TransformationLinkScene.cpp @@ -7,14 +7,14 @@ // ------------------------------------------------------------------------- #include "TestScenes/TransformationLinkScene.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" -#include "SceneImpl.h" -#include "Scene/ClientScene.h" +#include "impl/SceneImpl.h" +#include "internal/SceneGraph/Scene/ClientScene.h" -namespace ramses_internal +namespace ramses::internal { TransformationLinkScene::TransformationLinkScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) @@ -31,7 +31,7 @@ namespace ramses_internal rightProviderNode->setTranslation({2.0f, 0.0f, 0.0f}); scene.createTransformationDataProvider(*rightProviderNode, transformProviderDataId_Right); - ramses::TriangleAppearance::EColor color = ramses::TriangleAppearance::EColor_Red; + TriangleAppearance::EColor color = TriangleAppearance::EColor_Red; ramses::MeshNode* mesh = nullptr; switch (state) { @@ -46,7 +46,7 @@ namespace ramses_internal if (TRANSFORMATION_CONSUMER_OVERRIDEN == state) { - color = ramses::TriangleAppearance::EColor_Blue; + color = TriangleAppearance::EColor_Blue; ramses::Node* parentTransformWhichWillBeOverridden = m_scene.createNode(); parentTransformWhichWillBeOverridden->setTranslation({1.2f, 1.2f, 0.0f}); consumerGroupNode->setParent(*parentTransformWhichWillBeOverridden); @@ -59,7 +59,7 @@ namespace ramses_internal } case TRANSFORMATION_CONSUMER_AND_PROVIDER: { - color = ramses::TriangleAppearance::EColor_White; + color = TriangleAppearance::EColor_White; ramses::Node* consumerGroupNode = m_scene.createNode("transform consumer"); scene.createTransformationDataConsumer(*consumerGroupNode, transformConsumerDataId); @@ -80,7 +80,7 @@ namespace ramses_internal } case TRANSFORMATION_PROVIDER: { - color = ramses::TriangleAppearance::EColor_Green; + color = TriangleAppearance::EColor_Green; ramses::Node* rotateNode = m_scene.createNode(); rotateNode->setRotation({0.0f, 0.0f, -60.0f}, ramses::ERotationType::Euler_XYZ); @@ -100,14 +100,14 @@ namespace ramses_internal } } - ramses::MeshNode* TransformationLinkScene::createTriangleMesh(ramses::TriangleAppearance::EColor color) + ramses::MeshNode* TransformationLinkScene::createTriangleMesh(TriangleAppearance::EColor color) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); ramses::MeshNode* mesh = m_scene.createMeshNode(); addMeshNodeToDefaultRenderGroup(*mesh); - ramses::Triangle triangle(m_scene, *effect, color); - mesh->setGeometryBinding(triangle.GetGeometry()); + Triangle triangle(m_scene, *effect, color); + mesh->setGeometry(triangle.GetGeometry()); mesh->setAppearance(triangle.GetAppearance()); return mesh; } diff --git a/integration/TestContent/src/Triangle.cpp b/tests/integration/test-content/Triangle.cpp similarity index 62% rename from integration/TestContent/src/Triangle.cpp rename to tests/integration/test-content/Triangle.cpp index 4209a977c..674f738d9 100644 --- a/integration/TestContent/src/Triangle.cpp +++ b/tests/integration/test-content/Triangle.cpp @@ -6,23 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include - -#include "PlatformAbstraction/PlatformTypes.h" - #include "TestScenes/Triangle.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/DataObject.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/DataObject.h" + +#include +#include -namespace ramses +namespace ramses::internal { - Triangle::Triangle(Scene& scene, const Effect& effect, enum TriangleAppearance::EColor color, float alpha, TriangleGeometry::EVerticesOrder vertOrder) + Triangle::Triangle(ramses::Scene& scene, const Effect& effect, enum TriangleAppearance::EColor color, float alpha, TriangleGeometry::EVerticesOrder vertOrder) : m_appearance(scene, effect, color, alpha) , m_geometry(scene, effect, vertOrder) { diff --git a/tests/integration/test-content/TriangleAppearance.cpp b/tests/integration/test-content/TriangleAppearance.cpp new file mode 100644 index 000000000..8883e2d64 --- /dev/null +++ b/tests/integration/test-content/TriangleAppearance.cpp @@ -0,0 +1,83 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "TestScenes/TriangleAppearance.h" + +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/DataObject.h" + +#include +#include + +namespace ramses::internal +{ + TriangleAppearance::TriangleAppearance(ramses::Scene& scene, const Effect& effect, enum TriangleAppearance::EColor color, float alpha) + : m_appearance(createAppearance(effect, scene)) + { + m_colorInput = effect.findUniformInput("color"); + if (m_colorInput.has_value()) { + setColor(color, alpha); + } + } + + Appearance& TriangleAppearance::createAppearance(const Effect& effect, ramses::Scene& scene) + { + return *scene.createAppearance(effect, "appearance"); + } + + void TriangleAppearance::setColor(enum EColor color, float alpha) + { + [[maybe_unused]] bool status = false; + switch (color) + { + case EColor_Red: + assert(m_colorInput.has_value()); + status = m_appearance.setInputValue(*m_colorInput, vec4f{ 1.f, 0.f, 0.f, alpha }); + break; + case EColor_Blue: + assert(m_colorInput.has_value()); + status = m_appearance.setInputValue(*m_colorInput, vec4f{ 0.f, 0.f, 1.f, alpha }); + break; + case EColor_Green: + assert(m_colorInput.has_value()); + status = m_appearance.setInputValue(*m_colorInput, vec4f{ 0.f, 1.f, 0.f, alpha }); + break; + case EColor_White: + assert(m_colorInput.has_value()); + status = m_appearance.setInputValue(*m_colorInput, vec4f{ 1.f, 1.f, 1.f, alpha }); + break; + case EColor_Grey: + assert(m_colorInput.has_value()); + status = m_appearance.setInputValue(*m_colorInput, vec4f{ 0.5f, 0.5f, 0.5f, alpha }); + break; + default: + assert(false && "Chosen color for triangle is not available!"); + break; + } + + assert(status); + } + + void TriangleAppearance::bindColor(const DataObject& colorDataObject) + { + assert(m_colorInput.has_value()); + [[maybe_unused]] const bool status = m_appearance.bindInput(*m_colorInput, colorDataObject); + assert(status); + } + + void TriangleAppearance::unbindColor() + { + assert(m_colorInput.has_value()); + [[maybe_unused]] const bool status = m_appearance.unbindInput(*m_colorInput); + assert(status); + } +} diff --git a/integration/TestContent/src/TriangleGeometry.cpp b/tests/integration/test-content/TriangleGeometry.cpp similarity index 59% rename from integration/TestContent/src/TriangleGeometry.cpp rename to tests/integration/test-content/TriangleGeometry.cpp index e609e0caf..7b6ae91ac 100644 --- a/integration/TestContent/src/TriangleGeometry.cpp +++ b/tests/integration/test-content/TriangleGeometry.cpp @@ -6,43 +6,39 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include - -#include "PlatformAbstraction/PlatformTypes.h" - #include "TestScenes/TriangleGeometry.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-framework-api/DataTypes.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Effect.h" +#include "ramses/framework/DataTypes.h" -namespace ramses +#include +#include + +namespace ramses::internal { - TriangleGeometry::TriangleGeometry(Scene& scene, const Effect& effect, EVerticesOrder vertOrder) + TriangleGeometry::TriangleGeometry(ramses::Scene& scene, const Effect& effect, EVerticesOrder vertOrder) : m_indices(createIndices(scene, vertOrder)) , m_geometry(createGeometry(scene, effect, m_indices)) { } - GeometryBinding& TriangleGeometry::createGeometry(Scene& scene, const Effect& effect, const ArrayResource& indices) + Geometry& TriangleGeometry::createGeometry(ramses::Scene& scene, const Effect& effect, const ArrayResource& indices) { - ramses::AttributeInput positionsInput; - effect.findAttributeInput("a_position", positionsInput); - - GeometryBinding* geometry = scene.createGeometryBinding(effect, "triangle geometry"); + Geometry* geometry = scene.createGeometry(effect, "triangle geometry"); geometry->setIndices(indices); const std::array vertexPositionsData{ ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{1.f, 0.f, -1.f}, ramses::vec3f{0.f, 1.f, -1.f} }; const ArrayResource* vertexPositions = scene.createArrayResource(3u, vertexPositionsData.data()); - geometry->setInputBuffer(positionsInput, *vertexPositions); + geometry->setInputBuffer(*effect.findAttributeInput("a_position"), *vertexPositions); return *geometry; } - const ArrayResource& TriangleGeometry::createIndices(Scene& scene, EVerticesOrder vertOrder) + const ArrayResource& TriangleGeometry::createIndices(ramses::Scene& scene, EVerticesOrder vertOrder) { const std::array indiceData_ccw = { 0, 1, 2 }; const std::array indiceData_cw = { 0, 2, 1 }; diff --git a/integration/TestContent/src/VisibilityScene.cpp b/tests/integration/test-content/VisibilityScene.cpp similarity index 72% rename from integration/TestContent/src/VisibilityScene.cpp rename to tests/integration/test-content/VisibilityScene.cpp index bdc5978e3..847e50f74 100644 --- a/integration/TestContent/src/VisibilityScene.cpp +++ b/tests/integration/test-content/VisibilityScene.cpp @@ -8,27 +8,27 @@ #include "TestScenes/VisibilityScene.h" #include "TestScenes/Triangle.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-utils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/ramses-utils.h" -namespace ramses_internal +namespace ramses::internal { VisibilityScene::VisibilityScene(ramses::Scene& scene, uint32_t state, const glm::vec3& cameraPosition, uint32_t vpWidth, uint32_t vpHeight) : IntegrationScene(scene, cameraPosition, vpWidth, vpHeight) { ramses::Effect* effect = getTestEffect("ramses-test-client-basic"); - ramses::Triangle triangle1(m_scene, *effect, ramses::TriangleAppearance::EColor_Red, 1.f, ramses::TriangleGeometry::EVerticesOrder_CCW); - ramses::Triangle triangle2(m_scene, *effect, ramses::TriangleAppearance::EColor_Blue, 1.f, ramses::TriangleGeometry::EVerticesOrder_CW); // different vertices order forces different indices resource used + Triangle triangle1(m_scene, *effect, TriangleAppearance::EColor_Red, 1.f, TriangleGeometry::EVerticesOrder_CCW); + Triangle triangle2(m_scene, *effect, TriangleAppearance::EColor_Blue, 1.f, TriangleGeometry::EVerticesOrder_CW); // different vertices order forces different indices resource used triangle2.GetAppearance().setCullingMode(ramses::ECullMode::Disabled); auto triangle1mesh = m_scene.createMeshNode("triangle1"); auto triangle2mesh = m_scene.createMeshNode("triangle2"); triangle1mesh->setAppearance(triangle1.GetAppearance()); - triangle1mesh->setGeometryBinding(triangle1.GetGeometry()); + triangle1mesh->setGeometry(triangle1.GetGeometry()); triangle2mesh->setAppearance(triangle2.GetAppearance()); - triangle2mesh->setGeometryBinding(triangle2.GetGeometry()); + triangle2mesh->setGeometry(triangle2.GetGeometry()); addMeshNodeToDefaultRenderGroup(*triangle1mesh); addMeshNodeToDefaultRenderGroup(*triangle2mesh); triangle1mesh->setTranslation({-1.f, 0.f, -1.f}); @@ -39,8 +39,8 @@ namespace ramses_internal void VisibilityScene::setState(uint32_t state) { - auto triangle1mesh = ramses::RamsesUtils::TryConvert(*m_scene.findObjectByName("triangle1")); - auto triangle2mesh = ramses::RamsesUtils::TryConvert(*m_scene.findObjectByName("triangle2")); + auto triangle1mesh = m_scene.findObject("triangle1"); + auto triangle2mesh = m_scene.findObject("triangle2"); assert(triangle1mesh && triangle2mesh); switch (state) @@ -61,6 +61,8 @@ namespace ramses_internal triangle1mesh->setVisibility(ramses::EVisibilityMode::Visible); triangle2mesh->setVisibility(ramses::EVisibilityMode::Visible); break; + default: + break; } } } diff --git a/integration/TestContent/res/ramses-test-client-3d-textured.frag b/tests/integration/test-content/res/ramses-test-client-3d-textured.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-3d-textured.frag rename to tests/integration/test-content/res/ramses-test-client-3d-textured.frag diff --git a/integration/TestContent/res/ramses-test-client-3d-textured.vert b/tests/integration/test-content/res/ramses-test-client-3d-textured.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-3d-textured.vert rename to tests/integration/test-content/res/ramses-test-client-3d-textured.vert diff --git a/integration/TestContent/res/ramses-test-client-Arimo-Regular.ttf b/tests/integration/test-content/res/ramses-test-client-Arimo-Regular.ttf similarity index 100% rename from integration/TestContent/res/ramses-test-client-Arimo-Regular.ttf rename to tests/integration/test-content/res/ramses-test-client-Arimo-Regular.ttf diff --git a/integration/TestContent/res/ramses-test-client-DroidKufi-Regular.ttf b/tests/integration/test-content/res/ramses-test-client-DroidKufi-Regular.ttf similarity index 100% rename from integration/TestContent/res/ramses-test-client-DroidKufi-Regular.ttf rename to tests/integration/test-content/res/ramses-test-client-DroidKufi-Regular.ttf diff --git a/integration/TestContent/res/ramses-test-client-Roboto-Bold.ttf b/tests/integration/test-content/res/ramses-test-client-Roboto-Bold.ttf similarity index 100% rename from integration/TestContent/res/ramses-test-client-Roboto-Bold.ttf rename to tests/integration/test-content/res/ramses-test-client-Roboto-Bold.ttf diff --git a/integration/TestContent/res/ramses-test-client-Roboto-Light.ttf b/tests/integration/test-content/res/ramses-test-client-Roboto-Light.ttf similarity index 100% rename from integration/TestContent/res/ramses-test-client-Roboto-Light.ttf rename to tests/integration/test-content/res/ramses-test-client-Roboto-Light.ttf diff --git a/integration/TestContent/res/ramses-test-client-WenQuanYiMicroHei.ttf b/tests/integration/test-content/res/ramses-test-client-WenQuanYiMicroHei.ttf similarity index 100% rename from integration/TestContent/res/ramses-test-client-WenQuanYiMicroHei.ttf rename to tests/integration/test-content/res/ramses-test-client-WenQuanYiMicroHei.ttf diff --git a/integration/TestContent/res/ramses-test-client-arrays.frag b/tests/integration/test-content/res/ramses-test-client-arrays.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-arrays.frag rename to tests/integration/test-content/res/ramses-test-client-arrays.frag diff --git a/integration/TestContent/res/ramses-test-client-arrays.vert b/tests/integration/test-content/res/ramses-test-client-arrays.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-arrays.vert rename to tests/integration/test-content/res/ramses-test-client-arrays.vert diff --git a/integration/TestContent/res/ramses-test-client-basic-extended.frag b/tests/integration/test-content/res/ramses-test-client-basic-extended.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-basic-extended.frag rename to tests/integration/test-content/res/ramses-test-client-basic-extended.frag diff --git a/integration/TestContent/res/ramses-test-client-basic-extended.vert b/tests/integration/test-content/res/ramses-test-client-basic-extended.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-basic-extended.vert rename to tests/integration/test-content/res/ramses-test-client-basic-extended.vert diff --git a/integration/TestContent/res/ramses-test-client-basic.frag b/tests/integration/test-content/res/ramses-test-client-basic.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-basic.frag rename to tests/integration/test-content/res/ramses-test-client-basic.frag diff --git a/integration/TestContent/res/ramses-test-client-basic.vert b/tests/integration/test-content/res/ramses-test-client-basic.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-basic.vert rename to tests/integration/test-content/res/ramses-test-client-basic.vert diff --git a/integration/TestContent/res/ramses-test-client-boolUniform.frag b/tests/integration/test-content/res/ramses-test-client-boolUniform.frag similarity index 75% rename from integration/TestContent/res/ramses-test-client-boolUniform.frag rename to tests/integration/test-content/res/ramses-test-client-boolUniform.frag index b4d7bf031..c182ba889 100644 --- a/integration/TestContent/res/ramses-test-client-boolUniform.frag +++ b/tests/integration/test-content/res/ramses-test-client-boolUniform.frag @@ -11,6 +11,7 @@ precision highp float; uniform bool makeSemiTransparent; +uniform bool colorCh[3]; out vec4 fragColor; @@ -22,5 +23,15 @@ void main(void) alpha = 0.5; } - fragColor = vec4(1.0, 0.0, 0.0, alpha); + vec3 color = vec3(0.0); + if (colorCh[0]) + color.r = 1.0; + + if (colorCh[1]) + color.g = 1.0; + + if (colorCh[2]) + color.b = 1.0; + + fragColor = vec4(color, alpha); } diff --git a/integration/TestContent/res/ramses-test-client-boolUniform.vert b/tests/integration/test-content/res/ramses-test-client-boolUniform.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-boolUniform.vert rename to tests/integration/test-content/res/ramses-test-client-boolUniform.vert diff --git a/integration/TestContent/res/ramses-test-client-cube-nx.png b/tests/integration/test-content/res/ramses-test-client-cube-nx.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-cube-nx.png rename to tests/integration/test-content/res/ramses-test-client-cube-nx.png diff --git a/integration/TestContent/res/ramses-test-client-cube-ny.png b/tests/integration/test-content/res/ramses-test-client-cube-ny.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-cube-ny.png rename to tests/integration/test-content/res/ramses-test-client-cube-ny.png diff --git a/integration/TestContent/res/ramses-test-client-cube-nz.png b/tests/integration/test-content/res/ramses-test-client-cube-nz.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-cube-nz.png rename to tests/integration/test-content/res/ramses-test-client-cube-nz.png diff --git a/integration/TestContent/res/ramses-test-client-cube-px.png b/tests/integration/test-content/res/ramses-test-client-cube-px.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-cube-px.png rename to tests/integration/test-content/res/ramses-test-client-cube-px.png diff --git a/integration/TestContent/res/ramses-test-client-cube-py.png b/tests/integration/test-content/res/ramses-test-client-cube-py.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-cube-py.png rename to tests/integration/test-content/res/ramses-test-client-cube-py.png diff --git a/integration/TestContent/res/ramses-test-client-cube-pz.png b/tests/integration/test-content/res/ramses-test-client-cube-pz.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-cube-pz.png rename to tests/integration/test-content/res/ramses-test-client-cube-pz.png diff --git a/integration/TestContent/res/ramses-test-client-cubeSphere.frag b/tests/integration/test-content/res/ramses-test-client-cubeSphere.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-cubeSphere.frag rename to tests/integration/test-content/res/ramses-test-client-cubeSphere.frag diff --git a/integration/TestContent/res/ramses-test-client-cubeSphere.vert b/tests/integration/test-content/res/ramses-test-client-cubeSphere.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-cubeSphere.vert rename to tests/integration/test-content/res/ramses-test-client-cubeSphere.vert diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-float.frag b/tests/integration/test-content/res/ramses-test-client-data-buffers-float.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-float.frag rename to tests/integration/test-content/res/ramses-test-client-data-buffers-float.frag diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-float.vert b/tests/integration/test-content/res/ramses-test-client-data-buffers-float.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-float.vert rename to tests/integration/test-content/res/ramses-test-client-data-buffers-float.vert diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-interleaved.frag b/tests/integration/test-content/res/ramses-test-client-data-buffers-interleaved.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-interleaved.frag rename to tests/integration/test-content/res/ramses-test-client-data-buffers-interleaved.frag diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-interleaved.vert b/tests/integration/test-content/res/ramses-test-client-data-buffers-interleaved.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-interleaved.vert rename to tests/integration/test-content/res/ramses-test-client-data-buffers-interleaved.vert diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-vec2.frag b/tests/integration/test-content/res/ramses-test-client-data-buffers-vec2.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-vec2.frag rename to tests/integration/test-content/res/ramses-test-client-data-buffers-vec2.frag diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-vec2.vert b/tests/integration/test-content/res/ramses-test-client-data-buffers-vec2.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-vec2.vert rename to tests/integration/test-content/res/ramses-test-client-data-buffers-vec2.vert diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-vec3.frag b/tests/integration/test-content/res/ramses-test-client-data-buffers-vec3.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-vec3.frag rename to tests/integration/test-content/res/ramses-test-client-data-buffers-vec3.frag diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-vec3.vert b/tests/integration/test-content/res/ramses-test-client-data-buffers-vec3.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-vec3.vert rename to tests/integration/test-content/res/ramses-test-client-data-buffers-vec3.vert diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-vec4.frag b/tests/integration/test-content/res/ramses-test-client-data-buffers-vec4.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-vec4.frag rename to tests/integration/test-content/res/ramses-test-client-data-buffers-vec4.frag diff --git a/integration/TestContent/res/ramses-test-client-data-buffers-vec4.vert b/tests/integration/test-content/res/ramses-test-client-data-buffers-vec4.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers-vec4.vert rename to tests/integration/test-content/res/ramses-test-client-data-buffers-vec4.vert diff --git a/integration/TestContent/res/ramses-test-client-data-buffers.frag b/tests/integration/test-content/res/ramses-test-client-data-buffers.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers.frag rename to tests/integration/test-content/res/ramses-test-client-data-buffers.frag diff --git a/integration/TestContent/res/ramses-test-client-data-buffers.vert b/tests/integration/test-content/res/ramses-test-client-data-buffers.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-data-buffers.vert rename to tests/integration/test-content/res/ramses-test-client-data-buffers.vert diff --git a/integration/TestContent/res/ramses-test-client-discard.frag b/tests/integration/test-content/res/ramses-test-client-discard.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-discard.frag rename to tests/integration/test-content/res/ramses-test-client-discard.frag diff --git a/integration/TestContent/res/ramses-test-client-discard.vert b/tests/integration/test-content/res/ramses-test-client-discard.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-discard.vert rename to tests/integration/test-content/res/ramses-test-client-discard.vert diff --git a/integration/TestContent/res/ramses-test-client-embedded-compositing-1.png b/tests/integration/test-content/res/ramses-test-client-embedded-compositing-1.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-embedded-compositing-1.png rename to tests/integration/test-content/res/ramses-test-client-embedded-compositing-1.png diff --git a/integration/TestContent/res/ramses-test-client-embedded-compositing-2.png b/tests/integration/test-content/res/ramses-test-client-embedded-compositing-2.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-embedded-compositing-2.png rename to tests/integration/test-content/res/ramses-test-client-embedded-compositing-2.png diff --git a/integration/TestContent/res/ramses-test-client-embedded-compositing-3.png b/tests/integration/test-content/res/ramses-test-client-embedded-compositing-3.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-embedded-compositing-3.png rename to tests/integration/test-content/res/ramses-test-client-embedded-compositing-3.png diff --git a/integration/TestContent/res/ramses-test-client-embedded-compositing-4.png b/tests/integration/test-content/res/ramses-test-client-embedded-compositing-4.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-embedded-compositing-4.png rename to tests/integration/test-content/res/ramses-test-client-embedded-compositing-4.png diff --git a/integration/TestContent/res/ramses-test-client-embedded-compositing-5.png b/tests/integration/test-content/res/ramses-test-client-embedded-compositing-5.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-embedded-compositing-5.png rename to tests/integration/test-content/res/ramses-test-client-embedded-compositing-5.png diff --git a/integration/TestContent/res/ramses-test-client-embedded-compositing-6.png b/tests/integration/test-content/res/ramses-test-client-embedded-compositing-6.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-embedded-compositing-6.png rename to tests/integration/test-content/res/ramses-test-client-embedded-compositing-6.png diff --git a/integration/TestContent/res/ramses-test-client-explicitLocation.frag b/tests/integration/test-content/res/ramses-test-client-explicitLocation.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-explicitLocation.frag rename to tests/integration/test-content/res/ramses-test-client-explicitLocation.frag diff --git a/integration/TestContent/res/ramses-test-client-explicitLocation.vert b/tests/integration/test-content/res/ramses-test-client-explicitLocation.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-explicitLocation.vert rename to tests/integration/test-content/res/ramses-test-client-explicitLocation.vert diff --git a/integration/TestContent/res/ramses-test-client-explicitLocationSwapped.frag b/tests/integration/test-content/res/ramses-test-client-explicitLocationSwapped.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-explicitLocationSwapped.frag rename to tests/integration/test-content/res/ramses-test-client-explicitLocationSwapped.frag diff --git a/integration/TestContent/res/ramses-test-client-explicitLocationSwapped.vert b/tests/integration/test-content/res/ramses-test-client-explicitLocationSwapped.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-explicitLocationSwapped.vert rename to tests/integration/test-content/res/ramses-test-client-explicitLocationSwapped.vert diff --git a/integration/TestContent/res/ramses-test-client-file-loading-basic.vert b/tests/integration/test-content/res/ramses-test-client-file-loading-basic.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-file-loading-basic.vert rename to tests/integration/test-content/res/ramses-test-client-file-loading-basic.vert diff --git a/integration/TestContent/res/ramses-test-client-file-loading-red.frag b/tests/integration/test-content/res/ramses-test-client-file-loading-red.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-file-loading-red.frag rename to tests/integration/test-content/res/ramses-test-client-file-loading-red.frag diff --git a/integration/TestContent/res/ramses-test-client-file-loading-texture.png b/tests/integration/test-content/res/ramses-test-client-file-loading-texture.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-file-loading-texture.png rename to tests/integration/test-content/res/ramses-test-client-file-loading-texture.png diff --git a/integration/TestContent/res/ramses-test-client-file-loading-texturing.frag b/tests/integration/test-content/res/ramses-test-client-file-loading-texturing.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-file-loading-texturing.frag rename to tests/integration/test-content/res/ramses-test-client-file-loading-texturing.frag diff --git a/integration/TestContent/res/ramses-test-client-file-loading-texturing.vert b/tests/integration/test-content/res/ramses-test-client-file-loading-texturing.vert similarity index 91% rename from integration/TestContent/res/ramses-test-client-file-loading-texturing.vert rename to tests/integration/test-content/res/ramses-test-client-file-loading-texturing.vert index a53130835..51dedd398 100644 --- a/integration/TestContent/res/ramses-test-client-file-loading-texturing.vert +++ b/tests/integration/test-content/res/ramses-test-client-file-loading-texturing.vert @@ -11,9 +11,9 @@ uniform highp mat4 mvpMatrix; attribute vec3 a_position; -attribute vec2 a_texcoord; +attribute lowp vec2 a_texcoord; -varying vec2 v_texcoord; +varying lowp vec2 v_texcoord; void main() { diff --git a/integration/TestContent/res/ramses-test-client-input.frag b/tests/integration/test-content/res/ramses-test-client-input.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-input.frag rename to tests/integration/test-content/res/ramses-test-client-input.frag diff --git a/integration/TestContent/res/ramses-test-client-input.vert b/tests/integration/test-content/res/ramses-test-client-input.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-input.vert rename to tests/integration/test-content/res/ramses-test-client-input.vert diff --git a/integration/TestContent/res/ramses-test-client-logo-cropped.png b/tests/integration/test-content/res/ramses-test-client-logo-cropped.png similarity index 100% rename from integration/TestContent/res/ramses-test-client-logo-cropped.png rename to tests/integration/test-content/res/ramses-test-client-logo-cropped.png diff --git a/integration/TestContent/res/ramses-test-client-mplus-1p-regular.ttf b/tests/integration/test-content/res/ramses-test-client-mplus-1p-regular.ttf similarity index 100% rename from integration/TestContent/res/ramses-test-client-mplus-1p-regular.ttf rename to tests/integration/test-content/res/ramses-test-client-mplus-1p-regular.ttf diff --git a/integration/TestContent/res/ramses-test-client-multiStageUniform.frag b/tests/integration/test-content/res/ramses-test-client-multiStageUniform.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-multiStageUniform.frag rename to tests/integration/test-content/res/ramses-test-client-multiStageUniform.frag diff --git a/integration/TestContent/res/ramses-test-client-multiStageUniform.vert b/tests/integration/test-content/res/ramses-test-client-multiStageUniform.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-multiStageUniform.vert rename to tests/integration/test-content/res/ramses-test-client-multiStageUniform.vert diff --git a/integration/TestContent/res/ramses-test-client-optimizedInput.frag b/tests/integration/test-content/res/ramses-test-client-optimizedInput.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-optimizedInput.frag rename to tests/integration/test-content/res/ramses-test-client-optimizedInput.frag diff --git a/integration/TestContent/res/ramses-test-client-optimizedInput.vert b/tests/integration/test-content/res/ramses-test-client-optimizedInput.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-optimizedInput.vert rename to tests/integration/test-content/res/ramses-test-client-optimizedInput.vert diff --git a/integration/TestContent/res/ramses-test-client-pixel-accurate-text.frag b/tests/integration/test-content/res/ramses-test-client-pixel-accurate-text.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-pixel-accurate-text.frag rename to tests/integration/test-content/res/ramses-test-client-pixel-accurate-text.frag diff --git a/integration/TestContent/res/ramses-test-client-pixel-accurate-text.vert b/tests/integration/test-content/res/ramses-test-client-pixel-accurate-text.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-pixel-accurate-text.vert rename to tests/integration/test-content/res/ramses-test-client-pixel-accurate-text.vert diff --git a/integration/TestContent/res/ramses-test-client-render-one-buffer-ms.frag b/tests/integration/test-content/res/ramses-test-client-render-one-buffer-ms.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-render-one-buffer-ms.frag rename to tests/integration/test-content/res/ramses-test-client-render-one-buffer-ms.frag diff --git a/integration/TestContent/res/ramses-test-client-render-one-buffer-ms.vert b/tests/integration/test-content/res/ramses-test-client-render-one-buffer-ms.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-render-one-buffer-ms.vert rename to tests/integration/test-content/res/ramses-test-client-render-one-buffer-ms.vert diff --git a/integration/TestContent/res/ramses-test-client-render-one-buffer.frag b/tests/integration/test-content/res/ramses-test-client-render-one-buffer.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-render-one-buffer.frag rename to tests/integration/test-content/res/ramses-test-client-render-one-buffer.frag diff --git a/integration/TestContent/res/ramses-test-client-render-one-buffer.vert b/tests/integration/test-content/res/ramses-test-client-render-one-buffer.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-render-one-buffer.vert rename to tests/integration/test-content/res/ramses-test-client-render-one-buffer.vert diff --git a/integration/TestContent/res/ramses-test-client-render-two-buffers.frag b/tests/integration/test-content/res/ramses-test-client-render-two-buffers.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-render-two-buffers.frag rename to tests/integration/test-content/res/ramses-test-client-render-two-buffers.frag diff --git a/integration/TestContent/res/ramses-test-client-render-two-buffers.vert b/tests/integration/test-content/res/ramses-test-client-render-two-buffers.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-render-two-buffers.vert rename to tests/integration/test-content/res/ramses-test-client-render-two-buffers.vert diff --git a/integration/TestContent/res/ramses-test-client-simple-color.frag b/tests/integration/test-content/res/ramses-test-client-simple-color.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-simple-color.frag rename to tests/integration/test-content/res/ramses-test-client-simple-color.frag diff --git a/integration/TestContent/res/ramses-test-client-simple-color.vert b/tests/integration/test-content/res/ramses-test-client-simple-color.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-simple-color.vert rename to tests/integration/test-content/res/ramses-test-client-simple-color.vert diff --git a/integration/TestContent/res/ramses-test-client-structUniform.frag b/tests/integration/test-content/res/ramses-test-client-structUniform.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-structUniform.frag rename to tests/integration/test-content/res/ramses-test-client-structUniform.frag diff --git a/integration/TestContent/res/ramses-test-client-structUniform.vert b/tests/integration/test-content/res/ramses-test-client-structUniform.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-structUniform.vert rename to tests/integration/test-content/res/ramses-test-client-structUniform.vert diff --git a/integration/TestContent/res/ramses-test-client-text.frag b/tests/integration/test-content/res/ramses-test-client-text.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-text.frag rename to tests/integration/test-content/res/ramses-test-client-text.frag diff --git a/integration/TestContent/res/ramses-test-client-text.vert b/tests/integration/test-content/res/ramses-test-client-text.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-text.vert rename to tests/integration/test-content/res/ramses-test-client-text.vert diff --git a/integration/TestContent/res/ramses-test-client-texture-buffer-allmips.frag b/tests/integration/test-content/res/ramses-test-client-texture-buffer-allmips.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-texture-buffer-allmips.frag rename to tests/integration/test-content/res/ramses-test-client-texture-buffer-allmips.frag diff --git a/integration/TestContent/res/ramses-test-client-texture-buffer-allmips.vert b/tests/integration/test-content/res/ramses-test-client-texture-buffer-allmips.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-texture-buffer-allmips.vert rename to tests/integration/test-content/res/ramses-test-client-texture-buffer-allmips.vert diff --git a/integration/TestContent/res/ramses-test-client-texture-buffer.frag b/tests/integration/test-content/res/ramses-test-client-texture-buffer.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-texture-buffer.frag rename to tests/integration/test-content/res/ramses-test-client-texture-buffer.frag diff --git a/integration/TestContent/res/ramses-test-client-texture-buffer.vert b/tests/integration/test-content/res/ramses-test-client-texture-buffer.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-texture-buffer.vert rename to tests/integration/test-content/res/ramses-test-client-texture-buffer.vert diff --git a/integration/TestContent/res/ramses-test-client-textureSize.frag b/tests/integration/test-content/res/ramses-test-client-textureSize.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-textureSize.frag rename to tests/integration/test-content/res/ramses-test-client-textureSize.frag diff --git a/integration/TestContent/res/ramses-test-client-textureSize.vert b/tests/integration/test-content/res/ramses-test-client-textureSize.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-textureSize.vert rename to tests/integration/test-content/res/ramses-test-client-textureSize.vert diff --git a/integration/TestContent/res/ramses-test-client-textured-cube.frag b/tests/integration/test-content/res/ramses-test-client-textured-cube.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-textured-cube.frag rename to tests/integration/test-content/res/ramses-test-client-textured-cube.frag diff --git a/integration/TestContent/res/ramses-test-client-textured-cube.vert b/tests/integration/test-content/res/ramses-test-client-textured-cube.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-textured-cube.vert rename to tests/integration/test-content/res/ramses-test-client-textured-cube.vert diff --git a/integration/TestContent/res/ramses-test-client-textured-with-texel-fetch.frag b/tests/integration/test-content/res/ramses-test-client-textured-with-texel-fetch.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-textured-with-texel-fetch.frag rename to tests/integration/test-content/res/ramses-test-client-textured-with-texel-fetch.frag diff --git a/integration/TestContent/res/ramses-test-client-textured-with-texel-fetch.vert b/tests/integration/test-content/res/ramses-test-client-textured-with-texel-fetch.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-textured-with-texel-fetch.vert rename to tests/integration/test-content/res/ramses-test-client-textured-with-texel-fetch.vert diff --git a/integration/TestContent/res/ramses-test-client-textured.frag b/tests/integration/test-content/res/ramses-test-client-textured.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-textured.frag rename to tests/integration/test-content/res/ramses-test-client-textured.frag diff --git a/integration/TestContent/res/ramses-test-client-textured.vert b/tests/integration/test-content/res/ramses-test-client-textured.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-textured.vert rename to tests/integration/test-content/res/ramses-test-client-textured.vert diff --git a/integration/TestContent/res/ramses-test-client-texturedWithColor.frag b/tests/integration/test-content/res/ramses-test-client-texturedWithColor.frag similarity index 100% rename from integration/TestContent/res/ramses-test-client-texturedWithColor.frag rename to tests/integration/test-content/res/ramses-test-client-texturedWithColor.frag diff --git a/integration/TestContent/res/ramses-test-client-texturedWithColor.vert b/tests/integration/test-content/res/ramses-test-client-texturedWithColor.vert similarity index 100% rename from integration/TestContent/res/ramses-test-client-texturedWithColor.vert rename to tests/integration/test-content/res/ramses-test-client-texturedWithColor.vert diff --git a/integration/TestContent/res/ramses-test-instancing-uniform.frag b/tests/integration/test-content/res/ramses-test-instancing-uniform.frag similarity index 100% rename from integration/TestContent/res/ramses-test-instancing-uniform.frag rename to tests/integration/test-content/res/ramses-test-instancing-uniform.frag diff --git a/integration/TestContent/res/ramses-test-instancing-uniform.vert b/tests/integration/test-content/res/ramses-test-instancing-uniform.vert similarity index 100% rename from integration/TestContent/res/ramses-test-instancing-uniform.vert rename to tests/integration/test-content/res/ramses-test-instancing-uniform.vert diff --git a/integration/TestContent/res/ramses-test-instancing-vertex.frag b/tests/integration/test-content/res/ramses-test-instancing-vertex.frag similarity index 100% rename from integration/TestContent/res/ramses-test-instancing-vertex.frag rename to tests/integration/test-content/res/ramses-test-instancing-vertex.frag diff --git a/integration/TestContent/res/ramses-test-instancing-vertex.vert b/tests/integration/test-content/res/ramses-test-instancing-vertex.vert similarity index 100% rename from integration/TestContent/res/ramses-test-instancing-vertex.vert rename to tests/integration/test-content/res/ramses-test-instancing-vertex.vert diff --git a/integration/TestContent/res/ramses-text-stress-tests.frag b/tests/integration/test-content/res/ramses-text-stress-tests.frag similarity index 100% rename from integration/TestContent/res/ramses-text-stress-tests.frag rename to tests/integration/test-content/res/ramses-text-stress-tests.frag diff --git a/integration/TestContent/res/ramses-text-stress-tests.vert b/tests/integration/test-content/res/ramses-text-stress-tests.vert similarity index 100% rename from integration/TestContent/res/ramses-text-stress-tests.vert rename to tests/integration/test-content/res/ramses-text-stress-tests.vert diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt new file mode 100644 index 000000000..69778a098 --- /dev/null +++ b/tests/unittests/CMakeLists.txt @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +add_subdirectory(framework-test-utils) +add_subdirectory(framework) +add_subdirectory(client) + +if(ANY_WINDOW_TYPE_ENABLED) + add_subdirectory(renderer) + add_subdirectory(ramses-cli) +endif() + +if(ramses-sdk_BUILD_TOOLS) + add_subdirectory(tools) +endif() diff --git a/tests/unittests/client/AppearanceTest.cpp b/tests/unittests/client/AppearanceTest.cpp new file mode 100644 index 000000000..e6ace6862 --- /dev/null +++ b/tests/unittests/client/AppearanceTest.cpp @@ -0,0 +1,1600 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "TestEffectCreator.h" +#include "impl/EffectImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/AppearanceImpl.h" +#include "impl/AppearanceUtils.h" + +namespace ramses::internal +{ + class AAppearanceTest : public ::testing::Test + { + public: + + static void SetUpTestSuite() + { + sharedTestState = std::make_unique(false); + } + + static void TearDownTestSuite() + { + sharedTestState = nullptr; + } + + void SetUp() override + { + EXPECT_TRUE(sharedTestState != nullptr); + sharedTestState->recreateAppearence(); + appearance = sharedTestState->appearance; + } + + protected: + struct TextureInputInfo + { + std::optional input; + ramses::TextureSampler* sampler = nullptr; + TextureSamplerMS* samplerMS = nullptr; + TextureSamplerExternal* samplerExternal = nullptr; + ramses::RenderBuffer* renderBuffer = nullptr; + Texture2D* texture2D = nullptr; + Texture3D* texture3D = nullptr; + TextureCube* textureCube = nullptr; + }; + + static void GetTexture2DInputInfo(TextureInputInfo& info) + { + info.input = sharedTestState->effect->findUniformInput("texture2dInput"); + EXPECT_TRUE(info.input.has_value()); + + const uint8_t data[] = { 1, 2, 3 }; + const MipLevelData mipData(3u, data); + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + EXPECT_TRUE(texture != nullptr); + info.texture2D = texture; + + ramses::TextureSampler* sampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + EXPECT_TRUE(sampler != nullptr); + info.sampler = sampler; + } + + static void GetTexture2DMSInputInfo(TextureInputInfo& info) + { + info.input = sharedTestState->effect->findUniformInput("texture2dMSInput"); + EXPECT_TRUE(info.input.has_value()); + + ramses::RenderBuffer* renderBuffer = sharedTestState->getScene().createRenderBuffer(2u, 2u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + EXPECT_TRUE(renderBuffer != nullptr); + info.renderBuffer = renderBuffer; + + TextureSamplerMS* samplerMS = sharedTestState->getScene().createTextureSamplerMS(*renderBuffer, "renderBuffer"); + EXPECT_TRUE(samplerMS != nullptr); + info.samplerMS = samplerMS; + } + + static void GetTexture3DInputInfo(TextureInputInfo& info) + { + info.input = sharedTestState->effect->findUniformInput("texture3dInput"); + EXPECT_TRUE(info.input.has_value()); + + const uint8_t data[] = { 1, 2, 3 }; + const MipLevelData mipData(3u, data); + Texture3D* texture = sharedTestState->getScene().createTexture3D(ETextureFormat::RGB8, 1u, 1u, 1u, 1u, &mipData, false); + EXPECT_TRUE(texture != nullptr); + info.texture3D = texture; + + ramses::TextureSampler* sampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + EXPECT_TRUE(sampler != nullptr); + info.sampler = sampler; + } + + static void GetTextureCubeInputInfo(TextureInputInfo& info) + { + info.input = sharedTestState->effect->findUniformInput("textureCubeInput"); + EXPECT_TRUE(info.input.has_value()); + + const std::byte data[] = { std::byte{1}, std::byte{2}, std::byte{3} }; + const CubeMipLevelData mipData(3u, data, data, data, data, data, data); + TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, 1u, &mipData, false); + EXPECT_TRUE(texture != nullptr); + info.textureCube = texture; + + ramses::TextureSampler* sampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + EXPECT_TRUE(sampler != nullptr); + info.sampler = sampler; + } + + static void GetTextureExternalInputInfo(TextureInputInfo& info) + { + info.input = sharedTestState->effect->findUniformInput("textureExternalInput"); + EXPECT_TRUE(info.input.has_value()); + + TextureSamplerExternal* sampler = sharedTestState->getScene().createTextureSamplerExternal(ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear); + EXPECT_TRUE(sampler != nullptr); + info.samplerExternal = sampler; + } + + static std::unique_ptr sharedTestState; + Appearance* appearance{nullptr}; + }; + + std::unique_ptr AAppearanceTest::sharedTestState; + + class AAppearanceTestWithSemanticUniforms : public AAppearanceTest + { + public: + static void SetUpTestSuite() + { + sharedTestState = std::make_unique(true); + } + }; + + TEST_F(AAppearanceTest, getsTheSameEffectUsedToCreateIt) + { + const Effect& effect = appearance->getEffect(); + EXPECT_EQ(&effect, sharedTestState->effect); + + const ramses::internal::DataLayout uniformLayout = sharedTestState->getInternalScene().getDataLayout(appearance->impl().getUniformDataLayout()); + const ramses::internal::ResourceContentHash& effectHashFromUniformLayout = uniformLayout.getEffectHash(); + EXPECT_EQ(appearance->getEffect().impl().getLowlevelResourceHash(), effectHashFromUniformLayout); + } + + TEST_F(AAppearanceTest, setGetBlendingFactors) + { + bool stat = appearance->setBlendingFactors(EBlendFactor::One, EBlendFactor::SrcAlpha, EBlendFactor::OneMinusSrcAlpha, EBlendFactor::DstAlpha); + EXPECT_TRUE(stat); + EBlendFactor srcColor = EBlendFactor::Zero; + EBlendFactor destColor = EBlendFactor::Zero; + EBlendFactor srcAlpha = EBlendFactor::Zero; + EBlendFactor destAlpha = EBlendFactor::Zero; + stat = appearance->getBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); + EXPECT_TRUE(stat); + EXPECT_EQ(EBlendFactor::One, srcColor); + EXPECT_EQ(EBlendFactor::SrcAlpha, destColor); + EXPECT_EQ(EBlendFactor::OneMinusSrcAlpha, srcAlpha); + EXPECT_EQ(EBlendFactor::DstAlpha, destAlpha); + } + + TEST_F(AAppearanceTest, setGetBlendingColor) + { + vec4f color{ std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + //default values + bool stat = appearance->getBlendingColor(color); + EXPECT_TRUE(stat); + EXPECT_EQ(color, vec4f(0.f, 0.f, 0.f, 0.f)); + + const vec4f colorToSet{ 0.1f, 0.2f, 0.3f, 0.4f }; + stat = appearance->setBlendingColor(colorToSet); + EXPECT_TRUE(stat); + + stat = appearance->getBlendingColor(color); + EXPECT_TRUE(stat); + EXPECT_EQ(colorToSet, color); + } + + TEST_F(AAppearanceTest, setGetDepthWrite) + { + EDepthWrite depthWriteMode = EDepthWrite::Disabled; + EXPECT_TRUE(appearance->setDepthWrite(EDepthWrite::Enabled)); + EXPECT_TRUE(appearance->getDepthWriteMode(depthWriteMode)); + EXPECT_EQ(EDepthWrite::Enabled, depthWriteMode); + + EXPECT_TRUE(appearance->setDepthWrite(EDepthWrite::Disabled)); + EXPECT_TRUE(appearance->getDepthWriteMode(depthWriteMode)); + EXPECT_EQ(EDepthWrite::Disabled, depthWriteMode); + + EXPECT_TRUE(appearance->setDepthWrite(EDepthWrite::Enabled)); + EXPECT_TRUE(appearance->getDepthWriteMode(depthWriteMode)); + EXPECT_EQ(EDepthWrite::Enabled, depthWriteMode); + } + + TEST_F(AAppearanceTest, setGetDepthFunction) + { + EDepthFunc depthFunc = EDepthFunc::Disabled; + EXPECT_TRUE(appearance->getDepthFunction(depthFunc)); + EXPECT_EQ(EDepthFunc::LessEqual, depthFunc); + + EXPECT_TRUE(appearance->setDepthFunction(EDepthFunc::GreaterEqual)); + EXPECT_TRUE(appearance->getDepthFunction(depthFunc)); + EXPECT_EQ(EDepthFunc::GreaterEqual, depthFunc); + } + + TEST_F(AAppearanceTest, setGetScissorTest) + { + EScissorTest mode = EScissorTest::Disabled; + EXPECT_TRUE(appearance->setScissorTest(EScissorTest::Enabled, 1, 2, 3u, 4u)); + EXPECT_TRUE(appearance->getScissorTestState(mode)); + EXPECT_EQ(EScissorTest::Enabled, mode); + + int16_t x = 0; + int16_t y = 0; + uint16_t width = 0u; + uint16_t height = 0; + EXPECT_TRUE(appearance->getScissorRegion(x, y, width, height)); + EXPECT_EQ(1, x); + EXPECT_EQ(2, y); + EXPECT_EQ(3u, width); + EXPECT_EQ(4u, height); + } + + TEST_F(AAppearanceTest, setGetStencilFunc) + { + bool stat = appearance->setStencilFunction(EStencilFunc::Equal, 2u, 0xef); + EXPECT_TRUE(stat); + EStencilFunc func = EStencilFunc::Disabled; + uint8_t ref = 0; + uint8_t mask = 0; + stat = appearance->getStencilFunction(func, ref, mask); + EXPECT_TRUE(stat); + EXPECT_EQ(EStencilFunc::Equal, func); + EXPECT_EQ(2u, ref); + EXPECT_EQ(0xef, mask); + } + + TEST_F(AAppearanceTest, setGetStencilOperation) + { + bool stat = appearance->setStencilOperation(EStencilOperation::Decrement, EStencilOperation::Increment, EStencilOperation::DecrementWrap); + EXPECT_TRUE(stat); + EStencilOperation sfail = EStencilOperation::Zero; + EStencilOperation dpfail = EStencilOperation::Zero; + EStencilOperation dppass = EStencilOperation::Zero; + stat = appearance->getStencilOperation(sfail, dpfail, dppass); + EXPECT_TRUE(stat); + EXPECT_EQ(EStencilOperation::Decrement, sfail); + EXPECT_EQ(EStencilOperation::Increment, dpfail); + EXPECT_EQ(EStencilOperation::DecrementWrap, dppass); + } + + TEST_F(AAppearanceTest, setGetBlendOperations) + { + EXPECT_TRUE(appearance->setBlendingOperations(EBlendOperation::Subtract, EBlendOperation::Max)); + EBlendOperation opColor = EBlendOperation::Disabled; + EBlendOperation opAlpha = EBlendOperation::Disabled; + EXPECT_TRUE(appearance->getBlendingOperations(opColor, opAlpha)); + EXPECT_EQ(EBlendOperation::Subtract, opColor); + EXPECT_EQ(EBlendOperation::Max, opAlpha); + } + + TEST_F(AAppearanceTest, setGetCullMode) + { + ECullMode mode = ECullMode::Disabled; + EXPECT_TRUE(appearance->setCullingMode(ECullMode::FrontFacing)); + EXPECT_TRUE(appearance->getCullingMode(mode)); + EXPECT_EQ(ECullMode::FrontFacing, mode); + EXPECT_TRUE(appearance->setCullingMode(ECullMode::Disabled)); + EXPECT_TRUE(appearance->getCullingMode(mode)); + EXPECT_EQ(ECullMode::Disabled, mode); + } + + TEST_F(AAppearanceTest, hasDrawModeTrianglesByDefault) + { + EDrawMode mode; + EXPECT_TRUE(appearance->getDrawMode(mode)); + EXPECT_EQ(EDrawMode::Triangles, mode); + } + + TEST_F(AAppearanceTest, setGetDrawMode) + { + EDrawMode mode = EDrawMode::Lines; + EXPECT_TRUE(appearance->setDrawMode(EDrawMode::Points)); + EXPECT_TRUE(appearance->getDrawMode(mode)); + EXPECT_EQ(EDrawMode::Points, mode); + } + + TEST_F(AAppearanceTest, setGetColorWriteMask) + { + bool writeR = false; + bool writeG = false; + bool writeB = false; + bool writeA = false; + EXPECT_TRUE(appearance->setColorWriteMask(true, false, true, false)); + EXPECT_TRUE(appearance->getColorWriteMask(writeR, writeG, writeB, writeA)); + EXPECT_TRUE(writeR); + EXPECT_FALSE(writeG); + EXPECT_TRUE(writeB); + EXPECT_FALSE(writeA); + EXPECT_TRUE(appearance->setColorWriteMask(false, true, false, true)); + EXPECT_TRUE(appearance->getColorWriteMask(writeR, writeG, writeB, writeA)); + EXPECT_FALSE(writeR); + EXPECT_TRUE(writeG); + EXPECT_FALSE(writeB); + EXPECT_TRUE(writeA); + } + + TEST_F(AAppearanceTest, reportsErrorWhenGetSetMismatchingInputTypeScalar) + { + const auto optUniform = sharedTestState->effect->findUniformInput("integerInput"); + ASSERT_TRUE(optUniform.has_value()); + + const float value = 42; + float getValue = 0; + + EXPECT_FALSE(appearance->setInputValue(*optUniform, value)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, getValue)); + } + + TEST_F(AAppearanceTest, reportsErrorWhenGetSetMismatchingInputTypeArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("integerInput"); + ASSERT_TRUE(optUniform.has_value()); + + const float values[] = { 42, 43, 44, 45, 46, 47 }; + float getValues[6]; + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 6u, values)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 6u, getValues)); + } + + TEST_F(AAppearanceTest, reportsErrorWhenGetSetMismatchingInputTypeTexture) + { + const auto optUniform = sharedTestState->effect->findUniformInput("integerInput"); + ASSERT_TRUE(optUniform.has_value()); + + const uint8_t texData[] = { 1, 2, 3 }; + const MipLevelData mipData(3u, texData); + + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + ASSERT_TRUE(texture != nullptr); + ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_FALSE(appearance->setInputTexture(*optUniform, *textureSampler)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureSampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture)); + } + + TEST_F(AAppearanceTest, reportsErrorWhenSetInputTextureFromADifferentScene) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optUniform.has_value()); + + const uint8_t texData[] = { 1, 2, 3 }; + const MipLevelData mipData(3u, texData); + + ramses::Scene& anotherScene = *sharedTestState->getClient().createScene(sceneId_t(1u)); + Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + ASSERT_TRUE(texture != nullptr); + ramses::TextureSampler* textureSampler = anotherScene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_FALSE(appearance->setInputTexture(*optUniform, *textureSampler)); + + EXPECT_TRUE(anotherScene.destroy(*texture)); + EXPECT_TRUE(sharedTestState->getClient().destroy(anotherScene)); + } + + TEST_F(AAppearanceTest, getsSamplerSetToUniformInput) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optUniform.has_value()); + + const uint8_t texData[] = { 1, 2, 3 }; + const MipLevelData mipData(3u, texData); + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + ASSERT_TRUE(texture != nullptr); + ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_TRUE(appearance->setInputTexture(*optUniform, *textureSampler)); + + const ramses::TextureSampler* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTexture(*optUniform, actualSampler)); + EXPECT_EQ(textureSampler, actualSampler); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureSampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture)); + } + + TEST_F(AAppearanceTest, getsNullSamplerIfNoneSetToUniformInput) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optUniform.has_value()); + + const ramses::TextureSampler* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTexture(*optUniform, actualSampler)); + EXPECT_EQ(nullptr, actualSampler); + } + + TEST_F(AAppearanceTest, getsNullSamplerMSIfNoneSetToUniformInput) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dMSInput"); + ASSERT_TRUE(optUniform.has_value()); + + const TextureSamplerMS* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTextureMS(*optUniform, actualSampler)); + EXPECT_EQ(nullptr, actualSampler); + } + + TEST_F(AAppearanceTest, getsNullSamplerExternalIfNoneSetToUniformInput) + { + const auto optUniform = sharedTestState->effect->findUniformInput("textureExternalInput"); + ASSERT_TRUE(optUniform.has_value()); + + const TextureSamplerExternal* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTextureExternal(*optUniform, actualSampler)); + EXPECT_EQ(nullptr, actualSampler); + } + + TEST_F(AAppearanceTest, failsToGetSamplerSetToUniformIfInputHasWrongType) + { + const auto optUniform = sharedTestState->effect->findUniformInput("integerInput"); + ASSERT_TRUE(optUniform.has_value()); + + const ramses::TextureSampler* actualSampler = nullptr; + EXPECT_FALSE(appearance->getInputTexture(*optUniform, actualSampler)); + EXPECT_EQ(nullptr, actualSampler); + } + + TEST_F(AAppearanceTest, failsToGetSamplerMSIfInputHasWrongType) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optUniform.has_value()); + + const TextureSamplerMS* actualSampler = nullptr; + EXPECT_FALSE(appearance->getInputTextureMS(*optUniform, actualSampler)); + EXPECT_EQ(nullptr, actualSampler); + } + + TEST_F(AAppearanceTest, failsToGetSamplerExternalIfInputHasWrongType) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optUniform.has_value()); + + const TextureSamplerExternal* actualSampler = nullptr; + EXPECT_FALSE(appearance->getInputTextureExternal(*optUniform, actualSampler)); + EXPECT_EQ(nullptr, actualSampler); + } + + /// Bool + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeBool) + { + const auto optUniform = sharedTestState->effect->findUniformInput("boolInput"); + ASSERT_TRUE(optUniform.has_value()); + + bool value = true; + bool& valueR = value; + const bool& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + bool getValue = false; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeBoolArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("boolInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + bool values[] = {true, false, true}; + bool getValues[3] = {false}; + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values)); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues)); + EXPECT_EQ(values, absl::MakeSpan(getValues)); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values)); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, values)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, values)); + } + + /// Int32 + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeInt32) + { + const auto optUniform = sharedTestState->effect->findUniformInput("integerInput"); + ASSERT_TRUE(optUniform.has_value()); + + int32_t value = 42; + int32_t& valueR = value; + const int32_t& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + int32_t getValue = 0; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeInt32Array) + { + const auto optUniform = sharedTestState->effect->findUniformInput("integerInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const int32_t value = 42; + int32_t values[] = { value, value * 2, value * 3 }; + int32_t getValues[3] = { 0 }; + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values)); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues)); + EXPECT_EQ(values, absl::MakeSpan(getValues)); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values)); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, values)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, values)); + } + + /// Float + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeFloat) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + + float value = 42; + float& valueR = value; + const float& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + float getValue = 0; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeFloatArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const float value = 42; + const float values[] = { value, value * 2, value * 3 }; + float getValues[3] = { 0 }; + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values)); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues)); + EXPECT_EQ(values, absl::MakeSpan(getValues)); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values)); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues)); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues)); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2i) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec2iInput"); + ASSERT_TRUE(optUniform.has_value()); + + vec2i value{ 42, 24 }; + vec2i& valueR = value; + const vec2i& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + vec2i getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2iArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec2iInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = { vec2i{42, 43}, vec2i{44, 45}, vec2i{46, 47} }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3i) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec3iInput"); + ASSERT_TRUE(optUniform.has_value()); + + vec3i value{ 42, 24, 4422 }; + vec3i& valueR = value; + const vec3i& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + vec3i getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3iArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec3iInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = { vec3i{42, 43, 444}, vec3i{44, 45, 555}, vec3i{46, 47, 666} }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4i) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec4iInput"); + ASSERT_TRUE(optUniform.has_value()); + + vec4i value{ 42, 24, 44, 22 }; + vec4i& valueR = value; + const vec4i& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + vec4i getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4iArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec4iInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = { vec4i{42, 43, 444, 555}, vec4i{44, 45, 666, 777}, vec4i{46, 47, 888, 999} }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2f) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec2fInput"); + ASSERT_TRUE(optUniform.has_value()); + + vec2f value{ 42.f, 24.f }; + vec2f& valueR = value; + const vec2f& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + vec2f getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector2fArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec2fInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = { vec2f{42.f, 43.f}, vec2f{44.f, 45.f}, vec2f{46.f, 47.f} }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3f) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec3fInput"); + ASSERT_TRUE(optUniform.has_value()); + + vec3f value{ 42.f, 24.f, 44.f }; + vec3f& valueR = value; + const vec3f& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + vec3f getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector3fArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec3fInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = { vec3f{42.f, 43.f, 444.f}, vec3f{44.f, 45.f, 666.f}, vec3f{46.f, 47.f, 888.f} }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4f) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec4fInput"); + ASSERT_TRUE(optUniform.has_value()); + + vec4f value{ 42.f, 24.f, 44.f, 55.f }; + vec4f& valueR = value; + const vec4f& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + vec4f getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeVector4fArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec4fInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = { vec4f{42.f, 43.f, 444.f, 555.f}, vec4f{44.f, 45.f, 666.f, 777.f}, vec4f{46.f, 47.f, 888.f, 999.f} }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + /// matrix22f + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix22f) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix22fInput"); + ASSERT_TRUE(optUniform.has_value()); + + matrix22f value{ 42.f, 43.f, 44.f, 45.f }; + matrix22f& valueR = value; + const matrix22f& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + matrix22f getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix22fArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix22fInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = + { + matrix22f{42.f, 43.f, 44.f, 45.f}, + matrix22f{46.f, 47.f, 48.f, 49.f}, + matrix22f{50.f, 51.f, 52.f, 53.f} + }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + /// Matrix33f + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix33f) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix33fInput"); + ASSERT_TRUE(optUniform.has_value()); + + matrix33f value{ 42.f, 43.f, 44.f, 45.f, 46.f, 47.f, 48.f, 49.f, 50.f }; + matrix33f& valueR = value; + const matrix33f& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + matrix33f getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix33fArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix33fInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = + { + matrix33f{42.f, 43.f, 44.f, 45.f, 11.f, 22.f, 33.f, 44.f, 55.f}, + matrix33f{46.f, 47.f, 48.f, 49.f, 66.f, 77.f, 88.f, 99.f, 10.f}, + matrix33f{50.f, 51.f, 52.f, 53.f, 20.f, 30.f, 40.f, 50.f, 60.f} + }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + /// Matrix44f + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix44f) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix44fInput"); + ASSERT_TRUE(optUniform.has_value()); + + matrix44f value{ 42.f, 43.f, 44.f, 45.f, 46.f, 47.f, 48.f, 49.f, 50.f, 51.f, 52.f, 53.f, 54.f, 55.f, 56.f, 57.f }; + matrix44f& valueR = value; + const matrix44f& valueCR = value; + auto valueM = value; + EXPECT_TRUE(appearance->setInputValue(*optUniform, value)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, valueCR)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, std::move(valueM))); + + matrix44f getValue; + EXPECT_TRUE(appearance->getInputValue(*optUniform, getValue)); + EXPECT_EQ(value, getValue); + } + + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeMatrix44fArray) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix44fInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + const std::vector values = + { + matrix44f{42.f, 43.f, 44.f, 45.f, 11.f, 22.f, 33.f, 44.f, 55.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f}, + matrix44f{46.f, 47.f, 48.f, 49.f, 66.f, 77.f, 88.f, 99.f, 10.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f}, + matrix44f{50.f, 51.f, 52.f, 53.f, 20.f, 30.f, 40.f, 50.f, 60.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f} + }; + std::vector getValues(values.size()); + + EXPECT_TRUE(appearance->setInputValue(*optUniform, 3u, values.data())); + EXPECT_TRUE(appearance->getInputValue(*optUniform, 3u, getValues.data())); + EXPECT_EQ(values, getValues); + + EXPECT_FALSE(appearance->setInputValue(*optUniform, 0u, values.data())); + EXPECT_FALSE(appearance->setInputValue(*optUniform, 11u, values.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 0u, getValues.data())); + EXPECT_FALSE(appearance->getInputValue(*optUniform, 11u, getValues.data())); + } + + /// Texture2D + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTexture2D) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optUniform.has_value()); + + const uint8_t texData[] = { 1, 2, 3 }; + const MipLevelData mipData(3u, texData); + + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + ASSERT_TRUE(texture != nullptr); + ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_TRUE(appearance->setInputTexture(*optUniform, *textureSampler)); + + const ramses::TextureSampler* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTexture(*optUniform, actualSampler)); + EXPECT_EQ(textureSampler, actualSampler); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureSampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture)); + } + + /// Texture2DMS + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTexture2DMS) + { + const auto optUniform = sharedTestState->effect->findUniformInput("texture2dMSInput"); + ASSERT_TRUE(optUniform.has_value()); + + ramses::RenderBuffer* renderBuffer = sharedTestState->getScene().createRenderBuffer(2u, 2u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + TextureSamplerMS* textureSampler = sharedTestState->getScene().createTextureSamplerMS(*renderBuffer, "renderBuffer"); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_EQ(textureSampler->impl().getTextureDataType(), ramses::internal::EDataType::TextureSampler2DMS); + EXPECT_TRUE(appearance->setInputTexture(*optUniform, *textureSampler)); + + const TextureSamplerMS* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTextureMS(*optUniform, actualSampler)); + EXPECT_EQ(textureSampler, actualSampler); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureSampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*renderBuffer)); + } + + /// TextureCube + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTextureCube) + { + const auto optUniform = sharedTestState->effect->findUniformInput("textureCubeInput"); + ASSERT_TRUE(optUniform.has_value()); + + const std::byte texData[] = { std::byte{1}, std::byte{2}, std::byte{3} }; + const CubeMipLevelData mipData(3u, texData, texData, texData, texData, texData, texData); + + TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, 1u, &mipData, false); + ASSERT_TRUE(texture != nullptr); + ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, *texture); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_TRUE(appearance->setInputTexture(*optUniform, *textureSampler)); + + const ramses::TextureSampler* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTexture(*optUniform, actualSampler)); + EXPECT_EQ(textureSampler, actualSampler); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureSampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture)); + } + + /// TextureExternal + TEST_F(AAppearanceTest, canHandleUniformInputsOfTypeTextureExternal) + { + const auto optUniform = sharedTestState->effect->findUniformInput("textureExternalInput"); + ASSERT_TRUE(optUniform.has_value()); + + TextureSamplerExternal* textureSampler = sharedTestState->getScene().createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear); + ASSERT_TRUE(textureSampler != nullptr); + + EXPECT_EQ(textureSampler->impl().getTextureDataType(), ramses::internal::EDataType::TextureSamplerExternal); + EXPECT_TRUE(appearance->setInputTexture(*optUniform, *textureSampler)); + + const TextureSamplerExternal* actualSampler = nullptr; + EXPECT_TRUE(appearance->getInputTextureExternal(*optUniform, actualSampler)); + EXPECT_EQ(textureSampler, actualSampler); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureSampler)); + } + + /// Binding data objects + TEST_F(AAppearanceTest, uniformInputIsNotBoundInitially) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_FALSE(appearance->isInputBound(*optUniform)); + } + + TEST_F(AAppearanceTest, canBindDataObjectToUniformInput) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_TRUE(appearance->bindInput(*optUniform, *dataObject)); + EXPECT_TRUE(appearance->isInputBound(*optUniform)); + EXPECT_EQ(dataObject->impl().getDataReference(), appearance->getDataObjectBoundToInput(*optUniform)->impl().getDataReference()); + + EXPECT_TRUE(appearance->unbindInput(*optUniform)); + EXPECT_FALSE(appearance->isInputBound(*optUniform)); + EXPECT_EQ(nullptr, appearance->getDataObjectBoundToInput(*optUniform)); + } + + TEST_F(AAppearanceTest, failsToSetOrGetValueIfInputBound) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_TRUE(appearance->setInputValue(*optUniform, 666.f)); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + dataObject->setValue(333.f); + + EXPECT_TRUE(appearance->bindInput(*optUniform, *dataObject)); + + const float setValue = 0.111f; + float value = 0.f; + EXPECT_FALSE(appearance->setInputValue(*optUniform, setValue)); + EXPECT_TRUE(dataObject->getValue(value)); + EXPECT_FLOAT_EQ(333.f, value); // failed setter does not modify data object + value = 0.f; + EXPECT_FALSE(appearance->getInputValue(*optUniform, value)); + EXPECT_FLOAT_EQ(0.f, value); // failed getter does not modify out parameter + + EXPECT_TRUE(appearance->unbindInput(*optUniform)); + + EXPECT_TRUE(appearance->getInputValue(*optUniform, value)); + EXPECT_EQ(666.f, value); // failed setter did not modify previously set value + } + + TEST_F(AAppearanceTest, failsToBindDataObjectToArrayUniformInput) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInputArray"); + ASSERT_TRUE(optUniform.has_value()); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_FALSE(appearance->bindInput(*optUniform, *dataObject)); + } + + TEST_F(AAppearanceTest, failsToBindDataObjectFromADifferentScene) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + + ramses::Scene& anotherScene = *sharedTestState->getClient().createScene(sceneId_t(1u)); + auto dataObject = anotherScene.createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_FALSE(appearance->bindInput(*optUniform, *dataObject)); + + EXPECT_TRUE(sharedTestState->getClient().destroy(anotherScene)); + } + + TEST_F(AAppearanceTest, failsToBindDataObjectOfMismatchingType) + { + const auto optUniform = sharedTestState->effect->findUniformInput("vec4fInput"); + ASSERT_TRUE(optUniform.has_value()); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_FALSE(appearance->bindInput(*optUniform, *dataObject)); + EXPECT_FALSE(appearance->isInputBound(*optUniform)); + } + + TEST_F(AAppearanceTestWithSemanticUniforms, failsToBindDataObjectIfInputHasSemantics) + { + const auto optUniform = sharedTestState->effect->findUniformInput("matrix44fInput"); + ASSERT_TRUE(optUniform.has_value()); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Matrix44F); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_FALSE(appearance->bindInput(*optUniform, *dataObject)); + } + + TEST_F(AAppearanceTest, unbindingDataObjectFallsBackToPreviouslySetValue) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_TRUE(appearance->setInputValue(*optUniform, 666.f)); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_TRUE(appearance->bindInput(*optUniform, *dataObject)); + dataObject->setValue(13.f); + + EXPECT_TRUE(appearance->unbindInput(*optUniform)); + float value = 0.f; + EXPECT_TRUE(appearance->getInputValue(*optUniform, value)); + EXPECT_FLOAT_EQ(666.f, value); + } + + TEST_F(AAppearanceTest, failsToUnbindIfInputIsNotBound) + { + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + + EXPECT_FALSE(appearance->unbindInput(*optUniform)); + } + + /// Validation + TEST_F(AAppearanceTest, reportsErrorWhenValidatedWithInvalidTextureSampler) + { + TextureInputInfo texture2DInputInfo; + GetTexture2DInputInfo(texture2DInputInfo); + + TextureInputInfo texture2DMSInputInfo; + GetTexture2DMSInputInfo(texture2DMSInputInfo); + + TextureInputInfo texture3DInputInfo; + GetTexture3DInputInfo(texture3DInputInfo); + + TextureInputInfo textureCubeInputInfo; + GetTextureCubeInputInfo(textureCubeInputInfo); + + TextureInputInfo textureExternalInfo; + GetTextureExternalInputInfo(textureExternalInfo); + + Appearance* newAppearance = sharedTestState->getScene().createAppearance(*sharedTestState->effect, "New Appearance"); + ASSERT_TRUE(nullptr != newAppearance); + + EXPECT_TRUE(newAppearance->setInputTexture(*texture2DInputInfo.input, *texture2DInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*texture2DMSInputInfo.input, *texture2DMSInputInfo.samplerMS)); + EXPECT_TRUE(newAppearance->setInputTexture(*texture3DInputInfo.input, *texture3DInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*textureCubeInputInfo.input, *textureCubeInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*textureExternalInfo.input, *textureExternalInfo.samplerExternal)); + ValidationReport report; + newAppearance->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DInputInfo.sampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DMSInputInfo.samplerMS)); + report.clear(); + newAppearance->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*newAppearance)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DInputInfo.texture2D)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DMSInputInfo.renderBuffer)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureCubeInputInfo.sampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureCubeInputInfo.textureCube)); + } + + TEST_F(AAppearanceTest, reportsErrorWhenValidatedWithInvalidTexture) + { + TextureInputInfo texture2DInputInfo; + GetTexture2DInputInfo(texture2DInputInfo); + + TextureInputInfo texture2DMSInputInfo; + GetTexture2DMSInputInfo(texture2DMSInputInfo); + + TextureInputInfo texture3DInputInfo; + GetTexture3DInputInfo(texture3DInputInfo); + + TextureInputInfo textureCubeInputInfo; + GetTextureCubeInputInfo(textureCubeInputInfo); + + TextureInputInfo textureExternalInfo; + GetTextureExternalInputInfo(textureExternalInfo); + + Appearance* newAppearance = sharedTestState->getScene().createAppearance(*sharedTestState->effect, "New Appearance"); + ASSERT_TRUE(nullptr != newAppearance); + EXPECT_TRUE(newAppearance->setInputTexture(*texture2DInputInfo.input, *texture2DInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*texture2DMSInputInfo.input, *texture2DMSInputInfo.samplerMS)); + EXPECT_TRUE(newAppearance->setInputTexture(*texture3DInputInfo.input, *texture3DInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*textureCubeInputInfo.input, *textureCubeInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*textureExternalInfo.input, *textureExternalInfo.samplerExternal)); + ValidationReport report; + newAppearance->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DInputInfo.texture2D)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DMSInputInfo.renderBuffer)); + report.clear(); + newAppearance->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*newAppearance)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DInputInfo.sampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DMSInputInfo.samplerMS)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureCubeInputInfo.sampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureCubeInputInfo.textureCube)); + } + + TEST_F(AAppearanceTest, reportsErrorWhenValidatedWithBoundDataObjectThatWasDestroyed) + { + TextureInputInfo texture2DInputInfo; + GetTexture2DInputInfo(texture2DInputInfo); + + TextureInputInfo texture2DMSInputInfo; + GetTexture2DMSInputInfo(texture2DMSInputInfo); + + TextureInputInfo texture3DInputInfo; + GetTexture3DInputInfo(texture3DInputInfo); + + TextureInputInfo textureCubeInputInfo; + GetTextureCubeInputInfo(textureCubeInputInfo); + + TextureInputInfo textureExternalInfo; + GetTextureExternalInputInfo(textureExternalInfo); + + Appearance* newAppearance = sharedTestState->getScene().createAppearance(*sharedTestState->effect, "New Appearance"); + ASSERT_TRUE(nullptr != newAppearance); + EXPECT_TRUE(newAppearance->setInputTexture(*texture2DInputInfo.input, *texture2DInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*texture2DMSInputInfo.input, *texture2DMSInputInfo.samplerMS)); + EXPECT_TRUE(newAppearance->setInputTexture(*texture3DInputInfo.input, *texture3DInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*textureCubeInputInfo.input, *textureCubeInputInfo.sampler)); + EXPECT_TRUE(newAppearance->setInputTexture(*textureExternalInfo.input, *textureExternalInfo.samplerExternal)); + ValidationReport report; + newAppearance->validate(report); + EXPECT_FALSE(report.hasIssue()); + + const auto optUniform = sharedTestState->effect->findUniformInput("floatInput"); + ASSERT_TRUE(optUniform.has_value()); + + auto dataObject = sharedTestState->getScene().createDataObject(ramses::EDataType::Float); + ASSERT_TRUE(dataObject != nullptr); + + EXPECT_TRUE(newAppearance->bindInput(*optUniform, *dataObject)); + report.clear(); + newAppearance->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*dataObject)); + report.clear(); + newAppearance->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*newAppearance)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DInputInfo.sampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DInputInfo.texture2D)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DMSInputInfo.samplerMS)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*texture2DMSInputInfo.renderBuffer)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureCubeInputInfo.sampler)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*textureCubeInputInfo.textureCube)); + } + + TEST_F(AAppearanceTest, failsWhenWrongBlendingOperationsSet) + { + bool stat = appearance->setBlendingOperations(EBlendOperation::Subtract, EBlendOperation::ReverseSubtract); + EXPECT_TRUE(stat); + + stat = appearance->setBlendingOperations(EBlendOperation::Add, EBlendOperation::Disabled); + EXPECT_FALSE(stat); + + stat = appearance->setBlendingOperations(EBlendOperation::Disabled, EBlendOperation::Add); + EXPECT_FALSE(stat); + + EBlendOperation opColor = EBlendOperation::Disabled; + EBlendOperation opAlpha = EBlendOperation::Disabled; + EXPECT_TRUE(appearance->getBlendingOperations(opColor, opAlpha)); + EXPECT_EQ(EBlendOperation::Subtract, opColor); + EXPECT_EQ(EBlendOperation::ReverseSubtract, opAlpha); + } + + TEST_F(AAppearanceTest, defaultBlendingFactors) + { + EBlendFactor srcColor = EBlendFactor::Zero; + EBlendFactor destColor = EBlendFactor::Zero; + EBlendFactor srcAlpha = EBlendFactor::Zero; + EBlendFactor destAlpha = EBlendFactor::Zero; + bool stat = appearance->getBlendingFactors(srcColor, destColor, srcAlpha, destAlpha); + EXPECT_TRUE(stat); + EXPECT_EQ(EBlendFactor::SrcAlpha, srcColor); + EXPECT_EQ(EBlendFactor::OneMinusSrcAlpha, destColor); + EXPECT_EQ(EBlendFactor::One, srcAlpha); + EXPECT_EQ(EBlendFactor::One, destAlpha); + } + + TEST_F(AAppearanceTest, defaultDepthWriteMode) + { + EDepthWrite depthWriteMode = EDepthWrite::Disabled; + EXPECT_TRUE(appearance->getDepthWriteMode(depthWriteMode)); + EXPECT_EQ(EDepthWrite::Enabled, depthWriteMode); + } + + TEST_F(AAppearanceTest, defaultScissorRegion) + { + int16_t x = std::numeric_limits::max(); + int16_t y = std::numeric_limits::max(); + uint16_t width = std::numeric_limits::max(); + uint16_t height = std::numeric_limits::max(); + EXPECT_TRUE(appearance->getScissorRegion(x, y, width, height)); + EXPECT_EQ(0, x); + EXPECT_EQ(0, y); + EXPECT_EQ(0u, width); + EXPECT_EQ(0u, height); + } + + TEST_F(AAppearanceTest, defaultStencilFunc) + { + EStencilFunc func = EStencilFunc::Always; + uint8_t ref = std::numeric_limits::max(); + uint8_t mask = std::numeric_limits::max(); + bool stat = appearance->getStencilFunction(func, ref, mask); + EXPECT_TRUE(stat); + EXPECT_EQ(EStencilFunc::Disabled, func); + EXPECT_EQ(0u, ref); + EXPECT_EQ(0xFF, mask); + } + + TEST_F(AAppearanceTest, defaultBlendingOperations) + { + EBlendOperation opColor = EBlendOperation::Add; + EBlendOperation opAlpha = EBlendOperation::Subtract; + EXPECT_TRUE(appearance->getBlendingOperations(opColor, opAlpha)); + EXPECT_EQ(EBlendOperation::Disabled, opColor); + EXPECT_EQ(EBlendOperation::Disabled, opAlpha); + } + + TEST_F(AAppearanceTest, defaultCullMode) + { + ECullMode mode = ECullMode::Disabled; + EXPECT_TRUE(appearance->getCullingMode(mode)); + EXPECT_EQ(ECullMode::BackFacing, mode); + } + + TEST_F(AAppearanceTest, defaultColorWriteMask) + { + bool writeR = false; + bool writeG = false; + bool writeB = false; + bool writeA = false; + EXPECT_TRUE(appearance->getColorWriteMask(writeR, writeG, writeB, writeA)); + EXPECT_TRUE(writeR); + EXPECT_TRUE(writeG); + EXPECT_TRUE(writeB); + EXPECT_TRUE(writeA); + } + + TEST_F(AAppearanceTest, defaultGetInputValue) + { + { + auto intOut = std::numeric_limits::max(); + auto uniformItegerInput = sharedTestState->effect->findUniformInput("integerInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformItegerInput, intOut)); + EXPECT_EQ(intOut, 0); + } + { + auto floatOut = std::numeric_limits::max(); + auto uniformFloatInput = sharedTestState->effect->findUniformInput("floatInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformFloatInput, floatOut)); + EXPECT_FLOAT_EQ(floatOut, 0.0f); + } + { + vec2i vec2iOut(42); + auto uniformVec2iInput = sharedTestState->effect->findUniformInput("vec2iInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformVec2iInput, vec2iOut)); + EXPECT_EQ(vec2iOut.x, 0); + EXPECT_EQ(vec2iOut.y, 0); + } + { + vec3i vec3iOut(42); + auto uniformVec3iInput = sharedTestState->effect->findUniformInput("vec3iInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformVec3iInput, vec3iOut)); + EXPECT_EQ(vec3iOut, vec3i{0}); + } + { + vec4i vec4iOut(42); + auto uniformVec4iInput = sharedTestState->effect->findUniformInput("vec4iInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformVec4iInput, vec4iOut)); + EXPECT_EQ(vec4iOut, vec4i{0}); + } + { + vec2f vec2fOut(42.f); + auto uniformVec2fInput = sharedTestState->effect->findUniformInput("vec2fInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformVec2fInput, vec2fOut)); + EXPECT_EQ(vec2fOut, vec2f{0.f}); + } + { + vec3f vec3fOut(42.f); + auto uniformVec3fInput = sharedTestState->effect->findUniformInput("vec3fInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformVec3fInput, vec3fOut)); + EXPECT_EQ(vec3fOut, vec3f{0.f}); + } + { + vec4f vec4fOut(42.f); + auto uniformVec4fInput = sharedTestState->effect->findUniformInput("vec4fInput"); + EXPECT_TRUE(appearance->getInputValue(*uniformVec4fInput, vec4fOut)); + EXPECT_EQ(vec4fOut, vec4f{0.f}); + } + { + auto uniformMatrix22fInput = sharedTestState->effect->findUniformInput("matrix22fInput"); + matrix22f matrix22fOut = {42, 43, 44, 45}; + EXPECT_TRUE(appearance->getInputValue(*uniformMatrix22fInput, matrix22fOut)); + for (const auto& i : matrix22fOut) + EXPECT_FLOAT_EQ(i, 0.0f); + } + { + auto uniformMatrix33fInput = sharedTestState->effect->findUniformInput("matrix33fInput"); + matrix33f matrix33fOut = {42, 43, 44, 45, 46, 47, 48, 49, 50}; + EXPECT_TRUE(appearance->getInputValue(*uniformMatrix33fInput, matrix33fOut)); + for (const auto& i : matrix33fOut) + EXPECT_FLOAT_EQ(i, 0.0f); + } + { + auto uniformMatrix44fInput = sharedTestState->effect->findUniformInput("matrix44fInput"); + matrix44f matrix44fOut = {42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}; + EXPECT_TRUE(appearance->getInputValue(*uniformMatrix44fInput, matrix44fOut)); + for (const auto& i : matrix44fOut) + EXPECT_FLOAT_EQ(i, 0.0f); + } + + { + auto integerInputArrayInput = sharedTestState->effect->findUniformInput("integerInputArray"); + int32_t integerInputArray[] = {42, 42, 42}; + EXPECT_TRUE(appearance->getInputValue(*integerInputArrayInput, 3u, integerInputArray)); + for (const auto& i : integerInputArray) + EXPECT_EQ(i, 0); + } + { + auto floatInputArrayInput = sharedTestState->effect->findUniformInput("floatInputArray"); + float floatInputArray[] = {42, 42, 42}; + EXPECT_TRUE(appearance->getInputValue(*floatInputArrayInput, 3u, floatInputArray)); + for (const auto& i : floatInputArray) + EXPECT_FLOAT_EQ(i, 0.0f); + } + { + auto vec2iInputArrayInput = sharedTestState->effect->findUniformInput("vec2iInputArray"); + vec2i vec2iInputArray[] = {vec2i{42, 43}, vec2i{44, 45}, vec2i{46, 47}}; + EXPECT_TRUE(appearance->getInputValue(*vec2iInputArrayInput, 3u, vec2iInputArray)); + for (const auto& i : vec2iInputArray) + EXPECT_EQ(i, vec2i{0}); + } + { + auto vec3iInputArrayInput = sharedTestState->effect->findUniformInput("vec3iInputArray"); + vec3i vec3iInputArray[] = {vec3i{42, 43, 44}, vec3i{45, 46, 47}, vec3i{48, 49, 50}}; + EXPECT_TRUE(appearance->getInputValue(*vec3iInputArrayInput, 3u, vec3iInputArray)); + for (const auto& i : vec3iInputArray) + EXPECT_EQ(i, vec3i{0}); + } + { + auto vec4iInputArrayInput = sharedTestState->effect->findUniformInput("vec4iInputArray"); + vec4i vec4iInputArray[] = {vec4i{42, 43, 44, 45}, vec4i{46, 47, 48, 49}, vec4i{50, 51, 52, 53}}; + EXPECT_TRUE(appearance->getInputValue(*vec4iInputArrayInput, 3u, vec4iInputArray)); + for (const auto& i : vec4iInputArray) + EXPECT_EQ(i, vec4i{0}); + } + { + auto vec2fInputArrayInput = sharedTestState->effect->findUniformInput("vec2fInputArray"); + vec2f vec2fInputArray[] = {vec2f{42, 43}, vec2f{44, 45}, vec2f{46, 47}}; + EXPECT_TRUE(appearance->getInputValue(*vec2fInputArrayInput, 3u, vec2fInputArray)); + for (const auto& i : vec2fInputArray) + EXPECT_EQ(i, vec2f{0}); + } + { + auto vec3fInputArrayInput = sharedTestState->effect->findUniformInput("vec3fInputArray"); + vec3f vec3fInputArray[] = {vec3f{42, 43, 44}, vec3f{45, 46, 47}, vec3f{48, 49, 50}}; + EXPECT_TRUE(appearance->getInputValue(*vec3fInputArrayInput, 3u, vec3fInputArray)); + for (const auto& i : vec3fInputArray) + EXPECT_EQ(i, vec3f{0}); + } + { + auto vec4fInputArrayInput = sharedTestState->effect->findUniformInput("vec4fInputArray"); + vec4f vec4fInputArray[] = {vec4f{42, 43, 44, 45}, vec4f{46, 47, 48, 49}, vec4f{50, 51, 52, 53}}; + EXPECT_TRUE(appearance->getInputValue(*vec4fInputArrayInput, 3u, vec4fInputArray)); + for (const auto& i : vec4fInputArray) + EXPECT_EQ(i, vec4f{0}); + } + { + auto matrix22fInputArrayInput = sharedTestState->effect->findUniformInput("matrix22fInputArray"); + matrix22f matrix22fInputArray[3] = {{42, 43, 44, 45}, {46, 47, 48, 49}, {50, 51, 52, 53}}; + EXPECT_TRUE(appearance->getInputValue(*matrix22fInputArrayInput, 3u, matrix22fInputArray)); + for (const auto& i : matrix22fInputArray) + EXPECT_EQ(i, matrix22f{0.0f}); + } + { + auto matrix33fInputArrayInput = sharedTestState->effect->findUniformInput("matrix33fInputArray"); + matrix33f matrix33fInputArray[3] = {{42, 43, 44, 46, 47, 48, 50, 51, 52}, {54, 55, 56, 46, 47, 48, 42, 43, 44}, {54, 55, 56, 46, 47, 48, 50, 51, 52}}; + EXPECT_TRUE(appearance->getInputValue(*matrix33fInputArrayInput, 3u, matrix33fInputArray)); + for (const auto& i : matrix33fInputArray) + EXPECT_EQ(i, matrix33f{0.0f}); + } + { + auto matrix44fInputArrayInput = sharedTestState->effect->findUniformInput("matrix44fInputArray"); + matrix44f matrix44fInputArray[3] = {{42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, + {46, 47, 48, 49, 42, 43, 44, 45, 54, 55, 56, 57, 46, 47, 48, 49}, + {50, 51, 52, 53, 54, 55, 56, 57, 42, 43, 44, 45, 50, 51, 52, 53}}; + EXPECT_TRUE(appearance->getInputValue(*matrix44fInputArrayInput, 3u, matrix44fInputArray)); + for (const auto& i : matrix44fInputArray) + EXPECT_EQ(i, matrix44f{0.0f}); + } + } + + class AnAppearanceWithGeometryShader : public AAppearanceTest + { + public: + + static void SetUpTestSuite() + { + sharedTestState = std::make_unique(false, true); + } + }; + + TEST_F(AnAppearanceWithGeometryShader, HasInitialDrawModeOfGeometryShadersRequirement) + { + EDrawMode mode; + EXPECT_TRUE(appearance->getDrawMode(mode)); + EXPECT_EQ(EDrawMode::Lines, mode); + } + + TEST_F(AnAppearanceWithGeometryShader, RefusesToChangeDrawingMode_WhenIncompatibleToGeometryShader) + { + // Shader uses lines, can't change to incompatible types + EXPECT_FALSE(appearance->setDrawMode(EDrawMode::Points)); + EXPECT_FALSE(appearance->setDrawMode(EDrawMode::Triangles)); + EXPECT_FALSE(appearance->setDrawMode(EDrawMode::TriangleFan)); + EDrawMode mode; + EXPECT_TRUE(appearance->getDrawMode(mode)); + EXPECT_EQ(EDrawMode::Lines, mode); + } + + TEST_F(AnAppearanceWithGeometryShader, AllowsChangingDrawMode_IfNewModeIsStillCompatible) + { + // Shader uses lines, change to line strip is ok - still produces lines for the geometry stage + EXPECT_TRUE(appearance->setDrawMode(EDrawMode::LineStrip)); + EDrawMode mode; + EXPECT_TRUE(appearance->getDrawMode(mode)); + EXPECT_EQ(EDrawMode::LineStrip, mode); + } + + TEST(AnAppearanceUtils, ChecksValidGeometryShaderModes) + { + // valid combinations of appearance draw mode (first) and GS input type (second) + const std::initializer_list> validDrawModeToGSModeCombinations = { + { EDrawMode::Points, EDrawMode::Points }, + { EDrawMode::Lines, EDrawMode::Lines }, + { EDrawMode::LineStrip, EDrawMode::Lines }, + { EDrawMode::LineLoop, EDrawMode::Lines }, + { EDrawMode::Triangles, EDrawMode::Triangles }, + { EDrawMode::TriangleStrip, EDrawMode::Triangles }, + { EDrawMode::TriangleFan, EDrawMode::Triangles } + }; + + for (int drawModeVal = 0; drawModeVal <= static_cast(EDrawMode::LineStrip); ++drawModeVal) + { + for (int gsInputTypeVal = 0; gsInputTypeVal <= static_cast(EDrawMode::LineStrip); ++gsInputTypeVal) + { + const auto drawMode = static_cast(drawModeVal); + const auto gsInputType = static_cast(gsInputTypeVal); + + // gs input mode restricted to only these types + if (gsInputType != EDrawMode::Points && gsInputType != EDrawMode::Lines && gsInputType != EDrawMode::Triangles) + continue; + + const auto it = std::find_if(validDrawModeToGSModeCombinations.begin(), validDrawModeToGSModeCombinations.end(), [&](const auto& modePair) { + return modePair.first == drawMode && modePair.second == gsInputType; + }); + const bool isValid = (it != validDrawModeToGSModeCombinations.end()); + + EXPECT_EQ(isValid, AppearanceUtils::GeometryShaderCompatibleWithDrawMode(gsInputType, drawMode)); + } + } + } +} diff --git a/client/test/ArrayBufferTest.cpp b/tests/unittests/client/ArrayBufferTest.cpp similarity index 55% rename from client/test/ArrayBufferTest.cpp rename to tests/unittests/client/ArrayBufferTest.cpp index 805dd72e0..f388370d3 100644 --- a/client/test/ArrayBufferTest.cpp +++ b/tests/unittests/client/ArrayBufferTest.cpp @@ -10,36 +10,25 @@ #include #include "ClientTestUtils.h" -#include "SceneImpl.h" -#include "ramses-utils.h" -#include "Scene/ClientScene.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/AttributeInput.h" -#include "ArrayBufferImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "DataTypeUtils.h" +#include "impl/SceneImpl.h" +#include "ramses/client/ramses-utils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/AttributeInput.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/DataTypeUtils.h" #include "glm/gtx/range.hpp" using namespace testing; -using namespace ramses_internal; -namespace ramses +namespace ramses::internal { - template - std::vector SomeDataVector(); - template <> std::vector SomeDataVector() { return { 1, 2, 3 }; } - template <> std::vector SomeDataVector() { return { 1, 2, 3 }; } - template <> std::vector SomeDataVector() { return { 1.f, 2.f, 3.f }; } - template <> std::vector SomeDataVector() { return { vec2f{1.f, 2.f}, vec2f{3.f, 4.f} }; } - template <> std::vector SomeDataVector() { return { vec3f{1.f, 2.f, 3.f}, vec3f{3.f, 4.f, 5.f} }; } - template <> std::vector SomeDataVector() { return { vec4f{1.f, 2.f, 3.f, 4.f}, vec4f{3.f, 4.f, 5.f, 6.f} }; } - template <> std::vector SomeDataVector() { return { 1, 2, 3, 4, 5, 6 }; } - template class AnArrayBuffer : public LocalTestClientWithScene, public ::testing::Test { protected: - static constexpr EDataType GetDataType() { return GetEDataType(); } + static constexpr ramses::EDataType GetDataType() { return GetEDataType(); } static constexpr uint32_t ElementSizeInBytes = EnumToSize(DataTypeUtils::ConvertDataTypeToInternal(GetDataType())); @@ -54,15 +43,15 @@ namespace ramses vec2f, vec3f, vec4f, - Byte + std::byte >; TYPED_TEST_SUITE(AnArrayBuffer, DataTypes); TYPED_TEST(AnArrayBuffer, IsAllocatedOnInternalSceneAfterCreation) { - EXPECT_TRUE(this->m_dataBuffer.m_impl.getDataBufferHandle().isValid()); - EXPECT_TRUE(this->m_scene.m_impl.getIScene().isDataBufferAllocated(this->m_dataBuffer.m_impl.getDataBufferHandle())); + EXPECT_TRUE(this->m_dataBuffer.impl().getDataBufferHandle().isValid()); + EXPECT_TRUE(this->m_scene.impl().getIScene().isDataBufferAllocated(this->m_dataBuffer.impl().getDataBufferHandle())); } TYPED_TEST(AnArrayBuffer, ContainsZeroedDataAfterCreation) @@ -71,7 +60,7 @@ namespace ramses EXPECT_EQ(0u, this->m_dataBuffer.getUsedNumberOfElements()); std::vector dataInBuffer(this->m_data.size()); - EXPECT_EQ(StatusOK, this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); + EXPECT_TRUE(this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); for (auto v : dataInBuffer) { if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) @@ -88,22 +77,22 @@ namespace ramses TYPED_TEST(AnArrayBuffer, CanUpdateWholeBuffer) { - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, this->m_dataBuffer.getMaximumNumberOfElements(), this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, this->m_dataBuffer.getMaximumNumberOfElements(), this->m_data.data())); std::vector dataInBuffer(this->m_data.size()); - EXPECT_EQ(StatusOK, this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); + EXPECT_TRUE(this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); EXPECT_EQ(this->m_data, dataInBuffer); } TYPED_TEST(AnArrayBuffer, CanUpdateSingleElement) { // set whole buffer first - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, this->m_dataBuffer.getMaximumNumberOfElements(), this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, this->m_dataBuffer.getMaximumNumberOfElements(), this->m_data.data())); // update only second element with first element from test data - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(1u, 1u, &this->m_data[0])); + EXPECT_TRUE(this->m_dataBuffer.updateData(1u, 1u, &this->m_data[0])); std::vector dataInBuffer(this->m_data.size()); - EXPECT_EQ(StatusOK, this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); + EXPECT_TRUE(this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); EXPECT_EQ(this->m_data[0], dataInBuffer[1]); for (size_t i = 0u; i < this->m_data.size(); ++i) { @@ -114,8 +103,8 @@ namespace ramses } // now update only first element with second element from test data - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 1u, &this->m_data[1])); - EXPECT_EQ(StatusOK, this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 1u, &this->m_data[1])); + EXPECT_TRUE(this->m_dataBuffer.getData(dataInBuffer.data(), this->m_dataBuffer.getMaximumNumberOfElements())); EXPECT_EQ(this->m_data[0], dataInBuffer[1]); EXPECT_EQ(this->m_data[1], dataInBuffer[0]); for (size_t i = 0u; i < this->m_data.size(); ++i) @@ -130,86 +119,90 @@ namespace ramses TYPED_TEST(AnArrayBuffer, CanNotBeUpdatedWhenDataSizeBiggerThanMaximumSize) { this->m_data.push_back(this->m_data.front()); - EXPECT_NE(StatusOK, this->m_dataBuffer.updateData(0u, uint32_t(this->m_data.size()), this->m_data.data())); + EXPECT_FALSE(this->m_dataBuffer.updateData(0u, uint32_t(this->m_data.size()), this->m_data.data())); } TYPED_TEST(AnArrayBuffer, CanNotBeUpdatedWhenDataSizeAndOffsetBiggerThanMaximumSize) { - EXPECT_NE(StatusOK, this->m_dataBuffer.updateData(1u, uint32_t(this->m_data.size()), this->m_data.data())); + EXPECT_FALSE(this->m_dataBuffer.updateData(1u, uint32_t(this->m_data.size()), this->m_data.data())); } TYPED_TEST(AnArrayBuffer, CanBeValidated) { const auto effect = TestEffects::CreateTestEffectWithAttribute(this->m_scene); - GeometryBinding& geom = this->createValidGeometry(effect); + Geometry& geom = this->createValidGeometry(effect); if (DataTypeUtils::IsValidIndicesType(this->m_dataBuffer.getDataType())) - EXPECT_EQ(StatusOK, geom.setIndices(this->m_dataBuffer)); - else if (EDataType::ByteBlob == this->GetDataType()) { - AttributeInput attrInput1; - effect->findAttributeInput("a_position", attrInput1); - AttributeInput attrInput2; - effect->findAttributeInput("a_vec2", attrInput2); + EXPECT_TRUE(geom.setIndices(this->m_dataBuffer)); + } + else if (ramses::EDataType::ByteBlob == this->GetDataType()) + { constexpr uint16_t nonZeroStride = 56u; - EXPECT_EQ(StatusOK, geom.setInputBuffer(attrInput1, this->m_dataBuffer, 0u, nonZeroStride)); - EXPECT_EQ(StatusOK, geom.setInputBuffer(attrInput2, this->m_dataBuffer, sizeof(float), nonZeroStride) ); + EXPECT_TRUE(geom.setInputBuffer(*effect->findAttributeInput("a_position"), this->m_dataBuffer, 0u, nonZeroStride)); + EXPECT_TRUE(geom.setInputBuffer(*effect->findAttributeInput("a_vec2"), this->m_dataBuffer, sizeof(float), nonZeroStride) ); } else { const char* validInputName = nullptr; switch (this->GetDataType()) { - case EDataType::Float: + case ramses::EDataType::Float: validInputName = "a_position"; break; - case EDataType::Vector2F: + case ramses::EDataType::Vector2F: validInputName = "a_vec2"; break; - case EDataType::Vector3F: + case ramses::EDataType::Vector3F: validInputName = "a_vec3"; break; - case EDataType::Vector4F: + case ramses::EDataType::Vector4F: validInputName = "a_vec4"; break; default: assert(false); } - AttributeInput attrInput; - effect->findAttributeInput(validInputName, attrInput); - EXPECT_EQ(StatusOK, geom.setInputBuffer(attrInput, this->m_dataBuffer)); + EXPECT_TRUE(geom.setInputBuffer(*effect->findAttributeInput(validInputName), this->m_dataBuffer)); } - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); - EXPECT_EQ(StatusOK, this->m_dataBuffer.validate()); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); + ValidationReport report; + this->m_dataBuffer.validate(report); + EXPECT_FALSE(report.hasIssue()); } TYPED_TEST(AnArrayBuffer, IsValidIfNotUsedByAnyMeshButUsedByPickableObject) { - ArrayBuffer* geometryBuffer = this->m_scene.createArrayBuffer(EDataType::Vector3F, 3, "geometryBuffer"); - EXPECT_EQ(StatusOK, geometryBuffer->updateData(0u, 1u, SomeDataVector().data())); + ArrayBuffer* geometryBuffer = this->m_scene.createArrayBuffer(ramses::EDataType::Vector3F, 3, "geometryBuffer"); + EXPECT_TRUE(geometryBuffer->updateData(0u, 1u, SomeDataVector().data())); const pickableObjectId_t id(2); ASSERT_TRUE(this->m_scene.createPickableObject(*geometryBuffer, id, "PickableObject")); - EXPECT_EQ(StatusOK, geometryBuffer->validate()); + ValidationReport report; + geometryBuffer->validate(report); + EXPECT_FALSE(report.hasIssue()); } TYPED_TEST(AnArrayBuffer, ReportsWarningIfNotUsedInGeometry) { - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); - EXPECT_NE(StatusOK, this->m_dataBuffer.validate()); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); + ValidationReport report; + this->m_dataBuffer.validate(report); + EXPECT_TRUE(report.hasIssue()); } TYPED_TEST(AnArrayBuffer, ReportsWarningIfUsedInGeometryButNotInitialized) { const auto effect = TestEffects::CreateTestEffectWithAttribute(this->m_scene); - GeometryBinding& geom = this->createValidGeometry(effect); + Geometry& geom = this->createValidGeometry(effect); if (DataTypeUtils::IsValidIndicesType(this->m_dataBuffer.getDataType())) + { geom.setIndices(this->m_dataBuffer); + } else { - AttributeInput attrInput; - effect->findAttributeInput("a_position", attrInput); - geom.setInputBuffer(attrInput, this->m_dataBuffer); + geom.setInputBuffer(*effect->findAttributeInput("a_position"), this->m_dataBuffer); } - EXPECT_NE(StatusOK, this->m_dataBuffer.validate()); + ValidationReport report; + this->m_dataBuffer.validate(report); + EXPECT_TRUE(report.hasIssue()); } TYPED_TEST(AnArrayBuffer, CanGetDataType) @@ -220,7 +213,7 @@ namespace ramses TYPED_TEST(AnArrayBuffer, CanGetMaximumSize) { EXPECT_EQ(this->m_data.size(), this->m_dataBuffer.getMaximumNumberOfElements()); - EXPECT_EQ(this->m_data.size(), this->m_dataBuffer.m_impl.getElementCount()); + EXPECT_EQ(this->m_data.size(), this->m_dataBuffer.impl().getElementCount()); } TYPED_TEST(AnArrayBuffer, CanGetUsedSize) @@ -228,65 +221,66 @@ namespace ramses EXPECT_EQ(0u, this->m_dataBuffer.getUsedNumberOfElements()); // set 1 element - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); EXPECT_EQ(1u, this->m_dataBuffer.getUsedNumberOfElements()); // set same element again - no change in used count - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); EXPECT_EQ(1u, this->m_dataBuffer.getUsedNumberOfElements()); // set 2 elements - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 2u, this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 2u, this->m_data.data())); EXPECT_EQ(2u, this->m_dataBuffer.getUsedNumberOfElements()); // set first element again - no change in used count - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, 1u, this->m_data.data())); EXPECT_EQ(2u, this->m_dataBuffer.getUsedNumberOfElements()); // set all elements - EXPECT_EQ(StatusOK, this->m_dataBuffer.updateData(0u, uint32_t(this->m_data.size()), this->m_data.data())); + EXPECT_TRUE(this->m_dataBuffer.updateData(0u, uint32_t(this->m_data.size()), this->m_data.data())); EXPECT_EQ(uint32_t(this->m_data.size()), this->m_dataBuffer.getUsedNumberOfElements()); EXPECT_EQ(this->m_dataBuffer.getMaximumNumberOfElements(), this->m_dataBuffer.getUsedNumberOfElements()); } TYPED_TEST(AnArrayBuffer, FailsToUpdateIfUsingWrongType) { - if (this->GetDataType() != EDataType::ByteBlob) + if (this->GetDataType() != ramses::EDataType::ByteBlob) { - EXPECT_NE(StatusOK, this->m_dataBuffer.updateData(0u, 1u, SomeDataVector().data())); + EXPECT_FALSE(this->m_dataBuffer.updateData(0u, 1u, SomeDataVector().data())); } else { - EXPECT_NE(StatusOK, this->m_dataBuffer.updateData(0u, 1u, SomeDataVector().data())); + EXPECT_FALSE(this->m_dataBuffer.updateData(0u, 1u, SomeDataVector().data())); } } TYPED_TEST(AnArrayBuffer, FailsToGetDataIfUsingWrongType) { - if (this->GetDataType() != EDataType::ByteBlob) + if (this->GetDataType() != ramses::EDataType::ByteBlob) { - auto data = SomeDataVector(); - EXPECT_NE(StatusOK, this->m_dataBuffer.getData(data.data(), 1u)); + auto data = SomeDataVector(); + EXPECT_FALSE(this->m_dataBuffer.getData(data.data(), 1u)); } else { auto data = SomeDataVector(); - EXPECT_NE(StatusOK, this->m_dataBuffer.getData(data.data(), 1u)); + EXPECT_FALSE(this->m_dataBuffer.getData(data.data(), 1u)); } } TEST(AnArrayBuffer, FailsToCreateForUnsupportedDataType) { LocalTestClientWithScene testScene; - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Int32, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Vector2I, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Vector3I, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Vector4I, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Matrix22F, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Matrix33F, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::Matrix44F, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::TextureSampler2D, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::TextureSampler2DMS, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::TextureSampler3D, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::TextureSamplerCube, 1u)); - EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(EDataType::TextureSamplerExternal, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Bool, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Int32, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Vector2I, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Vector3I, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Vector4I, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Matrix22F, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Matrix33F, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::Matrix44F, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::TextureSampler2D, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::TextureSampler2DMS, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::TextureSampler3D, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::TextureSamplerCube, 1u)); + EXPECT_EQ(nullptr, testScene.getScene().createArrayBuffer(ramses::EDataType::TextureSamplerExternal, 1u)); } } diff --git a/client/test/BlitPassTest.cpp b/tests/unittests/client/BlitPassTest.cpp similarity index 51% rename from client/test/BlitPassTest.cpp rename to tests/unittests/client/BlitPassTest.cpp index c263d3d38..a6ef0c66a 100644 --- a/client/test/BlitPassTest.cpp +++ b/tests/unittests/client/BlitPassTest.cpp @@ -9,61 +9,59 @@ #include #include "ClientTestUtils.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/RenderBuffer.h" -#include "SceneImpl.h" -#include "BlitPassImpl.h" -#include "RenderBufferImpl.h" -#include "ramses-utils.h" -#include "Scene/ClientScene.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/RenderBuffer.h" +#include "impl/SceneImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/RenderBufferImpl.h" +#include "ramses/client/ramses-utils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" using namespace testing; -using namespace ramses_internal; -namespace ramses +namespace ramses::internal { class ABlitPass : public LocalTestClientWithScene, public testing::Test { protected: ABlitPass() - : LocalTestClientWithScene() - , sourceRenderBuffer(*createRenderBuffer()) + : sourceRenderBuffer(*createRenderBuffer()) , destinationRenderBuffer(*createRenderBuffer()) , blitpass(*m_scene.createBlitPass(sourceRenderBuffer, destinationRenderBuffer, "BlitPass")) - , blitpassHandle(blitpass.m_impl.getBlitPassHandle()) + , blitpassHandle(blitpass.impl().getBlitPassHandle()) { } - RenderBuffer* createRenderBuffer() + ramses::RenderBuffer* createRenderBuffer() { - RenderBuffer* renderBuffer = m_scene.createRenderBuffer(16u, 12u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(16u, 12u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); return renderBuffer; } - RenderBuffer& sourceRenderBuffer; - RenderBuffer& destinationRenderBuffer; - BlitPass& blitpass; - const ramses_internal::BlitPassHandle blitpassHandle; + ramses::RenderBuffer& sourceRenderBuffer; + ramses::RenderBuffer& destinationRenderBuffer; + ramses::BlitPass& blitpass; + const ramses::internal::BlitPassHandle blitpassHandle; }; TEST_F(ABlitPass, CanGetSourceAndDestinationBuffers) { - const RenderBuffer& srcRB = blitpass.getSourceRenderBuffer(); - EXPECT_EQ(srcRB.m_impl.getRenderBufferHandle(), sourceRenderBuffer.m_impl.getRenderBufferHandle()); + const ramses::RenderBuffer& srcRB = blitpass.getSourceRenderBuffer(); + EXPECT_EQ(srcRB.impl().getRenderBufferHandle(), sourceRenderBuffer.impl().getRenderBufferHandle()); - const RenderBuffer& dstRB = blitpass.getDestinationRenderBuffer(); - EXPECT_EQ(dstRB.m_impl.getRenderBufferHandle(), destinationRenderBuffer.m_impl.getRenderBufferHandle()); + const ramses::RenderBuffer& dstRB = blitpass.getDestinationRenderBuffer(); + EXPECT_EQ(dstRB.impl().getRenderBufferHandle(), destinationRenderBuffer.impl().getRenderBufferHandle()); } TEST_F(ABlitPass, BlittingRegionIsSetToCompleteBufferByDefault) { - const ramses_internal::PixelRectangle& sourceRegion = m_scene.m_impl.getIScene().getBlitPass(blitpassHandle).sourceRegion; + const ramses::internal::PixelRectangle& sourceRegion = m_scene.impl().getIScene().getBlitPass(blitpassHandle).sourceRegion; EXPECT_EQ(0u, sourceRegion.x); EXPECT_EQ(0u, sourceRegion.y); EXPECT_EQ(static_cast(sourceRenderBuffer.getWidth()), sourceRegion.width); EXPECT_EQ(static_cast(sourceRenderBuffer.getHeight()), sourceRegion.height); - const ramses_internal::PixelRectangle& destinationRegion = m_scene.m_impl.getIScene().getBlitPass(blitpassHandle).destinationRegion; + const ramses::internal::PixelRectangle& destinationRegion = m_scene.impl().getIScene().getBlitPass(blitpassHandle).destinationRegion; EXPECT_EQ(0u, destinationRegion.x); EXPECT_EQ(0u, destinationRegion.y); EXPECT_EQ(static_cast(destinationRenderBuffer.getWidth()), destinationRegion.width); @@ -71,12 +69,12 @@ namespace ramses { //client HL api - uint32_t sourceXOut; - uint32_t sourceYOut; - uint32_t destinationXOut; - uint32_t destinationYOut; - uint32_t widthOut; - uint32_t heightOut; + uint32_t sourceXOut = std::numeric_limits::max(); + uint32_t sourceYOut = std::numeric_limits::max(); + uint32_t destinationXOut = std::numeric_limits::max(); + uint32_t destinationYOut = std::numeric_limits::max(); + uint32_t widthOut = std::numeric_limits::max(); + uint32_t heightOut = std::numeric_limits::max(); blitpass.getBlittingRegion(sourceXOut, sourceYOut, destinationXOut, destinationYOut, widthOut, heightOut); EXPECT_EQ(0u, sourceXOut); EXPECT_EQ(0u, sourceYOut); @@ -95,15 +93,15 @@ namespace ramses const uint32_t destinationY(4u); const int32_t width(5u); const int32_t height(6u); - EXPECT_EQ(StatusOK, blitpass.setBlittingRegion(sourceX, sourceY, destinationX, destinationY, width, height)); + EXPECT_TRUE(blitpass.setBlittingRegion(sourceX, sourceY, destinationX, destinationY, width, height)); - const ramses_internal::PixelRectangle& sourceRegion = m_scene.m_impl.getIScene().getBlitPass(blitpassHandle).sourceRegion; + const ramses::internal::PixelRectangle& sourceRegion = m_scene.impl().getIScene().getBlitPass(blitpassHandle).sourceRegion; EXPECT_EQ(sourceX, sourceRegion.x); EXPECT_EQ(sourceY, sourceRegion.y); EXPECT_EQ(width, sourceRegion.width); EXPECT_EQ(height, sourceRegion.height); - const ramses_internal::PixelRectangle& destinationRegion = m_scene.m_impl.getIScene().getBlitPass(blitpassHandle).destinationRegion; + const ramses::internal::PixelRectangle& destinationRegion = m_scene.impl().getIScene().getBlitPass(blitpassHandle).destinationRegion; EXPECT_EQ(destinationX, destinationRegion.x); EXPECT_EQ(destinationY, destinationRegion.y); EXPECT_EQ(width, destinationRegion.width); @@ -111,12 +109,12 @@ namespace ramses { //client HL api - uint32_t sourceXOut; - uint32_t sourceYOut; - uint32_t destinationXOut; - uint32_t destinationYOut; - uint32_t widthOut; - uint32_t heightOut; + uint32_t sourceXOut = std::numeric_limits::max(); + uint32_t sourceYOut = std::numeric_limits::max(); + uint32_t destinationXOut = std::numeric_limits::max(); + uint32_t destinationYOut = std::numeric_limits::max(); + uint32_t widthOut = std::numeric_limits::max(); + uint32_t heightOut = std::numeric_limits::max(); blitpass.getBlittingRegion(sourceXOut, sourceYOut, destinationXOut, destinationYOut, widthOut, heightOut); EXPECT_EQ(sourceX, sourceXOut); EXPECT_EQ(sourceY, sourceYOut); @@ -129,20 +127,20 @@ namespace ramses TEST_F(ABlitPass, CannotSetInvalidBlittingRegion) { - EXPECT_NE(StatusOK, blitpass.setBlittingRegion(0u, 0u, 0u, 0u, sourceRenderBuffer.getWidth() + 1, 10u)); - EXPECT_NE(StatusOK, blitpass.setBlittingRegion(0u, 0u, 0u, 0u, 10u, sourceRenderBuffer.getHeight() + 1u)); - EXPECT_NE(StatusOK, blitpass.setBlittingRegion(sourceRenderBuffer.getWidth(), 0u, 0u, 0u, 10, 10u)); - EXPECT_NE(StatusOK, blitpass.setBlittingRegion(0, sourceRenderBuffer.getHeight(), 0u, 0u, 10, 10u)); - EXPECT_NE(StatusOK, blitpass.setBlittingRegion(0u, 0u, sourceRenderBuffer.getWidth(), 0u, 10, 10u)); - EXPECT_NE(StatusOK, blitpass.setBlittingRegion(0u, 0u, 0, sourceRenderBuffer.getHeight(), 10, 10u)); - - const ramses_internal::PixelRectangle& sourceRegion = m_scene.m_impl.getIScene().getBlitPass(blitpassHandle).sourceRegion; + EXPECT_FALSE(blitpass.setBlittingRegion(0u, 0u, 0u, 0u, sourceRenderBuffer.getWidth() + 1, 10u)); + EXPECT_FALSE(blitpass.setBlittingRegion(0u, 0u, 0u, 0u, 10u, sourceRenderBuffer.getHeight() + 1u)); + EXPECT_FALSE(blitpass.setBlittingRegion(sourceRenderBuffer.getWidth(), 0u, 0u, 0u, 10, 10u)); + EXPECT_FALSE(blitpass.setBlittingRegion(0, sourceRenderBuffer.getHeight(), 0u, 0u, 10, 10u)); + EXPECT_FALSE(blitpass.setBlittingRegion(0u, 0u, sourceRenderBuffer.getWidth(), 0u, 10, 10u)); + EXPECT_FALSE(blitpass.setBlittingRegion(0u, 0u, 0, sourceRenderBuffer.getHeight(), 10, 10u)); + + const ramses::internal::PixelRectangle& sourceRegion = m_scene.impl().getIScene().getBlitPass(blitpassHandle).sourceRegion; EXPECT_EQ(0u, sourceRegion.x); EXPECT_EQ(0u, sourceRegion.y); EXPECT_EQ(static_cast(sourceRenderBuffer.getWidth()), sourceRegion.width); EXPECT_EQ(static_cast(sourceRenderBuffer.getHeight()), sourceRegion.height); - const ramses_internal::PixelRectangle& destinationRegion = m_scene.m_impl.getIScene().getBlitPass(blitpassHandle).destinationRegion; + const ramses::internal::PixelRectangle& destinationRegion = m_scene.impl().getIScene().getBlitPass(blitpassHandle).destinationRegion; EXPECT_EQ(0u, destinationRegion.x); EXPECT_EQ(0u, destinationRegion.y); EXPECT_EQ(static_cast(destinationRenderBuffer.getWidth()), destinationRegion.width); @@ -151,20 +149,26 @@ namespace ramses TEST_F(ABlitPass, canValidate) { - EXPECT_EQ(StatusOK, blitpass.validate()); + ValidationReport report; + blitpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ABlitPass, failsValidationIfSourceRenderBufferGetsDestroyed) { m_scene.destroy(sourceRenderBuffer); - EXPECT_NE(StatusOK, blitpass.validate()); + ValidationReport report; + blitpass.validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(ABlitPass, failsValidationIfDestinationRenderBufferGetsDestroyed) { m_scene.destroy(destinationRenderBuffer); - EXPECT_NE(StatusOK, blitpass.validate()); + ValidationReport report; + blitpass.validate(report); + EXPECT_TRUE(report.hasError()); } } diff --git a/tests/unittests/client/CMakeLists.txt b/tests/unittests/client/CMakeLists.txt new file mode 100644 index 000000000..8230c550c --- /dev/null +++ b/tests/unittests/client/CMakeLists.txt @@ -0,0 +1,45 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + + +if(ramses-sdk_TEXT_SUPPORT) + file(GLOB + RAMSES_CLIENT_TEXT_TEST_FILES + text/*.h + text/*.cpp) +endif() + +if(ramses-sdk_ENABLE_LOGIC) + file(GLOB_RECURSE RAMSES_CLIENT_LOGIC_TEST_FILES + logic/*.cpp + logic/*.h + ) + + set(RAMSES_CLIENT_LOGIC_TEST_INCLUDE_DIRS + logic/shared + ) +endif() + +createModule( + NAME ramses-client-test + TYPE BINARY + INCLUDE_PATHS . + ${RAMSES_CLIENT_LOGIC_TEST_INCLUDE_DIRS} + SRC_FILES *.h + *.cpp + ${RAMSES_CLIENT_TEXT_TEST_FILES} + ${RAMSES_CLIENT_LOGIC_TEST_FILES} + RESOURCE_FOLDERS res + DEPENDENCIES ramses-client + framework-test-utils + ramses-gmock-main +) + +makeTestFromTarget( + TARGET ramses-client-test + SUFFIX UNITTEST) diff --git a/client/test/CameraTest.cpp b/tests/unittests/client/CameraTest.cpp similarity index 57% rename from client/test/CameraTest.cpp rename to tests/unittests/client/CameraTest.cpp index db08898f2..109a19baf 100644 --- a/client/test/CameraTest.cpp +++ b/tests/unittests/client/CameraTest.cpp @@ -9,15 +9,15 @@ #include #include "ClientTestUtils.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/DataObject.h" -#include "Math3d/CameraMatrixHelper.h" -#include "DataObjectImpl.h" -#include "CameraNodeImpl.h" -#include "ramses-utils.h" - -namespace ramses +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/DataObject.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "impl/DataObjectImpl.h" +#include "impl/CameraNodeImpl.h" +#include "ramses/client/ramses-utils.h" + +namespace ramses::internal { using namespace testing; @@ -57,18 +57,20 @@ namespace ramses TYPED_TEST(ACamera, canDestroyCamera) { - EXPECT_EQ(StatusOK, this->m_scene.destroy(*this->camera)); + EXPECT_TRUE(this->m_scene.destroy(*this->camera)); } TYPED_TEST(ACamera, reportsErrorWhenValidatingInitialState) { - EXPECT_NE(StatusOK, this->camera->validate()); + ValidationReport report; + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } TYPED_TEST(ACamera, reportsErrorWhenViewportWidthExceedsMaximum) { this->camera->setFrustum(-0.01f, 0.01f, -0.01f, 0.01f, 0.1f, 1000.f); - EXPECT_NE(StatusOK, this->camera->setViewport(0u, 0u, 32768 + 1, 16u)); + EXPECT_FALSE(this->camera->setViewport(0u, 0u, 32768 + 1, 16u)); // Keeps default values EXPECT_EQ(16u, this->camera->getViewportWidth()); EXPECT_EQ(16u, this->camera->getViewportHeight()); @@ -77,7 +79,7 @@ namespace ramses TYPED_TEST(ACamera, reportsErrorWhenViewportHeightExceedsMaximum) { this->camera->setFrustum(-0.01f, 0.01f, -0.01f, 0.01f, 0.1f, 1000.f); - EXPECT_NE(StatusOK, this->camera->setViewport(0u, 0u, 16u, 32768 + 1)); + EXPECT_FALSE(this->camera->setViewport(0u, 0u, 16u, 32768 + 1)); // Keeps default values EXPECT_EQ(16u, this->camera->getViewportWidth()); EXPECT_EQ(16u, this->camera->getViewportHeight()); @@ -85,7 +87,7 @@ namespace ramses TYPED_TEST(ACamera, canSetItsViewport) { - EXPECT_EQ(StatusOK, this->camera->setViewport(1, -2, 100, 200)); + EXPECT_TRUE(this->camera->setViewport(1, -2, 100, 200)); EXPECT_EQ(1, this->camera->getViewportX()); EXPECT_EQ(-2, this->camera->getViewportY()); EXPECT_EQ(100u, this->camera->getViewportWidth()); @@ -96,18 +98,20 @@ namespace ramses { this->camera->setFrustum(-0.01f, 0.01f, -0.01f, 0.01f, 0.1f, 1000.f); this->camera->setViewport(0u, 0u, 16u, 32u); - EXPECT_EQ(StatusOK, this->camera->validate()); + ValidationReport report; + this->camera->validate(report); + EXPECT_FALSE(report.hasIssue()); } TYPED_TEST(ACamera, reportsErrorIfTryingToRetrieveProjectionMatrixWithoutBeingInitialized) { matrix44f projMat; - EXPECT_NE(StatusOK, this->camera->getProjectionMatrix(projMat)); + EXPECT_FALSE(this->camera->getProjectionMatrix(projMat)); } TYPED_TEST(ACamera, canSetFrustum) { - EXPECT_EQ(StatusOK, this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f)); + EXPECT_TRUE(this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f)); EXPECT_EQ(0.1f, this->camera->getLeftPlane()); EXPECT_EQ(0.2f, this->camera->getRightPlane()); @@ -120,32 +124,32 @@ namespace ramses TYPED_TEST(ACamera, failsToSetFrustumIfInvalid) { // leftPlane >= rightPlane - EXPECT_NE(StatusOK, this->camera->setFrustum(0.2f, 0.1f, 0.3f, 0.4f, 0.5f, 0.6f)); - EXPECT_NE(StatusOK, this->camera->setFrustum(0.1f, 0.1f, 0.3f, 0.4f, 0.5f, 0.6f)); + EXPECT_FALSE(this->camera->setFrustum(0.2f, 0.1f, 0.3f, 0.4f, 0.5f, 0.6f)); + EXPECT_FALSE(this->camera->setFrustum(0.1f, 0.1f, 0.3f, 0.4f, 0.5f, 0.6f)); // bottomPlane >= topPlane - EXPECT_NE(StatusOK, this->camera->setFrustum(0.1f, 0.2f, 0.4f, 0.3f, 0.5f, 0.6f)); - EXPECT_NE(StatusOK, this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.3f, 0.5f, 0.6f)); + EXPECT_FALSE(this->camera->setFrustum(0.1f, 0.2f, 0.4f, 0.3f, 0.5f, 0.6f)); + EXPECT_FALSE(this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.3f, 0.5f, 0.6f)); // nearPlane >= farPlane - EXPECT_NE(StatusOK, this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.6f, 0.5f)); - EXPECT_NE(StatusOK, this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.5f)); + EXPECT_FALSE(this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.6f, 0.5f)); + EXPECT_FALSE(this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.5f)); } TYPED_TEST(ACamera, failsToSetViewportIfInvalid) { - EXPECT_NE(StatusOK, this->camera->setViewport(0, 0, 0u, 0u)); - EXPECT_NE(StatusOK, this->camera->setViewport(0, 0, 1u, 0u)); - EXPECT_NE(StatusOK, this->camera->setViewport(0, 0, 0u, 1u)); + EXPECT_FALSE(this->camera->setViewport(0, 0, 0u, 0u)); + EXPECT_FALSE(this->camera->setViewport(0, 0, 1u, 0u)); + EXPECT_FALSE(this->camera->setViewport(0, 0, 0u, 1u)); } TYPED_TEST(ACamera, returnsProjectionMatrix) { - EXPECT_EQ(StatusOK, this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f)); + EXPECT_TRUE(this->camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f)); - const auto projType = (this->camera->getType() == ERamsesObjectType::PerspectiveCamera ? ramses_internal::ECameraProjectionType::Perspective : ramses_internal::ECameraProjectionType::Orthographic); - const auto expectedMatrix = ramses_internal::CameraMatrixHelper::ProjectionMatrix(ramses_internal::ProjectionParams::Frustum(projType, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f)); + const auto projType = (this->camera->getType() == ERamsesObjectType::PerspectiveCamera ? ECameraProjectionType::Perspective : ECameraProjectionType::Orthographic); + const auto expectedMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Frustum(projType, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f)); matrix44f projMatData; - EXPECT_EQ(StatusOK, this->camera->getProjectionMatrix(projMatData)); + EXPECT_TRUE(this->camera->getProjectionMatrix(projMatData)); for (uint32_t i = 0u; i < 16u; ++i) { @@ -162,11 +166,11 @@ namespace ramses TYPED_TEST(ACamera, canBindViewportData) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); @@ -174,17 +178,17 @@ namespace ramses TYPED_TEST(ACamera, canBindFrustumPlanesData) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); } TYPED_TEST(ACamera, failsToBindViewportIfWrongType) { - const auto wrongDataObject = this->m_scene.createDataObject(EDataType::Vector3F); - EXPECT_NE(StatusOK, this->camera->bindViewportOffset(*wrongDataObject)); - EXPECT_NE(StatusOK, this->camera->bindViewportSize(*wrongDataObject)); + const auto wrongDataObject = this->m_scene.createDataObject(ramses::EDataType::Vector3F); + EXPECT_FALSE(this->camera->bindViewportOffset(*wrongDataObject)); + EXPECT_FALSE(this->camera->bindViewportSize(*wrongDataObject)); EXPECT_FALSE(this->camera->isViewportOffsetBound()); EXPECT_FALSE(this->camera->isViewportSizeBound()); @@ -193,11 +197,11 @@ namespace ramses TYPED_TEST(ACamera, failsToBindViewportDataIfDataObjectFromDifferentScene) { auto otherScene = this->client.createScene(sceneId_t{ this->m_scene.getSceneId().getValue() + 1 }); - const auto do1 = otherScene->createDataObject(EDataType::Vector2I); - const auto do2 = otherScene->createDataObject(EDataType::Vector2I); + const auto do1 = otherScene->createDataObject(ramses::EDataType::Vector2I); + const auto do2 = otherScene->createDataObject(ramses::EDataType::Vector2I); - EXPECT_NE(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_NE(StatusOK, this->camera->bindViewportSize(*do2)); + EXPECT_FALSE(this->camera->bindViewportOffset(*do1)); + EXPECT_FALSE(this->camera->bindViewportSize(*do2)); EXPECT_FALSE(this->camera->isViewportOffsetBound()); EXPECT_FALSE(this->camera->isViewportSizeBound()); @@ -205,43 +209,43 @@ namespace ramses TYPED_TEST(ACamera, failsToBindFrustumIfWrongType) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); - const auto wrongDataObject = this->m_scene.createDataObject(EDataType::Vector3F); - EXPECT_NE(StatusOK, this->camera->bindFrustumPlanes(*do1, *wrongDataObject)); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); + const auto wrongDataObject = this->m_scene.createDataObject(ramses::EDataType::Vector3F); + EXPECT_FALSE(this->camera->bindFrustumPlanes(*do1, *wrongDataObject)); EXPECT_FALSE(this->camera->isFrustumPlanesBound()); - EXPECT_NE(StatusOK, this->camera->bindFrustumPlanes(*wrongDataObject, *do2)); + EXPECT_FALSE(this->camera->bindFrustumPlanes(*wrongDataObject, *do2)); EXPECT_FALSE(this->camera->isFrustumPlanesBound()); } TYPED_TEST(ACamera, failsToBindFrustumDataIfDataObjectFromDifferentScene) { auto otherScene = this->client.createScene(sceneId_t{ this->m_scene.getSceneId().getValue() + 1 }); - const auto do1 = otherScene->createDataObject(EDataType::Vector4F); - const auto do2 = otherScene->createDataObject(EDataType::Vector2F); + const auto do1 = otherScene->createDataObject(ramses::EDataType::Vector4F); + const auto do2 = otherScene->createDataObject(ramses::EDataType::Vector2F); - EXPECT_NE(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_FALSE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_FALSE(this->camera->isFrustumPlanesBound()); } TYPED_TEST(ACamera, canUnbindViewportData) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); - EXPECT_EQ(do1->m_impl.getDataReference(), this->camera->m_impl.getViewportOffsetHandle()); - EXPECT_EQ(do2->m_impl.getDataReference(), this->camera->m_impl.getViewportSizeHandle()); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); + EXPECT_EQ(do1->impl().getDataReference(), this->camera->impl().getViewportOffsetHandle()); + EXPECT_EQ(do2->impl().getDataReference(), this->camera->impl().getViewportSizeHandle()); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportOffset()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportSize()); - EXPECT_NE(do1->m_impl.getDataReference(), this->camera->m_impl.getViewportOffsetHandle()); - EXPECT_NE(do2->m_impl.getDataReference(), this->camera->m_impl.getViewportSizeHandle()); + EXPECT_TRUE(this->camera->unbindViewportOffset()); + EXPECT_TRUE(this->camera->unbindViewportSize()); + EXPECT_NE(do1->impl().getDataReference(), this->camera->impl().getViewportOffsetHandle()); + EXPECT_NE(do2->impl().getDataReference(), this->camera->impl().getViewportSizeHandle()); EXPECT_FALSE(this->camera->isViewportOffsetBound()); EXPECT_FALSE(this->camera->isViewportSizeBound()); @@ -249,31 +253,31 @@ namespace ramses TYPED_TEST(ACamera, canUnbindFrustumPlanesData) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); - EXPECT_EQ(do1->m_impl.getDataReference(), this->camera->m_impl.getFrustrumPlanesHandle()); - EXPECT_EQ(do2->m_impl.getDataReference(), this->camera->m_impl.getFrustrumNearFarPlanesHandle()); + EXPECT_EQ(do1->impl().getDataReference(), this->camera->impl().getFrustrumPlanesHandle()); + EXPECT_EQ(do2->impl().getDataReference(), this->camera->impl().getFrustrumNearFarPlanesHandle()); - EXPECT_EQ(StatusOK, this->camera->unbindFrustumPlanes()); + EXPECT_TRUE(this->camera->unbindFrustumPlanes()); EXPECT_FALSE(this->camera->isFrustumPlanesBound()); - EXPECT_NE(do1->m_impl.getDataReference(), this->camera->m_impl.getFrustrumPlanesHandle()); - EXPECT_NE(do2->m_impl.getDataReference(), this->camera->m_impl.getFrustrumNearFarPlanesHandle()); + EXPECT_NE(do1->impl().getDataReference(), this->camera->impl().getFrustrumPlanesHandle()); + EXPECT_NE(do2->impl().getDataReference(), this->camera->impl().getFrustrumNearFarPlanesHandle()); } TYPED_TEST(ACamera, getsReferencedViewportValuesWhenBound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->setViewport(-1, -2, 16, 32)); + EXPECT_TRUE(this->camera->setViewport(-1, -2, 16, 32)); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); @@ -285,14 +289,14 @@ namespace ramses TYPED_TEST(ACamera, getsReferencedFrustumPlanesValuesWhenBound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, 2.f, 3.f, 4.f }); do2->setValue(vec2f{ 5.f, 6.f }); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); EXPECT_EQ(1.f, this->camera->getLeftPlane()); @@ -305,15 +309,15 @@ namespace ramses TYPED_TEST(ACamera, getsCorrectViewportValuesWhenBoundFromOneDataObjectToAnother) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->setViewport(-1, -2, 16, 32)); + EXPECT_TRUE(this->camera->setViewport(-1, -2, 16, 32)); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); @@ -322,8 +326,8 @@ namespace ramses EXPECT_EQ(3u, this->camera->getViewportWidth()); EXPECT_EQ(4u, this->camera->getViewportHeight()); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do2)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do1)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do2)); + EXPECT_TRUE(this->camera->bindViewportSize(*do1)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); @@ -335,22 +339,22 @@ namespace ramses TYPED_TEST(ACamera, getsCorrectFrustumValuesWhenBoundFromOneDataObjectToAnother) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, 2.f, 3.f, 4.f }); do2->setValue(vec2f{ 5.f, 6.f }); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); - const auto do3 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do4 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do3 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do4 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do3->setValue(vec4f{ -1.f, -2.f, -3.f, -4.f }); do4->setValue(vec2f{ -5.f, -6.f }); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do3, *do4)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do3, *do4)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); EXPECT_EQ(-1.f, this->camera->getLeftPlane()); @@ -363,15 +367,15 @@ namespace ramses TYPED_TEST(ACamera, getsReferencedViewportValuesWhenBound_alsoWhenChangedInDataObject) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->setViewport(-1, -2, 16, 32)); + EXPECT_TRUE(this->camera->setViewport(-1, -2, 16, 32)); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); @@ -391,14 +395,14 @@ namespace ramses TYPED_TEST(ACamera, getsReferencedFrustumValuesWhenBound_alsoWhenChangedInDataObject) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, 2.f, 3.f, 4.f }); do2->setValue(vec2f{ 5.f, 6.f }); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); do1->setValue(vec4f{ -1.f, -2.f, -3.f, -4.f }); @@ -416,14 +420,14 @@ namespace ramses { auto camera2 = &this->template createObject("camera"); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->setViewport(-1, -2, 16, 32)); - EXPECT_EQ(StatusOK, camera2->setViewport(-11, -22, 166, 322)); + EXPECT_TRUE(this->camera->setViewport(-1, -2, 16, 32)); + EXPECT_TRUE(camera2->setViewport(-11, -22, 166, 322)); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, camera2->bindViewportSize(*do1)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(camera2->bindViewportSize(*do1)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(camera2->isViewportSizeBound()); @@ -450,17 +454,17 @@ namespace ramses TYPED_TEST(ACamera, canBindDataToTwoCamerasFrustum) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, 2.f, 3.f, 4.f }); do2->setValue(vec2f{ 5.f, 6.f }); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); auto camera2 = &this->template createObject("camera"); - EXPECT_EQ(StatusOK, camera2->setFrustum(-10.f, 10.f, -10.f, 10.f, 0.01f, 10.f)); + EXPECT_TRUE(camera2->setFrustum(-10.f, 10.f, -10.f, 10.f, 0.01f, 10.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); - EXPECT_EQ(StatusOK, camera2->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(camera2->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); EXPECT_TRUE(camera2->isFrustumPlanesBound()); @@ -495,15 +499,15 @@ namespace ramses TYPED_TEST(ACamera, getsOriginalViewportValuesWhenUnbound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->setViewport(-1, -2, 16, 32)); + EXPECT_TRUE(this->camera->setViewport(-1, -2, 16, 32)); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); EXPECT_TRUE(this->camera->isViewportOffsetBound()); EXPECT_TRUE(this->camera->isViewportSizeBound()); @@ -512,8 +516,8 @@ namespace ramses EXPECT_EQ(3u, this->camera->getViewportWidth()); EXPECT_EQ(4u, this->camera->getViewportHeight()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportOffset()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportSize()); + EXPECT_TRUE(this->camera->unbindViewportOffset()); + EXPECT_TRUE(this->camera->unbindViewportSize()); EXPECT_EQ(-1, this->camera->getViewportX()); EXPECT_EQ(-2, this->camera->getViewportY()); @@ -523,17 +527,17 @@ namespace ramses TYPED_TEST(ACamera, getsOriginalFrustumValuesWhenUnbound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, 2.f, 3.f, 4.f }); do2->setValue(vec2f{ 5.f, 6.f }); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); - EXPECT_EQ(StatusOK, this->camera->unbindFrustumPlanes()); + EXPECT_TRUE(this->camera->unbindFrustumPlanes()); EXPECT_EQ(-1.f, this->camera->getLeftPlane()); EXPECT_EQ(1.f, this->camera->getRightPlane()); @@ -545,19 +549,19 @@ namespace ramses TYPED_TEST(ACamera, viewportValueSetIfBoundOnlyAffectsOriginalValueThatBecomesActiveAfterUnbound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); - EXPECT_EQ(StatusOK, this->camera->setViewport(-1, -2, 16, 32)); + EXPECT_TRUE(this->camera->setViewport(-1, -2, 16, 32)); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); EXPECT_EQ(1, this->camera->getViewportX()); EXPECT_EQ(2, this->camera->getViewportY()); EXPECT_EQ(16u, this->camera->getViewportWidth()); EXPECT_EQ(32u, this->camera->getViewportHeight()); - EXPECT_EQ(StatusOK, this->camera->setViewport(-11, -22, 166, 322)); + EXPECT_TRUE(this->camera->setViewport(-11, -22, 166, 322)); EXPECT_EQ(1, this->camera->getViewportX()); EXPECT_EQ(2, this->camera->getViewportY()); @@ -565,7 +569,7 @@ namespace ramses EXPECT_EQ(166u, this->camera->getViewportWidth()); EXPECT_EQ(322u, this->camera->getViewportHeight()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportOffset()); + EXPECT_TRUE(this->camera->unbindViewportOffset()); // offset set back to original value, which was updated while bound EXPECT_EQ(-11, this->camera->getViewportX()); @@ -576,18 +580,18 @@ namespace ramses TYPED_TEST(ACamera, frustumValueSetIfBoundOnlyAffectOriginalValuesThatBecomeActiveAfterUnbound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, 2.f, 3.f, 4.f }); do2->setValue(vec2f{ 5.f, 6.f }); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); // frustum planes currently bound so these values have no effect (yet) - EXPECT_EQ(StatusOK, this->camera->setFrustum(-10.f, 10.f, -10.f, 10.f, 0.01f, 10.f)); + EXPECT_TRUE(this->camera->setFrustum(-10.f, 10.f, -10.f, 10.f, 0.01f, 10.f)); EXPECT_EQ(1.f, this->camera->getLeftPlane()); EXPECT_EQ(2.f, this->camera->getRightPlane()); @@ -596,7 +600,7 @@ namespace ramses EXPECT_EQ(5.f, this->camera->getNearPlane()); EXPECT_EQ(6.f, this->camera->getFarPlane()); - EXPECT_EQ(StatusOK, this->camera->unbindFrustumPlanes()); + EXPECT_TRUE(this->camera->unbindFrustumPlanes()); // only now when unbounded values fall back to last directly set values EXPECT_EQ(-10.f, this->camera->getLeftPlane()); @@ -610,111 +614,131 @@ namespace ramses TYPED_TEST(ACamera, doesNotReportValidationErrorWhenViewportNotInitializedButBoundToValidData) { // make frustum valid - not testing frustum validity here - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); + EXPECT_TRUE(this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); - EXPECT_EQ(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); + ValidationReport report; + this->camera->validate(report); + EXPECT_FALSE(report.hasIssue()); } TYPED_TEST(ACamera, reportsValidationErrorWhenViewportNotInitializedAndBoundToValidDataButUnboundAfterwards) { // make frustum valid - not testing frustum validity here - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); + EXPECT_TRUE(this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 3, 4 }); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); - EXPECT_EQ(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); + ValidationReport report; + this->camera->validate(report); + EXPECT_FALSE(report.hasIssue()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportOffset()); - EXPECT_NE(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->unbindViewportOffset()); + report.clear(); + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); - EXPECT_EQ(StatusOK, this->camera->unbindViewportSize()); - EXPECT_NE(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->unbindViewportSize()); + report.clear(); + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } TYPED_TEST(ACamera, reportsValidationErrorWhenFrustumNotInitializedAndBoundToValidDataButUnboundAfterwards) { // make viewport valid - not testing viewport validity here - EXPECT_EQ(StatusOK, this->camera->setViewport(0, 0, 16, 16)); + EXPECT_TRUE(this->camera->setViewport(0, 0, 16, 16)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ -1.f, 1.f, -1.f, 1.f }); do2->setValue(vec2f{ 1.f, 10.f }); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); // bound values valid - EXPECT_EQ(StatusOK, this->camera->validate()); + ValidationReport report; + this->camera->validate(report); + EXPECT_FALSE(report.hasIssue()); - EXPECT_EQ(StatusOK, this->camera->unbindFrustumPlanes()); + EXPECT_TRUE(this->camera->unbindFrustumPlanes()); // no valid original values - EXPECT_NE(StatusOK, this->camera->validate()); + report.clear(); + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } TYPED_TEST(ACamera, reportsValidationErrorWhenViewportNotInitializedAndBoundToInvalidData) { // make frustum valid - not testing frustum validity here - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); + EXPECT_TRUE(this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 0, 4 }); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); - EXPECT_NE(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); + ValidationReport report; + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } TYPED_TEST(ACamera, reportsValidationErrorWhenFrustumNotInitializedAndBoundToInvalidData) { // make viewport valid - not testing viewport validity here - EXPECT_EQ(StatusOK, this->camera->setViewport(0, 0, 16, 16)); + EXPECT_TRUE(this->camera->setViewport(0, 0, 16, 16)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, -1.f, 1.f, -1.f }); do2->setValue(vec2f{ 1.f, 10.f }); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); - EXPECT_NE(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); + ValidationReport report; + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } TYPED_TEST(ACamera, reportsValidationErrorWhenViewportInitializedButBoundToInvalidData) { // make frustum valid - not testing frustum validity here - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); + EXPECT_TRUE(this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); - EXPECT_EQ(StatusOK, this->camera->setViewport(0, 0, 16, 16)); + EXPECT_TRUE(this->camera->setViewport(0, 0, 16, 16)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector2I); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2I); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2I); do1->setValue(vec2i{ 1, 2 }); do2->setValue(vec2i{ 0, 4 }); - EXPECT_EQ(StatusOK, this->camera->bindViewportOffset(*do1)); - EXPECT_EQ(StatusOK, this->camera->bindViewportSize(*do2)); - EXPECT_NE(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->bindViewportOffset(*do1)); + EXPECT_TRUE(this->camera->bindViewportSize(*do2)); + ValidationReport report; + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } TYPED_TEST(ACamera, reportsValidationErrorWhenFrustumInitializedButBoundToInvalidData) { // make viewport valid - not testing viewport validity here - EXPECT_EQ(StatusOK, this->camera->setViewport(0, 0, 16, 16)); + EXPECT_TRUE(this->camera->setViewport(0, 0, 16, 16)); - EXPECT_EQ(StatusOK, this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); + EXPECT_TRUE(this->camera->setFrustum(-1, 1, -1, 1, 0.1f, 0.2f)); - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); do1->setValue(vec4f{ 1.f, -1.f, 1.f, -1.f }); do2->setValue(vec2f{ 1.f, 10.f }); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); - EXPECT_NE(StatusOK, this->camera->validate()); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); + ValidationReport report; + this->camera->validate(report); + EXPECT_TRUE(report.hasError()); } class APerspectiveCamera : public LocalTestClientWithScene, public testing::Test @@ -735,7 +759,7 @@ namespace ramses const float nearPlane = 0.1f; const float farPlane = 100.f; - EXPECT_EQ(StatusOK, camera->setFrustum(fov, aspectRatio, nearPlane, farPlane)); + EXPECT_TRUE(camera->setFrustum(fov, aspectRatio, nearPlane, farPlane)); EXPECT_FLOAT_EQ(fov, camera->getVerticalFieldOfView()); EXPECT_FLOAT_EQ(aspectRatio, camera->getAspectRatio()); @@ -750,23 +774,23 @@ namespace ramses const float nearPlane = 0.1f; const float farPlane = 100.f; - EXPECT_NE(StatusOK, camera->setFrustum(0.f, aspectRatio, nearPlane, farPlane)); - EXPECT_NE(StatusOK, camera->setFrustum(180.f, aspectRatio, nearPlane, farPlane)); - EXPECT_NE(StatusOK, camera->setFrustum(fov, 0.f, nearPlane, farPlane)); - EXPECT_NE(StatusOK, camera->setFrustum(fov, -1.f, nearPlane, farPlane)); - EXPECT_NE(StatusOK, camera->setFrustum(fov, aspectRatio, 0.f, farPlane)); - EXPECT_NE(StatusOK, camera->setFrustum(fov, aspectRatio, nearPlane, nearPlane)); + EXPECT_FALSE(camera->setFrustum(0.f, aspectRatio, nearPlane, farPlane)); + EXPECT_FALSE(camera->setFrustum(180.f, aspectRatio, nearPlane, farPlane)); + EXPECT_FALSE(camera->setFrustum(fov, 0.f, nearPlane, farPlane)); + EXPECT_FALSE(camera->setFrustum(fov, -1.f, nearPlane, farPlane)); + EXPECT_FALSE(camera->setFrustum(fov, aspectRatio, 0.f, farPlane)); + EXPECT_FALSE(camera->setFrustum(fov, aspectRatio, nearPlane, nearPlane)); } TEST_F(APerspectiveCamera, canBindFrustumAndGetOriginalValuesWhenUnbound) { - const auto do1 = this->m_scene.createDataObject(EDataType::Vector4F); - const auto do2 = this->m_scene.createDataObject(EDataType::Vector2F); + const auto do1 = this->m_scene.createDataObject(ramses::EDataType::Vector4F); + const auto do2 = this->m_scene.createDataObject(ramses::EDataType::Vector2F); EXPECT_TRUE(RamsesUtils::SetPerspectiveCameraFrustumToDataObjects(45.f, 2.3f, 2.f, 20.f, *do1, *do2)); - EXPECT_EQ(StatusOK, this->camera->setFrustum(19.f, 1.33f, 0.1f, 1.f)); + EXPECT_TRUE(this->camera->setFrustum(19.f, 1.33f, 0.1f, 1.f)); - EXPECT_EQ(StatusOK, this->camera->bindFrustumPlanes(*do1, *do2)); + EXPECT_TRUE(this->camera->bindFrustumPlanes(*do1, *do2)); EXPECT_TRUE(this->camera->isFrustumPlanesBound()); EXPECT_FLOAT_EQ(45.f, this->camera->getVerticalFieldOfView()); @@ -774,7 +798,7 @@ namespace ramses EXPECT_EQ(2.f, this->camera->getNearPlane()); EXPECT_EQ(20.f, this->camera->getFarPlane()); - EXPECT_EQ(StatusOK, this->camera->unbindFrustumPlanes()); + EXPECT_TRUE(this->camera->unbindFrustumPlanes()); EXPECT_FLOAT_EQ(19.f, this->camera->getVerticalFieldOfView()); EXPECT_FLOAT_EQ(1.33f, this->camera->getAspectRatio()); diff --git a/tests/unittests/client/ClientApplicationLogicTest.cpp b/tests/unittests/client/ClientApplicationLogicTest.cpp new file mode 100644 index 000000000..3473fa0ab --- /dev/null +++ b/tests/unittests/client/ClientApplicationLogicTest.cpp @@ -0,0 +1,274 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" +#include "internal/ClientApplicationLogic.h" +#include "ComponentMocks.h" +#include "ResourceMock.h" +#include "DummyResource.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/Components/ResourceComponent.h" +#include "internal/Components/SceneGraphComponent.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/Communication/TransportCommon/FakeConnectionSystem.h" +#include "internal/Communication/TransportCommon/FakeConnectionStatusUpdateNotifier.h" +#include "internal/Components/FileInputStreamContainer.h" + +namespace ramses::internal +{ + class AClientApplicationLogic : public ::testing::Test + { + public: + AClientApplicationLogic() + : dummyGuid(555) + , logic(dummyGuid, frameworkLock) + , sceneId(44u) + , dummyScene(SceneInfo(sceneId)) + { + logic.init(resourceComponent, scenegraphProviderComponent); + } + + protected: + Guid dummyGuid; + StrictMock resourceComponent; + StrictMock scenegraphProviderComponent; + PlatformLock frameworkLock; + ClientApplicationLogic logic; + + SceneId sceneId; + ClientScene dummyScene; + + void createDummyScene() + { + EXPECT_CALL(scenegraphProviderComponent, handleCreateScene(Ref(dummyScene), false, Ref(logic))); + logic.createScene(dummyScene, false); + } + }; + + TEST_F(AClientApplicationLogic, sceneDistributionIsDisabledInitially) + { + createDummyScene(); + EXPECT_FALSE(logic.isScenePublished(sceneId)); + } + + TEST_F(AClientApplicationLogic, canEnableLocalOnlySceneDistribution) + { + createDummyScene(); + + EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode::LocalOnly)); + logic.publishScene(sceneId, EScenePublicationMode::LocalOnly); + + EXPECT_TRUE(logic.isScenePublished(sceneId)); + } + + TEST_F(AClientApplicationLogic, canEnableSceneDistribution) + { + createDummyScene(); + EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode::LocalAndRemote)); + logic.publishScene(sceneId, EScenePublicationMode::LocalAndRemote); + + EXPECT_TRUE(logic.isScenePublished(sceneId)); + } + + TEST_F(AClientApplicationLogic, canDisableLocalOnlySceneDistribution) + { + createDummyScene(); + EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode::LocalOnly)); + logic.publishScene(sceneId, EScenePublicationMode::LocalOnly); + + EXPECT_CALL(scenegraphProviderComponent, handleUnpublishScene(sceneId)); + logic.unpublishScene(sceneId); + + EXPECT_FALSE(logic.isScenePublished(sceneId)); + } + + TEST_F(AClientApplicationLogic, canDisableSceneDistribution) + { + createDummyScene(); + EXPECT_CALL(scenegraphProviderComponent, handlePublishScene(sceneId, EScenePublicationMode::LocalAndRemote)); + logic.publishScene(sceneId, EScenePublicationMode::LocalAndRemote); + + EXPECT_CALL(scenegraphProviderComponent, handleUnpublishScene(sceneId)); + logic.unpublishScene(sceneId); + + EXPECT_FALSE(logic.isScenePublished(sceneId)); + } + + TEST_F(AClientApplicationLogic, forwardsAddingOfResourceFileToResourceComponent) + { + const ResourceInfo resourceInfo(EResourceType::VertexArray, ResourceContentHash(44u, 0), 2u, 1u); + ResourceTableOfContents resourceToc; + resourceToc.registerContents(resourceInfo, 0u, 1u); + const std::string fileName("resourceFile"); + InputStreamContainerSPtr resourceFileStream(std::make_shared(fileName)); + + EXPECT_CALL(resourceComponent, addResourceFile(resourceFileStream, Ref(resourceToc))); + logic.addResourceFile(resourceFileStream, resourceToc); + } + + TEST_F(AClientApplicationLogic, triesToGetRequestedResourceFromResourceComponent) + { + const ResourceContentHash dummyResourceHash(44u, 0); + ON_CALL(resourceComponent, getResource(_)).WillByDefault(Return(ManagedResource())); + EXPECT_CALL(resourceComponent, getResource(dummyResourceHash)); + EXPECT_EQ(ManagedResource(), logic.getResource(dummyResourceHash)); + } + + TEST_F(AClientApplicationLogic, triesToGetHashUsageFromResourceComponent) + { + const ResourceContentHash dummyResourceHash(44u, 0); + ON_CALL(resourceComponent, getResourceHashUsage(_)).WillByDefault(Return(ResourceHashUsage())); + EXPECT_CALL(resourceComponent, getResourceHashUsage(dummyResourceHash)); + EXPECT_FALSE(logic.getHashUsage(dummyResourceHash).isValid()); + } + + TEST_F(AClientApplicationLogic, addsAndRemovesResourceFilesFromComponent) + { + InputStreamContainerSPtr resourceFileInputStream; + ResourceTableOfContents toc; + + EXPECT_CALL(resourceComponent, addResourceFile(_,_)); + const auto handle = logic.addResourceFile(resourceFileInputStream, toc); + + EXPECT_CALL(resourceComponent, removeResourceFile(handle)); + logic.removeResourceFile(handle); + } + + TEST_F(AClientApplicationLogic, gathersSceneReferenceEventsInAContainer) + { + SceneReferenceEvent event(SceneId { 123 }); + event.referencedScene = SceneId{ 123456789 }; + + EXPECT_TRUE(logic.popSceneReferenceEvents().empty()); + logic.handleSceneReferenceEvent(event, Guid{}); + const auto result = logic.popSceneReferenceEvents(); + EXPECT_EQ(result.size(), 1u); + const auto sre = result.front(); + EXPECT_EQ(sre.referencedScene, event.referencedScene); + + logic.handleSceneReferenceEvent(event, Guid{}); + logic.handleSceneReferenceEvent(event, Guid{}); + logic.handleSceneReferenceEvent(event, Guid{}); + logic.handleSceneReferenceEvent(event, Guid{}); + EXPECT_EQ(logic.popSceneReferenceEvents().size(), 4u); + } + + + TEST_F(AClientApplicationLogic, incomingResourceAvailabilityEventDoesNoHarm) + { + ResourceAvailabilityEvent event; + event.sceneid = SceneId { 123 }; + + logic.handleResourceAvailabilityEvent(event, Guid {}); + } + + TEST_F(AClientApplicationLogic, returnsReturnValueFromComponentOnFlush) + { + EXPECT_CALL(scenegraphProviderComponent, handleFlush(_, _, _)).WillOnce(Return(true)); + EXPECT_TRUE(logic.flush(sceneId, {}, {})); + + EXPECT_CALL(scenegraphProviderComponent, handleFlush(_, _, _)).WillOnce(Return(false)); + EXPECT_FALSE(logic.flush(sceneId, {}, {})); + } + + class AClientApplicationLogicWithRealComponents : public ::testing::Test + { + public: + AClientApplicationLogicWithRealComponents() + : resComp(stats, fwlock) + , sceneComp(clientId, commSystem, connStatusUpdateNotifier, resComp, fwlock, ramses::EFeatureLevel_Latest) + , logic(clientId, fwlock) + { + logic.init(resComp, sceneComp); + } + + protected: + const SceneId sceneId = SceneId(1u); + const Guid clientId = Guid(1); + const Guid renderer1 = Guid(2); + const Guid renderer2 = Guid(3); + + PlatformLock fwlock; + StatisticCollectionFramework stats; + ResourceComponent resComp; + FakeConnectionSystem commSystem; + FakeConnectionStatusUpdateNotifier connStatusUpdateNotifier; + SceneGraphComponent sceneComp; + + ClientApplicationLogic logic; + }; + + TEST_F(AClientApplicationLogicWithRealComponents, keepsResourcesAliveForNewSubscriberForShadowCopyScene) + { + ClientScene clientScene{ SceneInfo(sceneId) }; + logic.createScene(clientScene, false); + logic.publishScene(sceneId, EScenePublicationMode::LocalAndRemote); + auto res = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {}); + res->setResourceData(ResourceBlob{ 1 }, { 1u, 1u }); + auto hash = res->getHash(); + { + // simulate creation of texture2d + auto manRes = logic.addResource(res); + + auto hashUsage = logic.getHashUsage(hash); + + // use texture2d + const auto dataSlotHandle = clientScene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, hash, {} }, {}); + + sceneComp.handleSubscribeScene(sceneId, renderer1); + EXPECT_TRUE(logic.flush(sceneId, {}, {})); + + // release data slot along with its texture2d (by leaving scope) + clientScene.releaseDataSlot(dataSlotHandle); + } + + EXPECT_EQ(resComp.getResource(hash).get(), res); + sceneComp.handleSubscribeScene(sceneId, renderer2); + } + + TEST_F(AClientApplicationLogicWithRealComponents, keepsAlsoOldResourcesAliveForNewSubscriberForShadowCopyScene) + { + ClientScene clientScene{ SceneInfo(sceneId) }; + logic.createScene(clientScene, false); + logic.publishScene(sceneId, EScenePublicationMode::LocalAndRemote); + auto res = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {}); + res->setResourceData(ResourceBlob{ 1 }, { 1u, 1u }); + auto res2 = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(2u, 2u, 1u, EPixelStorageFormat::R8, true, {}, { 4u }), {}); + res2->setResourceData(ResourceBlob{ 2 }, { 2u, 2u }); + auto hash = res->getHash(); + auto hash2 = res2->getHash(); + { + // simulate creation of texture2d + auto manRes = logic.addResource(res); + auto hashUsage = logic.getHashUsage(hash); + + // use texture2d + const auto dataSlotHandle = clientScene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, hash, {} }, {}); + + sceneComp.handleSubscribeScene(sceneId, renderer1); + EXPECT_TRUE(logic.flush(sceneId, {}, {})); + + // another stream texture + auto manRes2 = logic.addResource(res2); + auto hashUsage2 = logic.getHashUsage(hash2); + + // use texture2d + clientScene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(1u), {}, {}, hash2, {}}, {}); + + EXPECT_TRUE(logic.flush(sceneId, {}, {})); + + // release first data slot along with its texture2d (by leaving scope) + clientScene.releaseDataSlot(dataSlotHandle); + } + + EXPECT_EQ(resComp.getResource(hash).get(), res); + EXPECT_EQ(resComp.getResource(hash2).get(), res2); + sceneComp.handleSubscribeScene(sceneId, renderer2); + } +} diff --git a/client/test/ClientEventHandlerMock.cpp b/tests/unittests/client/ClientEventHandlerMock.cpp similarity index 95% rename from client/test/ClientEventHandlerMock.cpp rename to tests/unittests/client/ClientEventHandlerMock.cpp index c39681668..2a3d36081 100644 --- a/client/test/ClientEventHandlerMock.cpp +++ b/tests/unittests/client/ClientEventHandlerMock.cpp @@ -8,7 +8,7 @@ #include "ClientEventHandlerMock.h" -namespace ramses +namespace ramses::internal { ClientEventHandlerMock::ClientEventHandlerMock() = default; ClientEventHandlerMock::~ClientEventHandlerMock() = default; diff --git a/client/test/ClientEventHandlerMock.h b/tests/unittests/client/ClientEventHandlerMock.h similarity index 70% rename from client/test/ClientEventHandlerMock.h rename to tests/unittests/client/ClientEventHandlerMock.h index d04b79847..1a67b78e6 100644 --- a/client/test/ClientEventHandlerMock.h +++ b/tests/unittests/client/ClientEventHandlerMock.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTEVENTHANDLERMOCK_H -#define RAMSES_CLIENTEVENTHANDLERMOCK_H +#pragma once #include "gmock/gmock.h" -#include "ramses-client-api/IClientEventHandler.h" -#include "ramses-client-api/SceneReference.h" +#include "ramses/client/IClientEventHandler.h" +#include "ramses/client/SceneReference.h" #include -namespace ramses +namespace ramses::internal { class ClientEventHandlerMock : public IClientEventHandler { @@ -24,12 +23,10 @@ namespace ramses ~ClientEventHandlerMock() override; MOCK_METHOD(void, sceneFileLoadFailed, (std::string_view filename), (override)); - MOCK_METHOD(void, sceneFileLoadSucceeded, (std::string_view filename, Scene* loadedScene), (override)); - MOCK_METHOD(void, sceneReferenceStateChanged, (SceneReference& sceneRef, RendererSceneState state), (override)); - MOCK_METHOD(void, sceneReferenceFlushed, (SceneReference& sceneRef, sceneVersionTag_t versionTag), (override)); + MOCK_METHOD(void, sceneFileLoadSucceeded, (std::string_view filename, ramses::Scene* loadedScene), (override)); + MOCK_METHOD(void, sceneReferenceStateChanged, (ramses::SceneReference& sceneRef, RendererSceneState state), (override)); + MOCK_METHOD(void, sceneReferenceFlushed, (ramses::SceneReference& sceneRef, sceneVersionTag_t versionTag), (override)); MOCK_METHOD(void, dataLinked, (sceneId_t providerScene, dataProviderId_t providerId, sceneId_t consumerScene, dataConsumerId_t consumerId, bool success), (override)); MOCK_METHOD(void, dataUnlinked, (sceneId_t consumerScene, dataConsumerId_t consumerId, bool success), (override)); }; } - -#endif diff --git a/tests/unittests/client/ClientTestUtils.cpp b/tests/unittests/client/ClientTestUtils.cpp new file mode 100644 index 000000000..b5408cf14 --- /dev/null +++ b/tests/unittests/client/ClientTestUtils.cpp @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ClientTestUtils.h" + +namespace ramses::internal +{ + template <> std::vector SomeDataVector() { return { 1, 2, 3 }; } + template <> std::vector SomeDataVector() { return { {1, 2}, {3, 4} }; } + template <> std::vector SomeDataVector() { return { {1, 2, 3}, {3, 4, 5} }; } + template <> std::vector SomeDataVector() { return { {1, 2, 3, 4}, {3, 4, 5, 6} }; } + template <> std::vector> SomeDataVector() { return { {1.f, 2.f, 3.f, 4.f, 5.f}, {3.f, 4.f, 5.f, 6.f, 7.f} }; } + + template <> std::vector SomeDataVector() { return { 1, 2, 3 }; } + template <> std::vector SomeDataVector() { return { 1, 2, 3 }; } + template <> std::vector SomeDataVector() { return { 1.f, 2.f, 3.f }; } + template <> std::vector SomeDataVector() { return { vec2f{1.f, 2.f}, vec2f{3.f, 4.f} }; } + template <> std::vector SomeDataVector() { return { vec3f{1.f, 2.f, 3.f}, vec3f{3.f, 4.f, 5.f} }; } + template <> std::vector SomeDataVector() { return { vec4f{1.f, 2.f, 3.f, 4.f}, vec4f{3.f, 4.f, 5.f, 6.f} }; } + template <> std::vector SomeDataVector() { return { std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6} }; } +} diff --git a/client/test/ClientTestUtils.h b/tests/unittests/client/ClientTestUtils.h similarity index 71% rename from client/test/ClientTestUtils.h rename to tests/unittests/client/ClientTestUtils.h index 01f7cbec7..823bb0e2e 100644 --- a/client/test/ClientTestUtils.h +++ b/tests/unittests/client/ClientTestUtils.h @@ -6,36 +6,35 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CLIENTTESTUTILS_H -#define RAMSES_CLIENTTESTUTILS_H +#pragma once -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" #include "CreationHelper.h" #include "MockActionCollector.h" -#include "RamsesFrameworkImpl.h" -#include "RamsesFrameworkConfigImpl.h" -#include "MeshNodeImpl.h" -#include "SceneImpl.h" -#include "Scene/ClientScene.h" -#include "RamsesClientImpl.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/EffectDescription.h" +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/SceneImpl.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "impl/RamsesClientImpl.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/EffectDescription.h" #include "TestEffects.h" #include -namespace ramses_internal +namespace ramses::internal { class ClientScene; -} -namespace ramses -{ + template + std::vector SomeDataVector(); + class LocalTestClient { public: @@ -43,10 +42,10 @@ namespace ramses : framework{ GetDefaultFrameworkConfig() } , client(*framework.createClient("localTestClient")) , m_creationHelper(nullptr, &client) - , sceneActionsCollector() { - sceneActionsCollector.init(framework.m_impl.getScenegraphComponent()); - framework.m_impl.getScenegraphComponent().setSceneRendererHandler(&sceneActionsCollector); + EXPECT_EQ(EFeatureLevel_Latest, framework.getFeatureLevel()); + sceneActionsCollector.init(framework.impl().getScenegraphComponent()); + framework.impl().getScenegraphComponent().setSceneRendererHandler(&sceneActionsCollector); } template @@ -70,8 +69,8 @@ namespace ramses //disabling the periodic logs is important as otherwise race conditions can occur in the tests that check //that the statistic counters are updated (periodic logger resets the counters) static RamsesFrameworkConfig config{EFeatureLevel_Latest}; - config.setLogLevel(ramses::ELogLevel::Off); - config.setConnectionSystem(ramses::EConnectionSystem::Off); + config.setLogLevel(ELogLevel::Off); + config.setConnectionSystem(EConnectionSystem::Off); config.setPeriodicLogInterval(std::chrono::seconds(0)); return config; } @@ -86,11 +85,10 @@ namespace ramses class LocalTestClientWithScene : public LocalTestClient { public: - explicit LocalTestClientWithScene(const SceneConfig& sceneConfig = SceneConfig()) - : LocalTestClient() - , m_scene(*client.createScene(sceneId_t(123u), sceneConfig)) + explicit LocalTestClientWithScene(EScenePublicationMode publicationMode = EScenePublicationMode::LocalOnly) + : m_scene(CreateScene(client, publicationMode)) , m_constRefToScene(m_scene) - , m_internalScene(m_scene.m_impl.getIScene()) + , m_internalScene(m_scene.impl().getIScene()) { m_creationHelper.setScene(&m_scene); } @@ -101,12 +99,18 @@ namespace ramses client.destroy(m_scene); } - Scene& getScene() + static ramses::Scene& CreateScene(RamsesClient& client, EScenePublicationMode publicationMode) + { + SceneConfig config{sceneId_t(123u), publicationMode}; + return *client.createScene(config); + } + + ramses::Scene& getScene() { return m_scene; } - ramses_internal::ClientScene& getInternalScene() + ClientScene& getInternalScene() { return m_internalScene; } @@ -116,13 +120,13 @@ namespace ramses return createObject("indices"); } - GeometryBinding& createValidGeometry(Effect* effect = nullptr, bool useIndices = true) + Geometry& createValidGeometry(Effect* effect = nullptr, bool useIndices = true) { if (nullptr == effect) { effect = TestEffects::CreateTestEffect(m_scene); } - GeometryBinding* geometry = m_scene.createGeometryBinding(*effect, "geometry"); + Geometry* geometry = m_scene.createGeometry(*effect, "geometry"); EXPECT_TRUE(geometry != nullptr); if (useIndices) { @@ -137,21 +141,21 @@ namespace ramses MeshNode* mesh = m_scene.createMeshNode(); Appearance* appearance = m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); mesh->setAppearance(*appearance); - GeometryBinding& geometry = createValidGeometry(); - mesh->setGeometryBinding(geometry); + Geometry& geometry = createValidGeometry(); + mesh->setGeometry(geometry); return *mesh; } - void setValidPerspectiveCameraParameters(PerspectiveCamera& camera) + static void SetValidPerspectiveCameraParameters(PerspectiveCamera& camera) { camera.setFrustum(-0.01f, 0.01f, -0.01f, 0.01f, 0.1f, 1000.f); camera.setViewport(0u, 0u, 16u, 32u); } protected: - Scene& m_scene; - const Scene& m_constRefToScene; - ramses_internal::ClientScene& m_internalScene; + ramses::Scene& m_scene; + const ramses::Scene& m_constRefToScene; + ClientScene& m_internalScene; }; class ClientTestUtils @@ -159,7 +163,7 @@ namespace ramses public: static bool CompareBinaryFiles(char const* fileName1, char const* fileName2) { - using namespace ramses_internal; + using namespace ramses::internal; File file1(fileName1); EXPECT_TRUE(file1.open(File::Mode::ReadOnlyBinary)); File file2(fileName2); @@ -199,5 +203,3 @@ namespace ramses } }; } - -#endif diff --git a/client/test/CreationHelper.cpp b/tests/unittests/client/CreationHelper.cpp similarity index 61% rename from client/test/CreationHelper.cpp rename to tests/unittests/client/CreationHelper.cpp index 19e6a8285..e481c3aa3 100644 --- a/client/test/CreationHelper.cpp +++ b/tests/unittests/client/CreationHelper.cpp @@ -9,29 +9,30 @@ #include "gtest/gtest.h" #include "CreationHelper.h" #include "TestEffects.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/EffectInputSemantic.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/PerspectiveCamera.h" - -namespace ramses +#include "ramses/client/Node.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Effect.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/EffectInputSemantic.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/Camera.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/logic/LogicEngine.h" + +namespace ramses::internal { - CreationHelper::CreationHelper(Scene* scene, RamsesClient* ramsesClient) + CreationHelper::CreationHelper(ramses::Scene* scene, RamsesClient* ramsesClient) : m_scene(scene) , m_ramsesClient(ramsesClient) { @@ -51,7 +52,7 @@ namespace ramses } } - void CreationHelper::setScene(Scene* scene) + void CreationHelper::setScene(ramses::Scene* scene) { m_scene = scene; } @@ -61,7 +62,7 @@ namespace ramses assert(m_scene != nullptr); for(const auto& obj : m_additionalAllocatedSceneObjects) { - ASSERT_TRUE(m_scene->destroy(*obj) == StatusOK); + ASSERT_TRUE(m_scene->destroy(*obj)); } m_additionalAllocatedSceneObjects.clear(); } @@ -80,15 +81,19 @@ namespace ramses template <> RamsesClient* CreationHelper::createObjectOfType(std::string_view name) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; - RamsesFramework* framework = new RamsesFramework(config); + auto* framework = new RamsesFramework(config); RamsesClient* allocatedClient = framework->createClient(name); if (allocatedClient) m_allocatedClientAndFrameworkComponents.push_back(ClientAndFramework(allocatedClient, framework)); return allocatedClient; } - template <> Scene* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::Scene* CreationHelper::createObjectOfType(std::string_view name) { - return m_ramsesClient->createScene(sceneId_t(999u), ramses::SceneConfig(), name); + return m_ramsesClient->createScene(sceneId_t(999u), name); + } + template <> LogicEngine* CreationHelper::createObjectOfType(std::string_view name) + { + return m_scene->createLogicEngine(name); } template <> Node* CreationHelper::createObjectOfType(std::string_view name) { @@ -111,7 +116,7 @@ namespace ramses EffectDescription effectDescription; effectDescription.setVertexShader("void main(void) {gl_Position=vec4(0);}"); effectDescription.setFragmentShader("void main(void) {gl_FragColor=vec4(1);}"); - return m_scene->createEffect(effectDescription, ResourceCacheFlag_DoNotCache, name); + return m_scene->createEffect(effectDescription, name); } template <> Appearance* CreationHelper::createObjectOfType(std::string_view name) { @@ -121,78 +126,82 @@ namespace ramses { std::array data = { 0u }; MipLevelData mipLevelData(static_cast(data.size()), data.data()); - return m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ResourceCacheFlag_DoNotCache, name); + return m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, name); } template <> Texture3D* CreationHelper::createObjectOfType(std::string_view name) { std::array data = { 0u }; MipLevelData mipLevelData(static_cast(data.size()), data.data()); - return m_scene->createTexture3D(ETextureFormat::RGBA8, 1u, 2u, 4u, 1, &mipLevelData, false, ResourceCacheFlag_DoNotCache, name); + return m_scene->createTexture3D(ETextureFormat::RGBA8, 1u, 2u, 4u, 1, &mipLevelData, false, name); } template <> TextureCube* CreationHelper::createObjectOfType(std::string_view name) { - uint8_t data[4] = { 0u }; // NOLINT(modernize-avoid-c-arrays) + std::byte data[4] = { std::byte{0u} }; // NOLINT(modernize-avoid-c-arrays) CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - return m_scene->createTextureCube(ETextureFormat::RGBA8, 1u, 1, &mipLevelData, false, {}, ResourceCacheFlag_DoNotCache, name); + return m_scene->createTextureCube(ETextureFormat::RGBA8, 1u, 1, &mipLevelData, false, {}, name); } template <> ArrayResource* CreationHelper::createObjectOfType(std::string_view name) { const uint16_t data = 0u; - return m_scene->createArrayResource(1u, &data, ResourceCacheFlag_DoNotCache, name); + return m_scene->createArrayResource(1u, &data, name); } - template <> RenderGroup* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::RenderGroup* CreationHelper::createObjectOfType(std::string_view name) { return m_scene->createRenderGroup(name); } - template <> RenderPass* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::RenderPass* CreationHelper::createObjectOfType(std::string_view name) { return m_scene->createRenderPass(name); } - template <> BlitPass* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::BlitPass* CreationHelper::createObjectOfType(std::string_view name) { - const RenderBuffer* sourceRenderBuffer = createObjectOfType("src render buffer"); - const RenderBuffer* destinationRenderBuffer = createObjectOfType("dst render buffer"); + const ramses::RenderBuffer* sourceRenderBuffer = createObjectOfType("src render buffer"); + const ramses::RenderBuffer* destinationRenderBuffer = createObjectOfType("dst render buffer"); return m_scene->createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, name); } - template <> TextureSampler* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::TextureSampler* CreationHelper::createObjectOfType(std::string_view name) { std::array data = { 0u }; MipLevelData mipLevelData(static_cast(data.size()), data.data()); - Texture2D* texture = m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ResourceCacheFlag_DoNotCache, "texture"); + Texture2D* texture = m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "texture"); return m_scene->createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Nearest, *texture, 1u, name); } template <> TextureSamplerMS* CreationHelper::createObjectOfType(std::string_view name) { - RenderBuffer* renderBuffer = m_scene->createRenderBuffer(16, 16, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u, "renderBuffer"); + ramses::RenderBuffer* renderBuffer = m_scene->createRenderBuffer(16, 16, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u, "renderBuffer"); return m_scene->createTextureSamplerMS(*renderBuffer, name); } - template <> RenderBuffer* CreationHelper::createObjectOfType(std::string_view name) + template <> TextureSamplerExternal* CreationHelper::createObjectOfType(std::string_view name) + { + return m_scene->createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, name); + } + template <> ramses::RenderBuffer* CreationHelper::createObjectOfType(std::string_view name) { - return m_scene->createRenderBuffer(16, 16, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u, name); + return m_scene->createRenderBuffer(16, 16, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u, name); } template <> RenderTarget* CreationHelper::createObjectOfType(std::string_view name) { RenderTargetDescription rtDesc; - rtDesc.addRenderBuffer(*createObjectOfType("rb")); + rtDesc.addRenderBuffer(*createObjectOfType("rb")); return m_scene->createRenderTarget(rtDesc, name); } - template <> GeometryBinding* CreationHelper::createObjectOfType(std::string_view name) + template <> Geometry* CreationHelper::createObjectOfType(std::string_view name) { - return m_scene->createGeometryBinding(*createObjectOfType("geometry_binding_effect"), name); + return m_scene->createGeometry(*createObjectOfType("geometry_binding_effect"), name); } template <> DataObject* CreationHelper::createObjectOfType(std::string_view name) { - return m_scene->createDataObject(EDataType::Float, name); + return m_scene->createDataObject(ramses::EDataType::Float, name); } template <> ArrayBuffer* CreationHelper::createObjectOfType(std::string_view name) { - return m_scene->createArrayBuffer(EDataType::UInt32, 13u, name); + return m_scene->createArrayBuffer(ramses::EDataType::UInt32, 13u, name); } template <> Texture2DBuffer* CreationHelper::createObjectOfType(std::string_view name) @@ -200,20 +209,20 @@ namespace ramses return m_scene->createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2, name); } - template <> PickableObject* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::PickableObject* CreationHelper::createObjectOfType(std::string_view name) { - const auto vb = m_scene->createArrayBuffer(EDataType::Vector3F, 3u, "vb"); + const auto vb = m_scene->createArrayBuffer(ramses::EDataType::Vector3F, 3u, "vb"); m_additionalAllocatedSceneObjects.push_back(vb); PerspectiveCamera* camera = m_scene->createPerspectiveCamera("pickableCamera"); m_additionalAllocatedSceneObjects.push_back(camera); camera->setFrustum(-1.4f, 1.4f, -1.4f, 1.4f, 1.f, 100.f); camera->setViewport(0, 0, 200, 200); - PickableObject* pickableObject = m_scene->createPickableObject(*vb, pickableObjectId_t{ 123u }, name); + ramses::PickableObject* pickableObject = m_scene->createPickableObject(*vb, pickableObjectId_t{ 123u }, name); pickableObject->setCamera(*camera); return pickableObject; } - template <> SceneReference* CreationHelper::createObjectOfType(std::string_view name) + template <> ramses::SceneReference* CreationHelper::createObjectOfType(std::string_view name) { // SceneReference cannot refer to same sceneID, create a different one every time ++m_lastReferencedSceneId.getReference(); diff --git a/tests/unittests/client/CreationHelper.h b/tests/unittests/client/CreationHelper.h new file mode 100644 index 000000000..710e8a981 --- /dev/null +++ b/tests/unittests/client/CreationHelper.h @@ -0,0 +1,113 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" + +#include +#include +#include + +namespace ramses +{ + class RamsesObject; + class SceneObject; + class RamsesClient; + class RamsesFramework; + class TextureCubeInput; + class Scene; + class Node; + class MeshNode; + class PerspectiveCamera; + class OrthographicCamera; + class Effect; + class Appearance; + class Texture2D; + class Texture3D; + class TextureCube; + class ArrayResource; + class RenderGroup; + class RenderPass; + class BlitPass; + class TextureSampler; + class TextureSamplerMS; + class TextureSamplerExternal; + class RenderBuffer; + class RenderTarget; + class RenderGroup; + class ArrayBuffer; + class Texture2DBuffer; + class Geometry; + class DataObject; + class PickableObject; + class SceneReference; + class LogicEngine; +} + +namespace ramses::internal +{ + class CreationHelper + { + public: + CreationHelper(ramses::Scene* scene, RamsesClient* ramsesClient); + ~CreationHelper(); + + void setScene(ramses::Scene* scene); + + template + ObjectType* createObjectOfType([[maybe_unused]] std::string_view name) + { + assert(false); + return NULL; + } + + void destroyAdditionalAllocatedSceneObjects(); + [[nodiscard]] size_t getAdditionalAllocatedNodeCount() const; + + private: + ramses::Scene* m_scene; + RamsesClient* m_ramsesClient; + + using ClientAndFramework = std::pair; + using RamsesClientAndFrameworkComponentVector = std::vector; + RamsesClientAndFrameworkComponentVector m_allocatedClientAndFrameworkComponents; + std::vector m_additionalAllocatedSceneObjects; + sceneId_t m_lastReferencedSceneId{ 123u }; + }; + + template <> ramses::RamsesClient* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Scene* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::LogicEngine* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Node* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::MeshNode* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::PerspectiveCamera* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::OrthographicCamera* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Effect* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Appearance* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Texture2D* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Texture3D* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::TextureCube* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::ArrayResource* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::RenderGroup* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::RenderPass* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::BlitPass* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::TextureSampler* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::TextureSamplerMS* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::TextureSamplerExternal* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::RenderBuffer* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::RenderTarget* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Geometry* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::DataObject* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::ArrayBuffer* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::Texture2DBuffer* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::PickableObject* CreationHelper::createObjectOfType(std::string_view name); + template <> ramses::SceneReference* CreationHelper::createObjectOfType(std::string_view name); +} diff --git a/client/test/DataObjectTest.cpp b/tests/unittests/client/DataObjectTest.cpp similarity index 70% rename from client/test/DataObjectTest.cpp rename to tests/unittests/client/DataObjectTest.cpp index d35e9f222..03e282e48 100644 --- a/client/test/DataObjectTest.cpp +++ b/tests/unittests/client/DataObjectTest.cpp @@ -9,14 +9,13 @@ #include #include "ClientTestUtils.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-utils.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/ramses-utils.h" #include "glm/gtx/range.hpp" using namespace testing; -using namespace ramses_internal; -namespace ramses +namespace ramses::internal { template class ADataObject : public LocalTestClientWithScene, public testing::Test @@ -25,6 +24,7 @@ namespace ramses template T SomeValue(); + template <> bool SomeValue() { return true; } template <> int32_t SomeValue() { return 1; } template <> float SomeValue() { return 2.f; } template <> vec2f SomeValue() { return vec2f{ 1.f, 2.f }; } @@ -38,6 +38,7 @@ namespace ramses template <> matrix44f SomeValue() { matrix44f ret; std::iota(begin(ret), end(ret), 1.f); return ret; } using DataTypes = ::testing::Types< + bool, int32_t, float, vec2f, @@ -65,7 +66,7 @@ namespace ramses ASSERT_TRUE(dataObject); TypeParam getVal; - EXPECT_EQ(StatusOK, dataObject->getValue(getVal)); + EXPECT_TRUE(dataObject->getValue(getVal)); EXPECT_EQ(TypeParam{}, getVal); } @@ -74,10 +75,10 @@ namespace ramses const auto dataObject = this->m_scene.createDataObject(GetEDataType(), "data"); ASSERT_TRUE(dataObject); - EXPECT_EQ(StatusOK, dataObject->setValue(SomeValue())); + EXPECT_TRUE(dataObject->setValue(SomeValue())); TypeParam getVal; - EXPECT_EQ(StatusOK, dataObject->getValue(getVal)); + EXPECT_TRUE(dataObject->getValue(getVal)); EXPECT_EQ(SomeValue(), getVal); } @@ -88,15 +89,15 @@ namespace ramses if constexpr (std::is_same_v) { - EXPECT_NE(StatusOK, dataObject->setValue(SomeValue())); - float getVal; - EXPECT_NE(StatusOK, dataObject->getValue(getVal)); + EXPECT_FALSE(dataObject->setValue(SomeValue())); + float getVal = NAN; + EXPECT_FALSE(dataObject->getValue(getVal)); } else { - EXPECT_NE(StatusOK, dataObject->setValue(SomeValue())); - int32_t getVal; - EXPECT_NE(StatusOK, dataObject->getValue(getVal)); + EXPECT_FALSE(dataObject->setValue(SomeValue())); + int32_t getVal = 0; + EXPECT_FALSE(dataObject->getValue(getVal)); } } @@ -105,13 +106,13 @@ namespace ramses RamsesFramework framework{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; auto client = framework.createClient("test"); auto scene = client->createScene(sceneId_t{ 123 }); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::UInt16)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::UInt32)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::ByteBlob)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::TextureSampler2D)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::TextureSampler2DMS)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::TextureSampler3D)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::TextureSamplerCube)); - EXPECT_EQ(nullptr, scene->createDataObject(EDataType::TextureSamplerExternal)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::UInt16)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::UInt32)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::ByteBlob)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::TextureSampler2D)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::TextureSampler2DMS)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::TextureSampler3D)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::TextureSamplerCube)); + EXPECT_EQ(nullptr, scene->createDataObject(ramses::EDataType::TextureSamplerExternal)); } } diff --git a/client/test/EffectDescriptionTest.cpp b/tests/unittests/client/EffectDescriptionTest.cpp similarity index 69% rename from client/test/EffectDescriptionTest.cpp rename to tests/unittests/client/EffectDescriptionTest.cpp index d7f71df14..1314c087f 100644 --- a/client/test/EffectDescriptionTest.cpp +++ b/tests/unittests/client/EffectDescriptionTest.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include -#include "ramses-client-api/EffectDescription.h" -#include "EffectDescriptionImpl.h" -#include "EffectInputSemanticUtils.h" +#include "ramses/client/EffectDescription.h" +#include "impl/EffectDescriptionImpl.h" +#include "impl/EffectInputSemanticUtils.h" #include using namespace testing; -namespace ramses +namespace ramses::internal { class EffectDescriptionTest : public testing::Test { @@ -23,7 +23,7 @@ namespace ramses protected: EEffectUniformSemantic getSemanticForUniform(std::string_view inputName) { - ramses_internal::EFixedSemantics* internalSemantic = effectDesc.m_impl.get().getSemanticsMap().get(std::string{inputName}); + EFixedSemantics* internalSemantic = effectDesc.impl().getSemanticsMap().get(std::string{inputName}); if (internalSemantic == nullptr) { return EEffectUniformSemantic::Invalid; @@ -34,7 +34,7 @@ namespace ramses EEffectAttributeSemantic getSemanticForAttribute(std::string_view inputName) { - ramses_internal::EFixedSemantics* internalSemantic = effectDesc.m_impl.get().getSemanticsMap().get(std::string{inputName}); + EFixedSemantics* internalSemantic = effectDesc.impl().getSemanticsMap().get(std::string{inputName}); if (internalSemantic == nullptr) { return EEffectAttributeSemantic::Invalid; @@ -52,55 +52,55 @@ namespace ramses EXPECT_STREQ("", effectDesc.getFragmentShader()); EXPECT_STREQ("", effectDesc.getGeometryShader()); EXPECT_EQ(0u, effectDesc.getNumberOfCompilerDefines()); - EXPECT_EQ(0u, effectDesc.m_impl.get().getSemanticsMap().size()); + EXPECT_EQ(0u, effectDesc.impl().getSemanticsMap().size()); } TEST_F(EffectDescriptionTest, setShaderWithNULLRetrievesEmptyString) { - EXPECT_EQ(StatusOK, effectDesc.setVertexShader({})); + EXPECT_TRUE(effectDesc.setVertexShader({})); EXPECT_STREQ("", effectDesc.getVertexShader()); - EXPECT_EQ(StatusOK, effectDesc.setFragmentShader({})); + EXPECT_TRUE(effectDesc.setFragmentShader({})); EXPECT_STREQ("", effectDesc.getFragmentShader()); - EXPECT_EQ(StatusOK, effectDesc.setGeometryShader({})); + EXPECT_TRUE(effectDesc.setGeometryShader({})); EXPECT_STREQ("", effectDesc.getGeometryShader()); } TEST_F(EffectDescriptionTest, retrievesSetVertexShader) { const char* src = "code"; - EXPECT_EQ(StatusOK, effectDesc.setVertexShader(src)); + EXPECT_TRUE(effectDesc.setVertexShader(src)); EXPECT_STREQ(src, effectDesc.getVertexShader()); const char* src2 = "bad code"; - EXPECT_EQ(StatusOK, effectDesc.setVertexShader(src2)); + EXPECT_TRUE(effectDesc.setVertexShader(src2)); EXPECT_STREQ(src2, effectDesc.getVertexShader()); } TEST_F(EffectDescriptionTest, retrievesSetFragmentShader) { const char* src = "code"; - EXPECT_EQ(StatusOK, effectDesc.setFragmentShader(src)); + EXPECT_TRUE(effectDesc.setFragmentShader(src)); EXPECT_STREQ(src, effectDesc.getFragmentShader()); const char* src2 = "bad code"; - EXPECT_EQ(StatusOK, effectDesc.setFragmentShader(src2)); + EXPECT_TRUE(effectDesc.setFragmentShader(src2)); EXPECT_STREQ(src2, effectDesc.getFragmentShader()); } TEST_F(EffectDescriptionTest, retrievesSetGeometryShader) { const char* src = "code"; - EXPECT_EQ(StatusOK, effectDesc.setGeometryShader(src)); + EXPECT_TRUE(effectDesc.setGeometryShader(src)); EXPECT_STREQ(src, effectDesc.getGeometryShader()); const char* src2 = "bad code"; - EXPECT_EQ(StatusOK, effectDesc.setGeometryShader(src2)); + EXPECT_TRUE(effectDesc.setGeometryShader(src2)); EXPECT_STREQ(src2, effectDesc.getGeometryShader()); } TEST_F(EffectDescriptionTest, addDefineAsNULLReportsError) { - EXPECT_NE(StatusOK, effectDesc.addCompilerDefine({})); + EXPECT_FALSE(effectDesc.addCompilerDefine({})); EXPECT_EQ(nullptr, effectDesc.getCompilerDefine(0u)); EXPECT_EQ(0u, effectDesc.getNumberOfCompilerDefines()); } @@ -110,11 +110,11 @@ namespace ramses const char* def1 = "my define"; const char* def2 = "my useless define"; - EXPECT_EQ(StatusOK, effectDesc.addCompilerDefine(def1)); + EXPECT_TRUE(effectDesc.addCompilerDefine(def1)); EXPECT_STREQ(def1, effectDesc.getCompilerDefine(0u)); EXPECT_EQ(1u, effectDesc.getNumberOfCompilerDefines()); - EXPECT_EQ(StatusOK, effectDesc.addCompilerDefine(def2)); + EXPECT_TRUE(effectDesc.addCompilerDefine(def2)); EXPECT_STREQ(def1, effectDesc.getCompilerDefine(0u)); EXPECT_STREQ(def2, effectDesc.getCompilerDefine(1u)); EXPECT_EQ(2u, effectDesc.getNumberOfCompilerDefines()); @@ -122,8 +122,8 @@ namespace ramses TEST_F(EffectDescriptionTest, addSemanticNameAsNULLReportsError) { - EXPECT_NE(StatusOK, effectDesc.setUniformSemantic({}, EEffectUniformSemantic::ViewMatrix)); - EXPECT_EQ(0u, effectDesc.m_impl.get().getSemanticsMap().size()); + EXPECT_FALSE(effectDesc.setUniformSemantic({}, EEffectUniformSemantic::ViewMatrix)); + EXPECT_EQ(0u, effectDesc.impl().getSemanticsMap().size()); } TEST_F(EffectDescriptionTest, canAddAndRetrieveSemanticInput) @@ -135,20 +135,20 @@ namespace ramses const EEffectUniformSemantic semanticType2 = EEffectUniformSemantic::ViewMatrix; const EEffectAttributeSemantic semanticType3 = EEffectAttributeSemantic::TextPositions; - EXPECT_EQ(StatusOK, effectDesc.setUniformSemantic(semantic1, semanticType1)); + EXPECT_TRUE(effectDesc.setUniformSemantic(semantic1, semanticType1)); EXPECT_EQ(semanticType1, getSemanticForUniform(semantic1)); - EXPECT_EQ(1u, effectDesc.m_impl.get().getSemanticsMap().size()); + EXPECT_EQ(1u, effectDesc.impl().getSemanticsMap().size()); - EXPECT_EQ(StatusOK, effectDesc.setUniformSemantic(semantic2, semanticType2)); + EXPECT_TRUE(effectDesc.setUniformSemantic(semantic2, semanticType2)); EXPECT_EQ(semanticType1, getSemanticForUniform(semantic1)); EXPECT_EQ(semanticType2, getSemanticForUniform(semantic2)); - EXPECT_EQ(2u, effectDesc.m_impl.get().getSemanticsMap().size()); + EXPECT_EQ(2u, effectDesc.impl().getSemanticsMap().size()); - EXPECT_EQ(StatusOK, effectDesc.setAttributeSemantic(semantic3, semanticType3)); + EXPECT_TRUE(effectDesc.setAttributeSemantic(semantic3, semanticType3)); EXPECT_EQ(semanticType1, getSemanticForUniform(semantic1)); EXPECT_EQ(semanticType2, getSemanticForUniform(semantic2)); EXPECT_EQ(semanticType3, getSemanticForAttribute(semantic3)); - EXPECT_EQ(3u, effectDesc.m_impl.get().getSemanticsMap().size()); + EXPECT_EQ(3u, effectDesc.impl().getSemanticsMap().size()); } TEST_F(EffectDescriptionTest, retrievesUnknownTypeForSemanticNotAdded) @@ -160,35 +160,35 @@ namespace ramses TEST_F(EffectDescriptionTest, setVertexShaderFromFile) { const char* fileName = "res/ramses-client-test_minimalShader.vert"; - EXPECT_EQ(StatusOK, effectDesc.setVertexShaderFromFile(fileName)); + EXPECT_TRUE(effectDesc.setVertexShaderFromFile(fileName)); } TEST_F(EffectDescriptionTest, setFragmentShaderFromFile) { const char* fileName = "res/ramses-client-test_minimalShader.frag"; - EXPECT_EQ(StatusOK, effectDesc.setFragmentShaderFromFile(fileName)); + EXPECT_TRUE(effectDesc.setFragmentShaderFromFile(fileName)); } TEST_F(EffectDescriptionTest, setGeometryShaderFromFile) { const char* fileName = "res/ramses-client-test_minimalShader.geom"; - EXPECT_EQ(StatusOK, effectDesc.setGeometryShaderFromFile(fileName)); + EXPECT_TRUE(effectDesc.setGeometryShaderFromFile(fileName)); } TEST_F(EffectDescriptionTest, reportsErrorWhenLoadingNonexistingFile) { const auto fileName = "nofile%&"; - EXPECT_NE(StatusOK, effectDesc.setVertexShaderFromFile(fileName)); - EXPECT_NE(StatusOK, effectDesc.setFragmentShaderFromFile(fileName)); - EXPECT_NE(StatusOK, effectDesc.setFragmentShaderFromFile(fileName)); - EXPECT_NE(StatusOK, effectDesc.setVertexShaderFromFile({})); - EXPECT_NE(StatusOK, effectDesc.setFragmentShaderFromFile({})); - EXPECT_NE(StatusOK, effectDesc.setFragmentShaderFromFile({})); + EXPECT_FALSE(effectDesc.setVertexShaderFromFile(fileName)); + EXPECT_FALSE(effectDesc.setFragmentShaderFromFile(fileName)); + EXPECT_FALSE(effectDesc.setFragmentShaderFromFile(fileName)); + EXPECT_FALSE(effectDesc.setVertexShaderFromFile({})); + EXPECT_FALSE(effectDesc.setFragmentShaderFromFile({})); + EXPECT_FALSE(effectDesc.setFragmentShaderFromFile({})); } TEST_F(EffectDescriptionTest, CanBeCopyAndMoveConstructed) { - ASSERT_EQ(StatusOK, effectDesc.setVertexShader("test")); + ASSERT_TRUE(effectDesc.setVertexShader("test")); EffectDescription effectDescCopy{ effectDesc }; EXPECT_STREQ("test", effectDescCopy.getVertexShader()); @@ -199,7 +199,7 @@ namespace ramses TEST_F(EffectDescriptionTest, CanBeCopyAndMoveAssigned) { - ASSERT_EQ(StatusOK, effectDesc.setVertexShader("test")); + ASSERT_TRUE(effectDesc.setVertexShader("test")); EffectDescription effectDescCopy; effectDescCopy = effectDesc; @@ -212,7 +212,7 @@ namespace ramses TEST_F(EffectDescriptionTest, CanBeSelfAssigned) { - ASSERT_EQ(StatusOK, effectDesc.setVertexShader("test")); + ASSERT_TRUE(effectDesc.setVertexShader("test")); #ifdef __clang__ #pragma clang diagnostic push diff --git a/tests/unittests/client/EffectInputTest.cpp b/tests/unittests/client/EffectInputTest.cpp new file mode 100644 index 000000000..04507ef58 --- /dev/null +++ b/tests/unittests/client/EffectInputTest.cpp @@ -0,0 +1,252 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "TestEffectCreator.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" +#include "impl/EffectImpl.h" +#include "impl/EffectInputImpl.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" + +using namespace testing; + +namespace ramses::internal +{ + class AnEffectInput : public ::testing::Test + { + public: + static void SetUpTestSuite() + { + sharedTestState = std::make_unique(true); + } + + static void TearDownTestSuite() + { + sharedTestState = nullptr; + } + + void SetUp() override + { + EXPECT_TRUE(sharedTestState != nullptr); + } + + protected: + static void CompareInput(const EffectInput& input1, const EffectInput& input2) + { + EXPECT_STREQ(input1.getName(), input2.getName()); + EXPECT_EQ(input1.getDataType(), input2.getDataType()); + EXPECT_EQ(input1.impl().getSemantics(), input2.impl().getSemantics()); + EXPECT_EQ(input1.impl().getElementCount(), input2.impl().getElementCount()); + EXPECT_EQ(input1.impl().getInputIndex(), input2.impl().getInputIndex()); + EXPECT_EQ(input1.impl().getEffectHash(), input2.impl().getEffectHash()); + } + + static std::unique_ptr sharedTestState; + }; + + std::unique_ptr AnEffectInput::sharedTestState; + + TEST_F(AnEffectInput, InitializedToDefaultUponCreation) + { + EffectInputImpl input; + EXPECT_EQ("", input.getName()); + EXPECT_EQ(0u, input.getElementCount()); + EXPECT_EQ(ramses::internal::ResourceContentHash::Invalid(), input.getEffectHash()); + EXPECT_EQ(ramses::internal::EDataType::Invalid, input.getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::Invalid, input.getSemantics()); + EXPECT_EQ(static_cast(-1), input.getInputIndex()); + } + + TEST_F(AnEffectInput, UniformInputIsInitializedToGivenValues) + { + const Effect* effect = sharedTestState->effect; + UniformInput input{ *effect->findUniformInput("texture2dInput") }; + + EXPECT_STREQ("texture2dInput", input.getName()); + EXPECT_EQ(1u, input.getElementCount()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), input.impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::TextureSampler2D, input.getDataType()); + EXPECT_EQ(ramses::internal::EDataType::TextureSampler2D, input.impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::TextTexture, input.impl().getSemantics()); + EXPECT_EQ(EEffectUniformSemantic::TextTexture, input.getSemantics()); + EXPECT_EQ(24u, input.impl().getInputIndex()); + } + + TEST_F(AnEffectInput, AttributeInputIsInitializedToGivenValues) + { + const Effect* effect = sharedTestState->effect; + AttributeInput input{ *effect->getAttributeInput(1u) }; + + EXPECT_STREQ("vec2fArrayInput", input.getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), input.impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector2F, input.getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector2Buffer, input.impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::TextPositionsAttribute, input.impl().getSemantics()); + EXPECT_EQ(EEffectAttributeSemantic::TextPositions, input.getSemantics()); + EXPECT_EQ(1u, input.impl().getInputIndex()); + } + + TEST_F(AnEffectInput, ReturnsCorrectDataType) + { + const ramses::internal::ResourceContentHash effectHash(1u, 0); + const std::string inputName("test"); + const ramses::internal::EFixedSemantics semantics = ramses::internal::EFixedSemantics::ModelMatrix; + const size_t index = 66u; + EffectInputImpl input; + + input.initialize(effectHash, inputName, ramses::internal::EDataType::UInt16, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::UInt16); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::UInt32, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::UInt32); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Bool, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Bool); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Int32, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Int32); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Vector2I, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Vector2I); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Vector3I, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Vector3I); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Vector4I, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Vector4I); + + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Float, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Float); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Vector2F, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Vector2F); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Vector3F, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Vector3F); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Vector4F, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Vector4F); + + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Matrix22F, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Matrix22F); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Matrix33F, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Matrix33F); + + input.initialize(effectHash, inputName, ramses::internal::EDataType::Matrix44F, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::Matrix44F); + + + input.initialize(effectHash, inputName, ramses::internal::EDataType::TextureSampler2D, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::TextureSampler2D); + input.initialize(effectHash, inputName, ramses::internal::EDataType::TextureSampler2DMS, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::TextureSampler2DMS); + input.initialize(effectHash, inputName, ramses::internal::EDataType::TextureSampler3D, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::TextureSampler3D); + input.initialize(effectHash, inputName, ramses::internal::EDataType::TextureSamplerCube, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::TextureSamplerCube); + input.initialize(effectHash, inputName, ramses::internal::EDataType::TextureSamplerExternal, semantics, 1u, index); + EXPECT_EQ(input.getDataType(), ramses::EDataType::TextureSamplerExternal); + } + + TEST_F(AnEffectInput, CanBeCopyAndMoveConstructed_Uniform) + { + const auto creference = sharedTestState->effect->getUniformInput(5u); + UniformInput inputCopy{*creference}; + CompareInput(inputCopy, *creference); + + auto reference = sharedTestState->effect->getUniformInput(5u); + UniformInput inputMove{std::move(*reference)}; + CompareInput(inputMove, *creference); + } + + TEST_F(AnEffectInput, CanBeCopyAndMoveAssigned_Uniform) + { + const auto creference = sharedTestState->effect->getUniformInput(4u); + UniformInput inputCopy{*sharedTestState->effect->getUniformInput(5u)}; + inputCopy = *creference; + CompareInput(inputCopy, *creference); + + auto reference = sharedTestState->effect->getUniformInput(4u); + UniformInput inputMove{std::move(*sharedTestState->effect->getUniformInput(5u))}; + inputMove = std::move(*reference); + CompareInput(inputMove, *creference); + } + + TEST_F(AnEffectInput, CanBeSelfAssigned_Uniform) + { + const auto reference = sharedTestState->effect->getUniformInput(5u); + UniformInput input{*reference}; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + input = input; + CompareInput(input, *reference); + + input = std::move(input); + // NOLINTNEXTLINE(bugprone-use-after-move) + CompareInput(input, *reference); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + TEST_F(AnEffectInput, CanBeCopyAndMoveConstructed_Attribute) + { + const auto creference = sharedTestState->effect->getAttributeInput(1u); + AttributeInput inputCopy{*creference}; + CompareInput(inputCopy, *creference); + + auto reference = sharedTestState->effect->getAttributeInput(1u); + AttributeInput inputMove{std::move(*reference)}; + CompareInput(inputMove, *creference); + } + + TEST_F(AnEffectInput, CanBeCopyAndMoveAssigned_Attribute) + { + const auto creference = sharedTestState->effect->getAttributeInput(1u); + AttributeInput inputCopy{*sharedTestState->effect->getAttributeInput(2u)}; + inputCopy = *creference; + CompareInput(inputCopy, *creference); + + auto reference = sharedTestState->effect->getAttributeInput(1u); + AttributeInput inputMove{*sharedTestState->effect->getAttributeInput(2u)}; + inputMove = std::move(*reference); + CompareInput(inputMove, *creference); + } + + TEST_F(AnEffectInput, CanBeSelfAssigned_Attribute) + { + const auto reference = sharedTestState->effect->getAttributeInput(1u); + AttributeInput input{*reference}; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + input = input; + CompareInput(input, *reference); + input = std::move(input); + // NOLINTNEXTLINE(bugprone-use-after-move) + CompareInput(input, *reference); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } +} diff --git a/client/test/EffectTest.cpp b/tests/unittests/client/EffectTest.cpp similarity index 55% rename from client/test/EffectTest.cpp rename to tests/unittests/client/EffectTest.cpp index 7c5915211..be52d2a7f 100644 --- a/client/test/EffectTest.cpp +++ b/tests/unittests/client/EffectTest.cpp @@ -6,28 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EffectImpl.h" +#include "impl/EffectImpl.h" #include "TestEffectCreator.h" -#include "ClientTestUtils.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/AttributeInput.h" -#include "EffectInputImpl.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/AttributeInput.h" +#include "impl/EffectInputImpl.h" #include "gtest/gtest.h" using namespace testing; -namespace ramses +namespace ramses::internal { class AnEffect : public ::testing::Test { public: - static void SetUpTestCase() + static void SetUpTestSuite() { sharedTestState = std::make_unique(); } - static void TearDownTestCase() + static void TearDownTestSuite() { sharedTestState = nullptr; } @@ -45,7 +44,7 @@ namespace ramses class AnEffectWithSemantics : public AnEffect { public: - static void SetUpTestCase() + static void SetUpTestSuite() { sharedTestState = std::make_unique(true); } @@ -54,7 +53,7 @@ namespace ramses TEST_F(AnEffect, hasProperNumberOfInputs) { const Effect* effect = sharedTestState->effect; - EXPECT_EQ(27u, effect->getUniformInputCount()); + EXPECT_EQ(29u, effect->getUniformInputCount()); EXPECT_EQ(4u, effect->getAttributeInputCount()); } @@ -66,222 +65,203 @@ namespace ramses TEST_F(AnEffect, reportsErrorWhenAskedForGeometryInputType_ButNoGeometryShaderProvided) { EDrawMode mode; - EXPECT_NE(StatusOK, sharedTestState->effect->getGeometryShaderInputType(mode)); + EXPECT_FALSE(sharedTestState->effect->getGeometryShaderInputType(mode)); } TEST_F(AnEffect, reportsErrorWhenAskingForNonExistingUniformInput) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_NE(StatusOK, effect->getUniformInput(99u, input)); - EXPECT_FALSE(input.isValid()); + const std::optional optInput = effect->getUniformInput(99u); + EXPECT_FALSE(optInput.has_value()); } TEST_F(AnEffect, reportsErrorWhenAskingForNonExistingAttributeInput) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_NE(StatusOK, effect->getAttributeInput(99u, input)); - EXPECT_FALSE(input.isValid()); + const std::optional optInput = effect->getAttributeInput(99u); + EXPECT_FALSE(optInput.has_value()); } TEST_F(AnEffect, reportsErrorWhenAskingForNonExistingUniformName) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_NE(StatusOK, effect->findUniformInput("xxx", input)); - EXPECT_FALSE(input.isValid()); + const std::optional optInput = effect->findUniformInput("xxx"); + EXPECT_FALSE(optInput.has_value()); } TEST_F(AnEffect, reportsErrorWhenAskingForNonExistingAttributeName) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_NE(StatusOK, effect->findAttributeInput("xxx", input)); - EXPECT_FALSE(input.isValid()); + const std::optional optInput = effect->findAttributeInput("xxx"); + EXPECT_FALSE(optInput.has_value()); } TEST_F(AnEffect, reportsErrorWhenAskingForNonExistingUniformSemantic) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_NE(StatusOK, effect->findUniformInput(ramses::EEffectUniformSemantic::NormalMatrix, input)); - EXPECT_FALSE(input.isValid()); + const std::optional optInput = effect->findUniformInput(EEffectUniformSemantic::NormalMatrix); + EXPECT_FALSE(optInput.has_value()); } TEST_F(AnEffect, reportsErrorWhenAskingForNonExistingAttributeSemantic) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_NE(StatusOK, effect->findAttributeInput(ramses::EEffectAttributeSemantic::Invalid, input)); - EXPECT_FALSE(input.isValid()); + const std::optional optInput = effect->findAttributeInput(EEffectAttributeSemantic::Invalid); + EXPECT_FALSE(optInput.has_value()); } TEST_F(AnEffect, getsUniformInputByIndex) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_EQ(StatusOK, effect->getUniformInput(5u, input)); - - EXPECT_STREQ("vec3fInputArray", input.getName()); - EXPECT_EQ(3u, input.getElementCount()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector3F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector3F, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::Invalid, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectUniformSemantic::Invalid, input.getSemantics()); - EXPECT_EQ(5u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->getUniformInput(5u); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec3fInputArray", optInput->getName()); + EXPECT_EQ(3u, optInput->getElementCount()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector3F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector3F, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::Invalid, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectUniformSemantic::Invalid, optInput->getSemantics()); + EXPECT_EQ(5u, optInput->impl().getInputIndex()); } TEST_F(AnEffectWithSemantics, findsUniformInputBySemantic) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_EQ(StatusOK, effect->findUniformInput(ramses::EEffectUniformSemantic::ModelViewMatrix, input)); - - EXPECT_STREQ("matrix44fInput", input.getName()); - EXPECT_EQ(1u, input.getElementCount()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Matrix44F, input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Matrix44F, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::ModelViewMatrix, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectUniformSemantic::ModelViewMatrix, input.getSemantics()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->findUniformInput(EEffectUniformSemantic::ModelViewMatrix); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("matrix44fInput", optInput->getName()); + EXPECT_EQ(1u, optInput->getElementCount()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Matrix44F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Matrix44F, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::ModelViewMatrix, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectUniformSemantic::ModelViewMatrix, optInput->getSemantics()); } TEST_F(AnEffect, getsAttributeInputByIndex) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_EQ(StatusOK, effect->getAttributeInput(1u, input)); - - EXPECT_STREQ("vec2fArrayInput", input.getName()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector2F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector2Buffer, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::Invalid, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectAttributeSemantic::Invalid, input.getSemantics()); - EXPECT_EQ(1u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->getAttributeInput(1u); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec2fArrayInput", optInput->getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector2F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector2Buffer, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::Invalid, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectAttributeSemantic::Invalid, optInput->getSemantics()); + EXPECT_EQ(1u, optInput->impl().getInputIndex()); } TEST_F(AnEffectWithSemantics, getsAttributeInputByIndex) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_EQ(StatusOK, effect->getAttributeInput(1u, input)); - - EXPECT_STREQ("vec2fArrayInput", input.getName()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector2F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector2Buffer, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::TextPositionsAttribute, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectAttributeSemantic::TextPositions, input.getSemantics()); - EXPECT_EQ(1u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->getAttributeInput(1u); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec2fArrayInput", optInput->getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector2F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector2Buffer, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::TextPositionsAttribute, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectAttributeSemantic::TextPositions, optInput->getSemantics()); + EXPECT_EQ(1u, optInput->impl().getInputIndex()); } TEST_F(AnEffectWithSemantics, findsAttributeInputBySemantic) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_EQ(StatusOK, effect->findAttributeInput(ramses::EEffectAttributeSemantic::TextPositions, input)); - - EXPECT_STREQ("vec2fArrayInput", input.getName()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector2F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector2Buffer, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::TextPositionsAttribute, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectAttributeSemantic::TextPositions, input.getSemantics()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->findAttributeInput(EEffectAttributeSemantic::TextPositions); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec2fArrayInput", optInput->getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector2F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector2Buffer, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::TextPositionsAttribute, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectAttributeSemantic::TextPositions, optInput->getSemantics()); } TEST_F(AnEffect, findsUniformInputByName) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_EQ(StatusOK, effect->findUniformInput("vec3fInputArray", input)); - - EXPECT_STREQ("vec3fInputArray", input.getName()); - EXPECT_EQ(3u, input.getElementCount()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector3F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector3F, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::Invalid, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectUniformSemantic::Invalid, input.getSemantics()); - EXPECT_EQ(5u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->findUniformInput("vec3fInputArray"); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec3fInputArray", optInput->getName()); + EXPECT_EQ(3u, optInput->getElementCount()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector3F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector3F, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::Invalid, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectUniformSemantic::Invalid, optInput->getSemantics()); + EXPECT_EQ(5u, optInput->impl().getInputIndex()); } TEST_F(AnEffect, findsAllTextureTypesOfUniformInputByName) { const Effect* effect = sharedTestState->effect; - UniformInput input2d; - EXPECT_EQ(StatusOK,effect->findUniformInput("texture2dInput", input2d)); - UniformInput input3d; - EXPECT_EQ(StatusOK, effect->findUniformInput("texture3dInput", input3d)); - UniformInput inputcube; - EXPECT_EQ(StatusOK, effect->findUniformInput("textureCubeInput", inputcube)); - UniformInput inputExternalTexture; - EXPECT_EQ(StatusOK, effect->findUniformInput("textureExternalInput", inputExternalTexture)); - - EXPECT_EQ(EDataType::TextureSampler2D, input2d.getDataType()); - EXPECT_TRUE(input2d.isValid()); - EXPECT_EQ(EDataType::TextureSampler3D, input3d.getDataType()); - EXPECT_TRUE(input3d.isValid()); - EXPECT_EQ(EDataType::TextureSamplerCube, inputcube.getDataType()); - EXPECT_TRUE(inputcube.isValid()); - EXPECT_EQ(EDataType::TextureSamplerExternal, inputExternalTexture.getDataType()); - EXPECT_TRUE(inputExternalTexture.isValid()); + const std::optional optInput2d = effect->findUniformInput("texture2dInput"); + const std::optional optInput3d = effect->findUniformInput("texture3dInput"); + const std::optional optInputcube = effect->findUniformInput("textureCubeInput"); + const std::optional optInputExternalTexture = effect->findUniformInput("textureExternalInput"); + + ASSERT_TRUE(optInput2d.has_value()); + EXPECT_EQ(ramses::EDataType::TextureSampler2D, optInput2d->getDataType()); + ASSERT_TRUE(optInput3d.has_value()); + EXPECT_EQ(ramses::EDataType::TextureSampler3D, optInput3d->getDataType()); + ASSERT_TRUE(optInputcube.has_value()); + EXPECT_EQ(ramses::EDataType::TextureSamplerCube, optInputcube->getDataType()); + ASSERT_TRUE(optInputExternalTexture.has_value()); + EXPECT_EQ(ramses::EDataType::TextureSamplerExternal, optInputExternalTexture->getDataType()); } TEST_F(AnEffect, findsAttributeInputByName) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_EQ(StatusOK, effect->findAttributeInput("vec2fArrayInput", input)); - - EXPECT_STREQ("vec2fArrayInput", input.getName()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector2F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector2Buffer, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::Invalid, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectAttributeSemantic::Invalid, input.getSemantics()); - EXPECT_EQ(1u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec2fArrayInput", optInput->getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector2F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector2Buffer, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::Invalid, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectAttributeSemantic::Invalid, optInput->getSemantics()); + EXPECT_EQ(1u, optInput->impl().getInputIndex()); } TEST_F(AnEffectWithSemantics, findsAttributeInputByName) { const Effect* effect = sharedTestState->effect; - AttributeInput input; - EXPECT_EQ(StatusOK, effect->findAttributeInput("vec2fArrayInput", input)); - - EXPECT_STREQ("vec2fArrayInput", input.getName()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::Vector2F, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::Vector2Buffer, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::TextPositionsAttribute, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectAttributeSemantic::TextPositions, input.getSemantics()); - EXPECT_EQ(1u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("vec2fArrayInput", optInput->getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::Vector2F, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::Vector2Buffer, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::TextPositionsAttribute, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectAttributeSemantic::TextPositions, optInput->getSemantics()); + EXPECT_EQ(1u, optInput->impl().getInputIndex()); } TEST_F(AnEffectWithSemantics, findsTextureInputWithSemantics) { const Effect* effect = sharedTestState->effect; - UniformInput input; - EXPECT_EQ(StatusOK, effect->findUniformInput("texture2dInput", input)); - - EXPECT_STREQ("texture2dInput", input.getName()); - EXPECT_EQ(effect->m_impl.getLowlevelResourceHash(), input.m_impl.get().getEffectHash()); - EXPECT_EQ(EDataType::TextureSampler2D, *input.getDataType()); - EXPECT_EQ(ramses_internal::EDataType::TextureSampler2D, input.m_impl.get().getInternalDataType()); - EXPECT_EQ(ramses_internal::EFixedSemantics::TextTexture, input.m_impl.get().getSemantics()); - EXPECT_EQ(EEffectUniformSemantic::TextTexture, input.getSemantics()); - EXPECT_EQ(22u, input.m_impl.get().getInputIndex()); - EXPECT_TRUE(input.isValid()); + const std::optional optInput = effect->findUniformInput("texture2dInput"); + ASSERT_TRUE(optInput.has_value()); + + EXPECT_STREQ("texture2dInput", optInput->getName()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), optInput->impl().getEffectHash()); + EXPECT_EQ(ramses::EDataType::TextureSampler2D, optInput->getDataType()); + EXPECT_EQ(ramses::internal::EDataType::TextureSampler2D, optInput->impl().getInternalDataType()); + EXPECT_EQ(ramses::internal::EFixedSemantics::TextTexture, optInput->impl().getSemantics()); + EXPECT_EQ(EEffectUniformSemantic::TextTexture, optInput->getSemantics()); + EXPECT_EQ(24u, optInput->impl().getInputIndex()); } TEST_F(AnEffect, canNotCreateEffectWhenTextPositionsSemanticsHasWrongType) @@ -302,12 +282,12 @@ namespace ramses "}"); /// Can create ... - EXPECT_NE(static_cast(nullptr), sharedTestState->getScene().m_impl.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, "")); + EXPECT_NE(static_cast(nullptr), sharedTestState->getScene().impl().createEffect(effectDesc, "")); effectDesc.setAttributeSemantic("a_position", EEffectAttributeSemantic::TextPositions); /// Can not create ... - EXPECT_EQ(static_cast(nullptr), sharedTestState->getScene().m_impl.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, "")); + EXPECT_EQ(static_cast(nullptr), sharedTestState->getScene().impl().createEffect(effectDesc, "")); } TEST_F(AnEffect, canRetrieveGLSLErrorMessageFromClient) @@ -326,7 +306,7 @@ namespace ramses "{" " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" "}"); - const Effect* effect = sharedTestState->getScene().createEffect(effectDesc, ResourceCacheFlag_DoNotCache); + const Effect* effect = sharedTestState->getScene().createEffect(effectDesc); EXPECT_EQ(nullptr, effect); using namespace ::testing; EXPECT_THAT(sharedTestState->getScene().getLastEffectErrorMessages(), @@ -353,7 +333,7 @@ namespace ramses "{" " gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);" "}"); - const Effect* effect1 = sharedTestState->getScene().createEffect(effectDesc, ResourceCacheFlag_DoNotCache); + const Effect* effect1 = sharedTestState->getScene().createEffect(effectDesc); EXPECT_EQ(nullptr, effect1); EXPECT_NE("", sharedTestState->getScene().getLastEffectErrorMessages()); @@ -364,7 +344,7 @@ namespace ramses " gl_Position = vec4(0.0)\n;" "}\n"); - const Effect* effect2 = sharedTestState->getScene().createEffect(effectDesc, ResourceCacheFlag_DoNotCache); + const Effect* effect2 = sharedTestState->getScene().createEffect(effectDesc); EXPECT_NE(nullptr, effect2); EXPECT_EQ("", sharedTestState->getScene().getLastEffectErrorMessages()); } @@ -387,12 +367,12 @@ namespace ramses "}"); /// Can create ... - EXPECT_NE(static_cast(nullptr), sharedTestState->getScene().m_impl.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, "")); + EXPECT_NE(static_cast(nullptr), sharedTestState->getScene().impl().createEffect(effectDesc, "")); effectDesc.setAttributeSemantic("a_texcoord", EEffectAttributeSemantic::TextTextureCoordinates); /// Can not create ... - EXPECT_EQ(static_cast(nullptr), sharedTestState->getScene().m_impl.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, "")); + EXPECT_EQ(static_cast(nullptr), sharedTestState->getScene().impl().createEffect(effectDesc, "")); } TEST_F(AnEffect, canNotCreateEffectWhenTextTextureSemanticsHasWrongType) @@ -413,12 +393,12 @@ namespace ramses "}"); /// Can create ... - EXPECT_NE(static_cast(nullptr), sharedTestState->getScene().m_impl.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, "")); + EXPECT_NE(static_cast(nullptr), sharedTestState->getScene().impl().createEffect(effectDesc, "")); effectDesc.setUniformSemantic("u_texture", EEffectUniformSemantic::TextTexture); /// Can not create ... - EXPECT_EQ(static_cast(nullptr), sharedTestState->getScene().m_impl.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, "")); + EXPECT_EQ(static_cast(nullptr), sharedTestState->getScene().impl().createEffect(effectDesc, "")); } TEST_F(AnEffect, supportsBoolUniforms) @@ -450,19 +430,19 @@ namespace ramses effectDesc.setVertexShader(vertShader); effectDesc.setFragmentShader(fragShader); - Effect* effect = sharedTestState->getScene().createEffect(effectDesc); + const Effect* effect = sharedTestState->getScene().createEffect(effectDesc); EXPECT_EQ("", sharedTestState->getScene().getLastEffectErrorMessages()); ASSERT_NE(nullptr, effect); - UniformInput uniform; - EXPECT_EQ(StatusOK, effect->findUniformInput("u_theBool", uniform)); + const std::optional optUniform = effect->findUniformInput("u_theBool"); + ASSERT_TRUE(optUniform.has_value()); Appearance* appearance = sharedTestState->getScene().createAppearance(*effect); ASSERT_NE(nullptr, appearance); - EXPECT_EQ(StatusOK, appearance->setInputValue(uniform, 0)); + EXPECT_TRUE(appearance->setInputValue(*optUniform, true)); } class AnEffectWithGeometryShader : public AnEffect @@ -506,7 +486,7 @@ namespace ramses ASSERT_NE(nullptr, effect); EXPECT_TRUE(effect->hasGeometryShader()); EDrawMode geometryShaderInput; - EXPECT_EQ(StatusOK, effect->getGeometryShaderInputType(geometryShaderInput)); + EXPECT_TRUE(effect->getGeometryShaderInputType(geometryShaderInput)); EXPECT_EQ(geometryShaderInput, EDrawMode::Points); } @@ -529,7 +509,7 @@ namespace ramses ASSERT_NE(nullptr, effect); EXPECT_TRUE(effect->hasGeometryShader()); EDrawMode geometryShaderInput; - EXPECT_EQ(StatusOK, effect->getGeometryShaderInputType(geometryShaderInput)); + EXPECT_TRUE(effect->getGeometryShaderInputType(geometryShaderInput)); EXPECT_EQ(geometryShaderInput, EDrawMode::Lines); } @@ -551,7 +531,7 @@ namespace ramses Effect* effect = sharedTestState->getScene().createEffect(effectDesc); ASSERT_NE(nullptr, effect); EDrawMode geometryShaderInput; - EXPECT_EQ(StatusOK, effect->getGeometryShaderInputType(geometryShaderInput)); + EXPECT_TRUE(effect->getGeometryShaderInputType(geometryShaderInput)); EXPECT_EQ(geometryShaderInput, EDrawMode::Triangles); } @@ -575,9 +555,58 @@ namespace ramses ASSERT_NE(nullptr, effect1); ASSERT_NE(nullptr, effect2); EDrawMode geometryShaderInput; - EXPECT_EQ(StatusOK, effect1->getGeometryShaderInputType(geometryShaderInput)); + EXPECT_TRUE(effect1->getGeometryShaderInputType(geometryShaderInput)); EXPECT_EQ(geometryShaderInput, EDrawMode::Triangles); - EXPECT_EQ(StatusOK, effect2->getGeometryShaderInputType(geometryShaderInput)); + EXPECT_TRUE(effect2->getGeometryShaderInputType(geometryShaderInput)); EXPECT_EQ(geometryShaderInput, EDrawMode::Triangles); } + + TEST_F(AnEffect, sceneValiadationProducesShaderWarnings) + { + EffectDescription effectDesc; + + effectDesc.setVertexShader(R"SHADER( + #version 100 + + uniform highp mat4 mvpMatrix; + + attribute vec3 a_position; + attribute lowp vec2 a_texcoord; + + // precision mismatch: lowp != highp + varying lowp vec2 v_texcoord; + + void main() + { + gl_Position = mvpMatrix * vec4(a_position, 1.0); + v_texcoord = a_texcoord; + } + )SHADER"); + + effectDesc.setFragmentShader(R"SHADER( + #version 100 + + uniform sampler2D textureSampler; + + // precision mismatch: highp != lowp + varying highp vec2 v_texcoord; + + void main(void) + { + gl_FragColor = texture2D(textureSampler, v_texcoord); + } + )SHADER"); + + const auto* effect = sharedTestState->getScene().createEffect(effectDesc); + EXPECT_NE(nullptr, effect); + + ValidationReport report; + sharedTestState->getScene().validate(report); + EXPECT_TRUE(report.hasIssue()); + + EXPECT_THAT(report.impl().toString(), HasSubstr("Precision mismatch: 'v_texcoord'. (Vertex: smooth out lowp 2-component vector of float, Fragment: smooth in highp 2-component vector of float)")); + ValidationReport report2; + sharedTestState->getScene().validate(report2); + EXPECT_EQ(report.getIssues(), report2.getIssues()); + } } diff --git a/tests/unittests/client/GeometryTest.cpp b/tests/unittests/client/GeometryTest.cpp new file mode 100644 index 000000000..f8fe2eb71 --- /dev/null +++ b/tests/unittests/client/GeometryTest.cpp @@ -0,0 +1,1092 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include "ClientTestUtils.h" +#include "TestEffectCreator.h" + +#include "ramses/client/Geometry.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/ArrayBuffer.h" + +#include "impl/ResourceImpl.h" +#include "impl/EffectImpl.h" +#include "impl/ArrayResourceImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "ramses/framework/EDataType.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" + +using namespace testing; + +namespace ramses::internal +{ + class GeometryTest : public ::testing::Test + { + public: + static void SetUpTestSuite() + { + sharedTestState = new TestEffectCreator; + } + + static void TearDownTestSuite() + { + delete sharedTestState; + sharedTestState = nullptr; + } + + void SetUp() override + { + EXPECT_TRUE(sharedTestState != nullptr); + } + + protected: + static void CheckHashSetToInternalScene(const Geometry& geometryBinding, ramses::internal::DataFieldHandle field, const Resource& resource, uint32_t expectedInstancingDivisor) + { + const ramses::internal::ResourceContentHash expectedHash = resource.impl().getLowlevelResourceHash(); + const ramses::internal::ResourceField& actualDataResource = sharedTestState->getInternalScene().getDataResource(geometryBinding.impl().getAttributeDataInstance(), field); + EXPECT_EQ(expectedHash, actualDataResource.hash); + EXPECT_EQ(expectedInstancingDivisor, actualDataResource.instancingDivisor); + } + + static void CheckDataBufferSetToInternalScene(const Geometry& geometryBinding, ramses::internal::DataFieldHandle field, const ArrayBufferImpl& dataBuffer, uint32_t expectedInstancingDivisor) + { + const ramses::internal::DataBufferHandle dataBufferHandle = dataBuffer.getDataBufferHandle(); + const ramses::internal::ResourceField& actualDataResource = sharedTestState->getInternalScene().getDataResource(geometryBinding.impl().getAttributeDataInstance(), field); + EXPECT_EQ(dataBufferHandle, actualDataResource.dataBuffer); + EXPECT_EQ(expectedInstancingDivisor, actualDataResource.instancingDivisor); + } + + static ArrayResource* SetVec3fArrayInput(Geometry& geometry) + { + const vec3f vert{ 0.f, 1.f, 2.f }; + auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vec3Vertices"); + EXPECT_TRUE(vertices != nullptr); + assert(vertices); + + const auto optInput = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + EXPECT_TRUE(optInput.has_value()); + assert(optInput != std::nullopt); + EXPECT_TRUE(geometry.setInputBuffer(*optInput, *vertices)); + + return vertices; + } + + static ArrayResource* SetVec2fArrayInput(Geometry& geometry) + { + const vec2f vert{ 0.f, 1.f }; + auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vec2Vertices"); + EXPECT_TRUE(vertices != nullptr); + assert(vertices); + + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + EXPECT_TRUE(optInput.has_value()); + assert(optInput != std::nullopt); + EXPECT_TRUE(geometry.setInputBuffer(*optInput, *vertices)); + + return vertices; + } + + static ArrayResource* SetVec4fArrayInput(Geometry& geometry) + { + const vec4f vert{ 0.f, 1.f, 2.f, 3.f }; + auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vec4Vertices"); + EXPECT_TRUE(vertices != nullptr); + assert(vertices); + + const auto optInput = sharedTestState->effect->findAttributeInput("vec4fArrayInput"); + EXPECT_TRUE(optInput.has_value()); + assert(optInput != std::nullopt); + EXPECT_TRUE(geometry.setInputBuffer(*optInput, *vertices)); + + return vertices; + } + + static ArrayResource* SetFloatArrayInput(Geometry& geometry) + { + float verts[8] = { 0.1f }; + auto vertices = sharedTestState->getScene().createArrayResource(8u, verts, "floatVertices"); + EXPECT_TRUE(vertices != nullptr); + assert(vertices); + + const auto optInput = sharedTestState->effect->findAttributeInput("floatArrayInput"); + EXPECT_TRUE(optInput.has_value()); + assert(optInput != std::nullopt); + EXPECT_TRUE(geometry.setInputBuffer(*optInput, *vertices)); + + return vertices; + } + + static ArrayResource* SetIndicesInput(Geometry& geometry) + { + uint32_t inds[3] = { 0u }; + auto indices = sharedTestState->getScene().createArrayResource(3u, inds, "indices"); + EXPECT_TRUE(indices != nullptr); + assert(indices); + + EXPECT_EQ(0u, geometry.impl().getIndicesCount()); + EXPECT_TRUE(geometry.setIndices(*indices)); + + return indices; + } + + static TestEffectCreator* sharedTestState; + }; + + TestEffectCreator* GeometryTest::sharedTestState = nullptr; + + TEST_F(GeometryTest, CanGetEffect) + { + Effect* emptyEffect = TestEffects::CreateTestEffect(sharedTestState->getScene()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*emptyEffect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const Effect& resultEffect = geometry->getEffect(); + EXPECT_EQ(resultEffect.getResourceId(), emptyEffect->getResourceId()); + EXPECT_EQ(resultEffect.impl().getLowlevelResourceHash(), emptyEffect->impl().getLowlevelResourceHash()); + + const uint32_t fieldCount = sharedTestState->getInternalScene().getDataLayout(geometry->impl().getAttributeDataLayout()).getFieldCount(); + EXPECT_EQ(1u, fieldCount); + + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getScene().destroy(*emptyEffect); + } + + TEST_F(GeometryTest, dataLayoutHasOnlyIndicesForEmptyEffect) + { + Effect* emptyEffect = TestEffects::CreateTestEffect(sharedTestState->getScene()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*emptyEffect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const uint32_t fieldCount = sharedTestState->getInternalScene().getDataLayout(geometry->impl().getAttributeDataLayout()).getFieldCount(); + EXPECT_EQ(1u, fieldCount); + + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getScene().destroy(*emptyEffect); + } + + TEST_F(GeometryTest, dataLayoutHasRightEffectHash) + { + Effect* emptyEffect = TestEffects::CreateTestEffect(sharedTestState->getScene()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*emptyEffect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const ramses::internal::DataLayout geometryLayout = sharedTestState->getInternalScene().getDataLayout(geometry->impl().getAttributeDataLayout()); + const ramses::internal::ResourceContentHash& effectHashFromGeometryLayout = geometryLayout.getEffectHash(); + + EXPECT_EQ(emptyEffect->impl().getLowlevelResourceHash(), effectHashFromGeometryLayout); + + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getScene().destroy(*emptyEffect); + } + + TEST_F(GeometryTest, indicesFieldIsCreatedAtFixedSlot) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const ramses::internal::DataFieldHandle indicesField(GeometryImpl::IndicesDataFieldIndex); + const ramses::internal::EFixedSemantics semantics = sharedTestState->getInternalScene().getDataLayout(geometry->impl().getAttributeDataLayout()).getField(indicesField).semantics; + EXPECT_EQ(ramses::internal::EFixedSemantics::Indices, semantics); + + sharedTestState->getScene().destroy(*geometry); + } + + TEST_F(GeometryTest, canSetResource) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const vec3f vert{ 0.f, 1.f, 2.f }; + ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vertices"); + ASSERT_TRUE(vertices != nullptr); + + const auto optInput = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + EXPECT_TRUE(geometry->setInputBuffer(*optInput, *vertices, 13u)); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(3u), *vertices, 13u); // first field is indices + + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getScene().destroy(*vertices); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingResourceWithMismatchingTypeInEffect) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const vec2f vert{ 1.f, 2.f }; + ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vertices"); + ASSERT_TRUE(vertices != nullptr); + + const auto optInput = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices)); + + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getScene().destroy(*vertices); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingVec2ArrayResourceFromAnotherScene) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const vec2f vert{ 1.f, 2.f }; + ramses::Scene& anotherScene(*sharedTestState->getClient().createScene(sceneId_t{ 0xf00 })); + ArrayResource* const vertices = anotherScene.createArrayResource(1u, &vert, "vec2Vertices"); + ASSERT_TRUE(vertices != nullptr); + + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices)); + + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getClient().destroy(anotherScene); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingIndexDataBufferFromAnotherScene) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ramses::Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); + ASSERT_NE(nullptr, otherScene); + + ArrayBuffer* const indices = otherScene->createArrayBuffer(ramses::EDataType::UInt32, 3u, "indices"); + ASSERT_NE(nullptr, indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + sharedTestState->getScene().destroy(*indices); + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getClient().destroy(*otherScene); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingVertexDataBufferFromAnotherScene) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ramses::Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); + ASSERT_NE(nullptr, otherScene); + + ArrayBuffer* const vertices = otherScene->createArrayBuffer(ramses::EDataType::Float, 3u, "vertices"); + ASSERT_NE(nullptr, vertices); + + const auto optInput = sharedTestState->effect->findAttributeInput("floatArrayInput"); + ASSERT_TRUE(optInput.has_value()); + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices)); + + sharedTestState->getScene().destroy(*vertices); + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getClient().destroy(*otherScene); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingArrayBufferByteBlobFromAnotherScene) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ramses::Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); + ASSERT_NE(nullptr, otherScene); + + ArrayBuffer* const vertices = otherScene->createArrayBuffer(ramses::EDataType::ByteBlob, 3u, "vertices"); + ASSERT_NE(nullptr, vertices); + + const auto optInputVec2 = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInputVec2.has_value()); + const auto optInputVec3 = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInputVec3.has_value()); + + constexpr uint16_t nonZeroStride = 13u; + EXPECT_FALSE(geometry->setInputBuffer(*optInputVec2, *vertices, 0u, nonZeroStride)); + EXPECT_FALSE(geometry->setInputBuffer(*optInputVec3, *vertices, 2 * sizeof(float), nonZeroStride)); + + sharedTestState->getScene().destroy(*vertices); + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getClient().destroy(*otherScene); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingArrayResourceByteBlobFromAnotherScene) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ramses::Scene* otherScene = sharedTestState->getClient().createScene(sceneId_t(777u)); + ASSERT_NE(nullptr, otherScene); + + const std::byte data[4] = { std::byte{0} }; + ArrayResource* const vertices = otherScene->createArrayResource(sizeof(data), data); + ASSERT_NE(nullptr, vertices); + + + const auto optInputVec2 = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInputVec2.has_value()); + const auto optInputVec3 = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInputVec3.has_value()); + + constexpr uint16_t nonZeroStride = 13u; + EXPECT_FALSE(geometry->setInputBuffer(*optInputVec2, *vertices, 0u, nonZeroStride)); + EXPECT_FALSE(geometry->setInputBuffer(*optInputVec3, *vertices, 2 * sizeof(float), nonZeroStride)); + + sharedTestState->getScene().destroy(*vertices); + sharedTestState->getScene().destroy(*geometry); + sharedTestState->getClient().destroy(*otherScene); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingUint16ArrayIndicesFromAnotherScene) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + uint16_t inds[3] = { 0u }; + ramses::Scene& anotherScene(*sharedTestState->getClient().createScene(sceneId_t{ 0xf00 })); + ArrayResource* const indices = anotherScene.createArrayResource(3u, inds, "indices"); + ASSERT_TRUE(indices != nullptr); + + EXPECT_EQ(0u, geometry->impl().getIndicesCount()); + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + sharedTestState->getClient().destroy(anotherScene); + } + + TEST_F(GeometryTest, canSetIndicesResource16) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const uint16_t inds[3] = { 0u }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(3u, inds, "indices"); + ASSERT_TRUE(indices != nullptr); + + EXPECT_EQ(0u, geometry->impl().getIndicesCount()); + EXPECT_TRUE(geometry->setIndices(*indices)); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(0u), *indices, 0u); + EXPECT_EQ(indices->impl().getElementCount(), geometry->impl().getIndicesCount()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, canSetIndicesResource32) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const uint32_t inds[3] = { 0u }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(3u, inds, "indices"); + ASSERT_TRUE(indices != nullptr); + + EXPECT_EQ(0u, geometry->impl().getIndicesCount()); + EXPECT_TRUE(geometry->setIndices(*indices)); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(0u), *indices, 0u); + EXPECT_EQ(indices->impl().getElementCount(), geometry->impl().getIndicesCount()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, canSetIndicesDataBuffer16) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ArrayBuffer* const indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::UInt16, 1u, "index data buffer"); + ASSERT_TRUE(indices != nullptr); + + EXPECT_EQ(0u, geometry->impl().getIndicesCount()); + EXPECT_TRUE(geometry->setIndices(*indices)); + CheckDataBufferSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(0u), indices->impl(), 0u); + EXPECT_EQ(indices->impl().getElementCount(), geometry->impl().getIndicesCount()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, canSetIndicesDataBuffer32) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ArrayBuffer* const indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::UInt32, 1u, "index data buffer"); + ASSERT_TRUE(indices != nullptr); + + EXPECT_EQ(0u, geometry->impl().getIndicesCount()); + EXPECT_TRUE(geometry->setIndices(*indices)); + CheckDataBufferSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(0u), indices->impl(), 0u); + EXPECT_EQ(indices->impl().getElementCount(), geometry->impl().getIndicesCount()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceFLoat) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const float inds[4] = { .0f, .0f, .0f, .0f }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(4u, inds, "indices"); + ASSERT_TRUE(indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceVec2F) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const vec2f indice{ 1.f, 2.f }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(1u, &indice, "indices"); + ASSERT_TRUE(indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceVec3F) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const vec3f indice{ 1.f, 2.f, 3.f }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(1u, &indice, "indices"); + ASSERT_TRUE(indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceVec4F) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const vec4f indice{ 1.f, 2.f, 3.f, 4.f }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(1u, &indice, "indices"); + ASSERT_TRUE(indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferFloat) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const float inds[4] = { .0f, .0f, .0f, .0f }; + ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Float, 1u, "indices"); + ASSERT_TRUE(indices); + indices->updateData(0u, 1u, inds); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferVec2F) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const float inds[4] = { .0f, .0f, .0f, .0f }; + ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Vector2F, 1u, "indices"); + ASSERT_TRUE(indices); + indices->updateData(0u, 1u, inds); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferVec3F) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const float inds[4] = { .0f, .0f, .0f, .0f }; + ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Vector3F, 1u, "indices"); + ASSERT_TRUE(indices); + indices->updateData(0u, 1u, inds); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferVec4F) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const float inds[4] = { .0f, .0f, .0f, .0f }; + ArrayBuffer* indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Vector4F, 1u, "indices"); + ASSERT_TRUE(indices); + indices->updateData(0u, 1u, inds); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayBufferByteBlob) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + ArrayBuffer* const indices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::ByteBlob, 1u, "indices"); + ASSERT_TRUE(indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSetIndicesWithWrongTypeArrayResourceByteBlob) + { + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const std::byte inds[4] = { std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3} }; + ArrayResource* const indices = sharedTestState->getScene().createArrayResource(sizeof(inds), inds, "indices"); + ASSERT_TRUE(indices); + + EXPECT_FALSE(geometry->setIndices(*indices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indices)); + } + + TEST_F(GeometryTest, canSetAttributeResourceInput) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const vec3f vert{ 0.f, 1.f, 2.f }; + ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vertices"); + ASSERT_TRUE(vertices != nullptr); + + EXPECT_TRUE(geometry->setInputBuffer(*optInput, *vertices)); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(3u), *vertices, 0u); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, canSetAttributeVertexDataBufferInput) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Vector3F, 3u, "vertices"); + ASSERT_TRUE(vertices != nullptr); + + EXPECT_TRUE(geometry->setInputBuffer(*optInput, *vertices, 16u)); + CheckDataBufferSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(3u), vertices->impl(), 16u); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, canVertexDataBufferInput_ArrayBufferByteBlob) + { + const auto optInputVec2 = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInputVec2.has_value()); + const auto optInputVec3 = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInputVec3.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ArrayBuffer* const interleavedVertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::ByteBlob, 5 * sizeof(float) *3u, "vertices"); + ASSERT_TRUE(interleavedVertices != nullptr); + + constexpr uint16_t nonZeroStride = 17u; + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec2, *interleavedVertices, 0u, nonZeroStride)); + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); + CheckDataBufferSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(2u), interleavedVertices->impl(), 0u); + CheckDataBufferSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(3u), interleavedVertices->impl(), 0u); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*interleavedVertices)); + } + + TEST_F(GeometryTest, canSetVertexDataBufferInput_ArrayResourceByteBlob) + { + const auto optInputVec2 = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInputVec2.has_value()); + const auto optInputVec3 = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInputVec3.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const float data[10] = { 1.f }; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) interleaved vertices passed as byte blob + ArrayResource* const interleavedVertices = sharedTestState->getScene().createArrayResource(sizeof(data), reinterpret_cast(data)); + ASSERT_TRUE(interleavedVertices != nullptr); + + constexpr uint16_t nonZeroStride = 17u; + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec2, *interleavedVertices, 0u, nonZeroStride)); + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(2u), *interleavedVertices, 0u); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(3u), *interleavedVertices, 0u); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*interleavedVertices)); + } + + TEST_F(GeometryTest, canSetArrayBufferByteBlobToSingleAttribute) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::ByteBlob, 1u, "vertices"); + ASSERT_TRUE(vertices); + + EXPECT_TRUE(geometry->setInputBuffer(*optInput, *vertices)); + CheckDataBufferSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(2u), vertices->impl(), 0u); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, canSetArrayResourceByteBlobToSingleAttribute) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const float data[10] = { 1.f }; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) interleaved vertices passed as byte blob + ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(sizeof(data), reinterpret_cast(data)); + ASSERT_TRUE(vertices); + + EXPECT_TRUE(geometry->setInputBuffer(*optInput, *vertices)); + CheckHashSetToInternalScene(*geometry, ramses::internal::DataFieldHandle(2u), *vertices, 0u); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, cannotSetStrideAndOffsetToNonByteBlobVertexDataBuffers) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Vector2F, 1u, "vertices"); + ASSERT_TRUE(vertices); + + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices, 1u, 2u)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, cannotSetStrideAndOffsetToNonByteBlobVertexArrayResource) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + const vec2f vert{ 0.f, 1.f }; + const auto vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vertices"); + ASSERT_TRUE(vertices); + + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices, 1u, 2u)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingAttributeResourceInputWithWrongType) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + const vec3f vert{ 0.f, 1.f, 2.f }; + ArrayResource* const vertices = sharedTestState->getScene().createArrayResource(1u, &vert, "vertices"); + ASSERT_TRUE(vertices != nullptr); + + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingAttributeVertexDataBufferInputWithWrongTypeUInt16) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::UInt16, 1u, "vertices"); + ASSERT_TRUE(vertices); + + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, reportsErrorWhenSettingAttributeVertexDataBufferInputWithWrongTypeUInt32) + { + const auto optInput = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInput.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry); + + ArrayBuffer* const vertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::UInt32, 1u, "vertices"); + ASSERT_TRUE(vertices); + + EXPECT_FALSE(geometry->setInputBuffer(*optInput, *vertices)); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertices)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedEffectResource) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + auto floatArray = SetFloatArrayInput(*geometry); + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*sharedTestState->effect)); + sharedTestState->effect = nullptr; + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + // restore the effect in sharedTestState after this test case + sharedTestState->effect = TestEffectCreator::createEffect(sharedTestState->getScene(), false); + ASSERT_TRUE(nullptr != sharedTestState->effect); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedIndicesResource) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + auto floatArray = SetFloatArrayInput(*geometry); + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedInputFloatArrayResource) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + auto floatArray = SetFloatArrayInput(*geometry); + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedInputVec2ArrayResource) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + auto floatArray = SetFloatArrayInput(*geometry); + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedInputVec3ArrayResource) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + auto floatArray = SetFloatArrayInput(*geometry); + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedInputVec4ArrayResource) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + auto floatArray = SetFloatArrayInput(*geometry); + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedArrayBufferByteBlob) + { + const auto optInputVec2 = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInputVec2.has_value()); + const auto optInputVec3 = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInputVec3.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + ArrayBuffer* const interleavedVertices = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::ByteBlob, 5 * sizeof(float) * 3u, "vertices"); + ASSERT_TRUE(interleavedVertices != nullptr); + std::vector dummyData(interleavedVertices->getMaximumNumberOfElements(), std::byte{0x00}); + interleavedVertices->updateData(0u, 1u, dummyData.data()); + + auto floatArray = SetFloatArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + constexpr uint16_t nonZeroStride = 12u; + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec2, *interleavedVertices, 0u, nonZeroStride)); + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*interleavedVertices)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedArrayResourceByteBlobl) + { + const auto optInputVec2 = sharedTestState->effect->findAttributeInput("vec2fArrayInput"); + ASSERT_TRUE(optInputVec2.has_value()); + const auto optInputVec3 = sharedTestState->effect->findAttributeInput("vec3fArrayInput"); + ASSERT_TRUE(optInputVec3.has_value()); + + Geometry* const geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + + uint32_t data[4] = { 0u }; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) interleaved vertices passed as byte blob + ArrayResource* const interleavedVertices = sharedTestState->getScene().createArrayResource(sizeof(data), reinterpret_cast(data)); + ASSERT_TRUE(interleavedVertices != nullptr); + + auto floatArray = SetFloatArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + constexpr uint16_t nonZeroStride = 12u; + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec2, *interleavedVertices, 0u, nonZeroStride)); + EXPECT_TRUE(geometry->setInputBuffer(*optInputVec3, *interleavedVertices, 2 * sizeof(float), nonZeroStride)); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*interleavedVertices)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*floatArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithDestroyedVertexDataBuffer) + { + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + + ArrayBuffer* const vertexDataBuffer = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Float, 3, "vertices"); + ASSERT_TRUE(vertexDataBuffer != nullptr); + const auto optInput = sharedTestState->effect->findAttributeInput("floatArrayInput"); + ASSERT_TRUE(optInput.has_value()); + geometry->setInputBuffer(*optInput, *vertexDataBuffer); + const float data[] = { 0 }; + vertexDataBuffer->updateData(0u, 1u, data); + + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertexDataBuffer)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } + + TEST_F(GeometryTest, reportsErrorWhenValidatedWithVertexDataBufferThatHasWrongType) + { + //It is possible that a data buffer gets deleted, and new data buffer gets created with same + //handle. Unfortunately validate can not check if a data buffer was destroyed and re-created + //but it can at least check that the assigned data buffer is of a correct type + Geometry* geometry = sharedTestState->getScene().createGeometry(*sharedTestState->effect, "geometry"); + + ArrayBuffer* const vertexDataBuffer = sharedTestState->getScene().createArrayBuffer(ramses::EDataType::Float, 3, "vertices"); + ASSERT_TRUE(vertexDataBuffer != nullptr); + const auto optInput = sharedTestState->effect->findAttributeInput("floatArrayInput"); + ASSERT_TRUE(optInput.has_value()); + geometry->setInputBuffer(*optInput, *vertexDataBuffer); + const float data[] = { 0 }; + vertexDataBuffer->updateData(0u, 1u, data); + + auto vec2fArray = SetVec2fArrayInput(*geometry); + auto vec3fArray = SetVec3fArrayInput(*geometry); + auto vec4fArray = SetVec4fArrayInput(*geometry); + auto indicesArray = SetIndicesInput(*geometry); + ValidationReport report; + geometry->validate(report); + EXPECT_FALSE(report.hasIssue()); + + //delete data buffer and create new one with same handle + ramses::internal::DataBufferHandle dataBufferHandle = vertexDataBuffer->impl().getDataBufferHandle(); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vertexDataBuffer)); + ASSERT_FALSE(sharedTestState->getScene().impl().getIScene().isDataBufferAllocated(dataBufferHandle)); + sharedTestState->getScene().impl().getIScene().allocateDataBuffer(ramses::internal::EDataBufferType::VertexBuffer, ramses::internal::EDataType::Vector2F, 10 * sizeof(float), dataBufferHandle); + ASSERT_TRUE(sharedTestState->getScene().impl().getIScene().isDataBufferAllocated(dataBufferHandle)); + report.clear(); + geometry->validate(report); + EXPECT_TRUE(report.hasError()); + + EXPECT_TRUE(sharedTestState->getScene().destroy(*geometry)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec2fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec3fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*vec4fArray)); + EXPECT_TRUE(sharedTestState->getScene().destroy(*indicesArray)); + } +} diff --git a/tests/unittests/client/GlslEffectTest.cpp b/tests/unittests/client/GlslEffectTest.cpp new file mode 100644 index 000000000..17fe41ee9 --- /dev/null +++ b/tests/unittests/client/GlslEffectTest.cpp @@ -0,0 +1,1050 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/glslEffectBlock/GlslEffect.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "gmock/gmock.h" + +#include +#include +#include + +namespace ramses::internal +{ + class AGlslEffect : public ::testing::Test + { + public: + const std::string basicVertexShader = R"SHADER( + #version 320 es + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + const std::string basicFragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + void main(void) + { + colorOut = vec4(0.0); + })SHADER"; + const std::string basicGeometryShader = R"SHADER( + #version 320 es + layout(points) in; + layout(points, max_vertices = 1) out; + void main() { + gl_Position = vec4(0.0); + EmitVertex(); + } + )SHADER"; + const std::vector emptyCompilerDefines{}; + const HashMap emptySemanticInputs{}; + + protected: + static void VerifyUniformInputExists(const EffectResource& effect, std::string_view uniformName) + { + const DataFieldHandle effecthandle = effect.getUniformDataFieldHandleByName(std::string(uniformName)); + EXPECT_TRUE(effecthandle.isValid()); + }; + }; + + TEST_F(AGlslEffect, canParseBasicShaders) + { + GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getUniformInputs().size()); + EXPECT_EQ(0u, res->getAttributeInputs().size()); + EXPECT_FALSE(res->getGeometryShaderInputType().has_value()); + EXPECT_EQ(std::string(), res->getName()); + } + + TEST_F(AGlslEffect, canParseBasicShaders_WithGeometryShader) + { + GlslEffect ge(basicVertexShader, basicFragmentShader, basicGeometryShader, emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getUniformInputs().size()); + EXPECT_EQ(0u, res->getAttributeInputs().size()); + EXPECT_EQ(EDrawMode::Points, res->getGeometryShaderInputType()); + EXPECT_EQ(std::string(), res->getName()); + } + + TEST_F(AGlslEffect, canParseGeometryShaderWithTriangles) + { + const std::string geometryShaderTriangles = R"SHADER( + #version 320 es + layout(triangles) in; + layout(points, max_vertices = 1) out; + void main() { + gl_Position = vec4(0.0); + } + )SHADER"; + + GlslEffect ge(basicVertexShader, basicFragmentShader, geometryShaderTriangles, emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getUniformInputs().size()); + EXPECT_EQ(0u, res->getAttributeInputs().size()); + EXPECT_EQ(EDrawMode::Triangles, res->getGeometryShaderInputType()); + EXPECT_EQ(std::string(), res->getName()); + } + + TEST_F(AGlslEffect, usesPassedName) + { + GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, "someName"); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(std::string("someName"), res->getName()); + } + + TEST_F(AGlslEffect, rejectsEmptyVertexShader) + { + GlslEffect ge("", basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, rejectsBrokenVertexShader) + { + GlslEffect ge("foo", basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, rejectsEmptyFragmentShader) + { + GlslEffect ge(basicVertexShader, "", "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, rejectsBrokenFragmentShader) + { + GlslEffect ge(basicVertexShader, "bar", "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, acceptsEmptyGeometryShader) + { + GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, rejectsBrokenGeometryShader) + { + GlslEffect ge(basicVertexShader, basicFragmentShader, "bar", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, usesProvidedDefines) + { + const char* vertexShader = + "void main(void)\n" + "{\n" + " gl_Position = DEFINE_ZERO;\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = DEFINE_ONE;\n" + "}\n"; + std::vector compilerDefines; + compilerDefines.emplace_back("DEFINE_ZERO vec4(0.0)"); + compilerDefines.emplace_back("DEFINE_ONE vec4(1.0)"); + GlslEffect ge(vertexShader, fragmentShader, "", compilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_THAT(res->getVertexShader(), ::testing::HasSubstr(compilerDefines[0])); + EXPECT_THAT(res->getVertexShader(), ::testing::HasSubstr(compilerDefines[1])); + EXPECT_THAT(res->getFragmentShader(), ::testing::HasSubstr(compilerDefines[0])); + EXPECT_THAT(res->getFragmentShader(), ::testing::HasSubstr(compilerDefines[1])); + } + + TEST_F(AGlslEffect, generatedShaderWithDefaultVersionAndDefinesEmbedded) + { + // this test will break if anything is changed in shader generation (even if no semantic change like adding newline!) + const char* vertexShader = + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + std::vector compilerDefines; + compilerDefines.emplace_back("FIRST_DEFINE foo"); + compilerDefines.emplace_back("OTHER_DEFINE bar"); + GlslEffect ge(vertexShader, fragmentShader, "", compilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + const char* expectedVertexShader = + "#version 100\n" + "#define FIRST_DEFINE foo\n" + "#define OTHER_DEFINE bar\n" + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* expectedFragmentShader = + "#version 100\n" + "#define FIRST_DEFINE foo\n" + "#define OTHER_DEFINE bar\n" + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + + EXPECT_STREQ(expectedVertexShader, res->getVertexShader()); + EXPECT_STREQ(expectedFragmentShader, res->getFragmentShader()); + } + + TEST_F(AGlslEffect, acceptsGLSLESShaders_Version300es) + { + const char* vertexShader = R"SHADER( + #version 300 es + in lowp vec3 a_position; + out lowp vec3 v_position; + void main(void) + { + v_position = a_position; + gl_Position = vec4(a_position, 1.0); + })SHADER"; + const char* fragmentShader = R"SHADER( + #version 300 es + in lowp vec3 v_position; + out lowp vec4 color; + void main(void) + { + color = vec4(v_position, 1.0); + })SHADER"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, acceptsGLSLESShaders_Version310es) + { + const char* vertexShader = R"SHADER( + #version 310 es + in lowp vec3 a_position; + out lowp vec3 v_position; + void main(void) + { + v_position = a_position; + gl_Position = vec4(a_position, 1.0); + })SHADER"; + const char* fragmentShader = R"SHADER( + #version 310 es + in lowp vec3 v_position; + out lowp vec4 color; + void main(void) + { + color = vec4(v_position, 1.0); + })SHADER"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, acceptsGLSLESShaders_Version310esWithGeometryShaderExtension) + { + const char* vertexShader = R"SHADER( + #version 310 es + in lowp vec3 a_position; + out lowp vec3 v_position; + void main(void) + { + v_position = a_position; + gl_Position = vec4(a_position, 1.0); + })SHADER"; + const char* fragmentShader = R"SHADER( + #version 310 es + in lowp vec3 v_position; + out lowp vec4 color; + void main(void) + { + color = vec4(v_position, 1.0); + })SHADER"; + + const std::string geometryShader = R"SHADER( + #version 310 es + #extension GL_EXT_geometry_shader : enable + layout(points) in; + layout(points, max_vertices = 1) out; + void main() { + gl_Position = vec4(0.0); + EmitVertex(); + } + )SHADER"; + + GlslEffect ge(vertexShader, fragmentShader, geometryShader, emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + EXPECT_EQ(EDrawMode::Points, res->getGeometryShaderInputType()); + + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, doesNotAcceptMixedES2VertexAndES3FragmentShaders) + { + const char* vertexShader = + "#version 100\n" + "attribute lowp vec3 a_position;\n" + "varying lowp vec3 v_position;\n" + "void main(void)\n" + "{\n" + " v_position = a_position;\n" + " gl_Position = vec4(a_position, 1.0);\n" + "}\n"; + const char* fragmentShader = + "#version 300 es\n" + "in lowp vec3 v_position;\n" + "out lowp vec4 color;\n" + "void main(void)\n" + "{\n" + " color = vec4(v_position, 1.0);\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, doesNotAcceptMixedES3VertexAndES2FragmentShaders) + { + const char* vertexShader = + "#version 300 es\n" + "in lowp vec3 a_position;\n" + "out lowp vec3 v_position;\n" + "void main(void)\n" + "{\n" + " v_position = a_position;\n" + " gl_Position = vec4(a_position, 1.0);\n" + "}\n"; + const char* fragmentShader = + "#version 100\n" + "varying lowp vec3 v_position;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(v_position, 1.0);\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, canParseShaderInputs) + { + const char* vertexShader = R"SHADER( + #version 320 es + precision highp float; + uniform bool uniformBool1; + uniform mat4 uniformWithSemantic; + uniform mat3 matrix3x3; + uniform mat2 matrix2x2; + in vec3 attributeWithSemantic; + in float attributeFloat; + void main(void) + { + gl_Position = vec4(0.0); + })SHADER"; + const char* fragmentShader = R"SHADER( + #version 320 es + precision highp float; + uniform bool uniformBool2; + uniform sampler2D uniformSampler; + uniform vec4 uniformVec; + out vec4 colorOut; + void main(void) + { + colorOut = vec4(0.0); + })SHADER"; + const std::string geometryShader = R"SHADER( + #version 320 es + precision highp float; + layout(points) in; + layout(points, max_vertices = 1) out; + out vec4 g_colorOut; + uniform bool uniformBool3; + uniform float uniformGeomFloat; + uniform vec4 uniformGeomVec; + uniform sampler2D uniformGeomSampler; + void main() { + gl_Position = uniformGeomVec + vec4(uniformGeomFloat); + g_colorOut = texture(uniformGeomSampler, vec2(0.0)); + EmitVertex(); + } + )SHADER"; + + HashMap semantics; + semantics.put("uniformWithSemantic", EFixedSemantics::ModelViewProjectionMatrix); + semantics.put("attributeWithSemantic", EFixedSemantics::CameraWorldPosition); + + GlslEffect ge(vertexShader, fragmentShader, geometryShader, emptyCompilerDefines, semantics, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + const EffectInputInformationVector& uniforms = res->getUniformInputs(); + const EffectInputInformationVector& attributes = res->getAttributeInputs(); + + ASSERT_EQ(11u, uniforms.size()); + EXPECT_EQ(EffectInputInformation("uniformBool1", 1, ramses::internal::EDataType::Bool, EFixedSemantics::Invalid), uniforms[0]); + EXPECT_EQ(EffectInputInformation("uniformWithSemantic", 1, ramses::internal::EDataType::Matrix44F, EFixedSemantics::ModelViewProjectionMatrix), uniforms[1]); + EXPECT_EQ(EffectInputInformation("matrix3x3", 1, ramses::internal::EDataType::Matrix33F, EFixedSemantics::Invalid), uniforms[2]); + EXPECT_EQ(EffectInputInformation("matrix2x2", 1, ramses::internal::EDataType::Matrix22F, EFixedSemantics::Invalid), uniforms[3]); + EXPECT_EQ(EffectInputInformation("uniformBool2", 1, ramses::internal::EDataType::Bool, EFixedSemantics::Invalid), uniforms[4]); + EXPECT_EQ(EffectInputInformation("uniformSampler", 1, ramses::internal::EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[5]); + EXPECT_EQ(EffectInputInformation("uniformVec", 1, ramses::internal::EDataType::Vector4F, EFixedSemantics::Invalid), uniforms[6]); + EXPECT_EQ(EffectInputInformation("uniformBool3", 1, ramses::internal::EDataType::Bool, EFixedSemantics::Invalid), uniforms[7]); + EXPECT_EQ(EffectInputInformation("uniformGeomFloat", 1, ramses::internal::EDataType::Float, EFixedSemantics::Invalid), uniforms[8]); + EXPECT_EQ(EffectInputInformation("uniformGeomVec", 1, ramses::internal::EDataType::Vector4F, EFixedSemantics::Invalid), uniforms[9]); + EXPECT_EQ(EffectInputInformation("uniformGeomSampler", 1, ramses::internal::EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[10]); + + ASSERT_EQ(2u, attributes.size()); + EXPECT_EQ(EffectInputInformation("attributeWithSemantic", 1, ramses::internal::EDataType::Vector3Buffer, EFixedSemantics::CameraWorldPosition), attributes[0]); + EXPECT_EQ(EffectInputInformation("attributeFloat", 1, ramses::internal::EDataType::FloatBuffer, EFixedSemantics::Invalid), attributes[1]); + } + + TEST_F(AGlslEffect, canParseSamplerInputsGLSLES2) + { + const char* vertexShader = + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "precision highp float;\n" + "uniform sampler2D s2d;\n" + "uniform samplerCube sc;\n" + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES texExternal;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getAttributeInputs().size()); + + const EffectInputInformationVector& uniforms = res->getUniformInputs(); + + ASSERT_EQ(3u, uniforms.size()); + EXPECT_EQ(EffectInputInformation("s2d", 1, ramses::internal::EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[0]); + EXPECT_EQ(EffectInputInformation("sc", 1, ramses::internal::EDataType::TextureSamplerCube, EFixedSemantics::Invalid), uniforms[1]); + EXPECT_EQ(EffectInputInformation("texExternal", 1, ramses::internal::EDataType::TextureSamplerExternal, EFixedSemantics::Invalid), uniforms[2]); + } + + TEST_F(AGlslEffect, canParseSamplerInputsGLSLES3) + { + const char* vertexShader = + "#version 300 es\n" + "precision highp float;" + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "#version 300 es\n" + "precision highp float;" + "uniform sampler2D s2d;\n" + "uniform highp sampler3D s3d;\n" + "uniform samplerCube sc;\n" + "#extension GL_OES_EGL_image_external_essl3 : require\n" + "uniform samplerExternalOES texExternal;\n" + + "out vec4 color;\n" + "void main(void)\n" + "{\n" + " color = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getAttributeInputs().size()); + + const EffectInputInformationVector& uniforms = res->getUniformInputs(); + + ASSERT_EQ(4u, uniforms.size()); + EXPECT_EQ(EffectInputInformation("s2d", 1, ramses::internal::EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[0]); + EXPECT_EQ(EffectInputInformation("s3d", 1, ramses::internal::EDataType::TextureSampler3D, EFixedSemantics::Invalid), uniforms[1]); + EXPECT_EQ(EffectInputInformation("sc", 1, ramses::internal::EDataType::TextureSamplerCube, EFixedSemantics::Invalid), uniforms[2]); + EXPECT_EQ(EffectInputInformation("texExternal", 1, ramses::internal::EDataType::TextureSamplerExternal, EFixedSemantics::Invalid), uniforms[3]); + } + + TEST_F(AGlslEffect, canParseSamplerInputsGLSLES31) + { + const char* vertexShader = + "#version 310 es\n" + "precision highp float;" + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "#version 310 es\n" + "precision highp float;" + "uniform sampler2D s2d;\n" + "uniform highp sampler2DMS s2dMS;" + "uniform highp sampler3D s3d;\n" + "uniform samplerCube sc;\n" + "#extension GL_OES_EGL_image_external_essl3 : require\n" + "uniform samplerExternalOES texExternal;\n" + "out vec4 color;\n" + "void main(void)\n" + "{\n" + " color = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getAttributeInputs().size()); + + const EffectInputInformationVector& uniforms = res->getUniformInputs(); + + ASSERT_EQ(5u, uniforms.size()); + EXPECT_EQ(EffectInputInformation("s2d", 1, ramses::internal::EDataType::TextureSampler2D, EFixedSemantics::Invalid), uniforms[0]); + EXPECT_EQ(EffectInputInformation("s2dMS", 1, ramses::internal::EDataType::TextureSampler2DMS, EFixedSemantics::Invalid), uniforms[1]); + EXPECT_EQ(EffectInputInformation("s3d", 1, ramses::internal::EDataType::TextureSampler3D, EFixedSemantics::Invalid), uniforms[2]); + EXPECT_EQ(EffectInputInformation("sc", 1, ramses::internal::EDataType::TextureSamplerCube, EFixedSemantics::Invalid), uniforms[3]); + EXPECT_EQ(EffectInputInformation("texExternal", 1, ramses::internal::EDataType::TextureSamplerExternal, EFixedSemantics::Invalid), uniforms[4]); + } + + + TEST_F(AGlslEffect, canParseArrayInputs) + { + const char* vertexShader = + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "precision highp float;\n" + "uniform vec3 v[4];\n" + "uniform mat4 m[2];\n" + "uniform int i[2];\n" + "uniform bool b[3];\n" + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getAttributeInputs().size()); + + const EffectInputInformationVector& uniforms = res->getUniformInputs(); + + ASSERT_EQ(4u, uniforms.size()); + EXPECT_EQ(EffectInputInformation("v", 4, ramses::internal::EDataType::Vector3F, EFixedSemantics::Invalid), uniforms[0]); + EXPECT_EQ(EffectInputInformation("m", 2, ramses::internal::EDataType::Matrix44F, EFixedSemantics::Invalid), uniforms[1]); + EXPECT_EQ(EffectInputInformation("i", 2, ramses::internal::EDataType::Int32, EFixedSemantics::Invalid), uniforms[2]); + EXPECT_EQ(EffectInputInformation("b", 3, ramses::internal::EDataType::Bool, EFixedSemantics::Invalid), uniforms[3]); + } + + TEST_F(AGlslEffect, failsWithWrongSemanticForType) + { + const char* vertexShader = + "precision highp float;\n" + "uniform vec3 uniformWithWrongSemantic;\n" + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + + HashMap semantics; + semantics.put("uniformWithWrongSemantic", EFixedSemantics::ModelViewProjectionMatrix); + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, semantics, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, ignoresNormalGlobalsAndVaryings) + { + const char* vertexShader = + "precision highp float;\n" + "mat4 foobar;\n" + "const float fl = float(0.1);\n" + "varying vec3 v3;\n" + "void main(void)\n" + "{\n" + " gl_Position = foobar * vec4(v3, fl);\n" + "}\n"; + const char* fragmentShader = + "precision highp float;\n" + "varying vec3 v3;\n" + "bool b;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(0u, res->getUniformInputs().size()); + EXPECT_EQ(0u, res->getAttributeInputs().size()); + } + + TEST_F(AGlslEffect, canParseStructUniform) + { + const char* vertexShader = + "precision highp float;\n" + "struct S {\n" + " vec3 a;\n" + " vec3 b;\n" + "};\n" + "uniform S s;\n" + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(2u, res->getUniformInputs().size()); + VerifyUniformInputExists(*res, "s.a"); + VerifyUniformInputExists(*res, "s.b"); + } + + TEST_F(AGlslEffect, canParseArrayOfStructUniform) + { + const char* vertexShader = + "precision highp float;\n" + "struct S {\n" + " vec3 a;\n" + " vec3 b;\n" + "};\n" + "uniform S s[4];\n" // Array declaration + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(8u, res->getUniformInputs().size()); + VerifyUniformInputExists(*res, "s[0].a"); + VerifyUniformInputExists(*res, "s[0].b"); + + VerifyUniformInputExists(*res, "s[1].a"); + VerifyUniformInputExists(*res, "s[1].b"); + + VerifyUniformInputExists(*res, "s[2].a"); + VerifyUniformInputExists(*res, "s[2].b"); + + VerifyUniformInputExists(*res, "s[3].a"); + VerifyUniformInputExists(*res, "s[3].b"); + } + + TEST_F(AGlslEffect, canParseNestedStructUniform) + { + const char* vertexShader = + "precision highp float;\n" + "struct simpleStruct {\n" + " vec3 a;\n" + " vec3 b;\n" + "};\n" + "struct nestedStruct {\n" // Include previous struct as a member + " vec3 c;\n" + " simpleStruct d;\n" + "};\n" + "uniform nestedStruct ns;\n" + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(3u, res->getUniformInputs().size()); + VerifyUniformInputExists(*res, "ns.c"); + VerifyUniformInputExists(*res, "ns.d.a"); + VerifyUniformInputExists(*res, "ns.d.b"); + } + + TEST_F(AGlslEffect, canParseArrayOfNestedStructUniforms) + { + const char* vertexShader = + "precision highp float;\n" + "struct simpleStruct {\n" + " vec3 a;\n" + " vec3 b;\n" + "};\n" + "struct nestedStruct {\n" // Include previous struct as a member + " simpleStruct c;\n" + "};\n" + "struct doubleNestedStruct {\n" // Add another level; this time as an array + " nestedStruct d[2];\n" + "};\n" + "uniform doubleNestedStruct ans[5];\n" // Array of those guys + "void main(void)\n" + "{\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0);\n" + "}\n"; + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(20u, res->getUniformInputs().size()); + + VerifyUniformInputExists(*res, "ans[0].d[0].c.a"); + VerifyUniformInputExists(*res, "ans[0].d[0].c.b"); + VerifyUniformInputExists(*res, "ans[0].d[1].c.a"); + VerifyUniformInputExists(*res, "ans[0].d[1].c.b"); + + VerifyUniformInputExists(*res, "ans[1].d[0].c.a"); + VerifyUniformInputExists(*res, "ans[1].d[0].c.b"); + VerifyUniformInputExists(*res, "ans[1].d[1].c.a"); + VerifyUniformInputExists(*res, "ans[1].d[1].c.b"); + + VerifyUniformInputExists(*res, "ans[2].d[0].c.a"); + VerifyUniformInputExists(*res, "ans[2].d[0].c.b"); + VerifyUniformInputExists(*res, "ans[2].d[1].c.a"); + VerifyUniformInputExists(*res, "ans[2].d[1].c.b"); + + VerifyUniformInputExists(*res, "ans[3].d[0].c.a"); + VerifyUniformInputExists(*res, "ans[3].d[0].c.b"); + VerifyUniformInputExists(*res, "ans[3].d[1].c.a"); + VerifyUniformInputExists(*res, "ans[3].d[1].c.b"); + + VerifyUniformInputExists(*res, "ans[4].d[0].c.a"); + VerifyUniformInputExists(*res, "ans[4].d[0].c.b"); + VerifyUniformInputExists(*res, "ans[4].d[1].c.a"); + VerifyUniformInputExists(*res, "ans[4].d[1].c.b"); + } + + class GlslEffectTestRunnable : public Runnable + { + public: + void run() override + { + const std::vector defs; + const ramses::internal::HashMap sems; + const char* v = "void main(){gl_Position=vec4(0);}"; + const char* f = "void main(){gl_FragColor=vec4(0);}"; + + GlslEffect eff(v, f, "", defs, sems, "myname"); + std::unique_ptr resource(eff.createEffectResource()); + createdSuccessfully = static_cast(resource); + } + + bool createdSuccessfully{false}; + }; + + TEST_F(AGlslEffect, UseGlslEffectFromDifferentThread) + { + PlatformThread th("GlslEffectThrd"); + GlslEffectTestRunnable r; + th.start(r); + th.join(); + EXPECT_TRUE(r.createdSuccessfully); + } + + TEST_F(AGlslEffect, UseGlslEffectFromMainThread) + { + GlslEffectTestRunnable r; + r.run(); + EXPECT_TRUE(r.createdSuccessfully); + } + + TEST_F(AGlslEffect, hasDefaultShaderVersion100) + { + const std::string basicVertexShader_v100 = R"SHADER( + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + const std::string basicFragmentShader_v100 = R"SHADER( + void main(void) + { + gl_FragColor = vec4(0.0); + })SHADER"; + + GlslEffect ge(basicVertexShader_v100, basicFragmentShader_v100, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + EXPECT_EQ(100u, ge.getShadingLanguageVersion()); + } + + TEST_F(AGlslEffect, isAbleToParseShadersWithVersionString) + { + GlslEffect ge(basicVertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + } + + TEST_F(AGlslEffect, acceptsReturnStatementsEveryWhere) + { + const char* vertexShader = R"SHADER( + #version 320 es + precision highp float; + out float attr; + void fun() { + if (attr < 1.0) return; + return; + } + void main(void) + { + if (attr > 1.0) return; + gl_Position = vec4(0.0); + return; + })SHADER"; + + GlslEffect ge(vertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, acceptsLoopsInShaders) + { + const char* vertexShader = R"SHADER( + #version 320 es + precision highp float; + uniform int value; + void main(void) + { + int i = 0; + for (i = 0; i < value; ++i) continue; + while (value < 10) --i; + do { break; } while (false); + gl_Position = vec4(0.0); + })SHADER"; + + GlslEffect ge(vertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, supportsExternalTextureExtension) + { + const char* vertexShader = + "#version 100\n" + "precision highp float;\n" + "attribute vec2 texCoords;\n" + "varying vec2 texCoord;\n" + "void main(void)\n" + "{\n" + " texCoord = texCoords;\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "#version 100\n" + "precision highp float;\n" + "#extension GL_OES_EGL_image_external : require\n" + "uniform samplerExternalOES tex;\n" + "varying vec2 texCoord;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = texture2D(tex, texCoord);\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_TRUE(res); + } + + TEST_F(AGlslEffect, doesNotSupportFloatTextureExtension) + { + const char* vertexShader = + "#version 100\n" + "precision highp float;\n" + "attribute vec2 texCoords;\n" + "varying vec2 texCoord;\n" + "void main(void)\n" + "{\n" + " texCoord = texCoords;\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "#version 100\n" + "precision highp float;\n" + "#extension GL_OES_texture_float : require\n" + "uniform sampler2D tex;\n" + "varying vec2 texCoord;\n" + "void main(void)\n" + "{\n" + " float color = texture2D(tex, texCoord);\n" + " gl_FragColor = vec4(vec3(color*255), 1.0);\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, doesNotSupport3DTextureExtension) + { + const char* vertexShader = + "#version 100\n" + "precision highp float;\n" + "attribute vec3 texCoords;\n" + "varying vec3 texCoord;\n" + "void main(void)\n" + "{\n" + " texCoord = texCoords;\n" + " gl_Position = vec4(0.0);\n" + "}\n"; + const char* fragmentShader = + "#version 100\n" + "precision highp float;\n" + "#extension GL_OES_texture_3D : require\n" + "uniform mediump sampler3D tex;\n" + "varying vec3 texCoord;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = texture3D(tex, texCoord);\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, treatsUniformDeclaredInBothStagesWithSameNameAsSingleUniform) + { + const char* vertexShader = + "uniform lowp vec4 dummy;\n" + "void main(void)\n" + "{\n" + " gl_Position = dummy;\n" + "}\n"; + const char* fragmentShader = + "uniform lowp vec4 dummy;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = dummy;\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + const std::unique_ptr res(ge.createEffectResource()); + ASSERT_TRUE(res); + + EXPECT_EQ(1u, res->getUniformInputs().size()); + } + + TEST_F(AGlslEffect, rejectsEffectWithUniformDeclaredInBothStagesWithSameNameButDifferentDataType) + { + const char* vertexShader = + "uniform lowp vec4 dummy;\n" + "void main(void)\n" + "{\n" + " gl_Position = dummy;\n" + "}\n"; + const char* fragmentShader = + "uniform lowp float dummy;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, dummy);\n" + "}\n"; + + GlslEffect ge(vertexShader, fragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + const std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + // This is a bug in glslang, or maybe wrong configuration in ramses. In gles 100, active should be allowed + TEST_F(AGlslEffect, doesNotAcceptActiveAsKeywordInVersion100) + { + const std::string basicVertexShader_v100 = R"SHADER( + attribute float active; + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + const std::string basicFragmentShader_v100 = R"SHADER( + void main(void) + { + gl_FragColor = vec4(0.0); + })SHADER"; + GlslEffect ge(basicVertexShader_v100, basicFragmentShader_v100, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + EXPECT_FALSE(res); + } + + TEST_F(AGlslEffect, canRetrieveGLSLErrorMessage) + { + const char* vertexShader = R"SHADER( + #version 320 es + out float inp; + void main(void) + { + gl_Position = vec4(0.0) + })SHADER"; + GlslEffect ge(vertexShader, basicFragmentShader, "", emptyCompilerDefines, emptySemanticInputs, ""); + std::unique_ptr res(ge.createEffectResource()); + ASSERT_FALSE(res); + using namespace ::testing; + EXPECT_THAT(ge.getEffectErrorMessages(), + AnyOf(Eq("[GLSL Compiler] vertex shader Shader Parsing Error:\n" + "ERROR: 2:5: '' : syntax error\n" + "ERROR: 1 compilation errors. No code generated.\n\n\n"), + Eq("[GLSL Compiler] vertex shader Shader Parsing Error:\n" + "ERROR: 2:5: '' : syntax error, unexpected RIGHT_BRACE, expecting COMMA or SEMICOLON\n" + "ERROR: 1 compilation errors. No code generated.\n\n\n"))); + } +} diff --git a/client/test/GlslLimitsTest.cpp b/tests/unittests/client/GlslLimitsTest.cpp similarity index 87% rename from client/test/GlslLimitsTest.cpp rename to tests/unittests/client/GlslLimitsTest.cpp index 2632b4d15..67838e2d1 100644 --- a/client/test/GlslLimitsTest.cpp +++ b/tests/unittests/client/GlslLimitsTest.cpp @@ -7,20 +7,15 @@ // ------------------------------------------------------------------------- #include -#include "glslEffectBlock/GLSlang.h" -#include "glslEffectBlock/GlslLimits.h" +#include "internal/glslEffectBlock/GLSlang.h" +#include "internal/glslEffectBlock/GlslLimits.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class GlslLimitsTest : public ::testing::Test { - public: - GlslLimitsTest() - { - - } }; TEST_F(GlslLimitsTest, parseGlslVersionFromString) @@ -32,7 +27,7 @@ namespace ramses_internal TEST_F(GlslLimitsTest, testLimitsOpenGL_20) { - TBuiltInResource res; + TBuiltInResource res{}; GlslLimits::InitCompilationResources(res, "#version 110"); EXPECT_EQ(res.maxClipPlanes, 6); @@ -42,7 +37,7 @@ namespace ramses_internal TEST_F(GlslLimitsTest, testLimitsOpenGL_ES_30) { - TBuiltInResource res; + TBuiltInResource res{}; GlslLimits::InitCompilationResources(res, "#version 300 es"); EXPECT_EQ(res.maxFragmentInputVectors, 15); @@ -51,7 +46,7 @@ namespace ramses_internal TEST_F(GlslLimitsTest, testLimitsOpenGL_42) { - TBuiltInResource res; + TBuiltInResource res{}; GlslLimits::InitCompilationResources(res, "#version 420"); EXPECT_EQ(res.maxAtomicCounterBufferSize, 16384); diff --git a/tests/unittests/client/GlslParserTest.cpp b/tests/unittests/client/GlslParserTest.cpp new file mode 100644 index 000000000..f2272e346 --- /dev/null +++ b/tests/unittests/client/GlslParserTest.cpp @@ -0,0 +1,526 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/glslEffectBlock/GlslParser.h" +#include "gmock/gmock.h" +#include + +namespace ramses::internal +{ + class AGlslParser : public ::testing::Test + { + public: + + const std::string basicVertexShader = R"SHADER( + #version 320 es + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + const std::string basicFragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + void main(void) + { + colorOut = vec4(0.0); + })SHADER"; + const std::string basicGeometryShader = R"SHADER( + #version 320 es + layout(points) in; + layout(points, max_vertices = 1) out; + void main() { + gl_Position = vec4(0.0); + EmitVertex(); + } + )SHADER"; + + static std::unique_ptr + MakeParser(const std::string& vertexShader, const std::string& fragmentShader, const std::string& geometryShader = std::string(), const std::vector& defines ={}) + { + return std::make_unique(vertexShader, fragmentShader, geometryShader, defines); + } + }; + + TEST_F(AGlslParser, canParseBasicShaders) + { + auto parser = MakeParser(basicVertexShader, basicFragmentShader, basicGeometryShader); + ASSERT_TRUE(parser); + + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, rejectsEmptyVertexShader) + { + auto parser = MakeParser("", basicFragmentShader, ""); + EXPECT_FALSE(parser->valid()); + EXPECT_THAT(parser->getErrors(), testing::HasSubstr("Linker Error")); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, rejectsBrokenVertexShader) + { + auto parser = MakeParser("foo", basicFragmentShader, ""); + EXPECT_FALSE(parser->valid()); + EXPECT_THAT(parser->getErrors(), testing::HasSubstr("Parsing Error")); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, rejectsEmptyFragmentShader) + { + auto parser = MakeParser(basicVertexShader, "", ""); + EXPECT_FALSE(parser->valid()); + EXPECT_THAT(parser->getErrors(), testing::HasSubstr("Linker Error")); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, rejectsBrokenFragmentShader) + { + auto parser = MakeParser(basicVertexShader, "bar", ""); + EXPECT_FALSE(parser->valid()); + EXPECT_THAT(parser->getErrors(), testing::HasSubstr("Parsing Error")); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, acceptsEmptyGeometryShader) + { + auto parser = MakeParser(basicVertexShader, basicFragmentShader, ""); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, rejectsBrokenGeometryShader) + { + auto parser = MakeParser(basicVertexShader, basicFragmentShader, "bar"); + EXPECT_FALSE(parser->valid()); + EXPECT_THAT(parser->getErrors(), testing::HasSubstr("Parsing Error")); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, usesProvidedDefines) + { + const char* vertexShader = + "void main(void)\n" + "{\n" + " gl_Position = DEFINE_ZERO;\n" + "}\n"; + const char* fragmentShader = + "void main(void)\n" + "{\n" + " gl_FragColor = DEFINE_ONE;\n" + "}\n"; + std::vector compilerDefines; + compilerDefines.emplace_back("DEFINE_ZERO vec4(0.0)"); + compilerDefines.emplace_back("DEFINE_ONE vec4(1.0)"); + + auto parser = MakeParser(vertexShader, fragmentShader, "", compilerDefines); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, rejectsMissingNewlines) + { + const auto vertexShader = "#version 320 es void main(void) { gl_Position = vec4(0.0); }"; + auto parser = MakeParser(vertexShader, basicFragmentShader, ""); + EXPECT_FALSE(parser->valid()); + EXPECT_THAT(parser->getErrors(), testing::HasSubstr("Shader contains #version without newline")); + EXPECT_TRUE(parser->generateWarnings().empty()); + } + + TEST_F(AGlslParser, warnUnusedVertexUniform) + { + const auto vertexShader = R"SHADER( + #version 320 es + uniform vec2 foo; + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + auto parser = MakeParser(vertexShader, basicFragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::UnusedUniform); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Unused [uniform]: 'foo'")); + } + + TEST_F(AGlslParser, warnUnusedVertexInput) + { + const auto vertexShader = R"SHADER( + #version 320 es + in vec2 foo; + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + auto parser = MakeParser(vertexShader, basicFragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::UnusedVarying); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Unused [in]: 'foo'")); + } + + TEST_F(AGlslParser, warnUnusedVertexOutput) + { + const auto vertexShader = R"SHADER( + #version 320 es + out vec2 foo; + void main(void) + { + gl_Position = vec4(0.0); + } + )SHADER"; + auto parser = MakeParser(vertexShader, basicFragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(2u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::UnusedVarying); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Unused [out]: 'foo'")); + EXPECT_EQ(warnings[1].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_THAT(warnings[1].msg, testing::StartsWith("Vertex shader output 'foo' is not input in fragment shader")); + } + + TEST_F(AGlslParser, warnUnusedVertexOutputInFragmentShader) + { + const auto vertexShader = R"SHADER( + #version 320 es + out vec2 foo; + void main(void) + { + foo = vec2(1.0); + gl_Position = vec4(0.0); + } + )SHADER"; + auto parser = MakeParser(vertexShader, basicFragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Vertex shader output 'foo' is not input in fragment shader")); + } + + TEST_F(AGlslParser, warnUnusedFragmentUniform) + { + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + uniform highp float foo; + void main(void) + { + colorOut = vec4(0.0); + })SHADER"; + + auto parser = MakeParser(basicVertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Fragment); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::UnusedUniform); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Unused [uniform]: 'foo'")); + } + + TEST_F(AGlslParser, warnUnusedFragmentInput) + { + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + in lowp float foo; + void main(void) + { + colorOut = vec4(0.0); + })SHADER"; + + auto parser = MakeParser(basicVertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(2u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Fragment); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::UnusedVarying); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Unused [in]: 'foo'")); + EXPECT_EQ(warnings[1].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_THAT(warnings[1].msg, testing::StartsWith("Fragment shader input 'foo' is not output in vertex shader")); + } + + TEST_F(AGlslParser, warnUnusedFragmentInputInVertexShader) + { + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + in lowp float foo; + void main(void) + { + colorOut = vec4(0.0); + colorOut.a = foo; + })SHADER"; + + auto parser = MakeParser(basicVertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Fragment); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_THAT(warnings[0].msg, testing::StartsWith("Fragment shader input 'foo' is not output in vertex shader")); + } + + TEST_F(AGlslParser, noWarningForExplicitLayoutLocation) + { + const auto vertexShader = R"SHADER( + #version 320 es + layout(location = 1) out vec2 foo1; + layout(location = 0) out float foo0; + void main(void) + { + foo0 = 0.0; + foo1 = vec2(1.0); + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + layout(location = 1) in highp vec2 bar; + layout(location = 0) in highp float bar0; + void main(void) + { + colorOut = vec4(0.0); + colorOut.a = bar.x; + colorOut.r = bar0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(0u, warnings.size()); + } + + TEST_F(AGlslParser, warnVertexLocationNotFoundButNameMatches) + { + const auto vertexShader = R"SHADER( + #version 320 es + layout(location = 1) out vec2 foo1; + out float foo0; + void main(void) + { + foo0 = 0.0; + foo1 = vec2(1.0); + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + layout(location = 1) in highp vec2 bar; + layout(location = 0) in highp float foo0; + void main(void) + { + colorOut = vec4(0.0); + colorOut.a = bar.x; + colorOut.r = foo0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Fragment); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_EQ(warnings[0].msg, "Fragment shader input 'layout(location = 0) foo0' is not output in vertex shader"); + } + + TEST_F(AGlslParser, warnFragmentLocationNotFoundButNameMatches) + { + const auto vertexShader = R"SHADER( + #version 320 es + layout(location = 1) out vec2 foo1; + layout(location = 0) out float foo0; + void main(void) + { + foo0 = 0.0; + foo1 = vec2(1.0); + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + layout(location = 1) in highp vec2 bar; + in highp float foo0; + void main(void) + { + colorOut = vec4(0.0); + colorOut.a = bar.x; + colorOut.r = foo0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_EQ(warnings[0].msg, "Vertex shader output 'layout(location = 0) foo0' is not input in fragment shader"); + } + + TEST_F(AGlslParser, warnTypeMismatch) + { + const auto vertexShader = R"SHADER( + #version 320 es + out vec2 foo0; + void main(void) + { + foo0 = vec2(0.0); + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + in highp float foo0; + void main(void) + { + colorOut = vec4(0.0); + colorOut.r = foo0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + ASSERT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + ASSERT_LE(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::InterfaceMismatch); + EXPECT_EQ(warnings[0].msg, "Type mismatch: 'foo0'. (Vertex: smooth out highp 2-component vector of float, Fragment: smooth in highp float)"); + } + + TEST_F(AGlslParser, warnPrecisionMismatch) + { + const auto vertexShader = R"SHADER( + #version 320 es + out lowp float foo0; + void main(void) + { + foo0 = 0.0; + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER( + #version 320 es + out lowp vec4 colorOut; + in highp float foo0; + void main(void) + { + colorOut = vec4(0.0); + colorOut.r = foo0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + ASSERT_EQ("", parser->getErrors()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(1u, warnings.size()); + ASSERT_LE(1u, warnings.size()); + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::PrecisionMismatch); + EXPECT_EQ(warnings[0].msg, "Precision mismatch: 'foo0'. (Vertex: smooth out lowp float, Fragment: smooth in highp float)"); + } + + TEST_F(AGlslParser, warnUniformPrecisionMismatch) + { + const auto vertexShader = R"SHADER( + #version 320 es + precision mediump float; + out float foo0; + uniform vec2 u_bar; + void main(void) + { + foo0 = u_bar.x; + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER( + #version 320 es + precision highp float; + out vec4 colorOut; + in float foo0; + uniform vec2 u_bar; + void main(void) + { + colorOut = vec4(u_bar.y); + colorOut.r = foo0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->getProgram() != nullptr); + ASSERT_TRUE(parser->getErrors().empty()); + auto warnings = parser->generateWarnings(); + EXPECT_EQ(2u, warnings.size()); + ASSERT_LE(2u, warnings.size()); + std::sort(warnings.begin(), warnings.end(), [](const auto& w1, const auto& w2) { return w1.msg < w2.msg; }); // order might be unstable + EXPECT_EQ(warnings[0].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[0].category, EShaderWarningCategory::PrecisionMismatch); + EXPECT_EQ(warnings[0].msg, "Precision mismatch: 'foo0'. (Vertex: smooth out mediump float, Fragment: smooth in highp float)"); + EXPECT_EQ(warnings[1].stage, EShaderStage::Vertex); + EXPECT_EQ(warnings[1].category, EShaderWarningCategory::PrecisionMismatch); + EXPECT_EQ(warnings[1].msg, "Precision mismatch: 'u_bar'. (Vertex: uniform mediump 2-component vector of float, Fragment: uniform highp 2-component vector of float)"); + } + + TEST_F(AGlslParser, shadersFromParts) + { + const auto vertexShader = R"SHADER(#version 320 es + out lowp float foo0; + void main(void) + { + foo0 = 0.0; + gl_Position = vec4(0.0); + } + )SHADER"; + + const auto fragmentShader = R"SHADER(#version 320 es + out lowp vec4 colorOut; + in highp float foo0; + void main(void) + { + colorOut = vec4(0.0); + colorOut.r = foo0; + })SHADER"; + + auto parser = MakeParser(vertexShader, fragmentShader); + EXPECT_TRUE(parser->valid()); + EXPECT_EQ(vertexShader, parser->getVertexShader()); + EXPECT_EQ(fragmentShader, parser->getFragmentShader()); + EXPECT_EQ("", parser->getGeometryShader()); + } +} diff --git a/client/test/IteratorTest.cpp b/tests/unittests/client/IteratorTest.cpp similarity index 90% rename from client/test/IteratorTest.cpp rename to tests/unittests/client/IteratorTest.cpp index 79f1c9f95..b3aea590c 100644 --- a/client/test/IteratorTest.cpp +++ b/tests/unittests/client/IteratorTest.cpp @@ -10,18 +10,18 @@ #include #include "ClientTestUtils.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/MeshNode.h" -#include "IteratorImpl.h" -#include "RamsesObjectVector.h" +#include "impl/IteratorImpl.h" +#include "impl/RamsesObjectVector.h" -namespace ramses +namespace ramses::internal { class IteratorTest : public LocalTestClientWithScene, public testing::Test { public: - IteratorTest() : LocalTestClientWithScene() + IteratorTest() { obj1 = &this->createObject("object1"); obj2 = &this->createObject("object2"); diff --git a/client/test/MeshNodeTest.cpp b/tests/unittests/client/MeshNodeTest.cpp similarity index 62% rename from client/test/MeshNodeTest.cpp rename to tests/unittests/client/MeshNodeTest.cpp index 798fba976..3cdbefdf1 100644 --- a/client/test/MeshNodeTest.cpp +++ b/tests/unittests/client/MeshNodeTest.cpp @@ -8,22 +8,21 @@ #include -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/ArrayResource.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/ArrayResource.h" #include "ClientTestUtils.h" -#include "GeometryBindingImpl.h" -#include "AppearanceImpl.h" -#include "MeshNodeImpl.h" -#include "ArrayResourceImpl.h" -#include "Resource/EffectResource.h" -#include "EffectImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/AppearanceImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/ArrayResourceImpl.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "impl/EffectImpl.h" using namespace testing; -using namespace ramses_internal; -namespace ramses +namespace ramses::internal { class MeshNodeTest : public LocalTestClientWithScene, public testing::Test { @@ -35,7 +34,7 @@ namespace ramses AssertionResult renderableDataInstancesAreSetInScene(DataInstanceHandle uniformDataInstance, DataInstanceHandle attributeDataInstance) { - const RenderableHandle renderableHandle = m_meshNode->m_impl.getRenderableHandle(); + const RenderableHandle renderableHandle = m_meshNode->impl().getRenderableHandle(); const auto actualDataInstances = m_internalScene.getRenderable(renderableHandle).dataInstances; if (uniformDataInstance != actualDataInstances[ERenderableDataSlotType_Uniforms]) { @@ -50,8 +49,8 @@ namespace ramses AssertionResult effectResourceIsSetInScene(const Appearance& appearance) { - const EffectImpl* effect = appearance.m_impl.getEffectImpl(); - const Renderable renderable = m_internalScene.getRenderable(m_meshNode->m_impl.getRenderableHandle()); + const EffectImpl* effect = appearance.impl().getEffectImpl(); + const Renderable renderable = m_internalScene.getRenderable(m_meshNode->impl().getRenderableHandle()); const DataLayoutHandle& dataLayout = m_internalScene.getLayoutOfDataInstance(renderable.dataInstances[ERenderableDataSlotType_Geometry]); const ResourceContentHash& effectHash = m_internalScene.getDataLayout(dataLayout).getEffectHash(); @@ -62,16 +61,16 @@ namespace ramses AssertionResult renderableStateIsSetInScene(const Appearance& appearance) { - const RenderableHandle renderableHandle = m_meshNode->m_impl.getRenderableHandle(); + const RenderableHandle renderableHandle = m_meshNode->impl().getRenderableHandle(); - EXPECT_EQ(appearance.m_impl.getRenderStateHandle(), m_internalScene.getRenderable(renderableHandle).renderState); + EXPECT_EQ(appearance.impl().getRenderStateHandle(), m_internalScene.getRenderable(renderableHandle).renderState); return AssertionSuccess(); } - AssertionResult meshNodeUniformAndAttributesIsSetInScene(const Appearance& appearance, const GeometryBinding& geometry) + AssertionResult meshNodeUniformAndAttributesIsSetInScene(const Appearance& appearance, const Geometry& geometry) { - EXPECT_TRUE(renderableDataInstancesAreSetInScene(appearance.m_impl.getUniformDataInstance(), geometry.m_impl.getAttributeDataInstance())); + EXPECT_TRUE(renderableDataInstancesAreSetInScene(appearance.impl().getUniformDataInstance(), geometry.impl().getAttributeDataInstance())); EXPECT_TRUE(effectResourceIsSetInScene(appearance)); EXPECT_TRUE(renderableStateIsSetInScene(appearance)); @@ -85,127 +84,139 @@ namespace ramses m_meshNode->setAppearance(*appearance); } - void setAGeometryBindingForTesting(bool useIndices = true) + void setAGeometryForTesting(bool useIndices = true) { - GeometryBinding& geometry = createValidGeometry(nullptr, useIndices); - m_meshNode->setGeometryBinding(geometry); + Geometry& geometry = createValidGeometry(nullptr, useIndices); + m_meshNode->setGeometry(geometry); } - MeshNode* m_meshNode; + MeshNode* m_meshNode{nullptr}; }; TEST_F(MeshNodeTest, canValidate) { setAnAppearanceForTesting(); - setAGeometryBindingForTesting(); + setAGeometryForTesting(); - EXPECT_EQ(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(MeshNodeTest, failsValidationIfNoAppearanceWasSet) { - setAGeometryBindingForTesting(); + setAGeometryForTesting(); - EXPECT_NE(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(MeshNodeTest, failsValidationIfNoGeometryWasSet) { setAnAppearanceForTesting(); - EXPECT_NE(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(MeshNodeTest, failsValidationIfStartIndexExeedsSizeOfIndexArray) { setAnAppearanceForTesting(); - setAGeometryBindingForTesting(); + setAGeometryForTesting(); m_meshNode->setStartIndex(2); - EXPECT_NE(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(MeshNodeTest, failsValidationIfIndexCountExeedsSizeOfIndexArray) { setAnAppearanceForTesting(); - setAGeometryBindingForTesting(); + setAGeometryForTesting(); m_meshNode->setIndexCount(2); - EXPECT_NE(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(MeshNodeTest, failsValidationIfStartIndexPlusIndexCountExeedsSizeOfIndexArray) { setAnAppearanceForTesting(); - setAGeometryBindingForTesting(); + setAGeometryForTesting(); m_meshNode->setStartIndex(1); m_meshNode->setIndexCount(1); - EXPECT_NE(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(MeshNodeTest, getsNullPointerForInitalAppearanceAndGeometry) { EXPECT_EQ(nullptr, m_meshNode->getAppearance()); - EXPECT_EQ(nullptr, m_meshNode->getGeometryBinding()); + EXPECT_EQ(nullptr, m_meshNode->getGeometry()); } TEST_F(MeshNodeTest, setsAndGetsSameAppearance) { Appearance* appearance = m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance)); EXPECT_EQ(appearance, m_meshNode->getAppearance()); } TEST_F(MeshNodeTest, reportsErrorWhenSetAppearanceFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(sceneId_t(12u)); Appearance* appearance = anotherScene.createAppearance(*TestEffects::CreateTestEffect(anotherScene), "appearance"); ASSERT_TRUE(appearance != nullptr); - EXPECT_NE(StatusOK, m_meshNode->setAppearance(*appearance)); + EXPECT_FALSE(m_meshNode->setAppearance(*appearance)); client.destroy(anotherScene); } TEST_F(MeshNodeTest, setsAndGetsSameGeometry) { - GeometryBinding& geometry = createValidGeometry(); + Geometry& geometry = createValidGeometry(); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); + EXPECT_TRUE(geometry.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry)); - EXPECT_EQ(&geometry, m_meshNode->getGeometryBinding()); + EXPECT_EQ(&geometry, m_meshNode->getGeometry()); } TEST_F(MeshNodeTest, reportsErrorWhenSetGeometryFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(sceneId_t(12u)); - GeometryBinding* geometry = anotherScene.createGeometryBinding(*TestEffects::CreateTestEffect(anotherScene), "geometry"); + Geometry* geometry = anotherScene.createGeometry(*TestEffects::CreateTestEffect(anotherScene), "geometry"); ASSERT_TRUE(geometry != nullptr); const uint16_t indices = 0; ArrayResource& indexArray = *anotherScene.createArrayResource(1u, &indices); - EXPECT_EQ(StatusOK, geometry->setIndices(indexArray)); + EXPECT_TRUE(geometry->setIndices(indexArray)); - EXPECT_NE(StatusOK, m_meshNode->setGeometryBinding(*geometry)); + EXPECT_FALSE(m_meshNode->setGeometry(*geometry)); client.destroy(anotherScene); } TEST_F(MeshNodeTest, settingGeometryWithIndicesSetsIndicesCount) { - GeometryBinding& geometry = createValidGeometry(); + Geometry& geometry = createValidGeometry(); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry)); - EXPECT_EQ(indexArray.m_impl.getElementCount(), m_meshNode->getIndexCount()); + EXPECT_TRUE(geometry.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry)); + EXPECT_EQ(indexArray.impl().getElementCount(), m_meshNode->getIndexCount()); } TEST_F(MeshNodeTest, settingGeometryHavingDifferentEffectFromAppearanceReportsError) @@ -214,12 +225,12 @@ namespace ramses Effect* effect2 = TestEffects::CreateDifferentTestEffect(m_scene); Appearance* appearance = m_scene.createAppearance(*effect1, "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance)); - GeometryBinding& geometry = *m_scene.createGeometryBinding(*effect2); + Geometry& geometry = *m_scene.createGeometry(*effect2); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); - EXPECT_NE(StatusOK, m_meshNode->setGeometryBinding(geometry)); + EXPECT_TRUE(geometry.setIndices(indexArray)); + EXPECT_FALSE(m_meshNode->setGeometry(geometry)); } TEST_F(MeshNodeTest, settingAppearanceHavingDifferentEffectFromGeometryReportsError) @@ -227,26 +238,26 @@ namespace ramses Effect* effect1 = TestEffects::CreateTestEffect(m_scene); Effect* effect2 = TestEffects::CreateDifferentTestEffect(m_scene); - GeometryBinding& geometry = *m_scene.createGeometryBinding(*effect2); + Geometry& geometry = *m_scene.createGeometry(*effect2); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry)); + EXPECT_TRUE(geometry.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry)); Appearance* appearance = m_scene.createAppearance(*effect1, "appearance"); - EXPECT_NE(StatusOK, m_meshNode->setAppearance(*appearance)); + EXPECT_FALSE(m_meshNode->setAppearance(*appearance)); } TEST_F(MeshNodeTest, setsAppearanceMultipleTimesAndChecksInScene) { - GeometryBinding& geometry = createValidGeometry(TestEffects::CreateTestEffect(m_scene)); + Geometry& geometry = createValidGeometry(TestEffects::CreateTestEffect(m_scene)); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry)); + EXPECT_TRUE(geometry.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry)); for (int i = 0; i < 3; i++) { Appearance* appearance = m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance)); EXPECT_EQ(appearance, m_meshNode->getAppearance()); EXPECT_TRUE(meshNodeUniformAndAttributesIsSetInScene(*appearance, geometry)); } @@ -255,14 +266,14 @@ namespace ramses TEST_F(MeshNodeTest, setsGeometryMultipleTimesAndChecksInScene) { Appearance* appearance = m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance)); for (int i = 0; i < 3; i++) { - GeometryBinding& geometry = createValidGeometry(TestEffects::CreateTestEffect(m_scene)); + Geometry& geometry = createValidGeometry(TestEffects::CreateTestEffect(m_scene)); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry)); + EXPECT_TRUE(geometry.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry)); EXPECT_EQ(appearance, m_meshNode->getAppearance()); EXPECT_TRUE(meshNodeUniformAndAttributesIsSetInScene(*appearance, geometry)); } @@ -271,18 +282,18 @@ namespace ramses TEST_F(MeshNodeTest, removeAppearanceAndGeometryRemovesTheseFromMeshNode) { Appearance* appearance = this->m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance)); - GeometryBinding& geometry = createValidGeometry(TestEffects::CreateTestEffect(m_scene)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance)); + Geometry& geometry = createValidGeometry(TestEffects::CreateTestEffect(m_scene)); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry)); + EXPECT_TRUE(geometry.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry)); - EXPECT_EQ(StatusOK, m_meshNode->removeAppearanceAndGeometry()); + EXPECT_TRUE(m_meshNode->removeAppearanceAndGeometry()); EXPECT_TRUE(nullptr == m_meshNode->getAppearance()); - EXPECT_TRUE(nullptr == m_meshNode->getGeometryBinding()); + EXPECT_TRUE(nullptr == m_meshNode->getGeometry()); - const RenderableHandle renderableHandle = m_meshNode->m_impl.getRenderableHandle(); + const RenderableHandle renderableHandle = m_meshNode->impl().getRenderableHandle(); EXPECT_FALSE(this->m_internalScene.getRenderable(renderableHandle).dataInstances[ERenderableDataSlotType_Uniforms].isValid()); EXPECT_FALSE(this->m_internalScene.getRenderable(renderableHandle).dataInstances[ERenderableDataSlotType_Geometry].isValid()); EXPECT_FALSE(this->m_internalScene.getRenderable(renderableHandle).renderState.isValid()); @@ -292,34 +303,34 @@ namespace ramses { // first set of appearance/geometry Effect* effect1 = TestEffects::CreateTestEffect(m_scene); - GeometryBinding& geometry1 = createValidGeometry(effect1); + Geometry& geometry1 = createValidGeometry(effect1); ArrayResource& indexArray = createValidIndexArray(); - EXPECT_EQ(StatusOK, geometry1.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry1)); + EXPECT_TRUE(geometry1.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry1)); Appearance* appearance1 = this->m_scene.createAppearance(*effect1, "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance1)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance1)); // reset - EXPECT_EQ(StatusOK, m_meshNode->removeAppearanceAndGeometry()); + EXPECT_TRUE(m_meshNode->removeAppearanceAndGeometry()); // second set of appearance/geometry not compatible with the previous ones Effect* effect2 = TestEffects::CreateDifferentTestEffect(m_scene); - GeometryBinding& geometry2 = createValidGeometry(effect2); - EXPECT_EQ(StatusOK, geometry2.setIndices(indexArray)); - EXPECT_EQ(StatusOK, m_meshNode->setGeometryBinding(geometry2)); + Geometry& geometry2 = createValidGeometry(effect2); + EXPECT_TRUE(geometry2.setIndices(indexArray)); + EXPECT_TRUE(m_meshNode->setGeometry(geometry2)); Appearance* appearance2 = this->m_scene.createAppearance(*effect2, "appearance"); - EXPECT_EQ(StatusOK, m_meshNode->setAppearance(*appearance2)); + EXPECT_TRUE(m_meshNode->setAppearance(*appearance2)); } TEST_F(MeshNodeTest, checksIfMeshNodeIsInitiallyVisible) { - EXPECT_EQ(m_meshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_meshNode->impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(MeshNodeTest, setsAndGetsSameStartIndex) { uint32_t startIndex = 1; - EXPECT_EQ(StatusOK, m_meshNode->setStartIndex(startIndex)); + EXPECT_TRUE(m_meshNode->setStartIndex(startIndex)); EXPECT_EQ(startIndex, m_meshNode->getStartIndex()); } @@ -327,7 +338,7 @@ namespace ramses TEST_F(MeshNodeTest, setsAndGetsSameStartVertex) { uint32_t startVertex = 231u; - EXPECT_EQ(StatusOK, m_meshNode->setStartVertex(startVertex)); + EXPECT_TRUE(m_meshNode->setStartVertex(startVertex)); EXPECT_EQ(startVertex, m_meshNode->getStartVertex()); } @@ -335,7 +346,7 @@ namespace ramses TEST_F(MeshNodeTest, setsAndGetsSameIndexCount) { const uint32_t indexCount = 1u; - EXPECT_EQ(StatusOK, m_meshNode->setIndexCount(indexCount)); + EXPECT_TRUE(m_meshNode->setIndexCount(indexCount)); EXPECT_EQ(indexCount, m_meshNode->getIndexCount()); } @@ -343,31 +354,30 @@ namespace ramses TEST_F(MeshNodeTest, setsAndGetsSameInstanceCount) { const uint32_t instanceCount(501u); - EXPECT_EQ(StatusOK, m_meshNode->setInstanceCount(instanceCount)); + EXPECT_TRUE(m_meshNode->setInstanceCount(instanceCount)); EXPECT_EQ(instanceCount, m_meshNode->getInstanceCount()); } - TEST_F(MeshNodeTest, doesNotAllowNoInstances) - { - EXPECT_NE(StatusOK, m_meshNode->setInstanceCount(0u)); - } - TEST_F(MeshNodeTest, succeedsValidationIfNotUsingIndexArray) { setAnAppearanceForTesting(); - setAGeometryBindingForTesting(false); + setAGeometryForTesting(false); m_meshNode->setIndexCount(1); - EXPECT_EQ(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(MeshNodeTest, failsValidationIfNotUsingIndexArrayAndIndexCountNotSet) { setAnAppearanceForTesting(); - setAGeometryBindingForTesting(false); + setAGeometryForTesting(false); - EXPECT_NE(StatusOK, m_meshNode->validate()); + ValidationReport report; + m_meshNode->validate(report); + EXPECT_TRUE(report.hasError()); } } diff --git a/tests/unittests/client/MockActionCollector.h b/tests/unittests/client/MockActionCollector.h new file mode 100644 index 000000000..1885957c9 --- /dev/null +++ b/tests/unittests/client/MockActionCollector.h @@ -0,0 +1,81 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "gmock/gmock.h" +#include "internal/Components/ISceneGraphConsumerComponent.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "SceneRendererHandlerMock.h" +#include "internal/Components/SceneUpdate.h" + +namespace ramses::internal +{ + class MockActionCollector : public ramses::internal::SceneRendererHandlerMock + { + public: + void init(ramses::internal::ISceneGraphConsumerComponent& sceneGraphConsumer) + { + m_sceneGraphConsumer = &sceneGraphConsumer; + } + + void resetCollecting() + { + m_collectedActions.clear(); + m_numReceivedActionLists = 0u; + } + + uint32_t getNumberOfActions() const + { + return m_collectedActions.numberOfActions(); + } + + ramses::internal::SceneActionCollection getCopyOfCollectedActions() + { + return m_collectedActions.copy(); + } + + uint32_t getNumReceivedActionLists() const + { + return m_numReceivedActionLists; + } + + private: + ramses::internal::SceneActionCollection m_collectedActions{}; + uint32_t m_numReceivedActionLists{0}; + + ramses::internal::ISceneGraphConsumerComponent* m_sceneGraphConsumer{nullptr}; + + void handleInitializeScene(const ramses::internal::SceneInfo& sceneInfo, const ramses::internal::Guid& providerID) override + { + ramses::internal::SceneRendererHandlerMock::handleInitializeScene(sceneInfo, providerID); + } + + void handleSceneUpdate(const ramses::internal::SceneId& sceneId, ramses::internal::SceneUpdate&& sceneUpdate, const ramses::internal::Guid& providerID) override + { + m_collectedActions.append(sceneUpdate.actions); + ramses::internal::SceneRendererHandlerMock::handleSceneUpdate(sceneId, std::move(sceneUpdate), providerID); + ++m_numReceivedActionLists; + } + + void handleNewSceneAvailable(const ramses::internal::SceneInfo& newScene, const ramses::internal::Guid& providerID) override + { + ramses::internal::SceneRendererHandlerMock::handleNewSceneAvailable(newScene, providerID); + if (m_sceneGraphConsumer != nullptr) + { + m_sceneGraphConsumer->subscribeScene(providerID, newScene.sceneID); + } + } + + void handleSceneBecameUnavailable(const ramses::internal::SceneId& unavailableScene, const ramses::internal::Guid& providerID) override + { + ramses::internal::SceneRendererHandlerMock::handleSceneBecameUnavailable(unavailableScene, providerID); + } + }; +} diff --git a/client/test/NodeLazyTransformTest.cpp b/tests/unittests/client/NodeLazyTransformTest.cpp similarity index 63% rename from client/test/NodeLazyTransformTest.cpp rename to tests/unittests/client/NodeLazyTransformTest.cpp index e10e2b1d6..4f1790a77 100644 --- a/client/test/NodeLazyTransformTest.cpp +++ b/tests/unittests/client/NodeLazyTransformTest.cpp @@ -9,13 +9,13 @@ #include #include "ClientTestUtils.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PickableObject.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PickableObject.h" #include "RamsesObjectTestTypes.h" -namespace ramses +namespace ramses::internal { template class NodeLazyTransformTest : public LocalTestClientWithScene, public testing::Test @@ -29,7 +29,7 @@ namespace ramses uint32_t getActualTransformCount() const { uint32_t count = 0u; - for (ramses_internal::TransformHandle handle(0u); handle < this->m_internalScene.getTransformCount(); ++handle) + for (ramses::internal::TransformHandle handle(0u); handle < this->m_internalScene.getTransformCount(); ++handle) { if (this->m_internalScene.isTransformAllocated(handle)) { @@ -40,7 +40,7 @@ namespace ramses return count; } - T* m_node; + T* m_node{nullptr}; }; TYPED_TEST_SUITE(NodeLazyTransformTest, NodeTypes); @@ -52,13 +52,13 @@ namespace ramses TYPED_TEST(NodeLazyTransformTest, callingSetterWithIdentityCreatesNoTransform) { - EXPECT_EQ(this->m_node->setTranslation({0.0f, 0.0f, 0.0f}), StatusOK); - EXPECT_EQ(this->m_node->translate({0.0f, 0.0f, 0.0f}), StatusOK); - EXPECT_EQ(this->m_node->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_ZYX), StatusOK); - EXPECT_EQ(this->m_node->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_XYZ), StatusOK); - EXPECT_EQ(this->m_node->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_ZYZ), StatusOK); - EXPECT_EQ(this->m_node->setScaling({1.0f, 1.0f, 1.0f}), StatusOK); - EXPECT_EQ(this->m_node->scale({1.0f, 1.0f, 1.0f}), StatusOK); + EXPECT_TRUE(this->m_node->setTranslation({0.0f, 0.0f, 0.0f})); + EXPECT_TRUE(this->m_node->translate({0.0f, 0.0f, 0.0f})); + EXPECT_TRUE(this->m_node->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_ZYX)); + EXPECT_TRUE(this->m_node->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_XYZ)); + EXPECT_TRUE(this->m_node->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_ZYZ)); + EXPECT_TRUE(this->m_node->setScaling({1.0f, 1.0f, 1.0f})); + EXPECT_TRUE(this->m_node->scale({1.0f, 1.0f, 1.0f})); EXPECT_EQ(this->getActualTransformCount(), 0u); } @@ -67,61 +67,61 @@ namespace ramses { vec3f value; - EXPECT_EQ(this->m_node->getTranslation(value), StatusOK); + EXPECT_TRUE(this->m_node->getTranslation(value)); EXPECT_EQ(value, vec3f(0.f)); - EXPECT_EQ(this->m_node->getRotation(value), StatusOK); + EXPECT_TRUE(this->m_node->getRotation(value)); EXPECT_EQ(value, vec3f(0.f)); quat q; - EXPECT_EQ(this->m_node->getRotation(q), StatusOK); + EXPECT_TRUE(this->m_node->getRotation(q)); EXPECT_EQ(q, glm::identity()); - EXPECT_EQ(this->m_node->getScaling(value), StatusOK); + EXPECT_TRUE(this->m_node->getScaling(value)); EXPECT_EQ(value, vec3f(1.f)); } TYPED_TEST(NodeLazyTransformTest, callingSetTranslationWithValueCreatesTransform) { EXPECT_EQ(this->getActualTransformCount(), 0u); - EXPECT_EQ(this->m_node->setTranslation({1.0f, 2.0f, 3.0f}), StatusOK); + EXPECT_TRUE(this->m_node->setTranslation({1.0f, 2.0f, 3.0f})); EXPECT_EQ(this->getActualTransformCount(), 1u); } TYPED_TEST(NodeLazyTransformTest, callingTranslateWithValueCreatesTransform) { EXPECT_EQ(this->getActualTransformCount(), 0u); - EXPECT_EQ(this->m_node->translate({1.0f, 2.0f, 3.0f}), StatusOK); + EXPECT_TRUE(this->m_node->translate({1.0f, 2.0f, 3.0f})); EXPECT_EQ(this->getActualTransformCount(), 1u); } TYPED_TEST(NodeLazyTransformTest, callingSetRotationWithValueCreatesTransform) { EXPECT_EQ(this->getActualTransformCount(), 0u); - EXPECT_EQ(this->m_node->setRotation({1.0f, 2.0f, 3.0f}, ERotationType::Euler_YXZ), StatusOK); + EXPECT_TRUE(this->m_node->setRotation({1.0f, 2.0f, 3.0f}, ERotationType::Euler_YXZ)); EXPECT_EQ(this->getActualTransformCount(), 1u); } TYPED_TEST(NodeLazyTransformTest, callingSetScalingWithValueCreatesTransform) { EXPECT_EQ(this->getActualTransformCount(), 0u); - EXPECT_EQ(this->m_node->setScaling({1.0f, 2.0f, 3.0f}), StatusOK); + EXPECT_TRUE(this->m_node->setScaling({1.0f, 2.0f, 3.0f})); EXPECT_EQ(this->getActualTransformCount(), 1u); } TYPED_TEST(NodeLazyTransformTest, callingScaleWithValueCreatesTransform) { EXPECT_EQ(this->getActualTransformCount(), 0u); - EXPECT_EQ(this->m_node->scale({1.0f, 2.0f, 3.0f}), StatusOK); + EXPECT_TRUE(this->m_node->scale({1.0f, 2.0f, 3.0f})); EXPECT_EQ(this->getActualTransformCount(), 1u); } TYPED_TEST(NodeLazyTransformTest, destructionDestroysTransform) { - EXPECT_EQ(this->m_node->setTranslation({1.0f, 2.0f, 3.0f}), StatusOK); + EXPECT_TRUE(this->m_node->setTranslation({1.0f, 2.0f, 3.0f})); EXPECT_EQ(this->getActualTransformCount(), 1u); - this->m_scene.destroy(*this->m_node); + EXPECT_TRUE(this->m_scene.destroy(*this->m_node)); EXPECT_EQ(this->getActualTransformCount(), 0u); } } diff --git a/client/test/NodeTest.cpp b/tests/unittests/client/NodeTest.cpp similarity index 70% rename from client/test/NodeTest.cpp rename to tests/unittests/client/NodeTest.cpp index d9aadf0a2..6d9d6ff03 100644 --- a/client/test/NodeTest.cpp +++ b/tests/unittests/client/NodeTest.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-utils.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/ramses-utils.h" -#include "NodeImpl.h" +#include "impl/NodeImpl.h" #include "ClientTestUtils.h" #include "RamsesObjectTestTypes.h" -#include "Math3d/Rotation.h" +#include "internal/Core/Math3d/Rotation.h" #include "glm/gtx/transform.hpp" #include using namespace testing; -namespace ramses +namespace ramses::internal { template class NodeTest : public LocalTestClientWithScene, public testing::Test @@ -43,7 +43,7 @@ namespace ramses EXPECT_EQ(0u, parent.getChildCount()); EXPECT_FALSE(parent.hasChild()); - EXPECT_EQ(StatusOK, parent.addChild(this->createNode("child"))); + EXPECT_TRUE(parent.addChild(this->createNode("child"))); EXPECT_TRUE(parent.hasChild()); } @@ -54,7 +54,7 @@ namespace ramses EXPECT_EQ(nullptr, child.getParent()); EXPECT_FALSE(child.hasParent()); - EXPECT_EQ(StatusOK, child.setParent(parent)); + EXPECT_TRUE(child.setParent(parent)); EXPECT_TRUE(child.hasParent()); } @@ -65,7 +65,7 @@ namespace ramses for (uint32_t i = 0; i < 10; i++) { - EXPECT_EQ(StatusOK, parent.addChild(this->createNode("child"))); + EXPECT_TRUE(parent.addChild(this->createNode("child"))); EXPECT_EQ(i + 1, parent.getChildCount()); } @@ -74,12 +74,12 @@ namespace ramses TYPED_TEST(NodeTest, shouldNotAddNodeFromOneSceneAsChildToNodeInOtherScene) { - Scene& otherScene = *this->client.createScene(sceneId_t(1234u)); + ramses::Scene& otherScene = *this->client.createScene(sceneId_t(1234u)); CreationHelper otherSceneCreationHelper(&otherScene, &this->client); Node& parent = *otherSceneCreationHelper.template createObjectOfType("parent"); EXPECT_EQ(0u, parent.getChildCount()); - EXPECT_NE(StatusOK, parent.addChild(this->createNode("child"))); + EXPECT_FALSE(parent.addChild(this->createNode("child"))); EXPECT_EQ(0u, parent.getChildCount()); EXPECT_FALSE(parent.hasChild()); @@ -89,7 +89,7 @@ namespace ramses { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); EXPECT_EQ(&parent, child.getParent()); } @@ -101,7 +101,7 @@ namespace ramses Node& child = this->createNode("child"); const Node& constChild = child; - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); EXPECT_EQ(constParent.getChild(0), parent.getChild(0)); EXPECT_EQ(constParent.getParent(), parent.getParent()); EXPECT_EQ(constChild.getParent(), child.getParent()); @@ -111,7 +111,7 @@ namespace ramses { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); ASSERT_TRUE(parent.getChild(0) != nullptr); ASSERT_TRUE(child.getParent() != nullptr); @@ -126,7 +126,7 @@ namespace ramses { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); ASSERT_TRUE(parent.getChild(0) != nullptr); ASSERT_TRUE(child.getParent() != nullptr); @@ -141,12 +141,12 @@ namespace ramses { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); ASSERT_TRUE(parent.getChild(0) != nullptr); ASSERT_TRUE(child.getParent() != nullptr); - EXPECT_EQ(StatusOK, parent.removeChild(child)); + EXPECT_TRUE(parent.removeChild(child)); EXPECT_EQ(nullptr, parent.getChild(0)); EXPECT_EQ(nullptr, child.getParent()); @@ -156,7 +156,7 @@ namespace ramses { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, child.setParent(parent)); + EXPECT_TRUE(child.setParent(parent)); EXPECT_EQ(&child, parent.getChild(0)); } @@ -164,12 +164,12 @@ namespace ramses { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, child.setParent(parent)); + EXPECT_TRUE(child.setParent(parent)); ASSERT_TRUE(child.getParent() != nullptr); ASSERT_TRUE(parent.getChild(0) != nullptr); - EXPECT_EQ(StatusOK, child.removeParent()); + EXPECT_TRUE(child.removeParent()); EXPECT_EQ(nullptr, child.getParent()); EXPECT_EQ(nullptr, parent.getChild(0)); @@ -181,15 +181,15 @@ namespace ramses Node& child1 = this->createNode("child1"); Node& child2 = this->createNode("child2"); Node& child3 = this->createNode("child3"); - EXPECT_EQ(StatusOK, parent.addChild(child1)); - EXPECT_EQ(StatusOK, parent.addChild(child2)); - EXPECT_EQ(StatusOK, parent.addChild(child3)); + EXPECT_TRUE(parent.addChild(child1)); + EXPECT_TRUE(parent.addChild(child2)); + EXPECT_TRUE(parent.addChild(child3)); ASSERT_TRUE(parent.getChild(0) != nullptr); ASSERT_TRUE(parent.getChild(1) != nullptr); ASSERT_TRUE(parent.getChild(2) != nullptr); - EXPECT_EQ(StatusOK, parent.removeAllChildren()); + EXPECT_TRUE(parent.removeAllChildren()); EXPECT_EQ(nullptr, parent.getChild(0)); EXPECT_EQ(nullptr, parent.getChild(1)); @@ -197,15 +197,15 @@ namespace ramses EXPECT_EQ(nullptr, child1.getParent()); EXPECT_EQ(nullptr, child2.getParent()); EXPECT_EQ(nullptr, child3.getParent()); - EXPECT_TRUE(child1.m_impl.isDirty()); - EXPECT_TRUE(child2.m_impl.isDirty()); - EXPECT_TRUE(child3.m_impl.isDirty()); + EXPECT_TRUE(child1.impl().isDirty()); + EXPECT_TRUE(child2.impl().isDirty()); + EXPECT_TRUE(child3.impl().isDirty()); } TYPED_TEST(NodeTest, reportsErrorWhenRemovinNonExistingParent) { Node& nodeWithNoParent = this->createNode("node"); - EXPECT_NE(StatusOK, nodeWithNoParent.removeParent()); + EXPECT_FALSE(nodeWithNoParent.removeParent()); } TYPED_TEST(NodeTest, shouldReplaceParentWithaddChild) @@ -214,8 +214,8 @@ namespace ramses Node& parent2 = this->createNode("parent2"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent1.addChild(child)); - EXPECT_EQ(StatusOK, parent2.addChild(child)); + EXPECT_TRUE(parent1.addChild(child)); + EXPECT_TRUE(parent2.addChild(child)); EXPECT_EQ(0u, parent1.getChildCount()); EXPECT_EQ(&parent2, child.getParent()); } @@ -226,8 +226,8 @@ namespace ramses Node& parent2 = this->createNode("parent2"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, child.setParent(parent1)); - EXPECT_EQ(StatusOK, child.setParent(parent2)); + EXPECT_TRUE(child.setParent(parent1)); + EXPECT_TRUE(child.setParent(parent2)); EXPECT_EQ(0u, parent1.getChildCount()); EXPECT_EQ(&parent2, child.getParent()); } @@ -239,10 +239,10 @@ namespace ramses Node& child3 = this->createNode("child3"); Node& child2 = this->createNode("child2"); Node& child4 = this->createNode("child4"); - ASSERT_EQ(StatusOK, parent.addChild(child1)); - ASSERT_EQ(StatusOK, parent.addChild(child2)); - ASSERT_EQ(StatusOK, parent.addChild(child3)); - ASSERT_EQ(StatusOK, parent.addChild(child4)); + ASSERT_TRUE(parent.addChild(child1)); + ASSERT_TRUE(parent.addChild(child2)); + ASSERT_TRUE(parent.addChild(child3)); + ASSERT_TRUE(parent.addChild(child4)); // children -> 1,2,3,4 @@ -259,10 +259,10 @@ namespace ramses Node& parent = this->createNode("parent"); Node& child2 = this->createNode("child2"); Node& child4 = this->createNode("child4"); - ASSERT_EQ(StatusOK, parent.addChild(child1)); - ASSERT_EQ(StatusOK, parent.addChild(child2)); - ASSERT_EQ(StatusOK, parent.addChild(child3)); - ASSERT_EQ(StatusOK, parent.addChild(child4)); + ASSERT_TRUE(parent.addChild(child1)); + ASSERT_TRUE(parent.addChild(child2)); + ASSERT_TRUE(parent.addChild(child3)); + ASSERT_TRUE(parent.addChild(child4)); // children -> 1,2,3,4 (tested in other test) @@ -297,98 +297,98 @@ namespace ramses TYPED_TEST(NodeTest, canBeConvertedToNode) { RamsesObject& obj = this->createNode("node"); - EXPECT_TRUE(RamsesUtils::TryConvert(obj) != nullptr); + EXPECT_TRUE(obj.as() != nullptr); const RamsesObject& constObj = obj; - EXPECT_TRUE(RamsesUtils::TryConvert(constObj) != nullptr); + EXPECT_TRUE(constObj.as() != nullptr); } TYPED_TEST(NodeTest, reportsErrorWhenSettingParentToItself) { Node& node = this->createNode("node"); - EXPECT_NE(StatusOK, node.setParent(node)); + EXPECT_FALSE(node.setParent(node)); } TYPED_TEST(NodeTest, reportsErrorWhenSettingParaentFromAnotherScene) { - Scene& anotherScene = *this->client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *this->client.createScene(sceneId_t(12u)); CreationHelper otherSceneCreationHelper(&anotherScene, &this->client); Node& parent = *otherSceneCreationHelper.template createObjectOfType("parent"); Node& node = this->createNode("node"); - EXPECT_NE(StatusOK, node.setParent(parent)); + EXPECT_FALSE(node.setParent(parent)); } TYPED_TEST(NodeTest, reportsErrorWhenSettingChildToItself) { Node& node = this->createNode("node"); - EXPECT_NE(StatusOK, node.addChild(node)); + EXPECT_FALSE(node.addChild(node)); } TYPED_TEST(NodeTest, isNotDirtyInitially) { Node& node = this->createNode("node"); - EXPECT_FALSE(node.m_impl.isDirty()); + EXPECT_FALSE(node.impl().isDirty()); } TYPED_TEST(NodeTest, canBeMarkedDirty) { Node& node = this->createNode("node"); - node.m_impl.markDirty(); - EXPECT_TRUE(node.m_impl.isDirty()); + node.impl().markDirty(); + EXPECT_TRUE(node.impl().isDirty()); } TYPED_TEST(NodeTest, isMarkedDirtyWhenSettingParent) { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, child.setParent(parent)); - EXPECT_TRUE(child.m_impl.isDirty()); + EXPECT_TRUE(child.setParent(parent)); + EXPECT_TRUE(child.impl().isDirty()); } TYPED_TEST(NodeTest, isMarkedDirtyWhenAddingToParent) { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); - EXPECT_TRUE(child.m_impl.isDirty()); + EXPECT_TRUE(parent.addChild(child)); + EXPECT_TRUE(child.impl().isDirty()); } TYPED_TEST(NodeTest, isMarkedDirtyWhenRemovedFromParent) { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); this->m_scene.flush(); // to clear dirty state - EXPECT_EQ(StatusOK, parent.removeChild(child)); - EXPECT_TRUE(child.m_impl.isDirty()); + EXPECT_TRUE(parent.removeChild(child)); + EXPECT_TRUE(child.impl().isDirty()); } TYPED_TEST(NodeTest, staysCleanWhenSettingChild) { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); - EXPECT_FALSE(parent.m_impl.isDirty()); + EXPECT_TRUE(parent.addChild(child)); + EXPECT_FALSE(parent.impl().isDirty()); } TYPED_TEST(NodeTest, isMarkedDirtyWhenParentDestroyed) { Node& parent = this->createNode("parent"); Node& child = this->createNode("child"); - EXPECT_EQ(StatusOK, parent.addChild(child)); + EXPECT_TRUE(parent.addChild(child)); this->m_scene.flush(); // to clear dirty state this->m_scene.destroy(parent); - EXPECT_TRUE(child.m_impl.isDirty()); + EXPECT_TRUE(child.impl().isDirty()); } TYPED_TEST(NodeTest, staysCleanWhenRemovingNonExistingParent) { Node& nodeWithNoParent = this->createNode("node"); - EXPECT_NE(StatusOK, nodeWithNoParent.removeParent()); - EXPECT_FALSE(nodeWithNoParent.m_impl.isDirty()); + EXPECT_FALSE(nodeWithNoParent.removeParent()); + EXPECT_FALSE(nodeWithNoParent.impl().isDirty()); } void expectMatricesEqual(const glm::mat4 mat1, const glm::mat4 mat2) @@ -403,7 +403,7 @@ namespace ramses { Node& node = this->createNode("node"); matrix44f modelMat; - EXPECT_EQ(StatusOK, node.getModelMatrix(modelMat)); + EXPECT_TRUE(node.getModelMatrix(modelMat)); expectMatricesEqual(glm::identity(), modelMat); } @@ -412,48 +412,48 @@ namespace ramses { Node& node = this->createNode("node"); - EXPECT_EQ(ramses::StatusOK, node.setRotation({1.f, 2.f, 3.f}, ERotationType::Euler_XZX)); + EXPECT_TRUE(node.setRotation({1.f, 2.f, 3.f}, ERotationType::Euler_XZX)); - const auto transformHandle = node.m_impl.getTransformHandle(); + const auto transformHandle = node.impl().getTransformHandle(); vec3f euler; - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler.x, 1.f); EXPECT_EQ(euler.y, 2.f); EXPECT_EQ(euler.z, 3.f); EXPECT_EQ(ERotationType::Euler_XZX, node.getRotationType()); - EXPECT_EQ(glm::vec3(1.f, 2.f, 3.f), glm::vec3(this->m_scene.m_impl.getIScene().getRotation(transformHandle))); - EXPECT_EQ(ramses_internal::ERotationType::Euler_XZX, this->m_scene.m_impl.getIScene().getRotationType(transformHandle)); + EXPECT_EQ(glm::vec3(1.f, 2.f, 3.f), glm::vec3(this->m_scene.impl().getIScene().getRotation(transformHandle))); + EXPECT_EQ(ERotationType::Euler_XZX, this->m_scene.impl().getIScene().getRotationType(transformHandle)); - EXPECT_EQ(ramses::StatusOK, node.setRotation({11.f, 12.f, 13.f}, ERotationType::Euler_XYZ)); + EXPECT_TRUE(node.setRotation({11.f, 12.f, 13.f}, ERotationType::Euler_XYZ)); - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler.x, 11.f); EXPECT_EQ(euler.y, 12.f); EXPECT_EQ(euler.z, 13.f); EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); - EXPECT_EQ(glm::vec3(11.f, 12.f, 13.f), glm::vec3(this->m_scene.m_impl.getIScene().getRotation(transformHandle))); - EXPECT_EQ(ramses_internal::ERotationType::Euler_XYZ, this->m_scene.m_impl.getIScene().getRotationType(transformHandle)); + EXPECT_EQ(glm::vec3(11.f, 12.f, 13.f), glm::vec3(this->m_scene.impl().getIScene().getRotation(transformHandle))); + EXPECT_EQ(ERotationType::Euler_XYZ, this->m_scene.impl().getIScene().getRotationType(transformHandle)); } TYPED_TEST(NodeTest, returnsErrorWhenSettingQuaternionConvention) { Node& node = this->createNode("node"); - EXPECT_NE(StatusOK, node.setRotation({10.f, 20.f, 30.f}, ERotationType::Quaternion)); + EXPECT_FALSE(node.setRotation({10.f, 20.f, 30.f}, ERotationType::Quaternion)); // default value is still set EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); quat quaternion; - EXPECT_EQ(StatusOK, node.getRotation(quaternion)); + EXPECT_TRUE(node.getRotation(quaternion)); EXPECT_EQ(quaternion, glm::identity()); vec3f euler; - EXPECT_EQ(StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler, vec3f{0.f}); - const auto transformHandle = node.m_impl.getTransformHandle(); + const auto transformHandle = node.impl().getTransformHandle(); EXPECT_FALSE(transformHandle.isValid()); } @@ -462,18 +462,18 @@ namespace ramses Node& node = this->createNode("node"); const quat q{0.830048f, -0.2907008f, 0.4666782f, -0.093407f}; - EXPECT_EQ(StatusOK, node.setRotation(q)); + EXPECT_TRUE(node.setRotation(q)); - const auto transformHandle = node.m_impl.getTransformHandle(); + const auto transformHandle = node.impl().getTransformHandle(); quat qOut; - EXPECT_EQ(ramses::StatusOK, node.getRotation(qOut)); + EXPECT_TRUE(node.getRotation(qOut)); EXPECT_EQ(qOut.x, q.x); EXPECT_EQ(qOut.y, q.y); EXPECT_EQ(qOut.z, q.z); EXPECT_EQ(qOut.w, q.w); - EXPECT_EQ(glm::vec4(q.x, q.y, q.z, q.w), this->m_scene.m_impl.getIScene().getRotation(transformHandle)); - EXPECT_EQ(ramses_internal::ERotationType::Quaternion, this->m_scene.m_impl.getIScene().getRotationType(transformHandle)); + EXPECT_EQ(glm::vec4(q.x, q.y, q.z, q.w), this->m_scene.impl().getIScene().getRotation(transformHandle)); + EXPECT_EQ(ERotationType::Quaternion, this->m_scene.impl().getIScene().getRotationType(transformHandle)); } TYPED_TEST(NodeTest, setsRotationQuaternionThenEuler) @@ -481,49 +481,49 @@ namespace ramses Node& node = this->createNode("node"); const quat q{0.830048f, -0.2907008f, 0.4666782f, -0.093407f}; - EXPECT_EQ(StatusOK, node.setRotation(q)); + EXPECT_TRUE(node.setRotation(q)); quat qOut; - EXPECT_EQ(ramses::StatusOK, node.getRotation(qOut)); + EXPECT_TRUE(node.getRotation(qOut)); - const auto transformHandle = node.m_impl.getTransformHandle(); - EXPECT_EQ(ramses::StatusOK, node.setRotation({11.f, 12.f, 13.f}, ERotationType::Euler_XYZ)); + const auto transformHandle = node.impl().getTransformHandle(); + EXPECT_TRUE(node.setRotation({11.f, 12.f, 13.f}, ERotationType::Euler_XYZ)); vec3f euler; - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); EXPECT_EQ(euler.x, 11.f); EXPECT_EQ(euler.y, 12.f); EXPECT_EQ(euler.z, 13.f); - EXPECT_EQ(glm::vec3(11.f, 12.f, 13.f), glm::vec3(this->m_scene.m_impl.getIScene().getRotation(transformHandle))); - EXPECT_EQ(ramses_internal::ERotationType::Euler_XYZ, this->m_scene.m_impl.getIScene().getRotationType(transformHandle)); - EXPECT_NE(ramses::StatusOK, node.getRotation(qOut)); + EXPECT_EQ(glm::vec3(11.f, 12.f, 13.f), glm::vec3(this->m_scene.impl().getIScene().getRotation(transformHandle))); + EXPECT_EQ(ERotationType::Euler_XYZ, this->m_scene.impl().getIScene().getRotationType(transformHandle)); + EXPECT_FALSE(node.getRotation(qOut)); } TYPED_TEST(NodeTest, setsRotationEulerThenQuaternion) { Node& node = this->createNode("node"); - EXPECT_EQ(ramses::StatusOK, node.setRotation({1.f, 2.f, 3.f}, ERotationType::Euler_XZX)); + EXPECT_TRUE(node.setRotation({1.f, 2.f, 3.f}, ERotationType::Euler_XZX)); vec3f euler; - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(ERotationType::Euler_XZX, node.getRotationType()); const quat q{0.830048f, -0.2907008f, 0.4666782f, -0.093407f}; - EXPECT_EQ(StatusOK, node.setRotation(q)); + EXPECT_TRUE(node.setRotation(q)); - const auto transformHandle = node.m_impl.getTransformHandle(); + const auto transformHandle = node.impl().getTransformHandle(); quat qOut; - EXPECT_EQ(ramses::StatusOK, node.getRotation(qOut)); + EXPECT_TRUE(node.getRotation(qOut)); EXPECT_EQ(qOut.x, q.x); EXPECT_EQ(qOut.y, q.y); EXPECT_EQ(qOut.z, q.z); EXPECT_EQ(qOut.w, q.w); - EXPECT_EQ(glm::vec4(q.x, q.y, q.z, q.w), this->m_scene.m_impl.getIScene().getRotation(transformHandle)); - EXPECT_EQ(ramses_internal::ERotationType::Quaternion, this->m_scene.m_impl.getIScene().getRotationType(transformHandle)); + EXPECT_EQ(glm::vec4(q.x, q.y, q.z, q.w), this->m_scene.impl().getIScene().getRotation(transformHandle)); + EXPECT_EQ(ERotationType::Quaternion, this->m_scene.impl().getIScene().getRotationType(transformHandle)); - EXPECT_NE(ramses::StatusOK, node.getRotation(euler)); + EXPECT_FALSE(node.getRotation(euler)); EXPECT_EQ(ERotationType::Quaternion, node.getRotationType()); } @@ -531,9 +531,9 @@ namespace ramses { Node& node = this->createNode("node"); quat q{0.5f, 0.5f, 0.5f, -0.5f}; - EXPECT_EQ(StatusOK, node.setRotation(q)); + EXPECT_TRUE(node.setRotation(q)); vec3f euler; - EXPECT_NE(StatusOK, node.getRotation(euler)); + EXPECT_FALSE(node.getRotation(euler)); EXPECT_EQ(ERotationType::Quaternion, node.getRotationType()); } @@ -541,8 +541,8 @@ namespace ramses { Node& node = this->createNode("node"); quat q; - EXPECT_EQ(StatusOK, node.setRotation({90.f, 0.f, 0.f})); - EXPECT_NE(StatusOK, node.getRotation(q)); + EXPECT_TRUE(node.setRotation({90.f, 0.f, 0.f}, ERotationType::Euler_XYZ)); + EXPECT_FALSE(node.getRotation(q)); } TYPED_TEST(NodeTest, getRotationReturnsDefaultValuesWithoutSetBefore) @@ -550,7 +550,7 @@ namespace ramses Node& node = this->createNode("node"); vec3f euler; - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler, vec3f{0.f}); EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); } @@ -561,7 +561,7 @@ namespace ramses Node& node = this->createNode("node"); quat q; EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); - EXPECT_EQ(ramses::StatusOK, node.getRotation(q)); + EXPECT_TRUE(node.getRotation(q)); EXPECT_EQ(0.f, q.x); EXPECT_EQ(0.f, q.y); EXPECT_EQ(0.f, q.z); @@ -573,12 +573,12 @@ namespace ramses Node& node = this->createNode("node"); vec3f euler; - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler, vec3f{0.f}); EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); - EXPECT_EQ(ramses::StatusOK, node.setTranslation({2, 2, 2})); - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.setTranslation({2, 2, 2})); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler, vec3f{0.f}); EXPECT_EQ(ERotationType::Euler_XYZ, node.getRotationType()); @@ -588,12 +588,12 @@ namespace ramses { Node& node = this->createNode("node"); - EXPECT_EQ(ramses::StatusOK, node.setTranslation({2, 2, 2})); + EXPECT_TRUE(node.setTranslation({2, 2, 2})); - EXPECT_EQ(ramses::StatusOK, node.setRotation({1.f, 2.f, 3.f}, ERotationType::Euler_ZYZ)); + EXPECT_TRUE(node.setRotation({1.f, 2.f, 3.f}, ERotationType::Euler_ZYZ)); vec3f euler; - EXPECT_EQ(ramses::StatusOK, node.getRotation(euler)); + EXPECT_TRUE(node.getRotation(euler)); EXPECT_EQ(euler, vec3f(1.f, 2.f, 3.f)); EXPECT_EQ(ERotationType::Euler_ZYZ, node.getRotationType()); @@ -603,7 +603,7 @@ namespace ramses { Node& node = this->createNode("node"); matrix44f modelMat; - EXPECT_EQ(StatusOK, node.getInverseModelMatrix(modelMat)); + EXPECT_TRUE(node.getInverseModelMatrix(modelMat)); expectMatricesEqual(glm::identity(), modelMat); } @@ -618,11 +618,11 @@ namespace ramses const auto transMat = glm::translate(glm::vec3{ 1.f, 2.f, 3.f }); const auto scaleMat = glm::scale(glm::vec3{ 4.f, 5.f, 6.f }); - const auto rotMat = ramses_internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ramses_internal::ERotationType::Euler_ZYX); + const auto rotMat = ramses::internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ERotationType::Euler_ZYX); const auto expectedModelMat = transMat * rotMat * scaleMat; matrix44f modelMat; - EXPECT_EQ(StatusOK, node.getModelMatrix(modelMat)); + EXPECT_TRUE(node.getModelMatrix(modelMat)); expectMatricesEqual(expectedModelMat, modelMat); } @@ -645,11 +645,11 @@ namespace ramses const auto transMat = glm::translate(glm::vec3{ 1.f, 2.f, 3.f }); const auto scaleMat = glm::scale(glm::vec3{ 4.f, 5.f, 6.f }); - const auto rotMat = ramses_internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ramses_internal::ERotationType::Euler_ZYX); + const auto rotMat = ramses::internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ERotationType::Euler_ZYX); const auto expectedModelMat = transMat * rotMat * scaleMat; matrix44f modelMat; - EXPECT_EQ(StatusOK, node.getModelMatrix(modelMat)); + EXPECT_TRUE(node.getModelMatrix(modelMat)); expectMatricesEqual(expectedModelMat, modelMat); } @@ -664,11 +664,11 @@ namespace ramses const auto transMat = glm::translate(glm::vec3{ -1.f, -2.f, -3.f }); const auto scaleMat = glm::inverse(glm::scale(glm::vec3{ 4.f, 5.f, 6.f })); - const auto rotMat = glm::transpose(ramses_internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ramses_internal::ERotationType::Euler_YZX)); + const auto rotMat = glm::transpose(ramses::internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ERotationType::Euler_YZX)); const auto expectedInverseModelMat = scaleMat * rotMat * transMat; matrix44f inverseModelMat; - EXPECT_EQ(StatusOK, node.getInverseModelMatrix(inverseModelMat)); + EXPECT_TRUE(node.getInverseModelMatrix(inverseModelMat)); expectMatricesEqual(expectedInverseModelMat, inverseModelMat); } @@ -691,11 +691,11 @@ namespace ramses const auto transMat = glm::translate(glm::vec3{ -1.f, -2.f, -3.f }); const auto scaleMat = glm::inverse(glm::scale(glm::vec3{ 4.f, 5.f, 6.f })); - const auto rotMat = glm::transpose(ramses_internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ramses_internal::ERotationType::Euler_YXY)); + const auto rotMat = glm::transpose(ramses::internal::Math3d::Rotation({ 7.f, 8.f, 9.f, 1.f }, ERotationType::Euler_YXY)); const auto expectedInverseModelMat = scaleMat * rotMat * transMat; matrix44f inverseModelMat; - EXPECT_EQ(StatusOK, node.getInverseModelMatrix(inverseModelMat)); + EXPECT_TRUE(node.getInverseModelMatrix(inverseModelMat)); expectMatricesEqual(expectedInverseModelMat, inverseModelMat); } @@ -709,14 +709,14 @@ namespace ramses // E.g. PickableObject depends on Camera which is a node as well and would be wrongly counted here. const size_t additionalAllocatedNodeCount = this->m_creationHelper.getAdditionalAllocatedNodeCount(); - EXPECT_EQ(1u, node.m_impl.getIScene().getNodeCount() - additionalAllocatedNodeCount); - EXPECT_TRUE(this->m_internalScene.isNodeAllocated(node.m_impl.getNodeHandle())); + EXPECT_EQ(1u, node.impl().getIScene().getNodeCount() - additionalAllocatedNodeCount); + EXPECT_TRUE(this->m_internalScene.isNodeAllocated(node.impl().getNodeHandle())); } TYPED_TEST(NodeTest, destructionDestroysLLNode) { Node& node = this->createNode("node"); - const auto handle = node.m_impl.getNodeHandle(); + const auto handle = node.impl().getNodeHandle(); this->m_scene.destroy(node); EXPECT_FALSE(this->m_internalScene.isNodeAllocated(handle)); } diff --git a/client/test/NodeTransformationTest.cpp b/tests/unittests/client/NodeTransformationTest.cpp similarity index 61% rename from client/test/NodeTransformationTest.cpp rename to tests/unittests/client/NodeTransformationTest.cpp index 7d5d3eb21..992a11327 100644 --- a/client/test/NodeTransformationTest.cpp +++ b/tests/unittests/client/NodeTransformationTest.cpp @@ -8,17 +8,17 @@ #include -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PickableObject.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PickableObject.h" #include "ClientTestUtils.h" #include "RamsesObjectTestTypes.h" #include "TestEqualHelper.h" -#include "Math3d/Rotation.h" +#include "internal/Core/Math3d/Rotation.h" -namespace ramses +namespace ramses::internal { using namespace testing; @@ -31,7 +31,7 @@ namespace ramses m_node = &this->template createObject("node"); } - T* m_node; + T* m_node{nullptr}; }; TYPED_TEST_SUITE(NodeTransformationTest, NodeTypes); @@ -40,12 +40,12 @@ namespace ramses { vec3f initialTranslation(0.f, 0.f, 0.f); vec3f actualTranslation; - EXPECT_EQ(StatusOK, this->m_node->getTranslation(actualTranslation)); + EXPECT_TRUE(this->m_node->getTranslation(actualTranslation)); EXPECT_EQ(initialTranslation, actualTranslation); vec3f translationVector(1.2f, 2.3f, 4.5f); - EXPECT_EQ(StatusOK, this->m_node->setTranslation(translationVector)); - EXPECT_EQ(StatusOK, this->m_node->getTranslation(actualTranslation)); + EXPECT_TRUE(this->m_node->setTranslation(translationVector)); + EXPECT_TRUE(this->m_node->getTranslation(actualTranslation)); EXPECT_EQ(translationVector, actualTranslation); } @@ -53,13 +53,13 @@ namespace ramses { vec3f initialTranslation(0.f, 0.f, 0.f); vec3f actualTranslation; - EXPECT_EQ(StatusOK, this->m_node->getTranslation(actualTranslation)); + EXPECT_TRUE(this->m_node->getTranslation(actualTranslation)); EXPECT_EQ(initialTranslation, actualTranslation); vec3f translationVector(1.2f, 2.3f, 4.5f); - EXPECT_EQ(StatusOK, this->m_node->translate(translationVector)); - EXPECT_EQ(StatusOK, this->m_node->translate(translationVector)); - EXPECT_EQ(StatusOK, this->m_node->getTranslation(actualTranslation)); + EXPECT_TRUE(this->m_node->translate(translationVector)); + EXPECT_TRUE(this->m_node->translate(translationVector)); + EXPECT_TRUE(this->m_node->getTranslation(actualTranslation)); EXPECT_EQ(2.f * translationVector, actualTranslation); } @@ -67,19 +67,19 @@ namespace ramses { vec3f initialRotation(0.f, 0.f, 0.f); vec3f actualRotation; - EXPECT_EQ(StatusOK, this->m_node->getRotation(actualRotation)); + EXPECT_TRUE(this->m_node->getRotation(actualRotation)); EXPECT_EQ(ERotationType::Euler_XYZ, this->m_node->getRotationType()); EXPECT_EQ(initialRotation, actualRotation); vec3f rotationVector_1(1.2f, 2.3f, 4.5f); - EXPECT_EQ(StatusOK, this->m_node->setRotation(rotationVector_1, ERotationType::Euler_ZYX)); - EXPECT_EQ(StatusOK, this->m_node->getRotation(actualRotation)); + EXPECT_TRUE(this->m_node->setRotation(rotationVector_1, ERotationType::Euler_ZYX)); + EXPECT_TRUE(this->m_node->getRotation(actualRotation)); EXPECT_EQ(rotationVector_1, actualRotation); EXPECT_EQ(ERotationType::Euler_ZYX, this->m_node->getRotationType()); vec3f rotationVector_2(2.2f, 3.3f, 5.5f); - EXPECT_EQ(StatusOK, this->m_node->setRotation(rotationVector_2, ERotationType::Euler_ZYZ)); - EXPECT_EQ(StatusOK, this->m_node->getRotation(actualRotation)); + EXPECT_TRUE(this->m_node->setRotation(rotationVector_2, ERotationType::Euler_ZYZ)); + EXPECT_TRUE(this->m_node->getRotation(actualRotation)); EXPECT_EQ(rotationVector_2, actualRotation); EXPECT_EQ(ERotationType::Euler_ZYZ, this->m_node->getRotationType()); } @@ -88,17 +88,17 @@ namespace ramses { vec3f initialScaling(1.f, 1.f, 1.f); vec3f actualScale; - EXPECT_EQ(StatusOK, this->m_node->getScaling(actualScale)); + EXPECT_TRUE(this->m_node->getScaling(actualScale)); EXPECT_EQ(initialScaling, actualScale); vec3f scalingVector_1(1.2f, 2.3f, 4.5f); - EXPECT_EQ(StatusOK, this->m_node->setScaling(scalingVector_1)); - EXPECT_EQ(StatusOK, this->m_node->getScaling(actualScale)); + EXPECT_TRUE(this->m_node->setScaling(scalingVector_1)); + EXPECT_TRUE(this->m_node->getScaling(actualScale)); EXPECT_EQ(scalingVector_1, actualScale); vec3f scalingVector_2(2.2f, 3.3f, 5.5f); - EXPECT_EQ(StatusOK, this->m_node->setScaling(scalingVector_2)); - EXPECT_EQ(StatusOK, this->m_node->getScaling(actualScale)); + EXPECT_TRUE(this->m_node->setScaling(scalingVector_2)); + EXPECT_TRUE(this->m_node->getScaling(actualScale)); EXPECT_EQ(scalingVector_2, actualScale); } @@ -106,19 +106,19 @@ namespace ramses { vec3f initialScaling(1.f, 1.f, 1.f); vec3f actualScale; - EXPECT_EQ(StatusOK, this->m_node->getScaling(actualScale)); + EXPECT_TRUE(this->m_node->getScaling(actualScale)); EXPECT_EQ(initialScaling, actualScale); vec3f scalingVector_1(4.f, 6.f, 8.f); - EXPECT_EQ(StatusOK, this->m_node->scale(scalingVector_1)); - EXPECT_EQ(StatusOK, this->m_node->getScaling(actualScale)); + EXPECT_TRUE(this->m_node->scale(scalingVector_1)); + EXPECT_TRUE(this->m_node->getScaling(actualScale)); EXPECT_EQ(scalingVector_1, actualScale); vec3f scalingVector_2(0.5f, 0.5f, 0.5f); - EXPECT_EQ(StatusOK, this->m_node->scale(scalingVector_2)); + EXPECT_TRUE(this->m_node->scale(scalingVector_2)); vec3f resultVector(2.f, 3.f, 4.f); - EXPECT_EQ(StatusOK, this->m_node->getScaling(actualScale)); + EXPECT_TRUE(this->m_node->getScaling(actualScale)); EXPECT_EQ(resultVector, actualScale); } @@ -141,16 +141,16 @@ namespace ramses this->m_node->addChild(*child1); child0->addChild(*grandChild); - EXPECT_EQ(StatusOK, this->m_node->setRotation({10.f, 0.f, 0.f}, ramses::ERotationType::Euler_ZYX)); - EXPECT_EQ(StatusOK, child0->setRotation({0.f, 20.f, 0.f}, ramses::ERotationType::Euler_XYZ)); - EXPECT_EQ(StatusOK, child1->setRotation({0.f, 20.f, 30.f}, ramses::ERotationType::Euler_ZYZ)); - EXPECT_EQ(StatusOK, grandChild->setRotation({0.f, 0.f, 30.f}, ramses::ERotationType::Euler_XZY)); + EXPECT_TRUE(this->m_node->setRotation({10.f, 0.f, 0.f}, ERotationType::Euler_ZYX)); + EXPECT_TRUE(child0->setRotation({0.f, 20.f, 0.f}, ERotationType::Euler_XYZ)); + EXPECT_TRUE(child1->setRotation({0.f, 20.f, 30.f}, ERotationType::Euler_ZYZ)); + EXPECT_TRUE(grandChild->setRotation({0.f, 0.f, 30.f}, ERotationType::Euler_XZY)); //expected matrices for rotation after transformation chain is applied - const auto expectedNodeRotationMatrix = ramses_internal::Math3d::Rotation({ 10.f , 0.f , 0.f, 1.f }, ramses_internal::ERotationType::Euler_ZYX); - const auto expectedChild0RorationMatrix = ramses_internal::Math3d::Rotation({ 10.f , 20.f, 0.f, 1.f }, ramses_internal::ERotationType::Euler_ZYX); - const auto expectedChild1RorationMatrix = ramses_internal::Math3d::Rotation({ 10.f , 20.f, 30.f, 1.f }, ramses_internal::ERotationType::Euler_ZYX); - const auto expectedGrandChildRorationMatrix = ramses_internal::Math3d::Rotation({ 10.f , 20.f, 30.f, 1.f }, ramses_internal::ERotationType::Euler_ZYX); + const auto expectedNodeRotationMatrix = ramses::internal::Math3d::Rotation({ 10.f , 0.f , 0.f, 1.f }, ERotationType::Euler_ZYX); + const auto expectedChild0RorationMatrix = ramses::internal::Math3d::Rotation({ 10.f , 20.f, 0.f, 1.f }, ERotationType::Euler_ZYX); + const auto expectedChild1RorationMatrix = ramses::internal::Math3d::Rotation({ 10.f , 20.f, 30.f, 1.f }, ERotationType::Euler_ZYX); + const auto expectedGrandChildRorationMatrix = ramses::internal::Math3d::Rotation({ 10.f , 20.f, 30.f, 1.f }, ERotationType::Euler_ZYX); matrix44f resultNodeMatrix; matrix44f resultChild0Matrix; @@ -161,10 +161,10 @@ namespace ramses child1 ->getModelMatrix(resultChild1Matrix); grandChild ->getModelMatrix(resultGrandChildMatrix); - ramses_internal::expectMatrixFloatEqual(expectedNodeRotationMatrix , resultNodeMatrix); - ramses_internal::expectMatrixFloatEqual(expectedChild0RorationMatrix , resultChild0Matrix); - ramses_internal::expectMatrixFloatEqual(expectedChild1RorationMatrix , resultChild1Matrix); - ramses_internal::expectMatrixFloatEqual(expectedGrandChildRorationMatrix , resultGrandChildMatrix); + ramses::internal::expectMatrixFloatEqual(expectedNodeRotationMatrix , resultNodeMatrix); + ramses::internal::expectMatrixFloatEqual(expectedChild0RorationMatrix , resultChild0Matrix); + ramses::internal::expectMatrixFloatEqual(expectedChild1RorationMatrix , resultChild1Matrix); + ramses::internal::expectMatrixFloatEqual(expectedGrandChildRorationMatrix , resultGrandChildMatrix); } template @@ -173,22 +173,22 @@ namespace ramses protected: void SetUp() override { - const ramses_internal::IScene& iscene = this->m_scene.m_impl.getIScene(); - ramses_internal::SceneInfo info(iscene.getSceneId(), iscene.getName()); + const ramses::internal::IScene& iscene = this->m_scene.impl().getIScene(); + ramses::internal::SceneInfo info(iscene.getSceneId(), iscene.getName()); EXPECT_CALL(this->sceneActionsCollector, handleNewSceneAvailable(info, _)); EXPECT_CALL(this->sceneActionsCollector, handleInitializeScene(info, _)); - EXPECT_EQ(StatusOK, m_scene.publish(EScenePublicationMode::LocalOnly)); + EXPECT_TRUE(m_scene.publish(EScenePublicationMode::LocalOnly)); m_node = &this->template createObject("node"); } void TearDown() override { - EXPECT_CALL(this->sceneActionsCollector, handleSceneBecameUnavailable(ramses_internal::SceneId(this->m_scene.m_impl.getSceneId().getValue()), _)); - EXPECT_EQ(StatusOK, m_scene.unpublish()); + EXPECT_CALL(this->sceneActionsCollector, handleSceneBecameUnavailable(ramses::internal::SceneId(this->m_scene.impl().getSceneId().getValue()), _)); + EXPECT_TRUE(m_scene.unpublish()); } - T* m_node; + T* m_node{nullptr}; }; TYPED_TEST_SUITE(NodeTransformationTestWithPublishedScene, NodeTypes); @@ -196,15 +196,15 @@ namespace ramses TYPED_TEST(NodeTransformationTestWithPublishedScene, setTranslateWithValuesEqualToCurrentValuesDoesNotCreateSceneActions) { vec3f translationVector(1.2f, 2.3f, 4.5f); - EXPECT_CALL(this->sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(this->m_scene.getSceneId().getValue()), _, _)); - EXPECT_EQ(StatusOK, this->m_node->setTranslation(translationVector)); + EXPECT_CALL(this->sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(this->m_scene.getSceneId().getValue()), _, _)); + EXPECT_TRUE(this->m_node->setTranslation(translationVector)); this->m_scene.flush(); EXPECT_LE(1u, this->sceneActionsCollector.getNumberOfActions()); Mock::VerifyAndClearExpectations(this); this->sceneActionsCollector.resetCollecting(); - EXPECT_EQ(StatusOK, this->m_node->setTranslation(translationVector)); + EXPECT_TRUE(this->m_node->setTranslation(translationVector)); this->m_scene.flush(); EXPECT_EQ(0u, this->sceneActionsCollector.getNumberOfActions()); // flush empty and optimized away } @@ -212,15 +212,15 @@ namespace ramses TYPED_TEST(NodeTransformationTestWithPublishedScene, setRotationWithValuesEqualToCurrentValuesDoesNotCreateSceneActions) { vec3f rotationVector(1.2f, 2.3f, 4.5f); - EXPECT_CALL(this->sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(this->m_scene.getSceneId().getValue()), _, _)); - EXPECT_EQ(StatusOK, this->m_node->setRotation(rotationVector, ERotationType::Euler_YXZ)); + EXPECT_CALL(this->sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(this->m_scene.getSceneId().getValue()), _, _)); + EXPECT_TRUE(this->m_node->setRotation(rotationVector, ERotationType::Euler_YXZ)); this->m_scene.flush(); EXPECT_LE(1u, this->sceneActionsCollector.getNumberOfActions()); Mock::VerifyAndClearExpectations(this); this->sceneActionsCollector.resetCollecting(); - EXPECT_EQ(StatusOK, this->m_node->setRotation(rotationVector, ERotationType::Euler_YXZ)); + EXPECT_TRUE(this->m_node->setRotation(rotationVector, ERotationType::Euler_YXZ)); this->m_scene.flush(); EXPECT_EQ(0u, this->sceneActionsCollector.getNumberOfActions()); // flush empty and optimized away } @@ -228,15 +228,15 @@ namespace ramses TYPED_TEST(NodeTransformationTestWithPublishedScene, setScalingWithValuesEqualToCurrentValuesDoesNotCreateSceneActions) { vec3f scalingVector(1.2f, 2.3f, 4.5f); - EXPECT_CALL(this->sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(this->m_scene.getSceneId().getValue()), _, _)); - EXPECT_EQ(StatusOK, this->m_node->setScaling(scalingVector)); + EXPECT_CALL(this->sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(this->m_scene.getSceneId().getValue()), _, _)); + EXPECT_TRUE(this->m_node->setScaling(scalingVector)); this->m_scene.flush(); EXPECT_LE(1u, this->sceneActionsCollector.getNumberOfActions()); Mock::VerifyAndClearExpectations(this); this->sceneActionsCollector.resetCollecting(); - EXPECT_EQ(StatusOK, this->m_node->setScaling(scalingVector)); + EXPECT_TRUE(this->m_node->setScaling(scalingVector)); this->m_scene.flush(); EXPECT_EQ(0u, this->sceneActionsCollector.getNumberOfActions()); // flush empty and optimized away } diff --git a/client/test/NodeVisibilityTest.cpp b/tests/unittests/client/NodeVisibilityTest.cpp similarity index 76% rename from client/test/NodeVisibilityTest.cpp rename to tests/unittests/client/NodeVisibilityTest.cpp index 139bdf35a..0bc011094 100644 --- a/client/test/NodeVisibilityTest.cpp +++ b/tests/unittests/client/NodeVisibilityTest.cpp @@ -8,21 +8,21 @@ #include -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PickableObject.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PickableObject.h" #include "ClientTestUtils.h" #include "RamsesObjectTestTypes.h" -#include "NodeImpl.h" -#include "MeshNodeImpl.h" -#include "CameraNodeImpl.h" -#include "PickableObjectImpl.h" +#include "impl/NodeImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/PickableObjectImpl.h" using namespace testing; -using namespace ramses_internal; +using namespace ramses::internal; -namespace ramses +namespace ramses::internal { template class ANodeVisibilityTest : public LocalTestClientWithScene, public testing::Test @@ -39,57 +39,57 @@ namespace ramses void testFlattenedVisibility(EVisibilityMode mode) { - EXPECT_EQ(this->m_childMesh->m_impl.getFlattenedVisibility(), mode); + EXPECT_EQ(this->m_childMesh->impl().getFlattenedVisibility(), mode); } - T* m_parentVisNode; - T* m_visibilityNode; - MeshNode* m_childMesh; + T* m_parentVisNode{nullptr}; + T* m_visibilityNode{nullptr}; + MeshNode* m_childMesh{nullptr}; }; TYPED_TEST_SUITE(ANodeVisibilityTest, NodeTypes); TYPED_TEST(ANodeVisibilityTest, isVisibleInitially) { - EXPECT_EQ(this->m_visibilityNode->m_impl.getVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(this->m_visibilityNode->impl().getVisibility(), EVisibilityMode::Visible); } TYPED_TEST(ANodeVisibilityTest, canChangeVisibility) { this->m_visibilityNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_EQ(this->m_visibilityNode->m_impl.getVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(this->m_visibilityNode->impl().getVisibility(), EVisibilityMode::Invisible); this->m_visibilityNode->setVisibility(EVisibilityMode::Off); - EXPECT_EQ(this->m_visibilityNode->m_impl.getVisibility(), EVisibilityMode::Off); + EXPECT_EQ(this->m_visibilityNode->impl().getVisibility(), EVisibilityMode::Off); } TYPED_TEST(ANodeVisibilityTest, isMarkedDirtyWhenChangingVisibility) { this->m_visibilityNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); } TYPED_TEST(ANodeVisibilityTest, staysCleanWhenSettingTheSameVisibility) { this->m_visibilityNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); // to clear dirty state this->m_visibilityNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_FALSE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_FALSE(this->m_visibilityNode->impl().isDirty()); } TYPED_TEST(ANodeVisibilityTest, confidenceTest_isMarkedDirtyWhenChangingVisibilityMultipleTimes) { this->m_visibilityNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); // to clear dirty state this->m_visibilityNode->setVisibility(EVisibilityMode::Visible); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); // to clear dirty state this->m_visibilityNode->setVisibility(EVisibilityMode::Off); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); } TYPED_TEST(ANodeVisibilityTest, hasFlattenedVisibilitySetInitially) @@ -102,7 +102,7 @@ namespace ramses TYPED_TEST(ANodeVisibilityTest, visibilityPropagatesFromParentToChildrenOnFlush) { this->m_visibilityNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); this->testFlattenedVisibility(EVisibilityMode::Invisible); @@ -111,7 +111,7 @@ namespace ramses TYPED_TEST(ANodeVisibilityTest, offVisibilityPropagatesFromParentToChildrenOnFlush) { this->m_visibilityNode->setVisibility(EVisibilityMode::Off); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); this->testFlattenedVisibility(EVisibilityMode::Off); @@ -120,7 +120,7 @@ namespace ramses TYPED_TEST(ANodeVisibilityTest, visibilityPropagatesFromGrandParentToChildrenOnFlush) { this->m_parentVisNode->setVisibility(EVisibilityMode::Invisible); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); this->testFlattenedVisibility(EVisibilityMode::Invisible); @@ -129,7 +129,7 @@ namespace ramses TYPED_TEST(ANodeVisibilityTest, offVisibilityPropagatesFromGrandParentToChildrenOnFlush) { this->m_parentVisNode->setVisibility(EVisibilityMode::Off); - EXPECT_TRUE(this->m_visibilityNode->m_impl.isDirty()); + EXPECT_TRUE(this->m_visibilityNode->impl().isDirty()); this->m_scene.flush(); this->testFlattenedVisibility(EVisibilityMode::Off); @@ -152,9 +152,9 @@ namespace ramses { this->m_childMesh->setVisibility(EVisibilityMode::Invisible); this->m_scene.flush(); - EXPECT_EQ(this->m_childMesh->m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(this->m_childMesh->impl().getFlattenedVisibility(), EVisibilityMode::Invisible); this->m_childMesh->setVisibility(EVisibilityMode::Off); this->m_scene.flush(); - EXPECT_EQ(this->m_childMesh->m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(this->m_childMesh->impl().getFlattenedVisibility(), EVisibilityMode::Off); } } diff --git a/client/test/PickableObjectTest.cpp b/tests/unittests/client/PickableObjectTest.cpp similarity index 69% rename from client/test/PickableObjectTest.cpp rename to tests/unittests/client/PickableObjectTest.cpp index 86651c2f8..ba04a8c48 100644 --- a/client/test/PickableObjectTest.cpp +++ b/tests/unittests/client/PickableObjectTest.cpp @@ -8,27 +8,25 @@ #include "gtest/gtest.h" #include "ClientTestUtils.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/ArrayBuffer.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/ArrayBuffer.h" -#include "PickableObjectImpl.h" -#include "CameraNodeImpl.h" -#include "ArrayBufferImpl.h" +#include "impl/PickableObjectImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/ArrayBufferImpl.h" using namespace testing; -using namespace ramses_internal; -namespace ramses +namespace ramses::internal { class APickableObject : public LocalTestClientWithScene, public testing::Test { protected: APickableObject() - : LocalTestClientWithScene() - , geometryBuffer(*createGeometryBuffer()) + : geometryBuffer(*createGeometryBuffer()) , pickableObject(*m_scene.createPickableObject(geometryBuffer, pickableObjectId_t(1u))) - , pickableObjectHandle(pickableObject.m_impl.getPickableObjectHandle()) + , pickableObjectHandle(pickableObject.impl().getPickableObjectHandle()) { } @@ -42,7 +40,7 @@ namespace ramses ArrayBuffer* createGeometryBuffer() { - return m_scene.createArrayBuffer(EDataType::Vector3F, 3u); + return m_scene.createArrayBuffer(ramses::EDataType::Vector3F, 3u); } void setGeometryBufferData() @@ -53,8 +51,8 @@ namespace ramses PerspectiveCamera* pickableCamera = nullptr; ArrayBuffer& geometryBuffer; - PickableObject& pickableObject; - const ramses_internal::PickableObjectHandle pickableObjectHandle; + ramses::PickableObject& pickableObject; + const PickableObjectHandle pickableObjectHandle; }; TEST_F(APickableObject, CanSetAndGetPickableCamera) @@ -64,39 +62,40 @@ namespace ramses const auto cam = pickableObject.getCamera(); EXPECT_EQ(cam, pickableObject.getCamera()); - EXPECT_EQ(cam->m_impl.getCameraHandle(), pickableCamera->m_impl.getCameraHandle()); + EXPECT_EQ(cam->impl().getCameraHandle(), pickableCamera->impl().getCameraHandle()); } TEST_F(APickableObject, CannotSetPickableCameraFromAnotherScene) { - Scene* anotherScene = client.createScene(sceneId_t(214u)); + ramses::Scene* anotherScene = client.createScene(sceneId_t(214u)); PerspectiveCamera* cameraFromAnotherScene = anotherScene->createPerspectiveCamera("pickableCamera"); ASSERT_NE(nullptr, cameraFromAnotherScene); cameraFromAnotherScene->setFrustum(-1.4f, 1.4f, -1.4f, 1.4f, 1.f, 100.f); cameraFromAnotherScene->setViewport(0, 0, 200, 200); - EXPECT_EQ(StatusOK, cameraFromAnotherScene->validate()); + ValidationReport report; + cameraFromAnotherScene->validate(report); + EXPECT_FALSE(report.hasIssue()); - EXPECT_NE(StatusOK, pickableObject.setCamera(*cameraFromAnotherScene)); + EXPECT_FALSE(pickableObject.setCamera(*cameraFromAnotherScene)); } TEST_F(APickableObject, CannotSetInvalidPickableCamera) { const PerspectiveCamera* invalidCamera = m_scene.createPerspectiveCamera(); - EXPECT_NE(StatusOK, pickableObject.setCamera(*invalidCamera)); + EXPECT_FALSE(pickableObject.setCamera(*invalidCamera)); } TEST_F(APickableObject, CanGetGeometryBufferOfPickableObject) { const ArrayBuffer& geoBuffer = pickableObject.getGeometryBuffer(); EXPECT_EQ(&geoBuffer, &geometryBuffer); - EXPECT_EQ(geoBuffer.m_impl.getDataBufferHandle(), geometryBuffer.m_impl.getDataBufferHandle()); + EXPECT_EQ(geoBuffer.impl().getDataBufferHandle(), geometryBuffer.impl().getDataBufferHandle()); } TEST_F(APickableObject, CanSetAndGetPickableObjectId) { const pickableObjectId_t testId(214); - const status_t success = pickableObject.setPickableObjectId(testId); - ASSERT_EQ(StatusOK, success); + ASSERT_TRUE(pickableObject.setPickableObjectId(testId)); const pickableObjectId_t pickableId = pickableObject.getPickableObjectId(); EXPECT_EQ(testId, pickableId); @@ -120,7 +119,9 @@ namespace ramses { setPickableCamera(); setGeometryBufferData(); - EXPECT_EQ(StatusOK, pickableObject.validate()); + ValidationReport report; + pickableObject.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(APickableObject, CannotValidatePickableObjectWhenReferencingDeletedGeometryBuffer) @@ -128,19 +129,25 @@ namespace ramses setPickableCamera(); setGeometryBufferData(); m_scene.destroy(geometryBuffer); - EXPECT_NE(StatusOK, pickableObject.validate()); + ValidationReport report; + pickableObject.validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(APickableObject, CannotValidatePickableObjectWhenReferencingInvalidGeometryBuffer) { setPickableCamera(); - EXPECT_NE(StatusOK, pickableObject.validate()); + ValidationReport report; + pickableObject.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(APickableObject, CannotValidatePickableObjectWithoutSettingCamera) { setGeometryBufferData(); - EXPECT_NE(StatusOK, pickableObject.validate()); + ValidationReport report; + pickableObject.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(APickableObject, CannotValidatePickableObjectWhenReferencingDeletedCamera) @@ -148,6 +155,8 @@ namespace ramses setPickableCamera(); setGeometryBufferData(); m_scene.destroy(*pickableCamera); - EXPECT_NE(StatusOK, pickableObject.validate()); + ValidationReport report; + pickableObject.validate(report); + EXPECT_TRUE(report.hasError()); } } diff --git a/client/test/QuadTest.cpp b/tests/unittests/client/QuadTest.cpp similarity index 99% rename from client/test/QuadTest.cpp rename to tests/unittests/client/QuadTest.cpp index 152234a52..55f96b153 100644 --- a/client/test/QuadTest.cpp +++ b/tests/unittests/client/QuadTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/Quad.h" +#include "impl/text/Quad.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { TEST(AQuadSize, KeepsParametersProvidedToConstructor) { diff --git a/client/test/RamsesClientTest.cpp b/tests/unittests/client/RamsesClientTest.cpp similarity index 67% rename from client/test/RamsesClientTest.cpp rename to tests/unittests/client/RamsesClientTest.cpp index e793a92d7..53755e4c3 100644 --- a/client/test/RamsesClientTest.cpp +++ b/tests/unittests/client/RamsesClientTest.cpp @@ -8,22 +8,22 @@ // API #include -#include "ramses-client-api/EffectDescription.h" -#include "ramses-utils.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/ramses-utils.h" -#include "RamsesClientImpl.h" -#include "SceneConfigImpl.h" -#include "Utils/File.h" +#include "impl/RamsesClientImpl.h" +#include "impl/SceneConfigImpl.h" +#include "internal/Core/Utils/File.h" #include "RamsesObjectTestTypes.h" -#include "EffectImpl.h" +#include "impl/EffectImpl.h" #include "ClientTestUtils.h" -#include "ClientApplicationLogic.h" -#include "SceneAPI/SceneId.h" +#include "internal/ClientApplicationLogic.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" #include "ClientEventHandlerMock.h" -#include "SceneReferencing/SceneReferenceEvent.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" -namespace ramses +namespace ramses::internal { using namespace testing; @@ -37,7 +37,7 @@ namespace ramses } protected: - ramses::EffectDescription effectDescriptionEmpty; + EffectDescription effectDescriptionEmpty; }; class ARamsesClient : public Test @@ -55,31 +55,49 @@ namespace ramses TEST_F(ARamsesClient, canBeValidated) { - EXPECT_EQ(StatusOK, m_client.validate()); + ValidationReport report; + m_client.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARamsesClient, failsValidationWhenContainsSceneWithInvalidRenderPass) { - ramses::Scene* scene = m_client.createScene(sceneId_t(1), ramses::SceneConfig(), ""); + ramses::Scene* scene = m_client.createScene(sceneId_t(1), ""); ASSERT_TRUE(nullptr != scene); - RenderPass* passWithoutCamera = scene->createRenderPass(); + ramses::RenderPass* passWithoutCamera = scene->createRenderPass(); ASSERT_TRUE(nullptr != passWithoutCamera); - EXPECT_NE(StatusOK, scene->validate()); - EXPECT_NE(StatusOK, m_client.validate()); + { + ValidationReport report; + scene->validate(report); + EXPECT_TRUE(report.hasIssue()); + } + { + ValidationReport report; + m_client.validate(report); + EXPECT_TRUE(report.hasIssue()); + } } TEST_F(ARamsesClient, failsValidationWhenContainsSceneWithInvalidCamera) { - ramses::Scene* scene = m_client.createScene(sceneId_t(1), ramses::SceneConfig(), ""); + ramses::Scene* scene = m_client.createScene(sceneId_t(1), ""); ASSERT_TRUE(nullptr != scene); - Camera* cameraWithoutValidValues = scene->createPerspectiveCamera(); + ramses::Camera* cameraWithoutValidValues = scene->createPerspectiveCamera(); ASSERT_TRUE(nullptr != cameraWithoutValidValues); - EXPECT_NE(StatusOK, scene->validate()); - EXPECT_NE(StatusOK, m_client.validate()); + { + ValidationReport report; + scene->validate(report); + EXPECT_TRUE(report.hasError()); + } + { + ValidationReport report; + m_client.validate(report); + EXPECT_TRUE(report.hasError()); + } } TEST_F(ARamsesClient, noEventHandlerCallbacksIfNoEvents) @@ -98,12 +116,6 @@ namespace ramses EXPECT_TRUE(m_framework.isConnected()); } - TEST_F(ALocalRamsesClient, requestNonexistantStatusMessage) - { - const char* msg = client.getStatusMessage(0xFFFFFFFF); - EXPECT_TRUE(nullptr != msg); - } - TEST(RamsesClient, canCreateClientWithNULLNameAndCmdLineArguments) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; @@ -124,7 +136,7 @@ namespace ramses { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework framework{config}; - EXPECT_EQ(ramses::StatusOK, framework.connect()); + EXPECT_TRUE(framework.connect()); EXPECT_EQ(framework.createClient({}), nullptr); } @@ -134,10 +146,10 @@ namespace ramses RamsesFramework framework{config}; auto* client = framework.createClient({}); ASSERT_NE(client, nullptr); - EXPECT_EQ(ramses::StatusOK, framework.connect()); - EXPECT_NE(ramses::StatusOK, framework.destroyClient(*client)); - EXPECT_EQ(ramses::StatusOK, framework.disconnect()); - EXPECT_EQ(ramses::StatusOK, framework.destroyClient(*client)); + EXPECT_TRUE(framework.connect()); + EXPECT_FALSE(framework.destroyClient(*client)); + EXPECT_TRUE(framework.disconnect()); + EXPECT_TRUE(framework.destroyClient(*client)); } TEST_F(ALocalRamsesClient, createsSceneWithGivenId) @@ -180,7 +192,7 @@ namespace ramses const sceneId_t sceneId(33u); ramses::Scene* scene = client.createScene(sceneId); ASSERT_TRUE(scene); - EXPECT_EQ(client.destroy(*scene), StatusOK); + EXPECT_TRUE(client.destroy(*scene)); EXPECT_FALSE(client.getScene(sceneId)); } @@ -204,44 +216,33 @@ namespace ramses EXPECT_TRUE(node != nullptr); } - TEST_F(ALocalRamsesClient, requestValidStatusMessage) - { - LocalTestClient otherClient; - Scene& scene = otherClient.createObject(); - - ramses::status_t status = client.destroy(scene); - EXPECT_NE(ramses::StatusOK, status); - const char* msg = client.getStatusMessage(status); - EXPECT_TRUE(nullptr != msg); - } - TEST_F(ALocalRamsesClient, isUnpublishedOnDestroy) { const sceneId_t sceneId(45u); ramses::Scene* scene = client.createScene(sceneId); - using ramses_internal::SceneInfoVector; - using ramses_internal::SceneInfo; - ramses_internal::SceneId internalSceneId(sceneId.getValue()); + using ramses::internal::SceneInfoVector; + using ramses::internal::SceneInfo; + ramses::internal::SceneId internalSceneId(sceneId.getValue()); EXPECT_CALL(sceneActionsCollector, handleNewSceneAvailable(SceneInfo(internalSceneId, scene->getName()), _)); EXPECT_CALL(sceneActionsCollector, handleInitializeScene(_, _)); - EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(sceneId.getValue()), _, _)); + EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(sceneId.getValue()), _, _)); EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(internalSceneId, _)); - scene->publish(ramses::EScenePublicationMode::LocalOnly); + scene->publish(EScenePublicationMode::LocalOnly); scene->flush(); - EXPECT_TRUE(client.m_impl.getClientApplication().isScenePublished(internalSceneId)); + EXPECT_TRUE(client.impl().getClientApplication().isScenePublished(internalSceneId)); client.destroy(*scene); - EXPECT_FALSE(client.m_impl.getClientApplication().isScenePublished(internalSceneId)); + EXPECT_FALSE(client.impl().getClientApplication().isScenePublished(internalSceneId)); } TEST_F(ALocalRamsesClient, returnsNullptrOnFindSceneReferenceIfThereIsNoSceneReference) { - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 123 }, sceneId_t{ 456 }), nullptr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 123 }, sceneId_t{ 456 }), nullptr); client.createScene(sceneId_t{ 123 }); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 123 }, sceneId_t{ 456 }), nullptr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 123 }, sceneId_t{ 456 }), nullptr); } TEST_F(ALocalRamsesClient, returnsNullptrOnFindSceneReferenceIfWrongReferencedSceneIdIsProvided) @@ -249,7 +250,7 @@ namespace ramses auto scene = client.createScene(sceneId_t{ 123 }); scene->createSceneReference(sceneId_t{ 456 }); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 123 }, sceneId_t{ 1 }), nullptr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 123 }, sceneId_t{ 1 }), nullptr); } TEST_F(ALocalRamsesClient, returnsNullptrOnFindSceneReferenceIfWrongMasterSceneIdIsProvided) @@ -259,9 +260,9 @@ namespace ramses auto scene2 = client.createScene(sceneId_t{ 1234 }); scene2->createSceneReference(sceneId_t{ 4567 }); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 1 }, sceneId_t{ 456 }), nullptr); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 1234 }, sceneId_t{ 456 }), nullptr); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 123 }, sceneId_t{ 4567 }), nullptr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 1 }, sceneId_t{ 456 }), nullptr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 1234 }, sceneId_t{ 456 }), nullptr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 123 }, sceneId_t{ 4567 }), nullptr); } TEST_F(ALocalRamsesClient, returnsSceneReferenceOnFindSceneReference) @@ -272,28 +273,28 @@ namespace ramses auto sr2 = scene->createSceneReference(sceneId_t{ 12345 }); auto sr3 = scene2->createSceneReference(sceneId_t{ 12345 }); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 123 }, sceneId_t{ 456 }), sr); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 123 }, sceneId_t{ 12345 }), sr2); - EXPECT_EQ(client.m_impl.findSceneReference(sceneId_t{ 1234 }, sceneId_t{ 12345 }), sr3); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 123 }, sceneId_t{ 456 }), sr); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 123 }, sceneId_t{ 12345 }), sr2); + EXPECT_EQ(client.impl().findSceneReference(sceneId_t{ 1234 }, sceneId_t{ 12345 }), sr3); } TEST_F(ALocalRamsesClient, callsAppropriateNotificationForSceneStateChangedEvent) { - constexpr auto masterScene = ramses_internal::SceneId{ 123 }; - constexpr auto reffedScene = ramses_internal::SceneId{ 456 }; + constexpr auto masterScene = ramses::internal::SceneId{ 123 }; + constexpr auto reffedScene = ramses::internal::SceneId{ 456 }; auto scene = client.createScene(sceneId_t{ masterScene.getValue() }); auto sr = scene->createSceneReference(sceneId_t{ reffedScene.getValue() }); - ramses_internal::SceneReferenceEvent event(masterScene); + ramses::internal::SceneReferenceEvent event(masterScene); event.referencedScene = reffedScene; - event.type = ramses_internal::SceneReferenceEventType::SceneStateChanged; - event.sceneState = ramses_internal::RendererSceneState::Rendered; + event.type = ramses::internal::SceneReferenceEventType::SceneStateChanged; + event.sceneState = RendererSceneState::Rendered; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); testing::StrictMock handler; - EXPECT_CALL(handler, sceneReferenceStateChanged(_, RendererSceneState::Rendered)).WillOnce([sr](SceneReference& ref, RendererSceneState) + EXPECT_CALL(handler, sceneReferenceStateChanged(_, RendererSceneState::Rendered)).WillOnce([sr](ramses::SceneReference& ref, RendererSceneState /*unused*/) { EXPECT_EQ(&ref, sr); }); @@ -302,21 +303,21 @@ namespace ramses TEST_F(ALocalRamsesClient, callsAppropriateNotificationForSceneFlushedEvent) { - constexpr auto masterScene = ramses_internal::SceneId{ 123 }; - constexpr auto reffedScene = ramses_internal::SceneId{ 456 }; + constexpr auto masterScene = ramses::internal::SceneId{ 123 }; + constexpr auto reffedScene = ramses::internal::SceneId{ 456 }; auto scene = client.createScene(sceneId_t{ masterScene.getValue() }); auto sr = scene->createSceneReference(sceneId_t{ reffedScene.getValue() }); - ramses_internal::SceneReferenceEvent event(masterScene); + ramses::internal::SceneReferenceEvent event(masterScene); event.referencedScene = reffedScene; - event.type = ramses_internal::SceneReferenceEventType::SceneFlushed; - event.tag = ramses_internal::SceneVersionTag{ 567 }; + event.type = ramses::internal::SceneReferenceEventType::SceneFlushed; + event.tag = ramses::internal::SceneVersionTag{ 567 }; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); testing::StrictMock handler; - EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 567 })).WillOnce([sr](SceneReference& ref, sceneVersionTag_t) + EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 567 })).WillOnce([sr](ramses::SceneReference& ref, sceneVersionTag_t /*version*/) { EXPECT_EQ(&ref, sr); }); @@ -325,21 +326,21 @@ namespace ramses TEST_F(ALocalRamsesClient, callsAppropriateNotificationForDataLinkedEvent) { - constexpr auto masterScene = ramses_internal::SceneId{ 123 }; - constexpr auto reffedScene = ramses_internal::SceneId{ 456 }; + constexpr auto masterScene = ramses::internal::SceneId{ 123 }; + constexpr auto reffedScene = ramses::internal::SceneId{ 456 }; auto scene = client.createScene(sceneId_t{ masterScene.getValue() }); scene->createSceneReference(sceneId_t{ reffedScene.getValue() }); - ramses_internal::SceneReferenceEvent event(masterScene); - event.type = ramses_internal::SceneReferenceEventType::DataLinked; + ramses::internal::SceneReferenceEvent event(masterScene); + event.type = ramses::internal::SceneReferenceEventType::DataLinked; event.providerScene = masterScene; event.consumerScene = reffedScene; - event.dataProvider = ramses_internal::DataSlotId{ 123 }; - event.dataConsumer = ramses_internal::DataSlotId{ 987 }; + event.dataProvider = ramses::internal::DataSlotId{ 123 }; + event.dataConsumer = ramses::internal::DataSlotId{ 987 }; event.status = false; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); testing::StrictMock handler; EXPECT_CALL(handler, dataLinked(sceneId_t{ masterScene.getValue() }, dataProviderId_t{ 123 }, sceneId_t{ reffedScene.getValue() }, dataConsumerId_t{ 987 }, false)); @@ -348,19 +349,19 @@ namespace ramses TEST_F(ALocalRamsesClient, callsAppropriateNotificationForDataUnlinkedEvent) { - constexpr auto masterScene = ramses_internal::SceneId{ 123 }; - constexpr auto reffedScene = ramses_internal::SceneId{ 456 }; + constexpr auto masterScene = ramses::internal::SceneId{ 123 }; + constexpr auto reffedScene = ramses::internal::SceneId{ 456 }; auto scene = client.createScene(sceneId_t{ masterScene.getValue() }); scene->createSceneReference(sceneId_t{ reffedScene.getValue() }); - ramses_internal::SceneReferenceEvent event(masterScene); - event.type = ramses_internal::SceneReferenceEventType::DataUnlinked; + ramses::internal::SceneReferenceEvent event(masterScene); + event.type = ramses::internal::SceneReferenceEventType::DataUnlinked; event.consumerScene = reffedScene; - event.dataConsumer = ramses_internal::DataSlotId{ 987 }; + event.dataConsumer = ramses::internal::DataSlotId{ 987 }; event.status = true; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); testing::StrictMock handler; EXPECT_CALL(handler, dataUnlinked(sceneId_t{ reffedScene.getValue() }, dataConsumerId_t{ 987 }, true)); @@ -369,32 +370,32 @@ namespace ramses TEST_F(ALocalRamsesClient, callsNotificationForEachEvent) { - constexpr auto masterScene = ramses_internal::SceneId{ 123 }; - constexpr auto reffedScene = ramses_internal::SceneId{ 456 }; + constexpr auto masterScene = ramses::internal::SceneId{ 123 }; + constexpr auto reffedScene = ramses::internal::SceneId{ 456 }; auto scene = client.createScene(sceneId_t{ masterScene.getValue() }); auto sr = scene->createSceneReference(sceneId_t{ reffedScene.getValue() }); - ramses_internal::SceneReferenceEvent event(masterScene); + ramses::internal::SceneReferenceEvent event(masterScene); event.referencedScene = reffedScene; - event.type = ramses_internal::SceneReferenceEventType::SceneFlushed; - event.tag = ramses_internal::SceneVersionTag{ 567 }; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); - event.tag = ramses_internal::SceneVersionTag{ 568 }; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); - event.tag = ramses_internal::SceneVersionTag{ 569 }; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + event.type = ramses::internal::SceneReferenceEventType::SceneFlushed; + event.tag = ramses::internal::SceneVersionTag{ 567 }; + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); + event.tag = ramses::internal::SceneVersionTag{ 568 }; + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); + event.tag = ramses::internal::SceneVersionTag{ 569 }; + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); testing::StrictMock handler; - EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 567 })).WillOnce([sr](SceneReference& ref, sceneVersionTag_t) + EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 567 })).WillOnce([sr](ramses::SceneReference& ref, sceneVersionTag_t /*version*/) { EXPECT_EQ(&ref, sr); }); - EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 568 })).WillOnce([sr](SceneReference& ref, sceneVersionTag_t) + EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 568 })).WillOnce([sr](ramses::SceneReference& ref, sceneVersionTag_t /*version*/) { EXPECT_EQ(&ref, sr); }); - EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 569 })).WillOnce([sr](SceneReference& ref, sceneVersionTag_t) + EXPECT_CALL(handler, sceneReferenceFlushed(_, sceneVersionTag_t{ 569 })).WillOnce([sr](ramses::SceneReference& ref, sceneVersionTag_t /*version*/) { EXPECT_EQ(&ref, sr); }); @@ -403,23 +404,23 @@ namespace ramses TEST_F(ALocalRamsesClient, doesNotCallAnyNotificationIfSceneReferenceDoesNotExistForNonDataLinkEvents) { - constexpr auto masterScene = ramses_internal::SceneId{ 123 }; - constexpr auto reffedScene = ramses_internal::SceneId{ 456 }; + constexpr auto masterScene = ramses::internal::SceneId{ 123 }; + constexpr auto reffedScene = ramses::internal::SceneId{ 456 }; client.createScene(sceneId_t{ masterScene.getValue() }); - ramses_internal::SceneReferenceEvent event(masterScene); + ramses::internal::SceneReferenceEvent event(masterScene); event.referencedScene = reffedScene; - event.type = ramses_internal::SceneReferenceEventType::SceneFlushed; - event.tag = ramses_internal::SceneVersionTag{ 567 }; + event.type = ramses::internal::SceneReferenceEventType::SceneFlushed; + event.tag = ramses::internal::SceneVersionTag{ 567 }; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); event.referencedScene = reffedScene; - event.type = ramses_internal::SceneReferenceEventType::SceneFlushed; - event.tag = ramses_internal::SceneVersionTag{ 567 }; + event.type = ramses::internal::SceneReferenceEventType::SceneFlushed; + event.tag = ramses::internal::SceneVersionTag{ 567 }; - client.m_impl.getClientApplication().handleSceneReferenceEvent(event, {}); + client.impl().getClientApplication().handleSceneReferenceEvent(event, {}); testing::StrictMock handler; client.dispatchEvents(handler); @@ -452,8 +453,8 @@ namespace ramses auto client1 = fw.createClient("first client"); auto client2 = fw.createClient("second client"); - EXPECT_EQ(StatusOK, fw.destroyClient(*client1)); - EXPECT_EQ(StatusOK, fw.destroyClient(*client2)); + EXPECT_TRUE(fw.destroyClient(*client1)); + EXPECT_TRUE(fw.destroyClient(*client2)); } TEST(ARamsesFrameworkImplInAClientLib, doesNotAcceptForeignCreatedClientsForDestruction) @@ -464,8 +465,8 @@ namespace ramses auto client1 = fw1.createClient("first client"); auto client2 = fw2.createClient("second client"); - EXPECT_NE(StatusOK, fw2.destroyClient(*client1)); - EXPECT_NE(StatusOK, fw1.destroyClient(*client2)); + EXPECT_FALSE(fw2.destroyClient(*client1)); + EXPECT_FALSE(fw1.destroyClient(*client2)); } TEST(ARamsesFrameworkImplInAClientLib, doesNotAcceptSameClientTwiceForDestruction) @@ -474,8 +475,8 @@ namespace ramses RamsesFramework fw{config}; auto client = fw.createClient("client"); - EXPECT_EQ(StatusOK, fw.destroyClient(*client)); - EXPECT_NE(StatusOK, fw.destroyClient(*client)); + EXPECT_TRUE(fw.destroyClient(*client)); + EXPECT_FALSE(fw.destroyClient(*client)); } TEST(ARamsesFrameworkImplInAClientLib, canCreateDestroyAndRecreateAClient) @@ -485,7 +486,7 @@ namespace ramses auto client = fw.createClient("client"); EXPECT_NE(nullptr, client); - EXPECT_EQ(fw.destroyClient(*client), StatusOK); + EXPECT_TRUE(fw.destroyClient(*client)); client = fw.createClient("client"); EXPECT_NE(nullptr, client); } diff --git a/tests/unittests/client/RamsesObjectOwnershipTest.cpp b/tests/unittests/client/RamsesObjectOwnershipTest.cpp new file mode 100644 index 000000000..a877d273d --- /dev/null +++ b/tests/unittests/client/RamsesObjectOwnershipTest.cpp @@ -0,0 +1,312 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/logic/LogicEngine.h" + +#include "impl/RamsesClientImpl.h" +#include "impl/SceneImpl.h" +#include "ClientTestUtils.h" +#include "RamsesObjectTestTypes.h" +#include "impl/NodeImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/Texture2DBufferImpl.h" +#include "impl/PickableObjectImpl.h" +#include "impl/SceneReferenceImpl.h" +#include "impl/Texture2DImpl.h" +#include "impl/Texture3DImpl.h" +#include "impl/TextureCubeImpl.h" +#include "impl/EffectImpl.h" +#include "impl/logic/LogicEngineImpl.h" + +#include "impl/RamsesObjectTypeTraits.h" +#include "impl/RamsesObjectTypeUtils.h" + +namespace ramses::internal +{ + using namespace testing; + + template + class SceneOwnershipTest : public LocalTestClientWithScene, public testing::Test + { + public: + SceneOwnershipTest() : LocalTestClientWithScene() + { + } + + void expectNoFrameworkObjectsAllocated() + { + m_creationHelper.destroyAdditionalAllocatedSceneObjects(); + const ramses::internal::IScene& scene = this->m_scene.impl().getIScene(); + + for (ramses::internal::NodeHandle i(0); i < scene.getNodeCount(); ++i) + { + EXPECT_FALSE(scene.isNodeAllocated(i)); + } + + for (ramses::internal::CameraHandle i(0); i < scene.getCameraCount(); ++i) + { + EXPECT_FALSE(scene.isCameraAllocated(i)); + } + + for (ramses::internal::TransformHandle i(0); i < scene.getTransformCount(); ++i) + { + EXPECT_FALSE(scene.isTransformAllocated(i)); + } + + for (ramses::internal::RenderableHandle i(0); i < scene.getRenderableCount(); ++i) + { + EXPECT_FALSE(scene.isRenderableAllocated(i)); + } + + for (ramses::internal::RenderStateHandle i(0); i < scene.getRenderStateCount(); ++i) + { + EXPECT_FALSE(scene.isRenderStateAllocated(i)); + } + + for (ramses::internal::DataLayoutHandle i(0); i < scene.getDataLayoutCount(); ++i) + { + EXPECT_FALSE(scene.isDataLayoutAllocated(i)); + } + + for (ramses::internal::DataInstanceHandle i(0); i < scene.getDataInstanceCount(); ++i) + { + EXPECT_FALSE(scene.isDataInstanceAllocated(i)); + } + + for (ramses::internal::RenderPassHandle i(0); i < scene.getRenderPassCount(); ++i) + { + EXPECT_FALSE(scene.isRenderPassAllocated(i)); + } + + for (ramses::internal::RenderTargetHandle i(0); i < scene.getRenderTargetCount(); ++i) + { + EXPECT_FALSE(scene.isRenderTargetAllocated(i)); + } + + for (ramses::internal::TextureSamplerHandle i(0); i < scene.getTextureSamplerCount(); ++i) + { + EXPECT_FALSE(scene.isTextureSamplerAllocated(i)); + } + } + }; + + template + class ClientOwnershipTest : public LocalTestClientWithScene, public testing::Test{}; + + TYPED_TEST_SUITE(SceneOwnershipTest, SceneObjectTypes); + TYPED_TEST_SUITE(ClientOwnershipTest, ClientObjectTypes); + + TYPED_TEST(SceneOwnershipTest, sceneObjectsAreOfTypeSceneObject) + { + auto obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::SceneObject)); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::ClientObject)); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::RamsesObject)); + } + + TYPED_TEST(SceneOwnershipTest, sceneObjectsHaveReferenceToTheirScene) + { + auto* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_EQ(&this->m_scene, &obj->getScene()); + EXPECT_EQ(&this->m_scene.impl(), &obj->impl().getSceneImpl()); + EXPECT_EQ(&this->m_scene.impl().getIScene(), &obj->impl().getIScene()); + EXPECT_EQ(&this->client.impl(), &obj->impl().getClientImpl()); + // also indirectly to framework + EXPECT_EQ(&this->framework, &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); + } + + TYPED_TEST(SceneOwnershipTest, sceneObjectsHaveReferenceToTheirScene_const) + { + const auto* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_EQ(&this->m_scene, &obj->getScene()); + EXPECT_EQ(&this->m_scene.impl(), &obj->impl().getSceneImpl()); + EXPECT_EQ(&this->m_scene.impl().getIScene(), &obj->impl().getIScene()); + EXPECT_EQ(&this->client.impl(), &obj->impl().getClientImpl()); + // also indirectly to framework + EXPECT_EQ(&this->framework, &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); + } + + TYPED_TEST(SceneOwnershipTest, sceneContainsCreatedObject) + { + const RamsesObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + const RamsesObject* sn = this->m_scene.findObject("objectName"); + ASSERT_TRUE(nullptr != sn); + EXPECT_EQ(obj, sn); + EXPECT_EQ(obj, this->m_constRefToScene.findObject("objectName")); + } + + TYPED_TEST(SceneOwnershipTest, sceneDoesNotContainDestroyedObject) + { + auto obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_TRUE(this->m_scene.destroy(*obj)); + const RamsesObject* sn = this->m_scene.findObject("objectName"); + ASSERT_TRUE(nullptr == sn); + + this->expectNoFrameworkObjectsAllocated(); + } + + TYPED_TEST(SceneOwnershipTest, creatingAndDestroyingObjectsUpdatesStatisticCounter) + { + this->m_scene.impl().getStatisticCollection().nextTimeInterval(); //object number is updated by nextTimeInterval() + uint32_t initialNumber = this->m_scene.impl().getStatisticCollection().statObjectsCount.getCounterValue(); + EXPECT_EQ(0u, this->m_scene.impl().getStatisticCollection().statObjectsCreated.getCounterValue()); + EXPECT_EQ(0u, this->m_scene.impl().getStatisticCollection().statObjectsDestroyed.getCounterValue()); + + auto obj = &this->template createObject("objectName"); + uint32_t numberCreated = this->m_scene.impl().getStatisticCollection().statObjectsCreated.getCounterValue(); + EXPECT_LE(1u, numberCreated); //some types create multiple scene objects (e.g. RenderTarget) + EXPECT_EQ(0u, this->m_scene.impl().getStatisticCollection().statObjectsDestroyed.getCounterValue()); + + this->m_scene.impl().getStatisticCollection().nextTimeInterval(); + EXPECT_EQ(initialNumber + numberCreated, this->m_scene.impl().getStatisticCollection().statObjectsCount.getCounterValue()); + + EXPECT_TRUE(this->m_scene.destroy(*obj)); + + EXPECT_LE(1u, this->m_scene.impl().getStatisticCollection().statObjectsDestroyed.getCounterValue()); + + this->m_scene.impl().getStatisticCollection().nextTimeInterval(); + EXPECT_GT(initialNumber + numberCreated, this->m_scene.impl().getStatisticCollection().statObjectsCount.getCounterValue()); + EXPECT_LE(initialNumber, this->m_scene.impl().getStatisticCollection().statObjectsCount.getCounterValue()); + } + + TYPED_TEST(SceneOwnershipTest, sceneObjectsCanBeFound) + { + auto* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + const auto id = obj->getSceneObjectId(); + const auto& constScene = this->m_scene; + EXPECT_EQ(this->m_scene.template findObject("objectName"), obj); + EXPECT_EQ(constScene.template findObject("objectName"), obj); + EXPECT_EQ(this->m_scene.template findObject(id), obj); + EXPECT_EQ(constScene.template findObject(id), obj); + // base type + EXPECT_EQ(this->m_scene.template findObject("objectName"), obj); + EXPECT_EQ(constScene.template findObject("objectName"), obj); + EXPECT_EQ(this->m_scene.template findObject(id), obj); + EXPECT_EQ(constScene.template findObject(id), obj); + // wrong type + if constexpr (std::is_same_v) + { + EXPECT_EQ(this->m_scene.template findObject("objectName"), nullptr); + EXPECT_EQ(constScene.template findObject("objectName"), nullptr); + EXPECT_EQ(this->m_scene.template findObject(id), nullptr); + EXPECT_EQ(constScene.template findObject(id), nullptr); + } + else + { + EXPECT_EQ(this->m_scene.template findObject("objectName"), nullptr); + EXPECT_EQ(constScene.template findObject("objectName"), nullptr); + EXPECT_EQ(this->m_scene.template findObject(id), nullptr); + EXPECT_EQ(constScene.template findObject(id), nullptr); + } + } + + TYPED_TEST(ClientOwnershipTest, clientContainsCreatedObject) + { + const RamsesObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + const RamsesObject* sn = this->client.findSceneByName("objectName"); + ASSERT_TRUE(nullptr != sn); + EXPECT_EQ(obj, sn); + } + + TYPED_TEST(ClientOwnershipTest, clientDoesNotContainDestroyedObject) + { + RamsesObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_TRUE(this->client.destroy(static_cast(*obj))); + const RamsesObject* sn = this->client.findSceneByName("objectName"); + ASSERT_TRUE(nullptr == sn); + } + + TYPED_TEST(SceneOwnershipTest, sceneObjectNameChanged) + { + RamsesObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_TRUE(obj->setName("otherObjectName")); + RamsesObject* sn = this->m_scene.findObject("otherObjectName"); + ASSERT_TRUE(nullptr != sn); + EXPECT_EQ(obj, sn); + } + + TYPED_TEST(ClientOwnershipTest, clientObjectsAreOfTypeClientObject) + { + const ClientObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::ClientObject)); + } + + TYPED_TEST(ClientOwnershipTest, clientObjectsHaveReferenceToTheirClient) + { + const ClientObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_EQ(&this->client.impl(), &obj->impl().getClientImpl()); + // also indirectly to framework + EXPECT_EQ(&this->framework, &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); + } + + TYPED_TEST(ClientOwnershipTest, clientObjectNameChanged) + { + RamsesObject* obj = &this->template createObject("objectName"); + ASSERT_TRUE(nullptr != obj); + EXPECT_TRUE(obj->setName("otherObjectName")); + RamsesObject* sn = this->client.findSceneByName("otherObjectName"); + ASSERT_TRUE(nullptr != sn); + EXPECT_EQ(obj, sn); + } + + TYPED_TEST(SceneOwnershipTest, objectFailsToBeDestroyedIfFromOtherScene) + { + auto& otherScene = this->template createObject("otherScene"); + + RamsesObject& obj = this->template createObject("objectName"); + auto& objTyped = static_cast(obj); + + EXPECT_FALSE(otherScene.destroy(objTyped)); + EXPECT_TRUE(this->framework.getLastError().has_value()); + } +} diff --git a/tests/unittests/client/RamsesObjectTest.cpp b/tests/unittests/client/RamsesObjectTest.cpp new file mode 100644 index 000000000..efe5af445 --- /dev/null +++ b/tests/unittests/client/RamsesObjectTest.cpp @@ -0,0 +1,226 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/SceneReference.h" +#include "ramses/client/logic/LogicEngine.h" + +#include "ramses/client/ramses-utils.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/ValidationReportImpl.h" +#include "RamsesObjectTestTypes.h" +#include "ClientTestUtils.h" + +using namespace testing; + +namespace ramses::internal +{ + template + class RamsesObjectTest : public LocalTestClientWithScene, public testing::Test + { + }; + + TYPED_TEST_SUITE(RamsesObjectTest, RamsesObjectTypes); + + TYPED_TEST(RamsesObjectTest, getType) + { + const RamsesObject& obj = this->template createObject("object"); + constexpr ERamsesObjectType type = TYPE_ID_OF_RAMSES_OBJECT::ID; + EXPECT_EQ(type, obj.getType()); + EXPECT_TRUE(obj.isOfType(ERamsesObjectType::RamsesObject)); + static_assert(RamsesObjectTypeUtils::IsConcreteType(type)); + } + + TYPED_TEST(RamsesObjectTest, isOfTypeForAllDefinedBaseClasses) + { + const RamsesObject& obj = this->template createObject("object"); + constexpr ERamsesObjectType type = TYPE_ID_OF_RAMSES_OBJECT::ID; + + auto baseType = type; + while (baseType != ERamsesObjectType::Invalid) + { + EXPECT_TRUE(RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, baseType)); + EXPECT_TRUE(obj.isOfType(type)); + baseType = RamsesObjectTraits[static_cast(baseType)].baseClassTypeID; + } + } + + TYPED_TEST(RamsesObjectTest, getSetName) + { + RamsesObject& obj = this->template createObject("object"); + EXPECT_EQ("object", obj.getName()); + EXPECT_TRUE(obj.setName("newName")); + EXPECT_EQ("newName", obj.getName()); + + if (obj.isOfType(ERamsesObjectType::SceneObject)) + { + EXPECT_EQ(nullptr, this->m_scene.findObject("object")); + EXPECT_EQ(&obj, this->m_scene.findObject("newName")); + } + } + + TYPED_TEST(RamsesObjectTest, getIdentificationString) + { + RamsesObject& obj = this->template createObject("object"); + const std::string typeName = RamsesObjectTypeUtils::GetRamsesObjectTypeName(obj.getType()); + if constexpr (std::is_base_of_v) + { + EXPECT_EQ(fmt::format("object [{} ScnObjId={}]", typeName, obj.as()->getSceneObjectId()), obj.impl().getIdentificationString()); + } + else + { + EXPECT_EQ(fmt::format("object [{}]", typeName), obj.impl().getIdentificationString()); + } + } + + TYPED_TEST(RamsesObjectTest, getSetUserID) + { + RamsesObject& obj = this->template createObject("object"); + EXPECT_EQ(0u, obj.getUserId().first); + EXPECT_EQ(0u, obj.getUserId().second); + + EXPECT_TRUE(obj.setUserId(11u, 22u)); + EXPECT_EQ(11u, obj.getUserId().first); + EXPECT_EQ(22u, obj.getUserId().second); + + const std::string typeName = RamsesObjectTypeUtils::GetRamsesObjectTypeName(obj.getType()); + if constexpr (std::is_base_of_v) + { + EXPECT_EQ(fmt::format("object [{} UserId=000000000000000B0000000000000016 ScnObjId={}]", typeName, obj.as()->getSceneObjectId()), obj.impl().getIdentificationString()); + } + else + { + EXPECT_EQ(fmt::format("object [{} UserId=000000000000000B0000000000000016]", typeName), obj.impl().getIdentificationString()); + } + } + + TYPED_TEST(RamsesObjectTest, convertToTypes) + { + RamsesObject& obj = this->template createObject("object"); + const RamsesObject& constObj = obj; + + EXPECT_TRUE(nullptr != obj.as()); + EXPECT_TRUE(nullptr != constObj.as()); + EXPECT_TRUE(nullptr != object_cast(&obj)); + EXPECT_TRUE(nullptr != object_cast(&constObj)); + + EXPECT_TRUE(nullptr != obj.as()); + EXPECT_TRUE(nullptr != constObj.as()); + EXPECT_TRUE(nullptr != object_cast(&obj)); + EXPECT_TRUE(nullptr != object_cast(&constObj)); + } + + TYPED_TEST(RamsesObjectTest, convertToItsClosestBaseClass) + { + using BaseClassType = typename CLASS_OF_RAMSES_OBJECT_TYPE::ID>::BaseTypeID>::ClassType; + + RamsesObject& obj = this->template createObject("object"); + const RamsesObject& constObj = obj; + + if (TYPE_ID_OF_RAMSES_OBJECT::ID != ERamsesObjectType::Invalid) + { + EXPECT_TRUE(nullptr != obj.as()); + EXPECT_TRUE(nullptr != constObj.as()); + EXPECT_TRUE(nullptr != object_cast(&obj)); + EXPECT_TRUE(nullptr != object_cast(&constObj)); + } + } + + TYPED_TEST(RamsesObjectTest, getRamsesObjectFromImpl) + { + RamsesObject& obj = this->template createObject("object"); + const RamsesObject& constObj = obj; + + RamsesObjectImpl& objImpl = obj.impl(); + const RamsesObjectImpl& constObjImpl = constObj.impl(); + + EXPECT_EQ(&obj, &objImpl.getRamsesObject()); + EXPECT_EQ(&constObj, &constObjImpl.getRamsesObject()); + } + + TYPED_TEST(RamsesObjectTest, convertToWrongType) + { + RamsesObject& obj = this->template createObject("object"); + const RamsesObject& constObj = obj; + + if (constObj.getType() != ERamsesObjectType::PerspectiveCamera) + { + EXPECT_TRUE(nullptr == obj.as()); + EXPECT_TRUE(nullptr == constObj.as()); + EXPECT_TRUE(nullptr == object_cast(&obj)); + EXPECT_TRUE(nullptr == object_cast(&constObj)); + } + else + { + EXPECT_TRUE(nullptr == obj.as()); + EXPECT_TRUE(nullptr == constObj.as()); + EXPECT_TRUE(nullptr == object_cast(&obj)); + EXPECT_TRUE(nullptr == object_cast(&constObj)); + } + } + + TEST(RamsesObjectTypesTest, TypeHasName) + { + static_assert(static_cast(ERamsesObjectType::TextureSamplerExternal) == 33, "add type name"); + + EXPECT_STREQ("Invalid", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Invalid)); + EXPECT_STREQ("ClientObject", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::ClientObject)); + EXPECT_STREQ("RamsesObject", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::RamsesObject)); + EXPECT_STREQ("SceneObject", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::SceneObject)); + EXPECT_STREQ("Client", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Client)); + EXPECT_STREQ("Scene", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Scene)); + EXPECT_STREQ("LogicEngine", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::LogicEngine)); + EXPECT_STREQ("LogicObject", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::LogicObject)); + EXPECT_STREQ("Node", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Node)); + EXPECT_STREQ("MeshNode", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::MeshNode)); + EXPECT_STREQ("Camera", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Camera)); + EXPECT_STREQ("PerspectiveCamera", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::PerspectiveCamera)); + EXPECT_STREQ("OrthographicCamera", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::OrthographicCamera)); + EXPECT_STREQ("Effect", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Effect)); + EXPECT_STREQ("Appearance", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Appearance)); + EXPECT_STREQ("Geometry", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Geometry)); + EXPECT_STREQ("PickableObject", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::PickableObject)); + EXPECT_STREQ("Resource", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Resource)); + EXPECT_STREQ("Texture2D", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Texture2D)); + EXPECT_STREQ("Texture3D", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Texture3D)); + EXPECT_STREQ("TextureCube", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::TextureCube)); + EXPECT_STREQ("ArrayResource", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::ArrayResource)); + EXPECT_STREQ("RenderGroup", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::RenderGroup)); + EXPECT_STREQ("RenderPass", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::RenderPass)); + EXPECT_STREQ("BlitPass", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::BlitPass)); + EXPECT_STREQ("TextureSampler", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::TextureSampler)); + EXPECT_STREQ("TextureSamplerMS", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::TextureSamplerMS)); + EXPECT_STREQ("RenderBuffer", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::RenderBuffer)); + EXPECT_STREQ("RenderTarget", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::RenderTarget)); + EXPECT_STREQ("ArrayBuffer", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::ArrayBuffer)); + EXPECT_STREQ("Texture2DBuffer", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::Texture2DBuffer)); + EXPECT_STREQ("DataObject", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::DataObject)); + EXPECT_STREQ("SceneReference", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::SceneReference)); + EXPECT_STREQ("TextureSamplerExternal", RamsesObjectTypeUtils::GetRamsesObjectTypeName(ERamsesObjectType::TextureSamplerExternal)); + } +} diff --git a/client/test/RamsesObjectTestTypes.h b/tests/unittests/client/RamsesObjectTestTypes.h similarity index 92% rename from client/test/RamsesObjectTestTypes.h rename to tests/unittests/client/RamsesObjectTestTypes.h index c6b407b93..9c4f4f16c 100644 --- a/client/test/RamsesObjectTestTypes.h +++ b/tests/unittests/client/RamsesObjectTestTypes.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RAMSESOBJECTTESTTYPES_H -#define RAMSES_RAMSESOBJECTTESTTYPES_H +#pragma once #include @@ -25,7 +24,7 @@ namespace ramses class MeshNode; class ArrayBuffer; class Texture2DBuffer; - class GeometryBinding; + class Geometry; class RenderGroup; class RenderPass; class RenderBuffer; @@ -34,12 +33,14 @@ namespace ramses class BlitPass; class TextureSampler; class TextureSamplerMS; + class TextureSamplerExternal; class Texture2D; class Texture3D; class TextureCube; class PickableObject; class SceneReference; class ArrayResource; + class LogicEngine; // Objects derived from Node class using NodeTypes = ::testing::Types< @@ -59,17 +60,19 @@ namespace ramses // Objects owned by Scene using SceneObjectTypes = ::testing::Types< + LogicEngine, Node, MeshNode, PerspectiveCamera, OrthographicCamera, Appearance, - GeometryBinding, + Geometry, RenderGroup, RenderPass, BlitPass, TextureSampler, TextureSamplerMS, + TextureSamplerExternal, RenderBuffer, RenderTarget, DataObject, @@ -90,6 +93,7 @@ namespace ramses using RamsesObjectTypes = ::testing::Types< RamsesClient, Scene, + LogicEngine, Node, MeshNode, PerspectiveCamera, @@ -105,14 +109,13 @@ namespace ramses BlitPass, TextureSampler, TextureSamplerMS, + TextureSamplerExternal, RenderBuffer, RenderTarget, ArrayBuffer, Texture2DBuffer, - GeometryBinding, + Geometry, DataObject, PickableObject, SceneReference>; } - -#endif diff --git a/tests/unittests/client/RamsesObjectValidationTest.cpp b/tests/unittests/client/RamsesObjectValidationTest.cpp new file mode 100644 index 000000000..0f612b7ce --- /dev/null +++ b/tests/unittests/client/RamsesObjectValidationTest.cpp @@ -0,0 +1,152 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/framework/ValidationReport.h" +#include "ramses/framework/RamsesObject.h" +#include "impl/RamsesObjectImpl.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + class TestRamsesObjectImpl : public RamsesObjectImpl + { + public: + explicit TestRamsesObjectImpl(std::string_view name, std::vector dependentObjs, EIssueType type) + : RamsesObjectImpl(ERamsesObjectType::Invalid, name) + , m_depObjs(std::move(dependentObjs)) + , m_type(type) + { + } + + void deinitializeFrameworkData() override {} + + void onValidate(ValidationReportImpl& report) const override + { + report.add(m_type, getName() + "msgA", &getRamsesObject()); + + // dependent objects will be validated after onValidate() returns + for (const auto depObj : m_depObjs) + report.addDependentObject(*this, depObj->impl()); + + report.add(m_type, getName() + "msgB", &getRamsesObject()); + } + + std::vector m_depObjs; + EIssueType m_type; + }; + + class TestRamsesObject : public RamsesObject + { + public: + explicit TestRamsesObject(std::string_view name, std::vector dependencies = {}, EIssueType t = EIssueType::Warning) + : RamsesObject(std::make_unique(name, std::move(dependencies), t)) + {} + + }; + + class ARamsesObjectValidation : public ::testing::Test + { + }; + + TEST_F(ARamsesObjectValidation, noDuplicatesInReport) + { + const TestRamsesObject obj1{"obj1", {}, EIssueType::Warning}; + const TestRamsesObject obj2{"obj2", {}, EIssueType::Warning}; + + ValidationReport report; + obj1.validate(report); + obj1.validate(report); + obj1.validate(report); + EXPECT_EQ(2u, report.getIssues().size()); + obj2.validate(report); + obj2.validate(report); + obj2.validate(report); + ASSERT_EQ(4u, report.getIssues().size()); + EXPECT_EQ("obj1msgA", report.getIssues()[0].message); + EXPECT_EQ("obj1msgB", report.getIssues()[1].message); + EXPECT_EQ("obj2msgA", report.getIssues()[2].message); + EXPECT_EQ("obj2msgB", report.getIssues()[3].message); + } + + TEST_F(ARamsesObjectValidation, validateTree) + { + // hierarchy of dependent object to validate + // obj1 - obj3 + // \ obj2 - obj3 + // obj3 exists twice in the tree + const TestRamsesObject objLvl3{"3"}; + const TestRamsesObject objLvl2{"2", {&objLvl3}}; + const TestRamsesObject objLvl1{"1", {&objLvl2, &objLvl3}}; + + { + ValidationReport report; + objLvl1.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + ASSERT_EQ(6u, report.getIssues().size()); + + const auto& issues = report.getIssues(); + EXPECT_EQ("1msgA", issues[0].message); + EXPECT_EQ(EIssueType::Warning, issues[0].type); + EXPECT_EQ(&objLvl1, issues[0].object); + EXPECT_EQ("2msgA", issues[2].message); + EXPECT_EQ(EIssueType::Warning, issues[2].type); + EXPECT_EQ(&objLvl2, issues[2].object); + EXPECT_EQ("3msgA", issues[4].message); + EXPECT_EQ(EIssueType::Warning, issues[4].type); + EXPECT_EQ(&objLvl3, issues[4].object); + } + { + ValidationReport report; + objLvl2.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + ASSERT_EQ(4u, report.getIssues().size()); + + const auto& issues = report.getIssues(); + EXPECT_EQ("2msgA", issues[0].message); + EXPECT_EQ(EIssueType::Warning, issues[0].type); + EXPECT_EQ(&objLvl2, issues[0].object); + EXPECT_EQ("3msgA", issues[2].message); + EXPECT_EQ(EIssueType::Warning, issues[2].type); + EXPECT_EQ(&objLvl3, issues[2].object); + } + { + ValidationReport report; + objLvl3.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + ASSERT_EQ(2u, report.getIssues().size()); + + const auto& issues = report.getIssues(); + EXPECT_EQ("3msgA", issues[0].message); + EXPECT_EQ(EIssueType::Warning, issues[0].type); + EXPECT_EQ(&objLvl3, issues[0].object); + } + } + + TEST_F(ARamsesObjectValidation, reportsStatusBasedSeverityOfValidation) + { + const TestRamsesObject objWarn{"warn", {}, EIssueType::Warning}; + const TestRamsesObject objErr{"err", {}, EIssueType::Error}; + + ValidationReport report; + EXPECT_FALSE(report.hasError()); + EXPECT_FALSE(report.hasIssue()); + + objWarn.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + + objErr.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + + EXPECT_EQ(4u, report.getIssues().size()); + } +} diff --git a/client/test/RamsesUtilsTest.cpp b/tests/unittests/client/RamsesUtilsTest.cpp similarity index 80% rename from client/test/RamsesUtilsTest.cpp rename to tests/unittests/client/RamsesUtilsTest.cpp index 022d5d261..8ab343d37 100644 --- a/client/test/RamsesUtilsTest.cpp +++ b/tests/unittests/client/RamsesUtilsTest.cpp @@ -8,33 +8,33 @@ #include #include "ClientTestUtils.h" -#include "ramses-utils.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/MipLevelData.h" -#include "ramses-client-api/TextureEnums.h" -#include "ramses-client-api/DataObject.h" -#include "Texture2DImpl.h" -#include "Utils/File.h" -#include "Math3d/ProjectionParams.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/MipLevelData.h" +#include "ramses/framework/TextureEnums.h" +#include "ramses/client/DataObject.h" +#include "impl/Texture2DImpl.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Math3d/ProjectionParams.h" #include using namespace testing; -namespace ramses +namespace ramses::internal { class ARamsesUtilsTest : public ::testing::Test, public LocalTestClientWithScene { public: static void LoadFileToVector(std::string_view fname, std::vector& data) { - ramses_internal::File f(fname); - ASSERT_TRUE(f.open(ramses_internal::File::Mode::ReadOnlyBinary)); + ramses::internal::File f(fname); + ASSERT_TRUE(f.open(ramses::internal::File::Mode::ReadOnlyBinary)); size_t filesize = 0; ASSERT_TRUE(f.getSizeInBytes(filesize)); data.resize(filesize); size_t numread = 0; - ASSERT_EQ(ramses_internal::EStatus::Ok, f.read(data.data(), filesize, numread)); + ASSERT_EQ(ramses::internal::EStatus::Ok, f.read(data.data(), filesize, numread)); ASSERT_EQ(filesize, numread); } }; @@ -44,19 +44,19 @@ namespace ramses { Texture2D* texture = RamsesUtils::CreateTextureResourceFromPng("res/sampleTexture.png", m_scene); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.destroy(*texture)); + EXPECT_TRUE(m_scene.destroy(*texture)); } TEST_F(ARamsesUtilsTest, createTextureResourceFromPng_withName) { Texture2D* texture = RamsesUtils::CreateTextureResourceFromPng("res/sampleTexture.png", m_scene, {}, "mytesttexturename"); ASSERT_TRUE(nullptr != texture); - ASSERT_STREQ("mytesttexturename", texture->getName()); + EXPECT_EQ("mytesttexturename", texture->getName()); } TEST_F(ARamsesUtilsTest, createTextureResourceFromPng_withSwizzle) { - const TextureSwizzle swizzle {ramses::ETextureChannelColor::Blue, ramses::ETextureChannelColor::Alpha, ramses::ETextureChannelColor::Green, ramses::ETextureChannelColor::Red}; + const TextureSwizzle swizzle {ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Green, ETextureChannelColor::Red}; Texture2D* texture = RamsesUtils::CreateTextureResourceFromPng("res/sampleTexture.png", m_scene, swizzle); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(swizzle.channelRed, texture->getTextureSwizzle().channelRed); @@ -72,12 +72,12 @@ namespace ramses LoadFileToVector("res/sampleTexture.png", buffer); Texture2D* texture = RamsesUtils::CreateTextureResourceFromPngBuffer(buffer, m_scene); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.destroy(*texture)); + EXPECT_TRUE(m_scene.destroy(*texture)); } TEST_F(ARamsesUtilsTest, createTextureResourceFromPngBuffer_withSwizzle) { - const TextureSwizzle swizzle {ramses::ETextureChannelColor::Blue, ramses::ETextureChannelColor::Alpha, ramses::ETextureChannelColor::Green, ramses::ETextureChannelColor::Red}; + const TextureSwizzle swizzle {ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Green, ETextureChannelColor::Red}; std::vector buffer; LoadFileToVector("res/sampleTexture.png", buffer); Texture2D* texture = RamsesUtils::CreateTextureResourceFromPngBuffer(buffer, m_scene, swizzle); @@ -86,7 +86,7 @@ namespace ramses EXPECT_EQ(swizzle.channelGreen, texture->getTextureSwizzle().channelGreen); EXPECT_EQ(swizzle.channelBlue, texture->getTextureSwizzle().channelBlue); EXPECT_EQ(swizzle.channelAlpha, texture->getTextureSwizzle().channelAlpha); - EXPECT_EQ(StatusOK, m_scene.destroy(*texture)); + EXPECT_TRUE(m_scene.destroy(*texture)); } TEST_F(ARamsesUtilsTest, createTextureResourceFromPngBuffer_withName) @@ -95,7 +95,7 @@ namespace ramses LoadFileToVector("res/sampleTexture.png", buffer); Texture2D* texture = RamsesUtils::CreateTextureResourceFromPngBuffer(buffer, m_scene, {}, "mytesttexturename"); ASSERT_TRUE(nullptr != texture); - ASSERT_STREQ("mytesttexturename", texture->getName()); + EXPECT_EQ("mytesttexturename", texture->getName()); } // texture from file: invalid uses @@ -321,10 +321,10 @@ namespace ramses const uint8_t pixelSize = 1u; const uint32_t width = 4u; const uint32_t height = 8u; - uint8_t data[32u]; + std::byte data[32u]; for (uint8_t i = 0; i < 32u; i++) { - data[i] = i + 1u; + data[i] = std::byte(i + 1u); } size_t mipMapCount = 0u; @@ -342,18 +342,18 @@ namespace ramses EXPECT_NE(data, mipData[0].m_data); // mip level 1 - EXPECT_EQ(15u, mipData[0].m_data[14]); + EXPECT_EQ(std::byte{15u}, mipData[0].m_data[14]); // mip level 2 - EXPECT_EQ(3u, mipData[1].m_data[0]); - EXPECT_EQ(29u, mipData[1].m_data[7]); + EXPECT_EQ(std::byte{3u}, mipData[1].m_data[0]); + EXPECT_EQ(std::byte{29u}, mipData[1].m_data[7]); // mip level 3 - EXPECT_EQ(8u, mipData[2].m_data[0]); - EXPECT_EQ(24u, mipData[2].m_data[1]); + EXPECT_EQ(std::byte{8u}, mipData[2].m_data[0]); + EXPECT_EQ(std::byte{24u}, mipData[2].m_data[1]); // mip level 4 - EXPECT_EQ(16u, mipData[3].m_data[0]); + EXPECT_EQ(std::byte{16u}, mipData[3].m_data[0]); RamsesUtils::DeleteGeneratedMipMaps(mipData, mipMapCount); EXPECT_FALSE(mipData); @@ -364,11 +364,11 @@ namespace ramses const uint8_t pixelSize = 2u; const uint32_t width = 8u; const uint32_t height = 4u; - uint8_t data[64u]; + std::byte data[64u]; for (uint8_t i = 0; i < 32u; i++) { - data[2*i] = i + 1u; - data[2*i + 1] = i + 1u + 32u; + data[2*i] = std::byte(i + 1u); + data[2*i + 1] = std::byte(i + 1u + 32u); } size_t mipMapCount = 0u; @@ -386,23 +386,23 @@ namespace ramses EXPECT_NE(data, mipData[0].m_data); // mip level 1 - EXPECT_EQ(15u, mipData[0].m_data[28]); + EXPECT_EQ(std::byte{15u}, mipData[0].m_data[28]); // mip level 2 - EXPECT_EQ(5u, mipData[1].m_data[0]); - EXPECT_EQ(37u, mipData[1].m_data[1]); - EXPECT_EQ(27u, mipData[1].m_data[14]); - EXPECT_EQ(59u, mipData[1].m_data[15]); + EXPECT_EQ(std::byte{5u}, mipData[1].m_data[0]); + EXPECT_EQ(std::byte{37u}, mipData[1].m_data[1]); + EXPECT_EQ(std::byte{27u}, mipData[1].m_data[14]); + EXPECT_EQ(std::byte{59u}, mipData[1].m_data[15]); // mip level 3 - EXPECT_EQ(14u, mipData[2].m_data[0]); - EXPECT_EQ(46u, mipData[2].m_data[1]); - EXPECT_EQ(18u, mipData[2].m_data[2]); - EXPECT_EQ(50u, mipData[2].m_data[3]); + EXPECT_EQ(std::byte{14u}, mipData[2].m_data[0]); + EXPECT_EQ(std::byte{46u}, mipData[2].m_data[1]); + EXPECT_EQ(std::byte{18u}, mipData[2].m_data[2]); + EXPECT_EQ(std::byte{50u}, mipData[2].m_data[3]); // mip level 4 - EXPECT_EQ(16u, mipData[3].m_data[0]); - EXPECT_EQ(48u, mipData[3].m_data[1]); + EXPECT_EQ(std::byte{16u}, mipData[3].m_data[0]); + EXPECT_EQ(std::byte{48u}, mipData[3].m_data[1]); RamsesUtils::DeleteGeneratedMipMaps(mipData, mipMapCount); EXPECT_FALSE(mipData); @@ -413,10 +413,10 @@ namespace ramses const uint8_t pixelSize = 1u; const uint32_t width = 4u; const uint32_t height = 4u; - uint8_t data[96u]; // = 4*4*6 + std::byte data[96u]; // = 4*4*6 for (uint8_t i = 0; i < 96u; i++) { - data[i] = i + 1u; + data[i] = std::byte(i + 1u); } size_t mipMapCount = 0u; @@ -438,28 +438,28 @@ namespace ramses EXPECT_NE(&data[80], mipData[0].m_dataNZ); // mip level 1 - EXPECT_EQ(11u, mipData[0].m_dataPX[10]); - EXPECT_EQ(27u, mipData[0].m_dataNX[10]); - EXPECT_EQ(43u, mipData[0].m_dataPY[10]); - EXPECT_EQ(59u, mipData[0].m_dataNY[10]); - EXPECT_EQ(75u, mipData[0].m_dataPZ[10]); - EXPECT_EQ(91u, mipData[0].m_dataNZ[10]); + EXPECT_EQ(std::byte{11u}, mipData[0].m_dataPX[10]); + EXPECT_EQ(std::byte{27u}, mipData[0].m_dataNX[10]); + EXPECT_EQ(std::byte{43u}, mipData[0].m_dataPY[10]); + EXPECT_EQ(std::byte{59u}, mipData[0].m_dataNY[10]); + EXPECT_EQ(std::byte{75u}, mipData[0].m_dataPZ[10]); + EXPECT_EQ(std::byte{91u}, mipData[0].m_dataNZ[10]); // mip level 2 - EXPECT_EQ(5u, mipData[1].m_dataPX[1]); - EXPECT_EQ(21u, mipData[1].m_dataNX[1]); - EXPECT_EQ(37u, mipData[1].m_dataPY[1]); - EXPECT_EQ(53u, mipData[1].m_dataNY[1]); - EXPECT_EQ(69u, mipData[1].m_dataPZ[1]); - EXPECT_EQ(85u, mipData[1].m_dataNZ[1]); + EXPECT_EQ(std::byte{5u}, mipData[1].m_dataPX[1]); + EXPECT_EQ(std::byte{21u}, mipData[1].m_dataNX[1]); + EXPECT_EQ(std::byte{37u}, mipData[1].m_dataPY[1]); + EXPECT_EQ(std::byte{53u}, mipData[1].m_dataNY[1]); + EXPECT_EQ(std::byte{69u}, mipData[1].m_dataPZ[1]); + EXPECT_EQ(std::byte{85u}, mipData[1].m_dataNZ[1]); // mip level 3 - EXPECT_EQ(8u, mipData[2].m_dataPX[0]); - EXPECT_EQ(24u, mipData[2].m_dataNX[0]); - EXPECT_EQ(40u, mipData[2].m_dataPY[0]); - EXPECT_EQ(56u, mipData[2].m_dataNY[0]); - EXPECT_EQ(72u, mipData[2].m_dataPZ[0]); - EXPECT_EQ(88u, mipData[2].m_dataNZ[0]); + EXPECT_EQ(std::byte{8u}, mipData[2].m_dataPX[0]); + EXPECT_EQ(std::byte{24u}, mipData[2].m_dataNX[0]); + EXPECT_EQ(std::byte{40u}, mipData[2].m_dataPY[0]); + EXPECT_EQ(std::byte{56u}, mipData[2].m_dataNY[0]); + EXPECT_EQ(std::byte{72u}, mipData[2].m_dataPZ[0]); + EXPECT_EQ(std::byte{88u}, mipData[2].m_dataNZ[0]); RamsesUtils::DeleteGeneratedMipMaps(mipData, mipMapCount); EXPECT_FALSE(mipData); @@ -467,15 +467,15 @@ namespace ramses TEST_F(ARamsesUtilsTest, canSetFrustumOnDataObjects) { - auto& do1 = *m_scene.createDataObject(EDataType::Vector4F); - auto& do2 = *m_scene.createDataObject(EDataType::Vector2F); + auto& do1 = *m_scene.createDataObject(ramses::EDataType::Vector4F); + auto& do2 = *m_scene.createDataObject(ramses::EDataType::Vector2F); EXPECT_TRUE(RamsesUtils::SetPerspectiveCameraFrustumToDataObjects(33.f, 1.5f, 1.f, 2.f, do1, do2)); - const auto expectedParams = ramses_internal::ProjectionParams::Perspective(33.f, 1.5f, 1.f, 2.f); + const auto expectedParams = ramses::internal::ProjectionParams::Perspective(33.f, 1.5f, 1.f, 2.f); vec4f planes1; vec2f planes2; - EXPECT_EQ(StatusOK, do1.getValue(planes1)); - EXPECT_EQ(StatusOK, do2.getValue(planes2)); + EXPECT_TRUE(do1.getValue(planes1)); + EXPECT_TRUE(do2.getValue(planes2)); EXPECT_EQ(expectedParams.leftPlane, planes1[0]); EXPECT_EQ(expectedParams.rightPlane, planes1[1]); EXPECT_EQ(expectedParams.bottomPlane, planes1[2]); @@ -486,8 +486,8 @@ namespace ramses TEST_F(ARamsesUtilsTest, failsToSetInvalidFrustumOnDataObjects) { - auto& do1 = *m_scene.createDataObject(EDataType::Vector4F); - auto& do2 = *m_scene.createDataObject(EDataType::Vector2F); + auto& do1 = *m_scene.createDataObject(ramses::EDataType::Vector4F); + auto& do2 = *m_scene.createDataObject(ramses::EDataType::Vector2F); EXPECT_FALSE(RamsesUtils::SetPerspectiveCameraFrustumToDataObjects(0.f, 1.5f, 1.f, 2.f, do1, do2)); EXPECT_FALSE(RamsesUtils::SetPerspectiveCameraFrustumToDataObjects(-33.f, 1.5f, 1.f, 2.f, do1, do2)); EXPECT_FALSE(RamsesUtils::SetPerspectiveCameraFrustumToDataObjects(33.f, 0.f, 1.f, 2.f, do1, do2)); @@ -499,6 +499,6 @@ namespace ramses TEST_F(ARamsesUtilsTest, getNodeHandleReturnsMemoryHandle) { MeshNode& node = this->createValidMeshNode(); - EXPECT_EQ(node.m_impl.getNodeHandle().asMemoryHandle(), RamsesUtils::GetNodeId(node).getValue()); + EXPECT_EQ(node.impl().getNodeHandle().asMemoryHandle(), RamsesUtils::GetNodeId(node).getValue()); } } diff --git a/client/test/RamsesVersionTest.cpp b/tests/unittests/client/RamsesVersionTest.cpp similarity index 90% rename from client/test/RamsesVersionTest.cpp rename to tests/unittests/client/RamsesVersionTest.cpp index e7ee8573f..eb6bd24ae 100644 --- a/client/test/RamsesVersionTest.cpp +++ b/tests/unittests/client/RamsesVersionTest.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RamsesVersion.h" +#include "internal/RamsesVersion.h" #include "gtest/gtest.h" -#include "Utils/BinaryOutputStream.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" #include "ramses-sdk-build-config.h" -namespace ramses_internal +namespace ramses::internal { class ARamsesVersion : public ::testing::Test { @@ -27,21 +27,21 @@ namespace ramses_internal } RamsesVersion::VersionInfo info; - ramses::EFeatureLevel featureLevel = ramses::EFeatureLevel_01; + EFeatureLevel featureLevel = EFeatureLevel_01; }; TEST_F(ARamsesVersion, canParseCurrentVersion) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); EXPECT_STREQ(::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, info.versionString.c_str()); EXPECT_STREQ(::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, info.gitHash.c_str()); - EXPECT_EQ(static_cast(std::atoi(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR)), info.major); - EXPECT_EQ(static_cast(std::atoi(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR)), info.minor); - EXPECT_EQ(ramses::EFeatureLevel_Latest, featureLevel); + EXPECT_EQ(static_cast(std::strtoul(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR, nullptr, 10)), info.major); + EXPECT_EQ(static_cast(std::strtoul(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR, nullptr, 10)), info.minor); + EXPECT_EQ(EFeatureLevel_Latest, featureLevel); } TEST_F(ARamsesVersion, canParseCurrentVersionFromFile) @@ -49,7 +49,7 @@ namespace ramses_internal { File fOut("ramsesVersionTest"); BinaryFileOutputStream out(fOut); - RamsesVersion::WriteToStream(out, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, ::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, ::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, EFeatureLevel_Latest); } File fIn("ramsesVersionTest"); BinaryFileInputStream in(fIn); @@ -57,15 +57,15 @@ namespace ramses_internal EXPECT_STREQ(::ramses_sdk::RAMSES_SDK_RAMSES_VERSION, info.versionString.c_str()); EXPECT_STREQ(::ramses_sdk::RAMSES_SDK_GIT_COMMIT_HASH, info.gitHash.c_str()); - EXPECT_EQ(static_cast(std::atoi(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR)), info.major); - EXPECT_EQ(static_cast(std::atoi(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR)), info.minor); - EXPECT_EQ(ramses::EFeatureLevel_Latest, featureLevel); + EXPECT_EQ(static_cast(std::strtoul(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MAJOR, nullptr, 10)), info.major); + EXPECT_EQ(static_cast(std::strtoul(::ramses_sdk::RAMSES_SDK_PROJECT_VERSION_MINOR, nullptr, 10)), info.minor); + EXPECT_EQ(EFeatureLevel_Latest, featureLevel); } TEST_F(ARamsesVersion, canParseReleaseVersion) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "17.0.1", "af98afa8e94", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "17.0.1", "af98afa8e94", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -78,7 +78,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseMasterVersion) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "0.0.0-devMaster", "af98afa8e94", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "0.0.0-devMaster", "af98afa8e94", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -91,7 +91,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseOtherVersionSuffix) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "0.0.0-releaseCandidate", "af98afa8e94", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "0.0.0-releaseCandidate", "af98afa8e94", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -104,7 +104,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseVersionNumbersInCorrectOrder) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "1.2.3-devMaster", "af98afa8e94", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "1.2.3-devMaster", "af98afa8e94", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -117,7 +117,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseLongVersionNumbers) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "11.22.333", "af98afa8e94", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "11.22.333", "af98afa8e94", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -130,7 +130,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseUnknownAsHashValue) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "1.3.2", "(unknown)", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "1.3.2", "(unknown)", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -143,7 +143,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseFullHashValue) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "1.3.2", "ce01334641b90dbfc02cbee5d072cec8f9000afe", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "1.3.2", "ce01334641b90dbfc02cbee5d072cec8f9000afe", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -154,7 +154,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseMajorMinorPatchTweak) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "1.3.2.5", "ce01334641b90dbfc02cbee5d072cec8f9000afe", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "1.3.2.5", "ce01334641b90dbfc02cbee5d072cec8f9000afe", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -167,7 +167,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseDifferentSuffix) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "1.3.2-foobar", "ce01334641b90dbfc02cbee5d072cec8f9000afe", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "1.3.2-foobar", "ce01334641b90dbfc02cbee5d072cec8f9000afe", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -180,7 +180,7 @@ namespace ramses_internal TEST_F(ARamsesVersion, canParseRandomStringAfterMajorMinor) { BinaryOutputStream out; - RamsesVersion::WriteToStream(out, "1.3.a.b.c+hotfix3", "ce01334641b90dbfc02cbee5d072cec8f9000afe", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "1.3.a.b.c+hotfix3", "ce01334641b90dbfc02cbee5d072cec8f9000afe", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_TRUE(RamsesVersion::ReadFromStream(in, info, featureLevel)); @@ -280,7 +280,7 @@ namespace ramses_internal s += ' '; } BinaryOutputStream out(CreateOutputStreamFromString(s)); - RamsesVersion::WriteToStream(out, "17.0.1", "af98afa8eeee94", ramses::EFeatureLevel_Latest); + RamsesVersion::WriteToStream(out, "17.0.1", "af98afa8eeee94", EFeatureLevel_Latest); BinaryInputStream in(out.getData()); EXPECT_FALSE(RamsesVersion::ReadFromStream(in, info, featureLevel)); } diff --git a/tests/unittests/client/RenderBufferTest.cpp b/tests/unittests/client/RenderBufferTest.cpp new file mode 100644 index 000000000..7c347e021 --- /dev/null +++ b/tests/unittests/client/RenderBufferTest.cpp @@ -0,0 +1,158 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/OrthographicCamera.h" +#include "impl/RenderBufferImpl.h" +#include "ClientTestUtils.h" + +using namespace testing; + +namespace ramses::internal +{ + class RenderBufferTest : public LocalTestClientWithScene, public testing::Test + { + protected: + void useInRenderPass(const ramses::RenderBuffer& rb) + { + RenderTargetDescription rtDesc; + rtDesc.addRenderBuffer(rb); + ramses::RenderTarget* rt = this->m_scene.createRenderTarget(rtDesc); + ramses::RenderPass* rp = this->m_scene.createRenderPass(); + OrthographicCamera* orthoCam = this->m_scene.createOrthographicCamera("camera"); + orthoCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); + orthoCam->setViewport(0, 0, 100, 200); + rp->setCamera(*orthoCam); + rp->setRenderTarget(rt); + } + + void useInBlitPassAsSource(const ramses::RenderBuffer& rb) + { + const ramses::RenderBuffer* dummyRb = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + this->m_scene.createBlitPass(rb, *dummyRb); + } + + void useInBlitPassAsDestination(const ramses::RenderBuffer& rb) + { + const ramses::RenderBuffer* dummyRb = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + this->m_scene.createBlitPass(*dummyRb, rb); + } + }; + + TEST_F(RenderBufferTest, canCreateRenderBuffer) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(600u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 4u, "RenderBuffer"); + ASSERT_TRUE(renderBuffer != nullptr); + EXPECT_EQ(600u, renderBuffer->getWidth()); + EXPECT_EQ(400u, renderBuffer->getHeight()); + EXPECT_EQ(ERenderBufferFormat::RGBA8, renderBuffer->getBufferFormat()); + EXPECT_EQ(ERenderBufferAccessMode::WriteOnly, renderBuffer->getAccessMode()); + EXPECT_EQ(4u, renderBuffer->getSampleCount()); + const ramses::internal::RenderBufferHandle rbHandle = renderBuffer->impl().getRenderBufferHandle(); + EXPECT_TRUE(m_internalScene.isRenderBufferAllocated(rbHandle)); + } + + TEST_F(RenderBufferTest, failsToCreateRenderBufferWithZeroWidthOrHeight) + { + ramses::RenderBuffer* rbWithZeroWidth = m_scene.createRenderBuffer(0u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_EQ(nullptr, rbWithZeroWidth); + + ramses::RenderBuffer* rbWithZeroHeight = m_scene.createRenderBuffer(400u, 0u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_EQ(nullptr, rbWithZeroHeight); + } + + TEST_F(RenderBufferTest, canCreateReadWriteMSAARenderBuffer) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(600u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u, "RenderBuffer"); + ASSERT_TRUE(renderBuffer != nullptr); + EXPECT_EQ(600u, renderBuffer->getWidth()); + EXPECT_EQ(400u, renderBuffer->getHeight()); + EXPECT_EQ(ERenderBufferFormat::RGBA8, renderBuffer->getBufferFormat()); + EXPECT_EQ(ERenderBufferAccessMode::ReadWrite, renderBuffer->getAccessMode()); + EXPECT_EQ(4u, renderBuffer->getSampleCount()); + const ramses::internal::RenderBufferHandle rbHandle = renderBuffer->impl().getRenderBufferHandle(); + EXPECT_TRUE(m_internalScene.isRenderBufferAllocated(rbHandle)); + } + + TEST_F(RenderBufferTest, reportsWarningIfNotUsedInAnyRenderPassNorBlitPass) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(RenderBufferTest, reportsWarningIfUsedInRenderPassButNotReferencedByAnySamplerNorUsedAsBlitPassSource) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + useInRenderPass(*renderBuffer); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(RenderBufferTest, reportsWarningIfReferencedBySamplerButNotUsedInAnyRenderPassNorUsedAsBlitPassDestination) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Nearest, *renderBuffer); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(RenderBufferTest, validatesWhenUsedInRenderPassAndReferencedBySampler) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + useInRenderPass(*renderBuffer); + m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Nearest, *renderBuffer); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(RenderBufferTest, validatesWhenUsedAsBlitPassDestinationAndReferencedBySampler) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + useInBlitPassAsDestination(*renderBuffer); + m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Nearest, *renderBuffer); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(RenderBufferTest, validatesWhenNOTReferencedBySamplerButUsedAsBlitPassSource) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + useInRenderPass(*renderBuffer); + useInBlitPassAsSource(*renderBuffer); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(RenderBufferTest, doesNotReportsErrorIfUsedInRenderPassButNotReferencedByAnySampler_DepthBufferType) + { + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(400u, 400u, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite); + ASSERT_TRUE(renderBuffer != nullptr); + useInRenderPass(*renderBuffer); + ValidationReport report; + renderBuffer->validate(report); + EXPECT_FALSE(report.hasIssue()); + } +} diff --git a/client/test/RenderGroupMeshIteratorTest.cpp b/tests/unittests/client/RenderGroupMeshIteratorTest.cpp similarity index 85% rename from client/test/RenderGroupMeshIteratorTest.cpp rename to tests/unittests/client/RenderGroupMeshIteratorTest.cpp index e9a748aeb..4151e48af 100644 --- a/client/test/RenderGroupMeshIteratorTest.cpp +++ b/tests/unittests/client/RenderGroupMeshIteratorTest.cpp @@ -6,27 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/RenderGroupMeshIterator.h" -#include "ramses-client-api/RenderGroup.h" +#include "ramses/client/RenderGroupMeshIterator.h" +#include "ramses/client/RenderGroup.h" #include "ClientTestUtils.h" using namespace testing; -namespace ramses +namespace ramses::internal { class ARenderGroupMeshIterator : public LocalTestClientWithScene, public testing::Test { public: ARenderGroupMeshIterator() - : LocalTestClientWithScene() - , renderGroup(*m_scene.createRenderGroup()) + : renderGroup(*m_scene.createRenderGroup()) , mesh1(*m_scene.createMeshNode()) , mesh2(*m_scene.createMeshNode()) { } protected: - RenderGroup& renderGroup; + ramses::RenderGroup& renderGroup; const MeshNode& mesh1; const MeshNode& mesh2; }; diff --git a/tests/unittests/client/RenderGroupTest.cpp b/tests/unittests/client/RenderGroupTest.cpp new file mode 100644 index 000000000..b21cf7c9f --- /dev/null +++ b/tests/unittests/client/RenderGroupTest.cpp @@ -0,0 +1,545 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "ClientTestUtils.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Geometry.h" +#include "impl/RenderGroupImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/RamsesClientImpl.h" +#include "internal/SceneGraph/SceneAPI/RenderGroup.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" + +using namespace testing; + +namespace ramses::internal +{ + class ARenderGroup : public LocalTestClientWithScene, public testing::Test + { + protected: + ARenderGroup() + : renderGroup(*m_scene.createRenderGroup("RenderGroup")) + , renderGroup2(*m_scene.createRenderGroup("RenderGroup2")) + { + } + + static void ExpectNumMeshesContained(uint32_t numMeshes, const ramses::RenderGroup& group) + { + EXPECT_EQ(numMeshes, static_cast(group.impl().getAllMeshes().size())); + } + + static void ExpectNumRenderGroupsContained(uint32_t numRenderGroups, const ramses::RenderGroup& group) + { + EXPECT_EQ(numRenderGroups, static_cast(group.impl().getAllRenderGroups().size())); + } + + void expectMeshContained(const MeshNode& mesh, int32_t order, const ramses::RenderGroup& group) + { + EXPECT_TRUE(group.containsMeshNode(mesh)); + int32_t actualOrder = 0; + EXPECT_TRUE(group.getMeshNodeOrder(mesh, actualOrder)); + EXPECT_EQ(order, actualOrder); + + const auto& internalRg = m_internalScene.getRenderGroup(group.impl().getRenderGroupHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderable(mesh.impl().getRenderableHandle(), internalRg)); + EXPECT_EQ(order, ramses::internal::RenderGroupUtils::FindRenderableEntry(mesh.impl().getRenderableHandle(), internalRg)->order); + } + + void expectRenderGroupContained(const ramses::RenderGroup& nestedRenderGroup, int32_t order, const ramses::RenderGroup& group) + { + EXPECT_TRUE(group.containsRenderGroup(nestedRenderGroup)); + int32_t actualOrder = 0; + EXPECT_TRUE(group.getRenderGroupOrder(nestedRenderGroup, actualOrder)); + EXPECT_EQ(order, actualOrder); + + const auto& internalRg = m_internalScene.getRenderGroup(group.impl().getRenderGroupHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(nestedRenderGroup.impl().getRenderGroupHandle(), internalRg)); + EXPECT_EQ(order, ramses::internal::RenderGroupUtils::FindRenderGroupEntry(nestedRenderGroup.impl().getRenderGroupHandle(), internalRg)->order); + } + + void expectMeshNotContained(const MeshNode& mesh, const ramses::RenderGroup& group) + { + EXPECT_FALSE(group.containsMeshNode(mesh)); + const auto& internalRg = m_internalScene.getRenderGroup(group.impl().getRenderGroupHandle()); + EXPECT_FALSE(ramses::internal::RenderGroupUtils::ContainsRenderable(mesh.impl().getRenderableHandle(), internalRg)); + } + + void expectRenderGroupNotContained(const ramses::RenderGroup& nestedRenderGroup, const ramses::RenderGroup& group) + { + EXPECT_FALSE(group.containsRenderGroup(nestedRenderGroup)); + const auto& internalRg = m_internalScene.getRenderGroup(group.impl().getRenderGroupHandle()); + EXPECT_FALSE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(nestedRenderGroup.impl().getRenderGroupHandle(), internalRg)); + } + + void addValidMeshToRenderGroup() + { + MeshNode& mesh = createValidMeshNode(); + EXPECT_TRUE(renderGroup.addMeshNode(mesh, 3)); + } + + void addBrokenMeshToRenderGroup() + { + addBrokenMeshToRenderGroup(renderGroup); + } + + void addBrokenMeshToRenderGroup(ramses::RenderGroup& renderGroupParam) + { + MeshNode* mesh = m_scene.createMeshNode(); + Appearance* appearance = m_scene.createAppearance(*TestEffects::CreateTestEffect(m_scene), "appearance"); + mesh->setAppearance(*appearance); + // missing geometry binding + EXPECT_TRUE(renderGroupParam.addMeshNode(*mesh, 3)); + } + + ramses::RenderGroup& renderGroup; + ramses::RenderGroup& renderGroup2; + }; + + TEST_F(ARenderGroup, canValidate) + { + addValidMeshToRenderGroup(); + + ValidationReport report; + renderGroup.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ARenderGroup, validationGivesWarningIfRenderGroupIsEmpty) + { + ValidationReport report; + renderGroup.validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(ARenderGroup, validationGivesWarningIfNestedRenderGroupIsEmpty) + { + addValidMeshToRenderGroup(); + ValidationReport report; + renderGroup.validate(report); + ASSERT_FALSE(report.hasIssue()); + + renderGroup.addRenderGroup(renderGroup2); + + report.clear(); + renderGroup.validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(ARenderGroup, validatesIfEmptyButNestedRenderGroupIsNot) + { + // empty -> invalid + ValidationReport report; + renderGroup.validate(report); + EXPECT_TRUE(report.hasIssue()); + + // nested group also empty -> invalid + renderGroup.addRenderGroup(renderGroup2); + report.clear(); + renderGroup.validate(report); + EXPECT_TRUE(report.hasIssue()); + + // add mesh to nested group -> valid + EXPECT_TRUE(renderGroup2.addMeshNode(createValidMeshNode(), 3)); + report.clear(); + renderGroup.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ARenderGroup, validationGivesWarningIfRenderGroupContainsInvalidMesh) + { + addBrokenMeshToRenderGroup(); + + ValidationReport report; + renderGroup.validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(ARenderGroup, validationGivesWarningIfNestedRenderGroupContainsInvalidMesh) + { + addValidMeshToRenderGroup(); + ValidationReport report; + renderGroup.validate(report); + ASSERT_FALSE(report.hasIssue()); + + addBrokenMeshToRenderGroup(renderGroup2); + + renderGroup.validate(report); + ASSERT_FALSE(report.hasIssue()); + + ASSERT_TRUE(renderGroup.addRenderGroup(renderGroup2)); + + report.clear(); + renderGroup.validate(report); + EXPECT_TRUE(report.hasIssue()); + } + + TEST_F(ARenderGroup, canAddMeshNodes) + { + MeshNode* mesh = m_scene.createMeshNode(); + MeshNode* mesh2 = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 3)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh2, -7)); + + ExpectNumMeshesContained(2u, renderGroup); + expectMeshContained(*mesh, 3, renderGroup); + expectMeshContained(*mesh2, -7, renderGroup); + } + + TEST_F(ARenderGroup, canAddRenderGroups) + { + addValidMeshToRenderGroup(); + MeshNode& mesh2 = createValidMeshNode(); + ASSERT_TRUE(renderGroup2.addMeshNode(mesh2, 5)); + + EXPECT_TRUE(renderGroup.addRenderGroup(renderGroup2, 2)); + + ValidationReport report; + renderGroup.validate(report); + EXPECT_FALSE(report.hasIssue()); + renderGroup2.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ARenderGroup, reportsErrorWhenAddMeshNodeFromAnotherScene) + { + ramses::Scene& anotherScene = *client.createScene(sceneId_t(12u)); + MeshNode* mesh = anotherScene.createMeshNode(); + + EXPECT_FALSE(renderGroup.addMeshNode(*mesh)); + } + + TEST_F(ARenderGroup, reportsErrorWhenAddRenderGroupFromAnotherScene) + { + ramses::Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::RenderGroup* nestedRenderGroup = anotherScene.createRenderGroup(); + + EXPECT_FALSE(renderGroup.addRenderGroup(*nestedRenderGroup)); + } + + TEST_F(ARenderGroup, canCheckContainmentOfMeshes) + { + MeshNode* mesh = m_scene.createMeshNode(); + MeshNode* mesh2 = m_scene.createMeshNode(); + MeshNode* mesh3 = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh3, 3)); + + ExpectNumMeshesContained(2u, renderGroup); + expectMeshContained(*mesh, 1, renderGroup); + expectMeshNotContained(*mesh2, renderGroup); + expectMeshContained(*mesh3, 3, renderGroup); + } + + TEST_F(ARenderGroup, canCheckContainmentOfNestedRenderGroups) + { + expectRenderGroupNotContained(renderGroup2, renderGroup); + + EXPECT_TRUE(renderGroup.addRenderGroup(renderGroup2, 2)); + + ExpectNumRenderGroupsContained(1u, renderGroup); + expectRenderGroupContained(renderGroup2, 2, renderGroup); + } + + TEST_F(ARenderGroup, doesNotDirectlyContainRenderablesOfNestedRenderGroups) + { + MeshNode* mesh = m_scene.createMeshNode(); + + ASSERT_TRUE(renderGroup2.addMeshNode(*mesh, 1)); + ASSERT_TRUE(renderGroup.addRenderGroup(renderGroup2)); + + expectMeshNotContained(*mesh, renderGroup); + } + + TEST_F(ARenderGroup, canRemoveMesh) + { + MeshNode* mesh = m_scene.createMeshNode(); + ASSERT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.removeMeshNode(*mesh)); + + ExpectNumMeshesContained(0u, renderGroup); + expectMeshNotContained(*mesh, renderGroup); + } + + TEST_F(ARenderGroup, canRemoveRenderGroup) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 1)); + EXPECT_TRUE(renderGroup.removeRenderGroup(*nestedRenderGroup)); + + ExpectNumRenderGroupsContained(0u, renderGroup); + expectRenderGroupNotContained(*nestedRenderGroup, renderGroup); + } + + TEST_F(ARenderGroup, removingMeshDoesNotAffectOtherMeshes) + { + MeshNode* mesh = m_scene.createMeshNode(); + MeshNode* mesh2 = m_scene.createMeshNode(); + MeshNode* mesh3 = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh2, 2)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh3, 3)); + + EXPECT_TRUE(renderGroup.removeMeshNode(*mesh2)); + + ExpectNumMeshesContained(2u, renderGroup); + expectMeshContained(*mesh, 1, renderGroup); + expectMeshNotContained(*mesh2, renderGroup); + expectMeshContained(*mesh3, 3, renderGroup); + } + + TEST_F(ARenderGroup, removingRenderGroupDoesNotAffectOtherRenderGroups) + { + ramses::RenderGroup* nestedRenderGroup1 = m_scene.createRenderGroup(); + ramses::RenderGroup* nestedRenderGroup2 = m_scene.createRenderGroup(); + ramses::RenderGroup* nestedRenderGroup3 = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup1, 1)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup2, 2)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup3, 3)); + + ASSERT_TRUE(renderGroup.removeRenderGroup(*nestedRenderGroup2)); + + ExpectNumRenderGroupsContained(2u, renderGroup); + expectRenderGroupContained(*nestedRenderGroup1, 1, renderGroup); + expectRenderGroupNotContained(*nestedRenderGroup2, renderGroup); + expectRenderGroupContained(*nestedRenderGroup3, 3, renderGroup); + } + + TEST_F(ARenderGroup, doesNotContainMeshWhichWasDestroyed) + { + MeshNode* mesh = m_scene.createMeshNode(); + ASSERT_TRUE(renderGroup.addMeshNode(*mesh)); + ASSERT_TRUE(m_scene.destroy(*mesh)); + + ExpectNumMeshesContained(0u, renderGroup); + } + + TEST_F(ARenderGroup, doesNotContainRenderGroupWhichWasDestroyed) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup)); + ASSERT_TRUE(m_scene.destroy(*nestedRenderGroup)); + + ExpectNumRenderGroupsContained(0u, renderGroup); + } + + TEST_F(ARenderGroup, destroyingMeshDoesNotAffectOtherMeshes) + { + MeshNode* mesh = m_scene.createMeshNode(); + MeshNode* mesh2 = m_scene.createMeshNode(); + MeshNode* mesh3 = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh2, 2)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh3, 3)); + + EXPECT_TRUE(m_scene.destroy(*mesh2)); + + ExpectNumMeshesContained(2u, renderGroup); + expectMeshContained(*mesh, 1, renderGroup); + expectMeshContained(*mesh3, 3, renderGroup); + } + + TEST_F(ARenderGroup, destroyingRenderGroupDoesNotAffectOtherRenderGroups) + { + ramses::RenderGroup* nestedRenderGroup1 = m_scene.createRenderGroup(); + ramses::RenderGroup* nestedRenderGroup2 = m_scene.createRenderGroup(); + ramses::RenderGroup* nestedRenderGroup3 = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup1, 1)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup2, 2)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup3, 3)); + + EXPECT_TRUE(m_scene.destroy(*nestedRenderGroup2)); + + ExpectNumRenderGroupsContained(2u, renderGroup); + expectRenderGroupContained(*nestedRenderGroup1, 1, renderGroup); + expectRenderGroupContained(*nestedRenderGroup3, 3, renderGroup); + } + + TEST_F(ARenderGroup, canRemoveAllMeshes) + { + MeshNode* mesh = m_scene.createMeshNode(); + MeshNode* mesh2 = m_scene.createMeshNode(); + MeshNode* mesh3 = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh2, 2)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh3, 3)); + + EXPECT_TRUE(renderGroup.removeAllRenderables()); + + ExpectNumMeshesContained(0u, renderGroup); + expectMeshNotContained(*mesh, renderGroup); + expectMeshNotContained(*mesh2, renderGroup); + expectMeshNotContained(*mesh3, renderGroup); + } + + TEST_F(ARenderGroup, canRemoveAllRenderGroups) + { + ramses::RenderGroup* nestedRenderGroup1 = m_scene.createRenderGroup(); + ramses::RenderGroup* nestedRenderGroup2 = m_scene.createRenderGroup(); + ramses::RenderGroup* nestedRenderGroup3 = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup1, 1)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup2, 2)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup3, 3)); + + EXPECT_TRUE(renderGroup.removeAllRenderGroups()); + + ExpectNumRenderGroupsContained(0u, renderGroup); + expectRenderGroupNotContained(*nestedRenderGroup1, renderGroup); + expectRenderGroupNotContained(*nestedRenderGroup2, renderGroup); + expectRenderGroupNotContained(*nestedRenderGroup3, renderGroup); + } + + TEST_F(ARenderGroup, reportsErrorWhenTryingToRemoveMeshWhichWasNotAdded) + { + MeshNode* mesh = m_scene.createMeshNode(); + bool status = renderGroup.removeMeshNode(*mesh); + + EXPECT_FALSE(status); + } + + TEST_F(ARenderGroup, reportsErrorWhenTryingToRemoveRenderGroupWhichWasNotAdded) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + EXPECT_FALSE(renderGroup.removeRenderGroup(*nestedRenderGroup)); + } + + TEST_F(ARenderGroup, canChangeMeshOrderByReaddingIt) + { + MeshNode* mesh = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 999)); + + expectMeshContained(*mesh, 999, renderGroup); + } + + TEST_F(ARenderGroup, canChangeRenderGroupOrderByReaddingIt) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 1)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 999)); + + expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup); + } + + TEST_F(ARenderGroup, canChangeMeshOrderByRemovingItAndAddingAgain) + { + MeshNode* mesh = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 1)); + EXPECT_TRUE(renderGroup.removeMeshNode(*mesh)); + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 999)); + + expectMeshContained(*mesh, 999, renderGroup); + } + + TEST_F(ARenderGroup, canChangeRenderGroupOrderByRemovingItAndAddingAgain) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 1)); + ASSERT_TRUE(renderGroup.removeRenderGroup(*nestedRenderGroup)); + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 999)); + + expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup); + } + + TEST_F(ARenderGroup, meshCanBeAddedToTwoGroupsWithDifferentOrder) + { + MeshNode* mesh = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 3)); + EXPECT_TRUE(renderGroup2.addMeshNode(*mesh, 999)); + + ExpectNumMeshesContained(1u, renderGroup); + ExpectNumMeshesContained(1u, renderGroup2); + expectMeshContained(*mesh, 3, renderGroup); + expectMeshContained(*mesh, 999, renderGroup2); + } + + TEST_F(ARenderGroup, renderGroupCanBeAddedToTwoGroupsWithDifferentOrder) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + + EXPECT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 3)); + EXPECT_TRUE(renderGroup2.addRenderGroup(*nestedRenderGroup, 999)); + + ExpectNumRenderGroupsContained(1u, renderGroup); + ExpectNumRenderGroupsContained(1u, renderGroup2); + expectRenderGroupContained(*nestedRenderGroup, 3, renderGroup); + expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup2); + } + + TEST_F(ARenderGroup, removingMeshFromGroupDoesNotAffectItsPlaceInAnotherGroup) + { + MeshNode* mesh = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 3)); + EXPECT_TRUE(renderGroup2.addMeshNode(*mesh, 999)); + + EXPECT_TRUE(renderGroup.removeMeshNode(*mesh)); + + ExpectNumMeshesContained(0u, renderGroup); + ExpectNumMeshesContained(1u, renderGroup2); + expectMeshNotContained(*mesh, renderGroup); + expectMeshContained(*mesh, 999, renderGroup2); + } + + TEST_F(ARenderGroup, removingRenderGroupFromGroupDoesNotAffectItsPlaceInAnotherGroup) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 3)); + ASSERT_TRUE(renderGroup2.addRenderGroup(*nestedRenderGroup, 999)); + + EXPECT_TRUE(renderGroup.removeRenderGroup(*nestedRenderGroup)); + + ExpectNumRenderGroupsContained(0u, renderGroup); + ExpectNumRenderGroupsContained(1u, renderGroup2); + expectRenderGroupNotContained(*nestedRenderGroup, renderGroup); + expectRenderGroupContained(*nestedRenderGroup, 999, renderGroup2); + } + + TEST_F(ARenderGroup, destroyedMeshIsRemovedFromAllItsGroups) + { + MeshNode* mesh = m_scene.createMeshNode(); + + EXPECT_TRUE(renderGroup.addMeshNode(*mesh, 3)); + EXPECT_TRUE(renderGroup2.addMeshNode(*mesh, 999)); + + EXPECT_TRUE(m_scene.destroy(*mesh)); + + ExpectNumMeshesContained(0u, renderGroup); + ExpectNumMeshesContained(0u, renderGroup2); + } + + TEST_F(ARenderGroup, destroyedNestedRenderGroupIsRemovedFromAllItsGroups) + { + ramses::RenderGroup* nestedRenderGroup = m_scene.createRenderGroup(); + + ASSERT_TRUE(renderGroup.addRenderGroup(*nestedRenderGroup, 3)); + ASSERT_TRUE(renderGroup2.addRenderGroup(*nestedRenderGroup, 999)); + + ASSERT_TRUE(m_scene.destroy(*nestedRenderGroup)); + + ExpectNumRenderGroupsContained(0u, renderGroup); + ExpectNumRenderGroupsContained(0u, renderGroup2); + } +} diff --git a/client/test/RenderPassGroupIteratorTest.cpp b/tests/unittests/client/RenderPassGroupIteratorTest.cpp similarity index 81% rename from client/test/RenderPassGroupIteratorTest.cpp rename to tests/unittests/client/RenderPassGroupIteratorTest.cpp index 8863409f8..ad8a2dcf9 100644 --- a/client/test/RenderPassGroupIteratorTest.cpp +++ b/tests/unittests/client/RenderPassGroupIteratorTest.cpp @@ -6,29 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/RenderPassGroupIterator.h" -#include "ramses-client-api/RenderPass.h" +#include "ramses/client/RenderPassGroupIterator.h" +#include "ramses/client/RenderPass.h" #include "ClientTestUtils.h" using namespace testing; -namespace ramses +namespace ramses::internal { class ARenderPassGroupIterator : public LocalTestClientWithScene, public testing::Test { public: ARenderPassGroupIterator() - : LocalTestClientWithScene() - , renderPass(*m_scene.createRenderPass()) + : renderPass(*m_scene.createRenderPass()) , group1(*m_scene.createRenderGroup()) , group2(*m_scene.createRenderGroup()) { } protected: - RenderPass& renderPass; - const RenderGroup& group1; - const RenderGroup& group2; + ramses::RenderPass& renderPass; + const ramses::RenderGroup& group1; + const ramses::RenderGroup& group2; }; TEST_F(ARenderPassGroupIterator, getNextNULLForEmptyRenderGroup) diff --git a/client/test/RenderPassTest.cpp b/tests/unittests/client/RenderPassTest.cpp similarity index 51% rename from client/test/RenderPassTest.cpp rename to tests/unittests/client/RenderPassTest.cpp index 56af06a56..c0e1b565b 100644 --- a/client/test/RenderPassTest.cpp +++ b/tests/unittests/client/RenderPassTest.cpp @@ -9,33 +9,31 @@ #include #include "ClientTestUtils.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Camera.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "SceneImpl.h" -#include "RenderPassImpl.h" -#include "RenderGroupImpl.h" -#include "RenderTargetImpl.h" -#include "ramses-utils.h" -#include "SceneAPI/RenderGroupUtils.h" -#include "Scene/ClientScene.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Camera.h" +#include "ramses/client/RenderTargetDescription.h" +#include "impl/SceneImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderTargetImpl.h" +#include "ramses/client/ramses-utils.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" using namespace testing; -using namespace ramses_internal; -namespace ramses +namespace ramses::internal { class ARenderPass : public LocalTestClientWithScene, public testing::Test { protected: ARenderPass() - : LocalTestClientWithScene() - , renderpass(*m_scene.createRenderPass("RenderPass")) + : renderpass(*m_scene.createRenderPass("RenderPass")) , renderpass2(*m_scene.createRenderPass("RenderPass2")) { } @@ -57,41 +55,41 @@ namespace ramses return perspCam; } - RenderTarget* createRenderTarget() + ramses::RenderTarget* createRenderTarget() { - const RenderBuffer* renderBuffer = m_scene.createRenderBuffer(16u, 12u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(16u, 12u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(*renderBuffer); return m_scene.createRenderTarget(rtDesc); } - void expectNumGroupsContained(uint32_t numGroups, const RenderPass& pass) + static void ExpectNumGroupsContained(uint32_t numGroups, const ramses::RenderPass& pass) { - EXPECT_EQ(numGroups, static_cast(pass.m_impl.getAllRenderGroups().size())); + EXPECT_EQ(numGroups, static_cast(pass.impl().getAllRenderGroups().size())); } - void expectGroupContained(const RenderGroup& group, int32_t order, const RenderPass& pass) + void expectGroupContained(const ramses::RenderGroup& group, int32_t order, const ramses::RenderPass& pass) { EXPECT_TRUE(pass.containsRenderGroup(group)); int32_t actualOrder = 0; - EXPECT_EQ(StatusOK, pass.getRenderGroupOrder(group, actualOrder)); + EXPECT_TRUE(pass.getRenderGroupOrder(group, actualOrder)); EXPECT_EQ(order, actualOrder); - const ramses_internal::RenderPass& internalRP = m_internalScene.getRenderPass(pass.m_impl.getRenderPassHandle()); - ASSERT_TRUE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(group.m_impl.getRenderGroupHandle(), internalRP)); - EXPECT_EQ(group.m_impl.getRenderGroupHandle(), ramses_internal::RenderGroupUtils::FindRenderGroupEntry(group.m_impl.getRenderGroupHandle(), internalRP)->renderGroup); - EXPECT_EQ(order, ramses_internal::RenderGroupUtils::FindRenderGroupEntry(group.m_impl.getRenderGroupHandle(), internalRP)->order); + const ramses::internal::RenderPass& internalRP = m_internalScene.getRenderPass(pass.impl().getRenderPassHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(group.impl().getRenderGroupHandle(), internalRP)); + EXPECT_EQ(group.impl().getRenderGroupHandle(), ramses::internal::RenderGroupUtils::FindRenderGroupEntry(group.impl().getRenderGroupHandle(), internalRP)->renderGroup); + EXPECT_EQ(order, ramses::internal::RenderGroupUtils::FindRenderGroupEntry(group.impl().getRenderGroupHandle(), internalRP)->order); } - void expectGroupNotContained(const RenderGroup& group, const RenderPass& pass) + void expectGroupNotContained(const ramses::RenderGroup& group, const ramses::RenderPass& pass) { EXPECT_FALSE(pass.containsRenderGroup(group)); - const ramses_internal::RenderPass& internalRP = m_internalScene.getRenderPass(pass.m_impl.getRenderPassHandle()); - EXPECT_FALSE(ramses_internal::RenderGroupUtils::ContainsRenderGroup(group.m_impl.getRenderGroupHandle(), internalRP)); + const ramses::internal::RenderPass& internalRP = m_internalScene.getRenderPass(pass.impl().getRenderPassHandle()); + EXPECT_FALSE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(group.impl().getRenderGroupHandle(), internalRP)); } - RenderPass& renderpass; - RenderPass& renderpass2; + ramses::RenderPass& renderpass; + ramses::RenderPass& renderpass2; }; TEST_F(ARenderPass, canValidate) @@ -99,181 +97,212 @@ namespace ramses renderpass.setCamera(*createDummyPerspectiveCamera()); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_None); + renderpass.setClearFlags(EClearFlag::None); - EXPECT_EQ(StatusOK, renderpass.validate()); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, failsValidationIfNoCameraWasSet) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); renderpass.addRenderGroup(*group); - EXPECT_NE(StatusOK, renderpass.validate()); + ValidationReport report; + renderpass.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(ARenderPass, failsValidationIfNoRenderGroupWasAdded) { renderpass.setCamera(*createDummyOrthoCamera()); - EXPECT_NE(StatusOK, renderpass.validate()); + + ValidationReport report; + renderpass.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(ARenderPass, validatesIfClearAllAndRenderTargetIsSet) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(renderTarget); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_All); - EXPECT_EQ(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::All); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, validatesIfClearStencilAndRenderTargetIsSet) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(renderTarget); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Stencil); - EXPECT_EQ(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::Stencil); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, validatesIfClearDepthAndRenderTargetIsSet) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(renderTarget); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Depth); - EXPECT_EQ(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::Depth); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, validatesIfClearColorAndRenderTargetIsSet) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(renderTarget); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Color); - EXPECT_EQ(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::Color); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, validatesIfClearNoneAndRenderTargetIsSet) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(renderTarget); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_None); - EXPECT_EQ(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::None); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, validatesIfClearNoneIsSetWithoutRenderTarget) { const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(nullptr); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_None); - EXPECT_EQ(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::None); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ARenderPass, failsValidationIfClearStencilIsSetWithoutRenderTarget) { const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(nullptr); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Stencil); - EXPECT_NE(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::Stencil); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + EXPECT_THAT(report.getIssues()[0].message, ::testing::HasSubstr("clear flags will have no effect")); + EXPECT_EQ(report.getIssues()[0].type, EIssueType::Warning); } TEST_F(ARenderPass, failsValidationIfClearDepthIsSetWithoutRenderTarget) { const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(nullptr); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Depth); - EXPECT_NE(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::Depth); + ValidationReport report; + renderpass.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); } TEST_F(ARenderPass, failsValidationIfClearColorIsSetWithoutRenderTarget) { const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(nullptr); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Color); - EXPECT_NE(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::Color); + ValidationReport report; + renderpass.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(ARenderPass, failsValidationIfClearAllIsSetWithoutRenderTarget) { const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); MeshNode& mesh = createValidMeshNode(); - RenderGroup* renderGroup = m_scene.createRenderGroup(); + ramses::RenderGroup* renderGroup = m_scene.createRenderGroup(); renderGroup->addMeshNode(mesh); renderpass.setCamera(*perspectiveCamera); renderpass.setRenderTarget(nullptr); renderpass.addRenderGroup(*renderGroup); - renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_All); - EXPECT_NE(StatusOK, renderpass.validate()); + renderpass.setClearFlags(EClearFlag::All); + ValidationReport report; + renderpass.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(ARenderPass, hasNoCameraByDefaultWhichIsInterpretedAsIdentityViewMatrix) @@ -283,40 +312,40 @@ namespace ramses TEST_F(ARenderPass, returnsTheSameCameraWhichWasSet) { - Camera* camera = createDummyPerspectiveCamera(); + ramses::Camera* camera = createDummyPerspectiveCamera(); - EXPECT_EQ(StatusOK, renderpass.setCamera(*camera)); + EXPECT_TRUE(renderpass.setCamera(*camera)); EXPECT_EQ(camera, renderpass.getCamera()); } TEST_F(ARenderPass, reportsErrorWhenSetCameraFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(sceneId_t(12u)); // create valid camera from another scene auto camera = anotherScene.createOrthographicCamera(); camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); camera->setViewport(0, 0, 100, 200); - EXPECT_NE(StatusOK, renderpass.setCamera(*camera)); + EXPECT_FALSE(renderpass.setCamera(*camera)); } TEST_F(ARenderPass, canBeAssignedCustomOrthoCameraOnlyWhenAllParametersAreValid) { OrthographicCamera* orthoCam = m_scene.createOrthographicCamera(); - EXPECT_NE(StatusOK, renderpass.setCamera(*orthoCam)); + EXPECT_FALSE(renderpass.setCamera(*orthoCam)); EXPECT_NE(orthoCam, renderpass.getCamera()); // Viewport is correct, this is enough because initialized correctly orthoCam->setViewport(0, 0, 5, 10); - EXPECT_NE(StatusOK, renderpass.setCamera(*orthoCam)); + EXPECT_FALSE(renderpass.setCamera(*orthoCam)); EXPECT_NE(orthoCam, renderpass.getCamera()); // Clipping planes set -> camera is valid now orthoCam->setFrustum(0.f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - EXPECT_EQ(StatusOK, renderpass.setCamera(*orthoCam)); + EXPECT_TRUE(renderpass.setCamera(*orthoCam)); EXPECT_EQ(orthoCam, renderpass.getCamera()); } @@ -324,118 +353,118 @@ namespace ramses { PerspectiveCamera* perspCam = m_scene.createPerspectiveCamera(); - EXPECT_NE(StatusOK, renderpass.setCamera(*perspCam)); + EXPECT_FALSE(renderpass.setCamera(*perspCam)); EXPECT_NE(perspCam, renderpass.getCamera()); // Viewport is correct, but still not enough perspCam->setViewport(0, 0, 5, 10); - EXPECT_NE(StatusOK, renderpass.setCamera(*perspCam)); + EXPECT_FALSE(renderpass.setCamera(*perspCam)); EXPECT_NE(perspCam, renderpass.getCamera()); // Frustum set -> camera is valid now perspCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); - EXPECT_EQ(StatusOK, renderpass.setCamera(*perspCam)); + EXPECT_TRUE(renderpass.setCamera(*perspCam)); EXPECT_EQ(perspCam, renderpass.getCamera()); } TEST_F(ARenderPass, confidence_setsCameraMulitpleTimesAndChecksInScene) { - Camera* camera = createDummyPerspectiveCamera(); - Camera* camera2 = createDummyOrthoCamera(); + ramses::Camera* camera = createDummyPerspectiveCamera(); + ramses::Camera* camera2 = createDummyOrthoCamera(); - EXPECT_EQ(StatusOK, renderpass.setCamera(*camera)); + EXPECT_TRUE(renderpass.setCamera(*camera)); EXPECT_EQ(camera, renderpass.getCamera()); - EXPECT_EQ(StatusOK, renderpass.setCamera(*camera2)); + EXPECT_TRUE(renderpass.setCamera(*camera2)); EXPECT_EQ(camera2, renderpass.getCamera()); } TEST_F(ARenderPass, hasFrameBufferAsDefaultRenderTarget) { EXPECT_EQ(nullptr, renderpass.getRenderTarget()); - EXPECT_FALSE(m_scene.m_impl.getIScene().getRenderPass(renderpass.m_impl.getRenderPassHandle()).renderTarget.isValid()); + EXPECT_FALSE(m_scene.impl().getIScene().getRenderPass(renderpass.impl().getRenderPassHandle()).renderTarget.isValid()); } TEST_F(ARenderPass, canBeAssignedFrameBufferAsRenderTarget) { - EXPECT_EQ(StatusOK, renderpass.setRenderTarget(nullptr)); + EXPECT_TRUE(renderpass.setRenderTarget(nullptr)); EXPECT_EQ(nullptr, renderpass.getRenderTarget()); - EXPECT_FALSE(m_scene.m_impl.getIScene().getRenderPass(renderpass.m_impl.getRenderPassHandle()).renderTarget.isValid()); + EXPECT_FALSE(m_scene.impl().getIScene().getRenderPass(renderpass.impl().getRenderPassHandle()).renderTarget.isValid()); } TEST_F(ARenderPass, canSwitchFromRenderTargetBackToFramebuffer) { // first set valid rendertarget PerspectiveCamera* camera = m_scene.createPerspectiveCamera(); - setValidPerspectiveCameraParameters(*camera); - EXPECT_EQ(StatusOK, renderpass.setCamera(*camera)); - RenderTarget* renderTarget = createRenderTarget(); + SetValidPerspectiveCameraParameters(*camera); + EXPECT_TRUE(renderpass.setCamera(*camera)); + ramses::RenderTarget* renderTarget = createRenderTarget(); renderpass.setRenderTarget(renderTarget); // then set framebuffer rendering - EXPECT_EQ(StatusOK, renderpass.setRenderTarget(nullptr)); + EXPECT_TRUE(renderpass.setRenderTarget(nullptr)); EXPECT_EQ(nullptr, renderpass.getRenderTarget()); - EXPECT_FALSE(m_scene.m_impl.getIScene().getRenderPass(renderpass.m_impl.getRenderPassHandle()).renderTarget.isValid()); + EXPECT_FALSE(m_scene.impl().getIScene().getRenderPass(renderpass.impl().getRenderPassHandle()).renderTarget.isValid()); } TEST_F(ARenderPass, reportsErrorWhenSetRenderTargetFromAnotherScene) { - Scene& anotherScene = *client.createScene(ramses::sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(ramses::sceneId_t(12u)); - const RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(16u, 12u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(16u, 12u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(*renderBuffer); - RenderTarget* renderTarget = anotherScene.createRenderTarget(rtDesc); + ramses::RenderTarget* renderTarget = anotherScene.createRenderTarget(rtDesc); ASSERT_TRUE(nullptr != renderTarget); PerspectiveCamera* camera = createDummyPerspectiveCamera(); ASSERT_TRUE(nullptr != camera); - EXPECT_EQ(StatusOK, renderpass.setCamera(*camera)); + EXPECT_TRUE(renderpass.setCamera(*camera)); - EXPECT_NE(StatusOK, renderpass.setRenderTarget(renderTarget)); + EXPECT_FALSE(renderpass.setRenderTarget(renderTarget)); } TEST_F(ARenderPass, canNotBeAssignedRenderTargetIfCameraIsNotSetFirst) { - RenderTarget* renderTarget = createRenderTarget(); - EXPECT_NE(StatusOK, renderpass.setRenderTarget(renderTarget)); - EXPECT_NE(renderTarget->m_impl.getRenderTargetHandle(), m_scene.m_impl.getIScene().getRenderPass(renderpass.m_impl.getRenderPassHandle()).renderTarget); + ramses::RenderTarget* renderTarget = createRenderTarget(); + EXPECT_FALSE(renderpass.setRenderTarget(renderTarget)); + EXPECT_NE(renderTarget->impl().getRenderTargetHandle(), m_scene.impl().getIScene().getRenderPass(renderpass.impl().getRenderPassHandle()).renderTarget); EXPECT_NE(renderTarget, renderpass.getRenderTarget()); } TEST_F(ARenderPass, canBeAssignedRenderTargetWithOrthoCamera) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const OrthographicCamera* orthoCamera = createDummyOrthoCamera(); - ASSERT_EQ(StatusOK, renderpass.setCamera(*orthoCamera)); - EXPECT_EQ(StatusOK, renderpass.setRenderTarget(renderTarget)); + ASSERT_TRUE(renderpass.setCamera(*orthoCamera)); + EXPECT_TRUE(renderpass.setRenderTarget(renderTarget)); - EXPECT_EQ(renderTarget->m_impl.getRenderTargetHandle(), m_scene.m_impl.getIScene().getRenderPass(renderpass.m_impl.getRenderPassHandle()).renderTarget); + EXPECT_EQ(renderTarget->impl().getRenderTargetHandle(), m_scene.impl().getIScene().getRenderPass(renderpass.impl().getRenderPassHandle()).renderTarget); EXPECT_EQ(renderTarget, renderpass.getRenderTarget()); } TEST_F(ARenderPass, canBeAssignedRenderTargetWithPerspectiveCamera) { - RenderTarget* renderTarget = createRenderTarget(); + ramses::RenderTarget* renderTarget = createRenderTarget(); const PerspectiveCamera* perspectiveCamera = createDummyPerspectiveCamera(); - ASSERT_EQ(StatusOK, renderpass.setCamera(*perspectiveCamera)); - EXPECT_EQ(StatusOK, renderpass.setRenderTarget(renderTarget)); + ASSERT_TRUE(renderpass.setCamera(*perspectiveCamera)); + EXPECT_TRUE(renderpass.setRenderTarget(renderTarget)); - EXPECT_EQ(renderTarget->m_impl.getRenderTargetHandle(), m_scene.m_impl.getIScene().getRenderPass(renderpass.m_impl.getRenderPassHandle()).renderTarget); + EXPECT_EQ(renderTarget->impl().getRenderTargetHandle(), m_scene.impl().getIScene().getRenderPass(renderpass.impl().getRenderPassHandle()).renderTarget); EXPECT_EQ(renderTarget, renderpass.getRenderTarget()); } TEST_F(ARenderPass, reportsErrorWhenDestroyingRenderTargetIfAssignedToRenderPass) { - RenderTarget* renderTarget = createRenderTarget(); - ASSERT_EQ(StatusOK, renderpass.setCamera(*createDummyPerspectiveCamera())); + ramses::RenderTarget* renderTarget = createRenderTarget(); + ASSERT_TRUE(renderpass.setCamera(*createDummyPerspectiveCamera())); - EXPECT_EQ(StatusOK, renderpass.setRenderTarget(renderTarget)); + EXPECT_TRUE(renderpass.setRenderTarget(renderTarget)); EXPECT_EQ(renderTarget, renderpass.getRenderTarget()); - EXPECT_NE(StatusOK, m_scene.destroy(*renderTarget)); + EXPECT_FALSE(m_scene.destroy(*renderTarget)); EXPECT_EQ(renderTarget, renderpass.getRenderTarget()); } @@ -447,7 +476,7 @@ namespace ramses TEST_F(ARenderPass, canBeDisabled) { - EXPECT_EQ(StatusOK, renderpass.setEnabled(false)); + EXPECT_TRUE(renderpass.setEnabled(false)); EXPECT_FALSE(renderpass.isEnabled()); } @@ -459,7 +488,7 @@ namespace ramses EXPECT_EQ(0.f, c[2]); EXPECT_EQ(1.f, c[3]); - EXPECT_EQ(StatusOK, renderpass.setClearColor({0.1f, 0.2f, 0.3f, 0.4f})); + EXPECT_TRUE(renderpass.setClearColor({0.1f, 0.2f, 0.3f, 0.4f})); c = renderpass.getClearColor(); EXPECT_EQ(0.1f, c[0]); EXPECT_EQ(0.2f, c[1]); @@ -469,49 +498,49 @@ namespace ramses TEST_F(ARenderPass, setsItsClearFlag) { - EXPECT_EQ(static_cast(ramses::EClearFlags::EClearFlags_All), renderpass.getClearFlags()); + EXPECT_EQ(EClearFlag::All, renderpass.getClearFlags()); - EXPECT_EQ(StatusOK, renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_None)); - EXPECT_EQ(static_cast(ramses::EClearFlags::EClearFlags_None), renderpass.getClearFlags()); + EXPECT_TRUE(renderpass.setClearFlags(EClearFlag::None)); + EXPECT_EQ(EClearFlag::None, renderpass.getClearFlags()); - EXPECT_EQ(StatusOK, renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Depth)); - EXPECT_EQ(static_cast(ramses::EClearFlags::EClearFlags_Depth), renderpass.getClearFlags()); + EXPECT_TRUE(renderpass.setClearFlags(EClearFlag::Depth)); + EXPECT_EQ(EClearFlag::Depth, renderpass.getClearFlags()); - EXPECT_EQ(StatusOK, renderpass.setClearFlags(ramses::EClearFlags::EClearFlags_Color | ramses::EClearFlags::EClearFlags_Stencil)); - EXPECT_EQ(static_cast(ramses::EClearFlags::EClearFlags_Color | ramses::EClearFlags::EClearFlags_Stencil), renderpass.getClearFlags()); + EXPECT_TRUE(renderpass.setClearFlags(EClearFlag::Color | EClearFlag::Stencil)); + EXPECT_EQ(EClearFlag::Color | EClearFlag::Stencil, renderpass.getClearFlags()); } TEST_F(ARenderPass, canAddRenderGroups) { - RenderGroup* group = m_scene.createRenderGroup(); - RenderGroup* group2 = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group2 = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 3)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group2, -7)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 3)); + EXPECT_TRUE(renderpass.addRenderGroup(*group2, -7)); - expectNumGroupsContained(2u, renderpass); + ExpectNumGroupsContained(2u, renderpass); expectGroupContained(*group, 3, renderpass); expectGroupContained(*group2, -7, renderpass); } TEST_F(ARenderPass, reportsErrorWhenAddRenderGroupFromAnotherScene) { - Scene& anotherScene = *client.createScene(ramses::sceneId_t(12u)); - RenderGroup* group = anotherScene.createRenderGroup(); + ramses::Scene& anotherScene = *client.createScene(ramses::sceneId_t(12u)); + ramses::RenderGroup* group = anotherScene.createRenderGroup(); - EXPECT_NE(StatusOK, renderpass.addRenderGroup(*group, 3)); + EXPECT_FALSE(renderpass.addRenderGroup(*group, 3)); } TEST_F(ARenderPass, canCheckContainmentOfRenderGroups) { - RenderGroup* group = m_scene.createRenderGroup(); - RenderGroup* group2 = m_scene.createRenderGroup(); - RenderGroup* group3 = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group2 = m_scene.createRenderGroup(); + ramses::RenderGroup* group3 = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 1)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group3, 3)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 1)); + EXPECT_TRUE(renderpass.addRenderGroup(*group3, 3)); - expectNumGroupsContained(2u, renderpass); + ExpectNumGroupsContained(2u, renderpass); expectGroupContained(*group, 1, renderpass); expectGroupNotContained(*group2, renderpass); expectGroupContained(*group3, 3, renderpass); @@ -519,27 +548,27 @@ namespace ramses TEST_F(ARenderPass, canRemoveRenderGroup) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); renderpass.addRenderGroup(*group, 1); renderpass.removeRenderGroup(*group); - expectNumGroupsContained(0u, renderpass); + ExpectNumGroupsContained(0u, renderpass); expectGroupNotContained(*group, renderpass); } TEST_F(ARenderPass, removingRenderGroupDoesNotAffectOtherRenderGroups) { - RenderGroup* group = m_scene.createRenderGroup(); - RenderGroup* group2 = m_scene.createRenderGroup(); - RenderGroup* group3 = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group2 = m_scene.createRenderGroup(); + ramses::RenderGroup* group3 = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 1)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group2, 2)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group3, 3)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 1)); + EXPECT_TRUE(renderpass.addRenderGroup(*group2, 2)); + EXPECT_TRUE(renderpass.addRenderGroup(*group3, 3)); - EXPECT_EQ(StatusOK, renderpass.removeRenderGroup(*group2)); + EXPECT_TRUE(renderpass.removeRenderGroup(*group2)); - expectNumGroupsContained(2u, renderpass); + ExpectNumGroupsContained(2u, renderpass); expectGroupContained(*group, 1, renderpass); expectGroupNotContained(*group2, renderpass); expectGroupContained(*group3, 3, renderpass); @@ -547,55 +576,55 @@ namespace ramses TEST_F(ARenderPass, doesNotContainRenderGroupWhichWasDestroyed) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); renderpass.addRenderGroup(*group); m_scene.destroy(*group); - expectNumGroupsContained(0u, renderpass); + ExpectNumGroupsContained(0u, renderpass); } TEST_F(ARenderPass, destroyingRenderGroupDoesNotAffectOtherRenderGroups) { - RenderGroup* group = m_scene.createRenderGroup(); - RenderGroup* group2 = m_scene.createRenderGroup(); - RenderGroup* group3 = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group2 = m_scene.createRenderGroup(); + ramses::RenderGroup* group3 = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 1)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group2, 2)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group3, 3)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 1)); + EXPECT_TRUE(renderpass.addRenderGroup(*group2, 2)); + EXPECT_TRUE(renderpass.addRenderGroup(*group3, 3)); - EXPECT_EQ(StatusOK, m_scene.destroy(*group2)); + EXPECT_TRUE(m_scene.destroy(*group2)); - expectNumGroupsContained(2u, renderpass); + ExpectNumGroupsContained(2u, renderpass); expectGroupContained(*group, 1, renderpass); expectGroupContained(*group3, 3, renderpass); } TEST_F(ARenderPass, reportsErrorWhenTryingToRemoveRenderGroupWhichWasNotAddedToPass) { - RenderGroup* group = m_scene.createRenderGroup(); - status_t status = renderpass.removeRenderGroup(*group); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + bool status = renderpass.removeRenderGroup(*group); - EXPECT_NE(StatusOK, status); + EXPECT_FALSE(status); } TEST_F(ARenderPass, canChangeRenderGroupOrderByReaddingIt) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 1)); - EXPECT_EQ(StatusOK, renderpass.removeRenderGroup(*group)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 999)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 1)); + EXPECT_TRUE(renderpass.removeRenderGroup(*group)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 999)); expectGroupContained(*group, 999, renderpass); } TEST_F(ARenderPass, groupCanBeAddedToTwoPassesWithDifferentOrder) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 3)); - EXPECT_EQ(StatusOK, renderpass2.addRenderGroup(*group, 999)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 3)); + EXPECT_TRUE(renderpass2.addRenderGroup(*group, 999)); expectGroupContained(*group, 3, renderpass); expectGroupContained(*group, 999, renderpass2); @@ -603,12 +632,12 @@ namespace ramses TEST_F(ARenderPass, removingRenderGroupFromPassDoesNotAffectItsPlaceInAnotherPass) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 3)); - EXPECT_EQ(StatusOK, renderpass2.addRenderGroup(*group, 999)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 3)); + EXPECT_TRUE(renderpass2.addRenderGroup(*group, 999)); - EXPECT_EQ(StatusOK, renderpass.removeRenderGroup(*group)); + EXPECT_TRUE(renderpass.removeRenderGroup(*group)); expectGroupNotContained(*group, renderpass); expectGroupContained(*group, 999, renderpass2); @@ -616,30 +645,30 @@ namespace ramses TEST_F(ARenderPass, destroyedRenderGroupIsRemovedFromAllItsPasses) { - RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 3)); - EXPECT_EQ(StatusOK, renderpass2.addRenderGroup(*group, 999)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 3)); + EXPECT_TRUE(renderpass2.addRenderGroup(*group, 999)); - EXPECT_EQ(StatusOK, m_scene.destroy(*group)); + EXPECT_TRUE(m_scene.destroy(*group)); - EXPECT_TRUE(renderpass.m_impl.getAllRenderGroups().empty()); - EXPECT_TRUE(renderpass2.m_impl.getAllRenderGroups().empty()); + EXPECT_TRUE(renderpass.impl().getAllRenderGroups().empty()); + EXPECT_TRUE(renderpass2.impl().getAllRenderGroups().empty()); } TEST_F(ARenderPass, canRemoveAllRenderGroups) { - RenderGroup* group = m_scene.createRenderGroup(); - RenderGroup* group2 = m_scene.createRenderGroup(); - RenderGroup* group3 = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group2 = m_scene.createRenderGroup(); + ramses::RenderGroup* group3 = m_scene.createRenderGroup(); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group, 1)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group2, 2)); - EXPECT_EQ(StatusOK, renderpass.addRenderGroup(*group3, 3)); + EXPECT_TRUE(renderpass.addRenderGroup(*group, 1)); + EXPECT_TRUE(renderpass.addRenderGroup(*group2, 2)); + EXPECT_TRUE(renderpass.addRenderGroup(*group3, 3)); - EXPECT_EQ(StatusOK, renderpass.removeAllRenderGroups()); + EXPECT_TRUE(renderpass.removeAllRenderGroups()); - expectNumGroupsContained(0u, renderpass); + ExpectNumGroupsContained(0u, renderpass); expectGroupNotContained(*group, renderpass); expectGroupNotContained(*group2, renderpass); expectGroupNotContained(*group3, renderpass); @@ -652,20 +681,20 @@ namespace ramses TEST_F(ARenderPass, canBeSetAndUnsetAsRenderOnce) { - EXPECT_EQ(StatusOK, renderpass.setRenderOnce(true)); + EXPECT_TRUE(renderpass.setRenderOnce(true)); EXPECT_TRUE(renderpass.isRenderOnce()); - EXPECT_EQ(StatusOK, renderpass.setRenderOnce(false)); + EXPECT_TRUE(renderpass.setRenderOnce(false)); EXPECT_FALSE(renderpass.isRenderOnce()); } TEST_F(ARenderPass, canRetriggerRenderOnce) { - EXPECT_EQ(StatusOK, renderpass.setRenderOnce(true)); - EXPECT_EQ(StatusOK, renderpass.retriggerRenderOnce()); + EXPECT_TRUE(renderpass.setRenderOnce(true)); + EXPECT_TRUE(renderpass.retriggerRenderOnce()); } TEST_F(ARenderPass, failsToRetriggerRenderOnceIfNotRenderOnce) { - EXPECT_NE(StatusOK, renderpass.retriggerRenderOnce()); + EXPECT_FALSE(renderpass.retriggerRenderOnce()); } } diff --git a/tests/unittests/client/RenderTargetDescriptionTest.cpp b/tests/unittests/client/RenderTargetDescriptionTest.cpp new file mode 100644 index 000000000..0f599365d --- /dev/null +++ b/tests/unittests/client/RenderTargetDescriptionTest.cpp @@ -0,0 +1,239 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTargetDescription.h" +#include "impl/RenderBufferImpl.h" +#include "impl/RenderTargetDescriptionImpl.h" +#include "ClientTestUtils.h" + +using namespace testing; + +namespace ramses::internal +{ + class RenderTargetDescriptionTest : public LocalTestClientWithScene, public testing::Test + { + public: + protected: + RenderTargetDescription rtDesc; + std::string errorMsg; + }; + + TEST_F(RenderTargetDescriptionTest, initialState) + { + EXPECT_TRUE(rtDesc.impl().getRenderBuffers().empty()); + } + + TEST_F(RenderTargetDescriptionTest, validatesAsErrorIfEmpty) + { + ValidationReport report; + rtDesc.validate(report); + EXPECT_TRUE(report.hasError()); + } + + TEST_F(RenderTargetDescriptionTest, canAddRenderBuffer) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + + ASSERT_EQ(1u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + + ValidationReport report; + rtDesc.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(RenderTargetDescriptionTest, canAddMultipleColorRenderBuffers) + { + const ramses::RenderBuffer& colorRb1 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer& colorRb2 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb1)); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb2)); + + ASSERT_EQ(2u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb1.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + EXPECT_EQ(colorRb2.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[1]); + + ValidationReport report; + rtDesc.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(RenderTargetDescriptionTest, canAddMultipleColorRenderBuffersWithDepthBuffer) + { + const ramses::RenderBuffer& colorRb1 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer& colorRb2 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer& depthRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::Depth32, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb1)); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb2)); + EXPECT_TRUE(rtDesc.addRenderBuffer(depthRb)); + + ASSERT_EQ(3u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb1.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + EXPECT_EQ(colorRb2.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[1]); + EXPECT_EQ(depthRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[2]); + + ValidationReport report; + rtDesc.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(RenderTargetDescriptionTest, clearsErrorMessageIfSucceeded) + { + errorMsg = "foo"; + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + } + + TEST_F(RenderTargetDescriptionTest, failsToAddSameRenderBufferTwice) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + EXPECT_FALSE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_EQ(errorMsg, "RenderTargetDescription::addRenderBuffer failed: trying to add a render buffer that is already contained!"); + + ASSERT_EQ(1u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + } + + TEST_F(RenderTargetDescriptionTest, failsToAddRenderBufferFromAnotherScene) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + + ramses::Scene& otherScene = *client.createScene(sceneId_t(666u)); + otherScene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); // create another dummy RB so the internal handle is not 1 + const ramses::RenderBuffer& otherRb = *otherScene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_FALSE(rtDesc.addRenderBuffer(otherRb, &errorMsg)); + EXPECT_EQ(errorMsg, "RenderTargetDescription::addRenderBuffer failed: all render buffers must be from the same scene!"); + + ASSERT_EQ(1u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + } + + TEST_F(RenderTargetDescriptionTest, failsToAddRenderBufferWithDifferentResolution) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + + const ramses::RenderBuffer& otherRb = *m_scene.createRenderBuffer(1u, 2u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_FALSE(rtDesc.addRenderBuffer(otherRb, &errorMsg)); + EXPECT_EQ(errorMsg, "RenderTargetDescription::addRenderBuffer failed: all render buffers must have the same resolution!"); + + ASSERT_EQ(1u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + } + + TEST_F(RenderTargetDescriptionTest, failsToAddMoreThanOneDepthBuffer) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer& depthRb1 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::Depth32, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer& depthRb2 = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::Depth24_Stencil8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + EXPECT_TRUE(rtDesc.addRenderBuffer(depthRb1, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + EXPECT_FALSE(rtDesc.addRenderBuffer(depthRb2, &errorMsg)); + EXPECT_EQ(errorMsg, "RenderTargetDescription::addRenderBuffer failed: cannot add more than one depth/stencil buffer!"); + + ASSERT_EQ(2u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + EXPECT_EQ(depthRb1.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[1]); + } + + TEST_F(RenderTargetDescriptionTest, failsToValidateAfterAddedRenderBufferDestroyed) + { + ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb)); + ValidationReport report; + rtDesc.validate(report); + EXPECT_FALSE(report.hasIssue()); + + m_scene.destroy(colorRb); + rtDesc.validate(report); + EXPECT_TRUE(report.hasError()); + } + + TEST_F(RenderTargetDescriptionTest, canAddRenderBuffersWithMsaaSampleCountNotZero) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 4u); + const ramses::RenderBuffer& depthRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::Depth16, ERenderBufferAccessMode::WriteOnly, 4u); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb)); + EXPECT_TRUE(rtDesc.addRenderBuffer(depthRb)); + + ASSERT_EQ(2u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + EXPECT_EQ(depthRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[1]); + } + + TEST_F(RenderTargetDescriptionTest, canNotAddRenderBuffersWithDifferentMsaaSampleCount) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 3u); + const ramses::RenderBuffer& depthRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 4u); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb, &errorMsg)); + EXPECT_TRUE(errorMsg.empty()); + EXPECT_FALSE(rtDesc.addRenderBuffer(depthRb, &errorMsg)); + EXPECT_EQ(errorMsg, "RenderTargetDescription::addRenderBuffer failed: all render buffers must have same MSAA sample count!"); + + ASSERT_EQ(1u, rtDesc.impl().getRenderBuffers().size()); + EXPECT_EQ(colorRb.impl().getRenderBufferHandle(), rtDesc.impl().getRenderBuffers()[0]); + } + + TEST_F(RenderTargetDescriptionTest, CanBeCopyAndMoveConstructed) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb)); + + RenderTargetDescription rtDescCopy{ rtDesc }; + EXPECT_THAT(rtDescCopy.impl().getRenderBuffers(), ElementsAre(colorRb.impl().getRenderBufferHandle())); + + RenderTargetDescription rtDescMove{ std::move(rtDesc) }; + EXPECT_THAT(rtDescMove.impl().getRenderBuffers(), ElementsAre(colorRb.impl().getRenderBufferHandle())); + } + + TEST_F(RenderTargetDescriptionTest, CanBeCopyAndMoveAssigned) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb)); + + RenderTargetDescription rtDescCopy; + rtDescCopy = rtDesc; + EXPECT_THAT(rtDescCopy.impl().getRenderBuffers(), ElementsAre(colorRb.impl().getRenderBufferHandle())); + + RenderTargetDescription rtDescMove; + rtDescMove = std::move(rtDesc); + EXPECT_THAT(rtDescMove.impl().getRenderBuffers(), ElementsAre(colorRb.impl().getRenderBufferHandle())); + } + + TEST_F(RenderTargetDescriptionTest, CanBeSelfAssigned) + { + const ramses::RenderBuffer& colorRb = *m_scene.createRenderBuffer(1u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + EXPECT_TRUE(rtDesc.addRenderBuffer(colorRb)); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + rtDesc = rtDesc; + EXPECT_THAT(rtDesc.impl().getRenderBuffers(), ElementsAre(colorRb.impl().getRenderBufferHandle())); + rtDesc = std::move(rtDesc); + // NOLINTNEXTLINE(bugprone-use-after-move) + EXPECT_THAT(rtDesc.impl().getRenderBuffers(), ElementsAre(colorRb.impl().getRenderBufferHandle())); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } +} diff --git a/client/test/RenderTargetTest.cpp b/tests/unittests/client/RenderTargetTest.cpp similarity index 64% rename from client/test/RenderTargetTest.cpp rename to tests/unittests/client/RenderTargetTest.cpp index 0a613755e..c032d830d 100644 --- a/client/test/RenderTargetTest.cpp +++ b/tests/unittests/client/RenderTargetTest.cpp @@ -8,16 +8,16 @@ #include -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "RenderTargetImpl.h" -#include "RenderBufferImpl.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/RenderTargetDescription.h" +#include "impl/RenderTargetImpl.h" +#include "impl/RenderBufferImpl.h" #include "ClientTestUtils.h" using namespace testing; -namespace ramses +namespace ramses::internal { class ARenderTarget : public LocalTestClientWithScene, public testing::Test { @@ -25,26 +25,26 @@ namespace ramses TEST_F(ARenderTarget, canBeCreated) { - const RenderBuffer& rb = *m_scene.createRenderBuffer(16u, 8u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer& rb = *m_scene.createRenderBuffer(16u, 8u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(rb); - const RenderTarget* renderTarget = m_scene.createRenderTarget(rtDesc, "RenderTarget"); + const ramses::RenderTarget* renderTarget = m_scene.createRenderTarget(rtDesc, "ramses::RenderTarget"); ASSERT_TRUE(renderTarget != nullptr); EXPECT_EQ(16u, renderTarget->getWidth()); EXPECT_EQ(8u, renderTarget->getHeight()); - const ramses_internal::RenderTargetHandle rtHandle = renderTarget->m_impl.getRenderTargetHandle(); + const RenderTargetHandle rtHandle = renderTarget->impl().getRenderTargetHandle(); EXPECT_TRUE(m_internalScene.isRenderTargetAllocated(rtHandle)); ASSERT_EQ(1u, m_internalScene.getRenderTargetRenderBufferCount(rtHandle)); - EXPECT_EQ(rb.m_impl.getRenderBufferHandle(), m_internalScene.getRenderTargetRenderBuffer(rtHandle, 0u)); + EXPECT_EQ(rb.impl().getRenderBufferHandle(), m_internalScene.getRenderTargetRenderBuffer(rtHandle, 0u)); } TEST_F(ARenderTarget, failsToCreateUsingInvalidRenderTargetDescription) { RenderTargetDescription invalidRtDesc; // no buffers - const RenderTarget* renderTarget = m_scene.createRenderTarget(invalidRtDesc, "RenderTarget"); + const ramses::RenderTarget* renderTarget = m_scene.createRenderTarget(invalidRtDesc, "RenderTarget"); EXPECT_TRUE(renderTarget == nullptr); } } diff --git a/client/test/ResourceTest.cpp b/tests/unittests/client/ResourceTest.cpp similarity index 51% rename from client/test/ResourceTest.cpp rename to tests/unittests/client/ResourceTest.cpp index b585b5613..3afaa242a 100644 --- a/client/test/ResourceTest.cpp +++ b/tests/unittests/client/ResourceTest.cpp @@ -6,32 +6,32 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/TextureSwizzle.h" -#include "ramses-client-api/SceneObjectIterator.h" -#include "ramses-utils.h" - -#include "Texture2DImpl.h" -#include "Texture3DImpl.h" -#include "TextureCubeImpl.h" -#include "ArrayResourceImpl.h" -#include "EffectImpl.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/TextureSwizzle.h" +#include "ramses/client/SceneObjectIterator.h" +#include "ramses/client/ramses-utils.h" + +#include "impl/Texture2DImpl.h" +#include "impl/Texture3DImpl.h" +#include "impl/TextureCubeImpl.h" +#include "impl/ArrayResourceImpl.h" +#include "impl/EffectImpl.h" #include "RamsesObjectTestTypes.h" #include "ClientTestUtils.h" -#include "Resource/IResource.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "Components/ManagedResource.h" -#include "RamsesClientImpl.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/Components/ManagedResource.h" +#include "impl/RamsesClientImpl.h" #include "UnsafeTestMemoryHelpers.h" #include #include -namespace ramses +namespace ramses::internal { using namespace testing; @@ -40,17 +40,17 @@ namespace ramses public: AResourceTestClient() { - m_oldLogLevel = ramses_internal::CONTEXT_HLAPI_CLIENT.getLogLevel(); - ramses_internal::CONTEXT_HLAPI_CLIENT.setLogLevel(ramses_internal::ELogLevel::Trace); + m_oldLogLevel = ramses::internal::CONTEXT_HLAPI_CLIENT.getLogLevel(); + ramses::internal::CONTEXT_HLAPI_CLIENT.setLogLevel(ELogLevel::Trace); } ~AResourceTestClient() override { - ramses_internal::CONTEXT_HLAPI_CLIENT.setLogLevel(m_oldLogLevel); + ramses::internal::CONTEXT_HLAPI_CLIENT.setLogLevel(m_oldLogLevel); } - ramses_internal::ManagedResource getCreatedResource(const ramses_internal::ResourceContentHash& hash) + ramses::internal::ManagedResource getCreatedResource(const ramses::internal::ResourceContentHash& hash) { - return client.m_impl.getClientApplication().getResource(hash); + return client.impl().getClientApplication().getResource(hash); } - ramses_internal::ELogLevel m_oldLogLevel; + ELogLevel m_oldLogLevel; }; //############################################################## @@ -61,18 +61,18 @@ namespace ramses { const uint8_t data[4 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.destroy(*texture)); + EXPECT_TRUE(m_scene.destroy(*texture)); } TEST_F(AResourceTestClient, createTextureWithMipMaps) { const uint8_t data[2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(2 * 2 * 4, data)); - mipLevelData.push_back(MipLevelData(1 * 1 * 4, data)); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2, 2, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(2 * 2 * 4, data); + mipLevelData.emplace_back(1 * 1 * 4, data); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2, 2, 2, &mipLevelData[0], false, {}, "name"); ASSERT_TRUE(nullptr != texture); } @@ -80,7 +80,7 @@ namespace ramses { const uint8_t data[4 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(10u, texture->getWidth()); EXPECT_EQ(12u, texture->getHeight()); @@ -90,7 +90,7 @@ namespace ramses { const uint8_t data[3 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureFormat::RGB8, texture->getTextureFormat()); } @@ -100,7 +100,7 @@ namespace ramses const uint8_t data[3 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); TextureSwizzle swizzle; - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, swizzle, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureChannelColor::Red, swizzle.channelRed); EXPECT_EQ(ETextureChannelColor::Green, swizzle.channelGreen); @@ -113,7 +113,7 @@ namespace ramses TextureSwizzle swizzle = { ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Red, ETextureChannelColor::Green }; const uint8_t data[3 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, swizzle, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(swizzle.channelRed, texture->getTextureSwizzle().channelRed); EXPECT_EQ(swizzle.channelGreen, texture->getTextureSwizzle().channelGreen); @@ -126,10 +126,10 @@ namespace ramses const uint8_t data[4 * 4 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(2 * 2 * 4, data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(2 * 2 * 4, data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 4u, 4u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, 2, &mipLevelData[0], false, {}, "name"); ASSERT_TRUE(nullptr != texture); } @@ -138,21 +138,22 @@ namespace ramses const uint8_t data[1] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(sizeof(data), data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureWithNullMipMapData) { const uint8_t data[4 * 4 * 4] = {}; + const uint8_t* dataNullPtr{nullptr}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), nullptr)); + mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 4u, 4u, 1, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, 1, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -161,27 +162,28 @@ namespace ramses const uint8_t data[4 * 4 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(0u, data)); + mipLevelData.emplace_back(0u, data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 4u, 4u, 1, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, 1, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureWithNullOrZeroSizeLowerMipMapData) { const uint8_t data[2 * 2 * 4] = {}; + const uint8_t* dataNullPtr{nullptr}; { std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(0u, data)); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(0u, data); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } { std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(sizeof(data), nullptr)); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } } @@ -190,9 +192,9 @@ namespace ramses { const uint8_t data[2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(2 * 2 * 4, data)); - mipLevelData.push_back(MipLevelData(1 * 1 * 2, data)); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(2 * 2 * 4, data); + mipLevelData.emplace_back(1 * 1 * 2, data); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -200,13 +202,13 @@ namespace ramses { const uint8_t data[4 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 0, 0, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 0, 0, 1, &mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureWithNoMipData) { - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1, 1, 1, nullptr, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 1, 1, 1, nullptr, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -214,19 +216,19 @@ namespace ramses { const uint8_t data[4 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, {}); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); - EXPECT_STREQ("", texture->getName()); + EXPECT_TRUE(texture->getName().empty()); } TEST_F(AResourceTestClient, createTextureCheckHashIsValid) { const uint8_t data[4 * 10 * 12] = {}; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ResourceContentHash hash = texture->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -235,54 +237,60 @@ namespace ramses uint8_t data[4 * 10 * 12] = {}; data[20] = 48; MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ResourceContentHash hash = texture->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); uint8_t data2[4 * 10 * 12] = {}; data[20] = 42; MipLevelData mipLevelData2(sizeof(data2), data2); - Texture2D* texture2 = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData2, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData2, false, {}, "name"); ASSERT_TRUE(nullptr != texture2); - const ramses_internal::ResourceContentHash hash2 = texture2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = texture2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } + template + std::array make_byte_array(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + TEST_F(AResourceTestClient, createTextureRGBA_AndCheckTexels) { - const uint8_t data[1 * 2 * 4] = { 1, 2, 3, 4, 5, 6, 7, 8 }; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2, 1, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, {}); + const auto data = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8); + MipLevelData mipLevelData(static_cast(data.size()), data.data()); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2, 1, 1, &mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); - ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data), res->getDecompressedDataSize()); + ASSERT_EQ(data.size(), res->getDecompressedDataSize()); EXPECT_EQ(data, res->getResourceData().span()); } TEST_F(AResourceTestClient, createTextureRGB_AndCheckTexels) { - const uint8_t data[1 * 2 * 3] = { 1, 2, 3, 4, 5, 6 }; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 2, 1, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, {}); + const auto data = make_byte_array(1, 2, 3, 4, 5, 6); + MipLevelData mipLevelData(static_cast(data.size()), data.data()); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 2, 1, 1, &mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); - ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data), res->getDecompressedDataSize()); + ASSERT_EQ(data.size(), res->getDecompressedDataSize()); EXPECT_EQ(data, res->getResourceData().span()); } TEST_F(AResourceTestClient, createTextureRGBWithMips_AndCheckTexels) { - const uint8_t data0[2 * 2 * 3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - const uint8_t data1[1 * 1 * 3] = { 13, 14, 15 }; - const MipLevelData mipLevelData[2] = { { sizeof(data0), data0 },{ sizeof(data1), data1 } }; - const Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 2, 2, 2, mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, {}); + const auto data0 = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + const auto data1 = make_byte_array(13, 14, 15); + const MipLevelData mipLevelData[2] = { { static_cast(data0.size()), data0.data() },{ static_cast(data1.size()), data1.data() } }; + const Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 2, 2, 2, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + const ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); ASSERT_EQ(sizeof(data0) + sizeof(data1), res->getDecompressedDataSize()); EXPECT_EQ(data0, res->getResourceData().span().subspan(0, sizeof(data0))); @@ -295,37 +303,37 @@ namespace ramses TEST_F(AResourceTestClient, createCubeTextureAndDestroyManually) { - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, &mipLevelData, false); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.destroy(*texture)); + EXPECT_TRUE(m_scene.destroy(*texture)); } TEST_F(AResourceTestClient, createCubeTextureWithMipMaps) { - const uint8_t data[2 * 2 * 4] = {}; + const std::byte data[2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(2 * 2 * 4, data, data, data, data, data, data)); - mipLevelData.push_back(CubeMipLevelData(1 * 1 * 4, data, data, data, data, data, data)); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 2, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(2 * 2 * 4, data, data, data, data, data, data); + mipLevelData.emplace_back(1 * 1 * 4, data, data, data, data, data, data); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2, 2, &mipLevelData[0], false, {}, "name"); ASSERT_TRUE(nullptr != texture); } TEST_F(AResourceTestClient, createCubeTextureAndCheckWidthHeight) { - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(10u, texture->getSize()); } TEST_F(AResourceTestClient, createCubeTextureAndCheckFormat) { - const uint8_t data[3 * 10 * 10] = {}; + const std::byte data[3 * 10 * 10] = {}; CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGB8, 10, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 10, 1, &mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureFormat::RGB8, texture->getTextureFormat()); } @@ -333,9 +341,9 @@ namespace ramses TEST_F(AResourceTestClient, createsCubeTextureWithDefaultSwizzle) { TextureSwizzle swizzle; - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; const CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, swizzle, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureChannelColor::Red, swizzle.channelRed); EXPECT_EQ(ETextureChannelColor::Green, swizzle.channelGreen); @@ -346,9 +354,9 @@ namespace ramses TEST_F(AResourceTestClient, createsCubeTextureWithNonDefaultSwizzle) { TextureSwizzle swizzle = { ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Red, ETextureChannelColor::Green }; - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; const CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, swizzle, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(swizzle.channelRed, texture->getTextureSwizzle().channelRed); EXPECT_EQ(swizzle.channelGreen, texture->getTextureSwizzle().channelGreen); @@ -358,193 +366,195 @@ namespace ramses TEST_F(AResourceTestClient, createCubeTextureOfZeroSize) { - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 0, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 0, 1, &mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createCubeTextureOfZeroDataSize) { - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; CubeMipLevelData mipLevelData(0, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createCubeTextureWithNoMipData) { - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, nullptr, false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, nullptr, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createCubeTextureWithoutName) { - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, {}); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); - EXPECT_STREQ("", texture->getName()); + EXPECT_TRUE(texture->getName().empty()); } TEST_F(AResourceTestClient, createCubeTextureRGBA_AndCheckTexels) { - const uint8_t data[2 * 2 * 4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 2, 1, &mipLevelData, false); + const auto dataArray = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + const auto* data = dataArray.data(); + CubeMipLevelData mipLevelData(static_cast(dataArray.size()), data, data, data, data, data, data); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2, 1, &mipLevelData, false); ASSERT_TRUE(nullptr != texture); - ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data) * 6u, res->getDecompressedDataSize()); + ASSERT_EQ(dataArray.size() * 6u, res->getDecompressedDataSize()); for (uint32_t i = 0u; i < 6u; ++i) - EXPECT_EQ(data, res->getResourceData().span().subspan(i*sizeof(data), sizeof(data))); + EXPECT_EQ(dataArray, res->getResourceData().span().subspan(i*dataArray.size(), dataArray.size())); } TEST_F(AResourceTestClient, createCubeTextureRGB_AndCheckTexels) { - const uint8_t data[2 * 2 * 3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGB8, 2, 1, &mipLevelData, false); + const auto dataArray = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + const auto* data = dataArray.data(); + CubeMipLevelData mipLevelData(static_cast(dataArray.size()), data, data, data, data, data, data); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 2, 1, &mipLevelData, false); ASSERT_TRUE(nullptr != texture); - ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data) * 6u, res->getDecompressedDataSize()); + ASSERT_EQ(dataArray.size() * 6u, res->getDecompressedDataSize()); for (uint32_t i = 0u; i < 6u; ++i) - EXPECT_EQ(data, res->getResourceData().span().subspan(i*sizeof(data), sizeof(data))); + EXPECT_EQ(dataArray, res->getResourceData().span().subspan(i*dataArray.size(), dataArray.size())); } TEST_F(AResourceTestClient, createCubeTextureRGBWithPerFaceDataAndMips_AndCheckTexels) { - const uint8_t data0px[2 * 2 * 3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - const uint8_t data1px[1 * 1 * 3] = { 13, 14, 15 }; - const uint8_t data0nx[2 * 2 * 3] = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 }; - const uint8_t data1nx[1 * 1 * 3] = { 130, 140, 150 }; - const uint8_t data0py[2 * 2 * 3] = { 11, 21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121 }; - const uint8_t data1py[1 * 1 * 3] = { 131, 141, 151 }; - const uint8_t data0ny[2 * 2 * 3] = { 12, 22, 32, 42, 52, 62, 72, 82, 92, 102, 112, 122 }; - const uint8_t data1ny[1 * 1 * 3] = { 132, 142, 152 }; - const uint8_t data0pz[2 * 2 * 3] = { 13, 23, 33, 43, 53, 63, 73, 83, 93, 103, 113, 123 }; - const uint8_t data1pz[1 * 1 * 3] = { 133, 143, 153 }; - const uint8_t data0nz[2 * 2 * 3] = { 14, 24, 34, 44, 54, 64, 74, 84, 94, 104, 114, 124 }; - const uint8_t data1nz[1 * 1 * 3] = { 134, 144, 154 }; + const auto data0px = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + const auto data1px = make_byte_array(13, 14, 15); + const auto data0nx = make_byte_array(10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120); + const auto data1nx = make_byte_array(130, 140, 150); + const auto data0py = make_byte_array(11, 21, 31, 41, 51, 61, 71, 81, 91, 101, 111, 121); + const auto data1py = make_byte_array(131, 141, 151); + const auto data0ny = make_byte_array(12, 22, 32, 42, 52, 62, 72, 82, 92, 102, 112, 122); + const auto data1ny = make_byte_array(132, 142, 152); + const auto data0pz = make_byte_array(13, 23, 33, 43, 53, 63, 73, 83, 93, 103, 113, 123 ); + const auto data1pz = make_byte_array(133, 143, 153); + const auto data0nz = make_byte_array(14, 24, 34, 44, 54, 64, 74, 84, 94, 104, 114, 124 ); + const auto data1nz = make_byte_array(134, 144, 154); const CubeMipLevelData mipLevelData[2] = { - { sizeof(data0px), data0px, data0nx, data0py, data0ny, data0pz, data0nz }, - { sizeof(data1px), data1px, data1nx, data1py, data1ny, data1pz, data1nz } + { static_cast(data0px.size()), data0px.data(), data0nx.data(), data0py.data(), data0ny.data(), data0pz.data(), data0nz.data() }, + { static_cast(data1px.size()), data1px.data(), data1nx.data(), data1py.data(), data1ny.data(), data1pz.data(), data1nz.data() } }; - const TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGB8, 2u, 2, mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, {}); + const TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 2u, 2, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + const ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(6u * (sizeof(data0px) + sizeof(data1px)), res->getDecompressedDataSize()); + ASSERT_EQ(6u * (data0px.size() + data1px.size()), res->getDecompressedDataSize()); // mips are bundled together per face: // - facePX mip0, facePX mip1 .. face PX mipN, face NX mips, face PY .. face NZ size_t dataStart = 0; - EXPECT_EQ(data0px, res->getResourceData().span().subspan(dataStart, sizeof(data0px))); - dataStart += sizeof(data0px); - EXPECT_EQ(data1px, res->getResourceData().span().subspan(dataStart, sizeof(data1px))); - dataStart += sizeof(data1px); + EXPECT_EQ(data0px, res->getResourceData().span().subspan(dataStart, data0px.size())); + dataStart += data0px.size(); + EXPECT_EQ(data1px, res->getResourceData().span().subspan(dataStart, data1px.size())); + dataStart += data1px.size(); - EXPECT_EQ(data0nx, res->getResourceData().span().subspan(dataStart, sizeof(data0nx))); - dataStart += sizeof(data0nx); - EXPECT_EQ(data1nx, res->getResourceData().span().subspan(dataStart, sizeof(data1nx))); - dataStart += sizeof(data1nx); + EXPECT_EQ(data0nx, res->getResourceData().span().subspan(dataStart, data0nx.size())); + dataStart += data0nx.size(); + EXPECT_EQ(data1nx, res->getResourceData().span().subspan(dataStart, data1nx.size())); + dataStart += data1nx.size(); - EXPECT_EQ(data0py, res->getResourceData().span().subspan(dataStart, sizeof(data0py))); - dataStart += sizeof(data0py); - EXPECT_EQ(data1py, res->getResourceData().span().subspan(dataStart, sizeof(data1py))); - dataStart += sizeof(data1py); + EXPECT_EQ(data0py, res->getResourceData().span().subspan(dataStart, data0py.size())); + dataStart += data0py.size(); + EXPECT_EQ(data1py, res->getResourceData().span().subspan(dataStart, data1py.size())); + dataStart += data1py.size(); - EXPECT_EQ(data0ny, res->getResourceData().span().subspan(dataStart, sizeof(data0ny))); - dataStart += sizeof(data0ny); - EXPECT_EQ(data1ny, res->getResourceData().span().subspan(dataStart, sizeof(data1ny))); - dataStart += sizeof(data1ny); + EXPECT_EQ(data0ny, res->getResourceData().span().subspan(dataStart, data0ny.size())); + dataStart += data0ny.size(); + EXPECT_EQ(data1ny, res->getResourceData().span().subspan(dataStart, data1ny.size())); + dataStart += data1ny.size(); - EXPECT_EQ(data0pz, res->getResourceData().span().subspan(dataStart, sizeof(data0pz))); - dataStart += sizeof(data0pz); - EXPECT_EQ(data1pz, res->getResourceData().span().subspan(dataStart, sizeof(data1pz))); - dataStart += sizeof(data1pz); + EXPECT_EQ(data0pz, res->getResourceData().span().subspan(dataStart, data0pz.size())); + dataStart += data0pz.size(); + EXPECT_EQ(data1pz, res->getResourceData().span().subspan(dataStart, data1pz.size())); + dataStart += data1pz.size(); - EXPECT_EQ(data0nz, res->getResourceData().span().subspan(dataStart, sizeof(data0nz))); - dataStart += sizeof(data0nz); - EXPECT_EQ(data1nz, res->getResourceData().span().subspan(dataStart, sizeof(data1nz))); + EXPECT_EQ(data0nz, res->getResourceData().span().subspan(dataStart, data0nz.size())); + dataStart += data0nz.size(); + EXPECT_EQ(data1nz, res->getResourceData().span().subspan(dataStart, data1nz.size())); } TEST_F(AResourceTestClient, createTextureCubeWithProvidedMipsButNotFullChain) { - const uint8_t data[4 * 4 * 4] = {}; + const std::byte data[4 * 4 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, data, data, data, data)); - mipLevelData.push_back(CubeMipLevelData(2 * 2 * 4, data, data, data, data, data, data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); + mipLevelData.emplace_back(2 * 2 * 4, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 4u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_NE(nullptr, texture); } TEST_F(AResourceTestClient, createTextureCubeWithMoreMipsThanExpected) { - const uint8_t data[1] = {}; + const std::byte data[1] = {}; std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, data, data, data, data)); - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, data, data, data, data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 1u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 1u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureCubeWithNullMipMapData) { - const uint8_t data[4 * 4 * 4] = {}; + const std::byte data[4 * 4 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, nullptr, data, data, data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, nullptr, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 4u, 1, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, 1, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureCubeWithZeroSizeMipMapData) { - const uint8_t data[4 * 4 * 4] = {}; + const std::byte data[4 * 4 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(0u, data, data, data, data, data, data)); + mipLevelData.emplace_back(0u, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 4u, 1, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, 1, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureCubeWithNullOrZeroSizeLowerMipMapData) { - const uint8_t data[2 * 2 * 4] = {}; + const std::byte data[2 * 2 * 4] = {}; { std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, data, data, data, data)); - mipLevelData.push_back(CubeMipLevelData(0u, data, data, data, data, data, data)); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); + mipLevelData.emplace_back(0u, data, data, data, data, data, data); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } { std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, data, data, data, data)); - mipLevelData.push_back(CubeMipLevelData(sizeof(data), data, data, data, data, nullptr, data)); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); + mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, nullptr, data); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } } TEST_F(AResourceTestClient, createTextureCubeWithWrongSizeOfLowerMipMapData) { - const uint8_t data[2 * 2 * 4] = {}; + const std::byte data[2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(CubeMipLevelData(2 * 2 * 4, data, data, data, data, data, data)); - mipLevelData.push_back(CubeMipLevelData(1 * 1 * 2, data, data, data, data, data, data)); - TextureCube* texture = m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(2 * 2 * 4, data, data, data, data, data, data); + mipLevelData.emplace_back(1 * 1 * 2, data, data, data, data, data, data); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -555,27 +565,27 @@ namespace ramses TEST_F(AResourceTestClient, create3DTextureAndDestroyManually) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + MipLevelData mipLevelData(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.destroy(*texture)); + EXPECT_TRUE(m_scene.destroy(*texture)); } TEST_F(AResourceTestClient, create3DTextureWithMipMaps) { const uint8_t data[2 * 2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(2 * 2 * 2 * 4, data)); - mipLevelData.push_back(MipLevelData(1 * 1 * 1 * 4, data)); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(2 * 2 * 2 * 4, data); + mipLevelData.emplace_back(1 * 1 * 1 * 4, data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); ASSERT_TRUE(nullptr != texture); } TEST_F(AResourceTestClient, create3DTextureAndCheckWidthHeightDepth) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + MipLevelData mipLevelData(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(10u, texture->getWidth()); EXPECT_EQ(12u, texture->getHeight()); @@ -585,8 +595,8 @@ namespace ramses TEST_F(AResourceTestClient, create3DTextureAndCheckFormat) { const uint8_t data[3 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGB8, 10, 12, 14, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + MipLevelData mipLevelData(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 10, 12, 14, 1, &mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureFormat::RGB8, texture->getTextureFormat()); } @@ -594,34 +604,34 @@ namespace ramses TEST_F(AResourceTestClient, create3DTextureOfZeroSize) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 0, 0, 0, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + MipLevelData mipLevelData(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 0, 0, 0, 1, &mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, create3DTextureWithNoMipData) { - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 1, 1, 1, 1, nullptr, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 1, 1, 1, 1, nullptr, false, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, create3DTextureWithoutName) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, {}); + MipLevelData mipLevelData(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); - EXPECT_STREQ("", texture->getName()); + EXPECT_TRUE(texture->getName().empty()); } TEST_F(AResourceTestClient, create3DTextureCheckHashIsValid) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + MipLevelData mipLevelData(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ResourceContentHash hash = texture->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -629,60 +639,60 @@ namespace ramses { uint8_t data[4 * 10 * 12 * 14] = {}; data[20] = 48; - MipLevelData mipLevelData1(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData1, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + MipLevelData mipLevelData1(static_cast(sizeof(data)), data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData1, false, "name"); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ResourceContentHash hash = texture->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); uint8_t data2[4 * 10 * 12 * 14] = {}; data[20] = 42; MipLevelData mipLevelData2(sizeof(data2), data2); - Texture3D* texture2 = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData2, false, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture3D* texture2 = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData2, false, "name"); ASSERT_TRUE(nullptr != texture2); - const ramses_internal::ResourceContentHash hash2 = texture2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = texture2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } TEST_F(AResourceTestClient, create3DTextureRGBA_AndCheckTexels) { - const uint8_t data[1 * 2 * 2 * 4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2, 1, 2, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, {}); + const auto data = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + MipLevelData mipLevelData(static_cast(data.size()), data.data()); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, 1, &mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); - ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data), res->getDecompressedDataSize()); + ASSERT_EQ(data.size(), res->getDecompressedDataSize()); EXPECT_EQ(data, res->getResourceData().span()); } TEST_F(AResourceTestClient, create3DTextureRGB_AndCheckTexels) { - const uint8_t data[1 * 2 * 2 * 3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGB8, 2, 1, 2, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, {}); + const auto data = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); + MipLevelData mipLevelData(static_cast(data.size()), data.data()); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 2, 1, 2, 1, &mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); - ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data), res->getDecompressedDataSize()); + ASSERT_EQ(data.size(), res->getDecompressedDataSize()); EXPECT_EQ(data, res->getResourceData().span()); } TEST_F(AResourceTestClient, create3DTextureRGBWithMips_AndCheckTexels) { - const uint8_t data0[2 * 2 * 2 * 3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 }; - const uint8_t data1[1 * 1 * 3] = { 13, 14, 15 }; - const MipLevelData mipLevelData[2] = { { sizeof(data0), data0 },{ sizeof(data1), data1 } }; - const Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGB8, 2u, 2u, 2u, 2, mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, {}); + const auto data0 = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120); + const auto data1 = make_byte_array(13, 14, 15); + const MipLevelData mipLevelData[2] = { { static_cast(data0.size()), data0.data() },{ static_cast(data1.size()), data1.data() } }; + const Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 2u, 2u, 2u, 2, mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); - const ramses_internal::ManagedResource res = getCreatedResource(texture->m_impl.getLowlevelResourceHash()); + const ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); - ASSERT_EQ(sizeof(data0) + sizeof(data1), res->getDecompressedDataSize()); - EXPECT_EQ(data0, res->getResourceData().span().subspan(0, sizeof(data0))); - EXPECT_EQ(data1, res->getResourceData().span().subspan(sizeof(data0))); + ASSERT_EQ(data0.size() + data1.size(), res->getDecompressedDataSize()); + EXPECT_EQ(data0, res->getResourceData().span().subspan(0, data0.size())); + EXPECT_EQ(data1, res->getResourceData().span().subspan(data0.size())); } TEST_F(AResourceTestClient, createTexture3DWithProvidedMipsButNotFullChain) @@ -690,10 +700,10 @@ namespace ramses const uint8_t data[4 * 4 * 4 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(2 * 2 * 2 * 4, data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(2 * 2 * 2 * 4, data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 4u, 4u, 4u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 4u, 4u, 4u, 2, &mipLevelData[0], false, "name"); EXPECT_NE(nullptr, texture); } @@ -702,21 +712,22 @@ namespace ramses const uint8_t data[1] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(sizeof(data), data)); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 1u, 1u, 1u, 2, &mipLevelData[0], false, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, create3DTextureWithNullMipMapData) { const uint8_t data[2 * 2 * 2 * 4] = {}; + const uint8_t* dataNullPtr{nullptr}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), nullptr)); + mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2u, 1, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 1, &mipLevelData[0], false, "name"); EXPECT_EQ(nullptr, texture); } @@ -725,27 +736,28 @@ namespace ramses const uint8_t data[2 * 2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(0, data)); + mipLevelData.emplace_back(0, data); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2u, 1, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 1, &mipLevelData[0], false, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, create3DTextureWithNullOrZeroSizeLowerMipMapData) { const uint8_t data[2 * 2 * 2 * 4] = {}; + const uint8_t* dataNullPtr{nullptr}; { std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(0u, data)); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(0u, data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); EXPECT_EQ(nullptr, texture); } { std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(sizeof(data), data)); - mipLevelData.push_back(MipLevelData(sizeof(data), nullptr)); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(static_cast(sizeof(data)), data); + mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); EXPECT_EQ(nullptr, texture); } } @@ -754,9 +766,9 @@ namespace ramses { const uint8_t data[2 * 2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(2 * 2 * 2 * 4, data)); - mipLevelData.push_back(MipLevelData(1 * 1 * 1 * 2, data)); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(2 * 2 * 2 * 4, data); + mipLevelData.emplace_back(1 * 1 * 1 * 2, data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); EXPECT_EQ(nullptr, texture); } @@ -764,9 +776,9 @@ namespace ramses { const uint8_t data[2 * 2 * 2 * 4] = {}; std::vector mipLevelData; - mipLevelData.push_back(MipLevelData(1, data)); - mipLevelData.push_back(MipLevelData(1, data)); - Texture3D* texture = m_scene.createTexture3D(ramses::ETextureFormat::ASTC_RGBA_4x4, 2u, 2u, 2u, 2, &mipLevelData[0], false, ramses::ResourceCacheFlag_DoNotCache, "name"); + mipLevelData.emplace_back(1, data); + mipLevelData.emplace_back(1, data); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::ASTC_RGBA_4x4, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); EXPECT_TRUE(nullptr != texture); } @@ -780,7 +792,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); auto b = m_scene.createArrayResource(2, data); - ASSERT_EQ(a->m_impl.getLowlevelResourceHash(), b->m_impl.getLowlevelResourceHash()); + ASSERT_EQ(a->impl().getLowlevelResourceHash(), b->impl().getLowlevelResourceHash()); } TEST_F(AResourceTestClient, creatingResourcesWithSameDataInternallySharesData) @@ -788,8 +800,8 @@ namespace ramses const vec2f data[2] = { vec2f{1.f,2.f}, vec2f{3.f,4.f} }; auto a = m_scene.createArrayResource(2, data); auto b = m_scene.createArrayResource(2, data); - ramses_internal::ManagedResource resourceA = client.m_impl.getResource(a->m_impl.getLowlevelResourceHash()); - ramses_internal::ManagedResource resourceB = client.m_impl.getResource(b->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource resourceA = client.impl().getResource(a->impl().getLowlevelResourceHash()); + ramses::internal::ManagedResource resourceB = client.impl().getResource(b->impl().getLowlevelResourceHash()); ASSERT_EQ(resourceA, resourceB); } @@ -799,8 +811,8 @@ namespace ramses const vec2f data2[2] = { vec2f{1.f,2.f}, vec2f{3.f,4.f} }; auto a = m_scene.createArrayResource(2, data1); auto b = m_scene.createArrayResource(2, data2); - ramses_internal::ManagedResource resourceA = client.m_impl.getResource(a->m_impl.getLowlevelResourceHash()); - ramses_internal::ManagedResource resourceB = client.m_impl.getResource(b->m_impl.getLowlevelResourceHash()); + ramses::internal::ManagedResource resourceA = client.impl().getResource(a->impl().getLowlevelResourceHash()); + ramses::internal::ManagedResource resourceB = client.impl().getResource(b->impl().getLowlevelResourceHash()); ASSERT_EQ(resourceA, resourceB); } @@ -812,8 +824,8 @@ namespace ramses auto b = m_scene.createArrayResource(2, data2); m_scene.destroy(*b); - ramses_internal::ManagedResource aRes = client.m_impl.getResource(a->m_impl.getLowlevelResourceHash()); - EXPECT_TRUE(ramses_internal::UnsafeTestMemoryHelpers::CompareMemoryBlobToSpan(&data1, sizeof(data1), aRes->getResourceData().span())); + ramses::internal::ManagedResource aRes = client.impl().getResource(a->impl().getLowlevelResourceHash()); + EXPECT_TRUE(ramses::internal::UnsafeTestMemoryHelpers::CompareMemoryBlobToSpan(&data1, sizeof(data1), aRes->getResourceData().span())); } TEST_F(AResourceTestClient, createFloatArray) @@ -822,7 +834,7 @@ namespace ramses const auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::Float); + EXPECT_EQ(a->getDataType(), ramses::EDataType::Float); } TEST_F(AResourceTestClient, createFloatArrayHashIsValid) @@ -831,7 +843,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -841,13 +853,13 @@ namespace ramses data[0] = 4; auto a = m_scene.createArrayResource(2, data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); float data2[2] = {}; data2[0] = 42.0f; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } @@ -863,7 +875,7 @@ namespace ramses const auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::Vector2F); + EXPECT_EQ(a->getDataType(), ramses::EDataType::Vector2F); } TEST_F(AResourceTestClient, createVector2fArrayHashIsValid) @@ -872,7 +884,7 @@ namespace ramses const auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -882,13 +894,13 @@ namespace ramses data[0][0] = 4; auto a = m_scene.createArrayResource(2, data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); vec2f data2[2] = { vec2f{1.f,2.f}, vec2f{3.f,4.f} }; data2[0][0] = 42.0f; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } @@ -904,7 +916,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::Vector3F); + EXPECT_EQ(a->getDataType(), ramses::EDataType::Vector3F); } TEST_F(AResourceTestClient, createVector3fArrayHashIsValid) @@ -913,7 +925,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -923,13 +935,13 @@ namespace ramses data[0][0] = 4.0f; auto a = m_scene.createArrayResource(2, data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); vec3f data2[2] = { vec3f{1.f,2.f,3.f}, vec3f{3.f,4.f,5.f} }; data2[0][0] = 44.0f; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } @@ -945,7 +957,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::Vector4F); + EXPECT_EQ(a->getDataType(), ramses::EDataType::Vector4F); } TEST_F(AResourceTestClient, createVector4fArrayHashIsValid) @@ -954,7 +966,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -964,13 +976,13 @@ namespace ramses data[0][0] = 4.0f; auto a = m_scene.createArrayResource(2, data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); vec4f data2[2] = { vec4f{1.f,2.f,3.f,4.f}, vec4f{3.f,4.f,5.f,6.f} }; data2[0][0] = 42.0f; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } @@ -986,7 +998,7 @@ namespace ramses const ArrayResource *a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::UInt16); + EXPECT_EQ(a->getDataType(), ramses::EDataType::UInt16); } TEST_F(AResourceTestClient, createUInt16ArrayResourceHashIsValid) @@ -995,7 +1007,7 @@ namespace ramses const ArrayResource* a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -1005,13 +1017,13 @@ namespace ramses data[0] = 4; auto a = m_scene.createArrayResource(2, data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); uint16_t data2[2] = {}; data2[0] = 42; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } @@ -1027,7 +1039,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::UInt32); + EXPECT_EQ(a->getDataType(), ramses::EDataType::UInt32); } TEST_F(AResourceTestClient, createUInt32ArrayHashIsValid) @@ -1036,7 +1048,7 @@ namespace ramses auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } @@ -1046,13 +1058,13 @@ namespace ramses data[0] = 4; auto a = m_scene.createArrayResource(2, data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); uint32_t data2[2] = {}; data2[0] = 42; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } @@ -1065,80 +1077,80 @@ namespace ramses TEST_F(AResourceTestClient, createByteBlobArray) { - const ramses::Byte data[2] = {}; + const std::byte data[2] = {}; const auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); EXPECT_EQ(a->getNumberOfElements(), 2u); - EXPECT_EQ(a->getDataType(), EDataType::ByteBlob); + EXPECT_EQ(a->getDataType(), ramses::EDataType::ByteBlob); } TEST_F(AResourceTestClient, createByteBlobArrayHashIsValid) { - const ramses::Byte data[2] = {}; + const std::byte data[2] = {}; const auto a = m_scene.createArrayResource(2, data); ASSERT_TRUE(nullptr != a); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash.isValid()); } TEST_F(AResourceTestClient, createByteBlobArrayHashIsUnique) { - ramses::Byte data[2] = {}; - data[0] = 4; + std::byte data[2] = {}; + data[0] = std::byte{4}; auto a = m_scene.createArrayResource(sizeof(data), data); - const ramses_internal::ResourceContentHash hash = a->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash = a->impl().getLowlevelResourceHash(); - ramses::Byte data2[2] = {}; - data2[0] = 5; + std::byte data2[2] = {}; + data2[0] = std::byte{5}; auto a2 = m_scene.createArrayResource(2, data2); - const ramses_internal::ResourceContentHash hash2 = a2->m_impl.getLowlevelResourceHash(); + const ramses::internal::ResourceContentHash hash2 = a2->impl().getLowlevelResourceHash(); EXPECT_TRUE(hash != hash2); } TEST_F(AResourceTestClient, createEmptyByteBlobArrayFails) { - auto a = m_scene.createArrayResource(0, nullptr); + auto a = m_scene.createArrayResource(0, nullptr); EXPECT_TRUE(nullptr == a); } TEST_F(AResourceTestClient, createAndDestroyEffect) { EffectDescription effectDesc; - EXPECT_EQ(StatusOK, effectDesc.setVertexShaderFromFile("res/ramses-client-test_shader.vert")); - EXPECT_EQ(StatusOK, effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag")); + EXPECT_TRUE(effectDesc.setVertexShaderFromFile("res/ramses-client-test_shader.vert")); + EXPECT_TRUE(effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag")); - auto effect = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "effect"); + auto effect = m_scene.createEffect(effectDesc, "effect"); ASSERT_TRUE(effect != nullptr); } TEST_F(AResourceTestClient, effectCreatedTwiceWithSameHashCanBeCreatedAndDestroyed) { EffectDescription effectDesc; - EXPECT_EQ(StatusOK, effectDesc.setVertexShaderFromFile("res/ramses-client-test_shader.vert")); - EXPECT_EQ(StatusOK, effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag")); + EXPECT_TRUE(effectDesc.setVertexShaderFromFile("res/ramses-client-test_shader.vert")); + EXPECT_TRUE(effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag")); - auto effect1 = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "effect1"); + auto effect1 = m_scene.createEffect(effectDesc, "effect1"); ASSERT_TRUE(effect1 != nullptr); - auto effect2 = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "effect2"); + auto effect2 = m_scene.createEffect(effectDesc, "effect2"); ASSERT_TRUE(effect2 != nullptr); - EXPECT_EQ(StatusOK, m_scene.destroy(*effect1)); - EXPECT_EQ(StatusOK, m_scene.destroy(*effect2)); + EXPECT_TRUE(m_scene.destroy(*effect1)); + EXPECT_TRUE(m_scene.destroy(*effect2)); } TEST_F(AResourceTestClient, effectCreatedTwiceWithDifferentNameProducesSameHash) { EffectDescription effectDesc; - EXPECT_EQ(StatusOK, effectDesc.setVertexShaderFromFile("res/ramses-client-test_shader.vert")); - EXPECT_EQ(StatusOK, effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag")); - auto effect1 = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "effect1"); + EXPECT_TRUE(effectDesc.setVertexShaderFromFile("res/ramses-client-test_shader.vert")); + EXPECT_TRUE(effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag")); + auto effect1 = m_scene.createEffect(effectDesc, "effect1"); ASSERT_TRUE(effect1 != nullptr); - auto effect2 = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "effect2"); + auto effect2 = m_scene.createEffect(effectDesc, "effect2"); ASSERT_TRUE(effect2 != nullptr); - EXPECT_EQ(effect1->m_impl.getLowlevelResourceHash(), effect2->m_impl.getLowlevelResourceHash()); + EXPECT_EQ(effect1->impl().getLowlevelResourceHash(), effect2->impl().getLowlevelResourceHash()); m_scene.destroy(*effect1); m_scene.destroy(*effect2); } @@ -1158,24 +1170,24 @@ namespace ramses TYPED_TEST(ResourceTest, canBeConvertedToResource) { RamsesObject& obj = this->createResource("resource"); - EXPECT_TRUE(RamsesUtils::TryConvert(obj) != nullptr); + EXPECT_TRUE(obj.as() != nullptr); const RamsesObject& constObj = obj; - EXPECT_TRUE(RamsesUtils::TryConvert(constObj) != nullptr); + EXPECT_TRUE(constObj.as() != nullptr); } TYPED_TEST(ResourceTest, sameResourcesWithDifferentNamesShareSameHash) { const std::string name_A("name_A"); const std::string name_B("name_B"); - Resource* resource_A = static_cast(&this->createResource(name_A)); - Resource* resource_B = static_cast(&this->createResource(name_B)); + auto* resource_A = static_cast(&this->createResource(name_A)); + auto* resource_B = static_cast(&this->createResource(name_B)); - RamsesObject* foundObject_A = this->getScene().findObjectByName(name_A); - RamsesObject* foundObject_B = this->getScene().findObjectByName(name_B); + RamsesObject* foundObject_A = this->getScene().findObject(name_A); + RamsesObject* foundObject_B = this->getScene().findObject(name_B); ASSERT_TRUE(resource_A); ASSERT_TRUE(resource_B); - EXPECT_EQ(resource_A->m_impl.getLowlevelResourceHash(), resource_B->m_impl.getLowlevelResourceHash()); + EXPECT_EQ(resource_A->impl().getLowlevelResourceHash(), resource_B->impl().getLowlevelResourceHash()); ASSERT_TRUE(foundObject_A); ASSERT_TRUE(foundObject_B); @@ -1186,40 +1198,40 @@ namespace ramses TYPED_TEST(ResourceTest, statisticCounterIsUpdated) { - EXPECT_EQ(0u, this->getFramework().m_impl.getStatisticCollection().statResourcesCreated.getCounterValue()); - EXPECT_EQ(0u, this->getFramework().m_impl.getStatisticCollection().statResourcesDestroyed.getCounterValue()); - EXPECT_EQ(0u, this->getFramework().m_impl.getStatisticCollection().statResourcesNumber.getCounterValue()); + EXPECT_EQ(0u, this->getFramework().impl().getStatisticCollection().statResourcesCreated.getCounterValue()); + EXPECT_EQ(0u, this->getFramework().impl().getStatisticCollection().statResourcesDestroyed.getCounterValue()); + EXPECT_EQ(0u, this->getFramework().impl().getStatisticCollection().statResourcesNumber.getCounterValue()); RamsesObject& obj = this->createResource("resource"); - Resource* res = RamsesUtils::TryConvert(obj); - EXPECT_EQ(1u, this->getFramework().m_impl.getStatisticCollection().statResourcesCreated.getCounterValue()); + auto* res = obj.as(); + EXPECT_EQ(1u, this->getFramework().impl().getStatisticCollection().statResourcesCreated.getCounterValue()); - this->getFramework().m_impl.getStatisticCollection().nextTimeInterval(); //statResourcesNumber is updated by nextTimeInterval() - EXPECT_EQ(1u, this->getFramework().m_impl.getStatisticCollection().statResourcesNumber.getCounterValue()); + this->getFramework().impl().getStatisticCollection().nextTimeInterval(); //statResourcesNumber is updated by nextTimeInterval() + EXPECT_EQ(1u, this->getFramework().impl().getStatisticCollection().statResourcesNumber.getCounterValue()); this->getScene().destroy(*res); - EXPECT_EQ(1u, this->getFramework().m_impl.getStatisticCollection().statResourcesDestroyed.getCounterValue()); + EXPECT_EQ(1u, this->getFramework().impl().getStatisticCollection().statResourcesDestroyed.getCounterValue()); - this->getFramework().m_impl.getStatisticCollection().nextTimeInterval(); - EXPECT_EQ(0u, this->getFramework().m_impl.getStatisticCollection().statResourcesNumber.getCounterValue()); + this->getFramework().impl().getStatisticCollection().nextTimeInterval(); + EXPECT_EQ(0u, this->getFramework().impl().getStatisticCollection().statResourcesNumber.getCounterValue()); } TYPED_TEST(ResourceTest, canCreateResourceSetNameAndAfterwardsDestroyResourceCleanly) { TypeParam& obj = this->createResource("resource"); obj.setName("otherName"); - EXPECT_EQ(StatusOK, this->m_scene.destroy(obj)); + EXPECT_TRUE(this->m_scene.destroy(obj)); SceneObjectIterator iterator(this->m_scene, ERamsesObjectType::Resource); EXPECT_EQ(iterator.getNext(), nullptr); - EXPECT_EQ(StatusOK, this->m_scene.saveToFile("someFileName.ramses", false)); + EXPECT_TRUE(this->m_scene.saveToFile("someFileName.ramses", {})); } TYPED_TEST(ResourceTest, canCreateSameResourceTwiceAndDeleteBothAgain) { TypeParam& obj1 = this->createResource("resource"); TypeParam& obj2 = this->createResource("resource"); - EXPECT_EQ(StatusOK, this->m_scene.destroy(obj1)); - EXPECT_EQ(StatusOK, this->m_scene.destroy(obj2)); + EXPECT_TRUE(this->m_scene.destroy(obj1)); + EXPECT_TRUE(this->m_scene.destroy(obj2)); } TYPED_TEST(ResourceTest, canFindResourceByItsNewIdAfterRenaming) @@ -1229,7 +1241,7 @@ namespace ramses EXPECT_EQ(this->m_scene.getResource(id), &obj); obj.setName("otherName"); EXPECT_EQ(this->m_scene.getResource(id), nullptr); - EXPECT_EQ(this->m_scene.findObjectByName("otherName"), &obj); + EXPECT_EQ(this->m_scene.findObject("otherName"), &obj); EXPECT_EQ(this->m_scene.getResource(obj.getResourceId()), &obj); } @@ -1238,10 +1250,10 @@ namespace ramses const auto id = this->createResource("resource").getResourceId(); this->createResource("resource"); const auto obj1 = this->m_scene.getResource(id); - EXPECT_EQ(StatusOK, this->m_scene.destroy(*obj1)); + EXPECT_TRUE(this->m_scene.destroy(*obj1)); const auto obj2 = this->m_scene.getResource(id); EXPECT_NE(obj1, obj2); - EXPECT_EQ(StatusOK, this->m_scene.destroy(*obj2)); + EXPECT_TRUE(this->m_scene.destroy(*obj2)); EXPECT_EQ(this->m_scene.getResource(id), nullptr); } } diff --git a/client/test/ClientCommands/SceneCommandBufferTest.cpp b/tests/unittests/client/SceneCommandBufferTest.cpp similarity index 92% rename from client/test/ClientCommands/SceneCommandBufferTest.cpp rename to tests/unittests/client/SceneCommandBufferTest.cpp index ec76d7056..bfb708e98 100644 --- a/client/test/ClientCommands/SceneCommandBufferTest.cpp +++ b/tests/unittests/client/SceneCommandBufferTest.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ClientCommands/SceneCommandBuffer.h" -#include "Utils/ThreadBarrier.h" +#include "internal/ClientCommands/SceneCommandBuffer.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "gmock/gmock.h" #include -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -53,7 +53,7 @@ namespace ramses_internal static bool operator==(const SceneCommandValidationRequest& a, const SceneCommandValidationRequest& b) { - return a.severity == b.severity && a.optionalObjectName == b.optionalObjectName; + return a.verbosity == b.verbosity && a.optionalObjectName == b.optionalObjectName; } static bool operator==(const SceneCommandDumpSceneToFile& a, const SceneCommandDumpSceneToFile& b) @@ -61,7 +61,7 @@ namespace ramses_internal return a.fileName == b.fileName && a.sendViaDLT == b.sendViaDLT; } - static bool operator==(const SceneCommandLogResourceMemoryUsage&, const SceneCommandLogResourceMemoryUsage&) + static bool operator==(const SceneCommandLogResourceMemoryUsage& /*unused*/, const SceneCommandLogResourceMemoryUsage& /*unused*/) { return true; } @@ -83,7 +83,7 @@ namespace ramses_internal EXPECT_CALL(visitor, handleSceneCommandFlushSceneVersion(cmd)); } { - SceneCommandValidationRequest cmd{ramses::EValidationSeverity::Error, "bar"}; + SceneCommandValidationRequest cmd{EIssueType::Error, "bar"}; buffer.enqueueCommand(cmd); EXPECT_CALL(visitor, handleSceneCommandValidationRequest(cmd)); } @@ -123,7 +123,7 @@ namespace ramses_internal std::thread t1([&]() { setupDoneBarrier.wait(); - buffer.enqueueCommand(SceneCommandValidationRequest{ramses::EValidationSeverity::Error, "bar"}); + buffer.enqueueCommand(SceneCommandValidationRequest{EIssueType::Error, "bar"}); buffer.enqueueCommand(SceneCommandFlushSceneVersion{12345u}); writersDoneBarrier.wait(); allDone.wait(); @@ -138,7 +138,7 @@ namespace ramses_internal std::thread t3([&]() { Sequence seqT1; Sequence seqT2; - EXPECT_CALL(visitor, handleSceneCommandValidationRequest(SceneCommandValidationRequest{ramses::EValidationSeverity::Error, "bar"})).InSequence(seqT1); + EXPECT_CALL(visitor, handleSceneCommandValidationRequest(SceneCommandValidationRequest{EIssueType::Error, "bar"})).InSequence(seqT1); EXPECT_CALL(visitor, handleSceneCommandFlushSceneVersion(SceneCommandFlushSceneVersion{12345u})).InSequence(seqT1); EXPECT_CALL(visitor, handleSceneCommandDumpSceneToFile(SceneCommandDumpSceneToFile{"somename", false})).InSequence(seqT2); EXPECT_CALL(visitor, handleSceneCommandLogResourceMemoryUsage(SceneCommandLogResourceMemoryUsage{})).InSequence(seqT2); diff --git a/tests/unittests/client/SceneCommandsTest.cpp b/tests/unittests/client/SceneCommandsTest.cpp new file mode 100644 index 000000000..932bee2d0 --- /dev/null +++ b/tests/unittests/client/SceneCommandsTest.cpp @@ -0,0 +1,82 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ClientTestUtils.h" +#include "internal/Core/Utils/File.h" + +#include + +namespace ramses::internal +{ + using ramses::internal::File; + + class DumpSceneToFileForTests : public LocalTestClientWithScene, public ::testing::Test + { + protected: + + void SetUp() override + { + auto file = getSceneDumpFile(); + if (file.exists()) + { + ASSERT_TRUE(file.remove()); + } + } + + File getSceneDumpFile() const + { + return File(fmt::format("{}{}.ramses", testing::TempDir(), m_scene.getSceneId()).c_str()); + } + }; + + TEST_F(DumpSceneToFileForTests, badParameters) + { + EXPECT_FALSE(getFramework().executeRamshCommand("dumpSceneToFile")); + EXPECT_FALSE(getFramework().executeRamshCommand(fmt::format("dumpSceneToFile {}", m_scene.getSceneId()))); + EXPECT_FALSE(getFramework().executeRamshCommand(fmt::format("dumpSceneToFile notAScene filename", m_scene.getSceneId()))); + } + + TEST_F(DumpSceneToFileForTests, dumpSceneWithNextFlush) + { + const auto filename = fmt::format("{}{}", testing::TempDir(), m_scene.getSceneId()); // .ramses is added internally + EXPECT_TRUE(getFramework().executeRamshCommand(fmt::format("dumpSceneToFile {} {}", m_scene.getSceneId(), filename))); + EXPECT_FALSE(getSceneDumpFile().exists()); + m_scene.flush(); + EXPECT_TRUE(getSceneDumpFile().exists()); + } + + TEST_F(DumpSceneToFileForTests, dumpSceneAndFlush) + { + const auto filename = fmt::format("{}{}", testing::TempDir(), m_scene.getSceneId()); // .ramses is added internally + EXPECT_FALSE(getSceneDumpFile().exists()); + EXPECT_TRUE(getFramework().executeRamshCommand(fmt::format("dumpSceneToFile -flush {} {}", m_scene.getSceneId(), filename))); + EXPECT_TRUE(getSceneDumpFile().exists()); + } + + TEST_F(DumpSceneToFileForTests, dumpSceneAndFlushInvalidScene) + { + const auto filename = fmt::format("{}{}", testing::TempDir(), m_scene.getSceneId()); // .ramses is added internally + EXPECT_FALSE(getSceneDumpFile().exists()); + EXPECT_FALSE(getFramework().executeRamshCommand(fmt::format("dumpSceneToFile -flush {} {}", m_scene.getSceneId().getValue() + 1, filename))); + EXPECT_FALSE(getSceneDumpFile().exists()); + } + + TEST_F(DumpSceneToFileForTests, dumpSceneToDltNoFile) + { + EXPECT_TRUE(getFramework().executeRamshCommand(fmt::format("dumpScene -sendViaDLT -flush {}", m_scene.getSceneId()))); + EXPECT_FALSE(getSceneDumpFile().exists()); + } + + TEST_F(DumpSceneToFileForTests, dumpSceneToDltStoreFile) + { + EXPECT_FALSE(getSceneDumpFile().exists()); + const auto filename = fmt::format("{}{}", testing::TempDir(), m_scene.getSceneId()); // .ramses is added internally + EXPECT_TRUE(getFramework().executeRamshCommand(fmt::format("dumpScene -sendViaDLT -flush {} {}", m_scene.getSceneId(), filename))); + EXPECT_TRUE(getSceneDumpFile().exists()); + } +} diff --git a/client/test/SceneDistributionTest.cpp b/tests/unittests/client/SceneDistributionTest.cpp similarity index 60% rename from client/test/SceneDistributionTest.cpp rename to tests/unittests/client/SceneDistributionTest.cpp index 15c35fac0..9540d265e 100644 --- a/client/test/SceneDistributionTest.cpp +++ b/tests/unittests/client/SceneDistributionTest.cpp @@ -8,39 +8,35 @@ #include "gtest/gtest.h" #include "ClientTestUtils.h" -#include "ramses-client-api/Node.h" -#include "SceneAPI/IScene.h" -#include "Scene/SceneActionCollection.h" +#include "ramses/client/Node.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" -namespace ramses +namespace ramses::internal { using namespace testing; - class ADistributedScene : public LocalTestClientWithScene, public ::testing::Test + class ADistributedScene : public LocalTestClientWithScene, public ::testing::TestWithParam { public: - ADistributedScene() + ADistributedScene() : LocalTestClientWithScene(GetParam()) { framework.connect(); } - ~ADistributedScene() override - { - } - void publishScene() { - const ramses_internal::IScene& iscene = m_scene.m_impl.getIScene(); - ramses_internal::SceneInfo info(iscene.getSceneId(), iscene.getName()); + const ramses::internal::IScene& iscene = m_scene.impl().getIScene(); + ramses::internal::SceneInfo info(iscene.getSceneId(), iscene.getName()); EXPECT_CALL(sceneActionsCollector, handleNewSceneAvailable(info, _)); EXPECT_CALL(sceneActionsCollector, handleInitializeScene(info, _)); - EXPECT_EQ(StatusOK, m_scene.publish()); + EXPECT_TRUE(m_scene.publish(GetParam())); } void unpublishScene() { - EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(ramses_internal::SceneId(m_scene.m_impl.getSceneId().getValue()), _)); - EXPECT_EQ(StatusOK, m_scene.unpublish()); + EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(ramses::internal::SceneId(m_scene.impl().getSceneId().getValue()), _)); + EXPECT_TRUE(m_scene.unpublish()); } void doSceneOperations() @@ -51,7 +47,7 @@ namespace ramses nodeTrans->setParent(*node); } - void expectActionListsEqual(const ramses_internal::SceneActionCollection& list1, const ramses_internal::SceneActionCollection& list2) const + static void ExpectActionListsEqual(const ramses::internal::SceneActionCollection& list1, const ramses::internal::SceneActionCollection& list2) { EXPECT_EQ(list1.numberOfActions(), list2.numberOfActions()); for (uint32_t i = 0u; i < list1.numberOfActions(); ++i) @@ -62,58 +58,67 @@ namespace ramses void expectSceneOperationsSent() { - EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(m_scene.m_impl.getSceneId().getValue()), _, _)); + EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(m_scene.impl().getSceneId().getValue()), _, _)); } void expectSceneUnpublication() { - EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(ramses_internal::SceneId(m_scene.m_impl.getSceneId().getValue()), _)); + EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(ramses::internal::SceneId(m_scene.impl().getSceneId().getValue()), _)); } protected: }; - TEST_F(ADistributedScene, flushProducesSingleActionList) + INSTANTIATE_TEST_SUITE_P( + ADistributedScene_Suite, + ADistributedScene, + ::testing::Values(EScenePublicationMode::LocalOnly, EScenePublicationMode::LocalAndRemote)); + + TEST_P(ADistributedScene, flushProducesSingleActionList) { publishScene(); expectSceneOperationsSent(); - EXPECT_EQ(StatusOK, m_scene.flush()); + EXPECT_TRUE(m_scene.flush()); sceneActionsCollector.resetCollecting(); doSceneOperations(); expectSceneOperationsSent(); - EXPECT_EQ(StatusOK, m_scene.flush()); - ramses_internal::SceneActionCollection actionsBeforeFirstFlush(sceneActionsCollector.getCopyOfCollectedActions()); + EXPECT_TRUE(m_scene.flush()); + ramses::internal::SceneActionCollection actionsBeforeFirstFlush(sceneActionsCollector.getCopyOfCollectedActions()); EXPECT_EQ(1u, sceneActionsCollector.getNumReceivedActionLists()); sceneActionsCollector.resetCollecting(); doSceneOperations(); expectSceneOperationsSent(); - EXPECT_EQ(StatusOK, m_scene.flush()); - ramses_internal::SceneActionCollection actionsBeforeSecondFlush(sceneActionsCollector.getCopyOfCollectedActions()); + EXPECT_TRUE(m_scene.flush()); + ramses::internal::SceneActionCollection actionsBeforeSecondFlush(sceneActionsCollector.getCopyOfCollectedActions()); EXPECT_EQ(1u, sceneActionsCollector.getNumReceivedActionLists()); sceneActionsCollector.resetCollecting(); - expectActionListsEqual(actionsBeforeFirstFlush, actionsBeforeSecondFlush); + ExpectActionListsEqual(actionsBeforeFirstFlush, actionsBeforeSecondFlush); expectSceneUnpublication(); } - TEST_F(ADistributedScene, publishCausesSceneToBeSentToImplicitSubscriberInLocalCase) + TEST_P(ADistributedScene, publishCausesSceneToBeSentToImplicitSubscriber) { expectSceneOperationsSent(); m_scene.flush(); sceneActionsCollector.resetCollecting(); doSceneOperations(); publishScene(); + + if (GetParam() == EScenePublicationMode::LocalOnly) // in local only case scene has to be flush to send anything + m_scene.flush(); + EXPECT_EQ(1u, sceneActionsCollector.getNumReceivedActionLists()); expectSceneUnpublication(); } - TEST_F(ADistributedScene, unpublishingSceneResetsPendingActions) + TEST_P(ADistributedScene, unpublishingSceneResetsPendingActions) { publishScene(); expectSceneOperationsSent(); - EXPECT_EQ(StatusOK, m_scene.flush()); + EXPECT_TRUE(m_scene.flush()); sceneActionsCollector.resetCollecting(); doSceneOperations(); @@ -123,27 +128,27 @@ namespace ramses sceneActionsCollector.resetCollecting(); // no new messages/calls, everything has been already flushed - EXPECT_EQ(StatusOK, m_scene.flush()); + EXPECT_TRUE(m_scene.flush()); EXPECT_EQ(0u, sceneActionsCollector.getNumReceivedActionLists()); EXPECT_EQ(0u, sceneActionsCollector.getNumberOfActions()); } - TEST_F(ADistributedScene, destroyingSceneCausesUnpublish) + TEST_P(ADistributedScene, destroyingSceneCausesUnpublish) { - const ramses_internal::SceneId sceneId(33u); - Scene* otherScene = client.createScene(ramses::sceneId_t(sceneId.getValue())); + const ramses::internal::SceneId sceneId(33u); + ramses::Scene* otherScene = client.createScene(sceneId_t(sceneId.getValue())); ASSERT_TRUE(otherScene != nullptr); - const ramses_internal::IScene& otherIScene = otherScene->m_impl.getIScene(); + const ramses::internal::IScene& otherIScene = otherScene->impl().getIScene(); - ramses_internal::SceneInfo sceneInfo(sceneId, otherIScene.getName()); + ramses::internal::SceneInfo sceneInfo(sceneId, otherIScene.getName()); EXPECT_CALL(sceneActionsCollector, handleNewSceneAvailable(sceneInfo, _)); - EXPECT_EQ(StatusOK, otherScene->publish()); + EXPECT_TRUE(otherScene->publish()); EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(sceneId, _)); client.destroy(*otherScene); } - TEST_F(ADistributedScene, confidence_emptyFlushDoesNotCollectAnySceneActions) + TEST_P(ADistributedScene, confidence_emptyFlushDoesNotCollectAnySceneActions) { // First do immediate mode, no commits publishScene(); diff --git a/tests/unittests/client/SceneFactoryTest.cpp b/tests/unittests/client/SceneFactoryTest.cpp new file mode 100644 index 000000000..b94a1f426 --- /dev/null +++ b/tests/unittests/client/SceneFactoryTest.cpp @@ -0,0 +1,51 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "gmock/gmock.h" +#include "impl/SceneFactory.h" +#include "internal/SceneGraph/Scene/ClientScene.h" + +namespace ramses::internal +{ + using namespace testing; + + class ASceneFactory : public Test + { + protected: + SceneFactory factory; + }; + + TEST_F(ASceneFactory, createsAndDeletesScene) + { + IScene* scene = factory.createScene(SceneInfo()); + ASSERT_TRUE(nullptr != scene); + auto sceneOwnPtr = factory.releaseScene(scene->getSceneId()); + EXPECT_EQ(scene, sceneOwnPtr.get()); + } + + TEST_F(ASceneFactory, cannotCreateTwoScenesWithTheSameId) + { + IScene* scene = factory.createScene(SceneInfo()); + ASSERT_TRUE(nullptr != scene); + EXPECT_TRUE(nullptr == factory.createScene(SceneInfo(scene->getSceneId()))); + } + + TEST_F(ASceneFactory, createsSceneWithProvidedOptions) + { + const SceneSizeInformation sizeInfo(1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u, 16u, 17u, 18u); + const SceneId sceneId(456u); + const SceneInfo sceneInfo(sceneId, "sceneName"); + auto* scene = static_cast(factory.createScene(sceneInfo)); + scene->preallocateSceneSize(sizeInfo); + ASSERT_TRUE(scene != nullptr); + EXPECT_EQ(std::string("sceneName"), scene->getName()); + EXPECT_EQ(sizeInfo, scene->getSceneSizeInformation()); + EXPECT_EQ(sceneId, scene->getSceneId()); + } +} diff --git a/client/test/SceneGraphIteratorTest.cpp b/tests/unittests/client/SceneGraphIteratorTest.cpp similarity index 86% rename from client/test/SceneGraphIteratorTest.cpp rename to tests/unittests/client/SceneGraphIteratorTest.cpp index af9ab555a..6c8f5170c 100644 --- a/client/test/SceneGraphIteratorTest.cpp +++ b/tests/unittests/client/SceneGraphIteratorTest.cpp @@ -6,21 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/SceneGraphIterator.h" +#include "ramses/client/SceneGraphIterator.h" #include "SimpleSceneTopology.h" -#include "NodeImpl.h" -#include "MeshNodeImpl.h" -#include "Collections/Vector.h" +#include "impl/NodeImpl.h" +#include "impl/MeshNodeImpl.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" using namespace testing; -namespace ramses +namespace ramses::internal { class SceneGraphIteratorTest : public SimpleSceneTopology { protected: - AssertionResult checkOrder(SceneGraphIterator& iterator, const std::vector& expectedOrder) const + static AssertionResult CheckOrder(SceneGraphIterator& iterator, const std::vector& expectedOrder) { for (const auto expectedNode : expectedOrder) { @@ -54,7 +54,7 @@ namespace ramses expectedOrder.push_back(&m_mesh2a); expectedOrder.push_back(&m_mesh2b); - EXPECT_TRUE(checkOrder(iterator, expectedOrder)); + EXPECT_TRUE(CheckOrder(iterator, expectedOrder)); } TEST_F(SceneGraphIteratorTest, traversesSceneGraph_BreadthFirst) @@ -70,7 +70,7 @@ namespace ramses expectedOrder.push_back(&m_mesh2a); expectedOrder.push_back(&m_mesh2b); - EXPECT_TRUE(checkOrder(iterator, expectedOrder)); + EXPECT_TRUE(CheckOrder(iterator, expectedOrder)); } TEST_F(SceneGraphIteratorTest, traverseOnlyOneLeaf_DepthFirst) diff --git a/client/test/SceneIteratorTest.cpp b/tests/unittests/client/SceneIteratorTest.cpp similarity index 94% rename from client/test/SceneIteratorTest.cpp rename to tests/unittests/client/SceneIteratorTest.cpp index 3ed4bd98e..88018ca58 100644 --- a/client/test/SceneIteratorTest.cpp +++ b/tests/unittests/client/SceneIteratorTest.cpp @@ -8,12 +8,12 @@ // API #include -#include "RamsesClientImpl.h" -#include "Utils/File.h" -#include "ramses-client-api/SceneIterator.h" +#include "impl/RamsesClientImpl.h" +#include "internal/Core/Utils/File.h" +#include "ramses/client/SceneIterator.h" #include "ClientTestUtils.h" -namespace ramses +namespace ramses::internal { class ASceneIterator : public LocalTestClientWithScene, public ::testing::Test { diff --git a/client/test/SceneObjectIteratorTest.cpp b/tests/unittests/client/SceneObjectIteratorTest.cpp similarity index 56% rename from client/test/SceneObjectIteratorTest.cpp rename to tests/unittests/client/SceneObjectIteratorTest.cpp index a351c0fad..8d0b8c323 100644 --- a/client/test/SceneObjectIteratorTest.cpp +++ b/tests/unittests/client/SceneObjectIteratorTest.cpp @@ -6,38 +6,40 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/SceneObjectIterator.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/PickableObject.h" -#include "ramses-client-api/SceneReference.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-utils.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/SceneObjectIterator.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/SceneReference.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" #include "ClientTestUtils.h" #include "RamsesObjectTestTypes.h" -#include "RamsesObjectTypeTraits.h" +#include "impl/RamsesObjectTypeTraits.h" using namespace testing; -namespace ramses +namespace ramses::internal { template class SceneObjectIteratorTest : public LocalTestClientWithScene, public testing::Test @@ -47,9 +49,9 @@ namespace ramses { } - ramses::ERamsesObjectType getTypeIDForObjectType() + ERamsesObjectType getTypeIDForObjectType() { - return ramses::TYPE_ID_OF_RAMSES_OBJECT::ID; + return TYPE_ID_OF_RAMSES_OBJECT::ID; } }; @@ -68,9 +70,9 @@ namespace ramses ASSERT_TRUE(nullptr != iteratedObject1); ASSERT_TRUE(nullptr != iteratedObject2); EXPECT_TRUE(ro1 == iteratedObject1); - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(*iteratedObject1)); + EXPECT_TRUE(nullptr != iteratedObject1->as()); EXPECT_TRUE(ro2 == iteratedObject2); - EXPECT_TRUE(nullptr != RamsesUtils::TryConvert(*iteratedObject2)); + EXPECT_TRUE(nullptr != iteratedObject2->as()); EXPECT_TRUE(nullptr == iterator.getNext()); } diff --git a/client/test/RamsesObjectRegistryTest.cpp b/tests/unittests/client/SceneObjectRegistryTest.cpp similarity index 64% rename from client/test/RamsesObjectRegistryTest.cpp rename to tests/unittests/client/SceneObjectRegistryTest.cpp index 3aeb1206f..2902f1e97 100644 --- a/client/test/RamsesObjectRegistryTest.cpp +++ b/tests/unittests/client/SceneObjectRegistryTest.cpp @@ -7,54 +7,54 @@ // ------------------------------------------------------------------------- #include -#include "ramses-client-api/RamsesObject.h" -#include "RamsesObjectRegistry.h" -#include "NodeImpl.h" -#include "ramses-client-api/Node.h" +#include "ramses/framework/RamsesObject.h" +#include "impl/SceneObjectRegistry.h" +#include "impl/NodeImpl.h" +#include "ramses/client/Node.h" #include "ClientTestUtils.h" #include -namespace ramses +namespace ramses::internal { using namespace testing; - class ARamsesObjectRegistry : public testing::Test + class ASceneObjectRegistry : public testing::Test { public: Node* createAndRegisterDummyObject() { - return &m_registry.createAndRegisterObject(std::make_unique(m_dummyScene.getScene().m_impl, ERamsesObjectType::Node, "")); + return &m_registry.createAndRegisterObject(std::make_unique(m_dummyScene.getScene().impl(), ERamsesObjectType::Node, "")); } protected: LocalTestClientWithScene m_dummyScene; - RamsesObjectRegistry m_registry; + SceneObjectRegistry m_registry; }; - TEST_F(ARamsesObjectRegistry, isEmptyUponCreation) + TEST_F(ASceneObjectRegistry, isEmptyUponCreation) { - RamsesObjectVector objects; + SceneObjectVector objects; m_registry.getObjectsOfType(objects, ERamsesObjectType::RamsesObject); EXPECT_TRUE(objects.empty()); EXPECT_EQ(0u, m_registry.getNumberOfObjects(ERamsesObjectType::Node)); } - TEST_F(ARamsesObjectRegistry, canAddObject) + TEST_F(ASceneObjectRegistry, canAddObject) { auto dummyObject = createAndRegisterDummyObject(); dummyObject->setName("name"); - EXPECT_EQ(dummyObject, m_registry.findObjectByName("name")); + EXPECT_EQ(dummyObject, m_registry.findObjectByName("name")); EXPECT_EQ(dummyObject, m_registry.findObjectById(sceneObjectId_t{1u})); - EXPECT_EQ(dummyObject, &dummyObject->m_impl.getRamsesObject()); + EXPECT_EQ(dummyObject, &dummyObject->impl().getRamsesObject()); EXPECT_EQ(1u, m_registry.getNumberOfObjects(ERamsesObjectType::Node)); EXPECT_EQ(0u, m_registry.getNumberOfObjects(ERamsesObjectType::RenderBuffer)); - RamsesObjectVector objects; + SceneObjectVector objects; m_registry.getObjectsOfType(objects, ERamsesObjectType::RamsesObject); ASSERT_EQ(1u, objects.size()); EXPECT_EQ(dummyObject, objects[0]); } - TEST_F(ARamsesObjectRegistry, sceneObjectsGetUniqueIds) + TEST_F(ASceneObjectRegistry, sceneObjectsGetUniqueIds) { const auto dummy1(createAndRegisterDummyObject()); const auto dummy2(createAndRegisterDummyObject()); @@ -74,109 +74,109 @@ namespace ramses EXPECT_EQ(sceneObjectIds.size(), 5u); } - TEST_F(ARamsesObjectRegistry, canRemoveObject) + TEST_F(ASceneObjectRegistry, canRemoveObject) { auto dummyObject = createAndRegisterDummyObject(); dummyObject->setName("name"); m_registry.destroyAndUnregisterObject(*dummyObject); - EXPECT_TRUE(nullptr == m_registry.findObjectByName("name")); + EXPECT_TRUE(nullptr == m_registry.findObjectByName("name")); EXPECT_TRUE(nullptr == m_registry.findObjectById(sceneObjectId_t{1u})); EXPECT_EQ(0u, m_registry.getNumberOfObjects(ERamsesObjectType::Node)); EXPECT_EQ(0u, m_registry.getNumberOfObjects(ERamsesObjectType::RenderBuffer)); - RamsesObjectVector objects; + SceneObjectVector objects; m_registry.getObjectsOfType(objects, ERamsesObjectType::RamsesObject); EXPECT_TRUE(objects.empty()); } - TEST_F(ARamsesObjectRegistry, canAddAndRetrieveObjectInfo) + TEST_F(ASceneObjectRegistry, canAddAndRetrieveObjectInfo) { auto dummyObject = createAndRegisterDummyObject(); dummyObject->setName("name"); - EXPECT_EQ(dummyObject, m_registry.findObjectByName("name")); + EXPECT_EQ(dummyObject, m_registry.findObjectByName("name")); EXPECT_EQ(dummyObject, m_registry.findObjectById(sceneObjectId_t{1u})); } - TEST_F(ARamsesObjectRegistry, canRenameObjectAndFindUnderNewName) + TEST_F(ASceneObjectRegistry, canRenameObjectAndFindUnderNewName) { auto dummyObject = createAndRegisterDummyObject(); dummyObject->setName("name"); - EXPECT_EQ(dummyObject, m_registry.findObjectByName("name")); + EXPECT_EQ(dummyObject, m_registry.findObjectByName("name")); EXPECT_EQ(dummyObject, m_registry.findObjectById(sceneObjectId_t{ 1u })); dummyObject->setName("newName"); - EXPECT_EQ(dummyObject, m_registry.findObjectByName("newName")); + EXPECT_EQ(dummyObject, m_registry.findObjectByName("newName")); EXPECT_EQ(dummyObject, m_registry.findObjectById(sceneObjectId_t{ 1u })); // cannot find under old name - EXPECT_EQ(nullptr, m_registry.findObjectByName("name")); + EXPECT_EQ(nullptr, m_registry.findObjectByName("name")); } - TEST_F(ARamsesObjectRegistry, cannotRetrieveObjectInfoAfterObjectDeleted) + TEST_F(ASceneObjectRegistry, cannotRetrieveObjectInfoAfterObjectDeleted) { auto dummyObject = createAndRegisterDummyObject(); dummyObject->setName("name"); m_registry.destroyAndUnregisterObject(*dummyObject); - EXPECT_TRUE(nullptr == m_registry.findObjectByName("name")); + EXPECT_TRUE(nullptr == m_registry.findObjectByName("name")); EXPECT_TRUE(nullptr == m_registry.findObjectById(sceneObjectId_t{1u})); } - TEST_F(ARamsesObjectRegistry, canRetrieveObjectFromImpl) + TEST_F(ASceneObjectRegistry, canRetrieveObjectFromImpl) { auto dummyObject = createAndRegisterDummyObject(); - EXPECT_EQ(dummyObject, &dummyObject->m_impl.getRamsesObject()); + EXPECT_EQ(dummyObject, &dummyObject->impl().getRamsesObject()); } - TEST_F(ARamsesObjectRegistry, hasNoDirtyNodesAfterInitialization) + TEST_F(ASceneObjectRegistry, hasNoDirtyNodesAfterInitialization) { EXPECT_EQ(0u, m_registry.getDirtyNodes().size()); } - TEST_F(ARamsesObjectRegistry, marksNodeDirty) + TEST_F(ASceneObjectRegistry, marksNodeDirty) { auto dummyObject = createAndRegisterDummyObject(); - m_registry.setNodeDirty(dummyObject->m_impl, true); + m_registry.setNodeDirty(dummyObject->impl(), true); EXPECT_EQ(1u, m_registry.getDirtyNodes().size()); - EXPECT_TRUE(m_registry.getDirtyNodes().contains(&dummyObject->m_impl)); - EXPECT_TRUE(m_registry.isNodeDirty(dummyObject->m_impl)); + EXPECT_TRUE(m_registry.getDirtyNodes().contains(&dummyObject->impl())); + EXPECT_TRUE(m_registry.isNodeDirty(dummyObject->impl())); } - TEST_F(ARamsesObjectRegistry, marksNodeClean) + TEST_F(ASceneObjectRegistry, marksNodeClean) { auto object1(createAndRegisterDummyObject()); - m_registry.setNodeDirty(object1->m_impl, true); + m_registry.setNodeDirty(object1->impl(), true); auto object2(createAndRegisterDummyObject()); - m_registry.setNodeDirty(object2->m_impl, true); + m_registry.setNodeDirty(object2->impl(), true); EXPECT_EQ(2u, m_registry.getDirtyNodes().size()); - m_registry.setNodeDirty(object1->m_impl, false); + m_registry.setNodeDirty(object1->impl(), false); EXPECT_EQ(1u, m_registry.getDirtyNodes().size()); - EXPECT_FALSE(m_registry.getDirtyNodes().contains(&object1->m_impl)); - EXPECT_FALSE(m_registry.isNodeDirty(object1->m_impl)); + EXPECT_FALSE(m_registry.getDirtyNodes().contains(&object1->impl())); + EXPECT_FALSE(m_registry.isNodeDirty(object1->impl())); } - TEST_F(ARamsesObjectRegistry, clearsDirtyNodes) + TEST_F(ASceneObjectRegistry, clearsDirtyNodes) { auto dummyObject = createAndRegisterDummyObject(); - m_registry.setNodeDirty(dummyObject->m_impl, true); + m_registry.setNodeDirty(dummyObject->impl(), true); m_registry.clearDirtyNodes(); EXPECT_EQ(0u, m_registry.getDirtyNodes().size()); - EXPECT_FALSE(m_registry.isNodeDirty(dummyObject->m_impl)); + EXPECT_FALSE(m_registry.isNodeDirty(dummyObject->impl())); } - TEST_F(ARamsesObjectRegistry, marksNodeCleanWhenDeleted) + TEST_F(ASceneObjectRegistry, marksNodeCleanWhenDeleted) { auto dummyObject = createAndRegisterDummyObject(); - m_registry.setNodeDirty(dummyObject->m_impl, true); + m_registry.setNodeDirty(dummyObject->impl(), true); m_registry.destroyAndUnregisterObject(*dummyObject); EXPECT_EQ(0u, m_registry.getDirtyNodes().size()); } - TEST_F(ARamsesObjectRegistry, nodeAppearsInDirtyListAfterMarkedDirty) + TEST_F(ASceneObjectRegistry, nodeAppearsInDirtyListAfterMarkedDirty) { auto dummyObject = createAndRegisterDummyObject(); - m_registry.setNodeDirty(dummyObject->m_impl, true); + m_registry.setNodeDirty(dummyObject->impl(), true); EXPECT_EQ(1u, m_registry.getDirtyNodes().size()); - EXPECT_TRUE(m_registry.getDirtyNodes().contains(&dummyObject->m_impl)); - EXPECT_TRUE(m_registry.isNodeDirty(dummyObject->m_impl)); + EXPECT_TRUE(m_registry.getDirtyNodes().contains(&dummyObject->impl())); + EXPECT_TRUE(m_registry.isNodeDirty(dummyObject->impl())); } } diff --git a/tests/unittests/client/ScenePersistationTest.cpp b/tests/unittests/client/ScenePersistationTest.cpp new file mode 100644 index 000000000..b3b4a2316 --- /dev/null +++ b/tests/unittests/client/ScenePersistationTest.cpp @@ -0,0 +1,1715 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "internal/PlatformAbstraction/PlatformError.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/PickableObject.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/ramses-utils.h" + +#include "ScenePersistationTest.h" +#include "impl/CameraNodeImpl.h" +#include "impl/AppearanceImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/EffectImpl.h" +#include "impl/SceneImpl.h" +#include "impl/Texture2DImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/PickableObjectImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/Texture2DBufferImpl.h" + +#include "internal/Core/Utils/File.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/BlitPass.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" +#include "internal/SceneGraph/SceneAPI/RenderPass.h" +#include "internal/SceneGraph/Scene/ESceneActionId.h" +#include "internal/SceneGraph/Scene/ResourceChanges.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" + +#include "TestEffects.h" +#include "FileDescriptorHelper.h" +#include "UnsafeTestMemoryHelpers.h" +#include "RamsesObjectTestTypes.h" + +#include +#include +#include + +namespace ramses::internal +{ + using namespace testing; + + TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromFile) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + EXPECT_NE(nullptr, m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {})); + } + + TEST_F(ASceneLoadedFromFile, logsExporterMetadata) + { + ramses::SaveFileConfig config; + config.setMetadataString("foo-bar-baz"); + config.setExporterVersion(1, 2, 3, 7); + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", config)); + + std::vector logs; + ramses::RamsesFramework::SetLogHandler([&logs](auto level, auto /*unused*/, auto message) { + if (level == ramses::ELogLevel::Info) + { + logs.push_back(std::string(message)); + } + }); + EXPECT_NE(nullptr, m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {})); + ramses::RamsesFramework::SetLogHandler(nullptr); + EXPECT_THAT(logs, Contains("Metadata: 'foo-bar-baz'")); + EXPECT_THAT(logs, Contains("Exporter version: 1.2.3 (file format version 7)")); + } + + TEST_F(ASceneLoadedFromFile, loadedSceneHasReferenceToClientAndFrameworkAndMatchingFeaturelevel) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + auto loadedScene = m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {}); + ASSERT_NE(nullptr, loadedScene); + EXPECT_EQ(&m_clientForLoading, &loadedScene->getRamsesClient()); + EXPECT_EQ(&m_frameworkForLoader, &loadedScene->getRamsesClient().getRamsesFramework()); + EXPECT_EQ(EFeatureLevel_Latest, loadedScene->getRamsesClient().getRamsesFramework().getFeatureLevel()); + } + + TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromMemoryWithExplicitDeleter) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + + ramses::internal::File f("someTemporaryFile.ram"); + size_t fileSize = 0; + EXPECT_TRUE(f.getSizeInBytes(fileSize)); + + std::unique_ptr data(new std::byte[fileSize], [](const auto* ptr) { delete[] ptr; }); + size_t numBytesRead = 0; + EXPECT_TRUE(f.open(ramses::internal::File::Mode::ReadOnlyBinary)); + EXPECT_EQ(ramses::internal::EStatus::Ok, f.read(data.get(), fileSize, numBytesRead)); + EXPECT_NE(nullptr, m_clientForLoading.loadSceneFromMemory(std::move(data), fileSize, {})); + } + + TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromMemoryWithImplicitDeleter) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + + ramses::internal::File f("someTemporaryFile.ram"); + size_t fileSize = 0; + EXPECT_TRUE(f.getSizeInBytes(fileSize)); + + std::unique_ptr data(new std::byte[fileSize]); + size_t numBytesRead = 0; + EXPECT_TRUE(f.open(ramses::internal::File::Mode::ReadOnlyBinary)); + EXPECT_EQ(ramses::internal::EStatus::Ok, f.read(data.get(), fileSize, numBytesRead)); + EXPECT_NE(nullptr, RamsesUtils::LoadSceneFromMemory(m_clientForLoading, std::move(data), fileSize, {})); + } + + TEST_F(ASceneLoadedFromFile, canWriteAndReadSceneFromFileDescriptor) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + + size_t fileSize = 0; + { + // write to a file with some offset + ramses::internal::File inFile("someTemporaryFile.ram"); + EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); + std::vector data(fileSize); + size_t numBytesRead = 0; + EXPECT_TRUE(inFile.open(ramses::internal::File::Mode::ReadOnlyBinary)); + EXPECT_EQ(ramses::internal::EStatus::Ok, inFile.read(data.data(), fileSize, numBytesRead)); + + ramses::internal::File outFile("someTemporaryFileWithOffset.ram"); + EXPECT_TRUE(outFile.open(ramses::internal::File::Mode::WriteOverWriteOldBinary)); + + uint32_t zeroData = 0; + EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); + EXPECT_TRUE(outFile.write(data.data(), data.size())); + EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); + } + + const int fd = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary ("someTemporaryFileWithOffset.ram"); + auto scene = m_clientForLoading.loadSceneFromFileDescriptor(fd, 4, fileSize, {}); + ASSERT_NE(nullptr, scene); + EXPECT_EQ(123u, scene->getSceneId().getValue()); + } + + TEST_F(ASceneLoadedFromFile, canReadSceneFromFileDescriptorCustomSceneId) + { + const char* filename = "someTemporaryFile.ram"; + EXPECT_TRUE(m_scene.saveToFile(filename, {})); + size_t fileSize = 0; + { + ramses::internal::File inFile(filename); + EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); + } + { + SceneConfig config; + config.setSceneId(sceneId_t(335)); + const int fdA = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary(filename); + auto sceneA = m_clientForLoading.loadSceneFromFileDescriptor(fdA, 0, fileSize, config); + ASSERT_TRUE(sceneA); + EXPECT_EQ(335u, sceneA->getSceneId().getValue()); + } + { + SceneConfig config; + config.setSceneId(sceneId_t(339)); + const int fdB = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary (filename); + auto sceneB = m_clientForLoading.loadSceneFromFileDescriptor(fdB, 0, fileSize, config); + ASSERT_TRUE(sceneB); + EXPECT_EQ(339u, sceneB->getSceneId().getValue()); + } + } + + TEST_F(ASceneLoadedFromFile, errorsReadingSceneFromFileDescriptorCustomSceneId) + { + const char* filename = "someTemporaryFile.ram"; + EXPECT_TRUE(m_scene.saveToFile(filename, {})); + size_t fileSize = 0; + { + ramses::internal::File inFile(filename); + EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); + } + + const int fd = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary(filename); + + static_cast(m_clientForLoading.createScene(SceneConfig(m_scene.getSceneId()))); + SceneConfig config; + config.setSceneId(m_scene.getSceneId()); + const auto duplicateSceneId = m_clientForLoading.loadSceneFromFileDescriptor(fd, 0, fileSize, config); + config.setSceneId(ramses::sceneId_t(340)); + const auto invalidFileDescriptor = m_clientForLoading.loadSceneFromFileDescriptor(0, 0, fileSize, config); + const auto invalidFileSize = m_clientForLoading.loadSceneFromFileDescriptor(fd, 0, 0, config); + + EXPECT_TRUE(duplicateSceneId == nullptr); + EXPECT_TRUE(invalidFileDescriptor == nullptr); + EXPECT_TRUE(invalidFileSize == nullptr); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAScene) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {}); + ASSERT_TRUE(nullptr != m_sceneLoaded); + + ObjectTypeHistogram origSceneNumbers; + ObjectTypeHistogram loadedSceneNumbers; + FillObjectTypeHistogramFromScene(origSceneNumbers, m_scene); + FillObjectTypeHistogramFromScene(loadedSceneNumbers, *m_sceneLoaded); + EXPECT_PRED_FORMAT2(AssertHistogramEqual, origSceneNumbers, loadedSceneNumbers); + + ramses::internal::SceneSizeInformation origSceneSizeInfo = m_scene.impl().getIScene().getSceneSizeInformation(); + ramses::internal::SceneSizeInformation loadedSceneSizeInfo = m_sceneLoaded->impl().getIScene().getSceneSizeInformation(); + EXPECT_EQ(origSceneSizeInfo, loadedSceneSizeInfo); + } + + TEST_F(ASceneLoadedFromFile, validationReportSameBeforeSavingAndAfterLoadingFromFile) + { + TestEffects::CreateTestEffectWithAllStages(this->m_scene, "eff"); + ValidationReport report1; + m_scene.validate(report1); + EXPECT_FALSE(report1.hasIssue()); + + doWriteReadCycle(); + + ValidationReport report2; + m_sceneLoaded->validate(report2); + EXPECT_FALSE(report2.hasIssue()); + EXPECT_EQ(report1.impl().toString(), report2.impl().toString()); + } + + TEST_F(ASceneLoadedFromFile, validationReportWithWarningsSameBeforeSavingAndAfterLoadingFromFile) + { + TestEffects::CreateTestEffectWithAllStagesWithWarnings(this->m_scene, "eff"); + ValidationReport report1; + m_scene.validate(report1); + EXPECT_TRUE(report1.hasIssue()); + + doWriteReadCycle(); + + ValidationReport report2; + m_sceneLoaded->validate(report2); + EXPECT_TRUE(report2.hasIssue()); + EXPECT_EQ(report1.impl().toString(), report2.impl().toString()); + } + + TEST_F(ASceneLoadedFromFile, validationReportWithWarningsSameBeforeSavingAndAfterLoadingFromFileWithCompression) + { + TestEffects::CreateTestEffectWithAllStagesWithWarnings(this->m_scene, "eff"); + ValidationReport report1; + m_scene.validate(report1); + EXPECT_TRUE(report1.hasIssue()); + + doWriteReadCycle(true, true, true); + + ValidationReport report2; + m_sceneLoaded->validate(report2); + EXPECT_TRUE(report2.hasIssue()); + EXPECT_EQ(report1.impl().toString(), report2.impl().toString()); + } + + TEST_F(ASceneLoadedFromFile, loadsLogic) + { + auto* logic = this->m_scene.createLogicEngine("my logic"); + auto* timer = logic->createTimerNode("dummy"); + + doWriteReadCycle(); + + auto* loadedLogic = this->getObjectForTesting("my logic"); + + EXPECT_EQ(logic->getSceneObjectId(), loadedLogic->getSceneObjectId()); + EXPECT_NE(nullptr, loadedLogic->findObject(timer->getSceneObjectId())); + EXPECT_EQ(1u, loadedLogic->getCollection().size()); + + EXPECT_TRUE(m_sceneLoaded->destroy(*loadedLogic)); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAPerspectiveCamera) + { + auto* camera = this->m_scene.createPerspectiveCamera("my cam"); + camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); + camera->setViewport(1, -2, 3, 4); + + doWriteReadCycle(); + + auto* loadedCamera = this->getObjectForTesting("my cam"); + EXPECT_EQ(camera->impl().getCameraHandle(), loadedCamera->impl().getCameraHandle()); + EXPECT_EQ(0.1f, loadedCamera->getLeftPlane()); + EXPECT_EQ(0.2f, loadedCamera->getRightPlane()); + EXPECT_EQ(0.3f, loadedCamera->getBottomPlane()); + EXPECT_EQ(0.4f, loadedCamera->getTopPlane()); + EXPECT_EQ(0.5f, loadedCamera->getNearPlane()); + EXPECT_EQ(0.6f, loadedCamera->getFarPlane()); + + EXPECT_EQ(1, loadedCamera->getViewportX()); + EXPECT_EQ(-2, loadedCamera->getViewportY()); + EXPECT_EQ(3u, loadedCamera->getViewportWidth()); + EXPECT_EQ(4u, loadedCamera->getViewportHeight()); + + ValidationReport report; + loadedCamera->validate(report); + EXPECT_FALSE(report.hasIssue()); + + m_sceneLoaded->destroy(*loadedCamera); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAnOrthographicCamera) + { + OrthographicCamera* camera = this->m_scene.createOrthographicCamera("my cam"); + camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); + camera->setViewport(1, -2, 3, 4); + + doWriteReadCycle(); + + auto* loadedCamera = this->getObjectForTesting("my cam"); + + EXPECT_EQ(camera->impl().getCameraHandle(), loadedCamera->impl().getCameraHandle()); + + EXPECT_FLOAT_EQ(0.1f, loadedCamera->getLeftPlane()); + EXPECT_FLOAT_EQ(0.2f, loadedCamera->getRightPlane()); + EXPECT_FLOAT_EQ(0.3f, loadedCamera->getBottomPlane()); + EXPECT_FLOAT_EQ(0.4f, loadedCamera->getTopPlane()); + EXPECT_FLOAT_EQ(0.5f, loadedCamera->getNearPlane()); + EXPECT_FLOAT_EQ(0.6f, loadedCamera->getFarPlane()); + + EXPECT_EQ(1, loadedCamera->getViewportX()); + EXPECT_EQ(-2, loadedCamera->getViewportY()); + EXPECT_EQ(3u, loadedCamera->getViewportWidth()); + EXPECT_EQ(4u, loadedCamera->getViewportHeight()); + + ValidationReport report; + loadedCamera->validate(report); + EXPECT_FALSE(report.hasIssue()); + + m_sceneLoaded->destroy(*loadedCamera); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAnEffect) + { + TestEffects::CreateTestEffectWithAllStages(this->m_scene, "eff"); + + doWriteReadCycle(); + + auto loadedEffect = this->getObjectForTesting("eff"); + ASSERT_TRUE(loadedEffect); + + // check uniforms + EXPECT_EQ(4u, loadedEffect->getUniformInputCount()); + std::optional optUniform = loadedEffect->getUniformInput(0u); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_STREQ("vs_uniform", optUniform->getName()); + EXPECT_EQ(ramses::EDataType::Float, optUniform->getDataType()); + EXPECT_EQ(1u, optUniform->getElementCount()); + + optUniform = loadedEffect->getUniformInput(1u); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_STREQ("colorRG", optUniform->getName()); + EXPECT_EQ(ramses::EDataType::Vector2F, optUniform->getDataType()); + EXPECT_EQ(1u, optUniform->getElementCount()); + + optUniform = loadedEffect->getUniformInput(2u); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_STREQ("colorBA", optUniform->getName()); + EXPECT_EQ(ramses::EDataType::Float, optUniform->getDataType()); + EXPECT_EQ(2u, optUniform->getElementCount()); + + optUniform = loadedEffect->getUniformInput(3u); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_STREQ("gs_uniform", optUniform->getName()); + EXPECT_EQ(ramses::EDataType::Float, optUniform->getDataType()); + EXPECT_EQ(1u, optUniform->getElementCount()); + + // check attributes + EXPECT_EQ(3u, loadedEffect->getAttributeInputCount()); + std::optional optAttrib = loadedEffect->getAttributeInput(0u); + ASSERT_TRUE(optAttrib.has_value()); + EXPECT_STREQ("a_position1", optAttrib->getName()); + EXPECT_EQ(ramses::EDataType::Vector3F, optAttrib->getDataType()); + + optAttrib = loadedEffect->getAttributeInput(1u); + ASSERT_TRUE(optAttrib.has_value()); + EXPECT_STREQ("a_position2", optAttrib->getName()); + EXPECT_EQ(ramses::EDataType::Float, optAttrib->getDataType()); + + optAttrib = loadedEffect->getAttributeInput(2u); + ASSERT_TRUE(optAttrib.has_value()); + EXPECT_STREQ("a_position3", optAttrib->getName()); + EXPECT_EQ(ramses::EDataType::Float, optAttrib->getDataType()); + + // GS + EXPECT_TRUE(loadedEffect->hasGeometryShader()); + EDrawMode gsInputType = EDrawMode::Points; + EXPECT_TRUE(loadedEffect->getGeometryShaderInputType(gsInputType)); + EXPECT_EQ(EDrawMode::Lines, gsInputType); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearance) + { + Effect* effect = TestEffects::CreateTestEffect(this->m_scene); + Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); + + doWriteReadCycle(); + + auto* loadedAppearance = this->getObjectForTesting("appearance"); + + EXPECT_EQ(appearance->getEffect().impl().getLowlevelResourceHash(), loadedAppearance->getEffect().impl().getLowlevelResourceHash()); + EXPECT_EQ(appearance->impl().getRenderStateHandle(), loadedAppearance->impl().getRenderStateHandle()); + EXPECT_EQ(appearance->impl().getUniformDataInstance(), loadedAppearance->impl().getUniformDataInstance()); + EXPECT_EQ(appearance->impl().getIScene().getSceneId(), loadedAppearance->impl().getIScene().getSceneId()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearanceWithGeometryShaderAndRestrictDrawMode) + { + const Effect* effect = TestEffects::CreateTestEffectWithAllStages(this->m_scene); + this->m_scene.createAppearance(*effect, "appearance"); + + doWriteReadCycle(); + + auto* loadedAppearance = this->getObjectForTesting("appearance"); + + EDrawMode drawMode = EDrawMode::Points; + EXPECT_TRUE(loadedAppearance->getDrawMode(drawMode)); + EXPECT_EQ(EDrawMode::Lines, drawMode); + + EDrawMode gsInputType = EDrawMode::Points; + ASSERT_TRUE(loadedAppearance->getEffect().hasGeometryShader()); + EXPECT_TRUE(loadedAppearance->getEffect().getGeometryShaderInputType(gsInputType)); + EXPECT_EQ(EDrawMode::Lines, gsInputType); + + // valid draw mode change + EXPECT_TRUE(loadedAppearance->setDrawMode(EDrawMode::LineStrip)); + + // invalid draw mode change + EXPECT_FALSE(loadedAppearance->setDrawMode(EDrawMode::Points)); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearanceWithGeometryShaderAndRestrictDrawMode_usingSameEffect) + { + const Effect* effect1 = TestEffects::CreateTestEffectWithAllStages(this->m_scene); + const Effect* effect2 = TestEffects::CreateTestEffectWithAllStages(this->m_scene); + this->m_scene.createAppearance(*effect1, "appearance1"); + this->m_scene.createAppearance(*effect2, "appearance2"); + + doWriteReadCycle(); + + auto* loadedAppearance1 = this->getObjectForTesting("appearance1"); + auto* loadedAppearance2 = this->getObjectForTesting("appearance2"); + + // appearance 1 + EDrawMode drawMode = EDrawMode::Points; + EDrawMode gsInputType = EDrawMode::Points; + EXPECT_TRUE(loadedAppearance1->getDrawMode(drawMode)); + EXPECT_EQ(EDrawMode::Lines, drawMode); + ASSERT_TRUE(loadedAppearance1->getEffect().hasGeometryShader()); + EXPECT_TRUE(loadedAppearance1->getEffect().getGeometryShaderInputType(gsInputType)); + EXPECT_EQ(EDrawMode::Lines, gsInputType); + + // appearance 2 + EXPECT_TRUE(loadedAppearance1->getDrawMode(drawMode)); + EXPECT_EQ(EDrawMode::Lines, drawMode); + ASSERT_TRUE(loadedAppearance1->getEffect().hasGeometryShader()); + EXPECT_TRUE(loadedAppearance1->getEffect().getGeometryShaderInputType(gsInputType)); + EXPECT_EQ(EDrawMode::Lines, gsInputType); + + // valid draw mode change + EXPECT_TRUE(loadedAppearance1->setDrawMode(EDrawMode::LineStrip)); + EXPECT_TRUE(loadedAppearance2->setDrawMode(EDrawMode::LineStrip)); + + // invalid draw mode change + EXPECT_FALSE(loadedAppearance1->setDrawMode(EDrawMode::Points)); + EXPECT_FALSE(loadedAppearance2->setDrawMode(EDrawMode::Points)); + } + + TEST_F(ASceneLoadedFromFile, keepingTrackOfsceneObjectIdsAndFindObjectByIdWork) + { + Effect* effect = TestEffects::CreateTestEffect(this->m_scene); + + const Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); + const Geometry* geometry = this->m_scene.createGeometry(*effect, "geometry"); + + const sceneObjectId_t appearanceIdBeforeSaveAndLoad = appearance->getSceneObjectId(); + const sceneObjectId_t geometryIdBeforeSaveAndLoad = geometry->getSceneObjectId(); + EXPECT_NE(appearanceIdBeforeSaveAndLoad, geometryIdBeforeSaveAndLoad); + + doWriteReadCycle(); + + const auto& appearanceLoaded = *this->m_scene.findObject(appearanceIdBeforeSaveAndLoad); + EXPECT_EQ(appearanceIdBeforeSaveAndLoad, appearanceLoaded.getSceneObjectId()); + + const auto& geometryLoaded = *this->m_scene.findObject(geometryIdBeforeSaveAndLoad); + EXPECT_EQ(geometryIdBeforeSaveAndLoad, geometryLoaded.getSceneObjectId()); + + const ramses::Camera* camera = this->m_scene.createOrthographicCamera("camera"); + EXPECT_NE(appearanceIdBeforeSaveAndLoad, camera->getSceneObjectId()); + EXPECT_NE(geometryIdBeforeSaveAndLoad, camera->getSceneObjectId()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAnAppearanceWithUniformValuesSetOrBound) + { + Effect* effect = TestEffects::CreateTestEffect(this->m_scene); + Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); + + std::optional optFragColorR = effect->findUniformInput("u_FragColorR"); + std::optional optFragColorG = effect->findUniformInput("u_FragColorG"); + + auto fragColorDataObject = this->m_scene.createDataObject(ramses::EDataType::Float); + fragColorDataObject->setValue(123.f); + + appearance->setInputValue(*optFragColorR, 567.f); + appearance->bindInput(*optFragColorG, *fragColorDataObject); + + doWriteReadCycle(); + + auto* loadedAppearance = this->getObjectForTesting("appearance"); + const Effect& loadedEffect = loadedAppearance->getEffect(); + + std::optional optFragColorROut = loadedEffect.findUniformInput("u_FragColorR"); + std::optional optFragColorGOut = loadedEffect.findUniformInput("u_FragColorG"); + + float resultR = 0.f; + loadedAppearance->getInputValue(*optFragColorROut, resultR); + EXPECT_FLOAT_EQ(567.f, resultR); + + ASSERT_TRUE(loadedAppearance->isInputBound(*optFragColorGOut)); + float resultG = 0.f; + const DataObject& fragColorDataObjectOut = *loadedAppearance->getDataObjectBoundToInput(*optFragColorGOut); + fragColorDataObjectOut.getValue(resultG); + EXPECT_EQ(123.f, resultG); + } + + TEST_F(ASceneLoadedFromFile, multipleAppearancesSharingSameEffectAreCorrectlyWrittenAndLoaded) + { + Effect* effect = TestEffects::CreateTestEffect(this->m_scene); + const Appearance* appearance1 = m_scene.createAppearance(*effect, "appearance1"); + const Appearance* appearance2 = m_scene.createAppearance(*effect, "appearance2"); + + // check data layout ref count on LL + EXPECT_EQ(appearance1->impl().getUniformDataLayout(), appearance2->impl().getUniformDataLayout()); + const uint32_t numReferences = m_scene.impl().getIScene().getNumDataLayoutReferences(appearance1->impl().getUniformDataLayout()); + EXPECT_EQ(2u, numReferences); + + doWriteReadCycle(); + + auto* loadedAppearance1 = this->getObjectForTesting("appearance1"); + auto* loadedAppearance2 = this->getObjectForTesting("appearance2"); + + // check data layout ref count on LL in loaded scene + EXPECT_EQ(loadedAppearance1->impl().getUniformDataLayout(), loadedAppearance2->impl().getUniformDataLayout()); + const uint32_t numReferencesLoaded = m_scene.impl().getIScene().getNumDataLayoutReferences(loadedAppearance1->impl().getUniformDataLayout()); + EXPECT_EQ(numReferences, numReferencesLoaded); + + m_sceneLoaded->destroy(*loadedAppearance2); + m_sceneLoaded->destroy(*loadedAppearance1); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteGeometry) + { + static const uint16_t inds[3] = { 0, 1, 2 }; + ArrayResource* const indices = this->m_scene.createArrayResource(3u, inds, "indices"); + + Effect* effect = TestEffects::CreateTestEffectWithAttribute(this->m_scene); + + Geometry* geometry = this->m_scene.createGeometry(*effect, "geometry"); + ASSERT_TRUE(geometry != nullptr); + EXPECT_TRUE(geometry->setIndices(*indices)); + + doWriteReadCycle(); + + const Geometry* loadedGeometry = this->getObjectForTesting("geometry"); + + EXPECT_EQ(geometry->impl().getEffectHash(), loadedGeometry->impl().getEffectHash()); + EXPECT_EQ(geometry->impl().getAttributeDataLayout(), loadedGeometry->impl().getAttributeDataLayout()); + EXPECT_EQ(geometry->impl().getAttributeDataInstance(), loadedGeometry->impl().getAttributeDataInstance()); + EXPECT_EQ(geometry->impl().getIndicesCount(), loadedGeometry->impl().getIndicesCount()); + + const Effect& loadedEffect = loadedGeometry->getEffect(); + EXPECT_EQ(effect->getResourceId(), loadedEffect.getResourceId()); + EXPECT_EQ(effect->impl().getLowlevelResourceHash(), loadedEffect.impl().getLowlevelResourceHash()); + EXPECT_EQ(effect->impl().getObjectRegistryHandle(), loadedEffect.impl().getObjectRegistryHandle()); + EXPECT_EQ(4u, loadedEffect.getAttributeInputCount()); + std::optional attributeInputOut = loadedEffect.findAttributeInput("a_position"); + EXPECT_TRUE(attributeInputOut.has_value()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode) + { + MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); + + doWriteReadCycle(); + + auto* loadedMeshNode = this->getObjectForTesting("a meshnode"); + + EXPECT_EQ(meshNode->getAppearance(), loadedMeshNode->getAppearance()); + EXPECT_EQ(meshNode->getGeometry(), loadedMeshNode->getGeometry()); + EXPECT_EQ(meshNode->getStartIndex(), loadedMeshNode->getStartIndex()); + EXPECT_EQ(meshNode->getIndexCount(), loadedMeshNode->getIndexCount()); + EXPECT_EQ(meshNode->impl().getFlattenedVisibility(), loadedMeshNode->impl().getFlattenedVisibility()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode_withVisibilityParent) + { + MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); + + Node* visibilityParent = this->m_scene.createNode("vis node"); + visibilityParent->setVisibility(EVisibilityMode::Invisible); + visibilityParent->addChild(*meshNode); + + // Apply visibility state only with flush, not before + EXPECT_EQ(meshNode->impl().getFlattenedVisibility(), EVisibilityMode::Visible); + this->m_scene.flush(); + EXPECT_EQ(meshNode->impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + + doWriteReadCycle(); + + auto* loadedMeshNode = this->getObjectForTesting("a meshnode"); + EXPECT_EQ(loadedMeshNode->impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode_withVisibilityParentOff) + { + MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); + + Node* visibilityParent = this->m_scene.createNode("vis node"); + visibilityParent->setVisibility(EVisibilityMode::Off); + visibilityParent->addChild(*meshNode); + + // Apply visibility state only with flush, not before + EXPECT_EQ(meshNode->impl().getFlattenedVisibility(), EVisibilityMode::Visible); + this->m_scene.flush(); + EXPECT_EQ(meshNode->impl().getFlattenedVisibility(), EVisibilityMode::Off); + + doWriteReadCycle(); + + auto* loadedMeshNode = this->getObjectForTesting("a meshnode"); + EXPECT_EQ(loadedMeshNode->impl().getFlattenedVisibility(), EVisibilityMode::Off); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteAMeshNode_withValues) + { + Effect* effect = TestEffects::CreateTestEffect(this->m_scene); + Appearance* appearance = this->m_scene.createAppearance(*effect, "appearance"); + + Geometry* geometry = this->m_scene.createGeometry(*effect, "geometry"); + const uint16_t data = 0u; + ArrayResource* indices = this->m_scene.createArrayResource(1u, &data, "indices"); + geometry->setIndices(*indices); + + MeshNode* meshNode = this->m_scene.createMeshNode("a meshnode"); + + //EXPECT_TRUE(meshNode.setIndexArray(indices)); + EXPECT_TRUE(meshNode->setAppearance(*appearance)); + EXPECT_TRUE(meshNode->setGeometry(*geometry)); + EXPECT_TRUE(meshNode->setStartIndex(456)); + EXPECT_TRUE(meshNode->setIndexCount(678u)); + EXPECT_TRUE(meshNode->impl().setFlattenedVisibility(EVisibilityMode::Off)); + doWriteReadCycle(); + + auto* loadedMeshNode = this->getObjectForTesting("a meshnode"); + + EXPECT_EQ(meshNode->getAppearance()->getName(), loadedMeshNode->getAppearance()->getName()); + EXPECT_EQ(meshNode->getAppearance()->getSceneObjectId(), loadedMeshNode->getAppearance()->getSceneObjectId()); + EXPECT_EQ(meshNode->getGeometry()->getName(), loadedMeshNode->getGeometry()->getName()); + EXPECT_EQ(meshNode->getGeometry()->getSceneObjectId(), loadedMeshNode->getGeometry()->getSceneObjectId()); + EXPECT_EQ(meshNode->getSceneObjectId(), loadedMeshNode->getSceneObjectId()); + EXPECT_EQ(456u, loadedMeshNode->getStartIndex()); + EXPECT_EQ(678u, loadedMeshNode->getIndexCount()); + EXPECT_EQ(loadedMeshNode->impl().getFlattenedVisibility(), EVisibilityMode::Off); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithVisibility) + { + Node* visibilityNode = this->m_scene.createNode("a visibilitynode"); + + visibilityNode->setVisibility(EVisibilityMode::Invisible); + + doWriteReadCycle(); + + Node* loadedVisibilityNode = this->getObjectForTesting("a visibilitynode"); + + EXPECT_EQ(loadedVisibilityNode->getVisibility(), EVisibilityMode::Invisible); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteARenderGroup) + { + ramses::RenderGroup* renderGroup = this->m_scene.createRenderGroup("a rendergroup"); + + MeshNode* meshA = this->m_scene.createMeshNode("meshA"); + MeshNode* meshB = this->m_scene.createMeshNode("meshB"); + + renderGroup->addMeshNode(*meshA, 1); + renderGroup->addMeshNode(*meshB, 2); + + doWriteReadCycle(); + + auto* loadedRenderGroup = this->getObjectForTesting("a rendergroup"); + const MeshNode* loadedMeshA = this->getObjectForTesting("meshA"); + const MeshNode* loadedMeshB = this->getObjectForTesting("meshB"); + + EXPECT_EQ(renderGroup->getName(), loadedRenderGroup->getName()); + EXPECT_EQ(renderGroup->getSceneObjectId(), loadedRenderGroup->getSceneObjectId()); + + EXPECT_EQ(2u, loadedRenderGroup->impl().getAllMeshes().size()); + EXPECT_EQ(&loadedMeshA->impl(), loadedRenderGroup->impl().getAllMeshes()[0]); + EXPECT_EQ(&loadedMeshB->impl(), loadedRenderGroup->impl().getAllMeshes()[1]); + + const auto& internalRg = m_sceneLoaded->impl().getIScene().getRenderGroup(renderGroup->impl().getRenderGroupHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderable(meshA->impl().getRenderableHandle(), internalRg)); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderable(meshB->impl().getRenderableHandle(), internalRg)); + EXPECT_EQ(1, ramses::internal::RenderGroupUtils::FindRenderableEntry(meshA->impl().getRenderableHandle(), internalRg)->order); + EXPECT_EQ(2, ramses::internal::RenderGroupUtils::FindRenderableEntry(meshB->impl().getRenderableHandle(), internalRg)->order); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteANestedRenderGroup) + { + ramses::RenderGroup* renderGroup = this->m_scene.createRenderGroup("a rendergroup"); + ramses::RenderGroup* nestedRenderGroup = this->m_scene.createRenderGroup("a nested rendergroup"); + + MeshNode* meshA = this->m_scene.createMeshNode("meshA"); + MeshNode* meshB = this->m_scene.createMeshNode("meshB"); + + ASSERT_TRUE(renderGroup->addMeshNode(*meshA, 1)); + ASSERT_TRUE(nestedRenderGroup->addMeshNode(*meshB, 2)); + ASSERT_TRUE(renderGroup->addRenderGroup(*nestedRenderGroup, 1)); + + doWriteReadCycle(); + + auto* loadedRenderGroup = this->getObjectForTesting("a rendergroup"); + auto* loadedNestedRenderGroup = this->getObjectForTesting("a nested rendergroup"); + const MeshNode* loadedMeshA = this->getObjectForTesting("meshA"); + const MeshNode* loadedMeshB = this->getObjectForTesting("meshB"); + + EXPECT_EQ(nestedRenderGroup->getName(), loadedNestedRenderGroup->getName()); + EXPECT_EQ(nestedRenderGroup->getSceneObjectId(), loadedNestedRenderGroup->getSceneObjectId()); + + EXPECT_EQ(1u, loadedRenderGroup->impl().getAllMeshes().size()); + EXPECT_EQ(1u, loadedRenderGroup->impl().getAllRenderGroups().size()); + EXPECT_EQ(1u, loadedNestedRenderGroup->impl().getAllMeshes().size()); + + EXPECT_EQ(&loadedMeshA->impl(), loadedRenderGroup->impl().getAllMeshes()[0]); + EXPECT_EQ(&loadedMeshB->impl(), loadedNestedRenderGroup->impl().getAllMeshes()[0]); + + EXPECT_EQ(&loadedNestedRenderGroup->impl(), loadedRenderGroup->impl().getAllRenderGroups()[0]); + + const auto& internalRg = m_sceneLoaded->impl().getIScene().getRenderGroup(renderGroup->impl().getRenderGroupHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderable(meshA->impl().getRenderableHandle(), internalRg)); + EXPECT_EQ(1, ramses::internal::RenderGroupUtils::FindRenderableEntry(meshA->impl().getRenderableHandle(), internalRg)->order); + + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(nestedRenderGroup->impl().getRenderGroupHandle(), internalRg)); + EXPECT_EQ(1, ramses::internal::RenderGroupUtils::FindRenderGroupEntry(nestedRenderGroup->impl().getRenderGroupHandle(), internalRg)->order); + + const auto& internalRgNested = m_sceneLoaded->impl().getIScene().getRenderGroup(nestedRenderGroup->impl().getRenderGroupHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderable(meshB->impl().getRenderableHandle(), internalRgNested)); + EXPECT_EQ(2, ramses::internal::RenderGroupUtils::FindRenderableEntry(meshB->impl().getRenderableHandle(), internalRgNested)->order); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteABasicRenderPass) + { + const int32_t renderOrder = 1; + + ramses::RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); + EXPECT_TRUE(renderPass->setRenderOrder(renderOrder)); + EXPECT_TRUE(renderPass->setEnabled(false)); + EXPECT_TRUE(renderPass->setRenderOnce(true)); + + doWriteReadCycle(); + + auto* loadedRenderPass = this->getObjectForTesting("a renderpass"); + + EXPECT_EQ(renderPass->getName(), loadedRenderPass->getName()); + EXPECT_EQ(renderPass->getSceneObjectId(), loadedRenderPass->getSceneObjectId()); + EXPECT_EQ(renderOrder, loadedRenderPass->getRenderOrder()); + EXPECT_FALSE(loadedRenderPass->isEnabled()); + EXPECT_TRUE(loadedRenderPass->isRenderOnce()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteARenderPassWithACamera) + { + ramses::RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); + + PerspectiveCamera* perspCam = this->m_scene.createPerspectiveCamera("camera"); + perspCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); + perspCam->setViewport(0, 0, 100, 200); + renderPass->setCamera(*perspCam); + + doWriteReadCycle(); + + auto* loadedRenderPass = this->getObjectForTesting("a renderpass"); + const auto* loadedCamera = this->getObjectForTesting("camera"); + + EXPECT_EQ(renderPass->getName(), loadedRenderPass->getName()); + EXPECT_EQ(renderPass->getSceneObjectId(), loadedRenderPass->getSceneObjectId()); + EXPECT_EQ(loadedCamera, loadedRenderPass->getCamera()); + ValidationReport report; + loadedCamera->validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteARenderPassWhichHasRenderGroups) + { + ramses::RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); + ramses::RenderGroup* groupA = this->m_scene.createRenderGroup("groupA"); + ramses::RenderGroup* groupB = this->m_scene.createRenderGroup("groupB"); + renderPass->addRenderGroup(*groupA, 1); + renderPass->addRenderGroup(*groupB, 2); + + doWriteReadCycle(); + + auto* loadedRenderPass = this->getObjectForTesting("a renderpass"); + const auto* loadedMeshA = this->getObjectForTesting("groupA"); + const auto* loadedMeshB = this->getObjectForTesting("groupB"); + + EXPECT_EQ(renderPass->getName(), loadedRenderPass->getName()); + EXPECT_EQ(renderPass->getSceneObjectId(), loadedRenderPass->getSceneObjectId()); + EXPECT_EQ(2u, loadedRenderPass->impl().getAllRenderGroups().size()); + EXPECT_EQ(&loadedMeshA->impl(), loadedRenderPass->impl().getAllRenderGroups()[0]); + EXPECT_EQ(&loadedMeshB->impl(), loadedRenderPass->impl().getAllRenderGroups()[1]); + + const ramses::internal::RenderPass& internalRP = m_sceneLoaded->impl().getIScene().getRenderPass(renderPass->impl().getRenderPassHandle()); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(groupA->impl().getRenderGroupHandle(), internalRP)); + ASSERT_TRUE(ramses::internal::RenderGroupUtils::ContainsRenderGroup(groupB->impl().getRenderGroupHandle(), internalRP)); + EXPECT_EQ(groupA->impl().getRenderGroupHandle(), ramses::internal::RenderGroupUtils::FindRenderGroupEntry(groupA->impl().getRenderGroupHandle(), internalRP)->renderGroup); + EXPECT_EQ(groupB->impl().getRenderGroupHandle(), ramses::internal::RenderGroupUtils::FindRenderGroupEntry(groupB->impl().getRenderGroupHandle(), internalRP)->renderGroup); + EXPECT_EQ(1, ramses::internal::RenderGroupUtils::FindRenderGroupEntry(groupA->impl().getRenderGroupHandle(), internalRP)->order); + EXPECT_EQ(2, ramses::internal::RenderGroupUtils::FindRenderGroupEntry(groupB->impl().getRenderGroupHandle(), internalRP)->order); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteBlitPass) + { + const int32_t renderOrder = 1; + const ramses::RenderBuffer* srcRenderBuffer = this->m_scene.createRenderBuffer(23, 42, ERenderBufferFormat::Depth32, ERenderBufferAccessMode::WriteOnly, 0u, "src renderBuffer"); + const ramses::RenderBuffer* dstRenderBuffer = this->m_scene.createRenderBuffer(23, 42, ERenderBufferFormat::Depth32, ERenderBufferAccessMode::WriteOnly, 0u, "dst renderBuffer"); + + ramses::BlitPass* blitPass = this->m_scene.createBlitPass(*srcRenderBuffer, *dstRenderBuffer, "a blitpass"); + EXPECT_TRUE(blitPass->setRenderOrder(renderOrder)); + EXPECT_TRUE(blitPass->setEnabled(false)); + + doWriteReadCycle(); + + const auto* loadedBlitPass = this->getObjectForTesting("a blitpass"); + + EXPECT_EQ(blitPass->getName(), loadedBlitPass->getName()); + EXPECT_EQ(blitPass->getSceneObjectId(), loadedBlitPass->getSceneObjectId()); + EXPECT_EQ(renderOrder, loadedBlitPass->getRenderOrder()); + EXPECT_FALSE(loadedBlitPass->isEnabled()); + + const ramses::internal::BlitPassHandle loadedBlitPassHandle = loadedBlitPass->impl().getBlitPassHandle(); + const ramses::internal::BlitPass& blitPassInternal = m_sceneLoaded->impl().getIScene().getBlitPass(loadedBlitPassHandle); + EXPECT_EQ(renderOrder, blitPassInternal.renderOrder); + EXPECT_FALSE(blitPassInternal.isEnabled); + EXPECT_EQ(srcRenderBuffer->impl().getRenderBufferHandle(), blitPassInternal.sourceRenderBuffer); + EXPECT_EQ(dstRenderBuffer->impl().getRenderBufferHandle(), blitPassInternal.destinationRenderBuffer); + EXPECT_EQ(renderOrder, blitPassInternal.renderOrder); + + const ramses::internal::PixelRectangle& sourceRegion = blitPassInternal.sourceRegion; + EXPECT_EQ(0u, sourceRegion.x); + EXPECT_EQ(0u, sourceRegion.y); + EXPECT_EQ(static_cast(srcRenderBuffer->getWidth()), sourceRegion.width); + EXPECT_EQ(static_cast(srcRenderBuffer->getHeight()), sourceRegion.height); + + const ramses::internal::PixelRectangle& destinationRegion = blitPassInternal.destinationRegion; + EXPECT_EQ(0u, destinationRegion.x); + EXPECT_EQ(0u, destinationRegion.y); + EXPECT_EQ(static_cast(dstRenderBuffer->getWidth()), destinationRegion.width); + EXPECT_EQ(static_cast(dstRenderBuffer->getHeight()), destinationRegion.height); + + //client HL api + { + uint32_t sourceXOut = std::numeric_limits::max(); + uint32_t sourceYOut = std::numeric_limits::max(); + uint32_t destinationXOut = std::numeric_limits::max(); + uint32_t destinationYOut = std::numeric_limits::max(); + uint32_t widthOut = std::numeric_limits::max(); + uint32_t heightOut = std::numeric_limits::max(); + loadedBlitPass->getBlittingRegion(sourceXOut, sourceYOut, destinationXOut, destinationYOut, widthOut, heightOut); + EXPECT_EQ(0u, sourceXOut); + EXPECT_EQ(0u, sourceYOut); + EXPECT_EQ(0u, destinationXOut); + EXPECT_EQ(0u, destinationYOut); + EXPECT_EQ(dstRenderBuffer->getWidth(), widthOut); + EXPECT_EQ(dstRenderBuffer->getHeight(), heightOut); + } + + EXPECT_EQ(srcRenderBuffer->impl().getRenderBufferHandle(), loadedBlitPass->getSourceRenderBuffer().impl().getRenderBufferHandle()); + EXPECT_EQ(srcRenderBuffer->impl().getObjectRegistryHandle(), loadedBlitPass->getSourceRenderBuffer().impl().getObjectRegistryHandle()); + + EXPECT_EQ(dstRenderBuffer->impl().getRenderBufferHandle(), loadedBlitPass->getDestinationRenderBuffer().impl().getRenderBufferHandle()); + EXPECT_EQ(dstRenderBuffer->impl().getObjectRegistryHandle(), loadedBlitPass->getDestinationRenderBuffer().impl().getObjectRegistryHandle()); + } + + TEST_F(ASceneLoadedFromFile, canReadWritePickableObject) + { + const ramses::EDataType geometryBufferDataType = ramses::EDataType::Vector3F; + const ArrayBuffer* geometryBuffer = this->m_scene.createArrayBuffer(geometryBufferDataType, 3u, "geometryBuffer"); + + const int32_t viewPort_x = 1; + const int32_t viewPort_y = 2; + const uint32_t viewPort_width = 200; + const uint32_t viewPort_height = 300; + PerspectiveCamera* pickableCamera = m_scene.createPerspectiveCamera("pickableCamera"); + pickableCamera->setFrustum(-1.4f, 1.4f, -1.4f, 1.4f, 1.f, 100.f); + pickableCamera->setViewport(viewPort_x, viewPort_y, viewPort_width, viewPort_height); + + const pickableObjectId_t id(2); + ramses::PickableObject* pickableObject = this->m_scene.createPickableObject(*geometryBuffer, id, "PickableObject"); + EXPECT_TRUE(pickableObject->setCamera(*pickableCamera)); + EXPECT_TRUE(pickableObject->setEnabled(false)); + + doWriteReadCycle(); + + const auto* loadedPickableObject = this->getObjectForTesting("PickableObject"); + + EXPECT_EQ(pickableObject->getName(), loadedPickableObject->getName()); + EXPECT_EQ(pickableObject->getSceneObjectId(), loadedPickableObject->getSceneObjectId()); + EXPECT_EQ(id, loadedPickableObject->getPickableObjectId()); + EXPECT_FALSE(loadedPickableObject->isEnabled()); + EXPECT_EQ(this->getObjectForTesting("pickableCamera"), loadedPickableObject->getCamera()); + EXPECT_EQ(this->getObjectForTesting("geometryBuffer"), &loadedPickableObject->getGeometryBuffer()); + + const ramses::internal::PickableObjectHandle loadedPickableObjectPassHandle = loadedPickableObject->impl().getPickableObjectHandle(); + const ramses::internal::PickableObject& pickableObjectInternal = m_sceneLoaded->impl().getIScene().getPickableObject(loadedPickableObjectPassHandle); + EXPECT_EQ(id.getValue(), pickableObjectInternal.id.getValue()); + EXPECT_FALSE(pickableObjectInternal.isEnabled); + EXPECT_EQ(geometryBuffer->impl().getDataBufferHandle(), pickableObjectInternal.geometryHandle); + EXPECT_EQ(pickableCamera->impl().getCameraHandle(), pickableObjectInternal.cameraHandle); + + EXPECT_EQ(geometryBuffer->impl().getDataBufferHandle(), loadedPickableObject->getGeometryBuffer().impl().getDataBufferHandle()); + EXPECT_EQ(geometryBuffer->impl().getObjectRegistryHandle(), loadedPickableObject->getGeometryBuffer().impl().getObjectRegistryHandle()); + + EXPECT_EQ(pickableCamera->impl().getCameraHandle(), loadedPickableObject->getCamera()->impl().getCameraHandle()); + EXPECT_EQ(pickableCamera->impl().getObjectRegistryHandle(), loadedPickableObject->getCamera()->impl().getObjectRegistryHandle()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteRenderBuffer) + { + ramses::RenderBuffer* renderBuffer = this->m_scene.createRenderBuffer(23, 42, ERenderBufferFormat::Depth16, ERenderBufferAccessMode::WriteOnly, 4u, "a renderTarget"); + + doWriteReadCycle(); + + auto* loadedRenderBuffer = this->getObjectForTesting("a renderTarget"); + + EXPECT_EQ(renderBuffer->getName(), loadedRenderBuffer->getName()); + EXPECT_EQ(renderBuffer->getSceneObjectId(), loadedRenderBuffer->getSceneObjectId()); + EXPECT_EQ(renderBuffer->getWidth(), loadedRenderBuffer->getWidth()); + EXPECT_EQ(renderBuffer->getHeight(), loadedRenderBuffer->getHeight()); + EXPECT_EQ(renderBuffer->getBufferFormat(), loadedRenderBuffer->getBufferFormat()); + EXPECT_EQ(renderBuffer->getAccessMode(), loadedRenderBuffer->getAccessMode()); + EXPECT_EQ(renderBuffer->getSampleCount(), loadedRenderBuffer->getSampleCount()); + + EXPECT_EQ(renderBuffer->impl().getRenderBufferHandle(), loadedRenderBuffer->impl().getRenderBufferHandle()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteARenderPassWithARenderTargetAndCamera) + { + ramses::RenderPass* renderPass = this->m_scene.createRenderPass("a renderpass"); + + ramses::RenderBuffer* renderBuffer = this->m_scene.createRenderBuffer(23u, 42u, ERenderBufferFormat::Depth32, ERenderBufferAccessMode::ReadWrite, 0u, "a renderBuffer"); + RenderTargetDescription rtDesc; + rtDesc.addRenderBuffer(*renderBuffer); + ramses::RenderTarget* renderTarget = this->m_scene.createRenderTarget(rtDesc, "target"); + + OrthographicCamera* orthoCam = this->m_scene.createOrthographicCamera("camera"); + orthoCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); + orthoCam->setViewport(0, 0, 100, 200); + + renderPass->setCamera(*orthoCam); + renderPass->setRenderTarget(renderTarget); + + doWriteReadCycle(); + + const auto* loadedRenderPass = this->getObjectForTesting("a renderpass"); + const auto* loadedRenderTarget = this->getObjectForTesting("target"); + const auto* loadedCamera = this->getObjectForTesting("camera"); + + EXPECT_EQ(loadedRenderTarget, loadedRenderPass->getRenderTarget()); + EXPECT_EQ(loadedCamera, loadedRenderPass->getCamera()); + ValidationReport report; + loadedRenderTarget->validate(report); + loadedCamera->validate(report); + EXPECT_FALSE(report.hasIssue()) << report.impl().toString(); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteRenderTarget) + { + const ramses::RenderBuffer& rb = *m_scene.createRenderBuffer(16u, 8u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + RenderTargetDescription rtDesc; + rtDesc.addRenderBuffer(rb); + + ramses::RenderTarget* renderTarget = this->m_scene.createRenderTarget(rtDesc, "a renderTarget"); + + doWriteReadCycle(); + + auto* loadedRenderTarget = this->getObjectForTesting("a renderTarget"); + + EXPECT_EQ(renderTarget->getName(), loadedRenderTarget->getName()); + EXPECT_EQ(renderTarget->getSceneObjectId(), loadedRenderTarget->getSceneObjectId()); + EXPECT_EQ(renderTarget->getWidth(), loadedRenderTarget->getWidth()); + EXPECT_EQ(renderTarget->getHeight(), loadedRenderTarget->getHeight()); + + EXPECT_EQ(renderTarget->impl().getRenderTargetHandle(), loadedRenderTarget->impl().getRenderTargetHandle()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteIndexDataBuffer) + { + ArrayBuffer& buffer = *m_scene.createArrayBuffer(ramses::EDataType::UInt32, 6u, "indexDB"); + buffer.updateData(3u, 2u, std::array{ {6, 7} }.data()); + + doWriteReadCycle(); + + const auto* loadedBuffer = this->getObjectForTesting("indexDB"); + + EXPECT_EQ(buffer.getName(), loadedBuffer->getName()); + EXPECT_EQ(buffer.impl().getDataBufferHandle(), loadedBuffer->impl().getDataBufferHandle()); + EXPECT_EQ(6 * sizeof(uint32_t), m_scene.impl().getIScene().getDataBuffer(loadedBuffer->impl().getDataBufferHandle()).data.size()); + EXPECT_EQ(5 * sizeof(uint32_t), m_scene.impl().getIScene().getDataBuffer(loadedBuffer->impl().getDataBufferHandle()).usedSize); + + const std::byte* loadedDataBufferData = m_scene.impl().getIScene().getDataBuffer(loadedBuffer->impl().getDataBufferHandle()).data.data(); + EXPECT_EQ(6u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedDataBufferData, 3)); + EXPECT_EQ(7u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedDataBufferData, 4)); + + EXPECT_EQ(6u, loadedBuffer->getMaximumNumberOfElements()); + EXPECT_EQ(5u, loadedBuffer->getUsedNumberOfElements()); + EXPECT_EQ(ramses::EDataType::UInt32, loadedBuffer->getDataType()); + std::array bufferDataOut{}; + EXPECT_TRUE(loadedBuffer->getData(bufferDataOut.data(), static_cast(bufferDataOut.size() * sizeof(uint32_t)))); + EXPECT_EQ(6u, bufferDataOut[3]); + EXPECT_EQ(7u, bufferDataOut[4]); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteTexture2DBuffer) + { + Texture2DBuffer& buffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2, "textureBuffer"); + buffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data()); + buffer.updateData(1, 0, 0, 1, 1, std::array{ {78} }.data()); + + doWriteReadCycle(); + + const auto* loadedBuffer = this->getObjectForTesting("textureBuffer"); + + EXPECT_EQ(buffer.getName(), loadedBuffer->getName()); + EXPECT_EQ(buffer.getSceneObjectId(), loadedBuffer->getSceneObjectId()); + EXPECT_EQ(buffer.impl().getTextureBufferHandle(), loadedBuffer->impl().getTextureBufferHandle()); + + //iscene + const ramses::internal::TextureBuffer& loadedInternalBuffer = m_scene.impl().getIScene().getTextureBuffer(loadedBuffer->impl().getTextureBufferHandle()); + ASSERT_EQ(2u, loadedInternalBuffer.mipMaps.size()); + EXPECT_EQ(3u, loadedInternalBuffer.mipMaps[0].width); + EXPECT_EQ(4u, loadedInternalBuffer.mipMaps[0].height); + EXPECT_EQ(1u, loadedInternalBuffer.mipMaps[1].width); + EXPECT_EQ(2u, loadedInternalBuffer.mipMaps[1].height); + EXPECT_EQ(56u, ramses::internal::TextureBuffer::GetMipMapDataSizeInBytes(loadedInternalBuffer)); + EXPECT_EQ(ramses::internal::EPixelStorageFormat::RGBA8, loadedInternalBuffer.textureFormat); + + const std::byte* loadedBufferDataMip0 = loadedInternalBuffer.mipMaps[0].data.data(); + const std::byte* loadedBufferDataMip1 = loadedInternalBuffer.mipMaps[1].data.data(); + EXPECT_EQ(12u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 0)); + EXPECT_EQ(23u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 1)); + EXPECT_EQ(34u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 3 * 1 + 0)); + EXPECT_EQ(56u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip0, 3 * 1 + 1)); + EXPECT_EQ(78u, ramses::internal::UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(loadedBufferDataMip1, 0)); + + //client API + EXPECT_EQ(2u, loadedBuffer->getMipLevelCount()); + uint32_t mipLevelWidthOut = 0u; + uint32_t mipLevelHeightOut = 0u; + EXPECT_TRUE(loadedBuffer->getMipLevelSize(0u, mipLevelWidthOut, mipLevelHeightOut)); + EXPECT_EQ(3u, mipLevelWidthOut); + EXPECT_EQ(4u, mipLevelHeightOut); + EXPECT_TRUE(loadedBuffer->getMipLevelSize(1u, mipLevelWidthOut, mipLevelHeightOut)); + EXPECT_EQ(1u, mipLevelWidthOut); + EXPECT_EQ(2u, mipLevelHeightOut); + + EXPECT_EQ(ETextureFormat::RGBA8, loadedBuffer->getTexelFormat()); + std::array bufferForMip0{}; + std::array bufferForMip1{}; + EXPECT_TRUE(loadedBuffer->getMipLevelData(0u, bufferForMip0.data(), static_cast(bufferForMip0.size() * sizeof(uint32_t)))); + EXPECT_TRUE(loadedBuffer->getMipLevelData(1u, bufferForMip1.data(), static_cast(bufferForMip1.size() * sizeof(uint32_t)))); + EXPECT_EQ(12u, bufferForMip0[0]); + EXPECT_EQ(23u, bufferForMip0[1]); + EXPECT_EQ(34u, bufferForMip0[3 * 1 + 0]); + EXPECT_EQ(56u, bufferForMip0[3 * 1 + 1]); + EXPECT_EQ(78u, bufferForMip1[0]); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteANode) + { + //generic node cannot be created, therefore using group node + Node* grandParent = this->m_scene.createNode("node1"); + Node* parent = this->m_scene.createNode("node2"); + Node* child = this->m_scene.createNode("node3"); + + grandParent->addChild(*parent); + child->setParent(*parent); + + child->setTranslation({1, 2, 3}); + child->setVisibility(EVisibilityMode::Invisible); + child->setRotation({1, 2, 3}, ERotationType::Euler_XYX); + child->setScaling({1, 2, 3}); + + doWriteReadCycle(); + + auto* loadedGrandParent = this->getObjectForTesting("node1"); + auto* loadedParent = this->getObjectForTesting("node2"); + auto* loadedChild = this->getObjectForTesting("node3"); + ASSERT_TRUE(nullptr != loadedGrandParent); + ASSERT_TRUE(nullptr != loadedParent); + ASSERT_TRUE(nullptr != loadedChild); + EXPECT_EQ(loadedParent, loadedChild->getParent()); + EXPECT_EQ(loadedGrandParent, loadedParent->getParent()); + EXPECT_EQ(loadedParent, loadedGrandParent->getChild(0u)); + EXPECT_EQ(1u, loadedGrandParent->getChildCount()); + EXPECT_EQ(1u, loadedParent->getChildCount()); + EXPECT_EQ(0u, loadedChild->getChildCount()); + + vec3f translation; + EXPECT_TRUE(loadedChild->getTranslation(translation)); + EXPECT_FLOAT_EQ(1, translation.x); + EXPECT_FLOAT_EQ(2, translation.y); + EXPECT_FLOAT_EQ(3, translation.z); + + EXPECT_EQ(loadedChild->getVisibility(), EVisibilityMode::Invisible); + + vec3f rotation; + EXPECT_TRUE(loadedChild->getRotation(rotation)); + EXPECT_FLOAT_EQ(1, rotation.x); + EXPECT_FLOAT_EQ(2, rotation.y); + EXPECT_FLOAT_EQ(3, rotation.z); + EXPECT_EQ(ERotationType::Euler_XYX, loadedChild->getRotationType()); + + vec3f scale; + EXPECT_TRUE(loadedChild->getScaling(scale)); + EXPECT_FLOAT_EQ(1, scale.x); + EXPECT_FLOAT_EQ(2, scale.y); + EXPECT_FLOAT_EQ(3, scale.z); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithTranslation) + { + Node* node = this->m_scene.createNode("translate node 1"); + Node* child = this->m_scene.createNode("groupnode child"); + + node->setTranslation({1, 2, 3}); + node->addChild(*child); + + doWriteReadCycle(); + + auto* loadedTranslateNode = getObjectForTesting("translate node 1"); + auto* loadedChild = getObjectForTesting("groupnode child"); + + ASSERT_TRUE(nullptr != loadedTranslateNode); + ASSERT_TRUE(nullptr != loadedChild); + + EXPECT_EQ(1u, node->getChildCount()); + EXPECT_EQ(loadedChild, loadedTranslateNode->getChild(0u)); + vec3f translation; + EXPECT_TRUE(node->getTranslation(translation)); + EXPECT_FLOAT_EQ(1, translation.x); + EXPECT_FLOAT_EQ(2, translation.y); + EXPECT_FLOAT_EQ(3, translation.z); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithRotation) + { + Node* node = this->m_scene.createNode("rotate node 1"); + Node* child = this->m_scene.createNode("groupnode child"); + + node->setRotation({1, 2, 3}, ERotationType::Euler_ZYX); + child->setParent(*node); + doWriteReadCycle(); + + auto* loadedRotateNode = getObjectForTesting("rotate node 1"); + auto* loadedChild = getObjectForTesting("groupnode child"); + + ASSERT_TRUE(nullptr != loadedRotateNode); + ASSERT_TRUE(nullptr != loadedChild); + + EXPECT_EQ(1u, loadedRotateNode->getChildCount()); + EXPECT_EQ(loadedChild, loadedRotateNode->getChild(0u)); + vec3f rotation; + EXPECT_TRUE(loadedRotateNode->getRotation(rotation)); + EXPECT_FLOAT_EQ(1, rotation.x); + EXPECT_FLOAT_EQ(2, rotation.y); + EXPECT_FLOAT_EQ(3, rotation.z); + EXPECT_EQ(ERotationType::Euler_ZYX, loadedRotateNode->getRotationType()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteANodeWithScaling) + { + Node* node = this->m_scene.createNode("scale node"); + Node* child = this->m_scene.createNode("groupnode child"); + + node->setScaling({1, 2, 3}); + child->setParent(*node); + doWriteReadCycle(); + + auto* loadedScaleNode = getObjectForTesting("scale node"); + auto* loadedChild = getObjectForTesting("groupnode child"); + + ASSERT_TRUE(nullptr != loadedScaleNode); + ASSERT_TRUE(nullptr != loadedChild); + + EXPECT_EQ(1u, loadedScaleNode->getChildCount()); + EXPECT_EQ(loadedChild, loadedScaleNode->getChild(0u)); + vec3f scale; + EXPECT_TRUE(loadedScaleNode->getScaling(scale)); + EXPECT_FLOAT_EQ(1, scale.x); + EXPECT_FLOAT_EQ(2, scale.y); + EXPECT_FLOAT_EQ(3, scale.z); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteATextureSampler) + { + const ETextureAddressMode wrapUMode = ETextureAddressMode::Mirror; + const ETextureAddressMode wrapVMode = ETextureAddressMode::Repeat; + const ETextureSamplingMethod minSamplingMethod = ETextureSamplingMethod::Linear_MipMapNearest; + const ETextureSamplingMethod magSamplingMethod = ETextureSamplingMethod::Linear; + const uint8_t data[4] = { 0u }; + const MipLevelData mipLevelData(sizeof(data), data); + Texture2D* texture = this->m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "texture"); + + auto* sampler = this->m_scene.createTextureSampler(wrapUMode, wrapVMode, minSamplingMethod, magSamplingMethod, *texture, 8u, "sampler"); + ASSERT_TRUE(nullptr != sampler); + + doWriteReadCycle(); + + auto* loadedSampler = getObjectForTesting("sampler"); + ASSERT_TRUE(nullptr != loadedSampler); + + EXPECT_EQ(wrapUMode, loadedSampler->getWrapUMode()); + EXPECT_EQ(wrapVMode, loadedSampler->getWrapVMode()); + EXPECT_EQ(minSamplingMethod, loadedSampler->getMinSamplingMethod()); + EXPECT_EQ(magSamplingMethod, loadedSampler->getMagSamplingMethod()); + EXPECT_EQ(8u, loadedSampler->getAnisotropyLevel()); + EXPECT_EQ(texture->impl().getLowlevelResourceHash(), this->m_sceneLoaded->impl().getIScene().getTextureSampler(loadedSampler->impl().getTextureSamplerHandle()).textureResource); + EXPECT_EQ(ERamsesObjectType::Texture2D, loadedSampler->impl().getTextureType()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteATextureSamplerMS) + { + ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + + TextureSamplerMS* sampler = this->m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); + ASSERT_TRUE(nullptr != sampler); + + doWriteReadCycle(); + + auto* loadedSampler = getObjectForTesting("sampler"); + ASSERT_TRUE(nullptr != loadedSampler); + + EXPECT_EQ(ERamsesObjectType::RenderBuffer, loadedSampler->impl().getTextureType()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteATextureSamplerExternal) + { + TextureSamplerExternal* sampler = this->m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, "sampler"); + ASSERT_TRUE(nullptr != sampler); + + doWriteReadCycle(); + + auto* loadedSampler = getObjectForTesting("sampler"); + ASSERT_TRUE(nullptr != loadedSampler); + + EXPECT_EQ(ERamsesObjectType::TextureSamplerExternal, loadedSampler->impl().getTextureType()); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteSceneId) + { + const sceneId_t sceneId = ramses::sceneId_t(1ULL << 63); + ramses::Scene& mScene(*client.createScene(SceneConfig(sceneId))); + + EXPECT_TRUE(mScene.saveToFile("someTempararyFile.ram", {})); + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); + ASSERT_TRUE(nullptr != m_sceneLoaded); + + EXPECT_EQ(sceneId, m_sceneLoaded->getSceneId()); + } + + TEST_F(ASceneLoadedFromFile, defaultsToLocalPublicationMode) + { + const sceneId_t sceneId(81); + EXPECT_TRUE(client.createScene(SceneConfig(sceneId))->saveToFile("someTempararyFile.ram", {})); + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); + ASSERT_TRUE(nullptr != m_sceneLoaded); + EXPECT_EQ(EScenePublicationMode::LocalOnly, m_sceneLoaded->impl().getPublicationModeSetFromSceneConfig()); + } + + TEST_F(ASceneLoadedFromFile, canOverridePublicationModeForLoadedFiles_savedAsLocalOnly_loadedAsRemote) + { + const sceneId_t sceneId(80); + SceneConfig config(sceneId, EScenePublicationMode::LocalOnly); + EXPECT_TRUE(client.createScene(config)->saveToFile("someTempararyFile.ram", {})); + + SceneConfig loadConfig; + loadConfig.setPublicationMode(EScenePublicationMode::LocalAndRemote); + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram", loadConfig); + ASSERT_TRUE(nullptr != m_sceneLoaded); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, m_sceneLoaded->impl().getPublicationModeSetFromSceneConfig()); + } + + TEST_F(ASceneLoadedFromFile, canOverridePublicationModeForLoadedFiles_savedAsRemote_loadedAsLocalOnly) + { + const sceneId_t sceneId(80); + SceneConfig config(sceneId, EScenePublicationMode::LocalAndRemote); + EXPECT_TRUE(client.createScene(config)->saveToFile("someTempararyFile.ram", {})); + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram", {}); // default is LocalOnly + ASSERT_TRUE(nullptr != m_sceneLoaded); + EXPECT_EQ(EScenePublicationMode::LocalOnly, m_sceneLoaded->impl().getPublicationModeSetFromSceneConfig()); + } + + TEST_F(ASceneLoadedFromFile, reportsErrorWhenSavingSceneToFileWithInvalidFileName) + { + EXPECT_FALSE(m_scene.saveToFile("?Euler_ZYX:/dummyFile", {})); + } + + TEST_F(ASceneLoadedFromFile, reportsErrorWhenSavingSceneToFileWithNoFileName) + { + EXPECT_FALSE(m_scene.saveToFile({}, {})); + } + + TEST_F(ASceneLoadedFromFile, overwritesExistingFileWhenSavingSceneToIt) + { + { + ramses::internal::File existingFile("dummyFile.dat"); + existingFile.createFile(); + } + + EXPECT_TRUE(m_scene.saveToFile("dummyFile.dat", {})); + { + ramses::internal::File fileShouldBeOverwritten("dummyFile.dat"); + EXPECT_TRUE(fileShouldBeOverwritten.open(ramses::internal::File::Mode::ReadOnly)); + size_t fileSize = 0; + EXPECT_TRUE(fileShouldBeOverwritten.getSizeInBytes(fileSize)); + EXPECT_NE(0u, fileSize); + } + + EXPECT_TRUE(ramses::internal::File("dummyFile.dat").remove()); + } + + TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromFileWithInvalidFileName) + { + ramses::Scene* scene = client.loadSceneFromFile("?Euler_ZYX:/dummyFile"); + EXPECT_TRUE(nullptr == scene); + } + + TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromFileWithoutFileName) + { + EXPECT_EQ(nullptr, client.loadSceneFromFile({})); + EXPECT_EQ(nullptr, client.loadSceneFromFile("")); + } + + TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromInvalidMemory) + { + auto deleter = [](const auto* ptr) { delete[] ptr; }; + EXPECT_EQ(nullptr, client.loadSceneFromMemory(std::unique_ptr(nullptr, deleter), 1, {})); + EXPECT_EQ(nullptr, client.loadSceneFromMemory(std::unique_ptr(new std::byte[1], deleter), 0, {})); + + EXPECT_EQ(nullptr, RamsesUtils::LoadSceneFromMemory(client, std::unique_ptr(nullptr), 1, {})); + EXPECT_EQ(nullptr, RamsesUtils::LoadSceneFromMemory(client, std::unique_ptr(new std::byte[1]), 0, {})); + } + + TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromInvalidFileDescriptor) + { + EXPECT_EQ(nullptr, client.loadSceneFromFileDescriptor(-1, 0, 1, {})); + EXPECT_EQ(nullptr, client.loadSceneFromFileDescriptor(1, 0, 0, {})); + } + + TEST_F(ASceneLoadedFromFile, doesNotLoadSceneFromUnexistingFile) + { + ramses::Scene* scene = client.loadSceneFromFile("ZEGETWTWAGTGSDGEg_thisfilename_in_this_directory_should_not_exist_DSAFDSFSTEZHDXHB"); + EXPECT_TRUE(nullptr == scene); + } + + TEST_F(ASceneLoadedFromFile, canHandleAllZeroFileOnSceneLoad) + { + const char* filename = "allzerofile.dat"; + { + ramses::internal::File file(filename); + EXPECT_TRUE(file.open(ramses::internal::File::Mode::WriteNew)); + std::vector zerovector(4096); + EXPECT_TRUE(file.write(&zerovector[0], zerovector.size())); + file.close(); + } + + ramses::Scene* scene = client.loadSceneFromFile(filename); + EXPECT_TRUE(scene == nullptr); + } + + TEST_F(ASceneLoadedFromFile, cannotLoadSameFileTwice) + { + const sceneId_t sceneId = ramses::sceneId_t(1ULL << 63); + ramses::Scene* scene = client.createScene(SceneConfig(sceneId)); + EXPECT_TRUE(scene->saveToFile("someTempararyFile.ram", {})); + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); + EXPECT_NE(nullptr, m_sceneLoaded); + + EXPECT_EQ(nullptr, m_clientForLoading.loadSceneFromFile("someTempararyFile.ram")); + } + + TEST_F(ASceneLoadedFromFile, cannotLoadScenesWithSameSceneIdTwice) + { + const sceneId_t sceneId = ramses::sceneId_t(1ULL << 63); + + { + ramses::Scene* scene = client.createScene(SceneConfig(sceneId)); + EXPECT_TRUE(scene->saveToFile("someTempararyFile.ram", {})); + client.destroy(*scene); + } + { + ramses::Scene* scene = client.createScene(SceneConfig(sceneId)); + EXPECT_TRUE(scene->saveToFile("someTempararyFile_2.ram", {})); + client.destroy(*scene); + } + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTempararyFile.ram"); + EXPECT_NE(nullptr, m_sceneLoaded); + + EXPECT_EQ(nullptr, m_clientForLoading.loadSceneFromFile("someTempararyFile_2.ram")); + } + + TEST_F(ASceneLoadedFromFile, cannotLoadSceneWithMismatchingFeatureLevel) + { + SaveSceneWithFeatureLevelToFile(EFeatureLevel(99), "someTemporaryFile.ram"); + EXPECT_EQ(nullptr, m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram")); + } + + TEST_F(ASceneLoadedFromFile, canGetFeatureLevelFromSceneFile) + { + SaveSceneWithFeatureLevelToFile(EFeatureLevel_Latest, "someTemporaryFile.ram"); + + EFeatureLevel featureLevel = EFeatureLevel_01; + EXPECT_TRUE(RamsesClient::GetFeatureLevelFromFile("someTemporaryFile.ram", featureLevel)); + EXPECT_EQ(EFeatureLevel_Latest, featureLevel); + } + + TEST_F(ASceneLoadedFromFile, failsToGetFeatureLevelFromFileWithUnknownFeatureLevel) + { + SaveSceneWithFeatureLevelToFile(EFeatureLevel(99), "someTemporaryFile.ram"); + + EFeatureLevel featureLevel = EFeatureLevel_01; + EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile("someTemporaryFile.ram", featureLevel)); + } + + TEST_F(ASceneLoadedFromFile, failsToGetFeatureLevelFromNonexistingFile) + { + EFeatureLevel featureLevel = EFeatureLevel_01; + EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile("doesnt.Exist", featureLevel)); + } + + TEST_F(ASceneLoadedFromFile, canGetFeatureLevelFromSceneFileViaFileDescriptor) + { + SaveSceneWithFeatureLevelToFile(EFeatureLevel_Latest, "someTemporaryFile.ram"); + + size_t fileSize = 0; + { + // write to a file with some offset + ramses::internal::File inFile("someTemporaryFile.ram"); + EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); + std::vector data(fileSize); + size_t numBytesRead = 0; + EXPECT_TRUE(inFile.open(ramses::internal::File::Mode::ReadOnlyBinary)); + EXPECT_EQ(ramses::internal::EStatus::Ok, inFile.read(data.data(), fileSize, numBytesRead)); + + ramses::internal::File outFile("someTemporaryFileWithOffset.ram"); + EXPECT_TRUE(outFile.open(ramses::internal::File::Mode::WriteOverWriteOldBinary)); + + uint32_t zeroData = 0; + EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); + EXPECT_TRUE(outFile.write(data.data(), data.size())); + EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); + } + const int fd = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary("someTemporaryFileWithOffset.ram"); + + EFeatureLevel featureLevel = EFeatureLevel_01; + EXPECT_TRUE(RamsesClient::GetFeatureLevelFromFile(fd, 4u, fileSize, featureLevel)); + EXPECT_EQ(EFeatureLevel_Latest, featureLevel); + } + + TEST_F(ASceneLoadedFromFile, failsToGetFeatureLevelFromInvalidFileDescriptor) + { + EFeatureLevel featureLevel = EFeatureLevel_01; + EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile(-1, 0u, 10u, featureLevel)); + EXPECT_FALSE(RamsesClient::GetFeatureLevelFromFile(1, 0u, 0u, featureLevel)); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteTransformDataSlot) + { + Node* node = this->m_scene.createNode("node"); + + EXPECT_TRUE(this->m_scene.impl().createTransformationDataConsumer(*node, dataConsumerId_t(2u))); + ASSERT_EQ(1u, this->m_scene.impl().getIScene().getDataSlotCount()); + + ramses::internal::DataSlotHandle slotHandle(0u); + EXPECT_TRUE(this->m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + + doWriteReadCycle(); + + const Node* nodeLoaded = this->getObjectForTesting("node"); + ramses::internal::NodeHandle nodeHandle = nodeLoaded->impl().getNodeHandle(); + ASSERT_EQ(1u, this->m_sceneLoaded->impl().getIScene().getDataSlotCount()); + + EXPECT_TRUE(this->m_sceneLoaded->impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(nodeHandle, this->m_sceneLoaded->impl().getIScene().getDataSlot(slotHandle).attachedNode); + EXPECT_EQ(ramses::internal::DataSlotId(2u), this->m_sceneLoaded->impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TransformationConsumer, this->m_sceneLoaded->impl().getIScene().getDataSlot(slotHandle).type); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteDataObject) + { + float setValue = 5.0f; + auto data = this->m_scene.createDataObject(ramses::EDataType::Float, "floatData"); + + EXPECT_TRUE(data->setValue(setValue)); + + doWriteReadCycle(); + + const auto loadedData = this->getObjectForTesting("floatData"); + ASSERT_TRUE(loadedData); + float loadedValue = 0.0f; + EXPECT_EQ(ramses::EDataType::Float, loadedData->getDataType()); + EXPECT_TRUE(loadedData->getValue(loadedValue)); + EXPECT_EQ(setValue, loadedValue); + } + + TEST_F(ASceneLoadedFromFile, canReadWriteSceneReferences) + { + constexpr ramses::sceneId_t referencedSceneId(444); + auto sr1 = this->m_scene.createSceneReference(referencedSceneId, "scene ref"); + sr1->requestState(RendererSceneState::Ready); + + constexpr ramses::sceneId_t referencedSceneId2(555); + auto sr2 = this->m_scene.createSceneReference(referencedSceneId2, "scene ref2"); + sr2->requestState(RendererSceneState::Rendered); + + doWriteReadCycle(); + + const ramses::SceneReference* loadedSceneRef = this->getObjectForTesting("scene ref"); + ASSERT_TRUE(loadedSceneRef); + EXPECT_EQ(referencedSceneId, loadedSceneRef->getReferencedSceneId()); + EXPECT_EQ(ramses::RendererSceneState::Ready, loadedSceneRef->getRequestedState()); + + const ramses::SceneReference* loadedSceneRef2 = this->getObjectForTesting("scene ref2"); + ASSERT_TRUE(loadedSceneRef2); + EXPECT_EQ(referencedSceneId2, loadedSceneRef2->getReferencedSceneId()); + EXPECT_EQ(ramses::RendererSceneState::Rendered, loadedSceneRef2->getRequestedState()); + } + + TEST_F(ASceneLoadedFromFile, savesLLResourceOnlyOnceIfTwoHLResourcesReferToIt) + { + std::vector inds(300); + std::iota(inds.begin(), inds.end(), static_cast(0u)); + this->m_scene.createArrayResource(300u, inds.data(), "indices"); + this->m_scene.createArrayResource(300u, inds.data(), "indices"); + this->m_scene.createArrayResource(300u, inds.data(), "indices"); + this->m_scene.createArrayResource(300u, inds.data(), "indices2"); + this->m_scene.createArrayResource(300u, inds.data(), "indices2"); + this->m_scene.createArrayResource(300u, inds.data(), "indices2"); + + doWriteReadCycle(); + std::ifstream in("someTemporaryFile.ram", std::ifstream::ate | std::ifstream::binary); + auto size = in.tellg(); + EXPECT_GT(1600, size) << "scene file size exceeds allowed max size. verify that LL resource is saved only once before adapting this number"; + } + + template + struct TestHelper + { + static T* create(ASceneLoadedFromFileTemplated* fixture, ramses::RamsesClient& /*unused*/, ramses::Scene& /*unused*/) + { + return &fixture->template createObject("a node"); + } + }; + + TYPED_TEST_SUITE(ASceneLoadedFromFileTemplated, NodeTypes); + TYPED_TEST(ASceneLoadedFromFileTemplated, canReadWriteAllNodes) + { + auto node = TestHelper::create(this, this->client, this->m_scene); + + node->setVisibility(EVisibilityMode::Invisible); + + auto child = &this->template createObject("child"); + auto parent = &this->template createObject("parent"); + + node->setTranslation({1, 2, 3}); + node->setRotation({4, 5, 6}, ERotationType::Euler_XZX); + node->setScaling({7, 8, 9}); + node->addChild(*child); + node->setParent(*parent); + + this->m_scene.flush(); + + this->doWriteReadCycle(); + + const auto loadedSuperNode = this->template getObjectForTesting("a node"); + const auto loadedChild = this->template getObjectForTesting("child"); + const auto loadedParent = this->template getObjectForTesting("parent"); + + ASSERT_TRUE(nullptr != loadedSuperNode); + ASSERT_TRUE(nullptr != loadedChild); + ASSERT_TRUE(nullptr != loadedParent); + + ASSERT_EQ(1u, loadedSuperNode->getChildCount()); + EXPECT_EQ(loadedChild, loadedSuperNode->getChild(0u)); + EXPECT_EQ(loadedParent, loadedSuperNode->getParent()); + vec3f value; + EXPECT_TRUE(loadedSuperNode->getTranslation(value)); + EXPECT_FLOAT_EQ(1, value.x); + EXPECT_FLOAT_EQ(2, value.y); + EXPECT_FLOAT_EQ(3, value.z); + EXPECT_TRUE(loadedSuperNode->getRotation(value)); + EXPECT_FLOAT_EQ(4, value.x); + EXPECT_FLOAT_EQ(5, value.y); + EXPECT_FLOAT_EQ(6, value.z); + EXPECT_EQ(ERotationType::Euler_XZX, loadedSuperNode->getRotationType()); + EXPECT_TRUE(loadedSuperNode->getScaling(value)); + EXPECT_FLOAT_EQ(7, value.x); + EXPECT_FLOAT_EQ(8, value.y); + EXPECT_FLOAT_EQ(9, value.z); + + EXPECT_EQ(loadedSuperNode->getVisibility(), EVisibilityMode::Invisible); + } + + TEST_F(ASceneLoadedFromFile, compressedFileIsSmallerThanUncompressedWhenUsingSaveSceneToFile) + { + ramses::Scene* scene = client.createScene(SceneConfig(sceneId_t(1))); + const std::vector data(1000u, 0u); + EXPECT_TRUE(scene->createArrayResource(static_cast(data.size()), data.data())); + + SaveFileConfig saveConfig; + saveConfig.setCompressionEnabled(false); + EXPECT_TRUE(scene->saveToFile("testscene.ramscene", saveConfig)); + + ramses::internal::File file("testscene.ramscene"); + EXPECT_TRUE(file.exists()); + size_t uncompressedFileSize = 0; + EXPECT_TRUE(file.getSizeInBytes(uncompressedFileSize)); + + saveConfig.setCompressionEnabled(true); + EXPECT_TRUE(scene->saveToFile("testscene.ramscene", saveConfig)); + + ramses::internal::File file2("testscene.ramscene"); + EXPECT_TRUE(file2.exists()); + size_t compressedFileSize = 0; + EXPECT_TRUE(file2.getSizeInBytes(compressedFileSize)); + + EXPECT_GT(uncompressedFileSize, compressedFileSize); + } + + TEST_F(ASceneLoadedFromFile, savedFilesAreConsistent) + { + for (const auto& name : { "ts1.ramscene", "ts2.ramscene", "ts3.ramscene", "ts4.ramscene", "ts5.ramscene", "ts6.ramscene" }) + { + EXPECT_TRUE(this->m_scene.saveToFile(name, {})); + } + + for (const auto& name : { "ts2.ramscene", "ts3.ramscene", "ts4.ramscene", "ts5.ramscene", "ts6.ramscene" }) + { + EXPECT_TRUE(ClientTestUtils::CompareBinaryFiles("ts1.ramscene", name)); + } + } + + TEST_F(ASceneLoadedFromFile, closesSceneFileAndLowLevelResourceWhenDestroyed) + { + EXPECT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", {})); + + m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram", {}); + ASSERT_TRUE(nullptr != m_sceneLoaded); + + const ramses::internal::SceneFileHandle handle = m_sceneLoaded->impl().getSceneFileHandle(); + EXPECT_TRUE(m_clientForLoading.impl().getClientApplication().hasResourceFile(handle)); + m_clientForLoading.destroy(*m_sceneLoaded); + + // scene gets destroyed asynchronously, so we can't just test after the destroy + // unfortunately there is no callback, but I don't want to skip the test + // => wait for it to happen in finite time, we don't test for performance here + uint32_t ticks = 60000u; + for (; ticks > 0; --ticks) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (!m_clientForLoading.impl().getClientApplication().hasResourceFile(handle)) + break; + } + EXPECT_GT(ticks, 0u); + } +} diff --git a/client/test/ScenePersistationTest.h b/tests/unittests/client/ScenePersistationTest.h similarity index 55% rename from client/test/ScenePersistationTest.h rename to tests/unittests/client/ScenePersistationTest.h index 0b1fea252..f50b6fc8c 100644 --- a/client/test/ScenePersistationTest.h +++ b/tests/unittests/client/ScenePersistationTest.h @@ -6,35 +6,33 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEPERSISTATIONTEST_H -#define RAMSES_SCENEPERSISTATIONTEST_H +#pragma once #include "gtest/gtest.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" #include "ClientTestUtils.h" -#include "SceneImpl.h" -#include "RamsesObjectTypeUtils.h" -#include "RamsesClientImpl.h" -#include "RamsesFrameworkConfigImpl.h" +#include "impl/SceneImpl.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "impl/SaveFileConfigImpl.h" #include -namespace ramses +namespace ramses::internal { class ASceneLoadedFromFile : public LocalTestClientWithScene, public ::testing::Test { public: ASceneLoadedFromFile() - : LocalTestClientWithScene() - , m_clientForLoading(*m_frameworkForLoader.createClient("client")) - , m_sceneLoaded(nullptr) + : m_clientForLoading(*m_frameworkForLoader.createClient("client")) { - m_frameworkForLoader.m_impl.getScenegraphComponent().setSceneRendererHandler(&sceneActionsCollector); + m_frameworkForLoader.impl().getScenegraphComponent().setSceneRendererHandler(&sceneActionsCollector); } - using ObjectTypeHistogram = ramses_internal::HashMap< ERamsesObjectType, uint32_t >; + using ObjectTypeHistogram = ramses::internal::HashMap< ERamsesObjectType, uint32_t >; - void fillObjectTypeHistorgramFromObjectVector(ObjectTypeHistogram& counter, const RamsesObjectVector& objects) + static void FillObjectTypeHistorgramFromObjectVector(ObjectTypeHistogram& counter, const SceneObjectVector& objects) { for (const auto obj : objects) { @@ -52,14 +50,14 @@ namespace ramses } - void fillObjectTypeHistogramFromScene( ObjectTypeHistogram& counter, const ramses::Scene& scene ) + static void FillObjectTypeHistogramFromScene( ObjectTypeHistogram& counter, const ramses::Scene& scene ) { - RamsesObjectVector objects; - scene.m_impl.getObjectRegistry().getObjectsOfType(objects, ERamsesObjectType::SceneObject); - fillObjectTypeHistorgramFromObjectVector( counter, objects ); + SceneObjectVector objects; + scene.impl().getObjectRegistry().getObjectsOfType(objects, ERamsesObjectType::SceneObject); + FillObjectTypeHistorgramFromObjectVector( counter, objects ); } - ::testing::AssertionResult AssertHistogramEqual( std::string_view m_expr, std::string_view n_expr, const ObjectTypeHistogram& m, const ObjectTypeHistogram& n ) + static ::testing::AssertionResult AssertHistogramEqual( std::string_view m_expr, std::string_view n_expr, const ObjectTypeHistogram& m, const ObjectTypeHistogram& n ) { bool isEqual = true; ::testing::AssertionResult wrongHistogramCount = ::testing::AssertionFailure(); @@ -71,24 +69,24 @@ namespace ramses << " have different sizes " << m.size() << " and " << n.size() << "\n"; wrongHistogramCount << m_expr << ":\n"; - for(ObjectTypeHistogram::ConstIterator iter = m.begin(); iter != m.end(); ++iter) + for(auto iter : m) { - wrongHistogramCount << "Type " << RamsesObjectTypeUtils::GetRamsesObjectTypeName( iter->key ) << ":\t" - << iter->value << "\n"; + wrongHistogramCount << "Type " << RamsesObjectTypeUtils::GetRamsesObjectTypeName( iter.key ) << ":\t" + << iter.value << "\n"; } wrongHistogramCount << n_expr << ":\n"; - for(ObjectTypeHistogram::ConstIterator iter = n.begin(); iter != n.end(); ++iter) + for(auto iter : n) { - wrongHistogramCount << "Type " << RamsesObjectTypeUtils::GetRamsesObjectTypeName( iter->key ) << ":\t" - << iter->value << "\n"; + wrongHistogramCount << "Type " << RamsesObjectTypeUtils::GetRamsesObjectTypeName( iter.key ) << ":\t" + << iter.value << "\n"; } isEqual = false; } else { - for(ObjectTypeHistogram::ConstIterator iter = m.begin(); iter != m.end(); ++iter) + for(auto iter = m.begin(); iter != m.end(); ++iter) { if(*m.get(iter->key) != *n.get(iter->key)) { @@ -100,20 +98,41 @@ namespace ramses } } - if( isEqual ) + if (isEqual) { return ::testing::AssertionSuccess(); } - else - { - return wrongHistogramCount; - } + return wrongHistogramCount; + } + + void checkSceneFile(const char* filename) + { + std::vector buffer; + SaveFileConfigImpl config; + config.setValidationEnabled(false); + EXPECT_TRUE(m_scene.impl().serialize(buffer, config)); + EXPECT_FALSE(buffer.empty()); + + ramses::internal::File f(filename); + EXPECT_TRUE(f.open(ramses::internal::File::Mode::ReadOnlyBinary)); + size_t numBytes = 0u; + EXPECT_TRUE(f.getSizeInBytes(numBytes)); + EXPECT_EQ(numBytes, buffer.size()); + std::vector fileBuffer; + fileBuffer.resize(numBytes); + EXPECT_EQ(ramses::internal::EStatus::Ok, f.read(fileBuffer.data(), numBytes, numBytes)); + EXPECT_TRUE(f.close()); + EXPECT_EQ(buffer, fileBuffer); } - void doWriteReadCycle(bool expectSameSceneSizeInfo = true, bool expectSameTypeHistogram = true) + void doWriteReadCycle(bool expectSameSceneSizeInfo = true, bool expectSameTypeHistogram = true, bool withCompression = false) { - const status_t status = m_scene.saveToFile("someTemporaryFile.ram", false); - EXPECT_EQ(StatusOK, status); + SaveFileConfig config; + config.setCompressionEnabled(withCompression); + config.setValidationEnabled(false); + ASSERT_TRUE(m_scene.saveToFile("someTemporaryFile.ram", config)); + + checkSceneFile("someTemporaryFile.ram"); m_sceneLoaded = m_clientForLoading.loadSceneFromFile("someTemporaryFile.ram"); ASSERT_TRUE(nullptr != m_sceneLoaded); @@ -122,50 +141,51 @@ namespace ramses { ObjectTypeHistogram origSceneNumbers; ObjectTypeHistogram loadedSceneNumbers; - fillObjectTypeHistogramFromScene(origSceneNumbers, m_scene); - fillObjectTypeHistogramFromScene(loadedSceneNumbers, *m_sceneLoaded); + FillObjectTypeHistogramFromScene(origSceneNumbers, m_scene); + FillObjectTypeHistogramFromScene(loadedSceneNumbers, *m_sceneLoaded); EXPECT_PRED_FORMAT2(AssertHistogramEqual, origSceneNumbers, loadedSceneNumbers); } // this can not be enabled for text serialization, because those are destroying/creating meshes if (expectSameSceneSizeInfo) { - ramses_internal::SceneSizeInformation origSceneSizeInfo = m_scene.m_impl.getIScene().getSceneSizeInformation(); - ramses_internal::SceneSizeInformation loadedSceneSizeInfo = m_sceneLoaded->m_impl.getIScene().getSceneSizeInformation(); + ramses::internal::SceneSizeInformation origSceneSizeInfo = m_scene.impl().getIScene().getSceneSizeInformation(); + ramses::internal::SceneSizeInformation loadedSceneSizeInfo = m_sceneLoaded->impl().getIScene().getSceneSizeInformation(); EXPECT_EQ(origSceneSizeInfo, loadedSceneSizeInfo); } } - void saveSceneWithFeatureLevelToFile(EFeatureLevel featureLevel, std::string_view fileName) + static void SaveSceneWithFeatureLevelToFile(EFeatureLevel featureLevel, std::string_view fileName) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; - config.m_impl.get().setFeatureLevelNoCheck(featureLevel); + config.impl().setFeatureLevelNoCheck(featureLevel); RamsesFramework someFramework{ config }; + EXPECT_EQ(featureLevel, someFramework.getFeatureLevel()); RamsesClient* someClient = someFramework.createClient("someClient"); ramses::Scene* scene = someClient->createScene(sceneId_t{ 123u }); - const status_t status = scene->saveToFile(fileName, false); - EXPECT_EQ(StatusOK, status); + EXPECT_EQ(featureLevel, scene->getRamsesClient().getRamsesFramework().getFeatureLevel()); + EXPECT_TRUE(scene->saveToFile(fileName, {})); } template T* getObjectForTesting(std::string_view name) { - RamsesObject* objectPerName = this->m_sceneLoaded->findObjectByName(name); + RamsesObject* objectPerName = this->m_sceneLoaded->findObject(name); EXPECT_TRUE(objectPerName != nullptr); if (!objectPerName) return nullptr; - EXPECT_STREQ(std::string{name}.c_str(), objectPerName->getName()); + EXPECT_EQ(name, objectPerName->getName()); - T* specificObject = RamsesUtils::TryConvert(*objectPerName); + T* specificObject = objectPerName->as(); EXPECT_TRUE(nullptr != specificObject); return specificObject; } ramses::RamsesFramework m_frameworkForLoader{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; ramses::RamsesClient& m_clientForLoading; - ramses::Scene* m_sceneLoaded; + ramses::Scene* m_sceneLoaded{nullptr}; }; class ASceneLoadedFromFileWithDefaultRenderPass : public ASceneLoadedFromFile @@ -178,5 +198,3 @@ namespace ramses }; } - -#endif diff --git a/client/test/ScenePersistationThreadedTest.cpp b/tests/unittests/client/ScenePersistationThreadedTest.cpp similarity index 71% rename from client/test/ScenePersistationThreadedTest.cpp rename to tests/unittests/client/ScenePersistationThreadedTest.cpp index 961896eff..9e677a183 100644 --- a/client/test/ScenePersistationThreadedTest.cpp +++ b/tests/unittests/client/ScenePersistationThreadedTest.cpp @@ -8,31 +8,30 @@ #include "gmock/gmock.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/ArrayResource.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Effect.h" -#include "ramses-utils.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/ArrayResource.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Effect.h" +#include "ramses/client/ramses-utils.h" #include "ClientEventHandlerMock.h" #include "TestEffects.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Utils/File.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/Utils/File.h" #include -namespace ramses +namespace ramses::internal { using namespace testing; - using namespace ramses_internal; namespace { @@ -47,19 +46,21 @@ namespace ramses Scene* scene = CreateScene(client, sceneId); ASSERT_TRUE(scene != nullptr); - ASSERT_EQ(StatusOK, scene->saveToFile(sceneFilename, false)); + SaveFileConfig config; + config.setValidationEnabled(false); + ASSERT_TRUE(scene->saveToFile(sceneFilename, config)); } private: static Scene* CreateScene(RamsesClient& client, sceneId_t sceneId) { - Scene* scene = client.createScene(sceneId, SceneConfig()); + Scene* scene = client.createScene(sceneId); PerspectiveCamera* camera = scene->createPerspectiveCamera("my cam"); camera->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); camera->setViewport(1, 2, 3, 4); - RenderGroup* renderGroup = scene->createRenderGroup("a rendergroup"); + ramses::RenderGroup* renderGroup = scene->createRenderGroup("a rendergroup"); renderGroup->addMeshNode(*scene->createMeshNode(), 3); //appearance @@ -68,11 +69,11 @@ namespace ramses // geometry binding static const uint16_t inds[3] = { 0, 1, 2 }; - ArrayResource* const indices = scene->createArrayResource(3u, inds, ramses::ResourceCacheFlag_DoNotCache, "indices"); + ArrayResource* const indices = scene->createArrayResource(3u, inds, "indices"); - GeometryBinding* geometry = scene->createGeometryBinding(*effect, "geometry"); + Geometry* geometry = scene->createGeometry(*effect, "geometry"); assert(geometry != nullptr); - EXPECT_EQ(StatusOK, geometry->setIndices(*indices)); + EXPECT_TRUE(geometry->setIndices(*indices)); //mesh node scene->createMeshNode("a meshnode"); @@ -90,8 +91,6 @@ namespace ramses public: ARamsesFileLoadedInSeveralThread() : client(*framework.createClient("ARamsesFileLoadedInSeveralThread")) - , numClientEvents(0) - , loadedScene(nullptr) { ON_CALL(eventHandler, sceneFileLoadSucceeded(_, _)).WillByDefault(DoAll(SaveArg<1>(&loadedScene), InvokeWithoutArgs(this, &ARamsesFileLoadedInSeveralThread::incNumClientEvents))); ON_CALL(eventHandler, sceneFileLoadFailed(_)).WillByDefault(InvokeWithoutArgs(this, &ARamsesFileLoadedInSeveralThread::incNumClientEvents)); @@ -119,17 +118,17 @@ namespace ramses RamsesFramework framework{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; RamsesClient& client; - StrictMock eventHandler; - int numClientEvents; - Scene* loadedScene; + StrictMock eventHandler{}; + int numClientEvents{0}; + Scene* loadedScene{nullptr}; - static void SetUpTestCase() + static void SetUpTestSuite() { RamsesFileCreator::SaveSceneToFile(sceneId_t(123u), sceneFile); RamsesFileCreator::SaveSceneToFile(sceneId_t(124u), otherSceneFile); } - static void TearDownTestCase() + static void TearDownTestSuite() { File(sceneFile).remove(); File(otherSceneFile).remove(); @@ -139,16 +138,17 @@ namespace ramses TEST_F(ARamsesFileLoadedInSeveralThread, canAsyncLoadSceneFile) { EXPECT_CALL(eventHandler, sceneFileLoadSucceeded(StrEq(sceneFile), _)); - EXPECT_EQ(StatusOK, client.loadSceneFromFileAsync(sceneFile)); + EXPECT_TRUE(client.loadSceneFromFileAsync(sceneFile)); ASSERT_TRUE(waitForNumClientEvents(1)); ASSERT_TRUE(loadedScene != nullptr); EXPECT_EQ(sceneId_t(123u), loadedScene->getSceneId()); } - TEST_F(ARamsesFileLoadedInSeveralThread, asyncLoadSceneFileWithoutEverCallingDispatchDoesNotLeakMemory) + // TODO needs to reenabled when the issue is fixed: ABPI-401386 + TEST_F(ARamsesFileLoadedInSeveralThread, DISABLED_asyncLoadSceneFileWithoutEverCallingDispatchDoesNotLeakMemory) { - EXPECT_EQ(StatusOK, client.loadSceneFromFileAsync(sceneFile)); + EXPECT_TRUE(client.loadSceneFromFileAsync(sceneFile)); // do nothing, scene load will finish before RamsesClient is destructed } } diff --git a/client/test/SceneReferenceTest.cpp b/tests/unittests/client/SceneReferenceTest.cpp similarity index 55% rename from client/test/SceneReferenceTest.cpp rename to tests/unittests/client/SceneReferenceTest.cpp index a29bcefa8..7aed77fb2 100644 --- a/client/test/SceneReferenceTest.cpp +++ b/tests/unittests/client/SceneReferenceTest.cpp @@ -10,12 +10,12 @@ #include "ClientTestUtils.h" #include "CreationHelper.h" -#include "ramses-client-api/SceneReference.h" -#include "SceneReferenceImpl.h" +#include "ramses/client/SceneReference.h" +#include "impl/SceneReferenceImpl.h" using namespace testing; -namespace ramses +namespace ramses::internal { class ASceneReference : public LocalTestClientWithScene, public ::testing::Test { @@ -23,19 +23,19 @@ namespace ramses TEST_F(ASceneReference, createSceneReference) { - SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444),"testSceneReference"); + ramses::SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444),"testSceneReference"); ASSERT_NE(nullptr, sceneReference); - const auto sceneRefHandle = sceneReference->m_impl.getSceneReferenceHandle(); + const auto sceneRefHandle = sceneReference->impl().getSceneReferenceHandle(); EXPECT_TRUE(sceneRefHandle.isValid()); ASSERT_TRUE(this->getInternalScene().isSceneReferenceAllocated(sceneRefHandle)); const auto& sceneRef = this->getInternalScene().getSceneReference(sceneRefHandle); EXPECT_EQ(444u, sceneRef.sceneId.getValue()); - EXPECT_EQ(ramses_internal::RendererSceneState::Available, sceneRef.requestedState); + EXPECT_EQ(RendererSceneState::Available, sceneRef.requestedState); EXPECT_EQ(0, sceneRef.renderOrder); EXPECT_FALSE(sceneRef.flushNotifications); - EXPECT_EQ(StatusOK, this->m_scene.destroy(*sceneReference)); + EXPECT_TRUE(this->m_scene.destroy(*sceneReference)); EXPECT_FALSE(this->getInternalScene().isSceneReferenceAllocated(sceneRefHandle)); } @@ -64,25 +64,25 @@ namespace ramses TEST_F(ASceneReference, canGetReferencedSceneSceneId) { - SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444), "testSceneReference"); + ramses::SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444), "testSceneReference"); ASSERT_EQ(sceneId_t(444), sceneReference->getReferencedSceneId()); } TEST_F(ASceneReference, getsInitialRequestedSceneState) { - SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444), "testSceneReference"); + ramses::SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444), "testSceneReference"); ASSERT_EQ(RendererSceneState::Available, sceneReference->getRequestedState()); } TEST_F(ASceneReference, canGetRequestedSceneState) { - SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444), "testSceneReference"); + ramses::SceneReference* sceneReference = this->m_scene.createSceneReference(sceneId_t(444), "testSceneReference"); sceneReference->requestState(RendererSceneState::Ready); ASSERT_EQ(RendererSceneState::Ready, sceneReference->getRequestedState()); - const auto sceneRefHandle = sceneReference->m_impl.getSceneReferenceHandle(); + const auto sceneRefHandle = sceneReference->impl().getSceneReferenceHandle(); ASSERT_TRUE(this->getInternalScene().isSceneReferenceAllocated(sceneRefHandle)); - EXPECT_EQ(ramses_internal::RendererSceneState::Ready, this->getInternalScene().getSceneReference(sceneRefHandle).requestedState); + EXPECT_EQ(RendererSceneState::Ready, this->getInternalScene().getSceneReference(sceneRefHandle).requestedState); } TEST_F(ASceneReference, rejectsLinkDataIfSceneReferencesAreInWrongState) @@ -90,36 +90,36 @@ namespace ramses auto reference1 = m_scene.createSceneReference(sceneId_t(111)); auto reference2 = m_scene.createSceneReference(sceneId_t(222)); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(nullptr, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(nullptr, dataProviderId_t(1), reference2, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference2, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(nullptr, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(nullptr, dataProviderId_t(1), reference2, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference2, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); - reference1->m_impl.setReportedState(RendererSceneState::Available); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + reference1->impl().setReportedState(RendererSceneState::Available); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - reference1->m_impl.setReportedState(RendererSceneState::Ready); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + reference1->impl().setReportedState(RendererSceneState::Ready); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - reference1->m_impl.setReportedState(RendererSceneState::Rendered); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + reference1->impl().setReportedState(RendererSceneState::Rendered); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference2, dataProviderId_t(1), reference1, dataConsumerId_t(2))); } TEST_F(ASceneReference, acceptsValidLinkDataParameters) { auto reference1 = m_scene.createSceneReference(sceneId_t(111)); auto reference2 = m_scene.createSceneReference(sceneId_t(222)); - reference1->m_impl.setReportedState(RendererSceneState::Ready); - reference2->m_impl.setReportedState(RendererSceneState::Rendered); + reference1->impl().setReportedState(RendererSceneState::Ready); + reference2->impl().setReportedState(RendererSceneState::Rendered); - EXPECT_EQ(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); - EXPECT_EQ(StatusOK, m_scene.linkData(nullptr, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - EXPECT_EQ(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); + EXPECT_TRUE(m_scene.linkData(reference1, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); + EXPECT_TRUE(m_scene.linkData(nullptr, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + EXPECT_TRUE(m_scene.linkData(reference1, dataProviderId_t(1), reference2, dataConsumerId_t(2))); } TEST_F(ASceneReference, rejectsInvalidLinkDataParameters) @@ -130,31 +130,31 @@ namespace ramses auto reference1 = m_scene.createSceneReference(sceneId_t(333)); - reference1->m_impl.setReportedState(RendererSceneState::Ready); - referenceFromOtherScene1->m_impl.setReportedState(RendererSceneState::Rendered); - referenceFromOtherScene2->m_impl.setReportedState(RendererSceneState::Rendered); + reference1->impl().setReportedState(RendererSceneState::Ready); + referenceFromOtherScene1->impl().setReportedState(RendererSceneState::Rendered); + referenceFromOtherScene2->impl().setReportedState(RendererSceneState::Rendered); - EXPECT_NE(StatusOK, m_scene.linkData(nullptr, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(nullptr, dataProviderId_t(1), referenceFromOtherScene1, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(reference1, dataProviderId_t(1), referenceFromOtherScene1, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(referenceFromOtherScene1, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(referenceFromOtherScene1, dataProviderId_t(1), reference1, dataConsumerId_t(2))); - EXPECT_NE(StatusOK, m_scene.linkData(referenceFromOtherScene1, dataProviderId_t(1), referenceFromOtherScene2, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(nullptr, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(nullptr, dataProviderId_t(1), referenceFromOtherScene1, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(reference1, dataProviderId_t(1), referenceFromOtherScene1, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(referenceFromOtherScene1, dataProviderId_t(1), nullptr, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(referenceFromOtherScene1, dataProviderId_t(1), reference1, dataConsumerId_t(2))); + EXPECT_FALSE(m_scene.linkData(referenceFromOtherScene1, dataProviderId_t(1), referenceFromOtherScene2, dataConsumerId_t(2))); } TEST_F(ASceneReference, rejectsInvalidUnlinkDataParameters) { auto otherScene = getClient().createScene(sceneId_t(555)); auto referenceFromOtherScene = otherScene->createSceneReference(sceneId_t(111)); - EXPECT_NE(StatusOK, m_scene.unlinkData(referenceFromOtherScene, dataConsumerId_t(1))); + EXPECT_FALSE(m_scene.unlinkData(referenceFromOtherScene, dataConsumerId_t(1))); } TEST_F(ASceneReference, canRequestAndUnrequestVersionNotifications) { auto reference = m_scene.createSceneReference(sceneId_t(111)); - EXPECT_EQ(StatusOK, reference->requestNotificationsForSceneVersionTags(true)); - EXPECT_EQ(StatusOK, reference->requestNotificationsForSceneVersionTags(false)); + EXPECT_TRUE(reference->requestNotificationsForSceneVersionTags(true)); + EXPECT_TRUE(reference->requestNotificationsForSceneVersionTags(false)); } TEST_F(ASceneReference, requestsVersionNotification) @@ -162,13 +162,13 @@ namespace ramses constexpr sceneId_t sceneId{ 444 }; auto reference = m_scene.createSceneReference(sceneId); ASSERT_NE(nullptr, reference); - const auto sceneRefHandle = reference->m_impl.getSceneReferenceHandle(); + const auto sceneRefHandle = reference->impl().getSceneReferenceHandle(); ASSERT_TRUE(this->getInternalScene().isSceneReferenceAllocated(sceneRefHandle)); - EXPECT_EQ(StatusOK, reference->requestNotificationsForSceneVersionTags(true)); + EXPECT_TRUE(reference->requestNotificationsForSceneVersionTags(true)); EXPECT_TRUE(this->getInternalScene().getSceneReference(sceneRefHandle).flushNotifications); - EXPECT_EQ(StatusOK, reference->requestNotificationsForSceneVersionTags(false)); + EXPECT_TRUE(reference->requestNotificationsForSceneVersionTags(false)); EXPECT_FALSE(this->getInternalScene().getSceneReference(sceneRefHandle).flushNotifications); } @@ -178,9 +178,9 @@ namespace ramses auto reference = m_scene.createSceneReference(sceneId); ASSERT_NE(nullptr, reference); - EXPECT_EQ(StatusOK, reference->setRenderOrder(-13)); + EXPECT_TRUE(reference->setRenderOrder(-13)); - const auto sceneRefHandle = reference->m_impl.getSceneReferenceHandle(); + const auto sceneRefHandle = reference->impl().getSceneReferenceHandle(); ASSERT_TRUE(this->getInternalScene().isSceneReferenceAllocated(sceneRefHandle)); EXPECT_EQ(-13, this->getInternalScene().getSceneReference(sceneRefHandle).renderOrder); } @@ -195,31 +195,31 @@ namespace ramses auto reference2 = m_scene.createSceneReference(sceneId2); ASSERT_NE(nullptr, reference1); ASSERT_NE(nullptr, reference2); - const auto sceneRefHandle1 = reference1->m_impl.getSceneReferenceHandle(); - const auto sceneRefHandle2 = reference2->m_impl.getSceneReferenceHandle(); - reference1->m_impl.setReportedState(RendererSceneState::Ready); - reference2->m_impl.setReportedState(RendererSceneState::Ready); + const auto sceneRefHandle1 = reference1->impl().getSceneReferenceHandle(); + const auto sceneRefHandle2 = reference2->impl().getSceneReferenceHandle(); + reference1->impl().setReportedState(RendererSceneState::Ready); + reference2->impl().setReportedState(RendererSceneState::Ready); - EXPECT_EQ(StatusOK, m_scene.linkData(reference1, providerId, reference2, consumerId)); - EXPECT_EQ(StatusOK, m_scene.linkData(reference1, providerId, nullptr, consumerId)); - EXPECT_EQ(StatusOK, m_scene.linkData(nullptr, providerId, reference2, consumerId)); + EXPECT_TRUE(m_scene.linkData(reference1, providerId, reference2, consumerId)); + EXPECT_TRUE(m_scene.linkData(reference1, providerId, nullptr, consumerId)); + EXPECT_TRUE(m_scene.linkData(nullptr, providerId, reference2, consumerId)); const auto& actions = this->getInternalScene().getSceneReferenceActions(); ASSERT_EQ(3u, actions.size()); - EXPECT_EQ(ramses_internal::SceneReferenceActionType::LinkData, actions[0].type); + EXPECT_EQ(ramses::internal::SceneReferenceActionType::LinkData, actions[0].type); EXPECT_EQ(sceneRefHandle1, actions[0].providerScene); EXPECT_EQ(providerId.getValue(), actions[0].providerId.getValue()); EXPECT_EQ(sceneRefHandle2, actions[0].consumerScene); EXPECT_EQ(consumerId.getValue(), actions[0].consumerId.getValue()); - EXPECT_EQ(ramses_internal::SceneReferenceActionType::LinkData, actions[1].type); + EXPECT_EQ(ramses::internal::SceneReferenceActionType::LinkData, actions[1].type); EXPECT_EQ(sceneRefHandle1, actions[1].providerScene); EXPECT_EQ(providerId.getValue(), actions[1].providerId.getValue()); EXPECT_FALSE(actions[1].consumerScene.isValid()); // master scene stored as invalid scene ref handle EXPECT_EQ(consumerId.getValue(), actions[1].consumerId.getValue()); - EXPECT_EQ(ramses_internal::SceneReferenceActionType::LinkData, actions[2].type); + EXPECT_EQ(ramses::internal::SceneReferenceActionType::LinkData, actions[2].type); EXPECT_FALSE(actions[2].providerScene.isValid()); // master scene stored as invalid scene ref handle EXPECT_EQ(providerId.getValue(), actions[2].providerId.getValue()); EXPECT_EQ(sceneRefHandle2, actions[2].consumerScene); @@ -232,19 +232,19 @@ namespace ramses constexpr dataConsumerId_t consumerId{ 13 }; auto reference = m_scene.createSceneReference(sceneId); ASSERT_NE(nullptr, reference); - const auto sceneRefHandle = reference->m_impl.getSceneReferenceHandle(); + const auto sceneRefHandle = reference->impl().getSceneReferenceHandle(); - EXPECT_EQ(StatusOK, m_scene.unlinkData(reference, consumerId)); - EXPECT_EQ(StatusOK, m_scene.unlinkData(nullptr, consumerId)); + EXPECT_TRUE(m_scene.unlinkData(reference, consumerId)); + EXPECT_TRUE(m_scene.unlinkData(nullptr, consumerId)); const auto& actions = this->getInternalScene().getSceneReferenceActions(); ASSERT_EQ(2u, actions.size()); - EXPECT_EQ(ramses_internal::SceneReferenceActionType::UnlinkData, actions[0].type); + EXPECT_EQ(ramses::internal::SceneReferenceActionType::UnlinkData, actions[0].type); EXPECT_EQ(sceneRefHandle, actions[0].consumerScene); EXPECT_EQ(consumerId.getValue(), actions[0].consumerId.getValue()); - EXPECT_EQ(ramses_internal::SceneReferenceActionType::UnlinkData, actions[1].type); + EXPECT_EQ(ramses::internal::SceneReferenceActionType::UnlinkData, actions[1].type); EXPECT_FALSE(actions[1].consumerScene.isValid()); EXPECT_EQ(consumerId.getValue(), actions[1].consumerId.getValue()); } diff --git a/client/test/SceneTest.cpp b/tests/unittests/client/SceneTest.cpp similarity index 55% rename from client/test/SceneTest.cpp rename to tests/unittests/client/SceneTest.cpp index fbb175c12..8f0d5769f 100644 --- a/client/test/SceneTest.cpp +++ b/tests/unittests/client/SceneTest.cpp @@ -7,44 +7,45 @@ // ------------------------------------------------------------------------- #include -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/SceneObjectIterator.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/BlitPass.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/DataObject.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-framework-api/EDataType.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-utils.h" - -#include "DataObjectImpl.h" -#include "RenderGroupImpl.h" -#include "RenderPassImpl.h" -#include "ArrayBufferImpl.h" -#include "BlitPassImpl.h" -#include "TextureSamplerImpl.h" -#include "Texture2DImpl.h" -#include "SceneConfigImpl.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/SceneObjectIterator.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/BlitPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/DataObject.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/framework/EDataType.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/ramses-utils.h" + +#include "impl/DataObjectImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/Texture2DImpl.h" +#include "impl/SceneConfigImpl.h" #include "ClientTestUtils.h" #include "SimpleSceneTopology.h" -#include "Components/FlushTimeInformation.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/Components/FlushTimeInformation.h" +#include "internal/PlatformAbstraction/PlatformTime.h" using namespace testing; -namespace ramses +namespace ramses::internal { class AScene : public LocalTestClientWithScene, public ::testing::Test { public: AScene() - : LocalTestClientWithScene() { effectDescriptionEmpty.setVertexShader("void main(void) {gl_Position=vec4(0);}"); effectDescriptionEmpty.setFragmentShader("void main(void) {gl_FragColor=vec4(0);}"); @@ -54,22 +55,22 @@ namespace ramses ramses::EffectDescription effectDescriptionEmpty; }; - TEST(SceneOjects, canGiveSceneID) + TEST(SceneOjects, canGiveOwningScene) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework framework{config}; RamsesClient& client(*framework.createClient({})); - Scene* sceneA = client.createScene(sceneId_t(1u)); - Scene* sceneB = client.createScene(sceneId_t(2u)); + ramses::Scene* sceneA = client.createScene(SceneConfig(sceneId_t(1u))); + ramses::Scene* sceneB = client.createScene(SceneConfig(sceneId_t(2u))); ramses::Node* fromSceneA = sceneA->createNode(); ramses::SceneObject* objectFromB = sceneB->createNode(); - EXPECT_EQ(sceneId_t(1u), fromSceneA->getSceneId()); - EXPECT_EQ(sceneId_t(2u), objectFromB->getSceneId()); + EXPECT_EQ(sceneA, &fromSceneA->getScene()); + EXPECT_EQ(sceneB, &objectFromB->getScene()); // use case to retrieve scene object itself - EXPECT_EQ(sceneA, client.getScene(fromSceneA->getSceneId())); + EXPECT_EQ(sceneA, client.getScene(fromSceneA->getScene().getSceneId())); } TEST(ASceneConfig, CanBeCopyAndMoveConstructed) @@ -78,10 +79,10 @@ namespace ramses config.setPublicationMode(EScenePublicationMode::LocalAndRemote); SceneConfig configCopy{ config }; - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configCopy.m_impl.get().getPublicationMode()); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configCopy.impl().getPublicationMode()); SceneConfig configMove{ std::move(config) }; - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configMove.m_impl.get().getPublicationMode()); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configMove.impl().getPublicationMode()); } TEST(ASceneConfig, CanBeCopyAndMoveAssigned) @@ -91,11 +92,11 @@ namespace ramses SceneConfig configCopy; configCopy = config; - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configCopy.m_impl.get().getPublicationMode()); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configCopy.impl().getPublicationMode()); SceneConfig configMove; configMove = std::move(config); - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configMove.m_impl.get().getPublicationMode()); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, configMove.impl().getPublicationMode()); } TEST(ASceneConfig, CanBeSelfAssigned) @@ -109,10 +110,10 @@ namespace ramses #pragma clang diagnostic ignored "-Wself-assign-overloaded" #endif config = config; - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, config.m_impl.get().getPublicationMode()); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, config.impl().getPublicationMode()); config = std::move(config); // NOLINTNEXTLINE(bugprone-use-after-move) - EXPECT_EQ(EScenePublicationMode::LocalAndRemote, config.m_impl.get().getPublicationMode()); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, config.impl().getPublicationMode()); #ifdef __clang__ #pragma clang diagnostic pop #endif @@ -130,11 +131,8 @@ namespace ramses { return end - start; } - else - { - constexpr auto limit = std::numeric_limits::max(); - return (limit - start) + end + 1; - } + constexpr auto limit = std::numeric_limits::max(); + return (limit - start) + end + 1; } TEST(GetElapsedSelfTest, overflow) @@ -152,10 +150,10 @@ namespace ramses RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; RamsesClient& remoteClient(*framework.createClient({})); framework.connect(); - SceneConfig config; + SceneConfig config(sceneId_t(1u)); config.setPublicationMode(EScenePublicationMode::LocalOnly); - Scene* distributedScene = remoteClient.createScene(sceneId_t(1u), config); - EXPECT_NE(StatusOK, distributedScene->publish(EScenePublicationMode::LocalAndRemote)); + ramses::Scene* distributedScene = remoteClient.createScene(config); + EXPECT_FALSE(distributedScene->publish(EScenePublicationMode::LocalAndRemote)); } TEST(DistributedSceneTest, canPublishRemotelyIfSetInConfig) @@ -163,20 +161,20 @@ namespace ramses RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; RamsesClient& remoteClient(*framework.createClient({})); framework.connect(); - SceneConfig config; + SceneConfig config(sceneId_t(1u)); config.setPublicationMode(EScenePublicationMode::LocalAndRemote); - Scene* distributedScene = remoteClient.createScene(sceneId_t(1u), config); - EXPECT_EQ(StatusOK, distributedScene->publish(EScenePublicationMode::LocalAndRemote)); + ramses::Scene* distributedScene = remoteClient.createScene(config); + EXPECT_TRUE(distributedScene->publish(EScenePublicationMode::LocalAndRemote)); } - TEST(DistributedSceneTest, canPublishRemotelyIfNoSettingInConfig) + TEST(DistributedSceneTest, cannotPublishRemotelyByDefault) { RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; RamsesClient& remoteClient(*framework.createClient({})); framework.connect(); - SceneConfig config; - Scene* distributedScene = remoteClient.createScene(sceneId_t(1u), config); - EXPECT_EQ(StatusOK, distributedScene->publish(EScenePublicationMode::LocalAndRemote)); + SceneConfig config(sceneId_t(1u)); + ramses::Scene* distributedScene = remoteClient.createScene(config); + EXPECT_FALSE(distributedScene->publish(EScenePublicationMode::LocalAndRemote)); } TEST(DistributedSceneTest, canPublishLocallyWhenEnablingLocalOnlyOptimisations) @@ -184,33 +182,50 @@ namespace ramses RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; RamsesClient& remoteClient(*framework.createClient({})); framework.connect(); - SceneConfig config; + SceneConfig config(sceneId_t(1u)); config.setPublicationMode(EScenePublicationMode::LocalOnly); - Scene* distributedScene = remoteClient.createScene(sceneId_t(1u), config); - EXPECT_EQ(StatusOK, distributedScene->publish(EScenePublicationMode::LocalOnly)); + ramses::Scene* distributedScene = remoteClient.createScene(config); + EXPECT_TRUE(distributedScene->publish(EScenePublicationMode::LocalOnly)); } - TEST(DistributedSceneTest, isPublishedAfterDisconnect) + TEST(DistributedSceneTest, isPublishedAfterDisconnect_LocalOnly) { RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; RamsesClient& remoteClient(*framework.createClient({})); framework.connect(); - SceneConfig config; - Scene* distributedScene = remoteClient.createScene(sceneId_t(1u), config); - EXPECT_EQ(StatusOK, distributedScene->publish(EScenePublicationMode::LocalAndRemote)); + SceneConfig config(sceneId_t(1u)); + config.setPublicationMode(EScenePublicationMode::LocalOnly); + ramses::Scene* distributedScene = remoteClient.createScene(config); + EXPECT_TRUE(distributedScene->publish(EScenePublicationMode::LocalOnly)); framework.disconnect(); EXPECT_TRUE(distributedScene->isPublished()); framework.connect(); EXPECT_TRUE(distributedScene->isPublished()); - EXPECT_NE(StatusOK, distributedScene->publish(EScenePublicationMode::LocalAndRemote)); + EXPECT_FALSE(distributedScene->publish(EScenePublicationMode::LocalOnly)); + } + + TEST(DistributedSceneTest, isPublishedAfterDisconnect_Remote) + { + RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; + RamsesClient& remoteClient(*framework.createClient({})); + framework.connect(); + SceneConfig config(sceneId_t(1u)); + config.setPublicationMode(EScenePublicationMode::LocalAndRemote); + ramses::Scene* distributedScene = remoteClient.createScene(config); + EXPECT_TRUE(distributedScene->publish(EScenePublicationMode::LocalAndRemote)); + framework.disconnect(); + EXPECT_TRUE(distributedScene->isPublished()); + framework.connect(); + EXPECT_TRUE(distributedScene->isPublished()); + EXPECT_FALSE(distributedScene->publish(EScenePublicationMode::LocalAndRemote)); } TEST(DistributedSceneTest, canPublishSceneIfNotConnected) { RamsesFramework framework{ LocalTestClient::GetDefaultFrameworkConfig() }; RamsesClient& remoteClient(*framework.createClient({})); - Scene* distributedScene = remoteClient.createScene(sceneId_t(1u)); - EXPECT_EQ(StatusOK, distributedScene->publish()); + ramses::Scene* distributedScene = remoteClient.createScene(SceneConfig(sceneId_t(1u))); + EXPECT_TRUE(distributedScene->publish()); EXPECT_TRUE(distributedScene->isPublished()); framework.connect(); EXPECT_TRUE(distributedScene->isPublished()); @@ -218,100 +233,108 @@ namespace ramses TEST_F(AScene, canValidate) { - EXPECT_EQ(StatusOK, m_scene.validate()); + ValidationReport report; + m_scene.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(AScene, failsValidationIfContainsInvalidSceneObject) { - RenderPass* passWithoutCamera = m_scene.createRenderPass(); - EXPECT_NE(StatusOK, m_scene.validate()); + ramses::RenderPass* passWithoutCamera = m_scene.createRenderPass(); + ValidationReport report; + m_scene.validate(report); + EXPECT_TRUE(report.hasIssue()); m_scene.destroy(*passWithoutCamera); - EXPECT_EQ(StatusOK, m_scene.validate()); + report.clear(); + m_scene.validate(report); + EXPECT_FALSE(report.hasIssue()); - Camera* cameraWithoutValidValues = m_scene.createPerspectiveCamera(); - EXPECT_NE(StatusOK, m_scene.validate()); + ramses::Camera* cameraWithoutValidValues = m_scene.createPerspectiveCamera(); + report.clear(); + m_scene.validate(report); + EXPECT_TRUE(report.hasError()); m_scene.destroy(*cameraWithoutValidValues); } TEST_F(AScene, doesNotDestroyCameraWhileItIsStillUsedByARenderPass) { - RenderPass* pass = m_scene.createRenderPass(); + ramses::RenderPass* pass = m_scene.createRenderPass(); PerspectiveCamera* perspCam = m_scene.createPerspectiveCamera(); perspCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); perspCam->setViewport(0, 0, 100, 200); pass->setCamera(*perspCam); - EXPECT_NE(StatusOK, m_scene.destroy(*perspCam)); + EXPECT_FALSE(m_scene.destroy(*perspCam)); } TEST_F(AScene, removesMeshNodeFromRenderGroupsOnDestruction) { - RenderGroup* group = m_scene.createRenderGroup(); - RenderGroup* group2 = m_scene.createRenderGroup(); + ramses::RenderGroup* group = m_scene.createRenderGroup(); + ramses::RenderGroup* group2 = m_scene.createRenderGroup(); MeshNode* mesh = m_scene.createMeshNode(); group->addMeshNode(*mesh); group2->addMeshNode(*mesh); m_scene.destroy(*mesh); - EXPECT_TRUE(group->m_impl.getAllMeshes().empty()); - EXPECT_TRUE(group2->m_impl.getAllMeshes().empty()); + EXPECT_TRUE(group->impl().getAllMeshes().empty()); + EXPECT_TRUE(group2->impl().getAllMeshes().empty()); } TEST_F(AScene, failsToCreateAppearanceWhenEffectIsFromAnotherScene) { - Scene& anotherScene(*client.createScene(sceneId_t{ 0xf00 })); - Effect* effect = anotherScene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "emptyEffect"); + ramses::Scene& anotherScene(*client.createScene(SceneConfig(sceneId_t{ 0xf00 }))); + Effect* effect = anotherScene.createEffect(effectDescriptionEmpty, "emptyEffect"); ASSERT_TRUE(nullptr != effect); Appearance* appearance = m_scene.createAppearance(*effect, "appearance"); EXPECT_TRUE(nullptr == appearance); } - TEST_F(AScene, failsToCreateGeometryBindingWhenEffectIsFromAnotherClient) + TEST_F(AScene, failsToCreateGeometryWhenEffectIsFromAnotherClient) { - Scene& anotherScene(*client.createScene(sceneId_t{ 0xf00 })); - Effect* effect = anotherScene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "emptyEffect"); + ramses::Scene& anotherScene(*client.createScene(SceneConfig(sceneId_t{ 0xf00 }))); + Effect* effect = anotherScene.createEffect(effectDescriptionEmpty, "emptyEffect"); ASSERT_TRUE(nullptr != effect); - GeometryBinding* geometryBinding = m_scene.createGeometryBinding(*effect, "geometryBinding"); + Geometry* geometryBinding = m_scene.createGeometry(*effect, "geometryBinding"); EXPECT_TRUE(nullptr == geometryBinding); } TEST_F(AScene, failsToCreateTextureSamplerWhenTextureIsFromAnotherScene) { - Scene& anotherScene(*client.createScene(sceneId_t{ 0xf00 })); + ramses::Scene& anotherScene(*client.createScene(SceneConfig(sceneId_t{ 0xf00 }))); { const uint8_t data[] = { 1, 2, 3 }; const MipLevelData mipData(3u, data); Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(texture != nullptr); - TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); EXPECT_TRUE(nullptr == textureSampler); } { const uint8_t data[1 * 2 * 2 * 4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = anotherScene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, 1, &mipLevelData, false, ramses::ResourceCacheFlag_DoNotCache, {}); + Texture3D* texture = anotherScene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, 1, &mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); - TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); EXPECT_TRUE(nullptr == textureSampler); } { - const uint8_t data[4 * 10 * 10] = {}; + const std::byte data[4 * 10 * 10] = {}; CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); TextureCube* texture = anotherScene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false); ASSERT_TRUE(nullptr != texture); - TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); EXPECT_TRUE(nullptr == textureSampler); } { - RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + ramses::RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); ASSERT_TRUE(nullptr != renderBuffer); TextureSamplerMS* textureSampler = m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); @@ -321,11 +344,11 @@ namespace ramses TEST_F(AScene, failsToCreateTextureSamplerWhenRenderTargetIsFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); + ramses::RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != renderBuffer); - TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *renderBuffer); + ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *renderBuffer); EXPECT_TRUE(nullptr == textureSampler); client.destroy(anotherScene); @@ -333,8 +356,8 @@ namespace ramses TEST_F(AScene, failsToCreateTextureSamplerMSWhenRenderTargetIsFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); + ramses::RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != renderBuffer); TextureSamplerMS* textureSampler = m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); @@ -345,16 +368,16 @@ namespace ramses TEST_F(AScene, failsToCreateTextureSamplerWhenRenderBufferIsNotReadable) { - RenderBuffer* renderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly); + ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly); ASSERT_TRUE(nullptr != renderBuffer); - TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *renderBuffer); + ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *renderBuffer); EXPECT_TRUE(nullptr == textureSampler); } TEST_F(AScene, failsToCreateTextureSamplerMSWhenRenderBufferIsNotReadable) { - RenderBuffer* renderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly); + ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly); ASSERT_TRUE(nullptr != renderBuffer); TextureSamplerMS* textureSampler = m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); @@ -363,24 +386,24 @@ namespace ramses TEST_F(AScene, reportsErrorWhenCreateTransformationDataProviderWithNodeFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); Node* node = anotherScene.createNode("node"); ASSERT_TRUE(nullptr != node); - EXPECT_NE(StatusOK, m_scene.createTransformationDataProvider(*node, dataProviderId_t{1u})); + EXPECT_FALSE(m_scene.createTransformationDataProvider(*node, dataProviderId_t{1u})); client.destroy(anotherScene); } TEST_F(AScene, reportsErrorWhenCreateTransformationDataConsumerWithNodeFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); Node* node = anotherScene.createNode("groupNode"); ASSERT_TRUE(nullptr != node); - EXPECT_NE(StatusOK, m_scene.createTransformationDataConsumer(*node, dataConsumerId_t{1u})); + EXPECT_FALSE(m_scene.createTransformationDataConsumer(*node, dataConsumerId_t{1u})); client.destroy(anotherScene); } @@ -390,27 +413,27 @@ namespace ramses m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); m_vis2.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); m_vis1.setVisibility(EVisibilityMode::Off); m_vis2.setVisibility(EVisibilityMode::Visible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, checksMeshVisibilityAddedToInvisibleParent) @@ -421,7 +444,7 @@ namespace ramses addMeshNode->setParent(m_vis2); m_scene.flush(); - EXPECT_EQ(addMeshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(addMeshNode->impl().getFlattenedVisibility(), EVisibilityMode::Invisible); //cleanup addMeshNode->removeParent(); @@ -435,7 +458,7 @@ namespace ramses addMeshNode->setParent(m_vis2); m_scene.flush(); - EXPECT_EQ(addMeshNode->m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(addMeshNode->impl().getFlattenedVisibility(), EVisibilityMode::Off); //cleanup addMeshNode->removeParent(); @@ -446,47 +469,47 @@ namespace ramses //Add another visibility hierarchy level below mesh 4 Node& m_node8 = *m_scene.createNode("node8"); MeshNode& m_node9 = *m_scene.createMeshNode("node9"); - EXPECT_EQ(StatusOK, m_node8.setParent(m_mesh1a)); - EXPECT_EQ(StatusOK, m_node9.setParent(m_node8)); + EXPECT_TRUE(m_node8.setParent(m_mesh1a)); + EXPECT_TRUE(m_node9.setParent(m_node8)); //Newly added mesh should be visible by default - EXPECT_EQ(m_node9.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_node9.impl().getFlattenedVisibility(), EVisibilityMode::Visible); m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_node9.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_node9.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); m_node8.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_node9.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_node9.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); m_vis1.setVisibility(EVisibilityMode::Visible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_node9.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_node9.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); m_node8.setVisibility(EVisibilityMode::Visible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_node9.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_node9.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, subTreeStaysInvisibleIfSettingVisibilityNodeVisibleParentedByInvisibleNode) @@ -506,16 +529,16 @@ namespace ramses // should stay invisible m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); } TEST_F(ASceneWithContent, nodesStayVisibleWhenRemovedVisibleParent) { m_scene.destroy(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodesBecomeVisibleWhenRemovedInvisibleParent) @@ -525,8 +548,8 @@ namespace ramses m_scene.destroy(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodesBecomeVisibleWhenRemovedOffParent) @@ -536,8 +559,8 @@ namespace ramses m_scene.destroy(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, visibleMeshBecomesInvisibleWhenMovedUnderInvisibleNode) @@ -547,7 +570,7 @@ namespace ramses m_mesh2a.setParent(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); } TEST_F(ASceneWithContent, visibleMeshBecomesOffWhenMovedUnderOffNode) @@ -557,7 +580,7 @@ namespace ramses m_mesh2a.setParent(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Off); } TEST_F(ASceneWithContent, visibleMeshWithoutParentBecomesInvisibleWhenSetInvisibleNodeAsParent) @@ -569,7 +592,7 @@ namespace ramses meshOrphan.setParent(m_vis1); m_scene.flush(); - EXPECT_EQ(meshOrphan.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(meshOrphan.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); } TEST_F(ASceneWithContent, visibleMeshWithoutParentBecomesOffWhenSetOffNodeAsParent) @@ -581,7 +604,7 @@ namespace ramses meshOrphan.setParent(m_vis1); m_scene.flush(); - EXPECT_EQ(meshOrphan.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(meshOrphan.impl().getFlattenedVisibility(), EVisibilityMode::Off); } TEST_F(ASceneWithContent, visibleMeshWithoutParentBecomesInvisibleWhenMovedUnderInvisibleNode) @@ -593,7 +616,7 @@ namespace ramses m_vis1.addChild(meshOrphan); m_scene.flush(); - EXPECT_EQ(meshOrphan.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(meshOrphan.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); } TEST_F(ASceneWithContent, visibleMeshWithoutParentBecomesOffWhenMovedUnderOffNode) @@ -605,7 +628,7 @@ namespace ramses m_vis1.addChild(meshOrphan); m_scene.flush(); - EXPECT_EQ(meshOrphan.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(meshOrphan.impl().getFlattenedVisibility(), EVisibilityMode::Off); } TEST_F(ASceneWithContent, meshInInvisibleBranchBecomesVisibleWhenMovedUnderVisibleNode) @@ -615,7 +638,7 @@ namespace ramses m_mesh1a.setParent(m_vis2); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, meshInOffBranchBecomesVisibleWhenMovedUnderVisibleNode) @@ -625,7 +648,7 @@ namespace ramses m_mesh1a.setParent(m_vis2); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodesStayVisibleIfParentBecomesInvisibleAndIsDeletedInSameCommit) @@ -633,8 +656,8 @@ namespace ramses m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.destroy(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodesStayVisibleIfParentBecomesOffAndIsDeletedInSameCommit) @@ -642,28 +665,28 @@ namespace ramses m_vis1.setVisibility(EVisibilityMode::Off); m_scene.destroy(m_vis1); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodeBecomesVisibleIfInvisibleParentIsRemovedFromIt) { m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); m_mesh1a.removeParent(); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodeBecomesVisibleIfOffParentIsRemovedFromIt) { m_vis1.setVisibility(EVisibilityMode::Off); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); m_mesh1a.removeParent(); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } TEST_F(ASceneWithContent, nodeBecomesInvisibleIfAnyOfItsAncestorsIsInvisible) @@ -678,20 +701,20 @@ namespace ramses // set parent invisible vis1a.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); // revert vis1a.setVisibility(EVisibilityMode::Visible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); // set grandparent invisible m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); } TEST_F(ASceneWithContent, nodeBecomesOffIfAnyOfItsAncestorsIsOff) @@ -706,20 +729,20 @@ namespace ramses // set parent invisible vis1a.setVisibility(EVisibilityMode::Off); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Off); // revert vis1a.setVisibility(EVisibilityMode::Visible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); // set grandparent invisible m_vis1.setVisibility(EVisibilityMode::Off); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Off); } TEST_F(ASceneWithContent, nodeBecomesOffIfAnyOfItsAncestorsIsOffAndInvisible) @@ -735,20 +758,20 @@ namespace ramses vis1a.setVisibility(EVisibilityMode::Off); m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Off); // revert vis1a.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); // set grandparent invisible m_vis1.setVisibility(EVisibilityMode::Off); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Off); } TEST_F(ASceneWithContent, offNodeStaysOffIndependentOfParentVisibility) @@ -761,35 +784,35 @@ namespace ramses m_mesh1b.setVisibility(EVisibilityMode::Invisible); m_vis1.setVisibility(EVisibilityMode::Visible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); // revert m_vis1.setVisibility(EVisibilityMode::Invisible); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Invisible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Invisible); // set grandparent invisible m_vis1.setVisibility(EVisibilityMode::Off); m_scene.flush(); - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Off); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Off); } TEST_F(AScene, canCreateATransformDataSlot) { Node* node = m_scene.createNode(); - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); - EXPECT_EQ(StatusOK, m_scene.createTransformationDataConsumer(*node, dataConsumerId_t(666u))); + EXPECT_TRUE(m_scene.createTransformationDataConsumer(*node, dataConsumerId_t(666u))); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(node->m_impl.getNodeHandle(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedNode); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TransformationConsumer, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(node->impl().getNodeHandle(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedNode); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TransformationConsumer, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, removesTransformDataSlotsOfNodeOnDestruction) @@ -797,186 +820,186 @@ namespace ramses Node* node = m_scene.createNode(); m_scene.createTransformationDataProvider(*node, dataProviderId_t(1412u)); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle linkHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(linkHandle)); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle linkHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(linkHandle)); m_scene.destroy(*node); - EXPECT_FALSE(m_scene.m_impl.getIScene().isDataSlotAllocated(linkHandle)); + EXPECT_FALSE(m_scene.impl().getIScene().isDataSlotAllocated(linkHandle)); } TEST_F(AScene, canNotCreateMoreThanOneTransformationDataSlotForANode) { Node* node = m_scene.createNode(); - EXPECT_EQ(StatusOK, m_scene.createTransformationDataProvider(*node, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createTransformationDataProvider(*node, dataProviderId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createTransformationDataConsumer(*node, dataConsumerId_t(3u))); + EXPECT_TRUE(m_scene.createTransformationDataProvider(*node, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createTransformationDataProvider(*node, dataProviderId_t(2u))); + EXPECT_FALSE(m_scene.createTransformationDataConsumer(*node, dataConsumerId_t(3u))); } TEST_F(AScene, canNotCreateMoreThanOneTransformationDataSlotWithTheSameId) { Node* node1 = m_scene.createNode(); Node* node2 = m_scene.createNode(); - EXPECT_EQ(StatusOK, m_scene.createTransformationDataProvider(*node1, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createTransformationDataProvider(*node2, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createTransformationDataConsumer(*node2, dataConsumerId_t(1u))); + EXPECT_TRUE(m_scene.createTransformationDataProvider(*node1, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createTransformationDataProvider(*node2, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createTransformationDataConsumer(*node2, dataConsumerId_t(1u))); - EXPECT_EQ(StatusOK, m_scene.createTransformationDataConsumer(*node2, dataConsumerId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createTransformationDataConsumer(*node1, dataConsumerId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createTransformationDataProvider(*node1, dataProviderId_t(2u))); + EXPECT_TRUE(m_scene.createTransformationDataConsumer(*node2, dataConsumerId_t(2u))); + EXPECT_FALSE(m_scene.createTransformationDataConsumer(*node1, dataConsumerId_t(2u))); + EXPECT_FALSE(m_scene.createTransformationDataProvider(*node1, dataProviderId_t(2u))); } TEST_F(AScene, reportsErrorWhenCreateDataProviderWithDataObjectFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); - auto dataObject = anotherScene.createDataObject(EDataType::Float); + auto dataObject = anotherScene.createDataObject(ramses::EDataType::Float); ASSERT_TRUE(nullptr != dataObject); - EXPECT_NE(StatusOK, m_scene.createDataProvider(*dataObject, dataProviderId_t{1u})); + EXPECT_FALSE(m_scene.createDataProvider(*dataObject, dataProviderId_t{1u})); client.destroy(anotherScene); } TEST_F(AScene, reportsErrorWhenCreateDataConsumerWithDataObjectFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); - auto dataObject = anotherScene.createDataObject(EDataType::Float); + auto dataObject = anotherScene.createDataObject(ramses::EDataType::Float); ASSERT_TRUE(nullptr != dataObject); - EXPECT_NE(StatusOK, m_scene.createDataConsumer(*dataObject, dataConsumerId_t{1u})); + EXPECT_FALSE(m_scene.createDataConsumer(*dataObject, dataConsumerId_t{1u})); client.destroy(anotherScene); } TEST_F(AScene, canCreateADataObjectDataSlot) { - auto dataObject = m_scene.createDataObject(EDataType::Float); - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + auto dataObject = m_scene.createDataObject(ramses::EDataType::Float); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); - EXPECT_EQ(StatusOK, m_scene.createDataConsumer(*dataObject, dataConsumerId_t(666u))); + EXPECT_TRUE(m_scene.createDataConsumer(*dataObject, dataConsumerId_t(666u))); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(dataObject->m_impl.getDataReference(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedDataReference); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_DataConsumer, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(dataObject->impl().getDataReference(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedDataReference); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::DataConsumer, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, removesDataSlotsOfDataObjectOnDestruction) { - auto dataObject = m_scene.createDataObject(EDataType::Float); + auto dataObject = m_scene.createDataObject(ramses::EDataType::Float); m_scene.createDataProvider(*dataObject, dataProviderId_t(1412u)); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle linkHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(linkHandle)); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle linkHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(linkHandle)); m_scene.destroy(*dataObject); - EXPECT_FALSE(m_scene.m_impl.getIScene().isDataSlotAllocated(linkHandle)); + EXPECT_FALSE(m_scene.impl().getIScene().isDataSlotAllocated(linkHandle)); } TEST_F(AScene, canNotCreateMoreThanOneDataSlotForADataObject) { - auto dataObject = m_scene.createDataObject(EDataType::Float); - EXPECT_EQ(StatusOK, m_scene.createDataProvider(*dataObject, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createDataProvider(*dataObject, dataProviderId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createDataConsumer(*dataObject, dataConsumerId_t(3u))); + auto dataObject = m_scene.createDataObject(ramses::EDataType::Float); + EXPECT_TRUE(m_scene.createDataProvider(*dataObject, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createDataProvider(*dataObject, dataProviderId_t(2u))); + EXPECT_FALSE(m_scene.createDataConsumer(*dataObject, dataConsumerId_t(3u))); } TEST_F(AScene, canNotCreateMoreThanOneDataSlotWithTheSameId) { - auto dataObject1 = m_scene.createDataObject(EDataType::Float); - auto dataObject2 = m_scene.createDataObject(EDataType::Float); - EXPECT_EQ(StatusOK, m_scene.createDataProvider(*dataObject1, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createDataProvider(*dataObject2, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createDataConsumer(*dataObject2, dataConsumerId_t(1u))); + auto dataObject1 = m_scene.createDataObject(ramses::EDataType::Float); + auto dataObject2 = m_scene.createDataObject(ramses::EDataType::Float); + EXPECT_TRUE(m_scene.createDataProvider(*dataObject1, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createDataProvider(*dataObject2, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createDataConsumer(*dataObject2, dataConsumerId_t(1u))); - EXPECT_EQ(StatusOK, m_scene.createDataConsumer(*dataObject2, dataConsumerId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createDataConsumer(*dataObject1, dataConsumerId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createDataProvider(*dataObject1, dataProviderId_t(2u))); + EXPECT_TRUE(m_scene.createDataConsumer(*dataObject2, dataConsumerId_t(2u))); + EXPECT_FALSE(m_scene.createDataConsumer(*dataObject1, dataConsumerId_t(2u))); + EXPECT_FALSE(m_scene.createDataProvider(*dataObject1, dataProviderId_t(2u))); } TEST_F(AScene, reportsErrorWhenCreateTextureProviderWithTextureFromAnotherScene) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework anotherFramework{config}; - Scene& anotherScene(*client.createScene(sceneId_t{ 0xf00 })); + ramses::Scene& anotherScene(*client.createScene(SceneConfig(sceneId_t{ 0xf00 }))); uint8_t data = 0u; MipLevelData mipData(1u, &data); const Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - EXPECT_NE(StatusOK, m_scene.createTextureProvider(*texture, dataProviderId_t{1u})); + EXPECT_FALSE(m_scene.createTextureProvider(*texture, dataProviderId_t{1u})); } TEST_F(AScene, reportsErrorWhenCreateTextureConsumerWithSamplerFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); uint8_t data = 0u; MipLevelData mipData(1u, &data); Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - const TextureSampler* sampler = anotherScene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + const ramses::TextureSampler* sampler = anotherScene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(nullptr != sampler); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{1u})); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{1u})); client.destroy(anotherScene); } TEST_F(AScene, reportsErrorWhenCreateTextureConsumerWithSamplerMSFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); + ramses::RenderBuffer* renderBuffer = anotherScene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); ASSERT_TRUE(nullptr != renderBuffer); const TextureSamplerMS* sampler = anotherScene.createTextureSamplerMS(*renderBuffer, "sampler"); ASSERT_TRUE(nullptr != sampler); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 1u })); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 1u })); client.destroy(anotherScene); } TEST_F(AScene, reportsErrorWhenCreateTextureConsumerWithSamplerExternalFromAnotherScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); const TextureSamplerExternal* sampler = anotherScene.createTextureSamplerExternal(ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear); ASSERT_TRUE(nullptr != sampler); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 1u })); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 1u })); client.destroy(anotherScene); } TEST_F(AScene, canCreateATextureProviderDataSlot) { - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data = 0u; MipLevelData mipData(1u, &data); Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.createTextureProvider(*texture, dataProviderId_t{666u})); + EXPECT_TRUE(m_scene.createTextureProvider(*texture, dataProviderId_t{666u})); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(texture->m_impl.getLowlevelResourceHash(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedTexture); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TextureProvider, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(texture->impl().getLowlevelResourceHash(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedTexture); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TextureProvider, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, canUpdateATextureProviderDataSlot) { - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data1 = 0u; MipLevelData mipData1(1u, &data1); @@ -988,91 +1011,91 @@ namespace ramses Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData2, false); ASSERT_TRUE(nullptr != texture2); - EXPECT_EQ(StatusOK, m_scene.createTextureProvider(*texture1, dataProviderId_t{666u})); - EXPECT_EQ(StatusOK, m_scene.updateTextureProvider(*texture2, dataProviderId_t{666u})); + EXPECT_TRUE(m_scene.createTextureProvider(*texture1, dataProviderId_t{666u})); + EXPECT_TRUE(m_scene.updateTextureProvider(*texture2, dataProviderId_t{666u})); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(texture2->m_impl.getLowlevelResourceHash(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedTexture); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TextureProvider, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(texture2->impl().getLowlevelResourceHash(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedTexture); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TextureProvider, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, canCreateATextureConsumerDataSlot) { - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data = 0u; MipLevelData mipData(1u, &data); Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - const TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + const ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(sampler->m_impl.getTextureSamplerHandle(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedTextureSampler); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TextureConsumer, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(sampler->impl().getTextureSamplerHandle(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedTextureSampler); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TextureConsumer, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, canCreateATextureConsumerDataSlotWithTextureSamplerMS) { - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); - RenderBuffer* renderBuffer = m_scene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); ASSERT_TRUE(nullptr != renderBuffer); const TextureSamplerMS* sampler = m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(sampler->m_impl.getTextureSamplerHandle(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedTextureSampler); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TextureConsumer, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(sampler->impl().getTextureSamplerHandle(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedTextureSampler); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TextureConsumer, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, canCreateATextureConsumerDataSlotWithTextureSamplerExternal) { - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); const TextureSamplerExternal* sampler = m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle slotHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(slotHandle)); - EXPECT_EQ(sampler->m_impl.getTextureSamplerHandle(), m_scene.m_impl.getIScene().getDataSlot(slotHandle).attachedTextureSampler); - EXPECT_EQ(ramses_internal::DataSlotId(666u), m_scene.m_impl.getIScene().getDataSlot(slotHandle).id); - EXPECT_EQ(ramses_internal::EDataSlotType_TextureConsumer, m_scene.m_impl.getIScene().getDataSlot(slotHandle).type); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle slotHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(slotHandle)); + EXPECT_EQ(sampler->impl().getTextureSamplerHandle(), m_scene.impl().getIScene().getDataSlot(slotHandle).attachedTextureSampler); + EXPECT_EQ(ramses::internal::DataSlotId(666u), m_scene.impl().getIScene().getDataSlot(slotHandle).id); + EXPECT_EQ(ramses::internal::EDataSlotType::TextureConsumer, m_scene.impl().getIScene().getDataSlot(slotHandle).type); } TEST_F(AScene, canNotCreateATextureConsumerDataSlotIfOtherThan2DTextureAssigned) { - EXPECT_EQ(0u, m_scene.m_impl.getIScene().getDataSlotCount()); + EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data = 0u; MipLevelData mipData(1u, &data); Texture3D* texture = m_scene.createTexture3D(ETextureFormat::R8, 1u, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - const TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + const ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(nullptr != sampler); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); } TEST_F(AScene, removesDataSlotsOfTextureSamplerOnDestruction) @@ -1082,18 +1105,18 @@ namespace ramses Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); - EXPECT_EQ(1u, m_scene.m_impl.getIScene().getDataSlotCount()); - ramses_internal::DataSlotHandle linkHandle(0u); - ASSERT_TRUE(m_scene.m_impl.getIScene().isDataSlotAllocated(linkHandle)); + EXPECT_EQ(1u, m_scene.impl().getIScene().getDataSlotCount()); + ramses::internal::DataSlotHandle linkHandle(0u); + ASSERT_TRUE(m_scene.impl().getIScene().isDataSlotAllocated(linkHandle)); m_scene.destroy(*sampler); - EXPECT_FALSE(m_scene.m_impl.getIScene().isDataSlotAllocated(linkHandle)); + EXPECT_FALSE(m_scene.impl().getIScene().isDataSlotAllocated(linkHandle)); } TEST_F(AScene, canNotCreateMoreThanOneConsumerForATextureSampler) @@ -1103,23 +1126,23 @@ namespace ramses Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{667u})); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{667u})); } TEST_F(AScene, canNotCreateMoreThanOneConsumerForATextureSamplerMS) { - RenderBuffer* renderBuffer = m_scene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); + ramses::RenderBuffer* renderBuffer = m_scene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGB8, ERenderBufferAccessMode::ReadWrite, 4u); ASSERT_TRUE(nullptr != renderBuffer); const TextureSamplerMS* sampler = m_scene.createTextureSamplerMS(*renderBuffer, "sampler"); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 667u })); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 667u })); } TEST_F(AScene, canNotCreateMoreThanOneConsumerForATextureSamplerExternal) @@ -1127,8 +1150,8 @@ namespace ramses const TextureSamplerExternal* sampler = m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 667u })); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 666u })); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t{ 667u })); } TEST_F(AScene, canNotCreateMoreThanOneProviderForATexture) @@ -1138,8 +1161,8 @@ namespace ramses Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - EXPECT_EQ(StatusOK, m_scene.createTextureProvider(*texture, dataProviderId_t{666u})); - EXPECT_NE(StatusOK, m_scene.createTextureProvider(*texture, dataProviderId_t{667u})); + EXPECT_TRUE(m_scene.createTextureProvider(*texture, dataProviderId_t{666u})); + EXPECT_FALSE(m_scene.createTextureProvider(*texture, dataProviderId_t{667u})); } TEST_F(AScene, canNotCreateMoreThanOneTextureConsumerOrProviderWithTheSameId) @@ -1151,18 +1174,18 @@ namespace ramses Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture2); - TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); + ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(nullptr != sampler); - TextureSampler* sampler2 = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture2); + ramses::TextureSampler* sampler2 = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture2); ASSERT_TRUE(nullptr != sampler2); - EXPECT_EQ(StatusOK, m_scene.createTextureProvider(*texture, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createTextureProvider(*texture2, dataProviderId_t(1u))); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t(1u))); + EXPECT_TRUE(m_scene.createTextureProvider(*texture, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createTextureProvider(*texture2, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t(1u))); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler, dataConsumerId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createTextureConsumer(*sampler2, dataConsumerId_t(2u))); - EXPECT_NE(StatusOK, m_scene.createTextureProvider(*texture, dataProviderId_t(2u))); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler, dataConsumerId_t(2u))); + EXPECT_FALSE(m_scene.createTextureConsumer(*sampler2, dataConsumerId_t(2u))); + EXPECT_FALSE(m_scene.createTextureProvider(*texture, dataProviderId_t(2u))); } TEST_F(AScene, canNotUpdateTextureProviderWhichWasNotCreatedBefore) @@ -1172,15 +1195,15 @@ namespace ramses Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); ASSERT_TRUE(nullptr != texture); - EXPECT_NE(StatusOK, m_scene.updateTextureProvider(*texture, dataProviderId_t(1u))); + EXPECT_FALSE(m_scene.updateTextureProvider(*texture, dataProviderId_t(1u))); } TEST_F(AScene, canCreateTextureSamplerForTexture2DWithDefaultAnisotropyLevel) { const Texture2D& texture2D = createObject("testTexture2D"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, texture2D); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, texture2D); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Nearest, sampler->getMinSamplingMethod()); @@ -1191,10 +1214,10 @@ namespace ramses TEST_F(AScene, canCreateTextureSamplerForRenderBufferWithDefaultAnisotropyLevel) { - const RenderBuffer& renderBuffer = createObject("renderBuffer"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, renderBuffer); + const ramses::RenderBuffer& renderBuffer = createObject("renderBuffer"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, renderBuffer); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Nearest, sampler->getMinSamplingMethod()); @@ -1206,9 +1229,9 @@ namespace ramses TEST_F(AScene, canCreateTextureSamplerForTextureCubeWithDefaultAnisotropyLevel) { const TextureCube& textureCube = createObject("testTextureCube"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Linear, sampler->getMinSamplingMethod()); @@ -1220,21 +1243,21 @@ namespace ramses TEST_F(AScene, cantCreateTextureSamplerForTexture2DWithWrongAnisotropyValue) { const Texture2D& texture = createObject("testTexture2D"); - TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, texture, 0); + ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, texture, 0); EXPECT_TRUE(nullptr == textureSampler); } TEST_F(AScene, cantCreateTextureSamplerForTextureCubeWithWrongAnisotropyValue) { const TextureCube& textureCube = createObject("testTextureCube"); - const TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, textureCube, 0); + const ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, textureCube, 0); EXPECT_TRUE(nullptr == textureSampler); } TEST_F(AScene, cantCreateTextureSamplerForRenderBufferWithWrongAnisotropyValue) { - const RenderBuffer& renderBuffer = createObject("renderBuffer"); - const TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, renderBuffer, 0); + const ramses::RenderBuffer& renderBuffer = createObject("renderBuffer"); + const ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, renderBuffer, 0); EXPECT_TRUE(nullptr == textureSampler); } @@ -1248,83 +1271,83 @@ namespace ramses TEST_F(AScene, canCreateBlitPass) { - const RenderBuffer& sourceRenderBuffer = createObject("src renderBuffer"); - const RenderBuffer& destinationRenderBuffer = createObject("dst renderBuffer"); - const BlitPass* blitPass = m_scene.createBlitPass(sourceRenderBuffer, destinationRenderBuffer, "blitpass"); + const ramses::RenderBuffer& sourceRenderBuffer = createObject("src renderBuffer"); + const ramses::RenderBuffer& destinationRenderBuffer = createObject("dst renderBuffer"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(sourceRenderBuffer, destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr != blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSourceRenderBufferFromDifferentScene) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - const RenderBuffer* sourceRenderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); + const ramses::RenderBuffer* sourceRenderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != sourceRenderBuffer); - const RenderBuffer& destinationRenderBuffer = createObject("dst renderBuffer"); - const BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, destinationRenderBuffer, "blitpass"); + const ramses::RenderBuffer& destinationRenderBuffer = createObject("dst renderBuffer"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSourceAndDestinationRenderBufferFromDifferentScenes) { - Scene& anotherScene = *client.createScene(sceneId_t(12u)); - const RenderBuffer* destinationRenderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); + const ramses::RenderBuffer* destinationRenderBuffer = anotherScene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != destinationRenderBuffer); - const RenderBuffer& sourceRenderBuffer = createObject("src renderBuffer"); - const BlitPass* blitPass = m_scene.createBlitPass(sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); + const ramses::RenderBuffer& sourceRenderBuffer = createObject("src renderBuffer"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSourceAndDestinationRenderBuffersHavingDifferentType) { - const RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::R8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Depth, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::R8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::Depth24, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != sourceRenderBuffer); ASSERT_TRUE(nullptr != destinationRenderBuffer); - const BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSourceAndDestinationRenderBuffersHavingDifferentFormat) { - const RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::R8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::R8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != sourceRenderBuffer); ASSERT_TRUE(nullptr != destinationRenderBuffer); - const BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSourceAndDestinationRenderBuffersHavinghDifferentWidth) { - const RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(1u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(1u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != sourceRenderBuffer); ASSERT_TRUE(nullptr != destinationRenderBuffer); - const BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSourceAndDestinationRenderBuffersHavingDifferentHeight) { - const RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(100u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); - const RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* sourceRenderBuffer = m_scene.createRenderBuffer(100u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* destinationRenderBuffer = m_scene.createRenderBuffer(100u, 100u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != sourceRenderBuffer); ASSERT_TRUE(nullptr != destinationRenderBuffer); - const BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(*sourceRenderBuffer, *destinationRenderBuffer, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } TEST_F(AScene, cannotCreateBlitPass_WithSameSourceAndDestinationRenderBuffers) { - const RenderBuffer* rb = m_scene.createRenderBuffer(100u, 1u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); + const ramses::RenderBuffer* rb = m_scene.createRenderBuffer(100u, 1u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite); ASSERT_TRUE(nullptr != rb); - const BlitPass* blitPass = m_scene.createBlitPass(*rb, *rb, "blitpass"); + const ramses::BlitPass* blitPass = m_scene.createBlitPass(*rb, *rb, "blitpass"); EXPECT_TRUE(nullptr == blitPass); } @@ -1333,16 +1356,16 @@ namespace ramses const ArrayBuffer* geometryBuffer = m_scene.createArrayBuffer(ramses::EDataType::Vector3F, 3); ASSERT_TRUE(nullptr != geometryBuffer); - const PickableObject* pickableObject = m_scene.createPickableObject(*geometryBuffer, pickableObjectId_t(1u)); + const ramses::PickableObject* pickableObject = m_scene.createPickableObject(*geometryBuffer, pickableObjectId_t(1u)); ASSERT_NE(nullptr, pickableObject); } TEST_F(AScene, cannotCreatePickableObjectWithGeometryBufferOfAnotherScene) { - Scene* anotherScene = client.createScene(sceneId_t(111u)); + ramses::Scene* anotherScene = client.createScene(SceneConfig(sceneId_t(111u))); const ArrayBuffer* geometryBufferFromOtherScene = anotherScene->createArrayBuffer(ramses::EDataType::Vector3F, 3); - const PickableObject* pickableObject = m_scene.createPickableObject(*geometryBufferFromOtherScene, pickableObjectId_t(1u)); + const ramses::PickableObject* pickableObject = m_scene.createPickableObject(*geometryBufferFromOtherScene, pickableObjectId_t(1u)); EXPECT_EQ(nullptr, pickableObject); } @@ -1351,7 +1374,7 @@ namespace ramses const ArrayBuffer* geometryBuffer = m_scene.createArrayBuffer(ramses::EDataType::Vector3F, 2u); ASSERT_TRUE(nullptr != geometryBuffer); - const PickableObject* pickableObject = m_scene.createPickableObject(*geometryBuffer, pickableObjectId_t(1u)); + const ramses::PickableObject* pickableObject = m_scene.createPickableObject(*geometryBuffer, pickableObjectId_t(1u)); EXPECT_EQ(nullptr, pickableObject); } @@ -1396,7 +1419,7 @@ namespace ramses TEST_F(AScene, canCreateInterleavedVertexDataBuffer) { - ArrayBuffer* const interleavedVertexBuffer = m_scene.createArrayBuffer(EDataType::ByteBlob, 14u); + ArrayBuffer* const interleavedVertexBuffer = m_scene.createArrayBuffer(ramses::EDataType::ByteBlob, 14u); ASSERT_NE(nullptr, interleavedVertexBuffer); EXPECT_EQ(ramses::EDataType::ByteBlob, interleavedVertexBuffer->getDataType()); @@ -1408,21 +1431,21 @@ namespace ramses TEST_F(AScene, flushIncreasesStatisticCounter) { - EXPECT_EQ(0u, m_scene.m_impl.getStatisticCollection().statFlushesTriggered.getCounterValue()); + EXPECT_EQ(0u, m_scene.impl().getStatisticCollection().statFlushesTriggered.getCounterValue()); m_scene.flush(); - EXPECT_EQ(1u, m_scene.m_impl.getStatisticCollection().statFlushesTriggered.getCounterValue()); + EXPECT_EQ(1u, m_scene.impl().getStatisticCollection().statFlushesTriggered.getCounterValue()); } TEST_F(AScene, canGetResourceByID) { - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr != effectFixture); const resourceId_t resourceID = effectFixture->getResourceId(); ramses::Resource* resource = m_scene.getResource(resourceID); ASSERT_TRUE(nullptr != resource); - const ramses::Effect* effectFound = RamsesUtils::TryConvert(*resource); + const auto* effectFound = resource->as(); ASSERT_TRUE(nullptr != effectFound); ASSERT_TRUE(effectFound == effectFixture); @@ -1433,7 +1456,7 @@ namespace ramses TEST_F(AScene, returnsNULLWhenResourceWithIDCannotBeFound) { - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr != effectFixture); const resourceId_t nonExistEffectId = { 0, 0 }; @@ -1442,7 +1465,7 @@ namespace ramses TEST_F(AScene, returnsNULLWhenTryingToFindDeletedResource) { - auto effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + auto effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr != effectFixture); const resourceId_t resourceID = effectFixture->getResourceId(); @@ -1458,14 +1481,14 @@ namespace ramses // effect from string: valid uses TEST_F(AScene, createEffectFromGLSLString_withName) { - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr != effectFixture); } TEST_F(AScene, createEffectFromGLSLString_withDefines) { effectDescriptionEmpty.addCompilerDefine("float dummy;"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr != effectFixture); } @@ -1473,35 +1496,35 @@ namespace ramses TEST_F(AScene, createEffectFromGLSLString_invalidVertexShader) { effectDescriptionEmpty.setVertexShader("void main(void) {dsadsadasd}"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr == effectFixture); } TEST_F(AScene, createEffectFromGLSLString_emptyVertexShader) { effectDescriptionEmpty.setVertexShader(""); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr == effectFixture); } TEST_F(AScene, createEffectFromGLSLString_invalidFragmentShader) { effectDescriptionEmpty.setFragmentShader("void main(void) {dsadsadasd}"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr == effectFixture); } TEST_F(AScene, createEffectFromGLSLString_emptyFragmentShader) { effectDescriptionEmpty.setFragmentShader(""); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr == effectFixture); } TEST_F(AScene, createEffectFromGLSLString_invalidDefines) { effectDescriptionEmpty.addCompilerDefine("thisisinvalidstuff\n8fd7f9ds"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr == effectFixture); } @@ -1514,7 +1537,7 @@ namespace ramses "gl_Position = someMatrix * vec4(1.0);" "}"); effectDescriptionEmpty.setUniformSemantic("someMatrix", ramses::EEffectUniformSemantic::ProjectionMatrix); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_TRUE(nullptr != effectFixture); } @@ -1527,7 +1550,7 @@ namespace ramses "gl_Position = someMatrix * vec4(1.0);" "}"); effectDescriptionEmpty.setUniformSemantic("someMatrix", ramses::EEffectUniformSemantic::ProjectionMatrix); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDescriptionEmpty, "name"); EXPECT_FALSE(nullptr != effectFixture); } @@ -1537,7 +1560,7 @@ namespace ramses ramses::EffectDescription effectDesc; effectDesc.setVertexShaderFromFile("res/ramses-client-test_minimalShader.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, "name"); EXPECT_TRUE(nullptr != effectFixture); } @@ -1548,7 +1571,7 @@ namespace ramses ramses::EffectDescription effectDesc; effectDesc.setVertexShaderFromFile("res/this_file_should_not_exist_fdsfdsjf84w9wufw.vert"); effectDesc.setFragmentShaderFromFile("res/ramses-client-test_minimalShader.frag"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, "name"); EXPECT_TRUE(nullptr == effectFixture); } @@ -1557,7 +1580,7 @@ namespace ramses ramses::EffectDescription effectDesc; effectDesc.setVertexShaderFromFile("res/ramses-client-test_minimalShader.frag"); effectDesc.setFragmentShaderFromFile("res/this_file_should_not_exist_fdsfdsjf84w9wufw.vert"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, "name"); EXPECT_TRUE(nullptr == effectFixture); } @@ -1566,7 +1589,7 @@ namespace ramses ramses::EffectDescription effectDesc; effectDesc.setVertexShaderFromFile(""); effectDesc.setFragmentShaderFromFile("res/this_file_should_not_exist_fdsfdsjf84w9wufw.vert"); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, "name"); EXPECT_TRUE(nullptr == effectFixture); } @@ -1575,80 +1598,113 @@ namespace ramses ramses::EffectDescription effectDesc; effectDesc.setVertexShaderFromFile("res/this_file_should_not_exist_fdsfdsjf84w9wufw.vert"); effectDesc.setFragmentShaderFromFile(""); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, "name"); EXPECT_TRUE(nullptr == effectFixture); } TEST_F(AScene, verifyHLAPILogCanHandleNullPtrReturnWhenEnabled) { - ramses_internal::ELogLevel oldLogLevel = ramses_internal::CONTEXT_HLAPI_CLIENT.getLogLevel(); - ramses_internal::CONTEXT_HLAPI_CLIENT.setLogLevel(ramses_internal::ELogLevel::Trace); + ELogLevel oldLogLevel = ramses::internal::CONTEXT_HLAPI_CLIENT.getLogLevel(); + ramses::internal::CONTEXT_HLAPI_CLIENT.setLogLevel(ELogLevel::Trace); ramses::EffectDescription effectDesc; effectDesc.setVertexShaderFromFile("res/this_file_should_not_exist_fdsfdsjf84w9wufw.vert"); effectDesc.setFragmentShaderFromFile(""); - const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "name"); + const ramses::Effect* effectFixture = m_scene.createEffect(effectDesc, "name"); EXPECT_TRUE(nullptr == effectFixture); - ramses_internal::CONTEXT_HLAPI_CLIENT.setLogLevel(oldLogLevel); + ramses::internal::CONTEXT_HLAPI_CLIENT.setLogLevel(oldLogLevel); } TEST_F(AScene, returnsFalseOnFlushWhenResourcesAreMissing) { m_creationHelper.createObjectOfType("meh"); - EXPECT_EQ(StatusOK, m_scene.flush()); // test legal scene state => flush success - m_scene.destroy(*RamsesUtils::TryConvert(*m_scene.findObjectByName("appearance effect"))); - m_scene.destroy(*RamsesUtils::TryConvert(*m_scene.findObjectByName("meh"))); - EXPECT_EQ(StatusOK, m_scene.flush()); // resource is deleted, but nobody needs it => flush success + EXPECT_TRUE(m_scene.flush()); // test legal scene state => flush success + m_scene.destroy(*m_scene.findObject("appearance effect")); + m_scene.destroy(*m_scene.findObject("meh")); + EXPECT_TRUE(m_scene.flush()); // resource is deleted, but nobody needs it => flush success auto* appearnace = m_creationHelper.createObjectOfType("meh2"); auto* mesh = m_creationHelper.createObjectOfType("meh3"); mesh->setAppearance(*appearnace); - m_scene.destroy(*RamsesUtils::TryConvert(*m_scene.findObjectByName("appearance effect"))); - EXPECT_NE(StatusOK, m_scene.flush()); // test scene with resource missing => flush failed + m_scene.destroy(*m_scene.findObject("appearance effect")); + EXPECT_FALSE(m_scene.flush()); // test scene with resource missing => flush failed } TEST_F(AScene, uniformTimeMatchesSyncClockInitiallyAndCanBeReset) { const int32_t tolerance = 10000; // 10 seconds to tolerate stucks during test execution - const auto now = ramses_internal::PlatformTime::GetMillisecondsSynchronized(); + const auto now = ramses::internal::PlatformTime::GetMillisecondsSynchronized(); const auto s32now = static_cast(now % std::numeric_limits::max()); EXPECT_GE(tolerance, GetElapsed(s32now, m_scene.getUniformTimeMs())); - EXPECT_EQ(StatusOK, m_scene.resetUniformTimeMs()); + EXPECT_TRUE(m_scene.resetUniformTimeMs()); EXPECT_GE(tolerance, GetElapsed(0, m_scene.getUniformTimeMs())); } TEST_F(AScene, resetUniformTimeMs) { - EXPECT_EQ(ramses_internal::FlushTime::InvalidTimestamp, m_scene.m_impl.getIScene().getEffectTimeSync()); - EXPECT_EQ(StatusOK, m_scene.resetUniformTimeMs()); - const auto timeSync = m_scene.m_impl.getIScene().getEffectTimeSync(); + EXPECT_EQ(ramses::internal::FlushTime::InvalidTimestamp, m_scene.impl().getIScene().getEffectTimeSync()); + EXPECT_TRUE(m_scene.resetUniformTimeMs()); + const auto timeSync = m_scene.impl().getIScene().getEffectTimeSync(); EXPECT_NE(0, timeSync.time_since_epoch().count()); - EXPECT_EQ(StatusOK, m_scene.flush()); + EXPECT_TRUE(m_scene.flush()); EXPECT_CALL(sceneActionsCollector, handleNewSceneAvailable(_, _)); + EXPECT_TRUE(m_scene.publish(ramses::EScenePublicationMode::LocalOnly)); + + // first flush sends scene init EXPECT_CALL(sceneActionsCollector, handleInitializeScene(_, _)); // internal timestamp contains sync time - EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(123u), _, _)).WillOnce([&](auto, const auto& update, auto) { - const ramses_internal::FlushTimeInformation& timeInfo = update.flushInfos.flushTimeInfo; + ramses::internal::PlatformThread::Sleep(10); + EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(123u), _, _)).WillOnce([&](auto /*unused*/, const auto& update, auto /*unused*/) { + const ramses::internal::FlushTimeInformation& timeInfo = update.flushInfos.flushTimeInfo; EXPECT_TRUE(timeInfo.isEffectTimeSync); EXPECT_EQ(timeSync, timeInfo.internalTimestamp); }); - - EXPECT_EQ(StatusOK, m_scene.publish(ramses::EScenePublicationMode::LocalOnly)); - Mock::VerifyAndClearExpectations(&sceneActionsCollector); + EXPECT_TRUE(m_scene.flush(42u)); // internal timestamp contains flush time - ramses_internal::PlatformThread::Sleep(10); - EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses_internal::SceneId(123u), _, _)).WillOnce([&](auto, const auto& update, auto) { - const ramses_internal::FlushTimeInformation& timeInfo = update.flushInfos.flushTimeInfo; + ramses::internal::PlatformThread::Sleep(10); + EXPECT_CALL(sceneActionsCollector, handleSceneUpdate_rvr(ramses::internal::SceneId(123u), _, _)).WillOnce([&](auto /*unused*/, const auto& update, auto /*unused*/) { + const ramses::internal::FlushTimeInformation& timeInfo = update.flushInfos.flushTimeInfo; EXPECT_FALSE(timeInfo.isEffectTimeSync); EXPECT_NE(timeSync, timeInfo.internalTimestamp); }); - EXPECT_EQ(StatusOK, m_scene.flush(42u)); + EXPECT_TRUE(m_scene.flush(43u)); EXPECT_CALL(sceneActionsCollector, handleSceneBecameUnavailable(_, _)); } + + TEST_F(AScene, canFindByNameWithSameNameUsedForDifferentTypes) + { + auto node = m_scene.createNode("test"); + const auto camNode1 = m_scene.createOrthographicCamera("test"); + const auto camNode2 = m_scene.createPerspectiveCamera("test"); + const auto arrBuffer = m_scene.createArrayBuffer(ramses::EDataType::Float, 1u, "test"); + ASSERT_TRUE(node); + ASSERT_TRUE(camNode1); + ASSERT_TRUE(camNode2); + ASSERT_TRUE(arrBuffer); + + // concrete types + EXPECT_EQ(camNode1, m_scene.findObject("test")); + EXPECT_EQ(camNode2, m_scene.findObject("test")); + EXPECT_EQ(arrBuffer, m_scene.findObject("test")); + + // this must be one of the 2 cameras + EXPECT_THAT(m_scene.findObject("test"), AnyOf(camNode1, camNode2)); + + // this must be one of the 3 nodes + EXPECT_THAT(m_scene.findObject("test"), AnyOf(node, camNode1, camNode2)); + + EXPECT_TRUE(node->setName("other")); + // now this must be one of the 2 cameras + EXPECT_THAT(m_scene.findObject("test"), AnyOf(camNode1, camNode2)); + + EXPECT_TRUE(m_scene.destroy(*node)); + // also now this must be one of the 2 cameras + EXPECT_THAT(m_scene.findObject("test"), AnyOf(camNode1, camNode2)); + } } diff --git a/client/test/SerializationHelperTest.cpp b/tests/unittests/client/SerializationHelperTest.cpp similarity index 89% rename from client/test/SerializationHelperTest.cpp rename to tests/unittests/client/SerializationHelperTest.cpp index ff15d9d13..ec92b36df 100644 --- a/client/test/SerializationHelperTest.cpp +++ b/tests/unittests/client/SerializationHelperTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SerializationHelper.h" +#include "impl/SerializationHelper.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { // TODO: write tests for methods } diff --git a/client/test/SimpleSceneTopology.h b/tests/unittests/client/SimpleSceneTopology.h similarity index 66% rename from client/test/SimpleSceneTopology.h rename to tests/unittests/client/SimpleSceneTopology.h index ee943ca32..3c896db4e 100644 --- a/client/test/SimpleSceneTopology.h +++ b/tests/unittests/client/SimpleSceneTopology.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SIMPLESCENETOPOLOGY_H -#define RAMSES_SIMPLESCENETOPOLOGY_H +#pragma once #include "gtest/gtest.h" #include "ClientTestUtils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Node.h" +#include "ramses/client/MeshNode.h" -#include "MeshNodeImpl.h" +#include "impl/MeshNodeImpl.h" -namespace ramses +namespace ramses::internal { class SimpleSceneTopology : public LocalTestClientWithScene, public ::testing::Test { @@ -42,18 +41,18 @@ namespace ramses , m_mesh2a(*m_scene.createMeshNode("mesh2a")) , m_mesh2b(*m_scene.createMeshNode("mesh2b")) { - EXPECT_EQ(StatusOK, m_root.addChild(m_vis1)); - EXPECT_EQ(StatusOK, m_root.addChild(m_vis2)); - EXPECT_EQ(StatusOK, m_vis1.addChild(m_mesh1a)); - EXPECT_EQ(StatusOK, m_vis1.addChild(m_mesh1b)); - EXPECT_EQ(StatusOK, m_vis2.addChild(m_mesh2a)); - EXPECT_EQ(StatusOK, m_vis2.addChild(m_mesh2b)); + EXPECT_TRUE(m_root.addChild(m_vis1)); + EXPECT_TRUE(m_root.addChild(m_vis2)); + EXPECT_TRUE(m_vis1.addChild(m_mesh1a)); + EXPECT_TRUE(m_vis1.addChild(m_mesh1b)); + EXPECT_TRUE(m_vis2.addChild(m_mesh2a)); + EXPECT_TRUE(m_vis2.addChild(m_mesh2b)); //Every Mesh should be visible by default - EXPECT_EQ(m_mesh1a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh1b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2a.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); - EXPECT_EQ(m_mesh2b.m_impl.getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh1b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2a.impl().getFlattenedVisibility(), EVisibilityMode::Visible); + EXPECT_EQ(m_mesh2b.impl().getFlattenedVisibility(), EVisibilityMode::Visible); } Node& m_root; @@ -65,5 +64,3 @@ namespace ramses MeshNode& m_mesh2b; }; } - -#endif diff --git a/client/test/TestEffectCreator.h b/tests/unittests/client/TestEffectCreator.h similarity index 88% rename from client/test/TestEffectCreator.h rename to tests/unittests/client/TestEffectCreator.h index 0ab6987cc..892dad8a2 100644 --- a/client/test/TestEffectCreator.h +++ b/tests/unittests/client/TestEffectCreator.h @@ -6,21 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTEFFECTCREATOR_H -#define RAMSES_TESTEFFECTCREATOR_H +#pragma once #include -#include "ramses-client-api/EffectDescription.h" +#include "ramses/client/EffectDescription.h" #include "ClientTestUtils.h" -namespace ramses +namespace ramses::internal { class TestEffectCreator : public LocalTestClientWithScene { public: explicit TestEffectCreator(bool withSemantics = false, bool withGeometryShader = false) - : LocalTestClientWithScene() { effect = createEffect(m_scene, withSemantics, withGeometryShader); EXPECT_TRUE(effect != nullptr); @@ -38,10 +36,10 @@ namespace ramses ~TestEffectCreator() override { - EXPECT_EQ(StatusOK, this->m_scene.destroy(*appearance)); + EXPECT_TRUE(this->m_scene.destroy(*appearance)); } - static Effect* createEffect(Scene& scene, bool withSemantics, bool withGeometryShader = false) + static Effect* createEffect(ramses::Scene& scene, bool withSemantics, bool withGeometryShader = false) { std::string VertexShader( "#version 320 es\n" @@ -62,6 +60,9 @@ namespace ramses "uniform mat4 matrix44fInput;\n" "uniform mat4 matrix44fInputArray[3];\n" + "uniform bool boolInput;\n" + "uniform bool boolInputArray[3];\n" + "uniform lowp int integerInput;\n" "uniform lowp int integerInputArray[3];\n" @@ -89,7 +90,8 @@ namespace ramses " values[2] += float(vec2iInputArray[0].x + vec3iInputArray[0].x + vec4iInputArray[0].x);\n" " values[3] += matrix22fInput[0][0] + matrix33fInput[0][0] + matrix22fInputArray[0][0].x + matrix33fInputArray[0][0].x;\n" " values = matrix44fInput * values;\n" - " gl_Position = values;\n" + " if (boolInput || boolInputArray[0] || boolInputArray[1] || boolInputArray[2])\n" + " gl_Position = values;\n" "}\n"); std::string FragmentShader( @@ -107,7 +109,7 @@ namespace ramses " FragColor = vec4(1.0) + texture(texture2dInput, vec2(0,0)) + texelFetch(texture2dMSInput, ivec2(0,0), 0) + texture(texture3dInput, vec3(0, 0, 0)) + texture(textureCubeInput, vec3(0,0,0)) + texture(textureExternalInput, vec2(0,0));\n" "}\n"); - ramses::EffectDescription effectDesc; + EffectDescription effectDesc; effectDesc.setVertexShader(VertexShader.c_str()); effectDesc.setFragmentShader(FragmentShader.c_str()); @@ -126,17 +128,15 @@ namespace ramses if (withSemantics) { - effectDesc.setAttributeSemantic("vec2fArrayInput", ramses::EEffectAttributeSemantic::TextPositions); - effectDesc.setUniformSemantic("matrix44fInput", ramses::EEffectUniformSemantic::ModelViewMatrix); - effectDesc.setUniformSemantic("texture2dInput", ramses::EEffectUniformSemantic::TextTexture); + effectDesc.setAttributeSemantic("vec2fArrayInput", EEffectAttributeSemantic::TextPositions); + effectDesc.setUniformSemantic("matrix44fInput", EEffectUniformSemantic::ModelViewMatrix); + effectDesc.setUniformSemantic("texture2dInput", EEffectUniformSemantic::TextTexture); } - return scene.createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "input test effect"); + return scene.createEffect(effectDesc, "input test effect"); } Effect* effect; Appearance* appearance; }; } - -#endif diff --git a/client/test/TestEffects.h b/tests/unittests/client/TestEffects.h similarity index 58% rename from client/test/TestEffects.h rename to tests/unittests/client/TestEffects.h index 5cb56a025..ba857a356 100644 --- a/client/test/TestEffects.h +++ b/tests/unittests/client/TestEffects.h @@ -6,19 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTEFFECTS_H -#define RAMSES_TESTEFFECTS_H +#pragma once -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/EffectDescription.h" -#include "RamsesClientImpl.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/EffectDescription.h" +#include "impl/RamsesClientImpl.h" -namespace ramses +#include + +namespace ramses::internal { class TestEffects { public: - static Effect* CreateTestEffect(Scene& scene, std::string_view name = {}, resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache) + static Effect* CreateTestEffect(ramses::Scene& scene, std::string_view name = {}) { EffectDescription effectDesc; effectDesc.setVertexShader( @@ -34,12 +35,12 @@ namespace ramses "{\n" " gl_FragColor = vec4(u_FragColorR, u_FragColorG, 0.0, 0.0); \n" "}\n"); - Effect* effect = scene.createEffect(effectDesc, cacheFlag, name); + Effect* effect = scene.createEffect(effectDesc, name); assert(effect != nullptr); return effect; } - static Effect* CreateDifferentTestEffect(Scene& scene, std::string_view name = {}, resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache) + static Effect* CreateDifferentTestEffect(ramses::Scene& scene, std::string_view name = {}) { EffectDescription effectDesc; effectDesc.setVertexShader( @@ -52,12 +53,12 @@ namespace ramses "{\n" " gl_FragColor = vec4(1.0); \n" "}\n"); - Effect* effect = scene.createEffect(effectDesc, cacheFlag, name); + Effect* effect = scene.createEffect(effectDesc, name); assert(effect != nullptr); return effect; } - static Effect* CreateTestEffectWithAttribute(Scene& scene, std::string_view name = {}, resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache) + static Effect* CreateTestEffectWithAttribute(ramses::Scene& scene, std::string_view name = {}) { EffectDescription effectDesc; effectDesc.setVertexShader( @@ -74,12 +75,56 @@ namespace ramses "{\n" " gl_FragColor = vec4(0.0); \n" "}\n"); - Effect* effect = scene.createEffect(effectDesc, cacheFlag, name); + Effect* effect = scene.createEffect(effectDesc, name); + assert(effect != nullptr); + return effect; + } + + static Effect* CreateTestEffectWithAllStages(ramses::Scene& scene, std::string_view name = {}) + { + const char* vs = R"SHADER( + #version 320 es + in vec3 a_position1; + in float a_position2; + in float a_position3; + uniform highp float vs_uniform; + void main(void) + { + gl_Position = vec4(vs_uniform, a_position1.y, a_position2, a_position3); + } + )SHADER"; + + const char* gs = R"SHADER( + #version 320 es + layout(lines) in; + layout(points, max_vertices = 1) out; + uniform highp float gs_uniform; + void main() { + gl_Position = vec4(gs_uniform, 0.0, 0.0, 1.0); + EmitVertex(); + } + )SHADER"; + + const char* fs = R"SHADER( + #version 320 es + uniform highp vec2 colorRG; + uniform highp float colorBA[2]; + out lowp vec4 colorOut; + void main(void) + { + colorOut = vec4(colorRG, colorBA[0], colorBA[1]); + })SHADER"; + + EffectDescription effectDesc; + effectDesc.setVertexShader(vs); + effectDesc.setFragmentShader(fs); + effectDesc.setGeometryShader(gs); + Effect* effect = scene.createEffect(effectDesc, name); assert(effect != nullptr); return effect; } - static Effect* CreateTestEffectWithAllStages(Scene& scene, std::string_view name = {}, resourceCacheFlag_t cacheFlag = ResourceCacheFlag_DoNotCache) + static Effect* CreateTestEffectWithAllStagesWithWarnings(ramses::Scene& scene, std::string_view name = {}) { const char* vs = R"SHADER( #version 320 es @@ -87,9 +132,13 @@ namespace ramses in float a_position2; in float a_position3; uniform highp float vs_uniform; + + out lowp vec3 v_texcoord; + void main(void) { gl_Position = vec4(vs_uniform, a_position1.y, a_position2, a_position3); + v_texcoord = a_position1; } )SHADER"; @@ -109,6 +158,10 @@ namespace ramses uniform highp vec2 colorRG; uniform highp float colorBA[2]; out lowp vec4 colorOut; + + // unused variable with wrong type: vec2 != vec3 + in highp vec2 v_texcoord; + void main(void) { colorOut = vec4(colorRG, colorBA[0], colorBA[1]); @@ -118,11 +171,9 @@ namespace ramses effectDesc.setVertexShader(vs); effectDesc.setFragmentShader(fs); effectDesc.setGeometryShader(gs); - Effect* effect = scene.createEffect(effectDesc, cacheFlag, name); + Effect* effect = scene.createEffect(effectDesc, name); assert(effect != nullptr); return effect; } }; } - -#endif diff --git a/client/test/Texture2DBufferTest.cpp b/tests/unittests/client/Texture2DBufferTest.cpp similarity index 65% rename from client/test/Texture2DBufferTest.cpp rename to tests/unittests/client/Texture2DBufferTest.cpp index b162de09f..52a8d2c0f 100644 --- a/client/test/Texture2DBufferTest.cpp +++ b/tests/unittests/client/Texture2DBufferTest.cpp @@ -10,17 +10,17 @@ #include #include "ClientTestUtils.h" -#include "SceneImpl.h" -#include "ramses-utils.h" -#include "Scene/ClientScene.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "Texture2DBufferImpl.h" +#include "impl/SceneImpl.h" +#include "ramses/client/ramses-utils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "ramses/client/Texture2DBuffer.h" +#include "impl/Texture2DBufferImpl.h" #include "UnsafeTestMemoryHelpers.h" using namespace testing; -using namespace ramses_internal; +using namespace ramses::internal; -namespace ramses +namespace ramses::internal { class ATexture2DBuffer : public LocalTestClientWithScene, public testing::Test { @@ -29,10 +29,10 @@ namespace ramses TEST_F(ATexture2DBuffer, IsAllocatedOnInternalSceneAfterCreation) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); - const ramses_internal::TextureBufferHandle textureBufferHandle = textureBuffer.m_impl.getTextureBufferHandle(); + const ramses::internal::TextureBufferHandle textureBufferHandle = textureBuffer.impl().getTextureBufferHandle(); EXPECT_TRUE(textureBufferHandle.isValid()); - EXPECT_TRUE(this->m_scene.m_impl.getIScene().isTextureBufferAllocated(textureBufferHandle)); + EXPECT_TRUE(this->m_scene.impl().getIScene().isTextureBufferAllocated(textureBufferHandle)); } TEST_F(ATexture2DBuffer, CanGetTexelFormat) @@ -45,10 +45,10 @@ namespace ramses TEST_F(ATexture2DBuffer, PropagatesItsPropertiesToInternalScene) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); - const ramses_internal::TextureBufferHandle textureBufferHandle = textureBuffer.m_impl.getTextureBufferHandle(); - const ramses_internal::TextureBuffer& internalTexBuffer = this->m_scene.m_impl.getIScene().getTextureBuffer(textureBufferHandle); + const ramses::internal::TextureBufferHandle textureBufferHandle = textureBuffer.impl().getTextureBufferHandle(); + const ramses::internal::TextureBuffer& internalTexBuffer = this->m_scene.impl().getIScene().getTextureBuffer(textureBufferHandle); - EXPECT_EQ(ramses_internal::ETextureFormat::RGBA8, internalTexBuffer.textureFormat); + EXPECT_EQ(ramses::internal::EPixelStorageFormat::RGBA8, internalTexBuffer.textureFormat); ASSERT_EQ(2u, internalTexBuffer.mipMaps.size()); EXPECT_EQ(3u, internalTexBuffer.mipMaps[0].width); EXPECT_EQ(4u, internalTexBuffer.mipMaps[0].height); @@ -85,11 +85,11 @@ namespace ramses TEST_F(ATexture2DBuffer, PropagatesDataUpdatesToInternalScene) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); - const ramses_internal::TextureBufferHandle textureBufferHandle = textureBuffer.m_impl.getTextureBufferHandle(); + const ramses::internal::TextureBufferHandle textureBufferHandle = textureBuffer.impl().getTextureBufferHandle(); // update mipLevel = 0 - EXPECT_EQ(StatusOK, textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); - const Byte* textureBufferDataMip0 = this->m_scene.m_impl.getIScene().getTextureBuffer(textureBufferHandle).mipMaps[0].data.data(); + EXPECT_TRUE(textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); + const std::byte* textureBufferDataMip0 = this->m_scene.impl().getIScene().getTextureBuffer(textureBufferHandle).mipMaps[0].data.data(); EXPECT_EQ(12u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(textureBufferDataMip0, 0)); EXPECT_EQ(23u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(textureBufferDataMip0, 1)); @@ -97,8 +97,8 @@ namespace ramses EXPECT_EQ(56u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(textureBufferDataMip0, 3 * 1 + 1)); // update mipLevel = 1 - EXPECT_EQ(StatusOK, textureBuffer.updateData(1, 0, 0, 1, 1, std::array{ {78} }.data())); - const Byte* textureBufferDataMip1 = this->m_scene.m_impl.getIScene().getTextureBuffer(textureBufferHandle).mipMaps[1].data.data(); + EXPECT_TRUE(textureBuffer.updateData(1, 0, 0, 1, 1, std::array{ {78} }.data())); + const std::byte* textureBufferDataMip1 = this->m_scene.impl().getIScene().getTextureBuffer(textureBufferHandle).mipMaps[1].data.data(); EXPECT_EQ(78u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(textureBufferDataMip1, 0)); } @@ -107,9 +107,9 @@ namespace ramses Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); // update mipLevel = 0 - EXPECT_EQ(StatusOK, textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); + EXPECT_TRUE(textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); - std::array mipLevel0RetrievedData; + std::array mipLevel0RetrievedData{}; textureBuffer.getMipLevelData(0u, mipLevel0RetrievedData.data(), textureBuffer.getMipLevelDataSizeInBytes(0u)); EXPECT_EQ(12u, mipLevel0RetrievedData[0]); @@ -118,9 +118,9 @@ namespace ramses EXPECT_EQ(56u, mipLevel0RetrievedData[3 * 1 + 1]); // update mipLevel = 1 - EXPECT_EQ(StatusOK, textureBuffer.updateData(1, 0, 0, 1, 1, std::array{ {78} }.data())); + EXPECT_TRUE(textureBuffer.updateData(1, 0, 0, 1, 1, std::array{ {78} }.data())); - std::array mipLevel1RetrievedData; + std::array mipLevel1RetrievedData{}; textureBuffer.getMipLevelData(1u, mipLevel1RetrievedData.data(), textureBuffer.getMipLevelDataSizeInBytes(1u)); EXPECT_EQ(78u, mipLevel1RetrievedData[0]); @@ -130,14 +130,14 @@ namespace ramses { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); - EXPECT_EQ(StatusOK, textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); - std::array mipLevel0RetrievedData; + EXPECT_TRUE(textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); + std::array mipLevel0RetrievedData{}; textureBuffer.getMipLevelData(0u, &mipLevel0RetrievedData, sizeof(uint32_t) * 2u); EXPECT_EQ(12u, mipLevel0RetrievedData[0]); EXPECT_EQ(23u, mipLevel0RetrievedData[1]); - EXPECT_EQ(StatusOK, textureBuffer.updateData(1, 0, 0, 1, 2, std::array{ {78, 87} }.data())); - uint32_t mipLevel1RetrievedData; + EXPECT_TRUE(textureBuffer.updateData(1, 0, 0, 1, 2, std::array{ {78, 87} }.data())); + uint32_t mipLevel1RetrievedData{}; textureBuffer.getMipLevelData(1u, &mipLevel1RetrievedData, sizeof(uint32_t)); EXPECT_EQ(78u, mipLevel1RetrievedData); } @@ -146,10 +146,10 @@ namespace ramses { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 0, 0, 0, 0, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 2, 2, 0, 0, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(1, 0, 0, 0, 0, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(1, 1, 1, 0, 0, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 0, 0, 0, 0, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 2, 2, 0, 0, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(1, 0, 0, 0, 0, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(1, 1, 1, 0, 0, std::array().data())); } TEST_F(ATexture2DBuffer, CanNotBeUpdatedWithDataSizeBiggerThanMaximumSize) @@ -157,26 +157,26 @@ namespace ramses Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); // offset exceeds total size - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 3, 0, 1, 1, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 0, 4, 1, 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 3, 0, 1, 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 0, 4, 1, 1, std::array().data())); // width/height exceed total size - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 0, 0, 3+1, 1, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 0, 0, 1, 4 + 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 0, 0, 3+1, 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 0, 0, 1, 4 + 1, std::array().data())); // offset + width/height exceed total size - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 1, 0, 3, 1, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 0, 1, 1, 4, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 1, 0, 3, 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 0, 1, 1, 4, std::array().data())); // second mipmap - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 1, 0, 3, 1, std::array().data())); - EXPECT_NE(StatusOK, textureBuffer.updateData(0, 0, 1, 1, 4, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 1, 0, 3, 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(0, 0, 1, 1, 4, std::array().data())); } TEST_F(ATexture2DBuffer, CanNotBeUpdatedForUnexistingMipMapLevel) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 3, 4, 2); - EXPECT_NE(StatusOK, textureBuffer.updateData(3, 0, 0, 1, 1, std::array().data())); + EXPECT_FALSE(textureBuffer.updateData(3, 0, 0, 1, 1, std::array().data())); } TEST_F(ATexture2DBuffer, RetrievesMipMapCount) @@ -191,16 +191,16 @@ namespace ramses const Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::R8, 8, 4, 4); uint32_t width = 0u; uint32_t height = 0u; - EXPECT_EQ(StatusOK, textureBuffer.getMipLevelSize(0, width, height)); + EXPECT_TRUE(textureBuffer.getMipLevelSize(0, width, height)); EXPECT_EQ(8u, width); EXPECT_EQ(4u, height); - EXPECT_EQ(StatusOK, textureBuffer.getMipLevelSize(1, width, height)); + EXPECT_TRUE(textureBuffer.getMipLevelSize(1, width, height)); EXPECT_EQ(4u, width); EXPECT_EQ(2u, height); - EXPECT_EQ(StatusOK, textureBuffer.getMipLevelSize(2, width, height)); + EXPECT_TRUE(textureBuffer.getMipLevelSize(2, width, height)); EXPECT_EQ(2u, width); EXPECT_EQ(1u, height); - EXPECT_EQ(StatusOK, textureBuffer.getMipLevelSize(3, width, height)); + EXPECT_TRUE(textureBuffer.getMipLevelSize(3, width, height)); EXPECT_EQ(1u, width); EXPECT_EQ(1u, height); } @@ -210,28 +210,34 @@ namespace ramses const Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::R8, 8, 4, 4); uint32_t width = 0u; uint32_t height = 0u; - EXPECT_NE(StatusOK, textureBuffer.getMipLevelSize(4, width, height)); + EXPECT_FALSE(textureBuffer.getMipLevelSize(4, width, height)); } TEST_F(ATexture2DBuffer, CanBeValidated) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 4, 4, 1); - EXPECT_EQ(StatusOK, textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); + EXPECT_TRUE(textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureBuffer); - EXPECT_EQ(StatusOK, textureBuffer.validate()); + ValidationReport report; + textureBuffer.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(ATexture2DBuffer, ReportsWarningIfNotUsedInSampler) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 4, 4, 1); - EXPECT_EQ(StatusOK, textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); - EXPECT_NE(StatusOK, textureBuffer.validate()); + EXPECT_TRUE(textureBuffer.updateData(0, 0, 0, 2, 2, std::array{ {12, 23, 34, 56} }.data())); + ValidationReport report; + textureBuffer.validate(report); + EXPECT_TRUE(report.hasIssue()); } TEST_F(ATexture2DBuffer, ReportsWarningIfUsedInSamplerButNotInitialized) { Texture2DBuffer& textureBuffer = *m_scene.createTexture2DBuffer(ETextureFormat::RGBA8, 4, 4, 1); m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureBuffer); - EXPECT_NE(StatusOK, textureBuffer.validate()); + ValidationReport report; + textureBuffer.validate(report); + EXPECT_TRUE(report.hasIssue()); } } diff --git a/client/test/TextureSamplerTest.cpp b/tests/unittests/client/TextureSamplerTest.cpp similarity index 53% rename from client/test/TextureSamplerTest.cpp rename to tests/unittests/client/TextureSamplerTest.cpp index bb7764860..474fad6a9 100644 --- a/client/test/TextureSamplerTest.cpp +++ b/tests/unittests/client/TextureSamplerTest.cpp @@ -10,29 +10,29 @@ #include "ClientTestUtils.h" #include "CreationHelper.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/TextureSamplerMS.h" -#include "ramses-client-api/TextureSamplerExternal.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTarget.h" -#include "ramses-client-api/Texture2D.h" -#include "ramses-client-api/Texture3D.h" -#include "ramses-client-api/TextureCube.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/RenderBuffer.h" -#include "ramses-client-api/RenderTargetDescription.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "TextureSamplerImpl.h" -#include "RenderBufferImpl.h" -#include "Texture2DImpl.h" -#include "Texture3DImpl.h" -#include "TextureCubeImpl.h" -#include "Texture2DBufferImpl.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/TextureSamplerMS.h" +#include "ramses/client/TextureSamplerExternal.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTarget.h" +#include "ramses/client/Texture2D.h" +#include "ramses/client/Texture3D.h" +#include "ramses/client/TextureCube.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/RenderBuffer.h" +#include "ramses/client/RenderTargetDescription.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/OrthographicCamera.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/Texture2DImpl.h" +#include "impl/Texture3DImpl.h" +#include "impl/TextureCubeImpl.h" +#include "impl/Texture2DBufferImpl.h" using namespace testing; -namespace ramses +namespace ramses::internal { class TextureSamplerTest : public LocalTestClientWithScene, public ::testing::Test { @@ -41,9 +41,9 @@ namespace ramses TEST_F(TextureSamplerTest, createSamplerForTexture2D) { const Texture2D& texture2D = createObject("testTexture2D"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u, "testSampler2D"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u, "testSampler2D"); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Linear, sampler->getMinSamplingMethod()); @@ -51,19 +51,19 @@ namespace ramses EXPECT_EQ(1u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::Texture2D, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); - const auto texHash = texture2D.m_impl.getLowlevelResourceHash(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); + const auto texHash = texture2D.impl().getLowlevelResourceHash(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); EXPECT_EQ(texHash, m_internalScene.getTextureSampler(samplerHandle).textureResource); } TEST_F(TextureSamplerTest, createSamplerForTexture2DWithAnisotropy) { const Texture2D& texture2D = createObject("testTexture2D"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 16u, "testSampler2D"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 16u, "testSampler2D"); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Linear, sampler->getMinSamplingMethod()); @@ -75,9 +75,9 @@ namespace ramses TEST_F(TextureSamplerTest, createSamplerForTexture3D) { const Texture3D& texture3D = createObject("testTexture3D"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D, "testSampler3D"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D, "testSampler3D"); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureAddressMode::Mirror, sampler->getWrapRMode()); @@ -90,9 +90,9 @@ namespace ramses TEST_F(TextureSamplerTest, createSamplerForTextureCube) { const TextureCube& textureCube = createObject("testTextureCube"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube, 1u, "testSamplerCube"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube, 1u, "testSamplerCube"); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Linear, sampler->getMinSamplingMethod()); @@ -104,9 +104,9 @@ namespace ramses TEST_F(TextureSamplerTest, createSamplerForTextureCubeWithAnisotropy) { const TextureCube& textureCube = createObject("testTextureCube"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube, 16u, "testSamplerCube"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube, 16u, "testSamplerCube"); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Linear, sampler->getMinSamplingMethod()); @@ -117,10 +117,10 @@ namespace ramses TEST_F(TextureSamplerTest, createSamplerForRenderBuffer) { - const RenderBuffer& renderBuffer = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, renderBuffer, 16u); + const ramses::RenderBuffer& renderBuffer = createObject(); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, renderBuffer, 16u); - ASSERT_NE(static_cast(nullptr), sampler); + ASSERT_NE(static_cast(nullptr), sampler); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); EXPECT_EQ(ETextureSamplingMethod::Linear, sampler->getMinSamplingMethod()); @@ -128,24 +128,24 @@ namespace ramses EXPECT_EQ(16u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::RenderBuffer, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); - const ramses_internal::RenderBufferHandle renderBufferHandle = renderBuffer.m_impl.getRenderBufferHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); + const ramses::internal::RenderBufferHandle renderBufferHandle = renderBuffer.impl().getRenderBufferHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::RenderBuffer, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::RenderBuffer, m_internalScene.getTextureSampler(samplerHandle).contentType); EXPECT_EQ(renderBufferHandle.asMemoryHandle(), m_internalScene.getTextureSampler(samplerHandle).contentHandle); } TEST_F(TextureSamplerTest, createSamplerMS) { - const RenderBuffer& renderBuffer = createObject(); + const ramses::RenderBuffer& renderBuffer = createObject(); TextureSamplerMS* sampler = this->m_scene.createTextureSamplerMS(renderBuffer, "renderBuffer"); ASSERT_NE(static_cast(nullptr), sampler); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); - const ramses_internal::RenderBufferHandle renderBufferHandle = renderBuffer.m_impl.getRenderBufferHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); + const ramses::internal::RenderBufferHandle renderBufferHandle = renderBuffer.impl().getRenderBufferHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::RenderBufferMS, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::RenderBufferMS, m_internalScene.getTextureSampler(samplerHandle).contentType); EXPECT_EQ(renderBufferHandle.asMemoryHandle(), m_internalScene.getTextureSampler(samplerHandle).contentHandle); } @@ -154,20 +154,20 @@ namespace ramses const TextureSamplerExternal* sampler = this->m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, "testSampler2DExternal"); ASSERT_NE(static_cast(nullptr), sampler); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); const auto& samplerInternal = m_internalScene.getTextureSampler(samplerHandle); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::ExternalTexture, samplerInternal.contentType); - EXPECT_EQ(ramses_internal::InvalidMemoryHandle, samplerInternal.contentHandle); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::ExternalTexture, samplerInternal.contentType); + EXPECT_EQ(ramses::internal::InvalidMemoryHandle, samplerInternal.contentHandle); EXPECT_FALSE(samplerInternal.textureResource.isValid()); - EXPECT_EQ(ramses_internal::EWrapMethod::Clamp, samplerInternal.states.m_addressModeU); - EXPECT_EQ(ramses_internal::EWrapMethod::Clamp, samplerInternal.states.m_addressModeV); - EXPECT_EQ(ramses_internal::EWrapMethod::Clamp, samplerInternal.states.m_addressModeR); - EXPECT_EQ(ramses_internal::ESamplingMethod::Linear, samplerInternal.states.m_minSamplingMode); - EXPECT_EQ(ramses_internal::ESamplingMethod::Linear, samplerInternal.states.m_magSamplingMode); + EXPECT_EQ(ETextureAddressMode::Clamp, samplerInternal.states.m_addressModeU); + EXPECT_EQ(ETextureAddressMode::Clamp, samplerInternal.states.m_addressModeV); + EXPECT_EQ(ETextureAddressMode::Clamp, samplerInternal.states.m_addressModeR); + EXPECT_EQ(ETextureSamplingMethod::Linear, samplerInternal.states.m_minSamplingMode); + EXPECT_EQ(ETextureSamplingMethod::Linear, samplerInternal.states.m_magSamplingMode); EXPECT_EQ(1u, samplerInternal.states.m_anisotropyLevel); } @@ -181,31 +181,36 @@ namespace ramses { auto sampler1 = m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear); const dataConsumerId_t consumerId{ 123u }; - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler1, consumerId)); - EXPECT_EQ(StatusOK, m_scene.validate()); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler1, consumerId)); + ValidationReport report; + m_scene.validate(report); + EXPECT_FALSE(report.hasIssue()); const auto sampler2 = m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear); - EXPECT_EQ(StatusOK, m_scene.createTextureConsumer(*sampler2, consumerId)); - EXPECT_NE(StatusOK, m_scene.validate()); - const std::string report = m_scene.getValidationReport(EValidationSeverity::Error); - EXPECT_THAT(report, HasSubstr("Duplicate texture consumer ID '123'")); + EXPECT_TRUE(m_scene.createTextureConsumer(*sampler2, consumerId)); + report.clear(); + m_scene.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_THAT(report.impl().toString(), HasSubstr("Duplicate texture consumer ID '123'")); // validates fine again after duplicate removed - EXPECT_EQ(StatusOK, m_scene.destroy(*sampler1)); - EXPECT_EQ(StatusOK, m_scene.validate()); + EXPECT_TRUE(m_scene.destroy(*sampler1)); + report.clear(); + m_scene.validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(TextureSamplerTest, cannotCreateSamplerForRenderBufferWithSamples) { - const RenderBuffer& renderBuffer = *m_scene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, renderBuffer, 16u); + const ramses::RenderBuffer& renderBuffer = *m_scene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, renderBuffer, 16u); EXPECT_EQ(nullptr, sampler); } TEST_F(TextureSamplerTest, cannotCreateSamplerMSForWriteOnlyRenderBuffer) { - const RenderBuffer& renderBuffer = *m_scene.createRenderBuffer(4u, 4u, ERenderBufferType::Color, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 4u); + const ramses::RenderBuffer& renderBuffer = *m_scene.createRenderBuffer(4u, 4u, ERenderBufferFormat::RGBA8, ERenderBufferAccessMode::WriteOnly, 4u); TextureSamplerMS* sampler = this->m_scene.createTextureSamplerMS(renderBuffer, "renderBuffer"); EXPECT_EQ(nullptr, sampler); @@ -213,65 +218,81 @@ namespace ramses TEST_F(TextureSamplerTest, reportsErrorWhenValidatedWithInvalidTexture2D) { - Texture2D& texture2D = createObject("testTexture2D"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u, "testSampler2D"); + auto& texture2D = createObject("testTexture2D"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u, "testSampler2D"); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, sampler->validate()); - - EXPECT_EQ(StatusOK, m_scene.destroy(texture2D)); - EXPECT_NE(StatusOK, sampler->validate()); + ValidationReport report; + sampler->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(m_scene.destroy(texture2D)); + report.clear(); + sampler->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(TextureSamplerTest, reportsErrorWhenValidatedWithInvalidTexture3D) { - Texture3D& texture3D = createObject("testTexture3D"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D, "testSampler3D"); + auto& texture3D = createObject("testTexture3D"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D, "testSampler3D"); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, sampler->validate()); - - EXPECT_EQ(StatusOK, m_scene.destroy(texture3D)); - EXPECT_NE(StatusOK, sampler->validate()); + ValidationReport report; + sampler->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(m_scene.destroy(texture3D)); + report.clear(); + sampler->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(TextureSamplerTest, reportsErrorWhenValidatedWithInvalidTextureCube) { - TextureCube& textureCube = createObject("testTextureCube"); - const TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube, 1u, "testSamplerCube"); + auto& textureCube = createObject("testTextureCube"); + const ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, textureCube, 1u, "testSamplerCube"); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, sampler->validate()); - - EXPECT_EQ(StatusOK, m_scene.destroy(textureCube)); - EXPECT_NE(StatusOK, sampler->validate()); + ValidationReport report; + sampler->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(m_scene.destroy(textureCube)); + report.clear(); + sampler->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(TextureSamplerTest, reportsErrorWhenValidatedWithInvalidRenderBuffer) { - RenderBuffer& renderBuffer = createObject(); + auto& renderBuffer = createObject(); RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(renderBuffer); - RenderTarget* rt = this->m_scene.createRenderTarget(rtDesc); - RenderPass& renderPass = createObject(); + ramses::RenderTarget* rt = this->m_scene.createRenderTarget(rtDesc); + auto& renderPass = createObject(); OrthographicCamera* orthoCam = this->m_scene.createOrthographicCamera("camera"); orthoCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); orthoCam->setViewport(0, 0, 100, 200); renderPass.setCamera(*orthoCam); renderPass.setRenderTarget(rt); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, renderBuffer); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, renderBuffer); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, sampler->validate()); - - EXPECT_EQ(StatusOK, m_scene.destroy(renderBuffer)); - EXPECT_NE(StatusOK, sampler->validate()); + ValidationReport report; + sampler->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(m_scene.destroy(renderBuffer)); + report.clear(); + sampler->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(TextureSamplerTest, reportsErrorWhenValidatedWithInvalidRenderBufferMS) { - RenderBuffer& renderBuffer = createObject(); + auto& renderBuffer = createObject(); RenderTargetDescription rtDesc; rtDesc.addRenderBuffer(renderBuffer); - RenderTarget* rt = this->m_scene.createRenderTarget(rtDesc); - RenderPass& renderPass = createObject(); + ramses::RenderTarget* rt = this->m_scene.createRenderTarget(rtDesc); + auto& renderPass = createObject(); OrthographicCamera* orthoCam = this->m_scene.createOrthographicCamera("camera"); orthoCam->setFrustum(0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f); orthoCam->setViewport(0, 0, 100, 200); @@ -280,27 +301,33 @@ namespace ramses TextureSamplerMS* sampler = this->m_scene.createTextureSamplerMS(renderBuffer, "textureSamplerMS"); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, sampler->validate()); - - EXPECT_EQ(StatusOK, m_scene.destroy(renderBuffer)); - EXPECT_NE(StatusOK, sampler->validate()); + ValidationReport report; + sampler->validate(report); + EXPECT_FALSE(report.hasIssue()); + + EXPECT_TRUE(m_scene.destroy(renderBuffer)); + report.clear(); + sampler->validate(report); + EXPECT_TRUE(report.hasError()); } TEST_F(TextureSamplerTest, doesNotReportErrorWhenValidatedWithExternalTexture) { TextureSamplerExternal* sampler = this->m_scene.createTextureSamplerExternal(ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear); ASSERT_TRUE(nullptr != sampler); - EXPECT_EQ(StatusOK, sampler->validate()); + ValidationReport report; + sampler->validate(report); + EXPECT_FALSE(report.hasIssue()); } TEST_F(TextureSamplerTest, setTextureDataToTexture2D) { const Texture3D& texture3D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D); ASSERT_NE(nullptr, sampler); const Texture2D& texture2D = createObject(); - EXPECT_EQ(StatusOK, sampler->setTextureData(texture2D)); + EXPECT_TRUE(sampler->setTextureData(texture2D)); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); @@ -310,21 +337,21 @@ namespace ramses EXPECT_EQ(1u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::Texture2D, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); EXPECT_EQ(1u, m_internalScene.getTextureSamplerCount()); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(texture2D.m_impl.getLowlevelResourceHash(), m_internalScene.getTextureSampler(samplerHandle).textureResource); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(texture2D.impl().getLowlevelResourceHash(), m_internalScene.getTextureSampler(samplerHandle).textureResource); } TEST_F(TextureSamplerTest, setTextureDataFromTexture3DToAnotherTexture3D) { const Texture3D& texture3Doriginal = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3Doriginal); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3Doriginal); ASSERT_NE(nullptr, sampler); const Texture3D& texture3D = createObject(); - EXPECT_EQ(StatusOK, sampler->setTextureData(texture3D)); + EXPECT_TRUE(sampler->setTextureData(texture3D)); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); @@ -334,31 +361,31 @@ namespace ramses EXPECT_EQ(1u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::Texture3D, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); EXPECT_EQ(1u, m_internalScene.getTextureSamplerCount()); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(texture3D.m_impl.getLowlevelResourceHash(), m_internalScene.getTextureSampler(samplerHandle).textureResource); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(texture3D.impl().getLowlevelResourceHash(), m_internalScene.getTextureSampler(samplerHandle).textureResource); } TEST_F(TextureSamplerTest, failsToSetTextureDataFromTexture2DToTexture3D) { const Texture2D& texture2D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u); ASSERT_NE(nullptr, sampler); const Texture3D& texture3D = createObject(); - EXPECT_NE(StatusOK, sampler->setTextureData(texture3D)); + EXPECT_FALSE(sampler->setTextureData(texture3D)); } TEST_F(TextureSamplerTest, setTextureDataToTextureCube) { const Texture3D& texture3D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture3D); ASSERT_NE(nullptr, sampler); const TextureCube& textureCube = createObject(); - EXPECT_EQ(StatusOK, sampler->setTextureData(textureCube)); + EXPECT_TRUE(sampler->setTextureData(textureCube)); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); @@ -368,21 +395,21 @@ namespace ramses EXPECT_EQ(1u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::TextureCube, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); EXPECT_EQ(1u, m_internalScene.getTextureSamplerCount()); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(textureCube.m_impl.getLowlevelResourceHash(), m_internalScene.getTextureSampler(samplerHandle).textureResource); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::ClientTexture, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(textureCube.impl().getLowlevelResourceHash(), m_internalScene.getTextureSampler(samplerHandle).textureResource); } TEST_F(TextureSamplerTest, setTextureDataToTexture2DBuffer) { const Texture2D& texture2D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u); ASSERT_NE(nullptr, sampler); const Texture2DBuffer& textureBuffer = createObject(); - EXPECT_EQ(StatusOK, sampler->setTextureData(textureBuffer)); + EXPECT_TRUE(sampler->setTextureData(textureBuffer)); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); @@ -391,21 +418,21 @@ namespace ramses EXPECT_EQ(1u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::Texture2DBuffer, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); EXPECT_EQ(1u, m_internalScene.getTextureSamplerCount()); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::TextureBuffer, m_internalScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(textureBuffer.m_impl.getTextureBufferHandle().asMemoryHandle(), m_internalScene.getTextureSampler(samplerHandle).contentHandle); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::TextureBuffer, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(textureBuffer.impl().getTextureBufferHandle().asMemoryHandle(), m_internalScene.getTextureSampler(samplerHandle).contentHandle); } TEST_F(TextureSamplerTest, setTextureDataToRenderBuffer) { const Texture2D& texture2D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D, 1u); ASSERT_NE(nullptr, sampler); - const RenderBuffer& buffer = createObject(); - EXPECT_EQ(StatusOK, sampler->setTextureData(buffer)); + const ramses::RenderBuffer& buffer = createObject(); + EXPECT_TRUE(sampler->setTextureData(buffer)); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapUMode()); EXPECT_EQ(ETextureAddressMode::Clamp, sampler->getWrapVMode()); @@ -414,47 +441,47 @@ namespace ramses EXPECT_EQ(1u, sampler->getAnisotropyLevel()); EXPECT_EQ(ERamsesObjectType::RenderBuffer, sampler->getTextureType()); - const ramses_internal::TextureSamplerHandle samplerHandle = sampler->m_impl.getTextureSamplerHandle(); + const ramses::internal::TextureSamplerHandle samplerHandle = sampler->impl().getTextureSamplerHandle(); ASSERT_TRUE(m_internalScene.isTextureSamplerAllocated(samplerHandle)); EXPECT_EQ(1u, m_internalScene.getTextureSamplerCount()); - EXPECT_EQ(ramses_internal::TextureSampler::ContentType::RenderBuffer, m_internalScene.getTextureSampler(samplerHandle).contentType); - EXPECT_EQ(buffer.m_impl.getRenderBufferHandle().asMemoryHandle(), m_internalScene.getTextureSampler(samplerHandle).contentHandle); + EXPECT_EQ(ramses::internal::TextureSampler::ContentType::RenderBuffer, m_internalScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(buffer.impl().getRenderBufferHandle().asMemoryHandle(), m_internalScene.getTextureSampler(samplerHandle).contentHandle); } TEST_F(TextureSamplerTest, failsToSetTextureDataToSamplerMarkedAsConsumer) { const auto& texture2D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D); ASSERT_NE(nullptr, sampler); - ASSERT_EQ(StatusOK, this->m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); + ASSERT_TRUE(this->m_scene.createTextureConsumer(*sampler, dataConsumerId_t{666u})); const auto& texture2Dother = createObject(); - EXPECT_NE(StatusOK, sampler->setTextureData(texture2Dother)); + EXPECT_FALSE(sampler->setTextureData(texture2Dother)); const auto& textureCube = createObject(); - EXPECT_NE(StatusOK, sampler->setTextureData(textureCube)); + EXPECT_FALSE(sampler->setTextureData(textureCube)); const auto& textureBuffer = createObject(); - EXPECT_NE(StatusOK, sampler->setTextureData(textureBuffer)); - const auto& renderBuffer = createObject(); - EXPECT_NE(StatusOK, sampler->setTextureData(renderBuffer)); + EXPECT_FALSE(sampler->setTextureData(textureBuffer)); + const auto& renderBuffer = createObject(); + EXPECT_FALSE(sampler->setTextureData(renderBuffer)); } TEST_F(TextureSamplerTest, failsToSetTextureDataFromOtherSceneThanSampler) { const auto& texture2D = createObject(); - TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D); + ramses::TextureSampler* sampler = this->m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, texture2D); ASSERT_NE(nullptr, sampler); RamsesClient& otherClient(*this->framework.createClient("other")); CreationHelper creationHelper(otherClient.createScene(sceneId_t(666u)), &otherClient); const auto& texture2Dother = *creationHelper.createObjectOfType({}); - EXPECT_NE(StatusOK, sampler->setTextureData(texture2Dother)); + EXPECT_FALSE(sampler->setTextureData(texture2Dother)); const auto& textureCube = *creationHelper.createObjectOfType({}); - EXPECT_NE(StatusOK, sampler->setTextureData(textureCube)); + EXPECT_FALSE(sampler->setTextureData(textureCube)); const auto& textureBuffer = *creationHelper.createObjectOfType({}); - EXPECT_NE(StatusOK, sampler->setTextureData(textureBuffer)); - const auto& renderBuffer = *creationHelper.createObjectOfType({}); - EXPECT_NE(StatusOK, sampler->setTextureData(renderBuffer)); + EXPECT_FALSE(sampler->setTextureData(textureBuffer)); + const auto& renderBuffer = *creationHelper.createObjectOfType({}); + EXPECT_FALSE(sampler->setTextureData(renderBuffer)); } } diff --git a/client/logic/unittests/api/AnchorPointTest.cpp b/tests/unittests/client/logic/api/AnchorPointTest.cpp similarity index 64% rename from client/logic/unittests/api/AnchorPointTest.cpp rename to tests/unittests/client/logic/api/AnchorPointTest.cpp index f234dcad9..71312719f 100644 --- a/client/logic/unittests/api/AnchorPointTest.cpp +++ b/tests/unittests/client/logic/api/AnchorPointTest.cpp @@ -10,58 +10,59 @@ #include "RamsesTestUtils.h" #include "SerializationTestUtils.h" -#include "WithTempDirectory.h" -#include "impl/LogicEngineImpl.h" -#include "impl/AnchorPointImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" -#include "generated/AnchorPointGen.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/AnchorPointImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/ValidationReportImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/flatbuffers/generated/AnchorPointGen.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" namespace ramses::internal { class AnAnchorPoint : public ALogicEngine { protected: - RamsesNodeBinding& m_nodeBinding{ *m_logicEngine.createRamsesNodeBinding(*m_node) }; - RamsesCameraBinding& m_cameraBinding{ *m_logicEngine.createRamsesCameraBinding(*m_camera) }; + NodeBinding& m_nodeBinding{ *m_logicEngine->createNodeBinding(*m_node) }; + CameraBinding& m_cameraBinding{ *m_logicEngine->createCameraBinding(*m_camera) }; }; TEST_F(AnAnchorPoint, HasANameAndIdAfterCreation) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); EXPECT_EQ("anchor", anchorPoint.getName()); - EXPECT_EQ(3u, anchorPoint.getId()); + EXPECT_EQ(11u, anchorPoint.getSceneObjectId().getValue()); } TEST_F(AnAnchorPoint, ReferencesBindings) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); + auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); EXPECT_EQ(&m_nodeBinding.getRamsesNode(), &anchorPoint.getRamsesNode()); EXPECT_EQ(&m_cameraBinding.getRamsesCamera(), &anchorPoint.getRamsesCamera()); - EXPECT_EQ(&m_nodeBinding.m_nodeBinding, &anchorPoint.m_anchorPointImpl.getRamsesNodeBinding()); - EXPECT_EQ(&m_cameraBinding.m_cameraBinding, &anchorPoint.m_anchorPointImpl.getRamsesCameraBinding()); + EXPECT_EQ(&m_nodeBinding.impl(), &anchorPoint.impl().getNodeBinding()); + EXPECT_EQ(&m_cameraBinding.impl(), &anchorPoint.impl().getCameraBinding()); } TEST_F(AnAnchorPoint, HasNoInputsAfterCreation) { - auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); + auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); EXPECT_EQ(nullptr, anchorPoint.getInputs()); } TEST_F(AnAnchorPoint, HasOutputsAfterCreation) { - auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); + auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_cameraBinding, "anchor"); ASSERT_NE(nullptr, anchorPoint.getOutputs()); ASSERT_EQ(2u, anchorPoint.getOutputs()->getChildCount()); EXPECT_EQ(anchorPoint.getOutputs()->getChild(0u), anchorPoint.getOutputs()->getChild("viewportCoords")); @@ -73,10 +74,9 @@ namespace ramses::internal TEST_F(AnAnchorPoint, ProducesErrorOnUpdateIfCameraNotInitialized) { const auto uninitializedCamera = m_scene->createOrthographicCamera(); - m_logicEngine.createAnchorPoint(m_nodeBinding, *m_logicEngine.createRamsesCameraBinding(*uninitializedCamera), "anchor"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Failed to retrieve projection matrix from Ramses camera!"); + m_logicEngine->createAnchorPoint(m_nodeBinding, *m_logicEngine->createCameraBinding(*uninitializedCamera), "anchor"); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_EQ(getLastErrorMessage(), "Failed to retrieve projection matrix from Ramses camera!"); } class AnAnchorPoint_Serialization : public AnAnchorPoint @@ -107,18 +107,18 @@ namespace ramses::internal auto fbAnchorPoint = rlogic_serialization::CreateAnchorPoint( m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u, 0u, 0u), - m_nodeBinding.getId(), - m_cameraBinding.getId(), + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u), + m_nodeBinding.getSceneObjectId().getValue(), + m_cameraBinding.getSceneObjectId().getValue(), 0, // no inputs (issue == ESerializationIssue::MissingRootOutput ? 0 : PropertyImpl::Serialize(*outputsImpl, m_flatBufferBuilder, m_serializationMap))); m_flatBufferBuilder.Finish(fbAnchorPoint); } if (issue != ESerializationIssue::CannotResolveNodeBinding) - m_deserializationMap.storeLogicObject(m_nodeBinding.getId(), m_nodeBinding.m_nodeBinding); + m_deserializationMap.storeLogicObject(m_nodeBinding.getSceneObjectId(), m_nodeBinding.impl()); if (issue != ESerializationIssue::CannotResolveCameraBinding) - m_deserializationMap.storeLogicObject(m_cameraBinding.getId(), m_cameraBinding.m_cameraBinding); + m_deserializationMap.storeLogicObject(m_cameraBinding.getSceneObjectId(), m_cameraBinding.impl()); const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); return AnchorPointImpl::Deserialize(serialized, m_errorReporting, m_deserializationMap); @@ -127,27 +127,27 @@ namespace ramses::internal flatbuffers::FlatBufferBuilder m_flatBufferBuilder; ErrorReporting m_errorReporting; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; }; TEST_F(AnAnchorPoint_Serialization, DeserializesAllData) { { - AnchorPointImpl anchor(m_nodeBinding.m_nodeBinding, m_cameraBinding.m_cameraBinding, "name", 1u); + AnchorPointImpl anchor(m_scene->impl(), m_nodeBinding.impl(), m_cameraBinding.impl(), "name", sceneObjectId_t{ 1u }); anchor.createRootProperties(); (void)AnchorPointImpl::Serialize(anchor, m_flatBufferBuilder, m_serializationMap); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - m_deserializationMap.storeLogicObject(m_nodeBinding.getId(), m_nodeBinding.m_nodeBinding); - m_deserializationMap.storeLogicObject(m_cameraBinding.getId(), m_cameraBinding.m_cameraBinding); + m_deserializationMap.storeLogicObject(m_nodeBinding.getSceneObjectId(), m_nodeBinding.impl()); + m_deserializationMap.storeLogicObject(m_cameraBinding.getSceneObjectId(), m_cameraBinding.impl()); std::unique_ptr deserialized = AnchorPointImpl::Deserialize(serialized, m_errorReporting, m_deserializationMap); ASSERT_TRUE(deserialized); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(deserialized->getName(), "name"); - EXPECT_EQ(deserialized->getId(), 1u); + EXPECT_EQ(deserialized->getSceneObjectId().getValue(), 1u); EXPECT_FALSE(deserialized->getInputs()); ASSERT_TRUE(deserialized->getOutputs()); ASSERT_EQ(2u, deserialized->getOutputs()->getChildCount()); @@ -155,57 +155,56 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::Vec2f, deserialized->getOutputs()->getChild(0u)->getType()); EXPECT_EQ("depth", deserialized->getOutputs()->getChild(1u)->getName()); EXPECT_EQ(EPropertyType::Float, deserialized->getOutputs()->getChild(1u)->getType()); - EXPECT_EQ(&m_nodeBinding.m_nodeBinding, &deserialized->getRamsesNodeBinding()); - EXPECT_EQ(&m_cameraBinding.m_cameraBinding, &deserialized->getRamsesCameraBinding()); + EXPECT_EQ(&m_nodeBinding.impl(), &deserialized->getNodeBinding()); + EXPECT_EQ(&m_cameraBinding.impl(), &deserialized->getCameraBinding()); } TEST_F(AnAnchorPoint_Serialization, CanSerializeWithNoIssue) { EXPECT_TRUE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_F(AnAnchorPoint_Serialization, ReportsSerializationError_MissingName) { EXPECT_FALSE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::MissingName)); - ASSERT_EQ(2u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LogicObject base from serialized data: missing name!"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "Fatal error during loading of AnchorPoint from serialized data: missing name and/or ID!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AnchorPoint from serialized data: missing name and/or ID!"); } TEST_F(AnAnchorPoint_Serialization, ReportsSerializationError_MissingRootOutput) { EXPECT_FALSE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::MissingRootOutput)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of AnchorPoint from serialized data: missing root output!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AnchorPoint from serialized data: missing root output!"); } TEST_F(AnAnchorPoint_Serialization, ReportsSerializationError_RootOutputNotStruct) { EXPECT_FALSE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::RootOutputNotStruct)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of AnchorPoint from serialized data: root output has unexpected type!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AnchorPoint from serialized data: root output has unexpected type!"); } TEST_F(AnAnchorPoint_Serialization, ReportsSerializationError_PropertyInvalid) { EXPECT_FALSE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::OutputPropertyInvalid)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of AnchorPoint: missing or invalid properties!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AnchorPoint: missing or invalid properties!"); } TEST_F(AnAnchorPoint_Serialization, ReportsSerializationError_UnresolvedNodeBinding) { EXPECT_FALSE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::CannotResolveNodeBinding)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of AnchorPoint: could not resolve NodeBinding and/or CameraBinding!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AnchorPoint: could not resolve NodeBinding and/or CameraBinding!"); } TEST_F(AnAnchorPoint_Serialization, ReportsSerializationError_UnresolvedCameraBinding) { EXPECT_FALSE(deserializeSerializedDataWithIssue(AnAnchorPoint_Serialization::ESerializationIssue::CannotResolveCameraBinding)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of AnchorPoint: could not resolve NodeBinding and/or CameraBinding!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AnchorPoint: could not resolve NodeBinding and/or CameraBinding!"); } class AnAnchorPoint_Math : public AnAnchorPoint @@ -214,7 +213,7 @@ namespace ramses::internal void SetUp() override { m_node->setTranslation({1.f, 2.f, 3.f}); - m_node->setRotation({-1.f, -2.f, -3.f}); + m_node->setRotation({-1.f, -2.f, -3.f}, ERotationType::Euler_XYZ); m_node->setScaling({1.f, 2.f, 3.f}); auto nodeTransNode = m_scene->createNode(); @@ -222,18 +221,20 @@ namespace ramses::internal nodeTransNode->addChild(*m_node); m_perspCamera.setTranslation({1.f, 2.f, -3.f}); - m_perspCamera.setRotation({-1.f, -2.f, 3.f}); + m_perspCamera.setRotation({-1.f, -2.f, 3.f}, ERotationType::Euler_XYZ); m_perspCamera.setScaling({1.f, 2.f, -3.f}); m_perspCamera.setFrustum(75.f, 0.8f, 0.01f, 100.f); m_perspCamera.setViewport(10, 20, 30u, 40u); - ASSERT_EQ(ramses::StatusOK, m_perspCamera.validate()); + ValidationReport report; + m_perspCamera.validate(report); m_orthoCamera.setTranslation({1.f, 2.f, -3.f}); - m_orthoCamera.setRotation({-1.f, -2.f, 3.f}); + m_orthoCamera.setRotation({-1.f, -2.f, 3.f}, ERotationType::Euler_XYZ); m_orthoCamera.setScaling({1.f, 2.f, -3.f}); m_orthoCamera.setFrustum(-0.5f, 0.5f, -1.f, 2.f, 0.01f, 100.f); m_orthoCamera.setViewport(10, 20, 30u, 40u); - ASSERT_EQ(ramses::StatusOK, m_orthoCamera.validate()); + m_orthoCamera.validate(report); + ASSERT_FALSE(report.hasIssue()) << report.impl().toString(); auto camTransNode = m_scene->createNode(); camTransNode->setTranslation({1.f, 2.f, -3.f}); @@ -241,16 +242,16 @@ namespace ramses::internal camTransNode->addChild(m_orthoCamera); } - ramses::PerspectiveCamera& m_perspCamera{ *m_scene->createPerspectiveCamera() }; - ramses::OrthographicCamera& m_orthoCamera{ *m_scene->createOrthographicCamera() }; - RamsesCameraBinding& m_perspCameraBinding{ *m_logicEngine.createRamsesCameraBinding(m_perspCamera) }; - RamsesCameraBinding& m_orthoCameraBinding{ *m_logicEngine.createRamsesCameraBinding(m_orthoCamera) }; + PerspectiveCamera& m_perspCamera{ *m_scene->createPerspectiveCamera() }; + OrthographicCamera& m_orthoCamera{ *m_scene->createOrthographicCamera() }; + CameraBinding& m_perspCameraBinding{ *m_logicEngine->createCameraBinding(m_perspCamera) }; + CameraBinding& m_orthoCameraBinding{ *m_logicEngine->createCameraBinding(m_orthoCamera) }; }; TEST_F(AnAnchorPoint_Math, CalculatesCoords_PerspCamera) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); const auto coords = *anchorPoint.getOutputs()->getChild(0u)->get(); EXPECT_FLOAT_EQ(17.560308f, coords[0]); EXPECT_FLOAT_EQ(19.317562f, coords[1]); @@ -259,8 +260,8 @@ namespace ramses::internal TEST_F(AnAnchorPoint_Math, CalculatesCoords_OrthoCamera) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_orthoCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_orthoCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); const auto coords = *anchorPoint.getOutputs()->getChild(0u)->get(); EXPECT_FLOAT_EQ(21.281908f, coords[0]); EXPECT_FLOAT_EQ(12.63566f, coords[1]); @@ -273,12 +274,12 @@ namespace ramses::internal void SetUp() override { AnAnchorPoint_Math::SetUp(); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); } void expectNodeExecuted(const LogicNode& node) const { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); const auto& executedNodes = report.getNodesExecuted(); const auto it = std::find_if(executedNodes.cbegin(), executedNodes.cend(), [&node](const auto& timedNode) { return timedNode.first == &node; }); EXPECT_NE(it, executedNodes.cend()); @@ -286,7 +287,7 @@ namespace ramses::internal void expectNodeSkipped(const LogicNode& node) const { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); const auto& skippedNodes = report.getNodesSkippedExecution(); const auto it = std::find(skippedNodes.cbegin(), skippedNodes.cend(), &node); EXPECT_NE(it, skippedNodes.cend()); @@ -295,34 +296,34 @@ namespace ramses::internal TEST_F(AnAnchorPoint_Dirtiness, RecalculatesWhenModelMatrixChanges) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); m_node->setTranslation({123.f, 231.f, 321.f}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); } TEST_F(AnAnchorPoint_Dirtiness, RecalculatesWhenViewMatrixChanges) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); m_perspCamera.setTranslation({123.f, 231.f, 321.f}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); } TEST_F(AnAnchorPoint_Dirtiness, RecalculatesWhenProjectionMatrixChanges) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); m_perspCamera.setFrustum(10.f, 1.f, 1000.f, 2000.f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); } @@ -331,12 +332,12 @@ namespace ramses::internal auto parentNode = m_scene->createNode(); parentNode->addChild(*m_node); - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); parentNode->setTranslation({123.f, 231.f, 321.f}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); } @@ -345,30 +346,30 @@ namespace ramses::internal auto parentNode = m_scene->createNode(); parentNode->addChild(m_perspCamera); - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); parentNode->setTranslation({123.f, 231.f, 321.f}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); } TEST_F(AnAnchorPoint_Dirtiness, RecalculatesWhenViewportChanges) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - EXPECT_TRUE(m_logicEngine.update()); + const auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); m_perspCamera.setViewport(666, -100, 333u, 444u); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(anchorPoint); } TEST_F(AnAnchorPoint_Dirtiness, OutputIsNotDirtyIfNothingChanged) { - const auto& anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); - const auto* script = m_logicEngine.createLuaScript(R"( + auto& anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBinding, m_perspCameraBinding, "anchor"); + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.coords = Type:Vec2f() OUT.y = Type:Float() @@ -379,14 +380,14 @@ namespace ramses::internal end )"); - ASSERT_TRUE(m_logicEngine.link(*anchorPoint.getOutputs()->getChild("viewportCoords"), *script->getInputs()->getChild("coords"))); - EXPECT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->link(*anchorPoint.getOutputs()->getChild("viewportCoords"), *script->getInputs()->getChild("coords"))); + EXPECT_TRUE(m_logicEngine->update()); expectNodeExecuted(*script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeSkipped(*script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectNodeSkipped(*script); } @@ -409,7 +410,7 @@ namespace ramses::internal } ramses::Node& m_nodeAnchor = *m_scene->createNode(); - RamsesNodeBinding& m_nodeBindingAnchor = *m_logicEngine.createRamsesNodeBinding(m_nodeAnchor); + NodeBinding& m_nodeBindingAnchor = *m_logicEngine->createNodeBinding(m_nodeAnchor); const std::string_view m_scriptSrc = R"( function interface(IN,OUT) @@ -422,19 +423,19 @@ namespace ramses::internal end )"; ramses::Node& m_nodeScript = *m_scene->createNode(); - RamsesNodeBinding& m_nodeBindingScript = *m_logicEngine.createRamsesNodeBinding(m_nodeScript); + NodeBinding& m_nodeBindingScript = *m_logicEngine->createNodeBinding(m_nodeScript); }; TEST_F(AnAnchorPoint_UpdateOrder, OutputIsCalculatedWithDoubleUpdateIfDependencyUnknown_createAnchorFirst) { // create anchor then script - AnchorPoint& m_anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBindingAnchor, m_perspCameraBinding); - LuaScript& m_passThruScript = *m_logicEngine.createLuaScript(m_scriptSrc); - m_logicEngine.link(*m_passThruScript.getOutputs()->getChild("trans"), *m_nodeBindingScript.getInputs()->getChild("translation")); + AnchorPoint& m_anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBindingAnchor, m_perspCameraBinding); + LuaScript& m_passThruScript = *m_logicEngine->createLuaScript(m_scriptSrc); + m_logicEngine->link(*m_passThruScript.getOutputs()->getChild("trans"), *m_nodeBindingScript.getInputs()->getChild("translation")); const vec2f initialCoordsExpected{ -9.3664684f, -6.0138535f }; const float initialDepthExpected = 0.99510324f; - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto coordsInitial = *m_anchorPoint.getOutputs()->getChild(0u)->get(); EXPECT_EQ(initialCoordsExpected, coordsInitial); EXPECT_EQ(initialDepthExpected, *m_anchorPoint.getOutputs()->getChild(1u)->get()); @@ -443,8 +444,8 @@ namespace ramses::internal const vec2f coordsAfterChangeExpected{ 525.07275f, 136.18787f }; const float depthAfterChangeExpected = 0.99979484f; - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->update()); const auto coordsAfterChange = *m_anchorPoint.getOutputs()->getChild(0u)->get(); EXPECT_EQ(coordsAfterChangeExpected, coordsAfterChange); @@ -454,13 +455,13 @@ namespace ramses::internal TEST_F(AnAnchorPoint_UpdateOrder, OutputIsCalculatedWithDoubleUpdateIfDependencyUnknown_createAnchorLast) { // create script then anchor - LuaScript& m_passThruScript = *m_logicEngine.createLuaScript(m_scriptSrc); - m_logicEngine.link(*m_passThruScript.getOutputs()->getChild("trans"), *m_nodeBindingScript.getInputs()->getChild("translation")); - AnchorPoint& m_anchorPoint = *m_logicEngine.createAnchorPoint(m_nodeBindingAnchor, m_perspCameraBinding); + LuaScript& m_passThruScript = *m_logicEngine->createLuaScript(m_scriptSrc); + m_logicEngine->link(*m_passThruScript.getOutputs()->getChild("trans"), *m_nodeBindingScript.getInputs()->getChild("translation")); + AnchorPoint& m_anchorPoint = *m_logicEngine->createAnchorPoint(m_nodeBindingAnchor, m_perspCameraBinding); const vec2f initialCoordsExpected{ -9.3664684f, -6.0138535f }; const float initialDepthExpected = 0.99510324f; - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto coordsInitial = *m_anchorPoint.getOutputs()->getChild(0u)->get(); EXPECT_EQ(initialCoordsExpected, coordsInitial); EXPECT_EQ(initialDepthExpected, *m_anchorPoint.getOutputs()->getChild(1u)->get()); @@ -469,8 +470,8 @@ namespace ramses::internal const vec2f coordsAfterChangeExpected{ 525.07275f, 136.18787f }; const float depthAfterChangeExpected = 0.99979484f; - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->update()); const auto coordsAfterChange = *m_anchorPoint.getOutputs()->getChild(0u)->get(); EXPECT_EQ(coordsAfterChangeExpected, coordsAfterChange); diff --git a/client/logic/unittests/api/AnimationNodeConfigTest.cpp b/tests/unittests/client/logic/api/AnimationNodeConfigTest.cpp similarity index 87% rename from client/logic/unittests/api/AnimationNodeConfigTest.cpp rename to tests/unittests/client/logic/api/AnimationNodeConfigTest.cpp index deb83da04..ad6d90607 100644 --- a/client/logic/unittests/api/AnimationNodeConfigTest.cpp +++ b/tests/unittests/client/logic/api/AnimationNodeConfigTest.cpp @@ -7,21 +7,22 @@ // ------------------------------------------------------------------------- #include -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/DataArray.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/DataArray.h" +#include "LogicEngineTest_Base.h" #include namespace ramses::internal { - class AnAnimationNodeConfig : public ::testing::Test + class AnAnimationNodeConfig : public ALogicEngineBase, public ::testing::Test { public: void SetUp() override { - m_dataFloat = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - m_dataVec2 = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); - m_dataVecVec = m_logicEngine.createDataArray(std::vector>{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + m_dataFloat = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + m_dataVec2 = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + m_dataVecVec = m_logicEngine->createDataArray(std::vector>{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); } static bool createConfig(const std::initializer_list channels) @@ -37,7 +38,6 @@ namespace ramses::internal } protected: - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; DataArray* m_dataFloat = nullptr; DataArray* m_dataVec2 = nullptr; DataArray* m_dataVecVec = nullptr; @@ -102,7 +102,7 @@ namespace ramses::internal TEST_F(AnAnimationNodeConfig, FailsToAddChannelIfTimestampsOrKeyframesTypeInvalid) { const AnimationChannel validChannel{ "ok", m_dataFloat, m_dataVec2 }; - const auto dataVec2OtherSize = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f } }); // single element only + const auto dataVec2OtherSize = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f } }); // single element only EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataVec2, m_dataVec2 } })); EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataFloat, dataVec2OtherSize } })); @@ -112,10 +112,10 @@ namespace ramses::internal { const AnimationChannel validChannel{ "ok", m_dataFloat, m_dataVec2 }; - const auto timeStampsDescending = m_logicEngine.createDataArray(std::vector{ { 1.f, 3.f, 2.f } }); + const auto timeStampsDescending = m_logicEngine->createDataArray(std::vector{ { 1.f, 3.f, 2.f } }); EXPECT_FALSE(createConfig({ validChannel, { "channel", timeStampsDescending, m_dataVec2 } })); - const auto timeStampsNotStrictAscend = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f, 2.f } }); + const auto timeStampsNotStrictAscend = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f, 2.f } }); EXPECT_FALSE(createConfig({ validChannel, { "channel", timeStampsNotStrictAscend, m_dataVec2 } })); } @@ -147,7 +147,7 @@ namespace ramses::internal EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataFloat, m_dataVec2, EInterpolationType::Cubic, m_dataVec2, m_dataFloat } })); EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataFloat, m_dataVec2, EInterpolationType::Cubic, m_dataFloat, m_dataVec2 } })); - const auto dataVec2OtherSize = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f } }); // single element only + const auto dataVec2OtherSize = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f } }); // single element only EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataFloat, m_dataVec2, EInterpolationType::Cubic, m_dataVec2, dataVec2OtherSize } })); EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataFloat, m_dataVec2, EInterpolationType::Cubic, dataVec2OtherSize, m_dataVec2 } })); } @@ -160,7 +160,7 @@ namespace ramses::internal tooBigArrays.resize(3u); for (auto& elementArray : tooBigArrays) elementArray.resize(MaxArrayPropertySize + 1u, 0.f); - const auto invalidData = m_logicEngine.createDataArray(tooBigArrays, "invalid"); + const auto invalidData = m_logicEngine->createDataArray(tooBigArrays, "invalid"); EXPECT_FALSE(createConfig({ validChannel, { "channel", m_dataFloat, invalidData } })); EXPECT_FALSE(createConfig({ { "channel", m_dataFloat, invalidData }, validChannel })); @@ -171,7 +171,7 @@ namespace ramses::internal auto dataWithLargerElementArrays = *m_dataVecVec->getData>(); for (auto& elementArray : dataWithLargerElementArrays) elementArray.push_back(0.f); - const auto largerData = m_logicEngine.createDataArray(dataWithLargerElementArrays, "invalid"); + const auto largerData = m_logicEngine->createDataArray(dataWithLargerElementArrays, "invalid"); const AnimationChannel validChannel{ "ok", m_dataFloat, m_dataVecVec, EInterpolationType::Cubic, m_dataVecVec, m_dataVecVec }; const AnimationChannel invalidChannel1{ "invalid", m_dataFloat, m_dataVecVec, EInterpolationType::Cubic, m_dataVecVec, largerData }; @@ -206,11 +206,11 @@ namespace ramses::internal TEST_F(AnAnimationNodeConfig, FailsToAddChannelIfDataExposeEnabledAndNumberOfKeyframesExceedsMaximum) { - const auto dataOk = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto dataOk = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); std::vector vecDataExceeding; vecDataExceeding.resize(MaxArrayPropertySize + 1u); std::iota(vecDataExceeding.begin(), vecDataExceeding.end(), 1.f); - const auto dataExceeding = m_logicEngine.createDataArray(vecDataExceeding); + const auto dataExceeding = m_logicEngine->createDataArray(vecDataExceeding); const AnimationChannel channelOk{ "channel1", dataOk, dataOk }; const AnimationChannel channelExceeding{ "channel2", dataExceeding, dataExceeding }; @@ -228,11 +228,11 @@ namespace ramses::internal TEST_F(AnAnimationNodeConfig, FailsToEnableDataExposeIfNumberOfKeyframesExceedsMaximum) { - const auto dataOk = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto dataOk = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); std::vector vecDataExceeding; vecDataExceeding.resize(MaxArrayPropertySize + 1u); std::iota(vecDataExceeding.begin(), vecDataExceeding.end(), 1.f); - const auto dataExceeding = m_logicEngine.createDataArray(vecDataExceeding); + const auto dataExceeding = m_logicEngine->createDataArray(vecDataExceeding); const AnimationChannel channelOk{ "channel1", dataOk, dataOk }; const AnimationChannel channelExceeding{ "channel2", dataExceeding, dataExceeding }; diff --git a/client/logic/unittests/api/AnimationNodeTest.cpp b/tests/unittests/client/logic/api/AnimationNodeTest.cpp similarity index 79% rename from client/logic/unittests/api/AnimationNodeTest.cpp rename to tests/unittests/client/logic/api/AnimationNodeTest.cpp index b9960c468..8c52de7b8 100644 --- a/client/logic/unittests/api/AnimationNodeTest.cpp +++ b/tests/unittests/client/logic/api/AnimationNodeTest.cpp @@ -7,37 +7,37 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "WithTempDirectory.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/Property.h" -#include "impl/AnimationNodeImpl.h" -#include "impl/DataArrayImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" -#include "internals/TypeData.h" -#include "internals/EPropertySemantics.h" -#include "generated/AnimationNodeGen.h" + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/AnimationNodeImpl.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/TypeData.h" +#include "internal/logic/EPropertySemantics.h" +#include "internal/logic/flatbuffers/generated/AnimationNodeGen.h" #include "flatbuffers/flatbuffers.h" +#include "LogicEngineTest_Base.h" #include namespace ramses::internal { - class AnAnimationNode : public ::testing::TestWithParam + class AnAnimationNode : public ALogicEngineBase, public ::testing::TestWithParam { public: void SetUp() override { - m_dataFloat = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - m_dataVec2 = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + m_dataFloat = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + m_dataVec2 = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); // Quaternions which are not normalized (ie. not of unit length). Used for tests to check they are normalized correctly - m_dataVec4 = m_logicEngine.createDataArray(std::vector{ { 2.f, 0.f, 0.f, 0.f }, { 0.f, 2.f, 0.f, 0.f } , { 0.f, 0.f, 2.f, 0.f } }); - m_dataVecVec = m_logicEngine.createDataArray(std::vector>{ { 1.f, 2.f, 3.f, 4.f, 5.f }, { 3.f, 4.f, 5.f, 6.f, 7.f }, { 5.f, 6.f, 7.f, 8.f, 9.f } }); + m_dataVec4 = m_logicEngine->createDataArray(std::vector{ { 2.f, 0.f, 0.f, 0.f }, { 0.f, 2.f, 0.f, 0.f } , { 0.f, 0.f, 2.f, 0.f } }); + m_dataVecVec = m_logicEngine->createDataArray(std::vector>{ { 1.f, 2.f, 3.f, 4.f, 5.f }, { 3.f, 4.f, 5.f, 6.f, 7.f }, { 5.f, 6.f, 7.f, 8.f, 9.f } }); m_saveFileConfigNoValidation.setValidationEnabled(false); } @@ -54,14 +54,14 @@ namespace ramses::internal if (!config.setExposingOfChannelDataAsProperties(GetParam())) return nullptr; - return m_logicEngine.createAnimationNode(config, name); + return m_logicEngine->createAnimationNode(config, name); } template void advanceAnimationAndExpectValues(AnimationNode& animNode, float progress, const T& expectedValue) { EXPECT_TRUE(animNode.getInputs()->getChild("progress")->set(progress)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); if constexpr (std::is_same_v>) { @@ -104,7 +104,7 @@ namespace ramses::internal void advanceAnimationAndExpectValues_twoChannels(AnimationNode& animNode, float progress, const vec2f& expectedValue1, const vec2f& expectedValue2) { animNode.getInputs()->getChild("progress")->set(progress); - m_logicEngine.update(); + m_logicEngine->update(); const auto val1 = *animNode.getOutputs()->getChild("channel1")->get(); EXPECT_FLOAT_EQ(expectedValue1[0], val1[0]); EXPECT_FLOAT_EQ(expectedValue1[1], val1[1]); @@ -113,7 +113,6 @@ namespace ramses::internal EXPECT_FLOAT_EQ(expectedValue2[1], val2[1]); } - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; DataArray* m_dataFloat = nullptr; DataArray* m_dataVec2 = nullptr; DataArray* m_dataVec4 = nullptr; @@ -134,9 +133,9 @@ namespace ramses::internal const AnimationChannel channel{ "channel", m_dataFloat, m_dataVec2 }; const AnimationChannels channels{ channel, channel }; const auto animNode = createAnimationNode(channels, "animNode"); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + expectNoError(); ASSERT_NE(nullptr, animNode); - EXPECT_EQ(animNode, m_logicEngine.findByName("animNode")); + EXPECT_EQ(animNode, m_logicEngine->findObject("animNode")); EXPECT_EQ("animNode", animNode->getName()); EXPECT_EQ(channels, animNode->getChannels()); @@ -145,19 +144,18 @@ namespace ramses::internal TEST_P(AnAnimationNode, IsDestroyed) { const auto animNode = createAnimationNode({ { "channel", m_dataFloat, m_dataVec2 } }, "animNode"); - EXPECT_TRUE(m_logicEngine.destroy(*animNode)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - EXPECT_EQ(nullptr, m_logicEngine.findByName("animNode")); + EXPECT_TRUE(m_logicEngine->destroy(*animNode)); + expectNoError(); + EXPECT_EQ(nullptr, m_logicEngine->findObject("animNode")); } TEST_P(AnAnimationNode, FailsToBeDestroyedIfFromOtherLogicInstance) { auto animNode = createAnimationNode({ { "channel", m_dataFloat, m_dataVec2 } }, "animNode"); - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_scene->createLogicEngine(); EXPECT_FALSE(otherEngine.destroy(*animNode)); - ASSERT_FALSE(otherEngine.getErrors().empty()); - EXPECT_EQ("Failed to destroy object 'animNode [Id=5]', cannot find it in this LogicEngine instance.", otherEngine.getErrors().front().message); + EXPECT_EQ("Failed to destroy object 'animNode [LogicObject ScnObjId=13]', cannot find it in this LogicEngine instance.", getLastErrorMessage()); } TEST_P(AnAnimationNode, ChangesName) @@ -166,16 +164,16 @@ namespace ramses::internal animNode->setName("an"); EXPECT_EQ("an", animNode->getName()); - EXPECT_EQ(animNode, m_logicEngine.findByName("an")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + EXPECT_EQ(animNode, m_logicEngine->findObject("an")); + expectNoError(); } TEST_P(AnAnimationNode, CanContainVariousAnimationChannels) { - const auto timeStamps1 = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f }); - const auto timeStamps2 = m_logicEngine.createDataArray(std::vector{ 3.f, 4.f, 5.f }); - const auto data1 = m_logicEngine.createDataArray(std::vector{ { 11.f, 22.f }, { 33.f, 44.f } }); - const auto data2 = m_logicEngine.createDataArray(std::vector{ { 11, 22 }, { 44, 55 }, { 66, 77 } }); + const auto timeStamps1 = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f }); + const auto timeStamps2 = m_logicEngine->createDataArray(std::vector{ 3.f, 4.f, 5.f }); + const auto data1 = m_logicEngine->createDataArray(std::vector{ { 11.f, 22.f }, { 33.f, 44.f } }); + const auto data2 = m_logicEngine->createDataArray(std::vector{ { 11, 22 }, { 44, 55 }, { 66, 77 } }); const AnimationChannel channel1{ "channel1", timeStamps1, data1, EInterpolationType::Step }; const AnimationChannel channel2{ "channel2", timeStamps1, data1, EInterpolationType::Linear }; @@ -185,9 +183,9 @@ namespace ramses::internal const auto animNode = createAnimationNode(channels, "animNode"); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + expectNoError(); ASSERT_NE(nullptr, animNode); - EXPECT_EQ(animNode, m_logicEngine.findByName("animNode")); + EXPECT_EQ(animNode, m_logicEngine->findObject("animNode")); EXPECT_EQ("animNode", animNode->getName()); EXPECT_FLOAT_EQ(5.f, *animNode->getOutputs()->getChild("duration")->get()); @@ -254,8 +252,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, DeterminesDurationFromHighestTimestamp) { - const auto timeStamps1 = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - const auto timeStamps2 = m_logicEngine.createDataArray(std::vector{ 4.f, 5.f, 6.f }); + const auto timeStamps1 = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto timeStamps2 = m_logicEngine->createDataArray(std::vector{ 4.f, 5.f, 6.f }); const auto animNode1 = createAnimationNode({ { "channel", timeStamps1, m_dataVec2 } }, "animNode1"); EXPECT_FLOAT_EQ(3.f, *animNode1->getOutputs()->getChild("duration")->get()); @@ -266,30 +264,30 @@ namespace ramses::internal TEST_P(AnAnimationNode, FailsToBeCreatedWithNoChannels) { EXPECT_EQ(nullptr, createAnimationNode({}, "animNode")); - EXPECT_EQ("Failed to create AnimationNode 'animNode': must provide at least one channel.", m_logicEngine.getErrors().front().message); + EXPECT_EQ("Failed to create AnimationNode 'animNode': must provide at least one channel.", getLastErrorMessage()); } TEST_P(AnAnimationNode, FailsToBeCreatedIfDataArrayFromOtherLogicInstance) { - LogicEngine otherInstance{ m_logicEngine.getFeatureLevel() }; + auto& otherInstance = *m_scene->createLogicEngine(); auto otherInstanceData = otherInstance.createDataArray(std::vector{ 1.f, 2.f, 3.f }); EXPECT_EQ(nullptr, createAnimationNode({ { "channel", otherInstanceData, m_dataFloat } }, "animNode")); - EXPECT_EQ("Failed to create AnimationNode 'animNode': timestamps or keyframes were not found in this logic instance.", m_logicEngine.getErrors().front().message); + EXPECT_EQ("Failed to create AnimationNode 'animNode': timestamps or keyframes were not found in this logic instance.", getLastErrorMessage()); EXPECT_EQ(nullptr, createAnimationNode({ { "channel", m_dataFloat, otherInstanceData } }, "animNode")); - EXPECT_EQ("Failed to create AnimationNode 'animNode': timestamps or keyframes were not found in this logic instance.", m_logicEngine.getErrors().front().message); + EXPECT_EQ("Failed to create AnimationNode 'animNode': timestamps or keyframes were not found in this logic instance.", getLastErrorMessage()); EXPECT_EQ(nullptr, createAnimationNode({ { "channel", m_dataFloat, m_dataFloat, EInterpolationType::Cubic, otherInstanceData, m_dataFloat } }, "animNode")); - EXPECT_EQ("Failed to create AnimationNode 'animNode': tangents were not found in this logic instance.", m_logicEngine.getErrors().front().message); + EXPECT_EQ("Failed to create AnimationNode 'animNode': tangents were not found in this logic instance.", getLastErrorMessage()); EXPECT_EQ(nullptr, createAnimationNode({ { "channel", m_dataFloat, m_dataFloat, EInterpolationType::Cubic, m_dataFloat, otherInstanceData } }, "animNode")); - EXPECT_EQ("Failed to create AnimationNode 'animNode': tangents were not found in this logic instance.", m_logicEngine.getErrors().front().message); + EXPECT_EQ("Failed to create AnimationNode 'animNode': tangents were not found in this logic instance.", getLastErrorMessage()); } TEST_P(AnAnimationNode, CanBeSerializedAndDeserialized) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_logicEngine; const auto timeStamps1 = otherEngine.createDataArray(std::vector{ 1.f, 2.f }, "ts1"); const auto timeStamps2 = otherEngine.createDataArray(std::vector{ 3.f, 4.f, 5.f }, "ts2"); @@ -328,15 +326,15 @@ namespace ramses::internal otherEngine.createAnimationNode(config1, "animNode1"); otherEngine.createAnimationNode(config2, "animNode2"); - ASSERT_TRUE(otherEngine.saveToFile("logic_animNodes.bin", m_saveFileConfigNoValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("logic_animNodes.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("logic_animNodes.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + ASSERT_TRUE(recreateFromFile("logic_animNodes.bin")); + expectNoError(); - EXPECT_EQ(2u, m_logicEngine.getCollection().size()); - const auto animNode1 = m_logicEngine.findByName("animNode1"); - const auto animNode2 = m_logicEngine.findByName("animNode2"); + EXPECT_EQ(2u, m_logicEngine->getCollection().size()); + const auto animNode1 = m_logicEngine->findObject("animNode1"); + const auto animNode2 = m_logicEngine->findObject("animNode2"); ASSERT_TRUE(animNode1 && animNode2); EXPECT_EQ("animNode1", animNode1->getName()); @@ -345,11 +343,11 @@ namespace ramses::internal EXPECT_FLOAT_EQ(5.f, *animNode2->getOutputs()->getChild("duration")->get()); // ptrs are different after loading, find data arrays to verify the references in loaded anim nodes match - const auto ts1 = m_logicEngine.findByName("ts1"); - const auto ts2 = m_logicEngine.findByName("ts2"); - const auto data1 = m_logicEngine.findByName("data1"); - const auto data2 = m_logicEngine.findByName("data2"); - const auto data3 = m_logicEngine.findByName("data3"); + const auto ts1 = m_logicEngine->findObject("ts1"); + const auto ts2 = m_logicEngine->findObject("ts2"); + const auto data1 = m_logicEngine->findObject("data1"); + const auto data2 = m_logicEngine->findObject("data2"); + const auto data3 = m_logicEngine->findObject("data3"); const AnimationChannel channel1{ "channel1", ts1, data1, EInterpolationType::Step }; const AnimationChannel channel2{ "channel2", ts1, data1, EInterpolationType::Linear }; const AnimationChannel channel3{ "channel3", ts2, data2, EInterpolationType::Linear }; @@ -421,10 +419,10 @@ namespace ramses::internal TEST_P(AnAnimationNode, WillSerializeAnimationIncludingProgress) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_logicEngine; const auto timeStamps = otherEngine.createDataArray(std::vector{ 1.f, 2.f }, "ts"); const auto data = otherEngine.createDataArray(std::vector{ 10, 20 }, "data"); @@ -438,15 +436,15 @@ namespace ramses::internal EXPECT_TRUE(otherEngine.update()); EXPECT_EQ(15, *animNode->getOutputs()->getChild("channel")->get()); - ASSERT_TRUE(otherEngine.saveToFile("logic_animNodes.bin", m_saveFileConfigNoValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("logic_animNodes.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("logic_animNodes.bin")); - const auto animNode = m_logicEngine.findByName("animNode"); + ASSERT_TRUE(recreateFromFile("logic_animNodes.bin")); + const auto animNode = m_logicEngine->findObject("animNode"); ASSERT_TRUE(animNode); // update node with no change to progress to check its state - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // progress same as when saved EXPECT_EQ(15, *animNode->getOutputs()->getChild("channel")->get()); @@ -460,8 +458,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, CanHandleProgressOutOfNormalizedRange) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ 10.f, 20.f }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ 10.f, 20.f }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); advanceAnimationAndExpectValues(*animNode, 0.0f, 10.f); @@ -473,8 +471,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_step_vec2f) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 0.f, 10.f }, { 1.f, 20.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 0.f, 10.f }, { 1.f, 20.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Step } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 0.f, 10.f }); @@ -485,8 +483,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_step_vec2i) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 0, 10 }, { 1, 20 } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 0, 10 }, { 1, 20 } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Step } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 0, 10 }); @@ -500,8 +498,8 @@ namespace ramses::internal if (GetParam()) GTEST_SKIP(); - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector>{ { 0.f, 10.f }, { 1.f, 20.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector>{ { 0.f, 10.f }, { 1.f, 20.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Step } }); advanceAnimationAndExpectValues>(*animNode, 0.f, { 0.f, 10.f }); @@ -512,8 +510,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_linear_vec2f) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 0.f, 10.f }, { 1.f, 20.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 0.f, 10.f }, { 1.f, 20.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 0.f, 10.f }); @@ -526,8 +524,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_linear_vec2i) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 0, 10 }, { 1, 20 } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 0, 10 }, { 1, 20 } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 0, 10 }); @@ -543,8 +541,8 @@ namespace ramses::internal if (GetParam()) GTEST_SKIP(); - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector>{ { 0.f, 10.f }, { 1.f, 20.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector>{ { 0.f, 10.f }, { 1.f, 20.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); advanceAnimationAndExpectValues>(*animNode, 0.f, { 0.f, 10.f }); @@ -557,7 +555,7 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_linear_quaternions) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f, 2.f }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f, 2.f }); const auto animNode = createAnimationNode({ { "channel", timeStamps, m_dataVec4, EInterpolationType::Linear_Quaternions } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 1, 0, 0, 0 }); @@ -569,11 +567,11 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_cubic_vec2f) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 0.f, 10.f }, { 1.f, 20.f } }); - const auto tangentsZero = m_logicEngine.createDataArray(std::vector{ { 0.f, 0.f }, { 0.f, 0.f } }); - const auto tangentsIn = m_logicEngine.createDataArray(std::vector{ { 0.f, 0.f }, { -1.f, -2.f } }); - const auto tangentsOut = m_logicEngine.createDataArray(std::vector{ { 2.f, 5.f }, { 0.f, 0.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 0.f, 10.f }, { 1.f, 20.f } }); + const auto tangentsZero = m_logicEngine->createDataArray(std::vector{ { 0.f, 0.f }, { 0.f, 0.f } }); + const auto tangentsIn = m_logicEngine->createDataArray(std::vector{ { 0.f, 0.f }, { -1.f, -2.f } }); + const auto tangentsOut = m_logicEngine->createDataArray(std::vector{ { 2.f, 5.f }, { 0.f, 0.f } }); // animation with one channel using zero tangents and another channel with non-zero tangents const auto animNode = createAnimationNode({ { "channel1", timeStamps, data, EInterpolationType::Cubic, tangentsZero, tangentsZero }, @@ -593,10 +591,10 @@ namespace ramses::internal if (GetParam()) GTEST_SKIP(); - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector>{ { 0.f, 10.f }, { 1.f, 20.f } }); - const auto tangentsIn = m_logicEngine.createDataArray(std::vector>{ { 0.f, 0.f }, { -1.f, -2.f } }); - const auto tangentsOut = m_logicEngine.createDataArray(std::vector>{ { 2.f, 5.f }, { 0.f, 0.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector>{ { 0.f, 10.f }, { 1.f, 20.f } }); + const auto tangentsIn = m_logicEngine->createDataArray(std::vector>{ { 0.f, 0.f }, { -1.f, -2.f } }); + const auto tangentsOut = m_logicEngine->createDataArray(std::vector>{ { 2.f, 5.f }, { 0.f, 0.f } }); const auto animNode = createAnimationNode({{ "channel", timeStamps, data, EInterpolationType::Cubic, tangentsIn, tangentsOut }}); advanceAnimationAndExpectValues>(*animNode, 0.f, { 0.f, 10.f }); @@ -609,8 +607,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_cubic_quaternions) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f, 2.f }); - const auto tangentsZero = m_logicEngine.createDataArray(std::vector{ { 0.f, 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f, 0.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f, 2.f }); + const auto tangentsZero = m_logicEngine->createDataArray(std::vector{ { 0.f, 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f, 0.f } }); const auto animNode = createAnimationNode({{ "channel", timeStamps, m_dataVec4, EInterpolationType::Cubic_Quaternions, tangentsZero, tangentsZero }}); advanceAnimationAndExpectValues(*animNode, 0.f, { 1, 0, 0, 0 }); @@ -624,9 +622,9 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_cubic_quaternions_withTangents) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f, 2.f }); - const auto tangentsIn = m_logicEngine.createDataArray(std::vector{ { 0.f, 0.f, 0.f, 0.f }, { 1.f, 1.f, 0.f, 0.f }, { 1.f, 1.f, 0.f, 0.f } }); - const auto tangentsOut = m_logicEngine.createDataArray(std::vector{ { 1.f, 1.f, 0.f, 0.f}, { 1.f, 1.f, 0.f, 0.f }, { 0.f, 0.f, 0.f, 0.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f, 2.f }); + const auto tangentsIn = m_logicEngine->createDataArray(std::vector{ { 0.f, 0.f, 0.f, 0.f }, { 1.f, 1.f, 0.f, 0.f }, { 1.f, 1.f, 0.f, 0.f } }); + const auto tangentsOut = m_logicEngine->createDataArray(std::vector{ { 1.f, 1.f, 0.f, 0.f}, { 1.f, 1.f, 0.f, 0.f }, { 0.f, 0.f, 0.f, 0.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, m_dataVec4, EInterpolationType::Cubic_Quaternions, tangentsIn, tangentsOut } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 1, 0, 0, 0 }); @@ -640,10 +638,10 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatesKeyframeValues_cubic_vec2i) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 0, 10 }, { 1, 20 } }); - const auto tangentsIn = m_logicEngine.createDataArray(std::vector{ { 0, 0 }, { -1, -2 } }); - const auto tangentsOut = m_logicEngine.createDataArray(std::vector{ { 2, 5 }, { 0, 0 } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 0, 10 }, { 1, 20 } }); + const auto tangentsIn = m_logicEngine->createDataArray(std::vector{ { 0, 0 }, { -1, -2 } }); + const auto tangentsOut = m_logicEngine->createDataArray(std::vector{ { 2, 5 }, { 0, 0 } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Cubic, tangentsIn, tangentsOut }, }); @@ -658,8 +656,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, InterpolatedValueBeforeFirstTimestampIsFirstKeyframe) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 1.f, 20.f }, { 2.f, 30.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 1.f, 20.f }, { 2.f, 30.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); advanceAnimationAndExpectValues(*animNode, 0.f, { 1.f, 20.f }); @@ -671,8 +669,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, CanJumpAnywhereInAnimation) { - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 10.f, 20.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 10.f, 20.f } }); const auto animNode = createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); advanceAnimationAndExpectValues(*animNode, 0.f, 10.f); @@ -687,8 +685,8 @@ namespace ramses::internal TEST_P(AnAnimationNode, GivesStableResultsWithExtremelySmallTimestamps) { constexpr float Eps = std::numeric_limits::epsilon(); - const auto timeStamps = m_logicEngine.createDataArray(std::vector{ Eps * 100, Eps * 200 }); - const auto data = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f } }); + const auto timeStamps = m_logicEngine->createDataArray(std::vector{ Eps * 100, Eps * 200 }); + const auto data = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f } }); auto& animNode = *createAnimationNode({ { "channel", timeStamps, data, EInterpolationType::Linear } }); // initialize output value by updating with zero progress and expect first keyframe value @@ -700,7 +698,7 @@ namespace ramses::internal const float progress = 0.01f * float(i); animNode.getInputs()->getChild("progress")->set(progress); - m_logicEngine.update(); + m_logicEngine->update(); const auto val = *animNode.getOutputs()->getChild("channel")->get(); // expect interpolated value to go through keyframes in stable manner @@ -716,12 +714,12 @@ namespace ramses::internal std::vector vecDataExceeding; vecDataExceeding.resize(MaxArrayPropertySize + 1u); std::iota(vecDataExceeding.begin(), vecDataExceeding.end(), 1.f); - const auto dataExceeding = m_logicEngine.createDataArray(vecDataExceeding); + const auto dataExceeding = m_logicEngine->createDataArray(vecDataExceeding); const AnimationChannel channelExceeding{ "channel2", dataExceeding, dataExceeding }; AnimationNodeConfig config; config.addChannel(channelExceeding); - EXPECT_NE(nullptr, m_logicEngine.createAnimationNode(config, "animNode")); + EXPECT_NE(nullptr, m_logicEngine->createAnimationNode(config, "animNode")); } class AnAnimationNode_SerializationLifecycle : public AnAnimationNode @@ -752,10 +750,10 @@ namespace ramses::internal { flatbuffers::FlatBufferBuilder flatBufferBuilder; SerializationMap serializationMap; - DeserializationMap deserializationMap; + DeserializationMap deserializationMap{ m_scene->impl() }; { - const auto data = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }); + const auto data = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); HierarchicalTypeData inputs = MakeStruct("", {}); if (issue != ESerializationIssue::PropertyInMissing) @@ -787,7 +785,7 @@ namespace ramses::internal outputs.children.push_back(MakeType("channel", EPropertyType::Float)); auto outputsImpl = std::make_unique(std::move(outputs), EPropertySemantics::AnimationOutput); - const auto dataFb = DataArrayImpl::Serialize(data->m_impl, flatBufferBuilder, serializationMap); + const auto dataFb = DataArrayImpl::Serialize(data->impl(), flatBufferBuilder, serializationMap); flatBufferBuilder.Finish(dataFb); const auto dataFbSerialized = flatbuffers::GetRoot(flatBufferBuilder.GetBufferPointer()); deserializationMap.storeDataArray(*dataFbSerialized, *data); @@ -832,14 +830,14 @@ namespace ramses::internal TEST_P(AnAnimationNode_SerializationLifecycle, FailsDeserializationIfEssentialDataMissing) { EXPECT_TRUE(deserializeSerializedDataWithIssue(AnAnimationNode_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); for (const auto issue : { ESerializationIssue::NameMissing, ESerializationIssue::IdMissing, ESerializationIssue::ChannelsMissing, ESerializationIssue::RootInMissing, ESerializationIssue::RootOutMissing }) { EXPECT_FALSE(deserializeSerializedDataWithIssue(issue)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of AnimationNode from serialized data: missing name, id, channels or in/out property data!", m_errorReporting.getErrors().back().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AnimationNode from serialized data: missing name, id, channels or in/out property data!", m_errorReporting.getError()->message); + m_errorReporting.reset(); } } @@ -848,9 +846,9 @@ namespace ramses::internal for (const auto issue : { ESerializationIssue::ChannelTimestampsMissing, ESerializationIssue::ChannelKeyframesMissing }) { EXPECT_FALSE(deserializeSerializedDataWithIssue(issue)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode' channel data: missing name, timestamps or keyframes!", m_errorReporting.getErrors().front().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode' channel data: missing name, timestamps or keyframes!", m_errorReporting.getError()->message); + m_errorReporting.reset(); } } @@ -859,18 +857,17 @@ namespace ramses::internal for (const auto issue : { ESerializationIssue::ChannelTangentsInMissing, ESerializationIssue::ChannelTangentsOutMissing }) { EXPECT_FALSE(deserializeSerializedDataWithIssue(issue)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode' channel 'channel' data: missing tangents!", m_errorReporting.getErrors().front().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode' channel 'channel' data: missing tangents!", m_errorReporting.getError()->message); + m_errorReporting.reset(); } } TEST_P(AnAnimationNode_SerializationLifecycle, FailsDeserializationIfInvalidInterpolationType) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ESerializationIssue::InvalidInterpolationType)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode' channel 'channel' data: missing or invalid interpolation type!", m_errorReporting.getErrors().front().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode' channel 'channel' data: missing or invalid interpolation type!", m_errorReporting.getError()->message); } TEST_P(AnAnimationNode_SerializationLifecycle, FailsDeserializationIfInvalidOrMissingProperties) @@ -878,16 +875,16 @@ namespace ramses::internal for (const auto issue : { ESerializationIssue::PropertyInMissing, ESerializationIssue::PropertyOutMissing, ESerializationIssue::PropertyInWrongName, ESerializationIssue::PropertyOutWrongName }) { EXPECT_FALSE(deserializeSerializedDataWithIssue(issue)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode': missing or invalid properties!", m_errorReporting.getErrors().front().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode': missing or invalid properties!", m_errorReporting.getError()->message); + m_errorReporting.reset(); } } TEST_P(AnAnimationNode_SerializationLifecycle, FailsDeserializationIfInvalidChannelsData) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ESerializationIssue::PropertyChannelsDataInvalid)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode': missing or invalid channels data property!", m_errorReporting.getErrors().front().message); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AnimationNode 'animNode': missing or invalid channels data property!", m_errorReporting.getError()->message); } } diff --git a/client/logic/unittests/api/AnimationNodeWithDataPropertiesTest.cpp b/tests/unittests/client/logic/api/AnimationNodeWithDataPropertiesTest.cpp similarity index 88% rename from client/logic/unittests/api/AnimationNodeWithDataPropertiesTest.cpp rename to tests/unittests/client/logic/api/AnimationNodeWithDataPropertiesTest.cpp index a61ad18c5..44ea2c6da 100644 --- a/client/logic/unittests/api/AnimationNodeWithDataPropertiesTest.cpp +++ b/tests/unittests/client/logic/api/AnimationNodeWithDataPropertiesTest.cpp @@ -7,26 +7,24 @@ // ------------------------------------------------------------------------- #include -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/Property.h" -#include "WithTempDirectory.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/Property.h" +#include "LogicEngineTest_Base.h" #include namespace ramses::internal { // all operations and behavior not specific to node 'with data properties' is tested in AnimationNodeTest.cpp - class AnAnimationNodeWithDataProperties : public ::testing::Test + class AnAnimationNodeWithDataProperties : public ALogicEngine { public: void SetUp() override { - m_dataFloat1 = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f, 2.f }); - m_dataFloat2 = m_logicEngine.createDataArray(std::vector{ 0.f, 10.f, 20.f }); - - m_configSaveFileWithoutValidation.setValidationEnabled(false); + m_dataFloat1 = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f, 2.f }); + m_dataFloat2 = m_logicEngine->createDataArray(std::vector{ 0.f, 10.f, 20.f }); } protected: @@ -41,7 +39,7 @@ namespace ramses::internal if (!config.setExposingOfChannelDataAsProperties(true)) return nullptr; - return m_logicEngine.createAnimationNode(config, name); + return m_logicEngine->createAnimationNode(config, name); } static void ExpectArrayDataProperty(const Property& arrayDataProp, const std::vector& arrayValues) @@ -77,15 +75,13 @@ namespace ramses::internal void advanceAnimationAndExpectValues(AnimationNode& animNode, float progress, float expectedValue) { EXPECT_TRUE(animNode.getInputs()->getChild("progress")->set(progress)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto val = *animNode.getOutputs()->getChild("channel")->get(); EXPECT_FLOAT_EQ(expectedValue, val); } - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; DataArray* m_dataFloat1 = nullptr; DataArray* m_dataFloat2 = nullptr; - SaveFileConfig m_configSaveFileWithoutValidation; }; TEST_F(AnAnimationNodeWithDataProperties, HasAnimationDataProperties) @@ -225,7 +221,7 @@ namespace ramses::internal animNode->getInputs()->getChild("channelsData")->getChild("channel")->getChild("timestamps")->getChild(2u)->set(50.f); animNode->getInputs()->getChild("channelsData")->getChild("channel")->getChild("keyframes")->getChild(2u)->set(1000.f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const AnimationChannels& channels = animNode->getChannels(); ASSERT_EQ(1u, channels.size()); @@ -237,10 +233,10 @@ namespace ramses::internal TEST_F(AnAnimationNodeWithDataProperties, CanBeSerializedAndDeserialized) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_logicEngine; const auto data1 = otherEngine.createDataArray(std::vector{ 0.f, 1.f, 2.f }, "data1"); const auto data2 = otherEngine.createDataArray(std::vector{ 0.f, 10.f, 20.f }, "data2"); @@ -252,15 +248,15 @@ namespace ramses::internal ASSERT_TRUE(config.addChannel(channel2)); ASSERT_TRUE(otherEngine.createAnimationNode(config, "animNode")); - ASSERT_TRUE(otherEngine.saveToFile("logic_animNodes.bin", m_configSaveFileWithoutValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("logic_animNodes.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("logic_animNodes.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + ASSERT_TRUE(recreateFromFile("logic_animNodes.bin")); + expectNoError(); - const auto data1 = m_logicEngine.findByName("data1"); - const auto data2 = m_logicEngine.findByName("data2"); - const auto animNode = m_logicEngine.findByName("animNode"); + const auto data1 = m_logicEngine->findObject("data1"); + const auto data2 = m_logicEngine->findObject("data2"); + const auto animNode = m_logicEngine->findObject("animNode"); ASSERT_TRUE(data1 && data2 && animNode); EXPECT_EQ("animNode", animNode->getName()); @@ -286,10 +282,10 @@ namespace ramses::internal TEST_F(AnAnimationNodeWithDataProperties, ResetsChannelDataToOriginalValuesWhenLoadedFromFile) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_logicEngine; const auto data1 = otherEngine.createDataArray(std::vector{ 0.f, 1.f, 2.f }, "data1"); const auto data2 = otherEngine.createDataArray(std::vector{ 0.f, 10.f, 20.f }, "data2"); @@ -305,16 +301,16 @@ namespace ramses::internal animNode->getInputs()->getChild("channelsData")->getChild("channel")->getChild("keyframes")->getChild(2u)->set(1000.f); EXPECT_TRUE(otherEngine.update()); - ASSERT_TRUE(otherEngine.saveToFile("logic_animNodes.bin", m_configSaveFileWithoutValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("logic_animNodes.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("logic_animNodes.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - EXPECT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(recreateFromFile("logic_animNodes.bin")); + expectNoError(); + EXPECT_TRUE(m_logicEngine->update()); - const auto data1 = m_logicEngine.findByName("data1"); - const auto data2 = m_logicEngine.findByName("data2"); - const auto animNode = m_logicEngine.findByName("animNode"); + const auto data1 = m_logicEngine->findObject("data1"); + const auto data2 = m_logicEngine->findObject("data2"); + const auto animNode = m_logicEngine->findObject("animNode"); ASSERT_TRUE(data1 && data2 && animNode); EXPECT_EQ("animNode", animNode->getName()); diff --git a/client/logic/unittests/api/RamsesAppearanceBindingTest.cpp b/tests/unittests/client/logic/api/AppearanceBindingTest.cpp similarity index 57% rename from client/logic/unittests/api/RamsesAppearanceBindingTest.cpp rename to tests/unittests/client/logic/api/AppearanceBindingTest.cpp index a6a834650..8bd6078c7 100644 --- a/client/logic/unittests/api/RamsesAppearanceBindingTest.cpp +++ b/tests/unittests/client/logic/api/AppearanceBindingTest.cpp @@ -11,56 +11,57 @@ #include "RamsesObjectResolverMock.h" #include "RamsesTestUtils.h" #include "SerializationTestUtils.h" -#include "WithTempDirectory.h" -#include "impl/LogicEngineImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/RamsesHelper.h" -#include "generated/RamsesAppearanceBindingGen.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkImpl.h" +#include "internal/logic/RamsesHelper.h" +#include "internal/logic/flatbuffers/generated/AppearanceBindingGen.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/RamsesAppearanceBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/AppearanceBinding.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Appearance.h" +#include "ramses/client/Effect.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Appearance.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" #include namespace ramses::internal { - class ARamsesAppearanceBinding : public ALogicEngine + class AAppearanceBinding : public ALogicEngine { protected: }; - TEST_F(ARamsesAppearanceBinding, HasANameAfterCreation) + TEST_F(AAppearanceBinding, HasANameAfterCreation) { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); EXPECT_EQ("AppearanceBinding", appearanceBinding.getName()); } - TEST_F(ARamsesAppearanceBinding, HasNoOutputsAfterCreation) + TEST_F(AAppearanceBinding, HasNoOutputsAfterCreation) { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); EXPECT_EQ(nullptr, appearanceBinding.getOutputs()); } - TEST_F(ARamsesAppearanceBinding, ProducesNoErrorsDuringUpdate_IfNoRamsesAppearanceIsAssigned) + TEST_F(AAppearanceBinding, ProducesNoErrorsDuringUpdate_IfNoRamsesAppearanceIsAssigned) { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - EXPECT_EQ(std::nullopt, appearanceBinding.m_impl.update()); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); + EXPECT_EQ(std::nullopt, appearanceBinding.impl().update()); } - // This fixture only contains serialization unit tests, for higher order tests see `ARamsesAppearanceBinding_WithRamses_AndFiles` - class ARamsesAppearanceBinding_SerializationLifecycle : public ARamsesAppearanceBinding + // This fixture only contains serialization unit tests, for higher order tests see `AAppearanceBinding_WithRamses_AndFiles` + class AAppearanceBinding_SerializationLifecycle : public AAppearanceBinding { protected: flatbuffers::FlatBufferBuilder m_flatBufferBuilder; @@ -68,21 +69,21 @@ namespace ramses::internal ::testing::StrictMock m_resolverMock; ErrorReporting m_errorReporting; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; }; // More unit tests with inputs/outputs declared in LogicNode (base class) serialization tests - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, RemembersBaseClassData) + TEST_F(AAppearanceBinding_SerializationLifecycle, RemembersBaseClassData) { // Serialize { - RamsesAppearanceBindingImpl binding(*m_appearance, "name", 1u); + AppearanceBindingImpl binding(m_scene->impl(), *m_appearance, "name", sceneObjectId_t{ 1u }); binding.createRootProperties(); - (void)RamsesAppearanceBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + (void)AppearanceBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); } // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); ASSERT_TRUE(serializedBinding.base()); ASSERT_TRUE(serializedBinding.base()->base()); @@ -98,26 +99,26 @@ namespace ramses::internal // Deserialize { EXPECT_CALL(m_resolverMock, findRamsesAppearanceInScene(::testing::Eq("name"), m_appearance->getSceneObjectId())).WillOnce(::testing::Return(m_appearance)); - std::unique_ptr deserializedBinding = RamsesAppearanceBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + std::unique_ptr deserializedBinding = AppearanceBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); ASSERT_TRUE(deserializedBinding); EXPECT_EQ(deserializedBinding->getName(), "name"); - EXPECT_EQ(deserializedBinding->getId(), 1u); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_EQ(deserializedBinding->getSceneObjectId().getValue(), 1u); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, RemembersRamsesAppearanceIdAndType_AndEffectHash) + TEST_F(AAppearanceBinding_SerializationLifecycle, RemembersRamsesAppearanceIdAndType_AndEffectHash) { // Serialize { - RamsesAppearanceBindingImpl binding(*m_appearance, "name", 1u); + AppearanceBindingImpl binding(m_scene->impl(), *m_appearance, "name", sceneObjectId_t{ 1u }); binding.createRootProperties(); - (void)RamsesAppearanceBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + (void)AppearanceBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); } // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); ASSERT_TRUE(serializedBinding.base()->boundRamsesObject()); EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectId(), m_appearance->getSceneObjectId().getValue()); @@ -128,36 +129,36 @@ namespace ramses::internal // Deserialize { EXPECT_CALL(m_resolverMock, findRamsesAppearanceInScene(::testing::Eq("name"), m_appearance->getSceneObjectId())).WillOnce(::testing::Return(m_appearance)); - std::unique_ptr deserializedBinding = RamsesAppearanceBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + std::unique_ptr deserializedBinding = AppearanceBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); ASSERT_TRUE(deserializedBinding); EXPECT_EQ(&deserializedBinding->getRamsesAppearance(), m_appearance); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); // Check that input was deserialized too EXPECT_EQ(deserializedBinding->getInputs()->getChild("floatUniform")->getType(), EPropertyType::Float); } } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) + TEST_F(AAppearanceBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) { { - auto binding = rlogic_serialization::CreateRamsesAppearanceBinding( + auto binding = rlogic_serialization::CreateAppearanceBinding( m_flatBufferBuilder, 0 // no base binding info ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesAppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = AppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesAppearanceBinding from serialized data: missing base class info!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AppearanceBinding from serialized data: missing base class info!"); } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ErrorWhenNoBindingName) + TEST_F(AAppearanceBinding_SerializationLifecycle, ErrorWhenNoBindingName) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -166,23 +167,22 @@ namespace ramses::internal 0, // no name! 1u) ); - auto binding = rlogic_serialization::CreateRamsesAppearanceBinding( + auto binding = rlogic_serialization::CreateAppearanceBinding( m_flatBufferBuilder, base ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesAppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = AppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesAppearanceBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AppearanceBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ErrorWhenNoBindingId) + TEST_F(AAppearanceBinding_SerializationLifecycle, ErrorWhenNoBindingId) { { auto base = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, @@ -190,20 +190,19 @@ namespace ramses::internal m_flatBufferBuilder.CreateString("name"), 0) // no id ); - auto binding = rlogic_serialization::CreateRamsesAppearanceBinding(m_flatBufferBuilder, base); + auto binding = rlogic_serialization::CreateAppearanceBinding(m_flatBufferBuilder, base); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesAppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = AppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesAppearanceBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of AppearanceBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ErrorWhenNoRootInput) + TEST_F(AAppearanceBinding_SerializationLifecycle, ErrorWhenNoRootInput) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -213,22 +212,22 @@ namespace ramses::internal 1u), 0 // no root input ); - auto binding = rlogic_serialization::CreateRamsesAppearanceBinding( + auto binding = rlogic_serialization::CreateAppearanceBinding( m_flatBufferBuilder, base ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesAppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = AppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesAppearanceBinding from serialized data: missing root input!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AppearanceBinding from serialized data: missing root input!"); } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ErrorWhenBoundAppearanceCannotBeResolved) + TEST_F(AAppearanceBinding_SerializationLifecycle, ErrorWhenBoundAppearanceCannotBeResolved) { const ramses::sceneObjectId_t mockObjectId {12}; { @@ -244,7 +243,7 @@ namespace ramses::internal ramsesRef, m_testUtils.serializeTestProperty("") ); - auto binding = rlogic_serialization::CreateRamsesAppearanceBinding( + auto binding = rlogic_serialization::CreateAppearanceBinding( m_flatBufferBuilder, base ); @@ -253,13 +252,13 @@ namespace ramses::internal EXPECT_CALL(m_resolverMock, findRamsesAppearanceInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(nullptr)); - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesAppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = AppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) + TEST_F(AAppearanceBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -270,22 +269,22 @@ namespace ramses::internal 0, m_testUtils.serializeTestProperty("", rlogic_serialization::EPropertyRootType::Struct, false, true) // rootInput with errors ); - auto binding = rlogic_serialization::CreateRamsesAppearanceBinding( + auto binding = rlogic_serialization::CreateAppearanceBinding( m_flatBufferBuilder, base ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesAppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = AppearanceBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); } - TEST_F(ARamsesAppearanceBinding_SerializationLifecycle, ReportsErrorWhenDeserializedWithDifferentAppearanceThenDuringSerialization) + TEST_F(AAppearanceBinding_SerializationLifecycle, ReportsErrorWhenDeserializedWithDifferentAppearanceThenDuringSerialization) { // Use different shaders than the "trivial ones" below to force different appearance inputs std::string_view differentVertShader = R"( @@ -311,26 +310,26 @@ namespace ramses::internal // Serialize { - RamsesAppearanceBindingImpl binding(*m_appearance, "name", 1u); + AppearanceBindingImpl binding(m_scene->impl(), *m_appearance, "name", sceneObjectId_t{ 1u }); binding.createRootProperties(); - (void)RamsesAppearanceBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + (void)AppearanceBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); } // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); // Deserialize with different appearance -> expect errors { EXPECT_CALL(m_resolverMock, findRamsesAppearanceInScene(::testing::Eq("name"), m_appearance->getSceneObjectId())).WillOnce(::testing::Return(&differentAppearance)); - std::unique_ptr deserializedBinding = RamsesAppearanceBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + std::unique_ptr deserializedBinding = AppearanceBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserializedBinding); - EXPECT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesAppearanceBinding from serialized data: effect signature doesn't match after loading!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of AppearanceBinding from serialized data: effect signature doesn't match after loading!"); } } - class ARamsesAppearanceBinding_WithRamses : public ARamsesAppearanceBinding + class AAppearanceBinding_WithRamses : public AAppearanceBinding { protected: const std::string_view m_vertShader_simple = R"( @@ -358,6 +357,7 @@ namespace ramses::internal #version 300 es uniform highp float floatUniform; + uniform bool boolUniform; uniform highp int intUniform; uniform highp ivec2 ivec2Uniform; uniform highp ivec3 ivec3Uniform; @@ -365,6 +365,7 @@ namespace ramses::internal uniform highp vec2 vec2Uniform; uniform highp vec3 vec3Uniform; uniform highp vec4 vec4Uniform; + uniform bool boolArray[2]; uniform highp ivec2 ivec2Array[2]; uniform highp vec2 vec2Array[2]; uniform highp ivec3 ivec3Array[2]; @@ -396,47 +397,37 @@ namespace ramses::internal return *m_scene->createEffect(effectDesc); } - ramses::Appearance& createTestAppearance(ramses::Effect& effect) + ramses::Appearance& createTestAppearance(ramses::Effect& effect, std::string_view name = "test appearance") { - return *m_scene->createAppearance(effect, "test appearance"); - } - - void expectErrorWhenLoadingFile(std::string_view fileName, std::string_view message) - { - EXPECT_FALSE(m_logicEngine.loadFromFile(fileName, m_scene)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(std::string(message), m_logicEngine.getErrors()[0].message); + return *m_scene->createAppearance(effect, name); } static float GetUniformValueFloat(ramses::Appearance& appearance, std::string_view uniformName) { - ramses::UniformInput uniform; - appearance.getEffect().findUniformInput(uniformName, uniform); - assert(uniform.isValid()); + const auto optUniform = appearance.getEffect().findUniformInput(uniformName); + assert(optUniform.has_value()); float value = 0.f; - appearance.getInputValue(uniform, value); + appearance.getInputValue(*optUniform, value); return value; } - static void SetUniformValueFloat(ramses::Appearance& appearance, std::string_view uniformName, float value) { - ramses::UniformInput uniform; - appearance.getEffect().findUniformInput(uniformName, uniform); - assert(uniform.isValid()); - appearance.setInputValue(uniform, value); + const auto optUniform = appearance.getEffect().findUniformInput(uniformName); + assert(optUniform.has_value()); + appearance.setInputValue(*optUniform, value); } }; - TEST_F(ARamsesAppearanceBinding_WithRamses, ReturnsReferenceToRamsesAppearance) + TEST_F(AAppearanceBinding_WithRamses, ReturnsReferenceToRamsesAppearance) { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); EXPECT_EQ(m_appearance, &appearanceBinding.getRamsesAppearance()); } - TEST_F(ARamsesAppearanceBinding_WithRamses, HasInputsAfterCreation) + TEST_F(AAppearanceBinding_WithRamses, HasInputsAfterCreation) { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); auto inputs = appearanceBinding.getInputs(); @@ -447,19 +438,18 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::Float, floatUniform->getType()); } - TEST_F(ARamsesAppearanceBinding_WithRamses, GivesInputs_BindingInputSemantics) + TEST_F(AAppearanceBinding_WithRamses, GivesInputs_BindingInputSemantics) { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - - auto inputs = appearanceBinding.getInputs(); - const auto inputCount = inputs->getChildCount(); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto inputs = appearanceBinding.getInputs(); + const auto inputCount = inputs->getChildCount(); for (size_t i = 0; i < inputCount; ++i) { - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild(i)->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild(i)->impl().getPropertySemantics()); } } - TEST_F(ARamsesAppearanceBinding_WithRamses, CreatesOnlyInputsForSupportedUniformTypes) + TEST_F(AAppearanceBinding_WithRamses, CreatesOnlyInputsForSupportedUniformTypes) { const std::string_view fragShader_ManyUniformTypes = R"( @@ -496,7 +486,7 @@ namespace ramses::internal })"; ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_simple, fragShader_ManyUniformTypes)); - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); const auto inputs = appearanceBinding.getInputs(); ASSERT_EQ(7u, inputs->getChildCount()); @@ -533,20 +523,23 @@ namespace ramses::internal } } - TEST_F(ARamsesAppearanceBinding_WithRamses, UpdatesAppearanceIfInputValuesWereSet) + TEST_F(AAppearanceBinding_WithRamses, UpdatesAppearanceIfInputValuesWereSet) { ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_allTypes, m_fragShader_trivial)); - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); auto inputs = appearanceBinding.getInputs(); - ASSERT_EQ(15u, inputs->getChildCount()); + ASSERT_EQ(17u, inputs->getChildCount()); EXPECT_TRUE(inputs->getChild("floatUniform")->set(42.42f)); + EXPECT_TRUE(inputs->getChild("boolUniform")->set(true)); EXPECT_TRUE(inputs->getChild("intUniform")->set(42)); EXPECT_TRUE(inputs->getChild("vec2Uniform")->set({ 0.1f, 0.2f })); EXPECT_TRUE(inputs->getChild("vec3Uniform")->set({ 1.1f, 1.2f, 1.3f })); EXPECT_TRUE(inputs->getChild("vec4Uniform")->set({ 2.1f, 2.2f, 2.3f, 2.4f })); EXPECT_TRUE(inputs->getChild("ivec2Uniform")->set({ 1, 2 })); EXPECT_TRUE(inputs->getChild("ivec3Uniform")->set({ 3, 4, 5 })); - EXPECT_TRUE(inputs->getChild("ivec4Uniform")->set({ 6, 7, 8, 9 })); + EXPECT_TRUE(inputs->getChild("ivec4Uniform")->set({6, 7, 8, 9})); + EXPECT_TRUE(inputs->getChild("boolArray")->getChild(0)->set(false)); + EXPECT_TRUE(inputs->getChild("boolArray")->getChild(1)->set(true)); EXPECT_TRUE(inputs->getChild("ivec2Array")->getChild(0)->set({ 11, 12 })); EXPECT_TRUE(inputs->getChild("ivec2Array")->getChild(1)->set({ 13, 14 })); EXPECT_TRUE(inputs->getChild("vec2Array")->getChild(0)->set({ .11f, .12f })); @@ -560,104 +553,134 @@ namespace ramses::internal EXPECT_TRUE(inputs->getChild("vec4Array")->getChild(0)->set({ .41f, .42f, .43f, .44f })); EXPECT_TRUE(inputs->getChild("vec4Array")->getChild(1)->set({ .45f, .46f, .47f, .48f })); - EXPECT_EQ(std::nullopt, appearanceBinding.m_impl.update()); + EXPECT_EQ(std::nullopt, appearanceBinding.impl().update()); - ramses::UniformInput uniform; + auto& effect = appearance.getEffect(); + std::optional optUniform; { float resultFloat = 0.f; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("floatUniform", uniform)); - appearance.getInputValue(uniform, resultFloat); + optUniform = effect.findUniformInput("floatUniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, resultFloat); EXPECT_FLOAT_EQ(42.42f, resultFloat); } + { + bool resultBool = true; + optUniform = effect.findUniformInput("boolUniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, resultBool); + EXPECT_EQ(true, resultBool); + } { int32_t resultInt32 = 0; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("intUniform", uniform)); - appearance.getInputValue(uniform, resultInt32); + optUniform = effect.findUniformInput("intUniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, resultInt32); EXPECT_EQ(42, resultInt32); } { ramses::vec2f result{ 0.0f, 0.0f }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec2Uniform", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("vec2Uniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec2f(0.1f, 0.2f)); } { ramses::vec3f result{ 0.0f, 0.0f, 0.0f }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec3Uniform", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("vec3Uniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec3f(1.1f, 1.2f, 1.3f)); } { ramses::vec4f result{ 0.0f, 0.0f, 0.0f, 0.0f }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec4Uniform", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("vec4Uniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec4f(2.1f, 2.2f, 2.3f, 2.4f)); - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec4Uniform_shouldHaveDefaultValue", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("vec4Uniform_shouldHaveDefaultValue"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec4f(0.0f, 0.0f, 0.0f, 0.0f)); } { ramses::vec2i result{ 0, 0 }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("ivec2Uniform", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("ivec2Uniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec2i(1, 2)); } { ramses::vec3i result{ 0, 0, 0 }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("ivec3Uniform", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("ivec3Uniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec3i(3, 4, 5)); } { ramses::vec4i result{ 0, 0, 0, 0 }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("ivec4Uniform", uniform)); - appearance.getInputValue(uniform, result); + optUniform = effect.findUniformInput("ivec4Uniform"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, result); EXPECT_EQ(result, vec4i(6, 7, 8, 9)); } // Arrays + { + std::array result{false}; + optUniform = effect.findUniformInput("boolArray"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); + EXPECT_THAT(result, ::testing::ElementsAre(false, true)); + } { std::array result{ ramses::vec2i{0, 0}, ramses::vec2i{0, 0} }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("ivec2Array", uniform)); - appearance.getInputValue(uniform, 2, result.data()); + optUniform = effect.findUniformInput("ivec2Array"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); EXPECT_THAT(result, ::testing::ElementsAre(ramses::vec2i{ 11, 12 }, ramses::vec2i{ 13, 14 })); } { std::array result{ ramses::vec2f{0.0f, 0.0f}, ramses::vec2f{0.0f, 0.0f} }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec2Array", uniform)); - appearance.getInputValue(uniform, 2, result.data()); + optUniform = effect.findUniformInput("vec2Array"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); EXPECT_THAT(result, ::testing::ElementsAre(ramses::vec2f{ .11f, .12f }, ramses::vec2f{ .13f, .14f })); } { std::array result{ ramses::vec3i{0, 0, 0}, ramses::vec3i{0, 0, 0} }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("ivec3Array", uniform)); - appearance.getInputValue(uniform, 2, result.data()); + optUniform = effect.findUniformInput("ivec3Array"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); EXPECT_THAT(result, ::testing::ElementsAre(ramses::vec3i{ 31, 32, 33 }, ramses::vec3i{ 34, 35, 36 })); } { std::array result = { ramses::vec3f{0.0f, 0.0f, 0.0f}, ramses::vec3f{0.0f, 0.0f, 0.0f} }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec3Array", uniform)); - appearance.getInputValue(uniform, 2, result.data()); + optUniform = effect.findUniformInput("vec3Array"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); EXPECT_THAT(result, ::testing::ElementsAre(ramses::vec3f{ .31f, .32f, .33f }, ramses::vec3f{ .34f, .35f, .36f })); } { std::array result = { ramses::vec4i{0, 0, 0, 0}, ramses::vec4i{0, 0, 0, 0} }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("ivec4Array", uniform)); - appearance.getInputValue(uniform, 2, result.data()); + optUniform = effect.findUniformInput("ivec4Array"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); EXPECT_THAT(result, ::testing::ElementsAre(ramses::vec4i{ 41, 42, 43, 44 }, ramses::vec4i{ 45, 46, 47, 48 })); } { std::array result = { ramses::vec4f{0.0f, 0.0f, 0.0f, 0.0f}, ramses::vec4f{0.0f, 0.0f, 0.0f, 0.0f} }; - ASSERT_EQ(ramses::StatusOK, appearance.getEffect().findUniformInput("vec4Array", uniform)); - appearance.getInputValue(uniform, 2, result.data()); + optUniform = effect.findUniformInput("vec4Array"); + ASSERT_TRUE(optUniform.has_value()); + appearance.getInputValue(*optUniform, 2, result.data()); EXPECT_THAT(result, ::testing::ElementsAre(ramses::vec4f{ .41f, .42f, .43f, .44f }, ramses::vec4f{ .45f, .46f, .47f, .48f })); } } - TEST_F(ARamsesAppearanceBinding_WithRamses, PropagateItsInputsToRamsesAppearanceOnUpdate_OnlyWhenExplicitlySet) + TEST_F(AAppearanceBinding_WithRamses, PropagateItsInputsToRamsesAppearanceOnUpdate_OnlyWhenExplicitlySet) { ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_twoUniforms, m_fragShader_trivial)); - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); // Set values directly to ramses appearance SetUniformValueFloat(appearance, "floatUniform1", 11.f); @@ -666,17 +689,17 @@ namespace ramses::internal // Set only one of the inputs to the binding object, the other one (floatUniform2) not EXPECT_TRUE(appearanceBinding.getInputs()->getChild("floatUniform1")->set(100.f)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Only propagate the value which was also set in the binding object EXPECT_FLOAT_EQ(100.f, GetUniformValueFloat(appearance, "floatUniform1")); EXPECT_FLOAT_EQ(22.f, GetUniformValueFloat(appearance, "floatUniform2")); } - TEST_F(ARamsesAppearanceBinding_WithRamses, PropagatesItsInputsToRamsesAppearanceOnUpdate_WithLinksInsteadOfSetCall) + TEST_F(AAppearanceBinding_WithRamses, PropagatesItsInputsToRamsesAppearanceOnUpdate_WithLinksInsteadOfSetCall) { ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_twoUniforms, m_fragShader_trivial)); - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); // Set values directly to ramses appearance SetUniformValueFloat(appearance, "floatUniform1", 11.f); @@ -691,32 +714,36 @@ namespace ramses::internal OUT.float = 42.42 end )"; - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("float"), *appearanceBinding.getInputs()->getChild("floatUniform1"))); + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("float"), *appearanceBinding.getInputs()->getChild("floatUniform1"))); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Only propagate the value which was also linked over the binding object's input to a script EXPECT_FLOAT_EQ(42.42f, GetUniformValueFloat(appearance, "floatUniform1")); EXPECT_FLOAT_EQ(22.f, GetUniformValueFloat(appearance, "floatUniform2")); } - class ARamsesAppearanceBinding_WithRamses_AndFiles : public ARamsesAppearanceBinding_WithRamses + class AAppearanceBinding_WithRamses_AndFiles : public AAppearanceBinding_WithRamses { - protected: - WithTempDirectory tempFolder; + public: + AAppearanceBinding_WithRamses_AndFiles() + { + withTempDirectory(); + } }; - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, KeepsItsPropertiesAfterDeserialization_WhenNoRamsesLinksAndSceneProvided) + TEST_F(AAppearanceBinding_WithRamses_AndFiles, KeepsItsPropertiesAfterDeserialization_WhenNoRamsesLinksAndSceneProvided) { { - m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "appearancebinding.bin")); + m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); + ASSERT_TRUE(saveToFileWithoutValidation("appearancebinding.bin")); } { - ASSERT_TRUE(m_logicEngine.loadFromFile("appearancebinding.bin", m_scene)); - auto loadedAppearanceBinding = m_logicEngine.findByName("AppearanceBinding"); + ASSERT_TRUE(recreateFromFile("appearancebinding.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto loadedAppearanceBinding = m_logicEngine->findObject("AppearanceBinding"); EXPECT_EQ(&loadedAppearanceBinding->getRamsesAppearance(), m_appearance); EXPECT_EQ(loadedAppearanceBinding->getInputs()->getChildCount(), 1u); EXPECT_EQ(loadedAppearanceBinding->getOutputs(), nullptr); @@ -724,13 +751,14 @@ namespace ramses::internal } } - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, ContainsItsInputsAfterDeserialization_WithoutReorderingThem) + TEST_F(AAppearanceBinding_WithRamses_AndFiles, ContainsItsInputsAfterDeserialization_WithoutReorderingThem) { std::vector inputOrderBeforeSaving; - - ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_allTypes, m_fragShader_trivial)); + sceneObjectId_t appearanceId; { - auto& rAppearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_allTypes, m_fragShader_trivial)); + appearanceId = appearance.getSceneObjectId(); + auto& rAppearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); auto inputs = rAppearanceBinding.getInputs(); inputOrderBeforeSaving.reserve(inputs->getChildCount()); @@ -740,28 +768,32 @@ namespace ramses::internal } inputs->getChild("floatUniform")->set(42.42f); + inputs->getChild("boolUniform")->set(true); inputs->getChild("intUniform")->set(42); inputs->getChild("vec2Uniform")->set({ 0.1f, 0.2f }); inputs->getChild("vec3Uniform")->set({ 1.1f, 1.2f, 1.3f }); inputs->getChild("vec4Uniform")->set({ 2.1f, 2.2f, 2.3f, 2.4f }); inputs->getChild("ivec2Uniform")->set({ 1, 2 }); inputs->getChild("ivec3Uniform")->set({ 3, 4, 5 }); - inputs->getChild("ivec4Uniform")->set({ 6, 7, 8, 9 }); + inputs->getChild("ivec4Uniform")->set({6, 7, 8, 9}); + inputs->getChild("boolArray")->getChild(1)->set(true); + inputs->getChild("boolArray")->getChild(0)->set(false); inputs->getChild("ivec2Array")->getChild(0)->set({ 11, 12 }); inputs->getChild("ivec2Array")->getChild(1)->set({ 13, 14 }); inputs->getChild("vec2Array")->getChild(0)->set({ .11f, .12f }); inputs->getChild("vec2Array")->getChild(1)->set({ .13f, .14f }); - m_logicEngine.update(); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "logic.bin")); + m_logicEngine->update(); + ASSERT_TRUE(saveToFileWithoutValidation("logic.bin")); } { - ASSERT_TRUE(m_logicEngine.loadFromFile("logic.bin", m_scene)); - auto loadedAppearanceBinding = m_logicEngine.findByName("AppearanceBinding"); - EXPECT_EQ(loadedAppearanceBinding->getRamsesAppearance().getSceneObjectId(), appearance.getSceneObjectId()); + ASSERT_TRUE(recreateFromFile("logic.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto loadedAppearanceBinding = m_logicEngine->findObject("AppearanceBinding"); + EXPECT_EQ(loadedAppearanceBinding->getRamsesAppearance().getSceneObjectId(), appearanceId); const auto& inputs = loadedAppearanceBinding->getInputs(); - ASSERT_EQ(15u, inputs->getChildCount()); + ASSERT_EQ(17u, inputs->getChildCount()); // check order after deserialization for (size_t i = 0; i < inputOrderBeforeSaving.size(); ++i) @@ -771,57 +803,63 @@ namespace ramses::internal auto expectValues = [&inputs](){ EXPECT_FLOAT_EQ(42.42f, *inputs->getChild("floatUniform")->get()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("floatUniform")->impl().getPropertySemantics()); + EXPECT_EQ(true, *inputs->getChild("boolUniform")->get()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("boolUniform")->impl().getPropertySemantics()); EXPECT_EQ(42, *inputs->getChild("intUniform")->get()); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("intUniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("intUniform")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("vec2Uniform")->get(), vec2f(0.1f, 0.2f)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec2Uniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec2Uniform")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("vec3Uniform")->get(), vec3f(1.1f, 1.2f, 1.3f)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec3Uniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec3Uniform")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("vec4Uniform")->get(), vec4f(2.1f, 2.2f, 2.3f, 2.4f)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec4Uniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec4Uniform")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("vec4Uniform_shouldHaveDefaultValue")->get(), vec4f(.0f, .0f, .0f, .0f)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec4Uniform_shouldHaveDefaultValue")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec4Uniform_shouldHaveDefaultValue")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("ivec2Uniform")->get(), vec2i(1, 2)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec2Uniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec2Uniform")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("ivec3Uniform")->get(), vec3i(3, 4, 5)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec3Uniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec3Uniform")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("ivec4Uniform")->get(), vec4i(6, 7, 8, 9)); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec4Uniform")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec4Uniform")->impl().getPropertySemantics()); // Arrays + EXPECT_EQ(EPropertyType::Array, inputs->getChild("boolArray")->getType()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("boolArray")->impl().getPropertySemantics()); + EXPECT_EQ(inputs->getChild("boolArray")->getChild(0)->get(), false); + EXPECT_EQ(inputs->getChild("boolArray")->getChild(1)->get(), true); EXPECT_EQ(EPropertyType::Array, inputs->getChild("ivec2Array")->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec2Array")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("ivec2Array")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("ivec2Array")->getChild(0)->get(), vec2i(11, 12)); EXPECT_EQ(*inputs->getChild("ivec2Array")->getChild(1)->get(), vec2i(13, 14)); EXPECT_EQ(EPropertyType::Array, inputs->getChild("vec2Array")->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec2Array")->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild("vec2Array")->impl().getPropertySemantics()); EXPECT_EQ(*inputs->getChild("vec2Array")->getChild(0)->get(), vec2f(.11f, .12f)); EXPECT_EQ(*inputs->getChild("vec2Array")->getChild(1)->get(), vec2f(.13f, .14f)); }; expectValues(); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); expectValues(); } } - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, ContainsItsInputsAfterDeserialization_WhenRamsesSceneIsRecreatedBetweenSaveAndLoad) + TEST_F(AAppearanceBinding_WithRamses_AndFiles, ContainsItsInputsAfterDeserialization_WhenRamsesSceneIsRecreatedBetweenSaveAndLoad) { const ramses::sceneObjectId_t appearanceIdBeforeReload = m_appearance->getSceneObjectId(); { - auto& rAppearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto& rAppearanceBinding = *m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); rAppearanceBinding.getInputs()->getChild("floatUniform")->set(42.42f); - m_logicEngine.update(); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "logic.bin")); + m_logicEngine->update(); + ASSERT_TRUE(saveToFileWithoutValidation("logic.bin")); } - recreate(); - { - ASSERT_TRUE(m_logicEngine.loadFromFile("logic.bin", m_scene)); - auto loadedAppearanceBinding = m_logicEngine.findByName("AppearanceBinding"); + ASSERT_TRUE(recreateFromFile("logic.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto loadedAppearanceBinding = m_logicEngine->findObject("AppearanceBinding"); EXPECT_EQ(loadedAppearanceBinding->getRamsesAppearance().getSceneObjectId(), appearanceIdBeforeReload); const auto& inputs = loadedAppearanceBinding->getInputs(); @@ -830,67 +868,37 @@ namespace ramses::internal } } - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, ProducesError_WhenHavingLinkToAppearance_ButNoSceneWasProvided) - { - { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - tempEngineForSaving.createRamsesAppearanceBinding(*m_appearance, "AppBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "WithRamsesAppearance.bin")); - } - { - EXPECT_FALSE(m_logicEngine.loadFromFile("WithRamsesAppearance.bin")); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(errors.size(), 1u); - EXPECT_EQ(errors[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); - } - } - - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, ProducesErrorIfAppearanceHasDifferentEffectThanSerializedAppearanceBinding) - { - auto& appearance = createTestAppearance(createTestEffect(m_vertShader_simple, m_fragShader_trivial)); - auto* appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); - - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "logic.bin")); - - // Simulate that a difference appearance with the same ID was created, but with different inputs - recreate(); - createTestAppearance(createTestEffect(m_vertShader_allTypes, m_fragShader_trivial)); - - expectErrorWhenLoadingFile("logic.bin", - "Fatal error during loading of RamsesAppearanceBinding from serialized data: effect signature doesn't match after loading!"); - - // Did not overwrite existing objects (because loading from file failed) - EXPECT_EQ(appearanceBinding, m_logicEngine.findByName("AppearanceBinding")); - } - - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, DoesNotReapplyAppearanceUniformValuesToRamses_WhenLoadingFromFileAndCallingUpdate_UntilSetToANewValue) + TEST_F(AAppearanceBinding_WithRamses_AndFiles, DoesNotReapplyAppearanceUniformValuesToRamses_WhenLoadingFromFileAndCallingUpdate_UntilSetToANewValue) { - ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_simple, m_fragShader_trivial)); - { - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_simple, m_fragShader_trivial), "app01"); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); auto inputs = appearanceBinding.getInputs(); // Set a different input over the binding object inputs->getChild("floatUniform")->set(42.42f); - m_logicEngine.update(); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "SomeValuesSet.bin")); - } + m_logicEngine->update(); + + // Set uniform to a different value than the one set on the ramses binding + SetUniformValueFloat(appearance, "floatUniform", 100.0f); - // Set uniform to a different value than the one set on the ramses binding - SetUniformValueFloat(appearance, "floatUniform", 100.0f); + ASSERT_TRUE(saveToFileWithoutValidation("SomeValuesSet.bin")); + } { - EXPECT_TRUE(m_logicEngine.loadFromFile("SomeValuesSet.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(recreateFromFile("SomeValuesSet.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto appearance = m_scene->findObject("app01"); + ASSERT_TRUE(appearance != nullptr); + EXPECT_TRUE(m_logicEngine->update()); // loadFromFile and update should not set any values whatsoever ... - EXPECT_FLOAT_EQ(100.f, GetUniformValueFloat(appearance, "floatUniform")); + EXPECT_FLOAT_EQ(100.f, GetUniformValueFloat(*appearance, "floatUniform")); // ... unless explicitly set again on the binding object + update() called - m_logicEngine.findByName("AppearanceBinding")->getInputs()->getChild("floatUniform")->set(42.42f); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_FLOAT_EQ(42.42f, GetUniformValueFloat(appearance, "floatUniform")); + m_logicEngine->findObject("AppearanceBinding")->getInputs()->getChild("floatUniform")->set(42.42f); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_FLOAT_EQ(42.42f, GetUniformValueFloat(*appearance, "floatUniform")); } } @@ -900,11 +908,10 @@ namespace ramses::internal // - value only re-applied to ramses if changed. Otherwise not. // The general expectation is that after loading + update(), the logic scene would overwrite ramses // properties wrapped by a LogicBinding if they are linked to a script - TEST_F(ARamsesAppearanceBinding_WithRamses_AndFiles, SetsOnlyAppearanceUniformsForWhichTheBindingInputIsLinked_AfterLoadingFromFile_AndCallingUpdate) + TEST_F(AAppearanceBinding_WithRamses_AndFiles, SetsOnlyAppearanceUniformsForWhichTheBindingInputIsLinked_AfterLoadingFromFile_AndCallingUpdate) { - ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_twoUniforms, m_fragShader_trivial)); - { + ramses::Appearance& appearance = createTestAppearance(createTestEffect(m_vertShader_twoUniforms, m_fragShader_trivial), "app01"); const std::string_view scriptSrc = R"( function interface(IN,OUT) IN.float = Type:Float() @@ -915,39 +922,43 @@ namespace ramses::internal end )"; - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - auto& appearanceBinding = *m_logicEngine.createRamsesAppearanceBinding(appearance, "AppearanceBinding"); + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + auto& appearanceBinding = *m_logicEngine->createAppearanceBinding(appearance, "AppearanceBinding"); script->getInputs()->getChild("float")->set(42.42f); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("float"), *appearanceBinding.getInputs()->getChild("floatUniform1"))); - ASSERT_TRUE(m_logicEngine.update()); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "SomeValuesLinked.bin")); - } + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("float"), *appearanceBinding.getInputs()->getChild("floatUniform1"))); + ASSERT_TRUE(m_logicEngine->update()); - // Set uniform1 to a different value than the one set by the link - SetUniformValueFloat(appearance, "floatUniform1", 100.0f); - // Set uniform2 to custom value - it should not be overwritten by logic at all, because there is no link - // or any set() calls to the corresponding RamsesAppearanceBinding input - SetUniformValueFloat(appearance, "floatUniform2", 200.0f); + // Set uniform1 to a different value than the one set by the link + SetUniformValueFloat(appearance, "floatUniform1", 100.0f); + // Set uniform2 to custom value - it should not be overwritten by logic at all, because there is no link + // or any set() calls to the corresponding AppearanceBinding input + SetUniformValueFloat(appearance, "floatUniform2", 200.0f); + + ASSERT_TRUE(saveToFileWithoutValidation("SomeValuesLinked.bin")); + } { - EXPECT_TRUE(m_logicEngine.loadFromFile("SomeValuesLinked.bin", m_scene)); + ASSERT_TRUE(recreateFromFile("SomeValuesLinked.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto appearance = m_scene->findObject("app01"); + ASSERT_TRUE(appearance != nullptr); // nothing happens before update() - EXPECT_FLOAT_EQ(100.0f, GetUniformValueFloat(appearance, "floatUniform1")); - EXPECT_FLOAT_EQ(200.0f, GetUniformValueFloat(appearance, "floatUniform2")); + EXPECT_FLOAT_EQ(100.0f, GetUniformValueFloat(*appearance, "floatUniform1")); + EXPECT_FLOAT_EQ(200.0f, GetUniformValueFloat(*appearance, "floatUniform2")); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Script is executed -> link is activated -> binding is updated, only for the linked uniform - EXPECT_FLOAT_EQ(42.42f, GetUniformValueFloat(appearance, "floatUniform1")); - EXPECT_FLOAT_EQ(200.0f, GetUniformValueFloat(appearance, "floatUniform2")); + EXPECT_FLOAT_EQ(42.42f, GetUniformValueFloat(*appearance, "floatUniform1")); + EXPECT_FLOAT_EQ(200.0f, GetUniformValueFloat(*appearance, "floatUniform2")); // Reset uniform manually and call update does nothing (must set binding input explicitly to cause overwrite in ramses) - SetUniformValueFloat(appearance, "floatUniform1", 100.0f); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_FLOAT_EQ(100.0f, GetUniformValueFloat(appearance, "floatUniform1")); - EXPECT_FLOAT_EQ(200.0f, GetUniformValueFloat(appearance, "floatUniform2")); + SetUniformValueFloat(*appearance, "floatUniform1", 100.0f); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_FLOAT_EQ(100.0f, GetUniformValueFloat(*appearance, "floatUniform1")); + EXPECT_FLOAT_EQ(200.0f, GetUniformValueFloat(*appearance, "floatUniform2")); } } } diff --git a/tests/unittests/client/logic/api/CameraBindingTest.cpp b/tests/unittests/client/logic/api/CameraBindingTest.cpp new file mode 100644 index 000000000..f69f63125 --- /dev/null +++ b/tests/unittests/client/logic/api/CameraBindingTest.cpp @@ -0,0 +1,1551 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" + +#include "RamsesObjectResolverMock.h" +#include "RamsesTestUtils.h" +#include "SerializationTestUtils.h" +#include "WithTempDirectory.h" + +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesHelper.h" +#include "internal/logic/flatbuffers/generated/CameraBindingGen.h" + +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/CameraBinding.h" + +#include "ramses/client/ramses-utils.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/OrthographicCamera.h" + +namespace ramses::internal +{ + constexpr int32_t DefaultViewportOffsetX = 0; + constexpr int32_t DefaultViewportOffsetY = 0; + constexpr uint32_t DefaultViewportWidth = 16u; + constexpr uint32_t DefaultViewportHeight = 16u; + + constexpr float NearPlaneDefault = 0.1f; + constexpr float FarPlaneDefault = 1.0f; + + constexpr float PerspectiveFrustumFOVdefault = 168.579f; + constexpr float PerspectiveFrustumARdefault = 1.f; + + constexpr float OrthoFrustumLPdefault = -1.f; + constexpr float OrthoFrustumRPdefault = 1.f; + constexpr float OrthoFrustumBPdefault = -1.f; + constexpr float OrthoFrustumTPdefault = 1.0f; + + class ACameraBinding : public ::testing::Test + { + protected: + ACameraBinding() + : m_testScene(m_ramsesTestSetup.createScene(ramses::sceneId_t(1))) + { + } + + void withTempDirectory() + { + m_withTempDirectory = std::make_unique(); + } + + bool saveToFileWithoutValidation(std::string_view filename) + { + SaveFileConfig configNoValidation; + configNoValidation.setValidationEnabled(false); + return m_testScene->saveToFile(filename, configNoValidation); + } + + bool recreateFromFile(std::string_view filename) + { + const auto orthoId = m_orthoCam->getSceneObjectId(); + const auto perspectiveId = m_perspectiveCam->getSceneObjectId(); + const auto logicEngineId = m_logicEngine->getSceneObjectId(); + + m_ramsesTestSetup.destroyScene(*m_testScene); + m_testScene = &m_ramsesTestSetup.loadSceneFromFile(filename); + + m_orthoCam = m_testScene->findObject(orthoId); + m_perspectiveCam = m_testScene->findObject(perspectiveId); + m_logicEngine = m_testScene->findObject(logicEngineId); + return m_logicEngine != nullptr; + } + + void expectError(std::string_view expectedMsg) + { + const auto issue = m_ramsesTestSetup.getFramework().getLastError(); + ASSERT_TRUE(issue.has_value()); + EXPECT_EQ(issue->message, expectedMsg); + } + + static void ExpectPropertyTypeAndChildCount(const Property* prop, EPropertyType type, uint32_t childCount) + { + ASSERT_NE(nullptr, prop); + EXPECT_EQ(type, prop->getType()); + EXPECT_EQ(childCount, prop->getChildCount()); + } + + static void ExpectDefaultViewportValues(const ramses::Camera& camera) + { + EXPECT_EQ(camera.getViewportX(), DefaultViewportOffsetX); + EXPECT_EQ(camera.getViewportY(), DefaultViewportOffsetY); + EXPECT_EQ(camera.getViewportWidth(), DefaultViewportWidth); + EXPECT_EQ(camera.getViewportHeight(), DefaultViewportHeight); + } + + static void ExpectDefaultPerspectiveCameraFrustumValues(const ramses::PerspectiveCamera& camera) + { + EXPECT_NEAR(camera.getVerticalFieldOfView(), PerspectiveFrustumFOVdefault, 0.001f); + EXPECT_EQ(camera.getAspectRatio(), PerspectiveFrustumARdefault); + EXPECT_EQ(camera.getNearPlane(), NearPlaneDefault); + EXPECT_EQ(camera.getFarPlane(), FarPlaneDefault); + } + + static void ExpectDefaultCameraFrustumPlanes(const ramses::Camera& camera) + { + EXPECT_EQ(camera.getLeftPlane(), OrthoFrustumLPdefault); + EXPECT_EQ(camera.getRightPlane(), OrthoFrustumRPdefault); + EXPECT_EQ(camera.getBottomPlane(), OrthoFrustumBPdefault); + EXPECT_EQ(camera.getTopPlane(), OrthoFrustumTPdefault); + EXPECT_EQ(camera.getNearPlane(), NearPlaneDefault); + EXPECT_EQ(camera.getFarPlane(), FarPlaneDefault); + } + + static void ExpectDefaultValues(const ramses::CameraBinding& cameraBinding) + { + ExpectDefaultViewportValues(cameraBinding.getRamsesCamera()); + if (cameraBinding.impl().hasFrustumPlanesProperties()) + { + ExpectDefaultCameraFrustumPlanes(cameraBinding.getRamsesCamera()); + } + else + { + ExpectDefaultPerspectiveCameraFrustumValues(*cameraBinding.getRamsesCamera().as()); + } + } + + static void ExpectInputPropertiesWithFrustumPlanes(const CameraBindingImpl& cameraBinding) + { + EXPECT_TRUE(cameraBinding.hasFrustumPlanesProperties()); + + const auto inputs = cameraBinding.getInputs(); + ASSERT_EQ(2u, inputs->getChildCount()); + + const auto vpProperties = inputs->getChild("viewport"); + const auto frustum = inputs->getChild("frustum"); + ASSERT_EQ(4u, vpProperties->getChildCount()); + ASSERT_EQ(6u, frustum->getChildCount()); + + const auto vpOffsetX = vpProperties->getChild("offsetX"); + const auto vpOffsety = vpProperties->getChild("offsetY"); + const auto vpWidth = vpProperties->getChild("width"); + const auto vpHeight = vpProperties->getChild("height"); + const auto nP = frustum->getChild("nearPlane"); + const auto fP = frustum->getChild("farPlane"); + const auto lp = frustum->getChild("leftPlane"); + const auto rP = frustum->getChild("rightPlane"); + const auto bP = frustum->getChild("bottomPlane"); + const auto tP = frustum->getChild("topPlane"); + + // Test that internal indices match properties resolved by name + EXPECT_EQ(vpProperties, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))); + EXPECT_EQ(frustum, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))); + + EXPECT_EQ(vpOffsetX, vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))); + EXPECT_EQ(vpOffsety, vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))); + EXPECT_EQ(vpWidth, vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))); + EXPECT_EQ(vpHeight, vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))); + EXPECT_EQ(nP, frustum->impl().getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::NearPlane))); + EXPECT_EQ(fP, frustum->impl().getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::FarPlane))); + EXPECT_EQ(lp, frustum->impl().getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::LeftPlane))); + EXPECT_EQ(rP, frustum->impl().getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::RightPlane))); + EXPECT_EQ(bP, frustum->impl().getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::BottomPlane))); + EXPECT_EQ(tP, frustum->impl().getChild(static_cast(ECameraFrustumPlanesPropertyStaticIndex::TopPlane))); + + ExpectPropertyTypeAndChildCount(inputs->getChild("viewport"), EPropertyType::Struct, 4); + ExpectPropertyTypeAndChildCount(inputs->getChild("frustum"), EPropertyType::Struct, 6); + + ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetX"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetY"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(vpProperties->getChild("width"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(vpProperties->getChild("height"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("nearPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("farPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("leftPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("rightPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("bottomPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("topPlane"), EPropertyType::Float, 0); + } + + static void ExpectInputPropertiesWithoutFrustumPlanes(const CameraBindingImpl& cameraBinding) + { + EXPECT_FALSE(cameraBinding.hasFrustumPlanesProperties()); + + const auto inputs = cameraBinding.getInputs(); + ASSERT_EQ(2u, inputs->getChildCount()); + + const auto vpProperties = inputs->getChild("viewport"); + const auto frustum = inputs->getChild("frustum"); + ASSERT_EQ(4u, vpProperties->getChildCount()); + ASSERT_EQ(4u, frustum->getChildCount()); + + const auto vpOffsetX = vpProperties->getChild("offsetX"); + const auto vpOffsety = vpProperties->getChild("offsetY"); + const auto vpWidth = vpProperties->getChild("width"); + const auto vpHeight = vpProperties->getChild("height"); + + const auto nP = frustum->getChild("nearPlane"); + const auto fP = frustum->getChild("farPlane"); + const auto fov = frustum->getChild("fieldOfView"); + const auto aR = frustum->getChild("aspectRatio"); + + // Test that internal indices match properties resolved by name + EXPECT_EQ(vpProperties, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Viewport))); + EXPECT_EQ(frustum, inputs->getChild(static_cast(ECameraPropertyStructStaticIndex::Frustum))); + + EXPECT_EQ(vpOffsetX, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))); + EXPECT_EQ(vpOffsety, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))); + EXPECT_EQ(vpWidth, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))); + EXPECT_EQ(vpHeight, vpProperties->getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))); + EXPECT_EQ(nP, frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::NearPlane))); + EXPECT_EQ(fP, frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FarPlane))); + EXPECT_EQ(fov, frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))); + EXPECT_EQ(aR, frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))); + + ExpectPropertyTypeAndChildCount(inputs->getChild("viewport"), EPropertyType::Struct, 4); + ExpectPropertyTypeAndChildCount(inputs->getChild("frustum"), EPropertyType::Struct, 4); + + ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetX"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(vpProperties->getChild("offsetY"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(vpProperties->getChild("width"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(vpProperties->getChild("height"), EPropertyType::Int32, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("nearPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("farPlane"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("fieldOfView"), EPropertyType::Float, 0); + ExpectPropertyTypeAndChildCount(frustum->getChild("aspectRatio"), EPropertyType::Float, 0); + } + + std::unique_ptr m_withTempDirectory; + RamsesTestSetup m_ramsesTestSetup; + ramses::Scene* m_testScene; + ramses::OrthographicCamera* m_orthoCam = { m_testScene->createOrthographicCamera() }; + ramses::PerspectiveCamera* m_perspectiveCam = { m_testScene->createPerspectiveCamera() }; + ramses::LogicEngine* m_logicEngine{ m_testScene->createLogicEngine() }; + }; + + TEST_F(ACameraBinding, HasANameAfterCreation) + { + auto& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, "CameraBinding"); + EXPECT_EQ("CameraBinding", cameraBinding.getName()); + } + + TEST_F(ACameraBinding, HasAIdAfterCreation) + { + auto& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, "CameraBinding"); + EXPECT_EQ(cameraBinding.getSceneObjectId().getValue(), 4u); + } + + TEST_F(ACameraBinding, HasNoOutputsAfterCreation) + { + auto& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + EXPECT_EQ(nullptr, cameraBinding.getOutputs()); + } + + TEST_F(ACameraBinding, ProducesNoErrorsDuringUpdate_IfNoRamsesCameraIsAssigned) + { + auto& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + EXPECT_EQ(std::nullopt, cameraBinding.impl().update()); + } + + TEST_F(ACameraBinding, ReturnsReferenceToRamsesCamera) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + EXPECT_EQ(m_perspectiveCam, &cameraBinding.getRamsesCamera()); + } + + TEST_F(ACameraBinding, HasInputsAfterInitializingWithPerspectiveCamera) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + ExpectInputPropertiesWithoutFrustumPlanes(cameraBinding.impl()); + } + + TEST_F(ACameraBinding, HasInputsAfterInitializingFromOrthoCamera) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + ExpectInputPropertiesWithFrustumPlanes(cameraBinding.impl()); + } + + TEST_F(ACameraBinding, HasInputsAfterInitializingFromOrthoCamera_createdWithFrustumPlanes) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBindingWithFrustumPlanes(*m_orthoCam); + ExpectInputPropertiesWithFrustumPlanes(cameraBinding.impl()); + } + + TEST_F(ACameraBinding, HasInputsAfterInitializingFromPerspectiveCamera_createdWithFrustumPlanes) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBindingWithFrustumPlanes(*m_perspectiveCam); + ExpectInputPropertiesWithFrustumPlanes(cameraBinding.impl()); + } + + TEST_F(ACameraBinding, DoesNotOverwriteDefaultValues_WhenCreatedFromOrthoCamera) + { + m_logicEngine->createCameraBinding(*m_orthoCam, ""); + m_logicEngine->update(); + + //Expect default values on the camera, because nothing was set so far + ExpectDefaultViewportValues(*m_orthoCam); + ExpectDefaultCameraFrustumPlanes(*m_orthoCam); + } + + TEST_F(ACameraBinding, DoesNotOverwriteDefaultValues_WhenCreatedFromPerspectiveCamera) + { + m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + m_logicEngine->update(); + + //Expect default values on the camera, because nothing was set so far + ExpectDefaultViewportValues(*m_perspectiveCam); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + } + + TEST_F(ACameraBinding, ReportsErrorOnUpdate_WhenSettingZeroToViewportSize) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + const auto inputs = cameraBinding.getInputs(); + auto vpProperties = inputs->getChild("viewport"); + // Setting illegal viewport values: width and height cannot be 0 so an error will be produced on ramses camera + vpProperties->getChild("width")->set(0); + + EXPECT_FALSE(m_logicEngine->update()); + expectError("Camera viewport size must be positive! (width: 0; height: 16)"); + + // Fix width, break height -> still generates error + vpProperties->getChild("width")->set(8); + vpProperties->getChild("height")->set(0); + + // Expect default values on the camera, because setting values failed + ExpectDefaultViewportValues(*m_orthoCam); + + EXPECT_FALSE(m_logicEngine->update()); + expectError("Camera viewport size must be positive! (width: 8; height: 0)"); + + // Fix height and update recovers the errors + vpProperties->getChild("height")->set(32); + EXPECT_TRUE(m_logicEngine->update()); + + EXPECT_EQ(m_orthoCam->getViewportWidth(), 8u); + EXPECT_EQ(m_orthoCam->getViewportHeight(), 32u); + } + + TEST_F(ACameraBinding, ReportsErrorOnUpdate_WhenSettingNegativeViewportSize) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + const auto inputs = cameraBinding.getInputs(); + auto vpProperties = inputs->getChild("viewport"); + // Setting illegal viewport values: width and height cannot be 0 so an error will be produced on ramses camera + vpProperties->getChild("width")->set(-1); + vpProperties->getChild("height")->set(-1); + + EXPECT_FALSE(m_logicEngine->update()); + expectError("Camera viewport size must be positive! (width: -1; height: -1)"); + + // Setting positive values recovers from the error + vpProperties->getChild("width")->set(10); + vpProperties->getChild("height")->set(12); + EXPECT_TRUE(m_logicEngine->update()); + + EXPECT_EQ(m_orthoCam->getViewportWidth(), 10u); + EXPECT_EQ(m_orthoCam->getViewportHeight(), 12u); + } + + TEST_F(ACameraBinding, ReportsErrorOnUpdate_WhenSettingInvalidFrustumValuesOnOrthoCamera) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + + auto frustum = cameraBinding.getInputs()->getChild("frustum"); + // Setting illegal frustum values: left plane cannot be smaller than right plane so an error will be produced on ramses camera + frustum->getChild("leftPlane")->set(2.f); + frustum->getChild("rightPlane")->set(1.f); + + EXPECT_FALSE(m_logicEngine->update()); + expectError("Camera::setFrustum failed - check validity of given frustum planes"); + + //Still expect default values on the camera, because setting values failed + ExpectDefaultCameraFrustumPlanes(*m_orthoCam); + + // Recovers from the error once values are ok + frustum->getChild("rightPlane")->set(3.f); + EXPECT_TRUE(m_logicEngine->update()); + } + + TEST_F(ACameraBinding, ReportsErrorOnUpdate_WhenSettingInvalidFrustumValuesOnPerspectiveCamera) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + auto frustum = cameraBinding.getInputs()->getChild("frustum"); + // Setting illegal frustum values: fov and aspect ratio cannot be 0 so an error will be produced on ramses camera + frustum->getChild("fieldOfView")->set(0.f); + frustum->getChild("aspectRatio")->set(0.f); + + EXPECT_FALSE(m_logicEngine->update()); + expectError("PerspectiveCamera::setFrustum failed - check validity of given frustum planes"); + + // Fixing just the FOV does not fix the issue, need to also fix aspect ratio + frustum->getChild("fieldOfView")->set(15.f); + EXPECT_FALSE(m_logicEngine->update()); + expectError("PerspectiveCamera::setFrustum failed - check validity of given frustum planes"); + + //Still expect default values on the camera, because setting values failed + ExpectDefaultViewportValues(*m_perspectiveCam); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + frustum->getChild("aspectRatio")->set(1.f); + EXPECT_TRUE(m_logicEngine->update()); + } + + TEST_F(ACameraBinding, InitializesInputPropertiesOfPerpespectiveCameraToMatchRamsesDefaultValues) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + const auto inputs = cameraBinding.getInputs(); + ASSERT_NE(nullptr, inputs); + + const auto vpProperties = inputs->getChild("viewport"); + const auto frustum = inputs->getChild("frustum"); + ASSERT_EQ(4u, vpProperties->getChildCount()); + ASSERT_EQ(4u, frustum->getChildCount()); + + EXPECT_EQ(*vpProperties->getChild("offsetX")->get(), m_perspectiveCam->getViewportX()); + EXPECT_EQ(*vpProperties->getChild("offsetY")->get(), m_perspectiveCam->getViewportY()); + EXPECT_EQ(static_cast(*vpProperties->getChild("width")->get()), m_perspectiveCam->getViewportWidth()); + EXPECT_EQ(static_cast(*vpProperties->getChild("height")->get()), m_perspectiveCam->getViewportHeight()); + + EXPECT_EQ(*frustum->getChild("nearPlane")->get(), m_perspectiveCam->getNearPlane()); + EXPECT_EQ(*frustum->getChild("farPlane")->get(), m_perspectiveCam->getFarPlane()); + EXPECT_NEAR(*frustum->getChild("fieldOfView")->get(), m_perspectiveCam->getVerticalFieldOfView(), 0.001f); + EXPECT_EQ(*frustum->getChild("aspectRatio")->get(), m_perspectiveCam->getAspectRatio()); + } + + TEST_F(ACameraBinding, InitializesInputPropertiesOfOrthographicCameraToMatchRamsesDefaultValues) + { + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_orthoCam, ""); + + const auto inputs = cameraBinding.getInputs(); + ASSERT_NE(nullptr, inputs); + + const auto vpProperties = inputs->getChild("viewport"); + const auto frustum = inputs->getChild("frustum"); + ASSERT_EQ(4u, vpProperties->getChildCount()); + ASSERT_EQ(6u, frustum->getChildCount()); + + EXPECT_EQ(*vpProperties->getChild("offsetX")->get(), m_orthoCam->getViewportX()); + EXPECT_EQ(*vpProperties->getChild("offsetY")->get(), m_orthoCam->getViewportY()); + EXPECT_EQ(static_cast(*vpProperties->getChild("width")->get()), m_orthoCam->getViewportWidth()); + EXPECT_EQ(static_cast(*vpProperties->getChild("height")->get()), m_orthoCam->getViewportHeight()); + + EXPECT_EQ(*frustum->getChild("nearPlane")->get(), m_orthoCam->getNearPlane()); + EXPECT_EQ(*frustum->getChild("farPlane")->get(), m_orthoCam->getFarPlane()); + EXPECT_EQ(*frustum->getChild("leftPlane")->get(), m_orthoCam->getLeftPlane()); + EXPECT_EQ(*frustum->getChild("rightPlane")->get(), m_orthoCam->getRightPlane()); + EXPECT_EQ(*frustum->getChild("bottomPlane")->get(), m_orthoCam->getBottomPlane()); + EXPECT_EQ(*frustum->getChild("topPlane")->get(), m_orthoCam->getTopPlane()); + } + + TEST_F(ACameraBinding, MarksInputsAsBindingInputsForPerspectiveCameraBinding) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + const auto inputs = cameraBinding->getInputs(); + for (size_t i = 0; i < inputs->getChildCount(); ++i) + { + const auto inputStruct = inputs->getChild(i); + EXPECT_EQ(EPropertySemantics::BindingInput, inputStruct->impl().getPropertySemantics()); + + for (size_t j = 0; j < inputs->getChild(i)->getChildCount(); ++j) + { + const auto inputProperty = inputStruct->impl().getChild(j); + EXPECT_EQ(EPropertySemantics::BindingInput, inputProperty->impl().getPropertySemantics()); + } + } + } + + TEST_F(ACameraBinding, MarksInputsAsBindingInputsForOrthoCameraBinding) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_orthoCam, ""); + + const auto inputs = cameraBinding->getInputs(); + for (size_t i = 0; i < inputs->getChildCount(); ++i) + { + const auto inputStruct = inputs->getChild(i); + EXPECT_EQ(EPropertySemantics::BindingInput, inputStruct->impl().getPropertySemantics()); + + for (size_t j = 0; j < inputs->getChild(i)->getChildCount(); ++j) + { + const auto inputProperty = inputStruct->impl().getChild(j); + EXPECT_EQ(EPropertySemantics::BindingInput, inputProperty->impl().getPropertySemantics()); + } + } + } + + TEST_F(ACameraBinding, ReturnsBoundRamsesCamera) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + EXPECT_EQ(m_perspectiveCam, &cameraBinding->getRamsesCamera()); + } + + TEST_F(ACameraBinding, DoesNotModifyRamsesWithoutUpdateBeingCalledWithPerspectiveCamera) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + auto inputs = cameraBinding->getInputs(); + auto vpProperties = inputs->getChild("viewport"); + auto frustum = inputs->getChild("frustum"); + + vpProperties->getChild("offsetX")->set(4); + vpProperties->getChild("offsetY")->set(7); + vpProperties->getChild("width")->set(11); + vpProperties->getChild("height")->set(19); + + frustum->getChild("nearPlane")->set(3.1f); + frustum->getChild("farPlane")->set(.2f); + frustum->getChild("fieldOfView")->set(4.2f); + frustum->getChild("aspectRatio")->set(2.1f); + + ExpectDefaultValues(*cameraBinding); + } + + TEST_F(ACameraBinding, DoesNotModifyRamsesWithoutUpdateBeingCalledWithOrthoCamera) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_orthoCam, ""); + + auto inputs = cameraBinding->getInputs(); + auto vpProperties = inputs->getChild("viewport"); + auto frustum = inputs->getChild("frustum"); + + vpProperties->getChild("offsetX")->set(4); + vpProperties->getChild("offsetY")->set(7); + vpProperties->getChild("width")->set(11); + vpProperties->getChild("height")->set(19); + + frustum->getChild("nearPlane")->set(3.1f); + frustum->getChild("farPlane")->set(.2f); + frustum->getChild("leftPlane")->set(6.2f); + frustum->getChild("rightPlane")->set(2.8f); + frustum->getChild("bottomPlane")->set(1.9f); + frustum->getChild("topPlane")->set(7.1f); + + ExpectDefaultValues(*cameraBinding); + } + + TEST_F(ACameraBinding, ModifiesRamsesPerspectiveCamOnUpdate_OnlyAfterExplicitlyAssignedToInputs) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + auto inputs = cameraBinding->getInputs(); + auto vpProperties = inputs->getChild("viewport"); + auto frustum = inputs->getChild("frustum"); + + const int32_t newVpOffsetX = 23; + vpProperties->getChild("offsetX")->set(newVpOffsetX); + + // Update not called yet -> still default values + ExpectDefaultValues(*cameraBinding); + + cameraBinding->impl().update(); + // Only propagated vpOffsetX, the others have default values + EXPECT_EQ(m_perspectiveCam->getViewportX(), newVpOffsetX); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 0); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 16u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 16u); + + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + // Set and test all properties + const int32_t newVpOffsetY = 13; + const int32_t newVpWidth = 56; + const int32_t newVpHeight = 45; + + const float newFov = 30.f; + const float newAR = 640.f / 480.f; + const float newNearPlane = 4.4f; + const float newFarPlane = 5.1f; + + vpProperties->getChild("offsetY")->set(newVpOffsetY); + vpProperties->getChild("width")->set(newVpWidth); + vpProperties->getChild("height")->set(newVpHeight); + + frustum->getChild("fieldOfView")->set(newFov); + frustum->getChild("aspectRatio")->set(newAR); + frustum->getChild("nearPlane")->set(newNearPlane); + frustum->getChild("farPlane")->set(newFarPlane); + cameraBinding->impl().update(); + + EXPECT_EQ(m_perspectiveCam->getViewportX(), newVpOffsetX); + EXPECT_EQ(m_perspectiveCam->getViewportY(), newVpOffsetY); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), static_cast(newVpWidth)); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), static_cast(newVpHeight)); + + EXPECT_NEAR(m_perspectiveCam->getVerticalFieldOfView(), newFov, 0.001f); + EXPECT_EQ(m_perspectiveCam->getAspectRatio(), newAR); + EXPECT_EQ(m_perspectiveCam->getNearPlane(), newNearPlane); + EXPECT_EQ(m_perspectiveCam->getFarPlane(), newFarPlane); + } + + TEST_F(ACameraBinding, ModifiesRamsesOrthoCamOnUpdate_OnlyAfterExplicitlyAssignedToInputs) + { + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_orthoCam, ""); + + auto inputs = cameraBinding->getInputs(); + auto vpProperties = inputs->getChild("viewport"); + auto frustum = inputs->getChild("frustum"); + + const int32_t newVpOffsetX = 23; + vpProperties->getChild("offsetX")->set(newVpOffsetX); + + // Update not called yet -> still default values + ExpectDefaultValues(*cameraBinding); + + cameraBinding->impl().update(); + // Only propagated vpOffsetX, the others have default values + EXPECT_EQ(m_orthoCam->getViewportX(), newVpOffsetX); + EXPECT_EQ(m_orthoCam->getViewportY(), 0); + EXPECT_EQ(m_orthoCam->getViewportWidth(), 16u); + EXPECT_EQ(m_orthoCam->getViewportHeight(), 16u); + + ExpectDefaultCameraFrustumPlanes(*m_orthoCam); + + // Set and test all properties + const int32_t newVpOffsetY = 13; + const int32_t newVpWidth = 56; + const int32_t newVpHeight = 45; + + const float newLeftPlane = 0.2f; + const float newRightPlane = 0.3f; + const float newBottomPlane = 0.4f; + const float newTopPlane = 0.5f; + const float newNearPlane = 4.f; + const float newFarPlane = 5.1f; + + vpProperties->getChild("offsetY")->set(newVpOffsetY); + vpProperties->getChild("width")->set(newVpWidth); + vpProperties->getChild("height")->set(newVpHeight); + + frustum->getChild("leftPlane")->set(newLeftPlane); + frustum->getChild("rightPlane")->set(newRightPlane); + frustum->getChild("bottomPlane")->set(newBottomPlane); + frustum->getChild("topPlane")->set(newTopPlane); + frustum->getChild("nearPlane")->set(newNearPlane); + frustum->getChild("farPlane")->set(newFarPlane); + cameraBinding->impl().update(); + + EXPECT_EQ(m_orthoCam->getViewportX(), newVpOffsetX); + EXPECT_EQ(m_orthoCam->getViewportY(), newVpOffsetY); + EXPECT_EQ(m_orthoCam->getViewportWidth(), static_cast(newVpWidth)); + EXPECT_EQ(m_orthoCam->getViewportHeight(), static_cast(newVpHeight)); + + EXPECT_EQ(m_orthoCam->getLeftPlane(), newLeftPlane); + EXPECT_EQ(m_orthoCam->getRightPlane(), newRightPlane); + EXPECT_EQ(m_orthoCam->getBottomPlane(), newBottomPlane); + EXPECT_EQ(m_orthoCam->getTopPlane(), newTopPlane); + EXPECT_EQ(m_orthoCam->getNearPlane(), newNearPlane); + EXPECT_EQ(m_orthoCam->getFarPlane(), newFarPlane); + } + + TEST_F(ACameraBinding, PropagatesItsInputsToRamsesPerspectiveCameraOnUpdate_WithLinksInsteadOfSetCall) + { + const std::string_view scriptSrc = R"( + function interface(IN,OUT) + OUT.vpProps = { + vpX = Type:Int32(), + vpY = Type:Int32(), + vpW = Type:Int32(), + vpH = Type:Int32() + } + end + function run(IN,OUT) + OUT.vpProps = { + vpX = 5, + vpY = 10, + vpW = 35, + vpH = 19 + } + end + )"; + + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vpProps")->getChild("vpX"), *cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vpProps")->getChild("vpY"), *cameraBinding->getInputs()->getChild("viewport")->getChild("offsetY"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vpProps")->getChild("vpW"), *cameraBinding->getInputs()->getChild("viewport")->getChild("width"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vpProps")->getChild("vpH"), *cameraBinding->getInputs()->getChild("viewport")->getChild("height"))); + + // Links have no effect before update() explicitly called + ExpectDefaultValues(*cameraBinding); + + m_logicEngine->update(); + + // Linked values got updates, not-linked values were not modified + EXPECT_EQ(m_perspectiveCam->getViewportX(), 5); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 10); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 35u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 19u); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + } + + TEST_F(ACameraBinding, PropagatesItsInputsToRamsesOrthoCameraOnUpdate_WithLinksInsteadOfSetCall) + { + const std::string_view scriptSrc = R"( + function interface(IN,OUT) + OUT.frustProps = { + lP = Type:Float(), + rP = Type:Float(), + bP = Type:Float(), + tP = Type:Float(), + nP = Type:Float(), + fP = Type:Float() + } + end + function run(IN,OUT) + OUT.frustProps = { + lP = 0.2, + rP = 0.3, + bP = 0.4, + tP = 0.5, + nP = 0.6, + fP = 0.7 + } + end + )"; + + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + + auto* cameraBinding = m_logicEngine->createCameraBinding(*m_orthoCam, ""); + + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("lP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("leftPlane"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("rP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("rightPlane"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("bP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("bottomPlane"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("tP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("topPlane"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("nP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("nearPlane"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("fP"), *cameraBinding->getInputs()->getChild("frustum")->getChild("farPlane"))); + + // Links have no effect before update() explicitly called + ExpectDefaultValues(*cameraBinding); + + m_logicEngine->update(); + + // Linked values got updates, not-linked values were not modified + EXPECT_EQ(m_orthoCam->getLeftPlane(), 0.2f); + EXPECT_EQ(m_orthoCam->getRightPlane(), 0.3f); + EXPECT_EQ(m_orthoCam->getBottomPlane(), 0.4f); + EXPECT_EQ(m_orthoCam->getTopPlane(), 0.5f); + EXPECT_EQ(m_orthoCam->getNearPlane(), 0.6f); + EXPECT_EQ(m_orthoCam->getFarPlane(), 0.7f); + ExpectDefaultViewportValues(*m_orthoCam); + } + + TEST_F(ACameraBinding, DoesNotOverrideExistingValuesAfterRamsesCameraIsAssignedToBinding) + { + m_perspectiveCam->setViewport(3, 4, 10u, 11u); + m_perspectiveCam->setFrustum(30.f, 640.f / 480.f, 2.3f, 5.6f); + + m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + + EXPECT_EQ(m_perspectiveCam->getViewportX(), 3); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 4); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 10u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 11u); + + EXPECT_NEAR(m_perspectiveCam->getVerticalFieldOfView(), 30.f, 0.001f); + EXPECT_EQ(m_perspectiveCam->getAspectRatio(), 640.f / 480.f); + EXPECT_EQ(m_perspectiveCam->getNearPlane(), 2.3f); + EXPECT_EQ(m_perspectiveCam->getFarPlane(), 5.6f); + } + + // This fixture only contains serialization unit tests, for higher order tests see `ACameraBinding_SerializationWithFile` + class ACameraBinding_SerializationLifecycle : public ACameraBinding + { + protected: + flatbuffers::Offset serializeRootInput(bool withFrustumPlanes, bool withError = false) + { + std::vector frustumPlanes = { + TypeData{ "nearPlane", EPropertyType::Float }, + TypeData{ "farPlane", EPropertyType::Float }, + }; + + if (withFrustumPlanes) + { + frustumPlanes.emplace_back("leftPlane", EPropertyType::Float); + frustumPlanes.emplace_back("rightPlane", EPropertyType::Float); + frustumPlanes.emplace_back("bottomPlane", EPropertyType::Float); + if (!withError) + frustumPlanes.emplace_back("topPlane", EPropertyType::Float); + } + else + { + frustumPlanes.emplace_back("fieldOfView", EPropertyType::Float); + frustumPlanes.emplace_back("aspectRatio", EPropertyType::Float); + } + + HierarchicalTypeData cameraBindingInputs( + TypeData{ "", EPropertyType::Struct }, + { + MakeStruct("viewport", + { + TypeData{"offsetX", EPropertyType::Int32}, + TypeData{"offsetY", EPropertyType::Int32}, + TypeData{"width", EPropertyType::Int32}, + TypeData{"height", EPropertyType::Int32} + } + ), + MakeStruct("frustum", frustumPlanes), + } + ); + + return PropertyImpl::Serialize(PropertyImpl{ cameraBindingInputs, EPropertySemantics::BindingInput }, m_flatBufferBuilder, m_serializationMap); + } + + flatbuffers::FlatBufferBuilder m_flatBufferBuilder; + SerializationTestUtils m_testUtils{ m_flatBufferBuilder }; + ::testing::StrictMock m_resolverMock; + ErrorReporting m_errorReporting; + SerializationMap m_serializationMap; + DeserializationMap m_deserializationMap{ m_testScene->impl() }; + }; + + // More unit tests with inputs/outputs declared in LogicNode (base class) serialization tests + TEST_F(ACameraBinding_SerializationLifecycle, RemembersBaseClassData) + { + // Serialize + { + CameraBindingImpl binding(m_testScene->impl(), *m_orthoCam, true, "name", sceneObjectId_t{ 1u }); + binding.createRootProperties(); + (void)CameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + } + + // Inspect flatbuffers data + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + + ASSERT_TRUE(serializedBinding.base()); + ASSERT_TRUE(serializedBinding.base()->base()); + ASSERT_TRUE(serializedBinding.base()->base()->name()); + EXPECT_EQ(serializedBinding.base()->base()->name()->string_view(), "name"); + EXPECT_EQ(serializedBinding.base()->base()->id(), 1u); + + ASSERT_TRUE(serializedBinding.base()->rootInput()); + EXPECT_EQ(serializedBinding.base()->rootInput()->rootType(), rlogic_serialization::EPropertyRootType::Struct); + ASSERT_TRUE(serializedBinding.base()->rootInput()->children()); + EXPECT_EQ(serializedBinding.base()->rootInput()->children()->size(), 2u); + + // Deserialize + { + EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_orthoCam->getSceneObjectId())).WillOnce(::testing::Return(m_orthoCam)); + std::unique_ptr deserializedBinding = CameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + + ASSERT_TRUE(deserializedBinding); + EXPECT_EQ(deserializedBinding->getName(), "name"); + EXPECT_EQ(deserializedBinding->getSceneObjectId().getValue(), 1u); + EXPECT_EQ(deserializedBinding->getInputs()->getType(), EPropertyType::Struct); + EXPECT_EQ(deserializedBinding->getInputs()->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(deserializedBinding->getInputs()->getName(), ""); + EXPECT_EQ(deserializedBinding->getInputs()->getChildCount(), 2u); + } + } + + TEST_F(ACameraBinding_SerializationLifecycle, RemembersRamsesCameraId) + { + // Serialize + { + CameraBindingImpl binding(m_testScene->impl(), *m_orthoCam, true, "name", sceneObjectId_t{ 1u }); + binding.createRootProperties(); + (void)CameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + } + + // Inspect flatbuffers data + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + + EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectId(), m_orthoCam->getSceneObjectId().getValue()); + EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectType(), static_cast(ramses::ERamsesObjectType::OrthographicCamera)); + + // Deserialize + { + EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_orthoCam->getSceneObjectId())).WillOnce(::testing::Return(m_orthoCam)); + std::unique_ptr deserializedBinding = CameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + + ASSERT_TRUE(deserializedBinding); + EXPECT_EQ(&deserializedBinding->getRamsesCamera(), m_orthoCam); + } + } + + TEST_F(ACameraBinding_SerializationLifecycle, SerializesInputProperties_withFrustumPlanes) + { + // Serialize + { + CameraBindingImpl binding(m_testScene->impl(), *m_orthoCam, true, "name", sceneObjectId_t{ 1u }); + binding.createRootProperties(); + (void)CameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + } + + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + + // Deserialize + { + EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_orthoCam->getSceneObjectId())).WillOnce(::testing::Return(m_orthoCam)); + std::unique_ptr deserializedBinding = CameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + ASSERT_TRUE(deserializedBinding); + + ExpectInputPropertiesWithFrustumPlanes(*deserializedBinding); + } + } + + TEST_F(ACameraBinding_SerializationLifecycle, SerializesInputProperties_withoutFrustumPlanes) + { + // Serialize + { + CameraBindingImpl binding(m_testScene->impl(), *m_perspectiveCam, false, "name", sceneObjectId_t{ 1u }); + binding.createRootProperties(); + (void)CameraBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + } + + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + + // Deserialize + { + EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), m_perspectiveCam->getSceneObjectId())).WillOnce(::testing::Return(m_perspectiveCam)); + std::unique_ptr deserializedBinding = CameraBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + ASSERT_TRUE(deserializedBinding); + + ExpectInputPropertiesWithoutFrustumPlanes(*deserializedBinding); + } + } + + TEST_F(ACameraBinding_SerializationLifecycle, ProducesErrorIfInputPropertiesInvalid) + { + { + auto ramsesRef = rlogic_serialization::CreateRamsesReference( + m_flatBufferBuilder, + 12u, + static_cast(ramses::ERamsesObjectType::OrthographicCamera) + ); + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + ramsesRef, + serializeRootInput(true, true) + ); + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of CameraBinding from serialized data: missing or invalid input properties!"); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) + { + { + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + 0 // no base binding info + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of CameraBinding from serialized data: missing base class info!"); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenNoBindingName) + { + { + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + 0, // no name! + 1u) + ); + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of CameraBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenNoBindingId) + { + { + auto base = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 0)); + auto binding = rlogic_serialization::CreateCameraBinding(m_flatBufferBuilder, base); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of CameraBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenNoRootInput) + { + { + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + 0 // no root input + ); + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of CameraBinding from serialized data: missing root input!"); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) + { + { + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + 0, + m_testUtils.serializeTestProperty("", rlogic_serialization::EPropertyRootType::Struct, false, true) // rootInput with errors + ); + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenBoundCameraCannotBeResolved) + { + const ramses::sceneObjectId_t mockObjectId{ 12 }; + { + auto ramsesRef = rlogic_serialization::CreateRamsesReference( + m_flatBufferBuilder, + mockObjectId.getValue() + ); + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + ramsesRef, + serializeRootInput(true) + ); + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(nullptr)); + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + } + + TEST_F(ACameraBinding_SerializationLifecycle, ErrorWhenSavedCameraTypeDoesNotMatchResolvedCameraType) + { + RamsesTestSetup ramses; + ramses::Scene* scene = ramses.createScene(); + auto* perspCamera = scene->createPerspectiveCamera(); + + const ramses::sceneObjectId_t mockObjectId{ 12 }; + { + auto ramsesRef = rlogic_serialization::CreateRamsesReference( + m_flatBufferBuilder, + mockObjectId.getValue(), + uint32_t(ramses::ERamsesObjectType::OrthographicCamera) // save ortho camera + ); + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + ramsesRef, + serializeRootInput(false) + ); + auto binding = rlogic_serialization::CreateCameraBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + // resolver returns perspective camera, but orthographic camera is expected -> error + EXPECT_CALL(m_resolverMock, findRamsesCameraInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(perspCamera)); + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = CameraBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of CameraBinding from serialized data: loaded type does not match referenced camera type!"); + } + + class ACameraBinding_SerializationWithFile : public ACameraBinding + { + public: + ACameraBinding_SerializationWithFile() + { + withTempDirectory(); + } + }; + + TEST_F(ACameraBinding_SerializationWithFile, ContainsItsDataAfterLoading) + { + const int32_t newVpOffsetX = 10; + const int32_t newVpOffsetY = 13; + const int32_t newVpWidth = 56; + const int32_t newVpHeight = 45; + + const float newFov = 30.f; + const float newAR = 640.f / 480.f; + const float newNearPlane = 4.4f; + const float newFarPlane = 5.1f; + { + auto& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, "CameraBinding"); + + auto inputs = cameraBinding.getInputs(); + auto vpProperties = inputs->getChild("viewport"); + auto frustum = inputs->getChild("frustum"); + + vpProperties->getChild("offsetX")->set(newVpOffsetX); + vpProperties->getChild("offsetY")->set(newVpOffsetY); + vpProperties->getChild("width")->set(newVpWidth); + vpProperties->getChild("height")->set(newVpHeight); + + frustum->getChild("fieldOfView")->set(newFov); + frustum->getChild("aspectRatio")->set(newAR); + frustum->getChild("nearPlane")->set(newNearPlane); + frustum->getChild("farPlane")->set(newFarPlane); + m_logicEngine->update(); + EXPECT_TRUE(saveToFileWithoutValidation("camerabinding.bin")); + } + { + EXPECT_TRUE(recreateFromFile("camerabinding.bin")); + const auto& loadedCameraBinding = *m_logicEngine->findObject("CameraBinding"); + EXPECT_EQ("CameraBinding", loadedCameraBinding.getName()); + EXPECT_EQ(loadedCameraBinding.getSceneObjectId().getValue(), 4u); + EXPECT_EQ(loadedCameraBinding.getRamsesCamera().getSceneObjectId(), m_perspectiveCam->getSceneObjectId()); + + const auto& inputs = loadedCameraBinding.getInputs(); + ASSERT_EQ(inputs->getChildCount(), 2u); + auto vpProperties = inputs->getChild("viewport"); + auto frustum = inputs->getChild("frustum"); + ASSERT_EQ(vpProperties->getChildCount(), 4u); + ASSERT_EQ(vpProperties->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + ASSERT_EQ(frustum->getChildCount(), 4u); + ASSERT_EQ(frustum->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + + EXPECT_EQ(*vpProperties->getChild("offsetX")->get(), newVpOffsetX); + EXPECT_EQ(vpProperties->getChild("offsetX")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(*vpProperties->getChild("offsetY")->get(), newVpOffsetY); + EXPECT_EQ(vpProperties->getChild("offsetY")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(*vpProperties->getChild("width")->get(), newVpWidth); + EXPECT_EQ(vpProperties->getChild("width")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(*vpProperties->getChild("height")->get(), newVpHeight); + EXPECT_EQ(vpProperties->getChild("height")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + + EXPECT_EQ(*frustum->getChild("nearPlane")->get(), newNearPlane); + EXPECT_EQ(frustum->getChild("nearPlane")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(*frustum->getChild("farPlane")->get(), newFarPlane); + EXPECT_EQ(frustum->getChild("farPlane")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_NEAR(*frustum->getChild("fieldOfView")->get(), newFov, 0.001f); + EXPECT_EQ(frustum->getChild("fieldOfView")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(*frustum->getChild("aspectRatio")->get(), newAR); + EXPECT_EQ(frustum->getChild("aspectRatio")->impl().getPropertySemantics(), EPropertySemantics::BindingInput); + + // Test that internal indices match properties resolved by name + EXPECT_EQ(vpProperties->getChild("offsetX"), vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetX))); + EXPECT_EQ(vpProperties->getChild("offsetY"), vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortOffsetY))); + EXPECT_EQ(vpProperties->getChild("width"), vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortWidth))); + EXPECT_EQ(vpProperties->getChild("height"), vpProperties->impl().getChild(static_cast(ECameraViewportPropertyStaticIndex::ViewPortHeight))); + + EXPECT_EQ(frustum->getChild("nearPlane"), frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::NearPlane))); + EXPECT_EQ(frustum->getChild("farPlane"), frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FarPlane))); + EXPECT_EQ(frustum->getChild("fieldOfView"), frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::FieldOfView))); + EXPECT_EQ(frustum->getChild("aspectRatio"), frustum->impl().getChild(static_cast(EPerspectiveCameraFrustumPropertyStaticIndex::AspectRatio))); + } + } + + TEST_F(ACameraBinding_SerializationWithFile, KeepsItsProperties_WhenNoRamsesLinksAndSceneProvided) + { + { + m_logicEngine->createCameraBinding(*m_orthoCam, "CameraBinding"); + ASSERT_TRUE(saveToFileWithoutValidation("camerabinding.bin")); + } + + { + ASSERT_TRUE(recreateFromFile("camerabinding.bin")); + auto loadedCameraBinding = m_logicEngine->findObject("CameraBinding"); + EXPECT_EQ(&loadedCameraBinding->getRamsesCamera(), m_orthoCam); + EXPECT_EQ(loadedCameraBinding->getInputs()->getChildCount(), 2u); + EXPECT_EQ(loadedCameraBinding->getOutputs(), nullptr); + EXPECT_EQ(loadedCameraBinding->getName(), "CameraBinding"); + } + } + + TEST_F(ACameraBinding_SerializationWithFile, RestoresLinkToRamsesCamera) + { + { + m_logicEngine->createCameraBinding(*m_perspectiveCam, "CameraBinding"); + EXPECT_TRUE(saveToFileWithoutValidation("camerabinding.bin")); + } + { + EXPECT_TRUE(recreateFromFile("camerabinding.bin")); + const auto& cameraBinding = *m_logicEngine->findObject("CameraBinding"); + EXPECT_EQ(&cameraBinding.getRamsesCamera(), m_perspectiveCam); + } + } + + TEST_F(ACameraBinding_SerializationWithFile, DoesNotModifyRamsesCameraProperties_WhenNoValuesWereExplicitlySetBeforeSaving) + { + { + m_logicEngine->createCameraBinding(*m_perspectiveCam, "CameraBinding"); + EXPECT_TRUE(saveToFileWithoutValidation("camerabinding.bin")); + } + { + EXPECT_TRUE(recreateFromFile("camerabinding.bin")); + EXPECT_TRUE(m_logicEngine->update()); + + ExpectDefaultValues(*m_logicEngine->findObject("CameraBinding")); + } + } + + // Tests that the camera properties don't overwrite ramses' values after loading from file, until + // set() is called again explicitly after loadFromFile() + TEST_F(ACameraBinding_SerializationWithFile, ReappliesViewportPropertiesToRamsesCamera_OnlyAfterExplicitlySetAgain) + { + { + auto& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, "CameraBinding"); + // Set some values to the binding's inputs. Those should be lost/discarded on save() because we dont' call update() below + auto vpProperties = cameraBinding.getInputs()->getChild("viewport"); + vpProperties->getChild("offsetX")->set(4); + vpProperties->getChild("offsetY")->set(5); + vpProperties->getChild("width")->set(6); + vpProperties->getChild("height")->set(7); + m_logicEngine->update(); + + m_perspectiveCam->setViewport(11, 12, 13u, 14u); + EXPECT_TRUE(saveToFileWithoutValidation("camerabinding.bin")); + } + + { + EXPECT_TRUE(recreateFromFile("camerabinding.bin")); + + // Artificially set to other values so that we can verify update() didn't change them + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + + EXPECT_TRUE(m_logicEngine->update()); + + // Camera binding does not re-apply its cached values to ramses camera viewport + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + + // Set only one value of viewport struct. Use the same value as the one in cache on purpose! + // Calling set forces set on ramses regardless of the value used + m_logicEngine->findObject("CameraBinding")->getInputs()->getChild("viewport")->getChild("offsetX")->set(11); + m_logicEngine->update(); + EXPECT_TRUE(m_logicEngine->update()); + + // vpOffsetX changed, the rest is taken from the initially saved inputs, not what was set on the camera! + EXPECT_EQ(m_perspectiveCam->getViewportX(), 11); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 12); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 13u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 14u); + } + } + + // This is sort of a confidence test, testing a combination of: + // - bindings only propagating their values to ramses camera if the value was set by an incoming link + // - saving and loading files + // The general expectation is that after loading + update(), the logic scene would overwrite only ramses + // properties wrapped by a LogicBinding which is linked to a script + TEST_F(ACameraBinding_SerializationWithFile, SetsOnlyRamsesCameraPropertiesForWhichTheBindingInputIsLinked_WhenCallingUpdateAfterLoading) + { + // These values should not be overwritten by logic on update() + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + + { + const std::string_view scriptSrc = R"( + function interface(IN,OUT) + OUT.frustProps = { + fov = Type:Float(), + aR = Type:Float(), + nP = Type:Float(), + fP = Type:Float() + } + end + function run(IN,OUT) + OUT.frustProps = { + fov = 30.0, + aR = 640.0 / 480.0, + nP = 2.3, + fP = 5.6 + } + end + )"; + + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, "CameraBinding"); + + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("fov"), *cameraBinding.getInputs()->getChild("frustum")->getChild("fieldOfView"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("aR"), *cameraBinding.getInputs()->getChild("frustum")->getChild("aspectRatio"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("nP"), *cameraBinding.getInputs()->getChild("frustum")->getChild("nearPlane"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("frustProps")->getChild("fP"), *cameraBinding.getInputs()->getChild("frustum")->getChild("farPlane"))); + + m_logicEngine->update(); + EXPECT_TRUE(saveToFileWithoutValidation("camerabinding.bin")); + } + + // Modify 'linked' properties before loading to check if logic will overwrite them after load + update + m_perspectiveCam->setFrustum(15.f, 320.f / 240.f, 4.1f, 7.9f); + + { + EXPECT_TRUE(recreateFromFile("camerabinding.bin")); + + EXPECT_TRUE(m_logicEngine->update()); + + // Viewport properties were not linked -> their values are not modified + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + // Frustum properties are linked -> values were updated + EXPECT_NEAR(m_perspectiveCam->getVerticalFieldOfView(), 30.f, 0.001f); + EXPECT_EQ(m_perspectiveCam->getAspectRatio(), 640.f / 480.f); + EXPECT_EQ(m_perspectiveCam->getNearPlane(), 2.3f); + EXPECT_EQ(m_perspectiveCam->getFarPlane(), 5.6f); + + // Manually setting values on ramses followed by a logic update has no effect + // Logic is not "dirty" and it doesn't know it needs to update ramses + m_perspectiveCam->setViewport(43, 34, 84u, 62u); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 43); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 34); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 84u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 62u); + } + } + + // Larger confidence tests which verify and document the entire data flow cycle of bindings + // There are smaller tests which test only properties and their data propagation rules (see property unit tests) + // There are also "dirtiness" tests which test when a camera is being re-updated (see logic engine dirtiness tests) + // These tests test everything in combination + + class ACameraBinding_DataFlow : public ACameraBinding + { + }; + + TEST_F(ACameraBinding_DataFlow, WithExplicitSet) + { + // Create camera and preset values + m_perspectiveCam->setViewport(11, 12, 13u, 14u); + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, ""); + // set other values to artificially check that the binding won't override them + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + + // Nothing happens here - binding does not overwrite ramses values because no user value set() was called and no link exists + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + + // Set only two view port properties + auto vpProperties = cameraBinding.getInputs()->getChild("viewport"); + vpProperties->getChild("offsetX")->set(4); + vpProperties->getChild("width")->set(21); + + // Update not called yet -> still has preset values for vpOffsetX and vpWidth in ramses camera, and default frustum values + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + // Update() triggers all viewport to be set on ramses to the two values that were explicitly set + // and the other two previous values of the binding input + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 4); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 12); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 21u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 14u); + // Frustum is not modified - only viewport was explicitly set + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + // Set two properties of each viewPort and frustum property struct + vpProperties->getChild("offsetY")->set(13); + vpProperties->getChild("height")->set(63); + auto frustum = cameraBinding.getInputs()->getChild("frustum"); + frustum->getChild("nearPlane")->set(2.3f); + frustum->getChild("farPlane")->set(5.6f); + + // On update all values of both structs are set + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 4); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 13); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 21u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 63u); + + EXPECT_NEAR(m_perspectiveCam->getVerticalFieldOfView(), PerspectiveFrustumFOVdefault, 0.001f); + EXPECT_EQ(m_perspectiveCam->getAspectRatio(), PerspectiveFrustumARdefault); + EXPECT_EQ(m_perspectiveCam->getNearPlane(), 2.3f); + EXPECT_EQ(m_perspectiveCam->getFarPlane(), 5.6f); + + // Calling update again does not "rewrite" the data to ramses. Check this by setting a value manually and call update() again + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + + // Set all properties manually this time + vpProperties->getChild("offsetX")->set(4); + vpProperties->getChild("offsetY")->set(5); + vpProperties->getChild("width")->set(6); + vpProperties->getChild("height")->set(7); + + frustum->getChild("fieldOfView")->set(30.f); + frustum->getChild("aspectRatio")->set(640.f / 480.f); + frustum->getChild("nearPlane")->set(1.3f); + frustum->getChild("farPlane")->set(7.6f); + m_logicEngine->update(); + + // All of the property values were passed to ramses + EXPECT_EQ(m_perspectiveCam->getViewportX(), 4); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 5); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 6u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 7u); + + EXPECT_NEAR(m_perspectiveCam->getVerticalFieldOfView(), 30.f, 0.001f); + EXPECT_EQ(m_perspectiveCam->getAspectRatio(), 640.f / 480.f); + EXPECT_EQ(m_perspectiveCam->getNearPlane(), 1.3f); + EXPECT_EQ(m_perspectiveCam->getFarPlane(), 7.6f); + } + + TEST_F(ACameraBinding_DataFlow, WithLinks) + { + const std::string_view scriptSrc = R"( + function interface(IN,OUT) + OUT.vpOffsetX = Type:Int32() + end + function run(IN,OUT) + OUT.vpOffsetX = 15 + end + )"; + + // Create camera and preset values + m_perspectiveCam->setViewport(11, 12, 13u, 14u); + + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + CameraBinding& cameraBinding = *m_logicEngine->createCameraBinding(*m_perspectiveCam, "CameraBinding"); + // set other values to artificially check that the binding won't override them + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + + // Adding and removing link does not set anything in ramses + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); + ASSERT_TRUE(m_logicEngine->unlink(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + // Create link and calling update -> sets values to ramses set by the link (vpOffsetX) + // and uses cached values in the binding for the other vp properties + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 15); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 12); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 13u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 14u); + // Does not touch the frustum because not linked or set at all + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + // Link does not overwrite manually set values as long as the actual value didnt change to avoid causing unnecessary sets on ramses + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + + // Remove link -> value is not overwritten any more + ASSERT_TRUE(m_logicEngine->unlink(*script->getOutputs()->getChild("vpOffsetX"), *cameraBinding.getInputs()->getChild("viewport")->getChild("offsetX"))); + m_perspectiveCam->setViewport(9, 8, 1u, 2u); + m_logicEngine->update(); + EXPECT_EQ(m_perspectiveCam->getViewportX(), 9); + EXPECT_EQ(m_perspectiveCam->getViewportY(), 8); + EXPECT_EQ(m_perspectiveCam->getViewportWidth(), 1u); + EXPECT_EQ(m_perspectiveCam->getViewportHeight(), 2u); + ExpectDefaultPerspectiveCameraFrustumValues(*m_perspectiveCam); + } +} diff --git a/client/logic/unittests/api/CollectionTest.cpp b/tests/unittests/client/logic/api/CollectionTest.cpp similarity index 98% rename from client/logic/unittests/api/CollectionTest.cpp rename to tests/unittests/client/logic/api/CollectionTest.cpp index d49a50e81..39b2df292 100644 --- a/client/logic/unittests/api/CollectionTest.cpp +++ b/tests/unittests/client/logic/api/CollectionTest.cpp @@ -9,9 +9,9 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "ramses-logic/Collection.h" +#include "ramses/client/logic/Collection.h" -namespace ramses +namespace ramses::internal { class TestCollectionItem { diff --git a/client/logic/unittests/api/DataArrayTest.cpp b/tests/unittests/client/logic/api/DataArrayTest.cpp similarity index 75% rename from client/logic/unittests/api/DataArrayTest.cpp rename to tests/unittests/client/logic/api/DataArrayTest.cpp index c13927d1b..4bcd91faf 100644 --- a/client/logic/unittests/api/DataArrayTest.cpp +++ b/tests/unittests/client/logic/api/DataArrayTest.cpp @@ -7,23 +7,23 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "WithTempDirectory.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/DataArray.h" -#include "impl/DataArrayImpl.h" -#include "internals/ErrorReporting.h" -#include "generated/DataArrayGen.h" +#include "ClientTestUtils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/DataArray.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/flatbuffers/generated/DataArrayGen.h" +#include "LogicEngineTest_Base.h" #include namespace ramses::internal { template - class ADataArray : public ::testing::Test + class ADataArray : public ALogicEngine { - protected: - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; }; using DataTypes = ::testing::Types < @@ -39,25 +39,13 @@ namespace ramses::internal >; TYPED_TEST_SUITE(ADataArray, DataTypes); - template - std::vector SomeDataVector(); - template <> std::vector SomeDataVector() { return { 1.f, 2.f, 3.f }; } - template <> std::vector SomeDataVector() { return { {1.f, 2.f}, {3.f, 4.f} }; } - template <> std::vector SomeDataVector() { return { {1.f, 2.f, 3.f}, {3.f, 4.f, 5.f} }; } - template <> std::vector SomeDataVector() { return { {1.f, 2.f, 3.f, 4.f}, {3.f, 4.f, 5.f, 6.f} }; } - template <> std::vector SomeDataVector() { return { 1, 2, 3 }; } - template <> std::vector SomeDataVector() { return { {1, 2}, {3, 4} }; } - template <> std::vector SomeDataVector() { return { {1, 2, 3}, {3, 4, 5} }; } - template <> std::vector SomeDataVector() { return { {1, 2, 3, 4}, {3, 4, 5, 6} }; } - template <> std::vector> SomeDataVector() { return { {1.f, 2.f, 3.f, 4.f, 5.f}, {3.f, 4.f, 5.f, 6.f, 7.f} }; } - TYPED_TEST(ADataArray, IsCreated) { const auto data = SomeDataVector(); - const auto dataArray = this->m_logicEngine.createDataArray(data, "dataarray"); - EXPECT_TRUE(this->m_logicEngine.getErrors().empty()); + const auto dataArray = this->m_logicEngine->createDataArray(data, "dataarray"); + this->expectNoError(); ASSERT_NE(nullptr, dataArray); - EXPECT_EQ(dataArray, this->m_logicEngine.template findByName("dataarray")); + EXPECT_EQ(dataArray, this->m_logicEngine->template findObject("dataarray")); EXPECT_EQ("dataarray", dataArray->getName()); EXPECT_EQ(EPropertyType(PropertyTypeToEnum::TYPE), dataArray->getDataType()); @@ -68,45 +56,43 @@ namespace ramses::internal TYPED_TEST(ADataArray, FailsToCreateIfEmptyDataProvided) { - EXPECT_EQ(nullptr, this->m_logicEngine.createDataArray(std::vector{}, "dataarray")); - ASSERT_FALSE(this->m_logicEngine.getErrors().empty()); - EXPECT_EQ("Cannot create DataArray 'dataarray' with empty data.", this->m_logicEngine.getErrors().front().message); + EXPECT_EQ(nullptr, this->m_logicEngine->createDataArray(std::vector{}, "dataarray")); + EXPECT_EQ("Cannot create DataArray 'dataarray' with empty data.", this->getLastErrorMessage()); } TYPED_TEST(ADataArray, IsDestroyed) { const auto data = SomeDataVector(); - auto dataArray = this->m_logicEngine.createDataArray(data, "dataarray"); + auto dataArray = this->m_logicEngine->createDataArray(data, "dataarray"); - EXPECT_TRUE(this->m_logicEngine.destroy(*dataArray)); - EXPECT_TRUE(this->m_logicEngine.getErrors().empty()); - EXPECT_EQ(nullptr, this->m_logicEngine.template findByName("dataarray")); + EXPECT_TRUE(this->m_logicEngine->destroy(*dataArray)); + this->expectNoError(); + EXPECT_EQ(nullptr, this->m_logicEngine->template findObject("dataarray")); } TYPED_TEST(ADataArray, FailsToBeDestroyedIfFromOtherLogicInstance) { - LogicEngine otherEngine{ this->m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *this->m_scene->createLogicEngine(); auto dataArray = otherEngine.createDataArray(SomeDataVector(), "dataarray"); - EXPECT_FALSE(this->m_logicEngine.destroy(*dataArray)); - ASSERT_FALSE(this->m_logicEngine.getErrors().empty()); - EXPECT_EQ("Failed to destroy object 'dataarray [Id=1]', cannot find it in this LogicEngine instance.", this->m_logicEngine.getErrors().front().message); + EXPECT_FALSE(this->m_logicEngine->destroy(*dataArray)); + EXPECT_EQ("Failed to destroy object 'dataarray [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance.", this->getLastErrorMessage()); } TYPED_TEST(ADataArray, ChangesName) { - auto dataArray = this->m_logicEngine.createDataArray(SomeDataVector(), "dataarray"); + auto dataArray = this->m_logicEngine->createDataArray(SomeDataVector(), "dataarray"); dataArray->setName("da"); EXPECT_EQ("da", dataArray->getName()); - EXPECT_EQ(dataArray, this->m_logicEngine.template findByName("da")); - EXPECT_TRUE(this->m_logicEngine.getErrors().empty()); + EXPECT_EQ(dataArray, this->m_logicEngine->template findObject("da")); + this->expectNoError(); } TYPED_TEST(ADataArray, ReturnsNullIfWrongDataTypeQueried) { const auto data = SomeDataVector(); - auto dataArray = this->m_logicEngine.createDataArray(data, "dataarray"); + auto dataArray = this->m_logicEngine->createDataArray(data, "dataarray"); if (dataArray->getDataType() != EPropertyType::Vec3f) { EXPECT_EQ(nullptr, dataArray->template getData()); @@ -115,7 +101,7 @@ namespace ramses::internal TYPED_TEST(ADataArray, CanBeSerializedAndDeserialized) { - WithTempDirectory tempDir; + this->withTempDirectory(); const auto data1 = SomeDataVector(); const auto data2 = SomeDataVector(); @@ -124,7 +110,7 @@ namespace ramses::internal const auto data5 = SomeDataVector>(); { - LogicEngine otherEngine{ this->m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *this->m_logicEngine; otherEngine.createDataArray(data1, "dataarray1"); otherEngine.createDataArray(data2, "dataarray2"); @@ -132,18 +118,18 @@ namespace ramses::internal otherEngine.createDataArray(data4, "dataarray4"); otherEngine.createDataArray(data5, "dataarray5"); - ASSERT_TRUE(otherEngine.saveToFile("LogicEngine.bin")); + ASSERT_TRUE(this->saveToFileWithoutValidation("LogicEngine.bin")); } - ASSERT_TRUE(this->m_logicEngine.loadFromFile("LogicEngine.bin")); - EXPECT_TRUE(this->m_logicEngine.getErrors().empty()); + ASSERT_TRUE(this->recreateFromFile("LogicEngine.bin")); + this->expectNoError(); - EXPECT_EQ(5u, this->m_logicEngine.template getCollection().size()); - const auto dataArray1 = this->m_logicEngine.template findByName("dataarray1"); - const auto dataArray2 = this->m_logicEngine.template findByName("dataarray2"); - const auto dataArray3 = this->m_logicEngine.template findByName("dataarray3"); - const auto dataArray4 = this->m_logicEngine.template findByName("dataarray4"); - const auto dataArray5 = this->m_logicEngine.template findByName("dataarray5"); + EXPECT_EQ(5u, this->m_logicEngine->template getCollection().size()); + const auto dataArray1 = this->m_logicEngine->template findObject("dataarray1"); + const auto dataArray2 = this->m_logicEngine->template findObject("dataarray2"); + const auto dataArray3 = this->m_logicEngine->template findObject("dataarray3"); + const auto dataArray4 = this->m_logicEngine->template findObject("dataarray4"); + const auto dataArray5 = this->m_logicEngine->template findObject("dataarray5"); ASSERT_TRUE(dataArray1 && dataArray2 && dataArray3 && dataArray4 && dataArray5); EXPECT_EQ(data1.size(), dataArray1->getNumElements()); @@ -175,17 +161,15 @@ namespace ramses::internal { 1.f, 2.f, 3.f } }; - LogicEngine logicEngine{ ramses::EFeatureLevel_Latest }; + ALogicEngineBase testSetup; + LogicEngine& logicEngine = testSetup.getLogicEngine(); EXPECT_EQ(nullptr, logicEngine.createDataArray(dataVector, "dataarray")); - ASSERT_EQ(1u, logicEngine.getErrors().size()); - EXPECT_EQ(logicEngine.getErrors()[0].message, "Failed to create DataArray of float arrays: all arrays must be of same size."); + EXPECT_EQ(testSetup.getLastErrorMessage(), "Failed to create DataArray of float arrays: all arrays must be of same size."); } - class ADataArray_SerializationLifecycle : public ::testing::Test + class ADataArray_SerializationLifecycle : public ALogicEngine { protected: - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; - enum class ESerializationIssue { AllValid, @@ -262,7 +246,7 @@ namespace ramses::internal builder.Finish(dataArrayFB); const auto& serialized = *flatbuffers::GetRoot(builder.GetBufferPointer()); - return DataArrayImpl::Deserialize(serialized, m_errorReporting); + return DataArrayImpl::Deserialize(serialized, m_errorReporting, m_deserializationMap); } std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) @@ -295,46 +279,45 @@ namespace ramses::internal flatBufferBuilder.Finish(dataArrayFB); const auto& serialized = *flatbuffers::GetRoot(flatBufferBuilder.GetBufferPointer()); - return DataArrayImpl::Deserialize(serialized, m_errorReporting); + return DataArrayImpl::Deserialize(serialized, m_errorReporting, m_deserializationMap); } ErrorReporting m_errorReporting; + DeserializationMap m_deserializationMap{ m_scene->impl() }; }; TEST_F(ADataArray_SerializationLifecycle, ReportsNoDeserializationErrorsWhenAllDataCorrect) { EXPECT_TRUE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(this->m_errorReporting.getErrors().empty()); + EXPECT_FALSE(this->m_errorReporting.getError().has_value()); } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithoutName) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::NameMissing)); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithoutId) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::IdMissing)); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithoutData) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::NoData)); - EXPECT_FALSE(this->m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data type!", this->m_errorReporting.getErrors().front().message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data type!", this->m_errorReporting.getError()->message); } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithWrongDataType) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::WrongDataType)); - EXPECT_FALSE(this->m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data type!", this->m_errorReporting.getErrors().front().message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data type!", this->m_errorReporting.getError()->message); } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithCorruptedDataUnion) @@ -354,9 +337,9 @@ namespace ramses::internal for(const auto& [unionType, dataArrayType] : invalidDataUnionPairs) { EXPECT_FALSE(deserializeSerializedData(unionType, dataArrayType)); - EXPECT_FALSE(this->m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data type!", this->m_errorReporting.getErrors().front().message); - this->m_errorReporting.clear(); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data type!", this->m_errorReporting.getError()->message); + this->m_errorReporting.reset(); } } @@ -375,29 +358,30 @@ namespace ramses::internal for (const auto& [unionType, dataArrayType] : dataUnionPairs) { EXPECT_FALSE(deserializeSerializedData(unionType, dataArrayType, true)); - EXPECT_FALSE(this->m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data size!", this->m_errorReporting.getErrors().front().message); - this->m_errorReporting.clear(); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data size!", this->m_errorReporting.getError()->message); + this->m_errorReporting.reset(); } } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithCorruptDataType) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::CorruptArrayDataType)); - EXPECT_FALSE(this->m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unsupported or corrupt data type '128'!", this->m_errorReporting.getErrors().front().message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unsupported or corrupt data type '128'!", this->m_errorReporting.getError()->message); } TEST_F(ADataArray_SerializationLifecycle, ReportsErrorWhenDeserializedWithWrongDataSize) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ADataArray_SerializationLifecycle::ESerializationIssue::WrongDataSize)); - EXPECT_FALSE(this->m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data size!", this->m_errorReporting.getErrors().front().message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of DataArray from serialized data: unexpected data size!", this->m_errorReporting.getError()->message); } TEST(AnimationChannel, EqualityOperatorsTests) { - LogicEngine engine{ ramses::EFeatureLevel_Latest }; + ALogicEngineBase testSetup; + LogicEngine& engine = testSetup.getLogicEngine(); const auto dataVector1 = SomeDataVector(); const auto dataVector2 = SomeDataVector>(); diff --git a/client/logic/unittests/api/IteratorTest.cpp b/tests/unittests/client/logic/api/IteratorTest.cpp similarity index 98% rename from client/logic/unittests/api/IteratorTest.cpp rename to tests/unittests/client/logic/api/IteratorTest.cpp index aa8276e3d..f6c463925 100644 --- a/client/logic/unittests/api/IteratorTest.cpp +++ b/tests/unittests/client/logic/api/IteratorTest.cpp @@ -9,9 +9,9 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "ramses-logic/Iterator.h" +#include "ramses/client/logic/Iterator.h" -namespace ramses +namespace ramses::internal { class TestIteratorItem { diff --git a/client/logic/unittests/api/LogicEngineTest_Animations.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Animations.cpp similarity index 63% rename from client/logic/unittests/api/LogicEngineTest_Animations.cpp rename to tests/unittests/client/logic/api/LogicEngineTest_Animations.cpp index 99d697ce4..69a40c89f 100644 --- a/client/logic/unittests/api/LogicEngineTest_Animations.cpp +++ b/tests/unittests/client/logic/api/LogicEngineTest_Animations.cpp @@ -7,42 +7,43 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/Node.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/client/Node.h" +#include "LogicEngineTest_Base.h" #include namespace ramses::internal { - class ALogicEngine_Animations : public ::testing::Test + class ALogicEngine_Animations : public ALogicEngine { public: void SetUp() override { - const auto dataArray = m_logicEngine.createDataArray(std::vector{ 0.f, 1.f }, "dataarray"); - const AnimationChannel channel{ "channel", dataArray, dataArray, ramses::EInterpolationType::Linear }; + const auto dataArray = m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }, "dataarray"); + const AnimationChannel channel{ "channel", dataArray, dataArray, EInterpolationType::Linear }; AnimationNodeConfig config; config.addChannel(channel); - m_animation1 = m_logicEngine.createAnimationNode(config, "animNode1"); - m_animation2 = m_logicEngine.createAnimationNode(config, "animNode2"); - m_timer = m_logicEngine.createTimerNode(); + m_animation1 = m_logicEngine->createAnimationNode(config, "animNode1"); + m_animation2 = m_logicEngine->createAnimationNode(config, "animNode2"); + m_timer = m_logicEngine->createTimerNode(); } protected: void setTickerAndUpdate(int64_t tick) { m_timer->getInputs()->getChild("ticker_us")->set(tick); - m_logicEngine.update(); + m_logicEngine->update(); } void expectNodeValues(float expectedTranslate, float expectedRotate) @@ -60,15 +61,9 @@ namespace ramses::internal EXPECT_NEAR(expectedRotate, vals[0], maxError); EXPECT_NEAR(expectedRotate, vals[1], maxError); EXPECT_NEAR(expectedRotate, vals[2], maxError); - EXPECT_EQ(ramses::ERotationType::Euler_XYZ, m_node->getRotationType()); + EXPECT_EQ(ERotationType::Euler_XYZ, m_node->getRotationType()); } - ramses::RamsesFramework m_ramsesFramework{ ramses::RamsesFrameworkConfig{ramses::EFeatureLevel_Latest} }; - ramses::RamsesClient* m_ramsesClient{ m_ramsesFramework.createClient("client") }; - ramses::Scene* m_scene{ m_ramsesClient->createScene(ramses::sceneId_t{123u}) }; - ramses::Node* m_node{ m_scene->createNode() }; - - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; AnimationNode* m_animation1 = nullptr; AnimationNode* m_animation2 = nullptr; TimerNode* m_timer = nullptr; @@ -116,27 +111,27 @@ namespace ramses::internal end )"; - const auto script = m_logicEngine.createLuaScript(scriptSrc); - const auto scriptScalarToVec1 = m_logicEngine.createLuaScript(ScriptScalarToVecSrc); - const auto scriptScalarToVec2 = m_logicEngine.createLuaScript(ScriptScalarToVecSrc); + const auto script = m_logicEngine->createLuaScript(scriptSrc); + const auto scriptScalarToVec1 = m_logicEngine->createLuaScript(ScriptScalarToVecSrc); + const auto scriptScalarToVec2 = m_logicEngine->createLuaScript(ScriptScalarToVecSrc); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); script->getInputs()->getChild("anim1Duration")->set(*m_animation1->getOutputs()->getChild("duration")->get()); script->getInputs()->getChild("anim2Duration")->set(*m_animation2->getOutputs()->getChild("duration")->get()); // controlScript -> anim1/channel -> scalarToVec -> nodeRotation // -> anim2/channel -> scalarToVec -> nodeScaling - m_logicEngine.link(*script->getOutputs()->getChild("anim1Progress"), *m_animation1->getInputs()->getChild("progress")); - m_logicEngine.link(*script->getOutputs()->getChild("anim2Progress"), *m_animation2->getInputs()->getChild("progress")); + m_logicEngine->link(*script->getOutputs()->getChild("anim1Progress"), *m_animation1->getInputs()->getChild("progress")); + m_logicEngine->link(*script->getOutputs()->getChild("anim2Progress"), *m_animation2->getInputs()->getChild("progress")); // link anim outputs to node bindings via helper to convert scalar to vec3 - m_logicEngine.link(*m_animation1->getOutputs()->getChild("channel"), *scriptScalarToVec1->getInputs()->getChild("scalar")); - m_logicEngine.link(*m_animation2->getOutputs()->getChild("channel"), *scriptScalarToVec2->getInputs()->getChild("scalar")); - m_logicEngine.link(*scriptScalarToVec1->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("translation")); - m_logicEngine.link(*scriptScalarToVec2->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("rotation")); + m_logicEngine->link(*m_animation1->getOutputs()->getChild("channel"), *scriptScalarToVec1->getInputs()->getChild("scalar")); + m_logicEngine->link(*m_animation2->getOutputs()->getChild("channel"), *scriptScalarToVec2->getInputs()->getChild("scalar")); + m_logicEngine->link(*scriptScalarToVec1->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("translation")); + m_logicEngine->link(*scriptScalarToVec2->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("rotation")); // link timer - m_logicEngine.link(*m_timer->getOutputs()->getChild("ticker_us"), *script->getInputs()->getChild("ticker")); + m_logicEngine->link(*m_timer->getOutputs()->getChild("ticker_us"), *script->getInputs()->getChild("ticker")); setTickerAndUpdate(1); // don't use 0 because it triggers timer auto generated time expectNodeValues(0.f, 0.f); @@ -195,27 +190,27 @@ namespace ramses::internal end )"; - const auto script = m_logicEngine.createLuaScript(scriptSrc); - const auto scriptScalarToVec1 = m_logicEngine.createLuaScript(ScriptScalarToVecSrc); - const auto scriptScalarToVec2 = m_logicEngine.createLuaScript(ScriptScalarToVecSrc); + const auto script = m_logicEngine->createLuaScript(scriptSrc); + const auto scriptScalarToVec1 = m_logicEngine->createLuaScript(ScriptScalarToVecSrc); + const auto scriptScalarToVec2 = m_logicEngine->createLuaScript(ScriptScalarToVecSrc); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); script->getInputs()->getChild("anim1Duration")->set(*m_animation1->getOutputs()->getChild("duration")->get()); script->getInputs()->getChild("anim2Duration")->set(*m_animation2->getOutputs()->getChild("duration")->get()); // controlScript -> anim1/channel -> scalarToVec -> nodeRotation // -> anim2/channel -> scalarToVec -> nodeScaling - m_logicEngine.link(*script->getOutputs()->getChild("anim1Progress"), *m_animation1->getInputs()->getChild("progress")); - m_logicEngine.link(*script->getOutputs()->getChild("anim2Progress"), *m_animation2->getInputs()->getChild("progress")); + m_logicEngine->link(*script->getOutputs()->getChild("anim1Progress"), *m_animation1->getInputs()->getChild("progress")); + m_logicEngine->link(*script->getOutputs()->getChild("anim2Progress"), *m_animation2->getInputs()->getChild("progress")); // link anim outputs to node bindings via helper to convert scalar to vec3 - m_logicEngine.link(*m_animation1->getOutputs()->getChild("channel"), *scriptScalarToVec1->getInputs()->getChild("scalar")); - m_logicEngine.link(*m_animation2->getOutputs()->getChild("channel"), *scriptScalarToVec2->getInputs()->getChild("scalar")); - m_logicEngine.link(*scriptScalarToVec1->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("translation")); - m_logicEngine.link(*scriptScalarToVec2->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("rotation")); + m_logicEngine->link(*m_animation1->getOutputs()->getChild("channel"), *scriptScalarToVec1->getInputs()->getChild("scalar")); + m_logicEngine->link(*m_animation2->getOutputs()->getChild("channel"), *scriptScalarToVec2->getInputs()->getChild("scalar")); + m_logicEngine->link(*scriptScalarToVec1->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("translation")); + m_logicEngine->link(*scriptScalarToVec2->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("rotation")); // link timer - m_logicEngine.link(*m_timer->getOutputs()->getChild("ticker_us"), *script->getInputs()->getChild("ticker")); + m_logicEngine->link(*m_timer->getOutputs()->getChild("ticker_us"), *script->getInputs()->getChild("ticker")); setTickerAndUpdate(0); expectNodeValues(0.f, 0.f); @@ -225,7 +220,7 @@ namespace ramses::internal for (int i = 0; i < 100; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds{ 20 }); - m_logicEngine.update(); + m_logicEngine->update(); vec3f vals; m_node->getTranslation(vals); @@ -271,21 +266,21 @@ namespace ramses::internal end )"; - const auto script = m_logicEngine.createLuaScript(scriptSrc); - const auto scriptScalarToVec = m_logicEngine.createLuaScript(ScriptScalarToVecSrc); + const auto script = m_logicEngine->createLuaScript(scriptSrc); + const auto scriptScalarToVec = m_logicEngine->createLuaScript(ScriptScalarToVecSrc); - const auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node); + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); // weak link duration - weak because it goes against the data flow direction - m_logicEngine.linkWeak(*m_animation1->getOutputs()->getChild("duration"), *script->getInputs()->getChild("animDuration")); + m_logicEngine->linkWeak(*m_animation1->getOutputs()->getChild("duration"), *script->getInputs()->getChild("animDuration")); // link animation to control script and to destination node property - m_logicEngine.link(*script->getOutputs()->getChild("animProgress"), *m_animation1->getInputs()->getChild("progress")); - m_logicEngine.link(*m_animation1->getOutputs()->getChild("channel"), *scriptScalarToVec->getInputs()->getChild("scalar")); - m_logicEngine.link(*scriptScalarToVec->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("translation")); + m_logicEngine->link(*script->getOutputs()->getChild("animProgress"), *m_animation1->getInputs()->getChild("progress")); + m_logicEngine->link(*m_animation1->getOutputs()->getChild("channel"), *scriptScalarToVec->getInputs()->getChild("scalar")); + m_logicEngine->link(*scriptScalarToVec->getOutputs()->getChild("vec"), *nodeBinding->getInputs()->getChild("translation")); // link timer - m_logicEngine.link(*m_timer->getOutputs()->getChild("ticker_us"), *script->getInputs()->getChild("ticker")); + m_logicEngine->link(*m_timer->getOutputs()->getChild("ticker_us"), *script->getInputs()->getChild("ticker")); // weak link limitation is undefined value during 1st update, we need to add extra update before expecting valid output setTickerAndUpdate(1); // don't use 0 because it triggers timer auto generated time diff --git a/tests/unittests/client/logic/api/LogicEngineTest_Compatibility.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Compatibility.cpp new file mode 100644 index 000000000..176407830 --- /dev/null +++ b/tests/unittests/client/logic/api/LogicEngineTest_Compatibility.cpp @@ -0,0 +1,252 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" + +#include "RamsesTestUtils.h" +#include "WithTempDirectory.h" +#include "PropertyLinkTestUtils.h" + +#include "ramses/client/logic/Property.h" + +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/RamsesVersion.h" + +#include "internal/logic/ApiObjects.h" +#include "internal/logic/FileUtils.h" + +#include "internal/logic/flatbuffers/generated/LogicEngineGen.h" +#include "fmt/format.h" + +#include + +namespace ramses::internal +{ + // These tests will always break on incompatible file format changes. + class ALogicEngine_Binary_Compatibility : public ::testing::Test + { + protected: + static void checkBaseContents(LogicEngine& logicEngine, ramses::Scene& ramsesScene) + { + ASSERT_NE(nullptr, logicEngine.findObject("nestedModuleMath")); + ASSERT_NE(nullptr, logicEngine.findObject("moduleMath")); + ASSERT_NE(nullptr, logicEngine.findObject("moduleTypes")); + const auto script1 = logicEngine.findObject("script1"); + ASSERT_NE(nullptr, script1); + EXPECT_NE(nullptr, script1->getInputs()->getChild("intInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("int64Input")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("vec2iInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("vec3iInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("vec4iInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("floatInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("vec2fInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("vec3fInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("vec4fInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("boolInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("stringInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("structInput")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("arrayInput")); + EXPECT_NE(nullptr, script1->getOutputs()->getChild("floatOutput")); + EXPECT_NE(nullptr, script1->getOutputs()->getChild("nodeTranslation")); + EXPECT_NE(nullptr, script1->getInputs()->getChild("floatInput")); + const auto script2 = logicEngine.findObject("script2"); + ASSERT_NE(nullptr, script2); + EXPECT_NE(nullptr, script2->getInputs()->getChild("floatInput")); + EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("offsetX")); + EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("offsetY")); + EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("width")); + EXPECT_NE(nullptr, script2->getOutputs()->getChild("cameraViewport")->getChild("height")); + EXPECT_NE(nullptr, script2->getOutputs()->getChild("floatUniform")); + const auto animNode = logicEngine.findObject("animNode"); + ASSERT_NE(nullptr, animNode); + EXPECT_EQ(1u, animNode->getInputs()->getChildCount()); + ASSERT_EQ(2u, animNode->getOutputs()->getChildCount()); + EXPECT_NE(nullptr, animNode->getOutputs()->getChild("channel")); + ASSERT_NE(nullptr, logicEngine.findObject("animNodeWithDataProperties")); + EXPECT_EQ(2u, logicEngine.findObject("animNodeWithDataProperties")->getInputs()->getChildCount()); + EXPECT_NE(nullptr, logicEngine.findObject("timerNode")); + + const auto nodeBinding = logicEngine.findObject("nodebinding"); + ASSERT_NE(nullptr, nodeBinding); + EXPECT_NE(nullptr, nodeBinding->getInputs()->getChild("enabled")); + EXPECT_TRUE(logicEngine.isLinked(*nodeBinding)); + const auto cameraBinding = logicEngine.findObject("camerabinding"); + ASSERT_NE(nullptr, cameraBinding); + const auto appearanceBinding = logicEngine.findObject("appearancebinding"); + ASSERT_NE(nullptr, appearanceBinding); + EXPECT_NE(nullptr, logicEngine.findObject("dataarray")); + + std::vector expectedLinks; + + const auto intf = logicEngine.findObject("intf"); + ASSERT_NE(nullptr, intf); + intf->getInputs()->getChild("struct")->getChild("floatInput")->set(42.5f); + expectedLinks.push_back({ intf->getOutputs()->getChild("struct")->getChild("floatInput"), script1->getInputs()->getChild("floatInput"), false }); + expectedLinks.push_back({ script1->getOutputs()->getChild("boolOutput"), nodeBinding->getInputs()->getChild("enabled"), false }); + expectedLinks.push_back({ script1->getOutputs()->getChild("floatOutput"), script2->getInputs()->getChild("floatInput"), false }); + expectedLinks.push_back({ script1->getOutputs()->getChild("nodeTranslation"), nodeBinding->getInputs()->getChild("translation"), false }); + expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("offsetX"), cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"), false }); + expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("offsetY"), cameraBinding->getInputs()->getChild("viewport")->getChild("offsetY"), false }); + expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("width"), cameraBinding->getInputs()->getChild("viewport")->getChild("width"), false }); + expectedLinks.push_back({ script2->getOutputs()->getChild("cameraViewport")->getChild("height"), cameraBinding->getInputs()->getChild("viewport")->getChild("height"), false }); + expectedLinks.push_back({ script2->getOutputs()->getChild("floatUniform"), appearanceBinding->getInputs()->getChild("floatUniform"), false }); + expectedLinks.push_back({ animNode->getOutputs()->getChild("channel"), appearanceBinding->getInputs()->getChild("animatedFloatUniform"), false }); + + const auto triLogicIntf = logicEngine.findObject("Interface_CameraCrane"); + ASSERT_TRUE(triLogicIntf); + const auto triLogicScript = logicEngine.findObject("CameraCrane"); + ASSERT_TRUE(triLogicScript); + const auto triCamNode = logicEngine.findObject("triangleCamNodeBinding"); + ASSERT_TRUE(triCamNode); + const auto triCamBinding = logicEngine.findObject("triangleCamBinding"); + ASSERT_TRUE(triCamBinding); + expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("CraneGimbal")->getChild("Yaw"), triLogicScript->getInputs()->getChild("yaw"), false }); + expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("CraneGimbal")->getChild("Pitch"), triLogicScript->getInputs()->getChild("pitch"), false }); + expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("Viewport")->getChild("Width"), triCamBinding->getInputs()->getChild("viewport")->getChild("width"), false }); + expectedLinks.push_back({ triLogicIntf->getOutputs()->getChild("Viewport")->getChild("Height"), triCamBinding->getInputs()->getChild("viewport")->getChild("height"), false }); + expectedLinks.push_back({ triLogicScript->getOutputs()->getChild("translation"), triCamNode->getInputs()->getChild("translation"), false }); + + PropertyLinkTestUtils::ExpectLinks(logicEngine, expectedLinks); + + EXPECT_TRUE(logicEngine.update()); + + // Values on Ramses are updated according to expectations + vec3f translation; + auto node = ramsesScene.findObject("test node"); + auto camera = ramsesScene.findObject("test camera"); + node->getTranslation(translation); + EXPECT_EQ(translation, vec3f(42.5f, 2.f, 3.f)); + // test that linked value from script propagated to ramses scene + EXPECT_EQ(ramses::EVisibilityMode::Off, node->getVisibility()); + + EXPECT_EQ(camera->getViewportX(), 45); + EXPECT_EQ(camera->getViewportY(), 47); + EXPECT_EQ(camera->getViewportWidth(), 143u); + EXPECT_EQ(camera->getViewportHeight(), 243u); + + // Animation node is linked and can be animated + EXPECT_FLOAT_EQ(2.f, *animNode->getOutputs()->getChild("duration")->get()); + animNode->getInputs()->getChild("progress")->set(0.75f); + EXPECT_TRUE(logicEngine.update()); + + auto appearance = ramsesScene.findObject("test appearance"); + const auto uniform = appearance->getEffect().getUniformInput(1); + ASSERT_TRUE(uniform.has_value()); + float floatValue = 0.f; + appearance->getInputValue(*uniform, floatValue); + EXPECT_FLOAT_EQ(1.5f, floatValue); + + EXPECT_EQ(957, *logicEngine.findObject("script2")->getOutputs()->getChild("nestedModulesResult")->get()); + + EXPECT_TRUE(logicEngine.findObject("renderpassbinding")); + EXPECT_TRUE(logicEngine.findObject("anchorpoint")); + + const auto cameraBindingPersp = logicEngine.findObject("camerabindingPersp"); + const auto cameraBindingPerspWithFrustumPlanes = logicEngine.findObject("camerabindingPerspWithFrustumPlanes"); + ASSERT_TRUE(cameraBindingPersp && cameraBindingPerspWithFrustumPlanes); + EXPECT_EQ(4u, cameraBindingPersp->getInputs()->getChild("frustum")->getChildCount()); + EXPECT_EQ(6u, cameraBindingPerspWithFrustumPlanes->getInputs()->getChild("frustum")->getChildCount()); + + EXPECT_TRUE(logicEngine.findObject("rendergroupbinding")); + EXPECT_TRUE(logicEngine.findObject("skin")); + const auto dataArray = logicEngine.findObject("dataarrayOfArrays"); + ASSERT_TRUE(dataArray); + EXPECT_EQ(EPropertyType::Array, dataArray->getDataType()); + EXPECT_EQ(2u, dataArray->getNumElements()); + const auto data = dataArray->getData>(); + ASSERT_TRUE(data); + const std::vector> expectedData{ { 1.f, 2.f, 3.f, 4.f, 5.f }, { 6.f, 7.f, 8.f, 9.f, 10.f } }; + EXPECT_EQ(expectedData, *data); + EXPECT_TRUE(logicEngine.findObject("meshnodebinding")); + } + + static void expectFeatureLevel02Content(const LogicEngine& /*logicEngine*/, ramses::Scene& /*ramsesScene*/) + { + // features added in future feature level expected to be present + } + + static void expectFeatureLevel02ContentNotPresent(const LogicEngine& /*logicEngine*/) + { + // features added in future feature level expected NOT to be present in previous feature levels + } + + static void checkContents(LogicEngine& logicEngine, ramses::Scene& scene) + { + const ramses::EFeatureLevel featureLevel = scene.getRamsesClient().getRamsesFramework().getFeatureLevel(); + + // check for content expected to exist + // higher feature level always contains content supported by lower level + switch (featureLevel) + { + //case ramses::EFeatureLevel_02: + // expectFeatureLevel02Content(logicEngine, scene); + // [[fallthrough]]; + case ramses::EFeatureLevel_01: + checkBaseContents(logicEngine, scene); + break; + } + + // check for content expected to not exist + // lower feature level never contains content supported only in higher level + switch (featureLevel) + { + case ramses::EFeatureLevel_01: + expectFeatureLevel02ContentNotPresent(logicEngine); + // [[fallthrough]]; + //case EFeatureLevel_02: + // break; + } + } + + static void saveAndReloadAndCheckContents(ramses::Scene& scene) + { + WithTempDirectory tempDir; + + ramses::SaveFileConfig noValidationConfig; + noValidationConfig.setValidationEnabled(false); + EXPECT_TRUE(scene.saveToFile("temp.ramses", noValidationConfig)); + + auto& client = scene.getRamsesClient(); + client.destroy(scene); + auto otherScene = client.loadSceneFromFile("temp.ramses"); + auto logicEngineAnother = otherScene->findObject("testAssetLogic"); + EXPECT_TRUE(logicEngineAnother->update()); + + checkContents(*logicEngineAnother, *otherScene); + client.destroy(*otherScene); + } + + ramses::Scene* loadRamsesScene(ramses::EFeatureLevel featureLevel) + { + switch(featureLevel) + { + case ramses::EFeatureLevel_01: + return &m_ramses.loadSceneFromFile("res/testScene_01.ramses"); + } + return nullptr; + } + + RamsesTestSetup m_ramses; + }; + + TEST_F(ALogicEngine_Binary_Compatibility, CanLoadAndUpdateABinaryFileExportedWithLastCompatibleVersionOfEngine_FeatureLevel01) + { + ramses::Scene* scene = loadRamsesScene(ramses::EFeatureLevel_01); + ASSERT_TRUE(scene); + auto* logicEngine = scene->findObject("testAssetLogic"); + ASSERT_TRUE(logicEngine); + EXPECT_TRUE(logicEngine->update()); + + checkContents(*logicEngine, *scene); + saveAndReloadAndCheckContents(*scene); + } +} diff --git a/client/logic/unittests/api/LogicEngineTest_DependencyExtraction.cpp b/tests/unittests/client/logic/api/LogicEngineTest_DependencyExtraction.cpp similarity index 94% rename from client/logic/unittests/api/LogicEngineTest_DependencyExtraction.cpp rename to tests/unittests/client/logic/api/LogicEngineTest_DependencyExtraction.cpp index 865d24b87..ddab06793 100644 --- a/client/logic/unittests/api/LogicEngineTest_DependencyExtraction.cpp +++ b/tests/unittests/client/logic/api/LogicEngineTest_DependencyExtraction.cpp @@ -8,7 +8,7 @@ #include "LogicEngineTest_Base.h" -namespace ramses +namespace ramses::internal { class ALogicEngine_DependencyExtraction : public ALogicEngine { @@ -20,17 +20,16 @@ namespace ramses extractedDependencies.push_back(dep); }; - ASSERT_TRUE(m_logicEngine.extractLuaDependencies(src, callbackFunc)); + ASSERT_TRUE(m_logicEngine->extractLuaDependencies(src, callbackFunc)); EXPECT_EQ(expected, extractedDependencies); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + expectNoError(); } void extractAndExpectError(std::string_view src, std::string_view errorMsgSubstr) { std::function callbackFunc = [](const std::string& /*unused*/) {}; - EXPECT_FALSE(m_logicEngine.extractLuaDependencies(src, callbackFunc)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr(errorMsgSubstr)); + EXPECT_FALSE(m_logicEngine->extractLuaDependencies(src, callbackFunc)); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(errorMsgSubstr)); } }; diff --git a/tests/unittests/client/logic/api/LogicEngineTest_Dirtiness.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Dirtiness.cpp new file mode 100644 index 000000000..8830c5f8e --- /dev/null +++ b/tests/unittests/client/logic/api/LogicEngineTest_Dirtiness.cpp @@ -0,0 +1,577 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/Property.h" + +#include "LogicEngineTest_Base.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/LuaInterfaceImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "internal/logic/ApiObjects.h" + +#include "RamsesTestUtils.h" + +namespace ramses::internal +{ + class ALogicEngine_DirtinessBase : public ALogicEngineBase + { + protected: + ApiObjects& m_apiObjects = { m_logicEngine->impl().getApiObjects() }; + + const std::string_view m_minimal_script = R"( + function interface(IN,OUT) + IN.data = Type:Int32() + OUT.data = Type:Int32() + end + function run(IN,OUT) + OUT.data = IN.data + end + )"; + + const std::string_view m_nested_properties_script = R"( + function interface(IN,OUT) + IN.data = { + nested = Type:Int32() + } + OUT.data = { + nested = Type:Int32() + } + end + function run(IN,OUT) + OUT.data.nested = IN.data.nested + end + )"; + + const std::string_view m_valid_empty_interface = R"( + function interface(IN,OUT) + end + )"; + + const std::string_view m_minimal_interface = R"( + function interface(IN) + IN.data = Type:Int32() + end + )"; + }; + + class ALogicEngine_Dirtiness : public ALogicEngine_DirtinessBase, public ::testing::Test + { + }; + + TEST_F(ALogicEngine_Dirtiness, CreatedObjectsAreDirtyAfterCreating) + { + const auto obj1 = m_logicEngine->createLuaScript(m_valid_empty_script); + EXPECT_TRUE(obj1->impl().isDirty()); + const auto obj2 = m_logicEngine->createLuaInterface(m_valid_empty_interface, "iface name"); + EXPECT_TRUE(obj2->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, CreatedBindingsAreNotDirtyAfterCreating) + { + const auto obj3 = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + EXPECT_FALSE(obj3->impl().isDirty()); + const auto obj4 = m_logicEngine->createAppearanceBinding(*m_appearance, ""); + EXPECT_FALSE(obj4->impl().isDirty()); + const auto obj5 = m_logicEngine->createRenderPassBinding(*m_renderPass, ""); + EXPECT_FALSE(obj5->impl().isDirty()); + const auto obj6 = createRenderGroupBinding(); + EXPECT_FALSE(obj6->impl().isDirty()); + const auto obj = m_logicEngine->createMeshNodeBinding(*m_meshNode); + EXPECT_FALSE(obj->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingNodeBinding_AndChangingInput) + { + NodeBinding* nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + nodeBinding->getInputs()->getChild("scaling")->set(vec3f{1.5f, 1.f, 1.f}); + EXPECT_TRUE(nodeBinding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingAppearanceBinding_AndChangingInput) + { + AppearanceBinding* appBinding = m_logicEngine->createAppearanceBinding(*m_appearance, ""); + appBinding->getInputs()->getChild("floatUniform")->set(15.f); + EXPECT_TRUE(appBinding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingCameraBinding_AndChangingInput) + { + CameraBinding* camBinding = m_logicEngine->createCameraBinding(*m_camera, ""); + camBinding->getInputs()->getChild("viewport")->getChild("width")->set(15); + EXPECT_TRUE(camBinding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingRenderPassBinding_AndChangingInput) + { + RenderPassBinding* binding = m_logicEngine->createRenderPassBinding(*m_renderPass, ""); + binding->getInputs()->getChild("enabled")->set(false); + EXPECT_TRUE(binding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingRenderGroupBinding_AndChangingInput) + { + auto binding = createRenderGroupBinding(); + binding->getInputs()->getChild("renderOrders")->getChild("mesh")->set(42); + EXPECT_TRUE(binding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, DirtyAfterCreatingMeshNodeBinding_AndChangingInput) + { + auto binding = m_logicEngine->createMeshNodeBinding(*m_meshNode); + binding->getInputs()->getChild("vertexOffset")->set(42); + EXPECT_TRUE(binding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, NotDirty_AfterCreatingObjectsAndCallingUpdate) + { + const auto obj1 = m_logicEngine->createLuaScript(m_valid_empty_script); + const auto obj2 = m_logicEngine->createLuaInterface(m_valid_empty_interface, "iface name"); + const auto obj3 = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + const auto obj4 = m_logicEngine->createAppearanceBinding(*m_appearance, ""); + const auto obj5 = m_logicEngine->createCameraBinding(*m_camera, ""); + const auto obj6 = m_logicEngine->createRenderPassBinding(*m_renderPass, ""); + const auto obj7 = createRenderGroupBinding(); + const auto obj8 = m_logicEngine->createMeshNodeBinding(*m_meshNode); + + m_logicEngine->update(); + EXPECT_FALSE(obj1->impl().isDirty()); + EXPECT_FALSE(obj2->impl().isDirty()); + EXPECT_FALSE(obj3->impl().isDirty()); + EXPECT_FALSE(obj4->impl().isDirty()); + EXPECT_FALSE(obj5->impl().isDirty()); + EXPECT_FALSE(obj6->impl().isDirty()); + EXPECT_FALSE(obj7->impl().isDirty()); + EXPECT_FALSE(obj8->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, Dirty_AfterSettingScriptInput) + { + LuaScript* script = m_logicEngine->createLuaScript(m_minimal_script); + m_logicEngine->update(); + + script->getInputs()->getChild("data")->set(5); + + EXPECT_TRUE(script->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, Dirty_AfterSettingNestedScriptInput) + { + LuaScript* script = m_logicEngine->createLuaScript(m_nested_properties_script); + m_logicEngine->update(); + + script->getInputs()->getChild("data")->getChild("nested")->set(5); + + EXPECT_TRUE(script->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, Dirty_AfterSettingInterfaceInput) + { + LuaInterface* intf = m_logicEngine->createLuaInterface(m_minimal_interface, "iface name"); + m_logicEngine->update(); + + intf->getInputs()->getChild("data")->set(5); + + EXPECT_TRUE(intf->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(intf->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, Dirty_WhenSettingBindingInputToDefaultValue) + { + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + m_logicEngine->update(); + + // zeroes is the default value + binding->getInputs()->getChild("translation")->set({0, 0, 0}); + EXPECT_TRUE(binding->impl().isDirty()); + m_logicEngine->update(); + + // Set different value, and then set again + binding->getInputs()->getChild("translation")->set({1, 2, 3}); + m_logicEngine->update(); + binding->getInputs()->getChild("translation")->set({1, 2, 3}); + EXPECT_TRUE(binding->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, Dirty_WhenSettingBindingInputToDifferentValue) + { + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + m_logicEngine->update(); + + // Set non-default value, and then set again to different value + binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); + m_logicEngine->update(); + EXPECT_FALSE(binding->impl().isDirty()); + binding->getInputs()->getChild("translation")->set({ 11, 12, 13 }); + EXPECT_TRUE(binding->impl().isDirty()); + } + + class ALogicEngine_DirtinessViaLink : public ALogicEngine_DirtinessBase, public ::testing::TestWithParam + { + protected: + void link(Property& src, Property& dst) + { + if (GetParam()) + { + EXPECT_TRUE(m_logicEngine->linkWeak(src, dst)); + } + else + { + EXPECT_TRUE(m_logicEngine->link(src, dst)); + } + } + }; + + INSTANTIATE_TEST_SUITE_P( + ALogicEngine_DirtinessViaLink_TestInstances, + ALogicEngine_DirtinessViaLink, + ::testing::Values(false, true)); + + TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenAddingLink) + { + LuaScript* script1 = m_logicEngine->createLuaScript(m_minimal_script); + LuaScript* script2 = m_logicEngine->createLuaScript(m_minimal_script); + m_logicEngine->update(); + + link(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); + EXPECT_TRUE(script1->impl().isDirty()); + EXPECT_TRUE(script2->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + } + + TEST_P(ALogicEngine_DirtinessViaLink, NotDirty_WhenRemovingLink) + { + LuaScript* script1 = m_logicEngine->createLuaScript(m_minimal_script); + LuaScript* script2 = m_logicEngine->createLuaScript(m_minimal_script); + link(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); + m_logicEngine->update(); + + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + m_logicEngine->unlink(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); + + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + } + + TEST_P(ALogicEngine_DirtinessViaLink, NotDirty_WhenRemovingNestedLink) + { + LuaScript* script1 = m_logicEngine->createLuaScript(m_nested_properties_script); + LuaScript* script2 = m_logicEngine->createLuaScript(m_nested_properties_script); + link(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); + m_logicEngine->update(); + + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + m_logicEngine->unlink(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); + + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + } + + // Removing link does not mark things dirty, but setting value does + TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenRemovingLink_AndSettingValueByCallingSetAfterwards) + { + LuaScript* script1 = m_logicEngine->createLuaScript(m_nested_properties_script); + LuaScript* script2 = m_logicEngine->createLuaScript(m_nested_properties_script); + m_logicEngine->update(); + + link(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); + m_logicEngine->update(); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + + m_logicEngine->unlink(*script1->getOutputs()->getChild("data")->getChild("nested"), *script2->getInputs()->getChild("data")->getChild("nested")); + script2->getInputs()->getChild("data")->getChild("nested")->set(5); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_TRUE(script2->impl().isDirty()); + } + + TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenAddingLinkToInterfaceInput) + { + LuaScript* script = m_logicEngine->createLuaScript(m_minimal_script); + LuaInterface* intf = m_logicEngine->createLuaInterface(m_minimal_interface, "iface name"); + m_logicEngine->update(); + + link(*script->getOutputs()->getChild("data"), *intf->getInputs()->getChild("data")); + EXPECT_TRUE(script->impl().isDirty()); + EXPECT_TRUE(intf->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(intf->impl().isDirty()); + } + + TEST_P(ALogicEngine_DirtinessViaLink, Dirty_WhenAddingLinkToInterfaceOutput) + { + LuaScript* script = m_logicEngine->createLuaScript(m_minimal_script); + LuaInterface* intf = m_logicEngine->createLuaInterface(m_minimal_interface, "iface name"); + m_logicEngine->update(); + + link(*intf->getOutputs()->getChild("data"), *script->getInputs()->getChild("data")); + EXPECT_TRUE(script->impl().isDirty()); + EXPECT_TRUE(intf->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(intf->impl().isDirty()); + } + + TEST_P(ALogicEngine_DirtinessViaLink, NotDirty_WhenRemovingLinkToInterface) + { + LuaScript* script = m_logicEngine->createLuaScript(m_minimal_script); + LuaInterface* intf = m_logicEngine->createLuaInterface(m_minimal_interface, "iface name"); + link(*script->getOutputs()->getChild("data"), *intf->getInputs()->getChild("data")); + m_logicEngine->update(); + + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(intf->impl().isDirty()); + m_logicEngine->unlink(*script->getOutputs()->getChild("data"), *intf->getInputs()->getChild("data")); + + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(intf->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(intf->impl().isDirty()); + } + + TEST_F(ALogicEngine_Dirtiness, Dirty_WhenScriptHadRuntimeError) + { + const std::string_view scriptWithError = R"( + function interface(IN,OUT) + end + function run(IN,OUT) + error("Snag!") + end + )"; + + const auto script = m_logicEngine->createLuaScript(scriptWithError); + EXPECT_FALSE(m_logicEngine->update()); + + EXPECT_TRUE(script->impl().isDirty()); + } + + // This is a bit of a special case, but an important one. If script A provides a value for script B and script A has an error + // AFTER it set a new value to B, then script B will be dirty (and not executed!) until the error in script A was fixed + TEST_F(ALogicEngine_Dirtiness, KeepsDirtynessStateOfDependentScript_UntilErrorInSourceScriptIsFixed) + { + const std::string_view scriptWithFixableError = R"( + function interface(IN,OUT) + IN.triggerError = Type:Bool() + IN.data = Type:Int32() + OUT.data = Type:Int32() + end + function run(IN,OUT) + OUT.data = IN.data + if IN.triggerError then + error("Snag!") + end + end + )"; + + LuaScript* script1 = m_logicEngine->createLuaScript(scriptWithFixableError); + LuaScript* script2 = m_logicEngine->createLuaScript(m_minimal_script); + + // No error -> have normal run -> nothing is dirty + script1->getInputs()->getChild("triggerError")->set(false); + m_logicEngine->link(*script1->getOutputs()->getChild("data"), *script2->getInputs()->getChild("data")); + m_logicEngine->update(); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + + // Trigger error -> keep in dirty state + script1->getInputs()->getChild("triggerError")->set(true); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_TRUE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + // "fix" the error and set a value -> expect nothing is dirty and value was propagated + script1->getInputs()->getChild("triggerError")->set(false); + script1->getInputs()->getChild("data")->set(15); + + m_logicEngine->update(); + EXPECT_FALSE(script1->impl().isDirty()); + EXPECT_FALSE(script2->impl().isDirty()); + EXPECT_EQ(15, *script2->getOutputs()->getChild("data")->get()); + } + + class ALogicEngine_BindingDirtiness : public ALogicEngine_Dirtiness + { + protected: + const std::string_view m_bindningDataScript = R"( + function interface(IN,OUT) + OUT.vec3f = Type:Vec3f() + end + function run(IN,OUT) + OUT.vec3f = {1, 2, 3} + end + )"; + }; + + TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterConstruction) + { + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterCreatingScript) + { + m_logicEngine->createLuaScript(m_valid_empty_script); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterCreatingNodeBinding) + { + m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, NotDirtyAfterCreatingAppearanceBinding) + { + m_logicEngine->createAppearanceBinding(*m_appearance, ""); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenSettingBindingInputToDefaultValue) + { + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + m_logicEngine->update(); + + // zeroes is the default value + binding->getInputs()->getChild("translation")->set({ 0, 0, 0 }); + EXPECT_TRUE(m_apiObjects.bindingsDirty()); + m_logicEngine->update(); + + // Set different value, and then set again + binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); + m_logicEngine->update(); + binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); + EXPECT_TRUE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenSettingBindingInputToDifferentValue) + { + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + m_logicEngine->update(); + + // Set non-default value, and then set again to different value + binding->getInputs()->getChild("translation")->set({ 1, 2, 3 }); + m_logicEngine->update(); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + binding->getInputs()->getChild("translation")->set({ 11, 12, 13 }); + EXPECT_TRUE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenAddingLink) + { + LuaScript* script = m_logicEngine->createLuaScript(m_bindningDataScript); + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + m_logicEngine->update(); + + m_logicEngine->link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); + EXPECT_TRUE(m_apiObjects.bindingsDirty()); + + // After update - not dirty + m_logicEngine->update(); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, NotDirty_WhenRemovingLink) + { + LuaScript* script = m_logicEngine->createLuaScript(m_bindningDataScript); + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + m_logicEngine->link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); + m_logicEngine->update(); + + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(binding->impl().isDirty()); + m_logicEngine->unlink(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); + + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(binding->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(binding->impl().isDirty()); + } + + // Special case, but worth testing as we want that bindings are always + // executed when adding link, even if the link was just "re-added" + TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenReAddingLink) + { + LuaScript* script = m_logicEngine->createLuaScript(m_bindningDataScript); + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation"))); + m_logicEngine->update(); + + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(binding->impl().isDirty()); + m_logicEngine->unlink(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); + m_logicEngine->link(*script->getOutputs()->getChild("vec3f"), *binding->getInputs()->getChild("rotation")); + + EXPECT_TRUE(script->impl().isDirty()); + EXPECT_TRUE(binding->impl().isDirty()); + m_logicEngine->update(); + EXPECT_FALSE(script->impl().isDirty()); + EXPECT_FALSE(binding->impl().isDirty()); + } + + TEST_F(ALogicEngine_BindingDirtiness, Dirty_WhenSettingDataToNestedAppearanceBindingInputs) + { + // Vertex shader with array -> results in nested binding inputs + const std::string_view vertShader_array = R"( + #version 300 es + + uniform highp vec4 vec4Array[2]; + + void main() + { + gl_Position = vec4Array[1]; + })"; + + const std::string_view fragShader_trivial = R"( + #version 300 es + + out lowp vec4 color; + void main(void) + { + color = vec4(1.0, 0.0, 0.0, 1.0); + })"; + + AppearanceBinding* binding = m_logicEngine->createAppearanceBinding(RamsesTestSetup::CreateTestAppearance(*m_scene, vertShader_array, fragShader_trivial), ""); + + m_logicEngine->update(); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + + EXPECT_TRUE(binding->getInputs()->getChild("vec4Array")->getChild(0)->set({ .1f, .2f, .3f, .4f })); + EXPECT_TRUE(m_apiObjects.bindingsDirty()); + + m_logicEngine->update(); + EXPECT_FALSE(m_apiObjects.bindingsDirty()); + } +} + diff --git a/tests/unittests/client/logic/api/LogicEngineTest_Factory.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Factory.cpp new file mode 100644 index 000000000..87f6cce93 --- /dev/null +++ b/tests/unittests/client/logic/api/LogicEngineTest_Factory.cpp @@ -0,0 +1,676 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" + +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/AnimationNodeConfig.h" + +#include "ramses/client/DataObject.h" + +#include "impl/logic/LogicNodeImpl.h" + +#include "FeatureLevelTestValues.h" + +#include +#include + +namespace ramses::internal +{ + // There are more specific "create/destroy" tests in ApiObjects unit tests! + class ALogicEngine_Factory : public ALogicEngineBase, public ::testing::TestWithParam + { + public: + ALogicEngine_Factory() : ALogicEngineBase{ GetParam() } + { + withTempDirectory(); + } + }; + + INSTANTIATE_TEST_SUITE_P( + ALogicEngine_FactoryTests, + ALogicEngine_Factory, + GetFeatureLevelTestValues()); + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingEmptyScript) + { + const LuaScript* script = m_logicEngine->createLuaScript(""); + ASSERT_EQ(nullptr, script); + EXPECT_EQ(getLastErrorMessage(), "[] No 'run' function defined!"); + } + + TEST_P(ALogicEngine_Factory, CreatesScriptFromValidLuaWithoutErrors) + { + const LuaScript* script = m_logicEngine->createLuaScript(m_valid_empty_script); + ASSERT_TRUE(nullptr != script); + expectNoError(); + } + + TEST_P(ALogicEngine_Factory, DestroysScriptWithoutErrors) + { + LuaScript* script = m_logicEngine->createLuaScript(m_valid_empty_script); + ASSERT_TRUE(script); + ASSERT_TRUE(m_logicEngine->destroy(*script)); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingScriptFromAnotherEngineInstance) + { + auto& otherLogicEngine = *m_scene->createLogicEngine(); + auto script = otherLogicEngine.createLuaScript(m_valid_empty_script); + ASSERT_TRUE(script); + ASSERT_FALSE(m_logicEngine->destroy(*script)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object ' [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, CreatesLuaModule) + { + const auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); + ASSERT_NE(nullptr, module); + expectNoError(); + + EXPECT_EQ(module, m_logicEngine->findObject("mymodule")); + ASSERT_EQ(1u, m_logicEngine->getCollection().size()); + EXPECT_EQ(module, *m_logicEngine->getCollection().cbegin()); + + const auto* constLogicEngine = m_logicEngine; + EXPECT_EQ(module, constLogicEngine->findObject("mymodule")); + } + + TEST_P(ALogicEngine_Factory, AllowsCreatingLuaModuleWithEmptyName) + { + EXPECT_NE(nullptr, m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "")); + expectNoError(); + } + + TEST_P(ALogicEngine_Factory, AllowsCreatingLuaModuleWithNameContainingNonAlphanumericChars) + { + EXPECT_NE(nullptr, m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "!@#$")); + expectNoError(); + } + + TEST_P(ALogicEngine_Factory, AllowsCreatingLuaModuleWithDupliciteNameEvenIfSourceDiffers) + { + ASSERT_TRUE(m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule")); + // same name and same source is OK + EXPECT_TRUE(m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule")); + + // same name and different source is also OK + EXPECT_TRUE(m_logicEngine->createLuaModule("return {}", {}, "mymodule")); + } + + TEST_P(ALogicEngine_Factory, CanDestroyLuaModule) + { + LuaModule* module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); + ASSERT_NE(nullptr, module); + EXPECT_TRUE(m_logicEngine->destroy(*module)); + expectNoError(); + EXPECT_FALSE(m_logicEngine->findObject("mymodule")); + } + + TEST_P(ALogicEngine_Factory, FailsToDestroyLuaModuleIfFromOtherLogicInstance) + { + auto& otherLogic = *m_scene->createLogicEngine(); + LuaModule* module = otherLogic.createLuaModule(m_moduleSourceCode); + ASSERT_NE(nullptr, module); + + EXPECT_FALSE(m_logicEngine->destroy(*module)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object ' [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, FailsToDestroyLuaModuleIfUsedInLuaScript) + { + LuaModule* module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); + ASSERT_NE(nullptr, module); + + constexpr std::string_view valid_empty_script = R"( + modules("mymodule") + function interface(IN,OUT) + end + function run(IN,OUT) + end + )"; + EXPECT_TRUE(m_logicEngine->createLuaScript(valid_empty_script, CreateDeps({ { "mymodule", module } }), "script")); + + EXPECT_FALSE(m_logicEngine->destroy(*module)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy LuaModule 'mymodule', it is used in LuaScript 'script'"); + } + + TEST_P(ALogicEngine_Factory, CanDestroyModuleAfterItIsNotUsedAnymore) + { + LuaModule* module = m_logicEngine->createLuaModule(m_moduleSourceCode); + ASSERT_NE(nullptr, module); + + constexpr std::string_view valid_empty_script = R"( + modules("mymodule") + function interface(IN,OUT) + end + function run(IN,OUT) + end + )"; + auto script = m_logicEngine->createLuaScript(valid_empty_script, CreateDeps({ { "mymodule", module } })); + ASSERT_NE(nullptr, script); + EXPECT_FALSE(m_logicEngine->destroy(*module)); + + EXPECT_TRUE(m_logicEngine->destroy(*script)); + EXPECT_TRUE(m_logicEngine->destroy(*module)); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingLuaScriptUsingModuleFromAnotherLogicInstance) + { + auto& other = *m_scene->createLogicEngine(); + const auto module = other.createLuaModule(m_moduleSourceCode); + ASSERT_NE(nullptr, module); + + EXPECT_EQ(nullptr, m_logicEngine->createLuaScript(m_valid_empty_script, CreateDeps({ { "name", module } }))); + EXPECT_EQ(getLastErrorMessage(), + "Failed to map Lua module 'name'! It was created on a different instance of LogicEngine."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingLuaModuleUsingModuleFromAnotherLogicInstance) + { + auto& other = *m_scene->createLogicEngine(); + const auto module = other.createLuaModule(m_moduleSourceCode); + ASSERT_NE(nullptr, module); + + LuaConfig config; + config.addDependency("name", *module); + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule(m_valid_empty_script, config)); + EXPECT_EQ(getLastErrorMessage(), + "Failed to map Lua module 'name'! It was created on a different instance of LogicEngine."); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateNodeBindingIfFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createNodeBinding(*m_node)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create NodeBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateAppearanceBindingIfFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createAppearanceBinding(*m_appearance)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create AppearanceBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateCameraBindingIfFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createCameraBinding(*m_camera)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create CameraBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createCameraBindingWithFrustumPlanes(*m_camera)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create CameraBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateRenderPassBindingIfFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createRenderPassBinding(*m_renderPass)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create RenderPassBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateRenderGroupBindingIfFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + EXPECT_EQ(nullptr, createRenderGroupBinding(logicEngineFromOtherScene)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create RenderGroupBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateRenderGroupBindingIfElementsFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + RenderGroupBindingElements elements; + EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); + auto rg = logicEngineFromOtherScene.getScene().createRenderGroup(); + // RG is from same scene as logic but elements are not + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createRenderGroupBinding(*rg, elements)); + + EXPECT_EQ(getLastErrorMessage(), "Failed to create RenderGroupBinding, elements are from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, FailsToCreateMeshNodeBindingIfFromOtherScene) + { + auto& logicEngineFromOtherScene{ *m_ramses.createScene(sceneId_t{ 666u })->createLogicEngine() }; + + EXPECT_EQ(nullptr, logicEngineFromOtherScene.createMeshNodeBinding(*m_meshNode)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create MeshNodeBinding, object is from sceneId=1 but LogicEngine is from sceneId=666"); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingNodeBindingFromAnotherEngineInstance) + { + auto& otherLogicEngine = *m_scene->createLogicEngine(); + + auto nodeBinding = otherLogicEngine.createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + ASSERT_TRUE(nodeBinding); + ASSERT_FALSE(m_logicEngine->destroy(*nodeBinding)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object 'NodeBinding [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingAppearanceBindingFromAnotherEngineInstance) + { + auto& otherLogicEngine = *m_scene->createLogicEngine(); + auto binding = otherLogicEngine.createAppearanceBinding(*m_appearance, "AppearanceBinding"); + ASSERT_TRUE(binding); + ASSERT_FALSE(m_logicEngine->destroy(*binding)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object 'AppearanceBinding [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, DestroysCameraBindingWithoutErrors) + { + auto binding = m_logicEngine->createCameraBinding(*m_camera, "CameraBinding"); + ASSERT_TRUE(binding); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingCameraBindingFromAnotherEngineInstance) + { + auto& otherLogicEngine = *m_scene->createLogicEngine(); + auto binding = otherLogicEngine.createCameraBinding(*m_camera, "CameraBinding"); + ASSERT_TRUE(binding); + ASSERT_FALSE(m_logicEngine->destroy(*binding)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object 'CameraBinding [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, DestroysRenderPassBindingWithoutErrors) + { + auto binding = m_logicEngine->createRenderPassBinding(*m_renderPass, "rp"); + ASSERT_TRUE(binding); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRenderPassBindingFromAnotherEngineInstance) + { + auto& otherLogicEngine = *m_scene->createLogicEngine(); + auto binding = otherLogicEngine.createRenderPassBinding(*m_renderPass, "rp"); + ASSERT_TRUE(binding); + ASSERT_FALSE(m_logicEngine->destroy(*binding)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object 'rp [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, DestroysRenderGroupBindingWithoutErrors) + { + auto binding = createRenderGroupBinding(); + ASSERT_TRUE(binding); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingRenderGroupBindingFromAnotherEngineInstance) + { + auto binding = createRenderGroupBinding(); + ASSERT_TRUE(binding); + + auto& otherLogicEngine = *m_scene->createLogicEngine(); + ASSERT_FALSE(otherLogicEngine.destroy(*binding)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object 'renderGroupBinding [LogicObject ScnObjId=9]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, DestroysMeshNodeBindingWithoutErrors) + { + auto binding = m_logicEngine->createMeshNodeBinding(*m_meshNode); + ASSERT_TRUE(binding); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorsWhenDestroyingMeshNodeBindingFromAnotherEngineInstance) + { + auto binding = m_logicEngine->createMeshNodeBinding(*m_meshNode); + ASSERT_TRUE(binding); + + auto& otherLogicEngine = *m_scene->createLogicEngine(); + ASSERT_FALSE(otherLogicEngine.destroy(*binding)); + EXPECT_EQ(getLastErrorMessage(), "Failed to destroy object ' [LogicObject ScnObjId=9]', cannot find it in this LogicEngine instance."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingAnchorPointAndNodeOrCameraFromAnotherInstance) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + const auto cameraBinding = m_logicEngine->createCameraBinding(*m_camera); + + auto& otherEngine = *m_scene->createLogicEngine(); + const auto nodeBindingOther = otherEngine.createNodeBinding(*m_node); + const auto cameraBindingOther = otherEngine.createCameraBinding(*m_camera); + + EXPECT_EQ(nullptr, m_logicEngine->createAnchorPoint(*nodeBindingOther, *cameraBinding, "anchor")); + EXPECT_EQ(getLastErrorMessage(), "Failed to create AnchorPoint 'anchor': provided Ramses node binding and/or camera binding were not found in this logic instance."); + + EXPECT_EQ(nullptr, m_logicEngine->createAnchorPoint(*nodeBinding, *cameraBindingOther, "anchor")); + EXPECT_EQ(getLastErrorMessage(), "Failed to create AnchorPoint 'anchor': provided Ramses node binding and/or camera binding were not found in this logic instance."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndNodeOrAppearanceFromAnotherInstance) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + const auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance); + + auto& otherEngine = *m_scene->createLogicEngine(); + const auto nodeBindingOther = otherEngine.createNodeBinding(*m_node); + const auto appearanceBindingOther = otherEngine.createAppearanceBinding(*m_appearance); + + EXPECT_EQ(nullptr, createSkinBinding(*nodeBindingOther, *appearanceBinding, *m_logicEngine)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create SkinBinding 'skin': one or more of the provided Ramses node bindings was not found in this logic instance."); + + EXPECT_EQ(nullptr, createSkinBinding(*nodeBinding, *appearanceBindingOther, *m_logicEngine)); + EXPECT_EQ(getLastErrorMessage(), "Failed to create SkinBinding 'skin': provided Ramses appearance binding was not found in this logic instance."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndNodesEmptyOrNull) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance); + const auto optUniform = m_appearance->getEffect().findUniformInput("jointMat"); + ASSERT_TRUE(optUniform.has_value()); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({}, {}, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, no or null joint node bindings provided."); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({ nodeBinding, nullptr }, { matrix44f{ 0.f }, matrix44f{ 0.f } }, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, no or null joint node bindings provided."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndNodesCountDifferentFromMatricesCount) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance); + + const auto optUniform = m_appearance->getEffect().findUniformInput("jointMat"); + ASSERT_TRUE(optUniform.has_value()); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({ nodeBinding }, { matrix44f{ 0.f }, matrix44f{ 0.f } }, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, number of inverse matrices must match the number of joints."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformInvalidOrFromAnotherEffect) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance); + + // valid uniform but from other effect + const std::string_view vertShader = R"( + #version 100 + uniform highp float someUniform; + attribute vec3 a_position; + void main() + { + gl_Position = someUniform * vec4(a_position, 1.0); + })"; + const std::string_view fragShader = R"( + #version 100 + void main(void) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + })"; + ramses::EffectDescription effectDesc; + effectDesc.setVertexShader(vertShader.data()); + effectDesc.setFragmentShader(fragShader.data()); + const auto otherEffect = m_scene->createEffect(effectDesc); + const auto optUniform = otherEffect->findUniformInput("someUniform"); + ASSERT_TRUE(optUniform.has_value()); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformIsBoundInRamses) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance); + + const auto optUniform = m_appearance->getEffect().findUniformInput("floatUniform"); + ASSERT_TRUE(optUniform.has_value()); + EXPECT_TRUE(m_appearance->bindInput(*optUniform, *m_scene->createDataObject(ramses::EDataType::Float))); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, provided uniform input must be pointing to valid uniform of the provided appearance's effect and must not be bound."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformDataTypeWrong) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance); + + const auto optUniform = m_appearance->getEffect().findUniformInput("floatUniform"); + ASSERT_TRUE(optUniform.has_value()); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, provided uniform input must be of type array of Matrix4x4 with element count matching number of joints."); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorWhenCreatingSkinBindingAndUniformPointsToArrayWithMismatchedSize) + { + const auto nodeBinding = m_logicEngine->createNodeBinding(*m_node); + + const std::string_view vertShader = R"( + #version 100 + uniform highp mat4 someArray[3]; + attribute vec3 a_position; + void main() + { + gl_Position = someArray[0] * vec4(a_position, 1.0); + })"; + const std::string_view fragShader = R"( + #version 100 + void main(void) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + })"; + ramses::EffectDescription effectDesc; + effectDesc.setVertexShader(vertShader.data()); + effectDesc.setFragmentShader(fragShader.data()); + const auto otherEffect = m_scene->createEffect(effectDesc); + const auto optUniform = otherEffect->findUniformInput("someArray"); + ASSERT_TRUE(optUniform.has_value()); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_scene->createAppearance(*otherEffect)); + + EXPECT_FALSE(m_logicEngine->createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, *optUniform, "skin")); + EXPECT_EQ(getLastErrorMessage(), "Cannot create SkinBinding, provided uniform input must be of type array of Matrix4x4 with element count matching number of joints."); + } + + TEST_P(ALogicEngine_Factory, RenamesObjectsAfterCreation) + { + auto script = m_logicEngine->createLuaScript(m_valid_empty_script); + auto nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto cameraBinding = m_logicEngine->createCameraBinding(*m_camera, "CameraBinding"); + + script->setName("same name twice"); + nodeBinding->setName("same name twice"); + appearanceBinding->setName(""); + cameraBinding->setName(""); + + EXPECT_EQ("same name twice", script->getName()); + EXPECT_EQ("same name twice", nodeBinding->getName()); + EXPECT_EQ("", appearanceBinding->getName()); + EXPECT_EQ("", cameraBinding->getName()); + + auto renderPassBinding = m_logicEngine->createRenderPassBinding(*m_renderPass, "rp"); + renderPassBinding->setName(""); + EXPECT_EQ("", renderPassBinding->getName()); + + auto renderGroupBinding = createRenderGroupBinding(); + renderGroupBinding->setName(""); + EXPECT_EQ("", renderGroupBinding->getName()); + } + + TEST_P(ALogicEngine_Factory, CanCastObjectsToValidTypes) + { + LogicObject* luaModule = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "luaModule"); + LogicObject* luaScript = m_logicEngine->createLuaScript(m_valid_empty_script, {}, "script"); + LogicObject* nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); + LogicObject* appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance, "appbinding"); + LogicObject* cameraBinding = m_logicEngine->createCameraBinding(*m_camera, "camerabinding"); + LogicObject* dataArray = m_logicEngine->createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); + AnimationNodeConfig config; + config.addChannel({ "channel", dataArray->as(), dataArray->as(), EInterpolationType::Linear }); + LogicObject* animNode = m_logicEngine->createAnimationNode(config, "animNode"); + LogicObject* renderPassBinding = m_logicEngine->createRenderPassBinding(*m_renderPass, "rp"); + LogicObject* renderGroupBinding = createRenderGroupBinding(); + LogicObject* skin = createSkinBinding(*m_logicEngine); + LogicObject* meshBinding = m_logicEngine->createMeshNodeBinding(*m_meshNode); + + EXPECT_TRUE(luaModule->as()); + EXPECT_TRUE(luaScript->as()); + EXPECT_TRUE(nodeBinding->as()); + EXPECT_TRUE(appearanceBinding->as()); + EXPECT_TRUE(cameraBinding->as()); + EXPECT_TRUE(dataArray->as()); + EXPECT_TRUE(animNode->as()); + EXPECT_TRUE(renderPassBinding->as()); + EXPECT_TRUE(renderGroupBinding->as()); + EXPECT_TRUE(skin->as()); + EXPECT_TRUE(meshBinding->as()); + + EXPECT_FALSE(luaModule->as()); + EXPECT_FALSE(luaScript->as()); + EXPECT_FALSE(nodeBinding->as()); + EXPECT_FALSE(appearanceBinding->as()); + EXPECT_FALSE(cameraBinding->as()); + EXPECT_FALSE(dataArray->as()); + EXPECT_FALSE(animNode->as()); + EXPECT_FALSE(renderPassBinding->as()); + EXPECT_FALSE(renderGroupBinding->as()); + EXPECT_FALSE(skin->as()); + EXPECT_FALSE(meshBinding->as()); + + //cast obj -> node -> binding -> appearanceBinding + auto* nodeCastFromObject = appearanceBinding->as(); + EXPECT_TRUE(nodeCastFromObject); + auto* bindingCastFromNode = nodeCastFromObject->as(); + EXPECT_TRUE(bindingCastFromNode); + auto* appearanceBindingCastFromBinding = bindingCastFromNode->as(); + EXPECT_TRUE(appearanceBindingCastFromBinding); + + //cast appearanceBinding -> binding -> node -> obj + EXPECT_TRUE(appearanceBindingCastFromBinding->as()); + EXPECT_TRUE(bindingCastFromNode->as()); + EXPECT_TRUE(nodeCastFromObject->as()); + + //cast obj -> node -> animationnode + auto* anNodeCastFromObject = animNode->as(); + EXPECT_TRUE(anNodeCastFromObject); + auto* animationCastFromNode = anNodeCastFromObject->as(); + EXPECT_TRUE(animationCastFromNode); + + //cast animationnode -> node -> obj + EXPECT_TRUE(animationCastFromNode->as()); + EXPECT_TRUE(anNodeCastFromObject->as()); + } + + TEST_P(ALogicEngine_Factory, CanCastObjectsToValidTypes_Const) + { + m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "luaModule"); + m_logicEngine->createLuaScript(m_valid_empty_script, {}, "script"); + m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodebinding"); + m_logicEngine->createAppearanceBinding(*m_appearance, "appbinding"); + m_logicEngine->createCameraBinding(*m_camera, "camerabinding"); + const LogicObject* dataArray = m_logicEngine->createDataArray(std::vector{1.f, 2.f, 3.f}, "dataarray"); + AnimationNodeConfig config; + config.addChannel({ "channel", dataArray->as(), dataArray->as(), EInterpolationType::Linear }); + m_logicEngine->createAnimationNode(config, "animNode"); + m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + createRenderGroupBinding(); + createSkinBinding(*m_logicEngine); + m_logicEngine->createMeshNodeBinding(*m_meshNode, "meshBinding"); + + const auto* immutableLogicEngine = m_logicEngine; + const auto* luaModuleConst = immutableLogicEngine->findObject("luaModule"); + const auto* luaScriptConst = immutableLogicEngine->findObject("script"); + const auto* nodeBindingConst = immutableLogicEngine->findObject("nodebinding"); + const auto* appearanceBindingConst = immutableLogicEngine->findObject("appbinding"); + const auto* cameraBindingConst = immutableLogicEngine->findObject("camerabinding"); + const auto* dataArrayConst = immutableLogicEngine->findObject("dataarray"); + const auto* animNodeConst = immutableLogicEngine->findObject("animNode"); + const auto* renderPassBindingConst = immutableLogicEngine->findObject("renderPass"); + const auto* renderGroupBindingConst = immutableLogicEngine->findObject("renderGroupBinding"); + const auto* skinConst = immutableLogicEngine->findObject("skin"); + const auto* meshBindingConst = immutableLogicEngine->findObject("meshBinding"); + + EXPECT_TRUE(luaModuleConst->as()); + EXPECT_TRUE(luaScriptConst->as()); + EXPECT_TRUE(nodeBindingConst->as()); + EXPECT_TRUE(appearanceBindingConst->as()); + EXPECT_TRUE(cameraBindingConst->as()); + EXPECT_TRUE(dataArrayConst->as()); + EXPECT_TRUE(animNodeConst->as()); + EXPECT_TRUE(renderPassBindingConst->as()); + EXPECT_TRUE(renderGroupBindingConst->as()); + EXPECT_TRUE(skinConst->as()); + EXPECT_TRUE(meshBindingConst->as()); + + EXPECT_FALSE(luaModuleConst->as()); + EXPECT_FALSE(luaScriptConst->as()); + EXPECT_FALSE(nodeBindingConst->as()); + EXPECT_FALSE(appearanceBindingConst->as()); + EXPECT_FALSE(cameraBindingConst->as()); + EXPECT_FALSE(dataArrayConst->as()); + EXPECT_FALSE(animNodeConst->as()); + EXPECT_FALSE(renderPassBindingConst->as()); + EXPECT_FALSE(renderGroupBindingConst->as()); + EXPECT_FALSE(skinConst->as()); + EXPECT_FALSE(meshBindingConst->as()); + + // cast obj -> node -> binding -> appearanceBinding + const auto* nodeCastFromObject = appearanceBindingConst->as(); + EXPECT_TRUE(nodeCastFromObject); + const auto* bindingCastFromNode = nodeCastFromObject->as(); + EXPECT_TRUE(bindingCastFromNode); + const auto* appearanceBindingCastFromBinding = bindingCastFromNode->as(); + EXPECT_TRUE(appearanceBindingCastFromBinding); + + // cast appearanceBinding -> binding -> node -> obj + EXPECT_TRUE(appearanceBindingCastFromBinding->as()); + EXPECT_TRUE(bindingCastFromNode->as()); + EXPECT_TRUE(nodeCastFromObject->as()); + + // cast obj -> node -> animationnode + const auto* anNodeCastFromObject = animNodeConst->as(); + EXPECT_TRUE(anNodeCastFromObject); + const auto* animationCastFromNode = anNodeCastFromObject->as(); + EXPECT_TRUE(animationCastFromNode); + + // cast animationnode -> node -> obj + EXPECT_TRUE(animationCastFromNode->as()); + EXPECT_TRUE(anNodeCastFromObject->as()); + } + + TEST_P(ALogicEngine_Factory, ProducesErrorIfWrongObjectTypeIsDestroyed) + { + struct UnknownObjectImpl: LogicNodeImpl + { + explicit UnknownObjectImpl(SceneImpl& scene) + : LogicNodeImpl(scene, "name", sceneObjectId_t{ 1u }) + { + } + + std::optional update() override { return std::nullopt; } + void createRootProperties() final {} + }; + + struct UnknownObject : LogicNode + { + explicit UnknownObject(std::unique_ptr impl) + : LogicNode(std::move(impl)) + { + } + }; + + UnknownObject unknownObject(std::make_unique(m_scene->impl())); + EXPECT_FALSE(m_logicEngine->destroy(unknownObject)); + EXPECT_EQ(getLastErrorMessage(), "Tried to destroy object 'name' with unknown type"); + } +} diff --git a/client/logic/unittests/api/LogicEngineTest_Linking.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Linking.cpp similarity index 67% rename from client/logic/unittests/api/LogicEngineTest_Linking.cpp rename to tests/unittests/client/logic/api/LogicEngineTest_Linking.cpp index 14daaf962..5811d3b9e 100644 --- a/client/logic/unittests/api/LogicEngineTest_Linking.cpp +++ b/tests/unittests/client/logic/api/LogicEngineTest_Linking.cpp @@ -12,40 +12,41 @@ #include "WithTempDirectory.h" #include "PropertyLinkTestUtils.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderPass.h" - -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/EStandardModule.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" - -#include "impl/LogicEngineImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/LogicNodeImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/ApiObjects.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Node.h" +#include "ramses/client/Scene.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderPass.h" + +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/EStandardModule.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" + +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "internal/logic/ApiObjects.h" #include "fmt/format.h" #include -namespace ramses +namespace ramses::internal { class ALogicEngine_Linking : public ALogicEngine { protected: ALogicEngine_Linking() - : m_sourceScript(*m_logicEngine.createLuaScript(m_minimalLinkScript, {}, "SourceScript")) - , m_targetScript(*m_logicEngine.createLuaScript(m_minimalLinkScript, {}, "TargetScript")) + : m_sourceScript(*m_logicEngine->createLuaScript(m_minimalLinkScript, {}, "SourceScript")) + , m_targetScript(*m_logicEngine->createLuaScript(m_minimalLinkScript, {}, "TargetScript")) , m_sourceProperty(*m_sourceScript.getOutputs()->getChild("source")) , m_targetProperty(*m_targetScript.getInputs()->getChild("target")) { @@ -76,7 +77,7 @@ namespace ramses LuaScript& m_sourceScript; LuaScript& m_targetScript; - const Property& m_sourceProperty; + Property& m_sourceProperty; Property& m_targetProperty; }; @@ -109,32 +110,28 @@ namespace ramses end )", std::get<1>(errorCase), std::get<0>(errorCase)); - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource); const auto sourceProperty = sourceScript->getOutputs()->getChild("outParam"); const auto targetProperty = targetScript->getInputs()->getChild("inParam"); - EXPECT_FALSE(m_logicEngine.link( + EXPECT_FALSE(m_logicEngine->link( *sourceProperty, *targetProperty )); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, std::get<2>(errorCase)); + EXPECT_EQ(getLastErrorMessage(), std::get<2>(errorCase)); } } TEST_F(ALogicEngine_Linking, ProducesErrorIfLogicNodeIsLinkedToItself) { - const auto targetPropertyFromTheSameScript = m_sourceScript.getInputs()->getChild("target"); + auto targetPropertyFromTheSameScript = m_sourceScript.getInputs()->getChild("target"); - EXPECT_FALSE(m_logicEngine.link(m_sourceProperty, *targetPropertyFromTheSameScript)); + EXPECT_FALSE(m_logicEngine->link(m_sourceProperty, *targetPropertyFromTheSameScript)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "Link source and target can't belong to the same node! ('SourceScript')"); + EXPECT_EQ(getLastErrorMessage(), "Link source and target can't belong to the same node! ('SourceScript')"); } TEST_F(ALogicEngine_Linking, ProducesErrorIfInputIsLinkedToOutput) @@ -142,10 +139,8 @@ namespace ramses const auto sourceProperty = m_sourceScript.getOutputs()->getChild("source"); const auto targetProperty = m_targetScript.getInputs()->getChild("target"); - EXPECT_FALSE(m_logicEngine.link(*targetProperty, *sourceProperty)); - const auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Failed to link input property 'target' to output property 'source'. Only outputs can be linked to inputs", errors[0].message); + EXPECT_FALSE(m_logicEngine->link(*targetProperty, *sourceProperty)); + EXPECT_EQ("Failed to link input property 'target' to output property 'source'. Only outputs can be linked to inputs", getLastErrorMessage()); } TEST_F(ALogicEngine_Linking, ProducesErrorIfInputIsLinkedToInput) @@ -153,76 +148,64 @@ namespace ramses const auto sourceInput = m_sourceScript.getInputs()->getChild("target"); const auto targetInput = m_targetScript.getInputs()->getChild("target"); - EXPECT_FALSE(m_logicEngine.link(*sourceInput, *targetInput)); - const auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Failed to link input property 'target' to input property 'target'. Only outputs can be linked to inputs", errors[0].message); + EXPECT_FALSE(m_logicEngine->link(*sourceInput, *targetInput)); + EXPECT_EQ("Failed to link input property 'target' to input property 'target'. Only outputs can be linked to inputs", getLastErrorMessage()); } TEST_F(ALogicEngine_Linking, ProducesErrorIfOutputIsLinkedToOutput) { - auto sourceScript = m_logicEngine.createLuaScript(m_linkScriptMultipleTypes); - auto targetScript = m_logicEngine.createLuaScript(m_linkScriptMultipleTypes); + auto sourceScript = m_logicEngine->createLuaScript(m_linkScriptMultipleTypes); + auto targetScript = m_logicEngine->createLuaScript(m_linkScriptMultipleTypes); const auto sourceOuput = sourceScript->getOutputs()->getChild("source_int"); const auto targetOutput = targetScript->getOutputs()->getChild("source_int"); - EXPECT_FALSE(m_logicEngine.link(*sourceOuput, *targetOutput)); + EXPECT_FALSE(m_logicEngine->link(*sourceOuput, *targetOutput)); - const auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Failed to link output property 'source_int' to output property 'source_int'. Only outputs can be linked to inputs", errors[0].message); + EXPECT_EQ("Failed to link output property 'source_int' to output property 'source_int'. Only outputs can be linked to inputs", getLastErrorMessage()); } TEST_F(ALogicEngine_Linking, ProducesNoErrorIfMatchingPropertiesAreLinked) { - EXPECT_TRUE(m_logicEngine.link(m_sourceProperty, m_targetProperty)); + EXPECT_TRUE(m_logicEngine->link(m_sourceProperty, m_targetProperty)); } TEST_F(ALogicEngine_Linking, ProducesErrorIfPropertyIsLinkedTwiceToSameProperty_LuaScript) { - EXPECT_TRUE(m_logicEngine.link(m_sourceProperty, m_targetProperty)); - EXPECT_FALSE(m_logicEngine.link(m_sourceProperty, m_targetProperty)); + EXPECT_TRUE(m_logicEngine->link(m_sourceProperty, m_targetProperty)); + EXPECT_FALSE(m_logicEngine->link(m_sourceProperty, m_targetProperty)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "The property 'target' of LogicNode 'TargetScript' is already linked (to property 'source' of LogicNode 'SourceScript')"); + EXPECT_EQ(getLastErrorMessage(), "The property 'target' of LogicNode 'TargetScript' is already linked (to property 'source' of LogicNode 'SourceScript')"); } TEST_F(ALogicEngine_Linking, ProducesErrorIfPropertyIsLinkedTwice_RamsesBinding) { - auto ramsesBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); + auto ramsesBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); const auto visibilityProperty = ramsesBinding->getInputs()->getChild("visibility"); - EXPECT_TRUE(m_logicEngine.link(m_sourceProperty, *visibilityProperty)); - EXPECT_FALSE(m_logicEngine.link(m_sourceProperty, *visibilityProperty)); + EXPECT_TRUE(m_logicEngine->link(m_sourceProperty, *visibilityProperty)); + EXPECT_FALSE(m_logicEngine->link(m_sourceProperty, *visibilityProperty)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "The property 'visibility' of LogicNode 'RamsesBinding' is already linked (to property 'source' of LogicNode 'SourceScript')"); + EXPECT_EQ(getLastErrorMessage(), "The property 'visibility' of LogicNode 'RamsesBinding' is already linked (to property 'source' of LogicNode 'SourceScript')"); } TEST_F(ALogicEngine_Linking, ProducesErrorIfNotLinkedPropertyIsUnlinked_LuaScript) { - EXPECT_FALSE(m_logicEngine.unlink(m_sourceProperty, m_targetProperty)); + EXPECT_FALSE(m_logicEngine->unlink(m_sourceProperty, m_targetProperty)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "Input property 'target' is not currently linked!"); + EXPECT_EQ(getLastErrorMessage(), "Input property 'target' is not currently linked!"); } - TEST_F(ALogicEngine_Linking, ProducesErrorIfNotLinkedPropertyIsUnlinked_RamsesNodeBinding) + TEST_F(ALogicEngine_Linking, ProducesErrorIfNotLinkedPropertyIsUnlinked_NodeBinding) { - auto ramsesBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); + auto ramsesBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); const auto visibilityProperty = ramsesBinding->getInputs()->getChild("visibility"); - EXPECT_FALSE(m_logicEngine.unlink(m_sourceProperty, *visibilityProperty)); + EXPECT_FALSE(m_logicEngine->unlink(m_sourceProperty, *visibilityProperty)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "Input property 'visibility' is not currently linked!"); + EXPECT_EQ(getLastErrorMessage(), "Input property 'visibility' is not currently linked!"); } TEST_F(ALogicEngine_Linking, ProducesNoErrorIfLinkedToMatchingType) @@ -246,8 +229,8 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource); const auto outputs = sourceScript->getOutputs(); const auto inputs = targetScript->getInputs(); @@ -266,37 +249,33 @@ namespace ramses auto vec2Source = outputs->getChild("vec2Source"); auto vec3Source = outputs->getChild("vec3Source"); - EXPECT_TRUE(m_logicEngine.link(*boolSource, *boolTarget)); - EXPECT_TRUE(m_logicEngine.link(*intSource, *intTarget)); - EXPECT_TRUE(m_logicEngine.link(*int64Source, *int64Target)); - EXPECT_TRUE(m_logicEngine.link(*floatSource, *floatTarget)); - EXPECT_TRUE(m_logicEngine.link(*vec2Source, *vec2Target)); - EXPECT_TRUE(m_logicEngine.link(*vec3Source, *vec3Target)); + EXPECT_TRUE(m_logicEngine->link(*boolSource, *boolTarget)); + EXPECT_TRUE(m_logicEngine->link(*intSource, *intTarget)); + EXPECT_TRUE(m_logicEngine->link(*int64Source, *int64Target)); + EXPECT_TRUE(m_logicEngine->link(*floatSource, *floatTarget)); + EXPECT_TRUE(m_logicEngine->link(*vec2Source, *vec2Target)); + EXPECT_TRUE(m_logicEngine->link(*vec3Source, *vec3Target)); } TEST_F(ALogicEngine_Linking, ProducesErrorOnNextUpdateIfLinkCycleWasCreated) { - LuaScript& loopScript = *m_logicEngine.createLuaScript(m_minimalLinkScript); - const Property* sourceInput = m_sourceScript.getInputs()->getChild("target"); - const Property* sourceOutput = m_sourceScript.getOutputs()->getChild("source"); - const Property* targetInput = m_targetScript.getInputs()->getChild("target"); - const Property* targetOutput = m_targetScript.getOutputs()->getChild("source"); - const Property* loopInput = loopScript.getInputs()->getChild("target"); - const Property* loopOutput = loopScript.getOutputs()->getChild("source"); - - EXPECT_TRUE(m_logicEngine.link(*sourceOutput, *targetInput)); - EXPECT_TRUE(m_logicEngine.link(*targetOutput, *loopInput)); - EXPECT_TRUE(m_logicEngine.link(*loopOutput, *sourceInput)); - EXPECT_FALSE(m_logicEngine.update()); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling update()!", errors[0].message); + LuaScript& loopScript = *m_logicEngine->createLuaScript(m_minimalLinkScript); + Property* sourceInput = m_sourceScript.getInputs()->getChild("target"); + Property* sourceOutput = m_sourceScript.getOutputs()->getChild("source"); + Property* targetInput = m_targetScript.getInputs()->getChild("target"); + Property* targetOutput = m_targetScript.getOutputs()->getChild("source"); + Property* loopInput = loopScript.getInputs()->getChild("target"); + Property* loopOutput = loopScript.getOutputs()->getChild("source"); + + EXPECT_TRUE(m_logicEngine->link(*sourceOutput, *targetInput)); + EXPECT_TRUE(m_logicEngine->link(*targetOutput, *loopInput)); + EXPECT_TRUE(m_logicEngine->link(*loopOutput, *sourceInput)); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_EQ("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling update()!", getLastErrorMessage()); // Also refuse to save to file - EXPECT_FALSE(m_logicEngine.saveToFile("will_not_write")); - errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling saveToFile()!", errors[0].message); + EXPECT_FALSE(saveToFileWithoutValidation("will_not_write")); + EXPECT_EQ("Failed to sort logic nodes based on links between their properties. Create a loop-free link graph before calling saveToFile()!", getLastErrorMessage()); } TEST_F(ALogicEngine_Linking, PropagatesValuesAcrossMultipleLinksInAChain) @@ -312,9 +291,9 @@ namespace ramses end )"; - auto script1 = m_logicEngine.createLuaScript(scriptSource); - auto script2 = m_logicEngine.createLuaScript(scriptSource); - auto script3 = m_logicEngine.createLuaScript(scriptSource); + auto script1 = m_logicEngine->createLuaScript(scriptSource); + auto script2 = m_logicEngine->createLuaScript(scriptSource); + auto script3 = m_logicEngine->createLuaScript(scriptSource); auto script1Input2 = script1->getInputs()->getChild("inString2"); auto script2Input1 = script2->getInputs()->getChild("inString1"); @@ -325,14 +304,14 @@ namespace ramses auto script2Output = script2->getOutputs()->getChild("outString"); auto script3Output = script3->getOutputs()->getChild("outString"); - m_logicEngine.link(*script1Output, *script2Input1); - m_logicEngine.link(*script2Output, *script3Input1); + m_logicEngine->link(*script1Output, *script2Input1); + m_logicEngine->link(*script2Output, *script3Input1); script1Input2->set(std::string("Script1")); script2Input2->set(std::string("Script2")); script3Input2->set(std::string("Script3")); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ("Script1Script2Script3", script3Output->get()); } @@ -356,8 +335,8 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource); const auto outputs = sourceScript->getOutputs(); const auto inputs = targetScript->getInputs(); @@ -365,15 +344,11 @@ namespace ramses auto structTarget = inputs->getChild("structTarget"); auto structSource = outputs->getChild("structSource"); - EXPECT_FALSE(m_logicEngine.link(*structSource, *structTarget)); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Can't link properties of complex types directly, currently only primitive properties can be linked", errors[0].message); + EXPECT_FALSE(m_logicEngine->link(*structSource, *structTarget)); + EXPECT_EQ("Can't link properties of complex types directly, currently only primitive properties can be linked", getLastErrorMessage()); - EXPECT_FALSE(m_logicEngine.link(*outputs, *inputs)); - errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Can't link properties of complex types directly, currently only primitive properties can be linked", errors[0].message); + EXPECT_FALSE(m_logicEngine->link(*outputs, *inputs)); + EXPECT_EQ("Can't link properties of complex types directly, currently only primitive properties can be linked", getLastErrorMessage()); } TEST_F(ALogicEngine_Linking, ProducesErrorOnLinkingArrays) @@ -387,16 +362,14 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource); auto arrayTarget = targetScript->getInputs()->getChild("array"); auto arraySource = sourceScript->getOutputs()->getChild("array"); - EXPECT_FALSE(m_logicEngine.link(*arraySource, *arrayTarget)); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("Can't link properties of complex types directly, currently only primitive properties can be linked", errors[0].message); + EXPECT_FALSE(m_logicEngine->link(*arraySource, *arrayTarget)); + EXPECT_EQ("Can't link properties of complex types directly, currently only primitive properties can be linked", getLastErrorMessage()); } TEST_F(ALogicEngine_Linking, ProducesErrorIfNotLinkedPropertyIsUnlinked_WhenAnotherLinkFromTheSameScriptExists) @@ -411,45 +384,41 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource); const auto sourceProperty = sourceScript->getOutputs()->getChild("intSource"); const auto targetProperty1 = targetScript->getInputs()->getChild("intTarget1"); const auto targetProperty2 = targetScript->getInputs()->getChild("intTarget2"); - EXPECT_TRUE(m_logicEngine.link(*sourceProperty, *targetProperty1)); + EXPECT_TRUE(m_logicEngine->link(*sourceProperty, *targetProperty1)); - EXPECT_FALSE(m_logicEngine.unlink(*sourceProperty, *targetProperty2)); + EXPECT_FALSE(m_logicEngine->unlink(*sourceProperty, *targetProperty2)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "Input property 'intTarget2' is not currently linked!"); + EXPECT_EQ(getLastErrorMessage(), "Input property 'intTarget2' is not currently linked!"); } TEST_F(ALogicEngine_Linking, ProducesErrorIfNotLinkedPropertyIsUnlinked_RamsesBinding) { - auto targetBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto targetBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); const auto visibilityProperty = targetBinding->getInputs()->getChild("visibility"); const auto unlinkedTargetProperty = targetBinding->getInputs()->getChild("translation"); - EXPECT_TRUE(m_logicEngine.link(m_sourceProperty, *visibilityProperty)); + EXPECT_TRUE(m_logicEngine->link(m_sourceProperty, *visibilityProperty)); - EXPECT_FALSE(m_logicEngine.unlink(m_sourceProperty, *unlinkedTargetProperty)); + EXPECT_FALSE(m_logicEngine->unlink(m_sourceProperty, *unlinkedTargetProperty)); - const auto& errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ(errors[0].message, "Input property 'translation' is not currently linked!"); + EXPECT_EQ(getLastErrorMessage(), "Input property 'translation' is not currently linked!"); } TEST_F(ALogicEngine_Linking, UnlinksPropertiesWhichAreLinked) { - ASSERT_TRUE(m_logicEngine.link( + ASSERT_TRUE(m_logicEngine->link( m_sourceProperty, m_targetProperty )); - EXPECT_TRUE(m_logicEngine.unlink( + EXPECT_TRUE(m_logicEngine->unlink( m_sourceProperty, m_targetProperty )); @@ -457,26 +426,26 @@ namespace ramses TEST_F(ALogicEngine_Linking, ProducesNoErrorsIfMultipleLinksFromSameSourceAreUnlinked) { - auto targetScript2 = m_logicEngine.createLuaScript(m_minimalLinkScript); + auto targetScript2 = m_logicEngine->createLuaScript(m_minimalLinkScript); const auto targetProperty2 = targetScript2->getInputs()->getChild("target"); - m_logicEngine.link( + m_logicEngine->link( m_sourceProperty, m_targetProperty ); - m_logicEngine.link( + m_logicEngine->link( m_sourceProperty, *targetProperty2 ); - EXPECT_TRUE(m_logicEngine.unlink( + EXPECT_TRUE(m_logicEngine->unlink( m_sourceProperty, m_targetProperty )); - EXPECT_TRUE(m_logicEngine.unlink( + EXPECT_TRUE(m_logicEngine->unlink( m_sourceProperty, *targetProperty2 )); @@ -484,17 +453,17 @@ namespace ramses TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinked) { - auto sourceScript = m_logicEngine.createLuaScript(m_linkScriptMultipleTypes); - auto targetScript = m_logicEngine.createLuaScript(m_linkScriptMultipleTypes); + auto sourceScript = m_logicEngine->createLuaScript(m_linkScriptMultipleTypes); + auto targetScript = m_logicEngine->createLuaScript(m_linkScriptMultipleTypes); auto output = sourceScript->getOutputs()->getChild("source_int"); auto input = targetScript->getInputs()->getChild("target_int"); - EXPECT_TRUE(m_logicEngine.link(*output, *input)); + EXPECT_TRUE(m_logicEngine->link(*output, *input)); sourceScript->getInputs()->getChild("target_int")->set(42); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(42, *targetScript->getOutputs()->getChild("source_int")->get()); } @@ -511,18 +480,18 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSrc); - auto targetScript = m_logicEngine.createLuaScript(scriptSrc); + auto sourceScript = m_logicEngine->createLuaScript(scriptSrc); + auto targetScript = m_logicEngine->createLuaScript(scriptSrc); auto output = sourceScript->getOutputs()->getChild("data"); auto input = targetScript->getInputs()->getChild("data"); - EXPECT_TRUE(m_logicEngine.link(*output, *input)); + EXPECT_TRUE(m_logicEngine->link(*output, *input)); const int64_t value = static_cast(std::numeric_limits::max()) + 1; sourceScript->getInputs()->getChild("data")->set(value); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(value, *targetScript->getOutputs()->getChild("data")->get()); } @@ -549,17 +518,17 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptArrayOfStructs); - auto targetScript = m_logicEngine.createLuaScript(scriptArrayOfStructs); + auto sourceScript = m_logicEngine->createLuaScript(scriptArrayOfStructs); + auto targetScript = m_logicEngine->createLuaScript(scriptArrayOfStructs); auto output = sourceScript->getOutputs()->getChild("data")->getChild(1)->getChild("one"); auto input = targetScript->getInputs()->getChild("data")->getChild(1)->getChild("two"); - EXPECT_TRUE(m_logicEngine.link(*output, *input)); + EXPECT_TRUE(m_logicEngine->link(*output, *input)); sourceScript->getInputs()->getChild("data")->getChild(1)->getChild("one")->set(42); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(42, *targetScript->getOutputs()->getChild("data")->getChild(1)->getChild("two")->get()); } @@ -584,17 +553,17 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptArrayOfStructs); - auto targetScript = m_logicEngine.createLuaScript(scriptArrayOfStructs); + auto sourceScript = m_logicEngine->createLuaScript(scriptArrayOfStructs); + auto targetScript = m_logicEngine->createLuaScript(scriptArrayOfStructs); auto output = sourceScript->getOutputs()->getChild("data")->getChild("one")->getChild(1); auto input = targetScript->getInputs()->getChild("data")->getChild("two")->getChild(1); - EXPECT_TRUE(m_logicEngine.link(*output, *input)); + EXPECT_TRUE(m_logicEngine->link(*output, *input)); sourceScript->getInputs()->getChild("data")->getChild("one")->getChild(1)->set(42); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(42, *targetScript->getOutputs()->getChild("data")->getChild("two")->getChild(1)->get()); } @@ -611,21 +580,21 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource); auto output = sourceScript->getOutputs()->getChild("intSource"); auto input = targetScript->getInputs()->getChild("intTarget"); - EXPECT_TRUE(m_logicEngine.link(*output, *input)); + EXPECT_TRUE(m_logicEngine->link(*output, *input)); sourceScript->getInputs()->getChild("intTarget")->set(42); - EXPECT_TRUE(m_logicEngine.unlink( + EXPECT_TRUE(m_logicEngine->unlink( *output, *input )); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(0, *targetScript->getOutputs()->getChild("intSource")->get()); } @@ -651,25 +620,25 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource1); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource2); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource1); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource2); auto output = sourceScript->getOutputs()->getChild("intSource"); auto input1 = targetScript->getInputs()->getChild("intTarget1"); auto input2 = targetScript->getInputs()->getChild("intTarget2"); - EXPECT_TRUE(m_logicEngine.link(*output, *input1)); - EXPECT_TRUE(m_logicEngine.link(*output, *input2)); + EXPECT_TRUE(m_logicEngine->link(*output, *input1)); + EXPECT_TRUE(m_logicEngine->link(*output, *input2)); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(5, *targetScript->getInputs()->getChild("intTarget1")->get()); EXPECT_EQ(5, *targetScript->getInputs()->getChild("intTarget2")->get()); - EXPECT_TRUE(m_logicEngine.unlink(*output, *input1)); + EXPECT_TRUE(m_logicEngine->unlink(*output, *input1)); input1->set(6); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(6, *input1->get()); EXPECT_EQ(5, *input2->get()); @@ -694,25 +663,25 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource1); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource2); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource1); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource2); auto sourceOutput = sourceScript->getOutputs()->getChild("output"); auto targetInput = targetScript->getInputs()->getChild("input"); // Set target value targetInput->set(100); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); - m_logicEngine.link(*sourceOutput, *targetInput); - m_logicEngine.unlink(*sourceOutput, *targetInput); - m_logicEngine.update(); + m_logicEngine->link(*sourceOutput, *targetInput); + m_logicEngine->unlink(*sourceOutput, *targetInput); + m_logicEngine->update(); // Value not overwritten by 5 from the link EXPECT_EQ(100, *targetInput->get()); } - TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForRamsesAppearanceBindings) + TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForAppearanceBindings) { const auto luaScriptSource = R"( function interface(IN,OUT) @@ -724,26 +693,26 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "TargetBinding"); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetBinding = m_logicEngine->createAppearanceBinding(*m_appearance, "TargetBinding"); auto sourceInput = sourceScript->getInputs()->getChild("floatInput"); auto sourceOutput = sourceScript->getOutputs()->getChild("floatOutput"); auto targetInput = targetBinding->getInputs()->getChild("floatUniform"); - m_logicEngine.link(*sourceOutput, *targetInput); + m_logicEngine->link(*sourceOutput, *targetInput); sourceInput->set(47.11f); - m_logicEngine.update(); + m_logicEngine->update(); - ramses::UniformInput floatUniform; - m_appearance->getEffect().findUniformInput("floatUniform", floatUniform); + const auto floatUniform = m_appearance->getEffect().findUniformInput("floatUniform"); + ASSERT_TRUE(floatUniform.has_value()); float result = 0.0f; - m_appearance->getInputValue(floatUniform, result); + m_appearance->getInputValue(*floatUniform, result); EXPECT_FLOAT_EQ(47.11f, result); } - TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForRamsesCameraBindings) + TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForCameraBindings) { const auto luaScriptSource = R"( function interface(IN,OUT) @@ -755,22 +724,22 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "TargetBinding"); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetBinding = m_logicEngine->createCameraBinding(*m_camera, "TargetBinding"); auto sourceInput = sourceScript->getInputs()->getChild("floatInput"); auto sourceOutput = sourceScript->getOutputs()->getChild("floatOutput"); auto targetInput = targetBinding->getInputs()->getChild("frustum")->getChild("farPlane"); - m_logicEngine.link(*sourceOutput, *targetInput); + m_logicEngine->link(*sourceOutput, *targetInput); sourceInput->set(47.11f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(47.11f, m_camera->getFarPlane()); } - TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForRamsesRenderPassBindings) + TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForRenderPassBindings) { const auto luaScriptSource = R"( function interface(IN,OUT) @@ -782,22 +751,22 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); - auto targetBinding = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "TargetBinding"); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); + auto targetBinding = m_logicEngine->createRenderPassBinding(*m_renderPass, "TargetBinding"); auto sourceInput = sourceScript->getInputs()->getChild("val"); auto sourceOutput = sourceScript->getOutputs()->getChild("val"); auto targetInput = targetBinding->getInputs()->getChild("renderOrder"); - m_logicEngine.link(*sourceOutput, *targetInput); + m_logicEngine->link(*sourceOutput, *targetInput); sourceInput->set(-11); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(-11, m_renderPass->getRenderOrder()); } - TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForRamsesRenderGroupBindings) + TEST_F(ALogicEngine_Linking, PropagatesOutputsToInputsIfLinkedForRenderGroupBindings) { const auto luaScriptSource = R"( function interface(IN,OUT) @@ -809,17 +778,17 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource); auto targetBinding = createRenderGroupBinding(); auto sourceInput = sourceScript->getInputs()->getChild("val"); auto sourceOutput = sourceScript->getOutputs()->getChild("val"); auto targetInput = targetBinding->getInputs()->getChild("renderOrders")->getChild("mesh"); - m_logicEngine.link(*sourceOutput, *targetInput); + m_logicEngine->link(*sourceOutput, *targetInput); sourceInput->set(-11); - m_logicEngine.update(); + m_logicEngine->update(); int32_t actualRenderOrder = 0; m_renderGroup->getMeshNodeOrder(*m_meshNode, actualRenderOrder); @@ -845,18 +814,18 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource1); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource2); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource1); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource2); auto sourceOutput = sourceScript->getOutputs()->getChild("output"); auto targetInput = targetScript->getInputs()->getChild("input"); // Execute run -> sets output to 5 - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); ASSERT_EQ(5, *sourceOutput->get()); - ASSERT_TRUE(m_logicEngine.link(*sourceOutput, *targetInput)); - m_logicEngine.update(); + ASSERT_TRUE(m_logicEngine->link(*sourceOutput, *targetInput)); + m_logicEngine->update(); EXPECT_EQ(5, *targetInput->get()); } @@ -883,8 +852,8 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource1); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource2); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource1); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource2); auto setOutput = sourceScript->getInputs()->getChild("setOutput"); setOutput->set(true); @@ -892,13 +861,13 @@ namespace ramses auto targetInput = targetScript->getInputs()->getChild("input"); // Execute run -> sets output to 5 - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); ASSERT_EQ(5, *sourceOutput->get()); // Disable output writing and add link setOutput->set(false); - ASSERT_TRUE(m_logicEngine.link(*sourceOutput, *targetInput)); - m_logicEngine.update(); + ASSERT_TRUE(m_logicEngine->link(*sourceOutput, *targetInput)); + m_logicEngine->update(); EXPECT_EQ(5, *targetInput->get()); } @@ -922,25 +891,25 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource1); - auto targetScript = m_logicEngine.createLuaScript(luaScriptSource2); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource1); + auto targetScript = m_logicEngine->createLuaScript(luaScriptSource2); auto sourceOutput = sourceScript->getOutputs()->getChild("output"); auto targetInput = targetScript->getInputs()->getChild("input"); targetInput->set(100); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); ASSERT_EQ(5, *sourceOutput->get()); ASSERT_EQ(100, *targetInput->get()); - m_logicEngine.link(*sourceOutput, *targetInput); - m_logicEngine.update(); + m_logicEngine->link(*sourceOutput, *targetInput); + m_logicEngine->update(); EXPECT_EQ(5, *targetInput->get()); - m_logicEngine.unlink(*sourceOutput, *targetInput); - m_logicEngine.update(); + m_logicEngine->unlink(*sourceOutput, *targetInput); + m_logicEngine->update(); // Value was overwritten after link + update EXPECT_EQ(5, *targetInput->get()); @@ -948,7 +917,7 @@ namespace ramses TEST_F(ALogicEngine_Linking, ProducesErrorIfLinkIsCreatedBetweenDifferentLogicEngines) { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& otherLogicEngine = *m_scene->createLogicEngine(); const auto luaScriptSource = R"( function interface(IN,OUT) IN.floatInput = Type:Float() @@ -959,24 +928,20 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(luaScriptSource, {}, "SourceScript"); + auto sourceScript = m_logicEngine->createLuaScript(luaScriptSource, {}, "SourceScript"); auto targetScript = otherLogicEngine.createLuaScript(luaScriptSource, {}, "TargetScript"); const auto sourceOutput = sourceScript->getOutputs()->getChild("floatOutput"); const auto targetInput = targetScript->getInputs()->getChild("floatInput"); - EXPECT_FALSE(m_logicEngine.link(*sourceOutput, *targetInput)); + EXPECT_FALSE(m_logicEngine->link(*sourceOutput, *targetInput)); { - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("LogicNode 'TargetScript' is not an instance of this LogicEngine", errors[0].message); + EXPECT_EQ("LogicNode 'TargetScript' is not an instance of this LogicEngine", getLastErrorMessage()); } EXPECT_FALSE(otherLogicEngine.link(*sourceOutput, *targetInput)); { - auto errors = otherLogicEngine.getErrors(); - ASSERT_EQ(1u, errors.size()); - EXPECT_EQ("LogicNode 'SourceScript' is not an instance of this LogicEngine", errors[0].message); + EXPECT_EQ("LogicNode 'SourceScript' is not an instance of this LogicEngine", getLastErrorMessage()); } } @@ -1004,9 +969,9 @@ namespace ramses end )"; - auto scriptA = m_logicEngine.createLuaScript(sourceScript); - auto scriptB = m_logicEngine.createLuaScript(sourceScript); - auto scriptC = m_logicEngine.createLuaScript(targetScript); + auto scriptA = m_logicEngine->createLuaScript(sourceScript); + auto scriptB = m_logicEngine->createLuaScript(sourceScript); + auto scriptC = m_logicEngine->createLuaScript(targetScript); auto inputA = scriptA->getInputs()->getChild("floatInput"); auto outputA = scriptA->getOutputs()->getChild("floatOutput"); @@ -1018,13 +983,13 @@ namespace ramses auto outputC1 = scriptC->getOutputs()->getChild("floatOutput1"); auto outputC2 = scriptC->getOutputs()->getChild("floatOutput2"); - m_logicEngine.link(*outputA, *inputC1); - m_logicEngine.link(*outputB, *inputC2); + m_logicEngine->link(*outputA, *inputC1); + m_logicEngine->link(*outputB, *inputC2); inputA->set(42.f); inputB->set(24.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *outputC1->get()); EXPECT_FLOAT_EQ(24.f, *outputC2->get()); @@ -1042,9 +1007,9 @@ namespace ramses end )"; - auto scriptA = m_logicEngine.createLuaScript(scriptSource); - auto scriptB = m_logicEngine.createLuaScript(scriptSource); - auto scriptC = m_logicEngine.createLuaScript(scriptSource); + auto scriptA = m_logicEngine->createLuaScript(scriptSource); + auto scriptB = m_logicEngine->createLuaScript(scriptSource); + auto scriptC = m_logicEngine->createLuaScript(scriptSource); auto inputA = scriptA->getInputs()->getChild("floatInput"); auto outputA = scriptA->getOutputs()->getChild("floatOutput"); @@ -1053,12 +1018,12 @@ namespace ramses auto inputC = scriptC->getInputs()->getChild("floatInput"); auto outputC = scriptC->getOutputs()->getChild("floatOutput"); - m_logicEngine.link(*outputA, *inputB); - m_logicEngine.link(*outputA, *inputC); + m_logicEngine->link(*outputA, *inputB); + m_logicEngine->link(*outputA, *inputC); inputA->set(42.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *outputB->get()); EXPECT_FLOAT_EQ(42.f, *outputC->get()); @@ -1088,9 +1053,9 @@ namespace ramses end )"; - auto scriptA = m_logicEngine.createLuaScript(sourceScript); - auto scriptB = m_logicEngine.createLuaScript(targetScript); - auto scriptC = m_logicEngine.createLuaScript(targetScript); + auto scriptA = m_logicEngine->createLuaScript(sourceScript); + auto scriptB = m_logicEngine->createLuaScript(targetScript); + auto scriptC = m_logicEngine->createLuaScript(targetScript); auto inputA = scriptA->getInputs()->getChild("floatInput"); auto outputA = scriptA->getOutputs()->getChild("floatOutput"); @@ -1104,14 +1069,14 @@ namespace ramses auto outputC1 = scriptC->getOutputs()->getChild("floatOutput1"); auto outputC2 = scriptC->getOutputs()->getChild("floatOutput2"); - m_logicEngine.link(*outputA, *inputB1); - m_logicEngine.link(*outputA, *inputB2); - m_logicEngine.link(*outputA, *inputC1); - m_logicEngine.link(*outputA, *inputC2); + m_logicEngine->link(*outputA, *inputB1); + m_logicEngine->link(*outputA, *inputB2); + m_logicEngine->link(*outputA, *inputC1); + m_logicEngine->link(*outputA, *inputC2); inputA->set(42.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *outputB1->get()); EXPECT_FLOAT_EQ(42.f, *outputB2->get()); @@ -1131,9 +1096,9 @@ namespace ramses end )"; - auto scriptA = m_logicEngine.createLuaScript(scriptSource); - auto scriptB = m_logicEngine.createLuaScript(scriptSource); - auto scriptC = m_logicEngine.createLuaScript(scriptSource); + auto scriptA = m_logicEngine->createLuaScript(scriptSource); + auto scriptB = m_logicEngine->createLuaScript(scriptSource); + auto scriptC = m_logicEngine->createLuaScript(scriptSource); auto inputA = scriptA->getInputs()->getChild("floatInput"); auto outputA = scriptA->getOutputs()->getChild("floatOutput"); @@ -1142,14 +1107,14 @@ namespace ramses auto inputC = scriptC->getInputs()->getChild("floatInput"); auto outputC = scriptC->getOutputs()->getChild("floatOutput"); - m_logicEngine.link(*outputA, *inputB); - m_logicEngine.link(*outputB, *inputC); + m_logicEngine->link(*outputA, *inputB); + m_logicEngine->link(*outputB, *inputC); - m_logicEngine.destroy(*scriptB); + m_logicEngine->destroy(*scriptB); inputA->set(42.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *outputA->get()); EXPECT_FLOAT_EQ(0.f, *inputC->get()); @@ -1186,8 +1151,8 @@ namespace ramses )"; // Create scripts in reversed order to make it more likely that order will be wrong unless ordered by dependencies - auto scriptB = m_logicEngine.createLuaScript(srcScriptB); - auto scriptA = m_logicEngine.createLuaScript(srcScriptA); + auto scriptB = m_logicEngine->createLuaScript(srcScriptB); + auto scriptA = m_logicEngine->createLuaScript(srcScriptA); auto scriptAOutput = scriptA->getOutputs()->getChild("output"); auto scriptAnested_str1 = scriptA->getOutputs()->getChild("nested")->getChild("str1"); @@ -1198,11 +1163,11 @@ namespace ramses auto scriptBnested_str2 = scriptB->getInputs()->getChild("nested")->getChild("str2"); // Do a crossover link between nested property and non-nested property - EXPECT_TRUE(m_logicEngine.link(*scriptAOutput, *scriptBnested_str1)); - EXPECT_TRUE(m_logicEngine.link(*scriptAnested_str1, *scriptBInput)); - EXPECT_TRUE(m_logicEngine.link(*scriptAnested_str2, *scriptBnested_str2)); + EXPECT_TRUE(m_logicEngine->link(*scriptAOutput, *scriptBnested_str1)); + EXPECT_TRUE(m_logicEngine->link(*scriptAnested_str1, *scriptBInput)); + EXPECT_TRUE(m_logicEngine->link(*scriptAnested_str2, *scriptBnested_str2)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto scriptB_concatenated = scriptB->getOutputs()->getChild("concat_all"); EXPECT_EQ(std::string("str1 {foo, str2}"), *scriptB_concatenated->get()); @@ -1222,9 +1187,9 @@ namespace ramses end )"; - auto script = m_logicEngine.createLuaScript(scriptSrc); + auto script = m_logicEngine->createLuaScript(scriptSrc); // TODO Violin add appearance binding here too, once test PR #305 is merged - auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); auto nestedOutput_bool = script->getOutputs()->getChild("nested")->getChild("bool"); auto nestedOutput_vec3f = script->getOutputs()->getChild("nested")->getChild("vec3f"); @@ -1232,10 +1197,10 @@ namespace ramses auto nodeBindingInput_bool = nodeBinding->getInputs()->getChild("visibility"); auto nodeBindingInput_vec3f = nodeBinding->getInputs()->getChild("translation"); - ASSERT_TRUE(m_logicEngine.link(*nestedOutput_bool, *nodeBindingInput_bool)); - ASSERT_TRUE(m_logicEngine.link(*nestedOutput_vec3f, *nodeBindingInput_vec3f)); + ASSERT_TRUE(m_logicEngine->link(*nestedOutput_bool, *nodeBindingInput_bool)); + ASSERT_TRUE(m_logicEngine->link(*nestedOutput_vec3f, *nodeBindingInput_vec3f)); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(false, *nodeBindingInput_bool->get()); EXPECT_EQ(*nodeBindingInput_vec3f->get(), vec3f(0.1f, 0.2f, 0.3f)); @@ -1271,9 +1236,9 @@ namespace ramses end )"; - auto scriptA = m_logicEngine.createLuaScript(sourceScript); - auto scriptB = m_logicEngine.createLuaScript(sourceScript); - auto scriptC = m_logicEngine.createLuaScript(targetScript); + auto scriptA = m_logicEngine->createLuaScript(sourceScript); + auto scriptB = m_logicEngine->createLuaScript(sourceScript); + auto scriptC = m_logicEngine->createLuaScript(targetScript); auto scriptAInput = scriptA->getInputs()->getChild("floatInput"); auto scriptAOutput = scriptA->getOutputs()->getChild("floatOutput"); @@ -1286,13 +1251,13 @@ namespace ramses auto scriptCOutput1 = scriptC->getOutputs()->getChild("floatOutput1"); auto scriptCOutput2 = scriptC->getOutputs()->getChild("floatOutput2"); - m_logicEngine.link(*scriptAOutput, *scriptBInput); - m_logicEngine.link(*scriptAOutput, *scriptCInput1); - m_logicEngine.link(*scriptBOutput, *scriptCInput2); + m_logicEngine->link(*scriptAOutput, *scriptBInput); + m_logicEngine->link(*scriptAOutput, *scriptCInput1); + m_logicEngine->link(*scriptBOutput, *scriptCInput2); scriptAInput->set(42.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *scriptCOutput1->get()); EXPECT_FLOAT_EQ(42.f, *scriptCOutput2->get()); @@ -1302,11 +1267,11 @@ namespace ramses * \ * ScriptA ----------->ScriptC */ - m_logicEngine.unlink(*scriptAOutput, *scriptBInput); + m_logicEngine->unlink(*scriptAOutput, *scriptBInput); scriptBInput->set(23.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *scriptCOutput1->get()); EXPECT_FLOAT_EQ(23.f, *scriptCOutput2->get()); @@ -1314,9 +1279,9 @@ namespace ramses TEST_F(ALogicEngine_Linking, canDestroyBindingAfterUnlinkingFromScript) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - auto* outScript = m_logicEngine.createLuaScript(R"( + auto* outScript = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.out_vec3f = Type:Vec3f() end @@ -1326,29 +1291,29 @@ namespace ramses end )"); - const Property* nodeTranslation = nodeBinding.getInputs()->getChild("translation"); - const Property* scriptOutVec3f = outScript->getOutputs()->getChild("out_vec3f"); + Property* nodeTranslation = nodeBinding.getInputs()->getChild("translation"); + Property* scriptOutVec3f = outScript->getOutputs()->getChild("out_vec3f"); - EXPECT_TRUE(m_logicEngine.link(*scriptOutVec3f, *nodeTranslation)); - EXPECT_TRUE(m_logicEngine.unlink(*scriptOutVec3f, *nodeTranslation)); + EXPECT_TRUE(m_logicEngine->link(*scriptOutVec3f, *nodeTranslation)); + EXPECT_TRUE(m_logicEngine->unlink(*scriptOutVec3f, *nodeTranslation)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(outScript->m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(outScript->m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->isLinked(*outScript)); - EXPECT_FALSE(m_logicEngine.m_impl->isLinked(nodeBinding)); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.impl())); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(outScript->impl())); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.impl())); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(outScript->impl())); + EXPECT_FALSE(m_logicEngine->impl().isLinked(*outScript)); + EXPECT_FALSE(m_logicEngine->impl().isLinked(nodeBinding)); - EXPECT_TRUE(m_logicEngine.destroy(nodeBinding)); + EXPECT_TRUE(m_logicEngine->destroy(nodeBinding)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALogicEngine_Linking, WHEN_ScriptWasUnlinkedFromBindingAndMultipleLinksDestroyed_THEN_UpdateDoesNotOverwriteBindingInputsByDanglingLinks) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - auto* outScript = m_logicEngine.createLuaScript(R"( + auto* outScript = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.translation = Type:Vec3f() OUT.visibility = Type:Bool() @@ -1358,35 +1323,34 @@ namespace ramses end )"); - ramses::Property* nodeTranslation = nodeBinding.getInputs()->getChild("translation"); - ramses::Property* nodeVisibility = nodeBinding.getInputs()->getChild("visibility"); - - const ramses::Property* scriptTranslation = outScript->getOutputs()->getChild("translation"); - const ramses::Property* scriptVisiblity = outScript->getOutputs()->getChild("visibility"); + Property* nodeTranslation = nodeBinding.getInputs()->getChild("translation"); + Property* nodeVisibility = nodeBinding.getInputs()->getChild("visibility"); + Property* scriptTranslation = outScript->getOutputs()->getChild("translation"); + Property* scriptVisiblity = outScript->getOutputs()->getChild("visibility"); - EXPECT_TRUE(m_logicEngine.link(*scriptVisiblity, *nodeVisibility)); - EXPECT_TRUE(m_logicEngine.link(*scriptTranslation, *nodeTranslation)); + EXPECT_TRUE(m_logicEngine->link(*scriptVisiblity, *nodeVisibility)); + EXPECT_TRUE(m_logicEngine->link(*scriptTranslation, *nodeTranslation)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); - EXPECT_TRUE(m_logicEngine.unlink(*scriptVisiblity, *nodeVisibility)); - EXPECT_TRUE(m_logicEngine.unlink(*scriptTranslation, *nodeTranslation)); + EXPECT_TRUE(m_logicEngine->unlink(*scriptVisiblity, *nodeVisibility)); + EXPECT_TRUE(m_logicEngine->unlink(*scriptTranslation, *nodeTranslation)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(outScript->m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->getApiObjects().getLogicNodeDependencies().isLinked(outScript->m_impl)); - EXPECT_FALSE(m_logicEngine.m_impl->isLinked(*outScript)); - EXPECT_FALSE(m_logicEngine.m_impl->isLinked(nodeBinding)); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.impl())); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(outScript->impl())); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(nodeBinding.impl())); + EXPECT_FALSE(m_logicEngine->impl().getApiObjects().getLogicNodeDependencies().isLinked(outScript->impl())); + EXPECT_FALSE(m_logicEngine->impl().isLinked(*outScript)); + EXPECT_FALSE(m_logicEngine->impl().isLinked(nodeBinding)); - EXPECT_TRUE(m_logicEngine.destroy(*outScript)); + EXPECT_TRUE(m_logicEngine->destroy(*outScript)); // Set some custom values after unlink const vec3f translationValues{ 11.f, 12.0f, 13.0f }; nodeTranslation->set(translationValues); nodeVisibility->set(false); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Check that custom values are kept EXPECT_EQ(*nodeTranslation->get(), translationValues); @@ -1406,9 +1370,9 @@ namespace ramses end )"; - auto script1 = m_logicEngine.createLuaScript(scriptSource); - auto script2 = m_logicEngine.createLuaScript(scriptSource); - auto script3 = m_logicEngine.createLuaScript(scriptSource); + auto script1 = m_logicEngine->createLuaScript(scriptSource); + auto script2 = m_logicEngine->createLuaScript(scriptSource); + auto script3 = m_logicEngine->createLuaScript(scriptSource); auto script1Input1 = script1->getInputs()->getChild("inString1"); auto script1Input2 = script1->getInputs()->getChild("inString2"); @@ -1420,30 +1384,33 @@ namespace ramses auto script2Output = script2->getOutputs()->getChild("outString"); auto script3Output = script3->getOutputs()->getChild("outString"); - m_logicEngine.link(*script1Output, *script2Input1); - m_logicEngine.link(*script2Output, *script3Input1); - m_logicEngine.linkWeak(*script3Output, *script1Input1); + m_logicEngine->link(*script1Output, *script2Input1); + m_logicEngine->link(*script2Output, *script3Input1); + m_logicEngine->linkWeak(*script3Output, *script1Input1); script1Input2->set(std::string("A")); script2Input2->set(std::string("B")); script3Input2->set(std::string("C")); // during 1st update the weak link has no value yet but will mark script1 dirty - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ("ABC", script3Output->get()); // every upcoming update will concatenate result from previous update with this update - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ("ABCABC", script3Output->get()); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ("ABCABCABC", script3Output->get()); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ("ABCABCABCABC", script3Output->get()); } class ALogicEngine_Linking_WithFiles : public ALogicEngine_Linking { - protected: - WithTempDirectory tempFolder; + public: + ALogicEngine_Linking_WithFiles() + { + withTempDirectory(); + } }; TEST_F(ALogicEngine_Linking_WithFiles, PreservesLinksBetweenScriptsAfterSavingAndLoading) @@ -1455,7 +1422,7 @@ namespace ramses * ScriptA ------------------> ScriptC */ - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; LuaConfig config; config.addStandardModuleDependency(EStandardModule::Base); const auto srcScriptAB = R"( @@ -1503,16 +1470,16 @@ namespace ramses ASSERT_EQ(std::string("A: forward 'From A' & B: forward forward 'From A'"), *scriptC_concatenate_AB->get()); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("links.bin")); } { - ASSERT_TRUE(m_logicEngine.loadFromFile("links.bin")); + ASSERT_TRUE(recreateFromFile("links.bin")); // Load all scripts and their properties - auto scriptC = m_logicEngine.findByName("ScriptC"); - auto scriptB = m_logicEngine.findByName("ScriptB"); - auto scriptA = m_logicEngine.findByName("ScriptA"); + auto scriptC = m_logicEngine->findObject("ScriptC"); + auto scriptB = m_logicEngine->findObject("ScriptB"); + auto scriptA = m_logicEngine->findObject("ScriptA"); auto scriptAInput = scriptA->getInputs()->getChild("input"); auto scriptAOutput = scriptA->getOutputs()->getChild("output"); @@ -1525,7 +1492,7 @@ namespace ramses auto scriptC_concatenate_AB = scriptC->getOutputs()->getChild("concatenate_AB"); // Internal check that deserialization did not result in more link copies - PropertyLinkTestUtils::ExpectLinks(m_logicEngine, { + PropertyLinkTestUtils::ExpectLinks(*m_logicEngine, { { scriptAOutput, scriptBInput, false }, { scriptAOutput, scriptC_fromA, true }, { scriptBOutput, scriptC_fromB, false } @@ -1536,7 +1503,7 @@ namespace ramses EXPECT_EQ(std::string("forward forward 'From A'"), *scriptBOutput->get()); EXPECT_EQ(std::string("A: forward 'From A' & B: forward forward 'From A'"), *scriptC_concatenate_AB->get()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Values should be still the same - because the data didn't change EXPECT_EQ(std::string("forward 'From A'"), *scriptAOutput->get()); @@ -1550,7 +1517,7 @@ namespace ramses EXPECT_FALSE(scriptC_fromA->set("yyy")); EXPECT_FALSE(scriptC_fromB->set("zzz")); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(std::string("forward 'A++'"), *scriptAOutput->get()); EXPECT_EQ(std::string("forward forward 'A++'"), *scriptBOutput->get()); @@ -1561,7 +1528,7 @@ namespace ramses TEST_F(ALogicEngine_Linking_WithFiles, PreservesNestedLinksBetweenScriptsAfterSavingAndLoading) { { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; const auto srcScriptA = R"( function interface(IN,OUT) IN.appendixNestedStr2 = Type:String() @@ -1612,15 +1579,15 @@ namespace ramses auto scriptB_concatenated = scriptB->getOutputs()->getChild("concat_all"); ASSERT_EQ(std::string("str1 {foo, str2}"), *scriptB_concatenated->get()); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "nested_links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("nested_links.bin")); } { - ASSERT_TRUE(m_logicEngine.loadFromFile("nested_links.bin")); + ASSERT_TRUE(recreateFromFile("nested_links.bin")); // Load all scripts and their properties - auto scriptA = m_logicEngine.findByName("ScriptA"); - auto scriptB = m_logicEngine.findByName("ScriptB"); + auto scriptA = m_logicEngine->findObject("ScriptA"); + auto scriptB = m_logicEngine->findObject("ScriptB"); auto scriptAOutput = scriptA->getOutputs()->getChild("output"); auto scriptAnested_str1 = scriptA->getOutputs()->getChild("nested")->getChild("str1"); @@ -1632,7 +1599,7 @@ namespace ramses auto scriptB_concatenated = scriptB->getOutputs()->getChild("concat_all"); // Internal check that deserialization did not result in more link copies - PropertyLinkTestUtils::ExpectLinks(m_logicEngine, { + PropertyLinkTestUtils::ExpectLinks(*m_logicEngine, { { scriptAOutput, scriptBnested_str1, false }, { scriptAnested_str1, scriptBInput, false }, { scriptAnested_str2, scriptBnested_str2, false } @@ -1647,7 +1614,7 @@ namespace ramses EXPECT_EQ(std::string("str2"), *scriptBnested_str2->get()); EXPECT_EQ(std::string("str1 {foo, str2}"), *scriptB_concatenated->get()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Values should be still the same - because the data didn't change EXPECT_EQ(std::string("str1 {foo, str2}"), *scriptB_concatenated->get()); @@ -1660,7 +1627,7 @@ namespace ramses EXPECT_FALSE(scriptBnested_str1->set("yyy")); EXPECT_FALSE(scriptBnested_str2->set("zzz")); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(std::string("str1 {foo, str2!bar}"), *scriptB_concatenated->get()); } @@ -1679,7 +1646,7 @@ namespace ramses OUT.outString = IN.inString1 .. IN.inString2 end)"; - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; auto script1 = tmpLogicEngine.createLuaScript(scriptSource, {}, "scriptA"); auto script2 = tmpLogicEngine.createLuaScript(scriptSource, {}, "scriptB"); auto script3 = tmpLogicEngine.createLuaScript(scriptSource, {}, "scriptC"); @@ -1706,22 +1673,22 @@ namespace ramses // during 1st update the weak link has no value yet so there is no concatenation from previous update EXPECT_STREQ("ABC", script3Output->get()->c_str()); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "weaklinks.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("weaklinks.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("weaklinks.bin")); - const auto script3 = m_logicEngine.findByName("scriptC"); + ASSERT_TRUE(recreateFromFile("weaklinks.bin")); + const auto script3 = m_logicEngine->findObject("scriptC"); const auto script3Output = script3->getOutputs()->getChild("outString"); EXPECT_STREQ("ABC", script3Output->get()->c_str()); // right after deserialization the weak link will propagate whatever value was serialized // therefore already 1st update after loading concatenates - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_STREQ("ABCABC", script3Output->get()->c_str()); // every upcoming update will concatenate result from previous update with this update - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_STREQ("ABCABCABC", script3Output->get()->c_str()); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_STREQ("ABCABCABCABC", script3Output->get()->c_str()); } @@ -1751,9 +1718,9 @@ namespace ramses static void ExpectVec3f(ramses::Appearance& appearance, const char* uniformName, vec3f expectedValue) { ramses::vec3f value{ 0.0f, 0.0f, 0.0f }; - ramses::UniformInput uniform; - appearance.getEffect().findUniformInput(uniformName, uniform); - appearance.getInputValue(uniform, value); + const auto uniform = appearance.getEffect().findUniformInput(uniformName); + ASSERT_TRUE(uniform.has_value()); + appearance.getInputValue(*uniform, value); EXPECT_EQ(expectedValue, value); } @@ -1765,9 +1732,9 @@ namespace ramses return *m_scene->createEffect(effectDesc); } - ramses::Appearance& createTestAppearance(ramses::Effect& effect) + ramses::Appearance& createTestAppearance(ramses::Effect& effect, std::string_view name = {}) { - return *m_scene->createAppearance(effect); + return *m_scene->createAppearance(effect, name); } const std::string_view m_vertShader = R"( @@ -1793,17 +1760,17 @@ namespace ramses TEST_F(ALogicEngine_Linking_WithBindings, PreservesLinksToNodeBindingsAfterSavingAndLoadingFromFile) { - auto ramsesNode1 = m_scene->createNode(); - auto ramsesNode2 = m_scene->createNode(); + { + auto ramsesNode1 = m_scene->createNode("node01"); + auto ramsesNode2 = m_scene->createNode("node02"); - ramsesNode1->setTranslation({1.1f, 1.2f, 1.3f}); - ramsesNode1->setRotation({2.1f, 2.2f, 2.3f}, ramses::ERotationType::Euler_ZYX); - ramsesNode1->setScaling({3.1f, 3.2f, 3.3f}); + ramsesNode1->setTranslation({1.1f, 1.2f, 1.3f}); + ramsesNode1->setRotation({2.1f, 2.2f, 2.3f}, ramses::ERotationType::Euler_ZYX); + ramsesNode1->setScaling({3.1f, 3.2f, 3.3f}); - ramsesNode2->setTranslation({11.1f, 11.2f, 11.3f}); + ramsesNode2->setTranslation({11.1f, 11.2f, 11.3f}); - { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; const auto scriptSrc = R"( function interface(IN,OUT) OUT.vec3f = Type:Vec3f() @@ -1816,8 +1783,8 @@ namespace ramses )"; auto script = tmpLogicEngine.createLuaScript(scriptSrc); - auto nodeBinding1 = tmpLogicEngine.createRamsesNodeBinding(*ramsesNode1, ramses::ERotationType::Euler_XYZ, "NodeBinding1"); - auto nodeBinding2 = tmpLogicEngine.createRamsesNodeBinding(*ramsesNode2, ramses::ERotationType::Euler_XYZ, "NodeBinding2"); + auto nodeBinding1 = tmpLogicEngine.createNodeBinding(*ramsesNode1, ramses::ERotationType::Euler_XYZ, "NodeBinding1"); + auto nodeBinding2 = tmpLogicEngine.createNodeBinding(*ramsesNode2, ramses::ERotationType::Euler_XYZ, "NodeBinding2"); auto scriptOutputVec3f = script->getOutputs()->getChild("vec3f"); auto scriptOutputBool = script->getOutputs()->getChild("visibility"); @@ -1845,26 +1812,29 @@ namespace ramses ExpectValues(*ramsesNode2, ENodePropertyStaticIndex::Translation, { 11.1f, 11.2f, 11.3f }); EXPECT_EQ(ramsesNode2->getVisibility(), ramses::EVisibilityMode::Visible); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "binding_links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("binding_links.bin")); } - // Make sure loading of bindings doesn't do anything to the node until update() is called - // To test that, we reset one node's properties to default - ramsesNode1->setTranslation({0.0f, 0.0f, 0.0f}); - ramsesNode1->setRotation({0.0f, 0.0f, 0.0f}); - ramsesNode1->setScaling({1.0f, 1.0f, 1.0f}); - ramsesNode1->setVisibility(ramses::EVisibilityMode::Visible); - { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding_links.bin", m_scene)); + ASSERT_TRUE(recreateFromFile("binding_links.bin")); + + auto ramsesNode1 = m_scene->findObject("node01"); + auto ramsesNode2 = m_scene->findObject("node02"); + + // Make sure loading of bindings doesn't do anything to the node until update() is called + // To test that, we reset one node's properties to default + ramsesNode1->setTranslation({0.0f, 0.0f, 0.0f}); + ramsesNode1->setRotation({0.0f, 0.0f, 0.0f}, ERotationType::Euler_XYZ); + ramsesNode1->setScaling({1.0f, 1.0f, 1.0f}); + ramsesNode1->setVisibility(ramses::EVisibilityMode::Visible); ExpectValues(*ramsesNode1, ENodePropertyStaticIndex::Rotation, { 0.0f, 0.0f, 0.0f }); ExpectValues(*ramsesNode1, ENodePropertyStaticIndex::Scaling, { 1.0f, 1.0f, 1.0f }); ExpectValues(*ramsesNode1, ENodePropertyStaticIndex::Translation, { 0.0f, 0.0f, 0.0f }); EXPECT_EQ(ramsesNode1->getVisibility(), ramses::EVisibilityMode::Visible); - auto nodeBinding1 = m_logicEngine.findByName("NodeBinding1"); - auto nodeBinding2 = m_logicEngine.findByName("NodeBinding2"); + auto nodeBinding1 = m_logicEngine->findObject("NodeBinding1"); + auto nodeBinding2 = m_logicEngine->findObject("NodeBinding2"); auto binding1TranslationInput = nodeBinding1->getInputs()->getChild("translation"); auto binding2RotationInput = nodeBinding2->getInputs()->getChild("rotation"); @@ -1872,12 +1842,12 @@ namespace ramses auto bindingVisibilityInput = nodeBinding1->getInputs()->getChild("visibility"); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding1TranslationInput->m_impl->setValue(vec3f{ 99.0f, 99.0f, 99.0f }); - binding2RotationInput->m_impl->setValue(vec3f{ 99.0f, 99.0f, 99.0f }); - bindingVisibilityInput->m_impl->setValue(true); + binding1TranslationInput->impl().setValue(vec3f{ 99.0f, 99.0f, 99.0f }); + binding2RotationInput->impl().setValue(vec3f{ 99.0f, 99.0f, 99.0f }); + bindingVisibilityInput->impl().setValue(true); // This should not be overwritten, but should keep the manual value instead ASSERT_TRUE(notLinkedManualInputProperty->set({100.0f, 101.0f, 102.0f})); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // These have default values ExpectValues(*ramsesNode1, ENodePropertyStaticIndex::Rotation, { 0.0f, 0.0f, 0.0f }); @@ -1898,21 +1868,20 @@ namespace ramses TEST_F(ALogicEngine_Linking_WithBindings, PreservesLinksToAppearanceBindingsAfterSavingAndLoadingFromFile) { - ramses::Effect& effect = createTestEffect(m_vertShader, m_fragShader); - auto& appearance1 = createTestAppearance(effect); - auto& appearance2 = createTestAppearance(effect); - ramses::UniformInput uniform1; - ramses::UniformInput uniform2; - appearance1.getEffect().findUniformInput("uniform1", uniform1); - appearance1.getEffect().findUniformInput("uniform2", uniform2); - - appearance1.setInputValue(uniform1, ramses::vec3f{1.1f, 1.2f, 1.3f}); - appearance1.setInputValue(uniform2, ramses::vec3f{2.1f, 2.2f, 2.3f}); - appearance2.setInputValue(uniform1, ramses::vec3f{3.1f, 3.2f, 3.3f}); - appearance2.setInputValue(uniform2, ramses::vec3f{4.1f, 4.2f, 4.3f}); - { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + ramses::Effect& effect = createTestEffect(m_vertShader, m_fragShader); + auto& appearance1 = createTestAppearance(effect, "appearance01"); + auto& appearance2 = createTestAppearance(effect, "appearance02"); + const auto uniform1 = appearance1.getEffect().findUniformInput("uniform1"); + const auto uniform2 = appearance1.getEffect().findUniformInput("uniform2"); + ASSERT_TRUE(uniform1.has_value() && uniform2.has_value()); + + appearance1.setInputValue(*uniform1, ramses::vec3f{1.1f, 1.2f, 1.3f}); + appearance1.setInputValue(*uniform2, ramses::vec3f{2.1f, 2.2f, 2.3f}); + appearance2.setInputValue(*uniform1, ramses::vec3f{3.1f, 3.2f, 3.3f}); + appearance2.setInputValue(*uniform2, ramses::vec3f{4.1f, 4.2f, 4.3f}); + + LogicEngine& tmpLogicEngine = *m_logicEngine; const auto scriptSrc = R"( function interface(IN,OUT) OUT.uniform = Type:Vec3f() @@ -1923,8 +1892,8 @@ namespace ramses )"; auto script = tmpLogicEngine.createLuaScript(scriptSrc); - auto appBinding1 = tmpLogicEngine.createRamsesAppearanceBinding(appearance1, "AppBinding1"); - auto appBinding2 = tmpLogicEngine.createRamsesAppearanceBinding(appearance2, "AppBinding2"); + auto appBinding1 = tmpLogicEngine.createAppearanceBinding(appearance1, "AppBinding1"); + auto appBinding2 = tmpLogicEngine.createAppearanceBinding(appearance2, "AppBinding2"); auto scriptOutput = script->getOutputs()->getChild("uniform"); auto binding1uniform1 = appBinding1->getInputs()->getChild("uniform1"); @@ -1942,26 +1911,30 @@ namespace ramses ExpectVec3f(appearance2, "uniform1", { 100.0f, 200.0f, 300.0f }); ExpectVec3f(appearance2, "uniform2", { 100.0f, 200.0f, 300.0f }); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "binding_links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("binding_links.bin")); } - // Make sure loading of bindings doesn't do anything to the appearance until update() is called - // To test that, we reset both appearances' properties to zeroes - appearance1.setInputValue(uniform1, ramses::vec3f{0.0f, 0.0f, 0.0f}); - appearance1.setInputValue(uniform2, ramses::vec3f{0.0f, 0.0f, 0.0f}); - appearance2.setInputValue(uniform1, ramses::vec3f{0.0f, 0.0f, 0.0f}); - appearance2.setInputValue(uniform2, ramses::vec3f{0.0f, 0.0f, 0.0f}); - { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding_links.bin", m_scene)); - + ASSERT_TRUE(recreateFromFile("binding_links.bin")); + + auto& appearance1 = *m_scene->findObject("appearance01"); + auto& appearance2 = *m_scene->findObject("appearance02"); + const auto uniform1 = appearance1.getEffect().findUniformInput("uniform1"); + const auto uniform2 = appearance1.getEffect().findUniformInput("uniform2"); + + // Make sure loading of bindings doesn't do anything to the appearance until update() is called + // To test that, we reset both appearances' properties to zeroes + appearance1.setInputValue(*uniform1, ramses::vec3f{0.0f, 0.0f, 0.0f}); + appearance1.setInputValue(*uniform2, ramses::vec3f{0.0f, 0.0f, 0.0f}); + appearance2.setInputValue(*uniform1, ramses::vec3f{0.0f, 0.0f, 0.0f}); + appearance2.setInputValue(*uniform2, ramses::vec3f{0.0f, 0.0f, 0.0f}); ExpectVec3f(appearance1, "uniform1", { 0.0f, 0.0f, 0.0f }); ExpectVec3f(appearance1, "uniform2", { 0.0f, 0.0f, 0.0f }); ExpectVec3f(appearance2, "uniform1", { 0.0f, 0.0f, 0.0f }); ExpectVec3f(appearance2, "uniform2", { 0.0f, 0.0f, 0.0f }); - auto appBinding1 = m_logicEngine.findByName("AppBinding1"); - auto appBinding2 = m_logicEngine.findByName("AppBinding2"); + auto appBinding1 = m_logicEngine->findObject("AppBinding1"); + auto appBinding2 = m_logicEngine->findObject("AppBinding2"); auto binding1uniform1 = appBinding1->getInputs()->getChild("uniform1"); auto binding1uniform2 = appBinding1->getInputs()->getChild("uniform2"); @@ -1969,13 +1942,13 @@ namespace ramses auto binding2uniform2 = appBinding2->getInputs()->getChild("uniform2"); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding1uniform1->m_impl->setValue(vec3f{ 99.0f, 99.0f, 99.0f }); + binding1uniform1->impl().setValue(vec3f{ 99.0f, 99.0f, 99.0f }); // This should not be overwritten, but should keep the manual value instead, because no link points to it ASSERT_TRUE(binding1uniform2->set({ 100.0f, 101.0f, 102.0f })); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding2uniform1->m_impl->setValue(vec3f{ 99.0f, 99.0f, 99.0f }); - binding2uniform2->m_impl->setValue(vec3f{ 99.0f, 99.0f, 99.0f }); - EXPECT_TRUE(m_logicEngine.update()); + binding2uniform1->impl().setValue(vec3f{ 99.0f, 99.0f, 99.0f }); + binding2uniform2->impl().setValue(vec3f{ 99.0f, 99.0f, 99.0f }); + EXPECT_TRUE(m_logicEngine->update()); ExpectVec3f(appearance1, "uniform1", { 100.0f, 200.0f, 300.0f }); ExpectVec3f(appearance1, "uniform2", { 100.0f, 101.0f, 102.0f }); @@ -1986,17 +1959,17 @@ namespace ramses TEST_F(ALogicEngine_Linking_WithBindings, PreservesLinksToCameraBindingsAfterSavingAndLoadingFromFile) { - auto camera1 = m_scene->createPerspectiveCamera(); - auto camera2 = m_scene->createPerspectiveCamera(); + { + auto camera1 = m_scene->createPerspectiveCamera("camera01"); + auto camera2 = m_scene->createPerspectiveCamera("camera02"); - camera1->setViewport(1, 2, 3u, 4u); - camera1->setFrustum(30.f, 640.f / 480.f, 1.f, 2.f); - camera2->setViewport(5, 6, 7u, 8u); - camera2->setFrustum(15.f, 320.f / 240.f, 3.f, 4.f); + camera1->setViewport(1, 2, 3u, 4u); + camera1->setFrustum(30.f, 640.f / 480.f, 1.f, 2.f); + camera2->setViewport(5, 6, 7u, 8u); + camera2->setFrustum(15.f, 320.f / 240.f, 3.f, 4.f); - { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; - const std::string_view scriptSrc = R"( + LogicEngine& tmpLogicEngine = *m_logicEngine; + const std::string_view scriptSrc = R"( function interface(IN,OUT) OUT.vpOffsetX = Type:Int32() OUT.farPlane = Type:Float() @@ -2008,8 +1981,8 @@ namespace ramses )"; auto script = tmpLogicEngine.createLuaScript(scriptSrc); - auto cameraBinding1 = tmpLogicEngine.createRamsesCameraBinding(*camera1, "CameraBinding1"); - auto cameraBinding2 = tmpLogicEngine.createRamsesCameraBinding(*camera2, "CameraBinding2"); + auto cameraBinding1 = tmpLogicEngine.createCameraBinding(*camera1, "CameraBinding1"); + auto cameraBinding2 = tmpLogicEngine.createCameraBinding(*camera2, "CameraBinding2"); auto scriptOutputOffset = script->getOutputs()->getChild("vpOffsetX"); auto scriptOutputFarPlane = script->getOutputs()->getChild("farPlane"); @@ -2028,25 +2001,27 @@ namespace ramses EXPECT_EQ(camera2->getViewportX(), 19); EXPECT_EQ(camera2->getFarPlane(), 7.8f); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "binding_links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("binding_links.bin")); } - - // Make sure loading of bindings doesn't do anything to the camera until update() is called - // To test that, we reset both cameras' properties - camera1->setViewport(0, 0, 1u, 1u); - camera1->setFrustum(0.1f, 0.1f, .1f, .2f); - camera2->setViewport(0, 0, 1u, 1u); - camera2->setFrustum(0.1f, 0.1f, .1f, .2f); { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding_links.bin", m_scene)); + ASSERT_TRUE(recreateFromFile("binding_links.bin")); + auto camera1 = m_scene->findObject("camera01"); + auto camera2 = m_scene->findObject("camera02"); + + // Make sure loading of bindings doesn't do anything to the camera until update() is called + // To test that, we reset both cameras' properties + camera1->setViewport(0, 0, 1u, 1u); + camera1->setFrustum(0.1f, 0.1f, .1f, .2f); + camera2->setViewport(0, 0, 1u, 1u); + camera2->setFrustum(0.1f, 0.1f, .1f, .2f); EXPECT_EQ(camera1->getViewportX(), 0); EXPECT_EQ(camera1->getFarPlane(), .2f); EXPECT_EQ(camera2->getViewportX(), 0); EXPECT_EQ(camera2->getFarPlane(), .2f); - auto cameraBinding1 = m_logicEngine.findByName("CameraBinding1"); - auto cameraBinding2 = m_logicEngine.findByName("CameraBinding2"); + auto cameraBinding1 = m_logicEngine->findObject("CameraBinding1"); + auto cameraBinding2 = m_logicEngine->findObject("CameraBinding2"); auto binding1cameraProperty1 = cameraBinding1->getInputs()->getChild("viewport")->getChild("offsetX"); auto binding1cameraProperty2 = cameraBinding1->getInputs()->getChild("frustum")->getChild("farPlane"); @@ -2054,13 +2029,13 @@ namespace ramses auto binding2cameraProperty2 = cameraBinding2->getInputs()->getChild("frustum")->getChild("farPlane"); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding1cameraProperty1->m_impl->setValue(100); + binding1cameraProperty1->impl().setValue(100); // This should not be overwritten, but should keep the manual value instead, because no link points to it ASSERT_TRUE(binding1cameraProperty2->set(100.f)); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding2cameraProperty1->m_impl->setValue(100); - binding2cameraProperty2->m_impl->setValue(100.f); - EXPECT_TRUE(m_logicEngine.update()); + binding2cameraProperty1->impl().setValue(100); + binding2cameraProperty2->impl().setValue(100.f); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(camera1->getViewportX(), 19); EXPECT_EQ(camera1->getFarPlane(), 100.f); @@ -2071,16 +2046,16 @@ namespace ramses TEST_F(ALogicEngine_Linking_WithBindings, PreservesLinksToRenderPassBindingsAfterSavingAndLoadingFromFile) { - auto rp1 = m_scene->createRenderPass(); - auto rp2 = m_scene->createRenderPass(); + { + auto rp1 = m_scene->createRenderPass("rp01"); + auto rp2 = m_scene->createRenderPass("rp02"); - rp1->setEnabled(true); - rp1->setRenderOrder(11); - rp2->setEnabled(false); - rp2->setRenderOrder(22); + rp1->setEnabled(true); + rp1->setRenderOrder(11); + rp2->setEnabled(false); + rp2->setRenderOrder(22); - { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; const std::string_view scriptSrc = R"( function interface(IN,OUT) OUT.enabled = Type:Bool() @@ -2093,8 +2068,8 @@ namespace ramses )"; auto script = tmpLogicEngine.createLuaScript(scriptSrc); - auto binding1 = tmpLogicEngine.createRamsesRenderPassBinding(*rp1, "RPBinding1"); - auto binding2 = tmpLogicEngine.createRamsesRenderPassBinding(*rp2, "RPBinding2"); + auto binding1 = tmpLogicEngine.createRenderPassBinding(*rp1, "RPBinding1"); + auto binding2 = tmpLogicEngine.createRenderPassBinding(*rp2, "RPBinding2"); auto scriptOutputEnabled = script->getOutputs()->getChild("enabled"); auto scriptOutputOrder = script->getOutputs()->getChild("order"); @@ -2113,25 +2088,28 @@ namespace ramses EXPECT_FALSE(rp2->isEnabled()); EXPECT_EQ(rp2->getRenderOrder(), 33); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "binding_links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("binding_links.bin")); } - - // Make sure loading of bindings doesn't do anything to the render pass until update() is called - // To test that, we reset both render passes' properties - rp1->setEnabled(true); - rp1->setRenderOrder(0); - rp2->setEnabled(true); - rp2->setRenderOrder(0); { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding_links.bin", m_scene)); + ASSERT_TRUE(recreateFromFile("binding_links.bin")); + + auto rp1 = m_scene->findObject("rp01"); + auto rp2 = m_scene->findObject("rp02"); + + // Make sure loading of bindings doesn't do anything to the render pass until update() is called + // To test that, we reset both render passes' properties + rp1->setEnabled(true); + rp1->setRenderOrder(0); + rp2->setEnabled(true); + rp2->setRenderOrder(0); EXPECT_TRUE(rp1->isEnabled()); EXPECT_EQ(rp1->getRenderOrder(), 0); EXPECT_TRUE(rp2->isEnabled()); EXPECT_EQ(rp2->getRenderOrder(), 0); - auto binding1 = m_logicEngine.findByName("RPBinding1"); - auto binding2 = m_logicEngine.findByName("RPBinding2"); + auto binding1 = m_logicEngine->findObject("RPBinding1"); + auto binding2 = m_logicEngine->findObject("RPBinding2"); auto binding1Property1 = binding1->getInputs()->getChild("enabled"); auto binding1Property2 = binding1->getInputs()->getChild("renderOrder"); @@ -2139,13 +2117,13 @@ namespace ramses auto binding2Property2 = binding2->getInputs()->getChild("renderOrder"); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding1Property1->m_impl->setValue(false); + binding1Property1->impl().setValue(false); // This should not be overwritten, but should keep the manual value instead, because no link points to it ASSERT_TRUE(binding1Property2->set(100)); // These values should be overwritten by the link - set them to a different value to make sure that happens - binding2Property1->m_impl->setValue(false); - binding2Property2->m_impl->setValue(100); - EXPECT_TRUE(m_logicEngine.update()); + binding2Property1->impl().setValue(false); + binding2Property2->impl().setValue(100); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_FALSE(rp1->isEnabled()); EXPECT_EQ(rp1->getRenderOrder(), 100); @@ -2159,7 +2137,7 @@ namespace ramses m_renderGroup->addMeshNode(*m_meshNode, 42); { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; const std::string_view scriptSrc = R"( function interface(IN,OUT) OUT.order = Type:Int32() @@ -2170,9 +2148,9 @@ namespace ramses )"; auto script = tmpLogicEngine.createLuaScript(scriptSrc); - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode)); - auto binding = tmpLogicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "binding"); + auto binding = tmpLogicEngine.createRenderGroupBinding(*m_renderGroup, elements, "binding"); ASSERT_TRUE(tmpLogicEngine.link(*script->getOutputs()->getChild("order"), *binding->getInputs()->getChild("renderOrders")->getChild("meshNode"))); ASSERT_TRUE(tmpLogicEngine.update()); @@ -2182,25 +2160,27 @@ namespace ramses EXPECT_EQ(33, renderOrder); // script has no inputs linked, validatation would fail - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "binding_links.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("binding_links.bin")); } - // Make sure loading of bindings doesn't do anything to the render group until update() is called - // To test that, we reset render group property - m_renderGroup->addMeshNode(*m_meshNode, 100); - { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding_links.bin", m_scene)); + ASSERT_TRUE(recreateFromFile("binding_links.bin")); + ASSERT_TRUE(m_renderGroup != nullptr); + ASSERT_TRUE(m_meshNode != nullptr); + + // Make sure loading of bindings doesn't do anything to the render group until update() is called + // To test that, we reset render group property + m_renderGroup->addMeshNode(*m_meshNode, 100); int32_t renderOrder = -1; m_renderGroup->getMeshNodeOrder(*m_meshNode, renderOrder); EXPECT_EQ(100, renderOrder); - auto binding = m_logicEngine.findByName("binding"); + auto binding = m_logicEngine->findObject("binding"); // This value should be overwritten by the link - set it to a different value to make sure that happens binding->getInputs()->getChild("renderOrders")->getChild("meshNode")->set(222); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); m_renderGroup->getMeshNodeOrder(*m_meshNode, renderOrder); EXPECT_EQ(33, renderOrder); @@ -2222,33 +2202,33 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSource); - auto middleScript = m_logicEngine.createLuaScript(scriptSource); - auto targetBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource); + auto middleScript = m_logicEngine->createLuaScript(scriptSource); + auto targetBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); auto sourceOutputBool = sourceScript->getOutputs()->getChild("output")->getChild("outBool"); auto middleInputBool = middleScript->getInputs()->getChild("input")->getChild("inBool"); auto middleOutputBool = middleScript->getOutputs()->getChild("output")->getChild("outBool"); auto targetInputBool = targetBinding->getInputs()->getChild("visibility"); - m_logicEngine.link(*sourceOutputBool, *middleInputBool); - m_logicEngine.link(*middleOutputBool, *targetInputBool); + m_logicEngine->link(*sourceOutputBool, *middleInputBool); + m_logicEngine->link(*middleOutputBool, *targetInputBool); - EXPECT_TRUE(m_logicEngine.isLinked(*sourceScript)); - EXPECT_TRUE(m_logicEngine.isLinked(*middleScript)); - EXPECT_TRUE(m_logicEngine.isLinked(*targetBinding)); + EXPECT_TRUE(m_logicEngine->isLinked(*sourceScript)); + EXPECT_TRUE(m_logicEngine->isLinked(*middleScript)); + EXPECT_TRUE(m_logicEngine->isLinked(*targetBinding)); - m_logicEngine.unlink(*middleOutputBool, *targetInputBool); + m_logicEngine->unlink(*middleOutputBool, *targetInputBool); - EXPECT_TRUE(m_logicEngine.isLinked(*sourceScript)); - EXPECT_TRUE(m_logicEngine.isLinked(*middleScript)); - EXPECT_FALSE(m_logicEngine.isLinked(*targetBinding)); + EXPECT_TRUE(m_logicEngine->isLinked(*sourceScript)); + EXPECT_TRUE(m_logicEngine->isLinked(*middleScript)); + EXPECT_FALSE(m_logicEngine->isLinked(*targetBinding)); - m_logicEngine.unlink(*sourceOutputBool, *middleInputBool); + m_logicEngine->unlink(*sourceOutputBool, *middleInputBool); - EXPECT_FALSE(m_logicEngine.isLinked(*sourceScript)); - EXPECT_FALSE(m_logicEngine.isLinked(*middleScript)); - EXPECT_FALSE(m_logicEngine.isLinked(*targetBinding)); + EXPECT_FALSE(m_logicEngine->isLinked(*sourceScript)); + EXPECT_FALSE(m_logicEngine->isLinked(*middleScript)); + EXPECT_FALSE(m_logicEngine->isLinked(*targetBinding)); } TEST_F(ALogicEngine_Linking, SetsAffectedNodesToDirtyAfterLinking) @@ -2262,21 +2242,21 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSource); - auto targetBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource); + auto targetBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetBinding->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetBinding->impl().isDirty()); auto output = sourceScript->getOutputs()->getChild("output"); auto input = targetBinding->getInputs()->getChild("visibility"); - m_logicEngine.link(*output, *input); + m_logicEngine->link(*output, *input); - EXPECT_TRUE(sourceScript->m_impl.isDirty()); - EXPECT_TRUE(targetBinding->m_impl.isDirty()); + EXPECT_TRUE(sourceScript->impl().isDirty()); + EXPECT_TRUE(targetBinding->impl().isDirty()); } TEST_F(ALogicEngine_Linking, SetsAffectedNodesToDirtyAfterLinkingWithStructs) @@ -2294,21 +2274,21 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSource); - auto targetScript = m_logicEngine.createLuaScript(scriptSource); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource); + auto targetScript = m_logicEngine->createLuaScript(scriptSource); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetScript->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetScript->impl().isDirty()); auto output = sourceScript->getOutputs()->getChild("struct")->getChild("outBool"); auto input = targetScript->getInputs()->getChild("struct")->getChild("inBool"); - m_logicEngine.link(*output, *input); + m_logicEngine->link(*output, *input); - EXPECT_TRUE(sourceScript->m_impl.isDirty()); - EXPECT_TRUE(targetScript->m_impl.isDirty()); + EXPECT_TRUE(sourceScript->impl().isDirty()); + EXPECT_TRUE(targetScript->impl().isDirty()); } TEST_F(ALogicEngine_Linking, SetsNeitherTargetNodeNorSourceNodeToDirtyAfterUnlink) @@ -2322,23 +2302,23 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSource); - auto targetBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource); + auto targetBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "RamsesBinding"); auto output = sourceScript->getOutputs()->getChild("output"); auto input = targetBinding->getInputs()->getChild("visibility"); - m_logicEngine.link(*output, *input); + m_logicEngine->link(*output, *input); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetBinding->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetBinding->impl().isDirty()); - m_logicEngine.unlink(*output, *input); + m_logicEngine->unlink(*output, *input); - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetBinding->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetBinding->impl().isDirty()); } class ALogicEngine_Linking_Confidence : public ALogicEngine @@ -2381,73 +2361,73 @@ namespace ramses TEST_F(ALogicEngine_Linking_Confidence, CanDestroyLinkedScriptsWithComplexTypes_WithLinkStillActive_DestroySourceFirst) { - LuaScript& sourceScript(*m_logicEngine.createLuaScript(m_scriptNestedStructs)); - LuaScript& targetScript(*m_logicEngine.createLuaScript(m_scriptNestedStructs)); + LuaScript& sourceScript(*m_logicEngine->createLuaScript(m_scriptNestedStructs)); + LuaScript& targetScript(*m_logicEngine->createLuaScript(m_scriptNestedStructs)); - const Property& sourceVec(*sourceScript.getOutputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); + Property& sourceVec(*sourceScript.getOutputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); Property& targetVec(*targetScript.getInputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.link(sourceVec, targetVec)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(sourceScript)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(targetScript)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->link(sourceVec, targetVec)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(sourceScript)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(targetScript)); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALogicEngine_Linking_Confidence, CanDestroyLinkedScriptsWithComplexTypes_WithLinkStillActive_DestroyTargetFirst) { - LuaScript& sourceScript(*m_logicEngine.createLuaScript(m_scriptNestedStructs)); - LuaScript& targetScript(*m_logicEngine.createLuaScript(m_scriptNestedStructs)); + LuaScript& sourceScript(*m_logicEngine->createLuaScript(m_scriptNestedStructs)); + LuaScript& targetScript(*m_logicEngine->createLuaScript(m_scriptNestedStructs)); - const Property& sourceVec(*sourceScript.getOutputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); + Property& sourceVec(*sourceScript.getOutputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); Property& targetVec(*targetScript.getInputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); // Update in-between each step, to make sure no crashes - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.link(sourceVec, targetVec)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(targetScript)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(sourceScript)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->link(sourceVec, targetVec)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(targetScript)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(sourceScript)); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALogicEngine_Linking_Confidence, CanDestroyLinkedScriptsWithComplexTypes_WithLinkStillActive_ArrayOfStructs) { - LuaScript& sourceScript(*m_logicEngine.createLuaScript(m_scriptArrayOfStructs)); - LuaScript& targetScript(*m_logicEngine.createLuaScript(m_scriptNestedStructs)); + LuaScript& sourceScript(*m_logicEngine->createLuaScript(m_scriptArrayOfStructs)); + LuaScript& targetScript(*m_logicEngine->createLuaScript(m_scriptNestedStructs)); - const Property& sourceVec(*sourceScript.getOutputs()->getChild("array")->getChild(0)->getChild("vec3f")); + Property& sourceVec(*sourceScript.getOutputs()->getChild("array")->getChild(0)->getChild("vec3f")); Property& targetVec(*targetScript.getInputs()->getChild("struct")->getChild("nested")->getChild("vec3f")); // Update in-between each step, to make sure no crashes - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.link(sourceVec, targetVec)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(sourceScript)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(targetScript)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->link(sourceVec, targetVec)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(sourceScript)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(targetScript)); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALogicEngine_Linking_Confidence, CanDestroyLinkedBinding_WithNestedLinkStillActive) { - LuaScript& sourceScript(*m_logicEngine.createLuaScript(m_scriptNestedStructs)); + LuaScript& sourceScript(*m_logicEngine->createLuaScript(m_scriptNestedStructs)); - auto targetBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto targetBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); auto translationProperty = targetBinding->getInputs()->getChild("translation"); - const Property& sourceVec(*sourceScript.getOutputs()->getChild("struct")->getChild(0)->getChild("vec3f")); + Property& sourceVec(*sourceScript.getOutputs()->getChild("struct")->getChild(0)->getChild("vec3f")); // Update in-between each step, to make sure no crashes - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.link(sourceVec, *translationProperty)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(sourceScript)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.destroy(*targetBinding)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->link(sourceVec, *translationProperty)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(sourceScript)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_TRUE(m_logicEngine->destroy(*targetBinding)); + EXPECT_TRUE(m_logicEngine->update()); } } diff --git a/tests/unittests/client/logic/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp b/tests/unittests/client/logic/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp new file mode 100644 index 000000000..48110e0ac --- /dev/null +++ b/tests/unittests/client/logic/api/LogicEngineTest_LogicNodeUpdateStatistics.cpp @@ -0,0 +1,440 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include + +#include "gtest/gtest.h" +#include "LogicEngineTest_Base.h" +#include "ramses/client/logic/Property.h" +#include "internal/logic/UpdateReport.h" +#include "internal/logic/LogicNodeUpdateStatistics.h" + +#include "LogTestUtils.h" + +namespace ramses::internal +{ + class ALogicEngine_LogicObjectStatistics : public ALogicEngine + { + protected: + std::vector m_logTypes; + std::vector m_logMessages; + ScopedLogContextLevel m_logCollector{CONTEXT_PERIODIC, ELogLevel::Info, [this](ELogLevel type, std::string_view message) + { + m_logTypes.emplace_back(type); + m_logMessages.emplace_back(message); + }}; + }; + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsWithNoNode) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + + statistics.setLoggingRate(1u); + statistics.collect(report, 0); + statistics.calculateAndLog(); + + ASSERT_EQ(5u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 0 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsForOneNode) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + statistics.setLoggingRate(1u); + + auto* node1 = m_logicEngine->createTimerNode("test node1"); + auto& dummyNodes = const_cast(report.getNodesExecuted()); + dummyNodes.emplace_back(&static_cast(node1)->impl(), std::chrono::microseconds(3)); + + statistics.collect(report, dummyNodes.size()); + statistics.calculateAndLog(); + + ASSERT_EQ(6u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 1 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]: [test node1:3]") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsForOneNodeWithZeroExecutionTime) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + statistics.setLoggingRate(1u); + + auto* node1 = m_logicEngine->createTimerNode("test node1"); + auto& dummyNodes = const_cast(report.getNodesExecuted()); + dummyNodes.emplace_back(&static_cast(node1)->impl(), std::chrono::microseconds(0)); + + statistics.collect(report, dummyNodes.size()); + statistics.calculateAndLog(); + + ASSERT_EQ(6u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 1 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]: [test node1:0]") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsForTwoNodes) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + statistics.setLoggingRate(1u); + + auto* node1 = m_logicEngine->createTimerNode("test node1"); + auto* node2 = m_logicEngine->createTimerNode("test node2"); + auto& dummyNodes = const_cast(report.getNodesExecuted()); + dummyNodes.emplace_back(&static_cast(node1)->impl(), std::chrono::microseconds(3)); + dummyNodes.emplace_back(&static_cast(node2)->impl(), std::chrono::microseconds(10)); + + statistics.collect(report, dummyNodes.size()); + statistics.calculateAndLog(); + + ASSERT_EQ(6u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 2 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]: [test node2:10] [test node1:3]") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsForFiveNodesWithSameTime) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + statistics.setLoggingRate(1u); + + auto* node1 = m_logicEngine->createTimerNode("test node1"); + auto* node2 = m_logicEngine->createTimerNode("test node2"); + auto* node3 = m_logicEngine->createTimerNode("test node3"); + auto* node4 = m_logicEngine->createTimerNode("test node4"); + auto* node5 = m_logicEngine->createTimerNode("test node5"); + auto& dummyNodes = const_cast(report.getNodesExecuted()); + dummyNodes.emplace_back(&static_cast(node1)->impl(), std::chrono::microseconds(10)); + dummyNodes.emplace_back(&static_cast(node2)->impl(), std::chrono::microseconds(10)); + dummyNodes.emplace_back(&static_cast(node3)->impl(), std::chrono::microseconds(10)); + dummyNodes.emplace_back(&static_cast(node4)->impl(), std::chrono::microseconds(10)); + dummyNodes.emplace_back(&static_cast(node5)->impl(), std::chrono::microseconds(10)); + + statistics.collect(report, dummyNodes.size()); + statistics.calculateAndLog(); + + ASSERT_EQ(6u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 5 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]: [test node1:10] [test node2:10] [test node3:10] [test node4:10] [test node5:10]") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsForOneHundredNodesWithRandomTime) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + statistics.setLoggingRate(1u); + + std::string nodeBaseName = "test node"; + auto& dummyNodes = const_cast(report.getNodesExecuted()); + + // Generate random numbers from 1 to 100 + std::mt19937 rng(std::random_device{}()); + std::uniform_int_distribution distribution(1, 100); + for (int32_t i = 0; i < 100; ++i) + { + auto* timerNode = m_logicEngine->createTimerNode(nodeBaseName + std::to_string(i)); + int randomTime = distribution(rng); + switch (i) + { + case 10: + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(609)); + break; + case 35: + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(721)); + break; + case 58: + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(831)); + break; + case 72: + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(101)); + break; + case 93: + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(616)); + break; + default: + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(randomTime)); + break; + } + } + + statistics.collect(report, dummyNodes.size()); + statistics.calculateAndLog(); + + ASSERT_EQ(6u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 100 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]: [test node58:831] [test node35:721] [test node93:616] [test node10:609] [test node72:101]") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, verifyLogsForOneHundredNodesWithRandomTimeInMultipleCollection) + { + LogicNodeUpdateStatistics statistics; + UpdateReport report; + statistics.setLoggingRate(1u); + + std::string nodeBaseName = "test node"; + auto& dummyNodes = const_cast(report.getNodesExecuted()); + + // Generate random numbers from 1 to 100 + std::mt19937 rng(std::random_device{}()); + std::uniform_int_distribution distribution(1, 100); + int32_t curNodeCount = 0; + while (curNodeCount < 100) + { + auto* timerNode = m_logicEngine->createTimerNode(nodeBaseName + std::to_string(curNodeCount++)); + int randomTime = distribution(rng); + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(randomTime)); + } + + // Collect 3 times + for (int j = 1; j <= 3; ++j) + { + // Change time for current nodes and make 1 slow + for (int i = 0; i < 100; ++i) + { + int randomTime = distribution(rng); + dummyNodes[i].second = std::chrono::microseconds(randomTime); + } + dummyNodes[30 * j].second = std::chrono::microseconds(600 + j); + + // add a new slow node + auto* timerNode = m_logicEngine->createTimerNode(nodeBaseName + std::to_string(curNodeCount++)); + dummyNodes.emplace_back(&static_cast(timerNode)->impl(), std::chrono::microseconds(500 + j)); + + statistics.collect(report, dummyNodes.size()); + } + + statistics.calculateAndLog(); + ASSERT_EQ(6u, m_logMessages.size()); + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg): 0/0/0 [u]sec") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg): 0%/0%/0% (0/0/0) of 103 nodes total") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg): 0/0/0") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]: [test node90:603] [test node60:602] [test node30:601] [test node102:503] [test node101:502]") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, LogsAllMessages) + { + m_logicEngine->setStatisticsLoggingRate(2u, EStatisticsLogMode::Detailed); + + constexpr auto scriptSource = R"( + function interface(IN,OUT) + IN.param = Type:Int32() + OUT.param = Type:Int32() + end + function run(IN,OUT) + OUT.param = IN.param + end + )"; + + auto node1 = m_logicEngine->createLuaScript(scriptSource); + auto node2 = m_logicEngine->createLuaScript(scriptSource); + auto node3 = m_logicEngine->createLuaScript(scriptSource); + auto node4 = m_logicEngine->createLuaScript(scriptSource); + auto node5 = m_logicEngine->createLuaScript(scriptSource); + + m_logicEngine->link(*node1->getOutputs()->getChild(0u), *node2->getInputs()->getChild(0u)); + m_logicEngine->link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); + m_logicEngine->link(*node3->getOutputs()->getChild(0u), *node4->getInputs()->getChild(0u)); + m_logicEngine->link(*node4->getOutputs()->getChild(0u), *node5->getInputs()->getChild(0u)); + + for (int32_t i = 0; i < 4; ++i) + { + node1->getInputs()->getChild(0u)->set(i); + EXPECT_TRUE(m_logicEngine->update()); + } + + // 6 log lines per frame and there are 2 frames + ASSERT_EQ(12u, m_logMessages.size()); + + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]:") != std::string::npos); + EXPECT_TRUE(m_logMessages[6].find("Time since last log:") != std::string::npos); + EXPECT_TRUE(m_logMessages[7].find("Update Execution time (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[8].find("Time between Update calls (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[9].find("Nodes Executed (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[10].find("Activated links (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[11].find("Slowest nodes [name:time_us]:") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, LogsMessagesWithCompactStatistics) + { + m_logicEngine->setStatisticsLoggingRate(1u, EStatisticsLogMode::Compact); + + constexpr auto scriptSource = R"( + function interface(IN,OUT) + IN.param = Type:Int32() + OUT.param = Type:Int32() + end + function run(IN,OUT) + OUT.param = IN.param + end + )"; + + m_logicEngine->createLuaScript(scriptSource); + EXPECT_TRUE(m_logicEngine->update()); + + ASSERT_EQ(5u, m_logMessages.size()); + + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg):") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, LogsMessagesWithDetailedStatistics) + { + m_logicEngine->setStatisticsLoggingRate(1u, EStatisticsLogMode::Detailed); + + constexpr auto scriptSource = R"( + function interface(IN,OUT) + IN.param = Type:Int32() + OUT.param = Type:Int32() + end + function run(IN,OUT) + OUT.param = IN.param + end + )"; + + m_logicEngine->createLuaScript(scriptSource); + EXPECT_TRUE(m_logicEngine->update()); + + ASSERT_EQ(6u, m_logMessages.size()); + + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]:") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, NoSlowestNodeWithUpdateReportDisabled) + { + m_logicEngine->setStatisticsLoggingRate(2u); + m_logicEngine->createTimerNode("test timer node"); + + m_logicEngine->update(); + EXPECT_TRUE(m_logMessages.empty()); + + m_logicEngine->update(); + ASSERT_EQ(5u, m_logMessages.size()); + + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg):") != std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, DoNotLogSameNode) + { + m_logicEngine->setStatisticsLoggingRate(2u, EStatisticsLogMode::Detailed); + m_logicEngine->createTimerNode("test timer node"); + + m_logicEngine->update(); + EXPECT_TRUE(m_logMessages.empty()); + + m_logicEngine->update(); + ASSERT_EQ(6u, m_logMessages.size()); + + EXPECT_TRUE(m_logMessages[0].find("First Statistics Log") != std::string::npos); + EXPECT_TRUE(m_logMessages[1].find("Update Execution time (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[3].find("Nodes Executed (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[4].find("Activated links (min/max/avg):") != std::string::npos); + EXPECT_TRUE(m_logMessages[5].find("Slowest nodes [name:time_us]:") != std::string::npos); + + size_t pos = m_logMessages[5].find("[test timer node:"); + EXPECT_TRUE(m_logMessages[5].find("[test timer node:", pos+1) == std::string::npos); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, NoLogsWhenLoggingRateZero) + { + m_logicEngine->setStatisticsLoggingRate(0u); + + for (int32_t i = 0; i < 4; ++i) + { + EXPECT_TRUE(m_logicEngine->update()); + } + + EXPECT_EQ(0u, m_logMessages.size()); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, NoLogsWhenLoggingRateZeroAndDetailedStatistics) + { + m_logicEngine->setStatisticsLoggingRate(0u, EStatisticsLogMode::Detailed); + m_logicEngine->createTimerNode("test timer node"); + + for (int32_t i = 0; i < 4; ++i) + { + EXPECT_TRUE(m_logicEngine->update()); + } + EXPECT_EQ(0u, m_logMessages.size()); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, LogsAccordingToLoggingRate) + { + m_logicEngine->setStatisticsLoggingRate(2u); + + m_logicEngine->update(); + EXPECT_TRUE(m_logMessages.empty()); + + m_logicEngine->update(); + EXPECT_EQ(5u ,m_logMessages.size()); + + m_logicEngine->update(); + EXPECT_EQ(5u, m_logMessages.size()); + + m_logicEngine->update(); + EXPECT_EQ(10u, m_logMessages.size()); + } + + TEST_F(ALogicEngine_LogicObjectStatistics, NoTimeBetweenUpdateCallsCalculationWithLoggingRateOne) + { + m_logicEngine->setStatisticsLoggingRate(1u); + + m_logicEngine->update(); + EXPECT_TRUE(m_logMessages[2].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + + m_logicEngine->update(); + EXPECT_TRUE(m_logMessages[7].find("Time between Update calls cannot be measured with loggingRate = 1") != std::string::npos); + } +} diff --git a/tests/unittests/client/logic/api/LogicEngineTest_Serialization.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Serialization.cpp new file mode 100644 index 000000000..07add8440 --- /dev/null +++ b/tests/unittests/client/logic/api/LogicEngineTest_Serialization.cpp @@ -0,0 +1,863 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" + +#include "RamsesTestUtils.h" +#include "FeatureLevelTestValues.h" +#include "PropertyLinkTestUtils.h" + +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/LuaInterface.h" + +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/UniformInput.h" +#include "ramses/framework/RamsesVersion.h" + +#include "impl/logic/DataArrayImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "internal/logic/ApiObjects.h" +#include "internal/logic/FileUtils.h" +#include "LogTestUtils.h" +#include "FileDescriptorHelper.h" + +#include "internal/logic/flatbuffers/generated/LogicEngineGen.h" +#include "ramses-sdk-build-config.h" +#include "fmt/format.h" + +#include +#include + +namespace ramses::internal +{ + const size_t SCENE_ISSUES = 6u; + + class ALogicEngine_Serialization : public ALogicEngineBase, public ::testing::TestWithParam + { + public: + ALogicEngine_Serialization() : ALogicEngineBase{ GetParam() } + { + withTempDirectory(); + } + + protected: + static std::vector CreateTestBuffer(const SaveFileConfig& config = {}) + { + RamsesTestSetup ramsesSetup{ GetParam() }; + LogicEngine& logicEngineForSaving{ *ramsesSetup.createScene()->createLogicEngine() }; + + const std::string src = R"( + function interface(IN,OUT) + IN.param = Type:Int32() + OUT.param2 = Type:Int32() + end + function run(IN,OUT) + end + )"; + + // Create simple (and compact) valid setup, where two (identical) scripts are created + // and their inputs and outputs are cross linked, so that none of the scripts + // generate a warning for having unlinked inputs or outputs + + auto* script1 = logicEngineForSaving.createLuaScript(src, {}, "luascript"); + auto* script2 = logicEngineForSaving.createLuaScript(src, {}, "luascript2"); + + //link output of 1st script to input of 2nd script + logicEngineForSaving.link(*script1->getOutputs()->getChild("param2"), *script2->getInputs()->getChild("param")); + //link output of 2nd script to input of 1st script, use weak link to avoid circular dependancy + logicEngineForSaving.linkWeak(*script2->getOutputs()->getChild("param2"), *script1->getInputs()->getChild("param")); + + EXPECT_TRUE(logicEngineForSaving.getScene().saveToFile("tempfile.bin", config)); + + return *FileUtils::LoadBinary("tempfile.bin"); + } + + static void SaveBufferToFile(const std::vector& bufferData, const std::string& file) + { + FileUtils::SaveBinary(file, static_cast(bufferData.data()), bufferData.size()); + } + + std::vector saveAndLoadAllTypesOfObjects() + { + LogicEngine& logicEngine = *m_logicEngine; + + logicEngine.createLuaModule(m_moduleSourceCode, {}, "module"); + logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); + auto* nodeBinding = logicEngine.createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); + auto* appearanceBinding = logicEngine.createAppearanceBinding(*m_appearance, "appearanceBinding"); + auto* cameraBinding = logicEngine.createCameraBinding(*m_camera, "cameraBinding"); + const auto* dataArray = logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataArray"); + AnimationNodeConfig config; + config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); + logicEngine.createAnimationNode(config, "animNode"); + logicEngine.createTimerNode("timerNode"); + logicEngine.createLuaInterface(R"( + function interface(IN, OUT) + end + )", "intf"); + + logicEngine.createRenderPassBinding(*m_renderPass, "rpBinding"); + logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); + createRenderGroupBinding(logicEngine); + createSkinBinding(*nodeBinding, *appearanceBinding, logicEngine); + logicEngine.createMeshNodeBinding(*m_meshNode, "mb"); + + EXPECT_TRUE(logicEngine.update()); + EXPECT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + const std::vector names{ "module", "script", "nodeBinding", "appearanceBinding", "cameraBinding", "dataArray", "animNode", + "timerNode", "intf", "rpBinding", "anchor", "renderGroupBinding", "skin", "mb" }; + + std::vector objects; + for (const auto& name : names) + { + auto obj = m_logicEngine->findObject(name); + EXPECT_NE(nullptr, obj); + objects.push_back(obj); + } + + return objects; + } + }; + + INSTANTIATE_TEST_SUITE_P( + ALogicEngine_SerializationTests, + ALogicEngine_Serialization, + GetFeatureLevelTestValues()); + + TEST_P(ALogicEngine_Serialization, ProducesErrorWhenProvidingAFolderAsTargetForSaving) + { + fs::create_directories("folder"); + EXPECT_FALSE(saveToFileWithoutValidation("folder")); + EXPECT_EQ("Scene::saveToFile failed, could not open file for writing: 'folder'", getLastErrorMessage()); + } + + TEST_P(ALogicEngine_Serialization, DISABLED_PrintsMetadataInfoOnLoad) + { + SaveFileConfig config; + config.setMetadataString("This is a scene exported for tests"); + config.setExporterVersion(3, 1, 2, 42); + + // Test different constructor variations + SaveFileConfig config2(config); + config2 = config; + config2 = std::move(config); + + SaveBufferToFile(CreateTestBuffer(config2), "LogicEngine.bin"); + + TestLogCollector logCollector(CONTEXT_CLIENT, ELogLevel::Info); + + // Test with file API + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + + ASSERT_EQ(logCollector.logs.size(), 3u); + EXPECT_THAT(logCollector.logs[0].message, ::testing::HasSubstr("Loading logic engine content from 'file 'LogicEngine.bin'")); + EXPECT_THAT(logCollector.logs[1].message, ::testing::HasSubstr("Logic Engine content metadata: 'This is a scene exported for tests'")); + EXPECT_THAT(logCollector.logs[2].message, ::testing::HasSubstr("Exporter version: 3.1.2 (file format version 42)")); + } + } + + TEST_P(ALogicEngine_Serialization, DISABLED_PrintsMetadataInfoOnLoad_NoVersionInfoProvided) + { + SaveFileConfig config; + SaveBufferToFile(CreateTestBuffer(config), "LogicEngine.bin"); + + TestLogCollector logCollector(CONTEXT_CLIENT, ELogLevel::Info); + + // Test with file API + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + + ASSERT_EQ(logCollector.logs.size(), 3u); + EXPECT_THAT(logCollector.logs[0].message, ::testing::HasSubstr("Loading logic engine content from 'file 'LogicEngine.bin'")); + EXPECT_THAT(logCollector.logs[1].message, ::testing::HasSubstr("Logic Engine content metadata: ''")); + EXPECT_THAT(logCollector.logs[2].message, ::testing::HasSubstr("Exporter version: 0.0.0 (file format version 0)")); + } + } + +// The Windows API doesn't allow non-admin access to symlinks, this breaks on dev machines +#ifndef _WIN32 + TEST_P(ALogicEngine_Serialization, CanBeDeserializedFromHardLink) + { + ASSERT_TRUE(saveToFileWithoutValidation("testfile.bin")); + fs::create_hard_link("testfile.bin", "hardlink"); + EXPECT_TRUE(recreateFromFile("hardlink")); + } + + TEST_P(ALogicEngine_Serialization, CanBeDeserializedFromSymLink) + { + ASSERT_TRUE(saveToFileWithoutValidation("testfile.bin")); + fs::create_symlink("testfile.bin", "symlink"); + EXPECT_TRUE(recreateFromFile("symlink")); + } +#endif + + TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserializedWithNoScriptsAndNoNodeBindings) + { + { + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + } + } + + TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserializedWithNoScripts) + { + { + LogicEngine& logicEngine = *m_logicEngine; + logicEngine.createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); + + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + { + auto rNodeBinding = m_logicEngine->findObject("binding"); + ASSERT_NE(nullptr, rNodeBinding); + } + } + } + + TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserializedWithoutNodeBindings) + { + { + LogicEngine& logicEngine = *m_logicEngine; + logicEngine.createLuaScript(R"( + function interface(IN,OUT) + IN.param = Type:Int32() + end + function run(IN,OUT) + end + )", {}, "luascript"); + + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + { + auto script = m_logicEngine->findObject("luascript"); + ASSERT_NE(nullptr, script); + const auto inputs = script->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(1u, inputs->getChildCount()); + } + } + } + + TEST_P(ALogicEngine_Serialization, ProducesErrorIfSavedWithValidationError) + { + // Put logic engine to a dirty state (create new object and don't call update) + NodeBinding* nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); + + std::vector messages; + std::vector messageTypes; + ScopedLogContextLevel scopedLogs(CONTEXT_CLIENT, ELogLevel::Warn, [&](ELogLevel msgType, std::string_view message) { + messages.emplace_back(message); + messageTypes.emplace_back(msgType); + }); + + // Set a value and save -> causes warning + nodeBinding->getInputs()->getChild("visibility")->set(false); + ASSERT_TRUE(nodeBinding->impl().isDirty()); + ASSERT_FALSE(saveToFile("LogicEngine.bin")); + + ASSERT_EQ(SCENE_ISSUES + 3u, messages.size()); + EXPECT_EQ("Saving logic engine content with manually updated binding values without calling update() will result in those values being lost!", messages[0]); + EXPECT_EQ("[binding [LogicObject ScnObjId=9]] Node [binding] has no ingoing links! Node should be deleted or properly linked!", messages[1]); + EXPECT_EQ("Failed to saveToFile() because validation errors were encountered! Refer to the documentation of saveToFile() for details how to address these gracefully.", messages.back()); + EXPECT_EQ(ELogLevel::Warn, messageTypes[0]); + EXPECT_EQ(ELogLevel::Warn, messageTypes[1]); + EXPECT_EQ(ELogLevel::Error, messageTypes.back()); + } + + TEST_P(ALogicEngine_Serialization, ProducesNoErrorIfDeserilizedSuccessfully) + { + saveAndLoadAllTypesOfObjects(); + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + { + auto moduleByName = m_logicEngine->findObject("module"); + auto moduleById = m_logicEngine->findObject(sceneObjectId_t{ 9u }); + ASSERT_NE(nullptr, moduleByName); + ASSERT_EQ(moduleById, moduleByName); + } + { + auto scriptByName = m_logicEngine->findObject("script"); + auto scriptById = m_logicEngine->findObject(sceneObjectId_t{ 10u }); + ASSERT_NE(nullptr, scriptByName); + ASSERT_EQ(scriptById, scriptByName); + const auto inputs = scriptByName->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(0u, inputs->getChildCount()); + EXPECT_TRUE(scriptByName->impl().isDirty()); + } + { + auto rNodeBindingByName = m_logicEngine->findObject("nodeBinding"); + auto rNodeBindingById = m_logicEngine->findObject(sceneObjectId_t{ 11u }); + ASSERT_NE(nullptr, rNodeBindingByName); + ASSERT_EQ(rNodeBindingById, rNodeBindingByName); + const auto inputs = rNodeBindingByName->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(5u, inputs->getChildCount()); + EXPECT_FALSE(rNodeBindingByName->impl().isDirty()); + } + { + auto rCameraBindingByName = m_logicEngine->findObject("cameraBinding"); + auto rCameraBindingById = m_logicEngine->findObject(sceneObjectId_t{ 13u }); + ASSERT_NE(nullptr, rCameraBindingByName); + ASSERT_EQ(rCameraBindingById, rCameraBindingByName); + const auto inputs = rCameraBindingByName->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(2u, inputs->getChildCount()); + EXPECT_FALSE(rCameraBindingByName->impl().isDirty()); + } + { + auto rAppearanceBindingByName = m_logicEngine->findObject("appearanceBinding"); + auto rAppearanceBindingById = m_logicEngine->findObject(sceneObjectId_t{ 12u }); + ASSERT_NE(nullptr, rAppearanceBindingByName); + ASSERT_EQ(rAppearanceBindingById, rAppearanceBindingByName); + const auto inputs = rAppearanceBindingByName->getInputs(); + ASSERT_NE(nullptr, inputs); + + ASSERT_EQ(1u, inputs->getChildCount()); + auto floatUniform = inputs->getChild(0); + ASSERT_NE(nullptr, floatUniform); + EXPECT_EQ("floatUniform", floatUniform->getName()); + EXPECT_EQ(EPropertyType::Float, floatUniform->getType()); + EXPECT_FALSE(rAppearanceBindingByName->impl().isDirty()); + } + { + const auto dataArrayByName = m_logicEngine->findObject("dataArray"); + const auto dataArrayById = m_logicEngine->findObject(sceneObjectId_t{ 14u }); + ASSERT_NE(nullptr, dataArrayByName); + ASSERT_EQ(dataArrayById, dataArrayByName); + EXPECT_EQ(EPropertyType::Float, dataArrayByName->getDataType()); + ASSERT_NE(nullptr, dataArrayByName->getData()); + const std::vector expectedData{ 1.f, 2.f, 3.f }; + EXPECT_EQ(expectedData, *m_logicEngine->findObject("dataArray")->getData()); + + const auto animNodeByName = m_logicEngine->findObject("animNode"); + const auto animNodeById = m_logicEngine->findObject(sceneObjectId_t{ 15u }); + ASSERT_NE(nullptr, animNodeByName); + ASSERT_EQ(animNodeById, animNodeByName); + ASSERT_EQ(1u, animNodeByName->getChannels().size()); + EXPECT_EQ(dataArrayByName, animNodeByName->getChannels().front().timeStamps); + EXPECT_EQ(dataArrayByName, animNodeByName->getChannels().front().keyframes); + } + { + auto rpBindingByName = m_logicEngine->findObject("rpBinding"); + auto rpBindingById = m_logicEngine->findObject(sceneObjectId_t{ 18u }); + ASSERT_NE(nullptr, rpBindingByName); + ASSERT_EQ(rpBindingById, rpBindingByName); + const auto inputs = rpBindingByName->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(4u, inputs->getChildCount()); + EXPECT_FALSE(rpBindingByName->impl().isDirty()); + + auto anchorByName = m_logicEngine->findObject("anchor"); + auto anchorById = m_logicEngine->findObject(sceneObjectId_t{ 19u }); + ASSERT_NE(nullptr, anchorByName); + ASSERT_EQ(anchorById, anchorByName); + const auto outputs = anchorByName->getOutputs(); + ASSERT_NE(nullptr, outputs); + EXPECT_EQ(2u, outputs->getChildCount()); + } + { + auto rgBindingByName = m_logicEngine->findObject("renderGroupBinding"); + auto rgBindingById = m_logicEngine->findObject(sceneObjectId_t{ 20u }); + ASSERT_NE(nullptr, rgBindingByName); + ASSERT_EQ(rgBindingById, rgBindingByName); + const auto inputs = rgBindingByName->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(1u, inputs->getChildCount()); + EXPECT_EQ(1u, inputs->getChild("renderOrders")->getChildCount()); + EXPECT_FALSE(rgBindingByName->impl().isDirty()); + } + { + auto skinByName = m_logicEngine->findObject("skin"); + auto skinById = m_logicEngine->findObject(sceneObjectId_t{ 21u }); + ASSERT_NE(nullptr, skinByName); + ASSERT_EQ(skinById, skinByName); + } + { + auto meshBindingByName = m_logicEngine->findObject("mb"); + auto meshBindingById = m_logicEngine->findObject(sceneObjectId_t{ 22u }); + ASSERT_NE(nullptr, meshBindingByName); + ASSERT_EQ(meshBindingById, meshBindingByName); + const auto inputs = meshBindingByName->getInputs(); + ASSERT_NE(nullptr, inputs); + EXPECT_EQ(4u, inputs->getChildCount()); + EXPECT_FALSE(meshBindingByName->impl().isDirty()); + } + } + } + + TEST_P(ALogicEngine_Serialization, ReplacesCurrentStateWithStateFromFile) + { + { + LogicEngine& logicEngine = *m_logicEngine; + logicEngine.createLuaScript(R"( + function interface(IN,OUT) + IN.param = Type:Int32() + end + function run(IN,OUT) + end + )", {}, "luascript"); + + logicEngine.createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + { + m_logicEngine->createLuaScript(R"( + function interface(IN,OUT) + IN.param2 = Type:Float() + end + function run(IN,OUT) + end + )", {}, "luascript2"); + + m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding2"); + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + { + ASSERT_EQ(nullptr, m_logicEngine->findObject("luascript2")); + ASSERT_EQ(nullptr, m_logicEngine->findObject("binding2")); + + auto script = m_logicEngine->findObject("luascript"); + ASSERT_NE(nullptr, script); + auto rNodeBinding = m_logicEngine->findObject("binding"); + ASSERT_NE(nullptr, rNodeBinding); + EXPECT_EQ(m_node, &rNodeBinding->getRamsesNode()); + } + } + } + + TEST_P(ALogicEngine_Serialization, DeserializesLinks) + { + { + std::string_view scriptSource = R"( + function interface(IN,OUT) + IN.input = Type:Int32() + OUT.output = Type:Int32() + end + function run(IN,OUT) + end + )"; + + LogicEngine& logicEngine = *m_logicEngine; + auto sourceScript1 = logicEngine.createLuaScript(scriptSource, {}, "SourceScript1"); + auto targetScript1 = logicEngine.createLuaScript(scriptSource, {}, "TargetScript1"); + auto sourceScript2 = logicEngine.createLuaScript(scriptSource, {}, "SourceScript2"); + auto targetScript2 = logicEngine.createLuaScript(scriptSource, {}, "TargetScript2"); + logicEngine.createLuaScript(scriptSource, {}, "NotLinkedScript"); + + auto srcOutput1 = sourceScript1->getOutputs()->getChild("output"); + auto tgtInput1 = targetScript1->getInputs()->getChild("input"); + auto srcOutput2 = sourceScript2->getOutputs()->getChild("output"); + auto tgtInput2 = targetScript2->getInputs()->getChild("input"); + + EXPECT_TRUE(logicEngine.link(*srcOutput1, *tgtInput1)); + EXPECT_TRUE(logicEngine.linkWeak(*srcOutput2, *tgtInput2)); + + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + { + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + auto sourceScript1 = m_logicEngine->findObject("SourceScript1"); + auto targetScript1 = m_logicEngine->findObject("TargetScript1"); + auto sourceScript2 = m_logicEngine->findObject("SourceScript2"); + auto targetScript2 = m_logicEngine->findObject("TargetScript2"); + auto notLinkedScript = m_logicEngine->findObject("NotLinkedScript"); + + EXPECT_TRUE(m_logicEngine->isLinked(*sourceScript1)); + EXPECT_TRUE(m_logicEngine->isLinked(*targetScript1)); + EXPECT_TRUE(m_logicEngine->isLinked(*sourceScript2)); + EXPECT_TRUE(m_logicEngine->isLinked(*targetScript2)); + EXPECT_FALSE(m_logicEngine->isLinked(*notLinkedScript)); + + const LogicNodeDependencies& internalNodeDependencies = m_logicEngine->impl().getApiObjects().getLogicNodeDependencies(); + EXPECT_TRUE(internalNodeDependencies.isLinked(sourceScript1->impl())); + EXPECT_TRUE(internalNodeDependencies.isLinked(targetScript1->impl())); + EXPECT_TRUE(internalNodeDependencies.isLinked(sourceScript2->impl())); + EXPECT_TRUE(internalNodeDependencies.isLinked(targetScript2->impl())); + + const auto srcOutput1 = sourceScript1->getOutputs()->getChild("output"); + const auto tgtInput1 = targetScript1->getInputs()->getChild("input"); + const auto srcOutput2 = sourceScript2->getOutputs()->getChild("output"); + const auto tgtInput2 = targetScript2->getInputs()->getChild("input"); + PropertyLinkTestUtils::ExpectLinks(*m_logicEngine, { + { srcOutput1, tgtInput1, false }, + { srcOutput2, tgtInput2, true } + }); + + const auto& srcOutLinks1 = srcOutput1->impl().getOutgoingLinks(); + const auto& srcOutLinks2 = srcOutput2->impl().getOutgoingLinks(); + ASSERT_EQ(1u, srcOutLinks1.size()); + ASSERT_EQ(1u, srcOutLinks2.size()); + EXPECT_EQ(&tgtInput1->impl(), srcOutLinks1[0].property); + EXPECT_EQ(&tgtInput2->impl(), srcOutLinks2[0].property); + EXPECT_FALSE(srcOutLinks1[0].isWeakLink); + EXPECT_TRUE(srcOutLinks2[0].isWeakLink); + EXPECT_EQ(&srcOutput1->impl(), tgtInput1->impl().getIncomingLink().property); + EXPECT_EQ(&srcOutput2->impl(), tgtInput2->impl().getIncomingLink().property); + EXPECT_FALSE(tgtInput1->impl().getIncomingLink().isWeakLink); + EXPECT_TRUE(tgtInput2->impl().getIncomingLink().isWeakLink); + } + } + + TEST_P(ALogicEngine_Serialization, InternalLinkDataIsDeletedAfterDeserialization) + { + std::string_view scriptSource = R"( + function interface(IN,OUT) + IN.input = Type:Int32() + OUT.output = Type:Int32() + end + function run(IN,OUT) + end + )"; + + auto sourceScript = m_logicEngine->createLuaScript(scriptSource, {}, "SourceScript"); + auto targetScript = m_logicEngine->createLuaScript(scriptSource, {}, "TargetScript"); + + // Save logic engine state without links to file + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + + // Create link (should be wiped after loading from file) + auto output = sourceScript->getOutputs()->getChild("output"); + auto input = targetScript->getInputs()->getChild("input"); + m_logicEngine->link(*output, *input); + + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + + auto sourceScriptAfterLoading = m_logicEngine->findObject("SourceScript"); + auto targetScriptAfterLoading = m_logicEngine->findObject("TargetScript"); + + // Make a copy of the object so that we can call non-const methods on it too (getTopologicallySortedNodes()) + // This can't happen in user code, we only do this to test internal data + LogicNodeDependencies internalNodeDependencies = m_logicEngine->impl().getApiObjects().getLogicNodeDependencies(); + ASSERT_TRUE(internalNodeDependencies.getTopologicallySortedNodes().has_value()); + + // New objects are not linked (because they weren't before saving) + EXPECT_FALSE(m_logicEngine->isLinked(*sourceScriptAfterLoading)); + EXPECT_FALSE(m_logicEngine->isLinked(*targetScriptAfterLoading)); + EXPECT_FALSE(internalNodeDependencies.isLinked(sourceScriptAfterLoading->impl())); + EXPECT_FALSE(internalNodeDependencies.isLinked(sourceScriptAfterLoading->impl())); + + // Internal topological graph has two unsorted nodes, before and after update() + EXPECT_EQ(2u, (*internalNodeDependencies.getTopologicallySortedNodes()).size()); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(2u, (*internalNodeDependencies.getTopologicallySortedNodes()).size()); + } + + TEST_P(ALogicEngine_Serialization, PreviouslyCreatedModulesAreDeletedInSolStateAfterDeserialization) + { + { + LogicEngine& logicEngineForSaving = *m_logicEngine; + const std::string_view moduleSrc = R"( + local mymath = {} + mymath.PI=3.1415 + return mymath + )"; + + std::string_view script = R"( + modules("mymath") + function interface(IN,OUT) + OUT.pi = Type:Float() + end + function run(IN,OUT) + OUT.pi = mymath.PI + end + )"; + + LuaModule* mymath = logicEngineForSaving.createLuaModule(moduleSrc, {}, "mymath"); + LuaConfig config; + config.addDependency("mymath", *mymath); + logicEngineForSaving.createLuaScript(script, config, "script"); + + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + + // Create a module with name colliding with the one from file - it should be deleted + const std::string_view moduleToBeWipedSrc = R"( + local mymath = {} + mymath.PI=4 + return mymath + )"; + + // This module will be overwritten when loading the file below. The logic engine should not + // keep any leftovers from modules or scripts when loading from file - all content should be + // taken from the file! + LuaModule* moduleToBeWiped = m_logicEngine->createLuaModule(moduleToBeWipedSrc, {}, "mymath"); + EXPECT_NE(nullptr, moduleToBeWiped); + + ASSERT_TRUE(recreateFromFile("LogicEngine.bin")); + + m_logicEngine->update(); + + auto script = m_logicEngine->findObject("script"); + + // This is the PI from the loaded module, not from 'moduleToBeWiped' + EXPECT_FLOAT_EQ(3.1415f, *script->getOutputs()->getChild("pi")->get()); + } + + TEST_P(ALogicEngine_Serialization, IDsAfterDeserializationAreUnique) + { + sceneObjectId_t serializedId{}; + { + LogicEngine& logicEngine = *m_logicEngine; + const auto script = logicEngine.createLuaScript(R"( + function interface(IN,OUT) + IN.param = Type:Int32() + end + function run(IN,OUT) + end + )", {}, "luascript"); + serializedId = script->getSceneObjectId(); + + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + auto script = m_logicEngine->findObject(serializedId); + ASSERT_NE(nullptr, script); + EXPECT_EQ("luascript", script->getName()); + + const auto anotherScript = m_logicEngine->createLuaScript(R"( + function interface(IN,OUT) + end + function run(IN,OUT) + end + )"); + EXPECT_GT(anotherScript->getSceneObjectId().getValue(), script->getSceneObjectId().getValue()); + } + + TEST_P(ALogicEngine_Serialization, persistsUserIds) + { + { + LogicEngine& logicEngine = *m_logicEngine; + auto* luaModule = logicEngine.createLuaModule(m_moduleSourceCode, {}, "module"); + auto* luaScript = logicEngine.createLuaScript(m_valid_empty_script, {}, "script"); + auto* nodeBinding = logicEngine.createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); + auto* appearanceBinding = logicEngine.createAppearanceBinding(*m_appearance, "appearanceBinding"); + auto* cameraBinding = logicEngine.createCameraBinding(*m_camera, "cameraBinding"); + auto* dataArray = logicEngine.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataArray"); + AnimationNodeConfig config; + config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); + auto* animationNode = logicEngine.createAnimationNode(config, "animNode"); + auto* timerNode = logicEngine.createTimerNode("timerNode"); + auto* luaInterface = logicEngine.createLuaInterface(R"( + function interface(IN, OUT) + end + )", "intf"); + auto* rpBinding = logicEngine.createRenderPassBinding(*m_renderPass, "rpBinding"); + auto* anchor = logicEngine.createAnchorPoint(*nodeBinding, *cameraBinding, "anchor"); + auto* rgBinding = createRenderGroupBinding(logicEngine); + auto* skin = createSkinBinding(logicEngine); + auto* meshBinding = logicEngine.createMeshNodeBinding(*m_meshNode, "mb"); + + EXPECT_TRUE(luaModule->setUserId(1u, 2u)); + EXPECT_TRUE(luaScript->setUserId(3u, 4u)); + EXPECT_TRUE(nodeBinding->setUserId(5u, 6u)); + EXPECT_TRUE(appearanceBinding->setUserId(7u, 8u)); + EXPECT_TRUE(cameraBinding->setUserId(9u, 10u)); + EXPECT_TRUE(dataArray->setUserId(11u, 12u)); + EXPECT_TRUE(animationNode->setUserId(13u, 14u)); + EXPECT_TRUE(timerNode->setUserId(15u, 16u)); + EXPECT_TRUE(luaInterface->setUserId(17u, 18u)); + EXPECT_TRUE(rpBinding->setUserId(19u, 20u)); + EXPECT_TRUE(anchor->setUserId(21u, 22u)); + EXPECT_TRUE(rgBinding->setUserId(23u, 24u)); + EXPECT_TRUE(skin->setUserId(25u, 26u)); + EXPECT_TRUE(meshBinding->setUserId(27u, 28u)); + + ASSERT_TRUE(saveToFileWithoutValidation("LogicEngine.bin")); + } + + EXPECT_TRUE(recreateFromFile("LogicEngine.bin")); + expectNoError(); + + auto obj = m_logicEngine->findObject("module"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(1u, obj->getUserId().first); + EXPECT_EQ(2u, obj->getUserId().second); + + obj = m_logicEngine->findObject("script"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(3u, obj->getUserId().first); + EXPECT_EQ(4u, obj->getUserId().second); + + obj = m_logicEngine->findObject("nodeBinding"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(5u, obj->getUserId().first); + EXPECT_EQ(6u, obj->getUserId().second); + + obj = m_logicEngine->findObject("appearanceBinding"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(7u, obj->getUserId().first); + EXPECT_EQ(8u, obj->getUserId().second); + + obj = m_logicEngine->findObject("cameraBinding"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(9u, obj->getUserId().first); + EXPECT_EQ(10u, obj->getUserId().second); + + obj = m_logicEngine->findObject("dataArray"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(11u, obj->getUserId().first); + EXPECT_EQ(12u, obj->getUserId().second); + + obj = m_logicEngine->findObject("animNode"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(13u, obj->getUserId().first); + EXPECT_EQ(14u, obj->getUserId().second); + + obj = m_logicEngine->findObject("timerNode"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(15u, obj->getUserId().first); + EXPECT_EQ(16u, obj->getUserId().second); + + obj = m_logicEngine->findObject("intf"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(17u, obj->getUserId().first); + EXPECT_EQ(18u, obj->getUserId().second); + + obj = m_logicEngine->findObject("rpBinding"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(19u, obj->getUserId().first); + EXPECT_EQ(20u, obj->getUserId().second); + + obj = m_logicEngine->findObject("anchor"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(21u, obj->getUserId().first); + EXPECT_EQ(22u, obj->getUserId().second); + + obj = m_logicEngine->findObject("renderGroupBinding"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(23u, obj->getUserId().first); + EXPECT_EQ(24u, obj->getUserId().second); + + obj = m_logicEngine->findObject("skin"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(25u, obj->getUserId().first); + EXPECT_EQ(26u, obj->getUserId().second); + + obj = m_logicEngine->findObject("mb"); + ASSERT_NE(nullptr, obj); + EXPECT_EQ(27u, obj->getUserId().first); + EXPECT_EQ(28u, obj->getUserId().second); + } + + TEST_P(ALogicEngine_Serialization, persistsLogicObjectImplToHLObjectMapping) + { + const auto objects = saveAndLoadAllTypesOfObjects(); + for (const auto& obj : objects) + { + EXPECT_EQ(obj, &obj->impl().getLogicObject()); + } + } + + TEST_P(ALogicEngine_Serialization, persistsOwnershipOfAllPropertiesByTheirLogicNode) + { + const auto objects = saveAndLoadAllTypesOfObjects(); + + int propsCount = 0; + for (const auto& obj : objects) + { + const auto logicNode = obj->as(); + if (!logicNode) + continue; + + std::deque props{ logicNode->getInputs(), logicNode->getOutputs() }; + while (!props.empty()) + { + const auto prop = props.back(); + props.pop_back(); + if (prop == nullptr) + continue; + + propsCount++; + EXPECT_EQ(obj, &prop->getOwningLogicNode()); + + for (size_t i = 0u; i < prop->getChildCount(); ++i) + props.push_back(prop->getChild(i)); + } + } + + // just check that the iterating over all props works + EXPECT_EQ(50, propsCount); + } + + TEST_P(ALogicEngine_Serialization, persistsPropertyImplToHLObjectMapping) + { + const auto objects = saveAndLoadAllTypesOfObjects(); + + int propsCount = 0; + for (const auto& obj : objects) + { + const auto logicNode = obj->as(); + if (!logicNode) + continue; + + std::deque props{ logicNode->getInputs(), logicNode->getOutputs() }; + while (!props.empty()) + { + const auto prop = props.back(); + props.pop_back(); + if (prop == nullptr) + continue; + + propsCount++; + EXPECT_EQ(prop, &prop->impl().getPropertyInstance()); + + for (size_t i = 0u; i < prop->getChildCount(); ++i) + props.push_back(prop->getChild(i)); + } + } + + // just check that the iterating over all props works + EXPECT_EQ(50, propsCount); + } +} diff --git a/tests/unittests/client/logic/api/LogicEngineTest_SerializedSize.cpp b/tests/unittests/client/logic/api/LogicEngineTest_SerializedSize.cpp new file mode 100644 index 000000000..9a6535ed2 --- /dev/null +++ b/tests/unittests/client/logic/api/LogicEngineTest_SerializedSize.cpp @@ -0,0 +1,370 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/UniformInput.h" + +#include "RamsesTestUtils.h" +#include "FeatureLevelTestValues.h" + +namespace ramses::internal +{ + class ALogicEngine_SerializedSize : public ALogicEngineBase, public ::testing::TestWithParam + { + protected: + ALogicEngine_SerializedSize() + : ALogicEngineBase{ GetParam() } + { + } + + const std::string_view m_valid_empty_interface = R"( + function interface(IN,OUT) + end + )"; + + const std::string_view m_script_source_code = R"( + function interface(IN,OUT) + IN.a = Type:Float() + IN.b = Type:Float() + OUT.value = Type:Float() + end + + function run(IN,OUT) + OUT.value = IN.a + IN.b + end + )"; + + const std::string_view m_script_with_module_source_code = R"( + modules("mymath") + + function interface(IN,OUT) + OUT.v = Type:Int32() + OUT.pi = Type:Float() + end + + function run(IN,OUT) + OUT.v = mymath.add(1,2) + OUT.pi = mymath.PI + end + )"; + + LuaScript* createScript(std::string_view source) + { + auto script = m_logicEngine->createLuaScript(source, {}, "script"); + EXPECT_NE(nullptr, script); + return script; + } + + LuaInterface* createInterface() + { + auto intf = m_logicEngine->createLuaInterface(m_valid_empty_interface, "intf"); + EXPECT_NE(nullptr, intf); + return intf; + } + + SkinBinding* createSkinBinding() + { + const auto node = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeForSkin"); + const auto appearance = m_logicEngine->createAppearanceBinding(*m_appearance, "appearanceForSkin"); + const auto optUniform = appearance->getRamsesAppearance().getEffect().findUniformInput("jointMat"); + EXPECT_TRUE(optUniform.has_value()); + assert(optUniform != std::nullopt); + return m_logicEngine->createSkinBinding({ node }, { matrix44f{ 0.f } }, *appearance, *optUniform, "skin"); + } + }; + + static constexpr size_t EmptySerializedSizeTotal{ 164u }; + + INSTANTIATE_TEST_SUITE_P( + ALogicEngine_SerializedSizeTests, + ALogicEngine_SerializedSize, + ramses::internal::GetFeatureLevelTestValues()); + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithoutContent) + { + EXPECT_EQ(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), EmptySerializedSizeTotal); + + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 0u); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithInterface) + { + createInterface(); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 112u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithModule) + { + this->m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "module"); + const auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceCodeOnly); + EXPECT_EQ(result, 232u); + } + + // TODO figure out how to deal with difference in exported size on different platforms + // byte code has different size on 32bit arch (currently known) + TEST_P(ALogicEngine_SerializedSize, DISABLED_ChecksSerializedSizeWithModule_withByteCode) + { + this->m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "module"); + + auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::ByteCodeOnly); + EXPECT_EQ(result, 366u); + + result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceAndByteCode); + EXPECT_EQ(result, 550u); + + // default is source and bytecode + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), + this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceAndByteCode)); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithEmptyScript) + { + createScript(m_valid_empty_script); + const auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceCodeOnly); + EXPECT_EQ(result, 312u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(ELuaSavingMode::SourceCodeOnly), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithScript) + { + this->m_logicEngine->createLuaScript(m_script_source_code, {}, "script"); + const auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceCodeOnly); + EXPECT_EQ(result, 616u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(ELuaSavingMode::SourceCodeOnly), EmptySerializedSizeTotal); + } + + // TODO figure out how to deal with difference in exported size on different platforms + // byte code has different size on 32bit arch (currently known) + TEST_P(ALogicEngine_SerializedSize, DISABLED_ChecksSerializedSizeWithScript_withByteCode) + { + this->m_logicEngine->createLuaScript(m_script_source_code, {}, "script"); + + auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::ByteCodeOnly); + EXPECT_EQ(result, 912u); + + result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceAndByteCode); + EXPECT_EQ(result, 1184u); + + // default is source and bytecode + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), + this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceAndByteCode)); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithScriptAndModuleDependency) + { + LuaConfig config; + const auto module = this->m_logicEngine->createLuaModule(m_moduleSourceCode, config, "module"); + config.addDependency("mymath", *module); + this->m_logicEngine->createLuaScript(m_script_with_module_source_code, config, "script"); + + const auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceCodeOnly); + EXPECT_EQ(result, 624u); + } + + // TODO figure out how to deal with difference in exported size on different platforms + // byte code has different size on 32bit arch (currently known) + TEST_P(ALogicEngine_SerializedSize, DISABLED_ChecksSerializedSizeWithScriptAndModuleDependency_withByteCode) + { + LuaConfig config; + const auto module = this->m_logicEngine->createLuaModule(m_moduleSourceCode, config, "module"); + config.addDependency("mymath", *module); + this->m_logicEngine->createLuaScript(m_script_with_module_source_code, config, "script"); + + auto result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::ByteCodeOnly); + EXPECT_EQ(result, 1000u); + + result = this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceAndByteCode); + EXPECT_EQ(result, 1304u); + + // default is source and bytecode + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), + this->m_logicEngine->getSerializedSize(ELuaSavingMode::SourceAndByteCode)); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithNodeBinding) + { + this->m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); + const auto result = this->m_logicEngine->getSerializedSize(); + EXPECT_EQ(result, 440u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithAppearanceBinding) + { + this->m_logicEngine->createAppearanceBinding(*m_appearance, "appearance"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 256u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCameraBinding) + { + this->m_logicEngine->createCameraBinding(*m_camera, "camera"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 728u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCameraBindingWithFrustumPlanes) + { + this->m_logicEngine->createCameraBindingWithFrustumPlanes(*m_camera, "camera"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 728u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithRenderPassBinding) + { + this->m_logicEngine->createRenderPassBinding(*m_renderPass, "renderpass"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 376u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithRenderGroupBinding) + { + RenderGroupBindingElements elements; + EXPECT_TRUE(elements.addElement(*m_meshNode)); + this->m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements, "rg"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 336u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithMeshNodeBinding) + { + this->m_logicEngine->createMeshNodeBinding(*m_meshNode, "mb"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 376u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + // Since there is a template specialization for animations collecting data arrays + // we test with different data arrays and reusing one single data array. + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithLinearAnimation_differentArrays) + { + // Test animation channel with different data arrays + auto dataArray1 = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data1"); + auto dataArray2 = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data2"); + ASSERT_NE(nullptr, dataArray1); + ASSERT_NE(nullptr, dataArray2); + + AnimationNodeConfig config; + config.addChannel({ "channel", dataArray1, dataArray2, EInterpolationType::Linear }); + this->m_logicEngine->createAnimationNode(config, "animation"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 378u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithLinearAnimation_sameArray) + { + // Test animation channel with the same data arrays + auto data = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); + ASSERT_NE(nullptr, data); + + AnimationNodeConfig config; + config.addChannel({ "channel", data, data, EInterpolationType::Linear }); + this->m_logicEngine->createAnimationNode(config, "animation"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 378u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + // Since there is a template specialization for animations collecting data arrays + // we test with different data arrays and reusing one single data array. + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCubicAnimation_differentArrays) + { + // Test animation channel with different data arrays + auto dataArray1 = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data1"); + auto dataArray2 = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data2"); + auto dataArray3 = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data3"); + auto dataArray4 = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data4"); + ASSERT_NE(nullptr, dataArray1); + ASSERT_NE(nullptr, dataArray2); + ASSERT_NE(nullptr, dataArray3); + ASSERT_NE(nullptr, dataArray4); + + AnimationNodeConfig config; + config.addChannel({ "channel", dataArray1, dataArray2, EInterpolationType::Cubic, dataArray3, dataArray4 }); + this->m_logicEngine->createAnimationNode(config, "animation"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 378u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithCubicAnimation_sameArray) + { + // Test animation channel with the same data arrays + auto data = this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); + ASSERT_NE(nullptr, data); + + AnimationNodeConfig config; + config.addChannel({ "channel", data, data, EInterpolationType::Cubic, data, data }); + this->m_logicEngine->createAnimationNode(config, "animation"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 378u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithDataArray) + { + this->m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 98u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithTimer) + { + this->m_logicEngine->createTimerNode("timer"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 290u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithAnchorPoint) + { + const auto node = this->m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); + const auto camera = this->m_logicEngine->createCameraBinding(*m_camera, "camera"); + ASSERT_TRUE(node && camera); + this->m_logicEngine->createAnchorPoint(*node, *camera, "timer"); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 248u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } + + TEST_P(ALogicEngine_SerializedSize, ChecksSerializedSizeWithSkinBinding) + { + createSkinBinding(); + EXPECT_EQ(this->m_logicEngine->getSerializedSize(), 184u); + EXPECT_GT(this->m_logicEngine->getTotalSerializedSize(), EmptySerializedSizeTotal); + } +} diff --git a/client/logic/unittests/api/LogicEngineTest_Update.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Update.cpp similarity index 63% rename from client/logic/unittests/api/LogicEngineTest_Update.cpp rename to tests/unittests/client/logic/api/LogicEngineTest_Update.cpp index b3c549e64..86308eb9c 100644 --- a/client/logic/unittests/api/LogicEngineTest_Update.cpp +++ b/tests/unittests/client/logic/api/LogicEngineTest_Update.cpp @@ -12,33 +12,34 @@ #include "RamsesTestUtils.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Node.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/Effect.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Node.h" -#include "impl/LogicNodeImpl.h" -#include "impl/LogicEngineImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/LuaScriptImpl.h" #include "fmt/format.h" -namespace ramses +namespace ramses::internal { class ALogicEngine_Update : public ALogicEngine { }; - TEST_F(ALogicEngine_Update, UpdatesRamsesNodeBindingValuesOnUpdate) + TEST_F(ALogicEngine_Update, UpdatesNodeBindingValuesOnUpdate) { - auto luaScript = m_logicEngine.createLuaScript(R"( + auto luaScript = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.param = Type:Bool() OUT.param = Type:Bool() @@ -48,27 +49,24 @@ namespace ramses end )"); - auto ramsesNodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); auto scriptInput = luaScript->getInputs()->getChild("param"); auto scriptOutput = luaScript->getOutputs()->getChild("param"); - auto nodeInput = ramsesNodeBinding->getInputs()->getChild("visibility"); + auto nodeInput = nodeBinding->getInputs()->getChild("visibility"); scriptInput->set(true); nodeInput->set(false); - m_logicEngine.link(*scriptOutput, *nodeInput); + m_logicEngine->link(*scriptOutput, *nodeInput); EXPECT_FALSE(*nodeInput->get()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_TRUE(*nodeInput->get()); } - TEST_F(ALogicEngine_Update, UpdatesRamsesCameraBindingValuesOnUpdate) + TEST_F(ALogicEngine_Update, UpdatesCameraBindingValuesOnUpdate) { - RamsesTestSetup testSetup; - ramses::Scene* scene = testSetup.createScene(); - - auto luaScript = m_logicEngine.createLuaScript(R"( + auto luaScript = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.param = Type:Int32() OUT.param = Type:Int32() @@ -78,26 +76,23 @@ namespace ramses end )"); - auto ramsesCameraBinding = m_logicEngine.createRamsesCameraBinding(*scene->createPerspectiveCamera(), "CameraBinding"); + auto cameraBinding = m_logicEngine->createCameraBinding(*m_scene->createPerspectiveCamera(), "CameraBinding"); auto scriptInput = luaScript->getInputs()->getChild("param"); auto scriptOutput = luaScript->getOutputs()->getChild("param"); - auto cameraInput = ramsesCameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"); + auto cameraInput = cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"); scriptInput->set(34); cameraInput->set(21); - m_logicEngine.link(*scriptOutput, *cameraInput); + m_logicEngine->link(*scriptOutput, *cameraInput); EXPECT_EQ(21, *cameraInput->get()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(34, *cameraInput->get()); } - TEST_F(ALogicEngine_Update, UpdatesARamsesAppearanceBinding) + TEST_F(ALogicEngine_Update, UpdatesAAppearanceBinding) { - RamsesTestSetup testSetup; - ramses::Scene* scene = testSetup.createScene(); - ramses::EffectDescription effectDesc; effectDesc.setFragmentShader(R"( #version 100 @@ -118,26 +113,26 @@ namespace ramses gl_Position = floatUniform * vec4(a_position, 1.0); })"); - const ramses::Effect* effect = scene->createEffect(effectDesc); - ramses::Appearance* appearance = scene->createAppearance(*effect); + const ramses::Effect* effect = m_scene->createEffect(effectDesc); + ramses::Appearance* appearance = m_scene->createAppearance(*effect); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*appearance, "appearancebinding"); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*appearance, "appearancebinding"); auto floatUniform = appearanceBinding->getInputs()->getChild("floatUniform"); floatUniform->set(47.11f); - m_logicEngine.update(); + m_logicEngine->update(); - ramses::UniformInput floatInput; - effect->findUniformInput("floatUniform", floatInput); + const auto floatInput = effect->findUniformInput("floatUniform"); + ASSERT_TRUE(floatInput.has_value()); float result = 0.0f; - appearance->getInputValue(floatInput, result); + appearance->getInputValue(*floatInput, result); EXPECT_FLOAT_EQ(47.11f, result); } TEST_F(ALogicEngine_Update, ProducesErrorIfLinkedScriptHasRuntimeError) { - auto scriptSource = R"( + auto scriptSource = R"( function interface(IN,OUT) IN.param = Type:Bool() OUT.param = Type:Bool() @@ -147,20 +142,17 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSource, WithStdModules({EStandardModule::Base})); - auto targetScript = m_logicEngine.createLuaScript(scriptSource, WithStdModules({EStandardModule::Base})); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource, WithStdModules({EStandardModule::Base})); + auto targetScript = m_logicEngine->createLuaScript(scriptSource, WithStdModules({EStandardModule::Base})); auto output = sourceScript->getOutputs()->getChild("param"); auto input = targetScript->getInputs()->getChild("param"); input->set(true); - m_logicEngine.link(*output, *input); + m_logicEngine->link(*output, *input); - EXPECT_FALSE(m_logicEngine.update()); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("This will die")); - EXPECT_THAT(m_logicEngine.getErrors()[0].object, sourceScript); + EXPECT_FALSE(m_logicEngine->update()); + expectErrorSubstring("This will die", sourceScript); } TEST_F(ALogicEngine_Update, PropagatesValuesOnlyToConnectedLogicNodes) @@ -181,10 +173,10 @@ namespace ramses end )"; - auto script = m_logicEngine.createLuaScript(scriptSource); - auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); - auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*m_camera, "CameraBinding"); + auto script = m_logicEngine->createLuaScript(scriptSource); + auto nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*m_appearance, "AppearanceBinding"); + auto cameraBinding = m_logicEngine->createCameraBinding(*m_camera, "CameraBinding"); auto nodeBindingTranslation = nodeBinding->getInputs()->getChild("translation"); nodeBindingTranslation->set(vec3f{1.f, 2.f, 3.f}); @@ -193,12 +185,12 @@ namespace ramses auto cameraBindingViewportOffsetX = cameraBinding->getInputs()->getChild("viewport")->getChild("offsetX"); cameraBindingViewportOffsetX->set(43); - m_logicEngine.update(); + m_logicEngine->update(); - ramses::UniformInput floatInput; - m_appearance->getEffect().findUniformInput("floatUniform", floatInput); + const auto floatInput = m_appearance->getEffect().findUniformInput("floatUniform"); + ASSERT_TRUE(floatInput.has_value()); float floatUniformValue = 0.0f; - m_appearance->getInputValue(floatInput, floatUniformValue); + m_appearance->getInputValue(*floatInput, floatUniformValue); EXPECT_FLOAT_EQ(42.f, floatUniformValue); EXPECT_EQ(43, m_camera->getViewportX()); @@ -218,12 +210,12 @@ namespace ramses auto scriptInputInt = script->getInputs()->getChild("inInt"); auto appearanceInput = appearanceBinding->getInputs()->getChild("floatUniform"); - m_logicEngine.link(*scriptOutputVec3, *nodeBindingScaling); + m_logicEngine->link(*scriptOutputVec3, *nodeBindingScaling); scriptInputVec3->set(vec3f{3.f, 2.f, 1.f}); scriptInputFloat->set(42.f); scriptInputInt->set(43); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, floatUniformValue); EXPECT_EQ(43, m_camera->getViewportX()); { @@ -242,29 +234,29 @@ namespace ramses EXPECT_EQ(values, vec3f(0.f, 0.f, 0.f)); } - ramses::UniformInput floatUniform; - m_appearance->getEffect().findUniformInput("floatUniform", floatUniform); + const auto floatUniform = m_appearance->getEffect().findUniformInput("floatUniform"); + ASSERT_TRUE(floatUniform.has_value()); floatUniformValue = 0.0f; - m_appearance->getInputValue(floatUniform, floatUniformValue); + m_appearance->getInputValue(*floatUniform, floatUniformValue); EXPECT_FLOAT_EQ(42.f, floatUniformValue); - m_logicEngine.link(*scriptOutputFloat, *appearanceInput); - m_logicEngine.link(*scriptOutputInt, *cameraBindingVpY); + m_logicEngine->link(*scriptOutputFloat, *appearanceInput); + m_logicEngine->link(*scriptOutputInt, *cameraBindingVpY); - m_logicEngine.update(); + m_logicEngine->update(); - m_appearance->getInputValue(floatUniform, floatUniformValue); + m_appearance->getInputValue(*floatUniform, floatUniformValue); EXPECT_FLOAT_EQ(42.f, floatUniformValue); EXPECT_EQ(43, m_camera->getViewportY()); - m_logicEngine.unlink(*scriptOutputVec3, *nodeBindingScaling); + m_logicEngine->unlink(*scriptOutputVec3, *nodeBindingScaling); } TEST_F(ALogicEngine_Update, OnlyUpdatesDirtyLogicNodes) { - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); auto scriptSource = R"( function interface(IN,OUT) @@ -276,55 +268,55 @@ namespace ramses end )"; - auto sourceScript = m_logicEngine.createLuaScript(scriptSource, {}); - auto targetScript = m_logicEngine.createLuaScript(scriptSource, {}); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource, {}); + auto targetScript = m_logicEngine->createLuaScript(scriptSource, {}); auto sourceInput = sourceScript->getInputs()->getChild("inFloat"); auto sourceOutput = sourceScript->getOutputs()->getChild("outFloat"); auto targetInput = targetScript->getInputs()->getChild("inFloat"); - EXPECT_TRUE(sourceScript->m_impl.isDirty()); - EXPECT_TRUE(targetScript->m_impl.isDirty()); + EXPECT_TRUE(sourceScript->impl().isDirty()); + EXPECT_TRUE(targetScript->impl().isDirty()); - m_logicEngine.link(*sourceOutput, *targetInput); + m_logicEngine->link(*sourceOutput, *targetInput); - EXPECT_TRUE(sourceScript->m_impl.isDirty()); - EXPECT_TRUE(targetScript->m_impl.isDirty()); + EXPECT_TRUE(sourceScript->impl().isDirty()); + EXPECT_TRUE(targetScript->impl().isDirty()); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetScript->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetScript->impl().isDirty()); // both scripts are updated, because its the first update - auto executedNodes = m_logicEngine.getLastUpdateReport().getNodesExecuted(); + auto executedNodes = m_logicEngine->getLastUpdateReport().getNodesExecuted(); ASSERT_EQ(2u, executedNodes.size()); EXPECT_EQ(sourceScript, executedNodes[0].first); EXPECT_EQ(targetScript, executedNodes[1].first); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_TRUE(m_logicEngine.getLastUpdateReport().getNodesExecuted().empty()); + EXPECT_TRUE(m_logicEngine->getLastUpdateReport().getNodesExecuted().empty()); targetInput->set(42.f); // targetScript is linked input and cannot be set manually so it is not dirty - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetScript->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetScript->impl().isDirty()); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_FALSE(sourceScript->m_impl.isDirty()); - EXPECT_FALSE(targetScript->m_impl.isDirty()); + EXPECT_FALSE(sourceScript->impl().isDirty()); + EXPECT_FALSE(targetScript->impl().isDirty()); // Nothing is updated, because targetScript is linked input and cannot be set manually - EXPECT_TRUE(m_logicEngine.getLastUpdateReport().getNodesExecuted().empty()); + EXPECT_TRUE(m_logicEngine->getLastUpdateReport().getNodesExecuted().empty()); sourceInput->set(24.f); - m_logicEngine.update(); + m_logicEngine->update(); // Both scripts are updated, because the input of the first script is changed and changes target through link. - executedNodes = m_logicEngine.getLastUpdateReport().getNodesExecuted(); + executedNodes = m_logicEngine->getLastUpdateReport().getNodesExecuted(); ASSERT_EQ(2u, executedNodes.size()); EXPECT_EQ(sourceScript, executedNodes[0].first); EXPECT_EQ(targetScript, executedNodes[1].first); @@ -346,7 +338,7 @@ namespace ramses std::array s = {}; for(auto& si : s) { - si = m_logicEngine.createLuaScript(scriptSource); + si = m_logicEngine->createLuaScript(scriptSource); } auto in1S0 = s[0]->getInputs()->getChild("in1"); @@ -374,22 +366,22 @@ namespace ramses s4 */ - ASSERT_TRUE(m_logicEngine.link(*out1S0, *in2S2)); - ASSERT_TRUE(m_logicEngine.link(*out1S0, *in2S1)); - ASSERT_TRUE(m_logicEngine.link(*out1S1, *in2S3)); - ASSERT_TRUE(m_logicEngine.link(*out1S2, *in1S1)); - ASSERT_TRUE(m_logicEngine.link(*out1S2, *in1S3)); - ASSERT_TRUE(m_logicEngine.link(*out1S3, *in1S5)); - ASSERT_TRUE(m_logicEngine.link(*out1S3, *in1S4)); - ASSERT_TRUE(m_logicEngine.link(*out1S4, *in2S5)); + ASSERT_TRUE(m_logicEngine->link(*out1S0, *in2S2)); + ASSERT_TRUE(m_logicEngine->link(*out1S0, *in2S1)); + ASSERT_TRUE(m_logicEngine->link(*out1S1, *in2S3)); + ASSERT_TRUE(m_logicEngine->link(*out1S2, *in1S1)); + ASSERT_TRUE(m_logicEngine->link(*out1S2, *in1S3)); + ASSERT_TRUE(m_logicEngine->link(*out1S3, *in1S5)); + ASSERT_TRUE(m_logicEngine->link(*out1S3, *in1S4)); + ASSERT_TRUE(m_logicEngine->link(*out1S4, *in2S5)); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); auto expectScriptsExecutedInOrder = [&s, this](std::vector expectedOrder) { - m_logicEngine.update(); + m_logicEngine->update(); - auto executedNodes = m_logicEngine.getLastUpdateReport().getNodesExecuted(); + auto executedNodes = m_logicEngine->getLastUpdateReport().getNodesExecuted(); ASSERT_EQ(expectedOrder.size(), executedNodes.size()); for (size_t i = 0; i < expectedOrder.size(); ++i) { @@ -433,40 +425,40 @@ namespace ramses end )"; - m_logicEngine.m_impl->disableTrackingDirtyNodes(); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->impl().disableTrackingDirtyNodes(); + m_logicEngine->enableUpdateReport(true); - auto sourceScript = m_logicEngine.createLuaScript(scriptSource, {}, "SourceScript"); - auto targetScript = m_logicEngine.createLuaScript(scriptSource, {}, "TargetScript"); + auto sourceScript = m_logicEngine->createLuaScript(scriptSource, {}, "SourceScript"); + auto targetScript = m_logicEngine->createLuaScript(scriptSource, {}, "TargetScript"); auto sourceInput = sourceScript->getInputs()->getChild("inFloat"); auto sourceOutput = sourceScript->getOutputs()->getChild("outFloat"); auto targetInput = targetScript->getInputs()->getChild("inFloat"); - m_logicEngine.link(*sourceOutput, *targetInput); - m_logicEngine.update(); + m_logicEngine->link(*sourceOutput, *targetInput); + m_logicEngine->update(); // both scripts are updated, because its the first update - auto executedNodes = m_logicEngine.getLastUpdateReport().getNodesExecuted(); + auto executedNodes = m_logicEngine->getLastUpdateReport().getNodesExecuted(); ASSERT_EQ(2u, executedNodes.size()); EXPECT_EQ(sourceScript, executedNodes[0].first); EXPECT_EQ(targetScript, executedNodes[1].first); - m_logicEngine.unlink(*sourceOutput, *targetInput); + m_logicEngine->unlink(*sourceOutput, *targetInput); targetInput->set(42.f); - m_logicEngine.update(); + m_logicEngine->update(); // Both scripts are updated, because dirty handling is disabled - executedNodes = m_logicEngine.getLastUpdateReport().getNodesExecuted(); + executedNodes = m_logicEngine->getLastUpdateReport().getNodesExecuted(); ASSERT_EQ(2u, executedNodes.size()); EXPECT_EQ(sourceScript, executedNodes[0].first); EXPECT_EQ(targetScript, executedNodes[1].first); sourceInput->set(24.f); - m_logicEngine.update(); + m_logicEngine->update(); // Both scripts are updated, because dirty handling is disabled - executedNodes = m_logicEngine.getLastUpdateReport().getNodesExecuted(); + executedNodes = m_logicEngine->getLastUpdateReport().getNodesExecuted(); ASSERT_EQ(2u, executedNodes.size()); EXPECT_EQ(sourceScript, executedNodes[0].first); EXPECT_EQ(targetScript, executedNodes[1].first); diff --git a/client/logic/unittests/api/LogicEngineTest_UpdateReport.cpp b/tests/unittests/client/logic/api/LogicEngineTest_UpdateReport.cpp similarity index 66% rename from client/logic/unittests/api/LogicEngineTest_UpdateReport.cpp rename to tests/unittests/client/logic/api/LogicEngineTest_UpdateReport.cpp index baaa3bfac..aef8a6fb2 100644 --- a/client/logic/unittests/api/LogicEngineTest_UpdateReport.cpp +++ b/tests/unittests/client/logic/api/LogicEngineTest_UpdateReport.cpp @@ -8,10 +8,10 @@ #include #include "LogicEngineTest_Base.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/Property.h" #include -namespace ramses +namespace ramses::internal { class ALogicEngine_UpdateReport : public ALogicEngine { @@ -33,7 +33,7 @@ namespace ramses TEST_F(ALogicEngine_UpdateReport, UpdateReportIsEmptyIfDisabledAndStatisticsAlsoDisabled) { // statistics are implicitly disabled - m_logicEngine.setStatisticsLoggingRate(0u); + m_logicEngine->setStatisticsLoggingRate(0u); constexpr auto scriptSource = R"( function interface(IN,OUT) @@ -43,14 +43,14 @@ namespace ramses end )"; - auto node1 = m_logicEngine.createLuaScript(scriptSource); - auto node2 = m_logicEngine.createLuaScript(scriptSource); - auto node3 = m_logicEngine.createLuaScript(scriptSource); + auto node1 = m_logicEngine->createLuaScript(scriptSource); + auto node2 = m_logicEngine->createLuaScript(scriptSource); + auto node3 = m_logicEngine->createLuaScript(scriptSource); // first enable update reporting to fill some data - m_logicEngine.enableUpdateReport(true); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_FALSE(m_logicEngine.getLastUpdateReport().getNodesExecuted().empty()); + m_logicEngine->enableUpdateReport(true); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_FALSE(m_logicEngine->getLastUpdateReport().getNodesExecuted().empty()); // make nodes dirty node1->getInputs()->getChild(0u)->set(13); @@ -58,9 +58,9 @@ namespace ramses node3->getInputs()->getChild(0u)->set(13); // disable reporting and expect empty - m_logicEngine.enableUpdateReport(false); - EXPECT_TRUE(m_logicEngine.update()); - const auto report = m_logicEngine.getLastUpdateReport(); + m_logicEngine->enableUpdateReport(false); + EXPECT_TRUE(m_logicEngine->update()); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_TRUE(report.getNodesExecuted().empty()); EXPECT_TRUE(report.getNodesSkippedExecution().empty()); EXPECT_EQ(report.getTopologySortExecutionTime().count(), 0); @@ -80,39 +80,39 @@ namespace ramses end )"; - auto node1 = m_logicEngine.createLuaScript(scriptSource); - auto node2 = m_logicEngine.createLuaScript(scriptSource); - auto node3 = m_logicEngine.createLuaScript(scriptSource); + auto node1 = m_logicEngine->createLuaScript(scriptSource); + auto node2 = m_logicEngine->createLuaScript(scriptSource); + auto node3 = m_logicEngine->createLuaScript(scriptSource); // link nodes so order of execution is deterministic - m_logicEngine.link(*node1->getOutputs()->getChild(0u), *node2->getInputs()->getChild(0u)); - m_logicEngine.link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); + m_logicEngine->link(*node1->getOutputs()->getChild(0u), *node2->getInputs()->getChild(0u)); + m_logicEngine->link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); // all updated - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); expectReportContainsExecutedNodes(report, { node1, node2, node3 }); EXPECT_TRUE(report.getNodesSkippedExecution().empty()); } // none updated - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_TRUE(report.getNodesExecuted().empty()); EXPECT_THAT(report.getNodesSkippedExecution(), ::testing::ElementsAre(node1, node2, node3)); } // re-link to trigger dirty - m_logicEngine.unlink(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); - m_logicEngine.link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); + m_logicEngine->unlink(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); + m_logicEngine->link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); // node2 and node3 updated - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); expectReportContainsExecutedNodes(report, { node2, node3 }); EXPECT_THAT(report.getNodesSkippedExecution(), ::testing::ElementsAre(node1)); } @@ -128,21 +128,21 @@ namespace ramses end )"; - auto node1 = m_logicEngine.createLuaScript(scriptSource); - auto node2 = m_logicEngine.createLuaScript(scriptSource); + auto node1 = m_logicEngine->createLuaScript(scriptSource); + auto node2 = m_logicEngine->createLuaScript(scriptSource); // first enable update reporting to fill some data - m_logicEngine.enableUpdateReport(true); - EXPECT_TRUE(m_logicEngine.update()); - auto report1 = m_logicEngine.getLastUpdateReport(); + m_logicEngine->enableUpdateReport(true); + EXPECT_TRUE(m_logicEngine->update()); + auto report1 = m_logicEngine->getLastUpdateReport(); - EXPECT_TRUE(m_logicEngine.update()); - auto report2 = m_logicEngine.getLastUpdateReport(); + EXPECT_TRUE(m_logicEngine->update()); + auto report2 = m_logicEngine->getLastUpdateReport(); node1->getInputs()->getChild(0u)->set(13); node2->getInputs()->getChild(0u)->set(13); - EXPECT_TRUE(m_logicEngine.update()); - auto report3 = m_logicEngine.getLastUpdateReport(); + EXPECT_TRUE(m_logicEngine->update()); + auto report3 = m_logicEngine->getLastUpdateReport(); std::vector reports; reports.resize(2u); @@ -173,15 +173,15 @@ namespace ramses end )"; - m_logicEngine.createLuaScript(slowScriptSource); - m_logicEngine.createLuaScript(slowScriptSource); - m_logicEngine.createLuaScript(slowScriptSource); + m_logicEngine->createLuaScript(slowScriptSource); + m_logicEngine->createLuaScript(slowScriptSource); + m_logicEngine->createLuaScript(slowScriptSource); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_EQ(3u, report.getNodesExecuted().size()); EXPECT_TRUE(report.getNodesSkippedExecution().empty()); @@ -210,38 +210,38 @@ namespace ramses end )"; - LuaScript* s1 = m_logicEngine.createLuaScript(scriptSource); - LuaScript* s2 = m_logicEngine.createLuaScript(scriptSource); + LuaScript* s1 = m_logicEngine->createLuaScript(scriptSource); + LuaScript* s2 = m_logicEngine->createLuaScript(scriptSource); - m_logicEngine.link(*s1->getOutputs()->getChild("ints")->getChild("int1"), *s2->getInputs()->getChild("ints")->getChild("int1")); - m_logicEngine.link(*s1->getOutputs()->getChild("ints")->getChild("int2"), *s2->getInputs()->getChild("ints")->getChild("int2")); + m_logicEngine->link(*s1->getOutputs()->getChild("ints")->getChild("int1"), *s2->getInputs()->getChild("ints")->getChild("int1")); + m_logicEngine->link(*s1->getOutputs()->getChild("ints")->getChild("int2"), *s2->getInputs()->getChild("ints")->getChild("int2")); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); // set non-default data s1->getInputs()->getChild("ints")->getChild("int1")->set(5); s1->getInputs()->getChild("ints")->getChild("int2")->set(5); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_EQ(2u, report.getTotalLinkActivations()); } - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_EQ(0u, report.getTotalLinkActivations()); } s1->getInputs()->getChild("ints")->getChild("int1")->set(6); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_EQ(1u, report.getTotalLinkActivations()); } s1->getInputs()->getChild("ints")->getChild("int1")->set(7); s1->getInputs()->getChild("ints")->getChild("int2")->set(7); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); EXPECT_EQ(2u, report.getTotalLinkActivations()); } } @@ -261,25 +261,25 @@ namespace ramses LuaConfig config; config.addStandardModuleDependency(EStandardModule::Base); - auto node1 = m_logicEngine.createLuaScript(scriptSource, config); - auto node2 = m_logicEngine.createLuaScript(scriptSource, config); - auto node3 = m_logicEngine.createLuaScript(scriptSource, config); + auto node1 = m_logicEngine->createLuaScript(scriptSource, config); + auto node2 = m_logicEngine->createLuaScript(scriptSource, config); + auto node3 = m_logicEngine->createLuaScript(scriptSource, config); // link nodes so order of execution is deterministic - m_logicEngine.link(*node1->getOutputs()->getChild(0u), *node2->getInputs()->getChild(0u)); - m_logicEngine.link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); + m_logicEngine->link(*node1->getOutputs()->getChild(0u), *node2->getInputs()->getChild(0u)); + m_logicEngine->link(*node2->getOutputs()->getChild(0u), *node3->getInputs()->getChild(0u)); - m_logicEngine.enableUpdateReport(true); + m_logicEngine->enableUpdateReport(true); // make it fail node1->getInputs()->getChild(0u)->set(33); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); // all updated node1->getInputs()->getChild(0u)->set(1); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); { - const auto report = m_logicEngine.getLastUpdateReport(); + const auto report = m_logicEngine->getLastUpdateReport(); expectReportContainsExecutedNodes(report, { node1, node2, node3 }); EXPECT_TRUE(report.getNodesSkippedExecution().empty()); diff --git a/client/logic/unittests/api/LogicEngineTest_Validation.cpp b/tests/unittests/client/logic/api/LogicEngineTest_Validation.cpp similarity index 52% rename from client/logic/unittests/api/LogicEngineTest_Validation.cpp rename to tests/unittests/client/logic/api/LogicEngineTest_Validation.cpp index 28b433768..2aa2b3b18 100644 --- a/client/logic/unittests/api/LogicEngineTest_Validation.cpp +++ b/tests/unittests/client/logic/api/LogicEngineTest_Validation.cpp @@ -8,77 +8,87 @@ #include "LogicEngineTest_Base.h" -#include "ramses-logic/Logger.h" -#include "ramses-logic/Property.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-logic/AnimationNodeConfig.h" +#include "ScopedLogContextLevel.h" +#include "ramses/client/logic/Property.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/logic/AnimationNodeConfig.h" -#include "impl/LoggerImpl.h" +#include "internal/Core/Utils/LogMacros.h" #include "LogTestUtils.h" #include "WithTempDirectory.h" #include "RamsesTestUtils.h" -namespace ramses +namespace ramses::internal { + const size_t SCENE_ISSUES = 6u; + class ALogicEngine_Validation : public ALogicEngine { + public: + ValidationReport report; }; TEST_F(ALogicEngine_Validation, LogsNoWarningsWhenSavingFile_WhenContentValid) { - ScopedLogContextLevel logCollector{ ELogLevel::Trace, [&](ELogLevel type, std::string_view /*message*/) + int warningCount = 0; + + ScopedLogContextLevel logCollector{ CONTEXT_CLIENT, ELogLevel::Trace, [&](ELogLevel type, std::string_view /*message*/) { if (type == ELogLevel::Warn) { - FAIL() << "Should have no warnings!"; + ++warningCount; } } }; + m_logicEngine->validate(report); + EXPECT_FALSE(report.hasIssue()); + WithTempDirectory tmpDir; // Empty logic engine has ho warnings - m_logicEngine.saveToFile("noWarnings.rlogic"); + saveToFile("noWarnings.ramses"); + EXPECT_LE(warningCount, SCENE_ISSUES); // logic is valid, but ramses scene is not } TEST_F(ALogicEngine_Validation, LogsWarningsWhenSavingFile_WhenContentHasValidationIssues) { std::vector warnings; - ScopedLogContextLevel logCollector{ ELogLevel::Trace, [&](ELogLevel type, std::string_view message) + ScopedLogContextLevel logCollector{ CONTEXT_CLIENT, ELogLevel::Trace, [&](ELogLevel type, std::string_view message) { if (type == ELogLevel::Warn) { warnings.emplace_back(message); } - } + } }; WithTempDirectory tmpDir; // Cause some validation issues on purpose - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); nodeBinding.getInputs()->getChild("scaling")->set({1.5f, 1.f, 1.f}); - m_logicEngine.saveToFile("noWarnings.rlogic"); + saveToFile("noWarnings.ramses"); - ASSERT_EQ(warnings.size(), 2u); + ASSERT_EQ(warnings.size(), SCENE_ISSUES + 2u); EXPECT_EQ(warnings[0], "Saving logic engine content with manually updated binding values without calling update() will result in those values being lost!"); - EXPECT_EQ(warnings[1], "[NodeBinding [Id=1]] Node [NodeBinding] has no ingoing links! Node should be deleted or properly linked!"); + EXPECT_EQ(warnings[1], "[NodeBinding [LogicObject ScnObjId=9]] Node [NodeBinding] has no ingoing links! Node should be deleted or properly linked!"); // Fixing the problems -> removes the warning warnings.clear(); - const auto* intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - m_logicEngine.link(*intf->getOutputs()->getChild("param_vec3f"), *nodeBinding.getInputs()->getChild("scaling")); - m_logicEngine.update(); + auto* intf = m_logicEngine->createLuaInterface(m_interfaceSourceCode, "intf"); + m_logicEngine->link(*intf->getOutputs()->getChild("param_vec3f"), *nodeBinding.getInputs()->getChild("scaling")); + m_logicEngine->update(); - m_logicEngine.saveToFile("noWarnings.rlogic"); + saveToFile("noWarnings.ramses"); - EXPECT_TRUE(warnings.empty()); + ASSERT_EQ(warnings.size(), SCENE_ISSUES); } TEST_F(ALogicEngine_Validation, LogsNoContentWarningsWhenSavingFile_WhenContentHasValidationIssues_ButValidationIsDisabled) { std::vector infoLogs; - ScopedLogContextLevel logCollector{ ELogLevel::Trace, [&](ELogLevel type, std::string_view message) + ScopedLogContextLevel logCollector{ CONTEXT_CLIENT, ELogLevel::Trace, [&](ELogLevel type, std::string_view message) { if (type == ELogLevel::Info) { @@ -93,7 +103,7 @@ namespace ramses WithTempDirectory tmpDir; // Cause some validation issues on purpose - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); nodeBinding.getInputs()->getChild("scaling")->set({ 1.5f, 1.f, 1.f }); SaveFileConfig conf; @@ -105,30 +115,31 @@ namespace ramses infoLogs.clear(); // Content warning doesn't show up because disabled - m_logicEngine.saveToFile("noWarnings.rlogic", conf); + saveToFile("noWarnings.ramses", conf); } TEST_F(ALogicEngine_Validation, ProducesWarningIfBindingValuesHaveDirtyValue) { // Create a binding in a "dirty" state - has a non-default value, but update() wasn't called and didn't pass the value to ramses - RamsesNodeBinding* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); + NodeBinding* nodeBinding = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); - const auto* intf = m_logicEngine.createLuaInterface(m_interfaceSourceCode, "intf"); - m_logicEngine.link(*intf->getOutputs()->getChild("param_vec3f"), *nodeBinding->getInputs()->getChild("scaling")); + auto* intf = m_logicEngine->createLuaInterface(m_interfaceSourceCode, "intf"); + m_logicEngine->link(*intf->getOutputs()->getChild("param_vec3f"), *nodeBinding->getInputs()->getChild("scaling")); nodeBinding->getInputs()->getChild("visibility")->set(false); // Expects warning - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); EXPECT_EQ(warnings[0].message, "Saving logic engine content with manually updated binding values without calling update() will result in those values being lost!"); - EXPECT_EQ(warnings[0].type, EWarningType::UnsafeDataState); + EXPECT_EQ(warnings[0].type, EIssueType::Warning); } TEST_F(ALogicEngine_Validation, ProducesWarningIfInterfaceHasUnboundOutputs) { - LuaInterface* intf = m_logicEngine.createLuaInterface(R"( + LuaInterface* intf = m_logicEngine->createLuaInterface(R"( function interface(IN,OUT) IN.param1 = Type:Int32() @@ -139,11 +150,12 @@ namespace ramses ASSERT_NE(nullptr, intf); // Expects warning - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(3u, warnings.size()); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::message, ::testing::HasSubstr("Interface [intf name] has unlinked output")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::message, ::testing::HasSubstr("Interface [intf name] has unlinked output")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } class ALogicEngine_ValidatingDanglingNodes : public ALogicEngine @@ -157,12 +169,12 @@ namespace ramses void createValidTestSetup() { //create two identical scripts each containing 1 input and 1 output - m_testScript = m_logicEngine.createLuaScript(m_scriptSrc, {}, "script1"); - auto* script2 = m_logicEngine.createLuaScript(m_scriptSrc, {}, "script2"); + m_testScript = m_logicEngine->createLuaScript(m_scriptSrc, {}, "script1"); + auto* script2 = m_logicEngine->createLuaScript(m_scriptSrc, {}, "script2"); //cross link inputs and outputs of each script so that none of the scripts is "dangling" - m_logicEngine.link(*m_testScript->getOutputs()->getChild("paramInt32"), *script2->getInputs()->getChild("paramTestReserved")); - m_logicEngine.linkWeak(*script2->getOutputs()->getChild("paramInt32"), *m_testScript->getInputs()->getChild("paramTestReserved")); + m_logicEngine->link(*m_testScript->getOutputs()->getChild("paramInt32"), *script2->getInputs()->getChild("paramTestReserved")); + m_logicEngine->linkWeak(*script2->getOutputs()->getChild("paramInt32"), *m_testScript->getInputs()->getChild("paramTestReserved")); } void linkNodeInput(LogicNode& node, std::string_view inputName, std::string_view testScriptOutputName = "paramInt32") @@ -172,12 +184,12 @@ namespace ramses void linkNodeInput(Property& nodeInput, std::string_view testScriptOutputName = "paramInt32") { - m_logicEngine.link(*m_testScript->getOutputs()->getChild(testScriptOutputName), nodeInput); + m_logicEngine->link(*m_testScript->getOutputs()->getChild(testScriptOutputName), nodeInput); } void linkNodeOutput(LogicNode& node, std::string_view outputName, std::string_view testScriptInputName = "paramInt32") { - m_logicEngine.link(*node.getOutputs()->getChild(outputName), *m_testScript->getInputs()->getChild(testScriptInputName)); + m_logicEngine->link(*node.getOutputs()->getChild(outputName), *m_testScript->getInputs()->getChild(testScriptInputName)); } const std::string m_scriptSrc = R"SRC( @@ -197,65 +209,69 @@ namespace ramses )SRC"; LuaScript* m_testScript = nullptr; + ValidationReport report; }; // -- scripts -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfScriptHasNoLinks) { - auto* script = m_logicEngine.createLuaScript(m_scriptSrc, {}, "scr"); + auto* script = m_logicEngine->createLuaScript(m_scriptSrc, {}, "scr"); ASSERT_NE(nullptr, script); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(2u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [scr] has no outgoing links! Node should be deleted or properly linked!")), - ::testing::Field(&WarningData::message, ::testing::StrEq("Node [scr] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [scr] has no outgoing links! Node should be deleted or properly linked!")), + ::testing::Field(&Issue::message, ::testing::StrEq("Node [scr] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfScriptHasNoIngoingLinks) { - auto* script = m_logicEngine.createLuaScript(m_scriptSrc, {}, "scr"); + auto* script = m_logicEngine->createLuaScript(m_scriptSrc, {}, "scr"); ASSERT_NE(nullptr, script); linkNodeOutput(*script, "paramInt32"); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [scr] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [scr] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfScriptHasNoOutgoingLinks) { - auto* script = m_logicEngine.createLuaScript(m_scriptSrc, {}, "scr"); + auto* script = m_logicEngine->createLuaScript(m_scriptSrc, {}, "scr"); ASSERT_NE(nullptr, script); linkNodeInput(*script, "paramInt32"); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [scr] has no outgoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [scr] has no outgoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfScriptHasIngoingAndOutgoingLinks) { - auto* script = m_logicEngine.createLuaScript(m_scriptSrc, {}, "scr"); + auto* script = m_logicEngine->createLuaScript(m_scriptSrc, {}, "scr"); ASSERT_NE(nullptr, script); linkNodeOutput(*script, "paramInt32"); linkNodeInput(*script, "paramInt32"); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- interfaces -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfInterfaceHasNoIngoingLinks) { - LuaInterface* intf = m_logicEngine.createLuaInterface(R"( + LuaInterface* intf = m_logicEngine->createLuaInterface(R"( function interface(INOUT) INOUT.param = Type:Int32() end @@ -264,303 +280,306 @@ namespace ramses linkNodeOutput(*intf, "param"); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- node bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfNodeBindingHasNoIngoingLinks) { - auto* nodeBind = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); + auto* nodeBind = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); ASSERT_NE(nullptr, nodeBind); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfNodeBindingHasIngoingLinks) { - auto* nodeBind = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); + auto* nodeBind = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "binding"); ASSERT_NE(nullptr, nodeBind); linkNodeInput(*nodeBind, "scaling", "paramVec3f"); - m_logicEngine.update(); + m_logicEngine->update(); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- appearance bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfAppearanceBindingHasNoIngoingLinks) { - auto* appearanceBind = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "binding"); + auto* appearanceBind = m_logicEngine->createAppearanceBinding(*m_appearance, "binding"); ASSERT_NE(nullptr, appearanceBind); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfAppearanceBindingHasIngoingLinks) { - auto* appearanceBind = m_logicEngine.createRamsesAppearanceBinding(*m_appearance, "binding"); + auto* appearanceBind = m_logicEngine->createAppearanceBinding(*m_appearance, "binding"); ASSERT_NE(nullptr, appearanceBind); linkNodeInput(*appearanceBind, "floatUniform", "paramFloat"); - m_logicEngine.update(); + m_logicEngine->update(); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- camera bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfCameraBindingHasNoIngoingLinks) { - auto* cameraBind = m_logicEngine.createRamsesCameraBinding(*m_camera, "binding"); + auto* cameraBind = m_logicEngine->createCameraBinding(*m_camera, "binding"); ASSERT_NE(nullptr, cameraBind); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfCameraBindingHasIngoingLinks) { - auto* cameraBind = m_logicEngine.createRamsesCameraBinding(*m_camera, "binding"); + auto* cameraBind = m_logicEngine->createCameraBinding(*m_camera, "binding"); ASSERT_NE(nullptr, cameraBind); - m_logicEngine.link(*m_testScript->getOutputs()->getChild("paramInt32"), *cameraBind->getInputs()->getChild("viewport")->getChild("offsetX")); - m_logicEngine.update(); + m_logicEngine->link(*m_testScript->getOutputs()->getChild("paramInt32"), *cameraBind->getInputs()->getChild("viewport")->getChild("offsetX")); + m_logicEngine->update(); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- render pass bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfRenderPassBindingHasNoIngoingLinks) { - auto* passBind = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "binding"); + auto* passBind = m_logicEngine->createRenderPassBinding(*m_renderPass, "binding"); ASSERT_NE(nullptr, passBind); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfRenderPassBindingHasIngoingLinks) { - auto* passBind = m_logicEngine.createRamsesRenderPassBinding(*m_renderPass, "binding"); + auto* passBind = m_logicEngine->createRenderPassBinding(*m_renderPass, "binding"); ASSERT_NE(nullptr, passBind); linkNodeInput(*passBind, "renderOrder", "paramInt32"); - m_logicEngine.update(); + m_logicEngine->update(); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- render group bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfRenderGroupBindingHasNoIngoingLinks) { - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; elements.addElement(*m_meshNode, "mesh"); - auto* binding = m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "binding"); + auto* binding = m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements, "binding"); ASSERT_NE(nullptr, binding); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfRenderGroupBindingHasIngoingLinks) { - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; elements.addElement(*m_meshNode, "mesh"); - auto* binding = m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "binding"); + auto* binding = m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements, "binding"); ASSERT_NE(nullptr, binding); linkNodeInput(*binding->getInputs()->getChild("renderOrders")->getChild("mesh"), "paramInt32"); - m_logicEngine.update(); + m_logicEngine->update(); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- mesh node bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfMeshNodeBindingHasNoIngoingLinks) { - m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "binding"); + m_logicEngine->createMeshNodeBinding(*m_meshNode, "binding"); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [binding] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfMeshNodeBindingHasIngoingLinks) { - const auto binding = m_logicEngine.createRamsesMeshNodeBinding(*m_meshNode, "binding"); + const auto binding = m_logicEngine->createMeshNodeBinding(*m_meshNode, "binding"); ASSERT_NE(nullptr, binding); linkNodeInput(*binding->getInputs()->getChild("indexOffset"), "paramInt32"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); - EXPECT_TRUE(m_logicEngine.validate().empty()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- animations -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfAnimationHasNoLinks) { - const auto* m_dataFloat = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - const auto* m_dataVec2 = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + const auto* m_dataFloat = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto* m_dataVec2 = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); const AnimationChannel channel{ "channel", m_dataFloat, m_dataVec2 }; AnimationNodeConfig config; config.addChannel(channel); - auto* anim = m_logicEngine.createAnimationNode(config, "anim"); + auto* anim = m_logicEngine->createAnimationNode(config, "anim"); ASSERT_NE(nullptr, anim); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(2u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [anim] has no outgoing links! Node should be deleted or properly linked!")), - ::testing::Field(&WarningData::message, ::testing::StrEq("Node [anim] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [anim] has no outgoing links! Node should be deleted or properly linked!")), + ::testing::Field(&Issue::message, ::testing::StrEq("Node [anim] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfAnimationHasNoOutgoingLinks) { - const auto* m_dataFloat = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - const auto* m_dataVec2 = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + const auto* m_dataFloat = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto* m_dataVec2 = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); const AnimationChannel channel{ "channel", m_dataFloat, m_dataVec2 }; AnimationNodeConfig config; config.addChannel(channel); - auto* anim = m_logicEngine.createAnimationNode(config, "anim"); + auto* anim = m_logicEngine->createAnimationNode(config, "anim"); ASSERT_NE(nullptr, anim); linkNodeInput(*anim, "progress", "paramFloat"); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [anim] has no outgoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [anim] has no outgoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfAnimationHasNoIngoingLinks) { - const auto* m_dataFloat = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - const auto* m_dataVec2 = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + const auto* m_dataFloat = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto* m_dataVec2 = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); const AnimationChannel channel{ "channel", m_dataFloat, m_dataVec2 }; AnimationNodeConfig config; config.addChannel(channel); - auto* anim = m_logicEngine.createAnimationNode(config, "anim"); + auto* anim = m_logicEngine->createAnimationNode(config, "anim"); ASSERT_NE(nullptr, anim); linkNodeOutput(*anim, "duration", "paramFloat"); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [anim] has no ingoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [anim] has no ingoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfAnimationHasIngoingAndOutgoingLinks) { - const auto* m_dataFloat = m_logicEngine.createDataArray(std::vector{ 1.f, 2.f, 3.f }); - const auto* m_dataVec2 = m_logicEngine.createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); + const auto* m_dataFloat = m_logicEngine->createDataArray(std::vector{ 1.f, 2.f, 3.f }); + const auto* m_dataVec2 = m_logicEngine->createDataArray(std::vector{ { 1.f, 2.f }, { 3.f, 4.f }, { 5.f, 6.f } }); const AnimationChannel channel{ "channel", m_dataFloat, m_dataVec2 }; AnimationNodeConfig config; config.addChannel(channel); - auto* anim = m_logicEngine.createAnimationNode(config, "anim"); + auto* anim = m_logicEngine->createAnimationNode(config, "anim"); ASSERT_NE(nullptr, anim); linkNodeInput(*anim, "progress", "paramFloat"); linkNodeOutput(*anim, "duration", "paramFloat"); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- timers -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, ProducesWarningIfTimerHasNoOutgoingLinks) { - auto* timer = m_logicEngine.createTimerNode("timer"); + auto* timer = m_logicEngine->createTimerNode("timer"); ASSERT_NE(nullptr, timer); - auto warnings = m_logicEngine.validate(); + m_logicEngine->validate(report); + const auto& warnings = report.getIssues(); ASSERT_EQ(1u, warnings.size()); - EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&WarningData::message, ::testing::StrEq("Node [timer] has no outgoing links! Node should be deleted or properly linked!")))); - EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_THAT(warnings, ::testing::ElementsAre(::testing::Field(&Issue::message, ::testing::StrEq("Node [timer] has no outgoing links! Node should be deleted or properly linked!")))); + EXPECT_THAT(warnings, ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfTimerHasOutgoingLinks) { - auto* timer = m_logicEngine.createTimerNode("timer"); + auto* timer = m_logicEngine->createTimerNode("timer"); ASSERT_NE(nullptr, timer); linkNodeOutput(*timer, "ticker_us", "paramInt64"); - auto warnings = m_logicEngine.validate(); - ASSERT_EQ(0u, warnings.size()); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- anchor points -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfAnchorPointHasNoOutgoingLinksAndItsBindingsNoIncomingLinks) { - auto* nodeBind = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); - auto* cameraBind = m_logicEngine.createRamsesCameraBinding(*m_camera, "cameraBinding"); - auto* anchor = m_logicEngine.createAnchorPoint(*nodeBind, *cameraBind, "anchorBinding"); + auto* nodeBind = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); + auto* cameraBind = m_logicEngine->createCameraBinding(*m_camera, "cameraBinding"); + auto* anchor = m_logicEngine->createAnchorPoint(*nodeBind, *cameraBind, "anchorBinding"); ASSERT_NE(nullptr, anchor); - m_logicEngine.update(); - EXPECT_TRUE(m_logicEngine.validate().empty()); + m_logicEngine->update(); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningIfAnchorPointHasOutgoingLink) { - auto* nodeBind = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); - auto* cameraBind = m_logicEngine.createRamsesCameraBinding(*m_camera, "cameraBinding"); - auto* anchor = m_logicEngine.createAnchorPoint(*nodeBind, *cameraBind, "anchorBinding"); + auto* nodeBind = m_logicEngine->createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); + auto* cameraBind = m_logicEngine->createCameraBinding(*m_camera, "cameraBinding"); + auto* anchor = m_logicEngine->createAnchorPoint(*nodeBind, *cameraBind, "anchorBinding"); ASSERT_NE(nullptr, anchor); linkNodeOutput(*anchor, "depth", "paramFloat"); - m_logicEngine.update(); - EXPECT_TRUE(m_logicEngine.validate().empty()); + m_logicEngine->update(); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } // -- skin bindings -- // TEST_F(ALogicEngine_ValidatingDanglingNodes, DoesNotProduceWarningWhenCheckingSkinBindingAndItsBindingsHaveNoIncomingLinks) { - const auto skin = createSkinBinding(m_logicEngine); + const auto skin = createSkinBinding(*m_logicEngine); ASSERT_NE(nullptr, skin); - m_logicEngine.update(); - EXPECT_TRUE(m_logicEngine.validate().empty()); - } - - TEST(GetVerboseDescriptionFunction, ReturnsCorrectString) - { - EXPECT_STREQ("Performance", GetVerboseDescription(EWarningType::Performance)); - EXPECT_STREQ("Unsafe Data State", GetVerboseDescription(EWarningType::UnsafeDataState)); - EXPECT_STREQ("Uninitialized Data", GetVerboseDescription(EWarningType::UninitializedData)); - EXPECT_STREQ("Precision Loss", GetVerboseDescription(EWarningType::PrecisionLoss)); - EXPECT_STREQ("Unused Content", GetVerboseDescription(EWarningType::UnusedContent)); - EXPECT_STREQ("Duplicate Content", GetVerboseDescription(EWarningType::DuplicateContent)); - EXPECT_STREQ("Other", GetVerboseDescription(EWarningType::Other)); + m_logicEngine->update(); + m_logicEngine->validate(report); + ASSERT_FALSE(report.hasIssue()); } } diff --git a/tests/unittests/client/logic/api/LogicNodeTest.cpp b/tests/unittests/client/logic/api/LogicNodeTest.cpp new file mode 100644 index 000000000..40741f038 --- /dev/null +++ b/tests/unittests/client/logic/api/LogicNodeTest.cpp @@ -0,0 +1,68 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" +#include "LogicEngineTest_Base.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/LogicNodeImpl.h" + +namespace ramses::internal +{ + class ALogicNode : public ALogicEngine + { + protected: + LogicNode& m_logicNode{ *m_logicEngine->createLuaInterface(m_interfaceSourceCode, "name") }; + }; + + TEST_F(ALogicNode, HasName) + { + EXPECT_EQ("name", m_logicNode.getName()); + } + + TEST_F(ALogicNode, CanReceiveNewName) + { + EXPECT_TRUE(m_logicNode.setName("newName")); + EXPECT_EQ("newName", m_logicNode.getName()); + } + + TEST_F(ALogicNode, DirtyByDefault) + { + EXPECT_TRUE(m_logicNode.impl().isDirty()); + } + + TEST_F(ALogicNode, DirtyWhenSetDirty) + { + m_logicNode.impl().setDirty(false); + EXPECT_FALSE(m_logicNode.impl().isDirty()); + m_logicNode.impl().setDirty(true); + EXPECT_TRUE(m_logicNode.impl().isDirty()); + } + + TEST_F(ALogicNode, OwnsProperties) + { + // const outputs + { + const Property* root = m_logicNode.getOutputs(); + EXPECT_EQ(&m_logicNode, &root->getOwningLogicNode()); + ASSERT_EQ(1u, root->getChildCount()); + + const auto prop = root->getChild(0); + EXPECT_EQ(&m_logicNode, &prop->getOwningLogicNode()); + } + + // non-const inputs + { + Property* root = m_logicNode.getInputs(); + EXPECT_EQ(&m_logicNode, &root->getOwningLogicNode()); + ASSERT_EQ(1u, root->getChildCount()); + + const auto prop = root->getChild(0); + EXPECT_EQ(&m_logicNode, &prop->getOwningLogicNode()); + } + } +} diff --git a/tests/unittests/client/logic/api/LogicObjectTest.cpp b/tests/unittests/client/logic/api/LogicObjectTest.cpp new file mode 100644 index 000000000..ba107a7ff --- /dev/null +++ b/tests/unittests/client/logic/api/LogicObjectTest.cpp @@ -0,0 +1,291 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" + +#include "impl/logic/LuaModuleImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LuaInterfaceImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/logic/AnimationNodeImpl.h" +#include "impl/logic/TimerNodeImpl.h" +#include "impl/logic/AnchorPointImpl.h" + +#include "impl/RamsesObjectTypeTraits.h" +#include "impl/RamsesObjectTypeUtils.h" +#include "impl/SceneImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/RamsesFrameworkImpl.h" + +#include "LogicEngineTestWithCreationHelper.h" + +namespace ramses::internal +{ + using namespace testing; + + template + class LogicOwnershipTest : public ALogicEngineBaseWithCreationHelper, public ::testing::Test + { + }; + + TYPED_TEST_SUITE(LogicOwnershipTest, LogicObjectTypes); + + TYPED_TEST(LogicOwnershipTest, logicObjectsAreOfTypeLogicObject) + { + const auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::LogicObject)); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::SceneObject)); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::RamsesObject)); + } + + TYPED_TEST(LogicOwnershipTest, sceneObjectsHaveReferenceToTheirSceneAndHL) + { + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_EQ(obj, &obj->impl().getLogicObject()); + EXPECT_EQ(this->m_scene, &obj->getScene()); + EXPECT_EQ(&this->m_scene->impl(), &obj->impl().getSceneImpl()); + EXPECT_EQ(&this->m_scene->impl().getIScene(), &obj->impl().getIScene()); + EXPECT_EQ(&this->m_ramses.getClient().impl(), &obj->impl().getClientImpl()); + // also indirectly to framework + EXPECT_EQ(&this->m_ramses.getFramework(), &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); + } + + TYPED_TEST(LogicOwnershipTest, sceneObjectsHaveReferenceToTheirScene_const) + { + const auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_EQ(obj, &obj->impl().getLogicObject()); + EXPECT_EQ(this->m_scene, &obj->getScene()); + EXPECT_EQ(&this->m_scene->impl(), &obj->impl().getSceneImpl()); + EXPECT_EQ(&this->m_scene->impl().getIScene(), &obj->impl().getIScene()); + EXPECT_EQ(&this->m_ramses.getClient().impl(), &obj->impl().getClientImpl()); + // also indirectly to framework + EXPECT_EQ(&this->m_ramses.getFramework(), &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); + } + + TYPED_TEST(LogicOwnershipTest, canFindCreatedObjectInLogicEngine) + { + { // non-const + const auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + + EXPECT_EQ(obj, this->m_logicEngine->template findObject("objectName")); + EXPECT_EQ(obj, this->m_logicEngine->template findObject("objectName")); + + EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); + + const auto coll = this->m_logicEngine->template getCollection(); + EXPECT_TRUE(std::find(coll.cbegin(), coll.cend(), obj) != coll.cend()); + const auto collobj = this->m_logicEngine->template getCollection(); + EXPECT_TRUE(std::find(collobj.cbegin(), collobj.cend(), obj) != collobj.cend()); + } + + { // const + const auto* obj = this->template createObjectOfType("objectName2"); + ASSERT_TRUE(obj); + const auto* logicEngineConst = this->m_logicEngine; + + EXPECT_EQ(obj, logicEngineConst->template findObject("objectName2")); + EXPECT_EQ(obj, logicEngineConst->template findObject("objectName2")); + + EXPECT_EQ(obj, logicEngineConst->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, logicEngineConst->template findObject(obj->getSceneObjectId())); + + const auto coll = logicEngineConst->template getCollection(); + EXPECT_TRUE(std::find(coll.cbegin(), coll.cend(), obj) != coll.cend()); + const auto collobj = logicEngineConst->template getCollection(); + EXPECT_TRUE(std::find(collobj.cbegin(), collobj.cend(), obj) != collobj.cend()); + } + } + + TYPED_TEST(LogicOwnershipTest, objectCanUseUserID) + { + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_TRUE(obj->setUserId(1u, 2u)); + EXPECT_EQ(1u, obj->getUserId().first); + EXPECT_EQ(2u, obj->getUserId().second); + EXPECT_TRUE(obj->setUserId(3u, 4u)); + EXPECT_EQ(3u, obj->getUserId().first); + EXPECT_EQ(4u, obj->getUserId().second); + } + + TYPED_TEST(LogicOwnershipTest, objectCanBeCast) + { + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + ASSERT_TRUE(obj->template as()); + EXPECT_TRUE(obj->template as()->template as()); + EXPECT_TRUE(object_cast(object_cast(obj))); + + const auto* objConst = obj; + ASSERT_TRUE(objConst->template as()); + EXPECT_TRUE(objConst->template as()->template as()); + EXPECT_TRUE(object_cast(object_cast(objConst))); + } + + TYPED_TEST(LogicOwnershipTest, objectCanBeCastFromRamsesObject) + { + RamsesObject* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_TRUE(obj->as()); + EXPECT_TRUE(obj->as()); + EXPECT_TRUE(obj->as()); + EXPECT_TRUE(object_cast(obj)); + EXPECT_TRUE(object_cast(obj)); + EXPECT_TRUE(object_cast(obj)); + + const RamsesObject* objConst = obj; + EXPECT_TRUE(objConst->as()); + EXPECT_TRUE(objConst->as()); + EXPECT_TRUE(objConst->as()); + EXPECT_TRUE(object_cast(objConst)); + EXPECT_TRUE(object_cast(objConst)); + EXPECT_TRUE(object_cast(objConst)); + } + + TYPED_TEST(LogicOwnershipTest, logicEngineDoesNotContainDestroyedObject) + { + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_TRUE(this->m_logicEngine->destroy(*obj)); + EXPECT_EQ(nullptr, this->m_logicEngine->template findObject("objectName")); + } + + TYPED_TEST(LogicOwnershipTest, sceneReportsErrorIfTryingToDestroyLogicObjectInIt) + { + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_FALSE(this->m_scene->destroy(*obj)); + EXPECT_EQ(obj, this->m_logicEngine->template findObject("objectName")); + } + + TYPED_TEST(LogicOwnershipTest, objectNameChanged) + { + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_TRUE(obj->setName("otherObjectName")); + EXPECT_EQ(nullptr, this->m_logicEngine->template findObject("objectName")); + EXPECT_EQ(obj, this->m_logicEngine->template findObject("otherObjectName")); + } + + TYPED_TEST(LogicOwnershipTest, objectFailsToBeDestroyedIfFromOtherLogicEngine) + { + auto otherEngine = this->m_scene->createLogicEngine(); + ASSERT_TRUE(otherEngine); + + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + EXPECT_FALSE(otherEngine->destroy(*obj)); + } + + TYPED_TEST(LogicOwnershipTest, hasAllPropertiesAfterDeserialization) + { + this->withTempDirectory(); + + sceneObjectId_t id{}; + { + // fill with some objects first + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + this->template createObjectOfType("objectName"); + + auto* obj = this->template createObjectOfType("objectName"); + ASSERT_TRUE(obj); + + // set/change some parameters + id = obj->getSceneObjectId(); + EXPECT_TRUE(obj->setName("newName")); + EXPECT_TRUE(obj->setUserId(1u, 2u)); + + ASSERT_TRUE(this->saveToFileWithoutValidation("file.tmp")); + ASSERT_TRUE(this->recreateFromFile("file.tmp")); + } + + const auto* obj = this->m_logicEngine->template findObject(id); + ASSERT_TRUE(obj); + + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::LogicObject)); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::SceneObject)); + EXPECT_TRUE(obj->isOfType(ERamsesObjectType::RamsesObject)); + + EXPECT_EQ(obj, &obj->impl().getLogicObject()); + EXPECT_EQ(this->m_scene, &obj->getScene()); + EXPECT_EQ(&this->m_scene->impl(), &obj->impl().getSceneImpl()); + EXPECT_EQ(&this->m_scene->impl().getIScene(), &obj->impl().getIScene()); + EXPECT_EQ(&this->m_ramses.getClient().impl(), &obj->impl().getClientImpl()); + EXPECT_EQ(&this->m_ramses.getFramework(), &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); + + EXPECT_EQ(obj, this->m_logicEngine->template findObject("newName")); + EXPECT_EQ(obj, this->m_logicEngine->template findObject("newName")); + EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_logicEngine->findObject(obj->getSceneObjectId())); + + const auto coll = this->m_logicEngine->template getCollection(); + EXPECT_TRUE(std::find(coll.cbegin(), coll.cend(), obj) != coll.cend()); + const auto collobj = this->m_logicEngine->template getCollection(); + EXPECT_TRUE(std::find(collobj.cbegin(), collobj.cend(), obj) != collobj.cend()); + + // check all ids unique + std::unordered_set ids; + for (const auto* o : coll) + ids.insert(o->getSceneObjectId()); + EXPECT_EQ(ids.size(), coll.size()); + + ASSERT_TRUE(obj->template as()); + EXPECT_TRUE(obj->template as()->template as()); + EXPECT_TRUE(object_cast(object_cast(obj))); + + EXPECT_EQ(1u, obj->getUserId().first); + EXPECT_EQ(2u, obj->getUserId().second); + } +} diff --git a/client/logic/unittests/api/LuaConfigTest.cpp b/tests/unittests/client/logic/api/LuaConfigTest.cpp similarity index 69% rename from client/logic/unittests/api/LuaConfigTest.cpp rename to tests/unittests/client/logic/api/LuaConfigTest.cpp index 6a6b3955f..a9bfabef0 100644 --- a/client/logic/unittests/api/LuaConfigTest.cpp +++ b/tests/unittests/client/logic/api/LuaConfigTest.cpp @@ -10,19 +10,19 @@ #include "gmock/gmock.h" #include "LogTestUtils.h" -#include "ramses-logic/LuaConfig.h" -#include "ramses-logic/LogicEngine.h" -#include "impl/LuaConfigImpl.h" +#include "ramses/client/logic/LuaConfig.h" +#include "ramses/client/logic/LogicEngine.h" +#include "impl/logic/LuaConfigImpl.h" +#include "LogicEngineTest_Base.h" #include "fmt/format.h" namespace ramses::internal { - class ALuaConfig : public ::testing::Test + class ALuaConfig : public ALogicEngine { protected: - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; - LuaModule* m_module{ m_logicEngine.createLuaModule(R"( + LuaModule* m_module{ m_logicEngine->createLuaModule(R"( local mymath = {} function mymath.add(a,b) print(a+b) @@ -31,7 +31,7 @@ namespace ramses::internal )") }; std::string m_errorMessage; - ScopedLogContextLevel m_scopedLogs{ELogLevel::Error, [&](ELogLevel msgType, std::string_view message) { + ScopedLogContextLevel m_scopedLogs{CONTEXT_CLIENT, ELogLevel::Error, [&](ELogLevel msgType, std::string_view message) { m_errorMessage = message; if (msgType != ELogLevel::Error) { @@ -43,8 +43,8 @@ namespace ramses::internal TEST_F(ALuaConfig, IsCreated) { LuaConfig config; - EXPECT_TRUE(config.m_impl->getModuleMapping().empty()); - EXPECT_FALSE(config.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_TRUE(config.impl().getModuleMapping().empty()); + EXPECT_FALSE(config.impl().hasDebugLogFunctionsEnabled()); } TEST_F(ALuaConfig, IsCopied) @@ -56,9 +56,9 @@ namespace ramses::internal config.enableDebugLogFunctions(); LuaConfig configCopy(config); - EXPECT_EQ(config.m_impl->getModuleMapping(), configCopy.m_impl->getModuleMapping()); - EXPECT_EQ(config.m_impl->getStandardModules(), configCopy.m_impl->getStandardModules()); - EXPECT_EQ(config.m_impl->hasDebugLogFunctionsEnabled(), configCopy.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_EQ(config.impl().getModuleMapping(), configCopy.impl().getModuleMapping()); + EXPECT_EQ(config.impl().getStandardModules(), configCopy.impl().getStandardModules()); + EXPECT_EQ(config.impl().hasDebugLogFunctionsEnabled(), configCopy.impl().hasDebugLogFunctionsEnabled()); } TEST_F(ALuaConfig, IsCopyAssigned) @@ -71,9 +71,9 @@ namespace ramses::internal // Copy assignment LuaConfig configCopy; configCopy = config; - EXPECT_EQ(config.m_impl->getModuleMapping(), configCopy.m_impl->getModuleMapping()); - EXPECT_EQ(config.m_impl->getStandardModules(), configCopy.m_impl->getStandardModules()); - EXPECT_EQ(config.m_impl->hasDebugLogFunctionsEnabled(), configCopy.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_EQ(config.impl().getModuleMapping(), configCopy.impl().getModuleMapping()); + EXPECT_EQ(config.impl().getStandardModules(), configCopy.impl().getStandardModules()); + EXPECT_EQ(config.impl().hasDebugLogFunctionsEnabled(), configCopy.impl().hasDebugLogFunctionsEnabled()); } TEST_F(ALuaConfig, IsMoved) @@ -85,9 +85,9 @@ namespace ramses::internal LuaConfig movedConfig(std::move(config)); - EXPECT_EQ(movedConfig.m_impl->getModuleMapping().at("mod1"), m_module); - EXPECT_THAT(movedConfig.m_impl->getStandardModules(), ::testing::ElementsAre(EStandardModule::Debug)); - EXPECT_TRUE(movedConfig.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_EQ(movedConfig.impl().getModuleMapping().at("mod1"), m_module); + EXPECT_THAT(movedConfig.impl().getStandardModules(), ::testing::ElementsAre(EStandardModule::Debug)); + EXPECT_TRUE(movedConfig.impl().hasDebugLogFunctionsEnabled()); } TEST_F(ALuaConfig, IsMoveAssigned) @@ -99,9 +99,9 @@ namespace ramses::internal LuaConfig movedAssigned; movedAssigned = std::move(config); - EXPECT_EQ(movedAssigned.m_impl->getModuleMapping().at("mod1"), m_module); - EXPECT_THAT(movedAssigned.m_impl->getStandardModules(), ::testing::ElementsAre(EStandardModule::Debug)); - EXPECT_FALSE(movedAssigned.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_EQ(movedAssigned.impl().getModuleMapping().at("mod1"), m_module); + EXPECT_THAT(movedAssigned.impl().getStandardModules(), ::testing::ElementsAre(EStandardModule::Debug)); + EXPECT_FALSE(movedAssigned.impl().hasDebugLogFunctionsEnabled()); } TEST_F(ALuaConfig, ProducesErrorWhenCreatingLuaScriptUsingModuleUnderInvalidName) @@ -151,9 +151,9 @@ namespace ramses::internal TEST_F(ALuaConfig, CanEnableDebugLogFunctions) { LuaConfig config; - EXPECT_FALSE(config.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_FALSE(config.impl().hasDebugLogFunctionsEnabled()); config.enableDebugLogFunctions(); - EXPECT_TRUE(config.m_impl->hasDebugLogFunctionsEnabled()); + EXPECT_TRUE(config.impl().hasDebugLogFunctionsEnabled()); } class ALuaConfig_StdModules : public ::testing::Test @@ -164,19 +164,19 @@ namespace ramses::internal { LuaConfig config; - EXPECT_TRUE(config.m_impl->getStandardModules().empty()); + EXPECT_TRUE(config.impl().getStandardModules().empty()); ASSERT_TRUE(config.addStandardModuleDependency(EStandardModule::Base)); - EXPECT_THAT(config.m_impl->getStandardModules(), ::testing::ElementsAre(EStandardModule::Base)); + EXPECT_THAT(config.impl().getStandardModules(), ::testing::ElementsAre(EStandardModule::Base)); ASSERT_TRUE(config.addStandardModuleDependency(EStandardModule::String)); - EXPECT_THAT(config.m_impl->getStandardModules(), ::testing::ElementsAre(EStandardModule::Base, EStandardModule::String)); + EXPECT_THAT(config.impl().getStandardModules(), ::testing::ElementsAre(EStandardModule::Base, EStandardModule::String)); } TEST_F(ALuaConfig_StdModules, CantAddTheSameStandardModuleTwice) { LuaConfig config; - EXPECT_TRUE(config.m_impl->getStandardModules().empty()); + EXPECT_TRUE(config.impl().getStandardModules().empty()); ASSERT_TRUE(config.addStandardModuleDependency(EStandardModule::Base)); EXPECT_FALSE(config.addStandardModuleDependency(EStandardModule::Base)); @@ -188,7 +188,7 @@ namespace ramses::internal EXPECT_TRUE(config.addStandardModuleDependency(EStandardModule::All)); - EXPECT_THAT(config.m_impl->getStandardModules(), ::testing::ElementsAre( + EXPECT_THAT(config.impl().getStandardModules(), ::testing::ElementsAre( EStandardModule::Base, EStandardModule::String, EStandardModule::Table, diff --git a/client/logic/unittests/api/LuaInterfaceTest.cpp b/tests/unittests/client/logic/api/LuaInterfaceTest.cpp similarity index 79% rename from client/logic/unittests/api/LuaInterfaceTest.cpp rename to tests/unittests/client/logic/api/LuaInterfaceTest.cpp index d13ee1f96..1e559bec7 100644 --- a/client/logic/unittests/api/LuaInterfaceTest.cpp +++ b/tests/unittests/client/logic/api/LuaInterfaceTest.cpp @@ -7,33 +7,33 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "WithTempDirectory.h" +#include "LogicEngineTest_Base.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "impl/LuaInterfaceImpl.h" -#include "impl/LogicEngineImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/ErrorReporting.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "impl/logic/LuaInterfaceImpl.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" -#include "generated/LuaInterfaceGen.h" +#include "internal/logic/flatbuffers/generated/LuaInterfaceGen.h" #include "SerializationTestUtils.h" namespace ramses::internal { - class ALuaInterface : public ::testing::Test + class ALuaInterface : public ALogicEngine { protected: LuaInterface* createTestInterface(std::string_view source, std::string_view interfaceName = "") { - return m_logicEngine.createLuaInterface(source, interfaceName); + return m_logicEngine->createLuaInterface(source, interfaceName); } void createTestInterfaceAndExpectFailure(std::string_view source, std::string_view interfaceName = "") { - const auto intf = m_logicEngine.createLuaInterface(source, interfaceName); + const auto intf = m_logicEngine->createLuaInterface(source, interfaceName); EXPECT_EQ(intf, nullptr); } @@ -45,8 +45,6 @@ namespace ramses::internal end )"; - - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; }; TEST_F(ALuaInterface, CanCompileLuaInterface) @@ -93,8 +91,7 @@ namespace ramses::internal end )SCRIPT", ""); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Can't create interface with empty name!")); } @@ -108,7 +105,7 @@ namespace ramses::internal intf->getInputs()->getChild(0)->set(123); ASSERT_EQ(intf->getInputs()->getChild(0)->get(), intf->getOutputs()->getChild(0)->get()); - intf->m_impl.update(); + intf->impl().update(); EXPECT_EQ(123, intf->getOutputs()->getChild(0)->get()); } @@ -131,7 +128,7 @@ namespace ramses::internal ASSERT_NE(nullptr, intf); EXPECT_STREQ("intf name", intf->getName().data()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 0u); + expectNoError(); } TEST_F(ALuaInterface, ReportsErrorIfInterfaceDidNotCompile) @@ -143,16 +140,14 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("[intf name] Error while loading interface. Lua stack trace:\n[string \"intf name\"]:3: unexpected symbol near 'not'")); } TEST_F(ALuaInterface, ReportsErrorIfNoInterfaceFunctionDefined) { createTestInterfaceAndExpectFailure(R"SCRIPT()SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_STREQ("[intf name] No 'interface' function defined!", m_logicEngine.getErrors()[0].message.c_str()); + EXPECT_STREQ("[intf name] No 'interface' function defined!", getLastErrorMessage().c_str()); } TEST_F(ALuaInterface, Sandboxing_ReportsErrorIfInitFunctionDefined) @@ -169,8 +164,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected function name 'init'! Only 'interface' function can be declared!")); } @@ -189,8 +183,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected function name 'run'! Only 'interface' function can be declared!")); } @@ -208,8 +201,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global access to key 'GLOBAL' in interface()!")); } @@ -227,8 +219,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Declaring global variables is forbidden (exception: the 'interface' function)! (found value of type 'number')")); } @@ -242,8 +233,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global access to key 'IN' in interface()!")); } @@ -257,8 +247,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Trying to read global variable 'table' in an interface!")); } @@ -272,8 +261,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected variable definition 'thisCausesError' in interface()!")); } @@ -287,8 +275,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected variable definition 'GLOBAL' in interface()!")); } @@ -304,7 +291,7 @@ namespace ramses::internal )SCRIPT", "intf name"); EXPECT_NE(nullptr, intf); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); + expectNoError(); } TEST_F(ALuaInterface, Sandboxing_ReportsErrorIfUnknownFunctionDefined) @@ -323,8 +310,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected function name 'HackToCatchDeadlineCozNobodyChecksDeliveries'! Only 'interface' function can be declared!")); } @@ -340,7 +326,7 @@ namespace ramses::internal )SCRIPT", "intf name"); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Function 'interface' can only be declared once!")); } @@ -355,7 +341,7 @@ namespace ramses::internal end )SCRIPT", specialFunction), "intf name"); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(fmt::format("Unexpected global access to key '{}' in interface()!", specialFunction))); } } @@ -414,7 +400,7 @@ namespace ramses::internal ASSERT_EQ(intf->getOutputs()->getChild(0)->getChild(0)->get(), 123); ASSERT_EQ(intf->getOutputs()->getChild(2)->getChild(0)->get(), 456); - intf->m_impl.update(); + intf->impl().update(); EXPECT_EQ(123, intf->getOutputs()->getChild(0)->getChild(0)->get()); EXPECT_EQ(456, intf->getOutputs()->getChild(2)->getChild(0)->get()); } @@ -430,16 +416,16 @@ namespace ramses::internal end )", "intf name"); - const auto& intfImpl = intf->m_interface; + const auto& intfImpl = intf->impl(); - const auto* output1 = intf->getOutputs()->getChild(0); - const auto* output21 = intf->getOutputs()->getChild(1)->getChild(0); - const auto* output22 = intf->getOutputs()->getChild(1)->getChild(1); + auto* output1 = intf->getOutputs()->getChild(0); + auto* output21 = intf->getOutputs()->getChild(1)->getChild(0); + auto* output22 = intf->getOutputs()->getChild(1)->getChild(1); auto unlinkedOutputs = intfImpl.collectUnlinkedProperties(); EXPECT_THAT(unlinkedOutputs, ::testing::UnorderedElementsAre(output1, output21, output22)); - LuaScript* inputsScript = m_logicEngine.createLuaScript(R"LUA_SCRIPT( + LuaScript* inputsScript = m_logicEngine->createLuaScript(R"LUA_SCRIPT( function interface(IN,OUT) IN.param1 = Type:Int32() @@ -453,16 +439,16 @@ namespace ramses::internal )LUA_SCRIPT", {}); //link 1 output - m_logicEngine.link(*output1, *inputsScript->getInputs()->getChild(0)); + m_logicEngine->link(*output1, *inputsScript->getInputs()->getChild(0)); unlinkedOutputs = intfImpl.collectUnlinkedProperties(); EXPECT_THAT(unlinkedOutputs, ::testing::UnorderedElementsAre(output21, output22)); //link 2nd output - m_logicEngine.link(*output21, *inputsScript->getInputs()->getChild(1)); + m_logicEngine->link(*output21, *inputsScript->getInputs()->getChild(1)); unlinkedOutputs = intfImpl.collectUnlinkedProperties(); EXPECT_THAT(unlinkedOutputs, ::testing::UnorderedElementsAre(output22)); - m_logicEngine.link(*output22, *inputsScript->getInputs()->getChild(2)); + m_logicEngine->link(*output22, *inputsScript->getInputs()->getChild(2)); unlinkedOutputs = intfImpl.collectUnlinkedProperties(); EXPECT_TRUE(unlinkedOutputs.empty()); } @@ -502,11 +488,11 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSrc1); + LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("mymod", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); const auto colorProp = intf->getInputs()->getChild(0); @@ -531,13 +517,13 @@ namespace ramses::internal end )"; - const LuaModule* mod1 = m_logicEngine.createLuaModule(m_moduleSrc1); - const LuaModule* mod2 = m_logicEngine.createLuaModule(m_moduleSrc2); + const LuaModule* mod1 = m_logicEngine->createLuaModule(m_moduleSrc1); + const LuaModule* mod2 = m_logicEngine->createLuaModule(m_moduleSrc2); LuaConfig config; config.addDependency("mymod1", *mod1); config.addDependency("mymod2", *mod2); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); const auto colorProp = intf->getInputs()->getChild(0); @@ -565,12 +551,12 @@ namespace ramses::internal end )"; - const LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSrc1); + const LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("mymod1", *mod); config.addDependency("mymod2", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); for (size_t i = 0u; i < 2; ++i) @@ -606,11 +592,11 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); LuaConfig config; config.addDependency("mymod", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); const auto colorProp = intf->getInputs()->getChild(0); @@ -634,11 +620,11 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSrc1); + LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("mymod", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); ASSERT_EQ(1u, intf->getInputs()->getChildCount()); @@ -677,11 +663,11 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); LuaConfig config; config.addDependency("mymod", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); ASSERT_EQ(1u, intf->getInputs()->getChildCount()); @@ -719,11 +705,11 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); LuaConfig config; config.addDependency("mymod", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); ASSERT_EQ(1u, intf->getInputs()->getChildCount()); @@ -753,12 +739,12 @@ namespace ramses::internal return mod )"; - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); LuaConfig config; config.addDependency("mymod", *mod); config.addStandardModuleDependency(EStandardModule::Base); - const auto intf = m_logicEngine.createLuaInterface(R"( + const auto intf = m_logicEngine->createLuaInterface(R"( modules("mymod") function interface(IN) IN.dummy = Type:Int32() @@ -786,14 +772,14 @@ namespace ramses::internal end )"; - const LuaModule* originalMod = m_logicEngine.createLuaModule(m_moduleSrc1); + const LuaModule* originalMod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig modConfig; modConfig.addDependency("originalMod", *originalMod); - const LuaModule* wrapMod = m_logicEngine.createLuaModule(wrapModuleSrc, modConfig); + const LuaModule* wrapMod = m_logicEngine->createLuaModule(wrapModuleSrc, modConfig); LuaConfig config; config.addDependency("mymod", *wrapMod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); const auto colorProp = intf->getInputs()->getChild(0); @@ -819,7 +805,7 @@ namespace ramses::internal LuaConfig config; config.addStandardModuleDependency(EStandardModule::Math); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); ASSERT_NE(intf, nullptr); ASSERT_EQ(1u, intf->getInputs()->getChildCount()); @@ -841,13 +827,12 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSrc1); + LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("dep2", *mod); - EXPECT_EQ(nullptr, m_logicEngine.createLuaInterface(src, "intf", config)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep2")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaInterface(src, "intf", config)); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep2")); } TEST_F(ALuaInterfaceWithModule, FailsToBeCreatedIfDeclaredDependencyDoesNotMatchProvidedDependency_ProvidedButNotDeclared) @@ -858,15 +843,14 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSrc1); + LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("dep1", *mod); config.addDependency("dep2", *mod); config.addDependency("dep3", *mod); - EXPECT_EQ(nullptr, m_logicEngine.createLuaInterface(src, "intf", config)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep1, dep2, dep3")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaInterface(src, "intf", config)); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep1, dep2, dep3")); } TEST_F(ALuaInterfaceWithModule, FailsToBeCreatedIfDeclaredDependencyDoesNotMatchProvidedDependency_ExractionError) @@ -877,13 +861,12 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSrc1); + LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("dep1", *mod); - EXPECT_EQ(nullptr, m_logicEngine.createLuaInterface(src, "intf", config)); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Error while extracting module dependencies: 'dep1' appears more than once in dependency list")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaInterface(src, "intf", config)); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error while extracting module dependencies: 'dep1' appears more than once in dependency list")); } TEST_F(ALuaInterfaceWithModule, FailsToBeCreatedWhenOverwritingModuleData) @@ -902,14 +885,13 @@ namespace ramses::internal end )"; - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); LuaConfig config; config.addDependency("mymod", *mod); - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf", config); + const auto intf = m_logicEngine->createLuaInterface(intfSrc, "intf", config); EXPECT_EQ(nullptr, intf); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaInterfaceWithModule, FailsToBeCreatedWhenUsingModuleOverwritingTypeSymbols) @@ -921,11 +903,11 @@ namespace ramses::internal end return m )"; - const auto luaModule = m_logicEngine.createLuaModule(modSrc); + const auto luaModule = m_logicEngine->createLuaModule(modSrc); LuaConfig config; config.addDependency("mymod", *luaModule); - const auto intf = m_logicEngine.createLuaInterface(R"( + const auto intf = m_logicEngine->createLuaInterface(R"( modules("mymod") function interface(IN) IN.dummy = Type:Int32() @@ -933,33 +915,31 @@ namespace ramses::internal end )", "intf", config); EXPECT_EQ(nullptr, intf); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Special global 'Type' symbol should not be overwritten in modules!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Special global 'Type' symbol should not be overwritten in modules!")); } TEST_F(ALuaInterfaceWithModule, FailsToBeCreatedWhenUsingModuleFromAnotherLogicInstance) { - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& otherLogicEngine = *m_scene->createLogicEngine(); const auto luaModule = otherLogicEngine.createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("mymod", *luaModule); - const auto intf = m_logicEngine.createLuaInterface(R"( + const auto intf = m_logicEngine->createLuaInterface(R"( modules("mymod") function interface(IN) IN.dummy = Type:Int32() end )", "intf", config); EXPECT_EQ(nullptr, intf); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, "Failed to map Lua module 'mymod'! It was created on a different instance of LogicEngine."); + EXPECT_EQ(getLastErrorMessage(), "Failed to map Lua module 'mymod'! It was created on a different instance of LogicEngine."); } TEST_F(ALuaInterfaceWithModule, CanBeSerializedAndLoadedInBaseFeatureLevel_confidenceTest) // just to verify that module dependency does not affect serialized interface { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine logicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& logicEngine = *m_logicEngine; const auto luaModule = logicEngine.createLuaModule(m_moduleSrc1); LuaConfig config; config.addDependency("mymod", *luaModule); @@ -971,13 +951,12 @@ namespace ramses::internal end )", "intf", config); - SaveFileConfig configNoValidation; - configNoValidation.setValidationEnabled(false); - EXPECT_TRUE(logicEngine.saveToFile("temp.rlogic", configNoValidation)); + EXPECT_TRUE(saveToFileWithoutValidation("temp.ramses")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("temp.rlogic")); - const auto intf = m_logicEngine.findByName("intf"); + ASSERT_TRUE(recreateFromFile("temp.ramses")); + ASSERT_TRUE(m_logicEngine != nullptr); + const auto intf = m_logicEngine->findObject("intf"); ASSERT_NE(intf, nullptr); const auto colorProp = intf->getInputs()->getChild(0); @@ -991,8 +970,7 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::Int32, colorProp->getChild("green")->getType()); } - // tests that behavior of deprecated API is unchanged, some old assets have modules keyword in interface with invalid arguments - TEST_F(ALuaInterfaceWithModule, IgnoresModulesDeclaredInScriptIfUsingOldMethodToCreateInterface) + TEST_F(ALuaInterfaceWithModule, VerifiesModulesDeclaredInScriptWhenCreatingInterface) { constexpr std::string_view intfSrc = R"( modules("nonExistentMod") @@ -1001,20 +979,9 @@ namespace ramses::internal end )"; - const auto intf = m_logicEngine.createLuaInterface(intfSrc, "intf"); - ASSERT_NE(intf, nullptr); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - - ASSERT_EQ(1u, intf->getInputs()->getChildCount()); - const auto prop = intf->getInputs()->getChild(0); - EXPECT_EQ("dummy", prop->getName()); - EXPECT_EQ(EPropertyType::Int32, prop->getType()); - - // if created with new API this will be error - const auto intfInvalid = m_logicEngine.createLuaInterface(intfSrc, "intf", {}); + const auto intfInvalid = m_logicEngine->createLuaInterface(intfSrc, "intf", {}); EXPECT_EQ(intfInvalid, nullptr); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code do not match those provided by LuaConfig")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code do not match those provided by LuaConfig")); } class ALuaInterface_Serialization : public ALuaInterface @@ -1037,7 +1004,7 @@ namespace ramses::internal const std::string_view name = (issue == ESerializationIssue::EmptyName ? "" : "intf"); auto intf = rlogic_serialization::CreateLuaInterface(m_flatBufferBuilder, - issue == ESerializationIssue::NameIdMissing ? 0 : rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, m_flatBufferBuilder.CreateString(name), 1u, 0u, 0u), + issue == ESerializationIssue::NameIdMissing ? 0 : rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, m_flatBufferBuilder.CreateString(name), 1u), issue == ESerializationIssue::RootMissing ? 0 : PropertyImpl::Serialize(*inputsImpl, m_flatBufferBuilder, m_serializationMap) ); m_flatBufferBuilder.Finish(intf); @@ -1050,18 +1017,18 @@ namespace ramses::internal flatbuffers::FlatBufferBuilder m_flatBufferBuilder; SerializationTestUtils m_testUtils{ m_flatBufferBuilder }; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; ErrorReporting m_errorReporting; }; TEST_F(ALuaInterface_Serialization, CanSerializeAndDeserializeLuaInterface) { - WithTempDirectory tempDirectory; + withTempDirectory(); // Serialize { - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; - const LuaScript* inputsScript = otherEngine.createLuaScript(R"LUA_SCRIPT( + LogicEngine& otherEngine = *m_logicEngine; + LuaScript* inputsScript = otherEngine.createLuaScript(R"LUA_SCRIPT( function interface(IN,OUT) IN.param1 = Type:Int32() IN.param2 = { x = Type:Float(), y = Type:Array(2, Type:String()) } @@ -1071,7 +1038,7 @@ namespace ramses::internal end )LUA_SCRIPT", {}); - const LuaInterface* intf = otherEngine.createLuaInterface(R"( + LuaInterface* intf = otherEngine.createLuaInterface(R"( function interface(inout) inout.param1 = Type:Int32() inout.param2 = { x = Type:Float(), y = Type:Array(2, Type:String()) } @@ -1083,16 +1050,15 @@ namespace ramses::internal otherEngine.link(*intf->getOutputs()->getChild("param2")->getChild("y")->getChild(0u), *inputsScript->getInputs()->getChild("param2")->getChild("y")->getChild(0u)); otherEngine.link(*intf->getOutputs()->getChild("param2")->getChild("y")->getChild(1u), *inputsScript->getInputs()->getChild("param2")->getChild("y")->getChild(1u)); - SaveFileConfig configNoValidation; - configNoValidation.setValidationEnabled(false); - ASSERT_TRUE(otherEngine.saveToFile("interface.rlogic", configNoValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("interface.ramses")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("interface.rlogic")); - const auto loadedIntf = m_logicEngine.findByName("intf"); + ASSERT_TRUE(recreateFromFile("interface.ramses")); + ASSERT_TRUE(m_logicEngine != nullptr); + const auto loadedIntf = m_logicEngine->findObject("intf"); ASSERT_NE(nullptr, loadedIntf); - EXPECT_EQ(2u, loadedIntf->getId()); + EXPECT_EQ(10u, loadedIntf->getSceneObjectId().getValue()); EXPECT_EQ(loadedIntf->getInputs(), loadedIntf->getOutputs()); ASSERT_EQ(2u, loadedIntf->getInputs()->getChildCount()); const auto param1 = loadedIntf->getInputs()->getChild("param1"); @@ -1119,40 +1085,40 @@ namespace ramses::internal TEST_F(ALuaInterface_Serialization, FailsToSaveToFileIfInterfaceOutputsNotLinked) { createTestInterface(m_minimalInterface, "intf name"); - EXPECT_FALSE(m_logicEngine.saveToFile("interface.rlogic")); + EXPECT_FALSE(saveToFile("interface.ramses")); } TEST_F(ALuaInterface_Serialization, CanSerializeWithNoIssue) { EXPECT_TRUE(deserializeSerializedDataWithIssue(ALuaInterface_Serialization::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_F(ALuaInterface_Serialization, FailsDeserializationIfEssentialDataMissing) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ALuaInterface_Serialization::ESerializationIssue::NameIdMissing)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of LuaInterface from serialized data: missing name and/or ID!", m_errorReporting.getErrors().back().message); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaInterface from serialized data: missing name and/or ID!", m_errorReporting.getError()->message); } TEST_F(ALuaInterface_Serialization, FailsDeserializationIfNameEmpty) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ALuaInterface_Serialization::ESerializationIssue::EmptyName)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of LuaInterface from serialized data: empty name!", m_errorReporting.getErrors().back().message); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaInterface from serialized data: empty name!", m_errorReporting.getError()->message); } TEST_F(ALuaInterface_Serialization, FailsDeserializationIfRootPropertyMissing) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ALuaInterface_Serialization::ESerializationIssue::RootMissing)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of LuaInterface from serialized data: missing root property!", m_errorReporting.getErrors().back().message); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaInterface from serialized data: missing root property!", m_errorReporting.getError()->message); } TEST_F(ALuaInterface_Serialization, FailsDeserializationIfRootNotStructType) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ALuaInterface_Serialization::ESerializationIssue::RootNotStruct)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of LuaScript from serialized data: root property has unexpected type!", m_errorReporting.getErrors().back().message); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaScript from serialized data: root property has unexpected type!", m_errorReporting.getError()->message); } } diff --git a/client/logic/unittests/api/LuaModuleTest.cpp b/tests/unittests/client/logic/api/LuaModuleTest.cpp similarity index 72% rename from client/logic/unittests/api/LuaModuleTest.cpp rename to tests/unittests/client/logic/api/LuaModuleTest.cpp index 43bbe8914..78a210694 100644 --- a/client/logic/unittests/api/LuaModuleTest.cpp +++ b/tests/unittests/client/logic/api/LuaModuleTest.cpp @@ -8,25 +8,25 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "WithTempDirectory.h" +#include "LogicEngineTest_Base.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LuaModule.h" -#include "impl/LuaModuleImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/SolState.h" -#include "internals/DeserializationMap.h" -#include "internals/SerializationMap.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LuaModule.h" +#include "impl/logic/LuaModuleImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/SolState.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/SerializationMap.h" -#include "generated/LuaModuleGen.h" +#include "internal/logic/flatbuffers/generated/LuaModuleGen.h" #include namespace ramses::internal { - class ALuaModule : public ::testing::Test + class ALuaModule : public ALogicEngine { protected: const std::string_view m_moduleSourceCode = R"( @@ -42,67 +42,65 @@ namespace ramses::internal LuaConfig config; for (const auto& [alias, moduleSrc] : dependencies) { - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); config.addDependency(alias, *mod); } return config; } - - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; }; TEST_F(ALuaModule, IsCreated) { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); + const auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); ASSERT_NE(nullptr, module); EXPECT_EQ("mymodule", module->getName()); - EXPECT_EQ(module->getId(), 1u); + EXPECT_EQ(module->getSceneObjectId().getValue(), 9u); } TEST_F(ALuaModule, ChangesName) { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode); + const auto module = m_logicEngine->createLuaModule(m_moduleSourceCode); ASSERT_NE(nullptr, module); module->setName("mm"); EXPECT_EQ("mm", module->getName()); - EXPECT_EQ(module, this->m_logicEngine.findByName("mm")); - EXPECT_TRUE(this->m_logicEngine.getErrors().empty()); + EXPECT_EQ(module, this->m_logicEngine->findObject("mm")); + expectNoError(); } TEST_F(ALuaModule, FailsCreationIfSourceInvalid) { - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule("!", {})); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Error while loading module")); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("unexpected symbol near '!'")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule("!", {})); + auto msg = getLastErrorMessage(); + EXPECT_THAT(msg, ::testing::HasSubstr("Error while loading module")); + EXPECT_THAT(msg, ::testing::HasSubstr("unexpected symbol near '!'")); - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule(R"( + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule(R"( local mymath = {} function mymath.add(a,b) print(a+b) return mymath )")); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Error while loading module")); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("expected (to close 'function'")); + msg = getLastErrorMessage(); + EXPECT_THAT(msg, ::testing::HasSubstr("Error while loading module")); + EXPECT_THAT(msg, ::testing::HasSubstr("expected (to close 'function'")); - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule(R"( + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule(R"( local mymath = {} function mymath.add(a,b) print(a+b end return mymath )")); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Error while loading module")); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("expected (to close '('")); + msg = getLastErrorMessage(); + EXPECT_THAT(msg, ::testing::HasSubstr("Error while loading module")); + EXPECT_THAT(msg, ::testing::HasSubstr("expected (to close '('")); } TEST_F(ALuaModule, ProducesErrorMessageCorrectlyNotConflictingWithFmtFormatSyntax) { - auto* module = m_logicEngine.createLuaModule(R"( + auto* module = m_logicEngine->createLuaModule(R"( local coalaModule = {} coalaModule.coalaStruct = { oink1 @@ -112,8 +110,7 @@ namespace ramses::internal )", {}, "missingComma"); ASSERT_EQ(nullptr, module); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'}' expected (to close '{'")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'}' expected (to close '{'")); } class ALuaModule_SerializationLifecycle : public ALuaModule @@ -122,39 +119,39 @@ namespace ramses::internal SolState m_solState; ErrorReporting m_errorReporting; flatbuffers::FlatBufferBuilder m_flatBufferBuilder; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; }; TEST_F(ALuaModule_SerializationLifecycle, CanBeSerialized) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine logic{ m_logicEngine.getFeatureLevel() }; + LogicEngine& logic = *m_logicEngine; logic.createLuaModule(m_moduleSourceCode, {}, "mymodule"); - EXPECT_TRUE(logic.saveToFile("module.tmp")); + EXPECT_TRUE(saveToFileWithoutValidation("module.tmp")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("module.tmp")); - const auto module = m_logicEngine.findByName("mymodule"); + ASSERT_TRUE(recreateFromFile("module.tmp")); + const auto module = m_logicEngine->findObject("mymodule"); ASSERT_NE(nullptr, module); EXPECT_EQ("mymodule", module->getName()); - EXPECT_EQ(module->getId(), 1u); + EXPECT_EQ(module->getSceneObjectId().getValue(), 9u); } TEST_F(ALuaModule_SerializationLifecycle, StoresDuplicateByteCodeOnce) { SerializationMap serializationMap; - LogicEngine logic{ m_logicEngine.getFeatureLevel() }; + LogicEngine& logic = *m_scene->createLogicEngine(); auto module1 = logic.createLuaModule(m_moduleSourceCode, {}, "mymodule1"); auto module2 = logic.createLuaModule(m_moduleSourceCode, {}, "mymodule2"); - auto serOffset1 = LuaModuleImpl::Serialize(module1->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); + auto serOffset1 = LuaModuleImpl::Serialize(module1->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); m_flatBufferBuilder.Finish(serOffset1); const auto& serialized1 = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - auto serOffset2 = LuaModuleImpl::Serialize(module2->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); + auto serOffset2 = LuaModuleImpl::Serialize(module2->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); m_flatBufferBuilder.Finish(serOffset2); const auto& serialized2 = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); @@ -177,9 +174,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of LuaModule from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaModule from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } TEST_F(ALuaModule_SerializationLifecycle, ProducesErrorWhenIdMissing) @@ -198,9 +194,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaModuleImpl::Deserialize(this->m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of LuaModule from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaModule from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } TEST_F(ALuaModule_SerializationLifecycle, ProducesErrorWhenBothsourceAndBytecodeMissing) @@ -223,8 +218,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaModule from serialized data: has neither Lua source code nor bytecode!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaModule from serialized data: has neither Lua source code nor bytecode!"); } TEST_F(ALuaModule_SerializationLifecycle, ProducesErrorWhensourceAndBytecodeEmpty) @@ -247,8 +242,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaModule from serialized data: has neither Lua source code nor bytecode!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaModule from serialized data: has neither Lua source code nor bytecode!"); } TEST_F(ALuaModule_SerializationLifecycle, ProducesErrorWhenDependenciesMissing) @@ -271,8 +266,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaModule from serialized data: missing dependencies!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaModule from serialized data: missing dependencies!"); } TEST_F(ALuaModule_SerializationLifecycle, ProducesErrorWhenByteCodeInvalidAndNoSourceAvailable) @@ -297,9 +292,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_THAT(m_errorReporting.getErrors()[0].message, ::testing::HasSubstr("Fatal error during loading of LuaModule 'name': failed loading pre-compiled byte code")); - EXPECT_THAT(m_errorReporting.getErrors()[1].message, ::testing::HasSubstr("Fatal error during loading of LuaModule 'name' from serialized data")); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_THAT(m_errorReporting.getError()->message, ::testing::HasSubstr("Fatal error during loading of LuaModule 'name': failed loading pre-compiled byte code")); } TEST_F(ALuaModule_SerializationLifecycle, WillTryToRecompileModuleFromSourceWhenByteCodeInvalid) @@ -343,10 +337,10 @@ namespace ramses::internal TEST_F(ALuaModule_SerializationLifecycle, SerializesSourceCodeOnly_InSourceOnlyMode) { - auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); + auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); SerializationMap serializationMap; - const auto fbModule = LuaModuleImpl::Serialize(module->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::SourceCodeOnly); + const auto fbModule = LuaModuleImpl::Serialize(module->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::SourceCodeOnly); m_flatBufferBuilder.Finish(fbModule); const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); @@ -357,10 +351,10 @@ namespace ramses::internal TEST_F(ALuaModule_SerializationLifecycle, SerializesBytecodeOnly_InBytecodeOnlyMode) { - auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); + auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); SerializationMap serializationMap; - const auto fbModule = LuaModuleImpl::Serialize(module->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); + const auto fbModule = LuaModuleImpl::Serialize(module->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); m_flatBufferBuilder.Finish(fbModule); const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); @@ -374,10 +368,10 @@ namespace ramses::internal // simulate module with no source code by serializing it with bytecode only and deserializing again std::unique_ptr moduleWithNoSourceCode; { - auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); + auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); SerializationMap serializationMap; - const auto fbModule = LuaModuleImpl::Serialize(module->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); + const auto fbModule = LuaModuleImpl::Serialize(module->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); m_flatBufferBuilder.Finish(fbModule); const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); moduleWithNoSourceCode = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); @@ -400,10 +394,10 @@ namespace ramses::internal // simulate module with no source code by serializing it with bytecode only and deserializing again std::unique_ptr moduleWithNoSourceCode; { - auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); + auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); SerializationMap serializationMap; - const auto fbModule = LuaModuleImpl::Serialize(module->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); + const auto fbModule = LuaModuleImpl::Serialize(module->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::ByteCodeOnly); m_flatBufferBuilder.Finish(fbModule); const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); moduleWithNoSourceCode = LuaModuleImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); @@ -423,10 +417,10 @@ namespace ramses::internal TEST_F(ALuaModule_SerializationLifecycle, SerializesBothSourceAndBytecode) { - auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymodule"); + auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymodule"); SerializationMap serializationMap; - const auto fbModule = LuaModuleImpl::Serialize(module->m_impl, m_flatBufferBuilder, serializationMap, ELuaSavingMode::SourceAndByteCode); + const auto fbModule = LuaModuleImpl::Serialize(module->impl(), m_flatBufferBuilder, serializationMap, ELuaSavingMode::SourceAndByteCode); m_flatBufferBuilder.Finish(fbModule); const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); @@ -439,18 +433,18 @@ namespace ramses::internal TEST_F(ALuaModule_SerializationLifecycle, DoesNotContainDebugLogFunctionsAfterDeserialization) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine logic{ m_logicEngine.getFeatureLevel() }; + LogicEngine& logic = *m_logicEngine; logic.createLuaModule(m_moduleSourceCode, {}, "mymodule"); - EXPECT_TRUE(logic.saveToFile("module.tmp")); + EXPECT_TRUE(saveToFileWithoutValidation("module.tmp")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("module.tmp")); - const auto module = m_logicEngine.findByName("mymodule"); + ASSERT_TRUE(recreateFromFile("module.tmp")); + const auto module = m_logicEngine->findObject("mymodule"); ASSERT_NE(nullptr, module); - EXPECT_FALSE(module->m_impl.hasDebugLogFunctions()); + EXPECT_FALSE(module->impl().hasDebugLogFunctions()); } class ALuaModuleWithDependency : public ALuaModule @@ -476,10 +470,10 @@ namespace ramses::internal TEST_F(ALuaModuleWithDependency, IsCreated) { - const auto quadsMod = m_logicEngine.createLuaModule(m_quadsSrc, createDeps({{"mymath", m_mathSrc}}), "quadsMod"); + const auto quadsMod = m_logicEngine->createLuaModule(m_quadsSrc, createDeps({{"mymath", m_mathSrc}}), "quadsMod"); ASSERT_NE(nullptr, quadsMod); EXPECT_EQ("quadsMod", quadsMod->getName()); - EXPECT_EQ(quadsMod->getId(), 2u); // module dependency has id 1u + EXPECT_EQ(quadsMod->getSceneObjectId().getValue(), 10u); // module dependency has id 8u } TEST_F(ALuaModuleWithDependency, HasTwoDependencies) @@ -500,11 +494,11 @@ namespace ramses::internal mymath.sub=_mathSub.sub return mymath )"; - const auto mathCombined = m_logicEngine.createLuaModule(mathCombinedSrc, createDeps({ {"_mathAdd", m_mathSrc}, {"_mathSub", mathSubSrc} })); + const auto mathCombined = m_logicEngine->createLuaModule(mathCombinedSrc, createDeps({ {"_mathAdd", m_mathSrc}, {"_mathSub", mathSubSrc} })); LuaConfig config; config.addDependency("_math", *mathCombined); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("_math") function interface(IN,OUT) OUT.added = Type:Int32() @@ -518,7 +512,7 @@ namespace ramses::internal )", config); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(3, *script->getOutputs()->getChild("added")->get()); EXPECT_EQ(10, *script->getOutputs()->getChild("subbed")->get()); } @@ -532,11 +526,11 @@ namespace ramses::internal math.add2=math2.add return math )"; - const auto mathCombined = m_logicEngine.createLuaModule(mathCombinedSrc, createDeps({ {"math1", m_mathSrc}, {"math2", m_mathSrc} })); + const auto mathCombined = m_logicEngine->createLuaModule(mathCombinedSrc, createDeps({ {"math1", m_mathSrc}, {"math2", m_mathSrc} })); LuaConfig config; config.addDependency("mathAll", *mathCombined); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mathAll") function interface(IN,OUT) @@ -549,7 +543,7 @@ namespace ramses::internal )", config); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(113, *script->getOutputs()->getChild("result")->get()); } @@ -557,7 +551,7 @@ namespace ramses::internal { const auto config = createDeps({ {"mymath", m_mathSrc} }); - const auto mathUser1 = m_logicEngine.createLuaModule(R"( + const auto mathUser1 = m_logicEngine->createLuaModule(R"( modules("mymath") local mathUser1 = {} function mathUser1.add(a, b) @@ -566,7 +560,7 @@ namespace ramses::internal return mathUser1 )", config); - const auto mathUser2 = m_logicEngine.createLuaModule(R"( + const auto mathUser2 = m_logicEngine->createLuaModule(R"( modules("mymath") local mathUser2 = {} function mathUser2.add(a, b) @@ -578,7 +572,7 @@ namespace ramses::internal LuaConfig scriptConfig; scriptConfig.addDependency("math1", *mathUser1); scriptConfig.addDependency("math2", *mathUser2); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("math1", "math2") function interface(IN,OUT) OUT.result1 = Type:Int32() @@ -592,32 +586,32 @@ namespace ramses::internal )", scriptConfig); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(5, *script->getOutputs()->getChild("result1")->get()); EXPECT_EQ(23, *script->getOutputs()->getChild("result2")->get()); } TEST_F(ALuaModuleWithDependency, CanBeSerialized) { - WithTempDirectory tmpDir; + withTempDirectory(); { - LogicEngine logic{ m_logicEngine.getFeatureLevel() }; + LogicEngine& logic = *m_logicEngine; LuaConfig config; config.addDependency("mymath", *logic.createLuaModule(m_mathSrc, {}, "mathMod")); logic.createLuaModule(m_quadsSrc, config, "quadsMod"); - EXPECT_TRUE(logic.saveToFile("dep_modules.tmp")); + EXPECT_TRUE(saveToFileWithoutValidation("dep_modules.tmp")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("dep_modules.tmp")); + ASSERT_TRUE(recreateFromFile("dep_modules.tmp")); - const LuaModule* mathMod = m_logicEngine.findByName("mathMod"); - auto quadsMod = m_logicEngine.findByName("quadsMod"); + const LuaModule* mathMod = m_logicEngine->findObject("mathMod"); + auto quadsMod = m_logicEngine->findObject("quadsMod"); ASSERT_NE(mathMod, nullptr); ASSERT_NE(quadsMod, nullptr); - EXPECT_EQ(mathMod->getId(), 1u); - EXPECT_EQ(quadsMod->getId(), 2u); + EXPECT_EQ(mathMod->getSceneObjectId().getValue(), 9u); + EXPECT_EQ(quadsMod->getSceneObjectId().getValue(), 10u); - EXPECT_THAT(quadsMod->m_impl.getDependencies(), ::testing::ElementsAre(std::pair({"mymath", mathMod}))); + EXPECT_THAT(quadsMod->impl().getDependencies(), ::testing::ElementsAre(std::pair({"mymath", mathMod}))); } TEST_F(ALuaModuleWithDependency, UpdatesWithoutIssues_WhenModulesWithRuntimeErrors_AreNeverCalled) @@ -630,11 +624,11 @@ namespace ramses::internal end return mymath )"; - const auto quadsMod = m_logicEngine.createLuaModule(m_quadsSrc, createDeps({ {"mymath", errors} })); + const auto quadsMod = m_logicEngine->createLuaModule(m_quadsSrc, createDeps({ {"mymath", errors} })); ASSERT_NE(nullptr, quadsMod); // This works fine, because neither the the quads module nor the math modules are ever called - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } class ALuaModuleDependencyMatch : public ALuaModuleWithDependency @@ -649,9 +643,8 @@ namespace ramses::internal mymathExt.pi = 3.14 return mymathExt )"; - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule(mathExt, createDeps({ {"dep2", m_moduleSourceCode} }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep2")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule(mathExt, createDeps({ {"dep2", m_moduleSourceCode} }))); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep2")); } TEST_F(ALuaModuleDependencyMatch, FailsToBeCreatedIfDeclaredDependencyDoesNotMatchProvidedDependency_ProvidedButNotDeclared) @@ -662,9 +655,8 @@ namespace ramses::internal mymathExt.pi = 3.14 return mymathExt )"; - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule(mathExt, createDeps({ {"dep1", m_moduleSourceCode}, {"dep2", m_moduleSourceCode}, {"dep3", m_moduleSourceCode} }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep1, dep2, dep3")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule(mathExt, createDeps({ {"dep1", m_moduleSourceCode}, {"dep2", m_moduleSourceCode}, {"dep3", m_moduleSourceCode} }))); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep1, dep2, dep3")); } TEST_F(ALuaModuleDependencyMatch, FailsToBeCreatedIfDeclaredDependencyDoesNotMatchProvidedDependency_ExractionError) @@ -675,8 +667,7 @@ namespace ramses::internal mymathExt.pi = 3.14 return mymathExt )"; - EXPECT_EQ(nullptr, m_logicEngine.createLuaModule(mathExt, createDeps({ {"dep1", m_moduleSourceCode} }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Error while extracting module dependencies: 'dep1' appears more than once in dependency list")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaModule(mathExt, createDeps({ {"dep1", m_moduleSourceCode} }))); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error while extracting module dependencies: 'dep1' appears more than once in dependency list")); } } diff --git a/client/logic/unittests/api/LuaScriptTest_Debug.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Debug.cpp similarity index 60% rename from client/logic/unittests/api/LuaScriptTest_Debug.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Debug.cpp index 2c58e60ed..bd9566c9a 100644 --- a/client/logic/unittests/api/LuaScriptTest_Debug.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Debug.cpp @@ -9,15 +9,15 @@ #include "LuaScriptTest_Base.h" #include "WithTempDirectory.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LuaModuleImpl.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LuaModuleImpl.h" #include "LogTestUtils.h" #include -namespace ramses +namespace ramses::internal { class ALuaScript_Debug : public ALuaScript { @@ -39,50 +39,43 @@ namespace ramses )"; // Silence logs, unless explicitly enabled, to reduce spam and speed up tests - ScopedLogContextLevel m_silenceLogs{ ELogLevel::Off }; + ScopedLogContextLevel m_silenceLogs{ CONTEXT_CLIENT, ELogLevel::Off }; }; TEST_F(ALuaScript_Debug, ProducesErrorWithFullStackTrace_WhenErrorsInInterface) { - LuaScript* script = m_logicEngine.createLuaScript(m_scriptWithInterfaceError, {}, "errorscript"); + LuaScript* script = m_logicEngine->createLuaScript(m_scriptWithInterfaceError, {}, "errorscript"); ASSERT_EQ(nullptr, script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr( - "[errorscript] Error while loading script. Lua stack trace:\n" - "lua: error: Invalid type of field 'prop'! Expected Type:T() syntax where T=Float,Int32,... Found a value of type 'nil' instead")); - // nullptr because no LogicNode was created - EXPECT_EQ(nullptr, m_logicEngine.getErrors()[0].object); + expectErrorSubstring( + "[errorscript] Error while loading script. Lua stack trace:\n" + "lua: error: Invalid type of field 'prop'! Expected Type:T() syntax where T=Float,Int32,... Found a value of type 'nil' instead"); } TEST_F(ALuaScript_Debug, ProducesErrorWithFullStackTrace_WhenRuntimeErrors) { - LuaScript* script = m_logicEngine.createLuaScript(m_scriptWithRuntimeError, {}, "errorscript"); + LuaScript* script = m_logicEngine->createLuaScript(m_scriptWithRuntimeError, {}, "errorscript"); ASSERT_NE(nullptr, script); - m_logicEngine.update(); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr( - "lua: error: Tried to access undefined struct property 'prop'")); - EXPECT_EQ(script, m_logicEngine.getErrors()[0].object); + m_logicEngine->update(); + expectErrorSubstring("lua: error: Tried to access undefined struct property 'prop'", script); } TEST_F(ALuaScript_Debug, ErrorStackTraceContainsScriptName_WhenScriptWasNotLoadedFromFile) { // Script loaded from string, not file - LuaScript* script = m_logicEngine.createLuaScript(m_scriptWithInterfaceError, {}, "errorscript"); + LuaScript* script = m_logicEngine->createLuaScript(m_scriptWithInterfaceError, {}, "errorscript"); // Error message contains script name in the stack (file not known) EXPECT_EQ(nullptr, script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr( + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "[errorscript] Error while loading script. Lua stack trace:\n" "lua: error: Invalid type of field 'prop'! Expected Type:T() syntax where T=Float,Int32,... Found a value of type 'nil' instead")); } TEST_F(ALuaScript_Debug, LogsDebugMessage) { - ScopedLogContextLevel enableLogs{ ELogLevel::Info }; + ScopedLogContextLevel enableLogs{ CONTEXT_CLIENT, ELogLevel::Info }; constexpr std::string_view src = R"( function init() @@ -99,15 +92,15 @@ namespace ramses LuaConfig config; config.enableDebugLogFunctions(); - const LuaScript* script = m_logicEngine.createLuaScript(src, config); + const LuaScript* script = m_logicEngine->createLuaScript(src, config); ASSERT_NE(nullptr, script); - EXPECT_TRUE(script->m_script.hasDebugLogFunctions()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(script->impl().hasDebugLogFunctions()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_Debug, LogsDebugMessageUsingModule) { - ScopedLogContextLevel enableLogs{ ELogLevel::Info }; + ScopedLogContextLevel enableLogs{ CONTEXT_CLIENT, ELogLevel::Info }; constexpr std::string_view moduleSrc = R"( local mymod = {} @@ -133,17 +126,17 @@ namespace ramses LuaConfig configModule; configModule.enableDebugLogFunctions(); - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc, configModule); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc, configModule); ASSERT_NE(nullptr, mod); - EXPECT_TRUE(mod->m_impl.hasDebugLogFunctions()); + EXPECT_TRUE(mod->impl().hasDebugLogFunctions()); LuaConfig config; config.addDependency("mymod", *mod); - const LuaScript* script = m_logicEngine.createLuaScript(src, config); + const LuaScript* script = m_logicEngine->createLuaScript(src, config); ASSERT_NE(nullptr, script); - EXPECT_FALSE(script->m_script.hasDebugLogFunctions()); + EXPECT_FALSE(script->impl().hasDebugLogFunctions()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_Debug, FailsToCompileScriptWhenUsingLogDebugFunctionWithoutEnabling) @@ -161,7 +154,7 @@ namespace ramses end )"; - const LuaScript* script = m_logicEngine.createLuaScript(src); + const LuaScript* script = m_logicEngine->createLuaScript(src); EXPECT_EQ(nullptr, script); } @@ -189,12 +182,12 @@ namespace ramses end )"; - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); ASSERT_NE(nullptr, mod); LuaConfig config; config.addDependency("mymod", *mod); - const LuaScript* script = m_logicEngine.createLuaScript(src, config); + const LuaScript* script = m_logicEngine->createLuaScript(src, config); EXPECT_EQ(nullptr, script); } @@ -204,18 +197,16 @@ namespace ramses LuaConfig config; config.enableDebugLogFunctions(); - LuaScript* script = m_logicEngine.createLuaScript(m_minimalScript, config, "script"); + LuaScript* script = m_logicEngine->createLuaScript(m_minimalScript, config, "script"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(script->m_script.hasDebugLogFunctions()); + EXPECT_TRUE(script->impl().hasDebugLogFunctions()); - EXPECT_FALSE(m_logicEngine.saveToFile("willNotSave.tmp")); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, "Cannot save to file, Lua script 'script [Id=1]' has enabled debug log functions, remove this script before saving."); - EXPECT_EQ(m_logicEngine.getErrors().front().object, script); + EXPECT_FALSE(saveToFileWithoutValidation("willNotSave.tmp")); + expectError("Cannot save to file, Lua script 'script [LogicObject ScnObjId=9]' has enabled debug log functions, remove this script before saving.", script); // can save after removal - EXPECT_TRUE(m_logicEngine.destroy(*script)); - EXPECT_TRUE(m_logicEngine.saveToFile("willSave.tmp")); + EXPECT_TRUE(m_logicEngine->destroy(*script)); + EXPECT_TRUE(saveToFileWithoutValidation("willSave.tmp")); } TEST_F(ALuaScript_Debug, FailsToSaveToFileIfModuleHasDebugLogFunctions) @@ -232,17 +223,15 @@ namespace ramses LuaConfig config; config.enableDebugLogFunctions(); - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc, config, "mod"); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc, config, "mod"); ASSERT_NE(nullptr, mod); - EXPECT_TRUE(mod->m_impl.hasDebugLogFunctions()); + EXPECT_TRUE(mod->impl().hasDebugLogFunctions()); - EXPECT_FALSE(m_logicEngine.saveToFile("willNotSave.tmp")); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_EQ(m_logicEngine.getErrors().front().message, "Cannot save to file, Lua module 'mod [Id=1]' has enabled debug log functions, remove this module before saving."); - EXPECT_EQ(m_logicEngine.getErrors().front().object, mod); + EXPECT_FALSE(saveToFileWithoutValidation("willNotSave.tmp")); + expectError("Cannot save to file, Lua module 'mod [LogicObject ScnObjId=9]' has enabled debug log functions, remove this module before saving.", mod); // can save after removal - EXPECT_TRUE(m_logicEngine.destroy(*mod)); - EXPECT_TRUE(m_logicEngine.saveToFile("willSave.tmp")); + EXPECT_TRUE(m_logicEngine->destroy(*mod)); + EXPECT_TRUE(saveToFileWithoutValidation("willSave.tmp")); } } diff --git a/client/logic/unittests/api/LuaScriptTest_Init.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Init.cpp similarity index 80% rename from client/logic/unittests/api/LuaScriptTest_Init.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Init.cpp index e4e3d6f69..7dbca6fc3 100644 --- a/client/logic/unittests/api/LuaScriptTest_Init.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Init.cpp @@ -8,14 +8,13 @@ #include "LuaScriptTest_Base.h" -#include "ramses-logic/Property.h" -#include "impl/LogicEngineImpl.h" -#include "internals/ApiObjects.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/LogicEngineImpl.h" +#include "internal/logic/ApiObjects.h" #include "LogTestUtils.h" -#include "WithTempDirectory.h" -namespace ramses +namespace ramses::internal { class ALuaScript_Init : public ALuaScript { @@ -23,7 +22,7 @@ namespace ramses TEST_F(ALuaScript_Init, CreatesGlobals) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.number = 5 GLOBAL.string = "foo" @@ -43,7 +42,7 @@ namespace ramses end )"); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(5, *script->getOutputs()->getChild("number")->get()); EXPECT_EQ("foo", *script->getOutputs()->getChild("string")->get()); EXPECT_EQ(false, *script->getOutputs()->getChild("bool")->get()); @@ -51,7 +50,7 @@ namespace ramses TEST_F(ALuaScript_Init, CanUseGlobalsInInterface) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.inputNames = {"foo", "bar"} end @@ -70,14 +69,14 @@ namespace ramses )", WithStdModules({EStandardModule::Base})); ASSERT_NE(nullptr, script); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_FLOAT_EQ(4.2f, *script->getOutputs()->getChild("foo")->get()); EXPECT_FLOAT_EQ(4.2f, *script->getOutputs()->getChild("bar")->get()); } TEST_F(ALuaScript_Init, CanModifyGlobalsAsIfTheyWereGlobalVariables) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.number = 5 end @@ -95,17 +94,17 @@ namespace ramses end )"); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(5, *script->getOutputs()->getChild("getGlobal")->get()); EXPECT_TRUE(script->getInputs()->getChild("setGlobal")->set(42)); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(42, *script->getOutputs()->getChild("getGlobal")->get()); } TEST_F(ALuaScript_Init, CanDeclareFunctions) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.fun = function () return 42 end end @@ -119,13 +118,13 @@ namespace ramses end )"); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(42, *script->getOutputs()->getChild("getGlobal")->get()); } TEST_F(ALuaScript_Init, CanUseStandardModules) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.number = math.floor(4.2) end @@ -139,7 +138,7 @@ namespace ramses end )", WithStdModules({ EStandardModule::Math })); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(4, *script->getOutputs()->getChild("getGlobal")->get()); } @@ -155,9 +154,9 @@ namespace ramses )"; LuaConfig config; - config.addDependency("mymath", *m_logicEngine.createLuaModule(moduleSourceCode)); + config.addDependency("mymath", *m_logicEngine->createLuaModule(moduleSourceCode)); - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( modules("mymath") function init() @@ -171,13 +170,13 @@ namespace ramses end )", config); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_FLOAT_EQ(8.1415f, *script->getOutputs()->getChild("getGlobal")->get()); } TEST_F(ALuaScript_Init, IssuesErrorWhenUsingUndeclaredStandardModule) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.number = math.floor(4.2) end @@ -188,17 +187,17 @@ namespace ramses )"); EXPECT_EQ(script, nullptr); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Trying to read global variable 'math' in the init() function!")); } TEST_F(ALuaScript_Init, InitializesAfterDeserilization) { - WithTempDirectory tmpFolder; + withTempDirectory(); { - LogicEngine tmpLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tmpLogicEngine = *m_logicEngine; auto* script = tmpLogicEngine.createLuaScript(R"( function init() GLOBAL.number = 5 @@ -219,13 +218,14 @@ namespace ramses ASSERT_TRUE(tmpLogicEngine.update()); ASSERT_EQ(5, *script->getOutputs()->getChild("globalValueBefore")->get()); ASSERT_EQ(42, *script->getOutputs()->getChild("globalValueAfter")->get()); - ASSERT_TRUE(SaveToFileWithoutValidation(tmpLogicEngine, "withGlobals.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("withGlobals.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("withGlobals.bin")); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_EQ(5, *m_logicEngine.findByName("withGlobals")->getOutputs()->getChild("globalValueBefore")->get()); - EXPECT_EQ(42, *m_logicEngine.findByName("withGlobals")->getOutputs()->getChild("globalValueAfter")->get()); + ASSERT_TRUE(recreateFromFile("withGlobals.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(5, *m_logicEngine->findObject("withGlobals")->getOutputs()->getChild("globalValueBefore")->get()); + EXPECT_EQ(42, *m_logicEngine->findObject("withGlobals")->getOutputs()->getChild("globalValueAfter")->get()); } TEST_F(ALuaScript_Init, DoesNotLeaveAnyLuaStackObjectsWhenLuaScriptDestroyed) @@ -238,10 +238,10 @@ namespace ramses for (int i = 0; i < 100; ++i) { - auto* script = m_logicEngine.createLuaScript(scriptText); + auto* script = m_logicEngine->createLuaScript(scriptText); ASSERT_TRUE(script != nullptr); - ASSERT_TRUE(m_logicEngine.destroy(*script)); - EXPECT_EQ(0, m_logicEngine.m_impl->getApiObjects().getNumElementsInLuaStack()); + ASSERT_TRUE(m_logicEngine->destroy(*script)); + EXPECT_EQ(0, m_logicEngine->impl().getApiObjects().getNumElementsInLuaStack()); } } @@ -289,21 +289,21 @@ namespace ramses for (int i = 0; i < 100; ++i) { - auto module = m_logicEngine.createLuaModule(moduleSourceCode); + auto module = m_logicEngine->createLuaModule(moduleSourceCode); ASSERT_TRUE(module != nullptr); LuaConfig config; config.addDependency("mymodule", *module); - auto script = m_logicEngine.createLuaScript(scriptSourceCode, config); + auto script = m_logicEngine->createLuaScript(scriptSourceCode, config); ASSERT_TRUE(script != nullptr); - ASSERT_TRUE(m_logicEngine.destroy(*script)); - ASSERT_TRUE(m_logicEngine.destroy(*module)); - EXPECT_EQ(0, m_logicEngine.m_impl->getApiObjects().getNumElementsInLuaStack()); + ASSERT_TRUE(m_logicEngine->destroy(*script)); + ASSERT_TRUE(m_logicEngine->destroy(*module)); + EXPECT_EQ(0, m_logicEngine->impl().getApiObjects().getNumElementsInLuaStack()); } } TEST_F(ALuaScript_Init, ScriptUsesInterfaceTypeDefinitionFromGlobal) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.outputType = Type:String() GLOBAL.outputName = "name" @@ -319,14 +319,14 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_STREQ("MrAnderson", script->getOutputs()->getChild("name")->get()->c_str()); } TEST_F(ALuaScript_Init, ScriptUsesInterfaceTypeDefinitionFromGlobal_Array) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.outputType = Type:Array(2, Type:Int32()) end @@ -340,7 +340,7 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto arrayOutput = script->getOutputs()->getChild("array"); ASSERT_NE(nullptr, arrayOutput); ASSERT_EQ(2u, arrayOutput->getChildCount()); @@ -350,7 +350,7 @@ namespace ramses TEST_F(ALuaScript_Init, ScriptUsesInterfaceStructDefinedInGlobal) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.outputDefinition = { value = Type:Int32() } end @@ -364,7 +364,7 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); ASSERT_NE(nullptr, script->getOutputs()->getChild("struct")); ASSERT_NE(nullptr, script->getOutputs()->getChild("struct")->getChild("value")); EXPECT_EQ(666, *script->getOutputs()->getChild("struct")->getChild("value")->get()); @@ -372,7 +372,7 @@ namespace ramses TEST_F(ALuaScript_Init, ScriptUsesInterfaceStructDefinedInGlobal_WithArray) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.outputDefinition = { value = Type:Int32(), @@ -390,7 +390,7 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); ASSERT_NE(nullptr, script->getOutputs()->getChild("struct")); ASSERT_NE(nullptr, script->getOutputs()->getChild("struct")->getChild("value")); EXPECT_EQ(666, *script->getOutputs()->getChild("struct")->getChild("value")->get()); @@ -402,9 +402,9 @@ namespace ramses TEST_F(ALuaScript_Init, SaveAndLoadScriptUsingInterfaceStructDefinedInGlobalWithArray) { - WithTempDirectory tmpFolder; + withTempDirectory(); { - LogicEngine otherLogic{ m_logicEngine.getFeatureLevel() }; + LogicEngine& otherLogic = *m_logicEngine; LuaScript* script = otherLogic.createLuaScript(R"( function init() GLOBAL.outputDefinition = { @@ -423,13 +423,14 @@ namespace ramses end)", {}, "script"); ASSERT_NE(nullptr, script); EXPECT_TRUE(otherLogic.update()); - EXPECT_TRUE(SaveToFileWithoutValidation(otherLogic, "intfInGlobal.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("intfInGlobal.bin")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("intfInGlobal.bin")); - EXPECT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(recreateFromFile("intfInGlobal.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + EXPECT_TRUE(m_logicEngine->update()); - const auto script = m_logicEngine.findByName("script"); + const auto script = m_logicEngine->findObject("script"); ASSERT_NE(nullptr, script->getOutputs()->getChild("struct")); ASSERT_NE(nullptr, script->getOutputs()->getChild("struct")->getChild("value")); EXPECT_EQ(666, *script->getOutputs()->getChild("struct")->getChild("value")->get()); @@ -445,7 +446,7 @@ namespace ramses TEST_F(ALuaScript_Init_Sandboxing, ReportsErrorWhenTryingToReadUnknownGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() local t = someGlobalVariable end @@ -457,7 +458,7 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Trying to read global variable 'someGlobalVariable' in the init() function! This" " can cause undefined behavior and is forbidden! Use the GLOBAL table to read/write global data!")); @@ -465,7 +466,7 @@ namespace ramses TEST_F(ALuaScript_Init_Sandboxing, ReportsErrorWhenTryingToDeclareUnknownGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() thisCausesError = 'bad' end @@ -477,13 +478,13 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global variable definition 'thisCausesError' in init()! Please use the GLOBAL table to declare global data and functions, or use modules!")); } TEST_F(ALuaScript_Init_Sandboxing, ReportsErrorWhenTryingToOverrideGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() GLOBAL = {} end @@ -495,13 +496,13 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(" Trying to override the GLOBAL table in init()! You can only add data, but not overwrite the table!")); } TEST_F(ALuaScript_Init_Sandboxing, ReportsErrorWhenTryingToOverrideTypeTable) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() Type = {} end @@ -513,13 +514,13 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(" Can't override the Type special table in init()")); } TEST_F(ALuaScript_Init_Sandboxing, ReportsErrorWhenTryingToDeclareInitFunctionTwice) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() end @@ -533,7 +534,7 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Function 'init' can only be declared once!")); } @@ -541,7 +542,7 @@ namespace ramses { for (const auto& specialFunction : std::vector{ "init", "run", "interface" }) { - LuaScript* script = m_logicEngine.createLuaScript(fmt::format(R"( + LuaScript* script = m_logicEngine->createLuaScript(fmt::format(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -554,7 +555,7 @@ namespace ramses ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(fmt::format("Trying to read global variable '{}' in the init() function! This can cause undefined behavior " "and is forbidden! Use the GLOBAL table to read/write global data!", specialFunction))); } diff --git a/client/logic/unittests/api/LuaScriptTest_Interface.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Interface.cpp similarity index 87% rename from client/logic/unittests/api/LuaScriptTest_Interface.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Interface.cpp index 71a1639b0..d97250506 100644 --- a/client/logic/unittests/api/LuaScriptTest_Interface.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Interface.cpp @@ -8,24 +8,24 @@ #include "LuaScriptTest_Base.h" -#include "ramses-logic/Property.h" -#include "impl/PropertyImpl.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/PropertyImpl.h" #include "LogTestUtils.h" #include "fmt/format.h" #include -namespace ramses +namespace ramses::internal { class ALuaScript_Interface : public ALuaScript { protected: // Silence logs, unless explicitly enabled, to reduce spam and speed up tests - ScopedLogContextLevel m_silenceLogs{ ELogLevel::Off }; + ScopedLogContextLevel m_silenceLogs{ CONTEXT_CLIENT, ELogLevel::Off }; }; TEST_F(ALuaScript_Interface, DoesNotGenerateErrorWhenOverwritingInputsInInterfaceFunction) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN = {} end @@ -36,12 +36,12 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); + expectNoError(); } TEST_F(ALuaScript_Interface, DoesNotGenerateErrorWhenOverwritingOutputsInInterfaceFunction) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT = {} end @@ -52,12 +52,12 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_EQ(m_logicEngine.getErrors().size(), 0u); + expectNoError(); } TEST_F(ALuaScript_Interface, ProducesErrorsIfARuntimeErrorOccursInInterface) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) error("emits error") end @@ -67,8 +67,7 @@ namespace ramses )", WithStdModules({EStandardModule::Base}), "errorInInterface"); ASSERT_EQ(nullptr, script); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::AllOf( + EXPECT_THAT(getLastErrorMessage(), ::testing::AllOf( ::testing::HasSubstr("[errorInInterface] Error while loading script. Lua stack trace:"), ::testing::HasSubstr("[C]: in function 'error'"))); } @@ -84,7 +83,7 @@ namespace ramses end )"; - LuaScript* script = m_logicEngine.createLuaScript(explicitStructSyntax); + LuaScript* script = m_logicEngine->createLuaScript(explicitStructSyntax); auto inputs = script->getInputs(); @@ -107,7 +106,7 @@ namespace ramses end )"; - LuaScript* script = m_logicEngine.createLuaScript(explicitStructSyntax); + LuaScript* script = m_logicEngine->createLuaScript(explicitStructSyntax); auto inputs = script->getInputs(); ASSERT_EQ(1u, inputs->getChildCount()); @@ -139,14 +138,14 @@ namespace ramses end )"; - LuaScript* script = m_logicEngine.createLuaScript(explicitStructSyntax); + LuaScript* script = m_logicEngine->createLuaScript(explicitStructSyntax); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().back().message, ::testing::HasSubstr("Type:Struct(T) invoked with invalid type parameter T!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Type:Struct(T) invoked with invalid type parameter T!")); } TEST_F(ALuaScript_Interface, ReturnsItsTopLevelOutputsByIndex_OrderedLexicographically) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithOutputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithOutputs); auto outputs = script->getOutputs(); @@ -179,7 +178,7 @@ namespace ramses TEST_F(ALuaScript_Interface, ReturnsNestedOutputsByIndex_OrderedLexicographically_whenDeclaredOneByOne) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.struct = {} OUT.struct.field3 = {} @@ -225,7 +224,7 @@ namespace ramses TEST_F(ALuaScript_Interface, CanDeclarePropertiesProgramatically) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.root = {} local lastStruct = OUT.root @@ -264,29 +263,29 @@ namespace ramses TEST_F(ALuaScript_Interface, MarksInputsAsInput) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithInputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithInputs); auto inputs = script->getInputs(); const auto inputCount = inputs->getChildCount(); for (size_t i = 0; i < inputCount; ++i) { - EXPECT_EQ(internal::EPropertySemantics::ScriptInput, inputs->getChild(i)->m_impl->getPropertySemantics()); + EXPECT_EQ(internal::EPropertySemantics::ScriptInput, inputs->getChild(i)->impl().getPropertySemantics()); } } TEST_F(ALuaScript_Interface, MarksOutputsAsOutput) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithOutputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithOutputs); auto outputs = script->getOutputs(); const auto outputCount = outputs->getChildCount(); for (size_t i = 0; i < outputCount; ++i) { - EXPECT_EQ(internal::EPropertySemantics::ScriptOutput, outputs->getChild(i)->m_impl->getPropertySemantics()); + EXPECT_EQ(internal::EPropertySemantics::ScriptOutput, outputs->getChild(i)->impl().getPropertySemantics()); } } TEST_F(ALuaScript_Interface, AllowsAccessToWrappedUserdata) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array_int = Type:Array(2, Type:Int32()) OUT.struct = {a=Type:Int32(), b={c = Type:Int32(), d=Type:Float()}} @@ -322,7 +321,7 @@ namespace ramses TEST_F(ALuaScript_Interface, ReportsErrorWhenAccessingStructByIndex) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.struct = {a=Type:Int32(), b={c = Type:Int32(), d=Type:Float()}} local causesError = OUT.struct[1] @@ -332,12 +331,12 @@ namespace ramses end )"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().back().message, ::testing::HasSubstr("lua: error: Bad index access to struct 'struct': Expected a string but got object of type number instead!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("lua: error: Bad index access to struct 'struct': Expected a string but got object of type number instead!")); } TEST_F(ALuaScript_Interface, ReportsErrorWhenAccessingArrayByString) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array_int = Type:Array(2, Type:Int32()) local causesError = IN.array_int["causesError"] @@ -347,12 +346,12 @@ namespace ramses end )"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().back().message, ::testing::HasSubstr("lua: error: Invalid index access in array 'array_int'")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("lua: error: Invalid index access in array 'array_int'")); } TEST_F(ALuaScript_Interface, ReportsErrorWhenAccessingArrayWithIndexOverflow) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array_int = Type:Array(2, Type:Int32()) local causesError = IN.array_int[3] @@ -362,12 +361,12 @@ namespace ramses end )"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().back().message, ::testing::HasSubstr("Invalid index access in array 'array_int'. Expected index in the range [0, 2] but got 3 instead!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Invalid index access in array 'array_int'. Expected index in the range [0, 2] but got 3 instead!")); } TEST_F(ALuaScript_Interface, AssignsDefaultValuesToItsInputs) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithInputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithInputs); auto inputs = script->getInputs(); auto speedInt32 = inputs->getChild("speed"); @@ -395,7 +394,7 @@ namespace ramses TEST_F(ALuaScript_Interface, AssignsDefaultValuesToItsOutputs) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithOutputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithOutputs); auto outputs = script->getOutputs(); auto speedInt32 = outputs->getChild("speed"); @@ -440,7 +439,7 @@ namespace ramses TEST_F(ALuaScript_Interface, AssignsDefaultValuesToArrays) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array_int = Type:Array(3, Type:Int32()) IN.array_float = Type:Array(3, Type:Float()) @@ -513,9 +512,9 @@ namespace ramses OUT.sizes = GLOBAL.sizes end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc); + auto* script = m_logicEngine->createLuaScript(scriptSrc); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(1, *script->getOutputs()->getChild("sizes")->getChild("inputsSizeSingle")->get()); EXPECT_EQ(2, *script->getOutputs()->getChild("sizes")->getChild("inputsSizeTwo")->get()); @@ -531,19 +530,18 @@ namespace ramses TEST_F(ALuaScript_Interface_Sandboxing, DeclaringGlobalSymbolsCausesCompilationErrors) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( globalVar = "this will cause error" )", WithStdModules({ EStandardModule::Base })); ASSERT_EQ(nullptr, script); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Declaring global variables is forbidden (exceptions: the functions 'init', 'interface' and 'run')! (found value of type 'string')")); } TEST_F(ALuaScript_Interface_Sandboxing, ReportsErrorWhenTryingToReadUnknownGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) local t = someGlobalVariable end @@ -552,14 +550,14 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Unexpected global access to key 'someGlobalVariable' in interface()! Only 'GLOBAL' and 'Type' are allowed as a key")); } TEST_F(ALuaScript_Interface_Sandboxing, ReportsErrorWhenSettingGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) thisCausesError = 'bad' end @@ -568,7 +566,7 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Unexpected global variable definition 'thisCausesError' in interface()! " "Use the GLOBAL table inside the init() function to declare global data and functions, or use modules!")); @@ -576,7 +574,7 @@ namespace ramses TEST_F(ALuaScript_Interface_Sandboxing, ReportsErrorWhenTryingToOverrideGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() end @@ -588,13 +586,13 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Trying to override the GLOBAL table in interface()! You can only read data, but not overwrite the GLOBAL table!")); } TEST_F(ALuaScript_Interface_Sandboxing, ReportsErrorWhenTryingToOverrideTypesTable) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() end @@ -606,13 +604,13 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Can't override the 'Type' symbol in interface()!")); } TEST_F(ALuaScript_Interface_Sandboxing, ReportsErrorWhenTryingToDeclareInterfaceFunctionTwice) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -623,7 +621,7 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Function 'interface' can only be declared once!")); } @@ -631,7 +629,7 @@ namespace ramses { for (const auto& specialFunction : std::vector{ "init", "run", "interface" }) { - LuaScript* script = m_logicEngine.createLuaScript(fmt::format(R"( + LuaScript* script = m_logicEngine->createLuaScript(fmt::format(R"( function init() end function run(IN,OUT) @@ -644,7 +642,7 @@ namespace ramses ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(fmt::format("Unexpected global access to key '{}' in interface()! Only 'GLOBAL' and 'Type' are allowed as a key", specialFunction))); } } @@ -653,9 +651,7 @@ namespace ramses // Need to keep as confidence test to safeguard against regressions TEST_F(ALuaScript_Interface, Bugfix_Confidence_HandlesComplexTypeDeclarationWithNestedStructs) { - ramses::LogicEngine logicEngine{ m_logicEngine.getFeatureLevel() }; - - std::string scriptText = R"( + const std::string_view scriptText = R"( function interface(IN,OUT) local craneGimbal = { cam_Translation = Type:Vec3f(), @@ -705,13 +701,13 @@ namespace ramses end )"; - auto* script = logicEngine.createLuaScript(scriptText); + auto* script = m_logicEngine->createLuaScript(scriptText); ASSERT_TRUE(script != nullptr); } TEST_F(ALuaScript_Interface, OwnsAllProperties_confidenceTest) { - const auto* script = m_logicEngine.createLuaScript(R"( + const auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array_int = Type:Array(2, Type:Int32()) OUT.struct = {a=Type:Int32(), b={c = Type:Int32(), d=Type:Float()}} diff --git a/client/logic/unittests/api/LuaScriptTest_Lifecycle.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Lifecycle.cpp similarity index 82% rename from client/logic/unittests/api/LuaScriptTest_Lifecycle.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Lifecycle.cpp index dc7ff42bc..dc2dfc8cb 100644 --- a/client/logic/unittests/api/LuaScriptTest_Lifecycle.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Lifecycle.cpp @@ -7,11 +7,10 @@ // ------------------------------------------------------------------------- #include "LuaScriptTest_Base.h" -#include "WithTempDirectory.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LogicEngineImpl.h" -#include "impl/PropertyImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/PropertyImpl.h" #include "fmt/format.h" #include @@ -20,27 +19,30 @@ namespace ramses::internal { class ALuaScript_Lifecycle : public ALuaScript { - protected: - WithTempDirectory tempFolder; + public: + ALuaScript_Lifecycle() + { + withTempDirectory(); + } }; TEST_F(ALuaScript_Lifecycle, ProducesNoErrorsWhenCreatedFromMinimalScript) { - auto* script = m_logicEngine.createLuaScript(m_minimalScript); + auto* script = m_logicEngine->createLuaScript(m_minimalScript); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + expectNoError(); } TEST_F(ALuaScript_Lifecycle, ProvidesNameAsPassedDuringCreation) { - auto* script = m_logicEngine.createLuaScript(m_minimalScript, {}, "script name"); + auto* script = m_logicEngine->createLuaScript(m_minimalScript, {}, "script name"); EXPECT_EQ("script name", script->getName()); } //This is actually bad (because it can cause undefined behavior), but we still have a test to show/test how it works TEST_F(ALuaScript_Lifecycle, KeepsLocalScopeSymbolsDuringRunMethod) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( -- 'Local' symbols in the global space are inherited by functions local localSymbol = "localSymbol" @@ -55,7 +57,7 @@ namespace ramses::internal )"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(script->getOutputs()->getChild("result")->get(), "localSymbol"); } @@ -66,7 +68,7 @@ namespace ramses::internal TEST_F(ALuaScript_LifecycleWithFiles, NoOutputs) { { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript( R"( function interface(IN,OUT) @@ -77,11 +79,11 @@ namespace ramses::internal )"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "script.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("script.bin")); } { - EXPECT_TRUE(m_logicEngine.loadFromFile("script.bin")); - const LuaScript* loadedScript = *m_logicEngine.getCollection().begin(); + EXPECT_TRUE(recreateFromFile("script.bin")); + const LuaScript* loadedScript = *m_logicEngine->getCollection().begin(); ASSERT_NE(nullptr, loadedScript); @@ -97,14 +99,14 @@ namespace ramses::internal EXPECT_EQ("param", inputs->getChild(0u)->getName()); EXPECT_EQ(EPropertyType::Int32, inputs->getChild(0u)->getType()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } } TEST_F(ALuaScript_LifecycleWithFiles, Arrays) { { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript( R"( function interface(IN,OUT) @@ -119,11 +121,11 @@ namespace ramses::internal script->getInputs()->getChild("array")->getChild(0)->set(0.1f); script->getInputs()->getChild("array")->getChild(1)->set(0.2f); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "script.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("script.bin")); } { - m_logicEngine.loadFromFile("script.bin"); - const LuaScript* loadedScript = m_logicEngine.findByName("MyScript"); + recreateFromFile("script.bin"); + const LuaScript* loadedScript = m_logicEngine->findObject("MyScript"); const auto inputs = loadedScript->getInputs(); @@ -149,7 +151,7 @@ namespace ramses::internal TEST_F(ALuaScript_LifecycleWithFiles, NestedArray) { { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript( R"( function interface(IN,OUT) @@ -163,11 +165,11 @@ namespace ramses::internal )", {}, "MyScript"); script->getInputs()->getChild("nested")->getChild("array")->getChild(0)->set({1.1f, 1.2f, 1.3f}); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "arrays.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("arrays.bin")); } { - m_logicEngine.loadFromFile("arrays.bin"); - const LuaScript* loadedScript = m_logicEngine.findByName("MyScript"); + recreateFromFile("arrays.bin"); + const LuaScript* loadedScript = m_logicEngine->findObject("MyScript"); const auto inputs = loadedScript->getInputs(); @@ -192,7 +194,7 @@ namespace ramses::internal TEST_F(ALuaScript_LifecycleWithFiles, NestedProperties) { { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript( R"( function interface(IN,OUT) @@ -211,11 +213,11 @@ namespace ramses::internal )"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "nested_array.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("nested_array.bin")); } { - EXPECT_TRUE(m_logicEngine.loadFromFile("nested_array.bin")); - const LuaScript* loadedScript = *m_logicEngine.getCollection().begin(); + EXPECT_TRUE(recreateFromFile("nested_array.bin")); + const LuaScript* loadedScript = *m_logicEngine->getCollection().begin(); ASSERT_NE(nullptr, loadedScript); @@ -252,7 +254,7 @@ namespace ramses::internal EXPECT_EQ("float_param", out_nested_child->getName()); EXPECT_EQ(EPropertyType::Float, out_nested_child->getType()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_FLOAT_EQ(47.11f, *outputs->getChild(0u)->get()); } } @@ -260,7 +262,7 @@ namespace ramses::internal TEST_F(ALuaScript_LifecycleWithFiles, ArrayOfStructs) { { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript( R"( function interface(IN,OUT) @@ -282,11 +284,11 @@ namespace ramses::internal ASSERT_NE(nullptr, script); script->getInputs()->getChild("arrayOfStructs")->getChild(1)->getChild("nested_struct")->getChild("nested_array")->getChild(0)->set(42.f); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "array_of_structs.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("array_of_structs.bin")); } { - EXPECT_TRUE(m_logicEngine.loadFromFile("array_of_structs.bin")); - LuaScript* loadedScript = *m_logicEngine.getCollection().begin(); + EXPECT_TRUE(recreateFromFile("array_of_structs.bin")); + LuaScript* loadedScript = *m_logicEngine->getCollection().begin(); ASSERT_NE(nullptr, loadedScript); @@ -300,7 +302,7 @@ namespace ramses::internal EXPECT_FLOAT_EQ(42.f, *loadedInput->get()); loadedInput->set(100.0f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto loadedOutput = loadedScript->getOutputs()->getChild("arrayOfStructs")->getChild(1)->getChild("nested_struct")->getChild("nested_array")->getChild(0); EXPECT_FLOAT_EQ(100.0f, *loadedOutput->get()); } @@ -345,15 +347,15 @@ namespace ramses::internal )"; { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript(scriptSrc, {}, "MyScript"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "arrays.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("arrays.bin")); } { - EXPECT_TRUE(m_logicEngine.loadFromFile("arrays.bin")); - const LuaScript* loadedScript = m_logicEngine.findByName("MyScript"); + EXPECT_TRUE(recreateFromFile("arrays.bin")); + const LuaScript* loadedScript = m_logicEngine->findObject("MyScript"); auto inputs = loadedScript->getInputs(); auto outputs = loadedScript->getOutputs(); @@ -396,7 +398,7 @@ namespace ramses::internal TEST_F(ALuaScript_LifecycleWithFiles, OverwritesCurrentData_WhenLoadedASecondTimeFromTheSameFile) { { - LogicEngine tempLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempLogicEngine = *m_logicEngine; auto script = tempLogicEngine.createLuaScript( R"( function interface(IN,OUT) @@ -408,15 +410,15 @@ namespace ramses::internal ASSERT_NE(nullptr, script); script->getInputs()->getChild("data")->set(42); - EXPECT_TRUE(SaveToFileWithoutValidation(tempLogicEngine, "script.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("script.bin")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("script.bin")); - auto loadedScript = *m_logicEngine.getCollection().begin(); + EXPECT_TRUE(recreateFromFile("script.bin")); + auto loadedScript = *m_logicEngine->getCollection().begin(); loadedScript->getInputs()->getChild("data")->set(5); - EXPECT_TRUE(m_logicEngine.loadFromFile("script.bin")); - loadedScript = *m_logicEngine.getCollection().begin(); + EXPECT_TRUE(recreateFromFile("script.bin")); + loadedScript = *m_logicEngine->getCollection().begin(); EXPECT_EQ(42, *loadedScript->getInputs()->getChild("data")->get()); } } diff --git a/client/logic/unittests/api/LuaScriptTest_Modules.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Modules.cpp similarity index 78% rename from client/logic/unittests/api/LuaScriptTest_Modules.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Modules.cpp index 9b64d4ff9..bb1c80a0f 100644 --- a/client/logic/unittests/api/LuaScriptTest_Modules.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Modules.cpp @@ -8,20 +8,20 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "WithTempDirectory.h" +#include "LogicEngineTest_Base.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" -#include "impl/LuaScriptImpl.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/LuaScriptImpl.h" #include using namespace testing; namespace ramses::internal { - class ALuaScriptWithModule : public ::testing::Test + class ALuaScriptWithModule : public ALogicEngine { protected: const std::string_view m_moduleSourceCode = R"( @@ -65,7 +65,7 @@ namespace ramses::internal LuaConfig config; for (const auto& [alias, moduleSrc] : dependencies) { - LuaModule* mod = m_logicEngine.createLuaModule(moduleSrc); + LuaModule* mod = m_logicEngine->createLuaModule(moduleSrc); config.addDependency(alias, *mod); } @@ -78,17 +78,15 @@ namespace ramses::internal config.addStandardModuleDependency(EStandardModule::Math); return config; } - - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; }; TEST_F(ALuaScriptWithModule, CanBeCreated) { LuaConfig config; - LuaModule* mod = m_logicEngine.createLuaModule(m_moduleSourceCode); + LuaModule* mod = m_logicEngine->createLuaModule(m_moduleSourceCode); config.addDependency("mymath", *mod); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) @@ -102,16 +100,16 @@ namespace ramses::internal end )", config); ASSERT_NE(nullptr, script); - EXPECT_THAT(script->m_script.getModules(), ElementsAre(Pair("mymath", mod))); + EXPECT_THAT(script->impl().getModules(), ElementsAre(Pair("mymath", mod))); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(3, *script->getOutputs()->getChild("v")->get()); EXPECT_FLOAT_EQ(3.1415f, *script->getOutputs()->getChild("pi")->get()); } TEST_F(ALuaScriptWithModule, UsesModuleUnderDifferentName) { - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymodule") function interface(IN,OUT) @@ -126,14 +124,14 @@ namespace ramses::internal )", createDeps({ { "mymodule", m_moduleSourceCode } })); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(3, *script->getOutputs()->getChild("v")->get()); EXPECT_FLOAT_EQ(3.1415f, *script->getOutputs()->getChild("pi")->get()); } TEST_F(ALuaScriptWithModule, MultipleModules) { - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymath", "mymath2") function interface(IN,OUT) @@ -145,20 +143,20 @@ namespace ramses::internal end )", createDeps({ { "mymath", m_moduleSourceCode }, { "mymath2", m_moduleSourceCode2 } })); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(13, *script->getOutputs()->getChild("v")->get()); } TEST_F(ALuaScriptWithModule, UsesSameModuleUnderMultipleNames) { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymathmodule"); + const auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymathmodule"); ASSERT_NE(nullptr, module); LuaConfig config; config.addDependency("mymath", *module); config.addDependency("mymath2", *module); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymath", "mymath2") function interface(IN,OUT) @@ -171,19 +169,19 @@ namespace ramses::internal )", config); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(33, *script->getOutputs()->getChild("v")->get()); } TEST_F(ALuaScriptWithModule, TwoScriptsUseSameModule) { - const auto module = m_logicEngine.createLuaModule(m_moduleSourceCode, {}, "mymathmodule"); + const auto module = m_logicEngine->createLuaModule(m_moduleSourceCode, {}, "mymathmodule"); ASSERT_NE(nullptr, module); LuaConfig config1; config1.addDependency("mymath", *module); - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) @@ -199,7 +197,7 @@ namespace ramses::internal LuaConfig config2; config2.addDependency("mymathother", *module); - const auto script2 = m_logicEngine.createLuaScript(R"( + const auto script2 = m_logicEngine->createLuaScript(R"( modules("mymathother") function interface(IN,OUT) @@ -212,7 +210,7 @@ namespace ramses::internal )", config2); ASSERT_NE(nullptr, script1); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(3, *script1->getOutputs()->getChild("v")->get()); EXPECT_EQ(30, *script2->getOutputs()->getChild("v")->get()); } @@ -224,11 +222,10 @@ namespace ramses::internal mymath.PI = peterPan -- does not exist return mymath )"; - LuaModule* luaModule = m_logicEngine.createLuaModule(moduleSrc, {}, "mod"); + LuaModule* luaModule = m_logicEngine->createLuaModule(moduleSrc, {}, "mod"); EXPECT_EQ(nullptr, luaModule); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Trying to read global variable 'peterPan' in module! This can cause undefined behavior and is forbidden!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Trying to read global variable 'peterPan' in module! This can cause undefined behavior and is forbidden!")); } TEST_F(ALuaScriptWithModule, ErrorIfModuleWritesGlobalVariables) @@ -238,11 +235,10 @@ namespace ramses::internal someGlobalVar = 15 return mymath )"; - LuaModule* luaModule = m_logicEngine.createLuaModule(moduleSrc, {}, "mod"); + LuaModule* luaModule = m_logicEngine->createLuaModule(moduleSrc, {}, "mod"); EXPECT_EQ(nullptr, luaModule); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Declaring global variables is forbidden in modules! (found value of type 'number' assigned to variable 'someGlobalVar')")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Declaring global variables is forbidden in modules! (found value of type 'number' assigned to variable 'someGlobalVar')")); } TEST_F(ALuaScriptWithModule, ErrorIfModuleDoesNotReturnTable) @@ -257,17 +253,16 @@ namespace ramses::internal for (const auto& moduleSrc : errorCases) { - LuaModule* luaModule = m_logicEngine.createLuaModule(moduleSrc, {}, "mod"); + LuaModule* luaModule = m_logicEngine->createLuaModule(moduleSrc, {}, "mod"); EXPECT_EQ(nullptr, luaModule); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); - EXPECT_EQ("[mod] Error while loading module. Module script must return a table!", m_logicEngine.getErrors()[0].message); + EXPECT_EQ("[mod] Error while loading module. Module script must return a table!", getLastErrorMessage()); } } TEST_F(ALuaScriptWithModule, CanUseTableDataAndItsTypeDefinitionFromModule) { - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) OUT.color = mymath.colorType() @@ -278,7 +273,7 @@ namespace ramses::internal )", createDeps({ { "mymath", m_moduleSourceCode2 } })); ASSERT_TRUE(script); - m_logicEngine.update(); + m_logicEngine->update(); const Property* colorOutput = script->getOutputs()->getChild("color"); ASSERT_TRUE(colorOutput && colorOutput->getChild("red") && colorOutput->getChild("green") && colorOutput->getChild("blue")); EXPECT_EQ(255, *colorOutput->getChild("red")->get()); @@ -297,7 +292,7 @@ namespace ramses::internal return mod )"; - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mod") function interface(IN,OUT) @@ -336,13 +331,13 @@ namespace ramses::internal LuaConfig moduleConfig; moduleConfig.addStandardModuleDependency(EStandardModule::Base); - LuaModule* mod = m_logicEngine.createLuaModule(modSrc, moduleConfig); + LuaModule* mod = m_logicEngine->createLuaModule(modSrc, moduleConfig); LuaConfig scriptConfig; scriptConfig.addDependency("coalas", *mod); scriptConfig.addStandardModuleDependency(EStandardModule::Base); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("coalas") function interface(IN,OUT) @@ -367,7 +362,7 @@ namespace ramses::internal )", scriptConfig); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); const Property& coala1 = *script->getOutputs()->getChild("coalas")->getChild(0); const Property& coala2 = *script->getOutputs()->getChild("coalas")->getChild(1); EXPECT_EQ("bamboo", *coala1.getChild("preferredFood")->get()); @@ -378,7 +373,7 @@ namespace ramses::internal TEST_F(ALuaScriptWithModule, CanUseTableDataAndItsTypeDefinitionFromModule_WithArray) { - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) IN.struct = mymath.structWithArray() @@ -391,7 +386,7 @@ namespace ramses::internal )", createDeps({ { "mymath", m_moduleSourceCode2 } })); ASSERT_TRUE(script); - m_logicEngine.update(); + m_logicEngine->update(); const Property* input = script->getInputs()->getChild("struct"); ASSERT_TRUE(input); @@ -416,7 +411,7 @@ namespace ramses::internal return mod )"; - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mod") function interface(IN,OUT) OUT.table1size = Type:Int32() @@ -431,7 +426,7 @@ namespace ramses::internal )", createDeps({ { "mod", modSrc } })); ASSERT_TRUE(script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(0, *script->getOutputs()->getChild("table1size")->get()); EXPECT_EQ(4, *script->getOutputs()->getChild("table2size")->get()); EXPECT_EQ(1, *script->getOutputs()->getChild("table3size")->get()); @@ -449,7 +444,7 @@ namespace ramses::internal auto dependencies = createDeps({ { "mod", modSrc } }); dependencies.addStandardModuleDependency(EStandardModule::Base); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mod") function interface(IN,OUT) OUT.vec4isize = Type:Int32() @@ -471,7 +466,7 @@ namespace ramses::internal )", dependencies); ASSERT_TRUE(script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(4, *script->getOutputs()->getChild("vec4isize")->get()); EXPECT_EQ(2, *script->getOutputs()->getChild("vec2fsize")->get()); EXPECT_EQ(*script->getOutputs()->getChild("vec4i")->get(), vec4i(4, 5, 6, 7)); @@ -488,7 +483,7 @@ namespace ramses::internal return mod )"; - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mod") function interface(IN,OUT) OUT.size = Type:Int32() @@ -499,7 +494,7 @@ namespace ramses::internal )", createDeps({ { "mod", modSrc } })); ASSERT_TRUE(script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(2, *script->getOutputs()->getChild("size")->get()); } TEST_F(ALuaScriptWithModule, ReportsErrorWhenCustomLengthFunctionCalledOnInvalidType) @@ -510,7 +505,7 @@ namespace ramses::internal return mod )"; - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mod") function interface(IN,OUT) OUT.size = Type:Int32() @@ -521,8 +516,8 @@ namespace ramses::internal )", createDeps({ { "mod", modSrc } })); ASSERT_TRUE(script); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("lua: error: rl_len() called on an unsupported type 'number'")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("lua: error: rl_len() called on an unsupported type 'number'")); } TEST_F(ALuaScriptWithModule, UsesModuleThatDependsOnAnotherModule) @@ -536,12 +531,12 @@ namespace ramses::internal return wrapped )"; - const auto wrapped = m_logicEngine.createLuaModule(wrappedModuleSrc, createDeps({ { "mymath", m_moduleSourceCode } })); + const auto wrapped = m_logicEngine->createLuaModule(wrappedModuleSrc, createDeps({ { "mymath", m_moduleSourceCode } })); LuaConfig config; config.addDependency("wrapped", *wrapped); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("wrapped") function interface(IN,OUT) OUT.result = Type:Int32() @@ -551,7 +546,7 @@ namespace ramses::internal end )", config); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const Property* result = script->getOutputs()->getChild("result"); ASSERT_TRUE(result); EXPECT_EQ(35, *result->get()); @@ -569,12 +564,12 @@ namespace ramses::internal return wrapped )"; - const auto wrapped = m_logicEngine.createLuaModule(wrappedModuleSrc, createDeps({ {"mymath", m_moduleSourceCode} })); + const auto wrapped = m_logicEngine->createLuaModule(wrappedModuleSrc, createDeps({ {"mymath", m_moduleSourceCode} })); LuaConfig config; config.addDependency("wrapped", *wrapped); - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( modules("wrapped") function interface(IN,OUT) OUT.add = Type:Int32() @@ -586,8 +581,8 @@ namespace ramses::internal end )", config); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Unexpected global access to key 'mymath' in run()! Only 'GLOBAL' is allowed as a key")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global access to key 'mymath' in run()! Only 'GLOBAL' is allowed as a key")); } TEST_F(ALuaScriptWithModule, ReloadsModuleUsingTheSameNameCausesItToBeRecompiled) @@ -614,35 +609,35 @@ namespace ramses::internal end )"; - auto module = m_logicEngine.createLuaModule(moduleSource, {}, "module"); + auto module = m_logicEngine->createLuaModule(moduleSource, {}, "module"); LuaConfig config; config.addDependency("module", *module); - auto script = m_logicEngine.createLuaScript(scriptSrc, config); + auto script = m_logicEngine->createLuaScript(scriptSrc, config); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); const Property* colorOutput = script->getOutputs()->getChild("pi"); EXPECT_FLOAT_EQ(3.1415f, *colorOutput->get()); - ASSERT_TRUE(m_logicEngine.destroy(*script)); - ASSERT_TRUE(m_logicEngine.destroy(*module)); - module = m_logicEngine.createLuaModule(moduleSource_Modified, {}, "module"); + ASSERT_TRUE(m_logicEngine->destroy(*script)); + ASSERT_TRUE(m_logicEngine->destroy(*module)); + module = m_logicEngine->createLuaModule(moduleSource_Modified, {}, "module"); config = {}; config.addDependency("module", *module); - script = m_logicEngine.createLuaScript(scriptSrc, config); + script = m_logicEngine->createLuaScript(scriptSrc, config); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); colorOutput = script->getOutputs()->getChild("pi"); EXPECT_FLOAT_EQ(4.0f, *colorOutput->get()); } TEST_F(ALuaScriptWithModule, CanBeSerialized) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine logic{ m_logicEngine.getFeatureLevel() }; + LogicEngine& logic = *m_logicEngine; // 2 scripts, one module used by first script, other module used by both scripts const auto module1 = logic.createLuaModule(m_moduleSourceCode, {}, "mymodule1"); const auto module2 = logic.createLuaModule(m_moduleSourceCode2, {}, "mymodule2"); @@ -678,21 +673,21 @@ namespace ramses::internal SaveFileConfig configNoValidation; configNoValidation.setValidationEnabled(false); - EXPECT_TRUE(logic.saveToFile("scriptmodules.tmp", configNoValidation)); + EXPECT_TRUE(saveToFileWithoutValidation("scriptmodules.tmp")); } - EXPECT_TRUE(m_logicEngine.loadFromFile("scriptmodules.tmp")); - m_logicEngine.update(); + ASSERT_TRUE(recreateFromFile("scriptmodules.tmp")); + m_logicEngine->update(); - const auto module1 = m_logicEngine.findByName("mymodule1"); - const auto module2 = m_logicEngine.findByName("mymodule2"); - const auto script1 = m_logicEngine.findByName("script1"); - const auto script2 = m_logicEngine.findByName("script2"); + const auto module1 = m_logicEngine->findObject("mymodule1"); + const auto module2 = m_logicEngine->findObject("mymodule2"); + const auto script1 = m_logicEngine->findObject("script1"); + const auto script2 = m_logicEngine->findObject("script2"); ASSERT_TRUE(module1 && module2 && script1 && script2); - EXPECT_THAT(script1->m_script.getModules(), UnorderedElementsAre(Pair("mymath", module1), Pair("mymathother", module2))); - EXPECT_THAT(script2->m_script.getModules(), UnorderedElementsAre(Pair("mymath", module2))); + EXPECT_THAT(script1->impl().getModules(), UnorderedElementsAre(Pair("mymath", module1), Pair("mymathother", module2))); + EXPECT_THAT(script2->impl().getModules(), UnorderedElementsAre(Pair("mymath", module2))); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(33, *script1->getOutputs()->getChild("v")->get()); const Property* colorOutput = script1->getOutputs()->getChild("color"); ASSERT_TRUE(colorOutput && colorOutput->getChild("red") && colorOutput->getChild("green") && colorOutput->getChild("blue")); @@ -720,7 +715,7 @@ namespace ramses::internal return mytypes )"; - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mytypes") function interface(IN,OUT) IN.struct = mytypes.mystruct() @@ -751,7 +746,7 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::String, addressStr->getType()); EXPECT_EQ(EPropertyType::Int32, addressNr->getType()); } - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScriptWithModule, UsesStructPropertyInInterfaceDefinedInModule_UseInArray) @@ -771,7 +766,7 @@ namespace ramses::internal return mytypes )"; - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mytypes") function interface(IN,OUT) IN.array_of_structs = Type:Array(2, mytypes.mystruct()) @@ -810,12 +805,12 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::Int32, addressNr->getType()); } } - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScriptWithModule, SerializesAndDeserializesScriptWithStructPropertyInInterfaceDefinedInModule) { - WithTempDirectory tempDirectory; + withTempDirectory(); { constexpr std::string_view moduleDefiningInterfaceType = R"( local mytypes = {} @@ -843,7 +838,7 @@ namespace ramses::internal OUT.array_of_structs[2].address.number = 42 end)"; - LogicEngine otherLogicEngine{ m_logicEngine.getFeatureLevel() }; + LogicEngine& otherLogicEngine = *m_logicEngine; LuaModule* module = otherLogicEngine.createLuaModule(moduleDefiningInterfaceType); ASSERT_NE(nullptr, module); @@ -857,11 +852,11 @@ namespace ramses::internal SaveFileConfig configNoValidation; configNoValidation.setValidationEnabled(false); - ASSERT_TRUE(otherLogicEngine.saveToFile("moduleWithInterface.bin", configNoValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("moduleWithInterface.bin")); } - m_logicEngine.loadFromFile("moduleWithInterface.bin"); - const LuaScript* script = m_logicEngine.findByName("script"); + ASSERT_TRUE(recreateFromFile("moduleWithInterface.bin")); + const LuaScript* script = m_logicEngine->findObject("script"); for (auto rootProp : std::initializer_list{ script->getInputs(), script->getOutputs() }) { @@ -893,13 +888,13 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::Int32, addressNr->getType()); } } - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(42, *script->getOutputs()->getChild(0u)->getChild(1u)->getChild("address")->getChild("number")->get()); } TEST_F(ALuaScriptWithModule, ScriptOverwritingBaseLibraryWontAffectOtherScriptUsingIt) { - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.v = Type:Float() OUT.v = Type:Int32() @@ -911,7 +906,7 @@ namespace ramses::internal )", WithStdMath()); ASSERT_NE(nullptr, script1); - const auto script2 = m_logicEngine.createLuaScript(R"( + const auto script2 = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.v = Type:Float() OUT.v = Type:Int32() @@ -925,19 +920,19 @@ namespace ramses::internal // first update runs fine script1->getInputs()->getChild("v")->set(1.2f); script2->getInputs()->getChild("v")->set(1.3f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(1, *script1->getOutputs()->getChild("v")->get()); EXPECT_EQ(2, *script2->getOutputs()->getChild("v")->get()); // force update of script2 again, after math.floor was set nil in script1 // script2 is NOT affected script2->getInputs()->getChild("v")->set(2.3f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(3, *script2->getOutputs()->getChild("v")->get()); // script1 broke itself by setting its dependency to nil and fails to update script1->getInputs()->getChild("v")->set(2.2f); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); } TEST_F(ALuaScriptWithModule, ScriptOverwritingBaseLibraryViaModuleWontAffectOtherScriptUsingIt) @@ -952,11 +947,11 @@ namespace ramses::internal return mymath )"; - const auto maliciousModule = m_logicEngine.createLuaModule(maliciousModuleSrc, WithStdMath()); + const auto maliciousModule = m_logicEngine->createLuaModule(maliciousModuleSrc, WithStdMath()); LuaConfig withMaliciousModule; withMaliciousModule.addDependency("mymath", *maliciousModule); - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) IN.v = Type:Float() @@ -968,7 +963,7 @@ namespace ramses::internal )", withMaliciousModule); ASSERT_NE(nullptr, script1); - const auto script2 = m_logicEngine.createLuaScript(R"( + const auto script2 = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.v = Type:Float() OUT.v = Type:Int32() @@ -982,19 +977,19 @@ namespace ramses::internal // first update runs fine script1->getInputs()->getChild("v")->set(1.2f); script2->getInputs()->getChild("v")->set(1.3f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(1, *script1->getOutputs()->getChild("v")->get()); EXPECT_EQ(2, *script2->getOutputs()->getChild("v")->get()); // force update of script2 again, after math.floor was set nil in script1 via module // script2 is NOT affected script2->getInputs()->getChild("v")->set(2.3f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(3, *script2->getOutputs()->getChild("v")->get()); // module broke itself by setting its math dependency to nil and script1 using it fails to update script1->getInputs()->getChild("v")->set(2.2f); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); } class ALuaScriptDependencyMatch : public ALuaScriptWithModule @@ -1010,9 +1005,8 @@ namespace ramses::internal function run(IN,OUT) end )"; - EXPECT_EQ(nullptr, m_logicEngine.createLuaScript(src, createDeps({ {"dep2", m_moduleSourceCode} }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep2")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaScript(src, createDeps({ {"dep2", m_moduleSourceCode} }))); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep2")); } TEST_F(ALuaScriptDependencyMatch, FailsToBeCreatedIfDeclaredDependencyDoesNotMatchProvidedDependency_ProvidedButNotDeclared) @@ -1024,9 +1018,8 @@ namespace ramses::internal function run(IN,OUT) end )"; - EXPECT_EQ(nullptr, m_logicEngine.createLuaScript(src, createDeps({ {"dep1", m_moduleSourceCode}, {"dep2", m_moduleSourceCode}, {"dep3", m_moduleSourceCode} }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep1, dep2, dep3")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaScript(src, createDeps({ {"dep1", m_moduleSourceCode}, {"dep2", m_moduleSourceCode}, {"dep3", m_moduleSourceCode} }))); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Module dependencies declared in source code: dep1, dep2\n Module dependencies provided on create API: dep1, dep2, dep3")); } TEST_F(ALuaScriptDependencyMatch, FailsToBeCreatedIfDeclaredDependencyDoesNotMatchProvidedDependency_ExractionError) @@ -1038,9 +1031,8 @@ namespace ramses::internal function run(IN,OUT) end )"; - EXPECT_EQ(nullptr, m_logicEngine.createLuaScript(src, createDeps({ {"dep1", m_moduleSourceCode} }))); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Error while extracting module dependencies: 'dep1' appears more than once in dependency list")); + EXPECT_EQ(nullptr, m_logicEngine->createLuaScript(src, createDeps({ {"dep1", m_moduleSourceCode} }))); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error while extracting module dependencies: 'dep1' appears more than once in dependency list")); } // Tests specifically modules isolation @@ -1061,13 +1053,13 @@ namespace ramses::internal end return mymath )"; - const auto mymathModule = m_logicEngine.createLuaModule(mymathModuleSrc, WithStdMath(), "mymath"); + const auto mymathModule = m_logicEngine->createLuaModule(mymathModuleSrc, WithStdMath(), "mymath"); ASSERT_NE(nullptr, mymathModule); LuaConfig config; config.addDependency("mymath", *mymathModule); - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) end @@ -1077,9 +1069,8 @@ namespace ramses::internal )", config); ASSERT_NE(nullptr, script1); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, FailsToCompileScriptOverwritingModuleFunction_InInterfaceFunction) @@ -1094,13 +1085,13 @@ namespace ramses::internal end return mymath )"; - const auto mymathModule = m_logicEngine.createLuaModule(mymathModuleSrc, WithStdMath()); + const auto mymathModule = m_logicEngine->createLuaModule(mymathModuleSrc, WithStdMath()); ASSERT_NE(nullptr, mymathModule); LuaConfig config; config.addDependency("mymath", *mymathModule); - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) mymath.floor1 = mymath.floor2 @@ -1109,8 +1100,7 @@ namespace ramses::internal end )", config); EXPECT_EQ(nullptr, script1); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, FailsToRunScriptOverwritingModuleData_InRunFunction) @@ -1120,13 +1110,13 @@ namespace ramses::internal mymath.data = 1 return mymath )"; - const auto mymathModule = m_logicEngine.createLuaModule(mymathModuleSrc); + const auto mymathModule = m_logicEngine->createLuaModule(mymathModuleSrc); ASSERT_NE(nullptr, mymathModule); LuaConfig config; config.addDependency("mymath", *mymathModule); - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) end @@ -1136,9 +1126,8 @@ namespace ramses::internal )", config); ASSERT_NE(nullptr, script1); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, FailsToCompileScriptOverwritingModuleData_InInterfaceFunction) @@ -1148,13 +1137,13 @@ namespace ramses::internal mymath.data = 1 return mymath )"; - const auto mymathModule = m_logicEngine.createLuaModule(mymathModuleSrc); + const auto mymathModule = m_logicEngine->createLuaModule(mymathModuleSrc); ASSERT_NE(nullptr, mymathModule); LuaConfig config; config.addDependency("mymath", *mymathModule); - const auto script1 = m_logicEngine.createLuaScript(R"( + const auto script1 = m_logicEngine->createLuaScript(R"( modules("mymath") function interface(IN,OUT) mymath.data = 42 @@ -1163,8 +1152,7 @@ namespace ramses::internal end )", config); EXPECT_EQ(nullptr, script1); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, ModuleCannotModifyItsDataWhenPassedFromScript) @@ -1192,11 +1180,10 @@ namespace ramses::internal end )"; - m_logicEngine.createLuaScript(scriptSrc, createDeps({ { "mappedMod", moduleSrc } })); + m_logicEngine->createLuaScript(scriptSrc, createDeps({ { "mappedMod", moduleSrc } })); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, FailsToRunScriptOverwritingModuleData_WhenDataNested) @@ -1225,11 +1212,10 @@ namespace ramses::internal end )"; - m_logicEngine.createLuaScript(scriptSrc, createDeps({ { "mappedMod", moduleSrc } })); + m_logicEngine->createLuaScript(scriptSrc, createDeps({ { "mappedMod", moduleSrc } })); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, FailsToRunScriptUsingModuleOverwritingNestedModuleData_InRunFunction) @@ -1239,7 +1225,7 @@ namespace ramses::internal mymath.data = 1 return mymath )"; - const auto mymathModule1 = m_logicEngine.createLuaModule(mymathModuleSrc1); + const auto mymathModule1 = m_logicEngine->createLuaModule(mymathModuleSrc1); ASSERT_NE(nullptr, mymathModule1); const std::string_view mymathModuleSrc2 = R"( @@ -1252,12 +1238,12 @@ namespace ramses::internal )"; LuaConfig configMod; configMod.addDependency("mymath", *mymathModule1); - const auto mymathModule2 = m_logicEngine.createLuaModule(mymathModuleSrc2, configMod); + const auto mymathModule2 = m_logicEngine->createLuaModule(mymathModuleSrc2, configMod); ASSERT_NE(nullptr, mymathModule2); LuaConfig config; config.addDependency("mymathWrap", *mymathModule2); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymathWrap") function interface(IN,OUT) end @@ -1267,9 +1253,8 @@ namespace ramses::internal )", config); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } TEST_F(ALuaScriptWithModule_Isolation, FailsToRunScriptUsingModuleOverwritingNestedModuleData_InInterfaceFunction) @@ -1279,7 +1264,7 @@ namespace ramses::internal mymath.data = 1 return mymath )"; - const auto mymathModule1 = m_logicEngine.createLuaModule(mymathModuleSrc1); + const auto mymathModule1 = m_logicEngine->createLuaModule(mymathModuleSrc1); ASSERT_NE(nullptr, mymathModule1); const std::string_view mymathModuleSrc2 = R"( @@ -1292,12 +1277,12 @@ namespace ramses::internal )"; LuaConfig configMod; configMod.addDependency("mymath", *mymathModule1); - const auto mymathModule2 = m_logicEngine.createLuaModule(mymathModuleSrc2, configMod); + const auto mymathModule2 = m_logicEngine->createLuaModule(mymathModuleSrc2, configMod); ASSERT_NE(nullptr, mymathModule2); LuaConfig config; config.addDependency("mymathWrap", *mymathModule2); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("mymathWrap") function interface(IN,OUT) mymathWrap.modify() @@ -1306,8 +1291,7 @@ namespace ramses::internal end )", config); EXPECT_EQ(nullptr, script); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Modifying module data is not allowed!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Modifying module data is not allowed!")); } @@ -1320,12 +1304,12 @@ namespace ramses::internal end return m )"; - const auto luaModule = m_logicEngine.createLuaModule(modSrc); + const auto luaModule = m_logicEngine->createLuaModule(modSrc); ASSERT_NE(nullptr, luaModule); LuaConfig config; config.addDependency("m", *luaModule); - const auto script = m_logicEngine.createLuaScript(R"( + const auto script = m_logicEngine->createLuaScript(R"( modules("m") function interface(IN,OUT) m.killTypes() @@ -1334,7 +1318,6 @@ namespace ramses::internal end )", config); EXPECT_EQ(nullptr, script); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors().front().message, ::testing::HasSubstr("Special global 'Type' symbol should not be overwritten in modules!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Special global 'Type' symbol should not be overwritten in modules!")); } } diff --git a/client/logic/unittests/api/LuaScriptTest_Runtime.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Runtime.cpp similarity index 82% rename from client/logic/unittests/api/LuaScriptTest_Runtime.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Runtime.cpp index bd2add144..ecd86d745 100644 --- a/client/logic/unittests/api/LuaScriptTest_Runtime.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Runtime.cpp @@ -8,35 +8,34 @@ #include "LuaScriptTest_Base.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" #include "fmt/format.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" - -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/PerspectiveCamera.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" + +#include "ramses/framework/RamsesFramework.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Effect.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/Scene.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/Node.h" +#include "ramses/client/PerspectiveCamera.h" #include -namespace ramses +namespace ramses::internal { class ALuaScript_Runtime : public ALuaScript { - protected: }; TEST_F(ALuaScript_Runtime, ReportsErrorWhenAssigningVectorComponentsIndividually) { - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.vec3f = Type:Vec3f() end @@ -46,15 +45,14 @@ namespace ramses end )"); - m_logicEngine.update(); + m_logicEngine->update(); - EXPECT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Error while writing to 'vec3f'. Can't assign individual components of vector types, must assign the whole vector")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error while writing to 'vec3f'. Can't assign individual components of vector types, must assign the whole vector")); } TEST_F(ALuaScript_Runtime, ProducesErrorIfUndefinedInputIsUsedInRun) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -63,14 +61,13 @@ namespace ramses )"); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Tried to access undefined struct property 'undefined'")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Tried to access undefined struct property 'undefined'")); } TEST_F(ALuaScript_Runtime, ProducesErrorIfUndefinedOutputIsUsedInRun) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -79,14 +76,13 @@ namespace ramses )"); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Tried to access undefined struct property 'undefined'")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Tried to access undefined struct property 'undefined'")); } TEST_F(ALuaScript_Runtime, ReportsSourceNodeOnRuntimeError) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -95,15 +91,13 @@ namespace ramses )", WithStdModules({ EStandardModule::Base })); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("this causes an error")); - EXPECT_EQ(script, m_logicEngine.getErrors()[0].object); + EXPECT_FALSE(m_logicEngine->update()); + expectErrorSubstring("this causes an error", script); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenTryingToWriteInputValues) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.value = Type:Float() end @@ -113,10 +107,8 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("lua: error: Error while writing to 'value'. Writing input values is not allowed, only outputs!")); - EXPECT_EQ(script, m_logicEngine.getErrors()[0].object); + EXPECT_FALSE(m_logicEngine->update()); + expectErrorSubstring("lua: error: Error while writing to 'value'. Writing input values is not allowed, only outputs!", script); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenTryingToAccessPropertiesWithNonStringIndexAtRunTime) @@ -132,7 +124,7 @@ namespace ramses for (const auto& singleCase : allErrorCases) { - auto script = m_logicEngine.createLuaScript( + auto script = m_logicEngine->createLuaScript( "function interface(inputs,outputs)\n" "end\n" "function run(inputs,outputs)\n" + @@ -141,17 +133,16 @@ namespace ramses "end\n"); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(singleCase.expectedErrorMessage)); - m_logicEngine.destroy(*script); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(singleCase.expectedErrorMessage)); + m_logicEngine->destroy(*script); } } TEST_F(ALuaScript_Runtime, SetsValueOfTopLevelInputSuccessfully_WhenTemplateMatchesDeclaredInputType) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithInputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithInputs); auto inputs = script->getInputs(); auto speedInt32 = inputs->getChild("speed"); @@ -196,7 +187,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProvidesCalculatedValueAfterExecution) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.a = Type:Int32() @@ -219,14 +210,14 @@ namespace ramses inputA->set(3); inputB->set(4); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(7, *result->get()); } TEST_F(ALuaScript_Runtime, ReadsDataFromVec234Inputs) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.vec2f = Type:Vec2f() IN.vec3f = Type:Vec3f() @@ -259,7 +250,7 @@ namespace ramses inputs->getChild("vec3i")->set({ 3, 4, 5 }); inputs->getChild("vec4i")->set({ 6, 7, 8, 9 }); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_FLOAT_EQ(21.9f, *outputs->getChild("sumOfAllFloats")->get()); EXPECT_EQ(45, *outputs->getChild("sumOfAllInts")->get()); @@ -267,7 +258,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, WritesValuesToVectorTypeOutputs) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.vec2f = Type:Vec2f() OUT.vec3f = Type:Vec3f() @@ -297,7 +288,7 @@ namespace ramses end )"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto outputs = script->getOutputs(); @@ -345,19 +336,19 @@ namespace ramses scriptSource += aCase; scriptSource += "\nend\n"; - auto* script = m_logicEngine.createLuaScript(scriptSource); + auto* script = m_logicEngine->createLuaScript(scriptSource); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - EXPECT_TRUE(m_logicEngine.destroy(*script)); + expectNoError(); + EXPECT_TRUE(m_logicEngine->destroy(*script)); } } TEST_F(ALuaScript_Runtime, PermitsAssigningOfVector_FromTable_WithKeyValuePairs) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.vec2f = Type:Vec2f() OUT.vec3i = Type:Vec3i() @@ -370,7 +361,7 @@ namespace ramses )"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto outputs = script->getOutputs(); @@ -381,7 +372,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, UsesNestedInputsToProduceResult) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.data = { a = Type:Int32(), @@ -404,8 +395,8 @@ namespace ramses inputA->set(3); inputB->set(4); - m_logicEngine.update(); - m_logicEngine.update(); + m_logicEngine->update(); + m_logicEngine->update(); EXPECT_EQ(7, *result->get()); } @@ -413,7 +404,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, StoresDataToNestedOutputs_AsWholeStruct) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.data = Type:Int32() OUT.struct = { @@ -438,7 +429,7 @@ namespace ramses input->set(5); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(10, *field1->get()); EXPECT_EQ(25, *field2->get()); @@ -446,7 +437,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, StoresDataToNestedOutputs_Individually) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.data = Type:Int32() OUT.data = { @@ -469,7 +460,7 @@ namespace ramses input->set(5); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(10, *field1->get()); EXPECT_EQ(25, *field2->get()); @@ -477,7 +468,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningNestedProperties_Underspecified) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.data = { field1 = Type:Int32(), @@ -495,9 +486,8 @@ namespace ramses auto field1 = outputs->getChild("data")->getChild("field1"); auto field2 = outputs->getChild("data")->getChild("field2"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("lua: error: Error while assigning struct 'data', expected a value for property 'field2' but found none!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("lua: error: Error while assigning struct 'data', expected a value for property 'field2' but found none!")); EXPECT_EQ(5, *field1->get()); EXPECT_EQ(0, *field2->get()); @@ -505,7 +495,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningNestedProperties_Overspecified) { - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.data = { field1 = Type:Int32(), @@ -521,14 +511,13 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Unexpected property 'not_specified' while assigning values to struct 'data'!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected property 'not_specified' while assigning values to struct 'data'!")); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningNestedProperties_WhenFieldHasWrongType) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.data = { field1 = Type:Int32(), @@ -549,9 +538,8 @@ namespace ramses auto field1 = outputs->getChild("data")->getChild("field1"); auto field2 = outputs->getChild("data")->getChild("field2"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Assigning string to 'Int32' output 'field2'!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Assigning string to 'Int32' output 'field2'!")); EXPECT_EQ(0, *field1->get()); EXPECT_EQ(0, *field2->get()); @@ -559,7 +547,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningNestedProperties_WhenNestedSubStructDoesNotMatch_AndInterruptsAssignment) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.data = { a_fieldBefore = Type:Int32(), @@ -585,9 +573,8 @@ namespace ramses auto nestedfield = outputs->getChild("data")->getChild("b_nested")->getChild("field"); auto fieldAfter = outputs->getChild("data")->getChild("c_fieldAfter"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Unexpected property 'wrong_field' while assigning values to struct 'b_nested'")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected property 'wrong_field' while assigning values to struct 'b_nested'")); // Assigned, because it was ordered alphabetically before the erroneous field EXPECT_EQ(4, *fieldBefore->get()); @@ -619,7 +606,7 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptWithArrays); + auto* script = m_logicEngine->createLuaScript(scriptWithArrays); auto inputs = script->getInputs(); auto in_array_int = inputs->getChild("array_int"); @@ -633,7 +620,7 @@ namespace ramses in_array_float->getChild(1)->set(0.2f); in_array_float->getChild(2)->set(0.3f); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto outputs = script->getOutputs(); auto out_array_int = outputs->getChild("array_int"); @@ -665,9 +652,9 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptWithMultiDimArrays); + auto* script = m_logicEngine->createLuaScript(scriptWithMultiDimArrays); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto array2DOut = script->getOutputs()->getChild("array2d"); @@ -696,7 +683,7 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptWithMultiDimArrays); + auto* script = m_logicEngine->createLuaScript(scriptWithMultiDimArrays); auto array3DIn = script->getInputs()->getChild("array3d"); @@ -711,7 +698,7 @@ namespace ramses } } - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto array3DOut = script->getOutputs()->getChild("array3d"); @@ -745,10 +732,10 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({EStandardModule::Base})); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({EStandardModule::Base})); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ("soldier 8-7", *script->getOutputs()->getChild("coala_army")->getChild(7)->getChild(6)->getChild("name")->get()); } @@ -780,14 +767,13 @@ namespace ramses for (const auto& invalidStatement : invalidStatements) { - auto script = m_logicEngine.createLuaScript(fmt::format(scriptTemplate, invalidStatement)); + auto script = m_logicEngine->createLuaScript(fmt::format(scriptTemplate, invalidStatement)); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Bad access to property 'array'! Error while extracting integer: expected a number, received")); - m_logicEngine.destroy(*script); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Bad access to property 'array'! Error while extracting integer: expected a number, received")); + m_logicEngine->destroy(*script); } } @@ -822,20 +808,19 @@ namespace ramses for (const auto& singleCase : allErrorCases) { - auto script = m_logicEngine.createLuaScript(fmt::format(scriptTemplate, singleCase.errorCode)); + auto script = m_logicEngine->createLuaScript(fmt::format(scriptTemplate, singleCase.errorCode)); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(singleCase.expectedErrorMessage)); - m_logicEngine.destroy(*script); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(singleCase.expectedErrorMessage)); + m_logicEngine->destroy(*script); } } TEST_F(ALuaScript_Runtime, AssignArrayValuesFromLuaTable) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.int_array = Type:Array(2, Type:Int32()) OUT.int64_array = Type:Array(2, Type:Int64()) @@ -854,7 +839,7 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto int_array = script->getOutputs()->getChild("int_array"); const auto int64_array = script->getOutputs()->getChild("int64_array"); @@ -876,7 +861,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, AssignArrayValuesFromLuaTable_WithExplicitKeys) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.int_array = Type:Array(3, Type:Int32()) end @@ -887,7 +872,7 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto int_array = script->getOutputs()->getChild("int_array"); @@ -898,7 +883,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningArrayWithFewerElementsThanRequired_UsingExplicitIndices) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.int_array = Type:Array(3, Type:Int32()) end @@ -909,15 +894,14 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Error during assignment of array property 'int_array'! Expected a value at index 3")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error during assignment of array property 'int_array'! Expected a value at index 3")); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningArrayFromLuaTableWithCorrectSizeButWrongIndices) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.int_array = Type:Array(3, Type:Int32()) end @@ -929,10 +913,9 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Error during assignment of array property 'int_array'! Expected a value at index 2")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error during assignment of array property 'int_array'! Expected a value at index 2")); } TEST_F(ALuaScript_Runtime, AssignsMultidimensionalArrays) @@ -948,10 +931,10 @@ namespace ramses end )"); - auto script = m_logicEngine.createLuaScript(scriptSrc); + auto script = m_logicEngine->createLuaScript(scriptSrc); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto outArray2d = script->getOutputs()->getChild("array2d_int"); EXPECT_EQ(1, *outArray2d->getChild(0)->getChild(0)->get()); @@ -1006,20 +989,19 @@ namespace ramses for (const auto& singleCase : allErrorCases) { - auto script = m_logicEngine.createLuaScript(fmt::format(scriptTemplate, singleCase.errorCode)); + auto script = m_logicEngine->createLuaScript(fmt::format(scriptTemplate, singleCase.errorCode)); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(singleCase.expectedErrorMessage)); - m_logicEngine.destroy(*script); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(singleCase.expectedErrorMessage)); + m_logicEngine->destroy(*script); } } TEST_F(ALuaScript_Runtime, AssignsValuesArraysInVariousLuaSyntaxStyles) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array = Type:Array(3, Type:Vec2i()) OUT.array = Type:Array(3, Type:Vec2i()) @@ -1036,7 +1018,7 @@ namespace ramses EXPECT_TRUE(script->getInputs()->getChild("array")->getChild(0)->set({ 1, 2 })); EXPECT_TRUE(script->getInputs()->getChild("array")->getChild(1)->set({ 3, 4 })); EXPECT_TRUE(script->getInputs()->getChild("array")->getChild(2)->set({ 5, 6 })); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(*script->getOutputs()->getChild("array")->getChild(0)->get(), vec2i(1, 2)); EXPECT_EQ(*script->getOutputs()->getChild("array")->getChild(1)->get(), vec2i(3, 4)); EXPECT_EQ(*script->getOutputs()->getChild("array")->getChild(2)->get(), vec2i(5, 6)); @@ -1044,7 +1026,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, AssignsValuesArraysInVariousLuaSyntaxStyles_InNestedStruct) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.struct = { array1 = Type:Array(1, Type:Vec2f()), @@ -1068,7 +1050,7 @@ namespace ramses EXPECT_TRUE(script->getInputs()->getChild("struct")->getChild("array1")->getChild(0)->set({ 0.1f, 0.2f })); EXPECT_TRUE(script->getInputs()->getChild("struct")->getChild("array2")->getChild(0)->set({ 1.1f, 1.2f, 1.3f })); EXPECT_TRUE(script->getInputs()->getChild("struct")->getChild("array2")->getChild(1)->set({ 2.1f, 2.2f, 2.3f })); - ASSERT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(m_logicEngine->update()); EXPECT_EQ(*script->getOutputs()->getChild("struct")->getChild("array1")->getChild(0)->get(), vec2f(0.1f, 0.2f)); EXPECT_EQ(*script->getOutputs()->getChild("struct")->getChild("array2")->getChild(0)->get(), vec3f(1.1f, 1.2f, 1.3f)); EXPECT_EQ(*script->getOutputs()->getChild("struct")->getChild("array2")->getChild(1)->get(), vec3f(2.1f, 2.2f, 2.3f)); @@ -1102,13 +1084,13 @@ namespace ramses for (const auto& aCase : allCases) { - auto* script = m_logicEngine.createLuaScript(fmt::format(scriptTemplate, aCase)); + auto* script = m_logicEngine->createLuaScript(fmt::format(scriptTemplate, aCase)); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - EXPECT_TRUE(m_logicEngine.destroy(*script)); + expectNoError(); + EXPECT_TRUE(m_logicEngine->destroy(*script)); } } @@ -1139,14 +1121,13 @@ namespace ramses for (const auto& aCase : allCases) { - auto* script = m_logicEngine.createLuaScript(fmt::format(scriptTemplate, aCase.errorCode)); + auto* script = m_logicEngine->createLuaScript(fmt::format(scriptTemplate, aCase.errorCode)); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(aCase.expectedErrorMessage)); - EXPECT_TRUE(m_logicEngine.destroy(*script)); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(aCase.expectedErrorMessage)); + EXPECT_TRUE(m_logicEngine->destroy(*script)); } } @@ -1178,20 +1159,19 @@ namespace ramses for (const auto& aCase : allCases) { - auto* script = m_logicEngine.createLuaScript(fmt::format(scriptTemplate, aCase.errorCode)); + auto* script = m_logicEngine->createLuaScript(fmt::format(scriptTemplate, aCase.errorCode)); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(aCase.expectedErrorMessage)); - EXPECT_TRUE(m_logicEngine.destroy(*script)); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(aCase.expectedErrorMessage)); + EXPECT_TRUE(m_logicEngine->destroy(*script)); } } TEST_F(ALuaScript_Runtime, ProducesErrorWhenImplicitlyRoundingNumbers) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.float1 = Type:Float() IN.float2 = Type:Float() @@ -1212,17 +1192,16 @@ namespace ramses float1Input->set(1.0f); float2Input->set(1.0f); - EXPECT_TRUE(m_logicEngine.update()); - ASSERT_TRUE(m_logicEngine.getErrors().empty()); + EXPECT_TRUE(m_logicEngine->update()); + expectNoError(); EXPECT_EQ(1, *intOutput->get()); EXPECT_EQ(1, *int64Output->get()); float1Input->set(2.5f); float2Input->set(1.f); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error during assignment of property 'int'! Error while extracting integer: implicit rounding (fractional part '0.5' is not negligible)")); EXPECT_EQ(1, *intOutput->get()); EXPECT_EQ(1, *int64Output->get()); @@ -1230,9 +1209,8 @@ namespace ramses float1Input->set(1.f); float2Input->set(2.5f); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Error during assignment of property 'int64'! Error while extracting integer: implicit rounding (fractional part '0.5' is not negligible)")); EXPECT_EQ(1, *intOutput->get()); EXPECT_EQ(1, *int64Output->get()); @@ -1240,7 +1218,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningNilToIntOutputs) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.int = Type:Int32() end @@ -1249,15 +1227,14 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Assigning nil to 'Int32' output 'int'!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Assigning nil to 'Int32' output 'int'!")); EXPECT_EQ(0, *script->getOutputs()->getChild("int")->get()); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningBoolToIntOutputs) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.int = Type:Int32() end @@ -1266,15 +1243,14 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Assigning bool to 'Int32' output 'int'!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Assigning bool to 'Int32' output 'int'!")); EXPECT_EQ(0, *script->getOutputs()->getChild("int")->get()); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningBoolToStringOutputs) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.str = Type:String() end @@ -1284,15 +1260,14 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Assigning bool to 'String' output 'str'!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Assigning bool to 'String' output 'str'!")); EXPECT_EQ("this is quite ok", *script->getOutputs()->getChild("str")->get()); } TEST_F(ALuaScript_Runtime, ProducesErrorWhenAssigningNumberToStringOutputs) { - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.str = Type:String() end @@ -1301,14 +1276,13 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Assigning number to 'String' output 'str'!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Assigning number to 'String' output 'str'!")); } TEST_F(ALuaScript_Runtime, SupportsMultipleLevelsOfNestedInputs_confidenceTest) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.rabbit = { color = { @@ -1339,7 +1313,7 @@ namespace ramses color->getChild("b")->set(0.75f); speed->set(20); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(45, result->get()); } @@ -1363,7 +1337,7 @@ namespace ramses for (const auto& singleCase : allCases) { - auto script = m_logicEngine.createLuaScript( + auto script = m_logicEngine->createLuaScript( "function interface(IN,OUT)\n" "end\n" "function run(IN,OUT)\n" + @@ -1372,10 +1346,9 @@ namespace ramses ); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(singleCase.expectedErrorMessage)); - m_logicEngine.destroy(*script); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(singleCase.expectedErrorMessage)); + m_logicEngine->destroy(*script); } } @@ -1391,7 +1364,7 @@ namespace ramses for (const auto& singleCase : allCases) { - auto script = m_logicEngine.createLuaScript( + auto script = m_logicEngine->createLuaScript( "function interface(IN,OUT)\n" "end\n" "function run(IN,OUT)\n" + @@ -1400,10 +1373,9 @@ namespace ramses ); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(singleCase.expectedErrorMessage)); - m_logicEngine.destroy(*script); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(singleCase.expectedErrorMessage)); + m_logicEngine->destroy(*script); } } @@ -1422,13 +1394,13 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptWithArrays); + auto* script = m_logicEngine->createLuaScript(scriptWithArrays); auto inputs = script->getInputs(); auto IN_array = inputs->getChild("array_structs"); IN_array->getChild(0)->getChild("name")->set("donald"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); auto outputs = script->getOutputs(); auto OUT_array = outputs->getChild("array_structs"); @@ -1442,7 +1414,7 @@ namespace ramses // The test makes sure we catch it and report an error accordingly TEST_F(ALuaScript_Runtime, ForbidsCallingInterfaceFunctionInsideTheRunFunction) { - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -1451,15 +1423,14 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, ::testing::HasSubstr("Unexpected global access to key 'interface' in run()!")); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global access to key 'interface' in run()!")); } TEST_F(ALuaScript_Runtime, AbortsAfterFirstRuntimeError) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.float = Type:Float() OUT.float = Type:Float() @@ -1472,13 +1443,13 @@ namespace ramses ASSERT_NE(nullptr, script); script->getInputs()->getChild("float")->set(0.1f); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); EXPECT_FLOAT_EQ(0.0f, *script->getOutputs()->getChild("float")->get()); } TEST_F(ALuaScript_Runtime, AssignOutputsFromInputsInDifferentWays_ConfidenceTest) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.assignmentType = Type:String() @@ -1612,11 +1583,11 @@ namespace ramses for (const auto& assignmentType : assignmentTypes) { EXPECT_TRUE(script->getInputs()->getChild("assignmentType")->set("nullify")); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_TRUE(script->getInputs()->getChild("assignmentType")->set(assignmentType)); - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + EXPECT_TRUE(m_logicEngine->update()); + expectNoError(); EXPECT_FLOAT_EQ(0.1f, *outputs->getChild("float")->get()); @@ -1645,7 +1616,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ForbidsOverwritingRunFunctionInsideTheRunFunction) { - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -1655,16 +1626,15 @@ namespace ramses end )"); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors().back().message, + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global variable definition 'run' in run()! " "Use the init() function to declare global data and functions, or use modules!")); } TEST_F(ALuaScript_Runtime, ProducesErrorIfInvalidOutPropertyIsAccessed) { - auto scriptWithInvalidOutParam = m_logicEngine.createLuaScript(R"( + auto scriptWithInvalidOutParam = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -1673,13 +1643,12 @@ namespace ramses )"); ASSERT_NE(nullptr, scriptWithInvalidOutParam); - m_logicEngine.update(); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); + EXPECT_FALSE(m_logicEngine->update()); } TEST_F(ALuaScript_Runtime, ProducesErrorIfInvalidNestedOutPropertyIsAccessed) { - auto scriptWithInvalidStructAccess = m_logicEngine.createLuaScript(R"( + auto scriptWithInvalidStructAccess = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -1688,13 +1657,12 @@ namespace ramses )"); ASSERT_NE(nullptr, scriptWithInvalidStructAccess); - m_logicEngine.update(); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); + EXPECT_FALSE(m_logicEngine->update()); } TEST_F(ALuaScript_Runtime, ProducesErrorIfValidestedButInvalidOutPropertyIsAccessed) { - auto scriptWithValidStructButInvalidField = m_logicEngine.createLuaScript(R"( + auto scriptWithValidStructButInvalidField = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) OUT.struct = { param = Type:Int32() @@ -1706,13 +1674,12 @@ namespace ramses )"); ASSERT_NE(nullptr, scriptWithValidStructButInvalidField); - m_logicEngine.update(); - EXPECT_FALSE(m_logicEngine.getErrors().empty()); + EXPECT_FALSE(m_logicEngine->update()); } TEST_F(ALuaScript_Runtime, CanAssignInputDirectlyToOutput) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.param_struct = { param1 = Type:Float(), @@ -1745,7 +1712,7 @@ namespace ramses param2_struct->getChild("b")->set(3); } - m_logicEngine.update(); + m_logicEngine->update(); { auto outputs = script->getOutputs(); @@ -1759,7 +1726,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, ProducesNoErrorIfOutputIsSetInFunction) { - auto script = m_logicEngine.createLuaScript(R"( + auto script = m_logicEngine->createLuaScript(R"( function init() GLOBAL.setPrimitive = function (output) output.param = 42 @@ -1784,7 +1751,7 @@ namespace ramses )"); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto outputs = script->getOutputs(); ASSERT_NE(nullptr, outputs); @@ -1800,7 +1767,7 @@ namespace ramses TEST_F(ALuaScript_Runtime, HasNoInfluenceOnBindingsIfTheyAreNotLinked) { - auto scriptSource = R"( + auto scriptSource = R"( function interface(IN,OUT) IN.inFloat = Type:Float() IN.inVec3 = Type:Vec3f() @@ -1832,9 +1799,9 @@ namespace ramses color = vec4(1.0, 0.0, 0.0, 1.0); })"; - auto script1 = m_logicEngine.createLuaScript(scriptSource); - auto script2 = m_logicEngine.createLuaScript(scriptSource); - auto script3 = m_logicEngine.createLuaScript(scriptSource); + auto script1 = m_logicEngine->createLuaScript(scriptSource); + auto script2 = m_logicEngine->createLuaScript(scriptSource); + auto script3 = m_logicEngine->createLuaScript(scriptSource); auto script1FloatInput = script1->getInputs()->getChild("inFloat"); auto script1FloatOutput = script1->getOutputs()->getChild("outFloat"); @@ -1849,22 +1816,18 @@ namespace ramses auto script3Vec3Input = script3->getInputs()->getChild("inVec3"); auto script3Vec3Output = script3->getOutputs()->getChild("outVec3"); - ramses::RamsesFramework ramsesFramework{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; - auto ramsesClient = ramsesFramework.createClient("client"); - auto ramsesScene = ramsesClient->createScene(ramses::sceneId_t(1)); - - ramses::EffectDescription ramsesEffectDesc; + EffectDescription ramsesEffectDesc; ramsesEffectDesc.setVertexShader(vertexShaderSource.data()); ramsesEffectDesc.setFragmentShader(fragmentShaderSource.data()); - auto ramsesEffect = ramsesScene->createEffect(ramsesEffectDesc); - auto ramsesAppearance = ramsesScene->createAppearance(*ramsesEffect); - ramses::PerspectiveCamera* camera = ramsesScene->createPerspectiveCamera(); + auto ramsesEffect = m_scene->createEffect(ramsesEffectDesc); + auto ramsesAppearance = m_scene->createAppearance(*ramsesEffect); + PerspectiveCamera* camera = m_scene->createPerspectiveCamera(); - auto nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - auto appearanceBinding = m_logicEngine.createRamsesAppearanceBinding(*ramsesAppearance, "AppearanceBinding"); - auto cameraBinding = m_logicEngine.createRamsesCameraBinding(*camera, "CameraBinding"); + auto nodeBinding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + auto appearanceBinding = m_logicEngine->createAppearanceBinding(*ramsesAppearance, "AppearanceBinding"); + auto cameraBinding = m_logicEngine->createCameraBinding(*camera, "CameraBinding"); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_TRUE(*nodeBinding->getInputs()->getChild("visibility")->get()); EXPECT_EQ(*nodeBinding->getInputs()->getChild("translation")->get(), vec3f(0.f, 0.f, 0.f)); @@ -1880,12 +1843,12 @@ namespace ramses EXPECT_EQ(camera->getNearPlane(), 0.1f); EXPECT_EQ(camera->getFarPlane(), 1.f); - m_logicEngine.link(*script1FloatOutput, *script2FloatInput); - m_logicEngine.link(*script2FloatOutput, *script3FloatInput); - m_logicEngine.link(*script1Vec3Output, *script2Vec3Input); - m_logicEngine.link(*script2Vec3Output, *script3Vec3Input); + m_logicEngine->link(*script1FloatOutput, *script2FloatInput); + m_logicEngine->link(*script2FloatOutput, *script3FloatInput); + m_logicEngine->link(*script1Vec3Output, *script2Vec3Input); + m_logicEngine->link(*script2Vec3Output, *script3Vec3Input); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_TRUE(*nodeBinding->getInputs()->getChild("visibility")->get()); EXPECT_EQ(*nodeBinding->getInputs()->getChild("translation")->get(), vec3f(0.f, 0.f, 0.f)); @@ -1901,30 +1864,30 @@ namespace ramses EXPECT_EQ(camera->getNearPlane(), 0.1f); EXPECT_EQ(camera->getFarPlane(), 1.f); - m_logicEngine.link(*script3Vec3Output, *nodeBinding->getInputs()->getChild("translation")); + m_logicEngine->link(*script3Vec3Output, *nodeBinding->getInputs()->getChild("translation")); script1Vec3Input->set(vec3f{1.f, 2.f, 3.f}); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(*nodeBinding->getInputs()->getChild("translation")->get(), vec3f(1.f, 2.f, 3.f)); - m_logicEngine.link(*script3FloatOutput, *appearanceBinding->getInputs()->getChild("floatUniform")); - m_logicEngine.link(*script3FloatOutput, *cameraBinding->getInputs()->getChild("frustum")->getChild("farPlane")); + m_logicEngine->link(*script3FloatOutput, *appearanceBinding->getInputs()->getChild("floatUniform")); + m_logicEngine->link(*script3FloatOutput, *cameraBinding->getInputs()->getChild("frustum")->getChild("farPlane")); script1FloatInput->set(42.f); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_FLOAT_EQ(42.f, *appearanceBinding->getInputs()->getChild("floatUniform")->get()); EXPECT_EQ(42.f, *cameraBinding->getInputs()->getChild("frustum")->getChild("farPlane")->get()); - m_logicEngine.unlink(*script3Vec3Output, *nodeBinding->getInputs()->getChild("translation")); + m_logicEngine->unlink(*script3Vec3Output, *nodeBinding->getInputs()->getChild("translation")); script1FloatInput->set(23.f); script1Vec3Input->set(vec3f{3.f, 2.f, 1.f}); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(*nodeBinding->getInputs()->getChild("translation")->get(), vec3f(1.f, 2.f, 3.f)); EXPECT_FLOAT_EQ(23.f, *appearanceBinding->getInputs()->getChild("floatUniform")->get()); @@ -1959,10 +1922,10 @@ namespace ramses OUT.language_of_debug_func = debuginfo.what end )"; - auto script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({EStandardModule::Base, EStandardModule::String, EStandardModule::Table, EStandardModule::Debug, EStandardModule::Math})); + auto script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({EStandardModule::Base, EStandardModule::String, EStandardModule::Table, EStandardModule::Debug, EStandardModule::Math})); ASSERT_NE(nullptr, script); - m_logicEngine.update(); + m_logicEngine->update(); EXPECT_EQ(42, *script->getOutputs()->getChild("floored_float")->get()); EXPECT_EQ("This is the modified text", *script->getOutputs()->getChild("string_gsub")->get()); @@ -2014,9 +1977,9 @@ namespace ramses end end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({EStandardModule::Base})); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({EStandardModule::Base})); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_RuntimeIterators, CallingCustomLengthFunctionOnNormalLuaTables_YieldsSameResultAsBuiltInSizeOperator) @@ -2036,9 +1999,9 @@ namespace ramses assert(rl_len(nonNumericTable) == #nonNumericTable) end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base })); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base })); ASSERT_NE(nullptr, script); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_RuntimeIterators, CustomRlNextFunctionWorksLikeItsBuiltInCounterpart_Structs) @@ -2081,13 +2044,13 @@ namespace ramses end end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); ASSERT_NE(nullptr, script); script->getInputs()->getChild("struct")->getChild("a")->set(11); script->getInputs()->getChild("struct")->getChild("b")->set(12); script->getInputs()->getChild("nested")->getChild("struct")->getChild("a")->set(11); script->getInputs()->getChild("nested")->getChild("struct")->getChild("b")->set(12); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_RuntimeIterators, CustomRlNextFunctionWorksLikeItsBuiltInCounterpart_Arrays) @@ -2130,13 +2093,13 @@ namespace ramses end end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); ASSERT_NE(nullptr, script); script->getInputs()->getChild("array_int")->getChild(0)->set(11); script->getInputs()->getChild("array_int")->getChild(1)->set(12); script->getInputs()->getChild("nested")->getChild("array_int")->getChild(0)->set(11); script->getInputs()->getChild("nested")->getChild("array_int")->getChild(1)->set(12); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_RuntimeIterators, Custom_IPairs_BehavesTheSameAsStandard_IPairs_Function_ForArrays) @@ -2191,13 +2154,13 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); ASSERT_NE(nullptr, script); script->getInputs()->getChild("array_int")->getChild(0)->set(11); script->getInputs()->getChild("array_int")->getChild(1)->set(12); script->getInputs()->getChild("nested")->getChild("array_int")->getChild(0)->set(11); script->getInputs()->getChild("nested")->getChild("array_int")->getChild(1)->set(12); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_RuntimeIterators, Custom_Pairs_BehavesTheSameAsStandard_Pairs_Function_ForArrays) @@ -2220,7 +2183,7 @@ namespace ramses OUT.nested = IN.nested -- compare iteration results to a static reference table - local refTable = {[1] = 11, [2] = 12} + local refTable = {11, 12} -- test multiple containers (which all have the same contents) local objectsToCheck = {IN.array_int, IN.nested.array_int, OUT.array_int, OUT.nested.array_int} @@ -2250,13 +2213,13 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); ASSERT_NE(nullptr, script); script->getInputs()->getChild("array_int")->getChild(0)->set(11); script->getInputs()->getChild("array_int")->getChild(1)->set(12); script->getInputs()->getChild("nested")->getChild("array_int")->getChild(0)->set(11); script->getInputs()->getChild("nested")->getChild("array_int")->getChild(1)->set(12); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_RuntimeIterators, Custom_Pairs_BehavesTheSameAsStandard_Pairs_Function_ForStructs) @@ -2290,13 +2253,16 @@ namespace ramses OUT.nested = IN.nested -- compare iteration results to a static reference table - local refTable = {int = 42, bool = false, nested = {int = 42, bool = false, nested = {}}} + -- (reference table is split into 2 arrays to guarantee stable sorting) + local refKeys = {"bool", "int", "nested"} + local refValues = {false, 42, {int = 42, bool = false, nested = {}}} -- test multiple containers (which all have the same contents) local objectsToCheck = {IN, IN.nested, OUT, OUT.nested} for k, container in pairs(objectsToCheck) do -- iterate manually over reference table... - local refKey,refValue = next(refTable) + local kKey, refKey = next(refKeys) + local kValue, refValue = next(refValues) -- ...and compare to rl_pairs results for key, value in rl_pairs(container) do if type(key) ~= 'string' then @@ -2311,23 +2277,24 @@ namespace ramses error("Expected value==refValue, but found " .. tostring(value) .. " != " .. tostring(refValue)) end -- progress refTable manually - refKey,refValue = next(refTable, refKey) + kKey, refKey = next(refKeys, kKey) + kValue, refValue = next(refValues, kValue) end - -- make sure there are no leftover elements in refTable + -- make sure there are no leftover elements assert(refKey == nil) assert(refValue == nil) end end )"; - auto* script = m_logicEngine.createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); + auto* script = m_logicEngine->createLuaScript(scriptSrc, WithStdModules({ EStandardModule::Base, EStandardModule::String })); ASSERT_NE(nullptr, script); script->getInputs()->getChild("int")->set(42); script->getInputs()->getChild("bool")->set(false); script->getInputs()->getChild("nested")->getChild("int")->set(42); script->getInputs()->getChild("nested")->getChild("bool")->set(false); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } class ALuaScript_Runtime_Sandboxing : public ALuaScript_Runtime @@ -2336,7 +2303,7 @@ namespace ramses TEST_F(ALuaScript_Runtime_Sandboxing, ReportsErrorWhenTryingToReadUnknownGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -2345,16 +2312,16 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); + EXPECT_FALSE(m_logicEngine->update()); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Unexpected global access to key 'someGlobalVariable' in run()! Only 'GLOBAL' is allowed as a key")); } TEST_F(ALuaScript_Runtime_Sandboxing, ReportsErrorWhenSettingGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -2363,14 +2330,14 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Unexpected global variable definition 'thisCausesError' in run()! Use the init() function to declare global data and functions, or use modules!")); } TEST_F(ALuaScript_Runtime_Sandboxing, ReportsErrorWhenTryingToOverrideGlobals) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() end @@ -2382,14 +2349,14 @@ namespace ramses end)"); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(" Trying to override the GLOBAL table in run()! You can only read data, but not overwrite the table!")); } TEST_F(ALuaScript_Runtime_Sandboxing, ReportsErrorWhenTryingToDeclareRunFunctionTwice) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -2400,7 +2367,7 @@ namespace ramses end)"); ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors().begin()->message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Function 'run' can only be declared once!")); } @@ -2408,7 +2375,7 @@ namespace ramses { for (const auto& specialFunction : std::vector{ "init", "run", "interface" }) { - LuaScript* script = m_logicEngine.createLuaScript(fmt::format(R"( + LuaScript* script = m_logicEngine->createLuaScript(fmt::format(R"( function init() end function interface(IN,OUT) @@ -2421,11 +2388,11 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr(fmt::format("Unexpected global access to key '{}' in run()! Only 'GLOBAL' is allowed as a key", specialFunction))); - ASSERT_TRUE(m_logicEngine.destroy(*script)); + ASSERT_TRUE(m_logicEngine->destroy(*script)); } } } diff --git a/client/logic/unittests/api/LuaScriptTest_Serialization.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Serialization.cpp similarity index 88% rename from client/logic/unittests/api/LuaScriptTest_Serialization.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Serialization.cpp index 4c0c03f4b..c51f6f468 100644 --- a/client/logic/unittests/api/LuaScriptTest_Serialization.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Serialization.cpp @@ -8,16 +8,17 @@ #include "gmock/gmock.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LogicEngineImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/LuaModuleImpl.h" -#include "internals/ErrorReporting.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LuaModuleImpl.h" +#include "impl/ErrorReporting.h" -#include "generated/LuaScriptGen.h" +#include "internal/logic/flatbuffers/generated/LuaScriptGen.h" #include "SerializationTestUtils.h" #include "FeatureLevelTestValues.h" +#include "RamsesTestUtils.h" namespace ramses::internal { @@ -28,8 +29,9 @@ namespace ramses::internal std::unique_ptr createTestScript(std::string_view source, std::string_view scriptName = "") { return std::make_unique( + m_deserializationMap.getScene(), *LuaCompilationUtils::CompileScriptOrImportPrecompiled(m_solState, {}, {}, std::string{ source }, scriptName, m_errorReporting, {}, {}, {}, false), - scriptName, 1u); + scriptName, sceneObjectId_t{ 1u }); } std::vector static GetByteCodeForSource(std::string_view source) @@ -56,7 +58,8 @@ namespace ramses::internal flatbuffers::FlatBufferBuilder m_flatBufferBuilder; SerializationTestUtils m_testUtils{ m_flatBufferBuilder }; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + RamsesTestSetup m_ramses; + DeserializationMap m_deserializationMap{ m_ramses.createScene()->impl() }; }; INSTANTIATE_TEST_SUITE_P( @@ -96,10 +99,10 @@ namespace ramses::internal std::unique_ptr deserializedScript = LuaScriptImpl::Deserialize(m_solState, serializedScript, m_errorReporting, m_deserializationMap); ASSERT_TRUE(deserializedScript); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(deserializedScript->getName(), "name"); - EXPECT_EQ(deserializedScript->getId(), 1u); + EXPECT_EQ(deserializedScript->getSceneObjectId().getValue(), 1u); } } @@ -148,7 +151,7 @@ namespace ramses::internal const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_TRUE(deserialized); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_P(ALuaScript_Serialization, StoresDuplicateByteCodeOnce) @@ -200,9 +203,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of LuaScript from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaScript from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenIdMissing) @@ -221,9 +223,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of LuaScript from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of LuaScript from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenBothLuaSourceCodeAndBytecodeMissing) @@ -248,8 +249,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript from serialized data: has neither Lua source code nor bytecode!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: has neither Lua source code nor bytecode!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenBothLuaSourceCodeAndBytecodeEmpty) @@ -274,8 +275,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript from serialized data: has neither Lua source code nor bytecode!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: has neither Lua source code nor bytecode!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenUserModulesMissing) @@ -298,8 +299,8 @@ namespace ramses::internal const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); EXPECT_EQ(nullptr, LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap)); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript from serialized data: missing user module dependencies!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: missing user module dependencies!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenStandardModulesMissing) @@ -322,8 +323,8 @@ namespace ramses::internal const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); EXPECT_EQ(nullptr, LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap)); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript from serialized data: missing standard module dependencies!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: missing standard module dependencies!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenRootInputMissing) @@ -348,8 +349,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript from serialized data: missing root input!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: missing root input!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenRootOutputMissing) @@ -374,8 +375,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript from serialized data: missing root output!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: missing root output!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenRootInputHasErrors) @@ -400,8 +401,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenRootOutputHasErrors) @@ -426,8 +427,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenByteCodeInvalidAndNoSourceAvailable) @@ -454,9 +455,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_THAT(m_errorReporting.getErrors()[0].message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'name': failed loading pre-compiled byte code")); - EXPECT_THAT(m_errorReporting.getErrors()[1].message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'name' from serialized data")); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_THAT(m_errorReporting.getError()->message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'name': failed loading pre-compiled byte code")); } TEST_P(ALuaScript_Serialization, WillTryToRecompileScriptFromSourceWhenByteCodeInvalid) @@ -618,9 +618,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_THAT(m_errorReporting.getErrors()[0].message, ::testing::HasSubstr("Declaring global variables is forbidden (exceptions: the functions 'init', 'interface' and 'run')!")); - EXPECT_THAT(m_errorReporting.getErrors()[1].message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'name' from serialized data!")); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_THAT(m_errorReporting.getError()->message, ::testing::HasSubstr("Declaring global variables is forbidden (exceptions: the functions 'init', 'interface' and 'run')!")); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenLuaScriptSourceHasSyntaxErrors) @@ -644,9 +643,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_THAT(m_errorReporting.getErrors()[0].message, ::testing::HasSubstr("Error while loading script. Lua stack trace")); - EXPECT_THAT(m_errorReporting.getErrors()[1].message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'script' from serialized data!")); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_THAT(m_errorReporting.getError()->message, ::testing::HasSubstr("Error while loading script. Lua stack trace")); } // Can't happen in normal usage; still, we test this case because we should not rely on correct export during loading @@ -680,10 +678,9 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_THAT(m_errorReporting.getErrors()[0].message, + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_THAT(m_errorReporting.getError()->message, ::testing::HasSubstr("Declaring global variables is forbidden (exceptions: the functions 'init', 'interface' and 'run')! (found value of type 'number')")); - EXPECT_THAT(m_errorReporting.getErrors()[1].message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'script' from serialized data!")); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenLuaScriptSourceBreaksSandbox_WhenExecuted) @@ -743,9 +740,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_THAT(m_errorReporting.getErrors()[0].message, ::testing::HasSubstr("This is not going to compile")); - EXPECT_THAT(m_errorReporting.getErrors()[1].message, ::testing::HasSubstr("Fatal error during loading of LuaScript 'script' from serialized data!")); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_THAT(m_errorReporting.getError()->message, ::testing::HasSubstr("This is not going to compile")); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenUserModuleHasNoName) @@ -775,8 +771,8 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript 'name' module data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript 'name' module data: missing name!"); } TEST_P(ALuaScript_Serialization, ProducesErrorWhenUserModuleHasNoData) @@ -806,7 +802,7 @@ namespace ramses::internal std::unique_ptr deserialized = LuaScriptImpl::Deserialize(m_solState, serialized, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaScript 'name' module data: could not resolve dependent module with id=42!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript 'name' module data: could not resolve dependent module with id=42!"); } } diff --git a/client/logic/unittests/api/LuaScriptTest_Syntax.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Syntax.cpp similarity index 70% rename from client/logic/unittests/api/LuaScriptTest_Syntax.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Syntax.cpp index c958f215d..5fa815949 100644 --- a/client/logic/unittests/api/LuaScriptTest_Syntax.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Syntax.cpp @@ -8,12 +8,12 @@ #include "LuaScriptTest_Base.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" #include "fmt/format.h" -namespace ramses +namespace ramses::internal { class ALuaScript_Syntax : public ALuaScript { @@ -22,39 +22,36 @@ namespace ramses TEST_F(ALuaScript_Syntax, ProducesErrorIfNoInterfaceIsPresent) { - LuaScript* scriptNoInterface = m_logicEngine.createLuaScript(R"( + LuaScript* scriptNoInterface = m_logicEngine->createLuaScript(R"( function run(IN,OUT) end )", {}, "scriptNoInterface"); ASSERT_EQ(nullptr, scriptNoInterface); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("[scriptNoInterface] No 'interface' function defined!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("[scriptNoInterface] No 'interface' function defined!")); } TEST_F(ALuaScript_Syntax, ProducesErrorIfNoRunIsPresent) { - LuaScript* scriptNoRun = m_logicEngine.createLuaScript(R"( + LuaScript* scriptNoRun = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end )", {}, "scriptNoRun"); ASSERT_EQ(nullptr, scriptNoRun); - EXPECT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("[scriptNoRun] No 'run' function defined!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("[scriptNoRun] No 'run' function defined!")); } TEST_F(ALuaScript_Syntax, CannotBeCreatedFromSyntacticallyIncorrectScript) { - LuaScript* script = m_logicEngine.createLuaScript("this.is.not.valid.lua.code", {}, "badSyntaxScript"); + LuaScript* script = m_logicEngine->createLuaScript("this.is.not.valid.lua.code", {}, "badSyntaxScript"); ASSERT_EQ(nullptr, script); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'' expected near 'not'")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'' expected near 'not'")); } TEST_F(ALuaScript_Syntax, ProducesErrorIfScriptReturnsValue) { - LuaScript* scriptNoRun = m_logicEngine.createLuaScript(R"( + LuaScript* scriptNoRun = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -63,13 +60,12 @@ namespace ramses )"); ASSERT_EQ(nullptr, scriptNoRun); - EXPECT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Expected no return value in script source, but a value of type 'string' was returned!")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Expected no return value in script source, but a value of type 'string' was returned!")); } TEST_F(ALuaScript_Syntax, PropagatesErrorsEmittedInLua_FromGlobalScope) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( error("Expect this error!") function interface(IN,OUT) @@ -79,15 +75,14 @@ namespace ramses end )", WithStdModules({EStandardModule::Base}), "scriptWithErrorInGlobalCode"); EXPECT_EQ(nullptr, script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr( + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Expect this error!\nstack traceback:\n" "\t[C]: in function 'error'")); } TEST_F(ALuaScript_Syntax, PropagatesErrorsEmittedInLua_DuringInterfaceDeclaration) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) error("Expect this error!") end @@ -96,15 +91,14 @@ namespace ramses end )", WithStdModules({EStandardModule::Base}), "scriptWithErrorInInterface"); EXPECT_EQ(nullptr, script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr( + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr( "Expect this error!\nstack traceback:\n" "\t[C]: in function 'error'")); } TEST_F(ALuaScript_Syntax, PropagatesErrorsEmittedInLua_DuringRun) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -115,19 +109,16 @@ namespace ramses ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr( + EXPECT_FALSE(m_logicEngine->update()); + expectErrorSubstring( "Expect this error!\n" "stack traceback:\n" - "\t[C]: in function 'error'")); - EXPECT_EQ(script, m_logicEngine.getErrors()[0].object); - EXPECT_EQ(m_logicEngine.getErrors()[0].object->getName(), "scriptWithErrorInRun"); + "\t[C]: in function 'error'", script); } TEST_F(ALuaScript_Syntax, ProducesErrorWhenIndexingVectorPropertiesOutOfRange) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.vec2f = Type:Vec2f() IN.vec3f = Type:Vec3f() @@ -181,25 +172,20 @@ namespace ramses if (i < 1 || i > componentCount) { - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - + EXPECT_FALSE(m_logicEngine->update()); if (i < 0) { - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Only non-negative integers supported as array index type! Error while extracting integer: expected non-negative number, received '-1'")); + expectErrorSubstring("Only non-negative integers supported as array index type! Error while extracting integer: expected non-negative number, received '-1'", script); } else { - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(fmt::format("Bad index '{}', expected 1 <= i <= {}", i, componentCount))); + expectErrorSubstring(fmt::format("Bad index '{}', expected 1 <= i <= {}", i, componentCount), script); } - - EXPECT_EQ(m_logicEngine.getErrors()[0].object->getName(), "scriptOOR"); - EXPECT_EQ(m_logicEngine.getErrors()[0].object, script); } else { - EXPECT_TRUE(m_logicEngine.update()); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + EXPECT_TRUE(m_logicEngine->update()); + expectNoError(); } } } @@ -207,7 +193,7 @@ namespace ramses TEST_F(ALuaScript_Syntax, ForbidsCallingInterfaceFunctionDirectly) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -219,14 +205,14 @@ namespace ramses ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Trying to read global variable 'interface' outside the scope of init(), interface() and run() functions! " "This can cause undefined behavior and is forbidden!")); } TEST_F(ALuaScript_Syntax, ForbidsCallingRunFunctionDirectly) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end @@ -238,14 +224,14 @@ namespace ramses ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Trying to read global variable 'run' outside the scope of init(), interface() and run() functions! " "This can cause undefined behavior and is forbidden!")); } TEST_F(ALuaScript_Syntax, ForbidsCallingInitFunctionDirectly) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function init() end function interface(IN,OUT) @@ -258,14 +244,14 @@ namespace ramses ASSERT_EQ(nullptr, script); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("Trying to read global variable 'init' outside the scope of init(), interface() and run() functions! " "This can cause undefined behavior and is forbidden!")); } TEST_F(ALuaScript_Syntax, CanUseLuaSyntaxForComputingArraySize) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array = Type:Array(3, Type:Int32()) OUT.array_size = Type:Int32() @@ -276,13 +262,13 @@ namespace ramses end )"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(3, *script->getOutputs()->getChild("array_size")->get()); } TEST_F(ALuaScript_Syntax, CanUseLuaSyntaxForComputingComplexArraySize) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array = Type:Array(3, { @@ -298,13 +284,13 @@ namespace ramses end )"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(3, *script->getOutputs()->getChild("array_size")->get()); } TEST_F(ALuaScript_Syntax, CanUseLuaSyntaxForComputingStructSize) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.struct = { data1 = Type:Vec3f(), @@ -319,13 +305,13 @@ namespace ramses end )"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(3, *script->getOutputs()->getChild("struct_size")->get()); } TEST_F(ALuaScript_Syntax, CanUseLuaSyntaxForComputingVec234Size) { - m_logicEngine.createLuaScript(R"( + m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.vec2f = Type:Vec2f() IN.vec3f = Type:Vec3f() @@ -345,12 +331,12 @@ namespace ramses end )"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); } TEST_F(ALuaScript_Syntax, CanUseLuaSyntaxForComputingSizeOfStrings) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.string = Type:String() OUT.string_size = Type:Int32() @@ -362,13 +348,13 @@ namespace ramses )"); script->getInputs()->getChild("string")->set("abcde"); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(5, *script->getOutputs()->getChild("string_size")->get()); } TEST_F(ALuaScript_Syntax, RaisesErrorWhenTryingToGetSizeOfNonArrayTypes) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.notArray = Type:Int32() end @@ -378,16 +364,13 @@ namespace ramses end )", {}, "invalidArraySizeAccess"); - ASSERT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("attempt to get length of field 'notArray' (a number value)")); - EXPECT_EQ(m_logicEngine.getErrors()[0].object->getName(), "invalidArraySizeAccess"); - EXPECT_EQ(m_logicEngine.getErrors()[0].object, script); + ASSERT_FALSE(m_logicEngine->update()); + expectErrorSubstring("attempt to get length of field 'notArray' (a number value)", script); } TEST_F(ALuaScript_Syntax, ProdocesErrorWhenIndexingVectorWithNonIntegerIndices) { - LuaScript* script = m_logicEngine.createLuaScript(R"( + LuaScript* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.vec = Type:Vec4i() @@ -426,12 +409,8 @@ namespace ramses for (const auto& error : errorTypes) { errorType->set(error); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("Only non-negative integers supported as array index type!")); - EXPECT_EQ(m_logicEngine.getErrors()[0].object->getName(), "invalidIndexingScript"); - EXPECT_EQ(m_logicEngine.getErrors()[0].object, script); + EXPECT_FALSE(m_logicEngine->update()); + expectErrorSubstring("Only non-negative integers supported as array index type!", script); } } @@ -492,23 +471,19 @@ namespace ramses scriptSource += errorCase.errorCode; scriptSource += "\nend\n"; - LuaScript* script = m_logicEngine.createLuaScript(scriptSource, {}, "mismatchedVecSizes"); + LuaScript* script = m_logicEngine->createLuaScript(scriptSource, {}, "mismatchedVecSizes"); ASSERT_NE(nullptr, script); - EXPECT_FALSE(m_logicEngine.update()); - - ASSERT_EQ(1u, m_logicEngine.getErrors().size()); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr(errorCase.expectedErrorMessage)); - EXPECT_EQ(m_logicEngine.getErrors()[0].object->getName(), "mismatchedVecSizes"); - EXPECT_EQ(m_logicEngine.getErrors()[0].object, script); + EXPECT_FALSE(m_logicEngine->update()); + expectErrorSubstring(errorCase.expectedErrorMessage, script); - EXPECT_TRUE(m_logicEngine.destroy(*script)); + EXPECT_TRUE(m_logicEngine->destroy(*script)); } } TEST_F(ALuaScript_Syntax, ProducesErrorIfRunFunctionDoesNotEndCorrectly) { - LuaScript* scriptWithWrongEndInRun = m_logicEngine.createLuaScript(R"( + LuaScript* scriptWithWrongEndInRun = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -516,13 +491,12 @@ namespace ramses )", {}, "missingEndInScript"); ASSERT_EQ(nullptr, scriptWithWrongEndInRun); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'=' expected near ''")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'=' expected near ''")); } TEST_F(ALuaScript_Syntax, ProducesErrorIfInterfaceFunctionDoesNotEndCorrectly) { - LuaScript* scriptWithWrongEndInInterface = m_logicEngine.createLuaScript(R"( + LuaScript* scriptWithWrongEndInInterface = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) ENDE function run(IN,OUT) @@ -530,39 +504,36 @@ namespace ramses )", {}, "missingEndInScript"); ASSERT_EQ(nullptr, scriptWithWrongEndInInterface); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'=' expected near 'function'")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'=' expected near 'function'")); } TEST_F(ALuaScript_Syntax, ProducesErrorIfInterfaceFunctionDoesNotEndAtAll) { - LuaScript* scriptWithNoEndInInterface = m_logicEngine.createLuaScript(R"( + LuaScript* scriptWithNoEndInInterface = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) function run(IN,OUT) end )", {}, "endlessInterface"); ASSERT_EQ(nullptr, scriptWithNoEndInInterface); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'end' expected (to close 'function' at line 2) near ''")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'end' expected (to close 'function' at line 2) near ''")); } TEST_F(ALuaScript_Syntax, ProducesErrorIfRunFunctionDoesNotEndAtAll) { - LuaScript* scriptWithNoEndInRun = m_logicEngine.createLuaScript(R"( + LuaScript* scriptWithNoEndInRun = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) )", {}, "endlessRun"); ASSERT_EQ(nullptr, scriptWithNoEndInRun); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'end' expected (to close 'function' at line 4) near ''")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'end' expected (to close 'function' at line 4) near ''")); } TEST_F(ALuaScript_Syntax, ProducesErrorMessageCorrectlyNotConflictingWithFmtFormatSyntax) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) end function run(IN,OUT) @@ -575,7 +546,6 @@ namespace ramses )", {}, "missingComma"); ASSERT_EQ(nullptr, script); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_THAT(m_logicEngine.getErrors()[0].message, ::testing::HasSubstr("'}' expected (to close '{'")); + EXPECT_THAT(getLastErrorMessage(), ::testing::HasSubstr("'}' expected (to close '{'")); } } diff --git a/client/logic/unittests/api/LuaScriptTest_Types.cpp b/tests/unittests/client/logic/api/LuaScriptTest_Types.cpp similarity index 92% rename from client/logic/unittests/api/LuaScriptTest_Types.cpp rename to tests/unittests/client/logic/api/LuaScriptTest_Types.cpp index a8b43a7b3..f438a4c23 100644 --- a/client/logic/unittests/api/LuaScriptTest_Types.cpp +++ b/tests/unittests/client/logic/api/LuaScriptTest_Types.cpp @@ -8,10 +8,10 @@ #include "LuaScriptTest_Base.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/Property.h" -namespace ramses +namespace ramses::internal { class ALuaScript_Types : public ALuaScript { @@ -19,7 +19,7 @@ namespace ramses TEST_F(ALuaScript_Types, FailsToSetValueOfTopLevelInput_WhenTemplateDoesNotMatchDeclaredInputType) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithInputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithInputs); auto inputs = script->getInputs(); auto speedInt32 = inputs->getChild("speed"); @@ -47,7 +47,7 @@ namespace ramses TEST_F(ALuaScript_Types, FailsToSetArrayDirectly) { - auto* script = m_logicEngine.createLuaScript(R"( + auto* script = m_logicEngine->createLuaScript(R"( function interface(IN,OUT) IN.array_int = Type:Array(2, Type:Int32()) end @@ -65,7 +65,7 @@ namespace ramses TEST_F(ALuaScript_Types, ProvidesIndexBasedAndNameBasedAccessToInputProperties) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithInputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithInputs); ASSERT_NE(nullptr, script); auto enabled_byIndex = script->getInputs()->getChild(0); EXPECT_NE(nullptr, enabled_byIndex); @@ -77,7 +77,7 @@ namespace ramses TEST_F(ALuaScript_Types, ProvidesIndexBasedAndNameBasedAccessToOutputProperties) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithOutputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithOutputs); ASSERT_NE(nullptr, script); auto enabled_byIndex = script->getOutputs()->getChild(0); EXPECT_NE(nullptr, enabled_byIndex); @@ -89,7 +89,7 @@ namespace ramses TEST_F(ALuaScript_Types, AssignsTypeToGlobalInputsStruct) { - auto* script = m_logicEngine.createLuaScript(m_minimalScript); + auto* script = m_logicEngine->createLuaScript(m_minimalScript); auto inputs = script->getInputs(); @@ -100,7 +100,7 @@ namespace ramses TEST_F(ALuaScript_Types, AssignsTypeToGlobalOutputsStruct) { - auto* script = m_logicEngine.createLuaScript(m_minimalScript); + auto* script = m_logicEngine->createLuaScript(m_minimalScript); auto outputs = script->getOutputs(); ASSERT_EQ(0u, outputs->getChildCount()); @@ -110,7 +110,7 @@ namespace ramses TEST_F(ALuaScript_Types, ReturnsItsTopLevelInputsByIndex_IndexEqualsLexicographicOrder) { - auto* script = m_logicEngine.createLuaScript(m_minimalScriptWithInputs); + auto* script = m_logicEngine->createLuaScript(m_minimalScriptWithInputs); auto inputs = script->getInputs(); @@ -155,7 +155,7 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptWithArrays); + auto* script = m_logicEngine->createLuaScript(scriptWithArrays); std::initializer_list rootProperties = {script->getInputs(), script->getOutputs()}; @@ -213,7 +213,7 @@ namespace ramses end )"; - auto* script = m_logicEngine.createLuaScript(scriptWithArrays); + auto* script = m_logicEngine->createLuaScript(scriptWithArrays); std::initializer_list rootProperties = { script->getInputs(), script->getOutputs() }; diff --git a/tests/unittests/client/logic/api/MeshNodeBindingTest.cpp b/tests/unittests/client/logic/api/MeshNodeBindingTest.cpp new file mode 100644 index 000000000..689b16a01 --- /dev/null +++ b/tests/unittests/client/logic/api/MeshNodeBindingTest.cpp @@ -0,0 +1,427 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" +#include "RamsesTestUtils.h" +#include "SerializationTestUtils.h" +#include "RamsesObjectResolverMock.h" + +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/TypeData.h" + +#include "internal/logic/flatbuffers/generated/MeshNodeBindingGen.h" + +namespace ramses::internal +{ + class AMeshNodeBinding : public ALogicEngine + { + public: + AMeshNodeBinding() + { + // in order for the tests to be closer to reality, use ramses MeshNode with actual geometry and appearance, + // the geometry affects some of the values exposed in the binding (namely instanceCount) + const std::array vertexPositionsArray = { ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{1.f, 0.f, -1.f}, ramses::vec3f{0.f, 1.f, -1.f} }; + const ramses::ArrayResource* vertexPositions = m_scene->createArrayResource(3u, vertexPositionsArray.data()); + const std::array indexArray = { 0, 1, 2 }; + const ramses::ArrayResource* indices = m_scene->createArrayResource(3u, indexArray.data()); + + ramses::EffectDescription effectDesc; + effectDesc.setVertexShader(R"( + #version 100 + attribute vec3 a_position; + void main() + { + gl_Position = vec4(a_position, 1.0); + } + )"); + effectDesc.setFragmentShader(R"( + #version 100 + void main(void) + { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + )"); + const ramses::Effect* effect = m_scene->createEffect(effectDesc); + ramses::Appearance* appearance = m_scene->createAppearance(*effect); + + m_geometry = m_scene->createGeometry(*effect); + const auto positionsInput = effect->findAttributeInput("a_position"); + EXPECT_TRUE(positionsInput.has_value()); + m_geometry->setInputBuffer(*positionsInput, *vertexPositions); + m_geometry->setIndices(*indices); + + m_meshNodeWithGeometry = m_scene->createMeshNode("meshNode"); + m_meshNodeWithGeometry->setAppearance(*appearance); + m_meshNodeWithGeometry->setGeometry(*m_geometry); + EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); + + m_meshBinding = m_logicEngine->createMeshNodeBinding(*m_meshNodeWithGeometry, "meshBinding"); + EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); + } + + bool recreateFromFile(std::string_view filename) override + { + const auto geometryId = m_geometry->getSceneObjectId(); + const auto meshNodeId = m_meshNodeWithGeometry->getSceneObjectId(); + const auto bindingId = m_meshBinding->getSceneObjectId(); + auto res = ALogicEngine::recreateFromFile(filename); + m_geometry = m_scene->findObject(geometryId); + m_meshNodeWithGeometry = m_scene->findObject(meshNodeId); + m_meshBinding = m_logicEngine->findObject(bindingId)->as(); + return res && m_geometry != nullptr && m_meshNodeWithGeometry != nullptr && m_meshBinding != nullptr; + } + + protected: + ramses::Geometry* m_geometry = nullptr; + ramses::MeshNode* m_meshNodeWithGeometry = nullptr; + MeshNodeBinding* m_meshBinding = nullptr; + }; + + TEST_F(AMeshNodeBinding, RefersToGivenRamsesObject) + { + EXPECT_EQ(m_meshNodeWithGeometry, &m_meshBinding->getRamsesMeshNode()); + const auto& mbConst = *m_meshBinding; + EXPECT_EQ(m_meshNodeWithGeometry, &mbConst.getRamsesMeshNode()); + const auto& mbImplConst = m_meshBinding->impl(); + EXPECT_EQ(m_meshNodeWithGeometry, &mbImplConst.getRamsesMeshNode()); + } + + TEST_F(AMeshNodeBinding, HasInputPropertiesAndNoOutputs) + { + ASSERT_NE(nullptr, m_meshBinding->getInputs()); + ASSERT_EQ(size_t(MeshNodeBindingImpl::EInputProperty::COUNT), m_meshBinding->getInputs()->getChildCount()); + EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::VertexOffset)), m_meshBinding->getInputs()->getChild("vertexOffset")); + EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset)), m_meshBinding->getInputs()->getChild("indexOffset")); + EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount)), m_meshBinding->getInputs()->getChild("indexCount")); + EXPECT_EQ(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount)), m_meshBinding->getInputs()->getChild("instanceCount")); + EXPECT_EQ(nullptr, m_meshBinding->getOutputs()); + } + + TEST_F(AMeshNodeBinding, InputPropertiesAreInitializedFromBoundMeshNode) + { + m_meshNodeWithGeometry->setStartVertex(42); + m_meshNodeWithGeometry->setStartIndex(43); + m_meshNodeWithGeometry->setIndexCount(44); + m_meshNodeWithGeometry->setInstanceCount(45); + // create new binding + const auto binding = m_logicEngine->createMeshNodeBinding(*m_meshNodeWithGeometry); + + EXPECT_EQ(42, *binding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::VertexOffset))->get()); + EXPECT_EQ(43, *binding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset))->get()); + EXPECT_EQ(44, *binding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount))->get()); + EXPECT_EQ(45, *binding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount))->get()); + } + + TEST_F(AMeshNodeBinding, SetsModifiedBoundValuesOnUpdate) + { + // initial values + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartVertex()); + EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartIndex()); + EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); + EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); + + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::VertexOffset))->set(42)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(42u, m_meshNodeWithGeometry->getStartVertex()); + EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartIndex()); + EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); + EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); + + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset))->set(43)); + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount))->set(44)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(42u, m_meshNodeWithGeometry->getStartVertex()); + EXPECT_EQ(43u, m_meshNodeWithGeometry->getStartIndex()); + EXPECT_EQ(44u, m_meshNodeWithGeometry->getIndexCount()); + EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); + + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount))->set(45)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(42u, m_meshNodeWithGeometry->getStartVertex()); + EXPECT_EQ(43u, m_meshNodeWithGeometry->getStartIndex()); + EXPECT_EQ(44u, m_meshNodeWithGeometry->getIndexCount()); + EXPECT_EQ(45u, m_meshNodeWithGeometry->getInstanceCount()); + } + + TEST_F(AMeshNodeBinding, FailsUpdateIfTryingToSetInvalidValue_VertexOffset) + { + // mesh vertex offset cannot be negative + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::VertexOffset))->set(-1)); + EXPECT_FALSE(m_logicEngine->update()); + expectError("MeshNodeBinding vertex offset cannot be negative", m_meshBinding); + } + + TEST_F(AMeshNodeBinding, FailsUpdateIfTryingToSetInvalidValue_IndexOffset) + { + // mesh index offset cannot be negative + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset))->set(-1)); + EXPECT_FALSE(m_logicEngine->update()); + expectError("MeshNodeBinding index offset cannot be negative", m_meshBinding); + } + + TEST_F(AMeshNodeBinding, FailsUpdateIfTryingToSetInvalidValue_IndexCount) + { + // mesh index count cannot be negative + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount))->set(-1)); + EXPECT_FALSE(m_logicEngine->update()); + expectError("MeshNodeBinding index count cannot be negative", m_meshBinding); + } + + TEST_F(AMeshNodeBinding, FailsUpdateIfTryingToSetInvalidValue_InstanceCount) + { + // mesh instance count cannot be negative + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount))->set(-1)); + EXPECT_FALSE(m_logicEngine->update()); + expectError("MeshNodeBinding instance count cannot be negative", m_meshBinding); + } + + class AMeshNodeBinding_SerializationLifecycle : public AMeshNodeBinding + { + protected: + enum class ESerializationIssue + { + AllValid, + MissingBase, + MissingName, + MissingRoot, + CorruptedInputProperties, + MissingBoundObject, + UnresolvedMeshNode, + InvalidBoundObjectType + }; + + std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) + { + { + auto inputsType = MakeStruct("", { + TypeData{"vertexOffset", EPropertyType::Int32}, + TypeData{"indexOffset", EPropertyType::Int32}, + (issue == ESerializationIssue::CorruptedInputProperties ? TypeData{"wrong", EPropertyType::Int32} : TypeData{"indexCount", EPropertyType::Int32}), + TypeData{"instanceCount", EPropertyType::Int32} + }); + auto inputs = std::make_unique(std::move(inputsType), EPropertySemantics::BindingInput); + + SerializationMap serializationMap; + const auto logicObject = rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u); + auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, + logicObject, + (issue == ESerializationIssue::MissingBoundObject ? 0 : rlogic_serialization::CreateRamsesReference(m_flatBufferBuilder, + 1u, (issue == ESerializationIssue::InvalidBoundObjectType ? 0 : static_cast(ramses::ERamsesObjectType::MeshNode)))), + (issue == ESerializationIssue::MissingRoot ? 0 : PropertyImpl::Serialize(*inputs, m_flatBufferBuilder, serializationMap))); + + auto fbMeshNodeBinding = rlogic_serialization::CreateMeshNodeBinding(m_flatBufferBuilder, (issue == ESerializationIssue::MissingBase ? 0 : fbRamsesBinding)); + m_flatBufferBuilder.Finish(fbMeshNodeBinding); + } + + switch (issue) + { + case ESerializationIssue::AllValid: + case ESerializationIssue::InvalidBoundObjectType: + EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(m_meshNodeWithGeometry)); + break; + case ESerializationIssue::UnresolvedMeshNode: + EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(nullptr)); + break; + default: + break; + } + + DeserializationMap deserializationMap{ m_scene->impl() }; + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + return MeshNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, deserializationMap); + } + + flatbuffers::FlatBufferBuilder m_flatBufferBuilder; + ::testing::StrictMock m_resolverMock; + ErrorReporting m_errorReporting; + }; + + TEST_F(AMeshNodeBinding_SerializationLifecycle, CanSerializeWithNoIssue) + { + EXPECT_TRUE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::AllValid)); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingBase) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingBase)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of MeshNodeBinding from serialized data: missing base class info!"); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingName) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingName)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of MeshNodeBinding from serialized data: missing name and/or ID!"); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingRoot) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingRoot)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of MeshNodeBinding from serialized data: missing root input!"); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_CorruptedInputProperties) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::CorruptedInputProperties)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of MeshNodeBinding from serialized data: corrupted root input!"); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_MissingBoundObject) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::MissingBoundObject)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of MeshNodeBinding from serialized data: missing ramses object reference!"); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_UnresolvedMeshNode) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::UnresolvedMeshNode)); + // error message is generated in resolver which is mocked here + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, ReportsSerializationError_InvalidBoundObjectType) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(AMeshNodeBinding_SerializationLifecycle::ESerializationIssue::InvalidBoundObjectType)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of MeshNodeBinding from serialized data: loaded object type does not match referenced object type!"); + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, KeepsItsPropertiesAfterDeserialization) + { + { + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset))->set(41)); + EXPECT_TRUE(m_meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount))->set(42)); + EXPECT_TRUE(m_logicEngine->update()); + ASSERT_TRUE(saveToFileWithoutValidation("binding.bin")); + } + + { + ASSERT_TRUE(recreateFromFile("binding.bin")); + const auto loadedBinding = m_logicEngine->findObject("meshBinding"); + ASSERT_TRUE(loadedBinding); + EXPECT_EQ(m_meshNodeWithGeometry, &loadedBinding->getRamsesMeshNode()); + + ASSERT_NE(nullptr, loadedBinding->getInputs()); + ASSERT_EQ(size_t(MeshNodeBindingImpl::EInputProperty::COUNT), loadedBinding->getInputs()->getChildCount()); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::VertexOffset)), loadedBinding->getInputs()->getChild("vertexOffset")); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset)), loadedBinding->getInputs()->getChild("indexOffset")); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount)), loadedBinding->getInputs()->getChild("indexCount")); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount)), loadedBinding->getInputs()->getChild("instanceCount")); + EXPECT_EQ(nullptr, loadedBinding->getOutputs()); + + EXPECT_EQ(41, *loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset))->get()); + EXPECT_EQ(42, *loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount))->get()); + + // confidence test - can set new values + EXPECT_TRUE(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset))->set(43)); + EXPECT_TRUE(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount))->set(44)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartVertex()); // did not change + EXPECT_EQ(43u, m_meshNodeWithGeometry->getStartIndex()); // changed before saving and again after loading + EXPECT_EQ(44u, m_meshNodeWithGeometry->getIndexCount()); // changed after loading + EXPECT_EQ(42u, m_meshNodeWithGeometry->getInstanceCount()); // changed before saving + } + } + + TEST_F(AMeshNodeBinding_SerializationLifecycle, DoesNotModifyAnyValueIfNotSetDuringSerializationAndDeserialization) + { + { + EXPECT_TRUE(m_logicEngine->update()); + ASSERT_TRUE(saveToFileWithoutValidation("binding.bin")); + } + + { + ASSERT_TRUE(recreateFromFile("binding.bin")); + const auto loadedBinding = m_logicEngine->findObject("meshBinding"); + ASSERT_TRUE(loadedBinding); + EXPECT_EQ(m_meshNodeWithGeometry, &loadedBinding->getRamsesMeshNode()); + + ASSERT_NE(nullptr, loadedBinding->getInputs()); + ASSERT_EQ(size_t(MeshNodeBindingImpl::EInputProperty::COUNT), loadedBinding->getInputs()->getChildCount()); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::VertexOffset)), loadedBinding->getInputs()->getChild("vertexOffset")); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexOffset)), loadedBinding->getInputs()->getChild("indexOffset")); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount)), loadedBinding->getInputs()->getChild("indexCount")); + EXPECT_EQ(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::InstanceCount)), loadedBinding->getInputs()->getChild("instanceCount")); + EXPECT_EQ(nullptr, loadedBinding->getOutputs()); + + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartVertex()); + EXPECT_EQ(0u, m_meshNodeWithGeometry->getStartIndex()); + EXPECT_EQ(3u, m_meshNodeWithGeometry->getIndexCount()); + EXPECT_EQ(1u, m_meshNodeWithGeometry->getInstanceCount()); + } + } + + // Note that this is not recommended usage of Ramses binding, modifying the bound object after it is bound + TEST_F(AMeshNodeBinding, DoesNotModifyIndexCountEvenIfGeometryAssignedAfterBindingIsCreated) + { + auto meshNode = m_scene->createMeshNode(); + auto meshBinding = m_logicEngine->createMeshNodeBinding(*meshNode); + EXPECT_EQ(0u, meshNode->getIndexCount()); + + // assign geometry which in ramses automatically updates index count based on its indices + meshNode->setGeometry(*m_geometry); + EXPECT_EQ(3u, meshNode->getIndexCount()); + + // mesh binding update must not affect that value even though binding's property holds indexCount=0 because it was initialized with it + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(3u, meshNode->getIndexCount()); + + // the index count value will be overridden only when explicitly set from binding (or link) + EXPECT_TRUE(meshBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount))->set(2)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(2u, meshNode->getIndexCount()); + } + + // Note that this is not recommended usage of Ramses binding, modifying the bound object after it is bound + TEST_F(AMeshNodeBinding, DoesNotModifyIndexCountEvenIfGeometryAssignedAfterBindingIsCreated_SerializedAndDeserialized) + { + { + auto meshNode = m_scene->createMeshNode("mesh01"); + m_logicEngine->createMeshNodeBinding(*meshNode, "bindingWithLateGeometry"); + EXPECT_EQ(0u, meshNode->getIndexCount()); + + // assign geometry which in ramses automatically updates index count based on its indices + meshNode->setGeometry(*m_geometry); + EXPECT_EQ(3u, meshNode->getIndexCount()); + + // mesh binding update must not affect that value even though binding's property holds indexCount=0 because it was initialized with it + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(3u, meshNode->getIndexCount()); + + ASSERT_TRUE(saveToFileWithoutValidation("binding.bin")); + } + + ASSERT_TRUE(recreateFromFile("binding.bin")); + auto meshNode = m_scene->findObject("mesh01"); + ASSERT_TRUE(meshNode != nullptr); + const auto loadedBinding = m_logicEngine->findObject("bindingWithLateGeometry"); + ASSERT_TRUE(loadedBinding); + EXPECT_EQ(meshNode, &loadedBinding->getRamsesMeshNode()); + + // value still unchanged + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(3u, meshNode->getIndexCount()); + + // the index count value will be overridden only when explicitly set from binding (or link) + EXPECT_TRUE(loadedBinding->getInputs()->getChild(size_t(MeshNodeBindingImpl::EInputProperty::IndexCount))->set(2)); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(2u, meshNode->getIndexCount()); + } +} diff --git a/client/logic/unittests/api/RamsesNodeBindingTest.cpp b/tests/unittests/client/logic/api/NodeBindingTest.cpp similarity index 55% rename from client/logic/unittests/api/RamsesNodeBindingTest.cpp rename to tests/unittests/client/logic/api/NodeBindingTest.cpp index 8fb6fe75c..c5765216a 100644 --- a/client/logic/unittests/api/RamsesNodeBindingTest.cpp +++ b/tests/unittests/client/logic/api/NodeBindingTest.cpp @@ -11,26 +11,26 @@ #include "RamsesObjectResolverMock.h" #include "RamsesTestUtils.h" #include "SerializationTestUtils.h" -#include "WithTempDirectory.h" #include "LogTestUtils.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/LogicEngineImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/ErrorReporting.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/MeshNode.h" +#include "ramses/client/Node.h" +#include "ramses/client/MeshNode.h" -#include "generated/RamsesNodeBindingGen.h" +#include "internal/logic/flatbuffers/generated/NodeBindingGen.h" #include "glm/gtc/type_ptr.hpp" namespace ramses::internal { - class ARamsesNodeBinding : public ALogicEngine + class ANodeBinding : public ALogicEngine { protected: static void ExpectDefaultValues(ramses::Node& node, ENodePropertyStaticIndex prop) @@ -49,7 +49,7 @@ namespace ramses::internal // Enabled and Visibility bind to a single 3-state node flag case ENodePropertyStaticIndex::Visibility: case ENodePropertyStaticIndex::Enabled: - EXPECT_EQ(node.getVisibility(), ramses::EVisibilityMode::Visible); + EXPECT_EQ(node.getVisibility(), EVisibilityMode::Visible); break; } } @@ -94,27 +94,27 @@ namespace ramses::internal } }; - TEST_F(ARamsesNodeBinding, KeepsNameProvidedDuringConstruction) + TEST_F(ANodeBinding, KeepsNameProvidedDuringConstruction) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); EXPECT_EQ("NodeBinding", nodeBinding.getName()); } - TEST_F(ARamsesNodeBinding, KeepsIdProvidedDuringConstruction) + TEST_F(ANodeBinding, KeepsIdProvidedDuringConstruction) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_EQ(nodeBinding.getId(), 1u); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + EXPECT_EQ(nodeBinding.getSceneObjectId().getValue(), 9u); } - TEST_F(ARamsesNodeBinding, ReturnsNullptrForOutputs) + TEST_F(ANodeBinding, ReturnsNullptrForOutputs) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); EXPECT_EQ(nullptr, nodeBinding.getOutputs()); } - TEST_F(ARamsesNodeBinding, ProvidesAccessToAllNodePropertiesInItsInputs) + TEST_F(ANodeBinding, ProvidesAccessToAllNodePropertiesInItsInputs) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); auto inputs = nodeBinding.getInputs(); ASSERT_NE(nullptr, inputs); @@ -126,10 +126,10 @@ namespace ramses::internal auto visibility = inputs->getChild("visibility"); // Test that internal indices match properties resolved by name - EXPECT_EQ(rotation, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Rotation))); - EXPECT_EQ(scaling, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Scaling))); - EXPECT_EQ(translation, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Translation))); - EXPECT_EQ(visibility, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Visibility))); + EXPECT_EQ(rotation, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Rotation))); + EXPECT_EQ(scaling, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Scaling))); + EXPECT_EQ(translation, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Translation))); + EXPECT_EQ(visibility, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Visibility))); ASSERT_NE(nullptr, rotation); EXPECT_EQ(EPropertyType::Vec3f, rotation->getType()); @@ -149,14 +149,14 @@ namespace ramses::internal auto enabled = inputs->getChild("enabled"); ASSERT_NE(nullptr, enabled); - EXPECT_EQ(enabled, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Enabled))); + EXPECT_EQ(enabled, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Enabled))); EXPECT_EQ(EPropertyType::Bool, enabled->getType()); EXPECT_EQ(0u, enabled->getChildCount()); } - TEST_F(ARamsesNodeBinding, InitializesInputPropertiesToMatchRamsesDefaultValues) + TEST_F(ANodeBinding, InitializesInputPropertiesToMatchRamsesDefaultValues) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); auto inputs = nodeBinding.getInputs(); ASSERT_NE(nullptr, inputs); @@ -169,12 +169,12 @@ namespace ramses::internal m_node->getRotation(zeroes); EXPECT_EQ(zeroes, vec3f(0.f, 0.f, 0.f)); - EXPECT_EQ(ramses::ERotationType::Euler_XYZ, m_node->getRotationType()); + EXPECT_EQ(ERotationType::Euler_XYZ, m_node->getRotationType()); m_node->getTranslation(zeroes); EXPECT_EQ(zeroes, vec3f(0.f, 0.f, 0.f)); m_node->getScaling(ones); EXPECT_EQ(ones, vec3f(1.f, 1.f, 1.f)); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Visible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Visible); EXPECT_EQ(zeroes, inputs->getChild("rotation")->get()); EXPECT_EQ(zeroes, inputs->getChild("translation")->get()); @@ -183,20 +183,20 @@ namespace ramses::internal EXPECT_EQ(true, *inputs->getChild("enabled")->get()); } - TEST_F(ARamsesNodeBinding, MarksInputsAsBindingInputs) + TEST_F(ANodeBinding, MarksInputsAsBindingInputs) { - auto* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + auto* nodeBinding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); auto inputs = nodeBinding->getInputs(); const auto inputCount = inputs->getChildCount(); for (size_t i = 0; i < inputCount; ++i) { - EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild(i)->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, inputs->getChild(i)->impl().getPropertySemantics()); } } - TEST_F(ARamsesNodeBinding, ReturnsNodePropertiesForInputsConst) + TEST_F(ANodeBinding, ReturnsNodePropertiesForInputsConst) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); const auto inputs = nodeBinding.getInputs(); ASSERT_NE(nullptr, inputs); EXPECT_EQ(5u, inputs->getChildCount()); @@ -228,15 +228,15 @@ namespace ramses::internal EXPECT_EQ(0u, enabled->getChildCount()); } - TEST_F(ARamsesNodeBinding, ReturnsBoundRamsesNode) + TEST_F(ANodeBinding, ReturnsBoundRamsesNode) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); EXPECT_EQ(m_node, &nodeBinding.getRamsesNode()); } - TEST_F(ARamsesNodeBinding, DoesNotModifyRamsesWithoutUpdateBeingCalled) + TEST_F(ANodeBinding, DoesNotModifyRamsesWithoutUpdateBeingCalled) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); auto inputs = nodeBinding.getInputs(); inputs->getChild("rotation")->set(vec3f{ 0.1f, 0.2f, 0.3f }); @@ -249,11 +249,11 @@ namespace ramses::internal } // This test is a bit too big, but splitting it creates a lot of test code duplication... Better keep it like this, it documents behavior quite well - TEST_F(ARamsesNodeBinding, ModifiesRamsesOnUpdate_OnlyAfterExplicitlyAssignedToInputs) + TEST_F(ANodeBinding, ModifiesRamsesOnUpdate_OnlyAfterExplicitlyAssignedToInputs) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); - nodeBinding.m_nodeBinding.update(); + nodeBinding.impl().update(); ExpectDefaultValues(*m_node); @@ -263,7 +263,7 @@ namespace ramses::internal // Updte not called yet -> still default values ExpectDefaultValues(*m_node); - nodeBinding.m_nodeBinding.update(); + nodeBinding.impl().update(); // Only propagated rotation, the others still have default values ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 0.1f, 0.2f, 0.3f }); ExpectDefaultValues(*m_node, ENodePropertyStaticIndex::Translation); @@ -277,17 +277,17 @@ namespace ramses::internal inputs->getChild("translation")->set(vec3f{ 2.1f, 2.2f, 2.3f }); inputs->getChild("visibility")->set(true); inputs->getChild("enabled")->set(false); - nodeBinding.m_nodeBinding.update(); + nodeBinding.impl().update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 42.1f, 42.2f, 42.3f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 1.1f, 1.2f, 1.3f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 2.1f, 2.2f, 2.3f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Off); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Off); } - TEST_F(ARamsesNodeBinding, PropagatesItsInputsToRamsesNodeOnUpdate) + TEST_F(ANodeBinding, PropagatesItsInputsToRamsesNodeOnUpdate) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); auto inputs = nodeBinding.getInputs(); inputs->getChild("rotation")->set(vec3f{0.1f, 0.2f, 0.3f}); @@ -296,15 +296,15 @@ namespace ramses::internal inputs->getChild("visibility")->set(false); inputs->getChild("enabled")->set(true); - nodeBinding.m_nodeBinding.update(); + nodeBinding.impl().update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 0.1f, 0.2f, 0.3f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 1.1f, 1.2f, 1.3f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 2.1f, 2.2f, 2.3f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); } - TEST_F(ARamsesNodeBinding, PropagatesItsInputsToRamsesNodeOnUpdate_WithLinksInsteadOfSetCall) + TEST_F(ANodeBinding, PropagatesItsInputsToRamsesNodeOnUpdate_WithLinksInsteadOfSetCall) { const std::string_view scriptSrc = R"( function interface(IN,OUT) @@ -317,142 +317,142 @@ namespace ramses::internal end )"; - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("visibility"), *nodeBinding.getInputs()->getChild("visibility"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("visibility"), *nodeBinding.getInputs()->getChild("visibility"))); // Links have no effect before update() explicitly called ExpectDefaultValues(*m_node); - m_logicEngine.update(); + m_logicEngine->update(); // Linked values got updates, not-linked values were not modified ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 2.f, 3.f }); ExpectDefaultValues(*m_node, ENodePropertyStaticIndex::Scaling); ExpectDefaultValues(*m_node, ENodePropertyStaticIndex::Translation); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); } - TEST_F(ARamsesNodeBinding, DoesNotOverrideExistingValuesAfterRamsesNodeIsAssignedToBinding) + TEST_F(ANodeBinding, DoesNotOverrideExistingValuesAfterRamsesNodeIsAssignedToBinding) { - m_node->setVisibility(ramses::EVisibilityMode::Off); - m_node->setRotation({0.1f, 0.2f, 0.3f}, ramses::ERotationType::Euler_ZYX); + m_node->setVisibility(EVisibilityMode::Off); + m_node->setRotation({0.1f, 0.2f, 0.3f}, ERotationType::Euler_ZYX); m_node->setScaling({1.1f, 1.2f, 1.3f}); m_node->setTranslation({2.1f, 2.2f, 2.3f}); - m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 0.1f, 0.2f, 0.3f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 1.1f, 1.2f, 1.3f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 2.1f, 2.2f, 2.3f }); } - TEST_F(ARamsesNodeBinding, DoesNotOverrideExistingQuaternionAfterRamsesNodeIsAssignedToBinding) + TEST_F(ANodeBinding, DoesNotOverrideExistingQuaternionAfterRamsesNodeIsAssignedToBinding) { quat quaternion{0.5f, 0.5f, -0.5f, 0.5f}; m_node->setRotation(quaternion); - m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion, "NodeBinding"); + m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion, "NodeBinding"); quat qOut; m_node->getRotation(qOut); EXPECT_EQ(quaternion, qOut); } - TEST_F(ARamsesNodeBinding, InitializesVisibilityPropertiesFromRamsesNode) + TEST_F(ANodeBinding, InitializesVisibilityPropertiesFromRamsesNode) { // visible - m_node->setVisibility(ramses::EVisibilityMode::Visible); - RamsesNodeBinding* nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + m_node->setVisibility(EVisibilityMode::Visible); + NodeBinding* nodeBinding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); EXPECT_TRUE(*nodeBinding->getInputs()->getChild("visibility")->get()); EXPECT_TRUE(*nodeBinding->getInputs()->getChild("enabled")->get()); - m_logicEngine.destroy(*nodeBinding); + m_logicEngine->destroy(*nodeBinding); // invisible - m_node->setVisibility(ramses::EVisibilityMode::Invisible); - nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + m_node->setVisibility(EVisibilityMode::Invisible); + nodeBinding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); EXPECT_FALSE(*nodeBinding->getInputs()->getChild("visibility")->get()); EXPECT_TRUE(*nodeBinding->getInputs()->getChild("enabled")->get()); - m_logicEngine.destroy(*nodeBinding); + m_logicEngine->destroy(*nodeBinding); // off - m_node->setVisibility(ramses::EVisibilityMode::Off); - nodeBinding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + m_node->setVisibility(EVisibilityMode::Off); + nodeBinding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); EXPECT_FALSE(*nodeBinding->getInputs()->getChild("visibility")->get()); EXPECT_FALSE(*nodeBinding->getInputs()->getChild("enabled")->get()); - m_logicEngine.destroy(*nodeBinding); + m_logicEngine->destroy(*nodeBinding); } - TEST_F(ARamsesNodeBinding, ResolvesVisibilityModeFromProperties) + TEST_F(ANodeBinding, ResolvesVisibilityModeFromProperties) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); auto inputs = nodeBinding.getInputs(); // visible inputs->getChild("visibility")->set(true); inputs->getChild("enabled")->set(true); - nodeBinding.m_nodeBinding.update(); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Visible); + nodeBinding.impl().update(); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Visible); // invisible inputs->getChild("visibility")->set(false); inputs->getChild("enabled")->set(true); - nodeBinding.m_nodeBinding.update(); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + nodeBinding.impl().update(); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); // off with visible (off overrides visibility) inputs->getChild("visibility")->set(true); inputs->getChild("enabled")->set(false); - nodeBinding.m_nodeBinding.update(); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Off); + nodeBinding.impl().update(); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Off); // off with invisible (off overrides visibility) inputs->getChild("visibility")->set(false); inputs->getChild("enabled")->set(false); - nodeBinding.m_nodeBinding.update(); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Off); + nodeBinding.impl().update(); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Off); } - class ARamsesNodeBinding_RotationTypes : public ARamsesNodeBinding + class ANodeBinding_RotationTypes : public ANodeBinding { }; - TEST_F(ARamsesNodeBinding_RotationTypes, HasRightHandedEulerXYZRotationConventionByDefault) + TEST_F(ANodeBinding_RotationTypes, HasRightHandedEulerXYZRotationConventionByDefault) { - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node); - EXPECT_EQ(ramses::ERotationType::Euler_XYZ, nodeBinding.getRotationType()); + EXPECT_EQ(ERotationType::Euler_XYZ, nodeBinding.getRotationType()); } - TEST_F(ARamsesNodeBinding_RotationTypes, AppliesAllEulerConventions) + TEST_F(ANodeBinding_RotationTypes, AppliesAllEulerConventions) { - std::vector enumValues = + std::vector enumValues = { - ramses::ERotationType::Euler_XYZ, - ramses::ERotationType::Euler_XZY, - ramses::ERotationType::Euler_YXZ, - ramses::ERotationType::Euler_YZX, - ramses::ERotationType::Euler_ZXY, - ramses::ERotationType::Euler_ZYX, - ramses::ERotationType::Euler_XYX, - ramses::ERotationType::Euler_XZX, - ramses::ERotationType::Euler_YXY, - ramses::ERotationType::Euler_YZY, - ramses::ERotationType::Euler_ZXZ, - ramses::ERotationType::Euler_ZYZ, + ERotationType::Euler_XYZ, + ERotationType::Euler_XZY, + ERotationType::Euler_YXZ, + ERotationType::Euler_YZX, + ERotationType::Euler_ZXY, + ERotationType::Euler_ZYX, + ERotationType::Euler_XYX, + ERotationType::Euler_XZX, + ERotationType::Euler_YXY, + ERotationType::Euler_YZY, + ERotationType::Euler_ZXZ, + ERotationType::Euler_ZYZ, }; for (const auto& rotationType : enumValues) { m_node->setRotation({0.f, 0.f, 0.f}, rotationType); - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, rotationType); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, rotationType); nodeBinding.getInputs()->getChild("rotation")->set({ 30, 45, 60 }); - m_logicEngine.update(); + m_logicEngine->update(); vec3f rotValues; m_node->getRotation(rotValues); @@ -462,62 +462,62 @@ namespace ramses::internal } } - TEST_F(ARamsesNodeBinding_RotationTypes, TakesOverRotationConventionFromRamsesNode_WhenEnumMatches) + TEST_F(ANodeBinding_RotationTypes, TakesOverRotationConventionFromRamsesNode_WhenEnumMatches) { - const std::vector enumValues = + const std::vector enumValues = { - ramses::ERotationType::Euler_XYZ, - ramses::ERotationType::Euler_XZY, - ramses::ERotationType::Euler_YXZ, - ramses::ERotationType::Euler_YZX, - ramses::ERotationType::Euler_ZXY, - ramses::ERotationType::Euler_ZYX, - ramses::ERotationType::Euler_XYX, - ramses::ERotationType::Euler_XZX, - ramses::ERotationType::Euler_YXY, - ramses::ERotationType::Euler_YZY, - ramses::ERotationType::Euler_ZXZ, - ramses::ERotationType::Euler_ZYZ, + ERotationType::Euler_XYZ, + ERotationType::Euler_XZY, + ERotationType::Euler_YXZ, + ERotationType::Euler_YZX, + ERotationType::Euler_ZXY, + ERotationType::Euler_ZYX, + ERotationType::Euler_XYX, + ERotationType::Euler_XZX, + ERotationType::Euler_YXY, + ERotationType::Euler_YZY, + ERotationType::Euler_ZXZ, + ERotationType::Euler_ZYZ, }; for (const auto& rotationType : enumValues) { m_node->setRotation({1, 2, 3}, rotationType); - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, rotationType, "NodeBinding"); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, rotationType, "NodeBinding"); EXPECT_EQ(*binding->getInputs()->getChild("rotation")->get(), vec3f(1.f, 2.f, 3.f)); EXPECT_EQ(binding->getRotationType(), rotationType); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); } } - TEST_F(ARamsesNodeBinding_RotationTypes, TakesOverQuaternionFromRamsesNode_WhenEnumMatches) + TEST_F(ANodeBinding_RotationTypes, TakesOverQuaternionFromRamsesNode_WhenEnumMatches) { m_node->setRotation(quat(1.f, 2.f, 3.f, 4.f)); - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion, "NodeBinding"); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion, "NodeBinding"); EXPECT_EQ(*binding->getInputs()->getChild("rotation")->get(), vec4f(2.f, 3.f, 4.f, 1.f)); - EXPECT_EQ(binding->getRotationType(), ramses::ERotationType::Quaternion); + EXPECT_EQ(binding->getRotationType(), ERotationType::Quaternion); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); } - TEST_F(ARamsesNodeBinding_RotationTypes, InitializesRotationWithZeroAndPrintsWarning_WhenConventionEnumsDontMatch) + TEST_F(ANodeBinding_RotationTypes, InitializesRotationWithZeroAndPrintsWarning_WhenConventionEnumsDontMatch) { - const std::vector> mismatchedEnumPairs = + const std::vector> mismatchedEnumPairs = { - {ramses::ERotationType::Euler_ZYX, ramses::ERotationType::Euler_XYZ}, - {ramses::ERotationType::Euler_YZX, ramses::ERotationType::Euler_ZYX}, - {ramses::ERotationType::Euler_ZXY, ramses::ERotationType::Euler_YZX}, - {ramses::ERotationType::Euler_XZY, ramses::ERotationType::Euler_ZXY}, - {ramses::ERotationType::Euler_YXZ, ramses::ERotationType::Euler_XZY}, - {ramses::ERotationType::Euler_XYZ, ramses::ERotationType::Euler_YXZ}, + {ERotationType::Euler_ZYX, ERotationType::Euler_XYZ}, + {ERotationType::Euler_YZX, ERotationType::Euler_ZYX}, + {ERotationType::Euler_ZXY, ERotationType::Euler_YZX}, + {ERotationType::Euler_XZY, ERotationType::Euler_ZXY}, + {ERotationType::Euler_YXZ, ERotationType::Euler_XZY}, + {ERotationType::Euler_XYZ, ERotationType::Euler_YXZ}, }; std::string warningMessage; ELogLevel messageType; - ScopedLogContextLevel scopedLogs(ELogLevel::Warn, [&warningMessage, &messageType](ELogLevel msgType, std::string_view message) { + ScopedLogContextLevel scopedLogs(CONTEXT_CLIENT, ELogLevel::Warn, [&warningMessage, &messageType](ELogLevel msgType, std::string_view message) { warningMessage = message; messageType = msgType; }); @@ -525,78 +525,84 @@ namespace ramses::internal for (const auto& [logicEnum, ramsesEnum] : mismatchedEnumPairs) { m_node->setRotation({1, 2, 3}, ramsesEnum); - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, logicEnum, "NodeBinding"); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, logicEnum, "NodeBinding"); EXPECT_EQ(messageType, ELogLevel::Warn); - EXPECT_EQ(warningMessage, fmt::format("Initial rotation values for RamsesNodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type.", binding->m_impl.getIdentificationString())); + EXPECT_EQ(warningMessage, + fmt::format("Initial rotation values for NodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type.", + binding->impl().getIdentificationString())); EXPECT_EQ(*binding->getInputs()->getChild("rotation")->get(), vec3f(0.f, 0.f, 0.f)); EXPECT_EQ(binding->getRotationType(), logicEnum); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); messageType = ELogLevel::Off; warningMessage = ""; } } - TEST_F(ARamsesNodeBinding_RotationTypes, InitializesQuaternionWithIdentityAndPrintsWarning_WhenConventionEnumsDontMatch) + TEST_F(ANodeBinding_RotationTypes, InitializesQuaternionWithIdentityAndPrintsWarning_WhenConventionEnumsDontMatch) { std::string warningMessage; ELogLevel messageType; - ScopedLogContextLevel scopedLogs(ELogLevel::Warn, [&warningMessage, &messageType](ELogLevel msgType, std::string_view message) { + ScopedLogContextLevel scopedLogs(CONTEXT_CLIENT, ELogLevel::Warn, [&warningMessage, &messageType](ELogLevel msgType, std::string_view message) { warningMessage = message; messageType = msgType; }); - m_node->setRotation({1, 2, 3}); - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion, "NodeBinding"); + m_node->setRotation({1, 2, 3}, ERotationType::Euler_XYZ); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion, "NodeBinding"); EXPECT_EQ(messageType, ELogLevel::Warn); - EXPECT_EQ(warningMessage, fmt::format("Initial rotation values for RamsesNodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type. Expected Quaternion, got Euler.", binding->m_impl.getIdentificationString())); + EXPECT_EQ( + warningMessage, + fmt::format( + "Initial rotation values for NodeBinding '{}' will not be imported from bound Ramses node due to mismatching rotation type. Expected Quaternion, got Euler.", + binding->impl().getIdentificationString())); EXPECT_EQ(*binding->getInputs()->getChild("rotation")->get(), vec4f(0.f, 0.f, 0.f, 1.f)); - EXPECT_EQ(binding->getRotationType(), ramses::ERotationType::Quaternion); + EXPECT_EQ(binding->getRotationType(), ERotationType::Quaternion); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); messageType = ELogLevel::Off; warningMessage = ""; } - TEST_F(ARamsesNodeBinding_RotationTypes, PrintsNoWarning_WhenConventionEnumsDontMatch_InSpecialCaseWhereRamsesRotationValuesAreZero) + TEST_F(ANodeBinding_RotationTypes, PrintsNoWarning_WhenConventionEnumsDontMatch_InSpecialCaseWhereRamsesRotationValuesAreZero) { - ScopedLogContextLevel scopedLogs(ELogLevel::Warn, [](ELogLevel /*unused*/, std::string_view /*unused*/) { + ScopedLogContextLevel scopedLogs(CONTEXT_CLIENT, ELogLevel::Warn, [](ELogLevel /*unused*/, std::string_view /*unused*/) { FAIL() << "Should not cause any warnings!"; }); - m_node->setRotation({0.f, 0.f, 0.f}, ramses::ERotationType::Euler_XYX); - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + m_node->setRotation({0.f, 0.f, 0.f}, ERotationType::Euler_XYX); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); EXPECT_EQ(*binding->getInputs()->getChild("rotation")->get(), vec3f(0.f, 0.f, 0.f)); - EXPECT_EQ(binding->getRotationType(), ramses::ERotationType::Euler_XYZ); + EXPECT_EQ(binding->getRotationType(), ERotationType::Euler_XYZ); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); } - TEST_F(ARamsesNodeBinding_RotationTypes, PrintsNoWarning_WhenNoQuaternion_InSpecialCaseWhereRamsesRotationValuesAreZero) + TEST_F(ANodeBinding_RotationTypes, PrintsNoWarning_WhenNoQuaternion_InSpecialCaseWhereRamsesRotationValuesAreZero) { - ScopedLogContextLevel scopedLogs(ELogLevel::Warn, [](ELogLevel /*unused*/, std::string_view /*unused*/) { + ScopedLogContextLevel scopedLogs(CONTEXT_CLIENT, ELogLevel::Warn, [](ELogLevel /*unused*/, std::string_view /*unused*/) { FAIL() << "Should not cause any warnings!"; }); - m_node->setRotation({0.f, 0.f, 0.f}, ramses::ERotationType::Euler_XYX); - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion, "NodeBinding"); + m_node->setRotation({0.f, 0.f, 0.f}, ERotationType::Euler_XYX); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion, "NodeBinding"); EXPECT_EQ(*binding->getInputs()->getChild("rotation")->get(), vec4f(0.f, 0.f, 0.f, 1.f)); - EXPECT_EQ(binding->getRotationType(), ramses::ERotationType::Quaternion); + EXPECT_EQ(binding->getRotationType(), ERotationType::Quaternion); - ASSERT_TRUE(m_logicEngine.destroy(*binding)); + ASSERT_TRUE(m_logicEngine->destroy(*binding)); } - TEST_F(ARamsesNodeBinding_RotationTypes, InitializesQuaternionAsVec4f) + TEST_F(ANodeBinding_RotationTypes, InitializesQuaternionAsVec4f) { - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion); EXPECT_EQ(5u, binding->getInputs()->getChildCount()); EXPECT_EQ(EPropertyType::Vec4f, binding->getInputs()->getChild("rotation")->getType()); @@ -608,40 +614,40 @@ namespace ramses::internal EXPECT_EQ(EPropertyType::Bool, binding->getInputs()->getChild("enabled")->getType()); } - TEST_F(ARamsesNodeBinding_RotationTypes, InitializesQuaternionValues_WhichResultsToNoRotationInEuler) + TEST_F(ANodeBinding_RotationTypes, InitializesQuaternionValues_WhichResultsToNoRotationInEuler) { - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion); ramses::Property* quatInput = binding->getInputs()->getChild("rotation"); ASSERT_EQ(quatInput->getType(), EPropertyType::Vec4f); EXPECT_EQ(*quatInput->get(), vec4f(0.f, 0.f, 0.f, 1.f)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 0.0f, 0.0f, 0.0f }); } - TEST_F(ARamsesNodeBinding_RotationTypes, QuaternionValuesAreReturned) + TEST_F(ANodeBinding_RotationTypes, QuaternionValuesAreReturned) { - ramses::RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Quaternion); + ramses::NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, ERotationType::Quaternion); ramses::Property* quatInput = binding->getInputs()->getChild("rotation"); quatInput->set({0.5f, 0, 0, 0.8660254f}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); ExpectQuat(*m_node, vec4f{0.5f, 0.0f, 0.0f, 0.8660254f}); quatInput->set({ 0, 0.258819f, 0, 0.9659258f }); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); ExpectQuat(*m_node, vec4f{0, 0.258819f, 0, 0.9659258f}); quatInput->set({ 0, 0, 0.7071068f, 0.7071068f }); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); ExpectQuat(*m_node, vec4f{0, 0, 0.7071068f, 0.7071068f}); } // This is a confidence test that checks that no matter which rotation type is used, the final result (matrix) // is always the same as if each rotation/axis was put in its own node with the order given by the node hierarchy - TEST_F(ARamsesNodeBinding_RotationTypes, Confidence_ComplexEulerRotations_ProduceTheSameRotationResult_AsWithSeparateRamsesNodesWithSingleAxisRotations) + TEST_F(ANodeBinding_RotationTypes, Confidence_ComplexEulerRotations_ProduceTheSameRotationResult_AsWithSeparateRamsesNodesWithSingleAxisRotations) { std::array threeNodes = { m_scene->createNode(), @@ -652,14 +658,14 @@ namespace ramses::internal threeNodes[2]->setParent(*threeNodes[1]); threeNodes[1]->setParent(*threeNodes[0]); - const std::vector> combinationsToTest = + const std::vector> combinationsToTest = { - {ramses::ERotationType::Euler_ZYX, {2, 1, 0}}, - {ramses::ERotationType::Euler_YZX, {1, 2, 0}}, - {ramses::ERotationType::Euler_ZXY, {2, 0, 1}}, - {ramses::ERotationType::Euler_XZY, {0, 2, 1}}, - {ramses::ERotationType::Euler_YXZ, {1, 0, 2}}, - {ramses::ERotationType::Euler_XYZ, {0, 1, 2}}, + {ERotationType::Euler_ZYX, {2, 1, 0}}, + {ERotationType::Euler_YZX, {1, 2, 0}}, + {ERotationType::Euler_ZXY, {2, 0, 1}}, + {ERotationType::Euler_XZY, {0, 2, 1}}, + {ERotationType::Euler_YXZ, {1, 0, 2}}, + {ERotationType::Euler_XYZ, {0, 1, 2}}, }; for (const auto& [rotationType, axesOrdering] : combinationsToTest) @@ -671,22 +677,22 @@ namespace ramses::internal const auto nodeIndex = 2-i; if (axis == 0) { - threeNodes[nodeIndex]->setRotation({10, 0, 0}, ramses::ERotationType::Euler_XYZ); + threeNodes[nodeIndex]->setRotation({10, 0, 0}, ERotationType::Euler_XYZ); } else if (axis == 1) { - threeNodes[nodeIndex]->setRotation({0, 20, 0}, ramses::ERotationType::Euler_XYZ); + threeNodes[nodeIndex]->setRotation({0, 20, 0}, ERotationType::Euler_XYZ); } else { - threeNodes[nodeIndex]->setRotation({0, 0, 30}, ramses::ERotationType::Euler_XYZ); + threeNodes[nodeIndex]->setRotation({0, 0, 30}, ERotationType::Euler_XYZ); } } m_node->setRotation({0, 0, 0}, rotationType); - RamsesNodeBinding* binding = m_logicEngine.createRamsesNodeBinding(*m_node, rotationType); + NodeBinding* binding = m_logicEngine->createNodeBinding(*m_node, rotationType); binding->getInputs()->getChild("rotation")->set({10, 20, 30}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); matrix44f matFromLogic; matrix44f matFromRamses; @@ -702,12 +708,12 @@ namespace ramses::internal } } - m_logicEngine.destroy(*binding); + m_logicEngine->destroy(*binding); } } - // This fixture only contains serialization unit tests, for higher order tests see `ARamsesNodeBinding_SerializationWithFile` - class ARamsesNodeBinding_SerializationLifecycle : public ARamsesNodeBinding + // This fixture only contains serialization unit tests, for higher order tests see `ANodeBinding_SerializationWithFile` + class ANodeBinding_SerializationLifecycle : public ANodeBinding { protected: flatbuffers::FlatBufferBuilder m_flatBufferBuilder; @@ -715,21 +721,21 @@ namespace ramses::internal ::testing::StrictMock m_resolverMock; ErrorReporting m_errorReporting; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; }; // More unit tests with inputs/outputs declared in LogicNode (base class) serialization tests - TEST_F(ARamsesNodeBinding_SerializationLifecycle, RemembersBaseClassData) + TEST_F(ANodeBinding_SerializationLifecycle, RemembersBaseClassData) { // Serialize { - RamsesNodeBindingImpl binding(*m_node, ramses::ERotationType::Euler_XYZ, "name", 1u); + NodeBindingImpl binding(m_scene->impl(), *m_node, ERotationType::Euler_XYZ, "name", sceneObjectId_t{ 1u }); binding.createRootProperties(); - (void)RamsesNodeBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + (void)NodeBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); } // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); ASSERT_TRUE(serializedBinding.base()); ASSERT_TRUE(serializedBinding.base()->base()); @@ -745,29 +751,29 @@ namespace ramses::internal // Deserialize { EXPECT_CALL(m_resolverMock, findRamsesNodeInScene(::testing::Eq("name"), m_node->getSceneObjectId())).WillOnce(::testing::Return(m_node)); - std::unique_ptr deserializedBinding = RamsesNodeBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + std::unique_ptr deserializedBinding = NodeBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); ASSERT_TRUE(deserializedBinding); EXPECT_EQ(deserializedBinding->getName(), "name"); - EXPECT_EQ(deserializedBinding->getId(), 1u); + EXPECT_EQ(deserializedBinding->getSceneObjectId().getValue(), 1u); EXPECT_EQ(deserializedBinding->getInputs()->getType(), EPropertyType::Struct); - EXPECT_EQ(deserializedBinding->getInputs()->m_impl->getPropertySemantics(), EPropertySemantics::BindingInput); + EXPECT_EQ(deserializedBinding->getInputs()->impl().getPropertySemantics(), EPropertySemantics::BindingInput); EXPECT_EQ(deserializedBinding->getInputs()->getName(), ""); EXPECT_EQ(deserializedBinding->getInputs()->getChildCount(), 5u); } } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, RemembersRamsesNodeId) + TEST_F(ANodeBinding_SerializationLifecycle, RemembersRamsesNodeId) { // Serialize { - RamsesNodeBindingImpl binding(*m_node, ramses::ERotationType::Euler_XYZ, "node", 1u); + NodeBindingImpl binding(m_scene->impl(), *m_node, ERotationType::Euler_XYZ, "node", sceneObjectId_t{ 1u }); binding.createRootProperties(); - (void)RamsesNodeBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + (void)NodeBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); } // Inspect flatbuffers data - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectId(), m_node->getSceneObjectId().getValue()); EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectType(), static_cast(ramses::ERamsesObjectType::Node)); @@ -775,36 +781,36 @@ namespace ramses::internal // Deserialize { EXPECT_CALL(m_resolverMock, findRamsesNodeInScene(::testing::Eq("node"), m_node->getSceneObjectId())).WillOnce(::testing::Return(m_node)); - std::unique_ptr deserializedBinding = RamsesNodeBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + std::unique_ptr deserializedBinding = NodeBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); ASSERT_TRUE(deserializedBinding); EXPECT_EQ(&deserializedBinding->getRamsesNode(), m_node); } } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, DoesNotOverwriteRamsesValuesAfterLoad) + TEST_F(ANodeBinding_SerializationLifecycle, DoesNotOverwriteRamsesValuesAfterLoad) { // Serialize { - RamsesNodeBindingImpl binding(*m_node, ramses::ERotationType::Euler_XYZ, "node", 1u); + NodeBindingImpl binding(m_scene->impl(), *m_node, ERotationType::Euler_XYZ, "node", sceneObjectId_t{ 1u }); binding.createRootProperties(); // Set non-standard values. These will not be used after deserialization, instead the binding // will re-load the values from ramses binding.getInputs()->getChild("rotation")->set({100, 200, 300}); binding.update(); - (void)RamsesNodeBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + (void)NodeBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); } - const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); // Deserialize { // Set values different than the ones during serialization so that we can check after // deserialization they were not touched - m_node->setRotation({11, 12, 13}); + m_node->setRotation({11, 12, 13}, ERotationType::Euler_XYZ); EXPECT_CALL(m_resolverMock, findRamsesNodeInScene(::testing::Eq("node"), m_node->getSceneObjectId())).WillOnce(::testing::Return(m_node)); - std::unique_ptr deserializedBinding = RamsesNodeBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + std::unique_ptr deserializedBinding = NodeBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_EQ(&deserializedBinding->getRamsesNode(), m_node); @@ -817,25 +823,25 @@ namespace ramses::internal } } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) { { - auto binding = rlogic_serialization::CreateRamsesNodeBinding( + auto binding = rlogic_serialization::CreateNodeBinding( m_flatBufferBuilder, 0 // no base binding info ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesNodeBinding from serialized data: missing base class info!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of NodeBinding from serialized data: missing base class info!"); } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenNoBindingName) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenNoBindingName) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -844,23 +850,22 @@ namespace ramses::internal 0, // no name! 1u) ); - auto binding = rlogic_serialization::CreateRamsesNodeBinding( + auto binding = rlogic_serialization::CreateNodeBinding( m_flatBufferBuilder, base ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing name!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesNodeBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of NodeBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenNoBindingId) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenNoBindingId) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -868,20 +873,19 @@ namespace ramses::internal rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, m_flatBufferBuilder.CreateString("name"), 0)); - auto binding = rlogic_serialization::CreateRamsesNodeBinding(m_flatBufferBuilder, base); + auto binding = rlogic_serialization::CreateNodeBinding(m_flatBufferBuilder, base); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(2u, this->m_errorReporting.getErrors().size()); - EXPECT_EQ("Fatal error during loading of LogicObject base from serialized data: missing or invalid ID!", this->m_errorReporting.getErrors()[0].message); - EXPECT_EQ("Fatal error during loading of RamsesNodeBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getErrors()[1].message); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of NodeBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenNoRootInput) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenNoRootInput) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -891,22 +895,22 @@ namespace ramses::internal 1u), 0 // no root input ); - auto binding = rlogic_serialization::CreateRamsesNodeBinding( + auto binding = rlogic_serialization::CreateNodeBinding( m_flatBufferBuilder, base ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesNodeBinding from serialized data: missing root input!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of NodeBinding from serialized data: missing root input!"); } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) { { auto base = rlogic_serialization::CreateRamsesBinding( @@ -917,22 +921,22 @@ namespace ramses::internal 0, m_testUtils.serializeTestProperty("", rlogic_serialization::EPropertyRootType::Struct, false, true) // rootInput with errors ); - auto binding = rlogic_serialization::CreateRamsesNodeBinding( + auto binding = rlogic_serialization::CreateNodeBinding( m_flatBufferBuilder, base ); m_flatBufferBuilder.Finish(binding); } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenBoundNodeCannotBeResolved) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenBoundNodeCannotBeResolved) { const ramses::sceneObjectId_t mockObjectId{ 12 }; { @@ -948,7 +952,7 @@ namespace ramses::internal ramsesRef, m_testUtils.serializeTestProperty("") ); - auto binding = rlogic_serialization::CreateRamsesNodeBinding( + auto binding = rlogic_serialization::CreateNodeBinding( m_flatBufferBuilder, base ); @@ -957,13 +961,13 @@ namespace ramses::internal EXPECT_CALL(m_resolverMock, findRamsesNodeInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(nullptr)); - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); } - TEST_F(ARamsesNodeBinding_SerializationLifecycle, ErrorWhenSavedNodeTypeDoesNotMatchResolvedNodeType) + TEST_F(ANodeBinding_SerializationLifecycle, ErrorWhenSavedNodeTypeDoesNotMatchResolvedNodeType) { RamsesTestSetup ramses; ramses::Scene* scene = ramses.createScene(); @@ -984,7 +988,7 @@ namespace ramses::internal ramsesRef, m_testUtils.serializeTestProperty("") ); - auto binding = rlogic_serialization::CreateRamsesNodeBinding( + auto binding = rlogic_serialization::CreateNodeBinding( m_flatBufferBuilder, base ); @@ -994,43 +998,42 @@ namespace ramses::internal // resolver returns mesh node, but normal node is expected -> error EXPECT_CALL(m_resolverMock, findRamsesNodeInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(meshNode)); - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = RamsesNodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = NodeBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesNodeBinding from serialized data: loaded node type does not match referenced node type!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of NodeBinding from serialized data: loaded node type does not match referenced node type!"); } - // TODO Violin needs more tests here: - // - deserialized with wrong object type which is not compatible to node - // - deserialized with properties but without node, or the other way around - // - rotation type different than the one in ramses node - - class ARamsesNodeBinding_SerializationWithFile : public ARamsesNodeBinding + class ANodeBinding_SerializationWithFile : public ANodeBinding { - protected: - WithTempDirectory tempFolder; + public: + ANodeBinding_SerializationWithFile() + { + withTempDirectory(); + } }; - TEST_F(ARamsesNodeBinding_SerializationWithFile, ContainsItsDataAfterDeserialization) + TEST_F(ANodeBinding_SerializationWithFile, ContainsItsDataAfterDeserialization) { { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - RamsesNodeBinding& nodeBinding = *tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_YXZ, "NodeBinding"); + LogicEngine& tempEngineForSaving = *m_logicEngine; + NodeBinding& nodeBinding = *tempEngineForSaving.createNodeBinding(*m_node, ERotationType::Euler_YXZ, "NodeBinding"); nodeBinding.getInputs()->getChild("rotation")->set(vec3f{ 0.1f, 0.2f, 0.3f }); nodeBinding.getInputs()->getChild("translation")->set(vec3f{ 1.1f, 1.2f, 1.3f }); nodeBinding.getInputs()->getChild("scaling")->set(vec3f{ 2.1f, 2.2f, 2.3f }); nodeBinding.getInputs()->getChild("visibility")->set(true); nodeBinding.getInputs()->getChild("enabled")->set(true); tempEngineForSaving.update(); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "OneBinding.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("OneBinding.bin")); } { - EXPECT_TRUE(m_logicEngine.loadFromFile("OneBinding.bin", m_scene)); - const auto& nodeBinding = *m_logicEngine.findByName("NodeBinding"); + ASSERT_TRUE(recreateFromFile("OneBinding.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + const auto& nodeBinding = *m_logicEngine->findObject("NodeBinding"); EXPECT_EQ("NodeBinding", nodeBinding.getName()); - EXPECT_EQ(nodeBinding.getId(), 1u); + EXPECT_EQ(nodeBinding.getSceneObjectId().getValue(), 9u); const auto& inputs = nodeBinding.getInputs(); ASSERT_EQ(inputs->getChildCount(), 5u); @@ -1039,100 +1042,69 @@ namespace ramses::internal auto translation = inputs->getChild("translation"); auto scaling = inputs->getChild("scaling"); auto visibility = inputs->getChild("visibility"); - EXPECT_EQ(ramses::ERotationType::Euler_YXZ, nodeBinding.getRotationType()); + EXPECT_EQ(ERotationType::Euler_YXZ, nodeBinding.getRotationType()); ASSERT_NE(nullptr, rotation); EXPECT_EQ("rotation", rotation->getName()); EXPECT_EQ(EPropertyType::Vec3f, rotation->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, rotation->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, rotation->impl().getPropertySemantics()); EXPECT_EQ(*rotation->get(), vec3f(0.1f, 0.2f, 0.3f)); ASSERT_NE(nullptr, translation); EXPECT_EQ("translation", translation->getName()); EXPECT_EQ(EPropertyType::Vec3f, translation->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, translation->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, translation->impl().getPropertySemantics()); EXPECT_EQ(*translation->get(), vec3f(1.1f, 1.2f, 1.3f)); ASSERT_NE(nullptr, scaling); EXPECT_EQ("scaling", scaling->getName()); EXPECT_EQ(EPropertyType::Vec3f, scaling->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, scaling->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, scaling->impl().getPropertySemantics()); EXPECT_EQ(*scaling->get(), vec3f(2.1f, 2.2f, 2.3f)); ASSERT_NE(nullptr, visibility); EXPECT_EQ("visibility", visibility->getName()); EXPECT_EQ(EPropertyType::Bool, visibility->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, visibility->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, visibility->impl().getPropertySemantics()); EXPECT_TRUE(*visibility->get()); // Test that internal indices match properties resolved by name - EXPECT_EQ(rotation, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Rotation))); - EXPECT_EQ(scaling, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Scaling))); - EXPECT_EQ(translation, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Translation))); - EXPECT_EQ(visibility, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Visibility))); + EXPECT_EQ(rotation, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Rotation))); + EXPECT_EQ(scaling, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Scaling))); + EXPECT_EQ(translation, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Translation))); + EXPECT_EQ(visibility, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Visibility))); auto enabled = inputs->getChild("enabled"); EXPECT_EQ("enabled", enabled->getName()); EXPECT_EQ(EPropertyType::Bool, enabled->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, enabled->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, enabled->impl().getPropertySemantics()); EXPECT_TRUE(*enabled->get()); - EXPECT_EQ(enabled, inputs->m_impl->getChild(static_cast(ENodePropertyStaticIndex::Enabled))); + EXPECT_EQ(enabled, inputs->impl().getChild(static_cast(ENodePropertyStaticIndex::Enabled))); } } - TEST_F(ARamsesNodeBinding_SerializationWithFile, RestoresLinkToRamsesNodeAfterLoadingFromFile) + TEST_F(ANodeBinding_SerializationWithFile, RestoresLinkToRamsesNodeAfterLoadingFromFile) { { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "OneBinding.bin")); + LogicEngine& tempEngineForSaving = *m_logicEngine; + tempEngineForSaving.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + EXPECT_TRUE(saveToFileWithoutValidation("OneBinding.bin")); } { - EXPECT_TRUE(m_logicEngine.loadFromFile("OneBinding.bin", m_scene)); - const auto& nodeBinding = *m_logicEngine.findByName("NodeBinding"); + ASSERT_TRUE(recreateFromFile("OneBinding.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + const auto& nodeBinding = *m_logicEngine->findObject("NodeBinding"); EXPECT_EQ(&nodeBinding.getRamsesNode(), m_node); } } - TEST_F(ARamsesNodeBinding_SerializationWithFile, ProducesErrorWhenDeserializingFromFile_WhenHavingLinkToRamsesNode_ButNoSceneWasProvided) + TEST_F(ANodeBinding_SerializationWithFile, DoesNotModifyRamsesNodePropertiesAfterLoadingFromFile_WhenNoValuesWereExplicitlySetBeforeSaving) { { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "WithRamsesNode.bin")); - } - { - EXPECT_FALSE(m_logicEngine.loadFromFile("WithRamsesNode.bin")); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(errors.size(), 1u); - EXPECT_EQ(errors[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); + LogicEngine& tempEngineForSaving = *m_logicEngine; + tempEngineForSaving.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + EXPECT_TRUE(saveToFileWithoutValidation("NoValuesSet.bin")); } - } - - TEST_F(ARamsesNodeBinding_SerializationWithFile, ProducesErrorWhenDeserializingFromFile_WhenHavingLinkToRamsesNode_WhichWasDeleted) - { { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "RamsesNodeDeleted.bin")); - } - - m_scene->destroy(*m_node); - - { - EXPECT_FALSE(m_logicEngine.loadFromFile("RamsesNodeDeleted.bin", m_scene)); - auto errors = m_logicEngine.getErrors(); - ASSERT_EQ(errors.size(), 1u); - EXPECT_EQ(errors[0].message, "Fatal error during loading from file! Serialized Ramses Logic object 'NodeBinding' points to a Ramses object (id: 1) which couldn't be found in the provided scene!"); - } - } - - TEST_F(ARamsesNodeBinding_SerializationWithFile, DoesNotModifyRamsesNodePropertiesAfterLoadingFromFile_WhenNoValuesWereExplicitlySetBeforeSaving) - { - { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "NoValuesSet.bin")); - } - { - EXPECT_TRUE(m_logicEngine.loadFromFile("NoValuesSet.bin", m_scene)); - EXPECT_TRUE(m_logicEngine.update()); + ASSERT_TRUE(recreateFromFile("NoValuesSet.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + EXPECT_TRUE(m_logicEngine->update()); ExpectDefaultValues(*m_node); } @@ -1140,11 +1112,11 @@ namespace ramses::internal // Tests that the node properties don't overwrite ramses' values after loading from file, until // set() is called again explicitly after loadFromFile() - TEST_F(ARamsesNodeBinding_SerializationWithFile, DoesNotReapplyPropertiesToRamsesAfterLoading_UntilExplicitlySetAgain) + TEST_F(ANodeBinding_SerializationWithFile, DoesNotReapplyPropertiesToRamsesAfterLoading_UntilExplicitlySetAgain) { { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; - RamsesNodeBinding& nodeBinding = *tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + LogicEngine& tempEngineForSaving = *m_logicEngine; + NodeBinding& nodeBinding = *tempEngineForSaving.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); // Set some values to the binding's inputs nodeBinding.getInputs()->getChild("translation")->set(vec3f{ 1.1f, 1.2f, 1.3f }); nodeBinding.getInputs()->getChild("rotation")->set(vec3f{ 2.1f, 2.2f, 2.3f }); @@ -1152,35 +1124,36 @@ namespace ramses::internal nodeBinding.getInputs()->getChild("visibility")->set(true); nodeBinding.getInputs()->getChild("enabled")->set(false); tempEngineForSaving.update(); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "AllValuesSet.bin")); - } - // Set properties to other values to check if they are overwritten after load - m_node->setTranslation({100.f, 100.f, 100.f}); - m_node->setRotation({100.f, 100.f, 100.f}, ramses::ERotationType::Euler_ZYX); - m_node->setScaling({100.f, 100.f, 100.f}); - m_node->setVisibility(ramses::EVisibilityMode::Invisible); + // Set properties to other values to check if they are overwritten after load + m_node->setTranslation({100.f, 100.f, 100.f}); + m_node->setRotation({100.f, 100.f, 100.f}, ERotationType::Euler_ZYX); + m_node->setScaling({100.f, 100.f, 100.f}); + m_node->setVisibility(EVisibilityMode::Invisible); - { - EXPECT_TRUE(m_logicEngine.loadFromFile("AllValuesSet.bin", m_scene)); + EXPECT_TRUE(saveToFileWithoutValidation("AllValuesSet.bin")); + } - EXPECT_TRUE(m_logicEngine.update()); + { + ASSERT_TRUE(recreateFromFile("AllValuesSet.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + EXPECT_TRUE(m_logicEngine->update()); // Node binding does not re-apply its values to ramses node ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 100.f, 100.f, 100.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 100.f, 100.f, 100.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 100.f, 100.f, 100.f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); // Set only scaling. Use the same value as before save on purpose! Calling set forces set on ramses - m_logicEngine.findByName("NodeBinding")->getInputs()->getChild("scaling")->set(vec3f{ 3.1f, 3.2f, 3.3f }); - EXPECT_TRUE(m_logicEngine.update()); + m_logicEngine->findObject("NodeBinding")->getInputs()->getChild("scaling")->set(vec3f{ 3.1f, 3.2f, 3.3f }); + EXPECT_TRUE(m_logicEngine->update()); // Only scaling changed, the rest is unchanged ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 100.f, 100.f, 100.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 100.f, 100.f, 100.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 3.1f, 3.2f, 3.3f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); } } @@ -1189,14 +1162,14 @@ namespace ramses::internal // - saving and loading files // The general expectation is that after loading + update(), the logic scene would overwrite only ramses // properties wrapped by a LogicBinding which is linked to a script - TEST_F(ARamsesNodeBinding_SerializationWithFile, SetsOnlyRamsesNodePropertiesForWhichTheBindingInputIsLinked_AfterLoadingFromFile_AndCallingUpdate) + TEST_F(ANodeBinding_SerializationWithFile, SetsOnlyRamsesNodePropertiesForWhichTheBindingInputIsLinked_AfterLoadingFromFile_AndCallingUpdate) { // These values should not be overwritten by logic on update() m_node->setScaling({22, 33, 44}); m_node->setTranslation({100, 200, 300}); { - LogicEngine tempEngineForSaving{ m_logicEngine.getFeatureLevel() }; + LogicEngine& tempEngineForSaving = *m_logicEngine; const std::string_view scriptSrc = R"( function interface(IN,OUT) @@ -1211,64 +1184,124 @@ namespace ramses::internal LuaScript* script = tempEngineForSaving.createLuaScript(scriptSrc); - RamsesNodeBinding& nodeBinding = *tempEngineForSaving.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *tempEngineForSaving.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); ASSERT_TRUE(tempEngineForSaving.link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); ASSERT_TRUE(tempEngineForSaving.link(*script->getOutputs()->getChild("visibility"), *nodeBinding.getInputs()->getChild("visibility"))); tempEngineForSaving.update(); - EXPECT_TRUE(SaveToFileWithoutValidation(tempEngineForSaving, "SomeInputsLinked.bin")); + EXPECT_TRUE(saveToFileWithoutValidation("SomeInputsLinked.bin")); } // Modify 'linked' properties before loading to check if logic will overwrite them after load + update - m_node->setRotation({100, 100, 100}, ramses::ERotationType::Euler_ZYX); - m_node->setVisibility(ramses::EVisibilityMode::Visible); + m_node->setRotation({100, 100, 100}, ERotationType::Euler_ZYX); + m_node->setVisibility(EVisibilityMode::Visible); { - EXPECT_TRUE(m_logicEngine.loadFromFile("SomeInputsLinked.bin", m_scene)); + ASSERT_TRUE(recreateFromFile("SomeInputsLinked.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // Translation and Scaling were not linked -> their values are not modified ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, { 100.f, 200.f, 300.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, { 22.f, 33.f, 44.f }); // Rotation and visibility are linked -> values were updated ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 2.f, 3.f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); // Manually setting values on ramses followed by a logic update has no effect // Logic is not "dirty" and it doesn't know it needs to update ramses - m_node->setRotation({1, 2, 3}, ramses::ERotationType::Euler_ZYX); - EXPECT_TRUE(m_logicEngine.update()); + m_node->setRotation({1, 2, 3}, ERotationType::Euler_ZYX); + EXPECT_TRUE(m_logicEngine->update()); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 2.f, 3.f }); } } + TEST_F(ANodeBinding_SerializationWithFile, deserializedNodeHasCorrectVisibilityMode) + { + for (bool enabled : {false, true}) + { + for (bool visibility : {false, true}) + { + const EVisibilityMode expectedMode = (enabled ? (visibility ? EVisibilityMode::Visible : EVisibilityMode::Invisible) : EVisibilityMode::Off); + + { + Scene* scene = m_ramses.createScene(sceneId_t{ 666 }); + Node* node = scene->createNode("node"); + LogicEngine* logic = scene->createLogicEngine("logic"); + NodeBinding* nodeBinding = logic->createNodeBinding(*node, ERotationType::Euler_YXZ, "nodeBinding"); + + Property* prop_enabled{ nodeBinding->getInputs()->getChild("enabled") }; + ASSERT_TRUE(prop_enabled != nullptr); + + Property* prop_visibility{ nodeBinding->getInputs()->getChild("visibility") }; + ASSERT_TRUE(prop_visibility != nullptr); + + EXPECT_TRUE(prop_enabled->set(enabled)); + EXPECT_TRUE(prop_visibility->set(visibility)); + EXPECT_TRUE(logic->update()); + + EXPECT_EQ(node->getVisibility(), expectedMode); + EXPECT_EQ(*prop_enabled->get(), enabled); + EXPECT_EQ(*prop_visibility->get(), visibility); + + SaveFileConfig saveConfig; + saveConfig.setValidationEnabled(false); + EXPECT_TRUE(scene->saveToFile("visibility.ramses", saveConfig)); + m_ramses.destroyScene(*scene); + } + + { + Scene& scene = m_ramses.loadSceneFromFile("visibility.ramses"); + auto logic = scene.findObject("logic"); + ASSERT_TRUE(logic); + auto node = scene.findObject("node"); + ASSERT_TRUE(node); + + auto nodeBinding = logic->findObject("nodeBinding"); + ASSERT_TRUE(nodeBinding); + + const Property* prop_enabled{ nodeBinding->getInputs()->getChild("enabled") }; + ASSERT_TRUE(prop_enabled != nullptr); + + const Property* prop_visibility{ nodeBinding->getInputs()->getChild("visibility") }; + ASSERT_TRUE(prop_visibility != nullptr); + + EXPECT_EQ(node->getVisibility(), expectedMode); + EXPECT_EQ(*prop_enabled->get(), enabled); + EXPECT_EQ(*prop_visibility->get(), visibility) << fmt::format(" enabled / visibility = {} / {}", enabled, visibility); + m_ramses.destroyScene(scene); + } + } + } + } + // Larger confidence tests which verify and document the entire data flow cycle of bindings // There are smaller tests which test only properties and their data propagation rules (see property unit tests) // There are also "dirtiness" tests which test when a node is being re-updated (see logic engine dirtiness tests) // These tests test everything in combination - class ARamsesNodeBinding_DataFlow : public ARamsesNodeBinding + class ANodeBinding_DataFlow : public ANodeBinding { }; - TEST_F(ARamsesNodeBinding_DataFlow, WithExplicitSet) + TEST_F(ANodeBinding_DataFlow, WithExplicitSet) { // Create node and preset values - m_node->setRotation({1.f, 1.f, 1.f}, ramses::ERotationType::Euler_ZYX); + m_node->setRotation({1.f, 1.f, 1.f}, ERotationType::Euler_ZYX); m_node->setScaling({2.f, 2.f, 2.f}); m_node->setTranslation({3.f, 3.f, 3.f}); - m_node->setVisibility(ramses::EVisibilityMode::Invisible); + m_node->setVisibility(EVisibilityMode::Invisible); - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); - m_logicEngine.update(); + m_logicEngine->update(); // Nothing happened - binding did not overwrite preset values because no user value set() ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 1.f, 1.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 2.f, 2.f, 2.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 3.f, 3.f, 3.f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); // Set rotation only Property* inputs = nodeBinding.getInputs(); @@ -1278,15 +1311,15 @@ namespace ramses::internal ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 1.f, 1.f }); // Update() only propagates rotation and does not touch other data - m_logicEngine.update(); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 42.f, 42.f, 42.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 2.f, 2.f, 2.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 3.f, 3.f, 3.f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Invisible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Invisible); // Calling update again does not "rewrite" the data to ramses. Check this by setting a value manually and call update() again - m_node->setRotation({1.f, 1.f, 1.f}, ramses::ERotationType::Euler_ZYX); - m_logicEngine.update(); + m_node->setRotation({1.f, 1.f, 1.f}, ERotationType::Euler_ZYX); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 1.f, 1.f }); // Set all properties manually this time @@ -1295,22 +1328,22 @@ namespace ramses::internal inputs->getChild("translation")->set(vec3f{ 300.f, 300.f, 300.f }); inputs->getChild("visibility")->set(true); inputs->getChild("enabled")->set(true); - m_logicEngine.update(); + m_logicEngine->update(); // All of the property values were passed to ramses ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 100.f, 100.f, 100.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Scaling, vec3f{ 200.f, 200.f, 200.f }); ExpectValues(*m_node, ENodePropertyStaticIndex::Translation, vec3f{ 300.f, 300.f, 300.f }); - EXPECT_EQ(m_node->getVisibility(), ramses::EVisibilityMode::Visible); + EXPECT_EQ(m_node->getVisibility(), EVisibilityMode::Visible); } - TEST_F(ARamsesNodeBinding_DataFlow, WithLinks) + TEST_F(ANodeBinding_DataFlow, WithLinks) { // Create node and preset values - m_node->setRotation({1.f, 1.f, 1.f}, ramses::ERotationType::Euler_ZYX); + m_node->setRotation({1.f, 1.f, 1.f}, ERotationType::Euler_ZYX); m_node->setScaling({2.f, 2.f, 2.f}); m_node->setTranslation({3.f, 3.f, 3.f}); - m_node->setVisibility(ramses::EVisibilityMode::Off); + m_node->setVisibility(EVisibilityMode::Off); const std::string_view scriptSrc = R"( function interface(IN,OUT) @@ -1322,35 +1355,35 @@ namespace ramses::internal end )"; - LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); script->getInputs()->getChild("rotation")->set({ 1.f, 2.f, 3.f }); - RamsesNodeBinding& nodeBinding = *m_logicEngine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); + NodeBinding& nodeBinding = *m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); // Adding and removing link does not set anything in ramses - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); - ASSERT_TRUE(m_logicEngine.unlink(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); - m_logicEngine.update(); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); + ASSERT_TRUE(m_logicEngine->unlink(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 1.f, 1.f }); // Create link and calling update -> sets values to ramses - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); - m_logicEngine.update(); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 1.f, 2.f, 3.f }); // Link does not overwrite manually set value ... - m_node->setRotation({100.f, 100.f, 100.f}, ramses::ERotationType::Euler_ZYX); - m_logicEngine.update(); + m_node->setRotation({100.f, 100.f, 100.f}, ERotationType::Euler_ZYX); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 100.f, 100.f, 100.f }); // ... until the linked script is re-executed and provides a new value script->getInputs()->getChild("rotation")->set({11.f, 12.f, 13.f}); - m_logicEngine.update(); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 11.f, 12.f, 13.f }); // Remove link -> value is not overwritten any more - ASSERT_TRUE(m_logicEngine.unlink(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); - m_node->setRotation({100.f, 100.f, 100.f}, ramses::ERotationType::Euler_ZYX); - m_logicEngine.update(); + ASSERT_TRUE(m_logicEngine->unlink(*script->getOutputs()->getChild("rotation"), *nodeBinding.getInputs()->getChild("rotation"))); + m_node->setRotation({100.f, 100.f, 100.f}, ERotationType::Euler_ZYX); + m_logicEngine->update(); ExpectValues(*m_node, ENodePropertyStaticIndex::Rotation, vec3f{ 100.f, 100.f, 100.f }); } } diff --git a/client/logic/unittests/api/PropertyTest.cpp b/tests/unittests/client/logic/api/PropertyTest.cpp similarity index 84% rename from client/logic/unittests/api/PropertyTest.cpp rename to tests/unittests/client/logic/api/PropertyTest.cpp index 54e7f615d..b93cc83a3 100644 --- a/client/logic/unittests/api/PropertyTest.cpp +++ b/tests/unittests/client/logic/api/PropertyTest.cpp @@ -8,31 +8,29 @@ #include "gtest/gtest.h" -#include "impl/PropertyImpl.h" -#include "impl/LuaScriptImpl.h" -#include "internals/SolState.h" -#include "internals/ErrorReporting.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "internal/logic/SolState.h" +#include "impl/ErrorReporting.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LuaScript.h" #include "flatbuffers/flatbuffers.h" -#include "generated/PropertyGen.h" +#include "internal/logic/flatbuffers/generated/PropertyGen.h" -#include "LogicNodeDummy.h" #include "LogTestUtils.h" +#include "LogicEngineTest_Base.h" #include #include namespace ramses::internal { - class AProperty : public ::testing::Test + class AProperty : public ALogicEngine { public: - LogicNodeDummyImpl m_dummyNode{ "DummyNode" }; - PropertyUniquePtr CreateInputProperty(EPropertyType type, bool assignDummyLogicNode = true) { return CreateProperty(MakeType("", type), EPropertySemantics::ScriptInput, assignDummyLogicNode); @@ -60,7 +58,9 @@ namespace ramses::internal } // Silence logs, unless explicitly enabled, to reduce spam and speed up tests - ScopedLogContextLevel m_silenceLogs{ELogLevel::Off}; + ScopedLogContextLevel m_silenceLogs{CONTEXT_CLIENT, ELogLevel::Off}; + + LogicNodeImpl& m_dummyNode{ m_logicEngine->createLuaScript(m_valid_empty_script)->impl() }; }; TEST_F(AProperty, HasANameAfterCreation) @@ -78,7 +78,7 @@ namespace ramses::internal TEST_F(AProperty, HasAReferenceFromImplToHLObject) { auto property(CreateInputProperty(EPropertyType::Float)); - auto& impl = *property->m_impl; + auto& impl = property->impl(); EXPECT_EQ(property.get(), &impl.getPropertyInstance()); const auto& constImpl = impl; EXPECT_EQ(property.get(), &constImpl.getPropertyInstance()); @@ -106,15 +106,15 @@ namespace ramses::internal { auto property1(CreateOutputProperty(EPropertyType::Float)); auto property2(CreateInputProperty(EPropertyType::Float)); - property2->m_impl->setIncomingLink(*property1->m_impl, false); - EXPECT_FALSE(property1->m_impl->isInput()); - EXPECT_TRUE(property1->m_impl->isOutput()); + property2->impl().setIncomingLink(property1->impl(), false); + EXPECT_FALSE(property1->impl().isInput()); + EXPECT_TRUE(property1->impl().isOutput()); EXPECT_TRUE(property1->isLinked()); EXPECT_TRUE(property1->hasOutgoingLink()); EXPECT_FALSE(property1->hasIncomingLink()); EXPECT_FALSE(property1->getIncomingLink()); - EXPECT_TRUE(property2->m_impl->isInput()); - EXPECT_FALSE(property2->m_impl->isOutput()); + EXPECT_TRUE(property2->impl().isInput()); + EXPECT_FALSE(property2->impl().isOutput()); EXPECT_TRUE(property2->isLinked()); EXPECT_TRUE(property2->hasIncomingLink()); EXPECT_FALSE(property2->hasOutgoingLink()); @@ -128,7 +128,7 @@ namespace ramses::internal EXPECT_EQ(property1->getOutgoingLink(0u)->target, property2.get()); EXPECT_FALSE(property1->getOutgoingLink(0u)->isWeakLink); - property2->m_impl->resetIncomingLink(); + property2->impl().resetIncomingLink(); EXPECT_FALSE(property1->isLinked()); EXPECT_FALSE(property2->isLinked()); EXPECT_FALSE(property1->getIncomingLink()); @@ -137,18 +137,38 @@ namespace ramses::internal EXPECT_EQ(0u, property2->getOutgoingLinksCount()); } + TEST_F(AProperty, CanGetLinksConst) + { + auto p1(CreateOutputProperty(EPropertyType::Float)); + auto p2(CreateInputProperty(EPropertyType::Float)); + p2->impl().setIncomingLink(p1->impl(), false); + const Property* property1 = p1.get(); + const Property* property2 = p2.get(); + EXPECT_FALSE(property1->impl().isInput()); + EXPECT_TRUE(property1->impl().isOutput()); + EXPECT_FALSE(property1->getIncomingLink()); + ASSERT_TRUE(property2->getIncomingLink()); + EXPECT_EQ(property2->getIncomingLink()->source, property1); + EXPECT_EQ(property2->getIncomingLink()->target, property2); + EXPECT_FALSE(property2->getIncomingLink()->isWeakLink); + ASSERT_TRUE(property1->getOutgoingLink(0u)); + EXPECT_EQ(property1->getOutgoingLink(0u)->source, property1); + EXPECT_EQ(property1->getOutgoingLink(0u)->target, property2); + EXPECT_FALSE(property1->getOutgoingLink(0u)->isWeakLink); + } + TEST_F(AProperty, CanLinkInterfaceProperties) { auto property1(CreateProperty(MakeType("name", EPropertyType::Float), EPropertySemantics::Interface, true)); auto property2(CreateProperty(MakeType("name", EPropertyType::Float), EPropertySemantics::Interface, true)); - property2->m_impl->setIncomingLink(*property1->m_impl, false); - EXPECT_TRUE(property1->m_impl->isInput()); - EXPECT_TRUE(property1->m_impl->isOutput()); + property2->impl().setIncomingLink(property1->impl(), false); + EXPECT_TRUE(property1->impl().isInput()); + EXPECT_TRUE(property1->impl().isOutput()); EXPECT_TRUE(property1->isLinked()); EXPECT_TRUE(property1->hasOutgoingLink()); EXPECT_FALSE(property1->hasIncomingLink()); - EXPECT_TRUE(property2->m_impl->isInput()); - EXPECT_TRUE(property2->m_impl->isOutput()); + EXPECT_TRUE(property2->impl().isInput()); + EXPECT_TRUE(property2->impl().isOutput()); EXPECT_TRUE(property2->isLinked()); EXPECT_TRUE(property2->hasIncomingLink()); EXPECT_FALSE(property2->hasOutgoingLink()); @@ -158,15 +178,15 @@ namespace ramses::internal { auto property1(CreateOutputProperty(EPropertyType::Float)); auto property2(CreateInputProperty(EPropertyType::Float)); - property2->m_impl->setIncomingLink(*property1->m_impl, true); - EXPECT_FALSE(property1->m_impl->isInput()); - EXPECT_TRUE(property1->m_impl->isOutput()); + property2->impl().setIncomingLink(property1->impl(), true); + EXPECT_FALSE(property1->impl().isInput()); + EXPECT_TRUE(property1->impl().isOutput()); EXPECT_TRUE(property1->isLinked()); EXPECT_TRUE(property1->hasOutgoingLink()); EXPECT_FALSE(property1->hasIncomingLink()); EXPECT_FALSE(property1->getIncomingLink()); - EXPECT_TRUE(property2->m_impl->isInput()); - EXPECT_FALSE(property2->m_impl->isOutput()); + EXPECT_TRUE(property2->impl().isInput()); + EXPECT_FALSE(property2->impl().isOutput()); EXPECT_TRUE(property2->isLinked()); EXPECT_TRUE(property2->hasIncomingLink()); EXPECT_FALSE(property2->hasOutgoingLink()); @@ -180,7 +200,7 @@ namespace ramses::internal EXPECT_EQ(property1->getOutgoingLink(0u)->target, property2.get()); EXPECT_TRUE(property1->getOutgoingLink(0u)->isWeakLink); - property2->m_impl->resetIncomingLink(); + property2->impl().resetIncomingLink(); EXPECT_FALSE(property1->isLinked()); EXPECT_FALSE(property2->isLinked()); EXPECT_FALSE(property1->getIncomingLink()); @@ -194,8 +214,8 @@ namespace ramses::internal auto property1(CreateOutputProperty(EPropertyType::Float)); auto property2(CreateInputProperty(EPropertyType::Float)); auto property3(CreateInputProperty(EPropertyType::Float)); - property2->m_impl->setIncomingLink(*property1->m_impl, true); - property3->m_impl->setIncomingLink(*property1->m_impl, false); + property2->impl().setIncomingLink(property1->impl(), true); + property3->impl().setIncomingLink(property1->impl(), false); ASSERT_EQ(2u, property1->getOutgoingLinksCount()); // link 1 @@ -235,20 +255,20 @@ namespace ramses::internal { auto prop(CreateBindingInput(EPropertyType::Float)); - EXPECT_FALSE(prop->m_impl->bindingInputHasNewValue()); - EXPECT_FALSE(prop->m_impl->checkForBindingInputNewValueAndReset()); + EXPECT_FALSE(prop->impl().bindingInputHasNewValue()); + EXPECT_FALSE(prop->impl().checkForBindingInputNewValueAndReset()); } TEST_F(AProperty, BindingInputHasUserValueAfterSetIsCalledSuccessfully) { auto prop(CreateBindingInput(EPropertyType::Float)); - EXPECT_FALSE(prop->m_impl->bindingInputHasNewValue()); + EXPECT_FALSE(prop->impl().bindingInputHasNewValue()); // Set with wrong type (failed sets) have no effect on user value status EXPECT_FALSE(prop->set(5)); - EXPECT_FALSE(prop->m_impl->bindingInputHasNewValue()); + EXPECT_FALSE(prop->impl().bindingInputHasNewValue()); EXPECT_TRUE(prop->set(0.5f)); - EXPECT_TRUE(prop->m_impl->checkForBindingInputNewValueAndReset()); + EXPECT_TRUE(prop->impl().checkForBindingInputNewValueAndReset()); } TEST_F(AProperty, BindingInputHasUserValueAfterLinkIsActivated_andValueChanged) @@ -257,11 +277,11 @@ namespace ramses::internal auto linkSource(CreateProperty(MakeType("", EPropertyType::Float), EPropertySemantics::ScriptOutput, true)); // Set to different than default value - linkSource->m_impl->setValue({0.5f}); + linkSource->impl().setValue({0.5f}); // Simulate link behavior - linkTarget->m_impl->setValue(linkSource->m_impl->getValue()); - EXPECT_TRUE(linkTarget->m_impl->checkForBindingInputNewValueAndReset()); + linkTarget->impl().setValue(linkSource->impl().getValue()); + EXPECT_TRUE(linkTarget->impl().checkForBindingInputNewValueAndReset()); } TEST_F(AProperty, BindingInputHasNewUserValueAfterLinkIsActivated_whenNewValueSameAsOldValue) @@ -274,19 +294,19 @@ namespace ramses::internal linkTarget->set(.5f); // Simulate link behavior - linkTarget->m_impl->setValue(linkSource->m_impl->getValue()); - EXPECT_TRUE(linkTarget->m_impl->checkForBindingInputNewValueAndReset()); + linkTarget->impl().setValue(linkSource->impl().getValue()); + EXPECT_TRUE(linkTarget->impl().checkForBindingInputNewValueAndReset()); } TEST_F(AProperty, BindingInputHasNoUserValueAnymore_WhenConsumed) { auto prop(CreateBindingInput(EPropertyType::Float)); - ASSERT_FALSE(prop->m_impl->bindingInputHasNewValue()); + ASSERT_FALSE(prop->impl().bindingInputHasNewValue()); EXPECT_TRUE(prop->set(0.5f)); // Consume value => has no value any more - EXPECT_TRUE(prop->m_impl->checkForBindingInputNewValueAndReset()); - EXPECT_FALSE(prop->m_impl->bindingInputHasNewValue()); + EXPECT_TRUE(prop->impl().checkForBindingInputNewValueAndReset()); + EXPECT_FALSE(prop->impl().bindingInputHasNewValue()); } TEST_F(AProperty, DoesntHaveChildrenAfterCreation) @@ -454,20 +474,20 @@ namespace ramses::internal auto inputProperty(CreateInputProperty(EPropertyType::Float)); auto outputProperty(CreateOutputProperty(EPropertyType::Int32)); - EXPECT_TRUE(inputProperty->m_impl->isInput()); - EXPECT_FALSE(inputProperty->m_impl->isOutput()); - EXPECT_EQ(internal::EPropertySemantics::ScriptInput, inputProperty->m_impl->getPropertySemantics()); - EXPECT_TRUE(outputProperty->m_impl->isOutput()); - EXPECT_FALSE(outputProperty->m_impl->isInput()); - EXPECT_EQ(internal::EPropertySemantics::ScriptOutput, outputProperty->m_impl->getPropertySemantics()); + EXPECT_TRUE(inputProperty->impl().isInput()); + EXPECT_FALSE(inputProperty->impl().isOutput()); + EXPECT_EQ(EPropertySemantics::ScriptInput, inputProperty->impl().getPropertySemantics()); + EXPECT_TRUE(outputProperty->impl().isOutput()); + EXPECT_FALSE(outputProperty->impl().isInput()); + EXPECT_EQ(EPropertySemantics::ScriptOutput, outputProperty->impl().getPropertySemantics()); } TEST_F(AProperty, CannotSetOutputManually) { auto outputProperty(CreateOutputProperty(EPropertyType::Int32)); - EXPECT_TRUE(outputProperty->m_impl->isOutput()); - EXPECT_EQ(internal::EPropertySemantics::ScriptOutput, outputProperty->m_impl->getPropertySemantics()); + EXPECT_TRUE(outputProperty->impl().isOutput()); + EXPECT_EQ(EPropertySemantics::ScriptOutput, outputProperty->impl().getPropertySemantics()); EXPECT_FALSE(outputProperty->set(45)); } @@ -683,7 +703,7 @@ namespace ramses::internal ErrorReporting m_errorReporting; flatbuffers::FlatBufferBuilder m_flatBufferBuilder; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_dummyNode.getSceneImpl() }; }; TEST_F(AProperty_SerializationLifecycle, StructWithoutChildren) @@ -818,7 +838,7 @@ namespace ramses::internal propVec3i->set({3, 4, 5}); propVec4i->set({6, 7, 8, 9}); - (void)PropertyImpl::Serialize(*rootImpl->m_impl, m_flatBufferBuilder, m_serializationMap); + (void)PropertyImpl::Serialize(rootImpl->impl(), m_flatBufferBuilder, m_serializationMap); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); @@ -870,7 +890,7 @@ namespace ramses::internal EXPECT_EQ(expectedValueVec2i, *propVec2i->get()); EXPECT_EQ(expectedValueVec3i, *propVec3i->get()); EXPECT_EQ(expectedValueVec4i, *propVec4i->get()); - EXPECT_FALSE(propDefValue->m_impl->checkForBindingInputNewValueAndReset()); + EXPECT_FALSE(propDefValue->impl().checkForBindingInputNewValueAndReset()); } TEST_F(AProperty_SerializationLifecycle, ErrorWhenNameMissing) @@ -887,8 +907,8 @@ namespace ramses::internal std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); } TEST_F(AProperty_SerializationLifecycle, ErrorWhenTypeCorrupted) @@ -908,8 +928,8 @@ namespace ramses::internal std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: invalid type!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: invalid type!"); } TEST_F(AProperty_SerializationLifecycle, ErrorWhenChildHasErrors) @@ -934,8 +954,8 @@ namespace ramses::internal std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: missing name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); } TEST_F(AProperty_SerializationLifecycle, ErrorWhenComplexTypeHasNoChildInfo) @@ -955,8 +975,8 @@ namespace ramses::internal std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: complex type has no child type info!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: complex type has no child type info!"); } TEST_F(AProperty_SerializationLifecycle, ErrorWhenMissingValueForPrimitiveType_AllUnionTypes) @@ -977,12 +997,12 @@ namespace ramses::internal } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - m_errorReporting.clear(); + m_errorReporting.reset(); std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: invalid union!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: invalid union!"); } } @@ -1005,8 +1025,8 @@ namespace ramses::internal std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, m_errorReporting, m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: invalid type!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: invalid type!"); } // two element type @@ -1049,8 +1069,8 @@ namespace ramses::internal std::unique_ptr deserialized = PropertyImpl::Deserialize(serialized, EPropertySemantics::ScriptInput, this->m_errorReporting, this->m_deserializationMap); EXPECT_FALSE(deserialized); - ASSERT_EQ(this->m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(this->m_errorReporting.getErrors()[0].message, "Fatal error during loading of Property from serialized data: invalid type!"); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ(this->m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: invalid type!"); } TEST_F(AProperty, DoesNotSetLogicNodeToDirtyIfValueIsNotChanged) @@ -1062,33 +1082,33 @@ namespace ramses::internal auto vec3iProperty = CreateInputProperty(EPropertyType::Vec3i); auto stringProperty = CreateInputProperty(EPropertyType::String); - int32Property->m_impl->setValue(42); - int64Property->m_impl->setValue(int64_t{ 421 }); - floatProperty->m_impl->setValue(42.f); - vec2fProperty->m_impl->setValue(vec2f{4.f, 2.f}); - vec3iProperty->m_impl->setValue(vec3i{4, 2, 3}); - stringProperty->m_impl->setValue(std::string("42")); - - int32Property->m_impl->getLogicNode().setDirty(false); - int64Property->m_impl->getLogicNode().setDirty(false); - floatProperty->m_impl->getLogicNode().setDirty(false); - vec2fProperty->m_impl->getLogicNode().setDirty(false); - vec3iProperty->m_impl->getLogicNode().setDirty(false); - stringProperty->m_impl->getLogicNode().setDirty(false); - - int32Property->m_impl->setValue(42); - int64Property->m_impl->setValue(int64_t{ 421 }); - floatProperty->m_impl->setValue(42.f); - vec2fProperty->m_impl->setValue(vec2f{ 4.f, 2.f }); - vec3iProperty->m_impl->setValue(vec3i{ 4, 2, 3 }); - stringProperty->m_impl->setValue(std::string("42")); - - EXPECT_FALSE(int32Property->m_impl->getLogicNode().isDirty()); - EXPECT_FALSE(int64Property->m_impl->getLogicNode().isDirty()); - EXPECT_FALSE(floatProperty->m_impl->getLogicNode().isDirty()); - EXPECT_FALSE(vec2fProperty->m_impl->getLogicNode().isDirty()); - EXPECT_FALSE(vec3iProperty->m_impl->getLogicNode().isDirty()); - EXPECT_FALSE(stringProperty->m_impl->getLogicNode().isDirty()); + int32Property->impl().setValue(42); + int64Property->impl().setValue(int64_t{ 421 }); + floatProperty->impl().setValue(42.f); + vec2fProperty->impl().setValue(vec2f{4.f, 2.f}); + vec3iProperty->impl().setValue(vec3i{4, 2, 3}); + stringProperty->impl().setValue(std::string("42")); + + int32Property->impl().getLogicNode().setDirty(false); + int64Property->impl().getLogicNode().setDirty(false); + floatProperty->impl().getLogicNode().setDirty(false); + vec2fProperty->impl().getLogicNode().setDirty(false); + vec3iProperty->impl().getLogicNode().setDirty(false); + stringProperty->impl().getLogicNode().setDirty(false); + + int32Property->impl().setValue(42); + int64Property->impl().setValue(int64_t{ 421 }); + floatProperty->impl().setValue(42.f); + vec2fProperty->impl().setValue(vec2f{ 4.f, 2.f }); + vec3iProperty->impl().setValue(vec3i{ 4, 2, 3 }); + stringProperty->impl().setValue(std::string("42")); + + EXPECT_FALSE(int32Property->impl().getLogicNode().isDirty()); + EXPECT_FALSE(int64Property->impl().getLogicNode().isDirty()); + EXPECT_FALSE(floatProperty->impl().getLogicNode().isDirty()); + EXPECT_FALSE(vec2fProperty->impl().getLogicNode().isDirty()); + EXPECT_FALSE(vec3iProperty->impl().getLogicNode().isDirty()); + EXPECT_FALSE(stringProperty->impl().getLogicNode().isDirty()); } TEST_F(AProperty, SetsLogicNodeToDirtyIfValueIsChanged) @@ -1100,19 +1120,19 @@ namespace ramses::internal auto vec3iProperty = CreateInputProperty(EPropertyType::Vec3i); auto stringProperty = CreateInputProperty(EPropertyType::String); - int32Property->m_impl->setValue(42); - int64Property->m_impl->setValue(int64_t{ 421 }); - floatProperty->m_impl->setValue(42.f); - vec2fProperty->m_impl->setValue(vec2f{4.f, 2.f}); - vec3iProperty->m_impl->setValue(vec3i{4, 2, 3}); - stringProperty->m_impl->setValue(std::string("42")); - - EXPECT_TRUE(int32Property->m_impl->getLogicNode().isDirty()); - EXPECT_TRUE(int64Property->m_impl->getLogicNode().isDirty()); - EXPECT_TRUE(floatProperty->m_impl->getLogicNode().isDirty()); - EXPECT_TRUE(vec2fProperty->m_impl->getLogicNode().isDirty()); - EXPECT_TRUE(vec3iProperty->m_impl->getLogicNode().isDirty()); - EXPECT_TRUE(stringProperty->m_impl->getLogicNode().isDirty()); + int32Property->impl().setValue(42); + int64Property->impl().setValue(int64_t{ 421 }); + floatProperty->impl().setValue(42.f); + vec2fProperty->impl().setValue(vec2f{4.f, 2.f}); + vec3iProperty->impl().setValue(vec3i{4, 2, 3}); + stringProperty->impl().setValue(std::string("42")); + + EXPECT_TRUE(int32Property->impl().getLogicNode().isDirty()); + EXPECT_TRUE(int64Property->impl().getLogicNode().isDirty()); + EXPECT_TRUE(floatProperty->impl().getLogicNode().isDirty()); + EXPECT_TRUE(vec2fProperty->impl().getLogicNode().isDirty()); + EXPECT_TRUE(vec3iProperty->impl().getLogicNode().isDirty()); + EXPECT_TRUE(stringProperty->impl().getLogicNode().isDirty()); } TEST_F(AProperty, FailsToSetINT64ValueThatCannotBeRepresentedInLua) @@ -1121,7 +1141,7 @@ namespace ramses::internal ELogLevel logType{ ELogLevel::Info }; std::string logMessage; - ScopedLogContextLevel logCollector{ ELogLevel::Error, [&](ELogLevel type, std::string_view message) + ScopedLogContextLevel logCollector{CONTEXT_CLIENT, ELogLevel::Error, [&](ELogLevel type, std::string_view message) { logType = type; logMessage = message; diff --git a/client/logic/unittests/api/PropertyTypeTest.cpp b/tests/unittests/client/logic/api/PropertyTypeTest.cpp similarity index 99% rename from client/logic/unittests/api/PropertyTypeTest.cpp rename to tests/unittests/client/logic/api/PropertyTypeTest.cpp index 954fbfd6e..827c18fc2 100644 --- a/client/logic/unittests/api/PropertyTypeTest.cpp +++ b/tests/unittests/client/logic/api/PropertyTypeTest.cpp @@ -8,9 +8,9 @@ #include "gtest/gtest.h" -#include "ramses-logic/EPropertyType.h" +#include "ramses/client/logic/EPropertyType.h" -namespace ramses +namespace ramses::internal { TEST(PropertyTypeToEnumTypeTrait, ConvertsSupportedTypesToCorrectEnum) { diff --git a/tests/unittests/client/logic/api/RenderGroupBindingElementsTest.cpp b/tests/unittests/client/logic/api/RenderGroupBindingElementsTest.cpp new file mode 100644 index 000000000..f5a1fb95f --- /dev/null +++ b/tests/unittests/client/logic/api/RenderGroupBindingElementsTest.cpp @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" +#include "ramses/client/RenderGroup.h" +#include "impl/logic/RenderGroupBindingElementsImpl.h" + +namespace ramses::internal +{ + class ARenderGroupBindingElements : public ALogicEngine + { + protected: + void expectElements(const RenderGroupBindingElementsImpl::Elements& elements) const + { + EXPECT_EQ(m_elements.impl().getElements(), elements); + } + + RenderGroupBindingElements m_elements; + }; + + TEST_F(ARenderGroupBindingElements, CanAddMeshAndRenderGroupElement) + { + EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); + EXPECT_TRUE(m_elements.addElement(*m_renderGroup, "rg")); + expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); + } + + TEST_F(ARenderGroupBindingElements, CanBeCopyAndMoveConstructed) + { + EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); + const RenderGroupBindingElementsImpl::Elements expectedElements{ { "mesh", m_meshNode } }; + + RenderGroupBindingElements elementsCopy{ m_elements }; + EXPECT_EQ(elementsCopy.impl().getElements(), expectedElements); + + RenderGroupBindingElements elementsMove{ std::move(elementsCopy) }; + EXPECT_EQ(elementsMove.impl().getElements(), expectedElements); + } + + TEST_F(ARenderGroupBindingElements, CanBeCopyAndMoveAssigned) + { + EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); + const RenderGroupBindingElementsImpl::Elements expectedElements{ { "mesh", m_meshNode } }; + + RenderGroupBindingElements elementsCopy; + elementsCopy = m_elements; + EXPECT_EQ(elementsCopy.impl().getElements(), expectedElements); + + RenderGroupBindingElements elementsMove; + elementsMove = std::move(elementsCopy); + EXPECT_EQ(elementsMove.impl().getElements(), expectedElements); + } + + TEST_F(ARenderGroupBindingElements, AddsElementUnderItsObjectNameIfNoElementNameProvided) + { + EXPECT_TRUE(m_elements.addElement(*m_meshNode)); + expectElements({ { "meshNode", m_meshNode } }); + } + + TEST_F(ARenderGroupBindingElements, FailsToAddElementIfNoElementNameProvidedAndObjectNameEmpty) + { + EXPECT_TRUE(m_renderGroup->getName().empty()); + EXPECT_FALSE(m_elements.addElement(*m_renderGroup)); + expectElements({}); + } + + TEST_F(ARenderGroupBindingElements, FailsToAddElementMoreThanOnce) + { + EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); + EXPECT_TRUE(m_elements.addElement(*m_renderGroup, "rg")); + expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); + EXPECT_FALSE(m_elements.addElement(*m_meshNode, "mesh")); + EXPECT_FALSE(m_elements.addElement(*m_renderGroup, "rg")); + expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); + } + + TEST_F(ARenderGroupBindingElements, FailsToAddElementFromOtherScene) + { + EXPECT_TRUE(m_elements.addElement(*m_meshNode, "mesh")); + EXPECT_TRUE(m_elements.addElement(*m_renderGroup, "rg")); + expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); + + auto otherScene{ m_ramses.createScene(sceneId_t{ 666u }) }; + auto otherRG = otherScene->createRenderGroup("rg2"); + auto otherMesh = otherScene->createMeshNode("mesh2"); + EXPECT_FALSE(m_elements.addElement(*otherRG, "rg2")); + EXPECT_FALSE(m_elements.addElement(*otherMesh, "mesh2")); + expectElements({ { "mesh", m_meshNode }, { "rg", m_renderGroup } }); + } +} diff --git a/client/logic/unittests/api/RamsesRenderGroupBindingTest.cpp b/tests/unittests/client/logic/api/RenderGroupBindingTest.cpp similarity index 52% rename from client/logic/unittests/api/RamsesRenderGroupBindingTest.cpp rename to tests/unittests/client/logic/api/RenderGroupBindingTest.cpp index 37cd435ae..f092bce26 100644 --- a/client/logic/unittests/api/RamsesRenderGroupBindingTest.cpp +++ b/tests/unittests/client/logic/api/RenderGroupBindingTest.cpp @@ -11,45 +11,45 @@ #include "RamsesObjectResolverMock.h" #include "RamsesTestUtils.h" #include "SerializationTestUtils.h" -#include "WithTempDirectory.h" -#include "impl/LogicEngineImpl.h" -#include "impl/RamsesRenderGroupBindingImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/RamsesHelper.h" -#include "generated/RamsesRenderGroupBindingGen.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesHelper.h" +#include "internal/logic/flatbuffers/generated/RenderGroupBindingGen.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" -#include "ramses-client-api/RenderGroup.h" +#include "ramses/client/RenderGroup.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-utils.h" namespace ramses::internal { - class ARamsesRenderGroupBinding : public ALogicEngine + class ARenderGroupBinding : public ALogicEngine { }; - TEST_F(ARamsesRenderGroupBinding, HasANameAfterCreation) + TEST_F(ARenderGroupBinding, HasANameAfterCreation) { auto& renderGroupBinding = *createRenderGroupBinding(); EXPECT_EQ("renderGroupBinding", renderGroupBinding.getName()); } - TEST_F(ARamsesRenderGroupBinding, RefersToGivenRenderGroup) + TEST_F(ARenderGroupBinding, RefersToGivenRenderGroup) { auto& renderGroupBinding = *createRenderGroupBinding(); EXPECT_EQ(m_renderGroup, &renderGroupBinding.getRamsesRenderGroup()); const auto& rpConst = renderGroupBinding; EXPECT_EQ(m_renderGroup, &rpConst.getRamsesRenderGroup()); - const auto& rpImplConst = renderGroupBinding.m_renderGroupBinding; + const auto& rpImplConst = renderGroupBinding.impl(); EXPECT_EQ(m_renderGroup, &rpImplConst.getRamsesRenderGroup()); } - TEST_F(ARamsesRenderGroupBinding, HasInputsAfterCreationWithCorrectNamesAndValues) + TEST_F(ARenderGroupBinding, HasInputsAfterCreationWithCorrectNamesAndValues) { const auto mn1 = m_scene->createMeshNode("meshNodeName1"); const auto mn2 = m_scene->createMeshNode("meshNodeName2"); @@ -63,13 +63,13 @@ namespace ramses::internal // add objects as elements to control through rendergroup binding // (intentionally in specific order and using both object or element name) - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*mn2, "elementMesh2Name")); EXPECT_TRUE(elements.addElement(*rg2, "elementRG2Name")); EXPECT_TRUE(elements.addElement(*mn1)); EXPECT_TRUE(elements.addElement(*rg1)); - const auto& renderGroupBinding = *m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements); + const auto& renderGroupBinding = *m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements); ASSERT_NE(nullptr, renderGroupBinding.getInputs()); ASSERT_EQ(1u, renderGroupBinding.getInputs()->getChildCount()); const auto renderOrdersProp = renderGroupBinding.getInputs()->getChild("renderOrders"); @@ -79,7 +79,7 @@ namespace ramses::internal for (size_t i = 0u; i < renderOrdersProp->getChildCount(); ++i) { ASSERT_EQ(EPropertyType::Int32, renderOrdersProp->getChild(i)->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, renderOrdersProp->getChild(i)->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, renderOrdersProp->getChild(i)->impl().getPropertySemantics()); } const auto prop1 = renderOrdersProp->getChild(0u); @@ -99,40 +99,37 @@ namespace ramses::internal EXPECT_EQ(-60, *prop4->get()); } - TEST_F(ARamsesRenderGroupBinding, HasNoOutputsAfterCreation) + TEST_F(ARenderGroupBinding, HasNoOutputsAfterCreation) { auto& renderGroupBinding = *createRenderGroupBinding(); EXPECT_EQ(nullptr, renderGroupBinding.getOutputs()); } - TEST_F(ARamsesRenderGroupBinding, FailsToBeCreatedIfNoProvidedElement) + TEST_F(ARenderGroupBinding, FailsToBeCreatedIfNoProvidedElement) { - RamsesRenderGroupBindingElements emptyElements; - EXPECT_EQ(nullptr, m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, emptyElements)); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create RamsesRenderGroupBinding, there were no elements provided."); + RenderGroupBindingElements emptyElements; + EXPECT_EQ(nullptr, m_logicEngine->createRenderGroupBinding(*m_renderGroup, emptyElements)); + EXPECT_EQ(getLastErrorMessage(), "Cannot create RenderGroupBinding, there were no elements provided."); } - TEST_F(ARamsesRenderGroupBinding, FailsToBeCreatedIfProvidedElementNotContainedInRenderGroupInRamses) + TEST_F(ARenderGroupBinding, FailsToBeCreatedIfProvidedElementNotContainedInRenderGroupInRamses) { const auto mn1 = m_scene->createMeshNode("meshNodeName1"); - RamsesRenderGroupBindingElements elements1; + RenderGroupBindingElements elements1; EXPECT_TRUE(elements1.addElement(*m_meshNode)); // contained EXPECT_TRUE(elements1.addElement(*mn1)); // not contained - EXPECT_EQ(nullptr, m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements1)); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create RamsesRenderGroupBinding, one or more of the provided elements is not contained in the RenderGroup to bind."); + EXPECT_EQ(nullptr, m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements1)); + EXPECT_EQ(getLastErrorMessage(), "Cannot create RenderGroupBinding, one or more of the provided elements is not contained in the RenderGroup to bind."); const auto rg1 = m_scene->createRenderGroup("renderGroupName1"); - RamsesRenderGroupBindingElements elements2; + RenderGroupBindingElements elements2; EXPECT_TRUE(elements2.addElement(*m_meshNode)); // contained EXPECT_TRUE(elements2.addElement(*rg1)); // not contained - EXPECT_EQ(nullptr, m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements2)); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot create RamsesRenderGroupBinding, one or more of the provided elements is not contained in the RenderGroup to bind."); + EXPECT_EQ(nullptr, m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements2)); + EXPECT_EQ(getLastErrorMessage(), "Cannot create RenderGroupBinding, one or more of the provided elements is not contained in the RenderGroup to bind."); } - TEST_F(ARamsesRenderGroupBinding, RemovesLinkWhenDestroyed) + TEST_F(ARenderGroupBinding, RemovesLinkWhenDestroyed) { auto& renderGroupBinding = *createRenderGroupBinding(); @@ -144,57 +141,57 @@ namespace ramses::internal OUT.val = 42 end )"; - const LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("val"), *renderGroupBinding.getInputs()->getChild("renderOrders")->getChild(0u))); + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("val"), *renderGroupBinding.getInputs()->getChild("renderOrders")->getChild(0u))); EXPECT_TRUE(script->getOutputs()->getChild("val")->isLinked()); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); - EXPECT_TRUE(m_logicEngine.destroy(renderGroupBinding)); + EXPECT_TRUE(m_logicEngine->destroy(renderGroupBinding)); EXPECT_FALSE(script->getOutputs()->getChild("val")->isLinked()); } - TEST_F(ARamsesRenderGroupBinding, TakesInitialValuesFromRamsesRenderGroup) + TEST_F(ARenderGroupBinding, TakesInitialValuesFromRamsesRenderGroup) { const auto nestedRG = m_scene->createRenderGroup(); m_renderGroup->addRenderGroup(*nestedRG, 41); m_renderGroup->addMeshNode(*m_meshNode, 42); - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); EXPECT_TRUE(elements.addElement(*nestedRG, "rg")); - const auto& renderGroupBinding = m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements); + const auto& renderGroupBinding = m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements); EXPECT_EQ(41, *renderGroupBinding->getInputs()->getChild("renderOrders")->getChild("rg")->get()); EXPECT_EQ(42, *renderGroupBinding->getInputs()->getChild("renderOrders")->getChild("mesh")->get()); } - TEST_F(ARamsesRenderGroupBinding, AppliesChangesToBoundObject) + TEST_F(ARenderGroupBinding, AppliesChangesToBoundObject) { auto& renderGroupBinding = *createRenderGroupBinding(); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild(0u)->set(42)); - m_logicEngine.update(); + m_logicEngine->update(); int32_t renderOrder = -1; m_renderGroup->getMeshNodeOrder(*m_meshNode, renderOrder); EXPECT_EQ(42, renderOrder); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild(0u)->set(-100)); - m_logicEngine.update(); + m_logicEngine->update(); m_renderGroup->getMeshNodeOrder(*m_meshNode, renderOrder); EXPECT_EQ(-100, renderOrder); } - TEST_F(ARamsesRenderGroupBinding, PropagateItsInputsToRamsesRenderGroupOnUpdate_OnlyWhenExplicitlySet) + TEST_F(ARenderGroupBinding, PropagateItsInputsToRamsesRenderGroupOnUpdate_OnlyWhenExplicitlySet) { const auto nestedRG = m_scene->createRenderGroup(); m_renderGroup->addRenderGroup(*nestedRG, 41); m_renderGroup->addMeshNode(*m_meshNode, 42); - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); EXPECT_TRUE(elements.addElement(*nestedRG, "rg")); - auto& renderGroupBinding = *m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements); + auto& renderGroupBinding = *m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements); - m_logicEngine.update(); + m_logicEngine->update(); int32_t renderOrder = -1; m_renderGroup->getRenderGroupOrder(*nestedRG, renderOrder); EXPECT_EQ(41, renderOrder); @@ -203,14 +200,14 @@ namespace ramses::internal // update only mesh EXPECT_TRUE(renderGroupBinding.getInputs()->getChild("renderOrders")->getChild("mesh")->set(-100)); - m_logicEngine.update(); + m_logicEngine->update(); m_renderGroup->getRenderGroupOrder(*nestedRG, renderOrder); EXPECT_EQ(41, renderOrder); // stays unchanged m_renderGroup->getMeshNodeOrder(*m_meshNode, renderOrder); EXPECT_EQ(-100, renderOrder); } - TEST_F(ARamsesRenderGroupBinding, PropagatesItsInputsToRamsesRenderGroupOnUpdate_WithLinksInsteadOfSetCall) + TEST_F(ARenderGroupBinding, PropagatesItsInputsToRamsesRenderGroupOnUpdate_WithLinksInsteadOfSetCall) { auto& renderGroupBinding = *createRenderGroupBinding(); @@ -223,55 +220,53 @@ namespace ramses::internal OUT.val = 42 end )"; - const LuaScript* script = m_logicEngine.createLuaScript(scriptSrc); - ASSERT_TRUE(m_logicEngine.link(*script->getOutputs()->getChild("val"), *renderGroupBinding.getInputs()->getChild("renderOrders")->getChild(0u))); - EXPECT_TRUE(m_logicEngine.update()); + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("val"), *renderGroupBinding.getInputs()->getChild("renderOrders")->getChild(0u))); + EXPECT_TRUE(m_logicEngine->update()); int32_t renderOrder = -1; m_renderGroup->getMeshNodeOrder(*m_meshNode, renderOrder); EXPECT_EQ(42, renderOrder); } - TEST_F(ARamsesRenderGroupBinding, GracefullyFailsUpdateIfChangingRenderOrderOfMeshNodeWhichWasRemovedFromBoundRenderGroup) + TEST_F(ARenderGroupBinding, GracefullyFailsUpdateIfChangingRenderOrderOfMeshNodeWhichWasRemovedFromBoundRenderGroup) { auto& renderGroupBinding = *createRenderGroupBinding(); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild(0u)->set(42)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // remove mesh from rendergroup using ramses API // this is illegal but can happen m_renderGroup->removeMeshNode(*m_meshNode); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild(0u)->set(-42)); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot set render order of MeshNode which is not contained in bound RenderGroup."); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_EQ(getLastErrorMessage(), "Cannot set render order of MeshNode which is not contained in bound RenderGroup."); } - TEST_F(ARamsesRenderGroupBinding, GracefullyFailsUpdateIfChangingRenderOrderOfRenderGroupWhichWasRemovedFromBoundRenderGroup) + TEST_F(ARenderGroupBinding, GracefullyFailsUpdateIfChangingRenderOrderOfRenderGroupWhichWasRemovedFromBoundRenderGroup) { const auto nestedRG = m_scene->createRenderGroup(); m_renderGroup->addRenderGroup(*nestedRG); - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); EXPECT_TRUE(elements.addElement(*nestedRG, "rg")); - auto& renderGroupBinding = *m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements); + auto& renderGroupBinding = *m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild("rg")->set(42)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // remove nested rendergroup from bound rendergroup using ramses API // this is illegal but can happen m_renderGroup->removeRenderGroup(*nestedRG); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild("rg")->set(-42)); - EXPECT_FALSE(m_logicEngine.update()); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Cannot set render order of RenderGroup which is not contained in bound RenderGroup."); + EXPECT_FALSE(m_logicEngine->update()); + EXPECT_EQ(getLastErrorMessage(), "Cannot set render order of RenderGroup which is not contained in bound RenderGroup."); } - class ARamsesRenderGroupBinding_SerializationLifecycle : public ARamsesRenderGroupBinding + class ARenderGroupBinding_SerializationLifecycle : public ARenderGroupBinding { protected: enum class ESerializationIssue @@ -289,7 +284,7 @@ namespace ramses::internal MismatchedElementRamsesRefObjectType }; - std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) + std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) { { auto inputsType = MakeStruct("", { @@ -303,9 +298,9 @@ namespace ramses::internal auto inputsImpl = std::make_unique(std::move(inputs), EPropertySemantics::BindingInput); auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, - rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u, 0u, 0u), + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u), (issue == ESerializationIssue::MissingRamsesRefObject ? 0 : rlogic_serialization::CreateRamsesReference(m_flatBufferBuilder, - 1u, (issue == ESerializationIssue::MismatchedRamsesRefObjectType ? 0 : static_cast(ramses::ERamsesObjectType::RenderGroup)))), + 1u, (issue == ESerializationIssue::MismatchedRamsesRefObjectType ? 0 : static_cast(ERamsesObjectType::RenderGroup)))), (issue == ESerializationIssue::MissingRootInput ? 0 : PropertyImpl::Serialize(*inputsImpl, m_flatBufferBuilder, m_serializationMap))); std::vector> elementsFB; @@ -315,7 +310,7 @@ namespace ramses::internal (issue == ESerializationIssue::MissingElementRamsesRefObject ? 0 : rlogic_serialization::CreateRamsesReference(m_flatBufferBuilder, 2u, (issue == ESerializationIssue::MismatchedElementRamsesRefObjectType ? 0 : static_cast(m_meshNode->getType())))))); - auto fbRenderGroupBinding = rlogic_serialization::CreateRamsesRenderGroupBinding(m_flatBufferBuilder, fbRamsesBinding, m_flatBufferBuilder.CreateVector(elementsFB)); + auto fbRenderGroupBinding = rlogic_serialization::CreateRenderGroupBinding(m_flatBufferBuilder, fbRamsesBinding, m_flatBufferBuilder.CreateVector(elementsFB)); m_flatBufferBuilder.Finish(fbRenderGroupBinding); } @@ -327,10 +322,10 @@ namespace ramses::internal case ESerializationIssue::MissingElementRamsesRefObject: case ESerializationIssue::ElementRamsesRefObjectNotInScene: case ESerializationIssue::MismatchedElementRamsesRefObjectType: - EXPECT_CALL(m_resolverMock, findRamsesRenderGroupInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(m_renderGroup)); + EXPECT_CALL(m_resolverMock, findRamsesRenderGroupInScene(::testing::Eq("name"), sceneObjectId_t{ 1u })).WillOnce(::testing::Return(m_renderGroup)); break; case ESerializationIssue::RamsesRefObjectNotInScene: - EXPECT_CALL(m_resolverMock, findRamsesRenderGroupInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 1u })).WillOnce(::testing::Return(nullptr)); + EXPECT_CALL(m_resolverMock, findRamsesRenderGroupInScene(::testing::Eq("name"), sceneObjectId_t{ 1u })).WillOnce(::testing::Return(nullptr)); break; default: break; @@ -340,17 +335,17 @@ namespace ramses::internal { case ESerializationIssue::AllValid: case ESerializationIssue::MismatchedElementRamsesRefObjectType: - EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 2u })).WillOnce(::testing::Return(m_meshNode)); + EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), sceneObjectId_t{ 2u })).WillOnce(::testing::Return(m_meshNode)); break; case ESerializationIssue::ElementRamsesRefObjectNotInScene: - EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), ramses::sceneObjectId_t{ 2u })).WillOnce(::testing::Return(nullptr)); + EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("name"), sceneObjectId_t{ 2u })).WillOnce(::testing::Return(nullptr)); break; default: break; } - const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - return RamsesRenderGroupBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + return RenderGroupBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); } flatbuffers::FlatBufferBuilder m_flatBufferBuilder; @@ -358,105 +353,104 @@ namespace ramses::internal ::testing::StrictMock m_resolverMock; ErrorReporting m_errorReporting; SerializationMap m_serializationMap; - DeserializationMap m_deserializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; }; - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, CanSerializeWithNoIssue) + TEST_F(ARenderGroupBinding_SerializationLifecycle, CanSerializeWithNoIssue) { - EXPECT_TRUE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_TRUE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::AllValid)); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingName) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingName) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingName)); - ASSERT_EQ(2u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LogicObject base from serialized data: missing name!"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing name and/or ID!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingName)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: missing name and/or ID!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingRootInput) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingRootInput) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingRootInput)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing root input!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingRootInput)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: missing root input!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_RootInputNotStruct) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_RootInputNotStruct) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::RootInputNotStruct)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: root input has unexpected type!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::RootInputNotStruct)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: root input has unexpected type!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingRamsesRefObject) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingRamsesRefObject) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingRamsesRefObject)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing ramses object reference!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingRamsesRefObject)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: missing ramses object reference!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_RamsesRefObjectNotInScene) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_RamsesRefObjectNotInScene) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::RamsesRefObjectNotInScene)); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::RamsesRefObjectNotInScene)); // error log is in ramses object resolver which is mocked } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MismatchedRamsesRefObjectType) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MismatchedRamsesRefObjectType) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::MismatchedRamsesRefObjectType)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: loaded object type does not match referenced object type!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::MismatchedRamsesRefObjectType)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: loaded object type does not match referenced object type!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_PropertiesDoNotMatchElements) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_PropertiesDoNotMatchElements) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::PropertiesDoNotMatchElements)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: input properties do not match RenderGroup's elements!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::PropertiesDoNotMatchElements)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: input properties do not match RenderGroup's elements!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingElementRamsesRefObject) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MissingElementRamsesRefObject) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingElementRamsesRefObject)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding 'name' elements data: missing name or Ramses reference!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::MissingElementRamsesRefObject)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding 'name' elements data: missing name or Ramses reference!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_ElementRamsesRefObjectNotInScene) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_ElementRamsesRefObjectNotInScene) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::ElementRamsesRefObjectNotInScene)); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::ElementRamsesRefObjectNotInScene)); // error log is in ramses object resolver which is mocked } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MismatchedElementRamsesRefObjectType) + TEST_F(ARenderGroupBinding_SerializationLifecycle, ReportsSerializationError_MismatchedElementRamsesRefObjectType) { - EXPECT_FALSE(deserializeSerializedDataWithIssue(ARamsesRenderGroupBinding_SerializationLifecycle::ESerializationIssue::MismatchedElementRamsesRefObjectType)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding 'name' elements data: loaded element object type does not match referenced object type!"); + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderGroupBinding_SerializationLifecycle::ESerializationIssue::MismatchedElementRamsesRefObjectType)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding 'name' elements data: loaded element object type does not match referenced object type!"); } - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, KeepsItsPropertiesAfterDeserialization) + TEST_F(ARenderGroupBinding_SerializationLifecycle, KeepsItsPropertiesAfterDeserialization) { { const auto nestedRG = m_scene->createRenderGroup(); m_renderGroup->addRenderGroup(*nestedRG); - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); EXPECT_TRUE(elements.addElement(*nestedRG, "rg")); - auto& renderGroupBinding = *m_logicEngine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "renderGroupBinding"); + auto& renderGroupBinding = *m_logicEngine->createRenderGroupBinding(*m_renderGroup, elements, "renderGroupBinding"); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild("mesh")->set(41)); EXPECT_TRUE(renderGroupBinding.getInputs()->getChild(0u)->getChild("rg")->set(42)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // binding has no inputs linked, validatation would fail - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); + ASSERT_TRUE(saveToFileWithoutValidation("binding.bin")); } { - ASSERT_TRUE(m_logicEngine.loadFromFile("binding.bin", m_scene)); - const auto loadedBinding = m_logicEngine.findByName("renderGroupBinding"); + ASSERT_TRUE(recreateFromFile("binding.bin")); + const auto loadedBinding = m_logicEngine->findObject("renderGroupBinding"); EXPECT_EQ(&loadedBinding->getRamsesRenderGroup(), m_renderGroup); ASSERT_NE(nullptr, loadedBinding->getInputs()); @@ -468,28 +462,14 @@ namespace ramses::internal const auto prop1 = renderOrdersProp->getChild(0u); EXPECT_EQ("mesh", prop1->getName()); EXPECT_EQ(EPropertyType::Int32, prop1->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, prop1->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, prop1->impl().getPropertySemantics()); EXPECT_EQ(41, *prop1->get()); const auto prop2 = renderOrdersProp->getChild(1u); EXPECT_EQ("rg", prop2->getName()); EXPECT_EQ(EPropertyType::Int32, prop2->getType()); - EXPECT_EQ(EPropertySemantics::BindingInput, prop2->m_impl->getPropertySemantics()); + EXPECT_EQ(EPropertySemantics::BindingInput, prop2->impl().getPropertySemantics()); EXPECT_EQ(42, *prop2->get()); } } - - TEST_F(ARamsesRenderGroupBinding_SerializationLifecycle, FailsToLoadWhenNoSceneProvided) - { - { - createRenderGroupBinding(); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - { - EXPECT_FALSE(m_logicEngine.loadFromFile("binding.bin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); - } - } } diff --git a/tests/unittests/client/logic/api/RenderPassBindingTest.cpp b/tests/unittests/client/logic/api/RenderPassBindingTest.cpp new file mode 100644 index 000000000..1b580af99 --- /dev/null +++ b/tests/unittests/client/logic/api/RenderPassBindingTest.cpp @@ -0,0 +1,579 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2022 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "LogicEngineTest_Base.h" + +#include "RamsesObjectResolverMock.h" +#include "RamsesTestUtils.h" +#include "SerializationTestUtils.h" +#include "WithTempDirectory.h" + +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/RamsesHelper.h" +#include "internal/logic/flatbuffers/generated/RenderPassBindingGen.h" + +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/ramses-utils.h" + +namespace ramses::internal +{ + class ARenderPassBinding : public ALogicEngine + { + }; + + TEST_F(ARenderPassBinding, HasANameAfterCreation) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + EXPECT_EQ("renderPass", renderPassBinding.getName()); + } + + TEST_F(ARenderPassBinding, RefersToGivenRenderPass) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + EXPECT_EQ(m_renderPass, &renderPassBinding.getRamsesRenderPass()); + const auto& rpConst = renderPassBinding; + EXPECT_EQ(m_renderPass, &rpConst.getRamsesRenderPass()); + const auto& rpImplConst = renderPassBinding.impl(); + EXPECT_EQ(m_renderPass, &rpImplConst.getRamsesRenderPass()); + } + + TEST_F(ARenderPassBinding, HasInputsAfterCreation) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + ASSERT_NE(nullptr, renderPassBinding.getInputs()); + ASSERT_EQ(std::size_t(RenderPassBindingImpl::EPropertyIndex_COUNT), renderPassBinding.getInputs()->getChildCount()); + EXPECT_EQ(renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_Enabled), renderPassBinding.getInputs()->getChild("enabled")); + EXPECT_EQ(EPropertyType::Bool, renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_Enabled)->getType()); + EXPECT_EQ(renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOrder), renderPassBinding.getInputs()->getChild("renderOrder")); + EXPECT_EQ(EPropertyType::Int32, renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOrder)->getType()); + EXPECT_EQ(renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_ClearColor), renderPassBinding.getInputs()->getChild("clearColor")); + EXPECT_EQ(EPropertyType::Vec4f, renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_ClearColor)->getType()); + EXPECT_EQ(renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOnce), renderPassBinding.getInputs()->getChild("renderOnce")); + EXPECT_EQ(EPropertyType::Bool, renderPassBinding.getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOnce)->getType()); + } + + TEST_F(ARenderPassBinding, HasNoOutputsAfterCreation) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + EXPECT_EQ(nullptr, renderPassBinding.getOutputs()); + } + + // This fixture only contains serialization unit tests, for higher order tests see `ARenderPassBinding_WithRamses_AndFiles` + class ARenderPassBinding_SerializationLifecycle : public ARenderPassBinding + { + protected: + enum class ESerializationIssue + { + AllValid, + RootNotStruct, + BoundObjectReferenceMissing, + BoundObjectTypeMismatch + }; + + std::unique_ptr deserializeSerializedDataWithIssue(ESerializationIssue issue) + { + { + auto inputsType = MakeStruct("", { + TypeData{"enabled", EPropertyType::Bool}, + TypeData{"renderOrder", EPropertyType::Int32}, + TypeData{"clearColor", EPropertyType::Vec4f}, + TypeData{"renderOnce", EPropertyType::Bool} + }); + + HierarchicalTypeData inputs = (issue == ESerializationIssue::RootNotStruct ? MakeType("", EPropertyType::Bool) : inputsType); + auto inputsImpl = std::make_unique(std::move(inputs), EPropertySemantics::BindingInput); + + auto fbRamsesBinding = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, m_flatBufferBuilder.CreateString("name"), 1u), + (issue == ESerializationIssue::BoundObjectReferenceMissing ? 0 : rlogic_serialization::CreateRamsesReference(m_flatBufferBuilder, + 1u, (issue == ESerializationIssue::BoundObjectTypeMismatch ? 0 : static_cast(ERamsesObjectType::RenderPass)))), + PropertyImpl::Serialize(*inputsImpl, m_flatBufferBuilder, m_serializationMap)); + + auto fbRenderPassBinding = rlogic_serialization::CreateRenderPassBinding(m_flatBufferBuilder, fbRamsesBinding); + m_flatBufferBuilder.Finish(fbRenderPassBinding); + } + + switch (issue) + { + case ESerializationIssue::AllValid: + case ESerializationIssue::BoundObjectTypeMismatch: + EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), sceneObjectId_t{ 1u })).WillOnce(::testing::Return(m_renderPass)); + break; + case ESerializationIssue::RootNotStruct: + case ESerializationIssue::BoundObjectReferenceMissing: + break; + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + return RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + } + + flatbuffers::FlatBufferBuilder m_flatBufferBuilder; + SerializationTestUtils m_testUtils {m_flatBufferBuilder}; + ::testing::StrictMock m_resolverMock; + ErrorReporting m_errorReporting; + SerializationMap m_serializationMap; + DeserializationMap m_deserializationMap{ m_scene->impl() }; + }; + + // More unit tests with inputs/outputs declared in LogicNode (base class) serialization tests + TEST_F(ARenderPassBinding_SerializationLifecycle, StoresBaseClassData) + { + // Serialize + { + RenderPassBindingImpl binding(m_scene->impl(), *m_renderPass, "name", sceneObjectId_t{ 1u }); + binding.createRootProperties(); + (void)RenderPassBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + } + + // Inspect flatbuffers data + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + + ASSERT_TRUE(serializedBinding.base()); + ASSERT_TRUE(serializedBinding.base()->base()); + ASSERT_TRUE(serializedBinding.base()->base()->name()); + EXPECT_EQ(serializedBinding.base()->base()->name()->string_view(), "name"); + EXPECT_EQ(serializedBinding.base()->base()->id(), 1u); + + ASSERT_TRUE(serializedBinding.base()->rootInput()); + EXPECT_EQ(serializedBinding.base()->rootInput()->rootType(), rlogic_serialization::EPropertyRootType::Struct); + ASSERT_TRUE(serializedBinding.base()->rootInput()->children()); + EXPECT_EQ(serializedBinding.base()->rootInput()->children()->size(), size_t(RenderPassBindingImpl::EPropertyIndex_COUNT)); + + // Deserialize + { + EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), m_renderPass->getSceneObjectId())).WillOnce(::testing::Return(m_renderPass)); + std::unique_ptr deserializedBinding = RenderPassBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + + ASSERT_TRUE(deserializedBinding); + EXPECT_EQ(deserializedBinding->getName(), "name"); + EXPECT_EQ(deserializedBinding->getSceneObjectId().getValue(), 1u); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + } + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, StoresIdAndTypeAndRenderPassRef) + { + // Serialize + { + RenderPassBindingImpl binding(m_scene->impl(), *m_renderPass, "name", sceneObjectId_t{ 1u }); + binding.createRootProperties(); + (void)RenderPassBindingImpl::Serialize(binding, m_flatBufferBuilder, m_serializationMap); + } + + // Inspect flatbuffers data + const auto& serializedBinding = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + + ASSERT_TRUE(serializedBinding.base()->boundRamsesObject()); + EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectId(), m_renderPass->getSceneObjectId().getValue()); + EXPECT_EQ(serializedBinding.base()->boundRamsesObject()->objectType(), static_cast(ERamsesObjectType::RenderPass)); + + // Deserialize + { + EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), m_renderPass->getSceneObjectId())).WillOnce(::testing::Return(m_renderPass)); + std::unique_ptr deserializedBinding = RenderPassBindingImpl::Deserialize(serializedBinding, m_resolverMock, m_errorReporting, m_deserializationMap); + + ASSERT_TRUE(deserializedBinding); + EXPECT_EQ(&deserializedBinding->getRamsesRenderPass(), m_renderPass); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + + // Check that input was deserialized too + EXPECT_EQ(deserializedBinding->getInputs()->getChild("enabled")->getType(), EPropertyType::Bool); + } + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ErrorWhenNoBindingBaseData) + { + { + auto binding = rlogic_serialization::CreateRenderPassBinding( + m_flatBufferBuilder, + 0 // no base binding info + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderPassBinding from serialized data: missing base class info!"); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ErrorWhenNoBindingName) + { + { + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + 0, // no name! + 1u) + ); + auto binding = rlogic_serialization::CreateRenderPassBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of RenderPassBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ErrorWhenNoBindingId) + { + { + auto base = rlogic_serialization::CreateRamsesBinding(m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 0) // no id + ); + auto binding = rlogic_serialization::CreateRenderPassBinding(m_flatBufferBuilder, base); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(this->m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of RenderPassBinding from serialized data: missing name and/or ID!", this->m_errorReporting.getError()->message); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ErrorWhenNoRootInput) + { + { + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + 0 // no root input + ); + auto binding = rlogic_serialization::CreateRenderPassBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderPassBinding from serialized data: missing root input!"); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ErrorWhenBoundRenderPassCannotBeResolved) + { + const sceneObjectId_t mockObjectId {12}; + { + auto ramsesRef = rlogic_serialization::CreateRamsesReference( + m_flatBufferBuilder, + mockObjectId.getValue() + ); + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + ramsesRef, + m_testUtils.serializeTestProperty("") + ); + auto binding = rlogic_serialization::CreateRenderPassBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + EXPECT_CALL(m_resolverMock, findRamsesRenderPassInScene(::testing::Eq("name"), mockObjectId)).WillOnce(::testing::Return(nullptr)); + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ErrorWhenRootInputHasErrors) + { + { + auto base = rlogic_serialization::CreateRamsesBinding( + m_flatBufferBuilder, + rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, + m_flatBufferBuilder.CreateString("name"), + 1u), + 0, + m_testUtils.serializeTestProperty("", rlogic_serialization::EPropertyRootType::Struct, false, true) // rootInput with errors + ); + auto binding = rlogic_serialization::CreateRenderPassBinding( + m_flatBufferBuilder, + base + ); + m_flatBufferBuilder.Finish(binding); + } + + const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); + std::unique_ptr deserialized = RenderPassBindingImpl::Deserialize(serialized, m_resolverMock, m_errorReporting, m_deserializationMap); + + EXPECT_FALSE(deserialized); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of Property from serialized data: missing name!"); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, CanSerializeWithNoIssue) + { + EXPECT_TRUE(deserializeSerializedDataWithIssue(ARenderPassBinding_SerializationLifecycle::ESerializationIssue::AllValid)); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ReportsSerializationError_RootNotStructType) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderPassBinding_SerializationLifecycle::ESerializationIssue::RootNotStruct)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderPassBinding from serialized data: root input has unexpected type!"); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ReportsSerializationError_BoundObjectReferenceMissing) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderPassBinding_SerializationLifecycle::ESerializationIssue::BoundObjectReferenceMissing)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderPassBinding from serialized data: missing ramses object reference!"); + } + + TEST_F(ARenderPassBinding_SerializationLifecycle, ReportsSerializationError_BoundObjectTypeMismatch) + { + EXPECT_FALSE(deserializeSerializedDataWithIssue(ARenderPassBinding_SerializationLifecycle::ESerializationIssue::BoundObjectTypeMismatch)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderPassBinding from serialized data: loaded object type does not match referenced object type!"); + } + + class ARenderPassBinding_WithRamses : public ARenderPassBinding + { + }; + + TEST_F(ARenderPassBinding_WithRamses, ReturnsReferenceToRamsesRenderPass) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + EXPECT_EQ(m_renderPass, &renderPassBinding.getRamsesRenderPass()); + } + + TEST_F(ARenderPassBinding_WithRamses, GivesInputs_BindingInputSemantics) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + for (size_t i = 0; i < renderPassBinding.getInputs()->getChildCount(); ++i) + { + EXPECT_EQ(EPropertySemantics::BindingInput, renderPassBinding.getInputs()->getChild(i)->impl().getPropertySemantics()); + } + } + + TEST_F(ARenderPassBinding_WithRamses, TakesInitialValuesFromRamsesRenderPass) + { + m_renderPass->setEnabled(false); + m_renderPass->setRenderOrder(42); + m_renderPass->setClearColor({0.1f, 0.2f, 0.3f, 0.4f}); + m_renderPass->setRenderOnce(true); + + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + auto inputs = renderPassBinding.getInputs(); + EXPECT_FALSE(*inputs->getChild("enabled")->get()); + EXPECT_EQ(42, *inputs->getChild("renderOrder")->get()); + EXPECT_EQ(*inputs->getChild("clearColor")->get(), vec4f(0.1f, 0.2f, 0.3f, 0.4f)); + EXPECT_TRUE(inputs->getChild("renderOnce")->set(true)); + } + + TEST_F(ARenderPassBinding_WithRamses, UpdatesRenderPassIfInputValuesWereSet) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + auto inputs = renderPassBinding.getInputs(); + ASSERT_EQ(std::size_t(RenderPassBindingImpl::EPropertyIndex_COUNT), inputs->getChildCount()); + EXPECT_TRUE(inputs->getChild("enabled")->set(false)); + EXPECT_TRUE(inputs->getChild("renderOrder")->set(42)); + EXPECT_TRUE(inputs->getChild("clearColor")->set({ 0.1f, 0.2f, 0.3f, 0.4f })); + EXPECT_TRUE(inputs->getChild("renderOnce")->set(true)); + + EXPECT_TRUE(m_logicEngine->update()); + + EXPECT_FALSE(m_renderPass->isEnabled()); + EXPECT_EQ(42, m_renderPass->getRenderOrder()); + vec4f clearColor = m_renderPass->getClearColor(); + EXPECT_EQ(clearColor, vec4f(0.1f, 0.2f, 0.3f, 0.4f)); + EXPECT_TRUE(m_renderPass->isRenderOnce()); + } + + TEST_F(ARenderPassBinding_WithRamses, PropagateItsInputsToRamsesRenderPassOnUpdate_OnlyWhenExplicitlySet) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + + // Set values directly + m_renderPass->setEnabled(false); + m_renderPass->setRenderOrder(3); + + // Set only one of the inputs to the binding object, the other one (enabled) not + EXPECT_TRUE(renderPassBinding.getInputs()->getChild("renderOrder")->set(99)); + + EXPECT_TRUE(m_logicEngine->update()); + + // Only propagate the value which was also set in the binding object + EXPECT_FALSE(m_renderPass->isEnabled()); + EXPECT_EQ(99, m_renderPass->getRenderOrder()); + } + + TEST_F(ARenderPassBinding_WithRamses, PropagatesItsInputsToRamsesRenderPassOnUpdate_WithLinksInsteadOfSetCall) + { + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + + // Set values directly + m_renderPass->setRenderOrder(13); + m_renderPass->setEnabled(false); + + // Link binding input to a script (binding is not set directly, but is linked) + const std::string_view scriptSrc = R"( + function interface(IN,OUT) + OUT.val = Type:Int32() + end + function run(IN,OUT) + OUT.val = 42 + end + )"; + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("val"), *renderPassBinding.getInputs()->getChild("renderOrder"))); + + EXPECT_TRUE(m_logicEngine->update()); + + // Only propagate the value which was also linked over the binding object's input to a script + EXPECT_EQ(42, m_renderPass->getRenderOrder()); + EXPECT_FALSE(m_renderPass->isEnabled()); + } + + class ARenderPassBinding_WithRamses_AndFiles : public ARenderPassBinding_WithRamses + { + public: + ARenderPassBinding_WithRamses_AndFiles() + { + withTempDirectory(); + } + }; + + TEST_F(ARenderPassBinding_WithRamses_AndFiles, KeepsItsPropertiesAfterDeserialization_WhenNoRamsesLinksAndSceneProvided) + { + { + m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + ASSERT_TRUE(saveToFileWithoutValidation("binding.bin")); + } + + { + ASSERT_TRUE(recreateFromFile("binding.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto loadedBinding = m_logicEngine->findObject("renderPass"); + EXPECT_EQ(&loadedBinding->getRamsesRenderPass(), m_renderPass); + EXPECT_EQ(loadedBinding->getName(), "renderPass"); + + ASSERT_NE(nullptr, loadedBinding->getInputs()); + ASSERT_EQ(std::size_t(RenderPassBindingImpl::EPropertyIndex_COUNT), loadedBinding->getInputs()->getChildCount()); + EXPECT_EQ(loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_Enabled), loadedBinding->getInputs()->getChild("enabled")); + EXPECT_EQ(EPropertyType::Bool, loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_Enabled)->getType()); + EXPECT_EQ(loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOrder), loadedBinding->getInputs()->getChild("renderOrder")); + EXPECT_EQ(EPropertyType::Int32, loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOrder)->getType()); + EXPECT_EQ(loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_ClearColor), loadedBinding->getInputs()->getChild("clearColor")); + EXPECT_EQ(EPropertyType::Vec4f, loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_ClearColor)->getType()); + EXPECT_EQ(loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOnce), loadedBinding->getInputs()->getChild("renderOnce")); + EXPECT_EQ(EPropertyType::Bool, loadedBinding->getInputs()->getChild(RenderPassBindingImpl::EPropertyIndex_RenderOnce)->getType()); + } + } + + TEST_F(ARenderPassBinding_WithRamses_AndFiles, KeepsPropertyValueAfterDeserializationWithScene) + { + const sceneObjectId_t bindingIdBeforeReload = m_renderPass->getSceneObjectId(); + { + auto& binding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + binding.getInputs()->getChild("renderOrder")->set(42); + m_logicEngine->update(); + EXPECT_EQ(42, m_renderPass->getRenderOrder()); + ASSERT_TRUE(saveToFileWithoutValidation("logic.bin")); + } + + { + ASSERT_TRUE(recreateFromFile("logic.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + auto loadedBinding = m_logicEngine->findObject("renderPass"); + EXPECT_EQ(loadedBinding->getRamsesRenderPass().getSceneObjectId(), bindingIdBeforeReload); + + ASSERT_NE(nullptr, loadedBinding->getInputs()); + ASSERT_EQ(std::size_t(RenderPassBindingImpl::EPropertyIndex_COUNT), loadedBinding->getInputs()->getChildCount()); + EXPECT_EQ(42, *loadedBinding->getInputs()->getChild("renderOrder")->get()); + EXPECT_EQ(42, m_renderPass->getRenderOrder()); + } + } + + // This is sort of a confidence test, testing a combination of: + // - bindings only propagating their values to ramses if the value was set by an incoming link + // - saving and loading files + // - value only re-applied to ramses if changed. Otherwise not. + // The general expectation is that after loading + update(), the logic scene would overwrite ramses + // properties wrapped by a LogicBinding if they are linked to a script + TEST_F(ARenderPassBinding_WithRamses_AndFiles, SetsOnlyRenderPassValuesForWhichTheBindingInputIsLinked_AfterLoadingFromFile_AndCallingUpdate) + { + { + const std::string_view scriptSrc = R"( + function interface(IN,OUT) + OUT.val = Type:Int32() + end + function run(IN,OUT) + OUT.val = 42 + end + )"; + + LuaScript* script = m_logicEngine->createLuaScript(scriptSrc); + auto& renderPassBinding = *m_logicEngine->createRenderPassBinding(*m_renderPass, "renderPass"); + + ASSERT_TRUE(m_logicEngine->link(*script->getOutputs()->getChild("val"), *renderPassBinding.getInputs()->getChild("renderOrder"))); + ASSERT_TRUE(m_logicEngine->update()); + + // Set renderOrder to a different value than the one set by the link + m_renderPass->setRenderOrder(13); + // Set renderOnce to custom value - it should not be overwritten by logic at all, because there is no link + // or any set() calls to the corresponding RenderPassBinding input + m_renderPass->setRenderOnce(true); + + ASSERT_TRUE(saveToFileWithoutValidation("SomeValuesLinked.bin")); + } + + { + ASSERT_TRUE(recreateFromFile("SomeValuesLinked.bin")); + ASSERT_TRUE(m_logicEngine != nullptr); + + // nothing happens before update() + EXPECT_EQ(13, m_renderPass->getRenderOrder()); + EXPECT_TRUE(m_renderPass->isRenderOnce()); + + EXPECT_TRUE(m_logicEngine->update()); + + // Script is executed -> link is activated -> binding is updated, only for the linked property + EXPECT_EQ(42, m_renderPass->getRenderOrder()); + EXPECT_TRUE(m_renderPass->isRenderOnce()); + + // Reset uniform manually and call update does nothing (must set binding input explicitly to cause overwrite in ramses) + m_renderPass->setRenderOrder(13); + EXPECT_TRUE(m_logicEngine->update()); + EXPECT_EQ(13, m_renderPass->getRenderOrder()); + EXPECT_TRUE(m_renderPass->isRenderOnce()); + } + } +} diff --git a/client/logic/unittests/api/SaveFileConfigTest.cpp b/tests/unittests/client/logic/api/SaveFileConfigTest.cpp similarity index 62% rename from client/logic/unittests/api/SaveFileConfigTest.cpp rename to tests/unittests/client/logic/api/SaveFileConfigTest.cpp index b26c1f097..f8952322d 100644 --- a/client/logic/unittests/api/SaveFileConfigTest.cpp +++ b/tests/unittests/client/logic/api/SaveFileConfigTest.cpp @@ -9,7 +9,7 @@ #include "gtest/gtest.h" #include "LogTestUtils.h" -#include "ramses-logic/SaveFileConfig.h" +#include "ramses/client/SaveFileConfig.h" #include "impl/SaveFileConfigImpl.h" #include "fmt/format.h" @@ -24,31 +24,34 @@ namespace ramses::internal config.setMetadataString("metadata"); config.setExporterVersion(1u, 2u, 3u, 4u); config.setValidationEnabled(false); + config.setCompressionEnabled(true); config.setLuaSavingMode(ELuaSavingMode::SourceAndByteCode); } static void checkValues(const SaveFileConfig& config) { - EXPECT_EQ("metadata", config.m_impl->getMetadataString()); - EXPECT_EQ(1u, config.m_impl->getExporterMajorVersion()); - EXPECT_EQ(2u, config.m_impl->getExporterMinorVersion()); - EXPECT_EQ(3u, config.m_impl->getExporterPatchVersion()); - EXPECT_EQ(4u, config.m_impl->getExporterFileFormatVersion()); - EXPECT_FALSE(config.m_impl->getValidationEnabled()); - EXPECT_EQ(ELuaSavingMode::SourceAndByteCode, config.m_impl->getLuaSavingMode()); + EXPECT_EQ("metadata", config.impl().getMetadataString()); + EXPECT_EQ(1u, config.impl().getExporterVersion().major); + EXPECT_EQ(2u, config.impl().getExporterVersion().minor); + EXPECT_EQ(3u, config.impl().getExporterVersion().patch); + EXPECT_EQ(4u, config.impl().getExporterVersion().fileFormat); + EXPECT_FALSE(config.impl().getValidationEnabled()); + EXPECT_EQ(ELuaSavingMode::SourceAndByteCode, config.impl().getLuaSavingMode()); + EXPECT_EQ("'metadata' exporter:1.2.3.4 validate:false compress:true lua:2", fmt::to_string(config.impl())); } }; TEST_F(ASaveFileConfig, HasDefaultValues) { SaveFileConfig config; - EXPECT_TRUE(config.m_impl->getMetadataString().empty()); - EXPECT_EQ(0u, config.m_impl->getExporterMajorVersion()); - EXPECT_EQ(0u, config.m_impl->getExporterMinorVersion()); - EXPECT_EQ(0u, config.m_impl->getExporterPatchVersion()); - EXPECT_EQ(0u, config.m_impl->getExporterFileFormatVersion()); - EXPECT_TRUE(config.m_impl->getValidationEnabled()); - EXPECT_EQ(ELuaSavingMode::SourceAndByteCode, config.m_impl->getLuaSavingMode()); + EXPECT_TRUE(config.impl().getMetadataString().empty()); + EXPECT_EQ(0u, config.impl().getExporterVersion().major); + EXPECT_EQ(0u, config.impl().getExporterVersion().minor); + EXPECT_EQ(0u, config.impl().getExporterVersion().patch); + EXPECT_EQ(0u, config.impl().getExporterVersion().fileFormat); + EXPECT_TRUE(config.impl().getValidationEnabled()); + EXPECT_EQ(ELuaSavingMode::SourceAndByteCode, config.impl().getLuaSavingMode()); + EXPECT_EQ("'' exporter:0.0.0.0 validate:true compress:false lua:2", fmt::to_string(config.impl())); } TEST_F(ASaveFileConfig, IsCopied) diff --git a/client/logic/unittests/api/SkinBindingTest.cpp b/tests/unittests/client/logic/api/SkinBindingTest.cpp similarity index 63% rename from client/logic/unittests/api/SkinBindingTest.cpp rename to tests/unittests/client/logic/api/SkinBindingTest.cpp index 2dc3f116d..6f78f44c5 100644 --- a/client/logic/unittests/api/SkinBindingTest.cpp +++ b/tests/unittests/client/logic/api/SkinBindingTest.cpp @@ -9,16 +9,16 @@ #include "LogicEngineTest_Base.h" #include "RamsesTestUtils.h" #include "SerializationTestUtils.h" -#include "WithTempDirectory.h" -#include "ramses-logic/SkinBinding.h" -#include "impl/LogicEngineImpl.h" -#include "impl/SkinBindingImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "internals/DeserializationMap.h" +#include "ramses/client/logic/SkinBinding.h" +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/ErrorReporting.h" -#include "generated/SkinBindingGen.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/flatbuffers/generated/SkinBindingGen.h" #include "glm/gtc/type_ptr.hpp" namespace ramses::internal @@ -29,19 +29,19 @@ namespace ramses::internal ASkinBinding() : m_appearance{ m_scene->createAppearance(createTestEffect()) } { - m_appearance->getEffect().findUniformInput("jointMat", m_uniform); + m_uniform = m_appearance->getEffect().findUniformInput("jointMat"); // add some transformations to the joints before calculating inverse mats and creating skin m_jointNodes[0]->setTranslation({1.f, 2.f, 3.f}); - m_jointNodes[1]->setRotation({10.f, 20.f, 30.f}); + m_jointNodes[1]->setRotation({10.f, 20.f, 30.f}, ERotationType::Euler_XYZ); m_skin = createSkinBinding(); } protected: - const ramses::Effect& createTestEffect() + const Effect& createTestEffect() { - ramses::EffectDescription effectDesc; + EffectDescription effectDesc; effectDesc.setVertexShader(m_vertShader.data()); effectDesc.setFragmentShader(m_fragShader.data()); return *m_scene->createEffect(effectDesc); @@ -55,7 +55,7 @@ namespace ramses::internal m_jointNodes[0]->getInverseModelMatrix(inverseMats[0]); m_jointNodes[1]->getInverseModelMatrix(inverseMats[1]); - return m_logicEngine.createSkinBinding(m_joints, inverseMats, *m_appearanceBinding, m_uniform, "skin"); + return m_logicEngine->createSkinBinding(m_joints, inverseMats, *m_appearanceBinding, *m_uniform, "skin"); } // this VS is not capable of vertex skinning but that is not needed for the tests here @@ -78,12 +78,12 @@ namespace ramses::internal gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); })"; - std::vector m_jointNodes{ m_scene->createNode(), m_scene->createNode() }; - std::vector m_joints{ m_logicEngine.createRamsesNodeBinding(*m_jointNodes[0]), m_logicEngine.createRamsesNodeBinding(*m_jointNodes[1]) }; + std::vector m_jointNodes{ m_scene->createNode(), m_scene->createNode() }; + std::vector m_joints{ m_logicEngine->createNodeBinding(*m_jointNodes[0]), m_logicEngine->createNodeBinding(*m_jointNodes[1]) }; - ramses::Appearance* m_appearance{ m_scene->createAppearance(createTestEffect()) }; - RamsesAppearanceBinding* m_appearanceBinding{ m_logicEngine.createRamsesAppearanceBinding(*m_appearance) }; - ramses::UniformInput m_uniform; + Appearance* m_appearance{ m_scene->createAppearance(createTestEffect()) }; + AppearanceBinding* m_appearanceBinding{ m_logicEngine->createAppearanceBinding(*m_appearance) }; + std::optional m_uniform { std::nullopt }; SkinBinding* m_skin = nullptr; }; @@ -93,10 +93,10 @@ namespace ramses::internal EXPECT_EQ(m_appearanceBinding, &m_skin->getAppearanceBinding()); const auto& skinConst = *m_skin; EXPECT_EQ(m_appearanceBinding, &skinConst.getAppearanceBinding()); - const auto& skinImplConst = m_skin->m_skinBinding; - EXPECT_EQ(&m_appearanceBinding->m_appearanceBinding, &skinImplConst.getAppearanceBinding()); + const auto& skinImplConst = m_skin->impl(); + EXPECT_EQ(&m_appearanceBinding->impl(), &skinImplConst.getAppearanceBinding()); - EXPECT_EQ(ramses::EDataType::Matrix44F, *m_skin->getAppearanceUniformInput().getDataType()); + EXPECT_EQ(EDataType::Matrix44F, m_skin->getAppearanceUniformInput().getDataType()); EXPECT_EQ(2u, m_skin->getAppearanceUniformInput().getElementCount()); } @@ -108,7 +108,7 @@ namespace ramses::internal TEST_F(ASkinBinding, SetsBoundUniformFromInitialJoints) { - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); // initially inverse binding mats are equal to the inverse transformation mats of joints // so result should be identity matrices @@ -119,8 +119,8 @@ namespace ramses::internal 0.f, 0.f, 0.f, 1.f }; - std::array uniformData{}; - m_appearance->getInputValue(m_uniform, 2u, uniformData.data()); + std::array uniformData{}; + m_appearance->getInputValue(*m_uniform, 2u, uniformData.data()); const matrix44f mat1 = uniformData[0]; const matrix44f mat2 = uniformData[1]; @@ -133,9 +133,9 @@ namespace ramses::internal TEST_F(ASkinBinding, UpdatesBoundUniformOnJointChange) { - m_jointNodes[0]->setRotation({-1.f, -2.f, -3.f}); + m_jointNodes[0]->setRotation({-1.f, -2.f, -3.f}, ERotationType::Euler_XYZ); m_jointNodes[1]->setTranslation({-1.f, -2.f, -3.f}); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const matrix44f expectedMat1 = { 0.998f, -0.0523f, 0.0349f, 0.f, @@ -150,8 +150,8 @@ namespace ramses::internal -1.f, -2.f, -3.f, 1.f }; - std::array uniformData{}; - m_appearance->getInputValue(m_uniform, 2u, uniformData.data()); + std::array uniformData{}; + m_appearance->getInputValue(*m_uniform, 2u, uniformData.data()); const matrix44f mat1 = uniformData[0]; const matrix44f mat2 = uniformData[1]; @@ -180,9 +180,9 @@ namespace ramses::internal { { const auto fbLogicObject = rlogic_serialization::CreateLogicObject(m_flatBufferBuilder, - (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u, 0u, 0u); + (issue == ESerializationIssue::MissingName ? 0 : m_flatBufferBuilder.CreateString("name")), 1u); - const std::vector jointIds{ 0u, 1u }; + const std::vector jointIds{ 2u, 3u }; std::vector inverseBindMatData; inverseBindMatData.resize((issue == ESerializationIssue::NumJointsMismatchMatrices ? 16u : 32u), 0.f); @@ -191,17 +191,21 @@ namespace ramses::internal fbLogicObject, (issue == ESerializationIssue::MissingJoints ? 0u : m_flatBufferBuilder.CreateVector(jointIds)), m_flatBufferBuilder.CreateVector(inverseBindMatData), - 2u, + 4u, (issue == ESerializationIssue::InvalidUniform ? m_flatBufferBuilder.CreateString("wrongUniform") : m_flatBufferBuilder.CreateString("jointMat"))); m_flatBufferBuilder.Finish(fbSkinBinding); } - DeserializationMap deserializationMap; - deserializationMap.storeLogicObject(0u, m_joints[0]->m_nodeBinding); - if (issue != ESerializationIssue::UnresolvedJointNodeBinding) - deserializationMap.storeLogicObject(1u, m_joints[1]->m_nodeBinding); - if (issue != ESerializationIssue::UnresolvedAppearanceBinding) - deserializationMap.storeLogicObject(2u, m_appearanceBinding->m_appearanceBinding); + DeserializationMap deserializationMap{ m_scene->impl() }; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + deserializationMap.storeLogicObject(sceneObjectId_t{ 2u }, const_cast(m_joints[0]->LogicObject::impl())); + if (issue != ESerializationIssue::UnresolvedJointNodeBinding) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + deserializationMap.storeLogicObject(sceneObjectId_t{ 3u }, const_cast(m_joints[1]->LogicObject::impl())); + } + if (issue != ESerializationIssue::UnresolvedAppearanceBinding) { + deserializationMap.storeLogicObject(sceneObjectId_t{ 4u }, m_appearanceBinding->impl()); + } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); return SkinBindingImpl::Deserialize(serialized, m_errorReporting, deserializationMap); @@ -214,63 +218,48 @@ namespace ramses::internal TEST_F(ASkinBinding_SerializationLifecycle, CanSerializeWithNoIssue) { EXPECT_TRUE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_F(ASkinBinding_SerializationLifecycle, ReportsSerializationError_MissingName) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::MissingName)); - ASSERT_EQ(2u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LogicObject base from serialized data: missing name!"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "Fatal error during loading of SkinBinding from serialized data: missing name and/or ID!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of SkinBinding from serialized data: missing name and/or ID!"); } TEST_F(ASkinBinding_SerializationLifecycle, ReportsSerializationError_MissingJoints) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::MissingJoints)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of SkinBinding from serialized data: missing or corrupted joints and/or inverse matrices data!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of SkinBinding from serialized data: missing or corrupted joints and/or inverse matrices data!"); } TEST_F(ASkinBinding_SerializationLifecycle, ReportsSerializationError_NumJointsMismatchMatrices) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::NumJointsMismatchMatrices)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of SkinBinding from serialized data: missing or corrupted joints and/or inverse matrices data!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of SkinBinding from serialized data: missing or corrupted joints and/or inverse matrices data!"); } TEST_F(ASkinBinding_SerializationLifecycle, ReportsSerializationError_UnresolvedJointNodeBinding) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::UnresolvedJointNodeBinding)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of SkinBinding from serialized data: could not resolve referenced node binding!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of SkinBinding from serialized data: could not resolve referenced node binding!"); } TEST_F(ASkinBinding_SerializationLifecycle, ReportsSerializationError_UnresolvedAppearanceBinding) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::UnresolvedAppearanceBinding)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of SkinBinding from serialized data: could not resolve referenced appearance binding!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of SkinBinding from serialized data: could not resolve referenced appearance binding!"); } TEST_F(ASkinBinding_SerializationLifecycle, ReportsSerializationError_InvalidUniform) { EXPECT_FALSE(deserializeSerializedDataWithIssue(ASkinBinding_SerializationLifecycle::ESerializationIssue::InvalidUniform)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of SkinBinding from serialized data: invalid or mismatching uniform input!"); - } - - TEST_F(ASkinBinding_SerializationLifecycle, FailsToLoadWhenNoSceneProvided) - { - { - createSkinBinding(); - ASSERT_TRUE(SaveToFileWithoutValidation(m_logicEngine, "binding.bin")); - } - - { - EXPECT_FALSE(m_logicEngine.loadFromFile("binding.bin")); - ASSERT_EQ(m_logicEngine.getErrors().size(), 1u); - EXPECT_EQ(m_logicEngine.getErrors()[0].message, "Fatal error during loading from file! File contains references to Ramses objects but no Ramses scene was provided!"); - } + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of SkinBinding from serialized data: invalid or mismatching uniform input!"); } } diff --git a/client/logic/unittests/api/TimerNodeTest.cpp b/tests/unittests/client/logic/api/TimerNodeTest.cpp similarity index 63% rename from client/logic/unittests/api/TimerNodeTest.cpp rename to tests/unittests/client/logic/api/TimerNodeTest.cpp index a6787ce08..4a3a47499 100644 --- a/client/logic/unittests/api/TimerNodeTest.cpp +++ b/tests/unittests/client/logic/api/TimerNodeTest.cpp @@ -7,73 +7,71 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "WithTempDirectory.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LuaScript.h" -#include "impl/TimerNodeImpl.h" -#include "impl/PropertyImpl.h" -#include "internals/ErrorReporting.h" -#include "internals/SerializationMap.h" -#include "internals/DeserializationMap.h" -#include "internals/TypeData.h" -#include "internals/EPropertySemantics.h" -#include "generated/TimerNodeGen.h" + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "impl/logic/TimerNodeImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/SerializationMap.h" +#include "internal/logic/DeserializationMap.h" +#include "internal/logic/TypeData.h" +#include "internal/logic/EPropertySemantics.h" +#include "internal/logic/flatbuffers/generated/TimerNodeGen.h" #include "flatbuffers/flatbuffers.h" +#include "LogicEngineTest_Base.h" #include #include namespace ramses::internal { - class ATimerNode : public ::testing::Test + class ATimerNode : public ALogicEngine { - protected: - LogicEngine m_logicEngine{ ramses::EFeatureLevel_Latest }; }; TEST_F(ATimerNode, IsCreated) { - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); + expectNoError(); ASSERT_NE(nullptr, timerNode); - EXPECT_EQ(timerNode, m_logicEngine.findByName("timerNode")); + EXPECT_EQ(timerNode, m_logicEngine->findObject("timerNode")); EXPECT_EQ("timerNode", timerNode->getName()); } TEST_F(ATimerNode, IsDestroyed) { - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); - EXPECT_TRUE(m_logicEngine.destroy(*timerNode)); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); - EXPECT_EQ(nullptr, m_logicEngine.findByName("timerNode")); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); + EXPECT_TRUE(m_logicEngine->destroy(*timerNode)); + expectNoError(); + EXPECT_EQ(nullptr, m_logicEngine->findObject("timerNode")); } TEST_F(ATimerNode, FailsToBeDestroyedIfFromOtherLogicInstance) { - auto timerNode = m_logicEngine.createTimerNode("timerNode"); + auto timerNode = m_logicEngine->createTimerNode("timerNode"); - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_scene->createLogicEngine(); EXPECT_FALSE(otherEngine.destroy(*timerNode)); - ASSERT_FALSE(otherEngine.getErrors().empty()); - EXPECT_EQ("Failed to destroy object 'timerNode [Id=1]', cannot find it in this LogicEngine instance.", otherEngine.getErrors().front().message); + EXPECT_EQ("Failed to destroy object 'timerNode [LogicObject ScnObjId=9]', cannot find it in this LogicEngine instance.", getLastErrorMessage()); } TEST_F(ATimerNode, ChangesName) { - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); timerNode->setName("an"); EXPECT_EQ("an", timerNode->getName()); - EXPECT_EQ(timerNode, m_logicEngine.findByName("an")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + EXPECT_EQ(timerNode, m_logicEngine->findObject("an")); + expectNoError(); } TEST_F(ATimerNode, HasPropertiesAfterCreation) { - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); const auto rootIn = timerNode->getInputs(); EXPECT_EQ("", rootIn->getName()); @@ -90,69 +88,112 @@ namespace ramses::internal TEST_F(ATimerNode, OutputsTicker_userTicker) { - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); EXPECT_TRUE(timerNode->getInputs()->getChild(0u)->set(1000000)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(1000000, *timerNode->getOutputs()->getChild(0u)->get()); // +0.5 second EXPECT_TRUE(timerNode->getInputs()->getChild(0u)->set(1500000)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(1500000, *timerNode->getOutputs()->getChild(0u)->get()); // no change EXPECT_TRUE(timerNode->getInputs()->getChild(0u)->set(1500000)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(1500000, *timerNode->getOutputs()->getChild(0u)->get()); // +10 second EXPECT_TRUE(timerNode->getInputs()->getChild(0u)->set(11500000)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); EXPECT_EQ(11500000, *timerNode->getOutputs()->getChild(0u)->get()); } TEST_F(ATimerNode, OutputsTicker_autoTicker) { - const auto timerNode = m_logicEngine.createTimerNode("timerNode"); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); // auto ticker is enabled if ticker input == 0 EXPECT_TRUE(timerNode->getInputs()->getChild(0u)->set(0)); - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); const auto initialTicker = *timerNode->getOutputs()->getChild(0u)->get(); EXPECT_TRUE(initialTicker > 0); auto ticker = initialTicker; for (int i = 0; i < 10; ++i) { - EXPECT_TRUE(m_logicEngine.update()); + EXPECT_TRUE(m_logicEngine->update()); + const auto nextTicker = *timerNode->getOutputs()->getChild(0u)->get(); + EXPECT_TRUE(nextTicker >= ticker); + ticker = nextTicker; + std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); + } + + // check that auto ticker is actually progressing + EXPECT_GT(ticker, initialTicker); + } + + TEST_F(ATimerNode, OutputsTickerInAutoModeWhenConnectedToInterface) + { + const std::string_view interfaceSrc = R"( + function interface(inputs) + inputs.someParam = Type:Int32() + inputs.ticker = Type:Int64() + end + )"; + const auto luaInterface = m_logicEngine->createLuaInterface(interfaceSrc, "intf"); + const auto timerNode = m_logicEngine->createTimerNode("timerNode"); + + EXPECT_TRUE(m_logicEngine->link(*luaInterface->getOutputs()->getChild("ticker"), *timerNode->getInputs()->getChild("ticker_us"))); + + EXPECT_TRUE(m_logicEngine->update()); + const auto initialTicker = *timerNode->getOutputs()->getChild(0u)->get(); + EXPECT_TRUE(initialTicker > 0); + + // no value set, expect ticker running in auto + int64_t ticker = initialTicker; + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(m_logicEngine->update()); const auto nextTicker = *timerNode->getOutputs()->getChild(0u)->get(); EXPECT_TRUE(nextTicker >= ticker); ticker = nextTicker; std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); } + // check that auto ticker is actually progressing + EXPECT_GT(ticker, initialTicker); + + // explicitly set to 0 and expect no change + EXPECT_TRUE(luaInterface->getInputs()->getChild("ticker")->set(0)); + for (int i = 0; i < 10; ++i) + { + EXPECT_TRUE(m_logicEngine->update()); + const auto nextTicker = *timerNode->getOutputs()->getChild(0u)->get(); + EXPECT_TRUE(nextTicker >= ticker); + ticker = nextTicker; + std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); + } // check that auto ticker is actually progressing EXPECT_GT(ticker, initialTicker); } TEST_F(ATimerNode, CanBeSerializedAndDeserialized) { - WithTempDirectory tempDir; + withTempDirectory(); { - LogicEngine otherEngine{ m_logicEngine.getFeatureLevel() }; + auto& otherEngine = *m_logicEngine; otherEngine.createTimerNode("timerNode"); - SaveFileConfig configNoValidation; - configNoValidation.setValidationEnabled(false); - ASSERT_TRUE(otherEngine.saveToFile("logic_timerNode.bin", configNoValidation)); + ASSERT_TRUE(saveToFileWithoutValidation("logic_timerNode.bin")); } - ASSERT_TRUE(m_logicEngine.loadFromFile("logic_timerNode.bin")); - EXPECT_TRUE(m_logicEngine.getErrors().empty()); + ASSERT_TRUE(recreateFromFile("logic_timerNode.bin")); + expectNoError(); - EXPECT_EQ(1u, m_logicEngine.getCollection().size()); - const auto timerNode = m_logicEngine.findByName("timerNode"); + EXPECT_EQ(1u, m_logicEngine->getCollection().size()); + const auto timerNode = m_logicEngine->findObject("timerNode"); ASSERT_TRUE(timerNode); EXPECT_EQ("timerNode", timerNode->getName()); @@ -189,7 +230,7 @@ namespace ramses::internal { flatbuffers::FlatBufferBuilder flatBufferBuilder; SerializationMap serializationMap; - DeserializationMap deserializationMap; + DeserializationMap deserializationMap{ m_scene->impl() }; { HierarchicalTypeData inputs = MakeStruct("", {}); @@ -236,14 +277,14 @@ namespace ramses::internal TEST_F(ATimerNode_SerializationLifecycle, FailsDeserializationIfEssentialDataMissing) { EXPECT_TRUE(deserializeSerializedDataWithIssue(ATimerNode_SerializationLifecycle::ESerializationIssue::AllValid)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); for (const auto issue : { ESerializationIssue::NameMissing, ESerializationIssue::IdMissing, ESerializationIssue::RootInMissing, ESerializationIssue::RootOutMissing }) { EXPECT_FALSE(deserializeSerializedDataWithIssue(issue)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of TimerNode from serialized data: missing name, id or in/out property data!", m_errorReporting.getErrors().back().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of TimerNode from serialized data: missing name, id or in/out property data!", m_errorReporting.getError()->message); + m_errorReporting.reset(); } } @@ -252,9 +293,9 @@ namespace ramses::internal for (const auto issue : { ESerializationIssue::PropertyInMissing, ESerializationIssue::PropertyOutMissing, ESerializationIssue::PropertyInWrongName, ESerializationIssue::PropertyOutWrongName }) { EXPECT_FALSE(deserializeSerializedDataWithIssue(issue)); - ASSERT_FALSE(m_errorReporting.getErrors().empty()); - EXPECT_EQ("Fatal error during loading of TimerNode 'timerNode': missing or invalid properties!", m_errorReporting.getErrors().front().message); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ("Fatal error during loading of TimerNode 'timerNode': missing or invalid properties!", m_errorReporting.getError()->message); + m_errorReporting.reset(); } } } diff --git a/client/logic/unittests/internal/ApiObjectsTest.cpp b/tests/unittests/client/logic/internal/ApiObjectsTest.cpp similarity index 58% rename from client/logic/unittests/internal/ApiObjectsTest.cpp rename to tests/unittests/client/logic/internal/ApiObjectsTest.cpp index df760c276..a7786ad12 100644 --- a/client/logic/unittests/internal/ApiObjectsTest.cpp +++ b/tests/unittests/client/logic/internal/ApiObjectsTest.cpp @@ -8,47 +8,50 @@ #include "gtest/gtest.h" -#include "generated/LogicEngineGen.h" - -#include "internals/ApiObjects.h" -#include "internals/SolState.h" -#include "internals/ErrorReporting.h" - -#include "impl/LogicEngineImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/LuaModuleImpl.h" -#include "impl/LuaScriptImpl.h" -#include "impl/LuaInterfaceImpl.h" -#include "impl/RamsesNodeBindingImpl.h" -#include "impl/RamsesAppearanceBindingImpl.h" -#include "impl/RamsesCameraBindingImpl.h" -#include "impl/RamsesRenderPassBindingImpl.h" -#include "impl/RamsesRenderGroupBindingImpl.h" -#include "impl/RamsesMeshNodeBindingImpl.h" -#include "impl/DataArrayImpl.h" -#include "impl/AnchorPointImpl.h" -#include "impl/SkinBindingImpl.h" - -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/RenderPass.h" +#include "internal/logic/flatbuffers/generated/LogicEngineGen.h" + +#include "internal/logic/ApiObjects.h" +#include "internal/logic/SolState.h" +#include "impl/ErrorReporting.h" + +#include "impl/logic/LogicEngineImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/LuaModuleImpl.h" +#include "impl/logic/LuaScriptImpl.h" +#include "impl/logic/LuaInterfaceImpl.h" +#include "impl/logic/NodeBindingImpl.h" +#include "impl/logic/AppearanceBindingImpl.h" +#include "impl/logic/CameraBindingImpl.h" +#include "impl/logic/RenderPassBindingImpl.h" +#include "impl/logic/RenderGroupBindingImpl.h" +#include "impl/logic/MeshNodeBindingImpl.h" +#include "impl/logic/DataArrayImpl.h" +#include "impl/logic/AnchorPointImpl.h" +#include "impl/logic/AnimationNodeImpl.h" +#include "impl/logic/SkinBindingImpl.h" +#include "impl/logic/TimerNodeImpl.h" + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/Scene.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/RenderPass.h" #include "RamsesTestUtils.h" #include "LogTestUtils.h" #include "SerializationTestUtils.h" @@ -57,7 +60,7 @@ namespace ramses::internal { - class AnApiObjects : public ::testing::TestWithParam + class AnApiObjects : public ::testing::TestWithParam { protected: AnApiObjects() @@ -65,14 +68,15 @@ namespace ramses::internal m_renderGroup->addMeshNode(*m_meshNode); } - ErrorReporting m_errorReporting; - ApiObjects m_apiObjects{ GetParam() }; + RamsesTestSetup m_ramses; + ramses::Scene* m_scene = { m_ramses.createScene() }; + ApiObjects m_apiObjects{ GetParam(), m_scene->impl() }; + + ErrorReporting m_errorReporting; flatbuffers::FlatBufferBuilder m_flatBufferBuilder; SerializationTestUtils m_testUtils{ m_flatBufferBuilder }; ::testing::StrictMock m_resolverMock; - RamsesTestSetup m_ramses; - ramses::Scene* m_scene = { m_ramses.createScene() }; ramses::Node* m_node = { m_scene->createNode() }; ramses::PerspectiveCamera* m_camera = { m_scene->createPerspectiveCamera() }; ramses::Appearance* m_appearance = { &RamsesTestSetup::CreateTrivialTestAppearance(*m_scene) }; @@ -116,43 +120,57 @@ namespace ramses::internal LuaInterface* createInterface(ApiObjects& apiObjects) { - auto intf = apiObjects.createLuaInterface(m_valid_empty_interface, {}, "intf", m_errorReporting, true); + auto intf = apiObjects.createLuaInterface(m_valid_empty_interface, {}, "intf", m_errorReporting); EXPECT_NE(nullptr, intf); return intf; } AnchorPoint* createAnchorPoint() { - const auto node = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto camera = m_apiObjects.createRamsesCameraBinding(*m_camera, true, "camera"); + auto* node = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "node"); + auto* camera = m_apiObjects.createCameraBinding(*m_camera, true, "camera"); EXPECT_TRUE(node && camera); - return m_apiObjects.createAnchorPoint(node->m_nodeBinding, camera->m_cameraBinding, "anchor"); + return m_apiObjects.createAnchorPoint(node->impl(), camera->impl(), "anchor"); } - RamsesRenderGroupBinding* createRenderGroupBinding() + RenderGroupBinding* createRenderGroupBinding() { - RamsesRenderGroupBindingElements elements; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); - return m_apiObjects.createRamsesRenderGroupBinding(*m_renderGroup, elements, "renderGroupBinding"); + return m_apiObjects.createRenderGroupBinding(*m_renderGroup, elements, "renderGroupBinding"); } - static SkinBinding* createSkinBinding(const RamsesNodeBinding& joint, const RamsesAppearanceBinding& appearance, ApiObjects& apiObjects) + static SkinBinding* createSkinBinding(const NodeBinding& joint, AppearanceBinding& appearance, ApiObjects& apiObjects) { - ramses::UniformInput uniform; - appearance.getRamsesAppearance().getEffect().findUniformInput("jointMat", uniform); - EXPECT_TRUE(uniform.isValid()); - return apiObjects.createSkinBinding({ &joint.m_nodeBinding }, { matrix44f{ 0.f } }, appearance.m_appearanceBinding, uniform, "skin"); + const auto optUniform = appearance.getRamsesAppearance().getEffect().findUniformInput("jointMat"); + EXPECT_TRUE(optUniform.has_value()); + assert(optUniform != std::nullopt); + return apiObjects.createSkinBinding({ &joint.impl() }, { matrix44f{ 0.f } }, appearance.impl(), *optUniform, "skin"); } SkinBinding* createSkinBinding(ApiObjects& apiObjects) { - const auto node = apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeForSkin"); - const auto appearance = apiObjects.createRamsesAppearanceBinding(*m_appearance, "appearanceForSkin"); + const auto* node = apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "nodeForSkin"); + auto* appearance = apiObjects.createAppearanceBinding(*m_appearance, "appearanceForSkin"); return createSkinBinding(*node, *appearance, apiObjects); } + const LogicObject* getApiObjectById(uint64_t id) + { + const auto& objs = m_apiObjects.getApiObjectContainer(); + const auto it = std::find_if(objs.cbegin(), objs.cend(), [id](const auto o) { return o->getSceneObjectId().getValue() == id; }); + const auto* obj = (it == objs.cend() ? nullptr : *it); + + const auto& objsOwn = m_apiObjects.getApiObjectOwningContainer(); + const auto it2 = std::find_if(objsOwn.cbegin(), objsOwn.cend(), [id](const auto& o) { return o->getSceneObjectId().getValue() == id; }); + const auto* objOwn = (it2 == objsOwn.cend() ? nullptr : it2->get()); + + EXPECT_EQ(obj, objOwn); + return obj; + } + // Silence logs, unless explicitly enabled, to reduce spam and speed up tests - ScopedLogContextLevel m_silenceLogs{ ELogLevel::Off }; + ScopedLogContextLevel m_silenceLogs{ CONTEXT_CLIENT, ELogLevel::Off }; size_t m_emptySerializedSizeTotal{164u}; }; @@ -161,12 +179,12 @@ namespace ramses::internal INSTANTIATE_TEST_SUITE_P( AnApiObjectsTests, AnApiObjects, - ramses::internal::GetFeatureLevelTestValues()); + GetFeatureLevelTestValues()); TEST_P(AnApiObjects, CreatesScriptFromValidLuaWithoutErrors) { const LuaScript* script = createScript(); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(script, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(script, m_apiObjects.getApiObjectContainer().back()); } @@ -181,14 +199,14 @@ namespace ramses::internal TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingScriptFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; LuaScript* script = createScript(otherInstance, m_valid_empty_script); EXPECT_EQ(script, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(script, otherInstance.getApiObjectContainer().back()); ASSERT_FALSE(m_apiObjects.destroy(*script, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'script [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, script); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'script [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, script); // Did not affect existence in otherInstance! EXPECT_EQ(script, otherInstance.getApiObjectOwningContainer().back().get()); @@ -198,7 +216,7 @@ namespace ramses::internal TEST_P(AnApiObjects, CreatesInterfaceFromValidLuaWithoutErrors) { const LuaInterface* intf = createInterface(); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(intf, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(intf, m_apiObjects.getApiObjectContainer().back()); } @@ -213,14 +231,14 @@ namespace ramses::internal TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingInterfaceFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; LuaInterface* intf = createInterface(otherInstance); EXPECT_EQ(intf, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(intf, otherInstance.getApiObjectContainer().back()); ASSERT_FALSE(m_apiObjects.destroy(*intf, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'intf [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, intf); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'intf [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, intf); // Did not affect existence in otherInstance! EXPECT_EQ(intf, otherInstance.getApiObjectOwningContainer().back().get()); @@ -232,7 +250,7 @@ namespace ramses::internal auto module = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); EXPECT_NE(nullptr, module); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); ASSERT_EQ(1u, m_apiObjects.getApiObjectContainer().size()); EXPECT_EQ(1u, m_apiObjects.getApiObjectContainer().size()); EXPECT_EQ(1u, m_apiObjects.getApiObjectOwningContainer().size()); @@ -241,217 +259,217 @@ namespace ramses::internal EXPECT_EQ(module, m_apiObjects.getApiObjectContainer().front()); } - TEST_P(AnApiObjects, CreatesRamsesNodeBindingWithoutErrors) + TEST_P(AnApiObjects, CreatesNodeBindingWithoutErrors) { - RamsesNodeBinding* ramsesNodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - EXPECT_NE(nullptr, ramsesNodeBinding); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); - EXPECT_EQ(ramsesNodeBinding, m_apiObjects.getApiObjectOwningContainer().back().get()); - EXPECT_EQ(ramsesNodeBinding, m_apiObjects.getApiObjectContainer().back()); + NodeBinding* nodeBinding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + EXPECT_NE(nullptr, nodeBinding); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + EXPECT_EQ(nodeBinding, m_apiObjects.getApiObjectOwningContainer().back().get()); + EXPECT_EQ(nodeBinding, m_apiObjects.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, DestroysRamsesNodeBindingWithoutErrors) + TEST_P(AnApiObjects, DestroysNodeBindingWithoutErrors) { - RamsesNodeBinding* ramsesNodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - ASSERT_NE(nullptr, ramsesNodeBinding); - m_apiObjects.destroy(*ramsesNodeBinding, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + NodeBinding* nodeBinding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + ASSERT_NE(nullptr, nodeBinding); + m_apiObjects.destroy(*nodeBinding, m_errorReporting); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); } - TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRamsesNodeBindingFromAnotherClassInstance) + TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingNodeBindingFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - RamsesNodeBinding* ramsesNodeBinding = otherInstance.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "NodeBinding"); - ASSERT_TRUE(ramsesNodeBinding); - EXPECT_EQ(ramsesNodeBinding, otherInstance.getApiObjectOwningContainer().back().get()); - EXPECT_EQ(ramsesNodeBinding, otherInstance.getApiObjectContainer().back()); - ASSERT_FALSE(m_apiObjects.destroy(*ramsesNodeBinding, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'NodeBinding [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, ramsesNodeBinding); + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + NodeBinding* nodeBinding = otherInstance.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "NodeBinding"); + ASSERT_TRUE(nodeBinding); + EXPECT_EQ(nodeBinding, otherInstance.getApiObjectOwningContainer().back().get()); + EXPECT_EQ(nodeBinding, otherInstance.getApiObjectContainer().back()); + ASSERT_FALSE(m_apiObjects.destroy(*nodeBinding, m_errorReporting)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'NodeBinding [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, nodeBinding); // Did not affect existence in otherInstance! - EXPECT_EQ(ramsesNodeBinding, otherInstance.getApiObjectOwningContainer().back().get()); - EXPECT_EQ(ramsesNodeBinding, otherInstance.getApiObjectContainer().back()); + EXPECT_EQ(nodeBinding, otherInstance.getApiObjectOwningContainer().back().get()); + EXPECT_EQ(nodeBinding, otherInstance.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, CreatesRamsesCameraBindingWithoutErrors) + TEST_P(AnApiObjects, CreatesCameraBindingWithoutErrors) { - RamsesCameraBinding* ramsesCameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, false, "CameraBinding"); - EXPECT_NE(nullptr, ramsesCameraBinding); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); - EXPECT_EQ(ramsesCameraBinding, m_apiObjects.getApiObjectOwningContainer().back().get()); - EXPECT_EQ(ramsesCameraBinding, m_apiObjects.getApiObjectContainer().back()); + CameraBinding* cameraBinding = m_apiObjects.createCameraBinding(*m_camera, false, "CameraBinding"); + EXPECT_NE(nullptr, cameraBinding); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + EXPECT_EQ(cameraBinding, m_apiObjects.getApiObjectOwningContainer().back().get()); + EXPECT_EQ(cameraBinding, m_apiObjects.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, DestroysRamsesCameraBindingWithoutErrors) + TEST_P(AnApiObjects, DestroysCameraBindingWithoutErrors) { - RamsesCameraBinding* ramsesCameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, "CameraBinding"); - ASSERT_NE(nullptr, ramsesCameraBinding); - m_apiObjects.destroy(*ramsesCameraBinding, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + CameraBinding* cameraBinding = m_apiObjects.createCameraBinding(*m_camera, true, "CameraBinding"); + ASSERT_NE(nullptr, cameraBinding); + m_apiObjects.destroy(*cameraBinding, m_errorReporting); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); } - TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRamsesCameraBindingFromAnotherClassInstance) + TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingCameraBindingFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - RamsesCameraBinding* ramsesCameraBinding = otherInstance.createRamsesCameraBinding(*m_camera, true, "CameraBinding"); - ASSERT_TRUE(ramsesCameraBinding); - EXPECT_EQ(ramsesCameraBinding, otherInstance.getApiObjectOwningContainer().back().get()); - EXPECT_EQ(ramsesCameraBinding, otherInstance.getApiObjectContainer().back()); - ASSERT_FALSE(m_apiObjects.destroy(*ramsesCameraBinding, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'CameraBinding [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, ramsesCameraBinding); + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + CameraBinding* cameraBinding = otherInstance.createCameraBinding(*m_camera, true, "CameraBinding"); + ASSERT_TRUE(cameraBinding); + EXPECT_EQ(cameraBinding, otherInstance.getApiObjectOwningContainer().back().get()); + EXPECT_EQ(cameraBinding, otherInstance.getApiObjectContainer().back()); + ASSERT_FALSE(m_apiObjects.destroy(*cameraBinding, m_errorReporting)); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'CameraBinding [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, cameraBinding); // Did not affect existence in otherInstance! - EXPECT_EQ(ramsesCameraBinding, otherInstance.getApiObjectOwningContainer().back().get()); - EXPECT_EQ(ramsesCameraBinding, otherInstance.getApiObjectContainer().back()); + EXPECT_EQ(cameraBinding, otherInstance.getApiObjectOwningContainer().back().get()); + EXPECT_EQ(cameraBinding, otherInstance.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, CreatesRamsesRenderPassBindingWithoutErrors) + TEST_P(AnApiObjects, CreatesRenderPassBindingWithoutErrors) { - RamsesRenderPassBinding* binding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, "RenderPassBinding"); + RenderPassBinding* binding = m_apiObjects.createRenderPassBinding(*m_renderPass, "RenderPassBinding"); EXPECT_NE(nullptr, binding); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(binding, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, m_apiObjects.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, DestroysRamsesRenderPassBindingWithoutErrors) + TEST_P(AnApiObjects, DestroysRenderPassBindingWithoutErrors) { - RamsesRenderPassBinding* binding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, "RenderPassBinding"); + RenderPassBinding* binding = m_apiObjects.createRenderPassBinding(*m_renderPass, "RenderPassBinding"); ASSERT_NE(nullptr, binding); m_apiObjects.destroy(*binding, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); } - TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRamsesRenderPassBindingFromAnotherClassInstance) + TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRenderPassBindingFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - RamsesRenderPassBinding* binding = otherInstance.createRamsesRenderPassBinding(*m_renderPass, "RenderPassBinding"); + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + RenderPassBinding* binding = otherInstance.createRenderPassBinding(*m_renderPass, "RenderPassBinding"); ASSERT_TRUE(binding); EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); ASSERT_FALSE(m_apiObjects.destroy(*binding, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'RenderPassBinding [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, binding); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'RenderPassBinding [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, binding); // Did not affect existence in otherInstance! EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, CreatesRamsesRenderGroupBindingWithoutErrors) + TEST_P(AnApiObjects, CreatesRenderGroupBindingWithoutErrors) { const auto binding = createRenderGroupBinding(); EXPECT_NE(nullptr, binding); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(binding, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, m_apiObjects.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, DestroysRamsesRenderGroupBindingWithoutErrors) + TEST_P(AnApiObjects, DestroysRenderGroupBindingWithoutErrors) { auto binding = createRenderGroupBinding(); ASSERT_NE(nullptr, binding); m_apiObjects.destroy(*binding, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); } - TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRamsesRenderGroupBindingFromAnotherClassInstance) + TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRenderGroupBindingFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - RamsesRenderGroupBindingElements elements; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode)); - auto binding = otherInstance.createRamsesRenderGroupBinding(*m_renderGroup, elements, "RenderGroupBinding"); + auto binding = otherInstance.createRenderGroupBinding(*m_renderGroup, elements, "RenderGroupBinding"); ASSERT_TRUE(binding); EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); ASSERT_FALSE(m_apiObjects.destroy(*binding, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'RenderGroupBinding [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, binding); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'RenderGroupBinding [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, binding); // Did not affect existence in otherInstance! EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, CreatesRamsesMeshNodeBindingWithoutErrors) + TEST_P(AnApiObjects, CreatesMeshNodeBindingWithoutErrors) { - const auto binding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + const auto binding = m_apiObjects.createMeshNodeBinding(*m_meshNode, "mb"); EXPECT_NE(nullptr, binding); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(binding, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, m_apiObjects.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, DestroysRamsesMeshNodeBindingWithoutErrors) + TEST_P(AnApiObjects, DestroysMeshNodeBindingWithoutErrors) { - auto binding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + auto binding = m_apiObjects.createMeshNodeBinding(*m_meshNode, "mb"); ASSERT_NE(nullptr, binding); m_apiObjects.destroy(*binding, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); } - TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRamsesMeshNodeBindingFromAnotherClassInstance) + TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingMeshNodeBindingFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - auto binding = otherInstance.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + auto binding = otherInstance.createMeshNodeBinding(*m_meshNode, "mb"); ASSERT_TRUE(binding); EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); EXPECT_FALSE(m_apiObjects.destroy(*binding, m_errorReporting)); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'mb [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, binding); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'mb [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, binding); // Did not affect existence in otherInstance! EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, CreatesRamsesAppearanceBindingWithoutErrors) + TEST_P(AnApiObjects, CreatesAppearanceBindingWithoutErrors) { - RamsesAppearanceBinding* binding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + AppearanceBinding* binding = m_apiObjects.createAppearanceBinding(*m_appearance, "AppearanceBinding"); EXPECT_NE(nullptr, binding); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(binding, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, m_apiObjects.getApiObjectContainer().back()); } - TEST_P(AnApiObjects, DestroysRamsesAppearanceBindingWithoutErrors) + TEST_P(AnApiObjects, DestroysAppearanceBindingWithoutErrors) { - RamsesAppearanceBinding* binding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + AppearanceBinding* binding = m_apiObjects.createAppearanceBinding(*m_appearance, "AppearanceBinding"); ASSERT_TRUE(binding); ASSERT_TRUE(m_apiObjects.destroy(*binding, m_errorReporting)); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); } - TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingRamsesAppearanceBindingFromAnotherClassInstance) + TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingAppearanceBindingFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - RamsesAppearanceBinding* binding = otherInstance.createRamsesAppearanceBinding(*m_appearance, "AppearanceBinding"); + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + AppearanceBinding* binding = otherInstance.createAppearanceBinding(*m_appearance, "AppearanceBinding"); ASSERT_TRUE(binding); EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(binding, otherInstance.getApiObjectContainer().back()); ASSERT_FALSE(m_apiObjects.destroy(*binding, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'AppearanceBinding [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, binding); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'AppearanceBinding [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, binding); // Did not affect existence in otherInstance! EXPECT_EQ(binding, otherInstance.getApiObjectOwningContainer().back().get()); @@ -463,7 +481,7 @@ namespace ramses::internal const std::vector data{ 1.f, 2.f, 3.f }; auto dataArray = m_apiObjects.createDataArray(data, "data"); EXPECT_NE(nullptr, dataArray); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); ASSERT_EQ(1u, m_apiObjects.getApiObjectContainer().size()); EXPECT_EQ(dataArray, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(dataArray, m_apiObjects.getApiObjectContainer().back()); @@ -476,7 +494,7 @@ namespace ramses::internal { auto dataArray = m_apiObjects.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); EXPECT_TRUE(m_apiObjects.destroy(*dataArray, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); @@ -492,53 +510,53 @@ namespace ramses::internal AnimationNodeConfig config; EXPECT_TRUE(config.addChannel({ "channel1", dataArray1, dataArray2 })); EXPECT_TRUE(config.addChannel({ "channel2", dataArray1, dataArray2, EInterpolationType::Cubic, dataArray3, dataArray4 })); - auto animNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); + auto animNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); EXPECT_FALSE(m_apiObjects.destroy(*dataArray1, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy data array 'data1', it is used in animation node 'animNode' channel 'channel1'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, dataArray1); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy data array 'data1', it is used in animation node 'animNode' channel 'channel1'"); + EXPECT_EQ(m_errorReporting.getError()->object, dataArray1); + m_errorReporting.reset(); EXPECT_FALSE(m_apiObjects.destroy(*dataArray2, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy data array 'data2', it is used in animation node 'animNode' channel 'channel1'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, dataArray2); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy data array 'data2', it is used in animation node 'animNode' channel 'channel1'"); + EXPECT_EQ(m_errorReporting.getError()->object, dataArray2); + m_errorReporting.reset(); EXPECT_FALSE(m_apiObjects.destroy(*dataArray3, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy data array 'data3', it is used in animation node 'animNode' channel 'channel2'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, dataArray3); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy data array 'data3', it is used in animation node 'animNode' channel 'channel2'"); + EXPECT_EQ(m_errorReporting.getError()->object, dataArray3); + m_errorReporting.reset(); EXPECT_FALSE(m_apiObjects.destroy(*dataArray4, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy data array 'data4', it is used in animation node 'animNode' channel 'channel2'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, dataArray4); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy data array 'data4', it is used in animation node 'animNode' channel 'channel2'"); + EXPECT_EQ(m_errorReporting.getError()->object, dataArray4); + m_errorReporting.reset(); // succeeds after destroying animation node EXPECT_TRUE(m_apiObjects.destroy(*animNode, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.destroy(*dataArray1, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*dataArray2, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*dataArray3, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*dataArray4, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_P(AnApiObjects, FailsToDestroyDataArrayFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; auto dataArray = otherInstance.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); ASSERT_NE(nullptr, dataArray); EXPECT_EQ(dataArray, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(dataArray, otherInstance.getApiObjectContainer().back()); EXPECT_FALSE(m_apiObjects.destroy(*dataArray, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'data [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, dataArray); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'data [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, dataArray); // Did not affect existence in otherInstance! EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); @@ -552,8 +570,8 @@ namespace ramses::internal ASSERT_NE(nullptr, dataArray); AnimationNodeConfig config; EXPECT_TRUE(config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear })); - auto animNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + auto animNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(animNode, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(animNode, m_apiObjects.getApiObjectContainer().back()); EXPECT_EQ(2u, m_apiObjects.getApiObjectContainer().size()); @@ -568,9 +586,9 @@ namespace ramses::internal ASSERT_NE(nullptr, dataArray); AnimationNodeConfig config; EXPECT_TRUE(config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear })); - auto animNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); + auto animNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); EXPECT_TRUE(m_apiObjects.destroy(*animNode, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); // did not affect data array EXPECT_TRUE(!m_apiObjects.getApiObjectContainer().empty()); @@ -580,18 +598,18 @@ namespace ramses::internal TEST_P(AnApiObjects, FailsToDestroyAnimationNodeFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; auto dataArray = otherInstance.createDataArray(std::vector{ 1.f, 2.f, 3.f }, "data"); ASSERT_NE(nullptr, dataArray); AnimationNodeConfig config; EXPECT_TRUE(config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear })); - auto animNode = otherInstance.createAnimationNode(*config.m_impl, "animNode"); + auto animNode = otherInstance.createAnimationNode(config.impl(), "animNode"); EXPECT_EQ(animNode, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(animNode, otherInstance.getApiObjectContainer().back()); EXPECT_FALSE(m_apiObjects.destroy(*animNode, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'animNode [Id=2]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, animNode); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'animNode [LogicObject ScnObjId=9]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, animNode); // Did not affect existence in otherInstance! EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); @@ -602,7 +620,7 @@ namespace ramses::internal TEST_P(AnApiObjects, CreatesTimerNode) { auto timerNode = m_apiObjects.createTimerNode("timerNode"); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); ASSERT_EQ(1u, m_apiObjects.getApiObjectOwningContainer().size()); EXPECT_EQ(timerNode, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_THAT(m_apiObjects.getApiObjectContainer(), ::testing::ElementsAre(timerNode)); @@ -613,7 +631,7 @@ namespace ramses::internal { auto timerNode = m_apiObjects.createTimerNode("timerNode"); EXPECT_TRUE(m_apiObjects.destroy(*timerNode, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectOwningContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); @@ -621,12 +639,12 @@ namespace ramses::internal TEST_P(AnApiObjects, FailsToDestroyTimerNodeFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; auto timerNode = otherInstance.createTimerNode("timerNode"); EXPECT_FALSE(m_apiObjects.destroy(*timerNode, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'timerNode [Id=1]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, timerNode); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'timerNode [LogicObject ScnObjId=8]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, timerNode); // Did not affect existence in otherInstance! EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); @@ -639,7 +657,7 @@ namespace ramses::internal { AnchorPoint* anchor = createAnchorPoint(); EXPECT_NE(nullptr, anchor); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(anchor, m_apiObjects.getApiObjectContainer().front()); EXPECT_EQ(anchor, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(anchor, m_apiObjects.getApiObjectContainer().back()); @@ -650,7 +668,7 @@ namespace ramses::internal AnchorPoint* anchor = createAnchorPoint(); ASSERT_NE(nullptr, anchor); m_apiObjects.destroy(*anchor, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_NE(anchor, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_NE(anchor, m_apiObjects.getApiObjectContainer().back()); @@ -658,17 +676,17 @@ namespace ramses::internal TEST_P(AnApiObjects, ProducesErrorsWhenDestroyingAnchorPointFromAnotherClassInstance) { - ApiObjects otherInstance{ GetParam() }; - const auto node = otherInstance.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto camera = otherInstance.createRamsesCameraBinding(*m_camera, true, "camera"); - AnchorPoint* anchor = otherInstance.createAnchorPoint(node->m_nodeBinding, camera->m_cameraBinding, "anchor"); + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; + auto* node = otherInstance.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "node"); + auto* camera = otherInstance.createCameraBinding(*m_camera, true, "camera"); + AnchorPoint* anchor = otherInstance.createAnchorPoint(node->impl(), camera->impl(), "anchor"); ASSERT_TRUE(anchor); EXPECT_EQ(anchor, otherInstance.getApiObjectOwningContainer().back().get()); EXPECT_EQ(anchor, otherInstance.getApiObjectContainer().back()); ASSERT_FALSE(m_apiObjects.destroy(*anchor, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy object 'anchor [Id=3]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, anchor); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy object 'anchor [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(m_errorReporting.getError()->object, anchor); // Did not affect existence in otherInstance! EXPECT_EQ(anchor, otherInstance.getApiObjectOwningContainer().back().get()); @@ -677,35 +695,35 @@ namespace ramses::internal TEST_P(AnApiObjects, FailsToDestroyNodeOrCameraBindingIfUsedInAnchorPoint) { - const auto node = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto camera = m_apiObjects.createRamsesCameraBinding(*m_camera, true, "camera"); - AnchorPoint* anchor = m_apiObjects.createAnchorPoint(node->m_nodeBinding, camera->m_cameraBinding, "anchor"); + auto* node = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "node"); + auto* camera = m_apiObjects.createCameraBinding(*m_camera, true, "camera"); + AnchorPoint* anchor = m_apiObjects.createAnchorPoint(node->impl(), camera->impl(), "anchor"); EXPECT_FALSE(m_apiObjects.destroy(*node, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy Ramses node binding 'node', it is used in anchor point 'anchor'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, node); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy Ramses node binding 'node', it is used in anchor point 'anchor'"); + EXPECT_EQ(m_errorReporting.getError()->object, node); + m_errorReporting.reset(); EXPECT_FALSE(m_apiObjects.destroy(*camera, m_errorReporting)); - EXPECT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy Ramses camera binding 'camera', it is used in anchor point 'anchor'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, camera); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy Ramses camera binding 'camera', it is used in anchor point 'anchor'"); + EXPECT_EQ(m_errorReporting.getError()->object, camera); + m_errorReporting.reset(); // succeeds after destroying anchor point EXPECT_TRUE(m_apiObjects.destroy(*anchor, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.destroy(*node, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*camera, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_P(AnApiObjects, CreatesSkinBindingWithoutErrors) { auto skin = createSkinBinding(m_apiObjects); ASSERT_NE(nullptr, skin); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_EQ(skin, m_apiObjects.getApiObjectContainer().front()); EXPECT_EQ(skin, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_EQ(skin, m_apiObjects.getApiObjectContainer().back()); @@ -716,7 +734,7 @@ namespace ramses::internal auto skin = createSkinBinding(m_apiObjects); ASSERT_NE(nullptr, skin); m_apiObjects.destroy(*skin, m_errorReporting); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_NE(skin, m_apiObjects.getApiObjectOwningContainer().back().get()); EXPECT_NE(skin, m_apiObjects.getApiObjectContainer().back()); @@ -727,13 +745,13 @@ namespace ramses::internal auto skin = createSkinBinding(m_apiObjects); ASSERT_NE(nullptr, skin); - ApiObjects otherInstance{ GetParam() }; + ApiObjects otherInstance{ GetParam(), m_scene->impl() }; ErrorReporting errorReporting; EXPECT_FALSE(otherInstance.destroy(*skin, errorReporting)); - ASSERT_EQ(errorReporting.getErrors().size(), 1u); - EXPECT_EQ(errorReporting.getErrors()[0].message, "Failed to destroy object 'skin [Id=3]', cannot find it in this LogicEngine instance."); - EXPECT_EQ(errorReporting.getErrors()[0].object, skin); + ASSERT_TRUE(errorReporting.getError().has_value()); + EXPECT_EQ(errorReporting.getError()->message, "Failed to destroy object 'skin [LogicObject ScnObjId=10]', cannot find it in this LogicEngine instance."); + EXPECT_EQ(errorReporting.getError()->object, skin); } TEST_P(AnApiObjects, FailsToDestroyNodeOrAppearanceBindingIfUsedInSkinBinding) @@ -741,45 +759,45 @@ namespace ramses::internal auto skin = createSkinBinding(m_apiObjects); ASSERT_NE(nullptr, skin); - const auto& nodes = m_apiObjects.getApiObjectContainer(); + const auto& nodes = m_apiObjects.getApiObjectContainer(); const auto it = std::find_if(nodes.cbegin(), nodes.cend(), [](const auto& n) { return n->getName() == "nodeForSkin"; }); ASSERT_TRUE(it != nodes.cend()); const auto nodeUsedInSkin = *it; - const auto& appearances = m_apiObjects.getApiObjectContainer(); + const auto& appearances = m_apiObjects.getApiObjectContainer(); const auto it2 = std::find_if(appearances.cbegin(), appearances.cend(), [](const auto& a) { return a->getName() == "appearanceForSkin"; }); ASSERT_TRUE(it2 != appearances.cend()); const auto appearanceUsedInSkin = *it2; EXPECT_FALSE(m_apiObjects.destroy(*nodeUsedInSkin, m_errorReporting)); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy Ramses node binding 'nodeForSkin', it is used in skin binding 'skin'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, nodeUsedInSkin); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy Ramses node binding 'nodeForSkin', it is used in skin binding 'skin'"); + EXPECT_EQ(m_errorReporting.getError()->object, nodeUsedInSkin); + m_errorReporting.reset(); EXPECT_FALSE(m_apiObjects.destroy(*appearanceUsedInSkin, m_errorReporting)); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Failed to destroy Ramses appearance binding 'appearanceForSkin', it is used in skin binding 'skin'"); - EXPECT_EQ(m_errorReporting.getErrors()[0].object, appearanceUsedInSkin); - m_errorReporting.clear(); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Failed to destroy Ramses appearance binding 'appearanceForSkin', it is used in skin binding 'skin'"); + EXPECT_EQ(m_errorReporting.getError()->object, appearanceUsedInSkin); + m_errorReporting.reset(); // succeeds after destroying skin binding EXPECT_TRUE(m_apiObjects.destroy(*skin, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); EXPECT_TRUE(m_apiObjects.destroy(*nodeUsedInSkin, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*appearanceUsedInSkin, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); } TEST_P(AnApiObjects, ProvidesEmptyCollections_WhenNothingWasCreated) { EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); - EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); - EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); - EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); - EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); - EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); - EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); + EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); + EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); + EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); + EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); + EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); + EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); EXPECT_TRUE(m_apiObjects.getApiObjectContainer().empty()); @@ -790,12 +808,12 @@ namespace ramses::internal const ApiObjects& apiObjectsConst = m_apiObjects; EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); - EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); - EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); - EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); - EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); - EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); - EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); + EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); + EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); + EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); + EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); + EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); + EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); EXPECT_TRUE(apiObjectsConst.getApiObjectContainer().empty()); @@ -829,36 +847,36 @@ namespace ramses::internal TEST_P(AnApiObjects, ProvidesNonEmptyNodeBindingsCollection_WhenNodeBindingsWereCreated) { - RamsesNodeBinding* binding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - ApiObjectContainer& nodes = m_apiObjects.getApiObjectContainer(); + NodeBinding* binding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); + ApiObjectContainer& nodes = m_apiObjects.getApiObjectContainer(); EXPECT_THAT(nodes, ::testing::ElementsAre(binding)); } TEST_P(AnApiObjects, ProvidesNonEmptyAppearanceBindingsCollection_WhenAppearanceBindingsWereCreated) { - RamsesAppearanceBinding* binding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, ""); - ApiObjectContainer& appearances = m_apiObjects.getApiObjectContainer(); + AppearanceBinding* binding = m_apiObjects.createAppearanceBinding(*m_appearance, ""); + ApiObjectContainer& appearances = m_apiObjects.getApiObjectContainer(); EXPECT_THAT(appearances, ::testing::ElementsAre(binding)); } TEST_P(AnApiObjects, ProvidesNonEmptyCameraBindingsCollection_WhenCameraBindingsWereCreated) { - RamsesCameraBinding* binding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, ""); - ApiObjectContainer& cameras = m_apiObjects.getApiObjectContainer(); + CameraBinding* binding = m_apiObjects.createCameraBinding(*m_camera, true, ""); + ApiObjectContainer& cameras = m_apiObjects.getApiObjectContainer(); EXPECT_THAT(cameras, ::testing::ElementsAre(binding)); } TEST_P(AnApiObjects, ProvidesNonEmptyRenderPassBindingsCollection_WhenRenderPassBindingsWereCreated) { - RamsesRenderPassBinding* binding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, ""); - ApiObjectContainer& renderPasses = m_apiObjects.getApiObjectContainer(); + RenderPassBinding* binding = m_apiObjects.createRenderPassBinding(*m_renderPass, ""); + ApiObjectContainer& renderPasses = m_apiObjects.getApiObjectContainer(); EXPECT_THAT(renderPasses, ::testing::ElementsAre(binding)); } TEST_P(AnApiObjects, ProvidesNonEmptyRenderGroupBindingsCollection_WhenRenderGroupBindingsWereCreated) { - const auto binding = createRenderGroupBinding(); - ApiObjectContainer& renderGroups = m_apiObjects.getApiObjectContainer(); + const auto* binding = createRenderGroupBinding(); + ApiObjectContainer& renderGroups = m_apiObjects.getApiObjectContainer(); EXPECT_THAT(renderGroups, ::testing::ElementsAre(binding)); } @@ -871,7 +889,7 @@ namespace ramses::internal TEST_P(AnApiObjects, ProvidesNonEmptySkinBindingsCollection_WhenSkinBindingsWereCreated) { - const auto skin = createSkinBinding(m_apiObjects); + const auto* skin = createSkinBinding(m_apiObjects); const auto& skins = m_apiObjects.getApiObjectContainer(); EXPECT_THAT(skins, ::testing::ElementsAre(skin)); } @@ -884,19 +902,19 @@ namespace ramses::internal auto* luaModule = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); auto* luaScript = createScript(m_apiObjects, m_valid_empty_script); auto* luaInterface = createInterface(); - auto* nodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - auto* appearanceBinding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, ""); - auto* cameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, ""); + auto* nodeBinding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); + auto* appearanceBinding = m_apiObjects.createAppearanceBinding(*m_appearance, ""); + auto* cameraBinding = m_apiObjects.createCameraBinding(*m_camera, true, ""); auto* dataArray = m_apiObjects.createDataArray(std::vector{1.f, 2.f, 3.f}, "data"); AnimationNodeConfig config; config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - auto* animationNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); + auto* animationNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); auto* timerNode = m_apiObjects.createTimerNode("timerNode"); - auto* renderPassBinding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, ""); - auto* anchor = m_apiObjects.createAnchorPoint(nodeBinding->m_nodeBinding, cameraBinding->m_cameraBinding, "anchor"); + auto* renderPassBinding = m_apiObjects.createRenderPassBinding(*m_renderPass, ""); + auto* anchor = m_apiObjects.createAnchorPoint(nodeBinding->impl(), cameraBinding->impl(), "anchor"); auto* renderGroupBinding = createRenderGroupBinding(); auto* skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_apiObjects); - auto* meshBinding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + auto* meshBinding = m_apiObjects.createMeshNodeBinding(*m_meshNode, "mb"); // feature level 01 always present std::vector expectedObjects{ luaModule, luaScript, luaInterface, nodeBinding, appearanceBinding, cameraBinding, dataArray, animationNode, @@ -916,197 +934,112 @@ namespace ramses::internal const auto* luaModule = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); const auto* luaScript = createScript(m_apiObjects, m_valid_empty_script); const auto* luaInterface = createInterface(); - const auto* nodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - const auto* appearanceBinding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, ""); - const auto* cameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, ""); + const auto* nodeBinding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); + auto* appearanceBinding = m_apiObjects.createAppearanceBinding(*m_appearance, ""); + const auto* cameraBinding = m_apiObjects.createCameraBinding(*m_camera, true, ""); const auto* dataArray = m_apiObjects.createDataArray(std::vector{1.f, 2.f, 3.f}, "data"); AnimationNodeConfig config; config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - const auto* animationNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); + const auto* animationNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); const auto* timerNode = m_apiObjects.createTimerNode("timerNode"); - const auto* renderPassBinding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, ""); + const auto* renderPassBinding = m_apiObjects.createRenderPassBinding(*m_renderPass, ""); const auto* anchor = createAnchorPoint(); const auto* renderGroupBinding = createRenderGroupBinding(); const auto* skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_apiObjects); - const auto* meshBinding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + const auto* meshBinding = m_apiObjects.createMeshNodeBinding(*m_meshNode, "mb"); - std::unordered_set logicObjectIds = + std::unordered_set ids = { - luaModule->getId(), - luaScript->getId(), - luaInterface->getId(), - nodeBinding->getId(), - appearanceBinding->getId(), - cameraBinding->getId(), - dataArray->getId(), - animationNode->getId(), - timerNode->getId(), - renderPassBinding->getId(), - anchor->getId(), - renderGroupBinding->getId(), - skin->getId(), - meshBinding->getId() + luaModule->getSceneObjectId(), + luaScript->getSceneObjectId(), + luaInterface->getSceneObjectId(), + nodeBinding->getSceneObjectId(), + appearanceBinding->getSceneObjectId(), + cameraBinding->getSceneObjectId(), + dataArray->getSceneObjectId(), + animationNode->getSceneObjectId(), + timerNode->getSceneObjectId(), + renderPassBinding->getSceneObjectId(), + anchor->getSceneObjectId(), + renderGroupBinding->getSceneObjectId(), + skin->getSceneObjectId(), + meshBinding->getSceneObjectId() }; - EXPECT_EQ(14u, logicObjectIds.size()); - } - - TEST_P(AnApiObjects, canGetLogicObjectById) - { - const auto* luaModule = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); - const auto* luaScript = createScript(m_apiObjects, m_valid_empty_script); - const auto* nodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - const auto* appearanceBinding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, ""); - const auto* cameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, ""); - const auto* dataArray = m_apiObjects.createDataArray(std::vector{1.f, 2.f, 3.f}, "data"); - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - const auto* animationNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); - const auto* timerNode = m_apiObjects.createTimerNode("timerNode"); - const auto* luaInterface = createInterface(); - const auto* renderPassBinding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, ""); - const auto* anchor = createAnchorPoint(); - const auto* renderGroupBinding = createRenderGroupBinding(); - const auto* skin = createSkinBinding(*nodeBinding, *appearanceBinding, m_apiObjects); - const auto* meshBinding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - - EXPECT_EQ(luaModule->getId(), 1u); - EXPECT_EQ(luaScript->getId(), 2u); - EXPECT_EQ(nodeBinding->getId(), 3u); - EXPECT_EQ(appearanceBinding->getId(), 4u); - EXPECT_EQ(cameraBinding->getId(), 5u); - EXPECT_EQ(dataArray->getId(), 6u); - EXPECT_EQ(animationNode->getId(), 7u); - EXPECT_EQ(timerNode->getId(), 8u); - EXPECT_EQ(luaInterface->getId(), 9u); - EXPECT_EQ(renderPassBinding->getId(), 10u); - EXPECT_EQ(anchor->getId(), 13u); - EXPECT_EQ(renderGroupBinding->getId(), 14u); - EXPECT_EQ(skin->getId(), 15u); - EXPECT_EQ(meshBinding->getId(), 16u); - - EXPECT_EQ(m_apiObjects.getApiObjectById(1u), luaModule); - EXPECT_EQ(m_apiObjects.getApiObjectById(2u), luaScript); - EXPECT_EQ(m_apiObjects.getApiObjectById(3u), nodeBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(4u), appearanceBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(5u), cameraBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(6u), dataArray); - EXPECT_EQ(m_apiObjects.getApiObjectById(7u), animationNode); - EXPECT_EQ(m_apiObjects.getApiObjectById(8u), timerNode); - EXPECT_EQ(m_apiObjects.getApiObjectById(9u), luaInterface); - EXPECT_EQ(m_apiObjects.getApiObjectById(10u), renderPassBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(13u), anchor); - EXPECT_EQ(m_apiObjects.getApiObjectById(14u), renderGroupBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(15u), skin); - EXPECT_EQ(m_apiObjects.getApiObjectById(16u), meshBinding); + EXPECT_EQ(14u, ids.size()); } TEST_P(AnApiObjects, logicObjectIdsAreRemovedFromIdMappingWhenObjectIsDestroyed) { const auto* luaModule = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); auto* luaScript = createScript(m_apiObjects, m_valid_empty_script); - const auto* nodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); - auto* appearanceBinding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, ""); - const auto* cameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, ""); + const auto* nodeBinding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); + auto* appearanceBinding = m_apiObjects.createAppearanceBinding(*m_appearance, ""); + const auto* cameraBinding = m_apiObjects.createCameraBinding(*m_camera, true, ""); const auto* dataArray = m_apiObjects.createDataArray(std::vector{1.f, 2.f, 3.f}, "data"); AnimationNodeConfig config; config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - auto* animationNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); + auto* animationNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); const auto* timerNode = m_apiObjects.createTimerNode("timerNode"); const auto* luaInterface = createInterface(); - const RamsesRenderPassBinding* renderPassBinding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, ""); + const RenderPassBinding* renderPassBinding = m_apiObjects.createRenderPassBinding(*m_renderPass, ""); const AnchorPoint* anchor = createAnchorPoint(); - const RamsesRenderGroupBinding* renderGroupBinding = createRenderGroupBinding(); + const RenderGroupBinding* renderGroupBinding = createRenderGroupBinding(); const SkinBinding* skinBinding = createSkinBinding(m_apiObjects); - const RamsesMeshNodeBinding* meshBinding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - - EXPECT_EQ(m_apiObjects.getApiObjectById(1u), luaModule); - EXPECT_EQ(m_apiObjects.getApiObjectById(2u), luaScript); - EXPECT_EQ(m_apiObjects.getApiObjectById(3u), nodeBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(4u), appearanceBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(5u), cameraBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(6u), dataArray); - EXPECT_EQ(m_apiObjects.getApiObjectById(7u), animationNode); - EXPECT_EQ(m_apiObjects.getApiObjectById(8u), timerNode); - EXPECT_EQ(m_apiObjects.getApiObjectById(9u), luaInterface); - EXPECT_EQ(m_apiObjects.getApiObjectById(10u), renderPassBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(13u), anchor); - EXPECT_EQ(m_apiObjects.getApiObjectById(14u), renderGroupBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(17u), skinBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(18u), meshBinding); + const MeshNodeBinding* meshBinding = m_apiObjects.createMeshNodeBinding(*m_meshNode, "mb"); + + EXPECT_EQ(getApiObjectById(8u), luaModule); + EXPECT_EQ(getApiObjectById(9u), luaScript); + EXPECT_EQ(getApiObjectById(10u), nodeBinding); + EXPECT_EQ(getApiObjectById(11u), appearanceBinding); + EXPECT_EQ(getApiObjectById(12u), cameraBinding); + EXPECT_EQ(getApiObjectById(13u), dataArray); + EXPECT_EQ(getApiObjectById(14u), animationNode); + EXPECT_EQ(getApiObjectById(15u), timerNode); + EXPECT_EQ(getApiObjectById(16u), luaInterface); + EXPECT_EQ(getApiObjectById(17u), renderPassBinding); + EXPECT_EQ(getApiObjectById(20u), anchor); + EXPECT_EQ(getApiObjectById(21u), renderGroupBinding); + EXPECT_EQ(getApiObjectById(24u), skinBinding); + EXPECT_EQ(getApiObjectById(25u), meshBinding); EXPECT_TRUE(m_apiObjects.destroy(*luaScript, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*appearanceBinding, m_errorReporting)); EXPECT_TRUE(m_apiObjects.destroy(*animationNode, m_errorReporting)); - EXPECT_EQ(m_apiObjects.getApiObjectById(1u), luaModule); - EXPECT_EQ(m_apiObjects.getApiObjectById(2u), nullptr); - EXPECT_EQ(m_apiObjects.getApiObjectById(3u), nodeBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(4u), nullptr); - EXPECT_EQ(m_apiObjects.getApiObjectById(5u), cameraBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(6u), dataArray); - EXPECT_EQ(m_apiObjects.getApiObjectById(7u), nullptr); - EXPECT_EQ(m_apiObjects.getApiObjectById(8u), timerNode); - EXPECT_EQ(m_apiObjects.getApiObjectById(9u), luaInterface); - EXPECT_EQ(m_apiObjects.getApiObjectById(10u), renderPassBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(13u), anchor); - EXPECT_EQ(m_apiObjects.getApiObjectById(14u), renderGroupBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(17u), skinBinding); - EXPECT_EQ(m_apiObjects.getApiObjectById(18u), meshBinding); - } - - TEST_P(AnApiObjects, logicObjectsGenerateIdentificationString) - { - const auto* luaModule = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); - auto* luaScript = createScript(m_apiObjects, m_valid_empty_script); - const auto* nodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); - auto* appearanceBinding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, "appearanceBinding"); - const auto* cameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, "cameraBinding"); - const auto* dataArray = m_apiObjects.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataArray"); - AnimationNodeConfig config; - config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - auto* animationNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); - const auto* timerNode = m_apiObjects.createTimerNode("timerNode"); - const auto* luaInterface = createInterface(); - const auto* renderPassBinding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, "renderPassBinding"); - const auto* anchor = createAnchorPoint(); - const auto renderGroupBinding = createRenderGroupBinding(); - const auto skin = createSkinBinding(m_apiObjects); - const auto* meshBinding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); - - EXPECT_EQ(luaModule->m_impl.getIdentificationString(), "module [Id=1]"); - EXPECT_EQ(luaScript->m_impl.getIdentificationString(), "script [Id=2]"); - EXPECT_EQ(nodeBinding->m_impl.getIdentificationString(), "nodeBinding [Id=3]"); - EXPECT_EQ(appearanceBinding->m_impl.getIdentificationString(), "appearanceBinding [Id=4]"); - EXPECT_EQ(cameraBinding->m_impl.getIdentificationString(), "cameraBinding [Id=5]"); - EXPECT_EQ(dataArray->m_impl.getIdentificationString(), "dataArray [Id=6]"); - EXPECT_EQ(animationNode->m_impl.getIdentificationString(), "animNode [Id=7]"); - EXPECT_EQ(timerNode->m_impl.getIdentificationString(), "timerNode [Id=8]"); - EXPECT_EQ(luaInterface->m_impl.getIdentificationString(), "intf [Id=9]"); - EXPECT_EQ(renderPassBinding->m_impl.getIdentificationString(), "renderPassBinding [Id=10]"); - EXPECT_EQ(anchor->m_impl.getIdentificationString(), "anchor [Id=13]"); - EXPECT_EQ(renderGroupBinding->m_impl.getIdentificationString(), "renderGroupBinding [Id=14]"); - EXPECT_EQ(skin->m_impl.getIdentificationString(), "skin [Id=17]"); - EXPECT_EQ(meshBinding->m_impl.getIdentificationString(), "mb [Id=18]"); + EXPECT_EQ(getApiObjectById(8u), luaModule); + EXPECT_EQ(getApiObjectById(9u), nullptr); + EXPECT_EQ(getApiObjectById(10u), nodeBinding); + EXPECT_EQ(getApiObjectById(11u), nullptr); + EXPECT_EQ(getApiObjectById(12u), cameraBinding); + EXPECT_EQ(getApiObjectById(13u), dataArray); + EXPECT_EQ(getApiObjectById(14u), nullptr); + EXPECT_EQ(getApiObjectById(15u), timerNode); + EXPECT_EQ(getApiObjectById(16u), luaInterface); + EXPECT_EQ(getApiObjectById(17u), renderPassBinding); + EXPECT_EQ(getApiObjectById(20u), anchor); + EXPECT_EQ(getApiObjectById(21u), renderGroupBinding); + EXPECT_EQ(getApiObjectById(24u), skinBinding); + EXPECT_EQ(getApiObjectById(25u), meshBinding); } TEST_P(AnApiObjects, logicObjectsGenerateIdentificationStringWithUserId) { auto* luaModule = m_apiObjects.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); auto* luaScript = createScript(m_apiObjects, m_valid_empty_script); - auto* nodeBinding = m_apiObjects.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "nodeBinding"); - auto* appearanceBinding = m_apiObjects.createRamsesAppearanceBinding(*m_appearance, "appearanceBinding"); - auto* cameraBinding = m_apiObjects.createRamsesCameraBinding(*m_camera, true, "cameraBinding"); - RamsesRenderPassBinding* renderPassBinding = m_apiObjects.createRamsesRenderPassBinding(*m_renderPass, "renderPassBinding"); + auto* nodeBinding = m_apiObjects.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "nodeBinding"); + auto* appearanceBinding = m_apiObjects.createAppearanceBinding(*m_appearance, "appearanceBinding"); + auto* cameraBinding = m_apiObjects.createCameraBinding(*m_camera, true, "cameraBinding"); + RenderPassBinding* renderPassBinding = m_apiObjects.createRenderPassBinding(*m_renderPass, "renderPassBinding"); auto* dataArray = m_apiObjects.createDataArray(std::vector{1.f, 2.f, 3.f}, "dataArray"); AnimationNodeConfig config; config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - auto* animationNode = m_apiObjects.createAnimationNode(*config.m_impl, "animNode"); + auto* animationNode = m_apiObjects.createAnimationNode(config.impl(), "animNode"); auto* timerNode = m_apiObjects.createTimerNode("timerNode"); auto* luaInterface = createInterface(); AnchorPoint* anchor = createAnchorPoint(); - RamsesRenderGroupBinding* renderGroupBinding = createRenderGroupBinding(); + RenderGroupBinding* renderGroupBinding = createRenderGroupBinding(); SkinBinding* skin = createSkinBinding(m_apiObjects); - RamsesMeshNodeBinding* meshBinding = m_apiObjects.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + MeshNodeBinding* meshBinding = m_apiObjects.createMeshNodeBinding(*m_meshNode, "mb"); EXPECT_TRUE(luaModule->setUserId(1u, 2u)); EXPECT_TRUE(luaScript->setUserId(3u, 4u)); @@ -1123,20 +1056,20 @@ namespace ramses::internal EXPECT_TRUE(skin->setUserId(25u, 26u)); EXPECT_TRUE(meshBinding->setUserId(27u, 28u)); - EXPECT_EQ(luaModule->m_impl.getIdentificationString(), "module [Id=1 UserId=00000000000000010000000000000002]"); - EXPECT_EQ(luaScript->m_impl.getIdentificationString(), "script [Id=2 UserId=00000000000000030000000000000004]"); - EXPECT_EQ(nodeBinding->m_impl.getIdentificationString(), "nodeBinding [Id=3 UserId=00000000000000050000000000000006]"); - EXPECT_EQ(appearanceBinding->m_impl.getIdentificationString(), "appearanceBinding [Id=4 UserId=00000000000000070000000000000008]"); - EXPECT_EQ(cameraBinding->m_impl.getIdentificationString(), "cameraBinding [Id=5 UserId=0000000000000009000000000000000A]"); - EXPECT_EQ(renderPassBinding->m_impl.getIdentificationString(), "renderPassBinding [Id=6 UserId=000000000000000B000000000000000C]"); - EXPECT_EQ(dataArray->m_impl.getIdentificationString(), "dataArray [Id=7 UserId=000000000000000D000000000000000E]"); - EXPECT_EQ(animationNode->m_impl.getIdentificationString(), "animNode [Id=8 UserId=000000000000000F0000000000000010]"); - EXPECT_EQ(timerNode->m_impl.getIdentificationString(), "timerNode [Id=9 UserId=00000000000000110000000000000012]"); - EXPECT_EQ(luaInterface->m_impl.getIdentificationString(), "intf [Id=10 UserId=00000000000000130000000000000014]"); - EXPECT_EQ(anchor->m_impl.getIdentificationString(), "anchor [Id=13 UserId=00000000000000150000000000000016]"); - EXPECT_EQ(renderGroupBinding->m_impl.getIdentificationString(), "renderGroupBinding [Id=14 UserId=00000000000000170000000000000018]"); - EXPECT_EQ(skin->m_impl.getIdentificationString(), "skin [Id=17 UserId=0000000000000019000000000000001A]"); - EXPECT_EQ(meshBinding->m_impl.getIdentificationString(), "mb [Id=18 UserId=000000000000001B000000000000001C]"); + EXPECT_EQ(luaModule->impl().getIdentificationString(), "module [LogicObject UserId=00000000000000010000000000000002 ScnObjId=8]"); + EXPECT_EQ(luaScript->impl().getIdentificationString(), "script [LogicObject UserId=00000000000000030000000000000004 ScnObjId=9]"); + EXPECT_EQ(nodeBinding->impl().getIdentificationString(), "nodeBinding [LogicObject UserId=00000000000000050000000000000006 ScnObjId=10]"); + EXPECT_EQ(appearanceBinding->impl().getIdentificationString(), "appearanceBinding [LogicObject UserId=00000000000000070000000000000008 ScnObjId=11]"); + EXPECT_EQ(cameraBinding->impl().getIdentificationString(), "cameraBinding [LogicObject UserId=0000000000000009000000000000000A ScnObjId=12]"); + EXPECT_EQ(renderPassBinding->impl().getIdentificationString(), "renderPassBinding [LogicObject UserId=000000000000000B000000000000000C ScnObjId=13]"); + EXPECT_EQ(dataArray->impl().getIdentificationString(), "dataArray [LogicObject UserId=000000000000000D000000000000000E ScnObjId=14]"); + EXPECT_EQ(animationNode->impl().getIdentificationString(), "animNode [LogicObject UserId=000000000000000F0000000000000010 ScnObjId=15]"); + EXPECT_EQ(timerNode->impl().getIdentificationString(), "timerNode [LogicObject UserId=00000000000000110000000000000012 ScnObjId=16]"); + EXPECT_EQ(luaInterface->impl().getIdentificationString(), "intf [LogicObject UserId=00000000000000130000000000000014 ScnObjId=17]"); + EXPECT_EQ(anchor->impl().getIdentificationString(), "anchor [LogicObject UserId=00000000000000150000000000000016 ScnObjId=20]"); + EXPECT_EQ(renderGroupBinding->impl().getIdentificationString(), "renderGroupBinding [LogicObject UserId=00000000000000170000000000000018 ScnObjId=21]"); + EXPECT_EQ(skin->impl().getIdentificationString(), "skin [LogicObject UserId=0000000000000019000000000000001A ScnObjId=24]"); + EXPECT_EQ(meshBinding->impl().getIdentificationString(), "mb [LogicObject UserId=000000000000001B000000000000001C ScnObjId=25]"); } TEST_P(AnApiObjects, ValidatesThatAllLuaInterfaceOutputsAreLinked_GeneratesWarningsIfOutputsNotLinked) @@ -1148,15 +1081,15 @@ namespace ramses::internal IN.param2 = {a=Type:Float(), b=Type:Int32()} end - )", {}, "intf name", m_errorReporting, true); + )", {}, "intf name", m_errorReporting); ASSERT_NE(nullptr, intf); - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateInterfaces(validationResults); - EXPECT_EQ(3u, validationResults.getWarnings().size()); - EXPECT_THAT(validationResults.getWarnings(), - ::testing::Each(::testing::Field(&WarningData::message, ::testing::HasSubstr("Interface [intf name] has unlinked output")))); - EXPECT_THAT(validationResults.getWarnings(), ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_EQ(3u, validationResults.getIssues().size()); + EXPECT_THAT(validationResults.getIssues(), + ::testing::Each(::testing::Field(&Issue::message, ::testing::HasSubstr("Interface [intf name] has unlinked output")))); + EXPECT_THAT(validationResults.getIssues(), ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_P(AnApiObjects, ValidatesThatAllLuaInterfaceOutputsAreLinked_DoesNotGenerateWarningsIfAllOutputsLinked) @@ -1168,7 +1101,7 @@ namespace ramses::internal IN.param2 = {a=Type:Float(), b=Type:Int32()} end - )", {}, "intf name", m_errorReporting, true); + )", {}, "intf name", m_errorReporting); LuaScript* inputsScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT( function interface(IN,OUT) @@ -1183,58 +1116,58 @@ namespace ramses::internal end )LUA_SCRIPT", {}, "inputs script", m_errorReporting); - const auto* output1 = intf->getOutputs()->getChild(0); - const auto* output21 = intf->getOutputs()->getChild(1)->getChild(0); - const auto* output22 = intf->getOutputs()->getChild(1)->getChild(1); + auto* output1 = intf->getOutputs()->getChild(0); + auto* output21 = intf->getOutputs()->getChild(1)->getChild(0); + auto* output22 = intf->getOutputs()->getChild(1)->getChild(1); - m_apiObjects.getLogicNodeDependencies().link(*output1->m_impl, *inputsScript->getInputs()->getChild(0)->m_impl, false, m_errorReporting); - m_apiObjects.getLogicNodeDependencies().link(*output21->m_impl, *inputsScript->getInputs()->getChild(1)->m_impl, false, m_errorReporting); - m_apiObjects.getLogicNodeDependencies().link(*output22->m_impl, *inputsScript->getInputs()->getChild(2)->m_impl, false, m_errorReporting); + m_apiObjects.getLogicNodeDependencies().link(output1->impl(), inputsScript->getInputs()->getChild(0)->impl(), false, m_errorReporting); + m_apiObjects.getLogicNodeDependencies().link(output21->impl(), inputsScript->getInputs()->getChild(1)->impl(), false, m_errorReporting); + m_apiObjects.getLogicNodeDependencies().link(output22->impl(), inputsScript->getInputs()->getChild(2)->impl(), false, m_errorReporting); - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateInterfaces(validationResults); - EXPECT_TRUE(validationResults.getWarnings().empty()); + EXPECT_FALSE(validationResults.hasIssue()); } TEST_P(AnApiObjects, ValidatesThatLuaInterfacesNamesAreUnique) { // single interface -> no warning - const auto intf1 = createInterface(); + const auto* intf1 = createInterface(); { - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateInterfaces(validationResults); - EXPECT_TRUE(validationResults.getWarnings().empty()); + EXPECT_FALSE(validationResults.hasIssue()); } - // two interfaces with same name -> warning + // two interfaces with same name -> error auto intf2 = createInterface(); { - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateInterfaces(validationResults); - ASSERT_EQ(1u, validationResults.getWarnings().size()); - EXPECT_EQ(validationResults.getWarnings().front().message, "Interface [intf] does not have a unique name"); - EXPECT_EQ(validationResults.getWarnings().front().object, intf1); - EXPECT_EQ(validationResults.getWarnings().front().type, EWarningType::Other); + ASSERT_EQ(1u, validationResults.getIssues().size()); + EXPECT_EQ(validationResults.getIssues().front().message, "Interface [intf] does not have a unique name"); + EXPECT_EQ(validationResults.getIssues().front().object, intf1); + EXPECT_EQ(validationResults.getIssues().front().type, EIssueType::Error); } // rename conflicting intf -> no warning intf2->setName("otherIntf"); { - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateInterfaces(validationResults); - EXPECT_TRUE(validationResults.getWarnings().empty()); + EXPECT_FALSE(validationResults.hasIssue()); } - // another interface with same name -> warning + // another interface with same name -> error auto intf3 = createInterface(); intf3->setName("otherIntf"); { - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateInterfaces(validationResults); - ASSERT_EQ(1u, validationResults.getWarnings().size()); - EXPECT_EQ(validationResults.getWarnings().front().message, "Interface [otherIntf] does not have a unique name"); - EXPECT_EQ(validationResults.getWarnings().front().object, intf2); - EXPECT_EQ(validationResults.getWarnings().front().type, EWarningType::Other); + ASSERT_EQ(1u, validationResults.getIssues().size()); + EXPECT_EQ(validationResults.getIssues().front().message, "Interface [otherIntf] does not have a unique name"); + EXPECT_EQ(validationResults.getIssues().front().object, intf2); + EXPECT_EQ(validationResults.getIssues().front().type, EIssueType::Error); } } @@ -1250,17 +1183,17 @@ namespace ramses::internal )", {}, "script name", m_errorReporting); ASSERT_NE(nullptr, script); - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateDanglingNodes(validationResults); - EXPECT_EQ(2u, validationResults.getWarnings().size()); - EXPECT_THAT(validationResults.getWarnings()[0].message, ::testing::HasSubstr("Node [script name] has no outgoing links")); - EXPECT_THAT(validationResults.getWarnings()[1].message, ::testing::HasSubstr("Node [script name] has no ingoing links")); - EXPECT_THAT(validationResults.getWarnings(), ::testing::Each(::testing::Field(&WarningData::type, ::testing::Eq(EWarningType::UnusedContent)))); + EXPECT_EQ(2u, validationResults.getIssues().size()); + EXPECT_THAT(validationResults.getIssues()[0].message, ::testing::HasSubstr("Node [script name] has no outgoing links")); + EXPECT_THAT(validationResults.getIssues()[1].message, ::testing::HasSubstr("Node [script name] has no ingoing links")); + EXPECT_THAT(validationResults.getIssues(), ::testing::Each(::testing::Field(&Issue::type, ::testing::Eq(EIssueType::Warning)))); } TEST_P(AnApiObjects, ValidatesDanglingNodes_DoesNotProduceWarningIfNodeHasNoInputs) { - const auto script = m_apiObjects.createLuaScript(R"( + auto* script = m_apiObjects.createLuaScript(R"( function interface(IN,OUT) OUT.param1 = Type:Int32() end @@ -1269,7 +1202,7 @@ namespace ramses::internal )", {}, "script name", m_errorReporting); ASSERT_NE(nullptr, script); - const auto dummyInputScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT( + auto* dummyInputScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT( function interface(IN) IN.param1 = Type:Int32() end @@ -1280,16 +1213,16 @@ namespace ramses::internal ASSERT_NE(nullptr, dummyInputScript); // link script's output in order to pass outputs validation - m_apiObjects.getLogicNodeDependencies().link(*script->getOutputs()->getChild(0u)->m_impl, *dummyInputScript->getInputs()->getChild(0u)->m_impl, false, m_errorReporting); + m_apiObjects.getLogicNodeDependencies().link(script->getOutputs()->getChild(0u)->impl(), dummyInputScript->getInputs()->getChild(0u)->impl(), false, m_errorReporting); - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateDanglingNodes(validationResults); - EXPECT_TRUE(validationResults.getWarnings().empty()); + EXPECT_FALSE(validationResults.hasIssue()); } TEST_P(AnApiObjects, ValidatesDanglingNodes_DoesNotProduceWarningIfNodeHasNoOutputs) { - const auto script = m_apiObjects.createLuaScript(R"( + auto* script = m_apiObjects.createLuaScript(R"( function interface(IN,OUT) IN.param1 = Type:Int32() end @@ -1298,7 +1231,7 @@ namespace ramses::internal )", {}, "script name", m_errorReporting); ASSERT_NE(nullptr, script); - const auto dummyOutputScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT( + auto* dummyOutputScript = m_apiObjects.createLuaScript(R"LUA_SCRIPT( function interface(IN,OUT) OUT.param1 = Type:Int32() end @@ -1309,119 +1242,11 @@ namespace ramses::internal ASSERT_NE(nullptr, dummyOutputScript); // link script's input in order to pass inputs validation - m_apiObjects.getLogicNodeDependencies().link(*dummyOutputScript->getOutputs()->getChild(0u)->m_impl, *script->getInputs()->getChild(0u)->m_impl, false, m_errorReporting); + m_apiObjects.getLogicNodeDependencies().link(dummyOutputScript->getOutputs()->getChild(0u)->impl(), script->getInputs()->getChild(0u)->impl(), false, m_errorReporting); - ValidationResults validationResults; + ValidationReportImpl validationResults; m_apiObjects.validateDanglingNodes(validationResults); - EXPECT_TRUE(validationResults.getWarnings().empty()); - } - - class AnApiObjects_SceneMismatch : public AnApiObjects - { - protected: - RamsesTestSetup m_testSetup; - ramses::Scene* scene1 {m_testSetup.createScene(ramses::sceneId_t(1))}; - ramses::Scene* scene2 {m_testSetup.createScene(ramses::sceneId_t(2))}; - }; - - INSTANTIATE_TEST_SUITE_P( - AnApiObjects_SceneMismatchTests, - AnApiObjects_SceneMismatch, - ramses::internal::GetFeatureLevelTestValues()); - - TEST_P(AnApiObjects_SceneMismatch, recognizesNodeBindingsCarryNodesFromDifferentScenes) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node1"), ramses::ERotationType::Euler_XYZ, "binding1"); - RamsesNodeBinding* binding2 = m_apiObjects.createRamsesNodeBinding(*scene2->createNode("node2"), ramses::ERotationType::Euler_XYZ, "binding2"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - EXPECT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses node 'node2' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(binding2, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, recognizesNodeBindingAndAppearanceBindingAreFromDifferentScenes) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - RamsesAppearanceBinding* appBinding = m_apiObjects.createRamsesAppearanceBinding(RamsesTestSetup::CreateTrivialTestAppearance(*scene2), "app binding"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - EXPECT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses appearance 'test appearance' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(appBinding, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, detectsNodeBindingIsFromDifferentScene) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - auto* otherSceneBinding = m_apiObjects.createRamsesNodeBinding(*scene2->createPerspectiveCamera("test camera"), ramses::ERotationType::Euler_XYZ, "other binding"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses node 'test camera' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(otherSceneBinding, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, detectsAppearanceBindingIsFromDifferentScene) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - auto* otherSceneBinding = m_apiObjects.createRamsesAppearanceBinding(RamsesTestSetup::CreateTrivialTestAppearance(*scene2), "other binding"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses appearance 'test appearance' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(otherSceneBinding, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, detectsCameraBindingIsFromDifferentScene) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - auto* otherSceneBinding = m_apiObjects.createRamsesCameraBinding(*scene2->createPerspectiveCamera("camera"), true, "other binding"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses camera 'camera' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(otherSceneBinding, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, detectsRenderPassBindingIsFromDifferentScene) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - auto* otherSceneBinding = m_apiObjects.createRamsesRenderPassBinding(*scene2->createRenderPass("render pass"), "other binding"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses render pass 'render pass' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(otherSceneBinding, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, detectsRenderGroupBindingIsFromDifferentScene) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - - auto rg = scene2->createRenderGroup("render group"); - const auto mesh = scene2->createMeshNode("mesh"); - EXPECT_EQ(ramses::StatusOK, rg->addMeshNode(*mesh)); - RamsesRenderGroupBindingElements elements; - EXPECT_TRUE(elements.addElement(*mesh)); - const auto* otherSceneBinding = m_apiObjects.createRamsesRenderGroupBinding(*rg, elements, "other binding"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses render group 'render group' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(otherSceneBinding, m_errorReporting.getErrors()[0].object); - } - - TEST_P(AnApiObjects_SceneMismatch, detectsMeshNodeBindingIsFromDifferentScene) - { - m_apiObjects.createRamsesNodeBinding(*scene1->createNode("node"), ramses::ERotationType::Euler_XYZ, "node binding"); - - const auto otherSceneBinding = m_apiObjects.createRamsesMeshNodeBinding(*scene2->createMeshNode("mesh"), "mb"); - - EXPECT_FALSE(m_apiObjects.checkBindingsReferToSameRamsesScene(m_errorReporting)); - ASSERT_EQ(1u, m_errorReporting.getErrors().size()); - EXPECT_EQ("Ramses mesh node 'mesh' is from scene with id:2 but other objects are from scene with id:1!", m_errorReporting.getErrors()[0].message); - EXPECT_EQ(otherSceneBinding, m_errorReporting.getErrors()[0].object); + EXPECT_FALSE(validationResults.hasIssue()); } class AnApiObjects_Serialization : public AnApiObjects @@ -1431,14 +1256,14 @@ namespace ramses::internal INSTANTIATE_TEST_SUITE_P( AnApiObjects_SerializationTests, AnApiObjects_Serialization, - ramses::internal::GetFeatureLevelTestValues()); + GetFeatureLevelTestValues()); TEST_P(AnApiObjects_Serialization, AlwaysCreatesEmptyFlatbuffersContainers_WhenNoObjectsPresent) { // Create without API objects -> serialize flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); + ApiObjects toSerialize(GetParam(), m_scene->impl()); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::ByteCodeOnly); } @@ -1465,8 +1290,6 @@ namespace ramses::internal ASSERT_NE(nullptr, serialized.links()); ASSERT_EQ(0u, serialized.links()->size()); - - EXPECT_EQ(0u, serialized.lastObjectId()); } TEST_P(AnApiObjects_Serialization, CreatesFlatbufferContainer_ForScripts) @@ -1474,7 +1297,7 @@ namespace ramses::internal // Create test flatbuffer with only a script flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; createScript(toSerialize, m_valid_empty_script); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::SourceAndByteCode); } @@ -1485,11 +1308,11 @@ namespace ramses::internal ASSERT_EQ(1u, serialized.luaScripts()->size()); const rlogic_serialization::LuaScript& serializedScript = *serialized.luaScripts()->Get(0); EXPECT_EQ("script", serializedScript.base()->name()->str()); - EXPECT_EQ(1u, serializedScript.base()->id()); + EXPECT_EQ(8u, serializedScript.base()->id()); EXPECT_EQ(m_valid_empty_script, serializedScript.luaSourceCode()->str()); EXPECT_TRUE(serializedScript.luaByteCode()->size() > 0); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "test", m_errorReporting, GetParam()); EXPECT_TRUE(deserialized); } @@ -1498,7 +1321,7 @@ namespace ramses::internal // Create test flatbuffer with only a script flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; createInterface(toSerialize); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::ByteCodeOnly); } @@ -1509,9 +1332,9 @@ namespace ramses::internal ASSERT_EQ(1u, serialized.luaInterfaces()->size()); const rlogic_serialization::LuaInterface& serializedInterface = *serialized.luaInterfaces()->Get(0); EXPECT_EQ("intf", serializedInterface.base()->name()->str()); - EXPECT_EQ(1u, serializedInterface.base()->id()); + EXPECT_EQ(8u, serializedInterface.base()->id()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "test", m_errorReporting, GetParam()); EXPECT_TRUE(deserialized); } @@ -1520,11 +1343,11 @@ namespace ramses::internal // Create test flatbuffer with only a node binding flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); - toSerialize.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - toSerialize.createRamsesAppearanceBinding(*m_appearance, "appearance"); - toSerialize.createRamsesCameraBinding(*m_camera, true, "camera"); - toSerialize.createRamsesRenderPassBinding(*m_renderPass, "rp"); + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; + toSerialize.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "node"); + toSerialize.createAppearanceBinding(*m_appearance, "appearance"); + toSerialize.createCameraBinding(*m_camera, true, "camera"); + toSerialize.createRenderPassBinding(*m_renderPass, "rp"); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::ByteCodeOnly); } @@ -1532,27 +1355,27 @@ namespace ramses::internal ASSERT_NE(nullptr, serialized.nodeBindings()); ASSERT_EQ(1u, serialized.nodeBindings()->size()); - const rlogic_serialization::RamsesNodeBinding& serializedNodeBinding = *serialized.nodeBindings()->Get(0); + const rlogic_serialization::NodeBinding& serializedNodeBinding = *serialized.nodeBindings()->Get(0); EXPECT_EQ("node", serializedNodeBinding.base()->base()->name()->str()); - EXPECT_EQ(1u, serializedNodeBinding.base()->base()->id()); + EXPECT_EQ(8u, serializedNodeBinding.base()->base()->id()); ASSERT_NE(nullptr, serialized.appearanceBindings()); ASSERT_EQ(1u, serialized.appearanceBindings()->size()); - const rlogic_serialization::RamsesAppearanceBinding& serializedAppBinding = *serialized.appearanceBindings()->Get(0); + const rlogic_serialization::AppearanceBinding& serializedAppBinding = *serialized.appearanceBindings()->Get(0); EXPECT_EQ("appearance", serializedAppBinding.base()->base()->name()->str()); - EXPECT_EQ(2u, serializedAppBinding.base()->base()->id()); + EXPECT_EQ(9u, serializedAppBinding.base()->base()->id()); ASSERT_NE(nullptr, serialized.cameraBindings()); ASSERT_EQ(1u, serialized.cameraBindings()->size()); - const rlogic_serialization::RamsesCameraBinding& serializedCameraBinding = *serialized.cameraBindings()->Get(0); + const rlogic_serialization::CameraBinding& serializedCameraBinding = *serialized.cameraBindings()->Get(0); EXPECT_EQ("camera", serializedCameraBinding.base()->base()->name()->str()); - EXPECT_EQ(3u, serializedCameraBinding.base()->base()->id()); + EXPECT_EQ(10u, serializedCameraBinding.base()->base()->id()); ASSERT_NE(nullptr, serialized.renderPassBindings()); ASSERT_EQ(1u, serialized.renderPassBindings()->size()); - const rlogic_serialization::RamsesRenderPassBinding& serializedRenderPassBinding = *serialized.renderPassBindings()->Get(0); + const rlogic_serialization::RenderPassBinding& serializedRenderPassBinding = *serialized.renderPassBindings()->Get(0); EXPECT_EQ("rp", serializedRenderPassBinding.base()->base()->name()->str()); - EXPECT_EQ(4u, serializedRenderPassBinding.base()->base()->id()); + EXPECT_EQ(11u, serializedRenderPassBinding.base()->base()->id()); } TEST_P(AnApiObjects_Serialization, CreatesFlatbufferContainers_ForLinks) @@ -1560,7 +1383,7 @@ namespace ramses::internal // Create test flatbuffer with a link between script and binding flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; const std::string_view scriptWithOutput = R"( function interface(IN,OUT) @@ -1573,11 +1396,11 @@ namespace ramses::internal end )"; - const LuaScript* script = createScript(toSerialize, scriptWithOutput); - RamsesNodeBinding* nodeBinding = toSerialize.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, ""); + LuaScript* script = createScript(toSerialize, scriptWithOutput); + NodeBinding* nodeBinding = toSerialize.createNodeBinding(*m_node, ERotationType::Euler_XYZ, ""); ASSERT_TRUE(toSerialize.getLogicNodeDependencies().link( - *script->getOutputs()->getChild("nested")->getChild("rotation")->m_impl, - *nodeBinding->getInputs()->getChild("rotation")->m_impl, + script->getOutputs()->getChild("nested")->getChild("rotation")->impl(), + nodeBinding->getInputs()->getChild("rotation")->impl(), false, m_errorReporting)); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::ByteCodeOnly); @@ -1589,7 +1412,7 @@ namespace ramses::internal ASSERT_EQ(1u, serialized.luaScripts()->size()); ASSERT_EQ(1u, serialized.nodeBindings()->size()); const rlogic_serialization::LuaScript& script = *serialized.luaScripts()->Get(0); - const rlogic_serialization::RamsesNodeBinding& binding = *serialized.nodeBindings()->Get(0); + const rlogic_serialization::NodeBinding& binding = *serialized.nodeBindings()->Get(0); ASSERT_NE(nullptr, serialized.links()); ASSERT_EQ(1u, serialized.links()->size()); @@ -1604,18 +1427,18 @@ namespace ramses::internal // Create dummy data and serialize flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; createScript(toSerialize, m_valid_empty_script); createInterface(toSerialize); - const auto nodeBinding = toSerialize.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto appearanceBinding = toSerialize.createRamsesAppearanceBinding(*m_appearance, "appearance"); - toSerialize.createRamsesCameraBinding(*m_camera, true, "camera"); - toSerialize.createRamsesRenderPassBinding(*m_renderPass, "rp"); - RamsesRenderGroupBindingElements elements; + const auto* nodeBinding = toSerialize.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "node"); + auto* appearanceBinding = toSerialize.createAppearanceBinding(*m_appearance, "appearance"); + toSerialize.createCameraBinding(*m_camera, true, "camera"); + toSerialize.createRenderPassBinding(*m_renderPass, "rp"); + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); - toSerialize.createRamsesRenderGroupBinding(*m_renderGroup, elements, "rg"); + toSerialize.createRenderGroupBinding(*m_renderGroup, elements, "rg"); createSkinBinding(*nodeBinding, *appearanceBinding, toSerialize); - toSerialize.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + toSerialize.createMeshNodeBinding(*m_meshNode, "mb"); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::ByteCodeOnly); } @@ -1629,7 +1452,7 @@ namespace ramses::internal EXPECT_CALL(m_resolverMock, findRamsesRenderGroupInScene(::testing::Eq("rg"), m_renderGroup->getSceneObjectId())).WillOnce(::testing::Return(m_renderGroup)); EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("rg"), m_meshNode->getSceneObjectId())).WillOnce(::testing::Return(m_meshNode)); EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("mb"), m_meshNode->getSceneObjectId())).WillOnce(::testing::Return(m_meshNode)); - std::unique_ptr apiObjectsOptional = ApiObjects::Deserialize(serialized, &m_resolverMock, "", m_errorReporting, GetParam()); + std::unique_ptr apiObjectsOptional = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "", m_errorReporting, GetParam()); ASSERT_TRUE(apiObjectsOptional); @@ -1639,64 +1462,64 @@ namespace ramses::internal LuaScript* script = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(script->getName(), "script"); - EXPECT_EQ(script, &script->m_script.getLogicObject()); + EXPECT_EQ(script, &script->impl().getLogicObject()); LuaInterface* intf = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(intf->getName(), "intf"); - EXPECT_EQ(intf, &intf->m_interface.getLogicObject()); + EXPECT_EQ(intf, &intf->impl().getLogicObject()); - RamsesNodeBinding* nodeBinding = apiObjects.getApiObjectContainer()[0]; + NodeBinding* nodeBinding = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(nodeBinding->getName(), "node"); - EXPECT_EQ(nodeBinding, &nodeBinding->m_nodeBinding.getLogicObject()); + EXPECT_EQ(nodeBinding, &nodeBinding->impl().getLogicObject()); - RamsesAppearanceBinding* appBinding = apiObjects.getApiObjectContainer()[0]; + AppearanceBinding* appBinding = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(appBinding->getName(), "appearance"); - EXPECT_EQ(appBinding, &appBinding->m_appearanceBinding.getLogicObject()); + EXPECT_EQ(appBinding, &appBinding->impl().getLogicObject()); - RamsesCameraBinding* camBinding = apiObjects.getApiObjectContainer()[0]; + CameraBinding* camBinding = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(camBinding->getName(), "camera"); - EXPECT_EQ(camBinding, &camBinding->m_cameraBinding.getLogicObject()); + EXPECT_EQ(camBinding, &camBinding->impl().getLogicObject()); - RamsesRenderPassBinding* rpBinding = apiObjects.getApiObjectContainer()[0]; + RenderPassBinding* rpBinding = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(rpBinding->getName(), "rp"); - EXPECT_EQ(rpBinding, &rpBinding->m_renderPassBinding.getLogicObject()); + EXPECT_EQ(rpBinding, &rpBinding->impl().getLogicObject()); - const auto rgBinding = apiObjects.getApiObjectContainer()[0]; + const auto* rgBinding = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(rgBinding->getName(), "rg"); - EXPECT_EQ(rgBinding, &rgBinding->m_renderGroupBinding.getLogicObject()); + EXPECT_EQ(rgBinding, &rgBinding->impl().getLogicObject()); - const auto skin = apiObjects.getApiObjectContainer()[0]; + const auto* skin = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(skin->getName(), "skin"); - EXPECT_EQ(skin, &skin->m_skinBinding.getLogicObject()); + EXPECT_EQ(skin, &skin->impl().getLogicObject()); - const auto meshBinding = apiObjects.getApiObjectContainer()[0]; + const auto* meshBinding = apiObjects.getApiObjectContainer()[0]; EXPECT_EQ(meshBinding->getName(), "mb"); - EXPECT_EQ(meshBinding, &meshBinding->m_meshNodeBinding.getLogicObject()); + EXPECT_EQ(meshBinding, &meshBinding->impl().getLogicObject()); } TEST_P(AnApiObjects_Serialization, ObjectsCreatedAfterLoadingReceiveUniqueId) { - ApiObjects beforeSaving(GetParam()); + std::unordered_set ids; // Create dummy data and serialize flatbuffers::FlatBufferBuilder builder; { - createScript(beforeSaving, m_valid_empty_script); - createScript(beforeSaving, m_valid_empty_script); - createScript(beforeSaving, m_valid_empty_script); + ApiObjects beforeSaving{ GetParam(), m_scene->impl() }; + ids.insert(createScript(beforeSaving, m_valid_empty_script)->getSceneObjectId()); + ids.insert(createScript(beforeSaving, m_valid_empty_script)->getSceneObjectId()); + ids.insert(createScript(beforeSaving, m_valid_empty_script)->getSceneObjectId()); + EXPECT_EQ(3u, ids.size()); ApiObjects::Serialize(beforeSaving, builder, ELuaSavingMode::ByteCodeOnly); } auto& serialized = *flatbuffers::GetRoot(builder.GetBufferPointer()); - EXPECT_EQ(3u, serialized.lastObjectId()); - - std::unique_ptr afterLoadingObjects = ApiObjects::Deserialize(serialized, &m_resolverMock, "", m_errorReporting, GetParam()); + std::unique_ptr afterLoadingObjects = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "", m_errorReporting, GetParam()); auto* newScript = createScript(*afterLoadingObjects, m_valid_empty_script); // new script's ID does not overlap with one of the IDs of the objects before saving - EXPECT_EQ(nullptr, beforeSaving.getApiObjectById(newScript->getId())); + EXPECT_EQ(ids.cend(), ids.find(newScript->getSceneObjectId())); } TEST_P(AnApiObjects_Serialization, ReConstructsLinksWhenCreatedFromDeserializedData) @@ -1704,7 +1527,7 @@ namespace ramses::internal // Create dummy data and serialize flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize(GetParam()); + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; const std::string_view scriptForLinks = R"( function interface(IN,OUT) @@ -1721,8 +1544,8 @@ namespace ramses::internal auto script1 = createScript(toSerialize, scriptForLinks); auto script2 = createScript(toSerialize, scriptForLinks); ASSERT_TRUE(toSerialize.getLogicNodeDependencies().link( - *script1->getOutputs()->getChild("nested")->getChild("integer")->m_impl, - *script2->getInputs()->getChild("integer")->m_impl, + script1->getOutputs()->getChild("nested")->getChild("integer")->impl(), + script2->getInputs()->getChild("integer")->impl(), false, m_errorReporting)); @@ -1731,7 +1554,7 @@ namespace ramses::internal auto& serialized = *flatbuffers::GetRoot(builder.GetBufferPointer()); - std::unique_ptr apiObjectsOptional = ApiObjects::Deserialize(serialized, &m_resolverMock, "", m_errorReporting, GetParam()); + std::unique_ptr apiObjectsOptional = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "", m_errorReporting, GetParam()); ASSERT_TRUE(apiObjectsOptional); @@ -1743,11 +1566,11 @@ namespace ramses::internal LuaScript* script2 = apiObjects.getApiObjectContainer()[1]; ASSERT_TRUE(script2); - EXPECT_TRUE(apiObjects.getLogicNodeDependencies().isLinked(script1->m_impl)); - EXPECT_TRUE(apiObjects.getLogicNodeDependencies().isLinked(script2->m_impl)); + EXPECT_TRUE(apiObjects.getLogicNodeDependencies().isLinked(script1->impl())); + EXPECT_TRUE(apiObjects.getLogicNodeDependencies().isLinked(script2->impl())); - const PropertyImpl* script1Output = script1->getOutputs()->getChild("nested")->getChild("integer")->m_impl.get(); - const PropertyImpl* script2Input = script2->getInputs()->getChild("integer")->m_impl.get(); + PropertyImpl* script1Output = &script1->getOutputs()->getChild("nested")->getChild("integer")->impl(); + PropertyImpl* script2Input = &script2->getInputs()->getChild("integer")->impl(); EXPECT_EQ(script1Output, script2Input->getIncomingLink().property); EXPECT_FALSE(script2Input->getIncomingLink().isWeakLink); @@ -1764,29 +1587,28 @@ namespace ramses::internal 0, // no modules container m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing Lua modules container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing Lua modules container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenScriptsContainerMissing) @@ -1797,29 +1619,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no scripts container m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing Lua scripts container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing Lua scripts container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenInterfacesContainerMissing) @@ -1830,29 +1651,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no interfaces container - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing Lua interfaces container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing Lua interfaces container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenNodeBindingsContainerMissing) @@ -1864,28 +1684,27 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no node bindings container - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing node bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing node bindings container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenAppearanceBindingsContainerMissing) @@ -1896,29 +1715,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no appearance bindings container - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing appearance bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing appearance bindings container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenCameraBindingsContainerMissing) @@ -1929,29 +1747,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no camera bindings container m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing camera bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing camera bindings container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenRenderPassBindingsContainerMissing) @@ -1962,29 +1779,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, 0u, // no render pass bindings container m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing renderpass bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing renderpass bindings container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenLinksContainerMissing) @@ -1995,29 +1811,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no links container - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing links container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing links container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenDataArrayContainerMissing) @@ -2028,29 +1843,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no data array container m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing data arrays container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing data arrays container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenAnimationNodeContainerMissing) @@ -2061,29 +1875,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no animation nodes container m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing animation nodes container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing animation nodes container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenTimerNodeContainerMissing) @@ -2094,29 +1907,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0, // no timer nodes container m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing timer nodes container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing timer nodes container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenAnchorPointContainerMissing) @@ -2127,29 +1939,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), 0u, // no anchor points container - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing anchor points container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing anchor points container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenRenderGroupBindingContainerMissing) @@ -2160,29 +1971,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0u, // no render group bindings container m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing rendergroup bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing rendergroup bindings container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenSkinBindingContainerMissing) @@ -2193,29 +2003,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), 0u, // no skin bindings container - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing skin bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing skin bindings container!"); } TEST_P(AnApiObjects_Serialization, ErrorWhenMeshNodeBindingContainerMissing) @@ -2226,17 +2035,16 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), 0u // no mesh node bindings container ); @@ -2244,11 +2052,11 @@ namespace ramses::internal } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading from serialized data: missing meshnode bindings container!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading from serialized data: missing meshnode bindings container!"); } TEST_P(AnApiObjects_Serialization, ReportsErrorWhenScriptCouldNotBeDeserialized) @@ -2259,30 +2067,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{ m_testUtils.serializeTestScriptWithError() }), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LogicObject base from serialized data: missing base table!"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "Fatal error during loading of LuaScript from serialized data: missing name and/or ID!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaScript from serialized data: missing name and/or ID!"); } TEST_P(AnApiObjects_Serialization, ReportsErrorWhenInterfaceCouldNotBeDeserialized) @@ -2293,29 +2099,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{m_testUtils.serializeTestInterfaceWithError()}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LuaInterface from serialized data: empty name!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaInterface from serialized data: empty name!"); } TEST_P(AnApiObjects_Serialization, ReportsErrorWhenModuleCouldNotBeDeserialized) @@ -2326,30 +2131,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{ m_testUtils.serializeTestModule(true) }), // module has errors m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 2u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of LogicObject base from serialized data: missing name!"); - EXPECT_EQ(m_errorReporting.getErrors()[1].message, "Fatal error during loading of LuaModule from serialized data: missing name and/or ID!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of LuaModule from serialized data: missing name and/or ID!"); } TEST_P(AnApiObjects_Serialization, ReportsErrorWhenRenderPassBindingCouldNotBeDeserialized) @@ -2360,29 +2163,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{ m_testUtils.serializeTestRenderPassBindingWithError() }), + m_flatBufferBuilder.CreateVector(std::vector>{ m_testUtils.serializeTestRenderPassBindingWithError() }), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderPassBinding from serialized data: missing base class info!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderPassBinding from serialized data: missing base class info!"); } TEST_P(AnApiObjects_Serialization, ReportsErrorWhenRenderGroupBindingCouldNotBeDeserialized) @@ -2393,29 +2195,28 @@ namespace ramses::internal m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - 0u, - m_flatBufferBuilder.CreateVector(std::vector>{}), + m_flatBufferBuilder.CreateVector(std::vector>{}), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{ m_testUtils.serializeTestRenderGroupBindingWithError() }), + m_flatBufferBuilder.CreateVector(std::vector>{ m_testUtils.serializeTestRenderGroupBindingWithError() }), m_flatBufferBuilder.CreateVector(std::vector>{}), - m_flatBufferBuilder.CreateVector(std::vector>{}) + m_flatBufferBuilder.CreateVector(std::vector>{}) ); m_flatBufferBuilder.Finish(apiObjects); } const auto& serialized = *flatbuffers::GetRoot(m_flatBufferBuilder.GetBufferPointer()); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "unit test", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "unit test", m_errorReporting, GetParam()); EXPECT_FALSE(deserialized); - ASSERT_EQ(m_errorReporting.getErrors().size(), 1u); - EXPECT_EQ(m_errorReporting.getErrors()[0].message, "Fatal error during loading of RamsesRenderGroupBinding from serialized data: missing base class info!"); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "Fatal error during loading of RenderGroupBinding from serialized data: missing base class info!"); } TEST_P(AnApiObjects_Serialization, FillsLogicObjectAndOwnedContainerOnDeserialization) @@ -2423,25 +2224,25 @@ namespace ramses::internal // Create dummy data and serialize flatbuffers::FlatBufferBuilder builder; { - ApiObjects toSerialize{ GetParam() }; + ApiObjects toSerialize{ GetParam(), m_scene->impl() }; toSerialize.createLuaModule(m_moduleSrc, {}, "module", m_errorReporting); createScript(toSerialize, m_valid_empty_script); createInterface(toSerialize); - const auto node = toSerialize.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "node"); - const auto appearance = toSerialize.createRamsesAppearanceBinding(*m_appearance, "appearance"); - const auto camera = toSerialize.createRamsesCameraBinding(*m_camera, true, "camera"); + auto* node = toSerialize.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "node"); + auto* appearance = toSerialize.createAppearanceBinding(*m_appearance, "appearance"); + auto* camera = toSerialize.createCameraBinding(*m_camera, true, "camera"); auto dataArray = toSerialize.createDataArray(std::vector{1.f, 2.f, 3.f}, "data"); AnimationNodeConfig config; config.addChannel({ "channel", dataArray, dataArray, EInterpolationType::Linear }); - toSerialize.createAnimationNode(*config.m_impl, "animNode"); + toSerialize.createAnimationNode(config.impl(), "animNode"); toSerialize.createTimerNode("timerNode"); - toSerialize.createRamsesRenderPassBinding(*m_renderPass, "rp"); - toSerialize.createAnchorPoint(node->m_nodeBinding, camera->m_cameraBinding, "anchor"); - RamsesRenderGroupBindingElements elements; + toSerialize.createRenderPassBinding(*m_renderPass, "rp"); + toSerialize.createAnchorPoint(node->impl(), camera->impl(), "anchor"); + RenderGroupBindingElements elements; EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); - toSerialize.createRamsesRenderGroupBinding(*m_renderGroup, elements, "rg"); + toSerialize.createRenderGroupBinding(*m_renderGroup, elements, "rg"); createSkinBinding(*node, *appearance, toSerialize); - toSerialize.createRamsesMeshNodeBinding(*m_meshNode, "mb"); + toSerialize.createMeshNodeBinding(*m_meshNode, "mb"); ApiObjects::Serialize(toSerialize, builder, ELuaSavingMode::ByteCodeOnly); } @@ -2455,7 +2256,7 @@ namespace ramses::internal EXPECT_CALL(m_resolverMock, findRamsesRenderGroupInScene(::testing::Eq("rg"), m_renderGroup->getSceneObjectId())).WillOnce(::testing::Return(m_renderGroup)); EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("rg"), m_meshNode->getSceneObjectId())).WillOnce(::testing::Return(m_meshNode)); EXPECT_CALL(m_resolverMock, findRamsesSceneObjectInScene(::testing::Eq("mb"), m_meshNode->getSceneObjectId())).WillOnce(::testing::Return(m_meshNode)); - std::unique_ptr deserialized = ApiObjects::Deserialize(serialized, &m_resolverMock, "", m_errorReporting, GetParam()); + std::unique_ptr deserialized = ApiObjects::Deserialize(m_scene->impl(), serialized, m_resolverMock, "", m_errorReporting, GetParam()); ASSERT_TRUE(deserialized); @@ -2469,17 +2270,17 @@ namespace ramses::internal apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], - apiObjects.getApiObjectContainer()[0], - apiObjects.getApiObjectContainer()[0], - apiObjects.getApiObjectContainer()[0], - apiObjects.getApiObjectContainer()[0], + apiObjects.getApiObjectContainer()[0], + apiObjects.getApiObjectContainer()[0], + apiObjects.getApiObjectContainer()[0], + apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], - apiObjects.getApiObjectContainer()[0], + apiObjects.getApiObjectContainer()[0], apiObjects.getApiObjectContainer()[0], - apiObjects.getApiObjectContainer()[0] + apiObjects.getApiObjectContainer()[0] }; ASSERT_EQ(expected.size(), logicObjects.size()); @@ -2488,7 +2289,7 @@ namespace ramses::internal { EXPECT_EQ(logicObjects[i], expected[i]); EXPECT_EQ(ownedObjects[i].get(), expected[i]); - EXPECT_EQ(logicObjects[i], &logicObjects[i]->m_impl->getLogicObject()); + EXPECT_EQ(logicObjects[i], &logicObjects[i]->impl().getLogicObject()); } } } diff --git a/client/logic/unittests/internal/DirectedAcyclicGraphTest.cpp b/tests/unittests/client/logic/internal/DirectedAcyclicGraphTest.cpp similarity index 93% rename from client/logic/unittests/internal/DirectedAcyclicGraphTest.cpp rename to tests/unittests/client/logic/internal/DirectedAcyclicGraphTest.cpp index 1b4e52e87..1d42b6044 100644 --- a/client/logic/unittests/internal/DirectedAcyclicGraphTest.cpp +++ b/tests/unittests/client/logic/internal/DirectedAcyclicGraphTest.cpp @@ -7,36 +7,35 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" - -#include "internals/DirectedAcyclicGraph.h" - -#include "LogicNodeDummy.h" +#include "internal/logic/DirectedAcyclicGraph.h" +#include "ramses/client/logic/LuaScript.h" +#include "impl/logic/LuaScriptImpl.h" +#include "LogicEngineTest_Base.h" namespace ramses::internal { - - class ADirectedAcyclicGraph : public ::testing::Test + class ADirectedAcyclicGraph : public ALogicEngine { protected: DirectedAcyclicGraph m_graph; - std::array m_testNodes + std::array m_testNodes { - LogicNodeDummyImpl { "1", false }, - LogicNodeDummyImpl { "2", false }, - LogicNodeDummyImpl { "3", false }, - LogicNodeDummyImpl { "4", false }, - LogicNodeDummyImpl { "5", false }, - LogicNodeDummyImpl { "6", false }, + &m_logicEngine->createLuaScript(m_valid_empty_script, {}, "1")->impl(), + &m_logicEngine->createLuaScript(m_valid_empty_script, {}, "2")->impl(), + &m_logicEngine->createLuaScript(m_valid_empty_script, {}, "3")->impl(), + &m_logicEngine->createLuaScript(m_valid_empty_script, {}, "4")->impl(), + &m_logicEngine->createLuaScript(m_valid_empty_script, {}, "5")->impl(), + &m_logicEngine->createLuaScript(m_valid_empty_script, {}, "6")->impl() }; // For easier access in the tests - LogicNodeDummyImpl& N1 { m_testNodes[0] }; - LogicNodeDummyImpl& N2 { m_testNodes[1] }; - LogicNodeDummyImpl& N3 { m_testNodes[2] }; - LogicNodeDummyImpl& N4 { m_testNodes[3] }; - LogicNodeDummyImpl& N5 { m_testNodes[4] }; - LogicNodeDummyImpl& N6 { m_testNodes[5] }; + LogicNodeImpl& N1 { *m_testNodes[0] }; + LogicNodeImpl& N2 { *m_testNodes[1] }; + LogicNodeImpl& N3 { *m_testNodes[2] }; + LogicNodeImpl& N4 { *m_testNodes[3] }; + LogicNodeImpl& N5 { *m_testNodes[4] }; + LogicNodeImpl& N6 { *m_testNodes[5] }; std::unordered_map m_ordering; @@ -44,7 +43,7 @@ namespace ramses::internal { for (size_t i = 0; i < howMany; ++i) { - m_graph.addNode(m_testNodes[i]); + m_graph.addNode(*m_testNodes[i]); } } diff --git a/client/logic/unittests/internal/EnvironmentProtectionTest.cpp b/tests/unittests/client/logic/internal/EnvironmentProtectionTest.cpp similarity index 99% rename from client/logic/unittests/internal/EnvironmentProtectionTest.cpp rename to tests/unittests/client/logic/internal/EnvironmentProtectionTest.cpp index 6c1778f88..1b49ac367 100644 --- a/client/logic/unittests/internal/EnvironmentProtectionTest.cpp +++ b/tests/unittests/client/logic/internal/EnvironmentProtectionTest.cpp @@ -8,8 +8,8 @@ #include "gmock/gmock.h" -#include "internals/SolState.h" -#include "internals/EnvironmentProtection.h" +#include "internal/logic/SolState.h" +#include "internal/logic/EnvironmentProtection.h" namespace ramses::internal { diff --git a/client/logic/unittests/internal/LogicNodeDependenciesTest.cpp b/tests/unittests/client/logic/internal/LogicNodeDependenciesTest.cpp similarity index 69% rename from client/logic/unittests/internal/LogicNodeDependenciesTest.cpp rename to tests/unittests/client/logic/internal/LogicNodeDependenciesTest.cpp index ed57475c1..9515cf718 100644 --- a/client/logic/unittests/internal/LogicNodeDependenciesTest.cpp +++ b/tests/unittests/client/logic/internal/LogicNodeDependenciesTest.cpp @@ -9,9 +9,10 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "internals/LogicNodeDependencies.h" -#include "internals/ErrorReporting.h" +#include "internal/logic/LogicNodeDependencies.h" +#include "impl/ErrorReporting.h" #include "LogicNodeDummy.h" +#include "RamsesTestUtils.h" namespace ramses::internal { @@ -59,11 +60,15 @@ namespace ramses::internal } } - LogicNodeDummyImpl m_nodeA{ "A", false }; - LogicNodeDummyImpl m_nodeB{ "B", false }; - LogicNodeDependencies m_dependencies; ErrorReporting m_errorReporting; + RamsesTestSetup m_ramses; + SceneImpl& m_scene{ m_ramses.createScene()->impl() }; + + LogicNodeDummyImpl m_nodeA{ m_scene, "A", false }; + LogicNodeDummyImpl m_nodeB{ m_scene, "B", false }; + RamsesBindingDummyImpl m_binding1{ m_scene }; + RamsesBindingDummyImpl m_binding2{ m_scene }; }; TEST_F(ALogicNodeDependencies, IsEmptyAfterConstruction) @@ -91,8 +96,8 @@ namespace ramses::internal m_dependencies.addNode(m_nodeA); m_dependencies.addNode(m_nodeB); - PropertyImpl& output = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - PropertyImpl& input = *m_nodeB.getInputs()->getChild("input1")->m_impl; + PropertyImpl& output = m_nodeA.getOutputs()->getChild("output1")->impl(); + PropertyImpl& input = m_nodeB.getInputs()->getChild("input1")->impl(); EXPECT_TRUE(m_dependencies.link(output, input, false, m_errorReporting)); @@ -108,8 +113,8 @@ namespace ramses::internal m_dependencies.addNode(m_nodeA); m_dependencies.addNode(m_nodeB); - PropertyImpl& output = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - PropertyImpl& input = *m_nodeB.getInputs()->getChild("input1")->m_impl; + PropertyImpl& output = m_nodeA.getOutputs()->getChild("output1")->impl(); + PropertyImpl& input = m_nodeB.getInputs()->getChild("input1")->impl(); EXPECT_TRUE(m_dependencies.link(output, input, true, m_errorReporting)); @@ -125,8 +130,8 @@ namespace ramses::internal m_dependencies.addNode(m_nodeA); m_dependencies.addNode(m_nodeB); - PropertyImpl& output = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - PropertyImpl& input = *m_nodeB.getInputs()->getChild("input1")->m_impl; + PropertyImpl& output = m_nodeA.getOutputs()->getChild("output1")->impl(); + PropertyImpl& input = m_nodeB.getInputs()->getChild("input1")->impl(); EXPECT_TRUE(m_dependencies.link(output, input, false, m_errorReporting)); EXPECT_TRUE(m_dependencies.unlink(output, input, m_errorReporting)); @@ -140,13 +145,13 @@ namespace ramses::internal TEST_F(ALogicNodeDependencies, RemovingSourceNode_RemovesLinks) { - auto nodeToDelete = std::make_unique("node"); + auto nodeToDelete = std::make_unique(m_scene, "node"); m_dependencies.addNode(*nodeToDelete); m_dependencies.addNode(m_nodeB); - PropertyImpl& input = *m_nodeB.getInputs()->getChild("input1")->m_impl; - EXPECT_TRUE(m_dependencies.link(*nodeToDelete->getOutputs()->getChild("output1")->m_impl, input, false, m_errorReporting)); + PropertyImpl& input = m_nodeB.getInputs()->getChild("input1")->impl(); + EXPECT_TRUE(m_dependencies.link(nodeToDelete->getOutputs()->getChild("output1")->impl(), input, false, m_errorReporting)); m_dependencies.removeNode(*nodeToDelete); nodeToDelete = nullptr; @@ -158,12 +163,12 @@ namespace ramses::internal TEST_F(ALogicNodeDependencies, RemovingTargetNode_RemovesLinks) { - auto nodeToDelete = std::make_unique("node"); + auto nodeToDelete = std::make_unique(m_scene, "node"); m_dependencies.addNode(m_nodeA); m_dependencies.addNode(*nodeToDelete); - PropertyImpl& output = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - EXPECT_TRUE(m_dependencies.link(output, *nodeToDelete->getInputs()->getChild("input1")->m_impl, false, m_errorReporting)); + PropertyImpl& output = m_nodeA.getOutputs()->getChild("output1")->impl(); + EXPECT_TRUE(m_dependencies.link(output, nodeToDelete->getInputs()->getChild("input1")->impl(), false, m_errorReporting)); m_dependencies.removeNode(*nodeToDelete); nodeToDelete = nullptr; @@ -175,18 +180,18 @@ namespace ramses::internal TEST_F(ALogicNodeDependencies, RemovingMiddleNode_DoesNotAffectRelativeOrderOfOtherNodes) { - auto nodeToDelete = std::make_unique("M", false); + auto nodeToDelete = std::make_unique(m_scene, "M", false); m_dependencies.addNode(m_nodeA); m_dependencies.addNode(*nodeToDelete); m_dependencies.addNode(m_nodeB); - PropertyImpl& output1A = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - PropertyImpl& output2A = *m_nodeA.getOutputs()->getChild("output2")->m_impl; - PropertyImpl& output1M = *nodeToDelete->getOutputs()->getChild("output1")->m_impl; - PropertyImpl& input1M = *nodeToDelete->getInputs()->getChild("input1")->m_impl; - PropertyImpl& input1B = *m_nodeB.getInputs()->getChild("input1")->m_impl; - PropertyImpl& input2B = *m_nodeB.getInputs()->getChild("input2")->m_impl; + PropertyImpl& output1A = m_nodeA.getOutputs()->getChild("output1")->impl(); + PropertyImpl& output2A = m_nodeA.getOutputs()->getChild("output2")->impl(); + PropertyImpl& output1M = nodeToDelete->getOutputs()->getChild("output1")->impl(); + PropertyImpl& input1M = nodeToDelete->getInputs()->getChild("input1")->impl(); + PropertyImpl& input1B = m_nodeB.getInputs()->getChild("input1")->impl(); + PropertyImpl& input2B = m_nodeB.getInputs()->getChild("input2")->impl(); // A -> M -> B // \ / @@ -217,8 +222,8 @@ namespace ramses::internal m_dependencies.addNode(m_nodeB); // Node A -> Node B (output of node A linked to input of node B) - PropertyImpl& inputB = *m_nodeB.getInputs()->getChild("input1")->m_impl; - PropertyImpl& outputA = *m_nodeA.getOutputs()->getChild("output1")->m_impl; + PropertyImpl& inputB = m_nodeB.getInputs()->getChild("input1")->impl(); + PropertyImpl& outputA = m_nodeA.getOutputs()->getChild("output1")->impl(); EXPECT_TRUE(m_dependencies.link(outputA, inputB, false, m_errorReporting)); expectSortedNodeOrder({ &m_nodeA, &m_nodeB }); @@ -226,8 +231,8 @@ namespace ramses::internal // Reverse dependency // Node B -> Node A (output of node B linked to input of node A) EXPECT_TRUE(m_dependencies.unlink(outputA, inputB, m_errorReporting)); - PropertyImpl& inputA = *m_nodeA.getInputs()->getChild("input1")->m_impl; - PropertyImpl& outputB = *m_nodeB.getOutputs()->getChild("output1")->m_impl; + PropertyImpl& inputA = m_nodeA.getInputs()->getChild("input1")->impl(); + PropertyImpl& outputB = m_nodeB.getOutputs()->getChild("output1")->impl(); EXPECT_TRUE(m_dependencies.link(outputB, inputA, false, m_errorReporting)); @@ -247,8 +252,8 @@ namespace ramses::internal m_dependencies.addNode(m_nodeB); // Node A -> Node B (output of node A linked to input of node B) - PropertyImpl& inputB = *m_nodeB.getInputs()->getChild("input1")->m_impl; - PropertyImpl& outputA = *m_nodeA.getOutputs()->getChild("output1")->m_impl; + PropertyImpl& inputB = m_nodeB.getInputs()->getChild("input1")->impl(); + PropertyImpl& outputA = m_nodeA.getOutputs()->getChild("output1")->impl(); EXPECT_TRUE(m_dependencies.link(outputA, inputB, false, m_errorReporting)); expectSortedNodeOrder({ &m_nodeA, &m_nodeB }); @@ -256,8 +261,8 @@ namespace ramses::internal // Reverse dependency but only using back link // Node B -> Node A (output of node B linked to input of node A) EXPECT_TRUE(m_dependencies.unlink(outputA, inputB, m_errorReporting)); - PropertyImpl& inputA = *m_nodeA.getInputs()->getChild("input1")->m_impl; - PropertyImpl& outputB = *m_nodeB.getOutputs()->getChild("output1")->m_impl; + PropertyImpl& inputA = m_nodeA.getInputs()->getChild("input1")->impl(); + PropertyImpl& outputB = m_nodeB.getOutputs()->getChild("output1")->impl(); EXPECT_TRUE(m_dependencies.link(outputB, inputA, true, m_errorReporting)); @@ -276,10 +281,10 @@ namespace ramses::internal m_dependencies.addNode(m_nodeA); m_dependencies.addNode(m_nodeB); - PropertyImpl& inputA = *m_nodeA.getInputs()->getChild("input1")->m_impl; - PropertyImpl& outputA = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - PropertyImpl& inputB = *m_nodeB.getInputs()->getChild("input1")->m_impl; - PropertyImpl& outputB = *m_nodeB.getOutputs()->getChild("output1")->m_impl; + PropertyImpl& inputA = m_nodeA.getInputs()->getChild("input1")->impl(); + PropertyImpl& outputA = m_nodeA.getOutputs()->getChild("output1")->impl(); + PropertyImpl& inputB = m_nodeB.getInputs()->getChild("input1")->impl(); + PropertyImpl& outputB = m_nodeB.getOutputs()->getChild("output1")->impl(); // Node A -> Node B (output of node A linked to input of node B) EXPECT_TRUE(m_dependencies.link(outputA, inputB, false, m_errorReporting)); @@ -302,14 +307,14 @@ namespace ramses::internal m_dependencies.addNode(*m_nodeANested); m_dependencies.addNode(*m_nodeBNested); - m_nestedOutputA = m_nodeANested->getOutputs()->getChild("outputStruct")->getChild("nested")->m_impl.get(); - m_nestedInputB = m_nodeBNested->getInputs()->getChild("inputStruct")->getChild("nested")->m_impl.get(); - m_arrayOutputA = m_nodeANested->getOutputs()->getChild("outputArray")->getChild(0)->m_impl.get(); - m_arrayInputB = m_nodeBNested->getInputs()->getChild("inputArray")->getChild(0)->m_impl.get(); + m_nestedOutputA = &m_nodeANested->getOutputs()->getChild("outputStruct")->getChild("nested")->impl(); + m_nestedInputB = &m_nodeBNested->getInputs()->getChild("inputStruct")->getChild("nested")->impl(); + m_arrayOutputA = &m_nodeANested->getOutputs()->getChild("outputArray")->getChild(0)->impl(); + m_arrayInputB = &m_nodeBNested->getInputs()->getChild("inputArray")->getChild(0)->impl(); } - std::unique_ptr m_nodeANested = std::make_unique( "A", true ); - std::unique_ptr m_nodeBNested = std::make_unique( "B", true ); + std::unique_ptr m_nodeANested = std::make_unique(m_scene, "A", true); + std::unique_ptr m_nodeBNested = std::make_unique(m_scene, "B", true); PropertyImpl* m_nestedOutputA; PropertyImpl* m_nestedInputB; @@ -319,28 +324,28 @@ namespace ramses::internal TEST_F(ALogicNodeDependencies_NestedLinks, ReportsErrorWhenUnlinkingStructInputs_BasedOnTheirType) { - PropertyImpl* structProperty = m_nodeBNested->getInputs()->getChild("inputStruct")->m_impl.get(); - EXPECT_FALSE(m_dependencies.unlink(*m_nestedOutputA, *structProperty, m_errorReporting)); - EXPECT_EQ("Can't unlink properties of complex types directly!", m_errorReporting.getErrors()[0].message); + PropertyImpl& structProperty = m_nodeBNested->getInputs()->getChild("inputStruct")->impl(); + EXPECT_FALSE(m_dependencies.unlink(*m_nestedOutputA, structProperty, m_errorReporting)); + EXPECT_EQ("Can't unlink properties of complex types directly!", m_errorReporting.getError()->message); } TEST_F(ALogicNodeDependencies_NestedLinks, ReportsErrorWhenUnlinkingArrayInputs_BasedOnTheirType) { - PropertyImpl* arrayProperty = m_nodeBNested->getInputs()->getChild("inputArray")->m_impl.get(); - EXPECT_FALSE(m_dependencies.unlink(*m_nestedOutputA, *arrayProperty, m_errorReporting)); - EXPECT_EQ("Can't unlink properties of complex types directly!", m_errorReporting.getErrors()[0].message); + PropertyImpl& arrayProperty = m_nodeBNested->getInputs()->getChild("inputArray")->impl(); + EXPECT_FALSE(m_dependencies.unlink(*m_nestedOutputA, arrayProperty, m_errorReporting)); + EXPECT_EQ("Can't unlink properties of complex types directly!", m_errorReporting.getError()->message); } TEST_F(ALogicNodeDependencies_NestedLinks, ReportsErrorWhenUnlinkingStructs_WithLinkedChildren) { EXPECT_TRUE(m_dependencies.link(*m_nestedOutputA, *m_nestedInputB, false, m_errorReporting)); - EXPECT_TRUE(m_errorReporting.getErrors().empty()); + EXPECT_FALSE(m_errorReporting.getError().has_value()); // Still can't unlink complex type - PropertyImpl* outputParentStruct = m_nodeANested->getOutputs()->getChild("outputStruct")->m_impl.get(); - PropertyImpl* inputParentStruct = m_nodeBNested->getInputs()->getChild("inputStruct")->m_impl.get(); - EXPECT_FALSE(m_dependencies.unlink(*outputParentStruct, *inputParentStruct, m_errorReporting)); - EXPECT_EQ("Can't unlink properties of complex types directly!", m_errorReporting.getErrors()[0].message); + PropertyImpl& outputParentStruct = m_nodeANested->getOutputs()->getChild("outputStruct")->impl(); + PropertyImpl& inputParentStruct = m_nodeBNested->getInputs()->getChild("inputStruct")->impl(); + EXPECT_FALSE(m_dependencies.unlink(outputParentStruct, inputParentStruct, m_errorReporting)); + EXPECT_EQ("Can't unlink properties of complex types directly!", m_errorReporting.getError()->message); } TEST_F(ALogicNodeDependencies_NestedLinks, ConnectingTwoNodes_CreatesALink) @@ -400,8 +405,8 @@ namespace ramses::internal // Reverse dependency // Node B -> Node A (output of node B linked to input of node A) EXPECT_TRUE(m_dependencies.unlink(*m_nestedOutputA, *m_nestedInputB, m_errorReporting)); - PropertyImpl& nestedInputA = *m_nodeANested->getInputs()->getChild("inputStruct")->getChild("nested")->m_impl; - PropertyImpl& nestedOutputB = *m_nodeBNested->getOutputs()->getChild("outputStruct")->getChild("nested")->m_impl; + PropertyImpl& nestedInputA = m_nodeANested->getInputs()->getChild("inputStruct")->getChild("nested")->impl(); + PropertyImpl& nestedOutputB = m_nodeBNested->getOutputs()->getChild("outputStruct")->getChild("nested")->impl(); EXPECT_TRUE(m_dependencies.link(nestedOutputB, nestedInputA, false, m_errorReporting)); @@ -416,30 +421,28 @@ namespace ramses::internal TEST_F(ALogicNodeDependencies, AddsDependencyToBinding) { - RamsesBindingDummyImpl binding; m_dependencies.addNode(m_nodeA); - m_dependencies.addNode(binding); + m_dependencies.addNode(m_binding1); - m_dependencies.addBindingDependency(binding, m_nodeA); + m_dependencies.addBindingDependency(m_binding1, m_nodeA); - expectSortedNodeOrder({ &binding, &m_nodeA }); + expectSortedNodeOrder({ &m_binding1, &m_nodeA }); } TEST_F(ALogicNodeDependencies, AddsDependencyToBinding_WithLinkedNodes) { - RamsesBindingDummyImpl binding; m_dependencies.addNode(m_nodeA); m_dependencies.addNode(m_nodeB); - m_dependencies.addNode(binding); + m_dependencies.addNode(m_binding1); - PropertyImpl& output = *m_nodeA.getOutputs()->getChild("output1")->m_impl; - PropertyImpl& input = *m_nodeB.getInputs()->getChild("input1")->m_impl; + PropertyImpl& output = m_nodeA.getOutputs()->getChild("output1")->impl(); + PropertyImpl& input = m_nodeB.getInputs()->getChild("input1")->impl(); EXPECT_TRUE(m_dependencies.link(output, input, false, m_errorReporting)); - m_dependencies.addBindingDependency(binding, m_nodeA); + m_dependencies.addBindingDependency(m_binding1, m_nodeA); // Sorted topologically - expectSortedNodeOrder({ &binding, &m_nodeA, &m_nodeB }); + expectSortedNodeOrder({ &m_binding1, &m_nodeA, &m_nodeB }); // Has exactly one link expectLink(output, { {&input, false} }); @@ -447,56 +450,51 @@ namespace ramses::internal TEST_F(ALogicNodeDependencies, RemovesDependencyToBinding) { - RamsesBindingDummyImpl binding; m_dependencies.addNode(m_nodeA); - m_dependencies.addNode(binding); + m_dependencies.addNode(m_binding1); - m_dependencies.addBindingDependency(binding, m_nodeA); - expectSortedNodeOrder({ &binding, &m_nodeA }); + m_dependencies.addBindingDependency(m_binding1, m_nodeA); + expectSortedNodeOrder({ &m_binding1, &m_nodeA }); - m_dependencies.removeBindingDependency(binding, m_nodeA); - expectUnsortedNodeOrder({ &binding, &m_nodeA }); + m_dependencies.removeBindingDependency(m_binding1, m_nodeA); + expectUnsortedNodeOrder({ &m_binding1, &m_nodeA }); } TEST_F(ALogicNodeDependencies, RemovesDependencyToBindingAfterNodeRemoved) { - RamsesBindingDummyImpl binding; m_dependencies.addNode(m_nodeA); - m_dependencies.addNode(binding); + m_dependencies.addNode(m_binding1); - m_dependencies.addBindingDependency(binding, m_nodeA); - expectSortedNodeOrder({ &binding, &m_nodeA }); + m_dependencies.addBindingDependency(m_binding1, m_nodeA); + expectSortedNodeOrder({ &m_binding1, &m_nodeA }); m_dependencies.removeNode(m_nodeA); - expectUnsortedNodeOrder({ &binding }); + expectUnsortedNodeOrder({ &m_binding1 }); } TEST_F(ALogicNodeDependencies, RemovesDependencyToBindingAfterBindingRemoved) { - RamsesBindingDummyImpl binding; m_dependencies.addNode(m_nodeA); - m_dependencies.addNode(binding); + m_dependencies.addNode(m_binding1); - m_dependencies.addBindingDependency(binding, m_nodeA); - expectSortedNodeOrder({ &binding, &m_nodeA }); + m_dependencies.addBindingDependency(m_binding1, m_nodeA); + expectSortedNodeOrder({ &m_binding1, &m_nodeA }); - m_dependencies.removeNode(binding); + m_dependencies.removeNode(m_binding1); expectUnsortedNodeOrder({ &m_nodeA }); } TEST_F(ALogicNodeDependencies, RespectsSwappedDependencyBetweenBindings) { - RamsesBindingDummyImpl binding1; - RamsesBindingDummyImpl binding2; - m_dependencies.addNode(binding1); - m_dependencies.addNode(binding2); + m_dependencies.addNode(m_binding1); + m_dependencies.addNode(m_binding2); - m_dependencies.addBindingDependency(binding1, binding2); - expectSortedNodeOrder({ &binding1, &binding2 }); + m_dependencies.addBindingDependency(m_binding1, m_binding2); + expectSortedNodeOrder({ &m_binding1, &m_binding2 }); - m_dependencies.removeBindingDependency(binding1, binding2); + m_dependencies.removeBindingDependency(m_binding1, m_binding2); - m_dependencies.addBindingDependency(binding2, binding1); - expectSortedNodeOrder({ &binding2, &binding1 }); + m_dependencies.addBindingDependency(m_binding2, m_binding1); + expectSortedNodeOrder({ &m_binding2, &m_binding1 }); } } diff --git a/client/logic/unittests/internal/LuaCustomizationsTest.cpp b/tests/unittests/client/logic/internal/LuaCustomizationsTest.cpp similarity index 98% rename from client/logic/unittests/internal/LuaCustomizationsTest.cpp rename to tests/unittests/client/logic/internal/LuaCustomizationsTest.cpp index 308dec612..191dce720 100644 --- a/client/logic/unittests/internal/LuaCustomizationsTest.cpp +++ b/tests/unittests/client/logic/internal/LuaCustomizationsTest.cpp @@ -8,13 +8,13 @@ #include "gmock/gmock.h" -#include "ramses-logic/Property.h" -#include "impl/PropertyImpl.h" -#include "internals/LuaCustomizations.h" -#include "internals/WrappedLuaProperty.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/ErrorReporting.h" -#include "internals/PropertyTypeExtractor.h" +#include "ramses/client/logic/Property.h" +#include "impl/logic/PropertyImpl.h" +#include "internal/logic/LuaCustomizations.h" +#include "internal/logic/WrappedLuaProperty.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "impl/ErrorReporting.h" +#include "internal/logic/PropertyTypeExtractor.h" #include "glm/gtx/range.hpp" #include @@ -37,12 +37,12 @@ namespace ramses::internal m_interfaceEnvironment = sol::environment(m_sol, sol::create, m_sol.globals()); PropertyTypeExtractor::RegisterTypes(m_interfaceEnvironment); - m_structProp.getChild("field1")->m_impl->setValue(5); - m_structProp.getChild("field2")->m_impl->setValue(6); + m_structProp.getChild("field1")->impl().setValue(5); + m_structProp.getChild("field2")->impl().setValue(6); - m_arrayProp.getChild(0)->m_impl->setValue(11); - m_arrayProp.getChild(1)->m_impl->setValue(12); - m_arrayProp.getChild(2)->m_impl->setValue(13); + m_arrayProp.getChild(0)->impl().setValue(11); + m_arrayProp.getChild(1)->impl().setValue(12); + m_arrayProp.getChild(2)->impl().setValue(13); } sol::protected_function_result run_WithResult(std::string_view source) @@ -541,7 +541,7 @@ namespace ramses::internal mod.mytable = {nested = {a = 42}} return mod )", "", errors, {}, false); - ASSERT_TRUE(errors.getErrors().empty()); + ASSERT_FALSE(errors.getError().has_value()); sol::load_result loadResult = solState.loadScript(R"( -- module has one key/value pair - a table named 'mytable' @@ -757,7 +757,7 @@ namespace ramses::internal nested = {a = 11, b = 12}} return mod )", "", errors, {}, false); - ASSERT_TRUE(errors.getErrors().empty()); + ASSERT_FALSE(errors.getError().has_value()); sol::load_result loadResult = solState.loadScript(R"( for k,v in rl_pairs(mod.mytable.nested) do @@ -966,7 +966,7 @@ namespace ramses::internal nested = {[1] = 11, [2] = 12}} return mod )", "", errors, {}, false); - ASSERT_TRUE(errors.getErrors().empty()); + ASSERT_FALSE(errors.getError().has_value()); // Check that iterating over custom indexed table works and the order is the same (ascending by numeric index) sol::load_result loadResult = solState.loadScript(R"( diff --git a/client/logic/unittests/internal/LuaTypeConversionsTest.cpp b/tests/unittests/client/logic/internal/LuaTypeConversionsTest.cpp similarity index 99% rename from client/logic/unittests/internal/LuaTypeConversionsTest.cpp rename to tests/unittests/client/logic/internal/LuaTypeConversionsTest.cpp index 19a69c04c..d12c7c517 100644 --- a/client/logic/unittests/internal/LuaTypeConversionsTest.cpp +++ b/tests/unittests/client/logic/internal/LuaTypeConversionsTest.cpp @@ -8,7 +8,7 @@ #include "gmock/gmock.h" -#include "internals/LuaTypeConversions.h" +#include "internal/logic/LuaTypeConversions.h" namespace ramses::internal { diff --git a/client/logic/unittests/internal/PropertyTypeExtractorTest.cpp b/tests/unittests/client/logic/internal/PropertyTypeExtractorTest.cpp similarity index 99% rename from client/logic/unittests/internal/PropertyTypeExtractorTest.cpp rename to tests/unittests/client/logic/internal/PropertyTypeExtractorTest.cpp index ff4c306be..467b5724e 100644 --- a/client/logic/unittests/internal/PropertyTypeExtractorTest.cpp +++ b/tests/unittests/client/logic/internal/PropertyTypeExtractorTest.cpp @@ -8,7 +8,7 @@ #include "gmock/gmock.h" -#include "internals/PropertyTypeExtractor.h" +#include "internal/logic/PropertyTypeExtractor.h" namespace ramses::internal { diff --git a/client/logic/unittests/internal/RamsesHelperTest.cpp b/tests/unittests/client/logic/internal/RamsesHelperTest.cpp similarity index 94% rename from client/logic/unittests/internal/RamsesHelperTest.cpp rename to tests/unittests/client/logic/internal/RamsesHelperTest.cpp index a09adb807..24c895228 100644 --- a/client/logic/unittests/internal/RamsesHelperTest.cpp +++ b/tests/unittests/client/logic/internal/RamsesHelperTest.cpp @@ -8,12 +8,13 @@ #include "gtest/gtest.h" -#include "internals/RamsesHelper.h" +#include "internal/logic/RamsesHelper.h" namespace ramses::internal { TEST(ARamsesHelper, ConvertsRamsesUniformTypeToEpropertyType) { + EXPECT_EQ(EPropertyType::Bool, ConvertRamsesUniformTypeToPropertyType(ramses::EDataType::Bool)); EXPECT_EQ(EPropertyType::Int32, ConvertRamsesUniformTypeToPropertyType(ramses::EDataType::Int32)); EXPECT_EQ(std::nullopt, ConvertRamsesUniformTypeToPropertyType(ramses::EDataType::UInt16)); EXPECT_EQ(std::nullopt, ConvertRamsesUniformTypeToPropertyType(ramses::EDataType::UInt32)); diff --git a/client/logic/unittests/internal/RamsesObjectResolverTest.cpp b/tests/unittests/client/logic/internal/RamsesObjectResolverTest.cpp similarity index 79% rename from client/logic/unittests/internal/RamsesObjectResolverTest.cpp rename to tests/unittests/client/logic/internal/RamsesObjectResolverTest.cpp index 920e30442..76e209497 100644 --- a/client/logic/unittests/internal/RamsesObjectResolverTest.cpp +++ b/tests/unittests/client/logic/internal/RamsesObjectResolverTest.cpp @@ -8,15 +8,15 @@ #include "gtest/gtest.h" -#include "internals/RamsesObjectResolver.h" -#include "internals/ErrorReporting.h" -#include "impl/RamsesNodeBindingImpl.h" +#include "internal/logic/RamsesObjectResolver.h" +#include "impl/ErrorReporting.h" +#include "impl/logic/NodeBindingImpl.h" #include "RamsesTestUtils.h" -#include "ramses-client-api/Node.h" -#include "ramses-client-api/Appearance.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderPass.h" +#include "ramses/client/Node.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderPass.h" namespace ramses::internal { @@ -26,35 +26,35 @@ namespace ramses::internal RamsesTestSetup m_ramsesTestSetup; ramses::Scene* m_scene { m_ramsesTestSetup.createScene() }; ErrorReporting m_errors; - RamsesObjectResolver m_resolver {m_errors, *m_scene}; + RamsesObjectResolver m_resolver {m_errors, m_scene->impl() }; }; TEST_F(ARamsesObjectResolver, FindsSceneNodeByItsId) { ramses::Node* node = m_scene->createNode(); EXPECT_EQ(node, m_resolver.findRamsesNodeInScene("some logic node", node->getSceneObjectId())); - EXPECT_TRUE(m_errors.getErrors().empty()); + EXPECT_FALSE(m_errors.getError().has_value()); } TEST_F(ARamsesObjectResolver, FindsAppearanceByItsId) { ramses::Appearance& appearance = RamsesTestSetup::CreateTrivialTestAppearance(*m_scene); EXPECT_EQ(&appearance, m_resolver.findRamsesAppearanceInScene("some logic node", appearance.getSceneObjectId())); - EXPECT_TRUE(m_errors.getErrors().empty()); + EXPECT_FALSE(m_errors.getError().has_value()); } TEST_F(ARamsesObjectResolver, FindsCameraByItsId) { ramses::Camera* camera = m_scene->createPerspectiveCamera(); EXPECT_EQ(camera, m_resolver.findRamsesCameraInScene("some logic node", camera->getSceneObjectId())); - EXPECT_TRUE(m_errors.getErrors().empty()); + EXPECT_FALSE(m_errors.getError().has_value()); } TEST_F(ARamsesObjectResolver, FindsRenderPassByItsId) { const ramses::RenderPass* rp = m_scene->createRenderPass(); EXPECT_EQ(rp, m_resolver.findRamsesRenderPassInScene("some logic node", rp->getSceneObjectId())); - EXPECT_TRUE(m_errors.getErrors().empty()); + EXPECT_FALSE(m_errors.getError().has_value()); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenNodeWithGivenIdDoesNotExist) @@ -62,8 +62,8 @@ namespace ramses::internal ramses::sceneObjectId_t fakeNodeId{ 42 }; EXPECT_FALSE(m_resolver.findRamsesNodeInScene("some logic node", fakeNodeId)); - EXPECT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenAppearanceWithGivenIdDoesNotExist) @@ -71,8 +71,8 @@ namespace ramses::internal ramses::sceneObjectId_t fakeAppearanceId{ 42 }; EXPECT_FALSE(m_resolver.findRamsesAppearanceInScene("some logic node", fakeAppearanceId)); - EXPECT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenCameraWithGivenIdDoesNotExist) @@ -80,8 +80,8 @@ namespace ramses::internal ramses::sceneObjectId_t fakeCameraId{ 42 }; EXPECT_FALSE(m_resolver.findRamsesCameraInScene("some logic node", fakeCameraId)); - EXPECT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenRenderPassWithGivenIdDoesNotExist) @@ -89,8 +89,8 @@ namespace ramses::internal ramses::sceneObjectId_t fakeRenderPassId{ 42 }; EXPECT_FALSE(m_resolver.findRamsesRenderPassInScene("some logic node", fakeRenderPassId)); - EXPECT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Serialized Ramses Logic object 'some logic node' points to a Ramses object (id: 42) which couldn't be found in the provided scene!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenObjectWithGivenIdExists_ButIsNotANode) @@ -98,8 +98,8 @@ namespace ramses::internal ramses::Appearance& appearance = RamsesTestSetup::CreateTrivialTestAppearance(*m_scene); EXPECT_FALSE(m_resolver.findRamsesNodeInScene("some logic node", appearance.getSceneObjectId())); - ASSERT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 2) which is not of the same type!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 2) which is not of the same type!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenResolvedObjectExists_ButIsNotCamera) @@ -107,8 +107,8 @@ namespace ramses::internal ramses::Node* node = m_scene->createNode(); EXPECT_FALSE(m_resolver.findRamsesCameraInScene("some logic node", node->getSceneObjectId())); - ASSERT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 1) which is not of the same type!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 1) which is not of the same type!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenResolvedObjectExists_ButIsNotAppearance) @@ -116,8 +116,8 @@ namespace ramses::internal ramses::Node* node = m_scene->createNode(); EXPECT_FALSE(m_resolver.findRamsesAppearanceInScene("some logic node", node->getSceneObjectId())); - ASSERT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 1) which is not of the same type!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 1) which is not of the same type!", m_errors.getError()->message); } TEST_F(ARamsesObjectResolver, ReportsErrorWhenResolvedObjectExists_ButIsNotRenderPass) @@ -125,8 +125,8 @@ namespace ramses::internal ramses::Node* node = m_scene->createNode(); EXPECT_FALSE(m_resolver.findRamsesRenderPassInScene("some logic node", node->getSceneObjectId())); - ASSERT_EQ(1u, m_errors.getErrors().size()); - EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 1) which is not of the same type!", m_errors.getErrors()[0].message); + ASSERT_TRUE(m_errors.getError().has_value()); + EXPECT_EQ("Fatal error during loading from file! Ramses binding 'some logic node' points to a Ramses scene object (id: 1) which is not of the same type!", m_errors.getError()->message); } // Special case (ramses Camera is also a Node) - test that it works as expected when resolved by Id @@ -135,6 +135,6 @@ namespace ramses::internal ramses::Camera* camera = m_scene->createPerspectiveCamera(); EXPECT_EQ(camera, m_resolver.findRamsesNodeInScene("some logic node", camera->getSceneObjectId())); - EXPECT_TRUE(m_errors.getErrors().empty()); + EXPECT_FALSE(m_errors.getError().has_value()); } } diff --git a/client/logic/unittests/internal/SolHelperTest.cpp b/tests/unittests/client/logic/internal/SolHelperTest.cpp similarity index 97% rename from client/logic/unittests/internal/SolHelperTest.cpp rename to tests/unittests/client/logic/internal/SolHelperTest.cpp index d67a44b24..102dc20fd 100644 --- a/client/logic/unittests/internal/SolHelperTest.cpp +++ b/tests/unittests/client/logic/internal/SolHelperTest.cpp @@ -8,7 +8,7 @@ #include "gtest/gtest.h" -#include "internals/SolHelper.h" +#include "internal/logic/SolHelper.h" namespace sol_helper { diff --git a/client/logic/unittests/internal/SolStateTest.cpp b/tests/unittests/client/logic/internal/SolStateTest.cpp similarity index 98% rename from client/logic/unittests/internal/SolStateTest.cpp rename to tests/unittests/client/logic/internal/SolStateTest.cpp index 708a6026e..f0af2569d 100644 --- a/client/logic/unittests/internal/SolStateTest.cpp +++ b/tests/unittests/client/logic/internal/SolStateTest.cpp @@ -8,10 +8,10 @@ #include "gmock/gmock.h" -#include "internals/SolState.h" -#include "internals/SolWrapper.h" -#include "internals/LuaCompilationUtils.h" -#include "internals/ErrorReporting.h" +#include "internal/logic/SolState.h" +#include "internal/logic/SolWrapper.h" +#include "internal/logic/LuaCompilationUtils.h" +#include "impl/ErrorReporting.h" namespace ramses::internal { diff --git a/client/logic/unittests/internal/TypeDataTest.cpp b/tests/unittests/client/logic/internal/TypeDataTest.cpp similarity index 99% rename from client/logic/unittests/internal/TypeDataTest.cpp rename to tests/unittests/client/logic/internal/TypeDataTest.cpp index 5e680a65e..eb2d60b99 100644 --- a/client/logic/unittests/internal/TypeDataTest.cpp +++ b/tests/unittests/client/logic/internal/TypeDataTest.cpp @@ -8,7 +8,7 @@ #include "gmock/gmock.h" -#include "internals/TypeData.h" +#include "internal/logic/TypeData.h" namespace ramses::internal { diff --git a/client/logic/unittests/internal/TypeUtilsTest.cpp b/tests/unittests/client/logic/internal/TypeUtilsTest.cpp similarity index 89% rename from client/logic/unittests/internal/TypeUtilsTest.cpp rename to tests/unittests/client/logic/internal/TypeUtilsTest.cpp index 48211caad..a8551ceda 100644 --- a/client/logic/unittests/internal/TypeUtilsTest.cpp +++ b/tests/unittests/client/logic/internal/TypeUtilsTest.cpp @@ -6,26 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "internals/TypeUtils.h" - +#include "internal/logic/TypeUtils.h" #include "gmock/gmock.h" -#include "LogicNodeDummy.h" - namespace ramses::internal { class ATypeUtils : public ::testing::Test { - protected: - std::unique_ptr createArrayProperty(size_t size, EPropertyType type) - { - auto property = std::make_unique(MakeArray("", size, type), EPropertySemantics::BindingInput); - property->setLogicNode(dummyNode); - - return property; - } - - LogicNodeDummyImpl dummyNode{ "DummyNode" }; }; TEST_F(ATypeUtils, DistinguishesValidTypeEnumsFromInvalidOnes) diff --git a/client/logic/unittests/internal/WrappedLuaPropertyTest.cpp b/tests/unittests/client/logic/internal/WrappedLuaPropertyTest.cpp similarity index 98% rename from client/logic/unittests/internal/WrappedLuaPropertyTest.cpp rename to tests/unittests/client/logic/internal/WrappedLuaPropertyTest.cpp index 4d4bc4804..df08c66b6 100644 --- a/client/logic/unittests/internal/WrappedLuaPropertyTest.cpp +++ b/tests/unittests/client/logic/internal/WrappedLuaPropertyTest.cpp @@ -8,9 +8,9 @@ #include "LuaScriptTest_Base.h" -#include "impl/PropertyImpl.h" -#include "ramses-logic/Property.h" -#include "internals/WrappedLuaProperty.h" +#include "impl/logic/PropertyImpl.h" +#include "ramses/client/logic/Property.h" +#include "internal/logic/WrappedLuaProperty.h" #include "LogTestUtils.h" #include "fmt/format.h" @@ -73,7 +73,7 @@ namespace ramses::internal case EPropertyType::Struct: for (size_t i = 0; i < prop.getChildCount(); ++i) { - setDummyDataRecursively(*prop.getChild(i)->m_impl); + setDummyDataRecursively(prop.getChild(i)->impl()); } break; } @@ -101,7 +101,7 @@ namespace ramses::internal TypeData{"Bool", EPropertyType::Bool}}); sol::state m_sol; - ScopedLogContextLevel m_silenceLogs = ScopedLogContextLevel{ELogLevel::Off}; + ScopedLogContextLevel m_silenceLogs = ScopedLogContextLevel{CONTEXT_CLIENT, ELogLevel::Off}; }; class AWrappedLuaProperty_Access : public AWrappedLuaProperty @@ -217,7 +217,7 @@ namespace ramses::internal m_sol["Nested"] = std::ref(wrapped); //Set second element to a different value - nestedArray.getChild("array")->getChild(1)->m_impl->setValue(vec3f{15, 16, 17}); + nestedArray.getChild("array")->getChild(1)->impl().setValue(vec3f{15, 16, 17}); EXPECT_FLOAT_EQ(1.1f, extractValue("Nested.array[1][1]")); EXPECT_FLOAT_EQ(1.2f, extractValue("Nested.array[1][2]")); diff --git a/client/logic/unittests/shared/FeatureLevelTestValues.h b/tests/unittests/client/logic/shared/FeatureLevelTestValues.h similarity index 93% rename from client/logic/unittests/shared/FeatureLevelTestValues.h rename to tests/unittests/client/logic/shared/FeatureLevelTestValues.h index 72f7ffc1a..9c2347245 100644 --- a/client/logic/unittests/shared/FeatureLevelTestValues.h +++ b/tests/unittests/client/logic/shared/FeatureLevelTestValues.h @@ -9,7 +9,7 @@ #pragma once #include "gtest/gtest.h" -#include "ramses-framework-api/EFeatureLevel.h" +#include "ramses/framework/EFeatureLevel.h" namespace ramses::internal { diff --git a/tests/unittests/client/logic/shared/LogicEngineTestWithCreationHelper.h b/tests/unittests/client/logic/shared/LogicEngineTestWithCreationHelper.h new file mode 100644 index 000000000..1c154b35e --- /dev/null +++ b/tests/unittests/client/logic/shared/LogicEngineTestWithCreationHelper.h @@ -0,0 +1,135 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/Appearance.h" + +#include "LogicEngineTest_Base.h" + +namespace ramses::internal +{ + using namespace testing; + + using LogicObjectTypes = ::testing::Types < + LuaModule, + LuaScript, + LuaInterface, + NodeBinding, + AppearanceBinding, + CameraBinding, + RenderPassBinding, + RenderGroupBinding, + MeshNodeBinding, + SkinBinding, + DataArray, + AnimationNode, + TimerNode, + AnchorPoint + >; + + class ALogicEngineBaseWithCreationHelper : public ALogicEngineBase + { + public: + template + ObjectType* createObjectOfType([[maybe_unused]] std::string_view name) + { + } + }; + + template <> inline LuaModule* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createLuaModule(this->m_moduleSourceCode, {}, name); + } + template <> inline LuaScript* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createLuaScript(this->m_valid_empty_script, {}, name); + } + template <> inline LuaInterface* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createLuaInterface(this->m_interfaceSourceCode, name); + } + template <> inline NodeBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createNodeBinding(*this->m_node, ERotationType::Euler_XYZ, name); + } + template <> inline AppearanceBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createAppearanceBinding(*this->m_appearance, name); + } + template <> inline CameraBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createCameraBinding(*this->m_camera, name); + } + template <> inline RenderPassBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createRenderPassBinding(*this->m_renderPass, name); + } + template <> inline RenderGroupBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + RenderGroupBindingElements elements; + EXPECT_TRUE(elements.addElement(*this->m_meshNode, "mesh")); + return this->m_logicEngine->createRenderGroupBinding(*this->m_renderGroup, elements, name); + } + template <> inline MeshNodeBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createMeshNodeBinding(*this->m_meshNode, name); + } + template <> inline SkinBinding* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + const auto nodeBinding = this->m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "nodeForSkin"); + auto appearanceBinding = this->m_logicEngine->createAppearanceBinding(*m_appearance, "appearanceForSkin"); + const auto optUniform = appearanceBinding->getRamsesAppearance().getEffect().findUniformInput("jointMat"); + assert(optUniform != std::nullopt); + return this->m_logicEngine->createSkinBinding({ nodeBinding }, { matrix44f{ 0.f } }, *appearanceBinding, *optUniform, name); + } + template <> inline DataArray* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createDataArray({1}, name); + } + template <> inline AnimationNode* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + auto* data = this->m_logicEngine->createDataArray(std::vector{ 0.f, 1.f }); + const AnimationChannel channel{ "channel", data, data, EInterpolationType::Step }; + AnimationNodeConfig config; + config.addChannel(channel); + return this->m_logicEngine->createAnimationNode(config, name); + } + template <> inline TimerNode* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + return this->m_logicEngine->createTimerNode(name); + } + template <> inline AnchorPoint* ALogicEngineBaseWithCreationHelper::createObjectOfType(std::string_view name) + { + const auto nodeBinding = this->m_logicEngine->createNodeBinding(*m_node, ERotationType::Euler_XYZ, "nodeForAnchor"); + const auto camBinding = this->m_logicEngine->createCameraBinding(*this->m_camera, "camForAnchor"); + return this->m_logicEngine->createAnchorPoint(*nodeBinding, *camBinding, name); + } +} diff --git a/tests/unittests/client/logic/shared/LogicEngineTest_Base.h b/tests/unittests/client/logic/shared/LogicEngineTest_Base.h new file mode 100644 index 000000000..66366e64b --- /dev/null +++ b/tests/unittests/client/logic/shared/LogicEngineTest_Base.h @@ -0,0 +1,243 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "RamsesTestUtils.h" +#include "WithTempDirectory.h" + +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/Appearance.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/ramses-utils.h" + +namespace ramses::internal +{ + class ALogicEngineBase + { + public: + explicit ALogicEngineBase(EFeatureLevel featureLevel = EFeatureLevel_Latest) + : m_ramses{ featureLevel } + { + // make ramses camera valid, needed for anchor points + m_camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 1.f); + m_renderGroup->addMeshNode(*m_meshNode); + m_saveFileConfigNoValidation.setValidationEnabled(false); + } + + virtual ~ALogicEngineBase() = default; + + LogicEngine& getLogicEngine() + { + return *m_logicEngine; + } + + static LuaConfig CreateDeps(const std::vector>& dependencies) + { + LuaConfig config; + for (const auto& [alias, module] : dependencies) + { + config.addDependency(alias, *module); + } + + return config; + } + + static LuaConfig WithStdModules(std::initializer_list modules) + { + LuaConfig config; + for (auto m : modules) + { + config.addStandardModuleDependency(m); + } + return config; + } + + bool saveToFile(std::string_view filename, const SaveFileConfig& config = {}) + { + return m_scene->saveToFile(filename, config); + } + + bool saveToFileWithoutValidation(std::string_view filename) + { + SaveFileConfig configNoValidation; + configNoValidation.setValidationEnabled(false); + return m_scene->saveToFile(filename, configNoValidation); + } + + virtual bool recreateFromFile(std::string_view filename) + { + const auto nodeId = m_node->getSceneObjectId(); + const auto cameraId = m_camera->getSceneObjectId(); + const auto appearanceId = m_appearance->getSceneObjectId(); + const auto renderPassId = m_renderPass->getSceneObjectId(); + const auto renderGroupId = m_renderGroup->getSceneObjectId(); + const auto meshNodeId = m_meshNode->getSceneObjectId(); + const auto logicEngineId = m_logicEngine->getSceneObjectId(); + + m_ramses.destroyScene(*m_scene); + m_scene = m_ramses.getClient().loadSceneFromFile(filename); + if (!m_scene) + throw std::runtime_error("scene not loaded"); + m_node = m_scene->findObject(nodeId); + m_camera = m_scene->findObject(cameraId); + m_appearance = m_scene->findObject(appearanceId); + m_renderPass = m_scene->findObject(renderPassId); + m_renderGroup = m_scene->findObject(renderGroupId); + m_meshNode = m_scene->findObject(meshNodeId); + m_logicEngine = m_scene->findObject(logicEngineId); + return m_logicEngine != nullptr; + } + + void expectNoError() + { + auto issue = m_ramses.getFramework().getLastError(); + EXPECT_FALSE(issue.has_value()) << issue->message; + } + + void expectError(std::string_view expectedMsg, const RamsesObject* expectedObj = nullptr) + { + const auto issue = m_ramses.getFramework().getLastError(); + ASSERT_TRUE(issue.has_value()); + EXPECT_EQ(expectedMsg, issue->message); + EXPECT_EQ(expectedObj, issue->object); + } + + void expectErrorSubstring(std::string_view expectedSubStr, const RamsesObject* expectedObj = nullptr) + { + const auto issue = m_ramses.getFramework().getLastError(); + ASSERT_TRUE(issue.has_value()); + EXPECT_THAT(issue->message, ::testing::HasSubstr(expectedSubStr)); + EXPECT_EQ(expectedObj, issue->object); + } + + std::string getLastErrorMessage() + { + const auto issue = m_ramses.getFramework().getLastError(); + EXPECT_TRUE(issue.has_value()); + return issue.value_or(Issue{}).message; + } + + protected: + std::unique_ptr m_withTempDirectory; // must be destroyed after all loaded scenes released the filesystem + SaveFileConfig m_saveFileConfigNoValidation; + RamsesTestSetup m_ramses; + ramses::Scene* m_scene { m_ramses.createScene() }; + + ramses::Node* m_node { m_scene->createNode() }; + ramses::OrthographicCamera* m_camera { m_scene->createOrthographicCamera() }; + ramses::Appearance* m_appearance { &RamsesTestSetup::CreateTrivialTestAppearance(*m_scene) }; + ramses::RenderPass* m_renderPass { m_scene->createRenderPass() }; + ramses::RenderGroup* m_renderGroup { m_scene->createRenderGroup() }; + ramses::MeshNode* m_meshNode { m_scene->createMeshNode("meshNode") }; + + LogicEngine* m_logicEngine{ m_scene->createLogicEngine("testLogic") }; + + const std::string_view m_valid_empty_script = R"( + function interface(IN,OUT) + end + function run(IN,OUT) + end + )"; + + const std::string_view m_invalid_empty_script = R"( + )"; + + const std::string_view m_moduleSourceCode = R"( + local mymath = {} + function mymath.add(a,b) + print(a+b) + end + return mymath + )"; + + const std::string_view m_interfaceSourceCode = R"( + function interface(inout_params) + inout_params.param_vec3f = Type:Vec3f() + end + )"; + + [[nodiscard]] LogicEngine* recreate() + { + const sceneId_t sceneId = m_scene->getSceneId(); + + m_ramses.destroyScene(*m_scene); + m_scene = m_ramses.createScene(sceneId); + m_node = m_scene->createNode(); + m_camera = m_scene->createOrthographicCamera(); + m_appearance = &RamsesTestSetup::CreateTrivialTestAppearance(*m_scene); + m_renderPass = m_scene->createRenderPass(); + m_renderGroup = m_scene->createRenderGroup(); + m_meshNode = m_scene->createMeshNode(); + + return m_scene->createLogicEngine(); + } + + RenderGroupBinding* createRenderGroupBinding(LogicEngine& logicEngine) + { + RenderGroupBindingElements elements; + EXPECT_TRUE(elements.addElement(*m_meshNode, "mesh")); + return logicEngine.createRenderGroupBinding(*m_renderGroup, elements, "renderGroupBinding"); + } + + RenderGroupBinding* createRenderGroupBinding() + { + return createRenderGroupBinding(*m_logicEngine); + } + + static SkinBinding* createSkinBinding(const NodeBinding& nodeBinding, AppearanceBinding& appearanceBinding, LogicEngine& logicEngine) + { + const auto optUniform = appearanceBinding.getRamsesAppearance().getEffect().findUniformInput("jointMat"); + EXPECT_TRUE(optUniform.has_value()); + assert(optUniform != std::nullopt); + return logicEngine.createSkinBinding({ &nodeBinding }, { matrix44f{ 0.f } }, appearanceBinding, *optUniform, "skin"); + } + + SkinBinding* createSkinBinding(LogicEngine& logicEngine) + { + const auto nodeBinding = logicEngine.createNodeBinding(*m_node, ERotationType::Euler_XYZ, "nodeForSkin"); + auto appearanceBinding = logicEngine.createAppearanceBinding(*m_appearance, "appearanceForSkin"); + return createSkinBinding(*nodeBinding, *appearanceBinding, logicEngine); + } + + void withTempDirectory() + { + m_withTempDirectory = std::make_unique(); + } + + size_t m_emptySerializedSizeTotal{164u}; + }; + + class ALogicEngine : public ALogicEngineBase, public ::testing::Test + { + public: + explicit ALogicEngine(EFeatureLevel featureLevel = EFeatureLevel_Latest) + : ALogicEngineBase{ featureLevel } + { + } + }; +} diff --git a/client/logic/unittests/shared/LogicNodeDummy.h b/tests/unittests/client/logic/shared/LogicNodeDummy.h similarity index 83% rename from client/logic/unittests/shared/LogicNodeDummy.h rename to tests/unittests/client/logic/shared/LogicNodeDummy.h index b36ee3d91..48c839034 100644 --- a/client/logic/unittests/shared/LogicNodeDummy.h +++ b/tests/unittests/client/logic/shared/LogicNodeDummy.h @@ -8,20 +8,20 @@ #pragma once -#include "impl/LogicNodeImpl.h" -#include "impl/PropertyImpl.h" -#include "impl/RamsesBindingImpl.h" +#include "impl/logic/LogicNodeImpl.h" +#include "impl/logic/PropertyImpl.h" +#include "impl/logic/RamsesBindingImpl.h" -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/Property.h" namespace ramses::internal { class LogicNodeDummyImpl : public LogicNodeImpl { public: - explicit LogicNodeDummyImpl(std::string_view name, bool createNestedProperties = false) - : LogicNodeImpl(name, 1u) + explicit LogicNodeDummyImpl(SceneImpl& scene, std::string_view name, bool createNestedProperties = false) + : LogicNodeImpl{ scene, name, sceneObjectId_t{1u} } { setRootProperties( std::make_unique(CreateTestInputsType(createNestedProperties), EPropertySemantics::ScriptInput), @@ -72,7 +72,7 @@ namespace ramses::internal class RamsesBindingDummyImpl : public RamsesBindingImpl { public: - RamsesBindingDummyImpl() : RamsesBindingImpl("dummybinding", 1u) + explicit RamsesBindingDummyImpl(SceneImpl& scene) : RamsesBindingImpl{ scene, "dummybinding", sceneObjectId_t{1u} } { } diff --git a/client/logic/unittests/shared/LuaScriptTest_Base.h b/tests/unittests/client/logic/shared/LuaScriptTest_Base.h similarity index 98% rename from client/logic/unittests/shared/LuaScriptTest_Base.h rename to tests/unittests/client/logic/shared/LuaScriptTest_Base.h index 870d9c27c..32a41089b 100644 --- a/client/logic/unittests/shared/LuaScriptTest_Base.h +++ b/tests/unittests/client/logic/shared/LuaScriptTest_Base.h @@ -10,7 +10,7 @@ #include "LogicEngineTest_Base.h" -namespace ramses +namespace ramses::internal { class ALuaScript : public ALogicEngine { diff --git a/client/logic/unittests/shared/PropertyLinkTestUtils.h b/tests/unittests/client/logic/shared/PropertyLinkTestUtils.h similarity index 54% rename from client/logic/unittests/shared/PropertyLinkTestUtils.h rename to tests/unittests/client/logic/shared/PropertyLinkTestUtils.h index e2eb10704..77bdc9c05 100644 --- a/client/logic/unittests/shared/PropertyLinkTestUtils.h +++ b/tests/unittests/client/logic/shared/PropertyLinkTestUtils.h @@ -10,15 +10,15 @@ #include "gtest/gtest.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/Property.h" #include #include #include #include -namespace ramses +namespace ramses::internal { class PropertyLinkTestUtils { @@ -26,19 +26,19 @@ namespace ramses // This util will collect all existing links in logic engine and match them with given expected links, // i.e. the expected links must not only exist but also be the only links in the engine. // This also tests all the relevant public API for querying links. - static void ExpectLinks(const LogicEngine& logicEngine, std::vector expectedLinks) + static void ExpectLinks(LogicEngine& logicEngine, std::vector expectedLinks) { // collect all exisintg links from logic engine - const auto allObjects = logicEngine.getCollection(); + auto allObjects = logicEngine.getCollection(); - std::vector allProperties; - for (const auto obj : allObjects) + std::vector allProperties; + for (auto obj : allObjects) { - const auto logicNode = obj->as(); + auto logicNode = obj->as(); if (!logicNode) continue; - std::deque props{ logicNode->getInputs(), logicNode->getOutputs() }; + std::deque props{ logicNode->getInputs(), logicNode->getOutputs() }; while (!props.empty()) { auto prop = props.back(); @@ -87,15 +87,51 @@ namespace ramses EXPECT_TRUE(expectedLinks[i].target->hasIncomingLink()); } + // test also const getter + std::vector allLinksConst; + for (const auto* prop : allProperties) + { + if (prop->hasIncomingLink()) + allLinksConst.push_back(*prop->getIncomingLink()); + } + + EXPECT_EQ(expectedLinks.size(), allLinksConst.size()) << "Count of expected links does not match actual count of const existing links"; + if (expectedLinks.size() != allLinksConst.size()) + return; + + auto propertLinkCompConst = [](const PropertyLinkConst& a, const PropertyLinkConst& b) { + assert(a.source != b.source || a.target != b.target); + if (a.source != b.source) + return a.source < b.source; + return a.target < b.target; + }; + std::sort(allLinksConst.begin(), allLinksConst.end(), propertLinkCompConst); + + for (size_t i = 0u; i < allLinksConst.size(); ++i) + { + EXPECT_EQ(expectedLinks[i].source, allLinksConst[i].source) << fmt::format("expected and actual source of const link #{} does not match", i); + EXPECT_EQ(expectedLinks[i].target, allLinksConst[i].target) << fmt::format("expected and actual target of const link #{} does not match", i); + EXPECT_EQ(expectedLinks[i].isWeakLink, allLinksConst[i].isWeakLink) << fmt::format("expected and actual weak link flag of const link #{} does not match", i); + } + // test also logic engine getter - auto engineLinks = logicEngine.getPropertyLinks(); + std::vector engineLinks = logicEngine.getPropertyLinks(); std::sort(engineLinks.begin(), engineLinks.end(), propertLinkComp); - const bool equals = std::equal(engineLinks.cbegin(), engineLinks.cend(), expectedLinks.cbegin(), expectedLinks.cend(), [](const auto& l1, const auto& l2) { + auto equals = [](const auto& l1, const auto& l2) { return l1.source == l2.source && l1.target == l2.target && l1.isWeakLink == l2.isWeakLink; - }); - EXPECT_TRUE(equals) << "Links retrieved from LogicEngine API do not match!"; + }; + bool isEqual = std::equal(engineLinks.cbegin(), engineLinks.cend(), expectedLinks.cbegin(), expectedLinks.cend(), equals); + EXPECT_TRUE(isEqual) << "Links retrieved from LogicEngine API do not match!"; + + // test also logic engine's const getter + const LogicEngine& logicEngineConst = logicEngine; + std::vector engineLinksConst = logicEngineConst.getPropertyLinks(); + std::sort(engineLinksConst.begin(), engineLinksConst.end(), propertLinkCompConst); + + isEqual = std::equal(engineLinksConst.cbegin(), engineLinksConst.cend(), expectedLinks.cbegin(), expectedLinks.cend(), equals); + EXPECT_TRUE(isEqual) << "Const links retrieved from LogicEngine API do not match!"; } }; } diff --git a/client/logic/unittests/shared/RamsesObjectResolverMock.h b/tests/unittests/client/logic/shared/RamsesObjectResolverMock.h similarity index 96% rename from client/logic/unittests/shared/RamsesObjectResolverMock.h rename to tests/unittests/client/logic/shared/RamsesObjectResolverMock.h index 6fb1366eb..114d1f0c5 100644 --- a/client/logic/unittests/shared/RamsesObjectResolverMock.h +++ b/tests/unittests/client/logic/shared/RamsesObjectResolverMock.h @@ -10,7 +10,7 @@ #include "gmock/gmock.h" -#include "internals/RamsesObjectResolver.h" +#include "internal/logic/RamsesObjectResolver.h" namespace ramses::internal { diff --git a/client/logic/unittests/shared/RamsesTestUtils.h b/tests/unittests/client/logic/shared/RamsesTestUtils.h similarity index 80% rename from client/logic/unittests/shared/RamsesTestUtils.h rename to tests/unittests/client/logic/shared/RamsesTestUtils.h index 0b30e530d..a008a5656 100644 --- a/client/logic/unittests/shared/RamsesTestUtils.h +++ b/tests/unittests/client/logic/shared/RamsesTestUtils.h @@ -15,32 +15,32 @@ #include #include -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/RenderPass.h" -#include "ramses-client-api/RenderGroup.h" -#include "ramses-client-api/AttributeInput.h" -#include "ramses-client-api/GeometryBinding.h" -#include "ramses-client-api/Effect.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-framework-api/DataTypes.h" - -namespace ramses +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/RenderPass.h" +#include "ramses/client/RenderGroup.h" +#include "ramses/client/AttributeInput.h" +#include "ramses/client/Geometry.h" +#include "ramses/client/Effect.h" +#include "ramses/client/MeshNode.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/DataTypes.h" + +namespace ramses::internal { struct TriangleTestScene { TriangleTestScene(ramses::RamsesClient& client, ramses::sceneId_t sceneId) { - scene = client.createScene(sceneId, ramses::SceneConfig(), "simple triangle scene"); + scene = client.createScene(sceneId, "simple triangle scene"); camera = scene->createPerspectiveCamera(); camera->setFrustum(20.0f, 1.0f, 0.1f, 100.0f); camera->setViewport(0, 0, 800, 800); camera->setTranslation({0.0f, 0.0f, 5.0f}); renderPass = scene->createRenderPass(); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); renderGroup = scene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -75,18 +75,16 @@ namespace ramses effectDesc.setUniformSemantic("mvpMatrix", ramses::EEffectUniformSemantic::ModelViewProjectionMatrix); - const ramses::Effect* effect = scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache); + const ramses::Effect* effect = scene->createEffect(effectDesc); appearance = scene->createAppearance(*effect); - ramses::GeometryBinding* geometry = scene->createGeometryBinding(*effect); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + ramses::Geometry* geometry = scene->createGeometry(*effect); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); meshNode = scene->createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); renderGroup->addMeshNode(*meshNode); @@ -106,12 +104,23 @@ namespace ramses class RamsesTestSetup { public: - RamsesTestSetup() + explicit RamsesTestSetup(EFeatureLevel featureLevel = EFeatureLevel_Latest) { - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFrameworkConfig frameworkConfig{ featureLevel }; frameworkConfig.setLogLevel(ramses::ELogLevel::Off); m_ramsesFramework = std::make_unique(frameworkConfig); m_ramsesClient = m_ramsesFramework->createClient("test client"); + assert(m_ramsesClient); + } + + ramses::RamsesFramework& getFramework() + { + return *m_ramsesFramework; + } + + ramses::RamsesClient& getClient() + { + return *m_ramsesClient; } ramses::Scene* createScene(ramses::sceneId_t sceneId = ramses::sceneId_t(1)) diff --git a/client/logic/unittests/shared/SerializationTestUtils.h b/tests/unittests/client/logic/shared/SerializationTestUtils.h similarity index 84% rename from client/logic/unittests/shared/SerializationTestUtils.h rename to tests/unittests/client/logic/shared/SerializationTestUtils.h index 2673dc804..d91b1728b 100644 --- a/client/logic/unittests/shared/SerializationTestUtils.h +++ b/tests/unittests/client/logic/shared/SerializationTestUtils.h @@ -8,11 +8,11 @@ #pragma once -#include "generated/LuaScriptGen.h" -#include "generated/LuaInterfaceGen.h" -#include "generated/PropertyGen.h" -#include "generated/RamsesRenderPassBindingGen.h" -#include "generated/RamsesRenderGroupBindingGen.h" +#include "internal/logic/flatbuffers/generated/LuaScriptGen.h" +#include "internal/logic/flatbuffers/generated/LuaInterfaceGen.h" +#include "internal/logic/flatbuffers/generated/PropertyGen.h" +#include "internal/logic/flatbuffers/generated/RenderPassBindingGen.h" +#include "internal/logic/flatbuffers/generated/RenderGroupBindingGen.h" namespace ramses::internal { @@ -91,17 +91,17 @@ namespace ramses::internal ); } - flatbuffers::Offset serializeTestRenderPassBindingWithError() + flatbuffers::Offset serializeTestRenderPassBindingWithError() { - return rlogic_serialization::CreateRamsesRenderPassBinding( + return rlogic_serialization::CreateRenderPassBinding( m_builder, 0 // no base -> causes errors ); } - flatbuffers::Offset serializeTestRenderGroupBindingWithError() + flatbuffers::Offset serializeTestRenderGroupBindingWithError() { - return rlogic_serialization::CreateRamsesRenderGroupBinding( + return rlogic_serialization::CreateRenderGroupBinding( m_builder, 0 // no base -> causes errors ); diff --git a/client/logic/unittests/shared/WithTempDirectory.h b/tests/unittests/client/logic/shared/WithTempDirectory.h similarity index 93% rename from client/logic/unittests/shared/WithTempDirectory.h rename to tests/unittests/client/logic/shared/WithTempDirectory.h index c8f4d9915..3ce68d7ae 100644 --- a/client/logic/unittests/shared/WithTempDirectory.h +++ b/tests/unittests/client/logic/shared/WithTempDirectory.h @@ -8,9 +8,9 @@ #pragma once -#include "internals/StdFilesystemWrapper.h" +#include "internal/logic/StdFilesystemWrapper.h" -namespace ramses +namespace ramses::internal { // Works like pushd/popd with temp directory class WithTempDirectory diff --git a/client/test/res/ramses-client-test_minimalShader.frag b/tests/unittests/client/res/ramses-client-test_minimalShader.frag similarity index 100% rename from client/test/res/ramses-client-test_minimalShader.frag rename to tests/unittests/client/res/ramses-client-test_minimalShader.frag diff --git a/client/test/res/ramses-client-test_minimalShader.geom b/tests/unittests/client/res/ramses-client-test_minimalShader.geom similarity index 100% rename from client/test/res/ramses-client-test_minimalShader.geom rename to tests/unittests/client/res/ramses-client-test_minimalShader.geom diff --git a/client/test/res/ramses-client-test_minimalShader.vert b/tests/unittests/client/res/ramses-client-test_minimalShader.vert similarity index 100% rename from client/test/res/ramses-client-test_minimalShader.vert rename to tests/unittests/client/res/ramses-client-test_minimalShader.vert diff --git a/client/test/res/ramses-client-test_shader.vert b/tests/unittests/client/res/ramses-client-test_shader.vert similarity index 100% rename from client/test/res/ramses-client-test_shader.vert rename to tests/unittests/client/res/ramses-client-test_shader.vert diff --git a/client/test/res/ramses-text-DroidKufi-Regular.ttf b/tests/unittests/client/res/ramses-text-DroidKufi-Regular.ttf similarity index 100% rename from client/test/res/ramses-text-DroidKufi-Regular.ttf rename to tests/unittests/client/res/ramses-text-DroidKufi-Regular.ttf diff --git a/tests/unittests/client/res/ramses-text-NotoSansThai-Regular.ttf b/tests/unittests/client/res/ramses-text-NotoSansThai-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d7351f8e3324ffd9e397e97cc0d151a6653d0fcc GIT binary patch literal 21720 zcmch934B~vdG9&*&ZZg7zK>=`qnXi+M$+t?_GProvTRGTC3%-^d5PsEwiDZAF9`_= z4U{FM5c*hJl0pd~2~FWgS&|S4+iQVBek2eIv@eiASwaG7HS_-G+&kKA6X@^t&FJ3y z-Lrn@JKy=vx1157gb)L{nTTZn$l%bh?s?sALPkmO_wd0J3#Z@HlZYb! zWkTrqk)?}=cg%g{ebQ)=d)&f4Fd9`MMpJ>PWTl&dyCtX zQ=^8ERj;=xL&>&cXL0_2`Gr_d$eXaabY4%fYo5{tI^5l~rDJ@%LNlNgT9fHqPtTt% zKTDV5onDKnL#^xWKEM1dQIIW_rv#VqJ7Ojt5+b1Lc{8xF+f-`1U%*2ZiZX<9kx(dK z1*|}B%V(~;zOO4*inc8dzwMT({=pq@xOQvj_~fzq?yzvn*pBvbokAV#&2(&Qndna^ zJ2z)j>6Yb_V&=cTK`7Z)c}36(Vf0Fdf{{Ghlg7;e6^&nec)o97uz&uDcIgn^TmHa6 zsWd?Ml|OiB33Vf&_IE)=A84HiQg)!l5M<<2=L?LO41ZPV9TOkW$GiLuV?D{)CjWu< zT_;k>V{~;NUwNQD5eqU%Nm{*1oCZ)(*WsOEx8da-Q zc1)2pAyt|b@>!W&=$2ad9r2pXZo8?rPH(g1ZH;l8+iZ6DGrmkN7*Qx=LG`>R;dN{F zYj^GH&UDzFL6_a4QT5eoJ#l|y%IQutb|x~`Wcn?Q(e_BunIAVR)D1D~+c(dyP%eSF zVt4}4Y?Wm;P{16DhEytA(>5KS>*yGnjE-hgyIOoly0>4I9#bmJyR^}5eRthj?4BRn zSsL}Z>ZZ-^!qLs;f85rTML(s=D`GpS+(XDL?*{}aMnM*dzEvXd31eIuQisek22M5j zSi-94RH+{KX6y4U^lbh>sqIitusfP8%Csfdk!&fX68SlYO%(fTRKV6+DGFL( z+X-x|>j~(v1tnv{mfFfvT0a(ZD1n6rl#guNJb0DMGu7EM3X~Fss3mCDsx2CmA;IuJ zJkoQtci7Z67;K3>ZSEM#__Xx3f%a^)kV@qjx|`MNUO{2C?dd6MfqiT&9-SUoe##QC zH9EB%$F2W=aePR|@n2sL$LB2#wni7ju?y?c2|6~&+?DGQ_;NC{$pwv=d}7Sc=9SiN z-PIWC-&NeYn40UENQ}jt9XX??w#F3GZn}B%_EY_J$sJwgchJS2{^0KUjy(m1Vo1$q zs{_5Lgi(^_y+pHWNeULSd?hoER)yGvpf6`JD@leNpFOs~Zc274EyJm?Sa^0k6KgWe zjLdeI3Rb_VGv)RA>b-_sG(4_tP0pqkCrT&#P0>)WX>!ZlM7GSO6V`UyMl-`x z5dWkD(&T5tHmv(GLKd*{!QmL4ES1rpI?BY$TBNdB$m%MWtw`;xU6kY`N1Q5-9E?un z6^>ZCC7$#dH7(7CEn_>rxt&c4s&k|37vnvh4N~*NpGatF1H| z4kwZ!e@JOI#To+)`^{U2UCvCXucTIX2|_p!Z62cEc9~30r^)2P5XcS;CN2Dw#Mllb zt%?E&?fG5Y*xw`*wu{RgFTaG?=x6qk~$rL8(xvRYLpR$+=zXTD3x1Q={A^gnv=f zHQDIx><_*8VyM5<8yjr1(nru~v%RC$*BUgHzXOGV^Q?>hGtm<_X{_=slTsMB3X{k7 zE(OyF-~BArO2Q;CGO{rLE~ix+IBy z?v6BQnoD;u?!|zA3hsqABlF7!zU7sY&NID{Uv=ToA{>YA#m+H=&i#3Swn1W zzOe@u(YQU!##&_ipM8E_{K?VsC;oGEI@i0sRS5t5=VJL|-}pjjsXso``2^#9X|0c9 z&&NpEo(HLzwTI*tzPh7w5#W^eR4cYZw%cKwTRJWZ-@MUVjKxdghOzF{Y?G~HIFg_C z#v_R%eHl+;)|b&Q1yuqC_<9ib>*R;;8^96m^ZkfBGME^CZ2>9!hK2WuZ z{2TSqEasD($6{;x^}kI{GS0eEI@_Vo|P5LQrveiM7YrUu_Up^c89RroD%30W&6niaMVqoR~D4buO(nt+3dQ z9+N*+mus6_)b8HZ8!g1FwKl6((CE@}yT4AOiN$@bu{vv@InYSs@nqp~?n9#eQA(b+M7Kntznh0cL`quo@qZm`WUpS__uRFfKs z^mNf<<-a%xBZgwv8W!X9ugD-cRoX8kk;2r(6)9E9mV>X8ZiKv*c{bWSy}VmDX72t2L`M4r5D~-V=-$v7vOi z1$|w7X!yV^G^EagCuyixyA3{fCe&ZZZqMCw_igkBlRM^8h+PVGSI13%{_el90fmk# zGL z)z3|)V%7m;%Hd#0vMms+Qhj!Or(dlW6&jx-F__Us%u1DBU87Xj2ikT=tl~V4X2LdA z@9c!ZrctQvh6y43ANd`{`l;?4Ztx!H7@TYE^2MBe4ZcXHMxpBnm+$^kWH=BQj(myD zL>dy}XP5~yv*#Ap&H_w2vr-cEMLRQQBw@j{xwm%?Wp>4U2Yd39Sx@=D=Js5?{VuDns^JxWQuFs|1+38NG;`G z{PiD$3Aatey_k(IMiI$GdVQ-z-b_|^6T6vyBGMOa?h0JSG$|Yz8Qtv?ZF>iLXXEo@ zx!m~ELQ}fK!2wmR1A$|FaEL|u+DO^{1K%7K*O#T2?V-(jfC+_`;aNW1QHQLAAE9E@OxT|IQ zr^HbLWxYS&BS;&C z>{;Me25^6yt=VfdY3&-DEp2Ry`>fh83D>nMjBZPkr{q*83f`I*nOlvBdSB zW}QWKuQ(YJYpq6Itx}n&)zk&_4O;5}!*7>xZRJ^U82TFlt=L}UEOH%E9fd9gHw`Ed z#U{8`29+OA7xM6EwIvJ&#gRu1PM0>NN@?v*tFjjI(IZ@2Os4Y6a9EiQS?hI%{$`ci z;j&YoPq4UMZb3{cROoy8Tj)Fer}o{R&gT+|L_TRSDEHiNcDf7^Rm9+OIF%-tB=!vZ#VV(1-HZMul8O3fjCZDK}U=m+=X#xolwZ-8NR}}|G6oah-VYQcC%hFf3L}I zHnu3^CbP+`)G0J-yEvW>hSI@cAn2%5>f54%&1A3&UN5y8@T4sYCFtERELGlzbpXp9 z{YGI@qiKzunAq_*FE`Jw2_|QXuskq}X24LHUWgXl<~gG|>8;r=9fe5M)Hm4Pkkg5^*%US(m`9DMNbJWf zbvw!>#b2RZcV*$SzCX6f^d)+*Obfg zat$bVikBN;?SaPdAkqJjJV@L-DLXuvqjrVKL*O8%mAM_b>9y*(`gYr(KQxePYO;DX zv5?wsn~GvmM-)c5B3xjO%r@G&g<*l z?~>Qp(78Q6-`z31d461>w9IQ`GXwNw`Q2STJ11!O@^j2ZjCSBbpcRZd!rNgo{!;&e zN{!tP>y>+B)_V;CV?BYcwmRlEm^+b49J$0>ptGBpyI|y&xw#ul=r51{UVx_uS}iQ! zXr8ambvD;J5y1>#YInBiwT;$fr?)iN)gP^gG&gAL%|j!n4xK;NvPY{{j3`u1n~OU} z^ZDk4AV87bGcv#H;_>0r{cMaD-2Mh*tY?~ZkxyM!0>KN&gcMhyAy>^TJTp63m`+WN z7{V+(qi#sH<$8+qbV5<=Tpx|;9lp5y9Gb3VDqWRNS3Zk%Oh&4*4T#^_5DGN}!r>=_ z^?`J7z20jhPIuS|O``w*s?A*7)7ta&T8vsa1p_Ugqm>=8=gj`t&zg7ju8mvd~_lAH#RQ(Wn5u zrMT5k|CG3u5MKU1k6Sf?d%pm@<1*fUEDP>sgXzlmYHVtDDH6dP-%|AL#m>c$$6n}d z$Pc@7F=+SkedDR^E_1dkFi>=N=TcjfvAtTSX;fp;HAS2aUcD{en{4iHG#fW*Oxl>o z;YYl$xi{TB7Kc?-!X8@#97K}D8j|cYDcE&+PXM$tiND#xq7TiNKP3lq3X3clQZ>!; z)##06p{<5+Ho6LED1V?DwPDyy{f}ZJS!sV9HcW;W#XX>nY^U)}V}o#p+}=Q~%h_oU zO?66UTHa>Y@t~HuO0~nIW4oPIJ8d+V+XOppypx-0tMROL2w!Ta36Ot%w1vI^3#NgP z>5Z0%xG~``VVN+aMrp9V_3dk5I_Hi$i~;EGmKwczxctJpS8%%)0*ZNbU@5_`$9Jo0 zeAWEY8c)s!A5Nu0_G67kgs>t)c45C(lU)>%ULsU}gEbHqJ8(7t@^qSUeK8s;HrSc2 zXJvSErHiRd;J1?omw`U$q@Kn$TU{nFZ*$bxYHF=v`uRs9NxxPxJe=x#vRnYU=oibw zPMbpPM=%h0LqbrSSdt|K8fE>g=wQ+im;wgps1$c*y*=s81hh`O+Z77eXU&H8Xla|@ zW_Ic@hc->E6quy9dqY7_eUr=Qv(!vzjGf(0T`q^u?=HVd?w;vRjA|l6NGv7;-HB;X zlu?8-{Svg~8q3;&etqreSO9eWc$Uks_3jrQN3!N`Xw7mQ{vjo@X zK0ZnL6G3W4G;J)$+M1q5x%%Pg$&-*EH5kX< zH8U6qF^ocLZ19(v!S>d1s{1N+)Nh!;C%bZyz_7yDoqpt!38a1eSY_ zDM?JRgLZ5GTZdAoRKq9_HI)DJDz_kXDU}Vr+p#w$$}^RZSKQbe7l7kDM#QN0s2Bjn zy?o>=5gDe6gx0FbpvIT~)Gi(hOYm;cz zF0#3N-IgC8y&WxQdCLmcG8}=7nl7DH9ZjYpVYF<4kJD;K%c3;X@@&I=uhq635{H#?mM%oXM4`+@Ihi^dde?2(lPqh z^2yvt%tB}U&bk0_G=h)60UQytzyV@EnyyO-OBVbXsWAXjmtMgB{zy_ znx9&X&zHu=O7rnUj^?yYqaGC$M(dBYnSFzFzWm|g!NFm=vwZ*HzRXMRCMLD@3bRJ< zKoA1`;ns_>1gaVtvyZVLS6uq*f-n?|iO2A^EnVBT)fujz9~vBl!0JUnrV9b6CKkK% zr&(a8E?}9=UlN<4->ru2Kt~ph`Z30e$O1+xpPPC8U=*|Kxp!PXJj28XV*9qehnn0* z*ZOb`k4?D@JT?`QPtbT<1|Ruq^mEl&zR41*yVP6$5h?z;;xu;!OoNyAny-n0 zmVYDct^AwtA8d~p0Mo4X-#C}mHP{&5e8%QdDulhdklEho8ZuhcCWv)fPN1eAH6bzk zdCjU{##!!B*R*!ue%kKT@M?h!Y8g$iJj`Oc@_X@#%l7s>dP_FnaKzzMD8#fbY;rWZ z2GN_D^~O9jLId=I6!ZeVhhH`*w=o>bLXihl7PH-|UYTD@ja6e!DC)-|ll}AIhF~e? z^k~1++!43BT?RQCSMO2Rz@`mO^_36NyW-7>j5pC9|0eM0#Tlh(;L#_qGT93tcMQdq zd%Ps?4djk|Mv3hwp%t;jig4xDux)3!aWK`8)GD;;hW4R`?pSMazGYLgZ-#k&yiT`A zEGoP z%M55|0?zO=A#Q((`lzy-XL?Ku^Ul7m?TPuW&SWVlglFS97~ti%(_>vdt(%hNC&0tO zN|Bbq!}X+%z)-!?NjS;Xqzb7qPfRKlooZ|)A$VRTrZXMVY;VX`SzfzFr?h`uC}(iNTanzcz;!3 z2qehZmE#Q$>FZ&=vf!?S+=G7zk#%wkyh48ox+7l4eFs@=#F%;*Uz=WpeNJ=NL`yJR7wm0q8f$NeAJ97WKlzxt*4P^J zg`75MjOnJ9u6l=VOO2&g>$2HB_FA2%sW+9IYyt1$d>yxaaDIU6gP5NyL}S<4VAaEP zSFpd9yIt;V2xg3kLDZT${tdki?0oZBG- z%(KFcUv;QQ%x1W8T)dzvwAv!!pwZz}Wkj>5(P58-8;l513(t29o1JE#&!23tZ~$Yf zo&xLzO;`DSgCl?mf}$iDE}{AAvX~f7CNlcIJ%9Kx;MVbOr#@6y=Xlx;o_B-i>F;0f zu-#MgqTu*SCjueA7{1j7`rku1P^kY?Fn-7#PPu{dppyRZ^-zAky}d3HwlZ^&$y}iY z>xg;jB`t=dy;0c9;$_w8O06@y!;Pc4P#W?z7wR9G4|;q!{h|oko0{w$MH>XFv01zM zw(SQl6eWRL-b+6a^?Cy#_nzZJ2l7=3+Q~5(l~3x*x|Ot+rD#D_wsCEi$#zNST!SZ9 zHdM5I?&|yw-&mE!y~os&b!3yCR;^eY^SI)^VtI=g`!)niS#!1l$>6#)0T9 z{Jd3uJ}y6hCO>JD43xhu1L0~;S_YP@2@V-}Mt)Xn+#|oAtQNeV6IWEHqr z1@5W>&sTx3R{`2kg~qGEYD@I^O4>?Inysd-B&{^I(jnu!2zJMhVO4_(XIJIpdY6w) z^||O+%hB0gJF}gcI3wGx4s8$mVXWD{+!sr zy!Dbc%k&TU%w@@JuX@i}8gw#jxz38JrnMfvM7>gzs@M7TTB`~gsOZ(&lvcA+6!zBm z4Z)yZ!;bUR`<2R^C|KdoSK*M1wN|ZHVR|P7x$oo83qKo;}{e7h`7VVdMl|%>E z5@~qbLQ7rDVQcjTMmt?OgGm>%HaCTeeyg>s-PxYaw!7w!*ECP$5=DcIK zPF{1(Np_V+MoI(2!`hopv7^7IZW`OZb;so7j;-6-91`*t@_ES1et5^&+v-O^PXy=P z*mp1aOY#``5iQWG;ODmri^9Fa55$sqi}*S5RYkjEm*Rtp2Nj=IyrwKFZ&rR(`KoHW z>W!-VRj;W>)wij?qA_X?X-;dd*W9gnzvf}hXEk5dd{^^wjisie=0MGzHDA`Mv?c9X z?T55K(y8(7(Cyb9)1A}Zpu0o&7rOg&f1`Uy_o-T1J6C&s?W487(EIf{eYgIC{xSV; z4OYW;!+nMy8{3Ra#y1<^W&E;9ZL*kBre4#e>4524(*vf*OhrN{MKT=0J{e+$na;KG=3 ziej2fqThKoJ^`99(%~^K zi%(Wy(^R^J$AI%47(2sx57~zJK$EZ&<#CA*Vr)|+%E#A6_S015H?)CR={@C_X_V{- zUS(X5SD!S8ljWp>)AW@@HEs1-z8?78D*O{@t06|=X5e!US1ah*Mbgr|m4AaWs9P}$ zXF%WENw2U#W42tDNEShwGUHpD-b7tzi;gej7uFR@rN zc9tnJ$Y>#6hc-IU2KY?TSVr_+{5Vdly+ho)VV$xStPr~B+lUfWzY}v!$R_DY_mT|7 zEDAcMpnz{hP~1YuK|%sj=eUn-*)l@=2x=}kRP7^q3U~|s$#=ZRz9`l#ueCyQzCBS!Raon2&^*=VlpQ%$8ScA z=X!E2IZMuwVbJ?Va*&7Ag223xGzDdkX1Gpk4q}ISD!@ z0QDnf3D2pOT4(v&B4%t6ZC#7|A(Wg%?Q^&S(BBtOo0XbDFAJzQg)&Dlt4nx0gFczx zj(wT7zXjhBK-K!y+SR)84YjM~F4qska&1pLIEH6|!6{(GMqBN<+R7i_+c7?(1+;Pw zV_3kL7s0=5JjaoCiX6uM@-*DcduPMIZWi^iguamg1Qv1@9gDB7NKf>{!!P^3Q zU7O0NA0P+Nb^t4djdN{J49nH}=j8ecKKti@O$XLP61EomCeX_&4>8`aahV5Mjk6akLM=jvNEjgMPMaShtX*f()2ViWt_lP31Sdqs# z=4|}afEYdhbh-vGds3^_x@Y-Z97OAfaUDdfZ0$2{zXngnQ4CME8qNR@M$76dWf-!O zjG7EPmU|8<=kR3IW%-OxncO*p_v#wDh_q|?$gV*dHV$b`GK^ktRyVGPv%r|m>}m9R z7QDf5Tf&o#@d)^T3pi{7Gsx&HNzz$BhdAYA{jqW%q|N0%Z-GiGP?0LAlB%ehYG@7B zQXQ?OdTO9XYNBRpp;l_6c4DFq>ZC5}#_!VVsF(VvpVrd=ZJo$jEsbdJu`opcx7P502fbRXT1pY|W12k9ZYNDtE^^e88 zJ$mZQIk|RDdgkTY`MjJyC_m+1a`H1Tm(P@>{LB!0W>V>t^q$JdPr02`R(_Z3r3%t} zUap@?OYb?kd}ctxGBdCi%S<)5Amu0lRo`z>)VM!iiu4Lfla;3z{ zg)?VPotGNH>)8_vOG`2WZE~Tu^@ZBj7RsijLTFuTNv_*2*KJ>4w|#BhT)$kXe|@3; zb%ly@q2l@uWU6H(YNh(C)Go>h49e98KoNxN+EGsiA1E~UxErJ?0zHcrcol$PH! zGK1!22Fl0`n(dcnH@l9mY0jppp|rdhGBN?vRW_De&*WqV%dKOu+zNvYW#yV#nbC7q z29_B-C%1$jqDxFyEuWL7D4Sb1ylh@h&aX?($*pJG8W}fVN=E=6p zZLDL*yv#cJDr;5gS`{Nz%E;m*Hz2dvfZV6lXb$UibrGdyVx(pC({d;2R4pe4e!{)9 zc=oJBnY7$fx+)T~HRp~k9a=ni>cj!5L~2;-BsIKN7^G!kke52il%zS$4D!x0sT9W? zPo9n^=afu-pgQ-}MI-Y>Mn*O@C?S~|T!Umj1^bOzGt%ciz)sj-wMaF>`a)a_Ru)BC z9qvASDJ-uL?v1b=MA&9+fF&!q6FP{9{c#j2o3KlY&`PHP?}k@j#6Er$cV@d$?5<}3 zpM~a1$#wXOumf)dd>iaL3hV54q})O71k7-QJ%cL%Sc6_8yzK?(UTpsrU`s}U34tBa z1$|ka=vp&V+i)T&} z+lhrU$BE~}@e@qbmuj;+YlV5=MAXyLzgIq`x_~zgE@shESe&dqE$}J;t0icj)i3)W zGFtR!ftEK0Jz@?T+%DLucOhM0c?H%nyBly>aE;-*3gz#@Qwgu)GwgH^xlnnDya~@c zDnCN4tMDd(@~p=l7`FgRmQ;RKdAai2$}5#0S+{xC8hOZSi<~pRYwPNOL#llyKldlpNUmtG1GQ4~>g!np$ z@)eNaDW?ybap6^mH&yH?#Gk$ALj1``Xn6|Cdv0`ko=IoCOk;~oxCJ02{#HCgd5@a z_#J(il%eZtq1QT~!FE7%ZK4T*Ks)V#COS+f;1ipIjtLJ7G|H<43;nQEA6n$K&=~&= z+Tq>8r|CQCd+7U-cPsrn`Y`e&I0iTN2JN9EDrqgL@l%DEA2;6dn*B6dn~mEqq>h zictEb0Gi@IGC2qp!)$GcZ7fB=ubpr=^4xdw6wDDX$#PXZw}$6p-jM!}{Ql3pWF3b; z&+p&nDRu_q*95GuUvn4-oB%KLTn|rSCzq+v#8apM=}Vcfj^fv1tkzw;{9_Cj5voNx z<3-$gF8-?+$~?>K9^!YLnL=)g!w9G&7g7QDH}LXrA#aNB!yn~!pXcyrIs6PS z&wOy85-(ZdDcTCdr_A9No?Fl1AcuntrpI^-@8j@`Je@LFBo&-O6>}U0e*lzpl2cCc9nzXg~sK@h>R&D39&uTmO$nD(6 z%Tt^$cVpCEHfI$dzx&y}{9_J3%=kdoUbq`N!cjmcu_L^J+zj2~ zui=S*JGqzq75NC@kC2DR-;>Xf$H|uoGpOhD0k}Jmm?o4duTlB3_4+^H1aM}^GRS%7UwYzGgXI8D4u$Brzp z$OSLU+7kgW%>`t}uBnBlG=sVQF!`kb+{A>~CC>4Fr5zHx3ce1Ms|Tkyk~nA}OtMCi#k0#XiqHYuVZNe#G0sptA6?f);7Vw{B za=2d&U5$-J>XYrv2Hx*Zq;_I1C2U!G0k8r3M3Ai%z}>tAgL+q>{uO9o1&YcM04Ali zSZiH~8QhC`{~>lMKXxPLg=hU^x1``Mz)bg1p==Oitd5K46D~F75(?-OA?Ud`pgzxK z!&s?r!wPE#%@Xh(Wk9(C*Hd?sJ)qe>(2RLB4@0wH8n4_t##Zf}>Dtf>w5&&Hz;Fngkn+BH@u)h1?OC5y#o#y=<;5{7$@0e||CDZX0L20T~aq@R^YdF@Al3Gxe@n{$@s0(ODhS;gp cDc&m^Ig8s#AL0Cwqvu!$=MSAbN`ck?1(uV;=Kufz literal 0 HcmV?d00001 diff --git a/client/test/res/ramses-text-Roboto-Bold.ttf b/tests/unittests/client/res/ramses-text-Roboto-Bold.ttf similarity index 100% rename from client/test/res/ramses-text-Roboto-Bold.ttf rename to tests/unittests/client/res/ramses-text-Roboto-Bold.ttf diff --git a/client/test/res/ramses-text-Roboto-Regular.ttf b/tests/unittests/client/res/ramses-text-Roboto-Regular.ttf similarity index 100% rename from client/test/res/ramses-text-Roboto-Regular.ttf rename to tests/unittests/client/res/ramses-text-Roboto-Regular.ttf diff --git a/tests/unittests/client/res/ramses-text-WenQuanYi-Micro-Hei.ttf b/tests/unittests/client/res/ramses-text-WenQuanYi-Micro-Hei.ttf new file mode 100644 index 0000000000000000000000000000000000000000..34dc78528c97714d86fdbb1ebf7c4fa2e9874979 GIT binary patch literal 4626376 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 z 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�ac90(@1u6!q!>%%rH* zw8_kaTHWjPhQQk)%dsNrb(zQ&HTB5LgAKZo;UY^O?H_u%031Q91 zB?Yy`h(ls=-=zzALbhQKswu9YFA{a;6hN9DkY7q(Ihd=G=v(l8ZB6Dq@FDfeJjAFlnz=t_KKEgdu7*Eisf=>*QIlT{B3Q}YWKB% zCp_N|oA59NvT8DaBy+erOD)Ph;ZUWT%$y3aQMhRnzYzXGJ1;mb%VC544YBTq=$Gf| zzDvyjdsDgnIb^H%;UDVuP!_)t;Fdo;lbD|C>?&$GS>Vxh8wh*~ODEk3KNDs}+JwkG zD{IjcX=ZJg{A1OCs|GD}NNl41Y*DD9ku-1%HK>>(n$QPwK(bzb2*h#e$EUXGaclRm ztVf-?)9yEi4$%aitBG?hKi|0$4vl}0ExhMD*Qg`OA#$aW-Csc$%}VH@1+prb2On&q z4%kdDI3!^dK;R>Y~;~BdTFVX4!8t2qWRWEd-a=sopLUGtf0=tJw z#cpU<&`qxe+7g=(s!l)ZOR8s;`>2wMx)bfns03rjGP{0YA=2Tt9XcPnBSP`E7)Rr3 z7|=^%ehYMPubS>;W`F-@%_KX!RKMT*`@VPYX7AmV$+|r2S6T^Mc6Q^cAz8rrtIs0C*5ZX=#hgp9Ks`kMyg2i%Dwznp|TldO`~8B zNTxWWDMkWnOcDRyyMm8MSlsPpMPc2YMb<$v>!u~6ZXWi#A~$OFMgcQ+TU8|e_M%Qy zZ&mYhn1ACVUK5Y#Fj2&hWF4>Ul}jFM51u{MY_()v(`CEP&wiu@buEf<&3Yu}!_OVtLh)?dE@JAcXI3tiR!_Y5FaM(yk9!M;mC4Vg3+?>! zK_%7!xq=!cG4t{8W3HUzHAuN{ERYdpuDRHM93u%|sB0>-bfuzb%nwUBXi!2uFN-+D z4W{am4wj>ZJf_TSgk8at3t>ypKx|p1SW*MKYI&Am1woc-V>(3(;hKu8Dqev1An>+K zUoCqIOqiag_G;kwP=8!&vQ%hd9dQhp54@!;ipQoX4pjxlj_f=r4f*YH`YR@4`;bgy zF@tZ^59O}>gyRe^`Ym`S)Do6bDUmiYV9hE{ahpMRRO$F|4zEQp=#(0`qUvE8$`_SY z!-kM^@MoK{vZ`T@|H`_Qu(zZeZW-mvu!gQ3_`v-FDO#ZuLz*0qrQ87DWfzIQ%lL^e zxb@--%Y+|PykAsPEv~LW`c#Tn7_K!IF&BNAjLRlVzXwiGWcss9g{)pDn9tvelcsq<=rTi6ggCcnv0+W&qGEv{IETk zv><6T438Hf-y?Mo>aKkxdqHv+6Ac+;Q(c+(oP)EYum}WGU`o5(&CsY1W}W=TrVxLG zU{ZcV5N#2pJI;CdJ$Iy!e)v5%-*-(f%Dj7;ogZyxukYNO{g|>-PJ8S#(W%0D?6bW; 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

SR&{72g0 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�FWBnA96 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%0AEEQcb$khPHF>T&;tuHS6=`^BpWOAMM&I_P_RKdz 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ķxb(c}cqwEXo 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

gynX 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

h50`4X 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

48*kyeq@681ryKNnE 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

dh#4(_?(HxJ;-`Bjheoq)h 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⩔U&M*v+Q3?IL8h?`3tW1 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%0D@`Mm#L2 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
8d*a&1vjzKJN35+sUqQUk|+Zpy~4?oTK`DP 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

gYNO?W6N`Z+NUFt86)g^i|wnOi{Qb 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

89DgdcKt))zLJ3G`2X-3kP)sRdXh*9%oU`6#uS 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

0NmUvoRb`gB01h}=p;%mC*VJfczuMK;V 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¨~^#cvQ@Nh5Juo4ARCoXjk2D2m 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
#Xb3!Q!&L6iKQJH4@XP<;pA1i>f1u_7M6n{T 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 z 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%0DAHG}JcFzQE( zHOL!{wV%)IenCPhdWOV+YLRSWo}WXP8(4L#LtGatW8TMU4yx{UNJif5p72KA3Tjv2 z%1z#I#@Qdmn~K%5#;tl7pt6XBpt;zJO=X6|Cha;STwZX;t~7TjRy}fu?4{#AHJ?RG zQBo#YmSRJ#^mQZ;6gF#BaMK-9TCe);1uf6*lsV7{W!tp ztq=w8EFL3SofL}_&Mr#1T-HPHafp%#pUK zcc$$GgRg_GZ;gx5plwwKN7~}4N3J1ZN2*o*jPUY#Jp0qevxhVvHflVq!JP3X`G?R< zW{w(9)Cggts~p-dxlcFPMtfphv|x$tBmj?#%#1tov@oH7JL>Rt)1(Lxw3sC`W{H1o zj*Tk&aT-%yFJr}{nTSFjV%z8vsweFNr_p@yg1eOC&^L@(@2uKl&1TAY4dW#*j<=$N z0S{3J4AGi4Emi7!i+&$I8ZQTfTE-)mco)1h9&Y1k8uX=Su8URP(H<}Ht=>u=jaL|j z8n1wdtCz+z-67$-w2dMn>B`O{2VC{U7apG0TUiC`jg?+Wxfhek5kYc7>4eI&!MYPg ztG<)veY-rY@7GfPzQ>C`7bAgKJKp8v_i-%4JC9{xI-;V0?BLcPzh@gpTGmoZk8)c8 zZPTd#_KrEja#Pgot=!qrW?azrQ^re3LsYjlN=ww%X}r}f`p9%b9Oe5t`c_~URbsfy z#iGAYW199SjR}UvuBz~-;Wq}cO?{uo)HP5N)kCt6eEcY#gj-jP=}8j#5b19@Clz|e zC2VQ&2c8QT`9Qk#R}3>!<4K&fle$>iN;mYBZV0w!M*c`^94O_X)wzT%m$)p;aEX>U zkKO69Zx8(=V=t!GI5eO+eNgtLZ%(ka&2C|IRxlJ5LkBIM;?i#mM~!`VfNtJ$%C9T$ zowo~#1nOIu92%JIB(#uST zyibFEZF+3MO4ef^FZfg4|J|ys4H99a%mTR6nJ?$V3RmsCy|=_#eqVv+QQ5|1T0D5v zHyGhG)pW}nMd%iOKcf3Qc)6;L9IE5oE@ITuc0M#29*osE`g+^Q=yM=}`~lS4h*Erf z-7CgUZ;wqSg7j>nGA?55mJCZ#Q3*gh!+yx2?=<#iE0nLN-6i32=7Kx_v+nk`G$L9O z?bWlW8j@;=@oHgf>|1JYntZ!8vhBs){+ykmavRJFTK(XnZG#wj1*?nl^0QUwc`i2n|Iyaq`Ull{&Wrkm~oAN)ZiVg zf5aj_dNIf5$bQ7g(g` zdOlk?@%(%wx6vEw`3X}pV!221&5a3jW}gGV+=|Sz#_3Y6@eBMYS97YaPbzKqwCwS( zy4pAP1mI~w71P{}qK5snB3;Jxh9>mu;Gz>0Wpfna)}j_E^z{8biMp{sgPC3u#qc-2Y4s%PZx@z1SG zMWD(%n_}ZsQ;{dZu!8e=%j7;4I9Bymc%uOVNIyfG<)6~9)m`X&HgI@gyKzoecEeN2 z^z(XDVf}<|474k>*K3D#ugbO!GT67c4*8aK>v@~=yy8JPPQ~nJ8t_`Ff2Q=eqv`9G zrq(jqNJZ$ALBO;Im`)OCg(!T z0l1ohbe0cmvs-Q+==f6X#Jf6+%Xq;Pp+Mg7-qNfwQCXjoz|*tU-ktK|c`| zK3#;l5A2Hg*@m%egV;h_I1%TL& zmy{Blod8GErOVZe#DSu9CXPh^58U0ny-v2Ha0~>oFC^`i?NZ++kknOmP+z>TCP6)m z$SZP)U#M^FvYK($kH--)Qr>dedj2ta%^!Hdv%|;l`~5#{(#OLQ>ld4$M2`OF$$w@S zl`N`PGCOy2Jpo5McE^xL8B8FEyXT0l`V01%1$}?d&rSElf@=T-_r?yWZQqF^mtNuo zaGW$vlA$Sm>Mbd|q^X7c5h$;L5>s;_r7N=x+_RD#f(LSuh%y02-KFGJEHgK zKYRfYs{I&Pf=koGS0BVajlRN`*70Pfk$IS!g}GSdNz7MC+ATPv4^mv6k-zN6-O{OwwnL7%`|m8y@mHMQ zf0)omO@cd5IU;;QJ6`hc8$8hD&EUvC{h#kf#$A7|HX4gUUEGRPiWEfWBZ}k~h~$nw zrU`}|E6S+JjeSh6jf+;N0J~g}h2=FvCXNS!Bg!~ z;u64-wP_j60{ovCsB|RC#WsmUbTf*OoCllye`6)AB=ro&L{ZAis-{3oR-QFPBSK-K zJ>So8^>%3*(+Y37v%!!pQ(III|M>Np3?=F%60aAdo{w8j7YqPvF9aJ1wdg; zch&_M+ODz13S>oeWr~6-SivU`UE3dcKofgGZD%WbK?p-|JEE5Mu(OUT2E&q0fPd$O zP0s{YAq>hqvg2|(R|<#7L7-4Lq^^V<{S?@RA&HL5Fldf zDIus$pZkbYm7s4d_))|w{*Px(LT`!Yb1!6CTma zxJD-n3Q(q{Nbx8vP(3ZL611#MQFcH|pk@-n1(PCw%P_(u1RA?{uXvPjQz#x4)l%dr z8+r5<(dPeU^8d(v{@gvKZZ9oJ$Afp{!vYa<`}UtI-Av*b2^1O4X5a;=y>dOzQNJi1 zShSEPG-zl2e@H^WWJM20EWHXUN;A>hb7F7YaXYIdDMM@*gfOv+1wTDtGYX@~>!ty< zT!11?S$vcxe@>;+C}UyCoxXvA-1}<;tc?TsH*f8uJSE8}PX(y0B2N+aZ=?L3C)?*m zUR@oe8?1WS_OUqhu^*yc8Uzchu%F%b`7i+uG4(t&Q=m9S7GwGO#t-)yaaZE@E_Sc( z=kgua;zjPullah+zD6k)r;413iOs=a0}V@_RRUr}4sk~NGWkt>H^j=UntCh1fomei zgG&qQ!B%~!@1~a8vlxngnEX#@0p8(M3fuR`DrGEk@kfZygj5A=u*Qq36y%IvCc1#&NDsniW~@ z#68J23ug2JntSG8^DdPnzuK*9cD#$mTu0?Hx@Yp2J zROj*ue!225{6-#xzxkSIgKj$_y(@L{*%#kVe?NWm#cZ9|EeY1{u(3lIA7ZgU>Q^qf zSB2H?1J2=MewbzS8KP#P8j5YXvNt(y@qN#8q9S2*X8D9BK`$8s&VZ%0I_1I=AGB)M5mGp|~wiM!uCDbeD2zd9Owx>KNfs5=P^lmDxJyWbMdu6pXJl^&|h1A0j40B^4z`Ei$i4jnw>$)Ghr`jFo% z{<9kjBO9VMsbuN4Z`E&#j=Eo*=-40m0Wlm-(?w@g&_Rz!6??%AA+8Ad#u+C{Z(29D zJFS7J@cX6oAg}YJ6%1jTL|5dY5335`>-+%fjXU9MN)A-4@+toj3*vugT#A#wJ__kq z->4HW3ibr+gvmrL)mt#k`PfXL7?r@v3y=KlQA3Cb6X@w&0-_GlsTP2z%{J*WGM~8d zO)f>Tsv=+^hmd44@_y!x?Rz-*3JjiPRbVA#@sOC7wh>zIQ<7RG1rp02mY7ODRzTJ&;1KOd4>t+W_}6 zPHG{{hb-Ki01e0M+{b;8Vo?>IhjMH1l|*51$Mfbu^AO%U%73J3V>x4Pk8p1!jY}4L zT$3jMrJpj8HaU^DtZeE}(+uwd)6z{7+yYX^(-0lpP|cYdbJ#0d0>b>P%1Wf*g04Jl z*W}I$T`3=)3?yf1!ZcmYM|pOeFiMFRwBt+3+o-4x@iP3pR3GLc6&8hMbvr-8C2tv$ z1ut9a*rZsGw8e?=jEb4C#i``-Ah;`n8?+H)dCId}+etF-1o7>F3oKHS&l@} zLC@RrIvQHka5aQ>-Ox7?=OoYfebgNimIx>&Ey-jwoPy4Dl3;Iv2OK7yyj>I~jZY zeLM4eck0xACjSC#of+DQ7)OS0ovdSaE6eSQK9=D%;L#w z<`#do%zgY_=60qu$4sj}t-;}Urzp6=ejp%0S9w<^rT_;*W^qQ`Z6|g=VH$6XOV5i1 zLr~W3GIw-q-P%_!Yd^31a_(o>t=Bwtgp3)!OVY;Y=->w3EQ#o>JIvB??ZtHd^8D9) z|JeMFTJ&1&f%{UeOZAuSnx};?UMv&tFnAYHkeQrhvj+RPT01>~dA-)N;qx5Znc@9t%fgpy@Yw zSk0v0H&XI#M@D_WFDXD2tqny!w`h`4^sroJU73+A4PY^3q62t;zA2|$ay8^_#N=N| zdfZQ1d-~7$_%_j8|q=55s6Htr`40`qVBG*4~UF%ri9ZnR&zw~%fZRDc`acJs7 zeTPO61-4O{7IjF&SZnB3?jgWkX(zFj6#)vgGlC^m+tWo#-~!Jxy}C$vn_%R&hl{DUI;|j##0*O~p?7 zYo(p!raziR6IHfw5z)M#*Uny0Mz{O5p!jF8nf4{EY>w!d+)N=*R@Igy&2}T!cwKFP zaVn;ZVPVAZxMIFz%$d0)^ot0Nb7&>aHK3BJb^(dy9!W{dx+mbh>{mqiGapRHQuR*A zIPfrNO{=36rHFQPF$~{@21s^OPsx9wdP50P?r&vBmGgjI!^)+QkYJ;6u{fxBdq=U0 zxkz()`zJhVcc6aWO)9E9t7Ezq^;Jc=uA?$vSNV3x-j{dj`!dV!QZ7cjcMNV<XKw(k_69vPQFBJO6j|AlV{#Y4v8FS7x-BVNY7xazHCVsf6qxy?$k&wK9Ki__T(IXZ7_Rdi1Z`d8Wkb@NsCpp1ZE8L2JPFM&hW*@d z)1B%JKLS7Ok58xXe*7AwWvLzEmt!*6A9-65 z2IhbLh?dn_z>rQF+OaZCDQBFf51=@un_$ZBw{r{VgDrMw6$Oij%C*~-RhZSh&*sKl zeABV9wELLkihzi490QPrQ@Y8s)e>u^sfqi66;N)#NZB@ZTX>Z4_#_LULktJ^WbCsa zhI(^EtcA>H_|G{2mQx+V6A~4F2dHCWcvL4tf1$<`lV= zWe=tcW-lQ`!*g)S$iK`LGFSJL=5p0sBt#P6-!qrRT)S7z^=q%IHM?hc`0WiIIwv0P z4=4g2_m>On%5n=6<1zs*jM#=j+?qs7{zVi|d$xLJKD}pmAK9}H6C%Cirme~n4B_6d zNV1Y|1E0D6((^^^TMLHhOEPjjpX}S_T|>nsi5bR%$`jDjH~s{ zsWCclAPYoBsWg;;Rs3jyqfR0J`L?{kPnqv`&*_w>^BD&ae8u=pRpcy853thW-wS@W zrrcn+bl9!=j`G=Jk*WDOgX8*yp_8)23;XPRnx8*qz9;f>&Cid_Cwj9b@BPK~U7h!{ ze%X=1rB`^|zm}mYFfl%|f2L#!-s0y^z7rz4$#DD{GLCX$pE>R|DhVBaL6>RWk~}ll zxOZ1;JMNw67Nt8`-^Hzf3X|EYoGb$}8gSyCZlJU7d||#9?>oiGtFwMN9XT`C)1B6r z|9tB^-LN3P068N!EGv{XqFj#mdHu|MKY8zJ?sK?FwiS7jAeBjh_58h{ z@AJ!h;f2Y5<+Yhc`XQMul}-`lY-}R=#$Khb+xOF1`F?)yylV}*9k^5#)SfvZBEdJ2 zM^&%_y?;JqM69JT1zX_OTC(?xdt0x?GcpQiO+0u#yRPCwI8HCtB6rrUF)sMQ7{U*a z7Ac&=KhrVt8Fl54*7Du=U%&n1_ixU7ONEP30BUKpxY1*5GQLJ$$j`4I{rnTzIQ=}v zjzS_B(`kRkF_6(G>v*0HSw}#SWrEX>?~#r_k1>>t8p~&RdLg6Zo-A35$6Bs=Dr?z3 zGsa0q$9?gQEn`?J=(LrW+66y8I)+<2M&Z`p&oc(P!{)45;tU>kiVl5dC(o?q2|rHy zN5!p%<#{d2&iv83Jq2>tZW7tm7>*LX}%@_u>e^DXq?{i?cZYRUNW`NmA1d_I5QNQb<;{N2{XmVh3B z3QqLg`uuslpZ9t01Y>`Lp40fozQnat01~R``87Vje371|B*YaMrYCxSxz88enFVi{ z7xvdUl(>pZ6OY|dekM;oe<`06+(20+!L8%x1;_1qdG<*3q`q|XDWBK!`BZ)`QJ?$ofXg;gn!LWREeiZglenH?19J>J zp+|q-ec|&iK4$rRbMg6~Hhzi?CAE&1^i1mat#y~YuW97u7*=pS{?pH&>AywKFZ_A# z*O7jD7e3-Aae`_CdpPYUej2+MK9Bc$$8xLB`F!`n_+4Lbp(nc>|0DFv=krhhe2VSx zm_MieCD5Ji!rI}qpZRk*e*UR^8izhM<`iiNLyz(~fBy5-vW4pNGQsil)(fAvdS89M zK7Ib<@@YqvcPalLmA7B%^QPh8<2ay7r&Nd>-&+#PQ~)j}y z@p-)xKh%J}tk+_^reB35G7fW6;pdU*#QmBQKWg(TU?gcTyP-yqaF49lWxTy9Z<1%m zi~3}`=~sanFdl|4^N-QBPRQreV@2jXgD&=0#ww$xjtl={Ec;_)Eq!{WAII{8BVC=Z#{!a3 ztTL(YO)&B1txX=q+p6|QnYiFr^0BeBe2wP`y4GUE@2ah@)Di7Tj{I6+`=8Fz zGcuv%OJrgansx%eZj2>*ZLNH>&&ZeN7ipKs7mr(6qb+(P0*6YCBACWwik&c8E4Mm6C8C!JTjWLM+sI`omj+1-6mV}O6A6rX3 zj&bJ4`X}?_)nn*AzPXm}5gp@NmMN)_^TXA?gtI-k*k^tmZ%_7CEQN5Ga~q*yL-rPo zYkJES$#zZiOm%CDo#lRzG1_sA%Ns<-aA>;480thu9%HnKJ!Njoq!97q=SANyNT1L& zT>X68qXDC+KWHuQhyBv1$_+^49mT}K7DI28Pm(@v{51^ zdpYY)JiMYI1yI$TM}vdr28ivD^xKh!Y`eYO2c?U!zt4MlI;ZLuN1Kfrh?BksCU4mC z_~x9_bfh6t)<@`E zK&R|cC>e0j*)ksg%9ylzAZJm=)L*AD?T?IUskOhJ&Tv*6-ABi)iNhUbX+1Q;ObBh; zB5&I~@2E?mhMTWI(fOwx(=|vFl*r)?Iyah3uzp9Gf5qCLIY(Vlbn>NF__GM853saz#$E8Q7wTqAU<#Y7Rm~z&ioOh*{KB_~`=O|R? z{dA5_V~Va*U$3U~r;Vxg()=T9pWM>P>bzI=3Z1+*slHb_kzq*e%3K3 zk`Im*54Qb(GnptL^e@2+UP$GfdT;QDzoF*qz78>5jL4~tDETnyop=DHohDh^xLNJ0 ztTsqzmX0r(JV7XGii+|8N)5I<`TM{892g;=v<{vxuV2TOIV>}LiB}8Z6}I;#p?U&d z#k6F@1Y9OWkyYLDp3VTk#W1G}v zmt}?F@>km|i?^#HCo#$Mw&1@Yy6CWfIddRaui;g2KIia?^U)!@Zhc4^K&F67qSy1V zqfi0hYfx}Nx7tM6iC4e;98zEK3G3jHw6f>utNPJ8)|u>i5U#`AQ+uxTm8Ez^r?8VK ziB6P1{72)Dq7?7-L8nF|(FdKLEHMwFZo8-rx=WHud*)W!Ey=Fm=afGOGK*G29;c?{b-xD5DwNEbtzf8Lax}4s%Ag5TsW0$BBgY@%)zzDmOq+gQ{<)LpC&PXl{qYR zf4saOY8{vN2fR{uRF0TUlJ0sP6yU-vza+}4Hbi24-W!Mg5TA!Q2qx-4OgJBRd4=PI zXI8RkD0|>Q5bFaCiDWdl0ZnAs2jwxY_~$VOI5hYsy<2OUh?~N2jcDHRYaCN|xd5$R z`mi@~e6f}QEE@-;^c(z0>N7uFgIn5>_h(o6RQdUS3!-dUA%F(&ZU2pZdki`hDW1mg z)HblZgVfzcm8?8TmDu&N8X_df^6r}US9FxbAR4v{^iH$w+yT@ zkB(raw=ZdUxAP08ANS#c{>QQ{PM(IqsRl*O??W-&Qr?#sq1}i=JD7e;0lMR?md>u?wqm6NC59<1qt^7-nupri*RLn2 zjfC}7tcM)$>Czweyo2Oj`qA|yYM%6Yz6svS@%5WvaWM}tK?34Gt$o<$9;d-d=_jyl zQ)DgYN%Y=;p7wZee))Oy-n??2;KjZ9=z7Gi ze^#b1_vR7bmi~dg!FG|SFrkgAFI{(8TI(Xu3du z0mo))2)|xE$0L5#KBm6I;;z^6WDe)2%<);g+GE!XND*15}cLdo%9NiC3?jqY4hOK7efUdz>g2THtC9O6D)-kTV}(kGyIfm27|w$0C`d z;&(Jag9gB3@OTdD(3F%N<%(F-GqQVn2;I`h>)^vAF?>BID5L1(7{yY~wPHa@bLQnr zXP#8nV!ojFFZU>|jS%Wvs1JvBT3jscIklguJ@H>ZM{unp+lwu_oMT^$Lr`BWGDpH) z9CH^x!>{`*SxcVGkx)9uqC`c;=-~zITDf`-ts-R(N6qm9uU^o@dwd}J&8;Rm-;vh~ z8ShA0%P&8VyfI!q&!c@=hw6?*L4IsKCiMy_`OCK28!GuJ9s@Q5l$LMAvM`!vO0LQS zz1L`dmYO!jQX%YbVFf;oKh@6`(43BY@Ra7Fo^pgAILg!JAu|2tQdvA#G)q3a)S7%` zyj%t>X+_(OL!Ud2nd8WEw2u3Aey048q0`(Ow59a} z50fdsQo0d9TgRg}KGAmbXU^-n_?bw4M&`+%`xy*~Y5Nd9{+Z^bpBedf^JmBbc#+p+ z(Al=>12o1#ko!b<{pa~JYJYChcHST3=6c%4JOD6={Tc66+D5Nj7vQU6r+A>}**yLk zForMY+4Ojz%y^)10R`U4DK6nCF{Cv>!xiR zy5+=tnINNBLsDCXJvUbALN4T-zA*C5Q*r!KG)2%<+@uK@#`Q)kj_drQN z=xaz4JSQHYiZ0joetlj+(~(z-s26zk{2qR;T;15i#Sk`5$rf&M;ngj&MArX*nqJ2z z4>^~*_oq7HDCd7CTH=#*!u9!^WZ7aUawX?Y$kuH@kOT-d;@$*Y$acUOI_oL-MVj9J z?o(+JJ#{{NZk{_}ekz*kMD)~BAe|s81J+59b8Oo_=}xKVd7e+7PSZ7?>^ygeELAjB zkp)d4KM^huj>u1nQ6l5AcfSlxkI&sxnpUUHo5LdYrTFw`KoflWv!e+i|MQ?}!NYp} z!rpl=%J^sLf~Hk>UIYfOC-y;HC>d{4dFx-_#IDVicao>z7ozFfW(kVI26#r%)Nv_` z$#Q8OxVC^k9nak_O%v8Z5}P+@!dg+tSC|I$0Ok8k6VZZ~^wZB|+a*OYKiew~i~{{D z27?lN1pr^yq2H^vS40;)Z`*yY4OMrd$2Fe{vF%Pa)Et`VA^@SFrGla5IW*9A{L|k3 zA~Y#mQ_+M0EPMCQmnQK;C*Y@O8UMHXo$z-&#lHISccV=%Ho^B`AMww0miI-^TWhel z#>Apdp#gl`z0ZK0bSgUq%GnGk42zn%vfI|#Ky~C1s95an<9xA_-=AYXiGd;x+DaJM!|$e36>ZnAm_GH^!O?eLZ1rRaWknu1QPDG#X)`3W?>+P@m$cIRsUQuZ%c>Lu<9)pX!I9sBod&!_kA5kHUb#V7py=>EOT zPPM*t)_~7(xqqj(&6D}W@n-D~`Y7xM8xUV5FF8*<{1MWYpekr1GdCzcC%`0VRQNQM zpF()aT;}}pEL^|QpPW7W@qBsjm$o{3t~sx&`B0QHv;rn>b(-}W{HQ)1m#NUHXQG9HT zEg~5*UqgclyRhXLKtq$Og_V8@A027pq}n=dl}?elHb2)~o1xZoRcbEYo)>%OvuCfL z>n0yrSB+~Tm36&1R|vM%ikt(v>&LULspx)Zp9sWu;K_APhk zMiUGTD?8&V>a*POAe8{W0og@7XRu*OMV8RipmhY49 zY88&!X??G_d#>lqVWz%&a7uC)gh#Q=22`0r&4+wznbc>%zvQQ$@2F3noVVcdIfzbo zfopQt3}K~D!7?9WrXzz%zLCj4%{>r5UEA^Qd9pUS$FF&Ld5_DzdcNwO-mCf^`1R)d zb?+|Ukixt2YU#HP%lkXXzX{e}C~42HO! zyM>1n3ruc6^tP1<<8gekOFnD-c!-vI{2l#F05zjdS&c9Js>xwEkAFJJ_ZsC-(=QV4 z9SqB&BCW#B>)(6yjac`k84jYoZVnIqk?1B<`VSrH|HDoGM6e+ME7?}#ces%Q+SBa& z6iwwJf#KRO{kb{GYk}xnR5fFZY}Lw!I*$E3@9i`1x0s?3eIh;`qP6DDA=3bWRi$|I zOOLO4FE-1O{^xj)wRxaf6_vV`Ha9;`zLK4kT3xYGj^o=md4H76g)sjpo33JhI$YT% znc)A?hD`LN$R^{fd*E}%H&OpbV?@-y3BrUPKX@|!ZP`)Q+#~*YboaELcN}TIJ+|VF8e8OEUNIpo+4q^;pqLwVrp*nyN5dLgTAjw;U-13**kTph`{TZD zFlQn)c8acGj-eb)!kCURg_$e56;G56^D^CvC*#=lC^ILXoXv?cpchqPCZ*KlM(}%e zHTG?Jj5+h1Rpe24(hcFge?F^>U8dS}C_->5Wbp7(UM8Q%7jrV?BVUq~GB>kCT#mBn zebU%xHS7&pr}h(Yjj}GxRZ%dumcx&a{UXl^h29!laAsGp(7z+kPx|qS=UV@ICD#Y) zT`(fGdRKgToqoS1i)X%E>GxlC{MX-4FX_ma=%@Bl-QU#6=~w?rM_%Wj6VsjV^b@?6 z=3#e~X_00bJB>l(mjhS$*`Kl9N;-8w>8f;g)4EdTk$d)2<{@V{I?kikqWl=U?4akV z>eD;{wWRU&JV&ebI-fo;i(#=l*vdPz8!TB4*@9B-t5Xuym0t1!(9_^HJ57UJxy)m! zyZ6a@jH|pyPv+Sk-9URL@+8Yvr9M)DjjF?qpe!HU2;+J>+$vXm%gK9vT*LTiv#K%K zQSZk4P_~O{wa0|*T5DFV)@o?}uXmglLrC<8?9XfFx!U!gJdfU=SIpC0B|9xq!u$BKX88PE51#Z8&&Z_RRsmk2^I~39;H<~tWVJg(HkG1!%cmC znV&AXKJ&e7NIU3zAr6^XGT|wIIB@O9wH|l{V5pjtv&5WCbJ`!lt@y}#DwSaGp`IgI zPbz#<`haR0_GdBbxADs1H5HB1uGM<9zD}N<=X8Ej#W$VE|6%gJ5WTY*&ktfSPSbW; zjq)(-<~XS4#x}o@gXiZtU9RYBZZ)6v2%Fv$9pO3}akmtf+_pr|Y2*&%*dJFXe?|q-)9UHsxwq;zUb3 zFNbd{_;#EpR5_}jJ5RpuNs%%)I9*pV&r;2^R`ZC>oP#kUdBd-pXU%%>GCoxDvKl=B z>g8g*G>_QN_2^%ac~soyBxDy{G)Vq8V&T=ri_5*M;p8|oGA0p?!d)2qBHZ;8%bHGh zL%m!ozQwFZztxZPocH7LJnrK?xoa=x5r=~q>*gf$sQd6dbH5rIruh};2|g50)^k?U zkJt0$Wpjr!|HL=e(+b~kUde)NWMZoqB=QE|xKD4$f!dMl{SvueA_xAkRC0h^y-hg!|mh&jSs=u`M7 z_)Smb`xWbQkwNY0B9>=_on+0MWjAbC`*z4{Jy(00D}jfCz4iHX(U!YquCV?vNmHVO zbvAcJI~3fmdR@QvTT-?CB)7E@JZoq4cNUYh58o?o6P-t<1aTW*FG&FL1B604TC%r)=i4W64f@1|#D z{gEy|doGQ5tM8_*n#+TaNtGe(#UZ<6hiJmB^dY=i?2`JY*JV=Y0|FdomZz-F{d$&BZ*QvQOuG zP|azimt&jm#jKildY@jqCZ*HIGo-xAS33P_O}pM^P21gRP1;uZ*JO=9@k0LYqEoUV zu_JW2ZH+RGhx2@GP#KgqzY@l{Yo&dk;L(Gc^*7v;7t zN_{K!Yok8$O6HKb&3TT~xxAWVziu&8N*V*K=^?zkUuax7X{q&8sIed+(k1f@K_^ z2v66c^dAdm*L26C32T_tdZ)8FMtP)m5!k^1d(NPsWAg7hozVecXnpb3Xsf z@o7SmGCm0)Kv;lvp~n|}uX*$n$FD_g@ix_u5b~6sAKw*O!uS7^_)}`NIPyoX;Z0vd ze@uAFE{8Hx^cPf^U@!f$<17BWmVPm_IK2-e{bNuQ{ya~Atn7!))mb~q%+*Sz_V;w&Zpi<6 zeADZF#$Wb(BQKw|6J9mPvwL9ry=PhGipwCcP_J5r%j;F!2clQyjyvnT6YY=oLD_I; z+u&j!uCDA#-`Ly@m7YWCidW6?#4flGPka5RpF``6*YN5^y?SX1JmQt|lPTNqp6>PX z`ld)v>C;mr{!SU?K|d!y@?bcIZx7iJQFH++L z{lbjPA>jE`_>5O7|C)>!PM#mnHQBQeQgEyaeb$f<;;)^XAw(yoJZ{zPxRzL8%)3wg z4bP89w*x&M4j{ue0vRu6yoH%P>`X-Ze4{g-rTVO;PsTgl<468Y$aC9)lf`;@Z<|(s zFw=3oRz?1#pTTiIUK(#)uZ*{6z0fE7v3O>@;G^_C&%bBsLr*`?zY~30UPk(m7nE~O zPsE|jc^zClkv{`akbuYf3>_U z`hXsoLsq!*=IEl+e!RHeXU~UP?+*0a6!~L79N^>re#Ut4uj0us<5e=ATJMYFU9I=> zd>r@VYP=Z!)h~`0UY`%o9?#q2YCQD8nLd0vkN5XA`!VS`Czw&=euSs}Ab#+8yl3^* zg?@2**YvseD)K(u0 zKQHh0y2~rwUR%juAq#V*d=3Wn`SLT-lb>nt*1EGT9d4z{IjDJ`UHwe_k)Pq}Z17!< zKO?k}KK0~h%De6RPCC}!1@D(UF8r=4yKL2NG;Tt}+ z$GoOEpANjqy91qgS9r^^xCB;RujiA0M&VLj@9EV%_^lFhF6!)pZ^~zS{WDj5BQL6m zN+Uy43$Hb8;%B}7nX7qj{>;aDP^6`y^Pu#e7B~2IB?s{B@y|3jcyP5I;Xzgz+DCnq zYx~K1uI0cUVqmS>RP#W~j`$U?d2szR-Vl|XpYdn3TtAh!tM!OE{OBw+=()%9T(9T) zXMQcYF0t_%?wmL9KH6d$*&{H_s}wR`Sx^{M;H1Gx?;?X+8?R#ChIi>}9>E z$W$;JHLs@6z53jdJ{NnsGS@%pb9x<*$J6V0G9J#SOZvunX(~k*OY|_txi5aqHo-ft9dnj z+napZR>O*j<^_F+N1xMtp$S!6nfVK!)9bkWoTl%3yd!;&Ki4R1oU>d>-}9WF=j?A* znVp(ft>ek(wA^2Qu95wC^0|||jN|FL*n>%x1E>72C;w*hos#Px?!W&+Dtz8@wq8OJ znD>)*ZExT?-ms$DBQYohl`?Z_x3Tzo5611s}Tm_8~P^Eo^9)%Qj6^@0Zu{A^i**oWbp_dcCrS12hy5rO`EA z*EHR-rgN8c1QQypzV!0I41cM;SUeo+?baHI$RiJl@L}0@%T=p;XeoKHdKyqakq2Gw z>$#K9mWQ8*hR>4+hjX#r%DJeKhunY(T}$>rBkhXfbli(ue0Xv$@Q1(hY<_o*`eQxp zFZKeN?9|suCXIcTIfKVu7+d6FmjYFhg+Y3-ZK7lRi@rJP2lur!JVGA2LBq1WJQprg z4z|d{0vft3in3f_JS^981YAVPgWe14lW5>vT=1clbJ41=uHj6mb72V$9_PY1K*I>L za0N#JAASKE_Xjx_?8;+I3*aLhIh$H0iEMqs-F@R+Jf?wLBKT)PgO-O+=7Y!sHp~GV=HlKw zpH0mNxi??f3+FTLZhLs5Cq+kUJt-E%Xh*F?9)7XA`(FwT;(raOdyqJ+g*B@!*?TNm z)oM<+KmGx;)=^jVZfNd{0W`dX1}Tv=q>7zkMG*Foe+hs5vED(CKhm$pFSZxs zYyN+fe)#{GKPq28zUI$U`d>djCYaveOa8OJivKBD`0(e*|3aEJeJ1~@Z;7b_y5j$F zd?^Ld<7@iGck#DZ@_E@6`a(L(VTq$%ix1@tp+9sK6-*TS%W7DI#I)T|ievnL?7cmZ ztVY&0SP9jAE8_dUH%gbk%u&@|@?K{5zQ(6HM7ge7?=Up^19-H0KZX z2mahVtNaB%i9fyxx*lH$FTj(#H-GXGUeKT53H==uM9=i!;cEtAP~%HnvI8=HaX#^u zeI$CIKMr01Pm9CHvhYv_%8a;uJk%QEKk5?S17N#!W*_kX4{m?)rTO8j=-f!Uc${QaN54E+9*el$V1N5(1`al-DeLv*4g;Z}GP<+#mRs z1#CmWX#g4IO$(Lnigsx^|1{b64U|XT4??ER^5&cTeN>|#zHItxEpLLqi`y~^@kiux z4*>X_zsL)3@;{~x-)g{gMT{$CACdPf(tpn1uV?yyHGc#a@z-Jh8~#A)V4pYa&u`?6 ziJ?R08SQW5`iG&TR*%ddN%L^?kL>v#qxVe3floZ^;M9o4={Egb(gRA#JAO}l(>2~v zp6Ii58+~qAfukl#PkV{e@4?@B*|8jNgW&fGr>|zePttAjxx#US@t)rc`SpxGqYb`> z3JM4L8M9`;hkR^kGpjG&^LxJc&g>aI_+z7+KFT5V3$aH#=MVk;&u8$j1^?^ckMjBN zGt~Vq=_UUeeZ<_Ed}%*d=$~-7L~uuD<0dif*1#TYRiHqx){>7fs1f zKY=eG;_V(2pE|9A&_@J#n7ZOGx<6%qa2u6UsfrBUSK(Qu%`?vUI zn2UoS^h4J=>5JcwANMWJ7w_6{@WsNnX$`Y69bf9H=>tcbON^-ox8J`9zvk=u>p1^* z{XHOd?MMDiY`Xg@KNQ``*p2Y%Ud#sMXT)y&i^T9%ctN~zyd1A{%m2FkG_v2r{4(aM zS$?4pXGQq5BtBG%x!W?An#j!$@{7!+!`)DkOKb5JCfJX@j5W_MI{!w}|H}NRMh?4# z^7LNReB$4Dehh#oR{Qh(i=Im>F;-Vq;HWdfum~bVAie$>~IKoS*4>p zuJ`;P;{h*H`K(LmUvoeFzI4&!xaIM%9=E?Uu0I?{^zG>(zih;$llf2k^d%OFh;07b zw!t6(pHH$j=}Rh-$!{-9^XY& zF_zJ8=?_n*G=2784eIk#i{FK_-{BVUJAb#orF%&w&#(Pmc+2yZ7LTOo*ZmIV{I`Ao zTnL`{-A&)$@w*dp$c;Ta0ul72{Bmv2H-Cp$9u!Z~QRN)G^wW1wKRv-4{)={a_FuXt zGI3W0?czu6UNt;RfjcN@H}q|tU(v>fXqPMnBz6K5tm$~wR%07t z`p&jyY{#-K%a^(?`M#LUn{;5OhiwDq&)UAlSD&?gi*GQ~_pP?^2ik@o(4m<4UgO}( zW(C_55Tq}>b(^^_dyYXi_r+U%JI-{t!*{?oE`p9ezK(+{eLJTAi`vfcplC}wT*YGE z#fFH*xB9i2>AHK&v!bRHxBjx-(0}jc03W<=*e14ycz8!!Bn|1?E51XY2EHe}hW;-3 z4mA`!*gU(yfd7~~5Y^EBZs~2?jNSEMu`r>!`_`3%@ z6~aW!tbsyIMO}n-^TvrY)-_JzT!YJcH(4YvwxT0DdY!v#|}@v z;fwG1_YZh2CwR_#KfPqBa*AV=E5lSA3;Xx9Z<TU&rH|e+m8;3Uj=%{Vcz& z=#Tt{Pr2jaiLw1X`*8hL*ynP_UEDpj|KdUaS^VDGA9uYId$O8!ps;a>Q8L2l!IWMN?Xw@?|`~@&0~Le>uLa@>libwN`aJ#nPb; z4{PXQtw|onUfPdOKmGU}J`PX&X|MIAJv?nL4fZ)-vholb(SheVqO;_B_AR=m)*>_1 z_;^or!`l8Wx+Fe?R&+=y&k&vSKpq}9;(BJ#^8`K3=)m*Shv=F^WQ1Bf$aCK*@0|1g z@%%vY!ZkYVB$GX7dcN*E+Q~U0OYr`&>H*!{4^DT$uEL>5IFnu2c}d5(|8C{IgiO;o zAv{5J<&y5M-|ZUqocuTL6-xizl^3w1hV2)vKM8rKrMw4prsyC%NP51X4*er}n8|&N zE+u(d?+dzaspnn0L={(a#I^B&Zszxws^M;E;XbbCyXE1_l<-3;>6EXO_kOxFHz&c2 z^n5>E&FSvh<*wf=JjKNQkbTfDU+agZp6rny=KhQDT|dAq`Hk(ez}Z}0b>#>5oj5yZ zT+e*abJ){-BW|&4x;tRc7FE~W51Xs_!yX)lTyfswVPwDv@$7#_T94?%kcUGoSc%r} z-beRzxRipf=5&{QpbPoA+!|K6o})ZV$P+nY+$+6DC+8B-vl8-(_sN^+IzbnGln(u~ z6YY|HkWO`uobI^6E>58A$JnFS`7m3eAKXv-ejdGjm1QXQ*Dqo39n_`|VBwb(ZyvQ) zq+t#f9(YbAe%ibEbttYPet6q*zulp}9sKg`@ujnaqZZhqHt?H`mG9Hf-4FT&cugd= zgS{45;i07%CjE|&^%Jv<)&3S+?FF|pjx?t_^u+L==XZ7^3v;dLfR)K_!LPMLUGO6V zl73TeUA3R=FJ9x8{&mT}A^G>Lo?Z_@XdNOQ{VfH*@eKCA@z>05@bbWZ<~D&pLW9Qq zLyxc|{aE`he@u9B4g+4uzpWJ-qQ3*b{4qbV3Rv|o*AHFgbMz5^@6&HBK1jc_9UYeX z{geFM$kZY$cxhG(p``eZ?z{bQmtO$=zz;7*3ID#8UuO8Ln1TE<;g9DR zo8nE6_Iyo2czz>aNqz%f9PlH&O!*35gP=7ZUu(Pre-bYgAEY1N!5s68zx7^n!b_kB z`x@Ql@n*gYe{1npZ+9=98}0d;^!!eKGy94BJlT))^CF%2?Z7 zpXBF<*1{j}jJF{l+~{Y9roi8zpY<-htnq8Mn^z6_MT|#<@bBhh`E;h=XaC9j+@|m^ z$Fm%NXY<)d_#5(z+w<{=@bWo((qHx&}fob z{L;kX{CZ)(_wgI^0r$s3KPT3;gjR+62lTtkFTg8yu2`QSK;GTo-;rOCcw|5A()}j- z;WkPr{A==cmE7C?gxBdd>>IH(TC9wmKA_cV({ECxQInU%4*3Xq06u7sN#x@ddWl@@ zlG9G_9;;TRQ-hb(qY)7^6wG*N&rjsH1^xH4{p@}&zf|*=0F${0)@x{N0ToInx^Pzsq?4Q!CnOAx4l!;D5*WnO6J< zdh9jzwuryOr4aF#_!9rfCxo9B|Njp*ohJ?P$MGZMZ-)^q8su-@XMf5cJbvON7W5ba zssCR=zdw+Cf#3W2Ys^1{|EJxhPI>$%UW^&470di1_j6Y4H_|WTFVDIEAwRrlzqS0y z?N_M9O90ii$c{CcKX>=+H~N2yzmpohBo0<&OXE%Fop5CD37iia$)a$o|?r z`D6db{K4&);{Z&93y(+;B-GY3d=;i$F5q>y*i~Oww zYWF#-Fky>{p6`RdSAI?7?|$ok_R>TX`KNfN{Lz1F^53EV?@N!V>LUMJ$X{~&QxHGc zXWu2_IEQx3Ifmt&QuqK|in9+X#62mh7!nEMvk#up9^?jJ(Yr z^*sUJC0cLOP#ae88k*HMUL5D|<-=>&#CzSc)@{^>ieC6O#mFmO*QraA6K`Wd?~b<- zbswlHLBC!*wM+UFJ0#KuEI~Vct(~4w)zZ+8*F|}<9Tkm@{7^nfRjg!(7QEZpvq0!h}vXp ze6r+pnza#jTT(rB2J<$%&!F!CNYT_sKTgt^% zgv$vk%64DSSJc8Sg8zDZ)l1WqU-jVpNgwS|`>nR%|Izj( z_`5g#o!MvWQXa%79z=hmslPM(%-bIq?F+OoB>$fWd#Zk#U^@8^TdB5%D9k1ckv*xs zsKvc!{}SJ3_WM}s1N~)f?LGhE{6Snr;{0pyH*V}X_pgCU=*eHy{tf%1(syc4`ScC{ z#`KrOyF&Jtcw5KA_IRU@b))~>{#J`5IpUx6_aFM#U-*mKzo8E**GK+z_i5Wc@^4EA z-*|ye_;=BMJ-#La8DDS3FRVH0L-*dcqLz4$hq-_CMZ70(_jeIr#@qe{-X?vrxA~ti zV2}R#XnX3fjNd!>aTof3lz$8UA|An>97ccBy(FbV468ws(EEk-BjR^ze?NYCb99;f zyMsL?KG*eE{RRHX{-+x}&-|;NXzq$n*G&8ed`@_q#S4xfN@n&1Y3Re}IlD*krS6cQ zCx2avv`z>}tC;hoSlZW~z3p~J{TTk*| z&Cec9GkTADO#Y(w!HqsM`)`BZVOj48{prq{;!u0K&&C}f@x$sA|G-U@%;|^or}nCG z!{1IB_8(}x&Q#-)o4va%<(+upW~y&^h7Af4{sme@SnGjZ|svjY@gA5us3J?KiGa4k9g?v`HX4mz2i{M|7`v8&uD+^k{8`FFe7)j`=lF6E z@?1>tXrJ&X<>lA%?&?_-e1Gm8de*c4`S`jTDim=t_;&w5w|~)vp1xxACteo*QM#bV zH3{CqReRp)pU=(}zeVtN|1SH4wd6ma`=RawNw_NSn}z=(^iTTl)I0Q8#ew=4T;Bm< zga5D~e}!GN7wv$$asQ=ZQOWlixt=$7Bo=fo;YB;(K7{|eXF1?v#`}?OSqD4FBfJzD z;pIU11ReI2X@>I8u^pJtukiN)~G*US5w1YBaRijdhOeM|7|7 zUqta6V8sb!7u0z*Yei&e92ghxE=G$_|D1bhe^*@br<9pe&{4%R^eH;&G#L*lt59BTK%USXJXoi;ubcC0X@(b|ta;EWJ2hIdu zn0DR2b-aAe74xJ;?l?Z&(9P^Q!wcx}`lyb880D?!7hY#S;RSR$?D9a~QqN(0_?)vd z^4SBrnVvJeKwi~nWETL9^7W(sfo_tgqp?bFj%{d49ru;1UI1 z>8;)&Pvi*WKslm2ZM{oD9^_c@<(ze>u%}he5A^qt>$g!_;Qn@shbma~suK_Ag40vX zVRP)^VqSvx)kOZrJA^#ntnd4iU)m^_R6y;O`{ASa$)H!=9;kA*him5m{SjP#|ONmUIVX$@C9D= zjF+c4eS}Bj228xy_Sl}@6TS}Mr2}6+=3fTZBncH8?a5wPN!|0~EhfTO_>pjb#+0Yw zOY{Kq+@X)Yoaf%h>VU5+++*I0S2z$uIA)b|r$7nb&})YK;&msm+9<~Bjvf1k#jri< zsRUMKJ>A=S&9KUGUo!f@jawYXJ`Qg|eux+03w^|X63YgtQVhM@UNgOBykgu~9p?NG zcs<6w&xC8DU-c2V-@Q5`?j2Z_hY`#kPB11Xi>5VxBLgJK8T?!y{>SN z7{c2}wASFoOFVxm<~@5!y=J%{^F446UU+GP=_%qdk+^5w8@z^ioZ*Y(KIZZ0Wb?K5 zUA?aQIOD~04!*wAyU5X}DSzG1Irn*8ratC~BRg+`q+Z@Z&CJ z-K#g^Uh;DKc*K1m=fwAMAE$YWe=aGFL)>1@#Zv0eImSm@*`H^aV9q^!$vzIe%#;~GPUz8u%s!F^uR_C<;VyClU2*;w{^AluJ)F_arkIhyJB^pd;=z2<#1 zmF6>jM9yb*i@g+@0&Hk*a+lXtALqP~Cq9?x<6g08Zo)zF+w$^1qL)j#kj8DYeW`et z7VG2FA<>8V#Ilck-b?$!PqY1s80XKg*zuLNr~ZM@w!Jjv2>TU#jl6!G&HFhoFJ&?L zvBtV>Z3OAqq^^naZT>awb=^pr5}?tOYzSI@#ws|WG@SaJV#oW zTz2499TPXfC;4d7Wc4n+hCTxKx4f_px89?dwEd93Fm|l)a(m-9<5gd(5AYgtH2N6OoV;E5 zaoxtGj)mUjaZYGq$7H_~Q zmY7(Xh~)&?6!m5wjC7yJ5+8IvH|7rLQDDi2t({?QgfpeD z(PKJZ;(8=&JeGQhy+rH-6HcK=g{_YIFrD?HCU3>2OCIwx1i@oEcH(**)_6RQId66Z z#d&yZJg)S(%B98ZIEwSoK4Am;eohBYc}~~mIpzfzD_9cJY#Xxg#z-$5)((`*A(;*-_Sg9uMq+zT~rGt@}Kd_PEZc`Rs`6 zy*w5;B|T=xUBrWZGzWPJmITw$*L@z#zAW-Orl|Go80R zkKN~S)t5O=xjm+%vajbc9h-eUkLd{QK92`B<$als)9&+F+GBpMb|D_KqqX<)n4h-{ zJS;4IQcdI_)QP{Hleu4TMx>(-phP(jj)_tu#T}&&@ZjUVL2D=fwM$+jwdw1_yL~&y2Vrb>G|1~4-A^{^GsXRgU+BWXC;J}`YCh@H1#K&8Ad^ov zYNW5)C`F2ga+9tTZG7;luiJo+x}&GCMVlkq40kaWvPyc+dXi;~e6Up`^&wddW%&pV zt~crQ-gY#f;NR4s0?0W(99syE*fXq^~5%(mvuv3OM z?zZFw`ck(F?KY##$)>79&#Vjm8?>>n+mKED$?i3DI$yUTo8r?ZR8dd$sn$K4HZk_1 zL>toQ#($j8_6(_?d@r7Gx)_#=~E(f zGzC^9bG@CCXIJeo7q?NDJotHLb1g{*o5P-4^STY$9M!%O9y97yuuJ_e7LeF?jATV%Z>+o2}eRwCjz)R4=ZbppE-FX4&0Wn1B~xY8!-gP{%M`D3LGrP6^9 zx7TeOoSbsKNgr{CbESp@G4oaH;)wfQQG++- zmPv9YSM!n}fzGS6bl-lV^O@nk1=~xVaTXPa31LZ5{d=;B2k&!=J}>Fr0t2n2->=ax zEPchi*q3%&_w&5(pcQwy*iOneT1*$w?^V5p)n>kq zSU~gxZIQG@n`56PT=;r&^%cHeH)`{GYz4l2CA+0t*oyGQ@9Lmx1qY*Q`sy-#=5()$Alr(cgGg@41hlcZa`5 zowa1}k+(*>kV}1TDWPH~U-r2ac0R1pF6>3vLY)`=477wTiH~ZPey;b3Eoo~igA}+d z)@XS+4{^b1$zp@#FzJYxu(jf6-e<@j^9ZmtS*#R}B5YZ=7?Ek@3R^GyeZms^T9E0C z^~f{-E^L*{Yfb0-*y4JR z*t(|u1;7L}dETU4EwBZZ8})E{)o^2c!j^lbGm*~aQlF=ME&9Am=rsWNNjbe~Gr||u z+2LojS&;f;?pl+-iZ5wzcf?n^CVLSVl)I1%1i}P;o^+Id^fzfKrVtZzS{YM0ySDda z?kjwKZk{$TG*)=BzYa!_o4mKMH_5H(SPdui{^#_`0DUvyWV^xtU5=4bHo1E?MmBUR z{)B>P=3~`B!Vwqp%zqlw=lCR?Ab;^8{d0+L+k?1C3aNK|Lwnt4fidJSF4uz2+1O|O zLwzQ_b(WE%IL6Mavga5jjM0~f@H6yV(UV-tKgpVvso&SS11Go-T7x!!I-**&8^_0N zl@_weXFLxP#xS-EoJe{{4(`C&J>5roi8C=qN}o;i#aI(QoV`B-;)(r_^{P<9DkAtKrJ zU0RyUNvv^`qmbHBl79pM3{NVGiv6@Jyl}a~;bZQ{oT3NXAlhRQM4yJ7w9@Z^9-SZQ zYl<;W?=Xm;`mMA#@}=DuV&Z@|Mgu&QQ%Stt@tH6@rw3*TFJCb3ji!U$ zWt^rr@~`zL%<;T7qqhgYsrn85n%`;6=W}rB_ko^mQ|y&ej*_>W78$kVRj4L1c}ayO zz!}?`a{ejX6T(JIxwMvIDcYxqXu<~D4)g7`_6e^X^muzOPBF4VpRrb)uL|zy<5wbC zeOw>sf&SCTl_HZ;Mq=aIXhnY2E3Fy#0bdzsAiH%-{Cxn54YF zp_0zqFZid!D&|$s2Y;nw4dx^NHc=9TJ@kpjI$qv>q0cmvJ>sk5!k$Pkcl8?JAIdq!T7DSqk6P3y7WmZJan65%Pa75F_`%Gl zP>xB-^GP7*s5Abf{-garjxL-s_UY1W5#s{m2>w|mh3fGT9357FLB03q1lm*eG{{xQF$;?p>*G<-(Mw#Kae z^~mhdD;3+AwP=4%m==S7@Sr`nr)&>EIMjX^|4E-~{ybh(tjfXPNC`k}Yd_$RzV`mM zW-01WkI;wOe^7rlU*Qjjk$*+JY)>o#|3Jjoc9_4d>Cf@GEm(o}y2OvQ!XFOi_@Vrh zY*9jn{$?;1_wR%D+`n5}(a=6fm`!WFNZE1x&)pUvU9s zg=V(*KvQl`;fm?oOXLX}1(}A*AYix{c9rr5jB@KohL$HbflF>BT;! z=+Bw=$9t2erM)BGBVd?ll=e*;v>a(#E~l1rk)^E%yJ6nS?IvtcQ+Lpx^QxQ~O;wFj z002&7uF}kHP+*ll^oOX(Zekv=)NP=_I!erkUypeG(&o8VQIl~W>e{EYhJmeV2VCxa z8A7+&9^*j6qj+-~tU;(+$9TfVnTv%R=`G<@AHX$Bl*xBcO9~J4rx|bnIfMhTYlj^uuG?(S?waNVf1YU%h2}4` zTZcKA=ua^28_j8k{!F@YDi(0v+*REMn$ZSQyF$_odvj}YCK{n#L^R5@AGbzBdm}s_ z$R3qkPBZSK&1nX^!KZBt8|1Y6hH+ciAbV&7(1>wP%5k_H`ymbWhwt4>8k5twaz3#> zE{ryi`#d{Nb6%yP{$P)vd`dWw6u#`wFfUq{Gv;`?ACqx8k7-CZezqnhQ4*Q&`^{}V z#{u@fM(sSJQP%r2&?Jz9nf6E{bZhR~K*l!t^vVXq+;iOq)E~Y&l~gu#W7}argz8S_ z8Hb|``yhlcYG=v0$8|f*A(1a|)kU_8(2nx*yxj?s`8s0OF22K0te;aBOMcp&b|#}d zh21pK4)YwbPcxTGmO{BHZ%1fEKgRRqt9F-I@p-#JF7+e4>Id<|9tdwo)+4*5vv%{5 zq1yh-SJL4Ya6i>?9xhpuH28f(oaM^Mi@=DGw!tGqXgeS5pu&j?YKYC&XxR< zJMH*P5A8Oxopz@k_Xpag@6<08?bKqOA8tb)F_F`;&nNm3t+$gWtNMu3Ox};bwja+- zSQ|*W$#%thJL!)-_8A6$ zgkyZn0h0bk!~v`4{{e z!A~vL+g%!3@8gJYwDWqqKBILz_|;OsSUD;?FN1U+O63+R+s#SG5K0or~WX%|V1L>C^zWd=@ z6e4W#?gU<=N|G>qwlHxsUiBnACH$XA(7nWL4@DVaiFDoY*jea0(x&5IYqah|+G~B6a%<$!_uKa6T%t(f z%=L9|^&Ne3ZQF^?l{tIS&yns%*HIqLEN}FrU9fMFk+wAl0AN5E$XJL?SRV`Yl4;zw zGCwS2jZkl4A98hugrP*ZWmGVa>->;*q4N!``;c~_ z^A#<7YL)a;&SQ<{#JH8s|Na3heo;DtTiMTk-{QnF0&ahg_M4?XT&HV&3Vi+~ISYOM z)$N0&KCAXAl5>J14{o3LwQuQDF>CGb^!Ljqh67-KrZ4rO_WWyLbALG~QN>lqKWMMvX@jFIq^C8c*!vazo%qWryWu~L5%=`jH{lULQl{xjd#@nnSuP)om(Mt691@kgq2v7EL zWZqx4ml+@LoOE~=`GnK>g7bIpv>)}kYG0$GcU_;5MtSg$l=yR^nDmKU^4FN3uk;Zq z675qlH|`HYpF8a*eU|Mhhp+0B1}XlTzi%P*xzm2Y&+P3Mq0gABm--L-Sok25j=7M1 z;rjFZoQ-SqF}CPtHS+R~f}HsN3)*cy-IaU6XNpWW<6(0F^^FN{TcQ`4<5Bsmb{PMTI|NPhg{O*s>>VG|d_s9SI`LBQfm6n@O&TtU^WwmWf)${(6NKSc0#5dwIlx0jvJD5gdFqh<&@?Q7)-DGNCj>=wta6Ge_2GYeu2(H~~3#&iB0tn^3w^Dlq<^Y_~JyFYH( z%s)@ZOQyD{cEGE2*vs9pJwv5Rk2hS)9Nz#cisvAXEA%UhAqz4g&p@9HO*1#!-&@`$ zRGh8#A^cf*F|@(~SDV*$UetBr1%c-W!Lt=brzm>uvM|i^FvMP@YnqN@nlAhmD&EuX z*YJ)GCLV(#DtT(dcy8v3E@oA~P)Ry)P<>p2fdPLPuv!*L8Q2d}^a7ax$b-OyEb z$g!I9xv9_R8dWwimW+?(q`1E7d_<0nThvjDxSiq&{h@d&f%71=fpZ?Uh+B#$Tm}BB z`~W$5b(J5YsEFfB5WD!6qz9Zky20ZXxI;X-AGO;Qw-mKa6Nfx-$!@Ygb{N`Dz%=7P zaeFYhoc-6zAsKbs)?7*yksSQ@C5&ABm+MA|Tlp7{BmY6%{{8z$af?{(*a3mNp%A8+ zMd%J;Tf#+AB;BMPb#!gRdAkLL;G3KCdlc{$4y@E7kN{AD69nc9*36)HaB z*8V^|jd5F4RWXlS8(;cN61?va23Zz_Da#Q{wvCwLawu-S56G!6m^H1%t;glOo*u;Q z2jtw3+lO*eJ`R3-yj_V~3phtSrJ?JFk>f2RlZc zb9^xq-%C@^#|;DfXc`!FZHgnM`>o=r%(IFIF7A$V`yuZoF8bvnj?gcPBgkvMTHek0 zF5_0n)6q9}GY9ll|Pl_b^|1J|+n;_*!6s$$!XnwoB&YF%`mY zAIe3;SV-ej)wUIlQ5X)Nk&95*fVqm86Jf66n3m&MCgs+e)sXXn{!B47$f3v$qnP&x z{z1WSRFaS2AItX0M;znTKZSA3Uuk?2bJ)BD%AW(_CoS^{<9^0cN7o(tA$fSiHDB%5 zY|cDi(@b;bFB=>s4DwE0nUlDZy4S&sX&F-Y!jhdm%k(@G-Gd#&O|v~b@0PfD7*jgt zu=%UUD#AqGG`Smwu7_Rh9b&3YaERIP|D88V82NrgKO&|e3*+HOWR3G!np;*~S4B9E z2(g7U%4)N`X|t?V!r1eo$Qxv3kfnb_)-)#8-MOjG=PJVI2yjNTl-1sEVh@YZdir+W389?EAuO{>$cZFZ3!?_e0BFR$sp z{q@^9eX5nmIep&&Roy;0;E$fQ@x?{V@V;zz(NXk)`CFx_PrKxp5G2u$YP-3MsULx(+T&uy6|PSAFtIIP&KcmaAxvm5{(iv35?8TOy@jhIGmS)N1|B2BhB3z` zeZs4^_KB5Ho+#C5HAZH*IOdBy)mOM#1aM5aC{^<<9@p3kcX;(8p7E%Fi>rJ!#Tj8M zM~v)%3xTZz$Cdy_wdcS9KCxGZ0G#s8pMJjPfmjtekK+^esQYwBk2}b`32Zbg*ie7| z_Io71J2^{BQ@U#_9|i+H)$Dyd3>M0~cR7Oxe>Z`je^A~bo6@*A>Do-Qiw~Kf?#ATDHd1 z>Bb%dPFp8EW`^KV(LMjV*LL{YfR3yIpo{#anjgkT+b2650%bxKU`&ag%qudxK;GyKt(bIXd2OnFnae6S}9lXS7 zJON+$f>lc2w51=B$1qDUo~nt*w?ESyV=+Evb|Ns=Jyl49-D|T~53d~tNQp7 z+`nvdR_m>fc`}`0$^-xJad5lc;jAsdPx^|PFP<{76MP&QTSR9yc}II6Z&N5S+Oxe{ zJZae5z2<+$qnRdtP}BEpp|u~2_DAG2{kHwn=Cx7{{CF=N+6TBMuH?i&Qp;y-$6oVC ztzfaW@Z){%u&F?gGAZd-wBO{|L)jod{*TS(TNsA_t^N3??f1FTTq=xD2j;P()<^vj zLq(pnNKRy@Tq(BcDxKe$($cU1ud0(VdmzDO{t2;V(O-zUaz6Z};Kz&|553L( zSSM#w_ycrLSN{J-_3(mAO^bgKUj zzj$OfvIE*5-q9Yf8zy*wkH@0}Ki=34v&hMh*zChCS>;UakLnSAyur!SPR1|ABW{VK zy^47z#iRWWKjOP4iMpTK-{^DY#|Z6}4(;JbosaSVF~#FIfBo~X^MJ&+H*JkO5|^`a zUrpEG5lIVQ?lvp5gEJdNk2C~VjnyjAn#aWBv}cUzA!uj#x%MZI%jQB0T_W}4fcr;# zmw%)^5dSe&q1|;{s_iAGZ^*=Y(o#!^e$I`h!b@=nfKMQ~ADfv^Sez1e$Kk2vp z;m=&P@e{ldf8ak+BWf@F`ELKP2=@u%TmEx@f|FwtH>W(pFtR^Ap?gug z-s-nI?l0k9Np_b|)hxAeLMOqlchVa**|WpC<~{w&D9t;j9ruTZFtSnOb-D)fHkkYD z@Uf!AQFAZ$Qj6#`PUquXd(9q^z8mdfs&3f0u@xql6YPO`V7y8bYm*HPwQy5TLxR}G zRd#AV+5@gq*iV1{_UR|3IBJ(d>AXweGB58B`)!G{jmy5T!<1=m0$NM%_X=N6w5p|R z7fnsW*T%!gxh@H4T|c>OdhBZy#O(%>w zsu`6)i|xjj<_r14SI#T;QJtxqljexV2IJm-D<`I=;NxT4O_wh3C8}K4=#_V>ty*}3C53s-5DcFep?;Ox-C9;l|7Q{Xwyh0yiv+ zFi8b&ydZf6v|&?+MZnE1ZNW23cu-p8IKmIZDHF5o#K39iE7CpT2DH%%?b%~Za|!lJ z3;f)nZ5d)1<0sFH$fa@&RMphhq-`+=Z5tMB$8w~p46oliz0GMitx~PvPF45D9(h#Y&QVTr4GdMuCBtLMysw3#t2Ja^ zVNYp6mmQSglBye9ancTSZF0d4fC>NodVc!p&&a$u;x!eouMFneed#!TVlxjF7_?>zj7j#}9`d%M+tU$&h{ZJjzmq8euQIZ(*+qHzX6u4dXcLlelL1-;q z?4;VvH~V75hZ(1CWN>`=s|*L~hCMj%fquX*_1Cv?;tu@!z2mUb=`GT)jw3y?CjAmse;dE& z-r3_gz4u+8Y**R6LA*|JX<>zu`+?gM<_?&}w{PRO(i9(85isl6If|!WN`c=D_)TIg zlgMuGCZCT^-n6`eGhi z61%R(TIwqO2<&1^s1uI2%nzN0c%|wFdHg;vwL=`VICoKYBQIdrq&}XL3VNlvFZ2T5 z%q4c=gSlQq?sX{lO1vFCfj1AXQjQS;I>LyB z(bU4tN`Uxmx)=mmtoI48i%IN0LA4Q1^G-#SvxpKWa15R`x8HY5+!4>eZ+L2o@cnL? ziW3K4-0;tAqal^RgeZbJ_CXpZ_^?A=hAuNIjq?py`E=lXukbwSD)CGSIiL*hiwGIG zCI~qw%VRDAQ;LxcHF4Dcm9dd>yo2$dV=%@%4H!iCk&|;#7Ibn0r$Czb*ajmI^*zE_ zq&epYe=o)iqYp5q)q95V;%r-#6FjrWQcMg&XFv3cCV`s5mMQk37g)UFC+8H%qv*24 zHO3&2z>ppO@j|a|qA0-I@}ys;7~nVS?=`je<3dYpk-s5BkxVRtKSfI*s}P zkJ@|iU0I4X;(N%az;|oMUS0XIjUXobVV}|fzgg@_{Zu*T%6O?Tbtis!@WfI4BXT|U zw@P}%N`M`$5FIwjzc%W#cKl5CE@m8j9~g5xhNN&pl7@pLd}@=IEftB@>je_D-d7bS zu`S1pgT2do9y8GKDrQ8cj#%mS8E^j){=bmvz`MOxMBFL0wdceTo0r{2vsDKVWw7m!FVsNIuMsbR8j-0Wyp-q~=l&mS1x5uOShoNG3AU?aZ;-Z|wm@@>Fn z{LeKR-~DlviMP6yAl#7@y>eTXN#fR++& zJ+*p@fQw~cXdcG<^2aC9HTt8vPj)#z+G_V_msw`Fbt>%96uOB&YR42v$ss8$adm`k z;<$4IH!FNUz?VG$K`txdvi9Om)DN-~Bn+K^iM+VH|!$u^f9_kt+RdU#Yd~}e1?BFPey+} zFw5&qI15Ha?lOhD>(7P`iH+0GrS%uzr{#|8l48`Tx_-6B892?*52Qd8MS7Au; zf0R$cvWgw#u75BpFGzQ}niOZi!@?exrUAz3zOMq`NxC$!F(mBM)apajabvqE4N6v+ zDfv!UVjf5P&G&Pe+;?QnA1F}&B32lL$SN+}NVC3de0cB6W^2`K=j8_ehsW^MctqEs zZ~VZ?kn^z4I3&mjK^*!ml2=f$f=`PeH-Ov-@%x5cp2Z*+7%klxsOdar>yYcL%XQOh zx#+$SJH+>OOx(mbp;QU{y)WXsHVZXMcP--Es1X528Y{51WcYayxF-YzLNCd1t4ag} z$)Ol0IU>f#m=>{j72{zPiV#2PrJ7e`h+}pZH?|CMrp2)}#kj@CrhmH_*XuFHTgNw} zDaIS$$GnX~{zyrM;_kE_z|}F&^89F|FI0726{*kD;xnJhbW7H4kl!I=!2QVK;l zC)Yvd5kpVS_Fk#m2y;_Uk;D#tgGl5O04L}Y8h}A$vb+GmzcicVo>$QK^0Ms$fi-1@}-Ts1Clk`bQC7Xx+!VX|4=Zj zxaM9+tA_B#8s$L3^b%3vWu70hjh-T5w-{rK90lx5+mS%bw^4T(@%e(7lL&F+IL?zZ zPm(-G7TBGzDMwm5jjU|v76#9m)-i#+5(8_1aU7Fd)LZd!wZ3+y`l>ccs>kphV_h?{0kVqcq$$Fe) zEg{BoY*Ql6X`WK&yT+$^3OZQ8-c7=w>rZ)Du{xrK0hfurxEq<%oQ%i5TnGZEb%c8r@Fk{6F5>Wr zDIkV*a~Rfln1tiQQU0lKNNl@H61O)qY@i@OE6kwJj*#o(o=o%(xnunE9W_infgzp8 z|IV#+3u;)1ePV7`!T$$K?(-p3Nuj#HBO<;u6>4F?FIWAmTF9cyG<4jY-VD zc6}p|k(i+K4I;BVCqCvhJHAd*$}OGEj5xX@PvQvjT*ul^@^DsvJf?1Suu(OEIU6R} zb%aOLO*2Wb$i?nx!$(Jy?sI>-%4r#!Gu+N{IYNbJIi6Q37c66w>}u*fHhawD9h8?~ z+rUL|9w9UE+B2ngk0(3YA5Na)c^}`DiR-CD$6L{J|PWXoYhdSj1KS55=OTW_!HO!vEhR@ro1rn70Dj%zZ9b(+ixvaiSqG}kvN z+iK^nj+Ee*2u=KvOT~E%t{|WKtPMNm=;QWWzb{}|xU~w-%7O1$x|`|=Dqi>VCJznv z-ShQ@P3EP1U{_dPkWk?E7^ACPXw;I>v!ZjM8VS|EzJ#{GX*^o!3zBr>naVS&an9b`drY{vUsBJ zp2Qm9TK?0nw~u`H3A;rOc2#}86Ixnv|0KdW{~VBGDgcC)L9SwCk}NiI@gH zJ9Kj#$zX+o?^rMm6$G@WC@TCv%?bEvTWU6vuZ@IKf|ywzaCZ@3Q#T>mGND@kk=o_cJ6qL}2s zw@9&tL|df$-b+K{4CB&ZmLt`|7B1@I52G(=cTkL5-~X~LdYkXO(M}!~@TJW+jfhE% zk*Ti0q~ttO@bFf7JZ*@_o^}I$+U0Jre+x|p9x%wjBL*f6zOK9WDK2}KJHA7S%!4t- zHGwJk#1WWsJ8q8<^*j^(e3z5x4}8NIn8b#fXK{MWUjnzOV@#NIatX)9BV1|N`04S) z4B<-R0$cS~ia4dCAx>XDz}24Y;K7!lZ?Th$J~Xsgs&V>CtJ^m)QOr@biir`ItC(8Q zOH?*N6xEO)5-4&0uB04Od;Hs`dQFInjIwcM3F_Ou_N(~2RXUF;&vu@w(ENK749 z@c>LY2eP9FI|hi)2~(KR_0VO&!$V9td*b2BlRBKR6Q(dxBU}PgC960t1FR>Eg&zD= z9&rv#;3U_jp~dcVU<&^x9-W7o2*(5;GSQa+HE>rv%5iov{jUpw)UY&Tc1+OdOgfw6SP zW6FIn>OA-1rrO9vZ^J02XHgPLpRk98`%OnakG+Gl2n7>3Vy`_GQhCEJ!G1OEC}U1Q zJKv4OnZwRc2k@D4AWvT6RA-BRC;fwYS&C&M)`=RPy<3*kp053Rc!PrKatFXlfO2S-= zQxURGzzkvmU3|=cF?GebKH5hB7bjrEh2pTG0hXVBWtbIdKwTKLCz^XX*q>lHKi_k_ ztn%o3pFl^6J>aJS7>&^%j8T4r-2m`FZ*Yu6c^!I)d_sq{2&cVa$JRzMFccX7Ez6qGx z5>Rvw*+wxka6;7+=6jnvd;mR*bvPAq`Ke!{!#Wj;&x#X(lJr4Ge6SWFJ_-03;4@+P zFPooG8_X3V;GrfV?@yXfptciuoyQ8hq!GV%9@1{VJIL6PTbmj`~1v!0yT8m?=At%AEJHF(n9E9$f0`B!?2 zZ2qo-3NX<(HEiX87b_}$uZ@$R4!RHwGCPp2BpFtEjHUd|tbfmdu# z@H^w){X4KZ;C+Ej(lRJeQi2Q#4M|IvbUdp$cn&(g6Ym6e!aLWr0_K5Bn{a7MymycA z{{0=i_uUDF5&%5n8p97QPD*z6P1C0z9!JgB@I@R6zMa}Y*{Y^M+6O$4tN=Fn_r&{V zC59GwPxCxYas*0Ol{EZa1rg=r;%o3(h%6VCSQv{eC-GTfuJSItpQ`$Fu0j-vg-NWs zHGSC$H?&v+9W0Y2)@6y?<>?U>6kxXk3moXM%-iLH||c zeW$d;>57vnQN>vj1?boXT~lGL5={Z6`b-gJ(s z(5^id1*I7tR&C72>9iE9nDXgRhdz|IEGTb!B_7_w%|REdQr{)p{Hyu5YWqohb_Ux^ z{mI-viu#nF`5}Q)*?c?Tn3Z@vATV(*h-Z*9+QR9cbWFFgi$^A>bC&`eFzkSmb?l;G zq@y>nx)m98sPs-5Up7yuyu%7zvn)E=i84Cp-8(LP95~#^I_irkPtP!f*Zw(d<`_~< z6T9z+V|Mnixyx%_v@CIbwL!5zPT{GzkIqrhrp3Df46`b9BQUuP6YKHgD1&c90XpPf zHvWMQHfoE^V`x}JyH&s82dvgHi(G$uqynX!MW+*vP0$CO?kwai4Z&EG%bXoM4Y3a0RZi zZHjs9%w94KmNC}>mpGh_@sKy}TeLjp@J%UimH}1h-f^*R^;bJj7gr zwdOj15f|tK>v$!egj-#47QHbq7un2D9H67=v?3C{ENH%`MIZXykg)uhsQPQ z6WQ6U$GG00KrLpvNEAMgYds9H!-=%PW1?A!iQBlt4sGKh8)cQ7UdJ8wzi>!x@KlEi zNgVLtlpdgHP`p0L@CP5{M(-}N%?@Tu$Vu-`?&V?TW#Pk$v1OKHTpg;Nfj0)0YhSyZtY2@$aTMwBdW=@Ss zo`)SL)Z{_H>D*&SXNYH(LLjctzhxl@Qb8!zhyUTxywZ;IHc6cij_rpTHa8HM~El+~_F=aca+MZin<7Tb{FL}P>i$5V8P#Y)m zcYt@lXE<8OxQaLU!8vBWHsS?5QJI5om=U@`5@-xcJ{@oJ zh4Ay>18lG=IQPRDRd~!=FH?Lv_ueCqdl<}~b`43e1^B%Z@=!2=S{+K{+)j<^y-x<+ zhJgp9j8@2?0dspkCx8!LE5~qvU2{GtVG9e#JVD51=&YW_8$Uvq3}Z zh8V*=88PN{UamGY02M67><@(}IMTInL=jZ1rgHJ#M>Q+Nz+MKcP+vEUK|C%-IpiGr z0}Xs9xY@(`b%x7hh&%cN1|%w$QmZb(P=_3dDJ6y-+b2mis~E1a^>~d5lCTR7IaB=c zv2K(@evyArWL1KmW*BK&QR{(muf(2lS76@*Gi~hS(3j2tr?{G(&y7Kf}vh+7RW6v5%o89|78bf8ynPlR5<-%-wp54xkz=g5or82&)x z9c~Vx11{ykkex6OfIs*|q}=B42V#)!ZeR`-oM|FLQvx83oKAhnpAx){HuhnhzE1Nr zW=GBZ-3pSDO+8O2B#k>*}5B0bG|+^VDfFCAwX!BfraK9v7k{}$zR5~lwb z`}F*29xTc)^gmJ`=88N^JM1*Wz@z{#b!Ls{_`9QNAxjZ_&zJY8p@szWX%rc)W#NNrh|f*^gi{!aiDi5a@;L4=%k>Ed zcp`$ewD^KtWErng^}eK9g@%QF&|^b7z$rFWyqy>-Ga{nd*TTpPbAT{qIApZ)oj-HQ(6{UC6Bb>lwvc%;5R zb9(Il_J*U_US^c+gvZOCd>kT{%nD9nM7Fh*e7l#nokhf-6_ods9?sb?avgO2qbo>9g zSh94^oSFB1=Dp8Mn$o*a1(;6YN|K(5H53VTXtH|~$gd%H8BEa%>bQ!E?F`{Km4;}K zCFH$`p@2?7+AmAu6s!HD)jW&mKqzp7`TZ%X$_T5l1P1$IUtxE2x}Rh=VIm9O9?l=0 zKv_~bCSV7HKUe_{xhC(6O^%5PSVI0V7e{g);c}{_B@u{wC&PrvTZxI>7;iI_F^2On z&Rg6|d{t zu{kUaB@*}?1tJnCDU^uDhSd;pI?F~7Flil#g;?wO{pljFCdeLBe&it6(>;hc#*Nr0 z)j0OW3c2A}M&hgoH6cA^&o1GtBtOP*5>cZ@awG;zcNF!aaEsIO{+Q^393UIklt0D3 zAF5N-9P~Xbl~@p!ZIJZEXD3i2Imx|!a_M;@fMEky==U&HC!lo0h@#|3-un;mTGo6- zCwXpBCyhehN{savOK}hsfvir@bAglSk$GLj$$-%+aS}%L3y$kRdB(_aEQi|_+IKWz zap}I2ZZc~{Pzq7RN-k{pvx9~!%450@_xB9;kDx3A(QSnL=>9O)!un7nBI%9$icZ2T z-UVZ;hk_Wgf=z%_R5xMeh_V^k0ysA!pHFtxV6ol>B^V1z4Kyn}-is2*5h+(PJt;)( z`wduW(5@Lot*MdpNG!^F%yIOgrXzxOq~00t2bvk_#YBaTpF;7>ppiu@aJpj5RqV-F zBdRh0yCMocSfFjaGOyn`ALsG8JF#-2hLyh$_a==58F`}(v*KhE*>qgD|r zZtSgsHpP&XJ%3M|IB77F?CsV1QiT-9M8}YQV{g%B7CCMoYmNT6Z;cESkZjpk=cJ}IIFSo|m&8!G>;*mhPOnTW9?v>gG< zT&YwFTSXOl=VMmdeYFrFIx$dd{5RQlmS@4k3HUui+lS-gv)Ud&tuX2DGMXG|ZeU78 z5`BH#T708nBPqghh$ubw7IYpE>7$)*r8dXrTW`7$=e>qMkzj$bxBvg(7;^)GI4&byI6NdSdJC<^xl#68vF!}@;Gcl8BeXp}H;MvIYsKdNX4|b;z%p3= zTXAj-Y5u=+Zj{=lxsk%ovOy|rgxg;Bn{0akxlFP-K6`FN4x>rWK%tNKLr5oC*VtR& zIDs{wWbb}P{-bJp92ZC1_5kt}y+?)PNQov&@r@Q4GVw7u z$j09Sb19xxBU-kc&PB{g{ap4wh^E4%i^kgY4HXoer;wx~4dhD4f00-Y{Ucaz<->(R zlulr(6yH*t9CGKV`g#~`H6@!L5xxSfW>WWMzepVS{DU~|&6wRpz0&wwbB^#qJu#ur zC0h+f(3stZ_iAq3Rs*`mY&fJ%uoJew&<8Vpgr&=Vk$8gyReP|8&VkDEskEnrw-fWE z)7=C!yJ;a_rHxhg`v4-oNh)9JV^JKyF8}pugGL|gKY;zHvn)+UB8wM)kv1Hjk^Os~ zkxr(}^V;E@V%PZu{g(RlXTVa`e1Fd~Mv|pJEH(DHPkRPxPvMieit|SqqdnQrah6Ty zvq_dU{A1pS84u4PuKL;c#9(oAtPP99f#uAH=VXhzVd8TJcs!BsO4894S~@Phhe#|& z_aLr0rmrX7ljb-v&!2?Ch|)duoG`+YxThaJK`&|$NUu$}hqsDU@_;WXQu&CM=__P- z!00L6V?By{2C;?@o()O^DQ2GL8B`pa`g8m_gx@fOcJL=z(uit-9Jam>7ZABH_h6;_ zvgNch zOyDniOk)d|_)D?f*3Y;nmNLyvTJuoi?<90*6Zne-6U3_$f79UaWcQTww|}sJps%5% zQj)*0Yd@R6=|myHdyFo#Z~aGgPbz8NBk@|~?-&ir=v~fVA9S5*xj_&~J^>fyv@5@_ zjK40}kLGN$9zt|NFK#K@qv=nioSur zQOsYZgz<7djn7{-!vguJNajab&=@Sv!#?n7vir*TB=74J{RLNX;)B0`Kh5QwcOm9< zm@JzxcU{BzQDH44MjBf)wO^aR2qjIc}hs;(p7i+PzWWQ&sb(0gHlsNo~sn}f9ew0l** zoM!?Y#3@hiMr^oUlN0`Ymd6ag-dY%qXR{>R1?dFZGqH3XhF5;wK!OY%NpqhXfF7l@ zCvhz+wn;m@<-0_n(5r_F*+edfOKJY8Rw-Y{uO*(kaAKXGbc-G;3>F*a*iqBen-i~% ztJwp7HJGPsGYOL$f7i8Aj`m|eo(W@1!;)= zyeOkDu05O=X%We%CZX@&!wcvU*Wr8{r*AS*CNpE7er-6x`iy=_Umx1dmD-iOAgueu zaK2S^6#%qCsbSJf@ky>7=S83pg#a-U#J70Sb~rM9%lIY+K7$viS2Np{`llaj0VZIw zG%upMH6Kaf@BleBV7(7mKjc~y>w|?XuEpVK(L$2w09isbYn|9`xh#o{(M--E(k~PG zFm|mrQcw~sV+4xyjRNb%cFX8Xa;LS>2VvHgz&h9-iFMejq)#T&Y>d9zNPaXyXE2)d zjeiEsLP^$d^vP@akklO$+a1F?+RY|#)~}1`kjCjj{gB(H?le_}n`E#+6l+XT9Ef&B z)WLi}bQSNBdg8EqR9BgTn0QZ)DOevd$#c|DvH*Wfn&pG#_t4pDSv3G!!b71R)!?ri zd*_UjFHQ#|@>ro?Z^fwsactDC(DkXKxgN=T7LUrD-C%K@jnojO_AwhWIO#Bwx8lUZ zj2=QVsh<^d$=Jg`r3E0Lq}MdwCl-LV7@*S;MF=N0;vQ5tB)|4!%2bw${;%)BTp56rYo**9(aSvx=Ok)Gu>qbbpvaI#d?<7Z@vcG! zWNXE-*Ma_^l?PIw9o-w7^j`R7Sdzi8&c4;KLa2ogyEn%NG$8x%=Z=ba!OAjR@f9)% zWRDgw_+!gNvt%oF!lc2>opXeM#6CX0DK7ehN3nOrF5e~+^u7e78i3dCe zK@-Tpgf=NY#(3-pkCR0nyKt~OmIu0c*Pxu2eMiHy9wKLtyxw;-*LT6h$YQSs=&8t2 z@i>%Bki!lz+mT}j>s^Fw%ubu`ZXL*_mI3z{>y$vB(XJom924-@7ho`bqI z>_ArJlQOJYp}!B2w{9tKhggv2Vv$cU5_*hQqP0u7m?>wlt`Bu^lin|5VVI9N85{Ev zGPihtI36jxKZpBclifdYPSUZ(liUy8uexL`#YQ`D;B?)lnvR8adK2w`M5;E8cyui4 z&tyao;@(|2n6))N8i)tD4=00gE75=}8N@gvkSSwrxJzxwqa-s}ZWZhG4~>Xk#QA-+ zi&osoKqT=nmIuUnklM(hawP(tT56+_+EA$>DcehY>0Kbiiq;9TrBp zLJ#u|`B8t9)*?7eNFgsVZj9jYVIGVdz)0Z#yLKQCMv&Je8hcGX6=_U8BWgFMSzo5V z$3L7!p+gdjUSqxyV-(xk6h4F@^`?zELB` zTe-`Kj}G{;h+uF+bD$W}8|QNj$hNSjKb_7j`POK*CC`M-p|MPgNRCCherl~3Yo>gl zTMF$N3Ro!c>I(Scp<@3;P7Ri5Pnax@wTK0bG>efAWhEv9w3ArE819M(c_d)~l|jP6 zuo(O_Ob)XcVKOr;+whw^yA4qrJ!mV=!$*syOw4bLV$bj~P>v%2pd^A5jWWPwcp|p- zF)T@J4<`IMluVYI2`7wnFcIV^Wf@HHUP+fCCX4NSDkcZ9b59AAXbJ~n2XK(8kxPI$ zz_!Q%+eg#RfRQS1Clf$B(ISD!M;^o)!*Lvy_%wzBiOF97@CY!OGDZinP4B2Ng6Urv z2*!h=2eTNjl18vS562|tTUlGcWEwl*g4!l1hlf!-M<9w9($pNzcpWmEBbxK!A}-DO zQjT#slS{B1F-A;OGJmEBN_2}r8IBilBnFT`JWK&kK{?i-BMqF)08+JP1#KiT2qOg@hF2(wW14+L>la;L$Q5kP_p_xR&NG9lb zc8RAU;ECvN_QjlYhsa7$YGho*p*NY)Cjpm4 zcal6I+X0?3M}a3R*p)Hj<#m%YH{c;Aj7F9&0X$;%-lF8c>i$^K!XVv&z2#ArSh zoAF#6Iy@`-fX#r3O&kOmAW-25*bya6m1tI!xhbsKL>q%S*#>mDHWD}d zK!UKwjn;%H;wi3055?sncp8oYPo@q}kb9CaaI9b}^(2uf;t4d$XwkC1l;&=+FNexY zv_qS(`KUL(1j^8atAxvbi6=SV$}|!yHe@RCUKR-YvG#6)#O!JHLHZ6Qu=y4emFPrmNf}3aJd;Yn637;1oRWqK1%ri}1CYv^ zBWyCiHXz$8)8*iw>|0Tn!wgF&CD47`XJGU5E)Z`Bh4bYtN@&7gRQ<4`C{L$=*N{fh{n)$(t@M*j`BLlos1lZt!N36Z{0|7 z$|2Unx_m~A%;!WQ1A~nEiR-g!O3oM4ax$+M$%2Byps3#n6o%>gYzR>gx;`g-(*L%X zEG?5KB2lpBcqHyC3>DBEWKj z^iOa@^e29p!LL!)E zfxIq`eowq^1pYbA6I>^?8qvWg^CIE$_QvoJD-U$-mGtr<`=?B7PaVl;5S8@dKz5PB4KGF3FoJb+@k6KW0u$*3F^ark! zrGDplPWteZ;GZOTSAvg9dijtq&P{A@jQ(g38?y|_>s+Y)wnA5wwI}5dqRG@A)TLEk z*N?1vd=mN7vwxK2PhXM`nAhPP>coCFzDSw_oE68_p?9}8R4MbgZ234 z_nPa_?}K@N>HB_IMRDwNHhzD9QYGT{VO=`z|BUaYd@kw{^j}6wkv$Kyu*>L&C=V3^ z!C07BDY2Qw8P!qZdFk3*lEqovWgyM0M+-h|!8U#^V&#Wkn`0v`LqU27*Y@CWwqm=W zp#scf3}ZCx&0|_XEX(I2Z>^xML%)w2LHb_OaM=8QoQBAlA=S`NGzcK>QoMis`#?T` z@53gIqDP`3Ry`dJ4KwJU)IlY>Or)W|oQ9cCr=b!lqv7bM)3CKS);C5&)HFk z$cah6FN{bUwnCq|CeW}oDTQg&@JSlNzc1cDPD8?B2*#+Sk?%+}?2_M$(Ib6N?E{Bl z&``>;$!OS`$I(1JrLp4LfiZf^?2p7@KjCmh59JfqA`nTQhC#y;Z%WsW;gGKFjY%Mq zE=$8&nmCvH^TQaTQlw!RzU@SuCh|kN6?uJ$L*(Uh=KbR|qPZL@jG8ozf`+Yx3kTg_|NF$O3{XC2B%kMAF_0|I67_u>-Dy9qt4dqzNaHx$O7`8@CB4Vt#c5vdg zWpi0xJ5EC>6Qyh}wTo*@G?dr&m_VeMR6yn^EOHm;1(cO@S!Nes*>JBB$G+n%i6wE! zfrv3>x<%ydK~4M35nqZgMJJR1rk`4W`u{}YYbz~5S}I-2OEwWNv`zLslbSUN5v=_ zh~vgE`Vu#K2hBbVBM*DzHn5bR?xp3qh*U>$q6uMUK-d0Asf~=^Y%5c0gRpmmv2g6W(#r!ep6)5+c&bOW#+g5EL@8X%h0TxddVD(}3E5ZvsplMj{6+m`mu?+xB zwn49Gqc;qyx{?L%j9;0E%1rad}IJJ|iFHzeCZ zXCh%NtjmUu(9SUOYt|#t1=}1D-usYoMPG(`L}-{L;)UVS&=BFhRF@SHkx!v*x|i_Y z$FUY#;vFmV`!H8VT8pnHZPSR|bse!ECA^2bXcat$&)D7 z=S;v@Io<~%fH{OuS2QAlkN77x6la6U0>;>vWw}HlDDfUuhe~pc=E@;>?+F=cZVdsa zMZEJNEbYgviDQ2p;6G6aqceo}=%?Zx?W8bo#_@iHc80OO#Pm1DWr--tcR|a>f%jgP z3=`fXJU7Ay5{0yp3qaf^Fb8u|zVqB+cpqznABuM$+CVVf$hDT<(0J0&wNR`_kgc@E z4jaJS4Fd1Uph%@jM+7piG($cPV3jmUi?AAzHitQuL-5`+!VvpOhm1jNHX|U5qVPjT zT8ppf!!Y(OJ~CEQy>w#_9k`JIheXd1_9-%~BmM!*-!VNkj6HB+032Z)*tWoIgXFs_ zBBFz=7vZ)X=DvKDq|hS>WcLE^{e5FPw~P8y$c>iT_%s>R)lYbj zL-!U&_&}eSTn2TqA=ppW!(}t!J`rWDL13;dT|j6yQ3x4*H~}k=$eVpp z!Yrc>>#ibAu*RP-Py6-ID+&3&5Y|PGg!29<3Zj5pUkH*GXCzP)L;V>O^;m<&;;^@l z)DE_k>LZw<9aj|Fp;0?w7N?c^5m~Y(};ZP13{bO&)rcof1q`ieUW*6@*y=u{Z;!!^e_l#B~3(_bvbS3gVM%0RT z)unmxP^?rTY=P=#sVS<~Zm+QR)MB zj2gk7S7aJtIAQEoU}d9;La&ht;2oq)<%sj&f_VZef`70j2Cz+#P_i`bhYu=& z42vT@i|J4FM6SVF)(;xTct1**$8NAGHX7l*V~=kgdso7&^)TFvM$=vtJO=-j$Ko8a z02$Ayu#OOyhOnc3@s;|5ogH$J^GXqGhwz1s4iWA%Jl6}{r|}l&#rp}cI2FhbxD~pL z0ofPqOVJP8d#HqcrD)rjuc-(Nv_jVVtYIA8l7qv{0x{q)-a0jS9U?9O2y>!ODGiVX$$Xd)k`cH&AuFk9B2@^-M@DvrTS zax2x&$Kkrfd* zp`{NI+bphw%p4ZEV%)B1*$Y>w5EH&2nb7_utZ?(X^kLUo!*pF3Gmf=Fs14C|BVq*o zoA6wb31YRtP)J%B30R@#b;E~^BO9RWf~pdiCOcgx*X#C99Iw{XL*wWbgDKJ;t;6PZ z!x--bx@fU5T^Gh;YH9t-{)KoP8NXJdOG$gDqA(o_gM2i~g4YS~Z~~1)iHD$N2*QqN2_7DiH{=zOJ0;s25d(+uaJb9lHLSQU zgfWTc;9>l*ahSY`h=DZm&|l=?;1RA13oLXbv$n{?$mBd65K0dY<}sC+q&0XLDcTg} z{01*vA{{&A+FrylCgEYiwdweuhfVP3P1kaCLS>ny02#Cxf*X2TmeoR_{!zKGn zp-w!E9mX4T9+s>xbN-Kr*2Bh8y3TBmydvUZ_%Pn|MrnRUMc-JofUXN3@j7X76-KB% zCB#zTA<230#k45K=3x&@^+SkuCGsO6w!Bsi9d@mphl$uB*Y>g$;`9pLhs-%PuEZ&>)-yJiqQ=EA zkbr>{>K{cZDcp9nhfxh3!XKgahoqd8c#M~>W0;G@{uQ-wN$#={R7VD3iWzBhjzuu) zq||N7!ZFRr!y(92SjI7E=1d|VD_xsVqlaEAlnCZyG5`(*_y`Ow^V*n_Q2QrdI~*a2 zg=9d)ZWK~g+Z-10o}{qIrt`jOWTd_9Ll`lG|XLnE4SM#D%(L| zh1aM|SDAXdHN}Opc=6uuZug@0ZnuXmYVX|Jv1k!?+-wq^qNAp|X(kR+*s-?rjmy@p zTgSGkz5^%SaN~_P&@-lso$!?D85ZU9!D;G?7M8TIQ!LD)_%UVUA}Lg%vuze!vAE;C z4%XiNRC{-K_fu$HG>Jz1=k__b&*59}L)#5E)cpLcE)szs@L7)YpAhT2YHxtm%Uo|aqiQr?JhktB{zkyn{wwA zJ|$a`tKbz@wPIK0G*;Q5xOTbgYut<7r?}U-H@WY0Yl|ySspOUJO1D<$6?V;|Ebd&~ z=~=X>9bI;JcDnX3`GwPO`_mor3;% z*04r=oC@ZQznymT>Bb*Ui!Xoe!!PY){Io5nvDv4`Pyc|Xl6 zk%cz0v=OHN8)4ptKr5l_KDbe>);-EJWv0@i{5p7@cNSA;F>ez?VW-!|bQ=>^ua$G* z5u9@m=PIjaTt(RqpXKwg-sx5NQVTUln){kX^QF~OgYN23aB6repE^g={Ff>f@rqjls;NalT?M?)( z+S@ui+ucOL_U=y5tFzNX#3DTG?QD1bp}VbPFX6AV6WHtMm>U2_N`Ge8G|ivcGQYOd zjlEvyEubA3Yq2+Pi+cvEaWOZYlUC=(Icfafjep$utABs$_JJK6AHC(vH-7KgyNs%P zPyOS->bHNtBl=ve)UsNTBsitU7pkge)j_=0R@YXGp5{bz3Uq(4k-q*s zK5jg=$C+@ZoWl8_?H3n%ZuR`sBRmg^*4;B6nzh?kU#HjjDt0RmxpwOoW%1q*_b$c^ z{%|jA-`oA+-fq_`ZF{>VZri%sKx_~mpUw@m75|hU*w@lAe_qqfnra&ZP|6NAD}BzI z)+e`ZymtGK`)|16Cb6;5vW>si^u4>rKXN^u+_C$SiW!mojU60>M zAKvxomGmLWHK81T@LlzJ?bpf_Wj5yaTfr)KOS4S{DyL$PN!2oI;9xEoaJoamS!~`c zHfyPOh6}%)GJ_pAgXuGt*89ByFCVPW)N^mWx8Au{FtJo6dQG!=LaJ)9w9g4SgLYTQ zxkP76*cps3WNhld!O`GChchG}Vr>l9Mre2>YOOUS78gsQShq-`QO@x;yk+fed)=Mwop><`-QDflyRy5x%>_By20_s22EgYA zY;3_o?0xHPYSEh{aThiN!krzpHPzY)Kyz~wo!x`^DG$TpD_H%~#)0|Q{OPszyWW5N zwRQLY9yZRu?!5C({pNXWi}CpL*L5d_)!4Ci^Pm3R`2EgDe*5V9J*x(H+o?ZNB<}i*6#iGnNxO`R}ES_5~}kJ7-qpgb-{SiRAZ4Ae*w7 z|9(saoAY@HRX`4Fl| zvsqNTaIi{V;jrqklAwy(JL?v)`gTkTCT7=ZUW)oVi~}x~u6C;1AuAe!E}MQgOoEKy!lvA( zi=RV%NC}6ZcnmyWTF#Gxu_iv%xQ4C2g{?QPxrJZ9m2EJt-fBSjFg9SkAv{c1bY*d{ zzFwcB@6$igRXxFO>s4cFM$M`@Rg<8vvQ-f1tzuh)-mY_9Y43hdD(|-L0?l<+r;8@f zLcv{&HYQGPV!AJDyu?m4X~d?pmqePc1qA#-51cya1g`lNDl=CeW{*RpfU!h*G&CQ( zu1x1!*h{q41^W2n!M)sI$3o*z4_5J819}>d*R3|?4mAZt$xd{?hZ{?#!>UB$R(9-8 z{Gt1mmmv&BB_5{*D`2>24B7~Qgt}dfeFoc!;~*FbBr#gJ=>fh50tG^aY=SM2A&VeG zY)Xx?An2)Fov|`&op-e&T*5Deg)~mE0`gP1)@T;{8f+ntDU89io!z|`Y6d{k=oG3( zI)rMrJc+7Fo=U~ff9qZL(#U&8+m`Qtf6M0Uudin7*`+*ZTw`o0RNc<%+41Z+R&VSx z_87mUMej`6iw^w_-Lflf!CD)$T9r!`=3;;&t8l0mo38jUqF*nP1i|A;0nQCHHo505 z)T>wv>*Rk+TU$=sat>R!Rc!v3589Tjy_&U|7~Tfk=UDA|=$~%{r^oA(b?G`$SEsK& z2yqpx@`l{?uhza*`%$fcJ{bwt0SmPi3QIH2bmp+Faawro71*hC@!pQbdtt$VnH?Q% z-L5~hQ8&QUIcz>F${!p#A?1;7VKt5FvH8o`*?Ydb?%T<2s|MfxW%kMu;~k#9gI)9L zOaFS=`i)oJ`^wL++P~j;)oS*0J^0ape$G@@2EX9&PW5udRkPaf7b{)N<+4_;zEj+f z`7mqhi`F-+A6SI~b}6nkJs5`DF~zrLy2Y`^rh`eH;1W8DsZO2k?j&AGd$hBCuLpv( zo7Jf9YPC@s0)8+M##9F#j@mSHYyf;WyU2LgIPlla1JAN^jrAKgB-8zuKd|$xM?ZPt zogISf?Z(T`?>pu0C~IW=*2Fcdu3CG|)@T0h`;X9AxN54yY|Si78it39Nqe2%J+M=WRP@kCkRXu8=W93ql0a80}Eka5m`Y^T3RB22|C)gY39B0VLFHH zTR&K_kUvm(x8;&mzhT07_dxczd!y_Nta-{Jp5AG6SHJb_pAMRI{1N)u2~5-|jmjy( zT1Gu(%u{>mlNGF@NuRb_@i0(vFj!md3wiYV@2s;io2>yot+q6*sgg6cle*O{X^5ZW z-DEXjI2WBJWO>s}D22ujZ&8od0utisr7N?0?G-Cu+4K9qJpZ3Yfv@Y~8`rJ7cl-Kv zo3w%3ea73y3wOW&!XJ%K*r{xuSdp*Te8={l#P8wNF=Z1la2)2et}G1(*6JH|zEuCF z4%XJ`ExLf}z@Du>#MTAVtP0QYEYES3Yy1j)N6e=lce`HbkhU!5S$8*TdP#t?<-D`t z6!#Wh)m%F_?|&VV_YTl$E@YWknW4;4z8h??H&irV3JuApU0P9lsiy*etxp1VZH<@G zyaCA`0>!MqgXKsdoLNy7vQ*bq^Ftvv~W{=w^=IoN@HGVY8+1A4IY<~X5;lHvef8O`N zz#olOBma8o#n+toZG%1jT7hZV(1s7j@!HR2s zxAy<$)_-Tk4a;b*m1AW2!wLf$swGS+fR87EkEU9<9IS$1;>DmV027<7N_>Y^*8?MD z^A^!e)65j@9VU`BAi2YXxg`xQ90f=anJKCX9Hzm=tS6pF5Q)C>OF-nm7%NkrQ z6k0H==U}_&F1Pjeij;ag4$WBnUP&KzzDG7dQ6qZCG$R~p`lGJ80e|v0n{G8E&5w*x z{;qt6Q#mR4`FiIZ=RW5rPSu&HxXotcTPHlQ%H{ZRgR|Ya7~Vqhu_a@m`@N3#_oO*c zdbDX4@PEcrf9LGHX5$IX#fSITJ#D3B>L zNJdN$rMr7JbWx3ssh#-?#O;Oo{Q2LrJ*(KBB#3`EVI{#HQ9H#x%ovLj47OOfO@$T0 zunXONy(&9JU8$~C1@;D1Rpq`-3`K*i3g6OmrLx0MdLmTH3t#~bU#}sHCLwO8$BEN# zxy4wub?d}2uMB>%hxhS*E-qBAQm$9TTDFl?t7?UzW5L)ltgO!7&{foDd6J%&TWazTsF*pck z+E%~6(z4XkhYt4$= z&e^kL!_IGi^V=&tJBo22rQzT*brr6>V!U> zRd;s4L6K%34h>ycqc`~>B9ul5ifVi;pkW~IIy=0gL0xu7-;KZAw(+W39y2pTsX_sqd~j z?{ot}J&FZfkMUjxyf|Uu&VW7B;XSk3GRwj(s_L9}rrlpvzd~^`#p!b9KnkbD7rnEJ z^;EH{Dy2T^^wdSs1{wBbB1)6-?C#6RP9U>k5n1`(Mn8OP-9lEQ-h@D#Df}DjS)<=Q zukm^|z&s!S_&Rp-!T&1!~4>JfsuXYzs0gk*Y))j zZY|`P$3WkiaxL`s9n!v;i>PW4+6g|Zqt1P%0{PGN3g=1)sOpu%iyp%KCW`=S7d?fU zHG3Rw2w_R{1sX~{|A(&_&%^3l#TNY6PnQ4a*5Bbs+IPP9-1g&GKf8#nU^ky}$8tCx ze=+tN@Seb0qTkh^pA|ajbHOQ>dXzm12xeu z+|u5=9+KyHc8>8w z>nrsS^n>t~gRYtooP6pB4WEPsEClKX>;!Z}#<>G?;fKjCAZWhp5%JZ+Wz+}sppf5z z=YI!0UIHG&rVGwiEVU~t_!Q5X3W5o~mF>z(a3JXNA@o{ZwX)K#R3uFl5Fp8R?%8YXH(p~Wv1x-p3jOf9!QZm)pL5ZBLM?QDjcsD* zvXyMp8QV`co-_VjcyKy<5UfYvi1$k*FBS)zZES^7X;oLaG{46E*t>yiVr9b0s;uo$ zR#vNB7n(V}I~TQ|Ey+-vDNu$)12`nWgu7E+@@V0qM<3;1eU#r_I0K=0bLkMCw-*z3 zpM2hl!B&UWzCyz$qinliTfmVY2&SF~YuJSZ5aJ%{Il2(`b0qGH*k9RAf5`}tJ`g<5 z*>2i#PIGHwUw)Le7_T9q^`ETy>S{Gnef8DV2YRaII5!@ASGH4s;N-|)u47Q-y=>gR<*r8_G2VIawU(VH+_^=3l&?e? zh>Srqc*e@uN4Zop@v)cf_Oe^O zKlO4i5p;H?JJjN3y0_NL_c=asaEHILHuMDtvpMP=a~$Gr>))(ghR(l^&*xj2ZmqR8 zTLpvw+=7IX!c=pijn<-4b36BED|3~+3MNnu*Y+#AOAqQ{%zFp32oHCD3(ll25#Q0> z@pO+TPmvX-1hSe#$w-$F3XDwf#KuyebmIF!Ym-KBw; z!zd1<4O}nw9swo5UOnk|g~zO(dtc>W)}6c5M~pw;n12VoKAE*&ApE2|PKRu}TE@`4 zh>%uSo@uG^oY}zJ@l@AJRkg1KCrzzEQVdf{qriaF8ZBnT48QZ9!n?c{lHYB-d+@%y ze)8`Iq7eVX-#6Amb=+$7{e;bApJVN8df~ys1DMWuE*xwrw`kpSS8%5DOp@1J_u3)C z5P?i`fpZz}#9Xs8ceEG}B!Mb5h~yyp!oVMBa72PyXqqDq#4dh>-ORc1XJhoqyPvto z*!3J=RCpF@?DW_Fwk>`e!@=_yvvIB*Gt39&8xJGJgH!`hr71S=na*#E?}{4{68FI< zLfXnX)purtt=-0LwJRN}Yo!HK1HK1{Qp{n=;3!l&g2;%ep^FgnZL?)g3ZlM9D{YKa z;_Pqya_zfk&1x-Ctni*mu$Bkc|KOgt^b*+$e+?c&G_D{trEUSX97xoy34X23$)-4& z!wMwLaWJ0)a>;|Yc4h_HY%ZH^i51^6*O_|ITN~1AD~Pld-c2G(f#_SbqJmX}wsz9b z@Do5?m{7$D(ncd8F#|_Iq6>4QNN$(LnwTW`yyiB>zVj%%fLo0>*iEk++uz!8&-Ua4 zck;6eNER697<(^*EkTfKwjw=s5NXB6nibfYNP!Vr zqd7%%hS$5&N$!iZ?$ISUiGUxuaK-2poc9*W0M3FP58KI4c|W88A?E!zz5F~o`=;Mz ze!*Dwtw)1T{nU7U{jdM_Z^oYw8XwPn;0zqZ`}MCrzkS!!%Xch)Y1Ma^MjqWV_`2-d zD$w_Q(AT1TF*plW!wQjMY&&CH;Nty&LA5ccHkcb6!eu4AOleS%MOV^l&-~?==pQLk zsI*4D=TYNqVHhhl&yF25_G>Wqur>fYwsG`04~^-MK%%#3+CctfBX7LRr-@zpvyejAAjnoReFus|-xl;;sIdX4 zWh=lvM_?H%vkCSP)Drm-$Kk_f&g$UFa@M1;5nT8VzO!1@5GwxonB#x|_EPPe8lSBp z8BlmK6JfA5Zv4DHq&7!`U|GJ&?#eIB^d=Abpk-0thR2!Lh;tK zM%|-E{jXTdoc4wj%-DYZoOLg5-eckgLKS(!-AX+>FL?T#`bG8JTJNjpEj6sB28l98 zF|U?t(d=5_;&bea?7UTH^*Xb{L*ffKx9~cg*yiqWr`!W>b&h+Ho4em}eeB}TxPI&6 z46#_6x?hGIkZa9$ccUZbS%}?Niv0GOX~sF&p`9 z3hPt)Nh#qgrEBKR8_53&sTq?zM^V%m8x1ECLX*4f59{=TD^pn~ zN-?AxhoTQ}t*2(eLhkz6=HnfF@shhAeInI5#ou_x6VKlrshjEhJeKx;{CZ?QsD11H zW&EV?$M0KRcmn3dMQP*PEw|nUOwI=np(`P`o(RseT5S%89dk_6R8$}!MPapCG)-4+ zs>7jh4o}5G_^g8|g>ynJ%@$!Jv&v?xx6Oh7Pv3o!zJm|j48v<|us z``OrpJd}-`T-a1wUp9Vh{P1x&wJV-wUt`OkG*#!w--*9GKJ>j-59%7 z`NhME6@8yo=?amJ$Lp-vouH2)&CB33&S8rf_c7L@JOn&pkUz)##6%knE~7B-{g#6k z{w+#hnuaEYn)%hVJqGd+in=1BvF#e$qCrq032`1G04^V@ zWSG2BJwMMyeLgQ<&A%g#-K?(Lx%t4gQZBGNkTbql%IeObUzJa=>3>-VN9jrSB71{T zf|_X`@lpLF_DRWtk=f~P3Vp%dB$pE*coY~JJJ|~3F@66n`{zoYO~^@dGy?*v+(S`@ zv796tp`0Y!z}GxbxT%zrWULYWnk)M??;-Y=UOfh*hMbiea8cK7f}9nuu#fal_($4k zsbfR{IHa(y(5dkbNY!oh`$a6Hx9AP7QY*Tz<})GD@jyAd~j$!Ci@^5^q63IHkEKbSKf zQ(r$A!ZqG*UI#YY zv>#T%Q{@zLeUJLkSs*>u)Z8+^v$jsOt=)O?>t{sN*M9JCH4j`3N?Z?}$knqE`Ds+X z9Bg*5#-*F;?yTc=P18bd#p;4TVD+8maG?An>~*mj%e7kMk|?)grU=o!v%5Q`Z9qx2 z#+D{0GjkUUsg$Ld4s^qmhH*efqF6WE`t|LxpFGTr=Re%?i^acs^v&lV-2ST_LtE}o zob$DNZ)YdFEMIx|w2jeSg9R6_w^%Q^^nst;i~)0H8~S<@Y6+;a>I=b{zR(BGgHG<8 z0d3=5s)#he&T7N%TI-as!=);M`Z*r;=|BWhD)re-7IL$zEtI%I=9$8bNJx;aI8$e* zSIpk9+iJb!ch9}`_Rx!KZb&ef@&4`C-Eafy4*E~syu*0a_|W*P@iIT|&8OH-w(T*= z3z-iDI$*801!3fFg5y)8g%kwI#Hq~VvRO{8s0G@fG|Yg+3*8;WR5G3L+>Wxqr#kzD ztyda1v5S7Z@-wn? z3&ge7%U&XSyl_QD#qKRlHzcrB*OS<=<)^HTRWK(zHhFC$d+?KY+5S1GHCbVt`!nL# z2J|finz)r(Wbi)c4QW-dJFyF=2a!?u;&9Xb@IhI)%G2QC7LP~Ngq<#Ns%8bL%*Zmp zMf8&4#LGGe$&DV9T9j-=#Mo0ayAigT8p`jtvhbe!cRuqG!WUI8%@lVGj~94uFxBR=Ow{b=&MSk@0JRS*ra^`(G97CACRg2yDft@{7e&mo`S9+SRma+WBjGC=R>Is8&~jUbB?XDGQWXuzjA}&1c!BsSPbl z*h=*c^#k=Ftd%aulqn5?#kHr@u0+W}ZQGJqL%bo`AR0){P1kLqhH2`|*{4}7pPzFI z?1NJ)D`&bomV2jiIK_x8QxO#$IEvPik0B#*7{^l>!&86*0%a(eYJUo&sj*PhwrOSy zBJjsClu2QNAR|dtQwwGFAI0#C6>FIENK5*G8FkgKzVzx73meYA>(N~Uh3EhC&0KWD zw^qIS^cle)y|H&M`^sZ)BPDdh`RcOErhX%K*OPkJ*H8b^Cgb|MHox+P_Dg!hv+ucJ z_tVvd$KL+nuWuR^&+K|5f-DR}8XYwlz_}Z~LG!Gtc~x9lnsyHY=w7?NRI#`$eoMxJ zx-(S(LsrN{aH_-10RuxaYYk(o(d{C8p5{MIf4mh}y>I+__in5G!hKLCdzflmUU(LB zy(6A7Ru?)6$4}$gow^e?ga?v;nv+*9g%v2yKxWia?XC1Ich%UIJF2vQ!i+?c5l$2G zX{g-ot}~--lxYMR=g;#Z)aKS|fDpd+;>B&3R2l!n_ONLFhb*EyFaN@#HJ2Cet+_h? z6$-rJxz`)j;tV|30_nUc=yzFl=TaW`CK1i?*83<6?Fidyr&cZZRH^dWZ{gWezQ{rc zJlst&&r&j}7>lDmPav?tyLUf4O!zL& zEsVv5Dy~LME7zclyF3%8l|zIpT~_4!C!1E|vx@pLHmSBuoiM5Bq2`nVw%0&6MDR?i z>zx_&I77A?4=f)hTw&c+5r)PfzCiTDv>>~zDDFw)R5Nv{ z0)ySJWi-{IDRpE+Ooa`hSE*CWYzUGg5G0qOKeL-a>=N;dHUw3TQrDyr0W~?*dg&|W zMnq!64c9grZTbR`p=3my4}D2i1j2D#*@*tN7yI|c;B02~3T@1} zt5v5m^wK3K5KOUBVhh^54lrSzbb=j9+s3uJw@jO_wNBl#yQ0EW>15)V+Fb0u`IG-( zug%IEu$%S=x@|D2XBq44o=C;ucboz_FR|mb#Jh zzLa1q0rmujG+Wc$t1tM;&vxHjYdv|_x+~SWJCA+%{z6(^_S{vg(Dd`b2ic_!koPkY zr&@v#)e;$^3Ok~XoI{m?sxX^T>kYe|DDEJoZ0fUOuN|-;U1RdB*ldrw6~e`Mmzym)#QDaQs)l^z~c5z4abQ zc&6MyebZJ0x!(#dtZ*&WY?>dgp`~40tgX{FX)kJTXvle?7^Awz=_H@%ex%51^s40! za-}_CMVJPTEU;(Df_1b@i9Kdd6myfvkItWsC^-2_FrG#Jd;el=H9lgFuV2J=8_Nc} zw|?KxBK(@O=duRl^^Jw~6tuy7M*oggmqAwskP|H#gkH#BSshwssbY?>I%dbqP_{G# zrTvbmsSJ1(6F1^VQ|%YMWvt!(?p-(UhQYU)|GIFzk$?CV{)GeZQs{Z70T*lWJO`qp zU2q}hC~TqQLdQFfj~&Pa)WXE;QhJmTlqjoaE?h&B+)CLam)&m<*oEC*DPv+a_>8x+ z#c*GiXlFoe*jN<_oW-4sP%zbv0zQhdv=x)!#qdd^44QOO)P%9W8qvpc->A|pXMgax z5mlEJu6XC{&#`)bW3jIX^XThK=xaS|dswMInA;5>?x!evpa7Gv(i5`P*4NId6?1A> zi~E~yuDfg9T*j!rTYE)q z91l8#gS2e`LGjzG@2P&YT2%Y--odM@xkFg$Tt4QrTB;pqa&|hbREJ&%E-TP*6sAI4 z5JhJ)N(J&2BUI*M1USC*50(+sEPX`(ztG2x4%UQ!6pVs|Ux?I9@l}Xp*Po<+ z>H5dt*naNJI=x}`hNsrG`SgbAtG4asr?X|o12m-zdmnl6;rsZP3s3C%$zN~f4U!oQ z*)2uASmNg2ANrO375g871R}~TjO=;q>()P`2%Typo>yO2|Evm?-v5ff!vDyH4!q=0 zrREHM6-w6SsSDX{Q*3+*6bOBBEI!agjo>OGTPOfm9k2*psA#ZMa%OKpm>X#+81@d7 zBmiPEmPY|87y!})AtHe;WP(VNu(p*%2K+UnPcjVE#0=xA7vY^Xy=Z)g{rCl_oabKT zoxILi#eP)SSNJu1&_ECh^8<37=0~k^S#U;|71*!0&anzCh|p1$(#%Ayi)w~d)hhF< z9-0|8UsV-rP}-Hna13B+nS-TJCDsYm)_TIO%BfUu02>#Q&kX!h91Sz1t&@sG5Jw{B zc`42-Sq3)^ro1!)7qinIWBO?*q&yMRWcG;<{P>L1PFNPbgv2cBa;=J6moosc zRCWR3hB&AccGS7DC{uNQJHNSblV~ff<+oiUPTKgx{7dxAy@=&duDf12H@LW3`AbEu z%?#-1HrGnQf?)R4tSx8b4ZuLH?V}Iu2krbdJFBv{+wZqOX;O@!KC~UuO;aE&OYz8V|lWr(({F=r+edaYpe1d_vD$63#-iICC7ZW!KHPgGZ|1A@th%wr zU*mRZ^HGJy-u%;_F5AQ&Vy79qjIVCGj2$P(!%jZ+WOj>=T9=<=w8jI*UVSS*ll@Nc zWvB&Q0X@?moMQ75%M_36KAU*Ja-Z@>1?#O~i%~Wzi~lfpSh5>*w-?J8Aw!UdOZ7uYGwXyI^m!uPRYogZI?vbo2wXi|izwsXgUcc`;e!#x|h zusUUV(5E<;)cD;2*yV07--K@0TS5wg|KwDAP}$0Rl@nesdl; zqqcncruA>IwZ`=~@^|x%L(e?(+AjvRweis3jMp~4{Hx_Ve>C^cuWn=~rJf_+D7)Bn zwL!fB{cKUr4%%nmqfALp$xabdWIzIy#E4hiGg!e+saRRD8nqpBrtMO^X>ZmmBz0?W zh1WZCR|CwaQ?%9ElL)`)8c+iZ4wPjuO^RqO43=4jx>Cxml|`{;*Pubb!rX8A67) zGK3_-Nbrk5#wz7Xg?pSHXGQIcHSC5Ob_FHJDqt1bkQ}%WjaY=^(rVWrR&4SME0_!5 zs;Ki>ss@i_#c$as6&V3yh(ig>K3? zdMcrfgeploiY1b?BYB7PX?^~G2s0lZ{F`_Uv9}hbL%Al{Mg~tcDkWKKb$hLHEDs&S zj`^y`<6mN_owpRteYLq_2_CLAvt|O-6eOUUn>(vpm18T8ZS||#jOEknYwgReFr`dS z0O=$=Se7@VR1$L))u&VihIdq&3UgZq7!YA#W|92_&!80eB5esw$xJsRbWtd*wvO#c zX71a*qw9{o=g!;m=cPHFuW|lq5pA4nGE=5*fV!L!q=Q4iiHLlJMzKYFYCmN3#ZyNt? zJi)%i8ghuz<#Ra$6~OGnOD^B@oBw(;_OHiZ`cG@Wci?R8dF*$cGJa}Y%x+~7_Dy!% z{+rl0*yr*08z^Fb8Gr9)|Gd8Ae0g8bMc0VwSFZo{6&LE7eM9G^ zUwGd*i7n>yzP94hGf{{~vB)*5E}lgWsuKM?#{Or^yAC89qOgFf#GjRSgQ~&@apLXSYuse@pUFuDKM`QM>lJhs520u5@e$gOw1BWP9I-xPkI> zn5ifrp>!0jdm{iyA*6QRlHV_;7xuAhw;nuLS_kRTpP+mjwtg+cD6Rb78a&%KbJp~y zXH2QDZ+Uv=)S1y4EIN}-oylg*oH3ISdn^z;WkwAaEI&8RH?8fthTIIcdd9jLn`Vg0 z8PjL*nbTObp|>H{Ab5kPfzO!M&@gR=I0U_NBLT9VX&EuWSGrE)2&O7**Io!062KH- zJrbC`O@fw~At+QmTgs6(_UTx@^1TOMJbT-p{vT;?0$){i?T??m&$;K$_ntfFX1qyA zLWmGZ2qBDD#0-GR1u1A45+?>l1LB010wP4D3W}nlPa>r}t5j1A0ih+})LNg4MV`f> zKozZZqP7l&+>_sT?Q=3A@BM!N&p#S+Z?f+`dsutzHGJ1v>|(vC`swMHU;O_<%RB+) z18GB%m*D$f`0t%Gns^E8`a%sVC)F05bnk9Bped@3%xT?@#FJ;8%C~Be9EqG2$us0_ zCNEeQPyLnS1z2ebQt>O>3ovEUs~t)^6c18E5cpHQgSP{pM(?um&if3-6?VZ>5)C~g z*<<#h_Ko%ydn+V!tKGGi@wiI@Ffb;(Q5N#Xwh<~Aw;unE;27`FEiWv6#K8l=7qQ! z*zH9;uvaRKhdl@hcAGK5?q9o7`oskbOQVU2B}XeR4slVkvg}ab_+Wjx{21?v5!TO$ zSQR##x5!PFS752QBwiW{sVBTzmR6@VAQi&~ZL4P0v>>1rtPzzYSHe08HMi%T38I_cx7gntEyN&8u&@vvO54 zyN@$r$rLiWBIkhy{gEdElMjZ)#ocb5X#{@yz zjkh+-aa_1XVEQzC5<5)&ij+M}1HUtW;+&Y?B;!&oVu%^-=8Lq|{E60we!hD84Rapa zR9$(;6;<^UnpRxDQW;yY>86z>KfQ3>@&zlJDrd~Oe(rjjTR-2oYF*2l5t_mu@B(>m zhF^o(oD^|h2>K7H)?Bu2ZsPsLEz6XDt$8Z+>$TurZP3f3&}l=`*tq7fRBeD6Gvbx} zRo!wwMP~&32r5gs!*&p(uoMutH{k2SsYFr^E-3+W&MhE=Yk^@JX4k&=;5~aDOmj5; ze*Gl1y`44do$c?E)ojy$UiRorR23V6F-*`qm2%-*xnz&BrO|R4w+u-*c|ZZ@SV}Cg zUzKfR-yR`AOl=n^dF3QP5u}pjq?SQL#>kM zl1fE=?1aB68@+j@i(Zi2U9ItKXQ0|T)b6Ra0RUumNS3=?4Vbg!a`_wxmcQ3ylZpxR zSRsH40F8^+!thcA`VhibL3t@jW7#=uXLj+!J7?Wi(V~Btqh~BqXHP0K2iVT`b2^hAfz7RVB+cr zL6|ZO5qx**4u18<{cVrkzkl!M-MiamZQn1p?S0|NU+%s26H3YPqkEkzU&x=2S2w6z z)#m`G_S&6a#2ue+b&F`T|K(nva}rlP*>Z{U?Z~14E0e|ep{V-nbg{ECeWCuxo4)+= zCN_e5^lNI_Y&N@AzcQ`&WmgyU9ax<#?f!xTOZ5K)RzCj_E*78l&_Y&`yc{<90-5`P zE;{XJC;V)spRMpS4gOG|YuE{iwMr}@u{ieHCs;z6`J+r4Uj{T9`iDNDyk_YuEDd!RA-mTC9DLT+wwKbJVhzayYVqh;eDd?X% zG{PdO1fge!RK zCWRD)2x;yH$5!nv;P@R(b7&4vz}}$Bo(81V5)qc<5++P3#fbwoNp>7VNL^wGq40!| zLMyDQ0M+GE_Aw#Az&GN{RrV#9>c<;*vS}##m`jc$_F?C(o!5$Rz;+|bi@nO95__-; zcOv5Hus-Ra3H89@1fYZMY+yJ?bqZ3zx{LTTK{uHu-N0%Hx?wI$K9IUCZC)3X`q0g} z?H5TfKoi|CKR!_4^7ghCV>qWh=qA@}PY-bV`}E$YJ<)+Nou6wja&moc6u6FBXaYXbwhunu9T( zM%+Pt=fKaMgGqlBcj$r+UlYGTlR~rXQIEe~0cjdRSw7z2Ss=4wc`Jf=@#S|nT4aBB zB%%=5V5h;&^(uY{`GJCp$PAbrBdt7J7Gxyc+Re>dTMLZNEsWH~QzP_`v1CNN?-&^>6M8_?^*zS6k}&`ls=W@nHNmvw!-48p=Fx|Fnn3 z_%qiqTd@|kOFW+ zj;u>ds!p@YT+Zbul-hUqW~!Om^^WAvh5w7-g$5a zLon3abf1ZM5J7K4OnTcd=#Ale0w3Ft`%mqm(DSrkBuz2sf#LpRQ@sA()`H&7>Aw#T zpaF}PM> z7s<8b;sdlD`VcOva5VON#-}YpKe=AXp`ZT0*TMyo-5(a8IzpurHAUlRH`B$m1bMi*Q4DC1Nq? z(x%aqF+8uYO}892UEo155s)0Dbwj!mflLsu*zpZ(kIuUCj;mL#`skZ>_SB2V)=VEZ z>h_Iu|FEYW0Fh^>&Kp~M`^=lKy|#Y)yJu^aR@Suj!MkprG%mYf%+!X*uiW+w@j%R# ztJ&DoQP>~#Km{Nq;5a!xlCa)r%geEad-vx&?H5g#aFYRHfDa^c*eyAE;sX$H4Vmnm z{yVB{Mtgcd<3KiOpz(nI?JXa5kEf#+Ns6+d@lpGgxomMRYs_Uuxfd8ufuPg(IhJGH zpF7a#lBC=?(ir6Xz}XT5S$rV!nX|zIdp~JVY0sRRyFc481u7?QXPF)<^H^Syu?8kp z%hF)1**3`4IZ~liDlLeQNps}-swA!SU@0#xjSZ-bXmLjvc@vmcHn>VX5j!2@u|d9s zl$&cy3l2?$qtT3TfkRHXEH*+0k#l4`v`YMdP;Fdz0eR2d5aRvb?T_nUS$&5u zZTRH_f_FfdR)8nRnvD^3vv`=0!-|73ufdYAt^!{zXltB7Tki7>-Q$-krk%HMF?M4tjc6Zj=gfn7Vl;nm(}G6-CZ!TTrSn&*<|2>g0BP|H zk`uS%h2CJb8be4M%c<5pPFb~C-1J}gYpn~0wET=}DSRS)8rW?qEfMvoPI+jQ!4MU- zXC(q*m&;B*tYQ&)38B9yGpkSu!Oy_dV0ea|R4QWS`2`kSm<8owx-U&XO4)Fr_^q(} z7-=@-Wwud!yQG?93npH?Vg5&xUWALU>*P`WhkyL{k^TB#S!_!)GVdsV{pF)KuCB_z zqq6p%B{%CI>Vpx(`!aKW@^_{@sJG}lfB*8Tr?93ixNimv{(;r>sU5X2)srOe!}?qN zX;yFdZlSHfcnbQgHum<Fvi{ZZ=wSTaYVvtkeJb*{bDi^Mv{*@8cDKM9g{ezU*74uYLk$J{)Xt1ztX`>&U(G4)T9*%zf+a zHg+JnH2cWg+le-D76%J@!|oC;F-{8ya5?G3Q zPl<_PhP{~b)Qyf0NUi?b(gkrl=JW+Zil<8Qwt9h3lssvUlmxPPPU6SfS9~==-alz9)Ta^qq&k z!v%e$HR@ZhP!3pr+tc?YYy-|*3Q!TT?%Py~AQ%ipCvH<(n`V4wnz+|V9<+8`!gBGM z0f;ADIfyMD#2N>&*dV42ViNs7sQbe`pBMR#guGAo8S8;X(l@3Kk`8(QkKw3krh#;5 z$PqXbd4je`j-d7}_ybWFxGH-h2!BVvGjUG;xHDVOe*uhQVf24EowcU3mFaA8I!mOp z+tS10aTuK`Eu80>YDX1hSb8FOEt{4a?0q8E!+2gf)=aOq6caJUM!?qMn>XBS9&$_NJEV*h>%ReyoRqVr|YxqPwG!wTQUqL=71~Apolpy(1u!$)U6C9wV1mOeZ((WG)IF<>tP^dHn zm(uyd0uSHla_MW>9qo@l-W?qH@LFKSV*qk1EO+bAa-Ue&-oEZ^5&N{@&wKS7^jmk( zSrB$UaqflP4(Hw>){uFy^?SR%X>-Bf(M_FbFXT{h?uBgGQ4=)IW)x>L5J~BkcDwr) z=icahl9XZYJ?X_3-?=tKqi;I*M&FZ68JA=aZ+d?j*RW5fqOH;QWY{Om1cj4b%jrk1 zwC+)h^I-H%G;8!d89V{!ffGB7WJLRq8~g#ey?Xk-L?Zu|XAfh$du)b87i;v(D7Mn< z6MRJa2(c2LJ)D2kBau_Pb8V^BJ+@Ra*5G;9LGTM>tku!pu|~7{uY<_TCC)%hoPjD- z`7-Q#QLMw|Sy952;Ba-HFA?7;`i`SL_yYKLs7)@1SOtq$+t~7O|8+C^C*E!JKS{b0 z{inr!jCh@v$)rqnZKm;*zWhf#$3lB)JZBORXL!yi@tl9QjXD2whzA+ZnJisz&LacQ zxg{&@eDe@I$moAEjw$w29*qY`V=H=Rf%U&xA5FQ-%)Zq_^D0DJmKH2v7e>>2dq&%H z*5}ZCX^i7Sv%LpN z(}^qlc|P-`_^o|pBk&n~!EZ)+q$1K~@u5M*Rd2UCqY>F#@0U(MX62fvArf{hfYYxL zdA|Y+;M5940!(xIVK#zv*j=ncz!kw-$aP@=6=Cx$5df(Gsz(5ZXfPHZ0TXk}2y)R( zna`S=DF<4!ygs={dFh|{+I;p1WkXX2Z`RizCqpM9?nO+fz{dg}SG6&%1-Wc8yn&!> zt#UaeTPsXken*|x>2wF>gcQ*tF-qO-MkptEAZ1zX%K~92FvKHD6-O~`M!*ux;WSSC zCnClv%I?-Lwrv8l+sn4;vmK5_Umg4%-@=srN0O(Nqr~awd#4cQMq~SW8y&AOOUB>w73>y7U>KQ-rQT8J~YV=QM&**=W!9SQA z50Jf*-1kmAsaFub-|SoMxCC+{aXNc1rxRm%1w0Y)w`ey(@Qq8*CviGC8qhp~!{7q% zS!+7S##kr!jFosD>(7@#KVqyRrlBw%67iukC}Pv$RP42gVymU-Iv`t2lr5Q&AUF5M zhGz6e@5=jn!gg_XD3>(lX9V_2nh+nz>zSk8nWeC;&gY!SJwbVgkPrkZLJ{rgvo=~h z$hu`@GothW04j8wEe)F`!B|BiJtGfM&g1(w&zbY$etpv#N_!Fc#bB1Ec+ToNfbxp; zTiA_|&lLpj1Bnm7*)8G0DnP@vw5pgh29AlkClZ{~;mlYpGaOR#@}r4JZd!V+Khqlu zc`TROK@sCiPKj95KEokwkLhg`<_A(oVHGPIPDoSnf_#kzXv0?k2o?R36;PNn|Cs(w zZUzRX=(2g+-(~kc{qP-Tz_W;Y*2kP}*UPsQylX@qw;#d~u%9IB91uJXGBWWvl&;*aSAN`-mdC40Be-))yH1_7VV3y zJFzww8||f9wqD#X8t#`1`&#D2-hPI)M9xFBr~a{)IQ!_`Cf5G%{dyPee~b2rOtfJ^fOzhcFx|*bGhw7~-w3yez{2DFiWIm;gqXIxS$X+ot@-7bvwiTs zf!Ux03*5fUv%Kcj{j8`~Z{~NfFKU-Bmot->{cibJdgTr*5gsb`9J+{4x;GwP=w$O` z2ACuA-^rMQBLyiWp6GDi-fL3)6pYXOuzyqk=7a{yDOk-fma2#S5#w3u#oY6FhPs1i^rdtT! zTl+qR@^8Q7AIP_WXnvlUy=((t7rH&!uxQ!VT`3rspDnUn5Q(toeg zkR+~~>IGf4puJ&pf&@mkn-cPFL09}z%OJu?SKq!f|hh&EN)8T*i5BukiyJYH--aMa=F#jrk^vv8>kEFB&Axa@O8}~Ez6stHU zp6l_jK-ES^3r6I=K%`yJKb$&z#Ml+cZek=}=zK`u6@4)Dw0u3~IHCJh;3tIaIFM)^`nQSC z;GlKRgQ0Wq4Z=Pz^lxx1=x;UP0-zU8W=$y=>SseYuo)yb8huad#&Si3-A!+c_QzTn zeUsd1^bI*v^i4h{DcZ-H-#u<|MvcDfgx)DUiZB|c3W2K8Q0YSpYN|`hjMl5Zh6SK~Pr%jSlp_r`B@Ou_ z117tL%qUQ~WW%S-q(t@gt`Jf7=Me#z1ROqYHGU6GD zjKvu%Gg>naW*pA2Wn@_LD#Jal)1sUzfizA*(o%~rQCOH(;K)k2Y#LkziIfnh5Jz&E zg7!+tOoYi4k&(!Bni@XSJP3Ja+9=a!3g44(ndavqvA;;(A7=O|^YxeQdZ4{^%QKfx ze`56(y++=*sB!KcGdJiTKlOBdZ5@C9j^(b`K17t74N_jwv19tTCzc>_)3%yhve_2) z^qYS=@-`2!VftGj4A8(n@S{zF28xJwVb>)44(xAfg1$a%3#ITLyE$mjhW5F__5iJb z`1EXp*Abr{>zKcAsphfB%d|K#W9EmP*pZ%_1&tfe5(H?m*(4P9X9-61a z(Iw@>PC=}?!N-ubE(;()A1aFd3T#_EtlMPz^p|OgZZ)lz3+}H!xqM3=sVK4HT4Z%iCnc-a_NJQQBd0XrP zvtOcx*U=8SLeS0_?_$`Mgap_1eQ!h4#(VB~tEDV+yaO=atbzZx@m4#GwScdIIr_H7 zIOc&4kiQn=fZ>Md<}c#Dc8L4^c$>}HtNjYPS&#O}BZBsq80}48Y8X_L7mQfl-Md14 zgMP0Tdq{m-K_i5XrQ2HAdo-hf9ZDY@3U1av`my3u!G~NNedzplqwd(`fa2Wv5@|4Kj>9xS@Is$=%6# zOJA-i*8Wj)uow^v-2D`DYNApp{uCLDMQ&b}ufMlne^)ML$G30SvmZ)c$)9)#_hfgD zl{9H_{GzZwsHId{tZ;JyIP13mIMGUahTq?vXeC39)Y^l=R7arJ>vn10)c*Na%z=tT z1ZLJ1T>B!Xi!OXK^@^{I%sxHoSA_YeCk4y4ZM{iYSgHPonTX}=JwKS4Sbn(Y>`Mlp zGB99d{~03otyWUaeG_(Q%|vwd*mHzWQrLfl@34b1RM9SzpOFPj&mpx2O^Ck5{cZG( z`xt$L6ToiQ6Do-RJ%`e6@rjGWwqao=n&btP5c? zI@>~NeZ8vUd>PLn|BvyUI)T>{o>&5`v&ml5BJiW<5FW*NP8~1^;uCNJaaQNg==&7f z1K6gJQ`&q6Y*?@%kPR!X2i=DwM(cxi$a!b>OMX9MNI-TW6}^C(OQ~>v;=q$3+vHW) z9_4j$=7$Q}sp_%qk#CI0QvLnxA4YB)!M8DJvacJqLvr+yr!$snP3h(+VocfSzYlh> zqpF9e*wMe_wfDsiVh-o{sXMAn`k^~i&WQB-snOQxo3M-}H1-31u#9e6dxW?fz4uxB zULIE}KC?)wFwY9v??yS!wPlK&Bx1go(tHmr!j{olAuLc`-_{t9(Kp!!Mc>ffHM4KB zCHWAO-~Y}OeG41E=(}UGX7mj#Q2*9FeG{D-efRQaVW;vt&ewPExO?<2>f7C~HDj=0 z$!CC-bW5n{ZLO-aHP|{(yRpk=vDrK^FiI}~kS->YZb3-^R|9_&=uFD*AQEm3hh!$7 zDO`B_t+V&Ey}jJhxgovrR>CVD_zg%9UAzms*>@r*{IvL>aAp{Vojg{LHI^YYxjh~& z3*k2IShicbSxM{Ddgc!NpGNgsY zcto^4l4F*J_~Va0Us^eOxA+crhnO^+@-aqcMT=%-`lS)sv$Bx+g*!5bHbfA*lHXtvwG&F; z6cBfU8kd~>Z_-19oO0m|VH{8?%0(8AjFZbtBOckB$t=N4URp8^e;R_supX}55x?sX z&G)^wYRs5buiy96{S*8Vx5{7jI}{IgwzjtBt{0lFcxFO=QONSD*E^_U^053to$vm~ z7B=EP9^3L5FIac<{wY)LKf3PjBll09aG^G`dgjo`#W&8nC^X@hR~>nqZU57oZxZc* z$66xTi+tn`%v5JJqINL1<<&FKz_fY^NvYfnrLv-OtRW!w;!V=9$z(~n7(Nhm8tNXX zuX_TRPxmJHFZZG#Iti9>dL}+WzBuYv?MD3T8s9}WE z$FkS}8h=>O5b)7Gvy=U)w(ywhL}A^nbpzX{*H5S)nbJOdT+D&VmHcGI>`g`E3xh+h zd}JXdSiAGi;nzIa$Vc6wmegJ|GE#Z>4b|4pR`43KasP^V2U)kl6 z?k4ZuX*1)xwET`Ke7Vq&s`)fzk{PPB4rG7^8<^dJ6aS~sCnYn#(4UfbP6QMrkKP!l z@rDvZ_nW}YAVI3YIny&u4n7yP2&?16l+$tcb(|fKv$x``G0w8$NZ-Uy=dcqw>~IbP z5Ln5KkI5^-pN{oQQQ0eQz}THt!#KAj}G7IeFX&f$H{W3v8X}yVd9$*DmfKc@Rtr zQwJ1S`u2S=}($cJPX^bYk9 z^i{;+rXer*F60IGN<}aVJ7w9TKEyCW+4%rMn5kYC(uS0LX++(ATv8E5B7Sg1qjfcM&5aMNm@q!V|8_pUG$^pSQMAbVSfmf zZ+$M0{Q<9~0kAYY2mOoa-rnb^Ry-!Z81dg!fudC270tOf_fYP=qi>tG>h{Z*scJ_4 z6H}%KpwLsw@ z)4kc{; z@O*ydyk*PgU9)P%{9)r4!y&00yXmT{Zn~x6%3E3Wn6f2f%F4#j8a@F(axqrZLzRpM zTgO^2v7%ljV$Tt{?!2o`qy$hA`)hOG7ekB`ZI_6cr~MNCG9;TY6VH^FiigX^HJ9G{ zvjeN2UNJ#Gv?Ox<+>MKtFKNWHBpqw`hUFv42gxH-x>I5sVYEF+)M&rc;Rt9O?DB)w z4H6C&?q160GBHC@;lbT>uG<&07%NH){U8oJ8uvw7nHqUp;V|a*WwV zj=WL-p#mOVk`2DYGmqjtBQb+?QGAfcyVfZ(9>irT^Cml*>zobFCODUmJ5hMj=im=` zdh#Hc*?~l8I*3%GxbT3P5h2g~`Q6;atBYH%X4lS~`TNB!*O&vTJQle*dgT=5U_DE2(#=f*)=Y&mGLSRzCn5s*Q# zNdV?glEnJTEQpR1LW&VvAy?;)n?9^$+UQtp^t6(p)5i_Cd3?#R@fGDIKkya2%4h7;2UGf9U`N~L#7nJ zyA+FSHoIM3qbN@K0Wb6yj;s$xZ3@hQE*`pYD6~D79nZ{k{Qj!3*DJ{C1c%aL=w8SP zU4*L)Z~8zTu)hbD`*H za(J8FZ5k=e_P3*bGxguG%pB8WM*9qE{|G`ixFMFA`62#+x|TnvZDQQ0sJAx3-AvnW2SutJ5Uz;&*$Jr>F>{6joOm5N2G;RuxuL)IaD;BN~16n|Hc zf&j^8PsgVtAMs2(tFp5^_}r02mD?L3YB(q%@nsHH?O=l(%_B>Ci z##QVw{hI%|UL$|{NzCV@IBPT?oHe{Zg8345NhRzOXwRTcsylb!1<8ty71RMBg`f^Q zE3~r|0=Ka}FeVUnWD#{ZC~b8FnX7?15!7L4pW^R!MG>y_i9{S$Rt1!*l^OgN^$dwJ zM9r*05PDFEhO=U6h4dCAV?|Pk6hxxt3{a+Brji+yq8O#DK?x;dtIYz|l%)7PWRRW9 zNDpDCP^awtz5c=Y%)v$av6tDW`{gaazxNMoI~y(Sm_O7ws&k6@=lAS4VJlR^R$#xI zjKAX*MPyaQ3(04|W6Ds-wWtK^z|h^aZj!^=umD3x4&=X#|oY6)^-O$Tlhg%N-W~nXd#dK;O_dsW0?hDEeNF_tp5L z6E$OImfPR8e`^2UuAmyR)gH0)a_hU+Pp#ium3+!kAF=Y0{LlPv{0FW?c_C_>A|eMR z%v6y)889lO(UOo{hfA`xLBU@XR4p|8+7;tz%e0dfB7q=mPUJ!%Nyvamh;v+8W+wn| zwjC)*RzonqL0+Cjxf^{x|KSlnkLPXLp8P7=y+*xf7CtS^i)a|N+AuHhdTc}*?us_p zB}0gHDv;XZms8=|{D5rtM!khzl%*4Cv{fan43`M zD~H8#Gz%E&O_5M_!lO~&c1Q8i?~)%!ccgrB_S>VyJFJiGd}2fPj@X7Lc0Q(BUT@Q1 z|Kbb%rIRPwxX(XlBRVfVx_{@}Z|~fH)Sxv;5>($0>qb64CPC^I_5RYNo8z@1OTGVB z;LTxw&3YBfqB2WDZB+Sibs{pQsj4$t55XL1#+#ttTBHn5{YGbt^PuxEl~}md-|Xl9 zMgR{jsR5TvC>ekPNDMwXhth_CRs|r%oIB8E0wb%bMU24QIr#)x4Pu65qJ&C^inyEq z_K|Og_Q|Q8_dKNS@IAEd*Ydmi$us%{cHk_^@P4FcfBK+Y-??Msr|ct|3;alQpCkLk ztuII(pvh_-!D`Pz|6xC`^Edcc_&4}F{1&@Eig79__?r?(-DK$G0g$kyUIv~$3wWBD zFh$OiNjv3gm`cKX{IcBOqpONQ-$wpFBSWk(prgtFit&kRd~`2mBPRu-7!hD=0dZ3{ z=WIYWfYBJt96C!mbB0B`B<9kK0Ig}&f3s=R_RrbVY$ThFq?v!vFa6v0O`o#;>|eSI z5@9zcoO~lfZtCkBfujSr@42~Ny3dC7|C>K$8P3lZvQKra60*%qjr`Gk2s~MPyS`}4 z9~ACy&w35*ZKC}eqkW@cqeAWhHYYZ~)8jqDcm$3~v;}8_+%(g$eKEx10M8fd#g0M$ zLY{5NSTqhp#+nJ6CHZN{?>NZia`udZ`o$PV3wi7z(eGvOVMTXC9up$jw{6*Zl8fw| z5XtBpUtALN6!@1sWb(_=r1UE13hM@F2NVqzW=OA}n{WcG6PB_PQ4KJ315@P#wHj3X z0m@!c88n7W`K2lnqj$&spTB_0TvulPhSKR7S;NO~9q3y0#!JUPdGk*{S-T0jl5G#J zklW`E-l2c3&uMb3N&V0NL}^J6Qa}w#wtJ1*XnSeQREs77zfJ&_^QrM6gR+n+%}ePS z?b+2nUrLocFi7>1S|N8Kh%+NY_1M$GiJ%%DkU)5m$a8^|c0lD&8Wn-BgBD2Q2*obI zoS_(Q7o0*;RHZlJ%jrIbcC!g%fJ2ezMI>>I{fMvPbNQY-oo+@Y@J(q-;--$ zevrqiL-mK>I5j@`nL7Nh-~MUO-R;R`30CyLFCW?bvs>=cA({5pHt?bjQXa3yMJ7*l zOn2PmxC`bbi^HLMs8)c7)U%;p8OA|TT$Jh^ScwgT18E-BE6P6D@k`_gM8ZwMWr~op zff1YSsrnHuMizTqKycA>U+!=eRO~K8!9Tj8;~KsE;5StR)YR%zzZe*=$&}`YqpQ z;sLT#KxD=7H4^Id0%J!IF4m|`2R!Gzze9!!D$fye)2QC1H=V5_8EF$}y2A1jD^0_DhGjoO$@4%Ge8`FAIO#L0@CT`0-p40{t0ow_S+5$oty zWeEEUSqA*jVuJe+DbBJT@R_PCI~5i#Kfn6a7axE5=~u}BFSV}Ouzn3MO1`1qb>Dr> zL1ekRf&G--ps&_9B?Izk*hZx@`t|zX^|KJSLBqyw-U*_Vr~`^&?Rt6WYd=hZbKm=PvyrWm4yvmqJtkNmkJ__>M&%FeP%F=*l>{BS8NRGr4sm|J#e34bVTNx*9_ z*ep_0%|XIf3a_BT&E(f6X$Dd+%772Snpmlr1(L#(Syz~m3sAc`7b?tH_Cs0DjGB={ zFC2H@eU+>-@4}3%k_)HJOmDt8st&kR|CchlWkI#;eqZyE&O*8@5;NJh|Gxd6JxL(z zF@A%VC_gPKdm=yWMq3MvXAzO0wiWy8Pm?STuX0XyT1YeIl>oxRp+0|d+8JN9Y$Bxa z760|?$!2oeeMkByr#03PYdQ&g<(6)WSGtj$u-fKkWv+`|KXJ(eTrA{5wQo@n(EX6> zmoC26#jbKKb#V*q-&AbvCKLuxBwH2Ag5i`%aeN-0n$zu);tq9}uVKX_i%Lj;rn*js zk)T_pC_<$nKXA@|C>z|GJN9?SZ*Z#r@x-3jPVPsgns)v8#oGZ6y;0w!hyRJ;V$F7% z{N@7b;dnz~Wln0Av~>V$NM~CmmM#q#Af?M;U*$#&ZwT=j7QQ3Mt`D-BAR8TIgM(Dv zImm-#uwG_o*V!4$3yrq3!FCq3dva5GUGW9%f(tx(If-1!1HL092|7C`MM(j{b_|OK zSw4h#+>?X~gd=j_hj5HS!A#bd{S3f0ZLr&N$j;E9a&r_igUiQWd+XSg_0Ml3W^~nW z^+^xk_3q{uS{IDtofjX|kL`I`zIEL%9-6gqWGv%q;z(DQU6?VE)vrA=$Tj+d8#kA_ z9(}ZB{gMS(8k`S-frejEC5$!J%7gQ90DBvhmc`2^J7+nsbIPNfY#@Q`m=l{e-ODca zvJuFW>t%{Jh|CfwOI+oX>~VX--e_0g<2e|Qz|Lj&*?pm~OS3u>Zm&HQ=t10p!NLEB zpbaR{Nd10N93iX{UeQ_^V7naDIU{wUtQ_;Eqpq|J|H)tV>)ZcwkCOkV`XQS)rZSZu z=uF71lM-8!waZQUX*BLZ4UDubo)xZk#j$6u3?HLfey#95`lLX@8;+vzttjFLNVXyD z5wyREWC$bg1(m!t6Jg*0(_KI)`su3Lo_z1kAHIM0sU?e_*|_q~jrymR&t9Q_s02iH z!8i0yDgu1=>LYKz{g!@HFYOg#Oc7~*e1c1^WE(+WQ6Dn+G25dmyF*>C^6OPrqq5N| zy8wmP)fAO43$6?D>$)lMf*|T(NZzS#hla$$L|&q(ouDi8ACs<7$^wVmxV%Uo5s(_> zVuR!n3d%coEW7I$`gC5t{fTi;+^FCG*g-Z=Z~DbmV@9V}vE96WG%C(i?_ZlcVc)$p zb}sTTYz8g3aEp}0GkK&MCj75aZ&p@PF$1W>+`&Bwmp4dI2m-iw>whIDh3JqCmjDDF zfWfmPg22UUY6|Mr?b5qWe5muH(W8I=)T8&Ue?XbE<)v>w*N>xw6652xymaz~!yV5e zr7zLswOyx_3XDTT4xJ2#Cl&P><*Fo(YaB?8u`X5&pcdY_fTe~q7_|GVSp)1EelK?? zG_MtGn?_fP;Z@KDQetK$hZmSGf7osMTqQ`au2krGZ(lck`rW9j_u^U%EV=F7U~?|w zgO*N$Kp}7=%4m!S{=VlOm3G)n564k$t=f(N;{dOce66S$q=3-ubZr@mBw27!>FN~h zMWwVeAgY2=RL|w0td@4>-`qUDef%f?)W?B`?b6r$?fWm2-zN1J^*EqtI?TU#k z#-iKzojJ~iwIVqzdoRk<*{E;pSG~DbUzN@0vgP`*PW=b{HIJUpn=%+87Cy$h6=4qd zDz&iOd=j56XDV9Ii-nRhl|ayoyH~@Hpp3VfD)46cnQ~3=^5APhIpkxiFW>j7uiMB& zwPjyb>7P8lv9L!X_nck>9pdt3n&!EX3+>J||b5T332s(t_9`pJA5{mjovw~h% zIFYH?Q__&6SJo8f^QYOZ2^ZYy*e;L>g;7Res|Irx$;XApfiOxcqlW+Sx9$tS`waF( znrY3pmu(1~qkk#cb}0)8f4?aMz=A2m!D~tfd^s!Mr8n#wl89e0V8ZZu%~hN9CthOj zw@sX$kyAEh>J8=W3{Q0apqJiwxov%*{x@t{@~84RcHk+;2H*AT#o0&@U8sI3EG>+O z*$n4AC$9pdt_itZnp|m9D=o15AC4D+JS|@EoRmOiTuX>7QwO!fnp&)lhcK%85LSd_ zPGKi`EkvnKkcdGZbYQeol!l!wJl!NR9I4`oLULg^5HZLWAAgz+9=~(EUhu?cM|Ltz z^`BXq&K^0#BKjBKCtpj|^VxegsJ#U|EBUovuUR(GS*pi31^j+;MqK6fSHtc;!e0qBEEMgXq0N@5)p*HRAIiR5`b_$_BqP+Z0(}BLix0sb zUBPN#iI!`IX2vrUnHw`(G7n}R&a@V1vP=X^Mm?59Bt7gm4k_vVn4*|7?jb0c*j)2$ zVA_!Hx_?PW7%a`OU6CG#pcbqK4oeP1Jc`?S>6Z)EUo`nY>c+2MxMQJye0PbnWVe1C zp|of8!+O(wKVv2On|I#B?q?+s-gW)Il7HT{iw|T&U>A|eE<$;d473BqS zFr{D2mSKA#JB?v`nF&)m*=YzsoaGN2eVf>Gv6sT8^Qd9}f!l@JlC5Z1Y(QqH2WiYD z2ljoJT7Jmda(x!T+J~x$@R^paV9amRZE7j)I7D|O$A+Ig0 zEgWg7t!5)@L&l|pV~U)3Mp+hd0SQGr*mGH3S^V-W zmL*MhvqCtq(&`UqvTd1cQzk+;G7-7~V5^36!83gh$LKURI+GPhdvMoKS1vbh_{=mp zBTLR83on=+$*^F8I3eBP94FztsdxiC0Y`)Ult9dmlS%Ihkq(5s^cJelO%=tWlWS{I z!b3*H3v-Hmu54HOxLjB9s0rgo2KMXGKl1k{Kh?7Cf`MZTf_B^exVgrJF1=yZ-D}4r zKjOtQjEP+GgI?OeCt^}uYKV`?%$PB9-bh|Gl8vk>FSlgZXrUMwL1@ASHE~PAvJsV; z4x)0iS_}=cynI-JMndSA%wdUCaLVyy*dx&oLb^Eb1Op@fM&}%Yi^W9DmK)M%rfdQm z0On*2ppJrU4}N7pg2%#|Pb^>@XLr=yaB-F+oEzP~R!M&>B{$;8y7-2=xlO6g)F$?w zBa)kv{4AOqc4)c72MidI>jQrnFg#ZaPF%VB#*1#c;l>FNWGCEs!%Y|6xO?TqnX_ii zWS@__aq%q|U3AOh8^_IBH7&oe`pSyeU%&I7{AsY=;v5Tqr@$r(A3t*S?*Z-+u^vV| z{{N73o$SR%`$pV{ee;P+=h~P-YZ0vbz3?NYNz2>)nzj;`1qt{y5Uf%DrQX>^K=2bN zzRW9~Gn~8}rUX)*5%cYYE}w3xk7U>z;{FzYE6npJ{HOh@-|vwdSSmuh8$5^>0&S7& zgq9!PHKFmD4pXXp4CFX;1c)UfMekTPTsRS6e237MT@ubpyqB6yx|8H zk@ckP;D})0e#4p;gXcgpM0A)?rtq7SYa;C6ov%Fbj{driL<4NZ2fwJ@^6+oZ$hF&& zAC1~Jn%&J7vU>L5jK^#LE%sl;tsxdprC2!3?IZ+2%AL~f4;Jr*f8hV!r-1w#@)6NT zE_e+`zh6+z4`=`}R1CU|*C37EzzvQWasIh$o_^pAF<7)=7;uXqqS581YLW0s9qJ#aJ>Z5OfG}Bzr2u5dok= zEcgpRjKcK-qd&G>L^qFP{_<>5N{f6;0n?PtZVd)kEFL*9HXyewS}3z`3fO^<|NPdD z!OV5fV?m2Q`9bofSN!*TD)-7){CDyZeeU58EdI0iG1uDLFrx|Jt*w}^ACa>c#q*qj zdK-QdKI=^fY(t@1J4z~Va4sOfsof$68by+p5}@xvzqoTKAhtvdpCTYAWe};c$ek~| zT~z!fx4;}Ith_J&@|SGfj)M0;`uNX<#`q|kk)=KP2rvPUy!-m$4_d^ya1JpFI){E~ z;cm^h5^^&g!_hc~KA(ihye^77KH)m;;+3u$E?!RhP(ZD>1pV#?ucKio*k%iy#E0R| zLv}c%8znCn1?WKmz^KK!BWs^==IGE7u>#YD(f2uYtdow{zMjJ-uR63V`3?KdJZK;p z(4oK~h%*M8l{jOM8+dFHgf_wH>3hb|9@mnntx-APDEj{F!}kCLPU2yL&I+_oOiyF-<2 zgu-C}DvQt+AqSKCCO8zuYhy`hJMpbl>bJD&cTj37eFa;E+&iZ&nplHu%!zPc0v80j z!X>6OVgD!-Y-v2hJ4?2OEUtPnC_1opIuU7@Laq zc?Dy0V{EJ9QIyV3sdhpki#hQVIs^TTI;iaJ>A2gDvbVFWTD>D^CC~_}QaOOBb}%c7 zIl;nr7T$+?S4cT)(Hb0bz@;|Gk_Gi8Ky8E*pnDe%a_s|hGZ^7OK>JWmBO*9a<_%S< z#E}`{4`7Ay5`8PL{HAkT>ylN^x_IIxef2lKUtCA)t*_t(U-188mz_DST&sVVyzX@| zUvV!A9)^34cr)ftdCPp#>+vb`?YG*SQIH+5rV;i^`wUcMwOA_MGu+>~<#IRd%^~pg z`UTQ$(%k?q!7o%H)kv4)*eJ?;4-=K$JszK9K3uXeH=y<%WAn9UT#EwST;f|`y&E=t zn`W~uaNZ4@Iu++rDRT*eFpU2_1BHa^KhLTK}CS({sVeTv~9$7kv zzVV`j%*!~M!b}Dx2-gf$5kt8I6|zzk9w(B-zc=d-?_zTvV>5Q)8eIBpj(`#b63r;=&(P0M1$)E47@%O#JaDuqD~zVNKYi?dA>Q`zgUjdN zzHIsCSAV$H z4}Zg)1pmaG9MYZfMvIlwl^nI6vVLn-9Xyo};_val^D|s6fRDtATS-zHmBq?RMNtZE z$84Y3{%u3ic+GAvWXITN?BDRU947G23OWgZAWDsZtZ0&z+bptTK#Wi!nCOlK`4T~S zm@|k>IL5LCaS@#{<=OX>`}XbQBi>BDP85WDis%YgCeVysp9vo8McvqJH_1uAcC(+l zf9`(VE!VrRb@RdQvF=OUveTXBrfPPl;~F@Ll7fFQ1CIuq9x;=!JEhv=u>lO_v&keM zHrmwy4U$AEa1P_1g5?g(!bAxYDvI1tLL>ms06I#4_vYU`S2dwxvMwlHb z6rs_7>SdMQ8D3uQW%)4Gco_=Sd9yrqo(4~oNAY-4t_iXc!B2ubFUZijEIAr%mVif4 zGYQx5SHVps3|V+VugUW%zQB0K7N-Di3jdX0D+J;ei#ehkn;wpYBc;vx|^w9mJ|N09QiCvcJ(U&I^ySC$&$E}MMfTqCN=M5!0*9ppd8SF?7os6 zt*xEQ_jAv_9m!+8S2FE`xsM=z)TZ}<#{9VYMFmn>^|%@og?X(q>eYeRz|>9(765`H zDNa;PG=NL+=+VW$|DzrHn*H-{c=Y}Kd`3N6%BzxZ-+4`fziRL$+`OpMPV<@_k6&YB zGibfm}%* z2urSWw#FPN#L5pbQlVJWViKwK*{JRzr|oL(jA6*%?dY>x#>hEzPD!~L<95LtH9DSc zao1D0m8cWQwORHshrI@JlbiBQxckaYMCis{B;1{l!OK7$F*re}DvcpLu}X+crzXN6 znTx6h`dp;+dghgh&=!-Ay!VGc{uR>GUAN=-G-3?bV+^QEHo zUa6i2TuznzXAMJSM0V82q@mbODeS|j0!(%^`UR5}8iEKr!Zp(dyR$w5N4m}3%)iHa`+UX8&8TCt?+GtQsQ$4 zv{_wH@uDotSxk_E6l6sr!2zY~Ty|7^f`#7HkA*yK@FZFfvc_OJ0tJ9M2-R3Hp;!QW zN7khKFM0AWI31UxUiZuIJn+hDc1%mI=Su=#9(Y7K_V^6_4uI^|>(`CmHi~C#`wMP$ zE?tT75l+w#kHZb|qV%&?LN(SQ+L2g{Dv@^(Ds%X(I}WYZ9aGC8)YT(n9a0aYo;)h| z$#NEJSb*uMjzEJJh&DJB1w}5wA;{a+GXdO3bA$&GQy{z}L?5^==~N0}gh;8+EhQs- z8W|jYC~vfB{f-?Q{m<<=xM%O71JAQ1*ETdRTr_u1xAun$^f~%geTlvlg52mwK4aIg z1s{K@KdEp32J@-_P5K3I2}n!gSxzNQ;Z8LT+(H9h74mk#QNO{y)&3mPdxuC!ZNsn0 zD{>kMQoXaNegjP~5C|iN5qCwS-_P8QR@p(E2L2B^uOf7jJY6O@x*sqcCpUZ2zK2X@oG-|BzdzNL-NPHyKLu4E(F1|(~n&G$pmdr;f~ zA}(0?oJWCQLG#d3%0L4T7sl2pm=A^B0W?LfFlJBtZgzvOZ@kJ~d3DnArCOk-1 z3LFMB3F3pM0~h{VQj=Ij@i38+m;6@bdPFoojOWmpDDjk{W$R1dSYdPB^ufNPXFh!Y z%(w;j{84`iDXAT&8-Anawp?`Ku|GYn|N9hNxqo2`*!Xu?to7Q8&fUp>v0K@q6~xKsG+r6YTYKX%{kLqKe&+e=izgHipKh-uH_UEhSP-Xfxrl?v~xef&|yAtGDyhoDkD@; zb^@wq@z&bCKBt;MGF(N*0Qzf<;=VW4Mu;%53<;9#Rv9HzW8|{3pfwq7oq_NZo_JV) z{>*?^_0^!y)jYxgO9Etr8 zpG=F{Mr#Q3>*?K^){JXN{7S2KLvlVH_j^2ky9;&eCFXG=wJ(I3{+cCmTAXE}TNaW0 z2Su~zouEL-f8w5o+CvAu`-?=3K3XQ6nmn?V&3!??pI!fw{_Ix$*_T+8zF{vfl?2umoDNV7YWsx~~m7D&PVFsVEKyX_g`s(HauQHscaOMq*?UKeO}x zGi#q4-}0w7K0Nr*KTdqUqHP~b(_h=no9kw7sopf@p`Y)oa9;S|nb1l^nxchm4xB>uqeIZJCYV#o0W53t}nm zmZ5hrS*;YJlf~U^XY-&_bE#5tw&I4g$xg4z9YpJ72Y~exJk4-D;s=CJBWM5~77Cd0 zS%vl)0Lil{(4;=w=}79I0W@%Rr~Js-cHYdqo!8Q_1Z|74s}bm(?@7R8AG#_})b^s< zRjk#=R{GdtA6U7|K}mbD+mv>P&H!QN2>0QT*2ilvLm-Uia94lAzvKKJ&NlNM{8cWm z;p`U9uHlHApTV*7EX^5|i`sDlnlKclB@weoMZSV;t|B-x!XbrDE z7kie~^^Kxo{9ftJxYHHuHc$(#vk4c=ZVTICVGmi@bj#HiUSgSGR8$`TeK>{$0d~~g zD#ZDeAC=i{^4&78kZa_H2yOq|eb&wY=zh=5A9erA{eoM*2ZxZ*f>C#&yTUC41?N8K zIqcz2dYI;kd4_sqq0~m9-H}pTwE=4by*AT2!!&b(c1<1$0(P0a)N4!Kni=*6zpDtn(1IG9OGv6uB&ixg>pJT1jm zZQ~JZm6iY9b_TFP8v?UzWLTh$`5|78w^qO3{a)nnku%7NulYjNvm=WlKaI$vB9kIK z8Yzr`f!4H2;4LU%ebjr(`z?$p#D0dN?WNwUyi2{Zu)dIag?n8!l3R1f-3j+%_e!_w zhCCSYc>Ol!7UWL!Zf2_%6NDHD8W1lD_eboB%z|^~VB(Y^Le2$>k_0PYyeTC`pj4@U z*cJ`Dk2W8*jd}RUyV0SRp$_N!Z^^6DuKr1Wav5)qp6px!!dQGshkI8+4u=2&{7%oA z9v{yKY5pJ1-UKkJ@?0N3-#N4I`%GrCOlGogGfBw44O=o3AepcP2!wHuIha?#Pbk3=6U=qK>68vk_R`5Q78tR9G`I0X>9WtN=zid8va&S+? z4(XS$13|kyiWib3ot_GTQWt-kzDY^KVH=d&zb02TU0(aFyjFUjECA`~KK@M31IBT- z)A3A+WID# zuyvE*v?c|k&1Ffdv>x>+WC>0%PuHfrCk3$t1&Ox79Ek#}G~Zq=!B(_!6q7i_-jHh^ zl`Gl#M<12nXJ=Vh)mJt0`N)5j$a(DN!s!_dAod*l=<1KxqvF)fHfV& znn8~uhSS5>a4&3}{a8aI_#}eBCkxsRqOZOe6=7y5Hz;_W%4#6Hg(+ss7o!wV*{K)> z;s^VWe)iN5`*i34*(HE2V|GMdZQs3<9p-Za-3XTpqtF*? zYZrP^Fe?cO1Ow{W-x1wToL0T160NE>NT<18_bC)cLV&#zRmV~(cCA{_CDm@kDHL0S z(qAwk_%Wtl_2k|nHj~PJ*{j79pC0_I^x&sY?30cTQ*y~N7BHNg->7yKYN)kl5TT@bxyqw!&NR5bdkEKTSb10UJ z%CIOzof=v?bcA7wM?O8c@rloRpZN432KZTW5_V@DXGCxPTBZ8wV6sJk=!B_L4FiGT!fam zO)qvn!Vbvu(V=*+n6q2nc$c_KeqBC_Q#Nu)KJru_4wuB^m!#8mkP?@$I5r_+o@-ud zmduGk={o6VNzyH!S&2=#_fA?=Ae4WrnFsL?B9bZV`Jq=SY?DQhS= z8lvLODr-ewTpnq4dYq8uYUa3&J&v*d;)y$Gpf4Rfs0oqzAF6St0BYg<}a^+AhP+=yjK&&cS0BVlFpHgGxx_#cwDo41bPcY{A&?#@MZh zhAoTbW+VivVmq(>5H2NXE)u4g-VTVfrXIz+qL|>3i)?&LV#!k)wp+tClW(H8!E5#q z?Ac1%X*7C)-KB)}Xz=2S*adJ31ZoTBr*byHP6ILxMg=ahV({5>{fdH?)t zR=`%{cOCA%59FW8u*V*L`QpF5dis|aUZedd8^nP9hh9f^AWzi69x4`@PlOeWb~Gxp zp+l)xa?chvAxbL=JhK-3F5SO%5Z?|#MVF+c61AozNaL(YNJ#36q+o@evQHN&ca4-8 zSBD5zkQqVurc#B)FVB2?ZU{U=tl?D_{?9l6h5cqo{uRrT-+%j0?A*jpA9@Pv@#sFb z?5UFw8{7fhH{>`u_=ez;YI0FJ0c_PiD%{)U zxFbWtA|QHmAb4h2133p!qLt_JWk~3YAaK$-DVZC{&?Pgy)^0HE2${CbV5WqRHnT$@ zON#270D$F z1yZnXz+c`%~Sh-i7~BAn&>s z4#-S)6B`-5vqa$ICg4k;dIavH)#|pJ zlWG{^C=qfbjcFsCM*x7VfhEZG)=%jhRCx{w=jK&v2HYY?eK`fnh&=wFGIIIdgW_5m zuVSRjEp)po$}2Tk?2QUz#ks`(e#rNi*N|~N@UbG_eTsY&xw>_1haddz0sgy$m*qb9 zi>ZFIN-wSNAGa#bw&H1%>?r)X@X=qy)lNRTL3wYgnKopO;m?J>fUTH{MNEp7l1FVr zWRW68YS=t1sO9&TMHDMjoXn(p(^A&59TczB2kso_jzH5lv zW{OpcXHLg?-J*V7Xa;XUj>&31h2&)n;bgPfnI#N2HWq4!ovjr@`O$K*oc1n+pOu$a z4@U}FVN+9miK(K(5FN0UsD#GGEU|vT7vi{qDuAuw{e4pPQUrbmhhcK0AIdUmLlNKs z(!L*YfP9Xvtf5%00@QWb(-7W5POS;TRw~!=aQ0YHAqj;QEWS`!ADvj@?3z$l90|>d ze(nBH*UbxZYb#2uB^RY zhx~}V@3j2Et4qzkd>{TdFMXBeqh^LJcX;<^$$ykTy8AbW=9aRb!^h%0%ex`pCBlud z3N3V4fhE^H5@OGT$TAeLCI%?#%K`~gCd(`<(co7O3qu0@Q6)#?HKI?CASPIW8BAXm zW_5=`+HShyF5+;)O?F^KI6)O+1M5)mtZ*#g9OKeM2R&Q>Fw=5T{frv2YT$645+&_o0?$4D{26ayMmk8-&(j5I|WEdC0^ z6W)02~eTc=O>N<(&2EK#=k*ydIhCA5rFZ$5V2%mcdMd+?Um?}&%J z2d!RWLV& z2`ZNJX7<=anq&@&lf9xTJ*5yc0&~;!FgH*cDjq0B5fy$O%opr*z;lnC3?Thl zUKB8P&P>`i>%r^WHDuo!r(A#kjL@W5gFE7`8dvSzvK>7SeH>SwCo!2;IdhrnjJ zHHKj~r4uA%{q#CEXf~Nd`+!O^YB_(2lEowGB*G$WJTwKV5O9hi9@Bt&CG}bSf$M_r zAj)Xx-&MYe!X*}#z_mml(cCu|70skLMB81Nm?Su z2k5FaY0b(fr%ZWvKFz%abH5up(TE7pPhzVuBezF~qEO69We2;MzQdqSXpR_97{xn` z`zRD{vc-*Fli5ObVOS}X3UCVceSv*bV7Cbm3E~C;86zI20H2^>x8J6Dh_WyF@F6wK z5QjH`aI-Gy_0U!1!YT21n9*jRZbSj9h&ToHPC}Hut?iSubnBEV3v{NRuaHvX1Gk~S zf9x8ar8Odm;wSx3Sq-T*;?-SzeXv4d<{$|JS9rDfK0E%xiNyz3oOqT!b1@vw(wTJG zp^*7A3fPgMk@A%0_x1N?XU*=P2h~GswFqnV8RWz%WHMFkj#tg>Su;CqW}9%8WZdp% zbKNW5qR)$pK@c5zygn(z+Ur{8y3Hl|1DTK%RNw`WefXJ)y>4QgO-D>8Op+-h!y-{t z`>>O}>15A1*v}` zOQjw(qTNu2Nhdk2DJjFvbXw6WSR<(Lk4WAibd=7-s`j5D$>V>{5Q>q2i&w4fm6FGJ z$Qv($PQtPxoyW1$&a^02r$zC-$dMyQKNk)kW`zfzW?PRuRene}=-zJJ;!fTCWBLXjpoTtvd4c#wQvd`{p~5DqUG?9CXf$HGDZP%veAM*py;Gtd_hJzz)( z6bbnTgQKWGs|^h-zqTRi66FUbM*c~1$%Hb` zre`*~@*)$eRfkm76QX&pP3*|ug|gLy#Rh{=s*{3VEn=aVb5$GZfwMUOH0e}Mhk_6YwBjGKnP4X2 z>nemIa-FTLE+wPVKiG0io(Yl9A+cGN(bcb|><(W2p zJqvsYqOezbRuysE;sJJ1+4+ULXVcCn2I`fauOCQY=ld9#md5G;T7w5;^RNa9zmlQH zO8j?_oei>~AlndRD}tz3!PZxtCM_n z%sMMA4;Kj}kGP)vo<1^)7Z3W6DLgy zRaL3`LVf+Q-rla8C$=tJw5n=hb#+h2q(!Pi;LT8f;&WzYLEnNYkCXuHG+e`EN;g1v z`QNOHq{)ln&Okl%gGR6lI8Q806ob*J7H%@(#!^KqrZ(rYy4iV>9Qk~x*j z9=&$%%{>M6EAE^7;?eo{EUYS+G|*7Du(!Kle$%&>`RZz`TsKrNs`WQcoKlh#u^4mh zp2}#2YsS`DrPcGcPMNcNpmM^kFS{JUVxK3^ZvkA}XC2ZyY$iz(i{pcJ&5PO#3gXvH z>{!~C^>AT)X-n;l#%xDUd0=aFiamQmTZ1tJW;`&^3+x(4_6PxEW zm^8+QxtrpH2fh-6G~O$(SLxK#g*C$A*r01lyZ`naZ8@RquG(;Et0%{9@mMP6mozP!T##ASQ5u=il2>=bsa>6OBUWpE zELyvISzlhf%VNkYt#F6tuiDf+aN}fuc-EdZJzHm$?|b}#8=BVk)@;9_`~DeitsC3j zk!ZQ=hT5g|nJpbN)YDa(4U#ma)l@NQep6LfQ?c1-E}byHb>fEJN{he1>k0eJfD(9| z4;zpk1c;z?^6c{JzKKQ7(#h2e<~?EzPpF+Xtsr!NW=NOWP+#p1ga_+G?G1T|4K+Yd za}LK-n|T~6RqGEdHL`?=lBfA=oroD03#BnnM*cjZG|xZJ;2+G(bL9+JRlvvM)QyJz zIJ%4${OWEu+04PPgmrKMopj~qR!CPix!LvFj@ZaYv$u2?sz#QpijrIA%(}6sL@hp| zF6rL*~0w(k%x`@ z?~?x<6qkmt>|Zlw&)XyKuqEIBo-|25NjNk>3Sk%KqOLjrx~u>OpHEv4 z)o!+%0Y8jpBr^sxR2h1mUs#yoQRyVJ7DcTH!o#nCZsQj|FXkZuhhDFu=m4!Lg3Q&7 zv=FPqwV%>Ns^j6Bl01j}-jC&$SD3+HZnu>9?+G?fT|Ol|$?vUIm*>ZqPZ-8^{n_gN z=eoOISSSkX_Ae?m>mCMKE#h;Kk*8JDVZ$V2)w#aVve3qm=nd6{IzqEUs*o>S6t0J1 zhybz>0pBI#OqfDQA9iPUyD1Jsxgd@@g4!KB>}Vx;ma^4KFlltS+#X}$=`Z{ebVxpz z4UFp<**T_V*)`*5H2cde#-PJr6wI>9fBP+BM+ZG^?VS@F9NGQLm*%rr|AvV^t+Wfi zN749-&n|MA=4(#1f~P%*e`-(7`uvxu{iEo47SUdw_pIbG$xJ;JlRSK4Or$cH~;#0~Xi`*4Q|*P?K(t?~RLbg5AX< zMRnuH7Zx*3F)O~LDPl!M*_R05A{dHlnZB0QUi!F>)wwPi;*rFpmdx5BYZyl>gHF~N zTD`V&xS$}UxXR()kgrV_JHm35c|qAc{OK<_Z&x>!f-}tnc8J|oOB8}BD6ApwklHpT zUSq3Y-QT~u-ezl9y`Xzvm7sM{6EsBClk#0x|6Z%-{=bej2}{T}n4S-SL${JUdUHJyMg zTsUm$+B~DwsM({|&R%>won6vF=MbxL3Ryxc__UVHX$A3ALu;Z{ENW%>t>tbo4kWS# z_(Fn!rK{bY!RBPF$k+^0zrgA-^@d zFR_Gw`;~eE#S!8QO-LzJ2vZo8JC9fOkWh1$4!R%y*($Fu z=vWqOSrRWSN-S+|Sdu7}w=^|uXm0ol`}*az)#%2ot;ZEr*0%7-gBOh$9 zuHOE^5v3j0j(@(Ku*GM7ey3`SrWl@Ro3J2OlW#OOxl7gRnt(fCi_R0q2j}J3BB(BG z>aHKC7whY527K;-R2nNv7Kueg`Ka=t68h?Mh@SVu%2fVk5I-nPew?{Ejt`{tp@dNG z0KrnoE$WE)0*r?ei6YbzeWA3iK)8oqX2B&8U3mXuOJ{9khh_QK7L=7O_}X$yM`LZL zW%2zBqn|zIiA<@rPppnkw%1LIdOXo-b+-2CxJmY!&WK06@U_3cc$2@irl!?@(~F

FD&;jx%b4HHP7C=*dHw_s0b{*`#9O>>p$PC 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

{iGJoJxH7BIY0N zxt8(XiT6}kms;!0$9V_vcjG$fLyIe`EjuyVF;VJXAS`9#lMhhtK z4Z=Wiz_RZ1cVb@SdA3#EPZfhU1iFR}gVLAj4j@N(l+y~}dYshlv}#e88{^)_+;G5Z z4umb<3-;oeDo4M~r;YCeYgznZE63kvb@>nDI{&a$8A!1f22$*5&~t%RxQ9gj?H1TK z0lVl2uuU3(96)64;3Key9d_JBJ9;{MFK8!Fsc`^$g8f2UO|7FkEqb95+L(y-gAV!G z@mkJ$H)%eIJO@0zwI4j<;5fIeHrngDX=-&rhdS8Bpa(!ZK>I<%8t`RV?E^aYC~1|K zlEWANEVodp1M~5Ci4#MaXM@IG_#ak$n{_b0%{oF`&)(AIu=eZp0BjLkmEoZFCl%}h z-!qc7j)D)v{)mcyvzQ>*h*RCvl^%UA$;l|HVl(CIYV+8PG)Z1o+$kL?fGO*oeNh0?}%V92!n zNAX>V{SSj}*?WO5pc7~SdNfa&k|Vzk+fs7`+EyK}Z3b-z&ot2h{KLxjDY5=h?RnBT z=)=PvoFMuZ{rhQY+KH`cZ~A>6J!6=VIRBHW-J;t7l)PD1Rrk*!H+@gUGp6Xsh4Vy?pE(q({RM$lHB~QNP1HuPPf0afn(EymnA|1VKA+jRhUg5u}`7&9vRD)38(L z+-@)Jz*pHp??<`RHAfE0x(nrNhiwmJy`Tp{nI8eUIuDXU&N`}d`cY7Td8sxc?e}=5 zWnN<$3EC=q)W-&RE>`Db$l1PDhejJH(WSwdgxIr82Q;+KjvxlWc2}|YgYN`-kPeQ3 z>0z{h7qo%bP}e^ed;pavVygNu=3dAvK|4SJ)h^8EFw)IH7f`JAwcx|xdw_!g?MM#* zN74S>xNeRI9|YjzXsEcb<8=$Kv+RDL5@IAc=Vw7Y?EbsJwzEzEmc0dF`_%$yOG4d)w1KByz%*^bzyY8GXa(8<`ja*TEkF>U zUqq*KKzlW)r<}H(0R3o7n@Y`xL2Cip(2q6*WlaEMbd2zJ@C508$Qkn>kfXu88URv) zI-(2c2HJs6paF7>_#I#RDw>^#uL;fgXUdYXRz;0m^d#_}iqgu_ z0{%9r>f>K~X{BEWo_5VzM*kjQKfpN5)6{n(y+-S(M_e_Qm`95Se4U%Xvu!0Vb%~N`Iz_2rxzSq9-MIeR>37`8t6HfO&KQVnaRSQBN7m%QPv|v~L1TAIb^dYqV)F zy%%^D=mij0-AgiW${9y^`Y;U}b>BjIP^X#iE`ae!yMRuOM?t#*>IlY@G_U)~^L;sE zi(GidXvaNNv)1#zN8-fNwv*_@7O{r?O`dwDMb>QCoy?bZ;z!Cdbc`aSPV%6S#Owrr zE5P=OXoL-3WV9#GcDVqcAJe9s`~i(_&;gBupez$*f;RBGfcimA7>AU8L^nWt z+EK?CwC&Jo%6X4R9YHzu z#fJ7wGd68I0m_)Be~+e2)0d!rGSI8jj73?8k!DQ#GiDgr4|D_H23ifAuX)DcSYtl) zrA;ru{-TV&v?1RFP*1xJ0Q2b5Y4Wt`&}oyeg^c#}Wn9|wnw2(k95jHZFKwu2USXge zpq<3;)M?622F4*6hjzk?T+-CDKC~fEXq`>UeA|G-nm+*g_hP^H<+XtDW~^A5nPxkf zw!+8Sbi$6HJ?qs9bZAPR@;05`3rbtcAJNqGqpnNKX(R0j8@E!+dqEM$t_AG|82c!2 zP=kK^Jvhafp8-{dBcRkV<}QFX&Ks!KP1fUOX$PWN*Kqo+%$Z1PCZ9WT{ zV8{DT(i7RAOea33jqpOnW;Dw3iXEw>uK<4pxDDVrs@1>`z;n*+1loX~0@Tx2+Jcm6 zKAWbU_){+Y0r1_xN`PgfeJ?;gWu(;i0MOeLKxxD0k)-sY4fRZSz@`bb)8kd&_JZF9 zkhTMRfmVP%!c)g|3n1k?Frrt_5^2jgq(l!eqX|nAy@s66;^{*P{SNt(1p%c&Dce&QoB_G(|Lzv$^l-b{>uBRXdUAcp;^ZP z#^XuGj>IFSEwAUy81xfRPVgQ?Y+AKmbYd%%`AE5AZMt+Ei6wfIwrU$viNn}pWA^uG z?IO0~OFxm3XIzODt7H2~Jxm+&9omoi)23CYC5Ge?OZOXgwB4)Iv}*yFr^JY*?OI2< z3HlOZFTB{qro~S5lBQjY5vSAOY10af7VCf^kJU3RGSV(1&6q^24g0E7>qJkwQOjC1 z#qTtZYlqgDv>8|8Pv$co3;b7lxtonYX!oZZw4hOXWNq!ltna^ zG%0<}wD?mlyu_2V*h|`^q7#2fM~u9AeMdQMI<>r3rzs~SrtlJva^XcMwx5Se4AbY+ z;!)qB%NFb3qUE%S)iDpzi;i(3z*T@*2FlE|$x94VCh;g^-lV3?v^^2e=K!%f#xUcw z$JlWFm*i*qnm(~{O`YlUkLGJ*%6hW+tb2KPej2|twl;vc8GHz%)T8vm<+VK28n%tC`W7mu;^^6!X$eT81oD<1u!&ua}1L9-a zo4n{pqqK{)m3EPFO1)WT#)u~htpioQwvkWzP|^3eV=Z7aS)N2`BqK4MEM_MfG^wCMpDuLF?&WSVly zSZ0FfPo}#xrHnlDBsI%Kp6CUrr!5iDlscvvuN~+Dm>*?KQ{SS~-8#J=lsXgirya3g z(iGVga`KchKgm;gp`Fmtmg(~}&sZW8yI2|?;{T&$Qcj_x#T`xedD?um zm?x9TSmHU@wBww{^p+U8X=Cyd$CRCjZyzP*r}@X0??kaF|2(!QiZfb2j3zr#xks}- zQC^=%&ip=WpGe&QTV;{>kx}|nbpM@nw7!wDnsd|NTgK7i$Ht01H^lbEXF0aHPRood zYZOdbY+TWOmNJU%$!L=<1LLvnBtMaxzOs%4`>Y&SZn60+Ws>-@{nU<_@VD8n-8z1U zrk$YT7n_G!&XcWYo2p|;S(%^kq)eN!Za4HImom^^c+N=?paBXk=}Vh-fc`x|1Yo}~&1XbRlcx_U z;|QgU@#s&`mY`n1JjI7H@?uY4=1Gw60H`Or4K#zN{z*XmJHa<;Fi+ahhkQH0Sd=kO zNmEaM%1H^v6<*RuAmj5=+VyHZhUS?eX1lqpuvSoDnmY%|&&2HF7e zHGRm(>gXpjp|oQeNF@*EL!LI&O#>(+#9n;3Zk@K2@6upDnzrO=(*!W4=qQ^B(3kb0 zTy#RIBhQ$eKSVB+`ZKkhGKs&}kg?vo0NSq6l(wvcl$B|Mwk!+J=d`DcHv0j}MMgRO znRhSH3W$z8^=uo`HccsK{)|I;FCad&A%8JIJ#j8TnSe4W??Ld4!I&ar+36$c2JrNu z9lx$nj@XWUvs03)sbTQBh(2lW$KLs9oa?+*;;P2AmH!P#P)sr1J zHY6r#FCg_{dCh!QA}zMGp`Mia%QHk%Po6fUjMJid`iQO-Jbjx1%9$n=8&Y|nAacfR z0VD<~b(EVnA~WUvupy=Z&D!47n=wr}`EJ7=?W+2Oa+Vpen>EiEy#Q^R3`D>alnH+_ z%JQ3f@e?|lPGk+ABOmb@jgFSrXm*s*mSfb^x5lJR-O2ccf2ZHcVv3IIwuS$DXbb$= z@1!PASu9w8%1A#8x?tC-A@ei+nt?Dd0Q3TT0kLJ8K9rHx0-|fxGRj3JlroXG8Tw6{ zr*DVmSqIt*72jAH?FrF`!ME!4&7kxb^n;HWX_m|Mrw(Z~e|3XD2plzRXxpyAJP6jq z#8RXe00TfXK${kz31EDtI|0hV0A-}KovnG=5YXA(ptL;^=uc2Dyrf6-F?HmbulW5v zG0Z&7*o-Ujgg5h&JWZMK;%C}Wr`tr8S8QoR-h}uuP8)t_5j1?wIPH+rw#k6#nOBF_ zOFUA_M=J;Y#7^{0T1H(P@CCz$I@3pdg;K}$t4xy@d+`w(s~3OTkLE+aSYMIRUgA+^ zrbSNLs$+Aqv=E@<&u~pH>sI7iv^AMS7PubN*IZPWV zhZ!R_zQl_4i}evZiAma~^QCXtD8p#Jv=uphr5wTwWvtQ4LVHurYh>{ex)`zphJ3XA z7(?u(45l4*tp-dRv15!j9f$V4fY^y#(&8hO_9icBu{YD?B`uV8W*x-F)Jv>b+NR@~ zKCG+QQO?+Wro}mm^4PeN7An4CFKOyyZ6uzVr^sl_7^Yt_(xNwgOdB&l^1QEW1KNR; z*=fI_L$H$=CZ#XiLSm3oFY6~n&a~*-41Tm&l+ni2k*7cFCvirz6Ps9H@i%?NhB3{! zv>DA#VxLGSc`%m57F&~=`q*|d%h9FF#rss=OPMhl&x~X0$e&2Z=ULRZX-b~=Hj*#n z^0yJBv7oKUgeMhHXYx`8@e#eGY0I(P6yp~gqu$V)I`Z^2<4GRWk)HsNm*cJkyp&5~ z@%RzHHf_%|eP~NwAU*~iib5}G@n@Pov6Qmd{LR>+W1JeppJnL)NPB@J0R6fE%7}JA z%1bJs4a-42eL8_2fHLxYSJ%E(Kd#7AUeFEN;=PV7udd06Ys zc*2`8O+S;jv_EYrm;6tZujpytrhQCb#$ein$VfZ1kHk0a$nzZ;X;|Bd-pp5I5|?R? zDRZn+M!gBj!T>4JuBnv8)X|S}+K8Mo`jHCXre%DVJ(?ZuB@S)LoBpDYl~GPeKBMU= z<2j9XQV#JEKQj(xVQnvRQjx{_P)C`-v=f=>BQpAluTasMl<`b|X&cjD^rDlr#1&hK zWyTkq*tVgq_=%0kB)*xS==grL9gwu-#WeW~fDWKVr%7Y^u$GBV()2gWDSA@sMx!T^ zQO;{lQbOtyYcJ(LkzDjA%Re?g(*p66G^xZCnWW{~Oww$N4joJ4oTz=oE;gp5O)9at zu0-ULC)-JmC()6LkI>O%q9YB*_(=OxCw69h;YBA@@{zXT{3f{Q84{KZUA|qv$%|Cv!iOR2&^D$m%Rw6xrZ44^ z7Amq>pIABVw~yj4aY*UI@(3TRlRQqO=kqYhmo^g1w2zHp+KWzPq@94QS8M~%Sp9eo zZh;;EWXyDd@78JlhM987w--F))B)t@0n{@-uW6b9vE{o!>X>GJ5rEI|nI=zL`U#~i z<%FD%sOtr~0Y3ZZ{V?B)(@*T%z_$aWlyw4(L7sNxIUno-Xh%713G$S60UZGCDQ67w z%>ds)lhRgb7(DGFT23ALSkPW%tHFyuDPz!&lzHt1P6wzn^KVDG4WNzq({GxVQ^!0= zsqX-qG-%fg(3d>*yk;g(Gy&q@sb%zMyc2=3PNbup$Mwnd<~ut+x91weli8cPHrTQ~ zc4;7%t?R~jOLl|ScY_kFW3#5@Sx2cS^<8@Wi#^h+y=X%}rpfd7^};i~7l;57gFM?! z;YuXGdF{q;reVPE;wP`9k$|71uzSH0(4r5SGANrFg4r?lY)C=FBd8Uar zpdFx11Xui z?*}N8G-c$)rg;<{^#b~eKV{_GH0{x}9hAC=PKzCFTLC@~qO4g{(F>)Vw(KiXg6$xD zzrl+SDeLl8t>e04<}YOvUsBV*9cj)DV%H3w&-^4V?Fq&vHRY6X?G5E-`NW6xdxoCA z?K(}JF0+~=Zb14lz;d)|usxd$8SSa-1&(Sk9%X`N@bqIGGZyV40QExYLp~O!e81M; z1Ip)qjKlL5Z5WGG>^n67T|*w$Jll!(f+p}SKxd4s7HR6b0O}=8SuD?V8_)$%-wIGB zl)Qj?0c8fb^daA1$jMViKjuS_7k}zvK^bDTvEwT^byDircK1gXWEptYCluXI7~}DOnb&|*LtyIJ;k4ta{AKNOp86K z=xE0=X!?r}?ZijyNeRm7OG=18W#mZ(Y%8%5DtU-st@a}oUTmbkV#^o=<4S&_Gi_-@ zJ?%tq(l$d*TT{n0(FK_Kh@DXSnDLv9G;Jh*^3+KlO!IzD(ozqh5;rz)`cXzoh<~l- zt27mzP|?$#GBe$VH1iE>%J^bOTLEpPe63o}H1)KXa+!A2)dGB4XLz=nQv{3pXkKTObbsR@@$W=_LuxRw2peQ zXTH=o1B^>botZb~rVrCnUQ+R49I>N}I@6bF(Ua1?Rol?cv^DL>i%#+|?S=1zT^Nvj zMQ&31n`!bApFH(aPSG*ltnJL$^fS}qFYzQVQt_t^(P8+~o~YH7*Icv_x%il}@Cj^U z^Pvx)rLTN+Zgp^TiBrWlp zVNaR#v&bnkdGRL|IeGDuxZ)@F)YD$lroZ@5Ci+;J$R(e!k!Nfy<|B4e4v9xv447&9 z^Ip!ZPpsZdo4oi?#yll&mX9)0DLZup(}dWYY4M}4#4_`fSmG=3DVO-9;xE3EugJwl z^d^-&NSk%6SiRV^8hMFKVhiPaXvvp0A~$1F#yW`|)26LiPw_Kt+H_nqHu(#+oUw>z zK=Lpt`RRsE;?RdWu@yPXL!Mx{O*^qCm35)ii?2}1Of+j7>M7@Y3E|C{!khWhj^HyK z>RCR{+hQwvQp#!pDHG#R-fYO2W(-n7%1K*^NowlJn>x`;9L9*%OImzMX)k3Hd6$++ z49Z1EN;&-`PqCBuB5%{Sl9o6^7Z@>UBlTr$iBCCs@gb#5%1lZ$Yq|+kd`Lx3KQoT_ zODu`QXXavO#t}OyS0(HilYZ2hwylPoHf@09Pk;JI9@J$c>0hsPBX9- z7^l-cptKjC7Vxy`270xe{)|U?o0gGM#+dYN0M-NaXSyBWa|))#mi~;zH2tWfpZIiY zIelvZ+APoMF84U#~AV+l5*P8k5v3Rz|)sLA}3{<`H=EAD6|;>czwqF2*$h# zpsq>NUeMQdTI@{P4jt1C8pS#-Hk8Nmrc89Pls2ZF@Isq)T&80|9c4mAf3kE3?7INg zo9#ugtvGf_B~5(=KuTFBKwq{QL0ihG6EI(bY1)u7FVdzFI{dqA#sk#vhH2jkv;z`@ zyu_f4pp1Ewr;o^mGA4QYi5=5Co+DZ=wxr#FnQlOuXa^XBK8(Y3ljfP;_5ZQ=E-|jA z-FKH=u7(;gC?N(li8x$BN?ejkA`wG?7zOfofDngUj7X3nQDUw!3L)kaVJ;$0B!~e) zN(4nE1tJfSQUioah(S$|N@73_i&+heSxHO^5h?tBkDqn=$@{nKobEoy{iVMiEbYD5 zUVA;4_qEUI({;MRv_hS^no-TI`FAe<-9-P+VflN9D>!G@z&z1hIKKhc6$_%UPHcPK zJbHcYZR(uf|1FqTEcqF|Sg`3Vzg%XLN2k_2$Xx2y%RGLG>M|3r ztYw|~yj`Q6X=jsXozz{uwSIxR_}d%RI=c3*!x>xNdAaoC*WE+< zEuZzA3DiaNUd)=UCbN1aw;%2QTD|T2`#^i<`(k{TwI5Bq(l>@P#?L=1OD-RsT=;Np z%-u$lSv8{N+g^BMi_Gndnc3HCx%OQCb5VD0-#-3iqwDJ9-TH<*6tl)5ukPFp>&s=X z+fVJEC7XABt-D-i{7y)2?5>e>we#Wj&8gehZ?(z0p53+g?I*K|tw%Ux%eQ%}NxpC0 z^j4kw(W^x}>wEIYs=m8sx8AqMuBNTc+dBJeW806u<+^LOvxoBdwEeDUe7o9K*XHpW zcJ4Cst+tyVs>3H{jW+(p`fOUx{4Z4gz1eoA)tR@oy43BvaaGrdPv#QCcVoO--)LHY zbAJY=)koLHw%_KhSIc*2?WLdZj?{_m%zL?Owti~k(dD~7(RKOu9QNE}_8Dzv6T^4o zzJBYquOE-@KEl5@^Ik9W`(k%3ed0ACZ)d_M+Uw@lUC;L1tzVn(^J)9ZN1Z)|cg?Ox zdfj-n$D_OF%#8P(r@M}I%VpN+d|UsJ{LmWN`{=-0nLE_~+&#rdy?M9p`n3J@_Ho@_ zG;K^C?WdypeKY&s#Y5VC^K!o*xc0lX~8+{)eu=Z{9WX&qVwBKa|T^^vR#Wzjv;=Z3{K&bz|#mp8HZK79e-*^U3_2Jax43l3xPreQ>s&Q5)ZQ zWG=iji>h-!)~)6b*IvtIjcDU#lyjfzy+1v}$HVB~^N7}%-hwsCwEp|w5})8H>UO;lczs! zwCTxpJ)`f&1=?B1*Ll7CUN+B+Iw9Bc>8~4YxZIO<&S*XadYMOKKU}MgSIfEn92n(= z^|D55d;Z+FoF3L{;+Lp4dkB~4e(a|<`fFb5f3W7xuRvGZU9VfKca8XU=exZBp0le- zotPP;XKw35dG=*|s59Fd?DYTlPVKBSf%lR8A0SVi zn4aIonN4q9XUD`qF8Otn&zn5`Zd^8+=o8oUl8+l*a{I}>2kS+{rRN!vcjNq>p4xTo z>u;^zJU_1~*PhvWADqkHM&#_N|GbNT)w6Z4EnKUuleEp4!&W+Fpx&v*CP}$upDMpP}kUL&b*KT`lu2eTq2*|(>sI!0nTlJyqtQq)(AAam!B6?E3f7TT-P`w>R~+vYv61&@w2wCF5I@}a$*JY z_gk%^xJ$YmDeY%#5E;FfHO`Es<;eqwu z^GZ+8)+=iq>PMS?V%KM%PK|rbOk!r+y5-DUsLPpKJ~Lg;y1VXwRQKVZ_hsE~Y-i%# z)qHNPSNz)C`Rq6SeV)J4?6cJ+PjohUV#~GO_H!=t7TtU4dPS?>KL)Z!R~J4pbKUsQ zc0A(K>RJ!;K+O!yf#+!>x!;rJ*Mu#(+7*~q9bEaIRU^NtSQFJ4|KG~lTU5^6o^a0i zcLRC%V!Wv_Gtp-D&(N3qlU0|$C#GIae9gyT1hVfX`QHcTK)q|)GpbV`t(rDhBbR8t z5B~YmK3;CJr}rtpLtgUkuic~EKmY9-eb>7NH#OQf_il{guR5Ik8mKq6dFB%J5=$UA zsYu`1FXKaCAt3|&tCZG7I0ZdPTbX)dAZEFW^(7+T-^$|w<)kc z28U+m^fGUZpZuQ(S=Y~Pbob<~Uo@GGPF~##L~}=;zR{VW<-cp5{VefwjX8)%cVAi0 zJ*DS=Gsk=eSOtt36M8YL~UwdbIhkr>`zE>ACOhv0cNyz3d}VKKn3_XMExtjlBQdQ+m~Yy;o4|E=l8nzyI9UaU83tI*V{Vi*7Ezfmbc$( z+C09A>#ChLGu`=B?ZwBa{u1cZ^6~A)cqXQAJqP-QGq#@HJau%|LAb>5nK5n}e|24L zIG<_qR@3J3ZFSxJ6`u$22weNm0=>l?EP*rTVhFqskzDPv)@tR0X_JRj6W$ouPp{>Y zC$>Mk=AUOdXD_kM!zJ1i?m2n8=j3f%R_|dGGb`8bxy_?7#!r3tZmuR!;~t}t_jy{+ zYmC;|>YTL>-}d}<+kQK1E+^Dl&uerKt#(A+&buf3^3my$`EYF=&vr&#YW+p(eZBZ* z{nUx=Z07Y`f$JukZ!2f+&(8Qcn1dOxw*bx=)nrCa?G1=V&ROTuH;M_E)m)8`OKyxd zdVT+8u&ljkHkC_lT!Yr%+`UeLKGtgVHrjK}zA@O=+^E)Bd+UmB{jb4Ks>Xj#RNIAX zx17FhZSFqSl{boIt?dU{Kech6U)yie6dD_nSKX za7NcR{<^A*#+m3czb{7P-(%m`57+hW*8V%emNRc_eOxQ+CC1-bXg%W_y|ruX)5`nv zc+n=tFSRkS*L|PUo6yU8a>?5>{^82o%Uoi(_=mT?taEacJ+t!uY?Cqf&g;>|WDnQX9ICUgL;cU;a~IJroBhPU zJ#TbI&(_v$?o8%et^Yot)tUd0JLr0KYk!~6<+^p&_b$3U|9nxd{r!Jev#-9Yb9(BZ zsJ6Wq^Q@b^?ME-y<+Ja7>%Yfky=c0=sTXx`>ix4*^>Xjcwf*Qm6}$U6q|Y4~Gn1G- zJDYxb`~3WOhxSFz8Qphz*2}t{YaTj#yASnQ$JqATygg5=^Y3D$j=rmH{kyp{nd`>* zwljJ!>zb2hw$&u>dRyl%+qGI@X3jOt53QBGMQ`lt z(~CCo(4DpVHGa{*7ju`(clMey#NU`aJ=Yk%vu567_b$}y;e7TG&KS;qbjjPb+x6u| z{98Zs%$r{WWBAr5d$rd->#UoY`K)Vg-u|~wqHX=d`DfNGpS>*M%~v%>FV~{Wr%r6O zhjO*;9@^RK&U*1s-_C?j)I*GR^C#z3pSiCcUHgZuacKU#^6%}z@7(wL-=%kGUxz$e zee%S1Ms6S9YTnDg%WPNMtzD<(%v0yR%jZ@;oH2FuddZJ#Pd-?Yi%pI4(LL0ksd`ob?AV1I`G!ZBzeuh&N z!TQ_0e^GPvPK$GcFs0qq*$ z?JYoe%~q?X+m~xStXII;B6Eul-)g#fckT4oc<3_%YTEUEx7xYnYW~qqFMe&*x2==M zCv~EHFm7i3bG+>9&}=-6pQCB@nfaW#zZ3u5e?0D*HND61@onqmS?j9Nhf}j?`0h-r zOD{3&C(7^Bx14$O?K78eby>r>1gXbWw@)h<@9bsvjy|5zX4V<=tYM6Ia%ZB?yuA-# zT=ns9XQPeA=bN<>)o0ciZOf^N&bc8l&N_V7i??-jtD3(NZz|SA*V_R0R{k5i3-TFo zJ@eqdB6t64?TZPppV}Go7w|se=$4&(odazsdau@_140^ zocdPV)_SIP#yZhlU;A=;#Pg2aeizXts#l{&^0roIZO=S&Q}XVNx)$~`>%4V~S<{z~ zCLVIh(=)c7*6Fpp&(^wKqwA$+3|vQDy!^dW)(&qS$o+d&FJ~=$7TJru>luZ;c7N*9 zcQ!rqRvV2my>>Qp#%Nt{1B~HwHf#N-wPzkb{mtz;n|-t%^6^qX29wGe+Zna3KDBWM z^wldGXXTA@LQQhD`Sn{~-4vL&$h^JyoBQWY@l4M-IcL-gIg#9GUv2`Tw+=rgS2wHC zp1D!Iz4Ybvv6p_dYUHBz&sL&yEpEQ}V2zIeW7@W3S~~|8djv&vc!0 z@6pFY-Z^9ICzsmTuA81wAN$e7zva@)8l&o$b&byHCCsP5Icw*{9|iGMBWF}2>{*Mf zX&$fiUC)0{{`Ua;Gawi5tQQ~q-8`DClUdhNYrj30wLWs;yR(^#S7P=yW;Q;0b)jBP zjb7G?nKMp-QD38oHhSyudRj-57_BjRW)d?utr}xEHO{$@Sfowik`j`3aacnt1BtoKfu`0b|f=?Yo9iIIG5J?M(X4=_9Xa z>S(jq=+*vlkQwL9Q|l4VI0Mcatwl8P4j+wiRkeqrXHX|HZ`3nL?Rr_$n7%Rfzftq( z^mT2ak9^B@dzqV}6RBOJ)n-pdc|FI#8FQn*Z;pqW3D8H*a~tKeuVt;}lE+^z9?t1w zFR+(AcB5W1FsXXA*1|jatdN zb?W0Iug;jCmCCF-d9}&)jy^r3d^GCi^a-CiqrFw_w;tU*9<5(A@zX~xa1Y@!XB~Y! zCv`?m+w=E8W009Ea{oPpI)DCbRCCX!Mr7?7xwF>l?G0-#XWyu10_?kAd841>-w*uf z!_52N>aefY|L&{vdRhy8ra<2Rca^z(IDPEN#nbp7f+ZLM``tO~c-Ws+=o3G=)N+A* zG{2AB_4M-JmB=rFbN;%J8KasJupflCo&Yt%JQ`y-V`j{QF}e8dU|4xQSdj6`Eb!3iq5oiUEO=VuC`mZ zv))^`Z@uX7>t;WhPt5$ZGZWtEuZi)^nzPE?)Y#rl*W1}oA%5z{9ewM)&&RzB&z-!x zhw$s_t#(rLeV*<|58-TA({k~CPjj=!qq|4vTd#dHSwr2v`Sgv$W=-|!wRyNUrax=e zFpuWYx@!KBM&}(n6Ti&2FZJpW7+w<2HdGjTh)jXb=&rEou`ZZ{|XB6>H%$n(Sb*YV6)98OM*Ej^N zW=0*jhPhEqa_dE{<@tXP7LEL>@|lUZvu$p@tejdAe|6qb3-6?@^iS^xc~ynD+Tto^er zJ=Lv&J%3+fE;k1LY~2vZjljPG)NR0T19JN6C$7LXaQ;83aYfWi4|99Y&uT7j-&%|+ zQn$TuiHl}VPH&@oh~~S-u4mTIo~@T4y#D554zixPcVew)+qaH3v7Kx4ef`HeuTQJB zZn^a2^a;APd^l%Xk9Pg8F1>cvdHM9(y5)zBCVV^7?jh$$j4pHX>t@}y|L;^j&q8gx z-*~i9Ot6NAJ=;?^sioO~;ap$6b#m)2 zoYSM_yLvUE)tI+)x4*jI*GJ9w#rOxz9$Isa=Mvn3YvBD@%e&uTM!u=_lo&sGb9J{h z>LJvPD(9?Pbz%XWQ)hh#!dbV_$KEx_KZnUJL3G(e>NP$8Y*7EKVb3}D(duugGiTJd z-J3J=i;6MP|F=tK`~LS1T~8l%#%r*t*b>#-Gg_NFqi1^7ZEoFi>#EPJ(fJKn0sC^{w>2Nvs8{;& z4ma=goVAzepQk!IsnJ~D_{3MAo7!7<`s$wx*XHT}km#A(bLr(X+~*lx;y%r%xcLF^eRKY| z*Z1+^-t&5|m-#Ohf5?6OklA+shkU-zJ?wk_`?#!W9M)$Q&b=kSAm2Ct-u~6=8IQ!) zx1CR}F5fNTjC0V|-8}o3Z*#p`ZTB3m7jOS=S$E!kcV6zhK>zQeKcv+sSdxp>pNjsO z>ZdgJKgZKM_2+Bjw`lTkUn{!ym+=DUuo9x_qpCl3w=|& zr+B)zRqc%{|34Aci&2w@I~4u7uS2~bga6$9e6H@tIKvNJ(`WueJwA8dpDT#=Z*B5$ z$B22B{`1>0*89F|_}_)7v*rKE=6?^sXSD&(U<>?=uE26nZclw;@tEoN*0qDyJsUL;Oylr*K|R^AS+{XMnoQMPq#lwm@zS5nOE9dwy}O!^iT18)bSCra z?1^cGdi@qPj%x1DEbC!UsME)|0`~MXmrt%%f8##5?mqs9a*HqM+y49WAG)vnjQ0=m z*?hsX{g8e5jJxx4KGRVLYigf~y}TdJT92D(z7Ni{eBPUOHeOv$zU^fn>V_TPtdW^^ z?vQV^>8IWoTTQ!G_;B;OuYDfxab2Hi<7?DA{Erc{$M@DAcJ^rPJN4d7+sk_>=WKe* zCJ$%y{c7v4mAlTcvtIW;x^=skef!j})x4Ky-ZkdUT=>l9{@7Io8E`^m$`0q{p{r*BLCa#{#;u>;Y^~r z$lja}*VTkiv>%Nz?@hRA+t(xt4*HISN=QU z;d73wx;HM;t*?3^=g)qoPHgAGb>py^OYR*zo4OmP&5ZfJXW~ru ztj0RgyhY2Kr*306&-*$&pbwupYmq(u?poWI^I2Tg+^F{N=kFkFDyL>2+=qN(^3?Ce z^!E9NQ`6OjOU#;?`BZHE+B|dnxb%#l>Z|#C!QTbixqbQPcHoWxS0kq;J-J!KIcFWr z$rrUYpVnM%QXw}6=^3LPk*k&K`pDVW$6Ty{YsYU{bN%GiL~ji5_pd$wuF?=Vr`Nhh zXZ`mf*6~my^iiwET1|ZAtc`N&gj`}flexB^nHe+oTTOKS{G&copf31_$@6QGT4B#P zuUHWSdm|7ZHTDvdr&cd#lv~%@++r_>{D!{j+@JBjQ~R99m9zhgfIP#`*-Os#IoeNYythwj_VvH-+&-@rd(baX z6aR4gqW7?me~)`&rrkq(m#d~1Ua#mEnkg`!B@!<-N9!w{cu+c{O^s z8vnlK8mJvrm}k~`;jEmqbD+-#+yKAl)og)1HR?0tod3NGYjws}BNr@d?_VP7CEseD z6?&>&fDusRKQGHWBj3*Ir6&Cqxtz7Uxifl?YHgmGHovMm{hc$)4MFD3?dk8&&iXUQ z_SQhXJ)wX0^3w2X?M0(cn~$m0EP!jdXAzyz`RJ_8Z-74jJ&U$4XDwF1S~%xAS<5&A zWAK|mGz)TP?c0+#w%EtD{caw8;;{O;r)aZYVtD_1Oy=%&0Gtsyqn^pLZ-16UAE8!_ z`9BFZwH`LnJa8>%mJMgmzZ+^C0X62+8dE3Qo7Y~VTo?YCVYqfy&a)V&ARetIT;})1 zR==;`0SzEAX?efRY=?!&6`R|ooQczxp4`nB9X@7W&D zWyj09oozkM12x{kKE3Btm)OoY*Vg8_%kajoE_LD|ZFqGp!YA(YNpDtX-B&oHGvN~D z+uS;z(wVLG1j_+MlPsum6{*>qM zY363+!udA0ZeefPaM5PA)ta|_?ysF!a|P_RvueU+&KN$Nb?$mi9{4?IZf^+OM`p6; z)b0O2JKVI<%USC+sZp-=%$!kOH_G|Xn#MqlYwP1V+8Og8`fiNgS$o-Ibn4xH{{OhVc+%Rsm7kXTxN{pPOT>Vr1sV9gSx-D(`#q^?`pUH@$kv>jxHssK(zkaqo_wCOu=iPR1zrNAr`L;7( zBF`PT-%t6ejovu=%o%;xb=J8&vv|F??!Nc!_jcCR`#q0+y+doae0=@0-gvbAaEGEl z7nI)T%=x^1{@zcEd8clBhw_{6F%z%+SxoBp;YUBoxYquj z#N2r`-G1hr^-eax-yf)3fD!P|q^#Zhw&wPn`KM~_dDN=A1M#&s|0lq>=I+UL>^Wn; z23w$~-u9gH?p zzXWRS8UFxKYhTD`{<_xko5~xXx-$5v}d% zp-#PsMvvsK*LwQ{Mk}@u>tbt&ZsjA{p~sLoH1JUSHReE&a2J5 zxf=gj$+)SIZ|BCf@7$lO`H*NW>?bbC^|Chldj|XeG|<=Fef(MA8dpHwutxjB-%l)n z-}UO%I?>YMi%L zw+35aPhMC%XRdx;b7#y)UPT<<`wxluUNjV)&$v@_wl(LKd4y>1;oam>uHS;za+$9`s0%l-D+ z%e=k!5S?BkciD~ZLvGpVqBq{uzPV_9$4wnRaZ!7jNu7RT{;p^GM&Vq`sZTGn#tBF- zGl}x%E$*lvnwbsxe*^UX7l7K#-H>}P-n0E{umWll{VrBN2GJ$TJ7;u8_-nM$UyELV z+Vvi|uYG*WwHouly_p-McOCa3CQTkLabA0EZ1wV~jY4i&u^_gvXKf79PcQeCx~tEe z(Lbwc>+h0xXFaoP`!fkfy^M#DwI*oXqfrm}{MzU2zYgpR`)jZS>PFxWWNu8p4}S^% z5x8g9aaQkPg}K~)t@G>PU#R?&cnhM-yk2tl<+6`OpZ5hAkA*2~jgN?*^!a?QoxAio ze$D&suJ<+98t*yZS6z?0JL>sZZ+m3;c{nC~AD0cfb=&(A`7zJtTE~8V$6oJCJd>Y2 zzWodw`}rMvy<`m-k1G7 z@v*u>U;BCeviTqT`5k+`V?R%SJ$gMNevI{8^H@0M zUAoq>&h_&;KaIOS_V?Mbe?PI{GjYuC&|{zP*w63S>mBpi{W7d|?B{pv^^W}v{WbTe zjAQ>gf9$(F_W8Q&dEPGfbN!2ZC&&IiJM{Yrzvt|I>iaJ9(WlOAH!kpO;appP>Ac-f zn`ggY`h3TJe#c(#Q}^|A|I?qD_KuGI{Eod|d)8y*uY8~UlyS@Xj)`O6VzY@Nt_vh}}JscCCyRR>$JLcK; zt>roP@jr9FnD_Ffzpp;!*Zx|6jC{fSc+Bt6W1Oj7<5Tzj*Lwe$&+eCDt-sdahfnSQ z*w4^k?~aKV)_Xo8?pW)XzjpuB&v(qbbgg4Qzn}W~KK1?crTgu#^=dbcI;xjxywdQx}xBF@H?Dtb%nTbBNFe^ZV3&{oMcbXQsWQU$p1@rT5dG z^%(gJpUdYX!ZXc}hzV;A8(h_V+(h$3$ZYhfjp5q3gr6S~$3CC;cTD_zzQgZk-n;f) z{M7ro<@p^G?XUmG$o=g389Ww_ai(^SV?MhdW36L7w_lcZevD^$%r$?E=Q!v4<(O#C z=(^UkCYm2YW}{Et^+jPe)T7E9-$Q1j zPi+j>jjiTu=Q&T-N!{+r{17r1eQIO4ZfrGQJKwU8V`9d7iw4UkZ}~5kyPlu9Ahqkt z8Q(+ZqEDUKZant+w(S3>j49`9-vysTTYu@i`}|V3(fkmypXgH?!?p3)=ldzYE1oTP zn|uG-@5`JuT_Z^CKIDw=A#>5E&TKc{;&)8Ae(v%Z@t9|Gtz)0ByPkXan)ljW?`y85 zcXz#?+WC(C{kh^!kBK}(pW`ub%(J=Hv7g_u*E_~D`(;|=*w63S>-`wd(q9t}!SA0# zy?y+r^w;>^G*~xz%YUhSg#V<$xXD}oOXWZH^G)mXnh}L{a^p>>ZhN28@9IbRwa?~j zKfmsIx^;JdKeRsf`Hua4w&M966ARW`9udbpn`<5W`5k+`V?MiIhP96U{Eofev7h0T z&)`jib(6RJm&#}Ow`c6uZSPCuL)JScy65TEU*b%y$1%_5TGIpfdDZadhmifY`YxAV zV*1C3)8-uQdfAKdJ!B^O)W&e#*lNCZe(div-~V}oZr%32L_T4yX@hRv_P#{kJx{m( z5@%{Xww&#!jPCs{ns=wYhcA_X?f0Ymjvf1aKjnADvvu#YTYstdBtH5t8+7Zo_a*Wb zYxx-n^fYhdd+T)PQg=O4Pw?J{apjHgA+yn^Him2CPyKvT&b<%5H}b~!kXbd0I+NIX z#H+pUHgE6T{17q|eQIO4HfDd_*zWCf&-b3kk9EE+=RPJ@thW!#${XK9W}{DS4A;g5 ze2aOl+h~3WnT>awZ_jo^-?RSD&>%`=TJTlwryIguk*IOSE6V{p?5kK|wd7tA3(q6Mi1{A>ReY_mJ7>QyaszaRJ}LUR!_ZyxmWmw|g=_gv>;r+8C~l z$9#4_##)E&>1Y1%JM`Y&{20&hm}|aw_OH?NbGbeuURmpyxMjU#;+SW1tz(|;=e7RY zzwcvym$_E^>wla3U6$Vyy!XA9ndnnzwi_4tws5YkzjWU2r_Hn9_s$kgdxk?E(H#5v z9ece`-Ph0k@AmtqV?V!RuXpVC;g#=`W5UniuheZc zKZNdF>R*)e9rG@KUhB)hyIbD9W8%OA>U&@yB}k%V?MiIhP8f- zXL!stkNphCe7}6^*W^Q=UHQzmd6!Gic+9(WE!R9Ie(LAz-sO*R$K5@A&H786?@M@m zsWbl6-_M_azjXZX*f04`9sAe6pZa=V^4a_%dL8@u`91vJJ^avq*L!`o`?)&#Dftsv zfti4t5GTYNFt7EPXwSW<9apW~6xdfAjK~*g)DMAv=JqpV?yRuyUhJ(aCw~p6&hOw& z<@JkpPCxVUH4fpk$7RjS8uaYP)4RB-wSCvNKL*jeR`_dpXI;}M#=w~+xIt@gQMu^U zcpqv;K=1fF>ps+ax5by}^YB?%+Y_0&mIrG%`+B&~r&^E5opmkehF}Zitcy2s44juM ze2uQ7r+24TEal9r#-20V+IRidH@Zakdk6G!UDuG?5Y>pn)qGfMwTsHx&z+dNewo83 zxqDFWxwbWCy=d&+0(s}%hn)Qzpm%CxyO!Q+oE7$kKF;eUzaYxF-*wF&)R3#s8Y}W^ z;EeNEHEya-t$e(V&g$)2xpRH(xxbJ4s=J}*KGZt1gm>Mdqu!9O!4PC0@vt5>DErX| z&ONAGRo}b=WpA}-e@ovzy9eXd>HpmOHR7-T=XN;j4e%KFe)P_IefOy6_rNLhW$v0N zti{LrwAc4;$HZH(uJNMA1yR@+(;a%7Q_caN!OO2k{}Npfegv*qk~KZk$qVPXA$s;# za?UuQ9-PB{pnfD?5hvgh%&6_bov`PV;V$n#6UV#!1l>n|%gnm+&Io3DZ}4++*LVOg z-)h`XS@#mIx6_=g;3mM{3~pTG`A*YkFa29E$9qjwzX4C2h4spwsZY-6_v-mBsjt9f zr?aQdnUf1J-qj~_q8fVg{Ws!O)m@X@r!KvFcr}lJ+&TNcJLO)kcF(})+MCqpu_Owf zYr$=;SHw9m-qqY%51wb4F&~047}fZJsFxbKbC4N(=0j{-nApS=1w+w!B@4xQ#{_?-t>-~R! zY8U_VA0+sKlFYhI2WwXzIqe()dM_#;dfH@+?!I*cAWXiExA$60^C!th>ttI zx2alZmOE~+t-U*RJ@N@)wtV;DGq{3(sT|t!9zIjEPSNOf3$Kotxw0>N3xKwK7Vs*S3ZzW-sTQR`>g&$iFK ztT#n>PwraGcu(cm9``$|9GbFU>XYXk@Arpm^?TGeRR`yu%Di6At(bWs&T4I>=N?Z+ zn{N_<*x7?&>xJrGy4z3XP#G&oU^�r_AE)SUzOGD1VGXW!lvxFcT^U-38t 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

2TOi`v7Q=BQmlw?XVrI|8J zS*9FQo~gi8WGXS0nJP?GrW#Y7sln7_YB9B$I!s-r9#fxbzyvcc#?6E<9>&XrGGRB015dNIA3K1^SxAJd;1zzk#tF@u>{W(YHs8OFph!nc2G3%KP%tmGtvzgh#Y-P4F+nF8APG%Rgo7uzcW%e=q86N{N zFoQ5CgE2TmFeF1UG{Z0~!!bM~Fd`!{GNUjmqcJ*TFeYO$e&zskkU7L0W{xmNnPbdx z<^*$+ImMi2&M;@0bIf_>0&|hM#9U^sFjtvt%ys4lbCbEn+-B}DcbR+4edYo4ka@&B zW}YxlnP<#%<^}VTdBwbD-Y{>Ocg%a{1M`vj#C&GHFkhK(%y;Go^OO0-{AT_zf7wiI zW;P3(mCeRxXM@-rY)&>8o14wU=4JD-`Pl+&LADTEm@UE& z%dzFz3T#ET5?h(A!d7LgvDMicY)!TnTbr%J)@AFl_1Ok&FzaI7YzXUNy=*8O#)h*I zY(usY+n8;_Hf5W!&DlscifzHRWTV*_wiVl&ZNs)@+p+E04s1uZ6Wf{X!ggi5vEA7o zY)`fq+nepf_GSCA{n-KRKz0y2n2lwJutV8lY#ckB9l?%d{xaj zo6M%LscafMo}Iu>WGAtc*(vN)b{adKox#pzXR)){IqY0^9y_01z%FDLv5VOy>{50a zyPRFYu4GrStJyW|T6P_~p54H1WH+&!*)8l=b{o5$-NEi;cd@(KJ?vg~AG@FRu@DQh z2#c~9i?akvvJ^|R49l_{%d-M2vJxw^3ahdjtFs1cvKH%S53mQ>L+oMp2z!)0#vW%+ zuqWA5>}mE4dzL-No@Xzx7uiefW%detmA%GZXK%1K*<0*w_6~cOy~o~XAFvPEN9<$v z3Hy|N#y)3XurJwH>}&Q7`<8vjzGpwMAK6drXZ8#GmHozkXMeCi*qqBI2XY+ zup6ow+VtSFRh^o$JB% zN*8^8_Z262PASZ)Y6lpDsyal^S0+(<5-8^w+061Xv3BA3LC<;HQz zTnd-UrE%lA3EV_(5;vKf!cFC-anrdO+)Qp3H=CQo&E@8C^SK4wLT(Yam|Mav<(6^F zxfR?>ZWXthTf?p8)^Y2(4cta<6StY$!foZYaof2a+)i#6x0~C;?dA4y`#B#6aWIE) zD2H)4M{p!ZaWuzpEXQ#?CvYMsaWbcHDyMNeXK*HGaenRqcaS^89p;X3N4aC%aqa|n zk~_tn=FV_uxpUlk?gDp_yTo1Qu5eemYut7226vOY#ogxaaCf2K8kO_x8$Sw7`_$Xns39m<=gS?`3`(Xz7yY>@4|QGyYb!m9(+%} z7vG!j!}sO;@%{M${6KyXKbVi@hwwxBVSF4voFBoD15o!`Ol8n5#PZ}JxJ=MV4)`9u6+{s@1RKgJ*DPw*%C zQ~YWE41bnC$Dijf@E7?@{AKrVxrIDJULl{5Unn3H6bcE2g(5;xp_ouyC?S*-N(rTfGD2CQoKRk< zAXF4836+H^LRF!fP+h1Y)D&t7wS_uDU7?;(UuYl%3ogMega{tND})MRLbwniG!z;M zjfEycQ=ysAT!<8+gcd?eAzFwLS_!R%HbPsWozPzBAaoQu37v&5LRX=i&|T;u^b~pt zy@ftPU!kASUlO#0kTN5yD6zUKk~e77~OpLZXl)j1|TS$wG>d zDx?YHg$cq$VUjRem?BISrU}!98Ny6qmM~kGBg_@%3G;;o!a`w@uvl0kEESds%Y_xf zN@10-T392j71jysg$=?+VUw^~*dlBdwh7yX9l}mwm#|ydBkUFS3Ht?~012>w2&jMw zxIhS`Knb+K2&}*fydVgoAPKUd2&$k7x?l*VUr7On_ag=@lf;f8QixFy^c?g)2S-l;fL^3_$B-n{s@1?Ok!p+iL%^NIPz0%Ae2kXTqOA{G^kiN(bdVo9--SXwM2mKDp1<;4nO zMX{1tS*#*f6|0HW#TsHwv6fg{tRvPH>xuQn24b-265V2m=n=hQs2C=OixFZ&v60wV zY$7%ln~BZENHI!mA+{8w#Tc=b*jj8OwiVln?ZpmaN3oOGS?nTq6}ySu#U5f$v6t9e z>?8IS`-%O<0pdV$kT_V36^DpJ#bIKcI9wbdjuhj?QQ~MZK^!9{ib>*Fah#YeriiIx znmAsZAWjq~iIc@C;#6^(I9;3}&J<^fv&A{$TydT_UtAzA6c>q$#UliJQeO;#P5+xLw>K?i6>4yTv`?UU8qeU-XHP2#bh_ikOIt zgh+~%NQ;cfik!%cf+&iTD2s}yikhg4hG>eG=ob%&2gO6;VeyD~R6Hgg7f*;M#Z%&G z@r-y@JSUzPFNhb#OX6kmig;DLCSDhBh&RPs;%)Jccvrk9-WMN;55-5~WATajRD32r z7hi}k#aH5M@s0Rad?&saKZqa2PvU3si}+RiCVm%xh(E<&;&1Vf_*cp#WtOr?S*2`J zb}2~8A?1{ENx7vwQeG*alwT?!6_g4|g{2}=QK^_zTq+@zluAjZr7}`kshm_^svuRA zDoK^4DpFOcnp9n?A=Q*>NwuXqQeCN@R9|W!1xqf;Erm!P$t#6QVN$phAvKg5NsXl@ zQd6m!)Le>`qNEm5ODS54ky=Tur8ZJqsh!kb>L7KLI!T?SE>c&io77$EA@!7cNxh{$ zQeUZ`)L$AP4U`5+gQZw$h%{6hCdEm^r4iCdDP9^Sjg}ImF;b$GB#o8ENy$=*lq#i3 z!l6SMro6@S=u6Pm9|OSr5(~vX_vHH+9U0i_DTCCp9D#;gh;4_Nw`Ev zq(n)y#7L~fNxURTq9jSOq)4iyNxEc6resNe>40=lIwT#Ijz~wPW72Wygmh9mC7qVe zNN1&U(s}8EbWyq_U6!s$SEXyxb?Jt5Q@SPHmhMP*rF+tS>4Ef6dL%uTo=8unXVP=& zh4fN-CB2s3NN=Tg(tGKH^ildGeU`pRU!`x-cj<@pQ~D+Smi|b8rq=az(k4 zTv@InSCy;D)#Vy;O}Um_TdpJ5mFvm%feAuBj(k_XC*PMJ$PeX5@?-gl{8WA>KbK#~ zFXdPAYx#}*R(>bHmp{lKQblQd}valvGM7rIj*DS*4s(Ua6o|R4OTz zl`2YArJ7P*siD+VYALmqI!axoo>E_Fpad%}#jS)W9>uGKDq%{v5}`Cy8YzvHCQ4JK znbKT|RHBp?N=qeLiBVc9t(7)PTcw@SUg@B8R5~f0l`cwGrJK@S>7n#gdMUk?K1yGu zpVD6$pbS(7DT9?*Wr#9V8K%T3!<7-rNF`nwrHob*lrc)8lBA4P#wp24iju0NDdUw1 z%0y+7GFh3TOjV{S)0G*@Ol6ibTbZNGRpu%4l?BQ|Ws$O2S)wdemMP1X70OCwm9kn{ zqpVfdDeILD%0^|AvRT=pY*n@?+m#*4PGy&}TiK)RRrV?S6`ukru!1P4f+@H{D5OFu zw8AK?!YRBWD54@MvZ5%eqA9v!D5hd5e&v92P&uR=R*on~m1D|r<%DulIi;Ld&M0S< zbIN(;f^t#0q+C|6C|8wh%5~+2a#Ojb+*a-=ca?j}edU4jPBVx{8s)bf7MKCW;KhNRn4YmSA*0X zYECtmnp@4I=2i2l`PBkyLA8)tSS_L!Rg0;`)e>q+wUk;~Eu)rI%cZ4N_0Xf;6{qb90J>R5H0nyjX%scM=!UY(#$ zR41vE)hX&!b(%U|ouSTDXQ{K*IqF<>o;qJ$pe|Gwsf*Pm>QZ%?x?EkMu2fg4tJO8? zT6LYeUfrN>R5z)c)h+5)b(^|f-J$MOcd5J8J?dU{pSoZ5sgMe*h>EJ1imQZ5s+3Br zjLNE<%BzAZs*)S^_idR9HB zo>woZ7u8GZW%Y`BRlTNOS8u2{)m!Rq^^SU1y{Fz+AE*!2N9tqsiTYH1rao6+s4vx5 z>TC6l`c{3XzE?k}AJtFlXZ4HvRsE)ZSAVEK)nDpw^^f{j%cN!2vS?YgY+80LNXwz+ z)N*OLwLDs0EuWTOE1(tB3TcJ4B3eNlb60|W|qL!qM)y8SbT8fscrD@}}3ED($ zk~UeJqD|GNY16eC+DvVhHd~vc&DG{<^R)%qLT!<@SX-hk)s|_?wH4Y*ZI!lKTcfSj z)@kdt4cbO+leSsgqHWc-Y1_3O+D>hkwp-hy?bY^a`!$~iX|RT9sD^2{Mrfo)X|%>@ ztj1})CTOB2X|kqhs-|hWW@x5nX@2d1c2GN{9oCL$N3~yZ`yb5hxSwZrTy0aXn*xgdS*R~o>kALXV-)D9C}VY zm!4bCqvzH0>G|~ndO^LAURW=p7uAdD#q|<;NxhU_S}&uQ)ywJS^$L1Ly^>y8ucBAg ztLfGC8hTB=mR?)0qu15z>Gkynda&-&-Fk@b(Y<=89;S!u5qd+tk=|HuqBqr>>CN>> zJxXt(x74Hc7`>I=T5qGb)!XUq^$vPRy_4Qq@1l3ryXoEa9(qr`m)=|NqxaSO>HYNq z`ape{hv-A~VS1cCTpyv2)Z_J0`e;2tAEPJgN%~lQoSv+w=&5>|K3<=oPt+&r zll3Y3RDGI0U7w-P)Mx3l^*Q=neV#sFU!X747wL=jCHhi*nZ8_Kp|8|e>8tfM`dWRR zzFyy;Z`3#GoAoXFR(+ekUEiVa)OYE-^*#DteV@Kx_vw%h>xho(n2zg&PU@6S>x|Co zoX+clF6xpl>x!=Gny%}HZt9lq*AM6i^+Wn${fK^4Kc*koPv|H0Q~GKBjDA)>r=Qm^ z=oj@%`epr!epSDwU)OKwH}zZkZT*gZSHGv<*B|H)^+)<+{fYimf2KdzU+6FOSNd!H zjs8}Dr@z-f=pXe@`e*%%{#E~`f7gHLKlNYwZ~c${*T`gKHnJF5jci7CBgn{M_ zxs5zVUL&88-zZ=dGzuAojUq--qnJ_LC}ET|N*Se%GDcaWoKfDWU{o|J8I_GHMpdJl zQQfFv)HG@twT(JPU89~+-)LY28!p3bgcu&fYlIqMMz|4SG&C9+jg2NoQ=^&D+=w)y zj21>qBie{DS{bd4Hbz^cozdRtV01J(8J&$TMpvVo(cS1_^fY=Iy^TIbU!$MV-xy#F zGzJ-ijaXxdG1M4l#2Lel5ynU(-WX+!HWG|6Mxv2qj5Wp?$wrEiYNQ$CjS0p?W0Eo1 zm|{#drWw|W0SGj*kWuowi(-v9mY;$m$BQ}W9&8d8T$>N0U5A?7^s06xIq}CK^e5c7_7k= zydfB(AsMov7^and+toHouFXN_~ldEHm(>~jcdkr+x?i<#BTW@a~o%p7J;Gnbj$ z%wy&?^O^b00%k$8kXhI)Viq-vnZ?Z#W=XS@S=uaPmNmzVb<24=A7GTml~=`p=#s2OI4n-OM1vys`@Y+^Pwo0-kcNHfZ8 zVYW1*%^0(l+1hMlwl&+C?adBmN3)aJ+3aF=HM^PJ%^qe?vzOW1>|^#d`t+ zkU7|lHHVl(&0%JoIoup!jx^)VQRZkf!5m{Inn~tZbDWuMrkJT_nmOK_U`{kAnUl>a z=2UZSDCBLHRf7#ow?rJ zU~V)wnVZcm=2ml?x!v4h?lgCqyUji3UUQ$h-}IS~37d$CnwW{3gh`r|Nt=wxnw-g- zf+?DkDVvI^nwqJbhH09X={FCU2hBs~Ve^Q2)I4S$H&2)+%~R%S^Ne}cJZGLaFPIn2 zOXg+sih0$%W?naMm^aN^=56zidDpyW-ZvkZ56wsBWAlmm)O=<>H(!`9%~$4Y^Nsn| zd}qElKbRlQPv&Ryi}}_3W_~w+m_N;5=5OhVb}PurVdb=PS-Gt| zR$eQgmES606|@Rjg{>l1QLC6$+$v#}v`Sf}tuj_wtDIHds$f;LDp{4SDppminpNGZ zVb!#1S+%V?R$Z%}Ro`l01zRr5ZG~7K%WH*NVOF>mVKuZGS&gkGR#U5))!d4-qO2BH zODo!nv07QJtu|I$tDV)}>R@%WI$52qE>>5oo7LUwVfD0nS-q`3R$r^1)!!Om4YUSX zgRNL=h&9w2X2n^#Yse zMr)I`+1g@lwYFK?tsT}*YnQd#+GFjt_F4Nap9NX4g;=PCS-3@5q(xb@#aOJxS-d4! zq9s|frC6$^S-NFdre#@v>wtC8I%FNTj#x*nW7cu&gmuz7Wu3OpSZA$s)_LoKbw)#qdSpGeo>)(UWxclESZ}R& z)_d!N_0jrdeYU<>U#)M}ck74s)B0uow*FXu{h9ok{aO53{n`B4{Xza5{+#|?{@ngN z{=EKt{`~#|{(}BO{=)tu{-XY3{^I@;{*wMu{?h(3{<8jZ{__3`{)+xe{>uI;{;K|J z{_6f3{+j+;{@VUJ{<{8p{`&p~{$Rh$@AilIJ$|o0)F0*#_eb~}`WyKh`5P{uch0{%C)Uzm>nWzm31Gzn#Cmzk|P{zmvbSzl*=Cznj0izlXo4zn8zazmLDK zzn{Oqe}I3We~^E$Kh{6QKh!_WALk$LAK@SAkN1!AkM<|{$M_TdN&d0^asFg~ia*t# z<{$5$;GgK9j&rU=xq%GH5jiGy zXmV0wj#x(qMGi@h8y^=G8xT1nlSU*Z#*NMq>&QGUhQ=okO&c>jA#P%xq5o@VYcVV- zHQP}8C#dDnSle9CFh{hsZO5kOhz1~W03_N0i3^C_(e`_>Lx;vCrsj^zXy%9p&~c6o zigql<1w@`!{{zo6;(zUItk^S1ks zBY8*vA7w;xTwG#8Y~rx^p+W6qho+^*1tkPT-uC}}F5y2)PiCfCh;GhXDFKnE+y7FR zC*^43cUG?P(x0sHPK2glvQ z!F9KYa;~>%=}6$WJ0>Kchk$u2B-jP?kZdE9l12k1Ca0a8A#n*w6SAi!B_^fh8x|jz z9G4QG63}u-CX5{!8))W=O-xFSONfh)%^N*7CEiYC;BwCBRB*qIo$tVL-an5DfCvBSxnnaDkvHx?5#_WUanh3>PoPya&AI>Y)xgE< zf%mfwu^-PqB0ae5!{QQBV{-so*(TdJvZmim4TP3%Ec@s{XbFMP94Dh%WJyfRGBG|! zk`rIno9ysnIvJEQf#yPk||-i%ggMk_3% z6`s+G_}2=}Knr#J@dUTbxc3js<;k$?%CP3ju;$9J=FYI@&hXg%&q}a6!%ugHJ$Htm z?hMQB49o5e%OM$-LozIfWLOT#$VEs7Mo0!mNCrko21ZB*Mo0#RCj-Nif#J!(@MK_k zGBEy^7f%L;Cj-Nif#J!(2+i;+G~BO(JM zA_F5L10x~>BO)wNGahe-wXpwL`-f#$3>dkt;D2{r!T+#a!T+#a!T+#a!T+#a!T+#a z!T;iM1^D z5CLM31hGeg*dqacB#1o{;70=dNPr&+avur&js*CT06!AoM*{pvfFB9)BLRLCz>fm> zU@zr~0{Bq?KMLSS0sJU{9|iEE0Dcs}j{^8$wd9Ec_)!2q3gEW@_$>f_3lM(`fZqb( zw*dGp0DcRA-vZ#b0Qg{4<^iiRPYVz~Se<#m>eAB!;Dgng2XLwfaH^*zzz3^NPfLLR zk84{3{FVT}CBSb9;%^D!Zwc^Qg7{m4_*(+}Xb^uih(8*{9}V!MLHyAGKN`dz4e+A@ zel)<32KdncKN{di1N>-!9}Vzh0DcU>j{*2#_3DWM_%Q%K2H?j4{1|{A1Mp)2ehk2m z0r)WhKL+5(IQU+$I`)Fqu{YSk_Xa!hdxIT(Z?J>!4R-Lo!4AGR*unRLdDa{3;Cq7| zd~dLW?+teFy}=H?HyGf%ocO&iCw{LB;JY0B=#ap3u4uow|2LJ9TeMfbZ13tpj|g?rk05J9TgC0N<&5 zTL<_~-P=0Ack15O0lriBwhr>?)V(X(se4-n@@`9j@6^4m1AM3MZ5`k{b#Lnc->G|B z2l!6i+d9B^>fY7?zEk(EXs7ON3Gkh|w{?(zr|xYXfY8t{++tFb&!9j z?rk09->G|7v{U!C1o?OB-qu0m$L;yWd$L?UT zy0Ve~*>Z*EOG_LE4h)A4Nys-Y&E80-9}6a@#0|@zIw8s4f!jw_ak2J8>EXMB-M}3; zfOr3c_k!hfsLLDX2^tfhn0~C45;rs{ahUUF2nZ}BDp%aZq4wU<1_XBk1Hc_@FS`>{ z(#FOm$0q?aXW3)>3zFq5du$yfD>69W&?I}mKgK@0OpQ&R}c3&rg8xEAS;&!{76}K&&)VrJ&x2-#=cR4F=TX)8(%UN;TItbQTaof5x zP+ZQ6+wF2z)V2g6IV);gchcx`R@Al*(&((HZQU7oE@wq;>-G*MIXP)ULfr7w9D!Ea z*xUh`oF21tb6C=ZM5i^xPKwh?8#^p;oaS~p3szeK)6Rm`)}0b_ISW=>4@?}k3`|_M zbVi8FS+Lr=GeTSrak*U%aoG~2&LJ*a2dM+2(d7`AeLXBM2w+&^kd!#59~}0wZ-V?e z>}Bh~35UIG-6R9k}6;nA_!$m@Pp!J0xc7pqm{MvvttT4vE=1=nIF$Y#sE4 zLt?fL`obYGTL*pNkeIE&h=z%Pf~Y#sRJkejUozZ`P2b>NpnZnh4_h(m5}mqTv01Y^V@H(Li|#346Z2j%6E zo2`R>amdZqLBBZUX6v9|9CEXD&@T?Txm^yq*%I`Nv#7Oo&@T?X**fSKhu&-*#P86X zt%LZTMXjxa_?<fj;Crr27n>!(CMEua$^v^K4Kr<;hZ+xO{ z&t|Q3BiU^*`+Hdc((RD=ibDr^o(NK47@G{>YRq{fd; znB=@25s}Y+aBN)q_o}pvxa>l=7YFHi4?GnD))XONeG&rJCn3&m!yV%69^4^do&qO3 zZg8?=OQ#UP$&Rf%g#b==Y~9JP2V}+rcm|y8*!P`604F=P?qnI9?AW?f2;gML?EyUG z0X*aZJmdj92{McI-8Qvnum| zC5{IyaXes&;{i(?k8`qPKM&#$cM8$tobcG!okH|DCp@<96r#sD;jwk65IxQbkF7fs zktYJ+M>rFK$2r|`dz{l9TY~&Mr#rR|@BvwR09kqfS$Y6jdH`8^09kqfS$Y6jdH`8^ z09kqfS$Y6jdH`8^09kqfS$Y6jdYoOJ+vDu=Yzg8AJm~>E=>a_H0X*peJn8v2{s2#U z08e@VPlD4Ow+AfeJYYHJadt&+a2jGuFkYQqk*$M#0pjog;_v|C@BrfQ0OIff;_v|C z@BrfQ0OIff;_v|C@BrfQ0OIff;_v|C@BrfQ0OIff;_v|C@BrfQL`UT87Gt04{@ak) z#|!Bf0#^c+49*wa;C#WB&I||67i`@bf#7_>)}0;z=L@zTWgE128~;Oah9Ed#upfv? zf586w=bRk4!3l#cot%1IA?Xj;dz*|0fD0~X5PDr<=?~anbuu0RKDeAJ^tu8*g#YIO z;DgI4L$Avz1FzdD120%%d)>eVcff^+{~2%t7u>)Fuo3dQ9YXfHfd^n?p)% z;DH->;07Lq01rZ(LiC0J4?+SSg#FI|U>YxA8n4YX>3rf10Um?^4?;i=LINJx$4LK@ z6aqYO4ruHbfd?Mo0oW~hJ-`ExLzZ5TGqk-PkOB`#fd@DM_BUP+aKHl`@T5E73G}nw za0j}}Zg>MjBfSymVY?9$$aDHL|E{?IJ!6j+`(OIQ=`y`x&s z$h+ML$!Pq$<@pEU`e!F_A>Fe3pQ|=G0IYwOJ^%c%zm|Yo|11Z>3V4_iV1}Llj8Nwb zgWC&u$m@JzaC-p{c>xc30T0>RX{UDq4|xGkcmYp%ZJw}q3kgX>1E0&?UceJxz!To^ zuFnN2y{YyAQ{uHZ zC4u{3+L8(E+p&kXbOJd!2JGw-?N)UNED2 z!HnvS_U5)1SBXQ??1)nxOJFq}3Z}+Ta99)S9HH8y&#A0Xm$L{6bwvjaxBp92LR_}= zpMZ@}uTxc_UZ<);y-rnydY!5Y^*Uu8>UFv<)az7LsMjI*P%p6Ub-FIp>&z>mUc2kY zrgvR>mxY3Dd}ydU+t7rxA>g7X`^dQ1jN(wRnh6D?G!%@|P_UW_1*@4*FiOLmGh}y|%Sl3*%Sl2QnBc=)PViyQ zESp|W>G-w`Op>+?Oi{KB^rbBW@7pqvL|X=8v1P!HEdxokWgv;R3?$K(fp}~gNTMwR zNwj641MQz=Zs_W!!4?5O|!9{c6BUv7^QulxVM_RH)4|Jg5}_v5o)KKb|H z&i}gycPwqkJ-DOxzTAU5YWerzj@tWj5ALYtfAa5Ls{Fruc*j!yy-O9f{dn(EMJ@k! z4^hj%cd4S5fBUSc<=^8xYWeqg&;Pq;?O59Wd)AKH_TRI1)b{`F9-_AYZ}$+j{eQcM zsO|sTJw$E)-|iu5+kZcA(R9hbpSP&x-_Kjr@~;X}%fH=2)bg(jQOm#GL)7xG3sK9z z-9t2c@^AMLwfrkZ)PDc%4x;w^Z+8&2-+#M<=@+7QOm!#%c7QlZ5?Z4ek)VBY2H&NUE+ucNM`)_v>we7#%P1Lskb~pKdx4Vg@?Y|aB zZToL`6SeKX-A&ZC|K1jh+V9xgpx zdbsp(>EY7DrRVyZpGW@fo1(tk^V|!UsPFbX{CfEH+_ouh>fHVHaO~k&eCKihe;j)_ z_HgXs*u$~sHb42f%}*@vet)vxb9S%Ve>nPZ^x^2k(TAfCM<0$p9DO+I+n(&(o>f4@J?>OS9 zZ+l|B{m;>dqYp=Y8>o1+g$AC5j8eK`7X^x^2k(TAhaa{m3k_)^yLZg+A$o9?&4IXQnM z=a1;L{QTYSqU3PxU&STm6Df|CtpTq2Pn0*f87S7G{`O7|k z+2=3&{AHiB>~ofV&a%&0_BqQwXW8d0yIzeOOgG2%YIePvU9V=>tNGsT&iUT$j-~x@ z?{-Ih&vV)JY<4}HUC(CMv)T1*zIVHGzIVH0d5@3m`Zjw%v-dN5KeP8Udq4BP_tW)l zc72;&-)7gh+4XI9ee2tnc-w5h>)Y)5HoLyfu5YvJ+wA%_yS~k?Z?o&$?D{slzRj+0 zv+LXJ`Zl}1&8}~=>)Y)5HoLyfu5YvJ+wA%_yS~k?Z?o&$?D{slp3Sajv+LRHdN#YB z&8}y&>)Gr&*7E^5yME2CSF`KYe6LsI=KtURi<|X-=c%|Ee`{mjqW=3F#q!V1SpI#^ zV)^gq|2|hy{~b@U{QLaH^6z+x<=^=;mVckOSpJ=FPQT7Z{de6G%fItYdc9Nc z@AXjB^6&XAYWerNF>2d?uN&jb7TbTX8>5zguN$M5f3F*(mVd7sqqhC`JQ}t9d)*kd z{Ci%FTK>Ime1G@4F_yOfUN=VV_`BDQQ9J(bbz{_yzkA&nwd3zzH%9IFyVs3T+yDRe z)4gttFPCip^)_nTf4z;`_Fr$KF8`f>V=4c78@2p<-59m}>uuEX?{#C;-v8@ue7R-& zueVX#{_Abjw*Pt?wfyUC)bg*lQOmzxMJ@k&6}9~9R(z=@|GE{m{OeZKfBC21^JUcX zuV+!qzn(=c|9Td+{OeiN@~>y{WuE-&S=92cXHnb!>si$DuV+!qzn(=c|9Td+{OeiN z@~>yFF}ktz{$J0c_WobbqW1n@&!TqxU(cd;{9n(acKlz@qIUe>`!%mIy0NtV*R!Z? z|Me_t+kZWa+V)@1qPG42-k# zZmgGo@7KJ>=*Ck1y-D?;5C0GU5C0GU5C0GU z5C0GU5C0GU5C0GU<9pzn$NzKvf8(2H>+kV*_<#6+_<#6+_<#6+_<#5x-*(^M$N$6s z!~eto!~gg)+t17A|KY#Um;s4?P z;s4?P;s4?P;s4?P;s4?P;s4?P;s3e)?0fis_<#6+_<#6+_<#6+_<#6+_<#6+_<#6+ z_<#6+_<#6+_<#6+_<#6+_<#6+_<#6+_<#6+_<#6+_<#6+_m{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZCI>;Hdafd2cO#PZMoSpJ=VWBGUf zjpd(zvHbh|#q!UeSpI$fWBKP_EdS2`vHUy#$MWy<7t6oTUo8LpkLBO_KbC*{KgR6I zzy3!p|N0-b{Ofwnbpum4fYzx{vI@~{6< z%fJ19)bg+YQOm#nM=k&QA43%7U;m?)fBlbI{`Eg<`Pcubwnbpum4fYzy3!p|N0-b{Ofwnbpum4fYzy3!p|N0-b{OfwgTnm4E$@TK@GvYWdgysO4Y(qn3aDk6QlqKWh2c z|ET3(|6{1H{Ofwnbpum4fYzy8Mva{1T)sO4Y(qn3aDk6QlqKWh2c|ET3( z|D%?F{f}Bc|6?@ppZ_r@isj9Zhd$Le-}sLh@qF`R9u%+Nd~=}0>o?!{kEn0H@gGs& zeB(c2z`T6^8~+jO<@4Y8k616C|HgmB`g{HH@Zb24STCRd#(%{6d;MYjM-14v{rorn zBi7q~{u}=h>uo>(jsJ-Cwx9nG{~!K8{D1iW@ZY$RcwoT(=f80yvHo6v88;HOeEvWD zH*O?ezt>+6|BV}o_4YsijT`xl8;PZS{u?(E>*e#`xRF@z{pY`NBe7mS|BV}o_44^| z+{kC#NG#>^-?))jFQ5O$jl_ES{5NhS*30L=aU-!_KL3py`HUNhrF{N-N;uZb=f9_f zW4(O-drCOg%jf^Yf8$2tb;lq7jT?#QLFDt_xRF>dpZ~^<#CrMsH*O@>%jdswBeCB0 z^WV6U&$y9T%ICjvBe7mS|BV}o_44^|+(@jK&wt}aV!eF+$FmFfJi`Bn{}2Bk{y+Tp zsPpGh=UB?;zek;8y?p+A)H&A6=f6jtKaV=c()RP;qt3D3_VYg;b-w>^{y+Tplym&N zeEvWD_mp$I?)~S#r<~()ANl-`r<`vN{~!K)yg7c}_VeH4&9PoS{~!K)yg6Q%&wr0M ze-HmX-W;`j{y+TpcyqiipZ^d4J>DFzJOA?E%#X2NKK~#7n+fuJ_-{tYsO^9LdyF~O z%jf^Ye~&T8>+<>k@ZV$1UrhFL_b>lF#vJSU!~ci>9%GKzz5o1w`0p|1c-{Wze@qS{ z$MMI1k1@wnT>Rm`$CzWi{m=i0{~lwG*KI%lAO3rcIbOH@{D1f#k1_xKe~jOX2bgYt z%!^_D-G1Y@;`O`#jo*s;Zoe5gqQ3qbzZLb}e&e^IzT0p7R@CzOpZIV5Ry@TfpZ~^h z#ro^N@mo>f?T@(&?*E(r@f^SUZht(tuD<&}p5wl?{y(00{7?K(`v1iL z#Q%7f?f!rC|B3&J|4IL!_#clS`+4s_|KqV*>*e!5@jo6b^}6lne>@d;*X#ci|KpiH z>+kWO_#cm*SudaeiT_FepZK5npY;FnaFl;<|Lgyg{@-}J&v?67%IClFcClVQ|Bbhc z^|qh?#@oeu+pqsO-tIHrE|!iz{u^%>>*e#`c)M6HpZ~_&#d`VtPyA2%|HOae@ZzCQ z`(OW`_@DTn^#6(fiT_FepZFj1zuf(*|4;l+{7?G-#Q((q#Q((q#Q((q#Q((qr2kL) z|HS|Q<_P-lK4!Z8e|#-qKkz^4{}cZc|C9bd>Hib|jZgfHPmHC{KmUzSjP=ex{7?G- z#Q((q#Q&uKPyA2(Px}AF|HS{q|HS{q|D^v<{7?K(`v1g#Ga1K2ypBKpf8u}Qf71Ua z{wMu^;(y|Q;(yZrC;lh?n=$z_V{$B=fBB#E|B3&J|B3%e|DX7u^#6(fiT_FepZK5n zpZK5j|B3&J|B3%e|DX7u_@DIuiT{cJiT_FepZK5npZK5j|B3&J|B3%e|DX7u^#6(f ziT`Gn{u2Ka{}ca{{y*_Q@jvlD>Hib|6aN$c6aV7@!N1Qp}8xQEH@9`H8tf=q#Hy$jxHUG`7@x=oU*2{PQKL+Cew?4)b z{(D_M|1~nKZ+vt7oP7Rg{%8JY{%8JY{%8JY z{%8JY{%809GyjdF{*0rJrT3rz##P69??3-D|Bbi)V)n4R9sJMk|7ZSZ{u`GaKkxnL zf98MYf98MYf98MI|HotXH;?~W|DXRIU;NMf&-(w&|IGia|Ihr-{LlLT%>T^)tpCsY z|IGi)|E&Md{LlQ)`v1)T%>S(a&-^!DJ*HOh{`22>^;qxo$N$WK_E>NK^WS*(Sa1LH-+1;|Z~yb(c=peD_E^g2zwzv`UOxYgXOH#r z_5a!Z|IB~m+vDeKKmUzy|BP>srO!YAGyjcqkJsh%-+1>}FQ5O$zsGvp&;QK-%>T^) z%>T^)%>T^)%>T^)%>T^)%>T^)%>T^)%>T^)%>T^)%>T^)?EZh|f98MYf98MYf98MY zf98MYf98MYf98M8>+yH~d-?zB|6lk2U;e-P|Cj%-{{Q9wtN(xb|LXr={>R@9kn{Kc z$8el`9K>`acRl}K-~Ye-fA#+_|6l$8>;C_%|9|=a^8e-k%m0`EFaKZu$0RB@pZ~A> z|1bYv{=fWx_5UybU;e-RfBFCF|6l&U{D1lX@;@e(x%vEm`Tz3&<^Rk7SO5R=|KMze*OQ;|Cj$S|6l&U{C|D_|MLIk|I7cE|1bYv{=fWx`ET~N&+KinyyqXYw?%!= z|7LHC`ksHx-WK(}{xEx6)Yl)gw?%!=KW1->+Huc+v$uU_Z;PdT{+qom*5B)Iv$sWk z{V{u6)c5>n_O__6KW1->+UHOI|MK7LZJ*iOVkw{hW^ar2^7;Sr-|TJix_thdy)D+; ze*T-iE!JOu%-$9=E?s}k-WK)s$LwuU%h&(E{5N}Bye^;rW^ar2^7(J}wpcG;|8MrT z&+Kinl+S;&x5awzKmX0%7VG8f|IOYO>*eeJ&E6L4|LXr={r}7Vm;bN+|K{S=THA{Ho90Z|DQYjj|b82`G^1U zV3ykc=l{$9cyPz-^7$VRlvppH|M8%M_44^21Mu&9{=fWx`5yziy>9#Y|MLIke+bw0h&_sRn zW8j7Q=Ep#UTk}7@kXOs+e|%wVy?p-17n#<}=YM>$X1#p=#}`4?%jbW55pmb^-?VvO z+(@@xKL6uJuJ!WyZ`Qo{ck=lkH^BV7eE!Fcox7g@al=Dx`}rRYv-P&0|K`m5qLK2t z{m=inz_(sL|3ChlGVlBN|LOltnHTGAKmSdc7wc_5|Kmh{bNC-8Hnn{I#|O}Q`=9?G z|3Ci64!l3d{|Hpue*S;_|M>s$|KtD1|BwG4|3ChJ{Qva-pZ@>j|EK@|`2XquKmLFE z|BwHl{{Q3ur~m)>|LOnDn)jJCFE+#Rr~fx=UaYtO_5UCLKmGs5|4;w_@&D8RfBgUS z{~!N9{(t=cT<@8cFLtN>&;L*VZ&to|-TTk~&;9?8{~!PVo0spu`|%(DKmLF2|Hr(1 z_jUdM$N$g$|BwHl{y%2w^Yilg|LOlf{(t=c^#33KKmLFE|BwG4|3Cfzr~m)>ZvwzL ztmO0G1c0&L@z4K{|DXQ<s$|8xJ}OoDNEJOA+iiSrNtKmLFG|M+ia!uWal{Qvm>@&D8RfBOHA{~!N9{r~6w zzqt$JW9s|MCCR|9|}d`2X?$j z|HuDN{~vRI{(Y_r|BL>=@W1H)O%NHU%lr8&{4e_d!vCWGFZ?h1|H6M0OUCcHpZ~)D z!vFXq6Zika|HA+HV;t7Y=YP@v7ycLi7yZA9E93Nj{fP-I{T})JFZ?h1|HA*G|1bP6 z{4e_d!vCWGFZ?h3FZ?h1|HA*m|HA*m|Dyjd{4e}3`v1cJqW>@aFZ%z&|HA*G|1bP6 z{4e_d!vDhm;{Jc(f8l@8{}=uj{ulli{ulli{eR(q;eXNp7ycLi7ycLi7yg@EH2C%Y z^WWs6vHqTaO)eVsz5Xz{Xw>)mr|{q8qOsol&wta42JenP{{Q#4X#R7o@W1fC@W1ds z<{0&J^7&u*U-)12|K=Nw%Lt!8{ulj!;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;lKG# z<1*%+|IK$A^*#TY?=))rpZ|sbh5v>Bh5v>Bh5v>Bh5v>Bh5v>Bh5v>Bh5v>BMgL#; zU-)16U-)16U-)16U-)16U-)16U-)16U-bWl|AqfW|6llD_+R*6_+R*6_+R*6_+R*6 z_+R*6_+R*6_+Rw@h5v>Bh5v>Bh5v>B#r^-n|HA*G|1bP6{4e}3{4e}3`v1cJ!vDhm z!vDhm!vDhm!vDhm!vCWGFZ?h3FZ?h3FZ?h3FZ?h3FZ?h3FZ?h3FZ?h3FZ?h3FZ%z& z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|Ck^A@4Qv{U-@78U-@74|CRrh|CRrh|CRrh|CRrh|JC>ZmH(ChmH(Ch=2ec# z#P9jnyvk8u|11B^uN>>|`M2`F^1tf;EB`D1tNy?8zw*EGzw*EGzw*EG-+a!|u3djB z|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1 z|11A1|11A1|11A1|11A1|11A1|11A1|11A1|11A1|EvDL^1t%G>i;YMtNy?8zv}-h z|11Bi{=f3S^1tf;EB`D1EB`D1EB`D1EB`D1tNy?8zw*EGzw*Dj|6loE`Cs{8`Cs{8 z`Cs{8`Cs{8`Cs{8`Cs{8`Cs{8`Cs+_mH$=$U-@74|CRrh|CRrh|CRrh|CRrh|CRrh z|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh z|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh z|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh z|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh|CRrh z|CRrh|CRrh|CRrh|CRrh|CRsoXF>k_Z~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~Slk zZ~SlkZ~SlkZ~Fhn|EB+M{BQhk`v1oN#{b6u#{b6u#{b6u#{b6u#{b6u#{b6u#{Z`O zZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~Slk zZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~SlkZ~Slkk3Xq$k5B$L{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|-|U%tKXKmQy58~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~@`^hy8uNI{!QWJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX z{ekiLy7k`w?)>lk@BHul@BHul@BHul@BHul@BHul@BHul@BHul@BHul@BHul@BHul z@BHul@BHul@BBAUa(tb9@BerHcm8+&|9ifq^S|@I^WS{dU*~`4f9HSaf9HSaf9HSa zf9HSaf4~06ILv!LzVpBHzw^KIzw^KIzw^KIzw^KIzw^KI|KE7e|Ig!p=YQva=YQva z=YQva=l{Rw?mGWF|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX z|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zLX|2zN9 z&m6-a?)Zz&|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYt^G@*2mc5E2mc5E z2mc5E2mc5E2mgouf9U@Q{|EmE{|EmE{|EmE{|EmE{|EmE{|Env{(rdM82lgnAN(Ku zAN(KuAN(KuANv2n|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k z|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k z|DpdM{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u{2%-u z{2%-u{2%-u{2%-u{2%-u{2%-u{2%)N!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk z!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-Vk!T-ts$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^XUw#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s#s9_s z#s9_s#s9_s#s9_s#s9_s#s9_s#eaVfI3B~i-#2dlZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo zZ~kxoZ~kxoZ~kxoZ~kxoZ~kxoZ~kxo`+M#2So?kcY4d;cfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAjyR|Nrs-$NwMyfBgUP|HuCy|9|}d@&CvFAOC;+ z|MCCF{~!N<{QvR)$NwMyfBgUP|HuCy|9|}d@&CvFAOC;+|MCCF{~!N<{QvR)$NwMy zfBgUP|HuCy|9|}d@&CvFAOC;+|MCCF{~!N<{QvR)$NwMyfBgUP|HuCy|9|}d@&CvF zAOC;+|MCCF{~!N<{QvR)$NwMyfBgUP|HuCy|9|}d@&CvFAOC;+|MCCF{~!N<{QvR) z$NwMyfBgUP|HuCy|9|}d@&CvFAOC;+|MCCF{~!N<{QvR)$NwMyfBgUP|HuCy|9|}d z@&CvFAOC;+|MCCF{~!N<{QvR)$N%5;KgI#x`6_<=|MCCF{~!N<{QvR)$NwMyfBgUP z|HuCy|9|}d@&CvFAOC;+|MCCF{~!N<{QvR)$NwMyfBgUb+aCV^`2XYokN-dZ|M>so z|BwGa{{Q&@so|BwGa{{Q&@sY|Nr>^so z|BwGa{{Q&@so|BwGa{{Q&@so|BwGa{{Q&@so|BwGa{{Q&@|KkEledqr;{6G9Z{ErF8{oK8tI{ZKU zKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%Z zKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%Z zKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%Z zKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKm0%ZKiB`5{_pSkzgYg=|BvP0^Ix(2 zoBu18f6w2>@^AjHSpI$fV)-}!V=VtZf3f`g{KfKb{wnbp zum4fYzy3!p|N0-b{Ofh`D|53}o{zonU`X9CY>wnbpum4fYzy3!p z|N0-b{Of;A?wnbpum4fYzy8OxlJc+rQOm#n zM=k&QAGQ4Jf7J4?|53}o{zonU`X9CY>wipVDgXK(wfyUU)bg+YQOm#nM=k&QAGQ4J zf7J4?|53}o{>L<$@~{6<%fJ3dE&uu-wfyUU)bg+YQOm#nM=k&QAGQ4Je@x#g|N0-b z{Ofwnbpum4fYzy3!p|N0-b{Ofwnbp zum4fYzy3!p|N0-b{Of;A3oHNnAGQ4Jf7J4?|53}o{zonU`X9CY>wnbpum4fYzy8N` zwDPb2QOm#nM=k&QAGQ4Jf7J4?|53}o{zonU`X9CY>wipxEC2c*wfyUU)bg+YQOm#n zM=k&QAGQ4Jf7J4?|53~5|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0` z|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+` zhyM@%AO1i5fB66K|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36e|HJ=> z{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci> z5C0$jKm33A|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0`|A+q% z{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@% zAO1i5fB66K|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36e|HJ=>{}2Bk z{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$j zKm33A|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0`|A+q%{~!K8 z{D1iW@c-fe!~ci>5C0$jKm33A|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5 zfB66K|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36ef8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Q zf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf8u}Qf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MYf98MY zf98MYf98MYf98MYf98MYf98MYf98MY|I7cE|1bYv{=fWx`Tz3&<^Rk7m;W#SU;e-R zfBFCN|Ks$|KtD1|BwG4|3ChJ{Qvm>@&Duh$N!K2 zAOAo8fBgUW|MCCh|HuE2{~!N9{(t=c`2X?$s$|KtD1|BwG4 z|3ChJ{Qvm>@&Duh$N!K2AOAo8fBgUW|MCCh|HuE2{~!N9{(t=c`2X?$s$|KtD1|BwG4|3ChJ{Qvm>@&Dt0{DJxZ#&yS^2fpJ^;}5c{@A%XBL*?o_ z{xtruxB8AhjXy+vYyN-y|M>s$KmK5_pS$C4;t%(#<@5jJ|HuE2{~!N9{(t=c_#c13 z^8P;lfBgUW|MCCh|HuE2{~!N9{(t=c`2X?$s$|KtD1|BwG4 z|3ChJ{Qvm>@&Duh$N!K2AOAo8fBcU>NOAWI|KkrXsO^9LfBgUW9~0pFIqyIJKmLFG z|M>s$|KtD1|BwG4|3ChJ{Qvm>@&Duh$N!K2AOAo8fBgUW|MCCh|HuE2{~!N9{(t=c z`2X?$s$|KtD1|BwG4|3ChJ{Qvm>@&Duh$N!K2AOAo8fBcV$ zXYTvN|BwG4|3ChJ{Qvm>@&Duh$N!K2F;Rlw>;32d$N!K2AOAo8fBgUW|MCCh|HuE2 z{~!N9{(t=c`2X?$s$|KtD1|BwG4|3ChJ{Qvm>@&Duh$N!K2 zF%bW6KgGa!%RBxs28yfi=Pw3MtMBJ81`@09`7Z|6s_*$X2AZnx{*Qr|>U;i;frz)} zf8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@O zf8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@O zf8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Oe*k}v7ycLi7ycLi7ycLhf8l@8 z{}=uj{eR(q;eX+O;eX+O;eXNp7ycLhf8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@O zf8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@Of8l@O zf8l@Of8l@Of8l@Of6@OJ{ulli{ulli{eR(q;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O z;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O z;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O;eX+O(f=3z z7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7ycLi7yg&)e>@(`W&Rib z7ycLi7ycLi7ycLi7ycLi7yeiNS2U-@78U-@78 zU-@78U-@78U-@78U-{qo-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7-}vA7 z-}vA7-}vA8|L;CS=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQwF`HbVX{r�^S|@I^S|@I^S|@I z^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I z^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I z^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I z^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I^S|@I z^S|?d@PFw42mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5EhyH)?fAD|sfAD|sfAD|sfAD|sfAD|s zfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|s zfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|s zfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|s zfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAD|sfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9w zfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAW9wfAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3u zfAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAN3ufAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy zfAfFyfAfFyfAfFyfAfFyfAfFyfAfFy|HuCy|9|}d@&CvFAOC;+|MCCF{~!N<|3}lE zZkb}n+{Y5q#R8)YT7P{4BZ5V!`<=l|vZ<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX<^SdX z<^SdX=rvLN*+xkEMzpelC|J(XM|G%yO^Z(oWKmWh2|MUOb`al1_ zb7lHJ|G%yO^Z(oWKmWh2|MUOb`al1_t^f1?+xkEMzpelC|2vbW|MUOb`al1_t^f1? z+xkEMzpelC|J(XM|G%yO^Z(oWKmWh;Z2CX{zpelC|J(XM|G%yO^Z(oWKmWh2|MUOb z`al1_t^f1?I}4}(^Z(oWKmWh2|MUOb`al1_t^f1?+xkEMzpelC|J(XM|G#r|`al1_ zt^f1?+xkEMzpelC|J(XM|G%yO^Z(oWKmWh2|MUMlgQx%V|J(XM|G%yO^Z(oWKmWh2 z|MUOb`al1_t^f1?+xq+u{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO{15yO z{15yO{15z({Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P z{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez&P{Ez%k z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K( z{7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?K({7?MP{LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlO^{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e~k{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC4~{vo0NzaRYm`Q_jHqssrv|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|L-B*-`~&oFwXb){@{BkX8+zFd=IDW z-}{5_A(8$2`}-c&*uVD&-$N7o_x|8}cwztEAAAoHew+V||Be5R|Be5R|Be5R|Be5R z|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R z|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R z|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R|Be5R z|Be42K)?S#^S|-G@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE z@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE z@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE z@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE z@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE z@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE@xSrE^S|@I z`~Bbf-~ImY{O^AMcm8+3|2zM?-~XNe-S7X-|L*sH=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva z=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=YQva=l|gU;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N z;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!$N;Q!?R-~FHO`*;86`~Kbk z`M!Vmf4=YE{h#mqcmL-*JMh>4zJK?JzOVkz|8ML6{QtK8&;M`h|NQ^9{?Gq!>;L@! zw*Jrm@7#g@&;M`h|NQ^9{?Gq!>;L@!w*JrmZ|nd3|F-_m|8ML6{Qu4@=>Poxw*Jrm zZ|nd3|F-_m|8ML6{QtK8&;M`h|NQ^9{?GsKyo3JF|8ML6{QtK8&;M`h|NQ^9{?Gq! z>;L@!w*JrmZ|nd3|ISM2|NQ^9{?Gq!>;L@!w*JrmZ|nd3|F-_m|8ML6{QtK8&;Re7 zh5pa~Z|nd3|F-_m|8ML6{QtK8&;M`h|NQ^9{?Gq!>;L@!&S>cW{QtK8&;M`h|NQ^9 z{?Gq!>;L@!w*JrmZ|nd3|F-_m|L=T<{?Gq!>;L@!w*JrmZ|nd3|F-_m|8ML6{QtK8 z&;M`h|NQ^XhUowN|F-_m|8ML6{QtK8&;M`h|NQ^9{?Gq!>;L@!w*Jrm?_7!g&;M`h z|NQ^9{?Gq!>;L@!w*JrmZ|nd3|F-_m|8ML6{Qu6R=>Poxw*JrmZ|nd3|F-_m|8ML6 z{QtK8&;M`h|NQ^9{?GsKJd6I%|8ML6{QtK8&;M`h|NQ^9{?Gq!>;L@!w*JrmZ|nd3 z|IWhb|NQ^9{?Gq!>;L@!w*JrmZ|nd3|F-_m|8ML6{QtK8&;RcnjsDO7Z|nd3|F-_m z|8ML6{QtK8&;M`h|NQ^9{?Gq!>;L@!&fw_({QtK8&;M`h|NQ^9{?Gq!>;L@!w*Jrm zZ|nd3|F%B=!_WWk4A0Ns!2iJi!2j_3{r~-Uzkc9<;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM;D6wM z;D6wM;D6wMs$|KtD1|BwG4|3ChJ z{Qvm>@&Duh$N!K2AOAo8fBgUW|MCCh|HuE2{~!N9{(t=c`2X?$s$|KtD1|BwG4|3ChJ{Qvm>@&Duh$N!K2AOAo8fBgUW|MCCh|HuE2{~!N9{(t=c z`2X?$s$|KtD1|BwG4|3ChJ{Qvm>@&Duh$N!K2AOAo8fBgUW z|MCCh|HuE2{~!N9{(t=c`2X?$s$|KtD1|BwG4|3Cg8{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy z{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{vZAy{@*`H{`Xws;s4?P;s4?P;s4?P;s4?P z;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P z;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P z;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P z;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s4?P;s1I5-^c&{p8vhq zzvut&_3!!rd;RwYv;OD*&HA7JH|u}?->m=nf3yDQ|IPZJ|2OM@ z{@=$P^*{e_*8lv!S^x9@X8q6qoAp2cZ`S|(zghqD|7QKq|NEGw{^$SA`k((d>wo^= ztpE9cv;OD*&HA7JH|u}?->m=ne;@DE|NOsM|MUN5{m=iK^*{e_*8lv!S^x9@X8q6q zoAp2c?_;Iwo^=tpE9cv;OD*eVkSQ^Z#c3&;OhC zKmTvm|NOsM|MUN5{m=iK^*{e_*8lv!kJ0LX{@<+s`G2$i=l{+6pZ_=OfBxUB|M`Ej z{^$SA`k(*z@m>AT|C{wc|8Lg+{J&ZM^Z#c3&;OhCKmTvm|NOsM|MUMoHmv{of3yDQ z|IPZJ|2OM@{@<+s`G2$i=l{+6pZ_=OfBxUcmGwXWZ`S|(zghqD|7QKq|C{wc|8Lg+ z{J&ZM^Z#c3&;R?FwEpM+&HA7JH|u}?->m=nf3yDQ|IPZJ|2OM@{@<+s`F|hJ*8lv! zS^x9@X8q6qoAp2cZ`S|(zghqD|7QKq|C{wc|Lwo^=tpE9cv;OD*&HA7J zH|u}?->m=nf3yDQ|9u=?|MUN5{m=iK^*{e_*8lv!S^x9@X8q6qoAp2cZ`S|(zmLJ| zfBxUB|M`Ej{^$SA`k((d>wo^=tpE9cv;OD*&HDKt_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm z_#gNm_#gNm_#gNm_#gNm_#gNm_#gNm_#gNmp8xmu&2uR5Kkz^BKkz^BKkz^BKkz^B zKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^B zKkz^BKkz^BKkz^BKkz^BKkz^BKkz^BKkz^DKk`5FKk`5FKk`5FKk`5FKk`5FKk`5F zKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5F zKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5F zKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5F zKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5FKk`5F zKk`5FKk`5FKk`5FKk`5FKk`5NcQ^7s@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF z@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF z@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF@;~xF z@;~xF@;~xF@;~xF@;~xF@;~xF@;~xFI{!!K|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky z|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky z|H%Ky|H%Ky|H%Ky|H%Ky|H%Ky|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q|HS{q z|HS{q|HS{q|HS{q|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi)|IGi) z|IGi)|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m z|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|HA*m|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$ z|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|H}W$|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u z|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|Hl8u|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|IYu; z|IYu;|IYu;|IYu;|IYu;|IYu;|IYu;|NA1IfA874e}?S`+x2W5ajp%pTYmZ|H1#k|H1#k|H1#k|H1#k z|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k z|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k z|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k z|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|H1#k|No!69{eBt zAN(KuAN(KuAN(KuAN(KuAN(KuAN(KuAN(KuAN;@n@VtKy_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@nS z`9JwTo&P8Qr}O{h|8)MJ{GZPMlmFBCfAW7i|4;r;=l{w7$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg! z$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Xg!$^Ys6KlwlT zKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlT zKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlT zKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlT zKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlT zKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKlwlTKl#7-zxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoS zzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoSzxcoTzxluUzxluUzxluU zzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluU zzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluU zzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluU zzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluU zzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluU zzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluUzxluU 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 z&;OhCzw`gi`k((d>wo^=tpA<=Z`S|(zpt~>|NOsM|2zNRtpE9cv;OD*&HCT@|7QKq z|C{wc|8Lg+&j0r{TKb>=H|u}?->m&o;$|8Lg+{J&ZM^Z#c3&;OhCKmTvm|NOsM|MUN5{m=jV znl$~-|C{wc|8Lg+{J&ZM^Z#c3&;OhCKmTvm|NOsMKmP;&1OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg61OEg6 z1OEg61OEg61OEg61OEg61OEg61OEg61OFrcBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+BmX1+ zBmX1+BmX1+BmX1+BmX1+BmWcs6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c6aN$c z6aN$c6aN$c6aO>+GygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOHGygOH zGyes8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s z8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~+>s8~;21JO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO4ZX zJO4ZXJO4ZXJO4ZXJO4ZXJO4ZXJO2m&2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E2mc5E z2mc5E2mc5E2mc5E2mdGkC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^ zC;un^C;un^C;un^C;un^C;un^C;un^C;un^C;un^C;#ssV*U5`Dc1eNsIKq*bN3I0 z%J2Pi_YY^v@BMT44@t`J{d4yZE6VTvbN3Go%J2Pi_YdyL@BMT45752L|NDp8Wc~cV ze<;kle*WJ-U}arD|L-4GvaX;1_YeM9*U$g^2Wj3q|L-4Wk$rytPyXLO-zfdi(=*1e_xy`zt4YPBr3oD`(jP`_1_mw%J1{v7ca_h|L=?wg{%`01&HwHEzxltN z|2O})^Z(}m=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ z=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ z=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ z=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ=KtpZ z=KtpZ=Kp_O-OZ9D&6OVJo&HG5Na;Fc*8Uq2%ZzaMmA1S95X6v>W(G10W{lF+%USVs z9vW9!$KhYU@T{)$M0RDIeE9$H|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A z|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0`|A+q%{~!K8{D1iW z@c-fe!~ci>5C0$jKm33A|M36e|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K z|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36e|HJ=>{}2Bk{y+SG`2X<# z;s3+`hyM@%AO1i5fB66K|Kb0`|A+q%{~!K8{D1iW@c-fe!~ci>5C0$jKm33A|M36e z|HJ=>{}2Bk{y+SG`2X<#;s3+`hyM@%AO1i5fB66K|Kb0`|A+q%{~!K8{D1gA_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR z_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR_&@kR`9JwT`9JwT`9JwT z`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT z`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT z`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT z`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT z`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT z`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT`9JwT 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 z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY z{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{%8JY{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli{ulli z{ulli{ulli{ulli{ulli{ulli{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82{#X82 z{#X82{#X82{#X82{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N{x|+N z{x|+N{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U& z{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{&)U&{vZ55 z_zxjXj|K|VA|C|3e z|8M@^{J;5s^Z(}m&HtPKH~(+`-~7M%fAjz5|IPoK|2O|{{@?t+`G52O=KszAoBuce zZ~ou>zxjXj|K|VA|C|3e|8M@^{J;5s^Z(}m&HtPKH~(+`-~7M%fAjz5|IPoK|2O|{ z{@?t+`G52O=KszAoBuceZ~ou>zxjXj|K|VA|C|3e|8M@^{J;5s^Z(}m&HtPKH~(+` z-~7M%fAjz5|IPoK|2O|{{@?t+`G52O=KszAoBuceZ~ou>zxjXj|K|VA|C|3e|8M@^ z{J;5s^Z(}m&HtPKH~(+`-~7M%fAjz5|IPoK|2O|{{@?t+`G52O=KszAoBuceZ~ou> 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 z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ) z{LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){LlQ){4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3 z{4e}3{4e}3{4e}3{4e}3{4e}3{4e}3{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44{IC44 z{IC44{IC44{IC44{IC44{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk z{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk z{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk z{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk z{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk z{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk z{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk{BQhk 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 Zl`Q$}-y!`e-1>J&fBM`19n!_({{l6v-4_4= literal 0 HcmV?d00001 diff --git a/client/test/res/rgba8_expectedFlipped.png b/tests/unittests/client/res/rgba8_expectedFlipped.png similarity index 100% rename from client/test/res/rgba8_expectedFlipped.png rename to tests/unittests/client/res/rgba8_expectedFlipped.png diff --git a/client/test/res/sampleTexture.png b/tests/unittests/client/res/sampleTexture.png similarity index 100% rename from client/test/res/sampleTexture.png rename to tests/unittests/client/res/sampleTexture.png diff --git a/client/test/res/sampleTexture_invalid.png b/tests/unittests/client/res/sampleTexture_invalid.png similarity index 100% rename from client/test/res/sampleTexture_invalid.png rename to tests/unittests/client/res/sampleTexture_invalid.png diff --git a/client/logic/unittests/res/testLogic_01.rlogic b/tests/unittests/client/res/testScene_01.ramses similarity index 64% rename from client/logic/unittests/res/testLogic_01.rlogic rename to tests/unittests/client/res/testScene_01.ramses index f8d7cee51bbe4e103b66894f88537fb41df08b4d..6f120426240b158445a0091e7990fea3f79fcc60 100644 GIT binary patch delta 5959 zcmcIoYiwLc6~43fx_+$VXY4w5?dv4ec48;%2Wgx*wT%AE5)Bp^h&El?%-|Wow zKAhA->XBx@nKS1#XU@#McVGL0c;}}gwr96*GGayUx55#BFtDj@<2vs;Z%cT%b#MNj z?f&S1FEYMq|Auz!gQMQI;Sn(VEnjphY~5u|TN9fgWu#FV#!)L0ZSh4SR zTj3ccw)Me8=dlE=G;+|%LUSdxJoJ}qzWu1Hx@C9o-S_yx$;Q-DG`eB}(kVm-DMYU+ zl$|odz@5xU9%L9dj`t+-dJH4hgP+98h@|TvmzLsy#Ig3vfH-!9iZED)mQ1cJp=~6w z!KIb!Xhm}RqiXWK#VJT#I@6YK+Ls&h{%cegG8#6PgMiniak$Ua%5}7s!qK^BaV2~$ zfwO~IpxjOKg?Z9%pLcI-cUaY~e{%jC#tAj2Y%QQdos(vlqX zDe=pvTVI86R3;I*JQ9)1aE-RnwNVP)yq8NX3?sMEY__4=9{S;%qk;mvZYcNvv>( zbh$5o>AzQ3z3|{me}DYWzux}4&y?ami(Pl5&|#Dg=|KrN?9*qd>T?-4yn0E&z|A zy{Rw@ZmHX-@O~hR`mnuV+S~^@Og#j6uzyn7*SEKL*WAo4=Oya@b?ON zlTm)P!U2U7Kg<7g&NPR@xK)cTtPHcs<2k!8igBx)c=sedw>jd zM2%;F4D_fPe?15JXQXEo<7W!r09Jtiwi*}Xx}f8Fg)Is@6>bC4@u0%}Kst`7@ev>$ ze^HH31L^nW7&@Kkysq$X3f~2820sti5-xqC7T67310;Wg!kudVAz&}~6Tt1jL%=@Z zCxBal&j9;@Uj+^TzX_!LmXqjEVGc-zSAbM_1Go+NCmdFJ^B}%*0FFSwb?G*?-Kh6szslobJ5aAVjLQ59EAD ztc*u730bBZ@5IQ-C5c0me0=kqm__YVV|u^NL1KYs_KfPZ=Rk% z^{x9J>Fs-F$Lp8Qthdb-^H`>+Zg=ycR$yPLxg#E`-X*Z=AJ?pPGZ(q>D>X-i{XnkA zK3waS?keJEYsZABw(HGH_DroO?y2|Wh?;m+!=?Ps)qCvhwsPUNd)q2)Z`-X!d~Q$T zLubO8^hL)FdwoNFe5UPGv2ewk`o^;CYptI6v;7BMBHOOoQ75wEojWcIk!3gStm|U{ z+;Ut^@}RId%s_C&!Z?K}=nyryQ!J@$6kBDChlWHuF8#ES9Q@#xkf$sf_WJ^36P7t? zMaCEP!tX9pHkvd;Pb^>@J6X*1$WZubma&HL?HDp+!@fx?>;p4}^a}>H9CMUl@HEZT zalb7EfH1M3sCORkYtq$wgZLKlpbN0F1fdgy}Bc_HX9IS9EP<%Vx_ zJ(!mQ840Bpn_L%=HrbdqA-Dj}6mVyHxkOU>7l1BNU9!|?M57ZyU-Z6!e>51LuHPelJt8md9u9qX&JMElU6i* zKuRV3&?$+=7$6Ohc!-$4Wz2XdYmJotx;!)}nl8MsXK*k$yZho(=7_EzRy6xX)UMLO z(E@GC3Ii6~qOW?n_Q)AcCDg0eJI@4imI)>C(j~8yQX)|kTnD3c5*W4uBUac9;ZGc} zQ;7N`+GKr@*k{7lasXoVi`FbkIKngZ@BcQ@S1diEWj!v59@V)E!}NS#WBuq3w>w1>4PL>pE{!W-y10o3Cvl z0QS;@z>{4?(I57W^#vz_VT2)F!(Fq9e<9V+jl9cUK$;z5O>ocCuTbubE`AodFW^U_ zt&#qTp#9v6Jo(Ruo=?75sizMg`#0Y0k7K$N-j}x9YP01nZ;npH7B2hDB&z~RQAvv5 zc}%>QrmQUe#MsliH%a~@swUsR;!1v=p}rQ2|3+m8z7w@KIcgR3;ziiBaLjIAg|Y~{ Ka1{RUBK#jHG%Q2_ delta 694 zcmZ`%O=uHQ5T4oWCYyBYZj#+@QVMGzupzB!XaWYTtm46fND76vCj-?I50w;uD599- zUeq22iQpj@(c-1b6XMN-;88uP2O;8F%%Mj;IBz4?iw{2Dyl=j5-kaSyc#EHV*tG%R z>efQT02@@$!2pW^uy+pNHlxaMmG=_wMOgZ)u^|UDaFn?coPs&l)u924d1E;TGPORlZ zM3;v74o*>Xx`npbpMHdz!wxN~Vv6?6YX#HfduOyH({a)9&MJ6>28G%mVpMplph3N2 zO-r#t5$}tv(8=}33X__?XFaRFe0+KB^!EAg&j;%-HcrwV|0_4zD%CXk*;Q(mkBfJu zWyA^c%ClOWNfga;h;*T1(6`xYbe$>Oxi(^wy2pkX7p;mL$CTKq4^#AG#h{JA#u)Vj zm+l8SQ+9u)bLqy`=JlJ~o7d=Gy&ygY&n-j|F1KT-iqT4_uwiG_Lq+sfM~DiA>t0tr zNzR0v3_Xg0|N7ywI5eV+ -namespace ramses +namespace ramses::internal { class AFontCascade : public testing::Test { @@ -22,7 +22,7 @@ namespace ramses { } - static void SetUpTestCase() + static void SetUpTestSuite() { FRegistry = new FontRegistry; LatinFont = FRegistry->createFreetype2Font("./res/ramses-text-Roboto-Bold.ttf"); @@ -31,7 +31,7 @@ namespace ramses ArabicFontInstance = FRegistry->createFreetype2FontInstance(ArabicFont, 12); } - static void TearDownTestCase() + static void TearDownTestSuite() { delete FRegistry; } diff --git a/client/test/text/FontRegistryTest.cpp b/tests/unittests/client/text/FontRegistryTest.cpp similarity index 92% rename from client/test/text/FontRegistryTest.cpp rename to tests/unittests/client/text/FontRegistryTest.cpp index 7fb8bbaa3..92f3c3e1e 100644 --- a/client/test/text/FontRegistryTest.cpp +++ b/tests/unittests/client/text/FontRegistryTest.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/FontRegistry.h" -#include "Utils/File.h" +#include "ramses/client/text/FontRegistry.h" +#include "internal/Core/Utils/File.h" #include "FileDescriptorHelper.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { class AFontRegistry : public testing::Test { @@ -41,9 +41,9 @@ namespace ramses TEST_F(AFontRegistry, CreatesAndDestroysFreetype2FontInstanceFromFileDescriptor) { const char* path = "./res/ramses-text-Roboto-Bold.ttf"; - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary(path); + const int fd = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary(path); size_t fileSize = 0; - EXPECT_TRUE(ramses_internal::File(path).getSizeInBytes(fileSize)); + EXPECT_TRUE(ramses::internal::File(path).getSizeInBytes(fileSize)); const FontId fontId = m_fontRegistry.createFreetype2FontFromFileDescriptor(fd, 0, fileSize); ASSERT_TRUE(fontId.isValid()); @@ -61,15 +61,15 @@ namespace ramses size_t fileSize = 0; { // write to a file with some offset - ramses_internal::File inFile("./res/ramses-text-Roboto-Bold.ttf"); + ramses::internal::File inFile("./res/ramses-text-Roboto-Bold.ttf"); EXPECT_TRUE(inFile.getSizeInBytes(fileSize)); std::vector data(fileSize); size_t numBytesRead = 0; - EXPECT_TRUE(inFile.open(ramses_internal::File::Mode::ReadOnlyBinary)); - EXPECT_EQ(ramses_internal::EStatus::Ok, inFile.read(data.data(), fileSize, numBytesRead)); + EXPECT_TRUE(inFile.open(ramses::internal::File::Mode::ReadOnlyBinary)); + EXPECT_EQ(ramses::internal::EStatus::Ok, inFile.read(data.data(), fileSize, numBytesRead)); - ramses_internal::File outFile("fontFileWithOffset.ttf"); - EXPECT_TRUE(outFile.open(ramses_internal::File::Mode::WriteOverWriteOldBinary)); + ramses::internal::File outFile("fontFileWithOffset.ttf"); + EXPECT_TRUE(outFile.open(ramses::internal::File::Mode::WriteOverWriteOldBinary)); uint32_t zeroData = 0; EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); @@ -77,7 +77,7 @@ namespace ramses EXPECT_TRUE(outFile.write(&zeroData, sizeof(zeroData))); } - const int fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary("fontFileWithOffset.ttf"); + const int fd = ramses::internal::FileDescriptorHelper::OpenFileDescriptorBinary("fontFileWithOffset.ttf"); const FontId fontId = m_fontRegistry.createFreetype2FontFromFileDescriptor(fd, 4, fileSize); ASSERT_TRUE(fontId.isValid()); diff --git a/tests/unittests/client/text/Freetype2FontInstanceTest.cpp b/tests/unittests/client/text/Freetype2FontInstanceTest.cpp new file mode 100644 index 000000000..c406bd45c --- /dev/null +++ b/tests/unittests/client/text/Freetype2FontInstanceTest.cpp @@ -0,0 +1,287 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/text/Freetype2FontInstance.h" +#include "ramses/client/text/FontRegistry.h" +#include "impl/text/Quad.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + class AFreetype2FontInstance : public testing::Test + { + public: + static void SetUpTestSuite() + { + FRegistry = new FontRegistry; + const auto fontId = FRegistry->createFreetype2Font("res/ramses-text-Roboto-Bold.ttf"); + const auto fontIdJp = FRegistry->createFreetype2Font("res/ramses-text-WenQuanYi-Micro-Hei.ttf"); + const auto fontThaiId = FRegistry->createFreetype2Font("res/ramses-text-NotoSansThai-Regular.ttf"); + + FontInstanceId4 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 4); + FontInstanceId10 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 10); + FontInstanceIdLatin = FRegistry->createFreetype2FontInstance(fontId, 10); + FontInstanceIdJapanese = FRegistry->createFreetype2FontInstance(fontIdJp, 10); + FontInstanceIdThai = FRegistry->createFreetype2FontInstance(fontThaiId, 10); + + FontInstance4 = static_cast(FRegistry->getFontInstance(FontInstanceId4)); + FontInstance10 = static_cast(FRegistry->getFontInstance(FontInstanceId10)); + FontInstanceLatin = static_cast(FRegistry->getFontInstance(FontInstanceIdLatin)); + FontInstanceJp = static_cast(FRegistry->getFontInstance(FontInstanceIdJapanese)); + FontInstanceThai = static_cast(FRegistry->getFontInstance(FontInstanceIdThai)); + } + + static void TearDownTestSuite() + { + delete FRegistry; + } + + protected: + static void ExpectGlyphMetricsEq(const GlyphMetrics& expected, const GlyphMetrics& actual) + { + EXPECT_EQ(expected.key.fontInstanceId, actual.key.fontInstanceId); + EXPECT_EQ(expected.key.identifier.getValue(), actual.key.identifier.getValue()); + EXPECT_EQ(expected.posX, actual.posX); + EXPECT_EQ(expected.posY, actual.posY); + EXPECT_EQ(expected.width, actual.width); + EXPECT_EQ(expected.height, actual.height); + EXPECT_EQ(expected.advance, actual.advance); + } + + static GlyphMetricsVector GetPositionedGlyphs(const std::u32string& str, IFontInstance& fontInstance) + { + GlyphMetricsVector ret; + ret.reserve(str.size()); + fontInstance.loadAndAppendGlyphMetrics(str.cbegin(), str.cend(), ret); + return ret; + } + + static FontRegistry* FRegistry; + static FontInstanceId FontInstanceId4; + static FontInstanceId FontInstanceId10; + static FontInstanceId FontInstanceIdLatin; + static FontInstanceId FontInstanceIdJapanese; + static FontInstanceId FontInstanceIdThai; + static Freetype2FontInstance* FontInstance4; + static Freetype2FontInstance* FontInstance10; + static Freetype2FontInstance* FontInstanceLatin; + static Freetype2FontInstance* FontInstanceJp; + static Freetype2FontInstance* FontInstanceThai; + }; + + FontRegistry* AFreetype2FontInstance::FRegistry(nullptr); + FontInstanceId AFreetype2FontInstance::FontInstanceId4(0u); + FontInstanceId AFreetype2FontInstance::FontInstanceId10(0u); + FontInstanceId AFreetype2FontInstance::FontInstanceIdLatin(0u); + FontInstanceId AFreetype2FontInstance::FontInstanceIdJapanese(0u); + FontInstanceId AFreetype2FontInstance::FontInstanceIdThai(0u); + Freetype2FontInstance* AFreetype2FontInstance::FontInstance4(nullptr); + Freetype2FontInstance* AFreetype2FontInstance::FontInstance10(nullptr); + Freetype2FontInstance* AFreetype2FontInstance::FontInstanceLatin(nullptr); + Freetype2FontInstance* AFreetype2FontInstance::FontInstanceJp(nullptr); + Freetype2FontInstance* AFreetype2FontInstance::FontInstanceThai(nullptr); + + TEST_F(AFreetype2FontInstance, ComputesHeightFromFontData) + { + EXPECT_EQ(5 , FontInstance4 ->getHeight()); + EXPECT_EQ(12, FontInstance10->getHeight()); + } + + TEST_F(AFreetype2FontInstance, ComputesAscenderFromFontData) + { + EXPECT_EQ(4, FontInstance4->getAscender()); + EXPECT_EQ(10, FontInstance10->getAscender()); + } + + TEST_F(AFreetype2FontInstance, ComputesDescenderFromFontData) + { + EXPECT_EQ(-1, FontInstance4->getDescender()); + EXPECT_EQ(-3, FontInstance10->getDescender()); + } + + TEST_F(AFreetype2FontInstance, ObtainsGlyphMetrics) + { + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(U"%", *FontInstance4); + + ASSERT_EQ(1u, positionedGlyphs.size()); + const GlyphKey key{ GlyphId(9u), FontInstanceId4 }; + ExpectGlyphMetricsEq({ key, 3u, 4u, 0, -1, 3 }, positionedGlyphs.front()); + } + + TEST_F(AFreetype2FontInstance, ObtainsMultipleGlyphMetrics) + { + const std::u32string str = U" abc 123 ._! "; + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance4); + + ASSERT_EQ(13u, positionedGlyphs.size()); + auto it = positionedGlyphs.cbegin(); + + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' + ExpectGlyphMetricsEq({ { GlyphId( 69u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //a + ExpectGlyphMetricsEq({ { GlyphId( 70u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //b + ExpectGlyphMetricsEq({ { GlyphId( 71u), FontInstanceId4 }, 2u, 4u, 0, -1, 2 }, *it++); //c + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' + ExpectGlyphMetricsEq({ { GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }, *it++); //1 + ExpectGlyphMetricsEq({ { GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }, *it++); //2 + ExpectGlyphMetricsEq({ { GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it++); //3 + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' + ExpectGlyphMetricsEq({ { GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }, *it++); //. + ExpectGlyphMetricsEq({ { GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }, *it++); //_ + ExpectGlyphMetricsEq({ { GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }, *it++); //! + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it++); //' ' + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AFreetype2FontInstance, ObtainsMultipleGlyphMetricsFromInstancesWithDifferentSizes) + { + const std::u32string str1 = U" 123 ._! "; + const std::u32string str2 = U" abc "; + const GlyphMetricsVector positionedGlyphs1 = GetPositionedGlyphs(str1, *FontInstance4); + const GlyphMetricsVector positionedGlyphs2 = GetPositionedGlyphs(str2, *FontInstance10); + + ASSERT_EQ(9u, positionedGlyphs1.size()); + ASSERT_EQ(5u, positionedGlyphs2.size()); + + auto it1 = positionedGlyphs1.cbegin(); + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' + ExpectGlyphMetricsEq({ { GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }, *it1++); //1 + ExpectGlyphMetricsEq({ { GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }, *it1++); //2 + ExpectGlyphMetricsEq({ { GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }, *it1++); //3 + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' + ExpectGlyphMetricsEq({ { GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }, *it1++); //. + ExpectGlyphMetricsEq({ { GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }, *it1++); //_ + ExpectGlyphMetricsEq({ { GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }, *it1++); //! + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }, *it1++); //' ' + EXPECT_EQ(it1, positionedGlyphs1.cend()); + + auto it2 = positionedGlyphs2.cbegin(); + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }, *it2++); //' ' + ExpectGlyphMetricsEq({ { GlyphId( 69u), FontInstanceId10 }, 6u, 5u, 0, 0, 5 }, *it2++); //a + ExpectGlyphMetricsEq({ { GlyphId( 70u), FontInstanceId10 }, 6u, 8u, 0, 0, 6 }, *it2++); //b + ExpectGlyphMetricsEq({ { GlyphId( 71u), FontInstanceId10 }, 5u, 5u, 0, 0, 5 }, *it2++); //c + ExpectGlyphMetricsEq({ { GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }, *it2++); //' ' + EXPECT_EQ(it2, positionedGlyphs2.cend()); + } + + TEST_F(AFreetype2FontInstance, CanLoadACharacterGlyphBitmapData) + { + QuadSize glyphBitmapSize; + const GlyphData bitmapData = FontInstance4->loadGlyphBitmapData(FontInstance4->getGlyphId(U'@'), glyphBitmapSize.x, glyphBitmapSize.y); + EXPECT_EQ(4u, glyphBitmapSize.x); + EXPECT_EQ(4u, glyphBitmapSize.y); + + EXPECT_EQ(bitmapData, std::vector({ + 0x1e, 0x57, 0x54, 0x7, + 0x66, 0x6f, 0x65, 0x51, + 0x5e, 0x8b, 0x96, 0x36, + 0x41, 0x58, 0x28, 0x0 + })); + } + + TEST_F(AFreetype2FontInstance, ReportsSupportedCharCodes) + { + const std::u32string str1 = U" 123 ._! "; + for (auto c : str1) + EXPECT_TRUE(FontInstance4->supportsCharacter(c)); + + const std::u32string str2 = U" abc "; + for (auto c : str2) + EXPECT_TRUE(FontInstance10->supportsCharacter(c)); + } + + TEST_F(AFreetype2FontInstance, ReportsSupportedCharCodesAfterAlreadyLoadedTheirMetrics) + { + const std::u32string str1 = U" 123 ._! "; + const std::u32string str2 = U" abc "; + const GlyphMetricsVector positionedGlyphs1 = GetPositionedGlyphs(str1, *FontInstance4); + const GlyphMetricsVector positionedGlyphs2 = GetPositionedGlyphs(str2, *FontInstance10); + EXPECT_FALSE(positionedGlyphs1.empty()); + EXPECT_FALSE(positionedGlyphs2.empty()); + + for (auto c : str1) + EXPECT_TRUE(FontInstance4->supportsCharacter(c)); + + for (auto c : str2) + EXPECT_TRUE(FontInstance10->supportsCharacter(c)); + } + + TEST_F(AFreetype2FontInstance, ReportsUnsupportedCharCode) + { + EXPECT_FALSE(FontInstance10->supportsCharacter(0x19aa)); + } + + TEST_F(AFreetype2FontInstance, ReportsAllSupportedChars) + { + const auto supportedChars = FontInstance10->getAllSupportedCharacters(); + EXPECT_TRUE(896u == supportedChars.size()); + EXPECT_TRUE(supportedChars.end() != supportedChars.find(165u)); + EXPECT_FALSE(supportedChars.end() != supportedChars.find(127u)); + } + + TEST_F(AFreetype2FontInstance, WillNotShapeJapaneseCombinedCharacters) + { + const std::u32string str = { 0x30D5, 0x3099, 0x30C4, 0x3099, }; //decompositions of U+30D6 and U+30C7 + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceJp); + ASSERT_EQ(4u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + ExpectGlyphMetricsEq({ { GlyphId(1614u), FontInstanceIdJapanese }, 8u, 9u, 1, -1, 10 }, *it++); //U+30D5 + ExpectGlyphMetricsEq({ { GlyphId(1556u), FontInstanceIdJapanese }, 3u, 3u, 7, 6, 10 }, *it++); //U+3099 + ExpectGlyphMetricsEq({ { GlyphId(1597u), FontInstanceIdJapanese }, 10u, 9u, 0, -1, 10 }, *it++); //U+30C6 + ExpectGlyphMetricsEq({ { GlyphId(1556u), FontInstanceIdJapanese }, 3u, 3u, 7, 6, 10 }, *it++); //U+3099 + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AFreetype2FontInstance, WillNotShapeFrenchCombinedCharacters) + { + const std::u32string str = {0x0061, 0x0301, }; //decomposition of U+00E1 + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceLatin); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + ExpectGlyphMetricsEq({ { GlyphId(69u), FontInstanceIdLatin}, 6u, 5u, 0, 0, 5 }, *it++); //U+0061 + ExpectGlyphMetricsEq({ { GlyphId(169u), FontInstanceIdLatin}, 4u, 2u, -4, 6, 0 }, *it++); //U+0301 + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AFreetype2FontInstance, WillNotShapeThaiCombinedCharacters) + { + const std::u32string str = { 0x0E1B, 0x0E31, 0x0E4A, 0x0E21, 0x0E19, 0x0E49, 0x0E33, 0x0E21, 0x0E31, }; //'ปั๊มน้ำมั' + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceThai); + ASSERT_EQ(9u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + ExpectGlyphMetricsEq({ { GlyphId(80u), FontInstanceIdThai}, 5u, 8u, 1, 0, 7 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(45u), FontInstanceIdThai}, 5u, 2u, -4, 7, 0 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(50u), FontInstanceIdThai}, 4u, 3u, -4, 7, 0 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(56u), FontInstanceIdThai}, 5u, 6u, 1, 0, 7 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(71u), FontInstanceIdThai}, 5u, 6u, 1, 0, 7 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(47u), FontInstanceIdThai}, 4u, 2u, -4, 7, 0 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(90u), FontInstanceIdThai}, 6u, 9u, -3, 0, 4 }, *it++); //0x0E33 stays 1 glyph (compared to HarfBuzz decomposition) + ExpectGlyphMetricsEq({ { GlyphId(56u), FontInstanceIdThai}, 5u, 6u, 1, 0, 7 }, *it++); + ExpectGlyphMetricsEq({ { GlyphId(45u), FontInstanceIdThai}, 5u, 2u, -4, 7, 0 }, *it++); + + EXPECT_EQ(it, positionedGlyphs.end()); + } + + TEST_F(AFreetype2FontInstance, WillNotShapeVietnameseCombinedCharacters) + { + const std::u32string str = { 0x00F4, 0x0309, }; //U"ổ" + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceLatin); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + ExpectGlyphMetricsEq({ { GlyphId(685u), FontInstanceIdLatin }, 6u, 8u, 0, 0, 6 }, *it++); //U+00F4 + ExpectGlyphMetricsEq({ { GlyphId(171u), FontInstanceIdLatin }, 3u, 3u, -4, 6, 0 }, *it++); //U+0309 + EXPECT_EQ(it, positionedGlyphs.end()); + } +} diff --git a/client/test/text/GlyphTextureAtlasTest.cpp b/tests/unittests/client/text/GlyphTextureAtlasTest.cpp similarity index 86% rename from client/test/text/GlyphTextureAtlasTest.cpp rename to tests/unittests/client/text/GlyphTextureAtlasTest.cpp index 58afd91fc..3b6302d84 100644 --- a/client/test/text/GlyphTextureAtlasTest.cpp +++ b/tests/unittests/client/text/GlyphTextureAtlasTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/GlyphTextureAtlas.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-text/GlyphGeometry.h" -#include "ramses-text-api/Glyph.h" -#include "ramses-text-api/FontRegistry.h" +#include "impl/text/GlyphTextureAtlas.h" +#include "ramses/client/RamsesClient.h" +#include "impl/text/GlyphGeometry.h" +#include "ramses/client/text/Glyph.h" +#include "ramses/client/text/FontRegistry.h" #include "gtest/gtest.h" namespace @@ -20,7 +20,7 @@ namespace constexpr const ramses::FontInstanceId FakeFontId{ 13u }; } -namespace ramses +namespace ramses::internal { class AGlyphTextureAtlas : public testing::Test { @@ -42,7 +42,7 @@ namespace ramses return m_atlas.mapGlyphsAndCreateGeometry(glyphs); } - bool glyphMappingsOverlap(const GlyphGeometry& glyphGeometry1, const GlyphGeometry& glyphGeometry2) const + static bool GlyphMappingsOverlap(const GlyphGeometry& glyphGeometry1, const GlyphGeometry& glyphGeometry2) { if (glyphGeometry1.atlasPage != glyphGeometry2.atlasPage) { @@ -67,7 +67,7 @@ namespace ramses return (x_hi > x_lo) && (y_hi > y_lo); } - void verifyGlyphMapping(const GlyphGeometry& glyphGeometry, float x_lo_expected, float y_lo_expected, float x_hi_expected, float y_hi_expected) const + static void VerifyGlyphMapping(const GlyphGeometry& glyphGeometry, float x_lo_expected, float y_lo_expected, float x_hi_expected, float y_hi_expected) { EXPECT_FLOAT_EQ(glyphGeometry.texcoords[1][0], x_lo_expected); EXPECT_FLOAT_EQ(glyphGeometry.texcoords[1][1], y_lo_expected); @@ -75,14 +75,14 @@ namespace ramses EXPECT_FLOAT_EQ(glyphGeometry.texcoords[3][1], y_hi_expected); } - void expectGeometrySize(size_t numGlyphs, GlyphGeometry const& glyphGeometry) + static void ExpectGeometrySize(size_t numGlyphs, GlyphGeometry const& glyphGeometry) { EXPECT_EQ(glyphGeometry.positions.size(), numGlyphs * 4u); EXPECT_EQ(glyphGeometry.indices.size(), numGlyphs * 6u); EXPECT_EQ(glyphGeometry.texcoords.size(), numGlyphs * 4u); } - void expectGeometry(int left, int right, int bottom, int top, GlyphGeometry const& geo, int i = 0, int xoffset = 0, int yoffset = 0) + static void ExpectGeometry(int left, int right, int bottom, int top, GlyphGeometry const& geo, int i = 0, int xoffset = 0, int yoffset = 0) { constexpr int padding = 1; constexpr float halfPadding = padding / 2.f; @@ -134,7 +134,7 @@ namespace ramses const GlyphMetricsVector glyphs = { {GlyphKey(GlyphId('a'), FakeFontId), 3, 5, 0, 0, 0} }; auto glyphGeometry = createTestGlyphGeometry(glyphs); - verifyGlyphMapping(glyphGeometry, 0.041666668f, 0.025f, 0.375f, 0.32499999f); + VerifyGlyphMapping(glyphGeometry, 0.041666668f, 0.025f, 0.375f, 0.32499999f); EXPECT_EQ(0u, glyphGeometry.atlasPage); } @@ -143,7 +143,7 @@ namespace ramses const GlyphMetricsVector glyphs = { { GlyphKey(GlyphId('a'), FakeFontId), 10, 18, 0, 0, 0 } }; auto glyphGeometry = createTestGlyphGeometry(glyphs); - verifyGlyphMapping(glyphGeometry, 0.041666668f, 0.025f, 0.95833331f, 0.97500002f); + VerifyGlyphMapping(glyphGeometry, 0.041666668f, 0.025f, 0.95833331f, 0.97500002f); EXPECT_EQ(0u, glyphGeometry.atlasPage); } @@ -154,9 +154,9 @@ namespace ramses const GlyphMetricsVector glyphRight = { { GlyphKey(GlyphId('b'), FakeFontId), 3, 5, 0, 0, 0 } }; auto glyphGeometryRight = createTestGlyphGeometry(glyphRight); - verifyGlyphMapping(glyphGeometryLeft, 0.041666668f, 0.025f, 0.375f, 0.32499999f); - verifyGlyphMapping(glyphGeometryRight, 0.45833334f, 0.025f, 0.79166669f, 0.32499999f); - EXPECT_FALSE(glyphMappingsOverlap(glyphGeometryRight, glyphGeometryLeft)); + VerifyGlyphMapping(glyphGeometryLeft, 0.041666668f, 0.025f, 0.375f, 0.32499999f); + VerifyGlyphMapping(glyphGeometryRight, 0.45833334f, 0.025f, 0.79166669f, 0.32499999f); + EXPECT_FALSE(GlyphMappingsOverlap(glyphGeometryRight, glyphGeometryLeft)); EXPECT_EQ(0u, glyphGeometryLeft.atlasPage); EXPECT_EQ(0u, glyphGeometryRight.atlasPage); } @@ -169,9 +169,9 @@ namespace ramses const GlyphMetricsVector glyphBottom = { { GlyphKey(GlyphId('b'), FakeFontId), 3, 5, 0, 0, 0 } }; auto glyphGeometryBottom = createTestGlyphGeometry(glyphBottom); - verifyGlyphMapping(glyphGeometryTop, 0.041666668f, 0.025f, 0.875f, 0.32499999f); - verifyGlyphMapping(glyphGeometryBottom, 0.041666668f, 0.375f, 0.375f, 0.67500001f); - EXPECT_FALSE(glyphMappingsOverlap(glyphGeometryTop, glyphGeometryBottom)); + VerifyGlyphMapping(glyphGeometryTop, 0.041666668f, 0.025f, 0.875f, 0.32499999f); + VerifyGlyphMapping(glyphGeometryBottom, 0.041666668f, 0.375f, 0.375f, 0.67500001f); + EXPECT_FALSE(GlyphMappingsOverlap(glyphGeometryTop, glyphGeometryBottom)); EXPECT_EQ(0u, glyphGeometryTop.atlasPage); EXPECT_EQ(0u, glyphGeometryBottom.atlasPage); } @@ -183,9 +183,9 @@ namespace ramses auto glyphGeometryMappedToTex1 = createTestGlyphGeometry(glyphMappedToTex1); auto glyphGeometryMappedToTex2 = createTestGlyphGeometry(glyphMappedToTex2); - verifyGlyphMapping(glyphGeometryMappedToTex1, 0.041666668f, 0.025f, 0.95833331f, 0.97500002f); - verifyGlyphMapping(glyphGeometryMappedToTex2, 0.041666668f, 0.025f, 0.375f, 0.32499999f); - EXPECT_FALSE(glyphMappingsOverlap(glyphGeometryMappedToTex1, glyphGeometryMappedToTex2)); + VerifyGlyphMapping(glyphGeometryMappedToTex1, 0.041666668f, 0.025f, 0.95833331f, 0.97500002f); + VerifyGlyphMapping(glyphGeometryMappedToTex2, 0.041666668f, 0.025f, 0.375f, 0.32499999f); + EXPECT_FALSE(GlyphMappingsOverlap(glyphGeometryMappedToTex1, glyphGeometryMappedToTex2)); EXPECT_EQ(0u, glyphGeometryMappedToTex1.atlasPage); EXPECT_EQ(1u, glyphGeometryMappedToTex2.atlasPage); } @@ -199,7 +199,7 @@ namespace ramses }; auto glyphGeometry = createTestGlyphGeometry(glyphs); - expectGeometrySize(1, glyphGeometry); + ExpectGeometrySize(1, glyphGeometry); m_atlas.unmapGlyphsFromPage(glyphs, glyphGeometry.atlasPage); } @@ -214,8 +214,8 @@ namespace ramses auto glyphGeometry = createTestGlyphGeometry({ { GlyphKey(GlyphId('a'), FakeFontId), right - left, top - bottom, left, bottom, 0 } }); EXPECT_EQ(0u, glyphGeometry.atlasPage); - expectGeometrySize(1u, glyphGeometry); - expectGeometry(left, right, bottom, top, glyphGeometry); + ExpectGeometrySize(1u, glyphGeometry); + ExpectGeometry(left, right, bottom, top, glyphGeometry); } TEST_F(AGlyphTextureAtlas, CreatesGeometryForSingleGlyphWithOffsetNotZero) @@ -228,8 +228,8 @@ namespace ramses auto glyphGeometry = createTestGlyphGeometry({ { GlyphKey(GlyphId('a'), FakeFontId), right - left, top - bottom, left, bottom, 0 } }); EXPECT_EQ(0u, glyphGeometry.atlasPage); - expectGeometrySize(1u, glyphGeometry); - expectGeometry(left, right, bottom, top, glyphGeometry); + ExpectGeometrySize(1u, glyphGeometry); + ExpectGeometry(left, right, bottom, top, glyphGeometry); } TEST_F(AGlyphTextureAtlas, CreatesMultipleGeometriesForGlyphsInSamePage) @@ -247,9 +247,9 @@ namespace ramses const auto geometry = createTestGlyphGeometry(glyphs); EXPECT_EQ(0u, geometry.atlasPage); - expectGeometrySize(glyphs.size(), geometry); - expectGeometry(left, right, bottom, top, geometry, 0, 0, 0); - expectGeometry(left, right, bottom, top, geometry, 1, 0, top - bottom + 2); + ExpectGeometrySize(glyphs.size(), geometry); + ExpectGeometry(left, right, bottom, top, geometry, 0, 0, 0); + ExpectGeometry(left, right, bottom, top, geometry, 1, 0, top - bottom + 2); } TEST_F(AGlyphTextureAtlas, CreatesMultipleGeometriesForGlyphsInSamePageWhenOffsetIsNotZero) @@ -267,9 +267,9 @@ namespace ramses const auto geometry = createTestGlyphGeometry(glyphs); EXPECT_EQ(0u, geometry.atlasPage); - expectGeometrySize(glyphs.size(), geometry); - expectGeometry(left, right, bottom, top, geometry, 0, 0, 0); - expectGeometry(left, right, bottom, top, geometry, 1, 0, top - bottom + 2); + ExpectGeometrySize(glyphs.size(), geometry); + ExpectGeometry(left, right, bottom, top, geometry, 0, 0, 0); + ExpectGeometry(left, right, bottom, top, geometry, 1, 0, top - bottom + 2); } TEST_F(AGlyphTextureAtlas, CreatesGeometryForStringWithSomeEmptyChars) @@ -286,9 +286,9 @@ namespace ramses EXPECT_EQ(0u, geometry.atlasPage); // 2 chars with valid geometry - expectGeometrySize(2u, geometry); - expectGeometry(4, 6, -1, 2, geometry, 0); - expectGeometry(9, 12, 1, 3, geometry, 1, 4); + ExpectGeometrySize(2u, geometry); + ExpectGeometry(4, 6, -1, 2, geometry, 0); + ExpectGeometry(9, 12, 1, 3, geometry, 1, 4); } TEST_F(AGlyphTextureAtlas, CreatesGeometryForStringWithSomeEmptyCharsWithZeroAdvance) @@ -305,9 +305,9 @@ namespace ramses EXPECT_EQ(0u, geometry.atlasPage); // 2 chars with valid geometry - expectGeometrySize(2u, geometry); - expectGeometry(1, 3, -1, 2, geometry, 0); - expectGeometry(3, 6, 1, 3, geometry, 1, 4); + ExpectGeometrySize(2u, geometry); + ExpectGeometry(1, 3, -1, 2, geometry, 0); + ExpectGeometry(3, 6, 1, 3, geometry, 1, 4); } TEST_F(AGlyphTextureAtlas, ReleasesGlyphFromPageAfterUnmappingAsOftenAsMappingIt) @@ -388,13 +388,13 @@ namespace ramses const auto geometryA = createTestGlyphGeometry(glyphsA); EXPECT_EQ(0u, geometryA.atlasPage); - expectGeometrySize(4, geometryA); - expectGeometry(0, 10, 0, 8, geometryA, 0, 0, 0); + ExpectGeometrySize(4, geometryA); + ExpectGeometry(0, 10, 0, 8, geometryA, 0, 0, 0); const auto geometryB = createTestGlyphGeometry(glyphsB); EXPECT_EQ(0u, geometryB.atlasPage); - expectGeometrySize(1, geometryB); - expectGeometry(0, 10, 0, 8, geometryB, 0, 0, 8 + 2); + ExpectGeometrySize(1, geometryB); + ExpectGeometry(0, 10, 0, 8, geometryB, 0, 0, 8 + 2); } TEST_F(AGlyphTextureAtlas, MapsNonFittingGlyphsToNewPageAndKeepsSpaceOnFirstPageOpen) diff --git a/client/test/text/GlyphTexturePageTest.cpp b/tests/unittests/client/text/GlyphTexturePageTest.cpp similarity index 92% rename from client/test/text/GlyphTexturePageTest.cpp rename to tests/unittests/client/text/GlyphTexturePageTest.cpp index fef4c1933..2c9fdef4b 100644 --- a/client/test/text/GlyphTexturePageTest.cpp +++ b/tests/unittests/client/text/GlyphTexturePageTest.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text/GlyphTexturePage.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Texture2DBuffer.h" -#include "ramses-client-api/TextureSampler.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/SceneObjectIterator.h" +#include "impl/text/GlyphTexturePage.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Texture2DBuffer.h" +#include "ramses/client/TextureSampler.h" +#include "ramses/client/Scene.h" +#include "ramses/client/SceneObjectIterator.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { class AGlyphTexturePage : public testing::Test { @@ -31,8 +31,8 @@ namespace ramses protected: RamsesFramework m_framework{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; RamsesClient& m_client; - Scene& m_scene; - GlyphTexturePage* m_glyphPage; + ramses::Scene& m_scene; + GlyphTexturePage* m_glyphPage{nullptr}; void expectFreeArea(uint32_t expected) { @@ -62,7 +62,7 @@ namespace ramses [[nodiscard]] GlyphTexturePage::QuadIndex getFittingFreeQuadIndex(EClaimedQuadSize size) const { - QuadSize quadSize = getQuadSize(size); + QuadSize quadSize = GetQuadSize(size); GlyphTexturePage::QuadIndex i(0); for (auto const& quad : m_glyphPage->getFreeSpace()) { @@ -80,7 +80,7 @@ namespace ramses return getFittingFreeQuadIndex(size) != std::numeric_limits::max(); } - [[nodiscard]] QuadSize getQuadSize(EClaimedQuadSize size) const + [[nodiscard]] static QuadSize GetQuadSize(EClaimedQuadSize size) { switch (size) { @@ -100,13 +100,13 @@ namespace ramses Quad claimTestQuad(EClaimedQuadSize size) { - QuadSize quadSize = getQuadSize(size); + QuadSize quadSize = GetQuadSize(size); auto freePlace = getFittingFreeQuadIndex(size); EXPECT_NE(freePlace, std::numeric_limits::max()); return Quad(m_glyphPage->claimSpace(freePlace, quadSize), quadSize); } - void expectQuadPlacing(Quad quad, EClaimedQuadPosition expectedPosition, EClaimedQuadSize expectedSize) const + static void ExpectQuadPlacing(Quad quad, EClaimedQuadPosition expectedPosition, EClaimedQuadSize expectedSize) { switch (expectedPosition) { @@ -165,7 +165,6 @@ namespace ramses AGlyphTexturePage::AGlyphTexturePage() : m_client(*m_framework.createClient("text test")) , m_scene(*m_client.createScene(sceneId_t(1u))) - , m_glyphPage(nullptr) { expectRamsesObjects(false); m_glyphPage = new GlyphTexturePage(m_scene, QuadSize(PageWidth, PageHeight)); @@ -186,8 +185,8 @@ namespace ramses TEST_F(AGlyphTexturePage, CreatesInternalDataCacheWithCorrectSizeAfterConstruction) { EXPECT_EQ(1u, m_glyphPage->getTextureBuffer().getMipLevelCount()); - uint32_t width; - uint32_t height; + uint32_t width = 0u; + uint32_t height = 0u; m_glyphPage->getTextureBuffer().getMipLevelSize(0, width, height); EXPECT_EQ(PageWidth, width); EXPECT_EQ(PageHeight, height); @@ -344,7 +343,7 @@ namespace ramses TEST_F(AGlyphTexturePage, MapsSingleGlyphWhichExactlyFitsPageSize) { const Quad quad = claimTestQuad(EClaimedQuadSize::FullPage); - expectQuadPlacing(quad, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::FullPage); + ExpectQuadPlacing(quad, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::FullPage); } TEST_F(AGlyphTexturePage, CanNotCreateAnotherGlyphWhenPageIsFull) @@ -356,10 +355,10 @@ namespace ramses TEST_F(AGlyphTexturePage, MapsSecondGlyphWhenSpaceStillAvailableInPage) { const Quad leftHalf = claimTestQuad(EClaimedQuadSize::HalfPage); - expectQuadPlacing(leftHalf, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::HalfPage); + ExpectQuadPlacing(leftHalf, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::HalfPage); const Quad rightHalf = claimTestQuad(EClaimedQuadSize::HalfPage); - expectQuadPlacing(rightHalf, EClaimedQuadPosition::TopRight, EClaimedQuadSize::HalfPage); + ExpectQuadPlacing(rightHalf, EClaimedQuadPosition::TopRight, EClaimedQuadSize::HalfPage); } TEST_F(AGlyphTexturePage, confidence_MapsFourGlyphs_WhichHaveQuarterPageSize_InOnePage) @@ -369,10 +368,10 @@ namespace ramses const Quad glyphMapping3 = claimTestQuad(EClaimedQuadSize::QuarterPage); const Quad glyphMapping4 = claimTestQuad(EClaimedQuadSize::QuarterPage); - expectQuadPlacing(glyphMapping1, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::QuarterPage); - expectQuadPlacing(glyphMapping2, EClaimedQuadPosition::TopRight, EClaimedQuadSize::QuarterPage); - expectQuadPlacing(glyphMapping3, EClaimedQuadPosition::BottomLeft, EClaimedQuadSize::QuarterPage); - expectQuadPlacing(glyphMapping4, EClaimedQuadPosition::BottomRight, EClaimedQuadSize::QuarterPage); + ExpectQuadPlacing(glyphMapping1, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::QuarterPage); + ExpectQuadPlacing(glyphMapping2, EClaimedQuadPosition::TopRight, EClaimedQuadSize::QuarterPage); + ExpectQuadPlacing(glyphMapping3, EClaimedQuadPosition::BottomLeft, EClaimedQuadSize::QuarterPage); + ExpectQuadPlacing(glyphMapping4, EClaimedQuadPosition::BottomRight, EClaimedQuadSize::QuarterPage); } TEST_F(AGlyphTexturePage, confidence_MapsAMosaicOfGlyphsWithHeterogeneousSizes_SoThatNewGlyphDoesNotFitOnPage) @@ -381,8 +380,8 @@ namespace ramses const Quad quarterPage_Page1 = claimTestQuad(EClaimedQuadSize::QuarterPage); claimTestQuad(EClaimedQuadSize::SinglePixel); - expectQuadPlacing(quarterPage_Page1, EClaimedQuadPosition::TopRight, EClaimedQuadSize::QuarterPage); - expectQuadPlacing(halfPage_Page1, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::HalfPage); + ExpectQuadPlacing(quarterPage_Page1, EClaimedQuadPosition::TopRight, EClaimedQuadSize::QuarterPage); + ExpectQuadPlacing(halfPage_Page1, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::HalfPage); EXPECT_FALSE(canClaimQuad(EClaimedQuadSize::QuarterPage)); } @@ -393,7 +392,7 @@ namespace ramses m_glyphPage->releaseSpace(glyph); const Quad newGlyph = claimTestQuad(EClaimedQuadSize::FullPage); - expectQuadPlacing(newGlyph, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::FullPage); + ExpectQuadPlacing(newGlyph, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::FullPage); } TEST_F(AGlyphTexturePage, ReleaseFourGlyphsAndMapNewGlyphWhichIsFourTimesBigger) @@ -409,7 +408,7 @@ namespace ramses m_glyphPage->releaseSpace(glyphMapping4); const Quad glyphMapping = claimTestQuad(EClaimedQuadSize::FullPage); - expectQuadPlacing(glyphMapping, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::FullPage); + ExpectQuadPlacing(glyphMapping, EClaimedQuadPosition::TopLeft, EClaimedQuadSize::FullPage); } TEST_F(AGlyphTexturePage, FindsFreeSpaceForSizeEqualToPageSizeIfEmpty) diff --git a/tests/unittests/client/text/HarfbuzzFontInstanceTest.cpp b/tests/unittests/client/text/HarfbuzzFontInstanceTest.cpp new file mode 100644 index 000000000..674d501be --- /dev/null +++ b/tests/unittests/client/text/HarfbuzzFontInstanceTest.cpp @@ -0,0 +1,751 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "impl/text/HarfbuzzFontInstance.h" +#include "ramses/client/text/FontRegistry.h" +#include "impl/text/Quad.h" +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "fmt/format.h" + +namespace ramses +{ + inline bool operator==(const GlyphMetrics& a, const GlyphMetrics& b) + { + return (a.key == b.key) + && (a.posX == b.posX) + && (a.posY == b.posY) + && (a.width == b.width) + && (a.height == b.height) + && (a.advance == b.advance); + } + + inline void PrintTo(const GlyphMetrics& param, ::std::ostream* os) + { + *os << fmt::format("GlyphMetrics key:({},font:{}) x:{} y:{} w:{} h:{} adv:{}", + param.key.identifier.getValue(), + param.key.fontInstanceId.getValue(), + param.posX, param.posY, param.width, param.height, param.advance); + } +} + +namespace ramses::internal +{ + class AHarfbuzzFontInstance : public testing::Test + { + public: + static void SetUpTestSuite() + { + FRegistry = new FontRegistry; + const auto fontId = FRegistry->createFreetype2Font("res/ramses-text-Roboto-Bold.ttf"); + const auto fontArabicId = FRegistry->createFreetype2Font("res/ramses-text-DroidKufi-Regular.ttf"); + const auto fontJapaneseId = FRegistry->createFreetype2Font("res/ramses-text-WenQuanYi-Micro-Hei.ttf"); + const auto fontThaiId = FRegistry->createFreetype2Font("res/ramses-text-NotoSansThai-Regular.ttf"); + + FontInstanceId4 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 4); + FontInstanceId10 = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontId, 10); + FontInstanceArabicId = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontArabicId, 10); + FontInstanceJapaneseId = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontJapaneseId, 10); + FontInstanceThaiId = FRegistry->createFreetype2FontInstanceWithHarfBuzz(fontThaiId, 10); + + FontInstance4 = static_cast(FRegistry->getFontInstance(FontInstanceId4)); + FontInstance10 = static_cast(FRegistry->getFontInstance(FontInstanceId10)); + FontInstanceArabic = FRegistry->getFontInstance(FontInstanceArabicId); + FontInstanceJapanese = FRegistry->getFontInstance(FontInstanceJapaneseId); + FontInstanceThai = FRegistry->getFontInstance(FontInstanceThaiId); + } + + static void TearDownTestSuite() + { + delete FRegistry; + } + + protected: + static GlyphMetricsVector GetPositionedGlyphs(const std::u32string& str, IFontInstance& fontInstance) + { + GlyphMetricsVector ret; + ret.reserve(str.size()); + fontInstance.loadAndAppendGlyphMetrics(str.cbegin(), str.cend(), ret); + return ret; + } + + static FontRegistry* FRegistry; + static FontInstanceId FontInstanceId4; + static FontInstanceId FontInstanceId10; + static FontInstanceId FontInstanceArabicId; + static FontInstanceId FontInstanceJapaneseId; + static FontInstanceId FontInstanceThaiId; + + // intentionally use base pointer + static Freetype2FontInstance* FontInstance4; + static Freetype2FontInstance* FontInstance10; + static IFontInstance* FontInstanceArabic; + static IFontInstance* FontInstanceJapanese; + static IFontInstance* FontInstanceThai; + }; + + FontRegistry* AHarfbuzzFontInstance::FRegistry(nullptr); + FontInstanceId AHarfbuzzFontInstance::FontInstanceId4(0u); + FontInstanceId AHarfbuzzFontInstance::FontInstanceId10(0u); + FontInstanceId AHarfbuzzFontInstance::FontInstanceArabicId(0u); + FontInstanceId AHarfbuzzFontInstance::FontInstanceJapaneseId(0u); + FontInstanceId AHarfbuzzFontInstance::FontInstanceThaiId(0u); + + Freetype2FontInstance* AHarfbuzzFontInstance::FontInstance4(nullptr); + Freetype2FontInstance* AHarfbuzzFontInstance::FontInstance10(nullptr); + IFontInstance* AHarfbuzzFontInstance::FontInstanceArabic(nullptr); + IFontInstance* AHarfbuzzFontInstance::FontInstanceJapanese(nullptr); + IFontInstance* AHarfbuzzFontInstance::FontInstanceThai(nullptr); + + ////// + /// These tests test base class functionality with no HB reshaping involved, essentially equivalent to freetype2 font instance + ////// + TEST_F(AHarfbuzzFontInstance, ComputesHeightFromFontData) + { + EXPECT_EQ(5 , FontInstance4 ->getHeight()); + EXPECT_EQ(12, FontInstance10->getHeight()); + } + + TEST_F(AHarfbuzzFontInstance, ComputesAscenderFromFontData) + { + EXPECT_EQ(4, FontInstance4->getAscender()); + EXPECT_EQ(10, FontInstance10->getAscender()); + } + + TEST_F(AHarfbuzzFontInstance, ComputesDescenderFromFontData) + { + EXPECT_EQ(-1, FontInstance4->getDescender()); + EXPECT_EQ(-3, FontInstance10->getDescender()); + } + + TEST_F(AHarfbuzzFontInstance, ObtainsGlyphMetrics) + { + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(U"%", *FontInstance4); + + ASSERT_EQ(1u, positionedGlyphs.size()); + const GlyphKey key{ GlyphId(9u), FontInstanceId4 }; + EXPECT_EQ(GlyphMetrics({ key, 3u, 4u, 0, -1, 3 }), positionedGlyphs.front()); + } + + TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetrics) + { + const std::u32string str = U" abc 123 ._! "; + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance4); + + ASSERT_EQ(13u, positionedGlyphs.size()); + auto it = positionedGlyphs.cbegin(); + + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it++); //' ' + EXPECT_EQ(GlyphMetrics({{ GlyphId( 69u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }), *it++); //a + EXPECT_EQ(GlyphMetrics({{ GlyphId( 70u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }), *it++); //b + EXPECT_EQ(GlyphMetrics({{ GlyphId( 71u), FontInstanceId4 }, 2u, 4u, 0, -1, 2 }), *it++); //c + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it++); //' ' + EXPECT_EQ(GlyphMetrics({{ GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }), *it++); //1 + EXPECT_EQ(GlyphMetrics({{ GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }), *it++); //2 + EXPECT_EQ(GlyphMetrics({{ GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }), *it++); //3 + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it++); //' ' + EXPECT_EQ(GlyphMetrics({{ GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }), *it++); //. + EXPECT_EQ(GlyphMetrics({{ GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }), *it++); //_ + EXPECT_EQ(GlyphMetrics({{ GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }), *it++); //! + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it++); //' ' + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsFromInstancesWithDifferentSizes) + { + const std::u32string str1 = U" 123 ._! "; + const std::u32string str2 = U" abc "; + const GlyphMetricsVector positionedGlyphs1 = GetPositionedGlyphs(str1, *FontInstance4); + const GlyphMetricsVector positionedGlyphs2 = GetPositionedGlyphs(str2, *FontInstance10); + + ASSERT_EQ(9u, positionedGlyphs1.size()); + ASSERT_EQ(5u, positionedGlyphs2.size()); + + auto it1 = positionedGlyphs1.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it1++); //' ' + EXPECT_EQ(GlyphMetrics({{ GlyphId( 21u), FontInstanceId4 }, 2u, 3u, 0, 0, 2 }), *it1++); //1 + EXPECT_EQ(GlyphMetrics({{ GlyphId( 22u), FontInstanceId4 }, 3u, 3u, 0, 0, 2 }), *it1++); //2 + EXPECT_EQ(GlyphMetrics({{ GlyphId( 23u), FontInstanceId4 }, 3u, 4u, 0, -1, 2 }), *it1++); //3 + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it1++); //' ' + EXPECT_EQ(GlyphMetrics({{ GlyphId( 18u), FontInstanceId4 }, 1u, 2u, 0, -1, 1 }), *it1++); //. + EXPECT_EQ(GlyphMetrics({{ GlyphId( 67u), FontInstanceId4 }, 2u, 1u, 0, -1, 2 }), *it1++); //_ + EXPECT_EQ(GlyphMetrics({{ GlyphId( 5u), FontInstanceId4 }, 1u, 4u, 0, -1, 1 }), *it1++); //! + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId4 }, 0u, 0u, 0, 0, 1 }), *it1++); //' ' + EXPECT_EQ(it1, positionedGlyphs1.cend()); + + auto it2 = positionedGlyphs2.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }), *it2++); //' ' + EXPECT_EQ(GlyphMetrics({{ GlyphId( 69u), FontInstanceId10 }, 6u, 5u, 0, 0, 5 }), *it2++); //a + EXPECT_EQ(GlyphMetrics({{ GlyphId( 70u), FontInstanceId10 }, 6u, 8u, 0, 0, 6 }), *it2++); //b + EXPECT_EQ(GlyphMetrics({{ GlyphId( 71u), FontInstanceId10 }, 5u, 5u, 0, 0, 5 }), *it2++); //c + EXPECT_EQ(GlyphMetrics({{ GlyphId( 4u), FontInstanceId10 }, 0u, 0u, 0, 0, 2 }), *it2++); //' ' + EXPECT_EQ(it2, positionedGlyphs2.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanLoadACharacterGlyphBitmapData) + { + QuadSize glyphBitmapSize; + const GlyphData bitmapData = FontInstance4->loadGlyphBitmapData(FontInstance4->getGlyphId(U'@'), glyphBitmapSize.x, glyphBitmapSize.y); + EXPECT_EQ(4u, glyphBitmapSize.x); + EXPECT_EQ(4u, glyphBitmapSize.y); + + EXPECT_EQ(bitmapData, std::vector({ 0x1e, 0x57, 0x54, 0x7, + 0x66, 0x6f, 0x65, 0x51, + 0x5e, 0x8b, 0x96, 0x36, + 0x41, 0x58, 0x28, 0x0 })); + } + + TEST_F(AHarfbuzzFontInstance, ReportsUnsupportedCharCode) + { + EXPECT_FALSE(FontInstance10->supportsCharacter(0x19aa)); + } + ////// + ////// + + TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsWithReshaping) + { + //logical order + const std::u32string str = {32, 1577, 1581, 1608, 1604, 32, }; //U" ةحول "; + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceArabic); + + ASSERT_EQ(6u, positionedGlyphs.size()); + auto it = positionedGlyphs.cbegin(); + + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(154u), FontInstanceArabicId }, 7u, 8u, 0, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 8u), FontInstanceArabicId }, 7u, 5u, -1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 47u), FontInstanceArabicId }, 6u, 7u, 0, -2, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 35u), FontInstanceArabicId }, 4u, 8u, -1, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsWithReshapingForVisualOrder) + { + //visual order + const std::u32string str = {0x0020, 0xfedd, 0xfeee, 0xfea3, 0xfe93, 0x0020, }; //U" ةحول "; + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceArabic); + + ASSERT_EQ(6u, positionedGlyphs.size()); + auto it = positionedGlyphs.cbegin(); + + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 34u), FontInstanceArabicId }, 6u, 10u, 0, -2, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 47u), FontInstanceArabicId }, 6u, 7u, 0, -2, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 8u), FontInstanceArabicId }, 7u, 5u, -1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(153u), FontInstanceArabicId }, 6u, 7u, 0, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsWithReshaping2) + { + // logical order + const std::u32string str = {32, 1575, 1604, 1593, 1585, 1576, 1610, 1577, 32, }; //U" العربية "; + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceArabic); + + ASSERT_EQ(8u, positionedGlyphs.size()); + auto it = positionedGlyphs.cbegin(); + + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 51u), FontInstanceArabicId }, 8u, 8u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 27u), FontInstanceArabicId }, 6u, 5u, -1, 0, 5 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 13u), FontInstanceArabicId }, 5u, 7u, -1, -2, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(135u), FontInstanceArabicId }, 5u, 7u, -1, -2, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(216u), FontInstanceArabicId }, 4u, 7u, -1, -2, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(153u), FontInstanceArabicId }, 6u, 7u, 0, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ObtainsMultipleGlyphMetricsWithReshapingForVisualOrder2) + { + // visual order + const std::u32string str = {0x0020, 0xfe94, 0xfef4, 0xfe91, 0xfeae, 0xfecc, 0xfedf, 0xfe8d, 0x0020, }; //U" العربية "; + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceArabic); + + ASSERT_EQ(9u, positionedGlyphs.size()); + auto it = positionedGlyphs.cbegin(); + + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(154u), FontInstanceArabicId }, 7u, 8u, 0, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(217u), FontInstanceArabicId }, 6u, 7u, -1, -2, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(134u), FontInstanceArabicId }, 4u, 7u, -1, -2, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 13u), FontInstanceArabicId }, 5u, 7u, -1, -2, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 28u), FontInstanceArabicId }, 8u, 5u, -1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 35u), FontInstanceArabicId }, 4u, 8u, -1, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 5u), FontInstanceArabicId }, 1u, 8u, 1, 0, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 3u), FontInstanceArabicId }, 0u, 0u, 0, 0, 3 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + +#if !defined(USE_HARFBUZZ_LEGACY_SHAPING) + TEST_F(AHarfbuzzFontInstance, CanShapeJapaneseCombiningCharacters) + { + const std::u32string str = {0x30D5, 0x3099, 0x30C6, 0x3099, }; //decompositions of U+30D6 and U+30C7 + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceJapanese); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.begin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(1615u), FontInstanceJapaneseId }, 9u, 10u, 1, -1, 10 }), *it++); //U+30D6 = U+30D5, U+3099 + EXPECT_EQ(GlyphMetrics({{ GlyphId(1600u), FontInstanceJapaneseId }, 10u, 10u, 0, -1, 10 }), *it++); //U+30C7 = U+30C6, U+3099 + EXPECT_EQ(it, positionedGlyphs.end()); + } + + TEST_F(AHarfbuzzFontInstance, CanShapeFrenchCombinedCharacters) + { + const std::u32string str = { 0x0061, 0x0301, }; //decomposition of U+00E1 + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(1u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(667u), FontInstanceId10 }, 6u, 8u, 0, 0, 5 }), *it++); //U+00E1 + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanShapeThaiCombinedCharacters) + { + const std::u32string str = { 0x0E1B, 0x0E31, 0x0E4A, 0x0E21, 0x0E19, 0x0E49, 0x0E33, 0x0E21, 0x0E31, }; //U"ปั๊มน้ำมั" + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceThai); + ASSERT_EQ(10u, positionedGlyphs.size()); //creates 10 glyphs: U+0E33 is decomposed to 2 glyphs + + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(80u), FontInstanceThaiId }, 5u, 8u, 1, 0, 6 }), *it++); //U+0E1B +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + EXPECT_EQ(GlyphMetrics({{ GlyphId(46u), FontInstanceThaiId }, 3u, 2u, -5, 7, 0 }), *it++); //U+0E31.narrow, shaped for U+0E1B + EXPECT_EQ(GlyphMetrics({{ GlyphId(52u), FontInstanceThaiId }, 4u, 2u, -5, 9, 0 }), *it++); //U+0E4A.small, shaped for U+0E1B +#else + EXPECT_EQ(GlyphMetrics({{ GlyphId(46u), FontInstanceThaiId }, 3u, 2u, -5, 6, 0 }), *it++); //U+0E31.narrow, shaped for U+0E1B + EXPECT_EQ(GlyphMetrics({{ GlyphId(52u), FontInstanceThaiId }, 4u, 3u, -5, 8, 0 }), *it++); //U+0E4A.small, shaped for U+0E1B +#endif + EXPECT_EQ(GlyphMetrics({{ GlyphId(56u), FontInstanceThaiId }, 5u, 6u, 1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(71u), FontInstanceThaiId }, 5u, 6u, 1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(59u), FontInstanceThaiId }, 2u, 2u, -3, 6, 0 }), *it++); //U+0E4D, decomposition of U+0E33 +#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ + EXPECT_EQ(GlyphMetrics({{ GlyphId(49u), FontInstanceThaiId }, 3u, 2u, -3, 9, 0 }), *it++); //U+0E49.small, shaped for U+0E4D +#else + EXPECT_EQ(GlyphMetrics({{ GlyphId(49u), FontInstanceThaiId }, 3u, 2u, -3, 8, 0 }), *it++); //U+0E49.small, shaped for U+0E4D +#endif + EXPECT_EQ(GlyphMetrics({{ GlyphId(86u), FontInstanceThaiId }, 4u, 6u, -1, 0, 4 }), *it++); //U+0E32, decomposition of U+0E33 + EXPECT_EQ(GlyphMetrics({{ GlyphId(56u), FontInstanceThaiId }, 5u, 6u, 1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(45u), FontInstanceThaiId }, 5u, 2u, -4, 7, 0 }), *it++); + + EXPECT_EQ(it, positionedGlyphs.end()); + } + + TEST_F(AHarfbuzzFontInstance, CanShapeVietnameseCombinedCharacters) + { + const std::u32string str = { 0x00F4, 0x0309, }; //U"ổ" + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(1u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(1107u), FontInstanceId10 }, 6u, 9u, 0, 0, 6 }), *it++); + EXPECT_EQ(it, positionedGlyphs.end()); + } + + TEST_F(AHarfbuzzFontInstance, ProducesEmptyListOfGlyphsWhenGivenEmptyString) + { + const std::u32string str = {}; //U""; empty string + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(0u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ProducesEmptyListOfGlyphsWhenGivenControlCharOnly) + { + const std::u32string str = {0x000A, }; //U"\n"; newline character + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(0u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ProducesEmptyListOfGlyphsWhenGivenUnavailablePuaChars) + { + const std::u32string str = {0xE000, 0xF8FF, }; //U""; private use area (PUA) block boundaries: U+E000..U+F8FF + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(0u, positionedGlyphs.size()); //not available in this font + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, ProducesEmptyListOfGlyphsWhenGivenUnavailableCjkChar) + { + const std::u32string str = {0xF900, }; //U"豈"; CJK Compatibility Ideographs are located just after PUA + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(0u, positionedGlyphs.size()); //not available in this font + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddSingleInheritedCharToBuffer) + { + const std::u32string str = {0x0301, }; //U"́"; combining acute accent + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(1u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddMultipleInheritedCharsToBuffer) + { + const std::u32string str = {0x0301, 0x0301, 0x0301, }; // U"́́́"; combining acute accent (3x) + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(3u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCharAtBeginningToBuffer) + { + const std::u32string str = {0x0301, 0x0041, }; //U"́A"; combining acute accent + A + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCharAtEndToBuffer) + { + const std::u32string str = {0x0041, 0x0301, }; //U"Á"; A + combining acute accent + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(1u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(640u), FontInstanceId10 }, 7u, 10u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCharAtBeginningToBufferWithLatinMainScript) + { + const std::u32string str = {0x0301, 0x0041, 0x0042, 0x0043, 0x0044, }; //U"́ABCD"; combining acute accent + ABCD + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(5u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 38u), FontInstanceId10 }, 6u, 7u, 0, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 39u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 40u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCharAtBeginningToBufferWithArabicMainScript) + { + // visual order + const std::u32string str = {0x064b, 0xFEDD, 0xFEEE, 0xFEA3, 0xFE93, }; //U"ًةحول"; Arabic Fathatan + reordered Arabic text + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceArabic); + ASSERT_EQ(5u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 98u), FontInstanceArabicId }, 4u, 3u, 0, 8, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 34u), FontInstanceArabicId }, 6u, 10u, 0, -2, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 47u), FontInstanceArabicId }, 6u, 7u, 0, -2, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 8u), FontInstanceArabicId }, 7u, 5u, -1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(153u), FontInstanceArabicId }, 6u, 7u, 0, 0, 6 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddSingleCommonCharToBuffer) + { + const std::u32string str = {0x002F, }; //U"/"; + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(1u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddMultipleCommonCharsToBuffer) + { + const std::u32string str = {0x002F, 0x002F, }; // U"//"; + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(19u), FontInstanceId10 }, 5u, 8u, -1, -1, 3 }), *it++); //contextual positioning + EXPECT_EQ(GlyphMetrics({{ GlyphId(19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonCharAtBeginningToBuffer) + { + const std::u32string str = {0x002F, 0x0041, }; //U"/A"; + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonCharAtEndToBuffer) + { + const std::u32string str = {0x0041, 0x002F, }; //U"A/"; + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonCharAtBeginningToBufferWithLatinMainScript) + { + const std::u32string str = {0x002F, 0x041, 0x042, 0x043, 0x044, }; //U"/ABCD"; + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(5u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(38u), FontInstanceId10 }, 6u, 7u, 0, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(39u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(40u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonCharAtBeginningToBufferWithArabicMainScript) + { + // visual order + const std::u32string str = {0x061f, 0xfedd, 0xfeee, 0xfea3, 0xfe93, }; //U"؟ةحول"; Arabic question mark + reordered Arabic text + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstanceArabic); + ASSERT_EQ(5u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 92u), FontInstanceArabicId }, 5u, 7u, 0, 0, 5 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 34u), FontInstanceArabicId }, 6u, 10u, 0, -2, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 47u), FontInstanceArabicId }, 6u, 7u, 0, -2, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 8u), FontInstanceArabicId }, 7u, 5u, -1, 0, 6 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(153u), FontInstanceArabicId }, 6u, 7u, 0, 0, 6 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCharToBuffer) + { + const std::u32string str = {0xEE01, }; //U""; PUA colon + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(1u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(440u), FontInstanceId10 }, 3u, 6u, 0, 1, 3 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCharsToBuffer) + { + const std::u32string str = {0xEE01, 0xF6C3, }; //U""; PUA colon + PUA comma + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(440u), FontInstanceId10 }, 3u, 6u, 0, 1, 3 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCharAtBeginningToBuffer) + { + const std::u32string str = {0xF6C3, 0x0041, }; //U"A"; PUA comma + A + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCharAtEndToBuffer) + { + const std::u32string str = {0x0041, 0xF6C3, }; //U"A"; A + PUA comma + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(2u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCommonAndUnknownCharsToBuffer) + { + const std::u32string str = {0x0301, 0x002F, 0xF6C3, }; //U"́/"; combining acute accent + / + PUA comma + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(3u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCommonAndUnknownCharsAtBeginningToBuffer) + { + const std::u32string str = {0x0301, 0x002F, 0xF6C3, 0x0041, }; //U"́/A"; combining acute accent + / + PUA comma + A + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(4u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddInheritedCommonAndUnknownCharsAtEndToBuffer) + { + const std::u32string str = {0x0041, 0x0301, 0x002F, 0xF6C3, }; // U"Á/"; A + combining acute accent + / + PUA comma + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(3u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(640u), FontInstanceId10 }, 7u, 10u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonUnknownAndInheritedCharsToBuffer) + { + const std::u32string str = {0x002F, 0xF6C3, 0x0301, }; //U"/́"; / + PUA comma + combining acute accent + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(3u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonUnknownAndInheritedCharsAtBeginningToBuffer) + { + const std::u32string str = {0x002F, 0xF6C3, 0x0301, 0x0041, }; //U"/́A"; / + PUA comma + combining acute accent + A + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(4u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddCommonUnknownAndInheritedCharsAtEndToBuffer) + { + const std::u32string str = {0x0041, 0x002F, 0xF6C3, 0x0301, }; // U"A/́"; A/ + PUA comma + combining acute accent + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(4u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCommonAndInheritedCharsToBuffer) + { + const std::u32string str = {0xF6C3, 0x002F, 0x0301, }; //U"/́"; PUA comma + / + combining acute accent + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(3u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCommonAndInheritedCharsAtBeginningToBuffer) + { + const std::u32string str = {0xF6C3, 0x002F, 0x0301, 0x0041, }; //U"/́A"; / + PUA comma + combining acute accent + A + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(4u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } + + TEST_F(AHarfbuzzFontInstance, CanAddUnknownCommonAndInheritedCharsAtEndToBuffer) + { + const std::u32string str = {0x0041, 0xF6C3, 0x002F, 0x0301, }; // U"A/́"; A + PUA comma + / + combining acute accent + + const GlyphMetricsVector positionedGlyphs = GetPositionedGlyphs(str, *FontInstance10); + ASSERT_EQ(4u, positionedGlyphs.size()); + + auto it = positionedGlyphs.cbegin(); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 37u), FontInstanceId10 }, 7u, 7u, 0, 0, 7 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(442u), FontInstanceId10 }, 2u, 2u, 0, -1, 0 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId( 19u), FontInstanceId10 }, 5u, 8u, -1, -1, 4 }), *it++); + EXPECT_EQ(GlyphMetrics({{ GlyphId(169u), FontInstanceId10 }, 4u, 2u, -4, 6, 0 }), *it++); + EXPECT_EQ(it, positionedGlyphs.cend()); + } +#endif +} diff --git a/client/test/text/LayoutUtilsHMITest.cpp b/tests/unittests/client/text/LayoutUtilsHMITest.cpp similarity index 89% rename from client/test/text/LayoutUtilsHMITest.cpp rename to tests/unittests/client/text/LayoutUtilsHMITest.cpp index f2bfe8ba2..25eced610 100644 --- a/client/test/text/LayoutUtilsHMITest.cpp +++ b/tests/unittests/client/text/LayoutUtilsHMITest.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/LayoutUtils.h" -#include "ramses-text-api/TextCache.h" -#include "ramses-text-api/FontCascade.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-client-api/RamsesClient.h" +#include "ramses/client/text/LayoutUtils.h" +#include "ramses/client/text/TextCache.h" +#include "ramses/client/text/FontCascade.h" +#include "ramses/client/text/FontRegistry.h" +#include "ramses/client/RamsesClient.h" #include #include -namespace ramses +namespace ramses::internal { class ALayoutUtilsHMI : public testing::Test { @@ -24,7 +24,7 @@ namespace ramses { } - static void SetUpTestCase() + static void SetUpTestSuite() { RamsesFrameworkConfig config{EFeatureLevel_Latest}; Framework = new RamsesFramework{config}; @@ -37,7 +37,7 @@ namespace ramses Latin24 = FRegistry->createFreetype2FontInstance(fontLatinId, 24u); } - static void TearDownTestCase() + static void TearDownTestSuite() { delete FRegistry; Framework->destroyClient(*Client); diff --git a/client/test/text/LayoutUtilsTest.cpp b/tests/unittests/client/text/LayoutUtilsTest.cpp similarity index 82% rename from client/test/text/LayoutUtilsTest.cpp rename to tests/unittests/client/text/LayoutUtilsTest.cpp index 2d59125f9..613f2779c 100644 --- a/client/test/text/LayoutUtilsTest.cpp +++ b/tests/unittests/client/text/LayoutUtilsTest.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/LayoutUtils.h" +#include "ramses/client/text/LayoutUtils.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { class ALayoutUtils : public testing::Test { protected: - void expectBBoxEq(const LayoutUtils::StringBoundingBox& expected, const LayoutUtils::StringBoundingBox& actual) + static void ExpectBBoxEq(const LayoutUtils::StringBoundingBox& expected, const LayoutUtils::StringBoundingBox& actual) { EXPECT_EQ(expected.offsetX, actual.offsetX); EXPECT_EQ(expected.offsetY, actual.offsetY); @@ -44,36 +44,36 @@ namespace ramses TEST_F(ALayoutUtils, EmptyBBoxForEmptyString) { - expectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cbegin(), m_positionedGlyphs.cbegin())); - expectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cend(), m_positionedGlyphs.cend())); - expectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crbegin(), m_positionedGlyphs.crbegin())); - expectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crend(), m_positionedGlyphs.crend())); + ExpectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cbegin(), m_positionedGlyphs.cbegin())); + ExpectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cend(), m_positionedGlyphs.cend())); + ExpectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crbegin(), m_positionedGlyphs.crbegin())); + ExpectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crend(), m_positionedGlyphs.crend())); } TEST_F(ALayoutUtils, EmptyBBoxForInvalidRange) { - expectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cend(), m_positionedGlyphs.cbegin())); - expectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crend(), m_positionedGlyphs.crbegin())); + ExpectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cend(), m_positionedGlyphs.cbegin())); + ExpectBBoxEq({ 0,0,0,0,0 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crend(), m_positionedGlyphs.crbegin())); } TEST_F(ALayoutUtils, GetsBBoxForString) { - expectBBoxEq({ -3, -1, 23, 14, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cbegin(), m_positionedGlyphs.cend())); + ExpectBBoxEq({ -3, -1, 23, 14, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cbegin(), m_positionedGlyphs.cend())); } TEST_F(ALayoutUtils, GetsBBoxForStringInReverse) { - expectBBoxEq({ 0, -1, 15, 14, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crbegin(), m_positionedGlyphs.crend())); + ExpectBBoxEq({ 0, -1, 15, 14, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crbegin(), m_positionedGlyphs.crend())); } TEST_F(ALayoutUtils, GetsBBoxForSubString) { - expectBBoxEq({ -4, 0, 23, 13, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cbegin() + 1, m_positionedGlyphs.cend() - 1)); + ExpectBBoxEq({ -4, 0, 23, 13, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.cbegin() + 1, m_positionedGlyphs.cend() - 1)); } TEST_F(ALayoutUtils, GetsBBoxForSubStringInReverse) { - expectBBoxEq({ 1, 0, 15, 13, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crbegin() + 1, m_positionedGlyphs.crend() - 1)); + ExpectBBoxEq({ 1, 0, 15, 13, 6 }, LayoutUtils::GetBoundingBoxForString(m_positionedGlyphs.crbegin() + 1, m_positionedGlyphs.crend() - 1)); } TEST_F(ALayoutUtils, EmptyGlyphsAtBeginningContributeToOffsetAndAdvanceOfBoundingBox) diff --git a/client/test/text/TextCacheTest.cpp b/tests/unittests/client/text/TextCacheTest.cpp similarity index 95% rename from client/test/text/TextCacheTest.cpp rename to tests/unittests/client/text/TextCacheTest.cpp index 7737a7ac0..5b6de7f40 100644 --- a/client/test/text/TextCacheTest.cpp +++ b/tests/unittests/client/text/TextCacheTest.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/TextCache.h" -#include "ramses-text-api/TextLine.h" -#include "ramses-text-api/FontRegistry.h" -#include "ramses-text-api/IFontInstance.h" -#include "ramses-text-api/LayoutUtils.h" -#include "ramses-client-api/Scene.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/UniformInput.h" -#include "ramses-client-api/MeshNode.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/EffectDescription.h" -#include "ramses-utils.h" +#include "ramses/client/text/TextCache.h" +#include "ramses/client/text/TextLine.h" +#include "ramses/client/text/FontRegistry.h" +#include "ramses/client/text/IFontInstance.h" +#include "ramses/client/text/LayoutUtils.h" +#include "ramses/client/Scene.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/UniformInput.h" +#include "ramses/client/MeshNode.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/EffectDescription.h" +#include "ramses/client/ramses-utils.h" #include "gtest/gtest.h" #include "gmock/gmock.h" namespace ramses { - bool operator==(const GlyphMetrics& a, const GlyphMetrics& b) + inline bool operator==(const GlyphMetrics& a, const GlyphMetrics& b) { return a.key == b.key && a.posX == b.posX @@ -32,7 +32,10 @@ namespace ramses && a.height == b.height && a.advance == b.advance; } +} +namespace ramses::internal +{ class ATextCache : public testing::Test { public: @@ -43,7 +46,7 @@ namespace ramses { } - static void SetUpTestCase() + static void SetUpTestSuite() { FRegistry = new FontRegistry; LatinFont = FRegistry->createFreetype2Font("./res/ramses-text-Roboto-Bold.ttf"); @@ -51,14 +54,14 @@ namespace ramses LatinFontInstance20 = FRegistry->createFreetype2FontInstance(LatinFont, 20); } - static void TearDownTestCase() + static void TearDownTestSuite() { delete FRegistry; } protected: - Effect* createTestEffect(Scene& scene) + static Effect* CreateTestEffect(Scene& scene) { EffectDescription effectDesc; effectDesc.setVertexShader( @@ -88,7 +91,7 @@ namespace ramses effectDesc.setAttributeSemantic("a_texcoord", EEffectAttributeSemantic::TextTextureCoordinates); effectDesc.setUniformSemantic("u_texture", EEffectUniformSemantic::TextTexture); - Effect* effect = scene.createEffect(effectDesc, ResourceCacheFlag_DoNotCache, ""); + Effect* effect = scene.createEffect(effectDesc, ""); return effect; } @@ -372,7 +375,7 @@ namespace ramses const std::u32string str = U" test "; const auto positionedGlyphs = m_textCache.getPositionedGlyphs(str, LatinFontInstance12); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); ASSERT_TRUE(TextCache::ContainsRenderableGlyphs(positionedGlyphs)); @@ -385,7 +388,7 @@ namespace ramses const MeshNode* meshNode = textLine->meshNode; ASSERT_TRUE(meshNode != nullptr); EXPECT_NE(nullptr, meshNode->getAppearance()); - EXPECT_NE(nullptr, meshNode->getGeometryBinding()); + EXPECT_NE(nullptr, meshNode->getGeometry()); EXPECT_EQ(24u, meshNode->getIndexCount()); EXPECT_NE(std::numeric_limitsatlasPage)>::max(), textLine->atlasPage); ASSERT_TRUE(textLine->indices != nullptr); @@ -401,7 +404,7 @@ namespace ramses const auto positionedGlyphs1 = m_textCache.getPositionedGlyphs(U" test ", LatinFontInstance12); const auto positionedGlyphs2 = m_textCache.getPositionedGlyphs(U"123abc", LatinFontInstance20); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); const TextLineId textLineId1 = m_textCache.createTextLine(positionedGlyphs1, *textEffect); @@ -421,8 +424,8 @@ namespace ramses ASSERT_TRUE(meshNode2 != nullptr); EXPECT_NE(nullptr, meshNode1->getAppearance()); EXPECT_NE(nullptr, meshNode2->getAppearance()); - EXPECT_NE(nullptr, meshNode1->getGeometryBinding()); - EXPECT_NE(nullptr, meshNode2->getGeometryBinding()); + EXPECT_NE(nullptr, meshNode1->getGeometry()); + EXPECT_NE(nullptr, meshNode2->getGeometry()); EXPECT_EQ(24u, meshNode1->getIndexCount()); EXPECT_EQ(36u, meshNode2->getIndexCount()); EXPECT_NE(std::numeric_limitsatlasPage)>::max(), textLine1->atlasPage); @@ -445,7 +448,7 @@ namespace ramses { const auto positionedGlyphs1 = m_textCache.getPositionedGlyphs(U" test ", LatinFontInstance12); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); const TextLineId textLineId1 = m_textCache.createTextLine(positionedGlyphs1, *textEffect); @@ -456,7 +459,7 @@ namespace ramses { const auto positionedGlyphs1 = m_textCache.getPositionedGlyphs(U" test ", LatinFontInstance12); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); const TextLineId textLineId1 = m_textCache.createTextLine(positionedGlyphs1, *textEffect); @@ -469,7 +472,7 @@ namespace ramses const auto positionedGlyphs1 = m_textCache.getPositionedGlyphs(U" test ", LatinFontInstance12); const auto positionedGlyphs2 = m_textCache.getPositionedGlyphs(U"123abc", LatinFontInstance20); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); const TextLineId textLineId1 = m_textCache.createTextLine(positionedGlyphs1, *textEffect); @@ -486,7 +489,7 @@ namespace ramses TEST_F(ATextCache, failsToCreateTextLineFromEmptyString) { - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); EXPECT_FALSE(m_textCache.createTextLine({}, *textEffect).isValid()); @@ -497,7 +500,7 @@ namespace ramses const std::u32string str = U" "; const auto positionedGlyphs = m_textCache.getPositionedGlyphs(str, LatinFontInstance12); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); ASSERT_FALSE(TextCache::ContainsRenderableGlyphs(positionedGlyphs)); @@ -524,7 +527,7 @@ namespace ramses const std::u32string str = U" test "; auto positionedGlyphs = m_textCache.getPositionedGlyphs(str, LatinFontInstance12); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); positionedGlyphs.back().key.fontInstanceId = FontInstanceId(999u); @@ -536,7 +539,7 @@ namespace ramses const std::u32string str = U"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const auto positionedGlyphs = m_textCache.getPositionedGlyphs(str, LatinFontInstance20); - Effect* textEffect = createTestEffect(m_scene); + Effect* textEffect = CreateTestEffect(m_scene); ASSERT_TRUE(textEffect != nullptr); EXPECT_FALSE(m_textCache.createTextLine(positionedGlyphs, *textEffect).isValid()); @@ -548,7 +551,7 @@ namespace ramses Scene& otherScene(*m_client.createScene(sceneId_t{ 111 })); - Effect* textEffect = createTestEffect(otherScene); + Effect* textEffect = CreateTestEffect(otherScene); ASSERT_TRUE(textEffect != nullptr); EXPECT_FALSE(m_textCache.createTextLine(positionedGlyphs, *textEffect).isValid()); diff --git a/client/test/text/UtfUtilsTest.cpp b/tests/unittests/client/text/UtfUtilsTest.cpp similarity index 99% rename from client/test/text/UtfUtilsTest.cpp rename to tests/unittests/client/text/UtfUtilsTest.cpp index be7fc7735..aacdd98cb 100644 --- a/client/test/text/UtfUtilsTest.cpp +++ b/tests/unittests/client/text/UtfUtilsTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-text-api/UtfUtils.h" +#include "ramses/client/text/UtfUtils.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { static std::string toUTF8String(std::initializer_list codes) { @@ -81,7 +81,7 @@ namespace ramses const std::string utf8String{ toUTF8String({ 65, 194, 162, 224, 176, 139, 240, 144, 144, 165 }) }; // u8"A¢ఋ𐐥" const uint32_t utf32CodePoints[] = { 0x41, 0xa2, 0xc0b, 0x10425 }; - size_t nextOffset = 0; + ptrdiff_t nextOffset = 0; for (uint32_t i = 0; i < numChars; ++i) { const auto result = UtfUtils::ExtractUnicodePointFromUTF8(utf8String.cbegin() + nextOffset, utf8String.cend()); diff --git a/renderer/Platform/Context_EGL/CMakeLists.txt b/tests/unittests/framework-test-utils/CMakeLists.txt similarity index 70% rename from renderer/Platform/Context_EGL/CMakeLists.txt rename to tests/unittests/framework-test-utils/CMakeLists.txt index 803cad289..12d24b58f 100644 --- a/renderer/Platform/Context_EGL/CMakeLists.txt +++ b/tests/unittests/framework-test-utils/CMakeLists.txt @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,12 +7,11 @@ # ------------------------------------------------------------------------- createModule( - NAME Context_EGL + NAME framework-test-utils TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF INCLUDE_PATHS include - SRC_FILES include/Context_EGL/*.h + SRC_FILES include/*.h src/*.cpp - DEPENDENCIES EGL - ramses-renderer-lib + DEPENDENCIES ramses-framework + ramses-gmock ) diff --git a/framework/FrameworkTestUtils/include/CommunicationSystemMock.h b/tests/unittests/framework-test-utils/include/CommunicationSystemMock.h similarity index 77% rename from framework/FrameworkTestUtils/include/CommunicationSystemMock.h rename to tests/unittests/framework-test-utils/include/CommunicationSystemMock.h index 502556ea5..2a87df40d 100644 --- a/framework/FrameworkTestUtils/include/CommunicationSystemMock.h +++ b/tests/unittests/framework-test-utils/include/CommunicationSystemMock.h @@ -6,17 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMUNICATIONSYSTEMMOCK_H -#define RAMSES_COMMUNICATIONSYSTEMMOCK_H +#pragma once #include -#include "framework_common_gmock_header.h" -#include "TransportCommon/ICommunicationSystem.h" -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" -#include "Components/ManagedResource.h" -#include "TransportCommon/ISceneUpdateSerializer.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Communication/TransportCommon/ISceneUpdateSerializer.h" -namespace ramses_internal +namespace ramses::internal { class CommunicationSystemMock : public ICommunicationSystem { @@ -30,9 +28,9 @@ namespace ramses_internal MOCK_METHOD(IConnectionStatusUpdateNotifier&, getRamsesConnectionStatusUpdateNotifier, (), (override)); // scene - MOCK_METHOD(bool, broadcastNewScenesAvailable, (const SceneInfoVector& newScenes, ramses::EFeatureLevel featureLevel), (override)); + MOCK_METHOD(bool, broadcastNewScenesAvailable, (const SceneInfoVector& newScenes, EFeatureLevel featureLevel), (override)); MOCK_METHOD(bool, broadcastScenesBecameUnavailable, (const SceneInfoVector& unavailableScenes), (override)); - MOCK_METHOD(bool, sendScenesAvailable, (const Guid& to, const SceneInfoVector& availableScenes, ramses::EFeatureLevel featureLevel), (override)); + MOCK_METHOD(bool, sendScenesAvailable, (const Guid& to, const SceneInfoVector& availableScenes, EFeatureLevel featureLevel), (override)); MOCK_METHOD(bool, sendSubscribeScene, (const Guid& to, const SceneId& sceneId), (override)); MOCK_METHOD(bool, sendUnsubscribeScene, (const Guid& to, const SceneId& sceneId), (override)); @@ -40,7 +38,7 @@ namespace ramses_internal MOCK_METHOD(bool, sendInitializeScene, (const Guid& to, const SceneId& sceneId), (override)); MOCK_METHOD(bool, sendSceneUpdate, (const Guid& to, const SceneId& sceneId, const ISceneUpdateSerializer& serializer), (override)); - MOCK_METHOD(bool, sendRendererEvent, (const Guid& to, const SceneId& sceneId, const std::vector& data), (override)); + MOCK_METHOD(bool, sendRendererEvent, (const Guid& to, const SceneId& sceneId, const std::vector& data), (override)); MOCK_METHOD(void, logConnectionInfo, (), (override)); MOCK_METHOD(void, triggerLogMessageForPeriodicLog, (), (override)); @@ -49,5 +47,3 @@ namespace ramses_internal void setSceneRendererServiceHandler(ISceneRendererServiceHandler* handler) override; }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/ComponentMocks.h b/tests/unittests/framework-test-utils/include/ComponentMocks.h similarity index 80% rename from framework/FrameworkTestUtils/include/ComponentMocks.h rename to tests/unittests/framework-test-utils/include/ComponentMocks.h index b074d7bb9..8e1f5431c 100644 --- a/framework/FrameworkTestUtils/include/ComponentMocks.h +++ b/tests/unittests/framework-test-utils/include/ComponentMocks.h @@ -6,25 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMPONENTMOCKS_H -#define RAMSES_COMPONENTMOCKS_H +#pragma once -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "Resource/IResource.h" -#include "Scene/ClientScene.h" -#include "Scene/EScenePublicationMode.h" -#include "Components/ISceneGraphProviderComponent.h" -#include "Components/ManagedResource.h" -#include "Components/ISceneGraphConsumerComponent.h" -#include "Components/IResourceProviderComponent.h" -#include "Components/ResourceTableOfContents.h" -#include "Components/ISceneProviderEventConsumer.h" -#include "SceneReferencing/SceneReferenceEvent.h" -#include "Components/ResourceAvailabilityEvent.h" +#include "internal/SceneGraph/Resource/IResource.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/Components/ISceneGraphProviderComponent.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/ISceneGraphConsumerComponent.h" +#include "internal/Components/IResourceProviderComponent.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/Components/ISceneProviderEventConsumer.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" +#include "internal/Components/ResourceAvailabilityEvent.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -34,7 +32,7 @@ namespace ramses_internal ResourceProviderComponentMock(); ~ResourceProviderComponentMock() override; - MOCK_METHOD(ManagedResource, manageResource, (const IResource& resource, bool deletionAllowed), (override)); + MOCK_METHOD(ManagedResource, manageResource, (const IResource& resource), (override)); MOCK_METHOD(ManagedResource, getResource, (ResourceContentHash hash), (override)); MOCK_METHOD(ManagedResource, loadResource, (const ResourceContentHash&), (override)); MOCK_METHOD(ResourceHashUsage, getResourceHashUsage, (const ResourceContentHash&), (override)); @@ -42,7 +40,7 @@ namespace ramses_internal MOCK_METHOD(bool, hasResourceFile, (SceneFileHandle), (const, override)); MOCK_METHOD(void, removeResourceFile, (SceneFileHandle), (override)); MOCK_METHOD(void, loadResourceFromFile, (SceneFileHandle), (override)); - void reserveResourceCount(uint32_t) override {}; + void reserveResourceCount(uint32_t /*totalCount*/) override {}; MOCK_METHOD(ManagedResourceVector, resolveResources, (ResourceContentHashVector& vec), (override)); MOCK_METHOD(ResourceInfo const&, getResourceInfo, (ResourceContentHash const& hash), (override)); MOCK_METHOD(bool, knowsResource, (ResourceContentHash const& hash), (const, override)); @@ -72,7 +70,7 @@ namespace ramses_internal MOCK_METHOD(void, sendSceneReferenceEvent, (const Guid& to, SceneReferenceEvent const& event), (override)); MOCK_METHOD(void, sendResourceAvailabilityEvent,(const Guid& to, ResourceAvailabilityEvent const& event), (override)); - void setSceneRendererHandler(ISceneRendererHandler*) override + void setSceneRendererHandler(ISceneRendererHandler* /*sceneRendererHandler*/) override { } }; @@ -87,5 +85,3 @@ namespace ramses_internal MOCK_METHOD(void, handleResourceAvailabilityEvent, (ResourceAvailabilityEvent const& event, const Guid& rendererId), (override)); }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/DummyResource.h b/tests/unittests/framework-test-utils/include/DummyResource.h similarity index 80% rename from framework/FrameworkTestUtils/include/DummyResource.h rename to tests/unittests/framework-test-utils/include/DummyResource.h index fd0dbfcb2..ac6003aa2 100644 --- a/framework/FrameworkTestUtils/include/DummyResource.h +++ b/tests/unittests/framework-test-utils/include/DummyResource.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DUMMYRESOURCE_H -#define RAMSES_DUMMYRESOURCE_H +#pragma once -#include "Resource/ResourceBase.h" +#include "internal/SceneGraph/Resource/ResourceBase.h" #include -namespace ramses_internal +namespace ramses::internal { class DummyResource : public ResourceBase { public: - DummyResource(const ResourceContentHash& hash, EResourceType typeId, ResourceCacheFlag cacheFlag = ResourceCacheFlag(123u), std::string_view name = {}) - : ResourceBase(typeId, cacheFlag, name) + DummyResource(const ResourceContentHash& hash, EResourceType typeId, std::string_view name = {}) + : ResourceBase(typeId, name) , m_hash(hash) { } @@ -47,5 +46,3 @@ namespace ramses_internal ResourceContentHash m_hash; }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/FileDescriptorHelper.h b/tests/unittests/framework-test-utils/include/FileDescriptorHelper.h similarity index 87% rename from framework/FrameworkTestUtils/include/FileDescriptorHelper.h rename to tests/unittests/framework-test-utils/include/FileDescriptorHelper.h index 61cb4c219..94f4856a4 100644 --- a/framework/FrameworkTestUtils/include/FileDescriptorHelper.h +++ b/tests/unittests/framework-test-utils/include/FileDescriptorHelper.h @@ -6,8 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTUTILS_FILEDESCRIPTORHELPER_H -#define RAMSES_TESTUTILS_FILEDESCRIPTORHELPER_H +#pragma once #include #if _WIN32 @@ -16,7 +15,7 @@ # include #endif -namespace ramses_internal +namespace ramses::internal { namespace FileDescriptorHelper { @@ -31,5 +30,3 @@ namespace ramses_internal } } } - -#endif diff --git a/framework/FrameworkTestUtils/include/IOStreamTester.h b/tests/unittests/framework-test-utils/include/IOStreamTester.h similarity index 81% rename from framework/FrameworkTestUtils/include/IOStreamTester.h rename to tests/unittests/framework-test-utils/include/IOStreamTester.h index efe9daf59..d38a62b28 100644 --- a/framework/FrameworkTestUtils/include/IOStreamTester.h +++ b/tests/unittests/framework-test-utils/include/IOStreamTester.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORKTESTUTILS_IOSTREAMTESTER_H -#define RAMSES_FRAMEWORKTESTUTILS_IOSTREAMTESTER_H +#pragma once -#include "Collections/IInputStream.h" -#include "Collections/IOutputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IOutputStream.h" #include "gtest/gtest.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { class IOStreamTester : public IInputStream, public IOutputStream { @@ -36,7 +35,7 @@ namespace ramses_internal return EStatus::Ok; } - EStatus seek(Int /*numberOfBytesToSeek*/, Seek /*origin*/) override + EStatus seek(int64_t /*numberOfBytesToSeek*/, Seek /*origin*/) override { assert(false); return EStatus::Error; @@ -50,12 +49,12 @@ namespace ramses_internal IOutputStream& write(const void* data, size_t size) override { - m_buffer.insert(m_buffer.end(), static_cast(data), static_cast(data)+size); + m_buffer.insert(m_buffer.end(), static_cast(data), static_cast(data)+size); return *this; } private: - std::vector m_buffer; + std::vector m_buffer; size_t m_readPos = 0; }; @@ -81,5 +80,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/InputStreamContainerMock.h b/tests/unittests/framework-test-utils/include/InputStreamContainerMock.h similarity index 81% rename from framework/FrameworkTestUtils/include/InputStreamContainerMock.h rename to tests/unittests/framework-test-utils/include/InputStreamContainerMock.h index a71646498..e28bf54fb 100644 --- a/framework/FrameworkTestUtils/include/InputStreamContainerMock.h +++ b/tests/unittests/framework-test-utils/include/InputStreamContainerMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INPUTSTREAMCONTAINERMOCK_H -#define RAMSES_INPUTSTREAMCONTAINERMOCK_H +#pragma once -#include "Components/InputStreamContainer.h" +#include "internal/Components/InputStreamContainer.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class InputStreamContainerMock : public IInputStreamContainer { @@ -23,5 +22,3 @@ namespace ramses_internal MOCK_METHOD(IInputStream&, getStream, (), (override)); }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/InputStreamMock.h b/tests/unittests/framework-test-utils/include/InputStreamMock.h similarity index 76% rename from framework/FrameworkTestUtils/include/InputStreamMock.h rename to tests/unittests/framework-test-utils/include/InputStreamMock.h index 7e84eb574..d28c4b356 100644 --- a/framework/FrameworkTestUtils/include/InputStreamMock.h +++ b/tests/unittests/framework-test-utils/include/InputStreamMock.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_INPUTSTREAMMOCK_H -#define RAMSES_INPUTSTREAMMOCK_H +#pragma once -#include "Collections/IInputStream.h" -#include "Components/InputStreamContainer.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" +#include "internal/Components/InputStreamContainer.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class InputStreamMock : public IInputStream { @@ -24,9 +23,7 @@ namespace ramses_internal MOCK_METHOD(IInputStream&, read, (void* data, size_t size), (override)); MOCK_METHOD(EStatus, getState, (), (const, override)); - MOCK_METHOD(EStatus, seek, (Int numberOfBytesToSeek, Seek origin), (override)); + MOCK_METHOD(EStatus, seek, (int64_t numberOfBytesToSeek, Seek origin), (override)); MOCK_METHOD(EStatus, getPos, (size_t& position), (const, override)); }; } - -#endif diff --git a/tests/unittests/framework-test-utils/include/LogTestUtils.h b/tests/unittests/framework-test-utils/include/LogTestUtils.h new file mode 100644 index 000000000..9ae74f45a --- /dev/null +++ b/tests/unittests/framework-test-utils/include/LogTestUtils.h @@ -0,0 +1,39 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ScopedLogContextLevel.h" +#include "internal/Core/Utils/LogMacros.h" + +namespace ramses::internal +{ + struct TestLog + { + ELogLevel type; + std::string message; + }; + + class TestLogCollector + { + public: + explicit TestLogCollector(LogContext& context, ELogLevel verbosityLimit) + : m_logCollector(context, verbosityLimit, [this](ELogLevel type, std::string_view message) + { + logs.emplace_back(TestLog{type, std::string{message}}); + }) + { + } + + std::vector logs; + + private: + ScopedLogContextLevel m_logCollector; + }; +} diff --git a/framework/FrameworkTestUtils/include/MockConnectionStatusUpdateNotifier.h b/tests/unittests/framework-test-utils/include/MockConnectionStatusUpdateNotifier.h similarity index 79% rename from framework/FrameworkTestUtils/include/MockConnectionStatusUpdateNotifier.h rename to tests/unittests/framework-test-utils/include/MockConnectionStatusUpdateNotifier.h index d53179fe9..0eb1fba46 100644 --- a/framework/FrameworkTestUtils/include/MockConnectionStatusUpdateNotifier.h +++ b/tests/unittests/framework-test-utils/include/MockConnectionStatusUpdateNotifier.h @@ -6,15 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MOCKCONNECTIONSTATUSUPDATENOTIFIER_H -#define RAMSES_MOCKCONNECTIONSTATUSUPDATENOTIFIER_H +#pragma once -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" -namespace ramses_internal +namespace ramses::internal { class MockConnectionStatusUpdateNotifier : public IConnectionStatusUpdateNotifier { @@ -26,5 +24,3 @@ namespace ramses_internal MOCK_METHOD(void, unregisterForConnectionUpdates, (IConnectionStatusListener* listener), (override)); }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/ResourceMock.h b/tests/unittests/framework-test-utils/include/ResourceMock.h similarity index 91% rename from framework/FrameworkTestUtils/include/ResourceMock.h rename to tests/unittests/framework-test-utils/include/ResourceMock.h index 000adbc0c..227c9371c 100644 --- a/framework/FrameworkTestUtils/include/ResourceMock.h +++ b/tests/unittests/framework-test-utils/include/ResourceMock.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEMOCK_H -#define RAMSES_RESOURCEMOCK_H +#pragma once -#include "Resource/IResource.h" +#include "internal/SceneGraph/Resource/IResource.h" #include "gmock/gmock.h" -#include "Components/IManagedResourceDeleterCallback.h" +#include "internal/Components/IManagedResourceDeleterCallback.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -35,7 +34,6 @@ namespace ramses_internal MOCK_METHOD(void, setResourceData, (ResourceBlob), (override)); MOCK_METHOD(void, setCompressedResourceData, (CompressedResourceBlob, CompressionLevel, uint32_t uncompressedSize, const ResourceContentHash&), (override)); MOCK_METHOD(void, serializeResourceMetadataToStream, (IOutputStream& output), (const, override)); - MOCK_METHOD(ResourceCacheFlag, getCacheFlag, (), (const, override)); MOCK_METHOD(const std::string&, getName, (), (const, override)); [[nodiscard]] const ResourceContentHash& getHash() const override @@ -74,4 +72,4 @@ namespace ramses_internal } }; } -#endif + diff --git a/framework/FrameworkTestUtils/include/SceneRendererHandlerMock.h b/tests/unittests/framework-test-utils/include/SceneRendererHandlerMock.h similarity index 76% rename from framework/FrameworkTestUtils/include/SceneRendererHandlerMock.h rename to tests/unittests/framework-test-utils/include/SceneRendererHandlerMock.h index a6d773678..946aff1c8 100644 --- a/framework/FrameworkTestUtils/include/SceneRendererHandlerMock.h +++ b/tests/unittests/framework-test-utils/include/SceneRendererHandlerMock.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENERENDERERHANDLERMOCK_H -#define RAMSES_SCENERENDERERHANDLERMOCK_H +#pragma once -#include "Components/ISceneRendererHandler.h" -#include "Collections/Guid.h" -#include "Components/SceneUpdate.h" +#include "internal/Components/ISceneRendererHandler.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" +#include "internal/Components/SceneUpdate.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class SceneRendererHandlerMock : public ISceneRendererHandler { @@ -25,13 +24,11 @@ namespace ramses_internal MOCK_METHOD(void, handleInitializeScene, (const SceneInfo& sceneInfo, const Guid& providerID), (override)); MOCK_METHOD(void, handleNewSceneAvailable, (const SceneInfo& newScene, const Guid& providerID), (override)); MOCK_METHOD(void, handleSceneBecameUnavailable, (const SceneId& unavailableScene, const Guid& providerID), (override)); - MOCK_METHOD(void, handleSceneUpdate_rvr, (const SceneId& sceneId, const SceneUpdate&& sceneUpdate, const Guid& providerID), ()); + MOCK_METHOD(void, handleSceneUpdate_rvr, (const SceneId& sceneId, const SceneUpdate& sceneUpdate, const Guid& providerID), ()); void handleSceneUpdate(const SceneId& sceneId, SceneUpdate&& sceneUpdate, const Guid& providerID) override { - handleSceneUpdate_rvr(sceneId, std::move(sceneUpdate), providerID); + handleSceneUpdate_rvr(sceneId, sceneUpdate, providerID); } }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/ScopedConsoleLogDisable.h b/tests/unittests/framework-test-utils/include/ScopedConsoleLogDisable.h similarity index 82% rename from framework/FrameworkTestUtils/include/ScopedConsoleLogDisable.h rename to tests/unittests/framework-test-utils/include/ScopedConsoleLogDisable.h index 181f8977f..ee5833daf 100644 --- a/framework/FrameworkTestUtils/include/ScopedConsoleLogDisable.h +++ b/tests/unittests/framework-test-utils/include/ScopedConsoleLogDisable.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORKTESTUTILS_SCOPEDCONSOLELOGDISABLE_H -#define RAMSES_FRAMEWORKTESTUTILS_SCOPEDCONSOLELOGDISABLE_H +#pragma once -#include "Utils/RamsesLogger.h" +#include "internal/Core/Utils/RamsesLogger.h" -namespace ramses_internal +namespace ramses::internal { class ScopedConsoleLogDisable { @@ -31,5 +30,3 @@ namespace ramses_internal ELogLevel m_logLevel; }; } - -#endif diff --git a/tests/unittests/framework-test-utils/include/ScopedLogContextLevel.h b/tests/unittests/framework-test-utils/include/ScopedLogContextLevel.h new file mode 100644 index 000000000..2eae75cd5 --- /dev/null +++ b/tests/unittests/framework-test-utils/include/ScopedLogContextLevel.h @@ -0,0 +1,57 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/RamsesLogger.h" + +namespace ramses::internal +{ + class ScopedLogContextLevel + { + public: + ScopedLogContextLevel(LogContext& context, ELogLevel scopeLogLevel) + : m_context(context) + , m_savedLogLevel(m_context.getLogLevel()) + { + m_context.setLogLevel(scopeLogLevel); + } + + using LogHandler = std::function; + + ScopedLogContextLevel(LogContext& context, ELogLevel scopeLogLevel, const LogHandler& handler) + : ScopedLogContextLevel(context, scopeLogLevel) + { + m_handler = handler; + m_unsetLogHandler = true; + auto ramsesHandler = [&](ELogLevel level, std::string_view ctx, std::string_view msg) { + if (ctx == m_context.getContextId()) + { + m_handler(level, msg); + } + }; + GetRamsesLogger().setLogHandler(ramsesHandler); + } + + ~ScopedLogContextLevel() + { + m_context.setLogLevel(m_savedLogLevel); + if (m_unsetLogHandler) + { + GetRamsesLogger().setLogHandler(LogHandlerFunc()); + } + } + + private: + LogContext& m_context; + LogHandler m_handler; + ELogLevel m_savedLogLevel; + bool m_unsetLogHandler = false; + }; +} diff --git a/framework/FrameworkTestUtils/include/ServiceHandlerMocks.h b/tests/unittests/framework-test-utils/include/ServiceHandlerMocks.h similarity index 73% rename from framework/FrameworkTestUtils/include/ServiceHandlerMocks.h rename to tests/unittests/framework-test-utils/include/ServiceHandlerMocks.h index 57b65fc18..333760bb5 100644 --- a/framework/FrameworkTestUtils/include/ServiceHandlerMocks.h +++ b/tests/unittests/framework-test-utils/include/ServiceHandlerMocks.h @@ -6,17 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SERVICEHANDLERMOCKS_H -#define RAMSES_SERVICEHANDLERMOCKS_H +#pragma once #include -#include "framework_common_gmock_header.h" -#include "TransportCommon/ServiceHandlerInterfaces.h" -#include "SceneReferencing/SceneReferenceEvent.h" -#include "TransportCommon/ServiceHandlerInterfaces.h" -#include "Components/ISceneProviderEventConsumer.h" +#include "internal/Communication/TransportCommon/ServiceHandlerInterfaces.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" +#include "internal/Communication/TransportCommon/ServiceHandlerInterfaces.h" +#include "internal/Components/ISceneProviderEventConsumer.h" -namespace ramses_internal +namespace ramses::internal { class SceneProviderServiceHandlerMock : public ISceneProviderServiceHandler { @@ -26,7 +24,7 @@ namespace ramses_internal MOCK_METHOD(void, handleSubscribeScene, (const SceneId& sceneId, const Guid& consumerID), (override)); MOCK_METHOD(void, handleUnsubscribeScene, (const SceneId& sceneId, const Guid& consumerID), (override)); - MOCK_METHOD(void, handleRendererEvent, (const SceneId& sceneId, const std::vector& data, const Guid& rendererID), (override)); + MOCK_METHOD(void, handleRendererEvent, (const SceneId& sceneId, const std::vector& data, const Guid& rendererID), (override)); }; class SceneRendererServiceHandlerMock : public ISceneRendererServiceHandler @@ -35,14 +33,12 @@ namespace ramses_internal SceneRendererServiceHandlerMock(); ~SceneRendererServiceHandlerMock() override; - MOCK_METHOD(void, handleNewScenesAvailable, (const SceneInfoVector& newScenes, const Guid& providerID, ramses::EFeatureLevel featureLevel), (override)); + MOCK_METHOD(void, handleNewScenesAvailable, (const SceneInfoVector& newScenes, const Guid& providerID, EFeatureLevel featureLevel), (override)); MOCK_METHOD(void, handleScenesBecameUnavailable, (const SceneInfoVector& unavailableScenes, const Guid& providerID), (override)); MOCK_METHOD(void, handleSceneNotAvailable, (const SceneId& sceneId, const Guid& providerID), (override)); MOCK_METHOD(void, handleInitializeScene, (const SceneId& sceneId, const Guid& providerID), (override)); - MOCK_METHOD(void, handleSceneUpdate, (const SceneId& sceneId, absl::Span actionData, const Guid& providerID), (override)); + MOCK_METHOD(void, handleSceneUpdate, (const SceneId& sceneId, absl::Span actionData, const Guid& providerID), (override)); }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/TestEqualHelper.h b/tests/unittests/framework-test-utils/include/TestEqualHelper.h similarity index 90% rename from framework/FrameworkTestUtils/include/TestEqualHelper.h rename to tests/unittests/framework-test-utils/include/TestEqualHelper.h index 53082ae54..f78aab906 100644 --- a/framework/FrameworkTestUtils/include/TestEqualHelper.h +++ b/tests/unittests/framework-test-utils/include/TestEqualHelper.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTEQUALHELPER_H -#define RAMSES_TESTEQUALHELPER_H +#pragma once #include "gtest/gtest.h" #include "glm/mat2x2.hpp" -namespace ramses_internal +namespace ramses::internal { template inline @@ -27,5 +26,3 @@ namespace ramses_internal } } } - -#endif diff --git a/framework/FrameworkTestUtils/include/TestRandom.h b/tests/unittests/framework-test-utils/include/TestRandom.h similarity index 85% rename from framework/FrameworkTestUtils/include/TestRandom.h rename to tests/unittests/framework-test-utils/include/TestRandom.h index d373b6f7c..c923eff8a 100644 --- a/framework/FrameworkTestUtils/include/TestRandom.h +++ b/tests/unittests/framework-test-utils/include/TestRandom.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTUTILS_TESTRANDOM_H -#define RAMSES_TESTUTILS_TESTRANDOM_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" +#include #include -namespace ramses_internal +namespace ramses::internal { class TestRandom { @@ -32,5 +31,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/framework/FrameworkTestUtils/include/UnsafeTestMemoryHelpers.h b/tests/unittests/framework-test-utils/include/UnsafeTestMemoryHelpers.h similarity index 76% rename from framework/FrameworkTestUtils/include/UnsafeTestMemoryHelpers.h rename to tests/unittests/framework-test-utils/include/UnsafeTestMemoryHelpers.h index ba897c8a1..12050cb65 100644 --- a/framework/FrameworkTestUtils/include/UnsafeTestMemoryHelpers.h +++ b/tests/unittests/framework-test-utils/include/UnsafeTestMemoryHelpers.h @@ -6,16 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTUTILS_UNSAFETESTMEMORYHELPERS_H -#define RAMSES_TESTUTILS_UNSAFETESTMEMORYHELPERS_H +#pragma once -#include "PlatformAbstraction/PlatformTypes.h" #include "absl/types/span.h" + +#include #include #include #include -namespace ramses_internal +namespace ramses::internal { namespace UnsafeTestMemoryHelpers { @@ -32,14 +32,14 @@ namespace ramses_internal } template - inline void WriteToMemoryBlob(T value, Byte* destBlob, size_t index = 0) + inline void WriteToMemoryBlob(T value, std::byte* destBlob, size_t index = 0) { static_assert(std::is_arithmetic::value, "T must be arithmetic type"); std::memcpy(destBlob + index*sizeof(T), &value, sizeof(T)); } template - inline T GetTypedValueFromMemoryBlob(const Byte* data, size_t index = 0) + inline T GetTypedValueFromMemoryBlob(const std::byte* data, size_t index = 0) { static_assert(std::is_arithmetic::value, "T must be arithmetic type"); T result; @@ -47,12 +47,10 @@ namespace ramses_internal return result; } - inline bool CompareMemoryBlobToSpan(const void* dataBlob, size_t blobSize, absl::Span theSpan) + inline bool CompareMemoryBlobToSpan(const void* dataBlob, size_t blobSize, absl::Span theSpan) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) safe to get Byte* - return absl::MakeSpan(reinterpret_cast(dataBlob), blobSize) == theSpan; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) safe to get std::byte* + return absl::MakeSpan(reinterpret_cast(dataBlob), blobSize) == theSpan; } } } - -#endif diff --git a/framework/FrameworkTestUtils/include/WindowsInvalidParameterCheckSuppression.h b/tests/unittests/framework-test-utils/include/WindowsInvalidParameterCheckSuppression.h similarity index 80% rename from framework/FrameworkTestUtils/include/WindowsInvalidParameterCheckSuppression.h rename to tests/unittests/framework-test-utils/include/WindowsInvalidParameterCheckSuppression.h index 87259df7d..f975d6582 100644 --- a/framework/FrameworkTestUtils/include/WindowsInvalidParameterCheckSuppression.h +++ b/tests/unittests/framework-test-utils/include/WindowsInvalidParameterCheckSuppression.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTUTILS_WINDOWSINVALIDPARAMETERCHECKSUPPRESSION_H -#define RAMSES_TESTUTILS_WINDOWSINVALIDPARAMETERCHECKSUPPRESSION_H +#pragma once -namespace ramses_internal +namespace ramses::internal { /* * On windows some system functions have extended error checking that can result @@ -25,11 +24,9 @@ namespace ramses_internal { public: WindowsInvalidParameterCheckSuppression(); - ~WindowsInvalidParameterCheckSuppression(); + ~WindowsInvalidParameterCheckSuppression(); // NOLINT(performance-trivially-destructible) private: - void* m_previousHandler; + void* m_previousHandler{nullptr}; }; } - -#endif diff --git a/framework/FrameworkTestUtils/src/CommunicationSystemMock.cpp b/tests/unittests/framework-test-utils/src/CommunicationSystemMock.cpp similarity index 87% rename from framework/FrameworkTestUtils/src/CommunicationSystemMock.cpp rename to tests/unittests/framework-test-utils/src/CommunicationSystemMock.cpp index 63133a8dc..12ddb2af5 100644 --- a/framework/FrameworkTestUtils/src/CommunicationSystemMock.cpp +++ b/tests/unittests/framework-test-utils/src/CommunicationSystemMock.cpp @@ -10,16 +10,16 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { CommunicationSystemMock::CommunicationSystemMock() = default; CommunicationSystemMock::~CommunicationSystemMock() = default; - void CommunicationSystemMock::setSceneProviderServiceHandler(ISceneProviderServiceHandler*) + void CommunicationSystemMock::setSceneProviderServiceHandler(ISceneProviderServiceHandler* /*handler*/) { } - void CommunicationSystemMock::setSceneRendererServiceHandler(ISceneRendererServiceHandler*) + void CommunicationSystemMock::setSceneRendererServiceHandler(ISceneRendererServiceHandler* /*handler*/) { } } diff --git a/framework/FrameworkTestUtils/src/ComponentMocks.cpp b/tests/unittests/framework-test-utils/src/ComponentMocks.cpp similarity index 85% rename from framework/FrameworkTestUtils/src/ComponentMocks.cpp rename to tests/unittests/framework-test-utils/src/ComponentMocks.cpp index 42b964809..b383ee1d3 100644 --- a/framework/FrameworkTestUtils/src/ComponentMocks.cpp +++ b/tests/unittests/framework-test-utils/src/ComponentMocks.cpp @@ -8,37 +8,21 @@ #include "ComponentMocks.h" -namespace ramses_internal +namespace ramses::internal { - ResourceProviderComponentMock::ResourceProviderComponentMock() - { - } + ResourceProviderComponentMock::ResourceProviderComponentMock() = default; - ResourceProviderComponentMock::~ResourceProviderComponentMock() - { - } + ResourceProviderComponentMock::~ResourceProviderComponentMock() = default; - SceneGraphProviderComponentMock::SceneGraphProviderComponentMock() - { - } + SceneGraphProviderComponentMock::SceneGraphProviderComponentMock() = default; - SceneGraphProviderComponentMock::~SceneGraphProviderComponentMock() - { - } + SceneGraphProviderComponentMock::~SceneGraphProviderComponentMock() = default; - SceneGraphConsumerComponentMock::SceneGraphConsumerComponentMock() - { - } + SceneGraphConsumerComponentMock::SceneGraphConsumerComponentMock() = default; - SceneGraphConsumerComponentMock::~SceneGraphConsumerComponentMock() - { - } + SceneGraphConsumerComponentMock::~SceneGraphConsumerComponentMock() = default; - SceneProviderEventConsumerMock::SceneProviderEventConsumerMock() - { - } + SceneProviderEventConsumerMock::SceneProviderEventConsumerMock() = default; - SceneProviderEventConsumerMock::~SceneProviderEventConsumerMock() - { - } + SceneProviderEventConsumerMock::~SceneProviderEventConsumerMock() = default; } diff --git a/framework/FrameworkTestUtils/src/InputStreamContainerMock.cpp b/tests/unittests/framework-test-utils/src/InputStreamContainerMock.cpp similarity index 96% rename from framework/FrameworkTestUtils/src/InputStreamContainerMock.cpp rename to tests/unittests/framework-test-utils/src/InputStreamContainerMock.cpp index 140854892..f0172425c 100644 --- a/framework/FrameworkTestUtils/src/InputStreamContainerMock.cpp +++ b/tests/unittests/framework-test-utils/src/InputStreamContainerMock.cpp @@ -8,7 +8,7 @@ #include "InputStreamContainerMock.h" -namespace ramses_internal +namespace ramses::internal { InputStreamContainerMock::InputStreamContainerMock() = default; InputStreamContainerMock::~InputStreamContainerMock() = default; diff --git a/framework/FrameworkTestUtils/src/InputStreamMock.cpp b/tests/unittests/framework-test-utils/src/InputStreamMock.cpp similarity index 95% rename from framework/FrameworkTestUtils/src/InputStreamMock.cpp rename to tests/unittests/framework-test-utils/src/InputStreamMock.cpp index 5a746b2de..3276b5330 100644 --- a/framework/FrameworkTestUtils/src/InputStreamMock.cpp +++ b/tests/unittests/framework-test-utils/src/InputStreamMock.cpp @@ -8,7 +8,7 @@ #include "InputStreamMock.h" -namespace ramses_internal +namespace ramses::internal { InputStreamMock::InputStreamMock() = default; InputStreamMock::~InputStreamMock() = default; diff --git a/framework/FrameworkTestUtils/src/MockConnectionStatusUpdateNotifier.cpp b/tests/unittests/framework-test-utils/src/MockConnectionStatusUpdateNotifier.cpp similarity index 89% rename from framework/FrameworkTestUtils/src/MockConnectionStatusUpdateNotifier.cpp rename to tests/unittests/framework-test-utils/src/MockConnectionStatusUpdateNotifier.cpp index b5748533b..333a7615b 100644 --- a/framework/FrameworkTestUtils/src/MockConnectionStatusUpdateNotifier.cpp +++ b/tests/unittests/framework-test-utils/src/MockConnectionStatusUpdateNotifier.cpp @@ -8,13 +8,9 @@ #include "MockConnectionStatusUpdateNotifier.h" -namespace ramses_internal +namespace ramses::internal { - MockConnectionStatusUpdateNotifier::MockConnectionStatusUpdateNotifier() - { - } + MockConnectionStatusUpdateNotifier::MockConnectionStatusUpdateNotifier() = default; - MockConnectionStatusUpdateNotifier::~MockConnectionStatusUpdateNotifier() - { - } + MockConnectionStatusUpdateNotifier::~MockConnectionStatusUpdateNotifier() = default; } diff --git a/framework/FrameworkTestUtils/src/ResourceMock.cpp b/tests/unittests/framework-test-utils/src/ResourceMock.cpp similarity index 91% rename from framework/FrameworkTestUtils/src/ResourceMock.cpp rename to tests/unittests/framework-test-utils/src/ResourceMock.cpp index f2dfdede1..d46e6e803 100644 --- a/framework/FrameworkTestUtils/src/ResourceMock.cpp +++ b/tests/unittests/framework-test-utils/src/ResourceMock.cpp @@ -8,7 +8,7 @@ #include "ResourceMock.h" -namespace ramses_internal +namespace ramses::internal { ResourceMock::ResourceMock(ResourceContentHash hash, EResourceType typeId) : m_hash(hash) @@ -17,7 +17,5 @@ namespace ramses_internal ON_CALL(*this, getDecompressedDataSize()).WillByDefault(Return(100u)); } - ResourceMock::~ResourceMock() - { - } + ResourceMock::~ResourceMock() = default; } diff --git a/framework/FrameworkTestUtils/src/SceneRendererHandlerMock.cpp b/tests/unittests/framework-test-utils/src/SceneRendererHandlerMock.cpp similarity index 96% rename from framework/FrameworkTestUtils/src/SceneRendererHandlerMock.cpp rename to tests/unittests/framework-test-utils/src/SceneRendererHandlerMock.cpp index 07a5b247e..91029c2d2 100644 --- a/framework/FrameworkTestUtils/src/SceneRendererHandlerMock.cpp +++ b/tests/unittests/framework-test-utils/src/SceneRendererHandlerMock.cpp @@ -8,7 +8,7 @@ #include "SceneRendererHandlerMock.h" -namespace ramses_internal +namespace ramses::internal { SceneRendererHandlerMock::SceneRendererHandlerMock() = default; SceneRendererHandlerMock::~SceneRendererHandlerMock() = default; diff --git a/framework/FrameworkTestUtils/src/ServiceHandlerMocks.cpp b/tests/unittests/framework-test-utils/src/ServiceHandlerMocks.cpp similarity index 88% rename from framework/FrameworkTestUtils/src/ServiceHandlerMocks.cpp rename to tests/unittests/framework-test-utils/src/ServiceHandlerMocks.cpp index 60ec7337a..5e24e1ee3 100644 --- a/framework/FrameworkTestUtils/src/ServiceHandlerMocks.cpp +++ b/tests/unittests/framework-test-utils/src/ServiceHandlerMocks.cpp @@ -8,21 +8,13 @@ #include "ServiceHandlerMocks.h" -namespace ramses_internal +namespace ramses::internal { - SceneProviderServiceHandlerMock::SceneProviderServiceHandlerMock() - { - } + SceneProviderServiceHandlerMock::SceneProviderServiceHandlerMock() = default; - SceneProviderServiceHandlerMock::~SceneProviderServiceHandlerMock() - { - } + SceneProviderServiceHandlerMock::~SceneProviderServiceHandlerMock() = default; - SceneRendererServiceHandlerMock::SceneRendererServiceHandlerMock() - { - } + SceneRendererServiceHandlerMock::SceneRendererServiceHandlerMock() = default; - SceneRendererServiceHandlerMock::~SceneRendererServiceHandlerMock() - { - } + SceneRendererServiceHandlerMock::~SceneRendererServiceHandlerMock() = default; } diff --git a/framework/FrameworkTestUtils/src/WindowsInvalidParameterCheckSuppression.cpp b/tests/unittests/framework-test-utils/src/WindowsInvalidParameterCheckSuppression.cpp similarity index 98% rename from framework/FrameworkTestUtils/src/WindowsInvalidParameterCheckSuppression.cpp rename to tests/unittests/framework-test-utils/src/WindowsInvalidParameterCheckSuppression.cpp index 57033faca..e5f671d49 100644 --- a/framework/FrameworkTestUtils/src/WindowsInvalidParameterCheckSuppression.cpp +++ b/tests/unittests/framework-test-utils/src/WindowsInvalidParameterCheckSuppression.cpp @@ -10,7 +10,7 @@ #include #include -namespace ramses_internal +namespace ramses::internal { #ifdef _WIN32 diff --git a/tests/unittests/framework/CMakeLists.txt b/tests/unittests/framework/CMakeLists.txt new file mode 100644 index 000000000..7a3539ba0 --- /dev/null +++ b/tests/unittests/framework/CMakeLists.txt @@ -0,0 +1,81 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +if (ramses-sdk_ENABLE_TCP_SUPPORT) + set(ramses-framework-test-TCP_MIXIN + INCLUDE_PATHS Communication/TransportTCP + SRC_FILES Communication/TransportTCP/*.h + Communication/TransportTCP/*.cpp) +endif() + +createModule( + NAME ramses-framework-test + TYPE BINARY + + INCLUDE_PATHS PlatformAbstraction + SRC_FILES PlatformAbstraction/*.h + PlatformAbstraction/*.cpp + INCLUDE_PATHS ramses-framework + SRC_FILES ramses-framework/*.h + ramses-framework/*.cpp + INCLUDE_PATHS Core/Math3d + SRC_FILES Core/Math3d/*.h + Core/Math3d/*.cpp + INCLUDE_PATHS Core/Utils + SRC_FILES Core/Utils/*.h + Core/Utils/*.cpp + SRC_FILES Core/Common/*.h + Core/Common/*.cpp + INCLUDE_PATHS Core/TaskFramework + SRC_FILES Core/TaskFramework/*.h + Core/TaskFramework/*.cpp + INCLUDE_PATHS SceneGraph/Scene + SRC_FILES SceneGraph/Scene/*.h + SceneGraph/Scene/*.cpp + INCLUDE_PATHS SceneGraph/Resource + SRC_FILES SceneGraph/Resource/*.h + SceneGraph/Resource/*.cpp + INCLUDE_PATHS SceneGraph/SceneAPI + SRC_FILES SceneGraph/SceneAPI/*.h + SceneGraph/SceneAPI/*.cpp + INCLUDE_PATHS Communication/TransportCommon + SRC_FILES Communication/TransportCommon/*.h + Communication/TransportCommon/*.cpp + INCLUDE_PATHS Ramsh + SRC_FILES Ramsh/*.h + Ramsh/*.cpp + INCLUDE_PATHS Components + SRC_FILES Components/*.h + Components/*.cpp + INCLUDE_PATHS DltLogAppender + SRC_FILES DltLogAppender/*.h + DltLogAppender/*.cpp + INCLUDE_PATHS Watchdog + SRC_FILES Watchdog/*.h + Watchdog/*.cpp + INCLUDE_PATHS SceneReferencing + SRC_FILES SceneReferencing/*.h + SceneReferencing/*.cpp + + ${ramses-framework-test-TCP_MIXIN} + + SRC_FILES main.cpp + + DEPENDENCIES ramses-framework + framework-test-utils + ramses-gmock-main +) + +# disable maybe uninitialized +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0) + target_compile_options(ramses-framework-test PRIVATE -Wno-maybe-uninitialized) + endif() +endif() + +makeTestFromTarget(TARGET ramses-framework-test SUFFIX UNITTEST) diff --git a/framework/Communication/TransportCommon/test/CommunicationSystemTest.cpp b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTest.cpp similarity index 97% rename from framework/Communication/TransportCommon/test/CommunicationSystemTest.cpp rename to tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTest.cpp index c29870b22..c6577c823 100644 --- a/framework/Communication/TransportCommon/test/CommunicationSystemTest.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTest.cpp @@ -9,10 +9,10 @@ #include "ServiceHandlerMocks.h" #include "CommunicationSystemTest.h" #include "ConnectionSystemTestHelper.h" -#include "Components/SceneUpdate.h" -#include "TransportCommon/SceneUpdateSerializer.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializer.h" -namespace ramses_internal +namespace ramses::internal { INSTANTIATE_TEST_SUITE_P(TypedCommunicationTest, ACommunicationSystem, ::testing::ValuesIn(CommunicationSystemTestState::GetAvailableCommunicationSystemTypes())); @@ -37,9 +37,9 @@ namespace ramses_internal Guid to(5); StatisticCollectionScene sceneStatistics; auto csw = std::make_unique(*state); - EXPECT_FALSE(csw->commSystem->broadcastNewScenesAvailable(SceneInfoVector(), ramses::EFeatureLevel_01)); + EXPECT_FALSE(csw->commSystem->broadcastNewScenesAvailable(SceneInfoVector(), EFeatureLevel_01)); EXPECT_FALSE(csw->commSystem->broadcastScenesBecameUnavailable(SceneInfoVector())); - EXPECT_FALSE(csw->commSystem->sendScenesAvailable(to, SceneInfoVector(), ramses::EFeatureLevel_01)); + EXPECT_FALSE(csw->commSystem->sendScenesAvailable(to, SceneInfoVector(), EFeatureLevel_01)); EXPECT_FALSE(csw->commSystem->sendSubscribeScene(to, SceneId(123))); EXPECT_FALSE(csw->commSystem->sendUnsubscribeScene(to, SceneId(123))); EXPECT_FALSE(csw->commSystem->sendInitializeScene(to, SceneId())); @@ -54,9 +54,9 @@ namespace ramses_internal csw->commSystem->connectServices(); csw->commSystem->disconnectServices(); - EXPECT_FALSE(csw->commSystem->broadcastNewScenesAvailable(SceneInfoVector(), ramses::EFeatureLevel_01)); + EXPECT_FALSE(csw->commSystem->broadcastNewScenesAvailable(SceneInfoVector(), EFeatureLevel_01)); EXPECT_FALSE(csw->commSystem->broadcastScenesBecameUnavailable(SceneInfoVector())); - EXPECT_FALSE(csw->commSystem->sendScenesAvailable(to, SceneInfoVector(), ramses::EFeatureLevel_01)); + EXPECT_FALSE(csw->commSystem->sendScenesAvailable(to, SceneInfoVector(), EFeatureLevel_01)); EXPECT_FALSE(csw->commSystem->sendSubscribeScene(to, SceneId(123))); EXPECT_FALSE(csw->commSystem->sendUnsubscribeScene(to, SceneId(123))); EXPECT_FALSE(csw->commSystem->sendInitializeScene(to, SceneId())); diff --git a/framework/Communication/TransportCommon/test/CommunicationSystemTest.h b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTest.h similarity index 87% rename from framework/Communication/TransportCommon/test/CommunicationSystemTest.h rename to tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTest.h index 3d3ba4218..3aa628876 100644 --- a/framework/Communication/TransportCommon/test/CommunicationSystemTest.h +++ b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTest.h @@ -6,16 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMUNICATION_COMMUNICATIONSYSTEMTEST_H -#define RAMSES_COMMUNICATION_COMMUNICATIONSYSTEMTEST_H +#pragma once -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" #include "MockConnectionStatusListener.h" #include "CommunicationSystemTestWrapper.h" -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -46,5 +44,3 @@ namespace ramses_internal #define TESTING_SERVICETYPE_RAMSES(commsysProvider) \ ::testing::Combine(::testing::ValuesIn(commsysProvider), ::testing::Values(EServiceType::Ramses)) } - -#endif diff --git a/framework/Communication/TransportCommon/test/CommunicationSystemTestWrapper.cpp b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTestWrapper.cpp similarity index 93% rename from framework/Communication/TransportCommon/test/CommunicationSystemTestWrapper.cpp rename to tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTestWrapper.cpp index e378dd023..27bc6701e 100644 --- a/framework/Communication/TransportCommon/test/CommunicationSystemTestWrapper.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTestWrapper.cpp @@ -7,14 +7,14 @@ // ------------------------------------------------------------------------- #include "CommunicationSystemTestWrapper.h" -#include "TransportCommon/CommunicationSystemFactory.h" -#include "Common/ParticipantIdentifier.h" -#include "TransportCommon/IConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/CommunicationSystemFactory.h" +#include "internal/Core/Common/ParticipantIdentifier.h" +#include "internal/Communication/TransportCommon/IConnectionStatusUpdateNotifier.h" #include "TestRandom.h" -#include "RamsesFrameworkConfigImpl.h" +#include "impl/RamsesFrameworkConfigImpl.h" #include -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -107,7 +107,7 @@ namespace ramses_internal : id(id_.isValid() ? id_ : Guid(TestRandom::Get(255, std::numeric_limits::max()))) , state(state_) { - ramses::RamsesFrameworkConfigImpl config(ramses::EFeatureLevel_Latest); + RamsesFrameworkConfigImpl config(EFeatureLevel_Latest); commSystem = CommunicationSystemFactory::ConstructCommunicationSystem(config, ParticipantIdentifier(id, name), frameworkLock, statisticCollection); state.knownCommunicationSystems.push_back(this); diff --git a/framework/Communication/TransportCommon/test/CommunicationSystemTestWrapper.h b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTestWrapper.h similarity index 88% rename from framework/Communication/TransportCommon/test/CommunicationSystemTestWrapper.h rename to tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTestWrapper.h index 0ac24c5e8..8b3dd61ca 100644 --- a/framework/Communication/TransportCommon/test/CommunicationSystemTestWrapper.h +++ b/tests/unittests/framework/Communication/TransportCommon/CommunicationSystemTestWrapper.h @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMUNICATIONSYSTEMTESTWRAPPER_H -#define RAMSES_COMMUNICATIONSYSTEMTESTWRAPPER_H +#pragma once -#include "TransportCommon/ICommunicationSystem.h" +#include "internal/Communication/TransportCommon/ICommunicationSystem.h" #include "MockConnectionStatusListener.h" -#include "PlatformAbstraction/PlatformLock.h" -#include "Utils/StatisticCollection.h" +#include "internal/PlatformAbstraction/PlatformLock.h" +#include "internal/Core/Utils/StatisticCollection.h" #include "ServiceHandlerMocks.h" #include "ConnectionSystemTestHelper.h" -#include "Collections/Guid.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" #include -namespace ramses_internal +namespace ramses::internal { enum class ECommunicationSystemType { @@ -76,5 +75,3 @@ namespace ramses_internal CommunicationSystemTestState& state; }; } - -#endif diff --git a/framework/Communication/TransportCommon/test/ConnectionStatusUpdateNotifierTest.cpp b/tests/unittests/framework/Communication/TransportCommon/ConnectionStatusUpdateNotifierTest.cpp similarity index 96% rename from framework/Communication/TransportCommon/test/ConnectionStatusUpdateNotifierTest.cpp rename to tests/unittests/framework/Communication/TransportCommon/ConnectionStatusUpdateNotifierTest.cpp index 02ac16428..5b5c3a89b 100644 --- a/framework/Communication/TransportCommon/test/ConnectionStatusUpdateNotifierTest.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/ConnectionStatusUpdateNotifierTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "TransportCommon/ConnectionStatusUpdateNotifier.h" +#include "internal/Communication/TransportCommon/ConnectionStatusUpdateNotifier.h" #include "MockConnectionStatusListener.h" -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" -namespace ramses_internal +namespace ramses::internal { using namespace ::testing; diff --git a/framework/Communication/TransportCommon/test/ConnectionSystemTestHelper.cpp b/tests/unittests/framework/Communication/TransportCommon/ConnectionSystemTestHelper.cpp similarity index 73% rename from framework/Communication/TransportCommon/test/ConnectionSystemTestHelper.cpp rename to tests/unittests/framework/Communication/TransportCommon/ConnectionSystemTestHelper.cpp index ad457e163..492ab2e75 100644 --- a/framework/Communication/TransportCommon/test/ConnectionSystemTestHelper.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/ConnectionSystemTestHelper.cpp @@ -7,15 +7,14 @@ // ------------------------------------------------------------------------- #include "ConnectionSystemTestHelper.h" -#include "TransportCommon/CommunicationSystemFactory.h" -#include "TransportCommon/IDiscoveryDaemon.h" -#include "RamsesFrameworkConfigImpl.h" +#include "internal/Communication/TransportCommon/CommunicationSystemFactory.h" +#include "internal/Communication/TransportCommon/IDiscoveryDaemon.h" +#include "impl/RamsesFrameworkConfigImpl.h" -namespace ramses_internal +namespace ramses::internal { AsyncEventCounter::AsyncEventCounter(uint32_t waitTimeMs) - : m_eventCounter(0) - , m_waitTimeMs(waitTimeMs) + : m_waitTimeMs(waitTimeMs) { } @@ -37,13 +36,10 @@ namespace ramses_internal m_eventCounter -= numberEventsToWaitFor; return testing::AssertionSuccess(); } - else - { - // clear all events on timeout - const auto currentEventCounter = m_eventCounter; - m_eventCounter = 0; - return testing::AssertionFailure() << "Timeout while waiting for SyncEvents (waitForEvents): Expected " << numberEventsToWaitFor << ", got " << currentEventCounter; - } + // clear all events on timeout + const auto currentEventCounter = m_eventCounter; + m_eventCounter = 0; + return testing::AssertionFailure() << "Timeout while waiting for SyncEvents (waitForEvents): Expected " << numberEventsToWaitFor << ", got " << currentEventCounter; } void AsyncEventCounter::discardPendingEvents() @@ -52,9 +48,9 @@ namespace ramses_internal m_eventCounter = 0; } - ConnectionSystemTestDaemon::ConnectionSystemTestDaemon(const std::function& configModifier) + ConnectionSystemTestDaemon::ConnectionSystemTestDaemon(const std::function& configModifier) { - ramses::RamsesFrameworkConfigImpl config(ramses::EFeatureLevel_Latest); + RamsesFrameworkConfigImpl config(EFeatureLevel_Latest); if (configModifier) configModifier(config); daemon = CommunicationSystemFactory::ConstructDiscoveryDaemon(config, frameworkLock, statisticCollection); diff --git a/framework/Communication/TransportCommon/test/ConnectionSystemTestHelper.h b/tests/unittests/framework/Communication/TransportCommon/ConnectionSystemTestHelper.h similarity index 80% rename from framework/Communication/TransportCommon/test/ConnectionSystemTestHelper.h rename to tests/unittests/framework/Communication/TransportCommon/ConnectionSystemTestHelper.h index 5efa38b41..0126081ba 100644 --- a/framework/Communication/TransportCommon/test/ConnectionSystemTestHelper.h +++ b/tests/unittests/framework/Communication/TransportCommon/ConnectionSystemTestHelper.h @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_FRAMEWORK_CONNECTIONSYSTEMTESTHELPER_H -#define RAMSES_FRAMEWORK_CONNECTIONSYSTEMTESTHELPER_H +#pragma once -#include "Utils/StatisticCollection.h" -#include "PlatformAbstraction/PlatformLock.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/PlatformAbstraction/PlatformLock.h" #include "gtest/gtest.h" #include #include #include #include -namespace ramses + +namespace ramses::internal { class RamsesFrameworkConfigImpl; -} -namespace ramses_internal -{ class IDiscoveryDaemon; class AsyncEventCounter @@ -39,14 +36,14 @@ namespace ramses_internal private: std::mutex lock; std::condition_variable cond; - uint32_t m_eventCounter; + uint32_t m_eventCounter{0}; uint32_t m_waitTimeMs; }; class ConnectionSystemTestDaemon { public: - explicit ConnectionSystemTestDaemon(const std::function& configModifier = {}); + explicit ConnectionSystemTestDaemon(const std::function& configModifier = {}); ~ConnectionSystemTestDaemon(); bool start(); @@ -57,5 +54,3 @@ namespace ramses_internal StatisticCollectionFramework statisticCollection; }; } - -#endif diff --git a/framework/Communication/TransportCommon/test/FlushInformationSerializationHelperTest.cpp b/tests/unittests/framework/Communication/TransportCommon/FlushInformationSerializationHelperTest.cpp similarity index 93% rename from framework/Communication/TransportCommon/test/FlushInformationSerializationHelperTest.cpp rename to tests/unittests/framework/Communication/TransportCommon/FlushInformationSerializationHelperTest.cpp index 3e3469404..431117599 100644 --- a/framework/Communication/TransportCommon/test/FlushInformationSerializationHelperTest.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/FlushInformationSerializationHelperTest.cpp @@ -6,28 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SceneUpdateSerializationHelper.h" -#include "Components/SceneUpdate.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h" +#include "internal/Components/SceneUpdate.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class AFlushInformationSerialization : public ::testing::Test { public: FlushInformation SerializeDeserialize(const FlushInformation& flushInfos) { - const absl::Span infos = FlushInformationSerialization::SerializeInfos(flushInfos, workingMem); + const absl::Span infos = FlushInformationSerialization::SerializeInfos(flushInfos, workingMem); return FlushInformationSerialization::Deserialize(infos); } - std::vector workingMem; + std::vector workingMem; FlushInformation in{}; }; TEST_F(AFlushInformationSerialization, minimumSizeIsSizeOfEmptyFlushInfoSerialized) { - const absl::Span infos = FlushInformationSerialization::SerializeInfos(in, workingMem); + const absl::Span infos = FlushInformationSerialization::SerializeInfos(in, workingMem); ASSERT_EQ(FlushInformation::getMinimumSize(), infos.size()); } diff --git a/framework/Communication/TransportCommon/test/MockConnectionStatusListener.cpp b/tests/unittests/framework/Communication/TransportCommon/MockConnectionStatusListener.cpp similarity index 92% rename from framework/Communication/TransportCommon/test/MockConnectionStatusListener.cpp rename to tests/unittests/framework/Communication/TransportCommon/MockConnectionStatusListener.cpp index b11b93987..4c40ffff2 100644 --- a/framework/Communication/TransportCommon/test/MockConnectionStatusListener.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/MockConnectionStatusListener.cpp @@ -8,13 +8,9 @@ #include "MockConnectionStatusListener.h" -namespace ramses_internal +namespace ramses::internal { - MockConnectionStatusListener::MockConnectionStatusListener() - { - } + MockConnectionStatusListener::MockConnectionStatusListener() = default; - MockConnectionStatusListener::~MockConnectionStatusListener() - { - } + MockConnectionStatusListener::~MockConnectionStatusListener() = default; } diff --git a/framework/Communication/TransportCommon/test/MockConnectionStatusListener.h b/tests/unittests/framework/Communication/TransportCommon/MockConnectionStatusListener.h similarity index 79% rename from framework/Communication/TransportCommon/test/MockConnectionStatusListener.h rename to tests/unittests/framework/Communication/TransportCommon/MockConnectionStatusListener.h index cfe74b6f0..38552fae7 100644 --- a/framework/Communication/TransportCommon/test/MockConnectionStatusListener.h +++ b/tests/unittests/framework/Communication/TransportCommon/MockConnectionStatusListener.h @@ -6,15 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MOCKCONNECTIONSTATUSLISTENER_H -#define RAMSES_MOCKCONNECTIONSTATUSLISTENER_H +#pragma once -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "TransportCommon/IConnectionStatusListener.h" +#include "internal/Communication/TransportCommon/IConnectionStatusListener.h" -namespace ramses_internal +namespace ramses::internal { class MockConnectionStatusListener : public IConnectionStatusListener { @@ -26,5 +24,3 @@ namespace ramses_internal MOCK_METHOD(void, participantHasDisconnected, (const Guid& guid), (override)); }; } - -#endif diff --git a/framework/Communication/TransportCommon/test/SceneUpdateSerializationHelperTest.cpp b/tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializationHelperTest.cpp similarity index 82% rename from framework/Communication/TransportCommon/test/SceneUpdateSerializationHelperTest.cpp rename to tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializationHelperTest.cpp index 51068d13f..b9e116526 100644 --- a/framework/Communication/TransportCommon/test/SceneUpdateSerializationHelperTest.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializationHelperTest.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SceneUpdateSerializationHelper.h" -#include "Scene/SceneActionCollection.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializationHelper.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" #include "ResourceSerializationTestHelper.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class ASceneActionSerialization : public ::testing::Test { public: SceneActionCollection SerializeDeserialize(const SceneActionCollection& actions) { - const absl::Span desc = SceneActionSerialization::SerializeDescription(actions, workingMem); - const absl::Span data = SceneActionSerialization::SerializeData(actions); + const absl::Span desc = SceneActionSerialization::SerializeDescription(actions, workingMem); + const absl::Span data = SceneActionSerialization::SerializeData(actions); return SceneActionSerialization::Deserialize(desc, data); } - std::vector workingMem; + std::vector workingMem; }; TEST_F(ASceneActionSerialization, canSerializeDeserializeEmptyCollection) @@ -50,12 +50,12 @@ namespace ramses_internal public: std::unique_ptr SerializeDeserialize(const IResource& resource) { - const absl::Span desc = ResourceSerialization::SerializeDescription(resource, workingMem); - const absl::Span data = ResourceSerialization::SerializeData(resource); + const absl::Span desc = ResourceSerialization::SerializeDescription(resource, workingMem); + const absl::Span data = ResourceSerialization::SerializeData(resource); return ResourceSerialization::Deserialize(desc, data); } - std::vector workingMem; + std::vector workingMem; }; template @@ -108,8 +108,8 @@ namespace ramses_internal TEST_F(AResourceSerialization, deserializeFailsWithDataSizeMatch) { const std::unique_ptr res(ResourceSerializationTestHelper::CreateTestResource(1000)); - const absl::Span desc = ResourceSerialization::SerializeDescription(*res, workingMem); - const absl::Span data = ResourceSerialization::SerializeData(*res); + const absl::Span desc = ResourceSerialization::SerializeDescription(*res, workingMem); + const absl::Span data = ResourceSerialization::SerializeData(*res); ASSERT_GT(data.size(), 0u); std::unique_ptr deserRes(ResourceSerialization::Deserialize(desc, data.subspan(1))); diff --git a/framework/Communication/TransportCommon/test/SceneUpdateSerializationTest.cpp b/tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializationTest.cpp similarity index 93% rename from framework/Communication/TransportCommon/test/SceneUpdateSerializationTest.cpp rename to tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializationTest.cpp index 8f8cf4c17..4f4c93dc8 100644 --- a/framework/Communication/TransportCommon/test/SceneUpdateSerializationTest.cpp +++ b/tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializationTest.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/SceneUpdateSerializer.h" -#include "TransportCommon/SceneUpdateStreamDeserializer.h" -#include "Components/SceneUpdate.h" -#include "Scene/SceneActionCollection.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializer.h" +#include "internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" #include "gtest/gtest.h" -#include "Components/ResourceDeleterCallingCallback.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" #include "ResourceMock.h" #include "ResourceSerializationTestHelper.h" -#include "Utils/StatisticCollection.h" +#include "internal/Core/Utils/StatisticCollection.h" #include "gmock/gmock.h" #include -namespace ramses_internal +namespace ramses::internal { class ASceneUpdateSerialization : public ::testing::Test { @@ -27,7 +27,7 @@ namespace ramses_internal bool serialize(size_t pktSize) { SceneUpdateSerializer sus(update, sceneStatistics); - std::vector vec(pktSize); + std::vector vec(pktSize); return sus.writeToPackets({vec.data(), vec.size()}, [&](size_t s) { data.push_back(vec); data.back().resize(s); @@ -90,8 +90,8 @@ namespace ramses_internal ManagedResource CreateTestResource(uint32_t blobSize) { - IResource* res = new ArrayResource(EResourceType_VertexArray, blobSize, EDataType::Vector3F, nullptr, ResourceCacheFlag(15u), "resName"); - if (blobSize) + IResource* res = new ArrayResource(EResourceType::VertexArray, blobSize, EDataType::Vector3F, nullptr, "resName"); + if (blobSize != 0u) ResourceSerializationTestHelper::SetResourceDataRandom(*res, blobSize); return ManagedResource{ res, deleterMock }; } @@ -107,7 +107,7 @@ namespace ramses_internal ResourceDeleterCallingCallback deleterMock; SceneUpdate update; StatisticCollectionScene sceneStatistics; - std::vector> data; + std::vector> data; }; TEST_F(ASceneUpdateSerialization, canSerializeDeserializeEmptyUpdate) @@ -206,7 +206,7 @@ namespace ramses_internal TEST_F(ASceneUpdateSerialization, failsSerializeWhenWriteFunctionFailsOnFirstPacket) { SceneUpdateSerializer sus(update, sceneStatistics); - std::vector vec(60); + std::vector vec(60); EXPECT_FALSE(sus.writeToPackets({vec.data(), vec.size()}, [&](size_t) { return false; })); @@ -216,7 +216,7 @@ namespace ramses_internal { update.resources.push_back(CreateTestResource(2500)); SceneUpdateSerializer sus(update, sceneStatistics); - std::vector vec(60); + std::vector vec(60); int cnt = 0; EXPECT_FALSE(sus.writeToPackets({vec.data(), vec.size()}, [&](size_t) { if (++cnt == 10) @@ -230,7 +230,7 @@ namespace ramses_internal for (size_t i = 0; i < 100; ++i) addTestActions(); SceneUpdateSerializer sus(update, sceneStatistics); - std::vector vec(60); + std::vector vec(60); int cnt = 0; EXPECT_FALSE(sus.writeToPackets({vec.data(), vec.size()}, [&](size_t) { if (++cnt == 5) @@ -337,7 +337,7 @@ namespace ramses_internal TEST_F(ASceneUpdateSerialization, failsDeserializeZeroFilledPacket) { - const auto res = deser.processData(std::vector(60, 0)); + const auto res = deser.processData(std::vector(60, std::byte{0})); EXPECT_EQ(SceneUpdateStreamDeserializer::ResultType::Failed, res.result); } @@ -349,7 +349,7 @@ namespace ramses_internal for (size_t i = 1; i < 24; ++i) { SCOPED_TRACE(i); - std::vector vec = data[0]; + std::vector vec = data[0]; vec.resize(i); SceneUpdateStreamDeserializer localDeser; EXPECT_EQ(SceneUpdateStreamDeserializer::ResultType::Failed, localDeser.processData(vec).result); @@ -366,7 +366,6 @@ namespace ramses_internal std::uniform_int_distribution dis(start, end); return dis(gen); }; - std::vector blob; for (int run = 0; run < 500; ++run) { @@ -376,8 +375,6 @@ namespace ramses_internal for (uint32_t i = 0; i < numActions; ++i) { update.actions.beginWriteSceneAction(static_cast(rnd(1, 50))); - blob.resize(rnd(0, 2000)); - std::iota(blob.begin(), blob.end(), static_cast(rnd(0, 255))); } const uint32_t numResources = rnd(0, 50); diff --git a/framework/Communication/TransportCommon/test/SceneUpdateSerializerTestHelper.h b/tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializerTestHelper.h similarity index 63% rename from framework/Communication/TransportCommon/test/SceneUpdateSerializerTestHelper.h rename to tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializerTestHelper.h index 277ec945c..21fd76c58 100644 --- a/framework/Communication/TransportCommon/test/SceneUpdateSerializerTestHelper.h +++ b/tests/unittests/framework/Communication/TransportCommon/SceneUpdateSerializerTestHelper.h @@ -6,32 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEUPDATESERIALIZERTESTHELPER_H -#define RAMSES_SCENEUPDATESERIALIZERTESTHELPER_H +#pragma once -#include "TransportCommon/ISceneUpdateSerializer.h" +#include "internal/Communication/TransportCommon/ISceneUpdateSerializer.h" #include "gmock/gmock.h" #include -namespace ramses_internal +namespace ramses::internal { class SceneUpdateSerializerMock : public ISceneUpdateSerializer { public: - MOCK_METHOD(bool, writeToPackets, (absl::Span packetMem, const std::function& writeDoneFunc), (const, override)); + MOCK_METHOD(bool, writeToPackets, (absl::Span packetMem, const std::function& writeDoneFunc), (const, override)); }; class FakseSceneUpdateSerializer : public ISceneUpdateSerializer { public: - FakseSceneUpdateSerializer(const std::vector>& data_, size_t expectedSize_) - : data(data_) + FakseSceneUpdateSerializer(std::vector> data_, size_t expectedSize_) + : data(std::move(data_)) , expectedSize(expectedSize_) { } - bool writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const override + bool writeToPackets(absl::Span packetMem, const std::function& writeDoneFunc) const override { EXPECT_EQ(expectedSize, packetMem.size()); for (const auto& d : data) @@ -45,14 +44,14 @@ namespace ramses_internal return true; } - std::vector> data; + std::vector> data; const size_t expectedSize; }; - inline std::vector> TestSerializeSceneUpdateToVectorChunked(const ISceneUpdateSerializer& serializer, uint32_t chunkSize = 100000) + inline std::vector> TestSerializeSceneUpdateToVectorChunked(const ISceneUpdateSerializer& serializer, uint32_t chunkSize = 100000) { - std::vector> result; - std::vector pkt(chunkSize); + std::vector> result; + std::vector pkt(chunkSize); bool ok = serializer.writeToPackets({pkt.data(), pkt.size()}, [&](size_t size) { result.push_back(pkt); result.back().resize(size); @@ -64,5 +63,3 @@ namespace ramses_internal return result; } } - -#endif diff --git a/framework/Communication/TransportTCP/test/TCPConnectionSystemTest.cpp b/tests/unittests/framework/Communication/TransportTCP/TCPConnectionSystemTest.cpp similarity index 96% rename from framework/Communication/TransportTCP/test/TCPConnectionSystemTest.cpp rename to tests/unittests/framework/Communication/TransportTCP/TCPConnectionSystemTest.cpp index f8c69fe47..4b3994270 100644 --- a/framework/Communication/TransportTCP/test/TCPConnectionSystemTest.cpp +++ b/tests/unittests/framework/Communication/TransportTCP/TCPConnectionSystemTest.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportTCP/TCPConnectionSystem.h" -#include "Utils/StatisticCollection.h" -#include "Utils/ThreadBarrier.h" +#include "internal/Communication/TransportTCP/TCPConnectionSystem.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "ScopedConsoleLogDisable.h" #include "CommunicationSystemTest.h" #include "gtest/gtest.h" #include -namespace ramses_internal +namespace ramses::internal { class ATCPConnectionSystem : public ::testing::Test { diff --git a/framework/Components/test/AbstractSenderAndReceiverTest.h b/tests/unittests/framework/Components/AbstractSenderAndReceiverTest.h similarity index 92% rename from framework/Components/test/AbstractSenderAndReceiverTest.h rename to tests/unittests/framework/Components/AbstractSenderAndReceiverTest.h index 7cbf81614..34b867ba4 100644 --- a/framework/Components/test/AbstractSenderAndReceiverTest.h +++ b/tests/unittests/framework/Components/AbstractSenderAndReceiverTest.h @@ -6,15 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ABSTRACTSENDERANDRECEIVERTEST_H -#define RAMSES_ABSTRACTSENDERANDRECEIVERTEST_H +#pragma once #include -#include "framework_common_gmock_header.h" #include "CommunicationSystemTestWrapper.h" -#include "Common/TypedMemoryHandle.h" +#include "internal/Core/Common/TypedMemoryHandle.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -34,9 +32,7 @@ namespace ramses_internal { } - ~AbstractSenderAndReceiverTest() override - { - } + ~AbstractSenderAndReceiverTest() override = default; ::testing::AssertionResult waitForEvent(int numberEvents = 1, uint32_t waitTimeMsOverride = 0) { @@ -99,5 +95,3 @@ namespace ramses_internal bool statisticsTestEnabled = true; }; } - -#endif diff --git a/framework/Components/test/ClientSceneLogicTest.cpp b/tests/unittests/framework/Components/ClientSceneLogicTest.cpp similarity index 82% rename from framework/Components/test/ClientSceneLogicTest.cpp rename to tests/unittests/framework/Components/ClientSceneLogicTest.cpp index 65848d473..e70a750a8 100644 --- a/framework/Components/test/ClientSceneLogicTest.cpp +++ b/tests/unittests/framework/Components/ClientSceneLogicTest.cpp @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" #include "ComponentMocks.h" -#include "Components/ClientSceneLogicShadowCopy.h" -#include "Components/ClientSceneLogicDirect.h" -#include "Components/ISceneGraphSender.h" -#include "Scene/ClientScene.h" -#include "Scene/EScenePublicationMode.h" -#include "Scene/SceneActionApplier.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "Components/SceneUpdate.h" -#include "Resource/ArrayResource.h" -#include "Resource/TextureResource.h" -#include "Resource/EffectResource.h" - -using namespace ramses_internal; - -namespace +#include "internal/Components/ClientSceneLogicShadowCopy.h" +#include "internal/Components/ClientSceneLogicDirect.h" +#include "internal/Components/ISceneGraphSender.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/EScenePublicationMode.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" + +namespace ramses::internal { // Need real matcher class to handle copying of ctor argument by calling explicit copy class ContainsSceneUpdateMatcher : public MatcherInterface @@ -75,12 +72,11 @@ namespace inline Matcher ContainsSceneUpdate(const SceneUpdate& sceneupdate) { return MakeMatcher(new ContainsSceneUpdateMatcher(sceneupdate)); } -} class SceneGraphSenderMock : public ISceneGraphSender { public: - SceneGraphSenderMock() {} + SceneGraphSenderMock() = default; ~SceneGraphSenderMock() override = default; MOCK_METHOD(void, sendPublishScene, (SceneId sceneId, EScenePublicationMode publicationMode, std::string_view name), (override)); MOCK_METHOD(void, sendUnpublishScene, (SceneId sceneId, EScenePublicationMode publicationMode), (override)); @@ -104,9 +100,9 @@ class AClientSceneLogic_All : public ::testing::Test , m_scene(SceneInfo(m_sceneId)) , m_sceneLogic(m_sceneGraphProviderComponent, m_scene, m_resourceComponent, m_myID) , m_rendererID(1337) - , m_arrayResourceRaw(new ArrayResource(EResourceType_IndexArray, 1, EDataType::UInt16, nullptr, ResourceCacheFlag_DoNotCache, {})) - , m_effectResourceRaw(new EffectResource("foo", {}, {}, {}, {}, {}, {}, ResourceCacheFlag_DoNotCache)) - , m_textureResourceRaw(new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {})) + , m_arrayResourceRaw(new ArrayResource(EResourceType::IndexArray, 1, EDataType::UInt16, nullptr, {})) + , m_effectResourceRaw(new EffectResource("foo", {}, {}, {}, {}, {}, {})) + , m_textureResourceRaw(new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {})) , m_arrayResource(m_arrayResourceRaw) , m_effectResource(m_effectResourceRaw) , m_textureResource(m_textureResourceRaw) @@ -123,8 +119,8 @@ class AClientSceneLogic_All : public ::testing::Test void publish() { std::string_view name{m_scene.getName()}; - EXPECT_CALL(m_sceneGraphProviderComponent, sendPublishScene(m_sceneId, ramses_internal::EScenePublicationMode_LocalAndRemote, name)); - m_sceneLogic.publish(ramses_internal::EScenePublicationMode_LocalAndRemote); + EXPECT_CALL(m_sceneGraphProviderComponent, sendPublishScene(m_sceneId, EScenePublicationMode::LocalAndRemote, name)); + m_sceneLogic.publish(EScenePublicationMode::LocalAndRemote); } void unpublish() @@ -232,13 +228,13 @@ class AClientSceneLogic_All : public ::testing::Test EXPECT_EQ(stats.statResourceMaxSize[index].getCounterValue(), cmp[2]); } - ramses_internal::Guid m_myID; - ramses_internal::SceneId m_sceneId; - ramses_internal::ClientScene m_scene; + Guid m_myID; + SceneId m_sceneId; + ClientScene m_scene; StrictMock m_resourceComponent; StrictMock m_sceneGraphProviderComponent; T m_sceneLogic; - ramses_internal::Guid m_rendererID; + Guid m_rendererID; ArrayResource* m_arrayResourceRaw; EffectResource* m_effectResourceRaw; @@ -252,13 +248,13 @@ class AClientSceneLogic_All : public ::testing::Test class AClientSceneLogic_ShadowCopy : public AClientSceneLogic_All { public: - AClientSceneLogic_ShadowCopy() {} + AClientSceneLogic_ShadowCopy() = default; }; class AClientSceneLogic_Direct : public AClientSceneLogic_All { public: - AClientSceneLogic_Direct() {} + AClientSceneLogic_Direct() = default; }; using ClientSceneLogicTypes = ::testing::Types; @@ -279,7 +275,7 @@ TYPED_TEST(AClientSceneLogic_All, doesNotSendSceneRegistrationWhenCreatingSceneR TYPED_TEST(AClientSceneLogic_All, doesNotSendSceneRegistrationWhenCreatingSceneRegisteringAppAndDisablingSceneDistribution) { // strict mock ensures that creating causes no further calls to SceneGraphProviderComponent - this->m_scene.allocateTransform(this->m_scene.allocateNode()); + this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(2u, this->m_scene.getSceneActionCollection().numberOfActions()); this->m_sceneLogic.unpublish(); } @@ -291,7 +287,7 @@ TYPED_TEST(AClientSceneLogic_All, isNotPublishedInitially) TYPED_TEST(AClientSceneLogic_All, clearsCollectedActionsOnFlush) { - this->m_scene.allocateTransform(this->m_scene.allocateNode()); + this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(2u, this->m_scene.getSceneActionCollection().numberOfActions()); this->m_sceneLogic.flushSceneActions({}, {}); @@ -301,7 +297,7 @@ TYPED_TEST(AClientSceneLogic_All, clearsCollectedActionsOnFlush) TYPED_TEST(AClientSceneLogic_All, keepsPendingActionIfNotFlushed) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); const SceneActionCollection& actions = this->m_scene.getSceneActionCollection(); EXPECT_EQ(1u, actions.numberOfActions()); @@ -313,7 +309,7 @@ TYPED_TEST(AClientSceneLogic_All, keepsPendingActionVectorIfNotFlushed) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateTransform(this->m_scene.allocateNode()); + this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); const SceneActionCollection& actions = this->m_scene.getSceneActionCollection(); EXPECT_EQ(2u, actions.numberOfActions()); @@ -340,9 +336,9 @@ TYPED_TEST(AClientSceneLogic_All, doesNotSendSceneOutIfSceneDistributedWithNoSub TYPED_TEST(AClientSceneLogic_All, sendsPublishOnlyOnce) { std::string_view name{this->m_scene.getName()}; - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendPublishScene(this->m_sceneId, ramses_internal::EScenePublicationMode_LocalAndRemote, name)); - this->m_sceneLogic.publish(ramses_internal::EScenePublicationMode_LocalAndRemote); - this->m_sceneLogic.publish(ramses_internal::EScenePublicationMode_LocalAndRemote); + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendPublishScene(this->m_sceneId, EScenePublicationMode::LocalAndRemote, name)); + this->m_sceneLogic.publish(EScenePublicationMode::LocalAndRemote); + this->m_sceneLogic.publish(EScenePublicationMode::LocalAndRemote); this->expectSceneUnpublish(); } @@ -366,7 +362,7 @@ TYPED_TEST(AClientSceneLogic_All, flushesPendingActionsIfSceneDistributedAndHasP { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); this->m_sceneLogic.flushSceneActions({}, {}); @@ -376,20 +372,107 @@ TYPED_TEST(AClientSceneLogic_All, flushesPendingActionsIfSceneDistributedAndHasP this->expectSceneUnpublish(); } -TEST_F(AClientSceneLogic_Direct, everyFlushGeneratesSceneActionSentToSubscriber) +TEST_F(AClientSceneLogic_Direct, unskippableEmptyFlushesGeneratesSceneActionSentToSubscriber) { + // has and checks first flush this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); + // flush with change this->m_sceneLogic.flushSceneActions({}, {}); Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); - this->expectFlushSceneActionList(false); + // empty -> nothing sent this->m_sceneLogic.flushSceneActions({}, {}); - this->expectFlushSceneActionList(false); + + // has expiration + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)); + this->m_sceneLogic.flushSceneActions({ FlushTime::Clock::time_point{std::chrono::milliseconds{1}}, FlushTime::Clock::time_point{}, {}, false }, {}); + + this->expectSceneUnpublish(); +} + +TEST_F(AClientSceneLogic_Direct, fluhsNotSkippedIfEmptyWithExpirationEnabledOrDisabled) +{ + // has and checks first flush + this->publishAndAddSubscriberWithoutPendingActions(); + + this->m_scene.allocateNode(0, {}); + + FlushTimeInformation flushTimeInfo = { FlushTime::InvalidTimestamp, {}, {} , false }; + + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); + // flush with change + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); + + // empty -> nothing sent + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + + // enable expiration + flushTimeInfo.expirationTimestamp = FlushTime::Clock::time_point{ std::chrono::milliseconds{1} }; + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)); + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + + // empty -> nothing sent + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + + // disable expiration + flushTimeInfo.expirationTimestamp = FlushTime::Clock::time_point{ std::chrono::milliseconds{0} }; + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)); + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + + this->expectSceneUnpublish(); +} + +TEST_F(AClientSceneLogic_Direct, flushNotSkippedIfEmptyWithEffectTimeSyncEnabled) +{ + this->publishAndAddSubscriberWithoutPendingActions(); + + FlushTimeInformation flushTimeInfo; + flushTimeInfo.internalTimestamp = FlushTime::Clock::time_point{ std::chrono::milliseconds(1000) }; + + // empty -> nothing sent + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); + + // send effect time sync + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)); + flushTimeInfo.isEffectTimeSync = true; + this->m_sceneLogic.flushSceneActions(flushTimeInfo, {}); + Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); + + this->expectSceneUnpublish(); +} + +TEST_F(AClientSceneLogic_Direct, skippedFlushesAreCounted) +{ + this->publish(); + this->expectSceneSend(); + this->expectFlushSceneActionList(); + this->addSubscriber(); + + uint32_t initialValue = this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue(); + //first flush + this->flush(); + Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); + EXPECT_EQ(initialValue, this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue()); + + // empty -> nothing sent + this->m_sceneLogic.flushSceneActions({}, {}); + EXPECT_EQ(initialValue + 1, this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue()); + + // non empty + this->m_scene.allocateNode(0, {}); + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)); this->m_sceneLogic.flushSceneActions({}, {}); + EXPECT_EQ(initialValue + 1, this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue()); + + // empty -> nothing sent + this->m_sceneLogic.flushSceneActions({}, {}); + EXPECT_EQ(initialValue + 2, this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue()); this->expectSceneUnpublish(); } @@ -399,7 +482,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, unskippableEmptyFlushesGeneratesSceneAction // has and checks first flush this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); // flush with change @@ -421,7 +504,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, fluhsNotSkippedIfEmptyWithExpirationEnabled // has and checks first flush this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); FlushTimeInformation flushTimeInfo = { FlushTime::InvalidTimestamp, {}, {} , false}; @@ -487,7 +570,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, skippedFlushesAreCounted) EXPECT_EQ(initialValue+1, this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue()); // non empty - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)); this->m_sceneLogic.flushSceneActions({}, {}); EXPECT_EQ(initialValue+1, this->m_scene.getStatisticCollection().statSceneActionsSentSkipped.getCounterValue()); @@ -504,9 +587,9 @@ TEST_F(AClientSceneLogic_ShadowCopy, doesNotClearPendingActionsIfWaitingSubscrib { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, m_sceneId, _)); this->expectFlushSceneActionList(); @@ -521,9 +604,9 @@ TEST_F(AClientSceneLogic_Direct, doesNotClearPendingActionsIfWaitingSubscriberRe { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); this->m_sceneLogic.addSubscriber(newRendererID); this->m_sceneLogic.removeSubscriber(newRendererID); @@ -536,12 +619,12 @@ TEST_F(AClientSceneLogic_ShadowCopy, doesNotClearPendingActionsIfSubscriberRemov { this->publishAndAddSubscriberWithoutPendingActions(); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, m_sceneId, _)); this->expectFlushSceneActionList(); this->m_sceneLogic.addSubscriber(newRendererID); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->m_sceneLogic.removeSubscriber(newRendererID); @@ -554,10 +637,10 @@ TEST_F(AClientSceneLogic_Direct, doesNotClearPendingActionsIfSubscriberRemovedIs { this->publishAndAddSubscriberWithoutPendingActions(); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); this->m_sceneLogic.addSubscriber(newRendererID); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->m_sceneLogic.removeSubscriber(newRendererID); EXPECT_FALSE(this->m_scene.getSceneActionCollection().empty()); @@ -613,7 +696,7 @@ TYPED_TEST(AClientSceneLogic_All, canDisableDistribution) TYPED_TEST(AClientSceneLogic_All, doesNotSendSceneAgainIfEnablingDistributionSecondTime) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_sceneLogic.publish(ramses_internal::EScenePublicationMode_LocalAndRemote); + this->m_sceneLogic.publish(EScenePublicationMode::LocalAndRemote); // No call expectation this->expectSceneUnpublish(); } @@ -632,7 +715,7 @@ TYPED_TEST(AClientSceneLogic_All, doesNotFlushPendingActionsIfSceneStoppedBeingD { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->unpublish(); this->m_sceneLogic.flushSceneActions({}, {}); @@ -642,13 +725,13 @@ TYPED_TEST(AClientSceneLogic_All, disablesDistributionTwiceAndDoesNotSendActions { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendUnpublishScene(this->m_sceneId, _)); this->m_sceneLogic.unpublish(); Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->m_sceneLogic.unpublish(); } @@ -656,7 +739,7 @@ TYPED_TEST(AClientSceneLogic_All, keepsCollectingPendingActionsIfSceneStoppedBei { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->unpublish(); @@ -679,7 +762,7 @@ TYPED_TEST(AClientSceneLogic_All, doesNotKeepPendingChangesAfterRepublish) this->unpublish(); this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->m_sceneLogic.flushSceneActions({}, {}); // no call expected, subscribers were removed with unpublish @@ -718,7 +801,7 @@ TYPED_TEST(AClientSceneLogic_All, continuesCollectingActionsAfterRepublish) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_EQ(1u, this->m_scene.getSceneActionCollection().numberOfActions()); this->unpublish(); @@ -778,7 +861,6 @@ TEST_F(AClientSceneLogic_Direct, doesNotAddAgainIfExistingSubscriberAddedAgain) this->flush(); Mock::VerifyAndClearExpectations(&m_sceneGraphProviderComponent); - this->expectFlushSceneActionList(false); this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); @@ -791,9 +873,9 @@ TEST_F(AClientSceneLogic_ShadowCopy, canSubscribeToSceneEvenWithPendingActions) // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, this->m_sceneId, _)); this->expectFlushSceneActionList(); this->m_sceneLogic.addSubscriber(newRendererID); @@ -806,9 +888,9 @@ TEST_F(AClientSceneLogic_Direct, canSubscribeToSceneEvenWithPendingActions) // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - const NodeHandle handle = this->m_scene.allocateNode(); + const NodeHandle handle = this->m_scene.allocateNode(0, {}); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); SceneUpdate update; SceneActionCollectionCreator creator(update.actions); @@ -833,7 +915,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, SendCleanSceneToNewSubscriber) // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); // expect direct scene send to new renderer EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, this->m_sceneId, _)); @@ -849,13 +931,12 @@ TEST_F(AClientSceneLogic_Direct, SendCleanSceneToNewSubscriber) // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); // expect direct scene send to new renderer EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, this->m_sceneId, _)); this->m_sceneLogic.addSubscriber(newRendererID); - EXPECT_CALL(m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{this->m_rendererID}, ContainsSceneUpdate(createFlushSceneActionList(false, 2)), _, _, _)); EXPECT_CALL(m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{newRendererID}, ContainsSceneUpdate(createFlushSceneActionList(true, 2)), _, _, _)); this->flush(); @@ -869,7 +950,7 @@ TYPED_TEST(AClientSceneLogic_All, doesAppendFlushActionIfOtherSceneActionsAreFlu this->m_scene.allocateNode(0u, NodeHandle(1)); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, update.actions.numberOfActions()); EXPECT_EQ(ESceneActionId::AllocateNode, update.actions[0].type()); @@ -890,7 +971,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, appendsDefaultFlushInfoWhenSendingSceneToNe this->m_sceneLogic.flushSceneActions(ftiIn, versionTagIn); this->expectSceneSend(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& updateFromSendScene, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& updateFromSendScene, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, updateFromSendScene.actions.numberOfActions()); EXPECT_EQ(ESceneActionId::AllocateNode, updateFromSendScene.actions[0].type()); @@ -918,7 +999,7 @@ TYPED_TEST(AClientSceneLogic_All, appendsTimeSyncInfoWhenSendingSceneToNewSubscr this->flush({expirationTime, flushTime, clockType, false}, {}); this->expectSceneSend(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& updateFromSendScene, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& updateFromSendScene, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, updateFromSendScene.actions.numberOfActions()); EXPECT_EQ(ESceneActionId::AllocateNode, updateFromSendScene.actions[0].type()); @@ -955,7 +1036,7 @@ TEST_F(AClientSceneLogic_Direct, appendsDefaultFlushInfoWhenSendingSceneToNewSub const FlushTimeInformation ftiInUsed{ FlushTime::Clock::time_point(std::chrono::milliseconds(5)), FlushTime::Clock::time_point(std::chrono::milliseconds(6)), FlushTime::Clock::getClockType(), false }; const SceneVersionTag versionTagUsed{ 666 }; - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& updateFromSendScene, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& updateFromSendScene, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, updateFromSendScene.actions.numberOfActions()); EXPECT_EQ(ESceneActionId::AllocateNode, updateFromSendScene.actions[0].type()); @@ -973,8 +1054,8 @@ TYPED_TEST(AClientSceneLogic_All, sendSceneSizesTogetherWithFlushIfSceneSizeIncr { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + this->m_scene.allocateNode(0, {}); + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_GE(update.actions.numberOfActions(), 0u); @@ -989,8 +1070,8 @@ TYPED_TEST(AClientSceneLogic_All, canUseNullptrWhenReadingFlush) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + this->m_scene.allocateNode(0, {}); + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_GE(update.actions.numberOfActions(), 0u); }); @@ -1003,14 +1084,14 @@ TYPED_TEST(AClientSceneLogic_All, doesNotSendSceneSizesTogetherWithFlushIfSceneS this->publishAndAddSubscriberWithoutPendingActions(); // create 2 nodes - const NodeHandle node1 = this->m_scene.allocateNode(); - const NodeHandle node2 = this->m_scene.allocateNode(); + const NodeHandle node1 = this->m_scene.allocateNode(0, {}); + const NodeHandle node2 = this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); this->m_sceneLogic.flushSceneActions({}, {}); // set child/parent relation - no size change this->m_scene.addChildToNode(node1, node2); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { EXPECT_FALSE(update.flushInfos.hasSizeInfo); }); @@ -1022,19 +1103,19 @@ TYPED_TEST(AClientSceneLogic_All, sendListOfNewResourcesTogetherWithFlush) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); this->m_sceneLogic.flushSceneActions({}, {}); const ResourceContentHash hash = this->m_arrayResource->getHash(); - const RenderTargetHandle renderTarget = this->m_scene.allocateRenderTarget(); - const RenderBufferHandle renderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::R16, ERenderBufferAccessMode_ReadWrite, 0u }); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, hash, {} }); - const BlitPassHandle blitpassHandle = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + const RenderTargetHandle renderTarget = this->m_scene.allocateRenderTarget({}); + const RenderBufferHandle renderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 1u, EPixelStorageFormat::R16F, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, hash, {} }, {}); + const BlitPassHandle blitpassHandle = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(4u, update.actions.numberOfActions()); auto& resourceChanges = update.flushInfos.resourceChanges; @@ -1059,18 +1140,18 @@ TYPED_TEST(AClientSceneLogic_All, sendsResourcesTogetherWithFlush) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); this->m_sceneLogic.flushSceneActions({}, {}); ResourceBlob blob(1024 * EnumToSize(EDataType::Float)); - std::iota(blob.data(), blob.data() + blob.size(), static_cast(10)); + std::generate(blob.data(), blob.data() + blob.size(), [](){ static uint8_t i{9}; return std::byte(++i); }); const ResourceContentHash hash = this->m_arrayResource->getHash(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, hash, {} }); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, hash, {} }, {}); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, update.resources.size()); EXPECT_EQ(hash, update.resources[0]->getHash()); @@ -1085,13 +1166,13 @@ TYPED_TEST(AClientSceneLogic_All, flushClearsListsOfChangedResources) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateRenderable(this->m_scene.allocateNode()); - this->m_scene.allocateRenderTarget(); - this->m_scene.allocateRenderBuffer({ 1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::R16, ERenderBufferAccessMode_ReadWrite, 0u }); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }); - this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); + this->m_scene.allocateRenderTarget({}); + this->m_scene.allocateRenderBuffer({ 1u, 1u, EPixelStorageFormat::R16F, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }, {}); + this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& resourceChanges = update.flushInfos.resourceChanges; EXPECT_EQ(1u, resourceChanges.m_resourcesAdded.size()); @@ -1101,8 +1182,8 @@ TYPED_TEST(AClientSceneLogic_All, flushClearsListsOfChangedResources) this->expectResourceQueries({ this->m_arrayResource }); this->m_sceneLogic.flushSceneActions({}, {}); - this->m_scene.allocateNode(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + this->m_scene.allocateNode(0, {}); + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& resourceChanges = update.flushInfos.resourceChanges; EXPECT_EQ(0u, resourceChanges.m_resourcesAdded.size()); @@ -1119,13 +1200,13 @@ TYPED_TEST(AClientSceneLogic_All, sendListOfObsoleteResourcesTogetherWithFlush) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); const ResourceContentHash hash = this->m_arrayResource->getHash(); - this->m_scene.allocateDataLayout({}, ResourceContentHash(45u, 0)); - const RenderTargetHandle renderTarget = this->m_scene.allocateRenderTarget(); - const RenderBufferHandle renderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::R16, ERenderBufferAccessMode_ReadWrite, 0u }); - const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, hash, {} }); - const BlitPassHandle blitpassHandle = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + this->m_scene.allocateDataLayout({}, ResourceContentHash(45u, 0), {}); + const RenderTargetHandle renderTarget = this->m_scene.allocateRenderTarget({}); + const RenderBufferHandle renderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 1u, EPixelStorageFormat::R16F, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, hash, {} }, {}); + const BlitPassHandle blitpassHandle = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)); this->expectResourceQueries({ this->m_arrayResource }); @@ -1136,7 +1217,7 @@ TYPED_TEST(AClientSceneLogic_All, sendListOfObsoleteResourcesTogetherWithFlush) this->m_scene.releaseDataSlot(dataSlot); this->m_scene.releaseBlitPass(blitpassHandle); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(4u, update.actions.numberOfActions()); @@ -1174,10 +1255,10 @@ TEST_F(AClientSceneLogic_ShadowCopy, sendsSceneToNewlySubscribedRendererAfterFlu this->m_scene.allocateRenderable(node, RenderableHandle(2)); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, this->m_sceneId, _)); // expect newly flushed actions to new renderer - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, update.actions.numberOfActions()); EXPECT_EQ(ESceneActionId::AllocateNode, update.actions[0].type()); @@ -1187,7 +1268,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, sendsSceneToNewlySubscribedRendererAfterFlu Mock::VerifyAndClearExpectations(&this->m_sceneGraphProviderComponent); // expect newly flushed actions to original and new renderer - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(UnorderedElementsAre(this->m_rendererID, newRendererID), _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(UnorderedElementsAre(this->m_rendererID, newRendererID), _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(1u, update.actions.numberOfActions()); EXPECT_EQ(ESceneActionId::AllocateRenderable, update.actions[0].type()); @@ -1202,19 +1283,19 @@ TEST_F(AClientSceneLogic_ShadowCopy, sendsSceneToNewlySubscribedRendererWithNewR { // add some active subscriber so actions are queued this->publish(); - auto rendHandle = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + auto rendHandle = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); const ResourceContentHash hash = this->m_textureResource->getHash(); - auto layoutHandle = this->m_scene.allocateDataLayout({}, hash); - auto instanceHandle = this->m_scene.allocateDataInstance(layoutHandle); + auto layoutHandle = this->m_scene.allocateDataLayout({}, hash, {}); + auto instanceHandle = this->m_scene.allocateDataInstance(layoutHandle, {}); this->m_scene.setRenderableDataInstance(rendHandle, ERenderableDataSlotType_Geometry, instanceHandle); this->expectResourceQueries({ this->m_textureResource }); this->m_sceneLogic.flushSceneActions({}, {}); - this->m_scene.allocateNode(); // action not flushed + this->m_scene.allocateNode(0, {}); // action not flushed - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, _, _)); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(4u, update.actions.numberOfActions()); @@ -1238,9 +1319,9 @@ TEST_F(AClientSceneLogic_ShadowCopy, sendsSceneToNewlySubscribedRendererWithVali const SceneVersionTag versionTagIn{ 333 }; this->m_sceneLogic.flushSceneActions(ftiIn, versionTagIn); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, _, _)); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(0u, update.actions.numberOfActions()); @@ -1264,9 +1345,9 @@ TEST_F(AClientSceneLogic_ShadowCopy, sendsSceneToNewlySubscribedRendererWithLast const SceneVersionTag versionTagIn2{ 123 }; this->m_sceneLogic.flushSceneActions(ftiIn2, versionTagIn2); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, _, _)); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(0u, update.actions.numberOfActions()); @@ -1281,21 +1362,21 @@ TEST_F(AClientSceneLogic_Direct, sendsSceneToNewlySubscribedRendererWithNewResou { // add some active subscriber so actions are queued this->publish(); - auto rendHandle = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + auto rendHandle = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); const ResourceContentHash hash = this->m_textureResource->getHash(); - auto layoutHandle = this->m_scene.allocateDataLayout({}, hash); - auto instanceHandle = this->m_scene.allocateDataInstance(layoutHandle); + auto layoutHandle = this->m_scene.allocateDataLayout({}, hash, {}); + auto instanceHandle = this->m_scene.allocateDataInstance(layoutHandle, {}); expectResourceQueries({ this->m_textureResource }); this->m_scene.setRenderableDataInstance(rendHandle, ERenderableDataSlotType_Geometry, instanceHandle); this->m_sceneLogic.flushSceneActions({}, {}); - this->m_scene.allocateNode(); // action not flushed + this->m_scene.allocateNode(0, {}); // action not flushed - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); this->m_sceneLogic.addSubscriber(newRendererID); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, _, _)); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { ASSERT_EQ(5u, update.actions.numberOfActions()); @@ -1315,14 +1396,14 @@ TEST_F(AClientSceneLogic_ShadowCopy, doesNotSendSceneUpdatesToNewSubscriberThatU // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, this->m_sceneId, _)); this->expectFlushSceneActionList(); this->m_sceneLogic.addSubscriber(newRendererID); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->m_sceneLogic.removeSubscriber(newRendererID); @@ -1338,13 +1419,13 @@ TEST_F(AClientSceneLogic_Direct, doesNotSendAnythingToNewSubscriberThatUnsubscri // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); const SceneInfo sceneInfo(this->m_sceneId, this->m_scene.getName()); this->m_sceneLogic.addSubscriber(newRendererID); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->m_sceneLogic.removeSubscriber(newRendererID); // expect flush to original renderer first @@ -1359,11 +1440,11 @@ TYPED_TEST(AClientSceneLogic_All, sceneReferenceActionsAreNotSentToNewlySubscrib this->m_scene.linkData(SceneReferenceHandle{ 1 }, DataSlotId{ 2 }, SceneReferenceHandle{ 3 }, DataSlotId{ 4 }); this->publish(); - const ramses_internal::Guid newRendererID("12345678-1234-5678-0000-123456789012"); + const Guid newRendererID("12345678-1234-5678-0000-123456789012"); this->m_sceneLogic.addSubscriber(newRendererID); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendCreateScene(newRendererID, _, _)); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ newRendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& sceneReferenceActions = update.flushInfos.sceneReferences; EXPECT_TRUE(sceneReferenceActions.empty()); @@ -1377,7 +1458,7 @@ TYPED_TEST(AClientSceneLogic_All, flushSendsOnlySceneReferenceActionsSinceLastFl this->publishAndAddSubscriberWithoutPendingActions(); this->m_scene.linkData(SceneReferenceHandle{ 1 }, DataSlotId{ 2 }, SceneReferenceHandle{ 3 }, DataSlotId{ 4 }); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& sceneReferenceActions = update.flushInfos.sceneReferences; ASSERT_EQ(1u, sceneReferenceActions.size()); @@ -1390,7 +1471,7 @@ TYPED_TEST(AClientSceneLogic_All, flushSendsOnlySceneReferenceActionsSinceLastFl this->flush(); this->m_scene.unlinkData(SceneReferenceHandle{ 1 }, DataSlotId{ 2 }); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& sceneReferenceActions = update.flushInfos.sceneReferences; ASSERT_EQ(1u, sceneReferenceActions.size()); @@ -1400,7 +1481,7 @@ TYPED_TEST(AClientSceneLogic_All, flushSendsOnlySceneReferenceActionsSinceLastFl }); this->flush(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { EXPECT_TRUE(update.flushInfos.resourceChanges.empty()); }); @@ -1413,7 +1494,7 @@ TYPED_TEST(AClientSceneLogic_All, unpublishesSceneAndDoesNotFlushBeforeRemovingS // add some active subscriber so actions are queued this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectSceneUnpublish(); this->m_sceneLogic.unpublish(); @@ -1524,7 +1605,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, sceneActionsAreNotModifiedWhenLastRendererU { this->publishAndAddSubscriberWithoutPendingActions(); - const NodeHandle node = this->m_scene.allocateNode(); + const NodeHandle node = this->m_scene.allocateNode(0, {}); EXPECT_EQ(1u, this->m_scene.getSceneActionCollection().numberOfActions()); this->m_sceneLogic.removeSubscriber(this->m_rendererID); @@ -1551,7 +1632,7 @@ TEST_F(AClientSceneLogic_Direct, sceneActionsAreNotModifiedWhenLastRendererUnsub { this->publishAndAddSubscriberWithoutPendingActions(); - const NodeHandle node = this->m_scene.allocateNode(); + const NodeHandle node = this->m_scene.allocateNode(0, {}); EXPECT_EQ(1u, this->m_scene.getSceneActionCollection().numberOfActions()); this->m_sceneLogic.removeSubscriber(this->m_rendererID); @@ -1576,11 +1657,11 @@ TYPED_TEST(AClientSceneLogic_All, increasesStatisticCounterForSceneActionsSentWi { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); uint32_t initialValue = this->m_scene.getStatisticCollection().statSceneActionsSent.getCounterValue(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { EXPECT_EQ(initialValue + update.actions.numberOfActions(), this->m_scene.getStatisticCollection().statSceneActionsSent.getCounterValue()); }); @@ -1597,7 +1678,7 @@ TEST_F(AClientSceneLogic_ShadowCopy, increasesStatisticCounterForSceneActionsSen this->m_sceneLogic.flushSceneActions({}, {}); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { EXPECT_EQ(update.actions.numberOfActions(), this->m_scene.getStatisticCollection().statSceneActionsSent.getCounterValue()); }); @@ -1645,20 +1726,20 @@ TYPED_TEST(AClientSceneLogic_All, resolvesClientResourcesWhenFlushing) Mock::VerifyAndClearExpectations(&this->m_resourceComponent); // strict behavior for this test - this->m_scene.allocateNode(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }); + this->m_scene.allocateNode(0, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_arrayResource }); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& resourceChanges = update.flushInfos.resourceChanges; EXPECT_EQ(1u, resourceChanges.m_resourcesAdded.size()); }); this->flush(); - this->m_scene.allocateNode(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(1u), {}, {}, this->m_textureResource->getHash(), {} }); + this->m_scene.allocateNode(0, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(1u), {}, {}, this->m_textureResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_textureResource }, { this->m_arrayResource, this->m_textureResource }); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& resourceChanges = update.flushInfos.resourceChanges; EXPECT_EQ(1u, resourceChanges.m_resourcesAdded.size()); @@ -1672,17 +1753,17 @@ TYPED_TEST(AClientSceneLogic_All, resolvesClientResourcesWhenFlushingUnpublished { Mock::VerifyAndClearExpectations(&this->m_resourceComponent); // strict behavior for this test - this->m_scene.allocateNode(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }); + this->m_scene.allocateNode(0, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_textureResource }); this->flush(); - this->m_scene.allocateNode(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(1u), {}, {}, this->m_arrayResource->getHash(), {} }); + this->m_scene.allocateNode(0, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(1u), {}, {}, this->m_arrayResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_arrayResource }, { this->m_arrayResource, this->m_textureResource }); this->flush(); this->publish(); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& resourceChanges = update.flushInfos.resourceChanges; EXPECT_EQ(2u, resourceChanges.m_resourcesAdded.size()); @@ -1716,7 +1797,7 @@ TYPED_TEST(AClientSceneLogic_All, doesNotTryToResolveResourcesWhenNoResourceChan { Mock::VerifyAndClearExpectations(&this->m_resourceComponent); // strict behavior for this test this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectResourceQueries({},{}, false, false, false); this->flush({}, SceneVersionTag{ 823746 }); @@ -1732,11 +1813,11 @@ TYPED_TEST(AClientSceneLogic_All, doesNotTryToResolveResourcesWhenNoResourceChan TYPED_TEST(AClientSceneLogic_All, resolvesOldResourcesForNewSubscriber) { this->publish(); - this->m_scene.allocateNode(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }); + this->m_scene.allocateNode(0, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_textureResource }, {}, false, false, true); this->flush(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectResourceQueries({}, { this->m_textureResource }, false, false, false); this->flush(); @@ -1760,18 +1841,18 @@ TYPED_TEST(AClientSceneLogic_All, succeedsASecondFlushContainingAllSceneActionsA this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }); - auto rendHandle = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); - auto layoutHandle = this->m_scene.allocateDataLayout({}, this->m_textureResource->getHash()); - auto instanceHandle = this->m_scene.allocateDataInstance(layoutHandle); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }, {}); + auto rendHandle = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); + auto layoutHandle = this->m_scene.allocateDataLayout({}, this->m_textureResource->getHash(), {}); + auto instanceHandle = this->m_scene.allocateDataInstance(layoutHandle, {}); this->m_scene.setRenderableDataInstance(rendHandle, ERenderableDataSlotType_Geometry, instanceHandle); - const RenderTargetHandle renderTarget = this->m_scene.allocateRenderTarget(); + const RenderTargetHandle renderTarget = this->m_scene.allocateRenderTarget({}); EXPECT_FALSE(this->m_sceneLogic.flushSceneActions({}, {})); Mock::VerifyAndClearExpectations(&this->m_resourceComponent); // strict behavior for this test - this->m_scene.allocateNode(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(1u), {}, {}, this->m_effectResource->getHash(), {} }); - EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto&, const auto& update, auto, auto, auto&) + this->m_scene.allocateNode(0, {}); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(1u), {}, {}, this->m_effectResource->getHash(), {} }, {}); + EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, _)).WillOnce([&](const auto& /*unused*/, const auto& update, auto /*unused*/, auto /*unused*/, auto& /*unused*/) { auto& resourceChanges = update.flushInfos.resourceChanges; EXPECT_EQ(3u, resourceChanges.m_resourcesAdded.size()); @@ -1799,7 +1880,7 @@ TYPED_TEST(AClientSceneLogic_All, fillsResourceStatisticsWithZerosIfNoResources) { EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, _, _, _)).Times(AnyNumber()); this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectSceneSend(); this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); @@ -1816,8 +1897,8 @@ TYPED_TEST(AClientSceneLogic_All, fillsResourceStatisticsWithZerosIfNoResourcesD { EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, _, _, _)).Times(AnyNumber()); this->publish(); - this->m_scene.allocateNode(); - auto dataSlotHandle = this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, { 0, 1 }, {} }); + this->m_scene.allocateNode(0, {}); + auto dataSlotHandle = this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, { 0, 1 }, {} }, {}); this->m_scene.releaseDataSlot(dataSlotHandle); this->expectSceneSend(); this->m_sceneLogic.addSubscriber(this->m_rendererID); @@ -1834,12 +1915,12 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfArrayResourceAddedA { EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, _, _, _)).Times(AnyNumber()); this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectSceneSend(); this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); - const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }); + const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_arrayResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_arrayResource }); this->flush(); @@ -1847,11 +1928,11 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfArrayResourceAddedA this->expectStatistics(EResourceStatisticIndex_Effect, { 0, 0, 0 }); this->expectStatistics(EResourceStatisticIndex_Texture, { 0, 0, 0 }); - auto newArray = new ArrayResource(EResourceType_IndexArray, 1, EDataType::UInt16, nullptr, ResourceCacheFlag_DoNotCache, {}); + auto newArray = new ArrayResource(EResourceType::IndexArray, 1, EDataType::UInt16, nullptr, {}); newArray->setResourceData(ResourceBlob{ 2 }, { 4u, 4u }); ManagedResource newManRes(newArray); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(1u), {}, {}, newArray->getHash(), {} }); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(1u), {}, {}, newArray->getHash(), {} }, {}); this->expectResourceQueries({ newManRes }, { this->m_arrayResource, newManRes }); this->flush(); @@ -1874,12 +1955,12 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfEffectAddedAndRemov { EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, _, _, _)).Times(AnyNumber()); this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectSceneSend(); this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); - const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_effectResource->getHash(), {} }); + const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_effectResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_effectResource }); this->flush(); @@ -1887,10 +1968,10 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfEffectAddedAndRemov this->expectStatistics(EResourceStatisticIndex_Effect, { 1, 6, 6 }); this->expectStatistics(EResourceStatisticIndex_Texture, { 0, 0, 0 }); - ManagedResource newManRes(new EffectResource("f00", "bar", {}, {}, {}, {}, {}, ResourceCacheFlag_DoNotCache)); + ManagedResource newManRes(new EffectResource("f00", "bar", {}, {}, {}, {}, {})); this->expectResourceQueries({ newManRes }, { this->m_effectResource, newManRes }); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(1u), {}, {}, newManRes->getHash(), {} }); + this->m_scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(1u), {}, {}, newManRes->getHash(), {}}, {}); this->flush(); this->expectStatistics(EResourceStatisticIndex_ArrayResource, { 0, 0, 0 }); @@ -1912,12 +1993,12 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfTextureAddedAndRemo { EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, _, _, _)).Times(AnyNumber()); this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectSceneSend(); this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); - const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }); + const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }, {}); this->expectResourceQueries({ this->m_textureResource }); this->flush(); @@ -1925,11 +2006,11 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfTextureAddedAndRemo this->expectStatistics(EResourceStatisticIndex_Effect, { 0, 0, 0 }); this->expectStatistics(EResourceStatisticIndex_Texture, { 1, 1, 1 }); - auto newTex = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {}); + auto newTex = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {}); newTex->setResourceData(ResourceBlob{ 2 }, { 4u, 4u }); ManagedResource newManRes(newTex); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, newManRes->getHash(), {} }); + this->m_scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, newManRes->getHash(), {} }, {}); this->expectResourceQueries({ newManRes }, { this->m_textureResource, newManRes }); this->flush(); @@ -1951,7 +2032,7 @@ TYPED_TEST(AClientSceneLogic_All, updatesResourceStatisticsIfTextureAddedAndRemo TYPED_TEST(AClientSceneLogic_All, clientSceneStatisticsIsPassedToSceneGraphComponentOnFlush) { this->publishAndAddSubscriberWithoutPendingActions(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(std::vector{ this->m_rendererID }, _, this->m_sceneId, _, Ref(this->m_scene.getStatisticCollection()))); @@ -1964,13 +2045,13 @@ TYPED_TEST(AClientSceneLogic_All, doesNotCallGetContentInfoWhenComponentDoesNotK { EXPECT_CALL(this->m_sceneGraphProviderComponent, sendSceneUpdate_rvr(_, _, _, _, _)).Times(AnyNumber()); this->publish(); - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); this->expectSceneSend(); this->m_sceneLogic.addSubscriber(this->m_rendererID); this->flush(); - this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {} }); - const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, this->m_effectResource->getHash(), {} }); + this->m_scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_textureResource->getHash(), {}}, {}); + const DataSlotHandle dataSlot = this->m_scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, this->m_effectResource->getHash(), {}}, {}); this->expectResourceQueries({ this->m_textureResource, this->m_effectResource }); this->flush(); @@ -1982,4 +2063,4 @@ TYPED_TEST(AClientSceneLogic_All, doesNotCallGetContentInfoWhenComponentDoesNotK this->flush(); this->expectSceneUnpublish(); } - +} diff --git a/framework/Components/test/InputStreamContainerTest.cpp b/tests/unittests/framework/Components/InputStreamContainerTest.cpp similarity index 70% rename from framework/Components/test/InputStreamContainerTest.cpp rename to tests/unittests/framework/Components/InputStreamContainerTest.cpp index 1045d4925..f9e232f2d 100644 --- a/framework/Components/test/InputStreamContainerTest.cpp +++ b/tests/unittests/framework/Components/InputStreamContainerTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/InputStreamContainer.h" -#include "Components/FileInputStreamContainer.h" -#include "Components/MemoryInputStreamContainer.h" -#include "Components/OffsetFileInputStreamContainer.h" +#include "internal/Components/InputStreamContainer.h" +#include "internal/Components/FileInputStreamContainer.h" +#include "internal/Components/MemoryInputStreamContainer.h" +#include "internal/Components/OffsetFileInputStreamContainer.h" #include "FileDescriptorHelper.h" #include "gtest/gtest.h" #include @@ -17,11 +17,17 @@ #include #include -namespace ramses_internal +namespace ramses::internal { + template + std::array make_byte_array(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + TEST(AInputStreamContainer, canCreateAndUseWithFileInputStream) { - const std::array dataW = {5, 4, 3, 2, 10}; + const std::array dataW = make_byte_array(5, 4, 3, 2, 10); { File f("test.bin"); EXPECT_TRUE(f.open(File::Mode::WriteNewBinary)); @@ -29,7 +35,7 @@ namespace ramses_internal } FileInputStreamContainer is("test.bin"); - std::array dataR = {0}; + std::array dataR = {std::byte{0}}; is.getStream().read(dataR.data(), dataR.size()); EXPECT_EQ(dataW, dataR); } @@ -40,8 +46,8 @@ namespace ramses_internal static bool deleted = false; deleted = false; { - const std::array dataW = {5, 4, 3, 2, 10}; - std::unique_ptr up(new Byte[dataW.size()], + const std::array dataW = make_byte_array(5, 4, 3, 2, 10); + std::unique_ptr up(new std::byte[dataW.size()], [](auto* ptr){ deleted = true; delete[] ptr; @@ -49,7 +55,7 @@ namespace ramses_internal std::memcpy(up.get(), dataW.data(), dataW.size()); MemoryInputStreamContainer is(std::move(up)); - std::array dataR = {0}; + std::array dataR = {std::byte{0}}; is.getStream().read(dataR.data(), dataR.size()); EXPECT_EQ(dataW, dataR); @@ -61,7 +67,7 @@ namespace ramses_internal TEST(AInputStreamContainer, canCreateAndUseWithOffsetFileInputStream) { - const std::array dataW = {5, 4, 3, 2, 10}; + const std::array dataW = make_byte_array(5, 4, 3, 2, 10); { File f("test.bin"); EXPECT_TRUE(f.open(File::Mode::WriteNewBinary)); @@ -70,10 +76,10 @@ namespace ramses_internal const int fd = FileDescriptorHelper::OpenFileDescriptorBinary("test.bin"); OffsetFileInputStreamContainer is(fd, 1, 3); - std::array dataR = {0}; + std::array dataR = {std::byte{0}}; is.getStream().read(dataR.data(), dataR.size()); - std::array dataWsub{4, 3, 2}; + std::array dataWsub = make_byte_array(4, 3, 2); EXPECT_EQ(dataWsub, dataR); } } diff --git a/framework/Components/test/ResourceAvailabilityEventTest.cpp b/tests/unittests/framework/Components/ResourceAvailabilityEventTest.cpp similarity index 87% rename from framework/Components/test/ResourceAvailabilityEventTest.cpp rename to tests/unittests/framework/Components/ResourceAvailabilityEventTest.cpp index 05fff5cec..266b2e613 100644 --- a/framework/Components/test/ResourceAvailabilityEventTest.cpp +++ b/tests/unittests/framework/Components/ResourceAvailabilityEventTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourceAvailabilityEvent.h" +#include "internal/Components/ResourceAvailabilityEvent.h" #include -namespace ramses_internal +namespace ramses::internal { bool checkEqual(ResourceAvailabilityEvent const& a, ResourceAvailabilityEvent const& b) { @@ -25,14 +25,14 @@ namespace ramses_internal a.sceneid = SceneId(123u); ResourceAvailabilityEvent b; b.sceneid = SceneId(123u); - std::vector testVec; + std::vector testVec; a.writeToBlob(testVec); b.readFromBlob(testVec); EXPECT_TRUE(checkEqual(a, b)); a.sceneid = SceneId{ 1234 }; - a.availableResources.push_back(ResourceContentHash(13u,14u)); + a.availableResources.emplace_back(13u,14u); EXPECT_FALSE(checkEqual(a, b)); diff --git a/framework/Components/test/ResourceComponentTest.cpp b/tests/unittests/framework/Components/ResourceComponentTest.cpp similarity index 92% rename from framework/Components/test/ResourceComponentTest.cpp rename to tests/unittests/framework/Components/ResourceComponentTest.cpp index 0257f7917..251927ab7 100644 --- a/framework/Components/test/ResourceComponentTest.cpp +++ b/tests/unittests/framework/Components/ResourceComponentTest.cpp @@ -6,22 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/ResourceComponent.h" +#include "internal/Components/ResourceComponent.h" #include "ResourceMock.h" #include "gmock/gmock.h" -#include "Resource/ArrayResource.h" -#include "Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" #include "DummyResource.h" -#include "Components/ResourceDeleterCallingCallback.h" -#include "Components/SceneUpdate.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" +#include "internal/Components/SceneUpdate.h" #include "SceneUpdateSerializerTestHelper.h" -#include "TransportCommon/SceneUpdateSerializer.h" -#include "TransportCommon/SceneUpdateStreamDeserializer.h" -#include "Components/FileInputStreamContainer.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializer.h" +#include "internal/Communication/TransportCommon/SceneUpdateStreamDeserializer.h" +#include "internal/Components/FileInputStreamContainer.h" #include "InputStreamMock.h" #include "InputStreamContainerMock.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; class ResourceComponentTestBase : public testing::Test @@ -51,7 +51,7 @@ namespace ramses_internal }; vertexData.resize(vertexData.size() + extraSize*3); - ArrayResource* resource = new ArrayResource(EResourceType_VertexArray, 3 + static_cast(extraSize), EDataType::Vector3F, vertexData.data(), ResourceCacheFlag(0u), "resName"); + auto* resource = new ArrayResource(EResourceType::VertexArray, 3 + static_cast(extraSize), EDataType::Vector3F, vertexData.data(), "resName"); return resource; } @@ -72,8 +72,8 @@ namespace ramses_internal for (uint32_t i = 0; i < num; ++i) { - IResource* resource = CreateTestResource(i*1.0f, extraSize); - ManagedResource managedResource = localResourceComponent.manageResource(*resource, true); + IResource* resource = CreateTestResource(static_cast(i), extraSize); + ManagedResource managedResource = localResourceComponent.manageResourceDeletionAllowed(*resource); managedResourceVec.push_back(managedResource); hashes.push_back(managedResource->getHash()); } @@ -84,7 +84,7 @@ namespace ramses_internal ResourcePersistation::WriteNamedResourcesWithTOCToStream(resourceOutputStream, managedResourceVec, compress); } - ramses_internal::ResourceTableOfContents resourceFileToc; + ResourceTableOfContents resourceFileToc; InputStreamContainerSPtr inputStream(std::make_shared(resourceFileName)); resourceFileToc.readTOCPosAndTOCFromStream(inputStream->getStream()); localResourceComponent.addResourceFile(inputStream, resourceFileToc); @@ -116,13 +116,13 @@ namespace ramses_internal subDir.remove(); } - void expectResourceSizeCalls(const ResourceMock* resource) + static void ExpectResourceSizeCalls(const ResourceMock* resource) { EXPECT_CALL(*resource, getCompressedDataSize()).Times(AnyNumber()); EXPECT_CALL(*resource, getDecompressedDataSize()).Times(AnyNumber()); } - std::vector> serializeResources(const ManagedResourceVector& resVec, uint32_t chunkSize = 100000) + std::vector> serializeResources(const ManagedResourceVector& resVec, uint32_t chunkSize = 100000) { SceneUpdate update{SceneActionCollection(), resVec, {}}; return TestSerializeSceneUpdateToVectorChunked(SceneUpdateSerializer(update, sceneStatistics), chunkSize); @@ -151,7 +151,7 @@ namespace ramses_internal struct TestResourceData { - std::vector data; + std::vector data; ResourceContentHash hash; }; @@ -192,7 +192,7 @@ namespace ramses_internal TEST_F(AResourceComponentTest, CanGetAlreadyManagedResource) { ResourceContentHash dummyResourceHash(47, 0); - DummyResource* dummyResource = new DummyResource(dummyResourceHash, EResourceType_VertexArray); + auto* dummyResource = new DummyResource(dummyResourceHash, EResourceType::VertexArray); ManagedResource dummyManagedResource = localResourceComponent.manageResource(*dummyResource); ManagedResource fetchedResource = localResourceComponent.getResource(dummyResourceHash); @@ -257,9 +257,9 @@ namespace ramses_internal TEST_F(AResourceComponentTest, ManageResourcesTakesOwnership) { - ResourceWithDestructorMock* resource = new ResourceWithDestructorMock(ResourceContentHash(47, 0), EResourceType_VertexArray); + auto* resource = new ResourceWithDestructorMock(ResourceContentHash(47, 0), EResourceType::VertexArray); { - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); EXPECT_CALL(*resource, Die()); ManagedResource managedRes = localResourceComponent.manageResource(*resource); }// raw pointer was transferred into component, and is deleted when last managed pointer goes away @@ -269,10 +269,10 @@ namespace ramses_internal TEST_F(AResourceComponentTest, canReturnAllResourcesManaged) { - DummyResource* resource = new DummyResource(ResourceContentHash(47, 0), EResourceType_VertexArray); + auto* resource = new DummyResource(ResourceContentHash(47, 0), EResourceType::VertexArray); ManagedResource managedRes = localResourceComponent.manageResource(*resource); - DummyResource* resource2 = new DummyResource(ResourceContentHash(49, 0), EResourceType_VertexArray); + auto* resource2 = new DummyResource(ResourceContentHash(49, 0), EResourceType::VertexArray); ManagedResource managedRes2 = localResourceComponent.manageResource(*resource2); ManagedResourceVector allResources = localResourceComponent.getResources(); @@ -284,8 +284,8 @@ namespace ramses_internal TEST_F(AResourceComponentTest, canReturnHashUsageReferringToKnownResource) { const ResourceContentHash hash(47, 0); - ResourceMock* resource = new ResourceMock(hash, EResourceType_VertexArray); - expectResourceSizeCalls(resource); + auto* resource = new ResourceMock(hash, EResourceType::VertexArray); + ExpectResourceSizeCalls(resource); ManagedResource managedRes = localResourceComponent.manageResource(*resource); ResourceHashUsage hashUsage = localResourceComponent.getResourceHashUsage(hash); @@ -301,7 +301,7 @@ namespace ramses_internal 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f }; - ArrayResource* resource = new ArrayResource(EResourceType_VertexArray, 3, EDataType::Vector3F, vertexData, ResourceCacheFlag(0u), "resName"); + auto* resource = new ArrayResource(EResourceType::VertexArray, 3, EDataType::Vector3F, vertexData, "resName"); ResourceContentHash hash = resource->getHash(); { File resourceFile(resourceFileName); @@ -321,7 +321,7 @@ namespace ramses_internal SceneFileHandle handle; { - ramses_internal::ResourceTableOfContents resourceFileToc; + ResourceTableOfContents resourceFileToc; InputStreamContainerSPtr inputStream(std::make_shared(resourceFileName)); resourceFileToc.readTOCPosAndTOCFromStream(inputStream->getStream()); @@ -532,7 +532,7 @@ namespace ramses_internal TEST_F(AResourceComponentTest, getsResourceInfoForResourceHashVector) { - IResource* tex = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {}); + IResource* tex = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {}); tex->setResourceData(ResourceBlob{ 2 }, { 4u, 4u }); auto res1 = localResourceComponent.manageResource(*CreateTestResource()); auto res2 = localResourceComponent.manageResource(*CreateTestResource(1.0f, 2)); @@ -559,7 +559,7 @@ namespace ramses_internal EXPECT_EQ(infoBefore.hash, handleAndHash.second); EXPECT_EQ(infoBefore.compressedSize, 0u); EXPECT_EQ(infoBefore.decompressedSize, 36u); - EXPECT_EQ(infoBefore.type, EResourceType_VertexArray); + EXPECT_EQ(infoBefore.type, EResourceType::VertexArray); ManagedResource fromfile = localResourceComponent.loadResource(handleAndHash.second); @@ -611,7 +611,7 @@ namespace ramses_internal EXPECT_CALL(*streamContainer, getStream()).WillRepeatedly(ReturnRef(stream)); ResourceTableOfContents toc; - toc.registerContents(ResourceInfo(EResourceType_Effect, hash, 10, 0), 0, 5); + toc.registerContents(ResourceInfo(EResourceType::Effect, hash, 10, 0), 0, 5); localResourceComponent.addResourceFile(streamContainer, toc); @@ -630,7 +630,7 @@ namespace ramses_internal EXPECT_CALL(*streamContainer, getStream()).WillRepeatedly(ReturnRef(stream)); ResourceTableOfContents toc; - toc.registerContents(ResourceInfo(EResourceType_Effect, hash, 10, 0), 0, 5); + toc.registerContents(ResourceInfo(EResourceType::Effect, hash, 10, 0), 0, 5); localResourceComponent.addResourceFile(streamContainer, toc); diff --git a/framework/Components/test/ResourceFileRegistryTest.cpp b/tests/unittests/framework/Components/ResourceFileRegistryTest.cpp similarity index 81% rename from framework/Components/test/ResourceFileRegistryTest.cpp rename to tests/unittests/framework/Components/ResourceFileRegistryTest.cpp index c4fe0683d..e063093ef 100644 --- a/framework/Components/test/ResourceFileRegistryTest.cpp +++ b/tests/unittests/framework/Components/ResourceFileRegistryTest.cpp @@ -6,24 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "Components/ResourceStorage.h" -#include "Components/ResourceTableOfContents.h" -#include "Components/ResourceFilesRegistry.h" -#include "Components/FileInputStreamContainer.h" +#include "internal/Components/ResourceStorage.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/Components/ResourceFilesRegistry.h" +#include "internal/Components/FileInputStreamContainer.h" -namespace ramses_internal +namespace ramses::internal { class AResourceFileRegistry : public ::testing::Test { public: AResourceFileRegistry() - : lock() - , stats() - , storage(lock, stats) - , registry() - {} + : storage(lock, stats) + { + } PlatformLock lock; StatisticCollectionFramework stats; @@ -43,7 +40,7 @@ namespace ramses_internal ResourceTableOfContents toc; std::string resourceFileName("testfile"); - toc.registerContents(ResourceInfo(EResourceType_VertexArray, hash, 22, 11), 0, 10); + toc.registerContents(ResourceInfo(EResourceType::VertexArray, hash, 22, 11), 0, 10); InputStreamContainerSPtr resourceFileStream(std::make_shared(resourceFileName)); const auto handle = registry.registerResourceFile(resourceFileStream, toc, storage); @@ -56,7 +53,7 @@ namespace ramses_internal ResourceContentHash hash(123, 0); ResourceTableOfContents toc; - toc.registerContents(ResourceInfo(EResourceType_VertexArray, hash, 22, 11), 0, 10); + toc.registerContents(ResourceInfo(EResourceType::VertexArray, hash, 22, 11), 0, 10); InputStreamContainerSPtr resourceFileStream(std::make_shared("testfile")); const auto handle = registry.registerResourceFile(resourceFileStream, toc, storage); @@ -75,7 +72,7 @@ namespace ramses_internal TEST_F(AResourceFileRegistry, storesResourceFileEntryWithResourceInfo) { ResourceContentHash hash(123, 0); - ResourceInfo resInfo(EResourceType_VertexArray, hash, 22, 11); + ResourceInfo resInfo(EResourceType::VertexArray, hash, 22, 11); uint32_t offset = 11u; uint32_t size = 22u; InputStreamContainerSPtr resourceFileStream(std::make_shared("testfile")); @@ -103,7 +100,7 @@ namespace ramses_internal InputStreamContainerSPtr resourceFileStream(std::make_shared("testfile")); ResourceTableOfContents toc; - toc.registerContents({ EResourceType_VertexArray, { 2, 2 }, 22, 11 }, 0, 10); + toc.registerContents({ EResourceType::VertexArray, { 2, 2 }, 22, 11 }, 0, 10); const SceneFileHandle handle = registry.registerResourceFile(resourceFileStream, toc, storage); EXPECT_EQ(nullptr, registry.getContentsOfResourceFile(SceneFileHandle(handle.getValue() + 1))); @@ -113,13 +110,13 @@ namespace ramses_internal { InputStreamContainerSPtr resourceFileStream(std::make_shared("testfile")); ResourceTableOfContents toc; - ResourceInfo info{ EResourceType_VertexArray, { 2, 2 }, 22, 11 }; + ResourceInfo info{ EResourceType::VertexArray, { 2, 2 }, 22, 11 }; toc.registerContents(info, 0, 10); const auto handle_1 = registry.registerResourceFile(resourceFileStream, toc, storage); InputStreamContainerSPtr resourceFileStream2(std::make_shared("testfile2")); ResourceTableOfContents toc2; - toc2.registerContents({ EResourceType_VertexArray, { 3, 3 }, 23, 13 }, 2, 12); + toc2.registerContents({ EResourceType::VertexArray, { 3, 3 }, 23, 13 }, 2, 12); const auto handle_2 = registry.registerResourceFile(resourceFileStream2, toc2, storage); (void)handle_2; @@ -135,15 +132,15 @@ namespace ramses_internal { InputStreamContainerSPtr resourceFileStream(std::make_shared("testfile")); ResourceTableOfContents toc; - toc.registerContents({ EResourceType_VertexArray, { 2, 2 }, 22, 11 }, 0, 10); - toc.registerContents({ EResourceType_VertexArray, { 5, 5 }, 22, 11 }, 10, 10); - toc.registerContents({ EResourceType_VertexArray, { 1, 1 }, 22, 11 }, 20, 10); + toc.registerContents({ EResourceType::VertexArray, { 2, 2 }, 22, 11 }, 0, 10); + toc.registerContents({ EResourceType::VertexArray, { 5, 5 }, 22, 11 }, 10, 10); + toc.registerContents({ EResourceType::VertexArray, { 1, 1 }, 22, 11 }, 20, 10); const auto handle_1 = registry.registerResourceFile(resourceFileStream, toc, storage); InputStreamContainerSPtr resourceFileStream2(std::make_shared("testfile2")); ResourceTableOfContents toc2; - toc2.registerContents({ EResourceType_VertexArray, { 3, 3 }, 23, 13 }, 2, 12); - toc2.registerContents({ EResourceType_VertexArray, { 5, 5 }, 23, 13 }, 14, 12); + toc2.registerContents({ EResourceType::VertexArray, { 3, 3 }, 23, 13 }, 2, 12); + toc2.registerContents({ EResourceType::VertexArray, { 5, 5 }, 23, 13 }, 14, 12); const auto handle_2 = registry.registerResourceFile(resourceFileStream2, toc2, storage); (void)handle_2; diff --git a/framework/Components/test/ResourceHashUsageTest.cpp b/tests/unittests/framework/Components/ResourceHashUsageTest.cpp similarity index 94% rename from framework/Components/test/ResourceHashUsageTest.cpp rename to tests/unittests/framework/Components/ResourceHashUsageTest.cpp index fdbf8e7dc..db7812dcb 100644 --- a/framework/Components/test/ResourceHashUsageTest.cpp +++ b/tests/unittests/framework/Components/ResourceHashUsageTest.cpp @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "Components/ResourceHashUsageCallback.h" -#include "Components/ResourceHashUsage.h" +#include "internal/Components/ResourceHashUsageCallback.h" +#include "internal/Components/ResourceHashUsage.h" -namespace ramses_internal +namespace ramses::internal { using testing::NiceMock; diff --git a/framework/Components/test/ResourcePersistationTest.cpp b/tests/unittests/framework/Components/ResourcePersistationTest.cpp similarity index 78% rename from framework/Components/test/ResourcePersistationTest.cpp rename to tests/unittests/framework/Components/ResourcePersistationTest.cpp index 669d8275f..7307c6c13 100644 --- a/framework/Components/test/ResourcePersistationTest.cpp +++ b/tests/unittests/framework/Components/ResourcePersistationTest.cpp @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/BinaryInputStream.h" -#include "framework_common_gmock_header.h" -#include "Components/ResourcePersistation.h" -#include "Resource/TextureResource.h" -#include "Resource/ArrayResource.h" -#include "Resource/EffectResource.h" -#include "Components/ManagedResource.h" -#include "Components/IManagedResourceDeleterCallback.h" -#include "Components/ResourceTableOfContents.h" -#include "Components/ResourceDeleterCallingCallback.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Components/ResourcePersistation.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/IManagedResourceDeleterCallback.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" #include "ResourceMock.h" #include "InputStreamMock.h" #include "UnsafeTestMemoryHelpers.h" @@ -26,7 +25,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { class AResourcePersistation : public ::testing::Test { @@ -36,7 +35,7 @@ namespace ramses_internal { } - std::unique_ptr readWriteResource(const ManagedResource& inResource) + static std::unique_ptr ReadWriteResource(const ManagedResource& inResource) { std::unique_ptr loaded; @@ -61,11 +60,10 @@ namespace ramses_internal std::unique_ptr createLoadedResource(const IResource& res, const EResourceType resourceType) { ManagedResource managedRes{ &res, m_deleterMock }; - std::unique_ptr loadedResource = readWriteResource(managedRes); + std::unique_ptr loadedResource = ReadWriteResource(managedRes); EXPECT_EQ(res.getResourceData().span(), loadedResource->getResourceData().span()); EXPECT_EQ(resourceType, loadedResource->getTypeID()); - EXPECT_EQ(res.getCacheFlag(), loadedResource->getCacheFlag()); return std::unique_ptr(static_cast(loadedResource.release())); } @@ -80,83 +78,76 @@ namespace ramses_internal TEST_F(AResourcePersistation, WriteRead_TextureResource) { const TextureSwizzleArray swizzle{ETextureChannelColor::Blue, ETextureChannelColor::Red, ETextureChannelColor::Alpha, ETextureChannelColor::Green}; - const TextureMetaInfo texDesc(2u, 3u, 1u, ETextureFormat::RGB8, false, swizzle, { 1u, 2u }); - const ResourceCacheFlag flag(15u); - TextureResource res(EResourceType_Texture3D, texDesc, flag, "resName"); + const TextureMetaInfo texDesc(2u, 3u, 1u, EPixelStorageFormat::RGB8, false, swizzle, { 1u, 2u }); + TextureResource res(EResourceType::Texture3D, texDesc, "resName"); ResourceBlob pixels(std::accumulate(texDesc.m_dataSizes.cbegin(), texDesc.m_dataSizes.cend(), 0u)); for (size_t i = 0; i < pixels.size(); ++i) { - pixels.data()[i] = static_cast(i); + pixels.data()[i] = std::byte{static_cast(i)}; } ASSERT_EQ(pixels.size(), res.getResourceData().size()); res.setResourceData(std::move(pixels)); - auto loadedTextureResource = createLoadedResource(res, EResourceType_Texture3D); + auto loadedTextureResource = createLoadedResource(res, EResourceType::Texture3D); ASSERT_EQ(res.getWidth(), loadedTextureResource->getWidth()); ASSERT_EQ(res.getHeight(), loadedTextureResource->getHeight()); ASSERT_EQ(res.getDepth(), loadedTextureResource->getDepth()); - ASSERT_EQ(ETextureFormat::RGB8, loadedTextureResource->getTextureFormat()); + ASSERT_EQ(EPixelStorageFormat::RGB8, loadedTextureResource->getTextureFormat()); ASSERT_EQ(ETextureChannelColor::Blue, loadedTextureResource->getTextureSwizzle()[0]); ASSERT_EQ(ETextureChannelColor::Red, loadedTextureResource->getTextureSwizzle()[1]); ASSERT_EQ(ETextureChannelColor::Alpha, loadedTextureResource->getTextureSwizzle()[2]); ASSERT_EQ(ETextureChannelColor::Green, loadedTextureResource->getTextureSwizzle()[3]); ASSERT_EQ(res.getGenerateMipChainFlag(), loadedTextureResource->getGenerateMipChainFlag()); ASSERT_EQ(texDesc.m_dataSizes, loadedTextureResource->getMipDataSizes()); - ASSERT_EQ(flag, loadedTextureResource->getCacheFlag()); EXPECT_EQ(std::string("resName"), loadedTextureResource->getName()); } TEST_F(AResourcePersistation, WriteRead_TextureResourceCube) { - const TextureMetaInfo texDesc(2u, 1u, 1u, ETextureFormat::RGB8, false, {}, { 1u, 2u }); - const ResourceCacheFlag flag(15u); - TextureResource res(EResourceType_TextureCube, texDesc, flag, "resName"); + const TextureMetaInfo texDesc(2u, 1u, 1u, EPixelStorageFormat::RGB8, false, {}, { 1u, 2u }); + TextureResource res(EResourceType::TextureCube, texDesc, "resName"); ResourceBlob pixels(6u * std::accumulate(texDesc.m_dataSizes.cbegin(), texDesc.m_dataSizes.cend(), 0u)); for (size_t i = 0; i < pixels.size(); ++i) { - pixels.data()[i] = static_cast(i); + pixels.data()[i] = std::byte{static_cast(i)}; } ASSERT_EQ(pixels.size(), res.getResourceData().size()); res.setResourceData(std::move(pixels)); - auto loadedTextureResource = createLoadedResource(res, EResourceType_TextureCube); + auto loadedTextureResource = createLoadedResource(res, EResourceType::TextureCube); ASSERT_EQ(res.getWidth(), loadedTextureResource->getWidth()); - ASSERT_EQ(ETextureFormat::RGB8, loadedTextureResource->getTextureFormat()); + ASSERT_EQ(EPixelStorageFormat::RGB8, loadedTextureResource->getTextureFormat()); ASSERT_EQ(res.getGenerateMipChainFlag(), loadedTextureResource->getGenerateMipChainFlag()); ASSERT_EQ(texDesc.m_dataSizes, loadedTextureResource->getMipDataSizes()); - ASSERT_EQ(flag, loadedTextureResource->getCacheFlag()); EXPECT_EQ(std::string("resName"), loadedTextureResource->getName()); } TEST_F(AResourcePersistation, WriteRead_VertexArrayResource) { const uint32_t cnt = 200; - const ResourceCacheFlag flag(15u); - ArrayResource res(EResourceType_VertexArray, cnt, EDataType::Vector2F, nullptr, flag, "resName"); + ArrayResource res(EResourceType::VertexArray, cnt, EDataType::Vector2F, nullptr, "resName"); ResourceBlob vertices(cnt * EnumToSize(EDataType::Vector2F)); for (size_t i = 0; i < 2*cnt; ++i) { - UnsafeTestMemoryHelpers::WriteToMemoryBlob(i*.1f, vertices.data(), i); + UnsafeTestMemoryHelpers::WriteToMemoryBlob(static_cast(i) * .1f, vertices.data(), i); } res.setResourceData(std::move(vertices)); - auto loadedVertexArrayResource = createLoadedResource(res, EResourceType_VertexArray); + auto loadedVertexArrayResource = createLoadedResource(res, EResourceType::VertexArray); ASSERT_EQ(res.getElementCount(), loadedVertexArrayResource->getElementCount()); ASSERT_EQ(EDataType::Vector2F, loadedVertexArrayResource->getElementType()); - ASSERT_EQ(flag, loadedVertexArrayResource->getCacheFlag()); EXPECT_EQ(std::string("resName"), loadedVertexArrayResource->getName()); } TEST_F(AResourcePersistation, WriteRead_Index16ArrayResource) { const uint32_t cnt = 220; - const ResourceCacheFlag flag(15u); - ArrayResource res(EResourceType_IndexArray, cnt, EDataType::UInt16, nullptr, flag, "resName"); + ArrayResource res(EResourceType::IndexArray, cnt, EDataType::UInt16, nullptr, "resName"); ResourceBlob indices(cnt * EnumToSize(EDataType::UInt16)); for (size_t i = 0; i < cnt; ++i) { @@ -164,20 +155,18 @@ namespace ramses_internal } res.setResourceData(std::move(indices)); - auto loadedIndexArrayResource = createLoadedResource(res, EResourceType_IndexArray); + auto loadedIndexArrayResource = createLoadedResource(res, EResourceType::IndexArray); ASSERT_EQ(res.getElementCount(), loadedIndexArrayResource->getElementCount()); ASSERT_EQ(EDataType::UInt16, loadedIndexArrayResource->getElementType()); - ASSERT_EQ(flag, loadedIndexArrayResource->getCacheFlag()); EXPECT_EQ(std::string("resName"), loadedIndexArrayResource->getName()); } TEST_F(AResourcePersistation, WriteRead_Index32ArrayResource) { const uint32_t cnt = 220; - const ResourceCacheFlag flag(15u); - ArrayResource res(EResourceType_IndexArray, cnt, EDataType::UInt32, nullptr, flag, "resName"); + ArrayResource res(EResourceType::IndexArray, cnt, EDataType::UInt32, nullptr, "resName"); ResourceBlob indices(cnt * EnumToSize(EDataType::UInt32)); for (size_t i = 0; i < cnt; ++i) { @@ -185,25 +174,22 @@ namespace ramses_internal } res.setResourceData(std::move(indices)); - auto loadedIndexArrayResource = createLoadedResource(res, EResourceType_IndexArray); + auto loadedIndexArrayResource = createLoadedResource(res, EResourceType::IndexArray); ASSERT_EQ(res.getElementCount(), loadedIndexArrayResource->getElementCount()); ASSERT_EQ(EDataType::UInt32, loadedIndexArrayResource->getElementType()); - ASSERT_EQ(flag, loadedIndexArrayResource->getCacheFlag()); EXPECT_EQ(std::string("resName"), loadedIndexArrayResource->getName()); } TEST_F(AResourcePersistation, WriteRead_EffectResource) { - const ResourceCacheFlag flag(15u); - EffectResource effectResource("vertexBla", "fragmentFoo", "geometryFoo", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "effect name", flag); + EffectResource effectResource("vertexBla", "fragmentFoo", "geometryFoo", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "effect name"); - auto loadedEffectResource = createLoadedResource(effectResource, EResourceType_Effect); + auto loadedEffectResource = createLoadedResource(effectResource, EResourceType::Effect); EXPECT_STREQ(effectResource.getVertexShader(), loadedEffectResource->getVertexShader()); EXPECT_STREQ(effectResource.getFragmentShader(), loadedEffectResource->getFragmentShader()); EXPECT_STREQ(effectResource.getGeometryShader(), loadedEffectResource->getGeometryShader()); - ASSERT_EQ(flag, loadedEffectResource->getCacheFlag()); EXPECT_EQ(std::string("effect name"), loadedEffectResource->getName()); EXPECT_EQ(EDrawMode::Lines, loadedEffectResource->getGeometryShaderInputType()); } @@ -213,16 +199,12 @@ namespace ramses_internal NiceMock managedResourceDeleter; ResourceDeleterCallingCallback dummyManagedResourceCallback(managedResourceDeleter); - const ResourceCacheFlag flag1(15u); - const ResourceCacheFlag flag2(16u); - const ResourceCacheFlag flag3(17u); - float dataA[9]; for (uint32_t i = 0u; i < 9; ++i) { dataA[i] = static_cast(i); } - ArrayResource res(EResourceType_VertexArray, 3, EDataType::Vector3F, dataA, flag1, "res1"); + ArrayResource res(EResourceType::VertexArray, 3, EDataType::Vector3F, dataA, "res1"); ManagedResource managedRes{ &res, dummyManagedResourceCallback }; const ResourceContentHash hash = managedRes->getHash(); @@ -231,11 +213,11 @@ namespace ramses_internal { dataB[i] = static_cast(i); } - ArrayResource res2(EResourceType_VertexArray, 6, EDataType::Vector3F, dataB, flag2, "res2"); + ArrayResource res2(EResourceType::VertexArray, 6, EDataType::Vector3F, dataB, "res2"); ManagedResource managedRes2{ &res2, dummyManagedResourceCallback }; const ResourceContentHash hash2 = managedRes2->getHash(); - EffectResource res3("foo", "bar", "qux", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "Some effect with a name", flag3); + EffectResource res3("foo", "bar", "qux", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "Some effect with a name"); ManagedResource managedRes3{ &res3, dummyManagedResourceCallback }; const ResourceContentHash hash3 = managedRes3->getHash(); @@ -257,7 +239,6 @@ namespace ramses_internal ASSERT_TRUE(loadedTOC.containsResource(hash)); auto loadedResource = ResourcePersistation::RetrieveResourceFromStream(instream, loadedTOC.getEntryForHash(hash)); ASSERT_TRUE(UnsafeTestMemoryHelpers::CompareMemoryBlobToSpan(dataA, sizeof(dataA), loadedResource->getResourceData().span())); - ASSERT_EQ(flag1, loadedResource->getCacheFlag()); EXPECT_EQ(std::string("res1"), loadedResource->getName()); } @@ -265,7 +246,6 @@ namespace ramses_internal ASSERT_TRUE(loadedTOC.containsResource(hash2)); auto loadedResource = ResourcePersistation::RetrieveResourceFromStream(instream, loadedTOC.getEntryForHash(hash2)); ASSERT_TRUE(UnsafeTestMemoryHelpers::CompareMemoryBlobToSpan(dataB, sizeof(dataB), loadedResource->getResourceData().span())); - ASSERT_EQ(flag2, loadedResource->getCacheFlag()); EXPECT_EQ(std::string("res2"), loadedResource->getName()); } @@ -274,24 +254,23 @@ namespace ramses_internal auto loadedResource = ResourcePersistation::RetrieveResourceFromStream(instream, loadedTOC.getEntryForHash(hash3)); EXPECT_STREQ(res3.getVertexShader(), loadedResource->convertTo()->getVertexShader()); EXPECT_STREQ(res3.getFragmentShader(), loadedResource->convertTo()->getFragmentShader()); - ASSERT_EQ(flag3, loadedResource->getCacheFlag()); EXPECT_EQ(std::string("Some effect with a name"), loadedResource->getName()); } } - static std::pair, ResourceFileEntry> getDummyResourceData() + static std::pair, ResourceFileEntry> getDummyResourceData() { BinaryOutputStream outStream; - EffectResource res("foo", "bar", "qux", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "Some effect with a name", ResourceCacheFlag(0)); + EffectResource res("foo", "bar", "qux", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "Some effect with a name"); NiceMock managedResourceDeleter; ResourceDeleterCallingCallback dummyManagedResourceCallback(managedResourceDeleter); ManagedResource managedRes{ &res, dummyManagedResourceCallback }; ResourcePersistation::WriteOneResourceToStream(outStream, managedRes); auto data = outStream.release(); - const uint32_t dataSize = static_cast(data.size()); + const auto dataSize = static_cast(data.size()); ResourceFileEntry entry{0, dataSize, - ResourceInfo(EResourceType_Effect, res.getHash(), dataSize, 0)}; + ResourceInfo(EResourceType::Effect, res.getHash(), dataSize, 0)}; return {data, entry}; } @@ -300,7 +279,7 @@ namespace ramses_internal const auto dummyResource = getDummyResourceData(); InputStreamMock stream; BinaryInputStream resStream(dummyResource.first.data()); - EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](Int offset, auto origin) { + EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](int64_t offset, auto origin) { return resStream.seek(offset, origin); }); EXPECT_CALL(stream, getState()).WillRepeatedly([&]() { @@ -330,7 +309,7 @@ namespace ramses_internal const auto dummyResource = getDummyResourceData(); InputStreamMock stream; BinaryInputStream resStream(dummyResource.first.data()); - EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](Int offset, auto origin) { + EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](int64_t offset, auto origin) { return resStream.seek(offset, origin); }); EXPECT_CALL(stream, getState()).WillRepeatedly([&]() { @@ -349,7 +328,7 @@ namespace ramses_internal const auto dummyResource = getDummyResourceData(); InputStreamMock stream; BinaryInputStream resStream(dummyResource.first.data()); - EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](Int offset, auto origin) { + EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](int64_t offset, auto origin) { return resStream.seek(offset, origin); }); EXPECT_CALL(stream, getState()).WillRepeatedly([&]() { @@ -372,7 +351,7 @@ namespace ramses_internal const auto dummyResource = getDummyResourceData(); InputStreamMock stream; BinaryInputStream resStream(dummyResource.first.data()); - EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](Int offset, auto origin) { + EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](int64_t offset, auto origin) { return resStream.seek(offset, origin); }); EXPECT_CALL(stream, getState()).WillRepeatedly(Return(EStatus::Error)); @@ -388,7 +367,7 @@ namespace ramses_internal const auto dummyResource = getDummyResourceData(); InputStreamMock stream; BinaryInputStream resStream(dummyResource.first.data()); - EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](Int offset, auto origin) { + EXPECT_CALL(stream, seek(_, _)).WillRepeatedly([&](int64_t offset, auto origin) { return resStream.seek(offset, origin); }); EXPECT_CALL(stream, read(_, _)).WillRepeatedly([&](void* data, size_t size) -> IInputStream& { diff --git a/framework/Components/test/ResourceSerializationTestHelper.h b/tests/unittests/framework/Components/ResourceSerializationTestHelper.h similarity index 79% rename from framework/Components/test/ResourceSerializationTestHelper.h rename to tests/unittests/framework/Components/ResourceSerializationTestHelper.h index b77b59398..144e41005 100644 --- a/framework/Components/test/ResourceSerializationTestHelper.h +++ b/tests/unittests/framework/Components/ResourceSerializationTestHelper.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCESERIALIZATIONTESTHELPER_H -#define RAMSES_RESOURCESERIALIZATIONTESTHELPER_H +#pragma once -#include "Resource/TextureResource.h" -#include "Resource/ArrayResource.h" -#include "Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" #include "gtest/gtest.h" -#include "Collections/Vector.h" -#include "Components/ManagedResource.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include "internal/Components/ManagedResource.h" #include "TestRandom.h" -namespace ramses_internal +namespace ramses::internal { class ResourceSerializationTestHelper { @@ -39,7 +38,6 @@ namespace ramses_internal { ASSERT_EQ(a.getTypeID(), b.getTypeID()); EXPECT_EQ(a.getHash(), b.getHash()); - EXPECT_EQ(a.getCacheFlag(), b.getCacheFlag()); EXPECT_EQ(a.getName(), b.getName()); ASSERT_EQ(a.isDeCompressedAvailable(), b.isDeCompressedAvailable()); @@ -60,13 +58,13 @@ namespace ramses_internal inline void ResourceSerializationTestHelper::SetResourceDataRandom(IResource& res, uint32_t blobSize) { - if (!blobSize) + if (blobSize == 0u) return; - const uint8_t seed = static_cast(TestRandom::Get(0, 256)); + const auto seed = static_cast(TestRandom::Get(0, 256)); ResourceBlob data(blobSize); for (size_t i = 0; i < data.size(); ++i) { - data.data()[i] = static_cast(i + seed); + data.data()[i] = static_cast(i + seed); } res.setResourceData(std::move(data)); } @@ -75,8 +73,8 @@ namespace ramses_internal template <> inline IResource* ResourceSerializationTestHelper::CreateTestResource(uint32_t blobSize) { - const TextureMetaInfo texDesc{ 16u, 17u, 1u, ETextureFormat::RGBA16, false, {}, { 18u, 19u} }; - TextureResource* resource = new TextureResource(EResourceType_Texture3D, texDesc, ResourceCacheFlag(15u), "resName"); + const TextureMetaInfo texDesc{ 16u, 17u, 1u, EPixelStorageFormat::RGBA16F, false, {}, { 18u, 19u} }; + auto* resource = new TextureResource(EResourceType::Texture3D, texDesc, "resName"); SetResourceDataRandom(*resource, blobSize); return resource; } @@ -96,7 +94,7 @@ namespace ramses_internal template <> inline IResource* ResourceSerializationTestHelper::CreateTestResource(uint32_t blobSize) { - ArrayResource* resource = new ArrayResource(EResourceType_VertexArray, 0, EDataType::Vector3F, nullptr, ResourceCacheFlag(15u), "resName"); + auto* resource = new ArrayResource(EResourceType::VertexArray, 0, EDataType::Vector3F, nullptr, "resName"); SetResourceDataRandom(*resource, blobSize); return resource; } @@ -124,7 +122,7 @@ namespace ramses_internal frag[i] = 'b'; geom[i] = 'c'; } - EffectResource* resource = new EffectResource(vert, frag, geom, EDrawMode::Points, EffectInputInformationVector(), EffectInputInformationVector(), "effect name", ResourceCacheFlag(1u)); + auto* resource = new EffectResource(vert, frag, geom, EDrawMode::Points, EffectInputInformationVector(), EffectInputInformationVector(), "effect name"); return resource; } @@ -137,5 +135,3 @@ namespace ramses_internal EXPECT_EQ(a.getGeometryShaderInputType(), b.getGeometryShaderInputType()); } } - -#endif diff --git a/framework/Components/test/ResourceStorageTest.cpp b/tests/unittests/framework/Components/ResourceStorageTest.cpp similarity index 77% rename from framework/Components/test/ResourceStorageTest.cpp rename to tests/unittests/framework/Components/ResourceStorageTest.cpp index b5a8d4004..8882c3f6c 100644 --- a/framework/Components/test/ResourceStorageTest.cpp +++ b/tests/unittests/framework/Components/ResourceStorageTest.cpp @@ -6,29 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" -#include "Components/ResourceStorage.h" +#include "internal/Components/ResourceStorage.h" #include "ComponentMocks.h" -#include "Components/ManagedResource.h" +#include "internal/Components/ManagedResource.h" #include "ResourceMock.h" #include "DummyResource.h" -#include "Utils/StatisticCollection.h" +#include "internal/Core/Utils/StatisticCollection.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class AResourceStorage : public testing::Test { public: AResourceStorage() - : lock() - , stats() - , storage(lock, stats) + : storage(lock, stats) { } - void expectResourceSizeCalls(const ResourceMock* resource) + static void ExpectResourceSizeCalls(const ResourceMock* resource) { EXPECT_CALL(*resource, getCompressedDataSize()).Times(AnyNumber()); EXPECT_CALL(*resource, getDecompressedDataSize()).Times(AnyNumber()); @@ -47,9 +44,9 @@ namespace ramses_internal TEST_F(AResourceStorage, DeletesResourceWhenManagedResourceGoesOutOfScope) { - ResourceWithDestructorMock* resource = new ResourceWithDestructorMock(ResourceContentHash(456u, 0), EResourceType_Invalid); + auto* resource = new ResourceWithDestructorMock(ResourceContentHash(456u, 0), EResourceType::Invalid); { - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); EXPECT_CALL(*resource, Die()); ManagedResource managed = storage.manageResource(*resource); } @@ -60,11 +57,11 @@ namespace ramses_internal TEST_F(AResourceStorage, DoesNotDeleteResourceWhenManagedResourceGoesOutOfScopeUntilHashUsageIsAlsoGone) { const ResourceContentHash hash(456u, 0); - ResourceWithDestructorMock* resource = new ResourceWithDestructorMock(hash, EResourceType_Invalid); + auto* resource = new ResourceWithDestructorMock(hash, EResourceType::Invalid); // take one hash usage ResourceHashUsage hashUsage = storage.getResourceHashUsage(hash); // take one resource data usage - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); ManagedResource managed = storage.manageResource(*resource); // let go of data @@ -74,7 +71,7 @@ namespace ramses_internal Mock::VerifyAndClearExpectations(resource); // let go of hash usage - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); EXPECT_CALL(*resource, Die()); hashUsage = ResourceHashUsage(); @@ -84,11 +81,11 @@ namespace ramses_internal TEST_F(AResourceStorage, DeletesResourceWhenOnlyHashUsageIsLeftButDeletionOK) { const ResourceContentHash hash(465, 0); - NiceMock* resource = new NiceMock(hash, EResourceType_Invalid); + auto* resource = new NiceMock(hash, EResourceType::Invalid); // take one hash usage ResourceHashUsage hashUsage = storage.getResourceHashUsage(hash); // take one resource data usage - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); ManagedResource managed = storage.manageResource(*resource, true); // let go of data usage, only hasUsage left, deletion is allowed, so data is deleted @@ -103,12 +100,12 @@ namespace ramses_internal TEST_F(AResourceStorage, StillMaintainsDeletionAllowedWhenSecondResourceWithNotAllowedIsCreated) { const ResourceContentHash hash(465, 0); - NiceMock* resource1 = new NiceMock(hash, EResourceType_Invalid); - NiceMock* resource2 = new NiceMock(hash, EResourceType_Invalid); + auto* resource1 = new NiceMock(hash, EResourceType::Invalid); + auto* resource2 = new NiceMock(hash, EResourceType::Invalid); // take one hash usage ResourceHashUsage hashUsage = storage.getResourceHashUsage(hash); // take one resource data usage - expectResourceSizeCalls(resource1); + ExpectResourceSizeCalls(resource1); ManagedResource managedDel = storage.manageResource(*resource1, true); // create second resource with same hash, will be deleted @@ -133,12 +130,12 @@ namespace ramses_internal TEST_F(AResourceStorage, DeletionAllowedCanButUpgradedToTrueByFollowupResourceWhereItIsAllowed) { const ResourceContentHash hash(465, 0); - NiceMock* resource1 = new NiceMock(hash, EResourceType_Invalid); - NiceMock* resource2 = new NiceMock(hash, EResourceType_Invalid); + auto* resource1 = new NiceMock(hash, EResourceType::Invalid); + auto* resource2 = new NiceMock(hash, EResourceType::Invalid); // take one hash usage ResourceHashUsage hashUsage = storage.getResourceHashUsage(hash); // take one resource data usage - expectResourceSizeCalls(resource1); + ExpectResourceSizeCalls(resource1); ManagedResource managedNoDel = storage.manageResource(*resource1, false); // create second resource with same hash, will be deleted but changes deletion status to true @@ -163,22 +160,22 @@ namespace ramses_internal TEST_F(AResourceStorage, DeletionAllowedStateIsMaintainedEvenWhenResourceWasAlreadyDeletedOnce) { const ResourceContentHash hash(465, 0); - NiceMock* resource1 = new NiceMock(hash, EResourceType_Invalid); + auto* resource1 = new NiceMock(hash, EResourceType::Invalid); // take one hash usage ResourceHashUsage hashUsage = storage.getResourceHashUsage(hash); { // create and delete resource with deletion allowed - expectResourceSizeCalls(resource1); + ExpectResourceSizeCalls(resource1); ManagedResource managed = storage.manageResource(*resource1, true); EXPECT_CALL(*resource1, Die()); } Mock::VerifyAndClearExpectations(resource1); - NiceMock* resource2 = new NiceMock(hash, EResourceType_Invalid); + auto* resource2 = new NiceMock(hash, EResourceType::Invalid); { - expectResourceSizeCalls(resource2); + ExpectResourceSizeCalls(resource2); ManagedResource managed = storage.manageResource(*resource2, false); EXPECT_CALL(*resource2, Die()); } @@ -188,8 +185,8 @@ namespace ramses_internal TEST_F(AResourceStorage, DeletesResourceAfterAllManagedResourcesGoOutOfScope) { const ResourceContentHash hash(456, 0); - ResourceWithDestructorMock* resource = new ResourceWithDestructorMock(hash, EResourceType_Invalid); - expectResourceSizeCalls(resource); + auto* resource = new ResourceWithDestructorMock(hash, EResourceType::Invalid); + ExpectResourceSizeCalls(resource); ManagedResource managed = storage.manageResource(*resource); ManagedResource usage1 = storage.getResource(hash); @@ -207,7 +204,7 @@ namespace ramses_internal Mock::VerifyAndClearExpectations(resource); // last usage goes away, not its really deleted - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); EXPECT_CALL(*resource, Die()).Times(1); usage2 = ManagedResource(); Mock::VerifyAndClearExpectations(resource); //to make sure the dynamically allocated mock resource is destroyed (Die() is called) @@ -216,9 +213,9 @@ namespace ramses_internal TEST_F(AResourceStorage, ReturnsAManagedResource) { const ResourceContentHash hash(456, 0); - ResourceMock* resource = new ResourceMock(hash, EResourceType_Invalid); + auto* resource = new ResourceMock(hash, EResourceType::Invalid); - expectResourceSizeCalls(resource); + ExpectResourceSizeCalls(resource); ManagedResource managedRes = storage.manageResource(*resource); ManagedResource obtainedRes = storage.getResource(hash); EXPECT_EQ(resource, obtainedRes.get()); @@ -250,12 +247,12 @@ namespace ramses_internal TEST_F(AResourceStorage, ReturnsAllManagedResources) { - ResourceMock* resource = new ResourceMock(ResourceContentHash(456, 0), EResourceType_Invalid); - expectResourceSizeCalls(resource); + auto* resource = new ResourceMock(ResourceContentHash(456, 0), EResourceType::Invalid); + ExpectResourceSizeCalls(resource); ManagedResource managedRes = storage.manageResource(*resource); - ResourceMock* resource2 = new ResourceMock(ResourceContentHash(123, 0), EResourceType_Invalid); - expectResourceSizeCalls(resource2); + auto* resource2 = new ResourceMock(ResourceContentHash(123, 0), EResourceType::Invalid); + ExpectResourceSizeCalls(resource2); ManagedResource managedRes2 = storage.manageResource(*resource2); ManagedResourceVector allResources = storage.getResources(); @@ -267,7 +264,7 @@ namespace ramses_internal TEST_F(AResourceStorage, StoresAndReturnsResourceInfo) { ResourceContentHash hash(123, 0); - ResourceInfo testResourceInfo(EResourceType_VertexArray, hash, 22, 11); + ResourceInfo testResourceInfo(EResourceType::VertexArray, hash, 22, 11); storage.storeResourceInfo(hash, testResourceInfo); ResourceHashUsage hashUsage = storage.getResourceHashUsage(hash); //ensure that resource entry is destructed @@ -278,12 +275,12 @@ namespace ramses_internal TEST_F(AResourceStorage, StoresResourceInfoFromProvidedResource) { ResourceContentHash hash(123, 0); - DummyResource* res = new DummyResource(hash, EResourceType_VertexArray); + auto* res = new DummyResource(hash, EResourceType::VertexArray); ManagedResource managedRes = storage.manageResource(*res); ResourceInfo returnedResourceInfo = storage.getResourceInfo(hash); - EXPECT_EQ(EResourceType_VertexArray, returnedResourceInfo.type); + EXPECT_EQ(EResourceType::VertexArray, returnedResourceInfo.type); EXPECT_EQ(hash, returnedResourceInfo.hash); EXPECT_EQ(res->getDecompressedDataSize(), returnedResourceInfo.decompressedSize); EXPECT_EQ(res->getCompressedDataSize(), returnedResourceInfo.compressedSize); diff --git a/framework/Components/test/ResourceTableOfContentsTest.cpp b/tests/unittests/framework/Components/ResourceTableOfContentsTest.cpp similarity index 82% rename from framework/Components/test/ResourceTableOfContentsTest.cpp rename to tests/unittests/framework/Components/ResourceTableOfContentsTest.cpp index f89c2aab6..71cdaa9af 100644 --- a/framework/Components/test/ResourceTableOfContentsTest.cpp +++ b/tests/unittests/framework/Components/ResourceTableOfContentsTest.cpp @@ -7,18 +7,18 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Components/ResourceTableOfContents.h" -#include "Utils/File.h" -#include "Utils/BinaryFileOutputStream.h" -#include "Utils/BinaryFileInputStream.h" -#include "PlatformAbstraction/PlatformMemory.h" -#include "Components/ManagedResource.h" -#include "Components/ResourcePersistation.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/Components/ResourceTableOfContents.h" +#include "internal/Core/Utils/File.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/Components/ManagedResource.h" +#include "internal/Components/ResourcePersistation.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TEST(AResourceTableOfContents, doesNotContainUnregisteredHash) { @@ -31,7 +31,7 @@ namespace ramses_internal { ResourceContentHash hash(4711u, 0); ResourceTableOfContents toc; - ResourceInfo resourceInfo(EResourceType_IndexArray, hash, 22u, 11u); + ResourceInfo resourceInfo(EResourceType::IndexArray, hash, 22u, 11u); uint32_t offsetWithinFile = 123u; uint32_t resourceSizeInBytes = 456u; toc.registerContents(resourceInfo, offsetWithinFile, resourceSizeInBytes); @@ -51,12 +51,12 @@ namespace ramses_internal ResourceTableOfContents toc; ResourceContentHash hashA(4711u, 0); - ResourceInfo resourceInfoA(EResourceType_IndexArray, hashA, 22u, 11u); + ResourceInfo resourceInfoA(EResourceType::IndexArray, hashA, 22u, 11u); uint32_t offsetInBytesA = 123u; uint32_t sizeInBytesA = 456u; toc.registerContents(resourceInfoA, offsetInBytesA, sizeInBytesA); ResourceContentHash hashB(234678u, 0); - ResourceInfo resourceInfoB(EResourceType_Texture2D, hashB, 44u, 33u); + ResourceInfo resourceInfoB(EResourceType::Texture2D, hashB, 44u, 33u); uint32_t offsetInBytesB = 987u; uint32_t sizeInBytesB = 654u; toc.registerContents(resourceInfoB, offsetInBytesB, sizeInBytesB); diff --git a/framework/Components/test/SceneGraphComponentTest.cpp b/tests/unittests/framework/Components/SceneGraphComponentTest.cpp similarity index 83% rename from framework/Components/test/SceneGraphComponentTest.cpp rename to tests/unittests/framework/Components/SceneGraphComponentTest.cpp index cad8900c5..733fbd5b9 100644 --- a/framework/Components/test/SceneGraphComponentTest.cpp +++ b/tests/unittests/framework/Components/SceneGraphComponentTest.cpp @@ -6,24 +6,24 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/SceneGraphComponent.h" +#include "internal/Components/SceneGraphComponent.h" #include "ComponentMocks.h" #include "CommunicationSystemMock.h" #include "MockConnectionStatusUpdateNotifier.h" #include "ServiceHandlerMocks.h" -#include "Components/FlushTimeInformation.h" +#include "internal/Components/FlushTimeInformation.h" #include "SceneRendererHandlerMock.h" -#include "TransportCommon/SceneUpdateSerializer.h" -#include "Components/SceneUpdate.h" +#include "internal/Communication/TransportCommon/SceneUpdateSerializer.h" +#include "internal/Components/SceneUpdate.h" #include "SceneUpdateSerializerTestHelper.h" -#include "Resource/ArrayResource.h" -#include "Resource/TextureResource.h" -#include "Components/ClientSceneLogicBase.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/Components/ClientSceneLogicBase.h" #include -using namespace ramses_internal; - +namespace ramses::internal +{ class ASceneGraphComponentBase : public ::testing::Test { public: @@ -32,13 +32,13 @@ class ASceneGraphComponentBase : public ::testing::Test , remoteParticipantID(12) , sceneGraphComponent(localParticipantID, communicationSystem, connectionStatusUpdateNotifier, resourceComponent, frameworkLock, ramses::EFeatureLevel_Latest) { - localSceneIdInfo = SceneInfo(localSceneId, "sceneName", EScenePublicationMode_LocalOnly); + localSceneIdInfo = SceneInfo(localSceneId, "sceneName", EScenePublicationMode::LocalOnly); localSceneIdInfoVector.push_back(localSceneIdInfo); - localAndRemoteSceneIdInfo = SceneInfo(localSceneId, "sceneName", EScenePublicationMode_LocalAndRemote); + localAndRemoteSceneIdInfo = SceneInfo(localSceneId, "sceneName", EScenePublicationMode::LocalAndRemote); localAndRemoteSceneIdInfoVector.push_back(localAndRemoteSceneIdInfo); } - SceneActionCollection createFakeSceneActionCollectionFromTypes(const std::vector& types) + static SceneActionCollection CreateFakeSceneActionCollectionFromTypes(const std::vector& types) { SceneActionCollection collection; for (auto t : types) @@ -48,7 +48,7 @@ class ASceneGraphComponentBase : public ::testing::Test return collection; } - std::vector> actionsToChunks(const SceneActionCollection& actions, uint32_t chunkSize = 100000, const ManagedResourceVector& resources = {}, const FlushInformation& flushinfo = {}) + std::vector> actionsToChunks(const SceneActionCollection& actions, uint32_t chunkSize = 100000, const ManagedResourceVector& resources = {}, const FlushInformation& flushinfo = {}) { SceneUpdate update{actions.copy(), resources, flushinfo.copy()}; return TestSerializeSceneUpdateToVectorChunked(SceneUpdateSerializer(update, sceneStatistics), chunkSize); @@ -56,7 +56,7 @@ class ASceneGraphComponentBase : public ::testing::Test void expectSendSceneActionsToNetwork(Guid remote, SceneId sceneId, const SceneActionCollection& expectedActions) { - EXPECT_CALL(communicationSystem, sendSceneUpdate(remote, sceneId, _)).WillOnce([&](auto, auto, auto& serializer) { + EXPECT_CALL(communicationSystem, sendSceneUpdate(remote, sceneId, _)).WillOnce([&](auto /*unused*/, auto /*unused*/, auto& serializer) { // grab actions directly out of serializer const auto actions = static_cast(serializer).getUpdate().actions.copy(); EXPECT_EQ(expectedActions, actions); @@ -68,7 +68,7 @@ class ASceneGraphComponentBase : public ::testing::Test { SceneInfo info(SceneId(sceneId), name, pubMode); EXPECT_CALL(consumer, handleNewSceneAvailable(info, _)); - if (pubMode != EScenePublicationMode_LocalOnly) + if (pubMode != EScenePublicationMode::LocalOnly) EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(SceneInfoVector{info}, ramses::EFeatureLevel_Latest)); sceneGraphComponent.sendPublishScene(SceneId(sceneId), pubMode, name); } @@ -96,7 +96,6 @@ class ASceneGraphComponent : public ASceneGraphComponentBase { public: ASceneGraphComponent() - : ASceneGraphComponentBase() { sceneGraphComponent.connectToNetwork(); } @@ -112,20 +111,20 @@ TEST_F(ASceneGraphComponent, sendsSceneToLocalConsumer) sceneGraphComponent.setSceneRendererHandler(&consumer); EXPECT_CALL(consumer, handleInitializeScene(_, _)).Times(1); - sceneGraphComponent.sendCreateScene(localParticipantID, SceneId(666u), EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(localParticipantID, SceneId(666u), EScenePublicationMode::LocalAndRemote); } TEST_F(ASceneGraphComponent, doesntSendSceneIfLocalConsumerIsntSet) { EXPECT_CALL(consumer, handleInitializeScene(_, localParticipantID)).Times(0); - sceneGraphComponent.sendCreateScene(localParticipantID, SceneId(666u), EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(localParticipantID, SceneId(666u), EScenePublicationMode::LocalAndRemote); } TEST_F(ASceneGraphComponent, sendsSceneToRemoteProvider) { EXPECT_CALL(communicationSystem, sendInitializeScene(remoteParticipantID, SceneId(666u))); - sceneGraphComponent.sendCreateScene(remoteParticipantID, SceneId(666u), EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(remoteParticipantID, SceneId(666u), EScenePublicationMode::LocalAndRemote); } TEST_F(ASceneGraphComponent, publishesSceneAtLocalConsumerInLocalAndRemoteMode) @@ -134,7 +133,7 @@ TEST_F(ASceneGraphComponent, publishesSceneAtLocalConsumerInLocalAndRemoteMode) EXPECT_CALL(consumer, handleNewSceneAvailable(localAndRemoteSceneIdInfo, _)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(localAndRemoteSceneIdInfoVector, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); } TEST_F(ASceneGraphComponentNotConnected, publishesSceneAtLocalConsumerInLocalAndRemoteMode) @@ -143,12 +142,12 @@ TEST_F(ASceneGraphComponentNotConnected, publishesSceneAtLocalConsumerInLocalAnd { EXPECT_CALL(consumer, handleNewSceneAvailable(localAndRemoteSceneIdInfo, _)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(_, ramses::EFeatureLevel_Latest)).Times(0); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); } { EXPECT_CALL(consumer, handleSceneBecameUnavailable(localAndRemoteSceneIdInfo.sceneID, _)); EXPECT_CALL(communicationSystem, broadcastScenesBecameUnavailable(_)).Times(0); - sceneGraphComponent.sendUnpublishScene(localSceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendUnpublishScene(localSceneId, EScenePublicationMode::LocalAndRemote); } } @@ -157,19 +156,19 @@ TEST_F(ASceneGraphComponent, publishesSceneAtLocalConsumerInLocalOnlyMode) sceneGraphComponent.setSceneRendererHandler(&consumer); EXPECT_CALL(consumer, handleNewSceneAvailable(localSceneIdInfo, _)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalOnly, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalOnly, "sceneName"); } TEST_F(ASceneGraphComponent, doesntPublishSceneIfLocalConsumerIsntSet) { EXPECT_CALL(consumer, handleNewSceneAvailable(localAndRemoteSceneIdInfo, _)).Times(0); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(localSceneIdInfoVector, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); } TEST_F(ASceneGraphComponent, doesNotPublishSceneToRemoteProviderInLocalOnlyMode) { - sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode_LocalOnly, "sceneName"); + sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode::LocalOnly, "sceneName"); // no expect needed, StrictMock on communicationSystem checks it already } @@ -179,12 +178,12 @@ TEST_F(ASceneGraphComponent, publishesSceneAtRemoteProvider) SceneInfoVector newScenes(1, SceneInfo(remoteSceneId, "sceneName")); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(newScenes, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); } TEST_F(ASceneGraphComponent, alreadyPublishedSceneGetsRepublishedWhenLocalConsumerIsSet) { - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalOnly, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalOnly, "sceneName"); EXPECT_CALL(consumer, handleNewSceneAvailable(localSceneIdInfo, _)); sceneGraphComponent.setSceneRendererHandler(&consumer); @@ -201,7 +200,7 @@ TEST_F(ASceneGraphComponent, locallySubscribedScenesGetUnsubscibedWhenLocalConsu sceneGraphComponent.handleCreateScene(scene, false, eventConsumer); EXPECT_CALL(consumer, handleNewSceneAvailable(sceneInfo, _)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(SceneInfoVector{ sceneInfo }, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.handlePublishScene(SceneId(1), EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.handlePublishScene(SceneId(1), EScenePublicationMode::LocalAndRemote); const auto* logic = sceneGraphComponent.getClientSceneLogicForScene(sceneId); ASSERT_TRUE(logic); @@ -221,11 +220,11 @@ TEST_F(ASceneGraphComponent, unpublishesSceneAtLocalConsumer) sceneGraphComponent.setSceneRendererHandler(&consumer); SceneInfoVector newScenes(1, SceneInfo(sceneId, "sceneName")); - EXPECT_CALL(consumer, handleNewSceneAvailable(SceneInfo(sceneId, "sceneName", EScenePublicationMode_LocalOnly), _)); - sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode_LocalOnly, "sceneName"); + EXPECT_CALL(consumer, handleNewSceneAvailable(SceneInfo(sceneId, "sceneName", EScenePublicationMode::LocalOnly), _)); + sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode::LocalOnly, "sceneName"); EXPECT_CALL(consumer, handleSceneBecameUnavailable(sceneId, _)); - sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode_LocalOnly); + sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode::LocalOnly); } TEST_F(ASceneGraphComponent, doesntUnpublishSceneIfLocalConsumerIsntSet) @@ -233,15 +232,15 @@ TEST_F(ASceneGraphComponent, doesntUnpublishSceneIfLocalConsumerIsntSet) EXPECT_CALL(consumer, handleSceneBecameUnavailable(_, _)).Times(0); const SceneId sceneId(1ull << 63); - sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode_LocalOnly, "sceneName"); - sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode_LocalOnly); + sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode::LocalOnly, "sceneName"); + sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode::LocalOnly); } TEST_F(ASceneGraphComponent, doesNotUnpublishesSceneAtRemoteProviderIfSceneWasPublishedLocalOnly) { const SceneId sceneId(1ull << 63); - sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode_LocalOnly, "sceneName"); - sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode_LocalOnly); + sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode::LocalOnly, "sceneName"); + sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode::LocalOnly); // expect noting on remote, checked by strict mock } @@ -250,18 +249,18 @@ TEST_F(ASceneGraphComponent, unpublishesSceneAtRemoteProvider) const SceneId sceneId(1ull << 63); sceneGraphComponent.newParticipantHasConnected(Guid(33)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(_, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); SceneInfoVector unavailableScenes(1, SceneInfo(sceneId, "sceneName")); EXPECT_CALL(communicationSystem, broadcastScenesBecameUnavailable(unavailableScenes)); - sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendUnpublishScene(sceneId, EScenePublicationMode::LocalAndRemote); } TEST_F(ASceneGraphComponent, sendsAvailableScenesToNewParticipant) { SceneInfoVector newScenes(1, SceneInfo(remoteSceneId, "sceneName")); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(newScenes, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); Guid id(33); EXPECT_CALL(communicationSystem, sendScenesAvailable(id, newScenes, ramses::EFeatureLevel_Latest)); @@ -271,7 +270,7 @@ TEST_F(ASceneGraphComponent, sendsAvailableScenesToNewParticipant) TEST_F(ASceneGraphComponentNotConnected, sendsAvailableScenesToNewParticipant) { SceneInfoVector newScenes(1, SceneInfo(remoteSceneId, "sceneName")); - sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); Guid id(33); EXPECT_CALL(communicationSystem, sendScenesAvailable(id, newScenes, ramses::EFeatureLevel_Latest)); @@ -281,7 +280,7 @@ TEST_F(ASceneGraphComponentNotConnected, sendsAvailableScenesToNewParticipant) TEST_F(ASceneGraphComponent, doesNotSendAvailableSceneToNewParticipantIfLocalOnlyScene) { SceneInfoVector newScenes(1, SceneInfo(remoteSceneId, "sceneName")); - sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode_LocalOnly, "sceneName"); + sceneGraphComponent.sendPublishScene(remoteSceneId, EScenePublicationMode::LocalOnly, "sceneName"); Guid id(33); EXPECT_CALL(communicationSystem, sendScenesAvailable(id, newScenes, ramses::EFeatureLevel_Latest)).Times(0); @@ -312,13 +311,13 @@ TEST_F(ASceneGraphComponent, sendsSceneActionToLocalConsumer) { sceneGraphComponent.setSceneRendererHandler(&consumer); - SceneActionCollection list(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(666u), _, localParticipantID)).WillOnce([&](auto, const auto& update, auto) { + SceneActionCollection list(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(666u), _, localParticipantID)).WillOnce([&](auto /*unused*/, const auto& update, auto /*unused*/) { EXPECT_EQ(list, update.actions); }); SceneUpdate update; update.actions = list.copy(); - sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), SceneId(666u), EScenePublicationMode_LocalOnly, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), SceneId(666u), EScenePublicationMode::LocalOnly, sceneStatistics); } TEST_F(ASceneGraphComponent, sendsResourcesUnCompressedToLocalConsumer) @@ -326,36 +325,36 @@ TEST_F(ASceneGraphComponent, sendsResourcesUnCompressedToLocalConsumer) sceneGraphComponent.setSceneRendererHandler(&consumer); ResourceBlob blob(1024 * EnumToSize(EDataType::Float)); - std::iota(blob.data(), blob.data() + blob.size(), static_cast(10)); + std::generate(blob.data(), blob.data() + blob.size(), [](){ static uint8_t i{9}; return std::byte(++i); }); ManagedResourceVector resources{ - std::make_shared(EResourceType_VertexArray, 1024u, EDataType::Float, blob.data(), ResourceCacheFlag_DoNotCache, "fl") + std::make_shared(EResourceType::VertexArray, 1024u, EDataType::Float, blob.data(), "fl") }; // not compressed after creating EXPECT_FALSE(resources[0]->isCompressedAvailable()); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(666u), _, localParticipantID)).WillOnce([&](auto, const auto& update, auto) { + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(666u), _, localParticipantID)).WillOnce([&](auto /*unused*/, const auto& update, auto /*unused*/) { EXPECT_EQ(resources, update.resources); // compressed for sending EXPECT_FALSE(update.resources[0]->isCompressedAvailable()); }); SceneUpdate update; update.resources = resources; - sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), SceneId(666u), EScenePublicationMode_LocalOnly, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), SceneId(666u), EScenePublicationMode::LocalOnly, sceneStatistics); } TEST_F(ASceneGraphComponent, sendsResourcesCompressedToRemoteProvider) { SceneId sceneId; EXPECT_CALL(communicationSystem, sendInitializeScene(_, _)).Times(1); - sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode::LocalAndRemote); ResourceBlob blob(1024 * EnumToSize(EDataType::Float)); - std::iota(blob.data(), blob.data() + blob.size(), static_cast(10)); + std::generate(blob.data(), blob.data() + blob.size(), [](){ static uint8_t i{9}; return std::byte(++i); }); ManagedResourceVector resourcesToSend{ - std::make_shared(EResourceType_VertexArray, 1024u, EDataType::Float, blob.data(), ResourceCacheFlag_DoNotCache, "fl") + std::make_shared(EResourceType::VertexArray, 1024u, EDataType::Float, blob.data(), "fl") }; - EXPECT_CALL(communicationSystem, sendSceneUpdate(remoteParticipantID, sceneId, _)).WillOnce([&](auto, auto, auto& serializer) { + EXPECT_CALL(communicationSystem, sendSceneUpdate(remoteParticipantID, sceneId, _)).WillOnce([&](auto /*unused*/, auto /*unused*/, auto& serializer) { // grab resources directly out of serializer const auto resources = static_cast(serializer).getUpdate().resources; EXPECT_EQ(resourcesToSend, resources); @@ -364,54 +363,54 @@ TEST_F(ASceneGraphComponent, sendsResourcesCompressedToRemoteProvider) }); SceneUpdate update; update.resources= resourcesToSend; - sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode_LocalAndRemote, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode::LocalAndRemote, sceneStatistics); } TEST_F(ASceneGraphComponent, doesntSendSceneActionIfLocalConsumerIsntSet) { - SceneActionCollection list(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + SceneActionCollection list(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); EXPECT_CALL(consumer, handleSceneUpdate_rvr(_, _, _)).Times(0); SceneUpdate update; update.actions = list.copy(); - sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), SceneId(666u), EScenePublicationMode_LocalOnly, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), SceneId(666u), EScenePublicationMode::LocalOnly, sceneStatistics); } TEST_F(ASceneGraphComponent, sendsSceneActionToRemoteProvider) { SceneId sceneId; EXPECT_CALL(communicationSystem, sendInitializeScene(_, _)).Times(1); - sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode::LocalAndRemote); - SceneActionCollection list(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + SceneActionCollection list(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); expectSendSceneActionsToNetwork(remoteParticipantID, sceneId, list); SceneUpdate update; update.actions = list.copy(); - sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode_LocalAndRemote, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode::LocalAndRemote, sceneStatistics); } TEST_F(ASceneGraphComponent, sendsAllSceneActionsAtOnceToRemoteProvider) { SceneId sceneId(1); EXPECT_CALL(communicationSystem, sendInitializeScene(_, _)).Times(1); - sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode::LocalAndRemote); - SceneActionCollection list(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction, ESceneActionId::SetDataVector2fArray, ESceneActionId::AllocateNode })); + SceneActionCollection list(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction, ESceneActionId::SetDataVector2fArray, ESceneActionId::AllocateNode })); expectSendSceneActionsToNetwork(remoteParticipantID, sceneId, list); SceneUpdate update; update.actions = list.copy(); - sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode_LocalAndRemote, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode::LocalAndRemote, sceneStatistics); } TEST_F(ASceneGraphComponent, doesNotsendSceneUpdateToRemoteIfSceneWasPublishedLocalOnly) { const SceneId sceneId(111); - sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode_LocalOnly, "sceneName"); - SceneActionCollection list(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + sceneGraphComponent.sendPublishScene(sceneId, EScenePublicationMode::LocalOnly, "sceneName"); + SceneActionCollection list(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); SceneUpdate update; update.actions = list.copy(); - sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), sceneId, EScenePublicationMode_LocalOnly, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ localParticipantID }, std::move(update), sceneId, EScenePublicationMode::LocalOnly, sceneStatistics); // expect noting for remote, check by strict mock } @@ -422,17 +421,17 @@ TEST_F(ASceneGraphComponent, canSendSceneActionsToLocalAndRemote) const SceneId sceneId(456); EXPECT_CALL(communicationSystem, sendInitializeScene(_, _)).Times(1); - sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode::LocalAndRemote); - SceneActionCollection list(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + SceneActionCollection list(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); InSequence seq; expectSendSceneActionsToNetwork(remoteParticipantID, sceneId, list); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(sceneId, _, localParticipantID)).WillOnce([&](auto, const auto& update, auto) { + EXPECT_CALL(consumer, handleSceneUpdate_rvr(sceneId, _, localParticipantID)).WillOnce([&](auto /*unused*/, const auto& update, auto /*unused*/) { EXPECT_EQ(list, update.actions); }); SceneUpdate update; update.actions = list.copy(); - sceneGraphComponent.sendSceneUpdate({ remoteParticipantID, localParticipantID }, std::move(update), sceneId, EScenePublicationMode_LocalAndRemote, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ remoteParticipantID, localParticipantID }, std::move(update), sceneId, EScenePublicationMode::LocalAndRemote, sceneStatistics); } TEST_F(ASceneGraphComponent, canRepublishALocalOnlySceneToBeDistributedRemotely) @@ -442,12 +441,12 @@ TEST_F(ASceneGraphComponent, canRepublishALocalOnlySceneToBeDistributedRemotely) // publish LocalOnly EXPECT_CALL(consumer, handleNewSceneAvailable(localSceneIdInfo, _)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalOnly, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalOnly, "sceneName"); Mock::VerifyAndClearExpectations(&communicationSystem); // unpublish EXPECT_CALL(consumer, handleSceneBecameUnavailable(_, _)); - sceneGraphComponent.sendUnpublishScene(localSceneId, EScenePublicationMode_LocalOnly); + sceneGraphComponent.sendUnpublishScene(localSceneId, EScenePublicationMode::LocalOnly); Mock::VerifyAndClearExpectations(&communicationSystem); Mock::VerifyAndClearExpectations(&consumer); @@ -455,7 +454,7 @@ TEST_F(ASceneGraphComponent, canRepublishALocalOnlySceneToBeDistributedRemotely) SceneInfoVector newScenes(1, SceneInfo(localSceneId, "sceneName")); EXPECT_CALL(consumer, handleNewSceneAvailable(localAndRemoteSceneIdInfo, localParticipantID)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(newScenes, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); } TEST_F(ASceneGraphComponent, canRepublishARemoteSceneToBeLocalOnly) @@ -466,27 +465,27 @@ TEST_F(ASceneGraphComponent, canRepublishARemoteSceneToBeLocalOnly) // publish LocalAndRemote EXPECT_CALL(consumer, handleNewSceneAvailable(localAndRemoteSceneIdInfo, localParticipantID)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(localAndRemoteSceneIdInfoVector, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalAndRemote, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalAndRemote, "sceneName"); Mock::VerifyAndClearExpectations(&communicationSystem); Mock::VerifyAndClearExpectations(&consumer); // unpublish EXPECT_CALL(consumer, handleSceneBecameUnavailable(localSceneId, localParticipantID)); EXPECT_CALL(communicationSystem, broadcastScenesBecameUnavailable(localAndRemoteSceneIdInfoVector)); - sceneGraphComponent.sendUnpublishScene(localSceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendUnpublishScene(localSceneId, EScenePublicationMode::LocalAndRemote); Mock::VerifyAndClearExpectations(&communicationSystem); Mock::VerifyAndClearExpectations(&consumer); // publish LocalOnly EXPECT_CALL(consumer, handleNewSceneAvailable(_, _)); - sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode_LocalOnly, "sceneName"); + sceneGraphComponent.sendPublishScene(localSceneId, EScenePublicationMode::LocalOnly, "sceneName"); Mock::VerifyAndClearExpectations(&communicationSystem); } TEST_F(ASceneGraphComponent, disconnectFromNetworkBroadcastsScenesUnavailableOnNetworkOnly) { sceneGraphComponent.setSceneRendererHandler(&consumer); - publishScene(1, "name", EScenePublicationMode_LocalAndRemote); + publishScene(1, "name", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(communicationSystem, broadcastScenesBecameUnavailable(SceneInfoVector{ SceneInfo(SceneId(1), "name") })); sceneGraphComponent.disconnectFromNetwork(); } @@ -494,9 +493,9 @@ TEST_F(ASceneGraphComponent, disconnectFromNetworkBroadcastsScenesUnavailableOnN TEST_F(ASceneGraphComponent, disconnectFromNetworkBroadcastsScenesUnavailableOnNetworkForAllRemoteScenes) { sceneGraphComponent.setSceneRendererHandler(&consumer); - publishScene(1, "name1", EScenePublicationMode_LocalAndRemote); - publishScene(5, "name2", EScenePublicationMode_LocalOnly); - publishScene(3, "name3", EScenePublicationMode_LocalAndRemote); + publishScene(1, "name1", EScenePublicationMode::LocalAndRemote); + publishScene(5, "name2", EScenePublicationMode::LocalOnly); + publishScene(3, "name3", EScenePublicationMode::LocalAndRemote); SceneInfoVector unpubScenes; EXPECT_CALL(communicationSystem, broadcastScenesBecameUnavailable(_)).WillOnce(DoAll(SaveArg<0>(&unpubScenes), Return(true))); @@ -510,7 +509,7 @@ TEST_F(ASceneGraphComponent, disconnectFromNetworkBroadcastsScenesUnavailableOnN TEST_F(ASceneGraphComponent, sendsPublishForNewParticipantsAfterDisconnect) { sceneGraphComponent.setSceneRendererHandler(&consumer); - publishScene(1, "name", EScenePublicationMode_LocalAndRemote); + publishScene(1, "name", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(communicationSystem, broadcastScenesBecameUnavailable(SceneInfoVector{ SceneInfo(SceneId(1), "name") })); sceneGraphComponent.disconnectFromNetwork(); @@ -529,7 +528,7 @@ TEST_F(ASceneGraphComponent, disconnectDoesNotAffectLocalScenesAtAllButUnpublish // subscribe local and remote EXPECT_CALL(consumer, handleNewSceneAvailable(sceneInfo, _)); EXPECT_CALL(communicationSystem, broadcastNewScenesAvailable(SceneInfoVector{ sceneInfo }, ramses::EFeatureLevel_Latest)); - sceneGraphComponent.handlePublishScene(SceneId(1), EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.handlePublishScene(SceneId(1), EScenePublicationMode::LocalAndRemote); EXPECT_CALL(communicationSystem, sendScenesAvailable(remoteParticipantID, SceneInfoVector{ sceneInfo }, ramses::EFeatureLevel_Latest)); sceneGraphComponent.newParticipantHasConnected(remoteParticipantID); @@ -599,12 +598,12 @@ TEST_F(ASceneGraphComponent, disconnectDoesNotAffectLocalScenesAtAllButUnpublish sceneGraphComponent.sendResourceAvailabilityEvent(localParticipantID, event2); EXPECT_CALL(eventConsumer, handleResourceAvailabilityEvent(_, _)).Times(1); - std::vector data1; + std::vector data1; event1.writeToBlob(data1); sceneGraphComponent.handleRendererEvent(event1.sceneid, data1, remoteParticipantID); EXPECT_CALL(scene2Consumer, handleResourceAvailabilityEvent(_, _)).Times(1); - std::vector data2; + std::vector data2; event2.writeToBlob(data2); sceneGraphComponent.handleRendererEvent(event2.sceneid, data2, remoteParticipantID); } @@ -614,7 +613,7 @@ TEST_F(ASceneGraphComponent, sendSceneReferenceEventViaCommSystemForRemotePartic event.type = SceneReferenceEventType::SceneFlushed; event.referencedScene = SceneId{ 456 }; event.tag = SceneVersionTag{ 1000 }; - EXPECT_CALL(communicationSystem, sendRendererEvent(remoteParticipantID, SceneId{ 123 }, _)).WillOnce([&event](const Guid&, const SceneId& sceneId , const std::vector& data) -> bool + EXPECT_CALL(communicationSystem, sendRendererEvent(remoteParticipantID, SceneId{ 123 }, _)).WillOnce([&event](const Guid& /*unused*/, const SceneId& sceneId , const std::vector& data) -> bool { SceneReferenceEvent e{ sceneId }; e.readFromBlob(data); @@ -632,7 +631,7 @@ TEST_F(ASceneGraphComponent, sendResourceAvailabilityEventViaCommSystemForRemote { ResourceAvailabilityEvent event; event.sceneid = SceneId { 123 }; - EXPECT_CALL(communicationSystem, sendRendererEvent(remoteParticipantID, SceneId{ 123 },_)).WillOnce([&event](const Guid&, const SceneId& sceneId, const std::vector& data) -> bool + EXPECT_CALL(communicationSystem, sendRendererEvent(remoteParticipantID, SceneId{ 123 },_)).WillOnce([&event](const Guid& /*unused*/, const SceneId& sceneId, const std::vector& data) -> bool { ResourceAvailabilityEvent e; e.readFromBlob(data); @@ -653,7 +652,7 @@ TEST_F(ASceneGraphComponent, sendSceneReferenceEventForLocalParticipantCallsHand event.type = SceneReferenceEventType::SceneFlushed; event.referencedScene = SceneId{ 456 }; event.tag = SceneVersionTag{ 1000 }; - EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, localParticipantID)).WillOnce([&event](SceneReferenceEvent const& e, const Guid&) + EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, localParticipantID)).WillOnce([&event](SceneReferenceEvent const& e, const Guid& /*unused*/) { EXPECT_EQ(e.masterSceneId, event.masterSceneId); EXPECT_EQ(e.type, event.type); @@ -672,7 +671,7 @@ TEST_F(ASceneGraphComponent, sendResourceAvailabilityEventForLocalParticipantCal event.type = SceneReferenceEventType::SceneFlushed; event.referencedScene = SceneId{ 456 }; event.tag = SceneVersionTag{ 1000 }; - EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, localParticipantID)).WillOnce([&event](SceneReferenceEvent const& e, const Guid&) + EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, localParticipantID)).WillOnce([&event](SceneReferenceEvent const& e, const Guid& /*unused*/) { EXPECT_EQ(e.masterSceneId, event.masterSceneId); EXPECT_EQ(e.type, event.type); @@ -703,14 +702,14 @@ TEST_F(ASceneGraphComponent, unpacksRendererEventToSceneReferenceEventAndForward event.type = SceneReferenceEventType::SceneFlushed; event.referencedScene = SceneId{ 456 }; event.tag = SceneVersionTag{ 1000 }; - EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, localParticipantID)).WillOnce([&event](SceneReferenceEvent const& e, const Guid&) + EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, localParticipantID)).WillOnce([&event](SceneReferenceEvent const& e, const Guid& /*unused*/) { EXPECT_EQ(e.masterSceneId, event.masterSceneId); EXPECT_EQ(e.type, event.type); EXPECT_EQ(e.referencedScene, event.referencedScene); EXPECT_EQ(e.tag, event.tag); }); - std::vector data; + std::vector data; event.writeToBlob(data); sceneGraphComponent.handleRendererEvent(event.masterSceneId, data, remoteParticipantID); } @@ -722,13 +721,13 @@ TEST_F(ASceneGraphComponent, unpacksRendererEventToResourceAvailabilityEventAndF SceneInfo sceneInfo(SceneInfo(SceneId{ 123 }, "foo")); ClientScene scene(sceneInfo); sceneGraphComponent.handleCreateScene(scene, false, eventConsumer); - event.availableResources.push_back(ResourceContentHash(1,2)); - EXPECT_CALL(eventConsumer, handleResourceAvailabilityEvent(_, localParticipantID)).WillOnce([&event](ResourceAvailabilityEvent const& e, const Guid&) + event.availableResources.emplace_back(1, 2); + EXPECT_CALL(eventConsumer, handleResourceAvailabilityEvent(_, localParticipantID)).WillOnce([&event](ResourceAvailabilityEvent const& e, const Guid& /*unused*/) { EXPECT_EQ(e.sceneid, event.sceneid); EXPECT_EQ(e.availableResources, event.availableResources); }); - std::vector data; + std::vector data; event.writeToBlob(data); sceneGraphComponent.handleRendererEvent(event.sceneid, data, remoteParticipantID); } @@ -736,7 +735,7 @@ TEST_F(ASceneGraphComponent, unpacksRendererEventToResourceAvailabilityEventAndF TEST_F(ASceneGraphComponent, doesNotForwardRendererEventIfSceneUnknown) { SceneReferenceEvent event(SceneId{ 123 }); - std::vector data; + std::vector data; event.writeToBlob(data); sceneGraphComponent.handleRendererEvent(event.masterSceneId, data, remoteParticipantID); } @@ -745,7 +744,7 @@ TEST_F(ASceneGraphComponent, doesNotForwardResourceAvailabilityRendererEventIfSc { ResourceAvailabilityEvent event; event.sceneid = SceneId { 123 }; - std::vector data; + std::vector data; event.writeToBlob(data); sceneGraphComponent.handleRendererEvent(event.sceneid, data, remoteParticipantID); } @@ -770,12 +769,12 @@ TEST_F(ASceneGraphComponent, forwardsEventsToCorrectHandler) sceneGraphComponent.sendSceneReferenceEvent(localParticipantID, event2); EXPECT_CALL(eventConsumer, handleSceneReferenceEvent(_, _)).Times(1); - std::vector data1; + std::vector data1; event1.writeToBlob(data1); sceneGraphComponent.handleRendererEvent(event1.masterSceneId, data1, remoteParticipantID); EXPECT_CALL(scene2Consumer, handleSceneReferenceEvent(_, _)).Times(1); - std::vector data2; + std::vector data2; event1.writeToBlob(data2); sceneGraphComponent.handleRendererEvent(event2.masterSceneId, data2, remoteParticipantID); } @@ -792,8 +791,8 @@ TEST_F(ASceneGraphComponent, forwardsPublishFromRemoteToConsumer) sceneGraphComponent.newParticipantHasConnected(Guid(33)); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_33(SceneId(1), "", EScenePublicationMode_LocalAndRemote); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_33(SceneId(1), "", EScenePublicationMode::LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_33, Guid(33))); sceneGraphComponent.handleNewScenesAvailable({info_33}, Guid(33), ramses::EFeatureLevel_Latest); @@ -808,8 +807,8 @@ TEST_F(ASceneGraphComponent, forwardsUnpublishFromRemoteToConsumer) sceneGraphComponent.newParticipantHasConnected(Guid(33)); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_33(SceneId(1), "", EScenePublicationMode_LocalAndRemote); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_33(SceneId(1), "", EScenePublicationMode::LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_33, Guid(33))); sceneGraphComponent.handleNewScenesAvailable({info_33}, Guid(33), ramses::EFeatureLevel_Latest); @@ -827,8 +826,8 @@ TEST_F(ASceneGraphComponent, unpublishesRemoteScenesToConsumerOnParticipantDisco sceneGraphComponent.newParticipantHasConnected(Guid(33)); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_33(SceneId(1), "", EScenePublicationMode_LocalAndRemote); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_33(SceneId(1), "", EScenePublicationMode::LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_33, _)); sceneGraphComponent.handleNewScenesAvailable({info_33}, Guid(33), ramses::EFeatureLevel_Latest); @@ -846,7 +845,7 @@ TEST_F(ASceneGraphComponent, ignoresDuplicatePublishFromOtherRemote) sceneGraphComponent.newParticipantHasConnected(Guid(22)); sceneGraphComponent.newParticipantHasConnected(Guid(33)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -858,7 +857,7 @@ TEST_F(ASceneGraphComponent, unpublishesFirstOnDuplicatePublishFromSameRemote) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -873,7 +872,7 @@ TEST_F(ASceneGraphComponent, ignoresPublishOfMismatchedFeatureLevelSceneFromRemo sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(_, _)).Times(0); sceneGraphComponent.handleNewScenesAvailable({ info_22 }, Guid(22), static_cast(99)); @@ -884,7 +883,7 @@ TEST_F(ASceneGraphComponent, ignoresDuplicateUnpublishFromRemote) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -898,7 +897,7 @@ TEST_F(ASceneGraphComponent, forwardsInitializeSceneToConsumer) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -920,7 +919,7 @@ TEST_F(ASceneGraphComponent, ignoresInitializeSceneFromWrongProvider) sceneGraphComponent.newParticipantHasConnected(Guid(22)); sceneGraphComponent.newParticipantHasConnected(Guid(33)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -932,22 +931,22 @@ TEST_F(ASceneGraphComponent, forwardsSceneActionListFromRemote) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); EXPECT_CALL(consumer, handleInitializeScene(info_22, Guid(22))); sceneGraphComponent.handleInitializeScene(SceneId(2), Guid(22)); - SceneActionCollection actions_1(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + SceneActionCollection actions_1(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { EXPECT_EQ(actions_1, update.actions); }); const auto actionBlobs_1 = actionsToChunks(actions_1); sceneGraphComponent.handleSceneUpdate(SceneId(2), actionBlobs_1[0], Guid(22)); - SceneActionCollection actions_2(createFakeSceneActionCollectionFromTypes({ ESceneActionId::CompoundRenderable })); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + SceneActionCollection actions_2(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::CompoundRenderable })); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { EXPECT_EQ(actions_2, update.actions); }); const auto actionBlobs_2 = actionsToChunks(actions_2); @@ -959,7 +958,7 @@ TEST_F(ASceneGraphComponent, ignoreSceneActionsFromRemoteForUnpublishedScene) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneActionCollection actions_1(createFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); + SceneActionCollection actions_1(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); sceneGraphComponent.handleSceneUpdate(SceneId(2), actionsToChunks(actions_1)[0], Guid(22)); } @@ -968,11 +967,11 @@ TEST_F(ASceneGraphComponent, ignoreSceneActionsFromRemoteWithoutInitializeScene) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); - SceneActionCollection actions_1(createFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); + SceneActionCollection actions_1(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); sceneGraphComponent.handleSceneUpdate(SceneId(2), actionsToChunks(actions_1)[0], Guid(22)); } @@ -982,14 +981,14 @@ TEST_F(ASceneGraphComponent, ignoreSceneActionsFromRemoteFromWrongProvider) sceneGraphComponent.newParticipantHasConnected(Guid(22)); sceneGraphComponent.newParticipantHasConnected(Guid(33)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); EXPECT_CALL(consumer, handleInitializeScene(info_22, Guid(22))); sceneGraphComponent.handleInitializeScene(SceneId(2), Guid(22)); - SceneActionCollection actions_1(createFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); + SceneActionCollection actions_1(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); sceneGraphComponent.handleSceneUpdate(SceneId(2), actionsToChunks(actions_1)[0], Guid(33)); } @@ -998,7 +997,7 @@ TEST_F(ASceneGraphComponent, ignoreSceneActionsFromRemoteWithEmptyData) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -1013,25 +1012,25 @@ TEST_F(ASceneGraphComponent, stopsDeserilizationFromRemotAfterInvalidPacket) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); EXPECT_CALL(consumer, handleInitializeScene(info_22, Guid(22))); sceneGraphComponent.handleInitializeScene(SceneId(2), Guid(22)); - SceneActionCollection actions_1(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + SceneActionCollection actions_1(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { EXPECT_EQ(actions_1, update.actions); }); const auto actionBlobs_1 = actionsToChunks(actions_1); sceneGraphComponent.handleSceneUpdate(SceneId(2), actionBlobs_1[0], Guid(22)); // break it - sceneGraphComponent.handleSceneUpdate(SceneId(2), {0}, Guid(22)); + sceneGraphComponent.handleSceneUpdate(SceneId(2), {std::byte{0}}, Guid(22)); // expect nothing - SceneActionCollection actions_2(createFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); + SceneActionCollection actions_2(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::AllocateNode })); sceneGraphComponent.handleSceneUpdate(SceneId(2), actionsToChunks(actions_2)[0], Guid(22)); } @@ -1040,7 +1039,7 @@ TEST_F(ASceneGraphComponent, brokenActionDeserilizeFromRemoteIsClearOnNextInitia sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -1048,13 +1047,13 @@ TEST_F(ASceneGraphComponent, brokenActionDeserilizeFromRemoteIsClearOnNextInitia sceneGraphComponent.handleInitializeScene(SceneId(2), Guid(22)); // break it - sceneGraphComponent.handleSceneUpdate(SceneId(2), {0}, Guid(22)); + sceneGraphComponent.handleSceneUpdate(SceneId(2), {std::byte{0}}, Guid(22)); EXPECT_CALL(consumer, handleInitializeScene(info_22, Guid(22))); sceneGraphComponent.handleInitializeScene(SceneId(2), Guid(22)); - SceneActionCollection actions_1(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + SceneActionCollection actions_1(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { EXPECT_EQ(actions_1, update.actions); }); const auto actionBlobs_1 = actionsToChunks(actions_1); @@ -1066,7 +1065,7 @@ TEST_F(ASceneGraphComponent, canHandleSceneActionFromRemoteSplitInMultipleChunks sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({info_22}, Guid(22), ramses::EFeatureLevel_Latest); @@ -1080,7 +1079,7 @@ TEST_F(ASceneGraphComponent, canHandleSceneActionFromRemoteSplitInMultipleChunks const auto actionBlobs = actionsToChunks(actions, 80); EXPECT_EQ(2u, actionBlobs.size()); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { EXPECT_EQ(actions, update.actions); }); for (const auto& b : actionBlobs) @@ -1092,7 +1091,7 @@ TEST_F(ASceneGraphComponent, deserializesAndForwardsFlushInformationFromRemote) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({ info_22 }, Guid(22), ramses::EFeatureLevel_Latest); @@ -1113,8 +1112,8 @@ TEST_F(ASceneGraphComponent, deserializesAndForwardsFlushInformationFromRemote) fi.sizeInfo.nodeCount = 555; fi.versionTag = SceneVersionTag{ 9876 }; - SceneActionCollection actions(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + SceneActionCollection actions(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { EXPECT_EQ(actions, update.actions); EXPECT_EQ(update.flushInfos.containsValidInformation, true); EXPECT_EQ(update.flushInfos.flushCounter, 666); @@ -1142,7 +1141,7 @@ TEST_F(ASceneGraphComponent, deserializesResourcesFromRemote) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({ info_22 }, Guid(22), ramses::EFeatureLevel_Latest); @@ -1154,15 +1153,15 @@ TEST_F(ASceneGraphComponent, deserializesResourcesFromRemote) const float data3 = 0.1f; ManagedResourceVector resources{ - std::make_shared(EResourceType_IndexArray, 1u, EDataType::UInt16, &data1, ResourceCacheFlag_DoNotCache, "ui16"), - std::make_shared(EResourceType_IndexArray, 1u, EDataType::UInt32, &data2, ResourceCacheFlag_DoNotCache, "ui32"), - std::make_shared(EResourceType_VertexArray, 1u, EDataType::Float, &data3, ResourceCacheFlag_DoNotCache, "fl") + std::make_shared(EResourceType::IndexArray, 1u, EDataType::UInt16, &data1, "ui16"), + std::make_shared(EResourceType::IndexArray, 1u, EDataType::UInt32, &data2, "ui32"), + std::make_shared(EResourceType::VertexArray, 1u, EDataType::Float, &data3, "fl") }; - SceneActionCollection actions(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + SceneActionCollection actions(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(resourceComponent, manageResource(_, false)).WillRepeatedly([&](const IResource& res, bool) { return ManagedResource(&res); }); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + EXPECT_CALL(resourceComponent, manageResource(_)).WillRepeatedly([&](const IResource& res) { return ManagedResource(&res); }); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { ASSERT_EQ(update.resources.size(), 3u); EXPECT_EQ(update.resources[0]->getTypeID(), resources[0]->getTypeID()); EXPECT_EQ(update.resources[0]->getHash(), resources[0]->getHash()); @@ -1181,7 +1180,7 @@ TEST_F(ASceneGraphComponent, deserializesResourcesFromRemote_SmallChunks) sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.newParticipantHasConnected(Guid(22)); - SceneInfo info_22(SceneId(2), "", EScenePublicationMode_LocalAndRemote); + SceneInfo info_22(SceneId(2), "", EScenePublicationMode::LocalAndRemote); EXPECT_CALL(consumer, handleNewSceneAvailable(info_22, Guid(22))); sceneGraphComponent.handleNewScenesAvailable({ info_22 }, Guid(22), ramses::EFeatureLevel_Latest); @@ -1193,15 +1192,15 @@ TEST_F(ASceneGraphComponent, deserializesResourcesFromRemote_SmallChunks) const float data3[] = { 0.1f, 0.2f, 0.3f, 0.4f }; ManagedResourceVector resources{ - std::make_shared(EResourceType_IndexArray, 4u, EDataType::UInt16, &data1, ResourceCacheFlag_DoNotCache, "ui16"), - std::make_shared(EResourceType_IndexArray, 4u, EDataType::UInt32, &data2, ResourceCacheFlag_DoNotCache, "ui32"), - std::make_shared(EResourceType_VertexArray, 4u, EDataType::Float, &data3, ResourceCacheFlag_DoNotCache, "fl") + std::make_shared(EResourceType::IndexArray, 4u, EDataType::UInt16, &data1, "ui16"), + std::make_shared(EResourceType::IndexArray, 4u, EDataType::UInt32, &data2, "ui32"), + std::make_shared(EResourceType::VertexArray, 4u, EDataType::Float, &data3, "fl") }; - SceneActionCollection actions(createFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); + SceneActionCollection actions(CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::TestAction })); - EXPECT_CALL(resourceComponent, manageResource(_, false)).WillRepeatedly([&](const IResource& res, bool) { return ManagedResource(&res); }); - EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto&, const auto& update, const auto&) { + EXPECT_CALL(resourceComponent, manageResource(_)).WillRepeatedly([&](const IResource& res) { return ManagedResource(&res); }); + EXPECT_CALL(consumer, handleSceneUpdate_rvr(SceneId(2), _, Guid(22))).WillOnce([&](const auto& /*unused*/, const auto& update, const auto& /*unused*/) { ASSERT_EQ(update.resources.size(), 3u); EXPECT_EQ(update.resources[0]->getTypeID(), resources[0]->getTypeID()); EXPECT_EQ(update.resources[0]->getHash(), resources[0]->getHash()); @@ -1226,19 +1225,19 @@ TEST_F(ASceneGraphComponent, returnsFalseForFlushOnWrongResolvedResourceNumber_S sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.handleCreateScene(scene, false, eventConsumer); - auto res = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {}); + auto res = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {}); res->setResourceData(ResourceBlob{ 1 }, { 1u, 1u }); ManagedResource manRes(res); ResourceInfo info(res); - scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, { 111, 111 }, {} }); + scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, {111, 111}, {}}, {}); EXPECT_CALL(resourceComponent, knowsResource(_)).WillOnce(Return(true)); EXPECT_CALL(resourceComponent, resolveResources(_)).Times(2).WillRepeatedly(Return(ManagedResourceVector{ manRes })); EXPECT_CALL(resourceComponent, getResourceInfo(_)).WillOnce(ReturnRef(info)); EXPECT_TRUE(sceneGraphComponent.handleFlush(sceneId, {}, {})); - scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, { 222, 222 }, {} }); + scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, {222, 222}, {}}, {}); EXPECT_CALL(resourceComponent, resolveResources(_)).WillOnce(Return(ManagedResourceVector{})); EXPECT_FALSE(sceneGraphComponent.handleFlush(sceneId, {}, {})); } @@ -1252,18 +1251,18 @@ TEST_F(ASceneGraphComponent, returnsFalseForFlushOnWrongResolvedResourceNumber_D sceneGraphComponent.setSceneRendererHandler(&consumer); sceneGraphComponent.handleCreateScene(scene, true, eventConsumer); - auto res = new TextureResource(EResourceType_Texture2D, TextureMetaInfo(1u, 1u, 1u, ETextureFormat::R8, false, {}, { 1u }), ResourceCacheFlag_DoNotCache, {}); + auto res = new TextureResource(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), {}); res->setResourceData(ResourceBlob{ 1 }, { 1u, 1u }); ManagedResource manRes(res); ResourceInfo info(res); - scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, { 111, 111 }, {} }); + scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, {111, 111}, {}}, {}); EXPECT_CALL(resourceComponent, knowsResource(_)).WillOnce(Return(true)); EXPECT_CALL(resourceComponent, resolveResources(_)).Times(1).WillRepeatedly(Return(ManagedResourceVector{ manRes })); EXPECT_CALL(resourceComponent, getResourceInfo(_)).WillOnce(ReturnRef(info)); EXPECT_TRUE(sceneGraphComponent.handleFlush(sceneId, {}, {})); - scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), {}, {}, { 222, 222 }, {} }); + scene.allocateDataSlot({EDataSlotType::TextureProvider, DataSlotId(0u), {}, {}, {222, 222}, {}}, {}); EXPECT_CALL(resourceComponent, resolveResources(_)).WillOnce(Return(ManagedResourceVector{})); EXPECT_FALSE(sceneGraphComponent.handleFlush(sceneId, {}, {})); } @@ -1272,14 +1271,15 @@ TEST_F(ASceneGraphComponent, sendSceneUpdatePassesSceneStatisticsToSerializer) { SceneId sceneId; EXPECT_CALL(communicationSystem, sendInitializeScene(_, _)).Times(1); - sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode_LocalAndRemote); + sceneGraphComponent.sendCreateScene(remoteParticipantID, sceneId, EScenePublicationMode::LocalAndRemote); - EXPECT_CALL(communicationSystem, sendSceneUpdate(remoteParticipantID, sceneId, _)).WillOnce([&](auto, auto, auto& serializer) { + EXPECT_CALL(communicationSystem, sendSceneUpdate(remoteParticipantID, sceneId, _)).WillOnce([&](auto /*unused*/, auto /*unused*/, auto& serializer) { const auto& stats = static_cast(serializer).getStatisticCollection(); EXPECT_EQ(&sceneStatistics, &stats); return true; }); SceneUpdate update; - sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode_LocalAndRemote, sceneStatistics); + sceneGraphComponent.sendSceneUpdate({ remoteParticipantID }, std::move(update), sceneId, EScenePublicationMode::LocalAndRemote, sceneStatistics); +} } diff --git a/framework/Components/test/SceneGraphProtocolSenderAndReceiverTest.cpp b/tests/unittests/framework/Components/SceneGraphProtocolSenderAndReceiverTest.cpp similarity index 85% rename from framework/Components/test/SceneGraphProtocolSenderAndReceiverTest.cpp rename to tests/unittests/framework/Components/SceneGraphProtocolSenderAndReceiverTest.cpp index 57e105e96..34561ec8a 100644 --- a/framework/Components/test/SceneGraphProtocolSenderAndReceiverTest.cpp +++ b/tests/unittests/framework/Components/SceneGraphProtocolSenderAndReceiverTest.cpp @@ -8,15 +8,21 @@ #include "AbstractSenderAndReceiverTest.h" #include "ServiceHandlerMocks.h" -#include "SceneAPI/SceneTypes.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include "SceneUpdateSerializerTestHelper.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; + template + std::vector make_byte_vector(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + class ASceneGraphProtocolSenderAndReceiverTest : public AbstractSenderAndReceiverTest { public: @@ -43,9 +49,9 @@ namespace ramses_internal { PlatformGuard g(receiverExpectCallLock); - EXPECT_CALL(consumerHandler, handleNewScenesAvailable(newScenes, senderId, ramses::EFeatureLevel_Latest)).WillOnce(InvokeWithoutArgs([&]{ sendEvent(); })); + EXPECT_CALL(consumerHandler, handleNewScenesAvailable(newScenes, senderId, EFeatureLevel_Latest)).WillOnce(InvokeWithoutArgs([&]{ sendEvent(); })); } - EXPECT_TRUE(sender.broadcastNewScenesAvailable(newScenes, ramses::EFeatureLevel_Latest)); + EXPECT_TRUE(sender.broadcastNewScenesAvailable(newScenes, EFeatureLevel_Latest)); ASSERT_TRUE(waitForEvent()); } @@ -91,9 +97,9 @@ namespace ramses_internal { PlatformGuard g(receiverExpectCallLock); - EXPECT_CALL(consumerHandler, handleNewScenesAvailable(newScenes, senderId, ramses::EFeatureLevel_Latest)).WillOnce(InvokeWithoutArgs([&]{ sendEvent(); })); + EXPECT_CALL(consumerHandler, handleNewScenesAvailable(newScenes, senderId, EFeatureLevel_Latest)).WillOnce(InvokeWithoutArgs([&]{ sendEvent(); })); } - EXPECT_TRUE(sender.sendScenesAvailable(receiverId, newScenes, ramses::EFeatureLevel_Latest)); + EXPECT_TRUE(sender.sendScenesAvailable(receiverId, newScenes, EFeatureLevel_Latest)); ASSERT_TRUE(waitForEvent()); } @@ -122,7 +128,7 @@ namespace ramses_internal TEST_P(ASceneGraphProtocolSenderAndReceiverTest, sendRendererEvent) { SceneId sceneId{432}; - std::vector data{7, 1, 2, 3, 4, 5, 6}; + std::vector data = make_byte_vector(7, 1, 2, 3, 4, 5, 6); { PlatformGuard g(receiverExpectCallLock); EXPECT_CALL(providerHandler, handleRendererEvent(sceneId, data, senderId)).WillOnce(InvokeWithoutArgs([&] { sendEvent(); })); @@ -140,7 +146,7 @@ namespace ramses_internal TEST_P(ASceneGraphProtocolSenderAndReceiverTest, sendRendererEventFailsForTooLargeData) { - std::vector data(35000); + std::vector data(35000); EXPECT_FALSE(sender.sendRendererEvent(receiverId, SceneId(111), data)); skipStatisticsTest(); } @@ -150,17 +156,17 @@ namespace ramses_internal skipStatisticsTest(); const SceneId sceneId{432}; - const std::vector blob_1({1, 2, 3, 4, 5, 6}); - const std::vector blob_2({255, 254, 11, 3}); + const std::vector blob_1 = make_byte_vector(1, 2, 3, 4, 5, 6); + const std::vector blob_2 = make_byte_vector(255, 254, 11, 3); { PlatformGuard g(receiverExpectCallLock); InSequence seq; - EXPECT_CALL(consumerHandler, handleSceneUpdate(sceneId, _, senderId)).WillOnce([&](auto, const auto& data, auto) { + EXPECT_CALL(consumerHandler, handleSceneUpdate(sceneId, _, senderId)).WillOnce([&](auto /*unused*/, const auto& data, auto /*unused*/) { EXPECT_EQ(blob_1, data); sendEvent(); }); - EXPECT_CALL(consumerHandler, handleSceneUpdate(sceneId, _, senderId)).WillOnce([&](auto, const auto& data, auto) { + EXPECT_CALL(consumerHandler, handleSceneUpdate(sceneId, _, senderId)).WillOnce([&](auto /*unused*/, const auto& data, auto /*unused*/) { EXPECT_EQ(blob_2, data); sendEvent(); }); diff --git a/framework/Components/test/SingleResourceSerializationTest.cpp b/tests/unittests/framework/Components/SingleResourceSerializationTest.cpp similarity index 94% rename from framework/Components/test/SingleResourceSerializationTest.cpp rename to tests/unittests/framework/Components/SingleResourceSerializationTest.cpp index d2a41f777..df99edcee 100644 --- a/framework/Components/test/SingleResourceSerializationTest.cpp +++ b/tests/unittests/framework/Components/SingleResourceSerializationTest.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Components/SingleResourceSerialization.h" +#include "internal/Components/SingleResourceSerialization.h" #include "gtest/gtest.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" #include "ResourceSerializationTestHelper.h" #include -namespace ramses_internal +namespace ramses::internal { namespace { diff --git a/framework/Core/Common/test/ParticipantIdentifierTest.cpp b/tests/unittests/framework/Core/Common/ParticipantIdentifierTest.cpp similarity index 92% rename from framework/Core/Common/test/ParticipantIdentifierTest.cpp rename to tests/unittests/framework/Core/Common/ParticipantIdentifierTest.cpp index 3c56667f7..b98635a02 100644 --- a/framework/Core/Common/test/ParticipantIdentifierTest.cpp +++ b/tests/unittests/framework/Core/Common/ParticipantIdentifierTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Common/ParticipantIdentifier.h" +#include "internal/Core/Common/ParticipantIdentifier.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TEST(ParticipantIdentifier, Constructor) { diff --git a/tests/unittests/framework/Core/Math3d/CameraMatrixHelperTest.cpp b/tests/unittests/framework/Core/Math3d/CameraMatrixHelperTest.cpp new file mode 100644 index 000000000..7795be254 --- /dev/null +++ b/tests/unittests/framework/Core/Math3d/CameraMatrixHelperTest.cpp @@ -0,0 +1,87 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2013 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" + +namespace ramses::internal +{ + TEST(ACameraMatrixHelper, ComputesProjectionMatrix) + { + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(30.0f, 2.0f, 0.1f, 10.0f)); + // Don't use matrix comparison operator here, but compare exact floats + // Rationale: tests should be explicit, should not use internal behaviour of matrix class + EXPECT_FLOAT_EQ(1.8660254f, projMatrix[0][0]); + EXPECT_FLOAT_EQ(0, projMatrix[1][0]); + EXPECT_FLOAT_EQ(0, projMatrix[2][0]); + EXPECT_FLOAT_EQ(0, projMatrix[3][0]); + EXPECT_FLOAT_EQ(0, projMatrix[0][1]); + EXPECT_FLOAT_EQ(3.73205f, projMatrix[1][1]); + EXPECT_FLOAT_EQ(0, projMatrix[2][1]); + EXPECT_FLOAT_EQ(0, projMatrix[3][1]); + EXPECT_FLOAT_EQ(0, projMatrix[0][2]); + EXPECT_FLOAT_EQ(0, projMatrix[1][2]); + EXPECT_FLOAT_EQ(-1.020202f, projMatrix[2][2]); + EXPECT_FLOAT_EQ(-0.20202021f, projMatrix[3][2]); + EXPECT_FLOAT_EQ(0, projMatrix[0][3]); + EXPECT_FLOAT_EQ(0, projMatrix[1][3]); + EXPECT_FLOAT_EQ(-1, projMatrix[2][3]); + EXPECT_FLOAT_EQ(0, projMatrix[3][3]); + } + + TEST(ACameraMatrixHelper, ComputesOrthographicMatrix) + { + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Frustum(ECameraProjectionType::Orthographic, 30, 40, 50, 60, 10, 20)); + // Don't use matrix comparison operator here, but compare exact floats + // Rationale: tests should be explicit, should not use internal behaviour of matrix class + EXPECT_FLOAT_EQ(0.2f, projMatrix[0][0]); + EXPECT_FLOAT_EQ(0, projMatrix[1][0]); + EXPECT_FLOAT_EQ(0, projMatrix[2][0]); + EXPECT_FLOAT_EQ(-7.0f, projMatrix[3][0]); + EXPECT_FLOAT_EQ(0, projMatrix[0][1]); + EXPECT_FLOAT_EQ(0.2f, projMatrix[1][1]); + EXPECT_FLOAT_EQ(0, projMatrix[2][1]); + EXPECT_FLOAT_EQ(-11.0f, projMatrix[3][1]); + EXPECT_FLOAT_EQ(0, projMatrix[0][2]); + EXPECT_FLOAT_EQ(0, projMatrix[1][2]); + EXPECT_FLOAT_EQ(-0.2f, projMatrix[2][2]); + EXPECT_FLOAT_EQ(-3.0f, projMatrix[3][2]); + EXPECT_FLOAT_EQ(0, projMatrix[0][3]); + EXPECT_FLOAT_EQ(0, projMatrix[1][3]); + EXPECT_FLOAT_EQ(0, projMatrix[2][3]); + EXPECT_FLOAT_EQ(1, projMatrix[3][3]); + } + + // Confidence test (above tests already test enough) + TEST(APerspectiveProjectionMatrix, ProjectsPointsCloserToViewerFartherFromCenterInNormalizedDeviceSpace) + { + const auto perpectivematrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(30.0f, 2.666f, 0.00001f, 100.f)); + // In right handed coordinate systems camera view ray is pointing towards -1, thus bigger negative Z means farther away from viewer + glm::vec4 vecClose(1.0f, 12.0f, -50.0f, 1.0f); + glm::vec4 vecFar(1.0f, 12.0f, -100.0f, 1.0f); + + glm::vec4 resultClose = perpectivematrix * vecClose; + glm::vec4 resultFar = perpectivematrix * vecFar; + EXPECT_GT(resultClose.x / resultClose.w, resultFar.x / resultFar.w); + EXPECT_GT(resultClose.y / resultClose.w, resultFar.y / resultFar.w); + } + + // Confidence test (above tests already test enough) + TEST(AOrthographicProjectionMatrix, ProjectsPointsWithDifferentDepthToTheSamePixelInNormalizedDeviceSpace) + { + const auto orthographicMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Frustum(ECameraProjectionType::Orthographic, + -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 100.0f)); + glm::vec4 vecA(1.0f, 2.0f, 0.0f, 1.f); + glm::vec4 vecB(1.0f, 2.0f, -50.0f, 1.f); + + glm::vec4 resultA = orthographicMatrix* vecA; + glm::vec4 resultB = orthographicMatrix* vecB; + EXPECT_FLOAT_EQ(resultA.x / resultA.w, resultB.x / resultB.w); + EXPECT_FLOAT_EQ(resultA.y / resultA.w, resultB.y / resultB.w); + } +} diff --git a/framework/Core/Math3d/test/ProjectionParamsTest.cpp b/tests/unittests/framework/Core/Math3d/ProjectionParamsTest.cpp similarity index 98% rename from framework/Core/Math3d/test/ProjectionParamsTest.cpp rename to tests/unittests/framework/Core/Math3d/ProjectionParamsTest.cpp index e2f7b2adc..4978818fb 100644 --- a/framework/Core/Math3d/test/ProjectionParamsTest.cpp +++ b/tests/unittests/framework/Core/Math3d/ProjectionParamsTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Math3d/ProjectionParams.h" -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/Core/Math3d/ProjectionParams.h" +#include "internal/PlatformAbstraction/PlatformMath.h" #include -namespace ramses_internal +namespace ramses::internal { class AProjectionParams : public testing::Test { diff --git a/framework/Core/Math3d/test/QuadTest.cpp b/tests/unittests/framework/Core/Math3d/QuadTest.cpp similarity index 97% rename from framework/Core/Math3d/test/QuadTest.cpp rename to tests/unittests/framework/Core/Math3d/QuadTest.cpp index 62ab3c218..f8e1a4b46 100644 --- a/framework/Core/Math3d/test/QuadTest.cpp +++ b/tests/unittests/framework/Core/Math3d/QuadTest.cpp @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" -#include "Math3d/Quad.h" +#include "internal/Core/Math3d/Quad.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { TEST(QuadTest, CanCreateQuad_DefaultConstructor) { diff --git a/framework/Core/Math3d/test/RotationTest.cpp b/tests/unittests/framework/Core/Math3d/RotationTest.cpp similarity index 94% rename from framework/Core/Math3d/test/RotationTest.cpp rename to tests/unittests/framework/Core/Math3d/RotationTest.cpp index b382922f2..852169487 100644 --- a/framework/Core/Math3d/test/RotationTest.cpp +++ b/tests/unittests/framework/Core/Math3d/RotationTest.cpp @@ -9,22 +9,21 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" #include "TestEqualHelper.h" -#include "Math3d/Rotation.h" -#include "Collections/StringOutputStream.h" +#include "internal/Core/Math3d/Rotation.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" #include "TestEqualHelper.h" #include #include -#include "framework_common_gmock_header.h" #include "glm/gtx/transform.hpp" #include "glm/gtx/euler_angles.hpp" #include "glm/ext.hpp" -namespace ramses_internal +namespace ramses::internal { class RotationEulerTest : public ::testing::TestWithParam { protected: - glm::mat4 calculateRotationMatrixAroundAxis(uint8_t axis, float angle) + static glm::mat4 CalculateRotationMatrixAroundAxis(uint8_t axis, float angle) { //Note: it does not matter which convention is used, since rotation around single axis //would result in same matrix using any convention @@ -36,6 +35,8 @@ namespace ramses_internal return glm::eulerAngleY(glm::radians(angle)); case 2: //z-axis return glm::eulerAngleZ(glm::radians(angle)); + default: + break; } assert(false); @@ -93,9 +94,9 @@ namespace ramses_internal const auto& rotationAxes = conventionTest.axesRepresentedByInputAngles; const auto& matrixOrder = conventionTest.matrixMultiplicationOrder; - const auto rotA0 = calculateRotationMatrixAroundAxis(rotationAxes[0], rotationAngle0); - const auto rotA1 = calculateRotationMatrixAroundAxis(rotationAxes[1], rotationAngle1); - const auto rotA2 = calculateRotationMatrixAroundAxis(rotationAxes[2], rotationAngle2); + const auto rotA0 = CalculateRotationMatrixAroundAxis(rotationAxes[0], rotationAngle0); + const auto rotA1 = CalculateRotationMatrixAroundAxis(rotationAxes[1], rotationAngle1); + const auto rotA2 = CalculateRotationMatrixAroundAxis(rotationAxes[2], rotationAngle2); const std::array rotMatrices = { rotA0, rotA1, rotA2 }; diff --git a/framework/Core/TaskFramework/test/MockITask.h b/tests/unittests/framework/Core/TaskFramework/test/MockITask.h similarity index 80% rename from framework/Core/TaskFramework/test/MockITask.h rename to tests/unittests/framework/Core/TaskFramework/test/MockITask.h index 977dfc0d8..05992d139 100644 --- a/framework/Core/TaskFramework/test/MockITask.h +++ b/tests/unittests/framework/Core/TaskFramework/test/MockITask.h @@ -6,28 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MOCKITASK_H -#define RAMSES_MOCKITASK_H +#pragma once -#include "framework_common_gmock_header.h" -#include "TaskFramework/ITask.h" +#include "internal/Core/TaskFramework/ITask.h" -namespace ramses_internal +namespace ramses::internal { class TaskHelper { public: - TaskHelper() - : m_Task(nullptr) - { - } - bool enqueue(ITask& Task) { m_Task = &Task; m_Task->addRef(); return true; } + void execute() { assert(m_Task); @@ -35,7 +29,7 @@ namespace ramses_internal m_Task->release(); } private: - ITask* m_Task; + ITask* m_Task{nullptr}; }; class MockITask : public ITask @@ -50,5 +44,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/Core/TaskFramework/test/MockTaskFinishHandler.h b/tests/unittests/framework/Core/TaskFramework/test/MockTaskFinishHandler.h similarity index 76% rename from framework/Core/TaskFramework/test/MockTaskFinishHandler.h rename to tests/unittests/framework/Core/TaskFramework/test/MockTaskFinishHandler.h index 6cab3ad44..8490d9b22 100644 --- a/framework/Core/TaskFramework/test/MockTaskFinishHandler.h +++ b/tests/unittests/framework/Core/TaskFramework/test/MockTaskFinishHandler.h @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MOCKTASKFINISHHANDLER_H -#define RAMSES_MOCKTASKFINISHHANDLER_H +#pragma once -#include "framework_common_gmock_header.h" -#include "TaskFramework/ITaskFinishHandler.h" +#include "internal/Core/TaskFramework/ITaskFinishHandler.h" -namespace ramses_internal +namespace ramses::internal { class MockTaskFinishHandler : public ITaskFinishHandler { @@ -21,5 +19,3 @@ namespace ramses_internal MOCK_METHOD(void, TaskFinished, (ITask& Task), (override)); }; } - -#endif diff --git a/framework/Core/TaskFramework/test/MockTaskQueue.h b/tests/unittests/framework/Core/TaskFramework/test/MockTaskQueue.h similarity index 78% rename from framework/Core/TaskFramework/test/MockTaskQueue.h rename to tests/unittests/framework/Core/TaskFramework/test/MockTaskQueue.h index b610ed597..9bf70bfd1 100644 --- a/framework/Core/TaskFramework/test/MockTaskQueue.h +++ b/tests/unittests/framework/Core/TaskFramework/test/MockTaskQueue.h @@ -6,15 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_MOCKTASKQUEUE_H -#define RAMSES_MOCKTASKQUEUE_H +#pragma once -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "TaskFramework/ITask.h" -#include "TaskFramework/ITaskQueue.h" +#include "internal/Core/TaskFramework/ITask.h" +#include "internal/Core/TaskFramework/ITaskQueue.h" -namespace ramses_internal +namespace ramses::internal { class MockTaskQueue : public ITaskQueue { @@ -24,5 +22,3 @@ namespace ramses_internal MOCK_METHOD(void, disableAcceptingTasksAfterExecutingCurrentQueue, (), (override)); }; } - -#endif diff --git a/framework/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp b/tests/unittests/framework/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp similarity index 94% rename from framework/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp rename to tests/unittests/framework/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp index 936d09dbb..80e63fc92 100644 --- a/framework/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp +++ b/tests/unittests/framework/Core/TaskFramework/test/ProcessingTaskQueueTest.cpp @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/ProcessingTaskQueue.h" -#include "framework_common_gmock_header.h" +#include "internal/Core/TaskFramework/ProcessingTaskQueue.h" #include "gmock/gmock.h" -#include "PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/PlatformTime.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class MockTask : public ITask { diff --git a/framework/Core/TaskFramework/test/TaskExecutingThreadTest.cpp b/tests/unittests/framework/Core/TaskFramework/test/TaskExecutingThreadTest.cpp similarity index 93% rename from framework/Core/TaskFramework/test/TaskExecutingThreadTest.cpp rename to tests/unittests/framework/Core/TaskFramework/test/TaskExecutingThreadTest.cpp index 1c54e3dd3..edc594b23 100644 --- a/framework/Core/TaskFramework/test/TaskExecutingThreadTest.cpp +++ b/tests/unittests/framework/Core/TaskFramework/test/TaskExecutingThreadTest.cpp @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformTypes.h" -#include "TaskFramework/TaskExecutingThread.h" -#include "framework_common_gmock_header.h" +#include "internal/Core/TaskFramework/TaskExecutingThread.h" #include "gmock/gmock.h" -#include "TaskFramework/ProcessingTaskQueue.h" -#include "PlatformAbstraction/PlatformEvent.h" +#include "internal/Core/TaskFramework/ProcessingTaskQueue.h" +#include "internal/PlatformAbstraction/PlatformEvent.h" #include "Watchdog/ThreadAliveNotifierMock.h" +#include #include #include using namespace testing; using namespace std::chrono_literals; -namespace ramses_internal +namespace ramses::internal { TEST(ATaskExecutingThread, callsAliveNotificationWithGivenThreadID) { diff --git a/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp b/tests/unittests/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp similarity index 89% rename from framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp rename to tests/unittests/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp index 11a0d0a43..4123547a1 100644 --- a/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp +++ b/tests/unittests/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.cpp @@ -8,13 +8,13 @@ #include "TaskFinishHandlerDecoratorTest.h" #include "gmock/gmock.h" -#include "TaskFramework/TaskFinishHandlerDecorator.h" +#include "internal/Core/TaskFramework/TaskFinishHandlerDecorator.h" #include "MockTaskFinishHandler.h" #include "MockITask.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TEST_F(TaskFinishHandlerDecoratorTest, FinisHandlerCalledAfterExecute) { @@ -44,7 +44,7 @@ namespace ramses_internal { MockTaskFinishHandler finishHandler; MockITask Task; - TaskFinishHandlerDecorator* decorator = new TaskFinishHandlerDecorator(finishHandler, Task); + auto* decorator = new TaskFinishHandlerDecorator(finishHandler, Task); delete decorator; EXPECT_EQ(1U, Task.getReferenceCount()); diff --git a/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h b/tests/unittests/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h similarity index 77% rename from framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h rename to tests/unittests/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h index c3a8ae630..a65c0f9eb 100644 --- a/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h +++ b/tests/unittests/framework/Core/TaskFramework/test/TaskFinishHandlerDecoratorTest.h @@ -6,18 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TASKFINISHHANDLERDECORATORTEST_H -#define RAMSES_TASKFINISHHANDLERDECORATORTEST_H +#pragma once -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class TaskFinishHandlerDecoratorTest : public ::testing::Test { public: }; } - -#endif diff --git a/framework/Core/TaskFramework/test/TaskForwardingQueueTest.cpp b/tests/unittests/framework/Core/TaskFramework/test/TaskForwardingQueueTest.cpp similarity index 93% rename from framework/Core/TaskFramework/test/TaskForwardingQueueTest.cpp rename to tests/unittests/framework/Core/TaskFramework/test/TaskForwardingQueueTest.cpp index e037ce3ed..9e8fb32d6 100644 --- a/framework/Core/TaskFramework/test/TaskForwardingQueueTest.cpp +++ b/tests/unittests/framework/Core/TaskFramework/test/TaskForwardingQueueTest.cpp @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "TaskFramework/EnqueueOnlyOneAtATimeQueue.h" -#include "TaskFramework/TaskForwardingQueue.h" +#include "internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h" +#include "internal/Core/TaskFramework/TaskForwardingQueue.h" #include "MockTaskQueue.h" #include "MockITask.h" -#include "TaskFramework/ITask.h" -#include "Collections/Vector.h" +#include "internal/Core/TaskFramework/ITask.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include #include #include -namespace ramses_internal +namespace ramses::internal { using namespace testing; diff --git a/framework/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp b/tests/unittests/framework/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp similarity index 82% rename from framework/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp rename to tests/unittests/framework/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp index ddeffbc33..97ea18ef4 100644 --- a/framework/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp +++ b/tests/unittests/framework/Core/TaskFramework/test/ThreadedTaskExecutorTest.cpp @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TaskFramework/ThreadedTaskExecutor.h" -#include "framework_common_gmock_header.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "TaskFramework/EnqueueOnlyOneAtATimeQueue.h" +#include "internal/Core/TaskFramework/ThreadedTaskExecutor.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/TaskFramework/EnqueueOnlyOneAtATimeQueue.h" #include "ThreadWatchdogConfig.h" #include "PlatformWatchDogMock.h" -#include "PlatformAbstraction/PlatformEvent.h" -#include "Utils/ThreadBarrier.h" +#include "internal/PlatformAbstraction/PlatformEvent.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include #include using namespace testing; -namespace ramses_internal +namespace ramses::internal { class TaskMock : public ITask { @@ -73,19 +72,19 @@ namespace ramses_internal { PlatformEvent syncWaiter; PlatformWatchdogMockCallback mockCallback; - EXPECT_CALL(mockCallback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(mockCallback, registerThread(ERamsesThreadIdentifier::Workers)); ThreadWatchdogConfig config; config.setThreadWatchDogCallback(&mockCallback); - config.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Workers, 100); + config.setWatchdogNotificationInterval(ERamsesThreadIdentifier::Workers, 100); - EXPECT_CALL(mockCallback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)) + EXPECT_CALL(mockCallback, notifyThread(ERamsesThreadIdentifier::Workers)) .Times(AtLeast(1)) .WillRepeatedly(InvokeWithoutArgs([&]() { syncWaiter.signal(); })); ThreadedTaskExecutor ex(2, config); EXPECT_TRUE(syncWaiter.wait(2000)); - EXPECT_CALL(mockCallback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(mockCallback, unregisterThread(ERamsesThreadIdentifier::Workers)); } TEST(AThreadedTaskExecutor, doesNotReportToWatchDogIfOneThreadIsBlocked) @@ -99,7 +98,7 @@ namespace ramses_internal ON_CALL(fastTask, execute()).WillByDefault([&]() { fastTasksCounter++; }); ThreadWatchdogConfig config; - config.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Workers, 0); // no time limitations - always notify + config.setWatchdogNotificationInterval(ERamsesThreadIdentifier::Workers, 0); // no time limitations - always notify config.setThreadWatchDogCallback(&mockCallback); TaskMock blockingTask; ThreadedTaskExecutor taskSystem(2, config); @@ -138,7 +137,7 @@ namespace ramses_internal TEST(ThreadedTaskExecutor, destructorWaitsForUnfinishedTasks) { - ThreadedTaskExecutor* ex = new ThreadedTaskExecutor(16); + auto* ex = new ThreadedTaskExecutor(16); NiceMock task; @@ -153,12 +152,12 @@ namespace ramses_internal { PlatformEvent syncWaiter; PlatformWatchdogMockCallback mockCallback; - EXPECT_CALL(mockCallback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(mockCallback, registerThread(ERamsesThreadIdentifier::Workers)); ThreadWatchdogConfig config; config.setThreadWatchDogCallback(&mockCallback); - config.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Workers, 10000); + config.setWatchdogNotificationInterval(ERamsesThreadIdentifier::Workers, 10000); - EXPECT_CALL(mockCallback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)) + EXPECT_CALL(mockCallback, notifyThread(ERamsesThreadIdentifier::Workers)) .Times(AtLeast(1)) .WillRepeatedly(InvokeWithoutArgs([&]() { syncWaiter.signal(); })); ThreadedTaskExecutor ex(2, config); @@ -169,15 +168,15 @@ namespace ramses_internal ex.stop(); }); - EXPECT_TRUE(syncWaiter.wait(config.getWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Workers))); + EXPECT_TRUE(syncWaiter.wait(config.getWatchdogNotificationInterval(ERamsesThreadIdentifier::Workers))); - EXPECT_CALL(mockCallback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(mockCallback, unregisterThread(ERamsesThreadIdentifier::Workers)); t.join(); } TEST(AThreadedTaskExecutor, stoppingReleasesUnhandledTasks) { - ThreadedTaskExecutor* ex = new ThreadedTaskExecutor(1); + auto* ex = new ThreadedTaskExecutor(1); PlatformLock taskBlockingLock; taskBlockingLock.lock(); diff --git a/framework/Core/Utils/test/BinaryFileInputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/BinaryFileInputStreamTest.cpp similarity index 92% rename from framework/Core/Utils/test/BinaryFileInputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/BinaryFileInputStreamTest.cpp index 3517490d6..95dbbbac9 100644 --- a/framework/Core/Utils/test/BinaryFileInputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/BinaryFileInputStreamTest.cpp @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/BinaryFileInputStream.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" -namespace ramses_internal +namespace ramses::internal { class BinaryFileInputStreamTest : public testing::Test { @@ -23,7 +22,7 @@ namespace ramses_internal int32_t intVal = 10; float floatVal = 20.f; std::string stringVal = "Dies ist ein Text"; - uint32_t strlen = static_cast(stringVal.size()); + auto strlen = static_cast(stringVal.size()); EXPECT_TRUE(m_file.write(&intVal, sizeof(int32_t))); EXPECT_TRUE(m_file.write(&floatVal, sizeof(float))); @@ -77,7 +76,7 @@ namespace ramses_internal File file("some/non/existing/path"); BinaryFileInputStream inputStream(file); - char data; + char data = 0; int size = 1; inputStream.read(&data, size); diff --git a/framework/Core/Utils/test/BinaryFileOutputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/BinaryFileOutputStreamTest.cpp similarity index 95% rename from framework/Core/Utils/test/BinaryFileOutputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/BinaryFileOutputStreamTest.cpp index 6330469f4..bba70de09 100644 --- a/framework/Core/Utils/test/BinaryFileOutputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/BinaryFileOutputStreamTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/BinaryFileOutputStream.h" +#include "internal/Core/Utils/BinaryFileOutputStream.h" #include -namespace ramses_internal +namespace ramses::internal { class BinaryFileOutputStreamTest : public testing::Test { diff --git a/framework/Core/Utils/test/BinaryInputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/BinaryInputStreamTest.cpp similarity index 80% rename from framework/Core/Utils/test/BinaryInputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/BinaryInputStreamTest.cpp index 356bdb675..77bf19dd8 100644 --- a/framework/Core/Utils/test/BinaryInputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/BinaryInputStreamTest.cpp @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/BinaryInputStream.h" -#include "framework_common_gmock_header.h" +#include "internal/Core/Utils/BinaryInputStream.h" #include "gtest/gtest.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" #include "UnsafeTestMemoryHelpers.h" #include -namespace ramses_internal +namespace ramses::internal { TEST(BinaryInputStreamTest, ReadInt32Value) { - Byte buffer[4]; + std::byte buffer[4]; int32_t value = 5; PlatformMemory::Copy(buffer, &value, sizeof(int32_t)); @@ -30,7 +29,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadUInt32Value) { - Byte buffer[4]; + std::byte buffer[4]; uint32_t value = 5; PlatformMemory::Copy(buffer, &value, sizeof(uint32_t)); @@ -43,7 +42,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadInt64Value) { - Byte buffer[8]; + std::byte buffer[8]; int64_t value = 5; PlatformMemory::Copy(buffer, &value, sizeof(int64_t)); @@ -56,7 +55,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadUInt64Value) { - Byte buffer[8]; + std::byte buffer[8]; uint64_t value = 5; PlatformMemory::Copy(buffer, &value, sizeof(uint64_t)); @@ -70,7 +69,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadUInt16Value) { - Byte buffer[2]; + std::byte buffer[2]; uint16_t value = 5; PlatformMemory::Copy(buffer, &value, sizeof(uint16_t)); @@ -83,9 +82,9 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadStringValue) { - Byte buffer[16]; + std::byte buffer[16]; std::string value = "Hello World"; - uint32_t strlen = static_cast(value.size()); + auto strlen = static_cast(value.size()); UnsafeTestMemoryHelpers::WriteToMemoryBlob(strlen, buffer); PlatformMemory::Copy(buffer + sizeof(uint32_t), value.c_str(), value.size() + 1); @@ -99,9 +98,9 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadEmptyStringValue) { - Byte buffer[16]; + std::byte buffer[16]; std::string value; - uint32_t strlen = static_cast(value.size()); + auto strlen = static_cast(value.size()); UnsafeTestMemoryHelpers::WriteToMemoryBlob(strlen, buffer); PlatformMemory::Copy(buffer + sizeof(uint32_t), value.c_str(), value.size() + 1); @@ -115,7 +114,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadBoolValue) { - Byte buffer[4]; + std::byte buffer[4]; bool value = true; PlatformMemory::Copy(buffer, &value, sizeof(bool)); @@ -128,7 +127,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadFloatValue) { - Byte buffer[4]; + std::byte buffer[4]; float value = 0.002f; PlatformMemory::Copy(buffer, &value, sizeof(float)); @@ -141,11 +140,11 @@ namespace ramses_internal TEST(BinaryInputStreamTest, ReadMultipleData) { - Byte buffer[1024]; + std::byte buffer[1024]; int32_t intVal = 5; float floatVal = 4.3f; std::string stringVal = "Hello World"; - uint32_t strlen = static_cast(stringVal.size()); + auto strlen = static_cast(stringVal.size()); bool boolVal = true; size_t offset = 0; @@ -185,7 +184,7 @@ namespace ramses_internal }; - Byte buffer[sizeof(uint16_t) + sizeof(uint32_t)]; + std::byte buffer[sizeof(uint16_t) + sizeof(uint32_t)]; TestEnum16 value16 = TestEnum16::TestEnumValue; TestEnum32 value32 = TestEnum32::TestEnumValue; @@ -206,7 +205,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, GetReadPosition) { - const Byte buffer[10] = {0}; + const std::byte buffer[10] = {std::byte{0}}; BinaryInputStream inStream(buffer); char readBuffer[10] = {0}; @@ -219,7 +218,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, canGetPos) { - const Byte buffer[10] = {0}; + const std::byte buffer[10] = {std::byte{0}}; BinaryInputStream inStream(buffer); size_t pos = 0; @@ -237,7 +236,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, CanSkipForward) { - const Byte buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); uint8_t in = 0; inStream.skip(3); @@ -253,7 +252,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, CanSkipBackward) { - const Byte buffer[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); uint8_t in = 0; inStream >> in; @@ -269,7 +268,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, currentReadBytesStartsWithZero) { - const Byte buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); EXPECT_EQ(0u, inStream.getCurrentReadBytes()); @@ -277,7 +276,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, currentReadBytesIsIncrementedByReading) { - const Byte buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); uint8_t in = 0; @@ -290,7 +289,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, currentReadBytesIsIncrementedBySkip) { - const Byte buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); inStream.skip(1234); @@ -301,7 +300,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, currentReadBytesIsDecrementedBySkippingBackwards) { - const Byte buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); inStream.skip(5555); @@ -312,7 +311,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, canSeekFromBeginning) { - const Byte buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); EXPECT_EQ(0u, inStream.getCurrentReadBytes()); @@ -328,7 +327,7 @@ namespace ramses_internal TEST(BinaryInputStreamTest, canSeekRelative) { - const Byte buffer[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const std::byte buffer[10] = {std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}, std::byte{10}}; BinaryInputStream inStream(buffer); EXPECT_EQ(0u, inStream.getCurrentReadBytes()); diff --git a/framework/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp similarity index 80% rename from framework/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp index 515dfc1e4..a2a3f395b 100644 --- a/framework/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/BinaryOffsetFileInputStreamTest.cpp @@ -7,16 +7,16 @@ // ------------------------------------------------------------------------- -#include "Utils/BinaryOffsetFileInputStream.h" +#include "internal/Core/Utils/BinaryOffsetFileInputStream.h" #include "WindowsInvalidParameterCheckSuppression.h" -#include "Collections/IInputStream.h" +#include "internal/PlatformAbstraction/Collections/IInputStream.h" #include "FileDescriptorHelper.h" -#include "Utils/File.h" +#include "internal/Core/Utils/File.h" #include "gtest/gtest.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ABinaryOffsetFileInputStream : public ::testing::Test { @@ -27,7 +27,7 @@ namespace ramses_internal File(testFileName).remove(); } - void writeFile(std::initializer_list data) + void writeFile(std::initializer_list data) { File f(testFileName); ASSERT_TRUE(f.open(File::Mode::WriteNewBinary)); @@ -37,13 +37,13 @@ namespace ramses_internal void openFile(int flags = O_RDONLY) { assert(fd == -1); - fd = ramses_internal::FileDescriptorHelper::OpenFileDescriptorBinary(testFileName, flags); + fd = FileDescriptorHelper::OpenFileDescriptorBinary(testFileName, flags); ASSERT_NE(-1, fd); } - std::vector readData(BinaryOffsetFileInputStream& is, size_t size) + std::vector readData(BinaryOffsetFileInputStream& is, size_t size) { - std::vector data(size); + std::vector data(size); is.read(data.data(), size); return data; } @@ -53,12 +53,18 @@ namespace ramses_internal int fd = -1; }; + template + std::vector make_byte_vector(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + TEST_F(ABinaryOffsetFileInputStream, readRegularFileInOneBlock) { writeFile({3, 2, 1}); openFile(); BinaryOffsetFileInputStream is(fd, 0, 3); - EXPECT_EQ(std::vector({3, 2, 1}), readData(is, 3)); + EXPECT_EQ(make_byte_vector(3, 2, 1), readData(is, 3)); EXPECT_EQ(EStatus::Ok, is.getState()); } @@ -67,9 +73,9 @@ namespace ramses_internal writeFile({3, 2, 1}); openFile(); BinaryOffsetFileInputStream is(fd, 0, 3); - EXPECT_EQ(std::vector({3}), readData(is, 1)); - EXPECT_EQ(std::vector({2}), readData(is, 1)); - EXPECT_EQ(std::vector({1}), readData(is, 1)); + EXPECT_EQ(make_byte_vector(3), readData(is, 1)); + EXPECT_EQ(make_byte_vector(2), readData(is, 1)); + EXPECT_EQ(make_byte_vector(1), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.getState()); } @@ -78,7 +84,7 @@ namespace ramses_internal writeFile({0, 0, 3, 2, 1, 0, 0}); openFile(); BinaryOffsetFileInputStream is(fd, 2, 3); - EXPECT_EQ(std::vector({3, 2, 1}), readData(is, 3)); + EXPECT_EQ(make_byte_vector(3, 2, 1), readData(is, 3)); EXPECT_EQ(EStatus::Ok, is.getState()); } @@ -121,19 +127,19 @@ namespace ramses_internal BinaryOffsetFileInputStream is(fd, 1, 3); EXPECT_EQ(EStatus::Ok, is.seek(0, IInputStream::Seek::FromBeginning)); - EXPECT_EQ(std::vector({1}), readData(is, 1)); + EXPECT_EQ(make_byte_vector(1), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.seek(0, IInputStream::Seek::Relative)); - EXPECT_EQ(std::vector({2}), readData(is, 1)); + EXPECT_EQ(make_byte_vector(2), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.seek(2, IInputStream::Seek::FromBeginning)); - EXPECT_EQ(std::vector({3}), readData(is, 1)); + EXPECT_EQ(make_byte_vector(3), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.seek(-1, IInputStream::Seek::Relative)); - EXPECT_EQ(std::vector({3}), readData(is, 1)); + EXPECT_EQ(make_byte_vector(3), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.seek(-3, IInputStream::Seek::Relative)); - EXPECT_EQ(std::vector({1}), readData(is, 1)); + EXPECT_EQ(make_byte_vector(1), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.getState()); } @@ -181,7 +187,7 @@ namespace ramses_internal EXPECT_EQ(EStatus::Ok, is.getPos(pos)); EXPECT_EQ(1u, pos); - EXPECT_EQ(std::vector({2}), readData(is, 1)); + EXPECT_EQ(std::vector({std::byte{2}}), readData(is, 1)); EXPECT_EQ(EStatus::Ok, is.getPos(pos)); EXPECT_EQ(2u, pos); } diff --git a/framework/Core/Utils/test/BinaryOutputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/BinaryOutputStreamTest.cpp similarity index 72% rename from framework/Core/Utils/test/BinaryOutputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/BinaryOutputStreamTest.cpp index ced9b66eb..2910a5d18 100644 --- a/framework/Core/Utils/test/BinaryOutputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/BinaryOutputStreamTest.cpp @@ -6,20 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/BinaryOutputStream.h" -#include "Utils/BinaryInputStream.h" -#include "Collections/Guid.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" #include "UnsafeTestMemoryHelpers.h" -namespace ramses_internal +namespace ramses::internal { TEST(BinaryOutputStreamTest, Constructor) { BinaryOutputStream outStream; EXPECT_EQ(0u, outStream.getSize()); + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(0u, position); EXPECT_EQ(16u, outStream.getCapacity()); } @@ -37,12 +39,16 @@ namespace ramses_internal outStream << static_cast(5) << static_cast(6) << static_cast(7); - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(5, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(uint16_t); EXPECT_EQ(6, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(uint16_t); EXPECT_EQ(7, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(3 * sizeof(uint16_t), position); } TEST(BinaryOutputStreamTest, InsertBool) @@ -51,12 +57,16 @@ namespace ramses_internal outStream << true << false << true; - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_TRUE(UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(bool); EXPECT_FALSE(UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(bool); EXPECT_TRUE(UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(3 * sizeof(bool), position); } TEST(BinaryOutputStreamTest, InsertInt) @@ -65,12 +75,16 @@ namespace ramses_internal outStream << 5 << 6 << 7; - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(5, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int32_t); EXPECT_EQ(6, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int32_t); EXPECT_EQ(7, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(3 * sizeof(int32_t), position); } TEST(BinaryOutputStreamTest, InsertUInt) @@ -78,7 +92,7 @@ namespace ramses_internal BinaryOutputStream outStream; outStream << 0u << 5u << 6u << 7u << std::numeric_limits::max(); - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(0u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(uint32_t); EXPECT_EQ(5u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); @@ -88,6 +102,10 @@ namespace ramses_internal EXPECT_EQ(7u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(uint32_t); EXPECT_EQ(std::numeric_limits::max(), UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(5 * sizeof(uint32_t), position); } TEST(BinaryOutputStreamTest, InsertInt64) @@ -96,12 +114,16 @@ namespace ramses_internal outStream << static_cast(5) << static_cast(6) << static_cast(7); - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(5, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int64_t); EXPECT_EQ(6, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int64_t); EXPECT_EQ(7, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(3 * sizeof(uint64_t), position); } TEST(BinaryOutputStreamTest, InsertUInt64) @@ -109,7 +131,7 @@ namespace ramses_internal BinaryOutputStream outStream; outStream << static_cast(0u) << static_cast(5u) << static_cast(6u) << static_cast(7u) << std::numeric_limits::max(); - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(0u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(uint64_t); EXPECT_EQ(5u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); @@ -119,6 +141,10 @@ namespace ramses_internal EXPECT_EQ(7u, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(uint64_t); EXPECT_EQ(std::numeric_limits::max(), UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(5 * sizeof(uint64_t), position); } TEST(BinaryOutputStreamTest, InsertFloat) @@ -126,12 +152,16 @@ namespace ramses_internal BinaryOutputStream outStream; outStream << 5.0f << 6.0f << 7.0f; - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(5.0f, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int32_t); EXPECT_EQ(6.0f, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int32_t); EXPECT_EQ(7.0f, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(3 * sizeof(float), position); } TEST(BinaryOutputStreamTest, InsertGuid) @@ -141,7 +171,7 @@ namespace ramses_internal Guid guid2; outStream << guid1 << guid2; - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); BinaryInputStream in(data); Guid fromStreamGuid1; @@ -150,6 +180,10 @@ namespace ramses_internal EXPECT_EQ(guid1, fromStreamGuid1); EXPECT_EQ(guid2, fromStreamGuid2); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(2 * sizeof(Guid::value_type), position); } TEST(BinaryOutputStreamTest, InsertMultipleData) @@ -158,10 +192,10 @@ namespace ramses_internal const std::string testString = "abcdefgh"; outStream << 5 << testString << 7.0f; - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); EXPECT_EQ(5, UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data)); data += sizeof(int32_t); - const uint32_t len = UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data); + const auto len = UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(data); EXPECT_EQ(len, static_cast(testString.size())); data += sizeof(int32_t); EXPECT_EQ(0, std::memcmp(testString.data(), data, testString.size())); @@ -175,16 +209,17 @@ namespace ramses_internal outStream << std::string("Hello World with a lot of characters"); - const uint32_t strlen = UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(outStream.getData()); - - char* buffer = new char[strlen + 1]; + const auto strlen = UnsafeTestMemoryHelpers::GetTypedValueFromMemoryBlob(outStream.getData()); - std::memcpy(buffer, outStream.getData() + sizeof(uint32_t), strlen); + std::vector buffer(strlen + 1); + std::memcpy(buffer.data(), outStream.getData() + sizeof(uint32_t), strlen); buffer[strlen] = 0; - EXPECT_STREQ("Hello World with a lot of characters", buffer); + EXPECT_STREQ("Hello World with a lot of characters", buffer.data()); - delete[] buffer; + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, outStream.getPos(position)); + EXPECT_EQ(strlen + sizeof(uint32_t), position); } TEST(BinaryOutputStreamTest, InserStronglyTypedEnum) @@ -204,7 +239,7 @@ namespace ramses_internal outStream << TestEnum16::TestEnumValue << TestEnum32::TestEnumValue; ASSERT_EQ(sizeof(uint16_t) + sizeof(uint32_t), outStream.getSize()); - const Byte* data = outStream.getData(); + const std::byte* data = outStream.getData(); TestEnum16 value16; std::memcpy(&value16, data, sizeof(value16)); EXPECT_EQ(TestEnum16::TestEnumValue, value16); @@ -221,7 +256,7 @@ namespace ramses_internal stream << static_cast(1); EXPECT_EQ(sizeof(uint32_t), stream.getSize()); - std::vector vec = stream.release(); + std::vector vec = stream.release(); EXPECT_EQ(sizeof(uint32_t), vec.size()); EXPECT_EQ(0u, stream.getSize()); EXPECT_TRUE(nullptr == stream.getData()); diff --git a/tests/unittests/framework/Core/Utils/test/EnumTraitsTest.cpp b/tests/unittests/framework/Core/Utils/test/EnumTraitsTest.cpp new file mode 100644 index 000000000..72d10e5e1 --- /dev/null +++ b/tests/unittests/framework/Core/Utils/test/EnumTraitsTest.cpp @@ -0,0 +1,192 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/Core/Utils/EnumTraits.h" +#include + +#if defined(RAMSES_HAS_ENUMTRAITS) + +namespace ramses::internal +{ + enum class EnumClass + { + Value1, Value2 + }; + + enum class EnumClassU8 : uint8_t + { + Value1, Value2 + }; + + enum EFoo + { + EFoo_Value1, EFoo_Value2 + }; + + enum EFooU16 : uint16_t + { + EFooU16_Value1, EFooU16_Value2 + }; + + TEST(EnumTraits, isSupported) + { + static_assert(EnumTraits::IsSupported()); + } + + TEST(EnumTraits, isValidEnumClass) + { + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid(0)>()); + static_assert(EnumTraits::internal::IsValid(1)>()); + static_assert(!EnumTraits::internal::IsValid(2)>()); + static_assert(!EnumTraits::internal::IsValid(-1)>()); + static_assert(!EnumTraits::internal::IsValid(15)>()); + } + + TEST(EnumTraits, isValidEnumClassU8) + { + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid(0)>()); + static_assert(EnumTraits::internal::IsValid(1)>()); + static_assert(!EnumTraits::internal::IsValid(2)>()); + static_assert(!EnumTraits::internal::IsValid(-1)>()); + static_assert(!EnumTraits::internal::IsValid(15)>()); + } + + TEST(EnumTraits, isValidEnum) + { + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid(0)>()); + static_assert(EnumTraits::internal::IsValid(1)>()); + static_assert(!EnumTraits::internal::IsValid(2)>()); + static_assert(!EnumTraits::internal::IsValid(-1)>()); + static_assert(!EnumTraits::internal::IsValid(15)>()); + } + + TEST(EnumTraits, isValidEnumU16) + { + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid()); + static_assert(EnumTraits::internal::IsValid(0)>()); + static_assert(EnumTraits::internal::IsValid(1)>()); + static_assert(!EnumTraits::internal::IsValid(2)>()); + static_assert(!EnumTraits::internal::IsValid(-1)>()); + static_assert(!EnumTraits::internal::IsValid(15)>()); + } + + TEST(EnumTraits, ElementCount0) + { + enum class Enum + { + }; + static_assert(0 == EnumTraits::internal::ElementCount::value); + static_assert(EnumTraits::VerifyElementCountIfSupported(0)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(1)); + } + + + TEST(EnumTraits, ElementCount1) + { + enum class Enum + { + A + }; + static_assert(1 == EnumTraits::internal::ElementCount::value); + static_assert(!EnumTraits::VerifyElementCountIfSupported(0)); + static_assert(EnumTraits::VerifyElementCountIfSupported(1)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(2)); + } + + TEST(EnumTraits, ElementCount1Offset) + { + enum class Enum + { + A = 25 + }; + static_assert(1 == EnumTraits::internal::ElementCount::value); + static_assert(!EnumTraits::VerifyElementCountIfSupported(0)); + static_assert(EnumTraits::VerifyElementCountIfSupported(1)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(2)); + } + + TEST(EnumTraits, ElementCount3Offset) + { + enum class Enum + { + A, B = 2, C + }; + static_assert(3 == EnumTraits::internal::ElementCount::value); + static_assert(!EnumTraits::VerifyElementCountIfSupported(0)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(1)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(2)); + static_assert(EnumTraits::VerifyElementCountIfSupported(3)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(4)); + } + + TEST(EnumTraits, ElementCount3) + { + enum class Enum + { + A, B, C + }; + static_assert(3 == EnumTraits::internal::ElementCount::value); + static_assert(!EnumTraits::VerifyElementCountIfSupported(0)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(1)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(2)); + static_assert(EnumTraits::VerifyElementCountIfSupported(3)); + static_assert(!EnumTraits::VerifyElementCountIfSupported(4)); + } + + TEST(EnumTraits, ElementCount127) + { + enum class Enum + { + E00, E01, E02, E03, E04, E05, E06, E07, E08, E09, + E10, E11, E12, E13, E14, E15, E16, E17, E18, E19, + E20, E21, E22, E23, E24, E25, E26, E27, E28, E29, + E30, E31, E32, E33, E34, E35, E36, E37, E38, E39, + E40, E41, E42, E43, E44, E45, E46, E47, E48, E49, + E50, E51, E52, E53, E54, E55, E56, E57, E58, E59, + E60, E61, E62, E63, E64, E65, E66, E67, E68, E69, + E70, E71, E72, E73, E74, E75, E76, E77, E78, E79, + E80, E81, E82, E83, E84, E85, E86, E87, E88, E89, + E90, E91, E92, E93, E94, E95, E96, E97, E98, E99, + E100, E101, E102, E103, E104, E105, E106, E107, E108, E109, + E110, E111, E112, E113, E114, E115, E116, E117, E118, E119, + E120, E121, E122, E123, E124, E125, E126, + }; + static_assert(127 == EnumTraits::internal::ElementCount::value); + static_assert(EnumTraits::VerifyElementCountIfSupported(127)); + } + + TEST(EnumTraits, ElementCount128) + { + enum class Enum + { + E00, E01, E02, E03, E04, E05, E06, E07, E08, E09, + E10, E11, E12, E13, E14, E15, E16, E17, E18, E19, + E20, E21, E22, E23, E24, E25, E26, E27, E28, E29, + E30, E31, E32, E33, E34, E35, E36, E37, E38, E39, + E40, E41, E42, E43, E44, E45, E46, E47, E48, E49, + E50, E51, E52, E53, E54, E55, E56, E57, E58, E59, + E60, E61, E62, E63, E64, E65, E66, E67, E68, E69, + E70, E71, E72, E73, E74, E75, E76, E77, E78, E79, + E80, E81, E82, E83, E84, E85, E86, E87, E88, E89, + E90, E91, E92, E93, E94, E95, E96, E97, E98, E99, + E100, E101, E102, E103, E104, E105, E106, E107, E108, E109, + E110, E111, E112, E113, E114, E115, E116, E117, E118, E119, + E120, E121, E122, E123, E124, E125, E126, E127, + }; + static_assert(128 == EnumTraits::internal::ElementCount::value); + } +} + +#endif diff --git a/framework/Core/Utils/test/FileTest.cpp b/tests/unittests/framework/Core/Utils/test/FileTest.cpp similarity index 98% rename from framework/Core/Utils/test/FileTest.cpp rename to tests/unittests/framework/Core/Utils/test/FileTest.cpp index 722b0786a..9a80eba8e 100644 --- a/framework/Core/Utils/test/FileTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/FileTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/File.h" +#include "internal/Core/Utils/File.h" #include -namespace ramses_internal +namespace ramses::internal { class AFile : public ::testing::Test { @@ -223,7 +223,7 @@ namespace ramses_internal // read data File f3("test.txt"); EXPECT_TRUE(f3.open(File::Mode::WriteExistingBinary)); - size_t bytes; + size_t bytes = 0; EXPECT_EQ(EStatus::Ok, f3.read(buf2, sizeof(buf2), bytes)); f3.close(); @@ -263,7 +263,7 @@ namespace ramses_internal char readBuffer[12]; File readFile("seektest.txt"); EXPECT_TRUE(readFile.open(File::Mode::ReadOnlyBinary)); - size_t bytes; + size_t bytes = 0u; EXPECT_EQ(EStatus::Ok, readFile.read(readBuffer, sizeof(readBuffer), bytes)); readFile.close(); @@ -380,7 +380,7 @@ namespace ramses_internal EXPECT_TRUE(file.write(buf1, strlen(buf1))); file.close(); - size_t byteSize; + size_t byteSize = 0u; EXPECT_TRUE(file.getSizeInBytes(byteSize)); EXPECT_EQ(14u, byteSize); } diff --git a/tests/unittests/framework/Core/Utils/test/ImageTest.cpp b/tests/unittests/framework/Core/Utils/test/ImageTest.cpp new file mode 100644 index 000000000..71b4494a8 --- /dev/null +++ b/tests/unittests/framework/Core/Utils/test/ImageTest.cpp @@ -0,0 +1,374 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/Core/Utils/Image.h" +#include "internal/Core/Utils/BinaryFileInputStream.h" +#include +#include + +namespace ramses::internal +{ + TEST(AnImage, canGetResolution) + { + uint8_t data[2 * 3 * 4] = { 3 }; // width*height*channels + + Image bitmap(2u, 3u, data, data + sizeof(data)); + EXPECT_EQ(2u, bitmap.getWidth()); + EXPECT_EQ(3u, bitmap.getHeight()); + EXPECT_EQ(6u, bitmap.getNumberOfPixels()); + } + + TEST(AnImage, canSaveAndLoadPNG) + { + const uint32_t w = 10u; + const uint32_t h = 5u; + std::vector data(w * h * 4); + std::iota(data.begin(), data.end(), uint8_t(0u)); + const Image bitmap(w, h, std::move(data)); + bitmap.saveToFilePNG("bitmapTest.png"); + Image loadedBitmap; + loadedBitmap.loadFromFilePNG("bitmapTest.png"); + EXPECT_EQ(bitmap, loadedBitmap); + EXPECT_EQ(bitmap.getWidth(), loadedBitmap.getWidth()); + EXPECT_EQ(bitmap.getHeight(), loadedBitmap.getHeight()); + EXPECT_EQ(bitmap.getData(), loadedBitmap.getData()); + } + + TEST(AnImage, ReportsTheSumOfAllItsPixels) + { + uint8_t data[400]; + + for (uint8_t i = 0; i < 100; ++i) + { + data[4 * i + 0] = 1; + data[4 * i + 1] = 2; + data[4 * i + 2] = 3; + data[4 * i + 3] = 4; + } + + Image bitmap(10, 10, data, data + sizeof(data)); + + const glm::ivec4 expectedSumOfPixelData{ 100u, 200u, 300u, 400u }; + EXPECT_EQ(expectedSumOfPixelData, bitmap.getSumOfPixelValues()); + } + + TEST(AnImage, ReportsTheSumOfAllItsPixels_4K_Image) + { + constexpr uint32_t width = 4096; + constexpr uint32_t height = 2160; + + constexpr uint8_t maxPixelValue = std::numeric_limits::max(); + std::vector data(width * height * 4, maxPixelValue); + const Image bitmap(width, height, std::move(data)); + + const glm::ivec4 resultSumOfPixelValues = bitmap.getSumOfPixelValues(); + ASSERT_EQ(resultSumOfPixelValues.x, resultSumOfPixelValues.y); + ASSERT_EQ(resultSumOfPixelValues.x, resultSumOfPixelValues.z); + ASSERT_EQ(resultSumOfPixelValues.x, resultSumOfPixelValues.w); + + constexpr int32_t maxSumValue = std::numeric_limits::max(); + EXPECT_EQ(resultSumOfPixelValues.x, maxSumValue); + } + + TEST(AnImage, CanGenerateSeparateColorAndAlphaImages) + { + std::vector data; + std::vector expectedColorData; + std::vector expectedAlphaData; + data.reserve(400u); + expectedColorData.reserve(400u); + expectedAlphaData.reserve(400u); + + for (uint8_t i = 0; i < 100; ++i) + { + data.push_back(1); + data.push_back(2); + data.push_back(3); + data.push_back(4); + + expectedColorData.push_back(1); + expectedColorData.push_back(2); + expectedColorData.push_back(3); + expectedColorData.push_back(255); + + expectedAlphaData.push_back(4); + expectedAlphaData.push_back(4); + expectedAlphaData.push_back(4); + expectedAlphaData.push_back(255); + } + + const Image bitmap(10, 10, std::move(data)); + const Image expectedColorBitmap(10, 10, std::move(expectedColorData)); + const Image expectedAlphaBitmap(10, 10, std::move(expectedAlphaData)); + + const auto resultBitmaps = bitmap.createSeparateColorAndAlphaImages(); + EXPECT_EQ(expectedColorBitmap, resultBitmaps.first); + EXPECT_EQ(expectedAlphaBitmap, resultBitmaps.second); + } + + TEST(AnImage, CanBeSubtractedFromOtherBitmapWithSameSize) + { + constexpr size_t Width = 5; + constexpr size_t Height = 3; + uint8_t data1[Width * Height * 4]; + uint8_t data2[Width * Height * 4]; + + for (uint8_t i = 0; i < Width * Height; ++i) + { + // create mosaic difference + const uint8_t value1 = (i % 2) ? 1 : 3; + const uint8_t value2 = (i % 2) ? 3 : 1; + + data1[4 * i + 0] = value1; + data1[4 * i + 1] = value1; + data1[4 * i + 2] = value1; + data1[4 * i + 3] = value1; + + data2[4 * i + 0] = value2; + data2[4 * i + 1] = value2; + data2[4 * i + 2] = value2; + data2[4 * i + 3] = value2; + } + + const Image bitmap1(Width, Height, data1, data1 + sizeof(data1)); + const Image bitmap2(Width, Height, data2, data2 + sizeof(data2)); + const Image bitmapDiff1 = bitmap1.createDiffTo(bitmap2); + const Image bitmapDiff2 = bitmap2.createDiffTo(bitmap1); + + // W*H pixels, times a difference of 2 (because of shifted mosaic pattern) + const glm::ivec4 expectedDiff(2u * Width * Height); + + EXPECT_EQ(expectedDiff, bitmapDiff1.getSumOfPixelValues()); + EXPECT_EQ(expectedDiff, bitmapDiff2.getSumOfPixelValues()); + EXPECT_EQ(2u, bitmapDiff2.getData()[3]); + EXPECT_EQ(2u, bitmapDiff1.getData()[3]); + } + + TEST(AnImage, YieldsBlackImageWhenSubtractedFromItself) + { + constexpr size_t Width = 3; + constexpr size_t Height = 5; + uint8_t data[Width * Height * 4]; + + for (uint8_t i = 0; i < Width*Height; ++i) + { + data[4 * i + 0] = 12u; + data[4 * i + 1] = 14u; + data[4 * i + 2] = 11u; + data[4 * i + 3] = 13u; + } + + const Image bitmap(Width, Height, data, data + sizeof(data)); + const Image bitmapDiff = bitmap.createDiffTo(bitmap); + + EXPECT_EQ(glm::ivec4(0u), bitmapDiff.getSumOfPixelValues()); + } + + TEST(AnImage, GivesEmptyImageIfEnlargingToSmallerSize) + { + const Image image(2, 2, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + EXPECT_EQ(Image(), image.createEnlarged(1, 3)); + EXPECT_EQ(Image(), image.createEnlarged(3, 1)); + EXPECT_EQ(Image(), image.createEnlarged(1, 1)); + } + + TEST(AnImage, GivesImageCopyIfEnlargingToSameSize) + { + const Image image(2, 2, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + EXPECT_EQ(image, image.createEnlarged(2, 2)); + } + + TEST(AnImage, CreatesEnlargedImageContainingOriginalAndRestFilledWithGivenValue_extendedWidth) + { + const Image image(2, 2, + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }); + const Image expectedImage(3, 2, + { + 1, 2, 3, 4, 5, 6, 7, 8, 33, 44, 55, 66, + 9, 10, 11, 12, 13, 14, 15, 16, 33, 44, 55, 66 + }); + EXPECT_EQ(expectedImage, image.createEnlarged(3, 2, {33, 44, 55, 66})); + } + + TEST(AnImage, CreatesEnlargedImageContainingOriginalAndRestFilledWithGivenValue_extendedHeight) + { + const Image image(2, 2, + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }); + const Image expectedImage(2, 3, + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 33, 44, 55, 66, 33, 44, 55, 66 + }); + EXPECT_EQ(expectedImage, image.createEnlarged(2, 3, { 33, 44, 55, 66 })); + } + + TEST(AnImage, CreatesEnlargedImageContainingOriginalAndRestFilledWithGivenValue_extendedWidthAndHeight) + { + const Image image(2, 2, + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }); + const Image expectedImage(3, 3, + { + 1, 2, 3, 4, 5, 6, 7, 8, 33, 44, 55, 66, + 9, 10, 11, 12, 13, 14, 15, 16, 33, 44, 55, 66, + 33, 44, 55, 66, 33, 44, 55, 66, 33, 44, 55, 66 + }); + EXPECT_EQ(expectedImage, image.createEnlarged(3, 3, { 33, 44, 55, 66 })); + } + + TEST(AnImage, CopyConstructedBitmapIsSame) + { + uint8_t data[400] = { 0 }; + + for (uint8_t i = 0; i < 100; ++i) + { + data[4 * i + 0] = 12u; + data[4 * i + 1] = 14u; + data[4 * i + 2] = 11u; + } + + Image bitmap(10, 10, data, data + sizeof(data)); + Image copied(bitmap); + (void)copied; + + EXPECT_EQ(bitmap, copied); + } + + TEST(AnImage, AssignedBitmapIsSame) + { + const Image image(2, 2, + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }); + + Image image2(1, 1, { 1, 2, 3, 4 }); + + image2 = image; + EXPECT_EQ(image, image2); + } + + TEST(AnImage, MoveAssignedBitmapIsSame) + { + Image image(2, 2, + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }); + + Image image2(1, 1, { 1, 2, 3, 4 }); + + const Image origImage = image; + (void)origImage; + + image2 = std::move(image); + EXPECT_EQ(origImage, image2); + } + + TEST(AnImage, UnequalSizeBitmapsAreDifferent) + { + static const uint8_t data[5 * 3 * 4] = { 0 }; + const Image bitmap(5, 3, data, data + sizeof(data)); + + const Image otherHeight(5, 2, data, data + 5*2*4); + const Image otherWidth(4, 3, data, data + 4*3*4); + const Image otherSizeSamePixelCount(3, 5, data, data + sizeof(data)); + + EXPECT_NE(bitmap, otherHeight); + EXPECT_NE(bitmap, otherWidth); + EXPECT_NE(bitmap, otherSizeSamePixelCount); + } + + TEST(AnImage, FlipsImage2x1) + { + const std::vector data + { + 1, 2, 3, 4, 5, 6, 7, 8 + }; + const Image expectedImage(2, 1, + { + 1, 2, 3, 4, 5, 6, 7, 8 + }); + + EXPECT_EQ(expectedImage, Image(2, 1, data.cbegin(), data.cend(), true)); + } + + TEST(AnImage, FlipsImage2x2) + { + const std::vector data + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 + }; + const Image expectedImage(2, 2, + { + 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8 + }); + + EXPECT_EQ(expectedImage, Image(2, 2, data.cbegin(), data.cend(), true)); + } + + TEST(AnImage, FlipsImage2x3) + { + const std::vector data + { + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24 + }; + const Image expectedImage(2, 3, + { + 17, 18, 19, 20, 21, 22, 23, 24, + 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, + }); + + EXPECT_EQ(expectedImage, Image(2, 3, data.cbegin(), data.cend(), true)); + } + + TEST(AnImage, FlipsImage4x4) + { + const std::vector data + { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 + }; + const Image expectedImage(4, 4, + { + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + }); + + EXPECT_EQ(expectedImage, Image(4, 4, data.cbegin(), data.cend(), true)); + } + + TEST(AnImage, getsNumOfNonBlackPixels) + { + std::array data{ { 0, 1, 0, 0, 0, 2, 1, 0, 5, 2, 1, 0, 13, 20, 1, 0 } }; + const Image bitmap(2, 2, data.cbegin(), data.cend()); + EXPECT_EQ(3u, bitmap.getNumberOfNonBlackPixels()); + EXPECT_EQ(2u, bitmap.getNumberOfNonBlackPixels(2)); + EXPECT_EQ(1u, bitmap.getNumberOfNonBlackPixels(5)); + EXPECT_EQ(0u, bitmap.getNumberOfNonBlackPixels(20)); + } +} diff --git a/framework/Core/Utils/test/InplaceStringTokenizerTest.cpp b/tests/unittests/framework/Core/Utils/test/InplaceStringTokenizerTest.cpp similarity index 95% rename from framework/Core/Utils/test/InplaceStringTokenizerTest.cpp rename to tests/unittests/framework/Core/Utils/test/InplaceStringTokenizerTest.cpp index 74881c7a8..6d0c4eeaa 100644 --- a/framework/Core/Utils/test/InplaceStringTokenizerTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/InplaceStringTokenizerTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/InplaceStringTokenizer.h" -#include "Collections/Vector.h" +#include "internal/Core/Utils/InplaceStringTokenizer.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { using namespace ::testing; @@ -19,7 +19,7 @@ namespace ramses_internal std::vector res; std::string in(str); InplaceStringTokenizer::TokenizeToCStrings(in, maxStringLength, splitToken, - [&res](const char* s) { res.push_back(s); }); + [&res](const char* s) { res.emplace_back(s); }); EXPECT_EQ(std::string(str), in); return res; } @@ -94,7 +94,7 @@ namespace ramses_internal std::vector res; std::string in(str); InplaceStringTokenizer::TokenizeToMultilineCStrings(in, maxBlockSize, splitToken, - [&res](const char* s) { res.push_back(s); }); + [&res](const char* s) { res.emplace_back(s); }); EXPECT_EQ(std::string(str), in); return res; } diff --git a/framework/Core/Utils/test/LogHelperTest.cpp b/tests/unittests/framework/Core/Utils/test/LogHelperTest.cpp similarity index 98% rename from framework/Core/Utils/test/LogHelperTest.cpp rename to tests/unittests/framework/Core/Utils/test/LogHelperTest.cpp index 6d4278d7e..7f7487924 100644 --- a/framework/Core/Utils/test/LogHelperTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/LogHelperTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/LogHelper.h" +#include "internal/Core/Utils/LogHelper.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { TEST(ALogHelper_StringToLogLevel, GivesCorrectLevels) { diff --git a/framework/Core/Utils/test/LogMacrosTest.cpp b/tests/unittests/framework/Core/Utils/test/LogMacrosTest.cpp similarity index 91% rename from framework/Core/Utils/test/LogMacrosTest.cpp rename to tests/unittests/framework/Core/Utils/test/LogMacrosTest.cpp index ebccef403..7d38403b2 100644 --- a/framework/Core/Utils/test/LogMacrosTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/LogMacrosTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/LogMacros.h" +#include "internal/Core/Utils/LogMacros.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { TEST(ALogMacro, canUseStringOutputStreamLog) { @@ -18,7 +18,7 @@ namespace ramses_internal TEST(ALogMacro, canUseStringOutputStreamLambdaLog) { - LOG_INFO_F(CONTEXT_FRAMEWORK, ([&](ramses_internal::StringOutputStream& sos) { + LOG_INFO_F(CONTEXT_FRAMEWORK, ([&](StringOutputStream& sos) { sos << "foo"; sos << 123 << 456; })); diff --git a/framework/Core/Utils/test/LoggingUtilsTest.cpp b/tests/unittests/framework/Core/Utils/test/LoggingUtilsTest.cpp similarity index 51% rename from framework/Core/Utils/test/LoggingUtilsTest.cpp rename to tests/unittests/framework/Core/Utils/test/LoggingUtilsTest.cpp index 7cdd8bff8..6f79b6285 100644 --- a/framework/Core/Utils/test/LoggingUtilsTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/LoggingUtilsTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/LoggingUtils.h" +#include "internal/Core/Utils/LoggingUtils.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { namespace { @@ -17,19 +17,11 @@ namespace ramses_internal { EFoo_A, EFoo_B, - EFoo_Last }; const std::array FooNames = {"EFoo_A", "EFoo_B"}; - ENUM_TO_STRING(EFoo, FooNames, EFoo_Last) - - enum class LogTestBar : uint16_t - { - X, - Y, - Last - }; + ENUM_TO_STRING(EFoo, FooNames, EFoo_B) const std::array LogTestBarNames = {"X", "Y"}; @@ -41,16 +33,12 @@ namespace ramses_internal } } -MAKE_ENUM_CLASS_PRINTABLE(ramses_internal::LogTestBar, - "LogTestBar", - ramses_internal::LogTestBarNames, - ramses_internal::LogTestBar::Last); -MAKE_ENUM_CLASS_PRINTABLE_NO_EXTRA_LAST(ramses_internal::LogTestBarNoLast, +MAKE_ENUM_CLASS_PRINTABLE(ramses::internal::LogTestBarNoLast, "LogTestBarNoLast", - ramses_internal::LogTestBarNames, - ramses_internal::LogTestBarNoLast::Y); + ramses::internal::LogTestBarNames, + ramses::internal::LogTestBarNoLast::Y); -namespace ramses_internal +namespace ramses::internal { TEST(ALoggingUtils, CanUseEnumToString) { @@ -58,22 +46,13 @@ namespace ramses_internal EXPECT_STREQ("EFoo_B", EnumToString(EFoo_B)); } - TEST(ALoggingUtils, CanUseEnumClassFormatter) - { - EXPECT_EQ("LogTestBar::X", fmt::format("{}", LogTestBar::X)); - EXPECT_EQ("LogTestBar::Y", fmt::format("{}", LogTestBar::Y)); - EXPECT_EQ("X", fmt::format("{:s}", LogTestBar::X)); - EXPECT_EQ("Y", fmt::format("{:s}", LogTestBar::Y)); - EXPECT_EQ("", fmt::to_string(LogTestBar::Last)); - } - TEST(ALoggingUtils, CanUsEnumClassFormatterShortSyntax) { EXPECT_EQ("LogTestBarNoLast::X", fmt::format("{}", LogTestBarNoLast::X)); EXPECT_EQ("LogTestBarNoLast::Y", fmt::format("{}", LogTestBarNoLast::Y)); EXPECT_EQ("X", fmt::format("{:s}", LogTestBarNoLast::X)); EXPECT_EQ("Y", fmt::format("{:s}", LogTestBarNoLast::Y)); - const LogTestBarNoLast invalidAbove = static_cast(2); - EXPECT_EQ("", fmt::to_string(invalidAbove)); + const auto invalidAbove = static_cast(2); + EXPECT_EQ("", fmt::to_string(invalidAbove)); } } diff --git a/framework/Core/Utils/test/MemoryPoolExplicitTest.cpp b/tests/unittests/framework/Core/Utils/test/MemoryPoolExplicitTest.cpp similarity index 91% rename from framework/Core/Utils/test/MemoryPoolExplicitTest.cpp rename to tests/unittests/framework/Core/Utils/test/MemoryPoolExplicitTest.cpp index ddf49a6e6..ca8962978 100644 --- a/framework/Core/Utils/test/MemoryPoolExplicitTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/MemoryPoolExplicitTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/MemoryPoolExplicit.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { template class AMemoryPoolExplicit : public testing::Test @@ -89,7 +88,7 @@ namespace ramses_internal { const uint32_t totalObjects = this->memoryPool.getTotalCount(); - const typename TypeParam::handle_type desiredHandle = static_cast(this->InitialSize - 10u); + const auto desiredHandle = static_cast(this->InitialSize - 10u); const typename TypeParam::handle_type someObject = this->memoryPool.allocate(desiredHandle); EXPECT_EQ(someObject, desiredHandle); @@ -102,7 +101,7 @@ namespace ramses_internal for (uint32_t i = 0u; i < totalCapacity; ++i) { - const typename TypeParam::handle_type handle = static_cast(i); + const auto handle = static_cast(i); if (handle != this->allocatedObject) { this->memoryPool.allocate(handle); @@ -111,7 +110,7 @@ namespace ramses_internal for (uint32_t i = 0u; i < totalCapacity; ++i) { - const typename TypeParam::handle_type handle = static_cast(i); + const auto handle = static_cast(i); EXPECT_TRUE(this->memoryPool.isAllocated(handle)); } diff --git a/framework/Core/Utils/test/MemoryPoolIteratorTest.cpp b/tests/unittests/framework/Core/Utils/test/MemoryPoolIteratorTest.cpp similarity index 97% rename from framework/Core/Utils/test/MemoryPoolIteratorTest.cpp rename to tests/unittests/framework/Core/Utils/test/MemoryPoolIteratorTest.cpp index af138c96a..3eef6a12c 100644 --- a/framework/Core/Utils/test/MemoryPoolIteratorTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/MemoryPoolIteratorTest.cpp @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/MemoryPoolIterator.h" -#include "Utils/MemoryPool.h" -#include "Utils/MemoryPoolExplicit.h" -#include "Common/StronglyTypedValue.h" -#include "framework_common_gmock_header.h" +#include "internal/Core/Utils/MemoryPoolIterator.h" +#include "internal/Core/Utils/MemoryPool.h" +#include "internal/Core/Utils/MemoryPoolExplicit.h" +#include "ramses/framework/StronglyTypedValue.h" #include "gtest/gtest.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { template class AMemoryPoolIterator : public testing::Test @@ -67,9 +66,8 @@ namespace ramses_internal typename TypeParam::const_iterator constIt = pool.cbegin(); EXPECT_EQ(constIt, pool.cend()); - for (const auto& e : pool) + for (const auto& e [[maybe_unused]] : pool) { - UNUSED(e); ASSERT_TRUE(false); } } @@ -87,9 +85,8 @@ namespace ramses_internal EXPECT_EQ(itConstPool2, constPool.cend()); EXPECT_EQ(itConstPool2, constPool.cend()); - for (const auto& e : constPool) + for (const auto& e [[maybe_unused]] : constPool) { - UNUSED(e); ASSERT_TRUE(false); } } @@ -106,9 +103,8 @@ namespace ramses_internal typename TypeParam::const_iterator constIt = pool.cbegin(); EXPECT_EQ(constIt, pool.cend()); - for (const auto& e : pool) + for (const auto& e [[maybe_unused]] : pool) { - UNUSED(e); ASSERT_TRUE(false); } } @@ -128,9 +124,8 @@ namespace ramses_internal EXPECT_EQ(itConstPool2, constPool.cend()); EXPECT_EQ(itConstPool2, constPool.cend()); - for (const auto& e : constPool) + for (const auto& e [[maybe_unused]] : constPool) { - UNUSED(e); ASSERT_TRUE(false); } } diff --git a/framework/Core/Utils/test/MemoryPoolTest.cpp b/tests/unittests/framework/Core/Utils/test/MemoryPoolTest.cpp similarity index 94% rename from framework/Core/Utils/test/MemoryPoolTest.cpp rename to tests/unittests/framework/Core/Utils/test/MemoryPoolTest.cpp index 78a19e5d4..439bf5717 100644 --- a/framework/Core/Utils/test/MemoryPoolTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/MemoryPoolTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/MemoryPool.h" +#include "internal/Core/Utils/MemoryPool.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { template class AMemoryPool : public testing::Test @@ -103,7 +102,7 @@ namespace ramses_internal uint32_t totalObjects = this->memoryPool.getTotalCount(); uint32_t actualObjects = this->memoryPool.getActualCount(); - typename TypeParam::handle_type desiredHandle = static_cast(this->InitialSize - 10u); + auto desiredHandle = static_cast(this->InitialSize - 10u); typename TypeParam::handle_type someObject = this->memoryPool.allocate(desiredHandle); EXPECT_EQ(someObject, desiredHandle); @@ -115,7 +114,7 @@ namespace ramses_internal { const uint32_t totalObjects = this->memoryPool.getTotalCount(); - typename TypeParam::handle_type desiredHandle = static_cast(totalObjects + 10u); + auto desiredHandle = static_cast(totalObjects + 10u); typename TypeParam::handle_type someObject = this->memoryPool.allocate(desiredHandle); EXPECT_EQ(someObject, desiredHandle); diff --git a/framework/Core/Utils/test/MessagePoolTest.cpp b/tests/unittests/framework/Core/Utils/test/MessagePoolTest.cpp similarity index 97% rename from framework/Core/Utils/test/MessagePoolTest.cpp rename to tests/unittests/framework/Core/Utils/test/MessagePoolTest.cpp index 826ad2d92..ab8b750db 100644 --- a/framework/Core/Utils/test/MessagePoolTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/MessagePoolTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/MessagePool.h" +#include "internal/Core/Utils/MessagePool.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class MessagePoolTest : public testing::Test { diff --git a/framework/Core/Utils/test/PeriodicLoggerHelperTest.cpp b/tests/unittests/framework/Core/Utils/test/PeriodicLoggerHelperTest.cpp similarity index 98% rename from framework/Core/Utils/test/PeriodicLoggerHelperTest.cpp rename to tests/unittests/framework/Core/Utils/test/PeriodicLoggerHelperTest.cpp index ad3038688..ff934005f 100644 --- a/framework/Core/Utils/test/PeriodicLoggerHelperTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/PeriodicLoggerHelperTest.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- -#include "Utils/PeriodicLoggerHelper.h" +#include "internal/Core/Utils/PeriodicLoggerHelper.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { TEST(PeriodicLoggerHelper, SummaryEntry_UInt32) { diff --git a/framework/Core/Utils/test/RawBinaryOutputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/RawBinaryOutputStreamTest.cpp similarity index 63% rename from framework/Core/Utils/test/RawBinaryOutputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/RawBinaryOutputStreamTest.cpp index cfcb91f11..fbb366c94 100644 --- a/framework/Core/Utils/test/RawBinaryOutputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/RawBinaryOutputStreamTest.cpp @@ -6,25 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/RawBinaryOutputStream.h" +#include "internal/Core/Utils/RawBinaryOutputStream.h" #include "gtest/gtest.h" + #include +#include -namespace ramses_internal +namespace ramses::internal { TEST(ARawBinaryOutputStream, hasExpectedDefaultValues) { - std::vector buffer(10); + std::vector buffer(10); RawBinaryOutputStream os(buffer.data(), buffer.size()); EXPECT_EQ(buffer.data(), os.getData()); EXPECT_EQ(buffer.size(), os.getSize()); EXPECT_EQ(0u, os.getBytesWritten()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(0u, position); } TEST(ARawBinaryOutputStream, canDoZeroWrite) { - std::vector buffer(10); + std::vector buffer(10); RawBinaryOutputStream os(buffer.data(), buffer.size()); const uint32_t d = 123; @@ -33,19 +39,24 @@ namespace ramses_internal os.write(nullptr, 0); EXPECT_EQ(0u, os.getBytesWritten()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(0u, position); } TEST(ARawBinaryOutputStream, canWriteData) { - std::vector buffer(450); - std::vector refBuffer(450); - RawBinaryOutputStream os(buffer.data(), buffer.size()); + std::vector buffer(450); + std::vector refBuffer(450); + RawBinaryOutputStream os(buffer.data(), buffer.size()); + size_t position = std::numeric_limits::max(); const uint8_t d8 = 123; const uint16_t d16 = 65531; const uint64_t d64 = 0xf897abd898798; - std::vector dVec(432); - std::iota(dVec.begin(), dVec.end(), static_cast(127)); + std::vector dVec(432); + std::generate(dVec.begin(), dVec.end(), [](){ static uint8_t i{126}; return std::byte(++i); }); EXPECT_EQ(refBuffer, buffer); @@ -53,21 +64,29 @@ namespace ramses_internal std::memcpy(refBuffer.data(), &d8, sizeof(d8)); EXPECT_EQ(1u, os.getBytesWritten()); EXPECT_EQ(refBuffer, buffer); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(position, os.getBytesWritten()); os.write(&d64, sizeof(d64)); std::memcpy(refBuffer.data()+1, &d64, sizeof(d64)); EXPECT_EQ(9u, os.getBytesWritten()); EXPECT_EQ(refBuffer, buffer); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(position, os.getBytesWritten()); os.write(dVec.data(), dVec.size()); std::memcpy(refBuffer.data()+9, dVec.data(), dVec.size()); EXPECT_EQ(441u, os.getBytesWritten()); EXPECT_EQ(refBuffer, buffer); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(position, os.getBytesWritten()); os.write(&d16, sizeof(d16)); std::memcpy(refBuffer.data()+441, &d16, sizeof(d16)); EXPECT_EQ(443u, os.getBytesWritten()); EXPECT_EQ(refBuffer, buffer); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(position, os.getBytesWritten()); EXPECT_EQ(buffer.data(), os.getData()); EXPECT_EQ(buffer.size(), os.getSize()); @@ -75,9 +94,9 @@ namespace ramses_internal TEST(ARawBinaryOutputStream, canWriteExactlyFittingData) { - std::vector buffer(4); - std::vector refBuffer(4); - RawBinaryOutputStream os(buffer.data(), buffer.size()); + std::vector buffer(4); + std::vector refBuffer(4); + RawBinaryOutputStream os(buffer.data(), buffer.size()); const uint32_t d = 123456789; os.write(&d, sizeof(d)); diff --git a/framework/Core/Utils/test/StatisticCollectionTest.cpp b/tests/unittests/framework/Core/Utils/test/StatisticCollectionTest.cpp similarity index 98% rename from framework/Core/Utils/test/StatisticCollectionTest.cpp rename to tests/unittests/framework/Core/Utils/test/StatisticCollectionTest.cpp index 1d5f37b77..cb01ee5b1 100644 --- a/framework/Core/Utils/test/StatisticCollectionTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/StatisticCollectionTest.cpp @@ -6,21 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/StatisticCollection.h" +#include "internal/Core/Utils/StatisticCollection.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class StatisticCollectionTest : public testing::Test { protected: - StatisticCollectionTest() - { - } - StatisticCollectionFramework m_statisticCollection; StatisticCollectionScene m_statisticCollectionScene; }; diff --git a/framework/Core/Utils/test/StringUtilsTest.cpp b/tests/unittests/framework/Core/Utils/test/StringUtilsTest.cpp similarity index 96% rename from framework/Core/Utils/test/StringUtilsTest.cpp rename to tests/unittests/framework/Core/Utils/test/StringUtilsTest.cpp index 3c1ff06d5..263c73036 100644 --- a/framework/Core/Utils/test/StringUtilsTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/StringUtilsTest.cpp @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Utils/StringUtils.h" -#include "SceneAPI/ResourceContentHash.h" +#include "internal/Core/Utils/StringUtils.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" #include #include -namespace ramses_internal +namespace ramses::internal { TEST(StringUtilsTest, trimStringLiterals) { diff --git a/framework/Core/Utils/test/TextureMathUtilsTest.cpp b/tests/unittests/framework/Core/Utils/test/TextureMathUtilsTest.cpp similarity index 98% rename from framework/Core/Utils/test/TextureMathUtilsTest.cpp rename to tests/unittests/framework/Core/Utils/test/TextureMathUtilsTest.cpp index 7a0f11d14..29b58828d 100644 --- a/framework/Core/Utils/test/TextureMathUtilsTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/TextureMathUtilsTest.cpp @@ -7,9 +7,9 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "Utils/TextureMathUtils.h" +#include "internal/Core/Utils/TextureMathUtils.h" -namespace ramses_internal +namespace ramses::internal { TEST(TextureMathUtilsTest, getsCorrectLowerMipSize) { diff --git a/framework/Core/Utils/test/ThreadBarrierTest.cpp b/tests/unittests/framework/Core/Utils/test/ThreadBarrierTest.cpp similarity index 93% rename from framework/Core/Utils/test/ThreadBarrierTest.cpp rename to tests/unittests/framework/Core/Utils/test/ThreadBarrierTest.cpp index 27371df7f..5df5bb139 100644 --- a/framework/Core/Utils/test/ThreadBarrierTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/ThreadBarrierTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/ThreadBarrier.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "gtest/gtest.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/PlatformThread.h" -namespace ramses_internal +namespace ramses::internal { class AThreadBarrier : public ::testing::Test { diff --git a/framework/Core/Utils/test/ThreadLocalLogTest.cpp b/tests/unittests/framework/Core/Utils/test/ThreadLocalLogTest.cpp similarity index 89% rename from framework/Core/Utils/test/ThreadLocalLogTest.cpp rename to tests/unittests/framework/Core/Utils/test/ThreadLocalLogTest.cpp index 85f62f858..0f2e3af8b 100644 --- a/framework/Core/Utils/test/ThreadLocalLogTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/ThreadLocalLogTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class AThreadLocalLog : public ::testing::Test { @@ -38,7 +38,7 @@ namespace ramses_internal TEST_F(AThreadLocalLog, canUseStringOutputStreamLambdaLog) { - LOG_INFO_RF(CONTEXT_FRAMEWORK, ([&](ramses_internal::StringOutputStream& sos) { + LOG_INFO_RF(CONTEXT_FRAMEWORK, ([&](ramses::internal::StringOutputStream& sos) { sos << "foo"; sos << 123 << 456; })); @@ -66,7 +66,7 @@ namespace ramses { TEST(AThreadLocalLogRamses, canUseContextFromRamsesNamespace) { - ramses_internal::ThreadLocalLog::SetPrefix(123); + ramses::internal::ThreadLocalLog::SetPrefix(123); LOG_INFO_R(CONTEXT_FRAMEWORK, "Foo " << 123 << 456); } } diff --git a/framework/Core/Utils/test/VectorBinaryOutputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/VectorBinaryOutputStreamTest.cpp similarity index 50% rename from framework/Core/Utils/test/VectorBinaryOutputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/VectorBinaryOutputStreamTest.cpp index 5eaa203e2..999219a91 100644 --- a/framework/Core/Utils/test/VectorBinaryOutputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/VectorBinaryOutputStreamTest.cpp @@ -6,15 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/VectorBinaryOutputStream.h" +#include "internal/Core/Utils/VectorBinaryOutputStream.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { + template + std::vector make_byte_vector(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + class AVectorBinaryOutputStream : public ::testing::Test { public: - std::vector vec; + std::vector vec; }; TEST_F(AVectorBinaryOutputStream, canInitializeEmpty) @@ -22,38 +28,54 @@ namespace ramses_internal VectorBinaryOutputStream os(vec); EXPECT_TRUE(vec.empty()); EXPECT_EQ(0u, os.asSpan().size()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(0u, position); } TEST_F(AVectorBinaryOutputStream, canInitializePrefilled) { - vec.assign({1, 2, 3}); + vec = make_byte_vector(1, 2, 3); VectorBinaryOutputStream os(vec); EXPECT_EQ(3u, vec.size()); EXPECT_EQ(0u, os.asSpan().size()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(0u, position); } TEST_F(AVectorBinaryOutputStream, canWriteTo) { VectorBinaryOutputStream os(vec); - Byte data[3] = {4, 3}; + std::byte data[3] = {std::byte{4}, std::byte{3}}; os.write(data, 2); EXPECT_EQ(2u, vec.size()); ASSERT_EQ(2u, os.asSpan().size()); - EXPECT_EQ(4u, os.asSpan()[0]); - EXPECT_EQ(3u, os.asSpan()[1]); - EXPECT_EQ(std::vector({4, 3}), vec); + EXPECT_EQ(std::byte{4u}, os.asSpan()[0]); + EXPECT_EQ(std::byte{3u}, os.asSpan()[1]); + EXPECT_EQ(make_byte_vector(4, 3), vec); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(2u, position); } TEST_F(AVectorBinaryOutputStream, canWriteToPrefilled) { - vec.assign({1, 2, 3}); + vec = make_byte_vector(1, 2, 3); VectorBinaryOutputStream os(vec); - Byte data[3] = {5, 4}; + std::byte data[3] = {std::byte{5}, std::byte{4}}; os.write(data, 2); EXPECT_EQ(5u, vec.size()); ASSERT_EQ(2u, os.asSpan().size()); - EXPECT_EQ(5u, os.asSpan()[0]); - EXPECT_EQ(4u, os.asSpan()[1]); - EXPECT_EQ(std::vector({1, 2, 3, 5, 4}), vec); + EXPECT_EQ(std::byte{5u}, os.asSpan()[0]); + EXPECT_EQ(std::byte{4u}, os.asSpan()[1]); + EXPECT_EQ(make_byte_vector(1, 2, 3, 5, 4), vec); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, os.getPos(position)); + EXPECT_EQ(2u, position); } } diff --git a/framework/Core/Utils/test/VoidOutputStreamTest.cpp b/tests/unittests/framework/Core/Utils/test/VoidOutputStreamTest.cpp similarity index 58% rename from framework/Core/Utils/test/VoidOutputStreamTest.cpp rename to tests/unittests/framework/Core/Utils/test/VoidOutputStreamTest.cpp index 7905212ed..f358357c9 100644 --- a/framework/Core/Utils/test/VoidOutputStreamTest.cpp +++ b/tests/unittests/framework/Core/Utils/test/VoidOutputStreamTest.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/VoidOutputStream.h" -#include "SceneAPI/ResourceContentHash.h" -#include "Collections/Guid.h" +#include "internal/Core/Utils/VoidOutputStream.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" #include "gmock/gmock.h" #include -namespace ramses_internal +namespace ramses::internal { class VoidOutputStreamTest : public testing::Test @@ -26,6 +26,10 @@ namespace ramses_internal { const uint32_t expectedSize = 0u; EXPECT_EQ(expectedSize, stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertUInt16) @@ -33,6 +37,10 @@ namespace ramses_internal const uint16_t value = 5u; stream << value; EXPECT_EQ( sizeof(uint16_t), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertBool) @@ -40,6 +48,10 @@ namespace ramses_internal const bool value = true; stream << value; EXPECT_EQ( sizeof(bool), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertInt32) @@ -47,6 +59,10 @@ namespace ramses_internal const int32_t value = -7; stream << value; EXPECT_EQ( sizeof(int32_t), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertUInt32) @@ -54,6 +70,10 @@ namespace ramses_internal const uint32_t value = 25u; stream << value; EXPECT_EQ( sizeof(uint32_t), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertInt64) @@ -61,6 +81,10 @@ namespace ramses_internal const int64_t value = -333; stream << value; EXPECT_EQ( sizeof(int64_t), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertUInt64) @@ -68,6 +92,10 @@ namespace ramses_internal const uint64_t value = 5432u; stream << value; EXPECT_EQ( sizeof(uint64_t), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertFloat) @@ -75,6 +103,10 @@ namespace ramses_internal const float value = 42.23f; stream << value; EXPECT_EQ( sizeof(float), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertGuid) @@ -82,6 +114,10 @@ namespace ramses_internal const Guid value; stream << value; EXPECT_EQ(sizeof(Guid::value_type), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertString) @@ -90,6 +126,10 @@ namespace ramses_internal const uint32_t expectedSize = sizeof(uint32_t) + static_cast(value.size()); // length info + string stream << value; EXPECT_EQ( expectedSize, stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertResourceContextHash) @@ -97,6 +137,10 @@ namespace ramses_internal const ResourceContentHash value; stream << value; EXPECT_EQ(sizeof(ResourceContentHash), stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } TEST_F(VoidOutputStreamTest, InsertRawData) @@ -104,6 +148,10 @@ namespace ramses_internal const uint32_t sentSize = 37u; stream.write(nullptr, sentSize); EXPECT_EQ( sentSize, stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } @@ -115,5 +163,9 @@ namespace ramses_internal const uint32_t expectedSize = sizeof(uint64_t) + sizeof(uint32_t) + 8 + sizeof(float); stream << intValue << testString << floatValue; EXPECT_EQ( expectedSize, stream.getSize()); + + size_t position = std::numeric_limits::max(); + EXPECT_EQ(EStatus::Ok, stream.getPos(position)); + EXPECT_EQ(stream.getSize(), position); } } diff --git a/tests/unittests/framework/DltLogAppender/DltAdapterTest.cpp b/tests/unittests/framework/DltLogAppender/DltAdapterTest.cpp new file mode 100644 index 000000000..94d5bdd10 --- /dev/null +++ b/tests/unittests/framework/DltLogAppender/DltAdapterTest.cpp @@ -0,0 +1,133 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include "internal/DltLogAppender/DltAdapter.h" +#include "internal/Core/Utils/LogContext.h" +#include "internal/Core/Utils/LogMessage.h" + +#include + +namespace ramses::internal +{ + class ADltAdapter : public ::testing::Test + { + public: + ADltAdapter() + : adapter(DltAdapter::getDltAdapter()) + , context(new LogContext("RXXX", "Test context")) + , logLevelChangeCallback([](const std::string& /*unused*/, int /*unused*/){}) + { + } + + void SetUp() override + { + if (DltAdapter::IsDummyAdapter()) + GTEST_SKIP(); + + if (adapter && adapter->isInitialized()) + adapter->uninitialize(); + } + + ~ADltAdapter() override + { + if (adapter && adapter->isInitialized()) + adapter->uninitialize(); + } + + DltAdapter* adapter; + std::unique_ptr context; + std::function logLevelChangeCallback; + }; + + // run always + TEST(DltAdapter, singletonNotNull) + { + EXPECT_TRUE(DltAdapter::getDltAdapter() != nullptr); + } + + TEST_F(ADltAdapter, initiallyUninitialized) + { + EXPECT_FALSE(adapter->isInitialized()); + } + + TEST_F(ADltAdapter, initializeAndUninitialize) + { + EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + EXPECT_TRUE(adapter->isInitialized()); + + adapter->uninitialize(); + EXPECT_FALSE(adapter->isInitialized()); + } + + TEST_F(ADltAdapter, doubleInitFails) + { + EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + EXPECT_FALSE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + } + + TEST_F(ADltAdapter, appIdMustBeValid) + { + EXPECT_FALSE(adapter->initialize("", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + EXPECT_FALSE(adapter->initialize("TEST123", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + } + + TEST_F(ADltAdapter, contextListMayNotBeEmpty) + { + EXPECT_FALSE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {}, true)); + } + + TEST_F(ADltAdapter, canLogOnContext) + { + EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + StringOutputStream sos(std::string("foo")); + LogMessage msg(*context, ELogLevel::Info, sos); + EXPECT_TRUE(adapter->logMessage(msg)); + } + + TEST_F(ADltAdapter, logFailsWithoutInit) + { + StringOutputStream sos(std::string("foo")); + LogMessage msg(*context, ELogLevel::Info, sos); + EXPECT_FALSE(adapter->logMessage(msg)); + } + + TEST_F(ADltAdapter, canLogWithLogLevelOff) + { + EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + StringOutputStream sos(std::string("foo")); + LogMessage msg(*context, ELogLevel::Off, sos); + EXPECT_TRUE(adapter->logMessage(msg)); + } + + TEST_F(ADltAdapter, logFailsWithNonDltContext) + { + EXPECT_TRUE(adapter->initialize("TEST", "Test application", true, logLevelChangeCallback, {context.get()}, true)); + + std::unique_ptr otherContext(new LogContext("RYYY", "Other test context")); + StringOutputStream sos(std::string("foo")); + LogMessage msg(*otherContext, ELogLevel::Info, sos); + EXPECT_FALSE(adapter->logMessage(msg)); + } + +#if defined(DLT_ENABLED) + +WARNINGS_PUSH +WARNING_DISABLE_LINUX(-Wold-style-cast) +#include +WARNINGS_POP + +TEST_F(ADltAdapter, canUseAlreadyRegisteredApp) +{ + DLT_REGISTER_APP("RAPP", "Ramses test app"); + EXPECT_TRUE(adapter->initialize("TEST", "Test application", false, logLevelChangeCallback, {context.get()}, true)); + adapter->uninitialize(); + DLT_UNREGISTER_APP(); +} +#endif +} diff --git a/framework/PlatformAbstraction/test/AbseilTest.cpp b/tests/unittests/framework/PlatformAbstraction/AbseilTest.cpp similarity index 99% rename from framework/PlatformAbstraction/test/AbseilTest.cpp rename to tests/unittests/framework/PlatformAbstraction/AbseilTest.cpp index dbc70908d..fbb590a3d 100644 --- a/framework/PlatformAbstraction/test/AbseilTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/AbseilTest.cpp @@ -17,7 +17,7 @@ #include -namespace ramses_internal +namespace ramses::internal { TEST(AAbseil, canUseVariant) { diff --git a/framework/PlatformAbstraction/test/BlockingQueueTest.cpp b/tests/unittests/framework/PlatformAbstraction/BlockingQueueTest.cpp similarity index 96% rename from framework/PlatformAbstraction/test/BlockingQueueTest.cpp rename to tests/unittests/framework/PlatformAbstraction/BlockingQueueTest.cpp index 5f1be73e9..32385719c 100644 --- a/framework/PlatformAbstraction/test/BlockingQueueTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/BlockingQueueTest.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Collections/BlockingQueue.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/Collections/BlockingQueue.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { TEST(ABlockingQueue, canPushAndPopValue) { @@ -42,7 +42,7 @@ namespace ramses_internal TEST(ABlockingQueue, hasTimeoutWhenPoppingEmptyQueue) { BlockingQueue queue; - int32_t val; + int32_t val = 0; EXPECT_FALSE(queue.pop(&val, std::chrono::milliseconds{10})); } @@ -52,7 +52,7 @@ namespace ramses_internal EXPECT_TRUE(queue.empty()); queue.push(3); EXPECT_FALSE(queue.empty()); - int32_t val; + int32_t val = 0; queue.pop(&val); EXPECT_TRUE(queue.empty()); } diff --git a/framework/PlatformAbstraction/test/ComplexTestType.h b/tests/unittests/framework/PlatformAbstraction/ComplexTestType.h similarity index 89% rename from framework/PlatformAbstraction/test/ComplexTestType.h rename to tests/unittests/framework/PlatformAbstraction/ComplexTestType.h index e4ef2d35d..e21eb9e9c 100644 --- a/framework/PlatformAbstraction/test/ComplexTestType.h +++ b/tests/unittests/framework/PlatformAbstraction/ComplexTestType.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEST_COMPLEXTESTTYPE_H -#define RAMSES_TEST_COMPLEXTESTTYPE_H +#pragma once #include -namespace ramses_internal +namespace ramses::internal { template class ComplexTestType @@ -75,12 +74,10 @@ namespace ramses_internal } template -struct std::hash> +struct std::hash> { - size_t operator()(const ramses_internal::ComplexTestType& key) + size_t operator()(const ramses::internal::ComplexTestType& key) { return std::hash()(key.value); } }; - -#endif diff --git a/framework/PlatformAbstraction/test/FmtlibTest.cpp b/tests/unittests/framework/PlatformAbstraction/FmtlibTest.cpp similarity index 96% rename from framework/PlatformAbstraction/test/FmtlibTest.cpp rename to tests/unittests/framework/PlatformAbstraction/FmtlibTest.cpp index 5017c1a4c..254f0d589 100644 --- a/framework/PlatformAbstraction/test/FmtlibTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/FmtlibTest.cpp @@ -9,7 +9,7 @@ #include "fmt/format.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { TEST(AFmtlib, CanFormatStrings) { diff --git a/framework/PlatformAbstraction/test/GuidTest.cpp b/tests/unittests/framework/PlatformAbstraction/GuidTest.cpp similarity index 95% rename from framework/PlatformAbstraction/test/GuidTest.cpp rename to tests/unittests/framework/PlatformAbstraction/GuidTest.cpp index 1486ec08e..7a4074309 100644 --- a/framework/PlatformAbstraction/test/GuidTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/GuidTest.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Collections/Guid.h" -#include "Utils/BinaryOutputStream.h" -#include "Utils/BinaryInputStream.h" +#include "internal/PlatformAbstraction/Collections/Guid.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/PlatformAbstraction/Hash.h" - -namespace ramses_internal +namespace ramses::internal { TEST(GuidTest, TestDifferentValuesAreNotEqual) { diff --git a/framework/PlatformAbstraction/test/HashMapTest.cpp b/tests/unittests/framework/PlatformAbstraction/HashMapTest.cpp similarity index 94% rename from framework/PlatformAbstraction/test/HashMapTest.cpp rename to tests/unittests/framework/PlatformAbstraction/HashMapTest.cpp index d1cbd9ee7..ad6779dc0 100644 --- a/framework/PlatformAbstraction/test/HashMapTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/HashMapTest.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Collections/HashMap.h" +#include "internal/PlatformAbstraction/Collections/HashMap.h" #include "ComplexTestType.h" #include "gtest/gtest.h" #include @@ -52,7 +52,7 @@ struct std::hash { size_t operator()(const MyStruct& c) { - return ramses_internal::HashValue(c.a, c.b); + return ramses::internal::HashValue(c.a, c.b); } }; @@ -61,16 +61,16 @@ struct std::hash { size_t operator()(const SomeClass& c) { - return ramses_internal::HashValue(c.i); + return ramses::internal::HashValue(c.i); } }; -namespace ramses_internal +namespace ramses::internal { -using RCKey = ramses_internal::ComplexTestType; -using RCValue = ramses_internal::ComplexTestType; +using RCKey = ramses::internal::ComplexTestType; +using RCValue = ramses::internal::ComplexTestType; using TestType = ComplexTestType<>; class HashMapTest: public ::testing::Test @@ -82,7 +82,7 @@ class HashMapTest: public ::testing::Test RCValue::Reset(); } - void expectRefCnt(int32_t refCnt) + static void ExpectRefCnt(int32_t refCnt) { EXPECT_EQ(refCnt, RCKey::RefCnt()); EXPECT_EQ(refCnt, RCValue::RefCnt()); @@ -190,11 +190,11 @@ TEST_F(HashMapTest, CanInsertWithoutRehashWhenConstructedWithCapacity) for (uint32_t param = 1; param < 400; ++param) { SCOPED_TRACE(param); - HashMap map(param); + HashMap map(param); const size_t oldCapacity = map.capacity(); EXPECT_GE(oldCapacity, param); - for (unsigned i = 0; i < param; ++i) + for (uint32_t i = 0; i < param; ++i) { map.put(i, i); EXPECT_EQ(i+1, map.size()); @@ -208,13 +208,13 @@ TEST_F(HashMapTest, CanInsertWithoutRehashWhenReservedToCapacity) for (uint32_t param = 1; param < 400; ++param) { SCOPED_TRACE(param); - HashMap map; + HashMap map; map.reserve(param); const size_t oldCapacity = map.capacity(); EXPECT_GE(oldCapacity, param); - for (unsigned i = 0; i < param; ++i) + for (uint32_t i = 0; i < param; ++i) { map.put(i, i); EXPECT_EQ(i+1, map.size()); @@ -416,12 +416,12 @@ TEST_F(HashMapTest, operator_subscript_assign) EXPECT_EQ(val_in, val_out); /* Objects */ - MyStruct structKey; + MyStruct structKey{}; std::memset(&structKey, 0, sizeof(structKey)); // make valgrind happy structKey.a = 1; - MyStruct structValue; + MyStruct structValue{}; structValue.a = 13; - MyStruct structReturn; + MyStruct structReturn{}; HashMap tab; tab[structKey] = structValue; @@ -537,7 +537,7 @@ TEST_F(HashMapTest, TestRemove) EXPECT_EQ(2u, newmap.size()); EXPECT_EQ(newmap.end(), newmap.find(2)); - int32_t value; + int32_t value = 0; EXPECT_TRUE(newmap.remove(3, &value)); EXPECT_EQ(1u, newmap.size()); EXPECT_EQ(30, value); @@ -657,7 +657,7 @@ TEST_F(HashMapTest, TestConstIterator2) TEST_F(HashMapTest, IteratorPrimitiveKeyComplexValue) { - MyStruct testStruct; + MyStruct testStruct{}; testStruct.a = 1; testStruct.b = -1; @@ -687,7 +687,7 @@ TEST_F(HashMapTest, IteratorPrimitiveKeyComplexValue) TEST_F(HashMapTest, IteratorPrimitiveKeyComplexPtrValue) { - MyStruct testStruct; + MyStruct testStruct{}; testStruct.a = 1; testStruct.b = -1; @@ -718,7 +718,7 @@ TEST_F(HashMapTest, IteratorPrimitiveKeyComplexPtrValue) TEST_F(HashMapTest, IteratorMethodCall) { - MyStruct testStruct; + MyStruct testStruct{}; testStruct.a = 1; testStruct.b = -1; @@ -868,20 +868,20 @@ TEST_F(HashMapTest, swapGlobal) TEST_F(HashMapTest, basicRefCountLifecycle) { - expectRefCnt(0); + ExpectRefCnt(0); { HashMap ht; - expectRefCnt(0); + ExpectRefCnt(0); // add stuff ht.put(RCKey(1), RCValue(2)); - expectRefCnt(1); + ExpectRefCnt(1); ht.put(RCKey(2), RCValue(3)); - expectRefCnt(2); + ExpectRefCnt(2); // overwrite ht.put(RCKey(1), RCValue(4)); - expectRefCnt(2); + ExpectRefCnt(2); // get auto it = ht.find(RCKey(1)); @@ -893,10 +893,10 @@ TEST_F(HashMapTest, basicRefCountLifecycle) ht.remove(RCKey(2), &v); EXPECT_EQ(RCValue(3u), v); } - expectRefCnt(1); + ExpectRefCnt(1); } // destructor destructs all - expectRefCnt(0); + ExpectRefCnt(0); } TEST_F(HashMapTest, clearDestructsAllElements) @@ -905,9 +905,9 @@ TEST_F(HashMapTest, clearDestructsAllElements) ht.put(RCKey(1), RCValue(2)); ht.put(RCKey(2), RCValue(3)); ht.put(RCKey(3), RCValue(4)); - expectRefCnt(3); + ExpectRefCnt(3); ht.clear(); - expectRefCnt(0); + ExpectRefCnt(0); } TEST_F(HashMapTest, copyCtorCopyConstructsObjects) @@ -916,13 +916,13 @@ TEST_F(HashMapTest, copyCtorCopyConstructsObjects) ht.put(RCKey(1), RCValue(2)); ht.put(RCKey(2), RCValue(3)); ht.put(RCKey(3), RCValue(4)); - expectRefCnt(3); + ExpectRefCnt(3); HashMap ht2(ht); - expectRefCnt(6); + ExpectRefCnt(6); ht.clear(); - expectRefCnt(3); + ExpectRefCnt(3); } TEST_F(HashMapTest, assignmentCopiesObjects) @@ -933,30 +933,30 @@ TEST_F(HashMapTest, assignmentCopiesObjects) ht.put(RCKey(1), RCValue(2)); ht.put(RCKey(2), RCValue(3)); ht.put(RCKey(3), RCValue(4)); - expectRefCnt(3); + ExpectRefCnt(3); ht2 = ht; - expectRefCnt(3 + 3); + ExpectRefCnt(3 + 3); } - expectRefCnt(3); + ExpectRefCnt(3); } TEST_F(HashMapTest, arrayAccessOperatorConstructsNewElementIfNotExisting) { HashMap ht; ht.put(RCKey(1), RCValue(2)); - expectRefCnt(1); + ExpectRefCnt(1); ht[RCKey(2)] = RCValue(3); - expectRefCnt(2); + ExpectRefCnt(2); } TEST_F(HashMapTest, arrayAccessOperatorReusesElementIfExisting) { HashMap ht; ht.put(RCKey(1), RCValue(2)); - expectRefCnt(1); + ExpectRefCnt(1); ht[RCKey(1)] = RCValue(3); - expectRefCnt(1); + ExpectRefCnt(1); } TEST_F(HashMapTest, swapKeepsNumberOfObjectsUnchanged) @@ -966,9 +966,9 @@ TEST_F(HashMapTest, swapKeepsNumberOfObjectsUnchanged) first.put(RCKey(1), RCValue(2)); first.put(RCKey(2), RCValue(3)); second.put(RCKey(3), RCValue(4)); - expectRefCnt(3); + ExpectRefCnt(3); first.swap(second); - expectRefCnt(3); + ExpectRefCnt(3); } TEST_F(HashMapTest, canConstructWithZeroCapacity) @@ -986,4 +986,4 @@ TEST_F(HashMapTest, constructWithCapacityHasAtLeastDefaultCapacity) EXPECT_GE(ht.capacity(), HM::DefaultHashMapCapacity); } -} // namespace ramses_internal +} // namespace ramses::internal diff --git a/tests/unittests/framework/PlatformAbstraction/HashSetTest.cpp b/tests/unittests/framework/PlatformAbstraction/HashSetTest.cpp new file mode 100644 index 000000000..9ad05aa4b --- /dev/null +++ b/tests/unittests/framework/PlatformAbstraction/HashSetTest.cpp @@ -0,0 +1,393 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "ComplexTestType.h" +#include +#include + +namespace ramses::internal +{ + using TestType = ComplexTestType<>; + + TEST(HashSet, Constructor_Default) + { + HashSet set; + (void)set; + } + + TEST(HashSet, copyConstructor) + { + HashSet set; + set.put(1); + set.put(2); + set.put(3); + + HashSet set2 = set; // copy + + set.clear(); + + EXPECT_TRUE(set2.contains(1)); + EXPECT_TRUE(set2.contains(2)); + EXPECT_TRUE(set2.contains(3)); + } + + TEST(HashSet, put) + { + int32_t value2 = 10; + int32_t value = 5; + + HashSeth1; + + auto it1 = h1.put(value); + auto it2 = h1.put(value2); + auto it3 = h1.put(value2); + + EXPECT_NE(h1.end(), it1); + EXPECT_NE(h1.end(), it2); + EXPECT_NE(h1.end(), it3); + + EXPECT_EQ(h1.find(value), it1); + EXPECT_EQ(h1.find(value2), it2); + EXPECT_EQ(it2, it3); + } + + TEST(HashSet, count) + { + int32_t value2 = 10; + int32_t value = 5; + HashSeth1; + + EXPECT_EQ(0u, h1.size()); + h1.put(value); + h1.put(value2); + + EXPECT_EQ(2u, h1.size()); + EXPECT_TRUE(h1.remove(value2)); + EXPECT_EQ(1u, h1.size()); + } + + TEST(HashSet, clear) + { + int32_t value = 5; + int32_t value2 = 6; + + HashSeth1; + h1.put(value); + h1.put(value2); + + EXPECT_EQ(2u, h1.size()); + h1.clear(); + EXPECT_EQ(0u, h1.size()); + } + + TEST(HashSet, remove) + { + int32_t value = 5; + int32_t value2 = 6; + + HashSet h1; + + h1.put(value); + EXPECT_FALSE(h1.remove(value2)); + + h1.put(value2); + EXPECT_EQ(2u, h1.size()); + + EXPECT_TRUE(h1.remove(value2)); + EXPECT_EQ(1u, h1.size()); + } + + TEST(HashSet, hasElement) + { + int32_t value = 5; + int32_t value2 = 6; + + HashSet h1; + + h1.put(value); + EXPECT_FALSE(h1.contains(value2)); + + h1.put(value2); + EXPECT_TRUE(h1.contains(value2)); + } + + TEST(HashSet, findsContainedElement) + { + HashSet container; + container.put(5); + container.put(6); + container.put(7); + + ASSERT_NE(container.end(), container.find(5)); + ASSERT_NE(container.end(), container.find(6)); + ASSERT_NE(container.end(), container.find(7)); + EXPECT_EQ(5, *container.find(5)); + EXPECT_EQ(6, *container.find(6)); + EXPECT_EQ(7, *container.find(7)); + + // const access + const HashSet& ccontainer = container; + ASSERT_NE(ccontainer.end(), ccontainer.find(5)); + ASSERT_NE(ccontainer.end(), ccontainer.find(6)); + ASSERT_NE(ccontainer.end(), ccontainer.find(7)); + EXPECT_EQ(5, *ccontainer.find(5)); + EXPECT_EQ(6, *ccontainer.find(6)); + EXPECT_EQ(7, *ccontainer.find(7)); + } + + TEST(HashSet, doesNotFindElementNotContained) + { + HashSet container; + container.put(5); + container.put(6); + container.put(7); + + EXPECT_EQ(container.end(), container.find(55)); + EXPECT_EQ(container.end(), container.find(99)); + + // const access + const HashSet& ccontainer = container; + EXPECT_EQ(ccontainer.end(), ccontainer.find(55)); + EXPECT_EQ(ccontainer.end(), ccontainer.find(99)); + } + + TEST(HashSetIterator, hasNext) + { + int32_t value = 10; + int32_t value2 = 12; + + HashSet h1; + HashSet::Iterator it = h1.begin(); + + EXPECT_EQ(it, h1.end()); + + h1.put(value); + h1.put(value2); + + it = h1.begin(); + EXPECT_NE(it, h1.end()); + } + + TEST(HashSetIterator, next) + { + int32_t value = 10; + int32_t value2 = 12; + + HashSet h1; + + int32_t check_value = 0; + int32_t check_value2 = 0; + + HashSet::Iterator it = h1.begin(); + + EXPECT_TRUE(it == h1.end()); + h1.put(value); + h1.put(value2); + + it = h1.begin(); + check_value = *it; + EXPECT_TRUE(check_value == value || check_value == value2); + + it++; + check_value = *it; + EXPECT_TRUE(check_value == value || check_value == value2); + + EXPECT_NE(check_value, check_value2); + } + + TEST(HashSetIterator, ForEach) + { + HashSet hashSet; + + hashSet.put(32); + hashSet.put(43); + hashSet.put(44); + + HashSet testHashSet; + for (auto el : hashSet) + { + testHashSet.put(el); + } + + EXPECT_TRUE(testHashSet.contains(32)); + EXPECT_TRUE(testHashSet.contains(43)); + EXPECT_TRUE(testHashSet.contains(44)); + } + + TEST(HashSet, returnedIteratorPointsToNextElementAfterDeletion) + { + HashSet set; + + set.put(1); + set.put(2); + set.put(3); + + // point to middle element + HashSet::Iterator i1 = set.begin(); + HashSet::Iterator i2 = set.begin(); + ++i2; + HashSet::Iterator i3 = set.begin(); + ++i3; + ++i3; + + // all iterators point to different elements + // no assumption which value each iterator points to, because no order is defined + ASSERT_NE(*i1, *i2); + ASSERT_NE(*i2, *i3); + ASSERT_NE(*i1, *i3); + + i2 = set.remove(i2); + + // i2 now points at next element -> i3 + EXPECT_EQ(i2, i3); + + i1 = set.remove(i1); + EXPECT_EQ(i1, i3); + } + + TEST(HashSet, canRemoveElementsDuringCycle) + { + HashSet set; + + set.put(1); + set.put(2); + set.put(3); + + HashSet::Iterator iter = set.begin(); + while (iter != set.end()) + { + iter = set.remove(iter); + } + + EXPECT_EQ(0u, set.size()); + } + + TEST(HashSet, HashSetWithComplexType) + { + TestType::Reset(); + { + HashSet s; + std::vector v; + for (size_t i = 0; i < 20; ++i) + { + TestType ctt(i*10); + v.push_back(ctt); + s.put(ctt); + } + + EXPECT_EQ(v.size(), s.size()); + for (const auto& ctt : v) + { + EXPECT_TRUE(s.contains(ctt)); + } + } + EXPECT_EQ(TestType::dtor_count, TestType::ctor_count + TestType::copyctor_count); + } + + TEST(HashSet, swapMemberFunction) + { + HashSet first; + HashSet second; + + first.put(1); + first.put(2); + second.put(3); + + first.swap(second); + EXPECT_EQ(2u, second.size()); + EXPECT_EQ(1u, first.size()); + + EXPECT_TRUE(second.contains(1)); + EXPECT_TRUE(second.contains(2)); + EXPECT_TRUE(first.contains(3)); + } + + TEST(HashSet, swapGlobal) + { + HashSet first; + HashSet second; + + first.put(1); + first.put(2); + second.put(3); + + using std::swap; + swap(first, second); + EXPECT_EQ(2u, second.size()); + EXPECT_EQ(1u, first.size()); + } + + TEST(HashSet, TestMoveConstructor) + { + HashSet set1; + set1.put(1); + set1.put(2); + set1.put(3); + + HashSet set2(std::move(set1)); + + EXPECT_EQ(3u, set2.size()); + EXPECT_TRUE(set2.contains(1)); + EXPECT_TRUE(set2.contains(2)); + EXPECT_TRUE(set2.contains(3)); + } + + TEST(HashSet, TestAssign) + { + HashSet set1; + set1.put(1); + set1.put(2); + set1.put(3); + + HashSet set2; + set2 = set1; + + EXPECT_EQ(3u, set1.size()); + EXPECT_TRUE(set1.contains(1)); + EXPECT_TRUE(set1.contains(2)); + EXPECT_TRUE(set1.contains(3)); + + EXPECT_EQ(3u, set2.size()); + EXPECT_TRUE(set2.contains(1)); + EXPECT_TRUE(set2.contains(2)); + EXPECT_TRUE(set2.contains(3)); + } + + + TEST(HashSet, TestMoveAssign) + { + HashSet set1; + set1.put(1); + set1.put(2); + set1.put(3); + + HashSet set2; + set2 = std::move(set1); + + EXPECT_EQ(3u, set2.size()); + EXPECT_TRUE(set2.contains(1)); + EXPECT_TRUE(set2.contains(2)); + EXPECT_TRUE(set2.contains(3)); + } + + TEST(HashSet, TestReserve) + { + HashSet set(40); + EXPECT_GE(set.capacity(), 40u); + EXPECT_LE(set.capacity(), 2 * 40u); + + set.reserve(100); + EXPECT_GE(set.capacity(), 100u); + EXPECT_LE(set.capacity(), 2 * 100u); + + const size_t old = set.capacity(); + set.reserve(90); + EXPECT_EQ(old, set.capacity()); + } +} diff --git a/framework/PlatformAbstraction/test/HashTest.cpp b/tests/unittests/framework/PlatformAbstraction/HashTest.cpp similarity index 67% rename from framework/PlatformAbstraction/test/HashTest.cpp rename to tests/unittests/framework/PlatformAbstraction/HashTest.cpp index 89d6e9376..6374278ca 100644 --- a/framework/PlatformAbstraction/test/HashTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/HashTest.cpp @@ -6,7 +6,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/Hash.h" +#include "internal/PlatformAbstraction/Hash.h" #include "gtest/gtest.h" namespace @@ -29,13 +29,13 @@ namespace template<> struct std::hash { - size_t operator()(const SomeClass&) + size_t operator()(const SomeClass& /*unused*/) { return 123; } }; -namespace ramses_internal +namespace ramses::internal { TEST(HashCombine, usesSeedAndValue) { @@ -107,19 +107,19 @@ namespace ramses_internal SomeClass* ptr = &clazz; // everything must compile... - UNUSED(std::hash()(sometype1)); - UNUSED(std::hash()(sometype2)); - UNUSED(std::hash()(sometype3)); - UNUSED(std::hash()(sometype4)); - UNUSED(std::hash()(sometype5)); - UNUSED(std::hash()(sometype6)); - UNUSED(std::hash()(sometype7)); - UNUSED(std::hash()(sometype8)); - UNUSED(std::hash()(sometype12)); - UNUSED(std::hash()(sometype14)); - UNUSED(std::hash()(clazz)); - UNUSED(std::hash()(ptr)); - UNUSED(std::hash()(ESomeenum_MEMBER0)); - UNUSED(std::hash()(SomeEnumClass::MEMBER)); + [[maybe_unused]] auto hash1 = std::hash()(sometype1); + [[maybe_unused]] auto hash2 = std::hash()(sometype2); + [[maybe_unused]] auto hash3 = std::hash()(sometype3); + [[maybe_unused]] auto hash4 = std::hash()(sometype4); + [[maybe_unused]] auto hash5 = std::hash()(sometype5); + [[maybe_unused]] auto hash6 = std::hash()(sometype6); + [[maybe_unused]] auto hash7 = std::hash()(sometype7); + [[maybe_unused]] auto hash8 = std::hash()(sometype8); + [[maybe_unused]] auto hash9 = std::hash()(sometype12); + [[maybe_unused]] auto hash10 = std::hash()(sometype14); + [[maybe_unused]] auto hash11 = std::hash()(clazz); + [[maybe_unused]] auto hash12 = std::hash()(ptr); + [[maybe_unused]] auto hash13 = std::hash()(ESomeenum_MEMBER0); + [[maybe_unused]] auto hash14 = std::hash()(SomeEnumClass::MEMBER); } } diff --git a/framework/PlatformAbstraction/test/HeapArrayTest.cpp b/tests/unittests/framework/PlatformAbstraction/HeapArrayTest.cpp similarity index 97% rename from framework/PlatformAbstraction/test/HeapArrayTest.cpp rename to tests/unittests/framework/PlatformAbstraction/HeapArrayTest.cpp index 3cc69753d..a55a403af 100644 --- a/framework/PlatformAbstraction/test/HeapArrayTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/HeapArrayTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Collections/HeapArray.h" +#include "internal/PlatformAbstraction/Collections/HeapArray.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { template class AHeapArray : public testing::Test diff --git a/framework/PlatformAbstraction/test/IOStreamTest.cpp b/tests/unittests/framework/PlatformAbstraction/IOStreamTest.cpp similarity index 99% rename from framework/PlatformAbstraction/test/IOStreamTest.cpp rename to tests/unittests/framework/PlatformAbstraction/IOStreamTest.cpp index 14aa7457e..fea9552a4 100644 --- a/framework/PlatformAbstraction/test/IOStreamTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/IOStreamTest.cpp @@ -10,7 +10,7 @@ #include "gtest/gtest.h" #include -namespace ramses_internal +namespace ramses::internal { class AIInutOutputStream : public ::testing::Test, public IOStreamTesterBase { diff --git a/framework/PlatformAbstraction/test/MathTest.cpp b/tests/unittests/framework/PlatformAbstraction/MathTest.cpp similarity index 92% rename from framework/PlatformAbstraction/test/MathTest.cpp rename to tests/unittests/framework/PlatformAbstraction/MathTest.cpp index 95dde2c86..845cd62f6 100644 --- a/framework/PlatformAbstraction/test/MathTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/MathTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformMath.h" +#include "internal/PlatformAbstraction/PlatformMath.h" #include -namespace ramses_internal +namespace ramses::internal { TEST(PlatformMath, PI) { diff --git a/tests/unittests/framework/PlatformAbstraction/NumericLimitsTest.cpp b/tests/unittests/framework/PlatformAbstraction/NumericLimitsTest.cpp new file mode 100644 index 000000000..761eb029a --- /dev/null +++ b/tests/unittests/framework/PlatformAbstraction/NumericLimitsTest.cpp @@ -0,0 +1,56 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" + +#include +#include + +namespace ramses::internal +{ + TEST(NumericLimits, ensureAvailableForAlltypes) + { + (void)std::numeric_limits::max(); + (void)std::numeric_limits::max(); + + (void)std::numeric_limits::max(); + (void)std::numeric_limits::max(); + + (void)std::numeric_limits::max(); + (void)std::numeric_limits::max(); + + (void)std::numeric_limits::max(); + (void)std::numeric_limits::max(); + + (void)std::numeric_limits::max(); + (void)std::numeric_limits::max(); + + (void)std::numeric_limits::max(); + (void)std::numeric_limits::max(); + } + + TEST(NumericLimits, testLimits) + { + EXPECT_EQ(std::numeric_limits::max(), static_cast(0x7fffffffffffffffLL)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(0x8000000000000000LL)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(0xffffffffffffffffULL)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(0x0)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(0x7fffffff)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(0x80000000)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(0xffffffff)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(0x0)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(32767)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(-32768)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(65535)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(0)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(127)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(-128)); + EXPECT_EQ(std::numeric_limits::max(), static_cast(255)); + EXPECT_EQ(std::numeric_limits::min(), static_cast(0)); + } +} diff --git a/framework/PlatformAbstraction/test/PlatformEndianess.cpp b/tests/unittests/framework/PlatformAbstraction/PlatformEndianess.cpp similarity index 94% rename from framework/PlatformAbstraction/test/PlatformEndianess.cpp rename to tests/unittests/framework/PlatformAbstraction/PlatformEndianess.cpp index 10d5b1f1d..3ac47f39e 100644 --- a/framework/PlatformAbstraction/test/PlatformEndianess.cpp +++ b/tests/unittests/framework/PlatformAbstraction/PlatformEndianess.cpp @@ -6,8 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" +#include #ifdef _WIN32 #include @@ -15,7 +15,7 @@ #include #endif -namespace ramses_internal +namespace ramses::internal { TEST(Endianess, isLittleEndianOnAllRamsesSystems) { diff --git a/framework/PlatformAbstraction/test/PlatformEnvironmentVariablesTest.cpp b/tests/unittests/framework/PlatformAbstraction/PlatformEnvironmentVariablesTest.cpp similarity index 93% rename from framework/PlatformAbstraction/test/PlatformEnvironmentVariablesTest.cpp rename to tests/unittests/framework/PlatformAbstraction/PlatformEnvironmentVariablesTest.cpp index c591b0dea..325d5d061 100644 --- a/framework/PlatformAbstraction/test/PlatformEnvironmentVariablesTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/PlatformEnvironmentVariablesTest.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformEnvironmentVariables.h" +#include "internal/PlatformAbstraction/PlatformEnvironmentVariables.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { TEST(EnvironmentVariables, getTest) { diff --git a/framework/PlatformAbstraction/test/PlatformSignalTest.cpp b/tests/unittests/framework/PlatformAbstraction/PlatformSignalTest.cpp similarity index 87% rename from framework/PlatformAbstraction/test/PlatformSignalTest.cpp rename to tests/unittests/framework/PlatformAbstraction/PlatformSignalTest.cpp index c5ea1a812..4dbc80888 100644 --- a/framework/PlatformAbstraction/test/PlatformSignalTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/PlatformSignalTest.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformSignal.h" +#include "internal/PlatformAbstraction/PlatformSignal.h" #include "gtest/gtest.h" #include -namespace ramses_internal +namespace ramses::internal { namespace { std::atomic handlerCalled {false}; - void handler(int32_t) + void handler(int32_t /*unused*/) { handlerCalled = true; } diff --git a/tests/unittests/framework/PlatformAbstraction/PlatformStringUtilsTest.cpp b/tests/unittests/framework/PlatformAbstraction/PlatformStringUtilsTest.cpp new file mode 100644 index 000000000..e03cbf714 --- /dev/null +++ b/tests/unittests/framework/PlatformAbstraction/PlatformStringUtilsTest.cpp @@ -0,0 +1,34 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/PlatformAbstraction/PlatformStringUtils.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + TEST(APlatformStringUtils, CopyWithEnoughBuffer) + { + char string1[20] = "My String"; + char string2[30]; + PlatformStringUtils::Copy(string2, sizeof(string2), string1); + EXPECT_STREQ(string1, string2); + } + + TEST(APlatformStringUtils, CopyWithTruncation) + { + char string1[20] = "My String"; + char string2[20]; + std::memset(string2, 42, sizeof(string2)); + PlatformStringUtils::Copy(string2, 4, string1); + EXPECT_STREQ("My ", string2); + + // check that only 4 bytes are written, an no more + for (size_t i = 4; i < sizeof(string2); ++i) + EXPECT_EQ(42, string2[i]); + } +} diff --git a/framework/PlatformAbstraction/test/PlatformThreadTest.cpp b/tests/unittests/framework/PlatformAbstraction/PlatformThreadTest.cpp similarity index 97% rename from framework/PlatformAbstraction/test/PlatformThreadTest.cpp rename to tests/unittests/framework/PlatformAbstraction/PlatformThreadTest.cpp index c7e03fa10..abca5d17a 100644 --- a/framework/PlatformAbstraction/test/PlatformThreadTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/PlatformThreadTest.cpp @@ -6,11 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PlatformAbstraction/PlatformThread.h" -#include "framework_common_gmock_header.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { namespace { diff --git a/framework/PlatformAbstraction/test/PlatformTimeTest.cpp b/tests/unittests/framework/PlatformAbstraction/PlatformTimeTest.cpp similarity index 97% rename from framework/PlatformAbstraction/test/PlatformTimeTest.cpp rename to tests/unittests/framework/PlatformAbstraction/PlatformTimeTest.cpp index 3ec3541a5..9a0fcd264 100644 --- a/framework/PlatformAbstraction/test/PlatformTimeTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/PlatformTimeTest.cpp @@ -7,11 +7,11 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "PlatformAbstraction/PlatformTime.h" -#include "PlatformAbstraction/synchronized_clock.h" +#include "internal/PlatformAbstraction/PlatformTime.h" +#include "internal/PlatformAbstraction/synchronized_clock.h" #include -namespace ramses_internal +namespace ramses::internal { TEST(PlatformTimeSynchronized, nowNotZero) { diff --git a/framework/PlatformAbstraction/test/StringOutputStreamTest.cpp b/tests/unittests/framework/PlatformAbstraction/StringOutputStreamTest.cpp similarity index 83% rename from framework/PlatformAbstraction/test/StringOutputStreamTest.cpp rename to tests/unittests/framework/PlatformAbstraction/StringOutputStreamTest.cpp index 4bf4f6087..950dbae8c 100644 --- a/framework/PlatformAbstraction/test/StringOutputStreamTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/StringOutputStreamTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Collections/StringOutputStream.h" -#include "framework_common_gmock_header.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" #include "UnsafeTestMemoryHelpers.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { TEST(AStringOutputStream, WriteFloat) { @@ -250,4 +249,29 @@ namespace ramses_internal stream.formatTo("{}", 789); EXPECT_EQ("123456789", stream.data()); } + + TEST(AStringOutputStream, CanStreamStringviewWhichCausedProblemsBefore) + { + const std::string_view str{ + "#version 300 es\n" + "\n" + "precision mediump float;\n" + "\n" + "in vec3 a_Position;\n" + "\n" + "uniform mat4 u_MVPMatrix;\n" + "\n" + "// fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk" + "// fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk" + "// fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk // fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk" + "// fdahfjkds ahfjkd hjfkd hjkf dhjskf hjdsk void main() {\n" + "\n" + "gl_Position = u_MVPMatrix * vec4(a_Position, 1.0); }" + }; + + StringOutputStream stream; + stream.formatTo("{}", str); + stream << str; + EXPECT_EQ(1718u, stream.size()); + } } diff --git a/framework/PlatformAbstraction/test/VectorTest.cpp b/tests/unittests/framework/PlatformAbstraction/VectorTest.cpp similarity index 96% rename from framework/PlatformAbstraction/test/VectorTest.cpp rename to tests/unittests/framework/PlatformAbstraction/VectorTest.cpp index 10381d843..93b15cf9b 100644 --- a/framework/PlatformAbstraction/test/VectorTest.cpp +++ b/tests/unittests/framework/PlatformAbstraction/VectorTest.cpp @@ -7,9 +7,9 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "Collections/Vector.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" -namespace ramses_internal +namespace ramses::internal { TEST(AVector, isInitiallyEmpty) { diff --git a/framework/Ramsh/test/RamshTest.cpp b/tests/unittests/framework/Ramsh/RamshTest.cpp similarity index 70% rename from framework/Ramsh/test/RamshTest.cpp rename to tests/unittests/framework/Ramsh/RamshTest.cpp index c486fa7e6..a9213f69d 100644 --- a/framework/Ramsh/test/RamshTest.cpp +++ b/tests/unittests/framework/Ramsh/RamshTest.cpp @@ -6,28 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Ramsh/Ramsh.h" -#include "Ramsh/RamshCommandArguments.h" -#include "Ramsh/RamshCommunicationChannelConsole.h" -#include "Ramsh/RamshCommandExit.h" -#include "framework_common_gmock_header.h" +#include "internal/Ramsh/Ramsh.h" +#include "internal/Ramsh/RamshCommandArguments.h" +#include "internal/Ramsh/RamshCommunicationChannelConsole.h" +#include "internal/Ramsh/RamshCommandExit.h" +#include "internal/Ramsh/RamshCommandArgumentsConverter.h" #include "gmock/gmock.h" #include -#include "Ramsh/RamshTools.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Utils/RamsesLogger.h" +#include "internal/Ramsh/RamshTools.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/Utils/RamsesLogger.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; template struct TestTuple { - T1 a1; - T2 a2; - T3 a3; - T4 a4; + T1 a1{}; + T2 a2{}; + T3 a3{}; + T4 a4{}; }; template @@ -79,7 +79,7 @@ namespace ramses_internal { // help is built-in command of Ramsh // trigger command @ Ramsh - input.push_back("help"); + input.emplace_back("help"); EXPECT_TRUE(ramsh.execute(input)); } @@ -90,7 +90,7 @@ namespace ramses_internal EXPECT_TRUE(ramsh.add(cmd)); // trigger command @ Ramsh - input.push_back("ABC"); + input.emplace_back("ABC"); EXPECT_TRUE(ramsh.execute(input)); } @@ -103,9 +103,9 @@ namespace ramses_internal EXPECT_TRUE(ramsh.add(cmd)); // trigger command @ Ramsh - input.push_back("long"); - input.push_back("arg1"); - input.push_back("arg2"); + input.emplace_back("long"); + input.emplace_back("arg1"); + input.emplace_back("arg2"); EXPECT_TRUE(ramsh.execute(input)); } @@ -118,7 +118,7 @@ namespace ramses_internal EXPECT_TRUE(ramsh.add(cmd)); // trigger command @ Ramsh - input.push_back("thisisalongername"); + input.emplace_back("thisisalongername"); EXPECT_TRUE(ramsh.execute(input)); } @@ -131,14 +131,14 @@ namespace ramses_internal EXPECT_TRUE(ramsh.add(cmd)); // trigger command @ Ramsh - input.push_back("X"); + input.emplace_back("X"); EXPECT_TRUE(ramsh.execute(input)); } TEST_F(RamshAPI, triggerInvalidCommand) { // trigger command @ Ramsh - input.push_back("invalid"); + input.emplace_back("invalid"); EXPECT_FALSE(ramsh.execute(input)); } @@ -168,6 +168,77 @@ namespace ramses_internal EXPECT_EQ(std::string("test 'argument'"), result3[5]); } + TEST_F(RamshAPI, parseUInt16) + { + uint16_t value = 0; + EXPECT_TRUE(ArgumentConverter::tryConvert("-1", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + EXPECT_TRUE(ArgumentConverter::tryConvert("0", value)); + EXPECT_EQ(0u, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("65535", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + } + + TEST_F(RamshAPI, parseInt16) + { + int16_t value = 0; + EXPECT_TRUE(ArgumentConverter::tryConvert("-1", value)); + EXPECT_EQ(-1, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("0", value)); + EXPECT_EQ(0, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("32767", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + EXPECT_TRUE(ArgumentConverter::tryConvert("-32768", value)); + EXPECT_EQ(std::numeric_limits::min(), value); + } + + TEST_F(RamshAPI, parseUInt32) + { + uint32_t value = 0; + EXPECT_TRUE(ArgumentConverter::tryConvert("-1", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + EXPECT_TRUE(ArgumentConverter::tryConvert("0", value)); + EXPECT_EQ(0u, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("4294967295", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + } + + TEST_F(RamshAPI, parseInt32) + { + int32_t value = 0; + EXPECT_TRUE(ArgumentConverter::tryConvert("-1", value)); + EXPECT_EQ(-1, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("0", value)); + EXPECT_EQ(0, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("2147483647", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + EXPECT_TRUE(ArgumentConverter::tryConvert("-2147483648", value)); + EXPECT_EQ(std::numeric_limits::min(), value); + } + + TEST_F(RamshAPI, parseUInt64) + { + uint64_t value = 0; + EXPECT_TRUE(ArgumentConverter::tryConvert("-1", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + EXPECT_TRUE(ArgumentConverter::tryConvert("0", value)); + EXPECT_EQ(0u, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("18446744073709551615", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + } + + TEST_F(RamshAPI, parseInt64) + { + int64_t value = 0; + EXPECT_TRUE(ArgumentConverter::tryConvert("-1", value)); + EXPECT_EQ(-1, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("0", value)); + EXPECT_EQ(0, value); + EXPECT_TRUE(ArgumentConverter::tryConvert("9223372036854775807", value)); + EXPECT_EQ(std::numeric_limits::max(), value); + EXPECT_TRUE(ArgumentConverter::tryConvert("-9223372036854775808", value)); + EXPECT_EQ(std::numeric_limits::min(), value); + } TEST_F(RamshAPI, typedCommand) { @@ -189,13 +260,13 @@ namespace ramses_internal EXPECT_TRUE(ramsh.add(typedCmd)); - input.push_back("typed"); + input.emplace_back("typed"); // arguments without flags - input.push_back("44"); - input.push_back("-bool"); - input.push_back("foobar"); - input.push_back("1.337"); + input.emplace_back("44"); + input.emplace_back("-bool"); + input.emplace_back("foobar"); + input.emplace_back("1.337"); EXPECT_TRUE(ramsh.execute(input)); @@ -208,16 +279,16 @@ namespace ramses_internal input.clear(); - input.push_back("typed"); + input.emplace_back("typed"); // scrambled arguments - input.push_back("-string"); - input.push_back("foo"); - input.push_back("-bool"); - input.push_back("-float"); - input.push_back("-1337"); - input.push_back("-int"); - input.push_back("123"); + input.emplace_back("-string"); + input.emplace_back("foo"); + input.emplace_back("-bool"); + input.emplace_back("-float"); + input.emplace_back("-1337"); + input.emplace_back("-int"); + input.emplace_back("123"); EXPECT_TRUE(ramsh.execute(input)); @@ -230,26 +301,26 @@ namespace ramses_internal input.clear(); - input.push_back("typed"); + input.emplace_back("typed"); // invalid argument - input.push_back("true"); - input.push_back("-bool"); - input.push_back("foobar"); - input.push_back("-floata"); - input.push_back("abcde"); + input.emplace_back("true"); + input.emplace_back("-bool"); + input.emplace_back("foobar"); + input.emplace_back("-floata"); + input.emplace_back("abcde"); EXPECT_FALSE(ramsh.execute(input)); input.clear(); - input.push_back("typed"); + input.emplace_back("typed"); // missing argument - input.push_back("-bool"); - input.push_back("foobar"); - input.push_back("-pair"); - input.push_back("1.337;1,3,3,7"); + input.emplace_back("-bool"); + input.emplace_back("foobar"); + input.emplace_back("-pair"); + input.emplace_back("1.337;1,3,3,7"); EXPECT_FALSE(ramsh.execute(input)); } @@ -279,7 +350,7 @@ namespace ramses_internal EXPECT_TRUE(ramsh.add(typedCmd)); // no input, just default values - input.push_back("typed"); + input.emplace_back("typed"); EXPECT_TRUE(ramsh.execute(input)); @@ -293,8 +364,8 @@ namespace ramses_internal input.clear(); // flipped bool - input.push_back("typed"); - input.push_back("-bool"); + input.emplace_back("typed"); + input.emplace_back("-bool"); EXPECT_TRUE(ramsh.execute(input)); @@ -308,14 +379,14 @@ namespace ramses_internal input.clear(); // partially set arguments - input.push_back("typed"); + input.emplace_back("typed"); - input.push_back("-float"); - input.push_back("-1337.1337"); - input.push_back("-string"); - input.push_back("foo"); - input.push_back("-int"); - input.push_back("90000"); + input.emplace_back("-float"); + input.emplace_back("-1337.1337"); + input.emplace_back("-string"); + input.emplace_back("foo"); + input.emplace_back("-int"); + input.emplace_back("90000"); EXPECT_TRUE(ramsh.execute(input)); @@ -345,7 +416,7 @@ namespace ramses_internal }; bool value = false; - input.push_back("c"); + input.emplace_back("c"); { auto refCmd = std::make_shared(value); refCmd->registerKeyword("c"); @@ -379,7 +450,7 @@ namespace ramses_internal std::unique_ptr inputProvider; }; - class RamshCommunicationChannelConsoleTestThread : public ramses_internal::Runnable + class RamshCommunicationChannelConsoleTestThread : public Runnable { public: explicit RamshCommunicationChannelConsoleTestThread() @@ -387,17 +458,15 @@ namespace ramses_internal GetRamsesLogger(); } - ~RamshCommunicationChannelConsoleTestThread() override - { - } + ~RamshCommunicationChannelConsoleTestThread() override = default; void run() override { - logSomeMessage(); + LogSomeMessage(); } protected: - void logSomeMessage() + static void LogSomeMessage() { StringOutputStream stream; stream << "This is some very interesting log message used for testing purpose"; @@ -411,9 +480,9 @@ namespace ramses_internal std::string input = "help\nbla"; EXPECT_CALL(ramsh, execute(std::vector{"help"})); - for (size_t i = 0; i < input.size(); i++) + for (char i : input) { - inputProvider->processInput(input[i]); + inputProvider->processInput(i); } } @@ -422,15 +491,15 @@ namespace ramses_internal // thread_sanitizer TEST_F(RamshCommunicationChannelConsoleTest, checkPossibleLogDeadlock) { - ramses_internal::PlatformThread thread("RamshConTest"); + PlatformThread thread("RamshConTest"); RamshCommunicationChannelConsoleTestThread runnable; thread.start(runnable); std::string input = "help\n"; - for (size_t i = 0; i < input.size(); i++) + for (char i : input) { - inputProvider->processInput(input[i]); + inputProvider->processInput(i); } thread.join(); @@ -443,7 +512,7 @@ namespace ramses_internal { std::vector input; for (const auto& inp : inpList) - input.push_back(inp); + input.emplace_back(inp); return input; } diff --git a/tests/unittests/framework/SceneGraph/Resource/ArrayResourceTest.cpp b/tests/unittests/framework/SceneGraph/Resource/ArrayResourceTest.cpp new file mode 100644 index 000000000..3ee9aabfa --- /dev/null +++ b/tests/unittests/framework/SceneGraph/Resource/ArrayResourceTest.cpp @@ -0,0 +1,36 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" + +namespace ramses::internal +{ + TEST(AArrayResource, hasCorrectArrayType) + { + ArrayResource vertArray(EResourceType::VertexArray, 5, EDataType::Vector3F, nullptr, {}); + ArrayResource indArray(EResourceType::IndexArray, 5, EDataType::Vector3F, nullptr, {}); + + EXPECT_EQ(EResourceType::VertexArray, vertArray.getTypeID()); + EXPECT_EQ(EResourceType::IndexArray, indArray.getTypeID()); + } + + TEST(AArrayResource, hasCorrectElementCount) + { + ArrayResource vertArray(EResourceType::VertexArray, 5, EDataType::Vector3F, nullptr, {}); + + EXPECT_EQ(5u, vertArray.getElementCount()); + } + + TEST(AArrayResource, hasCorrectElementType) + { + ArrayResource vertArray(EResourceType::VertexArray, 5, EDataType::Vector3F, nullptr, {}); + + EXPECT_EQ(EDataType::Vector3F, vertArray.getElementType()); + } +} diff --git a/framework/SceneGraph/Resource/test/CityhashTest.cpp b/tests/unittests/framework/SceneGraph/Resource/CityhashTest.cpp similarity index 97% rename from framework/SceneGraph/Resource/test/CityhashTest.cpp rename to tests/unittests/framework/SceneGraph/Resource/CityhashTest.cpp index 0772611b0..7c98d1998 100644 --- a/framework/SceneGraph/Resource/test/CityhashTest.cpp +++ b/tests/unittests/framework/SceneGraph/Resource/CityhashTest.cpp @@ -5,13 +5,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" #include // test for different data sizes inspired by internal cityhash test (city-test.cc) -namespace ramses_internal +namespace ramses::internal { static const uint32_t numTests = 20; static const uint32_t dataSize = 1 << numTests; diff --git a/framework/SceneGraph/Resource/test/EffectResourceTest.cpp b/tests/unittests/framework/SceneGraph/Resource/EffectResourceTest.cpp similarity index 76% rename from framework/SceneGraph/Resource/test/EffectResourceTest.cpp rename to tests/unittests/framework/SceneGraph/Resource/EffectResourceTest.cpp index 246518fbb..6c4968f61 100644 --- a/framework/SceneGraph/Resource/test/EffectResourceTest.cpp +++ b/tests/unittests/framework/SceneGraph/Resource/EffectResourceTest.cpp @@ -7,13 +7,12 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "framework_common_gmock_header.h" -#include "Resource/EffectResource.h" -#include "Utils/BinaryOutputStream.h" -#include "Utils/BinaryInputStream.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" #include -namespace ramses_internal +namespace ramses::internal { class AEffectResource : public testing::Test { @@ -32,12 +31,12 @@ namespace ramses_internal attributeInputs.push_back(attributeA); } - std::unique_ptr serializeDeserialize(const EffectResource& effectResource, std::string_view name) + static std::unique_ptr SerializeDeserialize(const EffectResource& effectResource, std::string_view name) { BinaryOutputStream outStream; effectResource.serializeResourceMetadataToStream(outStream); BinaryInputStream inStream(outStream.getData()); - std::unique_ptr resource = EffectResource::CreateResourceFromMetadataStream(inStream, effectResource.getCacheFlag(), name); + std::unique_ptr resource = EffectResource::CreateResourceFromMetadataStream(inStream, name); if (!resource) { return nullptr; @@ -53,14 +52,14 @@ namespace ramses_internal TEST_F(AEffectResource, canBeCreatedWithName) { - EffectResource effect("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "myname", ResourceCacheFlag(0u)); + EffectResource effect("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "myname"); EXPECT_EQ(std::string{"myname"}, effect.getName()); EXPECT_FALSE(effect.getGeometryShaderInputType().has_value()); } TEST_F(AEffectResource, canBeCreatedWithShaders) { - EffectResource effect("verttext", "fragtext", "geomtext", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), "", ResourceCacheFlag(0u)); + EffectResource effect("verttext", "fragtext", "geomtext", EDrawMode::Lines, EffectInputInformationVector(), EffectInputInformationVector(), ""); EXPECT_STREQ("verttext", effect.getVertexShader()); EXPECT_STREQ("fragtext", effect.getFragmentShader()); EXPECT_STREQ("geomtext", effect.getGeometryShader()); @@ -69,7 +68,7 @@ namespace ramses_internal TEST_F(AEffectResource, canBeCreatedWithInputs) { - EffectResource effect("", "", "", {}, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect("", "", "", {}, uniformInputs, attributeInputs, ""); EXPECT_EQ(uniformInputs, effect.getUniformInputs()); EXPECT_EQ(attributeInputs, effect.getAttributeInputs()); EXPECT_FALSE(effect.getGeometryShaderInputType().has_value()); @@ -77,7 +76,7 @@ namespace ramses_internal TEST_F(AEffectResource, canGetInputsByName) { - EffectResource effect("", "", "", {}, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect("", "", "", {}, uniformInputs, attributeInputs, ""); EXPECT_EQ(DataFieldHandle(1), effect.getUniformDataFieldHandleByName("uni B")); EXPECT_EQ(DataFieldHandle::Invalid(), effect.getUniformDataFieldHandleByName("does not exist")); @@ -88,50 +87,50 @@ namespace ramses_internal TEST_F(AEffectResource, sameParametersGiveSameHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); EXPECT_EQ(effect1.getHash(), effect2.getHash()); } TEST_F(AEffectResource, differentVertexShaderResultsInDifferentHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - EffectResource effect2("XXX", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + EffectResource effect2("XXX", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); EXPECT_NE(effect1.getHash(), effect2.getHash()); } TEST_F(AEffectResource, differentFragmentShaderResultsInDifferentHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - EffectResource effect2("asd", "XXX", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + EffectResource effect2("asd", "XXX", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); EXPECT_NE(effect1.getHash(), effect2.getHash()); } TEST_F(AEffectResource, differentGeometryShaderResultsInDifferentHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - EffectResource effect2("asd", "def", "XXX", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + EffectResource effect2("asd", "def", "XXX", EDrawMode::Lines, uniformInputs, attributeInputs, ""); EXPECT_NE(effect1.getHash(), effect2.getHash()); } TEST_F(AEffectResource, differentUniformInputResultsInDifferentHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, EffectInputInformationVector(), attributeInputs, "", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, EffectInputInformationVector(), attributeInputs, ""); EXPECT_NE(effect1.getHash(), effect2.getHash()); } TEST_F(AEffectResource, differentAttributeInputResultsInDifferentHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, EffectInputInformationVector(), "", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, EffectInputInformationVector(), ""); EXPECT_NE(effect1.getHash(), effect2.getHash()); } TEST_F(AEffectResource, differentNameDoesNotChangeHash) { - EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "some name", ResourceCacheFlag(0u)); - EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "different name", ResourceCacheFlag(0u)); + EffectResource effect1("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "some name"); + EffectResource effect2("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "different name"); EXPECT_EQ(effect1.getHash(), effect2.getHash()); } @@ -139,17 +138,17 @@ namespace ramses_internal TEST_F(AEffectResource, hasCorrectTypeAfterSerializeAndDeserialize) { - EffectResource effectBefore("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(0u)); - std::unique_ptr effectAfter(serializeDeserialize(effectBefore, "")); + EffectResource effectBefore("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + std::unique_ptr effectAfter(SerializeDeserialize(effectBefore, "")); ASSERT_TRUE(effectAfter); - EXPECT_EQ(EResourceType_Effect, effectAfter->getTypeID()); + EXPECT_EQ(EResourceType::Effect, effectAfter->getTypeID()); } TEST_F(AEffectResource, isEqualAfterSerializeAndDeserialize) { - EffectResource effectBefore("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "", ResourceCacheFlag(15u)); - std::unique_ptr effectAfter(serializeDeserialize(effectBefore, "")); + EffectResource effectBefore("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, ""); + std::unique_ptr effectAfter(SerializeDeserialize(effectBefore, "")); ASSERT_TRUE(effectAfter); EXPECT_EQ(effectBefore.getHash(), effectAfter->getHash()); @@ -159,13 +158,12 @@ namespace ramses_internal EXPECT_EQ(effectBefore.getGeometryShaderInputType(), effectAfter->getGeometryShaderInputType()); EXPECT_EQ(effectBefore.getUniformInputs(), effectAfter->getUniformInputs()); EXPECT_EQ(effectBefore.getAttributeInputs(), effectAfter->getAttributeInputs()); - EXPECT_EQ(effectBefore.getCacheFlag(), effectAfter->getCacheFlag()); } TEST_F(AEffectResource, isEqualAfterSerializeAndDeserializeNoGeometryShader) { - EffectResource effectBefore("asd", "def", {}, {}, uniformInputs, attributeInputs, {}, ResourceCacheFlag(15u)); - std::unique_ptr effectAfter(serializeDeserialize(effectBefore, "")); + EffectResource effectBefore("asd", "def", {}, {}, uniformInputs, attributeInputs, {}); + std::unique_ptr effectAfter(SerializeDeserialize(effectBefore, "")); ASSERT_TRUE(effectAfter); EXPECT_EQ(effectBefore.getHash(), effectAfter->getHash()); @@ -175,13 +173,12 @@ namespace ramses_internal EXPECT_EQ(effectBefore.getGeometryShaderInputType(), effectAfter->getGeometryShaderInputType()); EXPECT_EQ(effectBefore.getUniformInputs(), effectAfter->getUniformInputs()); EXPECT_EQ(effectBefore.getAttributeInputs(), effectAfter->getAttributeInputs()); - EXPECT_EQ(effectBefore.getCacheFlag(), effectAfter->getCacheFlag()); } TEST_F(AEffectResource, hasNameProvidedToSerializeAfterSerializeAndDeserialize) { - EffectResource effectBefore("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "some name", ResourceCacheFlag(0u)); - std::unique_ptr effectAfter(serializeDeserialize(effectBefore, "different name")); + EffectResource effectBefore("asd", "def", "xyz", EDrawMode::Lines, uniformInputs, attributeInputs, "some name"); + std::unique_ptr effectAfter(SerializeDeserialize(effectBefore, "different name")); ASSERT_TRUE(effectAfter); EXPECT_EQ(std::string{"different name"}, effectAfter->getName()); diff --git a/framework/SceneGraph/Resource/test/LZ4CompressionUtilsTest.cpp b/tests/unittests/framework/SceneGraph/Resource/LZ4CompressionUtilsTest.cpp similarity index 82% rename from framework/SceneGraph/Resource/test/LZ4CompressionUtilsTest.cpp rename to tests/unittests/framework/SceneGraph/Resource/LZ4CompressionUtilsTest.cpp index a3de9cb61..97ed739c6 100644 --- a/framework/SceneGraph/Resource/test/LZ4CompressionUtilsTest.cpp +++ b/tests/unittests/framework/SceneGraph/Resource/LZ4CompressionUtilsTest.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Resource/LZ4CompressionUtils.h" -#include "Collections/Vector.h" +#include "internal/SceneGraph/Resource/LZ4CompressionUtils.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" #include "gtest/gtest.h" #include -namespace ramses_internal +namespace ramses::internal { - void checkCompressionDecompression(const std::vector& input) + void checkCompressionDecompression(const std::vector& input) { for (auto level : { LZ4CompressionUtils::CompressionLevel::Fast, LZ4CompressionUtils::CompressionLevel::High }) @@ -59,14 +59,14 @@ namespace ramses_internal TEST(LZ4CompressionUtilsTest, TestSmallData) { - std::vector small = {10, 20, 30, 40, 50, 60}; + std::vector small = {std::byte{10}, std::byte{20}, std::byte{30}, std::byte{40}, std::byte{50}, std::byte{60}}; checkCompressionDecompression(small); } TEST(LZ4CompressionUtilsTest, TestBigData) { - std::vector big(1024 * 128); - std::iota(big.begin(), big.end(), static_cast(5)); + std::vector big(1024 * 128); + std::generate(big.begin(), big.end(), [](){ static uint8_t i{4}; return std::byte(++i); }); checkCompressionDecompression(big); } } diff --git a/framework/SceneGraph/Resource/test/ResourceTest.cpp b/tests/unittests/framework/SceneGraph/Resource/ResourceTest.cpp similarity index 81% rename from framework/SceneGraph/Resource/test/ResourceTest.cpp rename to tests/unittests/framework/SceneGraph/Resource/ResourceTest.cpp index bf123e448..891b1dbf5 100644 --- a/framework/SceneGraph/Resource/test/ResourceTest.cpp +++ b/tests/unittests/framework/SceneGraph/Resource/ResourceTest.cpp @@ -6,28 +6,27 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Utils/LogMacros.h" -#include "framework_common_gmock_header.h" -#include "Resource/ResourceBase.h" -#include "Utils/ThreadBarrier.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/SceneGraph/Resource/ResourceBase.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "gtest/gtest.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { namespace { class TestResource : public ResourceBase { public: - explicit TestResource(EResourceType typeID, ResourceCacheFlag cacheFlag, std::string_view name) - : ResourceBase(typeID, cacheFlag, name) + explicit TestResource(EResourceType typeID, std::string_view name) + : ResourceBase(typeID, name) {} - void serializeResourceMetadataToStream(IOutputStream&) const override {} + void serializeResourceMetadataToStream(IOutputStream& /*output*/) const override {} }; class ResourceCompression : public ::testing::TestWithParam @@ -38,7 +37,7 @@ namespace ramses_internal { public: explicit DummyResource(uint32_t metadata = 0, std::string_view name = {}) - : ResourceBase(EResourceType_Invalid, ResourceCacheFlag(15u), name) + : ResourceBase(EResourceType::Invalid, name) , m_metadata(metadata) {} @@ -63,16 +62,16 @@ namespace ramses_internal { SCOPED_TRACE(dataSize); - TestResource res(EResourceType_Invalid, ResourceCacheFlag(0), {}); + TestResource res(EResourceType::Invalid, {}); ResourceBlob data(dataSize); for (uint32_t idx = 0; idx < dataSize; ++idx) { - data.data()[idx] = static_cast(idx+1); + data.data()[idx] = std::byte{static_cast(idx+1)}; } res.setResourceData(ResourceBlob(data.size(), data.data())); // copy data res.compress(GetParam()); - TestResource resFromCompressed(EResourceType_Invalid, ResourceCacheFlag(0), {}); + TestResource resFromCompressed(EResourceType::Invalid, {}); resFromCompressed.setCompressedResourceData(CompressedResourceBlob(res.getCompressedResourceData().size(), res.getCompressedResourceData().data()), IResource::CompressionLevel::Realtime, res.getDecompressedDataSize(), res.getHash()); resFromCompressed.decompress(); @@ -86,7 +85,7 @@ namespace ramses_internal for (uint32_t dataSize = 1; dataSize < 1001; ++dataSize) { SCOPED_TRACE(dataSize); - TestResource res(EResourceType_Invalid, ResourceCacheFlag(0), {}); + TestResource res(EResourceType::Invalid, {}); res.setResourceData(ResourceBlob(dataSize)); res.compress(GetParam()); EXPECT_FALSE(res.isCompressedAvailable()); @@ -113,7 +112,7 @@ namespace ramses_internal TEST_F(AResource, hasZeroSizesByDefault) { - TestResource emptyRes(EResourceType_Invalid, ResourceCacheFlag(0), {}); + TestResource emptyRes(EResourceType::Invalid, {}); EXPECT_EQ(0u, emptyRes.getDecompressedDataSize()); EXPECT_EQ(0u, emptyRes.getCompressedDataSize()); } @@ -123,7 +122,7 @@ namespace ramses_internal for (uint32_t dataSize = 1; dataSize < 2000; ++dataSize) { SCOPED_TRACE(dataSize); - TestResource res(EResourceType_Invalid, ResourceCacheFlag(0), {}); + TestResource res(EResourceType::Invalid, {}); res.setResourceData(ResourceBlob(dataSize)); res.compress(IResource::CompressionLevel::None); EXPECT_FALSE(res.isCompressedAvailable()); @@ -132,21 +131,21 @@ namespace ramses_internal TEST_F(AResource, canGetEmptyName) { - TestResource emptyNameRes(EResourceType_Invalid, ResourceCacheFlag(0), {}); + TestResource emptyNameRes(EResourceType::Invalid, {}); EXPECT_EQ(std::string{}, emptyNameRes.getName()); } TEST_F(AResource, canGetNonEmptyName) { - TestResource nonEmptyNameRes(EResourceType_Invalid, ResourceCacheFlag(0), "foobar"); + TestResource nonEmptyNameRes(EResourceType::Invalid, "foobar"); EXPECT_EQ(std::string{"foobar"}, nonEmptyNameRes.getName()); } TEST_F(AResource, givesSameHashForDifferentNames) { - TestResource noNameRes(EResourceType_Invalid, ResourceCacheFlag(0), ""); - TestResource namedRes(EResourceType_Invalid, ResourceCacheFlag(0), "some name"); - TestResource otherNamedRes(EResourceType_Invalid, ResourceCacheFlag(0), "other name"); + TestResource noNameRes(EResourceType::Invalid, ""); + TestResource namedRes(EResourceType::Invalid, "some name"); + TestResource otherNamedRes(EResourceType::Invalid, "other name"); EXPECT_EQ(noNameRes.getHash(), namedRes.getHash()); EXPECT_EQ(noNameRes.getHash(), otherNamedRes.getHash()); @@ -154,14 +153,8 @@ namespace ramses_internal TEST_F(AResource, canGetType) { - TestResource res(EResourceType_Effect, ResourceCacheFlag(0), {}); - EXPECT_EQ(EResourceType_Effect, res.getTypeID()); - } - - TEST_F(AResource, canGetCacheFlag) - { - TestResource res(EResourceType_Invalid, ResourceCacheFlag(11), {}); - EXPECT_EQ(ResourceCacheFlag(11), res.getCacheFlag()); + TestResource res(EResourceType::Effect, {}); + EXPECT_EQ(EResourceType::Effect, res.getTypeID()); } TEST_F(AResource, returnsInvalidHashForEmptyResources) @@ -199,7 +192,7 @@ namespace ramses_internal DummyResource res; res.setResourceData(std::move(zeroBlobA)); const ResourceContentHash hash = res.getHash(); - zeroBlobB.data()[0] = 1; + zeroBlobB.data()[0] = std::byte{1}; res.setResourceData(std::move(zeroBlobB)); EXPECT_NE(hash, res.getHash()); } @@ -229,7 +222,7 @@ namespace ramses_internal { DummyResource resA; ResourceBlob blob(4096); - std::iota(blob.data(), blob.data() + blob.size(), static_cast(10)); + std::generate(blob.data(), blob.data() + blob.size(), [](){ static uint8_t i{9}; return std::byte(++i); }); resA.setResourceData(std::move(blob)); resA.compress(IResource::CompressionLevel::Realtime); ASSERT_TRUE(resA.isCompressedAvailable()); @@ -261,9 +254,9 @@ namespace ramses_internal { // the following parameters will generate a blob which compresses differently with Offline and Realtime compression std::mt19937 gen(123456); // NOLINT(cert-msc32-c,cert-msc51-cpp) we do want deterministically created values - std::uniform_int_distribution dis(0, 32); - std::vector nonTrivialData(4096); - std::generate(nonTrivialData.begin(), nonTrivialData.end(), [&](){ return static_cast(dis(gen)); }); + std::uniform_int_distribution dis(0, 32); + std::vector nonTrivialData(4096); + std::generate(nonTrivialData.begin(), nonTrivialData.end(), [&](){ return static_cast(dis(gen)); }); DummyResource resA; resA.setResourceData(ResourceBlob{ nonTrivialData.size(), nonTrivialData.data() }); @@ -319,9 +312,9 @@ namespace ramses_internal constexpr size_t resourceSize = 2000; for (size_t i = 0; i < numResources; ++i) { - auto res = std::make_unique(EResourceType_Effect, ResourceCacheFlag(0), ""); + auto res = std::make_unique(EResourceType::Effect, ""); ResourceBlob blob(resourceSize); - std::iota(blob.data(), blob.data() + blob.size(), static_cast(10)); + std::generate(blob.data(), blob.data() + blob.size(), [](){ static uint8_t j{9}; return std::byte(++j); }); res->setResourceData(std::move(blob)); resources.push_back(std::move(res)); } @@ -329,28 +322,31 @@ namespace ramses_internal void run() { - std::vector threads(funcs.size()); - ThreadBarrier startBarrier(static_cast(threads.size())); - for (size_t i = 0; i < threads.size(); ++i) - threads[i] = std::thread([func = std::move(funcs[i]), &startBarrier]() { + std::vector threads; + threads.reserve(funcs.size()); + ThreadBarrier startBarrier(static_cast(funcs.size())); + for (auto& f : funcs) + { + threads.emplace_back([func = std::move(f), &startBarrier]() { startBarrier.wait(); func(); }); + } for (auto& t : threads) t.join(); } void makeCompressedOnly() { - for (size_t i = 0; i < resources.size(); ++i) + for (auto & resource : resources) { - auto& res = resources[i]; + auto& res = resource; res->compress(IResource::CompressionLevel::Realtime); - auto compressedRes = std::make_unique(res->getTypeID(), res->getCacheFlag(), res->getName()); + auto compressedRes = std::make_unique(res->getTypeID(), res->getName()); CompressedResourceBlob compressedData(res->getCompressedResourceData().size(), res->getCompressedResourceData().data()); compressedRes->setCompressedResourceData(std::move(compressedData), IResource::CompressionLevel::Realtime, res->getDecompressedDataSize(), res->getHash()); - resources[i] = std::move(compressedRes); + resource = std::move(compressedRes); } } @@ -361,15 +357,17 @@ namespace ramses_internal TEST_F(AResourceThreaded, simultaneousCompression) { for (size_t i = 0; i < 2; ++i) - funcs.push_back([&]() { + { + funcs.emplace_back([&]() { size_t value = 0; for (auto& res : resources) { res->compress(IResource::CompressionLevel::Realtime); - value += res->getCompressedResourceData().data()[0]; + value += std::to_integer(res->getCompressedResourceData().data()[0]); } LOG_DEBUG(CONTEXT_FRAMEWORK, "Test result " << value); }); + } run(); } @@ -377,51 +375,59 @@ namespace ramses_internal { makeCompressedOnly(); for (size_t i = 0; i < 2; ++i) - funcs.push_back([&]() { + { + funcs.emplace_back([&]() { size_t value = 0; for (auto& res : resources) { res->decompress(); - value += res->getResourceData().data()[0]; + value += std::to_integer(res->getResourceData().data()[0]); value += res->getDecompressedDataSize(); } LOG_DEBUG(CONTEXT_FRAMEWORK, "Test result " << value); }); + } run(); } TEST_F(AResourceThreaded, simultaneousCompressAndRead) { for (size_t i = 0; i < 2; ++i) - funcs.push_back([&]() { + { + funcs.emplace_back([&]() { size_t value = 0; for (auto& res : resources) { value += res->getCompressedDataSize(); value += res->isCompressedAvailable() ? 1: 0; if (res->isCompressedAvailable()) + { value += res->getCompressedResourceData().size(); + } else + { res->compress(IResource::CompressionLevel::Realtime); + } } LOG_DEBUG(CONTEXT_FRAMEWORK, "Test result " << value); }); + } run(); } TEST_F(AResourceThreaded, simultaneousRecompressAndDecompress) { makeCompressedOnly(); - funcs.push_back([&]() { + funcs.emplace_back([&]() { for (auto& res : resources) res->compress(IResource::CompressionLevel::Offline); }); - funcs.push_back([&]() { + funcs.emplace_back([&]() { size_t value = 0; for (auto& res : resources) { res->decompress(); - value += res->getResourceData().data()[0]; + value += std::to_integer(res->getResourceData().data()[0]); value += res->getDecompressedDataSize(); } LOG_DEBUG(CONTEXT_FRAMEWORK, "Test result " << value); diff --git a/framework/SceneGraph/Resource/test/TextureResourceTest.cpp b/tests/unittests/framework/SceneGraph/Resource/TextureResourceTest.cpp similarity index 80% rename from framework/SceneGraph/Resource/test/TextureResourceTest.cpp rename to tests/unittests/framework/SceneGraph/Resource/TextureResourceTest.cpp index b90d5ec34..8075abc30 100644 --- a/framework/SceneGraph/Resource/test/TextureResourceTest.cpp +++ b/tests/unittests/framework/SceneGraph/Resource/TextureResourceTest.cpp @@ -5,11 +5,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" -namespace ramses_internal +namespace ramses::internal { class TextureResourceTest : public ::testing::Test { @@ -20,7 +19,7 @@ namespace ramses_internal m_texDesc.m_width = 1u; m_texDesc.m_height = 2u; m_texDesc.m_depth = 3u; - m_texDesc.m_format = ETextureFormat::RGB8; + m_texDesc.m_format = EPixelStorageFormat::RGB8; m_texDesc.m_swizzle = {ETextureChannelColor::Green, ETextureChannelColor::Blue, ETextureChannelColor::Red, ETextureChannelColor::Alpha}; m_texDesc.m_dataSizes = m_mipLevelSizes; m_texDesc.m_generateMipChain = false; @@ -32,8 +31,7 @@ namespace ramses_internal TEST_F(TextureResourceTest, textureResourceStoresMetaInfo) { - const ResourceCacheFlag flag(15u); - TextureResource texRes(EResourceType_Texture3D, this->m_texDesc, flag, {}); + TextureResource texRes(EResourceType::Texture3D, this->m_texDesc, {}); // check properties EXPECT_EQ(texRes.getWidth(), this->m_texDesc.m_width); @@ -41,17 +39,15 @@ namespace ramses_internal EXPECT_EQ(texRes.getDepth(), this->m_texDesc.m_depth); EXPECT_EQ(texRes.getTextureFormat(), this->m_texDesc.m_format); EXPECT_EQ(texRes.getTextureSwizzle(), this->m_texDesc.m_swizzle); - EXPECT_EQ(texRes.getTypeID(), EResourceType_Texture3D); + EXPECT_EQ(texRes.getTypeID(), EResourceType::Texture3D); EXPECT_EQ(texRes.getMipDataSizes(), this->m_mipLevelSizes); EXPECT_EQ(texRes.getResourceData().size(), std::accumulate(this->m_mipLevelSizes.begin(), this->m_mipLevelSizes.end(), 0u)); EXPECT_EQ(texRes.getGenerateMipChainFlag(), this->m_texDesc.m_generateMipChain); - EXPECT_EQ(texRes.getCacheFlag(), flag); } TEST_F(TextureResourceTest, textureResourceStoresMetaInfoWithCorrectSizeForCubeTexture) { - const ResourceCacheFlag flag(15u); - TextureResource texRes(EResourceType_TextureCube, this->m_texDesc, flag, {}); + TextureResource texRes(EResourceType::TextureCube, this->m_texDesc, {}); // check properties EXPECT_EQ(texRes.getWidth(), this->m_texDesc.m_width); @@ -59,10 +55,9 @@ namespace ramses_internal EXPECT_EQ(texRes.getDepth(), this->m_texDesc.m_depth); EXPECT_EQ(texRes.getTextureFormat(), this->m_texDesc.m_format); EXPECT_EQ(texRes.getTextureSwizzle(), this->m_texDesc.m_swizzle); - EXPECT_EQ(texRes.getTypeID(), EResourceType_TextureCube); + EXPECT_EQ(texRes.getTypeID(), EResourceType::TextureCube); EXPECT_EQ(texRes.getMipDataSizes(), this->m_mipLevelSizes); EXPECT_EQ(texRes.getResourceData().size(), 6u * std::accumulate(this->m_mipLevelSizes.begin(), this->m_mipLevelSizes.end(), 0u)); EXPECT_EQ(texRes.getGenerateMipChainFlag(), this->m_texDesc.m_generateMipChain); - EXPECT_EQ(texRes.getCacheFlag(), flag); } } diff --git a/framework/SceneGraph/Scene/test/ActionTestScene.cpp b/tests/unittests/framework/SceneGraph/Scene/ActionTestScene.cpp similarity index 96% rename from framework/SceneGraph/Scene/test/ActionTestScene.cpp rename to tests/unittests/framework/SceneGraph/Scene/ActionTestScene.cpp index 95602614c..9bdcc0f8e 100644 --- a/framework/SceneGraph/Scene/test/ActionTestScene.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/ActionTestScene.cpp @@ -7,13 +7,12 @@ // ------------------------------------------------------------------------- #include "ActionTestScene.h" -#include "Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" -namespace ramses_internal +namespace ramses::internal { ActionTestScene::ActionTestScene(const SceneInfo& sceneInfo) : m_scene(sceneInfo) - , m_actionCollector() { } @@ -27,7 +26,7 @@ namespace ramses_internal return m_scene.getSceneId(); } - void ActionTestScene::setEffectTimeSync(FlushTime::Clock::time_point) + void ActionTestScene::setEffectTimeSync(FlushTime::Clock::time_point /*t*/) { // not set by a scene action } @@ -42,7 +41,7 @@ namespace ramses_internal return m_scene.getRenderableCount(); } - RenderableHandle ActionTestScene::allocateRenderable(NodeHandle nodeHandle, RenderableHandle handle /*= RenderableHandle::Invalid()*/) + RenderableHandle ActionTestScene::allocateRenderable(NodeHandle nodeHandle, RenderableHandle handle) { const RenderableHandle actualHandle = m_actionCollector.allocateRenderable(nodeHandle, handle); flushPendingSceneActions(); @@ -113,7 +112,7 @@ namespace ramses_internal return m_scene.getRenderStateCount(); } - RenderStateHandle ActionTestScene::allocateRenderState(RenderStateHandle stateHandle /*= RenderStateHandle::Invalid()*/) + RenderStateHandle ActionTestScene::allocateRenderState(RenderStateHandle stateHandle) { const RenderStateHandle actualHandle = m_actionCollector.allocateRenderState(stateHandle); flushPendingSceneActions(); @@ -207,7 +206,7 @@ namespace ramses_internal return m_scene.getCameraCount(); } - CameraHandle ActionTestScene::allocateCamera(ECameraProjectionType projType, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle /*= CameraHandle::Invalid()*/) + CameraHandle ActionTestScene::allocateCamera(ECameraProjectionType projType, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle) { const CameraHandle actualHandle = m_actionCollector.allocateCamera(projType, nodeHandle, dataInstance, handle); flushPendingSceneActions(); @@ -230,7 +229,7 @@ namespace ramses_internal return m_scene.getNodeCount(); } - NodeHandle ActionTestScene::allocateNode(uint32_t childrenCount, NodeHandle handle /*= NodeHandle::Invalid()*/) + NodeHandle ActionTestScene::allocateNode(uint32_t childrenCount, NodeHandle handle) { const NodeHandle actualHandle = m_actionCollector.allocateNode(childrenCount, handle); flushPendingSceneActions(); @@ -253,7 +252,7 @@ namespace ramses_internal return m_scene.getTransformCount(); } - TransformHandle ActionTestScene::allocateTransform(NodeHandle nodeHandle, TransformHandle handle /*= TransformHandle::Invalid()*/) + TransformHandle ActionTestScene::allocateTransform(NodeHandle nodeHandle, TransformHandle handle) { const TransformHandle actualHandle = m_actionCollector.allocateTransform(nodeHandle, handle); flushPendingSceneActions(); @@ -374,7 +373,7 @@ namespace ramses_internal return m_scene.getDataInstanceCount(); } - DataInstanceHandle ActionTestScene::allocateDataInstance(DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle /*= DataInstanceHandle::Invalid()*/) + DataInstanceHandle ActionTestScene::allocateDataInstance(DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle) { const DataInstanceHandle actualHandle = m_actionCollector.allocateDataInstance(finishedLayoutHandle, instanceHandle); flushPendingSceneActions(); @@ -417,6 +416,11 @@ namespace ramses_internal return m_scene.getDataVector4fArray(containerHandle, field); } + const bool* ActionTestScene::getDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle field) const + { + return m_scene.getDataBooleanArray(containerHandle, field); + } + const int32_t* ActionTestScene::getDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle field) const { return m_scene.getDataIntegerArray(containerHandle, field); @@ -487,6 +491,11 @@ namespace ramses_internal return m_scene.getDataSingleVector4f(containerHandle, field); } + bool ActionTestScene::getDataSingleBoolean(DataInstanceHandle containerHandle, DataFieldHandle field) const + { + return m_scene.getDataSingleBoolean(containerHandle, field); + } + int32_t ActionTestScene::getDataSingleInteger(DataInstanceHandle containerHandle, DataFieldHandle field) const { return m_scene.getDataSingleInteger(containerHandle, field); @@ -564,6 +573,12 @@ namespace ramses_internal flushPendingSceneActions(); } + void ActionTestScene::setDataBooleanArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) + { + m_actionCollector.setDataBooleanArray(containerHandle, field, elementCount, data); + flushPendingSceneActions(); + } + void ActionTestScene::setDataIntegerArray(DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) { m_actionCollector.setDataIntegerArray(containerHandle, field, elementCount, data); @@ -630,6 +645,12 @@ namespace ramses_internal flushPendingSceneActions(); } + void ActionTestScene::setDataSingleBoolean(DataInstanceHandle containerHandle, DataFieldHandle field, bool data) + { + m_actionCollector.setDataSingleBoolean(containerHandle, field, data); + flushPendingSceneActions(); + } + void ActionTestScene::setDataSingleInteger(DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) { m_actionCollector.setDataSingleInteger(containerHandle, field, data); @@ -672,7 +693,7 @@ namespace ramses_internal flushPendingSceneActions(); } - TextureSamplerHandle ActionTestScene::allocateTextureSampler(const TextureSampler& sampler, TextureSamplerHandle handle /*= TextureSamplerHandle::Invalid()*/) + TextureSamplerHandle ActionTestScene::allocateTextureSampler(const TextureSampler& sampler, TextureSamplerHandle handle) { const TextureSamplerHandle actualHandle = m_actionCollector.allocateTextureSampler(sampler, handle); flushPendingSceneActions(); @@ -757,7 +778,7 @@ namespace ramses_internal return m_scene.getRenderGroup(groupHandle); } - RenderPassHandle ActionTestScene::allocateRenderPass(uint32_t renderGroupCount, RenderPassHandle handle /*= RenderPassHandle::Invalid()*/) + RenderPassHandle ActionTestScene::allocateRenderPass(uint32_t renderGroupCount, RenderPassHandle handle) { const RenderPassHandle actualHandle = m_actionCollector.allocateRenderPass(renderGroupCount, handle); flushPendingSceneActions(); @@ -786,7 +807,7 @@ namespace ramses_internal flushPendingSceneActions(); } - RenderTargetHandle ActionTestScene::allocateRenderTarget(RenderTargetHandle targetHandle /*= RenderTargetHandle::Invalid()*/) + RenderTargetHandle ActionTestScene::allocateRenderTarget(RenderTargetHandle targetHandle) { const RenderTargetHandle actualHandle = m_actionCollector.allocateRenderTarget(targetHandle); flushPendingSceneActions(); @@ -861,7 +882,7 @@ namespace ramses_internal return m_scene.getDataBufferCount(); } - void ActionTestScene::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) + void ActionTestScene::updateDataBuffer(DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) { m_actionCollector.updateDataBuffer(handle, offsetInBytes, dataSizeInBytes, data); flushPendingSceneActions(); @@ -877,7 +898,7 @@ namespace ramses_internal return m_scene.getDataBuffer(handle); } - TextureBufferHandle ActionTestScene::allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) + TextureBufferHandle ActionTestScene::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) { const TextureBufferHandle actualHandle = m_actionCollector.allocateTextureBuffer(textureFormat, mipMapDimensions, handle); flushPendingSceneActions(); @@ -895,7 +916,7 @@ namespace ramses_internal return m_scene.getTextureBufferCount(); } - void ActionTestScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) + void ActionTestScene::updateTextureBuffer(TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) { m_actionCollector.updateTextureBuffer(handle, mipLevel, x, y, width, height, data); flushPendingSceneActions(); @@ -911,7 +932,7 @@ namespace ramses_internal return m_scene.isTextureBufferAllocated(handle); } - DataSlotHandle ActionTestScene::allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle /*= DataSlotHandle::Invalid()*/) + DataSlotHandle ActionTestScene::allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle) { const DataSlotHandle actualHandle = m_actionCollector.allocateDataSlot(dataSlot, handle); flushPendingSceneActions(); @@ -991,7 +1012,7 @@ namespace ramses_internal return m_scene.getSceneReference(handle); } - void ActionTestScene::setRenderPassClearFlag(RenderPassHandle handle, uint32_t clearFlag) + void ActionTestScene::setRenderPassClearFlag(RenderPassHandle handle, ClearFlags clearFlag) { m_actionCollector.setRenderPassClearFlag(handle, clearFlag); flushPendingSceneActions(); @@ -1050,7 +1071,7 @@ namespace ramses_internal return m_scene.getRenderPass(passHandle); } - PickableObjectHandle ActionTestScene::allocatePickableObject(DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle /* = PickableObjectHandle::Invalid() */) + PickableObjectHandle ActionTestScene::allocatePickableObject(DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle) { const PickableObjectHandle resultHandle = m_actionCollector.allocatePickableObject(geometryHandle, nodeHandle, id, pickableHandle); flushPendingSceneActions(); @@ -1096,7 +1117,7 @@ namespace ramses_internal return m_scene.getPickableObject(pickableHandle); } - BlitPassHandle ActionTestScene::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle /*= BlitPassHandle::Invalid()*/) + BlitPassHandle ActionTestScene::allocateBlitPass(RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) { const BlitPassHandle resultHandle = m_actionCollector.allocateBlitPass(sourceRenderBufferHandle, destinationRenderBufferHandle, passHandle); flushPendingSceneActions(); diff --git a/framework/SceneGraph/Scene/test/ActionTestScene.h b/tests/unittests/framework/SceneGraph/Scene/ActionTestScene.h similarity index 64% rename from framework/SceneGraph/Scene/test/ActionTestScene.h rename to tests/unittests/framework/SceneGraph/Scene/ActionTestScene.h index 43e90be35..0015122ab 100644 --- a/framework/SceneGraph/Scene/test/ActionTestScene.h +++ b/tests/unittests/framework/SceneGraph/Scene/ActionTestScene.h @@ -6,13 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_ACTIONTESTSCENE_H -#define RAMSES_ACTIONTESTSCENE_H +#pragma once -#include "framework_common_gmock_header.h" -#include "Scene/ActionCollectingScene.h" +#include "internal/SceneGraph/Scene/ActionCollectingScene.h" -namespace ramses_internal +namespace ramses::internal { // This test facade tests multiple things: // It tests that scene actions are created symmetrically @@ -33,10 +31,10 @@ namespace ramses_internal [[nodiscard]] FlushTime::Clock::time_point getEffectTimeSync() const override; // Renderable - RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle = RenderableHandle::Invalid()) override; + RenderableHandle allocateRenderable (NodeHandle nodeHandle, RenderableHandle handle) override; void releaseRenderable (RenderableHandle renderableHandle) override; - [[nodiscard]] bool isRenderableAllocated (RenderableHandle renderableHandle) const override; - [[nodiscard]] uint32_t getRenderableCount () const override; + [[nodiscard]] bool isRenderableAllocated (RenderableHandle renderableHandle) const override; + [[nodiscard]] uint32_t getRenderableCount () const override; void setRenderableDataInstance (RenderableHandle renderableHandle, ERenderableDataSlotType slot, DataInstanceHandle newDataInstance) override; void setRenderableStartIndex (RenderableHandle renderableHandle, uint32_t startIndex) override; void setRenderableIndexCount (RenderableHandle renderableHandle, uint32_t indexCount) override; @@ -47,10 +45,10 @@ namespace ramses_internal [[nodiscard]] const Renderable& getRenderable (RenderableHandle renderableHandle) const override; // Render state - RenderStateHandle allocateRenderState (RenderStateHandle stateHandle = RenderStateHandle::Invalid()) override; + RenderStateHandle allocateRenderState (RenderStateHandle stateHandle) override; void releaseRenderState (RenderStateHandle stateHandle) override; - [[nodiscard]] bool isRenderStateAllocated (RenderStateHandle stateHandle) const override; - [[nodiscard]] uint32_t getRenderStateCount () const override; + [[nodiscard]] bool isRenderStateAllocated (RenderStateHandle stateHandle) const override; + [[nodiscard]] uint32_t getRenderStateCount () const override; void setRenderStateBlendFactors (RenderStateHandle stateHandle, EBlendFactor srcColor, EBlendFactor destColor, EBlendFactor srcAlpha, EBlendFactor destAlpha) override; void setRenderStateBlendOperations (RenderStateHandle stateHandle, EBlendOperation operationColor, EBlendOperation operationAlpha) override; void setRenderStateBlendColor (RenderStateHandle stateHandle, const glm::vec4& color) override; @@ -65,19 +63,19 @@ namespace ramses_internal [[nodiscard]] const RenderState& getRenderState (RenderStateHandle stateHandle) const override; // Camera - CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle = CameraHandle::Invalid()) override; + CameraHandle allocateCamera (ECameraProjectionType type, NodeHandle nodeHandle, DataInstanceHandle dataInstance, CameraHandle handle) override; void releaseCamera (CameraHandle cameraHandle) override; [[nodiscard]] bool isCameraAllocated (CameraHandle handle) const override; [[nodiscard]] uint32_t getCameraCount () const override; [[nodiscard]] const Camera& getCamera (CameraHandle cameraHandle) const override; // Creation/Deletion - NodeHandle allocateNode (uint32_t childrenCount = 0u, NodeHandle handle = NodeHandle::Invalid()) override; + NodeHandle allocateNode (uint32_t childrenCount, NodeHandle handle) override; void releaseNode (NodeHandle nodeHandle) override; [[nodiscard]] bool isNodeAllocated (NodeHandle node) const override; [[nodiscard]] uint32_t getNodeCount () const override; - TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle = TransformHandle::Invalid()) override; + TransformHandle allocateTransform (NodeHandle nodeHandle, TransformHandle handle) override; void releaseTransform (TransformHandle transform) override; [[nodiscard]] bool isTransformAllocated (TransformHandle transformHandle) const override; [[nodiscard]] uint32_t getTransformCount () const override; @@ -99,83 +97,87 @@ namespace ramses_internal void setRotation (TransformHandle handle, const glm::vec4& rotation, ERotationType rotationType) override; void setScaling (TransformHandle handle, const glm::vec3& scaling) override; - DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle = DataLayoutHandle::Invalid()) override; + DataLayoutHandle allocateDataLayout (const DataFieldInfoVector& dataFields, const ResourceContentHash& effectHash, DataLayoutHandle handle) override; void releaseDataLayout (DataLayoutHandle layoutHandle) override; [[nodiscard]] bool isDataLayoutAllocated (DataLayoutHandle layoutHandle) const override; [[nodiscard]] uint32_t getDataLayoutCount () const override; [[nodiscard]] const DataLayout& getDataLayout (DataLayoutHandle layoutHandle) const override; - DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle = DataInstanceHandle::Invalid()) override; + DataInstanceHandle allocateDataInstance (DataLayoutHandle finishedLayoutHandle, DataInstanceHandle instanceHandle) override; void releaseDataInstance (DataInstanceHandle containerHandle) override; [[nodiscard]] bool isDataInstanceAllocated (DataInstanceHandle containerHandle) const override; [[nodiscard]] uint32_t getDataInstanceCount () const override; [[nodiscard]] DataLayoutHandle getLayoutOfDataInstance (DataInstanceHandle containerHandle) const override; - [[nodiscard]] const float* getDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::vec2* getDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::vec3* getDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::vec4* getDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const int32_t* getDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::mat2* getDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::mat3* getDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::mat4* getDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::ivec2* getDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::ivec3* getDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::ivec4* getDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const ResourceField& getDataResource (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] TextureSamplerHandle getDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] DataInstanceHandle getDataReference (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - - void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) override; - void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) override; - void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) override; - void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) override; - void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) override; - void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) override; - void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) override; - void setDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec4* data) override; - void setDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat2* data) override; - void setDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat3* data) override; - void setDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat4* data) override; - void setDataResource (DataInstanceHandle containerHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) override; - void setDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) override; - void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) override; + [[nodiscard]] const float* getDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::vec2* getDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::vec3* getDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::vec4* getDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const bool* getDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const int32_t* getDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::mat2* getDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::mat3* getDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::mat4* getDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::ivec2* getDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::ivec3* getDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::ivec4* getDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const ResourceField& getDataResource (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] TextureSamplerHandle getDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] DataInstanceHandle getDataReference (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + + void setDataFloatArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const float* data) override; + void setDataVector2fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec2* data) override; + void setDataVector3fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec3* data) override; + void setDataVector4fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::vec4* data) override; + void setDataBooleanArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const bool* data) override; + void setDataIntegerArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const int32_t* data) override; + void setDataVector2iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec2* data) override; + void setDataVector3iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec3* data) override; + void setDataVector4iArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::ivec4* data) override; + void setDataMatrix22fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat2* data) override; + void setDataMatrix33fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat3* data) override; + void setDataMatrix44fArray (DataInstanceHandle containerHandle, DataFieldHandle field, uint32_t elementCount, const glm::mat4* data) override; + void setDataResource (DataInstanceHandle containerHandle, DataFieldHandle field, const ResourceContentHash& hash, DataBufferHandle dataBuffer, uint32_t instancingDivisor, uint16_t offsetWithinElementInBytes, uint16_t stride) override; + void setDataTextureSamplerHandle (DataInstanceHandle containerHandle, DataFieldHandle field, TextureSamplerHandle samplerHandle) override; + void setDataReference (DataInstanceHandle containerHandle, DataFieldHandle field, DataInstanceHandle dataRef) override; // get/setData*Array wrappers for elementCount == 1 - [[nodiscard]] float getDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::vec2& getDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::vec3& getDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::vec4& getDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] int32_t getDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::mat2& getDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::mat3& getDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::mat4& getDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::ivec2& getDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::ivec3& getDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - [[nodiscard]] const glm::ivec4& getDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field) const override; - - void setDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field, float data) override; - void setDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec2& data) override; - void setDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec3& data) override; - void setDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec4& data) override; - void setDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) override; - void setDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec2& data) override; - void setDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec3& data) override; - void setDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec4& data) override; - void setDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat2& data) override; - void setDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat3& data) override; - void setDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat4& data) override; + [[nodiscard]] float getDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::vec2& getDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::vec3& getDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::vec4& getDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] bool getDataSingleBoolean (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] int32_t getDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::mat2& getDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::mat3& getDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::mat4& getDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::ivec3& getDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::ivec4& getDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + [[nodiscard]] const glm::ivec2& getDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field) const override; + + void setDataSingleFloat (DataInstanceHandle containerHandle, DataFieldHandle field, float data) override; + void setDataSingleVector2f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec2& data) override; + void setDataSingleVector3f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec3& data) override; + void setDataSingleVector4f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::vec4& data) override; + void setDataSingleBoolean (DataInstanceHandle containerHandle, DataFieldHandle field, bool data) override; + void setDataSingleInteger (DataInstanceHandle containerHandle, DataFieldHandle field, int32_t data) override; + void setDataSingleVector2i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec2& data) override; + void setDataSingleVector3i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec3& data) override; + void setDataSingleVector4i (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::ivec4& data) override; + void setDataSingleMatrix22f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat2& data) override; + void setDataSingleMatrix33f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat3& data) override; + void setDataSingleMatrix44f (DataInstanceHandle containerHandle, DataFieldHandle field, const glm::mat4& data) override; // Texture sampler description - TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle = TextureSamplerHandle::Invalid()) override; + TextureSamplerHandle allocateTextureSampler (const TextureSampler& sampler, TextureSamplerHandle handle) override; void releaseTextureSampler (TextureSamplerHandle handle) override; [[nodiscard]] bool isTextureSamplerAllocated (TextureSamplerHandle handle) const override; [[nodiscard]] uint32_t getTextureSamplerCount () const override; [[nodiscard]] const TextureSampler& getTextureSampler (TextureSamplerHandle handle) const override; // Render groups - RenderGroupHandle allocateRenderGroup (uint32_t renderableCount = 0u, uint32_t nestedGroupCount = 0u, RenderGroupHandle groupHandle = RenderGroupHandle::Invalid()) override; + RenderGroupHandle allocateRenderGroup (uint32_t renderableCount, uint32_t nestedGroupCount, RenderGroupHandle groupHandle) override; void releaseRenderGroup (RenderGroupHandle groupHandle) override; [[nodiscard]] bool isRenderGroupAllocated (RenderGroupHandle groupHandle) const override; [[nodiscard]] uint32_t getRenderGroupCount () const override; @@ -186,12 +188,12 @@ namespace ramses_internal [[nodiscard]] const RenderGroup& getRenderGroup (RenderGroupHandle groupHandle) const override; // Render passes - RenderPassHandle allocateRenderPass (uint32_t renderGroupCount = 0u, RenderPassHandle passHandle = RenderPassHandle::Invalid()) override; + RenderPassHandle allocateRenderPass (uint32_t renderGroupCount, RenderPassHandle passHandle) override; void releaseRenderPass (RenderPassHandle passHandle) override; [[nodiscard]] bool isRenderPassAllocated (RenderPassHandle passHandle) const override; - [[nodiscard]] uint32_t getRenderPassCount () const override; + [[nodiscard]] uint32_t getRenderPassCount () const override; void setRenderPassClearColor (RenderPassHandle passHandle, const glm::vec4& clearColor) override; - void setRenderPassClearFlag (RenderPassHandle passHandle, uint32_t clearFlag) override; + void setRenderPassClearFlag (RenderPassHandle passHandle, ClearFlags clearFlag) override; void setRenderPassCamera (RenderPassHandle passHandle, CameraHandle cameraHandle) override; void setRenderPassRenderTarget (RenderPassHandle passHandle, RenderTargetHandle targetHandle) override; void setRenderPassRenderOrder (RenderPassHandle passHandle, int32_t renderOrder) override; @@ -203,7 +205,7 @@ namespace ramses_internal [[nodiscard]] const RenderPass& getRenderPass (RenderPassHandle passHandle) const override; //Blit passes - BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle = BlitPassHandle::Invalid()) override; + BlitPassHandle allocateBlitPass (RenderBufferHandle sourceRenderBufferHandle, RenderBufferHandle destinationRenderBufferHandle, BlitPassHandle passHandle) override; void releaseBlitPass (BlitPassHandle passHandle) override; [[nodiscard]] bool isBlitPassAllocated (BlitPassHandle passHandle) const override; [[nodiscard]] uint32_t getBlitPassCount () const override; @@ -213,7 +215,7 @@ namespace ramses_internal [[nodiscard]] const BlitPass& getBlitPass (BlitPassHandle passHandle) const override; //Pickable object - PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle = PickableObjectHandle::Invalid()) override; + PickableObjectHandle allocatePickableObject (DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle) override; void releasePickableObject (PickableObjectHandle pickableHandle) override; [[nodiscard]] bool isPickableObjectAllocated (PickableObjectHandle pickableHandle) const final override; [[nodiscard]] uint32_t getPickableObjectCount () const final override; @@ -223,7 +225,7 @@ namespace ramses_internal [[nodiscard]] const PickableObject& getPickableObject (PickableObjectHandle pickableHandle) const override; // Render targets - RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle = RenderTargetHandle::Invalid()) override; + RenderTargetHandle allocateRenderTarget (RenderTargetHandle targetHandle) override; void releaseRenderTarget (RenderTargetHandle targetHandle) override; [[nodiscard]] bool isRenderTargetAllocated (RenderTargetHandle targetHandle) const override; [[nodiscard]] uint32_t getRenderTargetCount () const override; @@ -232,36 +234,36 @@ namespace ramses_internal [[nodiscard]] RenderBufferHandle getRenderTargetRenderBuffer (RenderTargetHandle targetHandle, uint32_t bufferIndex) const override; // Render buffers - RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle = RenderBufferHandle::Invalid()) override; + RenderBufferHandle allocateRenderBuffer (const RenderBuffer& renderBuffer, RenderBufferHandle handle) override; void releaseRenderBuffer (RenderBufferHandle handle) override; [[nodiscard]] bool isRenderBufferAllocated (RenderBufferHandle handle) const override; [[nodiscard]] uint32_t getRenderBufferCount () const override; [[nodiscard]] const RenderBuffer& getRenderBuffer (RenderBufferHandle handle) const override; // Data buffers - DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle = DataBufferHandle::Invalid()) override; + DataBufferHandle allocateDataBuffer (EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle) override; void releaseDataBuffer (DataBufferHandle handle) override; [[nodiscard]] uint32_t getDataBufferCount () const override; - void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const Byte* data) override; + void updateDataBuffer (DataBufferHandle handle, uint32_t offsetInBytes, uint32_t dataSizeInBytes, const std::byte* data) override; [[nodiscard]] bool isDataBufferAllocated (DataBufferHandle handle) const override; [[nodiscard]] const GeometryDataBuffer& getDataBuffer (DataBufferHandle handle) const override; //Texture buffers - TextureBufferHandle allocateTextureBuffer (ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()) override; + TextureBufferHandle allocateTextureBuffer (EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle) override; void releaseTextureBuffer (TextureBufferHandle handle) override; [[nodiscard]] bool isTextureBufferAllocated (TextureBufferHandle handle) const override; [[nodiscard]] uint32_t getTextureBufferCount () const override; - void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const Byte* data) override; + void updateTextureBuffer (TextureBufferHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const std::byte* data) override; [[nodiscard]] const TextureBuffer& getTextureBuffer (TextureBufferHandle handle) const override; - DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()) override; + DataSlotHandle allocateDataSlot (const DataSlot& dataSlot, DataSlotHandle handle) override; void setDataSlotTexture (DataSlotHandle handle, const ResourceContentHash& texture) override; void releaseDataSlot (DataSlotHandle handle) override; [[nodiscard]] bool isDataSlotAllocated (DataSlotHandle handle) const override; [[nodiscard]] uint32_t getDataSlotCount () const override; [[nodiscard]] const DataSlot& getDataSlot (DataSlotHandle handle) const override; - SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle = {}) override; + SceneReferenceHandle allocateSceneReference (SceneId sceneId, SceneReferenceHandle handle) override; void releaseSceneReference (SceneReferenceHandle handle) override; void requestSceneReferenceState (SceneReferenceHandle handle, RendererSceneState state) override; void requestSceneReferenceFlushNotifications(SceneReferenceHandle handle, bool enable) override; @@ -283,5 +285,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/SceneGraph/Scene/test/DataLayoutCachedSceneTest.cpp b/tests/unittests/framework/SceneGraph/Scene/DataLayoutCachedSceneTest.cpp similarity index 85% rename from framework/SceneGraph/Scene/test/DataLayoutCachedSceneTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/DataLayoutCachedSceneTest.cpp index 578610c54..e6ebb9afa 100644 --- a/framework/SceneGraph/Scene/test/DataLayoutCachedSceneTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/DataLayoutCachedSceneTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Scene/DataLayoutCachedScene.h" +#include "internal/SceneGraph/Scene/DataLayoutCachedScene.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ADataLayoutCachedScene : public testing::Test { @@ -28,30 +27,30 @@ namespace ramses_internal TEST_F(ADataLayoutCachedScene, canAllocateDataLayout) { - const DataLayoutHandle dataLayout = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = scene.allocateDataLayout({DataFieldInfo(EDataType::Float)}, ResourceContentHash(123u, 0u), {}); EXPECT_TRUE(dataLayout.isValid()); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout)); } TEST_F(ADataLayoutCachedScene, canAllocateEmptyDataLayout) { - const DataLayoutHandle dataLayout = scene.allocateDataLayout({}, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = scene.allocateDataLayout({}, ResourceContentHash(123u, 0u), {}); EXPECT_TRUE(dataLayout.isValid()); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout)); } TEST_F(ADataLayoutCachedScene, willGiveSameHandleForExistingLayout) { - const DataLayoutHandle dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 2u, EFixedSemantics::ModelMatrix) }, ResourceContentHash(123u, 0u)); - const DataLayoutHandle dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 2u, EFixedSemantics::ModelMatrix) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 2u, EFixedSemantics::ModelMatrix) }, ResourceContentHash(123u, 0u), {}); + const DataLayoutHandle dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 2u, EFixedSemantics::ModelMatrix) }, ResourceContentHash(123u, 0u), {}); EXPECT_EQ(dataLayout1, dataLayout2); EXPECT_EQ(2u, scene.getNumDataLayoutReferences(dataLayout1)); } TEST_F(ADataLayoutCachedScene, willAllocateNewHandleForNonMatchingDataFieldTypes) { - DataLayoutHandle dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, {}); - DataLayoutHandle dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Int32) }, {}); + DataLayoutHandle dataLayout1 = scene.allocateDataLayout({DataFieldInfo(EDataType::Float)}, {}, {}); + DataLayoutHandle dataLayout2 = scene.allocateDataLayout({DataFieldInfo(EDataType::Int32)}, {}, {}); EXPECT_NE(dataLayout1, dataLayout2); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout1)); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout2)); @@ -59,8 +58,8 @@ namespace ramses_internal TEST_F(ADataLayoutCachedScene, willAllocateNewHandleForNonMatchingDataFieldElementCounts) { - DataLayoutHandle dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 2u) }, {}); - DataLayoutHandle dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 3u) }, {}); + DataLayoutHandle dataLayout1 = scene.allocateDataLayout({DataFieldInfo(EDataType::Float, 2u)}, {}, {}); + DataLayoutHandle dataLayout2 = scene.allocateDataLayout({DataFieldInfo(EDataType::Float, 3u)}, {}, {}); EXPECT_NE(dataLayout1, dataLayout2); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout1)); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout2)); @@ -68,8 +67,8 @@ namespace ramses_internal TEST_F(ADataLayoutCachedScene, willAllocateNewHandleForNonMatchingDataFieldSemantics) { - const auto dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 1u, EFixedSemantics::ViewMatrix) }, {}); - const auto dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 1u, EFixedSemantics::ModelMatrix) }, {}); + const auto dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 1u, EFixedSemantics::ViewMatrix) }, {}, {}); + const auto dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 1u, EFixedSemantics::ModelMatrix) }, {}, {}); EXPECT_NE(dataLayout1, dataLayout2); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout1)); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout2)); @@ -77,8 +76,8 @@ namespace ramses_internal TEST_F(ADataLayoutCachedScene, willAllocateNewHandleForNonMatchingEffectHash) { - const auto dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash{123u, 0u}); - const auto dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash{124u, 0u}); + const auto dataLayout1 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash{123u, 0u}, {}); + const auto dataLayout2 = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash{124u, 0u}, {}); EXPECT_NE(dataLayout1, dataLayout2); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout1)); EXPECT_EQ(1u, scene.getNumDataLayoutReferences(dataLayout2)); @@ -86,9 +85,9 @@ namespace ramses_internal TEST_F(ADataLayoutCachedScene, keepsDataLayoutUntilItsUsageCountDropsToZero) { - const DataLayoutHandle dataLayout = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u)); - EXPECT_EQ(dataLayout, scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u))); - EXPECT_EQ(dataLayout, scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u))); + const DataLayoutHandle dataLayout = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u), {}); + EXPECT_EQ(dataLayout, scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u), {})); + EXPECT_EQ(dataLayout, scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u), {})); EXPECT_EQ(3u, scene.getNumDataLayoutReferences(dataLayout)); scene.releaseDataLayout(dataLayout); @@ -105,18 +104,18 @@ namespace ramses_internal TEST_F(ADataLayoutCachedScene, confidenceTest_canHandleCreationAndCacheLargeDataLayout) { - const DataLayoutHandle dataLayoutPrevious = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayoutPrevious = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u), {}); const DataFieldInfoVector dataFields(100u, DataFieldInfo(EDataType::Int32)); - const DataLayoutHandle dataLayout1 = scene.allocateDataLayout(dataFields, ResourceContentHash(123u, 0u)); - const DataLayoutHandle dataLayout2 = scene.allocateDataLayout(dataFields, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout1 = scene.allocateDataLayout(dataFields, ResourceContentHash(123u, 0u), {}); + const DataLayoutHandle dataLayout2 = scene.allocateDataLayout(dataFields, ResourceContentHash(123u, 0u), {}); EXPECT_TRUE(dataLayout1.isValid()); EXPECT_EQ(dataLayout1, dataLayout2); EXPECT_EQ(2u, scene.getNumDataLayoutReferences(dataLayout1)); // check that previous data layout exists and is cached EXPECT_TRUE(scene.isDataLayoutAllocated(dataLayoutPrevious)); - EXPECT_EQ(dataLayoutPrevious, scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u))); + EXPECT_EQ(dataLayoutPrevious, scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u), {})); EXPECT_EQ(2u, scene.getNumDataLayoutReferences(dataLayout1)); } } diff --git a/framework/SceneGraph/Scene/test/ResourceChangeCollectingSceneTest.cpp b/tests/unittests/framework/SceneGraph/Scene/ResourceChangeCollectingSceneTest.cpp similarity index 88% rename from framework/SceneGraph/Scene/test/ResourceChangeCollectingSceneTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/ResourceChangeCollectingSceneTest.cpp index 2f2b02e0b..86c5b1c89 100644 --- a/framework/SceneGraph/Scene/test/ResourceChangeCollectingSceneTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/ResourceChangeCollectingSceneTest.cpp @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Scene/ResourceChangeCollectingScene.h" -#include "SceneUtils/ResourceUtils.h" +#include "internal/SceneGraph/Scene/ResourceChangeCollectingScene.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" -namespace ramses_internal +namespace ramses::internal { class AResourceChangeCollectingScene : public testing::Test { @@ -43,21 +42,21 @@ namespace ramses_internal protected: RenderableHandle createRenderable() { - return scene.allocateRenderable(scene.allocateNode()); + return scene.allocateRenderable(scene.allocateNode(0, {}), {}); } DataInstanceHandle createUniformDataInstanceWithSampler(RenderableHandle renderable, ResourceContentHash texHash) { - const DataInstanceHandle uniformData = scene.allocateDataInstance(testUniformLayout); + const DataInstanceHandle uniformData = scene.allocateDataInstance(testUniformLayout, {}); scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Uniforms, uniformData); - const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, texHash }); + const TextureSamplerHandle sampler = scene.allocateTextureSampler({{}, texHash}, {}); scene.setDataTextureSamplerHandle(uniformData, samplerField, sampler); return uniformData; } DataInstanceHandle createVertexDataInstance(RenderableHandle renderable) { - const DataInstanceHandle geometryData = scene.allocateDataInstance(testGeometryLayout); + const DataInstanceHandle geometryData = scene.allocateDataInstance(testGeometryLayout, {}); scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Geometry, geometryData); return geometryData; @@ -88,7 +87,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdRenderTargetIsTracked) { - const RenderTargetHandle handle = scene.allocateRenderTarget(); + const RenderTargetHandle handle = scene.allocateRenderTarget({}); ASSERT_EQ(1u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); EXPECT_EQ(ESceneResourceAction_CreateRenderTarget, sceneResourceActions[0].action); @@ -99,7 +98,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, destroyedRenderTargetIsTracked) { - const RenderTargetHandle targetHandle = scene.allocateRenderTarget(); + const RenderTargetHandle targetHandle = scene.allocateRenderTarget({}); scene.resetResourceChanges(); scene.releaseRenderTarget(targetHandle); @@ -113,7 +112,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdRenderBufferIsTracked) { - const RenderBufferHandle handle = scene.allocateRenderBuffer({ 1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::R8, ERenderBufferAccessMode_ReadWrite, 0u }); + const RenderBufferHandle handle = scene.allocateRenderBuffer({ 1u, 1u, EPixelStorageFormat::R8, ERenderBufferAccessMode::ReadWrite, 0u }, {}); ASSERT_EQ(1u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); EXPECT_EQ(ESceneResourceAction_CreateRenderBuffer, sceneResourceActions[0].action); @@ -124,7 +123,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, destroyedRenderBufferIsTracked) { - const RenderBufferHandle bufferHandle = scene.allocateRenderBuffer({ 1u, 1u, ERenderBufferType_ColorBuffer, ETextureFormat::R8, ERenderBufferAccessMode_ReadWrite, 0u }); + const RenderBufferHandle bufferHandle = scene.allocateRenderBuffer({ 1u, 1u, EPixelStorageFormat::R8, ERenderBufferAccessMode::ReadWrite, 0u }, {}); scene.resetResourceChanges(); scene.releaseRenderBuffer(bufferHandle); @@ -138,7 +137,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdBlitPassIsTracked) { - const BlitPassHandle handle = scene.allocateBlitPass(RenderBufferHandle(0u), RenderBufferHandle(1u)); + const BlitPassHandle handle = scene.allocateBlitPass(RenderBufferHandle(0u), RenderBufferHandle(1u), {}); ASSERT_EQ(1u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); EXPECT_EQ(ESceneResourceAction_CreateBlitPass, sceneResourceActions[0].action); @@ -150,7 +149,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, destroyedBlitPassIsTracked) { - const BlitPassHandle handle = scene.allocateBlitPass(RenderBufferHandle(0u), RenderBufferHandle(1u)); + const BlitPassHandle handle = scene.allocateBlitPass(RenderBufferHandle(0u), RenderBufferHandle(1u), {}); scene.resetResourceChanges(); scene.releaseBlitPass(handle); @@ -164,7 +163,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdDataBufferIsTracked) { - const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); ASSERT_EQ(1u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); EXPECT_EQ(ESceneResourceAction_CreateDataBuffer, sceneResourceActions[0].action); @@ -175,7 +174,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdAndUpdatedDataBufferIsTrackedAndSameAsExtractedFromScene) { - const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); scene.updateDataBuffer(handle, 0, 0, nullptr); ASSERT_EQ(2u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); @@ -190,7 +189,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, destroyedDataBufferIsTracked) { - const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); scene.resetResourceChanges(); scene.releaseDataBuffer(handle); @@ -204,7 +203,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, updatedDataBufferIsTracked) { - const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle handle = scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); scene.resetResourceChanges(); scene.updateDataBuffer(handle, 0u, 0u, nullptr); @@ -218,7 +217,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdTextureBufferIsTracked) { - const TextureBufferHandle handle = scene.allocateTextureBuffer(ETextureFormat::R16F, { { 1, 1 } }); + const TextureBufferHandle handle = scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { { 1, 1 } }, {}); ASSERT_EQ(1u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); EXPECT_EQ(ESceneResourceAction_CreateTextureBuffer, sceneResourceActions[0].action); @@ -229,7 +228,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, createdAndUpdatedTextureBufferIsTrackedAndSameAsExtractedFromScene) { - const TextureBufferHandle handle = scene.allocateTextureBuffer(ETextureFormat::R16F, { { 1, 1 } }); + const TextureBufferHandle handle = scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { { 1, 1 } }, {}); scene.updateTextureBuffer(handle, 0, 0, 0, 0, 0, nullptr); ASSERT_EQ(2u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); @@ -244,7 +243,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, destroyedTextureBufferIsTracked) { - const TextureBufferHandle handle = scene.allocateTextureBuffer(ETextureFormat::R16F, { {1, 1} }); + const TextureBufferHandle handle = scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { {1, 1} }, {}); scene.resetResourceChanges(); scene.releaseTextureBuffer(handle); @@ -258,10 +257,10 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, updatedTextureBufferIsTracked) { - const TextureBufferHandle handle = scene.allocateTextureBuffer(ETextureFormat::R16F, { { 1, 1 } }); + const TextureBufferHandle handle = scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { { 1, 1 } }, {}); scene.resetResourceChanges(); - const Byte dummyData[2] = { 0 }; // width*height*sizeof(float16) bytes needed + const std::byte dummyData[2] = { std::byte{0} }; // width*height*sizeof(float16) bytes needed scene.updateTextureBuffer(handle, 0u, 0u, 0u, 1u, 1u, dummyData); ASSERT_EQ(1u, sceneResourceActions.size()); EXPECT_EQ(handle, sceneResourceActions[0].handle); @@ -284,7 +283,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, doesNotMarkClientResourcesDirtyOnNewDataInstance) { - scene.allocateDataInstance(testUniformLayout); + scene.allocateDataInstance(testUniformLayout, {}); EXPECT_FALSE(scene.haveResourcesChanged()); } @@ -352,7 +351,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, marksClientResourcesDirtyWhenAllocatingATextureSampler) { - scene.allocateTextureSampler({ {}, { 123, 0 } }); + scene.allocateTextureSampler({ {}, { 123, 0 } }, {}); EXPECT_TRUE(scene.haveResourcesChanged()); } @@ -361,8 +360,8 @@ namespace ramses_internal TextureSampler sampler; sampler.contentType = TextureSampler::ContentType::RenderBuffer; sampler.textureResource = ResourceContentHash::Invalid(); - sampler.contentHandle = scene.allocateRenderBuffer({}).asMemoryHandle(); - scene.allocateTextureSampler(sampler); + sampler.contentHandle = scene.allocateRenderBuffer({}, {}).asMemoryHandle(); + scene.allocateTextureSampler(sampler, {}); EXPECT_FALSE(scene.haveResourcesChanged()); } @@ -372,14 +371,14 @@ namespace ramses_internal const DataInstanceHandle dataInstance = createUniformDataInstanceWithSampler(renderable, { 123, 0 }); scene.resetResourceChanges(); - const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, { 456, 0 } }); + const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, { 456, 0 } }, {}); scene.setDataTextureSamplerHandle(dataInstance, samplerField, sampler); EXPECT_TRUE(scene.haveResourcesChanged()); } TEST_F(AResourceChangeCollectingScene, marksClientResourcesDirtyWhenReleasingATextureSampler) { - auto handle = scene.allocateTextureSampler({ {}, { 123, 0 } }); + auto handle = scene.allocateTextureSampler({ {}, { 123, 0 } }, {}); scene.resetResourceChanges(); scene.releaseTextureSampler(handle); @@ -391,8 +390,8 @@ namespace ramses_internal TextureSampler sampler; sampler.contentType = TextureSampler::ContentType::RenderBuffer; sampler.textureResource = ResourceContentHash::Invalid(); - sampler.contentHandle = scene.allocateRenderBuffer({}).asMemoryHandle(); - auto handle = scene.allocateTextureSampler(sampler); + sampler.contentHandle = scene.allocateRenderBuffer({}, {}).asMemoryHandle(); + auto handle = scene.allocateTextureSampler(sampler, {}); scene.resetResourceChanges(); scene.releaseTextureSampler(handle); @@ -401,19 +400,19 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, marksClientResourcesDirtyWhenAllocatingADataSlot) { - scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), { 123, 0 }, TextureSamplerHandle() }); + scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), { 123, 0 }, TextureSamplerHandle() }, {}); EXPECT_TRUE(scene.haveResourcesChanged()); } TEST_F(AResourceChangeCollectingScene, marksClientResourcesNotDirtyWhenAllocatingADataSlotWithInvalidTexture) { - scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); + scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, {}); EXPECT_FALSE(scene.haveResourcesChanged()); } TEST_F(AResourceChangeCollectingScene, marksClientResourcesDirtyWhenSettingTextureToDataSlot) { - const DataSlotHandle dataSlot = scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), { 123, 0 }, TextureSamplerHandle() }); + const DataSlotHandle dataSlot = scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), { 123, 0 }, TextureSamplerHandle() }, {}); scene.resetResourceChanges(); scene.setDataSlotTexture(dataSlot, { 456, 0 }); @@ -422,7 +421,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, marksClientResourcesDirtyWhenReleasingADataSlot) { - const DataSlotHandle dataSlot = scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), { 123, 0 }, TextureSamplerHandle() }); + const DataSlotHandle dataSlot = scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), { 123, 0 }, TextureSamplerHandle() }, {}); scene.resetResourceChanges(); scene.releaseDataSlot(dataSlot); @@ -431,7 +430,7 @@ namespace ramses_internal TEST_F(AResourceChangeCollectingScene, marksClientResourcesNotDirtyWhenReleasingADataSlotWithInvalidTexture) { - const DataSlotHandle dataSlot = scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); + const DataSlotHandle dataSlot = scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, {}); scene.resetResourceChanges(); scene.releaseDataSlot(dataSlot); diff --git a/framework/SceneGraph/Scene/test/ResourceUtilsTest.cpp b/tests/unittests/framework/SceneGraph/Scene/ResourceUtilsTest.cpp similarity index 95% rename from framework/SceneGraph/Scene/test/ResourceUtilsTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/ResourceUtilsTest.cpp index d752d4f61..23bec39ee 100644 --- a/framework/SceneGraph/Scene/test/ResourceUtilsTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/ResourceUtilsTest.cpp @@ -7,12 +7,11 @@ // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "SceneUtils/ResourceUtils.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" -namespace ramses_internal +namespace ramses::internal { using SceneTypes = ::testing::Types< ClientScene, @@ -287,13 +286,13 @@ namespace ramses_internal TYPED_TEST(AResourceUtils, addsTextureResourceWhenUsedByCreatedDataSlot) { - this->scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); + this->scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); this->expectResources({ hash1 }); } TYPED_TEST(AResourceUtils, addsTextureResourceWhenUsedBySetDataSlotTexture) { - const auto slotHandle = this->scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); + const auto slotHandle = this->scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); this->expectResources({ hash1 }); this->scene.setDataSlotTexture(slotHandle, hash2); this->expectResources({ hash2 }); @@ -301,7 +300,7 @@ namespace ramses_internal TYPED_TEST(AResourceUtils, removesTextureWhenUsedByReleasedDataSlot) { - const DataSlotHandle dataSlot = this->scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); + const DataSlotHandle dataSlot = this->scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); this->expectResources({ hash1 }); this->scene.releaseDataSlot(dataSlot); @@ -314,7 +313,7 @@ namespace ramses_internal this->createUniformDataInstanceWithSampler(renderable, hash1); this->expectResources({ hash1 }); - this->scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); + this->scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); this->expectResources({ hash1 }); } @@ -324,7 +323,7 @@ namespace ramses_internal this->createUniformDataInstanceWithSampler(renderable, hash1); this->expectResources({ hash1 }); - auto dataSlot = this->scene.allocateDataSlot({ EDataSlotType_TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); + auto dataSlot = this->scene.allocateDataSlot({ EDataSlotType::TextureProvider, DataSlotId(0u), NodeHandle(), DataInstanceHandle(), hash1, TextureSamplerHandle() }, this->template getNextHandle()); this->expectResources({ hash1 }); this->scene.releaseDataSlot(dataSlot); diff --git a/framework/SceneGraph/Scene/test/SceneActionCollectionBasicTypesTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionBasicTypesTest.cpp similarity index 97% rename from framework/SceneGraph/Scene/test/SceneActionCollectionBasicTypesTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionBasicTypesTest.cpp index 125bc500b..583a581ee 100644 --- a/framework/SceneGraph/Scene/test/SceneActionCollectionBasicTypesTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionBasicTypesTest.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneActionCollection.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" #include "gtest/gtest.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/SceneVersionTag.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/SceneVersionTag.h" -namespace ramses_internal +namespace ramses::internal { template class ASceneActionCollectionBasicTypes : public ::testing::Test diff --git a/framework/SceneGraph/Scene/test/SceneActionCollectionComplexTypesTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionComplexTypesTest.cpp similarity index 93% rename from framework/SceneGraph/Scene/test/SceneActionCollectionComplexTypesTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionComplexTypesTest.cpp index b56c90c79..b19675732 100644 --- a/framework/SceneGraph/Scene/test/SceneActionCollectionComplexTypesTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionComplexTypesTest.cpp @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Scene/SceneActionCollection.h" -#include "Utils/BinaryInputStream.h" -#include "Utils/BinaryOutputStream.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" -namespace ramses_internal +namespace ramses::internal { class ASceneActionCollectionComplexTypes : public ::testing::Test { @@ -35,10 +34,7 @@ namespace ramses_internal Suspendisse viverra, orci vel commodo aliquam, sem felis \ egestas tortor, non sollicitudin lorem ipsum ut nullam.") , guid(Guid("9d2aeb99-6eea-4acb-8d93-df619186cff9")) - , bufferSize(96u) - , buffer(bufferSize, 0x71) - , fval(3.0f) - , uival(5u) + , buffer(bufferSize, std::byte{0x71}) { } @@ -50,12 +46,12 @@ namespace ramses_internal const std::string strLong; const Guid guid; - const uint32_t bufferSize; - const std::vector buffer; + const uint32_t bufferSize{96u}; + const std::vector buffer; const float staticBuffer[5] = { 1.f, 2.f, 3.f, 4.f, 5.f }; - const float fval; - const uint32_t uival; + const float fval{3.0f}; + const uint32_t uival{5u}; }; TEST_F(ASceneActionCollectionComplexTypes, WriteStringAndCheckBufferSize) @@ -196,8 +192,8 @@ namespace ramses_internal collection.write(buffer.data(), bufferSize); SceneActionCollection::SceneActionReader reader(collection[0]); - const Byte* data = nullptr; - uint32_t size; + const std::byte* data = nullptr; + uint32_t size = 0u; reader.readWithoutCopy(data, size); EXPECT_TRUE(reader.isFullyRead()); @@ -214,8 +210,8 @@ namespace ramses_internal collection.write(buffer.data(), 0u); SceneActionCollection::SceneActionReader reader(collection[0]); - const Byte* data = nullptr; - uint32_t size; + const std::byte* data = nullptr; + uint32_t size = 0u; reader.readWithoutCopy(data, size); EXPECT_TRUE(reader.isFullyRead()); @@ -257,7 +253,7 @@ namespace ramses_internal ASSERT_EQ(1u, collection.numberOfActions()); SceneActionCollection::SceneActionReader reader(collection[0]); - float readFval; + float readFval = NAN; reader.read(readFval); EXPECT_EQ(fval, readFval); @@ -265,8 +261,8 @@ namespace ramses_internal reader.read(readStr1); EXPECT_EQ(str, readStr1); - const Byte* data = nullptr; - uint32_t size; + const std::byte* data = nullptr; + uint32_t size = 0u; reader.readWithoutCopy(data, size); EXPECT_EQ(buffer, absl::MakeSpan(data, size)); @@ -283,7 +279,7 @@ namespace ramses_internal reader.read(readStr3); EXPECT_EQ(strEmpty, readStr3); - uint32_t readUival; + uint32_t readUival = 0u; reader.read(readUival); EXPECT_EQ(uival, readUival); diff --git a/framework/SceneGraph/Scene/test/SceneActionCollectionCreatorAndApplierTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionCreatorAndApplierTest.cpp similarity index 85% rename from framework/SceneGraph/Scene/test/SceneActionCollectionCreatorAndApplierTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionCreatorAndApplierTest.cpp index 2ecb680de..3575099ec 100644 --- a/framework/SceneGraph/Scene/test/SceneActionCollectionCreatorAndApplierTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionCreatorAndApplierTest.cpp @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "Scene/SceneActionApplier.h" -#include "Components/FlushTimeInformation.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/Components/FlushTimeInformation.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ASceneActionCollectionCreatorAndApplier : public ::testing::Test { diff --git a/framework/SceneGraph/Scene/test/SceneActionCollectionTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionTest.cpp similarity index 95% rename from framework/SceneGraph/Scene/test/SceneActionCollectionTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionTest.cpp index 81920ebeb..9d6f84e3b 100644 --- a/framework/SceneGraph/Scene/test/SceneActionCollectionTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionCollectionTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Scene/SceneActionCollection.h" -#include "PlatformAbstraction/PlatformMemory.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class ASceneActionCollection : public ::testing::Test { @@ -181,13 +181,13 @@ namespace ramses_internal SceneActionCollection c; c.beginWriteSceneAction(ESceneActionId::TestAction); c.write(123u); - std::vector oldDataC(c.collectionData()); + std::vector oldDataC(c.collectionData()); SceneActionCollection d; d.beginWriteSceneAction(ESceneActionId::AllocateNode); d.write(456u); d.beginWriteSceneAction(ESceneActionId::ReleaseRenderable); - std::vector oldDataD(d.collectionData()); + std::vector oldDataD(d.collectionData()); c.swap(d); @@ -206,13 +206,13 @@ namespace ramses_internal SceneActionCollection c; c.beginWriteSceneAction(ESceneActionId::TestAction); c.write(123u); - std::vector oldDataC(c.collectionData()); + std::vector oldDataC(c.collectionData()); SceneActionCollection d; d.beginWriteSceneAction(ESceneActionId::AllocateNode); d.write(456u); d.beginWriteSceneAction(ESceneActionId::ReleaseRenderable); - std::vector oldDataD(d.collectionData()); + std::vector oldDataD(d.collectionData()); using std::swap; swap(c, d); @@ -273,7 +273,7 @@ namespace ramses_internal d.beginWriteSceneAction(ESceneActionId::AllocateNode); d.write(456u); - std::vector oldDataC(c.collectionData()); + std::vector oldDataC(c.collectionData()); c.append(d); EXPECT_EQ(oldDataC.size() + d.collectionData().size(), c.collectionData().size()); @@ -317,7 +317,7 @@ namespace ramses_internal const SceneActionCollection::SceneActionReader incompleteReader(c.back()); const uint32_t offsetIncomplete = incompleteReader.offsetInCollection(); const uint32_t sizeIncomplete = incompleteReader.size(); - std::vector data(c.collectionData()); + std::vector data(c.collectionData()); SceneActionCollection d; d.beginWriteSceneAction(ESceneActionId::AllocateNode); @@ -340,7 +340,7 @@ namespace ramses_internal c.write(123u); const uint32_t sizeIncomplete = c.front().size(); - std::vector data(c.collectionData()); + std::vector data(c.collectionData()); SceneActionCollection dTemp; dTemp.beginWriteSceneAction(ESceneActionId::TestAction); @@ -373,7 +373,7 @@ namespace ramses_internal c.write(123u); const uint32_t sizeIncomplete = c.front().size(); - std::vector data(c.collectionData()); + std::vector data(c.collectionData()); SceneActionCollection d; d.beginWriteSceneAction(ESceneActionId::Incomplete); @@ -403,7 +403,7 @@ namespace ramses_internal c.write(123u); c.write(456u); - unsigned int value; + unsigned int value = 0u; SceneActionCollection::SceneActionReader reader(c[0]); EXPECT_FALSE(reader.isFullyRead()); reader.read(value); @@ -420,7 +420,7 @@ namespace ramses_internal c.beginWriteSceneAction(ESceneActionId::AllocateNode); c.write(456u); - unsigned int value; + unsigned int value = 0u; SceneActionCollection::SceneActionReader reader(c[1]); EXPECT_FALSE(reader.isFullyRead()); reader.read(value); @@ -531,7 +531,7 @@ namespace ramses_internal c.write(789u); SceneActionCollection d; - std::vector& rawDataD = d.getRawDataForDirectWriting(); + std::vector& rawDataD = d.getRawDataForDirectWriting(); rawDataD.insert(rawDataD.end(), c.collectionData().begin(), c.collectionData().end()); EXPECT_EQ(c.collectionData(), d.collectionData()); @@ -545,7 +545,7 @@ namespace ramses_internal TEST_F(ASceneActionCollection, hasCorrectActionOffsetsAndSizesAfterWritingMultipleActionsWithDifferentTypes) { uint32_t bufferSize = 102; - std::vector buffer(bufferSize, 0xFA); + std::vector buffer(bufferSize, std::byte{0xFA}); std::string str{"hello world"}; SceneActionCollection c; diff --git a/framework/SceneGraph/Scene/test/SceneActionHelperAndApplierTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionHelperAndApplierTest.cpp similarity index 97% rename from framework/SceneGraph/Scene/test/SceneActionHelperAndApplierTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionHelperAndApplierTest.cpp index 5324bf5f0..826763335 100644 --- a/framework/SceneGraph/Scene/test/SceneActionHelperAndApplierTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionHelperAndApplierTest.cpp @@ -8,21 +8,20 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "Scene/Scene.h" -#include "SceneAPI/RenderState.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "Scene/SceneActionApplier.h" -#include "Resource/IResource.h" +#include "internal/SceneGraph/Scene/Scene.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Resource/IResource.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class SceneForSceneActionApplierTesting : public Scene { public: - SceneForSceneActionApplierTesting() - {} + SceneForSceneActionApplierTesting() = default; MOCK_METHOD(RenderableHandle, allocateRenderable, (NodeHandle, RenderableHandle), (override)); MOCK_METHOD(void , setRenderableStartIndex, (RenderableHandle, uint32_t), (override)); diff --git a/framework/SceneGraph/Scene/test/SceneActionUtils.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionUtils.cpp similarity index 98% rename from framework/SceneGraph/Scene/test/SceneActionUtils.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionUtils.cpp index 068cd8717..e2134cb0b 100644 --- a/framework/SceneGraph/Scene/test/SceneActionUtils.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionUtils.cpp @@ -10,7 +10,7 @@ #include "SceneActionUtils.h" #include -namespace ramses_internal +namespace ramses::internal { uint32_t SceneActionCollectionUtils::CountNumberOfActionsOfType(const SceneActionCollection& actions, ESceneActionId type) { diff --git a/framework/SceneGraph/Scene/test/SceneActionUtils.h b/tests/unittests/framework/SceneGraph/Scene/SceneActionUtils.h similarity index 85% rename from framework/SceneGraph/Scene/test/SceneActionUtils.h rename to tests/unittests/framework/SceneGraph/Scene/SceneActionUtils.h index 5a408869e..2023b3d59 100644 --- a/framework/SceneGraph/Scene/test/SceneActionUtils.h +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionUtils.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEACTIONUTILS_H -#define RAMSES_SCENEACTIONUTILS_H +#pragma once -#include "Scene/SceneActionCollection.h" +#include "internal/SceneGraph/Scene/SceneActionCollection.h" -namespace ramses_internal +namespace ramses::internal { using SceneActionIdVector = std::vector ; @@ -22,5 +21,3 @@ namespace ramses_internal static uint32_t CountNumberOfActionsOfType(const SceneActionCollection& actions, const SceneActionIdVector& types); }; } - -#endif diff --git a/framework/SceneGraph/Scene/test/SceneActionUtilsTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneActionUtilsTest.cpp similarity index 96% rename from framework/SceneGraph/Scene/test/SceneActionUtilsTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneActionUtilsTest.cpp index 6e8361dd2..69ac0fe1a 100644 --- a/framework/SceneGraph/Scene/test/SceneActionUtilsTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneActionUtilsTest.cpp @@ -6,14 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- - -#ifndef RAMSES_SCENEACTIONUTILSTEST_H -#define RAMSES_SCENEACTIONUTILSTEST_H - #include "SceneActionUtils.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { class SceneActionVectorUtilsTest : public testing::Test { @@ -69,7 +65,7 @@ namespace ramses_internal multiType.push_back(ESceneActionId::AllocateCamera); } - static SceneActionCollection createSceneActionVector(const std::vector& types) + static SceneActionCollection createSceneActionVector(const std::vector& types) { SceneActionCollection result; for(auto type : types) @@ -133,5 +129,3 @@ namespace ramses_internal EXPECT_EQ(0u, SceneActionCollectionUtils::CountNumberOfActionsOfType(actionsEmpty, multiType)); } } - -#endif diff --git a/framework/SceneGraph/Scene/test/SceneDescriberTest.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneDescriberTest.cpp similarity index 70% rename from framework/SceneGraph/Scene/test/SceneDescriberTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneDescriberTest.cpp index 6175d6dba..a76d1b900 100644 --- a/framework/SceneGraph/Scene/test/SceneDescriberTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneDescriberTest.cpp @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gmock/gmock.h" -#include "Scene/SceneDescriber.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/SceneDescriber.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include "SceneActionUtils.h" -#include "Scene/SceneActionCollectionCreator.h" -#include "Scene/SceneActionApplier.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" +#include "internal/SceneGraph/Scene/SceneActionApplier.h" #include "TestEqualHelper.h" -#include "Utils/MemoryUtils.h" +#include "internal/Core/Utils/MemoryUtils.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class SceneDescriberTest : public testing::Test { @@ -27,7 +26,7 @@ namespace ramses_internal : creator(actions) {} - void expectAllocateNodeAction(SceneActionCollection::SceneActionReader action, NodeHandle handle, uint32_t expectedChildrenCount) + static void ExpectAllocateNodeAction(SceneActionCollection::SceneActionReader action, NodeHandle handle, uint32_t expectedChildrenCount) { EXPECT_EQ(ESceneActionId::AllocateNode, action.type()); NodeHandle actualHandle; @@ -38,7 +37,7 @@ namespace ramses_internal EXPECT_EQ(handle, actualHandle); } - void expectAddChildToNodeAction(SceneActionCollection::SceneActionReader action, NodeHandle parent, NodeHandle child) + static void ExpectAddChildToNodeAction(SceneActionCollection::SceneActionReader action, NodeHandle parent, NodeHandle child) { EXPECT_EQ(ESceneActionId::AddChildToNode, action.type()); NodeHandle actualParent; @@ -51,33 +50,22 @@ namespace ramses_internal struct RenderableCreationData { - RenderableCreationData() - : startIndex(3u) - , indexCount(9u) - , instanceCount(7u) - , visibility(EVisibilityMode::Invisible) - , state(23u) - , effectHash(47u, 0u) - , geoInstanceHandle(37u) - , uniformInstanceHandle(77u) - , startVertex(199u) - {} - - uint32_t startIndex; - uint32_t indexCount; - uint32_t instanceCount; - EVisibilityMode visibility; - RenderStateHandle state; - ResourceContentHash effectHash; - DataInstanceHandle geoInstanceHandle; - DataInstanceHandle uniformInstanceHandle; - uint32_t startVertex; + RenderableCreationData() {} // NOLINT(modernize-use-equals-default): build issues with clang-12 + uint32_t startIndex{3u}; + uint32_t indexCount{9u}; + uint32_t instanceCount{7u}; + EVisibilityMode visibility{EVisibilityMode::Invisible}; + RenderStateHandle state{23u}; + ResourceContentHash effectHash{47u, 0u}; + DataInstanceHandle geoInstanceHandle{37u}; + DataInstanceHandle uniformInstanceHandle{77u}; + uint32_t startVertex{199u}; }; - void createRenderable(const RenderableCreationData& data = RenderableCreationData()) + void createRenderable(const RenderableCreationData& data = RenderableCreationData{}) { - const NodeHandle node = m_scene.allocateNode(); - const RenderableHandle renderable = m_scene.allocateRenderable(node); + const NodeHandle node = m_scene.allocateNode(0, {}); + const RenderableHandle renderable = m_scene.allocateRenderable(node, {}); m_scene.setRenderableStartIndex(renderable, data.startIndex); m_scene.setRenderableIndexCount(renderable, data.indexCount); m_scene.setRenderableRenderState(renderable, data.state); @@ -90,56 +78,32 @@ namespace ramses_internal struct StateCreationData { - StateCreationData() - : bfSrcColor(EBlendFactor::DstAlpha) - , bfDstColor(EBlendFactor::One) - , bfSrcAlpha(EBlendFactor::OneMinusSrcAlpha) - , bfDstAlpha(EBlendFactor::SrcAlpha) - , boColor(EBlendOperation::Subtract) - , boAlpha(EBlendOperation::Max) - , blendColor(0.1f, 0.2f, 0.3f, 0.4f) - , cullMode(ECullMode::BackFacing) - , drawMode(EDrawMode::Lines) - , depthWrite(EDepthWrite::Enabled) - , depthFunc(EDepthFunc::LessEqual) - , scissorTest(EScissorTest::Enabled) - , scissorRegion{ - 12, 14, 16, 18 - } - , stencilFunc(EStencilFunc::NotEqual) - , stencilRefValue(99u) - , stencilMask(3u) - , stencilOpFail(EStencilOp::Replace) - , stencilOpDepthFail(EStencilOp::Decrement) - , stencilOpDepthPass(EStencilOp::Zero) - , colorWriteMask(0x80) - {} - - EBlendFactor bfSrcColor; - EBlendFactor bfDstColor; - EBlendFactor bfSrcAlpha; - EBlendFactor bfDstAlpha; - EBlendOperation boColor; - EBlendOperation boAlpha; - glm::vec4 blendColor; - ECullMode cullMode; - EDrawMode drawMode; - EDepthWrite depthWrite; - EDepthFunc depthFunc; - EScissorTest scissorTest; - RenderState::ScissorRegion scissorRegion; - EStencilFunc stencilFunc; - uint8_t stencilRefValue; - uint8_t stencilMask; - EStencilOp stencilOpFail; - EStencilOp stencilOpDepthFail; - EStencilOp stencilOpDepthPass; - ColorWriteMask colorWriteMask; + StateCreationData() {} // NOLINT(modernize-use-equals-default): build issues with clang-12 + EBlendFactor bfSrcColor{EBlendFactor::DstAlpha}; + EBlendFactor bfDstColor{EBlendFactor::One}; + EBlendFactor bfSrcAlpha{EBlendFactor::OneMinusSrcAlpha}; + EBlendFactor bfDstAlpha{EBlendFactor::SrcAlpha}; + EBlendOperation boColor{EBlendOperation::Subtract}; + EBlendOperation boAlpha{EBlendOperation::Max}; + glm::vec4 blendColor{0.1f, 0.2f, 0.3f, 0.4f}; + ECullMode cullMode{ECullMode::BackFacing}; + EDrawMode drawMode{EDrawMode::Lines}; + EDepthWrite depthWrite{EDepthWrite::Enabled}; + EDepthFunc depthFunc{EDepthFunc::LessEqual}; + EScissorTest scissorTest{EScissorTest::Enabled}; + RenderState::ScissorRegion scissorRegion{12, 14, 16, 18}; + EStencilFunc stencilFunc{EStencilFunc::NotEqual}; + uint8_t stencilRefValue{99u}; + uint8_t stencilMask{3u}; + EStencilOp stencilOpFail{EStencilOp::Replace}; + EStencilOp stencilOpDepthFail{EStencilOp::Decrement}; + EStencilOp stencilOpDepthPass{EStencilOp::Zero}; + ColorWriteMask colorWriteMask{0x80}; }; void createState(const StateCreationData& data = StateCreationData()) { - const RenderStateHandle state = m_scene.allocateRenderState(); + const RenderStateHandle state = m_scene.allocateRenderState({}); m_scene.setRenderStateBlendFactors( state, data.bfSrcColor, data.bfDstColor, data.bfSrcAlpha, data.bfDstAlpha); m_scene.setRenderStateBlendOperations(state, data.boColor, data.boAlpha); m_scene.setRenderStateBlendColor( state, data.blendColor); @@ -160,10 +124,10 @@ namespace ramses_internal TEST_F(SceneDescriberTest, checksDescriptionActionsForSceneWithParentAndThreeChildren) { - NodeHandle parent = m_scene.allocateNode(); - NodeHandle child2 = m_scene.allocateNode(); - NodeHandle child3 = m_scene.allocateNode(); - NodeHandle child1 = m_scene.allocateNode(); + NodeHandle parent = m_scene.allocateNode(0, {}); + NodeHandle child2 = m_scene.allocateNode(0, {}); + NodeHandle child3 = m_scene.allocateNode(0, {}); + NodeHandle child1 = m_scene.allocateNode(0, {}); m_scene.addChildToNode(parent, child1); m_scene.addChildToNode(parent, child2); @@ -174,14 +138,14 @@ namespace ramses_internal ASSERT_EQ(7u, actions.numberOfActions()); uint32_t actionIdx = 0u; - expectAllocateNodeAction (actions[actionIdx++], parent, 3u); - expectAllocateNodeAction (actions[actionIdx++], child2, 0u); - expectAllocateNodeAction (actions[actionIdx++], child3, 0u); - expectAllocateNodeAction (actions[actionIdx++], child1, 0u); + ExpectAllocateNodeAction (actions[actionIdx++], parent, 3u); + ExpectAllocateNodeAction (actions[actionIdx++], child2, 0u); + ExpectAllocateNodeAction (actions[actionIdx++], child3, 0u); + ExpectAllocateNodeAction (actions[actionIdx++], child1, 0u); - expectAddChildToNodeAction(actions[actionIdx++], parent, child1); - expectAddChildToNodeAction(actions[actionIdx++], parent, child2); - expectAddChildToNodeAction(actions[actionIdx++], parent, child3); + ExpectAddChildToNodeAction(actions[actionIdx++], parent, child1); + ExpectAddChildToNodeAction(actions[actionIdx++], parent, child2); + ExpectAddChildToNodeAction(actions[actionIdx++], parent, child3); } TEST_F(SceneDescriberTest, skipSceneActionForDataInstancesWithBinaryDataEqualZero) @@ -199,11 +163,12 @@ namespace ramses_internal DataFieldInfo{ EDataType::Vector4I, dataFieldElementCount, EFixedSemantics::Invalid }, DataFieldInfo{ EDataType::Matrix22F, dataFieldElementCount, EFixedSemantics::Invalid }, DataFieldInfo{ EDataType::Matrix33F, dataFieldElementCount, EFixedSemantics::Invalid }, - DataFieldInfo{ EDataType::Matrix44F, dataFieldElementCount, EFixedSemantics::Invalid } + DataFieldInfo{ EDataType::Matrix44F, dataFieldElementCount, EFixedSemantics::Invalid }, + DataFieldInfo{ EDataType::Bool, dataFieldElementCount, EFixedSemantics::Invalid } }; - const DataLayoutHandle dataLayout = m_scene.allocateDataLayout(dataFieldInfos, ResourceContentHash::Invalid()); - const DataInstanceHandle dataInstance = m_scene.allocateDataInstance(dataLayout); + const DataLayoutHandle dataLayout = m_scene.allocateDataLayout(dataFieldInfos, ResourceContentHash::Invalid(), {}); + const DataInstanceHandle dataInstance = m_scene.allocateDataInstance(dataLayout, {}); SceneDescriber::describeScene(m_scene, creator); @@ -228,6 +193,7 @@ namespace ramses_internal EXPECT_TRUE(MemoryUtils::AreAllBytesZero(newScene.getDataMatrix22fArray(dataInstance, DataFieldHandle(8u)), dataFieldElementCount)); EXPECT_TRUE(MemoryUtils::AreAllBytesZero(newScene.getDataMatrix33fArray(dataInstance, DataFieldHandle(9u)), dataFieldElementCount)); EXPECT_TRUE(MemoryUtils::AreAllBytesZero(newScene.getDataMatrix44fArray(dataInstance, DataFieldHandle(10u)), dataFieldElementCount)); + EXPECT_TRUE(MemoryUtils::AreAllBytesZero(newScene.getDataBooleanArray(dataInstance, DataFieldHandle(11u)), dataFieldElementCount)); } TEST_F(SceneDescriberTest, checksDescriptionActionsForSceneWithRenderableAndCompoundAction) @@ -250,9 +216,9 @@ namespace ramses_internal TEST_F(SceneDescriberTest, sameAmountOfDataLayoutsCreatedIsSerializedToSceneActions_withoutCompacting) { - const DataLayoutHandle dataLayout1 = m_scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u)); - const DataLayoutHandle dataLayout2 = m_scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u)); - const DataLayoutHandle dataLayout3 = m_scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout1 = m_scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u), {}); + const DataLayoutHandle dataLayout2 = m_scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u), {}); + const DataLayoutHandle dataLayout3 = m_scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u), {}); // the test itself does not need to test that the compacting happened or not // but it is actually the main purpose to check that the describer properly expands the actions if compacted @@ -269,9 +235,9 @@ namespace ramses_internal TEST_F(SceneDescriberTest, sameAmountOfDataLayoutsCreatedIsSerializedToSceneActions_compactingUsed) { ClientScene scene; - const DataLayoutHandle dataLayout1 = scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u)); - const DataLayoutHandle dataLayout2 = scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u)); - const DataLayoutHandle dataLayout3 = scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout1 = scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u), {}); + const DataLayoutHandle dataLayout2 = scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u), {}); + const DataLayoutHandle dataLayout3 = scene.allocateDataLayout({ DataFieldInfo{ EDataType::Float }, DataFieldInfo{ EDataType::Float } }, ResourceContentHash(123u, 0u), {}); // the test itself does not need to test that the compacting happened or not // but it is actually the main purpose to check that the describer properly expands the actions if compacted diff --git a/framework/SceneGraph/Scene/test/ScenePersistationTest.cpp b/tests/unittests/framework/SceneGraph/Scene/ScenePersistationTest.cpp similarity index 83% rename from framework/SceneGraph/Scene/test/ScenePersistationTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/ScenePersistationTest.cpp index 3069f6ced..a7673f4c7 100644 --- a/framework/SceneGraph/Scene/test/ScenePersistationTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/ScenePersistationTest.cpp @@ -6,21 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" #include "gtest/gtest.h" -#include "Scene/ScenePersistation.h" -#include "Scene/ClientScene.h" +#include "internal/SceneGraph/Scene/ScenePersistation.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include "TestingScene.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TEST(AScenePersistation, canReadWrite) { ClientScene scene; - NodeHandle parentNodeHandle = scene.allocateNode(); - NodeHandle childNodeHandle = scene.allocateNode(); + NodeHandle parentNodeHandle = scene.allocateNode(0, {}); + NodeHandle childNodeHandle = scene.allocateNode(0, {}); scene.addChildToNode(parentNodeHandle, childNodeHandle); ScenePersistation::WriteSceneToFile("testfile", scene); diff --git a/framework/SceneGraph/Scene/test/SceneTest.h b/tests/unittests/framework/SceneGraph/Scene/SceneTest.h similarity index 75% rename from framework/SceneGraph/Scene/test/SceneTest.h rename to tests/unittests/framework/SceneGraph/Scene/SceneTest.h index 2d43cc44e..aec366c38 100644 --- a/framework/SceneGraph/Scene/test/SceneTest.h +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest.h @@ -7,18 +7,16 @@ // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENETEST_H -#define RAMSES_SCENETEST_H +#pragma once -#include "framework_common_gmock_header.h" -#include "SceneAPI/RenderState.h" -#include "SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" #include "gtest/gtest.h" #include "ActionTestScene.h" -#include "Scene/ResourceChangeCollectingScene.h" -#include "Scene/DataLayoutCachedScene.h" +#include "internal/SceneGraph/Scene/ResourceChangeCollectingScene.h" +#include "internal/SceneGraph/Scene/DataLayoutCachedScene.h" -namespace ramses_internal +namespace ramses::internal { using SceneTypes = ::testing::Types< Scene, @@ -37,5 +35,3 @@ namespace ramses_internal }; } - -#endif diff --git a/framework/SceneGraph/Scene/test/SceneTest_BlitPasses.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_BlitPasses.cpp similarity index 86% rename from framework/SceneGraph/Scene/test/SceneTest_BlitPasses.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_BlitPasses.cpp index 1d9976348..424c4335b 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_BlitPasses.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_BlitPasses.cpp @@ -11,7 +11,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -19,7 +19,7 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getBlitPassCount()); - BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); EXPECT_EQ(1u, this->m_scene.getBlitPassCount()); EXPECT_TRUE(this->m_scene.isBlitPassAllocated(pass)); @@ -27,7 +27,7 @@ namespace ramses_internal TYPED_TEST(AScene, BlitPassReleased) { - BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); this->m_scene.releaseBlitPass(pass); EXPECT_FALSE(this->m_scene.isBlitPassAllocated(pass)); @@ -40,10 +40,10 @@ namespace ramses_internal TYPED_TEST(AScene, BlitPassCanGetSourceAndDestinationRenderBuffers) { - const RenderBufferHandle sourceRenderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }); - const RenderBufferHandle destinationRenderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }); + const RenderBufferHandle sourceRenderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + const RenderBufferHandle destinationRenderBuffer = this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, {}); - BlitPassHandle pass = this->m_scene.allocateBlitPass(sourceRenderBuffer, destinationRenderBuffer); + BlitPassHandle pass = this->m_scene.allocateBlitPass(sourceRenderBuffer, destinationRenderBuffer, {}); EXPECT_EQ(sourceRenderBuffer, this->m_scene.getBlitPass(pass).sourceRenderBuffer); EXPECT_EQ(destinationRenderBuffer, this->m_scene.getBlitPass(pass).destinationRenderBuffer); } @@ -60,7 +60,7 @@ namespace ramses_internal 5u, 6u, 7u, 8u }; - BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); this->m_scene.setBlitPassRegions(pass, sourceRegion, destinationRegion); const BlitPass& blitPass = this->m_scene.getBlitPass(pass); @@ -77,7 +77,7 @@ namespace ramses_internal TYPED_TEST(AScene, CanDisableBlitPass) { - BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); EXPECT_TRUE(this->m_scene.getBlitPass(pass).isEnabled); this->m_scene.setBlitPassEnabled(pass, false); @@ -86,7 +86,7 @@ namespace ramses_internal TYPED_TEST(AScene, createsBlitPassWithOrderZeroAndSetsOrderTwice) { - BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u)); + BlitPassHandle pass = this->m_scene.allocateBlitPass(RenderBufferHandle(1u), RenderBufferHandle(2u), {}); EXPECT_EQ(0, this->m_scene.getBlitPass(pass).renderOrder); this->m_scene.setBlitPassRenderOrder(pass, 42); diff --git a/framework/SceneGraph/Scene/test/SceneTest_Camera.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Camera.cpp similarity index 83% rename from framework/SceneGraph/Scene/test/SceneTest_Camera.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_Camera.cpp index 78cb6cd17..09e6ba8bd 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_Camera.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Camera.cpp @@ -11,19 +11,19 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); static DataInstanceHandle AllocDummyDataInstance(IScene& scene) { - const auto dataLayout = scene.allocateDataLayout({ DataFieldInfo{ramses_internal::EDataType::Vector2I}, DataFieldInfo{ramses_internal::EDataType::Vector2I} }, ResourceContentHash(123u, 0u)); - return scene.allocateDataInstance(dataLayout); + const auto dataLayout = scene.allocateDataLayout({DataFieldInfo{ramses::internal::EDataType::Vector2I}, DataFieldInfo{ramses::internal::EDataType::Vector2I}}, ResourceContentHash(123u, 0u), {}); + return scene.allocateDataInstance(dataLayout, {}); } static CameraHandle AllocCamera(ECameraProjectionType type, IScene& scene) { - return scene.allocateCamera(type, scene.allocateNode(), AllocDummyDataInstance(scene)); + return scene.allocateCamera(type, scene.allocateNode(0, {}), AllocDummyDataInstance(scene), {}); } TYPED_TEST(AScene, DoesNotHaveAnyCamerasInitially) @@ -51,15 +51,15 @@ namespace ramses_internal TYPED_TEST(AScene, GetsAssignedCameraNode) { - const NodeHandle node = this->m_scene.allocateNode(); - const CameraHandle camera = this->m_scene.allocateCamera(ECameraProjectionType::Perspective, node, AllocDummyDataInstance(this->m_scene)); + const NodeHandle node = this->m_scene.allocateNode(0, {}); + const CameraHandle camera = this->m_scene.allocateCamera(ECameraProjectionType::Perspective, node, AllocDummyDataInstance(this->m_scene), {}); EXPECT_EQ(node, this->m_scene.getCamera(camera).node); } TYPED_TEST(AScene, GetsAssignedDataInstance) { const auto dataInst = AllocDummyDataInstance(this->m_scene); - const CameraHandle camera = this->m_scene.allocateCamera(ECameraProjectionType::Perspective, this->m_scene.allocateNode(), dataInst); + const CameraHandle camera = this->m_scene.allocateCamera(ECameraProjectionType::Perspective, this->m_scene.allocateNode(0, {}), dataInst, {}); EXPECT_EQ(dataInst, this->m_scene.getCamera(camera).dataInstance); } diff --git a/framework/SceneGraph/Scene/test/SceneTest_DataBuffers.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataBuffers.cpp similarity index 66% rename from framework/SceneGraph/Scene/test/SceneTest_DataBuffers.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_DataBuffers.cpp index a9521456f..dd62cc28d 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_DataBuffers.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataBuffers.cpp @@ -8,12 +8,12 @@ #include "SceneTest.h" -#include "SceneAPI/GeometryDataBuffer.h" +#include "internal/SceneGraph/SceneAPI/GeometryDataBuffer.h" #include using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -21,7 +21,7 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getDataBufferCount()); - const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); EXPECT_EQ(1u, this->m_scene.getDataBufferCount()); EXPECT_TRUE(this->m_scene.isDataBufferAllocated(dataBuffer)); @@ -29,7 +29,7 @@ namespace ramses_internal TYPED_TEST(AScene, DataBufferReleased) { - const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); this->m_scene.releaseDataBuffer(dataBuffer); EXPECT_FALSE(this->m_scene.isDataBufferAllocated(dataBuffer)); @@ -42,35 +42,41 @@ namespace ramses_internal TYPED_TEST(AScene, CanGetDataBufferType) { - const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); EXPECT_EQ(EDataBufferType::IndexBuffer, this->m_scene.getDataBuffer(dataBuffer).bufferType); } TYPED_TEST(AScene, CanGetDataBufferDataType) { - const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); EXPECT_EQ(EDataType::UInt32, this->m_scene.getDataBuffer(dataBuffer).dataType); } TYPED_TEST(AScene, CanGetDataBufferSize) { - const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); + const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); EXPECT_EQ(10u, this->m_scene.getDataBuffer(dataBuffer).data.size()); } + template + std::array make_byte_array(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + TYPED_TEST(AScene, CanUpdateDataBuffer) { - const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); - this->m_scene.updateDataBuffer(dataBuffer, 0u, 4u, std::array{{ 0x0A, 0x1B, 0x2C, 0x3D }}.data()); - EXPECT_EQ(0x0A, this->m_scene.getDataBuffer(dataBuffer).data[0]); - EXPECT_EQ(0x1B, this->m_scene.getDataBuffer(dataBuffer).data[1]); - EXPECT_EQ(0x2C, this->m_scene.getDataBuffer(dataBuffer).data[2]); - EXPECT_EQ(0x3D, this->m_scene.getDataBuffer(dataBuffer).data[3]); - - this->m_scene.updateDataBuffer(dataBuffer, 1u, 2u, std::array{ { 0x77, 0xAB }}.data()); - EXPECT_EQ(0x0A, this->m_scene.getDataBuffer(dataBuffer).data[0]); //stays same - EXPECT_EQ(0x77, this->m_scene.getDataBuffer(dataBuffer).data[1]); - EXPECT_EQ(0xAB, this->m_scene.getDataBuffer(dataBuffer).data[2]); - EXPECT_EQ(0x3D, this->m_scene.getDataBuffer(dataBuffer).data[3]); //stays same + const DataBufferHandle dataBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); + this->m_scene.updateDataBuffer(dataBuffer, 0u, 4u, make_byte_array(0x0A, 0x1B, 0x2C, 0x3D).data()); + EXPECT_EQ(std::byte{0x0A}, this->m_scene.getDataBuffer(dataBuffer).data[0]); + EXPECT_EQ(std::byte{0x1B}, this->m_scene.getDataBuffer(dataBuffer).data[1]); + EXPECT_EQ(std::byte{0x2C}, this->m_scene.getDataBuffer(dataBuffer).data[2]); + EXPECT_EQ(std::byte{0x3D}, this->m_scene.getDataBuffer(dataBuffer).data[3]); + + this->m_scene.updateDataBuffer(dataBuffer, 1u, 2u, make_byte_array(0x77, 0xAB).data()); + EXPECT_EQ(std::byte{0x0A}, this->m_scene.getDataBuffer(dataBuffer).data[0]); //stays same + EXPECT_EQ(std::byte{0x77}, this->m_scene.getDataBuffer(dataBuffer).data[1]); + EXPECT_EQ(std::byte{0xAB}, this->m_scene.getDataBuffer(dataBuffer).data[2]); + EXPECT_EQ(std::byte{0x3D}, this->m_scene.getDataBuffer(dataBuffer).data[3]); //stays same } } diff --git a/framework/SceneGraph/Scene/test/SceneTest_DataInstances.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataInstances.cpp similarity index 83% rename from framework/SceneGraph/Scene/test/SceneTest_DataInstances.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_DataInstances.cpp index 5361d64c9..841f8b6cf 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_DataInstances.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataInstances.cpp @@ -8,14 +8,13 @@ #include "SceneTest.h" -#include "framework_common_gmock_header.h" #include "TestEqualHelper.h" #include "glm/gtx/transform.hpp" #include "glm/gtx/matrix_transform_2d.hpp" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -26,8 +25,8 @@ namespace ramses_internal TYPED_TEST(AScene, InitializesDataInstanceFieldsWithZero) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u)); - const DataInstanceHandle containerHandle = this->m_scene.allocateDataInstance(dataLayout); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Float)}, ResourceContentHash(123u, 0u), {}); + const DataInstanceHandle containerHandle = this->m_scene.allocateDataInstance(dataLayout, {}); EXPECT_EQ(0.0f, this->m_scene.getDataSingleFloat(containerHandle, DataFieldHandle(0u))); EXPECT_EQ(0.0f, this->m_scene.getDataSingleFloat(containerHandle, DataFieldHandle(1u))); @@ -35,8 +34,8 @@ namespace ramses_internal TYPED_TEST(AScene, InitializesDataInstanceBufferFieldsWithInvalidHashAndDataBufferAndZeroDivisor) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::UInt16Buffer), DataFieldInfo(EDataType::FloatBuffer) }, ResourceContentHash(123u, 0u)); - const DataInstanceHandle containerHandle = this->m_scene.allocateDataInstance(dataLayout); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::UInt16Buffer), DataFieldInfo(EDataType::FloatBuffer) }, ResourceContentHash(123u, 0u), {}); + const DataInstanceHandle containerHandle = this->m_scene.allocateDataInstance(dataLayout, {}); { const ResourceField& dataResource = this->m_scene.getDataResource(containerHandle, DataFieldHandle(0u)); @@ -55,8 +54,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsTheValueOfADataInstanceProperty) { - const DataFieldInfoVector dataFields = - { + const DataFieldInfoVector dataFields = { DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Vector2F), DataFieldInfo(EDataType::Vector3F), @@ -73,10 +71,11 @@ namespace ramses_internal DataFieldInfo(EDataType::Matrix22F), DataFieldInfo(EDataType::Matrix33F), DataFieldInfo(EDataType::Matrix44F), - DataFieldInfo(EDataType::DataReference) + DataFieldInfo(EDataType::DataReference), + DataFieldInfo(EDataType::Bool) }; - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout(dataFields, ResourceContentHash(123u, 0u)); - const DataInstanceHandle containerHandle = this->m_scene.allocateDataInstance(dataLayout); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout(dataFields, ResourceContentHash(123u, 0u), {}); + const DataInstanceHandle containerHandle = this->m_scene.allocateDataInstance(dataLayout, {}); const glm::mat2 zeroMatrix22{0.f}; const glm::mat3 zeroMatrix33{0.f}; @@ -87,6 +86,7 @@ namespace ramses_internal EXPECT_EQ(glm::vec2(0.0f) , this->m_scene.getDataSingleVector2f (containerHandle, DataFieldHandle(1u))); EXPECT_EQ(glm::vec3(0.0f) , this->m_scene.getDataSingleVector3f (containerHandle, DataFieldHandle(2u))); EXPECT_EQ(glm::vec4(0.0f) , this->m_scene.getDataSingleVector4f (containerHandle, DataFieldHandle(3u))); + EXPECT_EQ(false , this->m_scene.getDataSingleBoolean (containerHandle, DataFieldHandle(17u))); EXPECT_EQ(0 , this->m_scene.getDataSingleInteger (containerHandle, DataFieldHandle(4u))); EXPECT_EQ(glm::ivec2(0) , this->m_scene.getDataSingleVector2i (containerHandle, DataFieldHandle(5u))); EXPECT_EQ(glm::ivec3(0) , this->m_scene.getDataSingleVector3i (containerHandle, DataFieldHandle(6u))); @@ -118,6 +118,7 @@ namespace ramses_internal this->m_scene.setDataSingleVector2f (containerHandle, DataFieldHandle(1u) , glm::vec2(0.12f, 0.34f)); this->m_scene.setDataSingleVector3f (containerHandle, DataFieldHandle(2u) , glm::vec3(0.13f, 0.35f, 0.46f)); this->m_scene.setDataSingleVector4f (containerHandle, DataFieldHandle(3u) , glm::vec4(0.14f, 0.36f, 0.47f, 0.58f)); + this->m_scene.setDataSingleBoolean (containerHandle, DataFieldHandle(17u), true); this->m_scene.setDataSingleInteger (containerHandle, DataFieldHandle(4u) , 123); this->m_scene.setDataSingleVector2i (containerHandle, DataFieldHandle(5u) , glm::ivec2(12, 34)); this->m_scene.setDataSingleVector3i (containerHandle, DataFieldHandle(6u) , glm::ivec3(13, 35, 46)); @@ -132,14 +133,15 @@ namespace ramses_internal this->m_scene.setDataSingleMatrix44f (containerHandle, DataFieldHandle(15u), glm::translate(glm::vec3{ 1.0f, 2.0f, 3.0f })); this->m_scene.setDataReference (containerHandle, DataFieldHandle(16u), dataRef); - EXPECT_EQ(12.3f , this->m_scene.getDataSingleFloat (containerHandle, DataFieldHandle(0u))); - EXPECT_EQ(glm::vec2(0.12f, 0.34f) , this->m_scene.getDataSingleVector2f (containerHandle, DataFieldHandle(1u))); - EXPECT_EQ(glm::vec3(0.13f, 0.35f, 0.46f) , this->m_scene.getDataSingleVector3f (containerHandle, DataFieldHandle(2u))); - EXPECT_EQ(glm::vec4(0.14f, 0.36f, 0.47f, 0.58f) , this->m_scene.getDataSingleVector4f (containerHandle, DataFieldHandle(3u))); - EXPECT_EQ(123 , this->m_scene.getDataSingleInteger (containerHandle, DataFieldHandle(4u))); - EXPECT_EQ(glm::ivec2(12, 34) , this->m_scene.getDataSingleVector2i (containerHandle, DataFieldHandle(5u))); - EXPECT_EQ(glm::ivec3(13, 35, 46) , this->m_scene.getDataSingleVector3i (containerHandle, DataFieldHandle(6u))); - EXPECT_EQ(glm::ivec4(14, 36, 47, 58) , this->m_scene.getDataSingleVector4i (containerHandle, DataFieldHandle(7u))); + EXPECT_EQ(12.3f , this->m_scene.getDataSingleFloat (containerHandle, DataFieldHandle(0u))); + EXPECT_EQ(glm::vec2(0.12f, 0.34f) , this->m_scene.getDataSingleVector2f (containerHandle, DataFieldHandle(1u))); + EXPECT_EQ(glm::vec3(0.13f, 0.35f, 0.46f) , this->m_scene.getDataSingleVector3f (containerHandle, DataFieldHandle(2u))); + EXPECT_EQ(glm::vec4(0.14f, 0.36f, 0.47f, 0.58f) , this->m_scene.getDataSingleVector4f (containerHandle, DataFieldHandle(3u))); + EXPECT_EQ(true , this->m_scene.getDataSingleBoolean (containerHandle, DataFieldHandle(17u))); + EXPECT_EQ(123 , this->m_scene.getDataSingleInteger (containerHandle, DataFieldHandle(4u))); + EXPECT_EQ(glm::ivec2(12, 34) , this->m_scene.getDataSingleVector2i (containerHandle, DataFieldHandle(5u))); + EXPECT_EQ(glm::ivec3(13, 35, 46) , this->m_scene.getDataSingleVector3i (containerHandle, DataFieldHandle(6u))); + EXPECT_EQ(glm::ivec4(14, 36, 47, 58) , this->m_scene.getDataSingleVector4i (containerHandle, DataFieldHandle(7u))); { const ResourceField& dataResourceOut = this->m_scene.getDataResource(containerHandle, DataFieldHandle(8u)); EXPECT_EQ(hash, dataResourceOut.hash); @@ -148,14 +150,14 @@ namespace ramses_internal EXPECT_EQ(56u, dataResourceOut.offsetWithinElementInBytes); EXPECT_EQ(76u, dataResourceOut.stride); } - EXPECT_EQ(samplerHandle2d , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(9u))); - EXPECT_EQ(samplerHandle2dMultisample , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(10u))); - EXPECT_EQ(samplerHandle3d , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(11u))); - EXPECT_EQ(samplerHandlecube , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(12u))); + EXPECT_EQ(samplerHandle2d , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(9u))); + EXPECT_EQ(samplerHandle2dMultisample , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(10u))); + EXPECT_EQ(samplerHandle3d , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(11u))); + EXPECT_EQ(samplerHandlecube , this->m_scene.getDataTextureSamplerHandle (containerHandle, DataFieldHandle(12u))); expectMatrixFloatEqual(glm::mat2(1.f, 2.f, 3.f, 4.f) , this->m_scene.getDataSingleMatrix22f(containerHandle, DataFieldHandle(13u))); expectMatrixFloatEqual(glm::rotate(glm::mat3(1.0f), glm::radians(45.f)), this->m_scene.getDataSingleMatrix33f(containerHandle, DataFieldHandle(14u))); - expectMatrixFloatEqual(glm::translate(glm::vec3{ 1.0f, 2.0f, 3.0f }) , this->m_scene.getDataSingleMatrix44f(containerHandle, DataFieldHandle(15u))); - EXPECT_EQ(dataRef , this->m_scene.getDataReference (containerHandle, DataFieldHandle(16u))); + expectMatrixFloatEqual(glm::translate(glm::vec3{1.0f, 2.0f, 3.0f}), this->m_scene.getDataSingleMatrix44f(containerHandle, DataFieldHandle(15u))); + EXPECT_EQ(dataRef , this->m_scene.getDataReference (containerHandle, DataFieldHandle(16u))); this->m_scene.setDataResource(containerHandle, DataFieldHandle(8u), ResourceContentHash::Invalid(), DataBufferHandle(1u), 123u, 88u, 12u); { @@ -170,18 +172,18 @@ namespace ramses_internal TYPED_TEST(AScene, CreatingDataInstanceIncreasesDataInstanceCount) { - DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u)); - this->m_scene.allocateDataInstance(dataLayout); + DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u), {}); + this->m_scene.allocateDataInstance(dataLayout, {}); EXPECT_EQ(1u, this->m_scene.getDataInstanceCount()); } TYPED_TEST(AScene, DataInstanceWithFieldWithElementCountGreaterOneReturnsSameValue) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Int32, 4u) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Int32, 4u) }, ResourceContentHash(123u, 0u), {}); const DataFieldHandle field(0u); - const DataInstanceHandle instance = this->m_scene.allocateDataInstance(dataLayout); + const DataInstanceHandle instance = this->m_scene.allocateDataInstance(dataLayout, {}); const std::array inValues = { 1, 2, 3, 4 }; this->m_scene.setDataIntegerArray(instance, field, 4, inValues.data()); @@ -194,9 +196,9 @@ namespace ramses_internal TYPED_TEST(AScene, DataInstanceFieldsWithElementCountGreaterOneDoesNotInfluenceOtherField) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 3u), DataFieldInfo(EDataType::Vector4Buffer) }, ResourceContentHash::Invalid()); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 3u), DataFieldInfo(EDataType::Vector4Buffer) }, ResourceContentHash::Invalid(), {}); - const DataInstanceHandle instance = this->m_scene.allocateDataInstance(dataLayout); + const DataInstanceHandle instance = this->m_scene.allocateDataInstance(dataLayout, {}); const std::array inValues0 = { 0.f, 0.f, 0.f }; const ResourceContentHash inValue1(std::numeric_limits::max(), std::numeric_limits::max()); const uint32_t inDivisor = std::numeric_limits::max(); diff --git a/framework/SceneGraph/Scene/test/SceneTest_DataLayouts.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataLayouts.cpp similarity index 85% rename from framework/SceneGraph/Scene/test/SceneTest_DataLayouts.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_DataLayouts.cpp index 700906f22..636570dba 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_DataLayouts.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataLayouts.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -21,7 +21,7 @@ namespace ramses_internal TYPED_TEST(AScene, CreatingDataLayoutIncreasesDataLayoutCount) { - this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u)); + this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u), {}); EXPECT_EQ(1u, this->m_scene.getDataLayoutCount()); } @@ -29,35 +29,35 @@ namespace ramses_internal { ResourceContentHash effectHash(123u, 0u); //This will internally call setEffectHash - const DataLayoutHandle dataLayoutHandle = this->m_scene.allocateDataLayout({}, effectHash); + const DataLayoutHandle dataLayoutHandle = this->m_scene.allocateDataLayout({}, effectHash, {}); const DataLayout& dataLayout = this->m_scene.getDataLayout(dataLayoutHandle); EXPECT_EQ(effectHash, dataLayout.getEffectHash()); } TYPED_TEST(AScene, CanGetNumberOfDataFieldsInLayout) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u), {}); EXPECT_EQ(0u, this->m_scene.getDataLayout(dataLayout).getFieldCount()); - const DataLayoutHandle dataLayout2 = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Int32) }, ResourceContentHash(456u, 0u)); + const DataLayoutHandle dataLayout2 = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Int32) }, ResourceContentHash(456u, 0u), {}); EXPECT_EQ(2u, this->m_scene.getDataLayout(dataLayout2).getFieldCount()); } TYPED_TEST(AScene, ReturnsCorrectDataFieldType) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash(123u, 0u), {}); EXPECT_EQ(EDataType::Float, this->m_scene.getDataLayout(dataLayout).getField(DataFieldHandle(0u)).dataType); } TYPED_TEST(AScene, ReturnsCorrectDataFieldElements) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float, 3u) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({DataFieldInfo(EDataType::Float, 3u)}, ResourceContentHash(123u, 0u), {}); EXPECT_EQ(3u, this->m_scene.getDataLayout(dataLayout).getField(DataFieldHandle(0u)).elementCount); } TYPED_TEST(AScene, SemanticCanBeSetAndRetrieved) { - const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({ DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Matrix33F, 1u, EFixedSemantics::ModelViewMatrix33) }, ResourceContentHash(123u, 0u)); + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Matrix33F, 1u, EFixedSemantics::ModelViewMatrix33)}, ResourceContentHash(123u, 0u), {}); EXPECT_EQ(EFixedSemantics::Invalid, this->m_scene.getDataLayout(dataLayout).getField(DataFieldHandle(0u)).semantics); EXPECT_EQ(EFixedSemantics::ModelViewMatrix33, this->m_scene.getDataLayout(dataLayout).getField(DataFieldHandle(1u)).semantics); diff --git a/framework/SceneGraph/Scene/test/SceneTest_DataSlot.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataSlot.cpp similarity index 85% rename from framework/SceneGraph/Scene/test/SceneTest_DataSlot.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_DataSlot.cpp index ac8dc4eac..c9387887e 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_DataSlot.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_DataSlot.cpp @@ -11,7 +11,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -26,13 +26,13 @@ namespace ramses_internal const DataInstanceHandle dataRef(13u); const ResourceContentHash textureRes(123u, 0); const TextureSamplerHandle sampler(16u); - const DataSlotHandle handle = this->m_scene.allocateDataSlot({ EDataSlotType_TransformationConsumer, DataSlotId(6), NodeHandle(5), dataRef, textureRes, sampler }, requestedHandle); + const DataSlotHandle handle = this->m_scene.allocateDataSlot({ EDataSlotType::TransformationConsumer, DataSlotId(6), NodeHandle(5), dataRef, textureRes, sampler }, requestedHandle); const DataSlot& dataSlot = this->m_scene.getDataSlot(handle); EXPECT_EQ(handle, requestedHandle); EXPECT_EQ(1u, this->m_scene.getDataSlotCount()); EXPECT_TRUE(this->m_scene.isDataSlotAllocated(handle)); - EXPECT_EQ(EDataSlotType_TransformationConsumer, dataSlot.type); + EXPECT_EQ(EDataSlotType::TransformationConsumer, dataSlot.type); EXPECT_EQ(DataSlotId(6), dataSlot.id); EXPECT_EQ(NodeHandle(5), dataSlot.attachedNode); EXPECT_EQ(dataRef, dataSlot.attachedDataReference); @@ -42,7 +42,7 @@ namespace ramses_internal TYPED_TEST(AScene, ReleasesDataSlot) { - const DataSlotHandle handle = this->m_scene.allocateDataSlot({ EDataSlotType_TransformationConsumer, DataSlotId(6), NodeHandle(5), DataInstanceHandle(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); + const DataSlotHandle handle = this->m_scene.allocateDataSlot({EDataSlotType::TransformationConsumer, DataSlotId(6), NodeHandle(5), DataInstanceHandle(), ResourceContentHash::Invalid(), TextureSamplerHandle()}, {}); this->m_scene.releaseDataSlot(handle); EXPECT_FALSE(this->m_scene.isDataSlotAllocated(handle)); diff --git a/framework/SceneGraph/Scene/test/SceneTest_Generic.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Generic.cpp similarity index 99% rename from framework/SceneGraph/Scene/test/SceneTest_Generic.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_Generic.cpp index 5488b0fc3..c49e7e2c4 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_Generic.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Generic.cpp @@ -11,7 +11,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); diff --git a/tests/unittests/framework/SceneGraph/Scene/SceneTest_IteratableMemoryPools.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_IteratableMemoryPools.cpp new file mode 100644 index 000000000..5fa672501 --- /dev/null +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_IteratableMemoryPools.cpp @@ -0,0 +1,215 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "gtest/gtest.h" +#include "ActionTestScene.h" +#include "internal/SceneGraph/Scene/ResourceChangeCollectingScene.h" +#include "internal/SceneGraph/Scene/DataLayoutCachedScene.h" +#include + +using namespace testing; + +namespace ramses::internal +{ + using IteratableSceneTypes = ::testing::Types< + Scene, + TransformationCachedScene, + ActionCollectingScene, + ResourceChangeCollectingScene + >; + + template + class AnIteratableScene : public testing::Test + { + protected: + SCENE m_scene; + + template + void runTest( + std::function allocateHandleF, + std::function releaseHandleF, + const MemPoolT& memPool) + { + using HandleT = typename MemPoolT::handle_type; + + const HandleT handle1 = allocateHandleF(); + const HandleT handle2 = allocateHandleF(); + const HandleT handle3 = allocateHandleF(); + const HandleT handle4 = allocateHandleF(); + const HandleT handle5 = allocateHandleF(); + const HandleT handle6 = allocateHandleF(); + + releaseHandleF(handle3); + const HandleT handle7 = allocateHandleF(); + releaseHandleF(handle1); + const HandleT handle8 = allocateHandleF(); + releaseHandleF(handle4); + releaseHandleF(handle5); + + ASSERT_EQ(HandleT{ 1u }, handle2); + ASSERT_EQ(HandleT{ 5u }, handle6); + ASSERT_EQ(HandleT{ 2u }, handle7); + ASSERT_EQ(HandleT{ 0u }, handle8); + + std::vector handles; + for (const auto& it : memPool) + { + handles.push_back(it.first); + } + + std::vector exepctedHandles{ handle8, handle2, handle7, handle6 }; + EXPECT_EQ(handles, exepctedHandles); + } + }; + + TYPED_TEST_SUITE(AnIteratableScene, IteratableSceneTypes); + + TYPED_TEST(AnIteratableScene, CanIterateOverRenderables) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateRenderable(NodeHandle{}, RenderableHandle{}); }; + auto releaseF = [scene = &this->m_scene](RenderableHandle renderable) { scene->releaseRenderable(renderable); }; + this->runTest(allocateF, releaseF, this->m_scene.getRenderables()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverRenderStates) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateRenderState(RenderStateHandle{}); }; + auto releaseF = [scene = &this->m_scene](RenderStateHandle renderState) { scene->releaseRenderState(renderState); }; + this->runTest(allocateF, releaseF, this->m_scene.getRenderStates()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverCameras) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateCamera(ECameraProjectionType::Orthographic, NodeHandle{}, DataInstanceHandle{}, CameraHandle{}); }; + auto releaseF = [scene = &this->m_scene](CameraHandle camera) { scene->releaseCamera(camera); }; + this->runTest(allocateF, releaseF, this->m_scene.getCameras()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverNodes) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateNode(0u, NodeHandle{}); }; + auto releaseF = [scene = &this->m_scene](NodeHandle node) { scene->releaseNode(node); }; + this->runTest(allocateF, releaseF, this->m_scene.getNodes()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverTransforms) + { + const auto nodeHandle = this->m_scene.allocateNode(0, {}); + + auto allocateF = [scene = &this->m_scene, nodeHandle] { return scene->allocateTransform(nodeHandle, TransformHandle{}); }; + auto releaseF = [scene = &this->m_scene](TransformHandle transform) { scene->releaseTransform(transform); }; + this->runTest(allocateF, releaseF, this->m_scene.getTransforms()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverDataLayouts) + { + auto allocateF = [&]() { + static uint64_t resourceHash = 1u; + return this->m_scene.allocateDataLayout(DataFieldInfoVector{}, ResourceContentHash{ resourceHash++, 0u }, {}); + }; + + auto releaseF = [scene = &this->m_scene](DataLayoutHandle dataLayout) { scene->releaseDataLayout(dataLayout); }; + this->runTest(allocateF, releaseF, this->m_scene.getDataLayouts()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverDataInstances) + { + const DataLayoutHandle dataLayout = this->m_scene.allocateDataLayout({DataFieldInfo(EDataType::Float), DataFieldInfo(EDataType::Float)}, ResourceContentHash(123u, 0u), {}); + + auto allocateF = [scene = &this->m_scene, dataLayout] { return scene->allocateDataInstance(dataLayout, DataInstanceHandle{}); }; + auto releaseF = [scene = &this->m_scene](DataInstanceHandle dataInstance) { scene->releaseDataInstance(dataInstance); }; + this->runTest(allocateF, releaseF, this->m_scene.getDataInstances()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverTextureSamplers) + { + const TextureSampler sampler({}, ResourceContentHash{ 123u, 0u }); + + auto allocateF = [scene = &this->m_scene, sampler] { return scene->allocateTextureSampler(sampler, TextureSamplerHandle{}); }; + auto releaseF = [scene = &this->m_scene](TextureSamplerHandle textureSampler) { scene->releaseTextureSampler(textureSampler); }; + this->runTest(allocateF, releaseF, this->m_scene.getTextureSamplers()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverRenderGroups) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateRenderGroup(0u, 0u, RenderGroupHandle{}); }; + auto releaseF = [scene = &this->m_scene](RenderGroupHandle renderGroup) { scene->releaseRenderGroup(renderGroup); }; + this->runTest(allocateF, releaseF, this->m_scene.getRenderGroups()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverRenderPasses) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateRenderPass(0u, RenderPassHandle{}); }; + auto releaseF = [scene = &this->m_scene](RenderPassHandle renderPass) { scene->releaseRenderPass(renderPass); }; + this->runTest(allocateF, releaseF, this->m_scene.getRenderPasses()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverBlitPasses) + { + const auto srcRenderBufferHandle = this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + const auto dstRenderBufferHandle = this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + + auto allocateF = [scene = &this->m_scene, srcRenderBufferHandle, dstRenderBufferHandle] { return scene->allocateBlitPass(srcRenderBufferHandle, dstRenderBufferHandle, BlitPassHandle{}); }; + auto releaseF = [scene = &this->m_scene](BlitPassHandle blitPass) { scene->releaseBlitPass(blitPass); }; + this->runTest(allocateF, releaseF, this->m_scene.getBlitPasses()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverPickableObjects) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocatePickableObject(DataBufferHandle{}, NodeHandle{}, PickableObjectId{ 1u }, PickableObjectHandle{}); }; + auto releaseF = [scene = &this->m_scene](PickableObjectHandle pickable) { scene->releasePickableObject(pickable); }; + this->runTest(allocateF, releaseF, this->m_scene.getPickableObjects()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverRenderTargets) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateRenderTarget(RenderTargetHandle{}); }; + auto releaseF = [scene = &this->m_scene](RenderTargetHandle renderTarget) { scene->releaseRenderTarget(renderTarget); }; + this->runTest(allocateF, releaseF, this->m_scene.getRenderTargets()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverRenderBuffers) + { + const RenderBuffer renderBuffer{ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }; + auto allocateF = [scene = &this->m_scene, renderBuffer] { return scene->allocateRenderBuffer(renderBuffer, RenderBufferHandle{}); }; + auto releaseF = [scene = &this->m_scene](RenderBufferHandle handle) { scene->releaseRenderBuffer(handle); }; + this->runTest(allocateF, releaseF, this->m_scene.getRenderBuffers()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverDataBuffers) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::Int32, 4u, DataBufferHandle{}); }; + auto releaseF = [scene = &this->m_scene](DataBufferHandle dataBuffer) { scene->releaseDataBuffer(dataBuffer); }; + this->runTest(allocateF, releaseF, this->m_scene.getDataBuffers()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverTextureBuffers) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateTextureBuffer(EPixelStorageFormat::R16F, MipMapDimensions{ {1, 1} }, TextureBufferHandle{}); }; + auto releaseF = [scene = &this->m_scene](TextureBufferHandle textureBuffer) { scene->releaseTextureBuffer(textureBuffer); }; + this->runTest(allocateF, releaseF, this->m_scene.getTextureBuffers()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverDataSlots) + { + const DataSlot dataSlot{ EDataSlotType::TransformationConsumer, DataSlotId{6u}, NodeHandle{5u}, DataInstanceHandle{1u}, ResourceContentHash{1u, 0u}, TextureSamplerHandle{1u} }; + + auto allocateF = [scene = &this->m_scene, dataSlot] { return scene->allocateDataSlot(dataSlot, DataSlotHandle{}); }; + auto releaseF = [scene = &this->m_scene](DataSlotHandle handle) { scene->releaseDataSlot(handle); }; + this->runTest(allocateF, releaseF, this->m_scene.getDataSlots()); + } + + TYPED_TEST(AnIteratableScene, CanIterateOverSceneReferences) + { + auto allocateF = [scene = &this->m_scene] { return scene->allocateSceneReference(SceneId{123u}, SceneReferenceHandle{}); }; + auto releaseF = [scene = &this->m_scene](SceneReferenceHandle sceneReference) { scene->releaseSceneReference(sceneReference); }; + this->runTest(allocateF, releaseF, this->m_scene.getSceneReferences()); + } +} diff --git a/framework/SceneGraph/Scene/test/SceneTest_Nodes.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Nodes.cpp similarity index 84% rename from framework/SceneGraph/Scene/test/SceneTest_Nodes.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_Nodes.cpp index 57b333dc9..f737cfdd1 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_Nodes.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Nodes.cpp @@ -11,14 +11,14 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); TYPED_TEST(AScene, ContainsCreatedNode) { - this->m_scene.allocateNode(); - NodeHandle node2 = this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); + NodeHandle node2 = this->m_scene.allocateNode(0, {}); EXPECT_TRUE(this->m_scene.isNodeAllocated(node2)); } @@ -30,7 +30,7 @@ namespace ramses_internal TYPED_TEST(AScene, CreatingNodeIncreasesNodeCount) { - this->m_scene.allocateNode(); + this->m_scene.allocateNode(0, {}); EXPECT_EQ(1u, this->m_scene.getNodeCount()); } diff --git a/framework/SceneGraph/Scene/test/SceneTest_PickableObjects.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_PickableObjects.cpp similarity index 93% rename from framework/SceneGraph/Scene/test/SceneTest_PickableObjects.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_PickableObjects.cpp index 8d4eca4e4..1691256b1 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_PickableObjects.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_PickableObjects.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -18,7 +18,7 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getPickableObjectCount()); - PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}); + PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}, {}); EXPECT_EQ(1u, this->m_scene.getPickableObjectCount()); EXPECT_TRUE(this->m_scene.isPickableObjectAllocated(pickableHandle)); @@ -26,7 +26,7 @@ namespace ramses_internal TYPED_TEST(AScene, PickableObjectReleased) { - PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}); + PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}, {}); this->m_scene.releasePickableObject(pickableHandle); EXPECT_FALSE(this->m_scene.isPickableObjectAllocated(pickableHandle)); @@ -39,11 +39,11 @@ namespace ramses_internal TYPED_TEST(AScene, PickableObjectCanGetPropertiesGivenAtAllocationTime) { - const DataBufferHandle geometryBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u); - const NodeHandle nodeHandle = this->m_scene.allocateNode(); + const DataBufferHandle geometryBuffer = this->m_scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, {}); + const NodeHandle nodeHandle = this->m_scene.allocateNode(0, {}); const PickableObjectId id{ 3u }; - PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(geometryBuffer, nodeHandle, id); + PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(geometryBuffer, nodeHandle, id, {}); EXPECT_EQ(geometryBuffer, this->m_scene.getPickableObject(pickableHandle).geometryHandle); EXPECT_EQ(nodeHandle, this->m_scene.getPickableObject(pickableHandle).nodeHandle); EXPECT_EQ(id, this->m_scene.getPickableObject(pickableHandle).id); @@ -51,7 +51,7 @@ namespace ramses_internal TYPED_TEST(AScene, PickableObjectCanSetId) { - const PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{ 3u }); + const PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{ 3u }, {}); EXPECT_EQ(1u, this->m_scene.getPickableObjectCount()); const PickableObjectId id{ 666u }; @@ -61,7 +61,7 @@ namespace ramses_internal TYPED_TEST(AScene, PickableObjectCanSetCamera) { - const PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}); + const PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}, {}); EXPECT_EQ(1u, this->m_scene.getPickableObjectCount()); const CameraHandle cameraHandle(11u); @@ -71,7 +71,7 @@ namespace ramses_internal TYPED_TEST(AScene, CanDisablePickableObject) { - PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}); + PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}, {}); EXPECT_TRUE(this->m_scene.getPickableObject(pickableHandle).isEnabled); this->m_scene.setPickableObjectEnabled(pickableHandle, false); @@ -80,7 +80,7 @@ namespace ramses_internal TYPED_TEST(AScene, PickableObjectEnabledByDefault) { - PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}); + PickableObjectHandle pickableHandle = this->m_scene.allocatePickableObject(DataBufferHandle(1u), NodeHandle(2u), PickableObjectId{3u}, {}); EXPECT_TRUE(this->m_scene.getPickableObject(pickableHandle).isEnabled); } } diff --git a/framework/SceneGraph/Scene/test/SceneTest_RenderBuffer.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderBuffer.cpp similarity index 67% rename from framework/SceneGraph/Scene/test/SceneTest_RenderBuffer.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderBuffer.cpp index c5a068355..c0a73812b 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_RenderBuffer.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderBuffer.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -23,7 +23,7 @@ namespace ramses_internal TYPED_TEST(AScene, canCreateRenderBuffer) { const RenderBufferHandle buffer(13u); - EXPECT_EQ(buffer, this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }, buffer)); + EXPECT_EQ(buffer, this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, buffer)); EXPECT_NE(0u, this->m_scene.getRenderBufferCount()); EXPECT_TRUE(this->m_scene.isRenderBufferAllocated(buffer)); } @@ -31,21 +31,20 @@ namespace ramses_internal TYPED_TEST(AScene, canGetRenderBufferProperties) { const RenderBufferHandle buffer(13u); - EXPECT_EQ(buffer, this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }, buffer)); + EXPECT_EQ(buffer, this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, buffer)); const RenderBuffer& renderBuffer = this->m_scene.getRenderBuffer(buffer); EXPECT_EQ(1u, renderBuffer.width); EXPECT_EQ(2u, renderBuffer.height); - EXPECT_EQ(ERenderBufferType_DepthBuffer, renderBuffer.type); - EXPECT_EQ(ETextureFormat::Depth16, renderBuffer.format); - EXPECT_EQ(ERenderBufferAccessMode_ReadWrite, renderBuffer.accessMode); + EXPECT_EQ(EPixelStorageFormat::Depth16, renderBuffer.format); + EXPECT_EQ(ERenderBufferAccessMode::ReadWrite, renderBuffer.accessMode); EXPECT_EQ(0u, renderBuffer.sampleCount); } TYPED_TEST(AScene, canReleaseRenderBuffer) { const RenderBufferHandle buffer(13u); - EXPECT_EQ(buffer, this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }, buffer)); + EXPECT_EQ(buffer, this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, buffer)); this->m_scene.releaseRenderBuffer(buffer); @@ -55,38 +54,36 @@ namespace ramses_internal TYPED_TEST(AScene, canCreateMultipleRenderBuffers) { const RenderBufferHandle buffer1(13u); - EXPECT_EQ(buffer1, this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }, buffer1)); + EXPECT_EQ(buffer1, this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, buffer1)); ASSERT_TRUE(this->m_scene.isRenderBufferAllocated(buffer1)); const RenderBufferHandle buffer2(17u); - EXPECT_EQ(buffer2, this->m_scene.allocateRenderBuffer({ 3u, 4u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA5551, ERenderBufferAccessMode_WriteOnly, 1u }, buffer2)); + EXPECT_EQ(buffer2, this->m_scene.allocateRenderBuffer({ 3u, 4u, EPixelStorageFormat::RGBA5551, ERenderBufferAccessMode::WriteOnly, 1u }, buffer2)); ASSERT_TRUE(this->m_scene.isRenderBufferAllocated(buffer2)); const RenderBuffer& renderBuffer1 = this->m_scene.getRenderBuffer(buffer1); EXPECT_EQ(1u, renderBuffer1.width); EXPECT_EQ(2u, renderBuffer1.height); - EXPECT_EQ(ERenderBufferType_DepthBuffer, renderBuffer1.type); - EXPECT_EQ(ETextureFormat::Depth16, renderBuffer1.format); - EXPECT_EQ(ERenderBufferAccessMode_ReadWrite, renderBuffer1.accessMode); + EXPECT_EQ(EPixelStorageFormat::Depth16, renderBuffer1.format); + EXPECT_EQ(ERenderBufferAccessMode::ReadWrite, renderBuffer1.accessMode); EXPECT_EQ(0u, renderBuffer1.sampleCount); const RenderBuffer& renderBuffer2 = this->m_scene.getRenderBuffer(buffer2); EXPECT_EQ(3u, renderBuffer2.width); EXPECT_EQ(4u, renderBuffer2.height); - EXPECT_EQ(ERenderBufferType_ColorBuffer, renderBuffer2.type); - EXPECT_EQ(ETextureFormat::RGBA5551, renderBuffer2.format); - EXPECT_EQ(ERenderBufferAccessMode_WriteOnly, renderBuffer2.accessMode); + EXPECT_EQ(EPixelStorageFormat::RGBA5551, renderBuffer2.format); + EXPECT_EQ(ERenderBufferAccessMode::WriteOnly, renderBuffer2.accessMode); EXPECT_EQ(1u, renderBuffer2.sampleCount); } TYPED_TEST(AScene, deletingRenderBufferDoesNotAffectOtherRenderBuffers) { const RenderBufferHandle buffer1(13u); - EXPECT_EQ(buffer1, this->m_scene.allocateRenderBuffer({ 1u, 2u, ERenderBufferType_DepthBuffer, ETextureFormat::Depth16, ERenderBufferAccessMode_ReadWrite, 0u }, buffer1)); + EXPECT_EQ(buffer1, this->m_scene.allocateRenderBuffer({ 1u, 2u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u }, buffer1)); EXPECT_TRUE(this->m_scene.isRenderBufferAllocated(buffer1)); const RenderBufferHandle buffer2(17u); - EXPECT_EQ(buffer2, this->m_scene.allocateRenderBuffer({ 3u, 4u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA5551, ERenderBufferAccessMode_WriteOnly, 1u }, buffer2)); + EXPECT_EQ(buffer2, this->m_scene.allocateRenderBuffer({ 3u, 4u, EPixelStorageFormat::RGBA5551, ERenderBufferAccessMode::WriteOnly, 1u }, buffer2)); EXPECT_TRUE(this->m_scene.isRenderBufferAllocated(buffer2)); this->m_scene.releaseRenderBuffer(buffer1); @@ -96,9 +93,8 @@ namespace ramses_internal const RenderBuffer& renderBuffer = this->m_scene.getRenderBuffer(buffer2); EXPECT_EQ(3u, renderBuffer.width); EXPECT_EQ(4u, renderBuffer.height); - EXPECT_EQ(ERenderBufferType_ColorBuffer, renderBuffer.type); - EXPECT_EQ(ETextureFormat::RGBA5551, renderBuffer.format); - EXPECT_EQ(ERenderBufferAccessMode_WriteOnly, renderBuffer.accessMode); + EXPECT_EQ(EPixelStorageFormat::RGBA5551, renderBuffer.format); + EXPECT_EQ(ERenderBufferAccessMode::WriteOnly, renderBuffer.accessMode); EXPECT_EQ(1u, renderBuffer.sampleCount); } } diff --git a/framework/SceneGraph/Scene/test/SceneTest_RenderGroups.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderGroups.cpp similarity index 89% rename from framework/SceneGraph/Scene/test/SceneTest_RenderGroups.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderGroups.cpp index ba03ada77..5ae079dab 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_RenderGroups.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderGroups.cpp @@ -7,11 +7,11 @@ // ------------------------------------------------------------------------- #include "SceneTest.h" -#include "SceneAPI/RenderGroupUtils.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -19,7 +19,7 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getRenderGroupCount()); - const RenderGroupHandle group = this->m_scene.allocateRenderGroup(); + const RenderGroupHandle group = this->m_scene.allocateRenderGroup(0, 0, {}); EXPECT_EQ(1u, this->m_scene.getRenderGroupCount()); EXPECT_TRUE(this->m_scene.isRenderGroupAllocated(group)); @@ -27,7 +27,7 @@ namespace ramses_internal TYPED_TEST(AScene, canReleaseRenderGroup) { - const RenderGroupHandle group = this->m_scene.allocateRenderGroup(); + const RenderGroupHandle group = this->m_scene.allocateRenderGroup(0, 0, {}); this->m_scene.releaseRenderGroup(group); EXPECT_FALSE(this->m_scene.isRenderGroupAllocated(group)); @@ -35,8 +35,8 @@ namespace ramses_internal TYPED_TEST(AScene, canAddRenderableToRenderGroup) { - const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(); - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); const RenderGroup& rg = this->m_scene.getRenderGroup(renderGroup); EXPECT_FALSE(RenderGroupUtils::ContainsRenderable(renderable, rg)); EXPECT_TRUE(rg.renderables.empty()); @@ -51,9 +51,9 @@ namespace ramses_internal TYPED_TEST(AScene, canRemoveRenderableFromRenderGroup) { - const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(); - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); - const RenderableHandle renderable2 = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); + const RenderableHandle renderable2 = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); this->m_scene.addRenderableToRenderGroup(renderGroup, renderable, 1); this->m_scene.addRenderableToRenderGroup(renderGroup, renderable2, 2); @@ -74,8 +74,8 @@ namespace ramses_internal TYPED_TEST(AScene, canAddRenderGroupToRenderGroup) { - const RenderGroupHandle renderGroupParent = this->m_scene.allocateRenderGroup(); - const RenderGroupHandle renderGroupChild = this->m_scene.allocateRenderGroup(); + const RenderGroupHandle renderGroupParent = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderGroupHandle renderGroupChild = this->m_scene.allocateRenderGroup(0, 0, {}); const RenderGroup& rg = this->m_scene.getRenderGroup(renderGroupParent); EXPECT_FALSE(RenderGroupUtils::ContainsRenderGroup(renderGroupChild, rg)); @@ -90,9 +90,9 @@ namespace ramses_internal TYPED_TEST(AScene, canRemoveRenderGroupFromRenderGroup) { - const RenderGroupHandle renderGroupParent = this->m_scene.allocateRenderGroup(); - const RenderGroupHandle renderGroupChild1 = this->m_scene.allocateRenderGroup(); - const RenderGroupHandle renderGroupChild2 = this->m_scene.allocateRenderGroup(); + const RenderGroupHandle renderGroupParent = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderGroupHandle renderGroupChild1 = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderGroupHandle renderGroupChild2 = this->m_scene.allocateRenderGroup(0, 0, {}); this->m_scene.addRenderGroupToRenderGroup(renderGroupParent, renderGroupChild1, 3u); this->m_scene.addRenderGroupToRenderGroup(renderGroupParent, renderGroupChild2, 4u); @@ -112,8 +112,8 @@ namespace ramses_internal TYPED_TEST(AScene, canReaddRenderableWithDifferentOrder) { - const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(); - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); this->m_scene.addRenderableToRenderGroup(renderGroup, renderable, 3); this->m_scene.removeRenderableFromRenderGroup(renderGroup, renderable); @@ -128,8 +128,8 @@ namespace ramses_internal TYPED_TEST(AScene, canReaddRenderGroupToRenderGroupWithDifferentOrder) { - const RenderGroupHandle renderGroupParent = this->m_scene.allocateRenderGroup(); - const RenderGroupHandle renderGroupChild = this->m_scene.allocateRenderGroup(); + const RenderGroupHandle renderGroupParent = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderGroupHandle renderGroupChild = this->m_scene.allocateRenderGroup(0, 0, {}); this->m_scene.addRenderGroupToRenderGroup(renderGroupParent, renderGroupChild, 3); this->m_scene.removeRenderGroupFromRenderGroup(renderGroupParent, renderGroupChild); diff --git a/framework/SceneGraph/Scene/test/SceneTest_RenderPasses.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderPasses.cpp similarity index 87% rename from framework/SceneGraph/Scene/test/SceneTest_RenderPasses.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderPasses.cpp index d5d86cc50..46bef63cf 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_RenderPasses.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderPasses.cpp @@ -8,11 +8,11 @@ #include "SceneTest.h" -#include "SceneAPI/RenderGroupUtils.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -20,7 +20,7 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getRenderPassCount()); - RenderPassHandle pass = this->m_scene.allocateRenderPass(); + RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); EXPECT_EQ(1u, this->m_scene.getRenderPassCount()); EXPECT_TRUE(this->m_scene.isRenderPassAllocated(pass)); @@ -28,12 +28,12 @@ namespace ramses_internal TYPED_TEST(AScene, RenderPassHasDefaultValues) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); const RenderPass& rp = this->m_scene.getRenderPass(pass); EXPECT_TRUE(rp.isEnabled); EXPECT_EQ(glm::vec4(0.f, 0.f, 0.f, 1.f), rp.clearColor); - EXPECT_EQ(static_cast(EClearFlags_All), rp.clearFlags); + EXPECT_EQ(EClearFlag::All, rp.clearFlags); EXPECT_FALSE(rp.camera.isValid()); EXPECT_FALSE(rp.renderTarget.isValid()); EXPECT_EQ(0, rp.renderOrder); @@ -42,7 +42,7 @@ namespace ramses_internal TYPED_TEST(AScene, RenderPassReleased) { - RenderPassHandle pass = this->m_scene.allocateRenderPass(); + RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); this->m_scene.releaseRenderPass(pass); EXPECT_FALSE(this->m_scene.isRenderPassAllocated(pass)); @@ -50,9 +50,9 @@ namespace ramses_internal TYPED_TEST(AScene, setsCameraToRenderPass) { - const RenderPassHandle renderPass = this->m_scene.allocateRenderPass(); - const auto dataLayout = this->m_scene.allocateDataLayout({DataFieldInfo{ramses_internal::EDataType::Vector2I}, DataFieldInfo{ramses_internal::EDataType::Vector2I}}, ResourceContentHash(123u, 0u)); - const CameraHandle camera = this->m_scene.allocateCamera(ECameraProjectionType::Perspective, this->m_scene.allocateNode(), this->m_scene.allocateDataInstance(dataLayout)); + const RenderPassHandle renderPass = this->m_scene.allocateRenderPass(0, {}); + const auto dataLayout = this->m_scene.allocateDataLayout({DataFieldInfo{ramses::internal::EDataType::Vector2I}, DataFieldInfo{ramses::internal::EDataType::Vector2I}}, ResourceContentHash(123u, 0u), {}); + const CameraHandle camera = this->m_scene.allocateCamera(ECameraProjectionType::Perspective, this->m_scene.allocateNode(0, {}), this->m_scene.allocateDataInstance(dataLayout, {}), {}); EXPECT_FALSE(this->m_scene.getRenderPass(renderPass).camera.isValid()); this->m_scene.setRenderPassCamera(renderPass, camera); @@ -66,17 +66,17 @@ namespace ramses_internal TYPED_TEST(AScene, RenderPassRenderTarget) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); EXPECT_FALSE(this->m_scene.getRenderPass(pass).renderTarget.isValid()); - const RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget(); + const RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget({}); this->m_scene.setRenderPassRenderTarget(pass, targetHandle); EXPECT_TRUE(this->m_scene.getRenderPass(pass).renderTarget.isValid()); } TYPED_TEST(AScene, DisableRenderPass) { - RenderPassHandle pass = this->m_scene.allocateRenderPass(); + RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); EXPECT_TRUE(this->m_scene.getRenderPass(pass).isEnabled); this->m_scene.setRenderPassEnabled(pass, false); @@ -85,7 +85,7 @@ namespace ramses_internal TYPED_TEST(AScene, createsRenderPassWithOrderZeroAndSetsOrderTwice) { - const RenderPassHandle renderPass = this->m_scene.allocateRenderPass(); + const RenderPassHandle renderPass = this->m_scene.allocateRenderPass(0, {}); const RenderPass& rp = this->m_scene.getRenderPass(renderPass); EXPECT_EQ(0, rp.renderOrder); @@ -98,30 +98,30 @@ namespace ramses_internal TYPED_TEST(AScene, AssignsDefaultClearValuesToRenderPass) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); const RenderPass& rp = this->m_scene.getRenderPass(pass); EXPECT_EQ(glm::vec4(0.f, 0.f, 0.f, 1.f), rp.clearColor); - EXPECT_EQ(static_cast(EClearFlags::EClearFlags_All), rp.clearFlags); + EXPECT_EQ(EClearFlag::All, rp.clearFlags); } TYPED_TEST(AScene, ReturnsClearParameterOfRenderPassWhichWereSetBefore) { const glm::vec4 clearColor(0.5f, 0.0f, 1.f, 0.25f); - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); - this->m_scene.setRenderPassClearFlag(pass, ramses_internal::EClearFlags::EClearFlags_None); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); + this->m_scene.setRenderPassClearFlag(pass, EClearFlag::None); this->m_scene.setRenderPassClearColor(pass, clearColor); const RenderPass& rp = this->m_scene.getRenderPass(pass); EXPECT_EQ(clearColor, rp.clearColor); - EXPECT_EQ(static_cast(EClearFlags::EClearFlags_None), rp.clearFlags); + EXPECT_EQ(EClearFlag::None, rp.clearFlags); } TYPED_TEST(AScene, canAddRenderGroupToRenderPass) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); - const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); + const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(0, 0, {}); const RenderPass& rp = this->m_scene.getRenderPass(pass); EXPECT_FALSE(RenderGroupUtils::ContainsRenderGroup(renderGroup, rp)); @@ -133,9 +133,9 @@ namespace ramses_internal TYPED_TEST(AScene, canRemoveRenderGroupFromRenderPass) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); - const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(); - const RenderGroupHandle renderGroup2 = this->m_scene.allocateRenderGroup(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); + const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(0, 0, {}); + const RenderGroupHandle renderGroup2 = this->m_scene.allocateRenderGroup(0, 0, {}); this->m_scene.addRenderGroupToRenderPass(pass, renderGroup, 1); this->m_scene.addRenderGroupToRenderPass(pass, renderGroup2, 2); @@ -150,8 +150,8 @@ namespace ramses_internal TYPED_TEST(AScene, canReaddRenderGroupWithDifferentOrder) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); - const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); + const RenderGroupHandle renderGroup = this->m_scene.allocateRenderGroup(0, 0, {}); this->m_scene.addRenderGroupToRenderPass(pass, renderGroup, 3); this->m_scene.removeRenderGroupFromRenderPass(pass, renderGroup); @@ -165,7 +165,7 @@ namespace ramses_internal TYPED_TEST(AScene, canSetRenderOnce) { - const RenderPassHandle pass = this->m_scene.allocateRenderPass(); + const RenderPassHandle pass = this->m_scene.allocateRenderPass(0, {}); this->m_scene.setRenderPassRenderOnce(pass, true); EXPECT_TRUE(this->m_scene.getRenderPass(pass).isRenderOnce); this->m_scene.setRenderPassRenderOnce(pass, false); diff --git a/framework/SceneGraph/Scene/test/SceneTest_RenderTarget.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderTarget.cpp similarity index 95% rename from framework/SceneGraph/Scene/test/SceneTest_RenderTarget.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderTarget.cpp index 909edb319..bbc83b09c 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_RenderTarget.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_RenderTarget.cpp @@ -11,7 +11,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -19,12 +19,12 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getRenderTargetCount()); - RenderTargetHandle targetHandle1 = this->m_scene.allocateRenderTarget(); + RenderTargetHandle targetHandle1 = this->m_scene.allocateRenderTarget({}); EXPECT_NE(RenderTargetHandle::Invalid(), targetHandle1); EXPECT_TRUE(this->m_scene.isRenderTargetAllocated(targetHandle1)); - RenderTargetHandle targetHandle2 = this->m_scene.allocateRenderTarget(); + RenderTargetHandle targetHandle2 = this->m_scene.allocateRenderTarget({}); EXPECT_NE(RenderTargetHandle::Invalid(), targetHandle2); EXPECT_TRUE(this->m_scene.isRenderTargetAllocated(targetHandle2)); @@ -32,7 +32,7 @@ namespace ramses_internal TYPED_TEST(AScene, DeleteRenderTargetsAndCheckScene) { - RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget(); + RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget({}); EXPECT_NE(RenderTargetHandle::Invalid(), targetHandle); EXPECT_TRUE(this->m_scene.isRenderTargetAllocated(targetHandle)); @@ -44,7 +44,7 @@ namespace ramses_internal TYPED_TEST(AScene, ContainsRenderTargetInSceneKnowsExactlyAboutAllCreatedRenderTargets) { - RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget(); + RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget({}); EXPECT_NE(RenderTargetHandle::Invalid(), targetHandle); EXPECT_TRUE(this->m_scene.isRenderTargetAllocated(targetHandle)); @@ -54,7 +54,7 @@ namespace ramses_internal TYPED_TEST(AScene, canAddRenderBufferToRenderTarget) { - const RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget(); + const RenderTargetHandle targetHandle = this->m_scene.allocateRenderTarget({}); EXPECT_EQ(0u, this->m_scene.getRenderTargetRenderBufferCount(targetHandle)); const RenderBufferHandle bufferHandle(44u); diff --git a/framework/SceneGraph/Scene/test/SceneTest_Renderables.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Renderables.cpp similarity index 84% rename from framework/SceneGraph/Scene/test/SceneTest_Renderables.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_Renderables.cpp index 719e6640a..0c0fc0031 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_Renderables.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Renderables.cpp @@ -11,19 +11,19 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); TYPED_TEST(AScene, ContainsCreatedRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_TRUE(this->m_scene.isRenderableAllocated(renderable)); } TYPED_TEST(AScene, ReleasesRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); this->m_scene.releaseRenderable(renderable); EXPECT_FALSE(this->m_scene.isRenderableAllocated(renderable)); @@ -31,23 +31,23 @@ namespace ramses_internal TYPED_TEST(AScene, ReturnsNodeOfRenderable) { - const NodeHandle node = this->m_scene.allocateNode(); - const RenderableHandle renderable = this->m_scene.allocateRenderable(node); + const NodeHandle node = this->m_scene.allocateNode(0, {}); + const RenderableHandle renderable = this->m_scene.allocateRenderable(node, {}); EXPECT_EQ(node, this->m_scene.getRenderable(renderable).node); } TYPED_TEST(AScene, ReturnsInvalidRenderStateForNewRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(RenderStateHandle::Invalid(), this->m_scene.getRenderable(renderable).renderState); } TYPED_TEST(AScene, SetsRenderStateForRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderableRenderState(renderable, state); EXPECT_EQ(state, this->m_scene.getRenderable(renderable).renderState); @@ -55,10 +55,10 @@ namespace ramses_internal TYPED_TEST(AScene, SetsDataInstanceToRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); - const DataLayoutHandle layoutHandle = this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u)); - const DataInstanceHandle dataInstance1 = this->m_scene.allocateDataInstance(layoutHandle); - const DataInstanceHandle dataInstance2 = this->m_scene.allocateDataInstance(layoutHandle); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); + const DataLayoutHandle layoutHandle = this->m_scene.allocateDataLayout({}, ResourceContentHash(123u, 0u), {}); + const DataInstanceHandle dataInstance1 = this->m_scene.allocateDataInstance(layoutHandle, {}); + const DataInstanceHandle dataInstance2 = this->m_scene.allocateDataInstance(layoutHandle, {}); this->m_scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Uniforms, dataInstance1); this->m_scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Geometry, dataInstance2); EXPECT_EQ(this->m_scene.getDataLayout(layoutHandle).getEffectHash(), ResourceContentHash(123u, 0u)); @@ -69,7 +69,7 @@ namespace ramses_internal TYPED_TEST(AScene, DrawsNothingIfIndicesRangeNotSpecified) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(0u, this->m_scene.getRenderable(renderable).startIndex); EXPECT_EQ(0u, this->m_scene.getRenderable(renderable).indexCount); @@ -77,7 +77,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStartIndexOfRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); this->m_scene.setRenderableStartIndex(renderable, 12u); EXPECT_EQ(12u, this->m_scene.getRenderable(renderable).startIndex); @@ -85,7 +85,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsIndexCountOfRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); this->m_scene.setRenderableIndexCount(renderable, 12); EXPECT_EQ(12u, this->m_scene.getRenderable(renderable).indexCount); @@ -93,7 +93,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsRenderableVisible) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); this->m_scene.setRenderableVisibility(renderable, EVisibilityMode::Off); EXPECT_EQ(EVisibilityMode::Off, this->m_scene.getRenderable(renderable).visibilityMode); this->m_scene.setRenderableVisibility(renderable, EVisibilityMode::Invisible); @@ -109,14 +109,14 @@ namespace ramses_internal TYPED_TEST(AScene, CreatingRenderableIncreasesRenderableCount) { - this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(1u, this->m_scene.getRenderableCount()); } TYPED_TEST(AScene, SetsStartVertexOfRenderable) { - const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode()); + const RenderableHandle renderable = this->m_scene.allocateRenderable(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(0u, this->m_scene.getRenderable(renderable).startVertex); this->m_scene.setRenderableStartVertex(renderable, 132u); diff --git a/framework/SceneGraph/Scene/test/SceneTest_SceneReference.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_SceneReference.cpp similarity index 99% rename from framework/SceneGraph/Scene/test/SceneTest_SceneReference.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_SceneReference.cpp index 1ff7772f4..c1bc61bb2 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_SceneReference.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_SceneReference.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); diff --git a/framework/SceneGraph/Scene/test/SceneTest_States.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_States.cpp similarity index 95% rename from framework/SceneGraph/Scene/test/SceneTest_States.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_States.cpp index 187d3a231..f8403805f 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_States.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_States.cpp @@ -10,14 +10,14 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); TYPED_TEST(AScene, ContainsCreatedState) { - this->m_scene.allocateRenderState(); - RenderStateHandle state2 = this->m_scene.allocateRenderState(); + this->m_scene.allocateRenderState({}); + RenderStateHandle state2 = this->m_scene.allocateRenderState({}); EXPECT_TRUE(this->m_scene.isRenderStateAllocated(state2)); } @@ -29,14 +29,14 @@ namespace ramses_internal TYPED_TEST(AScene, CreatingStateIncreasesStateCount) { - this->m_scene.allocateRenderState(); + this->m_scene.allocateRenderState({}); EXPECT_EQ(1u, this->m_scene.getRenderStateCount()); } TYPED_TEST(AScene, AssignsDefaultValuesToNewlyCreatedState) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); const RenderState& rs = this->m_scene.getRenderState(state); const RenderState::ScissorRegion scissorRegion{ 0, 0, 0u, 0u }; @@ -62,7 +62,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateBlendFactors) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateBlendFactors(state, EBlendFactor::One, EBlendFactor::SrcAlpha, EBlendFactor::OneMinusSrcAlpha, EBlendFactor::DstAlpha); const RenderState& rs = this->m_scene.getRenderState(state); @@ -74,7 +74,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateBlendOperations) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateBlendOperations(state, EBlendOperation::Add, EBlendOperation::Subtract); const RenderState& rs = this->m_scene.getRenderState(state); @@ -84,7 +84,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateBlendColor) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateBlendColor(state, { 0.1f, 0.2f, 0.3f, 0.4f }); const RenderState& rs = this->m_scene.getRenderState(state); @@ -96,7 +96,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateCullMode) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateCullMode(state, ECullMode::FrontFacing); const RenderState& rs = this->m_scene.getRenderState(state); @@ -105,7 +105,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateDrawMode) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateDrawMode(state, EDrawMode::Triangles); const RenderState& rs = this->m_scene.getRenderState(state); @@ -114,7 +114,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateDepthWrite) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateDepthWrite(state, EDepthWrite::Disabled); const RenderState& rs = this->m_scene.getRenderState(state); @@ -123,7 +123,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateDepthFunc) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateDepthFunc(state, EDepthFunc::GreaterEqual); const RenderState& rs = this->m_scene.getRenderState(state); @@ -132,7 +132,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateScissorTest) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateScissorTest(state, EScissorTest::Enabled, { 1, 2, 3, 4 }); const RenderState& rs = this->m_scene.getRenderState(state); @@ -143,7 +143,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateStencilFunc) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateStencilFunc(state, EStencilFunc::NotEqual, 12u, 0xAA); const RenderState& rs = this->m_scene.getRenderState(state); @@ -154,7 +154,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateStencilOps) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); this->m_scene.setRenderStateStencilOps(state, EStencilOp::Increment, EStencilOp::IncrementWrap, EStencilOp::Decrement); const RenderState& rs = this->m_scene.getRenderState(state); @@ -165,7 +165,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsStateColorWriteMask) { - const RenderStateHandle state = this->m_scene.allocateRenderState(); + const RenderStateHandle state = this->m_scene.allocateRenderState({}); const ColorWriteMask colorMask = EColorWriteFlag_Green | EColorWriteFlag_Alpha; this->m_scene.setRenderStateColorWriteMask(state, colorMask); diff --git a/framework/SceneGraph/Scene/test/SceneTest_TextureBuffers.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_TextureBuffers.cpp similarity index 67% rename from framework/SceneGraph/Scene/test/SceneTest_TextureBuffers.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_TextureBuffers.cpp index 1a4ba85ab..23111202c 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_TextureBuffers.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_TextureBuffers.cpp @@ -11,7 +11,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); @@ -19,7 +19,7 @@ namespace ramses_internal { EXPECT_EQ(0u, this->m_scene.getTextureBufferCount()); - const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(ETextureFormat::R16F, { {1, 1} }); + const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { {1, 1} }, {}); EXPECT_EQ(1u, this->m_scene.getTextureBufferCount()); EXPECT_TRUE(this->m_scene.isTextureBufferAllocated(textureBuffer)); @@ -27,7 +27,7 @@ namespace ramses_internal TYPED_TEST(AScene, ReleasesTextureBuffer) { - const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(ETextureFormat::R16F, { { 1, 1 } }); + const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { { 1, 1 } }, {}); this->m_scene.releaseTextureBuffer(textureBuffer); EXPECT_FALSE(this->m_scene.isTextureBufferAllocated(textureBuffer)); @@ -40,9 +40,9 @@ namespace ramses_internal TYPED_TEST(AScene, StoresTextureBufferProperties) { - const TextureBufferHandle handle = this->m_scene.allocateTextureBuffer(ETextureFormat::R16F, { {2, 2}, { 1, 1 } }); + const TextureBufferHandle handle = this->m_scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { {2, 2}, { 1, 1 } }, {}); const TextureBuffer& texBuffer = this->m_scene.getTextureBuffer(handle); - EXPECT_EQ(ETextureFormat::R16F, texBuffer.textureFormat); + EXPECT_EQ(EPixelStorageFormat::R16F, texBuffer.textureFormat); ASSERT_EQ(2u, texBuffer.mipMaps.size()); EXPECT_EQ(2u, texBuffer.mipMaps[0].width); EXPECT_EQ(2u, texBuffer.mipMaps[0].height); @@ -50,33 +50,39 @@ namespace ramses_internal EXPECT_EQ(1u, texBuffer.mipMaps[1].height); } + template + std::array make_byte_array(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + TYPED_TEST(AScene, UpdatesTextureBufferDataPartially) { - const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(ETextureFormat::R8, { { 4, 4} }); - const std::array data = {1u, 2u, 3u, 4u}; + const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(EPixelStorageFormat::R8, { { 4, 4} }, {}); + const std::array data = make_byte_array(1u, 2u, 3u, 4u); this->m_scene.updateTextureBuffer(textureBuffer, 0u, 0u, 0u, 2u, 2u, data.data()); const TextureBuffer& texBuffer = this->m_scene.getTextureBuffer(textureBuffer); - EXPECT_EQ(1u, texBuffer.mipMaps[0].data[0 * 4 + 0]); - EXPECT_EQ(2u, texBuffer.mipMaps[0].data[0 * 4 + 1]); - EXPECT_EQ(3u, texBuffer.mipMaps[0].data[1 * 4 + 0]); - EXPECT_EQ(4u, texBuffer.mipMaps[0].data[1 * 4 + 1]); + EXPECT_EQ(std::byte{1u}, texBuffer.mipMaps[0].data[0 * 4 + 0]); + EXPECT_EQ(std::byte{2u}, texBuffer.mipMaps[0].data[0 * 4 + 1]); + EXPECT_EQ(std::byte{3u}, texBuffer.mipMaps[0].data[1 * 4 + 0]); + EXPECT_EQ(std::byte{4u}, texBuffer.mipMaps[0].data[1 * 4 + 1]); - const std::array data2 = { 10u, 20u, 30u, 40u }; + const std::array data2 = make_byte_array(10u, 20u, 30u, 40u); this->m_scene.updateTextureBuffer(textureBuffer, 0u, 1u, 1u, 2u, 2u, data2.data()); - EXPECT_EQ(1u, texBuffer.mipMaps[0].data[0 * 4 + 0]); //stays same - EXPECT_EQ(2u, texBuffer.mipMaps[0].data[0 * 4 + 1]); //stays same - EXPECT_EQ(3u, texBuffer.mipMaps[0].data[1 * 4 + 0]); //stays same - EXPECT_EQ(10u, texBuffer.mipMaps[0].data[1 * 4 + 1]); - EXPECT_EQ(20u, texBuffer.mipMaps[0].data[1 * 4 + 2]); - EXPECT_EQ(30u, texBuffer.mipMaps[0].data[2 * 4 + 1]); - EXPECT_EQ(40u, texBuffer.mipMaps[0].data[2 * 4 + 2]); + EXPECT_EQ(std::byte{1u}, texBuffer.mipMaps[0].data[0 * 4 + 0]); //stays same + EXPECT_EQ(std::byte{2u}, texBuffer.mipMaps[0].data[0 * 4 + 1]); //stays same + EXPECT_EQ(std::byte{3u}, texBuffer.mipMaps[0].data[1 * 4 + 0]); //stays same + EXPECT_EQ(std::byte{10u}, texBuffer.mipMaps[0].data[1 * 4 + 1]); + EXPECT_EQ(std::byte{20u}, texBuffer.mipMaps[0].data[1 * 4 + 2]); + EXPECT_EQ(std::byte{30u}, texBuffer.mipMaps[0].data[2 * 4 + 1]); + EXPECT_EQ(std::byte{40u}, texBuffer.mipMaps[0].data[2 * 4 + 2]); } TYPED_TEST(AScene, TextureBufferDoesNotHaveUsedRegionsUponCreation) { EXPECT_EQ(0u, this->m_scene.getTextureBufferCount()); - const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(ETextureFormat::R16F, { {2, 2}, {1, 1} }); + const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(EPixelStorageFormat::R16F, { {2, 2}, {1, 1} }, {}); const TextureBuffer& texBuffer = this->m_scene.getTextureBuffer(textureBuffer); ASSERT_EQ(2u, texBuffer.mipMaps.size()); @@ -86,8 +92,8 @@ namespace ramses_internal TYPED_TEST(AScene, UpdatesUsedRegionsWhenTextureBufferDataGetsUpdated) { - const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(ETextureFormat::R8, { { 4, 4}, {2, 2} }); - const std::array data = { 0u }; + const TextureBufferHandle textureBuffer = this->m_scene.allocateTextureBuffer(EPixelStorageFormat::R8, { { 4, 4}, {2, 2} }, {}); + const std::array data = { std::byte{0u} }; const TextureBuffer& texBuffer = this->m_scene.getTextureBuffer(textureBuffer); diff --git a/framework/SceneGraph/Scene/test/SceneTest_TextureSamplers.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_TextureSamplers.cpp similarity index 86% rename from framework/SceneGraph/Scene/test/SceneTest_TextureSamplers.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_TextureSamplers.cpp index 11d8a1c8d..46f6eb9d4 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_TextureSamplers.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_TextureSamplers.cpp @@ -11,17 +11,17 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); - static const TextureSamplerStates SamplerStates = { EWrapMethod::Clamp, EWrapMethod::Repeat, EWrapMethod::RepeatMirrored, ESamplingMethod::Linear, ESamplingMethod::Nearest, 4u }; + static const TextureSamplerStates SamplerStates = { ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Nearest, 4u }; TYPED_TEST(AScene, TextureSamplerCreated) { EXPECT_EQ(0u, this->m_scene.getTextureSamplerCount()); - const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ {}, ResourceContentHash(0,1) }); + const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ {}, ResourceContentHash(0,1) }, {}); EXPECT_EQ(1u, this->m_scene.getTextureSamplerCount()); EXPECT_TRUE(this->m_scene.isTextureSamplerAllocated(sampler)); @@ -29,7 +29,7 @@ namespace ramses_internal TYPED_TEST(AScene, TextureSamplerReleased) { - const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ {}, ResourceContentHash(0,1) }); + const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ {}, ResourceContentHash(0,1) }, {}); this->m_scene.releaseTextureSampler(sampler); EXPECT_EQ(1u, this->m_scene.getTextureSamplerCount()); @@ -43,7 +43,7 @@ namespace ramses_internal TYPED_TEST(AScene, AllocatedTextureSamplerHasGivenStates) { - const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, ResourceContentHash(0,1) }); + const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, ResourceContentHash(0,1) }, {}); EXPECT_EQ(SamplerStates.m_addressModeU, this->m_scene.getTextureSampler(sampler).states.m_addressModeU); EXPECT_EQ(SamplerStates.m_addressModeV, this->m_scene.getTextureSampler(sampler).states.m_addressModeV); @@ -56,7 +56,7 @@ namespace ramses_internal TYPED_TEST(AScene, AllocatedTextureSamplerHasGivenReferencesToResources_TextureResrouce) { const ResourceContentHash resource(1u, 2u); - const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, resource }); + const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, resource }, {}); EXPECT_TRUE(this->m_scene.getTextureSampler(sampler).textureResource.isValid()); EXPECT_EQ(InvalidMemoryHandle, this->m_scene.getTextureSampler(sampler).contentHandle); @@ -66,7 +66,7 @@ namespace ramses_internal TYPED_TEST(AScene, AllocatedTextureSamplerHasGivenReferencesToResources_RenderBuffer) { const RenderBufferHandle buffer(3u); - const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, buffer }); + const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, buffer }, {}); EXPECT_FALSE(this->m_scene.getTextureSampler(sampler).textureResource.isValid()); EXPECT_NE(InvalidMemoryHandle, this->m_scene.getTextureSampler(sampler).contentHandle); @@ -76,7 +76,7 @@ namespace ramses_internal TYPED_TEST(AScene, AllocatedTextureSamplerHasGivenReferencesToResources_TextureBuffer) { const TextureBufferHandle textureBuffer(1432u); - const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({ SamplerStates, textureBuffer }); + const TextureSamplerHandle sampler = this->m_scene.allocateTextureSampler({SamplerStates, textureBuffer}, {}); EXPECT_FALSE(this->m_scene.getTextureSampler(sampler).textureResource.isValid()); EXPECT_NE(InvalidMemoryHandle, this->m_scene.getTextureSampler(sampler).contentHandle); diff --git a/framework/SceneGraph/Scene/test/SceneTest_Transforms.cpp b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Transforms.cpp similarity index 87% rename from framework/SceneGraph/Scene/test/SceneTest_Transforms.cpp rename to tests/unittests/framework/SceneGraph/Scene/SceneTest_Transforms.cpp index ad27d9551..59f1f9952 100644 --- a/framework/SceneGraph/Scene/test/SceneTest_Transforms.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/SceneTest_Transforms.cpp @@ -11,19 +11,19 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { TYPED_TEST_SUITE(AScene, SceneTypes); TYPED_TEST(AScene, ContainsCreatedTransform) { - const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode()); + const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); EXPECT_TRUE(this->m_scene.isTransformAllocated(transform)); } TYPED_TEST(AScene, ReleasesTransform) { - const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode()); + const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); this->m_scene.releaseTransform(transform); EXPECT_FALSE(this->m_scene.isTransformAllocated(transform)); @@ -36,7 +36,7 @@ namespace ramses_internal TYPED_TEST(AScene, CreatingTransformsIncreasesTransformsCount) { - this->m_scene.allocateTransform(this->m_scene.allocateNode()); + this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); EXPECT_EQ(1u, this->m_scene.getTransformCount()); } @@ -48,7 +48,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsTranslationForTransform) { - const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode()); + const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); const glm::vec3 translation(1, 2, 3); @@ -58,7 +58,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsRotationForTransform) { - const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode()); + const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); const glm::vec4 rotation(4, 5, 6, 1.f); @@ -69,7 +69,7 @@ namespace ramses_internal TYPED_TEST(AScene, SetsScalingForTransform) { - const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode()); + const TransformHandle transform = this->m_scene.allocateTransform(this->m_scene.allocateNode(0, {}), {}); const glm::vec3 scaling(7, 8, 9); @@ -79,8 +79,8 @@ namespace ramses_internal TYPED_TEST(AScene, CanGetAssignedNodeHandle) { - const NodeHandle node = this->m_scene.allocateNode(); - const TransformHandle transHandle = this->m_scene.allocateTransform(node); + const NodeHandle node = this->m_scene.allocateNode(0, {}); + const TransformHandle transHandle = this->m_scene.allocateTransform(node, {}); EXPECT_EQ(node, this->m_scene.getTransformNode(transHandle)); } diff --git a/framework/SceneGraph/Scene/test/TestingScene.h b/tests/unittests/framework/SceneGraph/Scene/TestingScene.h similarity index 91% rename from framework/SceneGraph/Scene/test/TestingScene.h rename to tests/unittests/framework/SceneGraph/Scene/TestingScene.h index c3bf400d2..1236fa02b 100644 --- a/framework/SceneGraph/Scene/test/TestingScene.h +++ b/tests/unittests/framework/SceneGraph/Scene/TestingScene.h @@ -7,21 +7,26 @@ // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTINGSCENE_H -#define RAMSES_TESTINGSCENE_H - -#include "framework_common_gmock_header.h" -#include "Scene/Scene.h" -#include "PlatformAbstraction/PlatformTypes.h" -#include "SceneAPI/TextureEnums.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/RenderGroupUtils.h" -#include +#pragma once + +#include "internal/SceneGraph/Scene/Scene.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/RenderGroupUtils.h" #include "glm/gtx/transform.hpp" #include "glm/gtx/matrix_transform_2d.hpp" -namespace ramses_internal +#include +#include + +namespace ramses::internal { + template + std::array make_byte_array(Ts&&... args) noexcept + { + return {std::byte(std::forward(args))...}; + } + template class TestingScene { @@ -87,7 +92,9 @@ namespace ramses_internal DataFieldInfo(EDataType::Matrix44F), DataFieldInfo(EDataType::TextureSampler2D), DataFieldInfo(EDataType::DataReference), - DataFieldInfo(EDataType::TextureSamplerExternal) + DataFieldInfo(EDataType::TextureSamplerExternal), + DataFieldInfo(EDataType::Bool), + DataFieldInfo(EDataType::Bool) }; scene.allocateDataLayout(uniformLayoutDataFields, effectHash, uniformLayout); @@ -99,8 +106,8 @@ namespace ramses_internal }; scene.allocateDataLayout(geometryLayoutDataFields, effectHash, vertexLayout); - scene.allocateDataInstance(scene.allocateDataLayout({ ramses_internal::DataFieldInfo{ramses_internal::EDataType::Vector2I}, ramses_internal::DataFieldInfo{ramses_internal::EDataType::Vector2I} }, - ResourceContentHash::Invalid()), cameraDataInstance); + scene.allocateDataInstance(scene.allocateDataLayout({ DataFieldInfo{ramses::internal::EDataType::Vector2I}, DataFieldInfo{ramses::internal::EDataType::Vector2I} }, + ResourceContentHash::Invalid(), {}), cameraDataInstance); scene.allocateTextureSampler({ samplerStates, textureHash }, samplerWithTextureResource); scene.allocateTextureSampler({ samplerStates, renderBuffer }, samplerWithRenderBuffer); @@ -116,6 +123,8 @@ namespace ramses_internal scene.setDataTextureSamplerHandle(uniformData, DataFieldHandle(4), samplerWithTextureResource); scene.setDataReference(uniformData, DataFieldHandle(5), dataRef); scene.setDataTextureSamplerHandle(uniformData, DataFieldHandle(6), samplerWithExternalTexture); + scene.setDataSingleBoolean(uniformData, DataFieldHandle(7), true); + scene.setDataSingleBoolean(uniformData, DataFieldHandle(8), false); scene.allocateDataInstance(vertexLayout, geometryData); scene.setDataResource(geometryData, DataFieldHandle(0u), indexArrayHash, DataBufferHandle::Invalid(), indexArrayDivisor, 0u, 0u); @@ -129,7 +138,7 @@ namespace ramses_internal scene.allocateCamera(ECameraProjectionType::Orthographic, cameraNode, cameraDataInstance, orthographicCamera); scene.allocateRenderTarget(renderTarget); - scene.allocateRenderBuffer({ 23u, 42u, ERenderBufferType_ColorBuffer, ETextureFormat::RGBA8, ERenderBufferAccessMode_ReadWrite, 3u }, renderBuffer); + scene.allocateRenderBuffer({ 23u, 42u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 3u }, renderBuffer); scene.addRenderTargetRenderBuffer(renderTarget, renderBuffer); scene.allocateRenderGroup(0u, 0u, renderGroup); @@ -145,7 +154,7 @@ namespace ramses_internal scene.addRenderGroupToRenderGroup(nestedRenderGroupParent, nestedRenderGroupChild, 22); scene.allocateRenderPass(0u, renderPass); - scene.setRenderPassClearFlag(renderPass, ramses_internal::EClearFlags::EClearFlags_None); + scene.setRenderPassClearFlag(renderPass, EClearFlag::None); scene.setRenderPassClearColor(renderPass, glm::vec4(0.5f, 0.0f, 1.f, 0.25f)); scene.setRenderPassCamera(renderPass, camera); scene.setRenderPassRenderTarget(renderPass, renderTarget); @@ -162,23 +171,23 @@ namespace ramses_internal scene.setBlitPassRenderOrder(blitPass, 14u); scene.setBlitPassRegions(blitPass, blitPassSourceRectangle, blitPassDestinationRectangle); - scene.allocateDataSlot({ EDataSlotType_TransformationConsumer, dataSlotId, parent, dataRef, textureHash, samplerWithTextureResource }, transformDataSlot); + scene.allocateDataSlot({ EDataSlotType::TransformationConsumer, dataSlotId, parent, dataRef, textureHash, samplerWithTextureResource }, transformDataSlot); scene.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 1024, indexDataBuffer); - scene.updateDataBuffer(indexDataBuffer, 1u, 2u, std::array{{0x77, 0xAB }}.data()); + scene.updateDataBuffer(indexDataBuffer, 1u, 2u, make_byte_array(0x77, 0xAB).data()); scene.allocateDataBuffer(EDataBufferType::VertexBuffer, EDataType::Float, 32, vertexDataBuffer); - scene.updateDataBuffer(vertexDataBuffer, 0u, 3u, std::array{{0x0A, 0x1B, 0x2C}}.data()); + scene.updateDataBuffer(vertexDataBuffer, 0u, 3u, make_byte_array(0x0A, 0x1B, 0x2C).data()); scene.allocatePickableObject(vertexDataBuffer, child, PickableObjectId{ 0u }, pickableHandle); scene.setPickableObjectId(pickableHandle, pickableId); scene.setPickableObjectCamera(pickableHandle, camera); scene.setPickableObjectEnabled(pickableHandle, false); - scene.allocateTextureBuffer(ETextureFormat::R8, { {8u, 8u}, {4u, 4u}, {2u, 2u} }, texture2DBuffer); - scene.updateTextureBuffer(texture2DBuffer, 0u, 3u, 4u, 1u, 3u, std::array{ {34u, 35u, 36u}}.data()); //partial update level 0 - scene.updateTextureBuffer(texture2DBuffer, 0u, 3u, 4u, 1u, 2u, std::array{ {134u, 135u}}.data()); //override partial update level 0 - scene.updateTextureBuffer(texture2DBuffer, 2u, 0u, 0u, 2u, 2u, std::array{ {00u, 10u, 01u, 11u}}.data()); //full update level 2 + scene.allocateTextureBuffer(EPixelStorageFormat::R8, { {8u, 8u}, {4u, 4u}, {2u, 2u} }, texture2DBuffer); + scene.updateTextureBuffer(texture2DBuffer, 0u, 3u, 4u, 1u, 3u, make_byte_array(34u, 35u, 36u).data()); //partial update level 0 + scene.updateTextureBuffer(texture2DBuffer, 0u, 3u, 4u, 1u, 2u, make_byte_array(134u, 135u).data()); //override partial update level 0 + scene.updateTextureBuffer(texture2DBuffer, 2u, 0u, 0u, 2u, 2u, make_byte_array(00u, 10u, 01u, 11u).data()); //full update level 2 scene.allocateSceneReference(sceneRefSceneId, sceneRef); scene.requestSceneReferenceState(sceneRef, RendererSceneState::Ready); @@ -317,7 +326,7 @@ namespace ramses_internal // check counts EXPECT_TRUE(otherScene.isDataLayoutAllocated(uniformLayout)); EXPECT_TRUE(otherScene.isDataLayoutAllocated(vertexLayout)); - EXPECT_EQ(7u, otherScene.getDataLayout(uniformLayout).getFieldCount()); + EXPECT_EQ(9u, otherScene.getDataLayout(uniformLayout).getFieldCount()); EXPECT_EQ(4u, otherScene.getDataLayout(vertexLayout).getFieldCount()); // check types EXPECT_EQ(EDataType::Float, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(0)).dataType); @@ -327,6 +336,8 @@ namespace ramses_internal EXPECT_EQ(EDataType::TextureSampler2D, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(4)).dataType); EXPECT_EQ(EDataType::DataReference, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(5)).dataType); EXPECT_EQ(EDataType::TextureSamplerExternal, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(6)).dataType); + EXPECT_EQ(EDataType::Bool, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(7)).dataType); + EXPECT_EQ(EDataType::Bool, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(8)).dataType); EXPECT_EQ(effectHash, otherScene.getDataLayout(uniformLayout).getEffectHash()); EXPECT_EQ(EDataType::Indices, otherScene.getDataLayout(vertexLayout).getField(DataFieldHandle(0)).dataType); EXPECT_EQ(EDataType::Vector4Buffer, otherScene.getDataLayout(vertexLayout).getField(DataFieldHandle(1)).dataType); @@ -341,6 +352,8 @@ namespace ramses_internal EXPECT_EQ(1u, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(4)).elementCount); EXPECT_EQ(1u, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(5)).elementCount); EXPECT_EQ(1u, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(6)).elementCount); + EXPECT_EQ(1u, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(7)).elementCount); + EXPECT_EQ(1u, otherScene.getDataLayout(uniformLayout).getField(DataFieldHandle(8)).elementCount); EXPECT_EQ(1u, otherScene.getDataLayout(vertexLayout).getField(DataFieldHandle(0)).elementCount); EXPECT_EQ(1u, otherScene.getDataLayout(vertexLayout).getField(DataFieldHandle(1)).elementCount); EXPECT_EQ(1u, otherScene.getDataLayout(vertexLayout).getField(DataFieldHandle(2)).elementCount); @@ -387,6 +400,8 @@ namespace ramses_internal } EXPECT_EQ(0.5f, otherScene.getDataSingleFloat(uniformData, DataFieldHandle(0u))); + EXPECT_EQ(true, otherScene.getDataSingleBoolean(uniformData, DataFieldHandle(7u))); + EXPECT_EQ(false, otherScene.getDataSingleBoolean(uniformData, DataFieldHandle(8u))); // array check const glm::vec4* dataVec4fArray = otherScene.getDataVector4fArray(uniformData, DataFieldHandle(1u)); @@ -470,7 +485,7 @@ namespace ramses_internal EXPECT_EQ(renderTarget, rp.renderTarget); EXPECT_EQ(1, rp.renderOrder); EXPECT_EQ(glm::vec4(0.5f, 0.0f, 1.f, 0.25f), rp.clearColor); - EXPECT_EQ(static_cast(EClearFlags::EClearFlags_None), rp.clearFlags); + EXPECT_EQ(EClearFlag::None, rp.clearFlags); EXPECT_FALSE(rp.isEnabled); EXPECT_TRUE(rp.isRenderOnce); @@ -508,9 +523,8 @@ namespace ramses_internal const RenderBuffer& renderBufferData = otherScene.getRenderBuffer(renderBuffer); EXPECT_EQ(23u, renderBufferData.width); EXPECT_EQ(42u, renderBufferData.height); - EXPECT_EQ(ERenderBufferType_ColorBuffer, renderBufferData.type); - EXPECT_EQ(ETextureFormat::RGBA8, renderBufferData.format); - EXPECT_EQ(ERenderBufferAccessMode_ReadWrite, renderBufferData.accessMode); + EXPECT_EQ(EPixelStorageFormat::RGBA8, renderBufferData.format); + EXPECT_EQ(ERenderBufferAccessMode::ReadWrite, renderBufferData.accessMode); EXPECT_EQ(3u, renderBufferData.sampleCount); EXPECT_EQ(1u, otherScene.getRenderTargetRenderBufferCount(renderTarget)); @@ -529,16 +543,16 @@ namespace ramses_internal EXPECT_EQ(3u, indexData.usedSize); EXPECT_EQ(EDataBufferType::IndexBuffer, indexData.bufferType); EXPECT_EQ(EDataType::UInt32, indexData.dataType); - EXPECT_EQ(0x77, indexData.data[1]); - EXPECT_EQ(0xAB, indexData.data[2]); + EXPECT_EQ(std::byte{0x77}, indexData.data[1]); + EXPECT_EQ(std::byte{0xAB}, indexData.data[2]); EXPECT_EQ(32u, vertexData.data.size()); EXPECT_EQ(3u, vertexData.usedSize); EXPECT_EQ(EDataBufferType::VertexBuffer, vertexData.bufferType); EXPECT_EQ(EDataType::Float, vertexData.dataType); - EXPECT_EQ(0x0A, vertexData.data[0]); - EXPECT_EQ(0x1B, vertexData.data[1]); - EXPECT_EQ(0x2C, vertexData.data[2]); + EXPECT_EQ(std::byte{0x0A}, vertexData.data[0]); + EXPECT_EQ(std::byte{0x1B}, vertexData.data[1]); + EXPECT_EQ(std::byte{0x2C}, vertexData.data[2]); } template @@ -546,7 +560,7 @@ namespace ramses_internal { ASSERT_TRUE(otherScene.isTextureBufferAllocated(texture2DBuffer)); const TextureBuffer& texBuffer = otherScene.getTextureBuffer(texture2DBuffer); - EXPECT_EQ(ETextureFormat::R8, texBuffer.textureFormat); + EXPECT_EQ(EPixelStorageFormat::R8, texBuffer.textureFormat); ASSERT_EQ(3u, texBuffer.mipMaps.size()); EXPECT_EQ(8u, texBuffer.mipMaps[0].width); @@ -559,14 +573,14 @@ namespace ramses_internal const auto& mipLevel0Data = texBuffer.mipMaps[0u].data; const auto& mipLevel2Data = texBuffer.mipMaps[2u].data; - EXPECT_EQ(134u, mipLevel0Data[4 * 8 + 3]); - EXPECT_EQ(135u, mipLevel0Data[5 * 8 + 3]); - EXPECT_EQ(36u, mipLevel0Data[6 * 8 + 3]); + EXPECT_EQ(std::byte{134u}, mipLevel0Data[4 * 8 + 3]); + EXPECT_EQ(std::byte{135u}, mipLevel0Data[5 * 8 + 3]); + EXPECT_EQ(std::byte{36u}, mipLevel0Data[6 * 8 + 3]); - EXPECT_EQ(00u, mipLevel2Data[0 * 2 + 0]); - EXPECT_EQ(10u, mipLevel2Data[0 * 2 + 1]); - EXPECT_EQ(01u, mipLevel2Data[1 * 2 + 0]); - EXPECT_EQ(11u, mipLevel2Data[1 * 2 + 1]); + EXPECT_EQ(std::byte{00u}, mipLevel2Data[0 * 2 + 0]); + EXPECT_EQ(std::byte{10u}, mipLevel2Data[0 * 2 + 1]); + EXPECT_EQ(std::byte{01u}, mipLevel2Data[1 * 2 + 0]); + EXPECT_EQ(std::byte{11u}, mipLevel2Data[1 * 2 + 1]); EXPECT_EQ(Quad(3, 4, 1, 3), texBuffer.mipMaps[0].usedRegion); EXPECT_EQ(Quad(0, 0, 0, 0), texBuffer.mipMaps[1].usedRegion); @@ -578,7 +592,7 @@ namespace ramses_internal { EXPECT_TRUE(otherScene.isDataSlotAllocated(transformDataSlot)); const DataSlot& dataSlot = otherScene.getDataSlot(transformDataSlot); - EXPECT_EQ(EDataSlotType_TransformationConsumer, dataSlot.type); + EXPECT_EQ(EDataSlotType::TransformationConsumer, dataSlot.type); EXPECT_EQ(dataSlotId, dataSlot.id); EXPECT_EQ(parent, dataSlot.attachedNode); EXPECT_EQ(dataRef, dataSlot.attachedDataReference); @@ -669,7 +683,7 @@ namespace ramses_internal const RenderGroupHandle nestedRenderGroupParent {61u}; const RenderGroupHandle nestedRenderGroupChild {62u}; const uint32_t renderableInstanceCount = 64u; - const TextureSamplerStates samplerStates { EWrapMethod::Repeat, EWrapMethod::Clamp, EWrapMethod::RepeatMirrored, ESamplingMethod::Nearest, ESamplingMethod::Linear, 32u }; + const TextureSamplerStates samplerStates { ETextureAddressMode::Repeat, ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, 32u }; const PixelRectangle blitPassSourceRectangle = PixelRectangle({1u, 2u, 300u, 400u}); const PixelRectangle blitPassDestinationRectangle = PixelRectangle({5u, 6u, 700u, 800u}); const DataBufferHandle indexDataBuffer { 65u }; @@ -681,5 +695,3 @@ namespace ramses_internal const SceneId sceneRefSceneId { 123 }; }; } - -#endif diff --git a/framework/SceneGraph/Scene/test/TransformationCachedSceneTest.cpp b/tests/unittests/framework/SceneGraph/Scene/TransformationCachedSceneTest.cpp similarity index 93% rename from framework/SceneGraph/Scene/test/TransformationCachedSceneTest.cpp rename to tests/unittests/framework/SceneGraph/Scene/TransformationCachedSceneTest.cpp index 94a4f917a..00f512221 100644 --- a/framework/SceneGraph/Scene/test/TransformationCachedSceneTest.cpp +++ b/tests/unittests/framework/SceneGraph/Scene/TransformationCachedSceneTest.cpp @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" -#include "Scene/TransformationCachedScene.h" -#include "Scene/Scene.h" +#include "internal/SceneGraph/Scene/TransformationCachedScene.h" +#include "internal/SceneGraph/Scene/Scene.h" #include "TestEqualHelper.h" -#include "Math3d/Rotation.h" +#include "internal/Core/Math3d/Rotation.h" #include "glm/gtx/transform.hpp" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ATransformationCachedScene : public testing::Test { @@ -23,9 +22,9 @@ namespace ramses_internal ATransformationCachedScene() : scene(SceneInfo()) { - this->nodeWithoutTransform = this->scene.allocateNode(); - this->nodeWithTransform = this->scene.allocateNode(); - this->transform = this->scene.allocateTransform(this->nodeWithTransform); + this->nodeWithoutTransform = this->scene.allocateNode(0, {}); + this->nodeWithTransform = this->scene.allocateNode(0, {}); + this->transform = this->scene.allocateTransform(this->nodeWithTransform, {}); } protected: @@ -160,8 +159,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, GivesCorrectValueWhenRotationConventionIsSetDifferentFromParent) { // add parent with transform - const NodeHandle childNode = this->scene.allocateNode(); - const TransformHandle childTransform = this->scene.allocateTransform(childNode); + const NodeHandle childNode = this->scene.allocateNode(0, {}); + const TransformHandle childTransform = this->scene.allocateTransform(childNode, {}); this->scene.addChildToNode(this->nodeWithTransform, childNode); this->expectIdentityMatrices(nodeWithoutTransform); @@ -184,8 +183,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, QuatParentEulerChild) { // add parent with transform - const NodeHandle childNode = this->scene.allocateNode(); - const TransformHandle childTransform = this->scene.allocateTransform(childNode); + const NodeHandle childNode = this->scene.allocateNode(0, {}); + const TransformHandle childTransform = this->scene.allocateTransform(childNode, {}); this->scene.addChildToNode(this->nodeWithTransform, childNode); this->expectIdentityMatrices(nodeWithoutTransform); @@ -208,8 +207,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, EulerParentQuatChild) { // add parent with transform - const NodeHandle childNode = this->scene.allocateNode(); - const TransformHandle childTransform = this->scene.allocateTransform(childNode); + const NodeHandle childNode = this->scene.allocateNode(0, {}); + const TransformHandle childTransform = this->scene.allocateTransform(childNode, {}); this->scene.addChildToNode(this->nodeWithTransform, childNode); this->expectIdentityMatrices(nodeWithoutTransform); @@ -232,8 +231,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, QuatParentQuatChild) { // add parent with transform - const NodeHandle childNode = this->scene.allocateNode(); - const TransformHandle childTransform = this->scene.allocateTransform(childNode); + const NodeHandle childNode = this->scene.allocateNode(0, {}); + const TransformHandle childTransform = this->scene.allocateTransform(childNode, {}); this->scene.addChildToNode(this->nodeWithTransform, childNode); this->expectIdentityMatrices(nodeWithoutTransform); @@ -266,8 +265,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, UpdatesMatrixCacheAfterAddingParentWithTransform) { // add parent with transform - const NodeHandle parent = this->scene.allocateNode(); - const TransformHandle parentTransform = this->scene.allocateTransform(parent); + const NodeHandle parent = this->scene.allocateNode(0, {}); + const TransformHandle parentTransform = this->scene.allocateTransform(parent, {}); this->scene.setScaling(parentTransform, glm::vec3(0.5f)); this->expectIdentityMatrices(this->nodeWithTransform); @@ -280,8 +279,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, UpdatesMatrixCacheAfterChangingTransformOfParent) { // add parent with transform - const NodeHandle parent = this->scene.allocateNode(); - const TransformHandle parentTransform = this->scene.allocateTransform(parent); + const NodeHandle parent = this->scene.allocateNode(0, {}); + const TransformHandle parentTransform = this->scene.allocateTransform(parent, {}); this->scene.setScaling(parentTransform, glm::vec3(0.5f)); this->scene.addChildToNode(parent, this->nodeWithTransform); @@ -307,8 +306,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, PropagatesMatricesToMultipleChildNodes) { // add parent with transform - const NodeHandle childLeft = this->scene.allocateNode(); - const NodeHandle childRight = this->scene.allocateNode(); + const NodeHandle childLeft = this->scene.allocateNode(0, {}); + const NodeHandle childRight = this->scene.allocateNode(0, {}); this->scene.addChildToNode(this->nodeWithTransform, childLeft); this->scene.addChildToNode(this->nodeWithTransform, childRight); @@ -326,8 +325,8 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, PropagatesMatricesToMultipleChildNodeWhenRotationConventionIsSet) { // add parent with transform - const NodeHandle childLeft = this->scene.allocateNode(); - const NodeHandle childRight = this->scene.allocateNode(); + const NodeHandle childLeft = this->scene.allocateNode(0, {}); + const NodeHandle childRight = this->scene.allocateNode(0, {}); this->scene.addChildToNode(this->nodeWithTransform, childLeft); this->scene.addChildToNode(this->nodeWithTransform, childRight); @@ -352,13 +351,13 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, confidenceTest_UpdatesMatricesOfMultipleChildNodesWithTransform) { // create a small transformation graph - parent and 2 children, all transforms - const NodeHandle childLeft = this->scene.allocateNode(); - const NodeHandle childRight = this->scene.allocateNode(); + const NodeHandle childLeft = this->scene.allocateNode(0, {}); + const NodeHandle childRight = this->scene.allocateNode(0, {}); this->scene.addChildToNode(this->nodeWithTransform, childLeft); this->scene.addChildToNode(this->nodeWithTransform, childRight); - TransformHandle childLeftTransform = this->scene.allocateTransform(childLeft); - TransformHandle childRightTransform = this->scene.allocateTransform(childRight); + TransformHandle childLeftTransform = this->scene.allocateTransform(childLeft, {}); + TransformHandle childRightTransform = this->scene.allocateTransform(childRight, {}); const glm::vec3 leftTranslation(0.1f, 0.2f, 0.3f); const glm::vec3 rightTranslation(0.4f, 0.5f, 0.6f); @@ -438,10 +437,10 @@ namespace ramses_internal TEST_F(ATransformationCachedScene, UpdatesMatricesWhenAddingNodeAsChildOfOtherNode) { - const NodeHandle parent = this->scene.allocateNode(); - const NodeHandle child = this->scene.allocateNode(); - const TransformHandle parentTransform = this->scene.allocateTransform(parent); - const TransformHandle childTransform = this->scene.allocateTransform(child); + const NodeHandle parent = this->scene.allocateNode(0, {}); + const NodeHandle child = this->scene.allocateNode(0, {}); + const TransformHandle parentTransform = this->scene.allocateTransform(parent, {}); + const TransformHandle childTransform = this->scene.allocateTransform(child, {}); glm::vec3 parentTranslation(1, 2, 3); glm::vec3 childTranslation(3, 4, 5); diff --git a/framework/SceneGraph/SceneAPI/test/ResourceContentHashTest.cpp b/tests/unittests/framework/SceneGraph/SceneAPI/ResourceContentHashTest.cpp similarity index 94% rename from framework/SceneGraph/SceneAPI/test/ResourceContentHashTest.cpp rename to tests/unittests/framework/SceneGraph/SceneAPI/ResourceContentHashTest.cpp index f1f692bc2..16fdef7e8 100644 --- a/framework/SceneGraph/SceneAPI/test/ResourceContentHashTest.cpp +++ b/tests/unittests/framework/SceneGraph/SceneAPI/ResourceContentHashTest.cpp @@ -6,15 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneAPI/ResourceContentHash.h" -#include "Utils/BinaryOutputStream.h" -#include "Utils/BinaryInputStream.h" -#include "Collections/HashSet.h" -#include "Collections/StringOutputStream.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/Core/Utils/BinaryOutputStream.h" +#include "internal/Core/Utils/BinaryInputStream.h" +#include "internal/PlatformAbstraction/Collections/HashSet.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" #include "gtest/gtest.h" #include -namespace ramses_internal +namespace ramses::internal { TEST(AResourceContentHash, canDefaultConstruct) { diff --git a/tests/unittests/framework/SceneGraph/SceneAPI/TextureSamplerStatesHashTest.cpp b/tests/unittests/framework/SceneGraph/SceneAPI/TextureSamplerStatesHashTest.cpp new file mode 100644 index 000000000..af6607dd7 --- /dev/null +++ b/tests/unittests/framework/SceneGraph/SceneAPI/TextureSamplerStatesHashTest.cpp @@ -0,0 +1,82 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/SceneGraph/SceneAPI/TextureSamplerStates.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + class TextureSamplerStatesHash : public ::testing::Test + { + protected: + static void ExpectSameHash(const TextureSamplerStates& state1, const TextureSamplerStates& state2) + { + const auto hash1 = state1.hash(); + const auto hash2 = state2.hash(); + EXPECT_EQ(hash1, hash2); + } + + static void ExpectNotSameHash(const TextureSamplerStates& state1, const TextureSamplerStates& state2) + { + const auto hash1 = state1.hash(); + const auto hash2 = state2.hash(); + EXPECT_NE(hash1, hash2); + } + }; + + TEST_F(TextureSamplerStatesHash, SameStatesHaveSameHash) + { + { + const TextureSamplerStates samplerStates; + const TextureSamplerStates otherSamplerStates; + ExpectSameHash(samplerStates, otherSamplerStates); + } + + { + const TextureSamplerStates samplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + ExpectSameHash(samplerStates, otherSamplerStates); + } + } + + TEST_F(TextureSamplerStatesHash, DifferentStatesHaveDifferentHash) + { + const TextureSamplerStates samplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + + //address mode U + { + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Repeat, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + ExpectNotSameHash(samplerStates, otherSamplerStates); + } + //address mode V + { + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + ExpectNotSameHash(samplerStates, otherSamplerStates); + } + //address mode R + { + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + ExpectNotSameHash(samplerStates, otherSamplerStates); + } + //min sampling + { + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Nearest_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 8u); + ExpectNotSameHash(samplerStates, otherSamplerStates); + } + //mag sampling + { + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Linear, 8u); + ExpectNotSameHash(samplerStates, otherSamplerStates); + } + //anisotropy level + { + const TextureSamplerStates otherSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear_MipMapLinear, ETextureSamplingMethod::Nearest_MipMapNearest, 1u); + ExpectNotSameHash(samplerStates, otherSamplerStates); + } + } +} diff --git a/framework/SceneReferencing/test/SceneReferenceEventTest.cpp b/tests/unittests/framework/SceneReferencing/SceneReferenceEventTest.cpp similarity index 93% rename from framework/SceneReferencing/test/SceneReferenceEventTest.cpp rename to tests/unittests/framework/SceneReferencing/SceneReferenceEventTest.cpp index 4cb5f97ed..445848151 100644 --- a/framework/SceneReferencing/test/SceneReferenceEventTest.cpp +++ b/tests/unittests/framework/SceneReferencing/SceneReferenceEventTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "SceneReferencing/SceneReferenceEvent.h" +#include "internal/SceneReferencing/SceneReferenceEvent.h" #include -namespace ramses_internal +namespace ramses::internal { bool checkEqual(SceneReferenceEvent const& a, SceneReferenceEvent const& b) { @@ -30,7 +30,7 @@ namespace ramses_internal { SceneReferenceEvent a(SceneId{ 123 }); SceneReferenceEvent b(SceneId{ 123 }); - std::vector testVec; + std::vector testVec; a.writeToBlob(testVec); b.readFromBlob(testVec); diff --git a/tests/unittests/framework/Watchdog/PlatformWatchDogMock.h b/tests/unittests/framework/Watchdog/PlatformWatchDogMock.h new file mode 100644 index 000000000..a0637cd0f --- /dev/null +++ b/tests/unittests/framework/Watchdog/PlatformWatchDogMock.h @@ -0,0 +1,23 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "gmock/gmock.h" +#include "ramses/framework/IThreadWatchdogNotification.h" + +namespace ramses::internal +{ + class PlatformWatchdogMockCallback : public IThreadWatchdogNotification + { + public: + MOCK_METHOD(void, notifyThread, (ERamsesThreadIdentifier), (override)); + MOCK_METHOD(void, registerThread, (ERamsesThreadIdentifier), (override)); + MOCK_METHOD(void, unregisterThread, (ERamsesThreadIdentifier), (override)); + }; +} diff --git a/framework/Watchdog/test/PlatformWatchDogTest.cpp b/tests/unittests/framework/Watchdog/PlatformWatchDogTest.cpp similarity index 54% rename from framework/Watchdog/test/PlatformWatchDogTest.cpp rename to tests/unittests/framework/Watchdog/PlatformWatchDogTest.cpp index f3321fb8b..918d7c2cc 100644 --- a/framework/Watchdog/test/PlatformWatchDogTest.cpp +++ b/tests/unittests/framework/Watchdog/PlatformWatchDogTest.cpp @@ -7,89 +7,89 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "Watchdog/PlatformWatchdog.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/Watchdog/PlatformWatchdog.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include "PlatformWatchDogMock.h" using namespace std::chrono_literals; -namespace ramses_internal +namespace ramses::internal { TEST(PlatformWatchDogTest, callsRegisterAndUnregister) { PlatformWatchdogMockCallback callback; - EXPECT_CALL(callback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - PlatformWatchdog watchdogNotifer(100ms, ramses::ERamsesThreadIdentifier::Workers, &callback); - EXPECT_CALL(callback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, registerThread(ERamsesThreadIdentifier::Workers)); + PlatformWatchdog watchdogNotifer(100ms, ERamsesThreadIdentifier::Workers, &callback); + EXPECT_CALL(callback, unregisterThread(ERamsesThreadIdentifier::Workers)); } TEST(PlatformWatchDogTest, callsThePlatformFunctionRightAway) { PlatformWatchdogMockCallback callback; - EXPECT_CALL(callback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - PlatformWatchdog watchdogNotifer(100ms, ramses::ERamsesThreadIdentifier::Workers, &callback); + EXPECT_CALL(callback, registerThread(ERamsesThreadIdentifier::Workers)); + PlatformWatchdog watchdogNotifer(100ms, ERamsesThreadIdentifier::Workers, &callback); - EXPECT_CALL(callback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, notifyThread(ERamsesThreadIdentifier::Workers)); watchdogNotifer.notifyWatchdog(); - EXPECT_CALL(callback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, unregisterThread(ERamsesThreadIdentifier::Workers)); } TEST(PlatformWatchDogTest, debouncesCallsToPlatformWatchdog) { PlatformWatchdogMockCallback callback; - EXPECT_CALL(callback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - PlatformWatchdog watchdogNotifer(10000ms, ramses::ERamsesThreadIdentifier::Workers, &callback); + EXPECT_CALL(callback, registerThread(ERamsesThreadIdentifier::Workers)); + PlatformWatchdog watchdogNotifer(10000ms, ERamsesThreadIdentifier::Workers, &callback); - EXPECT_CALL(callback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); + EXPECT_CALL(callback, notifyThread(ERamsesThreadIdentifier::Workers)).Times(1); watchdogNotifer.notifyWatchdog(); // this calls right away watchdogNotifer.notifyWatchdog(); // no call, not enough time passed watchdogNotifer.notifyWatchdog(); // no call, not enough time passed watchdogNotifer.notifyWatchdog(); // no call, not enough time passed - EXPECT_CALL(callback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, unregisterThread(ERamsesThreadIdentifier::Workers)); } TEST(PlatformWatchDogTest, againCallsPlatformAfterDebounceTime) { PlatformWatchdogMockCallback callback; - EXPECT_CALL(callback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - PlatformWatchdog watchdogNotifer(200ms, ramses::ERamsesThreadIdentifier::Workers, &callback); + EXPECT_CALL(callback, registerThread(ERamsesThreadIdentifier::Workers)); + PlatformWatchdog watchdogNotifer(200ms, ERamsesThreadIdentifier::Workers, &callback); - EXPECT_CALL(callback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); + EXPECT_CALL(callback, notifyThread(ERamsesThreadIdentifier::Workers)).Times(1); watchdogNotifer.notifyWatchdog(); // this calls right away watchdogNotifer.notifyWatchdog(); // no call, not enough time passed watchdogNotifer.notifyWatchdog(); // no call, not enough time passed ::testing::Mock::VerifyAndClearExpectations(&watchdogNotifer); - EXPECT_CALL(callback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); + EXPECT_CALL(callback, notifyThread(ERamsesThreadIdentifier::Workers)).Times(1); PlatformThread::Sleep(500); watchdogNotifer.notifyWatchdog(); // this calls, because first after wait time watchdogNotifer.notifyWatchdog(); // no call, not enough time passed watchdogNotifer.notifyWatchdog(); // no call, not enough time passed - EXPECT_CALL(callback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, unregisterThread(ERamsesThreadIdentifier::Workers)); } TEST(PlatformWatchDogTest, alwaysAllowsFirstNotification) { PlatformWatchdogMockCallback callback; - EXPECT_CALL(callback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - PlatformWatchdog watchdogNotifer(std::chrono::milliseconds::max(), ramses::ERamsesThreadIdentifier::Workers, &callback); - EXPECT_CALL(callback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, registerThread(ERamsesThreadIdentifier::Workers)); + PlatformWatchdog watchdogNotifer(std::chrono::milliseconds::max(), ERamsesThreadIdentifier::Workers, &callback); + EXPECT_CALL(callback, unregisterThread(ERamsesThreadIdentifier::Workers)); - EXPECT_CALL(callback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); + EXPECT_CALL(callback, notifyThread(ERamsesThreadIdentifier::Workers)).Times(1); watchdogNotifer.notifyWatchdog(); } TEST(PlatformWatchDogTest, alwaysNotifiesWithZeroInterval) { PlatformWatchdogMockCallback callback; - EXPECT_CALL(callback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - PlatformWatchdog watchdogNotifer(0ms, ramses::ERamsesThreadIdentifier::Workers, &callback); - EXPECT_CALL(callback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(callback, registerThread(ERamsesThreadIdentifier::Workers)); + PlatformWatchdog watchdogNotifer(0ms, ERamsesThreadIdentifier::Workers, &callback); + EXPECT_CALL(callback, unregisterThread(ERamsesThreadIdentifier::Workers)); - EXPECT_CALL(callback, notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(6); + EXPECT_CALL(callback, notifyThread(ERamsesThreadIdentifier::Workers)).Times(6); EXPECT_EQ(0ms, watchdogNotifer.calculateTimeout()); watchdogNotifer.notifyWatchdog(); EXPECT_EQ(0ms, watchdogNotifer.calculateTimeout()); diff --git a/framework/Watchdog/test/ThreadWatchdogTest.cpp b/tests/unittests/framework/Watchdog/ThreadWatchdogTest.cpp similarity index 89% rename from framework/Watchdog/test/ThreadWatchdogTest.cpp rename to tests/unittests/framework/Watchdog/ThreadWatchdogTest.cpp index a00339ac0..32eee9d23 100644 --- a/framework/Watchdog/test/ThreadWatchdogTest.cpp +++ b/tests/unittests/framework/Watchdog/ThreadWatchdogTest.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ThreadWatchdogConfig.h" +#include "impl/ThreadWatchdogConfig.h" #include "PlatformWatchDogMock.h" -#include "Watchdog/ThreadWatchdog.h" +#include "internal/Watchdog/ThreadWatchdog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class AThreadWatchdog : public ::testing::Test { @@ -20,13 +20,13 @@ namespace ramses_internal void SetUp() override { config.setThreadWatchDogCallback(&mockCallback); - config.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Workers, 0); + config.setWatchdogNotificationInterval(ERamsesThreadIdentifier::Workers, 0); EXPECT_CALL(mockCallback, registerThread(_)); } void createWatchdog() { - watchdog = std::make_unique(config, ramses::ERamsesThreadIdentifier::Workers); + watchdog = std::make_unique(config, ERamsesThreadIdentifier::Workers); } void TearDown() override @@ -46,18 +46,18 @@ namespace ramses_internal config.setThreadWatchDogCallback(&mockCallback); { - EXPECT_CALL(mockCallback, registerThread(ramses::ERamsesThreadIdentifier::Workers)); - ThreadWatchdog watchdog(config, ramses::ERamsesThreadIdentifier::Workers); + EXPECT_CALL(mockCallback, registerThread(ERamsesThreadIdentifier::Workers)); + ThreadWatchdog watchdog(config, ERamsesThreadIdentifier::Workers); Mock::VerifyAndClearExpectations(&mockCallback); - EXPECT_CALL(mockCallback, unregisterThread(ramses::ERamsesThreadIdentifier::Workers)); + EXPECT_CALL(mockCallback, unregisterThread(ERamsesThreadIdentifier::Workers)); } { - EXPECT_CALL(mockCallback, registerThread(ramses::ERamsesThreadIdentifier::Renderer)); - ThreadWatchdog watchdog(config, ramses::ERamsesThreadIdentifier::Renderer); + EXPECT_CALL(mockCallback, registerThread(ERamsesThreadIdentifier::Renderer)); + ThreadWatchdog watchdog(config, ERamsesThreadIdentifier::Renderer); Mock::VerifyAndClearExpectations(&mockCallback); - EXPECT_CALL(mockCallback, unregisterThread(ramses::ERamsesThreadIdentifier::Renderer)); + EXPECT_CALL(mockCallback, unregisterThread(ERamsesThreadIdentifier::Renderer)); } } @@ -228,7 +228,7 @@ namespace ramses_internal // TODO(Carsten): test if actual value is respected as soon as time system can be mocked TEST_F(AThreadWatchdog, respectsNotificationInterval) { - config.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Workers, static_cast(-1)); + config.setWatchdogNotificationInterval(ERamsesThreadIdentifier::Workers, static_cast(-1)); createWatchdog(); auto t0 = watchdog->registerThread(); auto t1 = watchdog->registerThread(); diff --git a/framework/test/main.cpp b/tests/unittests/framework/main.cpp similarity index 100% rename from framework/test/main.cpp rename to tests/unittests/framework/main.cpp diff --git a/framework/ramses-framework/test/APILoggingHelperTest.cpp b/tests/unittests/framework/ramses-framework/APILoggingHelperTest.cpp similarity index 95% rename from framework/ramses-framework/test/APILoggingHelperTest.cpp rename to tests/unittests/framework/ramses-framework/APILoggingHelperTest.cpp index 834cc4384..c00e28e62 100644 --- a/framework/ramses-framework/test/APILoggingHelperTest.cpp +++ b/tests/unittests/framework/ramses-framework/APILoggingHelperTest.cpp @@ -7,9 +7,9 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "APILoggingHelper.h" +#include "impl/APILoggingHelper.h" -namespace ramses_internal +namespace ramses::internal { TEST(APILoggingHelperTest, emptyArgumentsTest) { diff --git a/framework/ramses-framework/test/ApiRamshCommandMock.cpp b/tests/unittests/framework/ramses-framework/ApiRamshCommandMock.cpp similarity index 95% rename from framework/ramses-framework/test/ApiRamshCommandMock.cpp rename to tests/unittests/framework/ramses-framework/ApiRamshCommandMock.cpp index 98eb377e6..7708e6c56 100644 --- a/framework/ramses-framework/test/ApiRamshCommandMock.cpp +++ b/tests/unittests/framework/ramses-framework/ApiRamshCommandMock.cpp @@ -8,7 +8,7 @@ #include "ApiRamshCommandMock.h" -namespace ramses_internal +namespace ramses::internal { ApiRamshCommandMock::ApiRamshCommandMock() = default; ApiRamshCommandMock::~ApiRamshCommandMock() = default; diff --git a/framework/ramses-framework/test/ApiRamshCommandMock.h b/tests/unittests/framework/ramses-framework/ApiRamshCommandMock.h similarity index 78% rename from framework/ramses-framework/test/ApiRamshCommandMock.h rename to tests/unittests/framework/ramses-framework/ApiRamshCommandMock.h index 0f5d0dd96..fed513b67 100644 --- a/framework/ramses-framework/test/ApiRamshCommandMock.h +++ b/tests/unittests/framework/ramses-framework/ApiRamshCommandMock.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TEST_APIRAMSHCOMMANDMOCK_H -#define RAMSES_TEST_APIRAMSHCOMMANDMOCK_H +#pragma once -#include "ramses-framework-api/IRamshCommand.h" +#include "ramses/framework/IRamshCommand.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { - class ApiRamshCommandMock : public ramses::IRamshCommand + class ApiRamshCommandMock : public IRamshCommand { public: ApiRamshCommandMock(); @@ -25,5 +24,3 @@ namespace ramses_internal MOCK_METHOD(bool, execute, (const std::vector& input), (override)); }; } - -#endif diff --git a/framework/ramses-framework/test/CommandTest.cpp b/tests/unittests/framework/ramses-framework/CommandTest.cpp similarity index 52% rename from framework/ramses-framework/test/CommandTest.cpp rename to tests/unittests/framework/ramses-framework/CommandTest.cpp index 09e9d160d..0f052168a 100644 --- a/framework/ramses-framework/test/CommandTest.cpp +++ b/tests/unittests/framework/ramses-framework/CommandTest.cpp @@ -10,23 +10,24 @@ #include "CommandTest.h" using namespace testing; -using namespace ramses_internal; - -TEST(ACommand, CanBeCreated) +namespace ramses::internal { - const DoThisCommand dtc(1u); - const DoNothingCommand dnc; + TEST(ACommand, CanBeCreated) + { + const DoThisCommand dtc(1u); + const DoNothingCommand dnc; - EXPECT_EQ(E_TestCommandTypes_DoThis, dtc.commandType); - EXPECT_EQ(E_TestCommandTypes_DoNothing, dnc.commandType); -} + EXPECT_EQ(E_TestCommandTypes_DoThis, dtc.commandType); + EXPECT_EQ(E_TestCommandTypes_DoNothing, dnc.commandType); + } -TEST(ACommand, canConvertToItsType) -{ - const auto c = std::make_unique(23u); - ASSERT_TRUE(c); + TEST(ACommand, canConvertToItsType) + { + const auto c = std::make_unique(23u); + ASSERT_TRUE(c); - const DoThisCommand& dtc = c->convertTo< DoThisCommand >(); - EXPECT_EQ(23u, dtc.value); + const DoThisCommand& dtc = c->convertTo(); + EXPECT_EQ(23u, dtc.value); + } } diff --git a/framework/ramses-framework/test/CommandTest.h b/tests/unittests/framework/ramses-framework/CommandTest.h similarity index 83% rename from framework/ramses-framework/test/CommandTest.h rename to tests/unittests/framework/ramses-framework/CommandTest.h index 3122ba8ff..577b98778 100644 --- a/framework/ramses-framework/test/CommandTest.h +++ b/tests/unittests/framework/ramses-framework/CommandTest.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_COMMANDTEST_H -#define RAMSES_COMMANDTEST_H +#pragma once -#include "CommandT.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "impl/CommandT.h" +#include "ramses/framework/RamsesFrameworkTypes.h" -namespace ramses_internal +namespace ramses::internal { enum E_TestCommandTypes { @@ -31,7 +30,7 @@ namespace ramses_internal , value(v) {} - uint32_t value; + uint32_t value = 0; }; @@ -41,4 +40,3 @@ namespace ramses_internal }; } -#endif // RAMSES_COMMANDTEST_H diff --git a/tests/unittests/framework/ramses-framework/DataTypeTest.cpp b/tests/unittests/framework/ramses-framework/DataTypeTest.cpp new file mode 100644 index 000000000..d6450c4f8 --- /dev/null +++ b/tests/unittests/framework/ramses-framework/DataTypeTest.cpp @@ -0,0 +1,273 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include "ramses/framework/EDataType.h" +#include "impl/DataTypesImpl.h" +#include "impl/DataTypeUtils.h" +#include "internal/SceneGraph/SceneAPI/EDataType.h" + +namespace ramses::internal +{ + using namespace testing; + + TEST(Datatype, CanBeConstructedFromInitializerList) + { + [[maybe_unused]] vec2f v2f{1.f, 2.f}; + [[maybe_unused]] vec3f v3f{1.f, 2.f, 3.f}; + [[maybe_unused]] vec4f v4f{1.f, 2.f, 3.f, 4.f}; + [[maybe_unused]] vec2i v2i{1, 2}; + [[maybe_unused]] vec3i v3i{1, 2, 3}; + [[maybe_unused]] vec4i v4i{1, 2, 3, 4}; + [[maybe_unused]] matrix22f m2{1.f, 2.f, 3.f, 4.f}; + [[maybe_unused]] matrix33f m3{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f}; + [[maybe_unused]] matrix44f m4{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f}; + } + + TEST(Datatype, CanBeCopyConstructed) + { + [[maybe_unused]] vec2f v2f{vec2f{1.f, 2.f}}; + [[maybe_unused]] vec3f v3f{vec3f{1.f, 2.f, 3.f}}; + [[maybe_unused]] vec4f v4f{vec4f{1.f, 2.f, 3.f, 4.f}}; + [[maybe_unused]] vec2i v2i{vec2i{1, 2}}; + [[maybe_unused]] vec3i v3i{vec3i{1, 2, 3}}; + [[maybe_unused]] vec4i v4i{vec4i{1, 2, 3, 4}}; + [[maybe_unused]] matrix22f m2{matrix22f{1.f, 2.f, 3.f, 4.f}}; + [[maybe_unused]] matrix33f m3{matrix33f{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f}}; + [[maybe_unused]] matrix44f m4{matrix44f{1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f}}; + } + + TEST(EDatatype, CanReturnNumberOfComponentsAtCompiletime) + { + static_assert(1u == GetNumberOfComponents(ramses::EDataType::UInt16), "Invalid number of components"); + static_assert(1u == GetNumberOfComponents(ramses::EDataType::UInt32), "Invalid number of components"); + static_assert(1u == GetNumberOfComponents(ramses::EDataType::Float), "Invalid number of components"); + static_assert(2u == GetNumberOfComponents(ramses::EDataType::Vector2F), "Invalid number of components"); + static_assert(3u == GetNumberOfComponents(ramses::EDataType::Vector3F), "Invalid number of components"); + static_assert(4u == GetNumberOfComponents(ramses::EDataType::Vector4F), "Invalid number of components"); + static_assert(1u == GetNumberOfComponents(ramses::EDataType::ByteBlob), "Invalid number of components"); + static_assert(1u == GetNumberOfComponents(ramses::EDataType::Bool), "Invalid number of components"); + static_assert(1u == GetNumberOfComponents(ramses::EDataType::Int32), "Invalid number of components"); + static_assert(2u == GetNumberOfComponents(ramses::EDataType::Vector2I), "Invalid number of components"); + static_assert(3u == GetNumberOfComponents(ramses::EDataType::Vector3I), "Invalid number of components"); + static_assert(4u == GetNumberOfComponents(ramses::EDataType::Vector4I), "Invalid number of components"); + static_assert(4u == GetNumberOfComponents(ramses::EDataType::Matrix22F), "Invalid number of components"); + static_assert(9u == GetNumberOfComponents(ramses::EDataType::Matrix33F), "Invalid number of components"); + static_assert(16u == GetNumberOfComponents(ramses::EDataType::Matrix44F), "Invalid number of components"); + + static_assert(0u == GetNumberOfComponents(ramses::EDataType::TextureSampler2D), "Invalid number of components"); + static_assert(0u == GetNumberOfComponents(ramses::EDataType::TextureSampler2DMS), "Invalid number of components"); + static_assert(0u == GetNumberOfComponents(ramses::EDataType::TextureSampler3D), "Invalid number of components"); + static_assert(0u == GetNumberOfComponents(ramses::EDataType::TextureSamplerCube), "Invalid number of components"); + static_assert(0u == GetNumberOfComponents(ramses::EDataType::TextureSamplerExternal), "Invalid number of components"); + } + + TEST(EDatatype, CanReturnSizeOfComponentAtCompiletime) + { + static_assert(sizeof(uint16_t) == GetSizeOfComponent(ramses::EDataType::UInt16), "component of EDataType::UInt16 should have size of uint16_t"); + static_assert(sizeof(uint32_t) == GetSizeOfComponent(ramses::EDataType::UInt32), "component of EDataType::UInt32 should have size of uint32_t"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Float), "component of EDataType::Float should have size of float"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Vector2F), "component of EDataType::Vector2F should have size of float"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Vector3F), "component of EDataType::Vector3F should have size of float"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Vector4F), "component of EDataType::Vector4F should have size of float"); + static_assert(sizeof(std::byte) == GetSizeOfComponent(ramses::EDataType::ByteBlob), "component of EDataType::ByteBlob should have size of byte"); + static_assert(sizeof(bool) == GetSizeOfComponent(ramses::EDataType::Bool), "Invalid component size"); + static_assert(sizeof(int32_t) == GetSizeOfComponent(ramses::EDataType::Int32), "Invalid component size"); + static_assert(sizeof(int32_t) == GetSizeOfComponent(ramses::EDataType::Vector2I), "Invalid component size"); + static_assert(sizeof(int32_t) == GetSizeOfComponent(ramses::EDataType::Vector3I), "Invalid component size"); + static_assert(sizeof(int32_t) == GetSizeOfComponent(ramses::EDataType::Vector4I), "Invalid component size"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Matrix22F), "Invalid component size"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Matrix33F), "Invalid component size"); + static_assert(sizeof(float) == GetSizeOfComponent(ramses::EDataType::Matrix44F), "Invalid component size"); + + static_assert(0u == GetSizeOfComponent(ramses::EDataType::TextureSampler2D), "Invalid component size"); + static_assert(0u == GetSizeOfComponent(ramses::EDataType::TextureSampler2DMS), "Invalid component size"); + static_assert(0u == GetSizeOfComponent(ramses::EDataType::TextureSampler3D), "Invalid component size"); + static_assert(0u == GetSizeOfComponent(ramses::EDataType::TextureSamplerCube), "Invalid component size"); + static_assert(0u == GetSizeOfComponent(ramses::EDataType::TextureSamplerExternal), "Invalid component size"); + } + + TEST(EDatatype, CanReturnSizesAtCompiletime) + { + static_assert(sizeof(uint16_t) == GetSizeOfDataType(ramses::EDataType::UInt16), "Invalid data type size"); + static_assert(sizeof(uint32_t) == GetSizeOfDataType(ramses::EDataType::UInt32), "Invalid data type size"); + static_assert(sizeof(float) == GetSizeOfDataType(ramses::EDataType::Float), "Invalid data type size"); + static_assert(sizeof(ramses::vec2f) == GetSizeOfDataType(ramses::EDataType::Vector2F), "Invalid data type size"); + static_assert(sizeof(ramses::vec3f) == GetSizeOfDataType(ramses::EDataType::Vector3F), "Invalid data type size"); + static_assert(sizeof(ramses::vec4f) == GetSizeOfDataType(ramses::EDataType::Vector4F), "Invalid data type size"); + static_assert(sizeof(std::byte) == GetSizeOfDataType(ramses::EDataType::ByteBlob), "Invalid data type size"); + static_assert(sizeof(bool) == GetSizeOfDataType(ramses::EDataType::Bool), "Invalid data type size"); + static_assert(sizeof(int32_t) == GetSizeOfDataType(ramses::EDataType::Int32), "Invalid data type size"); + static_assert(sizeof(ramses::vec2i) == GetSizeOfDataType(ramses::EDataType::Vector2I), "Invalid data type size"); + static_assert(sizeof(ramses::vec3i) == GetSizeOfDataType(ramses::EDataType::Vector3I), "Invalid data type size"); + static_assert(sizeof(ramses::vec4i) == GetSizeOfDataType(ramses::EDataType::Vector4I), "Invalid data type size"); + static_assert(sizeof(ramses::matrix22f) == GetSizeOfDataType(ramses::EDataType::Matrix22F), "Invalid data type size"); + static_assert(sizeof(ramses::matrix33f) == GetSizeOfDataType(ramses::EDataType::Matrix33F), "Invalid data type size"); + static_assert(sizeof(ramses::matrix44f) == GetSizeOfDataType(ramses::EDataType::Matrix44F), "Invalid data type size"); + + static_assert(0u == GetSizeOfDataType(ramses::EDataType::TextureSampler2D), "Invalid data type size"); + static_assert(0u == GetSizeOfDataType(ramses::EDataType::TextureSampler2DMS), "Invalid data type size"); + static_assert(0u == GetSizeOfDataType(ramses::EDataType::TextureSampler3D), "Invalid data type size"); + static_assert(0u == GetSizeOfDataType(ramses::EDataType::TextureSamplerCube), "Invalid data type size"); + static_assert(0u == GetSizeOfDataType(ramses::EDataType::TextureSamplerExternal), "Invalid data type size"); + } + + TEST(EDataType, EnsureGivesSameSizeOnPublicAPI) + { + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::UInt16) == ramses::internal::EnumToSize(ramses::internal::EDataType::UInt16)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::UInt32) == ramses::internal::EnumToSize(ramses::internal::EDataType::UInt32)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Float) == ramses::internal::EnumToSize(ramses::internal::EDataType::Float)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector2F) == ramses::internal::EnumToSize(ramses::internal::EDataType::Vector2F)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector3F) == ramses::internal::EnumToSize(ramses::internal::EDataType::Vector3F)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector4F) == ramses::internal::EnumToSize(ramses::internal::EDataType::Vector4F)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::ByteBlob) == ramses::internal::EnumToSize(ramses::internal::EDataType::ByteBlob)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Bool) == ramses::internal::EnumToSize(ramses::internal::EDataType::Bool)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Int32) == ramses::internal::EnumToSize(ramses::internal::EDataType::Int32)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector2I) == ramses::internal::EnumToSize(ramses::internal::EDataType::Vector2I)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector3I) == ramses::internal::EnumToSize(ramses::internal::EDataType::Vector3I)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Vector4I) == ramses::internal::EnumToSize(ramses::internal::EDataType::Vector4I)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Matrix22F) == ramses::internal::EnumToSize(ramses::internal::EDataType::Matrix22F)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Matrix33F) == ramses::internal::EnumToSize(ramses::internal::EDataType::Matrix33F)); + static_assert(ramses::GetSizeOfDataType(ramses::EDataType::Matrix44F) == ramses::internal::EnumToSize(ramses::internal::EDataType::Matrix44F)); + + // not relevant for sampler types + } + + TEST(EDataType, EnsureGivesSameNumberOfComponentsOnPublicAPI) + { + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::UInt16) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::UInt16)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::UInt32) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::UInt32)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Float) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Float)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector2F) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Vector2F)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector3F) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Vector3F)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector4F) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Vector4F)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::ByteBlob) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::ByteBlob)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Bool) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Bool)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Int32) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Int32)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector2I) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Vector2I)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector3I) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Vector3I)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Vector4I) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Vector4I)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Matrix22F) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Matrix22F)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Matrix33F) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Matrix33F)); + static_assert(ramses::GetNumberOfComponents(ramses::EDataType::Matrix44F) == ramses::internal::EnumToNumComponents(ramses::internal::EDataType::Matrix44F)); + + // not relevant for sampler types + } + + TEST(EDataType, IsUniformInputDataType) + { + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + static_assert(ramses::IsUniformInputDataType()); + + static_assert(!ramses::IsUniformInputDataType()); + static_assert(!ramses::IsUniformInputDataType()); + static_assert(!ramses::IsUniformInputDataType()); + } + + TEST(EDataType, IsArrayResourceDataType) + { + static_assert(ramses::IsArrayResourceDataType()); + static_assert(ramses::IsArrayResourceDataType()); + static_assert(ramses::IsArrayResourceDataType()); + static_assert(ramses::IsArrayResourceDataType()); + static_assert(ramses::IsArrayResourceDataType()); + static_assert(ramses::IsArrayResourceDataType()); + static_assert(ramses::IsArrayResourceDataType()); + + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + static_assert(!ramses::IsArrayResourceDataType()); + } + + TEST(EDataType, GetEDataType) + { + static_assert(ramses::EDataType::UInt16 == ramses::GetEDataType()); + static_assert(ramses::EDataType::UInt32 == ramses::GetEDataType()); + static_assert(ramses::EDataType::Float == ramses::GetEDataType()); + static_assert(ramses::EDataType::Vector2F == ramses::GetEDataType()); + static_assert(ramses::EDataType::Vector3F == ramses::GetEDataType()); + static_assert(ramses::EDataType::Vector4F == ramses::GetEDataType()); + static_assert(ramses::EDataType::ByteBlob == ramses::GetEDataType()); + static_assert(ramses::EDataType::Bool == ramses::GetEDataType()); + static_assert(ramses::EDataType::Int32 == ramses::GetEDataType()); + static_assert(ramses::EDataType::Vector2I == ramses::GetEDataType()); + static_assert(ramses::EDataType::Vector3I == ramses::GetEDataType()); + static_assert(ramses::EDataType::Vector4I == ramses::GetEDataType()); + static_assert(ramses::EDataType::Matrix22F == ramses::GetEDataType()); + static_assert(ramses::EDataType::Matrix33F == ramses::GetEDataType()); + static_assert(ramses::EDataType::Matrix44F == ramses::GetEDataType()); + } + + TEST(EDataType, InternalToPublic) + { + EXPECT_EQ(ramses::EDataType::Bool, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Bool)); + EXPECT_EQ(ramses::EDataType::Int32, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Int32)); + EXPECT_EQ(ramses::EDataType::UInt16, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::UInt16)); + EXPECT_EQ(ramses::EDataType::UInt32, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::UInt32)); + EXPECT_EQ(ramses::EDataType::Float, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Float)); + EXPECT_EQ(ramses::EDataType::Vector2F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector2F)); + EXPECT_EQ(ramses::EDataType::Vector3F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector3F)); + EXPECT_EQ(ramses::EDataType::Vector4F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector4F)); + EXPECT_EQ(ramses::EDataType::Vector2I, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector2I)); + EXPECT_EQ(ramses::EDataType::Vector3I, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector3I)); + EXPECT_EQ(ramses::EDataType::Vector4I, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector4I)); + EXPECT_EQ(ramses::EDataType::Matrix22F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Matrix22F)); + EXPECT_EQ(ramses::EDataType::Matrix33F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Matrix33F)); + EXPECT_EQ(ramses::EDataType::Matrix44F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Matrix44F)); + EXPECT_EQ(ramses::EDataType::TextureSampler2D, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::TextureSampler2D)); + EXPECT_EQ(ramses::EDataType::TextureSampler2DMS, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::TextureSampler2DMS)); + EXPECT_EQ(ramses::EDataType::TextureSampler3D, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::TextureSampler3D)); + EXPECT_EQ(ramses::EDataType::TextureSamplerCube, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::TextureSamplerCube)); + EXPECT_EQ(ramses::EDataType::TextureSamplerExternal, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::TextureSamplerExternal)); + + EXPECT_EQ(ramses::EDataType::Float, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::FloatBuffer)); + EXPECT_EQ(ramses::EDataType::UInt16, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::UInt16Buffer)); + EXPECT_EQ(ramses::EDataType::Vector2F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector2Buffer)); + EXPECT_EQ(ramses::EDataType::Vector3F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector3Buffer)); + EXPECT_EQ(ramses::EDataType::Vector4F, DataTypeUtils::ConvertDataTypeFromInternal(ramses::internal::EDataType::Vector4Buffer)); + } + + TEST(EDataType, PublicToInternal) + { + EXPECT_EQ(ramses::internal::EDataType::Bool, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Bool)); + EXPECT_EQ(ramses::internal::EDataType::Int32, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Int32)); + EXPECT_EQ(ramses::internal::EDataType::UInt16, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::UInt16)); + EXPECT_EQ(ramses::internal::EDataType::UInt32, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::UInt32)); + EXPECT_EQ(ramses::internal::EDataType::Float, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Float)); + EXPECT_EQ(ramses::internal::EDataType::Vector2F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector2F)); + EXPECT_EQ(ramses::internal::EDataType::Vector3F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector3F)); + EXPECT_EQ(ramses::internal::EDataType::Vector4F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector4F)); + EXPECT_EQ(ramses::internal::EDataType::Vector2I, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector2I)); + EXPECT_EQ(ramses::internal::EDataType::Vector3I, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector3I)); + EXPECT_EQ(ramses::internal::EDataType::Vector4I, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Vector4I)); + EXPECT_EQ(ramses::internal::EDataType::Matrix22F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Matrix22F)); + EXPECT_EQ(ramses::internal::EDataType::Matrix33F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Matrix33F)); + EXPECT_EQ(ramses::internal::EDataType::Matrix44F, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::Matrix44F)); + EXPECT_EQ(ramses::internal::EDataType::TextureSampler2D, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSampler2D)); + EXPECT_EQ(ramses::internal::EDataType::TextureSampler2DMS, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSampler2DMS)); + EXPECT_EQ(ramses::internal::EDataType::TextureSampler3D, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSampler3D)); + EXPECT_EQ(ramses::internal::EDataType::TextureSamplerCube, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSamplerCube)); + EXPECT_EQ(ramses::internal::EDataType::TextureSamplerExternal, DataTypeUtils::ConvertDataTypeToInternal(ramses::EDataType::TextureSamplerExternal)); + } +} diff --git a/tests/unittests/framework/ramses-framework/ErrorReportingTest.cpp b/tests/unittests/framework/ramses-framework/ErrorReportingTest.cpp new file mode 100644 index 000000000..d08074056 --- /dev/null +++ b/tests/unittests/framework/ramses-framework/ErrorReportingTest.cpp @@ -0,0 +1,145 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" + +#include "impl/ErrorReporting.h" +#include "impl/RamsesObjectImpl.h" +#include "ramses/framework/RamsesObject.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/ThreadBarrier.h" +#include "LogTestUtils.h" +#include + +namespace ramses::internal +{ + class DummyObjectImpl : public RamsesObjectImpl + { + public: + DummyObjectImpl(ERamsesObjectType type, std::string_view name) + : RamsesObjectImpl(type, name) + {} + + void deinitializeFrameworkData() override {}; + }; + class DummyObject : public RamsesObject + { + public: + explicit DummyObject(std::string_view name) + : RamsesObject{ std::make_unique(ERamsesObjectType::Node, name) } + { + } + ~DummyObject() override = default; + }; + + class AErrorReporting : public ::testing::Test + { + protected: + ErrorReporting m_errorReporting; + std::vector m_loggedErrors; + ScopedLogContextLevel m_logCollector{CONTEXT_CLIENT, ELogLevel::Info, [this](ELogLevel type, std::string_view message) { + EXPECT_EQ(ELogLevel::Error, type); + m_loggedErrors.emplace_back(std::string(message)); + }}; + + DummyObject object1{ "obj1" }; + DummyObject object2{ "obj2" }; + }; + + TEST_F(AErrorReporting, HasNoErrorInitially) + { + EXPECT_FALSE(m_errorReporting.getError().has_value()); + } + + TEST_F(AErrorReporting, ProducesNoLogsDuringConstruction) + { + EXPECT_EQ(0u, m_loggedErrors.size()); + } + + TEST_F(AErrorReporting, StoresAffectedObjectWhenProvided) + { + m_errorReporting.set("error 1", &object1); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "error 1"); + EXPECT_EQ(m_errorReporting.getError()->object, &object1); + EXPECT_EQ(m_errorReporting.getError()->type, EIssueType::Error); + + m_errorReporting.set("error 2", &object2); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "error 2"); + EXPECT_EQ(m_errorReporting.getError()->object, &object2); + EXPECT_EQ(m_errorReporting.getError()->type, EIssueType::Error); + } + + TEST_F(AErrorReporting, StoresAffectedObjectWhenProvidedAsImpl) + { + m_errorReporting.set("error 1", object1.impl()); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "error 1"); + EXPECT_EQ(m_errorReporting.getError()->object, &object1); + EXPECT_EQ(m_errorReporting.getError()->type, EIssueType::Error); + + m_errorReporting.set("error 2", object2.impl()); + ASSERT_TRUE(m_errorReporting.getError().has_value()); + EXPECT_EQ(m_errorReporting.getError()->message, "error 2"); + EXPECT_EQ(m_errorReporting.getError()->object, &object2); + EXPECT_EQ(m_errorReporting.getError()->type, EIssueType::Error); + } + + TEST_F(AErrorReporting, LogsErrorsInTheOrderAdded) + { + m_errorReporting.set("error 1", &object1); + m_errorReporting.set("error 2", &object2); + + ASSERT_EQ(m_loggedErrors.size(), 2u); + EXPECT_EQ(m_loggedErrors[0], "[obj1] error 1"); + EXPECT_EQ(m_loggedErrors[1], "[obj2] error 2"); + } + + TEST_F(AErrorReporting, ClearsError) + { + m_errorReporting.set("error 1", nullptr); + EXPECT_TRUE(m_errorReporting.getError().has_value()); + + m_errorReporting.reset(); + EXPECT_FALSE(m_errorReporting.getError().has_value()); + } + + TEST_F(AErrorReporting, IsThreadsafe) + { + ThreadBarrier initDoneBarrier(3); + ThreadBarrier setDoneBarrier(3); + ThreadBarrier getDone(3); + + std::thread t1([&]() { + initDoneBarrier.wait(); + m_errorReporting.set("error 1", nullptr); + setDoneBarrier.wait(); + EXPECT_TRUE(m_errorReporting.getError().has_value()); + getDone.wait(); + }); + std::thread t2([&]() { + initDoneBarrier.wait(); + m_errorReporting.set("error 2", nullptr); + setDoneBarrier.wait(); + EXPECT_TRUE(m_errorReporting.getError().has_value()); + getDone.wait(); + }); + std::thread t3([&]() { + initDoneBarrier.wait(); + m_errorReporting.set("error 3", nullptr); + setDoneBarrier.wait(); + EXPECT_TRUE(m_errorReporting.getError().has_value()); + getDone.wait(); + }); + t1.join(); + t2.join(); + t3.join(); + } +} diff --git a/tests/unittests/framework/ramses-framework/FlagsTest.cpp b/tests/unittests/framework/ramses-framework/FlagsTest.cpp new file mode 100644 index 000000000..c1eb32357 --- /dev/null +++ b/tests/unittests/framework/ramses-framework/FlagsTest.cpp @@ -0,0 +1,131 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/framework/Flags.h" +#include "gmock/gmock.h" + +namespace ramses +{ + namespace internal + { + enum class ETestFlag : uint32_t; + } + template <> struct is_flag : std::true_type {}; +} + +namespace ramses::internal +{ + enum class ETestFlag : uint32_t + { + None = 0, Foo = 1, Bar = 2, Baz = 4, All = Foo|Bar|Baz + }; + + enum EUnrelated + { + EUnrelated_A = 1, EUnrelated_B = 2, EUnrelated_C = 4 + }; + + using TestFlags = Flags; + + TEST(AFlags, DefaultConstruct) + { + TestFlags flags; + + EXPECT_FALSE(flags.isSet(ETestFlag::Foo)); + EXPECT_FALSE(flags.isSet(ETestFlag::Bar)); + EXPECT_FALSE(flags.isSet(ETestFlag::Baz)); + EXPECT_FALSE(flags.isSet(ETestFlag::All)); + EXPECT_EQ(0u, flags.value()); + } + + TEST(AFlags, ConstructFromEnum) + { + TestFlags flags = ETestFlag::Foo; + + EXPECT_TRUE(flags.isSet(ETestFlag::Foo)); + EXPECT_FALSE(flags.isSet(ETestFlag::Bar)); + EXPECT_FALSE(flags.isSet(ETestFlag::Baz)); + EXPECT_FALSE(flags.isSet(ETestFlag::Baz | ETestFlag::Foo)); + EXPECT_FALSE(flags.isSet(ETestFlag::All)); + EXPECT_EQ(1u, flags.value()); + } + + TEST(AFlags, ConstructFromEnum2) + { + TestFlags flags = ETestFlag::Foo | ETestFlag::Bar; + + EXPECT_TRUE(flags.isSet(ETestFlag::None)); + EXPECT_TRUE(flags.isSet(ETestFlag::Foo)); + EXPECT_TRUE(flags.isSet(ETestFlag::Bar)); + EXPECT_TRUE(flags.isSet(ETestFlag::Bar | ETestFlag::Foo)); + EXPECT_FALSE(flags.isSet(ETestFlag::Baz)); + EXPECT_FALSE(flags.isSet(ETestFlag::All)); + EXPECT_EQ(3u, flags.value()); + } + + TEST(AFlags, ConstructFromEnum3) + { + TestFlags flags = ETestFlag::Foo | ETestFlag::Bar | ETestFlag::Baz; + + EXPECT_TRUE(flags.isSet(ETestFlag::None)); + EXPECT_TRUE(flags.isSet(ETestFlag::Foo)); + EXPECT_TRUE(flags.isSet(ETestFlag::Bar)); + EXPECT_TRUE(flags.isSet(ETestFlag::Baz)); + EXPECT_TRUE(flags.isSet(ETestFlag::Baz | ETestFlag::Foo | ETestFlag::Bar)); + EXPECT_TRUE(flags.isSet(ETestFlag::All)); + EXPECT_EQ(7u, flags.value()); + } + + TEST(AFlags, operators) + { + EXPECT_TRUE(ETestFlag::Foo == TestFlags(1)); + EXPECT_TRUE(ETestFlag::Foo != TestFlags(2)); + EXPECT_EQ(TestFlags(3), ETestFlag::Foo | ETestFlag::Bar); + EXPECT_EQ(TestFlags(3), ETestFlag::Foo | TestFlags(2)); + } + + TEST(AFlags, setFlag) + { + TestFlags flags; + flags.setFlag(ETestFlag::Foo, true); + EXPECT_EQ(TestFlags(ETestFlag::Foo), flags); + flags.setFlag(ETestFlag::Bar, true); + EXPECT_EQ(TestFlags(ETestFlag::Foo | ETestFlag::Bar), flags); + flags.setFlag(ETestFlag::Foo, false); + EXPECT_EQ(TestFlags(ETestFlag::Bar), flags); + flags.setFlag(ETestFlag::Bar, false); + EXPECT_EQ(TestFlags(), flags); + } + + TEST(AFlags, UnrelatedEnumDoesNotMatchOperators) + { + // verifies that unrelated enums do not instantiate global flag operators: + // Flags operator|(EUnrelated, EUnrelated) + const auto unrelated = EUnrelated(EUnrelated_A | EUnrelated_B); + EXPECT_EQ(unrelated, 3); + EXPECT_EQ(EUnrelated_A, EUnrelated_A); + EXPECT_NE(EUnrelated_B, EUnrelated_A); + } + + TEST(AFlags, ConvertFromInt) + { + TestFlags flags(1); + EXPECT_EQ(ETestFlag::Foo, flags); + } + + // enforce performance guarantees + static_assert(std::is_trivially_copyable::value, "expectation failed"); + static_assert(std::is_default_constructible::value, "expectation failed"); + static_assert(std::is_trivially_destructible::value, "expectation failed"); + + static_assert((ETestFlag::Foo | ETestFlag::Bar).value() == 3); + static_assert((ETestFlag::Foo | ETestFlag::Bar).isSet(ETestFlag::Foo)); + static_assert(!(ETestFlag::Baz | ETestFlag::Bar).isSet(ETestFlag::Foo)); + static_assert(sizeof(TestFlags) == sizeof(ETestFlag)); + static_assert(sizeof(TestFlags) == sizeof(std::underlying_type_t)); +} diff --git a/framework/ramses-framework/test/PublicRamshCommandTest.cpp b/tests/unittests/framework/ramses-framework/PublicRamshCommandTest.cpp similarity index 95% rename from framework/ramses-framework/test/PublicRamshCommandTest.cpp rename to tests/unittests/framework/ramses-framework/PublicRamshCommandTest.cpp index 1cab78a87..a085d1a94 100644 --- a/framework/ramses-framework/test/PublicRamshCommandTest.cpp +++ b/tests/unittests/framework/ramses-framework/PublicRamshCommandTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "PublicRamshCommand.h" +#include "impl/PublicRamshCommand.h" #include "ApiRamshCommandMock.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { using namespace ::testing; @@ -40,8 +40,8 @@ namespace ramses_internal { std::vector inp; - inp.push_back("asd"); - inp.push_back("def"); + inp.emplace_back("asd"); + inp.emplace_back("def"); EXPECT_CALL(*mock, execute(std::vector{"asd", "def"})).WillOnce(Return(true)); EXPECT_TRUE(cmd.executeInput(inp)); } diff --git a/tests/unittests/framework/ramses-framework/RamsesFrameworkConfigTest.cpp b/tests/unittests/framework/ramses-framework/RamsesFrameworkConfigTest.cpp new file mode 100644 index 000000000..7fd714123 --- /dev/null +++ b/tests/unittests/framework/ramses-framework/RamsesFrameworkConfigTest.cpp @@ -0,0 +1,212 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "impl/RamsesFrameworkConfigImpl.h" + +namespace ramses::internal +{ + class ARamsesFrameworkConfig : public testing::Test + { + protected: + RamsesFrameworkConfig frameworkConfig{EFeatureLevel_01}; + }; + + TEST_F(ARamsesFrameworkConfig, IsInitializedCorrectly) + { + EXPECT_EQ(EFeatureLevel_01, frameworkConfig.getFeatureLevel()); + EXPECT_EQ(ERamsesShellType::Default, frameworkConfig.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, FailsToIntializeFeatureLevel) + { + RamsesFrameworkConfig config{static_cast(99)}; + EXPECT_EQ(EFeatureLevel_01, config.getFeatureLevel()); + } + + TEST_F(ARamsesFrameworkConfig, CanBeCopyAndMoveConstructed) + { + frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); + + RamsesFrameworkConfig configCopy{frameworkConfig}; + EXPECT_EQ(ERamsesShellType::Console, configCopy.impl().m_shellType); + + RamsesFrameworkConfig configMove{std::move(frameworkConfig)}; + EXPECT_EQ(ERamsesShellType::Console, configMove.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, CanBeCopyAndMoveAssigned) + { + frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); + + RamsesFrameworkConfig configCopy{EFeatureLevel_Latest}; + configCopy = frameworkConfig; + EXPECT_EQ(ERamsesShellType::Console, configCopy.impl().m_shellType); + + RamsesFrameworkConfig configMove{EFeatureLevel_Latest}; + configMove = std::move(frameworkConfig); + EXPECT_EQ(ERamsesShellType::Console, configMove.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, CanBeSelfAssigned) + { + frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + frameworkConfig = frameworkConfig; + EXPECT_EQ(ERamsesShellType::Console, frameworkConfig.impl().m_shellType); + frameworkConfig = std::move(frameworkConfig); + // NOLINTNEXTLINE(bugprone-use-after-move) + EXPECT_EQ(ERamsesShellType::Console, frameworkConfig.impl().m_shellType); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + TEST_F(ARamsesFrameworkConfig, CanSetFeatureLevel) + { + EXPECT_TRUE(frameworkConfig.setFeatureLevel(EFeatureLevel_Latest)); + EXPECT_EQ(EFeatureLevel_Latest, frameworkConfig.getFeatureLevel()); + } + + TEST_F(ARamsesFrameworkConfig, FailsToSetUnsupportedFeatureLevel) + { + EXPECT_FALSE(frameworkConfig.setFeatureLevel(static_cast(99))); + EXPECT_EQ(EFeatureLevel_01, frameworkConfig.getFeatureLevel()); + } + + TEST_F(ARamsesFrameworkConfig, CanSetShellConsoleType) + { + frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Console); + EXPECT_EQ(ERamsesShellType::Console, frameworkConfig.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, CanSetShellTypeNone) + { + frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::None); + EXPECT_EQ(ERamsesShellType::None, frameworkConfig.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, CanSetShellTypeDefault) + { + frameworkConfig.setRequestedRamsesShellType(ERamsesShellType::Default); + EXPECT_EQ(ERamsesShellType::Default, frameworkConfig.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, TestSetandGetApplicationInformation) + { + const char* application_id = "myap"; + const char* application_description = "mydescription"; + + frameworkConfig.setDLTApplicationID(application_id); + frameworkConfig.setDLTApplicationDescription(application_description); + + EXPECT_EQ(application_id, frameworkConfig.getDLTApplicationID()); + EXPECT_EQ(application_description, frameworkConfig.getDLTApplicationDescription()); + } + + TEST_F(ARamsesFrameworkConfig, CanSetLogLevel) + { + EXPECT_FALSE(frameworkConfig.impl().loggerConfig.logLevel.has_value()); + frameworkConfig.setLogLevel(ELogLevel::Debug); + EXPECT_EQ(ELogLevel::Debug, frameworkConfig.impl().loggerConfig.logLevel); + frameworkConfig.setLogLevel(ELogLevel::Warn); + EXPECT_EQ(ELogLevel::Warn, frameworkConfig.impl().loggerConfig.logLevel); + } + + TEST_F(ARamsesFrameworkConfig, CanSetLogLevelContext) + { + EXPECT_TRUE(frameworkConfig.impl().loggerConfig.logLevelContexts.empty()); + frameworkConfig.setLogLevel("RRND", ELogLevel::Debug); + EXPECT_EQ(ELogLevel::Debug, frameworkConfig.impl().loggerConfig.logLevelContexts["RRND"]); + frameworkConfig.setLogLevel("RRND", ELogLevel::Error); + EXPECT_EQ(ELogLevel::Error, frameworkConfig.impl().loggerConfig.logLevelContexts["RRND"]); + } + + TEST_F(ARamsesFrameworkConfig, CanSetLogLevelConsole) + { + EXPECT_FALSE(frameworkConfig.impl().loggerConfig.logLevelConsole.has_value()); + frameworkConfig.setLogLevelConsole(ELogLevel::Debug); + EXPECT_EQ(ELogLevel::Debug, frameworkConfig.impl().loggerConfig.logLevelConsole); + } + + TEST_F(ARamsesFrameworkConfig, CanSetParticipantGuid) + { + EXPECT_EQ(Guid(), frameworkConfig.impl().getUserProvidedGuid()); + EXPECT_TRUE(frameworkConfig.setParticipantGuid(4400)); + EXPECT_EQ(Guid(4400), frameworkConfig.impl().getUserProvidedGuid()); + EXPECT_FALSE(frameworkConfig.setParticipantGuid(1)); + EXPECT_EQ(Guid(1), frameworkConfig.impl().getUserProvidedGuid()); + EXPECT_FALSE(frameworkConfig.setParticipantGuid(0)); + EXPECT_EQ(Guid(0), frameworkConfig.impl().getUserProvidedGuid()); + } + + TEST_F(ARamsesFrameworkConfig, CanSetParticipantName) + { + EXPECT_EQ("", frameworkConfig.impl().getParticipantName()); + EXPECT_TRUE(frameworkConfig.setParticipantName("foo")); + EXPECT_EQ("foo", frameworkConfig.impl().getParticipantName()); + EXPECT_TRUE(frameworkConfig.setParticipantName("foo/bar")); + EXPECT_EQ("foo/bar", frameworkConfig.impl().getParticipantName()); + } + + TEST_F(ARamsesFrameworkConfig, CanSetPeriodicLogInterval) + { + EXPECT_EQ(2u, frameworkConfig.impl().periodicLogTimeout); + EXPECT_TRUE(frameworkConfig.impl().m_periodicLogsEnabled); + frameworkConfig.setPeriodicLogInterval(std::chrono::seconds(0)); + EXPECT_EQ(0u, frameworkConfig.impl().periodicLogTimeout); + EXPECT_FALSE(frameworkConfig.impl().m_periodicLogsEnabled); + frameworkConfig.setPeriodicLogInterval(std::chrono::seconds(3)); + EXPECT_EQ(3u, frameworkConfig.impl().periodicLogTimeout); + EXPECT_TRUE(frameworkConfig.impl().m_periodicLogsEnabled); + } + + TEST_F(ARamsesFrameworkConfig, CanSetInterfaceSelectionSocket) + { + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getIPAddress(), "127.0.0.1"); + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getPort(), 0); + frameworkConfig.setInterfaceSelectionIPForTCPCommunication("192.168.1.1"); + frameworkConfig.setInterfaceSelectionPortForTCPCommunication(5543); + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getIPAddress(), "192.168.1.1"); + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getPort(), 5543); + } + + TEST_F(ARamsesFrameworkConfig, CanSetDaemonSocket) + { + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getDaemonIPAddress(), "127.0.0.1"); + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getDaemonPort(), 5999); + frameworkConfig.setDaemonIPForTCPCommunication("192.168.1.1"); + frameworkConfig.setDaemonPortForTCPCommunication(5543); + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getDaemonIPAddress(), "192.168.1.1"); + EXPECT_EQ(frameworkConfig.impl().m_tcpConfig.getDaemonPort(), 5543); + } + + TEST_F(ARamsesFrameworkConfig, CanSetConnectionSystem) + { + EXPECT_EQ(EConnectionProtocol::TCP, frameworkConfig.impl().getUsedProtocol()); + EXPECT_TRUE(frameworkConfig.setConnectionSystem(EConnectionSystem::Off)); + EXPECT_EQ(EConnectionProtocol::Off, frameworkConfig.impl().getUsedProtocol()); + EXPECT_TRUE(frameworkConfig.setConnectionSystem(EConnectionSystem::TCP)); + EXPECT_EQ(EConnectionProtocol::TCP, frameworkConfig.impl().getUsedProtocol()); + } + + TEST_F(ARamsesFrameworkConfig, CanSetTCPKeepAlive) + { + EXPECT_EQ(std::chrono::milliseconds(300), frameworkConfig.impl().m_tcpConfig.getAliveInterval()); + EXPECT_EQ(std::chrono::milliseconds(300 * 6), frameworkConfig.impl().m_tcpConfig.getAliveTimeout()); + frameworkConfig.setConnectionKeepaliveSettings(std::chrono::milliseconds(250), std::chrono::milliseconds(9000)); + EXPECT_EQ(std::chrono::milliseconds(250), frameworkConfig.impl().m_tcpConfig.getAliveInterval()); + EXPECT_EQ(std::chrono::milliseconds(9000), frameworkConfig.impl().m_tcpConfig.getAliveTimeout()); + } +} diff --git a/framework/ramses-framework/test/RamsesFrameworkTest.cpp b/tests/unittests/framework/ramses-framework/RamsesFrameworkTest.cpp similarity index 60% rename from framework/ramses-framework/test/RamsesFrameworkTest.cpp rename to tests/unittests/framework/ramses-framework/RamsesFrameworkTest.cpp index 4b96a9f72..0eb8500af 100644 --- a/framework/ramses-framework/test/RamsesFrameworkTest.cpp +++ b/tests/unittests/framework/ramses-framework/RamsesFrameworkTest.cpp @@ -6,22 +6,26 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- +#include + #include "gmock/gmock.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "RamsesFrameworkImpl.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "impl/RamsesFrameworkImpl.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "ApiRamshCommandMock.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "Utils/LogMacros.h" -using namespace ramses; using namespace testing; +namespace ramses::internal +{ TEST(ARamsesFramework, canDefaultConstruct) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; - EXPECT_GT(fw.m_impl.getParticipantAddress().getParticipantId().get(), 0xFF); + EXPECT_GT(fw.impl().getParticipantAddress().getParticipantId().get(), 0xFF); } TEST(ARamsesFramework, canConstructFromConfig) @@ -29,7 +33,7 @@ TEST(ARamsesFramework, canConstructFromConfig) RamsesFrameworkConfig config{EFeatureLevel_Latest}; config.setParticipantGuid(0x123); RamsesFramework fw{config}; - EXPECT_EQ(fw.m_impl.getParticipantAddress().getParticipantId().get(), 0x123); + EXPECT_EQ(fw.impl().getParticipantAddress().getParticipantId().get(), 0x123); } TEST(ARamsesFramework, isNotConnectedInitially) @@ -44,9 +48,9 @@ TEST(ARamsesFramework, connectLifeCycleOK) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; - EXPECT_EQ(ramses::StatusOK, fw.connect()); + EXPECT_TRUE(fw.connect()); EXPECT_TRUE(fw.isConnected()); - EXPECT_EQ(ramses::StatusOK, fw.disconnect()); + EXPECT_TRUE(fw.disconnect()); } TEST(ARamsesFramework, reportsErrorWhenConnectingSecondTime) @@ -54,7 +58,7 @@ TEST(ARamsesFramework, reportsErrorWhenConnectingSecondTime) RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; fw.connect(); - EXPECT_NE(ramses::StatusOK, fw.connect()); + EXPECT_FALSE(fw.connect()); EXPECT_TRUE(fw.isConnected()); } @@ -63,24 +67,22 @@ TEST(ARamsesFramework, reportsErrorWhenDisconnectingSecondTime) RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; fw.connect(); - EXPECT_EQ(ramses::StatusOK, fw.disconnect()); - EXPECT_NE(ramses::StatusOK, fw.disconnect()); + EXPECT_TRUE(fw.disconnect()); + EXPECT_FALSE(fw.disconnect()); } -namespace -{ - class PartialApiRamshCommandMock : public ramses_internal::ApiRamshCommandMock + class PartialApiRamshCommandMock : public ApiRamshCommandMock { public: - explicit PartialApiRamshCommandMock(const std::string& kw_) - : kw(kw_) + explicit PartialApiRamshCommandMock(std::string kw_) + : kw(std::move(kw_)) {} [[nodiscard]] const std::string& keyword() const override { return kw; } [[nodiscard]] const std::string& help() const override { return helpText; } std::string kw; std::string helpText{"text"}; }; -} + TEST(ARamsesFramework, canAddAndTriggerRamshCommands) { @@ -89,47 +91,47 @@ TEST(ARamsesFramework, canAddAndTriggerRamshCommands) RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; - EXPECT_EQ(StatusOK, fw.addRamshCommand(cmd_a)); - EXPECT_EQ(StatusOK, fw.addRamshCommand(cmd_b)); + EXPECT_TRUE(fw.addRamshCommand(cmd_a)); + EXPECT_TRUE(fw.addRamshCommand(cmd_b)); std::string inp_a = "foo"; EXPECT_CALL(*cmd_a, execute(std::vector{"foo"})).WillOnce(Return(true)); - EXPECT_EQ(StatusOK, fw.executeRamshCommand(inp_a)); + EXPECT_TRUE(fw.executeRamshCommand(inp_a)); std::string inp_b = "bar baz"; EXPECT_CALL(*cmd_b, execute(std::vector{"bar", "baz"})).WillOnce(Return(true)); - EXPECT_EQ(StatusOK, fw.executeRamshCommand(inp_b)); + EXPECT_TRUE(fw.executeRamshCommand(inp_b)); cmd_a.reset(); - EXPECT_NE(StatusOK, fw.executeRamshCommand(inp_a)); + EXPECT_FALSE(fw.executeRamshCommand(inp_a)); } TEST(ARamsesFramwork, canExecuteExistingAndFailingInvalidRamshCommands) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; - EXPECT_EQ(StatusOK, fw.executeRamshCommand("help")); - EXPECT_EQ(StatusOK, fw.executeRamshCommand("setLogLevelConsole trace")); - EXPECT_NE(StatusOK, fw.executeRamshCommand("invalid ramsh command")); - EXPECT_NE(StatusOK, fw.executeRamshCommand("")); + EXPECT_TRUE(fw.executeRamshCommand("help")); + EXPECT_TRUE(fw.executeRamshCommand("setLogLevelConsole trace")); + EXPECT_FALSE(fw.executeRamshCommand("invalid ramsh command")); + EXPECT_FALSE(fw.executeRamshCommand("")); } TEST(ARamsesFramework, failsToAddInvalidRamshCommands) { RamsesFrameworkConfig config{EFeatureLevel_Latest}; RamsesFramework fw{config}; - EXPECT_NE(StatusOK, fw.addRamshCommand({})); - EXPECT_NE(StatusOK, fw.addRamshCommand(std::make_shared>(""))); - EXPECT_NE(StatusOK, fw.addRamshCommand(std::make_shared>("a b"))); - EXPECT_NE(StatusOK, fw.addRamshCommand(std::make_shared>("a\"b"))); - EXPECT_NE(StatusOK, fw.addRamshCommand(std::make_shared>("help"))); + EXPECT_FALSE(fw.addRamshCommand({})); + EXPECT_FALSE(fw.addRamshCommand(std::make_shared>(""))); + EXPECT_FALSE(fw.addRamshCommand(std::make_shared>("a b"))); + EXPECT_FALSE(fw.addRamshCommand(std::make_shared>("a\"b"))); + EXPECT_FALSE(fw.addRamshCommand(std::make_shared>("help"))); } TEST(ARamsesFramework, SetLogHandler) { bool loggerCalled {false}; - RamsesFramework::SetLogHandler([&loggerCalled](auto level, auto& context, auto& message) + RamsesFramework::SetLogHandler([&loggerCalled](auto level, auto context, auto message) { if (level == ELogLevel::Warn && context == "RFRA" && message == "SetLogHandlerTest") { @@ -149,14 +151,14 @@ TEST(ARamsesFramework, SetLogHandler) TEST(ARamsesFramework, CanSetAndResetLogHandlerMultipleTimes) { - RamsesFramework::SetLogHandler([](auto /*level*/, auto& /*context*/, auto& /*message*/) {}); + RamsesFramework::SetLogHandler([](auto /*level*/, auto /*context*/, auto /*message*/) {}); RamsesFramework::SetLogHandler(nullptr); - RamsesFramework::SetLogHandler([](auto /*level*/, auto& /*context*/, auto& /*message*/) {}); + RamsesFramework::SetLogHandler([](auto /*level*/, auto /*context*/, auto /*message*/) {}); RamsesFramework::SetLogHandler(nullptr); - RamsesFramework::SetLogHandler([](auto /*level*/, auto& /*context*/, auto& /*message*/) {}); - RamsesFramework::SetLogHandler([](auto /*level*/, auto& /*context*/, auto& /*message*/) {}); + RamsesFramework::SetLogHandler([](auto /*level*/, auto /*context*/, auto /*message*/) {}); + RamsesFramework::SetLogHandler([](auto /*level*/, auto /*context*/, auto /*message*/) {}); RamsesFramework::SetLogHandler(nullptr); RamsesFramework::SetLogHandler(nullptr); } @@ -164,15 +166,23 @@ TEST(ARamsesFramework, CanSetAndResetLogHandlerMultipleTimes) TEST(ARamsesFramework, multipleInstancesCanBeCreatedInParallel) { // check stability of sensitive framework components (e.g. logger singleton) when used in multiple instances in parallel + const size_t numThreads = 32u; + ramses::internal::ThreadBarrier initBarrier(numThreads); + ramses::internal::ThreadBarrier deinitBarrier(numThreads); + std::vector threads; - threads.reserve(100); - for (size_t t = 0u; t < threads.capacity(); ++t) - threads.emplace_back([]() { - RamsesFrameworkConfig config{EFeatureLevel_Latest}; - RamsesFramework fw{config}; + threads.reserve(numThreads); + for (size_t t = 0u; t < numThreads; ++t) + { + threads.emplace_back([&]() { + RamsesFrameworkConfig config{ EFeatureLevel_Latest }; + initBarrier.wait(); + RamsesFramework fw{ config }; std::this_thread::sleep_for(std::chrono::milliseconds{ 10 }); - }); - + deinitBarrier.wait(); + }); + } for (auto& t : threads) t.join(); } +} diff --git a/framework/ramses-framework/test/RamsesVersionTest.cpp b/tests/unittests/framework/ramses-framework/RamsesVersionTest.cpp similarity index 93% rename from framework/ramses-framework/test/RamsesVersionTest.cpp rename to tests/unittests/framework/ramses-framework/RamsesVersionTest.cpp index df3cd1a03..45d4bff58 100644 --- a/framework/ramses-framework/test/RamsesVersionTest.cpp +++ b/tests/unittests/framework/ramses-framework/RamsesVersionTest.cpp @@ -6,11 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-framework-api/RamsesVersion.h" +#include "ramses/framework/RamsesVersion.h" #include "ramses-sdk-build-config.h" #include "gtest/gtest.h" -namespace ramses +namespace ramses::internal { TEST(ARamsesVersion, hasExpectedValues) { diff --git a/framework/ramses-framework/test/StronglyTypedValueTest.cpp b/tests/unittests/framework/ramses-framework/StronglyTypedValueTest.cpp similarity index 91% rename from framework/ramses-framework/test/StronglyTypedValueTest.cpp rename to tests/unittests/framework/ramses-framework/StronglyTypedValueTest.cpp index 994c74463..25c0e362f 100644 --- a/framework/ramses-framework/test/StronglyTypedValueTest.cpp +++ b/tests/unittests/framework/ramses-framework/StronglyTypedValueTest.cpp @@ -6,20 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-framework-api/StronglyTypedValue.h" +#include "ramses/framework/StronglyTypedValue.h" #include "UnsafeTestMemoryHelpers.h" #include "gmock/gmock.h" #include #include -namespace ramses +namespace ramses::internal { namespace { struct UInt32Tag {}; - using StronglyTypedUInt32 = StronglyTypedValue; + using StronglyTypedUInt32 = ramses::StronglyTypedValue; - using StronglyTypedPtr = StronglyTypedValue; + using StronglyTypedPtr = ramses::StronglyTypedValue; } TEST(AStronglyTypedValue, CanBeCreatedWithDefaultValue) @@ -159,8 +159,8 @@ namespace ramses EXPECT_TRUE(StronglyTypedUInt32(123).isValid()); EXPECT_TRUE(StronglyTypedUInt32(0).isValid()); - EXPECT_TRUE(StronglyTypedPtr(ramses_internal::UnsafeTestMemoryHelpers::ForgeArbitraryPointer(123u)).isValid()); - EXPECT_TRUE(StronglyTypedPtr(ramses_internal::UnsafeTestMemoryHelpers::ForgeArbitraryPointer(1u)).isValid()); + EXPECT_TRUE(StronglyTypedPtr(UnsafeTestMemoryHelpers::ForgeArbitraryPointer(123u)).isValid()); + EXPECT_TRUE(StronglyTypedPtr(UnsafeTestMemoryHelpers::ForgeArbitraryPointer(1u)).isValid()); } // enforce performance guarantees diff --git a/tests/unittests/framework/ramses-framework/ValidationReportTest.cpp b/tests/unittests/framework/ramses-framework/ValidationReportTest.cpp new file mode 100644 index 000000000..f64c30dd8 --- /dev/null +++ b/tests/unittests/framework/ramses-framework/ValidationReportTest.cpp @@ -0,0 +1,153 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" +#include "ramses/framework/ValidationReport.h" +#include "impl/ValidationReportImpl.h" + +namespace ramses::internal +{ + class AValidationReport : public testing::Test + { + protected: + ValidationReport report; + }; + + TEST_F(AValidationReport, IsInitializedEmpty) + { + EXPECT_FALSE(report.hasError()); + EXPECT_FALSE(report.hasIssue()); + EXPECT_TRUE(report.getIssues().empty()); + } + + TEST_F(AValidationReport, DistinguishesErrorsAndWarnings) + { + const Issue warn = {EIssueType::Warning, "fooWarn", nullptr}; + const Issue err = {EIssueType::Error, "fooErr", nullptr}; + + report.impl().add(warn.type, warn.message, warn.object); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + EXPECT_EQ(1u, report.getIssues().size()); + + report.impl().add(warn.type, warn.message, warn.object); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + EXPECT_EQ(2u, report.getIssues().size()); + + report.impl().add(err.type, err.message, err.object); + EXPECT_TRUE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + EXPECT_EQ(3u, report.getIssues().size()); + } + + TEST_F(AValidationReport, addVisit) + { + const auto* obj1 = reinterpret_cast(1); + const auto* obj2 = reinterpret_cast(2); + + EXPECT_TRUE(report.impl().addVisit(obj1)); + EXPECT_FALSE(report.impl().addVisit(obj1)); + EXPECT_TRUE(report.impl().addVisit(obj2)); + EXPECT_FALSE(report.impl().addVisit(obj2)); + + report.clear(); + EXPECT_TRUE(report.impl().addVisit(obj1)); + EXPECT_TRUE(report.impl().addVisit(obj2)); + EXPECT_FALSE(report.impl().addVisit(obj1)); + EXPECT_FALSE(report.impl().addVisit(obj2)); + } + + TEST_F(AValidationReport, dependentObjects) + { + const auto* obj1 = reinterpret_cast(1); + const auto* obj2 = reinterpret_cast(2); + const auto* obj3 = reinterpret_cast(3); + + report.impl().addDependentObject(*obj1, *obj2); + report.impl().addDependentObject(*obj1, *obj3); + report.impl().addDependentObject(*obj2, *obj3); + + EXPECT_EQ(2u, report.impl().getDependentObjects(obj1).size()); + EXPECT_EQ(1u, report.impl().getDependentObjects(obj2).size()); + EXPECT_TRUE(report.impl().getDependentObjects(obj3).empty()); + EXPECT_TRUE(report.impl().getDependentObjects(nullptr).empty()); + } + + TEST_F(AValidationReport, clear) + { + const auto* obj1 = reinterpret_cast(1); + const auto* obj2 = reinterpret_cast(2); + const Issue issue1 = {EIssueType::Warning, "foo1", nullptr}; + const Issue issue2 = {EIssueType::Warning, "foo2", nullptr}; + + report.impl().add(issue1.type, issue1.message, issue1.object); + report.impl().addDependentObject(*obj1, *obj2); + report.impl().add(issue2.type, issue2.message, issue2.object); + + EXPECT_EQ(2u, report.getIssues().size()); + EXPECT_EQ(1u, report.impl().getDependentObjects(obj1).size()); + + report.clear(); + EXPECT_TRUE(report.getIssues().empty()); + EXPECT_TRUE(report.impl().getDependentObjects(obj1).empty()); + } + + TEST_F(AValidationReport, CanBeCopyAndMoveConstructed) + { + const Issue issue = {EIssueType::Warning, "foo", nullptr}; + report.impl().add(issue.type, issue.message, issue.object); + + ValidationReport reportCopy{report}; + ASSERT_TRUE(reportCopy.hasIssue()); + EXPECT_EQ(reportCopy.getIssues().back(), issue); + + ValidationReport reportMove{std::move(report)}; + ASSERT_TRUE(reportMove.hasIssue()); + EXPECT_EQ(reportMove.getIssues().back(), issue); + } + + TEST_F(AValidationReport, CanBeCopyAndMoveAssigned) + { + const Issue issue = {EIssueType::Warning, "foo", nullptr}; + report.impl().add(issue.type, issue.message, issue.object); + + ValidationReport reportCopy; + reportCopy = report; + ASSERT_TRUE(reportCopy.hasIssue()); + EXPECT_EQ(reportCopy.getIssues().back(), issue); + + ValidationReport reportMove; + reportMove = std::move(report); + ASSERT_TRUE(reportMove.hasIssue()); + EXPECT_EQ(reportMove.getIssues().back(), issue); + } + + TEST_F(AValidationReport, CanBeSelfAssigned) + { + const Issue issue = {EIssueType::Warning, "foo", nullptr}; + report.impl().add(issue.type, issue.message, issue.object); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + report = report; + ASSERT_TRUE(report.hasIssue()); + EXPECT_EQ(report.getIssues().back(), issue); + report = std::move(report); + // NOLINTNEXTLINE(bugprone-use-after-move) + ASSERT_TRUE(report.hasIssue()); + // NOLINTNEXTLINE(bugprone-use-after-move) + EXPECT_EQ(report.getIssues().back(), issue); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } +} diff --git a/framework/test/res/sampleImage.png b/tests/unittests/framework/res/sampleImage.png similarity index 100% rename from framework/test/res/sampleImage.png rename to tests/unittests/framework/res/sampleImage.png diff --git a/tests/unittests/ramses-cli/CMakeLists.txt b/tests/unittests/ramses-cli/CMakeLists.txt new file mode 100644 index 000000000..40afb4408 --- /dev/null +++ b/tests/unittests/ramses-cli/CMakeLists.txt @@ -0,0 +1,21 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME ramses-cli-test + TYPE BINARY + SRC_FILES *.cpp + DEPENDENCIES ramses-cli + ramses-framework + ramses-renderer + ramses-gmock-main +) + +makeTestFromTarget( + TARGET ramses-cli-test + SUFFIX UNITTEST) diff --git a/tests/unittests/ramses-cli/ramses-cli-test.cpp b/tests/unittests/ramses-cli/ramses-cli-test.cpp new file mode 100644 index 000000000..16d8904c5 --- /dev/null +++ b/tests/unittests/ramses-cli/ramses-cli-test.cpp @@ -0,0 +1,335 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- +#include "gmock/gmock.h" +#include "ramses-cli.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "impl/DisplayConfigImpl.h" +#include "impl/RendererConfigImpl.h" + +namespace ramses::internal +{ + class ARamsesFrameworkConfig : public testing::Test + { + public: + ARamsesFrameworkConfig() + { + ramses::registerOptions(cli, config); + } + + protected: + CLI::App cli; + RamsesFrameworkConfig config{EFeatureLevel_Latest}; + }; + + class ARendererConfig : public testing::Test + { + public: + ARendererConfig() + { + ramses::registerOptions(cli, config); + } + + protected: + CLI::App cli; + ramses::RendererConfig config; + }; + + class ADisplayConfig : public testing::Test + { + public: + ADisplayConfig() + { + ramses::registerOptions(cli, config); + } + + protected: + CLI::App cli; + ramses::DisplayConfig config; + }; + + + TEST_F(ARamsesFrameworkConfig, cliConnection) + { + EXPECT_EQ(EConnectionProtocol::TCP, config.impl().getUsedProtocol()); + EXPECT_THROW(cli.parse(std::vector{"--connection"}), CLI::ParseError); + cli.parse(std::vector{"--connection=off"}); + EXPECT_EQ(EConnectionProtocol::Off, config.impl().getUsedProtocol()); + cli.parse(std::vector{"--connection=tcp"}); + EXPECT_EQ(EConnectionProtocol::TCP, config.impl().getUsedProtocol()); + } + + TEST_F(ARamsesFrameworkConfig, cliRamsh) + { + EXPECT_EQ(ERamsesShellType::Default, config.impl().m_shellType); + cli.parse(std::vector{"--ramsh"}); + EXPECT_EQ(ERamsesShellType::Console, config.impl().m_shellType); + cli.parse(std::vector{"--no-ramsh"}); + EXPECT_EQ(ERamsesShellType::None, config.impl().m_shellType); + } + + TEST_F(ARamsesFrameworkConfig, cliGuid) + { + EXPECT_EQ(Guid(), config.impl().getUserProvidedGuid()); + EXPECT_THROW(cli.parse(std::vector{"--guid"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--guid=foo"}), CLI::ParseError); + cli.parse(std::vector{"--guid=266"}); + EXPECT_EQ(Guid(266), config.impl().getUserProvidedGuid()); + } + + TEST_F(ARamsesFrameworkConfig, cliIp) + { + EXPECT_EQ("127.0.0.1", config.impl().m_tcpConfig.getIPAddress()); + EXPECT_THROW(cli.parse(std::vector{"--ip"}), CLI::ParseError); + cli.parse(std::vector{"--ip=localhost"}); + EXPECT_EQ("localhost", config.impl().m_tcpConfig.getIPAddress()); + } + + TEST_F(ARamsesFrameworkConfig, cliPort) + { + EXPECT_EQ(0u, config.impl().m_tcpConfig.getPort()); + EXPECT_THROW(cli.parse(std::vector{"--port"}), CLI::ParseError); + cli.parse(std::vector{"--port=8937"}); + EXPECT_EQ(8937u, config.impl().m_tcpConfig.getPort()); + } + + TEST_F(ARamsesFrameworkConfig, cliDaemonIp) + { + EXPECT_EQ("127.0.0.1", config.impl().m_tcpConfig.getDaemonIPAddress()); + EXPECT_THROW(cli.parse(std::vector{"--daemon-ip"}), CLI::ParseError); + cli.parse(std::vector{"--daemon-ip=localhost"}); + EXPECT_EQ("localhost", config.impl().m_tcpConfig.getDaemonIPAddress()); + } + + TEST_F(ARamsesFrameworkConfig, cliDaemonPort) + { + EXPECT_EQ(5999u, config.impl().m_tcpConfig.getDaemonPort()); + EXPECT_THROW(cli.parse(std::vector{"--daemon-port"}), CLI::ParseError); + cli.parse(std::vector{"--daemon-port=8937"}); + EXPECT_EQ(8937u, config.impl().m_tcpConfig.getDaemonPort()); + } + + TEST_F(ARamsesFrameworkConfig, cliTcpAlive) + { + EXPECT_EQ(std::chrono::milliseconds(300u), config.impl().m_tcpConfig.getAliveInterval()); + EXPECT_THROW(cli.parse(std::vector{"--tcp-alive"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--tcp-alive=500"}), CLI::ParseError); + cli.parse("--tcp-alive=500 5000"); + EXPECT_EQ(std::chrono::milliseconds(500u), config.impl().m_tcpConfig.getAliveInterval()); + EXPECT_EQ(std::chrono::milliseconds(5000u), config.impl().m_tcpConfig.getAliveTimeout()); + } + + TEST_F(ARamsesFrameworkConfig, cliPeriodicLogTimeout) + { + EXPECT_EQ(2u, config.impl().periodicLogTimeout); + EXPECT_THROW(cli.parse(std::vector{"--logp"}), CLI::ParseError); + cli.parse(std::vector{"--logp=27"}); + EXPECT_EQ(27u, config.impl().periodicLogTimeout); + EXPECT_TRUE(config.impl().m_periodicLogsEnabled); + cli.parse(std::vector{"--logp=0"}); + EXPECT_EQ(0u, config.impl().periodicLogTimeout); + EXPECT_FALSE(config.impl().m_periodicLogsEnabled); + } + + TEST_F(ARamsesFrameworkConfig, cliLogLevel) + { + EXPECT_THROW(cli.parse(std::vector{"--log-level"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--log-level=foo"}), CLI::ParseError); + cli.parse(std::vector{"--log-level=trace"}); + EXPECT_EQ(ELogLevel::Trace, config.impl().loggerConfig.logLevel.value()); + cli.parse(std::vector{"--log-level=1"}); + EXPECT_EQ(ELogLevel::Fatal, config.impl().loggerConfig.logLevel.value()); + cli.parse(std::vector{"-linfo"}); + EXPECT_EQ(ELogLevel::Info, config.impl().loggerConfig.logLevel.value()); + cli.parse(std::vector{"-l0"}); + EXPECT_EQ(ELogLevel::Off, config.impl().loggerConfig.logLevel.value()); + } + + TEST_F(ARamsesFrameworkConfig, cliLogLevelConsole) + { + EXPECT_THROW(cli.parse(std::vector{"--log-level-console"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--log-level-console=foo"}), CLI::ParseError); + cli.parse(std::vector{"--log-level-console=trace"}); + EXPECT_EQ(ELogLevel::Trace, config.impl().loggerConfig.logLevelConsole.value()); + cli.parse(std::vector{"--log-level-console=1"}); + EXPECT_EQ(ELogLevel::Fatal, config.impl().loggerConfig.logLevelConsole.value()); + } + + TEST_F(ARamsesFrameworkConfig, cliLogContexts) + { + EXPECT_THROW(cli.parse(std::vector{"--log-context"}), CLI::ParseError); + EXPECT_THAT([&]() { cli.parse(std::vector{"--log-context=badformat"}); }, + testing::ThrowsMessage(testing::HasSubstr("':' missing. Expected: CONTEXT:LOGLEVEL"))); + EXPECT_THROW(cli.parse(std::vector{"--log-context=ctx:42"}), CLI::ParseError); + cli.parse("--log-context=ctx1:trace"); + EXPECT_EQ(ELogLevel::Trace, config.impl().loggerConfig.logLevelContexts["ctx1"]); + cli.parse("--log-context=ctx1:trace ctx1:debug ctx2:info"); + EXPECT_EQ(ELogLevel::Debug, config.impl().loggerConfig.logLevelContexts["ctx1"]); + EXPECT_EQ(ELogLevel::Info, config.impl().loggerConfig.logLevelContexts["ctx2"]); + cli.parse("--log-context=ctx1:0 ctx2:FaTAL"); + EXPECT_EQ(ELogLevel::Off, config.impl().loggerConfig.logLevelContexts["ctx1"]); + EXPECT_EQ(ELogLevel::Fatal, config.impl().loggerConfig.logLevelContexts["ctx2"]); + cli.parse("--log-context=ctx1:trace --log-context ctx2:info"); + EXPECT_EQ(ELogLevel::Trace, config.impl().loggerConfig.logLevelContexts["ctx1"]); + EXPECT_EQ(ELogLevel::Info, config.impl().loggerConfig.logLevelContexts["ctx2"]); + } + + TEST_F(ARamsesFrameworkConfig, cliDltAppId) + { + EXPECT_EQ("RAMS", config.impl().getDLTApplicationID()); + EXPECT_THROW(cli.parse(std::vector{"--dlt-app-id"}), CLI::ParseError); + cli.parse(std::vector{"--dlt-app-id=RROO"}); + EXPECT_EQ("RROO", config.impl().getDLTApplicationID()); + } + + TEST_F(ARamsesFrameworkConfig, cliDltAppDescription) + { + EXPECT_THROW(cli.parse(std::vector{"--dlt-app-description"}), CLI::ParseError); + cli.parse(std::vector{"--dlt-app-description=foo"}); + EXPECT_EQ("foo", config.impl().getDLTApplicationDescription()); + } + + TEST_F(ARamsesFrameworkConfig, cliParticipantName) + { + EXPECT_EQ("", config.impl().getParticipantName()); + cli.parse("foo", true); + EXPECT_EQ("foo", config.impl().getParticipantName()); + cli.parse("path/to/bar", true); + EXPECT_EQ("bar", config.impl().getParticipantName()); + cli.parse(R"(c:\path\to\foobar.exe)", true); + EXPECT_EQ("foobar.exe", config.impl().getParticipantName()); + } + + TEST_F(ADisplayConfig, cliDeviceType) + { + cli.parse(std::vector{"--device-type=gl42"}); + EXPECT_EQ(ramses::EDeviceType::GL_4_2, config.getDeviceType()); + + cli.parse(std::vector{"--device-type=GL45"}); + EXPECT_EQ(ramses::EDeviceType::GL_4_5, config.getDeviceType()); + + cli.parse(std::vector{"--device-type=GLES_3_0"}); + EXPECT_EQ(ramses::EDeviceType::GLES_3_0, config.getDeviceType()); + } + + TEST_F(ADisplayConfig, cliWindowType) + { + cli.parse(std::vector{"--window-type=windows"}); + EXPECT_EQ(ramses::EWindowType::Windows, config.getWindowType()); + + cli.parse(std::vector{"--window-type=WAYLAND-IVI"}); + EXPECT_EQ(ramses::EWindowType::Wayland_IVI, config.getWindowType()); + } + + TEST_F(ADisplayConfig, cliWindowRectangle) + { + cli.parse(std::vector{"--xpos=5", "--ypos=10", "--width=420", "--height=998"}); + int32_t x = 0u; + int32_t y = 0u; + uint32_t w = 0u; + uint32_t h = 0u; + EXPECT_TRUE(config.impl().getWindowRectangle(x, y, w, h)); + EXPECT_EQ(5, x); + EXPECT_EQ(10, y); + EXPECT_EQ(420u, w); + EXPECT_EQ(998u, h); + } + + TEST_F(ADisplayConfig, cliFullscreen) + { + cli.parse(std::vector{"--fullscreen"}); + EXPECT_TRUE(config.isWindowFullscreen()); + config.setWindowFullscreen(false); + cli.parse(std::vector{"-f"}); + EXPECT_TRUE(config.isWindowFullscreen()); + } + + TEST_F(ADisplayConfig, cliResizable) + { + cli.parse(std::vector{"--resizable"}); + EXPECT_TRUE(config.impl().getInternalDisplayConfig().isResizable()); + } + + TEST_F(ADisplayConfig, cliMsaa) + { + EXPECT_THROW(cli.parse(std::vector{"--msaa"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--msaa=7"}), CLI::ParseError); + EXPECT_EQ(1u, config.impl().getInternalDisplayConfig().getAntialiasingSampleCount()); + cli.parse(std::vector{"--msaa=2"}); + EXPECT_EQ(2u, config.impl().getInternalDisplayConfig().getAntialiasingSampleCount()); + cli.parse(std::vector{"--msaa=4"}); + EXPECT_EQ(4u, config.impl().getInternalDisplayConfig().getAntialiasingSampleCount()); + cli.parse(std::vector{"--msaa=8"}); + EXPECT_EQ(8u, config.impl().getInternalDisplayConfig().getAntialiasingSampleCount()); + } + + TEST_F(ADisplayConfig, cliIviVisible) + { + EXPECT_FALSE(config.impl().getInternalDisplayConfig().getStartVisibleIvi()); + cli.parse(std::vector{"--ivi-visible"}); + EXPECT_TRUE(config.impl().getInternalDisplayConfig().getStartVisibleIvi()); + } + + TEST_F(ADisplayConfig, cliIviLayer) + { + EXPECT_THROW(cli.parse(std::vector{"--ivi-layer"}), CLI::ParseError); + cli.parse(std::vector{"--ivi-layer=42"}); + EXPECT_EQ(42u, config.impl().getInternalDisplayConfig().getWaylandIviLayerID().getValue()); + } + + TEST_F(ADisplayConfig, cliIviSurface) + { + EXPECT_THROW(cli.parse(std::vector{"--ivi-surface"}), CLI::ParseError); + cli.parse(std::vector{"--ivi-surface=78"}); + EXPECT_EQ(78u, config.impl().getInternalDisplayConfig().getWaylandIviSurfaceID().getValue()); + } + + TEST_F(ADisplayConfig, cliClear) + { + EXPECT_THROW(cli.parse(std::vector{"--clear"}), CLI::ParseError); + cli.parse(std::vector{"--clear=1,0,0.4,0.7"}); + EXPECT_FLOAT_EQ(1.f, config.impl().getInternalDisplayConfig().getClearColor()[0]); + EXPECT_FLOAT_EQ(0.f, config.impl().getInternalDisplayConfig().getClearColor()[1]); + EXPECT_FLOAT_EQ(0.4f, config.impl().getInternalDisplayConfig().getClearColor()[2]); + EXPECT_FLOAT_EQ(0.7f, config.impl().getInternalDisplayConfig().getClearColor()[3]); + } + + TEST_F(ADisplayConfig, cliThrowsErrorsForExtras) + { + EXPECT_THROW(cli.parse(std::vector{"--foo"}), CLI::ExtrasError); + } + + TEST_F(ADisplayConfig, cliEcDisplay) + { + EXPECT_THROW(cli.parse(std::vector{"--ec-display"}), CLI::ParseError); + cli.parse(std::vector{"--ec-display=wse"}); + EXPECT_EQ("wse", config.getWaylandEmbeddedCompositingSocketName()); + } + + TEST_F(ADisplayConfig, cliEcGroup) + { + EXPECT_THROW(cli.parse(std::vector{"--ec-socket-group"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--ec-socket-group=grp"}), CLI::RequiresError); + cli.parse(std::vector{"--ec-socket-group=grp", "--ec-display=wse"}); + EXPECT_EQ("grp", config.impl().getWaylandSocketEmbeddedGroup()); + } + + TEST_F(ADisplayConfig, cliEcPermissions) + { + EXPECT_THROW(cli.parse(std::vector{"--ec-socket-permissions"}), CLI::ParseError); + EXPECT_THROW(cli.parse(std::vector{"--ec-socket-permissions=0744"}), CLI::RequiresError); + cli.parse(std::vector{"--ec-socket-permissions=0744", "--ec-display=wse"}); + EXPECT_EQ(0744u, config.impl().getWaylandSocketEmbeddedPermissions()); + } + + TEST_F(ARendererConfig, cliIviControl) + { + EXPECT_FALSE(config.impl().getInternalRendererConfig().getSystemCompositorControlEnabled()); + cli.parse(std::vector{"--ivi-control"}); + EXPECT_TRUE(config.impl().getInternalRendererConfig().getSystemCompositorControlEnabled()); + } +} diff --git a/tests/unittests/renderer/CMakeLists.txt b/tests/unittests/renderer/CMakeLists.txt new file mode 100644 index 000000000..e108315e3 --- /dev/null +++ b/tests/unittests/renderer/CMakeLists.txt @@ -0,0 +1,34 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +add_subdirectory(renderer-test-common) +add_subdirectory(renderer-lib) +add_subdirectory(ramses-renderer) + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WINDOWS) + add_subdirectory(window-windows) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_X11) + add_subdirectory(window-x11) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI OR ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) + add_subdirectory(wayland-test-utils) + add_subdirectory(window-wayland-common) + add_subdirectory(embedded-compositor-wayland) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_WL_SHELL) + add_subdirectory(window-wayland-wl-shell) +endif() + +if(ramses-sdk_ENABLE_WINDOW_TYPE_WAYLAND_IVI) + add_subdirectory(system-compositor-controller-wayland) + add_subdirectory(window-wayland-ivi) +endif() diff --git a/tests/unittests/renderer/embedded-compositor-wayland/CMakeLists.txt b/tests/unittests/renderer/embedded-compositor-wayland/CMakeLists.txt new file mode 100644 index 000000000..51b0491ed --- /dev/null +++ b/tests/unittests/renderer/embedded-compositor-wayland/CMakeLists.txt @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME embedded-compositor-wayland-test + TYPE BINARY + ENABLE_INSTALL ON + SRC_FILES *.cpp + *.h + DEPENDENCIES Platform + renderer-test-common + wayland-test-utils +) + +makeTestFromTarget( + TARGET embedded-compositor-wayland-test + SUFFIX RNDSANDWICHTEST_SWRAST + ) diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/EmbeddedCompositor_WaylandMock.h b/tests/unittests/renderer/embedded-compositor-wayland/EmbeddedCompositor_WaylandMock.h similarity index 73% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/EmbeddedCompositor_WaylandMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/EmbeddedCompositor_WaylandMock.h index 448b3578e..97e22d6e0 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/EmbeddedCompositor_WaylandMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/EmbeddedCompositor_WaylandMock.h @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITOR_WAYLANDMOCK_H -#define RAMSES_EMBEDDEDCOMPOSITOR_WAYLANDMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/IEmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/IWaylandBuffer.h" -#include "EmbeddedCompositor_Wayland/IWaylandSurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandRegion.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IEmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositor_WaylandMock : public IEmbeddedCompositor_Wayland { @@ -32,5 +31,3 @@ namespace ramses_internal MOCK_METHOD(void, removeWaylandRegion, (IWaylandRegion& waylandRegion), (override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/EmbeddedCompositor_Wayland_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/EmbeddedCompositor_Wayland_Test.cpp similarity index 80% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/EmbeddedCompositor_Wayland_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/EmbeddedCompositor_Wayland_Test.cpp index ef36695ec..0fddb429f 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/EmbeddedCompositor_Wayland_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/EmbeddedCompositor_Wayland_Test.cpp @@ -8,27 +8,31 @@ #include "gmock/gmock.h" #include "TestWithWaylandEnvironment.h" -#include "RendererLib/RendererConfig.h" -#include "EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h" +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h" #include "ContextMock.h" #include "PlatformMock.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Collections/StringOutputStream.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" #include "WaylandSurfaceMock.h" #include "WaylandBufferMock.h" #include "WaylandCompositorConnectionMock.h" #include "WaylandRegionMock.h" #include "WaylandBufferResourceMock.h" -#include "EmbeddedCompositor_Wayland/WaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBuffer.h" #include "wayland-client.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "internal/Platform/EGL/Context_EGL.h" #include #include #include +#include -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -52,7 +56,7 @@ namespace ramses_internal return false; } - struct stat buf; + struct stat buf{}; if (fstat(fd, &buf) < 0) { return false; @@ -81,7 +85,7 @@ namespace ramses_internal const auto fullPath = fmt::format("{}/{}", WaylandEnvironmentUtils::GetVariable(WaylandEnvironmentVariable::XDGRuntimeDir), ecSocketPath); - struct stat statbuf; + struct stat statbuf{}; if (stat(fullPath.c_str(), &statbuf) != 0) return 0; // NOLINTNEXTLINE(hicpp-signed-bitwise) @@ -99,8 +103,8 @@ namespace ramses_internal { } - explicit ConnectToDisplayRunnable(const std::string& clientFileName) - : m_clientSocketFileName(clientFileName) + explicit ConnectToDisplayRunnable(std::string clientFileName) + : m_clientSocketFileName(std::move(clientFileName)) , m_result(false) , m_started(false) , m_ended(false) @@ -157,10 +161,14 @@ namespace ramses_internal // caller is expected to have a display prefix for logs ThreadLocalLog::SetPrefix(1); - if(xdgRuntimeDirSet) + if (xdgRuntimeDirSet) + { WaylandEnvironmentUtils::SetVariable(WaylandEnvironmentVariable::XDGRuntimeDir, m_initialValueOfXdgRuntimeDir); + } else + { WaylandEnvironmentUtils::UnsetVariable(WaylandEnvironmentVariable::XDGRuntimeDir); + } DisplayConfig displayConfig; displayConfig.setWaylandEmbeddedCompositingSocketName(ecSocketName); @@ -187,7 +195,7 @@ namespace ramses_internal return client.couldConnectToEmbeddedCompositor(); } - ContextMock context; + Context_EGL context = {nullptr, nullptr, nullptr, nullptr, nullptr, 0}; std::unique_ptr embeddedCompositor; UnixDomainSocket socket = UnixDomainSocket("testingSocket", m_initialValueOfXdgRuntimeDir); @@ -395,6 +403,96 @@ namespace ramses_internal EXPECT_FALSE(embeddedCompositor->hasUpdatedStreamTextureSources()); } + TEST_F(AEmbeddedCompositor_Wayland, QuickReconnect) + { + EXPECT_TRUE(init("wayland-10")); + + const WaylandIviSurfaceId surfaceIVIId(123); + + embeddedCompositor->addToUpdatedStreamTextureSourceIds(surfaceIVIId); + + auto newStreams = embeddedCompositor->dispatchNewStreamTextureSourceIds(); + auto obsoleteStreams = embeddedCompositor->dispatchObsoleteStreamTextureSourceIds(); + EXPECT_EQ(newStreams.size(), 1); + EXPECT_EQ(obsoleteStreams.size(), 0); + + for (int i = 1; i < 5; ++i) + { + int count = i; + while (count-- != 0) + { + // quick "reconnect" + embeddedCompositor->removeFromUpdatedStreamTextureSourceIds(surfaceIVIId); + embeddedCompositor->addToUpdatedStreamTextureSourceIds(surfaceIVIId); + } + + // both "newStreams" and "obsoleteStreams" contain the surfaceID + // must be handled on client side + // see RendererSceneUpdater::handleECStreamAvailabilityChanges() + newStreams = embeddedCompositor->dispatchNewStreamTextureSourceIds(); + obsoleteStreams = embeddedCompositor->dispatchObsoleteStreamTextureSourceIds(); + EXPECT_EQ(newStreams.size(), 1) << "iteration: " << i; + EXPECT_EQ(obsoleteStreams.size(), 1) << "iteration: " << i; + } + } + + TEST_F(AEmbeddedCompositor_Wayland, QuickConnectAndDisconnect) + { + EXPECT_TRUE(init("wayland-10")); + + const WaylandIviSurfaceId surfaceIVIId(123); + + for (int i = 1; i < 5; ++i) + { + int count = i; + while (count-- != 0) + { + // connect and disconnect again + embeddedCompositor->addToUpdatedStreamTextureSourceIds(surfaceIVIId); + embeddedCompositor->removeFromUpdatedStreamTextureSourceIds(surfaceIVIId); + } + + auto newStreams = embeddedCompositor->dispatchNewStreamTextureSourceIds(); + auto obsoleteStreams = embeddedCompositor->dispatchObsoleteStreamTextureSourceIds(); + EXPECT_TRUE(newStreams.empty()) << "iteration: " << i; + EXPECT_TRUE(obsoleteStreams.empty()) << "iteration: " << i; + } + } + + TEST_F(AEmbeddedCompositor_Wayland, QuickReconnectAndFinallyDisconnect) + { + EXPECT_TRUE(init("wayland-10")); + + const WaylandIviSurfaceId surfaceIVIId(123); + + embeddedCompositor->addToUpdatedStreamTextureSourceIds(surfaceIVIId); + + auto newStreams = embeddedCompositor->dispatchNewStreamTextureSourceIds(); + auto obsoleteStreams = embeddedCompositor->dispatchObsoleteStreamTextureSourceIds(); + EXPECT_EQ(newStreams.size(), 1); + EXPECT_EQ(obsoleteStreams.size(), 0); + + for (int i = 1; i < 5; ++i) + { + int count = i; + while (count-- != 0) + { + embeddedCompositor->removeFromUpdatedStreamTextureSourceIds(surfaceIVIId); + embeddedCompositor->addToUpdatedStreamTextureSourceIds(surfaceIVIId); + } + embeddedCompositor->removeFromUpdatedStreamTextureSourceIds(surfaceIVIId); + + newStreams = embeddedCompositor->dispatchNewStreamTextureSourceIds(); + obsoleteStreams = embeddedCompositor->dispatchObsoleteStreamTextureSourceIds(); + EXPECT_EQ(newStreams.size(), 0) << "iteration: " << i; + EXPECT_EQ(obsoleteStreams.size(), 1) << "iteration: " << i; + + embeddedCompositor->addToUpdatedStreamTextureSourceIds(surfaceIVIId); + newStreams = embeddedCompositor->dispatchNewStreamTextureSourceIds(); + EXPECT_EQ(newStreams.size(), 1) << "iteration: " << i; + } + } + TEST_F(AEmbeddedCompositor_Wayland, CanDispatchUpdatedStreamTextureSourceIds) { EXPECT_TRUE(init("wayland-10")); @@ -557,10 +655,10 @@ namespace ramses_internal RendererLogContext logContext(ERendererLogLevelFlag_Details); - EXPECT_CALL(surface, logInfos(Ref(logContext))).WillOnce(LogSomeSurfaceLogToContext()); + EXPECT_CALL(surface, logInfos(Ref(logContext), _)).WillOnce(LogSomeSurfaceLogToContext()); embeddedCompositor->logInfos(logContext); - EXPECT_STREQ(logContext.getStream().c_str(), "1 connected wayland client(s)\n SomeSurfaceLog"); + EXPECT_STREQ(logContext.getStream().c_str(), "1 wayland surface(s)\n SomeSurfaceLog0 wayland buffer(s)\n0 wayland region(s) - [not supported]\n"); } TEST_F(AEmbeddedCompositor_Wayland, getOrCreateBufferCreatesNewBuffer) @@ -568,7 +666,7 @@ namespace ramses_internal EXPECT_TRUE(init("wayland-10")); StrictMock bufferResource; - StrictMock* bufferResourceCloned = new StrictMock; + auto* bufferResourceCloned = new StrictMock; EXPECT_CALL(bufferResource, clone()).WillOnce(Return(bufferResourceCloned)); EXPECT_CALL(*bufferResourceCloned, addDestroyListener(_)); @@ -583,9 +681,9 @@ namespace ramses_internal EXPECT_TRUE(init("wayland-10")); StrictMock bufferResource; - StrictMock* bufferResourceCloned = new StrictMock; + auto* bufferResourceCloned = new StrictMock; - wl_resource* waylandNativeBufferResource(reinterpret_cast(123)); + auto* waylandNativeBufferResource(reinterpret_cast(123)); EXPECT_CALL(bufferResource, clone()).WillOnce(Return(bufferResourceCloned)); EXPECT_CALL(*bufferResourceCloned, addDestroyListener(_)); @@ -604,7 +702,7 @@ namespace ramses_internal EXPECT_TRUE(init("wayland-10")); StrictMock bufferResource; - StrictMock* bufferResourceCloned = new StrictMock; + auto* bufferResourceCloned = new StrictMock; StrictMock surface; embeddedCompositor->addWaylandSurface(surface); diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/NativeWaylandResourceMock.h b/tests/unittests/renderer/embedded-compositor-wayland/NativeWaylandResourceMock.h similarity index 88% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/NativeWaylandResourceMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/NativeWaylandResourceMock.h index 41aded4dd..7702f1469 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/NativeWaylandResourceMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/NativeWaylandResourceMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_NATIVEWAYLANDRESOURCEMOCK_H -#define RAMSES_NATIVEWAYLANDRESOURCEMOCK_H +#pragma once -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class NativeWaylandResourceMock: public INativeWaylandResource { @@ -36,5 +35,3 @@ namespace ramses_internal } }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferMock.h similarity index 79% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferMock.h index 09b502ba4..7a73cdfbd 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDBUFFERMOCK_H -#define RAMSES_WAYLANDBUFFERMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/IWaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class WaylandBufferMock : public IWaylandBuffer { @@ -21,7 +20,6 @@ namespace ramses_internal MOCK_METHOD(void, reference, (), (override)); MOCK_METHOD(void, release, (), (override)); MOCK_METHOD(bool, isSharedMemoryBuffer, (), (const, override)); + MOCK_METHOD(void, logInfos, (RendererLogContext&, const WaylandEGLExtensionProcs&), (const, override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferResourceMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferResourceMock.h similarity index 80% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferResourceMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferResourceMock.h index 7af303d03..15af117da 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferResourceMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferResourceMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDBUFFERRESOURCEMOCK_H -#define RAMSES_WAYLANDBUFFERRESOURCEMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" -namespace ramses_internal +namespace ramses::internal { class WaylandBufferResourceMock : public WaylandBufferResource { @@ -23,13 +22,11 @@ namespace ramses_internal MOCK_METHOD(void, setImplementation, (const void* implementation, void* data, IWaylandResourceDestroyFuncT destroyCallback), (override)); MOCK_METHOD(void, addDestroyListener, (wl_listener* listener), (override)); MOCK_METHOD(wl_resource*, getLowLevelHandle, (), (override)); - MOCK_METHOD(int32_t, bufferGetSharedMemoryWidth, (), (const, override)); - MOCK_METHOD(int32_t, bufferGetSharedMemoryHeight, (), (const, override)); + MOCK_METHOD(int32_t, getWidth, (), (const, override)); + MOCK_METHOD(int32_t, getHeight, (), (const, override)); MOCK_METHOD(const void*, bufferGetSharedMemoryData, (), (const, override)); MOCK_METHOD(void, bufferSendRelease, (), (override)); MOCK_METHOD(void, destroy, (), (override)); MOCK_METHOD(WaylandBufferResource*, clone, (), (const, override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferResource_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferResource_Test.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferResource_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferResource_Test.cpp index edc210a5a..b7d4cc0aa 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandBufferResource_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandBufferResource_Test.cpp @@ -7,20 +7,20 @@ // ------------------------------------------------------------------------- #define WL_HIDE_DEPRECATED -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "Utils/ThreadBarrier.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "gtest/gtest.h" #include "wayland-client.h" #include "wayland-server.h" #include "sys/mman.h" #include "fcntl.h" -#include "Utils/File.h" +#include "internal/Core/Utils/File.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -29,9 +29,7 @@ namespace ramses_internal class ClientRunnable : public Runnable { public: - ClientRunnable() - { - } + ClientRunnable() = default; void init(int fd) { @@ -60,7 +58,7 @@ namespace ramses_internal std::string fileName; int fd = CreateAnonymousFile(size, fileName); - uint8_t* bufferData = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + auto* bufferData = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); for (uint32_t i = 0; i < width * height; i++) { @@ -93,9 +91,9 @@ namespace ramses_internal private: - int CreateAnonymousFile(off_t size, std::string& fileName) + static int CreateAnonymousFile(off_t size, std::string& fileName) { - int ret; + int ret = 0; fileName ="SHMBuffer-XXXXXX"; @@ -119,7 +117,7 @@ namespace ramses_internal static void RegistryHandleGlobalCallBack( void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { - ClientRunnable* clientRunnable = static_cast(data); + auto* clientRunnable = static_cast(data); if (strcmp(interface, "wl_compositor") == 0) { clientRunnable->m_compositor = @@ -133,21 +131,17 @@ namespace ramses_internal } } - static void RegistryHandleGlobalRemoveCallBack(void* data, struct wl_registry* registry, uint32_t name) - { - UNUSED(data); - UNUSED(registry); - UNUSED(name); - } + static void RegistryHandleGlobalRemoveCallBack([[maybe_unused]] void* data, [[maybe_unused]] wl_registry* registry, [[maybe_unused]] uint32_t name) {} - int m_fd = 0; - ramses_internal::ThreadBarrier m_startBarrier{2}; - wl_compositor* m_compositor = nullptr; - wl_shm* m_shm = nullptr; + int m_fd = 0; + ThreadBarrier m_startBarrier{2}; + wl_compositor* m_compositor = nullptr; + wl_shm* m_shm = nullptr; const struct Registry_Listener : public wl_registry_listener { Registry_Listener() + : wl_registry_listener() { global = RegistryHandleGlobalCallBack; global_remove = RegistryHandleGlobalRemoveCallBack; @@ -201,36 +195,32 @@ namespace ramses_internal protected: static void CompositorBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) { - AWaylandBufferResource* awaylandBufferResource = static_cast(data); - wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, version, id); + auto* awaylandBufferResource = static_cast(data); + wl_resource* resource = wl_resource_create(client, &wl_compositor_interface, static_cast(version), id); wl_resource_set_implementation(resource, &awaylandBufferResource->m_compositorInterface, awaylandBufferResource, nullptr); } static void CompositorCreateSurfaceCallback(wl_client* client, wl_resource* clientResource, uint32_t id) { wl_resource* resource = wl_resource_create(client, &wl_surface_interface, 1, id); - AWaylandBufferResource* awaylandBufferResource = static_cast(wl_resource_get_user_data(clientResource)); + auto* awaylandBufferResource = static_cast(wl_resource_get_user_data(clientResource)); wl_resource_set_implementation(resource, &awaylandBufferResource->m_surfaceInterface, awaylandBufferResource, nullptr); } - static void SurfaceAttachCallback(wl_client* client, wl_resource* surfaceResource, wl_resource* bufferResource, int x, int y) + static void SurfaceAttachCallback( + [[maybe_unused]] wl_client* client, [[maybe_unused]] wl_resource* surfaceResource, wl_resource* bufferResource, [[maybe_unused]] int x, [[maybe_unused]] int y) { - UNUSED(client); - UNUSED(surfaceResource); - UNUSED(x); - UNUSED(y); - - AWaylandBufferResource* awaylandBufferResource = static_cast(wl_resource_get_user_data(surfaceResource)); + auto* awaylandBufferResource = static_cast(wl_resource_get_user_data(surfaceResource)); WaylandBufferResource waylandBufferResource(bufferResource); - int32_t width = waylandBufferResource.bufferGetSharedMemoryWidth(); + int32_t width = waylandBufferResource.getWidth(); EXPECT_EQ(10, width); - int32_t height = waylandBufferResource.bufferGetSharedMemoryHeight(); + int32_t height = waylandBufferResource.getHeight(); EXPECT_EQ(20, height); - const uint8_t* data = static_cast(waylandBufferResource.bufferGetSharedMemoryData()); + const auto* data = static_cast(waylandBufferResource.bufferGetSharedMemoryData()); EXPECT_NE(nullptr, data); for (int32_t i = 0; i < width * height; i++) { @@ -243,6 +233,7 @@ namespace ramses_internal const struct Compositor_Interface : public wl_compositor_interface { Compositor_Interface() + : wl_compositor_interface() { create_surface = CompositorCreateSurfaceCallback; } @@ -251,19 +242,20 @@ namespace ramses_internal const struct Surface_Interface : public wl_surface_interface { Surface_Interface() + : wl_surface_interface() { attach = SurfaceAttachCallback; } } m_surfaceInterface; - static void ResourceDestroyedListener(wl_listener* listener, void* data) + static void ResourceDestroyedListener(wl_listener* listener, [[maybe_unused]] void* data) { - UNUSED(data); WARNINGS_PUSH WARNING_DISABLE_LINUX(-Winvalid-offsetof) WARNING_DISABLE_LINUX(-Wcast-align) WARNING_DISABLE_LINUX(-Wold-style-cast) + // NOLINTNEXTLINE(modernize-use-auto, cppcoreguidelines-pro-type-cstyle-cast) AWaylandBufferResource* waylandBufferResource = wl_container_of(listener, waylandBufferResource, m_destroyListener); waylandBufferResource->m_resourceDestroyedListenerCalled++; @@ -276,7 +268,7 @@ namespace ramses_internal wl_client* m_client = nullptr; ClientRunnable m_clientRunnable; bool m_surfaceAttachCalled = false; - wl_listener m_destroyListener; + wl_listener m_destroyListener{}; uint32_t m_resourceDestroyedListenerCalled = 0; }; @@ -319,7 +311,7 @@ namespace ramses_internal wl_resource* resource = wl_resource_create(m_client, &wl_buffer_interface, 1, 0); WaylandBufferResource waylandBufferResource(resource); - EXPECT_EQ(waylandBufferResource.bufferGetSharedMemoryWidth(), 0); + EXPECT_EQ(waylandBufferResource.getWidth(), 0); waylandBufferResource.destroy(); } @@ -332,7 +324,7 @@ namespace ramses_internal wl_resource* resource = wl_resource_create(m_client, &wl_buffer_interface, 1, 0); WaylandBufferResource waylandBufferResource(resource); - EXPECT_EQ(waylandBufferResource.bufferGetSharedMemoryHeight(), 0); + EXPECT_EQ(waylandBufferResource.getHeight(), 0); waylandBufferResource.destroy(); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCallbackResourceMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCallbackResourceMock.h similarity index 86% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCallbackResourceMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandCallbackResourceMock.h index 76ed24c78..a9b9bc038 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCallbackResourceMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCallbackResourceMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCALLBACKRESOURCEMOCK_H -#define RAMSES_WAYLANDCALLBACKRESOURCEMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/WaylandCallbackResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h" -namespace ramses_internal +namespace ramses::internal { class WaylandCallbackResourceMock: public WaylandCallbackResource { @@ -27,5 +26,3 @@ namespace ramses_internal MOCK_METHOD(void, destroy, (), (override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCallbackResource_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCallbackResource_Test.cpp similarity index 79% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCallbackResource_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandCallbackResource_Test.cpp index 335cb888a..7e52ba635 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCallbackResource_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCallbackResource_Test.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandCallbackResource.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Utils/ThreadBarrier.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "gtest/gtest.h" #include "wayland-client.h" #include "wayland-client-protocol.h" #include "wayland-server.h" #include -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -70,10 +70,10 @@ namespace ramses_internal static void RegistryHandleGlobalCallBack( void* data, wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { - ClientRunnable* clientRunnable = static_cast(data); + auto* clientRunnable = static_cast(data); if (strcmp(interface, "wl_display") == 0) { - wl_display* display = + auto* display = static_cast(wl_registry_bind(registry, name, &wl_display_interface, version)); if (nullptr != display) @@ -84,28 +84,22 @@ namespace ramses_internal } } - static void RegistryHandleGlobalRemoveCallBack(void* data, wl_registry* wl_registry, uint32_t name) - { - UNUSED(data); - UNUSED(wl_registry); - UNUSED(name); - } + static void RegistryHandleGlobalRemoveCallBack([[maybe_unused]] void* data, [[maybe_unused]] wl_registry* wl_registry, [[maybe_unused]] uint32_t name) {} - static void SyncCallbackDone(void *data, wl_callback* callback, uint32_t callbackData) + static void SyncCallbackDone(void* data, [[maybe_unused]] wl_callback* callback, uint32_t callbackData) { - UNUSED(callback); - ClientRunnable* clientRunnable = static_cast(data); + auto* clientRunnable = static_cast(data); EXPECT_EQ(33u, callbackData); clientRunnable->m_callbackReceived = true; } - int m_fd = 0; - std::atomic m_callbackReceived; - ramses_internal::ThreadBarrier m_startBarrier{2}; + int m_fd = 0; + std::atomic m_callbackReceived; + ThreadBarrier m_startBarrier{2}; const struct Registry_Listener : public wl_registry_listener { - Registry_Listener() + Registry_Listener() : wl_registry_listener() { global = RegistryHandleGlobalCallBack; global_remove = RegistryHandleGlobalRemoveCallBack; @@ -114,7 +108,7 @@ namespace ramses_internal const struct SyncCallback_Listener : public wl_callback_listener { - SyncCallback_Listener() + SyncCallback_Listener() : wl_callback_listener() { done = SyncCallbackDone; } @@ -164,32 +158,24 @@ namespace ramses_internal protected: static void DisplayBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) { - AWaylandCallbackResource* awaylandCallbackResource = static_cast(data); + auto* awaylandCallbackResource = static_cast(data); wl_resource* resource = wl_resource_create(client, &wl_display_interface, version, id); wl_resource_set_implementation(resource, &awaylandCallbackResource->m_displayInterface, data, nullptr); } - static void DisplaySync(struct wl_client* client, wl_resource* resource, uint32_t id) + static void DisplaySync([[maybe_unused]] wl_client* client, [[maybe_unused]] wl_resource* resource, uint32_t id) { - UNUSED(client); - UNUSED(resource); - wl_resource* callbackResource = wl_resource_create(client, &wl_callback_interface, 1, id); WaylandCallbackResource waylandCallbackResource(callbackResource); waylandCallbackResource.callbackSendDone(33); waylandCallbackResource.destroy(); } - static void GetRegistry(struct wl_client* client, struct wl_resource* resource, uint32_t registry) - { - UNUSED(client); - UNUSED(registry); - UNUSED(resource); - } + static void GetRegistry([[maybe_unused]] wl_client* client, [[maybe_unused]] wl_resource* resource, [[maybe_unused]] uint32_t registry) {} const struct Display_Interface : public wl_display_interface { - Display_Interface() + Display_Interface() : wl_display_interface() { sync = DisplaySync; get_registry = GetRegistry; diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandClientMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandClientMock.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandClientMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandClientMock.h index 1d15b9b36..e18507484 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandClientMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandClientMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCLIENTMOCK_H -#define RAMSES_WAYLANDCLIENTMOCK_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class WaylandClientMock : public IWaylandClient { @@ -27,5 +26,3 @@ namespace ramses_internal MOCK_METHOD(WaylandCallbackResource*, callbackResourceCreate, (const wl_interface* interface, int version, uint32_t id), (override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandClient_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandClient_Test.cpp similarity index 82% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandClient_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandClient_Test.cpp index f4ace470d..2d3037567 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandClient_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandClient_Test.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandClient.h" -#include "EmbeddedCompositor_Wayland/INativeWaylandResource.h" -#include "EmbeddedCompositor_Wayland/WaylandCallbackResource.h" -#include "EmbeddedCompositor_Wayland/WaylandDisplay.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Utils/ThreadBarrier.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/INativeWaylandResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCallbackResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandDisplay.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include "gtest/gtest.h" #include "wayland-client.h" #include -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -33,18 +33,9 @@ namespace ramses_internal assert(m_display != nullptr); } - ~AWaylandClient() override - { - } - protected: - - static void CompositorBindCallback(wl_client* client, void* data, uint32_t version, uint32_t id) + static void CompositorBindCallback(wl_client* client, [[maybe_unused]] void* data, [[maybe_unused]] uint32_t version, [[maybe_unused]] uint32_t id) { - UNUSED(data); - UNUSED(version); - UNUSED(id); - WaylandClient waylandClient(client); waylandClient.postNoMemory(); } @@ -54,14 +45,14 @@ namespace ramses_internal m_resourceDestroyedListenerCalled++; } - static void ResourceDestroyedListener(wl_listener* listener, void* data) + static void ResourceDestroyedListener(wl_listener* listener, [[maybe_unused]] void* data) { - UNUSED(data); WARNINGS_PUSH WARNING_DISABLE_LINUX(-Winvalid-offsetof) WARNING_DISABLE_LINUX(-Wcast-align) - AWaylandClient* waylandClient = static_cast(wl_container_of(listener, waylandClient, m_destroyListener)); + // NOLINTNEXTLINE(modernize-use-auto, cppcoreguidelines-pro-type-cstyle-cast) + AWaylandClient* waylandClient = wl_container_of(listener, waylandClient, m_destroyListener); WARNINGS_POP @@ -72,7 +63,7 @@ namespace ramses_internal WaylandDisplay m_waylandDisplay; wl_display* m_display = nullptr; uint32_t m_resourceDestroyedListenerCalled = 0; - wl_listener m_destroyListener; + wl_listener m_destroyListener{}; }; namespace @@ -103,7 +94,7 @@ namespace ramses_internal wl_display_roundtrip(display); // Store current protocol error value in member, which is checked by the test in the main thread. - m_protocolErrorCode = wl_display_get_protocol_error(display, nullptr, nullptr); + m_protocolErrorCode = static_cast(wl_display_get_protocol_error(display, nullptr, nullptr)); wl_registry_destroy(registry); wl_display_disconnect(display); @@ -120,32 +111,25 @@ namespace ramses_internal } private: - static void RegistryHandleGlobalCallBack( - void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) + static void + RegistryHandleGlobalCallBack([[maybe_unused]] void* data, struct wl_registry* registry, uint32_t name, const char* interface, [[maybe_unused]] uint32_t version) { - UNUSED(data); - UNUSED(version); - if (strcmp(interface, "wl_compositor") == 0) { wl_registry_bind(registry, name, &wl_compositor_interface, 1); } } - static void RegistryHandleGlobalRemoveCallBack(void* data, struct wl_registry* registry, uint32_t name) - { - UNUSED(data); - UNUSED(registry); - UNUSED(name); - } + static void RegistryHandleGlobalRemoveCallBack([[maybe_unused]] void* data, [[maybe_unused]] struct wl_registry* registry, [[maybe_unused]] uint32_t name) {} - int m_fd; - std::atomic m_protocolErrorCode; - ramses_internal::ThreadBarrier m_startBarrier{2}; + int m_fd; + std::atomic m_protocolErrorCode; + ThreadBarrier m_startBarrier{2}; const struct Registry_Listener : public wl_registry_listener { Registry_Listener() + : wl_registry_listener() { global = RegistryHandleGlobalCallBack; global_remove = RegistryHandleGlobalRemoveCallBack; diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCompositorConnectionMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCompositorConnectionMock.h similarity index 77% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCompositorConnectionMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandCompositorConnectionMock.h index 9919d2f90..44e69ce58 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCompositorConnectionMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCompositorConnectionMock.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDCOMPOSITORCONNECTIONMOCK_H -#define RAMSES_WAYLANDCOMPOSITORCONNECTIONMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/IWaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" -namespace ramses_internal +namespace ramses::internal { class WaylandCompositorConnectionMock : public IWaylandCompositorConnection { @@ -23,5 +22,3 @@ namespace ramses_internal MOCK_METHOD(void, compositorCreateRegion, (IWaylandClient& client, uint32_t id), (override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCompositorConnection_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCompositorConnection_Test.cpp similarity index 91% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCompositorConnection_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandCompositorConnection_Test.cpp index 747e4e449..a5bfa48d5 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandCompositorConnection_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandCompositorConnection_Test.cpp @@ -6,25 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/IWaylandRegion.h" -#include "EmbeddedCompositor_Wayland/IWaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h" #include "WaylandClientMock.h" #include "NativeWaylandResourceMock.h" #include "EmbeddedCompositor_WaylandMock.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; class AWaylandCompositorConnection : public Test { public: - AWaylandCompositorConnection() - { - } + AWaylandCompositorConnection() = default; ~AWaylandCompositorConnection() override { @@ -90,7 +88,7 @@ namespace ramses_internal const uint32_t id = 1; const uint32_t interfaceVersion = 4; - NativeWaylandResourceMock* surfaceResource = new NativeWaylandResourceMock; + auto* surfaceResource = new NativeWaylandResourceMock; InSequence s; EXPECT_CALL(m_client, resourceCreate(&wl_surface_interface, interfaceVersion, id)) @@ -120,7 +118,7 @@ namespace ramses_internal const uint32_t id = 1; const uint32_t interfaceVersion = 4; - NativeWaylandResourceMock* regionResource = new NativeWaylandResourceMock; + auto* regionResource = new NativeWaylandResourceMock; InSequence s; EXPECT_CALL(m_client, resourceCreate(&wl_region_interface, interfaceVersion, id)) diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandGlobal_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandGlobal_Test.cpp similarity index 91% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandGlobal_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandGlobal_Test.cpp index 69888ff73..6143e7016 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandGlobal_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandGlobal_Test.cpp @@ -6,10 +6,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandGlobal.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandGlobal.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandIVISurfaceMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandIVISurfaceMock.h similarity index 75% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandIVISurfaceMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandIVISurfaceMock.h index 41d1593cd..df0ac6269 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandIVISurfaceMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandIVISurfaceMock.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDIVISURFACEMOCK_H -#define RAMSES_WAYLANDIVISURFACEMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/IWaylandIVISurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "EmbeddedCompositor_Wayland/IWaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandIVISurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h" -namespace ramses_internal +namespace ramses::internal { class WaylandIVISurfaceMock : public IWaylandIVISurface { @@ -25,5 +24,3 @@ namespace ramses_internal MOCK_METHOD(WaylandIviSurfaceId, getIviId, (), (const, override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandRegionMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandRegionMock.h similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandRegionMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandRegionMock.h index 2d1390e33..42af4fc71 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandRegionMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandRegionMock.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDREGIONMOCK_H -#define RAMSES_WAYLANDREGIONMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/IWaylandRegion.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandRegion.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" -namespace ramses_internal +namespace ramses::internal { class WaylandRegionMock : public IWaylandRegion { @@ -23,5 +22,3 @@ namespace ramses_internal MOCK_METHOD(void, regionSubtract, (IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height), (override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandResourceLifecycleTest.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandResourceLifecycleTest.cpp similarity index 88% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandResourceLifecycleTest.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandResourceLifecycleTest.cpp index 6dc7b430b..08376e53d 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandResourceLifecycleTest.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandResourceLifecycleTest.cpp @@ -6,33 +6,34 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" #include "EmbeddedCompositor_WaylandMock.h" #include "WaylandClientMock.h" #include "NativeWaylandResourceMock.h" #include "WaylandSurfaceMock.h" #include "ContextMock.h" - -#include "EmbeddedCompositor_Wayland/EmbeddedCompositor_Wayland.h" -#include "EmbeddedCompositor_Wayland/WaylandSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandShellSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandIVISurface.h" -#include "EmbeddedCompositor_Wayland/WaylandRegion.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufParams.h" -#include "EmbeddedCompositor_Wayland/WaylandCompositorConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandIVIApplicationConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandShellConnection.h" -#include "EmbeddedCompositor_Wayland/WaylandOutputConnection.h" -#include "EmbeddedCompositor_Wayland/LinuxDmabufConnection.h" - -#include "RendererLib/RendererConfig.h" -#include "RendererLib/DisplayConfig.h" +#include "internal/Platform/EGL/Context_EGL.h" + +#include "internal/Platform/Wayland/EmbeddedCompositor/EmbeddedCompositor_Wayland.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVISurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandRegion.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufParams.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandCompositorConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandIVIApplicationConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandOutputConnection.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/LinuxDmabufConnection.h" + +#include "internal/RendererLib/RendererConfig.h" +#include "internal/RendererLib/DisplayConfig.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -112,8 +113,8 @@ namespace ramses_internal //need to focus on other aspects of resource lifecycle NiceMock m_compositorMock; NiceMock m_surfaceMock; - NiceMock m_contextMock; - EmbeddedCompositor_Wayland m_waylandCompositor{{}, m_contextMock}; + Context_EGL m_context = {nullptr, nullptr, nullptr, nullptr, nullptr, 0}; + EmbeddedCompositor_Wayland m_waylandCompositor{{}, m_context}; StrictMock m_clientMock; StrictMock* m_nativeResourceMock = nullptr; diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandResource_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandResource_Test.cpp similarity index 89% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandResource_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandResource_Test.cpp index 9dd0afbff..4386aab41 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandResource_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandResource_Test.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/NativeWaylandResource.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/NativeWaylandResource.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include "gtest/gtest.h" #include "wayland-client.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -36,9 +36,8 @@ namespace ramses_internal } protected: - void resourceDestroyedListener(void* data) + void resourceDestroyedListener([[maybe_unused]] void* data) { - UNUSED(data); m_resourceDestroyedListenerCalled++; } @@ -54,24 +53,23 @@ namespace ramses_internal WARNING_DISABLE_LINUX(-Wcast-align) WARNING_DISABLE_LINUX(-Wold-style-cast) + // NOLINTNEXTLINE(modernize-use-auto, cppcoreguidelines-pro-type-cstyle-cast) AWaylandResource* waylandResource = wl_container_of(listener, waylandResource, m_destroyListener); waylandResource->resourceDestroyedListener(data); WARNINGS_POP } - static void ResourceDestroyedCallback(wl_resource* clientResource) + static void ResourceDestroyedCallback([[maybe_unused]] wl_resource* clientResource) { - UNUSED(clientResource); - - AWaylandResource* waylandResource = static_cast(wl_resource_get_user_data(clientResource)); + auto* waylandResource = static_cast(wl_resource_get_user_data(clientResource)); waylandResource->resourceDestroyedCallback(); } wl_display* m_display = nullptr; UnixDomainSocket m_socket = UnixDomainSocket("testingSocket", WaylandEnvironmentUtils::GetVariable(WaylandEnvironmentVariable::XDGRuntimeDir)); wl_client* m_client = nullptr; - wl_listener m_destroyListener; + wl_listener m_destroyListener{}; uint32_t m_resourceDestroyedListenerCalled = 0; uint32_t m_resourceDestroyedCallbackCalled = 0; diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandShellSurfaceMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandShellSurfaceMock.h similarity index 90% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandShellSurfaceMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandShellSurfaceMock.h index b3f9693c9..d744218cd 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandShellSurfaceMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandShellSurfaceMock.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDSHELLSURFACEMOCK_H -#define RAMSES_WAYLANDSHELLSURFACEMOCK_H +#pragma once #include "gmock/gmock.h" -#include "EmbeddedCompositor_Wayland/IWaylandShellSurface.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" -namespace ramses_internal +namespace ramses::internal { class WaylandShellSurfaceMock : public IWaylandShellSurface { @@ -33,5 +32,3 @@ namespace ramses_internal MOCK_METHOD(std::string&, getTitle, (), (const, override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandShellSurface_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandShellSurface_Test.cpp similarity index 92% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandShellSurface_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandShellSurface_Test.cpp index d8ca0e6fc..148748be8 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandShellSurface_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandShellSurface_Test.cpp @@ -6,28 +6,20 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandShellSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandShellSurface.h" #include "WaylandClientMock.h" #include "NativeWaylandResourceMock.h" #include "WaylandSurfaceMock.h" #include "gtest/gtest.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; class AWaylandShellSurface : public Test { public: - AWaylandShellSurface() - { - } - - ~AWaylandShellSurface() override - { - } - WaylandShellSurface* createWaylandShellSurface() { const uint32_t interfaceVersion = 1; @@ -105,7 +97,7 @@ namespace ramses_internal TEST_F(AWaylandShellSurface, CanBeDestroyedWhenSurfaceWasAlreadyDeleted) { - StrictMock* surface = new StrictMock; + auto* surface = new StrictMock; m_shellSurfaceResource = new StrictMock; @@ -122,7 +114,7 @@ namespace ramses_internal EXPECT_CALL(*surface, hasShellSurface()).WillOnce(Return(false)); EXPECT_CALL(*surface, setShellSurface(NotNull())); - WaylandShellSurface* shellSurface = new WaylandShellSurface(m_client, m_shellResource, id, *surface); + auto* shellSurface = new WaylandShellSurface(m_client, m_shellResource, id, *surface); delete surface; shellSurface->surfaceWasDeleted(); diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandSurfaceMock.h b/tests/unittests/renderer/embedded-compositor-wayland/WaylandSurfaceMock.h similarity index 87% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandSurfaceMock.h rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandSurfaceMock.h index 1d5a9447d..20009bfe1 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandSurfaceMock.h +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandSurfaceMock.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WAYLANDSURFACEMOCK_H -#define RAMSES_WAYLANDSURFACEMOCK_H +#pragma once -#include "EmbeddedCompositor_Wayland/IWaylandSurface.h" -#include "EmbeddedCompositor_Wayland/WaylandBufferResource.h" -#include "EmbeddedCompositor_Wayland/IWaylandBuffer.h" -#include "EmbeddedCompositor_Wayland/IWaylandClient.h" -#include "RendererLib/RendererLogContext.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandBufferResource.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandBuffer.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/IWaylandClient.h" +#include "internal/RendererLib/RendererLogContext.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class WaylandSurfaceMock : public IWaylandSurface { @@ -34,7 +33,7 @@ namespace ramses_internal MOCK_METHOD(void, surfaceDamageBuffer, (IWaylandClient& client, int32_t x, int32_t y, int32_t width, int32_t height), (override)); MOCK_METHOD(void, setShellSurface, (IWaylandShellSurface* shellSurface), (override)); MOCK_METHOD(bool, hasShellSurface, (), (const, override)); - MOCK_METHOD(void, logInfos, (RendererLogContext& context), (const, override)); + MOCK_METHOD(void, logInfos, (RendererLogContext& context, const WaylandEGLExtensionProcs&), (const, override)); MOCK_METHOD(WaylandIviSurfaceId, getIviSurfaceId, (), (const, override)); MOCK_METHOD(void, sendFrameCallbacks, (uint32_t time), (override)); MOCK_METHOD(IWaylandBuffer*, getWaylandBuffer, (), (const, override)); @@ -50,5 +49,3 @@ namespace ramses_internal MOCK_METHOD(bool, dispatchBufferTypeChanged, (), (override)); }; } - -#endif diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandSurface_Test.cpp b/tests/unittests/renderer/embedded-compositor-wayland/WaylandSurface_Test.cpp similarity index 94% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandSurface_Test.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/WaylandSurface_Test.cpp index c62e9f7d4..e4d43ae04 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/WaylandSurface_Test.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/WaylandSurface_Test.cpp @@ -6,7 +6,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "EmbeddedCompositor_Wayland/WaylandSurface.h" +#include "internal/Platform/Wayland/EmbeddedCompositor/WaylandSurface.h" +#include "internal/Platform/Wayland/WaylandEGLExtensionProcs.h" #include "gtest/gtest.h" #include "WaylandClientMock.h" @@ -19,21 +20,13 @@ #include "EmbeddedCompositor_WaylandMock.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; class AWaylandSurface : public Test { public: - AWaylandSurface() - { - } - - ~AWaylandSurface() override - { - } - void createWaylandSurface() { m_surfaceResource = new StrictMock; @@ -65,6 +58,7 @@ namespace ramses_internal { EXPECT_CALL(expectation.first, isSharedMemoryBuffer()).WillOnce(Return(expectation.second)); } + EXPECT_CALL(bufferMock, getResource()).Times(AtMost(1)).WillRepeatedly(ReturnRef(bufferResourceMock)); EXPECT_CALL(m_iviSurface, getIviId()).Times(AtMost(1)); //in case an ivi-surface is set m_waylandSurface->surfaceAttach(m_client, bufferResourceMock, 0, 0); @@ -339,7 +333,7 @@ namespace ramses_internal { createWaylandSurface(); - WaylandCallbackResourceMock* callbackResource = new WaylandCallbackResourceMock; + auto* callbackResource = new WaylandCallbackResourceMock; const uint32_t id(1); EXPECT_CALL(m_client, callbackResourceCreate(&wl_callback_interface, 1, id)).WillOnce(Return(callbackResource)); @@ -371,13 +365,13 @@ namespace ramses_internal { createWaylandSurface(); - WaylandCallbackResourceMock* callbackResource1 = new WaylandCallbackResourceMock; + auto* callbackResource1 = new WaylandCallbackResourceMock; const uint32_t id(1); EXPECT_CALL(m_client, callbackResourceCreate(&wl_callback_interface, 1, id)).WillOnce(Return(callbackResource1)); m_waylandSurface->surfaceFrame(m_client, id); - WaylandCallbackResourceMock* callbackResource2 = new WaylandCallbackResourceMock; + auto* callbackResource2 = new WaylandCallbackResourceMock; EXPECT_CALL(m_client, callbackResourceCreate(&wl_callback_interface, 1, id)).WillOnce(Return(callbackResource2)); m_waylandSurface->surfaceFrame(m_client, id); @@ -409,10 +403,11 @@ namespace ramses_internal { createWaylandSurface(); + WaylandEGLExtensionProcs eglExt(EGL_NO_DISPLAY); RendererLogContext logContext(ERendererLogLevelFlag_Details); - m_waylandSurface->logInfos(logContext); + m_waylandSurface->logInfos(logContext, eglExt); - EXPECT_STREQ(logContext.getStream().c_str(), "[ivi-surface-id: 4294967295; title: \"\"]\n"); + EXPECT_STREQ(logContext.getStream().c_str(), "ivi-surface:4294967295; title: \"\"; client[pid:-1 uid:0 gid:0]; commitedFrames: 0\n"); deleteWaylandSurface(); } @@ -424,11 +419,12 @@ namespace ramses_internal WaylandIviSurfaceId iviId(123); + WaylandEGLExtensionProcs eglExt(EGL_NO_DISPLAY); RendererLogContext logContext(ERendererLogLevelFlag_Details); EXPECT_CALL(m_iviSurface, getIviId()).WillOnce(Return(iviId)); - m_waylandSurface->logInfos(logContext); + m_waylandSurface->logInfos(logContext, eglExt); - EXPECT_STREQ(logContext.getStream().c_str(), "[ivi-surface-id: 123; title: \"\"]\n"); + EXPECT_STREQ(logContext.getStream().c_str(), "ivi-surface:123; title: \"\"; client[pid:-1 uid:0 gid:0]; commitedFrames: 0\n"); deleteWaylandSurface(true); } @@ -440,11 +436,12 @@ namespace ramses_internal std::string title("A Title"); + WaylandEGLExtensionProcs eglExt(EGL_NO_DISPLAY); RendererLogContext logContext(ERendererLogLevelFlag_Details); EXPECT_CALL(m_shellSurface, getTitle()).WillOnce(ReturnRef(title)); - m_waylandSurface->logInfos(logContext); + m_waylandSurface->logInfos(logContext, eglExt); - EXPECT_STREQ(logContext.getStream().c_str(), "[ivi-surface-id: 4294967295; title: \"A Title\"]\n"); + EXPECT_STREQ(logContext.getStream().c_str(), "ivi-surface:4294967295; title: \"A Title\"; client[pid:-1 uid:0 gid:0]; commitedFrames: 0\n"); deleteWaylandSurface(false, true); } @@ -458,12 +455,13 @@ namespace ramses_internal WaylandIviSurfaceId iviId(123); std::string title("A Title"); + WaylandEGLExtensionProcs eglExt(EGL_NO_DISPLAY); RendererLogContext logContext(ERendererLogLevelFlag_Details); EXPECT_CALL(m_iviSurface, getIviId()).WillOnce(Return(iviId)); EXPECT_CALL(m_shellSurface, getTitle()).WillOnce(ReturnRef(title)); - m_waylandSurface->logInfos(logContext); + m_waylandSurface->logInfos(logContext, eglExt); - EXPECT_STREQ(logContext.getStream().c_str(), "[ivi-surface-id: 123; title: \"A Title\"]\n"); + EXPECT_STREQ(logContext.getStream().c_str(), "ivi-surface:123; title: \"A Title\"; client[pid:-1 uid:0 gid:0]; commitedFrames: 0\n"); deleteWaylandSurface(true, true); } diff --git a/renderer/Platform/EmbeddedCompositor_Wayland/test/main.cpp b/tests/unittests/renderer/embedded-compositor-wayland/main.cpp similarity index 81% rename from renderer/Platform/EmbeddedCompositor_Wayland/test/main.cpp rename to tests/unittests/renderer/embedded-compositor-wayland/main.cpp index 52c0f0260..a4eb0fbd0 100644 --- a/renderer/Platform/EmbeddedCompositor_Wayland/test/main.cpp +++ b/tests/unittests/renderer/embedded-compositor-wayland/main.cpp @@ -9,15 +9,15 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "PlatformAbstraction/PlatformConsole.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/PlatformAbstraction/PlatformConsole.h" +#include "internal/Core/Utils/ThreadLocalLog.h" int main(int argc, char* argv[]) { testing::InitGoogleMock(&argc, argv); // set log prefix for all tests - ramses_internal::ThreadLocalLog::SetPrefix(1); + ramses::internal::ThreadLocalLog::SetPrefix(1); return RUN_ALL_TESTS(); } diff --git a/renderer/ramses-renderer-impl/test/BinaryShaderCacheProxyTest.cpp b/tests/unittests/renderer/ramses-renderer/BinaryShaderCacheProxyTest.cpp similarity index 73% rename from renderer/ramses-renderer-impl/test/BinaryShaderCacheProxyTest.cpp rename to tests/unittests/renderer/ramses-renderer/BinaryShaderCacheProxyTest.cpp index 6454e7bb2..c7caa5272 100644 --- a/renderer/ramses-renderer-impl/test/BinaryShaderCacheProxyTest.cpp +++ b/tests/unittests/renderer/ramses-renderer/BinaryShaderCacheProxyTest.cpp @@ -6,19 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-renderer-api/IBinaryShaderCache.h" -#include "BinaryShaderCacheProxy.h" -#include "Utils/ThreadBarrier.h" +#include "ramses/renderer/IBinaryShaderCache.h" +#include "impl/BinaryShaderCacheProxy.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include #include #include using namespace testing; -namespace ramses +namespace ramses::internal { - class BinaryShaderCacheMock : public IBinaryShaderCache + class BinaryShaderCacheMock : public ramses::IBinaryShaderCache { public: BinaryShaderCacheMock() = default; @@ -29,8 +29,8 @@ namespace ramses MOCK_METHOD(uint32_t, getBinaryShaderSize, (effectId_t effectId), (const, override)); MOCK_METHOD(binaryShaderFormatId_t, getBinaryShaderFormat, (effectId_t effectId), (const, override)); MOCK_METHOD(bool, shouldBinaryShaderBeCached, (effectId_t effectId, sceneId_t sceneId), (const, override)); - MOCK_METHOD(void, getBinaryShaderData, (effectId_t effectId, uint8_t* buffer, uint32_t bufferSize), (const, override)); - MOCK_METHOD(void, storeBinaryShader, (effectId_t effectId, sceneId_t sceneId, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat), (override)); + MOCK_METHOD(void, getBinaryShaderData, (effectId_t effectId, std::byte* buffer, uint32_t bufferSize), (const, override)); + MOCK_METHOD(void, storeBinaryShader, (effectId_t effectId, sceneId_t sceneId, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, binaryShaderFormatId_t binaryShaderFormat), (override)); MOCK_METHOD(void, binaryShaderUploaded, (effectId_t effectId, bool success), (const, override)); }; @@ -38,31 +38,28 @@ namespace ramses { public: ABinaryShaderCacheProxy() - : cache() - , proxy(cache) + : proxy(cache) { } - ~ABinaryShaderCacheProxy() override - { - } + ~ABinaryShaderCacheProxy() override = default; protected: testing::StrictMock cache; BinaryShaderCacheProxy proxy; - ramses_internal::ResourceContentHash effectHash = ramses_internal::ResourceContentHash{ 0x1234567890abcdef, 0xfedcba0987654321 }; + ResourceContentHash effectHash = ResourceContentHash{ 0x1234567890abcdef, 0xfedcba0987654321 }; effectId_t effectExternal = { 0x1234567890abcdef, 0xfedcba0987654321 }; }; TEST_F(ABinaryShaderCacheProxy, forwardsDeviceSupportsBinaryShaderFormatsCallsToCacheAndTransformsVectorCorrectly) { - std::vector format{ - ramses_internal::BinaryShaderFormatID{ 1 }, - ramses_internal::BinaryShaderFormatID{ 2 }, - ramses_internal::BinaryShaderFormatID{ 3 }, - ramses_internal::BinaryShaderFormatID{ 4 } + std::vector format{ + BinaryShaderFormatID{ 1 }, + BinaryShaderFormatID{ 2 }, + BinaryShaderFormatID{ 3 }, + BinaryShaderFormatID{ 4 } }; EXPECT_CALL(cache, deviceSupportsBinaryShaderFormats(_, 4)); @@ -82,16 +79,16 @@ namespace ramses EXPECT_EQ(3u, proxy.getBinaryShaderSize(effectHash)); EXPECT_CALL(cache, getBinaryShaderFormat(effectExternal)).WillOnce(Return(binaryShaderFormatId_t{ 3u })); - EXPECT_EQ(ramses_internal::BinaryShaderFormatID{ 3u }, proxy.getBinaryShaderFormat(effectHash)); + EXPECT_EQ(BinaryShaderFormatID{ 3u }, proxy.getBinaryShaderFormat(effectHash)); EXPECT_CALL(cache, shouldBinaryShaderBeCached(effectExternal, sceneId_t{ 111 })).WillOnce(Return(true)); - EXPECT_TRUE(proxy.shouldBinaryShaderBeCached(effectHash, ramses_internal::SceneId{ 111 })); + EXPECT_TRUE(proxy.shouldBinaryShaderBeCached(effectHash, SceneId{ 111 })); EXPECT_CALL(cache, getBinaryShaderData(effectExternal, nullptr, 123)); proxy.getBinaryShaderData(effectHash, nullptr, 123); EXPECT_CALL(cache, storeBinaryShader(effectExternal, sceneId_t{ 111 }, nullptr, 123, binaryShaderFormatId_t{ 3u })); - proxy.storeBinaryShader(effectHash, ramses_internal::SceneId{ 111 }, nullptr, 123, ramses_internal::BinaryShaderFormatID{ 3u }); + proxy.storeBinaryShader(effectHash, SceneId{ 111 }, nullptr, 123, BinaryShaderFormatID{ 3u }); EXPECT_CALL(cache, binaryShaderUploaded(effectExternal, true)); proxy.binaryShaderUploaded(effectHash, true); @@ -99,7 +96,7 @@ namespace ramses TEST_F(ABinaryShaderCacheProxy, makesCallsToCacheInAThreadsafeWay) { - ramses_internal::ThreadBarrier startBarrier; + ThreadBarrier startBarrier; auto randomlyExecuteFunctions = [this, &startBarrier]() { std::random_device randomSource; std::mt19937 gen(randomSource()); @@ -120,13 +117,13 @@ namespace ramses proxy.getBinaryShaderFormat(effectHash); break; case 3: - proxy.shouldBinaryShaderBeCached(effectHash, ramses_internal::SceneId{ 111 }); + proxy.shouldBinaryShaderBeCached(effectHash, SceneId{ 111 }); break; case 4: proxy.getBinaryShaderData(effectHash, nullptr, 123); break; case 5: - proxy.storeBinaryShader(effectHash, ramses_internal::SceneId{ 111 }, nullptr, 123, ramses_internal::BinaryShaderFormatID{ 3u }); + proxy.storeBinaryShader(effectHash, SceneId{ 111 }, nullptr, 123, BinaryShaderFormatID{ 3u }); break; case 6: proxy.binaryShaderUploaded(effectHash, true); @@ -138,13 +135,13 @@ namespace ramses }; int w = 0; // all threads write this variable to trigger tsan - EXPECT_CALL(cache, hasBinaryShader(_)).Times(AnyNumber()).WillRepeatedly([&](auto) { w = 8; return true; }); - EXPECT_CALL(cache, getBinaryShaderSize(_)).Times(AnyNumber()).WillRepeatedly([&](auto) { w = 22; return 3u; }); - EXPECT_CALL(cache, getBinaryShaderFormat(_)).Times(AnyNumber()).WillRepeatedly([&](auto) { w = 800; return binaryShaderFormatId_t{ 3 }; }); - EXPECT_CALL(cache, shouldBinaryShaderBeCached(_, _)).Times(AnyNumber()).WillRepeatedly([&](auto, auto) { w = 12; return true; }); - EXPECT_CALL(cache, getBinaryShaderData(_, _, _)).Times(AnyNumber()).WillRepeatedly([&](auto, auto, auto) { w = 120; }); - EXPECT_CALL(cache, storeBinaryShader(_, _, _, _, _)).Times(AnyNumber()).WillRepeatedly([&](auto, auto, auto, auto, auto) { w = 60; }); - EXPECT_CALL(cache, binaryShaderUploaded(_, _)).Times(AnyNumber()).WillRepeatedly([&](auto, auto) { w = 99; }); + EXPECT_CALL(cache, hasBinaryShader(_)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/) { w = 8; return true; }); + EXPECT_CALL(cache, getBinaryShaderSize(_)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/) { w = 22; return 3u; }); + EXPECT_CALL(cache, getBinaryShaderFormat(_)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/) { w = 800; return binaryShaderFormatId_t{ 3 }; }); + EXPECT_CALL(cache, shouldBinaryShaderBeCached(_, _)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/, auto /*unused*/) { w = 12; return true; }); + EXPECT_CALL(cache, getBinaryShaderData(_, _, _)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/, auto /*unused*/, auto /*unused*/) { w = 120; }); + EXPECT_CALL(cache, storeBinaryShader(_, _, _, _, _)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/, auto /*unused*/, auto /*unused*/, auto /*unused*/, auto /*unused*/) { w = 60; }); + EXPECT_CALL(cache, binaryShaderUploaded(_, _)).Times(AnyNumber()).WillRepeatedly([&](auto /*unused*/, auto /*unused*/) { w = 99; }); startBarrier.init_wait_for_num(3); std::thread t1(randomlyExecuteFunctions); diff --git a/tests/unittests/renderer/ramses-renderer/BinaryShaderCacheTest.cpp b/tests/unittests/renderer/ramses-renderer/BinaryShaderCacheTest.cpp new file mode 100644 index 000000000..91e43eb7f --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/BinaryShaderCacheTest.cpp @@ -0,0 +1,296 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/renderer/BinaryShaderCache.h" +#include "impl/BinaryShaderCacheImpl.h" +#include "gtest/gtest.h" +#include "internal/RendererLib/Types.h" +#include "internal/Core/Utils/File.h" +#include + +#include +#include + +template +std::array make_byte_array(Ts&&... args) noexcept +{ + return {std::byte(std::forward(args))...}; +} + +namespace ramses::internal +{ + class ABinaryShaderCache : public testing::Test + { + public: + ABinaryShaderCache() + : m_binaryShaderFilePath("test.binaryshader") + { + + } + + ~ABinaryShaderCache() override + { + File binaryShaderFile(m_binaryShaderFilePath); + if (binaryShaderFile.exists()) + { + binaryShaderFile.remove(); + } + } + + void createTestFile() + { + ramses::BinaryShaderCache cache; + + const auto shaderData1 = make_byte_array(12u, 34u, 56u, 78u); + const ramses::binaryShaderFormatId_t format1{ 123u }; + const ramses::effectId_t effectHash1 = {11u, 0}; + cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData1.data(), static_cast(shaderData1.size()), format1); + + const auto shaderData2 = make_byte_array(13u, 14u, 66u, 7u, 89u, 10u); + const ramses::binaryShaderFormatId_t format2{ 112u }; + const ramses::effectId_t effectHash2 = {12u, 0}; + cache.storeBinaryShader(effectHash2, ramses::sceneId_t(2u), shaderData2.data(), static_cast(shaderData2.size()), format2); + + cache.saveToFile(m_binaryShaderFilePath.c_str()); + } + + void enlargeTestFile() + { + ramses::internal::File file(m_binaryShaderFilePath.c_str()); + size_t fileSize(0); + EXPECT_TRUE(file.getSizeInBytes(fileSize)); + EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); + EXPECT_TRUE(file.seek(fileSize, File::SeekOrigin::BeginningOfFile)); + char data(33); + EXPECT_TRUE(file.write(&data, sizeof(data))); + } + + void truncateTestFile(int32_t size) + { + ramses::internal::File file(m_binaryShaderFilePath.c_str()); + EXPECT_TRUE(file.open(File::Mode::ReadOnlyBinary)); + size_t fileSize(0); + EXPECT_TRUE(file.getSizeInBytes(fileSize)); + std::vector data(fileSize); + + size_t numReadBytes = 0u; + EXPECT_EQ(EStatus::Ok, file.read(data.data(), fileSize, numReadBytes)); + + file.close(); + EXPECT_TRUE(file.open(File::Mode::WriteNewBinary)); + + int32_t newSize = (size < 0) ? static_cast(fileSize) + size : size; + + EXPECT_TRUE(file.write(data.data(), newSize)); + } + + void corruptTestFile() + { + ramses::internal::File file(m_binaryShaderFilePath); + size_t fileSize(0); + EXPECT_TRUE(file.getSizeInBytes(fileSize)); + EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); + const auto offset = sizeof(BinaryShaderCacheImpl::FileHeader); + EXPECT_TRUE(file.seek(offset, File::SeekOrigin::BeginningOfFile)); + char data = 0; + size_t numBytesRead = 0u; + EXPECT_EQ(EStatus::Ok, file.read(&data, sizeof(data), numBytesRead)); + EXPECT_TRUE(file.seek(offset, File::SeekOrigin::BeginningOfFile)); + data++; + EXPECT_TRUE(file.write(&data, sizeof(data))); + } + + void corruptVersionInTestFile() + { + ramses::internal::File file(m_binaryShaderFilePath); + size_t fileSize(0); + EXPECT_TRUE(file.getSizeInBytes(fileSize)); + EXPECT_TRUE(file.open(File::Mode::WriteExistingBinary)); + const auto transportVersionOffset = offsetof(BinaryShaderCacheImpl::FileHeader, transportVersion); + EXPECT_TRUE(file.seek(transportVersionOffset, File::SeekOrigin::BeginningOfFile)); + char data = 0; + size_t numBytesRead = 0u; + EXPECT_EQ(EStatus::Ok, file.read(&data, sizeof(data), numBytesRead)); + EXPECT_TRUE(file.seek(transportVersionOffset, File::SeekOrigin::BeginningOfFile)); + data++; + EXPECT_TRUE(file.write(&data, sizeof(data))); + } + + protected: + ramses::BinaryShaderCache m_cache; + const std::string m_binaryShaderFilePath; + }; + + TEST_F(ABinaryShaderCache, canStoreAndGetBinaryShader) + { + const auto shaderData = make_byte_array(12u, 34u, 56u, 78u); + const auto shaderDataSize = static_cast(shaderData.size()); + const ramses::binaryShaderFormatId_t format{ 123u }; + const ramses::effectId_t effectHash1 = { 11u, 0 }; + m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData.data(), shaderDataSize, format); + + m_cache.deviceSupportsBinaryShaderFormats(&format, 1u); + EXPECT_TRUE(m_cache.hasBinaryShader(effectHash1)); + EXPECT_EQ(shaderDataSize, m_cache.getBinaryShaderSize(effectHash1)); + EXPECT_EQ(format, m_cache.getBinaryShaderFormat(effectHash1)); + + std::vector dataRead(shaderDataSize); + m_cache.getBinaryShaderData(effectHash1, &dataRead.front(), shaderDataSize); + + // make sure the data value is the same. + for (uint32_t index = 0; index < shaderDataSize; index++) + { + EXPECT_TRUE(shaderData[index] == dataRead[index]); + } + } + + TEST_F(ABinaryShaderCache, reportsNoShaderAvailableIfShaderCachedButWithWrongFormat) + { + const auto shaderData = make_byte_array(12u, 34u, 56u, 78u); + const ramses::binaryShaderFormatId_t format{ 123u }; + const ramses::effectId_t effectHash1 = { 11u, 0 }; + m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData.data(), uint32_t(shaderData.size()), format); + + const ramses::binaryShaderFormatId_t supportedFormat{ 124u }; + m_cache.deviceSupportsBinaryShaderFormats(&supportedFormat, 1u); + EXPECT_FALSE(m_cache.hasBinaryShader(effectHash1)); + } + + TEST_F(ABinaryShaderCache, reportsNoShaderAvailableIfShaderNotCachedAndDeviceReportsFormatZeroSupported) // zero format is used as invalid format in cache impl + { + const ramses::binaryShaderFormatId_t supportedFormat{ 0u }; + m_cache.deviceSupportsBinaryShaderFormats(&supportedFormat, 1u); + const ramses::effectId_t effectHash1 = { 11u, 0 }; + EXPECT_FALSE(m_cache.hasBinaryShader(effectHash1)); + } + + TEST_F(ABinaryShaderCache, canReturnFalseWhenBinaryShaderNotInCache) + { + const auto shaderData = make_byte_array(12u, 34u, 56u, 78u); + const auto shaderDataSize = static_cast(shaderData.size()); + const ramses::binaryShaderFormatId_t format{ 123u }; + const ramses::effectId_t effectHash1 = { 11u, 0 }; + m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData.data(), shaderDataSize, format); + + ramses::effectId_t nonExistEffectHash = { 13u, 0 }; + EXPECT_FALSE(m_cache.hasBinaryShader(nonExistEffectHash)); + EXPECT_EQ(ramses::binaryShaderFormatId_t{ 0u }, m_cache.getBinaryShaderFormat(nonExistEffectHash)); + EXPECT_EQ(0u, m_cache.getBinaryShaderSize(nonExistEffectHash)); + } + + TEST_F(ABinaryShaderCache, canSaveToAndLoadFromBinaryShaderFile) + { + const auto shaderData1 = make_byte_array(12u, 34u, 56u, 78u); + const auto shaderDataSize1 = static_cast(shaderData1.size()); + const ramses::binaryShaderFormatId_t format1{ 123u }; + const ramses::effectId_t effectHash1 = { 11u, 0 }; + m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData1.data(), shaderDataSize1, format1); + + const auto shaderData2 = make_byte_array(13u, 14u, 66u, 7u, 89u, 10u); + const auto shaderDataSize2 = static_cast(shaderData2.size()); + const ramses::binaryShaderFormatId_t format2{ 112u }; + const ramses::effectId_t effectHash2 = { 12u, 0 }; + m_cache.storeBinaryShader(effectHash2, ramses::sceneId_t(2u), shaderData2.data(), shaderDataSize2, format2); + + m_cache.saveToFile(m_binaryShaderFilePath.c_str()); + + ramses::BinaryShaderCache newCache; + EXPECT_TRUE(newCache.loadFromFile(m_binaryShaderFilePath.c_str())); + + // need to init with supported formats in order to report cached shaders + std::array supportedFormats = { format1, format2 }; + newCache.deviceSupportsBinaryShaderFormats(supportedFormats.data(), uint32_t(supportedFormats.size())); + + // check shader data 1 + EXPECT_TRUE(newCache.hasBinaryShader(effectHash1)); + EXPECT_EQ(format1, newCache.getBinaryShaderFormat(effectHash1)); + + const uint32_t shaderDataSize1Read = newCache.getBinaryShaderSize(effectHash1); + EXPECT_EQ(shaderDataSize1, shaderDataSize1Read); + + std::vector shaderData1Read(shaderDataSize1Read); + newCache.getBinaryShaderData(effectHash1, &shaderData1Read.front(), shaderDataSize1Read); + for (uint32_t index = 0; index < shaderDataSize1Read; index++) + { + EXPECT_EQ(shaderData1[index], shaderData1Read[index]); + } + + // check shader data 2 + EXPECT_TRUE(newCache.hasBinaryShader(effectHash2)); + EXPECT_EQ(format2, newCache.getBinaryShaderFormat(effectHash2)); + + const uint32_t shaderDataSize2Read = newCache.getBinaryShaderSize(effectHash2); + EXPECT_EQ(shaderDataSize2, shaderDataSize2Read); + + std::vector shaderData2Read(shaderDataSize2Read); + newCache.getBinaryShaderData(effectHash2, &shaderData2Read.front(), shaderDataSize2Read); + for (uint32_t index = 0; index < shaderDataSize2Read; index++) + { + EXPECT_EQ(shaderData2[index], shaderData2Read[index]); + } + } + + TEST_F(ABinaryShaderCache, canReturnFalseWhenTryToLoadFromNonExistFile) + { + EXPECT_FALSE(m_cache.loadFromFile("NonExistFile.binaryshader")); + } + + TEST_F(ABinaryShaderCache, reportsSuccessOnCorrectFile) + { + createTestFile(); + EXPECT_TRUE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); + } + + TEST_F(ABinaryShaderCache, reportsFailOnFileToLarge) + { + createTestFile(); + enlargeTestFile(); + EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); + } + + TEST_F(ABinaryShaderCache, reportsFailOnFileToShort) + { + createTestFile(); + truncateTestFile(-1); + EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); + } + + TEST_F(ABinaryShaderCache, reportsFailOnFileShorterThanHeader) + { + createTestFile(); + truncateTestFile(sizeof(BinaryShaderCacheImpl::FileHeader) - 1); + EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); + } + + TEST_F(ABinaryShaderCache, reportsFailOnNotMatchingChecksum) + { + createTestFile(); + corruptTestFile(); + EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); + } + + TEST_F(ABinaryShaderCache, reportsFailOnInvalidFileVersion) + { + createTestFile(); + corruptVersionInTestFile(); + EXPECT_FALSE(m_cache.loadFromFile(m_binaryShaderFilePath.c_str())); + } + + TEST_F(ABinaryShaderCache, handlesDoubleStoreProperly) + { + const auto shaderData = make_byte_array(12u, 34u, 56u, 78u); + const auto shaderDataSize = static_cast(shaderData.size()); + const ramses::binaryShaderFormatId_t format{ 123u }; + const ramses::effectId_t effectHash1 = { 11u, 0 }; + m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData.data(), shaderDataSize, format); + m_cache.deviceSupportsBinaryShaderFormats(&format, 1u); + EXPECT_TRUE(m_cache.hasBinaryShader(effectHash1)); + m_cache.storeBinaryShader(effectHash1, ramses::sceneId_t(1u), shaderData.data(), shaderDataSize, format); + EXPECT_TRUE(m_cache.hasBinaryShader(effectHash1)); + } +} diff --git a/tests/unittests/renderer/ramses-renderer/CMakeLists.txt b/tests/unittests/renderer/ramses-renderer/CMakeLists.txt new file mode 100644 index 000000000..1368335b1 --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/CMakeLists.txt @@ -0,0 +1,22 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME ramses-renderer-test + TYPE BINARY + SRC_FILES *.h + *.cpp + DEPENDENCIES ramses-client + ramses-renderer + ramses-gmock-main + renderer-test-common +) + +makeTestFromTarget( + TARGET ramses-renderer-test + SUFFIX UNITTEST) diff --git a/tests/unittests/renderer/ramses-renderer/DisplayConfigTest.cpp b/tests/unittests/renderer/ramses-renderer/DisplayConfigTest.cpp new file mode 100644 index 000000000..2ca1b3f9a --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/DisplayConfigTest.cpp @@ -0,0 +1,428 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ramses/framework/ValidationReport.h" +#include "ramses/renderer/DisplayConfig.h" +#include "impl/DisplayConfigImpl.h" +#include "impl/ValidationReportImpl.h" +#include "gmock/gmock.h" + +namespace ramses::internal +{ + class ADisplayConfig : public ::testing::Test + { + protected: + ramses::DisplayConfig config; + }; + + TEST_F(ADisplayConfig, hasDefaultValuesUponConstruction) + { + const ramses::internal::DisplayConfig defaultDisplayConfig; + const ramses::internal::DisplayConfig& displayConfig = config.impl().getInternalDisplayConfig(); + + EXPECT_EQ(defaultDisplayConfig.getWindowPositionX(), displayConfig.getWindowPositionX()); + EXPECT_EQ(defaultDisplayConfig.getWindowPositionY(), displayConfig.getWindowPositionY()); + EXPECT_EQ(defaultDisplayConfig.getDesiredWindowWidth(), displayConfig.getDesiredWindowWidth()); + EXPECT_EQ(defaultDisplayConfig.getDesiredWindowHeight(), displayConfig.getDesiredWindowHeight()); + + EXPECT_EQ(defaultDisplayConfig.getFullscreenState(), displayConfig.getFullscreenState()); + + EXPECT_EQ(defaultDisplayConfig.getAntialiasingSampleCount(), displayConfig.getAntialiasingSampleCount()); + EXPECT_EQ(defaultDisplayConfig.getStartVisibleIvi(), displayConfig.getStartVisibleIvi()); + + EXPECT_EQ(defaultDisplayConfig.getGPUMemoryCacheSize(), displayConfig.getGPUMemoryCacheSize()); + EXPECT_EQ(defaultDisplayConfig.getClearColor(), displayConfig.getClearColor()); + + EXPECT_TRUE(defaultDisplayConfig.getWaylandDisplay().empty()); + + EXPECT_FALSE(defaultDisplayConfig.getWaylandIviSurfaceID().isValid()); + EXPECT_FALSE(defaultDisplayConfig.getWaylandIviLayerID().isValid()); + + EXPECT_FALSE(defaultDisplayConfig.getWindowsWindowHandle().isValid()); + EXPECT_FALSE(defaultDisplayConfig.getX11WindowHandle().isValid()); + + EXPECT_EQ(defaultDisplayConfig.getWaylandSocketEmbedded(), displayConfig.getWaylandSocketEmbedded()); + EXPECT_EQ(defaultDisplayConfig.getWaylandSocketEmbeddedGroup(), displayConfig.getWaylandSocketEmbeddedGroup()); + EXPECT_EQ(defaultDisplayConfig.getWaylandSocketEmbeddedFD(), displayConfig.getWaylandSocketEmbeddedFD()); + EXPECT_EQ(defaultDisplayConfig.getPlatformRenderNode(), displayConfig.getPlatformRenderNode()); + } + + TEST_F(ADisplayConfig, setsDeviceType) + { + EXPECT_TRUE(config.setDeviceType(ramses::EDeviceType::GL_4_2)); + EXPECT_EQ(ramses::EDeviceType::GL_4_2, config.impl().getInternalDisplayConfig().getDeviceType()); + } + + TEST_F(ADisplayConfig, setsWindowType) + { + EXPECT_TRUE(config.setWindowType(ramses::EWindowType::Wayland_IVI)); + EXPECT_EQ(ramses::EWindowType::Wayland_IVI, config.impl().getInternalDisplayConfig().getWindowType()); + } + + TEST_F(ADisplayConfig, setsFullscreenState) + { + EXPECT_TRUE(config.setWindowFullscreen(true)); + EXPECT_TRUE(config.impl().getInternalDisplayConfig().getFullscreenState()); + EXPECT_TRUE(config.isWindowFullscreen()); + + EXPECT_TRUE(config.setWindowFullscreen(false)); + EXPECT_FALSE(config.impl().getInternalDisplayConfig().getFullscreenState()); + EXPECT_FALSE(config.isWindowFullscreen()); + } + + TEST_F(ADisplayConfig, setsWindowRect) + { + EXPECT_TRUE(config.setWindowRectangle(15, 16, 123u, 345u)); + int32_t x = 0; + int32_t y = 0; + uint32_t width = 0; + uint32_t height = 0; + config.getWindowRectangle(x, y, width, height); + EXPECT_EQ(15, x); + EXPECT_EQ(16, y); + EXPECT_EQ(123u, width); + EXPECT_EQ(345u, height); + } + + TEST_F(ADisplayConfig, failsToSetInvalidWindowRect) + { + EXPECT_TRUE(config.setWindowRectangle(15, 16, 1u, 2u)); + + EXPECT_FALSE(config.setWindowRectangle(15, 16, 0u, 2u)); + EXPECT_FALSE(config.setWindowRectangle(15, 16, 1u, 0u)); + EXPECT_FALSE(config.setWindowRectangle(15, 16, 0u, 0u)); + + EXPECT_EQ(15, config.impl().getInternalDisplayConfig().getWindowPositionX()); + EXPECT_EQ(16, config.impl().getInternalDisplayConfig().getWindowPositionY()); + EXPECT_EQ(1u, config.impl().getInternalDisplayConfig().getDesiredWindowWidth()); + EXPECT_EQ(2u, config.impl().getInternalDisplayConfig().getDesiredWindowHeight()); + } + + TEST_F(ADisplayConfig, failsToSetUnsupportedMultisampling) + { + EXPECT_FALSE(config.setMultiSampling(0u)); + EXPECT_FALSE(config.setMultiSampling(3u)); + EXPECT_FALSE(config.setMultiSampling(5u)); + EXPECT_FALSE(config.setMultiSampling(6u)); + EXPECT_FALSE(config.setMultiSampling(7u)); + EXPECT_FALSE(config.setMultiSampling(9u)); + + EXPECT_EQ(1u, config.impl().getInternalDisplayConfig().getAntialiasingSampleCount()); + } + + TEST_F(ADisplayConfig, setsAndGetsMultisampling) + { + EXPECT_TRUE(config.setMultiSampling(2u)); + + EXPECT_EQ(2u, config.impl().getInternalDisplayConfig().getAntialiasingSampleCount()); + + uint32_t sampleCount = 0; + config.getMultiSamplingSamples(sampleCount); + EXPECT_EQ(2u, sampleCount); + } + + TEST_F(ADisplayConfig, setsWindowIVIVisible) + { + EXPECT_TRUE(config.setWindowIviVisible()); + EXPECT_TRUE(config.impl().getInternalDisplayConfig().getStartVisibleIvi()); + } + + TEST_F(ADisplayConfig, setsAndGetsWaylandDisplay) + { + EXPECT_TRUE(config.setWaylandDisplay("xxx")); + EXPECT_EQ("xxx", config.getWaylandDisplay()); + EXPECT_EQ("xxx", config.impl().getInternalDisplayConfig().getWaylandDisplay()); + } + + TEST_F(ADisplayConfig, setsAndGetsWaylandIviSurfaceId) + { + EXPECT_TRUE(config.setWaylandIviSurfaceID(ramses::waylandIviSurfaceId_t(25))); + EXPECT_EQ(ramses::waylandIviSurfaceId_t(25), config.getWaylandIviSurfaceID()); + EXPECT_EQ(ramses::internal::WaylandIviSurfaceId(25), config.impl().getInternalDisplayConfig().getWaylandIviSurfaceID()); + } + + TEST_F(ADisplayConfig, setsAndGetsWaylandIviLayerId) + { + EXPECT_TRUE(config.setWaylandIviLayerID(ramses::waylandIviLayerId_t(36))); + EXPECT_EQ(ramses::waylandIviLayerId_t(36), config.getWaylandIviLayerID()); + EXPECT_EQ(ramses::internal::WaylandIviLayerId(36), config.impl().getInternalDisplayConfig().getWaylandIviLayerID()); + } + + TEST_F(ADisplayConfig, setsAndGetsX11WindowHandle) + { + EXPECT_TRUE(config.setX11WindowHandle(ramses::X11WindowHandle(42u))); + EXPECT_EQ(ramses::X11WindowHandle(42u), config.getX11WindowHandle()); + EXPECT_EQ(ramses::internal::X11WindowHandle(42), config.impl().getInternalDisplayConfig().getX11WindowHandle()); + } + + TEST_F(ADisplayConfig, IsValidUponConstruction) + { + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeWindowsAndDeviceTypeNotGLES30) + { + config.setWindowType(ramses::EWindowType::Windows); + config.setDeviceType(ramses::EDeviceType::GL_4_5); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeAndroidAndDeviceTypeGLES30) + { + config.setWindowType(ramses::EWindowType::Android); + config.setDeviceType(ramses::EDeviceType::GLES_3_0); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeIOSAndDeviceTypeGLES30) + { + config.setWindowType(ramses::EWindowType::iOS); + config.setDeviceType(ramses::EDeviceType::GLES_3_0); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeAndroidAndExternalAndroidWindowHandleProvided) + { + std::uintptr_t dummyVoidPointer{ 1u }; + config.setAndroidNativeWindow(reinterpret_cast(dummyVoidPointer)); + config.setWindowType(ramses::EWindowType::Android); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeIOSAndExternalIOSWindowHandleProvided) + { + std::uintptr_t dummyVoidPointer{ 1u }; + config.setIOSNativeWindow(ramses::IOSNativeWindowPtr{ reinterpret_cast(dummyVoidPointer) }); + config.setWindowType(ramses::EWindowType::iOS); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeWindowsAndExternalWindowsWindowHandleProvided) + { + std::uintptr_t dummyVoidPointer{ 1u }; + config.setWindowsWindowHandle(reinterpret_cast(dummyVoidPointer)); + config.setWindowType(ramses::EWindowType::Windows); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, IsValidIfWindowTypeX11AndExternalX11WindowHandleProvided) + { + ramses::X11WindowHandle dummyX11Window{ 1u }; + config.setX11WindowHandle(dummyX11Window); + config.setWindowType(ramses::EWindowType::X11); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasIssue()); + } + + TEST_F(ADisplayConfig, ValidationWarningIfCompositorEmbeddedCompositorDisplsayAndSocketFdAreSet) + { + config.setWaylandEmbeddedCompositingSocketFD(1234); + config.setWaylandEmbeddedCompositingSocketName("abc"); + ramses::ValidationReport report; + config.validate(report); + EXPECT_FALSE(report.hasError()); + EXPECT_TRUE(report.hasIssue()); + EXPECT_THAT(report.impl().toString(), ::testing::HasSubstr("WARNING: Competing settings for EmbeddedCompositor are set")); + } + + TEST_F(ADisplayConfig, ValidationErrorIfWindowTypeNotWindowsAndDeviceTypeNotGLES30) + { + config.setWindowType(ramses::EWindowType::Android); + config.setDeviceType(ramses::EDeviceType::GL_4_2); + ramses::ValidationReport report; + config.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_THAT(report.impl().toString(), ::testing::HasSubstr("ERROR: Selected window type supports only GL ES 3.0 device type")); + } + + TEST_F(ADisplayConfig, ValidationErrorIfExternalHandleWindowsSetAndTypeNotWindows) + { + std::uintptr_t dummyVoidPointer{ 1u }; + config.setWindowsWindowHandle(reinterpret_cast(dummyVoidPointer)); + config.setWindowType(ramses::EWindowType::X11); + ramses::ValidationReport report; + config.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_THAT(report.impl().toString(), ::testing::HasSubstr("ERROR: External Windows window handle is set and selected window type is not Windows")); + } + + TEST_F(ADisplayConfig, ValidationErrorIfExternalHandleAndroidSetAndTypeNotAndroid) + { + std::uintptr_t dummyVoidPointer{ 1u }; + config.setAndroidNativeWindow(reinterpret_cast(dummyVoidPointer)); + config.setWindowType(ramses::EWindowType::X11); + ramses::ValidationReport report; + config.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_THAT(report.impl().toString(), ::testing::HasSubstr("ERROR: External Android window handle is set and selected window type is not Android")); + } + + TEST_F(ADisplayConfig, ValidationErrorIfExternalHandleIOSSetAndTypeNotIOS) + { + std::uintptr_t dummyVoidPointer{ 1u }; + config.setIOSNativeWindow(ramses::IOSNativeWindowPtr{ reinterpret_cast(dummyVoidPointer) }); + config.setWindowType(ramses::EWindowType::X11); + ramses::ValidationReport report; + config.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_THAT(report.impl().toString(), ::testing::HasSubstr("ERROR: External iOS window handle is set and selected window type is not iOS")); + } + + TEST_F(ADisplayConfig, ValidationErrorIfExternalHandleX11SetAndTypeNotX11) + { + config.setX11WindowHandle(ramses::X11WindowHandle(123u)); + config.setWindowType(ramses::EWindowType::Android); + ramses::ValidationReport report; + config.validate(report); + EXPECT_TRUE(report.hasError()); + EXPECT_THAT(report.impl().toString(), ::testing::HasSubstr("ERROR: External X11 window handle is set and selected window type is not X11")); + } + + TEST_F(ADisplayConfig, CanBeCopyAndMoveConstructed) + { + config.setWindowFullscreen(true); + + ramses::DisplayConfig configCopy{ config }; + EXPECT_TRUE(configCopy.isWindowFullscreen()); + + ramses::DisplayConfig configMove{ std::move(config) }; + EXPECT_TRUE(configMove.isWindowFullscreen()); + } + + TEST_F(ADisplayConfig, CanBeCopyAndMoveAssigned) + { + config.setWindowFullscreen(true); + + ramses::DisplayConfig configCopy; + configCopy = config; + EXPECT_TRUE(configCopy.isWindowFullscreen()); + + ramses::DisplayConfig configMove; + configMove = std::move(config); + EXPECT_TRUE(configMove.isWindowFullscreen()); + } + + TEST_F(ADisplayConfig, CanBeSelfAssigned) + { + config.setWindowFullscreen(true); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + config = config; + EXPECT_TRUE(config.isWindowFullscreen()); + config = std::move(config); + // NOLINTNEXTLINE(bugprone-use-after-move) + EXPECT_TRUE(config.isWindowFullscreen()); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + TEST_F(ADisplayConfig, setClearColor) + { + const float red = 0.1f; + const float green = 0.2f; + const float blue = 0.3f; + const float alpha = 0.4f; + EXPECT_TRUE(config.setClearColor({red, green, blue, alpha})); + + const glm::vec4& clearColor = config.impl().getInternalDisplayConfig().getClearColor(); + EXPECT_EQ(clearColor.r, red); + EXPECT_EQ(clearColor.g, green); + EXPECT_EQ(clearColor.b, blue); + EXPECT_EQ(clearColor.a, alpha); + } + + TEST_F(ADisplayConfig, setDepthStencilBufferType) + { + EXPECT_TRUE(config.setDepthStencilBufferType(ramses::EDepthBufferType::Depth)); + EXPECT_EQ(ramses::EDepthBufferType::Depth, config.impl().getInternalDisplayConfig().getDepthStencilBufferType()); + } + + TEST_F(ADisplayConfig, setAsyncEffectUploadEnabled) + { + EXPECT_TRUE(config.setAsyncEffectUploadEnabled(false)); + EXPECT_FALSE(config.impl().getInternalDisplayConfig().isAsyncEffectUploadEnabled()); + } + + TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketGroup) + { + config.setWaylandEmbeddedCompositingSocketGroup("permissionGroup"); + EXPECT_EQ("permissionGroup", config.impl().getWaylandSocketEmbeddedGroup()); + } + + TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketPermissions) + { + config.setWaylandEmbeddedCompositingSocketPermissions(0660); + EXPECT_EQ(0660u, config.impl().getWaylandSocketEmbeddedPermissions()); + } + + TEST_F(ADisplayConfig, cannotSetInvalidEmbeddedCompositingSocketPermissions) + { + EXPECT_FALSE(config.setWaylandEmbeddedCompositingSocketPermissions(0)); + } + + TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketname) + { + config.setWaylandEmbeddedCompositingSocketName("wayland-x123"); + EXPECT_EQ("wayland-x123", config.getWaylandEmbeddedCompositingSocketName()); + } + + TEST_F(ADisplayConfig, canSetEmbeddedCompositingSocketFD) + { + config.setWaylandEmbeddedCompositingSocketFD(23); + EXPECT_EQ(23, config.impl().getWaylandSocketEmbeddedFD()); + } + + TEST_F(ADisplayConfig, canSetPlatformRenderNode) + { + config.setPlatformRenderNode("abcd"); + EXPECT_EQ("abcd", config.impl().getPlatformRenderNode()); + } + + TEST_F(ADisplayConfig, canSetSwapInterval) + { + EXPECT_EQ(-1, config.impl().getSwapInterval()); + EXPECT_TRUE(config.setSwapInterval(0)); + EXPECT_EQ(0, config.impl().getSwapInterval()); + } + + TEST_F(ADisplayConfig, canSetScenePriority) + { + EXPECT_EQ(0, config.impl().getScenePriority(ramses::sceneId_t(551))); + EXPECT_TRUE(config.setScenePriority(ramses::sceneId_t(551), 4)); + EXPECT_EQ(4, config.impl().getScenePriority(ramses::sceneId_t(551))); + } + + TEST_F(ADisplayConfig, canSetResourceUploadBatchSize) + { + EXPECT_EQ(10u, config.impl().getResourceUploadBatchSize()); + EXPECT_TRUE(config.setResourceUploadBatchSize(1)); + EXPECT_EQ(1u, config.impl().getResourceUploadBatchSize()); + EXPECT_FALSE(config.setResourceUploadBatchSize(0)); + EXPECT_EQ(1u, config.impl().getResourceUploadBatchSize()); + } +} diff --git a/renderer/ramses-renderer-impl/test/RamsesRendererDispatchTest.cpp b/tests/unittests/renderer/ramses-renderer/RamsesRendererDispatchTest.cpp similarity index 74% rename from renderer/ramses-renderer-impl/test/RamsesRendererDispatchTest.cpp rename to tests/unittests/renderer/ramses-renderer/RamsesRendererDispatchTest.cpp index 122acd361..b2d7259e2 100644 --- a/renderer/ramses-renderer-impl/test/RamsesRendererDispatchTest.cpp +++ b/tests/unittests/renderer/ramses-renderer/RamsesRendererDispatchTest.cpp @@ -7,22 +7,21 @@ // ------------------------------------------------------------------------- #include -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" - -#include "RamsesFrameworkImpl.h" -#include "RamsesRendererImpl.h" -#include "RendererLib/RendererCommands.h" -#include "RendererLib/DisplayEventHandler.h" -#include "RendererLib/EKeyModifier.h" -#include "Scene/ClientScene.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" + +#include "impl/RamsesFrameworkImpl.h" +#include "impl/RamsesRendererImpl.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/RendererLib/DisplayEventHandler.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/SceneGraph/Scene/ClientScene.h" #include "RendererEventTestHandler.h" -#include "RamsesRendererUtils.h" //This is needed to abstract from a specific rendering platform #include "PlatformFactoryMock.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -32,7 +31,7 @@ namespace ramses_internal ARamsesRendererDispatch() : m_renderer(*m_framework.createRenderer(ramses::RendererConfig())) { - m_renderer.m_impl.getDisplayDispatcher().injectPlatformFactory(std::make_unique()); + m_renderer.impl().getDisplayDispatcher().injectPlatformFactory(std::make_unique()); m_renderer.setLoopMode(ramses::ELoopMode::UpdateOnly); } @@ -40,7 +39,6 @@ namespace ramses_internal { //Create a display ramses::DisplayConfig displayConfig; - EXPECT_EQ(ramses::StatusOK, displayConfig.validate()); return m_renderer.createDisplay(displayConfig); } @@ -60,10 +58,10 @@ namespace ramses_internal void updateAndDispatch(RendererEventTestHandler& eventHandler, uint32_t loops = 1u) { - EXPECT_EQ(ramses::StatusOK, m_renderer.flush()); + EXPECT_TRUE(m_renderer.flush()); for (uint32_t i = 0u; i < loops; ++i) doUpdateLoop(); - EXPECT_EQ(ramses::StatusOK, m_renderer.dispatchEvents(eventHandler)); + EXPECT_TRUE(m_renderer.dispatchEvents(eventHandler)); } protected: @@ -80,9 +78,9 @@ namespace ramses_internal TEST_F(ARamsesRendererDispatch, generatesFAILEventForDisplayCreation) { constexpr ramses::displayId_t displayId{ 123u }; - RendererEvent evt{ ramses_internal::ERendererEventType::DisplayCreateFailed }; + RendererEvent evt{ ERendererEventType::DisplayCreateFailed }; evt.displayHandle = DisplayHandle{ displayId.getValue() }; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); m_handler.expectDisplayCreated(displayId, ramses::ERendererEventResult::Failed); } @@ -91,7 +89,7 @@ namespace ramses_internal { const ramses::displayId_t displayId = createDisplayAndExpectResult(); - EXPECT_EQ(ramses::StatusOK, m_renderer.destroyDisplay(displayId)); + EXPECT_TRUE(m_renderer.destroyDisplay(displayId)); updateAndDispatch(m_handler); m_handler.expectDisplayDestroyed(displayId, ramses::ERendererEventResult::Ok); } @@ -99,7 +97,7 @@ namespace ramses_internal TEST_F(ARamsesRendererDispatch, generatesFAILEventForInvalidDisplayDestruction) { const ramses::displayId_t displayId(1u); - EXPECT_EQ(ramses::StatusOK, m_renderer.destroyDisplay(displayId)); + EXPECT_TRUE(m_renderer.destroyDisplay(displayId)); updateAndDispatch(m_handler); m_handler.expectDisplayDestroyed(displayId, ramses::ERendererEventResult::Failed); @@ -134,7 +132,7 @@ namespace ramses_internal updateAndDispatch(m_handler); m_handler.expectOffscreenBufferCreated(displayId, bufferId, ramses::ERendererEventResult::Ok); - EXPECT_EQ(ramses::StatusOK, m_renderer.destroyOffscreenBuffer(displayId, bufferId)); + EXPECT_TRUE(m_renderer.destroyOffscreenBuffer(displayId, bufferId)); updateAndDispatch(m_handler); m_handler.expectOffscreenBufferDestroyed(displayId, bufferId, ramses::ERendererEventResult::Ok); } @@ -143,7 +141,7 @@ namespace ramses_internal { ramses::displayId_t displayId(0u); const ramses::displayBufferId_t bufferId(0u); - EXPECT_EQ(ramses::StatusOK, m_renderer.destroyOffscreenBuffer(displayId, bufferId)); + EXPECT_TRUE(m_renderer.destroyOffscreenBuffer(displayId, bufferId)); updateAndDispatch(m_handler); m_handler.expectOffscreenBufferDestroyed(displayId, bufferId, ramses::ERendererEventResult::Failed); } @@ -177,7 +175,7 @@ namespace ramses_internal updateAndDispatch(m_handler); m_handler.expectExternalBufferCreated(displayId, bufferId, DeviceMock::FakeExternalTextureGlId, ramses::ERendererEventResult::Ok); - EXPECT_EQ(ramses::StatusOK, m_renderer.destroyExternalBuffer(displayId, bufferId)); + EXPECT_TRUE(m_renderer.destroyExternalBuffer(displayId, bufferId)); updateAndDispatch(m_handler); m_handler.expectExternalBufferDestroyed(displayId, bufferId, ramses::ERendererEventResult::Ok); } @@ -186,72 +184,72 @@ namespace ramses_internal { ramses::displayId_t displayId(0u); const ramses::externalBufferId_t bufferId(0u); - EXPECT_EQ(ramses::StatusOK, m_renderer.destroyExternalBuffer(displayId, bufferId)); + EXPECT_TRUE(m_renderer.destroyExternalBuffer(displayId, bufferId)); updateAndDispatch(m_handler); m_handler.expectExternalBufferDestroyed(displayId, bufferId, ramses::ERendererEventResult::Failed); } TEST_F(ARamsesRendererDispatch, generatesEventForWindowClosed) { - ramses_internal::RendererEvent evt{ ramses_internal::ERendererEventType::WindowClosed }; + RendererEvent evt{ ERendererEventType::WindowClosed }; evt.displayHandle = DisplayHandle{ 2u }; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); m_handler.expectWindowClosed(ramses::displayId_t{ 2u }); } TEST_F(ARamsesRendererDispatch, generatesEventForWindowResized) { - ramses_internal::RendererEvent evt{ ramses_internal::ERendererEventType::WindowResizeEvent }; + RendererEvent evt{ ERendererEventType::WindowResizeEvent }; evt.displayHandle = DisplayHandle{ 2u }; evt.resizeEvent.width = 100; evt.resizeEvent.height = 200; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); m_handler.expectWindowResized(ramses::displayId_t{ 2u }, 100, 200); } TEST_F(ARamsesRendererDispatch, generatesEventForWindowMoved) { - ramses_internal::RendererEvent evt{ ramses_internal::ERendererEventType::WindowMoveEvent }; + RendererEvent evt{ ERendererEventType::WindowMoveEvent }; evt.displayHandle = DisplayHandle{ 2u }; evt.moveEvent.posX = 100; evt.moveEvent.posY = 200; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); m_handler.expectWindowMoved(ramses::displayId_t{ 2u }, 100, 200); } TEST_F(ARamsesRendererDispatch, generatesEventForKeyPressed) { - ramses_internal::RendererEvent evt{ ramses_internal::ERendererEventType::WindowKeyEvent }; + RendererEvent evt{ ERendererEventType::WindowKeyEvent }; evt.displayHandle = DisplayHandle{ 2u }; - evt.keyEvent.type = ramses_internal::EKeyEventType_Pressed; - evt.keyEvent.modifier = ramses_internal::EKeyModifier_Ctrl | ramses_internal::EKeyModifier_Shift; - evt.keyEvent.keyCode = ramses_internal::EKeyCode_E; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + evt.keyEvent.type = EKeyEvent::Pressed; + evt.keyEvent.modifier = EKeyModifier::Ctrl | EKeyModifier::Shift; + evt.keyEvent.keyCode = ramses::EKeyCode_E; + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); - m_handler.expectKeyEvent(ramses::displayId_t{ 2u }, ramses::EKeyEvent::Pressed, ramses::EKeyModifier_Ctrl | ramses::EKeyModifier_Shift, ramses::EKeyCode_E); + m_handler.expectKeyEvent(ramses::displayId_t{ 2u }, ramses::EKeyEvent::Pressed, ramses::EKeyModifier::Ctrl | ramses::EKeyModifier::Shift, ramses::EKeyCode_E); } TEST_F(ARamsesRendererDispatch, generatesEventForKeyReleased) { - ramses_internal::RendererEvent evt{ ramses_internal::ERendererEventType::WindowKeyEvent }; + RendererEvent evt{ ERendererEventType::WindowKeyEvent }; evt.displayHandle = DisplayHandle{ 2u }; - evt.keyEvent.type = ramses_internal::EKeyEventType_Released; - evt.keyEvent.modifier = ramses_internal::EKeyModifier_Ctrl | ramses_internal::EKeyModifier_Shift; - evt.keyEvent.keyCode = ramses_internal::EKeyCode_F; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + evt.keyEvent.type = EKeyEvent::Released; + evt.keyEvent.modifier = EKeyModifier::Ctrl | EKeyModifier::Shift; + evt.keyEvent.keyCode = ramses::EKeyCode_F; + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); - m_handler.expectKeyEvent(ramses::displayId_t{ 2u }, ramses::EKeyEvent::Released, ramses::EKeyModifier_Ctrl | ramses::EKeyModifier_Shift, ramses::EKeyCode_F); + m_handler.expectKeyEvent(ramses::displayId_t{ 2u }, ramses::EKeyEvent::Released, ramses::EKeyModifier::Ctrl | ramses::EKeyModifier::Shift, ramses::EKeyCode_F); } TEST_F(ARamsesRendererDispatch, generatesEventsForMouseActionsOnDisplay) { - ramses_internal::RendererEvent evt{ ramses_internal::ERendererEventType::WindowMouseEvent }; + RendererEvent evt{ ERendererEventType::WindowMouseEvent }; evt.displayHandle = DisplayHandle{ 2u }; - evt.mouseEvent.type = ramses_internal::EMouseEventType_RightButtonDown; + evt.mouseEvent.type = EMouseEvent::RightButtonDown; evt.mouseEvent.pos = { 20, 30 }; - m_renderer.m_impl.getDisplayDispatcher().injectRendererEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(evt)); updateAndDispatch(m_handler); m_handler.expectMouseEvent(ramses::displayId_t{ 2u }, ramses::EMouseEvent::RightButtonDown, 20, 30); } diff --git a/renderer/ramses-renderer-impl/test/RamsesRendererPickingTest.cpp b/tests/unittests/renderer/ramses-renderer/RamsesRendererPickingTest.cpp similarity index 82% rename from renderer/ramses-renderer-impl/test/RamsesRendererPickingTest.cpp rename to tests/unittests/renderer/ramses-renderer/RamsesRendererPickingTest.cpp index 0d05d977f..49ece3441 100644 --- a/renderer/ramses-renderer-impl/test/RamsesRendererPickingTest.cpp +++ b/tests/unittests/renderer/ramses-renderer/RamsesRendererPickingTest.cpp @@ -7,34 +7,34 @@ // ------------------------------------------------------------------------- #include -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-framework-api/RendererSceneState.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/RendererSceneState.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" #include "RendererEventTestHandler.h" -#include "SceneAPI/SceneId.h" -#include "RamsesRendererImpl.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "impl/RamsesRendererImpl.h" #include "PlatformFactoryMock.h" #include "PlatformMock.h" -#include "ramses-client-api/OrthographicCamera.h" -#include "ramses-client-api/PerspectiveCamera.h" -#include "ramses-client-api/ArrayBuffer.h" -#include "ramses-client-api/PickableObject.h" +#include "ramses/client/OrthographicCamera.h" +#include "ramses/client/PerspectiveCamera.h" +#include "ramses/client/ArrayBuffer.h" +#include "ramses/client/PickableObject.h" -namespace ramses_internal +namespace ramses::internal { class RendererSceneControlEventHandler final : public ramses::RendererSceneControlEventHandlerEmpty { public: - void objectsPicked(ramses::sceneId_t, const ramses::pickableObjectId_t* pickedObjects, size_t pickedObjectsCount) override + void objectsPicked(ramses::sceneId_t /*sceneId*/, const ramses::pickableObjectId_t* pickedObjects, size_t pickedObjectsCount) override { for (size_t i = 0; i < pickedObjectsCount; ++i) - m_pickedObjects.push_back(std::move(pickedObjects[i])); + m_pickedObjects.push_back(pickedObjects[i]); } void expectObjectsPicked(const std::vector& expectedPickedObjs) @@ -51,17 +51,17 @@ namespace ramses_internal public: void createDisplay() { - m_renderer.m_impl.getDisplayDispatcher().injectPlatformFactory(std::make_unique()); + m_renderer.impl().getDisplayDispatcher().injectPlatformFactory(std::make_unique()); m_displayId = m_renderer.createDisplay({}); updateAndDispatch(); } void updateAndDispatch() { - EXPECT_EQ(ramses::StatusOK, m_renderer.flush()); - EXPECT_EQ(ramses::StatusOK, m_sceneControl.flush()); - EXPECT_EQ(ramses::StatusOK, m_renderer.doOneLoop()); - EXPECT_EQ(ramses::StatusOK, m_sceneControl.dispatchEvents(m_sceneControlEventHandler)); + EXPECT_TRUE(m_renderer.flush()); + EXPECT_TRUE(m_sceneControl.flush()); + EXPECT_TRUE(m_renderer.doOneLoop()); + EXPECT_TRUE(m_sceneControl.dispatchEvents(m_sceneControlEventHandler)); } protected: diff --git a/tests/unittests/renderer/ramses-renderer/RamsesRendererTest.cpp b/tests/unittests/renderer/ramses-renderer/RamsesRendererTest.cpp new file mode 100644 index 000000000..e02b73248 --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/RamsesRendererTest.cpp @@ -0,0 +1,935 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include "gmock/gmock.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" + +#include "internal/RendererLib/RendererCommands.h" +#include "impl/RamsesRendererImpl.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/PlatformEvent.h" +#include "RendererCommandVisitorMock.h" +#include "PlatformFactoryMock.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" + + +namespace ramses::internal +{ + using namespace testing; + + static ramses::RamsesRenderer* CreateRenderer(ramses::RamsesFramework& fw, const ramses::RendererConfig& config) + { + auto* createdRenderer = fw.createRenderer(config); + createdRenderer->impl().getDisplayDispatcher().injectPlatformFactory(std::make_unique()); + return createdRenderer; + } + + class SafeThreadWatchdogNotificationMock : public ramses::IThreadWatchdogNotification + { + public: + explicit SafeThreadWatchdogNotificationMock(ramses::internal::PlatformLock& lock) + : m_lock(lock) + { + } + + MOCK_METHOD(void, safe_notifyThread, (ramses::ERamsesThreadIdentifier)); + MOCK_METHOD(void, safe_registerThread, (ramses::ERamsesThreadIdentifier)); + MOCK_METHOD(void, safe_unregisterThread, (ramses::ERamsesThreadIdentifier)); + + private: + void notifyThread(ramses::ERamsesThreadIdentifier threadID) override + { + ramses::internal::PlatformGuard g(m_lock); + safe_notifyThread(threadID); + } + + void registerThread(ramses::ERamsesThreadIdentifier threadID) override + { + ramses::internal::PlatformGuard g(m_lock); + safe_registerThread(threadID); + } + + void unregisterThread(ramses::ERamsesThreadIdentifier threadID) override + { + ramses::internal::PlatformGuard g(m_lock); + safe_unregisterThread(threadID); + } + + ramses::internal::PlatformLock& m_lock; + }; + + + class ARamsesRenderer : public ::testing::Test + { + protected: + explicit ARamsesRenderer(const ramses::RendererConfig& rendererConfig = ramses::RendererConfig()) + : framework(ramses::RamsesFrameworkConfig{ramses::EFeatureLevel_Latest}) + , renderer(*CreateRenderer(framework, rendererConfig)) + , commandBuffer(renderer.impl().getPendingCommands()) + { + } + + ramses::displayId_t addDisplay() + { + //Create a display + ramses::DisplayConfig displayConfig; + return renderer.createDisplay(displayConfig); + } + + protected: + ramses::RamsesFramework framework; + ramses::RamsesRenderer& renderer; + const ramses::internal::RendererCommands& commandBuffer; + StrictMock cmdVisitor; + }; + + class ARamsesRendererWithDisplay : public ARamsesRenderer + { + void SetUp() override + { + displayId = addDisplay(); + EXPECT_NE(ramses::displayId_t::Invalid(), displayId); + renderer.flush(); + } + + void TearDown() override + { + renderer.destroyDisplay(displayId); + } + + protected: + ramses::displayId_t displayId; + }; + + class ARamsesRendererWithSystemCompositorController : public ARamsesRenderer + { + public: + ARamsesRendererWithSystemCompositorController() + : ARamsesRenderer(CreateRendererConfigWithSystemCompositor()) + { + } + + static ramses::RendererConfig CreateRendererConfigWithSystemCompositor() + { + ramses::RendererConfig config; + config.enableSystemCompositorControl(); + return config; + } + }; + + TEST_F(ARamsesRenderer, hasNoCommandsOnStartUp) + { + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRenderer, canOnlyGetOneSceneControlAPI) + { + const auto api = renderer.getSceneControlAPI(); + EXPECT_TRUE(api != nullptr); + EXPECT_EQ(api, renderer.getSceneControlAPI()); + EXPECT_EQ(api, renderer.getSceneControlAPI()); + } + + /* + * Display + */ + TEST_F(ARamsesRenderer, createsACommandForDisplayCreation) + { + const ramses::displayId_t displayId = addDisplay(); + EXPECT_NE(ramses::displayId_t::Invalid(), displayId); + + EXPECT_CALL(cmdVisitor, createDisplayContext(_, ramses::internal::DisplayHandle{ displayId.getValue() }, _)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRenderer, createsMultipleCommandsForMultipleDisplayCreation) + { + EXPECT_NE(ramses::displayId_t::Invalid(), addDisplay()); + EXPECT_NE(ramses::displayId_t::Invalid(), addDisplay()); + EXPECT_NE(ramses::displayId_t::Invalid(), addDisplay()); + + EXPECT_CALL(cmdVisitor, createDisplayContext(_, _, _)).Times(3); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForDisplayDestruction) + { + EXPECT_TRUE(renderer.destroyDisplay(displayId)); + EXPECT_CALL(cmdVisitor, destroyDisplayContext(_)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRenderer, canQueryDisplayFramebufferIDs) + { + const ramses::displayId_t displayId1 = addDisplay(); + const auto fbId1 = renderer.getDisplayFramebuffer(displayId1); + EXPECT_TRUE(fbId1.isValid()); + } + + TEST_F(ARamsesRenderer, displayFramebufferIDsAreUnique) + { + EXPECT_FALSE(renderer.getDisplayFramebuffer(ramses::displayId_t::Invalid()).isValid()); + + const ramses::displayId_t displayId1 = addDisplay(); + const ramses::displayId_t displayId2 = addDisplay(); + const ramses::displayId_t displayId3 = addDisplay(); + const auto fbId1 = renderer.getDisplayFramebuffer(displayId1); + const auto fbId2 = renderer.getDisplayFramebuffer(displayId2); + const auto fbId3 = renderer.getDisplayFramebuffer(displayId3); + EXPECT_TRUE(fbId1.isValid()); + EXPECT_TRUE(fbId2.isValid()); + EXPECT_TRUE(fbId3.isValid()); + EXPECT_NE(fbId1, fbId2); + EXPECT_NE(fbId1, fbId3); + EXPECT_NE(fbId2, fbId3); + } + + TEST_F(ARamsesRenderer, displayFramebufferIsInvalidIfDisplayDestroyed) + { + const ramses::displayId_t displayId = addDisplay(); + EXPECT_TRUE(renderer.getDisplayFramebuffer(displayId).isValid()); + renderer.destroyDisplay(displayId); + EXPECT_FALSE(renderer.getDisplayFramebuffer(displayId).isValid()); + } + + TEST_F(ARamsesRenderer, createsCommandForLoggingRenderInfo) + { + EXPECT_TRUE(renderer.logRendererInfo()); + EXPECT_CALL(cmdVisitor, logInfo(_, _, _)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRenderer, canCreateDisplayWithEmbeddedCompositingSetOnDisplayConfig) + { + ramses::DisplayConfig displayConfig; + displayConfig.setWaylandEmbeddedCompositingSocketName("ec-socket"); + const ramses::displayId_t displayId = renderer.createDisplay(displayConfig); + EXPECT_NE(ramses::displayId_t::Invalid(), displayId); + + EXPECT_CALL(cmdVisitor, createDisplayContext(_, ramses::internal::DisplayHandle{ displayId.getValue() }, _)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRenderer, displayIsCreatedWithDefaultFramerate) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework ramsesFramework{frameworkConfig}; + ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); + + ramsesRenderer->startThread(); + const ramses::displayId_t displayId = ramsesRenderer->createDisplay({}); + ramsesRenderer->flush(); + EXPECT_NEAR(60.f, ramsesRenderer->getFramerateLimit(displayId), 0.01f); + ramsesRenderer->stopThread(); + } + + TEST_F(ARamsesRenderer, canSetAndGetFPSLimitOfNonExistingDisplay) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework ramsesFramework{frameworkConfig}; + ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); + + constexpr ramses::displayId_t futureDisplay{ 22u }; + EXPECT_NEAR(60.f, ramsesRenderer->getFramerateLimit(futureDisplay), 0.01f); + + EXPECT_TRUE(ramsesRenderer->setFramerateLimit(futureDisplay, 10.f)); + EXPECT_NEAR(10.f, ramsesRenderer->getFramerateLimit(futureDisplay), 0.01f); + } + + TEST_F(ARamsesRenderer, failsToSetNegativeOrZeroFPSLimit) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework ramsesFramework{frameworkConfig}; + ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); + + constexpr ramses::displayId_t futureDisplay{ 22u }; + EXPECT_FALSE(ramsesRenderer->setFramerateLimit(futureDisplay, 0.f)); + EXPECT_FALSE(ramsesRenderer->setFramerateLimit(futureDisplay, -10.f)); + } + + TEST_F(ARamsesRenderer, failsToSetFPSLimitInNonThreadedMode) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework ramsesFramework{frameworkConfig}; + ramses::RamsesRenderer* ramsesRenderer = CreateRenderer(ramsesFramework, {}); + ramsesRenderer->doOneLoop(); + + constexpr ramses::displayId_t futureDisplay{ 22u }; + EXPECT_FALSE(ramsesRenderer->setFramerateLimit(futureDisplay, 10.f)); + } + + /* + * Offscreen buffers + */ + TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithDepthStencilBufferAsDefault) + { + EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u)); + EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses::internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses::EDepthBufferType::DepthStencil)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithoutDepthStencilBuffer) + { + EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u, ramses::EDepthBufferType::None)); + EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses::internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses::EDepthBufferType::None)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithDepthBuffer) + { + EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u, ramses::EDepthBufferType::Depth)); + EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses::internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses::EDepthBufferType::Depth)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferCreate_WithDepthStencilBuffer) + { + EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 40u, 40u, 4u, ramses::EDepthBufferType::DepthStencil)); + EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(_, ramses::internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, 4u, false, ramses::EDepthBufferType::DepthStencil)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForDmaOffscreenBufferCreate) + { + constexpr uint32_t fourccFormat = 777u; + constexpr uint32_t bufferUsageFlags = 888u; + constexpr uint64_t modifier = 999u; + EXPECT_NE(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 40u, 40u, fourccFormat, bufferUsageFlags, modifier)); + EXPECT_CALL(cmdVisitor, handleDmaBufferCreateRequest(_, ramses::internal::DisplayHandle{ displayId.getValue() }, 40u, 40u, ramses::internal::DmaBufferFourccFormat(fourccFormat), ramses::internal::DmaBufferUsageFlags(bufferUsageFlags), ramses::internal::DmaBufferModifiers(modifier))); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, failsToCreateDmaOffscreenBufferIfRendererIsRunningInOwnThread) + { + renderer.startThread(); + + constexpr uint32_t fourccFormat = 777u; + constexpr uint32_t bufferUsageFlags = 888u; + constexpr uint64_t modifier = 999u; + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 40u, 40u, fourccFormat, bufferUsageFlags, modifier)); + EXPECT_CALL(cmdVisitor, handleDmaBufferCreateRequest(_, _, _, _, _, _, _)).Times(0); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, canGetDmaOffscreenBufferFDAndStride) + { + ramses::internal::RendererEvent event; + event.eventType = ramses::internal::ERendererEventType::OffscreenBufferCreated; + event.displayHandle = ramses::internal::DisplayHandle{displayId.getValue()}; + event.offscreenBuffer = ramses::internal::OffscreenBufferHandle{ 10u }; + event.dmaBufferFD = 20; + event.dmaBufferStride = 30u; + renderer.impl().getDisplayDispatcher().injectRendererEvent(std::move(event)); + + ramses::RendererEventHandlerEmpty dummyHandler; + renderer.dispatchEvents(dummyHandler); + int resultFD = -1; + uint32_t resultStride = 0u; + EXPECT_TRUE(renderer.getDmaOffscreenBufferFDAndStride(displayId, ramses::displayBufferId_t{ 10u }, resultFD, resultStride)); + EXPECT_EQ(20, resultFD); + EXPECT_EQ(30u, resultStride); + } + + TEST_F(ARamsesRendererWithDisplay, reportsErrorIfGetingFDAndStrideForUnknownDmaOffscreenBuffer) + { + int resultFD = -1; + uint32_t resultStride = 0u; + EXPECT_FALSE(renderer.getDmaOffscreenBufferFDAndStride(displayId, ramses::displayBufferId_t{ 10u }, resultFD, resultStride)); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForOffscreenBufferDestroy) + { + const ramses::displayBufferId_t bufferId(0u); + EXPECT_TRUE(renderer.destroyOffscreenBuffer(displayId, bufferId)); + EXPECT_CALL(cmdVisitor, handleBufferDestroyRequest(ramses::internal::OffscreenBufferHandle{ bufferId.getValue() }, ramses::internal::DisplayHandle{ displayId.getValue() })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, failsToCreateOffscreenBufferWithUnsupportedResolution) + { + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 0u, 1u, 4u)); + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 1u, 0u, 4u)); + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createOffscreenBuffer(displayId, 0u, 0u, 4u)); + } + + TEST_F(ARamsesRendererWithDisplay, failsToCreateDmaOffscreenBufferWithUnsupportedResolution) + { + constexpr uint32_t fourccFormat = 777u; + constexpr uint32_t bufferUsageFlags = 888u; + constexpr uint64_t modifier = 999u; + + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 0u, 1u, fourccFormat, bufferUsageFlags, modifier)); + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 1u, 0u, fourccFormat, bufferUsageFlags, modifier)); + EXPECT_EQ(ramses::displayBufferId_t::Invalid(), renderer.createDmaOffscreenBuffer(displayId, 0u, 0u, fourccFormat, bufferUsageFlags, modifier)); + } + + /* + * Stream buffers + */ + TEST_F(ARamsesRendererWithDisplay, createsCommandForStreamBufferCreate) + { + constexpr ramses::waylandIviSurfaceId_t source{ 123u }; + const auto streamBuffer = renderer.createStreamBuffer(displayId, source); + EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(ramses::internal::StreamBufferHandle{ streamBuffer.getValue() }, ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::WaylandIviSurfaceId{ source.getValue() })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForStreamBufferDestroy) + { + constexpr ramses::streamBufferId_t streamBuffer{ 123u }; + EXPECT_TRUE(renderer.destroyStreamBuffer(displayId, streamBuffer)); + EXPECT_CALL(cmdVisitor, handleBufferDestroyRequest(ramses::internal::StreamBufferHandle{ streamBuffer.getValue() }, ramses::internal::DisplayHandle{ displayId.getValue() })); + cmdVisitor.visit(commandBuffer); + } + + /* + * External buffers + */ + TEST_F(ARamsesRendererWithDisplay, createsCommandForExternalBufferCreate) + { + const ramses::externalBufferId_t externalBuffer = renderer.createExternalBuffer(displayId); + EXPECT_NE(ramses::externalBufferId_t::Invalid(), externalBuffer); + EXPECT_CALL(cmdVisitor, handleExternalBufferCreateRequest(ramses::internal::ExternalBufferHandle{ externalBuffer.getValue() }, ramses::internal::DisplayHandle{ displayId.getValue() })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, failsToCreateExternalBufferIfRendererIsRunningInOwnThread) + { + renderer.startThread(); + + EXPECT_FALSE(renderer.createExternalBuffer(displayId).isValid()); + EXPECT_CALL(cmdVisitor, handleExternalBufferCreateRequest(_, _)).Times(0); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForExternalBufferDestroy) + { + ramses::externalBufferId_t externalBuffer{ 123u }; + EXPECT_TRUE(renderer.destroyExternalBuffer(displayId, externalBuffer)); + EXPECT_CALL(cmdVisitor, handleExternalBufferDestroyRequest(ramses::internal::ExternalBufferHandle{ externalBuffer.getValue() }, ramses::internal::DisplayHandle{ displayId.getValue() })); + cmdVisitor.visit(commandBuffer); + } + + /* + * Read Pixels + */ + TEST_F(ARamsesRendererWithDisplay, createsCommandForReadPixels) + { + ramses::displayBufferId_t bufferId{ 123u }; + EXPECT_TRUE(renderer.readPixels(displayId, bufferId, 1u, 2u, 3u, 4u)); + EXPECT_CALL(cmdVisitor, handleReadPixels(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{ bufferId.getValue() }, 1u, 2u, 3u, 4u, false, std::string_view{})); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, reportsErrorIfReadingPixelsForUnknownDisplay) + { + EXPECT_FALSE(renderer.readPixels(ramses::displayId_t{ 999u }, {}, 1u, 2u, 3u, 4u)); + } + + TEST_F(ARamsesRendererWithDisplay, createsNoCommandForEmptyReadPixels) + { + EXPECT_FALSE(renderer.readPixels(displayId, {}, 0u, 0u, 10u, 0u)); + EXPECT_FALSE(renderer.readPixels(displayId, {}, 0u, 0u, 0u, 10u)); + cmdVisitor.visit(commandBuffer); + } + + /* + * SystemCompositorControl + */ + TEST_F(ARamsesRenderer, createsNoCommandForSystemCompositorControllerIfNotEnabledFromConfig) + { + EXPECT_FALSE(renderer.setSurfaceVisibility(0, true)); + EXPECT_FALSE(renderer.setSurfaceOpacity(0, 0.2f)); + EXPECT_FALSE(renderer.setSurfaceRectangle(0, 1, 2, 3, 4)); + EXPECT_FALSE(renderer.setLayerVisibility(17,true)); + EXPECT_FALSE(renderer.takeSystemCompositorScreenshot("unused_name", -1)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetSurfaceVisibility) + { + EXPECT_TRUE(renderer.setSurfaceVisibility(1, true)); + EXPECT_CALL(cmdVisitor, systemCompositorSetIviSurfaceVisibility(ramses::internal::WaylandIviSurfaceId{ 1u }, true)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetSurfaceOpacity) + { + EXPECT_TRUE(renderer.setSurfaceOpacity(1, 0.2f)); + EXPECT_CALL(cmdVisitor, systemCompositorSetIviSurfaceOpacity(ramses::internal::WaylandIviSurfaceId{ 1u }, 0.2f)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetSurfaceRectangle) + { + EXPECT_TRUE(renderer.setSurfaceRectangle(1, 2, 3, 4, 5)); + EXPECT_CALL(cmdVisitor, systemCompositorSetIviSurfaceDestRectangle(ramses::internal::WaylandIviSurfaceId{ 1u }, 2, 3, 4, 5)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerAddSurfaceToLayer) + { + EXPECT_TRUE(renderer.impl().systemCompositorAddIviSurfaceToIviLayer(1, 2)); + EXPECT_CALL(cmdVisitor, systemCompositorAddIviSurfaceToIviLayer(ramses::internal::WaylandIviSurfaceId{ 1u }, ramses::internal::WaylandIviLayerId{ 2u })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerSetLayerVisibility) + { + EXPECT_TRUE(renderer.setLayerVisibility(17, true)); + EXPECT_CALL(cmdVisitor, systemCompositorSetIviLayerVisibility(ramses::internal::WaylandIviLayerId{ 17u }, true)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithSystemCompositorController, createsCommandForSystemCompositorControllerTakeScreenshot) + { + EXPECT_TRUE(renderer.takeSystemCompositorScreenshot("name", -1)); + EXPECT_CALL(cmdVisitor, systemCompositorScreenshot(std::string_view{"name"}, -1)); + cmdVisitor.visit(commandBuffer); + } + + /* + * Threading and thread sanitizer tests + */ + class SceneStateEventHandler : public ramses::RendererEventHandlerEmpty + { + public: + explicit SceneStateEventHandler(ramses::RamsesRenderer& renderer) + : m_renderer(renderer) + { + } + + void displayCreated(ramses::displayId_t /*displayId*/, ramses::ERendererEventResult result) override + { + m_displayCreated = (result == ramses::ERendererEventResult::Ok); + } + + void waitForDisplayCreationEvent() + { + while (!m_displayCreated) + { + m_renderer.dispatchEvents(*this); + std::this_thread::sleep_for(std::chrono::milliseconds{ 10 }); + } + } + + private: + ramses::RamsesRenderer& m_renderer; + bool m_displayCreated = false; + }; + + TEST_F(ARamsesRenderer, canRunRendererInItsOwnThread) + { + addDisplay(); + renderer.flush(); + EXPECT_TRUE(renderer.startThread()); + SceneStateEventHandler eventHandler(renderer); + eventHandler.waitForDisplayCreationEvent(); + EXPECT_TRUE(renderer.stopThread()); + } + + TEST_F(ARamsesRenderer, canRunRendererInItsOwnThreadAndCallAPIMethods) + { + const ramses::displayId_t displayId = addDisplay(); + renderer.flush(); + EXPECT_TRUE(renderer.startThread()); + SceneStateEventHandler eventHandler(renderer); + eventHandler.waitForDisplayCreationEvent(); + + // most of these will fail but the purpose is to create and submit renderer commands for renderer running in another thread + // thread sanitizer or other analyzer would catch race conditions when running this test + renderer.getSceneControlAPI()->handlePickEvent(ramses::sceneId_t(0u), 1u, 2u); + renderer.flush(); + + renderer.readPixels(displayId, {}, 1u, 2u, 3u, 4u); + const auto ob = renderer.createOffscreenBuffer(displayId, 1u, 1u, 4u); + renderer.destroyOffscreenBuffer(displayId, ob); + renderer.flush(); + + renderer.setSurfaceVisibility(0u, true); + renderer.setSurfaceOpacity(0u, 1.0f); + renderer.setSurfaceRectangle(0u, 0, 0, 0, 0); + renderer.takeSystemCompositorScreenshot("", -1); + renderer.setFrameTimerLimits(10001u, 10000u, 10000u); + renderer.setLayerVisibility(0u, true); + renderer.flush(); + + renderer.dispatchEvents(eventHandler); + EXPECT_TRUE(renderer.stopThread()); + } + + TEST_F(ARamsesRenderer, createsThreadedDisplayIfThreadStartedRightAfterCreation) + { + // Confidence test for a pre-existing race between starting threaded renderer and command dispatcher + // processing createDisplay command before display thread started - ending up in non-threaded display creation. + // 1. generate createDisplay cmd + flush + // 2. start 'general renderer' thread + // a. threaded mode for displays is enabled + // b. start dispatcher thread and process command + // c. display is created in threaded mode + // If a) would come last then display would be created non-threaded and won't be updated (and dispatcher asserts). + // Current implementation makes sure this does not happen, this test is an attempt (cannot be reliably reproduced) to catch regression. + + EXPECT_TRUE(renderer.createDisplay({}).isValid()); + EXPECT_TRUE(renderer.flush()); + EXPECT_TRUE(renderer.startThread()); + EXPECT_TRUE(renderer.stopThread()); + } + + TEST(ARamsesRendererWithSeparateRendererThread, canNotifyPerWatchdog) + { + ramses::internal::PlatformLock expectLock; + SafeThreadWatchdogNotificationMock notificationMock(expectLock); + + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + EXPECT_TRUE(frameworkConfig.setWatchdogNotificationInterval(ramses::ERamsesThreadIdentifier::Renderer, 500)); + EXPECT_TRUE(frameworkConfig.setWatchdogNotificationCallBack(¬ificationMock)); + { + ramses::internal::PlatformGuard g(expectLock); + EXPECT_CALL(notificationMock, safe_registerThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); + EXPECT_CALL(notificationMock, safe_notifyThread(ramses::ERamsesThreadIdentifier::Workers)).Times(AtLeast(1)); + } + ramses::RamsesFramework framework(frameworkConfig); + + { + ramses::internal::PlatformGuard g(expectLock); + EXPECT_CALL(notificationMock, safe_registerThread(ramses::ERamsesThreadIdentifier::Renderer)).Times(1); + } + + // syncWaiter must outlive renderer because renderer calls syncWaiter + // via mock until its dtor has run (only then thread gets really stopped!) + ramses::internal::PlatformEvent syncWaiter; + ramses::RamsesRenderer& renderer(*framework.createRenderer(ramses::RendererConfig())); + { + ramses::internal::PlatformGuard g(expectLock); + EXPECT_CALL(notificationMock, safe_notifyThread(ramses::ERamsesThreadIdentifier::Renderer)) + .Times(AtLeast(1)) + .WillRepeatedly(InvokeWithoutArgs([&]() { syncWaiter.signal(); })); + } + EXPECT_TRUE(renderer.startThread()); + + EXPECT_TRUE(syncWaiter.wait(60000)); + + { + ramses::internal::PlatformGuard g(expectLock); + EXPECT_CALL(notificationMock, safe_unregisterThread(ramses::ERamsesThreadIdentifier::Workers)).Times(1); + EXPECT_CALL(notificationMock, safe_unregisterThread(ramses::ERamsesThreadIdentifier::Renderer)).Times(1); + } + EXPECT_TRUE(renderer.stopThread()); + framework.destroyRenderer(renderer); + } + + class RenderThreadLoopTimingsNotification final : public ramses::RendererEventHandlerEmpty + { + public: + void renderThreadLoopTimings(ramses::displayId_t displayId, std::chrono::microseconds /*maximumLoopTime*/, std::chrono::microseconds /*averageLooptime*/) override + { + m_timeReports[displayId]++; + } + + bool displaysReported(std::initializer_list displays, size_t minCount = 2u) + { + return m_timeReports.size() == displays.size() + && std::all_of(displays.begin(), displays.end(), [&](const auto d) { return m_timeReports[d] >= minCount; }); + } + + private: + std::unordered_map m_timeReports; + }; + + TEST(ARamsesRendererNonThreaded, reportsFrameTimings) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework{frameworkConfig}; + ramses::RendererConfig rConfig; + rConfig.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds{ 50 }); + ramses::RamsesRenderer& renderer(*CreateRenderer(framework, rConfig)); + + const auto display1 = renderer.createDisplay({}); + const auto display2 = renderer.createDisplay({}); + renderer.flush(); + + // wait for either 60 seconds or until event was received + RenderThreadLoopTimingsNotification eventHandler; + const auto startTS = std::chrono::steady_clock::now(); + while (!eventHandler.displaysReported({ display1, display2 }) + && std::chrono::steady_clock::now() - startTS < std::chrono::minutes{ 1 }) + { + renderer.doOneLoop(); + renderer.dispatchEvents(eventHandler); + } + EXPECT_TRUE(eventHandler.displaysReported({ display1, display2 })); + + framework.destroyRenderer(renderer); + } + + TEST(ARamsesRendererWithSeparateRendererThread, reportsFrameTimings) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework{frameworkConfig}; + ramses::RendererConfig rConfig; + rConfig.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds{ 50 }); + ramses::RamsesRenderer& renderer(*CreateRenderer(framework, rConfig)); + + const auto display1 = renderer.createDisplay({}); + const auto display2 = renderer.createDisplay({}); + renderer.flush(); + + EXPECT_TRUE(renderer.startThread()); + + // wait for either 60 seconds or until event was received + RenderThreadLoopTimingsNotification eventHandler; + const auto startTS = std::chrono::steady_clock::now(); + while (!eventHandler.displaysReported({ display1, display2 }) + && std::chrono::steady_clock::now() - startTS < std::chrono::minutes{ 1 }) + { + renderer.dispatchEvents(eventHandler); + std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); + } + EXPECT_TRUE(eventHandler.displaysReported({ display1, display2 })); + + EXPECT_TRUE(renderer.stopThread()); + framework.destroyRenderer(renderer); + } + + TEST(ARamsesRendererWithSeparateRendererThread, willNotReportsFrameTimingsIfDisabled) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework{frameworkConfig}; + ramses::RendererConfig rConfig; + rConfig.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds{ 0 }); + ramses::RamsesRenderer& renderer(*CreateRenderer(framework, rConfig)); + + const auto display1 = renderer.createDisplay({}); + renderer.flush(); + + EXPECT_TRUE(renderer.startThread()); + + RenderThreadLoopTimingsNotification eventHandler; + const auto startTS = std::chrono::steady_clock::now(); + while (!eventHandler.displaysReported({ display1 }) && std::chrono::steady_clock::now() - startTS < std::chrono::seconds{ 5 }) + { + renderer.dispatchEvents(eventHandler); + std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); + } + EXPECT_FALSE(eventHandler.displaysReported({ display1 })); + + EXPECT_TRUE(renderer.stopThread()); + framework.destroyRenderer(renderer); + } + + TEST(ARamsesRendererWithSeparateRendererThread, TSAN_periodicallyDispatchEvents) + { + // this test is meant for TSAN + // - run renderer with display in thread with 30fps + // - keep dispatching events with higher frequency + + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework{frameworkConfig}; + ramses::RamsesRenderer& renderer(*CreateRenderer(framework, {})); + ramses::RamsesClient& client(*framework.createClient({})); + framework.connect(); + + auto scene = client.createScene(ramses::sceneId_t{ 321u }); + scene->createNode(); + scene->flush(); + scene->publish(ramses::EScenePublicationMode::LocalOnly); + + renderer.setFrameTimerLimits(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); + renderer.setLoopMode(ramses::ELoopMode::UpdateOnly); + const auto displayId = renderer.createDisplay({}); + renderer.setFramerateLimit(displayId, 30.f); + renderer.flush(); + + renderer.startThread(); + + ramses::RendererEventHandlerEmpty dummyHandler; + ramses::RendererSceneControlEventHandlerEmpty dummyHandler2; + const auto startTS = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - startTS < std::chrono::seconds{ 5 }) + { + renderer.dispatchEvents(dummyHandler); + renderer.getSceneControlAPI()->dispatchEvents(dummyHandler2); + std::this_thread::sleep_for(std::chrono::milliseconds{ 5 }); + } + + renderer.stopThread(); + framework.disconnect(); + framework.destroyRenderer(renderer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingFrameTimerLimits) + { + EXPECT_TRUE(renderer.setFrameTimerLimits(10001u, 10002u, 10003u)); + EXPECT_CALL(cmdVisitor, setLimitsFrameBudgets(10001u, 10002u, 10003u)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingSkippingUnmodifiedBuffersFeature) + { + EXPECT_TRUE(renderer.setSkippingOfUnmodifiedBuffers(true)); + EXPECT_CALL(cmdVisitor, setSkippingOfUnmodifiedBuffers(true)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearFlags_FB) + { + EXPECT_TRUE(renderer.setDisplayBufferClearFlags(displayId, renderer.getDisplayFramebuffer(displayId), ramses::EClearFlag::Color)); + EXPECT_CALL(cmdVisitor, handleSetClearFlags(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{}, ramses::ClearFlags(ramses::EClearFlag::Color))); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearFlags_FBImplicitlyUsingInvalidDisplayBuffer) + { + EXPECT_TRUE(renderer.setDisplayBufferClearFlags(displayId, ramses::displayBufferId_t::Invalid(), ramses::EClearFlag::Color)); + EXPECT_CALL(cmdVisitor, handleSetClearFlags(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{}, ramses::ClearFlags(ramses::EClearFlag::Color))); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearFlags_OB) + { + EXPECT_TRUE(renderer.setDisplayBufferClearFlags(displayId, ramses::displayBufferId_t{ 666u }, ramses::EClearFlag::Color)); + EXPECT_CALL(cmdVisitor, handleSetClearFlags(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{ 666u }, ramses::ClearFlags(ramses::EClearFlag::Color))); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, reportsErrorIfSettingClearFlagsForUnknownDisplay) + { + EXPECT_FALSE(renderer.setDisplayBufferClearFlags(ramses::displayId_t{ 999u }, {}, ramses::EClearFlag::Color)); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearColor_FB) + { + EXPECT_TRUE(renderer.setDisplayBufferClearColor(displayId, renderer.getDisplayFramebuffer(displayId), {1, 2, 3, 4})); + EXPECT_CALL(cmdVisitor, handleSetClearColor(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{}, glm::vec4{ 1, 2, 3, 4 })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearColor_FBImplicitlyUsingInvalidDisplayBuffer) + { + EXPECT_TRUE(renderer.setDisplayBufferClearColor(displayId, ramses::displayBufferId_t::Invalid(), {1, 2, 3, 4})); + EXPECT_CALL(cmdVisitor, handleSetClearColor(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{}, glm::vec4{ 1, 2, 3, 4 })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingClearColor_OB) + { + EXPECT_TRUE(renderer.setDisplayBufferClearColor(displayId, ramses::displayBufferId_t{666u}, {1, 2, 3, 4})); + EXPECT_CALL(cmdVisitor, handleSetClearColor(ramses::internal::DisplayHandle{ displayId.getValue() }, ramses::internal::OffscreenBufferHandle{ 666u }, glm::vec4{ 1, 2, 3, 4 })); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, reportsErrorIfSettingClearColorForUnknownDisplay) + { + EXPECT_FALSE(renderer.setDisplayBufferClearColor(ramses::displayId_t{999u}, {}, {1, 2, 3, 4})); + } + + TEST_F(ARamsesRendererWithDisplay, createsCommandForSettingExternallyOwnedWindowSize) + { + EXPECT_TRUE(renderer.setExternallyOwnedWindowSize(displayId, 123u, 456u)); + EXPECT_CALL(cmdVisitor, handleSetExternallyOwnedWindowSize(ramses::internal::DisplayHandle{ displayId.getValue() }, 123u, 456u)); + cmdVisitor.visit(commandBuffer); + } + + TEST_F(ARamsesRendererWithDisplay, reportsErrorIfSettingExternallyOwnedWindowSizeForUnknownDisplay) + { + EXPECT_FALSE(renderer.setExternallyOwnedWindowSize({}, 123u, 456u)); + } + + TEST(ARamsesFrameworkInARendererLib, canCreateARenderer) + { + ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework fw{fwConfig}; + + auto renderer = fw.createRenderer(ramses::RendererConfig()); + EXPECT_NE(nullptr, renderer); + } + + TEST(ARamsesFrameworkInARendererLib, canNotCreateMultipleRenderer) + { + ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework fw{fwConfig}; + + auto renderer1 = fw.createRenderer(ramses::RendererConfig()); + auto renderer2 = fw.createRenderer(ramses::RendererConfig()); + EXPECT_NE(nullptr, renderer1); + EXPECT_EQ(nullptr, renderer2); + } + + TEST(ARamsesFrameworkInARendererLib, acceptsLocallyCreatedRendererForDestruction) + { + ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework fw{fwConfig}; + + auto renderer = fw.createRenderer(ramses::RendererConfig()); + EXPECT_TRUE(fw.destroyRenderer(*renderer)); + } + + TEST(ARamsesFrameworkInARendererLib, doesNotAcceptForeignCreatedRendererForDestruction) + { + ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework fw1{fwConfig}; + ramses::RamsesFramework fw2{fwConfig}; + + auto renderer1 = fw1.createRenderer(ramses::RendererConfig()); + auto renderer2 = fw2.createRenderer(ramses::RendererConfig()); + EXPECT_FALSE(fw2.destroyRenderer(*renderer1)); + EXPECT_FALSE(fw1.destroyRenderer(*renderer2)); + } + + TEST(ARamsesFrameworkInARendererLib, doesNotAcceptSameRendererTwiceForDestruction) + { + ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework fw{fwConfig}; + + auto renderer = fw.createRenderer(ramses::RendererConfig()); + EXPECT_TRUE(fw.destroyRenderer(*renderer)); + EXPECT_FALSE(fw.destroyRenderer(*renderer)); + } + + TEST(ARamsesFrameworkInARendererLib, canCreateDestroyAndRecreateARenderer) + { + ramses::RamsesFrameworkConfig fwConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework fw{fwConfig}; + + auto renderer = fw.createRenderer(ramses::RendererConfig()); + EXPECT_NE(nullptr, renderer); + EXPECT_TRUE(fw.destroyRenderer(*renderer)); + renderer = fw.createRenderer(ramses::RendererConfig()); + EXPECT_NE(nullptr, renderer); + } + + TEST(ARamsesFrameworkInARendererLib, createRendererFailsWhenConnected) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework{frameworkConfig}; + EXPECT_TRUE(framework.connect()); + EXPECT_EQ(framework.createRenderer(ramses::RendererConfig()), nullptr); + } + + TEST(ARamsesFrameworkInARendererLib, destroyRendererFailsWhenConnected) + { + ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFramework framework{frameworkConfig}; + auto* renderer = framework.createRenderer(ramses::RendererConfig()); + ASSERT_NE(renderer, nullptr); + EXPECT_TRUE(framework.connect()); + EXPECT_FALSE(framework.destroyRenderer(*renderer)); + EXPECT_TRUE(framework.disconnect()); + EXPECT_TRUE(framework.destroyRenderer(*renderer)); + } +} diff --git a/tests/unittests/renderer/ramses-renderer/RendererConfigTest.cpp b/tests/unittests/renderer/ramses-renderer/RendererConfigTest.cpp new file mode 100644 index 000000000..d9d835950 --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/RendererConfigTest.cpp @@ -0,0 +1,105 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include +#include "ramses/renderer/BinaryShaderCache.h" +#include "ramses/renderer/RendererConfig.h" +#include "impl/RendererConfigImpl.h" + +namespace ramses::internal +{ + TEST(ARendererConfig, hasDefaultValuesUponConstruction) + { + const ramses::internal::RendererConfig defaultConfig; + ramses::RendererConfig config; + EXPECT_EQ(nullptr, config.impl().getBinaryShaderCache()); + + const ramses::internal::RendererConfig& internalConfig = config.impl().getInternalRendererConfig(); + + EXPECT_EQ(defaultConfig.getFrameCallbackMaxPollTime(), internalConfig.getFrameCallbackMaxPollTime()); + EXPECT_EQ(defaultConfig.getRenderThreadLoopTimingReportingPeriod(), internalConfig.getRenderThreadLoopTimingReportingPeriod()); + EXPECT_EQ(defaultConfig.getSystemCompositorControlEnabled(), internalConfig.getSystemCompositorControlEnabled()); + } + + TEST(ARendererConfig, canEnableSystemCompositor) + { + ramses::RendererConfig config; + EXPECT_TRUE(config.enableSystemCompositorControl()); + EXPECT_TRUE(config.impl().getInternalRendererConfig().getSystemCompositorControlEnabled()); + } + + TEST(ARendererConfig, CanBeCopyAndMoveConstructed) + { + ramses::RendererConfig config; + config.setRenderThreadLoopTimingReportingPeriod(std::chrono::seconds{ 1 }); + + ramses::RendererConfig configCopy{ config }; + EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); + + ramses::RendererConfig configMove{ std::move(config) }; + EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); + } + + TEST(ARendererConfig, CanBeCopyAndMoveAssigned) + { + ramses::RendererConfig config; + config.setRenderThreadLoopTimingReportingPeriod(std::chrono::seconds{ 1 }); + + ramses::RendererConfig configCopy; + configCopy = config; + EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); + + ramses::RendererConfig configMove; + configMove = std::move(config); + EXPECT_EQ(std::chrono::seconds{ 1 }, configCopy.getRenderThreadLoopTimingReportingPeriod()); + } + + TEST(ARendererConfig, CanBeSelfAssigned) + { + ramses::RendererConfig config; + config.setRenderThreadLoopTimingReportingPeriod(std::chrono::seconds{ 1 }); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-move" +#pragma clang diagnostic ignored "-Wself-assign-overloaded" +#endif + config = config; + EXPECT_EQ(std::chrono::seconds{ 1 }, config.getRenderThreadLoopTimingReportingPeriod()); + config = std::move(config); + // NOLINTNEXTLINE(bugprone-use-after-move) + EXPECT_EQ(std::chrono::seconds{ 1 }, config.getRenderThreadLoopTimingReportingPeriod()); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + TEST(ARendererConfig, canSetBinaryShaderCache) + { + ramses::RendererConfig config; + + ramses::BinaryShaderCache cache; + config.setBinaryShaderCache(cache); + EXPECT_EQ(&cache, config.impl().getBinaryShaderCache()); + } + + TEST(ARendererConfig, setsAndGetsWaylandDisplay) + { + ramses::RendererConfig config; + EXPECT_TRUE(config.setSystemCompositorWaylandDisplay("xxx")); + EXPECT_EQ("xxx", config.getSystemCompositorWaylandDisplay()); + EXPECT_EQ("xxx", config.impl().getInternalRendererConfig().getWaylandDisplayForSystemCompositorController()); + } + + TEST(ARendererConfig, setsAndGetsLoopCountPeriod) + { + ramses::RendererConfig config; + EXPECT_TRUE(config.setRenderThreadLoopTimingReportingPeriod(std::chrono::milliseconds(1234))); + EXPECT_EQ(std::chrono::milliseconds(1234), config.getRenderThreadLoopTimingReportingPeriod()); + } +} diff --git a/tests/unittests/renderer/ramses-renderer/RendererEventTestHandler.h b/tests/unittests/renderer/ramses-renderer/RendererEventTestHandler.h new file mode 100644 index 000000000..55aba3f47 --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/RendererEventTestHandler.h @@ -0,0 +1,358 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "ramses/renderer/IRendererEventHandler.h" +#include "internal/PlatformAbstraction/PlatformMemory.h" +#include "internal/PlatformAbstraction/Collections/Vector.h" +#include + +namespace ramses::internal +{ + enum ERendererEventTestType + { + ERendererEventTestType_Undefined = 0, + + ERendererEventTestType_OffscreenBufferCreated, + ERendererEventTestType_OffscreenBufferDestroyed, + ERendererEventTestType_PixelsRead, + ERendererEventTestType_DisplayCreated, + ERendererEventTestType_DisplayDestroyed, + ERendererEventTestType_WindowClosed, + ERendererEventTestType_WindowResized, + ERendererEventTestType_WindowMoved, + ERendererEventTestType_KeyEvent, + ERendererEventTestType_MouseEvent, + ERendererEventTestType_RenderThreadPeriodicLoopTimes, + ERendererEventTestType_ExternalBufferCreated, + ERendererEventTestType_ExternalBufferDestroyed, + }; + + struct RendererTestEvent + { + RendererTestEvent() = default; + + bool operator==(const RendererTestEvent& other) const + { + return eventType == other.eventType + && result == other.result + && sceneId == other.sceneId + && displayId == other.displayId + && bufferId == other.bufferId + && keyEvent == other.keyEvent + && keyModifiers == other.keyModifiers + && keyCode == other.keyCode + && mouseEvent == other.mouseEvent + && mousePosX == other.mousePosX + && mousePosY == other.mousePosY + && windowWidth == other.windowWidth + && windowHeight == other.windowHeight + && externalBufferId == other.externalBufferId + && externalBufferTextureGlId == other.externalBufferTextureGlId; + } + + ERendererEventTestType eventType = ERendererEventTestType_Undefined; + ramses::ERendererEventResult result = ramses::ERendererEventResult::Failed; + + ramses::sceneId_t sceneId { 0u }; + ramses::displayId_t displayId; + ramses::displayBufferId_t bufferId; + + ramses::EKeyEvent keyEvent = ramses::EKeyEvent::Invalid; + ramses::KeyModifiers keyModifiers; + ramses::EKeyCode keyCode = ramses::EKeyCode_Unknown; + + ramses::EMouseEvent mouseEvent = ramses::EMouseEvent::Invalid; + int32_t mousePosX = 0; + int32_t mousePosY = 0; + uint32_t windowWidth = 0u; + uint32_t windowHeight = 0u; + int32_t windowPosX = 0; + int32_t windowPosY = 0; + + std::chrono::microseconds renderthread_maximumLoopTime{0}; + std::chrono::microseconds renderthread_avg_looptime{0}; + + ramses::externalBufferId_t externalBufferId; + uint32_t externalBufferTextureGlId { 0u }; + }; + + class RendererEventTestHandler : public ramses::IRendererEventHandler + { + public: + ~RendererEventTestHandler() override + { + EXPECT_TRUE(m_events.empty()); + } + + void offscreenBufferCreated(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_OffscreenBufferCreated; + event.displayId = displayId; + event.bufferId = offscreenBufferId; + event.result = result; + m_events.push_back(event); + } + + void offscreenBufferDestroyed(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_OffscreenBufferDestroyed; + event.displayId = displayId; + event.bufferId = offscreenBufferId; + event.result = result; + m_events.push_back(event); + } + + void framebufferPixelsRead(const uint8_t* /*pixelData*/, const uint32_t /*pixelDataSize*/, ramses::displayId_t displayId, ramses::displayBufferId_t displayBufferId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_PixelsRead; + event.result = result; + event.displayId = displayId; + event.bufferId = displayBufferId; + m_events.push_back(event); + } + + void displayCreated(ramses::displayId_t displayId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_DisplayCreated; + event.result = result; + event.displayId = displayId; + m_events.push_back(event); + } + + void displayDestroyed(ramses::displayId_t displayId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_DisplayDestroyed; + event.result = result; + event.displayId = displayId; + m_events.push_back(event); + } + + void windowClosed(ramses::displayId_t displayId) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_WindowClosed; + event.displayId = displayId; + m_events.push_back(event); + } + + void windowResized(ramses::displayId_t displayId, uint32_t width, uint32_t height) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_WindowResized; + event.displayId = displayId; + event.windowWidth = width; + event.windowHeight = height; + m_events.push_back(event); + } + + void windowMoved(ramses::displayId_t displayId, int32_t posX, int32_t posY) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_WindowMoved; + event.displayId = displayId; + event.windowPosX = posX; + event.windowPosY = posY; + m_events.push_back(event); + } + + void keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent keyEvent, ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_KeyEvent; + event.displayId = displayId; + event.keyEvent = keyEvent; + event.keyModifiers = keyModifiers; + event.keyCode = keyCode; + m_events.push_back(event); + } + + void mouseEvent(ramses::displayId_t displayId, ramses::EMouseEvent mouseEvent, int32_t mousePosX, int32_t mousePosY) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_MouseEvent; + event.mouseEvent = mouseEvent; + event.displayId = displayId; + event.mousePosX = mousePosX; + event.mousePosY = mousePosY; + m_events.push_back(event); + } + + void externalBufferCreated(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, uint32_t textureGlId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_ExternalBufferCreated; + event.displayId = displayId; + event.externalBufferId = externalBufferId; + event.externalBufferTextureGlId = textureGlId; + event.result = result; + m_events.push_back(event); + } + + void externalBufferDestroyed(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, ramses::ERendererEventResult result) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_ExternalBufferDestroyed; + event.displayId = displayId; + event.externalBufferId = externalBufferId; + event.result = result; + m_events.push_back(event); + } + + void expectOffscreenBufferCreated(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_OffscreenBufferCreated; + event.displayId = displayId; + event.bufferId = offscreenBufferId; + event.result = result; + expectEvent(event); + } + + void expectOffscreenBufferDestroyed(ramses::displayId_t displayId, ramses::displayBufferId_t offscreenBufferId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_OffscreenBufferDestroyed; + event.displayId = displayId; + event.bufferId = offscreenBufferId; + event.result = result; + expectEvent(event); + } + + void expectExternalBufferCreated(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, uint32_t textureGlId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_ExternalBufferCreated; + event.displayId = displayId; + event.externalBufferId = externalBufferId; + event.externalBufferTextureGlId = textureGlId; + event.result = result; + expectEvent(event); + } + + void expectExternalBufferDestroyed(ramses::displayId_t displayId, ramses::externalBufferId_t externalBufferId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_ExternalBufferDestroyed; + event.displayId = displayId; + event.externalBufferId = externalBufferId; + event.result = result; + expectEvent(event); + } + + void expectFramebufferPixelsRead(const uint8_t* /*pixelData*/, const uint32_t /*pixelDataSize*/, ramses::displayId_t displayId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_PixelsRead; + event.result = result; + event.displayId = displayId; + expectEvent(event); + } + + void expectDisplayCreated(ramses::displayId_t displayId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_DisplayCreated; + event.result = result; + event.displayId = displayId; + expectEvent(event); + } + + void expectDisplayDestroyed(ramses::displayId_t displayId, ramses::ERendererEventResult result) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_DisplayDestroyed; + event.result = result; + event.displayId = displayId; + expectEvent(event); + } + + void expectWindowClosed(ramses::displayId_t displayId) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_WindowClosed; + event.displayId = displayId; + expectEvent(event); + } + + void expectWindowResized(ramses::displayId_t displayId, uint32_t width, uint32_t height) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_WindowResized; + event.displayId = displayId; + event.windowWidth = width; + event.windowHeight = height; + expectEvent(event); + } + + void expectWindowMoved(ramses::displayId_t displayId, int32_t posX, int32_t posY) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_WindowMoved; + event.displayId = displayId; + event.windowPosX = posX; + event.windowPosY = posY; + expectEvent(event); + } + + void expectKeyEvent(ramses::displayId_t displayId, ramses::EKeyEvent keyEvent, ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_KeyEvent; + event.displayId = displayId; + event.keyEvent = keyEvent; + event.keyModifiers = keyModifiers; + event.keyCode = keyCode; + expectEvent(event); + } + + void expectMouseEvent(ramses::displayId_t displayId, ramses::EMouseEvent mouseEvent, int32_t mousePosX, int32_t mousePosY) + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_MouseEvent; + event.displayId = displayId; + event.mouseEvent = mouseEvent; + event.mousePosX = mousePosX; + event.mousePosY = mousePosY; + expectEvent(event); + } + + void renderThreadLoopTimings(ramses::displayId_t displayId, std::chrono::microseconds maximumLoopTimeMilliseconds, std::chrono::microseconds averageLooptimeMilliseconds) override + { + RendererTestEvent event; + event.eventType = ERendererEventTestType_RenderThreadPeriodicLoopTimes; + event.displayId = displayId; + event.renderthread_maximumLoopTime = maximumLoopTimeMilliseconds; + event.renderthread_avg_looptime = averageLooptimeMilliseconds; + expectEvent(event); + } + + void expectNoEvent() + { + EXPECT_TRUE(m_events.empty()); + } + + private: + void expectEvent(const RendererTestEvent& event, const size_t withinLast = 1u) + { + auto it = std::find(m_events.begin(), m_events.begin() + static_cast(std::min(m_events.size(), withinLast)), event); + EXPECT_TRUE(it != m_events.end()) << "expected event " << event.eventType << " not emitted"; + + if (it != m_events.end()) + m_events.erase(it); + } + + std::vector m_events; + }; +} diff --git a/tests/unittests/renderer/ramses-renderer/RendererMateTest.cpp b/tests/unittests/renderer/ramses-renderer/RendererMateTest.cpp new file mode 100644 index 000000000..d2e20d430 --- /dev/null +++ b/tests/unittests/renderer/ramses-renderer/RendererMateTest.cpp @@ -0,0 +1,197 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2014 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" +#include "impl/RendererMate.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" +#include "internal/RendererLib/RendererCommands.h" +#include "impl/RamsesRendererImpl.h" +#include "internal/Core/Utils/ThreadBarrier.h" +#include "RendererCommandVisitorMock.h" +#include "PlatformFactoryMock.h" + +using namespace testing; + +namespace ramses::internal +{ + class ARendererMate : public Test + { + public: + ARendererMate() + : renderer(*ramsesFramework.createRenderer(ramses::RendererConfig())) + , rendererMate(renderer.impl(), ramsesFramework.impl()) + , rendererMateRendererEventHandler(rendererMate) + , rendererMateEventHandler(rendererMate) + { + renderer.impl().getDisplayDispatcher().injectPlatformFactory(std::make_unique()); + + displayId = renderer.createDisplay({}); + rendererMate.setSceneMapping(sceneId, displayId); + StrictMock visitor; + EXPECT_CALL(visitor, createDisplayContext(_, _, _)); + visitor.visit(renderer.impl().getPendingCommands()); + clearCommands(); + } + + protected: + void expectLogConfirmationCommand() + { + StrictMock visitor; + EXPECT_CALL(visitor, handleConfirmationEcho(DisplayHandle{ displayId.getValue() }, _)); + visitor.visit(renderer.impl().getPendingCommands()); + clearCommands(); + } + + void clearCommands() + { + const_cast(renderer.impl().getPendingCommands()).clear(); + } + + void publishAndExpectToGetToState(ramses::RendererSceneState state, bool expectConfirmation = false) + { + assert(state != ramses::RendererSceneState::Available); + EXPECT_FALSE(isSceneShown()); + + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Available); + if (state != ramses::RendererSceneState::Unavailable) + rendererMateEventHandler.sceneStateChanged(sceneId, state); + + EXPECT_EQ(state, rendererMate.getLastReportedSceneState(sceneId)); + if (expectConfirmation) + expectLogConfirmationCommand(); + clearCommands(); + } + + [[nodiscard]] bool isSceneShown() const + { + return rendererMate.getLastReportedSceneState(sceneId) == ramses::RendererSceneState::Rendered; + } + + private: + ramses::RamsesFramework ramsesFramework{ ramses::RamsesFrameworkConfig{ramses::EFeatureLevel_Latest} }; + ramses::RamsesRenderer& renderer; // restrict access to renderer, acts only as dummy for RendererMate and to create (dummy) displays + + protected: + RendererMate rendererMate; + ramses::IRendererEventHandler& rendererMateRendererEventHandler; + ramses::IRendererSceneControlEventHandler& rendererMateEventHandler; + + const ramses::sceneId_t sceneId{ 33 }; + ramses::displayId_t displayId; + const ramses::displayBufferId_t offscreenBufferId{ 2 }; + }; + + TEST_F(ARendererMate, willShowSceneWhenPublished) + { + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered); + publishAndExpectToGetToState(ramses::RendererSceneState::Rendered); + } + + TEST_F(ARendererMate, willShowSceneWhenPublishedAndLogsConfirmation) + { + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, "dummy msg"); + publishAndExpectToGetToState(ramses::RendererSceneState::Rendered, true); + } + + TEST_F(ARendererMate, willLogConfirmationEvenIfSceneAlreadyRendered) + { + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, ""); // no confirmation + publishAndExpectToGetToState(ramses::RendererSceneState::Rendered); + + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, "confirmation"); + expectLogConfirmationCommand(); + } + + TEST_F(ARendererMate, reportsCorrectSceneShowState) + { + // show first time + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered); + publishAndExpectToGetToState(ramses::RendererSceneState::Rendered); + + // explicitly hide + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Ready); + EXPECT_TRUE(isSceneShown()); // hide command not processed yet + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Ready); + EXPECT_FALSE(isSceneShown()); + + // show again + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered); + EXPECT_FALSE(isSceneShown()); // show command not processed yet + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Rendered); + EXPECT_TRUE(isSceneShown()); + + // unexpected unpublish!! + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Unavailable); + EXPECT_FALSE(isSceneShown()); + } + + TEST_F(ARendererMate, continuesToShowSceneAndLogsConfirmationAfterReconnect) + { + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Available); + rendererMate.setSceneState(sceneId, ramses::RendererSceneState::Rendered, "scene is shown now"); + + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Available); + + // simulate disconnect, renderer framework sends unpublish for all its scenes + rendererMateEventHandler.sceneStateChanged(sceneId, ramses::RendererSceneState::Unavailable); + + publishAndExpectToGetToState(ramses::RendererSceneState::Rendered, true); // expect confirmation + } + + TEST_F(ARendererMate, canBeCalledFromAnotherThread) + { + // this test simulates 2 threads executing commands (e.g. ramsh commands) + // and a thread dispatching and checking some states (e.g. standalone renderer) + + ThreadBarrier initBarrier(3); + ThreadBarrier finishedBarrier(3); + + constexpr ramses::sceneId_t sceneId1{ 10123 }; + constexpr ramses::sceneId_t sceneId2{ 10124 }; + rendererMateEventHandler.sceneStateChanged(sceneId1, ramses::RendererSceneState::Available); + rendererMateEventHandler.sceneStateChanged(sceneId2, ramses::RendererSceneState::Available); + + auto executeMethods = [&](ramses::sceneId_t sId) + { + initBarrier.wait(); + EXPECT_TRUE(rendererMate.setSceneState(sId, ramses::RendererSceneState::Available)); + EXPECT_TRUE(rendererMate.setSceneMapping(sId, displayId)); + EXPECT_TRUE(rendererMate.setSceneDisplayBufferAssignment(sId, offscreenBufferId)); + EXPECT_TRUE(rendererMate.setSceneState(sId, ramses::RendererSceneState::Ready)); + rendererMate.linkOffscreenBuffer(offscreenBufferId, sId, ramses::dataConsumerId_t{ 123 }); + rendererMate.linkData(sId, ramses::dataProviderId_t{ 123 }, ramses::sceneId_t{ sId.getValue() + 10 }, ramses::dataConsumerId_t{ 123 }); + rendererMate.processConfirmationEchoCommand(displayId, "foo"); + finishedBarrier.wait(); + }; + + std::thread t1(executeMethods, sceneId1); + std::thread t2(executeMethods, sceneId2); + + std::thread t3([&] + { + ramses::RendererSceneControlEventHandlerEmpty handler; + initBarrier.wait(); + for (int i = 0; i < 10; ++i) + { + rendererMate.dispatchAndFlush(handler); + EXPECT_TRUE(rendererMate.isRunning()); + rendererMate.getLastReportedSceneState(sceneId1); + rendererMate.getLastReportedSceneState(sceneId2); + rendererMate.enableKeysHandling(); + } + finishedBarrier.wait(); + }); + + t1.join(); + t2.join(); + t3.join(); + } +} diff --git a/renderer/ramses-renderer-impl/test/RendererSceneControlEventHandlerMock.cpp b/tests/unittests/renderer/ramses-renderer/RendererSceneControlEventHandlerMock.cpp similarity index 96% rename from renderer/ramses-renderer-impl/test/RendererSceneControlEventHandlerMock.cpp rename to tests/unittests/renderer/ramses-renderer/RendererSceneControlEventHandlerMock.cpp index b2b66b902..878c531f5 100644 --- a/renderer/ramses-renderer-impl/test/RendererSceneControlEventHandlerMock.cpp +++ b/tests/unittests/renderer/ramses-renderer/RendererSceneControlEventHandlerMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses +namespace ramses::internal { RendererSceneControlEventHandlerMock::RendererSceneControlEventHandlerMock() = default; RendererSceneControlEventHandlerMock::~RendererSceneControlEventHandlerMock() = default; diff --git a/renderer/ramses-renderer-impl/test/RendererSceneControlEventHandlerMock.h b/tests/unittests/renderer/ramses-renderer/RendererSceneControlEventHandlerMock.h similarity index 92% rename from renderer/ramses-renderer-impl/test/RendererSceneControlEventHandlerMock.h rename to tests/unittests/renderer/ramses-renderer/RendererSceneControlEventHandlerMock.h index 78d8e15cc..c282e0479 100644 --- a/renderer/ramses-renderer-impl/test/RendererSceneControlEventHandlerMock.h +++ b/tests/unittests/renderer/ramses-renderer/RendererSceneControlEventHandlerMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENECONTROLEVENTHANDLERMOCK_H -#define RAMSES_RENDERERSCENECONTROLEVENTHANDLERMOCK_H +#pragma once #include "gmock/gmock.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" -namespace ramses +namespace ramses::internal { class RendererSceneControlEventHandlerMock : public IRendererSceneControlEventHandler { @@ -39,5 +38,3 @@ namespace ramses MOCK_METHOD(void, streamAvailabilityChanged, (waylandIviSurfaceId_t, bool), (override)); }; } - -#endif diff --git a/renderer/ramses-renderer-impl/test/RendererSceneControlTest.cpp b/tests/unittests/renderer/ramses-renderer/RendererSceneControlTest.cpp similarity index 52% rename from renderer/ramses-renderer-impl/test/RendererSceneControlTest.cpp rename to tests/unittests/renderer/ramses-renderer/RendererSceneControlTest.cpp index c07b1e011..6f94b3daa 100644 --- a/renderer/ramses-renderer-impl/test/RendererSceneControlTest.cpp +++ b/tests/unittests/renderer/ramses-renderer/RendererSceneControlTest.cpp @@ -8,28 +8,28 @@ #include #include "gmock/gmock.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/Types.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-renderer-api/IRendererEventHandler.h" - -#include "RamsesRendererImpl.h" -#include "RendererSceneControlImpl.h" -#include "RendererEventCollector.h" -#include "RendererLib/RendererCommands.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/Types.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/renderer/IRendererEventHandler.h" + +#include "impl/RamsesRendererImpl.h" +#include "impl/RendererSceneControlImpl.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/RendererLib/RendererCommands.h" #include "RendererSceneControlEventHandlerMock.h" #include "RendererCommandVisitorMock.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #include "PlatformFactoryMock.h" #include using namespace testing; -namespace ramses +namespace ramses::internal { class ARendererSceneControl : public ::testing::Test { @@ -37,33 +37,33 @@ namespace ramses ARendererSceneControl() : m_renderer(*m_framework.createRenderer({})) , m_sceneControlAPI(*m_renderer.getSceneControlAPI()) - , m_pendingCommands(m_sceneControlAPI.m_impl.getPendingCommands()) + , m_pendingCommands(m_sceneControlAPI.impl().getPendingCommands()) { - m_renderer.m_impl.getDisplayDispatcher().injectPlatformFactory(std::make_unique()); - m_displayId = m_renderer.createDisplay(DisplayConfig{}); + m_renderer.impl().getDisplayDispatcher().injectPlatformFactory(std::make_unique()); + m_displayId = m_renderer.createDisplay(ramses::DisplayConfig{}); - ramses_internal::ThreadLocalLog::SetPrefix(2); + ThreadLocalLog::SetPrefix(2); m_renderer.setLoopMode(ELoopMode::UpdateOnly); } - void simulateSceneEventFromRenderer(ramses_internal::ERendererEventType eventType, ramses_internal::SceneId sceneId, ramses_internal::RendererSceneState state) + void simulateSceneEventFromRenderer(ERendererEventType eventType, SceneId sceneId, RendererSceneState state) { - ramses_internal::RendererEvent event{ eventType }; + RendererEvent event{ eventType }; event.sceneId = sceneId; event.state = state; - m_renderer.m_impl.getDisplayDispatcher().injectSceneControlEvent(std::move(event)); + m_renderer.impl().getDisplayDispatcher().injectSceneControlEvent(std::move(event)); } void submitEventsFromRenderer() { - ramses_internal::RendererEventVector rendererEvts; - ramses_internal::RendererEventVector sceneEvts; + RendererEventVector rendererEvts; + RendererEventVector sceneEvts; m_eventsFromRenderer.appendAndConsumePendingEvents(rendererEvts, sceneEvts); for (auto&& evt : rendererEvts) - m_renderer.m_impl.getDisplayDispatcher().injectSceneControlEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectSceneControlEvent(std::move(evt)); for (auto&& evt : sceneEvts) - m_renderer.m_impl.getDisplayDispatcher().injectSceneControlEvent(std::move(evt)); + m_renderer.impl().getDisplayDispatcher().injectSceneControlEvent(std::move(evt)); } void dispatchSceneControlEvents() @@ -77,11 +77,11 @@ namespace ramses RamsesFramework m_framework{ RamsesFrameworkConfig{EFeatureLevel_Latest} }; RamsesRenderer& m_renderer; RendererSceneControl& m_sceneControlAPI; - ramses_internal::RendererEventCollector m_eventsFromRenderer; // to simulate events coming from internal renderer (alternative to mock) + RendererEventCollector m_eventsFromRenderer; // to simulate events coming from internal renderer (alternative to mock) StrictMock m_eventHandler; - const ramses_internal::RendererCommands& m_pendingCommands; + const RendererCommands& m_pendingCommands; displayId_t m_displayId; - StrictMock m_cmdVisitor; + StrictMock m_cmdVisitor; }; TEST_F(ARendererSceneControl, hasNoCommandsOnStartUp) @@ -91,7 +91,7 @@ namespace ramses TEST_F(ARendererSceneControl, clearsAllPendingCommandsWhenCallingFlush) { - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(sceneId_t{ 1u }, RendererSceneState::Available)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(sceneId_t{ 1u }, RendererSceneState::Available)); m_sceneControlAPI.flush(); m_cmdVisitor.visit(m_pendingCommands); } @@ -99,8 +99,8 @@ namespace ramses TEST_F(ARendererSceneControl, createsCommandMakeSceneAvailable) { constexpr sceneId_t scene{ 1u }; - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Available)); - EXPECT_CALL(m_cmdVisitor, setSceneState(ramses_internal::SceneId{ scene.getValue() }, ramses_internal::RendererSceneState::Available)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Available)); + EXPECT_CALL(m_cmdVisitor, setSceneState(SceneId{ scene.getValue() }, RendererSceneState::Available)); m_cmdVisitor.visit(m_pendingCommands); } @@ -110,8 +110,8 @@ namespace ramses // has to set mapping before attempting to make scene rendered m_sceneControlAPI.setSceneMapping(scene, displayId_t{ 2 }); m_sceneControlAPI.flush(); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); - EXPECT_CALL(m_cmdVisitor, setSceneState(ramses_internal::SceneId{ scene.getValue() }, ramses_internal::RendererSceneState::Rendered)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); + EXPECT_CALL(m_cmdVisitor, setSceneState(SceneId{ scene.getValue() }, RendererSceneState::Rendered)); m_cmdVisitor.visit(m_pendingCommands); } @@ -122,12 +122,12 @@ namespace ramses constexpr dataProviderId_t providerId{ 3u }; constexpr dataConsumerId_t consumerId{ 4u }; - EXPECT_EQ(StatusOK, m_sceneControlAPI.linkData(scene1, providerId, scene2, consumerId)); + EXPECT_TRUE(m_sceneControlAPI.linkData(scene1, providerId, scene2, consumerId)); EXPECT_CALL(m_cmdVisitor, handleSceneDataLinkRequest( - ramses_internal::SceneId{ scene1.getValue() }, - ramses_internal::DataSlotId{ providerId.getValue() }, - ramses_internal::SceneId{ scene2.getValue() }, - ramses_internal::DataSlotId{ consumerId.getValue() })); + SceneId{ scene1.getValue() }, + DataSlotId{ providerId.getValue() }, + SceneId{ scene2.getValue() }, + DataSlotId{ consumerId.getValue() })); m_cmdVisitor.visit(m_pendingCommands); } @@ -138,17 +138,17 @@ namespace ramses m_sceneControlAPI.unlinkData(scene, consumerId); EXPECT_CALL(m_cmdVisitor, handleDataUnlinkRequest( - ramses_internal::SceneId{ scene.getValue() }, - ramses_internal::DataSlotId{ consumerId.getValue() })); + SceneId{ scene.getValue() }, + DataSlotId{ consumerId.getValue() })); m_cmdVisitor.visit(m_pendingCommands); } TEST_F(ARendererSceneControl, createsCommandForHandlePickEvent) { - constexpr ramses::sceneId_t scene(0u); - EXPECT_EQ(ramses::StatusOK, m_sceneControlAPI.handlePickEvent(scene, 1, 2)); + constexpr sceneId_t scene(0u); + EXPECT_TRUE(m_sceneControlAPI.handlePickEvent(scene, 1, 2)); EXPECT_CALL(m_cmdVisitor, handlePick( - ramses_internal::SceneId{ scene.getValue() }, + SceneId{ scene.getValue() }, glm::vec2{ 1, 2 })); m_cmdVisitor.visit(m_pendingCommands); } @@ -159,11 +159,11 @@ namespace ramses constexpr displayBufferId_t ob{ 5u }; // must set mapping before assigning to display buffer - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneMapping(scene, m_displayId)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneDisplayBufferAssignment(scene, ob, 11)); + EXPECT_TRUE(m_sceneControlAPI.setSceneMapping(scene, m_displayId)); + EXPECT_TRUE(m_sceneControlAPI.setSceneDisplayBufferAssignment(scene, ob, 11)); InSequence seq; - EXPECT_CALL(m_cmdVisitor, setSceneMapping(ramses_internal::SceneId{ scene.getValue() }, ramses_internal::DisplayHandle{ m_displayId.getValue() })); - EXPECT_CALL(m_cmdVisitor, setSceneDisplayBufferAssignment(ramses_internal::SceneId{ scene.getValue() }, ramses_internal::OffscreenBufferHandle{ ob.getValue() }, 11)); + EXPECT_CALL(m_cmdVisitor, setSceneMapping(SceneId{ scene.getValue() }, DisplayHandle{ m_displayId.getValue() })); + EXPECT_CALL(m_cmdVisitor, setSceneDisplayBufferAssignment(SceneId{ scene.getValue() }, OffscreenBufferHandle{ ob.getValue() }, 11)); m_cmdVisitor.visit(m_pendingCommands); } @@ -172,19 +172,19 @@ namespace ramses constexpr sceneId_t scene{ 1u }; // must set mapping before assigning to display buffer - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneMapping(scene, m_displayId)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneDisplayBufferAssignment(scene, {}, 11)); + EXPECT_TRUE(m_sceneControlAPI.setSceneMapping(scene, m_displayId)); + EXPECT_TRUE(m_sceneControlAPI.setSceneDisplayBufferAssignment(scene, {}, 11)); InSequence seq; - EXPECT_CALL(m_cmdVisitor, setSceneMapping(ramses_internal::SceneId{ scene.getValue() }, ramses_internal::DisplayHandle{ m_displayId.getValue() })); - EXPECT_CALL(m_cmdVisitor, setSceneDisplayBufferAssignment(ramses_internal::SceneId{ scene.getValue() }, ramses_internal::OffscreenBufferHandle{}, 11)); + EXPECT_CALL(m_cmdVisitor, setSceneMapping(SceneId{ scene.getValue() }, DisplayHandle{ m_displayId.getValue() })); + EXPECT_CALL(m_cmdVisitor, setSceneDisplayBufferAssignment(SceneId{ scene.getValue() }, OffscreenBufferHandle{}, 11)); m_cmdVisitor.visit(m_pendingCommands); } TEST_F(ARendererSceneControl, failsToAssignSceneWithNoMappingInfo) { constexpr sceneId_t sceneWithoutMapping{ 11 }; - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneDisplayBufferAssignment(sceneWithoutMapping, m_renderer.getDisplayFramebuffer(m_displayId))); - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneDisplayBufferAssignment(sceneWithoutMapping, displayBufferId_t{ 123 })); + EXPECT_FALSE(m_sceneControlAPI.setSceneDisplayBufferAssignment(sceneWithoutMapping, m_renderer.getDisplayFramebuffer(m_displayId))); + EXPECT_FALSE(m_sceneControlAPI.setSceneDisplayBufferAssignment(sceneWithoutMapping, displayBufferId_t{ 123 })); m_cmdVisitor.visit(m_pendingCommands); } @@ -194,11 +194,11 @@ namespace ramses constexpr sceneId_t consumerScene{ 2u }; constexpr dataConsumerId_t dataConsumer{ 3u }; - EXPECT_EQ(StatusOK, m_sceneControlAPI.linkOffscreenBuffer(bufferId, consumerScene, dataConsumer)); + EXPECT_TRUE(m_sceneControlAPI.linkOffscreenBuffer(bufferId, consumerScene, dataConsumer)); EXPECT_CALL(m_cmdVisitor, handleBufferToSceneDataLinkRequest( - ramses_internal::OffscreenBufferHandle{ bufferId.getValue() }, - ramses_internal::SceneId{ consumerScene.getValue() }, - ramses_internal::DataSlotId{ dataConsumer.getValue() })); + OffscreenBufferHandle{ bufferId.getValue() }, + SceneId{ consumerScene.getValue() }, + DataSlotId{ dataConsumer.getValue() })); m_cmdVisitor.visit(m_pendingCommands); } @@ -208,11 +208,11 @@ namespace ramses constexpr sceneId_t consumerScene{ 2u }; constexpr dataConsumerId_t dataConsumer{ 3u }; - EXPECT_EQ(StatusOK, m_sceneControlAPI.linkStreamBuffer(bufferId, consumerScene, dataConsumer)); + EXPECT_TRUE(m_sceneControlAPI.linkStreamBuffer(bufferId, consumerScene, dataConsumer)); EXPECT_CALL(m_cmdVisitor, handleBufferToSceneDataLinkRequest( - ramses_internal::StreamBufferHandle{ bufferId.getValue() }, - ramses_internal::SceneId{ consumerScene.getValue() }, - ramses_internal::DataSlotId{ dataConsumer.getValue() })); + StreamBufferHandle{ bufferId.getValue() }, + SceneId{ consumerScene.getValue() }, + DataSlotId{ dataConsumer.getValue() })); m_cmdVisitor.visit(m_pendingCommands); } @@ -222,53 +222,53 @@ namespace ramses constexpr sceneId_t consumerScene{ 2u }; constexpr dataConsumerId_t dataConsumer{ 3u }; - EXPECT_EQ(StatusOK, m_sceneControlAPI.linkExternalBuffer(bufferId, consumerScene, dataConsumer)); + EXPECT_TRUE(m_sceneControlAPI.linkExternalBuffer(bufferId, consumerScene, dataConsumer)); EXPECT_CALL(m_cmdVisitor, handleBufferToSceneDataLinkRequest( - ramses_internal::ExternalBufferHandle{ bufferId.getValue() }, - ramses_internal::SceneId{ consumerScene.getValue() }, - ramses_internal::DataSlotId{ dataConsumer.getValue() })); + ExternalBufferHandle{ bufferId.getValue() }, + SceneId{ consumerScene.getValue() }, + DataSlotId{ dataConsumer.getValue() })); m_cmdVisitor.visit(m_pendingCommands); } TEST_F(ARendererSceneControl, failsToMakeSceneReadyOrRenderedIfNoMappingSet) { const sceneId_t sceneWithNoMapping{ 1234 }; - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneState(sceneWithNoMapping, RendererSceneState::Ready)); - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneState(sceneWithNoMapping, RendererSceneState::Rendered)); + EXPECT_FALSE(m_sceneControlAPI.setSceneState(sceneWithNoMapping, RendererSceneState::Ready)); + EXPECT_FALSE(m_sceneControlAPI.setSceneState(sceneWithNoMapping, RendererSceneState::Rendered)); } TEST_F(ARendererSceneControl, failsToChangeMappingPropertiesIfSceneAlreadyReadyOrRendered) { constexpr sceneId_t scene{ 1u }; - constexpr ramses_internal::SceneId sceneInternal{ scene.getValue() }; + constexpr SceneId sceneInternal{ scene.getValue() }; constexpr displayId_t display{ 2u }; constexpr displayId_t otherDisplay{ 3u }; InSequence seq; - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneMapping(scene, display)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Ready)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.setSceneMapping(scene, display)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Ready)); + EXPECT_TRUE(m_sceneControlAPI.flush()); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Available)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Available); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Available); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Ready)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Ready); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Ready); dispatchSceneControlEvents(); // scene is now in ready state and will fail to change mapping - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); + EXPECT_FALSE(m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); + EXPECT_TRUE(m_sceneControlAPI.flush()); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Rendered)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Rendered); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Rendered); dispatchSceneControlEvents(); // scene is now in rendered state and will fail to change mapping - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); + EXPECT_FALSE(m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); // to clear commands - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.flush()); } TEST_F(ARendererSceneControl, failsToChangeMappingPropertiesIfSceneAlreadySetToReadyOrRendered) @@ -278,13 +278,13 @@ namespace ramses constexpr displayId_t otherDisplay{ 3u }; // set initial mapping - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneMapping(scene, display)); + EXPECT_TRUE(m_sceneControlAPI.setSceneMapping(scene, display)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); + EXPECT_FALSE(m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Ready)); - EXPECT_NE(StatusOK, m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Ready)); + EXPECT_FALSE(m_sceneControlAPI.setSceneMapping(scene, otherDisplay)); } TEST_F(ARendererSceneControl, canRunRendererInItsOwnThreadAndCallSceneAPIMethods) @@ -297,7 +297,7 @@ namespace ramses { } - void displayCreated(displayId_t, ERendererEventResult) override + void displayCreated(displayId_t /*displayId*/, ERendererEventResult /*result*/) override { m_displayCreated = true; } @@ -316,7 +316,7 @@ namespace ramses bool m_displayCreated = false; }; - EXPECT_EQ(StatusOK, m_renderer.startThread()); + EXPECT_TRUE(m_renderer.startThread()); // this is just to make sure the renderer is already running in thread DisplayCreateEventHandler rndHandler(m_renderer); @@ -331,46 +331,46 @@ namespace ramses m_sceneControlAPI.linkData(scene, dataProviderId_t(1u), sceneId_t(2u), dataConsumerId_t(3u)); m_sceneControlAPI.unlinkData(scene, dataConsumerId_t(1u)); m_sceneControlAPI.handlePickEvent(scene, 1.f, 2.f); - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.flush()); m_sceneControlAPI.setSceneDisplayBufferAssignment(scene, displayBufferId_t{ 12 }, 11); m_sceneControlAPI.linkOffscreenBuffer(displayBufferId_t{ 12 }, scene, dataConsumerId_t(0u)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.flush()); RendererSceneControlEventHandlerEmpty handler; - EXPECT_EQ(StatusOK, m_sceneControlAPI.dispatchEvents(handler)); + EXPECT_TRUE(m_sceneControlAPI.dispatchEvents(handler)); - EXPECT_EQ(StatusOK, m_renderer.stopThread()); + EXPECT_TRUE(m_renderer.stopThread()); } TEST_F(ARendererSceneControl, dispatchesEventsForSceneStateChanges) { constexpr sceneId_t scene{ 1u }; - constexpr ramses_internal::SceneId sceneInternal{ scene.getValue() }; + constexpr SceneId sceneInternal{ scene.getValue() }; constexpr displayId_t display{ 2u }; InSequence seq; - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneMapping(scene, display)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.setSceneMapping(scene, display)); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Rendered)); + EXPECT_TRUE(m_sceneControlAPI.flush()); // from published to rendered EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Available)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Available); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Available); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Ready)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Ready); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Ready); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Rendered)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Rendered); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Rendered); dispatchSceneControlEvents(); // back to unavailable - EXPECT_EQ(StatusOK, m_sceneControlAPI.setSceneState(scene, RendererSceneState::Available)); - EXPECT_EQ(StatusOK, m_sceneControlAPI.flush()); + EXPECT_TRUE(m_sceneControlAPI.setSceneState(scene, RendererSceneState::Available)); + EXPECT_TRUE(m_sceneControlAPI.flush()); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Ready)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Ready); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Ready); EXPECT_CALL(m_eventHandler, sceneStateChanged(scene, RendererSceneState::Available)); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, sceneInternal, ramses_internal::RendererSceneState::Available); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, sceneInternal, RendererSceneState::Available); dispatchSceneControlEvents(); } @@ -378,18 +378,18 @@ namespace ramses TEST_F(ARendererSceneControl, dispatchesOffscreenBufferLinkedEventFromRenderer) { constexpr sceneId_t consumerScene{ 3 }; - constexpr ramses_internal::SceneId consumerSceneInternal{ consumerScene.getValue() }; + constexpr SceneId consumerSceneInternal{ consumerScene.getValue() }; constexpr dataConsumerId_t consumerId{ 4 }; - constexpr ramses_internal::DataSlotId consumerIdInternal{ consumerId.getValue() }; + constexpr DataSlotId consumerIdInternal{ consumerId.getValue() }; constexpr displayBufferId_t offscreenBufferId{ 5 }; - constexpr ramses_internal::OffscreenBufferHandle obInternal{ offscreenBufferId.getValue() }; + constexpr OffscreenBufferHandle obInternal{ offscreenBufferId.getValue() }; InSequence seq; EXPECT_CALL(m_eventHandler, offscreenBufferLinked(offscreenBufferId, consumerScene, consumerId, true)); - m_eventsFromRenderer.addBufferEvent(ramses_internal::ERendererEventType::SceneDataBufferLinked, obInternal, consumerSceneInternal, consumerIdInternal); + m_eventsFromRenderer.addBufferEvent(ERendererEventType::SceneDataBufferLinked, obInternal, consumerSceneInternal, consumerIdInternal); EXPECT_CALL(m_eventHandler, offscreenBufferLinked(offscreenBufferId, consumerScene, consumerId, false)); - m_eventsFromRenderer.addBufferEvent(ramses_internal::ERendererEventType::SceneDataBufferLinkFailed, obInternal, consumerSceneInternal, consumerIdInternal); + m_eventsFromRenderer.addBufferEvent(ERendererEventType::SceneDataBufferLinkFailed, obInternal, consumerSceneInternal, consumerIdInternal); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -398,18 +398,18 @@ namespace ramses TEST_F(ARendererSceneControl, dispatchesExternalBufferLinkedEventFromRenderer) { constexpr sceneId_t consumerScene{ 3 }; - constexpr ramses_internal::SceneId consumerSceneInternal{ consumerScene.getValue() }; + constexpr SceneId consumerSceneInternal{ consumerScene.getValue() }; constexpr dataConsumerId_t consumerId{ 4 }; - constexpr ramses_internal::DataSlotId consumerIdInternal{ consumerId.getValue() }; + constexpr DataSlotId consumerIdInternal{ consumerId.getValue() }; constexpr externalBufferId_t bufferId{ 5 }; - constexpr ramses_internal::ExternalBufferHandle ebInternal{ bufferId.getValue() }; + constexpr ExternalBufferHandle ebInternal{ bufferId.getValue() }; InSequence seq; EXPECT_CALL(m_eventHandler, externalBufferLinked(bufferId, consumerScene, consumerId, true)); - m_eventsFromRenderer.addBufferEvent(ramses_internal::ERendererEventType::SceneDataBufferLinked, ebInternal, consumerSceneInternal, consumerIdInternal); + m_eventsFromRenderer.addBufferEvent(ERendererEventType::SceneDataBufferLinked, ebInternal, consumerSceneInternal, consumerIdInternal); EXPECT_CALL(m_eventHandler, externalBufferLinked(bufferId, consumerScene, consumerId, false)); - m_eventsFromRenderer.addBufferEvent(ramses_internal::ERendererEventType::SceneDataBufferLinkFailed, ebInternal, consumerSceneInternal, consumerIdInternal); + m_eventsFromRenderer.addBufferEvent(ERendererEventType::SceneDataBufferLinkFailed, ebInternal, consumerSceneInternal, consumerIdInternal); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -418,18 +418,18 @@ namespace ramses TEST_F(ARendererSceneControl, dispatchesStreamBufferLinkedEventFromRenderer) { constexpr sceneId_t consumerScene{ 3 }; - constexpr ramses_internal::SceneId consumerSceneInternal{ consumerScene.getValue() }; + constexpr SceneId consumerSceneInternal{ consumerScene.getValue() }; constexpr dataConsumerId_t consumerId{ 4 }; - constexpr ramses_internal::DataSlotId consumerIdInternal{ consumerId.getValue() }; + constexpr DataSlotId consumerIdInternal{ consumerId.getValue() }; constexpr streamBufferId_t bufferId{ 5 }; - constexpr ramses_internal::StreamBufferHandle sbInternal{ bufferId.getValue() }; + constexpr StreamBufferHandle sbInternal{ bufferId.getValue() }; InSequence seq; EXPECT_CALL(m_eventHandler, streamBufferLinked(bufferId, consumerScene, consumerId, true)); - m_eventsFromRenderer.addBufferEvent(ramses_internal::ERendererEventType::SceneDataBufferLinked, sbInternal, consumerSceneInternal, consumerIdInternal); + m_eventsFromRenderer.addBufferEvent(ERendererEventType::SceneDataBufferLinked, sbInternal, consumerSceneInternal, consumerIdInternal); EXPECT_CALL(m_eventHandler, streamBufferLinked(bufferId, consumerScene, consumerId, false)); - m_eventsFromRenderer.addBufferEvent(ramses_internal::ERendererEventType::SceneDataBufferLinkFailed, sbInternal, consumerSceneInternal, consumerIdInternal); + m_eventsFromRenderer.addBufferEvent(ERendererEventType::SceneDataBufferLinkFailed, sbInternal, consumerSceneInternal, consumerIdInternal); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -438,20 +438,20 @@ namespace ramses TEST_F(ARendererSceneControl, dispatchesDataLinkedEventFromRenderer) { constexpr sceneId_t providerScene{ 1 }; - constexpr ramses_internal::SceneId providerSceneInternal{ providerScene.getValue() }; + constexpr SceneId providerSceneInternal{ providerScene.getValue() }; constexpr dataProviderId_t providerId{ 2 }; - constexpr ramses_internal::DataSlotId providerIdInternal{ providerId.getValue() }; + constexpr DataSlotId providerIdInternal{ providerId.getValue() }; constexpr sceneId_t consumerScene{ 3 }; - constexpr ramses_internal::SceneId consumerSceneInternal{ consumerScene.getValue() }; + constexpr SceneId consumerSceneInternal{ consumerScene.getValue() }; constexpr dataConsumerId_t consumerId{ 4 }; - constexpr ramses_internal::DataSlotId consumerIdInternal{ consumerId.getValue() }; + constexpr DataSlotId consumerIdInternal{ consumerId.getValue() }; InSequence seq; EXPECT_CALL(m_eventHandler, dataLinked(providerScene, providerId, consumerScene, consumerId, true)); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataLinked, providerSceneInternal, consumerSceneInternal, providerIdInternal, consumerIdInternal); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataLinked, providerSceneInternal, consumerSceneInternal, providerIdInternal, consumerIdInternal); EXPECT_CALL(m_eventHandler, dataLinked(providerScene, providerId, consumerScene, consumerId, false)); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataLinkFailed, providerSceneInternal, consumerSceneInternal, providerIdInternal, consumerIdInternal); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataLinkFailed, providerSceneInternal, consumerSceneInternal, providerIdInternal, consumerIdInternal); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -460,19 +460,19 @@ namespace ramses TEST_F(ARendererSceneControl, dispatchesDataUnlinkedEventFromRenderer) { constexpr sceneId_t consumerScene{ 3 }; - constexpr ramses_internal::SceneId consumerSceneInternal{ consumerScene.getValue() }; + constexpr SceneId consumerSceneInternal{ consumerScene.getValue() }; constexpr dataConsumerId_t consumerId{ 4 }; - constexpr ramses_internal::DataSlotId consumerIdInternal{ consumerId.getValue() }; + constexpr DataSlotId consumerIdInternal{ consumerId.getValue() }; InSequence seq; EXPECT_CALL(m_eventHandler, dataUnlinked(consumerScene, consumerId, true)); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataUnlinked, {}, consumerSceneInternal, {}, consumerIdInternal); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataUnlinked, {}, consumerSceneInternal, {}, consumerIdInternal); EXPECT_CALL(m_eventHandler, dataUnlinked(consumerScene, consumerId, false)); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataUnlinkFailed, {}, consumerSceneInternal, {}, consumerIdInternal); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataUnlinkFailed, {}, consumerSceneInternal, {}, consumerIdInternal); // this event is ignored and will be removed - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, {}, consumerSceneInternal, {}, consumerIdInternal); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, {}, consumerSceneInternal, {}, consumerIdInternal); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -481,20 +481,20 @@ namespace ramses TEST_F(ARendererSceneControl, dispatchesHandlePickEventFromRenderer) { constexpr sceneId_t providerScene{ 3 }; - constexpr ramses_internal::SceneId providerSceneInternal{ providerScene.getValue() }; + constexpr SceneId providerSceneInternal{ providerScene.getValue() }; constexpr pickableObjectId_t pickable1{ 567u }; constexpr pickableObjectId_t pickable2{ 578u }; - constexpr ramses_internal::PickableObjectId pickable1Internal{pickable1.getValue()}; - constexpr ramses_internal::PickableObjectId pickable2Internal{pickable2.getValue()}; + constexpr PickableObjectId pickable1Internal{pickable1.getValue()}; + constexpr PickableObjectId pickable2Internal{pickable2.getValue()}; constexpr std::array pickableObjects{ pickable1, pickable2 }; - EXPECT_CALL(m_eventHandler, objectsPicked(providerScene, _, pickableObjects.size())).WillOnce(Invoke([&](auto, auto pickedObjects, auto pickedObjectsCount) + EXPECT_CALL(m_eventHandler, objectsPicked(providerScene, _, pickableObjects.size())).WillOnce(Invoke([&](auto /*unused*/, auto pickedObjects, auto pickedObjectsCount) { ASSERT_EQ(pickableObjects.size(), pickedObjectsCount); for (size_t i = 0u; i < pickedObjectsCount; ++i) EXPECT_EQ(pickableObjects[i], pickedObjects[i]); })); - m_eventsFromRenderer.addPickedEvent(ramses_internal::ERendererEventType::ObjectsPicked, providerSceneInternal, {pickable1Internal, pickable2Internal}); + m_eventsFromRenderer.addPickedEvent(ERendererEventType::ObjectsPicked, providerSceneInternal, {pickable1Internal, pickable2Internal}); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -506,7 +506,7 @@ namespace ramses constexpr sceneVersionTag_t tag{ 4 }; EXPECT_CALL(m_eventHandler, sceneFlushed(scene, tag)); - m_eventsFromRenderer.addSceneFlushEvent(ramses_internal::ERendererEventType::SceneFlushed, ramses_internal::SceneId{ scene.getValue() }, ramses_internal::SceneVersionTag{ tag }); + m_eventsFromRenderer.addSceneFlushEvent(ERendererEventType::SceneFlushed, SceneId{ scene.getValue() }, SceneVersionTag{ tag }); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -518,16 +518,16 @@ namespace ramses InSequence seq; EXPECT_CALL(m_eventHandler, sceneExpirationMonitoringEnabled(scene)); - m_eventsFromRenderer.addSceneExpirationEvent(ramses_internal::ERendererEventType::SceneExpirationMonitoringEnabled, ramses_internal::SceneId{ scene.getValue() }); + m_eventsFromRenderer.addSceneExpirationEvent(ERendererEventType::SceneExpirationMonitoringEnabled, SceneId{ scene.getValue() }); EXPECT_CALL(m_eventHandler, sceneExpired(scene)); - m_eventsFromRenderer.addSceneExpirationEvent(ramses_internal::ERendererEventType::SceneExpired, ramses_internal::SceneId{ scene.getValue() }); + m_eventsFromRenderer.addSceneExpirationEvent(ERendererEventType::SceneExpired, SceneId{ scene.getValue() }); EXPECT_CALL(m_eventHandler, sceneRecoveredFromExpiration(scene)); - m_eventsFromRenderer.addSceneExpirationEvent(ramses_internal::ERendererEventType::SceneRecoveredFromExpiration, ramses_internal::SceneId{ scene.getValue() }); + m_eventsFromRenderer.addSceneExpirationEvent(ERendererEventType::SceneRecoveredFromExpiration, SceneId{ scene.getValue() }); EXPECT_CALL(m_eventHandler, sceneExpirationMonitoringDisabled(scene)); - m_eventsFromRenderer.addSceneExpirationEvent(ramses_internal::ERendererEventType::SceneExpirationMonitoringDisabled, ramses_internal::SceneId{ scene.getValue() }); + m_eventsFromRenderer.addSceneExpirationEvent(ERendererEventType::SceneExpirationMonitoringDisabled, SceneId{ scene.getValue() }); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -539,10 +539,10 @@ namespace ramses InSequence seq; EXPECT_CALL(m_eventHandler, streamAvailabilityChanged(stream, true)); - m_eventsFromRenderer.addStreamSourceEvent(ramses_internal::ERendererEventType::StreamSurfaceAvailable, ramses_internal::WaylandIviSurfaceId{ stream.getValue() }); + m_eventsFromRenderer.addStreamSourceEvent(ERendererEventType::StreamSurfaceAvailable, WaylandIviSurfaceId{ stream.getValue() }); EXPECT_CALL(m_eventHandler, streamAvailabilityChanged(stream, false)); - m_eventsFromRenderer.addStreamSourceEvent(ramses_internal::ERendererEventType::StreamSurfaceUnavailable, ramses_internal::WaylandIviSurfaceId{ stream.getValue() }); + m_eventsFromRenderer.addStreamSourceEvent(ERendererEventType::StreamSurfaceUnavailable, WaylandIviSurfaceId{ stream.getValue() }); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -552,16 +552,16 @@ namespace ramses { InSequence seq; EXPECT_CALL(m_eventHandler, dataProviderCreated(sceneId_t{ 123u }, dataProviderId_t{ 1u })); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataSlotProviderCreated, ramses_internal::SceneId{ 123u }, {}, ramses_internal::DataSlotId{ 1u }, {}); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataSlotProviderCreated, SceneId{ 123u }, {}, DataSlotId{ 1u }, {}); EXPECT_CALL(m_eventHandler, dataProviderDestroyed(sceneId_t{ 124u }, dataProviderId_t{ 2u })); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataSlotProviderDestroyed, ramses_internal::SceneId{ 124u }, {}, ramses_internal::DataSlotId{ 2u }, {}); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataSlotProviderDestroyed, SceneId{ 124u }, {}, DataSlotId{ 2u }, {}); EXPECT_CALL(m_eventHandler, dataConsumerCreated(sceneId_t{ 125u }, dataConsumerId_t{ 3u })); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataSlotConsumerCreated, {}, ramses_internal::SceneId{ 125u }, {}, ramses_internal::DataSlotId{ 3u }); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataSlotConsumerCreated, {}, SceneId{ 125u }, {}, DataSlotId{ 3u }); EXPECT_CALL(m_eventHandler, dataConsumerDestroyed(sceneId_t{ 126u }, dataConsumerId_t{ 4u })); - m_eventsFromRenderer.addDataLinkEvent(ramses_internal::ERendererEventType::SceneDataSlotConsumerDestroyed, {}, ramses_internal::SceneId{ 126u }, {}, ramses_internal::DataSlotId{ 4u }); + m_eventsFromRenderer.addDataLinkEvent(ERendererEventType::SceneDataSlotConsumerDestroyed, {}, SceneId{ 126u }, {}, DataSlotId{ 4u }); submitEventsFromRenderer(); dispatchSceneControlEvents(); @@ -586,15 +586,15 @@ namespace ramses case RendererSceneState::Unavailable: break; case RendererSceneState::Available: - EXPECT_EQ(StatusOK, m_sceneControl.setSceneMapping(sceneId, displayId_t{ 1 })); - EXPECT_EQ(StatusOK, m_sceneControl.setSceneState(sceneId, RendererSceneState::Ready)); - EXPECT_EQ(StatusOK, m_sceneControl.flush()); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, ramses_internal::SceneId{ sceneId.getValue() }, ramses_internal::RendererSceneState::Ready); + EXPECT_TRUE(m_sceneControl.setSceneMapping(sceneId, displayId_t{ 1 })); + EXPECT_TRUE(m_sceneControl.setSceneState(sceneId, RendererSceneState::Ready)); + EXPECT_TRUE(m_sceneControl.flush()); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, SceneId{ sceneId.getValue() }, RendererSceneState::Ready); break; case RendererSceneState::Ready: - EXPECT_EQ(StatusOK, m_sceneControl.setSceneState(sceneId, RendererSceneState::Rendered)); - EXPECT_EQ(StatusOK, m_sceneControl.flush()); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, ramses_internal::SceneId{ sceneId.getValue() }, ramses_internal::RendererSceneState::Rendered); + EXPECT_TRUE(m_sceneControl.setSceneState(sceneId, RendererSceneState::Rendered)); + EXPECT_TRUE(m_sceneControl.flush()); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, SceneId{ sceneId.getValue() }, RendererSceneState::Rendered); break; case RendererSceneState::Rendered: m_reachedRenderedState = true; @@ -614,12 +614,12 @@ namespace ramses return m_reachedRenderedState; } - void simulateSceneEventFromRenderer(ramses_internal::ERendererEventType eventType, ramses_internal::SceneId sceneId, ramses_internal::RendererSceneState state) + void simulateSceneEventFromRenderer(ERendererEventType eventType, SceneId sceneId, RendererSceneState state) { - ramses_internal::RendererEvent event{ eventType }; + RendererEvent event{ eventType }; event.sceneId = sceneId; event.state = state; - m_renderer.m_impl.getDisplayDispatcher().injectSceneControlEvent(std::move(event)); + m_renderer.impl().getDisplayDispatcher().injectSceneControlEvent(std::move(event)); } RendererSceneControl& m_sceneControl; @@ -632,7 +632,7 @@ namespace ramses RecursiveEventTestHandler handler(m_sceneControlAPI, m_renderer); - simulateSceneEventFromRenderer(ramses_internal::ERendererEventType::SceneStateChanged, ramses_internal::SceneId{ 123 }, ramses_internal::RendererSceneState::Available); + simulateSceneEventFromRenderer(ERendererEventType::SceneStateChanged, SceneId{ 123 }, RendererSceneState::Available); EXPECT_TRUE(handler.waitForRenderedStateReached()); } } diff --git a/renderer/ramses-renderer-impl/test/RendererSceneControlWithRendererTest.cpp b/tests/unittests/renderer/ramses-renderer/RendererSceneControlWithRendererTest.cpp similarity index 81% rename from renderer/ramses-renderer-impl/test/RendererSceneControlWithRendererTest.cpp rename to tests/unittests/renderer/ramses-renderer/RendererSceneControlWithRendererTest.cpp index af55c0bca..d4997ad0b 100644 --- a/renderer/ramses-renderer-impl/test/RendererSceneControlWithRendererTest.cpp +++ b/tests/unittests/renderer/ramses-renderer/RendererSceneControlWithRendererTest.cpp @@ -7,28 +7,28 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "RendererSceneControlImpl.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-client-api/RamsesClient.h" -#include "ramses-client-api/Scene.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "Utils/LogMacros.h" -#include "RamsesRendererImpl.h" +#include "impl/RendererSceneControlImpl.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/client/RamsesClient.h" +#include "ramses/client/Scene.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "internal/Core/Utils/LogMacros.h" +#include "impl/RamsesRendererImpl.h" #include "PlatformFactoryMock.h" #include using namespace testing; -namespace ramses +namespace ramses::internal { class RendererSceneStateTracker final : public RendererSceneControlEventHandlerEmpty { public: - void sceneStateChanged(sceneId_t, RendererSceneState state) override + void sceneStateChanged(sceneId_t /*sceneId*/, RendererSceneState state) override { m_lastState = state; } @@ -43,9 +43,9 @@ namespace ramses : m_renderer(*m_framework.createRenderer({})) , m_client(*m_framework.createClient("client")) , m_sceneControl(*m_renderer.getSceneControlAPI()) - , m_scene(*m_client.createScene(m_sceneId)) + , m_scene(CreateScene(m_client, m_sceneId)) { - m_renderer.m_impl.getDisplayDispatcher().injectPlatformFactory(std::make_unique()); + m_renderer.impl().getDisplayDispatcher().injectPlatformFactory(std::make_unique()); m_displayId = m_renderer.createDisplay({}); m_renderer.flush(); @@ -65,7 +65,17 @@ namespace ramses displayId_t m_displayId; const sceneId_t m_sceneId{ 33u }; - Scene& m_scene; + ramses::Scene& m_scene; + + private: + static ramses::Scene& CreateScene(RamsesClient& client, sceneId_t sceneId) + { + // Run tests in remote compatible mode, i.e. using shadow copy scene, so that scene does not have to be flushed + // at every re-subscription. + // Alternative is to flush scene on every state change (or at least re-subscription) before render loop. + SceneConfig config{sceneId, EScenePublicationMode::LocalAndRemote}; + return *client.createScene(config); + } }; class ARendererSceneControlWithRenderer : public Test @@ -83,12 +93,12 @@ namespace ramses ramsesClientRenderer->m_sceneControl.dispatchEvents(m_sceneStateTracker); } - static void SetUpTestCase() + static void SetUpTestSuite() { ramsesClientRenderer = std::make_unique(); } - static void TearDownTestCase() + static void TearDownTestSuite() { ramsesClientRenderer.reset(); } @@ -165,17 +175,17 @@ namespace ramses static constexpr int NumStepsToReachTargetState = NumLoopsToReachTargetState * 2; // 2 steps per loop static constexpr int NumCombinations = NumStepsToReachTargetState * NumStates * NumStates; // can switch to new state at any step - [[nodiscard]] RendererSceneState GetTargetState1() const + [[nodiscard]] static RendererSceneState GetTargetState1() { return RendererSceneState(StartState + (GetParam() / NumStepsToReachTargetState) / NumStates); } - [[nodiscard]] RendererSceneState GetTargetState2() const + [[nodiscard]] static RendererSceneState GetTargetState2() { return RendererSceneState(StartState + (GetParam() / NumStepsToReachTargetState) % NumStates); } - [[nodiscard]] int GetStepToSwitchState() const + [[nodiscard]] static int GetStepToSwitchState() { return GetParam() % NumStepsToReachTargetState; } @@ -185,7 +195,7 @@ namespace ramses TEST_P(ARendererSceneControlWithRenderer_TargetToTarget, switchFromTargetStateToTargetStateAtEveryStep) { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "Testing switch from target state " << EnumToString(GetTargetState1()) << " to target state " << EnumToString(GetTargetState2()) << " at step " << GetStepToSwitchState()); + LOG_INFO(CONTEXT_RENDERER, "Testing switch from target state " << EnumToString(GetTargetState1()) << " to target state " << EnumToString(GetTargetState2()) << " at step " << GetStepToSwitchState()); ramsesClientRenderer->m_sceneControl.setSceneState(ramsesClientRenderer->m_sceneId, GetTargetState1()); ramsesClientRenderer->m_sceneControl.flush(); @@ -193,7 +203,7 @@ namespace ramses int step = 0; for (int i = 0; i < NumLoopsToReachTargetState; ++i) { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); + LOG_INFO(CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); if (step == GetStepToSwitchState()) { ramsesClientRenderer->m_sceneControl.setSceneState(ramsesClientRenderer->m_sceneId, GetTargetState2()); @@ -202,7 +212,7 @@ namespace ramses ramsesClientRenderer->m_renderer.doOneLoop(); ++step; - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); + LOG_INFO(CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); if (step == GetStepToSwitchState()) { ramsesClientRenderer->m_sceneControl.setSceneState(ramsesClientRenderer->m_sceneId, GetTargetState2()); @@ -234,17 +244,17 @@ namespace ramses static constexpr int NumStepsToReachTargetState = NumLoopsToReachTargetState * 2; // 2 steps per loop static constexpr int NumCombinations = (NumStepsToReachTargetState - 5) * MaxNumStepsRepublishComesAfter * NumStates; // can unpublish at any step, with room to process (-5), and encodes when publish comes (*MaxNumStepsRepublishComesAfter) and target state - [[nodiscard]] RendererSceneState GetTargetState() const + [[nodiscard]] static RendererSceneState GetTargetState() { return RendererSceneState(StartState + (GetParam() % NumStates)); } - [[nodiscard]] int GetStepToUnpublish() const + [[nodiscard]] static int GetStepToUnpublish() { return (GetParam() / NumStates) / MaxNumStepsRepublishComesAfter; } - [[nodiscard]] int GetStepToRepublish() const + [[nodiscard]] static int GetStepToRepublish() { // either at same step or one step return GetStepToUnpublish() + (GetParam() / NumStates) % MaxNumStepsRepublishComesAfter; @@ -255,7 +265,7 @@ namespace ramses TEST_P(ARendererSceneControlWithRenderer_ReachTargetStateWithUnpublish, unpublishAtEveryStepWhileReachingEveryState) { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "Testing unpublish from target state " << EnumToString(GetTargetState()) << " at step " << GetStepToUnpublish() << " and re-publish at step " << GetStepToRepublish()); + LOG_INFO(CONTEXT_RENDERER, "Testing unpublish from target state " << EnumToString(GetTargetState()) << " at step " << GetStepToUnpublish() << " and re-publish at step " << GetStepToRepublish()); ramsesClientRenderer->m_sceneControl.setSceneState(ramsesClientRenderer->m_sceneId, GetTargetState()); ramsesClientRenderer->m_sceneControl.flush(); @@ -263,7 +273,7 @@ namespace ramses int step = 0; for (int i = 0; i < NumLoopsToReachTargetState; ++i) { - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); + LOG_INFO(CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); if (step == GetStepToUnpublish()) ramsesClientRenderer->m_scene.unpublish(); if (step == GetStepToRepublish()) @@ -271,7 +281,7 @@ namespace ramses ramsesClientRenderer->m_renderer.doOneLoop(); ++step; - LOG_INFO(ramses_internal::CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); + LOG_INFO(CONTEXT_RENDERER, "LOOP: " << i << " STEP: " << step); if (step == GetStepToUnpublish()) ramsesClientRenderer->m_scene.unpublish(); if (step == GetStepToRepublish()) diff --git a/tests/unittests/renderer/renderer-lib/CMakeLists.txt b/tests/unittests/renderer/renderer-lib/CMakeLists.txt new file mode 100644 index 000000000..327060cf1 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/CMakeLists.txt @@ -0,0 +1,24 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME ramses-renderer-lib-test + TYPE BINARY + SRC_FILES RendererFramework/*.cpp + RendererFramework/*.h + RendererLib/*.cpp + RendererLib/*.h + RendererCommands/*.cpp + RendererCommands/*.h + DEPENDENCIES renderer-test-common + ramses-gmock-main +) + +makeTestFromTarget( + TARGET ramses-renderer-lib-test + SUFFIX UNITTEST) diff --git a/tests/unittests/renderer/renderer-lib/RendererCommands/AssignSceneTest.cpp b/tests/unittests/renderer/renderer-lib/RendererCommands/AssignSceneTest.cpp new file mode 100644 index 000000000..ea3e808f4 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererCommands/AssignSceneTest.cpp @@ -0,0 +1,86 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RamshCommands/AssignScene.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" + +using namespace ::testing; + +namespace ramses::internal +{ + class ARamshAssignScene : public ::testing::Test + { + public: + ARamshAssignScene() + : m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectCommand(const SceneId& scene, const DisplayHandle& display, const OffscreenBufferHandle& ob) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, setSceneMapping(scene, display)); + EXPECT_CALL(cmdVisitor, setSceneDisplayBufferAssignment(scene, ob, 0)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + void expectCommand(const SceneId& scene, const DisplayHandle& display) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, setSceneMapping(scene, display)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + void expectCommand(const SceneId& scene, const OffscreenBufferHandle& ob) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, setSceneDisplayBufferAssignment(scene, ob, 0)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + RendererCommandBuffer m_rendererCommandBuffer; + AssignScene m_cmd; + }; + + TEST_F(ARamshAssignScene, missingArguments) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"assign"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{})); + } + + TEST_F(ARamshAssignScene, invalidScene) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"assign", "0"})); + } + + TEST_F(ARamshAssignScene, missingTarget) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"assign", "43300001"})); + } + + TEST_F(ARamshAssignScene, assignToDisplay) + { + EXPECT_TRUE(m_cmd.executeInput({"assign", "433000001", "-displayId", "0"})); + expectCommand(SceneId(433000001), DisplayHandle(0)); + } + + TEST_F(ARamshAssignScene, assignToOffscreenBuffer) + { + EXPECT_TRUE(m_cmd.executeInput({"assign", "433000001", "-ob", "7"})); + expectCommand(SceneId(433000001), OffscreenBufferHandle(7)); + } + + TEST_F(ARamshAssignScene, assignToDisplayAndOffscreen) + { + EXPECT_TRUE(m_cmd.executeInput({"assign", "433000001", "-ob", "7", "-displayId", "1"})); + expectCommand(SceneId(433000001), DisplayHandle(1), OffscreenBufferHandle(7)); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererCommands/CreateOffscreenBufferTest.cpp b/tests/unittests/renderer/renderer-lib/RendererCommands/CreateOffscreenBufferTest.cpp new file mode 100644 index 000000000..61903d4ac --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererCommands/CreateOffscreenBufferTest.cpp @@ -0,0 +1,71 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RamshCommands/CreateOffscreenBuffer.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" + +using namespace ::testing; + +namespace ramses::internal +{ + class ARamshCreateOffscreenBuffer : public ::testing::Test + { + public: + ARamshCreateOffscreenBuffer() + : m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectCommand(const DisplayHandle& display, const OffscreenBufferHandle& buffer, uint32_t width, uint32_t height) + { + StrictMock cmdVisitor; + const uint32_t samples = 0; + const bool interruptible = false; + EXPECT_CALL(cmdVisitor, handleBufferCreateRequest(buffer, display, width, height, samples, interruptible, ramses::EDepthBufferType::DepthStencil)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + RendererCommandBuffer m_rendererCommandBuffer; + CreateOffscreenBuffer m_cmd; + }; + + TEST_F(ARamshCreateOffscreenBuffer, missingArguments) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"unlink"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{})); + } + + TEST_F(ARamshCreateOffscreenBuffer, create) + { + EXPECT_TRUE(m_cmd.executeInput({"obCreate", "0", "7", "400", "240"})); + expectCommand(DisplayHandle(0), OffscreenBufferHandle(7), 400, 240); + } + + TEST_F(ARamshCreateOffscreenBuffer, invalidDisplay) + { + EXPECT_FALSE(m_cmd.executeInput({"obCreate", "foo", "7", "400", "240"})); + } + + TEST_F(ARamshCreateOffscreenBuffer, invalidOffscreenBuffer) + { + EXPECT_FALSE(m_cmd.executeInput({"obCreate", "0", "bar", "400", "240"})); + } + + TEST_F(ARamshCreateOffscreenBuffer, zeroWidth) + { + EXPECT_FALSE(m_cmd.executeInput({"obCreate", "0", "7", "0", "240"})); + } + + TEST_F(ARamshCreateOffscreenBuffer, zeroHeight) + { + EXPECT_FALSE(m_cmd.executeInput({"obCreate", "0", "7", "1025", "0"})); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererCommands/LinkUnlinkTest.cpp b/tests/unittests/renderer/renderer-lib/RendererCommands/LinkUnlinkTest.cpp new file mode 100644 index 000000000..54d87e680 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererCommands/LinkUnlinkTest.cpp @@ -0,0 +1,81 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RamshCommands/LinkUnlink.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" + +using namespace ::testing; + +namespace ramses::internal +{ + class ARamshLinkBuffer : public ::testing::Test + { + public: + ARamshLinkBuffer() + : m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectCommand(const OffscreenBufferHandle& obHandle, const SceneId& scene, const DataSlotId& slot) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, handleBufferToSceneDataLinkRequest(obHandle, scene, slot)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + RendererCommandBuffer m_rendererCommandBuffer; + LinkBuffer m_cmd; + }; + + TEST_F(ARamshLinkBuffer, missingArguments) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"link"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{})); + } + + TEST_F(ARamshLinkBuffer, linkOffscreenBuffer) + { + EXPECT_TRUE(m_cmd.executeInput({"link", "433000001", "26589712", "42"})); + expectCommand(OffscreenBufferHandle(42), SceneId(433000001), DataSlotId(26589712)); + } + + class ARamshUnlinkBuffer : public ::testing::Test + { + public: + ARamshUnlinkBuffer() + : m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectCommand(const SceneId& scene, const DataSlotId& slot) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, handleDataUnlinkRequest(scene, slot)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + RendererCommandBuffer m_rendererCommandBuffer; + UnlinkBuffer m_cmd; + }; + + TEST_F(ARamshUnlinkBuffer, missingArguments) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"unlink"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{})); + } + + TEST_F(ARamshUnlinkBuffer, unlinkOffscreenBuffer) + { + EXPECT_TRUE(m_cmd.executeInput({"unlink", "433000001", "26589712"})); + expectCommand(SceneId(433000001), DataSlotId(26589712)); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererCommands/ScreenshotTest.cpp b/tests/unittests/renderer/renderer-lib/RendererCommands/ScreenshotTest.cpp new file mode 100644 index 000000000..c65b7861f --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererCommands/ScreenshotTest.cpp @@ -0,0 +1,81 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RamshCommands/Screenshot.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" + +#include + +using namespace ::testing; + +namespace ramses::internal +{ + class AScreenshot : public ::testing::Test + { + public: + AScreenshot() + : m_defaultFilename("unnamed.png") + , m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectScreenshotCommand(const std::string& filename, const DisplayHandle& displayHandle = DisplayHandle(0u), bool autoSize = true, const OffscreenBufferHandle& obHandle = OffscreenBufferHandle::Invalid()) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, handleReadPixels(displayHandle, obHandle, _, _, _, _, autoSize, std::string_view(filename))); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + const std::string m_defaultFilename; + RendererCommandBuffer m_rendererCommandBuffer; + Screenshot m_cmd; + }; + + TEST_F(AScreenshot, executesScreenshotNoArgsDefined) + { + EXPECT_TRUE(m_cmd.executeInput(std::vector{})); + expectScreenshotCommand(m_defaultFilename); + } + + TEST_F(AScreenshot, executesScreenshotWithFilename) + { + const std::string filename("someFilename.png"); + EXPECT_TRUE(m_cmd.executeInput({"-filename", filename})); + expectScreenshotCommand(filename); + } + + TEST_F(AScreenshot, executesScreenshotWithOffscreenBuffer) + { + const std::string filename("someFilename.png"); + EXPECT_TRUE(m_cmd.executeInput({"-filename", filename, "-ob", "42"})); + expectScreenshotCommand(filename, DisplayHandle(0), true, OffscreenBufferHandle(42)); + } + + TEST_F(AScreenshot, executesScreenshotWithDisplay) + { + const DisplayHandle displayHandle(23u); + EXPECT_TRUE(m_cmd.executeInput(std::vector{"-displayId", "23"})); + expectScreenshotCommand(m_defaultFilename, displayHandle); + } + + TEST_F(AScreenshot, brokenArgumentsAreNotExecuted) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"foo"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{"-foo"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{"-filename", "firstfile.png", "secondfile.png"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{"-displayId", "0", "1", "2"})); + } + + TEST_F(AScreenshot, emptyOptionsAreValid) + { + EXPECT_TRUE(m_cmd.executeInput(std::vector{"-filename", "-displayId"})); + expectScreenshotCommand(m_defaultFilename); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererCommands/SetClearColorTest.cpp b/tests/unittests/renderer/renderer-lib/RendererCommands/SetClearColorTest.cpp new file mode 100644 index 000000000..fda656a1c --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererCommands/SetClearColorTest.cpp @@ -0,0 +1,60 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RamshCommands/SetClearColor.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" + +using namespace ::testing; + +namespace ramses::internal +{ + class ASetClearColor : public ::testing::Test + { + public: + ASetClearColor() + : m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectCommand(const DisplayHandle& displayHandle, const OffscreenBufferHandle& obHandle, const glm::vec4& color) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, handleSetClearColor(displayHandle, obHandle, color)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + RendererCommandBuffer m_rendererCommandBuffer; + SetClearColor m_cmd; + }; + + TEST_F(ASetClearColor, missingArguments) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"clc"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{})); + } + + TEST_F(ASetClearColor, tooManyArguments) + { + EXPECT_FALSE(m_cmd.executeInput({"clc", "0", "0", "0", "0", "0", "0"})); + } + + TEST_F(ASetClearColor, clearDisplayColor) + { + EXPECT_TRUE(m_cmd.executeInput({"clc", "42", "1", "0", "1", "1"})); + expectCommand(DisplayHandle(42), OffscreenBufferHandle::Invalid(), {1.f, 0.f, 1.f, 1.f}); + } + + TEST_F(ASetClearColor, clearOffscreenBuffer) + { + EXPECT_TRUE(m_cmd.executeInput({"clc", "42", "0.1", "0.2", "0.3", "1", "-ob", "3"})); + expectCommand(DisplayHandle(42), OffscreenBufferHandle(3), {0.1f, 0.2f, 0.3f, 1.f}); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererCommands/SetSceneStateTest.cpp b/tests/unittests/renderer/renderer-lib/RendererCommands/SetSceneStateTest.cpp new file mode 100644 index 000000000..92338a441 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererCommands/SetSceneStateTest.cpp @@ -0,0 +1,72 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2023 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RamshCommands/SetSceneState.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" + +using namespace ::testing; + +namespace ramses::internal +{ + class ARamshSetSceneState : public ::testing::Test + { + public: + ARamshSetSceneState() + : m_cmd(m_rendererCommandBuffer) + { + } + + protected: + void expectCommand(const SceneId& scene, const RendererSceneState& state) + { + StrictMock cmdVisitor; + EXPECT_CALL(cmdVisitor, setSceneState(scene, state)); + cmdVisitor.visit(m_rendererCommandBuffer); + } + + RendererCommandBuffer m_rendererCommandBuffer; + SetSceneState m_cmd; + }; + + TEST_F(ARamshSetSceneState, missingArguments) + { + EXPECT_FALSE(m_cmd.executeInput(std::vector{"scenestate"})); + EXPECT_FALSE(m_cmd.executeInput(std::vector{})); + } + + TEST_F(ARamshSetSceneState, unavailable) + { + EXPECT_TRUE(m_cmd.executeInput({"scenestate", "433000002", "0"})); + expectCommand(SceneId(433000002), RendererSceneState::Unavailable); + } + + TEST_F(ARamshSetSceneState, available) + { + EXPECT_TRUE(m_cmd.executeInput({"scenestate", "433000002", "1"})); + expectCommand(SceneId(433000002), RendererSceneState::Available); + } + + TEST_F(ARamshSetSceneState, ready) + { + EXPECT_TRUE(m_cmd.executeInput({"scenestate", "433000002", "2"})); + expectCommand(SceneId(433000002), RendererSceneState::Ready); + } + + TEST_F(ARamshSetSceneState, rendered) + { + EXPECT_TRUE(m_cmd.executeInput({"scenestate", "433000002", "3"})); + expectCommand(SceneId(433000002), RendererSceneState::Rendered); + } + + TEST_F(ARamshSetSceneState, invalid) + { + EXPECT_FALSE(m_cmd.executeInput({"scenestate", "433000002", "4"})); + } +} diff --git a/renderer/RendererLib/RendererFramework/test/RendererFrameworkLogicTest.cpp b/tests/unittests/renderer/renderer-lib/RendererFramework/RendererFrameworkLogicTest.cpp similarity index 81% rename from renderer/RendererLib/RendererFramework/test/RendererFrameworkLogicTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererFramework/RendererFrameworkLogicTest.cpp index a7a22c1a2..cd48d04d0 100644 --- a/renderer/RendererLib/RendererFramework/test/RendererFrameworkLogicTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererFramework/RendererFrameworkLogicTest.cpp @@ -6,20 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" -#include "RendererFramework/RendererFrameworkLogic.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RendererFrameworkLogic.h" +#include "internal/RendererLib/RendererCommandBuffer.h" #include "ComponentMocks.h" -#include "ResourceMock.h" -#include "MockConnectionStatusUpdateNotifier.h" #include "RendererCommandVisitorMock.h" #include "MockResourceHash.h" -#include - using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ARendererFrameworkLogic : public testing::Test { @@ -38,7 +33,7 @@ namespace ramses_internal cmdVisitor.visit(rendererCommandBuffer); } - SceneActionCollection createFakeSceneActionCollectionFromTypes(const std::vector& types) + static SceneActionCollection CreateFakeSceneActionCollectionFromTypes(const std::vector& types) { SceneActionCollection collection; for (auto t : types) @@ -62,51 +57,51 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, generatesPublishedRendererCommand) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)); visitPendingCommands(); } TEST_F(ARendererFrameworkLogic, generatesReceiveRendererCommand) { - const SceneInfo sceneInfo{ sceneId, sceneName, EScenePublicationMode_LocalAndRemote }; + const SceneInfo sceneInfo{ sceneId, sceneName, EScenePublicationMode::LocalAndRemote }; fixture.handleNewSceneAvailable(sceneInfo, providerID); fixture.handleInitializeScene(sceneInfo, providerID); InSequence seq; - EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)); + EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)); EXPECT_CALL(cmdVisitor, handleSceneReceived(sceneInfo)); visitPendingCommands(); } TEST_F(ARendererFrameworkLogic, generatesUnpublishRendererCommand) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, "", EScenePublicationMode_LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, "", EScenePublicationMode::LocalAndRemote), providerID); fixture.handleSceneBecameUnavailable(sceneId, providerID); InSequence seq; - EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)); + EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)); EXPECT_CALL(cmdVisitor, handleSceneUnpublished(sceneId)); visitPendingCommands(); } TEST_F(ARendererFrameworkLogic, ignoresSecondPublishFromDifferentProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), Guid(30)); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), Guid(30)); - EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)); + EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)); visitPendingCommands(); } TEST_F(ARendererFrameworkLogic, handlesSceneUpdateWithFlush) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, "", EScenePublicationMode_LocalAndRemote), providerID); - EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote)); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, "", EScenePublicationMode::LocalAndRemote), providerID); + EXPECT_CALL(cmdVisitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)); visitPendingCommands(); SceneUpdate sceneUpdate; - sceneUpdate.actions = createFakeSceneActionCollectionFromTypes({ ESceneActionId::AddChildToNode }); + sceneUpdate.actions = CreateFakeSceneActionCollectionFromTypes({ ESceneActionId::AddChildToNode }); sceneUpdate.resources.push_back(MockResourceHash::GetManagedResource(MockResourceHash::EffectHash)); // copy before move SceneUpdate expectedData; @@ -116,7 +111,7 @@ namespace ramses_internal fixture.handleSceneUpdate(sceneId, std::move(sceneUpdate), providerID); - EXPECT_CALL(cmdVisitor, handleSceneUpdate(sceneId, _)).WillOnce(Invoke([&](auto, const SceneUpdate& data) + EXPECT_CALL(cmdVisitor, handleSceneUpdate(sceneId, _)).WillOnce(Invoke([&](auto /*unused*/, const SceneUpdate& data) { EXPECT_EQ(expectedData.actions, data.actions); EXPECT_EQ(expectedData.resources, data.resources); @@ -132,8 +127,8 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, willSendSubscribeMessageToCorrectProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(SceneId{123}, "foo", EScenePublicationMode_LocalAndRemote), Guid{456}); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(SceneId{123}, "foo", EScenePublicationMode::LocalAndRemote), Guid{456}); EXPECT_CALL(sceneGraphConsumerComponent, subscribeScene(providerID, sceneId)); fixture.sendSubscribeScene(sceneId); @@ -146,8 +141,8 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, willSendUnsubscribeMessageToCorrectProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode_LocalAndRemote), Guid{ 456 }); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode::LocalAndRemote), Guid{ 456 }); EXPECT_CALL(sceneGraphConsumerComponent, unsubscribeScene(providerID, sceneId)); fixture.sendUnsubscribeScene(sceneId); @@ -160,10 +155,10 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, willSendCorrectSceneStateChangedMessageToCorrectProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode_LocalAndRemote), Guid{ 456 }); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode::LocalAndRemote), Guid{ 456 }); - EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const&, SceneReferenceEvent const& event) + EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const& /*unused*/, SceneReferenceEvent const& event) { EXPECT_EQ(event.masterSceneId, sceneId); EXPECT_EQ(event.type, SceneReferenceEventType::SceneStateChanged); @@ -180,10 +175,10 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, willSendCorrectSceneFlushedMessageToCorrectProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode_LocalAndRemote), Guid{ 456 }); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode::LocalAndRemote), Guid{ 456 }); - EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const&, SceneReferenceEvent const& event) + EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const& /*unused*/, SceneReferenceEvent const& event) { EXPECT_EQ(event.masterSceneId, sceneId); EXPECT_EQ(event.type, SceneReferenceEventType::SceneFlushed); @@ -200,10 +195,10 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, willSendCorrectDataLinkedMessageToCorrectProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode_LocalAndRemote), Guid{ 456 }); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode::LocalAndRemote), Guid{ 456 }); - EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const&, SceneReferenceEvent const& event) + EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const& /*unused*/, SceneReferenceEvent const& event) { EXPECT_EQ(event.masterSceneId, sceneId); EXPECT_EQ(event.type, SceneReferenceEventType::DataLinked); @@ -223,10 +218,10 @@ namespace ramses_internal TEST_F(ARendererFrameworkLogic, willSendCorrectDataUnlinkedMessageToCorrectProvider) { - fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode_LocalAndRemote), providerID); - fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode_LocalAndRemote), Guid{ 456 }); + fixture.handleNewSceneAvailable(SceneInfo(sceneId, sceneName, EScenePublicationMode::LocalAndRemote), providerID); + fixture.handleNewSceneAvailable(SceneInfo(SceneId{ 123 }, "foo", EScenePublicationMode::LocalAndRemote), Guid{ 456 }); - EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const&, SceneReferenceEvent const& event) + EXPECT_CALL(sceneGraphConsumerComponent, sendSceneReferenceEvent(providerID, _)).WillOnce([this](Guid const& /*unused*/, SceneReferenceEvent const& event) { EXPECT_EQ(event.masterSceneId, sceneId); EXPECT_EQ(event.type, SceneReferenceEventType::DataUnlinked); diff --git a/renderer/RendererLib/RendererLib/test/AsyncEffectUploaderTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/AsyncEffectUploaderTest.cpp similarity index 95% rename from renderer/RendererLib/RendererLib/test/AsyncEffectUploaderTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/AsyncEffectUploaderTest.cpp index 3b6f6bb5d..a4e372a8f 100644 --- a/renderer/RendererLib/RendererLib/test/AsyncEffectUploaderTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/AsyncEffectUploaderTest.cpp @@ -6,20 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererLib/AsyncEffectUploader.h" +#include "internal/RendererLib/AsyncEffectUploader.h" +#include "internal/SceneGraph/Resource/EffectResource.h" #include "PlatformMock.h" -#include "Utils/ThreadLocalLog.h" -#include -#include "Watchdog/ThreadAliveNotifierMock.h" -#include -#include +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" using namespace testing; using namespace std::chrono_literals; -namespace ramses_internal +namespace ramses::internal { class AnAsyncEffectUploader : public testing::Test { @@ -74,7 +70,7 @@ namespace ramses_internal for(uint32_t i = 0u; i < count; ++i) { const auto randomString = std::to_string(++createdEffectCounter); - const EffectResource* effect = new EffectResource(randomString, "", "", {}, {}, {}, "", ResourceCacheFlag_DoNotCache); + const EffectResource* effect = new EffectResource(randomString, "", "", {}, {}, {}, ""); result.push_back(effect); createdEffects.emplace_back(effect); //keep track of created resource to avoid mem-leak @@ -224,7 +220,7 @@ namespace ramses_internal std::promise barrierRestShaderCanBePushedForUpload; std::promise barrierShaderUploadCanBeFinished; - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(Ref(*effectToUploadAndBlock[0]))).WillOnce(Invoke([&](const auto&) { + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(Ref(*effectToUploadAndBlock[0]))).WillOnce(Invoke([&](const auto& /*unused*/) { //unblock main thread to allow pushing effects to be uploaded while upload thread is busy barrierRestShaderCanBePushedForUpload.set_value(); //block upload thread till rest of shaders are pushed for upload in calls to sync() @@ -241,7 +237,7 @@ namespace ramses_internal //upload rest of shaders, and divide them on several calls to sync std::promise barrierRestShadersCanBeUploaded; EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).Times(5u); - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([&](const auto&) { + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([&](const auto& /*unused*/) { barrierRestShadersCanBeUploaded.get_future().get(); return std::make_unique(1u, 2u); })).RetiresOnSaturation(); @@ -263,7 +259,7 @@ namespace ramses_internal TEST_F(AnAsyncEffectUploader, notifiesWatchdogInbetweenEveryShaderUpload) { - EXPECT_CALL(notifier, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(5)).WillRepeatedly([this](auto) { notifyCounter++; }); + EXPECT_CALL(notifier, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(5)).WillRepeatedly([this](auto /*unused*/) { notifyCounter++; }); EXPECT_CALL(notifier, calculateTimeout()).Times(AtLeast(0)).WillRepeatedly([this]() { timeoutCounter++; return 20ms; }); createResourceUploadingRenderBackend(false); @@ -275,7 +271,7 @@ namespace ramses_internal TEST_F(AnAsyncEffectUploader, notifiesWatchdogRegularlyWithNoWorkToDo) { - EXPECT_CALL(notifier, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(10)).WillRepeatedly([this](auto) { notifyCounter++; }); + EXPECT_CALL(notifier, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(10)).WillRepeatedly([this](auto /*unused*/) { notifyCounter++; }); EXPECT_CALL(notifier, calculateTimeout()).Times(AtLeast(10)).WillRepeatedly([this]() { timeoutCounter++; return 1ms; }); createResourceUploadingRenderBackend(false); @@ -293,7 +289,7 @@ namespace ramses_internal const auto effects = createUniqueEffects(effectCount); constexpr std::chrono::milliseconds nonTrivialTime{ 100u }; - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).Times(AnyNumber()).WillRepeatedly(Invoke([&](const auto&) { + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).Times(AnyNumber()).WillRepeatedly(Invoke([&](const auto& /*unused*/) { std::this_thread::sleep_for(nonTrivialTime); return std::make_unique(1u, 2u); })); @@ -302,7 +298,7 @@ namespace ramses_internal //so test needs to block main thread till 1st shader upload std::promise barrierDestroyCanBeCalled; const auto& effectToUploadAndBlock = effects[0]; - EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(Ref(*effectToUploadAndBlock))).WillOnce(Invoke([&](const auto&) { + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(Ref(*effectToUploadAndBlock))).WillOnce(Invoke([&](const auto& /*unused*/) { //unblock main thread to allow destroy to be called while thread is busy with shader upload barrierDestroyCanBeCalled.set_value(); diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/BufferLinksTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/BufferLinksTest.cpp new file mode 100644 index 000000000..8cd04e38a --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/BufferLinksTest.cpp @@ -0,0 +1,168 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/BufferLinks.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + using namespace testing; + + using BufferTypes = ::testing::Types < + OffscreenBufferHandle, + StreamBufferHandle > ; + + template + class ABufferLinks : public ::testing::Test + { + protected: + static bool LinksEqual(const BufferLink& link1, const BufferLink& link2) + { + return link1.providerBuffer == link2.providerBuffer + && link1.consumerSceneId == link2.consumerSceneId + && link1.consumerSlot == link2.consumerSlot; + } + + static bool ContainsLink(const BufferLinkVector& links, const BufferLink& link) + { + for(const auto& linkIter : links) + { + if (LinksEqual(linkIter, link)) + { + return true; + } + } + + return false; + } + + void expectNoLinks() + { + EXPECT_FALSE(this->sceneLinks.hasAnyLinksToProvider(this->consumerScene1)); + EXPECT_FALSE(this->sceneLinks.hasAnyLinksToProvider(this->consumerScene2)); + EXPECT_FALSE(this->sceneLinks.hasAnyLinksToConsumer(this->providerBuffer1)); + EXPECT_FALSE(this->sceneLinks.hasAnyLinksToConsumer(this->providerBuffer2)); + EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene1, this->consumerSlot1)); + EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene1, this->consumerSlot2)); + EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene2, this->consumerSlot1)); + EXPECT_FALSE(this->sceneLinks.hasLinkedProvider(this->consumerScene2, this->consumerSlot2)); + expectLinkCount(this->providerBuffer1, 0u); + expectLinkCount(this->providerBuffer2, 0u); + expectLinkCount(this->consumerScene1, 0u); + expectLinkCount(this->consumerScene2, 0u); + } + + void expectLinkCount(SceneId sceneId, uint32_t providerScenesLinked) + { + BufferLinkVector links; + this->sceneLinks.getLinkedProviders(sceneId, links); + EXPECT_EQ(providerScenesLinked, links.size()); + } + + void expectLinkCount(BUFFERHANDLE providerBuffer, uint32_t consumerScenesLinked) + { + BufferLinkVector links; + this->sceneLinks.getLinkedConsumers(providerBuffer, links); + EXPECT_EQ(consumerScenesLinked, links.size()); + } + + void expectLink( + BUFFERHANDLE providerBuffer, + SceneId consumerSceneId, + DataSlotHandle consumerSlotHandle) + { + EXPECT_TRUE(this->sceneLinks.hasAnyLinksToProvider(consumerSceneId)); + EXPECT_TRUE(this->sceneLinks.hasAnyLinksToConsumer(providerBuffer)); + EXPECT_TRUE(this->sceneLinks.hasLinkedProvider(consumerSceneId, consumerSlotHandle)); + + BufferLink expectedLink; + expectedLink.providerBuffer = providerBuffer; + expectedLink.consumerSceneId = consumerSceneId; + expectedLink.consumerSlot = consumerSlotHandle; + + BufferLinkVector links; + this->sceneLinks.getLinkedProviders(consumerSceneId, links); + EXPECT_TRUE(ContainsLink(links, expectedLink)); + links.clear(); + + EXPECT_TRUE(LinksEqual(expectedLink, this->sceneLinks.getLinkedProvider(consumerSceneId, consumerSlotHandle))); + + this->sceneLinks.getLinkedConsumers(providerBuffer, links); + EXPECT_TRUE(ContainsLink(links, expectedLink)); + } + + BufferLinks sceneLinks; + + const BUFFERHANDLE providerBuffer1{ 1u }; + const BUFFERHANDLE providerBuffer2{ 2u }; + const SceneId consumerScene1{ 3u }; + const SceneId consumerScene2{ 4u }; + + const DataSlotHandle consumerSlot1{ 5u }; + const DataSlotHandle consumerSlot2{ 6u }; + }; + + TYPED_TEST_SUITE(ABufferLinks, BufferTypes); + + TYPED_TEST(ABufferLinks, reportsNoLinksInitially) + { + this->expectNoLinks(); + } + + TYPED_TEST(ABufferLinks, canAddLink) + { + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); + this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); + this->expectLinkCount(this->providerBuffer1, 1u); + this->expectLinkCount(this->consumerScene1, 1u); + } + + TYPED_TEST(ABufferLinks, canAddAndRemoveLink) + { + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); + this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot1); + this->expectNoLinks(); + } + + TYPED_TEST(ABufferLinks, canAddAndRemoveMultipleLinks) + { + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene2, this->consumerSlot1); + this->sceneLinks.addLink(this->providerBuffer2, this->consumerScene2, this->consumerSlot2); + + this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); + this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); + this->expectLink(this->providerBuffer1, this->consumerScene2, this->consumerSlot1); + this->expectLink(this->providerBuffer2, this->consumerScene2, this->consumerSlot2); + + this->expectLinkCount(this->providerBuffer1, 3u); + this->expectLinkCount(this->consumerScene1, 2u); + this->expectLinkCount(this->consumerScene2, 2u); + + this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot1); + this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot2); + this->sceneLinks.removeLink(this->consumerScene2, this->consumerSlot1); + this->sceneLinks.removeLink(this->consumerScene2, this->consumerSlot2); + this->expectNoLinks(); + } + + TYPED_TEST(ABufferLinks, removingLinkDoesNotAffectOtherLinks) + { + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot1); + this->sceneLinks.addLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); + this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot1); + + this->expectLink(this->providerBuffer1, this->consumerScene1, this->consumerSlot2); + this->expectLinkCount(this->providerBuffer1, 1u); + this->expectLinkCount(this->consumerScene1, 1u); + + this->sceneLinks.removeLink(this->consumerScene1, this->consumerSlot2); + this->expectNoLinks(); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkCachedSceneTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkCachedSceneTest.cpp new file mode 100644 index 000000000..590f84677 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkCachedSceneTest.cpp @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/DataReferenceLinkCachedScene.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "SceneAllocateHelper.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + using namespace testing; + + class ADataReferenceLinkCachedScene : public ::testing::Test + { + public: + ADataReferenceLinkCachedScene() + : rendererScenes(rendererEventCollector) + , scene(rendererScenes.createScene(SceneInfo(SceneId(3u)))) + , sceneAllocator(scene) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + const DataLayoutHandle layout = sceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Int32) }, ResourceContentHash::Invalid()); + dataRef = sceneAllocator.allocateDataInstance(layout); + + dataSlot = sceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, DataSlotId(1u), NodeHandle(), dataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }); + } + + protected: + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + DataReferenceLinkCachedScene& scene; + SceneAllocateHelper sceneAllocator; + + DataInstanceHandle dataRef; + DataSlotHandle dataSlot; + }; + + TEST_F(ADataReferenceLinkCachedScene, canSetAndGetValue) + { + scene.setDataSingleInteger(dataRef, DataFieldHandle(0u), 13); + EXPECT_EQ(13, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); + } + + TEST_F(ADataReferenceLinkCachedScene, canSetValueDirectlyWithoutUpdatingFallbackValue) + { + scene.setValueWithoutUpdatingFallbackValue(dataRef, DataFieldHandle(0u), DataInstanceValueVariant(33)); + EXPECT_EQ(33, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); + } + + TEST_F(ADataReferenceLinkCachedScene, canRestoreFallbackValue) + { + scene.setDataSingleInteger(dataRef, DataFieldHandle(0u), 13); + + scene.setValueWithoutUpdatingFallbackValue(dataRef, DataFieldHandle(0u), DataInstanceValueVariant(33)); + + scene.restoreFallbackValue(dataRef, DataFieldHandle(0u)); + EXPECT_EQ(13, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); + } + + TEST_F(ADataReferenceLinkCachedScene, storesFallbackValueWhenSlotAllocated) + { + scene.releaseDataSlot(dataSlot); + + scene.setDataSingleInteger(dataRef, DataFieldHandle(0u), 13); + + sceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, DataSlotId(1u), NodeHandle(), dataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }); + + scene.restoreFallbackValue(dataRef, DataFieldHandle(0u)); + EXPECT_EQ(13, scene.getDataSingleInteger(dataRef, DataFieldHandle(0u))); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkManagerTest.cpp new file mode 100644 index 000000000..285fef4d2 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DataReferenceLinkManagerTest.cpp @@ -0,0 +1,483 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "SceneAllocateHelper.h" +#include "internal/SceneGraph/SceneUtils/ISceneDataArrayAccessor.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + using namespace testing; + + class ADataReferenceLinkManager : public ::testing::Test + { + public: + explicit ADataReferenceLinkManager(bool createSlots = true) + : rendererScenes(rendererEventCollector) + , sceneLinksManager(rendererScenes.getSceneLinksManager()) + , dataReferenceLinkManager(sceneLinksManager.getDataReferenceLinkManager()) + , providerSceneId(3u) + , consumerSceneId(4u) + , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) + , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) + , providerSceneAllocator(providerScene) + , consumerSceneAllocator(consumerScene) + , providerSlotHandle(55u) + , consumerSlotHandle(66u) + , providerId(33u) + , consumerId(44u) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + if (createSlots) + { + const DataLayoutHandle providerLayout = providerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + providerDataRef = providerSceneAllocator.allocateDataInstance(providerLayout, DataInstanceHandle(3u)); + + const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + consumerDataRef = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(5u)); + + providerSceneAllocator.allocateDataSlot({ EDataSlotType::DataProvider, providerId, NodeHandle(), providerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, providerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, consumerId, NodeHandle(), consumerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); + } + } + + protected: + void expectRendererEvent(ERendererEventType event, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(event, events.front().eventType); + EXPECT_EQ(providerSId, events.front().providerSceneId); + EXPECT_EQ(consumerSId, events.front().consumerSceneId); + EXPECT_EQ(pId, events.front().providerdataId); + EXPECT_EQ(cId, events.front().consumerdataId); + } + + void expectRendererEvent(ERendererEventType event, SceneId consumerSId, DataSlotId cId, SceneId providerSId) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(event, events.front().eventType); + EXPECT_EQ(consumerSId, events.front().consumerSceneId); + EXPECT_EQ(cId, events.front().consumerdataId); + EXPECT_EQ(providerSId, events.front().providerSceneId); + } + + static void SetDataValue(DataInstanceHandle dataRef, IScene& scene, float value) + { + scene.setDataSingleFloat(dataRef, DataFieldHandle(0u), value); + } + + static void ExpectDataValue(DataInstanceHandle dataRef, const IScene& scene, float value) + { + const float actualValue = scene.getDataSingleFloat(dataRef, DataFieldHandle(0u)); + EXPECT_EQ(value, actualValue); + } + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneLinksManager& sceneLinksManager; + const DataReferenceLinkManager& dataReferenceLinkManager; + const SceneId providerSceneId; + const SceneId consumerSceneId; + IScene& providerScene; + DataReferenceLinkCachedScene& consumerScene; + SceneAllocateHelper providerSceneAllocator; + SceneAllocateHelper consumerSceneAllocator; + + DataInstanceHandle providerDataRef; + DataInstanceHandle consumerDataRef; + + const DataSlotHandle providerSlotHandle; + const DataSlotHandle consumerSlotHandle; + const DataSlotId providerId; + const DataSlotId consumerId; + }; + + TEST_F(ADataReferenceLinkManager, failsToCreateLinkOfMismatchingDataTypes) + { + const DataLayoutHandle providerLayout = providerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Int32) }, ResourceContentHash::Invalid()); + const DataInstanceHandle providerDataRef2 = providerSceneAllocator.allocateDataInstance(providerLayout, DataInstanceHandle(9u)); + + const DataSlotId providerId2(999u); + const DataSlotHandle slotHandle(43u); + providerSceneAllocator.allocateDataSlot({ EDataSlotType::DataProvider, providerId2, NodeHandle(), providerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); + + sceneLinksManager.createDataLink(providerSceneId, providerId2, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinkFailed, providerSceneId, providerId2, consumerSceneId, consumerId); + } + + TEST_F(ADataReferenceLinkManager, canResolveLinkedData) + { + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(consumerDataRef, consumerScene, 666.f); + ExpectDataValue(providerDataRef, providerScene, 666.f); + } + + TEST_F(ADataReferenceLinkManager, canResolveLinkedDataToMultipleConsumers) + { + const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + const DataInstanceHandle consumerDataRef2 = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(9u)); + + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle(43u); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, consumerId2, NodeHandle(), consumerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + SetDataValue(consumerDataRef2, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId2); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(consumerDataRef, consumerScene, 666.f); + ExpectDataValue(consumerDataRef2, consumerScene, 666.f); + ExpectDataValue(providerDataRef, providerScene, 666.f); + } + + TEST_F(ADataReferenceLinkManager, doesNotResolveLinkedDataIfConsumerUnlinked) + { + const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + const DataInstanceHandle consumerDataRef2 = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(9u)); + + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle(43u); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, consumerId2, NodeHandle(), consumerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + SetDataValue(consumerDataRef2, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId2); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(consumerDataRef, consumerScene, -1.f); + ExpectDataValue(consumerDataRef2, consumerScene, 666.f); + ExpectDataValue(providerDataRef, providerScene, 666.f); + } + + TEST_F(ADataReferenceLinkManager, confidenceTest_canResolveLinkedWithThreeScenesAndTwoLinks) + { + const SceneId middleSceneId(145u); + DataReferenceLinkCachedScene& middleScene = rendererScenes.createScene(SceneInfo(middleSceneId)); + SceneAllocateHelper middleSceneAllocator(middleScene); + + DataInstanceHandle middleProviderDataRef; + DataInstanceHandle middleConsumerDataRef; + + const DataLayoutHandle layout = middleSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + middleProviderDataRef = middleSceneAllocator.allocateDataInstance(layout, DataInstanceHandle(3u)); + middleConsumerDataRef = middleSceneAllocator.allocateDataInstance(layout, DataInstanceHandle(5u)); + + const DataSlotHandle middleProviderSlotHandle(18u); + const DataSlotHandle middleConsumerSlotHandle(19u); + const DataSlotId middleProviderId(18u); + const DataSlotId middleConsumerId(19u); + middleSceneAllocator.allocateDataSlot({ EDataSlotType::DataProvider, middleProviderId, NodeHandle(), middleProviderDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, middleProviderSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, middleSceneId, middleProviderId, SceneId(0u), DataSlotId(0u)); + middleSceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, middleConsumerId, NodeHandle(), middleConsumerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, middleConsumerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), middleSceneId, middleConsumerId); + + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(middleConsumerDataRef, middleScene, -1.f); + + SetDataValue(middleProviderDataRef, middleScene, 333.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, middleSceneId, middleConsumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, middleSceneId, middleConsumerId); + sceneLinksManager.createDataLink(middleSceneId, middleProviderId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, middleSceneId, middleProviderId, consumerSceneId, consumerId); + + dataReferenceLinkManager.resolveLinksForConsumerScene(middleScene); + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(providerDataRef, providerScene, 666.f); + ExpectDataValue(middleConsumerDataRef, middleScene, 666.f); + ExpectDataValue(middleProviderDataRef, middleScene, 333.f); + ExpectDataValue(consumerDataRef, consumerScene, 333.f); + } + + TEST_F(ADataReferenceLinkManager, unlinkedDataFallsBackToPreviousValue) + { + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(providerDataRef, providerScene, 666.f); + ExpectDataValue(consumerDataRef, consumerScene, 666.f); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); + + ExpectDataValue(providerDataRef, providerScene, 666.f); + ExpectDataValue(consumerDataRef, consumerScene, -1.f); + } + + TEST_F(ADataReferenceLinkManager, dataFallsBackToPreviouslySetValueIfProviderSceneDestroyed) + { + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + ExpectDataValue(providerDataRef, providerScene, 666.f); + ExpectDataValue(consumerDataRef, consumerScene, 666.f); + + SetDataValue(consumerDataRef, consumerScene, -333.f); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + ExpectDataValue(providerDataRef, providerScene, 666.f); + ExpectDataValue(consumerDataRef, consumerScene, 666.f); + + rendererScenes.destroyScene(providerSceneId); + + ExpectDataValue(consumerDataRef, consumerScene, -333.f); + } + + TEST_F(ADataReferenceLinkManager, confidenceTest_createTwoLinksChangeValueAndUnlink) + { + const DataLayoutHandle consumerLayout = consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + const DataInstanceHandle consumerDataRef2 = consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(9u)); + + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle(43u); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, consumerId2, NodeHandle(), consumerDataRef2, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + SetDataValue(providerDataRef, providerScene, 666.f); + SetDataValue(consumerDataRef, consumerScene, -1.f); + SetDataValue(consumerDataRef2, consumerScene, -1.f); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId2); + + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(consumerDataRef, consumerScene, 666.f); + ExpectDataValue(consumerDataRef2, consumerScene, 666.f); + ExpectDataValue(providerDataRef, providerScene, 666.f); + + SetDataValue(providerDataRef, providerScene, 123.f); + dataReferenceLinkManager.resolveLinksForConsumerScene(consumerScene); + + ExpectDataValue(consumerDataRef, consumerScene, 123.f); + ExpectDataValue(consumerDataRef2, consumerScene, 123.f); + ExpectDataValue(providerDataRef, providerScene, 123.f); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); + + ExpectDataValue(consumerDataRef, consumerScene, -1.f); + ExpectDataValue(consumerDataRef2, consumerScene, 123.f); + ExpectDataValue(providerDataRef, providerScene, 123.f); + } + + template + class ADataReferenceLinkManagerTyped : public ADataReferenceLinkManager + { + public: + ADataReferenceLinkManagerTyped() + : ADataReferenceLinkManager(false) + { + } + }; + + using ItemTypes = ::testing::Types < + bool, + int32_t, + float, + glm::vec2, + glm::vec3, + glm::vec4, + glm::ivec2, + glm::ivec3, + glm::ivec4, + glm::mat2, + glm::mat3, + glm::mat4 + >; + + TYPED_TEST_SUITE(ADataReferenceLinkManagerTyped, ItemTypes); + + template + T GetSomeValue() + { + return T(5); + } + + template <> bool GetSomeValue() + { + return true; + } + + template <> + glm::mat2 GetSomeValue() + { + return glm::mat2(1,2,3,4); + } + + template <> + glm::mat3 GetSomeValue() + { + return glm::mat3(1, 2, 3, 4, 5, 6, 7, 8, 9); + } + + template <> + glm::mat4 GetSomeValue() + { + return glm::mat4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + } + + template + T GetSomeValue2() + { + return T(13); + } + + template <> bool GetSomeValue2() + { + return true; + } + + template <> + glm::mat2 GetSomeValue2() + { + return glm::mat2(-1, -2, -3, -4); + } + + template <> + glm::mat3 GetSomeValue2() + { + return glm::mat3(-1, -2, -3, -4, -5, -6, -7, -8, -9); + } + + template <> + glm::mat4 GetSomeValue2() + { + return glm::mat4(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16); + } + + template + T GetSomeValue3() + { + return T(-1513); + } + + template <> bool GetSomeValue3() + { + return false; + } + + template <> + glm::mat2 GetSomeValue3() + { + return glm::mat2(-11, -22, -33, -44); + } + + template <> + glm::mat3 GetSomeValue3() + { + return glm::mat3(-11, -21, -31, -41, -51, -61, -71, -81, -91); + } + + template <> + glm::mat4 GetSomeValue3() + { + return glm::mat4(-12, -22, -32, -42, -52, -62, -72, -82, -92, -102, -112, -122, -132, -142, -152, -162); + } + + TYPED_TEST(ADataReferenceLinkManagerTyped, confidenceTest_linkAndChangeAndUnlink) + { + const DataLayoutHandle providerLayout = this->providerSceneAllocator.allocateDataLayout({ DataFieldInfo(TypeToEDataTypeTraits::DataType) }, ResourceContentHash::Invalid()); + this->providerDataRef = this->providerSceneAllocator.allocateDataInstance(providerLayout, DataInstanceHandle(23u)); + + const DataLayoutHandle consumerLayout = this->consumerSceneAllocator.allocateDataLayout({ DataFieldInfo(TypeToEDataTypeTraits::DataType) }, ResourceContentHash::Invalid()); + this->consumerDataRef = this->consumerSceneAllocator.allocateDataInstance(consumerLayout, DataInstanceHandle(24u)); + + this->providerSceneAllocator.allocateDataSlot({ EDataSlotType::DataProvider, this->providerId, NodeHandle(), this->providerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, this->providerSlotHandle); + this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->providerSceneId, this->providerId, SceneId(0u), DataSlotId(0u)); + this->consumerSceneAllocator.allocateDataSlot({ EDataSlotType::DataConsumer, this->consumerId, NodeHandle(), this->consumerDataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, this->consumerSlotHandle); + this->expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), this->consumerSceneId, this->consumerId); + + const auto providerValue = GetSomeValue(); + const auto consumerValueInitial = GetSomeValue2(); + const auto consumerValueNew = GetSomeValue3(); + + // create link + ISceneDataArrayAccessor::SetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u), 1u, &providerValue); + ISceneDataArrayAccessor::SetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u), 1u, &consumerValueInitial); + + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + + this->dataReferenceLinkManager.resolveLinksForConsumerScene(this->consumerScene); + TypeParam actualProviderValue = ISceneDataArrayAccessor::GetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u))[0]; + TypeParam actualConsumerValue = ISceneDataArrayAccessor::GetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u))[0]; + EXPECT_EQ(providerValue, actualProviderValue); + EXPECT_EQ(providerValue, actualConsumerValue); + + // change value on consumer while linked + ISceneDataArrayAccessor::SetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u), 1u, &consumerValueNew); + this->dataReferenceLinkManager.resolveLinksForConsumerScene(this->consumerScene); + actualProviderValue = ISceneDataArrayAccessor::GetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u))[0]; + actualConsumerValue = ISceneDataArrayAccessor::GetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u))[0]; + EXPECT_EQ(providerValue, actualProviderValue); + EXPECT_EQ(providerValue, actualConsumerValue); + + // remove link + this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinked, this->consumerSceneId, this->consumerId, this->providerSceneId); + + actualProviderValue = ISceneDataArrayAccessor::GetDataArray(&this->providerScene, this->providerDataRef, DataFieldHandle(0u))[0]; + actualConsumerValue = ISceneDataArrayAccessor::GetDataArray(&this->consumerScene, this->consumerDataRef, DataFieldHandle(0u))[0]; + EXPECT_EQ(providerValue, actualProviderValue); + EXPECT_EQ(consumerValueNew, actualConsumerValue); + } +} diff --git a/renderer/RendererLib/RendererLib/test/DisplayBundleMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayBundleMock.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/test/DisplayBundleMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayBundleMock.cpp index 319587260..e600a30d7 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayBundleMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayBundleMock.cpp @@ -11,7 +11,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { DisplayBundleMock::DisplayBundleMock() { diff --git a/renderer/RendererLib/RendererLib/test/DisplayBundleMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayBundleMock.h similarity index 86% rename from renderer/RendererLib/RendererLib/test/DisplayBundleMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayBundleMock.h index e14d88ff2..c957959bf 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayBundleMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayBundleMock.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYBUNDLEMOCK_H -#define RAMSES_DISPLAYBUNDLEMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/DisplayBundle.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "RendererAPI/IEmbeddedCompositor.h" +#include "internal/RendererLib/DisplayBundle.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" -namespace ramses_internal +namespace ramses::internal { class DisplayBundleMock : public IDisplayBundle { @@ -34,4 +33,4 @@ namespace ramses_internal MOCK_METHOD(std::atomic_int&, traceId, (), (override)); }; } -#endif + diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DisplayConfigTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayConfigTest.cpp new file mode 100644 index 000000000..40fb8da85 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayConfigTest.cpp @@ -0,0 +1,135 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/DisplayConfig.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + class AInternalDisplayConfig : public ::testing::Test + { + public: + ramses::internal::DisplayConfig m_config; + }; + + TEST_F(AInternalDisplayConfig, hasDefaultValues) + { + EXPECT_EQ(ramses::EDeviceType::GLES_3_0, m_config.getDeviceType()); + EXPECT_FALSE(m_config.getFullscreenState()); + EXPECT_EQ(1u, m_config.getAntialiasingSampleCount()); + EXPECT_EQ(1280u, m_config.getDesiredWindowWidth()); + EXPECT_EQ(480u, m_config.getDesiredWindowHeight()); + EXPECT_EQ(0, m_config.getWindowPositionX()); + EXPECT_EQ(0, m_config.getWindowPositionY()); + EXPECT_TRUE(!m_config.getWaylandIviLayerID().isValid()); + EXPECT_FALSE(m_config.getStartVisibleIvi()); + EXPECT_FALSE(m_config.isResizable()); + EXPECT_EQ(0u, m_config.getGPUMemoryCacheSize()); + EXPECT_EQ(glm::vec4(0.f,0.f,0.f,1.f), m_config.getClearColor()); + EXPECT_EQ("", m_config.getWaylandDisplay()); + EXPECT_EQ(ramses::EDepthBufferType::DepthStencil, m_config.getDepthStencilBufferType()); + EXPECT_TRUE(m_config.isAsyncEffectUploadEnabled()); + EXPECT_EQ(std::string(""), m_config.getWaylandSocketEmbedded()); + EXPECT_EQ(std::string(""), m_config.getWaylandSocketEmbeddedGroup()); + EXPECT_EQ(-1, m_config.getWaylandSocketEmbeddedFD()); + EXPECT_EQ(std::string(""), m_config.getPlatformRenderNode()); + EXPECT_EQ(-1, m_config.getSwapInterval()); + EXPECT_EQ(0, m_config.getScenePriority(ramses::internal::SceneId())); + EXPECT_EQ(0, m_config.getScenePriority(ramses::internal::SceneId(15562))); + EXPECT_EQ(10u, m_config.getResourceUploadBatchSize()); + } + + TEST_F(AInternalDisplayConfig, setAndGetValues) + { + m_config.setDeviceType(ramses::EDeviceType::GL_4_2); + EXPECT_EQ(ramses::EDeviceType::GL_4_2, m_config.getDeviceType()); + + m_config.setWindowType(ramses::EWindowType::Android); + EXPECT_EQ(ramses::EWindowType::Android, m_config.getWindowType()); + + m_config.setFullscreenState(true); + EXPECT_TRUE(m_config.getFullscreenState()); + + m_config.setAntialiasingSampleCount(2u); + EXPECT_EQ(2u, m_config.getAntialiasingSampleCount()); + + m_config.setDesiredWindowWidth(100u); + EXPECT_EQ(100u, m_config.getDesiredWindowWidth()); + + m_config.setDesiredWindowHeight(200u); + EXPECT_EQ(200u, m_config.getDesiredWindowHeight()); + + m_config.setWindowPositionX(-10); + EXPECT_EQ(-10, m_config.getWindowPositionX()); + + m_config.setWindowPositionY(10); + EXPECT_EQ(10, m_config.getWindowPositionY()); + + m_config.setWaylandIviLayerID(ramses::internal::WaylandIviLayerId(102u)); + EXPECT_EQ(102u, m_config.getWaylandIviLayerID().getValue()); + + m_config.setStartVisibleIvi(true); + EXPECT_TRUE(m_config.getStartVisibleIvi()); + + m_config.setGPUMemoryCacheSize(256u); + EXPECT_EQ(256u, m_config.getGPUMemoryCacheSize()); + + m_config.setResizable(false); + EXPECT_FALSE(m_config.isResizable()); + + const glm::vec4 clearColor(0.1f, 0.2f, 0.3f, 0.4f); + m_config.setClearColor(clearColor); + EXPECT_EQ(clearColor, m_config.getClearColor()); + + m_config.setDepthStencilBufferType(ramses::EDepthBufferType::Depth); + EXPECT_EQ(ramses::EDepthBufferType::Depth, m_config.getDepthStencilBufferType()); + + m_config.setWaylandDisplay("ramses display"); + EXPECT_EQ("ramses display", m_config.getWaylandDisplay()); + + m_config.setAsyncEffectUploadEnabled(false); + EXPECT_FALSE(m_config.isAsyncEffectUploadEnabled()); + + m_config.setWaylandEmbeddedCompositingSocketName("wayland-11"); + EXPECT_EQ(std::string("wayland-11"), m_config.getWaylandSocketEmbedded()); + + m_config.setWaylandEmbeddedCompositingSocketFD(42); + EXPECT_EQ(42, m_config.getWaylandSocketEmbeddedFD()); + + m_config.setWaylandEmbeddedCompositingSocketGroup("groupname1"); + EXPECT_EQ(std::string("groupname1"), m_config.getWaylandSocketEmbeddedGroup()); + + m_config.setWaylandEmbeddedCompositingSocketPermissions(0654); + EXPECT_EQ(0654u, m_config.getWaylandSocketEmbeddedPermissions()); + + m_config.setPlatformRenderNode("/some/render/node"); + EXPECT_EQ(std::string("/some/render/node"), m_config.getPlatformRenderNode()); + + m_config.setSwapInterval(2); + EXPECT_EQ(2, m_config.getSwapInterval()); + + m_config.setResourceUploadBatchSize(3); + EXPECT_EQ(3u, m_config.getResourceUploadBatchSize()); + + m_config.setScenePriority(ramses::internal::SceneId(15562), -1); + EXPECT_EQ(-1, m_config.getScenePriority(ramses::internal::SceneId(15562))); + EXPECT_EQ(0, m_config.getScenePriority(ramses::internal::SceneId(15562 + 1))); + EXPECT_EQ(1u, m_config.getScenePriorities().size()); + EXPECT_EQ(-1, m_config.getScenePriorities().at(ramses::internal::SceneId(15562))); + } + + TEST_F(AInternalDisplayConfig, canBeCompared) + { + ramses::internal::DisplayConfig config1; + ramses::internal::DisplayConfig config2; + EXPECT_EQ(config1, config2); + + config1.setAntialiasingSampleCount(4u); + EXPECT_NE(config1, config2); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.cpp new file mode 100644 index 000000000..95242560a --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.cpp @@ -0,0 +1,26 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "DisplayControllerMock.h" +#include "WindowMock.h" +namespace ramses::internal { + + const DeviceResourceHandle DisplayControllerMock::FakeFrameBufferHandle(15); + const ProjectionParams DisplayControllerMock::FakeProjectionParams(ProjectionParams::Perspective(30.0f, 2.666f, 0.00001f, 100.f)); + + DisplayControllerMock::DisplayControllerMock() + { + using namespace ::testing; + ON_CALL(*this, getDisplayBuffer()).WillByDefault(Return(FakeFrameBufferHandle)); + ON_CALL(*this, getDisplayWidth()).WillByDefault(Return(WindowMock::FakeWidth)); + ON_CALL(*this, getDisplayHeight()).WillByDefault(Return(WindowMock::FakeHeight)); + ON_CALL(*this, renderScene(_, _, _)).WillByDefault(Return(SceneRenderExecutionIterator())); + } + + DisplayControllerMock::~DisplayControllerMock() = default; +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.h new file mode 100644 index 000000000..958f236b1 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerMock.h @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "gmock/gmock.h" + +namespace ramses::internal{ + + class DisplayControllerMock : public IDisplayController + { + public: + DisplayControllerMock(); + ~DisplayControllerMock() override; + + static const DeviceResourceHandle FakeFrameBufferHandle; + static const ProjectionParams FakeProjectionParams; + + MOCK_METHOD(void, handleWindowEvents, (), (override)); + MOCK_METHOD(bool, canRenderNewFrame, (), (const, override)); + MOCK_METHOD(void, enableContext, (), (override)); + MOCK_METHOD(void, swapBuffers, (), (override)); + MOCK_METHOD(void, clearBuffer, (DeviceResourceHandle, ClearFlags clearFlags, const glm::vec4&), (override)); + MOCK_METHOD(SceneRenderExecutionIterator, renderScene, (const RendererCachedScene&, RenderingContext&, const FrameTimer*), (override)); + MOCK_METHOD(DeviceResourceHandle, getDisplayBuffer, (), (const, override)); + MOCK_METHOD(void, readPixels, (DeviceResourceHandle framebufferHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, std::vector& dataOut), (override)); + MOCK_METHOD(uint32_t, getDisplayWidth, (), (const, override)); + MOCK_METHOD(uint32_t, getDisplayHeight, (), (const, override)); + MOCK_METHOD(IRenderBackend&, getRenderBackend, (), (const, override)); + MOCK_METHOD(IEmbeddedCompositingManager&, getEmbeddedCompositingManager, (), (override)); + MOCK_METHOD(void, validateRenderingStatusHealthy, (), (const, override)); + }; +} + diff --git a/renderer/RendererLib/RendererLib/test/DisplayControllerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerTest.cpp similarity index 85% rename from renderer/RendererLib/RendererLib/test/DisplayControllerTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerTest.cpp index 462cb5d96..3397c415c 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayControllerTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerTest.cpp @@ -8,7 +8,7 @@ #include "DisplayControllerTestBase.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -19,13 +19,13 @@ namespace ramses_internal TEST_F(ADisplayController, canBeCreatedAndDestroyed) { IDisplayController& displayController = createDisplayController(); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, createsFramebuffer) { IDisplayController& displayController = createDisplayController(); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, queriesSurfaceIfItCanRenderNewFrame) @@ -38,14 +38,14 @@ namespace ramses_internal EXPECT_FALSE(displayController.canRenderNewFrame()); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, doesNotEnableContextOnBeginFrame) { IDisplayController& displayController = createDisplayController(); EXPECT_CALL(m_renderBackend.contextMock, enable()).Times(0); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, activatesBufferAndClearsOnClearBuffer_clearAll) @@ -60,11 +60,11 @@ namespace ramses_internal EXPECT_CALL(m_renderBackend.deviceMock, depthWrite(EDepthWrite::Enabled)); RenderState::ScissorRegion scissorRegion{}; EXPECT_CALL(m_renderBackend.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(m_renderBackend.deviceMock, clear(EClearFlags_All)); + EXPECT_CALL(m_renderBackend.deviceMock, clear(ClearFlags(EClearFlag::All))); - displayController.clearBuffer(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, EClearFlags_All, clearColor); + displayController.clearBuffer(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, EClearFlag::All, clearColor); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, activatesBufferAndClearsOnClearBuffer_clearColor) @@ -78,11 +78,11 @@ namespace ramses_internal EXPECT_CALL(m_renderBackend.deviceMock, clearColor(clearColor)); RenderState::ScissorRegion scissorRegion{}; EXPECT_CALL(m_renderBackend.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); - EXPECT_CALL(m_renderBackend.deviceMock, clear(EClearFlags_Color)); + EXPECT_CALL(m_renderBackend.deviceMock, clear(ClearFlags(EClearFlag::Color))); - displayController.clearBuffer(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, EClearFlags_Color, clearColor); + displayController.clearBuffer(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, EClearFlag::Color, clearColor); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, doesNotActivatesBufferNorClearsOnClearBuffer_clearNone) @@ -90,9 +90,9 @@ namespace ramses_internal const glm::vec4 clearColor(0.1f, 0.2f, 0.3f, 0.4f); IDisplayController& displayController = createDisplayController(); - displayController.clearBuffer(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, EClearFlags_None, clearColor); + displayController.clearBuffer(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, EClearFlag::None, clearColor); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, swapsBuffersAndCallsFrameRenderedOnEndFrame) @@ -109,7 +109,7 @@ namespace ramses_internal displayController.swapBuffers(); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, canHandleWindowEvents) @@ -122,7 +122,7 @@ namespace ramses_internal displayController.handleWindowEvents(); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, readsPixelsFromFramebuffer) @@ -137,12 +137,12 @@ namespace ramses_internal InSequence seq; EXPECT_CALL(m_renderBackend.deviceMock, activateRenderTarget(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle)); EXPECT_CALL(m_renderBackend.deviceMock, readPixels(_, x, y, width, height)); - UInt8Vector pixels; + std::vector pixels; displayController.readPixels(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, x, y, width, height, pixels); EXPECT_EQ(width * height * 4u, pixels.size()); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } TEST_F(ADisplayController, readsPixelsFromOffscreenBuffer) @@ -160,11 +160,11 @@ namespace ramses_internal InSequence seq; EXPECT_CALL(m_renderBackend.deviceMock, activateRenderTarget(obRenderTargetDeviceHandle)); EXPECT_CALL(m_renderBackend.deviceMock, readPixels(_, x, y, width, height)); - UInt8Vector pixels; + std::vector pixels; displayController.readPixels(obRenderTargetDeviceHandle, x, y, width, height, pixels); EXPECT_EQ(width * height * 4u, pixels.size()); - destroyDisplayController(displayController); + DestroyDisplayController(displayController); } } diff --git a/renderer/RendererLib/RendererLib/test/DisplayControllerTestBase.h b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerTestBase.h similarity index 82% rename from renderer/RendererLib/RendererLib/test/DisplayControllerTestBase.h rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerTestBase.h index c0f349b34..0d6f4610e 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayControllerTestBase.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayControllerTestBase.h @@ -6,21 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYCONTROLLERTESTBASE_H -#define RAMSES_DISPLAYCONTROLLERTESTBASE_H +#pragma once -#include "renderer_common_gmock_header.h" -#include "RendererLib/DisplayController.h" +#include "internal/RendererLib/DisplayController.h" #include "RenderBackendMock.h" -namespace ramses_internal +namespace ramses::internal { class DisplayControllerTestBase { public: - virtual ~DisplayControllerTestBase() - { - } + virtual ~DisplayControllerTestBase() = default; protected: void expectCreationSequence() @@ -44,7 +40,7 @@ namespace ramses_internal return controller; } - void destroyDisplayController(IDisplayController& controller) + static void DestroyDisplayController(IDisplayController& controller) { delete &controller; } @@ -52,5 +48,3 @@ namespace ramses_internal ::testing::StrictMock m_renderBackend; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/test/DisplayDispatcherMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherMock.cpp similarity index 99% rename from renderer/RendererLib/RendererLib/test/DisplayDispatcherMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherMock.cpp index ad7c80ae1..cd1e961e8 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayDispatcherMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherMock.cpp @@ -9,7 +9,7 @@ #include "DisplayDispatcherMock.h" #include "PlatformFactoryMock.h" -namespace ramses_internal +namespace ramses::internal { DisplayDispatcherMock::DisplayDispatcherMock(const RendererConfig& config, IRendererSceneEventSender& rendererSceneSender, IThreadAliveNotifier& notifier) : DisplayDispatcher(std::make_unique(), config, rendererSceneSender, notifier) diff --git a/renderer/RendererLib/RendererLib/test/DisplayDispatcherMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherMock.h similarity index 93% rename from renderer/RendererLib/RendererLib/test/DisplayDispatcherMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherMock.h index 57908eabc..b9cbba765 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayDispatcherMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherMock.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYDISPATCHERMOCK_H -#define RAMSES_DISPLAYDISPATCHERMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/DisplayDispatcher.h" +#include "internal/RendererLib/DisplayDispatcher.h" #include "PlatformMock.h" #include "DisplayBundleMock.h" #include "DisplayThreadMock.h" -namespace ramses_internal +namespace ramses::internal { class DisplayDispatcherMock : public DisplayDispatcher { @@ -54,4 +53,4 @@ namespace ramses_internal bool m_threaded; }; } -#endif + diff --git a/renderer/RendererLib/RendererLib/test/DisplayDispatcherTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherTest.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/test/DisplayDispatcherTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherTest.cpp index 10abffd0b..bfd40b4f6 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayDispatcherTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherTest.cpp @@ -9,12 +9,12 @@ #include "gmock/gmock.h" #include "DisplayDispatcherMock.h" #include "RendererSceneEventSenderMock.h" -#include "Watchdog/ThreadAliveNotifierMock.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" #include using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ADisplayDispatcher : public ::testing::Test { @@ -109,7 +109,7 @@ namespace ramses_internal })); } - void expectEvents(const RendererEventVector& expectedEvts, const RendererEventVector& evts) + static void ExpectEvents(const RendererEventVector& expectedEvts, const RendererEventVector& evts) { ASSERT_EQ(expectedEvts.size(), evts.size()); for (size_t i = 0; i < evts.size(); ++i) @@ -120,14 +120,14 @@ namespace ramses_internal { RendererEventVector evts; m_displayDispatcher.dispatchRendererEvents(evts); - expectEvents(expectedEvents, evts); + ExpectEvents(expectedEvents, evts); } void dispatchAndExpectSceneControlEvents(const RendererEventVector& expectedEvents) { RendererEventVector evts; m_displayDispatcher.dispatchSceneControlEvents(evts); - expectEvents(expectedEvents, evts); + ExpectEvents(expectedEvents, evts); } protected: @@ -155,7 +155,7 @@ namespace ramses_internal createDisplay(display1); createDisplay(display2); - m_commandBuffer.enqueueCommand(RendererCommand::CreateOffscreenBuffer{ display1, {}, {}, {}, {}, {}, ERenderBufferType_DepthStencilBuffer }); + m_commandBuffer.enqueueCommand(RendererCommand::CreateOffscreenBuffer{ display1, {}, {}, {}, {}, {}, EDepthBufferType::DepthStencil }); m_commandBuffer.enqueueCommand(RendererCommand::DestroyOffscreenBuffer{ display2, {} }); expectCommandPushed(display1, RendererCommand::CreateOffscreenBuffer{}); @@ -311,7 +311,7 @@ namespace ramses_internal update(); // will expect these commands to be pushed to any new display - m_displayDispatcher.m_expectedBroadcastCommandsForNewDisplays.push_back(RendererCommand::ScenePublished{ scene3, EScenePublicationMode::EScenePublicationMode_LocalOnly }); + m_displayDispatcher.m_expectedBroadcastCommandsForNewDisplays.push_back(RendererCommand::ScenePublished{ scene3, EScenePublicationMode::LocalOnly }); m_displayDispatcher.m_expectedBroadcastCommandsForNewDisplays.push_back(RendererCommand::SetLimits_FrameBudgets{}); expectNoCommandPushed(display1); @@ -460,7 +460,7 @@ namespace ramses_internal createDisplay(display1); m_commandBuffer.enqueueCommand(RendererCommand::DestroyOffscreenBuffer{ display2, {} }); - m_commandBuffer.enqueueCommand(RendererCommand::CreateOffscreenBuffer{ display2, {}, {}, {}, {}, {}, ERenderBufferType_DepthStencilBuffer }); + m_commandBuffer.enqueueCommand(RendererCommand::CreateOffscreenBuffer{ display2, {}, {}, {}, {}, {}, EDepthBufferType::DepthStencil }); m_commandBuffer.enqueueCommand(RendererCommand::CreateDmaOffscreenBuffer{ display2, {}, {}, {}, {}, {}, {} }); expectNoCommandPushed(display1); update(); diff --git a/renderer/RendererLib/RendererLib/test/DisplayDispatcherThreadedTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherThreadedTest.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/test/DisplayDispatcherThreadedTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherThreadedTest.cpp index a72b6a849..db7eacdf7 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayDispatcherThreadedTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayDispatcherThreadedTest.cpp @@ -9,14 +9,14 @@ #include "gmock/gmock.h" #include "DisplayDispatcherMock.h" #include "RendererSceneEventSenderMock.h" -#include "Watchdog/ThreadAliveNotifierMock.h" -#include "Utils/ThreadBarrier.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" +#include "internal/Core/Utils/ThreadBarrier.h" #include using namespace testing; using namespace std::chrono_literals; -namespace ramses_internal +namespace ramses::internal { template class MOCK_TYPE> class ADisplayDispatcherThreaded : public ::testing::Test diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DisplayEventHandlerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayEventHandlerTest.cpp new file mode 100644 index 000000000..171e25dc2 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayEventHandlerTest.cpp @@ -0,0 +1,150 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/DisplayEventHandler.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/RendererLib/Enums/EKeyEvent.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal +{ + using namespace testing; + + class ADisplayEventHandler : public ::testing::Test + { + protected: + ADisplayEventHandler() + : m_displayHandle(5110u) + , m_displayEventHandler(m_displayHandle, m_eventCollector) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + } + + [[nodiscard]] RendererEvent getRendererEvent(uint32_t index) const + { + const auto events = m_eventCollector.getRendererEvents(); + if (events.size() <= index) + { + return RendererEvent(ERendererEventType::Invalid); + } + return events[index]; + } + + RendererEventCollector m_eventCollector; + DisplayHandle m_displayHandle; + DisplayEventHandler m_displayEventHandler; + }; + + TEST_F(ADisplayEventHandler, createsRendererEventOnKeyPressEvent) + { + KeyModifiers modifier = EKeyModifier::Alt | EKeyModifier::Shift; + EKeyCode keyCode = ramses::EKeyCode_4; + EKeyEvent keyEventType = EKeyEvent::Pressed; + m_displayEventHandler.onKeyEvent(keyEventType, modifier, keyCode); + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowKeyEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(keyEventType, event.keyEvent.type); + EXPECT_EQ(modifier, event.keyEvent.modifier); + EXPECT_EQ(keyCode, event.keyEvent.keyCode); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnKeyReleasedEvent) + { + KeyModifiers modifier = EKeyModifier::Alt | EKeyModifier::Shift; + EKeyCode keyCode = ramses::EKeyCode_A; + EKeyEvent keyEventType = EKeyEvent::Released; + m_displayEventHandler.onKeyEvent(keyEventType, modifier, keyCode); + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowKeyEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(keyEventType, event.keyEvent.type); + EXPECT_EQ(modifier, event.keyEvent.modifier); + EXPECT_EQ(keyCode, event.keyEvent.keyCode); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnMouseMoveEvent) + { + const glm::ivec2 mousePos(5, 20); + m_displayEventHandler.onMouseEvent(EMouseEvent::Move, mousePos.x, mousePos.y); + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(EMouseEvent::Move, event.mouseEvent.type); + EXPECT_EQ(mousePos, event.mouseEvent.pos); + } + + TEST_F(ADisplayEventHandler, createsRendererEventAndInvokesTouchHandlerOnMouseEvent) + { + glm::ivec2 mousePosition(90u, 250u); + EMouseEvent mouseEventType = EMouseEvent::LeftButtonDown; + m_displayEventHandler.onMouseEvent(mouseEventType, mousePosition.x, mousePosition.y); + + // check renderer event collector + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(mouseEventType, event.mouseEvent.type); + EXPECT_EQ(mousePosition, event.mouseEvent.pos); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnMouseEventWindowEnter) + { + m_displayEventHandler.onMouseEvent(EMouseEvent::WindowEnter, 5, 10); + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(EMouseEvent::WindowEnter, event.mouseEvent.type); + EXPECT_EQ(glm::ivec2(5, 10), event.mouseEvent.pos); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnMouseEventWindowLeave) + { + m_displayEventHandler.onMouseEvent(EMouseEvent::WindowLeave, 10, 20); + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowMouseEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(EMouseEvent::WindowLeave, event.mouseEvent.type); + EXPECT_EQ(glm::ivec2(10, 20), event.mouseEvent.pos); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnWindowClosedEvent) + { + m_displayEventHandler.onClose(); + + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowClosed, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnWindowResizeEvent) + { + m_displayEventHandler.onResize(1280u, 480u); + + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowResizeEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(1280u, event.resizeEvent.width); + EXPECT_EQ(480u, event.resizeEvent.height); + } + + TEST_F(ADisplayEventHandler, createsRendererEventOnWindowMoveEvent) + { + m_displayEventHandler.onWindowMove(1280, 480); + + const RendererEvent event = getRendererEvent(0u); + EXPECT_EQ(ERendererEventType::WindowMoveEvent, event.eventType); + EXPECT_EQ(m_displayHandle, event.displayHandle); + EXPECT_EQ(1280, event.moveEvent.posX); + EXPECT_EQ(480, event.moveEvent.posY); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/DisplaySetupTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplaySetupTest.cpp new file mode 100644 index 000000000..f73388cb1 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplaySetupTest.cpp @@ -0,0 +1,546 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/DisplaySetup.h" +#include "internal/SceneGraph/SceneAPI/RenderState.h" +#include "gtest/gtest.h" + +namespace ramses::internal +{ + using namespace testing; + + class ADisplaySetup : public ::testing::Test + { + protected: + void expectAssignedScenesInOrder(DeviceResourceHandle buffer, std::initializer_list assignedScenes) + { + for (const auto& mappedScene : assignedScenes) + EXPECT_EQ(buffer, displaySetup.findDisplayBufferSceneIsAssignedTo(mappedScene.sceneId)); + + const AssignedScenes& bufferScenes = displaySetup.getDisplayBuffer(buffer).scenes; + ASSERT_EQ(assignedScenes.size(), bufferScenes.size()); + const bool areEqual = std::equal(assignedScenes.begin(), assignedScenes.end(), bufferScenes.cbegin(), [](const AssignedSceneInfo& a, const AssignedSceneInfo& b) + { + return a.sceneId == b.sceneId + && a.globalSceneOrder == b.globalSceneOrder + && a.shown == b.shown; + }); + EXPECT_TRUE(areEqual); + } + + DisplaySetup displaySetup; + + const Viewport viewport{ 1,2,3,4 }; + const glm::vec4 clearColor{ 10,20,30,40 }; + }; + + TEST_F(ADisplaySetup, hasNoBuffersInitially) + { + EXPECT_TRUE(displaySetup.getDisplayBuffers().empty()); + EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); + EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); + } + + TEST_F(ADisplaySetup, canRegisterFramebufferInfoWithInitialValues) + { + const DeviceResourceHandle bufferHandle(33u); + displaySetup.registerDisplayBuffer(bufferHandle, viewport, clearColor, false, false); + + const auto& bufferInfo = displaySetup.getDisplayBuffer(bufferHandle); + EXPECT_EQ(viewport, bufferInfo.viewport); + EXPECT_EQ(clearColor, bufferInfo.clearColor); + EXPECT_TRUE(bufferInfo.scenes.empty()); + EXPECT_FALSE(bufferInfo.isInterruptible); + EXPECT_TRUE(bufferInfo.needsRerender); + + EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); + EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); + } + + TEST_F(ADisplaySetup, canRegisterNonInterruptibleOBInfoWithInitialValues) + { + const DeviceResourceHandle bufferHandle(33u); + displaySetup.registerDisplayBuffer(bufferHandle, viewport, clearColor, true, false); + + const auto& bufferInfo = displaySetup.getDisplayBuffer(bufferHandle); + EXPECT_EQ(viewport, bufferInfo.viewport); + EXPECT_EQ(clearColor, bufferInfo.clearColor); + EXPECT_TRUE(bufferInfo.scenes.empty()); + EXPECT_FALSE(bufferInfo.isInterruptible); + EXPECT_TRUE(bufferInfo.needsRerender); + + EXPECT_EQ(DeviceHandleVector{ bufferHandle }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + + EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); + } + + TEST_F(ADisplaySetup, canRegisterInterruptibleOBInfoWithInitialValues) + { + const DeviceResourceHandle bufferHandle(33u); + displaySetup.registerDisplayBuffer(bufferHandle, viewport, clearColor, true, true); + + const auto& bufferInfo = displaySetup.getDisplayBuffer(bufferHandle); + EXPECT_EQ(viewport, bufferInfo.viewport); + EXPECT_EQ(clearColor, bufferInfo.clearColor); + EXPECT_TRUE(bufferInfo.scenes.empty()); + EXPECT_TRUE(bufferInfo.isInterruptible); + EXPECT_TRUE(bufferInfo.needsRerender); + + EXPECT_EQ(DeviceHandleVector{ bufferHandle }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + + EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); + } + + TEST_F(ADisplaySetup, canBeQueriedForAnyRegisteredTypeOfBufferViaItsHandle) + { + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleFB).isOffscreenBuffer); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).isOffscreenBuffer); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).isInterruptible); + } + + TEST_F(ADisplaySetup, canUnregisterOB) + { + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + displaySetup.unregisterDisplayBuffer(bufferHandleOB); + displaySetup.unregisterDisplayBuffer(bufferHandleOBint); + + EXPECT_TRUE(displaySetup.getDisplayBuffers().empty()); + EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); + EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); + } + + TEST_F(ADisplaySetup, marksNewlyRegisteredBuffersToRerender) + { + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } + + TEST_F(ADisplaySetup, canSetRerenderFlagOfABuffer) + { + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_FALSE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + + EXPECT_TRUE(displaySetup.getNonInterruptibleOffscreenBuffersToRender().empty()); + EXPECT_TRUE(displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid()).empty()); + + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, true); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, true); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, true); + + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } + + TEST_F(ADisplaySetup, canMapAndUnmapSceneToBuffer) + { + const SceneId scene1(12u); + const SceneId scene2(13u); + const SceneId scene3(14u); + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 0); + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 0); + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 0); + + expectAssignedScenesInOrder(bufferHandleFB, { { scene1, 0, false } }); + expectAssignedScenesInOrder(bufferHandleOB, { { scene2, 0, false } }); + expectAssignedScenesInOrder(bufferHandleOBint, { { scene3, 0, false } }); + + displaySetup.unassignScene(scene1); + displaySetup.unassignScene(scene2); + displaySetup.unassignScene(scene3); + + expectAssignedScenesInOrder(bufferHandleFB, {}); + expectAssignedScenesInOrder(bufferHandleOB, {}); + expectAssignedScenesInOrder(bufferHandleOBint, {}); + EXPECT_FALSE(displaySetup.findDisplayBufferSceneIsAssignedTo(scene1).isValid()); + EXPECT_FALSE(displaySetup.findDisplayBufferSceneIsAssignedTo(scene2).isValid()); + EXPECT_FALSE(displaySetup.findDisplayBufferSceneIsAssignedTo(scene3).isValid()); + } + + TEST_F(ADisplaySetup, storesAssignedScenesInRenderingOrder) + { + const SceneId scene1(12u); + const SceneId scene2(13u); + const SceneId scene3(14u); + const DeviceResourceHandle bufferHandleOB(34u); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 10); + expectAssignedScenesInOrder(bufferHandleOB, { { scene2, 10, false } }); + + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOB, -10); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene2, 10, false } }); + + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOB, 0); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, false },{ scene2, 10, false } }); + + displaySetup.unassignScene(scene1); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene2, 10, false } }); + + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOB, 666); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene2, 10, false },{ scene1, 666, false } }); + + displaySetup.unassignScene(scene2); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 666, false } }); + + displaySetup.unassignScene(scene3); + expectAssignedScenesInOrder(bufferHandleOB, { { scene1, 666, false } }); + + displaySetup.unassignScene(scene1); + expectAssignedScenesInOrder(bufferHandleOB, {}); + } + + TEST_F(ADisplaySetup, triggersRerenderForBufferWithMappedOrUnmappedScene) + { + const SceneId scene1(12u); + const SceneId scene2(13u); + const SceneId scene3(14u); + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + // reset re-render state + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // trigger re-render by mapping + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 0); + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 0); + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 0); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + + // reset re-render state + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // trigger re-render by unmapping + displaySetup.unassignScene(scene1); + displaySetup.unassignScene(scene2); + displaySetup.unassignScene(scene3); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } + + TEST_F(ADisplaySetup, canSetSceneShown) + { + const SceneId scene1(12u); + const SceneId scene2(13u); + const SceneId scene3(14u); + const DeviceResourceHandle bufferHandleOB(34u); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOB, 0); + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 10); + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOB, -10); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, false },{ scene2, 10, false } }); + + displaySetup.setSceneShown(scene1, true); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, true },{ scene2, 10, false } }); + displaySetup.setSceneShown(scene2, true); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, true },{ scene2, 10, true } }); + displaySetup.setSceneShown(scene3, true); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, true },{ scene1, 0, true },{ scene2, 10, true } }); + + displaySetup.setSceneShown(scene1, false); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, true },{ scene1, 0, false },{ scene2, 10, true } }); + displaySetup.setSceneShown(scene2, false); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, true },{ scene1, 0, false },{ scene2, 10, false } }); + displaySetup.setSceneShown(scene3, false); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, -10, false },{ scene1, 0, false },{ scene2, 10, false } }); + } + + TEST_F(ADisplaySetup, triggersRerenderForBufferWithChangedSceneShowState) + { + const SceneId scene1(12u); + const SceneId scene2(13u); + const SceneId scene3(14u); + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 0); + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 0); + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 0); + + // reset re-render state + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // trigger re-render by scene showing + displaySetup.setSceneShown(scene1, true); + displaySetup.setSceneShown(scene2, true); + displaySetup.setSceneShown(scene3, true); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + + // reset re-render state + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // trigger re-render by scene hiding + displaySetup.setSceneShown(scene1, false); + displaySetup.setSceneShown(scene2, false); + displaySetup.setSceneShown(scene3, false); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } + + TEST_F(ADisplaySetup, canSetClearFlagsForAnyRegisteredBuffer) + { + constexpr DeviceResourceHandle bufferHandleFB{ 33u }; + constexpr DeviceResourceHandle bufferHandleOB{ 34u }; + constexpr DeviceResourceHandle bufferHandleOBint{ 35u }; + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + EXPECT_EQ(EClearFlag::All, displaySetup.getDisplayBuffer(bufferHandleFB).clearFlags); + EXPECT_EQ(EClearFlag::All, displaySetup.getDisplayBuffer(bufferHandleOB).clearFlags); + EXPECT_EQ(EClearFlag::All, displaySetup.getDisplayBuffer(bufferHandleOBint).clearFlags); + + displaySetup.setClearFlags(bufferHandleFB, EClearFlag::None); + displaySetup.setClearFlags(bufferHandleOB, EClearFlag::Color); + displaySetup.setClearFlags(bufferHandleOBint, EClearFlag::All); + + EXPECT_EQ(EClearFlag::None, displaySetup.getDisplayBuffer(bufferHandleFB).clearFlags); + EXPECT_EQ(EClearFlag::Color, displaySetup.getDisplayBuffer(bufferHandleOB).clearFlags); + EXPECT_EQ(EClearFlag::All, displaySetup.getDisplayBuffer(bufferHandleOBint).clearFlags); + } + + TEST_F(ADisplaySetup, canSetClearColorForAnyRegisteredBuffer) + { + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + EXPECT_EQ(clearColor, displaySetup.getDisplayBuffer(bufferHandleFB).clearColor); + EXPECT_EQ(clearColor, displaySetup.getDisplayBuffer(bufferHandleOB).clearColor); + EXPECT_EQ(clearColor, displaySetup.getDisplayBuffer(bufferHandleOBint).clearColor); + + const glm::vec4 clr1{ 9, 8, 7, 6 }; + const glm::vec4 clr2{ 5, 4, 3, 2 }; + const glm::vec4 clr3{ 1, 0, -1, -2 }; + displaySetup.setClearColor(bufferHandleFB, clr1); + displaySetup.setClearColor(bufferHandleOB, clr2); + displaySetup.setClearColor(bufferHandleOBint, clr3); + + EXPECT_EQ(clr1, displaySetup.getDisplayBuffer(bufferHandleFB).clearColor); + EXPECT_EQ(clr2, displaySetup.getDisplayBuffer(bufferHandleOB).clearColor); + EXPECT_EQ(clr3, displaySetup.getDisplayBuffer(bufferHandleOBint).clearColor); + } + + TEST_F(ADisplaySetup, triggersRerenderForBufferWithChangedClearColor) + { + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + // reset re-render state + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // trigger re-render of all buffers by setting clear color for a single buffer + displaySetup.setClearColor(bufferHandleOB, clearColor); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } + + TEST_F(ADisplaySetup, canResizeDisplayBuffer) + { + const DeviceResourceHandle bufferHandleFB(33u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + + EXPECT_EQ(viewport.width, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.width); + EXPECT_EQ(viewport.height, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.height); + + displaySetup.setDisplayBufferSize(bufferHandleFB, 100u, 999u); + EXPECT_EQ(100u, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.width); + EXPECT_EQ(999u, displaySetup.getDisplayBuffer(bufferHandleFB).viewport.height); + } + + TEST_F(ADisplaySetup, triggersRerenderForBufferWithResizedDisplayBuffer) + { + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + // reset re-render state + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // trigger re-render of all buffers by resizing any display buffer + displaySetup.setDisplayBufferSize(bufferHandleOB, 1u, 2u); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } + + TEST_F(ADisplaySetup, getsListOfInterruptibleBuffersToRerenderStartingFromInterruptedBuffer) + { + const DeviceResourceHandle bufferHandle1(33u); + const DeviceResourceHandle bufferHandle2(34u); + const DeviceResourceHandle bufferHandle3(35u); + displaySetup.registerDisplayBuffer(bufferHandle1, viewport, clearColor, true, true); + displaySetup.registerDisplayBuffer(bufferHandle2, viewport, clearColor, true, true); + displaySetup.registerDisplayBuffer(bufferHandle3, viewport, clearColor, true, true); + + const DeviceHandleVector expectedBuffersToRerender{ bufferHandle2, bufferHandle3 }; + EXPECT_EQ(expectedBuffersToRerender, displaySetup.getInterruptibleOffscreenBuffersToRender(bufferHandle2)); + } + + TEST_F(ADisplaySetup, getsListOfInterruptibleBuffersToRerenderStartingFromInterruptedBufferAndOnlyThoseMarkedToRerender) + { + const DeviceResourceHandle bufferHandle1(33u); + const DeviceResourceHandle bufferHandle2(34u); + const DeviceResourceHandle bufferHandle3(35u); + const DeviceResourceHandle bufferHandle4(36u); + const DeviceResourceHandle bufferHandle5(37u); + displaySetup.registerDisplayBuffer(bufferHandle1, viewport, clearColor, true, true); + displaySetup.registerDisplayBuffer(bufferHandle2, viewport, clearColor, true, true); + displaySetup.registerDisplayBuffer(bufferHandle3, viewport, clearColor, true, true); + displaySetup.registerDisplayBuffer(bufferHandle4, viewport, clearColor, true, true); + displaySetup.registerDisplayBuffer(bufferHandle5, viewport, clearColor, true, true); + + displaySetup.setDisplayBufferToBeRerendered(bufferHandle2, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandle4, false); + + // the interrupted buffer (2) is always put to list regardless of its re-render flag, + // buffer1 is skipped because it is before interrupted buffer, + // buffer4 is skipped because it is not marked to re-render + const DeviceHandleVector expectedBuffersToRerender{ bufferHandle2, bufferHandle3, bufferHandle5 }; + EXPECT_EQ(expectedBuffersToRerender, displaySetup.getInterruptibleOffscreenBuffersToRender(bufferHandle2)); + } + + TEST_F(ADisplaySetup, canAssignAnAlreadyAssignedSceneToAnotherBufferAndPreserveShowState) + { + const SceneId scene1(12u); + const SceneId scene2(13u); + const SceneId scene3(14u); + const DeviceResourceHandle bufferHandleFB(33u); + const DeviceResourceHandle bufferHandleOB(34u); + const DeviceResourceHandle bufferHandleOBint(35u); + displaySetup.registerDisplayBuffer(bufferHandleFB, viewport, clearColor, false, false); + displaySetup.registerDisplayBuffer(bufferHandleOB, viewport, clearColor, true, false); + displaySetup.registerDisplayBuffer(bufferHandleOBint, viewport, clearColor, true, true); + + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleFB, 1); + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleOB, 2); + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOBint, 3); + + displaySetup.setSceneShown(scene1, true); + displaySetup.setSceneShown(scene2, false); + displaySetup.setSceneShown(scene3, true); + + expectAssignedScenesInOrder(bufferHandleFB, { { scene1, 1, true } }); + expectAssignedScenesInOrder(bufferHandleOB, { { scene2, 2, false } }); + expectAssignedScenesInOrder(bufferHandleOBint, { { scene3, 3, true } }); + + // reset re-render states + displaySetup.setDisplayBufferToBeRerendered(bufferHandleFB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOB, false); + displaySetup.setDisplayBufferToBeRerendered(bufferHandleOBint, false); + + // change scenes assignments + displaySetup.assignSceneToDisplayBuffer(scene2, bufferHandleFB, 1); + displaySetup.assignSceneToDisplayBuffer(scene3, bufferHandleOB, 2); + displaySetup.assignSceneToDisplayBuffer(scene1, bufferHandleOBint, 3); + + expectAssignedScenesInOrder(bufferHandleFB, { { scene2, 1, false } }); + expectAssignedScenesInOrder(bufferHandleOB, { { scene3, 2, true } }); + expectAssignedScenesInOrder(bufferHandleOBint, { { scene1, 3, true } }); + + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleFB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOB).needsRerender); + EXPECT_TRUE(displaySetup.getDisplayBuffer(bufferHandleOBint).needsRerender); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOB }, displaySetup.getNonInterruptibleOffscreenBuffersToRender()); + EXPECT_EQ(DeviceHandleVector{ bufferHandleOBint }, displaySetup.getInterruptibleOffscreenBuffersToRender(DeviceResourceHandle::Invalid())); + } +} diff --git a/renderer/RendererLib/RendererLib/test/DisplayThreadMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadMock.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/test/DisplayThreadMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadMock.cpp index eb11407f4..7e8bcc2ba 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayThreadMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadMock.cpp @@ -8,7 +8,7 @@ #include "DisplayThreadMock.h" -namespace ramses_internal +namespace ramses::internal { DisplayThreadMock::DisplayThreadMock() { diff --git a/renderer/RendererLib/RendererLib/test/DisplayThreadMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadMock.h similarity index 87% rename from renderer/RendererLib/RendererLib/test/DisplayThreadMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadMock.h index d250063d3..fb1b62d96 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayThreadMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadMock.h @@ -6,14 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DISPLAYTHREADMOCK_H -#define RAMSES_DISPLAYTHREADMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/DisplayThread.h" +#include "internal/RendererLib/DisplayThread.h" #include "PlatformMock.h" -namespace ramses_internal +namespace ramses::internal { class DisplayThreadMock : public IDisplayThread { @@ -28,4 +27,4 @@ namespace ramses_internal MOCK_METHOD(uint32_t, getFrameCounter, (), (const, override)); }; } -#endif + diff --git a/renderer/RendererLib/RendererLib/test/DisplayThreadTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadTest.cpp similarity index 93% rename from renderer/RendererLib/RendererLib/test/DisplayThreadTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadTest.cpp index 7ef92d39c..9a7ccfea8 100644 --- a/renderer/RendererLib/RendererLib/test/DisplayThreadTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/DisplayThreadTest.cpp @@ -7,20 +7,19 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "RendererLib/DisplayThread.h" +#include "internal/RendererLib/DisplayThread.h" #include "DisplayBundleMock.h" -#include "Utils/ThreadBarrier.h" -#include "Watchdog/ThreadAliveNotifierMock.h" +#include "internal/Core/Utils/ThreadBarrier.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" using namespace testing; using namespace std::chrono_literals; -namespace ramses_internal +namespace ramses::internal { class AThreadAliveHandlerExpectingOneRegisterAndUnregister final : public StrictMock { public: AThreadAliveHandlerExpectingOneRegisterAndUnregister() - : StrictMock() { EXPECT_CALL(*this, registerThread()).WillOnce(Return(ThreadAliveNotifierMock::dummyThreadId)); EXPECT_CALL(*this, unregisterThread(ThreadAliveNotifierMock::dummyThreadId)).Times(1); @@ -53,7 +52,7 @@ namespace ramses_internal { EXPECT_CALL(m_aliveHandlerMock, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(9)); EXPECT_CALL(m_aliveHandlerMock, calculateTimeout()).Times(AtLeast(0)).WillRepeatedly(Return(20ms)); - EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto, auto) + EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto /*unused*/, auto /*unused*/) { m_loopCount++; })); @@ -82,7 +81,7 @@ namespace ramses_internal { EXPECT_CALL(m_aliveHandlerMock, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(9)); EXPECT_CALL(m_aliveHandlerMock, calculateTimeout()).Times(AtLeast(0)).WillRepeatedly(Return(20ms)); - EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto, auto) + EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto /*unused*/, auto /*unused*/) { m_loopCount++; })); @@ -103,7 +102,7 @@ namespace ramses_internal { EXPECT_CALL(m_aliveHandlerMock, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(9)); EXPECT_CALL(m_aliveHandlerMock, calculateTimeout()).Times(AtLeast(0)).WillRepeatedly(Return(20ms)); - EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateOnly, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto, auto) + EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateOnly, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto /*unused*/, auto /*unused*/) { m_loopCount++; })); @@ -118,7 +117,7 @@ namespace ramses_internal { EXPECT_CALL(m_aliveHandlerMock, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(9)); EXPECT_CALL(m_aliveHandlerMock, calculateTimeout()).Times(AtLeast(0)).WillRepeatedly(Return(20ms)); - EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto, auto) + EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto /*unused*/, auto /*unused*/) { m_loopCount++; })); @@ -131,14 +130,14 @@ namespace ramses_internal TEST_F(ADisplayThread, issuesAtLeastOneNotificationPerLoopOrPerWait) { - EXPECT_CALL(m_aliveHandlerMock, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(10)).WillRepeatedly([this](auto) { m_notifyCounter++; }); + EXPECT_CALL(m_aliveHandlerMock, notifyAlive(ThreadAliveNotifierMock::dummyThreadId)).Times(AtLeast(10)).WillRepeatedly([this](auto /*unused*/) { m_notifyCounter++; }); EXPECT_CALL(m_aliveHandlerMock, calculateTimeout()).Times(AtLeast(0)).WillRepeatedly([this]() { m_timeoutCounter++; EXPECT_GE(m_notifyCounter, m_timeoutCounter + m_loopCount); return 20ms; }); - EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto, auto) + EXPECT_CALL(m_displayBundleMock, doOneLoop(ELoopMode::UpdateAndRender, _)).Times(AnyNumber()).WillRepeatedly(Invoke([&](auto /*unused*/, auto /*unused*/) { if (++m_loopCount == 10) m_displayThread.stopUpdating(); diff --git a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositingManagerMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerMock.cpp similarity index 97% rename from renderer/RendererLib/RendererTestCommon/EmbeddedCompositingManagerMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerMock.cpp index cbbb40e7a..a7ae6b6ad 100644 --- a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositingManagerMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerMock.cpp @@ -9,7 +9,7 @@ #include "EmbeddedCompositingManagerMock.h" -namespace ramses_internal +namespace ramses::internal { EmbeddedCompositingManagerMock::EmbeddedCompositingManagerMock() { diff --git a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositingManagerMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerMock.h similarity index 87% rename from renderer/RendererLib/RendererTestCommon/EmbeddedCompositingManagerMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerMock.h index 8c1331b24..df4c87637 100644 --- a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositingManagerMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerMock.h @@ -7,14 +7,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITINGMANAGERMOCK_H -#define RAMSES_EMBEDDEDCOMPOSITINGMANAGERMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositingManager.h" #include "gmock/gmock.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -33,4 +31,4 @@ namespace ramses_internal MOCK_METHOD(bool, hasRealCompositor, (), (const, override)); }; } -#endif + diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerTest.cpp new file mode 100644 index 000000000..80ccbc3a1 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/EmbeddedCompositingManagerTest.cpp @@ -0,0 +1,274 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/EmbeddedCompositingManager.h" +#include "DeviceMock.h" +#include "EmbeddedCompositorMock.h" +#include "internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal +{ + using namespace testing; + + class AnEmbeddedCompositingManager : public ::testing::Test + { + public: + AnEmbeddedCompositingManager() + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + } + + void expectStreamTexUpload(WaylandIviSurfaceId iviSurface, DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle) + { + EXPECT_CALL(deviceMock, uploadStreamTexture2D(_, _, _, _, _, _)).WillOnce(Return(textureDeviceHandle)); + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(iviSurface)).WillOnce(Return(false)); + } + + void expectStreamTexUnload(DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle) + { + EXPECT_CALL(deviceMock, deleteTexture(textureDeviceHandle)); + } + + void addStreamReference(WaylandIviSurfaceId iviSurface, DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle, bool expectTextureUpload = true) + { + if (expectTextureUpload) + expectStreamTexUpload(iviSurface, textureDeviceHandle); + embeddedCompositingManager.refStream(iviSurface); + } + + void removeStreamReference(WaylandIviSurfaceId iviSurface, DeviceResourceHandle textureDeviceHandle = compositedTextureDeviceHandle, bool expectTextureUnload = true) + { + if (expectTextureUnload) + expectStreamTexUnload(textureDeviceHandle); + embeddedCompositingManager.unrefStream(iviSurface); + } + + void expectStreamTextureChangedState(const WaylandIviSurfaceIdVector& expectedStreamsWithStateChange, const WaylandIviSurfaceIdVector& expectedNewStreams, const WaylandIviSurfaceIdVector& expectedObsoleteStreams) + { + WaylandIviSurfaceIdVector streamsWithStateChange; + WaylandIviSurfaceIdVector newStreams; + WaylandIviSurfaceIdVector obsoleteStreams; + embeddedCompositingManager.dispatchStateChangesOfSources(streamsWithStateChange, newStreams, obsoleteStreams); + + EXPECT_EQ(expectedStreamsWithStateChange, streamsWithStateChange); + EXPECT_EQ(expectedNewStreams, newStreams); + EXPECT_EQ(expectedObsoleteStreams, obsoleteStreams); + } + + protected: + StrictMock deviceMock; + StrictMock embeddedCompositorMock; + TextureUploadingAdapter_Base textureUploadingAdapter = TextureUploadingAdapter_Base(deviceMock); + EmbeddedCompositingManager embeddedCompositingManager = EmbeddedCompositingManager(deviceMock, embeddedCompositorMock, textureUploadingAdapter); + const WaylandIviSurfaceId streamTextureSourceId = WaylandIviSurfaceId(11u); + const WaylandIviSurfaceId streamTextureSourceId2 = WaylandIviSurfaceId(12u); + + static constexpr DeviceResourceHandle compositedTextureDeviceHandle{ 111u }; + }; + + TEST_F(AnEmbeddedCompositingManager, CanUploadAndDeleteStreamTexture) + { + addStreamReference(streamTextureSourceId); + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); + + removeStreamReference(streamTextureSourceId); + } + + TEST_F(AnEmbeddedCompositingManager, ReturnsInvalidDeviceResourceHandleForInvalidStreamSourceId) + { + const WaylandIviSurfaceId fakeId(234); + + //simulate compositing content is not available + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(fakeId)).WillOnce(Return(false)); + EXPECT_EQ(DeviceResourceHandle::Invalid(), embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(fakeId)); + } + + TEST_F(AnEmbeddedCompositingManager, CanGetCorrectDeviceHandleForValidStreamTextureId) + { + addStreamReference(streamTextureSourceId); + + //simulate compositing content is available + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + //expect composited texture + EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); + + //simulate compositing content is NOT available + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(false)); + //expect invalid texture handle + EXPECT_EQ(DeviceResourceHandle::Invalid(), embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); + + removeStreamReference(streamTextureSourceId); + } + + TEST_F(AnEmbeddedCompositingManager, CanUpdateCompositingResources) + { + const WaylandIviSurfaceIdSet fakeUpdatedStreamTextureSourceIds{ streamTextureSourceId, streamTextureSourceId2 }; + + addStreamReference(streamTextureSourceId); + addStreamReference(streamTextureSourceId2); + + { + InSequence s; + EXPECT_CALL(embeddedCompositorMock, handleRequestsFromClients()); + EXPECT_CALL(embeddedCompositorMock, endFrame(false)); + embeddedCompositingManager.processClientRequests(); + + EXPECT_CALL(embeddedCompositorMock, hasUpdatedStreamTextureSources()).WillOnce(Return(true)); + EXPECT_TRUE(embeddedCompositingManager.hasUpdatedContentFromStreamSourcesToUpload()); + + EXPECT_CALL(embeddedCompositorMock, dispatchUpdatedStreamTextureSourceIds()).WillOnce(Return(fakeUpdatedStreamTextureSourceIds)); + } + + // these expectations don't necessarily come in same order + EXPECT_CALL(embeddedCompositorMock, uploadCompositingContentForStreamTexture(streamTextureSourceId, _, _)).WillOnce(Return(13u)); + EXPECT_CALL(embeddedCompositorMock, uploadCompositingContentForStreamTexture(streamTextureSourceId2, _, _)).WillOnce(Return(6u)); + StreamSourceUpdates updates; + embeddedCompositingManager.uploadResourcesAndGetUpdates(updates); + ASSERT_EQ(2u, updates.size()); + int idx1 = 0; + int idx2 = 1; + // updates might come in different order + if (updates.front().first != streamTextureSourceId) + std::swap(idx1, idx2); + EXPECT_EQ(streamTextureSourceId, updates[idx1].first); + EXPECT_EQ(13u, updates[idx1].second); + EXPECT_EQ(streamTextureSourceId2, updates[idx2].first); + EXPECT_EQ(6u, updates[idx2].second); + } + + TEST_F(AnEmbeddedCompositingManager, CanNotifyClients) + { + InSequence s; + EXPECT_CALL(embeddedCompositorMock, endFrame(true)); + embeddedCompositingManager.notifyClients(); + } + + TEST_F(AnEmbeddedCompositingManager, UploadsOneCompositedTextureForTwoEqualStreamTexturesSourceIds) + { + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); + + //make sure it returns the composited texture device handle for the stream textures id + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillRepeatedly(Return(true)); + EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); + } + + TEST_F(AnEmbeddedCompositingManager, UploadsTwoCompositedTextureForTwoDifferentStreamTextureSourceIds) + { + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + + const WaylandIviSurfaceId secondStreamTextureSourceId(22); + const DeviceResourceHandle secondCompositedTextureDeviceHandle(222); + addStreamReference(secondStreamTextureSourceId, secondCompositedTextureDeviceHandle); + + //make sure it returns a different composited texture device handle for each stream texture source id + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); + + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(secondStreamTextureSourceId)).WillOnce(Return(true)); + EXPECT_EQ(secondCompositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(secondStreamTextureSourceId)); + } + + TEST_F(AnEmbeddedCompositingManager, DeletesCompositedTextureOnlyAfterAllReferencesRemoved) + { + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); + + removeStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); + removeStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + } + + TEST_F(AnEmbeddedCompositingManager, CanUploadAndDeleteStreamTextureWithAvailableContent) + { + EXPECT_CALL(deviceMock, uploadStreamTexture2D(_, _, _, _, _, _)).WillOnce(Return(compositedTextureDeviceHandle)); + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + EXPECT_CALL(embeddedCompositorMock, uploadCompositingContentForStreamTexture(streamTextureSourceId, _, _)); + + embeddedCompositingManager.refStream(streamTextureSourceId); + + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + EXPECT_EQ(compositedTextureDeviceHandle, embeddedCompositingManager.getCompositedTextureDeviceHandleForStreamTexture(streamTextureSourceId)); + + removeStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + } + + TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_NoChange) + { + const WaylandIviSurfaceIdSet empty; + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(empty)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); + + expectStreamTextureChangedState({}, {}, {}); + } + + TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_IfStreamSourceBecomesAvailable) + { + const WaylandIviSurfaceIdSet empty; + const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); + + expectStreamTextureChangedState({}, { streamTextureSourceId }, {}); + } + + TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_EmptyAfterDispatching) + { + const WaylandIviSurfaceIdSet empty; + const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); + expectStreamTextureChangedState({}, { streamTextureSourceId }, {}); + + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(empty)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); + expectStreamTextureChangedState({}, {}, {}); + } + + TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_IfStreamSourceBecomesUnavailable) + { + const WaylandIviSurfaceIdSet empty; + const WaylandIviSurfaceIdSet obsoleteStreams{ streamTextureSourceId }; + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(empty)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(obsoleteStreams)); + + expectStreamTextureChangedState({}, {}, { streamTextureSourceId }); + } + + TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_multipleRefs) + { + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle, false); + + const WaylandIviSurfaceIdSet empty; + const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + + expectStreamTextureChangedState({ streamTextureSourceId }, { streamTextureSourceId }, {}); + } + + TEST_F(AnEmbeddedCompositingManager, CanDispatchStreamTexturesStateChange_multipleSources) + { + addStreamReference(streamTextureSourceId, compositedTextureDeviceHandle); + addStreamReference(streamTextureSourceId2, compositedTextureDeviceHandle); + + const WaylandIviSurfaceIdSet empty; + const WaylandIviSurfaceIdSet newStreams{ streamTextureSourceId }; + EXPECT_CALL(embeddedCompositorMock, dispatchNewStreamTextureSourceIds()).WillOnce(Return(newStreams)); + EXPECT_CALL(embeddedCompositorMock, dispatchObsoleteStreamTextureSourceIds()).WillOnce(Return(empty)); + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId)).WillOnce(Return(true)); + EXPECT_CALL(embeddedCompositorMock, isContentAvailableForStreamTexture(streamTextureSourceId2)).WillOnce(Return(true)); + + // order changed due to hashmap, not important for test + expectStreamTextureChangedState({ streamTextureSourceId2, streamTextureSourceId }, { streamTextureSourceId }, {}); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/IntersectionUtilsTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/IntersectionUtilsTest.cpp new file mode 100644 index 000000000..704edda8b --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/IntersectionUtilsTest.cpp @@ -0,0 +1,581 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2019 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/IntersectionUtils.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/RendererLib/RendererScenes.h" +#include "SceneAllocateHelper.h" +#include "internal/Core/Math3d/ProjectionParams.h" +#include "internal/Core/Math3d/CameraMatrixHelper.h" +#include "glm/gtx/transform.hpp" + +namespace ramses::internal +{ + static DataBufferHandle prepareGeometryBuffer(TransformationLinkCachedScene& scene, SceneAllocateHelper& sceneAllocator, const float vertexPositionsTriangle[], const uint32_t bufferSize) + { + DataBufferHandle geometryBuffer = sceneAllocator.allocateDataBuffer(EDataBufferType::VertexBuffer, EDataType::Vector3F, bufferSize); + const auto* data = reinterpret_cast(vertexPositionsTriangle); + scene.updateDataBuffer(geometryBuffer, 0, bufferSize, data); + return geometryBuffer; + } + + static CameraHandle preparePickableCamera(TransformationLinkCachedScene& scene, SceneAllocateHelper& sceneAllocator, const glm::ivec2 viewportOffset, const glm::ivec2 viewportSize, const glm::vec3 translation, const glm::vec3 rotation, const glm::vec3 scale) + { + NodeHandle cameraNodeHandle = sceneAllocator.allocateNode(); + const auto dataLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference} }, {}); + const auto dataInstance = sceneAllocator.allocateDataInstance(dataLayout); + const auto vpDataRefLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I} }, {}); + const auto vpOffsetInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); + const auto vpSizeInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); + const auto frustumPlanesLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector4F} }, {}); + const auto frustumPlanes = sceneAllocator.allocateDataInstance(frustumPlanesLayout); + const auto frustumNearFarLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2F} }, {}); + const auto frustumNearFar = sceneAllocator.allocateDataInstance(frustumNearFarLayout); + scene.setDataReference(dataInstance, Camera::ViewportOffsetField, vpOffsetInstance); + scene.setDataReference(dataInstance, Camera::ViewportSizeField, vpSizeInstance); + scene.setDataReference(dataInstance, Camera::FrustumPlanesField, frustumPlanes); + scene.setDataReference(dataInstance, Camera::FrustumNearFarPlanesField, frustumNearFar); + const CameraHandle cameraHandle = sceneAllocator.allocateCamera(ECameraProjectionType::Perspective, cameraNodeHandle, dataInstance); + scene.setDataSingleVector2i(vpOffsetInstance, DataFieldHandle{ 0 }, viewportOffset); + scene.setDataSingleVector2i(vpSizeInstance, DataFieldHandle{ 0 }, viewportSize); + const ProjectionParams params = ProjectionParams::Perspective(19.f, static_cast(viewportSize.x) / static_cast(viewportSize.y), 0.1f, 100.f); + scene.setDataSingleVector4f(frustumPlanes, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); + scene.setDataSingleVector2f(frustumNearFar, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); + + TransformHandle cameraTransformation = sceneAllocator.allocateTransform(cameraNodeHandle); + scene.setTranslation(cameraTransformation, translation); + scene.setRotation(cameraTransformation, glm::vec4(rotation, 1.f), ERotationType::Euler_XYZ); + scene.setScaling(cameraTransformation, scale); + return cameraHandle; + } + + static void preparePickableObject(TransformationLinkCachedScene& scene, SceneAllocateHelper& sceneAllocator, DataBufferHandle geometryBuffer, CameraHandle cameraHandle, PickableObjectId pickableId, const glm::vec3 translation, const glm::vec3 rotation, const glm::vec3 scale) + { + const NodeHandle pickableNodeHandle = sceneAllocator.allocateNode(); + const PickableObjectHandle pickableHandle = sceneAllocator.allocatePickableObject(geometryBuffer, pickableNodeHandle, pickableId); + scene.setPickableObjectCamera(pickableHandle, cameraHandle); + TransformHandle pickableTransformation = sceneAllocator.allocateTransform(pickableNodeHandle); + scene.setTranslation(pickableTransformation, translation); + scene.setRotation(pickableTransformation, glm::vec4(rotation, 1.f), ERotationType::Euler_XYZ); + scene.setScaling(pickableTransformation, scale); + } + + static void checkSceneForIntersectedPickableObjects(const TransformationLinkCachedScene& scene, const glm::vec2 coords, const glm::ivec2 dispResolution, const PickableObjectIds& expectedPickables) + { + const auto xCoordDisplaySpace = static_cast(std::lroundf((coords.x + 1.f) * static_cast(dispResolution.x) / 2.f)); + const auto yCoordDisplaySpace = static_cast(std::lroundf((coords.y + 1.f) * static_cast(dispResolution.y) / 2.f)); + + PickableObjectIds resultPickables; + IntersectionUtils::CheckSceneForIntersectedPickableObjects(scene, {xCoordDisplaySpace, yCoordDisplaySpace}, resultPickables); + + EXPECT_EQ(resultPickables, expectedPickables); + } + + TEST(IntersectionUtilsTest, canIntersectTriangle1) + { + const glm::vec3 rayOrig(0, 0, 0); + const glm::vec3 v0(0, 0, 5); + const glm::vec3 v1(1, 0, 5); + const glm::vec3 v2(0, 1, 5); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayHit(0, 0, 1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayHit, intersectionPoint, distance); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(5.f, distance); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); + EXPECT_FLOAT_EQ(5.f, intersectionPoint.z); + } + + TEST(IntersectionUtilsTest, canIntersectTriangle2) + { + const glm::vec3 rayOrig(1, 1, 1); + const glm::vec3 v0(1, 1, 5); + const glm::vec3 v1(2, 1, 5); + const glm::vec3 v2(1, 2, 5); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayHit(0, 0, 1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayHit, intersectionPoint, distance); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(4.f, distance); + EXPECT_FLOAT_EQ(1.f, intersectionPoint.x); + EXPECT_FLOAT_EQ(1.f, intersectionPoint.y); + EXPECT_FLOAT_EQ(5.f, intersectionPoint.z); + } + + TEST(IntersectionUtilsTest, noIntersectRayNotPointingToTriangle) + { + const glm::vec3 rayOrig(0, 0, 0); + const glm::vec3 v0(0, 0, 5); + const glm::vec3 v1(1, 0, 5); + const glm::vec3 v2(0, 1, 5); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayMiss(0, 20, 1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, glm::normalize(rayMiss), intersectionPoint, distance); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, noIntersectRayPrallelToTriangle) + { + const glm::vec3 rayOrig(0, 0, 0); + const glm::vec3 v0(0, 0, 5); + const glm::vec3 v1(1, 0, 5); + const glm::vec3 v2(0, 1, 5); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayParallel(0, 1, 0); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayParallel, intersectionPoint, distance); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay1) + { + const glm::vec3 rayOrig(0, 0, 1); + const glm::vec3 v0(0, 0, 0); + const glm::vec3 v1(1, 0, 0); + const glm::vec3 v2(0, 1, 0); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayDir(0, 0, 1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay2) + { + const glm::vec3 rayOrig(0, 0, 1); + const glm::vec3 v0(0, 0, 0); + const glm::vec3 v1(1, 0, 0); + const glm::vec3 v2(0, 1, 0); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayDir(0, 0, -1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(1.f, distance); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.z); + } + + TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay3) + { + const glm::vec3 rayOrig(0, 0, -1); + const glm::vec3 v0(0, 0, 0); + const glm::vec3 v1(1, 0, 0); + const glm::vec3 v2(0, 1, 0); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayDir(0, 0, 1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(1.f, distance); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.z); + } + + TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay4) + { + const glm::vec3 rayOrig(0, 0, -1); + const glm::vec3 v0(0, 0, 0); + const glm::vec3 v1(1, 0, 0); + const glm::vec3 v2(0, 1, 0); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayDir(0, 0, -1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, noIntersectTriangleBehindRay5) + { + const glm::vec3 rayOrig(1, 1, 3); + const glm::vec3 v0(1, 1, 1); + const glm::vec3 v1(2, 1, 1); + const glm::vec3 v2(1, 2, 1); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + const glm::vec3 rayDir(0, 0, 1); + glm::vec3 intersectionPoint; + float distance = NAN; + bool intersectionHappened = IntersectionUtils::IntersectRayVsTriangle(triangle, rayOrig, rayDir, intersectionPoint, distance); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, canCalculateNormalOfPlane) + { + const glm::vec3 v0(0, 0, 5); + const glm::vec3 v1(1, 0, 5); + const glm::vec3 v2(0, 1, 5); + const IntersectionUtils::Triangle triangle{ v0, v1, v2 }; + glm::vec3 normal = IntersectionUtils::CalculatePlaneNormal(triangle); + glm::vec3 expectedNormal(0.0f, 0.0f, 1.0f); + + for (uint32_t i = 0; i < 3; ++i) + { + EXPECT_FLOAT_EQ(normal[i], expectedNormal[i]); + } + } + + TEST(IntersectionUtilsTest, canPickSingleTriangle) + { + const std::vector triangleData{ -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f }; + + const auto modelMatrix = glm::identity(); + const auto cameraTransformationMatrix = glm::translate(glm::vec3{ 0.0f, 0.0f, 1.0f }); + const auto viewMatrix = glm::inverse(cameraTransformationMatrix); + const auto projectionMatrix = glm::identity(); + + const glm::vec2 ndsPickCoords(0.0f, 0.0f); + + glm::vec3 intersectionPoint; + bool intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangleData.data(), triangleData.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(intersectionPoint.x, 0.f); + EXPECT_FLOAT_EQ(intersectionPoint.y, 0.f); + EXPECT_FLOAT_EQ(intersectionPoint.z, 0.f); + + const auto scalingModelMatrix = glm::scale(glm::vec3{ 1.0f, 2.0f, 3.0f }); + + intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangleData.data(), triangleData.size(), scalingModelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(intersectionPoint.x, 0.f); + EXPECT_FLOAT_EQ(intersectionPoint.y, 0.f); + EXPECT_FLOAT_EQ(intersectionPoint.z, 0.f); + } + + TEST(IntersectionUtilsTest, noPickSingleTriangleTranslatedAwayFromPick) + { + const std::vector triangle1Data{ -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f }; + + const auto modelMatrix = glm::translate(glm::vec3{ 0.0f, 1.0f, 0.0f }); + const auto cameraTransformationMatrix = glm::translate(glm::vec3{ 0.0f, 0.0f, 1.0f }); + const auto viewMatrix = glm::inverse(cameraTransformationMatrix); + const auto projectionMatrix = glm::identity(); + const glm::vec2 ndsPickCoords(0.0f, 0.0f); + + glm::vec3 intersectionPoint; + bool intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangle1Data.data(), triangle1Data.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + EXPECT_FALSE(intersectionHappened); + + const std::vector triangle2Data{-4.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 7.0f, 0.0f}; + + intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, triangle2Data.data(), triangle2Data.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, canIntersectMultipleTriangleGeometry) + { + const std::vector triangle1Data{ 0.0f, -1.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f }; + const std::vector triangle2Data{ 0.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f }; + //quad including origin + std::vector quad; + quad.insert(quad.end(), triangle1Data.begin(), triangle1Data.end()); + quad.insert(quad.end(), triangle2Data.begin(), triangle2Data.end()); + + const auto modelMatrix = glm::identity(); + const auto cameraTransformationMatrix = glm::translate(glm::vec3{0.0f, 0.0f, 1.0f}); + const auto viewMatrix = glm::inverse(cameraTransformationMatrix); + const auto projectionMatrix = glm::identity(); + const glm::vec2 ndsPickCoords(0.0f, 0.0f); + + glm::vec3 intersectionPoint; + bool intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, quad.data(), quad.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + EXPECT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(intersectionPoint.x, 0.f); + EXPECT_FLOAT_EQ(intersectionPoint.y, 0.f); + EXPECT_FLOAT_EQ(intersectionPoint.z, 0.f); + } + + TEST(IntersectionUtilsTest, noIntersectPickNotPointingToMultipleTriangles) + { + const std::vector triangle1Data{ -2.0f, 0.0f, 0.0f, + -2.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f }; + const std::vector triangle2Data{ -1.0f, 0.0f, 0.0f, + -2.0f, 0.0f, 0.0f, + -1.0f, 1.0f, 0.0f }; + // quad not including origin + std::vector quad; + quad.insert(quad.end(), triangle1Data.begin(), triangle1Data.end()); + quad.insert(quad.end(), triangle2Data.begin(), triangle2Data.end()); + + const auto modelMatrix = glm::identity(); + const auto cameraTransformationMatrix = glm::translate(glm::vec3{0.0f, 0.0f, 1.0f}); + const auto viewMatrix = glm::inverse(cameraTransformationMatrix); + const auto projectionMatrix = glm::identity(); + const glm::vec2 ndsPickCoords(0.0f, 0.0f); + + glm::vec3 intersectionPoint; + bool intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, quad.data(), quad.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + EXPECT_FALSE(intersectionHappened); + } + + TEST(IntersectionUtilsTest, findsPickedObjectInSceneWhenIntersected) + { + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes(rendererEventCollector); + TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); + SceneAllocateHelper sceneAllocator(scene); + float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; + const glm::ivec2 dispResolution = { 1280, 480 }; + + //Prepare PickableCamera + const CameraHandle cameraHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { -4.f, 0.f, 11.f }, { 0.f, -40.f, 0.f }, { 1.f, 1.f, 1.f }); + + //Prepare GeometryBuffer + DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); + + //Prepare PickableObject + PickableObjectId pickableId(341u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId, { 0.1f, 1.0f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 10.0f, 10.0f, 10.0f }); + + //Testing scene for intersection + const glm::vec2 coordsInViewportSpaceHit1 = {0.310937f, 0.354166f}; + const glm::vec2 coordsInViewportSpaceMiss1_1 = {0.309374f, 0.395833f}; + const glm::vec2 coordsInViewportSpaceMiss1_2 = {0.312500f, 0.312500f}; + + const glm::vec2 coordsInViewportSpaceHit2 = { -0.603125f, 0.945833f }; + const glm::vec2 coordsInViewportSpaceMiss2 = { -0.603125f, 0.987500f }; + + const glm::vec2 coordsInViewportSpaceHit3 = { -0.970313f, 0.533333f }; + const glm::vec2 coordsInViewportSpaceMiss3 = { -0.968750f, 0.495833f }; + + //Test first area of PickableObject + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHit1, dispResolution, { pickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1_1, dispResolution, {}); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1_2, dispResolution, {}); + + //Test second area of PickableObject + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHit2, dispResolution, { pickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss2, dispResolution, {}); + + //Test third area of PickableObject + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHit3, dispResolution, { pickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss3, dispResolution, {}); + } + + TEST(IntersectionUtilsTest, findsMultiplePickedObjectsNotOverlappingInSceneWhenIntersected) + { + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes(rendererEventCollector); + TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); + SceneAllocateHelper sceneAllocator(scene); + float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; + const glm::ivec2 dispResolution = { 1280, 480 }; + + //Prepare PickableCamera + const CameraHandle cameraHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { -4.f, 0.f, 11.f }, { 0.f, -40.f, 0.f }, { 1.f, 1.f, 1.f }); + + //Prepare GeometryBuffer + DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); + + //Prepare PickableObject1 + PickableObjectId pickableId(341u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId, { 0.1f, 1.0f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); + + //Prepare PickableObject2 + PickableObjectId pickableId2(235u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId2, { 6.0f, 0.9f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); + + //Testing scene for intersection + const glm::vec2 coordsInViewportSpaceHitPickable1 = { -0.382812f, 0.441667f }; + const glm::vec2 coordsInViewportSpaceHitPickable2 = { -0.379687f, 0.395833f }; + const glm::vec2 coordsInViewportSpaceMiss1 = { -0.382812f, 0.466667f }; + const glm::vec2 coordsInViewportSpaceMiss2 = { -0.378125f, 0.425000f }; + const glm::vec2 coordsInViewportSpaceMiss3 = { -0.379687f, 0.383333f }; + + //Test PickableObjects + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable1, dispResolution, { pickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable2, dispResolution, { pickableId2 }); + + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1, dispResolution, {}); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss2, dispResolution, {}); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss3, dispResolution, {}); + } + + TEST(IntersectionUtilsTest, findsMultiplePickedObjectsOverlappingInSceneWhenIntersected) + { + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes(rendererEventCollector); + TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); + SceneAllocateHelper sceneAllocator(scene); + float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; + const glm::ivec2 dispResolution = { 1280, 480 }; + + //Prepare PickableCamera + const CameraHandle cameraHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { -4.f, 0.f, 11.f }, { 0.f, -40.f, 0.f }, { 1.f, 1.f, 1.f }); + + //Prepare GeometryBuffer + DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); + + //Prepare PickableObject1 + PickableObjectId pickableId(341u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId, { 0.1f, 1.0f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); + + //Prepare PickableObject2 + PickableObjectId pickableId2(235u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraHandle, pickableId2, { 4.0f, 0.9f, -1.0f }, { -70.0f, 0.0f, 0.0f }, { 3.0f, 3.0f, 3.0f }); + + //Testing scene for intersection + const glm::vec2 coordsInViewportSpaceHitPickable1 = { -0.585938f, 0.575000f }; + const glm::vec2 coordsInViewportSpaceHitPickable2 = { -0.590625f, 0.458333f }; + const glm::vec2 coordsInViewportSpaceHitPickable1and2 = { -0.582812f, 0.512500f }; + const glm::vec2 coordsInViewportSpaceMiss1 = { -0.585938f, 0.595833f }; + const glm::vec2 coordsInViewportSpaceMiss2 = { -0.607812f, 0.408333f }; + + //Test PickableObjects + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable1, dispResolution, { pickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable2, dispResolution, { pickableId2 }); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceHitPickable1and2, dispResolution, { pickableId, pickableId2 }); + + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss1, dispResolution, {}); + checkSceneForIntersectedPickableObjects(scene, coordsInViewportSpaceMiss2, dispResolution, {}); + } + + TEST(IntersectionUtilsTest, picksIntersectionPointAtNearestTriangleInPickable) + { + const std::vector trianglesVertices{ + -1.0f, 0.0f, -10.0f, + 1.0f, 0.0f, -10.0f, + 0.0f, 1.0f, -10.0f, + + -1.0f, 0.0f, -12.0f, + 1.0f, 0.0f, -12.0f, + 0.0f, 1.0f, -12.0f, + + 0.0f, -1.0f, -9.0f, + 1.0f, 0.0f, -9.0f, + 0.0f, 1.0f, -9.0f, + + 0.0f, -1.0f, -3.0f, + 1.0f, 0.0f, -3.0f, + 0.0f, 1.0f, -3.0f, + + 0.0f, -1.0f, -15.0f, + 1.0f, 0.0f, -15.0f, + 0.0f, 1.0f, -15.0f + }; + + const auto modelMatrix = glm::identity(); + const auto cameraTransformationMatrix = glm::translate(glm::vec3{ 0.0f, 0.0f, 1.0f }); + const auto viewMatrix = glm::inverse(cameraTransformationMatrix); + const auto projectionMatrix(CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(30.f, 1.f, 0.1f, 100.f))); + const glm::vec2 ndsPickCoords(0.0f, 0.0f); + + glm::vec3 intersectionPoint; + const bool intersectionHappened = + IntersectionUtils::TestGeometryPicked(ndsPickCoords, trianglesVertices.data(), trianglesVertices.size(), modelMatrix, viewMatrix, projectionMatrix, intersectionPoint); + ASSERT_TRUE(intersectionHappened); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.x); + EXPECT_FLOAT_EQ(0.f, intersectionPoint.y); + EXPECT_FLOAT_EQ(-3.f, intersectionPoint.z); + } + + TEST(IntersectionUtilsTest, canSortPickedPickablesWithDistanceWhenPickablesUseDifferentCameras) + { + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes(rendererEventCollector); + TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); + SceneAllocateHelper sceneAllocator(scene); + float vertexPositionsTriangle[] = { -1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f }; + const glm::ivec2 dispResolution = { 1280, 480 }; + + //Prepare PickableCamera + const CameraHandle cameraFrontPickableHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { 0.f, 0.f, 11.f }, { 0.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }); + const CameraHandle cameraBackPickableHandle = preparePickableCamera(scene, sceneAllocator, { 0, 0 }, dispResolution, { 15.f, 0.f, 25.f }, { 0.f, 0.f, 0.f }, { 1.f, 5.f, .01f }); + + //Prepare GeometryBuffer + const DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); + + //Prepare pibckable that appears in the front + const PickableObjectId frontPickableId(341u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraFrontPickableHandle, frontPickableId, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 1.0f, 1.0f, 1.0f }); + + //Prepare pibckable that appears in the back (even though the distance between pickable and camera is 1.0, the camera is scaled big enough to make this pickable partially occluded by the former pickable) + const PickableObjectId backPickableId(235u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, cameraBackPickableHandle, backPickableId, { 12.5f, 0.f, 24.0f }, { 0.0f, 0.0f, 0.0f }, { 10.0f, 10.0f, 1.0f }); + + //Testing scene for intersection + const glm::vec2 coordsInNDSHitFrontPickable = { -0.0625f, 0.16666666666f }; + const glm::vec2 coordsInNDSHitBackPickable = { -0.21875f, 0.02083333333f }; + const glm::vec2 coordsInNDSHitBothPickables = { -0.1875f, 0.02083333333f }; + + //Test PickableObjects + checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitFrontPickable, dispResolution, { frontPickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBackPickable, dispResolution, { backPickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBothPickables, dispResolution, {frontPickableId, backPickableId}); + } + + TEST(IntersectionUtilsTest, handlesPickableViewportCorrectly) + { + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes(rendererEventCollector); + TransformationLinkCachedScene scene(rendererScenes.getSceneLinksManager(), {}); + SceneAllocateHelper sceneAllocator(scene); + float vertexPositionsTriangle[] = { -1.f, -1.f, 0.f, 1.f, -1.f, 0.f, 0.f, 1.f, 0.f }; + const glm::ivec2 dispResolution = { 1280, 480 }; + + //Prepare two cameras with overlapping viewports + const glm::ivec2 viewportSize = { 1280 * 2 / 3, 480 * 2 / 3 }; + const glm::ivec2 viewportOffset1 = { 0, 0 }; + const glm::ivec2 viewportOffset2 = { 1280 / 3, 480 / 3 }; + const CameraHandle bottomLeftCamera = preparePickableCamera(scene, sceneAllocator, viewportOffset1, viewportSize, { 0.f, 0.f, 1.f }, { 0.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }); + const CameraHandle topRightCamera = preparePickableCamera(scene, sceneAllocator, viewportOffset2, viewportSize, { 0.f, 0.f, 1.01f }, { 0.f, 0.f, 0.f }, { 1.f, 1.f, 1.f }); + + //Prepare GeometryBuffer + const DataBufferHandle geometryBuffer = prepareGeometryBuffer(scene, sceneAllocator, vertexPositionsTriangle, sizeof(vertexPositionsTriangle)); + + const PickableObjectId bottomLeftPickableId(341u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, bottomLeftCamera, bottomLeftPickableId, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 1.0f, 1.0f, 1.0f }); + + const PickableObjectId topRightPickableId(235u); + preparePickableObject(scene, sceneAllocator, geometryBuffer, topRightCamera, topRightPickableId, { 0.f, 0.f, 0.f }, { 0.f, 0.f, 0.f }, { 1.0f, 1.0f, 1.0f }); + + //Testing scene for intersection + const glm::vec2 coordsInNDSHitBottomLeftPickable = { -.5f, -.5f }; + const glm::vec2 coordsInNDSHitTopRightPickable = { .5f, .5f }; + const glm::vec2 coordsInNDSHitBothPickables = { 0.f, 0.f }; + const glm::vec2 coordsInNDSMissPickablesInBottomRight = { .5f, -.5f }; + const glm::vec2 coordsInNDSMissPickablesInTopLeft = { -.5f, .5f }; + + //Test PickableObjects + checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBottomLeftPickable, dispResolution, { bottomLeftPickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitTopRightPickable, dispResolution, { topRightPickableId }); + checkSceneForIntersectedPickableObjects(scene, coordsInNDSHitBothPickables, dispResolution, { bottomLeftPickableId, topRightPickableId }); + + checkSceneForIntersectedPickableObjects(scene, coordsInNDSMissPickablesInTopLeft, dispResolution, {}); + checkSceneForIntersectedPickableObjects(scene, coordsInNDSMissPickablesInBottomRight, dispResolution, {}); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/LinkManagerBaseTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/LinkManagerBaseTest.cpp new file mode 100644 index 000000000..118c4c880 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/LinkManagerBaseTest.cpp @@ -0,0 +1,181 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/TransformationLinkManager.h" +#include "internal/RendererLib/DataReferenceLinkManager.h" +#include "internal/RendererLib/TransformationLinkCachedScene.h" +#include "internal/RendererLib/TextureLinkManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "SceneLinksTestUtils.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +using namespace testing; + +namespace ramses::internal +{ + template + class ALinkManager : public ::testing::Test + { + public: + ALinkManager() + : rendererScenes(rendererEventCollector) + , linkManager(rendererScenes) + , providerSceneId(3u) + , consumerSceneId(4u) + , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) + , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) + , providerSceneAllocator(providerScene) + , consumerSceneAllocator(consumerScene) + , providerSlotHandle(55u) + , consumerSlotHandle(66u) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + createDataSlot(providerSceneAllocator, providerSlotHandle, DataSlotId(33u), true); + createDataSlot(consumerSceneAllocator, consumerSlotHandle, DataSlotId(44u), false); + } + + protected: + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + T linkManager; + + const SceneId providerSceneId; + const SceneId consumerSceneId; + IScene& providerScene; + IScene& consumerScene; + SceneAllocateHelper providerSceneAllocator; + SceneAllocateHelper consumerSceneAllocator; + const DataSlotHandle providerSlotHandle; + const DataSlotHandle consumerSlotHandle; + }; + + using ManagerTypes = ::testing::Types < + LinkManagerBase, + TransformationLinkManager, + DataReferenceLinkManager, + TextureLinkManager + >; + + TYPED_TEST_SUITE(ALinkManager, ManagerTypes); + + TYPED_TEST(ALinkManager, reportsNoDependenciesInitially) + { + EXPECT_TRUE(this->linkManager.getDependencyChecker().isEmpty()); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); + } + + TYPED_TEST(ALinkManager, canCreateLinkFromConsumerToProvider) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + const SceneLink& link = this->linkManager.getSceneLinks().getLinkedProvider(this->consumerSceneId, this->consumerSlotHandle); + EXPECT_EQ(this->providerSceneId, link.providerSceneId); + EXPECT_EQ(this->consumerSceneId, link.consumerSceneId); + EXPECT_EQ(this->providerSlotHandle, link.providerSlot); + EXPECT_EQ(this->consumerSlotHandle, link.consumerSlot); + + EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->providerSceneId)); + } + + TYPED_TEST(ALinkManager, reportsNoLinksForConsumerSceneIfSceneLinksRemoved) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + this->linkManager.removeSceneLinks(this->consumerSceneId); + EXPECT_TRUE(this->linkManager.getDependencyChecker().isEmpty()); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); + } + + TYPED_TEST(ALinkManager, reportsNoLinksForConsumerSceneIfProviderSceneDestroyed) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + this->linkManager.removeSceneLinks(this->providerSceneId); + EXPECT_TRUE(this->linkManager.getDependencyChecker().isEmpty()); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); + } + + TYPED_TEST(ALinkManager, reportsNoLinksForSceneWithRemovedLink) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_TRUE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); + EXPECT_FALSE(this->linkManager.getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); + } + + TYPED_TEST(ALinkManager, canLinkUnlinkAndLinkAgain) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_TRUE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + + const SceneLink& link = this->linkManager.getSceneLinks().getLinkedProvider(this->consumerSceneId, this->consumerSlotHandle); + EXPECT_EQ(this->providerSceneId, link.providerSceneId); + EXPECT_EQ(this->consumerSceneId, link.consumerSceneId); + EXPECT_EQ(this->providerSlotHandle, link.providerSlot); + EXPECT_EQ(this->consumerSlotHandle, link.consumerSlot); + + EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->providerSceneId)); + } + + TYPED_TEST(ALinkManager, createsTwoLinksSameProviderDifferentConsumers) + { + const DataSlotHandle consumerSlot2(43u); + createDataSlot(this->consumerSceneAllocator, consumerSlot2, DataSlotId(88u), false); + + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, consumerSlot2)); + + const SceneLink& link = this->linkManager.getSceneLinks().getLinkedProvider(this->consumerSceneId, consumerSlot2); + EXPECT_EQ(this->providerSceneId, link.providerSceneId); + EXPECT_EQ(this->consumerSceneId, link.consumerSceneId); + EXPECT_EQ(this->providerSlotHandle, link.providerSlot); + EXPECT_EQ(consumerSlot2, link.consumerSlot); + + EXPECT_TRUE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + EXPECT_FALSE(this->linkManager.getDependencyChecker().hasDependencyAsConsumer(this->providerSceneId)); + } + + TYPED_TEST(ALinkManager, failsToCreateLinksCausingCyclicDependency) + { + const DataSlotHandle slotHandle(43u); + createDataSlot(this->consumerSceneAllocator, slotHandle, DataSlotId(88u), true); + + const DataSlotHandle slotHandle2(43u); + createDataSlot(this->providerSceneAllocator, slotHandle2, DataSlotId(89u), false); + + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_FALSE(this->linkManager.createDataLink(this->consumerSceneId, slotHandle, this->providerSceneId, slotHandle2)); + } + + TYPED_TEST(ALinkManager, failsToCreateLinkIfConsumerAlreadyLinked) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_FALSE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + } + + TYPED_TEST(ALinkManager, failsToRemoveLinkIfConsumerNotLinked) + { + EXPECT_FALSE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); + } + + TYPED_TEST(ALinkManager, failsToRemoveLinkIfConsumerAlreadyUnlinked) + { + EXPECT_TRUE(this->linkManager.createDataLink(this->providerSceneId, this->providerSlotHandle, this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_TRUE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); + EXPECT_FALSE(this->linkManager.removeDataLink(this->consumerSceneId, this->consumerSlotHandle)); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/PendingSceneResourcesUtilsTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/PendingSceneResourcesUtilsTest.cpp new file mode 100644 index 000000000..55740f013 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/PendingSceneResourcesUtilsTest.cpp @@ -0,0 +1,365 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/PendingSceneResourcesUtils.h" +#include "RendererResourceManagerMock.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/SceneGraph/Scene/Scene.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" +#include "SceneAllocateHelper.h" + +namespace ramses::internal { + using namespace testing; + + class APendingSceneResourcesUtils : public ::testing::Test + { + public: + APendingSceneResourcesUtils() + : rendererScenes(rendererEventCollector) + , sceneLinksManager(rendererScenes, rendererEventCollector) + , scene(sceneLinksManager, SceneInfo(sceneID)) + , allocateHelper(scene) + { + allocateHelper.allocateRenderTarget(renderTargetHandle); + allocateHelper.allocateRenderBuffer({ 16u, 16u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }, renderBufferHandle); + allocateHelper.allocateBlitPass(RenderBufferHandle(81u), RenderBufferHandle(82u), blitPassHandle); + allocateHelper.allocateDataBuffer(EDataBufferType::IndexBuffer, EDataType::UInt32, 10u, dataBufferHandle); + allocateHelper.allocateTextureBuffer(EPixelStorageFormat::R8, { { 32, 32 },{ 16, 16 },{ 8, 8 } }, textureBufferHandle); + } + + protected: + const SceneId sceneID = SceneId(13u); + const RenderTargetHandle renderTargetHandle = RenderTargetHandle(5u); + const RenderBufferHandle renderBufferHandle = RenderBufferHandle(6u); + const BlitPassHandle blitPassHandle = BlitPassHandle(8u); + const DataBufferHandle dataBufferHandle = DataBufferHandle(9u); + const TextureBufferHandle textureBufferHandle = TextureBufferHandle(10u); + + const MemoryHandle dummyHandle = MemoryHandle(123u); + const MemoryHandle dummyHandle2 = MemoryHandle(124u); + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneLinksManager sceneLinksManager; + + RendererCachedScene scene; + SceneAllocateHelper allocateHelper; + StrictMock resourceManager; + }; + + struct BasicActionSet + { + explicit BasicActionSet(ESceneResourceAction c = ESceneResourceAction_Invalid, ESceneResourceAction d = ESceneResourceAction_Invalid, ESceneResourceAction u = ESceneResourceAction_Invalid) + : create(c) + , destroy(d) + , update(u) + { + } + + ESceneResourceAction create; + ESceneResourceAction destroy; + ESceneResourceAction update; + }; + + static const BasicActionSet ActionSet_RenderTarget{ ESceneResourceAction_CreateRenderTarget, ESceneResourceAction_DestroyRenderTarget }; + static const BasicActionSet ActionSet_RenderBuffer{ ESceneResourceAction_CreateRenderBuffer, ESceneResourceAction_DestroyRenderBuffer }; + static const BasicActionSet ActionSet_BlitPass{ ESceneResourceAction_CreateBlitPass, ESceneResourceAction_DestroyBlitPass }; + static const BasicActionSet ActionSet_DataBuffer{ ESceneResourceAction_CreateDataBuffer, ESceneResourceAction_DestroyDataBuffer, ESceneResourceAction_UpdateDataBuffer }; + static const BasicActionSet ActionSet_TextureBuffer{ ESceneResourceAction_CreateTextureBuffer, ESceneResourceAction_DestroyTextureBuffer, ESceneResourceAction_UpdateTextureBuffer }; + + static const BasicActionSet TestSceneResourceActions[] = + { + ActionSet_RenderTarget, + ActionSet_RenderBuffer, + ActionSet_BlitPass, + ActionSet_DataBuffer, + ActionSet_TextureBuffer + }; + + static const BasicActionSet TestSceneResourceActions_Buffers[] = + { + ActionSet_DataBuffer, + ActionSet_TextureBuffer + }; + + TEST_F(APendingSceneResourcesUtils, appliesSceneResourceActions) + { + SceneResourceActionVector actions; + actions.push_back(SceneResourceAction(renderBufferHandle.asMemoryHandle(), ESceneResourceAction_CreateRenderBuffer)); + actions.push_back(SceneResourceAction(renderTargetHandle.asMemoryHandle(), ESceneResourceAction_CreateRenderTarget)); + actions.push_back(SceneResourceAction(blitPassHandle.asMemoryHandle(), ESceneResourceAction_CreateBlitPass)); + actions.push_back(SceneResourceAction(dataBufferHandle.asMemoryHandle(), ESceneResourceAction_CreateDataBuffer)); + actions.push_back(SceneResourceAction(dataBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateDataBuffer)); + actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_CreateTextureBuffer)); + actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + const std::array data{}; + scene.updateTextureBuffer(textureBufferHandle, 0, 0, 0, 1, 1, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 1, 0, 0, 1, 1, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 2, 0, 0, 1, 1, data.data()); + + InSequence seq; + EXPECT_CALL(resourceManager, uploadRenderTargetBuffer(renderBufferHandle, sceneID, _)); + EXPECT_CALL(resourceManager, uploadRenderTarget(renderTargetHandle, _, sceneID)); + EXPECT_CALL(resourceManager, uploadBlitPassRenderTargets(blitPassHandle, _, _, sceneID)); + EXPECT_CALL(resourceManager, uploadDataBuffer(dataBufferHandle, _, _, _, sceneID)); + EXPECT_CALL(resourceManager, updateDataBuffer(dataBufferHandle, _, _, sceneID)); + EXPECT_CALL(resourceManager, uploadTextureBuffer(textureBufferHandle, _, _, _, _, sceneID)); + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, _, _, _, _, sceneID)).Times(3u); // 3 mips + PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); + } + + TEST_F(APendingSceneResourcesUtils, appliesSceneResourceActions_Unloads) + { + SceneResourceActionVector actions; + actions.push_back(SceneResourceAction(renderTargetHandle.asMemoryHandle(), ESceneResourceAction_DestroyRenderTarget)); + actions.push_back(SceneResourceAction(renderBufferHandle.asMemoryHandle(), ESceneResourceAction_DestroyRenderBuffer)); + actions.push_back(SceneResourceAction(blitPassHandle.asMemoryHandle(), ESceneResourceAction_DestroyBlitPass)); + actions.push_back(SceneResourceAction(dataBufferHandle.asMemoryHandle(), ESceneResourceAction_DestroyDataBuffer)); + actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_DestroyTextureBuffer)); + + InSequence seq; + EXPECT_CALL(resourceManager, unloadRenderTarget(renderTargetHandle, sceneID)); + EXPECT_CALL(resourceManager, unloadRenderTargetBuffer(renderBufferHandle, sceneID)); + EXPECT_CALL(resourceManager, unloadBlitPassRenderTargets(blitPassHandle, sceneID)); + EXPECT_CALL(resourceManager, unloadDataBuffer(dataBufferHandle, sceneID)); + EXPECT_CALL(resourceManager, unloadTextureBuffer(textureBufferHandle, sceneID)); + PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); + } + + TEST_F(APendingSceneResourcesUtils, getsSceneResourcesFromSceneAndApliesThem) + { + SceneResourceActionVector actions; + size_t resSize = 999u; + ResourceUtils::GetAllSceneResourcesFromScene(actions, scene, resSize); + EXPECT_FALSE(actions.empty()); + EXPECT_EQ(1344u, resSize); + + static const std::array data{}; + scene.updateTextureBuffer(textureBufferHandle, 0u, 0u, 0u, 32, 32, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 1u, 0u, 0u, 16, 16, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 2u, 0u, 0u, 8, 8, data.data()); + + InSequence seq; + EXPECT_CALL(resourceManager, uploadRenderTargetBuffer(renderBufferHandle, sceneID, _)); + EXPECT_CALL(resourceManager, uploadRenderTarget(renderTargetHandle, _, sceneID)); + EXPECT_CALL(resourceManager, uploadBlitPassRenderTargets(blitPassHandle, RenderBufferHandle(81), RenderBufferHandle(82), sceneID)); + EXPECT_CALL(resourceManager, uploadDataBuffer(dataBufferHandle, _, _, _, sceneID)); + EXPECT_CALL(resourceManager, updateDataBuffer(dataBufferHandle, _, _, sceneID)); + + EXPECT_CALL(resourceManager, uploadTextureBuffer(textureBufferHandle, _, _, _, _, sceneID)); + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 0u, Quad{0u, 0u, 32, 32}, 32, _, sceneID)); + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 1u, Quad{0u, 0u, 16, 16}, 16, _, sceneID)); + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 2u, Quad{0u, 0u, 8, 8}, 8, _, sceneID)); + PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); + } + + TEST_F(APendingSceneResourcesUtils, cancelsOutCreateAndDeleteDuringConsolidation) + { + for (const auto& crateDestroyPair : TestSceneResourceActions) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // test create/destroy pair across old/new and also another pair within new only + actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + EXPECT_TRUE(actions.empty()); + } + } + + TEST_F(APendingSceneResourcesUtils, doesNotCancelOutDestroyAndCreateDuringConsolidation) + { + for (const auto& crateDestroyPair : TestSceneResourceActions) + { + SceneResourceActionVector actionsNew; + SceneResourceActionVector actions; + + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(2u, actions.size()); + EXPECT_EQ(crateDestroyPair.destroy, actions.front().action); + EXPECT_EQ(crateDestroyPair.create, actions.back().action); + } + } + + TEST_F(APendingSceneResourcesUtils, doesNotCancelOutCreateAndDeleteOfDifferentHandlesDuringConsolidation) + { + for (const auto& crateDestroyPair : TestSceneResourceActions) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // test create/destroy pair across old/new and also another pair within new only + actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle2, crateDestroyPair.destroy)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(2u, actions.size()); + EXPECT_EQ(SceneResourceAction(dummyHandle, crateDestroyPair.create), actions.front()); + EXPECT_EQ(SceneResourceAction(dummyHandle2, crateDestroyPair.destroy), actions.back()); + } + } + + TEST_F(APendingSceneResourcesUtils, keepsAdditionalCreateActionAfterCanceledCreateAndDeleteDuringConsolidation) + { + for (const auto& crateDestroyPair : TestSceneResourceActions) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // test create/destroy pair across old/new and also another pair within new only + actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + // additional create + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(1u, actions.size()); + EXPECT_EQ(crateDestroyPair.create, actions.front().action); + } + } + + TEST_F(APendingSceneResourcesUtils, keepsAdditionalDestroyActionAfterCanceledCreateAndDeleteDuringConsolidation) + { + for (const auto& crateDestroyPair : TestSceneResourceActions) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // test create/destroy pair across old/new and also another pair within new only + actions.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.create)); + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + // additional create + actionsNew.push_back(SceneResourceAction(dummyHandle, crateDestroyPair.destroy)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(1u, actions.size()); + EXPECT_EQ(crateDestroyPair.destroy, actions.front().action); + } + } + + TEST_F(APendingSceneResourcesUtils, squashesBufferUpdateActionsToSingleActionDuringConsolidation) + { + for (const auto& bufferAction : TestSceneResourceActions_Buffers) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // updates both in old and new actions + actions.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(1u, actions.size()); + EXPECT_EQ(bufferAction.update, actions.front().action); + } + } + + TEST_F(APendingSceneResourcesUtils, removesAnyDataBufferUpdateActionsBeforeDestroyDuringConsolidation) + { + for (const auto& bufferAction : TestSceneResourceActions_Buffers) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // updates both in old and new actions + actions.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + + // destroy buffer + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.destroy)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(1u, actions.size()); + EXPECT_EQ(bufferAction.destroy, actions.front().action); + } + } + + TEST_F(APendingSceneResourcesUtils, keepsDataBufferUpdateActionForNewlyCreatedAfterRemovedPreviousUpdatesDueToDestroy) + { + for (const auto& bufferAction : TestSceneResourceActions_Buffers) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + + // create buffer + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.create)); + // updates both in old and new actions + actions.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + // destroy buffer + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.destroy)); + // all previous ones should be canceled + + // create new buffer + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.create)); + // update new buffer + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + actionsNew.push_back(SceneResourceAction(dummyHandle, bufferAction.update)); + + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + ASSERT_EQ(2u, actions.size()); + EXPECT_EQ(bufferAction.create, actions.front().action); + EXPECT_EQ(bufferAction.update, actions.back().action); + } + } + + TEST_F(APendingSceneResourcesUtils, textureBufferPartialUpdate) + { + SceneResourceActionVector actions; + actions.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + + const std::array data{}; + scene.updateTextureBuffer(textureBufferHandle, 0, 1, 2, 3, 4, data.data()); + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 0u, Quad{1, 2, 3, 4}, 32, _, sceneID)); + PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); + } + + TEST_F(APendingSceneResourcesUtils, textureBufferCombinePartialUpdates) + { + SceneResourceActionVector actions; + SceneResourceActionVector actionsNew; + actionsNew.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + actionsNew.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + actionsNew.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + actionsNew.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + actionsNew.push_back(SceneResourceAction(textureBufferHandle.asMemoryHandle(), ESceneResourceAction_UpdateTextureBuffer)); + + const std::array data{}; + scene.updateTextureBuffer(textureBufferHandle, 0, 2, 2, 5, 4, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 1, 1, 1, 2, 2, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 0, 1, 1, 2, 2, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 0, 11, 12, 2, 2, data.data()); + scene.updateTextureBuffer(textureBufferHandle, 1, 0, 0, 3, 4, data.data()); + PendingSceneResourcesUtils::ConsolidateSceneResourceActions(actionsNew, actions); + + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 0u, Quad{1, 1, 12, 13}, 32, _, sceneID)); + EXPECT_CALL(resourceManager, updateTextureBuffer(textureBufferHandle, 1u, Quad{0, 0, 3, 4}, 16, _, sceneID)); + EXPECT_EQ(actions.size(), 1u); + PendingSceneResourcesUtils::ApplySceneResourceActions(actions, scene, resourceManager); + } +} diff --git a/renderer/RendererLib/RendererLib/test/PlatformTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/PlatformTest.cpp similarity index 84% rename from renderer/RendererLib/RendererLib/test/PlatformTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/PlatformTest.cpp index dfd5fda11..02b2e2b99 100644 --- a/renderer/RendererLib/RendererLib/test/PlatformTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/PlatformTest.cpp @@ -6,22 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" #include "Platform_BaseMock.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererAPI/IRenderBackend.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" #include "WindowEventHandlerMock.h" #include "RenderBackendMock.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class APlatformTest : public Test { public: - static void SetUpTestCase() + static void SetUpTestSuite() { // caller is expected to have a display prefix for logs ThreadLocalLog::SetPrefix(1); @@ -48,7 +47,7 @@ namespace ramses_internal return platform.createRenderBackend(displayConfig, windowEventHandlerMock); } - IResourceUploadRenderBackend* createResourceUploadRenderBackend(Platform_BaseMock& platform) + static IResourceUploadRenderBackend* CreateResourceUploadRenderBackend(Platform_BaseMock& platform) { InSequence s; EXPECT_CALL(platform, createContextUploading()); @@ -58,7 +57,7 @@ namespace ramses_internal return platform.createResourceUploadRenderBackend(); } - void verifyAndClearExpectationsOnRenderBackendMockObjects(Platform_BaseMock& platform) + static void VerifyAndClearExpectationsOnRenderBackendMockObjects(Platform_BaseMock& platform) { Mock::VerifyAndClearExpectations(platform.window); Mock::VerifyAndClearExpectations(platform.context); @@ -66,23 +65,23 @@ namespace ramses_internal Mock::VerifyAndClearExpectations(platform.embeddedCompositor); } - void destroyRenderBackend(Platform_BaseMock& platform) + static void DestroyRenderBackend(Platform_BaseMock& platform) { InSequence s; EXPECT_CALL(*platform.context, disable()); platform.destroyRenderBackend(); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } - void destroyResourceUploadRenderBackend(Platform_BaseMock& platform) + static void DestroyResourceUploadRenderBackend(Platform_BaseMock& platform) { InSequence s; EXPECT_CALL(*platform.contextUploading, disable()); platform.destroyResourceUploadRenderBackend(); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } RendererConfig rendererConfig; @@ -96,7 +95,7 @@ namespace ramses_internal IRenderBackend* renderBackend = createRenderBackend(platform); ASSERT_TRUE(nullptr != renderBackend); - destroyRenderBackend(platform); + DestroyRenderBackend(platform); } TEST_F(APlatformTest, CanCreateAndDestroyResourceUploadRenderBackend) @@ -105,11 +104,11 @@ namespace ramses_internal IRenderBackend* mainRenderBackend = createRenderBackend(platform); ASSERT_TRUE(nullptr != mainRenderBackend); - IResourceUploadRenderBackend* renderBackend = createResourceUploadRenderBackend(platform); + IResourceUploadRenderBackend* renderBackend = CreateResourceUploadRenderBackend(platform); ASSERT_TRUE(nullptr != renderBackend); - destroyResourceUploadRenderBackend(platform); - destroyRenderBackend(platform); + DestroyResourceUploadRenderBackend(platform); + DestroyRenderBackend(platform); } TEST_F(APlatformTest, RenderBackendCreationFailsIfWindowFailsInitialization) @@ -123,7 +122,7 @@ namespace ramses_internal IRenderBackend* renderBackend = platform.createRenderBackend(displayConfig, windowEventHandlerMock); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } } @@ -140,7 +139,7 @@ namespace ramses_internal IRenderBackend* renderBackend = platform.createRenderBackend(displayConfig, windowEventHandlerMock); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } } @@ -161,7 +160,7 @@ namespace ramses_internal IRenderBackend* renderBackend = platform.createRenderBackend(displayConfig, windowEventHandlerMock); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } } @@ -185,7 +184,7 @@ namespace ramses_internal IRenderBackend* renderBackend = platform.createRenderBackend(displayConfig, windowEventHandlerMock); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } } @@ -211,7 +210,7 @@ namespace ramses_internal IRenderBackend* renderBackend = platform.createRenderBackend(displayConfig, windowEventHandlerMock); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } } @@ -228,10 +227,10 @@ namespace ramses_internal IResourceUploadRenderBackend* renderBackend = platform.createResourceUploadRenderBackend(); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } - destroyRenderBackend(platform); + DestroyRenderBackend(platform); } TEST_F(APlatformTest, ResourceUploadRenderBackendCreationFailsIfDeviceFailsInitialization) @@ -250,9 +249,9 @@ namespace ramses_internal IResourceUploadRenderBackend* renderBackend = platform.createResourceUploadRenderBackend(); EXPECT_EQ(nullptr, renderBackend); - verifyAndClearExpectationsOnRenderBackendMockObjects(platform); + VerifyAndClearExpectationsOnRenderBackendMockObjects(platform); } - destroyRenderBackend(platform); + DestroyRenderBackend(platform); } } diff --git a/renderer/RendererLib/RendererTestCommon/Platform_BaseMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/Platform_BaseMock.cpp similarity index 93% rename from renderer/RendererLib/RendererTestCommon/Platform_BaseMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/Platform_BaseMock.cpp index 1cc37934d..b7c443b5d 100644 --- a/renderer/RendererLib/RendererTestCommon/Platform_BaseMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/Platform_BaseMock.cpp @@ -10,18 +10,18 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { Platform_BaseMock::Platform_BaseMock(const RendererConfig& config) : Platform_Base(config) { - ON_CALL(*this, createWindow(_, _)).WillByDefault(Invoke([this](auto&, auto&) { + ON_CALL(*this, createWindow(_, _)).WillByDefault(Invoke([this](auto& /*unused*/, auto& /*unused*/) { assert(!m_window); m_window = std::move(windowOwningPtr); return true; })); - ON_CALL(*this, createContext(_)).WillByDefault(Invoke([this](auto&) { + ON_CALL(*this, createContext(_)).WillByDefault(Invoke([this](auto& /*unused*/) { assert(!m_context); m_context = std::move(contextOwningPtr); return true; @@ -45,7 +45,7 @@ namespace ramses_internal return true; })); - ON_CALL(*this, createEmbeddedCompositor(_)).WillByDefault(Invoke([this](auto&) { + ON_CALL(*this, createEmbeddedCompositor(_)).WillByDefault(Invoke([this](auto& /*unused*/) { assert(!m_embeddedCompositor); m_embeddedCompositor = std::move(embeddedCompositorOwningPtr); return true; @@ -57,7 +57,7 @@ namespace ramses_internal return m_systemCompositorController.get(); })); - ON_CALL(*this, createTextureUploadingAdapter(_)).WillByDefault(Invoke([this](auto&) { + ON_CALL(*this, createTextureUploadingAdapter(_)).WillByDefault(Invoke([this](auto& /*unused*/) { assert(!m_textureUploadingAdapter); m_textureUploadingAdapter = std::move(textureUploadingAdapterOwningPtr); })); diff --git a/renderer/RendererLib/RendererTestCommon/Platform_BaseMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/Platform_BaseMock.h similarity index 91% rename from renderer/RendererLib/RendererTestCommon/Platform_BaseMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/Platform_BaseMock.h index 598306ea3..ada9c9ff1 100644 --- a/renderer/RendererLib/RendererTestCommon/Platform_BaseMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/Platform_BaseMock.h @@ -6,22 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORM_BASEMOCK_H -#define RAMSES_PLATFORM_BASEMOCK_H +#pragma once -#include "gmock/gmock.h" -#include "renderer_common_gmock_header.h" -#include "Platform_Base/Platform_Base.h" -#include "Platform_Base/TextureUploadingAdapter_Base.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "RendererLib/RendererConfig.h" +#include "internal/RendererLib/PlatformBase/Platform_Base.h" +#include "internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h" #include "WindowMock.h" #include "ContextMock.h" #include "EmbeddedCompositorMock.h" #include "SystemCompositorControllerMock.h" #include "DeviceMock.h" +#include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class Platform_BaseMock : public Platform_Base { @@ -60,5 +56,3 @@ namespace ramses_internal std::unique_ptr textureUploadingAdapterOwningPtr{ textureUploadingAdapter }; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/test/RenderExecutorInternalStateTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorInternalStateTest.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/test/RenderExecutorInternalStateTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorInternalStateTest.cpp index 013b92e1f..4cb4e4369 100644 --- a/renderer/RendererLib/RendererLib/test/RenderExecutorInternalStateTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorInternalStateTest.cpp @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" -#include "RenderExecutorInternalState.h" +#include "internal/RendererLib/RenderExecutorInternalState.h" #include "RenderBackendMock.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" #include "TestEqualHelper.h" #include "ResourceDeviceHandleAccessorMock.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/RendererEventCollector.h" #include "SceneAllocateHelper.h" #include -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -40,7 +39,7 @@ namespace ramses_internal { public: ARenderExecutorInternalState() - : m_renderContext{ DeviceResourceHandle(0u), FakeVpWidth, FakeVpHeight, SceneRenderExecutionIterator{}, EClearFlags_All, glm::vec4{1.f}, false } + : m_renderContext{ DeviceResourceHandle(0u), FakeVpWidth, FakeVpHeight, SceneRenderExecutionIterator{}, EClearFlag::All, glm::vec4{1.f}, false } , m_executorState(m_device, m_renderContext) , m_executorStateWithTimer(m_device, m_renderContext, &m_frameTimer) , m_rendererScenes(m_rendererEventCollector) @@ -106,7 +105,7 @@ namespace ramses_internal EXPECT_EQ(FakeVpWidth, m_executorState.getRenderingContext().viewportWidth); EXPECT_EQ(FakeVpHeight, m_executorState.getRenderingContext().viewportHeight); EXPECT_EQ(SceneRenderExecutionIterator{}, m_executorState.getRenderingContext().renderFrom); - EXPECT_EQ(EClearFlags_All, m_executorState.getRenderingContext().displayBufferClearPending); + EXPECT_EQ(EClearFlag::All, m_executorState.getRenderingContext().displayBufferClearPending); EXPECT_EQ(glm::vec4{ 1.f }, m_executorState.getRenderingContext().displayBufferClearColor); } diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorTest.cpp new file mode 100644 index 000000000..229687733 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RenderExecutorTest.cpp @@ -0,0 +1,1783 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererResourceManagerMock.h" +#include "RenderBackendMock.h" +#include "internal/RendererLib/RenderExecutor.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/SceneGraph/SceneUtils/DataLayoutCreationHelper.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "SceneAllocateHelper.h" +#include "internal/PlatformAbstraction/PlatformMath.h" +#include "internal/Components/EffectUniformTime.h" +#include "MockResourceHash.h" +#include "glm/gtx/transform.hpp" +#include "glm/gtx/quaternion.hpp" + +namespace ramses::internal { + using namespace testing; + + namespace + { + const float fakeFieldOfView = 19.f; + const float fakeAspectRatio = 0.5f; + const float fakeNearPlane = 0.1f; + const float fakeFarPlane = 1500.f; + + const int32_t fakeViewportX = 15; + const int32_t fakeViewportY = 16; + const uint32_t fakeViewportWidth = 17u; + const uint32_t fakeViewportHeight = 18u; + + const uint32_t startIndex = 12u; + const uint32_t indexCount = 13; + const uint32_t startVertex = 14u; + + const ExternalBufferHandle fakeExternalBuffer{ 1240u }; + const DeviceResourceHandle fakeExternalTextureDeviceHandle{ 1050u }; + } + + using DataInstances = std::pair; + + // This Matrix comparison matcher is needed to compare the MVP etc matrices. It must allow + // a relatively high error because heavy optimizations on some platforms may lead to significant + // differences in precision (likely because values are kept in 80bit FP registers) + MATCHER_P(PermissiveMatrixEq, other, "") + { + (void)(result_listener); + for (glm::length_t i = 0; i < arg.length(); ++i) + { + for (glm::length_t k = 0; k < arg[i].length(); ++k) + { + const auto a = arg[i][k]; + const auto b = other[i][k]; + + const float fDelta = std::abs(a - b); + if (fDelta > 1e-5f) + { + float relativeError = 0.f; + if (std::abs(b) > std::abs(a)) + { + relativeError = std::abs((a - b) / b); + } + else + { + relativeError = std::abs((a - b) / a); + } + if (relativeError > 1.e-7f) + { + return false; + } + } + } + } + + return true; + } + + static int32_t GetElapsed(int32_t start, int32_t end) + { + if (start == end) + return 0; + if (start < end) + { + return end - start; + } + constexpr auto limit = std::numeric_limits::max(); + return (limit - start) + end + 1; + } + + MATCHER_P(TimeEq, expectedTime, "") + { + (void)(result_listener); + const int32_t tolerance = 10000; // tolerate stuck during test execution + return GetElapsed(expectedTime, arg) < tolerance; + } + + class FakeEffectInputs + { + public: + explicit FakeEffectInputs(bool withTimeMs) + { + uniformInputs.push_back(EffectInputInformation("dataRefField1", 1, EDataType::Float, EFixedSemantics::Invalid)); + dataRefField1 = DataFieldHandle(0); + uniformInputs.push_back(EffectInputInformation("fieldModelMatrix", 1, EDataType::Matrix44F, EFixedSemantics::ModelMatrix)); + fieldModelMatrix = DataFieldHandle(1); + uniformInputs.push_back(EffectInputInformation("fieldViewMatrix", 1, EDataType::Matrix44F, EFixedSemantics::ViewMatrix)); + fieldViewMatrix = DataFieldHandle(2); + uniformInputs.push_back(EffectInputInformation("fieldProjMatrix", 1, EDataType::Matrix44F, EFixedSemantics::ProjectionMatrix)); + fieldProjMatrix = DataFieldHandle(3); + uniformInputs.push_back(EffectInputInformation("textureField", 1, EDataType::TextureSampler2D, EFixedSemantics::Invalid)); + textureField = DataFieldHandle(4); + uniformInputs.push_back(EffectInputInformation("textureFieldMS", 1, EDataType::TextureSampler2DMS, EFixedSemantics::Invalid)); + textureFieldMS = DataFieldHandle(5); + uniformInputs.push_back(EffectInputInformation("dataRefField2", 1, EDataType::Float, EFixedSemantics::Invalid)); + dataRefField2 = DataFieldHandle(6); + uniformInputs.push_back(EffectInputInformation("dataRefFieldMatrix22f", 1, EDataType::Matrix22F, EFixedSemantics::Invalid)); + dataRefFieldMatrix22f = DataFieldHandle(7); + uniformInputs.push_back(EffectInputInformation("textureFieldExternal", 1, EDataType::TextureSamplerExternal, EFixedSemantics::Invalid)); + textureFieldExternal = DataFieldHandle(8); + + uniformInputs.push_back(EffectInputInformation("dataRefFieldBool", 1, EDataType::Bool, EFixedSemantics::Invalid)); + dataRefFieldBool = DataFieldHandle(9); + + if (withTimeMs) + { + uniformInputs.push_back(EffectInputInformation("timeMs", 1, EDataType::Int32, EFixedSemantics::TimeMs)); + dataRefTimeMs = DataFieldHandle(10); + } + + attributeInputs.push_back(EffectInputInformation("vertPosField", 1, EDataType::Vector3Buffer, EFixedSemantics::Invalid)); + vertPosField = DataFieldHandle(0); + attributeInputs.push_back(EffectInputInformation("vertTexcoordField", 1, EDataType::Vector2Buffer, EFixedSemantics::TextTextureCoordinatesAttribute)); + vertTexcoordField = DataFieldHandle(1); + } + + EffectInputInformationVector uniformInputs; + EffectInputInformationVector attributeInputs; + + DataFieldHandle vertPosField; + DataFieldHandle vertTexcoordField; + DataFieldHandle dataRefField1; + DataFieldHandle dataRefField2; + DataFieldHandle dataRefFieldMatrix22f; + DataFieldHandle dataRefFieldBool; + DataFieldHandle dataRefTimeMs; + DataFieldHandle textureField; + DataFieldHandle textureFieldMS; + DataFieldHandle textureFieldExternal; + DataFieldHandle fieldModelMatrix; + DataFieldHandle fieldViewMatrix; + DataFieldHandle fieldProjMatrix; + }; + + + class ARenderExecutorBase: public ::testing::Test + { + public: + explicit ARenderExecutorBase(bool withTimeMs) + : device(renderer.deviceMock) + , renderContext{ DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, fakeViewportWidth, fakeViewportHeight, {}, EClearFlag::All, glm::vec4{0.f}, false } + , rendererScenes(rendererEventCollector) + , scene(rendererScenes.createScene(SceneInfo())) + , sceneAllocator(scene) + , fakeEffectInputs(withTimeMs) + , indicesField(0u) + , vertPosField(1u) + , vertTexcoordField(2u) + , textureField (fakeEffectInputs.textureField ) + , textureFieldMS (fakeEffectInputs.textureFieldMS ) + , textureFieldExternal (fakeEffectInputs.textureFieldExternal ) + , fieldModelMatrix (fakeEffectInputs.fieldModelMatrix ) + , fieldViewMatrix (fakeEffectInputs.fieldViewMatrix ) + , fieldProjMatrix (fakeEffectInputs.fieldProjMatrix ) + { + InputIndexVector referencedInputs; + scene.preallocateSceneSize(SceneSizeInformation(0u, 0u, 0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)); + uniformLayout = DataLayoutCreationHelper::CreateUniformDataLayoutMatchingEffectInputs(scene, fakeEffectInputs.uniformInputs, referencedInputs, MockResourceHash::EffectHash, DataLayoutHandle(0u)); + + DataFieldInfoVector dataFields(3u); + dataFields[indicesField.asMemoryHandle()] = DataFieldInfo(EDataType::Indices, 1u, EFixedSemantics::Indices); + dataFields[vertPosField.asMemoryHandle()] = DataFieldInfo(EDataType::Vector3Buffer, 1u, EFixedSemantics::Invalid); + dataFields[vertTexcoordField.asMemoryHandle()] = DataFieldInfo(EDataType::Vector2Buffer, 1u, EFixedSemantics::TextPositionsAttribute); + geometryLayout = sceneAllocator.allocateDataLayout(dataFields, MockResourceHash::EffectHash, DataLayoutHandle(2u)); + } + + protected: + enum class EExpectedRenderStateChange + { + None, + CausedByClear, + All, + }; + + StrictMock renderer; + StrictMock& device; + NiceMock resourceManager; + + RenderingContext renderContext; + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + RendererCachedScene& scene; + SceneAllocateHelper sceneAllocator; + + FakeEffectInputs fakeEffectInputs; + TextureSamplerHandle sampler; + TextureSamplerHandle samplerMS; + TextureSamplerHandle samplerExternal; + const DataFieldHandle indicesField; + const DataFieldHandle vertPosField; + const DataFieldHandle vertTexcoordField; + const DataFieldHandle textureField; + const DataFieldHandle textureFieldMS; + const DataFieldHandle textureFieldExternal; + const DataFieldHandle fieldModelMatrix; + const DataFieldHandle fieldViewMatrix; + const DataFieldHandle fieldProjMatrix; + DataInstanceHandle dataRef1; + DataInstanceHandle dataRef2; + DataInstanceHandle dataRefBool; + DataInstanceHandle dataRefMatrix22f; + + DataLayoutHandle uniformLayout; + DataLayoutHandle geometryLayout; + + Sequence deviceSequence; + + static ProjectionParams GetDefaultProjectionParams(ECameraProjectionType cameraProjType = ECameraProjectionType::Perspective) + { + if (cameraProjType == ECameraProjectionType::Perspective) + return ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane); + return ProjectionParams::Frustum(ECameraProjectionType::Orthographic, -1.f, 1.f, -10.f, 10.f, 1.f, 10.f); + } + + RenderPassHandle createRenderPassWithCamera(const ProjectionParams& params, const Viewport& viewport = { fakeViewportX, fakeViewportY, fakeViewportWidth, fakeViewportHeight }) + { + const RenderPassHandle pass = sceneAllocator.allocateRenderPass(); + const NodeHandle cameraNode = sceneAllocator.allocateNode(); + const auto dataLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference} }, {}); + const auto dataInstance = sceneAllocator.allocateDataInstance(dataLayout); + const auto vpDataRefLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I} }, {}); + const auto vpOffsetInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); + const auto vpSizeInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); + const auto frustumPlanesLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector4F} }, {}); + const auto frustumPlanes = sceneAllocator.allocateDataInstance(frustumPlanesLayout); + const auto frustumNearFarLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2F} }, {}); + const auto frustumNearFar = sceneAllocator.allocateDataInstance(frustumNearFarLayout); + scene.setDataReference(dataInstance, Camera::ViewportOffsetField, vpOffsetInstance); + scene.setDataReference(dataInstance, Camera::ViewportSizeField, vpSizeInstance); + scene.setDataReference(dataInstance, Camera::FrustumPlanesField, frustumPlanes); + scene.setDataReference(dataInstance, Camera::FrustumNearFarPlanesField, frustumNearFar); + const CameraHandle camera = sceneAllocator.allocateCamera(params.getProjectionType(), cameraNode, dataInstance); + + scene.setDataSingleVector4f(frustumPlanes, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); + scene.setDataSingleVector2f(frustumNearFar, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); + + scene.setDataSingleVector2i(vpOffsetInstance, DataFieldHandle{ 0 }, { viewport.posX, viewport.posY }); + scene.setDataSingleVector2i(vpSizeInstance, DataFieldHandle{ 0 }, { int32_t(viewport.width), int32_t(viewport.height) }); + + sceneAllocator.allocateTransform(cameraNode); + scene.setRenderPassCamera(pass, camera); + return pass; + } + + [[nodiscard]] TransformHandle findTransformForNode(NodeHandle node) const + { + for (TransformHandle transform(0u); transform < scene.getTransformCount(); ++transform) + { + if (scene.isTransformAllocated(transform) && scene.getTransformNode(transform) == node) + { + return transform; + } + } + + assert(false); + return TransformHandle::Invalid(); + } + + DataInstances createTestDataInstance(bool setTextureSampler = true, bool setIndexArray = true) + { + //create samplers + sampler = sceneAllocator.allocateTextureSampler({ { ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Nearest_MipMapNearest, ETextureSamplingMethod::Nearest, 2u }, MockResourceHash::TextureHash }); + samplerMS = sceneAllocator.allocateTextureSampler({ {}, MockResourceHash::TextureHash }); + samplerExternal = sceneAllocator.allocateTextureSampler( + { + { + ETextureAddressMode::Repeat, + ETextureAddressMode::Clamp, + ETextureAddressMode::Mirror, + ETextureSamplingMethod::Nearest_MipMapNearest, + ETextureSamplingMethod::Nearest, 1u + }, + TextureSampler::ContentType::ExternalTexture, + ResourceContentHash::Invalid(), + fakeExternalBuffer.asMemoryHandle() + }); + + // create data instance + DataInstances dataInstances; + dataInstances.first = sceneAllocator.allocateDataInstance(uniformLayout); + dataInstances.second = sceneAllocator.allocateDataInstance(geometryLayout); + + // create referenced data instance + // explicit preallocation needed because here we use DataLayoutCreationHelper which allocates inside, + // we cannot use scene allocation helper + MemoryHandle nextHandle = std::max(scene.getDataInstanceCount(), scene.getDataLayoutCount()); + scene.preallocateSceneSize(SceneSizeInformation(0u, 0u, 0u, 0u, 0u, nextHandle + 4u, nextHandle + 4u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)); + dataRef1 = DataLayoutCreationHelper::CreateAndBindDataReference( + scene, dataInstances.first, fakeEffectInputs.dataRefField1, EDataType::Float, DataLayoutHandle(nextHandle), DataInstanceHandle(nextHandle)); + dataRef2 = DataLayoutCreationHelper::CreateAndBindDataReference( + scene, dataInstances.first, fakeEffectInputs.dataRefField2, EDataType::Float, DataLayoutHandle(nextHandle + 1u), DataInstanceHandle(nextHandle + 1u)); + dataRefMatrix22f = DataLayoutCreationHelper::CreateAndBindDataReference( + scene, dataInstances.first, fakeEffectInputs.dataRefFieldMatrix22f, EDataType::Matrix22F, DataLayoutHandle(nextHandle + 2u), DataInstanceHandle(nextHandle + 2u)); + dataRefBool = DataLayoutCreationHelper::CreateAndBindDataReference( + scene, dataInstances.first, fakeEffectInputs.dataRefFieldBool, EDataType::Bool, DataLayoutHandle(nextHandle + 3u), DataInstanceHandle(nextHandle + 3u)); + scene.setDataSingleFloat(dataRef1, DataFieldHandle(0u), 0.1f); + scene.setDataSingleFloat(dataRef2, DataFieldHandle(0u), -666.f); + scene.setDataSingleMatrix22f(dataRefMatrix22f, DataFieldHandle(0u), glm::mat2(1, 2, 3, 4)); + scene.setDataSingleBoolean(dataRefBool, DataFieldHandle(0u), true); + + if (setIndexArray) + { + scene.setDataResource(dataInstances.second, indicesField, MockResourceHash::IndexArrayHash, DataBufferHandle::Invalid(), 2u, 0u, 0u); + } + scene.setDataResource(dataInstances.second, vertPosField, MockResourceHash::VertArrayHash, DataBufferHandle::Invalid(), 3u, 17u, 77u); + scene.setDataResource(dataInstances.second, vertTexcoordField, MockResourceHash::VertArrayHash2, DataBufferHandle::Invalid(), 4u, 18u, 88u); + ON_CALL(resourceManager, getResourceDeviceHandle(MockResourceHash::VertArrayHash2)).WillByDefault(Return(DeviceMock::FakeVertexBufferDeviceHandle)); + + if (setTextureSampler) + { + scene.setDataTextureSamplerHandle(dataInstances.first, textureField, sampler); + scene.setDataTextureSamplerHandle(dataInstances.first, textureFieldMS, samplerMS); + scene.setDataTextureSamplerHandle(dataInstances.first, textureFieldExternal, samplerExternal); + } + ON_CALL(resourceManager, getExternalBufferDeviceHandle(fakeExternalBuffer)).WillByDefault(Return(fakeExternalTextureDeviceHandle)); + + return dataInstances; + } + + RenderGroupHandle createRenderGroup(RenderPassHandle pass) + { + const RenderGroupHandle renderGroup = sceneAllocator.allocateRenderGroup(); + scene.addRenderGroupToRenderPass(pass, renderGroup, 0); + return renderGroup; + } + + RenderableHandle createTestRenderable( + DataInstances dataInstances, + RenderGroupHandle group = RenderGroupHandle::Invalid(), + RenderableHandle renderableHandle = RenderableHandle::Invalid()) + { + const RenderableHandle renderable = sceneAllocator.allocateRenderable(sceneAllocator.allocateNode(), renderableHandle); + + scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Uniforms, dataInstances.first); + scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Geometry, dataInstances.second); + scene.setRenderableRenderState(renderable, sceneAllocator.allocateRenderState()); + + scene.setRenderableStartIndex(renderable, startIndex); + scene.setRenderableIndexCount(renderable, indexCount); + scene.setRenderableStartVertex(renderable, startVertex); + + if (group.isValid()) + { + scene.addRenderableToRenderGroup(group, renderable, 0); + } + + return renderable; + } + + TransformHandle addTransformToNode(NodeHandle node) + { + return sceneAllocator.allocateTransform(node); + } + + TransformHandle addTransformToRenderable(RenderableHandle renderableHandle) + { + const NodeHandle node = scene.getRenderable(renderableHandle).node; + return addTransformToNode(node); + } + + RenderTargetHandle createRenderTarget(uint32_t width = 800u, uint32_t height = 600u) + { + const RenderTargetHandle targetHandle = sceneAllocator.allocateRenderTarget(); + const RenderBufferHandle colorBufferHandle = sceneAllocator.allocateRenderBuffer({ width, height, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }); + const RenderBufferHandle depthBufferHandle = sceneAllocator.allocateRenderBuffer({ width, height, EPixelStorageFormat::Depth24, ERenderBufferAccessMode::WriteOnly, 0u }); + scene.addRenderTargetRenderBuffer(targetHandle, colorBufferHandle); + scene.addRenderTargetRenderBuffer(targetHandle, depthBufferHandle); + + return targetHandle; + } + + RenderBufferHandle createRenderbuffer() + { + const RenderBufferHandle bufferHandle = sceneAllocator.allocateRenderBuffer({ 10u, 20u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }); + + return bufferHandle; + } + + BlitPassHandle createBlitPass(RenderBufferHandle sourceRenderBuffer, RenderBufferHandle destinationRenderBuffer) + { + const BlitPassHandle passHandle = sceneAllocator.allocateBlitPass(sourceRenderBuffer, destinationRenderBuffer); + return passHandle; + } + + void expectFrameWithSinglePass( + RenderableHandle renderable, + const ProjectionParams& projectionParams, + glm::mat4 expectedModelMatrix = glm::mat4(1.f), + glm::mat4 expectedViewMatrix = glm::mat4(1.f)) + { + expectActivateRenderTarget(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle, true); + if (renderContext.displayBufferClearPending != EClearFlag::None) + expectClearRenderTarget(renderContext.displayBufferClearPending); + const glm::mat4 projMatrix = CameraMatrixHelper::ProjectionMatrix(projectionParams); + expectFrameRenderCommands(renderable, expectedModelMatrix, expectedViewMatrix, projMatrix); + } + + void expectFrameRenderCommands(RenderableHandle /*renderable*/, + glm::mat4 expectedModelMatrix = glm::mat4(1.f), + glm::mat4 expectedViewMatrix = glm::mat4(1.f), + glm::mat4 expectedProjMatrix = glm::mat4(1.f), + bool expectShaderActivation = true, + EExpectedRenderStateChange expectRenderStateChanges = EExpectedRenderStateChange::All, + uint32_t instanceCount = 1u, + bool expectIndexedRendering = true, + int32_t expectedUniformTimeMs = 0) + { + // TODO violin this is not entirely needed, only need to check that draw call is at the end of the commands + InSequence seq; + + const DeviceResourceHandle FakeShaderDeviceHandle = DeviceMock::FakeShaderDeviceHandle ; + const DeviceResourceHandle FakeTextureDeviceHandle = DeviceMock::FakeTextureDeviceHandle ; + + // RetiresOnSaturation makes it possible to invoke this expect method multiple times without + // the expectations override each other .RetiresOnSaturation(); + + if (expectRenderStateChanges == EExpectedRenderStateChange::All) + { + EXPECT_CALL(device, scissorTest(_, _)) .InSequence(deviceSequence); + EXPECT_CALL(device, depthFunc(_)) .InSequence(deviceSequence); + EXPECT_CALL(device, depthWrite(_)) .InSequence(deviceSequence); + EXPECT_CALL(device, stencilFunc(_,_,_)) .InSequence(deviceSequence); + EXPECT_CALL(device, stencilOp(_,_,_)) .InSequence(deviceSequence); + EXPECT_CALL(device, blendOperations(_, _)) .InSequence(deviceSequence); + EXPECT_CALL(device, blendFactors(_, _, _, _)) .InSequence(deviceSequence); + EXPECT_CALL(device, blendColor(_)) .InSequence(deviceSequence); + EXPECT_CALL(device, colorMask(_, _, _, _)) .InSequence(deviceSequence); + EXPECT_CALL(device, cullMode(_)) .InSequence(deviceSequence); + } + else if (expectRenderStateChanges == EExpectedRenderStateChange::CausedByClear) + { + EXPECT_CALL(device, scissorTest(_, _)).InSequence(deviceSequence); + EXPECT_CALL(device, depthWrite(_)).InSequence(deviceSequence); + EXPECT_CALL(device, colorMask(_, _, _, _)).InSequence(deviceSequence); + } + EXPECT_CALL(device, drawMode(_)).InSequence(deviceSequence); + + if (expectShaderActivation) + { + EXPECT_CALL(device, activateShader(FakeShaderDeviceHandle)) .InSequence(deviceSequence); + } + EXPECT_CALL(device, activateVertexArray(_)) .InSequence(deviceSequence); + EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefField1, 1, Matcher(Pointee(Eq(0.1f))))) .InSequence(deviceSequence); + EXPECT_CALL(device, setConstant(fieldModelMatrix, 1, Matcher(Pointee(PermissiveMatrixEq(expectedModelMatrix))))) .InSequence(deviceSequence); + EXPECT_CALL(device, setConstant(fieldViewMatrix, 1, Matcher(Pointee(PermissiveMatrixEq(expectedViewMatrix))))) .InSequence(deviceSequence); + EXPECT_CALL(device, setConstant(fieldProjMatrix, 1, Matcher(Pointee(PermissiveMatrixEq(expectedProjMatrix))))) .InSequence(deviceSequence); + EXPECT_CALL(device, activateTexture(FakeTextureDeviceHandle, textureField)) .InSequence(deviceSequence); + const TextureSamplerStates expectedSamplerStates(ETextureAddressMode::Clamp, ETextureAddressMode::Repeat, ETextureAddressMode::Mirror, ETextureSamplingMethod::Nearest_MipMapNearest, ETextureSamplingMethod::Nearest, 2u); + EXPECT_CALL(device, activateTextureSamplerObject(Property(&TextureSamplerStates::hash, Eq(expectedSamplerStates.hash())), textureField)).InSequence(deviceSequence); + EXPECT_CALL(device, activateTexture(FakeTextureDeviceHandle, textureFieldMS)) .InSequence(deviceSequence); + + EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefField2, 1, Matcher(Pointee(Eq(-666.f))))) .InSequence(deviceSequence); + EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefFieldMatrix22f, 1, Matcher(Pointee(Eq(glm::mat2(1,2,3,4)))))) .InSequence(deviceSequence); + + EXPECT_CALL(device, activateTexture(fakeExternalTextureDeviceHandle, textureFieldExternal)).InSequence(deviceSequence); + const TextureSamplerStates expectedSamplerExternalStates(ETextureAddressMode::Repeat, ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Nearest_MipMapNearest, ETextureSamplingMethod::Nearest, 1u); + EXPECT_CALL(device, activateTextureSamplerObject(Property(&TextureSamplerStates::hash, Eq(expectedSamplerExternalStates.hash())), textureFieldExternal)).InSequence(deviceSequence); + + EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefFieldBool, 1, Matcher(Pointee(Eq(true))))) .InSequence(deviceSequence); + + if (fakeEffectInputs.dataRefTimeMs.isValid()) + { + EXPECT_CALL(device, setConstant(fakeEffectInputs.dataRefTimeMs, 1, Matcher(Pointee(TimeEq(expectedUniformTimeMs))))) .InSequence(deviceSequence); + } + if (expectIndexedRendering) + { + EXPECT_CALL(device, drawIndexedTriangles(startIndex, indexCount, instanceCount)).InSequence(deviceSequence); + } + else + { + EXPECT_CALL(device, drawTriangles(startIndex, indexCount, instanceCount)).InSequence(deviceSequence); + } + } + + void expectActivateRenderTarget(DeviceResourceHandle rtDeviceHandle, bool expectViewport = true, const Viewport& viewport = Viewport(fakeViewportX, fakeViewportY, fakeViewportWidth, fakeViewportHeight)) + { + EXPECT_CALL(device, activateRenderTarget(rtDeviceHandle)).InSequence(deviceSequence); + if (expectViewport) + { + EXPECT_CALL(device, setViewport(viewport.posX, viewport.posY, viewport.width, viewport.height)).InSequence(deviceSequence); + } + } + + void expectActivateFramebufferRenderTarget(bool expectViewport = true) + { + EXPECT_CALL(device, activateRenderTarget(DeviceMock::FakeFrameBufferRenderTargetDeviceHandle)).InSequence(deviceSequence); + if (expectViewport) + EXPECT_CALL(device, setViewport(fakeViewportX, fakeViewportY, fakeViewportWidth, fakeViewportHeight)).InSequence(deviceSequence); + } + + void expectClearRenderTarget(ClearFlags clearFlags = EClearFlag::All) + { + if (clearFlags.isSet(EClearFlag::Color)) + { + EXPECT_CALL(device, colorMask(true, true, true, true)).InSequence(deviceSequence); + EXPECT_CALL(device, clearColor(_)).InSequence(deviceSequence); + } + if (clearFlags.isSet(EClearFlag::Depth)) + { + EXPECT_CALL(device, depthWrite(EDepthWrite::Enabled)).InSequence(deviceSequence); + } + + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(device, scissorTest(EScissorTest::Disabled, scissorRegion)).InSequence(deviceSequence); + + EXPECT_CALL(device, clear(clearFlags)).InSequence(deviceSequence); + } + + void expectDepthStencilDiscard() + { + EXPECT_CALL(device, discardDepthStencil()).InSequence(deviceSequence); + } + + void updateScenes(const RenderableVector& renderablesWithUpdatedVAOs) + { + scene.updateRenderablesAndResourceCache(resourceManager); + scene.updateRenderableVertexArrays(resourceManager, renderablesWithUpdatedVAOs); + scene.markVertexArraysClean(); + scene.updateRenderableWorldMatrices(); + } + + void expectRenderingWithProjection(RenderableHandle renderable, const glm::mat4& projMatrix, const uint32_t instanceCount = 1u) + { + updateScenes({renderable}); + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable, glm::identity(), glm::identity(), projMatrix, true, EExpectedRenderStateChange::All, instanceCount); + executeScene(); + + Mock::VerifyAndClearExpectations(&renderer); + } + + SceneRenderExecutionIterator executeScene(const FrameTimer* frameTimer = nullptr) + { + RenderExecutor executor(device, renderContext, frameTimer); + return executor.executeScene(scene); + } + }; + + class ARenderExecutor : public ARenderExecutorBase + { + public: + ARenderExecutor() + : ARenderExecutorBase(false) + { + } + }; + + class ARenderExecutorTimeMs : public ARenderExecutorBase + { + public: + ARenderExecutorTimeMs() + : ARenderExecutorBase(true) + { + } + }; + + TEST_F(ARenderExecutor, RendersEmptyFrameForEmptyScene) + { + updateScenes({}); + executeScene({}); + } + + TEST_F(ARenderExecutor, RenderRenderPassIntoRenderTarget) + { + const RenderPassHandle pass = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + scene.setRenderPassClearFlag(pass, EClearFlag::None); + scene.setRenderPassRenderTarget(pass, targetHandle); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + updateScenes({ renderable }); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + { + InSequence seq; + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectFrameRenderCommands(renderable, glm::identity(), glm::identity(), expectedProjectionMatrix); + } + + executeScene(); + } + + TEST_F(ARenderExecutor, RenderRenderableWithoutIndexArray) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(true, false), createRenderGroup(pass)); + scene.setRenderPassClearFlag(pass, EClearFlag::None); + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + updateScenes({ renderable }); + + { + InSequence seq; + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable, glm::identity(), glm::identity(), expectedProjectionMatrix, true, EExpectedRenderStateChange::All, 1, false); + } + + executeScene(); + } + + TEST_F(ARenderExecutor, RenderMultipleConsecutiveRenderPassesIntoOneRenderTarget) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(pass1, targetHandle); + + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass2, EClearFlag::None); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); + scene.setRenderPassRenderTarget(pass2, targetHandle); + + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + updateScenes({ renderable1, renderable2 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + { + InSequence seq; + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + expectDepthStencilDiscard(); + } + + executeScene(); + } + + TEST_F(ARenderExecutor, RenderMultipleRenderPassesIntoMultipleRenderTargets) + { + const auto projParams1 = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const Viewport fakeVp1(1, 2, 3, 4); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams1, fakeVp1); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + const RenderTargetHandle targetHandle1 = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(pass1, targetHandle1); + + const auto projParams2 = GetDefaultProjectionParams(ECameraProjectionType::Orthographic); + const Viewport fakeVp2(5, 6, 7, 8); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams2, fakeVp2); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); + const RenderTargetHandle targetHandle2 = createRenderTarget(17, 21); + scene.setRenderPassRenderTarget(pass2, targetHandle2); + + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + updateScenes({ renderable1, renderable2 }); + + const auto expectedProjectionMatrix1 = CameraMatrixHelper::ProjectionMatrix(projParams1); + const auto expectedProjectionMatrix2 = CameraMatrixHelper::ProjectionMatrix(projParams2); + + const DeviceResourceHandle renderTargetDeviceHandle1 = resourceManager.getRenderTargetDeviceHandle(targetHandle1, scene.getSceneId()); + const DeviceResourceHandle renderTargetDeviceHandle2 = resourceManager.getRenderTargetDeviceHandle(targetHandle2, scene.getSceneId()); + + { + InSequence seq; + + expectActivateRenderTarget(renderTargetDeviceHandle1, true, fakeVp1); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix1); + expectDepthStencilDiscard(); + expectActivateRenderTarget(renderTargetDeviceHandle2, true, fakeVp2); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix2, false, EExpectedRenderStateChange::CausedByClear); + expectDepthStencilDiscard(); + } + + executeScene(); + } + + TEST_F(ARenderExecutor, ResetsCachedRenderStatesAfterClearingRenderTargets) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); + const RenderTargetHandle targetHandle2 = createRenderTarget(17, 21); + scene.setRenderPassRenderTarget(pass2, targetHandle2); + + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + updateScenes({ renderable1, renderable2 }); + + const DeviceResourceHandle renderTargetDeviceHandle2 = resourceManager.getRenderTargetDeviceHandle(targetHandle2, scene.getSceneId()); + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + { + InSequence seq; + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), projMatrix); + expectActivateRenderTarget(renderTargetDeviceHandle2, false); + expectClearRenderTarget(); + //render states are set again but shader and index buffer do not have to be set again + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), projMatrix, false, EExpectedRenderStateChange::CausedByClear); + expectDepthStencilDiscard(); + } + + executeScene(); + } + + TEST_F(ARenderExecutor, RenderMultipleRenderPassesIntoOneRenderTargetAndEachClearsWithDifferentFlags) + { + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass1, EClearFlag::All); + scene.setRenderPassRenderTarget(pass1, targetHandle); + + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass2, EClearFlag::Color); + scene.setRenderPassRenderTarget(pass2, targetHandle); + + const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass3, EClearFlag::Depth); + scene.setRenderPassRenderTarget(pass3, targetHandle); + + const RenderPassHandle pass4 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass4, EClearFlag::Stencil); + scene.setRenderPassRenderTarget(pass4, targetHandle); + + const RenderPassHandle pass5 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass5, EClearFlag::Color | EClearFlag::Depth); + scene.setRenderPassRenderTarget(pass5, targetHandle); + + const RenderPassHandle pass6 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass6, EClearFlag::Color | EClearFlag::Stencil); + scene.setRenderPassRenderTarget(pass6, targetHandle); + + const RenderPassHandle pass7 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass7, EClearFlag::Depth | EClearFlag::Stencil); + scene.setRenderPassRenderTarget(pass7, targetHandle); + + const RenderPassHandle pass8 = createRenderPassWithCamera(projParams); + scene.setRenderPassClearFlag(pass8, EClearFlag::None); + scene.setRenderPassRenderTarget(pass8, targetHandle); + + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + scene.setRenderPassRenderOrder(pass3, 2); + scene.setRenderPassRenderOrder(pass4, 3); + scene.setRenderPassRenderOrder(pass5, 4); + scene.setRenderPassRenderOrder(pass6, 5); + scene.setRenderPassRenderOrder(pass7, 6); + scene.setRenderPassRenderOrder(pass8, 7); + + updateScenes({}); + + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + { + InSequence seq; + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(EClearFlag::All); // pass 1 + expectClearRenderTarget(EClearFlag::Color); // pass 2 + expectClearRenderTarget(EClearFlag::Depth); // pass 3 + expectClearRenderTarget(EClearFlag::Stencil); // pass 4 + expectClearRenderTarget(EClearFlag::Color | EClearFlag::Depth); // pass 5 + expectClearRenderTarget(EClearFlag::Color | EClearFlag::Stencil); // pass 6 + expectDepthStencilDiscard(); + expectClearRenderTarget(EClearFlag::Depth | EClearFlag::Stencil); // pass 7 + // pass 8 no clear expectation + expectDepthStencilDiscard(); + } + + executeScene(); + } + + TEST_F(ARenderExecutor, DoesNotRenderRenderableIfResourcesInvalid) + { + const RenderPassHandle pass = createRenderPassWithCamera(GetDefaultProjectionParams()); + DataInstances InvalidDataInstances; + createTestRenderable(InvalidDataInstances, createRenderGroup(pass)); + + updateScenes({}); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + // empty frame + + executeScene(); + } + + TEST_F(ARenderExecutor, DoesNotRenderRenderableWithNoResourcesAssigned) + { + const RenderPassHandle pass = createRenderPassWithCamera(GetDefaultProjectionParams()); + const RenderGroupHandle group = createRenderGroup(pass); + const RenderableHandle renderable = sceneAllocator.allocateRenderable(sceneAllocator.allocateNode()); + scene.addRenderableToRenderGroup(group, renderable, 0); + + updateScenes({ renderable }); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + // empty frame + + executeScene(); + } + + TEST_F(ARenderExecutor, DoesNotRenderRenderableWithoutDataInstance) + { + const RenderPassHandle pass = createRenderPassWithCamera(GetDefaultProjectionParams()); + + DataInstances InvalidDataInstances; + createTestRenderable(InvalidDataInstances, createRenderGroup(pass), RenderableHandle::Invalid()); + + updateScenes({}); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + executeScene(); + } + + TEST_F(ARenderExecutor, expectUpdateSceneDefaultMatricesIdentity) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); + + updateScenes({ renderable }); + expectFrameWithSinglePass(renderable, projParams); + executeScene(); + + Mock::VerifyAndClearExpectations(&device); + } + + TEST_F(ARenderExecutor, SceneViewMatrixChangeViaCameraTransformation) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); + + updateScenes({ renderable }); + expectFrameWithSinglePass(renderable, projParams); + executeScene(); + Mock::VerifyAndClearExpectations(&device); + + const NodeHandle cameraNode = scene.getCamera(scene.getRenderPass(pass).camera).node; + const TransformHandle cameraTransform = findTransformForNode(cameraNode); + scene.setTranslation(cameraTransform, glm::vec3(2.f, 3.f, 4.f)); + + updateScenes({}); + expectFrameWithSinglePass(renderable, projParams, glm::identity(), glm::translate(glm::vec3(-2.f, -3.f, -4.f))); + executeScene(); + Mock::VerifyAndClearExpectations(&device); + } + + TEST_F(ARenderExecutor, RendersRenderableWithRendererProjection) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle renderPass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass)); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + expectRenderingWithProjection(renderable, projMatrix); + } + + TEST_F(ARenderExecutor, RendersMultipleRenderableInstancesWithRendererProjection) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle renderPass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass)); + const uint32_t instanceCount = 23u; + scene.setRenderableInstanceCount(renderable, instanceCount); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + expectRenderingWithProjection(renderable, projMatrix, instanceCount); + } + + TEST_F(ARenderExecutor, RendersRenderableWithOrthographicProjection) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Orthographic); + const RenderPassHandle renderPass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass)); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + expectRenderingWithProjection(renderable, projMatrix); + } + + TEST_F(ARenderExecutor, RendersRenderableInTwoPassesUsingTheSameCamera) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle renderPass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle renderPass2 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass1)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass2)); + const CameraHandle camera1 = scene.getRenderPass(renderPass1).camera; + const CameraHandle camera2 = scene.getRenderPass(renderPass2).camera; + const NodeHandle cameraNode1 = scene.getCamera(camera1).node; + const NodeHandle cameraNode2 = scene.getCamera(camera2).node; + const TransformHandle cameraTransform1 = findTransformForNode(cameraNode1); + const TransformHandle cameraTransform2 = findTransformForNode(cameraNode2); + + scene.setRenderPassRenderOrder(renderPass1, 0); + scene.setRenderPassRenderOrder(renderPass2, 1); + + scene.setTranslation(cameraTransform1, glm::vec3(1.f, 2.f, 3.f)); + scene.setTranslation(cameraTransform2, glm::vec3(4.f, 5.f, 6.f)); + + updateScenes({ renderable1, renderable2 }); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + expectFrameRenderCommands(renderable1, glm::identity(), glm::translate(glm::vec3(-1.f, -2.f, -3.f)), projMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::translate(glm::vec3(-4.f, -5.f, -6.f)), projMatrix, false, EExpectedRenderStateChange::None); + executeScene(); + Mock::VerifyAndClearExpectations(&device); + } + + TEST_F(ARenderExecutor, RenderStatesAppliedOnceIfSameForMultipleRenderables) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle renderPass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle renderPass2 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass1)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass2)); + + updateScenes({ renderable1, renderable2 }); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), projMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), projMatrix, false, EExpectedRenderStateChange::None); + + executeScene(); + Mock::VerifyAndClearExpectations(&device); + } + + TEST_F(ARenderExecutor, RenderStatesAppliedForEachRenderableIfDifferent) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle renderPass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle renderPass2 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass1)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(renderPass2)); + + const RenderStateHandle state1 = scene.getRenderable(renderable1).renderState; + const RenderStateHandle state2 = scene.getRenderable(renderable2).renderState; + + scene.setRenderStateScissorTest(state1, EScissorTest::Disabled, {}); + scene.setRenderStateScissorTest(state2, EScissorTest::Enabled, {}); + scene.setRenderStateDepthWrite(state1, EDepthWrite::Enabled); + scene.setRenderStateDepthWrite(state2, EDepthWrite::Disabled); + scene.setRenderStateDepthFunc(state1, EDepthFunc::Always); + scene.setRenderStateDepthFunc(state2, EDepthFunc::Disabled); + scene.setRenderStateStencilFunc(state1, EStencilFunc::Always, 0u, 0u); + scene.setRenderStateStencilFunc(state2, EStencilFunc::Equal, 0u, 0u); + scene.setRenderStateBlendFactors(state1, EBlendFactor::ConstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor); + scene.setRenderStateBlendFactors(state2, EBlendFactor::DstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor, EBlendFactor::ConstColor); + scene.setRenderStateBlendOperations(state1, EBlendOperation::Add, EBlendOperation::Add); + scene.setRenderStateBlendOperations(state2, EBlendOperation::Subtract, EBlendOperation::Add); + scene.setRenderStateBlendColor(state1, glm::vec4(1.f)); + scene.setRenderStateBlendColor(state2, glm::vec4(.5f)); + scene.setRenderStateColorWriteMask(state1, EColorWriteFlag_Red); + scene.setRenderStateColorWriteMask(state2, EColorWriteFlag_Blue); + scene.setRenderStateCullMode(state1, ECullMode::BackFacing); + scene.setRenderStateCullMode(state2, ECullMode::Disabled); + + updateScenes({ renderable1, renderable2 }); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), projMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), projMatrix, false, EExpectedRenderStateChange::All); + + executeScene(); + Mock::VerifyAndClearExpectations(&device); + } + + TEST_F(ARenderExecutor, UpdatesModelMatrixWhenChangingTranslationRotationOrScalingOfNode) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(), createRenderGroup(pass)); + const TransformHandle transform = addTransformToRenderable(renderable); + + scene.setTranslation(transform, glm::vec3(0.5f)); + updateScenes({ renderable }); + expectFrameWithSinglePass(renderable, projParams, glm::translate(glm::vec3(0.5f))); + executeScene(); + + Mock::VerifyAndClearExpectations(&device); + + scene.setTranslation(transform, glm::vec3(0.15f)); + scene.setScaling(transform, glm::vec3(0.25f)); + const auto rotation = glm::normalize(glm::quat(1.f, 2.f, 3.f, 4.f)); + scene.setRotation(transform, glm::vec4(rotation.x, rotation.y, rotation.z, rotation.w), ERotationType::Quaternion); + updateScenes({}); + expectFrameWithSinglePass(renderable, projParams, + glm::translate(glm::vec3(0.15f)) * glm::scale(glm::vec3(0.25f)) * glm::toMat4(rotation)); + executeScene(); + } + + TEST_F(ARenderExecutor, AppliesParentTransformationToBothChildNodes) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderGroupHandle group = createRenderGroup(pass); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), group); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), group); + const NodeHandle childNode1 = scene.getRenderable(renderable1).node; + const NodeHandle childNode2 = scene.getRenderable(renderable2).node; + const NodeHandle parentNode = sceneAllocator.allocateNode(); + scene.addChildToNode(parentNode, childNode1); + scene.addChildToNode(parentNode, childNode2); + const TransformHandle transformChild1 = addTransformToNode(childNode1); + const TransformHandle transformChild2 = addTransformToNode(childNode2); + const TransformHandle transformParent = addTransformToNode(parentNode); + + scene.setTranslation(transformParent, glm::vec3(0.25f)); + scene.setTranslation(transformChild1, glm::vec3(0.15f)); + scene.setTranslation(transformChild2, glm::vec3(0.05f)); + updateScenes({ renderable1, renderable2 }); + + const auto projMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + expectFrameRenderCommands(renderable1, glm::translate(glm::vec3(0.40f)), glm::identity(), projMatrix); + expectFrameRenderCommands(renderable2, glm::translate(glm::vec3(0.30f)), glm::identity(), projMatrix, false, EExpectedRenderStateChange::None); + executeScene(); + + Mock::VerifyAndClearExpectations(&device); + + scene.setTranslation(transformParent, glm::vec3(0.0f)); + updateScenes({}); + + expectActivateFramebufferRenderTarget(); + expectFrameRenderCommands(renderable1, glm::translate(glm::vec3(0.15f)), glm::identity(), projMatrix); + expectFrameRenderCommands(renderable2, glm::translate(glm::vec3(0.05f)), glm::identity(), projMatrix, false, EExpectedRenderStateChange::None); + executeScene(); + + Mock::VerifyAndClearExpectations(&device); + } + + TEST_F(ARenderExecutor, ExecutesBlitPass) + { + const RenderBufferHandle sourceRenderBuffer = createRenderbuffer(); + const RenderBufferHandle destinationRenderBuffer = createRenderbuffer(); + const BlitPassHandle blitPassHandle = createBlitPass(sourceRenderBuffer, destinationRenderBuffer); + updateScenes({}); + + EXPECT_CALL(device, blitRenderTargets(_, _, _, _, _)).InSequence(deviceSequence); + executeScene(); + + scene.releaseRenderBuffer(sourceRenderBuffer); + scene.releaseRenderBuffer(destinationRenderBuffer); + scene.releaseBlitPass(blitPassHandle); + updateScenes({}); + } + + TEST_F(ARenderExecutor, ActivatesRenderTargetForRenderPassAfterExecutingBlitPass) + { + const RenderBufferHandle sourceRenderBuffer = createRenderbuffer(); + const RenderBufferHandle destinationRenderBuffer = createRenderbuffer(); + const BlitPassHandle blitPass = createBlitPass(sourceRenderBuffer, destinationRenderBuffer); + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle firstRenderPass = createRenderPassWithCamera(projParams); + const RenderPassHandle secondRenderPass = createRenderPassWithCamera(projParams); + + scene.setRenderPassRenderOrder(firstRenderPass , 0); + scene.setBlitPassRenderOrder(blitPass , 1); + scene.setRenderPassRenderOrder(secondRenderPass , 2); + updateScenes({}); + + { + //first render pass + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + //blit pass + EXPECT_CALL(device, blitRenderTargets(_, _, _, _, _)).InSequence(deviceSequence); + + //second render pass + expectActivateFramebufferRenderTarget(false); + } + + executeScene(); + + scene.releaseRenderBuffer(sourceRenderBuffer); + scene.releaseRenderBuffer(destinationRenderBuffer); + scene.releaseBlitPass(blitPass); + updateScenes({}); + } + + TEST_F(ARenderExecutor, willRenderAllRenderablesIfWithinTimeBudget) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const DataInstances dataInstances = createTestDataInstance(); + const RenderableHandle renderable1 = createTestRenderable(dataInstances, createRenderGroup(pass1)); + const RenderableHandle renderable2 = createTestRenderable(dataInstances, createRenderGroup(pass1)); + const RenderableHandle renderable3 = createTestRenderable(dataInstances, createRenderGroup(pass2)); + const RenderableHandle renderable4 = createTestRenderable(dataInstances, createRenderGroup(pass2)); + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(pass1, targetHandle); + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + updateScenes({ renderable1 ,renderable2, renderable3, renderable4 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + { + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + expectDepthStencilDiscard(); + + expectActivateFramebufferRenderTarget(false); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable3, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::CausedByClear); + expectFrameRenderCommands(renderable4, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + } + + FrameTimer frameTimer; + frameTimer.startFrame(); + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, std::numeric_limits::max()); + + const auto renderIterator = executeScene(&frameTimer); + EXPECT_EQ(SceneRenderExecutionIterator(), renderIterator); // finished + } + + TEST_F(ARenderExecutor, willRenderAtLeastOneRenderableBatchIfExceededTimeBudget) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const DataInstances dataInstances = createTestDataInstance(); + const RenderGroupHandle renderGroup = createRenderGroup(pass1); + std::array batchRenderables; + for (auto& renderable : batchRenderables) + renderable = createTestRenderable(dataInstances, renderGroup); + [[maybe_unused]] const RenderableHandle renderableOutOfBudget = createTestRenderable(dataInstances, renderGroup); + + updateScenes({ batchRenderables.cbegin(), batchRenderables.cend() }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + + { + InSequence s; + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + + // one batch of renderables is rendered, first sets states + expectFrameRenderCommands(batchRenderables.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); + for (auto it = batchRenderables.cbegin() + 1; it != batchRenderables.cend(); ++it) + expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + } + + FrameTimer frameTimer; + frameTimer.startFrame(); + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, 0u); + + const auto renderIterator = executeScene(&frameTimer); + EXPECT_NE(SceneRenderExecutionIterator(), renderIterator); // not finished + } + + TEST_F(ARenderExecutor, willContinueRenderingWhereLeftOffLastRenderWhenInterrupted) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const DataInstances dataInstances = createTestDataInstance(); + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(pass1, targetHandle); + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + const RenderGroupHandle renderGroup1 = createRenderGroup(pass1); + const RenderGroupHandle renderGroup2 = createRenderGroup(pass2); + std::array batchRenderables1; + std::array batchRenderables2; + std::array batchRenderables3; + std::array batchRenderables4; + for (auto& renderable : batchRenderables1) + renderable = createTestRenderable(dataInstances, renderGroup1); + for (auto& renderable : batchRenderables2) + renderable = createTestRenderable(dataInstances, renderGroup1); + for (auto& renderable : batchRenderables3) + renderable = createTestRenderable(dataInstances, renderGroup2); + for (auto& renderable : batchRenderables4) + renderable = createTestRenderable(dataInstances, renderGroup2); + + RenderableVector allRenderables; + allRenderables.insert(allRenderables.end(), batchRenderables1.cbegin(), batchRenderables1.cend()); + allRenderables.insert(allRenderables.end(), batchRenderables2.cbegin(), batchRenderables2.cend()); + allRenderables.insert(allRenderables.end(), batchRenderables3.cbegin(), batchRenderables3.cend()); + allRenderables.insert(allRenderables.end(), batchRenderables4.cbegin(), batchRenderables4.cend()); + + updateScenes(allRenderables); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + FrameTimer frameTimer; + frameTimer.startFrame(); + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, 0u); + + { + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(); + expectFrameRenderCommands(batchRenderables1.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); + for (auto it = batchRenderables1.cbegin() + 1; it != batchRenderables1.cend(); ++it) + expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + } + SceneRenderExecutionIterator renderIterator = executeScene(&frameTimer); + EXPECT_EQ(0u, renderIterator.getRenderPassIdx()); + EXPECT_EQ(1u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); + EXPECT_EQ(1u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); + + { + expectActivateRenderTarget(renderTargetDeviceHandle); // no clear + expectFrameRenderCommands(batchRenderables2.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); + for (auto it = batchRenderables2.cbegin() + 1; it != batchRenderables2.cend(); ++it) + expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + } + renderContext.renderFrom = renderIterator; + renderIterator = executeScene(&frameTimer); + EXPECT_EQ(0u, renderIterator.getRenderPassIdx()); + EXPECT_EQ(2u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); + EXPECT_EQ(2u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); + + { + // last render interrupted at last renderable from pass1, so it continues from there including activating of pass1's RT + // even though there are no more renderables to render + expectActivateRenderTarget(renderTargetDeviceHandle); + expectDepthStencilDiscard(); + + expectActivateFramebufferRenderTarget(false); + expectClearRenderTarget(); + expectFrameRenderCommands(batchRenderables3.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); + for (auto it = batchRenderables3.cbegin() + 1; it != batchRenderables3.cend(); ++it) + expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + } + renderContext.renderFrom = renderIterator; + renderIterator = executeScene(&frameTimer); + EXPECT_EQ(1u, renderIterator.getRenderPassIdx()); + EXPECT_EQ(1u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); + EXPECT_EQ(3u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); + + { + expectActivateFramebufferRenderTarget(); + expectFrameRenderCommands(batchRenderables4.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); + for (auto it = batchRenderables4.cbegin() + 1; it != batchRenderables4.cend(); ++it) + expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + } + renderContext.renderFrom = renderIterator; + renderIterator = executeScene(&frameTimer); + EXPECT_EQ(1u, renderIterator.getRenderPassIdx()); + EXPECT_EQ(2u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getRenderableIdx()); + EXPECT_EQ(4u * RenderExecutor::NumRenderablesToRenderInBetweenTimeBudgetChecks, renderIterator.getFlattenedRenderableIdx()); + + { + // last render interrupted at last renderable from pass2, so it continues from there including activating of pass2's RT being FB + // even though there are no more renderables to render + expectActivateFramebufferRenderTarget(); + // no additional render operation (eg. blit passes) + } + renderContext.renderFrom = renderIterator; + renderIterator = executeScene(&frameTimer); + EXPECT_EQ(SceneRenderExecutionIterator(), renderIterator); // finished + } + + TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_MainOnly) + { + const RenderPassHandle passMain = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + updateScenes({ renderable1 }); + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + + executeScene(); + + // main disp buffer was cleared, no pending clear in rendering context + EXPECT_EQ(EClearFlag::None, renderContext.displayBufferClearPending); + } + + TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_RenderTargetThenMain) + { + const RenderPassHandle passRT = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderPassHandle passMain = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passRT)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); + const RenderTargetHandle rt = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(passRT, rt); + scene.setRenderPassRenderOrder(passRT, 1); + scene.setRenderPassRenderOrder(passMain, 2); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + updateScenes({ renderable1, renderable2 }); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectDepthStencilDiscard(); + + expectActivateFramebufferRenderTarget(false); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, ARenderExecutor::EExpectedRenderStateChange::CausedByClear); + + executeScene(); + + // main disp buffer was cleared, no pending clear in rendering context + EXPECT_EQ(EClearFlag::None, renderContext.displayBufferClearPending); + } + + TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_MainThenRenderTarget) + { + const RenderPassHandle passMain = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderPassHandle passRT = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(passRT)); + const RenderTargetHandle rt = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(passRT, rt); + scene.setRenderPassRenderOrder(passMain, 1); + scene.setRenderPassRenderOrder(passRT, 2); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + updateScenes({ renderable1, renderable2 }); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectActivateRenderTarget(renderTargetDeviceHandle, false); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, ARenderExecutor::EExpectedRenderStateChange::CausedByClear); + expectDepthStencilDiscard(); + + executeScene(); + + // main disp buffer was cleared, no pending clear in rendering context + EXPECT_EQ(EClearFlag::None, renderContext.displayBufferClearPending); + } + + TEST_F(ARenderExecutor, DoesNotClearDispBufferWhenNotRenderingIntoIt_RenderTargetOnly) + { + const RenderPassHandle passRT = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(passRT)); + const RenderTargetHandle rt = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(passRT, rt); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + updateScenes({ renderable1 }); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectDepthStencilDiscard(); + + executeScene(); + + // main disp buffer was NOT cleared, pending clear still set in rendering context + EXPECT_EQ(EClearFlag::All, renderContext.displayBufferClearPending); + } + + TEST_F(ARenderExecutor, ClearsDispBufferBeforeRenderingIntoIt_InterruptedRenderTargetThenMain) + { + const RenderPassHandle passRT = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const DataInstances dataInstances = createTestDataInstance(); + const RenderTargetHandle rt = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(passRT, rt); + + const RenderGroupHandle renderGroup = createRenderGroup(passRT); + RenderableVector batchRenderables; + batchRenderables.resize(RenderExecutor::DefaultNumRenderablesToRenderInBetweenTimeBudgetChecks); + for (auto& renderable : batchRenderables) + renderable = createTestRenderable(dataInstances, renderGroup); + + const RenderPassHandle passMain = createRenderPassWithCamera(GetDefaultProjectionParams(ECameraProjectionType::Perspective)); + const RenderableHandle renderableMain = createTestRenderable(createTestDataInstance(), createRenderGroup(passMain)); + + scene.setRenderPassRenderOrder(passRT, 1); + scene.setRenderPassRenderOrder(passMain, 2); + + auto allRenderables = batchRenderables; + allRenderables.push_back(renderableMain); + updateScenes(allRenderables); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(ProjectionParams::Perspective(fakeFieldOfView, fakeAspectRatio, fakeNearPlane, fakeFarPlane)); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(rt, scene.getSceneId()); + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(EClearFlag::All); + + // one batch of renderables is rendered, first sets states + expectFrameRenderCommands(batchRenderables.front(), glm::identity(), glm::identity(), expectedProjectionMatrix); + for (auto it = batchRenderables.cbegin() + 1; it != batchRenderables.cend(); ++it) + expectFrameRenderCommands(*it, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + + FrameTimer frameTimer; + frameTimer.startFrame(); + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::OffscreenBufferRender, 0u); + + const auto renderIterator = executeScene(&frameTimer); + EXPECT_NE(SceneRenderExecutionIterator(), renderIterator); // not finished + + // main disp buffer was NOT cleared due to interrupt, pending clear kept in rendering context + EXPECT_EQ(EClearFlag::All, renderContext.displayBufferClearPending); + + expectActivateRenderTarget(renderTargetDeviceHandle); // will be activated to finish previous pass (although no more renderables to render in it) + expectDepthStencilDiscard(); + + expectActivateFramebufferRenderTarget(false); + expectClearRenderTarget(EClearFlag::All); + expectFrameRenderCommands(renderableMain, glm::identity(), glm::identity(), expectedProjectionMatrix); + + renderContext.renderFrom = renderIterator; + executeScene(); + + // main disp buffer was cleared, no pending clear in rendering context + EXPECT_EQ(EClearFlag::None, renderContext.displayBufferClearPending); + } + + TEST_F(ARenderExecutor, discardsDepthStencilAfterLastPassRenderingToFB_ifAllowedByRenderContext) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); + scene.setRenderPassClearFlag(pass2, EClearFlag::None); + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + updateScenes({ renderable1, renderable2 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + expectDepthStencilDiscard(); + + renderContext.displayBufferDepthDiscard = true; + executeScene(); + } + + TEST_F(ARenderExecutor, doesNotDiscardDepthStencil_ifNotAllowedByRenderContext) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); + scene.setRenderPassClearFlag(pass2, EClearFlag::None); + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + + updateScenes({ renderable1, renderable2 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + // no discard + + renderContext.displayBufferDepthDiscard = false; + executeScene(); + } + + TEST_F(ARenderExecutor, discardsDepthStencilAfterLastPassRenderingToFB_withRTPassInBetween) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + const RenderableHandle renderable2 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass2)); + const RenderableHandle renderable3 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass3)); + scene.setRenderPassClearFlag(pass3, EClearFlag::None); + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + scene.setRenderPassRenderOrder(pass3, 2); + + // second pass uses RT + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(pass2, targetHandle); + + updateScenes({ renderable1, renderable2, renderable3 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + // pass1 + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + // no discard + + // pass2 + expectActivateRenderTarget(renderTargetDeviceHandle, false); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable2, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::CausedByClear); + expectDepthStencilDiscard(); // discard for RT + + // pass3 + expectActivateFramebufferRenderTarget(false); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix, false, EExpectedRenderStateChange::None); + expectDepthStencilDiscard(); // discard for FB + + renderContext.displayBufferDepthDiscard = true; + executeScene(); + } + + TEST_F(ARenderExecutor, discardsDepthStencilWhenRenderToRTWithClear) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + const RenderTargetHandle targetHandle = createRenderTarget(16, 20); + scene.setRenderPassRenderTarget(pass1, targetHandle); + + updateScenes({ renderable1 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + expectDepthStencilDiscard(); + + executeScene(); + } + + TEST_F(ARenderExecutor, doesNotDiscardDepthStencilWhenRenderToRTWithNoDepthComponent) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderableHandle renderable1 = createTestRenderable(createTestDataInstance(), createRenderGroup(pass1)); + const RenderTargetHandle targetHandle = sceneAllocator.allocateRenderTarget(); + const RenderBufferHandle colorBufferHandle = sceneAllocator.allocateRenderBuffer({ 1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }); + scene.addRenderTargetRenderBuffer(targetHandle, colorBufferHandle); + scene.setRenderPassRenderTarget(pass1, targetHandle); + + updateScenes({ renderable1 }); + + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable1, glm::identity(), glm::identity(), expectedProjectionMatrix); + // dicard would be here if there was a depth component to discard + + executeScene(); + } + + TEST_F(ARenderExecutor, discardsDepthStencilWhenRenderToRT_onlyIfNextPassClearsDepthAndStencil) + { + const RenderTargetHandle targetHandle = createRenderTarget(1, 1); + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass2 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass4 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass5 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass6 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass7 = createRenderPassWithCamera(projParams); + const RenderPassHandle pass8 = createRenderPassWithCamera(projParams); + + scene.setRenderPassRenderTarget(pass1, targetHandle); + scene.setRenderPassRenderTarget(pass2, targetHandle); + scene.setRenderPassRenderTarget(pass3, targetHandle); + scene.setRenderPassRenderTarget(pass4, targetHandle); + scene.setRenderPassRenderTarget(pass5, targetHandle); + scene.setRenderPassRenderTarget(pass6, targetHandle); + // pass7 is FB pass + scene.setRenderPassRenderTarget(pass8, targetHandle); + + scene.setRenderPassClearFlag(pass1, EClearFlag::None); + scene.setRenderPassClearFlag(pass2, EClearFlag::Color); + scene.setRenderPassClearFlag(pass3, EClearFlag::Depth); + scene.setRenderPassClearFlag(pass4, EClearFlag::Stencil); + scene.setRenderPassClearFlag(pass5, EClearFlag::Color | EClearFlag::Depth); + scene.setRenderPassClearFlag(pass6, EClearFlag::Color | EClearFlag::Stencil); + scene.setRenderPassClearFlag(pass7, EClearFlag::All); + scene.setRenderPassClearFlag(pass8, EClearFlag::Depth | EClearFlag::Stencil); + + scene.setRenderPassRenderOrder(pass1, 0); + scene.setRenderPassRenderOrder(pass2, 1); + scene.setRenderPassRenderOrder(pass3, 2); + scene.setRenderPassRenderOrder(pass4, 3); + scene.setRenderPassRenderOrder(pass5, 4); + scene.setRenderPassRenderOrder(pass6, 5); + scene.setRenderPassRenderOrder(pass7, 6); + scene.setRenderPassRenderOrder(pass8, 7); + + updateScenes({}); + + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + expectActivateRenderTarget(renderTargetDeviceHandle); + // pass 1, no clear + expectClearRenderTarget(EClearFlag::Color); // pass 2 + expectClearRenderTarget(EClearFlag::Depth); // pass 3 + expectClearRenderTarget(EClearFlag::Stencil); // pass 4 + expectClearRenderTarget(EClearFlag::Color | EClearFlag::Depth); // pass 5 + expectClearRenderTarget(EClearFlag::Color | EClearFlag::Stencil); // pass 6 + expectDepthStencilDiscard(); // next pass (pass8) rendering to RT clears depth+stencil, can discard + + // pass 7 to FB + expectActivateFramebufferRenderTarget(false); + expectClearRenderTarget(); + expectDepthStencilDiscard(); // no other pass rendering to FB, can discard + + // pass 8 + expectActivateRenderTarget(renderTargetDeviceHandle, false); + expectClearRenderTarget(EClearFlag::Depth | EClearFlag::Stencil); + // next pass is pass1 next frame which does not clear, therefore cannot discard here even though at end of frame + + renderContext.displayBufferDepthDiscard = true; + executeScene(); + } + + TEST_F(ARenderExecutor, doesNotDiscardDepthStencilIfUpcomingPassUsesItAsBlitPassSource) + { + const RenderTargetHandle targetHandle = createRenderTarget(1, 1); + const RenderBufferHandle depthBuffer = scene.getRenderTargetRenderBuffer(targetHandle, 1u); + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + + const RenderPassHandle pass1 = createRenderPassWithCamera(projParams); + const BlitPassHandle pass2 = createBlitPass(depthBuffer, createRenderbuffer()); + const RenderPassHandle pass3 = createRenderPassWithCamera(projParams); + + scene.setRenderPassRenderTarget(pass1, targetHandle); + scene.setRenderPassRenderTarget(pass3, targetHandle); + + scene.setRenderPassClearFlag(pass1, EClearFlag::All); + scene.setRenderPassClearFlag(pass3, EClearFlag::All); + + scene.setRenderPassRenderOrder(pass1, 0); + scene.setBlitPassRenderOrder(pass2, 1); + scene.setRenderPassRenderOrder(pass3, 2); + + updateScenes({}); + + // pass1 + const DeviceResourceHandle renderTargetDeviceHandle = resourceManager.getRenderTargetDeviceHandle(targetHandle, scene.getSceneId()); + expectActivateRenderTarget(renderTargetDeviceHandle); + expectClearRenderTarget(); + // no discard due to blit using depth as source next + + // pass2 blit + EXPECT_CALL(device, blitRenderTargets(_, _, _, _, _)).InSequence(deviceSequence); + + // pass3 + expectActivateRenderTarget(renderTargetDeviceHandle, false); + expectClearRenderTarget(); + expectDepthStencilDiscard(); + + executeScene(); + } + + TEST_F(ARenderExecutorTimeMs, SetsActiveShaderAnimationFlagOnRenderedScene) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(true, false), createRenderGroup(pass)); + scene.setRenderPassClearFlag(pass, EClearFlag::None); + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + updateScenes({ renderable }); + + { + InSequence seq; + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable, + glm::identity(), + glm::identity(), + expectedProjectionMatrix, + true, + EExpectedRenderStateChange::All, + 1, + false, + EffectUniformTime::GetMilliseconds({})); + } + + EXPECT_FALSE(scene.hasActiveShaderAnimation()); + executeScene(); + EXPECT_TRUE(scene.hasActiveShaderAnimation()); + } + + TEST_F(ARenderExecutorTimeMs, UniformTimeMsStartsAtZeroAfterEffectTimeSync) + { + const auto projParams = GetDefaultProjectionParams(ECameraProjectionType::Perspective); + const RenderPassHandle pass = createRenderPassWithCamera(projParams); + const RenderableHandle renderable = createTestRenderable(createTestDataInstance(true, false), createRenderGroup(pass)); + scene.setRenderPassClearFlag(pass, EClearFlag::None); + const auto expectedProjectionMatrix = CameraMatrixHelper::ProjectionMatrix(projParams); + + scene.setEffectTimeSync(FlushTime::Clock::now()); + + updateScenes({ renderable }); + + { + InSequence seq; + + expectActivateFramebufferRenderTarget(); + expectClearRenderTarget(); + expectFrameRenderCommands(renderable, + glm::identity(), + glm::identity(), + expectedProjectionMatrix, + true, + EExpectedRenderStateChange::All, + 1, + false, + 0); + } + + executeScene(); + } +} diff --git a/renderer/RendererLib/RendererLib/test/RendererCachedSceneTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererCachedSceneTest.cpp similarity index 99% rename from renderer/RendererLib/RendererLib/test/RendererCachedSceneTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererCachedSceneTest.cpp index 05983e5c4..616e20b0e 100644 --- a/renderer/RendererLib/RendererLib/test/RendererCachedSceneTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererCachedSceneTest.cpp @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" #include "TestSceneHelper.h" -#include "RendererLib/RendererCachedScene.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" -namespace ramses_internal +namespace ramses::internal { class ARendererCachedScene : public testing::Test { diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandBufferTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandBufferTest.cpp new file mode 100644 index 000000000..1f8411ab5 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandBufferTest.cpp @@ -0,0 +1,325 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "RendererCommandVisitorMock.h" +#include "internal/Core/Utils/ThreadBarrier.h" +#include +#include + +namespace ramses::internal { + using namespace testing; + + class ARendererCommandBuffer : public ::testing::Test + { + protected: + void fillBufferWithCommands(RendererCommandBuffer& buffer); + void expectFilledBufferVisits(); + + StrictMock visitor{}; + + const SceneId sceneId{ 12u }; + const SceneInfo sceneInfo{ sceneId, "testScene" }; + const DisplayHandle displayHandle{ 1u }; + const DisplayConfig displayConfig{}; + const OffscreenBufferHandle obHandle{ 6u }; + const SceneId providerSceneId{}; + const SceneId consumerSceneId{}; + const DataSlotId providerId{ 1 }; + const DataSlotId consumerId{ 2 }; + const glm::vec4 clearColor{ 1.f, 0.f, 0.5f, 0.2f }; + const StreamBufferHandle streamBuffer{ 7u }; + const WaylandIviSurfaceId source{ 8u }; + const WaylandIviLayerId layer{ 9u }; + IBinaryShaderCache* dummyBinaryShaderCache = reinterpret_cast(std::uintptr_t{ 0x1 }); + }; + + void ARendererCommandBuffer::fillBufferWithCommands(RendererCommandBuffer& buffer) + { + buffer.enqueueCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode::LocalAndRemote }); + buffer.enqueueCommand(RendererCommand::SceneUnpublished{ sceneId }); + buffer.enqueueCommand(RendererCommand::ReceiveScene{ sceneInfo }); + buffer.enqueueCommand(RendererCommand::SetSceneState{ sceneId, RendererSceneState::Rendered }); + buffer.enqueueCommand(RendererCommand::SetSceneMapping{ sceneId, displayHandle }); + buffer.enqueueCommand(RendererCommand::SetSceneDisplayBufferAssignment{ sceneId, obHandle, -13 }); + buffer.enqueueCommand(RendererCommand::CreateDisplay{ displayHandle, displayConfig, dummyBinaryShaderCache }); + buffer.enqueueCommand(RendererCommand::DestroyDisplay{ displayHandle }); + buffer.enqueueCommand(RendererCommand::LinkData{ providerSceneId, providerId, consumerSceneId, consumerId }); + buffer.enqueueCommand(RendererCommand::UnlinkData{ consumerSceneId, consumerId }); + buffer.enqueueCommand(RendererCommand::SetClearFlags{ displayHandle, obHandle, EClearFlag::Color }); + buffer.enqueueCommand(RendererCommand::SetClearColor{ displayHandle, obHandle, clearColor }); + buffer.enqueueCommand(RendererCommand::SetExterallyOwnedWindowSize{ displayHandle, 1u, 2u}); + buffer.enqueueCommand(RendererCommand::CreateStreamBuffer{ displayHandle, streamBuffer, source }); + buffer.enqueueCommand(RendererCommand::DestroyStreamBuffer{ displayHandle, streamBuffer }); + buffer.enqueueCommand(RendererCommand::LinkStreamBuffer{ streamBuffer, consumerSceneId, consumerId }); + buffer.enqueueCommand(RendererCommand::SCListIviSurfaces{}); + buffer.enqueueCommand(RendererCommand::SCSetIviSurfaceVisibility{ source, true }); + buffer.enqueueCommand(RendererCommand::SCSetIviSurfaceOpacity{ source, 0.5f }); + buffer.enqueueCommand(RendererCommand::SCSetIviLayerVisibility{ layer, true }); + buffer.enqueueCommand(RendererCommand::SCAddIviSurfaceToIviLayer{ source, layer }); + buffer.enqueueCommand(RendererCommand::SCRemoveIviSurfaceFromIviLayer{ source, layer }); + buffer.enqueueCommand(RendererCommand::SCDestroyIviSurface{ source }); + } + + void ARendererCommandBuffer::expectFilledBufferVisits() + { + InSequence seq; + EXPECT_CALL(visitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)); + EXPECT_CALL(visitor, handleSceneUnpublished(sceneId)); + EXPECT_CALL(visitor, handleSceneReceived(sceneInfo)); + EXPECT_CALL(visitor, setSceneState(sceneId, RendererSceneState::Rendered)); + EXPECT_CALL(visitor, setSceneMapping(sceneId, displayHandle)); + EXPECT_CALL(visitor, setSceneDisplayBufferAssignment(sceneId, obHandle, -13)); + EXPECT_CALL(visitor, createDisplayContext(displayConfig, displayHandle, dummyBinaryShaderCache)); + EXPECT_CALL(visitor, destroyDisplayContext(displayHandle)); + EXPECT_CALL(visitor, handleSceneDataLinkRequest(providerSceneId, providerId, consumerSceneId, consumerId)); + EXPECT_CALL(visitor, handleDataUnlinkRequest(consumerSceneId, consumerId)); + EXPECT_CALL(visitor, handleSetClearFlags(displayHandle, obHandle, ClearFlags(EClearFlag::Color))); + EXPECT_CALL(visitor, handleSetClearColor(displayHandle, obHandle, clearColor)); + EXPECT_CALL(visitor, handleSetExternallyOwnedWindowSize(displayHandle, 1u, 2u)); + EXPECT_CALL(visitor, handleBufferCreateRequest(streamBuffer, displayHandle, source)); + EXPECT_CALL(visitor, handleBufferDestroyRequest(streamBuffer, displayHandle)); + EXPECT_CALL(visitor, handleBufferToSceneDataLinkRequest(streamBuffer, consumerSceneId, consumerId)); + EXPECT_CALL(visitor, systemCompositorListIviSurfaces()); + EXPECT_CALL(visitor, systemCompositorSetIviSurfaceVisibility(source, true)); + EXPECT_CALL(visitor, systemCompositorSetIviSurfaceOpacity(source, 0.5f)); + EXPECT_CALL(visitor, systemCompositorSetIviLayerVisibility(layer, true)); + EXPECT_CALL(visitor, systemCompositorAddIviSurfaceToIviLayer(source, layer)); + EXPECT_CALL(visitor, systemCompositorRemoveIviSurfaceFromIviLayer(source, layer)); + EXPECT_CALL(visitor, systemCompositorDestroyIviSurface(source)); + } + + TEST_F(ARendererCommandBuffer, canEnqueueCommands) + { + RendererCommandBuffer buffer; + fillBufferWithCommands(buffer); + + expectFilledBufferVisits(); + visitor.visit(buffer); + } + + TEST_F(ARendererCommandBuffer, canAddCommandsFromCommandsContainer) + { + RendererCommandBuffer buffer; + fillBufferWithCommands(buffer); + RendererCommands cmds; + buffer.swapCommands(cmds); + + RendererCommandBuffer destinationContainer; + destinationContainer.addAndConsumeCommandsFrom(cmds); + + // expect no visits + visitor.visit(buffer); + + expectFilledBufferVisits(); + visitor.visit(destinationContainer); + } + + TEST_F(ARendererCommandBuffer, canAddCommandsFromTwoContainers) + { + RendererCommandBuffer destinationContainer; + + RendererCommandBuffer buffer; + RendererCommands cmds; + fillBufferWithCommands(buffer); + buffer.swapCommands(cmds); + destinationContainer.addAndConsumeCommandsFrom(cmds); + + // expect no visits on consumed buffer + visitor.visit(buffer); + + fillBufferWithCommands(buffer); + buffer.swapCommands(cmds); + destinationContainer.addAndConsumeCommandsFrom(cmds); + + // expect no visits on consumed buffer + visitor.visit(buffer); + + InSequence seq; + expectFilledBufferVisits(); + expectFilledBufferVisits(); + visitor.visit(destinationContainer); + } + + TEST_F(ARendererCommandBuffer, addCommandsFromVariousContainersToContainerInAnotherThread) + { + // 2 threads providing commands (e.g. scene control and renderer HL cmd queues) + // 1 thread consuming those and adding to destContainer (e.g. dispatcher queue) + // 1 thread consuming destContainer and adding to finalContainer (e.g. executor in displaythread) + + RendererCommandBuffer destContainer; + RendererCommandBuffer finalContainer; + + ThreadBarrier initBarrier{ 4 }; + ThreadBarrier producersDoneBarrier{ 4 }; + ThreadBarrier consumerProducerDoneBarrier{ 2 }; + ThreadBarrier allDoneBarrier{ 4 }; + + std::thread cmdProducer1{ [&]() + { + initBarrier.wait(); + RendererCommandBuffer buffer; + { + buffer.enqueueCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode::LocalAndRemote }); + buffer.enqueueCommand(RendererCommand::SceneUnpublished{ sceneId }); + } + RendererCommands cmds; + buffer.swapCommands(cmds); + destContainer.addAndConsumeCommandsFrom(cmds); + producersDoneBarrier.wait(); + allDoneBarrier.wait(); + } }; + + std::thread cmdProducer2{ [&]() + { + initBarrier.wait(); + RendererCommandBuffer buffer; + { + buffer.enqueueCommand(RendererCommand::ReceiveScene{ sceneInfo }); + buffer.enqueueCommand(RendererCommand::SetSceneState{ sceneId, RendererSceneState::Rendered }); + } + RendererCommands cmds; + buffer.swapCommands(cmds); + destContainer.addAndConsumeCommandsFrom(cmds); + producersDoneBarrier.wait(); + allDoneBarrier.wait(); + } }; + + std::thread cmdConsumerAndProducer{ [&]() + { + initBarrier.wait(); + RendererCommandBuffer buffer; + { + buffer.enqueueCommand(RendererCommand::SetSceneMapping{ sceneId, displayHandle }); + buffer.enqueueCommand(RendererCommand::SetSceneDisplayBufferAssignment{ sceneId, obHandle, -13 }); + } + RendererCommands cmds; + buffer.swapCommands(cmds); + destContainer.addAndConsumeCommandsFrom(cmds); + producersDoneBarrier.wait(); + + destContainer.swapCommands(cmds); + finalContainer.addAndConsumeCommandsFrom(cmds); + { + buffer.enqueueCommand(RendererCommand::CreateDisplay{ displayHandle, displayConfig, dummyBinaryShaderCache }); + buffer.enqueueCommand(RendererCommand::DestroyDisplay{ displayHandle }); + } + buffer.swapCommands(cmds); + finalContainer.addAndConsumeCommandsFrom(cmds); + consumerProducerDoneBarrier.wait(); + allDoneBarrier.wait(); + } }; + + std::thread cmdConsumer{ [&]() + { + initBarrier.wait(); + producersDoneBarrier.wait(); + consumerProducerDoneBarrier.wait(); + + Sequence s1; + Sequence s2; + Sequence s3; + Sequence s4; + EXPECT_CALL(visitor, handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote)).InSequence(s1); + EXPECT_CALL(visitor, handleSceneUnpublished(sceneId)).InSequence(s1); + EXPECT_CALL(visitor, handleSceneReceived(sceneInfo)).InSequence(s2); + EXPECT_CALL(visitor, setSceneState(sceneId, RendererSceneState::Rendered)).InSequence(s2); + EXPECT_CALL(visitor, setSceneMapping(sceneId, displayHandle)).InSequence(s3); + EXPECT_CALL(visitor, setSceneDisplayBufferAssignment(sceneId, obHandle, -13)).InSequence(s3); + EXPECT_CALL(visitor, createDisplayContext(displayConfig, displayHandle, dummyBinaryShaderCache)).InSequence(s4); + EXPECT_CALL(visitor, destroyDisplayContext(displayHandle)).InSequence(s4); + visitor.visit(finalContainer); + + allDoneBarrier.wait(); + } }; + + cmdProducer1.join(); + cmdProducer2.join(); + cmdConsumerAndProducer.join(); + cmdConsumer.join(); + } + + TEST_F(ARendererCommandBuffer, notifiesAnotherThreadAboutNewCommands) + { + RendererCommandBuffer buffer; + + ThreadBarrier initialCommandsBarrier{ 2 }; + ThreadBarrier otherCommandsBarrier{ 2 }; + ThreadBarrier doneBarrier{ 2 }; + + bool producerCanContinue = false; + std::mutex producerCanContinueLock; + std::condition_variable producerCanContinueCvar; + + std::thread cmdProducer{ [&]() + { + // some init cmd + buffer.enqueueCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode::LocalAndRemote }); + initialCommandsBarrier.wait(); + + // add another cmd and wait for listener set + buffer.enqueueCommand(RendererCommand::SceneUnpublished{ sceneId }); + { + std::unique_lock lk{ producerCanContinueLock }; + producerCanContinueCvar.wait(lk, [&] { return producerCanContinue; }); + } + + // this cmd is guaranteed to be pushed after consumer waiting using cvar + buffer.enqueueCommand(RendererCommand::DestroyDisplay{ DisplayHandle{} }); + otherCommandsBarrier.wait(); + + buffer.enqueueCommand(RendererCommand::DestroyDisplay{ DisplayHandle{} }); + doneBarrier.wait(); + } }; + + std::thread cmdListener{ [&]() + { + initialCommandsBarrier.wait(); + + { + std::unique_lock lk{ producerCanContinueLock }; + producerCanContinue = true; + producerCanContinueCvar.notify_one(); + } + + // wait forever (20s) till new command comes and do something with it + RendererCommands cmds; + buffer.blockingSwapCommands(cmds, std::chrono::milliseconds{20000}); + ASSERT_FALSE(cmds.empty()); + + // any other commands will be pushed without notification + otherCommandsBarrier.wait(); + + doneBarrier.wait(); + } }; + + cmdProducer.join(); + cmdListener.join(); + } + + TEST_F(ARendererCommandBuffer, canInterruptBlockingSwapCommands) + { + RendererCommandBuffer buffer; + ThreadBarrier initialCommandsBarrier{ 2 }; + + std::thread unblocker{ [&]() + { + initialCommandsBarrier.wait(); + buffer.interruptBlockingSwapCommands(); + } }; + + initialCommandsBarrier.wait(); + RendererCommands cmds; + buffer.blockingSwapCommands(cmds, std::chrono::milliseconds{1000000000}); // wait forever + + EXPECT_TRUE(cmds.empty()); + + unblocker.join(); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandExecutorTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandExecutorTest.cpp new file mode 100644 index 000000000..b6b30d30f --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererCommandExecutorTest.cpp @@ -0,0 +1,497 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "ResourceDeviceHandleAccessorMock.h" +#include "RendererMock.h" +#include "DisplayControllerMock.h" +#include "RenderBackendMock.h" +#include "internal/RendererLib/RendererCommandExecutor.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/RendererResourceManager.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "ComponentMocks.h" +#include "RendererSceneUpdaterFacade.h" +#include "RendererSceneControlLogicMock.h" +#include "SceneReferenceLogicMock.h" +#include "SystemCompositorControllerMock.h" +#include "EmbeddedCompositingManagerMock.h" +#include "RendererSceneEventSenderMock.h" +#include "RendererSceneUpdaterMock.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal { + + class ARendererCommandExecutor : public ::testing::Test + { + public: + ARendererCommandExecutor() + : m_rendererScenes(m_rendererEventCollector) + , m_expirationMonitor(m_rendererScenes, m_rendererEventCollector, m_rendererStatistics) + , m_renderer(m_displayHandle, m_rendererScenes, m_rendererEventCollector, m_expirationMonitor, m_rendererStatistics) + , m_sceneStateExecutor(m_renderer, m_sceneEventSender, m_rendererEventCollector) + , m_commandExecutor(m_renderer, m_commandBuffer, m_sceneUpdater, m_sceneControlLogic, m_rendererEventCollector, m_frameTimer) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + // enable SC + ON_CALL(m_renderer.m_platform, getSystemCompositorController()).WillByDefault(Return(&m_renderer.m_platform.systemCompositorControllerMock)); + } + + void doCommandExecutorLoop() + { + m_renderer.getProfilerStatistics().markFrameFinished(std::chrono::microseconds{ 0u }); + m_commandExecutor.executePendingCommands(); + } + + void addDisplayController() + { + ASSERT_FALSE(m_renderer.hasDisplayController()); + m_renderer.createDisplayContext({}); + } + + void removeDisplayController() + { + ASSERT_TRUE(m_renderer.hasDisplayController()); + EXPECT_CALL(m_renderer.m_platform.renderBackendMock.windowMock, getWaylandIviSurfaceID()); + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, destroySurface(_)); + EXPECT_CALL(m_renderer.m_platform, destroyRenderBackend()); + m_renderer.destroyDisplayContext(); + } + + StrictMock& getDisplayControllerMock() + { + return *m_renderer.m_displayController; + } + + RendererEventVector consumeRendererEvents() + { + RendererEventVector events; + RendererEventVector dummy; + m_rendererEventCollector.appendAndConsumePendingEvents(events, dummy); + return events; + } + + RendererEventVector consumeSceneControlEvents() + { + RendererEventVector events; + RendererEventVector dummy; + m_rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + return events; + } + + protected: + DisplayHandle m_displayHandle{ 1u }; + RendererEventCollector m_rendererEventCollector; + RendererScenes m_rendererScenes; + SceneExpirationMonitor m_expirationMonitor; + StrictMock m_sceneRefLogic; + RendererStatistics m_rendererStatistics; + StrictMock m_renderer; + StrictMock m_sceneEventSender; + RendererCommandBuffer m_commandBuffer; + SceneStateExecutor m_sceneStateExecutor; + FrameTimer m_frameTimer; + StrictMock m_sceneUpdater; + StrictMock m_sceneControlLogic; + RendererCommandExecutor m_commandExecutor; + }; + + TEST_F(ARendererCommandExecutor, setSceneState) + { + constexpr SceneId sceneId{ 123 }; + m_commandBuffer.enqueueCommand(RendererCommand::SetSceneState{ sceneId, RendererSceneState::Ready }); + + EXPECT_CALL(m_sceneControlLogic, setSceneState(sceneId, RendererSceneState::Ready)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setSceneMappingIsNoop) + { + constexpr SceneId sceneId{ 123 }; + constexpr DisplayHandle display{ 2 }; + m_commandBuffer.enqueueCommand(RendererCommand::SetSceneMapping{ sceneId, display }); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setSceneDisplayBufferAssignment) + { + constexpr SceneId sceneId{ 123 }; + constexpr OffscreenBufferHandle ob{ 2 }; + m_commandBuffer.enqueueCommand(RendererCommand::SetSceneDisplayBufferAssignment{ sceneId, ob, -13 }); + + EXPECT_CALL(m_sceneControlLogic, setSceneDisplayBufferAssignment(sceneId, ob, -13)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, readPixelsFromDisplayBuffer) + { + constexpr DisplayHandle display{ 1u }; + constexpr OffscreenBufferHandle buffer{ 2u }; + + m_commandBuffer.enqueueCommand(RendererCommand::ReadPixels{ display, buffer, 1, 2, 3, 4, true, true, "file" }); + EXPECT_CALL(m_sceneUpdater, handleReadPixels(buffer, _)).WillOnce(Invoke([&](auto /*unused*/, auto&& info) + { + EXPECT_EQ(1u, info.rectangle.x); + EXPECT_EQ(2u, info.rectangle.y); + EXPECT_EQ(3u, info.rectangle.width); + EXPECT_EQ(4u, info.rectangle.height); + EXPECT_EQ(std::string("file"), info.filename); + EXPECT_TRUE(info.fullScreen); + EXPECT_TRUE(info.sendViaDLT); + })); + doCommandExecutorLoop(); + + EXPECT_TRUE(consumeRendererEvents().empty()); // events are only added by the WindowedRenderer + } + + TEST_F(ARendererCommandExecutor, createOffscreenBuffer) + { + constexpr DisplayHandle display{ 1 }; + constexpr OffscreenBufferHandle buffer{ 2 }; + + m_commandBuffer.enqueueCommand(RendererCommand::CreateOffscreenBuffer{ display, buffer, 1u, 2u, 3u, true, EDepthBufferType::DepthStencil }); + EXPECT_CALL(m_sceneUpdater, handleBufferCreateRequest(buffer, 1u, 2u, 3u, true, EDepthBufferType::DepthStencil)).WillOnce(Return(true)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, createDmaOffscreenBuffer) + { + constexpr DisplayHandle display{ 1 }; + constexpr OffscreenBufferHandle buffer{ 2 }; + constexpr DmaBufferFourccFormat fourccFormat{ 555u }; + constexpr DmaBufferUsageFlags usageFlags{ 666u }; + constexpr DmaBufferModifiers modifiers{ 777u }; + + m_commandBuffer.enqueueCommand(RendererCommand::CreateDmaOffscreenBuffer{ display, buffer, 1u, 2u, fourccFormat, usageFlags, modifiers }); + EXPECT_CALL(m_sceneUpdater, handleDmaBufferCreateRequest(buffer, 1u, 2u, fourccFormat, usageFlags, modifiers)).WillOnce(Return(true)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, destroysOffscreenBufferAndGenerateEvent) + { + constexpr DisplayHandle display{ 1 }; + constexpr OffscreenBufferHandle buffer{ 2 }; + + m_commandBuffer.enqueueCommand(RendererCommand::DestroyOffscreenBuffer{ display, buffer }); + EXPECT_CALL(m_sceneUpdater, handleBufferDestroyRequest(buffer)).WillOnce(Return(true)); + doCommandExecutorLoop(); + + const RendererEventVector events = consumeRendererEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(ERendererEventType::OffscreenBufferDestroyed, events.front().eventType); + EXPECT_EQ(display, events.front().displayHandle); + EXPECT_EQ(buffer, events.front().offscreenBuffer); + EXPECT_EQ(-1, events.front().dmaBufferFD); + EXPECT_EQ(0u, events.front().dmaBufferStride); + } + + TEST_F(ARendererCommandExecutor, destroysOffscreenBufferAndGenerateEventOnFail) + { + constexpr DisplayHandle display{ 1 }; + constexpr OffscreenBufferHandle buffer{ 2 }; + + m_commandBuffer.enqueueCommand(RendererCommand::DestroyOffscreenBuffer{ display, buffer }); + EXPECT_CALL(m_sceneUpdater, handleBufferDestroyRequest(buffer)).WillOnce(Return(false)); + doCommandExecutorLoop(); + + const RendererEventVector events = consumeRendererEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(ERendererEventType::OffscreenBufferDestroyFailed, events.front().eventType); + EXPECT_EQ(display, events.front().displayHandle); + EXPECT_EQ(buffer, events.front().offscreenBuffer); + } + + TEST_F(ARendererCommandExecutor, triggersLinkOffscreenBufferToConsumer) + { + const OffscreenBufferHandle buffer(1u); + const SceneId consumerScene(2u); + const DataSlotId consumerData(3u); + m_commandBuffer.enqueueCommand(RendererCommand::LinkOffscreenBuffer{ buffer, consumerScene, consumerData }); + + EXPECT_CALL(m_sceneUpdater, handleBufferToSceneDataLinkRequest(buffer, consumerScene, consumerData)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, createStreamBuffer) + { + constexpr StreamBufferHandle buffer{ 123u }; + constexpr DisplayHandle display{ 1 }; + constexpr WaylandIviSurfaceId source{ 2 }; + + m_commandBuffer.enqueueCommand(RendererCommand::CreateStreamBuffer{ display, buffer, source }); + EXPECT_CALL(m_sceneUpdater, handleBufferCreateRequest(buffer, source)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, destroysStreamBuffer) + { + constexpr StreamBufferHandle buffer{ 123u }; + constexpr DisplayHandle display{ 1 }; + + m_commandBuffer.enqueueCommand(RendererCommand::DestroyStreamBuffer{ display, buffer }); + EXPECT_CALL(m_sceneUpdater, handleBufferDestroyRequest(buffer)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, linkStreamBufferToConsumer) + { + constexpr StreamBufferHandle buffer{ 1u }; + constexpr SceneId consumerScene{ 2u }; + constexpr DataSlotId consumerData{ 3u }; + m_commandBuffer.enqueueCommand(RendererCommand::LinkStreamBuffer{ buffer, consumerScene, consumerData }); + + EXPECT_CALL(m_sceneUpdater, handleBufferToSceneDataLinkRequest(buffer, consumerScene, consumerData)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, createExternalBuffer) + { + constexpr ExternalBufferHandle buffer{ 123u }; + constexpr DisplayHandle display{ 1 }; + + m_commandBuffer.enqueueCommand(RendererCommand::CreateExternalBuffer{ display, buffer }); + EXPECT_CALL(m_sceneUpdater, handleExternalBufferCreateRequest(buffer)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, destroysExternalBuffer) + { + constexpr ExternalBufferHandle buffer{ 123u }; + constexpr DisplayHandle display{ 1 }; + + m_commandBuffer.enqueueCommand(RendererCommand::DestroyExternalBuffer{ display, buffer }); + EXPECT_CALL(m_sceneUpdater, handleExternalBufferDestroyRequest(buffer)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, linkExternalBufferToConsumer) + { + constexpr ExternalBufferHandle buffer{ 1u }; + constexpr SceneId consumerScene{ 2u }; + constexpr DataSlotId consumerData{ 3u }; + m_commandBuffer.enqueueCommand(RendererCommand::LinkExternalBuffer{ buffer, consumerScene, consumerData }); + + EXPECT_CALL(m_sceneUpdater, handleBufferToSceneDataLinkRequest(buffer, consumerScene, consumerData)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, createsAndDestroysDisplay) + { + const DisplayHandle displayHandle(33u); + const DisplayConfig displayConfig; + + m_commandBuffer.enqueueCommand(RendererCommand::CreateDisplay{ displayHandle, displayConfig, nullptr }); + EXPECT_CALL(m_sceneUpdater, createDisplayContext(displayConfig, nullptr)); + doCommandExecutorLoop(); + + m_commandBuffer.enqueueCommand(RendererCommand::DestroyDisplay{ displayHandle }); + EXPECT_CALL(m_sceneUpdater, destroyDisplayContext()); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, linkData) + { + constexpr SceneId sceneProviderId(1u); + constexpr SceneId sceneConsumerId(2u); + constexpr DataSlotId providerSlotId(3u); + constexpr DataSlotId consumerSlotId(4u); + + m_commandBuffer.enqueueCommand(RendererCommand::LinkData{ sceneProviderId, providerSlotId, sceneConsumerId, consumerSlotId }); + EXPECT_CALL(m_sceneUpdater, handleSceneDataLinkRequest(sceneProviderId, providerSlotId, sceneConsumerId, consumerSlotId)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, unlinkData) + { + constexpr SceneId sceneConsumerId(2u); + constexpr DataSlotId consumerSlotId(4u); + + m_commandBuffer.enqueueCommand(RendererCommand::UnlinkData{ sceneConsumerId, consumerSlotId }); + EXPECT_CALL(m_sceneUpdater, handleDataUnlinkRequest(sceneConsumerId, consumerSlotId)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, processesSceneActions) + { + const SceneId sceneId(7u); + const NodeHandle node(33u); + + SceneUpdate actions; + SceneActionCollectionCreator creator(actions.actions); + creator.allocateNode(0u, node); + actions.flushInfos.flushCounter = 1u; + actions.flushInfos.containsValidInformation = true; + m_commandBuffer.enqueueCommand(RendererCommand::UpdateScene{ sceneId, std::move(actions) }); + + EXPECT_CALL(m_sceneUpdater, handleSceneUpdate(sceneId, _)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setsLayerVisibility) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviLayerVisibility{ WaylandIviLayerId(23u), true }); + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviLayerVisibility{ WaylandIviLayerId(25u), false }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(23u),true)); + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(25u),false)); + + m_commandExecutor.executePendingCommands(); + } + + TEST_F(ARendererCommandExecutor, setsSurfaceVisibility) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceVisibility{ WaylandIviSurfaceId(11u), true }); + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceVisibility{ WaylandIviSurfaceId(22u), false }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(11u),true)); + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(22u),false)); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, listsIviSurfaces) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCListIviSurfaces{}); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, listIVISurfaces()); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setsSurfaceOpacity) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceOpacity{ WaylandIviSurfaceId(1u), 1.f }); + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceOpacity{ WaylandIviSurfaceId(2u), 0.2f }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceOpacity(WaylandIviSurfaceId(1u),1.f)); + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceOpacity(WaylandIviSurfaceId(2u),0.2f)); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setsSurfaceRectangle) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceDestRectangle{ WaylandIviSurfaceId(111u), 0, 0, 640, 480 }); + m_commandBuffer.enqueueCommand(RendererCommand::SCSetIviSurfaceDestRectangle{ WaylandIviSurfaceId(222u), -1,-10, 1920, 1080 }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceDestinationRectangle(WaylandIviSurfaceId(111u), 0, 0, 640, 480)); + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, setSurfaceDestinationRectangle(WaylandIviSurfaceId(222u), -1,-10, 1920, 1080)); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, callsSystemCompositorAddSurfaceToLayer) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCAddIviSurfaceToIviLayer{ WaylandIviSurfaceId(222u), WaylandIviLayerId(111u) }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, addSurfaceToLayer(WaylandIviSurfaceId(222u), WaylandIviLayerId(111u))); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, callsSystemCompositorRemoveSurfaceFromLayer) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCRemoveIviSurfaceFromIviLayer{ WaylandIviSurfaceId(345u), WaylandIviLayerId(123u) }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, removeSurfaceFromLayer(WaylandIviSurfaceId(345u), WaylandIviLayerId(123u))); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, callsSystemCompositorDestroySurface) + { + m_commandBuffer.enqueueCommand(RendererCommand::SCDestroyIviSurface{ WaylandIviSurfaceId(51u) }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, destroySurface(WaylandIviSurfaceId(51u))); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, callsSystemCompositorScreenshot) + { + const std::string firstName("somefilename.png"); + const std::string otherName("somethingelse.png"); + + m_commandBuffer.enqueueCommand(RendererCommand::SCScreenshot{ 1, firstName }); + m_commandBuffer.enqueueCommand(RendererCommand::SCScreenshot{ -1, otherName }); + + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, doScreenshot(std::string_view{firstName}, 1)); + EXPECT_CALL(m_renderer.m_platform.systemCompositorControllerMock, doScreenshot(std::string_view{otherName}, -1)); + + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setClearFlags) + { + constexpr DisplayHandle display{ 1 }; + constexpr OffscreenBufferHandle buffer{ 2 }; + + m_commandBuffer.enqueueCommand(RendererCommand::SetClearFlags{ display, buffer, EClearFlag::Color }); + EXPECT_CALL(m_sceneUpdater, handleSetClearFlags(buffer, ClearFlags(EClearFlag::Color))); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setClearColor) + { + constexpr DisplayHandle display{ 1 }; + constexpr OffscreenBufferHandle buffer{ 2 }; + constexpr glm::vec4 clearColor(1.f, 0.f, 0.2f, 0.3f); + + m_commandBuffer.enqueueCommand(RendererCommand::SetClearColor{ display, buffer, clearColor }); + EXPECT_CALL(m_sceneUpdater, handleSetClearColor(buffer, clearColor)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, resizeDisplayWindowExterally) + { + constexpr DisplayHandle display{ 1 }; + + m_commandBuffer.enqueueCommand(RendererCommand::SetExterallyOwnedWindowSize{ display, 1u, 2u }); + EXPECT_CALL(m_sceneUpdater, handleSetExternallyOwnedWindowSize(1u, 2u)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, setFrameTimerLimits) + { + //default values + ASSERT_EQ(PlatformTime::InfiniteDuration, m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::SceneResourcesUpload)); + ASSERT_EQ(PlatformTime::InfiniteDuration, m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::ResourcesUpload)); + ASSERT_EQ(PlatformTime::InfiniteDuration, m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::OffscreenBufferRender)); + + m_commandBuffer.enqueueCommand(RendererCommand::SetLimits_FrameBudgets{ 4u, 1u, 3u }); + doCommandExecutorLoop(); + + EXPECT_EQ(std::chrono::microseconds(4u), m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::SceneResourcesUpload)); + EXPECT_EQ(std::chrono::microseconds(1u), m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::ResourcesUpload)); + EXPECT_EQ(std::chrono::microseconds(3u), m_frameTimer.getTimeBudgetForSection(EFrameTimerSectionBudget::OffscreenBufferRender)); + } + + TEST_F(ARendererCommandExecutor, forwardsPickEventToSceneUpdater) + { + const SceneId sceneId{ 123u }; + const glm::vec2 coords{ 0.1f, 0.2f }; + m_commandBuffer.enqueueCommand(RendererCommand::PickEvent{ sceneId, coords }); + EXPECT_CALL(m_sceneUpdater, handlePickEvent(sceneId, coords)); + doCommandExecutorLoop(); + } + + TEST_F(ARendererCommandExecutor, triggersLogRinfo) + { + m_commandBuffer.enqueueCommand(RendererCommand::LogInfo{ ERendererLogTopic::Displays, true, NodeHandle{ 6u } }); + EXPECT_CALL(m_sceneUpdater, logRendererInfo(ERendererLogTopic::Displays, true, NodeHandle{ 6u })); + doCommandExecutorLoop(); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererConfigTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererConfigTest.cpp new file mode 100644 index 000000000..6f96e19a5 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererConfigTest.cpp @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2015 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RendererConfig.h" + +namespace ramses::internal +{ + TEST(AInternalRendererConfig, hasDefaultValues) + { + ramses::internal::RendererConfig config; + EXPECT_FALSE(config.getSystemCompositorControlEnabled()); + EXPECT_EQ(std::chrono::microseconds{10000u}, config.getFrameCallbackMaxPollTime()); + EXPECT_EQ("", config.getWaylandDisplayForSystemCompositorController()); + } + + TEST(AInternalRendererConfig, canEnableSystemCompositorControl) + { + ramses::internal::RendererConfig config; + config.enableSystemCompositorControl(); + EXPECT_TRUE(config.getSystemCompositorControlEnabled()); + } + + TEST(AInternalRendererConfig, canSetGetMaxFramecallbackPollTime) + { + ramses::internal::RendererConfig config; + + config.setFrameCallbackMaxPollTime(std::chrono::microseconds{123u}); + EXPECT_EQ(std::chrono::microseconds{123u}, config.getFrameCallbackMaxPollTime()); + } + + TEST(AInternalRendererConfig, canSetGetWaylandDisplayForSystemCompositorController) + { + ramses::internal::RendererConfig config; + + config.setWaylandDisplayForSystemCompositorController("ramses wd"); + EXPECT_EQ("ramses wd", config.getWaylandDisplayForSystemCompositorController()); + } +} diff --git a/renderer/RendererLib/RendererLib/test/RendererEventCollectorTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererEventCollectorTest.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/test/RendererEventCollectorTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererEventCollectorTest.cpp index 8176788ed..68af761af 100644 --- a/renderer/RendererLib/RendererLib/test/RendererEventCollectorTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererEventCollectorTest.cpp @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" #include "gtest/gtest.h" -#include "RendererEventCollector.h" -#include "RendererLib/EKeyModifier.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ARendererEventCollector : public testing::Test { @@ -67,7 +66,7 @@ namespace ramses_internal TEST_F(ARendererEventCollector, CanAddRendererEventWithPixelData) { - UInt8Vector pixelData; + std::vector pixelData; pixelData.push_back(8); const OffscreenBufferHandle bufferHandle{ 123u }; @@ -203,9 +202,9 @@ namespace ramses_internal { const DisplayHandle displayHandle(125u); KeyEvent keyEvent; - keyEvent.modifier = EKeyModifier_Ctrl | EKeyModifier_Alt; + keyEvent.modifier = EKeyModifier::Ctrl | EKeyModifier::Alt; keyEvent.keyCode = EKeyCode_K; - keyEvent.type = EKeyEventType_Pressed; + keyEvent.type = EKeyEvent::Pressed; m_rendererEventCollector.addWindowEvent(ERendererEventType::WindowKeyEvent, displayHandle, keyEvent); const RendererEventVector resultEvents = consumeRendererEvents(); @@ -222,7 +221,7 @@ namespace ramses_internal const DisplayHandle displayHandle(124u); MouseEvent mouseEvent; mouseEvent.pos = glm::ivec2(50u, 60u); - mouseEvent.type = EMouseEventType_LeftButtonDown; + mouseEvent.type = EMouseEvent::LeftButtonDown; m_rendererEventCollector.addWindowEvent(ERendererEventType::WindowMouseEvent, displayHandle, mouseEvent); const RendererEventVector resultEvents = consumeRendererEvents(); diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererLogContextTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererLogContextTest.cpp new file mode 100644 index 000000000..35d27e6e3 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererLogContextTest.cpp @@ -0,0 +1,86 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RendererLogContext.h" + +namespace ramses::internal +{ + class ARendererLogContext : public ::testing::Test + { + public: + ARendererLogContext() + : contextError (ERendererLogLevelFlag_Error) + , contextWarn (ERendererLogLevelFlag_Warn) + , contextInfo (ERendererLogLevelFlag_Info) + , contextDetails(ERendererLogLevelFlag_Details) + { + } + + ~ARendererLogContext() override = default; + + RendererLogContext contextError; + RendererLogContext contextWarn; + RendererLogContext contextInfo; + RendererLogContext contextDetails; + }; + + TEST_F(ARendererLogContext, activatesLogLevelFlagsErrorCorrectly) + { + EXPECT_TRUE (contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); + EXPECT_FALSE(contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); + EXPECT_FALSE(contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); + EXPECT_FALSE(contextError.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); + } + TEST_F(ARendererLogContext, activatesLogLevelFlagsWarnCorrectly) + { + EXPECT_TRUE (contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); + EXPECT_TRUE (contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); + EXPECT_FALSE(contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); + EXPECT_FALSE(contextWarn.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); + } + + TEST_F(ARendererLogContext, activatesLogLevelFlagsInfoCorrectly) + { + EXPECT_TRUE (contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); + EXPECT_TRUE (contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); + EXPECT_TRUE (contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); + EXPECT_FALSE(contextInfo.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); + } + + TEST_F(ARendererLogContext, activatesLogLevelFlagsDetailsCorrectly) + { + EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Error)); + EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Warn)); + EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Info)); + EXPECT_TRUE(contextDetails.isLogLevelFlagEnabled(ERendererLogLevelFlag_Details)); + } + + TEST_F(ARendererLogContext, matchesAllNodesByDefault) + { + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(0u))); + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(123u))); + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle::Invalid())); + } + + TEST_F(ARendererLogContext, matchesExactHandle) + { + contextInfo.setNodeHandleFilter(NodeHandle(123u)); + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(123u))); + EXPECT_FALSE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(0u))); + EXPECT_FALSE(contextInfo.isMatchingNodeHandeFilter(NodeHandle::Invalid())); + } + + TEST_F(ARendererLogContext, matchesAllNodesWithWildcard) + { + contextInfo.setNodeHandleFilter(NodeHandle::Invalid()); + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(0u))); + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle(123u))); + EXPECT_TRUE(contextInfo.isMatchingNodeHandeFilter(NodeHandle::Invalid())); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.cpp new file mode 100644 index 000000000..b7fdca0b7 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.cpp @@ -0,0 +1,92 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererMock.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/PlatformInterface/IDisplayController.h" +#include "internal/RendererLib/PlatformInterface/ISystemCompositorController.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/DisplayConfig.h" + +namespace ramses::internal { + using namespace testing; + + const FrameTimer RendererMock::FrameTimerInstance; + + RendererMock::RendererMock(DisplayHandle display, const IPlatform& platform, const RendererScenes& rendererScenes, + const RendererEventCollector& eventCollector, const SceneExpirationMonitor& expirationMonitor, const RendererStatistics& statistics) + : Renderer(display, const_cast(platform), const_cast(rendererScenes), + const_cast(eventCollector), FrameTimerInstance, const_cast(expirationMonitor), const_cast(statistics)) + { + // by default do not track modified scenes, concrete tests will override + EXPECT_CALL(*this, markBufferWithSceneForRerender(_)).Times(AnyNumber()); + // by default do not track set clear color, concrete tests will override + EXPECT_CALL(*this, setClearColor(_, _)).Times(AnyNumber()); + } + + RendererMock::~RendererMock() = default; + + template class MOCK_TYPE> + RendererMockWithMockDisplay::RendererMockWithMockDisplay(DisplayHandle display, const RendererScenes& rendererScenes, + const RendererEventCollector& eventCollector, const SceneExpirationMonitor& expirationMonitor, const RendererStatistics& statistics) + : RendererMock(display, m_platform, const_cast(rendererScenes), + const_cast(eventCollector), const_cast(expirationMonitor), const_cast(statistics)) + { + } + + template class MOCK_TYPE> + RendererMockWithMockDisplay::~RendererMockWithMockDisplay() = default; + + template class MOCK_TYPE> + IDisplayController* RendererMockWithMockDisplay::createDisplayControllerFromConfig(const DisplayConfig& displayConfig) + { + assert(m_displayController == nullptr); + m_displayController = new MOCK_TYPE < DisplayControllerMock >; + + ON_CALL(*m_displayController, getRenderBackend()).WillByDefault(ReturnRef(m_platform.renderBackendMock)); + ON_CALL(*m_displayController, getEmbeddedCompositingManager()).WillByDefault(ReturnRef(m_embeddedCompositingManager)); + + EXPECT_CALL(*m_displayController, getDisplayWidth()).Times(AnyNumber()).WillRepeatedly(Return(displayConfig.getDesiredWindowWidth())); + EXPECT_CALL(*m_displayController, getDisplayHeight()).Times(AnyNumber()).WillRepeatedly(Return(displayConfig.getDesiredWindowHeight())); + EXPECT_CALL(*m_displayController, getDisplayBuffer()).Times(AnyNumber()); + EXPECT_CALL(*m_displayController, getRenderBackend()).Times(AnyNumber()); + EXPECT_CALL(*m_displayController, getEmbeddedCompositingManager()).Times(AnyNumber()); + + EXPECT_CALL(m_platform.renderBackendMock.contextMock, disable()).Times(AtMost(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(m_platform.renderBackendMock.contextMock, enable()).Times(AtMost(1)).WillRepeatedly(Return(true)); + + EXPECT_CALL(*this, setClearColor(_, displayConfig.getClearColor())); + + return m_displayController; + } + + template class MOCK_TYPE> + void RendererMockWithMockDisplay::markBufferWithSceneForRerender(SceneId sceneId) + { + Renderer::markBufferWithSceneForRerender(sceneId); // NOLINT clang-tidy: We really mean to call into Renderer + RendererMock::markBufferWithSceneForRerender(sceneId); + } + + template class MOCK_TYPE> + void RendererMockWithMockDisplay::setClearFlags(DeviceResourceHandle bufferDeviceHandle, ClearFlags clearFlags) + { + Renderer::setClearFlags(bufferDeviceHandle, clearFlags); // NOLINT clang-tidy: We really mean to call into Renderer + RendererMock::setClearFlags(bufferDeviceHandle, clearFlags); + } + + template class MOCK_TYPE> + void RendererMockWithMockDisplay::setClearColor(DeviceResourceHandle bufferDeviceHandle, const glm::vec4& clearColor) + { + Renderer::setClearColor(bufferDeviceHandle, clearColor); // NOLINT clang-tidy: We really mean to call into Renderer + RendererMock::setClearColor(bufferDeviceHandle, clearColor); + } + + template class RendererMockWithMockDisplay < NiceMock >; + template class RendererMockWithMockDisplay < StrictMock >; +} diff --git a/renderer/RendererLib/RendererTestCommon/RendererMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.h similarity index 78% rename from renderer/RendererLib/RendererTestCommon/RendererMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.h index 407c7937c..1ab9687f6 100644 --- a/renderer/RendererLib/RendererTestCommon/RendererMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererMock.h @@ -6,32 +6,30 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERMOCK_H -#define RAMSES_RENDERERMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" -#include "RendererLib/Renderer.h" -#include "RendererLib/FrameTimer.h" +#include "internal/RendererLib/Renderer.h" +#include "internal/RendererLib/FrameTimer.h" #include "PlatformMock.h" #include "DisplayControllerMock.h" #include "RenderBackendMock.h" #include "EmbeddedCompositingManagerMock.h" -namespace ramses_internal +namespace ramses::internal { class DisplayConfig; class RendererScenes; class SceneExpirationMonitor; -class RendererMock : public ramses_internal::Renderer +class RendererMock : public Renderer { public: - RendererMock(DisplayHandle display, const ramses_internal::IPlatform& platform, const RendererScenes& rendererScenes, + RendererMock(DisplayHandle display, const IPlatform& platform, const RendererScenes& rendererScenes, const RendererEventCollector& eventCollector, const SceneExpirationMonitor& expirationMonitor, const RendererStatistics& statistics); ~RendererMock() override; MOCK_METHOD(void, markBufferWithSceneForRerender, (SceneId), (override)); - MOCK_METHOD(void, setClearFlags, (DeviceResourceHandle, uint32_t), (override)); + MOCK_METHOD(void, setClearFlags, (DeviceResourceHandle, ClearFlags), (override)); MOCK_METHOD(void, setClearColor, (DeviceResourceHandle, const glm::vec4&), (override)); static const FrameTimer FrameTimerInstance; @@ -45,14 +43,12 @@ class RendererMockWithMockDisplay : public RendererMock const RendererEventCollector& eventCollector, const SceneExpirationMonitor& expirationMonitor, const RendererStatistics& statistics); ~RendererMockWithMockDisplay() override; - IDisplayController* createDisplayControllerFromConfig(const ramses_internal::DisplayConfig& displayConfig) override; + IDisplayController* createDisplayControllerFromConfig(const ramses::internal::DisplayConfig& displayConfig) override; void markBufferWithSceneForRerender(SceneId sceneId) override; - void setClearFlags(DeviceResourceHandle bufferDeviceHandle, uint32_t clearFlags) override; + void setClearFlags(DeviceResourceHandle bufferDeviceHandle, ClearFlags clearFlags) override; void setClearColor(DeviceResourceHandle bufferDeviceHandle, const glm::vec4& clearColor) override; - using ramses_internal::Renderer::getDisplaySetup; - MOCK_TYPE< PlatformMock > m_platform; MOCK_TYPE< DisplayControllerMock >* m_displayController = nullptr; MOCK_TYPE< EmbeddedCompositingManagerMock > m_embeddedCompositingManager; @@ -61,4 +57,4 @@ class RendererMockWithMockDisplay : public RendererMock using RendererMockWithNiceMockDisplay = RendererMockWithMockDisplay< ::testing::NiceMock>; using RendererMockWithStrictMockDisplay = RendererMockWithMockDisplay< ::testing::StrictMock>; } -#endif + diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.cpp new file mode 100644 index 000000000..787275ac4 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.cpp @@ -0,0 +1,149 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererResourceManagerMock.h" +#include "DeviceMock.h" +#include "internal/PlatformAbstraction/FmtBase.h" +#include "MockResourceHash.h" + +namespace ramses::internal { + using namespace testing; + + RendererResourceManagerMock::RendererResourceManagerMock() + { + // report common fake resources as uploaded by default + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::EffectHash)).WillByDefault(Return(DeviceMock::FakeShaderDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::VertArrayHash)).WillByDefault(Return(DeviceMock::FakeVertexBufferDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::VertArrayHash2)).WillByDefault(Return(DeviceMock::FakeVertexBufferDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::IndexArrayHash3)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::TextureHash)).WillByDefault(Return(DeviceMock::FakeTextureDeviceHandle)); + ON_CALL(*this, getResourceDeviceHandle(MockResourceHash::TextureHash2)).WillByDefault(Return(DeviceMock::FakeTextureDeviceHandle)); + + ON_CALL(*this, getResourceStatus(MockResourceHash::EffectHash)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::VertArrayHash)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::VertArrayHash2)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::IndexArrayHash)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::IndexArrayHash2)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::IndexArrayHash3)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::TextureHash)).WillByDefault(Return(EResourceStatus::Uploaded)); + ON_CALL(*this, getResourceStatus(MockResourceHash::TextureHash2)).WillByDefault(Return(EResourceStatus::Uploaded)); + + ON_CALL(*this, getResourceType(MockResourceHash::EffectHash)).WillByDefault(Return(EResourceType::Effect)); + ON_CALL(*this, getResourceType(MockResourceHash::VertArrayHash)).WillByDefault(Return(EResourceType::VertexArray)); + ON_CALL(*this, getResourceType(MockResourceHash::VertArrayHash2)).WillByDefault(Return(EResourceType::VertexArray)); + ON_CALL(*this, getResourceType(MockResourceHash::IndexArrayHash)).WillByDefault(Return(EResourceType::IndexArray)); + ON_CALL(*this, getResourceType(MockResourceHash::IndexArrayHash2)).WillByDefault(Return(EResourceType::IndexArray)); + ON_CALL(*this, getResourceType(MockResourceHash::IndexArrayHash3)).WillByDefault(Return(EResourceType::IndexArray)); + ON_CALL(*this, getResourceType(MockResourceHash::TextureHash)).WillByDefault(Return(EResourceType::Texture2D)); + ON_CALL(*this, getResourceType(MockResourceHash::TextureHash2)).WillByDefault(Return(EResourceType::Texture2D)); + + ON_CALL(*this, getRenderTargetDeviceHandle(_, _)).WillByDefault(Return(DeviceMock::FakeRenderTargetDeviceHandle)); + ON_CALL(*this, getRenderTargetBufferDeviceHandle(_, _)).WillByDefault(Return(DeviceMock::FakeRenderBufferDeviceHandle)); + ON_CALL(*this, getOffscreenBufferDeviceHandle(_)).WillByDefault(Return(DeviceMock::FakeRenderTargetDeviceHandle)); + ON_CALL(*this, getOffscreenBufferColorBufferDeviceHandle(_)).WillByDefault(Return(DeviceMock::FakeRenderBufferDeviceHandle)); + ON_CALL(*this, getOffscreenBufferHandle(DeviceResourceHandle::Invalid())).WillByDefault(Return(OffscreenBufferHandle::Invalid())); + ON_CALL(*this, getStreamBufferDeviceHandle(_)).WillByDefault(Return(DeviceMock::FakeRenderTargetDeviceHandle)); + ON_CALL(*this, getVertexArrayDeviceHandle(_, _)).WillByDefault(Return(DeviceMock::FakeVertexArrayDeviceHandle)); + ON_CALL(*this, getExternalBufferDeviceHandle(Ne(ExternalBufferHandle::Invalid()))).WillByDefault(Return(DeviceMock::FakeExternalTextureDeviceHandle)); + ON_CALL(*this, getEmptyExternalBufferDeviceHandle()).WillByDefault(Return(DeviceMock::FakeEmptyExternalTextureDeviceHandle)); + + ON_CALL(*this, getBlitPassRenderTargetsDeviceHandle(_, _, _, _)).WillByDefault(DoAll(SetArgReferee<2>(DeviceMock::FakeBlitPassRenderTargetDeviceHandle), SetArgReferee<3>(DeviceMock::FakeBlitPassRenderTargetDeviceHandle))); + + // no need to strictly test getters + EXPECT_CALL(*this, getResourceDeviceHandle(_)).Times(AnyNumber()); + EXPECT_CALL(*this, getDataBufferDeviceHandle(_, _)).Times(AnyNumber()); + EXPECT_CALL(*this, getVertexArrayDeviceHandle(_, _)).Times(AnyNumber()); + EXPECT_CALL(*this, getTextureBufferDeviceHandle(_, _)).Times(AnyNumber()); + EXPECT_CALL(*this, getRenderTargetDeviceHandle(_, _)).Times(AnyNumber()); + EXPECT_CALL(*this, getRenderTargetBufferDeviceHandle(_, _)).Times(AnyNumber()); + EXPECT_CALL(*this, getBlitPassRenderTargetsDeviceHandle(_, _, _, _)).Times(AnyNumber()); + EXPECT_CALL(*this, getOffscreenBufferColorBufferDeviceHandle(_)).Times(AnyNumber()); + EXPECT_CALL(*this, getStreamBufferDeviceHandle(_)).Times(AnyNumber()); + EXPECT_CALL(*this, getResourcesInUseByScene(_)).Times(AnyNumber()); + EXPECT_CALL(*this, getExternalBufferDeviceHandle(_)).Times(AnyNumber()); + EXPECT_CALL(*this, getEmptyExternalBufferDeviceHandle()).Times(AnyNumber()); + } + + RendererResourceManagerRefCountMock::~RendererResourceManagerRefCountMock() + { + expectNoResourceReferences(); + } + + void RendererResourceManagerRefCountMock::referenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) + { + for (const auto r : resources) + m_refCounts[sceneId][r]++; + RendererResourceManagerMock::referenceResourcesForScene(sceneId, resources); + } + + void RendererResourceManagerRefCountMock::unreferenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) + { + for (const auto r : resources) + { + m_refCounts[sceneId][r]--; + ASSERT_GE(m_refCounts[sceneId][r], 0) << fmt::format("Resource {} from scene {} has {} refs", r, sceneId, m_refCounts[sceneId][r]).c_str(); + } + RendererResourceManagerMock::unreferenceResourcesForScene(sceneId, resources); + } + + void RendererResourceManagerRefCountMock::unreferenceAllResourcesForScene(SceneId sceneId) + { + m_refCounts.erase(sceneId); + RendererResourceManagerMock::unreferenceAllResourcesForScene(sceneId); + } + + const ResourceContentHashVector* RendererResourceManagerRefCountMock::getResourcesInUseByScene(SceneId sceneId) const + { + RendererResourceManagerMock::getResourcesInUseByScene(sceneId); + + const auto it = m_refCounts.find(sceneId); + if (it == m_refCounts.cend()) + return nullptr; + + m_tempUsedResources.clear(); + for (const auto& r : it->second) + { + if (r.second > 0) + m_tempUsedResources.push_back(r.first); + } + + return &m_tempUsedResources; + } + + void RendererResourceManagerRefCountMock::expectNoResourceReferencesForScene(SceneId sceneId) const + { + const auto it = m_refCounts.find(sceneId); + if (it != m_refCounts.cend()) + { + for (const auto& r : it->second) + { + EXPECT_EQ(0, r.second) << fmt::format("Resource {} from scene {} has {} refs", r.first, sceneId, r.second).c_str(); + } + } + } + + void RendererResourceManagerRefCountMock::expectNoResourceReferences() const + { + for (const auto& sr : m_refCounts) + expectNoResourceReferencesForScene(sr.first); + } + + int RendererResourceManagerRefCountMock::getResourceRefCount(ResourceContentHash resource) const + { + return std::accumulate(m_refCounts.cbegin(), m_refCounts.cend(), 0, [&](int refs, const auto& sceneRefsIt) + { + const auto it = sceneRefsIt.second.find(resource); + if (it != sceneRefsIt.second.cend()) + refs += it->second; + return refs; + }); + } + +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.h new file mode 100644 index 000000000..78c487efd --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerMock.h @@ -0,0 +1,97 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/RendererLib/IRendererResourceManager.h" +#include +#include "gmock/gmock.h" + +namespace ramses::internal{ + + class RendererResourceManagerMock : public IRendererResourceManager + { + public: + RendererResourceManagerMock(); + // IResourceDeviceHandleAccessor + MOCK_METHOD(DeviceResourceHandle, getResourceDeviceHandle, (const ResourceContentHash&), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getRenderTargetDeviceHandle, (RenderTargetHandle, SceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getRenderTargetBufferDeviceHandle, (RenderBufferHandle, SceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferDeviceHandle, (OffscreenBufferHandle), (const, override)); + MOCK_METHOD(OffscreenBufferHandle, getOffscreenBufferHandle, (DeviceResourceHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferColorBufferDeviceHandle, (OffscreenBufferHandle), (const, override)); + MOCK_METHOD(int, getDmaOffscreenBufferFD, (OffscreenBufferHandle), (const, override)); + MOCK_METHOD(uint32_t, getDmaOffscreenBufferStride, (OffscreenBufferHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getStreamBufferDeviceHandle, (StreamBufferHandle bufferHandle), (const, override)); + MOCK_METHOD(void, getBlitPassRenderTargetsDeviceHandle, (BlitPassHandle, SceneId, DeviceResourceHandle&, DeviceResourceHandle&), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getDataBufferDeviceHandle, (DataBufferHandle, SceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getTextureBufferDeviceHandle, (TextureBufferHandle, SceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getExternalBufferDeviceHandle, (ExternalBufferHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getEmptyExternalBufferDeviceHandle, (), (const, override)); + MOCK_METHOD(uint32_t, getExternalBufferGlId, (ExternalBufferHandle), (const, override)); + // IRendererResourceManager + MOCK_METHOD(EResourceStatus, getResourceStatus, (const ResourceContentHash& hash), (const, override)); + MOCK_METHOD(EResourceType, getResourceType, (const ResourceContentHash& hash), (const, override)); + MOCK_METHOD(void, referenceResourcesForScene, (SceneId sceneId, const ResourceContentHashVector& resources), (override)); + MOCK_METHOD(void, unreferenceResourcesForScene, (SceneId sceneId, const ResourceContentHashVector& resources), (override)); + MOCK_METHOD(void, unloadAllSceneResourcesForScene, (SceneId sceneId), (override)); + MOCK_METHOD(void, unreferenceAllResourcesForScene, (SceneId sceneId), (override)); + MOCK_METHOD(const ResourceContentHashVector*, getResourcesInUseByScene, (SceneId sceneId), (const, override)); + MOCK_METHOD(void, provideResourceData, (const ManagedResource& mr), (override)); + MOCK_METHOD(bool, hasResourcesToBeUploaded, (), (const, override)); + MOCK_METHOD(void, uploadAndUnloadPendingResources, (), (override)); + MOCK_METHOD(void, uploadRenderTargetBuffer, (RenderBufferHandle renderBufferHandle, SceneId sceneId, const RenderBuffer& renderBuffer), (override)); + MOCK_METHOD(void, unloadRenderTargetBuffer, (RenderBufferHandle renderBufferHandle, SceneId sceneId), (override)); + MOCK_METHOD(void, uploadRenderTarget, (RenderTargetHandle renderTarget, const RenderBufferHandleVector& rtBufferHandles, SceneId sceneId), (override)); + MOCK_METHOD(void, unloadRenderTarget, (RenderTargetHandle renderTarget, SceneId sceneId), (override)); + MOCK_METHOD(void, uploadOffscreenBuffer, (OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType), (override)); + MOCK_METHOD(void, uploadDmaOffscreenBuffer, (OffscreenBufferHandle bufferHandle, uint32_t width, uint32_t height, DmaBufferFourccFormat dmaBufferFourccFormat, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers), (override)); + MOCK_METHOD(void, unloadOffscreenBuffer, (OffscreenBufferHandle bufferHandle), (override)); + MOCK_METHOD(void, uploadStreamBuffer, (StreamBufferHandle bufferHandle, WaylandIviSurfaceId surfaceId), (override)); + MOCK_METHOD(void, unloadStreamBuffer, (StreamBufferHandle bufferHandle), (override)); + MOCK_METHOD(void, uploadBlitPassRenderTargets, (BlitPassHandle, RenderBufferHandle, RenderBufferHandle, SceneId), (override)); + MOCK_METHOD(void, unloadBlitPassRenderTargets, (BlitPassHandle, SceneId), (override)); + MOCK_METHOD(void, uploadDataBuffer, (DataBufferHandle dataBufferHandle, EDataBufferType dataBufferType, EDataType dataType, uint32_t elementCount, SceneId sceneId), (override)); + MOCK_METHOD(void, unloadDataBuffer, (DataBufferHandle dataBufferHandle, SceneId sceneId), (override)); + MOCK_METHOD(void, updateDataBuffer, (DataBufferHandle handle, uint32_t dataSizeInBytes, const std::byte* data, SceneId sceneId), (override)); + + MOCK_METHOD(void, uploadTextureBuffer, (TextureBufferHandle textureBufferHandle, uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, SceneId sceneId), (override)); + MOCK_METHOD(void, unloadTextureBuffer, (TextureBufferHandle textureBufferHandle, SceneId sceneId), (override)); + MOCK_METHOD(void, updateTextureBuffer, (TextureBufferHandle textureBufferHandle, uint32_t mipLevel, const Quad& area, uint32_t stride, const std::byte* data, SceneId sceneId), (override)); + + MOCK_METHOD(void, uploadVertexArray, (RenderableHandle renderableHandle, const VertexArrayInfo& vertexArrayInfo, SceneId sceneId), (override)); + MOCK_METHOD(void, unloadVertexArray, (RenderableHandle renderableHandle, SceneId sceneId), (override)); + MOCK_METHOD(DeviceResourceHandle, getVertexArrayDeviceHandle, (RenderableHandle renderableHandle, SceneId sceneId), (const, override)); + + MOCK_METHOD(void, uploadExternalBuffer, (ExternalBufferHandle), (override)); + MOCK_METHOD(void, unloadExternalBuffer, (ExternalBufferHandle), (override)); + + MOCK_METHOD(const StreamUsage&, getStreamUsage, (WaylandIviSurfaceId source), (const, override)); + }; + + class RendererResourceManagerRefCountMock : public RendererResourceManagerMock + { + public: + ~RendererResourceManagerRefCountMock() override; + + void referenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) override; + void unreferenceResourcesForScene(SceneId sceneId, const ResourceContentHashVector& resources) override; + [[nodiscard]] const ResourceContentHashVector* getResourcesInUseByScene(SceneId sceneId) const override; + void unreferenceAllResourcesForScene(SceneId sceneId) override; + + void expectNoResourceReferencesForScene(SceneId sceneId) const; + void expectNoResourceReferences() const; + [[nodiscard]] int getResourceRefCount(ResourceContentHash resource) const; + + private: + std::unordered_map> m_refCounts; + mutable ResourceContentHashVector m_tempUsedResources; + }; + +} + diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerTest.cpp new file mode 100644 index 000000000..cc0222583 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceManagerTest.cpp @@ -0,0 +1,1380 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RendererResourceManager.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "ResourceDeviceHandleAccessorMock.h" +#include "PlatformMock.h" +#include "EmbeddedCompositingManagerMock.h" +#include "MockResourceHash.h" +#include "ResourceUploaderMock.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal { + using namespace testing; + + class ARendererResourceManager : public ::testing::Test + { + public: + explicit ARendererResourceManager() + : fakeSceneId(66u) + , asyncEffectUploader(platform, platform.renderBackendMock, notifier, 1) + , resourceManager(platform.renderBackendMock, std::unique_ptr{ resUploader }, asyncEffectUploader, embeddedCompositingManager, {}, frameTimer, stats) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + InSequence s; + EXPECT_CALL(platform.renderBackendMock.contextMock, disable()).WillOnce(Return(true)); + EXPECT_CALL(platform, createResourceUploadRenderBackend()); + EXPECT_CALL(platform.renderBackendMock.contextMock, enable()).WillOnce(Return(true)); + const bool status = asyncEffectUploader.createResourceUploadRenderBackendAndStartThread(); + EXPECT_TRUE(status); + } + + ~ARendererResourceManager() override + { + // no actual unload expected but clears internal lists + resourceManager.unloadAllSceneResourcesForScene(fakeSceneId); + + EXPECT_CALL(platform, destroyResourceUploadRenderBackend()); + asyncEffectUploader.destroyResourceUploadRenderBackendAndStopThread(); + } + + void referenceResource(ResourceContentHash hash, SceneId sceneId) + { + const ResourceContentHashVector list{ hash }; + + resourceManager.referenceResourcesForScene(sceneId, list); + EXPECT_TRUE(contains_c(*resourceManager.getResourcesInUseByScene(sceneId), hash)); + } + + void unreferenceResource(ResourceContentHash hash, SceneId sceneId) + { + const ResourceContentHashVector list{ hash }; + + resourceManager.unreferenceResourcesForScene(sceneId, list); + EXPECT_TRUE(!resourceManager.getResourcesInUseByScene(sceneId) || !contains_c(*resourceManager.getResourcesInUseByScene(sceneId), hash)); + } + + void expectStreamUsedBy(WaylandIviSurfaceId source, const std::vector& sbUsage) + { + EXPECT_THAT(sbUsage, ::testing::UnorderedElementsAreArray(resourceManager.getStreamUsage(source))); + } + + void expectResourceUploaded(const ResourceContentHash& hash, EResourceType type, DeviceResourceHandle resultDeviceHandle = ResourceUploaderMock::FakeResourceDeviceHandle) + { + EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([hash, type, resultDeviceHandle](auto& /*unused*/, const auto& rd, auto& /*unused*/) { + EXPECT_EQ(hash, rd.hash); + EXPECT_EQ(type, rd.type); + + return resultDeviceHandle; + })); + } + + void expectResourceUnloaded(const ResourceContentHash& hash, EResourceType type, DeviceResourceHandle deviceHandle = ResourceUploaderMock::FakeResourceDeviceHandle) + { + EXPECT_CALL(*resUploader, unloadResource(Ref(platform.renderBackendMock), type, hash , deviceHandle)); + } + + void expectDeviceFlushOnWindows() + { +#if defined(_WIN32) + EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, flush()); +#endif + } + + void uploadShader(const ResourceContentHash& hash, bool expectSuccess = true) + { + EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([hash](auto& /*unused*/, const auto& rd, auto& /*unused*/) { + EXPECT_EQ(hash, rd.hash); + EXPECT_EQ(EResourceType::Effect, rd.type); + + return std::optional{}; + })); + + if (expectSuccess) + { + EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)); + expectDeviceFlushOnWindows(); + EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); + EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(Ref(platform.renderBackendMock), _, _, _)); + } + else + { + EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillRepeatedly(Invoke([](const auto& /*unused*/) {return std::move(std::unique_ptr{}); })); + expectDeviceFlushOnWindows(); + EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)).Times(0); + EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(Ref(platform.renderBackendMock), _, _, _)).Times(0); + } + + resourceManager.uploadAndUnloadPendingResources(); + ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(hash)); + + constexpr std::chrono::seconds timeoutTime{ 2u }; + const auto startTime = std::chrono::steady_clock::now(); + while (resourceManager.getResourceStatus(hash) == EResourceStatus::ScheduledForUpload + && std::chrono::steady_clock::now() - startTime < timeoutTime) + { + std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); + resourceManager.uploadAndUnloadPendingResources(); + } + } + + protected: + StrictMock platform; + StrictMock additionalRenderer; + StrictMock embeddedCompositingManager; + SceneId fakeSceneId; + RendererStatistics stats; + StrictMock* resUploader = new StrictMock; + FrameTimer frameTimer; + NiceMock notifier; + AsyncEffectUploader asyncEffectUploader; + RendererResourceManager resourceManager; + }; + + TEST_F(ARendererResourceManager, PerformsCorrectlyResourceLifecycle) + { + constexpr ResourceContentHash resource = MockResourceHash::VertArrayHash; + + // register some resource + referenceResource(resource, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); + EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + // provide data + const auto managedRes = MockResourceHash::GetManagedResource(resource); + resourceManager.provideResourceData(managedRes); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(resource)); + EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + // upload the resource + expectResourceUploaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); + EXPECT_TRUE(resourceManager.getResourceDeviceHandle(resource).isValid()); + EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); + + // delete resource + ResourceContentHashVector resources; + resources.push_back(resource); + resourceManager.unreferenceResourcesForScene(fakeSceneId, { resource }); + expectResourceUnloaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + // Make sure the resource was deleted before the resourceManager gets out of scope + // and deletes it automatically + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + } + + TEST_F(ARendererResourceManager, ReferencedResourceHasCorrectStatus) + { + ResourceContentHash resource = MockResourceHash::VertArrayHash; + + referenceResource(resource, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); + EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + unreferenceResource(resource, fakeSceneId); + } + + TEST_F(ARendererResourceManager, ReferencedAndProvidedResourceHasCorrectStatus) + { + ResourceContentHash resource = MockResourceHash::VertArrayHash; + + referenceResource(resource, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); + EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); + EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + const auto managedRes = MockResourceHash::GetManagedResource(resource); + resourceManager.provideResourceData(managedRes); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(resource)); + EXPECT_FALSE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + unreferenceResource(resource, fakeSceneId); + } + + TEST_F(ARendererResourceManager, unreferencingResourceThatWasNotUploadedYetWillNotReportItAsPendingForUploadAnymore) + { + ResourceContentHash resource = MockResourceHash::VertArrayHash; + + referenceResource(resource, fakeSceneId); + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(resource)); + + unreferenceResource(resource, fakeSceneId); + EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); + } + + TEST_F(ARendererResourceManager, DeletesResourceUsedBySingleSceneWhenUnreferenced) + { + ResourceContentHash resource = MockResourceHash::VertArrayHash; + + // request some resource + referenceResource(resource, fakeSceneId); + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + expectResourceUploaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); + EXPECT_TRUE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + resourceManager.unreferenceResourcesForScene(fakeSceneId, { resource }); + expectResourceUnloaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + // Make sure the resource was deleted before the resourceManager gets out of scope + // and deletes it automatically + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + } + + TEST_F(ARendererResourceManager, referencingAnUnloadedResourceAgainShouldUploadItAgain) + { + ResourceContentHash resource = MockResourceHash::VertArrayHash; + const auto managedRes = MockResourceHash::GetManagedResource(resource); + + // ref + referenceResource(resource, fakeSceneId); + resourceManager.provideResourceData(managedRes); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + expectResourceUploaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + // unref + ResourceContentHashVector resources; + resources.push_back(resource); + resourceManager.unreferenceResourcesForScene(fakeSceneId, resources); + expectResourceUnloaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); + + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + + // ref again + referenceResource(resource, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource)); + resourceManager.provideResourceData(managedRes); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + expectResourceUploaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + //general clean-up (expect needed because of strict mock) + unreferenceResource(resource, fakeSceneId); + expectResourceUnloaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + } + + TEST_F(ARendererResourceManager, deletesNoLongerNeededResourcesWhenSceneDestroyed) + { + ResourceContentHash resource = MockResourceHash::VertArrayHash; + + referenceResource(resource, fakeSceneId); + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + expectResourceUploaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + ResourceContentHashVector usedResources; + usedResources.push_back(resource); + resourceManager.unreferenceResourcesForScene(fakeSceneId, usedResources); + expectResourceUnloaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + + // Make sure the resource was deleted before the resourceManager gets out of scope + // and deletes it automatically + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + } + + TEST_F(ARendererResourceManager, keepsTrackOfStreamUsageByStreamBuffers) + { + constexpr WaylandIviSurfaceId source{ 666u }; + constexpr WaylandIviSurfaceId source2{ 667u }; + constexpr SceneId scene1{ 13u }; + constexpr SceneId scene2{ 14u }; + constexpr StreamBufferHandle sb1{ 21u }; + constexpr StreamBufferHandle sb2{ 22u }; + constexpr StreamBufferHandle sb3{ 23u }; + + EXPECT_CALL(embeddedCompositingManager, refStream(source)).Times(2); + EXPECT_CALL(embeddedCompositingManager, refStream(source2)); + resourceManager.uploadStreamBuffer(sb1, source); + resourceManager.uploadStreamBuffer(sb2, source); + resourceManager.uploadStreamBuffer(sb3, source2); + expectStreamUsedBy(source, { sb1, sb2 }); + expectStreamUsedBy(source2, { sb3 }); + + EXPECT_CALL(embeddedCompositingManager, unrefStream(source)).Times(2); + EXPECT_CALL(embeddedCompositingManager, unrefStream(source2)); + resourceManager.unloadStreamBuffer(sb1); + resourceManager.unloadStreamBuffer(sb2); + resourceManager.unloadStreamBuffer(sb3); + expectStreamUsedBy(source, {}); + expectStreamUsedBy(source2, {}); + + resourceManager.unloadAllSceneResourcesForScene(scene1); + resourceManager.unloadAllSceneResourcesForScene(scene2); + } + + TEST_F(ARendererResourceManager, doesNotDeleteResourcesNeededByAnotherSceneWhenSceneDestroyed) + { + ResourceContentHash vertResource = MockResourceHash::VertArrayHash; + ResourceContentHash indexResource = MockResourceHash::IndexArrayHash; + + const DeviceResourceHandle vertDeviceHandle{ 111u }; + const DeviceResourceHandle indexDeviceHandle{ 222u }; + + const SceneId fakeSceneId2(4u); + + //index array is also needed by scene 2 + referenceResource(vertResource, fakeSceneId); + referenceResource(indexResource, fakeSceneId); + referenceResource(indexResource, fakeSceneId2); + + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(vertResource)); + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(indexResource)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + InSequence s; + expectResourceUploaded(vertResource, EResourceType::VertexArray, vertDeviceHandle); + expectResourceUploaded(indexResource, EResourceType::IndexArray, indexDeviceHandle); + resourceManager.uploadAndUnloadPendingResources(); + + resourceManager.unreferenceResourcesForScene(fakeSceneId, { vertResource, indexResource }); + expectResourceUnloaded(vertResource, EResourceType::VertexArray, vertDeviceHandle); + resourceManager.uploadAndUnloadPendingResources(); + + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + + //general clean-up (expect needed because of strict mock) + unreferenceResource(indexResource, fakeSceneId2); + expectResourceUnloaded(indexResource, EResourceType::IndexArray, indexDeviceHandle); + resourceManager.uploadAndUnloadPendingResources(); + } + + TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadDataBuffer_IndexBuffer) + { + const DataBufferHandle dataBuffer(1u); + const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; + const EDataType dataType = EDataType::UInt32; + const uint32_t sizeInBytes = 1024u; + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateIndexBuffer(dataType, sizeInBytes)); + resourceManager.uploadDataBuffer(dataBuffer, dataBufferType, dataType, sizeInBytes, fakeSceneId); + + EXPECT_EQ(DeviceMock::FakeIndexBufferDeviceHandle, resourceManager.getDataBufferDeviceHandle(dataBuffer, fakeSceneId)); + + const std::byte dummyData[10] = {}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadIndexBufferData(DeviceMock::FakeIndexBufferDeviceHandle, dummyData, 7u)); + resourceManager.updateDataBuffer(dataBuffer, 7u, dummyData, fakeSceneId); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteIndexBuffer(DeviceMock::FakeIndexBufferDeviceHandle)); + resourceManager.unloadDataBuffer(dataBuffer, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadDataBuffer_VertexBuffer) + { + const DataBufferHandle dataBuffer(1u); + const EDataBufferType dataBufferType = EDataBufferType::VertexBuffer; + const EDataType dataType = EDataType::UInt32; + const uint32_t sizeInBytes = 1024u; + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexBuffer(sizeInBytes)); + resourceManager.uploadDataBuffer(dataBuffer, dataBufferType, dataType, sizeInBytes, fakeSceneId); + + EXPECT_EQ(DeviceMock::FakeVertexBufferDeviceHandle, resourceManager.getDataBufferDeviceHandle(dataBuffer, fakeSceneId)); + + const std::byte dummyData[10] = {}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadVertexBufferData(DeviceMock::FakeVertexBufferDeviceHandle, dummyData, 7u)); + resourceManager.updateDataBuffer(dataBuffer, 7u, dummyData, fakeSceneId); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexBuffer(DeviceMock::FakeVertexBufferDeviceHandle)); + resourceManager.unloadDataBuffer(dataBuffer, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadTextureBuffer_WithOneMipLevel) + { + InSequence seq; + const TextureBufferHandle textureBuffer(1u); + const uint32_t expectedSize = (10u * 20u) * 4u; + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateTexture2D(10u, 20u, EPixelStorageFormat::RGBA8, DefaultTextureSwizzleArray, 1u, expectedSize)); + resourceManager.uploadTextureBuffer(textureBuffer, 10u, 20u, EPixelStorageFormat::RGBA8, 1u, fakeSceneId); + + EXPECT_EQ(DeviceMock::FakeTextureDeviceHandle, resourceManager.getTextureBufferDeviceHandle(textureBuffer, fakeSceneId)); + + const std::byte dummyTexture[10] = {}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, bindTexture(DeviceMock::FakeTextureDeviceHandle)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadTextureData(DeviceMock::FakeTextureDeviceHandle, 0u, 2u, 3u, 0u, 4u, 5u, 1u, dummyTexture, 0u, 10u)); + resourceManager.updateTextureBuffer(textureBuffer, 0u, Quad{2u, 3u, 4u, 5u}, 10u, dummyTexture, fakeSceneId); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(DeviceMock::FakeTextureDeviceHandle)); + resourceManager.unloadTextureBuffer(textureBuffer, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUpdateAndUnloadTextureBuffer_WithSeveralMipLevels) + { + InSequence seq; + const TextureBufferHandle textureBuffer(1u); + const uint32_t expectedSize = (10u * 20u + 5u * 10u)* 4u; + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateTexture2D(10u, 20u, EPixelStorageFormat::RGBA8, DefaultTextureSwizzleArray, 2u, expectedSize)); + resourceManager.uploadTextureBuffer(textureBuffer, 10u, 20u, EPixelStorageFormat::RGBA8, 2u, fakeSceneId); + + EXPECT_EQ(DeviceMock::FakeTextureDeviceHandle, resourceManager.getTextureBufferDeviceHandle(textureBuffer, fakeSceneId)); + + const std::byte dummyTexture[10] = {}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, bindTexture(DeviceMock::FakeTextureDeviceHandle)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadTextureData(DeviceMock::FakeTextureDeviceHandle, 1u, 2u, 3u, 0u, 4u, 5u, 1u, _, 0u, 10u)); + resourceManager.updateTextureBuffer(textureBuffer, 1u, Quad{2u, 3u, 4u, 5u}, 10u, dummyTexture, fakeSceneId); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(DeviceMock::FakeTextureDeviceHandle)); + resourceManager.unloadTextureBuffer(textureBuffer, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUnloadRenderTargetBuffer) + { + RenderBufferHandle bufferHandle(1u); + const RenderBuffer colorBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)); + resourceManager.uploadRenderTargetBuffer(bufferHandle, fakeSceneId, colorBuffer); + + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getRenderTargetBufferDeviceHandle(bufferHandle, fakeSceneId)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)); + resourceManager.unloadRenderTargetBuffer(bufferHandle, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUnloadBlitPassRenderTargets) + { + const BlitPassHandle blitPassHandle(100u); + const RenderBufferHandle sourceRenderBufferHandle(101u); + const RenderBufferHandle destinationRenderBufferHandle(102u); + + const RenderBuffer renderBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u); + + const DeviceResourceHandle sourceRenderBufferDeviceHandle(201u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, + uploadRenderBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)) + .WillOnce(Return(sourceRenderBufferDeviceHandle)); + resourceManager.uploadRenderTargetBuffer(sourceRenderBufferHandle, fakeSceneId, renderBuffer); + + const DeviceResourceHandle destinationRenderBufferDeviceHandle(202u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, + uploadRenderBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)) + .WillOnce(Return(destinationRenderBufferDeviceHandle)); + resourceManager.uploadRenderTargetBuffer(destinationRenderBufferHandle, fakeSceneId, renderBuffer); + + const DeviceResourceHandle blittingRenderTargetSrcDeviceHandle(203u); + const DeviceResourceHandle blittingRenderTargetDstDeviceHandle(204u); + DeviceHandleVector rbsSrc; + rbsSrc.push_back(sourceRenderBufferDeviceHandle); + DeviceHandleVector rbsDst; + rbsDst.push_back(destinationRenderBufferDeviceHandle); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(rbsSrc))).WillOnce(Return(blittingRenderTargetSrcDeviceHandle)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(rbsDst))).WillOnce(Return(blittingRenderTargetDstDeviceHandle)); + resourceManager.uploadBlitPassRenderTargets(blitPassHandle, sourceRenderBufferHandle, destinationRenderBufferHandle, fakeSceneId); + + DeviceResourceHandle actualBlitPassRTSrc; + DeviceResourceHandle actualBlitPassRTDst; + resourceManager.getBlitPassRenderTargetsDeviceHandle(blitPassHandle, fakeSceneId, actualBlitPassRTSrc, actualBlitPassRTDst); + EXPECT_EQ(blittingRenderTargetSrcDeviceHandle, actualBlitPassRTSrc); + EXPECT_EQ(blittingRenderTargetDstDeviceHandle, actualBlitPassRTDst); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(actualBlitPassRTSrc)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(actualBlitPassRTDst)); + resourceManager.unloadBlitPassRenderTargets(blitPassHandle, fakeSceneId); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2); + resourceManager.unloadRenderTargetBuffer(sourceRenderBufferHandle, fakeSceneId); + resourceManager.unloadRenderTargetBuffer(destinationRenderBufferHandle, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUnloadRenderTargetWithBuffers) + { + RenderTargetHandle targetHandle(1u); + + RenderBufferHandleVector bufferHandles; + bufferHandles.push_back(RenderBufferHandle(1u)); + bufferHandles.push_back(RenderBufferHandle(5u)); + + const RenderBuffer colorBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u); + const RenderBuffer depthBuffer(800u, 600u, EPixelStorageFormat::Depth24, ERenderBufferAccessMode::ReadWrite, 0u); + + { + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, EPixelStorageFormat::Depth24, ERenderBufferAccessMode::ReadWrite, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + } + + resourceManager.uploadRenderTargetBuffer(bufferHandles[0], fakeSceneId, colorBuffer); + resourceManager.uploadRenderTargetBuffer(bufferHandles[1], fakeSceneId, depthBuffer); + + resourceManager.uploadRenderTarget(targetHandle, bufferHandles, fakeSceneId); + + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getRenderTargetBufferDeviceHandle(bufferHandles[0], fakeSceneId)); + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getRenderTargetBufferDeviceHandle(bufferHandles[1], fakeSceneId)); + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getRenderTargetDeviceHandle(targetHandle, fakeSceneId)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2); + resourceManager.unloadRenderTargetBuffer(bufferHandles[0], fakeSceneId); + resourceManager.unloadRenderTargetBuffer(bufferHandles[1], fakeSceneId); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + resourceManager.unloadRenderTarget(targetHandle, fakeSceneId); + } + + TEST_F(ARendererResourceManager, canUploadAndUnloadVertexArray) + { + VertexArrayInfo vaInfo; + vaInfo.shader = DeviceMock::FakeShaderDeviceHandle; + vaInfo.indexBuffer = DeviceMock::FakeIndexBufferDeviceHandle; + vaInfo.vertexBuffers.push_back({ DeviceMock::FakeVertexBufferDeviceHandle,DataFieldHandle{2u}, 3u, 4u, EDataType::Vector2F, 5u, 6u }); + + const RenderableHandle renderable{ 123u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexArray(Ref(vaInfo))); + resourceManager.uploadVertexArray(renderable, vaInfo, fakeSceneId); + + EXPECT_EQ(DeviceMock::FakeVertexArrayDeviceHandle, resourceManager.getVertexArrayDeviceHandle(renderable, fakeSceneId)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexArray(DeviceMock::FakeVertexArrayDeviceHandle)); + resourceManager.unloadVertexArray(renderable, fakeSceneId); + } + + TEST_F(ARendererResourceManager, GetsInvalidDeviceHandleForUnknownOffscreenBuffer) + { + const OffscreenBufferHandle bufferHandle(1u); + EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); + EXPECT_FALSE(resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle).isValid()); + } + + TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorBuffer) + { + const OffscreenBufferHandle bufferHandle(1u); + + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, EDepthBufferType::None); + + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)); + } + + TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthBuffers) + { + const OffscreenBufferHandle bufferHandle(1u); + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::Depth32, ERenderBufferAccessMode::WriteOnly, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, ramses::EDepthBufferType::Depth); + + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); + } + + TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthStencilAttached) + { + const OffscreenBufferHandle bufferHandle(1u); + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::Depth24_Stencil8, ERenderBufferAccessMode::WriteOnly, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil); + + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); + } + + TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthStencilAttachedWithMSAAenabled) + { + const OffscreenBufferHandle bufferHandle(1u); + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 4u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::Depth24_Stencil8, ERenderBufferAccessMode::WriteOnly, 4u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 4u, false, EDepthBufferType::DepthStencil); + + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); + } + + TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorBuffer_WithDoubleBuffering) + { + const OffscreenBufferHandle bufferHandle(1u); + const DeviceResourceHandle colorBufferDeviceHandle1{ 7771u }; + const DeviceResourceHandle colorBufferDeviceHandle2{ 7778u }; + const DeviceResourceHandle offscreenBufferDeviceHandle1{ 7798u }; + const DeviceResourceHandle offscreenBufferDeviceHandle2{ 7799u }; + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)) + .WillOnce(Return(colorBufferDeviceHandle1)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({ colorBufferDeviceHandle1 })))).WillOnce(Return(offscreenBufferDeviceHandle1)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)) + .WillOnce(Return(colorBufferDeviceHandle2)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({ colorBufferDeviceHandle2 })))).WillOnce(Return(offscreenBufferDeviceHandle2)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle1)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle2)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, EDepthBufferType::None); + + EXPECT_EQ(offscreenBufferDeviceHandle1, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(colorBufferDeviceHandle1, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(offscreenBufferDeviceHandle1)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2); + EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); + } + + TEST_F(ARendererResourceManager, UploadsOffscreenBufferWithColorAndDepthStencilBuffers_WithDoubleBuffering) + { + const OffscreenBufferHandle bufferHandle(1u); + const DeviceResourceHandle colorBufferDeviceHandle1{ 7771u }; + const DeviceResourceHandle colorBufferDeviceHandle2{ 7778u }; + const DeviceResourceHandle depthBufferDeviceHandle{ 7796u }; + const DeviceResourceHandle offscreenBufferDeviceHandle1{ 7798u }; + const DeviceResourceHandle offscreenBufferDeviceHandle2{ 7799u }; + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)) + .WillOnce(Return(colorBufferDeviceHandle1)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::Depth24_Stencil8, ERenderBufferAccessMode::WriteOnly, 0u)) + .WillOnce(Return(depthBufferDeviceHandle)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({colorBufferDeviceHandle1, depthBufferDeviceHandle})))).WillOnce(Return(offscreenBufferDeviceHandle1)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(1u, 1u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)) + .WillOnce(Return(colorBufferDeviceHandle2)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(Eq(DeviceHandleVector({ colorBufferDeviceHandle2, depthBufferDeviceHandle })))).WillOnce(Return(offscreenBufferDeviceHandle2)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle1)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(offscreenBufferDeviceHandle2)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, EDepthBufferType::DepthStencil); + + EXPECT_EQ(offscreenBufferDeviceHandle1, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(colorBufferDeviceHandle1, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(offscreenBufferDeviceHandle1)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2); + EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(3u); + } + + TEST_F(ARendererResourceManager, UploadsDmaOffscreenBuffer) + { + constexpr OffscreenBufferHandle bufferHandle(1u); + + constexpr DmaBufferFourccFormat bufferFormat{ 777u }; + constexpr DmaBufferUsageFlags bufferUsageFlags{ 888u }; + constexpr DmaBufferModifiers bufferModifiers{ 999u }; + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadDmaRenderBuffer(10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadDmaOffscreenBuffer(bufferHandle, 10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers); + + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(DeviceMock::FakeDmaRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, getDmaRenderBufferFD(DeviceMock::FakeDmaRenderBufferDeviceHandle)).WillOnce(Return(123)); + EXPECT_EQ(123, resourceManager.getDmaOffscreenBufferFD(bufferHandle)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, getDmaRenderBufferStride(DeviceMock::FakeDmaRenderBufferDeviceHandle)).WillOnce(Return(432u)); + EXPECT_EQ(432u, resourceManager.getDmaOffscreenBufferStride(bufferHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, destroyDmaRenderBuffer(_)); + } + + TEST_F(ARendererResourceManager, CanUnloadOffscreenBuffer_WithColorBuffer) + { + const OffscreenBufferHandle bufferHandle(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, EDepthBufferType::None); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)); + resourceManager.unloadOffscreenBuffer(bufferHandle); + + EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); + } + + TEST_F(ARendererResourceManager, CanUnloadOffscreenBuffer_WithColorAndDepthStencilBuffers) + { + const OffscreenBufferHandle bufferHandle(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); + resourceManager.unloadOffscreenBuffer(bufferHandle); + + EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); + } + + TEST_F(ARendererResourceManager, CanUnloadDoubleBufferedOffscreenBuffer_WithColorBuffer) + { + const OffscreenBufferHandle bufferHandle(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)).Times(2u); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, EDepthBufferType::None); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2u); + resourceManager.unloadOffscreenBuffer(bufferHandle); + + EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); + } + + TEST_F(ARendererResourceManager, CanUnloadDoubleBufferedOffscreenBuffer_WithColorAndDepthStencilBuffers) + { + const OffscreenBufferHandle bufferHandle(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _)).Times(3u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)).Times(2u); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, pairRenderTargetsForDoubleBuffering(_, _)); + resourceManager.uploadOffscreenBuffer(bufferHandle, 1u, 1u, 0u, true, EDepthBufferType::DepthStencil); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, unpairRenderTargets(_)).Times(1u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(3u); + resourceManager.unloadOffscreenBuffer(bufferHandle); + + EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); + } + + TEST_F(ARendererResourceManager, CanUnloadDmaOffscreenBuffer) + { + constexpr OffscreenBufferHandle bufferHandle(1u); + + constexpr DmaBufferFourccFormat bufferFormat{ 777u }; + constexpr DmaBufferUsageFlags bufferUsageFlags{ 888u }; + constexpr DmaBufferModifiers bufferModifiers{ 999u }; + InSequence seq; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadDmaRenderBuffer(10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)); + RenderState::ScissorRegion scissorRegion{}; + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, scissorRegion)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)); + resourceManager.uploadDmaOffscreenBuffer(bufferHandle, 10u, 20u, bufferFormat, bufferUsageFlags, bufferModifiers); + + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getOffscreenBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(DeviceMock::FakeDmaRenderBufferDeviceHandle, resourceManager.getOffscreenBufferColorBufferDeviceHandle(bufferHandle)); + EXPECT_EQ(bufferHandle, resourceManager.getOffscreenBufferHandle(DeviceMock::FakeRenderTargetDeviceHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, destroyDmaRenderBuffer(_)); + resourceManager.unloadOffscreenBuffer(bufferHandle); + EXPECT_FALSE(resourceManager.getOffscreenBufferDeviceHandle(bufferHandle).isValid()); + } + + TEST_F(ARendererResourceManager, UploadsAndUnloadsStreamBuffers) + { + constexpr StreamBufferHandle sbHandle1{ 1u }; + constexpr StreamBufferHandle sbHandle2{ 2u }; + constexpr WaylandIviSurfaceId sbSrc1{ 11u }; + constexpr WaylandIviSurfaceId sbSrc2{ 12u }; + EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc1)); + resourceManager.uploadStreamBuffer(sbHandle1, sbSrc1); + EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc2)); + resourceManager.uploadStreamBuffer(sbHandle2, sbSrc2); + + EXPECT_CALL(embeddedCompositingManager, getCompositedTextureDeviceHandleForStreamTexture(sbSrc1)).WillOnce(Return(DeviceMock::FakeRenderTargetDeviceHandle)); + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, resourceManager.getStreamBufferDeviceHandle(sbHandle1)); + EXPECT_CALL(embeddedCompositingManager, getCompositedTextureDeviceHandleForStreamTexture(sbSrc2)).WillOnce(Return(DeviceMock::FakeRenderBufferDeviceHandle)); + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, resourceManager.getStreamBufferDeviceHandle(sbHandle2)); + + EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc1)); + resourceManager.unloadStreamBuffer(sbHandle1); + EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc2)); + resourceManager.unloadStreamBuffer(sbHandle2); + } + + TEST_F(ARendererResourceManager, returnsInvalidDeviceHandleForUnknownStreamBuffer) + { + EXPECT_FALSE(resourceManager.getStreamBufferDeviceHandle(StreamBufferHandle::Invalid()).isValid()); + EXPECT_FALSE(resourceManager.getStreamBufferDeviceHandle(StreamBufferHandle{ 9999u }).isValid()); + } + + TEST_F(ARendererResourceManager, UploadsAndUnloadsExternalBuffers) + { + constexpr ExternalBufferHandle ebHandle1{ 1u }; + constexpr ExternalBufferHandle ebHandle2{ 2u }; + + constexpr DeviceResourceHandle deviceHandle1{ 11111u }; + constexpr DeviceResourceHandle deviceHandle2{ 22222u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle1)); + resourceManager.uploadExternalBuffer(ebHandle1); + EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle2)); + resourceManager.uploadExternalBuffer(ebHandle2); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle1)); + resourceManager.unloadExternalBuffer(ebHandle1); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle2)); + resourceManager.unloadExternalBuffer(ebHandle2); + } + + TEST_F(ARendererResourceManager, FailsUploadingExternalBuffersIfExtensionNotSupported) + { + constexpr ExternalBufferHandle ebHandle{ 1u }; + + EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(false)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).Times(0); + resourceManager.uploadExternalBuffer(ebHandle); + } + + TEST_F(ARendererResourceManager, CanGetDeviceHandleForExternalBuffer) + { + constexpr ExternalBufferHandle ebHandle{ 1u }; + + constexpr DeviceResourceHandle deviceHandle{ 11111u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle)); + resourceManager.uploadExternalBuffer(ebHandle); + + EXPECT_EQ(deviceHandle, resourceManager.getExternalBufferDeviceHandle(ebHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle)); + resourceManager.unloadExternalBuffer(ebHandle); + } + + TEST_F(ARendererResourceManager, ReturnsInvalidDeviceHandleForUnknownExternalBuffer) + { + constexpr ExternalBufferHandle ebHandle{ 1u }; + EXPECT_FALSE(resourceManager.getExternalBufferDeviceHandle(ebHandle).isValid()); + } + + TEST_F(ARendererResourceManager, CanGetGlIdForExternalBuffer) + { + constexpr ExternalBufferHandle ebHandle{ 1u }; + + constexpr DeviceResourceHandle deviceHandle{ 11111u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, isExternalTextureExtensionSupported()).WillOnce(Return(true)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateExternalTexture()).WillOnce(Return(deviceHandle)); + resourceManager.uploadExternalBuffer(ebHandle); + + uint32_t glId{ 567u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, getTextureAddress(deviceHandle)).WillOnce(Return(glId)); + EXPECT_EQ(glId, resourceManager.getExternalBufferGlId(ebHandle)); + + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(deviceHandle)); + resourceManager.unloadExternalBuffer(ebHandle); + } + + TEST_F(ARendererResourceManager, ReturnsInvalidGlIdForUnknownExternalBuffer) + { + constexpr ExternalBufferHandle ebHandle{ 1u }; + EXPECT_EQ(0u, resourceManager.getExternalBufferGlId(ebHandle)); + } + + TEST_F(ARendererResourceManager, UploadAndDeleteValidShader) + { + auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, ""); + const ResourceContentHash resHash = effectRes->getHash(); + + // request some resources + referenceResource(resHash, fakeSceneId); + resourceManager.provideResourceData(std::move(effectRes)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + uploadShader(resHash); + + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resHash)); + unreferenceResource(resHash, fakeSceneId); + expectResourceUnloaded(resHash, EResourceType::Effect, DeviceMock::FakeShaderDeviceHandle); + resourceManager.uploadAndUnloadPendingResources(); + } + + TEST_F(ARendererResourceManager, DoesNotUnregisterResourceThatWasUploaded) + { + const ResourceContentHash resource = MockResourceHash::VertArrayHash; + + referenceResource(resource, fakeSceneId); + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + expectResourceUploaded(resource, EResourceType::VertexArray); + resourceManager.uploadAndUnloadPendingResources(); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); + EXPECT_TRUE(resourceManager.getResourceDeviceHandle(resource).isValid()); + + resourceManager.unreferenceResourcesForScene(fakeSceneId, { resource }); + ASSERT_TRUE(resourceManager.getRendererResourceRegistry().containsResource(resource)); + + expectResourceUnloaded(resource, EResourceType::VertexArray); + } + + TEST_F(ARendererResourceManager, DoesNotUnregisterResourceThatWasScheduledForUpload) + { + auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, ""); + const ResourceContentHash resHash = effectRes->getHash(); + + // request some resources + referenceResource(resHash, fakeSceneId); + resourceManager.provideResourceData(std::move(effectRes)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + //simulate shader was not uploaded by ResourceUploader (not found in bin shader cache) so it gets uploaded by AsyncEffectUploader + EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([resHash](auto& /*unused*/, const auto& rd, auto& /*unused*/) { + EXPECT_EQ(resHash, rd.hash); + EXPECT_EQ(EResourceType::Effect, rd.type); + + return std::optional{}; + })); + + EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)); + expectDeviceFlushOnWindows(); + EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); + EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(Ref(platform.renderBackendMock), _, _, _)); + resourceManager.uploadAndUnloadPendingResources(); + ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(resHash)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + ASSERT_TRUE(resourceManager.getRendererResourceRegistry().containsResource(resHash)); + + constexpr std::chrono::seconds timeoutTime{ 2u }; + const auto startTime = std::chrono::steady_clock::now(); + while (resourceManager.getResourceStatus(resHash) == EResourceStatus::ScheduledForUpload + && std::chrono::steady_clock::now() - startTime < timeoutTime) + { + std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); + resourceManager.uploadAndUnloadPendingResources(); + } + + expectResourceUnloaded(resHash, EResourceType::Effect, DeviceMock::FakeShaderDeviceHandle); + } + + TEST_F(ARendererResourceManager, DoesNotUnloadEffectThatGetsUnreferencedAndReReferencedWhileCompiling) + { + auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, ""); + const ResourceContentHash resHash = effectRes->getHash(); + + // request some resources + referenceResource(resHash, fakeSceneId); + resourceManager.provideResourceData(std::move(effectRes)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + //simulate shader was not uploaded by ResourceUploader (not found in bin shader cache) so it gets uploaded by AsyncEffectUploader + EXPECT_CALL(*resUploader, uploadResource(Ref(platform.renderBackendMock), _, _)).WillOnce(Invoke([resHash](auto& /*unused*/, const auto& rd, auto& /*unused*/) { + EXPECT_EQ(resHash, rd.hash); + EXPECT_EQ(EResourceType::Effect, rd.type); + + return std::optional{}; + })); + + //block call to upload shader to simulate scene getting unreferenced while AsyncEffectUploader is still uploading + std::promise barrier; + EXPECT_CALL(platform.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([&](const auto& /*unused*/) { + barrier.get_future().get(); + return std::make_unique(1u, 2u); + })); + expectDeviceFlushOnWindows(); + EXPECT_CALL(platform.renderBackendMock.deviceMock, registerShader(_)); + EXPECT_CALL(*resUploader, storeShaderInBinaryShaderCache(_, _, _, _)); + + resourceManager.uploadAndUnloadPendingResources(); + ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceManager.getResourceStatus(resHash)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + resourceManager.uploadAndUnloadPendingResources(); + + const SceneId fakeSceneId2{ 432u }; + ASSERT_NE(fakeSceneId, fakeSceneId2); + referenceResource(resHash, fakeSceneId2); + resourceManager.uploadAndUnloadPendingResources(); + + barrier.set_value(); + + constexpr std::chrono::seconds timeoutTime{ 2u }; + const auto startTime = std::chrono::steady_clock::now(); + while (EResourceStatus::Uploaded != resourceManager.getResourceStatus(resHash) + && std::chrono::steady_clock::now() - startTime < timeoutTime) + { + std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); + resourceManager.uploadAndUnloadPendingResources(); + } + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resHash)); + const auto& effectSceneUsage = resourceManager.getRendererResourceRegistry().getResourceDescriptor(resHash).sceneUsage; + ASSERT_EQ(1u, effectSceneUsage.size()); + EXPECT_EQ(fakeSceneId2, effectSceneUsage[0]); + + expectResourceUnloaded(resHash, EResourceType::Effect, DeviceMock::FakeShaderDeviceHandle); + unreferenceResource(resHash, fakeSceneId2); + } + + TEST_F(ARendererResourceManager, UploadInvalidShaderResultsInBrokenResource) + { + auto effectRes = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector{}, EffectInputInformationVector{}, ""); + const ResourceContentHash resHash = effectRes->getHash(); + + // request some resources + referenceResource(resHash, fakeSceneId); + resourceManager.provideResourceData(std::move(effectRes)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + + uploadShader(resHash, false); + + // resource must be broken + EXPECT_EQ(EResourceStatus::Broken, resourceManager.getResourceStatus(resHash)); + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + + EXPECT_FALSE(resourceManager.hasResourcesToBeUploaded()); + + // not expecting any unload + unreferenceResource(resHash, fakeSceneId); + } + + TEST_F(ARendererResourceManager, DeletesEffectOnlyWhenDestructed_NotWhenUnrequested) + { + const ResourceContentHash resource = MockResourceHash::EffectHash; + + // request some resources + referenceResource(resource, fakeSceneId); + resourceManager.provideResourceData(MockResourceHash::GetManagedResource(resource)); + EXPECT_TRUE(resourceManager.hasResourcesToBeUploaded()); + uploadShader(resource); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(resource)); + + unreferenceResource(resource, fakeSceneId); + + // Make sure the effect was not deleted, before resource manager is destroyed + Mock::VerifyAndClearExpectations(&platform.renderBackendMock.deviceMock); + + // delete resource + expectResourceUnloaded(resource, EResourceType::Effect, DeviceMock::FakeShaderDeviceHandle); + } + + TEST_F(ARendererResourceManager, unloadsAllSceneResources) + { + // upload render buffer + RenderTargetHandle targetHandle(1u); + RenderBufferHandleVector bufferHandles; + bufferHandles.push_back(RenderBufferHandle(1u)); + bufferHandles.push_back(RenderBufferHandle(5u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(800u, 600u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u)); + const RenderBuffer colorBuffer(800u, 600u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u); + const RenderBuffer depthBuffer(800u, 600u, EPixelStorageFormat::Depth16, ERenderBufferAccessMode::ReadWrite, 0u); + resourceManager.uploadRenderTargetBuffer(bufferHandles[0], fakeSceneId, colorBuffer); + resourceManager.uploadRenderTargetBuffer(bufferHandles[1], fakeSceneId, depthBuffer); + + // upload render target + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)); + resourceManager.uploadRenderTarget(targetHandle, bufferHandles, fakeSceneId); + + // upload blit pass + const BlitPassHandle blitPassHandle(100u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); + resourceManager.uploadBlitPassRenderTargets(blitPassHandle, bufferHandles[0], bufferHandles[1], fakeSceneId); + + //upload index data buffer + const DataBufferHandle indexDataBufferHandle(123u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateIndexBuffer(_, _)); + resourceManager.uploadDataBuffer(indexDataBufferHandle, EDataBufferType::IndexBuffer, EDataType::Float, 10u, fakeSceneId); + + //upload vertex data buffer + const DataBufferHandle vertexDataBufferHandle(777u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexBuffer(_)); + resourceManager.uploadDataBuffer(vertexDataBufferHandle, EDataBufferType::VertexBuffer, EDataType::Float, 10u, fakeSceneId); + + //upload texture buffer + const TextureBufferHandle textureBufferHandle(666u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateTexture2D(_, _, _, _, _, _)); + resourceManager.uploadTextureBuffer(textureBufferHandle, 1u, 2u, EPixelStorageFormat::RGBA8, 1u, fakeSceneId); + + //upload vertex array + VertexArrayInfo vaInfo; + vaInfo.shader = DeviceMock::FakeShaderDeviceHandle; + vaInfo.indexBuffer = DeviceMock::FakeIndexBufferDeviceHandle; + vaInfo.vertexBuffers.push_back({ DeviceMock::FakeVertexBufferDeviceHandle,DataFieldHandle{2u}, 3u, 4u, EDataType::Vector2F, 5u, 6u }); + + const RenderableHandle renderable{ 123u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, allocateVertexArray(Ref(vaInfo))); + resourceManager.uploadVertexArray(renderable, vaInfo, fakeSceneId); + + // unload all scene resources + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(2); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(3); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteIndexBuffer(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexBuffer(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteTexture(_)); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteVertexArray(_)); + resourceManager.unloadAllSceneResourcesForScene(fakeSceneId); + + // Make sure the resource was deleted before the resourceManager gets out of scope + // and deletes it automatically + Mock::VerifyAndClearExpectations(&platform.renderBackendMock); + } + + TEST_F(ARendererResourceManager, canUnreferenceAllResourcesUsedByAScene) + { + const SceneId fakeSceneId2(fakeSceneId.getValue() + 1u); + const auto resource1 = MockResourceHash::VertArrayHash; + const auto resource2 = MockResourceHash::IndexArrayHash; + const auto resource3 = MockResourceHash::TextureHash; + + // request some resources + referenceResource(resource1, fakeSceneId); + referenceResource(resource2, fakeSceneId); + referenceResource(resource2, fakeSceneId2); + referenceResource(resource3, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource1)); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource2)); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource3)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(resource2)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId2); + } + + TEST_F(ARendererResourceManager, canProvideResourceData) + { + const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); + // resource must be referenced by scene before providing data + referenceResource(MockResourceHash::EffectHash, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.provideResourceData(managedRes); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + } + + TEST_F(ARendererResourceManager, providingResourceDataMulitpleTimesIsNoop) + { + const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); + // resource must be referenced by scene before providing data + referenceResource(MockResourceHash::EffectHash, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.provideResourceData(managedRes); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + // resource is already provided so providing data again is noop + resourceManager.provideResourceData(managedRes); + resourceManager.provideResourceData(managedRes); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + } + + TEST_F(ARendererResourceManager, providingResourceDataWhenAlreadyUploadedIsNoop) + { + const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); + // resource must be referenced by scene before providing data + referenceResource(MockResourceHash::EffectHash, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.provideResourceData(managedRes); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + // upload the resource + uploadShader(MockResourceHash::EffectHash); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + EXPECT_TRUE(resourceManager.getResourceDeviceHandle(MockResourceHash::EffectHash).isValid()); + + // resource is already provided so providing data again is noop + resourceManager.provideResourceData(managedRes); + resourceManager.provideResourceData(managedRes); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + EXPECT_TRUE(resourceManager.getResourceDeviceHandle(MockResourceHash::EffectHash).isValid()); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + expectResourceUnloaded(MockResourceHash::EffectHash, EResourceType::Effect, DeviceMock::FakeShaderDeviceHandle); + } + + TEST_F(ARendererResourceManager, rereferencingResourceKeptInCacheIsNotUploadedAgain) + { + const auto managedRes = MockResourceHash::GetManagedResource(MockResourceHash::EffectHash); + // resource must be referenced by scene before providing data + referenceResource(MockResourceHash::EffectHash, fakeSceneId); + EXPECT_EQ(EResourceStatus::Registered, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.provideResourceData(managedRes); + EXPECT_EQ(EResourceStatus::Provided, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + // upload the resource + uploadShader(MockResourceHash::EffectHash); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + EXPECT_TRUE(resourceManager.getResourceDeviceHandle(MockResourceHash::EffectHash).isValid()); + + unreferenceResource(MockResourceHash::EffectHash, fakeSceneId); + resourceManager.uploadAndUnloadPendingResources(); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + referenceResource(MockResourceHash::EffectHash, fakeSceneId); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.uploadAndUnloadPendingResources(); + EXPECT_EQ(EResourceStatus::Uploaded, resourceManager.getResourceStatus(MockResourceHash::EffectHash)); + + resourceManager.unreferenceAllResourcesForScene(fakeSceneId); + expectResourceUnloaded(MockResourceHash::EffectHash, EResourceType::Effect, DeviceMock::FakeShaderDeviceHandle); + } + + TEST_F(ARendererResourceManager, UnloadsAllRemainingOffscreenBuffersAndStreamBuffersAtDestruction) + { + // 2 offscreen buffers + constexpr OffscreenBufferHandle obHandle{ 1u }; + constexpr OffscreenBufferHandle obHandle2{ 2u }; + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, uploadRenderBuffer(_, _, _, _, _)).Times(4u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, activateRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, colorMask(true, true, true, true)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clearColor(glm::vec4{ 0.f, 0.f, 0.f, 1.f })).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, depthWrite(EDepthWrite::Enabled)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, scissorTest(EScissorTest::Disabled, RenderState::ScissorRegion{})).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, clear(_)).Times(2u); + resourceManager.uploadOffscreenBuffer(obHandle, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil); + resourceManager.uploadOffscreenBuffer(obHandle2, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil); + + // 2 stream buffers + constexpr StreamBufferHandle sbHandle{ 1u }; + constexpr StreamBufferHandle sbHandle2{ 2u }; + constexpr WaylandIviSurfaceId sbSrc1{ 11u }; + constexpr WaylandIviSurfaceId sbSrc2{ 12u }; + EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc1)); + EXPECT_CALL(embeddedCompositingManager, refStream(sbSrc2)); + resourceManager.uploadStreamBuffer(sbHandle, sbSrc1); + resourceManager.uploadStreamBuffer(sbHandle2, sbSrc2); + + // will destroy OBs directly on device + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderTarget(_)).Times(2u); + EXPECT_CALL(platform.renderBackendMock.deviceMock, deleteRenderBuffer(_)).Times(4u); + + // will destroy SBs via EC manager + EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc1)); + EXPECT_CALL(embeddedCompositingManager, unrefStream(sbSrc2)); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceRegistryTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceRegistryTest.cpp new file mode 100644 index 000000000..1284e08c0 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererResourceRegistryTest.cpp @@ -0,0 +1,435 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RendererResourceRegistry.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "ResourceMock.h" +#include "MockResourceHash.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +using namespace testing; + +namespace ramses::internal +{ + class ARendererResourceRegistry : public ::testing::Test + { + public: + protected: + ARendererResourceRegistry() + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + } + + void expectResourcesUsedByScene(SceneId sceneId, const ResourceContentHashVector& res) + { + if (res.empty()) + { + EXPECT_EQ(nullptr, registry.getResourcesInUseByScene(sceneId)); + } + else + { + ASSERT_NE(nullptr, registry.getResourcesInUseByScene(sceneId)); + EXPECT_EQ(res, *registry.getResourcesInUseByScene(sceneId)); + } + } + + ManagedResource testManagedResource{ MockResourceHash::GetManagedResource(MockResourceHash::IndexArrayHash) }; + RendererResourceRegistry registry; + }; + + TEST_F(ARendererResourceRegistry, doesNotContainAnythingInitially) + { + const ResourceContentHash resource(123u, 0u); + EXPECT_EQ(0u, registry.getAllResourceDescriptors().size()); + EXPECT_FALSE(registry.containsResource(resource)); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + expectResourcesUsedByScene(SceneId{ 3u }, {}); + } + + TEST_F(ARendererResourceRegistry, canRegisterResource) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + + EXPECT_EQ(1u, registry.getAllResourceDescriptors().size()); + EXPECT_TRUE(registry.containsResource(resource)); + + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_EQ(resource, rd.hash); + } + + TEST_F(ARendererResourceRegistry, registeredResourceHasCorrectStatus) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_FALSE(rd.deviceHandle.isValid()); + EXPECT_FALSE(rd.resource); + EXPECT_TRUE(rd.sceneUsage.empty()); + EXPECT_EQ(EResourceStatus::Registered, rd.status); + EXPECT_EQ(EResourceType::Invalid, rd.type); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, unregisteredResourceIsNotContainedAnymore) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.unregisterResource(resource); + + EXPECT_EQ(0u, registry.getAllResourceDescriptors().size()); + EXPECT_FALSE(registry.containsResource(resource)); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canAddSceneUsageRef) + { + const SceneId sceneId(11u); + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + + expectResourcesUsedByScene(sceneId, {}); + registry.addResourceRef(resource, sceneId); + expectResourcesUsedByScene(sceneId, { resource }); + + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + ASSERT_EQ(1u, rd.sceneUsage.size()); + EXPECT_EQ(sceneId, rd.sceneUsage[0]); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canRemoveSceneUsageRef) + { + const SceneId sceneId(11u); + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + + registry.addResourceRef(resource, sceneId); + registry.removeResourceRef(resource, sceneId); + expectResourcesUsedByScene(sceneId, {}); + + EXPECT_FALSE(registry.containsResource(resource)); + } + + TEST_F(ARendererResourceRegistry, canSetResourceData) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_EQ(EResourceStatus::Provided, rd.status); + EXPECT_EQ(testManagedResource, rd.resource); + EXPECT_EQ(EResourceType::IndexArray, rd.type); + EXPECT_EQ(0u, rd.compressedSize); + EXPECT_EQ(1u, rd.decompressedSize); + EXPECT_FALSE(rd.deviceHandle.isValid()); + + EXPECT_FALSE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canSetResourceFromProvidedToUploaded_willReleaseResourceData) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_EQ(EResourceStatus::Provided, rd.status); + + const DeviceResourceHandle deviceHandle(123456u); + registry.setResourceUploaded(resource, deviceHandle, 666u); + + EXPECT_EQ(EResourceStatus::Uploaded, rd.status); + EXPECT_EQ(deviceHandle, rd.deviceHandle); + EXPECT_FALSE(rd.resource); + EXPECT_EQ(EResourceType::IndexArray, rd.type); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canSetResourceProvidedToBroken_willReleaseResourceData) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + + registry.setResourceBroken(resource); + + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_EQ(EResourceStatus::Broken, rd.status); + EXPECT_FALSE(rd.resource); + EXPECT_EQ(EResourceType::IndexArray, rd.type); + EXPECT_FALSE(rd.deviceHandle.isValid()); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canSetResourceFromScheduledForUploadToUploaded_willReleaseResourceData) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_EQ(EResourceStatus::Provided, rd.status); + + registry.setResourceScheduledForUpload(resource); + EXPECT_EQ(EResourceStatus::ScheduledForUpload, rd.status); + + const DeviceResourceHandle deviceHandle(123456u); + registry.setResourceUploaded(resource, deviceHandle, 666u); + + EXPECT_EQ(EResourceStatus::Uploaded, rd.status); + EXPECT_EQ(deviceHandle, rd.deviceHandle); + EXPECT_FALSE(rd.resource); + EXPECT_EQ(EResourceType::IndexArray, rd.type); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canSetResourceScheduledForUploadToBroken_willReleaseResourceData) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + registry.setResourceScheduledForUpload(resource); + + registry.setResourceBroken(resource); + + const ResourceDescriptor& rd = registry.getResourceDescriptor(resource); + EXPECT_EQ(EResourceStatus::Broken, rd.status); + EXPECT_FALSE(rd.resource); + EXPECT_EQ(EResourceType::IndexArray, rd.type); + EXPECT_FALSE(rd.deviceHandle.isValid()); + + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, providedResourceIsInProvidedList) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + + const ResourceContentHashVector& resources = registry.getAllProvidedResources(); + ASSERT_EQ(1u, resources.size()); + EXPECT_TRUE(contains_c(resources, resource)); + } + + TEST_F(ARendererResourceRegistry, reportsIfHasAnyResourcesScheduledForUpload) + { + EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); + const ResourceContentHash resource(123u, 0u); + + registry.registerResource(resource); + EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); + + registry.setResourceData(resource, testManagedResource); + EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); + + registry.setResourceScheduledForUpload(resource); + EXPECT_EQ(EResourceStatus::ScheduledForUpload, registry.getResourceStatus(resource)); + EXPECT_TRUE(registry.hasAnyResourcesScheduledForUpload()); + + const DeviceResourceHandle deviceHandle(123456u); + registry.setResourceUploaded(resource, deviceHandle, 666u); + EXPECT_FALSE(registry.hasAnyResourcesScheduledForUpload()); + } + + TEST_F(ARendererResourceRegistry, uploadedResourceIsRemovedFromProvidedList) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + EXPECT_FALSE(registry.getAllProvidedResources().empty()); + + registry.setResourceUploaded(resource, DeviceResourceHandle{ 123u }, 666u); + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + } + + TEST_F(ARendererResourceRegistry, scheduledForUploadResourceIsRemovedFromProvidedList) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + EXPECT_FALSE(registry.getAllProvidedResources().empty()); + + registry.setResourceScheduledForUpload(resource); + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + } + + TEST_F(ARendererResourceRegistry, brokenResourceIsRemovedFromProvidedList) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + EXPECT_FALSE(registry.getAllProvidedResources().empty()); + + registry.setResourceBroken(resource); + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + } + + TEST_F(ARendererResourceRegistry, unregisteredResourceIsRemovedFromProvidedList) + { + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.setResourceData(resource, testManagedResource); + EXPECT_FALSE(registry.getAllProvidedResources().empty()); + + registry.unregisterResource(resource); + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + } + + TEST_F(ARendererResourceRegistry, unusedResourceIsRemovedFromProvidedList) + { + const SceneId sceneId(11u); + const ResourceContentHash resource(123u, 0u); + registry.registerResource(resource); + registry.addResourceRef(resource, sceneId); + + registry.setResourceData(resource, testManagedResource); + EXPECT_FALSE(registry.getAllProvidedResources().empty()); + + registry.removeResourceRef(resource, sceneId); + EXPECT_TRUE(registry.getAllProvidedResources().empty()); + } + + TEST_F(ARendererResourceRegistry, resourceWithoutReferenceIsPutInUnusedListIfUploadedOrUnregisteredIfNotUploaded) + { + const SceneId sceneId(11u); + + const ResourceContentHash resource1(123u, 0u); + const ResourceContentHash resource2(124u, 0u); + const ResourceContentHash resource3(125u, 0u); + const ResourceContentHash resource4(126u, 0u); + + registry.registerResource(resource1); + registry.registerResource(resource2); + registry.registerResource(resource3); + registry.registerResource(resource4); + + registry.addResourceRef(resource1, sceneId); + registry.addResourceRef(resource2, sceneId); + registry.addResourceRef(resource3, sceneId); + registry.addResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); + + registry.setResourceData(resource4, testManagedResource); + registry.setResourceUploaded(resource4, DeviceResourceHandle{ 123u }, 666u); + + const ResourceContentHashVector& resources = registry.getAllResourcesNotInUseByScenes(); + EXPECT_TRUE(resources.empty()); + + registry.removeResourceRef(resource1, sceneId); + registry.removeResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource2, resource3 }); + + ASSERT_EQ(1u, resources.size()); + EXPECT_TRUE(contains_c(resources, resource4)); + + EXPECT_FALSE(registry.containsResource(resource1)); + } + + TEST_F(ARendererResourceRegistry, previouslyUnusedResourceIsRemovedFromUnusedListWhenUsedAgain) + { + const SceneId sceneId(11u); + + const ResourceContentHash resource1(123u, 0u); + const ResourceContentHash resource2(124u, 0u); + const ResourceContentHash resource3(125u, 0u); + const ResourceContentHash resource4(126u, 0u); + + registry.registerResource(resource1); + registry.registerResource(resource2); + registry.registerResource(resource3); + registry.registerResource(resource4); + + registry.addResourceRef(resource1, sceneId); + registry.addResourceRef(resource2, sceneId); + registry.addResourceRef(resource3, sceneId); + registry.addResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); + + registry.removeResourceRef(resource1, sceneId); + registry.removeResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource2, resource3 }); + + EXPECT_FALSE(registry.containsResource(resource1)); + EXPECT_FALSE(registry.containsResource(resource4)); + + registry.registerResource(resource1); + registry.registerResource(resource4); + + registry.addResourceRef(resource1, sceneId); + registry.addResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource2, resource3, resource1, resource4 }); + + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } + + TEST_F(ARendererResourceRegistry, canReferenceResourceMultipleTimes) + { + const SceneId sceneId(11u); + + const ResourceContentHash resource1(123u, 0u); + const ResourceContentHash resource2(124u, 0u); + const ResourceContentHash resource3(125u, 0u); + const ResourceContentHash resource4(126u, 0u); + + registry.registerResource(resource1); + registry.registerResource(resource2); + registry.registerResource(resource3); + registry.registerResource(resource4); + + registry.addResourceRef(resource1, sceneId); + registry.addResourceRef(resource2, sceneId); + registry.addResourceRef(resource3, sceneId); + registry.addResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); + + registry.addResourceRef(resource1, sceneId); + registry.addResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); + + registry.removeResourceRef(resource1, sceneId); + registry.removeResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource1, resource2, resource3, resource4 }); + + registry.removeResourceRef(resource1, sceneId); + registry.removeResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource2, resource3 }); + + EXPECT_FALSE(registry.containsResource(resource1)); + EXPECT_FALSE(registry.containsResource(resource4)); + + registry.registerResource(resource1); + registry.registerResource(resource4); + + registry.addResourceRef(resource1, sceneId); + registry.addResourceRef(resource4, sceneId); + expectResourcesUsedByScene(sceneId, { resource2, resource3, resource1, resource4 }); + + EXPECT_TRUE(registry.getAllResourcesNotInUseByScenes().empty()); + } +} diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneControlLogicMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicMock.cpp similarity index 90% rename from renderer/RendererLib/RendererLib/test/RendererSceneControlLogicMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicMock.cpp index 8a844cff8..b292937be 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneControlLogicMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicMock.cpp @@ -10,12 +10,12 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { RendererSceneControlLogicMock::RendererSceneControlLogicMock() { // by default scene is unknown and logic returns invalid/default parameters - ON_CALL(*this, getSceneInfo(_, _, _, _)).WillByDefault(Invoke([](auto, auto& targetState, auto& ob, auto& renderOrder) + ON_CALL(*this, getSceneInfo(_, _, _, _)).WillByDefault(Invoke([](auto /*unused*/, auto& targetState, auto& ob, auto& renderOrder) { targetState = RendererSceneState::Unavailable; ob = OffscreenBufferHandle::Invalid(); diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneControlLogicMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicMock.h similarity index 85% rename from renderer/RendererLib/RendererLib/test/RendererSceneControlLogicMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicMock.h index fd0dfcf06..2ca9fba0f 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneControlLogicMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENECONTROLLOGICMOCK_H -#define RAMSES_RENDERERSCENECONTROLLOGICMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/RendererSceneControlLogic.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" -namespace ramses_internal +namespace ramses::internal { class RendererSceneControlLogicMock : public IRendererSceneControlLogic { @@ -25,5 +24,3 @@ namespace ramses_internal MOCK_METHOD(void, getSceneInfo, (SceneId, RendererSceneState&, OffscreenBufferHandle&, int32_t&), (const, override)); }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneControlLogicTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicTest.cpp similarity index 99% rename from renderer/RendererLib/RendererLib/test/RendererSceneControlLogicTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicTest.cpp index 7a90e5d3a..b4fd49cd8 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneControlLogicTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneControlLogicTest.cpp @@ -8,12 +8,12 @@ #include "gmock/gmock.h" #include "RendererSceneStateControlMock.h" -#include "RendererLib/RendererSceneControlLogic.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/RendererSceneControlLogic.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ARendererSceneControlLogic : public Test { diff --git a/renderer/RendererLib/RendererTestCommon/RendererSceneEventSenderMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneEventSenderMock.h similarity index 88% rename from renderer/RendererLib/RendererTestCommon/RendererSceneEventSenderMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneEventSenderMock.h index 1d0028250..42b97a80f 100644 --- a/renderer/RendererLib/RendererTestCommon/RendererSceneEventSenderMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneEventSenderMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENEEVENTSENDERMOCK_H -#define RAMSES_RENDERERSCENEEVENTSENDERMOCK_H +#pragma once -#include "RendererFramework/IRendererSceneEventSender.h" +#include "internal/RendererLib/IRendererSceneEventSender.h" #include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class RendererSceneEventSenderMock : public IRendererSceneEventSender { @@ -28,4 +27,4 @@ namespace ramses_internal MOCK_METHOD(void, sendDataUnlinked, (SceneId masterScene, SceneId consumerScene, DataSlotId consumer, bool success), (override)); }; } -#endif + diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneResourceRegistryTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneResourceRegistryTest.cpp new file mode 100644 index 000000000..a5e67d785 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneResourceRegistryTest.cpp @@ -0,0 +1,175 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RendererSceneResourceRegistry.h" +#include "internal/SceneGraph/SceneAPI/EDataBufferType.h" + +using namespace testing; + +namespace ramses::internal +{ + class ARendererSceneResourceRegistry : public ::testing::Test + { + public: + protected: + RendererSceneResourceRegistry registry; + }; + + TEST_F(ARendererSceneResourceRegistry, doesNotContainAnythingInitially) + { + RenderBufferHandleVector rbs; + RenderTargetHandleVector rts; + BlitPassHandleVector bps; + DataBufferHandleVector dbs; + TextureBufferHandleVector tbs; + + registry.getAllRenderBuffers(rbs); + registry.getAllRenderTargets(rts); + registry.getAllBlitPasses(bps); + registry.getAllDataBuffers(dbs); + registry.getAllTextureBuffers(tbs); + + + EXPECT_TRUE(rbs.empty()); + EXPECT_TRUE(rts.empty()); + EXPECT_TRUE(bps.empty()); + EXPECT_TRUE(dbs.empty()); + EXPECT_TRUE(tbs.empty()); + } + + TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveRenderBuffer) + { + const RenderBufferHandle rb(13u); + const DeviceResourceHandle deviceHandle(123u); + registry.addRenderBuffer(rb, deviceHandle, 0u, false); + + EXPECT_EQ(deviceHandle, registry.getRenderBufferDeviceHandle(rb)); + + RenderBufferHandleVector rbs; + registry.getAllRenderBuffers(rbs); + ASSERT_EQ(1u, rbs.size()); + EXPECT_EQ(rb, rbs[0]); + rbs.clear(); + + registry.removeRenderBuffer(rb); + registry.getAllRenderBuffers(rbs); + EXPECT_TRUE(rbs.empty()); + } + + TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveRenderTarget) + { + const RenderTargetHandle rt(13u); + const DeviceResourceHandle deviceHandle(123u); + registry.addRenderTarget(rt, deviceHandle); + + EXPECT_EQ(deviceHandle, registry.getRenderTargetDeviceHandle(rt)); + + RenderTargetHandleVector rts; + registry.getAllRenderTargets(rts); + ASSERT_EQ(1u, rts.size()); + EXPECT_EQ(rt, rts[0]); + rts.clear(); + + registry.removeRenderTarget(rt); + registry.getAllRenderTargets(rts); + EXPECT_TRUE(rts.empty()); + } + + TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveBlitPass) + { + const BlitPassHandle bp(13u); + const DeviceResourceHandle deviceHandle1(123u); + const DeviceResourceHandle deviceHandle2(124u); + registry.addBlitPass(bp, deviceHandle1, deviceHandle2); + + DeviceResourceHandle deviceHandle1actual; + DeviceResourceHandle deviceHandle2actual; + registry.getBlitPassDeviceHandles(bp, deviceHandle1actual, deviceHandle2actual); + EXPECT_EQ(deviceHandle1, deviceHandle1actual); + EXPECT_EQ(deviceHandle2, deviceHandle2actual); + + BlitPassHandleVector bps; + registry.getAllBlitPasses(bps); + ASSERT_EQ(1u, bps.size()); + EXPECT_EQ(bp, bps[0]); + bps.clear(); + + registry.removeBlitPass(bp); + registry.getAllBlitPasses(bps); + EXPECT_TRUE(bps.empty()); + } + + TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveDataBuffers) + { + const DataBufferHandle db(13u); + const DeviceResourceHandle deviceHandle(123u); + const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; + registry.addDataBuffer(db, deviceHandle, dataBufferType, 0u); + + DataBufferHandleVector dbs; + registry.getAllDataBuffers(dbs); + ASSERT_EQ(1u, dbs.size()); + EXPECT_EQ(db, dbs[0]); + dbs.clear(); + + registry.removeDataBuffer(db); + registry.getAllDataBuffers(dbs); + EXPECT_TRUE(dbs.empty()); + } + + TEST_F(ARendererSceneResourceRegistry, canGetDataBufferDeviceHandle) + { + const DataBufferHandle db(13u); + const DeviceResourceHandle deviceHandle(123u); + const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; + registry.addDataBuffer(db, deviceHandle, dataBufferType, 0u); + + EXPECT_EQ(deviceHandle, registry.getDataBufferDeviceHandle(db)); + registry.removeDataBuffer(db); + } + + TEST_F(ARendererSceneResourceRegistry, canGetDataBufferType) + { + const DataBufferHandle db(13u); + const DeviceResourceHandle deviceHandle(123u); + const EDataBufferType dataBufferType = EDataBufferType::IndexBuffer; + registry.addDataBuffer(db, deviceHandle, dataBufferType, 0u); + + EXPECT_EQ(dataBufferType, registry.getDataBufferType(db)); + registry.removeDataBuffer(db); + } + + TEST_F(ARendererSceneResourceRegistry, canAddAndRemoveTextureBuffers) + { + const TextureBufferHandle tb(13u); + const DeviceResourceHandle deviceHandle(123u); + registry.addTextureBuffer(tb, deviceHandle, EPixelStorageFormat::RG8, 0u); + + TextureBufferHandleVector tbs; + registry.getAllTextureBuffers(tbs); + ASSERT_EQ(1u, tbs.size()); + EXPECT_EQ(tb, tbs[0]); + tbs.clear(); + + registry.removeTextureBuffer(tb); + registry.getAllTextureBuffers(tbs); + EXPECT_TRUE(tbs.empty()); + } + + TEST_F(ARendererSceneResourceRegistry, canGetTextureBufferAttributes) + { + const TextureBufferHandle tb(13u); + const DeviceResourceHandle deviceHandle(123u); + registry.addTextureBuffer(tb, deviceHandle, EPixelStorageFormat::RG8, 0u); + + EXPECT_EQ(deviceHandle, registry.getTextureBufferDeviceHandle(tb)); + EXPECT_EQ(EPixelStorageFormat::RG8, registry.getTextureBufferFormat(tb)); + registry.removeTextureBuffer(tb); + } +} diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneStateControlMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneStateControlMock.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/test/RendererSceneStateControlMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneStateControlMock.cpp index 7948ebaa3..974c83f60 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneStateControlMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneStateControlMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { RendererSceneStateControlMock::RendererSceneStateControlMock() { diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneStateControlMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneStateControlMock.h similarity index 88% rename from renderer/RendererLib/RendererLib/test/RendererSceneStateControlMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneStateControlMock.h index b60e2b49d..3dfa1b5c2 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneStateControlMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneStateControlMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENESTATECONTROLMOCK_H -#define RAMSES_RENDERERSCENESTATECONTROLMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/IRendererSceneStateControl.h" +#include "internal/RendererLib/IRendererSceneStateControl.h" -namespace ramses_internal +namespace ramses::internal { class RendererSceneStateControlMock : public IRendererSceneStateControl { @@ -29,5 +28,3 @@ namespace ramses_internal MOCK_METHOD(bool, handleSceneDisplayBufferAssignmentRequest, (SceneId sceneId, OffscreenBufferHandle buffer, int32_t sceneRenderOrder), (override)); }; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/RendererSceneUpdaterFacade.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterFacade.cpp similarity index 79% rename from renderer/RendererLib/RendererTestCommon/RendererSceneUpdaterFacade.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterFacade.cpp index e6c1cf6b1..b8c6219cb 100644 --- a/renderer/RendererLib/RendererTestCommon/RendererSceneUpdaterFacade.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterFacade.cpp @@ -7,13 +7,9 @@ // ------------------------------------------------------------------------- #include "RendererSceneUpdaterFacade.h" -#include "Components/SceneUpdate.h" -#include "RendererAPI/IEmbeddedCompositingManager.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererLib/IResourceUploader.h" -#include "Watchdog/IThreadAliveNotifier.h" +#include "internal/Components/SceneUpdate.h" -namespace ramses_internal +namespace ramses::internal { RendererSceneUpdaterPartialMock::RendererSceneUpdaterPartialMock( DisplayHandle display, @@ -24,8 +20,7 @@ namespace ramses_internal const RendererEventCollector& rendererEventCollector, const FrameTimer& frameTimer, const SceneExpirationMonitor& expirationMonitor, - IThreadAliveNotifier& notifier, - const IRendererResourceCache* rendererResourceCache) + IThreadAliveNotifier& notifier) : RendererSceneUpdater(display, platform, const_cast(renderer), @@ -34,8 +29,7 @@ namespace ramses_internal const_cast(rendererEventCollector), const_cast(frameTimer), const_cast(expirationMonitor), - const_cast(notifier), - const_cast(rendererResourceCache)) + const_cast(notifier)) { } @@ -50,9 +44,8 @@ namespace ramses_internal const RendererEventCollector& rendererEventCollector, const FrameTimer& frameTimer, const SceneExpirationMonitor& expirationMonitor, - IThreadAliveNotifier& notifier, - const IRendererResourceCache* rendererResourceCache) - : RendererSceneUpdaterPartialMock(display, platform, renderer, rendererScenes, sceneStateExecutor, rendererEventCollector, frameTimer, expirationMonitor, notifier, rendererResourceCache) + IThreadAliveNotifier& notifier) + : RendererSceneUpdaterPartialMock(display, platform, renderer, rendererScenes, sceneStateExecutor, rendererEventCollector, frameTimer, expirationMonitor, notifier) { } @@ -86,15 +79,9 @@ namespace ramses_internal IBinaryShaderCache* binaryShaderCache) { RendererSceneUpdaterPartialMock::createResourceManager(renderBackend, embeddedCompositingManager, displayConfig, binaryShaderCache); - testing::StrictMock* resMgrMock = new testing::StrictMock; + auto* resMgrMock = new testing::StrictMock; assert(m_resourceManagerMock == nullptr); m_resourceManagerMock = resMgrMock; return std::unique_ptr{ resMgrMock }; } - - void RendererSceneUpdaterFacade::destroyResourceManager() - { - RendererSceneUpdaterPartialMock::destroyResourceManager(); - RendererSceneUpdater::destroyResourceManager(); // NOLINT clang-tidy: We really mean to call into RendererSceneUpdater - } } diff --git a/renderer/RendererLib/RendererTestCommon/RendererSceneUpdaterFacade.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterFacade.h similarity index 81% rename from renderer/RendererLib/RendererTestCommon/RendererSceneUpdaterFacade.h rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterFacade.h index e1637295e..4a27192f3 100644 --- a/renderer/RendererLib/RendererTestCommon/RendererSceneUpdaterFacade.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterFacade.h @@ -6,35 +6,31 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENEUPDATERFACADE_H -#define RAMSES_RENDERERSCENEUPDATERFACADE_H +#pragma once -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererSceneUpdater.h" #include "RendererResourceManagerMock.h" +#include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { class RendererSceneUpdaterPartialMock : public RendererSceneUpdater { public: RendererSceneUpdaterPartialMock(DisplayHandle display, IPlatform& platform, const Renderer& renderer, const RendererScenes& rendererScenes, const SceneStateExecutor& sceneStateExecutor, - const RendererEventCollector& rendererEventCollector, const FrameTimer& frameTimer, const SceneExpirationMonitor& expirationMonitor, IThreadAliveNotifier& notifier, const IRendererResourceCache* rendererResourceCache); + const RendererEventCollector& rendererEventCollector, const FrameTimer& frameTimer, const SceneExpirationMonitor& expirationMonitor, IThreadAliveNotifier& notifier); ~RendererSceneUpdaterPartialMock() override; MOCK_METHOD(void, handleSceneUpdate, (SceneId sceneId, SceneUpdate&& update), (override)); MOCK_METHOD(void, handlePickEvent, (SceneId sceneId, glm::vec2 coords), (override)); MOCK_METHOD(std::unique_ptr, createResourceManager, (IRenderBackend&, IEmbeddedCompositingManager&, const DisplayConfig&, IBinaryShaderCache*), (override)); - MOCK_METHOD(void, destroyResourceManager, (), (override)); }; class RendererSceneUpdaterFacade : public RendererSceneUpdaterPartialMock { public: RendererSceneUpdaterFacade(DisplayHandle display, IPlatform& platform, const Renderer& renderer, const RendererScenes& rendererScenes, const SceneStateExecutor& sceneStateExecutor, - const RendererEventCollector& rendererEventCollector, const FrameTimer& frameTimer, const SceneExpirationMonitor& expirationMonitor, IThreadAliveNotifier& notifier, const IRendererResourceCache* rendererResourceCache); + const RendererEventCollector& rendererEventCollector, const FrameTimer& frameTimer, const SceneExpirationMonitor& expirationMonitor, IThreadAliveNotifier& notifier); ~RendererSceneUpdaterFacade() override; void handleSceneUpdate(SceneId sceneId, SceneUpdate&& update) override; @@ -44,15 +40,16 @@ namespace ramses_internal testing::StrictMock* m_resourceManagerMock = nullptr; + [[nodiscard]] bool hasResourceManager() const + { + return RendererSceneUpdaterPartialMock::hasResourceManager(); + } + protected: std::unique_ptr createResourceManager( IRenderBackend& renderBackend, IEmbeddedCompositingManager& embeddedCompositingManager, const DisplayConfig& displayConfig, IBinaryShaderCache* binaryShaderCache) override; - - void destroyResourceManager() override; }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterMock.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/test/RendererSceneUpdaterMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterMock.cpp index 1a174d7de..ff4ee2660 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { RendererSceneUpdaterMock::RendererSceneUpdaterMock() = default; RendererSceneUpdaterMock::~RendererSceneUpdaterMock() = default; diff --git a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterMock.h similarity index 90% rename from renderer/RendererLib/RendererLib/test/RendererSceneUpdaterMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterMock.h index 5f953e60f..685140191 100644 --- a/renderer/RendererLib/RendererLib/test/RendererSceneUpdaterMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterMock.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERSCENEUPDATERMOCK_H -#define RAMSES_RENDERERSCENEUPDATERMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/IRendererSceneUpdater.h" -#include "Components/SceneUpdate.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererAPI/IBinaryShaderCache.h" +#include "internal/RendererLib/IRendererSceneUpdater.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/PlatformInterface/IBinaryShaderCache.h" -namespace ramses_internal +namespace ramses::internal { class RendererSceneUpdaterMock : public IRendererSceneUpdater { @@ -29,14 +28,14 @@ namespace ramses_internal MOCK_METHOD(void, handleScenePublished, (SceneId sceneId, EScenePublicationMode mode), (override)); MOCK_METHOD(void, handleSceneUnpublished, (SceneId sceneId), (override)); MOCK_METHOD(void, handleSceneReceived, (const SceneInfo& sceneInfo), (override)); - MOCK_METHOD(bool, handleBufferCreateRequest, (OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, ERenderBufferType depthStencilBufferType), (override)); + MOCK_METHOD(bool, handleBufferCreateRequest, (OffscreenBufferHandle buffer, uint32_t width, uint32_t height, uint32_t sampleCount, bool isDoubleBuffered, EDepthBufferType depthStencilBufferType), (override)); MOCK_METHOD(bool, handleDmaBufferCreateRequest, (OffscreenBufferHandle buffer, uint32_t width, uint32_t height, DmaBufferFourccFormat format, DmaBufferUsageFlags dmaBufferUsageFlags, DmaBufferModifiers dmaBufferModifiers), (override)); MOCK_METHOD(bool, handleBufferDestroyRequest, (OffscreenBufferHandle buffer), (override)); MOCK_METHOD(bool, handleBufferCreateRequest, (StreamBufferHandle buffer, WaylandIviSurfaceId source), (override)); MOCK_METHOD(bool, handleBufferDestroyRequest, (StreamBufferHandle buffer), (override)); MOCK_METHOD(bool, handleExternalBufferCreateRequest, (ExternalBufferHandle), (override)); MOCK_METHOD(bool, handleExternalBufferDestroyRequest, (ExternalBufferHandle), (override)); - MOCK_METHOD(void, handleSetClearFlags, (OffscreenBufferHandle buffer, uint32_t), (override)); + MOCK_METHOD(void, handleSetClearFlags, (OffscreenBufferHandle buffer, ClearFlags), (override)); MOCK_METHOD(void, handleSetClearColor, (OffscreenBufferHandle buffer, const glm::vec4& clearColor), (override)); MOCK_METHOD(void, handleSetExternallyOwnedWindowSize, (uint32_t, uint32_t), (override)); MOCK_METHOD(void, handleReadPixels, (OffscreenBufferHandle buffer, ScreenshotInfo&& screenshotInfo), (override)); @@ -52,5 +51,3 @@ namespace ramses_internal MOCK_METHOD(void, logRendererInfo, (ERendererLogTopic topic, bool verbose, NodeHandle nodeFilter), (const, override)); }; } - -#endif diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.cpp new file mode 100644 index 000000000..003b9c12e --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.cpp @@ -0,0 +1,5245 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererSceneUpdaterTest.h" +#include + +namespace ramses::internal { + + /////////////////////////// + // General tests + /////////////////////////// + + TEST_F(ARendererSceneUpdater, doesNotGenerateRendererEventForUnnamedFlush) + { + createPublishAndSubscribeScene(); + expectNoEvent(); + + performFlush(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, generatesRendererEventForNamedFlush) + { + createPublishAndSubscribeScene(); + expectNoEvent(); + + const SceneVersionTag version(15u); + performFlush(0u, version); + update(); + + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[0].eventType); + EXPECT_EQ(version, events[0].sceneVersionTag); + } + + TEST_F(ARendererSceneUpdater, lastAppliedFlushVersionTagIsTracked) + { + createPublishAndSubscribeScene(); + expectNoEvent(); + + EXPECT_FALSE(rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag.isValid()); + + SceneVersionTag version{ 15u }; + performFlush(0u, version); + update(); + expectSceneEvent(ERendererEventType::SceneFlushed); + EXPECT_EQ(version, rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag); + + version = SceneVersionTag{ 1111u }; + performFlush(0u, version); + update(); + expectSceneEvent(ERendererEventType::SceneFlushed); + EXPECT_EQ(version, rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag); + + // invalid version tag is also tracked + performFlush(0u, SceneVersionTag::Invalid()); + update(); + EXPECT_FALSE(rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag.isValid()); + + version = SceneVersionTag{ 2222u }; + performFlush(0u, version); + update(); + expectSceneEvent(ERendererEventType::SceneFlushed); + EXPECT_EQ(version, rendererScenes.getStagingInfo(getSceneId()).lastAppliedVersionTag); + } + + TEST_F(ARendererSceneUpdater, preallocatesSceneForSizesProvidedInFlushInfo) + { + createPublishAndSubscribeScene(); + expectNoEvent(); + + // perform empty flush only with notification of scene size change + SceneSizeInformation sizeInfo; + sizeInfo.renderableCount = 12u; + performFlush(0u, SceneVersionTag::Invalid(), &sizeInfo); + update(); + + const IScene& rendererScene = rendererScenes.getScene(getSceneId()); + EXPECT_EQ(sizeInfo, rendererScene.getSceneSizeInformation()); + } + + TEST_F(ARendererSceneUpdater, ignoresSceneActionsForNotSubscribedScene) + { + createStagingScene(); + publishScene(); + + performFlush(0u, SceneVersionTag(1u)); + update(); + // there is no scene to apply actions to + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, ignoresSceneActionsForNotReceivedScene) + { + createStagingScene(); + publishScene(); + requestSceneSubscription(); + + performFlush(0u, SceneVersionTag(1u)); + update(); + // there is no scene to apply actions to + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, ignoresSceneActionsAddedAfterSceneWasUnsubscribed) + { + createPublishAndSubscribeScene(); + + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); + EXPECT_FALSE(rendererScenes.hasScene(getSceneId())); + + performFlush(0u, SceneVersionTag(1u)); + update(); + // there is no scene to apply actions to + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, ignoresSceneActionsAddedAfterSceneWasUnsubscribedByError) + { + createPublishAndSubscribeScene(); + + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), true); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribedIndirect); + EXPECT_FALSE(rendererScenes.hasScene(getSceneId())); + + performFlush(0u, SceneVersionTag(1u)); + update(); + // there is no scene to apply actions to + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, ignoresSceneActionsAddedBetweenUnsubscriptionAndResubscription) + { + createPublishAndSubscribeScene(); + + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); + + performFlush(0u, SceneVersionTag(1u)); + + EXPECT_CALL(sceneEventSender, sendSubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneSubscriptionRequest(getSceneId()); + rendererSceneUpdater->handleSceneReceived(SceneInfo(getSceneId())); + + // named flush ignored + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, canCreateAndDestroyDisplayContext) + { + createDisplayAndExpectSuccess(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, createDisplayFailsIfCreationOfResourceUploadRenderBackendFails) + { + EXPECT_CALL(renderer.m_platform, createResourceUploadRenderBackend()).WillOnce(Return(nullptr)); + EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); + rendererSceneUpdater->createDisplayContext({}, nullptr); + EXPECT_FALSE(renderer.hasDisplayController()); + expectEvent(ERendererEventType::DisplayCreateFailed); + } + + TEST_F(ARendererSceneUpdater, canNotDestroyNonExistantDisplay) + { + destroyDisplay(true); + } + + TEST_F(ARendererSceneUpdater, canNotDestroyDisplayIfItHasMapRequestedScenes) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + requestMapScene(); + + destroyDisplay(true); + + unpublishMapRequestedScene(); + destroyDisplay(); + + EXPECT_FALSE(renderer.hasDisplayController()); + } + + TEST_F(ARendererSceneUpdater, canNotDestroyDisplayIfItHasMappedScenes) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + destroyDisplay(true); + + unmapScene(); + destroyDisplay(); + + EXPECT_FALSE(renderer.hasDisplayController()); + } + + TEST_F(ARendererSceneUpdater, canNotDestroyDisplayIfItHasRenderedScenes) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + destroyDisplay(true); + EXPECT_TRUE(renderer.hasDisplayController()); + + hideScene(); + unmapScene(); + destroyDisplay(); + + EXPECT_FALSE(renderer.hasDisplayController()); + } + + TEST_F(ARendererSceneUpdater, canDestroyDisplayIfMappedSceneGetsUnmapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDestroyDisplayIfMappedSceneGetsUnpublished) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + unpublishMappedScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, destroyingSceneUpdaterUnmapsAnyMappedSceneFromRenderer) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(getSceneId()).isValid()); + } + + TEST_F(ARendererSceneUpdater, destroyingSceneUpdaterDestroysAllDisplayContexts) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + + EXPECT_FALSE(renderer.hasDisplayController()); + } + + TEST_F(ARendererSceneUpdater, renderOncePassesAreRetriggeredWhenSceneMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + auto& stageScene = *stagingScene[0]; + const RenderPassHandle pass = stageScene.allocateRenderPass(0, {}); + const auto dataLayout = stageScene.allocateDataLayout({DataFieldInfo{EDataType::Vector2I}, DataFieldInfo{EDataType::Vector2I}}, ResourceContentHash::Invalid(), {}); + const CameraHandle camera = stageScene.allocateCamera(ECameraProjectionType::Orthographic, stageScene.allocateNode(0, {}), stageScene.allocateDataInstance(dataLayout, {}), {}); + stageScene.setRenderPassCamera(pass, camera); + stageScene.setRenderPassRenderOnce(pass, true); + performFlush(); + + RendererCachedScene& scene = rendererScenes.getScene(stageScene.getSceneId()); + + // simulate render frame + // pass is in list to render + update(); + scene.markAllRenderOncePassesAsRendered(); + EXPECT_EQ(pass, scene.getSortedRenderingPasses()[0].getRenderPassHandle()); + + // simulate render frame + // pass is not in list to render anymore + update(); + scene.markAllRenderOncePassesAsRendered(); + EXPECT_TRUE(scene.getSortedRenderingPasses().empty()); + + // unmap scene + hideScene(); + unmapScene(); + update(); + scene.markAllRenderOncePassesAsRendered(); + + // remap scene and expect that render once pass is in list to render again + mapScene(); + showScene(); + update(); + scene.markAllRenderOncePassesAsRendered(); + EXPECT_EQ(pass, scene.getSortedRenderingPasses()[0].getRenderPassHandle()); + + // simulate render frame + // pass is not in list to render anymore + update(); + scene.markAllRenderOncePassesAsRendered(); + EXPECT_TRUE(scene.getSortedRenderingPasses().empty()); + + hideScene(); + unmapScene(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canHideSceneIfNotShownYet) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + update(); + + // request show and hide within same frame + rendererSceneUpdater->handleSceneShowRequest(getSceneId()); + rendererSceneUpdater->handleSceneHideRequest(getSceneId()); + expectInternalSceneStateEvents({ ERendererEventType::SceneShowFailed, ERendererEventType::SceneHidden }); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + + // show failed (was canceled) and scene is still in mapped state + update(); + expectNoEvent(); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canNotUnmapSceneWhichWasRequestedToBeShown) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + update(); + + rendererSceneUpdater->handleSceneShowRequest(getSceneId()); + + rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); + expectInternalSceneStateEvents({ ERendererEventType::SceneUnmapFailed }); + EXPECT_EQ(ESceneState::RenderRequested, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + expectInternalSceneStateEvent(ERendererEventType::SceneShown); + EXPECT_EQ(ESceneState::Rendered, sceneStateExecutor.getSceneState(getSceneId())); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canUnsubscribeSceneIfSubscriptionRequested) + { + createStagingScene(); + publishScene(); + requestSceneSubscription(); + // scene is now in subscription requested state, waiting for scene to arrive + update(); + + // request unsubscribe cancels the subscription and reports unsubscribed + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); + expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + expectNoEvent(); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + } + + TEST_F(ARendererSceneUpdater, canUnsubscribeSceneIfSubscriptionPending) + { + createStagingScene(); + publishScene(); + requestSceneSubscription(); + receiveScene(); + // scene is now in subscription pending state, scene arrived, waiting for initial flush + update(); + + // request unsubscribe cancels the subscription and reports unsubscribed + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(getSceneId(), false); + expectInternalSceneStateEvent(ERendererEventType::SceneUnsubscribed); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + expectNoEvent(); + EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(getSceneId())); + } + + + /////////////////////////// + // Offscreen buffer tests + /////////////////////////// + + TEST_F(ARendererSceneUpdater, canCreateOffscreenBuffer_WithColorBufferOnly) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, false, EDepthBufferType::None); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 4u, false, EDepthBufferType::None)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canCreateOffscreenBuffer_WithDepthStencilBuffers) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 4u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canCreateDoubleBufferedOffscreenBuffer_WithColorBufferOnly) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true, EDepthBufferType::None); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, EDepthBufferType::None)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canCreateDoubleBufferedOffscreenBuffer_WithDepthStencilBuffers) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canCreateDmaOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + constexpr OffscreenBufferHandle buffer(1u); + constexpr DmaBufferFourccFormat fourccFormat{ 123u }; + constexpr DmaBufferUsageFlags usageFlags{ 456u }; + constexpr DmaBufferModifiers modifiers{ 789u }; + + expectDmaOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, fourccFormat, usageFlags, modifiers); + + constexpr int resultFD = 111; + constexpr uint32_t resultStride = 222u; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getDmaOffscreenBufferFD(buffer)).WillOnce(Return(resultFD)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getDmaOffscreenBufferStride(buffer)).WillOnce(Return(resultStride)); + EXPECT_TRUE(rendererSceneUpdater->handleDmaBufferCreateRequest(buffer, 1u, 1u, fourccFormat, usageFlags, modifiers)); + + RendererEventVector rendererEvents; + RendererEventVector sceneEvents; + rendererEventCollector.appendAndConsumePendingEvents(rendererEvents, sceneEvents); + ASSERT_EQ(rendererEvents.size(), 1u); + EXPECT_EQ(sceneEvents.size(), 0u); + + const auto& event = rendererEvents.front(); + EXPECT_EQ(ERendererEventType::OffscreenBufferCreated, event.eventType); + EXPECT_EQ(Display, event.displayHandle); + EXPECT_EQ(buffer, event.offscreenBuffer); + EXPECT_EQ(resultFD, event.dmaBufferFD); + EXPECT_EQ(resultStride, event.dmaBufferStride); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateOffscreenBufferOnUnknownDisplay) + { + const OffscreenBufferHandle buffer(1u); + EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreateFailed); + } + + TEST_F(ARendererSceneUpdater, failsToCreateDmaOffscreenBufferOnUnknownDisplay) + { + const OffscreenBufferHandle buffer(1u); + constexpr DmaBufferFourccFormat fourccFormat{ 123u }; + constexpr DmaBufferUsageFlags usageFlags{ 456u }; + constexpr DmaBufferModifiers modifiers{ 789u }; + EXPECT_FALSE(rendererSceneUpdater->handleDmaBufferCreateRequest(buffer, 1u, 1u, fourccFormat, usageFlags, modifiers)); + expectEvent(ERendererEventType::OffscreenBufferCreateFailed); + } + + TEST_F(ARendererSceneUpdater, failsToCreateOffscreenBufferWithSameID) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreateFailed); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateDmaOffscreenBufferWithSameID) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + constexpr DmaBufferFourccFormat fourccFormat{ 123u }; + constexpr DmaBufferUsageFlags usageFlags{ 456u }; + constexpr DmaBufferModifiers modifiers{ 789u }; + + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_FALSE(rendererSceneUpdater->handleDmaBufferCreateRequest(buffer, 1u, 1u, fourccFormat, usageFlags, modifiers)); + expectEvent(ERendererEventType::OffscreenBufferCreateFailed); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDestroyOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDestroyDoubleBufferedOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToDestroyOffscreenBufferOnUnknownDisplay) + { + const OffscreenBufferHandle buffer(1u); + EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + } + + TEST_F(ARendererSceneUpdater, failsToDestroyUnknownOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canAssignSceneToOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + mapScene(0u); + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer, 11)); + + unmapScene(0u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canAssignSceneToFramebuffer_MultipleTimes) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + mapScene(0u); + // Scene can be always assigned to framebuffer + EXPECT_TRUE(assignSceneToDisplayBuffer(0)); + EXPECT_TRUE(assignSceneToDisplayBuffer(0)); + EXPECT_TRUE(assignSceneToDisplayBuffer(0)); + + unmapScene(0u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToAssignSceneToDisplayBufferOnInvalidDisplay) + { + createPublishAndSubscribeScene(); + EXPECT_FALSE(assignSceneToDisplayBuffer(0)); + } + + TEST_F(ARendererSceneUpdater, failsToAssignSceneToNonExistingDisplayBuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + + createPublishAndSubscribeScene(); + mapScene(0u); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_FALSE(assignSceneToDisplayBuffer(0, buffer)); + + unmapScene(0u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, confidence_failsToDestroyOffscreenBufferIfScenesAreAssignedToIt_DestroysAfterSceneIsAssignedToFramebuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + mapScene(0u); + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); + + EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0)); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + unmapScene(0u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, setsClearFlagsForOB) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + EXPECT_CALL(renderer, setClearFlags(DeviceMock::FakeRenderTargetDeviceHandle, ClearFlags(EClearFlag::Color))); + rendererSceneUpdater->handleSetClearFlags(buffer, EClearFlag::Color); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, setsClearFlagsForFBIfNoOBSpecified) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + EXPECT_CALL(renderer, setClearFlags(renderer.getDisplayController().getDisplayBuffer(), ClearFlags(EClearFlag::Color))); + rendererSceneUpdater->handleSetClearFlags(OffscreenBufferHandle::Invalid(), EClearFlag::Color); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotSetClearFlagsIfOBSpecifiedButNotFound) + { + createDisplayAndExpectSuccess(); + + constexpr OffscreenBufferHandle invalidOB{ 1234u }; + EXPECT_CALL(renderer, setClearFlags(_, _)).Times(0u); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(invalidOB)).WillOnce(Return(DeviceResourceHandle::Invalid())); + rendererSceneUpdater->handleSetClearFlags(invalidOB, EClearFlag::Color); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotSetClearFlagsIfDisplayInvalid) + { + EXPECT_CALL(renderer, setClearFlags(_, _)).Times(0u); + rendererSceneUpdater->handleSetClearFlags({}, EClearFlag::Color); + } + + TEST_F(ARendererSceneUpdater, setsClearColorForOB) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + + EXPECT_CALL(renderer, setClearColor(DeviceMock::FakeRenderTargetDeviceHandle, glm::vec4{ 1, 2, 3, 4 })); + rendererSceneUpdater->handleSetClearColor(buffer, { 1, 2, 3, 4 }); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, setsClearColorForFBIfNoOBSpecified) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + EXPECT_CALL(renderer, setClearColor(renderer.getDisplayController().getDisplayBuffer(), glm::vec4{ 1, 2, 3, 4 })); + rendererSceneUpdater->handleSetClearColor(OffscreenBufferHandle::Invalid(), { 1, 2, 3, 4 }); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotSetClearColorIfOBSpecifiedButNotFound) + { + createDisplayAndExpectSuccess(); + + constexpr OffscreenBufferHandle invalidOB{ 1234u }; + EXPECT_CALL(renderer, setClearColor(_, _)).Times(0u); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(invalidOB)).WillOnce(Return(DeviceResourceHandle::Invalid())); + rendererSceneUpdater->handleSetClearColor(invalidOB, { 1, 2, 3, 4 }); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotSetClearColorIfDisplayInvalid) + { + EXPECT_CALL(renderer, setClearColor(_, _)).Times(0u); + rendererSceneUpdater->handleSetClearColor({}, { 1, 2, 3, 4 }); + } + + TEST_F(ARendererSceneUpdater, resizesExternallyOwnedDisplayWindow) + { + createDisplayAndExpectSuccess(); + + EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(123u, 456u)).WillOnce(Return(true)); + rendererSceneUpdater->handleSetExternallyOwnedWindowSize(123u, 456u); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotResizeExternallyOwnedDisplayWindowIfDisplayInvalid) + { + EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(_, _)).Times(0); + rendererSceneUpdater->handleSetExternallyOwnedWindowSize(123u, 456u); + } + + TEST_F(ARendererSceneUpdater, readPixelsFromDisplayFramebuffer) + { + createDisplayAndExpectSuccess(); + + const uint32_t x = 1u; + const uint32_t y = 2u; + const uint32_t width = 3u; + const uint32_t height = 4u; + const bool fullScreen = false; + const bool sendViaDLT = false; + const std::string_view filename; + + readPixels({}, x, y, width, height, fullScreen, sendViaDLT, filename); + expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, x, y, width, height); + doRenderLoop(); + rendererSceneUpdater->processScreenshotResults(); + expectReadPixelsEvents({ {OffscreenBufferHandle::Invalid(), true} }); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, readPixelsFromOffscreenbuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 10u, 10u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + const uint32_t x = 1u; + const uint32_t y = 2u; + const uint32_t width = 3u; + const uint32_t height = 4u; + const bool fullScreen = false; + const bool sendViaDLT = false; + const std::string_view filename; + + readPixels(buffer, x, y, width, height, fullScreen, sendViaDLT, filename); + expectDisplayControllerReadPixels(DeviceMock::FakeRenderTargetDeviceHandle, x, y, width, height); + doRenderLoop(); + rendererSceneUpdater->processScreenshotResults(); + expectReadPixelsEvents({ {buffer, true} }); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, createsReadPixelsFailedEventIfInvalidOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + + const uint32_t x = 1u; + const uint32_t y = 2u; + const uint32_t width = 3u; + const uint32_t height = 4u; + const bool fullScreen = false; + const bool sendViaDLT = false; + const std::string_view filename; + + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + readPixels(buffer, x, y, width, height, fullScreen, sendViaDLT, filename); + + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + doRenderLoop(); + expectReadPixelsEvents({ {buffer, false} }); + + rendererSceneUpdater->processScreenshotResults(); + expectNoEvent(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, createsReadPixelsFailedEventIfRectangleIsOutOfDisplayBounds) + { + const auto displayWidth = WindowMock::FakeWidth; + const auto displayHeight = WindowMock::FakeHeight; + DisplayConfig dispConfig; + dispConfig.setDesiredWindowWidth(displayWidth); + dispConfig.setDesiredWindowHeight(displayHeight); + createDisplayAndExpectSuccess(dispConfig); + + const bool fullScreen = false; + const bool sendViaDLT = false; + const std::string_view filename; + + + readPixels({}, displayWidth, 0u, 1u, 1u, fullScreen, sendViaDLT, filename); + readPixels({}, 0u, displayHeight, 1u, 1u, fullScreen, sendViaDLT, filename); + readPixels({}, 0u, 0u, displayWidth + 1u, 1u, fullScreen, sendViaDLT, filename); + readPixels({}, 0u, 0u, 0u, displayHeight + 1u, fullScreen, sendViaDLT, filename); + + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + doRenderLoop(); + + expectReadPixelsEvents({ { OffscreenBufferHandle::Invalid(), false }, + { OffscreenBufferHandle::Invalid(), false }, + { OffscreenBufferHandle::Invalid(), false }, + { OffscreenBufferHandle::Invalid(), false } }); + + rendererSceneUpdater->processScreenshotResults(); + expectNoEvent(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, createsReadPixelsFailedEventIfRectangleIsOutOfOffscreenBufferBounds) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 10u, 10u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + const bool fullScreen = false; + const bool sendViaDLT = false; + const std::string_view filename; + + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).Times(4).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + readPixels(buffer, 10, 0u, 1u, 1u, fullScreen, sendViaDLT, filename); + readPixels(buffer, 0u, 10, 1u, 1u, fullScreen, sendViaDLT, filename); + readPixels(buffer, 0u, 0u, 11u, 1u, fullScreen, sendViaDLT, filename); + readPixels(buffer, 0u, 0u, 0u, 11u, fullScreen, sendViaDLT, filename); + + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + doRenderLoop(); + + expectReadPixelsEvents({ { buffer, false }, + { buffer, false }, + { buffer, false }, + { buffer, false } }); + + rendererSceneUpdater->processScreenshotResults(); + expectNoEvent(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, readPixelsFromDisplayAndSaveToFileWithoutGeneratingEvent) + { + createDisplayAndExpectSuccess(); + + const uint32_t x = 1u; + const uint32_t y = 2u; + const uint32_t width = 3u; + const uint32_t height = 4u; + const bool fullScreen = false; + const bool sendViaDLT = false; + const std::string_view filename{"testScreenshot"}; + + readPixels({}, x, y, width, height, fullScreen, sendViaDLT, filename); + expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, x, y, width, height); + doRenderLoop(); + expectNoEvent(); + + rendererSceneUpdater->processScreenshotResults(); + expectNoEvent(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, readPixelsFromDisplayFullscreen) + { + const auto displayWidth = WindowMock::FakeWidth; + const auto displayHeight = WindowMock::FakeHeight; + DisplayConfig dispConfig; + dispConfig.setDesiredWindowWidth(displayWidth); + dispConfig.setDesiredWindowHeight(displayHeight); + createDisplayAndExpectSuccess(dispConfig); + + const uint32_t x = 1u; + const uint32_t y = 2u; + const uint32_t width = 3u; + const uint32_t height = 4u; + const bool fullScreen = true; + const bool sendViaDLT = false; + const std::string_view filename; + + readPixels({}, x, y, width, height, fullScreen, sendViaDLT, filename); + expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, 0, 0, WindowMock::FakeWidth, WindowMock::FakeHeight); + doRenderLoop(); + + rendererSceneUpdater->processScreenshotResults(); + expectReadPixelsEvents({ {OffscreenBufferHandle::Invalid(), true} }); + + destroyDisplay(); + } + + /////////////////////////// + // Stream buffer tests + /////////////////////////// + + TEST_F(ARendererSceneUpdater, canCreateStreamBuffer) + { + createDisplayAndExpectSuccess(); + + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + expectStreamBufferUploaded(buffer, source); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateStreamBufferOnUnknownDisplay) + { + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + } + + TEST_F(ARendererSceneUpdater, failsToCreateStreamBufferWithSameID) + { + createDisplayAndExpectSuccess(); + + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + expectStreamBufferUploaded(buffer, source); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + EXPECT_FALSE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDestroyStreamBuffer) + { + createDisplayAndExpectSuccess(); + + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + expectStreamBufferUploaded(buffer, source); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + expectStreamBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToDestroyStreamBufferOnUnknownDisplay) + { + constexpr StreamBufferHandle buffer{ 1u }; + EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + } + + TEST_F(ARendererSceneUpdater, failsToDestroyNonExistentStreamBuffer) + { + createDisplayAndExpectSuccess(); + + constexpr StreamBufferHandle buffer{ 1u }; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + EXPECT_FALSE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + /////////////////////////// + // External buffer tests + /////////////////////////// + + TEST_F(ARendererSceneUpdater, canCreateExternalBuffer) + { + createDisplayAndExpectSuccess(); + + constexpr ExternalBufferHandle buffer{ 1u }; + expectExternalBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreated); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, FailsToCreateExternalBufferIfResourceManagerFailsToUpload) + { + createDisplayAndExpectSuccess(); + + constexpr ExternalBufferHandle buffer{ 1u }; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadExternalBuffer(buffer)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferDeviceHandle(buffer)).Times(1u).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferGlId(buffer)).Times(0); + + EXPECT_FALSE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreateFailed); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateExternalBufferOnUnknownDisplay) + { + constexpr ExternalBufferHandle buffer{ 1u }; + EXPECT_FALSE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreateFailed); + } + + TEST_F(ARendererSceneUpdater, canDestroyExternalBuffer) + { + createDisplayAndExpectSuccess(); + + constexpr ExternalBufferHandle buffer{ 1u }; + expectExternalBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreated); + + expectExternalBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferDestroyRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferDestroyed); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToDestroyExternalBufferOnUnknownDisplay) + { + constexpr ExternalBufferHandle buffer{ 1u }; + EXPECT_FALSE(rendererSceneUpdater->handleExternalBufferDestroyRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferDestroyFailed); + } + + /////////////////////////// + // Data linking tests + /////////////////////////// + + TEST_F(ARendererSceneUpdater, updatesDataLinks) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + DataInstanceHandle consumerDataRef; + const float providedValue(333.f); + createDataSlotsAndLinkThem(consumerDataRef, providedValue); + + update(); + + IScene& scene2 = rendererScenes.getScene(getSceneId(1u)); + const float consumedValue = scene2.getDataSingleFloat(consumerDataRef, DataFieldHandle(0u)); + EXPECT_FLOAT_EQ(providedValue, consumedValue); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, updatesDataLinksAlsoIfProviderSceneNotShown) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + // only consumer scene is mapped and shown + mapScene(1u); + showScene(1u); + + DataInstanceHandle consumerDataRef; + const float providedValue(333.f); + createDataSlotsAndLinkThem(consumerDataRef, providedValue); + + update(); + + IScene& scene2 = rendererScenes.getScene(getSceneId(1u)); + const float consumedValue = scene2.getDataSingleFloat(consumerDataRef, DataFieldHandle(0u)); + EXPECT_FLOAT_EQ(providedValue, consumedValue); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotUpdatesDataLinksIfConsumerSceneNotShown) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + DataInstanceHandle consumerDataRef; + const float providedValue(333.f); + createDataSlotsAndLinkThem(consumerDataRef, providedValue); + + hideScene(1u); + update(); + + IScene& scene2 = rendererScenes.getScene(getSceneId(1u)); + const float consumedValue = scene2.getDataSingleFloat(consumerDataRef, DataFieldHandle(0u)); + EXPECT_FLOAT_EQ(0.f, consumedValue); + + hideScene(0u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, removesTextureLinkWhenProviderSceneUnmapped) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); + createTextureSlotsAndLinkThem(); + update(); + + hideScene(0u); + unmapScene(0u); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, removesTextureLinkWhenConsumerSceneUnmapped) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); + createTextureSlotsAndLinkThem(); + update(); + + hideScene(1u); + unmapScene(1u); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + unmapScene(0u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateTextureLinkIfProviderSceneNotMapped) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(1u); + showScene(1u); + + createTextureSlotsAndLinkThem(nullptr, 0u, 1u, false); + update(); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateTextureLinkIfBothProviderAndConsumerSceneNotMapped) + { + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + createTextureSlotsAndLinkThem(nullptr, 0u, 1u, false); + update(); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(getSceneId(1u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(getSceneId(1u))); + } + + TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenBufferDestroyed_OB) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + const DataSlotId consumerId1 = createTextureConsumer(0u); + const DataSlotId consumerId2 = createTextureConsumer(1u); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + createBufferLink(buffer, getSceneId(0u), consumerId1); + createBufferLink(buffer, getSceneId(1u), consumerId2); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenBufferDestroyed_SB) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + const DataSlotId consumerId1 = createTextureConsumer(0u); + const DataSlotId consumerId2 = createTextureConsumer(1u); + + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + expectStreamBufferUploaded(buffer, source); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + createBufferLink(buffer, getSceneId(0u), consumerId1); + createBufferLink(buffer, getSceneId(1u), consumerId2); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + expectStreamBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenBufferDestroyed_EB) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + const DataSlotId consumerId1 = createTextureConsumer(0u, TextureSampler::ContentType::ExternalTexture); + const DataSlotId consumerId2 = createTextureConsumer(1u, TextureSampler::ContentType::ExternalTexture); + + constexpr ExternalBufferHandle buffer{ 1u }; + expectExternalBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreated); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + createBufferLink(buffer, getSceneId(0u), consumerId1); + createBufferLink(buffer, getSceneId(1u), consumerId2); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + expectExternalBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferDestroyRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferDestroyed); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenConsumerSceneUnmapped_keepsOtherConsumerLinked_OB) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + const DataSlotId consumerId1 = createTextureConsumer(0u); + const DataSlotId consumerId2 = createTextureConsumer(1u); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + createBufferLink(buffer, getSceneId(0u), consumerId1); + createBufferLink(buffer, getSceneId(1u), consumerId2); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + unmapScene(0u); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenConsumerSceneUnmapped_keepsOtherConsumerLinked_SB) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + const DataSlotId consumerId1 = createTextureConsumer(0u); + const DataSlotId consumerId2 = createTextureConsumer(1u); + + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + expectStreamBufferUploaded(buffer, source); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + createBufferLink(buffer, getSceneId(0u), consumerId1); + createBufferLink(buffer, getSceneId(1u), consumerId2); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + unmapScene(0u); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, triggersRemovalOfBufferLinkWhenConsumerSceneUnmapped_keepsOtherConsumerLinked_EB) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0u); + mapScene(1u); + + showScene(0u); + showScene(1u); + + const DataSlotId consumerId1 = createTextureConsumer(0u, TextureSampler::ContentType::ExternalTexture); + const DataSlotId consumerId2 = createTextureConsumer(1u, TextureSampler::ContentType::ExternalTexture); + + constexpr ExternalBufferHandle buffer{ 1u }; + expectExternalBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreated); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + createBufferLink(buffer, getSceneId(0u), consumerId1); + createBufferLink(buffer, getSceneId(1u), consumerId2); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(0u); + unmapScene(0u); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(0u))); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToProvider(getSceneId(1u))); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfConsumerSceneNotOnDisplay_OB) + { + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createBufferLink(buffer, SceneId{ 666u }, DataSlotId{ 1u }, true); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfConsumerSceneNotOnDisplay_SB) + { + createDisplayAndExpectSuccess(); + + constexpr StreamBufferHandle buffer{ 1u }; + constexpr WaylandIviSurfaceId source{ 2u }; + expectStreamBufferUploaded(buffer, source); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, source)); + + createBufferLink(buffer, SceneId{ 666u }, DataSlotId{ 1u }, true); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfConsumerSceneNotOnDisplay_EB) + { + createDisplayAndExpectSuccess(); + + constexpr ExternalBufferHandle buffer{ 1u }; + expectExternalBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleExternalBufferCreateRequest(buffer)); + expectEvent(ERendererEventType::ExternalBufferCreated); + + createBufferLink(buffer, SceneId{ 666u }, DataSlotId{ 1u }, true); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfDisplayInvalid) + { + createBufferLink(OffscreenBufferHandle{ 3u }, SceneId{ 666u }, DataSlotId{ 1u }, true); + createBufferLink(StreamBufferHandle{ 3u }, SceneId{ 666u }, DataSlotId{ 1u }, true); + } + + TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfBufferUnknown_OB) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + constexpr OffscreenBufferHandle buffer{ 123u }; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + createBufferLink(buffer, getSceneId(), DataSlotId{ 1u }, true); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getOffscreenBufferLinks().hasAnyLinksToConsumer(buffer)); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, failsToCreateBufferLinkIfBufferUnknown_EB) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + constexpr ExternalBufferHandle buffer{ 123u }; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + createBufferLink(buffer, getSceneId(), DataSlotId{ 1u }, true); + + EXPECT_FALSE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getExternalBufferLinks().hasAnyLinksToConsumer(buffer)); + + unmapScene(); + destroyDisplay(); + } + + ///////////////////////////////////////////// + // Other tests + ///////////////////////////////////////////// + + TEST_F(ARendererSceneUpdater, updateSceneStreamTexturesDirtinessGeneratesEventsForNewAndObsoleteStreamSurfaces) + { + createDisplayAndExpectSuccess(); + + const WaylandIviSurfaceId newStreamId(7563u); + const WaylandIviSurfaceId obsoleteStreamId(8883u); + const WaylandIviSurfaceIdVector newStreams{ newStreamId }; + const WaylandIviSurfaceIdVector obsoleteStreams{ obsoleteStreamId }; + + expectNoEvent(); + + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(DoAll(SetArgReferee<1>(newStreams), SetArgReferee<2>(obsoleteStreams))); + + update(); + + RendererEventVector resultEvents; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, resultEvents); + ASSERT_EQ(2u, resultEvents.size()); + EXPECT_EQ(ERendererEventType::StreamSurfaceUnavailable, resultEvents[0].eventType); + EXPECT_EQ(obsoleteStreamId, resultEvents[0].streamSourceId); + EXPECT_EQ(ERendererEventType::StreamSurfaceAvailable, resultEvents[1].eventType); + EXPECT_EQ(newStreamId, resultEvents[1].streamSourceId); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, willShowSceneEvenIfFlushesPending) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + performFlushWithCreateNodeAction(); + mapScene(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + + // simulate effect not uploaded yet + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + // scene shown even if flush pending + showScene(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, appliesBigPendingWithinOneUpdate) + { + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + performFlushWithCreateNodeAction(0, 10000); + performFlushWithCreateNodeAction(1, 10000); + performFlushWithCreateNodeAction(0, 10000); + performFlushWithCreateNodeAction(1, 10000); + performFlushWithCreateNodeAction(0, 10000); + performFlushWithCreateNodeAction(1, 10000); + + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(0u)); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(1u)); + } + + ///////////////////////////////////////////// + // Tests for marking scenes as modified + ///////////////////////////////////////////// + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfStreamBufferStateIsNotUpdated) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + constexpr StreamBufferHandle streamBuffer{ 13u }; + + const auto dataSlotId = createRenderableWithTextureConsumer(); + createBufferLink(streamBuffer, getSceneId(0u), dataSlotId); + update(); + + expectRenderableResourcesClean(); + + constexpr WaylandIviSurfaceId source{ 12u }; + const StreamUsage fakeStreamUsage{ {}, { streamBuffer} }; + const WaylandIviSurfaceIdVector changedSources{ source }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); + update(); + expectRenderableResourcesClean(); + + { + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)); + + expectNoModifiedScenesReportedToRenderer(); + update(); + } + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfStreamSourceContentIsUpdated_usedByStreamBufferLinkedToScenes) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + constexpr WaylandIviSurfaceId source{ 12u }; + constexpr StreamBufferHandle sb1{ 13u }; + constexpr StreamBufferHandle sb2{ 14u }; + const StreamUsage fakeStreamUsage{ sb1, sb2 }; + + // link both SBs to scenes + const auto dataSlotId1 = createTextureConsumer(0u); + const auto dataSlotId2 = createTextureConsumer(1u); + createBufferLink(sb1, getSceneId(0u), dataSlotId1); + createBufferLink(sb2, getSceneId(1u), dataSlotId2); + update(); + + StreamSourceUpdates updates{ {source, 1u } }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, hasUpdatedContentFromStreamSourcesToUpload()).WillOnce(Return(true)); + EXPECT_CALL(renderer.m_embeddedCompositingManager, uploadResourcesAndGetUpdates(_)).WillOnce(SetArgReferee<0>(updates)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); + + expectModifiedScenesReportedToRenderer({ 0u, 1u }); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfStreamSourceAvailabilityChanged_usedByStreamBufferLinkedToScenes) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + constexpr WaylandIviSurfaceId source{ 12u }; + constexpr StreamBufferHandle sb1{ 13u }; + constexpr StreamBufferHandle sb2{ 14u }; + const StreamUsage fakeStreamUsage{ sb1, sb2 }; + + // link both SBs to scenes + const auto dataSlotId1 = createTextureConsumer(0u); + const auto dataSlotId2 = createTextureConsumer(1u); + createBufferLink(sb1, getSceneId(0u), dataSlotId1); + createBufferLink(sb2, getSceneId(1u), dataSlotId2); + update(); + + WaylandIviSurfaceIdVector changedSources{ source }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); + + expectModifiedScenesReportedToRenderer({ 0u, 1u }); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, WillNotUnlinkStreamBuffer_IfStreamSourceBecameAvailabilityChanges_sameSource) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + constexpr WaylandIviSurfaceId source{ 12u }; + constexpr StreamBufferHandle sb1{ 13u }; + constexpr StreamBufferHandle sb2{ 14u }; + const StreamUsage fakeStreamUsage{ sb1, sb2 }; + + // link both SBs to scenes + const auto dataSlotId1 = createTextureConsumer(0u); + const auto dataSlotId2 = createTextureConsumer(1u); + createBufferLink(sb1, getSceneId(0u), dataSlotId1); + createBufferLink(sb2, getSceneId(1u), dataSlotId2); + update(); + + WaylandIviSurfaceIdVector changedSources{ source }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); + + expectModifiedScenesReportedToRenderer({ 0u, 1u }); + update(); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb1)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb2)); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, WillNotUnlinkStreamBuffer_IfOneOfStreamSourcesChangesAvailability) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + constexpr WaylandIviSurfaceId source1{ 12u }; + constexpr WaylandIviSurfaceId source2{ 13u }; + constexpr StreamBufferHandle sb1{ 13u }; + constexpr StreamBufferHandle sb2{ 14u }; + const StreamUsage fakeStreamUsage1{ {}, { sb1 } }; + const StreamUsage fakeStreamUsage2{ {}, { sb2 } }; + + // link both SBs to scenes + const auto dataSlotId1 = createTextureConsumer(0u); + const auto dataSlotId2 = createTextureConsumer(1u); + createBufferLink(sb1, getSceneId(0u), dataSlotId1); + createBufferLink(sb2, getSceneId(1u), dataSlotId2); + update(); + + const WaylandIviSurfaceIdVector changedSources{ source1, source2 }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source1)).WillOnce(ReturnRef(fakeStreamUsage1)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source2)).WillOnce(ReturnRef(fakeStreamUsage2)); + + expectModifiedScenesReportedToRenderer({ 1u }); + update(); + + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb1)); + EXPECT_TRUE(rendererScenes.getSceneLinksManager().getTextureLinkManager().getStreamBufferLinks().hasAnyLinksToConsumer(sb2)); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfNonEmptyFlushApplied) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + update(); + + performFlushWithCreateNodeAction(); + expectModifiedScenesReportedToRenderer(); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfEmptyFlushAppliedOnEmptyScene) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + update(); + + expectNoModifiedScenesReportedToRenderer(); + performFlush(0u); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfEmptyFlushAppliedOnNonEmptyScene) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + performFlushWithCreateNodeAction(); + expectModifiedScenesReportedToRenderer(); + update(); + + expectNoModifiedScenesReportedToRenderer(); + performFlush(0u); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfFlushAppliedWithResources) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + + expectModifiedScenesReportedToRenderer(); + expectVertexArrayUploaded(); + update(); + expectRenderableResourcesClean(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfPendingFlushNotApplied) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + + { + setRenderableResources(); + expectModifiedScenesReportedToRenderer(); + expectVertexArrayUploaded(); + update(); + expectRenderableResourcesClean(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + { + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + + expectNoModifiedScenesReportedToRenderer(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + } + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_AfterPendingFlushApplied) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + { + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectModifiedScenesReportedToRenderer(); + expectVertexArrayUploaded(); + update(); + expectRenderableResourcesClean(); + } + expectNoModifiedScenesReportedToRenderer(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + { + // blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).WillOnce(Return(DeviceResourceHandle::Invalid())).RetiresOnSaturation(); + } + expectNoModifiedScenesReportedToRenderer(); + expectVertexArrayUnloaded(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + { + // unblock flush + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); + } + expectModifiedScenesReportedToRenderer(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_IfSceneIsConsumerAndProviderSceneIsUpdated) + { + // s0 [modified] -> s1 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + DataInstanceHandle consumerDataRef; + DataInstanceHandle providerDataRef; + createDataSlotsAndLinkThem(consumerDataRef, 333.f, &providerDataRef); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + updateProviderDataSlot(0u, providerDataRef, 777.f); + performFlush(); + expectModifiedScenesReportedToRenderer({0u, 1u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_DataLinking_IfSceneIsProviderAndConsumerIsUpdated) + { + // s0 -> s1 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(1u); + showScene(1u); + + DataInstanceHandle consumerDataRef; + const float providedValue(333.f); + createDataSlotsAndLinkThem(consumerDataRef, providedValue); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + stagingScene[1u]->setDataSingleFloat(consumerDataRef, DataFieldHandle(0u), 324.f); + performFlush(1u); + expectModifiedScenesReportedToRenderer({1u}); //only consumer if marked as modified + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(1u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_IndirectlyDependantConsumersIfProviderUpdated) + { + // s0 [modified] -> s1 [modified] -> s2 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + DataInstanceHandle providerDataRef; + { + //link scene 1 to scene 0 + DataInstanceHandle consumerDataRef; + createDataSlotsAndLinkThem(consumerDataRef, 333.f, &providerDataRef); + update(); + } + + { + //linke scene 2 to scene 1 + DataInstanceHandle consumerDataRef2; + createDataSlotsAndLinkThem(consumerDataRef2, 666.f, nullptr, 1u, 2u); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + updateProviderDataSlot(0u, providerDataRef, 1.0f); + performFlush(); + expectModifiedScenesReportedToRenderer({0u, 1u, 2u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_OnlyDependantConsumersIfProviderUpdated) + { + // s0 -> s1 [modified] -> s2 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + { + //link scene 1 to scene 0 + DataInstanceHandle consumerDataRef; + createDataSlotsAndLinkThem(consumerDataRef, 333.f); + update(); + } + + DataInstanceHandle providerDataRef; + { + //linke scene 2 to scene 1 + DataInstanceHandle consumerDataRef2; + createDataSlotsAndLinkThem(consumerDataRef2, 666.f, &providerDataRef, 1u, 2u); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + updateProviderDataSlot(1u, providerDataRef, 1.0f); + performFlush(1u); + expectModifiedScenesReportedToRenderer({1u, 2u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_IfSceneConsumesFromAnotherConsumerOfDifferentLinkingType) + { + // tex linking : s0 -> s1 + // data ref linking : s1 [modified] -> s2 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + { + //tex. link scene 1 to scene 0 + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); + createTextureSlotsAndLinkThem(); + update(); + } + + DataInstanceHandle providerDataRef; + { + //data ref. linke scene 2 to scene 1 + DataInstanceHandle consumerDataRef2; + createDataSlotsAndLinkThem(consumerDataRef2, 666.f, &providerDataRef, 1u, 2u); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + updateProviderDataSlot(1u, providerDataRef, 1.0f); + performFlush(1u); + expectModifiedScenesReportedToRenderer({1u, 2u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_DataLinking_ConfidenceTest) + { + // s0 -> s1 [modified] -> s2 [modified] -> s3 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(1u); + mapScene(2u); + mapScene(3u); + showScene(1u); + showScene(2u); + showScene(3u); + + { + //link scene 1 to scene 0 + DataInstanceHandle consumerDataRef; + createDataSlotsAndLinkThem(consumerDataRef, 333.f); + update(); + } + + DataInstanceHandle providerDataRef; + { + //linke scene 2 to scene 1 + DataInstanceHandle consumerDataRef; + createDataSlotsAndLinkThem(consumerDataRef, 666.f, &providerDataRef, 1u, 2u); + update(); + } + + { + //linke scene 3 to scene 2 + DataInstanceHandle consumerDataRef; + createDataSlotsAndLinkThem(consumerDataRef, 766.f, nullptr, 2u, 3u); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + updateProviderDataSlot(1u, providerDataRef, 1.0f); + performFlush(1u); + expectModifiedScenesReportedToRenderer({1u, 2u, 3u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(1u); + hideScene(2u); + hideScene(3u); + unmapScene(1u); + unmapScene(2u); + unmapScene(3u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TextureLinking_IfSceneIsConsumerAndProviderSceneIsUpdated) + { + // s0 [modified] -> s1 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + showScene(0u); + mapScene(1u); + showScene(1u); + + DataSlotHandle providerDataSlotHandle; + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); + createTextureSlotsAndLinkThem(&providerDataSlotHandle); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + expectResourcesUnreferenced({ MockResourceHash::TextureHash }); + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash2 }); + updateProviderTextureSlot(0u, providerDataSlotHandle, MockResourceHash::TextureHash2); + performFlush(); + expectModifiedScenesReportedToRenderer({0u, 1u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TextureLinking_ConfidenceTest) + { + // s0 -> s1 [modified] -> s2 [modified] -> s3 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(); + mapScene(1u); + mapScene(2u); + mapScene(3u); + showScene(1u); + showScene(2u); + showScene(3u); + + { + //link scene 1 to scene 0 + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }); + createTextureSlotsAndLinkThem(); + update(); + } + + DataSlotHandle providerDataSlotHandle; + { + //link scene 2 to scene 1 + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, 1u); + createTextureSlotsAndLinkThem(&providerDataSlotHandle, 1u, 2u); + update(); + } + + { + //link scene 3 to scene 2 + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, 2u); + createTextureSlotsAndLinkThem(nullptr, 2u, 3u); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + expectResourcesUnreferenced({ MockResourceHash::TextureHash }, 1u); + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash2 }, 1u); + updateProviderTextureSlot(1u, providerDataSlotHandle, MockResourceHash::TextureHash2); + performFlush(1u); + expectModifiedScenesReportedToRenderer({1u, 2u, 3u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(1u); + hideScene(2u); + hideScene(3u); + unmapScene(); + unmapScene(1u); + unmapScene(2u); + unmapScene(3u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TransformationLinking_IfSceneIsConsumerAndProviderSceneIsUpdated) + { + // s0 [modified] -> s1 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + TransformHandle providerTransformHandle; + createTransformationSlotsAndLinkThem(&providerTransformHandle); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + stagingScene[0]->setTranslation(providerTransformHandle, {0.f, 1.f, 2.f}); + performFlush(); + expectModifiedScenesReportedToRenderer({0u, 1u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_TransformationLinking_ConfidenceTest) + { + // s0 -> s1 [modified] -> s2 [modified] -> s3 [modified] + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(); + mapScene(1u); + mapScene(2u); + mapScene(3u); + showScene(1u); + showScene(2u); + showScene(3u); + + { + //link scene 1 to scene 0 + createTransformationSlotsAndLinkThem(); + update(); + } + + TransformHandle providerTransformHandle; + { + //linke scene 2 to scene 1 + createTransformationSlotsAndLinkThem(&providerTransformHandle, 1u, 2u); + update(); + } + + { + //linke scene 3 to scene 2 + createTransformationSlotsAndLinkThem(nullptr, 2u, 3u); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + stagingScene[1]->setTranslation(providerTransformHandle, {0.f, 1.f, 2.f}); + performFlush(1u); + expectModifiedScenesReportedToRenderer({1u, 2u, 3u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(1u); + hideScene(2u); + hideScene(3u); + unmapScene(); + unmapScene(1u); + unmapScene(2u); + unmapScene(3u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_IfSceneConsumesFromModifiedOffscreenBuffer) + { + // s0 [modified] -> ob1 -> s1 [modified] + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); + + const DataSlotId consumerId = createTextureConsumer(1u); + createBufferLink(buffer, stagingScene[1u]->getSceneId() , consumerId); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update scene mapped to buffer + performFlushWithCreateNodeAction(0u); + //expect both scenes modified + expectModifiedScenesReportedToRenderer({0u, 1u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_IfSceneConsumesFromModifiedOffscreenBufferWithSeveralScenes) + { + // s0 -- + // |-> ob1 -> s2 [modified] + // s1 [modified] -- + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); + EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffer)); + + const DataSlotId consumerId = createTextureConsumer(2u); + createBufferLink(buffer, getSceneId(2u), consumerId); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update scene mapped to buffer + performFlushWithCreateNodeAction(1u); + + expectModifiedScenesReportedToRenderer({ 1u, 2u }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_OffscreenBufferLinking_UnmodifiedProviderToOffscreenBuffer) + { + // s0 -> ob1 -> s1 [modified] + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); + + const DataSlotId consumerId = createTextureConsumer(1u); + createBufferLink(buffer, stagingScene[1u]->getSceneId(), consumerId); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update consumer + performFlushWithCreateNodeAction(1u); + //expect only consumer modified + expectModifiedScenesReportedToRenderer({ 1u }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_TwoConsumersFromModifiedOffscreenBuffer) + { + // --> s1 [modified] + // s0 [modified] -> ob1-| + // --> s2 [modified] + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer)); + + { + const DataSlotId consumerId = createTextureConsumer(1u); + createBufferLink(buffer, stagingScene[1u]->getSceneId() , consumerId); + update(); + } + { + const DataSlotId consumerId = createTextureConsumer(2u); + createBufferLink(buffer, stagingScene[2u]->getSceneId() , consumerId); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update scene mapped to buffer + performFlushWithCreateNodeAction(0u); + //expect both scenes modified + expectModifiedScenesReportedToRenderer({0u, 1u, 2u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_IndirectConsumerFromModifiedOffscreenBuffer) + { + // s0 [modified] -> ob1 -> s1 [modified] -> ob2 -> s2 [modified] + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer1(1u); + expectOffscreenBufferUploaded(buffer1); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer1, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + const OffscreenBufferHandle buffer2(2u); + const DeviceResourceHandle offscreenBufferDeviceHandle(5556u); + expectOffscreenBufferUploaded(buffer2, offscreenBufferDeviceHandle); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer2, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer1)); + + { + EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffer2)); + const DataSlotId consumerId = createTextureConsumer(1u); + createBufferLink(buffer1, stagingScene[1u]->getSceneId() , consumerId); + update(); + } + { + const DataSlotId consumerId = createTextureConsumer(2u); + createBufferLink(buffer2, stagingScene[2u]->getSceneId() , consumerId); + update(); + } + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update scene mapped to buffer 1 + performFlushWithCreateNodeAction(0u); + //expect all scenes modified + expectModifiedScenesReportedToRenderer({0u, 1u, 2u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_OffscreenBufferLinking_ConsumerFromUnmodifiedOffscreenBuffer) + { + // s0 [modified] -> ob1 + // s1 -> ob2 -> s2 + createDisplayAndExpectSuccess(); + + const OffscreenBufferHandle buffer1(1u); + expectOffscreenBufferUploaded(buffer1); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer1, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + const OffscreenBufferHandle buffer2(2u); + const DeviceResourceHandle offscreenBufferDeviceHandle(5556u); + expectOffscreenBufferUploaded(buffer2, offscreenBufferDeviceHandle); + + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer2, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + mapScene(2u); + showScene(0u); + showScene(1u); + showScene(2u); + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffer1)); + EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffer2)); + + const DataSlotId consumerId = createTextureConsumer(2u); + createBufferLink(buffer2, stagingScene[2u]->getSceneId() , consumerId); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update scene mapped to buffer 1 + performFlushWithCreateNodeAction(0u); + //expect only this scene to be modified + expectModifiedScenesReportedToRenderer({0u}); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(0u); + hideScene(1u); + hideScene(2u); + unmapScene(0u); + unmapScene(1u); + unmapScene(2u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_ConfidenceTest1) + { + // --> s1 + // s0 -> ob0 -| --> s3 [modified] + // --> s2 [modified] -> ob1 -| + // --> s4 [modified] -> ob2 -> s5 [modified] + + createDisplayAndExpectSuccess(); + + const std::array buffers{ { OffscreenBufferHandle{ 1u }, OffscreenBufferHandle{ 2u }, OffscreenBufferHandle{ 3u } } }; + for (const auto buffer : buffers) + { + expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(buffer.asMemoryHandle())); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + } + + for (uint32_t i = 0u; i < 6; ++i) + { + createPublishAndSubscribeScene(); + mapScene(i); + showScene(i); + } + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffers[0])); + EXPECT_TRUE(assignSceneToDisplayBuffer(2, buffers[1])); + EXPECT_TRUE(assignSceneToDisplayBuffer(4, buffers[2])); + + { + const DataSlotId consumerId = createTextureConsumer(1u); + createBufferLink(buffers[0u], getSceneId(1u), consumerId); + } + { + const DataSlotId consumerId = createTextureConsumer(2u); + createBufferLink(buffers[0u], getSceneId(2u), consumerId); + } + { + const DataSlotId consumerId = createTextureConsumer(3u); + createBufferLink(buffers[1u], getSceneId(3u), consumerId); + } + { + const DataSlotId consumerId = createTextureConsumer(4u); + createBufferLink(buffers[1u], getSceneId(4u), consumerId); + } + { + const DataSlotId consumerId = createTextureConsumer(5u); + createBufferLink(buffers[2u], getSceneId(5u), consumerId); + } + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update s2 + performFlushWithCreateNodeAction(2u); + + expectModifiedScenesReportedToRenderer({ 2u, 3u, 4u, 5u }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + + for (uint32_t i = 0u; i < 6; ++i) + { + hideScene(i); + unmapScene(i); + } + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_OffscreenBufferLinking_ConfidenceTest2) + { + // s0 -- + // |-> ob0 -> s2 ------------> ob1 -- + // s1 -- |-> s4 [modified] + // s3 [modified] -> ob2 -- + + createDisplayAndExpectSuccess(); + + const std::array buffers{ { OffscreenBufferHandle{ 1u }, OffscreenBufferHandle{ 2u }, OffscreenBufferHandle{ 3u } } }; + for (const auto buffer : buffers) + { + expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(buffer.asMemoryHandle())); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + } + + for (uint32_t i = 0u; i < 5; ++i) + { + createPublishAndSubscribeScene(); + mapScene(i); + showScene(i); + } + + EXPECT_TRUE(assignSceneToDisplayBuffer(0, buffers[0])); + EXPECT_TRUE(assignSceneToDisplayBuffer(1, buffers[0])); + EXPECT_TRUE(assignSceneToDisplayBuffer(2, buffers[1])); + EXPECT_TRUE(assignSceneToDisplayBuffer(3, buffers[2])); + + { + const DataSlotId consumerId = createTextureConsumer(2u); + createBufferLink(buffers[0u], getSceneId(2u), consumerId); + } + { + const DataSlotId consumerId = createTextureConsumer(4u); + createBufferLink(buffers[1u], getSceneId(4u), consumerId); + } + { + const DataSlotId consumerId = createTextureConsumer(4u); + createBufferLink(buffers[2u], getSceneId(4u), consumerId); + } + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + //update s3 + performFlushWithCreateNodeAction(3u); + + expectModifiedScenesReportedToRenderer({ 3u, 4u }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + for (uint32_t i = 0u; i < 5; ++i) + { + hideScene(i); + unmapScene(i); + } + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfShaderAnimationIsActive) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + + createRenderable(); + setRenderableResources(); + + expectModifiedScenesReportedToRenderer(); + expectVertexArrayUploaded(); + update(); + expectRenderableResourcesClean(); + + // simulate rendering + auto& rendererScene = rendererScenes.getScene(stagingScene[0]->getSceneId()); + EXPECT_FALSE(rendererScene.hasActiveShaderAnimation()); + rendererScene.setActiveShaderAnimation(true); + + expectModifiedScenesReportedToRenderer(); + update(); + + // flush resets shader animation + EXPECT_TRUE(rendererScene.hasActiveShaderAnimation()); + performFlush(); + // empty flush marks scene modified if shader animation was active before + expectModifiedScenesReportedToRenderer(); + update(); + EXPECT_FALSE(rendererScene.hasActiveShaderAnimation()); + + performFlush(); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfOtherSceneHasShaderAnimation) + { + createDisplayAndExpectSuccess(); + + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + mapScene(0); + mapScene(1); + + showScene(0); + showScene(1); + + // simulate rendering + auto& rendererScene1 = rendererScenes.getScene(getSceneId(0)); + auto& rendererScene2 = rendererScenes.getScene(getSceneId(1)); + + EXPECT_FALSE(rendererScene1.hasActiveShaderAnimation()); + EXPECT_FALSE(rendererScene2.hasActiveShaderAnimation()); + rendererScene2.setActiveShaderAnimation(true); + + expectModifiedScenesReportedToRenderer({1}); + update(); + + EXPECT_FALSE(rendererScene1.hasActiveShaderAnimation()); + EXPECT_TRUE(rendererScene2.hasActiveShaderAnimation()); + + hideScene(0); + hideScene(1); + unmapScene(0); + unmapScene(1); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfEffectTimeIsSynced) + { + createDisplayAndExpectSuccess(); + const auto scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + + createRenderable(); + setRenderableResources(); + + expectModifiedScenesReportedToRenderer(); + expectVertexArrayUploaded(); + update(); + expectRenderableResourcesClean(); + + const auto& rendererScene = rendererScenes.getScene(stagingScene[0]->getSceneId()); + EXPECT_EQ(FlushTime::InvalidTimestamp, rendererScene.getEffectTimeSync()); + performFlushWithUniformTimeSync(scene, 1000u); + + expectModifiedScenesReportedToRenderer(); + update(); + EXPECT_EQ(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u)), rendererScene.getEffectTimeSync()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfOffscreenBufferLinkedToScene) + { + createDisplayAndExpectSuccess(); + const uint32_t scene1 = createPublishAndSubscribeScene(); + const uint32_t scene2 = createPublishAndSubscribeScene(); + mapScene(scene1); + mapScene(scene2); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_TRUE(assignSceneToDisplayBuffer(scene2, buffer)); + + const DataSlotId consumer = createTextureConsumer(scene1); + + showScene(scene1); + showScene(scene2); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + createBufferLink(buffer, getSceneId(scene1), consumer); + expectModifiedScenesReportedToRenderer({ scene1 }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(scene1); + hideScene(scene2); + unmapScene(scene1); + unmapScene(scene2); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfOffscreenBufferUnlinkedFromScene) + { + createDisplayAndExpectSuccess(); + const uint32_t scene1 = createPublishAndSubscribeScene(); + const uint32_t scene2 = createPublishAndSubscribeScene(); + mapScene(scene1); + mapScene(scene2); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_TRUE(assignSceneToDisplayBuffer(scene2, buffer)); + + const DataSlotId consumer = createTextureConsumer(scene1); + + showScene(scene1); + showScene(scene2); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + createBufferLink(buffer, getSceneId(scene1), consumer); + expectModifiedScenesReportedToRenderer({ scene1 }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + unlinkConsumer(getSceneId(scene1), consumer); + expectModifiedScenesReportedToRenderer({ scene1 }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(scene1); + hideScene(scene2); + unmapScene(scene1); + unmapScene(scene2); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_WhenProviderSceneAssignedToOBIsShownAfterOBLinked) + { + createDisplayAndExpectSuccess(); + const uint32_t scene1 = createPublishAndSubscribeScene(); + const uint32_t scene2 = createPublishAndSubscribeScene(); + const DataSlotId consumer = createTextureConsumer(scene1); + mapScene(scene1); + mapScene(scene2); + showScene(scene1); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_TRUE(assignSceneToDisplayBuffer(scene2, buffer)); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + createBufferLink(buffer, getSceneId(scene1), consumer); + expectModifiedScenesReportedToRenderer({ scene1 }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + expectModifiedScenesReportedToRenderer({ scene1, scene2 }); + showScene(scene2); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(scene1); + hideScene(scene2); + unmapScene(scene1); + unmapScene(scene2); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_IfTransformationConsumerAndProviderLinkedOrUnlinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t providerScene = createPublishAndSubscribeScene(); + const uint32_t consumerScene = createPublishAndSubscribeScene(); + mapScene(providerScene); + mapScene(consumerScene); + showScene(providerScene); + showScene(consumerScene); + + const auto providerConsumer = createTransformationSlots(nullptr, providerScene, consumerScene); + update(); + + linkProviderToConsumer(getSceneId(providerScene), providerConsumer.first, getSceneId(consumerScene), providerConsumer.second); + expectModifiedScenesReportedToRenderer({ consumerScene }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + unlinkConsumer(getSceneId(consumerScene), providerConsumer.second); + expectModifiedScenesReportedToRenderer({ consumerScene }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(providerScene); + hideScene(consumerScene); + unmapScene(providerScene); + unmapScene(consumerScene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_IfDataConsumerAndProviderLinkedOrUnlinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t providerScene = createPublishAndSubscribeScene(); + const uint32_t consumerScene = createPublishAndSubscribeScene(); + mapScene(providerScene); + mapScene(consumerScene); + showScene(providerScene); + showScene(consumerScene); + + DataInstanceHandle dataRef; + const auto providerConsumer = createDataSlots(dataRef, 1.f, nullptr, providerScene, consumerScene); + update(); + + linkProviderToConsumer(getSceneId(providerScene), providerConsumer.first, getSceneId(consumerScene), providerConsumer.second); + expectModifiedScenesReportedToRenderer({ consumerScene }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + unlinkConsumer(getSceneId(consumerScene), providerConsumer.second); + expectModifiedScenesReportedToRenderer({ consumerScene }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(providerScene); + hideScene(consumerScene); + unmapScene(providerScene); + unmapScene(consumerScene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarkSceneAsModified_IfTextureConsumerAndProviderLinkedOrUnlinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t providerScene = createPublishAndSubscribeScene(); + const uint32_t consumerScene = createPublishAndSubscribeScene(); + mapScene(providerScene); + mapScene(consumerScene); + showScene(providerScene); + showScene(consumerScene); + + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, providerScene); + const auto providerConsumer = createTextureSlots(nullptr, providerScene, consumerScene); + update(); + + linkProviderToConsumer(getSceneId(providerScene), providerConsumer.first, getSceneId(consumerScene), providerConsumer.second); + expectModifiedScenesReportedToRenderer({ consumerScene }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + unlinkConsumer(getSceneId(consumerScene), providerConsumer.second); + expectModifiedScenesReportedToRenderer({ consumerScene }); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(providerScene); + hideScene(consumerScene); + unmapScene(providerScene); + unmapScene(consumerScene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfModifiedSceneNotShown) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + update(); + + performFlushWithCreateNodeAction(); + expectNoModifiedScenesReportedToRenderer(); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, MarksSceneAsModified_IfSceneNotModifiedButSkippingDisabled) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + update(); + + performFlushWithCreateNodeAction(); + expectModifiedScenesReportedToRenderer(); + update(); + + expectNoModifiedScenesReportedToRenderer(); + update(); + + // no change to scene but disabled skipping feature + rendererSceneUpdater->setSkippingOfUnmodifiedScenes(false); + expectModifiedScenesReportedToRenderer(); + update(); + + expectModifiedScenesReportedToRenderer(); + update(); + + rendererSceneUpdater->setSkippingOfUnmodifiedScenes(true); + expectNoModifiedScenesReportedToRenderer(); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, DoesNotMarkSceneAsModified_IfSkippingDisabledButSceneNotShown) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + update(); + + performFlushWithCreateNodeAction(); + expectNoModifiedScenesReportedToRenderer(); + update(); + + // no change to scene but disabled skipping feature + rendererSceneUpdater->setSkippingOfUnmodifiedScenes(false); + // still not reported to re-render because not shown + expectNoModifiedScenesReportedToRenderer(); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfInterruptedSceneIsHidden) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(sceneInterrupted); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneIsHidden) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneIsShown) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + const uint32_t sceneToShow = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + mapScene(sceneToShow); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + showScene(sceneToShow); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(sceneToShow); + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + unmapScene(sceneToShow); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotResetInterruptedRenderingIfAnotherSceneIsMapped) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + const uint32_t sceneToMap = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + mapScene(sceneToMap); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + unmapScene(sceneToMap); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotResetInterruptedRenderingIfAnotherSceneIsUnmapped) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + const uint32_t sceneToUnmap = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + mapScene(sceneToUnmap); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + unmapScene(sceneToUnmap); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfInterruptedSceneAssignedToFramebuffer) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + EXPECT_TRUE(assignSceneToDisplayBuffer(sceneInterrupted)); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneAssignedToFramebuffer) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + const uint32_t sceneToAssign = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + mapScene(sceneToAssign); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(321u)); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_TRUE(assignSceneToDisplayBuffer(sceneToAssign, buffer)); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + EXPECT_TRUE(assignSceneToDisplayBuffer(sceneToAssign)); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + unmapScene(sceneToAssign); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneAssignedToOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + const auto buffer = showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + EXPECT_TRUE(assignSceneToDisplayBuffer(scene, buffer)); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneLinkedToOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + const DataSlotId texConsumer = createTextureConsumer(scene); + + const auto buffer = showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + createBufferLink(buffer, getSceneId(scene), texConsumer); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfAnotherSceneUnlinkedFromOffscreenBuffer) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + const uint32_t sceneToUnlink = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + mapScene(sceneToUnlink); + + const OffscreenBufferHandle buffer(1u); + expectOffscreenBufferUploaded(buffer, DeviceResourceHandle(321u)); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + const DataSlotId texConsumer = createTextureConsumer(sceneToUnlink); + createBufferLink(buffer, getSceneId(sceneToUnlink), texConsumer); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + unlinkConsumer(getSceneId(sceneToUnlink), texConsumer); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + unmapScene(sceneToUnlink); + + expectOffscreenBufferDeleted(buffer); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTransformationLinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + // provider/consumer order does not matter here + const auto providerConsumerId = createTransformationSlots(nullptr, scene, sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTransformationUnlinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + // provider/consumer order does not matter here + const auto providerConsumerId = createTransformationSlots(nullptr, scene, sceneInterrupted); + linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + unlinkConsumer(getSceneId(sceneInterrupted), providerConsumerId.second); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfDataLinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + // provider/consumer order does not matter here + DataInstanceHandle dataRef; + const auto providerConsumerId = createDataSlots(dataRef, 1.f, nullptr, scene, sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfDataUnlinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + // provider/consumer order does not matter here + DataInstanceHandle dataRef; + const auto providerConsumerId = createDataSlots(dataRef, 1.f, nullptr, scene, sceneInterrupted); + linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + unlinkConsumer(getSceneId(sceneInterrupted), providerConsumerId.second); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTexturesLinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + // provider/consumer order does not matter here + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, scene); + const auto providerConsumerId = createTextureSlots(nullptr, scene, sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfTexturesUnlinked) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + // provider/consumer order does not matter here + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, scene); + const auto providerConsumerId = createTextureSlots(nullptr, scene, sceneInterrupted); + linkProviderToConsumer(getSceneId(scene), providerConsumerId.first, getSceneId(sceneInterrupted), providerConsumerId.second); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + unlinkConsumer(getSceneId(sceneInterrupted), providerConsumerId.second); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfOffscreenBufferCreated) + { + createDisplayAndExpectSuccess(); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + const OffscreenBufferHandle buffer2(1u); + expectOffscreenBufferUploaded(buffer2, DeviceResourceHandle(321u)); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer2, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + expectOffscreenBufferDeleted(buffer2); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer2)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfOffscreenBufferDeleted) + { + createDisplayAndExpectSuccess(); + + constexpr OffscreenBufferHandle buffer(1u); + constexpr DeviceResourceHandle bufferDeviceHandle{ 321u }; + expectOffscreenBufferUploaded(buffer, bufferDeviceHandle); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, false, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + expectOffscreenBufferDeleted(buffer, bufferDeviceHandle); + EXPECT_TRUE(rendererSceneUpdater->handleBufferDestroyRequest(buffer)); + + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, blocksFlushesForSceneAssignedToInterruptibleOBWhenThereIsInterruption) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + performFlush(sceneInterrupted); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + performFlush(sceneInterrupted); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + + performFlush(sceneInterrupted); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + + renderer.resetRenderInterruptState(); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotBlockFlushesForSceneAssignedToNormalOBWhenThereIsInterruption) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + mapScene(scene); + mapScene(sceneInterrupted); + + performFlush(sceneInterrupted); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(scene)); + + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + performFlush(scene); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + + performFlush(scene); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + + renderer.resetRenderInterruptState(); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(scene)); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resetsInterruptedRenderingIfMaximumNumberOfPendingFlushesReached) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + const uint32_t sceneInterrupted = createPublishAndSubscribeScene(); + + createRenderable(sceneInterrupted); + setRenderableResources(sceneInterrupted); + + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, sceneInterrupted); + mapScene(scene); + mapScene(sceneInterrupted); + + expectVertexArrayUploaded(sceneInterrupted); + showAndInitiateInterruptedRendering(scene, sceneInterrupted); + + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + // add blocking flush so that upcoming flushes are queuing up + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }, sceneInterrupted); + setRenderableResources(sceneInterrupted, MockResourceHash::IndexArrayHash2); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + update(); + + // flushes are blocked due to unresolved resource + const SceneVersionTag pendingFlushTag(124u); + performFlush(sceneInterrupted, pendingFlushTag); + update(); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + // will force apply and log blocking resources + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, sceneInterrupted); + expectVertexArrayUnloaded(sceneInterrupted); + for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(sceneInterrupted); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + expectSceneEvent(ERendererEventType::SceneFlushed); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(sceneInterrupted)); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + + hideScene(scene); + hideScene(sceneInterrupted); + unmapScene(scene); + unmapScene(sceneInterrupted); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsExpirationForSubscribedSceneButWithNoFurtherFlushes) + { + const uint32_t scene = createPublishAndSubscribeScene(false); + // initial flush and enable monitoring + performFlushWithExpiration(scene, 1000u + 1u); + expectInternalSceneStateEvent(ERendererEventType::SceneSubscribed); + + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + // no further flush, no expiration updates + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + + expectSceneEvent(ERendererEventType::SceneExpired); + } + + TEST_F(ARendererSceneUpdater, doesNotReportExpirationForNotShownSceneBeingFlushedRegularly) + { + const uint32_t scene = createPublishAndSubscribeScene(); + // enable monitoring + performFlushWithExpiration(scene, 2000); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + expectNoEvent(); + } + } + + TEST_F(ARendererSceneUpdater, reportsExpirationForNotShownSceneNotBeingFlushed) + { + const uint32_t scene = createPublishAndSubscribeScene(); + // flush once only to set limit + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + + expectSceneEvent(ERendererEventType::SceneExpired); + } + + TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpiredForNotShownScene) + { + const uint32_t scene = createPublishAndSubscribeScene(); + // flush once only to set limit + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + // expect exactly one event + expectSceneEvent(ERendererEventType::SceneExpired); + + for (uint32_t i = 10u; i < 20u; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + // expect exactly one event + expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); + } + + TEST_F(ARendererSceneUpdater, doesNotReportExpirationForSceneBeingFlushedAndRenderedRegularly) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // enable monitoring + performFlushWithExpiration(scene, 2000); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.onRendered(getSceneId()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + expectNoEvent(); + } + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsExpirationForSceneBeingFlushedButNotRenderedRegularly) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // scene must be rendered at least once with valid expiration, only then it can expire + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + const NodeHandle nodeHandle(3u); + const TransformHandle transform(2u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + sceneAllocator.allocateTransform(nodeHandle, transform); + + for (uint32_t i = 0u; i < 10u; ++i) + { + iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpired); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotReportExpirationForSceneBeingFlushedButNotRenderedBecauseItIsHidden) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // scene must be rendered at least once with valid expiration, only then it can expire + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + hideScene(); + + const NodeHandle nodeHandle(3u); + const TransformHandle transform(2u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + sceneAllocator.allocateTransform(nodeHandle, transform); + + for (uint32_t i = 0u; i < 10u; ++i) + { + iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectNoEvent(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsExpirationForSceneNotBeingFlushedOnlyRendered) + { + const uint32_t scene = createPublishAndSubscribeScene(); + // flush once only to set limit + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + expirationMonitor.onRendered(getSceneId()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + + expectSceneEvent(ERendererEventType::SceneExpired); + } + + TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpirationForSceneNotBeingRenderedRegularly_byRenderingRegularlyAgain) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // scene must be rendered at least once with valid expiration, only then it can expire + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + const NodeHandle nodeHandle(3u); + const TransformHandle transform(2u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + sceneAllocator.allocateTransform(nodeHandle, transform); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpired); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.onRendered(getSceneId()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpirationForSceneNotBeingRenderedRegularly_byHidingSceneAndKeepingRegularFlushes) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // scene must be rendered at least once with valid expiration, only then it can expire + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + const NodeHandle nodeHandle(3u); + const TransformHandle transform(2u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + sceneAllocator.allocateTransform(nodeHandle, transform); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpired); + + hideScene(); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + iscene.setTranslation(transform, {}); // so that flush is not 'empty' - modifies scene + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotReportExpirationForSceneBeingFlushedRegularlyButSkippedRenderingDueToEmptyFlushes) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // enable monitoring + performFlushWithExpiration(scene, 2000); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + expectNoEvent(); + } + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsExpirationForNotShownSceneBeingFlushedButBlockedByMissingResource) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + + // flush with expiration info to initiate monitoring + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + // blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(scene); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); // blocked due to missing resource + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpired); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsExpirationSceneBeingFlushedAndRenderedButBlockedByMissingResource) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // flush with expiration info to initiate monitoring + performFlushWithExpiration(scene, 1000u + 2u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + // blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(scene); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2; ++i) + { + performFlushWithExpiration(scene, 1000u + i + 2u); // blocked due to missing resource + update(); + expirationMonitor.onRendered(getSceneId()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpired); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reportsRecoveryAfterExpirationForSceneBeingFlushedAndRenderedButBlockedByMissingResource) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + uint32_t expTS = 1000u + 2u; + + // flush with expiration info to initiate monitoring + performFlushWithExpiration(scene, expTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + // blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderableNoFlush(scene); + performFlushWithExpiration(scene, expTS); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + expectVertexArrayUploaded(); + update(); + + for (uint32_t i = 0u; i < ForceApplyFlushesLimit / 2u; ++i) + { + performFlushWithExpiration(scene, expTS++); // blocked due to missing resource + update(); + expirationMonitor.onRendered(getSceneId()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpired); + + // simulate upload + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + update(); + + for (uint32_t i = ForceApplyFlushesLimit / 2u; i < ForceApplyFlushesLimit; ++i) + { + performFlushWithExpiration(scene, expTS++); // not blocked anymore + update(); + expirationMonitor.onRendered(getSceneId()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneRecoveredFromExpiration); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withEmptyFlush) + { + const uint32_t scene = createPublishAndSubscribeScene(); + update(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 1u; i < 10u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); + } + expectNoEvent(); + + // disable expiration + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withNonEmptyFlush) + { + const uint32_t scene = createPublishAndSubscribeScene(); + update(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 1u; i < 10u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); + } + expectNoEvent(); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(3u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withEmptyFlush_rendered) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 1u; i < 10u; ++i) + { + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); + } + expectNoEvent(); + + // disable expiration + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + hideScene(scene); + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withNonEmptyFlush_rendered) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 1u; i < 10u; ++i) + { + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); + } + expectNoEvent(); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(3u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + hideScene(scene); + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withEmptyFlush_hiddenAfterRendered) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 1u; i < 10u; ++i) + { + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); + } + expectNoEvent(); + + hideScene(scene); + + // disable expiration + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_withNonEmptyFlush_hiddenAfterRendered) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 1u; i < 10u; ++i) + { + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(i))); + } + expectNoEvent(); + + hideScene(scene); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(3u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_whileModifyingScene_confidenceTest) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + // make change + const NodeHandle nodeHandle(i); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, initialTS + i); + + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i - 1))); + } + expectNoEvent(); + + hideScene(scene); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(10u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 10u; i < 20u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_whenHiddenInBetweenFlushes_confidenceTest) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + // make change + const NodeHandle nodeHandle(i); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, initialTS + i); + + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i - 1))); + + // hide scene in between applying flushes with timestamps + if (i == 8) + hideScene(scene); + } + expectNoEvent(); + + // disable expiration + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 10u; i < 20u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationChecking_whenHiddenInBetweenEmptyFlushes_confidenceTest) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + update(); + doRenderLoop(); + + // flush once only to set limit + const uint32_t initialTS = 1000u; + performFlushWithExpiration(scene, initialTS); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringEnabled); + + for (uint32_t i = 0u; i < 10u; ++i) + { + performFlushWithExpiration(scene, initialTS + i); + + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i - 1))); + + // hide scene in between applying flushes with timestamps + if (i == 8) + hideScene(scene); + } + expectNoEvent(); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(10u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + update(); + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + for (uint32_t i = 10u; i < 20u; ++i) + { + update(); + doRenderLoop(); + // check with TS after initial TS + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(initialTS + i))); + } + // expect no expiration as last flush disabled expiration + expectNoEvent(); + + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired) + { + const uint32_t scene = createPublishAndSubscribeScene(); + + for (uint32_t i = 0u; i < 5u; ++i) + { + performFlushWithExpiration(scene, 1000u + 2u); // will expire + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); + + // disable expiration + performFlushWithExpiration(scene, 0u); + + for (uint32_t i = 0u; i < 5u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired_rendered) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + for (uint32_t i = 0u; i < 5u; ++i) + { + performFlushWithExpiration(scene, 1000u + 2u); // will expire + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); + + // disable expiration + performFlushWithExpiration(scene, 0u); + + for (uint32_t i = 0u; i < 5u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + hideScene(scene); + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired_nonEmptyFlush) + { + const uint32_t scene = createPublishAndSubscribeScene(); + + for (uint32_t i = 0u; i < 5u; ++i) + { + performFlushWithExpiration(scene, 1000u + 2u); // will expire + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(10u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + + for (uint32_t i = 0u; i < 5u; ++i) + { + update(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + } + + TEST_F(ARendererSceneUpdater, canDisableExpirationCheckingWhileAlreadyExpired_rendered_nonEmptyFlush) + { + createDisplayAndExpectSuccess(); + const uint32_t scene = createPublishAndSubscribeScene(); + mapScene(scene); + showScene(scene); + + for (uint32_t i = 0u; i < 5u; ++i) + { + performFlushWithExpiration(scene, 1000u + 2u); // will expire + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvents({ ERendererEventType::SceneExpirationMonitoringEnabled, ERendererEventType::SceneExpired }); + + // disable expiration together with some scene changes + const NodeHandle nodeHandle(10u); + IScene& iscene = *stagingScene[scene]; + SceneAllocateHelper sceneAllocator(iscene); + sceneAllocator.allocateNode(0u, nodeHandle); + performFlushWithExpiration(scene, 0u); + + for (uint32_t i = 0u; i < 5u; ++i) + { + update(); + doRenderLoop(); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::time_point(std::chrono::milliseconds(1000u + i))); + } + expectSceneEvent(ERendererEventType::SceneExpirationMonitoringDisabled); + + hideScene(scene); + unmapScene(scene); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, ignoresPickEventForUnknownScene) + { + rendererSceneUpdater->handlePickEvent(SceneId{ 123u }, { 0, 0 }); + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, reportsNoPickedObjectsForSceneWithNoPickableObjects) + { + createPublishAndSubscribeScene(); + rendererSceneUpdater->handlePickEvent(getSceneId(), { 0, 0 }); + expectNoEvent(); + } + + TEST_F(ARendererSceneUpdater, reportsPickedObjects) + { + DisplayConfig config; + config.setDesiredWindowWidth(1280u); + config.setDesiredWindowHeight(480u); + createDisplayAndExpectSuccess(config); + const auto sceneIdx = createPublishAndSubscribeScene(); + mapScene(); + + // create scene with 2 pickable triangles around origin + IScene& iscene = *stagingScene[sceneIdx]; + SceneAllocateHelper sceneAllocator(iscene); + const NodeHandle nodeHandle(0u); + sceneAllocator.allocateNode(0u, nodeHandle); + const std::array geomData{ -1.f, 0.f, -0.5f, 0.f, 1.f, -0.5f, 0.f, 0.f, -0.5f }; + const auto geomHandle = sceneAllocator.allocateDataBuffer(EDataBufferType::VertexBuffer, EDataType::Vector3F, uint32_t(geomData.size() * sizeof(float))); + iscene.updateDataBuffer(geomHandle, 0, uint32_t(geomData.size() * sizeof(float)), reinterpret_cast(geomData.data())); + + const auto dataLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference}, DataFieldInfo{EDataType::DataReference} }, {}); + const auto dataInstance = sceneAllocator.allocateDataInstance(dataLayout); + const auto vpDataRefLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I} }, {}); + const auto vpOffsetInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); + const auto vpSizeInstance = sceneAllocator.allocateDataInstance(vpDataRefLayout); + const auto frustumPlanesLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector4F} }, {}); + const auto frustumPlanes = sceneAllocator.allocateDataInstance(frustumPlanesLayout); + const auto frustumNearFarLayout = sceneAllocator.allocateDataLayout({ DataFieldInfo{EDataType::Vector2F} }, {}); + const auto frustumNearFar = sceneAllocator.allocateDataInstance(frustumNearFarLayout); + iscene.setDataReference(dataInstance, Camera::ViewportOffsetField, vpOffsetInstance); + iscene.setDataReference(dataInstance, Camera::ViewportSizeField, vpSizeInstance); + iscene.setDataReference(dataInstance, Camera::FrustumPlanesField, frustumPlanes); + iscene.setDataReference(dataInstance, Camera::FrustumNearFarPlanesField, frustumNearFar); + const auto cameraHandle = sceneAllocator.allocateCamera(ECameraProjectionType::Orthographic, nodeHandle, dataInstance); + + iscene.setDataSingleVector2i(vpOffsetInstance, DataFieldHandle{ 0 }, { 0, 0 }); + iscene.setDataSingleVector2i(vpSizeInstance, DataFieldHandle{ 0 }, { 1280, 480 }); + const ProjectionParams params = ProjectionParams::Frustum(ECameraProjectionType::Orthographic, -1.f, 1.f, -1.f, 1.f, 0.1f, 100.f); + iscene.setDataSingleVector4f(frustumPlanes, DataFieldHandle{ 0 }, { params.leftPlane, params.rightPlane, params.bottomPlane, params.topPlane }); + iscene.setDataSingleVector2f(frustumNearFar, DataFieldHandle{ 0 }, { params.nearPlane, params.farPlane }); + + const PickableObjectId id1{ 666u }; + const PickableObjectId id2{ 667u }; + const auto pickableHandle1 = sceneAllocator.allocatePickableObject(geomHandle, nodeHandle, id1); + const auto pickableHandle2 = sceneAllocator.allocatePickableObject(geomHandle, nodeHandle, id2); + iscene.setPickableObjectCamera(pickableHandle1, cameraHandle); + iscene.setPickableObjectCamera(pickableHandle2, cameraHandle); + performFlush(); + + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadDataBuffer(_, _, _, _, _)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, updateDataBuffer(_, _, _, _)); + update(); + + EXPECT_CALL(*rendererSceneUpdater, handlePickEvent(_, _)); + rendererSceneUpdater->handlePickEvent(getSceneId(), { -0.375000f, 0.250000f }); + expectSceneEvent(ERendererEventType::ObjectsPicked); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, emitsSequenceOfSceneStateChangesWhenRepublished_fromSubscribed) + { + createPublishAndSubscribeScene(); + + const SceneId sceneId = stagingScene[0]->getSceneId(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(getSceneId())); + rendererSceneUpdater->handleSceneUnpublished(sceneId); + rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote); + expectInternalSceneStateEvents({ ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished, ERendererEventType::ScenePublished }); + } + + TEST_F(ARendererSceneUpdater, emitsSequenceOfSceneStateChangesWhenRepublished_fromMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectUnloadOfSceneResources(); + + const SceneId sceneId = stagingScene[0]->getSceneId(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); + rendererSceneUpdater->handleSceneUnpublished(sceneId); + rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote); + expectInternalSceneStateEvents({ ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished, ERendererEventType::ScenePublished }); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, emitsSequenceOfSceneStateChangesWhenRepublished_fromShown) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectUnloadOfSceneResources(); + + const SceneId sceneId = stagingScene[0]->getSceneId(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); + rendererSceneUpdater->handleSceneUnpublished(sceneId); + rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote); + expectInternalSceneStateEvents({ ERendererEventType::SceneHiddenIndirect, ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished, ERendererEventType::ScenePublished }); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, propagatesGeneratedSceneReferenceActionsToSceneReferenceControl) + { + createPublishAndSubscribeScene(); + + constexpr SceneReferenceAction action1{ SceneReferenceActionType::LinkData, SceneReferenceHandle{1}, DataSlotId{2}, SceneReferenceHandle{3}, DataSlotId{4} }; + constexpr SceneReferenceAction action2{ SceneReferenceActionType::UnlinkData, SceneReferenceHandle{5}, DataSlotId{6}, SceneReferenceHandle{7}, DataSlotId{8} }; + const SceneReferenceActionVector sceneRefActions{ action1, action2 }; + + performFlush(0u, {}, nullptr, {}, sceneRefActions); + + EXPECT_CALL(sceneReferenceLogic, addActions(getSceneId(), _)).WillOnce([&](auto /*unused*/, const auto& actions) + { + ASSERT_EQ(2u, actions.size()); + EXPECT_EQ(action1.type, actions[0].type); + EXPECT_EQ(action1.providerScene, actions[0].providerScene); + EXPECT_EQ(action1.providerId, actions[0].providerId); + EXPECT_EQ(action1.consumerScene, actions[0].consumerScene); + EXPECT_EQ(action1.consumerId, actions[0].consumerId); + EXPECT_EQ(action2.type, actions[1].type); + EXPECT_EQ(action2.providerScene, actions[1].providerScene); + EXPECT_EQ(action2.providerId, actions[1].providerId); + EXPECT_EQ(action2.consumerScene, actions[1].consumerScene); + EXPECT_EQ(action2.consumerId, actions[1].consumerId); + }); + update(); + + //make empty flush to make sure actions were consumed + performFlush(0u, {}, nullptr, {}, {}); + EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0u); + update(); + } + + TEST_F(ARendererSceneUpdater, propagatesGeneratedSceneReferenceActionsToSceneReferenceControlOnlyAfterFlushApplied) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // simulate blocking flush on missing resource + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + expectVertexArrayUploaded(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // send scene reference actions in flush + constexpr SceneReferenceAction action1{ SceneReferenceActionType::LinkData, SceneReferenceHandle{1}, DataSlotId{2}, SceneReferenceHandle{3}, DataSlotId{4} }; + constexpr SceneReferenceAction action2{ SceneReferenceActionType::UnlinkData, SceneReferenceHandle{5}, DataSlotId{6}, SceneReferenceHandle{7}, DataSlotId{8} }; + const SceneReferenceActionVector sceneRefActions{ action1, action2 }; + performFlush(0u, {}, nullptr, {}, sceneRefActions); + EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock pending flushes by providing resource + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + + EXPECT_CALL(sceneReferenceLogic, addActions(getSceneId(), _)).WillOnce([&](auto /*unused*/, const auto& actions) + { + ASSERT_EQ(2u, actions.size()); + EXPECT_EQ(action1.type, actions[0].type); + EXPECT_EQ(action1.providerScene, actions[0].providerScene); + EXPECT_EQ(action1.providerId, actions[0].providerId); + EXPECT_EQ(action1.consumerScene, actions[0].consumerScene); + EXPECT_EQ(action1.consumerId, actions[0].consumerId); + EXPECT_EQ(action2.type, actions[1].type); + EXPECT_EQ(action2.providerScene, actions[1].providerScene); + EXPECT_EQ(action2.providerId, actions[1].providerId); + EXPECT_EQ(action2.consumerScene, actions[1].consumerScene); + EXPECT_EQ(action2.consumerId, actions[1].consumerId); + }); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + //make empty flush to make sure actions were consumed + performFlush(0u, {}, nullptr, {}, {}); + EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0u); + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, queuesAndPropagatesSceneReferenceActionsFromMultipleFlushesToSceneReferenceControl) + { + createPublishAndSubscribeScene(); + + constexpr SceneReferenceAction action1{ SceneReferenceActionType::LinkData, SceneReferenceHandle{1}, DataSlotId{2}, SceneReferenceHandle{3}, DataSlotId{4} }; + constexpr SceneReferenceAction action2{ SceneReferenceActionType::UnlinkData, SceneReferenceHandle{5}, DataSlotId{6}, SceneReferenceHandle{7}, DataSlotId{8} }; + + performFlush(0u, {}, nullptr, {}, { action1 }); + performFlush(0u, {}, nullptr, {}, {}); + performFlush(0u, {}, nullptr, {}, { action2 }); + + EXPECT_CALL(sceneReferenceLogic, addActions(getSceneId(), _)).WillOnce([&](auto /*unused*/, const auto& actions) + { + ASSERT_EQ(2u, actions.size()); + EXPECT_EQ(action1.type, actions[0].type); + EXPECT_EQ(action1.providerScene, actions[0].providerScene); + EXPECT_EQ(action1.providerId, actions[0].providerId); + EXPECT_EQ(action1.consumerScene, actions[0].consumerScene); + EXPECT_EQ(action1.consumerId, actions[0].consumerId); + EXPECT_EQ(action2.type, actions[1].type); + EXPECT_EQ(action2.providerScene, actions[1].providerScene); + EXPECT_EQ(action2.providerId, actions[1].providerId); + EXPECT_EQ(action2.consumerScene, actions[1].consumerScene); + EXPECT_EQ(action2.consumerId, actions[1].consumerId); + }); + update(); + + //make empty flush to make sure actions were consumed + performFlush(0u, {}, nullptr, {}, {}); + EXPECT_CALL(sceneReferenceLogic, addActions(_, _)).Times(0u); + update(); + } + +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.h b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.h new file mode 100644 index 000000000..5018a6605 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest.h @@ -0,0 +1,1033 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "RendererMock.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/SceneGraph/Scene/ActionCollectingScene.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" +#include "internal/RendererLib/RenderingContext.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/RendererSceneUpdater.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "ComponentMocks.h" +#include "ResourceDeviceHandleAccessorMock.h" +#include "MockResourceHash.h" +#include "SceneReferenceLogicMock.h" +#include "SceneAllocateHelper.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "RendererSceneEventSenderMock.h" +#include "RendererSceneUpdaterFacade.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +#include +#include +#include +#include + +namespace ramses::internal { + + class ARendererSceneUpdater : public ::testing::Test + { + public: + ARendererSceneUpdater() + : rendererScenes(rendererEventCollector) + , expirationMonitor(rendererScenes, rendererEventCollector, rendererStatistics) + , renderer(Display, rendererScenes, rendererEventCollector, expirationMonitor, rendererStatistics) + , sceneStateExecutor(renderer, sceneEventSender, rendererEventCollector) + , rendererSceneUpdater(new RendererSceneUpdaterFacade(Display, renderer.m_platform, renderer, rendererScenes, sceneStateExecutor, rendererEventCollector, frameTimer, expirationMonitor, notifier)) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + rendererSceneUpdater->setSceneReferenceLogicHandler(sceneReferenceLogic); + frameTimer.startFrame(); + rendererSceneUpdater->setLimitFlushesForceApply(ForceApplyFlushesLimit); + rendererSceneUpdater->setLimitFlushesForceUnsubscribe(ForceUnsubscribeFlushLimit); + + EXPECT_CALL(renderer, setClearColor(_, _)).Times(0); + // called explicitly from tests, no sense in tracking + EXPECT_CALL(*rendererSceneUpdater, handleSceneUpdate(_, _)).Times(AnyNumber()); + } + + void TearDown() override + { + expectNoEvent(); + } + + protected: + void update() + { + frameTimer.startFrame(); + renderer.getProfilerStatistics().markFrameFinished(std::chrono::microseconds{ 0u }); + EXPECT_CALL(sceneReferenceLogic, update()); + if (rendererSceneUpdater->m_resourceManagerMock) + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, hasResourcesToBeUploaded()); + rendererSceneUpdater->updateScenes(); + } + + uint32_t createStagingScene() + { + const auto sceneIndex = static_cast(stagingScene.size()); + const SceneId sceneId(sceneIndex); + stagingScene.emplace_back(new ActionCollectingScene(SceneInfo(sceneId))); + + return sceneIndex; + } + + SceneId getSceneId(uint32_t sceneIndex = 0u) + { + return stagingScene[sceneIndex]->getSceneId(); + } + + void publishScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = getSceneId(sceneIndex); + rendererSceneUpdater->handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote); + EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::Published); + expectInternalSceneStateEvent(ERendererEventType::ScenePublished); + } + + void requestSceneSubscription(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = getSceneId(sceneIndex); + EXPECT_CALL(sceneEventSender, sendSubscribeScene(sceneId)); + rendererSceneUpdater->handleSceneSubscriptionRequest(sceneId); + EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::SubscriptionRequested); + } + + void receiveScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = getSceneId(sceneIndex); + rendererSceneUpdater->handleSceneReceived(SceneInfo(sceneId)); + EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::SubscriptionPending); + EXPECT_TRUE(rendererScenes.hasScene(sceneId)); + } + + uint32_t createPublishAndSubscribeScene(bool withInitialFlush = true) + { + const uint32_t sceneIndex = createStagingScene(); + const SceneId sceneId = getSceneId(sceneIndex); + + publishScene(sceneIndex); + requestSceneSubscription(sceneIndex); + receiveScene(sceneIndex); + + if (withInitialFlush) + { + //receive initial flush + performFlush(sceneIndex); + EXPECT_TRUE(sceneStateExecutor.getSceneState(sceneId) == ESceneState::Subscribed); + expectInternalSceneStateEvent(ERendererEventType::SceneSubscribed); + } + + return sceneIndex; + } + + static void ExpectEventsEqual(const std::initializer_list expectedEvents, const RendererEventVector& actualEvents) + { + ASSERT_EQ(expectedEvents.size(), actualEvents.size()); + auto eventIt = actualEvents.cbegin(); + for (auto expectedEvent : expectedEvents) + { + EXPECT_EQ(expectedEvent, eventIt->eventType); + ++eventIt; + } + } + + void expectEvents(const std::initializer_list expectedEvents) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(events, dummy); + ExpectEventsEqual(expectedEvents, events); + EXPECT_TRUE(dummy.empty()) << " expected renderer events only but there are also unchecked scene events"; + } + + void expectSceneEvents(const std::initializer_list expectedEvents) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ExpectEventsEqual(expectedEvents, events); + EXPECT_TRUE(dummy.empty()) << " expected scene events only but there are also unchecked renderer events"; + } + + void expectInternalSceneStateEvents(const std::initializer_list expectedEvents) + { + InternalSceneStateEvents events; + rendererEventCollector.dispatchInternalSceneStateEvents(events); + ASSERT_EQ(expectedEvents.size(), events.size()) << "internal scene state events"; + auto expectedIt = expectedEvents.begin(); + for (size_t i = 0; i < events.size(); ++i) + EXPECT_EQ(*expectedIt++, events[i].type) << "internal scene state event #" << i; + } + + void expectNoEvent() + { + RendererEventVector events; + RendererEventVector sceneEvents; + rendererEventCollector.appendAndConsumePendingEvents(events, sceneEvents); + EXPECT_TRUE(events.empty()) << " pending unchecked renderer events"; + EXPECT_TRUE(sceneEvents.empty()) << " pending unchecked scene events"; + + expectInternalSceneStateEvents({}); + } + + void expectEvent(ERendererEventType eventType) + { + expectEvents({ eventType }); + } + + void expectSceneEvent(ERendererEventType eventType) + { + expectSceneEvents({ eventType }); + } + + void expectInternalSceneStateEvent(ERendererEventType eventType) + { + expectInternalSceneStateEvents({ eventType }); + } + + void requestMapScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + rendererSceneUpdater->handleSceneMappingRequest(sceneId); + expectNoEvent(); + } + + void mapScene(uint32_t sceneIndex = 0u) + { + requestMapScene(sceneIndex); + update(); // will set from map requested to being mapped and uploaded (if all pending flushes applied) + update(); // will set to mapped (if all resources uploaded) + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + } + + bool assignSceneToDisplayBuffer(uint32_t sceneIndex, OffscreenBufferHandle buffer = {}, int32_t renderOrder = 0) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + const bool result = rendererSceneUpdater->handleSceneDisplayBufferAssignmentRequest(sceneId, buffer, renderOrder); + if (result) + { + EXPECT_EQ(renderOrder, renderer.getSceneGlobalOrder(sceneId)); + } + + return result; + } + + void showScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + rendererSceneUpdater->handleSceneShowRequest(sceneId); + update(); + expectInternalSceneStateEvent(ERendererEventType::SceneShown); + } + + void hideScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + rendererSceneUpdater->handleSceneHideRequest(sceneId); + expectInternalSceneStateEvent(ERendererEventType::SceneHidden); + } + + void unmapScene(uint32_t sceneIndex = 0u, bool expectResourcesUnload = true) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + if (expectResourcesUnload) + expectUnloadOfSceneResources(sceneIndex); + + rendererSceneUpdater->handleSceneUnmappingRequest(sceneId); + expectInternalSceneStateEvent(ERendererEventType::SceneUnmapped); + expectNoResourceReferencedByScene(sceneIndex); + } + + void expectNoResourceReferencedByScene(uint32_t sceneIndex = 0u) + { + rendererSceneUpdater->m_resourceManagerMock->expectNoResourceReferencesForScene(getSceneId(sceneIndex)); + } + + int getResourceRefCount(ResourceContentHash resource) + { + return rendererSceneUpdater->m_resourceManagerMock->getResourceRefCount(resource); + } + + void expectUnloadOfSceneResources(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadAllSceneResourcesForScene(sceneId)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceAllResourcesForScene(sceneId)); + } + + void unpublishMapRequestedScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); + rendererSceneUpdater->handleSceneUnpublished(sceneId); + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished }); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + void unpublishMappedScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + expectUnloadOfSceneResources(sceneIndex); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); + rendererSceneUpdater->handleSceneUnpublished(sceneId); + expectInternalSceneStateEvents({ ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished }); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + void unpublishShownScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(sceneId)); + rendererSceneUpdater->handleSceneUnpublished(sceneId); + expectInternalSceneStateEvents({ ERendererEventType::SceneHiddenIndirect, ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect, ERendererEventType::SceneUnpublished }); + + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + void unsubscribeMapRequestedScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(sceneId, true); + + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect }); + + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + void unsubscribeMappedScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(sceneId, true); + + expectInternalSceneStateEvents({ ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect }); + + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + void unsubscribeShownScene(uint32_t sceneIndex = 0u) + { + const SceneId sceneId = stagingScene[sceneIndex]->getSceneId(); + rendererSceneUpdater->handleSceneUnsubscriptionRequest(sceneId, true); + + expectInternalSceneStateEvents({ ERendererEventType::SceneHiddenIndirect, ERendererEventType::SceneUnmappedIndirect, ERendererEventType::SceneUnsubscribedIndirect }); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + NodeHandle performFlushWithCreateNodeAction(uint32_t sceneIndex = 0u, size_t numNodes = 1u) + { + NodeHandle nodeHandle; + IScene& scene = *stagingScene[sceneIndex]; + SceneAllocateHelper sceneAllocator(scene); + for (size_t i = 0; i < numNodes; ++i) + nodeHandle = sceneAllocator.allocateNode(0, {}); + performFlush(sceneIndex); + + return nodeHandle; + } + + void expectReadPixelsEvents(const std::initializer_list>& expectedEvents) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(events, dummy); + ASSERT_EQ(events.size(), expectedEvents.size()); + + auto eventsIt = events.cbegin(); + for (const auto& expectedEvent : expectedEvents) + { + const auto& event = *eventsIt; + EXPECT_EQ(event.offscreenBuffer, std::get<0>(expectedEvent)); + if (std::get<1>(expectedEvent)) + { + EXPECT_EQ(event.eventType, ERendererEventType::ReadPixelsFromFramebuffer); + } + else + { + EXPECT_EQ(event.eventType, ERendererEventType::ReadPixelsFromFramebufferFailed); + } + } + + } + + void performFlush(uint32_t sceneIndex = 0u, SceneVersionTag version = SceneVersionTag::Invalid(), const SceneSizeInformation* sizeInfo = nullptr, const FlushTimeInformation& timeInfo = {}, const SceneReferenceActionVector& sceneRefActions = {}) + { + ActionCollectingScene& scene = *stagingScene[sceneIndex]; + const SceneSizeInformation newSizeInfo = (sizeInfo ? *sizeInfo : scene.getSceneSizeInformation()); + const SceneSizeInformation currSizeInfo = rendererScenes.hasScene(scene.getSceneId()) ? rendererScenes.getScene(scene.getSceneId()).getSceneSizeInformation() : SceneSizeInformation(); + + SceneUpdate update; + update.actions = (std::move(scene.getSceneActionCollection())); + + SceneActionCollectionCreator creator(update.actions); + + ResourceContentHashVector resources; + ResourceChanges resourceChanges; + ResourceUtils::GetAllResourcesFromScene(resources, scene); + ResourceUtils::DiffResources(previousResources[sceneIndex], resources, resourceChanges); + previousResources[sceneIndex].swap(resources); + resourceChanges.m_sceneResourceActions = scene.getSceneResourceActions(); + + for (const auto& resHash : resourceChanges.m_resourcesAdded) + update.resources.push_back(MockResourceHash::GetManagedResource(resHash)); + + update.flushInfos = {1u, version, newSizeInfo, resourceChanges, sceneRefActions, timeInfo, newSizeInfo>currSizeInfo, true}; + scene.resetResourceChanges(); + rendererSceneUpdater->handleSceneUpdate(stagingScene[sceneIndex]->getSceneId(), std::move(update)); + } + + void performFlushWithUniformTimeSync(uint32_t sceneIndex, uint32_t internalTS) + { + const FlushTimeInformation timeInfo{ {}, FlushTime::Clock::time_point(std::chrono::milliseconds(internalTS)), FlushTime::Clock::getClockType(), true }; + performFlush(sceneIndex, {}, nullptr, timeInfo); + } + + void performFlushWithExpiration(uint32_t sceneIndex, uint32_t expirationTS) + { + const FlushTimeInformation timeInfo{ FlushTime::Clock::time_point(std::chrono::milliseconds(expirationTS)), {}, FlushTime::Clock::getClockType(), false }; + performFlush(sceneIndex, {}, nullptr, timeInfo); + } + + bool lastFlushWasAppliedOnRendererScene(uint32_t sceneIndex = 0u) + { + return !rendererSceneUpdater->hasPendingFlushes(stagingScene[sceneIndex]->getSceneId()); + } + + void createDisplayAndExpectSuccess(const DisplayConfig& displayConfig = DisplayConfig()) + { + ASSERT_TRUE(rendererSceneUpdater->m_resourceManagerMock == nullptr); + EXPECT_CALL(*rendererSceneUpdater, createResourceManager(_, _, _, _)); + EXPECT_CALL(renderer.m_platform, createResourceUploadRenderBackend()); + rendererSceneUpdater->createDisplayContext(displayConfig, nullptr); + EXPECT_TRUE(renderer.hasDisplayController()); + expectEvent(ERendererEventType::DisplayCreated); + + // no offscreen buffers reported uploaded by default + ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(_)).WillByDefault(Return(DeviceResourceHandle::Invalid())); + ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferColorBufferDeviceHandle(_)).WillByDefault(Return(DeviceResourceHandle::Invalid())); + // scene updater queries display/offscreen buffer from device handle when updating modified scenes states - these are invalid for display framebuffer + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferHandle(DisplayControllerMock::FakeFrameBufferHandle)).Times(AnyNumber()).WillRepeatedly(Return(OffscreenBufferHandle::Invalid())); + + // scene updater logic might call resource manager for ref/unref of resources with empty list - no need to track those + ResourceContentHashVector emptyResources; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(_, emptyResources)).Times(AnyNumber()); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceResourcesForScene(_, emptyResources)).Times(AnyNumber()); + + // querying of resources state happens often, concrete tests can control status reported + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceStatus(_)).Times(AnyNumber()); + + // by default skip path triggering upload/unload, concrete tests can override + ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, hasResourcesToBeUploaded()).WillByDefault(Return(false)); + } + + void destroyDisplay(bool expectFail = false) + { + if (!expectFail) + { + ASSERT_TRUE(rendererSceneUpdater->m_resourceManagerMock != nullptr); + EXPECT_TRUE(rendererSceneUpdater->hasResourceManager()); + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); + rendererSceneUpdater->m_resourceManagerMock->expectNoResourceReferences(); + } + rendererSceneUpdater->destroyDisplayContext(); + if (expectFail) + { + expectEvent(ERendererEventType::DisplayDestroyFailed); + } + else + { + EXPECT_FALSE(rendererSceneUpdater->hasResourceManager()); + EXPECT_FALSE(renderer.hasDisplayController()); + expectEvent(ERendererEventType::DisplayDestroyed); + } + } + + void createRenderable(uint32_t sceneIndex = 0u, bool withVertexArray = false, bool withTextureSampler = false, EVisibilityMode visibility = EVisibilityMode::Visible) + { + createRenderableNoFlush(sceneIndex, withVertexArray, withTextureSampler, visibility); + performFlush(sceneIndex); + } + + void createRenderableNoFlush(uint32_t sceneIndex = 0u, bool withVertexArray = false, bool withTextureSampler = false, EVisibilityMode visibility = EVisibilityMode::Visible) + { + const NodeHandle renderableNode(1u); + const RenderPassHandle renderPassHandle(2u); + const RenderGroupHandle renderGroupHandle(3u); + const CameraHandle cameraHandle(4u); + const DataLayoutHandle uniformDataLayoutHandle(0u); + const DataLayoutHandle geometryDataLayoutHandle(1u); + const DataLayoutHandle camDataLayoutHandle(2u); + const DataInstanceHandle camDataHandle(2u); + + IScene& scene = *stagingScene[sceneIndex]; + + scene.allocateRenderPass(0u, renderPassHandle); + scene.allocateRenderGroup(0u, 0u, renderGroupHandle); + scene.allocateNode(0u, renderableNode); + scene.allocateRenderable(renderableNode, renderableHandle); + scene.allocateDataLayout({ DataFieldInfo{EDataType::Vector2I}, DataFieldInfo{EDataType::Vector2I} }, MockResourceHash::EffectHash, camDataLayoutHandle); + scene.allocateCamera(ECameraProjectionType::Perspective, scene.allocateNode(0, {}), scene.allocateDataInstance(camDataLayoutHandle, camDataHandle), cameraHandle); + + scene.addRenderableToRenderGroup(renderGroupHandle, renderableHandle, 0u); + scene.addRenderGroupToRenderPass(renderPassHandle, renderGroupHandle, 0u); + scene.setRenderPassCamera(renderPassHandle, cameraHandle); + scene.setRenderPassEnabled(renderPassHandle, true); + + DataFieldInfoVector uniformDataFields; + if (withTextureSampler) + { + uniformDataFields.push_back(DataFieldInfo{ EDataType::TextureSampler2D }); + } + scene.allocateDataLayout(uniformDataFields, MockResourceHash::EffectHash, uniformDataLayoutHandle); + scene.allocateDataInstance(uniformDataLayoutHandle, uniformDataInstanceHandle); + scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Uniforms, uniformDataInstanceHandle); + + DataFieldInfoVector geometryDataFields; + geometryDataFields.push_back(DataFieldInfo{ EDataType::Indices, 1u, EFixedSemantics::Indices }); + if (withVertexArray) + { + geometryDataFields.push_back(DataFieldInfo{ EDataType::Vector3Buffer, 1u, EFixedSemantics::Invalid }); + } + scene.allocateDataLayout(geometryDataFields, MockResourceHash::EffectHash, geometryDataLayoutHandle); + scene.allocateDataInstance(geometryDataLayoutHandle, geometryDataInstanceHandle); + scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Geometry, geometryDataInstanceHandle); + scene.setRenderableVisibility(renderableHandle, visibility); + } + + void destroyRenderable(uint32_t sceneIndex = 0u) + { + const RenderGroupHandle renderGroupHandle(3u); + IScene& scene = *stagingScene[sceneIndex]; + scene.removeRenderableFromRenderGroup(renderGroupHandle, renderableHandle); + scene.releaseRenderable(renderableHandle); + + performFlush(sceneIndex); + } + + void setRenderableResources(uint32_t sceneIndex = 0u, ResourceContentHash indexArrayHash = MockResourceHash::IndexArrayHash) + { + setRenderableResourcesNoFlush(sceneIndex, indexArrayHash); + performFlush(sceneIndex); + } + + void setRenderableResourcesNoFlush(uint32_t sceneIndex = 0u, ResourceContentHash indexArrayHash = MockResourceHash::IndexArrayHash) + { + IScene& scene = *stagingScene[sceneIndex]; + scene.setDataResource(geometryDataInstanceHandle, DataFieldHandle(0u), indexArrayHash, DataBufferHandle::Invalid(), 0u, 0u, 0u); + } + + DataSlotId createRenderableWithTextureConsumer(uint32_t sceneIndex = 0u) + { + expectVertexArrayUploaded(sceneIndex); + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, sceneIndex); + createRenderable(sceneIndex, false, true); + setRenderableResources(sceneIndex); + update(); + + IScene& scene = *stagingScene[sceneIndex]; + scene.allocateTextureSampler({ {}, MockResourceHash::TextureHash }, samplerHandle); + scene.setDataTextureSamplerHandle(uniformDataInstanceHandle, DataFieldHandle(0u), samplerHandle); + const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); + scene.allocateDataSlot({EDataSlotType::TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), samplerHandle}, {}); + + expectResourcesReferencedAndProvided({ MockResourceHash::TextureHash }, sceneIndex); + performFlush(sceneIndex); + update(); + + expectSceneEvent(ERendererEventType::SceneDataSlotConsumerCreated); + + return consumerId; + } + + void removeRenderableResources(uint32_t sceneIndex = 0u) + { + IScene& scene = *stagingScene[sceneIndex]; + // it is not allowed to set both resource and data buffer as invalid, so for the purpose of the test cases here we set to non-existent data buffer with handle 0 + scene.setDataResource(geometryDataInstanceHandle, DataFieldHandle(0u), ResourceContentHash::Invalid(), DataBufferHandle(0u), 0u, 0u, 0u); + + performFlush(sceneIndex); + } + + void expectResourcesReferenced(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u, int times = 1) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(getSceneId(sceneIdx), resources)).Times(times); + } + + void expectResourcesProvided(int times = 1) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, provideResourceData(_)).Times(times); + } + + void expectResourcesReferencedAndProvided(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u) + { + // most tests add 1 resource in 1 flush, referencing logic is executed per flush + for (const auto& res : resources) + expectResourcesReferenced({ res }, sceneIdx, 1); + expectResourcesProvided(int(resources.size())); + } + + void expectResourcesReferencedAndProvided_altogether(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(getSceneId(sceneIdx), resources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, provideResourceData(_)).Times(int(resources.size())); + } + + void expectResourcesUnreferenced(const ResourceContentHashVector& resources, uint32_t sceneIdx = 0u, int times = 1) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceResourcesForScene(getSceneId(sceneIdx), resources)).Times(times); + } + + void reportResourceAs(ResourceContentHash resource, EResourceStatus status) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceStatus(resource)).Times(AnyNumber()).WillRepeatedly(Return(status)); + } + + void expectOffscreenBufferUploaded(OffscreenBufferHandle buffer, DeviceResourceHandle rtDeviceHandleToReturn = DeviceMock::FakeRenderTargetDeviceHandle, bool doubleBuffered = false, EDepthBufferType depthStencilBufferType = EDepthBufferType::DepthStencil) + { + { + InSequence seq; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadOffscreenBuffer(buffer, _, _, _, doubleBuffered, depthStencilBufferType)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillRepeatedly(Return(rtDeviceHandleToReturn)); + } + // scene updater queries offscreen buffer from device handle when updating modified scenes states + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferHandle(rtDeviceHandleToReturn)).Times(AnyNumber()).WillRepeatedly(Return(buffer)); + } + + void expectDmaOffscreenBufferUploaded(OffscreenBufferHandle buffer, DeviceResourceHandle rtDeviceHandleToReturn, DmaBufferFourccFormat fourccFormat, DmaBufferUsageFlags usageFlags, DmaBufferModifiers modifiers) + { + { + InSequence seq; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadDmaOffscreenBuffer(buffer, _, _, fourccFormat, usageFlags, modifiers)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillRepeatedly(Return(rtDeviceHandleToReturn)); + } + // scene updater queries offscreen buffer from device handle when updating modified scenes states + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferHandle(rtDeviceHandleToReturn)).Times(AnyNumber()).WillRepeatedly(Return(buffer)); + } + + void expectOffscreenBufferDeleted(OffscreenBufferHandle buffer, DeviceResourceHandle deviceHandle = DeviceMock::FakeRenderTargetDeviceHandle) + { + InSequence seq; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillOnce(Return(deviceHandle)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadOffscreenBuffer(buffer)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getOffscreenBufferDeviceHandle(buffer)).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + } + + void expectStreamBufferUploaded(StreamBufferHandle buffer, WaylandIviSurfaceId source, DeviceResourceHandle rtDeviceHandleToReturn = DeviceMock::FakeRenderTargetDeviceHandle) + { + InSequence seq; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadStreamBuffer(buffer, source)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).WillRepeatedly(Return(rtDeviceHandleToReturn)); + } + + void expectStreamBufferDeleted(StreamBufferHandle buffer) + { + InSequence seq; + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).WillOnce(Return(DeviceMock::FakeRenderTargetDeviceHandle)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadStreamBuffer(buffer)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(buffer)).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + } + + void expectExternalBufferUploaded(ExternalBufferHandle buffer, DeviceResourceHandle deviceHandleToReturn = DeviceMock::FakeExternalTextureDeviceHandle) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadExternalBuffer(buffer)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferDeviceHandle(buffer)).Times(AnyNumber()).WillRepeatedly(Return(deviceHandleToReturn)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getExternalBufferGlId(buffer)).Times(AnyNumber()); + } + + void expectExternalBufferDeleted(ExternalBufferHandle buffer) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadExternalBuffer(buffer)); + } + + void expectBlitPassUploaded() + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(0u), _)).Times(2); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadBlitPassRenderTargets(_, _, _, getSceneId(0u))); + } + + void expectBlitPassDeleted() + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadRenderTargetBuffer(_, getSceneId(0u))).Times(2); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadBlitPassRenderTargets(_, getSceneId(0u))); + } + + void expectStreamTextureUploaded() + { + static constexpr DeviceResourceHandle FakeStreamTextureDeviceHandle{ 987 }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, getCompositedTextureDeviceHandleForStreamTexture(_)).WillRepeatedly(Return(FakeStreamTextureDeviceHandle)); + } + + void expectVertexArrayUploaded(uint32_t sceneIdx = 0u) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadVertexArray(_, _, getSceneId(sceneIdx))); + } + + void expectVertexArrayUnloaded(uint32_t sceneIdx = 0u) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadVertexArray(_, getSceneId(sceneIdx))); + } + + void createRenderTargetWithBuffers(uint32_t sceneIndex = 0u, RenderTargetHandle renderTargetHandle = RenderTargetHandle{ 0u }, RenderBufferHandle bufferHandle = RenderBufferHandle{ 0u }, RenderBufferHandle depthHandle = RenderBufferHandle{ 1u }) + { + IScene& scene = *stagingScene[sceneIndex]; + scene.allocateRenderBuffer({ 16u, 16u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }, bufferHandle); + scene.allocateRenderBuffer({ 16u, 16u, EPixelStorageFormat::Depth32, ERenderBufferAccessMode::ReadWrite, 0u }, depthHandle); + scene.allocateRenderTarget(renderTargetHandle); + scene.addRenderTargetRenderBuffer(renderTargetHandle, bufferHandle); + scene.addRenderTargetRenderBuffer(renderTargetHandle, depthHandle); + performFlush(sceneIndex); + } + + void expectRenderTargetUploaded(uint32_t sceneIdx = 0u) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(sceneIdx), _)).Times(2); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTarget(_, _, getSceneId(sceneIdx))); + } + + void expectRenderTargetUnloaded(uint32_t sceneIdx = 0u) + { + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadRenderTargetBuffer(_, getSceneId(sceneIdx))).Times(2); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadRenderTarget(_, getSceneId(sceneIdx))); + } + + void createBlitPass() + { + const BlitPassHandle blitPassHandle(0u); + IScene& scene = *stagingScene[0]; + const RenderBufferHandle sourceBufferHandle = scene.allocateRenderBuffer({ 16u, 16u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + const RenderBufferHandle destinationBufferHandle = scene.allocateRenderBuffer({ 16u, 16u, EPixelStorageFormat::RGBA8, ERenderBufferAccessMode::ReadWrite, 0u }, {}); + scene.allocateBlitPass(sourceBufferHandle, destinationBufferHandle, blitPassHandle); + performFlush(); + } + + void destroyRenderTargetWithBuffers(uint32_t sceneIndex = 0u) + { + const RenderTargetHandle renderTargetHandle(0u); + const RenderBufferHandle bufferHandle(0u); + const RenderBufferHandle depthHandle(1u); + IScene& scene = *stagingScene[sceneIndex]; + scene.releaseRenderTarget(renderTargetHandle); + scene.releaseRenderBuffer(bufferHandle); + scene.releaseRenderBuffer(depthHandle); + performFlush(sceneIndex); + } + + void destroyBlitPass() + { + const BlitPassHandle blitPassHandle(0u); + IScene& scene = *stagingScene[0]; + scene.releaseBlitPass(blitPassHandle); + performFlush(); + } + + std::pair createDataSlots(DataInstanceHandle& dataRefConsumer, float providedValue, DataInstanceHandle* dataRefProviderOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) + { + DataInstanceHandle dataRefProvider; + + IScene& scene1 = *stagingScene[providerSceneIdx]; + IScene& scene2 = *stagingScene[consumerSceneIdx]; + + const DataLayoutHandle dataLayout1 = scene1.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid(), {}); + const DataLayoutHandle dataLayout2 = scene2.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid(), {}); + dataRefProvider = scene1.allocateDataInstance(dataLayout1, {}); + dataRefConsumer = scene2.allocateDataInstance(dataLayout2, {}); + if (nullptr != dataRefProviderOut) + *dataRefProviderOut = dataRefProvider; + + scene1.setDataSingleFloat(dataRefProvider, DataFieldHandle(0u), providedValue); + scene2.setDataSingleFloat(dataRefConsumer, DataFieldHandle(0u), 0.f); + + const DataSlotId providerId(getNextFreeDataSlotIdForDataLinking()); + const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); + scene1.allocateDataSlot({ EDataSlotType::DataProvider, providerId, NodeHandle(), dataRefProvider, ResourceContentHash::Invalid(), TextureSamplerHandle() }, {}); + scene2.allocateDataSlot({ EDataSlotType::DataConsumer, consumerId, NodeHandle(), dataRefConsumer, ResourceContentHash::Invalid(), TextureSamplerHandle() }, {}); + + performFlush(providerSceneIdx); + performFlush(consumerSceneIdx); + update(); + expectSceneEvents({ ERendererEventType::SceneDataSlotConsumerCreated, ERendererEventType::SceneDataSlotProviderCreated }); + + return{ providerId, consumerId }; + } + + void createDataSlotsAndLinkThem(DataInstanceHandle& dataRefConsumer, float providedValue, DataInstanceHandle* dataRefProviderOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) + { + const auto providerConsumer = createDataSlots(dataRefConsumer, providedValue, dataRefProviderOut, providerSceneIdx, consumerSceneIdx); + linkProviderToConsumer(getSceneId(providerSceneIdx), providerConsumer.first, getSceneId(consumerSceneIdx), providerConsumer.second); + } + + void updateProviderDataSlot(uint32_t sceneIdx, DataInstanceHandle dataRefProvider, float newProvidedValue) + { + IScene& scene = *stagingScene[sceneIdx]; + scene.setDataSingleFloat(dataRefProvider, DataFieldHandle(0u), newProvidedValue); + } + + std::pair createTextureSlots(DataSlotHandle* providerDataSlotHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) + { + IScene& scene1 = *stagingScene[providerSceneIdx]; + IScene& scene2 = *stagingScene[consumerSceneIdx]; + + const TextureSamplerHandle sampler = scene2.allocateTextureSampler({ {}, RenderBufferHandle(999) }, {}); + + const DataSlotId providerId(getNextFreeDataSlotIdForDataLinking()); + const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); + DataSlotHandle providerDataSlot = scene1.allocateDataSlot({ EDataSlotType::TextureProvider, providerId, NodeHandle(), DataInstanceHandle::Invalid(), MockResourceHash::TextureHash, TextureSamplerHandle() }, {}); + if (nullptr != providerDataSlotHandleOut) + *providerDataSlotHandleOut = providerDataSlot; + scene2.allocateDataSlot({ EDataSlotType::TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }, {}); + + performFlush(providerSceneIdx); + performFlush(consumerSceneIdx); + update(); + expectSceneEvents({ ERendererEventType::SceneDataSlotConsumerCreated, ERendererEventType::SceneDataSlotProviderCreated }); + + return{ providerId, consumerId }; + } + + void createTextureSlotsAndLinkThem(DataSlotHandle* providerDataSlotHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u, bool expectSuccess = true) + { + const auto providerConsumer = createTextureSlots(providerDataSlotHandleOut, providerSceneIdx, consumerSceneIdx); + linkProviderToConsumer(getSceneId(providerSceneIdx), providerConsumer.first, getSceneId(consumerSceneIdx), providerConsumer.second, expectSuccess); + } + + DataSlotId createTextureConsumer(uint32_t sceneIndex, TextureSampler::ContentType contentType = TextureSampler::ContentType::RenderBuffer) + { + IScene& scene = *stagingScene[sceneIndex]; + const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, contentType, {}, MemoryHandle{999u} }, {}); + const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); + scene.allocateDataSlot({ EDataSlotType::TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }, {}); + + performFlush(sceneIndex); + update(); + expectSceneEvent(ERendererEventType::SceneDataSlotConsumerCreated); + + return consumerId; + } + + template + void createBufferLink(BUFFERHANDLE buffer, SceneId consumerSceneId, DataSlotId consumerId, bool expectFail = false) + { + rendererSceneUpdater->handleBufferToSceneDataLinkRequest(buffer, consumerSceneId, consumerId); + expectSceneEvent(expectFail ? ERendererEventType::SceneDataBufferLinkFailed : ERendererEventType::SceneDataBufferLinked); + } + + void updateProviderTextureSlot(uint32_t sceneIdx, DataSlotHandle providerDataSlot, ResourceContentHash newProvidedValue) + { + IScene& scene = *stagingScene[sceneIdx]; + scene.setDataSlotTexture(providerDataSlot, newProvidedValue); + } + + std::pair createTransformationSlots(TransformHandle* providerTransformHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) + { + IScene& scene1 = *stagingScene[providerSceneIdx]; + IScene& scene2 = *stagingScene[consumerSceneIdx]; + + const auto nodeHandle1 = scene1.allocateNode(0, {}); + const auto nodeHandle2 = scene2.allocateNode(0, {}); + + const auto providerTransformHandle = scene1.allocateTransform(nodeHandle1, {}); + scene2.allocateTransform(nodeHandle2, {}); + if (nullptr != providerTransformHandleOut) + *providerTransformHandleOut = providerTransformHandle; + + const DataSlotId providerId(getNextFreeDataSlotIdForDataLinking()); + const DataSlotId consumerId(getNextFreeDataSlotIdForDataLinking()); + scene1.allocateDataSlot({ EDataSlotType::TransformationProvider, providerId, nodeHandle1, {}, ResourceContentHash::Invalid(), TextureSamplerHandle() }, {}); + scene2.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerId, nodeHandle2, {}, ResourceContentHash::Invalid(), TextureSamplerHandle() }, {}); + + performFlush(providerSceneIdx); + performFlush(consumerSceneIdx); + update(); + expectSceneEvents({ ERendererEventType::SceneDataSlotConsumerCreated, ERendererEventType::SceneDataSlotProviderCreated }); + + return { providerId, consumerId }; + } + + void createTransformationSlotsAndLinkThem(TransformHandle* providerTransformHandleOut = nullptr, uint32_t providerSceneIdx = 0u, uint32_t consumerSceneIdx = 1u) + { + const auto providerConsumer = createTransformationSlots(providerTransformHandleOut, providerSceneIdx, consumerSceneIdx); + linkProviderToConsumer(getSceneId(providerSceneIdx), providerConsumer.first, getSceneId(consumerSceneIdx), providerConsumer.second); + } + + void linkProviderToConsumer(SceneId providerSceneId, DataSlotId providerId, SceneId consumerSceneId, DataSlotId consumerId, bool expectSuccess = true) + { + rendererSceneUpdater->handleSceneDataLinkRequest(providerSceneId, providerId, consumerSceneId, consumerId); + expectSceneEvent(expectSuccess ? ERendererEventType::SceneDataLinked : ERendererEventType::SceneDataLinkFailed); + } + + void unlinkConsumer(SceneId consumerSceneId, DataSlotId consumerId) + { + rendererSceneUpdater->handleDataUnlinkRequest(consumerSceneId, consumerId); + expectSceneEvent(ERendererEventType::SceneDataUnlinked); + } + + void destroySceneUpdater() + { + EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); + rendererSceneUpdater.reset(); + expectEvent(ERendererEventType::DisplayDestroyed); + } + + void expectRenderableResourcesClean(uint32_t sceneIndex = 0u) + { + const RendererCachedScene& scene = rendererScenes.getScene(stagingScene[sceneIndex]->getSceneId()); + EXPECT_FALSE(scene.renderableResourcesDirty(renderableHandle)); + } + + void expectRenderableResourcesDirty(uint32_t sceneIndex = 0u) + { + const RendererCachedScene& scene = rendererScenes.getScene(stagingScene[sceneIndex]->getSceneId()); + EXPECT_TRUE(scene.renderableResourcesDirty(renderableHandle)); + } + + void expectModifiedScenesReportedToRenderer(std::initializer_list indices = {0u}) + { + for (auto idx : indices) + { + const SceneId sceneId = stagingScene[idx]->getSceneId(); + EXPECT_CALL(renderer, markBufferWithSceneForRerender(sceneId)); + } + } + + void expectNoModifiedScenesReportedToRenderer() + { + EXPECT_CALL(renderer, markBufferWithSceneForRerender(_)).Times(0u); + } + + DataSlotId getNextFreeDataSlotIdForDataLinking() + { + return DataSlotId(dataSlotIdForDataLinking.getReference()++); + } + + OffscreenBufferHandle showAndInitiateInterruptedRendering(uint32_t sceneIdx, uint32_t interruptedSceneIdx) + { + // assign scene to double buffered OB + const OffscreenBufferHandle buffer(111u); + expectOffscreenBufferUploaded(buffer, DeviceMock::FakeRenderTargetDeviceHandle, true); + EXPECT_TRUE(rendererSceneUpdater->handleBufferCreateRequest(buffer, 1u, 1u, 0u, true, EDepthBufferType::DepthStencil)); + expectEvent(ERendererEventType::OffscreenBufferCreated); + EXPECT_TRUE(assignSceneToDisplayBuffer(interruptedSceneIdx, buffer)); + + showScene(sceneIdx); + showScene(interruptedSceneIdx); + update(); + + EXPECT_CALL(*renderer.m_displayController, getRenderBackend()).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, getDisplayBuffer()).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, clearBuffer(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).Times(AnyNumber()); + EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).Times(AnyNumber()); + + EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()); + EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).WillOnce(Return(true)); + EXPECT_CALL(*renderer.m_displayController, swapBuffers()); + + EXPECT_CALL(*renderer.m_displayController, renderScene(Ref(rendererScenes.getScene(getSceneId(sceneIdx))), _, nullptr)).WillOnce( + [](const auto& /*unused*/, const RenderingContext& renderContext, const auto* /*unused*/) { + EXPECT_EQ(DisplayControllerMock::FakeFrameBufferHandle, renderContext.displayBufferDeviceHandle); + return SceneRenderExecutionIterator{}; + }); + + SceneRenderExecutionIterator interruptedState; + interruptedState.incrementRenderableIdx(); + EXPECT_CALL(*renderer.m_displayController, renderScene(Ref(rendererScenes.getScene(getSceneId(interruptedSceneIdx))), _, _)).WillOnce( + [interruptedState](const auto& /*unused*/, const RenderingContext& renderContext, const auto* /*unused*/) { + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, renderContext.displayBufferDeviceHandle); + return interruptedState; + }); + + renderer.doOneRenderLoop(); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + + return buffer; + } + + void doRenderLoop() + { + EXPECT_CALL(*renderer.m_displayController, getRenderBackend()).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, getDisplayBuffer()).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, clearBuffer(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).Times(AnyNumber()); + EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).Times(AnyNumber()); + + EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()); + EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).WillOnce(Return(true)); + EXPECT_CALL(*renderer.m_displayController, swapBuffers()).Times(AnyNumber()); + EXPECT_CALL(*renderer.m_displayController, renderScene(_, _, _)).Times(AnyNumber()); + + renderer.doOneRenderLoop(); + } + + void readPixels(OffscreenBufferHandle obHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height, bool fullscreen, bool sendViaDLT, std::string_view filename) + { + ScreenshotInfo screenshotInfo; + screenshotInfo.rectangle.x = x; + screenshotInfo.rectangle.y = y; + screenshotInfo.rectangle.width = width; + screenshotInfo.rectangle.height = height; + screenshotInfo.fullScreen = fullscreen; + screenshotInfo.sendViaDLT = sendViaDLT; + screenshotInfo.filename = filename; + + rendererSceneUpdater->handleReadPixels(obHandle, std::move(screenshotInfo)); + } + + void expectDisplayControllerReadPixels(DeviceResourceHandle deviceHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + EXPECT_CALL(*renderer.m_displayController, readPixels(deviceHandle, x, y, width, height, _)).WillOnce(Invoke( + [](auto /*unused*/, auto /*unused*/, auto /*unused*/, auto w, auto h, auto& dataOut) { + dataOut.resize(w * h * 4); + } + )); + } + + static constexpr DisplayHandle Display{ 1u }; + + const RenderableHandle renderableHandle{ 1 }; + const DataInstanceHandle uniformDataInstanceHandle{ 0 }; + const DataInstanceHandle geometryDataInstanceHandle{ 1 }; + + const TextureSamplerHandle samplerHandle{ 2 }; + + std::vector> stagingScene; + DataSlotId dataSlotIdForDataLinking{9911u}; + + static constexpr size_t ForceApplyFlushesLimit = 10u; + static constexpr size_t ForceUnsubscribeFlushLimit = 20u; + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneExpirationMonitor expirationMonitor; + StrictMock sceneReferenceLogic; + RendererStatistics rendererStatistics; + StrictMock renderer; + StrictMock sceneEventSender; + FrameTimer frameTimer; + SceneStateExecutor sceneStateExecutor; + NiceMock notifier; + std::unique_ptr rendererSceneUpdater; + + std::unordered_map previousResources; + }; +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest_Resources.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest_Resources.cpp new file mode 100644 index 000000000..4bd940c6b --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererSceneUpdaterTest_Resources.cpp @@ -0,0 +1,2423 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "RendererSceneUpdaterTest.h" +#include "TestRandom.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include + +namespace ramses::internal { + + TEST_F(ARendererSceneUpdater, referencesAndProvidesResourcesWhenSceneMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + mapScene(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, referencesAndProvidesOnlyResourcesInUseWhenSceneMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + setRenderableResources(0u, MockResourceHash::IndexArrayHash3); + update(); + + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + mapScene(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesNotInUse) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }); + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash3 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash3); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesNotInUseOnlyAfterPendingFlushesApplied) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + // pending flush + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // unblock resource + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, sameResourceComingFromMultipleScenesIsReferencedAndProvidedMultipleTimes) + { + createDisplayAndExpectSuccess(); + const auto s1 = createPublishAndSubscribeScene(); + const auto s2 = createPublishAndSubscribeScene(); + const auto s3 = createPublishAndSubscribeScene(); + mapScene(s1); + mapScene(s2); + mapScene(s3); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); + createRenderable(s1); + setRenderableResources(s1); + update(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s2); + createRenderable(s2); + setRenderableResources(s2); + update(); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s3); + createRenderable(s3); + setRenderableResources(s3); + update(); + EXPECT_EQ(3, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(3, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(s1); + unmapScene(s2); + unmapScene(s3); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, sameResourceComingFromMultipleScenesIsUnreferencedWhenSceneUnmappedOrUnpublished) + { + createDisplayAndExpectSuccess(); + const auto s1 = createPublishAndSubscribeScene(); + const auto s2 = createPublishAndSubscribeScene(); + const auto s3 = createPublishAndSubscribeScene(); + mapScene(s1); + mapScene(s2); + mapScene(s3); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); + createRenderable(s1); + setRenderableResources(s1); + update(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s2); + createRenderable(s2); + setRenderableResources(s2); + update(); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s3); + createRenderable(s3); + setRenderableResources(s3); + update(); + EXPECT_EQ(3, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(3, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // destroy renderable in s1 + destroyRenderable(s1); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); + update(); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + // unmap s1 + unmapScene(s1); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + // unmap s2 + unmapScene(s2); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + // unpublish s3 + unpublishMappedScene(s3); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, triggersResourceUploadAndUnloadWhenResourceProvided) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + + // trigger unload/upload code path + ON_CALL(*rendererSceneUpdater->m_resourceManagerMock, hasResourcesToBeUploaded()).WillByDefault(Return(true)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadAndUnloadPendingResources()); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMapRequestedSceneWhenSceneGetsUnpublished) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + // blocking flush so that scene will never get mapped + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + requestMapScene(); + update(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectUnloadOfSceneResources(); + unpublishMapRequestedScene(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByRenderedSceneWhenSceneGetsUnpublished) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectVertexArrayUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectUnloadOfSceneResources(); + unpublishShownScene(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMappedSceneWhenSceneGetsUnsubscribedByError) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectUnloadOfSceneResources(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); + unsubscribeMappedScene(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMapRequestedSceneWhenSceneGetsUnsubscribedByError) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + // blocking flush so that scene will never get mapped + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + requestMapScene(); + update(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectUnloadOfSceneResources(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); + unsubscribeMapRequestedScene(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByRenderedSceneWhenSceneGetsUnsubscribedByError) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectVertexArrayUploaded(); + update(); + + expectUnloadOfSceneResources(); + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); + unsubscribeShownScene(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unreferencesResourcesInUseByMappedSceneWhenUpdaterDestructed) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + update(); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + } + + TEST_F(ARendererSceneUpdater, confidenceTest_doesNotReferenceOrUnreferenceResourcesIfSceneNotMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createRenderable(); + setRenderableResources(); + + update(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, updatesScenesCachedStreamBufferHandles_SingleScene) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + constexpr StreamBufferHandle streamBuffer{ 13u }; + + const auto dataSlotId = createRenderableWithTextureConsumer(); + createBufferLink(streamBuffer, getSceneId(0u), dataSlotId); + update(); + + expectRenderableResourcesClean(); + + constexpr WaylandIviSurfaceId source{ 12u }; + const StreamUsage fakeStreamUsage{ {}, { streamBuffer} }; + const WaylandIviSurfaceIdVector changedSources{ source }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillOnce(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); + update(); + expectRenderableResourcesClean(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, updatesScenesCachedStreamBufferHandles_MultipleScenes) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + + constexpr StreamBufferHandle streamBuffer{ 13u }; + + const auto dataSlotId1 = createRenderableWithTextureConsumer(0u); + const auto dataSlotId2 = createRenderableWithTextureConsumer(1u); + createBufferLink(streamBuffer, getSceneId(0u), dataSlotId1); + createBufferLink(streamBuffer, getSceneId(1u), dataSlotId2); + update(); + + expectRenderableResourcesClean(0u); + expectRenderableResourcesClean(1u); + + constexpr WaylandIviSurfaceId source{ 112u }; + const StreamUsage fakeStreamUsage{ {}, { streamBuffer} }; + + const WaylandIviSurfaceIdVector changedSources{ source }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillRepeatedly(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer)).Times(2u); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source)).WillOnce(ReturnRef(fakeStreamUsage)); + update(); + expectRenderableResourcesClean(0u); + expectRenderableResourcesClean(1u); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, updatesScenesCachedStreamBufferHandles_MultipleScenes_MultipleSources) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + mapScene(0u); + mapScene(1u); + showScene(0u); + showScene(1u); + + + constexpr StreamBufferHandle streamBuffer1{ 13u }; + constexpr StreamBufferHandle streamBuffer2{ 15u }; + + const auto dataSlotId1 = createRenderableWithTextureConsumer(0u); + const auto dataSlotId2 = createRenderableWithTextureConsumer(1u); + createBufferLink(streamBuffer1, getSceneId(0u), dataSlotId1); + createBufferLink(streamBuffer2, getSceneId(1u), dataSlotId2); + update(); + + expectRenderableResourcesClean(0u); + expectRenderableResourcesClean(1u); + + constexpr WaylandIviSurfaceId source1{ 112u }; + constexpr WaylandIviSurfaceId source2{ 121u }; + const StreamUsage fakeStreamUsage1{ {}, { streamBuffer1} }; + const StreamUsage fakeStreamUsage2{ {}, { streamBuffer2} }; + + const WaylandIviSurfaceIdVector changedSources{ source1, source2 }; + EXPECT_CALL(renderer.m_embeddedCompositingManager, dispatchStateChangesOfSources(_, _, _)).WillRepeatedly(SetArgReferee<0>(changedSources)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer1)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamBufferDeviceHandle(streamBuffer2)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source1)).WillOnce(ReturnRef(fakeStreamUsage1)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getStreamUsage(source2)).WillOnce(ReturnRef(fakeStreamUsage2)); + update(); + expectRenderableResourcesClean(0u); + expectRenderableResourcesClean(1u); + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, pendingFlushesAreNotAppliedIfResourcesNotReady) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, appliesPendingFlushesAtOnceAndInOrderWhenUnblocked_mapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectNoEvent(); + + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + const SceneVersionTag version1(1u); + const SceneVersionTag version2(2u); + const SceneVersionTag version3(3u); + + performFlush(0u, version1); + update(); + expectNoEvent(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(0u, version2); + update(); + expectNoEvent(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(0u, version3); + update(); + expectNoEvent(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock pending flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(3u, events.size()); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[0].eventType); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[1].eventType); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[2].eventType); + EXPECT_EQ(version1.getValue(), events[0].sceneVersionTag.getValue()); + EXPECT_EQ(version2.getValue(), events[1].sceneVersionTag.getValue()); + EXPECT_EQ(version3.getValue(), events[2].sceneVersionTag.getValue()); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + } + + TEST_F(ARendererSceneUpdater, uploadsAndUnloadsVertexArray) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + createRenderable(); + setRenderableResources(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + expectVertexArrayUploaded(); + update(); + expectRenderableResourcesClean(); + + destroyRenderable(); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + expectVertexArrayUnloaded(); + update(); + expectRenderableResourcesClean(); + + //next call to update does not release to any more attempts to unload + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotUploadVertexArrayIfRenderableVisilityOff) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + createRenderable(0u, false, false, EVisibilityMode::Off); + setRenderableResources(); + + update(); + expectRenderableResourcesDirty(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, appliesPendingFlushesAtOnceAndInOrderWhenUnblocked_shown) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectVertexArrayUploaded(); + expectNoEvent(); + + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + const SceneVersionTag version1(1u); + const SceneVersionTag version2(2u); + const SceneVersionTag version3(3u); + + performFlush(0u, version1); + update(); + expectNoEvent(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(0u, version2); + update(); + expectNoEvent(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(0u, version3); + update(); + expectNoEvent(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock pending flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(3u, events.size()); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[0].eventType); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[1].eventType); + EXPECT_EQ(ERendererEventType::SceneFlushed, events[2].eventType); + EXPECT_EQ(version1.getValue(), events[0].sceneVersionTag.getValue()); + EXPECT_EQ(version2.getValue(), events[1].sceneVersionTag.getValue()); + EXPECT_EQ(version3.getValue(), events[2].sceneVersionTag.getValue()); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + } + + TEST_F(ARendererSceneUpdater, pendingFlushesAreNotAppliedUntilBlockingResourceUploadedEvenIfUnreferencedByOtherFlush) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectVertexArrayUploaded(); + expectNoEvent(); + + // block on indices not uploaded + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // replace indices with another one which is uploaded right away + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + performFlush(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // even though not used anymore wait until previously blocking resource uploaded + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + // it will be unreferenced as unused right after + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + performFlush(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, swappingResourcesWhileBlockedEndsUpInEqualRefAndUnrefCounts) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + + // block on indices1 + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // swap indices1 for indices2 + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // swap back - indices1 is referenced and provided again (client sends resource once more) + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // swap once more - indices2 is referenced and provided again (client sends resource once more) + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // unblock both + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); + // indices is unused and therefore unreferenced + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 2); // was reffed twice, no zero + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); // unrefs are applied sequentially after apply - indices2 was swapped in/out + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // destroy renderable so there are no more resources in use + destroyRenderable(); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash2 }); + update(); + + expectNoResourceReferencedByScene(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, pendingFlushesWillBeAppliedIfSceneGetsUnmapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(); + + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, resourceUsedByRenderedSceneUnreferencedInFlushWillBeUnreferencedOnceFlushIsReady) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // renderable with resources uploaded and shown + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + expectVertexArrayUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // swap indices and block on it + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).WillOnce(Return(DeviceResourceHandle::Invalid())).RetiresOnSaturation(); + expectVertexArrayUnloaded(); + + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canRemapScene) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + mapScene(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + } + + TEST_F(ARendererSceneUpdater, canRemapSceneWithUnusedTextureSampler) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(0u, false, true); // with sampler that will not be set + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + mapScene(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + } + + TEST_F(ARendererSceneUpdater, mappedSceneWithBlockingFlushGetsUnmappedAndRemapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // resource will not be blocked next time used + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + + expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + mapScene(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + EXPECT_CALL(renderer.m_platform, destroyResourceUploadRenderBackend()); + expectUnloadOfSceneResources(); + destroySceneUpdater(); + } + + TEST_F(ARendererSceneUpdater, newRenderTargetIsUploadedOnlyAfterPendingFlushApplied) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + update(); + + // emulate blocking flush with new RT + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + createRenderTargetWithBuffers(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock pending flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + expectRenderTargetUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, renderTargetIsUnloadedOnlyAfterPendingFlushAppliedIfAlreadyUsedByRendererScene) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + createRenderTargetWithBuffers(); + expectRenderTargetUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // emulate blocking flush with release of RT + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + destroyRenderTargetWithBuffers(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock pending flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + expectRenderTargetUnloaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, renderTargetIsUploadedOnceWhenMappedIfCreatedBeforeMapping) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createRenderTargetWithBuffers(); + update(); + + expectRenderTargetUploaded(); + mapScene(); + + expectRenderTargetUnloaded(); + destroyRenderTargetWithBuffers(); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, renderTargetIsUploadedOnceWhenCreatedIfAlreadyMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + createRenderTargetWithBuffers(); + expectRenderTargetUploaded(); + update(); + + expectRenderTargetUnloaded(); + destroyRenderTargetWithBuffers(); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unloadsAndUploadsRenderTargetWithSameHandleDestroyedAndCreatedInSameLoop) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + createRenderTargetWithBuffers(); + expectRenderTargetUploaded(); + update(); + + destroyRenderTargetWithBuffers(); + createRenderTargetWithBuffers(); + expectRenderTargetUnloaded(); + expectRenderTargetUploaded(); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, doesNotUploadOrUnloadRenderTargetWithSameHandleCreatedAndDestroyedInSameLoop) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + createRenderTargetWithBuffers(); + destroyRenderTargetWithBuffers(); + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, renderTargetAndBuffersAreReuploadedAfterSceneRemapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + createRenderTargetWithBuffers(); + expectRenderTargetUploaded(); + update(); + + unmapScene(); + update(); + + expectRenderTargetUploaded(); + mapScene(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, blitPassesReuploadedAfterSceneRemapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + createBlitPass(); + expectBlitPassUploaded(); + update(); + + unmapScene(); + update(); + + expectBlitPassUploaded(); + mapScene(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, blockedResourceAndUnmapWillReferenceThatResourceWithNextMap) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + mapScene(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canDestroyDisplayIfThereArePendingUploads) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, onlyMapsASceneIfAllNeededResourcesAreUploaded) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // simulate upload + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + + update(); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, onlyMapsASceneIfAllNeededResourcesAreUploaded_WithTwoDifferentBlockingResources) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + // block 2 resources + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock 1st resource + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + + // still blocked + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock 2nd resource + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + + // only now is scene mapped + update(); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canUnmapSceneWhenSceneIsMapRequestedWithMapFailedEvent) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnmapped }); + EXPECT_EQ(ESceneState::Subscribed, sceneStateExecutor.getSceneState(getSceneId())); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canUnmapSceneWhenSceneIsMappingAndUploadingWithMapFailedEvent) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + expectUnloadOfSceneResources(); + rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnmapped }); + EXPECT_EQ(ESceneState::Subscribed, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, unmappingSceneWhenSceneIsMappingAndUploadingWillUnloadItsResources) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + setRenderableResources(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + expectUnloadOfSceneResources(); + rendererSceneUpdater->handleSceneUnmappingRequest(getSceneId()); + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnmapped }); + EXPECT_EQ(ESceneState::Subscribed, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, renderTargetIsUploadedWhenSceneMappingAndUploadingEvenIfMappingBlockedByFlush) + { + // create scene with RT + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createRenderable(); + createRenderTargetWithBuffers(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // request map but block resource + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + expectRenderTargetUploaded(); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock flush by making resource available + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + + update(); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + + update(); + + unmapScene(); + destroyDisplay(); + } + + // This is to reproduce and prove fix for a crash that happens in very very special constellation + // Note: this is probably outdated setup not reproducible in current code, keeping as confidence test + TEST_F(ARendererSceneUpdater, confidenceTest_renderTargetIsUploadedInCorrectOrderAfterSceneMappedWithShowRequestAndBlockingFlushComingAtSameTime) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + // create renderable + createRenderTargetWithBuffers(); + createRenderable(); + setRenderableResources(); + + // request map scene + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + // block resource + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + expectRenderTargetUploaded(); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // do one loop in uploading state that will upload resolvable resources + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // swap invalid resource for another invalid (this will put the first invalid into 'to be unreferenced' list in current implementation) + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock mapping + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + + update(); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + + ////////////////// + // unmap scene will unload render target, invalid resources is still in 'to be unreferenced' list + unmapScene(); + update(); + + // request map + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash2 }); + expectRenderTargetUploaded(); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // the scene will get to mapped state and within that very frame do: + // 1. add dummy sync flush + // 2. switch to rendered state (show scene) + performFlush(0u); + update(); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + expectVertexArrayUploaded(); + // request show scene + showScene(); + + // if render target (or any other scene resources) is not uploaded, this would crash/assert + update(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, multipleFlushesWithRemoveAndAddResourceReferenceWhileAlreadyBlocked) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // create blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + createRenderable(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // add new resource reference + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // remove and add again the previously added resource reference + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }, 0u); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + + // update + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 1); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }); + expectVertexArrayUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // update + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, multipleFlushesWithAddAndRemoveBlockingResourceWhileAlreadyBlocked) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // create blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + createRenderable(); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // remove and add again the blocking resource + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }); + expectResourcesProvided(2); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + + // update + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock flushes + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }); + expectVertexArrayUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // update + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, multipleFlushesWithAddAndRemoveAndAddBlockingResourceReferenceWhileAlreadyBlocked) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // create blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + createRenderable(); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // add, remove and add again another blocking resource (MockResourceHash::IndexArrayHash) + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, 0u, 2); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + expectResourcesProvided(3); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + update(); + + // update + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 1); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); + expectVertexArrayUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // update + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, multipleFlushesWithRemoveAndAddAndRemoveBlockingResourceReferenceWhileAlreadyBlocked) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + // create blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + createRenderable(); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // remove, add and remove again another blocking resource (MockResourceHash::IndexArrayHash) + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 2); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, 0u, 1); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + expectResourcesProvided(3); + update(); + + // update + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unblock flushes + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, 0u, 2); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); + expectVertexArrayUploaded(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // update + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, willForceMapSceneAfterMaximumNumberOfPendingFlushesReached) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + // mapping blocked by effect + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + update(); + + // add blocking flush so that upcoming flushes are queuing up + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + setRenderableResources(); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // will force apply and log blocking resources + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); + for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(); + update(); + } + // expect force mapped + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, willForceMapSceneAfterMaximumWaitingTimeReached) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + createRenderable(); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + // mapping blocked by effect + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + update(); + + constexpr std::chrono::milliseconds testTimeout{ 30 }; + rendererSceneUpdater->setForceMapTimeout(testTimeout); + std::this_thread::sleep_for(testTimeout * 2); + update(); + update(); + + // expect force mapped + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, forceAppliesPendingFlushesAfterMaximumNumberReachedWhenSceneMappedOrRendered) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + update(); + + // add blocking flush so that upcoming flushes are queuing up + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + + setRenderableResources(); + update(); + + // type queried for logging of missing resources + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); + + // mapped state + { + // flushes are blocked due to unresolved resource + for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + } + + showScene(); + + // rendered state + { + // add new blocking flush so that upcoming flushes are queuing up + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + + // flushes are blocked due to unresolved resource + for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + } + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, reactsOnDynamicChangesOfFlushForceApplyLimit) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + update(); + + // add blocking flush so that upcoming flushes are queuing up + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + setRenderableResources(); + update(); + + // Reduce flush limit -> expect force flush earlier + constexpr size_t newShorterFlushLimit = ForceApplyFlushesLimit / 2u; + rendererSceneUpdater->setLimitFlushesForceApply(newShorterFlushLimit); + + // type queried for logging of missing resources + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); + + // mapped state + { + // flushes are blocked due to unresolved resource + for (size_t i = 0u; i < newShorterFlushLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + } + + showScene(); + + // rendered state + { + // add new blocking flush so that upcoming flushes are queuing up + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }); + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash2 }); + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceDeviceHandle(MockResourceHash::IndexArrayHash2)).Times(AnyNumber()).WillRepeatedly(Return(DeviceResourceHandle::Invalid())); + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + + // flushes are blocked due to unresolved resource + for (size_t i = 0u; i < newShorterFlushLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + } + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, applyingPendingFlushesAfterMaximumNumberOfPendingFlushesReachedDoesNotAffectOtherScene) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createPublishAndSubscribeScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }, 0u); + createRenderable(0u); + mapScene(0u); + expectVertexArrayUploaded(0u); + showScene(0u); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }, 1u); + createRenderable(1u); + mapScene(1u); + expectVertexArrayUploaded(1u); + showScene(1u); + + // blocking resource + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }, 0u); + setRenderableResources(0u, MockResourceHash::IndexArrayHash); + expectVertexArrayUnloaded(0u); + update(); + + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }, 1u); + setRenderableResources(1u, MockResourceHash::IndexArrayHash); + expectVertexArrayUnloaded(1u); + update(); + + // type queried for logging of missing resources + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); + + { + // flushes are blocked due to unresolved resource + expectVertexArrayUploaded(1u); + for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(1u); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + // but only for scene 1, scene 0 stays blocked as the number of pending flushes there is below the maximum threshold + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene(0u)); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(1u)); + + // repeat for scene 0 + expectVertexArrayUploaded(0u); + for (size_t i = 0u; i < ForceApplyFlushesLimit + 1u; ++i) + { + performFlushWithCreateNodeAction(0u); + update(); + } + + // after maximum of pending flushes was reached the flushes were applied regardless of missing resource + // also for scene 0 now + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(0u)); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene(1u)); + } + + hideScene(0u); + hideScene(1u); + unmapScene(0u); + unmapScene(1u); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, nonBlockingFlushesGetAppliedEvenIfSceneIsBlockedToBeMappedDueToInvalidResourceFromBefore) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + // scene is now blocked to be mapped due to effect + + // blocking flush cannot be applied + expectResourcesReferencedAndProvided({ MockResourceHash::IndexArrayHash }); + setRenderableResources(); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + // scene still cannot be mapped because it still uses effect, also flushes are blocking now + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock indices meaning that the pending flushes are non-blocking + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Uploaded); + update(); + // pending flushes were applied even though scene is waiting to be mapped + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + // scene still cannot be mapped because it still uses effect + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + + // unblock also effect + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + update(); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canUnmapSceneWithPendingFlushAndRequestMapInSingleLoop) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + // blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + + // unmap and request map + unmapScene(); + expectResourcesReferenced({ MockResourceHash::EffectHash }); + requestMapScene(); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + + // scene is not mapped so pending flushes are applied to scene + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + // and scene switches to mapping/uploading state + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // next update causes all resources in use to be uploaded if available + update(); + // scene is now blocked to be mapped due to use of effect + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock effect + reportResourceAs(MockResourceHash::EffectHash, EResourceStatus::Uploaded); + update(); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, canUnmapSceneWithPendingFlushAndRequestMapAndAddAnotherPendingFlushInSingleLoop) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + // blocking flush + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + reportResourceAs(MockResourceHash::IndexArrayHash, EResourceStatus::Provided); + update(); + EXPECT_FALSE(lastFlushWasAppliedOnRendererScene()); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // unmap and request map + unmapScene(); + requestMapScene(); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // in addition add one more blocking flush replacing previous blocking resource + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + EXPECT_EQ(ESceneState::MapRequested, sceneStateExecutor.getSceneState(getSceneId())); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + // indices1 was unreferenced when unmapped + // it will be also replaced with indices2 when flush from above gets applied so it will not be requested again when mapped + // scene is not mapped so pending flushes are applied to scene + expectResourcesReferenced({ MockResourceHash::EffectHash }, 0u, 1); // sync logic to compensate missing ref for new logic when scene re-mapped + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, 0u, 1); // new logic (new flush with provided indices2) + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Provided); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + // and scene switches to mapping/uploading state + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); // new logic ref comes later before providing + + // next update causes all resources in use to be uploaded if available + expectResourcesProvided(1); // only indices2 is provided because it was kept by new logic for mapping + update(); + // scene is now blocked to be mapped due to use of indices2 + EXPECT_EQ(ESceneState::MappingAndUploading, sceneStateExecutor.getSceneState(getSceneId())); + + // unblock indices2 + reportResourceAs(MockResourceHash::IndexArrayHash2, EResourceStatus::Uploaded); + update(); + expectInternalSceneStateEvent(ERendererEventType::SceneMapped); + EXPECT_EQ(ESceneState::Mapped, sceneStateExecutor.getSceneState(getSceneId())); + + update(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, forceUnsubscribesSceneIfSceneResourcesUploadExceedsBudgetWhileMapping) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::SceneResourcesUpload, 0u); + + // create many scene resources (if not enough scene resources the budget is not even checked) + for (uint32_t i = 0; i < 40; ++i) + { + createRenderTargetWithBuffers(0u, RenderTargetHandle{ i }, RenderBufferHandle{ i * 2 }, RenderBufferHandle{ i * 2 + 1 }); + update(); + expectNoEvent(); + } + + // expect scene unsubscribe + requestMapScene(); + + // context is enabled twice, first before uploading, second when unloading due to forced unsubscribe/destroy + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(), _)).Times(AtLeast(2)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTarget(_, _, getSceneId())).Times(AnyNumber()); + // render buffers are collected first therefore render targets might or might not be uploaded before interruption, depending on checking frequency (internal logic of scene resources uploader) + + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); + + expectUnloadOfSceneResources(); + update(); + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect }); + + update(); + expectNoEvent(); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, forceUnsubscribesSceneIfSceneResourcesUploadExceedsBudgetWhileMapping_doesNotAffectOtherAlreadyRenderedScene) + { + createDisplayAndExpectSuccess(); + const auto sceneIdx1 = createPublishAndSubscribeScene(); + + // create scene with some resources and render it + mapScene(sceneIdx1); + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(sceneIdx1); + setRenderableResources(sceneIdx1); + expectVertexArrayUploaded(); + update(); + showScene(sceneIdx1); + + // next one is malicious scene with too many scene resources + const auto sceneIdx2 = createPublishAndSubscribeScene(); + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::SceneResourcesUpload, 0u); + + // create many scene resources (if not enough scene resources the budget is not even checked) + for (uint32_t i = 0; i < 40; ++i) + { + createRenderTargetWithBuffers(sceneIdx2, RenderTargetHandle{ i }, RenderBufferHandle{ i * 2 }, RenderBufferHandle{ i * 2 + 1 }); + update(); + expectNoEvent(); + } + + // expect scene unsubscribe + requestMapScene(sceneIdx2); + + // context is enabled twice, first before uploading, second when unloading due to forced unsubscribe/destroy + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTargetBuffer(_, getSceneId(sceneIdx2), _)).Times(AtLeast(2)); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadRenderTarget(_, _, getSceneId(sceneIdx2))).Times(AnyNumber()); + // render buffers are collected first therefore render targets might or might not be uploaded before interruption, depending on checking frequency (internal logic of scene resources uploader) + + EXPECT_CALL(sceneEventSender, sendUnsubscribeScene(_)); + + expectUnloadOfSceneResources(sceneIdx2); + update(); + expectInternalSceneStateEvents({ ERendererEventType::SceneMapFailed, ERendererEventType::SceneUnsubscribedIndirect }); + + update(); + expectNoEvent(); + + // hide and unmap first scene + hideScene(sceneIdx1); + unmapScene(sceneIdx1); + + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, confidenceTest_forceApplyPendingFlushes_keepCyclingThroughResourcesAndSimulateRandomlyCannotUpload) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + showScene(); + + const std::array resourcesToCycle{ MockResourceHash::IndexArrayHash, MockResourceHash::IndexArrayHash2, MockResourceHash::IndexArrayHash3 }; + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, resourcesToCycle[0] }); + createRenderable(); + setRenderableResources(0u, resourcesToCycle[0]); + expectVertexArrayUploaded(); + update(); + + constexpr int NumFlushesToForceApply = 5; + rendererSceneUpdater->setLimitFlushesForceApply(NumFlushesToForceApply); + + // due to random resources not uploaded strict un/ref mocking is not feasible + // important check is at end that resources ref count reaches zero + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, referenceResourcesForScene(getSceneId(), _)).Times(AnyNumber()); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unreferenceResourcesForScene(getSceneId(), _)).Times(AnyNumber()); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, provideResourceData(_)).Times(AnyNumber()); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, uploadVertexArray(_, _, getSceneId())).Times(AnyNumber()); + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, unloadVertexArray(_, getSceneId())).Times(AnyNumber()); + // will force apply and log blocking resources + EXPECT_CALL(*rendererSceneUpdater->m_resourceManagerMock, getResourceType(_)).Times(AnyNumber()); + + // start from 1, 0 is active at this point + for (int i = 1; i < 100; ++i) + { + ResourceContentHash nextRes = resourcesToCycle[i % 3]; + + if (lastFlushWasAppliedOnRendererScene()) + { + // reset reporting of all resources to default - uploaded + for (const auto r : resourcesToCycle) + reportResourceAs(r, EResourceStatus::Uploaded); + + // randomly block next resource + if (TestRandom::Get(0, 10) > 7) + reportResourceAs(nextRes, EResourceStatus::Provided); + } + + setRenderableResources(0u, nextRes); + update(); + } + + destroyRenderable(); + update(); + + // in case loop ended with blocking flush reset reporting of all resources to uploaded + for (const auto r : resourcesToCycle) + reportResourceAs(r, EResourceStatus::Uploaded); + update(); + EXPECT_TRUE(lastFlushWasAppliedOnRendererScene()); + + // make sure that after destroying renderable reference counts reach zero + expectNoResourceReferencedByScene(); + + hideScene(); + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, referencesAndUnreferencesResourcesSharedByTwoRenderablesFromTwoScenes) + { + createDisplayAndExpectSuccess(); + + const auto s1 = createPublishAndSubscribeScene(); + const auto s2 = createPublishAndSubscribeScene(); + mapScene(s1); + mapScene(s2); + + expectResourcesReferenced({ MockResourceHash::EffectHash }, s1); + expectResourcesReferenced({ MockResourceHash::EffectHash }, s2); + expectResourcesProvided(2); + createRenderable(s1); + createRenderable(s2); + update(); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); + + // res1 used + { + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s1); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s2); + expectResourcesProvided(2); + setRenderableResources(s1); + setRenderableResources(s2); + update(); + } + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); + + // res2 used, res1 unneeded + { + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, s1); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash }, s2); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, s1); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }, s2); + expectResourcesProvided(2); + setRenderableResources(s1, MockResourceHash::IndexArrayHash2); + setRenderableResources(s2, MockResourceHash::IndexArrayHash2); + update(); + } + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); + + // res3 used, res2 unneeded, res1 unloaded + { + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, s1); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash2 }, s2); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash3 }, s1); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash3 }, s2); + expectResourcesProvided(2); + setRenderableResources(s1, MockResourceHash::IndexArrayHash3); + setRenderableResources(s2, MockResourceHash::IndexArrayHash3); + update(); + } + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash3)); + + // res1 used, res3 unneeded, res2 unloaded + { + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash3 }, s1); + expectResourcesUnreferenced({ MockResourceHash::IndexArrayHash3 }, s2); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s1); + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }, s2); + expectResourcesProvided(2); + setRenderableResources(s1, MockResourceHash::IndexArrayHash); + setRenderableResources(s2, MockResourceHash::IndexArrayHash); + update(); + } + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(2, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); + + destroyRenderable(s1); + destroyRenderable(s2); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s1); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }, s2); + update(); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash3)); + expectNoResourceReferencedByScene(s1); + expectNoResourceReferencedByScene(s2); + + unmapScene(s1); + unmapScene(s2); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, ConsolidatesNoMoreNeededResourcesBeforeMapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + createRenderable(); + + // res1 used + setRenderableResources(); + update(); + + // res2 used, res1 unneeded + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + update(); + + // res3 used, res1 + res2 unneeded + setRenderableResources(0u, MockResourceHash::IndexArrayHash3); + update(); + + // even if cycled through 3 resources only last one is uploaded + expectResourcesReferencedAndProvided_altogether({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash3 }); + mapScene(); + + destroyRenderable(); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash3 }); + update(); + expectNoResourceReferencedByScene(); + + unmapScene(); + destroyDisplay(); + } + + ///////////////////// + // tests to make sure request logic and new push logic can co-exist + ///////////////////// + + TEST_F(ARendererSceneUpdater, keepsTrackOfRefsAfterSceneRemapped) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + update(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + unmapScene(); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + expectResourcesReferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + mapScene(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + destroyRenderable(); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + update(); + expectNoResourceReferencedByScene(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, keepsTrackOfRefsAfterSceneRemapped_resourceAddedInBetweenReMappingIsProvided) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash }); + createRenderable(); + update(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + unmapScene(); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + setRenderableResources(); + + expectResourcesReferenced({ MockResourceHash::EffectHash }); // extra ref due to re-map for new logic + expectResourcesReferenced({ MockResourceHash::IndexArrayHash }); // ref for new logic from arrived resource + expectResourcesProvided(); // provided resource added in between mapping + mapScene(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + + destroyRenderable(); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + update(); + expectNoResourceReferencedByScene(); + + unmapScene(); + destroyDisplay(); + } + + TEST_F(ARendererSceneUpdater, keepsTrackOfRefsAfterSceneRemapped_resourceAddedAndRemovedInBetweenReMapping) + { + createDisplayAndExpectSuccess(); + createPublishAndSubscribeScene(); + mapScene(); + + expectResourcesReferencedAndProvided({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash }); + createRenderable(); + setRenderableResources(); + update(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + unmapScene(); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + setRenderableResources(0u, MockResourceHash::IndexArrayHash2); + + expectResourcesReferenced({ MockResourceHash::EffectHash }); // extra ref due to re-map for new logic + expectResourcesReferenced({ MockResourceHash::IndexArrayHash2 }); // ref for new logic from arrived resource + expectResourcesProvided(); // provided resource added in between mapping + mapScene(); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::EffectHash)); + EXPECT_EQ(0, getResourceRefCount(MockResourceHash::IndexArrayHash)); + EXPECT_EQ(1, getResourceRefCount(MockResourceHash::IndexArrayHash2)); + + destroyRenderable(); + expectResourcesUnreferenced({ MockResourceHash::EffectHash, MockResourceHash::IndexArrayHash2 }); + update(); + expectNoResourceReferencedByScene(); + + unmapScene(); + destroyDisplay(); + } + +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererScenesTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererScenesTest.cpp new file mode 100644 index 000000000..9a7eb5eb2 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererScenesTest.cpp @@ -0,0 +1,103 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" + +#include + +using namespace testing; +namespace ramses::internal +{ + class ARendererScenes : public ::testing::Test + { + public: + ARendererScenes() + : rendererScenes(eventCollector) + { + } + + protected: + RendererEventCollector eventCollector; + RendererScenes rendererScenes; + }; + + TEST_F(ARendererScenes, isEmptyInitially) + { + EXPECT_EQ(rendererScenes.begin(), rendererScenes.end()); + EXPECT_EQ(0u, rendererScenes.size()); + } + + TEST_F(ARendererScenes, isEmptyInitiallyConst) + { + const RendererScenes rendererScenesConst(eventCollector); + EXPECT_EQ(rendererScenesConst.begin(), rendererScenesConst.end()); + EXPECT_EQ(0u, rendererScenesConst.size()); + } + + TEST_F(ARendererScenes, createsSceneAndStagingInfo) + { + const std::string sceneName("bla"); + const SceneId sceneID(12u); + SceneInfo sceneInfo(sceneID, sceneName); + IScene& createdScene = rendererScenes.createScene(sceneInfo); + + SceneSizeInformation sceneSizeInfo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18); + createdScene.preallocateSceneSize(sceneSizeInfo); + + EXPECT_EQ(1u, rendererScenes.size()); + EXPECT_NE(rendererScenes.begin(), rendererScenes.end()); + EXPECT_TRUE(rendererScenes.hasScene(sceneID)); + EXPECT_EQ(&createdScene, &rendererScenes.getScene(sceneID)); + EXPECT_EQ(sceneSizeInfo, createdScene.getSceneSizeInformation()); + EXPECT_EQ(sceneName, createdScene.getName()); + EXPECT_EQ(sceneID, createdScene.getSceneId()); + rendererScenes.getStagingInfo(sceneID); + } + + TEST_F(ARendererScenes, destroysSceneAndStagingInfo) + { + const SceneId sceneID(12u); + rendererScenes.createScene(SceneInfo(sceneID)); + + rendererScenes.destroyScene(sceneID); + + EXPECT_EQ(0u, rendererScenes.size()); + EXPECT_EQ(rendererScenes.begin(), rendererScenes.end()); + EXPECT_FALSE(rendererScenes.hasScene(sceneID)); + } + + TEST_F(ARendererScenes, canIterateOverScenes) + { + const SceneId sceneID1(12u); + rendererScenes.createScene(SceneInfo(sceneID1)); + + const SceneId sceneID2(13u); + rendererScenes.createScene(SceneInfo(sceneID2)); + + const SceneId sceneID3(14u); + rendererScenes.createScene(SceneInfo(sceneID3)); + + EXPECT_EQ(3u, rendererScenes.size()); + EXPECT_NE(rendererScenes.begin(), rendererScenes.end()); + EXPECT_TRUE(rendererScenes.hasScene(sceneID1)); + EXPECT_TRUE(rendererScenes.hasScene(sceneID2)); + EXPECT_TRUE(rendererScenes.hasScene(sceneID3)); + + uint32_t count = 0u; + for(auto rendScene : rendererScenes) + { + EXPECT_TRUE(sceneID1 == rendScene.key || sceneID2 == rendScene.key || sceneID3 == rendScene.key); + EXPECT_TRUE(nullptr != rendScene.value.scene); + EXPECT_TRUE(nullptr != rendScene.value.stagingInfo); + ++count; + } + EXPECT_EQ(3u, count); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererStatisticsTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererStatisticsTest.cpp new file mode 100644 index 000000000..4099d271f --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererStatisticsTest.cpp @@ -0,0 +1,423 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/Core/Utils/LogMacros.h" +#include "gmock/gmock.h" + +using namespace testing; + +namespace ramses::internal +{ + class ARendererStatistics : public ::testing::Test + { + public: + [[nodiscard]] std::string logOutput() const + { + StringOutputStream strstr; + stats.writeStatsToStream(strstr); + return strstr.release(); + } + + protected: + RendererStatistics stats; + const SceneId sceneId1{ 11 }; + const SceneId sceneId2{ 22 }; + const DeviceResourceHandle ob1{ 11 }; + const DeviceResourceHandle ob2{ 22 }; + const DeviceResourceHandle ob3{ 33 }; + }; + + TEST_F(ARendererStatistics, tracksDrawCallsPerFrame) + { + stats.frameFinished(1u); + stats.frameFinished(2u); + stats.frameFinished(3u); + stats.frameFinished(4u); + EXPECT_EQ(2u, stats.getDrawCallsPerFrame()); + + stats.reset(); + EXPECT_EQ(0u, stats.getDrawCallsPerFrame()); + + stats.frameFinished(3u); + stats.frameFinished(3u); + EXPECT_EQ(3u, stats.getDrawCallsPerFrame()); + } + + TEST_F(ARendererStatistics, tracksFrameCount) + { + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 3")); + } + + TEST_F(ARendererStatistics, tracksSceneRenderedCount) + { + stats.sceneRendered(sceneId1); + stats.frameFinished(0u); + stats.sceneRendered(sceneId1); + stats.sceneRendered(sceneId2); + stats.frameFinished(0u); + stats.sceneRendered(sceneId1); + stats.sceneRendered(sceneId2); + stats.frameFinished(0u); + stats.sceneRendered(sceneId2); + + EXPECT_THAT(logOutput(), HasSubstr("Scene 11: rendered 3")); + EXPECT_THAT(logOutput(), HasSubstr("Scene 22: rendered 3")); + } + + TEST_F(ARendererStatistics, untracksScene) + { + stats.sceneRendered(sceneId1); + stats.frameFinished(0u); + stats.sceneRendered(sceneId1); + stats.sceneRendered(sceneId2); + stats.frameFinished(0u); + + stats.untrackScene(sceneId1); + + EXPECT_THAT(logOutput(), Not(HasSubstr("Scene 11"))); + EXPECT_THAT(logOutput(), HasSubstr("Scene 22: rendered 1")); + } + + TEST_F(ARendererStatistics, tracksSceneArrivedFlushesIndependentlyFromFrames) + { + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.trackArrivedFlush(sceneId2, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.trackArrivedFlush(sceneId2, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.frameFinished(0u); + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + + EXPECT_THAT(logOutput(), HasSubstr("Scene 11:")); + EXPECT_THAT(logOutput(), HasSubstr("FArrived 3")); + EXPECT_THAT(logOutput(), HasSubstr("Scene 22:")); + EXPECT_THAT(logOutput(), HasSubstr("FArrived 2")); + } + + TEST_F(ARendererStatistics, tracksFramesWhereSceneFlushArrived) + { + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.frameFinished(0u); + stats.trackArrivedFlush(sceneId1, 1, 2, 3, 4, std::chrono::milliseconds{0}); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 4")); + EXPECT_THAT(logOutput(), HasSubstr("framesFArrived 2")); + } + + TEST_F(ARendererStatistics, tracksFramesWhereSceneFlushApplied) + { + stats.flushApplied(sceneId1); + stats.flushApplied(sceneId1); + stats.flushApplied(sceneId1); + stats.frameFinished(0u); + stats.flushApplied(sceneId1); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 4")); + EXPECT_THAT(logOutput(), HasSubstr("framesFApplied 2")); + } + + TEST_F(ARendererStatistics, tracksFramesWhereSceneFlushBlocked) + { + stats.flushBlocked(sceneId1); + stats.flushBlocked(sceneId1); + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 4")); + EXPECT_THAT(logOutput(), HasSubstr("framesFBlocked 2")); + } + + TEST_F(ARendererStatistics, tracksMaximumConsecutiveFramesWhereSceneFlushNotApplied) + { + stats.flushApplied(sceneId1); + stats.flushApplied(sceneId1); + stats.flushApplied(sceneId1); + stats.frameFinished(0u); + stats.flushApplied(sceneId1); + stats.frameFinished(0u); + stats.frameFinished(0u); //x + stats.frameFinished(0u); //x + stats.flushApplied(sceneId1); + stats.flushApplied(sceneId1); + stats.frameFinished(0u); + stats.frameFinished(0u); //x + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 6")); + EXPECT_THAT(logOutput(), HasSubstr("FApplied 6")); + EXPECT_THAT(logOutput(), HasSubstr("framesFApplied 3")); + EXPECT_THAT(logOutput(), HasSubstr("maxFramesWithNoFApplied 2")); + } + + TEST_F(ARendererStatistics, tracksMaximumConsecutiveFramesWhereSceneFlushBlocked) + { + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + stats.flushBlocked(sceneId1); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 6")); + EXPECT_THAT(logOutput(), HasSubstr("framesFBlocked 5")); + EXPECT_THAT(logOutput(), HasSubstr("maxFramesFBlocked 3")); + } + + TEST_F(ARendererStatistics, tracksFramebufferAndOffscreenBufferSwapCounts) + { + stats.framebufferSwapped(); + stats.offscreenBufferSwapped(ob1, false); + stats.framebufferSwapped(); + stats.offscreenBufferSwapped(ob2, false); + stats.offscreenBufferSwapped(ob3, false); + stats.frameFinished(0u); + + stats.framebufferSwapped(); + stats.offscreenBufferSwapped(ob1, false); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("FB: 3; OB11: 2; OB22: 1; OB33: 1")); + } + + TEST_F(ARendererStatistics, tracksInterruptibleOffscreenBuffer) + { + stats.offscreenBufferInterrupted(ob1); + stats.frameFinished(0u); + stats.offscreenBufferSwapped(ob1, true); + stats.frameFinished(0u); + stats.offscreenBufferInterrupted(ob1); + stats.frameFinished(0u); + stats.offscreenBufferSwapped(ob1, true); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("OB11: 2 (intr: 2)")); + } + + TEST_F(ARendererStatistics, untracksOffscreenBuffer) + { + stats.offscreenBufferSwapped(ob1, false); + stats.offscreenBufferSwapped(ob2, false); + stats.offscreenBufferSwapped(ob3, false); + stats.frameFinished(0u); + stats.untrackOffscreenBuffer(ob2); + + EXPECT_THAT(logOutput(), Not(HasSubstr("OB22"))); + } + + TEST_F(ARendererStatistics, tracksStreamTextureSource) + { + const WaylandIviSurfaceId src{ 99u }; + stats.streamTextureUpdated(src, 2u); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.streamTextureUpdated(src, 9u); + stats.frameFinished(0u); + stats.streamTextureUpdated(src, 1u); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("numFrames 5")); + EXPECT_THAT(logOutput(), HasSubstr("SourceId ivi-surface:99: upd 12, framesUpd 3, maxUpdInFrame 9, maxFramesWithNoUpd 2")); +} + + TEST_F(ARendererStatistics, logsValidNumbersWhenStreamTextureInactive) + { + const WaylandIviSurfaceId src{ 99u }; + stats.streamTextureUpdated(src, 2u); // will register source + stats.reset(); + stats.frameFinished(0u); + stats.frameFinished(0u); + stats.frameFinished(0u); + + EXPECT_THAT(logOutput(), HasSubstr("SourceId ivi-surface:99: upd 0, framesUpd 0, maxUpdInFrame 0, maxFramesWithNoUpd 3")); +} + + TEST_F(ARendererStatistics, untracksStreamTextureSource) + { + const WaylandIviSurfaceId src{ 99u }; + stats.streamTextureUpdated(src, 2u); + stats.frameFinished(0u); + + stats.untrackStreamTexture(src); + + EXPECT_THAT(logOutput(), Not(HasSubstr("SourceId ivi-surface:99"))); +} + + TEST_F(ARendererStatistics, tracksResourceUploads) + { + stats.resourceUploaded(2u); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("resUploaded 1 (2 B)")); + + stats.reset(); + EXPECT_THAT(logOutput(), Not(HasSubstr("resUploaded"))); + + stats.resourceUploaded(2u); + stats.resourceUploaded(77u); + stats.resourceUploaded(100u); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("resUploaded 3 (179 B)")); + + stats.reset(); + EXPECT_THAT(logOutput(), Not(HasSubstr("resUploaded"))); + } + + TEST_F(ARendererStatistics, tracksSceneResourceUploads) + { + stats.sceneResourceUploaded(sceneId1, 2u); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("RSUploaded 1 (2 B)")); + + stats.reset(); + EXPECT_THAT(logOutput(), Not(HasSubstr("RSUploaded"))); + + stats.sceneResourceUploaded(sceneId1, 2u); + stats.sceneResourceUploaded(sceneId1, 77u); + stats.sceneResourceUploaded(sceneId2, 100u); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("RSUploaded 2 (79 B)")); //scene1 + EXPECT_THAT(logOutput(), HasSubstr("RSUploaded 1 (100 B)")); //scene2 + + stats.reset(); + EXPECT_THAT(logOutput(), Not(HasSubstr("RSUploaded"))); + } + + TEST_F(ARendererStatistics, tracksShaderCompilationAndTimes) + { + stats.shaderCompiled(std::chrono::microseconds(2u), "some effect", SceneId(123)); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("shadersCompiled 1")); + EXPECT_THAT(logOutput(), HasSubstr("for total ms:0")); + + stats.reset(); + EXPECT_THAT(logOutput(), Not(HasSubstr("shadersCompiled"))); + + stats.shaderCompiled(std::chrono::microseconds(3000u), "some effect name", SceneId(123)); + stats.shaderCompiled(std::chrono::microseconds(5000u), "some effect name", SceneId(124)); + stats.shaderCompiled(std::chrono::microseconds(7000u), "longest effect name", SceneId(125)); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("shadersCompiled 3")); + EXPECT_THAT(logOutput(), HasSubstr("for total ms:15")); + EXPECT_THAT(logOutput(), HasSubstr("longest: longest effect name")); + EXPECT_THAT(logOutput(), HasSubstr("from scene:125")); + EXPECT_THAT(logOutput(), HasSubstr("ms:7")); + + stats.reset(); + EXPECT_THAT(logOutput(), Not(HasSubstr("shadersCompiled"))); + } + + + TEST_F(ARendererStatistics, tracksExpirationOffsets) + { + stats.addExpirationOffset(sceneId1, -100); + stats.addExpirationOffset(sceneId1, -80); + stats.addExpirationOffset(sceneId1, -120); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/3:-120/-80/-100)")); + stats.reset(); + + stats.addExpirationOffset(sceneId1, -30); + stats.addExpirationOffset(sceneId1, 10); + stats.addExpirationOffset(sceneId1, -10); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (1/3:-30/10/-10)")); + stats.reset(); + + stats.addExpirationOffset(sceneId1, -30); + stats.addExpirationOffset(sceneId2, 30); + stats.addExpirationOffset(sceneId1, -20); + stats.addExpirationOffset(sceneId2, 20); + stats.addExpirationOffset(sceneId1, -10); + stats.addExpirationOffset(sceneId2, 10); + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/3:-30/-10/-20)")); + EXPECT_THAT(logOutput(), HasSubstr("Exp (3/3:10/30/20)")); + stats.reset(); + + stats.frameFinished(0u); + EXPECT_THAT(logOutput(), Not(HasSubstr("Exp ("))); + } + + + TEST_F(ARendererStatistics, confidenceTest_fullLogOutput) + { + for (size_t period = 0u; period < 2u; ++period) + { + stats.trackArrivedFlush(sceneId1, 123, 5, 3, 4, std::chrono::milliseconds{2}); + stats.trackArrivedFlush(sceneId2, 6, 7, 8, 9, std::chrono::milliseconds{5}); + stats.flushBlocked(sceneId1); + stats.flushApplied(sceneId2); + stats.framebufferSwapped(); + stats.frameFinished(0); + + stats.resourceUploaded(2u); + stats.resourceUploaded(77u); + stats.shaderCompiled(std::chrono::microseconds(0u), "", SceneId(54321)); + stats.shaderCompiled(std::chrono::microseconds(1000u), "slow effect", SceneId(12345)); + + stats.trackArrivedFlush(sceneId1, 123, 5, 3, 4, std::chrono::milliseconds{3}); + stats.flushBlocked(sceneId1); + stats.sceneRendered(sceneId2); + stats.framebufferSwapped(); + stats.frameFinished(100); + + stats.trackArrivedFlush(sceneId1, 123, 5, 3, 4, std::chrono::milliseconds{6}); + stats.trackArrivedFlush(sceneId2, 6, 7, 8, 9, std::chrono::milliseconds{11}); + stats.flushApplied(sceneId1); + stats.flushBlocked(sceneId2); + stats.sceneRendered(sceneId1); + stats.sceneRendered(sceneId2); + stats.framebufferSwapped(); + stats.offscreenBufferInterrupted(ob1); + stats.framebufferSwapped(); + stats.frameFinished(0); + + stats.sceneResourceUploaded(sceneId1, 3u); + stats.sceneResourceUploaded(sceneId1, 77u); + stats.sceneResourceUploaded(sceneId2, 200u); + + stats.sceneRendered(sceneId1); + stats.framebufferSwapped(); + stats.offscreenBufferSwapped(ob1, true); + stats.frameFinished(0); + + EXPECT_THAT(logOutput(), HasSubstr("Avg framerate: ")); + EXPECT_THAT(logOutput(), HasSubstr("FPS [minFrameTime ")); + EXPECT_THAT(logOutput(), HasSubstr("us, maxFrameTime ")); + EXPECT_THAT(logOutput(), HasSubstr("], drawcallsPerFrame 25, numFrames 4")); + EXPECT_THAT(logOutput(), HasSubstr("resUploaded 2 (79 B)")); + EXPECT_THAT(logOutput(), HasSubstr("shadersCompiled 2")); + EXPECT_THAT(logOutput(), HasSubstr("for total ms:1")); + EXPECT_THAT(logOutput(), HasSubstr("FB: 5; OB11: 1 (intr: 1)")); + EXPECT_THAT(logOutput(), HasSubstr("Scene 11: rendered 2, framesFArrived 3, framesFApplied 1, framesFBlocked 2, maxFramesWithNoFApplied 2, maxFramesFBlocked 2, FArrived 3, FApplied 1, actions/F (123/123/123), dt/F (2/6/3.6666667), RC+/F (5/5/5), RC-/F (3/3/3), RS/F (4/4/4), RSUploaded 2 (80 B)")); + EXPECT_THAT(logOutput(), HasSubstr("Scene 22: rendered 2, framesFArrived 2, framesFApplied 1, framesFBlocked 1, maxFramesWithNoFApplied 3, maxFramesFBlocked 1, FArrived 2, FApplied 1, actions/F (6/6/6), dt/F (5/11/8), RC+/F (7/7/7), RC-/F (8/8/8), RS/F (9/9/9), RSUploaded 1 (200 B)")); + EXPECT_THAT(logOutput(), HasSubstr("slow effect")); + stats.reset(); + } + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/RendererTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/RendererTest.cpp new file mode 100644 index 000000000..db356526f --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/RendererTest.cpp @@ -0,0 +1,2665 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/RendererConfig.h" +#include "RenderBackendMock.h" +#include "PlatformMock.h" +#include "internal/RendererLib/RenderingContext.h" +#include "internal/RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RendererCachedScene.h" +#include "internal/RendererLib/DisplayController.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "DisplayControllerMock.h" +#include "internal/PlatformAbstraction/Collections/Pair.h" +#include "RendererMock.h" +#include "ComponentMocks.h" +#include "TestSceneHelper.h" +#include +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal +{ + class ARenderer : public ::testing::TestWithParam // parametrized with/out system compositor + { + public: + ARenderer() + : rendererScenes(rendererEventCollector) + , expirationMonitor(rendererScenes, rendererEventCollector, rendererStatistics) + , renderer(DisplayHandle{ 1u }, rendererScenes, rendererEventCollector, expirationMonitor, rendererStatistics) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + sceneRenderInterrupted.incrementRenderableIdx(); + sceneRenderInterrupted2.incrementRenderableIdx(); + sceneRenderInterrupted2.incrementRenderableIdx(); + + // Enable/disable SC + if (GetParam()) + ON_CALL(renderer.m_platform, getSystemCompositorController()).WillByDefault(Return(&renderer.m_platform.systemCompositorControllerMock)); + } + + ~ARenderer() override + { + if (renderer.hasDisplayController()) + destroyDisplayController(); + for (const auto& sceneIt : rendererScenes) + expirationMonitor.onDestroyed(sceneIt.key); + } + + void createDisplayController() + { + ASSERT_FALSE(renderer.hasDisplayController()); + renderer.createDisplayContext({}); + } + + void destroyDisplayController() + { + ASSERT_TRUE(renderer.hasDisplayController()); + if (GetParam()) + { + EXPECT_CALL(*renderer.m_displayController, getRenderBackend()); + EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, getWaylandIviSurfaceID()); + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, destroySurface(_)); + } + EXPECT_CALL(renderer.m_platform, destroyRenderBackend()); + renderer.destroyDisplayContext(); + } + + void expectOffscreenBufferCleared(DeviceResourceHandle buffer, ClearFlags clearFlags = EClearFlag::All, const glm::vec4& clearColor = Renderer::DefaultClearColor) + { + EXPECT_CALL(*renderer.m_displayController, clearBuffer(buffer, clearFlags, clearColor)).InSequence(SeqRender); + } + + void expectInterruptibleOffscreenBufferSwapped(DeviceResourceHandle buffer) + { + EXPECT_CALL(renderer.m_platform.renderBackendMock.deviceMock, swapDoubleBufferedRenderTarget(buffer)).InSequence(SeqRender); + + // rendering to interruptible buffers calls this uninteresting getter which gets reset with gmock verification + EXPECT_CALL(*renderer.m_displayController, getRenderBackend()).Times(AnyNumber()); + } + + void expectFrameBufferRendered(bool expectRerender = true, ClearFlags expectRendererClear = EClearFlag::All, const glm::vec4& clearColor = Renderer::DefaultClearColor) + { + EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()).InSequence(SeqPreRender); + EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).InSequence(SeqPreRender).WillOnce(Return(true)); + + if (expectRerender) + { + // normally render executor clears, in some cases (no scene assigned) renderer clears instead + if (expectRendererClear != EClearFlag::None) + EXPECT_CALL(*renderer.m_displayController, clearBuffer(DisplayControllerMock::FakeFrameBufferHandle, expectRendererClear, clearColor)); + } + else + { + EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).InSequence(SeqRender); + EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).InSequence(SeqRender); + } + } + + void expectSwapBuffers() + { + EXPECT_CALL(*renderer.m_displayController, swapBuffers()).InSequence(SeqRender); + EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).InSequence(SeqRender); + EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).InSequence(SeqRender); + } + + void doOneRendererLoop() + { + renderer.getProfilerStatistics().markFrameFinished(std::chrono::microseconds{ 0u }); + renderer.doOneRenderLoop(); + } + + enum class EDiscardDepth + { + Allowed, + Disallowed + }; + + void expectSceneRenderedExt( + SceneId sceneId, + DeviceResourceHandle buffer, + ClearFlags dispBufferClearFlags, + const glm::vec4& dispBufferClearColor, + SceneRenderExecutionIterator expectedRenderBegin, + SceneRenderExecutionIterator iteratorToReturn, + ClearFlags clearFlagsToModify, + EDiscardDepth discardAllowed, + const FrameTimer* frameTimer = nullptr) + { + EXPECT_CALL(*renderer.m_displayController, renderScene(Ref(rendererScenes.getScene(sceneId)), _, frameTimer)) + .WillOnce([=](const auto& /*unused*/, RenderingContext& renderContext, const auto* /*unused*/) { + EXPECT_EQ(buffer, renderContext.displayBufferDeviceHandle); + EXPECT_EQ(expectedRenderBegin, renderContext.renderFrom); + EXPECT_EQ(dispBufferClearFlags, renderContext.displayBufferClearPending); + if (dispBufferClearFlags != EClearFlag::None) // color is relevant only if clearing something + { + EXPECT_EQ(dispBufferClearColor, renderContext.displayBufferClearColor); + } + renderContext.displayBufferClearPending = clearFlagsToModify; + EXPECT_EQ((discardAllowed == EDiscardDepth::Allowed), renderContext.displayBufferDepthDiscard); + return iteratorToReturn; + }); + } + + void expectSceneRendered(SceneId sceneId, DeviceResourceHandle buffer = DisplayControllerMock::FakeFrameBufferHandle, + ClearFlags dispBufferClearFlags = EClearFlag::All, const glm::vec4& dispBufferClearColor = Renderer::DefaultClearColor) + { + expectSceneRenderedExt(sceneId, buffer, dispBufferClearFlags, dispBufferClearColor, sceneRenderBegin, sceneRenderBegin, dispBufferClearFlags, EDiscardDepth::Disallowed); + } + + void expectSceneRenderedWithInterruptionEnabled(SceneId sceneId, DeviceResourceHandle buffer, SceneRenderExecutionIterator expectedRenderBegin, + SceneRenderExecutionIterator stateToSimulate, ClearFlags dispBufferClearFlags = EClearFlag::All) + { + expectSceneRenderedExt(sceneId, buffer, dispBufferClearFlags, Renderer::DefaultClearColor, expectedRenderBegin, stateToSimulate, dispBufferClearFlags, EDiscardDepth::Disallowed, &RendererMock::FrameTimerInstance); + } + + void expectSceneRendered(SceneId sceneId, DeviceResourceHandle buffer, EDiscardDepth discardAllowed) + { + expectSceneRenderedExt(sceneId, buffer, EClearFlag::All, Renderer::DefaultClearColor, sceneRenderBegin, sceneRenderBegin, EClearFlag::All, discardAllowed); + } + + void expectDisplayControllerReadPixels(DeviceResourceHandle deviceHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + EXPECT_CALL(*renderer.m_displayController, readPixels(deviceHandle, x, y, width, height, _)).WillOnce(Invoke( + [](auto /*unused*/, auto /*unused*/, auto /*unused*/, auto w, auto h, auto& dataOut) { + dataOut.resize(w * h * 4); + } + )); + } + + IScene& createScene(SceneId sceneId = SceneId()) + { + rendererScenes.createScene(SceneInfo(sceneId)); + return rendererScenes.getScene(sceneId); + } + + void assignSceneToDisplayBuffer(SceneId sceneId, int32_t sceneRenderOrder, DeviceResourceHandle displayBuffer = DisplayControllerMock::FakeFrameBufferHandle) + { + renderer.assignSceneToDisplayBuffer(sceneId, displayBuffer, sceneRenderOrder); + EXPECT_EQ(displayBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_EQ(sceneRenderOrder, renderer.getSceneGlobalOrder(sceneId)); + } + + void unassignScene(SceneId sceneId) + { + renderer.unassignScene(sceneId); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + void showScene(SceneId sceneId) + { + renderer.setSceneShown(sceneId, true); + } + + void hideScene(SceneId sceneId) + { + renderer.setSceneShown(sceneId, false); + } + + void initiateExpirationMonitoring(std::initializer_list scenes) + { + // A workaround to be able to check if scene was reported as rendered. + // Expiration monitor holds TS for applied flushes and TS for rendered scene. + // On rendered the TS of rendered scene is simply TS of last applied flush. + // Rendered scene TS can either be invalid - never reported as rendered, + // or the value below if reported as rendered + for (auto sceneId : scenes) + expirationMonitor.onFlushApplied(sceneId, currentFakeTime, {}, 0); + } + + void expectScenesReportedToExpirationMonitorAsRendered(std::initializer_list expectedScenesToBeReported) + { + for (auto sceneId : expectedScenesToBeReported) + { + EXPECT_NE(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(sceneId)); + } + } + + void scheduleScreenshot(const DeviceResourceHandle bufferHandle, uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + ScreenshotInfo screenshot; + screenshot.rectangle = { x, y, width, height }; + renderer.scheduleScreenshot(bufferHandle, std::move(screenshot)); + } + + protected: + RendererCommandBuffer rendererCommandBuffer; + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneExpirationMonitor expirationMonitor; + RendererStatistics rendererStatistics; + StrictMock renderer; + + const SceneRenderExecutionIterator sceneRenderBegin{}; + SceneRenderExecutionIterator sceneRenderInterrupted; + SceneRenderExecutionIterator sceneRenderInterrupted2; + + // sequence of ordered expectations during render + Sequence SeqRender; + // sequence of ordered expectations at beginning of render (display events, can render frame, etc.) + Sequence SeqPreRender; + + const FlushTime::Clock::time_point currentFakeTime{ std::chrono::milliseconds(1000) }; + }; + + INSTANTIATE_TEST_SUITE_P(, ARenderer, ::testing::Values(false, true)); + + TEST_P(ARenderer, ListIviSurfacesInSystemCompositorController) + { + if (GetParam()) + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, listIVISurfaces()); + renderer.systemCompositorListIviSurfaces(); + } + + TEST_P(ARenderer, SetsVisibilityInSystemCompositorController) + { + if (GetParam()) + { + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(1), false)); + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setSurfaceVisibility(WaylandIviSurfaceId(2), true)); + } + renderer.systemCompositorSetIviSurfaceVisibility(WaylandIviSurfaceId(1), false); + renderer.systemCompositorSetIviSurfaceVisibility(WaylandIviSurfaceId(2), true); + } + + TEST_P(ARenderer, TakesScreenshotFromSystemCompositorController) + { + std::string_view fileName("screenshot.png"); + const int32_t screenIviId = 3; + if (GetParam()) + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, doScreenshot(fileName, screenIviId)); + renderer.systemCompositorScreenshot(fileName, screenIviId); + } + + TEST_P(ARenderer, rendersOneLoop) + { + createDisplayController(); + EXPECT_TRUE(renderer.hasDisplayController()); + + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, unregisteredSceneIsNotMapped) + { + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(SceneId(0u)).isValid()); + } + + TEST_P(ARenderer, doesNotMapCreatedScene) + { + const SceneId sceneId(12u); + createScene(sceneId); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + TEST_P(ARenderer, canMapSceneOnDisplay) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + unassignScene(sceneId); + } + + TEST_P(ARenderer, assignsSceneToNativeFramebufferOfDisplayWhenMappingScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + + EXPECT_EQ(DisplayControllerMock::FakeFrameBufferHandle, renderer.getBufferSceneIsAssignedTo(sceneId)); + unassignScene(sceneId); + } + + TEST_P(ARenderer, assignsSceneToOffscreenBuffer) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + EXPECT_EQ(fakeOffscreenBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, assignsSceneToInterruptibleOffscreenBuffer) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + EXPECT_EQ(fakeOffscreenBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_TRUE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, assignsSceneFromInterruptibleOffscreenBufferToNormalOB) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + const DeviceResourceHandle ob(313u); + const DeviceResourceHandle obInterruptible(314u); + renderer.registerOffscreenBuffer(ob, 1u, 1u, false); + renderer.registerOffscreenBuffer(obInterruptible, 1u, 1u, true); + + assignSceneToDisplayBuffer(sceneId, 0, obInterruptible); + EXPECT_EQ(obInterruptible, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_TRUE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); + + assignSceneToDisplayBuffer(sceneId, 0, ob); + EXPECT_EQ(ob, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, SetsLayerVisibilityInSystemCompositorController) + { + if (GetParam()) + { + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(18u), false)); + EXPECT_CALL(renderer.m_platform.systemCompositorControllerMock, setLayerVisibility(WaylandIviLayerId(17u), true)); + } + renderer.systemCompositorSetIviLayerVisibility(WaylandIviLayerId(18u), false); + renderer.systemCompositorSetIviLayerVisibility(WaylandIviLayerId(17u), true); + } + + TEST_P(ARenderer, doesNotClearOrRenderToOffscreenBufferIfThereIsNoSceneAssignedToIt) + { + createDisplayController(); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + + expectFrameBufferRendered(true, EClearFlag::All); + // no offscreen buffer clear expectation + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, clearsOffscreenBufferIfThereIsSceneAssignedToItAndNotShown) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + + expectOffscreenBufferCleared(fakeOffscreenBuffer); + expectFrameBufferRendered(true, EClearFlag::All); + expectSwapBuffers(); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearsOffscreenBufferAndFramebufferWithRelatedColors) + { + const glm::vec4 displayClearColor(.1f, .2f, .3f, .4f); + createDisplayController(); + renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, displayClearColor); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + + expectOffscreenBufferCleared(fakeOffscreenBuffer); + expectFrameBufferRendered(true, EClearFlag::All, displayClearColor); + expectSwapBuffers(); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearsFramebufferWithCustomClearColor) + { + const glm::vec4 displayClearColor(.1f, .2f, .3f, .4f); + createDisplayController(); + renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, displayClearColor); + expectFrameBufferRendered(true, EClearFlag::All, displayClearColor); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, clearsOffscreenBufferWithCustomClearColor) + { + createDisplayController(); + + const glm::vec4 obClearColor(.1f, .2f, .3f, .4f); + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + renderer.setClearColor(fakeOffscreenBuffer, obClearColor); + + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + + expectOffscreenBufferCleared(fakeOffscreenBuffer, EClearFlag::All, obClearColor); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearsBothFramebufferAndOffscreenBufferWithDifferentClearColors) + { + createDisplayController(); + const glm::vec4 displayClearColor(.4f, .3f, .2f, .1f); + renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, displayClearColor); + + const glm::vec4 obClearColor(.1f, .2f, .3f, .4f); + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + renderer.setClearColor(fakeOffscreenBuffer, obClearColor); + + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + + expectOffscreenBufferCleared(fakeOffscreenBuffer, EClearFlag::All, obClearColor); + expectFrameBufferRendered(true, EClearFlag::All, displayClearColor); + expectSwapBuffers(); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearsFBIfNoSceneAssigned) + { + createDisplayController(); + + // use some non-default clear flags + EXPECT_CALL(renderer, setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, ClearFlags(EClearFlag::Depth))); + renderer.setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, EClearFlag::Depth); + + expectFrameBufferRendered(true, EClearFlag::Depth); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, clearsFBIfNoShownSceneAssigned) + { + createDisplayController(); + + // assign scene to trigger render of OB + constexpr SceneId sceneId1{ 12u }; + constexpr SceneId sceneId2{ 13u }; + createScene(sceneId1); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneId1, 0); + assignSceneToDisplayBuffer(sceneId2, 0); + + // use some non-default clear flags + EXPECT_CALL(renderer, setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, ClearFlags(EClearFlag::Depth))); + renderer.setClearFlags(DisplayControllerMock::FakeFrameBufferHandle, EClearFlag::Depth); + + expectFrameBufferRendered(true, EClearFlag::Depth); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, clearsOBOnRerenderIfNoSceneAssigned) + { + createDisplayController(); + + constexpr DeviceResourceHandle fakeOffscreenBuffer1{ 313u }; + constexpr DeviceResourceHandle fakeOffscreenBuffer2{ 314u }; + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, true); + + // use some non-default clear flags + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer1, ClearFlags(EClearFlag::Depth))); + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer2, ClearFlags(EClearFlag::Depth))); + EXPECT_CALL(renderer, setClearColor(fakeOffscreenBuffer1, glm::vec4{ 1,2,3,4 })); + EXPECT_CALL(renderer, setClearColor(fakeOffscreenBuffer2, Renderer::DefaultClearColor)); + renderer.setClearFlags(fakeOffscreenBuffer1, EClearFlag::Depth); + renderer.setClearFlags(fakeOffscreenBuffer2, EClearFlag::Depth); + renderer.setClearColor(fakeOffscreenBuffer1, glm::vec4{ 1,2,3,4 }); + renderer.setClearColor(fakeOffscreenBuffer2, Renderer::DefaultClearColor); + + expectOffscreenBufferCleared(fakeOffscreenBuffer1, EClearFlag::Depth, glm::vec4{ 1,2,3,4 }); + expectFrameBufferRendered(); + expectOffscreenBufferCleared(fakeOffscreenBuffer2, EClearFlag::Depth); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, clearsOBOnRerenderIfNoShownSceneAssigned) + { + createDisplayController(); + + constexpr DeviceResourceHandle fakeOffscreenBuffer1{ 313u }; + constexpr DeviceResourceHandle fakeOffscreenBuffer2{ 314u }; + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, true); + + // assign scene to trigger render of OB + constexpr SceneId sceneId1{ 12u }; + constexpr SceneId sceneId2{ 13u }; + createScene(sceneId1); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId2, 0, fakeOffscreenBuffer2); + + // use some non-default clear flags + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer1, ClearFlags(EClearFlag::Depth))); + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer2, ClearFlags(EClearFlag::Depth))); + renderer.setClearFlags(fakeOffscreenBuffer1, EClearFlag::Depth); + renderer.setClearFlags(fakeOffscreenBuffer2, EClearFlag::Depth); + + expectOffscreenBufferCleared(fakeOffscreenBuffer1, EClearFlag::Depth); + expectFrameBufferRendered(); + expectOffscreenBufferCleared(fakeOffscreenBuffer2, EClearFlag::Depth); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); + expectSwapBuffers(); + doOneRendererLoop(); + + unassignScene(sceneId1); + unassignScene(sceneId2); + } + + TEST_P(ARenderer, rendersTwoOffscreenBuffersWithContentInCorrectOrder) + { + createDisplayController(); + + DeviceResourceHandle fakeOffscreenBuffer1(313u); + DeviceResourceHandle fakeOffscreenBuffer2(314u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, false); + + const SceneId sceneId1(12u); + const SceneId sceneId2(13u); + createScene(sceneId1); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId2, 0, fakeOffscreenBuffer2); + showScene(sceneId1); + showScene(sceneId2); + + expectSceneRendered(sceneId1, fakeOffscreenBuffer1, EDiscardDepth::Allowed); + expectSceneRendered(sceneId2, fakeOffscreenBuffer2, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + unassignScene(sceneId1); + unassignScene(sceneId2); + } + + TEST_P(ARenderer, assignSceneToFramebufferFromPreviouslyAssignedToOffscreenBuffer) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId); + unassignScene(sceneId); + expectOffscreenBufferCleared(fakeOffscreenBuffer); // will also clear OB after scene is removed from it + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + const DeviceResourceHandle framebuffer = DisplayControllerMock::FakeFrameBufferHandle; + assignSceneToDisplayBuffer(sceneId, 0, framebuffer); + showScene(sceneId); + EXPECT_EQ(framebuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneId); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, rendersScenesInOrderAccordingToLocalOrderWithinDisplayBuffer) + { + createDisplayController(); + + const DeviceResourceHandle fakeOffscreenBuffer1(313u); + const DeviceResourceHandle fakeOffscreenBuffer2(314u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, false); + + const SceneId sceneId1(12u); + const SceneId sceneId2(13u); + const SceneId sceneId3(14u); + const SceneId sceneId4(15u); + createScene(sceneId1); + createScene(sceneId2); + createScene(sceneId3); + createScene(sceneId4); + assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId2, 1, fakeOffscreenBuffer2); + assignSceneToDisplayBuffer(sceneId3, 2, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId4, 3, fakeOffscreenBuffer2); + showScene(sceneId1); + showScene(sceneId2); + showScene(sceneId3); + showScene(sceneId4); + + { + InSequence s; + expectSceneRendered(sceneId1, fakeOffscreenBuffer1, EDiscardDepth::Disallowed); + expectSceneRendered(sceneId3, fakeOffscreenBuffer1, EDiscardDepth::Allowed); + } + { + InSequence s; + expectSceneRendered(sceneId2, fakeOffscreenBuffer2, EDiscardDepth::Disallowed); + expectSceneRendered(sceneId4, fakeOffscreenBuffer2, EDiscardDepth::Allowed); + } + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + hideScene(sceneId3); + hideScene(sceneId4); + unassignScene(sceneId1); + unassignScene(sceneId2); + unassignScene(sceneId3); + unassignScene(sceneId4); + } + + TEST_P(ARenderer, confidence_assignsSceneToOffscreenBufferAndReassignsToFramebuffer) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + EXPECT_EQ(fakeOffscreenBuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); + + const DeviceResourceHandle framebuffer = DisplayControllerMock::FakeFrameBufferHandle; + assignSceneToDisplayBuffer(sceneId, 0, framebuffer); + EXPECT_EQ(framebuffer, renderer.getBufferSceneIsAssignedTo(sceneId)); + EXPECT_FALSE(renderer.isSceneAssignedToInterruptibleOffscreenBuffer(sceneId)); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, returnsInvalidDisplayWhenQueryingLocationOfUnmappedScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + unassignScene(sceneId); + EXPECT_FALSE(renderer.getBufferSceneIsAssignedTo(sceneId).isValid()); + } + + TEST_P(ARenderer, doesNotRenderAMappedScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, rendersMappedAndShownScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, rendersTwoMappedAndShownScenes) + { + createDisplayController(); + + const SceneId sceneId1(12u); + createScene(sceneId1); + assignSceneToDisplayBuffer(sceneId1, 0); + showScene(sceneId1); + + const SceneId sceneId2(13u); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneId2, 0); + showScene(sceneId2); + + expectSceneRendered(sceneId1); + expectSceneRendered(sceneId2); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + unassignScene(sceneId1); + unassignScene(sceneId2); + } + + TEST_P(ARenderer, rendersTwoMappedAndShownScenesWithAscendingRenderOrder) + { + createDisplayController(); + + const SceneId sceneId1(12u); + createScene(sceneId1); + assignSceneToDisplayBuffer(sceneId1, 1); + showScene(sceneId1); + + const SceneId sceneId2(13u); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneId2, 2); + showScene(sceneId2); + + { + InSequence seq; + expectSceneRendered(sceneId1); + expectSceneRendered(sceneId2); + } + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + unassignScene(sceneId1); + unassignScene(sceneId2); + } + + TEST_P(ARenderer, rendersTwoMappedAndShownScenesWithDescendingRenderOrder) + { + createDisplayController(); + + const SceneId sceneId1(12u); + createScene(sceneId1); + assignSceneToDisplayBuffer(sceneId1, 2); + showScene(sceneId1); + + + const SceneId sceneId2(13u); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneId2, 1); + showScene(sceneId2); + + { + InSequence seq; + expectSceneRendered(sceneId2); + expectSceneRendered(sceneId1); + } + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + unassignScene(sceneId1); + unassignScene(sceneId2); + } + + TEST_P(ARenderer, doesNotRenderSceneThatWasNotMapped) + { + createDisplayController(); + createScene(); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, doesNotRenderUnmappedScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + unassignScene(sceneId); + + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + } + + TEST_P(ARenderer, skipsFrameIfDisplayControllerCanNotRenderNewFrame) + { + createDisplayController(); + + EXPECT_CALL(*renderer.m_displayController, handleWindowEvents()); + //mock that disp controller can not render new frame by returning false + EXPECT_CALL(*renderer.m_displayController, canRenderNewFrame()).WillRepeatedly(Return(false)); + + //renderer must not try to render on that display + EXPECT_CALL(*renderer.m_displayController, swapBuffers()).Times(0); + EXPECT_CALL(*renderer.m_displayController, getEmbeddedCompositingManager()).Times(0); + EXPECT_CALL(renderer.m_embeddedCompositingManager, notifyClients()).Times(0); + + renderer.doOneRenderLoop(); + } + + TEST_P(ARenderer, canTakeASingleScreenshot_Framebuffer) + { + createDisplayController(); + + scheduleScreenshot(DisplayControllerMock::FakeFrameBufferHandle, 20u, 30u, 100u, 100u); + + expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, 20u, 30u, 100u, 100u); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + auto screenshots = renderer.dispatchProcessedScreenshots(); + ASSERT_EQ(1u, screenshots.size()); + EXPECT_EQ(DisplayControllerMock::FakeFrameBufferHandle, screenshots.begin()->first); + + // check that screenshot request got deleted + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + screenshots = renderer.dispatchProcessedScreenshots(); + EXPECT_EQ(0u, screenshots.size()); + } + + TEST_P(ARenderer, canTakeASingleScreenshot_Offscreenbuffer) + { + createDisplayController(); + const DeviceResourceHandle obDeviceHandle{ 567u }; + renderer.registerOffscreenBuffer(obDeviceHandle, 10u, 20u, false); + + scheduleScreenshot(obDeviceHandle, 1u, 2u, 3u, 4u); + + expectDisplayControllerReadPixels(obDeviceHandle, 1u, 2u, 3u, 4u); + expectOffscreenBufferCleared(obDeviceHandle); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + auto screenshots = renderer.dispatchProcessedScreenshots(); + ASSERT_EQ(1u, screenshots.size()); + EXPECT_EQ(obDeviceHandle, screenshots.begin()->first); + + // check that screenshot request got deleted + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + screenshots = renderer.dispatchProcessedScreenshots(); + EXPECT_EQ(0u, screenshots.size()); + } + + TEST_P(ARenderer, canTakeASingleScreenshot_InterruptibleOffscreenbuffer) + { + createDisplayController(); + const DeviceResourceHandle obDeviceHandle{ 567u }; + renderer.registerOffscreenBuffer(obDeviceHandle, 10u, 20u, true); + + scheduleScreenshot(obDeviceHandle, 1u, 2u, 3u, 4u); + + expectDisplayControllerReadPixels(obDeviceHandle, 1u, 2u, 3u, 4u); + expectFrameBufferRendered(); + expectOffscreenBufferCleared(obDeviceHandle); + expectInterruptibleOffscreenBufferSwapped(obDeviceHandle); + expectSwapBuffers(); + doOneRendererLoop(); + + auto screenshots = renderer.dispatchProcessedScreenshots(); + ASSERT_EQ(1u, screenshots.size()); + EXPECT_EQ(obDeviceHandle, screenshots.begin()->first); + + // check that screenshot request got deleted + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + screenshots = renderer.dispatchProcessedScreenshots(); + EXPECT_EQ(0u, screenshots.size()); + } + + TEST_P(ARenderer, takeMultipleScreenshotsOfADisplayOverritesPreviousScreenshot) + { + createDisplayController(); + + scheduleScreenshot(DisplayControllerMock::FakeFrameBufferHandle, 10u, 10u, 110u, 110u); + scheduleScreenshot(DisplayControllerMock::FakeFrameBufferHandle, 30u, 30u, 130u, 130u); + + expectDisplayControllerReadPixels(DisplayControllerMock::FakeFrameBufferHandle, 30u, 30u, 130u, 130u); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + auto screenshots1 = renderer.dispatchProcessedScreenshots(); + ASSERT_EQ(1u, screenshots1.size()); + const auto& screenshots1FB = std::find_if(std::cbegin(screenshots1), std::cend(screenshots1), [&](const auto& p) {return p.first == DisplayControllerMock::FakeFrameBufferHandle; }); + ASSERT_NE(screenshots1.cend(), screenshots1FB); + + // check that screenshot request got deleted + EXPECT_CALL(*renderer.m_displayController, readPixels(_, _, _, _, _, _)).Times(0); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + screenshots1 = renderer.dispatchProcessedScreenshots(); + ASSERT_EQ(0u, screenshots1.size()); + } + + TEST_P(ARenderer, marksRenderOncePassesAsRenderedAfterRenderingScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + auto& scene = rendererScenes.getScene(sceneId); + TestSceneHelper sceneHelper(scene); + const auto dataLayout = sceneHelper.m_sceneAllocator.allocateDataLayout({ DataFieldInfo{ramses::internal::EDataType::Vector2I}, DataFieldInfo{ramses::internal::EDataType::Vector2I} }, ResourceContentHash::Invalid()); + const CameraHandle camera = sceneHelper.m_sceneAllocator.allocateCamera(ECameraProjectionType::Orthographic, sceneHelper.m_sceneAllocator.allocateNode(), sceneHelper.m_sceneAllocator.allocateDataInstance(dataLayout)); + const RenderPassHandle pass = sceneHelper.m_sceneAllocator.allocateRenderPass(); + scene.setRenderPassCamera(pass, camera); + + // render + scene.updateRenderablesAndResourceCache(sceneHelper.resourceManager); + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // pass in list means it was rendered + const auto& passesToRender = scene.getSortedRenderingPasses(); + ASSERT_EQ(1u, passesToRender.size()); + EXPECT_EQ(pass, passesToRender[0].getRenderPassHandle()); + + // set as render once pass + // now this is expected to be rendered once only + scene.setRenderPassRenderOnce(pass, true); + + // render + scene.updateRenderablesAndResourceCache(sceneHelper.resourceManager); + expectSceneRendered(sceneId); + renderer.markBufferWithSceneForRerender(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // pass in list means it was rendered + ASSERT_EQ(1u, passesToRender.size()); + EXPECT_EQ(pass, passesToRender[0].getRenderPassHandle()); + + // render + scene.updateRenderablesAndResourceCache(sceneHelper.resourceManager); + expectSceneRendered(sceneId); + renderer.markBufferWithSceneForRerender(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // pass not in list means it was not rendered anymore + EXPECT_TRUE(passesToRender.empty()); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, doesNotClearAndRerenderIfNoChangeToScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no further expectations for scene render or clear + expectFrameBufferRendered(false); + doOneRendererLoop(); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, doesNotClearAndRerenderOffscreenBufferIfNoChangeToScene) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // no further expectations for scene render or clear + expectFrameBufferRendered(false); + doOneRendererLoop(); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderIfSceneMarkedAsChanged) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + // mark change + renderer.markBufferWithSceneForRerender(sceneId); + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + // mark change + renderer.markBufferWithSceneForRerender(sceneId); + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderOffscreenBufferIfSceneMarkedAsChanged) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + // mark change + renderer.markBufferWithSceneForRerender(sceneId); + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(false); + doOneRendererLoop(); // framebuffer not cleared/swapped as there is no scene assigned + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + // mark change + renderer.markBufferWithSceneForRerender(sceneId); + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(false); + doOneRendererLoop(); // framebuffer not cleared/swapped as there is no scene assigned + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderBothFramebufferAndOffscreenBufferIfSceneAssignedFromOneToTheOther) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // assign back to FB causes clear/render of both buffers + renderer.assignSceneToDisplayBuffer(sceneId, DisplayControllerMock::FakeFrameBufferHandle, 0); + expectOffscreenBufferCleared(fakeOffscreenBuffer); + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneId, DisplayControllerMock::FakeFrameBufferHandle); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // assign back to offscreen buffer causes clear/render of both buffers + renderer.assignSceneToDisplayBuffer(sceneId, fakeOffscreenBuffer, 0); + expectSceneRendered(sceneId, fakeOffscreenBuffer, EDiscardDepth::Allowed); + expectFrameBufferRendered(true); // framebuffer cleared/swapped + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderBufferIfSceneHidden) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // hiding scene causes clear of FB but no render as scene is hidden + hideScene(sceneId); + expectFrameBufferRendered(true); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderBufferIfClearColorChanged) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // setting clear color causes clear/render + renderer.setClearColor(DisplayControllerMock::FakeFrameBufferHandle, { 1, 2, 3, 4 }); + expectSceneRendered(sceneId, DisplayControllerMock::FakeFrameBufferHandle, EClearFlag::All, { 1, 2, 3, 4 }); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderBothFramebufferAndOffscreenBufferIfOBClearColorChanges) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + + const glm::vec4 obClearColor1(.1f, .2f, .3f, .4f); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, false); + renderer.setClearColor(fakeOffscreenBuffer, obClearColor1); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectSceneRenderedExt(sceneId, fakeOffscreenBuffer, EClearFlag::All, obClearColor1, {}, {}, EClearFlag::All, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // change clear color + const glm::vec4 obClearColor2(.2f, .3f, .4f, .5f); + renderer.setClearColor(fakeOffscreenBuffer, obClearColor2); + expectFrameBufferRendered(true); + expectSceneRenderedExt(sceneId, fakeOffscreenBuffer, EClearFlag::All, obClearColor2, {}, {}, EClearFlag::All, EDiscardDepth::Allowed); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndRerenderBuffersIfExternallyOwnedWindowResized) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // external resizing causes clear/render + EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(1u, 2u)).WillOnce(Return(true)); + renderer.setExternallyOwnedWindowSize(1u, 2u); + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, doesNotClearAndRerenderBuffersIfExternallyOwnedWindowResizeFails) + { + createDisplayController(); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0); + showScene(sceneId); + + expectSceneRendered(sceneId); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + // failed external resizing causes clear/render + EXPECT_CALL(renderer.m_platform.renderBackendMock.windowMock, setExternallyOwnedWindowSize(1u, 2u)).WillOnce(Return(false)); + renderer.setExternallyOwnedWindowSize(1u, 2u); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneId); + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndSwapInterruptibleOBOnlyOnceIfNoMoreShownScenes) + { + createDisplayController(); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, true); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectFrameBufferRendered(); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + expectSceneRenderedWithInterruptionEnabled(sceneId, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin, EClearFlag::All); + expectSwapBuffers(); + doOneRendererLoop(); + + // re-render FB to reflect finished interruptible OB in previous frame + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // hide scene will trigger re-render and extra clear of OB + hideScene(sceneId); + expectFrameBufferRendered(false); + expectOffscreenBufferCleared(fakeOffscreenBuffer); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + // re-render FB to reflect finished interruptible OB in previous frame + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + unassignScene(sceneId); + } + + TEST_P(ARenderer, clearAndSwapInterruptibleOBOnlyOnceIfNoMoreMappedScenes) + { + createDisplayController(); + + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 2u, true); + + const SceneId sceneId(12u); + createScene(sceneId); + assignSceneToDisplayBuffer(sceneId, 0, fakeOffscreenBuffer); + showScene(sceneId); + + expectFrameBufferRendered(); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + expectSceneRenderedWithInterruptionEnabled(sceneId, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); + expectSwapBuffers(); + doOneRendererLoop(); + + // re-render FB to reflect finished interruptible OB in previous frame + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // hide scene will trigger re-render of OB + hideScene(sceneId); + unassignScene(sceneId); + expectFrameBufferRendered(false); + expectOffscreenBufferCleared(fakeOffscreenBuffer); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + // re-render FB to reflect finished interruptible OB in previous frame + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change + expectFrameBufferRendered(false); + doOneRendererLoop(); + expectFrameBufferRendered(false); + doOneRendererLoop(); + } + + TEST_P(ARenderer, rendersScenesToFBAndOBWithNoInterruption) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneIdOB(13u); + createScene(sceneIdFB); + createScene(sceneIdOB); + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + + showScene(sceneIdFB); + showScene(sceneIdOB); + + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneIdFB); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneIdOB); + hideScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, doesNotSwapOffscreenBufferIfRenderingIntoItInterrupted) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdOB(13u); + createScene(sceneIdOB); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + showScene(sceneIdOB); + + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted, EClearFlag::All); // expect clear + + expectFrameBufferRendered(); + // expect no OB swap due to interruption + expectSwapBuffers(); + doOneRendererLoop(); + + renderer.resetRenderInterruptState(); + hideScene(sceneIdOB); + unassignScene(sceneIdOB); + } + + TEST_P(ARenderer, doesNotClearAndPassesPreviousStateWhenInterruptedAndSwapsAfterFinishing) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdOB(13u); + createScene(sceneIdOB); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + showScene(sceneIdOB); + + // start rendering and interrupt + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted, EClearFlag::All); // expect clear + expectFrameBufferRendered(); + // expect no OB swap due to interruption + expectSwapBuffers(); + doOneRendererLoop(); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // continue from interruption point and interrupt again + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderInterrupted2, EClearFlag::None); // no clear due to previous interruption + expectFrameBufferRendered(false); + // no OB swap due to interruption again + doOneRendererLoop(); + EXPECT_TRUE(renderer.hasAnyBufferWithInterruptedRendering()); + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // continue from interruption point and finish + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted2, sceneRenderBegin, EClearFlag::None); // no clear due to previous interruption + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); // swap after finish + doOneRendererLoop(); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // re-render FB to reflect change happened to OB in previous frame + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + EXPECT_FALSE(renderer.hasAnyBufferWithInterruptedRendering()); + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneIdOB); + unassignScene(sceneIdOB); + } + + TEST_P(ARenderer, rendersScenesToFBEvenIfOBInterrupted) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneIdOB(13u); + createScene(sceneIdFB); + createScene(sceneIdOB); + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + + showScene(sceneIdFB); + showScene(sceneIdOB); + + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(false, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + // re-render FB to reflect change happened to OB in previous frame + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneIdOB); + hideScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, alwaysRendersScenesToFBWhenModifiedEvenIfOBInterrupted) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneIdOB(13u); + createScene(sceneIdFB); + createScene(sceneIdOB); + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + + showScene(sceneIdFB); + showScene(sceneIdOB); + + //FB rendered, OB interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + //modify FB scene + renderer.markBufferWithSceneForRerender(sceneIdFB); + //FB rendered, OB interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderInterrupted2, EClearFlag::None); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + //modify FB scene + renderer.markBufferWithSceneForRerender(sceneIdFB); + //FB rendered, OB finished + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted2, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + expectSwapBuffers(); + doOneRendererLoop(); + + // re-render FB to reflect change happened to OB in previous frame + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneIdOB); + hideScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, doesNotSkipFramesTillAllInterruptionsFinishedAndRendered) + { + // 1 FB, 1 OB, 1 scene per OB + + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneIdOB(13u); + createScene(sceneIdFB); + createScene(sceneIdOB); + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + + showScene(sceneIdFB); + showScene(sceneIdOB); + + // FB rendered, OB interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // FB skipped, OB finished + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + // re-render FB to reflect change happened to OB in previous frame + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneIdOB); + hideScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleScenes) + { + // 1 FB, 1 OB, 2 scenes per OB + + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneId1OB(13u); + const SceneId sceneId2OB(14u); + createScene(sceneIdFB); + createScene(sceneId1OB); + createScene(sceneId2OB); + assignSceneToDisplayBuffer(sceneId2OB, 1, fakeOffscreenBuffer); + assignSceneToDisplayBuffer(sceneId1OB, 0, fakeOffscreenBuffer); + assignSceneToDisplayBuffer(sceneIdFB, 0); + + showScene(sceneIdFB); + showScene(sceneId1OB); + showScene(sceneId2OB); + + // FB rendered, OB scene1 interrupted, OB scene2 skipped + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped, OB scene1 finished, OB scene2 interrupted + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted, EClearFlag::None); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped (as OB is not done rendering yet), OB scene1 skipped, OB scene2 finished + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // re-render FB to reflect change happened to OB scene 1 and scene 2 in previous frames + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId2OB); + hideScene(sceneId1OB); + hideScene(sceneIdFB); + unassignScene(sceneId2OB); + unassignScene(sceneId1OB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleScenes_FirstSceneNeverInterrupted) + { + // 1 FB, 1 OB, 2 scenes per OB + + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneId1OB(13u); + const SceneId sceneId2OB(14u); + createScene(sceneIdFB); + createScene(sceneId1OB); + createScene(sceneId2OB); + assignSceneToDisplayBuffer(sceneId2OB, 1, fakeOffscreenBuffer); + assignSceneToDisplayBuffer(sceneId1OB, 0, fakeOffscreenBuffer); + assignSceneToDisplayBuffer(sceneIdFB, 0); + + showScene(sceneIdFB); + showScene(sceneId1OB); + showScene(sceneId2OB); + + // FB rendered, OB scene1 fully rendered, OB scene2 interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped, OB scene1 skipped, OB scene2 finished + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // re-render FB to reflect change happened to OB scene 1 and scene 2 in previous frames + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId2OB); + hideScene(sceneId1OB); + hideScene(sceneIdFB); + unassignScene(sceneId2OB); + unassignScene(sceneId1OB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleOffscreenBuffers) + { + // 1 FB, 2 OB, 1 scene per OB + + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer1(313u); + const DeviceResourceHandle fakeOffscreenBuffer2(314u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 1u, true); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneIdOB1(13u); + const SceneId sceneIdOB2(14u); + createScene(sceneIdFB); + createScene(sceneIdOB1); + createScene(sceneIdOB2); + assignSceneToDisplayBuffer(sceneIdOB2, 0, fakeOffscreenBuffer2); + assignSceneToDisplayBuffer(sceneIdOB1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneIdFB, 0); + + showScene(sceneIdFB); + showScene(sceneIdOB1); + showScene(sceneIdOB2); + + // FB rendered, OB1 interrupted, OB2 skipped + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB1, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped, OB1 finished, OB2 interrupted + expectSceneRenderedWithInterruptionEnabled(sceneIdOB1, fakeOffscreenBuffer1, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB2, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB re-rendered to reflect changed in OB1, OB1 skipped, OB2 finished + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB2, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // re-render FB to reflect change happened to OB2 in previous frame + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneIdOB2); + hideScene(sceneIdOB1); + hideScene(sceneIdFB); + unassignScene(sceneIdOB2); + unassignScene(sceneIdOB1); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, interruptingEveryFrameGetsToAllRenderedState_withMultipleOffscreenBuffersAndScenes) + { + // 1 FB, 2 OB, 2 scene per OB + + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer1(313u); + const DeviceResourceHandle fakeOffscreenBuffer2(314u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 1u, true); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneId1OB1(13u); + const SceneId sceneId2OB1(14u); + const SceneId sceneId1OB2(15u); + const SceneId sceneId2OB2(16u); + createScene(sceneIdFB); + createScene(sceneId1OB1); + createScene(sceneId2OB1); + createScene(sceneId1OB2); + createScene(sceneId2OB2); + assignSceneToDisplayBuffer(sceneId1OB2, 0, fakeOffscreenBuffer2); + assignSceneToDisplayBuffer(sceneId2OB2, 1, fakeOffscreenBuffer2); + assignSceneToDisplayBuffer(sceneId1OB1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId2OB1, 1, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneIdFB, 0); + + showScene(sceneIdFB); + showScene(sceneId1OB1); + showScene(sceneId2OB1); + showScene(sceneId1OB2); + showScene(sceneId2OB2); + + // FB rendered, OB1 scene1 interrupted, rest skipped + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB1, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped, OB1 scene1 finished, OB1 scene2 interrupted, rest skipped + expectSceneRenderedWithInterruptionEnabled(sceneId1OB1, fakeOffscreenBuffer1, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB1, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderInterrupted, EClearFlag::None); + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped, OB1 scene1 skipped, OB1 scene2 finished, OB2 scene1 interrupted, rest skipped + expectSceneRenderedWithInterruptionEnabled(sceneId2OB1, fakeOffscreenBuffer1, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB2, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB re-rendered to reflect changes in OB1, OB1 skipped, OB2 scene1 finished, OB2 scene2 interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB2, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB2, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted, EClearFlag::None); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB skipped, OB1 skipped, OB2 scene2 finished + expectSceneRenderedWithInterruptionEnabled(sceneId2OB2, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB re-rendered to reflect changes in OB2, rest skipped + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId2OB2); + hideScene(sceneId1OB2); + hideScene(sceneId2OB1); + hideScene(sceneId1OB1); + hideScene(sceneIdFB); + unassignScene(sceneId2OB2); + unassignScene(sceneId1OB2); + unassignScene(sceneId2OB1); + unassignScene(sceneId1OB1); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, willRerenderSceneThatWasRenderedAndModifiedWhileOtherSceneInterrupted) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneId1OB(13u); + const SceneId sceneId2OB(14u); + createScene(sceneIdFB); + createScene(sceneId1OB); + createScene(sceneId2OB); + assignSceneToDisplayBuffer(sceneId2OB, 1, fakeOffscreenBuffer); + assignSceneToDisplayBuffer(sceneId1OB, 0, fakeOffscreenBuffer); + assignSceneToDisplayBuffer(sceneIdFB, 0); + + showScene(sceneIdFB); + showScene(sceneId1OB); + showScene(sceneId2OB); + + // FB rendered, OB scene1 fully rendered, OB scene2 interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // modify OB scene1 + renderer.markBufferWithSceneForRerender(sceneId1OB); + + // FB skipped, OB scene1 skipped, OB scene2 finished + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // re-render FB to reflect changes + // re-render OB scene1 (and OB scene2 as they share buffer) also as it was modified while interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderBegin); + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // re-render FB one more time to reflect changes + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId2OB); + hideScene(sceneId1OB); + hideScene(sceneIdFB); + unassignScene(sceneId2OB); + unassignScene(sceneId1OB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, willRerenderSceneThatWasRenderedAndModifiedWhileOtherSceneOnAnotherInterruptibleOBInterrupted) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer1(313u); + const DeviceResourceHandle fakeOffscreenBuffer2(314u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 1u, true); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneId1OB(13u); + const SceneId sceneId2OB(14u); + createScene(sceneIdFB); + createScene(sceneId1OB); + createScene(sceneId2OB); + assignSceneToDisplayBuffer(sceneId2OB, 0, fakeOffscreenBuffer2); + assignSceneToDisplayBuffer(sceneId1OB, 1, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneIdFB, 0); + + showScene(sceneIdFB); + showScene(sceneId1OB); + showScene(sceneId2OB); + + // FB rendered, OB1 scene fully rendered, OB2 scene interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer2, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // modify OB1 scene + renderer.markBufferWithSceneForRerender(sceneId1OB); + + // FB re-rendered to reflect finished OB1, OB1 scene skipped due to interruption, OB2 scene finished + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId2OB, fakeOffscreenBuffer2, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer2); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB re-rendered to reflect finished OB2, OB1 scene re-rendered due to modification, OB2 scene skipped + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneId1OB, fakeOffscreenBuffer1, sceneRenderBegin, sceneRenderBegin); + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer1); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB re-rendered once more to reflect changes from OB1 + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneId2OB); + hideScene(sceneId1OB); + hideScene(sceneIdFB); + unassignScene(sceneId2OB); + unassignScene(sceneId1OB); + unassignScene(sceneIdFB); + } + + TEST_P(ARenderer, willRenderAllScenesFromAllBuffersInOneFrameIfWithinBudget) + { + // 1 OB, 2 interruptible OBs, 2 scenes per OB + + createDisplayController(); + DeviceResourceHandle disp1OB(313u); + DeviceResourceHandle disp1OBint1(315u); + DeviceResourceHandle disp1OBint2(316u); + renderer.registerOffscreenBuffer(disp1OB, 1u, 1u, false); + renderer.registerOffscreenBuffer(disp1OBint1, 1u, 1u, true); + renderer.registerOffscreenBuffer(disp1OBint2, 1u, 1u, true); + + const SceneId sceneIdDisp1FB(12u); + const SceneId sceneIdDisp1OBscene(14u); + const SceneId sceneIdDisp1OBint1scene1(16u); + const SceneId sceneIdDisp1OBint1scene2(17u); + const SceneId sceneIdDisp1OBint2scene1(18u); + const SceneId sceneIdDisp1OBint2scene2(19u); + + createScene(sceneIdDisp1FB); + createScene(sceneIdDisp1OBscene); + createScene(sceneIdDisp1OBint1scene1); + createScene(sceneIdDisp1OBint1scene2); + createScene(sceneIdDisp1OBint2scene1); + createScene(sceneIdDisp1OBint2scene2); + + assignSceneToDisplayBuffer(sceneIdDisp1FB, 0); + assignSceneToDisplayBuffer(sceneIdDisp1OBscene, 0, disp1OB); + assignSceneToDisplayBuffer(sceneIdDisp1OBint2scene1, 0, disp1OBint2); + assignSceneToDisplayBuffer(sceneIdDisp1OBint2scene2, 1, disp1OBint2); + assignSceneToDisplayBuffer(sceneIdDisp1OBint1scene1, 0, disp1OBint1); + assignSceneToDisplayBuffer(sceneIdDisp1OBint1scene2, 1, disp1OBint1); + + showScene(sceneIdDisp1FB); + showScene(sceneIdDisp1OBscene); + showScene(sceneIdDisp1OBint1scene1); + showScene(sceneIdDisp1OBint1scene2); + showScene(sceneIdDisp1OBint2scene1); + showScene(sceneIdDisp1OBint2scene2); + + // all rendered + expectSceneRendered(sceneIdDisp1OBscene, disp1OB, EDiscardDepth::Allowed); + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneIdDisp1FB); + + expectInterruptibleOffscreenBufferSwapped(disp1OBint1); + expectInterruptibleOffscreenBufferSwapped(disp1OBint2); + expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint1scene1, disp1OBint1, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint1scene2, disp1OBint1, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint2scene1, disp1OBint2, sceneRenderBegin, sceneRenderBegin); + expectSceneRenderedWithInterruptionEnabled(sceneIdDisp1OBint2scene2, disp1OBint2, sceneRenderBegin, sceneRenderBegin); + + expectSwapBuffers(); + + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // FB has to be re-rendered because there were some interruptible OBs finished last frame, + // interruptible OBs are rendered at end of frame so the FBs have to be rendered again next frame + // in order to use the latest state of the OBs wherever they are used in FBs + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneIdDisp1FB); + expectSwapBuffers(); + + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + Mock::VerifyAndClearExpectations(renderer.m_displayController); + + hideScene(sceneIdDisp1FB); + hideScene(sceneIdDisp1OBscene); + hideScene(sceneIdDisp1OBint1scene1); + hideScene(sceneIdDisp1OBint1scene2); + hideScene(sceneIdDisp1OBint2scene1); + hideScene(sceneIdDisp1OBint2scene2); + + unassignScene(sceneIdDisp1FB); + unassignScene(sceneIdDisp1OBscene); + unassignScene(sceneIdDisp1OBint1scene1); + unassignScene(sceneIdDisp1OBint1scene2); + unassignScene(sceneIdDisp1OBint2scene1); + unassignScene(sceneIdDisp1OBint2scene2); + } + + TEST_P(ARenderer, canMapSceneWhileThereIsInterruption) + { + createDisplayController(); + const DeviceResourceHandle fakeOffscreenBuffer(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer, 1u, 1u, true); + + const SceneId sceneIdFB(12u); + const SceneId sceneIdOB(13u); + const SceneId sceneId2(14u); + createScene(sceneIdFB); + createScene(sceneIdOB); + createScene(sceneId2); + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, fakeOffscreenBuffer); + + showScene(sceneIdFB); + showScene(sceneIdOB); + + // FB rendered, OB interrupted + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderBegin, sceneRenderInterrupted); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // map other scene to FB while interrupted + assignSceneToDisplayBuffer(sceneId2, 0); + + // FB rendered because of new scene mapped, OB finished + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOB, fakeOffscreenBuffer, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(fakeOffscreenBuffer); + expectSwapBuffers(); + doOneRendererLoop(); + + // re-render FB to reflect change happened to OB in previous frame + expectSceneRendered(sceneIdFB); + expectFrameBufferRendered(true, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + // no change -> nothing rendered + expectFrameBufferRendered(false); + doOneRendererLoop(); + + hideScene(sceneIdOB); + hideScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdFB); + unassignScene(sceneId2); + } + + TEST_P(ARenderer, doesNotReportSceneIfNotRenderedToExpirationMonitor) + { + createDisplayController(); + const SceneId sceneIdFB(1u); + const SceneId sceneIdOB(2u); + const SceneId sceneIdOBint(3u); + createScene(sceneIdFB); + createScene(sceneIdOB); + createScene(sceneIdOBint); + + initiateExpirationMonitoring({ sceneIdOB, sceneIdFB, sceneIdOBint }); + + DeviceResourceHandle ob(316u); + DeviceResourceHandle obInt(317u); + renderer.registerOffscreenBuffer(ob, 1u, 1u, false); + renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); + + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, ob); + assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); + + expectOffscreenBufferCleared(ob); + expectFrameBufferRendered(); + expectOffscreenBufferCleared(obInt); + expectInterruptibleOffscreenBufferSwapped(obInt); + expectSwapBuffers(); + doOneRendererLoop(); + + expectScenesReportedToExpirationMonitorAsRendered({}); + + unassignScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdOBint); + expirationMonitor.onDestroyed(sceneIdFB); + expirationMonitor.onDestroyed(sceneIdOB); + expirationMonitor.onDestroyed(sceneIdOBint); + } + + TEST_P(ARenderer, reportsSceneAsRenderedToExpirationMonitor) + { + createDisplayController(); + const SceneId sceneIdFB(1u); + const SceneId sceneIdOB(2u); + const SceneId sceneIdOBint(3u); + createScene(sceneIdFB); + createScene(sceneIdOB); + createScene(sceneIdOBint); + + initiateExpirationMonitoring({ sceneIdOB, sceneIdFB, sceneIdOBint }); + + DeviceResourceHandle ob(316u); + DeviceResourceHandle obInt(317u); + renderer.registerOffscreenBuffer(ob, 1u, 1u, false); + renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); + + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOB, 0, ob); + assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); + + showScene(sceneIdFB); + showScene(sceneIdOB); + showScene(sceneIdOBint); + + expectFrameBufferRendered(true, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(obInt); + expectSceneRendered(sceneIdOB, ob, EDiscardDepth::Allowed); + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOBint, obInt, sceneRenderBegin, sceneRenderBegin); + expectSwapBuffers(); + doOneRendererLoop(); + + expectScenesReportedToExpirationMonitorAsRendered({ sceneIdOB, sceneIdFB, sceneIdOBint }); + + hideScene(sceneIdFB); + hideScene(sceneIdOB); + hideScene(sceneIdOBint); + unassignScene(sceneIdFB); + unassignScene(sceneIdOB); + unassignScene(sceneIdOBint); + expirationMonitor.onDestroyed(sceneIdFB); + expirationMonitor.onDestroyed(sceneIdOB); + expirationMonitor.onDestroyed(sceneIdOBint); + } + + TEST_P(ARenderer, reportsSceneAsRenderedToExpirationMonitorOnlyAfterFullyRenderedAndNotDuringInterruption) + { + createDisplayController(); + const SceneId sceneIdFB(1u); + const SceneId sceneIdOBint(3u); + createScene(sceneIdFB); + createScene(sceneIdOBint); + + initiateExpirationMonitoring({ sceneIdFB, sceneIdOBint }); + + DeviceResourceHandle obInt(317u); + renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); + + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); + + showScene(sceneIdFB); + showScene(sceneIdOBint); + + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneIdFB); + expectSceneRenderedWithInterruptionEnabled(sceneIdOBint, obInt, sceneRenderBegin, sceneRenderInterrupted); + expectSwapBuffers(); + doOneRendererLoop(); + + // only FB scene reported as rendered, OB scene is interrupted + expectScenesReportedToExpirationMonitorAsRendered({ sceneIdFB }); + + expectFrameBufferRendered(false); + expectInterruptibleOffscreenBufferSwapped(obInt); + expectSceneRenderedWithInterruptionEnabled(sceneIdOBint, obInt, sceneRenderInterrupted, sceneRenderBegin, EClearFlag::None); + doOneRendererLoop(); + + // OB scene is reported now as it was fully rendered + expectScenesReportedToExpirationMonitorAsRendered({ sceneIdOBint }); + + hideScene(sceneIdFB); + hideScene(sceneIdOBint); + unassignScene(sceneIdFB); + unassignScene(sceneIdOBint); + expirationMonitor.onDestroyed(sceneIdFB); + expirationMonitor.onDestroyed(sceneIdOBint); + } + + TEST_P(ARenderer, pendingClearFlagPersistsAcrossScenes_FB) + { + createDisplayController(); + constexpr SceneId scene1{ 1u }; + constexpr SceneId scene2{ 3u }; + createScene(scene1); + createScene(scene2); + + assignSceneToDisplayBuffer(scene1, 0); + assignSceneToDisplayBuffer(scene2, 0); + + showScene(scene1); + showScene(scene2); + + expectFrameBufferRendered(true, EClearFlag::None); + // simulate that the render executor cleared and reset pending clear in context + expectSceneRenderedExt(scene1, DisplayControllerMock::FakeFrameBufferHandle, EClearFlag::All, Renderer::DefaultClearColor, {}, {}, EClearFlag::None, EDiscardDepth::Disallowed); + // next scene render will pass the modified flags + expectSceneRendered(scene2, DisplayControllerMock::FakeFrameBufferHandle, EClearFlag::None); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(scene1); + hideScene(scene2); + unassignScene(scene1); + unassignScene(scene2); + } + + TEST_P(ARenderer, pendingClearFlagPersistsAcrossScenes_OB) + { + createDisplayController(); + constexpr SceneId scene1{ 1u }; + constexpr SceneId scene2{ 3u }; + createScene(scene1); + createScene(scene2); + + constexpr DeviceResourceHandle ob{ 317u }; + renderer.registerOffscreenBuffer(ob, 1u, 1u, false); + + assignSceneToDisplayBuffer(scene1, 0, ob); + assignSceneToDisplayBuffer(scene2, 0, ob); + + showScene(scene1); + showScene(scene2); + + expectFrameBufferRendered(); + // simulate that the render executor cleared and reset pending clear in context + expectSceneRenderedExt(scene1, ob, EClearFlag::All, Renderer::DefaultClearColor, {}, {}, EClearFlag::None, EDiscardDepth::Disallowed); + // next scene render will pass the modified flags + expectSceneRenderedExt(scene2, ob, EClearFlag::None, Renderer::DefaultClearColor, {}, {}, EClearFlag::None, EDiscardDepth::Allowed); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(scene1); + hideScene(scene2); + unassignScene(scene1); + unassignScene(scene2); + } + + TEST_P(ARenderer, pendingClearFlagPersistsAcrossScenes_OBinterruptible) + { + createDisplayController(); + constexpr SceneId scene1{ 1u }; + constexpr SceneId scene2{ 3u }; + createScene(scene1); + createScene(scene2); + + constexpr DeviceResourceHandle ob{ 317u }; + renderer.registerOffscreenBuffer(ob, 1u, 1u, true); + + assignSceneToDisplayBuffer(scene1, 0, ob); + assignSceneToDisplayBuffer(scene2, 0, ob); + + showScene(scene1); + showScene(scene2); + + expectFrameBufferRendered(); + // simulate that the render executor cleared and reset pending clear in context + expectSceneRenderedExt(scene1, ob, EClearFlag::All, Renderer::DefaultClearColor, {}, {}, EClearFlag::None, EDiscardDepth::Disallowed, &RendererMock::FrameTimerInstance); + // next scene render will pass the modified flags + expectSceneRenderedWithInterruptionEnabled(scene2, ob, {}, {}, EClearFlag::None); + expectInterruptibleOffscreenBufferSwapped(ob); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(scene1); + hideScene(scene2); + unassignScene(scene1); + unassignScene(scene2); + } + + TEST_P(ARenderer, allowDepthDiscardOnlyForLastRenderedSceneToOffscreenbuffer) + { + createDisplayController(); + + const DeviceResourceHandle fakeOffscreenBuffer1(313u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); + + const SceneId sceneId1(12u); + const SceneId sceneId2(13u); + const SceneId sceneId3(14u); + createScene(sceneId1); + createScene(sceneId2); + createScene(sceneId3); + assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId2, 1, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId3, 2, fakeOffscreenBuffer1); + showScene(sceneId1); + showScene(sceneId2); + // sceneId3 not shown + + expectSceneRendered(sceneId1, fakeOffscreenBuffer1, EDiscardDepth::Disallowed); + expectSceneRendered(sceneId2, fakeOffscreenBuffer1, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + unassignScene(sceneId1); + unassignScene(sceneId2); + unassignScene(sceneId3); + } + + TEST_P(ARenderer, allowDepthDiscardOnlyIfBothDepthAndStencilClearEnabled) + { + createDisplayController(); + + const DeviceResourceHandle fakeOffscreenBuffer1(313u); + const DeviceResourceHandle fakeOffscreenBuffer2(314u); + const DeviceResourceHandle fakeOffscreenBuffer3(315u); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer1, 1u, 2u, false); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer2, 1u, 2u, false); + renderer.registerOffscreenBuffer(fakeOffscreenBuffer3, 1u, 2u, false); + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer1, EClearFlag::Color | EClearFlag::Stencil)); + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer2, EClearFlag::Color | EClearFlag::Depth)); + EXPECT_CALL(renderer, setClearFlags(fakeOffscreenBuffer3, EClearFlag::Depth | EClearFlag::Stencil)); + renderer.setClearFlags(fakeOffscreenBuffer1, EClearFlag::Color | EClearFlag::Stencil); + renderer.setClearFlags(fakeOffscreenBuffer2, EClearFlag::Color | EClearFlag::Depth); + renderer.setClearFlags(fakeOffscreenBuffer3, EClearFlag::Depth | EClearFlag::Stencil); + + const SceneId sceneId1(12u); + const SceneId sceneId2(13u); + const SceneId sceneId3(14u); + createScene(sceneId1); + createScene(sceneId2); + createScene(sceneId3); + assignSceneToDisplayBuffer(sceneId1, 0, fakeOffscreenBuffer1); + assignSceneToDisplayBuffer(sceneId2, 0, fakeOffscreenBuffer2); + assignSceneToDisplayBuffer(sceneId3, 0, fakeOffscreenBuffer3); + showScene(sceneId1); + showScene(sceneId2); + showScene(sceneId3); + + expectSceneRenderedExt(sceneId1, fakeOffscreenBuffer1, EClearFlag::Color | EClearFlag::Stencil, Renderer::DefaultClearColor, {}, {}, {}, EDiscardDepth::Disallowed); + expectSceneRenderedExt(sceneId2, fakeOffscreenBuffer2, EClearFlag::Color | EClearFlag::Depth, Renderer::DefaultClearColor, {}, {}, {}, EDiscardDepth::Disallowed); + expectSceneRenderedExt(sceneId3, fakeOffscreenBuffer3, EClearFlag::Depth | EClearFlag::Stencil, Renderer::DefaultClearColor, {}, {}, {}, EDiscardDepth::Allowed); + expectFrameBufferRendered(); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneId1); + hideScene(sceneId2); + hideScene(sceneId3); + unassignScene(sceneId1); + unassignScene(sceneId2); + unassignScene(sceneId3); + } + + TEST_P(ARenderer, neverAllowsDepthDiscardForFBOrInterruptibleOB) + { + createDisplayController(); + const SceneId sceneIdFB(1u); + const SceneId sceneIdOBint(3u); + createScene(sceneIdFB); + createScene(sceneIdOBint); + + DeviceResourceHandle obInt(317u); + renderer.registerOffscreenBuffer(obInt, 1u, 1u, true); + + assignSceneToDisplayBuffer(sceneIdFB, 0); + assignSceneToDisplayBuffer(sceneIdOBint, 0, obInt); + + showScene(sceneIdFB); + showScene(sceneIdOBint); + + expectFrameBufferRendered(true, EClearFlag::None); + expectSceneRendered(sceneIdFB, DisplayControllerMock::FakeFrameBufferHandle, EDiscardDepth::Disallowed); + expectSceneRenderedExt(sceneIdOBint, obInt, EClearFlag::All, Renderer::DefaultClearColor, {}, {}, {}, EDiscardDepth::Disallowed, &RendererMock::FrameTimerInstance); + expectInterruptibleOffscreenBufferSwapped(obInt); + expectSwapBuffers(); + doOneRendererLoop(); + + hideScene(sceneIdFB); + hideScene(sceneIdOBint); + unassignScene(sceneIdFB); + unassignScene(sceneIdOBint); + } +} diff --git a/renderer/RendererLib/RendererLib/test/ResourceCachedSceneTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceCachedSceneTest.cpp similarity index 98% rename from renderer/RendererLib/RendererLib/test/ResourceCachedSceneTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/ResourceCachedSceneTest.cpp index 179c6ba13..45a510541 100644 --- a/renderer/RendererLib/RendererLib/test/ResourceCachedSceneTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceCachedSceneTest.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "TestSceneHelper.h" -#include "RendererLib/ResourceCachedScene.h" -#include "RendererLib/RendererResourceManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/ResourceCachedScene.h" +#include "internal/RendererLib/RendererResourceManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" #include "SceneAllocateHelper.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #include -namespace ramses_internal +namespace ramses::internal { class AResourceCachedScene : public ::testing::Test { @@ -30,7 +30,7 @@ namespace ramses_internal ThreadLocalLog::SetPrefix(1); sceneAllocator.allocateDataBuffer(EDataBufferType::VertexBuffer, EDataType::Vector3Buffer, sizeof(float) * 3, verticesDataBuffer); - sceneAllocator.allocateTextureBuffer(ETextureFormat::R8, { { 1u, 1u } }, textureBuffer); + sceneAllocator.allocateTextureBuffer(EPixelStorageFormat::R8, { { 1u, 1u } }, textureBuffer); ON_CALL(sceneHelper.resourceManager, getResourceDeviceHandle(resourceNotUploadedToDevice)).WillByDefault(Return(DeviceResourceHandle::Invalid())); ON_CALL(sceneHelper.resourceManager, getDataBufferDeviceHandle(verticesDataBuffer, _)).WillByDefault(Return(vertexDataBufferDeviceHandle)); @@ -134,7 +134,7 @@ namespace ramses_internal sceneHelper.createAndAssignVertexDataInstance(renderable); sceneHelper.setResourcesToRenderable(renderable); - sceneAllocator.allocateDataSlot({ EDataSlotType_TextureConsumer, DataSlotId{213u}, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), samplerHandle }); + sceneAllocator.allocateDataSlot({ EDataSlotType::TextureConsumer, DataSlotId{213u}, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), samplerHandle }); scene.setTextureSamplerContentSource(samplerHandle, streamBuffer); return samplerHandle; @@ -822,7 +822,7 @@ namespace ramses_internal scene.markVertexArraysClean(); EXPECT_FALSE(scene.isRenderableVertexArrayDirty(renderable)); - scene.updateDataBuffer(verticesDataBuffer, sizeof(float) * 3, 0u, reinterpret_cast(std::array().data())); + scene.updateDataBuffer(verticesDataBuffer, sizeof(float) * 3, 0u, reinterpret_cast(std::array().data())); EXPECT_FALSE(scene.renderableResourcesDirty(renderable)); EXPECT_FALSE(scene.isRenderableVertexArrayDirty(renderable)); @@ -1003,7 +1003,7 @@ namespace ramses_internal updateRenderableResourcesAndVertexArray({ renderable }); EXPECT_FALSE(scene.renderableResourcesDirty(renderable)); - scene.updateTextureBuffer(textureBuffer, 0u, 0u, 0u, 1u, 1u, std::array().data()); + scene.updateTextureBuffer(textureBuffer, 0u, 0u, 0u, 1u, 1u, std::array().data()); updateRenderableResources(); EXPECT_FALSE(scene.renderableResourcesDirty(renderable)); diff --git a/renderer/RendererLib/RendererTestCommon/ResourceUploaderMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderMock.cpp similarity index 91% rename from renderer/RendererLib/RendererTestCommon/ResourceUploaderMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderMock.cpp index c76960905..c805435d4 100644 --- a/renderer/RendererLib/RendererTestCommon/ResourceUploaderMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderMock.cpp @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "RendererLib/IResourceUploader.h" #include "ResourceUploaderMock.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { const DeviceResourceHandle ResourceUploaderMock::FakeResourceDeviceHandle(996699u); diff --git a/renderer/RendererLib/RendererTestCommon/ResourceUploaderMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderMock.h similarity index 80% rename from renderer/RendererLib/RendererTestCommon/ResourceUploaderMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderMock.h index c9604466d..ac23a0059 100644 --- a/renderer/RendererLib/RendererTestCommon/ResourceUploaderMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderMock.h @@ -6,16 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEUPLOADERMOCK_H -#define RAMSES_RESOURCEUPLOADERMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/IResourceUploader.h" #include "gmock/gmock.h" -#include "RendererLib/IResourceUploader.h" -#include "RendererAPI/IRenderBackend.h" -#include "RendererLib/ResourceDescriptor.h" -namespace ramses_internal +namespace ramses::internal { class ResourceUploaderMock : public IResourceUploader { @@ -29,5 +25,3 @@ namespace ramses_internal static const DeviceResourceHandle FakeResourceDeviceHandle; }; } - -#endif diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderTest.cpp new file mode 100644 index 000000000..989931ba8 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploaderTest.cpp @@ -0,0 +1,580 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2012 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/ResourceUploader.h" +#include "internal/RendererLib/PlatformInterface/IBinaryShaderCache.h" +#include "RenderBackendMock.h" +#include "ResourceMock.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/RendererLib/ResourceDescriptor.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" +#include "internal/Core/Utils/ThreadBarrier.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include + +namespace ramses::internal +{ + class BinaryShaderProviderMock : public IBinaryShaderCache + { + public: + MOCK_METHOD(void, deviceSupportsBinaryShaderFormats, (const std::vector&), (override)); + MOCK_METHOD(bool, hasBinaryShader, (ResourceContentHash), (const, override)); + MOCK_METHOD(uint32_t, getBinaryShaderSize, (ResourceContentHash), (const, override)); + MOCK_METHOD(BinaryShaderFormatID, getBinaryShaderFormat, (ResourceContentHash), (const, override)); + MOCK_METHOD(bool, shouldBinaryShaderBeCached, (ResourceContentHash, SceneId), (const, override)); + MOCK_METHOD(void, getBinaryShaderData, (ResourceContentHash, std::byte*, uint32_t), (const, override)); + MOCK_METHOD(void, storeBinaryShader, (ResourceContentHash, SceneId, const std::byte*, uint32_t, BinaryShaderFormatID), (override)); + MOCK_METHOD(void, binaryShaderUploaded, (ResourceContentHash, bool), (const, override)); + MOCK_METHOD(std::once_flag&, binaryShaderFormatsReported, (), (override)); + }; + + class BinaryShaderProviderFake final : public StrictMock + { + public: + BinaryShaderProviderFake() + { + ON_CALL(*this, shouldBinaryShaderBeCached(_,_)).WillByDefault(Return(true)); + ON_CALL(*this, binaryShaderFormatsReported()).WillByDefault(ReturnRef(m_binaryShaderFormatReported)); + } + + [[nodiscard]] bool hasBinaryShader(ResourceContentHash effectHash) const override + { + BinaryShaderProviderMock::hasBinaryShader(effectHash); + if (effectHash == m_effectHash) + { + return true; + } + + return false; + } + + [[nodiscard]] uint32_t getBinaryShaderSize(ResourceContentHash effectHash) const override + { + BinaryShaderProviderMock::getBinaryShaderSize(effectHash); + if (effectHash == m_effectHash) + { + return m_binaryShaderDataSize; + } + + return 0; + } + + [[nodiscard]] BinaryShaderFormatID getBinaryShaderFormat(ResourceContentHash effectHash) const override + { + BinaryShaderProviderMock::getBinaryShaderFormat(effectHash); + return (effectHash == m_effectHash ? m_binaryShaderFormat : BinaryShaderFormatID::Invalid()); + } + + ResourceContentHash m_effectHash; + const uint8_t* m_binaryShaderData{nullptr}; + uint32_t m_binaryShaderDataSize{0u}; + BinaryShaderFormatID m_binaryShaderFormat; + std::once_flag m_binaryShaderFormatReported; + }; + + class AResourceUploader : public ::testing::Test + { + public: + AResourceUploader() + : uploader(true) + , dummyManagedResourceCallback(managedResourceDeleter) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + } + + StrictMock renderer; + ResourceUploader uploader; + + StrictMock managedResourceDeleter; + ResourceDeleterCallingCallback dummyManagedResourceCallback; + uint32_t vramSize = 0; + }; + + TEST_F(AResourceUploader, uploadsVertexArrayResource) + { + const ArrayResource res(EResourceType::VertexArray, 1, EDataType::Float, nullptr, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateVertexBuffer(res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadVertexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(res.getDecompressedDataSize(), vramSize); + } + + TEST_F(AResourceUploader, uploadsIndexArrayResource16) + { + const ArrayResource res(EResourceType::IndexArray, 1, EDataType::UInt16, nullptr, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateIndexBuffer(res.getElementType(), res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadIndexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(res.getDecompressedDataSize(), vramSize); + } + + TEST_F(AResourceUploader, uploadsTexture2DResource) + { + const uint32_t mipCount = 2u; + const TextureMetaInfo texDesc(2u, 2u, 1u, EPixelStorageFormat::R8, false, DefaultTextureSwizzleArray, { 4, 1 }); + TextureResource res(EResourceType::Texture2D, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateTexture2D(2u, 2u, EPixelStorageFormat::R8, DefaultTextureSwizzleArray, mipCount, 5u)).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 2u, 2u, 1u, _, _, 0u)); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 1u, 0u, 0u, 0u, 1u, 1u, 1u, _, _, 0u)); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(2u * 2 + 1, vramSize); + } + + TEST_F(AResourceUploader, uploadsTexture2DResourceWithNonDefaultTextureSwizzleArray) + { + const uint32_t mipCount = 1u; + const TextureSwizzleArray swizzle {ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Green, ETextureChannelColor::Red}; + const TextureMetaInfo texDesc(2u, 2u, 1u, EPixelStorageFormat::R8, false, swizzle, { 4 }); + TextureResource res(EResourceType::Texture2D, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateTexture2D(2u, 2u, EPixelStorageFormat::R8, swizzle, mipCount, 4u)).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 2u, 2u, 1u, _, _, 0u)); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(2u * 2, vramSize); + } + + TEST_F(AResourceUploader, uploadsTexture2DResourceWithMipGen) + { + const TextureMetaInfo texDesc(4u, 1u, 1u, EPixelStorageFormat::R8, true, DefaultTextureSwizzleArray, { 4 }); + TextureResource res(EResourceType::Texture2D, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateTexture2D(4u, 1u, EPixelStorageFormat::R8, DefaultTextureSwizzleArray, 3u, 7u)).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 4u, 1u, 1u, _, _, 0u)); + EXPECT_CALL(renderer.deviceMock, generateMipmaps(DeviceResourceHandle(123))); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(4u + 2 + 1, vramSize); + } + + TEST_F(AResourceUploader, uploadsTexture3DResource) + { + const uint32_t mipCount = 2u; + const TextureMetaInfo texDesc(2u, 2u, 2u, EPixelStorageFormat::R8, false, DefaultTextureSwizzleArray, { 8, 1 }); + TextureResource res(EResourceType::Texture3D, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateTexture3D(2u, 2u, 2u, EPixelStorageFormat::R8, mipCount, 9u)).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 2u, 2u, 2u, _, _, 0u)); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 1u, 0u, 0u, 0u, 1u, 1u, 1u, _, _, 0u)); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(2u * 2 * 2 + 1, vramSize); + } + + TEST_F(AResourceUploader, uploadsTexture3DResourceWithMipGen) + { + const TextureMetaInfo texDesc(4u, 1u, 2u, EPixelStorageFormat::R8, true, DefaultTextureSwizzleArray, { 8 }); + TextureResource res(EResourceType::Texture3D, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateTexture3D(4u, 1u, 2u, EPixelStorageFormat::R8, 3u, 11u)).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, 0u, 4u, 1u, 2u, _, _, 0u)); + EXPECT_CALL(renderer.deviceMock, generateMipmaps(DeviceResourceHandle(123))); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(4u * 1 * 2 + 2 * 1 * 1 + 1, vramSize); + } + + TEST_F(AResourceUploader, uploadsTextureCubeResource) + { + const uint32_t mipCount = 2u; + const TextureMetaInfo texDesc(2u, 1u, 1u, EPixelStorageFormat::R8, false, DefaultTextureSwizzleArray, { 4, 1 }); + TextureResource res(EResourceType::TextureCube, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + InSequence seq; + EXPECT_CALL(renderer.deviceMock, allocateTextureCube(2u, EPixelStorageFormat::R8, DefaultTextureSwizzleArray, mipCount, 6 * 5)).WillOnce(Return(DeviceResourceHandle(123))); + for (uint32_t i = 0u; i < 6u; ++i) + { + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, i, 2u, 2u, 1u, _, _, 0u)); + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 1u, 0u, 0u, i, 1u, 1u, 1u, _, _, 0u)); + } + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(6u * (2 * 2 + 1), vramSize); + } + + TEST_F(AResourceUploader, uploadsTextureCubeResourceWithMipGen) + { + const TextureMetaInfo texDesc(4u, 1u, 1u, EPixelStorageFormat::R8, true, DefaultTextureSwizzleArray, { 16 }); + TextureResource res(EResourceType::TextureCube, texDesc, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + InSequence seq; + EXPECT_CALL(renderer.deviceMock, allocateTextureCube(4u, EPixelStorageFormat::R8, DefaultTextureSwizzleArray, 3u, 6 * (16 + 4 + 1))).WillOnce(Return(DeviceResourceHandle(123))); + for (uint32_t i = 0u; i < 6u; ++i) + { + EXPECT_CALL(renderer.deviceMock, uploadTextureData(DeviceResourceHandle(123), 0u, 0u, 0u, i, 4u, 4u, 1u, _, _, 0u)); + } + EXPECT_CALL(renderer.deviceMock, generateMipmaps(DeviceResourceHandle(123))); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + EXPECT_EQ(6u * (4 * 4 + 2 * 2 + 1), vramSize); + } + + TEST_F(AResourceUploader, canStoreBinaryShader) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + const SceneId sceneUsingResource(14); + + BinaryShaderProviderFake binaryShaderProvider; + EXPECT_CALL(binaryShaderProvider, shouldBinaryShaderBeCached(res.getHash(), _)).WillOnce(Return(true)); + EXPECT_CALL(renderer.deviceMock, getBinaryShader(DeviceMock::FakeShaderDeviceHandle, _, _)).WillOnce(DoAll(SetArgReferee<1>(std::vector(10)), Return(true))); + EXPECT_CALL(binaryShaderProvider, storeBinaryShader(_, sceneUsingResource, _, _, _)); + + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + uploaderWithBinaryProvider.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); + } + + TEST_F(AResourceUploader, doesNoStoreBinaryShaderIfFailedToGetBinaryShaderFromDevice) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + const SceneId sceneUsingResource(14); + + BinaryShaderProviderFake binaryShaderProvider; + + EXPECT_CALL(binaryShaderProvider, shouldBinaryShaderBeCached(res.getHash(), _)).WillOnce(Return(true)); + EXPECT_CALL(renderer.deviceMock, getBinaryShader(DeviceMock::FakeShaderDeviceHandle, _, _)).WillOnce(Return(false)); + EXPECT_CALL(binaryShaderProvider, storeBinaryShader(_, sceneUsingResource, _, _, _)).Times(0); + + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + uploaderWithBinaryProvider.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); + } + + TEST_F(AResourceUploader, ifShaderShouldNotBeCachedNoDownloadWillHappen) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + const SceneId sceneUsingResource(14); + + BinaryShaderProviderFake binaryShaderProvider; + EXPECT_CALL(binaryShaderProvider, shouldBinaryShaderBeCached(res.getHash(), _)).WillOnce(Return(false)); + EXPECT_CALL(renderer.deviceMock, getBinaryShader(DeviceMock::FakeShaderDeviceHandle, _, _)).Times(0); + EXPECT_CALL(binaryShaderProvider, storeBinaryShader(_, sceneUsingResource, _, _, _)).Times(0); + + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + uploaderWithBinaryProvider.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); + } + + TEST_F(AResourceUploader, doesNotTryToStoreBinaryShaderIfNoBinaryShaderCacheAvailable) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + const SceneId sceneUsingResource(14); + + EXPECT_CALL(renderer.deviceMock, getBinaryShader(_, _, _)).Times(0); + uploader.storeShaderInBinaryShaderCache(renderer, DeviceMock::FakeShaderDeviceHandle, res.getHash(), sceneUsingResource); + } + + TEST_F(AResourceUploader, doesNotReturnDeviceHandleForEffectResourceWithoutBinaryShaderCache) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); + EXPECT_FALSE(uploader.uploadResource(renderer, resourceObject, vramSize).has_value()); + } + + TEST_F(AResourceUploader, uploadsShaderWithoutBinaryShaderCacheIfAsyncUploadDisabled) + { + ResourceUploader uploaderWithoutCacheOrAsyncUpload(false); + + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); + EXPECT_CALL(renderer.deviceMock, uploadShader(_)); + EXPECT_CALL(renderer.deviceMock, registerShader(_)); + EXPECT_EQ(DeviceMock::FakeShaderDeviceHandle, uploaderWithoutCacheOrAsyncUpload.uploadResource(renderer, resourceObject, vramSize)); + } + + TEST_F(AResourceUploader, doesNotReturnDeviceHandleIfShaderIsNotCached) + { + BinaryShaderProviderFake binaryShaderProvider; + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); + + EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); + EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); + EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())).WillOnce(Return(false)); + + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject, vramSize).has_value()); + } + + TEST_F(AResourceUploader, uploadShaderIfShaderIsNotCachedAndAsyncUploadDisabled) + { + BinaryShaderProviderFake binaryShaderProvider; + ResourceUploader uploaderWithCacheButNoAsyncUpload(false, &binaryShaderProvider); + + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); + + EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); + EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); + EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())).WillOnce(Return(false)); + + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(renderer.deviceMock, uploadShader(_)); + EXPECT_CALL(renderer.deviceMock, registerShader(_)); + EXPECT_EQ(DeviceMock::FakeShaderDeviceHandle, uploaderWithCacheButNoAsyncUpload.uploadResource(renderer, resourceObject, vramSize)); + } + + TEST_F(AResourceUploader, uploadsEffectResourceFromBinaryShaderCacheWhenCacheHit) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + + uint8_t binaryShaderData[] = { 1u, 2u, 3u, 4u }; + + BinaryShaderProviderFake binaryShaderProvider; + binaryShaderProvider.m_effectHash = res.getHash(); + binaryShaderProvider.m_binaryShaderData = binaryShaderData; + binaryShaderProvider.m_binaryShaderDataSize = sizeof(binaryShaderData) / sizeof(uint8_t); + binaryShaderProvider.m_binaryShaderFormat = BinaryShaderFormatID{ 12u }; + + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); + + EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); + EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); + EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())); + EXPECT_CALL(binaryShaderProvider, getBinaryShaderSize(res.getHash())); + EXPECT_CALL(binaryShaderProvider, getBinaryShaderFormat(res.getHash())); + EXPECT_CALL(binaryShaderProvider, getBinaryShaderData(res.getHash(), _, _)); + EXPECT_CALL(binaryShaderProvider, binaryShaderUploaded(_, true)).Times(1); + + EXPECT_CALL(renderer.deviceMock, uploadBinaryShader(_, _, _, _)).WillOnce(Return(DeviceResourceHandle(123))); + + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_EQ(123u, uploaderWithBinaryProvider.uploadResource(renderer, resourceObject, vramSize)); + } + + TEST_F(AResourceUploader, doesNotReturnDeviceHandleForEffectResourceWithBrokenBinaryShaderCache) + { + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + SceneId sceneUsingResource(14); + + uint8_t binaryShaderData[] = { 1u, 2u, 3u, 4u }; + + BinaryShaderProviderFake binaryShaderProvider; + binaryShaderProvider.m_effectHash = res.getHash(); + binaryShaderProvider.m_binaryShaderData = binaryShaderData; + binaryShaderProvider.m_binaryShaderDataSize = sizeof(binaryShaderData) / sizeof(uint8_t); + binaryShaderProvider.m_binaryShaderFormat = BinaryShaderFormatID{ 12u }; + + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(1); + + // Set up so it looks like there is a valid binary shader cache + EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()); + EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })); + EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())); + EXPECT_CALL(binaryShaderProvider, getBinaryShaderSize(res.getHash())); + EXPECT_CALL(binaryShaderProvider, getBinaryShaderFormat(res.getHash())); + EXPECT_CALL(binaryShaderProvider, getBinaryShaderData(res.getHash(), _, _)); + + // Make the upload from cache fail and expect the callback telling that the cache was broken + EXPECT_CALL(renderer.deviceMock, uploadBinaryShader(_, _, _, _)).WillOnce(Return(DeviceResourceHandle::Invalid())); + EXPECT_CALL(binaryShaderProvider, binaryShaderUploaded(_, false)).Times(1); + + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + resourceObject.sceneUsage = { sceneUsingResource }; + EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject, vramSize).has_value()); + } + + TEST_F(AResourceUploader, providesSupportedBinaryShaderFormatsOnlyOnce) + { + BinaryShaderProviderFake binaryShaderProvider; + ResourceUploader uploaderWithBinaryProvider(true, &binaryShaderProvider); + + EffectResource res("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(Ref(res))).Times(3); + + EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()).Times(3); + EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })).Times(1); + + EXPECT_CALL(binaryShaderProvider, hasBinaryShader(res.getHash())).Times(3); + + ResourceDescriptor resourceObject1; + resourceObject1.resource = ManagedResource{ &res, dummyManagedResourceCallback }; + resourceObject1.sceneUsage = { SceneId{1u} }; + ResourceDescriptor resourceObject2 = resourceObject1; + resourceObject2.resource = ManagedResource{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject3 = resourceObject1; + resourceObject3.resource = ManagedResource{ &res, dummyManagedResourceCallback }; + + EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject1, vramSize).has_value()); + EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject2, vramSize).has_value()); + EXPECT_FALSE(uploaderWithBinaryProvider.uploadResource(renderer, resourceObject3, vramSize).has_value()); + } + + TEST_F(AResourceUploader, providesSupportedBinaryShaderFormatsOnlyOnceFromMultipleThreadsBeforeFirstHasBinaryShader) + { + BinaryShaderProviderFake binaryShaderProvider; + + ResourceUploader uploaderWithBinaryProvider1(true, &binaryShaderProvider); + ResourceUploader uploaderWithBinaryProvider2(true, &binaryShaderProvider); + ResourceUploader uploaderWithBinaryProvider3(true, &binaryShaderProvider); + + EffectResource res1("1", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + EffectResource res2("2", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + EffectResource res3("3", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(3); + EXPECT_CALL(binaryShaderProvider, binaryShaderFormatsReported()).Times(3); + + { + InSequence inseq; // it would be wrong if any hasBinaryShader from "the other two" threads get called before deviceSupportsBinaryShaderFormats + EXPECT_CALL(binaryShaderProvider, deviceSupportsBinaryShaderFormats(std::vector{ DeviceMock::FakeSupportedBinaryShaderFormat })).Times(1).WillOnce( + [](auto /*unused*/) { std::this_thread::sleep_for(std::chrono::milliseconds(2)); }); + EXPECT_CALL(binaryShaderProvider, hasBinaryShader(_)).Times(3); + } + + ResourceDescriptor resourceObject1; + ResourceDeleterCallingCallback dummyManagedResourceCallback1(managedResourceDeleter); + resourceObject1.resource = ManagedResource{ &res1, dummyManagedResourceCallback1 }; + resourceObject1.sceneUsage = { SceneId{1u} }; + ResourceDescriptor resourceObject2; + ResourceDeleterCallingCallback dummyManagedResourceCallback2(managedResourceDeleter); + resourceObject2.resource = ManagedResource{ &res2, dummyManagedResourceCallback2 }; + resourceObject2.sceneUsage = { SceneId{2u} }; + ResourceDescriptor resourceObject3; + ResourceDeleterCallingCallback dummyManagedResourceCallback3(managedResourceDeleter); + resourceObject3.resource = ManagedResource{ &res3, dummyManagedResourceCallback3 }; + resourceObject3.sceneUsage = { SceneId{3u} }; + + ThreadBarrier startBarrier(3); + std::thread t1([&]() { + ThreadLocalLog::SetPrefix(2); + startBarrier.wait(); + uint32_t ramSize = 0; + EXPECT_FALSE(uploaderWithBinaryProvider1.uploadResource(renderer, resourceObject1, ramSize).has_value()); + }); + std::thread t2([&]() { + ThreadLocalLog::SetPrefix(3); + startBarrier.wait(); + uint32_t ramSize = 0; + EXPECT_FALSE(uploaderWithBinaryProvider2.uploadResource(renderer, resourceObject2, ramSize).has_value()); + }); + std::thread t3([&]() { + ThreadLocalLog::SetPrefix(4); + startBarrier.wait(); + uint32_t ramSize = 0; + EXPECT_FALSE(uploaderWithBinaryProvider3.uploadResource(renderer, resourceObject3, ramSize).has_value()); + }); + + t1.join(); + t2.join(); + t3.join(); + } + + + TEST_F(AResourceUploader, unloadsVertexArrayResource) + { + const DeviceResourceHandle handle(123u); + EXPECT_CALL(renderer.deviceMock, deleteVertexBuffer(handle)); + uploader.unloadResource(renderer, EResourceType::VertexArray, ResourceContentHash(456u, 0), handle); + } + + TEST_F(AResourceUploader, unloadsIndexArrayResource) + { + const DeviceResourceHandle handle(123u); + EXPECT_CALL(renderer.deviceMock, deleteIndexBuffer(handle)); + uploader.unloadResource(renderer, EResourceType::IndexArray, ResourceContentHash(456u, 0), handle); + } + + TEST_F(AResourceUploader, unloadsTexture2DResource) + { + const DeviceResourceHandle handle(123u); + EXPECT_CALL(renderer.deviceMock, deleteTexture(handle)); + uploader.unloadResource(renderer, EResourceType::Texture2D, ResourceContentHash(456u, 0), handle); + } + + TEST_F(AResourceUploader, unloadsTexture3DResource) + { + const DeviceResourceHandle handle(123u); + EXPECT_CALL(renderer.deviceMock, deleteTexture(handle)); + uploader.unloadResource(renderer, EResourceType::Texture3D, ResourceContentHash(456u, 0), handle); + } + + TEST_F(AResourceUploader, unloadsTextureCubeResource) + { + const DeviceResourceHandle handle(123u); + EXPECT_CALL(renderer.deviceMock, deleteTexture(handle)); + uploader.unloadResource(renderer, EResourceType::TextureCube, ResourceContentHash(456u, 0), handle); + } + + TEST_F(AResourceUploader, unloadsEffectResource) + { + const DeviceResourceHandle handle(123u); + EXPECT_CALL(renderer.deviceMock, deleteShader(handle)); + uploader.unloadResource(renderer, EResourceType::Effect, ResourceContentHash(456u, 0), handle); + } + + TEST_F(AResourceUploader, uploadsWithMultipleRenderBackends) + { + StrictMock additionalRenderer; + + const ArrayResource res(EResourceType::VertexArray, 1, EDataType::Float, nullptr, {}); + ManagedResource managedRes{ &res, dummyManagedResourceCallback }; + ResourceDescriptor resourceObject; + resourceObject.resource = managedRes; + EXPECT_CALL(managedResourceDeleter, managedResourceDeleted(_)).Times(1); + + EXPECT_CALL(renderer.deviceMock, allocateVertexBuffer(res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(renderer.deviceMock, uploadVertexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); + EXPECT_EQ(123u, uploader.uploadResource(renderer, resourceObject, vramSize)); + + EXPECT_CALL(additionalRenderer.deviceMock, allocateVertexBuffer(res.getDecompressedDataSize())).WillOnce(Return(DeviceResourceHandle(123))); + EXPECT_CALL(additionalRenderer.deviceMock, uploadVertexBufferData(DeviceResourceHandle(123), res.getResourceData().data(), res.getDecompressedDataSize())); + EXPECT_EQ(123u, uploader.uploadResource(additionalRenderer, resourceObject, vramSize)); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploadingManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploadingManagerTest.cpp new file mode 100644 index 000000000..694a9a6d8 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/ResourceUploadingManagerTest.cpp @@ -0,0 +1,879 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2017 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/ResourceUploadingManager.h" +#include "internal/RendererLib/RendererResourceRegistry.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "ResourceUploaderMock.h" +#include "ResourceMock.h" +#include "PlatformMock.h" +#include "MockResourceHash.h" +#include "internal/Components/ResourceDeleterCallingCallback.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + + +namespace ramses::internal +{ + + namespace + { + DisplayConfig makeConfig(uint64_t resourceCacheSize, SceneId preferredScene, SceneId deprivedScene) + { + DisplayConfig cfg; + cfg.setGPUMemoryCacheSize(resourceCacheSize); + if (preferredScene.isValid()) + { + cfg.setScenePriority(preferredScene, -1); + } + if (deprivedScene.isValid()) + { + cfg.setScenePriority(deprivedScene, 12); + } + return cfg; + } + } + + class AResourceUploadingManager : public ::testing::Test + { + public: + explicit AResourceUploadingManager(const DisplayConfig& cfg = DisplayConfig()) + : dummyResource(EResourceType::IndexArray, 5, EDataType::UInt16, reinterpret_cast(m_dummyData), {}) + , dummyEffectResource("", "", "", {}, EffectInputInformationVector(), EffectInputInformationVector(), "") + , dummyManagedResourceCallback(managedResourceDeleter) + , sceneId(66u) + , uploader(new StrictMock) + , asyncEffectUploader(platformMock, platformMock.renderBackendMock, notifier, 1) + , rendererResourceUploader(resourceRegistry, std::unique_ptr{ uploader }, platformMock.renderBackendMock, asyncEffectUploader, cfg, frameTimer, stats) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + InSequence s; + EXPECT_CALL(platformMock.renderBackendMock.contextMock, disable()).WillOnce(Return(true)); + EXPECT_CALL(platformMock, createResourceUploadRenderBackend()); + EXPECT_CALL(platformMock.renderBackendMock.contextMock, enable()).WillOnce(Return(true)); + const bool status = asyncEffectUploader.createResourceUploadRenderBackendAndStartThread(); + EXPECT_TRUE(status); + } + + ~AResourceUploadingManager() override + { + EXPECT_CALL(platformMock, destroyResourceUploadRenderBackend()); + asyncEffectUploader.destroyResourceUploadRenderBackendAndStopThread(); + } + + void registerAndProvideResource(ResourceContentHash hash, bool effectResource = false, const IResource* resource = nullptr, SceneId id = {}) + { + resourceRegistry.registerResource(hash); + resourceRegistry.addResourceRef(hash, id.isValid() ? id : sceneId); + + if (!resource) + resource = (effectResource ? static_cast(&dummyEffectResource) : &dummyResource); + resourceRegistry.setResourceData(hash, ManagedResource{ resource, dummyManagedResourceCallback }); + + ASSERT_TRUE(contains_c(resourceRegistry.getAllProvidedResources(), hash)); + } + + void makeResourceUnused(ResourceContentHash hash, SceneId id = {}) + { + resourceRegistry.removeResourceRef(hash, id.isValid() ? id : sceneId); + if (resourceRegistry.containsResource(hash)) + { + ASSERT_TRUE(contains_c(resourceRegistry.getAllResourcesNotInUseByScenes(), hash)); + } + } + + void expectResourceUploaded(ResourceContentHash hash, DeviceResourceHandle deviceHandle = ResourceUploaderMock::FakeResourceDeviceHandle) + { + ASSERT_TRUE(resourceRegistry.containsResource(hash)); + const ResourceDescriptor& rd = resourceRegistry.getResourceDescriptor(hash); + EXPECT_EQ(EResourceStatus::Uploaded, rd.status); + EXPECT_EQ(deviceHandle, rd.deviceHandle); + EXPECT_FALSE(rd.resource); + } + + void expectResourceUploadFailed(ResourceContentHash hash) + { + ASSERT_TRUE(resourceRegistry.containsResource(hash)); + const ResourceDescriptor& rd = resourceRegistry.getResourceDescriptor(hash); + EXPECT_EQ(EResourceStatus::Broken, rd.status); + EXPECT_FALSE(rd.deviceHandle.isValid()); + EXPECT_FALSE(rd.resource); + } + + void expectResourceStatus(ResourceContentHash hash, EResourceStatus status) + { + ASSERT_TRUE(resourceRegistry.containsResource(hash)); + const ResourceDescriptor& rd = resourceRegistry.getResourceDescriptor(hash); + EXPECT_EQ(status, rd.status); + } + + void expectResourceUnloaded(ResourceContentHash hash) + { + EXPECT_FALSE(resourceRegistry.containsResource(hash)); + } + + void expectDeviceFlushOnWindows() + { +#if defined(_WIN32) + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, flush()); +#endif + } + + void uploadShader(const ResourceContentHash& hash, bool expectSuccess = true) + { + const std::optional unsetDeviceHandle; + EXPECT_CALL(*uploader, uploadResource(_, _, _)).WillOnce(Return(unsetDeviceHandle)); + + if (expectSuccess) + { + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)); + expectDeviceFlushOnWindows(); + EXPECT_CALL(platformMock.renderBackendMock.deviceMock, registerShader(_)); + EXPECT_CALL(*uploader, storeShaderInBinaryShaderCache(Ref(platformMock.renderBackendMock), DeviceMock::FakeShaderDeviceHandle, hash, sceneId)); + } + else + { + EXPECT_CALL(platformMock.resourceUploadRenderBackendMock.deviceMock, uploadShader(_)).WillOnce(Invoke([](const auto& /*unused*/) {return std::move(std::unique_ptr{}); })); + expectDeviceFlushOnWindows(); + EXPECT_CALL(platformMock.renderBackendMock.deviceMock, registerShader(_)).Times(0); + EXPECT_CALL(*uploader, storeShaderInBinaryShaderCache(_, _, _, _)).Times(0); + } + + rendererResourceUploader.uploadAndUnloadPendingResources(); + ASSERT_EQ(EResourceStatus::ScheduledForUpload, resourceRegistry.getResourceStatus(hash)); + + constexpr std::chrono::seconds timeoutTime{ 2u }; + const auto startTime = std::chrono::steady_clock::now(); + while (resourceRegistry.getResourceStatus(hash) == EResourceStatus::ScheduledForUpload + && std::chrono::steady_clock::now() - startTime < timeoutTime) + { + std::this_thread::sleep_for(std::chrono::milliseconds{ 5u }); + rendererResourceUploader.uploadAndUnloadPendingResources(); + } + } + + protected: + static const uint16_t m_dummyData[5]; + + RendererResourceRegistry resourceRegistry; + StrictMock platformMock; + + const ArrayResource dummyResource; + const EffectResource dummyEffectResource; + NiceMock managedResourceDeleter; + ResourceDeleterCallingCallback dummyManagedResourceCallback; + + const SceneId sceneId; + + FrameTimer frameTimer; + RendererStatistics stats; + NiceMock notifier; + StrictMock* uploader; + AsyncEffectUploader asyncEffectUploader; + ResourceUploadingManager rendererResourceUploader; + }; + + const uint16_t AResourceUploadingManager::m_dummyData[5] = { 0x1C }; + + class AResourceUploadingManager_WithVRAMCache : public AResourceUploadingManager + { + public: + AResourceUploadingManager_WithVRAMCache() + : AResourceUploadingManager(makeConfig(30u, {}, {})) + { + } + }; + + class AResourceUploadingManager_ScenePriority : public AResourceUploadingManager + { + public: + AResourceUploadingManager_ScenePriority() + : AResourceUploadingManager(makeConfig(0u, getPreferredScene(), getDeprivedScene())) + { + } + + static SceneId getPreferredScene() + { + return SceneId{113114}; + } + + static SceneId getDeprivedScene() + { + return SceneId{551122}; + } + }; + + TEST_F(AResourceUploadingManager, hasNothingToUploadUnloadInitially) + { + EXPECT_FALSE(rendererResourceUploader.hasAnythingToUpload()); + // no call expectations + rendererResourceUploader.uploadAndUnloadPendingResources(); + } + + TEST_F(AResourceUploadingManager, reportsItemsToUploadWhenRegistryHasProvidedResource) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res); + + EXPECT_TRUE(rendererResourceUploader.hasAnythingToUpload()); + + makeResourceUnused(res); + } + + TEST_F(AResourceUploadingManager, reportsItemsToUploadWhenRegistryHasScheduledForUploadResource) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res); + resourceRegistry.setResourceScheduledForUpload(res); + EXPECT_TRUE(resourceRegistry.hasAnyResourcesScheduledForUpload()); + EXPECT_EQ(EResourceStatus::ScheduledForUpload, resourceRegistry.getResourceStatus(res)); + + EXPECT_TRUE(rendererResourceUploader.hasAnythingToUpload()); + + resourceRegistry.setResourceBroken(res); + makeResourceUnused(res); + } + + TEST_F(AResourceUploadingManager, reportsNothingToUploadWhenProvidedResourceUnused) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res); + makeResourceUnused(res); + + EXPECT_FALSE(rendererResourceUploader.hasAnythingToUpload()); + } + + TEST_F(AResourceUploadingManager, uploadsProvidedResource) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)); + makeResourceUnused(res); + } + + TEST_F(AResourceUploadingManager, unloadsUnusedResource) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res); + + makeResourceUnused(res); + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUnloaded(res); + } + + TEST_F(AResourceUploadingManager, setsBrokenStatusForResourceFailedToUpload) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)).WillOnce(Return(DeviceResourceHandle::Invalid())); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploadFailed(res); + + makeResourceUnused(res); + } + + TEST_F(AResourceUploadingManager, uploadEffectAndStoreInBinaryShaderCacheIfResourceUploaderDidNotReturnDeviceHandle) + { + const auto resHash = dummyEffectResource.getHash(); + registerAndProvideResource(resHash, true); + + uploadShader(resHash); + expectResourceUploaded(resHash, DeviceMock::FakeShaderDeviceHandle); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)); + makeResourceUnused(resHash); + } + + TEST_F(AResourceUploadingManager, setsBrokenStatusForEffectIfUploadFailed) + { + const auto resHash = dummyEffectResource.getHash(); + registerAndProvideResource(resHash, true); + + uploadShader(resHash, false); + expectResourceUploadFailed(resHash); + + makeResourceUnused(resHash); + } + + TEST_F(AResourceUploadingManager, uploadsAllProvidedResourcesInOneUpdate_defaultUploadStrategy) + { + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + const ResourceContentHash res5(1238u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2); + registerAndProvideResource(res3); + registerAndProvideResource(res4); + registerAndProvideResource(res5); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(5u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + expectResourceUploaded(res1); + expectResourceUploaded(res2); + expectResourceUploaded(res3); + expectResourceUploaded(res4); + expectResourceUploaded(res5); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + makeResourceUnused(res4); + makeResourceUnused(res5); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(5); + } + + TEST_F(AResourceUploadingManager, willUnloadAnyRemainingResourcesWhenDestructed) + { + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2, true); + registerAndProvideResource(res3); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(3u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + Mock::VerifyAndClearExpectations(&uploader); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); + } + + TEST_F(AResourceUploadingManager, willNotUnloadEffectResourceUntilDestructor) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res, true); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res); + + makeResourceUnused(res); + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res); + Mock::VerifyAndClearExpectations(&uploader); + + // destructor will unload kept effects + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); + } + + TEST_F(AResourceUploadingManager, uploadsAtLeastOneResourcePerUpdateIfOutOfTimeBudget) + { + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + + registerAndProvideResource(res1, false); + registerAndProvideResource(res2, false); + registerAndProvideResource(res3, false); + + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, 0u); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res1); + expectResourceStatus(res2, EResourceStatus::Provided); + expectResourceStatus(res3, EResourceStatus::Provided); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res1); + expectResourceUploaded(res2); + expectResourceStatus(res3, EResourceStatus::Provided); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(2u); + } + + TEST_F(AResourceUploadingManager, uploadsBatchOfResourceBeforeCheckingIfOutOfTimeBudget) + { + // first resource is always uploaded so batch is check frequency constant + 1 + const auto numResourcesInBatch = static_cast(rendererResourceUploader.getResourceUploadBatchSize()) + 1; + // create resources to fill one batch and extra resource in addition + std::vector resList(numResourcesInBatch + 1); + uint64_t hash = 1234u; + for (auto& res : resList) + { + res = ResourceContentHash(hash++, 0u); + registerAndProvideResource(res, false); + } + + // set budget to infinite to make sure the batch gets to be processed + // then right after set budget to 0 and expect rest of batch to be uploaded till next budget check + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(numResourcesInBatch) + .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); return ResourceUploaderMock::FakeResourceDeviceHandle; })) + .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, 0u); return ResourceUploaderMock::FakeResourceDeviceHandle; })) + .WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); + + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + for (int i = 0; i < numResourcesInBatch; ++i) + expectResourceUploaded(resList[i]); + + // last resource is outside of batch and was skipped for uploading + expectResourceStatus(resList.back(), EResourceStatus::Provided); + + for (const auto& res : resList) + makeResourceUnused(res); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(numResourcesInBatch); + } + + TEST_F(AResourceUploadingManager, checksTimeBudgetForEachLargeResourceWhenUploading) + { + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + + const std::vector dummyData(ResourceUploadingManager::LargeResourceByteSizeThreshold / 4 + 1, 0u); + const ArrayResource largeResource(EResourceType::IndexArray, static_cast(dummyData.size()), EDataType::UInt32, dummyData.data(), ""); + + registerAndProvideResource(res1, false, &largeResource); + registerAndProvideResource(res2, false, &largeResource); + registerAndProvideResource(res3, false, &largeResource); + + // set budget to infinite to make sure more than just first resource is processed + // then right after set budget to 0 and test if the other resources were uploaded + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2) + .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); return ResourceUploaderMock::FakeResourceDeviceHandle; })) + .WillOnce(InvokeWithoutArgs([this]() { frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, 0u); return ResourceUploaderMock::FakeResourceDeviceHandle; })); + + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res1); + expectResourceUploaded(res2); + // last resource was skipped because even though within batch it is large resources and those are checked for time budget separately + expectResourceStatus(res3, EResourceStatus::Provided); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(2); + } + + TEST_F(AResourceUploadingManager, uploadsOnlyResourcesFittingIntoTimeBudgetInOneUpdate_uploadSlow) + { + const std::vector dummyData(ResourceUploadingManager::LargeResourceByteSizeThreshold / 4 + 1, 0u); + const ArrayResource largeResource(EResourceType::IndexArray, static_cast(dummyData.size()), EDataType::UInt32, dummyData.data(), ""); + + // using large resources so that time budget checks happen on every resources, not just once per batch + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + + registerAndProvideResource(res1, false, &largeResource); + registerAndProvideResource(res2, false, &largeResource); + registerAndProvideResource(res3, false, &largeResource); + registerAndProvideResource(res4, false, &largeResource); + + //set section time budget so that to be enough for upload of several resources + const uint32_t sectionTimeBudgetMiillis = 10u; + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, sectionTimeBudgetMiillis * 1000u); + + //make sure that not all resources will be uploaded + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtMost(3)).WillRepeatedly(InvokeWithoutArgs([&]() {PlatformThread::Sleep(4); return ResourceUploaderMock::FakeResourceDeviceHandle; })); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + // expect first res uploaded + expectResourceUploaded(res1); + // expect last res not uploaded + expectResourceStatus(res4, EResourceStatus::Provided); + + // upload rest + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res2); + expectResourceUploaded(res3); + expectResourceUploaded(res4); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + makeResourceUnused(res4); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(4); + } + + TEST_F(AResourceUploadingManager, uploadsOnlyResourcesFittingIntoTimeBudgetInOneUpdate_decompressSlow) + { + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + + NiceMock resource1{ res1, EResourceType::IndexArray }; + NiceMock resource2{ res2, EResourceType::IndexArray }; + NiceMock resource3{ res3, EResourceType::IndexArray }; + NiceMock resource4{ res4, EResourceType::IndexArray }; + + // simulate large resources so that time budget checks happen on every resources, not just once per batch + ON_CALL(resource1, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); + ON_CALL(resource2, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); + ON_CALL(resource3, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); + ON_CALL(resource4, getDecompressedDataSize()).WillByDefault(Return(ResourceUploadingManager::LargeResourceByteSizeThreshold + 1)); + ON_CALL(resource1, isDeCompressedAvailable()).WillByDefault(Return(true)); + ON_CALL(resource2, isDeCompressedAvailable()).WillByDefault(Return(true)); + ON_CALL(resource3, isDeCompressedAvailable()).WillByDefault(Return(true)); + ON_CALL(resource4, isDeCompressedAvailable()).WillByDefault(Return(true)); + + registerAndProvideResource(res1, false, &resource1); + registerAndProvideResource(res2, false, &resource2); + registerAndProvideResource(res3, false, &resource3); + registerAndProvideResource(res4, false, &resource4); + + const uint32_t sectionTimeBudgetMiillis = 10u; + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, sectionTimeBudgetMiillis * 1000u); + + //make sure that not all resources will be uploaded + EXPECT_CALL(resource1, decompress()).Times(AtMost(3)).WillRepeatedly(InvokeWithoutArgs([]() { PlatformThread::Sleep(10); })); + + frameTimer.startFrame(); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + // expect first res uploaded + expectResourceUploaded(res1); + // expect last res not uploaded + expectResourceStatus(res4, EResourceStatus::Provided); + + // upload rest + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); + frameTimer.startFrame(); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(AtLeast(1)).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res2); + expectResourceUploaded(res3); + expectResourceUploaded(res4); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + makeResourceUnused(res4); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(4); + } + + TEST_F(AResourceUploadingManager, doesNotReportKeptEffectAsPendingUnload) + { + const ResourceContentHash res(1234u, 0u); + registerAndProvideResource(res, true); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res); + + makeResourceUnused(res); + + EXPECT_FALSE(rendererResourceUploader.hasAnythingToUpload()); + + // destructor will unload kept effects + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); + } + + TEST_F(AResourceUploadingManager_WithVRAMCache, willUploadResourcesEvenIfExceedingCacheSize) + { + // test resource has size of 10 bytes + // cache is set to 30 bytes + + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + const ResourceContentHash res5(1238u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2); + registerAndProvideResource(res3); + registerAndProvideResource(res4); + registerAndProvideResource(res5); + + // all uploaded even though cache is actually smaller + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(5u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + expectResourceUploaded(res1); + expectResourceUploaded(res2); + expectResourceUploaded(res3); + expectResourceUploaded(res4); + expectResourceUploaded(res5); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + makeResourceUnused(res4); + makeResourceUnused(res5); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(5); + } + + TEST_F(AResourceUploadingManager_WithVRAMCache, willUnloadUnusedCachedResourcesIfCacheSizeExceeded) + { + // test resource has size of 10 bytes + // cache is set to 30 bytes + + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + const ResourceContentHash res5(1238u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2); + registerAndProvideResource(res3); + registerAndProvideResource(res4); + registerAndProvideResource(res5); + + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(5u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + makeResourceUnused(res1); + makeResourceUnused(res2); + makeResourceUnused(res3); + makeResourceUnused(res4); + makeResourceUnused(res5); + + // unload anything exceeding cache limit + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(2u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + expectResourceUploaded(res3); + expectResourceUploaded(res4); + expectResourceUploaded(res5); + Mock::VerifyAndClearExpectations(&uploader); + + // destructor will unload kept resources + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); + } + + TEST_F(AResourceUploadingManager_WithVRAMCache, willOnlyUnloadResourcesWhenNewResourcesAreToBeUploadedAndSoThatCacheSizeIsUsedAtMaximum) + { + // test resource has size of 10 bytes + // cache is set to 30 bytes + + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2); + + // cache is 20/30 filled + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + makeResourceUnused(res2); + + // cache stays unchanged, 10/30 used resource, 10/30 unused resource, 10/30 available + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + registerAndProvideResource(res3); + registerAndProvideResource(res4); + + // 20 bytes is needed, 10/30 is available right away, 10/30 needs to be freed + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + expectResourceUploaded(res1); + expectResourceUploaded(res3); + expectResourceUploaded(res4); + Mock::VerifyAndClearExpectations(&uploader); + + makeResourceUnused(res1); + makeResourceUnused(res3); + makeResourceUnused(res4); + + // destructor will unload kept resources + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); + } + + TEST_F(AResourceUploadingManager_WithVRAMCache, willNotUnloadAnyCachedUnusedResourceIfThereIsEnoughRemainingCacheAvailable) + { + // test resource has size of 10 bytes + // cache is set to 30 bytes + + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2); + + // cache is 20/30 filled + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + makeResourceUnused(res2); + + // cache stays unchanged, 10/30 used resource, 10/30 unused resource, 10/30 available + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + registerAndProvideResource(res3); + + // 10 bytes is needed, 10/30 is available right away, nothing needs to be freed + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(0u); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + expectResourceUploaded(res1); + expectResourceUploaded(res2); + expectResourceUploaded(res3); + Mock::VerifyAndClearExpectations(&uploader); + + makeResourceUnused(res1); + makeResourceUnused(res3); + + // destructor will unload kept resources + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); + } + + TEST_F(AResourceUploadingManager_WithVRAMCache, willUnloadResourcesOfSameOrGreaterSizeOfResourcesToBeUploaded_AlreadyUsingMoreThanCacheSize) + { + // test resource has size of 10 bytes + // cache is set to 30 bytes + + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + const ResourceContentHash res5(1238u, 0u); + + registerAndProvideResource(res1); + registerAndProvideResource(res2); + registerAndProvideResource(res3); + registerAndProvideResource(res4); + + // more than cache size is used + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(4u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + makeResourceUnused(res1); + makeResourceUnused(res2); + + // unload unused resources exceeding cache limit + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res2); + + registerAndProvideResource(res5); + + // cache is full and exactly 1 unused cached resource will be freed for 1 new resource to be uploaded + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(1u); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1u); + rendererResourceUploader.uploadAndUnloadPendingResources(); + + expectResourceUploaded(res3); + expectResourceUploaded(res4); + expectResourceUploaded(res5); + Mock::VerifyAndClearExpectations(&uploader); + + makeResourceUnused(res3); + makeResourceUnused(res4); + makeResourceUnused(res5); + + // destructor will unload kept resources + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(3u); + } + + TEST_F(AResourceUploadingManager_ScenePriority, uploadsPreferredResourcesFirst) + { + const std::vector dummyData(ResourceUploadingManager::LargeResourceByteSizeThreshold / 4 + 1, 0u); + const ArrayResource largeResource(EResourceType::IndexArray, static_cast(dummyData.size()), EDataType::UInt32, dummyData.data(), ""); + + // using large resources so that time budget checks happen on every resource, not just once per batch + const ResourceContentHash res1(1234u, 0u); + const ResourceContentHash res2(1235u, 0u); + const ResourceContentHash res3(1236u, 0u); + const ResourceContentHash res4(1237u, 0u); + + registerAndProvideResource(res1, false, &largeResource, getDeprivedScene()); + registerAndProvideResource(res2, false, &largeResource, getDeprivedScene()); + registerAndProvideResource(res3, false, &largeResource, getPreferredScene()); + registerAndProvideResource(res4, false, &largeResource); + + //set section time budget so the first resource upload will exceed it + const uint32_t sectionTimeBudgetMillis = 10u; + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, sectionTimeBudgetMillis * 1000u); + + //make sure that only 1 resource will be uploaded + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1).WillRepeatedly(InvokeWithoutArgs([&]() {PlatformThread::Sleep(12); return ResourceUploaderMock::FakeResourceDeviceHandle; })); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + // expect preferred resource uploaded + expectResourceUploaded(res3); + // expect others not uploaded + expectResourceStatus(res1, EResourceStatus::Provided); + expectResourceStatus(res2, EResourceStatus::Provided); + expectResourceStatus(res4, EResourceStatus::Provided); + + //make sure that only 1 resource will be uploaded + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(1).WillRepeatedly(InvokeWithoutArgs([&]() {PlatformThread::Sleep(12); return ResourceUploaderMock::FakeResourceDeviceHandle; })); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + // expect preferred resource uploaded + expectResourceUploaded(res4); + // expect others not uploaded + expectResourceStatus(res1, EResourceStatus::Provided); + expectResourceStatus(res2, EResourceStatus::Provided); + + // upload rest + frameTimer.setSectionTimeBudget(EFrameTimerSectionBudget::ResourcesUpload, std::numeric_limits::max()); + EXPECT_CALL(*uploader, uploadResource(_, _, _)).Times(2).WillRepeatedly(Return(ResourceUploaderMock::FakeResourceDeviceHandle)); + frameTimer.startFrame(); + rendererResourceUploader.uploadAndUnloadPendingResources(); + expectResourceUploaded(res1); + expectResourceUploaded(res2); + + makeResourceUnused(res1, getDeprivedScene()); + makeResourceUnused(res2, getDeprivedScene()); + makeResourceUnused(res3, getPreferredScene()); + makeResourceUnused(res4); + + EXPECT_CALL(*uploader, unloadResource(_, _, _, _)).Times(4); + } + +} diff --git a/renderer/RendererLib/RendererLib/test/SceneAllocateHelper.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneAllocateHelper.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/test/SceneAllocateHelper.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneAllocateHelper.cpp index b11881fe3..1e6b751b0 100644 --- a/renderer/RendererLib/RendererLib/test/SceneAllocateHelper.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneAllocateHelper.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "SceneAllocateHelper.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/RenderBuffer.h" +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/RenderBuffer.h" -namespace ramses_internal +namespace ramses::internal { SceneAllocateHelper::SceneAllocateHelper(IScene& scene) : m_scene(scene) @@ -192,7 +192,7 @@ namespace ramses_internal return m_scene.allocateDataBuffer(dataBufferType, dataType, maximumSizeInBytes, preallocateHandle(handle)); } - TextureBufferHandle SceneAllocateHelper::allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) + TextureBufferHandle SceneAllocateHelper::allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle /*= TextureBufferHandle::Invalid()*/) { return m_scene.allocateTextureBuffer(textureFormat, mipMapDimensions, preallocateHandle(handle)); } diff --git a/renderer/RendererLib/RendererLib/test/SceneAllocateHelper.h b/tests/unittests/renderer/renderer-lib/RendererLib/SceneAllocateHelper.h similarity index 83% rename from renderer/RendererLib/RendererLib/test/SceneAllocateHelper.h rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneAllocateHelper.h index acf55d102..5739a8e28 100644 --- a/renderer/RendererLib/RendererLib/test/SceneAllocateHelper.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneAllocateHelper.h @@ -6,19 +6,18 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEALLOCATEHELPER_H -#define RAMSES_SCENEALLOCATEHELPER_H - -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/ECameraProjectionType.h" -#include "SceneAPI/EDataSlotType.h" -#include "SceneAPI/DataSlot.h" -#include "SceneAPI/TextureEnums.h" -#include "SceneAPI/DataFieldInfo.h" -#include "SceneAPI/MipMapSize.h" -#include "SceneAPI/SceneId.h" - -namespace ramses_internal +#pragma once + +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/ECameraProjectionType.h" +#include "internal/SceneGraph/SceneAPI/EDataSlotType.h" +#include "internal/SceneGraph/SceneAPI/DataSlot.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/SceneAPI/DataFieldInfo.h" +#include "internal/SceneGraph/SceneAPI/MipMapSize.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" + +namespace ramses::internal { class IScene; enum class EDataBufferType : uint8_t; @@ -45,7 +44,7 @@ namespace ramses_internal RenderBufferHandle allocateRenderBuffer(const RenderBuffer& renderBuffer, RenderBufferHandle handle = RenderBufferHandle::Invalid()); DataSlotHandle allocateDataSlot(const DataSlot& dataSlot, DataSlotHandle handle = DataSlotHandle::Invalid()); DataBufferHandle allocateDataBuffer(EDataBufferType dataBufferType, EDataType dataType, uint32_t maximumSizeInBytes, DataBufferHandle handle = DataBufferHandle::Invalid()); - TextureBufferHandle allocateTextureBuffer(ETextureFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()); + TextureBufferHandle allocateTextureBuffer(EPixelStorageFormat textureFormat, const MipMapDimensions& mipMapDimensions, TextureBufferHandle handle = TextureBufferHandle::Invalid()); PickableObjectHandle allocatePickableObject(DataBufferHandle geometryHandle, NodeHandle nodeHandle, PickableObjectId id, PickableObjectHandle pickableHandle = PickableObjectHandle::Invalid()); SceneReferenceHandle allocateSceneReference(SceneId sceneId, SceneReferenceHandle handle = {}); @@ -56,5 +55,3 @@ namespace ramses_internal IScene& m_scene; }; } - -#endif diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/SceneDependencyCheckerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneDependencyCheckerTest.cpp new file mode 100644 index 000000000..e0983b864 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneDependencyCheckerTest.cpp @@ -0,0 +1,407 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/SceneDependencyChecker.h" +#include "internal/SceneGraph/Scene/Scene.h" +#include "internal/RendererLib/Types.h" + +namespace ramses::internal +{ + class ASceneDependencyChecker : public ::testing::Test + { + public: + SceneDependencyChecker dependencyChecker; + + [[nodiscard]] static bool CheckSceneOrder(SceneId sceneFirst, SceneId sceneSecond, const SceneIdVector& sceneList) + { + bool foundFirst = false; + for (auto i : sceneList) + { + if (i == sceneFirst) + { + foundFirst = true; + } + if (i == sceneSecond) + { + if (foundFirst) + { + return true; + } + return false; + } + } + return false; + } + }; + + TEST_F(ASceneDependencyChecker, isEmptyInitially) + { + EXPECT_TRUE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canAddDependencyToIndependentScenes) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //check that the scenes are not dependent in th first place + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveDependencyOfDependentScenes) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + dependencyChecker.removeDependency(scene1, scene2); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + EXPECT_TRUE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, returnsCorrectDependencyListForOneDependency) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + //check scene dependency list + const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); + EXPECT_EQ(2u, sceneList.size()); + EXPECT_TRUE(CheckSceneOrder(scene1, scene2, sceneList)); + } + + TEST_F(ASceneDependencyChecker, canNotAddReverseDependencyToDependentScenes) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + //add reverse dependency should not be possible + EXPECT_FALSE(dependencyChecker.addDependency(scene2, scene1)); + + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canAddMultipleDependenciesForOneScene) + { + SceneId scene1(3u); + SceneId scene2(5u); + SceneId scene3(7u); + + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene3)); + + //check scene dependency list + const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); + EXPECT_EQ(3u, sceneList.size()); + EXPECT_TRUE(CheckSceneOrder(scene1, scene2, sceneList)); + EXPECT_TRUE(CheckSceneOrder(scene1, scene3, sceneList)); + + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canAddMultipleDependenciesForMultipleScenes) + { + SceneId scene1(3u); + SceneId scene2(5u); + SceneId scene3(7u); + SceneId scene4(17u); + + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene4)); + + //check scene dependency list + const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); + EXPECT_EQ(4u, sceneList.size()); + EXPECT_TRUE(CheckSceneOrder(scene1, scene2, sceneList)); + EXPECT_TRUE(CheckSceneOrder(scene3, scene4, sceneList)); + + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canAddMultipleCascadingDependenciesForMultipleScenes) + { + SceneId scene1(3u); + SceneId scene2(5u); + SceneId scene3(7u); + SceneId scene4(17u); + + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); + + //check scene dependency list + const SceneIdVector& sceneList = dependencyChecker.getDependentScenesInOrder(); + EXPECT_EQ(4u, sceneList.size()); + EXPECT_TRUE(CheckSceneOrder(scene1, scene2, sceneList)); + EXPECT_TRUE(CheckSceneOrder(scene1, scene3, sceneList)); + EXPECT_TRUE(CheckSceneOrder(scene1, scene4, sceneList)); + EXPECT_TRUE(CheckSceneOrder(scene2, scene3, sceneList)); + EXPECT_TRUE(CheckSceneOrder(scene2, scene4, sceneList)); + + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveSingleDependentScene) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + //check dependencies + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + EXPECT_EQ(2u, dependencyChecker.getDependentScenesInOrder().size()); + + //remove scene + dependencyChecker.removeScene(scene2); + + //check dependency removal for both scenes + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + EXPECT_EQ(0u, dependencyChecker.getDependentScenesInOrder().size()); + EXPECT_TRUE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveSingleSceneWithDependency) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + //check dependencies + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_EQ(2u, dependencyChecker.getDependentScenesInOrder().size()); + + //remove scene + dependencyChecker.removeScene(scene1); + + //check dependency removal for both scenes + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + EXPECT_EQ(0u, dependencyChecker.getDependentScenesInOrder().size()); + EXPECT_TRUE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveSingleDependentSceneFromCascadingDependency) + { + SceneId scene1(3u); + SceneId scene2(5u); + SceneId scene3(7u); + SceneId scene4(17u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); + + //check dependencies + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); + EXPECT_EQ(4u, dependencyChecker.getDependentScenesInOrder().size()); + + //remove scene + dependencyChecker.removeScene(scene1); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); + + EXPECT_EQ(3u, dependencyChecker.getDependentScenesInOrder().size()); + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveSingleSceneWithDependencyFromCascadingDependency) + { + SceneId scene1(3u); + SceneId scene2(5u); + SceneId scene3(7u); + SceneId scene4(17u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); + + //check dependencies + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); + EXPECT_EQ(4u, dependencyChecker.getDependentScenesInOrder().size()); + + //remove scene + dependencyChecker.removeScene(scene4); + + //check dependency removal for both scenes + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene4)); + + EXPECT_EQ(3u, dependencyChecker.getDependentScenesInOrder().size()); + EXPECT_FALSE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveSingleDependentSceneWithDependenciesFromCascadingDependency) + { + SceneId scene1(3u); + SceneId scene2(5u); + SceneId scene3(7u); + SceneId scene4(17u); + + //add a dependency + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene3)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene4)); + + //check dependencies + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); + EXPECT_EQ(4u, dependencyChecker.getDependentScenesInOrder().size()); + + //remove scene + dependencyChecker.removeScene(scene2); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene4)); + + EXPECT_EQ(0u, dependencyChecker.getDependentScenesInOrder().size()); + EXPECT_TRUE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, canRemoveDependencyAddedTwiceWithTwoRemovalsOnly) + { + SceneId scene1(3u); + SceneId scene2(5u); + + //add a dependency twice + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene2)); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + dependencyChecker.removeDependency(scene1, scene2); + + //scene2 should still be dependent + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + dependencyChecker.removeDependency(scene1, scene2); + + //now it should not be dependent anymore + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + + EXPECT_TRUE(dependencyChecker.isEmpty()); + } + + TEST_F(ASceneDependencyChecker, confidenceTest_complexDependencyHierarchy) + { + const SceneId scene1(1u); + const SceneId scene2(2u); + const SceneId scene3(3u); + const SceneId scene4(4u); + const SceneId scene5(5u); + const SceneId scene6(6u); + const SceneId scene7(7u); + + EXPECT_TRUE(dependencyChecker.addDependency(scene1, scene5)); + EXPECT_TRUE(dependencyChecker.addDependency(scene2, scene5)); + EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene5)); + EXPECT_TRUE(dependencyChecker.addDependency(scene3, scene7)); + EXPECT_TRUE(dependencyChecker.addDependency(scene5, scene4)); + EXPECT_TRUE(dependencyChecker.addDependency(scene6, scene1)); + EXPECT_TRUE(dependencyChecker.addDependency(scene6, scene2)); + EXPECT_TRUE(dependencyChecker.addDependency(scene7, scene2)); + + // fail to create cyclic dependency + EXPECT_FALSE(dependencyChecker.addDependency(scene4, scene7)); + + // query dependencies + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene4)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene5)); + EXPECT_TRUE(dependencyChecker.hasDependencyAsConsumer(scene7)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene6)); + + // get ordered list of scenes + const auto& orderedScenes = dependencyChecker.getDependentScenesInOrder(); + ASSERT_EQ(7u, orderedScenes.size()); + EXPECT_EQ(scene6, orderedScenes[0]); + EXPECT_EQ(scene3, orderedScenes[1]); + EXPECT_EQ(scene1, orderedScenes[2]); + EXPECT_EQ(scene7, orderedScenes[3]); + EXPECT_EQ(scene2, orderedScenes[4]); + EXPECT_EQ(scene5, orderedScenes[5]); + EXPECT_EQ(scene4, orderedScenes[6]); + + // remove dependencies + dependencyChecker.removeDependency(scene1, scene5); + dependencyChecker.removeDependency(scene2, scene5); + dependencyChecker.removeDependency(scene3, scene2); + dependencyChecker.removeDependency(scene3, scene5); + dependencyChecker.removeDependency(scene3, scene7); + dependencyChecker.removeDependency(scene5, scene4); + dependencyChecker.removeDependency(scene6, scene1); + dependencyChecker.removeDependency(scene6, scene2); + dependencyChecker.removeDependency(scene7, scene2); + + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene1)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene2)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene3)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene4)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene5)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene6)); + EXPECT_FALSE(dependencyChecker.hasDependencyAsConsumer(scene7)); + + EXPECT_TRUE(dependencyChecker.getDependentScenesInOrder().empty()); + } +} diff --git a/renderer/RendererLib/RendererLib/test/SceneDisplayTrackerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneDisplayTrackerTest.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/test/SceneDisplayTrackerTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneDisplayTrackerTest.cpp index 4fe84559f..a304c94eb 100644 --- a/renderer/RendererLib/RendererLib/test/SceneDisplayTrackerTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneDisplayTrackerTest.cpp @@ -7,10 +7,10 @@ // ------------------------------------------------------------------------- #include "gtest/gtest.h" -#include "RendererLib/SceneDisplayTracker.h" -#include "RendererLib/RendererCommands.h" +#include "internal/RendererLib/SceneDisplayTracker.h" +#include "internal/RendererLib/RendererCommands.h" -namespace ramses_internal +namespace ramses::internal { class ASceneDisplayTracker : public ::testing::Test { @@ -72,7 +72,7 @@ namespace ramses_internal EXPECT_EQ(sceneDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::PickEvent{ sceneId, {} })); EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::CreateDisplay{ cmdDisplay, {}, {} })); EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::DestroyDisplay{ cmdDisplay })); - EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::CreateOffscreenBuffer{ cmdDisplay, {}, {}, {}, {}, {}, ERenderBufferType_DepthStencilBuffer })); + EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::CreateOffscreenBuffer{ cmdDisplay, {}, {}, {}, {}, {}, EDepthBufferType::DepthStencil })); EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::CreateDmaOffscreenBuffer{ cmdDisplay, {}, {}, {}, {}, {}, {} })); EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::DestroyOffscreenBuffer{ cmdDisplay, {} })); EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::CreateStreamBuffer{ cmdDisplay, {}, {} })); @@ -85,7 +85,7 @@ namespace ramses_internal EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::ReadPixels{ cmdDisplay, {}, {}, {}, {}, {}, {}, {}, {} })); EXPECT_EQ(cmdDisplay, tracker.determineDisplayFromRendererCommand(RendererCommand::ConfirmationEcho{ cmdDisplay, {} })); // broadcast commands - EXPECT_FALSE(tracker.determineDisplayFromRendererCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode_LocalOnly })); + EXPECT_FALSE(tracker.determineDisplayFromRendererCommand(RendererCommand::ScenePublished{ sceneId, EScenePublicationMode::LocalOnly })); EXPECT_FALSE(tracker.determineDisplayFromRendererCommand(RendererCommand::SceneUnpublished{ sceneId })); EXPECT_FALSE(tracker.determineDisplayFromRendererCommand(RendererCommand::SetSkippingOfUnmodifiedBuffers{})); EXPECT_FALSE(tracker.determineDisplayFromRendererCommand(RendererCommand::LogStatistics{})); diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/SceneExpirationMonitorTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneExpirationMonitorTest.cpp new file mode 100644 index 000000000..150306201 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneExpirationMonitorTest.cpp @@ -0,0 +1,944 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2018 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gmock/gmock.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererStatistics.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +using namespace testing; + +namespace ramses::internal +{ + class ASceneExpirationMonitor : public ::testing::Test + { + public: + ASceneExpirationMonitor() + : rendererScenes(eventCollector) + , expirationMonitor(rendererScenes, eventCollector, statistics) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + rendererScenes.createScene(SceneInfo{ scene1 }); + rendererScenes.createScene(SceneInfo{ scene2 }); + rendererScenes.createScene(SceneInfo{ scene3 }); + rendererScenes.createScene(SceneInfo{ scene4 }); + rendererScenes.createScene(SceneInfo{ scene5 }); + } + + ~ASceneExpirationMonitor() override + { + rendererScenes.destroyScene(scene1); + rendererScenes.destroyScene(scene2); + rendererScenes.destroyScene(scene3); + rendererScenes.destroyScene(scene4); + rendererScenes.destroyScene(scene5); + expectNoEvent(); + } + + protected: + void expectNoEvent() + { + RendererEventVector events; + RendererEventVector dummy; + eventCollector.appendAndConsumePendingEvents(dummy, events); + EXPECT_TRUE(events.empty()); + for (const auto& e : events) + { + EXPECT_EQ(ERendererEventType::Invalid, e.eventType); + EXPECT_EQ(0u, e.sceneId.getValue()); + } + } + + void expectEvents(std::initializer_list< std::pair > expectedEvents) + { + RendererEventVector events; + RendererEventVector dummy; + eventCollector.appendAndConsumePendingEvents(dummy, events); + EXPECT_EQ(expectedEvents.size(), events.size()); + for (const auto& expectedEvent : expectedEvents) + { + auto matchesEvent = [expectedEvent](const RendererEvent& e) { return e.sceneId == expectedEvent.first && e.eventType == expectedEvent.second; }; + auto it = std::find_if(events.begin(), events.end(), matchesEvent); + ASSERT_TRUE(it != events.end()); + events.erase(it); + } + } + + void stopMonitoringScenes(std::initializer_list scenes) + { + for (const auto scene : scenes) + { + expirationMonitor.onDestroyed(scene); + expectEvents({ { scene, ERendererEventType::SceneExpirationMonitoringDisabled } }); + } + } + + void stopMonitoringSceneAndDiscardAllEvents(SceneId sceneId) + { + expirationMonitor.onDestroyed(sceneId); + RendererEventVector dummy; + eventCollector.appendAndConsumePendingEvents(dummy, dummy); + } + + [[nodiscard]] std::string logOutput() const + { + StringOutputStream strstr; + statistics.writeStatsToStream(strstr); + return strstr.release(); + } + + RendererEventCollector eventCollector; + RendererScenes rendererScenes; + SceneExpirationMonitor expirationMonitor; + RendererStatistics statistics; + + const FlushTime::Clock::time_point currentFakeTime{ std::chrono::milliseconds(10000) }; + + const SceneId scene1{ 22u }; + const SceneId scene2{ 23u }; + const SceneId scene3{ 24u }; + const SceneId scene4{ 25u }; + const SceneId scene5{ 26u }; + }; + + TEST_F(ASceneExpirationMonitor, reportInvalidTSForScenesWithNoExpiration) + { + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene3)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene4)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene5)); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, doesNotCheckAnythingIfNoScenesMonitored) + { + expirationMonitor.checkExpiredScenes(currentFakeTime); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, doesNotGenerateEventIfZeroLimit) + { + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expirationMonitor.onFlushApplied(scene2, FlushTime::InvalidTimestamp, {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, TSFromFlushAppliedDoesNotAffectRenderedTS) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + stopMonitoringScenes({ scene1, scene2 }); + } + + TEST_F(ASceneExpirationMonitor, lastTSFromFlushAppliedBecomesRenderedTSWhenSceneReportedRendered) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onRendered(scene1); + EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onRendered(scene2); + EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + stopMonitoringScenes({ scene1, scene2 }); + } + + TEST_F(ASceneExpirationMonitor, renderedTSIsResetIfSceneHidden) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onHidden(scene1); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onHidden(scene2); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + stopMonitoringScenes({ scene1, scene2 }); + } + + TEST_F(ASceneExpirationMonitor, renderedTSIsTakenFromLastAppliedFlushIfSceneRenderedAgainAfterHidden) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::hours(2), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + expirationMonitor.onHidden(scene1); + expirationMonitor.onHidden(scene2); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + // if rendered again, TS becomes valid - taken from last applied flush + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + EXPECT_EQ(currentFakeTime + std::chrono::hours(1), expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + EXPECT_EQ(currentFakeTime + std::chrono::hours(2), expirationMonitor.getExpirationTimestampOfRenderedScene(scene2)); + + stopMonitoringScenes({ scene1, scene2 }); + } + + TEST_F(ASceneExpirationMonitor, doesNotGenerateEventIfLimitNotExceeded) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime); + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, AlwaysExpiresIfZeroTimeComesFromClock) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + // Zero time comes when PTP is broken or has lost sync for too long + expirationMonitor.checkExpiredScenes( FlushTime::Clock::time_point{ std::chrono::milliseconds(0) }); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, canHandleTimeStampInFutureAndTreatsAsNotExceeded) + { + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::time_point(currentFakeTime + std::chrono::hours(100)) + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime); + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventIfAppliedFlushTSExpired) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventIfLastAppliedFlushTSExpired) + { + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectNoEvent(); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventIfRenderedTSExpired) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectEvents({ {scene1, ERendererEventType::SceneExpired} }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventIfLastAppliedFlushTSExpiredEvenIfRenderedTSNotExpired) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime, {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventIfLastPendingFlushExpired) + { + // enable monitoring by applying at least one flush with timestamp + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + // create some pending flushes + auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; + pendingFlushes.resize(3u); + pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; + pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; + pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::hours(1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectNoEvent(); + + pendingFlushes.push_back({}); + pendingFlushes[3].timeInfo.expirationTimestamp = currentFakeTime; + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventIfAnyOfPendingFlushesExpiredEvenIfAppliedAndRenderedNotExpired) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + expirationMonitor.onRendered(scene1); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + // create some pending flushes + auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; + pendingFlushes.resize(3u); + pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; + pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; + pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::hours(1); + expectNoEvent(); + + pendingFlushes.push_back({}); + pendingFlushes[3].timeInfo.expirationTimestamp = currentFakeTime; + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventOnlyOnceIfExpiredAndCheckedMultipleTimesWithSingleApplyOnly) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + // still in exceeded state, no more events + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(4)); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(5)); + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, evaluatesExpirationBasedOnLimitFromLastAppliedFlush) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectNoEvent(); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(100)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventForAllScenesWhereAppropriate) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onFlushApplied(scene2, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene3, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene2, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene3, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + expirationMonitor.onRendered(scene3); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectEvents({ { scene1, ERendererEventType::SceneExpired },{ scene3, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1, scene2, scene3 }); + } + + TEST_F(ASceneExpirationMonitor, generatesEventOnlyOnceIfSceneKeepsBeingExpired) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + for (size_t i = 0; i < 5u; ++i) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 2)); + } + // is still exceeded + expirationMonitor.onRendered(scene1); + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesRecoveryEventWhenExceedingStops) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); + expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesRecoveryEventOnlyOnceWhenExceedingStopsAndKeepsBelowLimit) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); + expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration } }); + + for (size_t i = 0; i < 5u; ++i) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 10)); + } + // is still below limit + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, willReportSceneThatKeepsBeingFlushedButNotRendered) + { + // scene must be rendered at least once with valid expiration, only then it can expire + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + RendererEventVector events; + for (size_t i = 0; i < 5; ++i) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(2), {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 1)); + } + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, willNotReportSceneThatKeepsBeingFlushedButHidden) + { + // scene must be rendered at least once with valid expiration, only then it can expire + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.onHidden(scene1); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + RendererEventVector events; + for (size_t i = 0; i < 5; ++i) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(i) + std::chrono::milliseconds(2), {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i + 1)); + } + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, willReportSceneThatKeepsBeingRenderedButNotFlushed) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(2), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + RendererEventVector events; + for (size_t i = 0; i < 5; ++i) + { + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i)); + } + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, doesNotGenerateEventIfLastRenderedTSExpiredButSceneHidden) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime); + expectNoEvent(); + + expirationMonitor.onHidden(scene1); + + // keep flushing up-to-date, no rendering + for (int i = 0; i < 5; ++i) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1 + i), {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(i)); + } + // if renderedTS would be checked there would be expiration event + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, generatesRecoveryEventIfRenderedTSExpiredButSceneGetsHiddenAndNewFlushesArrive) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + // hiding alone will not cause recovery - last applied flush still expired + expirationMonitor.onHidden(scene1); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + expectNoEvent(); + + // keep scene hidden and apply new up-to-date flush + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(5), {}, 0); + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(3)); + expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, confidenceTest_checkingOfOneSceneDoesNotAffectCheckingOfOtherScene) + { + // flush 1 + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + // few cycles of scene 2 + expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene2, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene2); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectEvents({ { scene1, ERendererEventType::SceneExpired },{ scene2, ERendererEventType::SceneExpired } }); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene2); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene2); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectNoEvent(); + + // both exceeded, get both back to normal + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectEvents({ { scene1, ERendererEventType::SceneRecoveredFromExpiration },{ scene2, ERendererEventType::SceneRecoveredFromExpiration } }); + + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene2, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene2); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + // both still below limit + expectNoEvent(); + + stopMonitoringScenes({ scene1, scene2 }); + } + + TEST_F(ASceneExpirationMonitor, confidenceTest_severalScenesWithDifferentBehaviorAndLimits) + { + //scene1 checking enabled, sometimes exceeds + //scene2 checking disabled + //scene3 checking enabled, never exceeds + //scene4 checking enabled, always exceeds + //scene5 checking enabled, sometimes exceeds + + // 'randomize' times of apply/render/checks + + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene3, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene5, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene3, ERendererEventType::SceneExpirationMonitoringEnabled }, { scene5, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene2); + expirationMonitor.onRendered(scene5); + + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectEvents({ {scene5, ERendererEventType::SceneExpired} }); + + expirationMonitor.onFlushApplied(scene4, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene4, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene3); + + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectEvents({ { scene4, ERendererEventType::SceneExpired } }); + + expirationMonitor.onFlushApplied(scene5, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onRendered(scene2); + expirationMonitor.onRendered(scene4); + expirationMonitor.onRendered(scene5); + + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectEvents({ { scene5, ERendererEventType::SceneRecoveredFromExpiration } }); + + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene3, FlushTime::Clock::now() + std::chrono::milliseconds(10000), {}, 0); + expirationMonitor.onFlushApplied(scene4, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + expirationMonitor.onRendered(scene3); + expirationMonitor.onRendered(scene5); + + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectNoEvent(); + + expirationMonitor.onFlushApplied(scene1, FlushTime::Clock::now() - std::chrono::milliseconds(10) + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + expirationMonitor.checkExpiredScenes(FlushTime::Clock::now()); + expectNoEvent(); + + stopMonitoringScenes({ scene1, scene3, scene4, scene5 }); + } + + TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneAndEmitsDisabledEventOnDestroy) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + expirationMonitor.onDestroyed(scene1); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + + // this would trigger exceeded event if still monitored + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + + // monitoring already disabled, no more disabled event on destroyed + expirationMonitor.onDestroyed(scene1); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneOnInvalidTS) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + // disable + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + + // this would trigger exceeded event if still monitored + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + + // monitoring already disabled, no more disabled event on destroyed + expirationMonitor.onDestroyed(scene1); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneOnInvalidTS_alreadyExpired) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + // expire + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + // disable + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + EXPECT_EQ(FlushTime::InvalidTimestamp, expirationMonitor.getExpirationTimestampOfRenderedScene(scene1)); + + // this would trigger exceeded event if still monitored + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + + // monitoring already disabled, no more disabled event on destroyed + expirationMonitor.onDestroyed(scene1); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, stopsMonitoringSceneOnInvalidTS_withExpiredPendingFlush) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + // disable + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + // create expired pending flush + auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; + pendingFlushes.resize(1u); + pendingFlushes[0].timeInfo.expirationTimestamp = currentFakeTime; + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(1)); + + // monitoring already disabled, no more disabled event on destroyed + expirationMonitor.onDestroyed(scene1); + expectNoEvent(); + } + + TEST_F(ASceneExpirationMonitor, canReenableMonitoringAfterDisable) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + // disable + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + // this would trigger exceeded event if still monitored + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectNoEvent(); + + // re-enable + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + // now it triggers exceeded + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, willNotReportRecoveredIfExpiredThenDisabledAndReenabled) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + // make expired + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + // disable + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + // this would trigger exceeded event if still monitored + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectNoEvent(); + + // re-enable + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + // if monitored the whole time this would trigger recovered event + // but there won't be any event because monitoring was disabled before + expirationMonitor.checkExpiredScenes(currentFakeTime - std::chrono::milliseconds(10)); + expectNoEvent(); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, willReportExpiredIfReenabledWhileExpired) + { + RendererEventVector events; + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + expirationMonitor.onRendered(scene1); + + // make expired + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + // disable + expirationMonitor.onFlushApplied(scene1, FlushTime::InvalidTimestamp, {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringDisabled } }); + + // this would trigger exceeded event if still monitored + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectNoEvent(); + + // re-enable + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expectEvents({ { scene1, ERendererEventType::SceneExpirationMonitoringEnabled } }); + + // scene is expired already before it was re-enabled, a new expire event will be emitted + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + expectEvents({ { scene1, ERendererEventType::SceneExpired } }); + + stopMonitoringScenes({ scene1 }); + } + + TEST_F(ASceneExpirationMonitor, willReportExpirationAppliedToStatistics) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + + expirationMonitor.checkExpiredScenes(currentFakeTime); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-1/-1/-1)")); + + statistics.reset(); + + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:9/9/9)")); + + stopMonitoringSceneAndDiscardAllEvents(scene1); + } + + TEST_F(ASceneExpirationMonitor, willReportExpirationRenderedToStatistics) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + + expirationMonitor.checkExpiredScenes(currentFakeTime); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-1/-1/-1)")); + + statistics.reset(); + + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:9/9/9)")); + + stopMonitoringSceneAndDiscardAllEvents(scene1); + } + + TEST_F(ASceneExpirationMonitor, willReportExpirationPendingToStatistics) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::hours(1), {}, 0); + + auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; + pendingFlushes.resize(3u); + pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; + pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; + pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::milliseconds(1); + + expirationMonitor.checkExpiredScenes(currentFakeTime); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-1/-1/-1)")); + + statistics.reset(); + + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(10)); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:9/9/9)")); + + stopMonitoringSceneAndDiscardAllEvents(scene1); + } + + TEST_F(ASceneExpirationMonitor, willReportMaximumExpirationToStatistics) + { + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(5), {}, 0); + expirationMonitor.onRendered(scene1); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(4), {}, 0); + + auto& pendingFlushes = rendererScenes.getStagingInfo(scene1).pendingData.pendingFlushes; + pendingFlushes.resize(3u); + pendingFlushes[0].timeInfo.expirationTimestamp = FlushTime::InvalidTimestamp; + pendingFlushes[1].timeInfo.expirationTimestamp = currentFakeTime; + pendingFlushes[2].timeInfo.expirationTimestamp = currentFakeTime + std::chrono::milliseconds(3); + + expirationMonitor.checkExpiredScenes(currentFakeTime); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-3/-3/-3)")); + + statistics.reset(); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(2), {}, 0); + expirationMonitor.onRendered(scene1); + + expirationMonitor.checkExpiredScenes(currentFakeTime); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (0/1:-2/-2/-2)")); + + statistics.reset(); + + expirationMonitor.onFlushApplied(scene1, currentFakeTime + std::chrono::milliseconds(1), {}, 0); + expirationMonitor.onRendered(scene1); + + expirationMonitor.checkExpiredScenes(currentFakeTime + std::chrono::milliseconds(2)); + statistics.frameFinished(0u); + EXPECT_THAT(logOutput(), HasSubstr("Exp (1/1:1/1/1)")); + + statistics.reset(); + + stopMonitoringSceneAndDiscardAllEvents(scene1); + } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksManagerTest.cpp new file mode 100644 index 000000000..1f2f063f6 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksManagerTest.cpp @@ -0,0 +1,355 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "SceneLinksTestUtils.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +using namespace testing; + +namespace ramses::internal +{ + template + class ASceneLinksManager : public ::testing::Test + { + public: + ASceneLinksManager() + : rendererScenes(rendererEventCollector) + , sceneLinksManager(rendererScenes.getSceneLinksManager()) + , concreteLinkManager(GetConcreteLinkManager(sceneLinksManager)) + , providerSceneId(3u) + , consumerSceneId(4u) + , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) + , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) + , providerSceneAllocator(providerScene) + , consumerSceneAllocator(consumerScene) + , providerId(33u) + , consumerId(44u) + , providerSlotHandle(5u) + , consumerSlotHandle(6u) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + createDataSlot(providerSceneAllocator, providerSlotHandle, providerId, true); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); + + createDataSlot(consumerSceneAllocator, consumerSlotHandle, consumerId, false); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); + } + + protected: + void expectRendererEvent(ERendererEventType event, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(event, events.front().eventType); + EXPECT_EQ(providerSId, events.front().providerSceneId); + EXPECT_EQ(consumerSId, events.front().consumerSceneId); + EXPECT_EQ(pId, events.front().providerdataId); + EXPECT_EQ(cId, events.front().consumerdataId); + } + + void expectRendererEvent(ERendererEventType event, SceneId consumerSId, DataSlotId cId, SceneId providerSId) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(event, events.front().eventType); + EXPECT_EQ(consumerSId, events.front().consumerSceneId); + EXPECT_EQ(cId, events.front().consumerdataId); + EXPECT_EQ(providerSId, events.front().providerSceneId); + } + + void expectConsumerLinkedToProvider() + { + EXPECT_TRUE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); + SceneLinkVector links; + this->concreteLinkManager.getSceneLinks().getLinkedConsumers(this->providerSceneId, this->providerSlotHandle, links); + ASSERT_EQ(1u, links.size()); + EXPECT_EQ(this->providerSceneId, links.front().providerSceneId); + EXPECT_EQ(this->providerSlotHandle, links.front().providerSlot); + EXPECT_EQ(this->consumerSceneId, links.front().consumerSceneId); + EXPECT_EQ(this->consumerSlotHandle, links.front().consumerSlot); + } + + void expectRendererEventUnlinkedAndDestroyedSlot(bool slotIsProvider, SceneId sceneIdOfDestroyedSlot, DataSlotId destroyedSlotId, SceneId consumerSId, DataSlotId cId, SceneId consumerSceneId2 = SceneId(0u), DataSlotId consumerId2 = DataSlotId(0u)) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + + const bool hasTwoLinksRemoved = (consumerId2.getValue() != 0u); + if (hasTwoLinksRemoved) + { + ASSERT_EQ(3u, events.size()); + } + else + { + ASSERT_EQ(2u, events.size()); + } + + uint32_t eventIdx = 0u; + + EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); + EXPECT_EQ(consumerSId, events[eventIdx].consumerSceneId); + EXPECT_EQ(cId, events[eventIdx].consumerdataId); + + if (hasTwoLinksRemoved) + { + ++eventIdx; + EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); + EXPECT_EQ(consumerSceneId2, events[eventIdx].consumerSceneId); + EXPECT_EQ(consumerId2, events[eventIdx].consumerdataId); + } + + ++eventIdx; + if (slotIsProvider) + { + EXPECT_EQ(ERendererEventType::SceneDataSlotProviderDestroyed, events[eventIdx].eventType); + EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].providerSceneId); + EXPECT_EQ(destroyedSlotId, events[eventIdx].providerdataId); + } + else + { + EXPECT_EQ(ERendererEventType::SceneDataSlotConsumerDestroyed, events[eventIdx].eventType); + EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].consumerSceneId); + EXPECT_EQ(destroyedSlotId, events[eventIdx].consumerdataId); + } + } + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneLinksManager& sceneLinksManager; + const T& concreteLinkManager; + const SceneId providerSceneId; + const SceneId consumerSceneId; + IScene& providerScene; + IScene& consumerScene; + SceneAllocateHelper providerSceneAllocator; + SceneAllocateHelper consumerSceneAllocator; + const DataSlotId providerId; + const DataSlotId consumerId; + const DataSlotHandle providerSlotHandle; + const DataSlotHandle consumerSlotHandle; + }; + + using ManagerTypes = ::testing::Types < + TransformationLinkManager, + DataReferenceLinkManager, + TextureLinkManager + >; + + TYPED_TEST_SUITE(ASceneLinksManager, ManagerTypes); + + TYPED_TEST(ASceneLinksManager, reportsLinkCreationEvent) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectConsumerLinkedToProvider(); + } + + TYPED_TEST(ASceneLinksManager, reportsLinkRemovalEvent) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinked, this->consumerSceneId, this->consumerId, this->providerSceneId); + } + + TYPED_TEST(ASceneLinksManager, reportsProviderSlotRemoved) + { + this->providerScene.releaseDataSlot(this->providerSlotHandle); + this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderDestroyed, this->providerSceneId, this->providerId, SceneId(0u), DataSlotId(0u)); + } + + TYPED_TEST(ASceneLinksManager, reportsConsumerSlotRemoved) + { + this->consumerScene.releaseDataSlot(this->consumerSlotHandle); + this->expectRendererEvent(ERendererEventType::SceneDataSlotConsumerDestroyed, SceneId(0u), DataSlotId(0u), this->consumerSceneId, this->consumerId); + } + + TYPED_TEST(ASceneLinksManager, removesLinkForSceneWithRemovedProviderSlot) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->providerScene.releaseDataSlot(this->providerSlotHandle); + this->expectRendererEventUnlinkedAndDestroyedSlot(true, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + + EXPECT_FALSE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); + EXPECT_FALSE(this->concreteLinkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + } + + TYPED_TEST(ASceneLinksManager, removesLinkForSceneWithRemovedConsumerSlot) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->consumerScene.releaseDataSlot(this->consumerSlotHandle); + this->expectRendererEventUnlinkedAndDestroyedSlot(false, this->consumerSceneId, this->consumerId, this->consumerSceneId, this->consumerId); + + EXPECT_FALSE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); + EXPECT_FALSE(this->concreteLinkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + } + + TYPED_TEST(ASceneLinksManager, secondLinkToConsumerOverwritesPreviousLink) + { + const DataSlotId providerId2(999u); + const DataSlotHandle slotHandle(43u); + createDataSlot(this->providerSceneAllocator, slotHandle, providerId2, true); + this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); + + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.createDataLink(this->providerSceneId, providerId2, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, providerId2, this->consumerSceneId, this->consumerId); + } + + TYPED_TEST(ASceneLinksManager, failsToCreateLinkForInvalidSceneId) + { + this->sceneLinksManager.createDataLink(SceneId(0u), this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, SceneId(0u), this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, SceneId(0u), this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, this->providerId, SceneId(0u), this->consumerId); + } + + TYPED_TEST(ASceneLinksManager, failsToCreateLinkForInvalidDataSlotId) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, DataSlotId(), this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, DataSlotId(), this->consumerSceneId, this->consumerId); + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, DataSlotId()); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, this->providerId, this->consumerSceneId, DataSlotId()); + } + + TYPED_TEST(ASceneLinksManager, failsToCreateLinkForSwappedDataSlotId) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->consumerId, this->consumerSceneId, this->providerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->providerSceneId, this->consumerId, this->consumerSceneId, this->providerId); + this->sceneLinksManager.createDataLink(this->consumerSceneId, this->providerId, this->providerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->consumerSceneId, this->providerId, this->providerSceneId, this->consumerId); + } + + TYPED_TEST(ASceneLinksManager, failsToCreateLinkForSwappedConsumerAndProvider) + { + this->sceneLinksManager.createDataLink(this->consumerSceneId, this->consumerId, this->providerSceneId, this->providerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->consumerSceneId, this->consumerId, this->providerSceneId, this->providerId); + } + + TYPED_TEST(ASceneLinksManager, canCreateLinkTwiceForSameConsumer) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + } + + TYPED_TEST(ASceneLinksManager, failsToRemoveLinkForInvalidSceneId) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.removeDataLink(SceneId(0u), this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, SceneId(0u), this->consumerId, SceneId::Invalid()); + } + + TYPED_TEST(ASceneLinksManager, failsToRemoveLinkForInvalidDataSlotId) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.removeDataLink(this->consumerSceneId, DataSlotId()); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, this->consumerSceneId, DataSlotId(), SceneId::Invalid()); + } + + TYPED_TEST(ASceneLinksManager, failsToRemoveLinkWhenProvidingProviderInsteadOfConsumer) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.removeDataLink(this->providerSceneId, this->providerId); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, this->providerSceneId, this->providerId, SceneId::Invalid()); + } + + TYPED_TEST(ASceneLinksManager, failsToRemoveLinkIfThereIsNoLink) + { + this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, this->consumerSceneId, this->consumerId, SceneId::Invalid()); + } + + TYPED_TEST(ASceneLinksManager, failsToCreateLinksCausingCyclicDependency) + { + const DataSlotId providerId2(999u); + const DataSlotHandle slotHandle(43u); + createDataSlot(this->consumerSceneAllocator, slotHandle, providerId2, true); + this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->consumerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); + + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle2(43u); + createDataSlot(this->providerSceneAllocator, slotHandle2, consumerId2, true); + this->expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, this->providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); + + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.createDataLink(this->consumerSceneId, providerId2, this->providerSceneId, consumerId2); + this->expectRendererEvent(ERendererEventType::SceneDataLinkFailed, this->consumerSceneId, providerId2, this->providerSceneId, consumerId2); + } + + TYPED_TEST(ASceneLinksManager, canLinkUnlinkAndLinkAgain) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.removeDataLink(this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataUnlinked, this->consumerSceneId, this->consumerId, this->providerSceneId); + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectConsumerLinkedToProvider(); + } + + TYPED_TEST(ASceneLinksManager, removesAllLinksToProviderOnProviderSlotDestruction) + { + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle(43u); + createDataSlot(this->consumerSceneAllocator, slotHandle, consumerId2, false); + this->expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), this->consumerSceneId, consumerId2); + + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, consumerId2); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, consumerId2); + + this->providerScene.releaseDataSlot(this->providerSlotHandle); + this->expectRendererEventUnlinkedAndDestroyedSlot(true, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId, this->consumerSceneId, consumerId2); + + EXPECT_FALSE(this->concreteLinkManager.getSceneLinks().hasLinkedConsumers(this->providerSceneId, this->providerSlotHandle)); + EXPECT_FALSE(this->concreteLinkManager.getDependencyChecker().hasDependencyAsConsumer(this->consumerSceneId)); + } + + using SceneLinksTextureManager = ASceneLinksManager; + + TEST_F(SceneLinksTextureManager, unlinksTextureLinksForUnmappedProviderScene) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + + this->sceneLinksManager.handleSceneUnmapped(this->providerSceneId); + EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(this->consumerSceneId)); + EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(this->consumerSceneId)); + } + + TEST_F(SceneLinksTextureManager, unlinksTextureLinksForUnmappedConsumerScene) + { + this->sceneLinksManager.createDataLink(this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + this->expectRendererEvent(ERendererEventType::SceneDataLinked, this->providerSceneId, this->providerId, this->consumerSceneId, this->consumerId); + + this->sceneLinksManager.handleSceneUnmapped(this->consumerSceneId); + EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToProvider(this->providerSceneId)); + EXPECT_FALSE(this->sceneLinksManager.getTextureLinkManager().getSceneLinks().hasAnyLinksToConsumer(this->providerSceneId)); + } +} diff --git a/renderer/RendererLib/RendererLib/test/SceneLinksTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTest.cpp similarity index 97% rename from renderer/RendererLib/RendererLib/test/SceneLinksTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTest.cpp index 71c763524..c8c9e6b18 100644 --- a/renderer/RendererLib/RendererLib/test/SceneLinksTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTest.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" #include "gtest/gtest.h" -#include "RendererLib/SceneLinks.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/SceneLinks.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -using namespace ramses_internal; +namespace ramses::internal +{ class ASceneLinks : public ::testing::Test { public: @@ -186,3 +186,4 @@ TEST_F(ASceneLinks, removingLinkDoesNotAffectOtherLinks) sceneLinks.removeLink(consumerScene1, consumerSlot2); expectNoLinks(); } +} diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTestUtils.h b/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTestUtils.h new file mode 100644 index 000000000..b8d9c8c8f --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneLinksTestUtils.h @@ -0,0 +1,79 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "SceneAllocateHelper.h" +#include "internal/RendererLib/SceneLinksManager.h" + +namespace ramses::internal { + + template + const T& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) + { + assert(false); + return sceneLinksManager.getTransformationLinkManager(); + } + + template <> + inline const TransformationLinkManager& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) + { + return sceneLinksManager.getTransformationLinkManager(); + } + + template <> + inline const DataReferenceLinkManager& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) + { + return sceneLinksManager.getDataReferenceLinkManager(); + } + + template <> + inline const TextureLinkManager& GetConcreteLinkManager(const SceneLinksManager& sceneLinksManager) + { + return sceneLinksManager.getTextureLinkManager(); + } + + template + void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) + { + scene.allocateDataSlot({ (providerType ? EDataSlotType::TransformationProvider : EDataSlotType::TransformationConsumer), slotId, NodeHandle(12u), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slot); + } + + template <> + inline void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) + { + const NodeHandle node = scene.allocateNode(); + scene.allocateTransform(node); + scene.allocateDataSlot({ (providerType ? EDataSlotType::TransformationProvider : EDataSlotType::TransformationConsumer), slotId, node, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slot); + } + + template <> + inline void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) + { + const DataLayoutHandle layout = scene.allocateDataLayout({ DataFieldInfo(EDataType::Float) }, ResourceContentHash::Invalid()); + const DataInstanceHandle dataRef = scene.allocateDataInstance(layout); + + scene.allocateDataSlot({ (providerType ? EDataSlotType::DataProvider : EDataSlotType::DataConsumer), slotId, NodeHandle(), dataRef, ResourceContentHash::Invalid(), TextureSamplerHandle() }, slot); + } + + template <> + inline void createDataSlot(SceneAllocateHelper& scene, DataSlotHandle slot, DataSlotId slotId, bool providerType) + { + if (providerType) + { + scene.allocateDataSlot({ EDataSlotType::TextureProvider, slotId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash(1234u, 0), TextureSamplerHandle() }, slot); + } + else + { + const TextureSamplerHandle sampler = scene.allocateTextureSampler({ {}, RenderBufferHandle(999) }); + scene.allocateDataSlot({ EDataSlotType::TextureConsumer, slotId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }, slot); + } + } +} + diff --git a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicMock.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicMock.cpp similarity index 96% rename from renderer/RendererLib/RendererLib/test/SceneReferenceLogicMock.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicMock.cpp index 71e37e3b7..d8389116e 100644 --- a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicMock.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { SceneReferenceLogicMock::SceneReferenceLogicMock() = default; SceneReferenceLogicMock::~SceneReferenceLogicMock() = default; diff --git a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicMock.h b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicMock.h similarity index 83% rename from renderer/RendererLib/RendererLib/test/SceneReferenceLogicMock.h rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicMock.h index 494207059..71c54da1f 100644 --- a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicMock.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicMock.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENEREFERENCELOGICMOCK_H -#define RAMSES_SCENEREFERENCELOGICMOCK_H +#pragma once #include "gmock/gmock.h" -#include "RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/SceneReferenceLogic.h" -namespace ramses_internal +namespace ramses::internal { class SceneReferenceLogicMock : public ISceneReferenceLogic { @@ -24,5 +23,3 @@ namespace ramses_internal MOCK_METHOD(void, update, (), (override)); }; } - -#endif diff --git a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicTest.cpp similarity index 89% rename from renderer/RendererLib/RendererLib/test/SceneReferenceLogicTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicTest.cpp index 595b40c52..bfc5c1ff9 100644 --- a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicTest.cpp @@ -7,20 +7,20 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "RendererLib/SceneReferenceLogic.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/SceneReferenceOwnership.h" +#include "internal/RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/SceneReferenceOwnership.h" #include "RendererSceneStateControlMock.h" #include "RendererSceneControlLogicMock.h" #include "RendererSceneUpdaterMock.h" #include "RendererSceneEventSenderMock.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/RendererEventCollector.h" #include "SceneAllocateHelper.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ASceneReferenceLogic : public Test { @@ -48,7 +48,7 @@ namespace ramses_internal m_scenes.createScene(SceneInfo{ RefSceneId22 }); // ignore this unless overridden by test - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; }); + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; }); } protected: @@ -59,7 +59,7 @@ namespace ramses_internal Mock::VerifyAndClearExpectations(&m_sceneUpdater); // ignore this unless overridden by test - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; }); + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; }); } void expectSceneRefEventSent(SceneId refScene, SceneId expectedMasterScene) @@ -151,7 +151,7 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, requestsStateChangeForReferencedScene) { // simulate master scene Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -169,7 +169,7 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // simulate master scene Available - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -181,7 +181,7 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, reactsToMasterSceneStateChangeSoThatReferencedScenesNeverHaveHigherStateThanMaster_fromAvailableUp) { // simulate master scene Available - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); @@ -191,11 +191,11 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // next query ref scenes will report their requested states - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // simulate master scene state Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -206,11 +206,11 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // next query ref scenes will report their requested states - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // simulate master scene state to Rendered - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); @@ -223,7 +223,7 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, reactsToMasterSceneStateChangeSoThatReferencedScenesNeverHaveHigherStateThanMaster_fromRenderedDown) { // simulate master scene Rendered - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); @@ -236,11 +236,11 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // next query ref scenes will report their requested states - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Rendered; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // simulate master scene state dropped to Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -250,11 +250,11 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // next query ref scenes will report their requested states - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // simulate master scene state dropped to Available - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); @@ -270,7 +270,7 @@ namespace ramses_internal constexpr OffscreenBufferHandle ob{ 3 }; // simulate master scene has mapping set - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob; renderOrder = -13; @@ -287,7 +287,7 @@ namespace ramses_internal constexpr OffscreenBufferHandle ob2{ 5 }; // simulate master scene has mapping set - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob1; renderOrder = -13; @@ -298,7 +298,7 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // from now on both master and ref scenes have same mapping set - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([&](auto, auto& state, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& state, auto& ob_, auto& renderOrder) { state = RendererSceneState::Unavailable; // scene state is irrelevant for this test ob_ = ob1; @@ -308,7 +308,7 @@ namespace ramses_internal m_logic.update(); // simulate master scene has new buffer set - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob2; renderOrder = -13; @@ -324,7 +324,7 @@ namespace ramses_internal constexpr OffscreenBufferHandle ob{ 4 }; // simulate master scene has mapping set - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob; renderOrder = -13; @@ -335,7 +335,7 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // from now on both master and ref scenes have same mapping set - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([&](auto, auto& state, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& state, auto& ob_, auto& renderOrder) { state = RendererSceneState::Unavailable; // scene state is irrelevant for this test ob_ = ob; @@ -345,7 +345,7 @@ namespace ramses_internal m_logic.update(); // simulate master scene has new render order set - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillOnce(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob; renderOrder = -3; @@ -364,7 +364,7 @@ namespace ramses_internal // scene render order can be set only after mapping info available { // simulate master scene has mapping set - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob1; renderOrder = -13; @@ -375,12 +375,12 @@ namespace ramses_internal m_logic.update(); // next time a ref scene is queried for mapping info use the last requested values - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob1; renderOrder = -13; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob1; renderOrder = -13; @@ -398,7 +398,7 @@ namespace ramses_internal m_logic.update(); // simulate master scene render order changed - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([&](auto, auto&, auto& ob_, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& /*unused*/, auto& ob_, auto& renderOrder) { ob_ = ob1; renderOrder = 123; @@ -413,11 +413,11 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, requestsAvailableStateForAllReferencedScenesOfDestroyedMasterScene) { // simulate master scenes Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -434,7 +434,7 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report every scene as requested ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -455,7 +455,7 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, canHandleMasterWithRefsAfterItWasDestroyedAndMadeAvailableAgain) { // simulate master scene Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // request ref scenes Ready m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Ready); @@ -465,8 +465,8 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report ref scenes as requested Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // destroy master m_scenes.destroyScene(MasterSceneId1); @@ -475,8 +475,8 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report ref scenes as requested Available - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // recreate master with refs SceneAllocateHelper masterScene1{ m_scenes.createScene(SceneInfo{ MasterSceneId1 }) }; @@ -487,7 +487,7 @@ namespace ramses_internal EXPECT_EQ(MasterSceneId1, m_ownership.getSceneOwner(RefSceneId12)); // simulate master scene Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // request ref scenes available in new cycle m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Ready); @@ -497,8 +497,8 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report ref scenes as requested Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // destroy master second time m_scenes.destroyScene(MasterSceneId1); @@ -514,7 +514,7 @@ namespace ramses_internal m_scenes.destroyScene(RefSceneId12); // simulate master scene Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // request ref scenes Ready m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Ready); @@ -524,8 +524,8 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report ref scenes as requested Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // destroy master m_scenes.destroyScene(MasterSceneId1); @@ -534,8 +534,8 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report ref scenes as requested Available - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // recreate master with refs SceneAllocateHelper masterScene1{ m_scenes.createScene(SceneInfo{ MasterSceneId1 }) }; @@ -546,7 +546,7 @@ namespace ramses_internal EXPECT_EQ(MasterSceneId1, m_ownership.getSceneOwner(RefSceneId12)); // simulate master scene Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // request ref scenes available in new cycle m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Ready); @@ -556,8 +556,8 @@ namespace ramses_internal updateLogicAndVerifyExpectations(); // now report ref scenes as requested Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId12, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); // destroy master second time m_scenes.destroyScene(MasterSceneId1); @@ -1451,16 +1451,16 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, referenceCanBeControlledUnderNewMasterWhenRereferenced) { // other scenes not relevant for this test, keep available - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // simulate original master and ref scene Rendered - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Rendered); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Rendered; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); m_logic.update(); // release ref from original master @@ -1479,7 +1479,7 @@ namespace ramses_internal EXPECT_EQ(MasterSceneId2, m_ownership.getSceneOwner(RefSceneId11)); // simulate new master Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -1497,16 +1497,16 @@ namespace ramses_internal // test handover from one master to another while READY, which is expected to be common use case, however would work in any other state // other scenes not relevant for this test, keep unavailable - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // simulate original master and ref scene READY - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Ready); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Ready; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); m_logic.update(); // release ref from original master @@ -1516,7 +1516,7 @@ namespace ramses_internal m_logic.update(); // new master must already be ready before handover - simulate new master READY - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -1542,16 +1542,16 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, referenceCanBeControlledUnderNewMasterWhenRereferencedAfterOldMasterDestroyed) { // other scenes not relevant for this test, keep available - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // simulate original master and ref scene Rendered - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); m_scenes.getScene(MasterSceneId2).requestSceneReferenceState(RefSceneHandle21, RendererSceneState::Rendered); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId21, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Rendered; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId21, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); m_logic.update(); // destroy original master @@ -1571,7 +1571,7 @@ namespace ramses_internal EXPECT_EQ(MasterSceneId1, m_ownership.getSceneOwner(RefSceneId21)); // simulate new master Ready - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Ready; })); @@ -1597,14 +1597,14 @@ namespace ramses_internal constexpr int32_t renderOrder2{ 6 }; // simulate original master and ref scene Rendered and mapped - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([&](auto, auto& targetState, auto& ob, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& targetState, auto& ob, auto& renderOrder) { targetState = RendererSceneState::Rendered; ob = ob1; renderOrder = renderOrder1; })); m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Rendered); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([&](auto, auto& targetState, auto& ob, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& targetState, auto& ob, auto& renderOrder) { targetState = RendererSceneState::Rendered; ob = ob1; @@ -1629,7 +1629,7 @@ namespace ramses_internal m_logic.update(); // simulate new master Ready with different assignment parameters - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([&](auto, auto& targetState, auto& ob, auto& renderOrder) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId2, _, _, _)).WillRepeatedly(Invoke([&](auto /*unused*/, auto& targetState, auto& ob, auto& renderOrder) { targetState = RendererSceneState::Rendered; ob = ob2; @@ -1646,16 +1646,16 @@ namespace ramses_internal TEST_F(ASceneReferenceLogic, ignoresActionsThatArriveAfterReferenceReleased) { // other scenes not relevant for this test, keep available - EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(_, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // simulate original master and ref scene Rendered - EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) + EXPECT_CALL(m_sceneLogic, getSceneInfo(MasterSceneId1, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); m_scenes.getScene(MasterSceneId1).requestSceneReferenceState(RefSceneHandle11, RendererSceneState::Rendered); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Rendered; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Rendered; })); m_logic.update(); // add some action @@ -1678,7 +1678,7 @@ namespace ramses_internal // ref scene is now under new master which did not request any state for it yet, i.e. default is available EXPECT_CALL(m_sceneLogic, setSceneState(RefSceneId11, RendererSceneState::Available)); m_logic.update(); - EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto, auto& targetState, auto&, auto&) { targetState = RendererSceneState::Available; })); + EXPECT_CALL(m_sceneLogic, getSceneInfo(RefSceneId11, _, _, _)).WillRepeatedly(Invoke([](auto /*unused*/, auto& targetState, auto& /*unused*/, auto& /*unused*/) { targetState = RendererSceneState::Available; })); // expect nothing m_logic.update(); diff --git a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicWithSceneUpdaterTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicWithSceneUpdaterTest.cpp similarity index 94% rename from renderer/RendererLib/RendererLib/test/SceneReferenceLogicWithSceneUpdaterTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicWithSceneUpdaterTest.cpp index 99df7e1fc..65a173bcf 100644 --- a/renderer/RendererLib/RendererLib/test/SceneReferenceLogicWithSceneUpdaterTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneReferenceLogicWithSceneUpdaterTest.cpp @@ -7,29 +7,29 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "RendererLib/SceneReferenceLogic.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/RendererSceneUpdater.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/FrameTimer.h" -#include "RendererLib/SceneReferenceOwnership.h" -#include "Scene/SceneActionCollectionCreator.h" +#include "internal/RendererLib/SceneReferenceLogic.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererSceneUpdater.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/FrameTimer.h" +#include "internal/RendererLib/SceneReferenceOwnership.h" +#include "internal/SceneGraph/Scene/SceneActionCollectionCreator.h" #include "RendererSceneStateControlMock.h" #include "RendererSceneControlLogicMock.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/RendererEventCollector.h" #include "SceneAllocateHelper.h" #include "PlatformMock.h" #include "RendererMock.h" #include "RendererSceneEventSenderMock.h" #include "ResourceDeviceHandleAccessorMock.h" -#include "Components/SceneUpdate.h" -#include "Watchdog/ThreadAliveNotifierMock.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Components/SceneUpdate.h" +#include "internal/Watchdog/ThreadAliveNotifierMock.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { // This is a component test consisting of full implementations (no mock) of RendererSceneUpdter, RendererSceneControlLogic and SceneReferenceLogic. // It is supposed to test only expectations affected by the interaction of those components, it is not supposed to replace each component's unit tests. @@ -69,7 +69,7 @@ namespace ramses_internal void publishScene(SceneId sceneId) { - m_sceneUpdater.handleScenePublished(sceneId, EScenePublicationMode_LocalAndRemote); + m_sceneUpdater.handleScenePublished(sceneId, EScenePublicationMode::LocalAndRemote); } void receiveScene(SceneId sceneId) @@ -153,7 +153,7 @@ namespace ramses_internal { m_sceneLogic.setSceneState(MasterSceneId, RendererSceneState::Rendered); - m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode_LocalAndRemote); + m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode::LocalAndRemote); auto& masterScene = m_scenes.getScene(MasterSceneId); const auto refSceneHandle = SceneAllocateHelper(masterScene).allocateSceneReference(RefSceneId); @@ -197,7 +197,7 @@ namespace ramses_internal EXPECT_CALL(m_sceneEventSenderFromSceneRefLogic, sendSceneStateChanged(MasterSceneId, RefSceneId, RendererSceneState::Ready)); masterScene.requestSceneReferenceState(refSceneHandle, RendererSceneState::Rendered); - m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode_LocalAndRemote); + m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode::LocalAndRemote); update(); receiveScene(RefSceneId); flushScene(RefSceneId); @@ -236,7 +236,7 @@ namespace ramses_internal m_sceneLogic.setSceneState(MasterSceneId, RendererSceneState::Rendered); masterScene.requestSceneReferenceState(refSceneHandle, RendererSceneState::Rendered); - m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode_LocalAndRemote); + m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode::LocalAndRemote); update(); receiveScene(RefSceneId); flushScene(RefSceneId); @@ -275,7 +275,7 @@ namespace ramses_internal m_sceneLogic.setSceneState(MasterSceneId, RendererSceneState::Rendered); masterScene.requestSceneReferenceState(refSceneHandle, RendererSceneState::Rendered); - m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode_LocalAndRemote); + m_sceneUpdater.handleScenePublished(RefSceneId, EScenePublicationMode::LocalAndRemote); update(); receiveScene(RefSceneId); flushScene(RefSceneId); diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/SceneResourceUploaderTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneResourceUploaderTest.cpp new file mode 100644 index 000000000..c60f0745a --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneResourceUploaderTest.cpp @@ -0,0 +1,103 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2021 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "internal/RendererLib/Types.h" +#include "internal/RendererLib/SceneResourceUploader.h" +#include "RendererResourceManagerMock.h" +#include "internal/SceneGraph/Scene/Scene.h" +#include "SceneAllocateHelper.h" +#include "internal/SceneGraph/SceneUtils/ResourceUtils.h" + +namespace ramses::internal { + using namespace testing; + + class ASceneResourceUploader : public ::testing::Test + { + public: + ASceneResourceUploader() + : scene(SceneInfo(sceneID)) + , allocateHelper(scene) + { + } + + protected: + const SceneId sceneID{ 13u }; + Scene scene; + SceneAllocateHelper allocateHelper; + StrictMock resourceManager; + }; + + + TEST_F(ASceneResourceUploader, UploadVertexArray) + { + const auto node = allocateHelper.allocateNode(); + const auto renderable = allocateHelper.allocateRenderable(node); + const DataFieldInfo indexField(EDataType::Indices, 1u, EFixedSemantics::Indices); + const DataFieldInfo vert1Field(EDataType::Vector2Buffer, 1u); + const DataFieldInfo vert2Field(EDataType::Vector3Buffer, 1u); + + const ResourceContentHash fakeEffectHash {123456u, 0u}; + const ResourceContentHash fakeIndexBufferHash {123456u, 1u}; + const ResourceContentHash fakeVertexBuffer1Hash {123456u, 2u}; + const ResourceContentHash fakeVertexBuffer2tHash {123456u, 3u}; + + const DeviceResourceHandle fakeEffectDeviceHandle { 999990u }; + const DeviceResourceHandle fakeIndexBufferDeviceHandle { 999991u }; + const DeviceResourceHandle fakeVertexBuffer1DeviceHandle{ 999992u }; + const DeviceResourceHandle fakeVertexBuffer2DeviceHandle{ 999993u }; + + const auto geomLayout = allocateHelper.allocateDataLayout({ indexField, vert1Field, vert2Field }, fakeEffectHash); + const auto geomInstance = allocateHelper.allocateDataInstance(geomLayout); + + scene.setRenderableDataInstance(renderable, ERenderableDataSlotType_Geometry, geomInstance); + scene.setRenderableStartVertex(renderable, 17u); + scene.setDataResource(geomInstance, DataFieldHandle{ 0u }, fakeIndexBufferHash, {}, 0u, 0u, 0u); + scene.setDataResource(geomInstance, DataFieldHandle{ 1u }, fakeVertexBuffer1Hash, {}, 11u, 12u, 13u); + scene.setDataResource(geomInstance, DataFieldHandle{ 2u }, fakeVertexBuffer2tHash, {}, 14u, 15u, 16u); + + VertexArrayInfo expectedVertexArrayInfo; + expectedVertexArrayInfo.shader = fakeEffectDeviceHandle; + expectedVertexArrayInfo.indexBuffer = fakeIndexBufferDeviceHandle; + expectedVertexArrayInfo.vertexBuffers.push_back({ fakeVertexBuffer1DeviceHandle, DataFieldHandle{1u}, 11u, 17u, EDataType::Vector2F, 12u, 13u }); + expectedVertexArrayInfo.vertexBuffers.push_back({ fakeVertexBuffer2DeviceHandle, DataFieldHandle{2u}, 14u, 17u, EDataType::Vector3F, 15u, 16u }); + + InSequence seq; + EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeEffectHash)) .WillOnce(Return(fakeEffectDeviceHandle)); + EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeIndexBufferHash)) .WillOnce(Return(fakeIndexBufferDeviceHandle)); + EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeVertexBuffer1Hash)) .WillOnce(Return(fakeVertexBuffer1DeviceHandle)); + EXPECT_CALL(resourceManager, getResourceDeviceHandle(fakeVertexBuffer2tHash)) .WillOnce(Return(fakeVertexBuffer2DeviceHandle)); + + VertexArrayInfo resultVertexArrayInfo; + EXPECT_CALL(resourceManager, uploadVertexArray(renderable, _, sceneID)).WillOnce(Invoke([&](auto /*unused*/, auto vai, auto /*unused*/) { + resultVertexArrayInfo = std::move(vai); + return DeviceResourceHandle{}; + })); + SceneResourceUploader::UploadVertexArray(scene, renderable, resourceManager); + + EXPECT_EQ(fakeEffectDeviceHandle, resultVertexArrayInfo.shader); + EXPECT_EQ(fakeIndexBufferDeviceHandle, resultVertexArrayInfo.indexBuffer); + + ASSERT_EQ(2u, resultVertexArrayInfo.vertexBuffers.size()); + + EXPECT_EQ(fakeVertexBuffer1DeviceHandle , resultVertexArrayInfo.vertexBuffers[0].deviceHandle); + EXPECT_EQ(DataFieldHandle{ 0u } , resultVertexArrayInfo.vertexBuffers[0].field); + EXPECT_EQ(11u , resultVertexArrayInfo.vertexBuffers[0].instancingDivisor); + EXPECT_EQ(17u , resultVertexArrayInfo.vertexBuffers[0].startVertex); + EXPECT_EQ(EDataType::Vector2Buffer , resultVertexArrayInfo.vertexBuffers[0].bufferDataType); + EXPECT_EQ(12u , resultVertexArrayInfo.vertexBuffers[0].offsetWithinElement); + EXPECT_EQ(13u , resultVertexArrayInfo.vertexBuffers[0].stride); + + EXPECT_EQ(fakeVertexBuffer2DeviceHandle , resultVertexArrayInfo.vertexBuffers[1].deviceHandle); + EXPECT_EQ(DataFieldHandle{ 1u } , resultVertexArrayInfo.vertexBuffers[1].field); + EXPECT_EQ(14u , resultVertexArrayInfo.vertexBuffers[1].instancingDivisor); + EXPECT_EQ(17u , resultVertexArrayInfo.vertexBuffers[1].startVertex); + EXPECT_EQ(EDataType::Vector3Buffer , resultVertexArrayInfo.vertexBuffers[1].bufferDataType); + EXPECT_EQ(15u , resultVertexArrayInfo.vertexBuffers[1].offsetWithinElement); + EXPECT_EQ(16u , resultVertexArrayInfo.vertexBuffers[1].stride); + } +} diff --git a/renderer/RendererLib/RendererLib/test/SceneStateExecutorTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneStateExecutorTest.cpp similarity index 99% rename from renderer/RendererLib/RendererLib/test/SceneStateExecutorTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneStateExecutorTest.cpp index f8f30d209..22663cf10 100644 --- a/renderer/RendererLib/RendererLib/test/SceneStateExecutorTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneStateExecutorTest.cpp @@ -6,18 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" -#include "RendererLib/SceneStateExecutor.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererLib/RendererScenes.h" -#include "RendererLib/SceneExpirationMonitor.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/SceneStateExecutor.h" +#include "internal/RendererLib/RendererLogContext.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/SceneExpirationMonitor.h" +#include "internal/RendererLib/RendererEventCollector.h" #include "ComponentMocks.h" #include "RendererMock.h" #include "RendererSceneEventSenderMock.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/Core/Utils/ThreadLocalLog.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; class ASceneStateExecutor : public testing::Test @@ -70,7 +69,7 @@ namespace ramses_internal void publishScene() { - sceneStateExecutor.setPublished(sceneId, EScenePublicationMode_LocalAndRemote); + sceneStateExecutor.setPublished(sceneId, EScenePublicationMode::LocalAndRemote); expectRendererEvent(ERendererEventType::ScenePublished); EXPECT_EQ(ESceneState::Published, sceneStateExecutor.getSceneState(sceneId)); } @@ -930,13 +929,13 @@ namespace ramses_internal TEST_F(ASceneStateExecutor, publishesScene) { - sceneStateExecutor.setPublished(sceneId, EScenePublicationMode_LocalAndRemote); + sceneStateExecutor.setPublished(sceneId, EScenePublicationMode::LocalAndRemote); expectRendererEvent(ERendererEventType::ScenePublished); } TEST_F(ASceneStateExecutor, canGetClientGuidOfKnownScene) { - sceneStateExecutor.setPublished(sceneId, EScenePublicationMode_LocalAndRemote); + sceneStateExecutor.setPublished(sceneId, EScenePublicationMode::LocalAndRemote); expectRendererEvent(ERendererEventType::ScenePublished); } diff --git a/renderer/RendererLib/RendererLib/test/SceneStateInfoTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/SceneStateInfoTest.cpp similarity index 76% rename from renderer/RendererLib/RendererLib/test/SceneStateInfoTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/SceneStateInfoTest.cpp index bcc9778af..6c16205a4 100644 --- a/renderer/RendererLib/RendererLib/test/SceneStateInfoTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/SceneStateInfoTest.cpp @@ -6,21 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "renderer_common_gmock_header.h" #include "gmock/gmock.h" -#include "RendererLib/SceneStateInfo.h" +#include "internal/RendererLib/SceneStateInfo.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ASceneStateInfo : public testing::Test { protected: - ASceneStateInfo() - { - } - void checkSceneIsUnknown(const SceneId scene) const { EXPECT_FALSE(isSceneKnown(scene)); @@ -54,22 +49,22 @@ namespace ramses_internal TEST_F(ASceneStateInfo, IsInitializedCorrectly) { checkNumberOfKnownScenes(0u); - EXPECT_EQ(EScenePublicationMode_Unpublished, sceneStateInfo.getScenePublicationMode(sceneId)); + EXPECT_FALSE(sceneStateInfo.getScenePublicationMode(sceneId).has_value()); } TEST_F(ASceneStateInfo, CanAddScene) { - sceneStateInfo.addScene(sceneId, EScenePublicationMode_LocalOnly); + sceneStateInfo.addScene(sceneId, EScenePublicationMode::LocalOnly); EXPECT_TRUE(sceneStateInfo.hasScene(sceneId)); - EXPECT_EQ(EScenePublicationMode_LocalOnly, sceneStateInfo.getScenePublicationMode(sceneId)); + EXPECT_EQ(EScenePublicationMode::LocalOnly, *sceneStateInfo.getScenePublicationMode(sceneId)); checkNumberOfKnownScenes(1u); checkSceneIsKnown(sceneId); } TEST_F(ASceneStateInfo, CanRemoveScene) { - sceneStateInfo.addScene(sceneId, EScenePublicationMode_LocalOnly); + sceneStateInfo.addScene(sceneId, EScenePublicationMode::LocalOnly); checkNumberOfKnownScenes(1u); checkSceneIsKnown(sceneId); @@ -82,18 +77,18 @@ namespace ramses_internal TEST_F(ASceneStateInfo, CanAddAndRemoveMultipleScenes) { SceneId sceneId2(5u); - sceneStateInfo.addScene(sceneId, EScenePublicationMode_LocalOnly); + sceneStateInfo.addScene(sceneId, EScenePublicationMode::LocalOnly); checkNumberOfKnownScenes(1u); checkSceneIsKnown(sceneId); checkSceneIsUnknown(sceneId2); - sceneStateInfo.addScene(sceneId2, EScenePublicationMode_LocalAndRemote); + sceneStateInfo.addScene(sceneId2, EScenePublicationMode::LocalAndRemote); checkNumberOfKnownScenes(2u); checkSceneIsKnown(sceneId); checkSceneIsKnown(sceneId2); - EXPECT_EQ(EScenePublicationMode_LocalOnly, sceneStateInfo.getScenePublicationMode(sceneId)); - EXPECT_EQ(EScenePublicationMode_LocalAndRemote, sceneStateInfo.getScenePublicationMode(sceneId2)); + EXPECT_EQ(EScenePublicationMode::LocalOnly, *sceneStateInfo.getScenePublicationMode(sceneId)); + EXPECT_EQ(EScenePublicationMode::LocalAndRemote, *sceneStateInfo.getScenePublicationMode(sceneId2)); sceneStateInfo.removeScene(sceneId); EXPECT_FALSE(sceneStateInfo.hasScene(sceneId)); @@ -110,13 +105,13 @@ namespace ramses_internal TEST_F(ASceneStateInfo, CanGetSceneState) { - sceneStateInfo.addScene(sceneId, EScenePublicationMode_LocalAndRemote); + sceneStateInfo.addScene(sceneId, EScenePublicationMode::LocalAndRemote); EXPECT_EQ(ESceneState::Published, sceneStateInfo.getSceneState(sceneId)); } TEST_F(ASceneStateInfo, CanSetSceneState) { - sceneStateInfo.addScene(sceneId, EScenePublicationMode_LocalAndRemote); + sceneStateInfo.addScene(sceneId, EScenePublicationMode::LocalAndRemote); sceneStateInfo.setSceneState(sceneId, ESceneState::Subscribed); EXPECT_EQ(ESceneState::Subscribed, sceneStateInfo.getSceneState(sceneId)); } diff --git a/renderer/RendererLib/RendererLib/test/TestSceneHelper.h b/tests/unittests/renderer/renderer-lib/RendererLib/TestSceneHelper.h similarity index 90% rename from renderer/RendererLib/RendererLib/test/TestSceneHelper.h rename to tests/unittests/renderer/renderer-lib/RendererLib/TestSceneHelper.h index a7b707494..0ea8aacc4 100644 --- a/renderer/RendererLib/RendererLib/test/TestSceneHelper.h +++ b/tests/unittests/renderer/renderer-lib/RendererLib/TestSceneHelper.h @@ -6,23 +6,21 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTSCENEHELPER_H -#define RAMSES_TESTSCENEHELPER_H - -#include "renderer_common_gmock_header.h" -#include "SceneAPI/IScene.h" -#include "SceneAPI/Renderable.h" -#include "SceneAPI/SceneTypes.h" -#include "SceneAPI/TextureSampler.h" -#include "RendererLib/RendererResourceManager.h" +#pragma once + +#include "internal/SceneGraph/SceneAPI/IScene.h" +#include "internal/SceneGraph/SceneAPI/Renderable.h" +#include "internal/SceneGraph/SceneAPI/SceneTypes.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/RendererLib/RendererResourceManager.h" #include "RendererResourceManagerMock.h" #include "EmbeddedCompositingManagerMock.h" #include "DeviceMock.h" #include "SceneAllocateHelper.h" -#include "Scene/DataLayout.h" +#include "internal/SceneGraph/Scene/DataLayout.h" #include "MockResourceHash.h" -namespace ramses_internal +namespace ramses::internal { class TestSceneHelper { @@ -34,9 +32,13 @@ namespace ramses_internal , m_indexArrayAvailable(indexArrayAvailable) { if (m_indexArrayAvailable) + { ON_CALL(resourceManager, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).WillByDefault(Return(DeviceMock::FakeIndexBufferDeviceHandle)); + } else + { ON_CALL(resourceManager, getResourceDeviceHandle(MockResourceHash::IndexArrayHash)).WillByDefault(Return(DeviceResourceHandle::Invalid())); + } DataFieldInfoVector geometryDataFields(2u); geometryDataFields[indicesField.asMemoryHandle()] = DataFieldInfo(EDataType::Indices, 1u, EFixedSemantics::Indices); @@ -105,8 +107,8 @@ namespace ramses_internal BlitPassHandle createBlitPassWithDummyRenderBuffers() { - const RenderBufferHandle sourceRenderBuffer = m_sceneAllocator.allocateRenderBuffer({ 16u, 12u, ERenderBufferType_ColorBuffer, ETextureFormat::R8, ERenderBufferAccessMode_ReadWrite, 0u }); - const RenderBufferHandle destinationRenderBuffer = m_sceneAllocator.allocateRenderBuffer({ 16u, 12u, ERenderBufferType_ColorBuffer, ETextureFormat::R8, ERenderBufferAccessMode_ReadWrite, 0u }); + const RenderBufferHandle sourceRenderBuffer = m_sceneAllocator.allocateRenderBuffer({ 16u, 12u, EPixelStorageFormat::R8, ERenderBufferAccessMode::ReadWrite, 0u }); + const RenderBufferHandle destinationRenderBuffer = m_sceneAllocator.allocateRenderBuffer({ 16u, 12u, EPixelStorageFormat::R8, ERenderBufferAccessMode::ReadWrite, 0u }); const BlitPassHandle pass = m_sceneAllocator.allocateBlitPass(sourceRenderBuffer, destinationRenderBuffer); return pass; @@ -115,7 +117,7 @@ namespace ramses_internal void createRenderTarget() { m_sceneAllocator.allocateRenderTarget(renderTarget); - m_sceneAllocator.allocateRenderBuffer({ 16u, 12u, ERenderBufferType_ColorBuffer, ETextureFormat::R8, ERenderBufferAccessMode_ReadWrite, 0u }, renderTargetColorBuffer); + m_sceneAllocator.allocateRenderBuffer({ 16u, 12u, EPixelStorageFormat::R8, ERenderBufferAccessMode::ReadWrite, 0u }, renderTargetColorBuffer); m_scene.addRenderTargetRenderBuffer(renderTarget, renderTargetColorBuffer); } @@ -165,10 +167,9 @@ namespace ramses_internal const auto& samplerStates = m_scene.getTextureSampler(handle).states; m_scene.releaseTextureSampler(handle); m_scene.allocateTextureSampler({ samplerStates, contentHandleOrHash }, handle); - bool hasDataSlot = false; + [[maybe_unused]] bool hasDataSlot = false; for (DataSlotHandle d(0u); d < m_scene.getDataSlotCount(); ++d) hasDataSlot |= (m_scene.isDataSlotAllocated(d) && m_scene.getDataSlot(d).attachedTextureSampler == handle); - UNUSED(hasDataSlot); assert(!hasDataSlot && "Recreating sampler that had data slot assigned, data slot must be recreated as well"); } @@ -192,5 +193,3 @@ namespace ramses_internal bool m_indexArrayAvailable; }; } - -#endif diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/TextureLinkManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/TextureLinkManagerTest.cpp new file mode 100644 index 000000000..04021fa21 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/TextureLinkManagerTest.cpp @@ -0,0 +1,1144 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/SceneGraph/SceneAPI/TextureSampler.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/ResourceCachedScene.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "TestSceneHelper.h" +#include "SceneAllocateHelper.h" +#include "MockResourceHash.h" +#include "internal/Core/Utils/ThreadLocalLog.h" + +namespace ramses::internal { + using namespace testing; + + // Tests also TextureLinkCachedScene class + + class ATextureLinkManager : public ::testing::Test + { + public: + ATextureLinkManager() + : rendererScenes(rendererEventCollector) + , sceneLinksManager(rendererScenes.getSceneLinksManager()) + , textureLinkManager(sceneLinksManager.getTextureLinkManager()) + , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) + , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) + , providerSceneAllocator(providerScene) + , consumerSceneAllocator(consumerScene) + , sceneHelper(consumerScene) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + consumerSceneAllocator.allocateTextureSampler({ {}, consumerTextureHash }, sampler); + consumerSceneAllocator.allocateTextureSampler({ {}, consumerTextureHash }, sampler2); + consumerSceneAllocator.allocateTextureSampler({ {}, consumerTextureHash }, sampler3); + consumerSceneAllocator.allocateTextureSampler({ {}, TextureSampler::ContentType::ExternalTexture, {}, InvalidMemoryHandle }, samplerExternal); + + providerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureProvider, providerId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }, providerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureConsumer, consumerId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler }, consumerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureConsumer, consumerId2, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler2 }, consumerSlotHandle2); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureConsumer, consumerId3, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), sampler3 }, consumerSlotHandle3); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId3); + + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureConsumer, consumerExternalId, NodeHandle(), DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), samplerExternal }, consumerExternalSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerExternalId); + providerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureProvider, providerExternalId, NodeHandle(), DataInstanceHandle::Invalid(), {}, TextureSamplerHandle() }, providerExternalSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerExternalId, SceneId(0u), DataSlotId(0u)); + + renderable = createRenderableWithResourcesAndMakeClean(sampler); + // create a renderable with sampler2 and sampler3 as well so it is used for device handle caching + createRenderableWithResourcesAndMakeClean(sampler2); + createRenderableWithResourcesAndMakeClean(sampler3); + + renderableWithExternalTexture = createRenderableWithExternalTextureSampler(samplerExternal); + } + + protected: + RendererEvent expectRendererEvent(ERendererEventType type) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + EXPECT_EQ(1u, events.size()); + if (events.size() == 1u) + { + RendererEvent event = events.front(); + EXPECT_EQ(type, event.eventType); + return event; + } + return {}; + } + + void expectRendererEvent(ERendererEventType type, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) + { + const RendererEvent event = expectRendererEvent(type); + EXPECT_EQ(providerSId, event.providerSceneId); + EXPECT_EQ(consumerSId, event.consumerSceneId); + EXPECT_EQ(pId, event.providerdataId); + EXPECT_EQ(cId, event.consumerdataId); + } + + void expectRendererEvent(ERendererEventType type, SceneId consumerSId, DataSlotId cId, SceneId providerSId) + { + const RendererEvent event = expectRendererEvent(type); + EXPECT_EQ(consumerSId, event.consumerSceneId); + EXPECT_EQ(cId, event.consumerdataId); + EXPECT_EQ(providerSId, event.providerSceneId); + } + + void expectRendererEvent(ERendererEventType type, OffscreenBufferHandle buffer, SceneId consumerSId, DataSlotId cId) + { + const RendererEvent event = expectRendererEvent(type); + EXPECT_EQ(buffer, event.offscreenBuffer); + EXPECT_FALSE(event.streamBuffer.isValid()); + EXPECT_FALSE(event.externalBuffer.isValid()); + EXPECT_EQ(consumerSId, event.consumerSceneId); + EXPECT_EQ(cId, event.consumerdataId); + } + + void expectRendererEvent(ERendererEventType type, StreamBufferHandle buffer, SceneId consumerSId, DataSlotId cId) + { + const RendererEvent event = expectRendererEvent(type); + EXPECT_EQ(buffer, event.streamBuffer); + EXPECT_FALSE(event.offscreenBuffer.isValid()); + EXPECT_FALSE(event.externalBuffer.isValid()); + EXPECT_EQ(consumerSId, event.consumerSceneId); + EXPECT_EQ(cId, event.consumerdataId); + } + + void expectRendererEvent(ERendererEventType type, ExternalBufferHandle buffer, SceneId consumerSId, DataSlotId cId) + { + const RendererEvent event = expectRendererEvent(type); + EXPECT_EQ(buffer, event.externalBuffer); + EXPECT_FALSE(event.offscreenBuffer.isValid()); + EXPECT_FALSE(event.streamBuffer.isValid()); + EXPECT_EQ(consumerSId, event.consumerSceneId); + EXPECT_EQ(cId, event.consumerdataId); + } + + void updateConsumerSceneResourcesCache() + { + consumerScene.updateRenderableResources(sceneHelper.resourceManager); + } + + RenderableHandle createRenderableWithResourcesAndMakeClean(TextureSamplerHandle samplerHandle) + { + const RenderableHandle renderableHandle = sceneHelper.createRenderable(); + const DataInstanceHandle uniformData = consumerSceneAllocator.allocateDataInstance(sceneHelper.testUniformLayout); + sceneHelper.m_scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Uniforms, uniformData); + sceneHelper.m_scene.setDataTextureSamplerHandle(uniformData, sceneHelper.samplerField, samplerHandle); + sceneHelper.createAndAssignVertexDataInstance(renderableHandle); + sceneHelper.setResourcesToRenderable(renderableHandle); + updateConsumerSceneResourcesCache(); + EXPECT_FALSE(consumerScene.renderableResourcesDirty(renderableHandle)); + return renderableHandle; + } + + RenderableHandle createRenderableWithExternalTextureSampler(TextureSamplerHandle samplerHandle) + { + const RenderableHandle renderableHandle = sceneHelper.createRenderable(); + DataFieldInfoVector uniformDataFields(1u); + constexpr DataFieldHandle sampleDataField{ 0u }; + uniformDataFields[sampleDataField.asMemoryHandle()] = DataFieldInfo(EDataType::TextureSamplerExternal); + const auto dataLayout = consumerSceneAllocator.allocateDataLayout(uniformDataFields, MockResourceHash::EffectHash); + const auto uniformDataInstance = consumerSceneAllocator.allocateDataInstance(dataLayout); + sceneHelper.m_scene.setRenderableDataInstance(renderableHandle, ERenderableDataSlotType_Uniforms, uniformDataInstance); + sceneHelper.m_scene.setDataTextureSamplerHandle(uniformDataInstance, sampleDataField, samplerHandle); + sceneHelper.createAndAssignVertexDataInstance(renderableHandle); + sceneHelper.setResourcesToRenderable(renderableHandle); + + updateConsumerSceneResourcesCache(); + return renderableHandle; + } + + void expectNoTextureLink() + { + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_EQ(TextureSampler::ContentType::ClientTexture, consumerScene.getTextureSampler(sampler).contentType); + EXPECT_EQ(consumerTextureHash, consumerScene.getTextureSampler(sampler).textureResource); + } + + void expectTextureLink(ResourceContentHash hash) + { + EXPECT_TRUE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_EQ(hash, textureLinkManager.getLinkedTexture(consumerSceneId, sampler)); + EXPECT_EQ(TextureSampler::ContentType::ClientTexture, consumerScene.getTextureSampler(sampler).contentType); + EXPECT_EQ(hash, consumerScene.getTextureSampler(sampler).textureResource); + } + + void expectNoBufferLink(TextureSamplerHandle samplerHandle) + { + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); + EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); + updateConsumerSceneResourcesCache(); + if (consumerScene.getTextureSampler(samplerHandle).contentType == TextureSampler::ContentType::ExternalTexture) + { + // sampler uses empty external textrue + EXPECT_EQ(DeviceMock::FakeEmptyExternalTextureDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[samplerHandle.asMemoryHandle()]); + } + else + { + // sampler uses fallback texture + EXPECT_EQ(DeviceMock::FakeTextureDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[samplerHandle.asMemoryHandle()]); + } + } + + void updateCacheAndExpectDeviceHandles(TextureSamplerHandle obSampler, TextureSamplerHandle sbSampler, TextureSamplerHandle ebSampler) + { + if (obSampler.isValid()) + EXPECT_CALL(sceneHelper.resourceManager, getOffscreenBufferColorBufferDeviceHandle(providerOffscreenBuffer)); + if (sbSampler.isValid()) + EXPECT_CALL(sceneHelper.resourceManager, getStreamBufferDeviceHandle(providerStreamBuffer)); + if(ebSampler.isValid()) + EXPECT_CALL(sceneHelper.resourceManager, getExternalBufferDeviceHandle(providerExternalBuffer)).WillOnce(Return(DeviceMock::FakeExternalTextureDeviceHandle)); + + updateConsumerSceneResourcesCache(); + + if (obSampler.isValid()) + { + EXPECT_EQ(DeviceMock::FakeRenderBufferDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[obSampler.asMemoryHandle()]); + } + if (sbSampler.isValid()) + { + EXPECT_EQ(DeviceMock::FakeRenderTargetDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[sbSampler.asMemoryHandle()]); + } + + if (ebSampler.isValid()) + { + EXPECT_EQ(DeviceMock::FakeExternalTextureDeviceHandle, consumerScene.getCachedHandlesForTextureSamplers()[ebSampler.asMemoryHandle()]); + } + } + + void expectOffscreenBufferLink(TextureSamplerHandle samplerHandle, bool withResourceCacheUpdate = true) + { + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); + EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); + EXPECT_TRUE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); + EXPECT_EQ(providerOffscreenBuffer, textureLinkManager.getLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); + EXPECT_EQ(TextureSampler::ContentType::OffscreenBuffer, consumerScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(providerOffscreenBuffer.asMemoryHandle(), consumerScene.getTextureSampler(samplerHandle).contentHandle); + + if (withResourceCacheUpdate) + updateCacheAndExpectDeviceHandles(samplerHandle, {}, {}); + } + + void expectStreamBufferLink(TextureSamplerHandle samplerHandle, bool withResourceCacheUpdate = true) + { + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); + EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); + EXPECT_TRUE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); + EXPECT_EQ(providerStreamBuffer, textureLinkManager.getLinkedStreamBuffer(consumerSceneId, samplerHandle)); + EXPECT_EQ(TextureSampler::ContentType::StreamBuffer, consumerScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(providerStreamBuffer.asMemoryHandle(), consumerScene.getTextureSampler(samplerHandle).contentHandle); + + if (withResourceCacheUpdate) + updateCacheAndExpectDeviceHandles({}, samplerHandle, {}); + } + + void expectExternalBufferLink(TextureSamplerHandle samplerHandle, bool withResourceCacheUpdate = true) + { + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, samplerHandle)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, samplerHandle)); + EXPECT_TRUE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerHandle)); + EXPECT_EQ(providerExternalBuffer, textureLinkManager.getLinkedExternalBuffer(consumerSceneId, samplerHandle)); + EXPECT_EQ(TextureSampler::ContentType::ExternalTexture, consumerScene.getTextureSampler(samplerHandle).contentType); + EXPECT_EQ(providerExternalBuffer.asMemoryHandle(), consumerScene.getTextureSampler(samplerHandle).contentHandle); + + if (withResourceCacheUpdate) + updateCacheAndExpectDeviceHandles({}, {}, samplerHandle); + } + + void expectRenderableDirtinessState(RenderableHandle handle, bool dirty) + { + consumerScene.updateRenderablesResourcesDirtiness(); + EXPECT_EQ(dirty, consumerScene.renderableResourcesDirty(handle)); + } + + void setFallbackTextureToConsumerSampler(TextureSamplerHandle samplerHandle, ResourceContentHash resource, RenderBufferHandle renderBufferHandle) + { + DataSlotId dataSlotId; + DataSlotHandle dataSlot; + for (DataSlotHandle d(0u); d < consumerScene.getDataSlotCount(); ++d) + { + if (consumerScene.isDataSlotAllocated(d) && consumerScene.getDataSlot(d).attachedTextureSampler == samplerHandle) + { + dataSlot = d; + dataSlotId = consumerScene.getDataSlot(d).id; + } + } + if (dataSlot.isValid()) + { + consumerScene.releaseDataSlot(dataSlot); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerDestroyed); + } + + if (resource.isValid()) + { + sceneHelper.recreateSamplerWithDifferentContent(samplerHandle, resource); + } + else if (renderBufferHandle.isValid()) + { + sceneHelper.recreateSamplerWithDifferentContent(samplerHandle, renderBufferHandle); + } + + if (dataSlot.isValid()) + { + consumerScene.allocateDataSlot({ EDataSlotType::TextureConsumer, dataSlotId, {}, {}, {}, samplerHandle }, dataSlot); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated); + } + } + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneLinksManager& sceneLinksManager; + const TextureLinkManager& textureLinkManager; + const SceneId providerSceneId{ 3u }; + const SceneId consumerSceneId{ 4u }; + IScene& providerScene; + ResourceCachedScene& consumerScene; + SceneAllocateHelper providerSceneAllocator; + SceneAllocateHelper consumerSceneAllocator; + + TestSceneHelper sceneHelper; + RenderableHandle renderable; + RenderableHandle renderableWithExternalTexture; + + const OffscreenBufferHandle providerOffscreenBuffer{ 333u }; + const StreamBufferHandle providerStreamBuffer{ 444u }; + const ExternalBufferHandle providerExternalBuffer{ 555u }; + const DataSlotHandle providerSlotHandle{ 55u }; + const DataSlotHandle providerExternalSlotHandle{ 56u }; + const DataSlotHandle consumerSlotHandle{ 66u }; + const DataSlotHandle consumerSlotHandle2{ 77u }; + const DataSlotHandle consumerSlotHandle3{ 88u }; + const DataSlotHandle consumerExternalSlotHandle{ 89u }; + const DataSlotId providerId{ 33u }; + const DataSlotId providerExternalId{ 34u }; + const DataSlotId consumerId{ 44u }; + const DataSlotId consumerId2{ 45u }; + const DataSlotId consumerId3{ 46u }; + const DataSlotId consumerExternalId{ 47u }; + + const TextureSamplerHandle sampler{ 3u }; + const TextureSamplerHandle sampler2{ 5u }; + const TextureSamplerHandle sampler3{ 7u }; + const TextureSamplerHandle samplerExternal{ 71u }; + const ResourceContentHash providerTextureHash{ MockResourceHash::TextureHash2 }; + const ResourceContentHash consumerTextureHash{ MockResourceHash::TextureHash }; + }; + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerInitially) + { + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinked) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + expectTextureLink(providerTextureHash); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndUnlinked) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSceneRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + rendererScenes.destroyScene(providerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + rendererScenes.destroyScene(consumerSceneId); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSlotRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + providerScene.releaseDataSlot(providerSlotHandle); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSceneUnmapped) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + sceneLinksManager.handleSceneUnmapped(providerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneUnmapped) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + + sceneLinksManager.handleSceneUnmapped(consumerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinked) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenUnlinked) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenConsumerSceneRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.handleSceneRemoved(consumerSceneId); + expectRenderableDirtinessState(renderable, true); + expectNoTextureLink(); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenConsumerSceneUnmapped) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.handleSceneUnmapped(consumerSceneId); + expectRenderableDirtinessState(renderable, true); + expectNoTextureLink(); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenProviderSceneRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.handleSceneRemoved(providerSceneId); + expectRenderableDirtinessState(renderable, true); + expectNoTextureLink(); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenProviderSceneUnmapped) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.handleSceneUnmapped(providerSceneId); + expectRenderableDirtinessState(renderable, true); + expectNoTextureLink(); + } + + TEST_F(ATextureLinkManager, doesNotMarkRenderableUsingSamplerDirtyWhenUnlinkedWithoutBeingLinked) + { + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, consumerSceneId, consumerId, SceneId::Invalid()); + expectRenderableDirtinessState(renderable, false); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinkedAndProviderSceneRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + rendererScenes.destroyScene(providerSceneId); + expectRenderableDirtinessState(renderable, true); + expectNoTextureLink(); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinkedAndProviderSlotRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + providerScene.releaseDataSlot(providerSlotHandle); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenLinkedAndConsumerSlotRemoved) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectRenderableDirtinessState(renderable, true); + } + + // buffer links tests + TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinkedBuffer_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + expectOffscreenBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinkedBuffer_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + expectStreamBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkForSamplerWhenLinkedBuffer_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); + expectExternalBufferLink(samplerExternal); + EXPECT_FALSE(consumerScene.renderableResourcesDirty(renderableWithExternalTexture)); + } + + TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToUnknownConsumerSlot_OB) + { + constexpr DataSlotId unknownDataId{ 131313u }; + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, unknownDataId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerOffscreenBuffer, consumerSceneId, unknownDataId); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToUnknownConsumerSlot_SB) + { + constexpr DataSlotId unknownDataId{ 131313u }; + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, unknownDataId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerStreamBuffer, consumerSceneId, unknownDataId); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToUnknownConsumerSlot_EB) + { + constexpr DataSlotId unknownDataId{ 131313u }; + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, unknownDataId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerExternalBuffer, consumerSceneId, unknownDataId); + expectNoBufferLink(samplerExternal); + } + + TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToConsumerSlotWithWrongType_OB) + { + // attempt to link to a provider type slot + constexpr DataSlotId providerDataId{ 131313u }; + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureProvider, providerDataId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, consumerSceneId, providerDataId, SceneId(0u), DataSlotId(0u)); + + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, providerDataId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerOffscreenBuffer, consumerSceneId, providerDataId); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToConsumerSlotWithWrongType_SB) + { + // attempt to link to a provider type slot + constexpr DataSlotId providerDataId{ 131313u }; + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureProvider, providerDataId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, consumerSceneId, providerDataId, SceneId(0u), DataSlotId(0u)); + + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, providerDataId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerStreamBuffer, consumerSceneId, providerDataId); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsLinkFailedWhenLinkingToConsumerSlotWithWrongType_EB) + { + // attempt to link to a provider type slot + constexpr DataSlotId providerDataId{ 131313u }; + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TextureProvider, providerDataId, NodeHandle(), DataInstanceHandle::Invalid(), providerTextureHash, TextureSamplerHandle() }); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, consumerSceneId, providerDataId, SceneId(0u), DataSlotId(0u)); + + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, providerDataId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinkFailed, providerExternalBuffer, consumerSceneId, providerDataId); + expectNoBufferLink(samplerExternal); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndUnlinked_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, SceneId::Invalid()); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndUnlinked_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, SceneId::Invalid()); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndUnlinked_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerExternalId, SceneId::Invalid()); + expectNoBufferLink(samplerExternal); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneRemoved_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + + rendererScenes.destroyScene(consumerSceneId); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneRemoved_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + + rendererScenes.destroyScene(consumerSceneId); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneRemoved_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); + + rendererScenes.destroyScene(consumerSceneId); + EXPECT_FALSE(textureLinkManager.hasLinkedExternalBuffer(consumerSceneId, samplerExternal)); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSlotRemoved_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + expectOffscreenBufferLink(sampler); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSlotRemoved_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + expectStreamBufferLink(sampler); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSlotRemoved_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); + expectExternalBufferLink(samplerExternal); + + consumerScene.releaseDataSlot(consumerExternalSlotHandle); + expectNoBufferLink(samplerExternal); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneUnmapped_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + + sceneLinksManager.handleSceneUnmapped(consumerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneUnmapped_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + + sceneLinksManager.handleSceneUnmapped(consumerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndConsumerSceneUnmapped_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); + + sceneLinksManager.handleSceneUnmapped(consumerSceneId); + expectNoBufferLink(samplerExternal); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndBufferRemoved_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + + sceneLinksManager.handleBufferDestroyed(providerOffscreenBuffer); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndBufferRemoved_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + + sceneLinksManager.handleBufferDestroyedOrSourceUnavailable(providerStreamBuffer); + expectNoTextureLink(); + expectNoBufferLink(sampler); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenBufferLinkedAndBufferRemoved_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerExternalBuffer, consumerSceneId, consumerExternalId); + + sceneLinksManager.handleBufferDestroyed(providerExternalBuffer); + expectNoBufferLink(samplerExternal); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinked_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + consumerScene.updateRenderablesResourcesDirtiness(); + EXPECT_TRUE(consumerScene.renderableResourcesDirty(renderable)); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinked_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + consumerScene.updateRenderablesResourcesDirtiness(); + EXPECT_TRUE(consumerScene.renderableResourcesDirty(renderable)); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinked_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + consumerScene.updateRenderablesResourcesDirtiness(); + EXPECT_TRUE(consumerScene.renderableResourcesDirty(renderableWithExternalTexture)); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferUnlinked_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferUnlinked_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferUnlinked_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerExternalId); + expectRenderableDirtinessState(renderable, false); + } + + TEST_F(ATextureLinkManager, doesNotMarkRenderableUsingSamplerDirtyWhenUnlinkedWithoutBeingBufferLinked) + { + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinkFailed, consumerSceneId, consumerId, SceneId::Invalid()); + expectRenderableDirtinessState(renderable, false); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndConsumerSlotRemoved_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndConsumerSlotRemoved_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndConsumerSlotRemoved_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + updateConsumerSceneResourcesCache(); + //expectRenderableDirtinessState(renderableWithExternalTexture, false); + + consumerScene.releaseDataSlot(consumerExternalSlotHandle); + expectRenderableDirtinessState(renderableWithExternalTexture, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndBufferRemoved_OB) + { + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.handleBufferDestroyed(providerOffscreenBuffer); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndBufferRemoved_SB) + { + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + sceneLinksManager.handleBufferDestroyedOrSourceUnavailable(providerStreamBuffer); + expectRenderableDirtinessState(renderable, true); + } + + TEST_F(ATextureLinkManager, marksRenderableUsingSamplerDirtyWhenBufferLinkedAndBufferRemoved_EB) + { + sceneLinksManager.createBufferLink(providerExternalBuffer, consumerSceneId, consumerExternalId); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderableWithExternalTexture, false); + + sceneLinksManager.handleBufferDestroyed(providerExternalBuffer); + expectRenderableDirtinessState(renderableWithExternalTexture, true); + } + + TEST_F(ATextureLinkManager, reportsLinkAndMarksRenderableUsingSamplerDirtyWhenProviderChangesTextureAssignedToSlot) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectTextureLink(providerTextureHash); + updateConsumerSceneResourcesCache(); + expectRenderableDirtinessState(renderable, false); + + const ResourceContentHash newTexture(123u, 456u); + providerScene.setDataSlotTexture(providerSlotHandle, newTexture); + expectTextureLink(newTexture); + expectRenderableDirtinessState(renderable, true); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectNoTextureLink(); + } + + // combined buffer and texture links tests + TEST_F(ATextureLinkManager, canCreateTextureLinkAndBufferLink) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + expectTextureLink(providerTextureHash); + + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + expectOffscreenBufferLink(sampler2); + + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + expectStreamBufferLink(sampler3); + } + + TEST_F(ATextureLinkManager, differentTypesOfLinksOverwriteEachOther) + { + // OB + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + expectOffscreenBufferLink(sampler); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + + // OB -> tex + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + expectTextureLink(providerTextureHash); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + + // tex -> OB + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + expectOffscreenBufferLink(sampler); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + + // OB -> SB + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + expectStreamBufferLink(sampler); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + + // SB -> tex + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + expectTextureLink(providerTextureHash); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + + // tex -> SB + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId); + expectStreamBufferLink(sampler); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler)); + + // SB -> OB + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId); + expectOffscreenBufferLink(sampler); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler)); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSceneRemoved_KeepsBufferLink) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + rendererScenes.destroyScene(providerSceneId); + expectNoTextureLink(); + expectOffscreenBufferLink(sampler2, false); + expectStreamBufferLink(sampler3, false); + updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndBufferRemoved_KeepsTextureLink_OB) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + + sceneLinksManager.handleBufferDestroyed(providerOffscreenBuffer); + expectTextureLink(providerTextureHash); + expectNoBufferLink(sampler2); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndBufferRemoved_KeepsTextureLink_SB) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId2); + + sceneLinksManager.handleBufferDestroyedOrSourceUnavailable(providerStreamBuffer); + expectTextureLink(providerTextureHash); + expectNoBufferLink(sampler2); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneRemoved_BufferAndTextureLinks) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + rendererScenes.destroyScene(consumerSceneId); + EXPECT_FALSE(textureLinkManager.hasLinkedTexture(consumerSceneId, sampler)); + EXPECT_FALSE(textureLinkManager.hasLinkedOffscreenBuffer(consumerSceneId, sampler2)); + EXPECT_FALSE(textureLinkManager.hasLinkedStreamBuffer(consumerSceneId, sampler3)); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndProviderSlotRemoved_KeepsBufferLink) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + providerScene.releaseDataSlot(providerSlotHandle); + expectNoTextureLink(); + expectOffscreenBufferLink(sampler2, false); + expectStreamBufferLink(sampler3, false); + updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved_KeepsBufferLink) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + consumerScene.releaseDataSlot(consumerSlotHandle); + expectNoTextureLink(); + expectOffscreenBufferLink(sampler2, false); + expectStreamBufferLink(sampler3, false); + updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved_KeepsTextureLink_OB) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + + consumerScene.releaseDataSlot(consumerSlotHandle2); + expectTextureLink(providerTextureHash); + expectNoBufferLink(sampler2); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSlotRemoved_KeepsTextureLink_SB) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId2); + + consumerScene.releaseDataSlot(consumerSlotHandle2); + expectTextureLink(providerTextureHash); + expectNoBufferLink(sampler2); + } + + TEST_F(ATextureLinkManager, reportsNoLinkForSamplerWhenLinkedAndConsumerSceneUnmapped_BufferAndTextureLinks) + { + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + sceneLinksManager.handleSceneUnmapped(consumerSceneId); + expectNoTextureLink(); + expectNoBufferLink(sampler2); + expectNoBufferLink(sampler3); + } + + TEST_F(ATextureLinkManager, canCreateTextureLinkAndBufferLinkIfPreviouslyUsingRenderTarget) + { + const RenderBufferHandle renderBuffer(14u); + setFallbackTextureToConsumerSampler(sampler, {}, renderBuffer); + setFallbackTextureToConsumerSampler(sampler2, {}, renderBuffer); + setFallbackTextureToConsumerSampler(sampler3, {}, renderBuffer); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + expectTextureLink(providerTextureHash); + expectOffscreenBufferLink(sampler2, false); + expectStreamBufferLink(sampler3, false); + updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); + } + + TEST_F(ATextureLinkManager, unlinkingTextureAndBufferFallsBackToPreviouslySetRenderTarget) + { + const RenderBufferHandle renderBuffer(14u); + setFallbackTextureToConsumerSampler(sampler, {}, renderBuffer); + setFallbackTextureToConsumerSampler(sampler2, {}, renderBuffer); + setFallbackTextureToConsumerSampler(sampler3, {}, renderBuffer); + + sceneLinksManager.createDataLink(providerSceneId, providerId, consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataLinked, providerSceneId, providerId, consumerSceneId, consumerId); + sceneLinksManager.createBufferLink(providerOffscreenBuffer, consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerOffscreenBuffer, consumerSceneId, consumerId2); + sceneLinksManager.createBufferLink(providerStreamBuffer, consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataBufferLinked, providerStreamBuffer, consumerSceneId, consumerId3); + + expectTextureLink(providerTextureHash); + expectOffscreenBufferLink(sampler2, false); + expectStreamBufferLink(sampler3, false); + updateCacheAndExpectDeviceHandles(sampler2, sampler3, {}); + + sceneLinksManager.removeDataLink(consumerSceneId, consumerId); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId, providerSceneId); + sceneLinksManager.removeDataLink(consumerSceneId, consumerId2); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId2, SceneId::Invalid()); + sceneLinksManager.removeDataLink(consumerSceneId, consumerId3); + expectRendererEvent(ERendererEventType::SceneDataUnlinked, consumerSceneId, consumerId3, SceneId::Invalid()); + + EXPECT_EQ(TextureSampler::ContentType::RenderBuffer, consumerScene.getTextureSampler(sampler).contentType); + EXPECT_EQ(TextureSampler::ContentType::RenderBuffer, consumerScene.getTextureSampler(sampler2).contentType); + EXPECT_EQ(TextureSampler::ContentType::RenderBuffer, consumerScene.getTextureSampler(sampler3).contentType); + EXPECT_EQ(renderBuffer.asMemoryHandle(), consumerScene.getTextureSampler(sampler).contentHandle); + EXPECT_EQ(renderBuffer.asMemoryHandle(), consumerScene.getTextureSampler(sampler2).contentHandle); + EXPECT_EQ(renderBuffer.asMemoryHandle(), consumerScene.getTextureSampler(sampler3).contentHandle); + } +} diff --git a/renderer/RendererLib/RendererLib/test/TransformationLinkCachedSceneTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkCachedSceneTest.cpp similarity index 87% rename from renderer/RendererLib/RendererLib/test/TransformationLinkCachedSceneTest.cpp rename to tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkCachedSceneTest.cpp index 0ae0d2ab8..0317b660a 100644 --- a/renderer/RendererLib/RendererLib/test/TransformationLinkCachedSceneTest.cpp +++ b/tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkCachedSceneTest.cpp @@ -6,20 +6,19 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "framework_common_gmock_header.h" -#include "RendererLib/TransformationLinkCachedScene.h" -#include "RendererLib/SceneLinksManager.h" -#include "RendererLib/RendererScenes.h" -#include "RendererEventCollector.h" +#include "internal/RendererLib/TransformationLinkCachedScene.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" #include "TestEqualHelper.h" #include "SceneAllocateHelper.h" -#include "Utils/ThreadLocalLog.h" -#include "Math3d/Rotation.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "internal/Core/Math3d/Rotation.h" #include "glm/gtx/transform.hpp" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class ATransformationLinkCachedScene : public testing::Test { @@ -130,13 +129,13 @@ namespace ramses_internal setAllTranslations(); // create provider/consumer slots - ramses_internal::DataSlotHandle slotHandle; - slotHandle = providerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationProvider, providerSceneProviderId, providerSceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, providerSceneProviderSlotHandle); - slotHandle = consumer1SceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumer1SceneConsumerId, consumer1SceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumer1SceneConsumerSlotHandle); - slotHandle = consumer1SceneAllocator.allocateDataSlot({ EDataSlotType_TransformationProvider, consumer1SceneProviderId, consumer1SceneNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); - slotHandle = consumer2SceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumer2SceneConsumerId, consumer2SceneRoot, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); - slotHandle = consumer2ndLevelSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumer2ndLevelSceneConsumerId, consumer2ndLevelSceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); - slotHandle = consumerWithoutTransformsSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, consumerWithoutTransformsSceneConsumerId, consumerWithoutTransformsRoot, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumerWithoutTransformsSceneConsumerSlotHandle); + DataSlotHandle slotHandle; + slotHandle = providerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationProvider, providerSceneProviderId, providerSceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, providerSceneProviderSlotHandle); + slotHandle = consumer1SceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumer1SceneConsumerId, consumer1SceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumer1SceneConsumerSlotHandle); + slotHandle = consumer1SceneAllocator.allocateDataSlot({ EDataSlotType::TransformationProvider, consumer1SceneProviderId, consumer1SceneNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); + slotHandle = consumer2SceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumer2SceneConsumerId, consumer2SceneRoot, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); + slotHandle = consumer2ndLevelSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumer2ndLevelSceneConsumerId, consumer2ndLevelSceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }); + slotHandle = consumerWithoutTransformsSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerWithoutTransformsSceneConsumerId, consumerWithoutTransformsRoot, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumerWithoutTransformsSceneConsumerSlotHandle); } void setAllTranslations(bool reset = false) @@ -188,12 +187,12 @@ namespace ramses_internal sceneLinksManager.createDataLink(providerScene.getSceneId(), providerSceneProviderId, consumerWithoutTransformsScene.getSceneId(), consumerWithoutTransformsSceneConsumerId); } - void expectIdentityMatrix(const NodeHandle nodeToCheck, const TransformationLinkCachedScene& scene) const + static void ExpectIdentityMatrix(const NodeHandle nodeToCheck, const TransformationLinkCachedScene& scene) { - expectCorrectMatrix(nodeToCheck, glm::identity(), scene); + ExpectCorrectMatrix(nodeToCheck, glm::identity(), scene); } - void expectCorrectMatrix(const NodeHandle nodeToCheck, const glm::mat4& worldMatrix, const TransformationLinkCachedScene& scene) const + static void ExpectCorrectMatrix(const NodeHandle nodeToCheck, const glm::mat4& worldMatrix, const TransformationLinkCachedScene& scene) { expectMatrixFloatEqual(worldMatrix, scene.updateMatrixCacheWithLinks(ETransformationMatrixType_World, nodeToCheck)); expectMatrixFloatEqual(glm::inverse(worldMatrix), scene.updateMatrixCacheWithLinks(ETransformationMatrixType_Object, nodeToCheck)); @@ -281,28 +280,28 @@ namespace ramses_internal { linkConsumer1SceneToProviderScene(); const glm::vec3 expectedTranslation = providerSceneRootTranslation + providerSceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, resolvesSingleDataDependencyForChildNodeOfConsumer) { linkConsumer1SceneToProviderScene(); const glm::vec3 expectedTranslation = providerSceneRootTranslation + providerSceneNodeTranslation + consumer1SceneNode2Translation; - expectCorrectMatrix(consumer1SceneNode2, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode2, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, resolvesSingleDataDependencyWithLinkToSceneWithRoot) { linkConsumer2SceneToProviderScene(); const glm::vec3 expectedTranslation = providerSceneRootTranslation + providerSceneNodeTranslation; - expectCorrectMatrix(consumer2SceneRoot, glm::translate(expectedTranslation), consumer2Scene); + ExpectCorrectMatrix(consumer2SceneRoot, glm::translate(expectedTranslation), consumer2Scene); } TEST_F(ATransformationLinkCachedScene, doesNotAffectSceneWhenLinkingItsChildAsConsumer) { linkConsumer1SceneToProviderScene(); const glm::vec3 expectedTranslation = consumer1SceneRootTranslation; - expectCorrectMatrix(consumer1SceneRootNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneRootNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, doesNotAffectSceneWithConsumerWhenLinkingItsChildAsProvider) @@ -310,7 +309,7 @@ namespace ramses_internal linkConsumer1SceneToProviderScene(); linkConsumer2ndLevelSceneToConsumer1Scene(); const glm::vec3 expectedTranslation = providerSceneRootTranslation + providerSceneNodeTranslation + consumer1SceneNode2Translation; - expectCorrectMatrix(consumer2ndLevelSceneNode, glm::translate(expectedTranslation), consumer2ndLevelScene); + ExpectCorrectMatrix(consumer2ndLevelSceneNode, glm::translate(expectedTranslation), consumer2ndLevelScene); } TEST_F(ATransformationLinkCachedScene, resolvesTransformationAcrossTwoLinks) @@ -319,26 +318,26 @@ namespace ramses_internal linkConsumer2ndLevelSceneToConsumer1Scene(); const glm::vec3 expectedTranslation1 = providerSceneRootTranslation + providerSceneNodeTranslation; const glm::vec3 expectedTranslation2 = providerSceneRootTranslation + providerSceneNodeTranslation + consumer1SceneNode2Translation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation1), consumer1Scene); - expectCorrectMatrix(consumer1SceneNode2, glm::translate(expectedTranslation2), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation1), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode2, glm::translate(expectedTranslation2), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, resolvesTransformationAndPropagatesToChild) { linkConsumer2SceneToProviderScene(); const glm::vec3 expectedTranslation = providerSceneRootTranslation + providerSceneNodeTranslation + consumer2SceneNodeTranslation; - expectCorrectMatrix(consumer2SceneNode, glm::translate(expectedTranslation), consumer2Scene); + ExpectCorrectMatrix(consumer2SceneNode, glm::translate(expectedTranslation), consumer2Scene); } TEST_F(ATransformationLinkCachedScene, resolvesTransformationAndPropagatesToSceneWithNoTransforms) { linkConsumerWithoutTransformsSceneToProviderScene(); const glm::vec3 expectedTranslation1 = providerSceneRootTranslation + providerSceneNodeTranslation; - expectCorrectMatrix(consumerWithoutTransformsNode1, glm::translate(expectedTranslation1), consumerWithoutTransformsScene); + ExpectCorrectMatrix(consumerWithoutTransformsNode1, glm::translate(expectedTranslation1), consumerWithoutTransformsScene); const glm::vec3 expectedTranslation2 = providerSceneRootTranslation + providerSceneNodeTranslation + consumerWithoutTransformsTransform2Translation; - expectCorrectMatrix(consumerWithoutTransformsNode2, glm::translate(expectedTranslation2), consumerWithoutTransformsScene); - expectCorrectMatrix(consumerWithoutTransformsNode3, glm::translate(expectedTranslation2), consumerWithoutTransformsScene); + ExpectCorrectMatrix(consumerWithoutTransformsNode2, glm::translate(expectedTranslation2), consumerWithoutTransformsScene); + ExpectCorrectMatrix(consumerWithoutTransformsNode3, glm::translate(expectedTranslation2), consumerWithoutTransformsScene); } TEST_F(ATransformationLinkCachedScene, resolvesTransformationAndPropagatesToSceneWithDifferentRotationConventions) @@ -351,7 +350,7 @@ namespace ramses_internal providerScene.setRotation(providerSceneTransform, { 10.f, 0.f, 0.f, 1.f }, ERotationType::Euler_XYZ); //only X-Axis consumer1Scene.setRotation(consumer1SceneTransform2, { 0.f, 10.f, 0.f, 1.f }, ERotationType::Euler_ZYX); //only Y-Axis - expectCorrectMatrix(consumer1SceneNode2, Math3d::Rotation({ 10.f, 10.f, 0.f, 1.f }, ERotationType::Euler_ZYX), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode2, Math3d::Rotation({ 10.f, 10.f, 0.f, 1.f }, ERotationType::Euler_ZYX), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, providerOverridesEulerWithQuaternion) @@ -365,10 +364,10 @@ namespace ramses_internal providerScene.setRotation(providerSceneTransform, rProvider, ERotationType::Quaternion); linkConsumer1SceneToProviderScene(); - expectCorrectMatrix(consumer1SceneNode2, Math3d::Rotation(rProvider, ERotationType::Quaternion), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode2, Math3d::Rotation(rProvider, ERotationType::Quaternion), consumer1Scene); sceneLinksManager.removeDataLink(consumer1Scene.getSceneId(), consumer1SceneConsumerId); - expectCorrectMatrix(consumer1SceneNode2, Math3d::Rotation(rConsumer, ERotationType::Euler_XZY), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode2, Math3d::Rotation(rConsumer, ERotationType::Euler_XZY), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, fallsBackToOriginalTransformationWhenProviderSlotIsRemoved) @@ -376,7 +375,7 @@ namespace ramses_internal linkConsumer1SceneToProviderScene(); providerScene.releaseDataSlot(providerSceneProviderSlotHandle); const glm::vec3 expectedTranslation = consumer1SceneRootTranslation + consumer1SceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, fallsBackToOriginalTransformationWhenConsumerSlotIsRemoved) @@ -384,7 +383,7 @@ namespace ramses_internal linkConsumer1SceneToProviderScene(); consumer1Scene.releaseDataSlot(consumer1SceneConsumerSlotHandle); const glm::vec3 expectedTranslation = consumer1SceneRootTranslation + consumer1SceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, recomputesDirtyTransformationWhenProviderSlotIsRemoved) @@ -396,7 +395,7 @@ namespace ramses_internal providerScene.releaseDataSlot(providerSceneProviderSlotHandle); const glm::vec3 expectedTranslation = consumer1SceneRootTranslation + consumer1SceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, recomputesDirtyTransformationWhenConsumerSlotIsRemoved) @@ -408,7 +407,7 @@ namespace ramses_internal consumer1Scene.releaseDataSlot(consumer1SceneConsumerSlotHandle); const glm::vec3 expectedTranslation = consumer1SceneRootTranslation + consumer1SceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, recomputesDirtyTransformationWhenProviderTransformationChanges) @@ -421,7 +420,7 @@ namespace ramses_internal providerScene.setTranslation(providerSceneRootTransform, newRootTranslation); const glm::vec3 expectedTranslation = newRootTranslation + providerSceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, recomputesDirtyTransformationWhenProviderRotationConventionChanges) @@ -435,11 +434,11 @@ namespace ramses_internal const glm::vec4 rotation{ 10.f, 20.f, 30.f, 1.f }; providerScene.setRotation(providerSceneTransform, rotation, ERotationType::Euler_XYZ); - expectCorrectMatrix(consumer1SceneNode, Math3d::Rotation(rotation, ERotationType::Euler_XYZ), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, Math3d::Rotation(rotation, ERotationType::Euler_XYZ), consumer1Scene); //change rotationType providerScene.setRotation(providerSceneTransform, rotation, ERotationType::Euler_ZYX); - expectCorrectMatrix(consumer1SceneNode, Math3d::Rotation(rotation, ERotationType::Euler_ZYX), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, Math3d::Rotation(rotation, ERotationType::Euler_ZYX), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, recomputesDirtyTransformationWhenProviderSceneRemoved) @@ -451,7 +450,7 @@ namespace ramses_internal rendererScenes.destroyScene(providerScene.getSceneId()); const glm::vec3 expectedTranslation = consumer1SceneRootTranslation + consumer1SceneNodeTranslation; - expectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); + ExpectCorrectMatrix(consumer1SceneNode, glm::translate(expectedTranslation), consumer1Scene); } TEST_F(ATransformationLinkCachedScene, canreleaseDataSlotOnUnlinkedProvider) @@ -472,7 +471,7 @@ namespace ramses_internal const DataSlotId dataSlotId(123u); const DataSlotHandle slotHandle(345u); - providerSceneAllocator.allocateDataSlot({ EDataSlotType_TransformationProvider, dataSlotId, providerSceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + providerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationProvider, dataSlotId, providerSceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); const RendererEventVector events = consumeSceneControlEvents(); ASSERT_EQ(1u, events.size()); @@ -500,7 +499,7 @@ namespace ramses_internal const DataSlotId dataSlotId(123u); const DataSlotHandle slotHandle(345u); - consumer1SceneAllocator.allocateDataSlot({ EDataSlotType_TransformationConsumer, dataSlotId, consumer1SceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + consumer1SceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, dataSlotId, consumer1SceneNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); const RendererEventVector events = consumeSceneControlEvents(); ASSERT_EQ(1u, events.size()); diff --git a/tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkManagerTest.cpp b/tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkManagerTest.cpp new file mode 100644 index 000000000..1275b4c31 --- /dev/null +++ b/tests/unittests/renderer/renderer-lib/RendererLib/TransformationLinkManagerTest.cpp @@ -0,0 +1,365 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2016 BMW Car IT GmbH +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#include "gtest/gtest.h" +#include "internal/RendererLib/SceneLinksManager.h" +#include "internal/RendererLib/TransformationLinkCachedScene.h" +#include "internal/RendererLib/RendererScenes.h" +#include "internal/RendererLib/RendererEventCollector.h" +#include "SceneAllocateHelper.h" +#include "internal/Core/Utils/ThreadLocalLog.h" +#include "TestEqualHelper.h" +#include "glm/gtx/transform.hpp" + +using namespace testing; + +namespace ramses::internal +{ + class ATransformationLinkManager : public ::testing::Test + { + public: + ATransformationLinkManager() + : rendererScenes(rendererEventCollector) + , sceneLinksManager(rendererScenes.getSceneLinksManager()) + , transformationLinkManager(const_cast(sceneLinksManager.getTransformationLinkManager())) + , providerSceneId(3u) + , consumerSceneId(4u) + , providerScene(rendererScenes.createScene(SceneInfo(providerSceneId))) + , consumerScene(rendererScenes.createScene(SceneInfo(consumerSceneId))) + , providerSceneAllocator(providerScene) + , consumerSceneAllocator(consumerScene) + , providerNode(8u) + , consumerNode(12u) + , providerTransform(9u) + , providerSlotHandle(55u) + , consumerSlotHandle(66u) + , providerId(33u) + , consumerId(44u) + { + // caller is expected to have a display prefix for logs + ThreadLocalLog::SetPrefix(1); + + providerSceneAllocator.allocateNode(0u, providerNode); + consumerSceneAllocator.allocateNode(0u, consumerNode); + + providerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationProvider, providerId, providerNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, providerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId, SceneId(0u), DataSlotId(0u)); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerId, consumerNode, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, consumerSlotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId); + } + + protected: + void expectRendererEvent(ERendererEventType event, SceneId providerSId, DataSlotId pId, SceneId consumerSId, DataSlotId cId) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(event, events.front().eventType); + EXPECT_EQ(providerSId, events.front().providerSceneId); + EXPECT_EQ(consumerSId, events.front().consumerSceneId); + EXPECT_EQ(pId, events.front().providerdataId); + EXPECT_EQ(cId, events.front().consumerdataId); + } + + void expectRendererEvent(ERendererEventType event, SceneId consumerSId, DataSlotId cId) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + ASSERT_EQ(1u, events.size()); + EXPECT_EQ(event, events.front().eventType); + EXPECT_EQ(consumerSId, events.front().consumerSceneId); + EXPECT_EQ(cId, events.front().consumerdataId); + } + + void expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType destroyedSlotType, SceneId sceneIdOfDestroyedSlot, DataSlotId destroyedSlotId, SceneId consumerSId, DataSlotId cId, SceneId consumerSceneId2 = SceneId(0u), DataSlotId consumerId2 = DataSlotId(0u)) + { + RendererEventVector events; + RendererEventVector dummy; + rendererEventCollector.appendAndConsumePendingEvents(dummy, events); + + const bool hasTwoLinksRemoved = (consumerId2.getValue() != 0u); + if (hasTwoLinksRemoved) + { + ASSERT_EQ(3u, events.size()); + } + else + { + ASSERT_EQ(2u, events.size()); + } + + uint32_t eventIdx = 0u; + + EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); + EXPECT_EQ(consumerSId, events[eventIdx].consumerSceneId); + EXPECT_EQ(cId, events[eventIdx].consumerdataId); + + if (hasTwoLinksRemoved) + { + ++eventIdx; + EXPECT_EQ(ERendererEventType::SceneDataUnlinkedAsResultOfClientSceneChange, events[eventIdx].eventType); + EXPECT_EQ(consumerSceneId2, events[eventIdx].consumerSceneId); + EXPECT_EQ(consumerId2, events[eventIdx].consumerdataId); + } + + ++eventIdx; + if (destroyedSlotType == EDataSlotType::TransformationProvider) + { + EXPECT_EQ(ERendererEventType::SceneDataSlotProviderDestroyed, events[eventIdx].eventType); + EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].providerSceneId); + EXPECT_EQ(destroyedSlotId, events[eventIdx].providerdataId); + } + else + { + EXPECT_EQ(ERendererEventType::SceneDataSlotConsumerDestroyed, events[eventIdx].eventType); + EXPECT_EQ(sceneIdOfDestroyedSlot, events[eventIdx].consumerSceneId); + EXPECT_EQ(destroyedSlotId, events[eventIdx].consumerdataId); + } + } + + RendererEventCollector rendererEventCollector; + RendererScenes rendererScenes; + SceneLinksManager& sceneLinksManager; + TransformationLinkManager& transformationLinkManager; + + const SceneId providerSceneId; + const SceneId consumerSceneId; + TransformationLinkCachedScene& providerScene; + TransformationLinkCachedScene& consumerScene; + SceneAllocateHelper providerSceneAllocator; + SceneAllocateHelper consumerSceneAllocator; + + const NodeHandle providerNode; + const NodeHandle consumerNode; + const TransformHandle providerTransform; + + const DataSlotHandle providerSlotHandle; + const DataSlotHandle consumerSlotHandle; + const DataSlotId providerId; + const DataSlotId consumerId; + }; + + TEST_F(ATransformationLinkManager, reportsNoLinkForInvalidNode) + { + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(providerSceneId, NodeHandle())); + } + + TEST_F(ATransformationLinkManager, reportsNoLinkForNonlinkedNode) + { + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(providerSceneId, providerNode)); + } + + TEST_F(ATransformationLinkManager, reportsExistingLinkForNodeAssociatedWithLink) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, reportsNoLinkToProviderForProviderNode) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(providerSceneId, providerNode)); + } + + TEST_F(ATransformationLinkManager, retrievesWorldTransformationFromLinkedProviderNode) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + providerSceneAllocator.allocateTransform(providerNode, providerTransform); + const glm::vec3 transVec(1.f, 2.f, 3.f); + providerScene.setTranslation(providerTransform, transVec); + + const auto expectedMat = glm::translate(transVec); + const auto transMat = transformationLinkManager.getLinkedTransformationFromDataProvider(ETransformationMatrixType_World, consumerSceneId, consumerNode); + + expectMatrixFloatEqual(expectedMat, transMat); + } + + TEST_F(ATransformationLinkManager, retrievesObjectTransformationFromLinkedProviderNode) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + providerSceneAllocator.allocateTransform(providerNode, providerTransform); + const glm::vec3 transVec(1.f, 2.f, 3.f); + providerScene.setTranslation(providerTransform, transVec); + + const auto expectedMat = glm::translate(-transVec); + const auto transMat = transformationLinkManager.getLinkedTransformationFromDataProvider(ETransformationMatrixType_Object, consumerSceneId, consumerNode); + + expectMatrixFloatEqual(expectedMat, transMat); + } + + TEST_F(ATransformationLinkManager, reportsNoLinksForSceneWithRemovedLink) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle)); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, reportsNoLinksForConsumerSceneIfDestroyed) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + rendererScenes.destroyScene(consumerSceneId); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, reportsNoLinksForConsumerSceneIfProviderSceneDestroyed) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + rendererScenes.destroyScene(providerSceneId); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, canLinkUnlinkAndLinkAgain) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + + EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, removesLinkForSceneWithRemovedProviderSlot) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + providerScene.releaseDataSlot(providerSlotHandle); + expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType::TransformationProvider, providerSceneId, providerId, consumerSceneId, consumerId); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, removesLinkForSceneWithRemovedConsumerSlot) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + consumerScene.releaseDataSlot(consumerSlotHandle); + expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType::TransformationConsumer, consumerSceneId, consumerId, consumerSceneId, consumerId); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + } + + TEST_F(ATransformationLinkManager, createsTwoLinksSameProviderDifferentConsumers) + { + const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle(43u); + + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, slotHandle)); + EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode2)); + } + + TEST_F(ATransformationLinkManager, removesAllLinksToProviderOnProviderSlotDestruction) + { + const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); + const DataSlotId consumerId2(999u); + const DataSlotHandle slotHandle(43u); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, slotHandle)); + + providerScene.releaseDataSlot(providerSlotHandle); + expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType::TransformationProvider, providerSceneId, providerId, consumerSceneId, consumerId, consumerSceneId, consumerId2); + + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode2)); + } + + TEST_F(ATransformationLinkManager, removingAllLinksToProviderOnProviderSlotDestructionDoesNotAffectOtherLinks) + { + const NodeHandle providerNode2 = providerSceneAllocator.allocateNode(); + const DataSlotId providerId2(999u); + const DataSlotHandle slotHandle(43u); + providerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationProvider, providerId2, providerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle); + expectRendererEvent(ERendererEventType::SceneDataSlotProviderCreated, providerSceneId, providerId2, SceneId(0u), DataSlotId(0u)); + + const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); + const DataSlotId consumerId2(888u); + const DataSlotHandle slotHandle2(654u); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle2); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, slotHandle, consumerSceneId, slotHandle2)); + + providerScene.releaseDataSlot(providerSlotHandle); + expectRendererEventUnlinkedAndDestroyedSlot(EDataSlotType::TransformationProvider, providerSceneId, providerId, consumerSceneId, consumerId); + + EXPECT_FALSE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode)); + EXPECT_TRUE(transformationLinkManager.nodeHasDataLinkToProvider(consumerSceneId, consumerNode2)); + } + + void markNodeTransformationClean(const TransformationLinkCachedScene& scene, NodeHandle node) + { + scene.updateMatrixCache(ETransformationMatrixType_World, node); + EXPECT_FALSE(scene.isMatrixCacheDirty(ETransformationMatrixType_World, node)); + } + + TEST_F(ATransformationLinkManager, canPropagateTransformationDirtinessToConsumerScene) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + markNodeTransformationClean(consumerScene, consumerNode); + + transformationLinkManager.propagateTransformationDirtinessToConsumers(providerSceneId, providerNode); + EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); + } + + TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenCreatingLink) + { + markNodeTransformationClean(consumerScene, consumerNode); + + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); + } + + TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenRemovingLink) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + markNodeTransformationClean(consumerScene, consumerNode); + + EXPECT_TRUE(transformationLinkManager.removeDataLink(consumerSceneId, consumerSlotHandle)); + EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); + } + + TEST_F(ATransformationLinkManager, doesNotPropagateTransformationDirtinessToConsumerWhenRemovingLinkFailed) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + markNodeTransformationClean(consumerScene, consumerNode); + + const NodeHandle consumerNode2 = consumerSceneAllocator.allocateNode(); + const DataSlotId consumerId2(888u); + const DataSlotHandle slotHandle2(654u); + consumerSceneAllocator.allocateDataSlot({ EDataSlotType::TransformationConsumer, consumerId2, consumerNode2, DataInstanceHandle::Invalid(), ResourceContentHash::Invalid(), TextureSamplerHandle() }, slotHandle2); + expectRendererEvent(ERendererEventType::SceneDataSlotConsumerCreated, SceneId(0u), DataSlotId(0u), consumerSceneId, consumerId2); + + EXPECT_FALSE(transformationLinkManager.removeDataLink(consumerSceneId, slotHandle2)); + EXPECT_FALSE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); + } + + TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenNodeTransformationModified) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + markNodeTransformationClean(consumerScene, consumerNode); + + providerSceneAllocator.allocateTransform(providerNode, providerTransform); + providerScene.setTranslation(providerTransform, glm::vec3(1.f)); + EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); + } + + TEST_F(ATransformationLinkManager, propagatesTransformationDirtinessToConsumerWhenProviderSceneDestroyed) + { + EXPECT_TRUE(transformationLinkManager.createDataLink(providerSceneId, providerSlotHandle, consumerSceneId, consumerSlotHandle)); + markNodeTransformationClean(consumerScene, consumerNode); + rendererScenes.destroyScene(providerSceneId); + + EXPECT_TRUE(consumerScene.isMatrixCacheDirty(ETransformationMatrixType_World, consumerNode)); + } +} diff --git a/renderer/Platform/Device_GL/CMakeLists.txt b/tests/unittests/renderer/renderer-test-common/CMakeLists.txt similarity index 59% rename from renderer/Platform/Device_GL/CMakeLists.txt rename to tests/unittests/renderer/renderer-test-common/CMakeLists.txt index ca911ff74..b4b346a24 100644 --- a/renderer/Platform/Device_GL/CMakeLists.txt +++ b/tests/unittests/renderer/renderer-test-common/CMakeLists.txt @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,14 +7,12 @@ # ------------------------------------------------------------------------- createModule( - NAME Device_GL + NAME renderer-test-common TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES include/Device_GL/*.h - src/*.cpp + + INCLUDE_PATHS . + SRC_FILES *.h + *.cpp DEPENDENCIES ramses-renderer-lib - OpenGL - # TODO move this to the OpenGL target where it belongs - PUBLIC_DEFINES ${OpenGL_DEFINITIONS} + framework-test-utils ) diff --git a/renderer/RendererLib/RendererTestCommon/ContextMock.cpp b/tests/unittests/renderer/renderer-test-common/ContextMock.cpp similarity index 82% rename from renderer/RendererLib/RendererTestCommon/ContextMock.cpp rename to tests/unittests/renderer/renderer-test-common/ContextMock.cpp index 6978cfb55..e639a6199 100644 --- a/renderer/RendererLib/RendererTestCommon/ContextMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/ContextMock.cpp @@ -10,14 +10,10 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { - ContextMock::ContextMock() - { - } + ContextMock::ContextMock() = default; - ContextMock::~ContextMock() - { - } + ContextMock::~ContextMock() = default; } diff --git a/renderer/RendererLib/RendererTestCommon/ContextMock.h b/tests/unittests/renderer/renderer-test-common/ContextMock.h similarity index 82% rename from renderer/RendererLib/RendererTestCommon/ContextMock.h rename to tests/unittests/renderer/renderer-test-common/ContextMock.h index 1c0ef15c6..dbeace7a4 100644 --- a/renderer/RendererLib/RendererTestCommon/ContextMock.h +++ b/tests/unittests/renderer/renderer-test-common/ContextMock.h @@ -6,16 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_CONTEXTMOCK_H -#define RAMSES_CONTEXTMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/IContext.h" #include "gmock/gmock.h" -#include "RendererAPI/IContext.h" -#include "Platform_Base/DeviceResourceMapper.h" -namespace ramses_internal +namespace ramses::internal { class ContextMock : public IContext { @@ -33,5 +30,3 @@ namespace ramses_internal MOCK_METHOD(void*, getProcAddress, (const char*), (const, override)); }; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/DeviceMock.cpp b/tests/unittests/renderer/renderer-test-common/DeviceMock.cpp similarity index 94% rename from renderer/RendererLib/RendererTestCommon/DeviceMock.cpp rename to tests/unittests/renderer/renderer-test-common/DeviceMock.cpp index c1048bffa..178aafa57 100644 --- a/renderer/RendererLib/RendererTestCommon/DeviceMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/DeviceMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { const DeviceResourceHandle DeviceMock::FakeShaderDeviceHandle(1111u); const DeviceResourceHandle DeviceMock::FakeVertexBufferDeviceHandle(2222u); @@ -48,11 +48,11 @@ namespace ramses_internal ON_CALL(*this, allocateVertexBuffer(_)).WillByDefault(Return(FakeVertexBufferDeviceHandle)); ON_CALL(*this, allocateIndexBuffer(_, _)).WillByDefault(Return(FakeIndexBufferDeviceHandle)); ON_CALL(*this, allocateVertexArray(_)).WillByDefault(Return(FakeVertexArrayDeviceHandle)); - ON_CALL(*this, uploadShader(_)).WillByDefault(Invoke([](const auto&){return std::make_unique(1u, 2u);})); + ON_CALL(*this, uploadShader(_)).WillByDefault(Invoke([](const auto& /*unused*/){return std::make_unique(1u, 2u);})); ON_CALL(*this, registerShader(_)).WillByDefault(Return(FakeShaderDeviceHandle)); ON_CALL(*this, uploadBinaryShader(_, _, _, _)).WillByDefault(Return(FakeShaderDeviceHandle)); ON_CALL(*this, allocateTexture2D(_, _, _, _, _, _)).WillByDefault(Return(FakeTextureDeviceHandle)); - ON_CALL(*this, uploadRenderBuffer(_, _, _, _, _, _)).WillByDefault(Return(FakeRenderBufferDeviceHandle)); + ON_CALL(*this, uploadRenderBuffer(_, _, _, _, _)).WillByDefault(Return(FakeRenderBufferDeviceHandle)); ON_CALL(*this, uploadDmaRenderBuffer(_, _, _, _, _)).WillByDefault(Return(FakeDmaRenderBufferDeviceHandle)); ON_CALL(*this, uploadRenderTarget(_)).WillByDefault(Return(FakeRenderTargetDeviceHandle)); ON_CALL(*this, getFramebufferRenderTarget()).WillByDefault(Return(FakeFrameBufferRenderTargetDeviceHandle)); diff --git a/renderer/RendererLib/RendererTestCommon/DeviceMock.h b/tests/unittests/renderer/renderer-test-common/DeviceMock.h similarity index 78% rename from renderer/RendererLib/RendererTestCommon/DeviceMock.h rename to tests/unittests/renderer/renderer-test-common/DeviceMock.h index 7f8a3c53d..f6048203b 100644 --- a/renderer/RendererLib/RendererTestCommon/DeviceMock.h +++ b/tests/unittests/renderer/renderer-test-common/DeviceMock.h @@ -6,19 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_DEVICEMOCK_H -#define RAMSES_DEVICEMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/IDevice.h" #include "gmock/gmock.h" -#include "RendererAPI/IDevice.h" -#include "RendererAPI/IContext.h" -#include "Resource/EffectResource.h" -#include "SceneAPI/RenderBuffer.h" -#include "SceneAPI/PixelRectangle.h" -#include "SceneAPI/TextureSamplerStates.h" - -namespace ramses_internal + +namespace ramses::internal { class DeviceResourceMapper; @@ -30,19 +23,20 @@ namespace ramses_internal MOCK_METHOD(bool, init, ()); - MOCK_METHOD(void, clear, (uint32_t), (override)); - - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const float*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::vec2*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::vec3*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::vec4*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const int32_t*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::ivec2*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::ivec3*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::ivec4*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::mat2*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::mat3*), (override)); - MOCK_METHOD(void, setConstant, (DataFieldHandle, uint32_t, const glm::mat4*), (override)); + MOCK_METHOD(void, clear, (ClearFlags), (override)); + + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const float*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::vec2*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::vec3*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::vec4*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const bool*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const int32_t*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::ivec2*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::ivec3*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::ivec4*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::mat2*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::mat3*), (override)); + MOCK_METHOD(bool, setConstant, (DataFieldHandle, uint32_t, const glm::mat4*), (override)); MOCK_METHOD(void, drawIndexedTriangles, (int32_t, int32_t, uint32_t), (override)); MOCK_METHOD(void, drawTriangles, (int32_t, int32_t, uint32_t), (override)); @@ -64,34 +58,34 @@ namespace ramses_internal MOCK_METHOD(void, setViewport, (int32_t, int32_t, uint32_t, uint32_t), (override)); MOCK_METHOD(DeviceResourceHandle, allocateVertexBuffer, (uint32_t), (override)); - MOCK_METHOD(void, uploadVertexBufferData, (DeviceResourceHandle, const Byte*, uint32_t), (override)); + MOCK_METHOD(void, uploadVertexBufferData, (DeviceResourceHandle, const std::byte*, uint32_t), (override)); MOCK_METHOD(void, deleteVertexBuffer, (DeviceResourceHandle), (override)); MOCK_METHOD(DeviceResourceHandle, allocateVertexArray, (const VertexArrayInfo&), (override)); MOCK_METHOD(void, activateVertexArray, (DeviceResourceHandle handle), (override)); MOCK_METHOD(void, deleteVertexArray, (DeviceResourceHandle handle), (override)); MOCK_METHOD(DeviceResourceHandle, allocateIndexBuffer, (EDataType, uint32_t), (override)); - MOCK_METHOD(void, uploadIndexBufferData, (DeviceResourceHandle, const Byte*, uint32_t), (override)); + MOCK_METHOD(void, uploadIndexBufferData, (DeviceResourceHandle, const std::byte*, uint32_t), (override)); MOCK_METHOD(void, deleteIndexBuffer, (DeviceResourceHandle), (override)); MOCK_METHOD(std::unique_ptr, uploadShader, (const EffectResource&), (override)); MOCK_METHOD(DeviceResourceHandle, registerShader, (std::unique_ptr), (override)); - MOCK_METHOD(DeviceResourceHandle, uploadBinaryShader, (const EffectResource&, const uint8_t* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat), (override)); - MOCK_METHOD(bool, getBinaryShader, (DeviceResourceHandle, UInt8Vector&, BinaryShaderFormatID&), (override)); + MOCK_METHOD(DeviceResourceHandle, uploadBinaryShader, (const EffectResource&, const std::byte* binaryShaderData, uint32_t binaryShaderDataSize, BinaryShaderFormatID binaryShaderFormat), (override)); + MOCK_METHOD(bool, getBinaryShader, (DeviceResourceHandle, std::vector&, BinaryShaderFormatID&), (override)); MOCK_METHOD(void, deleteShader, (DeviceResourceHandle), (override)); MOCK_METHOD(void, activateShader, (DeviceResourceHandle), (override)); - MOCK_METHOD(DeviceResourceHandle, allocateTexture2D, (uint32_t width, uint32_t height, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes), (override)); - MOCK_METHOD(DeviceResourceHandle, allocateTexture3D, (uint32_t width, uint32_t height, uint32_t depth, ETextureFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes), (override)); - MOCK_METHOD(DeviceResourceHandle, allocateTextureCube, (uint32_t faceSize, ETextureFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes), (override)); + MOCK_METHOD(DeviceResourceHandle, allocateTexture2D, (uint32_t width, uint32_t height, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes), (override)); + MOCK_METHOD(DeviceResourceHandle, allocateTexture3D, (uint32_t width, uint32_t height, uint32_t depth, EPixelStorageFormat textureFormat, uint32_t mipLevelCount, uint32_t totalSizeInBytes), (override)); + MOCK_METHOD(DeviceResourceHandle, allocateTextureCube, (uint32_t faceSize, EPixelStorageFormat textureFormat, const TextureSwizzleArray& swizzle, uint32_t mipLevelCount, uint32_t totalSizeInBytes), (override)); MOCK_METHOD(DeviceResourceHandle, allocateExternalTexture, (), (override)); MOCK_METHOD(DeviceResourceHandle, getEmptyExternalTexture, (), (const, override)); MOCK_METHOD(void, bindTexture, (DeviceResourceHandle handle), (override)); MOCK_METHOD(void, generateMipmaps, (DeviceResourceHandle handle), (override)); - MOCK_METHOD(void, uploadTextureData, (DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const Byte* data, uint32_t dataSize), (override)); - MOCK_METHOD(DeviceResourceHandle, uploadStreamTexture2D, (DeviceResourceHandle handle, uint32_t width, uint32_t height, ETextureFormat format, const uint8_t* data, const TextureSwizzleArray& swizzle), (override)); + MOCK_METHOD(void, uploadTextureData, (DeviceResourceHandle handle, uint32_t mipLevel, uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const std::byte* data, uint32_t dataSize, uint32_t stride), (override)); + MOCK_METHOD(DeviceResourceHandle, uploadStreamTexture2D, (DeviceResourceHandle handle, uint32_t width, uint32_t height, EPixelStorageFormat format, const std::byte* data, const TextureSwizzleArray& swizzle), (override)); MOCK_METHOD(void, deleteTexture, (DeviceResourceHandle), (override)); MOCK_METHOD(void, activateTexture, (DeviceResourceHandle, DataFieldHandle), (override)); - MOCK_METHOD(DeviceResourceHandle, uploadRenderBuffer, (uint32_t, uint32_t, ERenderBufferType, ETextureFormat, ERenderBufferAccessMode, uint32_t), (override)); + MOCK_METHOD(DeviceResourceHandle, uploadRenderBuffer, (uint32_t, uint32_t, EPixelStorageFormat, ERenderBufferAccessMode, uint32_t), (override)); MOCK_METHOD(void, deleteRenderBuffer, (DeviceResourceHandle), (override)); MOCK_METHOD(DeviceResourceHandle, uploadDmaRenderBuffer, (uint32_t, uint32_t, DmaBufferFourccFormat, DmaBufferUsageFlags, DmaBufferModifiers), (override)); MOCK_METHOD(int, getDmaRenderBufferFD, (DeviceResourceHandle handle), (override)); @@ -121,7 +115,7 @@ namespace ramses_internal MOCK_METHOD(void, flush, (), (override)); - MOCK_METHOD(int, getTextureAddress, (DeviceResourceHandle), (const, override)); + MOCK_METHOD(uint32_t, getTextureAddress, (DeviceResourceHandle), (const, override)); MOCK_METHOD(uint32_t, getGPUHandle, (DeviceResourceHandle), (const, override)); static const DeviceResourceHandle FakeShaderDeviceHandle ; @@ -145,5 +139,3 @@ namespace ramses_internal }; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositorMock.cpp b/tests/unittests/renderer/renderer-test-common/EmbeddedCompositorMock.cpp similarity index 97% rename from renderer/RendererLib/RendererTestCommon/EmbeddedCompositorMock.cpp rename to tests/unittests/renderer/renderer-test-common/EmbeddedCompositorMock.cpp index fb77f1204..d6976757d 100644 --- a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositorMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/EmbeddedCompositorMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { EmbeddedCompositorMock::EmbeddedCompositorMock() { diff --git a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositorMock.h b/tests/unittests/renderer/renderer-test-common/EmbeddedCompositorMock.h similarity index 88% rename from renderer/RendererLib/RendererTestCommon/EmbeddedCompositorMock.h rename to tests/unittests/renderer/renderer-test-common/EmbeddedCompositorMock.h index 17b2a4d39..c6e1d9861 100644 --- a/renderer/RendererLib/RendererTestCommon/EmbeddedCompositorMock.h +++ b/tests/unittests/renderer/renderer-test-common/EmbeddedCompositorMock.h @@ -6,16 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_EMBEDDEDCOMPOSITORMOCK_H -#define RAMSES_EMBEDDEDCOMPOSITORMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/IEmbeddedCompositor.h" #include "gmock/gmock.h" -#include "RendererAPI/IEmbeddedCompositor.h" -#include "RendererLib/RendererLogContext.h" -#include "RendererAPI/ITextureUploadingAdapter.h" -namespace ramses_internal +namespace ramses::internal { class EmbeddedCompositorMock : public IEmbeddedCompositor { @@ -38,8 +34,7 @@ namespace ramses_internal MOCK_METHOD(bool , hasSurfaceForStreamTexture, (WaylandIviSurfaceId), (const, override)); MOCK_METHOD(std::string, getTitleOfWaylandIviSurface, (WaylandIviSurfaceId), (const, override)); MOCK_METHOD(void, logInfos, (RendererLogContext&), (const, override)); + MOCK_METHOD(void, logPeriodicInfo, (StringOutputStream&), (const, override)); MOCK_METHOD(bool, isRealCompositor, (), (const, override)); }; } - -#endif diff --git a/tests/unittests/renderer/renderer-test-common/MockResourceHash.h b/tests/unittests/renderer/renderer-test-common/MockResourceHash.h new file mode 100644 index 000000000..32dd71860 --- /dev/null +++ b/tests/unittests/renderer/renderer-test-common/MockResourceHash.h @@ -0,0 +1,60 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "internal/SceneGraph/Resource/ArrayResource.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/Components/ManagedResource.h" + +namespace ramses::internal +{ + namespace MockResourceHash + { + static constexpr ResourceContentHash EffectHash{ 120u, 0u }; + static constexpr ResourceContentHash VertArrayHash{ 123u, 0u }; + static constexpr ResourceContentHash VertArrayHash2{ 124u, 0u }; + static constexpr ResourceContentHash IndexArrayHash{ 125u, 0u }; + static constexpr ResourceContentHash IndexArrayHash2{ 126u, 0u }; + static constexpr ResourceContentHash IndexArrayHash3{ 127u, 0u }; + static constexpr ResourceContentHash TextureHash{ 128u, 0u }; + static constexpr ResourceContentHash TextureHash2{ 129u, 0u }; + + static inline ManagedResource GetManagedResource(const ResourceContentHash& hash) + { + std::unique_ptr res; + if (hash == EffectHash) + { + res = std::make_unique("", "", "", std::optional{}, EffectInputInformationVector(), EffectInputInformationVector(), ""); + } + else if (hash == VertArrayHash || hash == VertArrayHash2) + { + res = std::make_unique(EResourceType::VertexArray, 0, EDataType::Float, nullptr, std::string_view{}); + } + else if (hash == IndexArrayHash || hash == IndexArrayHash2 || hash == IndexArrayHash3) + { + res = std::make_unique(EResourceType::IndexArray, 0, EDataType::UInt16, nullptr, std::string_view{}); + } + else if (hash == TextureHash) + { + res = std::make_unique(EResourceType::Texture2D, TextureMetaInfo(1u, 1u, 1u, EPixelStorageFormat::R8, false, {}, { 1u }), std::string_view{}); + } + else if (hash == TextureHash2) + { + res = std::make_unique(EResourceType::Texture2D, TextureMetaInfo(2u, 2u, 1u, EPixelStorageFormat::R8, true, {}, {4u}), std::string_view{}); + } + + if (res) + res->setResourceData(ResourceBlob{ 1 }, hash); + + return res; + } + } +} diff --git a/renderer/RendererLib/RendererTestCommon/PlatformFactoryMock.h b/tests/unittests/renderer/renderer-test-common/PlatformFactoryMock.h similarity index 63% rename from renderer/RendererLib/RendererTestCommon/PlatformFactoryMock.h rename to tests/unittests/renderer/renderer-test-common/PlatformFactoryMock.h index 8d0e0c0bb..56b09d3f3 100644 --- a/renderer/RendererLib/RendererTestCommon/PlatformFactoryMock.h +++ b/tests/unittests/renderer/renderer-test-common/PlatformFactoryMock.h @@ -6,30 +6,28 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMFACTORYMOCK_H -#define RAMSES_PLATFORMFACTORYMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" -#include "RendererAPI/IPlatformFactory.h" +#include "internal/RendererLib/PlatformInterface/IPlatformFactory.h" #include "PlatformMock.h" -namespace ramses_internal +namespace ramses::internal { template class MOCK_TYPE> class PlatformFactoryMock : public IPlatformFactory { public: + template, PlatformFactoryMock<::testing::NiceMock>>::value>::type> PlatformFactoryMock() { - ON_CALL(*this, createPlatform(_, _)).WillByDefault(Invoke([](){ - return std::make_unique>(); - })); + // Add expectations for NiceMock to avoid "unexpected call" messages. StrictMock should still cause test failure for unexpected calls. + EXPECT_CALL(*this, createPlatform(_, _)).Times(AnyNumber()).WillRepeatedly([](){ + return std::make_unique>();}); } + MOCK_METHOD(std::unique_ptr, createPlatform, (const RendererConfig& rendererConfig, const DisplayConfig& dispConfig), (override)); }; using PlatformFactoryNiceMock = PlatformFactoryMock< ::testing::NiceMock>; using PlatformFactoryStrictMock = PlatformFactoryMock< ::testing::StrictMock>; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/PlatformMock.cpp b/tests/unittests/renderer/renderer-test-common/PlatformMock.cpp similarity index 97% rename from renderer/RendererLib/RendererTestCommon/PlatformMock.cpp rename to tests/unittests/renderer/renderer-test-common/PlatformMock.cpp index 5565cb8b3..f85d4c4b6 100644 --- a/renderer/RendererLib/RendererTestCommon/PlatformMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/PlatformMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { template class MOCK_TYPE> PlatformMock::PlatformMock() diff --git a/renderer/RendererLib/RendererTestCommon/PlatformMock.h b/tests/unittests/renderer/renderer-test-common/PlatformMock.h similarity index 80% rename from renderer/RendererLib/RendererTestCommon/PlatformMock.h rename to tests/unittests/renderer/renderer-test-common/PlatformMock.h index 6c2968c3b..d11aa9318 100644 --- a/renderer/RendererLib/RendererTestCommon/PlatformMock.h +++ b/tests/unittests/renderer/renderer-test-common/PlatformMock.h @@ -6,23 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_PLATFORMMOCK_H -#define RAMSES_PLATFORMMOCK_H - -#include "renderer_common_gmock_header.h" -#include "DeviceMock.h" -#include "EmbeddedCompositorMock.h" -#include "RendererAPI/IPlatform.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "RendererAPI/ISystemCompositorController.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererAPI/IResourceUploadRenderBackend.h" +#pragma once +#include "internal/RendererLib/PlatformInterface/IPlatform.h" #include "RenderBackendMock.h" #include "SystemCompositorControllerMock.h" #include "ResourceUploadRenderBackendMock.h" -namespace ramses_internal +namespace ramses::internal { class RendererConfig; @@ -47,5 +38,3 @@ namespace ramses_internal using PlatformNiceMock = PlatformMock< ::testing::NiceMock>; using PlatformStrictMock = PlatformMock< ::testing::StrictMock>; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/RenderBackendMock.cpp b/tests/unittests/renderer/renderer-test-common/RenderBackendMock.cpp similarity index 94% rename from renderer/RendererLib/RendererTestCommon/RenderBackendMock.cpp rename to tests/unittests/renderer/renderer-test-common/RenderBackendMock.cpp index 6573c19c2..6ed351c4c 100644 --- a/renderer/RendererLib/RendererTestCommon/RenderBackendMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/RenderBackendMock.cpp @@ -8,7 +8,7 @@ #include "RenderBackendMock.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -29,9 +29,7 @@ namespace ramses_internal } template class MOCK_TYPE> - RenderBackendMock::~RenderBackendMock() - { - } + RenderBackendMock::~RenderBackendMock() = default; template class RenderBackendMock < NiceMock > ; template class RenderBackendMock < StrictMock > ; diff --git a/renderer/RendererLib/RendererTestCommon/RenderBackendMock.h b/tests/unittests/renderer/renderer-test-common/RenderBackendMock.h similarity index 65% rename from renderer/RendererLib/RendererTestCommon/RenderBackendMock.h rename to tests/unittests/renderer/renderer-test-common/RenderBackendMock.h index a7e06b431..2a4513691 100644 --- a/renderer/RendererLib/RendererTestCommon/RenderBackendMock.h +++ b/tests/unittests/renderer/renderer-test-common/RenderBackendMock.h @@ -7,19 +7,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERBACKENDMOCK_H -#define RAMSES_RENDERBACKENDMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererAPI/IRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IRenderBackend.h" #include "EmbeddedCompositorMock.h" #include "DeviceMock.h" #include "WindowMock.h" #include "ContextMock.h" -#include "Platform_Base/TextureUploadingAdapter_Base.h" +#include "internal/RendererLib/PlatformBase/TextureUploadingAdapter_Base.h" +#include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -29,11 +27,11 @@ namespace ramses_internal public: RenderBackendMock(); ~RenderBackendMock() override; - MOCK_METHOD(ramses_internal::IWindow&, getWindow, (), (const, override)); - MOCK_METHOD(ramses_internal::IContext&, getContext, (), (const, override)); - MOCK_METHOD(ramses_internal::IDevice&, getDevice, (), (const, override)); - MOCK_METHOD(ramses_internal::IEmbeddedCompositor&, getEmbeddedCompositor, (), (const, override)); - MOCK_METHOD(ramses_internal::ITextureUploadingAdapter&, getTextureUploadingAdapter, (), (const, override)); + MOCK_METHOD(IWindow&, getWindow, (), (const, override)); + MOCK_METHOD(IContext&, getContext, (), (const, override)); + MOCK_METHOD(IDevice&, getDevice, (), (const, override)); + MOCK_METHOD(IEmbeddedCompositor&, getEmbeddedCompositor, (), (const, override)); + MOCK_METHOD(ITextureUploadingAdapter&, getTextureUploadingAdapter, (), (const, override)); MOCK_TYPE< DeviceMock > deviceMock; MOCK_TYPE< WindowMock > windowMock; @@ -45,4 +43,4 @@ namespace ramses_internal using RenderBackendNiceMock = RenderBackendMock< ::testing::NiceMock>; using RenderBackendStrictMock = RenderBackendMock< ::testing::StrictMock>; } -#endif + diff --git a/renderer/RendererLib/RendererTestCommon/RendererCommandVisitorMock.cpp b/tests/unittests/renderer/renderer-test-common/RendererCommandVisitorMock.cpp similarity index 92% rename from renderer/RendererLib/RendererTestCommon/RendererCommandVisitorMock.cpp rename to tests/unittests/renderer/renderer-test-common/RendererCommandVisitorMock.cpp index 52985745f..8e4f012c8 100644 --- a/renderer/RendererLib/RendererTestCommon/RendererCommandVisitorMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/RendererCommandVisitorMock.cpp @@ -7,9 +7,9 @@ // ------------------------------------------------------------------------- #include "RendererCommandVisitorMock.h" -#include "RendererLib/RendererCommandBuffer.h" +#include "internal/RendererLib/RendererCommandBuffer.h" -namespace ramses_internal +namespace ramses::internal { RendererCommandVisitorMock::RendererCommandVisitorMock() = default; RendererCommandVisitorMock::~RendererCommandVisitorMock() = default; diff --git a/renderer/RendererLib/RendererTestCommon/RendererCommandVisitorMock.h b/tests/unittests/renderer/renderer-test-common/RendererCommandVisitorMock.h similarity index 96% rename from renderer/RendererLib/RendererTestCommon/RendererCommandVisitorMock.h rename to tests/unittests/renderer/renderer-test-common/RendererCommandVisitorMock.h index 0aac4c920..777f4a214 100644 --- a/renderer/RendererLib/RendererTestCommon/RendererCommandVisitorMock.h +++ b/tests/unittests/renderer/renderer-test-common/RendererCommandVisitorMock.h @@ -6,20 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RENDERERCOMMANDVISITORMOCK_H -#define RAMSES_RENDERERCOMMANDVISITORMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/RendererCommands.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/SceneGraph/SceneAPI/SceneId.h" +#include "internal/RendererLib/Types.h" #include "gmock/gmock.h" -#include "RendererLib/RendererCommands.h" -#include "RendererLib/DisplayConfig.h" -#include "SceneAPI/SceneId.h" -#include "RendererAPI/Types.h" -namespace ramses_internal +namespace ramses::internal { class RendererCommandBuffer; - class IBinaryShaderCache; class RendererCommandVisitorMock { @@ -160,7 +157,7 @@ namespace ramses_internal handleReadPixels(cmd.display, cmd.offscreenBuffer, cmd.offsetX, cmd.offsetY, cmd.width, cmd.height, cmd.fullScreen, cmd.filename); } - void operator()(const RendererCommand::SCListIviSurfaces&) + void operator()(const RendererCommand::SCListIviSurfaces& /*unused*/) { systemCompositorListIviSurfaces(); } @@ -226,7 +223,7 @@ namespace ramses_internal } template - void operator()(const T&) + void operator()(const T& /*unused*/) { assert(!"unhandled command"); } @@ -242,11 +239,11 @@ namespace ramses_internal MOCK_METHOD(void, destroyDisplayContext, (DisplayHandle)); MOCK_METHOD(void, handleSceneDataLinkRequest, (SceneId, DataSlotId, SceneId, DataSlotId)); MOCK_METHOD(void, handleDataUnlinkRequest, (SceneId, DataSlotId)); - MOCK_METHOD(void, handleSetClearFlags, (DisplayHandle, OffscreenBufferHandle, uint32_t)); + MOCK_METHOD(void, handleSetClearFlags, (DisplayHandle, OffscreenBufferHandle, ClearFlags)); MOCK_METHOD(void, handleSetClearColor, (DisplayHandle, OffscreenBufferHandle, const glm::vec4&)); MOCK_METHOD(void, handleSetExternallyOwnedWindowSize, (DisplayHandle, uint32_t, uint32_t)); MOCK_METHOD(void, handlePick, (SceneId, const glm::vec2&)); - MOCK_METHOD(void, handleBufferCreateRequest, (OffscreenBufferHandle, DisplayHandle, uint32_t, uint32_t, uint32_t, bool, ERenderBufferType)); + MOCK_METHOD(void, handleBufferCreateRequest, (OffscreenBufferHandle, DisplayHandle, uint32_t, uint32_t, uint32_t, bool, EDepthBufferType)); MOCK_METHOD(void, handleDmaBufferCreateRequest, (OffscreenBufferHandle, DisplayHandle, uint32_t, uint32_t, DmaBufferFourccFormat, DmaBufferUsageFlags, DmaBufferModifiers)); MOCK_METHOD(void, handleBufferDestroyRequest, (OffscreenBufferHandle, DisplayHandle)); MOCK_METHOD(void, handleBufferCreateRequest, (StreamBufferHandle, DisplayHandle, WaylandIviSurfaceId)); @@ -272,5 +269,3 @@ namespace ramses_internal MOCK_METHOD(void, handleConfirmationEcho, (DisplayHandle, std::string_view)); }; } - -#endif diff --git a/tests/unittests/renderer/renderer-test-common/ResourceDeviceHandleAccessorMock.h b/tests/unittests/renderer/renderer-test-common/ResourceDeviceHandleAccessorMock.h new file mode 100644 index 000000000..f2055e138 --- /dev/null +++ b/tests/unittests/renderer/renderer-test-common/ResourceDeviceHandleAccessorMock.h @@ -0,0 +1,37 @@ +// ------------------------------------------------------------------------- +// Copyright (C) 2020 BMW AG +// ------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// ------------------------------------------------------------------------- + +#pragma once + +#include "internal/RendererLib/IResourceDeviceHandleAccessor.h" +#include "gmock/gmock.h" + +namespace ramses::internal +{ + class ResourceDeviceHandleAccessorMock : public IResourceDeviceHandleAccessor + { + public: + MOCK_METHOD(DeviceResourceHandle, getResourceDeviceHandle, (const ResourceContentHash& resourceHash), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getRenderTargetDeviceHandle, (RenderTargetHandle targetHandle, SceneId sceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getRenderTargetBufferDeviceHandle, (RenderBufferHandle bufferHandle, SceneId sceneId), (const, override)); + MOCK_METHOD(void, getBlitPassRenderTargetsDeviceHandle, (BlitPassHandle blitPassHandle, SceneId sceneId, DeviceResourceHandle&, DeviceResourceHandle&), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferDeviceHandle, (OffscreenBufferHandle bufferHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getOffscreenBufferColorBufferDeviceHandle, (OffscreenBufferHandle bufferHandle), (const, override)); + MOCK_METHOD(int, getDmaOffscreenBufferFD, (OffscreenBufferHandle), (const, override)); + MOCK_METHOD(uint32_t, getDmaOffscreenBufferStride, (OffscreenBufferHandle), (const, override)); + MOCK_METHOD(OffscreenBufferHandle, getOffscreenBufferHandle, (DeviceResourceHandle bufferDeviceHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getStreamBufferDeviceHandle, (StreamBufferHandle bufferHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getDataBufferDeviceHandle, (DataBufferHandle dataBufferHandle, SceneId sceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getTextureBufferDeviceHandle, (TextureBufferHandle textureBufferHandle, SceneId sceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getVertexArrayDeviceHandle, (RenderableHandle renderableHandle, SceneId sceneId), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getExternalBufferDeviceHandle, (ExternalBufferHandle), (const, override)); + MOCK_METHOD(DeviceResourceHandle, getEmptyExternalBufferDeviceHandle, (), (const, override)); + MOCK_METHOD(uint32_t, getExternalBufferGlId, (ExternalBufferHandle), (const, override)); + }; +} + diff --git a/renderer/RendererLib/RendererTestCommon/ResourceUploadRenderBackendMock.h b/tests/unittests/renderer/renderer-test-common/ResourceUploadRenderBackendMock.h similarity index 78% rename from renderer/RendererLib/RendererTestCommon/ResourceUploadRenderBackendMock.h rename to tests/unittests/renderer/renderer-test-common/ResourceUploadRenderBackendMock.h index 7233c187d..b232eba4f 100644 --- a/renderer/RendererLib/RendererTestCommon/ResourceUploadRenderBackendMock.h +++ b/tests/unittests/renderer/renderer-test-common/ResourceUploadRenderBackendMock.h @@ -6,16 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_RESOURCEUPLOADRENDERBACKENDMOCK_H -#define RAMSES_RESOURCEUPLOADRENDERBACKENDMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" -#include "gmock/gmock.h" -#include "RendererAPI/IResourceUploadRenderBackend.h" +#include "internal/RendererLib/PlatformInterface/IResourceUploadRenderBackend.h" #include "DeviceMock.h" #include "ContextMock.h" +#include "gmock/gmock.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; @@ -33,8 +31,8 @@ namespace ramses_internal } ~ResourceUploadRenderBackendMock() override = default; - MOCK_METHOD(ramses_internal::IContext&, getContext, (), (const, override)); - MOCK_METHOD(ramses_internal::IDevice&, getDevice, (), (const, override)); + MOCK_METHOD(IContext&, getContext, (), (const, override)); + MOCK_METHOD(IDevice&, getDevice, (), (const, override)); MOCK_TYPE< DeviceMock > deviceMock; MOCK_TYPE< ContextMock > contextMock; @@ -43,4 +41,4 @@ namespace ramses_internal using ResourceUploadRenderBackendNiceMock = ResourceUploadRenderBackendMock< ::testing::NiceMock>; using ResourceUploadRenderBackendStrictMock = ResourceUploadRenderBackendMock< ::testing::StrictMock>; } -#endif + diff --git a/renderer/RendererLib/RendererTestCommon/SystemCompositorControllerMock.cpp b/tests/unittests/renderer/renderer-test-common/SystemCompositorControllerMock.cpp similarity index 94% rename from renderer/RendererLib/RendererTestCommon/SystemCompositorControllerMock.cpp rename to tests/unittests/renderer/renderer-test-common/SystemCompositorControllerMock.cpp index 34cffcf87..3f49a8a5c 100644 --- a/renderer/RendererLib/RendererTestCommon/SystemCompositorControllerMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/SystemCompositorControllerMock.cpp @@ -10,14 +10,12 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { SystemCompositorControllerMock::SystemCompositorControllerMock() { ON_CALL(*this, addSurfaceToLayer(_,_)).WillByDefault(Return(true)); } - SystemCompositorControllerMock::~SystemCompositorControllerMock() - { - } + SystemCompositorControllerMock::~SystemCompositorControllerMock() = default; } diff --git a/renderer/RendererLib/RendererTestCommon/SystemCompositorControllerMock.h b/tests/unittests/renderer/renderer-test-common/SystemCompositorControllerMock.h similarity index 87% rename from renderer/RendererLib/RendererTestCommon/SystemCompositorControllerMock.h rename to tests/unittests/renderer/renderer-test-common/SystemCompositorControllerMock.h index bcb9f6b3c..d5529738d 100644 --- a/renderer/RendererLib/RendererTestCommon/SystemCompositorControllerMock.h +++ b/tests/unittests/renderer/renderer-test-common/SystemCompositorControllerMock.h @@ -6,15 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SYSTEMCOMPOSITORCONTROLLERMOCK_H -#define RAMSES_SYSTEMCOMPOSITORCONTROLLERMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/ISystemCompositorController.h" #include "gmock/gmock.h" -#include "RendererAPI/ISystemCompositorController.h" - -namespace ramses_internal +namespace ramses::internal { class SystemCompositorControllerMock : public ISystemCompositorController { @@ -35,5 +32,3 @@ namespace ramses_internal MOCK_METHOD(void, listIVISurfaces, (), (const, override)); }; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/WindowEventHandlerMock.cpp b/tests/unittests/renderer/renderer-test-common/WindowEventHandlerMock.cpp similarity index 76% rename from renderer/RendererLib/RendererTestCommon/WindowEventHandlerMock.cpp rename to tests/unittests/renderer/renderer-test-common/WindowEventHandlerMock.cpp index 978e6d8e7..b501509c2 100644 --- a/renderer/RendererLib/RendererTestCommon/WindowEventHandlerMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/WindowEventHandlerMock.cpp @@ -9,16 +9,10 @@ #include "WindowEventHandlerMock.h" -namespace ramses_internal +namespace ramses::internal { - WindowEventHandlerMock::WindowEventHandlerMock() - { + WindowEventHandlerMock::WindowEventHandlerMock() = default; - } - - WindowEventHandlerMock::~WindowEventHandlerMock() - { - - } + WindowEventHandlerMock::~WindowEventHandlerMock() = default; } diff --git a/renderer/RendererLib/RendererTestCommon/WindowEventHandlerMock.h b/tests/unittests/renderer/renderer-test-common/WindowEventHandlerMock.h similarity index 66% rename from renderer/RendererLib/RendererTestCommon/WindowEventHandlerMock.h rename to tests/unittests/renderer/renderer-test-common/WindowEventHandlerMock.h index cb96a7cd8..07fbceaf5 100644 --- a/renderer/RendererLib/RendererTestCommon/WindowEventHandlerMock.h +++ b/tests/unittests/renderer/renderer-test-common/WindowEventHandlerMock.h @@ -6,16 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOWEVENTHANDLERMOCK_H -#define RAMSES_WINDOWEVENTHANDLERMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/IWindowEventHandler.h" #include "gmock/gmock.h" -#include "RendererAPI/IWindowEventHandler.h" -#include "RendererAPI/IWindow.h" - -namespace ramses_internal +namespace ramses::internal { class WindowEventHandlerMock : public IWindowEventHandler { @@ -24,11 +20,9 @@ namespace ramses_internal ~WindowEventHandlerMock() override; MOCK_METHOD(void, onResize, (uint32_t width, uint32_t height), (override)); - MOCK_METHOD(void, onKeyEvent, (EKeyEventType event, uint32_t modifiers, EKeyCode keyCode), (override)); - MOCK_METHOD(void, onMouseEvent, (EMouseEventType event, int32_t posX, int32_t posY), (override)); + MOCK_METHOD(void, onKeyEvent, (EKeyEvent event, KeyModifiers modifiers, EKeyCode keyCode), (override)); + MOCK_METHOD(void, onMouseEvent, (EMouseEvent event, int32_t posX, int32_t posY), (override)); MOCK_METHOD(void, onClose, (), (override)); MOCK_METHOD(void, onWindowMove, (int32_t posX, int32_t posY), (override)); }; } - -#endif diff --git a/renderer/RendererLib/RendererTestCommon/WindowMock.cpp b/tests/unittests/renderer/renderer-test-common/WindowMock.cpp similarity index 94% rename from renderer/RendererLib/RendererTestCommon/WindowMock.cpp rename to tests/unittests/renderer/renderer-test-common/WindowMock.cpp index 81564840b..f716e3e5e 100644 --- a/renderer/RendererLib/RendererTestCommon/WindowMock.cpp +++ b/tests/unittests/renderer/renderer-test-common/WindowMock.cpp @@ -10,7 +10,7 @@ using namespace testing; -namespace ramses_internal +namespace ramses::internal { WindowMock::WindowMock() { @@ -28,8 +28,6 @@ namespace ramses_internal EXPECT_CALL(*this, getHeight()).Times(AnyNumber()); } - WindowMock::~WindowMock() - { - } + WindowMock::~WindowMock() = default; } diff --git a/renderer/RendererLib/RendererTestCommon/WindowMock.h b/tests/unittests/renderer/renderer-test-common/WindowMock.h similarity index 91% rename from renderer/RendererLib/RendererTestCommon/WindowMock.h rename to tests/unittests/renderer/renderer-test-common/WindowMock.h index d2d722afa..6b1f4b462 100644 --- a/renderer/RendererLib/RendererTestCommon/WindowMock.h +++ b/tests/unittests/renderer/renderer-test-common/WindowMock.h @@ -6,15 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_WINDOWMOCK_H -#define RAMSES_WINDOWMOCK_H +#pragma once -#include "renderer_common_gmock_header.h" +#include "internal/RendererLib/PlatformInterface/IWindow.h" #include "gmock/gmock.h" -#include "RendererAPI/IWindow.h" - -namespace ramses_internal +namespace ramses::internal { class WindowMock : public IWindow { @@ -47,5 +44,3 @@ namespace ramses_internal void createDefaultMockCalls(); }; } - -#endif diff --git a/tests/unittests/renderer/system-compositor-controller-wayland/CMakeLists.txt b/tests/unittests/renderer/system-compositor-controller-wayland/CMakeLists.txt new file mode 100644 index 000000000..8a1fa28a9 --- /dev/null +++ b/tests/unittests/renderer/system-compositor-controller-wayland/CMakeLists.txt @@ -0,0 +1,22 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME system-compositor-controller-wayland-test + TYPE BINARY + SRC_FILES *.cpp + *.h + DEPENDENCIES Platform + wayland-test-utils + renderer-test-common +) + +makeTestFromTarget( + TARGET system-compositor-controller-wayland-test + SUFFIX RNDSANDWICHTEST_SWRAST + ) diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/test/SystemCompositorController_Wayland_IVI_Test.cpp b/tests/unittests/renderer/system-compositor-controller-wayland/SystemCompositorController_Wayland_IVI_Test.cpp similarity index 88% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/test/SystemCompositorController_Wayland_IVI_Test.cpp rename to tests/unittests/renderer/system-compositor-controller-wayland/SystemCompositorController_Wayland_IVI_Test.cpp index 19f933d87..b831d5ea5 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/test/SystemCompositorController_Wayland_IVI_Test.cpp +++ b/tests/unittests/renderer/system-compositor-controller-wayland/SystemCompositorController_Wayland_IVI_Test.cpp @@ -8,12 +8,12 @@ #include "gmock/gmock.h" #include "TestWithWaylandEnvironment.h" -#include "SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "Collections/StringOutputStream.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/PlatformAbstraction/Collections/StringOutputStream.h" -namespace ramses_internal +namespace ramses::internal { using namespace testing; diff --git a/renderer/Platform/SystemCompositorController_Wayland_IVI/test/main.cpp b/tests/unittests/renderer/system-compositor-controller-wayland/main.cpp similarity index 91% rename from renderer/Platform/SystemCompositorController_Wayland_IVI/test/main.cpp rename to tests/unittests/renderer/system-compositor-controller-wayland/main.cpp index ed1feefef..20bbbe30b 100644 --- a/renderer/Platform/SystemCompositorController_Wayland_IVI/test/main.cpp +++ b/tests/unittests/renderer/system-compositor-controller-wayland/main.cpp @@ -9,7 +9,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "PlatformAbstraction/PlatformConsole.h" +#include "internal/PlatformAbstraction/PlatformConsole.h" int main(int argc, char* argv[]) { diff --git a/tests/unittests/renderer/wayland-test-utils/CMakeLists.txt b/tests/unittests/renderer/wayland-test-utils/CMakeLists.txt new file mode 100644 index 000000000..a25fb8d66 --- /dev/null +++ b/tests/unittests/renderer/wayland-test-utils/CMakeLists.txt @@ -0,0 +1,17 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME wayland-test-utils + TYPE STATIC_LIBRARY + INCLUDE_PATHS . + SRC_FILES *.h + *.cpp + DEPENDENCIES Platform + ramses-gmock +) diff --git a/renderer/Platform/WaylandUtilities/WaylandTestUtils/TestWithWaylandEnvironment.h b/tests/unittests/renderer/wayland-test-utils/TestWithWaylandEnvironment.h similarity index 92% rename from renderer/Platform/WaylandUtilities/WaylandTestUtils/TestWithWaylandEnvironment.h rename to tests/unittests/renderer/wayland-test-utils/TestWithWaylandEnvironment.h index 6279ff742..e2560ecdd 100644 --- a/renderer/Platform/WaylandUtilities/WaylandTestUtils/TestWithWaylandEnvironment.h +++ b/tests/unittests/renderer/wayland-test-utils/TestWithWaylandEnvironment.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTWITHWAYLANDENVIRONMENT_H -#define RAMSES_TESTWITHWAYLANDENVIRONMENT_H +#pragma once #include "gtest/gtest.h" -#include "WaylandUtilities/WaylandEnvironmentUtils.h" +#include "internal/Platform/Wayland/WaylandEnvironmentUtils.h" -namespace ramses_internal +namespace ramses::internal { class TestWithWaylandEnvironment : public ::testing::Test { @@ -43,5 +42,3 @@ namespace ramses_internal }; } -#endif - diff --git a/renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/AWindowWayland.h b/tests/unittests/renderer/window-wayland-common/AWindowWayland.h similarity index 84% rename from renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/AWindowWayland.h rename to tests/unittests/renderer/window-wayland-common/AWindowWayland.h index e9e2785eb..8ec53b392 100644 --- a/renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/AWindowWayland.h +++ b/tests/unittests/renderer/window-wayland-common/AWindowWayland.h @@ -6,17 +6,16 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_AWINDOWWAYLAND_H -#define RAMSES_AWINDOWWAYLAND_H +#pragma once #include "WindowEventHandlerMock.h" #include "TestWithWaylandEnvironment.h" -#include "RendererLib/DisplayConfig.h" -#include "WaylandUtilities/UnixDomainSocket.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/Platform/Wayland/UnixDomainSocket.h" +#include "internal/Core/Utils/ThreadLocalLog.h" #include -namespace ramses_internal +namespace ramses::internal { template class AWindowWayland : public TestWithWaylandEnvironment @@ -48,9 +47,7 @@ namespace ramses_internal } ::testing::StrictMock m_eventHandlerMock; - ramses_internal::DisplayConfig m_config; + ramses::internal::DisplayConfig m_config; WINDOWTYPE* m_window = nullptr; }; } - -#endif diff --git a/renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/AWindowWaylandWithEventHandling.h b/tests/unittests/renderer/window-wayland-common/AWindowWaylandWithEventHandling.h similarity index 88% rename from renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/AWindowWaylandWithEventHandling.h rename to tests/unittests/renderer/window-wayland-common/AWindowWaylandWithEventHandling.h index dd4db4b40..d99f8ef7e 100644 --- a/renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/AWindowWaylandWithEventHandling.h +++ b/tests/unittests/renderer/window-wayland-common/AWindowWaylandWithEventHandling.h @@ -6,13 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_AWINDOWWAYLANDWITHEVENTHANDLING_H -#define RAMSES_AWINDOWWAYLANDWITHEVENTHANDLING_H +#pragma once #include "AWindowWayland.h" -#include "RendererLib/EKeyEventType.h" -#include "RendererLib/EKeyModifier.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/RendererLib/Enums/EKeyEvent.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include #include #include @@ -24,7 +23,7 @@ #define EV_RELEASED 0 #define EV_PRESSED 1 -namespace ramses_internal +namespace ramses::internal { using namespace ::testing; @@ -32,8 +31,6 @@ namespace ramses_internal class AWindowWaylandWithEventHandling : public AWindowWayland { public: - AWindowWaylandWithEventHandling() {} - void SetUp() override { openInputDevices(); @@ -46,9 +43,9 @@ namespace ramses_internal // Mouse click needed to get keyboard input focus. sendMouseButtonEvent(BTN_LEFT, true); sendMouseButtonEvent(BTN_LEFT, false); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(AnyNumber()); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonDown, _, _)).Times(AnyNumber()); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonUp, _, _)).Times(AnyNumber()); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(AnyNumber()); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonDown, _, _)).Times(AnyNumber()); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonUp, _, _)).Times(AnyNumber()); processAllEvents(); } @@ -88,15 +85,15 @@ namespace ramses_internal eglDisplay = eglGetDisplay(this->m_window->getNativeDisplayHandle()); ASSERT_TRUE(eglDisplay != EGL_NO_DISPLAY); - EGLint iMajorVersion; - EGLint iMinorVersion; + EGLint iMajorVersion = 0; + EGLint iMinorVersion = 0; ASSERT_TRUE(eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion)); ASSERT_TRUE(eglBindAPI(EGL_OPENGL_ES_API) != EGL_FALSE); std::array surfaceAttributes = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE}; - EGLConfig eglConfig; - EGLint num_config; + EGLConfig eglConfig = nullptr; + EGLint num_config = 0; ASSERT_TRUE(eglChooseConfig(eglDisplay, surfaceAttributes.data(), &eglConfig, 1, &num_config)); ASSERT_TRUE(num_config == 1); @@ -138,7 +135,7 @@ namespace ramses_internal bool openInputDevice(const char* deviceName, int& deviceFd) { - deviceFd = open(deviceName, O_WRONLY); + deviceFd = open(deviceName, O_WRONLY); // NOLINT(cppcoreguidelines-pro-type-vararg) return (deviceFd >= 0); } @@ -150,7 +147,7 @@ namespace ramses_internal bool sendInputEvent(int inputFd, uint16_t type, uint16_t key, int32_t value) { - struct input_event event; + struct input_event event{}; memset(&event, 0, sizeof(event)); event.type = type; @@ -204,13 +201,13 @@ namespace ramses_internal void testKeyCode(uint32_t virtualKeyCode, EKeyCode expectedRamsesKeyCode, - EKeyModifier expectedModifier = EKeyModifier_NoModifier) + KeyModifiers expectedModifier = KeyModifiers()) { sendKeyEvent(virtualKeyCode); - EXPECT_CALL(this->m_eventHandlerMock, onKeyEvent(EKeyEventType_Pressed, expectedModifier, expectedRamsesKeyCode)) + EXPECT_CALL(this->m_eventHandlerMock, onKeyEvent(EKeyEvent::Pressed, expectedModifier, expectedRamsesKeyCode)) .Times(1); EXPECT_CALL(this->m_eventHandlerMock, - onKeyEvent(EKeyEventType_Released, EKeyModifier_NoModifier, expectedRamsesKeyCode)) + onKeyEvent(EKeyEvent::Released, KeyModifiers(), expectedRamsesKeyCode)) .Times(1); processAllEvents(); } @@ -231,5 +228,3 @@ namespace ramses_internal EGLContext eglContext = EGL_NO_CONTEXT; }; } - -#endif diff --git a/renderer/Platform/Platform_Android_EGL/CMakeLists.txt b/tests/unittests/renderer/window-wayland-common/CMakeLists.txt similarity index 68% rename from renderer/Platform/Platform_Android_EGL/CMakeLists.txt rename to tests/unittests/renderer/window-wayland-common/CMakeLists.txt index c748ec4e4..9c23d4a30 100644 --- a/renderer/Platform/Platform_Android_EGL/CMakeLists.txt +++ b/tests/unittests/renderer/window-wayland-common/CMakeLists.txt @@ -7,11 +7,11 @@ # ------------------------------------------------------------------------- createModule( - NAME Platform_Android_EGL + NAME window-wayland-common TYPE STATIC_LIBRARY - ENABLE_INSTALL OFF - INCLUDE_PATHS include - SRC_FILES src/*.cpp - DEPENDENCIES Platform_EGL - AndroidSDK + INCLUDE_PATHS . + SRC_FILES *.h + DEPENDENCIES Platform + renderer-test-common + wayland-test-utils ) diff --git a/renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/TestCases.h b/tests/unittests/renderer/window-wayland-common/TestCases.h similarity index 89% rename from renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/TestCases.h rename to tests/unittests/renderer/window-wayland-common/TestCases.h index 4ca49fd85..044676679 100644 --- a/renderer/Platform/Window_Wayland_Test/include/Window_Wayland_Test/TestCases.h +++ b/tests/unittests/renderer/window-wayland-common/TestCases.h @@ -6,10 +6,9 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_TESTCASES_H -#define RAMSES_TESTCASES_H +#pragma once -namespace ramses_internal +namespace ramses::internal { using namespace ::testing; @@ -24,16 +23,16 @@ namespace ramses_internal this->testKeyCode(KEY_TAB, EKeyCode_Tab); this->testKeyCode(KEY_F1, EKeyCode_F1); this->testKeyCode(KEY_SPACE, EKeyCode_Space); - this->testKeyCode(KEY_LEFTSHIFT, EKeyCode_ShiftLeft, EKeyModifier_Shift); - this->testKeyCode(KEY_RIGHTALT, EKeyCode_AltRight, EKeyModifier_Alt); + this->testKeyCode(KEY_LEFTSHIFT, EKeyCode_ShiftLeft, EKeyModifier::Shift); + this->testKeyCode(KEY_RIGHTALT, EKeyCode_AltRight, EKeyModifier::Alt); } TYPED_TEST_P(AWindowWaylandWithEventHandling, leftMouseButtonDownEventTriggersLeftButtonDownEvent) { this->sendMouseButtonEvent(BTN_LEFT, true); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(2); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonDown, _, _)).Times(1); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(2); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonDown, _, _)).Times(1); this->processAllEvents(); // release button to apply default button state for following tests @@ -45,10 +44,10 @@ namespace ramses_internal this->sendMouseButtonEvent(BTN_LEFT, true); // to simulate button release event we have to press it first this->sendMouseButtonEvent(BTN_LEFT, false); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(3); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(3); Sequence seq; - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonDown, _, _)).Times(1).InSequence(seq); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonUp, _, _)).Times(1).InSequence(seq); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonDown, _, _)).Times(1).InSequence(seq); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonUp, _, _)).Times(1).InSequence(seq); this->processAllEvents(); } @@ -56,8 +55,8 @@ namespace ramses_internal { this->sendMouseButtonEvent(BTN_RIGHT, true); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(2); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonDown, _, _)).Times(1); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(2); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonDown, _, _)).Times(1); this->processAllEvents(); // release button to apply default button state for following tests @@ -69,10 +68,10 @@ namespace ramses_internal this->sendMouseButtonEvent(BTN_RIGHT, true); // to simulate button release event we have to press it first this->sendMouseButtonEvent(BTN_RIGHT, false); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(3); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(3); Sequence seq; - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonDown, _, _)).Times(1).InSequence(seq); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonUp, _, _)).Times(1).InSequence(seq); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonDown, _, _)).Times(1).InSequence(seq); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonUp, _, _)).Times(1).InSequence(seq); this->processAllEvents(); } @@ -80,8 +79,8 @@ namespace ramses_internal { this->sendMouseButtonEvent(BTN_MIDDLE, true); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(2); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonDown, _, _)).Times(1); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(2); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonDown, _, _)).Times(1); this->processAllEvents(); // release button to apply default button state for following tests @@ -93,10 +92,10 @@ namespace ramses_internal this->sendMouseButtonEvent(BTN_MIDDLE, true); // to simulate button release event we have to press it first this->sendMouseButtonEvent(BTN_MIDDLE, false); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, _, _)).Times(3); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, _, _)).Times(3); Sequence seq; - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonDown, _, _)).Times(1).InSequence(seq); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonUp, _, _)).Times(1).InSequence(seq); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonDown, _, _)).Times(1).InSequence(seq); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonUp, _, _)).Times(1).InSequence(seq); this->processAllEvents(); } @@ -104,7 +103,7 @@ namespace ramses_internal { this->sendMouseWheelEvent(1); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_WheelUp, _, _)).Times(1); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::WheelUp, _, _)).Times(1); this->processAllEvents(); } @@ -112,7 +111,7 @@ namespace ramses_internal { this->sendMouseWheelEvent(-1); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_WheelDown, _, _)).Times(1); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::WheelDown, _, _)).Times(1); this->processAllEvents(); } @@ -120,7 +119,7 @@ namespace ramses_internal { this->sendMouseMoveEvent(5); - EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEventType_Move, 5, 0)).Times(1); + EXPECT_CALL(this->m_eventHandlerMock, onMouseEvent(EMouseEvent::Move, 5, 0)).Times(1); this->processAllEvents(); } @@ -201,5 +200,3 @@ namespace ramses_internal doesNotSupportExternallyOwnedWindowResizing ); } - -#endif diff --git a/tests/unittests/renderer/window-wayland-ivi/CMakeLists.txt b/tests/unittests/renderer/window-wayland-ivi/CMakeLists.txt new file mode 100644 index 000000000..889a2e1b0 --- /dev/null +++ b/tests/unittests/renderer/window-wayland-ivi/CMakeLists.txt @@ -0,0 +1,21 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME window-wayland-ivi-test + TYPE BINARY + SRC_FILES *.cpp + *.h + DEPENDENCIES Platform + window-wayland-common + ) + +makeTestFromTarget( + TARGET window-wayland-ivi-test + SUFFIX RNDSANDWICHTEST_SWRAST + EXTRA_ARGS --gtest_filter=-*AWindowWaylandWithEventHandling*) diff --git a/renderer/Platform/Window_Wayland_IVI/test/Window_Wayland_IVI_Test.cpp b/tests/unittests/renderer/window-wayland-ivi/Window_Wayland_IVI_Test.cpp similarity index 83% rename from renderer/Platform/Window_Wayland_IVI/test/Window_Wayland_IVI_Test.cpp rename to tests/unittests/renderer/window-wayland-ivi/Window_Wayland_IVI_Test.cpp index 8e2170f06..cd0497c55 100644 --- a/renderer/Platform/Window_Wayland_IVI/test/Window_Wayland_IVI_Test.cpp +++ b/tests/unittests/renderer/window-wayland-ivi/Window_Wayland_IVI_Test.cpp @@ -6,13 +6,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Wayland_Test/AWindowWaylandWithEventHandling.h" -#include "Window_Wayland_Test/TestCases.h" -#include "Window_Wayland_IVI/Window_Wayland_IVI.h" -#include "SystemCompositorController_Wayland_IVI/SystemCompositorController_Wayland_IVI.h" +#include "AWindowWaylandWithEventHandling.h" +#include "TestCases.h" +#include "internal/Platform/Wayland/IVI/Window_Wayland_IVI.h" +#include "internal/Platform/Wayland/IVI/SystemCompositorController/SystemCompositorController_Wayland_IVI.h" #include "PlatformMock.h" -namespace ramses_internal +namespace ramses::internal { template <> void AWindowWaylandWithEventHandling::makeWindowVisible() { diff --git a/renderer/Platform/Window_Wayland_IVI/test/main.cpp b/tests/unittests/renderer/window-wayland-ivi/main.cpp similarity index 85% rename from renderer/Platform/Window_Wayland_IVI/test/main.cpp rename to tests/unittests/renderer/window-wayland-ivi/main.cpp index de87cd423..4bdb9a821 100644 --- a/renderer/Platform/Window_Wayland_IVI/test/main.cpp +++ b/tests/unittests/renderer/window-wayland-ivi/main.cpp @@ -9,8 +9,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "PlatformAbstraction/PlatformConsole.h" -#include "ramses-framework-api/RamsesFrameworkConfig.h" +#include "internal/PlatformAbstraction/PlatformConsole.h" +#include "ramses/framework/RamsesFrameworkConfig.h" int main(int argc, char* argv[]) { diff --git a/tests/unittests/renderer/window-wayland-wl-shell/CMakeLists.txt b/tests/unittests/renderer/window-wayland-wl-shell/CMakeLists.txt new file mode 100644 index 000000000..bdf3f36c9 --- /dev/null +++ b/tests/unittests/renderer/window-wayland-wl-shell/CMakeLists.txt @@ -0,0 +1,21 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME window-wayland-wl-shell-test + TYPE BINARY + SRC_FILES *.cpp + *.h + DEPENDENCIES Platform + window-wayland-common +) + +makeTestFromTarget( + TARGET window-wayland-wl-shell-test + SUFFIX RNDSANDWICHTEST_SWRAST + ) diff --git a/renderer/Platform/Window_Wayland_Shell/test/Window_Wayland_Shell_Test.cpp b/tests/unittests/renderer/window-wayland-wl-shell/Window_Wayland_Shell_Test.cpp similarity index 76% rename from renderer/Platform/Window_Wayland_Shell/test/Window_Wayland_Shell_Test.cpp rename to tests/unittests/renderer/window-wayland-wl-shell/Window_Wayland_Shell_Test.cpp index e9b3fc3da..7dc6601c3 100644 --- a/renderer/Platform/Window_Wayland_Shell/test/Window_Wayland_Shell_Test.cpp +++ b/tests/unittests/renderer/window-wayland-wl-shell/Window_Wayland_Shell_Test.cpp @@ -6,12 +6,12 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "Window_Wayland_Test/AWindowWaylandWithEventHandling.h" -#include "Window_Wayland_Test/TestCases.h" -#include "Window_Wayland_Shell/Window_Wayland_Shell.h" +#include "AWindowWaylandWithEventHandling.h" +#include "TestCases.h" +#include "internal/Platform/Wayland/WlShell/Window_Wayland_Shell.h" #include "PlatformMock.h" -namespace ramses_internal +namespace ramses::internal { INSTANTIATE_TYPED_TEST_SUITE_P(Window_Wayland_Shell_Test, AWindowWayland, Window_Wayland_Shell); } diff --git a/renderer/Platform/Window_Wayland_Shell/test/main.cpp b/tests/unittests/renderer/window-wayland-wl-shell/main.cpp similarity index 100% rename from renderer/Platform/Window_Wayland_Shell/test/main.cpp rename to tests/unittests/renderer/window-wayland-wl-shell/main.cpp diff --git a/tests/unittests/renderer/window-windows/CMakeLists.txt b/tests/unittests/renderer/window-windows/CMakeLists.txt new file mode 100644 index 000000000..174b1f7f0 --- /dev/null +++ b/tests/unittests/renderer/window-windows/CMakeLists.txt @@ -0,0 +1,22 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME window-windows-test + TYPE BINARY + SRC_FILES *.cpp + *.h + DEPENDENCIES Platform + renderer-test-common + ramses-gmock-main +) + +makeTestFromTarget( + TARGET window-windows-test + SUFFIX RNDSANDWICHTEST + ) diff --git a/renderer/Platform/Window_Windows/test/Window_Window_Test.cpp b/tests/unittests/renderer/window-windows/Window_Window_Test.cpp similarity index 77% rename from renderer/Platform/Window_Windows/test/Window_Window_Test.cpp rename to tests/unittests/renderer/window-windows/Window_Window_Test.cpp index 997801564..99e0843a9 100644 --- a/renderer/Platform/Window_Windows/test/Window_Window_Test.cpp +++ b/tests/unittests/renderer/window-windows/Window_Window_Test.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "Window_Windows/Window_Windows.h" +#include "internal/Platform/Windows/Window_Windows.h" #include "WindowEventHandlerMock.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/EKeyModifier.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class AWindowWindows : public testing::Test { @@ -60,13 +60,13 @@ namespace ramses_internal ASSERT_TRUE(PostMessage(window.getNativeWindowHandle(), WM_MOUSELEAVE, 0, 0)); } - void testKeyCode(uint8_t virtualKeyCode, EKeyCode expectedRamesKeyCode, uint32_t expectedModifier = EKeyModifier_NoModifier) + void testKeyCode(uint8_t virtualKeyCode, EKeyCode expectedRamesKeyCode, KeyModifiers expectedModifier = {}) { sendKeyEvent(virtualKeyCode); Sequence seq; - EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEventType_Pressed, expectedModifier, expectedRamesKeyCode)).Times(1).InSequence(seq); - EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEventType_Released, EKeyModifier_NoModifier, expectedRamesKeyCode)).Times(1).InSequence(seq); + EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEvent::Pressed, expectedModifier, expectedRamesKeyCode)).Times(1).InSequence(seq); + EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEvent::Released, KeyModifiers(), expectedRamesKeyCode)).Times(1).InSequence(seq); processAllEvents(); } @@ -141,14 +141,14 @@ namespace ramses_internal testKeyCode(VK_TAB, EKeyCode_Tab); testKeyCode(VK_F1, EKeyCode_F1); testKeyCode(VK_SPACE, EKeyCode_Space); - testKeyCode(VK_SHIFT, EKeyCode_ShiftLeft, EKeyModifier_Shift); + testKeyCode(VK_SHIFT, EKeyCode_ShiftLeft, EKeyModifier::Shift); } TEST_F(AWindowWindows, leftMouseButtonDownEventTriggersLeftButtonDownEvent) { sendMouseEvent(5, 10, WM_LBUTTONDOWN); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonDown, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonDown, 5, 10)).Times(1); processAllEvents(); } @@ -156,7 +156,7 @@ namespace ramses_internal { sendMouseEvent(5, 10, WM_LBUTTONUP); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonUp, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonUp, 5, 10)).Times(1); processAllEvents(); } @@ -164,7 +164,7 @@ namespace ramses_internal { sendMouseEvent(6, 11, WM_RBUTTONDOWN); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonDown, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonDown, 6, 11)).Times(1); processAllEvents(); } @@ -172,7 +172,7 @@ namespace ramses_internal { sendMouseEvent(6, 11, WM_RBUTTONUP); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonUp, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonUp, 6, 11)).Times(1); processAllEvents(); } @@ -180,7 +180,7 @@ namespace ramses_internal { sendMouseEvent(6, 11, WM_MBUTTONDOWN); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonDown, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonDown, 6, 11)).Times(1); processAllEvents(); } @@ -188,7 +188,7 @@ namespace ramses_internal { sendMouseEvent(6, 11, WM_MBUTTONUP); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonUp, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonUp, 6, 11)).Times(1); processAllEvents(); } @@ -200,7 +200,7 @@ namespace ramses_internal POINT pt = {7, 12}; ScreenToClient(window.getNativeWindowHandle(), &pt); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WheelUp, pt.x, pt.y)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WheelUp, pt.x, pt.y)).Times(1); processAllEvents(); } @@ -212,7 +212,7 @@ namespace ramses_internal POINT pt = {7, 12}; ScreenToClient(window.getNativeWindowHandle(), &pt); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WheelDown, pt.x, pt.y)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WheelDown, pt.x, pt.y)).Times(1); processAllEvents(); } @@ -224,7 +224,7 @@ namespace ramses_internal POINT pt = {7, 12}; ScreenToClient(window.getNativeWindowHandle(), &pt); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WheelUp, pt.x, pt.y)).Times(3); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WheelUp, pt.x, pt.y)).Times(3); processAllEvents(); } @@ -232,9 +232,9 @@ namespace ramses_internal { sendMouseEvent(5, 10, WM_MOUSEMOVE); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowEnter, 5, 10)).Times(AnyNumber()); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_Move, 5, 10)).Times(1); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowLeave, 5, 10)).Times(AnyNumber()); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowEnter, 5, 10)).Times(AnyNumber()); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::Move, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowLeave, 5, 10)).Times(AnyNumber()); processAllEvents(); } @@ -243,9 +243,9 @@ namespace ramses_internal sendMouseEvent(5, 10, WM_MOUSEMOVE); // to simulate a window leave event we have to enter the window first sendWindowLeaveEvent(); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowEnter, 5, 10)).Times(AnyNumber()); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_Move, 5, 10)).Times(1); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowLeave, 5, 10)).Times(AtLeast(1)); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowEnter, 5, 10)).Times(AnyNumber()); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::Move, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowLeave, 5, 10)).Times(AtLeast(1)); processAllEvents(); } @@ -254,9 +254,9 @@ namespace ramses_internal sendWindowLeaveEvent(); // to simulate a window enter event we have to leave the window first sendMouseEvent(5, 10, WM_MOUSEMOVE); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowEnter, 5, 10)).Times(1); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_Move, 5, 10)).Times(1); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowLeave, _, _)).Times(AnyNumber()); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowEnter, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::Move, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowLeave, _, _)).Times(AnyNumber()); processAllEvents(); } diff --git a/tests/unittests/renderer/window-x11/CMakeLists.txt b/tests/unittests/renderer/window-x11/CMakeLists.txt new file mode 100644 index 000000000..669d8d6c3 --- /dev/null +++ b/tests/unittests/renderer/window-x11/CMakeLists.txt @@ -0,0 +1,22 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2023 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME window-x11-test + TYPE BINARY + SRC_FILES *.cpp + *.h + DEPENDENCIES Platform + ramses-gmock-main + renderer-test-common +) + +makeTestFromTarget( + TARGET window-x11-test + SUFFIX RNDSANDWICHTEST_SWRAST + ) diff --git a/renderer/Platform/Platform_X11_EGL/test/Window_X11_Test.cpp b/tests/unittests/renderer/window-x11/Window_X11_Test.cpp similarity index 86% rename from renderer/Platform/Platform_X11_EGL/test/Window_X11_Test.cpp rename to tests/unittests/renderer/window-x11/Window_X11_Test.cpp index 755329cad..f631096db 100644 --- a/renderer/Platform/Platform_X11_EGL/test/Window_X11_Test.cpp +++ b/tests/unittests/renderer/window-x11/Window_X11_Test.cpp @@ -7,15 +7,15 @@ // ------------------------------------------------------------------------- #include "gmock/gmock.h" -#include "Platform_X11_EGL/Window_X11.h" +#include "internal/Platform/X11/Window_X11.h" #include "WindowEventHandlerMock.h" -#include "RendererLib/DisplayConfig.h" -#include "RendererLib/EKeyModifier.h" -#include "Utils/ThreadLocalLog.h" +#include "internal/RendererLib/DisplayConfig.h" +#include "internal/RendererLib/Enums/EKeyModifier.h" +#include "internal/Core/Utils/ThreadLocalLog.h" using namespace testing; -namespace ramses_internal +namespace ramses::internal { class WindowX11 : public testing::Test { @@ -152,13 +152,13 @@ namespace ramses_internal XSync(display, static_cast(false)); } - void testKeyCode(uint32_t virtualKeyCode, EKeyCode expectedRamesKeyCode, uint32_t expectedModifier = EKeyModifier_NoModifier) + void testKeyCode(uint32_t virtualKeyCode, EKeyCode expectedRamesKeyCode, KeyModifiers expectedModifier = EKeyModifier::NoModifier) { sendKeyEvent(virtualKeyCode); Sequence seq; - EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEventType_Pressed, expectedModifier, expectedRamesKeyCode)).Times(1).InSequence(seq); - EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEventType_Released, EKeyModifier_NoModifier, expectedRamesKeyCode)).Times(1).InSequence(seq); + EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEvent::Pressed, expectedModifier, expectedRamesKeyCode)).Times(1).InSequence(seq); + EXPECT_CALL(eventHandlerMock, onKeyEvent(EKeyEvent::Released, KeyModifiers(), expectedRamesKeyCode)).Times(1).InSequence(seq); proceseAllEvents(); } @@ -187,14 +187,14 @@ namespace ramses_internal testKeyCode(XK_Tab, EKeyCode_Tab); testKeyCode(XK_F1, EKeyCode_F1); testKeyCode(XK_space, EKeyCode_Space); - testKeyCode(XK_Shift_L, EKeyCode_ShiftLeft, EKeyModifier_Shift); + testKeyCode(XK_Shift_L, EKeyCode_ShiftLeft, EKeyModifier::Shift); } TEST_F(WindowX11, leftMouseButtonDownEventTriggersLeftButtonDownEvent) { sendMouseButtonEvent(5, 10, Button1, true); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonDown, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonDown, 5, 10)).Times(1); proceseAllEvents(); } @@ -202,7 +202,7 @@ namespace ramses_internal { sendMouseButtonEvent(5, 10, Button1, false); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_LeftButtonUp, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::LeftButtonUp, 5, 10)).Times(1); proceseAllEvents(); } @@ -210,7 +210,7 @@ namespace ramses_internal { sendMouseButtonEvent(6, 11, Button3, true); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonDown, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonDown, 6, 11)).Times(1); proceseAllEvents(); } @@ -218,7 +218,7 @@ namespace ramses_internal { sendMouseButtonEvent(6, 11, Button3, false); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_RightButtonUp, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::RightButtonUp, 6, 11)).Times(1); proceseAllEvents(); } @@ -226,7 +226,7 @@ namespace ramses_internal { sendMouseButtonEvent(6, 11, Button2, true); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonDown, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonDown, 6, 11)).Times(1); proceseAllEvents(); } @@ -234,7 +234,7 @@ namespace ramses_internal { sendMouseButtonEvent(6, 11, Button2, false); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_MiddleButtonUp, 6, 11)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::MiddleButtonUp, 6, 11)).Times(1); proceseAllEvents(); } @@ -242,7 +242,7 @@ namespace ramses_internal { sendMouseButtonEvent(7, 12, Button4, true); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WheelUp, 7, 12)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WheelUp, 7, 12)).Times(1); proceseAllEvents(); } @@ -250,7 +250,7 @@ namespace ramses_internal { sendMouseButtonEvent(7, 12, Button5, true); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WheelDown, 7, 12)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WheelDown, 7, 12)).Times(1); proceseAllEvents(); } @@ -258,7 +258,7 @@ namespace ramses_internal { sendMouseMoveEvent(5, 10); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_Move, 5, 10)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::Move, 5, 10)).Times(1); proceseAllEvents(); } @@ -266,7 +266,7 @@ namespace ramses_internal { sendCrossingEvent(false, 30, 50); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowLeave, 30, 50)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowLeave, 30, 50)).Times(1); proceseAllEvents(); } @@ -274,7 +274,7 @@ namespace ramses_internal { sendCrossingEvent(true, 10, 20); - EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEventType_WindowEnter, 10, 20)).Times(1); + EXPECT_CALL(eventHandlerMock, onMouseEvent(EMouseEvent::WindowEnter, 10, 20)).Times(1); proceseAllEvents(); } diff --git a/integration/StressTests/CMakeLists.txt b/tests/unittests/tools/CMakeLists.txt similarity index 80% rename from integration/StressTests/CMakeLists.txt rename to tests/unittests/tools/CMakeLists.txt index 8243bebfd..3297f7200 100644 --- a/integration/StressTests/CMakeLists.txt +++ b/tests/unittests/tools/CMakeLists.txt @@ -1,9 +1,11 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. # ------------------------------------------------------------------------- -ADD_SUBDIRECTORY(ResourceStressTests) +if(ramses-sdk_ENABLE_LOGIC) + add_subdirectory(ramses-logic-viewer) +endif() diff --git a/tests/unittests/tools/ramses-logic-viewer/CMakeLists.txt b/tests/unittests/tools/ramses-logic-viewer/CMakeLists.txt new file mode 100644 index 000000000..d2e113875 --- /dev/null +++ b/tests/unittests/tools/ramses-logic-viewer/CMakeLists.txt @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2022 BMW AG +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +createModule( + NAME ramses-logic-viewer-test + TYPE BINARY + ENABLE_INSTALL ON + INCLUDE_PATHS ${PROJECT_SOURCE_DIR}/tools/ramses-logic-viewer + ${PROJECT_SOURCE_DIR}/tests/unittests/client/logic/shared + SRC_FILES LogicViewerTestBase.h + LogicViewerTestBase.cpp + LogicViewerTest.cpp + LogicViewerLuaTest.cpp + DEPENDENCIES ramses-logic-viewer-lib + ramses-client + ramses-gmock + ramses-gmock-main +) + +MakeTestFromTarget( + TARGET ramses-logic-viewer-test + SUFFIX UNITTEST) diff --git a/client/logic/tools/ramses-logic-viewer-test/LogicViewerLuaTest.cpp b/tests/unittests/tools/ramses-logic-viewer/LogicViewerLuaTest.cpp similarity index 89% rename from client/logic/tools/ramses-logic-viewer-test/LogicViewerLuaTest.cpp rename to tests/unittests/tools/ramses-logic-viewer/LogicViewerLuaTest.cpp index 1790dbd5c..41be4ccab 100644 --- a/client/logic/tools/ramses-logic-viewer-test/LogicViewerLuaTest.cpp +++ b/tests/unittests/tools/ramses-logic-viewer/LogicViewerLuaTest.cpp @@ -7,10 +7,8 @@ // ------------------------------------------------------------------------- #include "LogicViewerTestBase.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" - -const char* const logicFile = "test.rlogic"; +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" namespace ramses::internal { @@ -22,13 +20,12 @@ namespace ramses::internal public: ALogicViewerLua() { - createLogicFile(); - EXPECT_TRUE(viewer.loadRamsesLogic(logicFile, m_scene)); + setupLogic(); } - void createLogicFile() + void setupLogic() { - LogicEngine engine{ ramses::EFeatureLevel_Latest }; + LogicEngine& engine = *m_logic; auto *interface = engine.createLuaInterface(R"( function interface(IN,OUT) IN.paramInt32 = Type:Int32() @@ -104,21 +101,21 @@ namespace ramses::internal ASSERT_TRUE(script != nullptr); - auto* nodeBinding = engine.createRamsesNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "foo"); + auto* nodeBinding = engine.createNodeBinding(*m_node, ramses::ERotationType::Euler_XYZ, "foo"); // make camera valid m_camera->setFrustum(-1.f, 1.f, -1.f, 1.f, 0.1f, 10.f); - auto* appearanceBind = engine.createRamsesAppearanceBinding(*m_appearance, "foo"); - auto* cameraBinding = engine.createRamsesCameraBinding(*m_camera, "foo"); - auto* passBinding = engine.createRamsesRenderPassBinding(*m_renderPass, "foo"); + auto* appearanceBind = engine.createAppearanceBinding(*m_appearance, "foo"); + auto* cameraBinding = engine.createCameraBinding(*m_camera, "foo"); + auto* passBinding = engine.createRenderPassBinding(*m_renderPass, "foo"); auto* timer = engine.createTimerNode("foo"); auto* anchor = engine.createAnchorPoint(*nodeBinding, *cameraBinding, "foo"); m_renderGroup->addRenderGroup(*m_nestedRenderGroup); - ramses::RamsesRenderGroupBindingElements elements; + ramses::RenderGroupBindingElements elements; elements.addElement(*m_nestedRenderGroup, "nestedRG"); - auto rgBinding = engine.createRamsesRenderGroupBinding(*m_renderGroup, elements, "rg"); - auto meshBinding = engine.createRamsesMeshNodeBinding(*m_meshNode, "mn"); + auto rgBinding = engine.createRenderGroupBinding(*m_renderGroup, elements, "rg"); + auto meshBinding = engine.createMeshNodeBinding(*m_meshNode, "mn"); ramses::DataArray* animTimestamps = engine.createDataArray(std::vector{ 0.f, 0.5f, 1.f, 1.5f }); // will be interpreted as seconds ramses::DataArray* animKeyframes = engine.createDataArray(std::vector{ {0.f, 0.f, 0.f}, {0.f, 0.f, 180.f}, {0.f, 0.f, 100.f}, {0.f, 0.f, 360.f} }); @@ -180,15 +177,13 @@ namespace ramses::internal *script->getInputs()->getChild("anchorData2")); engine.update(); - - engine.saveToFile(logicFile); } - void unlinkInput(const ramses::Property& inputProperty) + void unlinkInput(ramses::Property& inputProperty) { assert(inputProperty.hasIncomingLink()); - const ramses::Property& sourceProperty = *inputProperty.getIncomingLink()->source; - EXPECT_TRUE(viewer.getEngine().unlink(sourceProperty, inputProperty)); + ramses::Property& sourceProperty = *inputProperty.getIncomingLink()->source; + EXPECT_TRUE(viewer.getLogic().unlink(sourceProperty, inputProperty)); } }; @@ -324,8 +319,9 @@ namespace ramses::internal TEST_F(ALogicViewerLua, setInputArrayByIndexOutOfBounds) { - EXPECT_THAT(loadLua(R"(rlogic.scripts.foo.IN.array[6].value = 14)").getMessage(), - testing::HasSubstr("attempt to index field '?' (a nil value)")); + auto result = loadLua(R"(rlogic.scripts.foo.IN.array[6].value = 14)"); + EXPECT_THAT(result.getMessage(), testing::HasSubstr("attempt to index")); + EXPECT_THAT(result.getMessage(), testing::HasSubstr("a nil value")); } TEST_F(ALogicViewerLua, setInputArrayByIndexBadSyntax) @@ -484,9 +480,9 @@ namespace ramses::internal TEST_F(ALogicViewerLua, getOutputArrayOutOfBounds) { - EXPECT_THAT(loadLua(R"( - rlogic.scripts.foo.IN.paramString.value = rlogic.scripts.foo.OUT.array[6].value - )").getMessage(), testing::HasSubstr("attempt to index field '?' (a nil value)")); + const auto result = loadLua(R"(rlogic.scripts.foo.IN.paramString.value = rlogic.scripts.foo.OUT.array[6].value)"); + EXPECT_THAT(result.getMessage(), testing::HasSubstr("attempt to index")); + EXPECT_THAT(result.getMessage(), testing::HasSubstr("a nil value")); } TEST_F(ALogicViewerLua, getOutputStruct) @@ -505,7 +501,7 @@ namespace ramses::internal EXPECT_EQ(Result(), loadLua(R"( rlogic.scripts.foo.IN.paramString.value = tostring(rlogic.scripts.foo.OUT.paramInt32) )")); - EXPECT_EQ("ConstProperty: paramInt32", getInput("foo", "paramString")->get().value()); + EXPECT_EQ("Property: paramInt32", getInput("foo", "paramString")->get().value()); } TEST_F(ALogicViewerLua, inputPropertyToString) @@ -757,7 +753,7 @@ namespace ramses::internal TEST_F(ALogicViewerLua, nodeBindingByName) { - auto* translation = getInput("foo", "translation"); + auto* translation = getInput("foo", "translation"); EXPECT_EQ(Result(), loadLua(R"( rlogic.nodeBindings.foo.IN.translation.value = {1,2,3} )")); @@ -768,7 +764,7 @@ namespace ramses::internal TEST_F(ALogicViewerLua, appearanceBindingByName) { - auto* floatUniform = getInput("foo", "floatUniform"); + auto* floatUniform = getInput("foo", "floatUniform"); //unlink input to avoid generating error for setting value for a linked input unlinkInput(*floatUniform); @@ -780,7 +776,7 @@ namespace ramses::internal TEST_F(ALogicViewerLua, cameraBindingByName) { - auto* frustum = getInput("foo", "frustum"); + auto* frustum = getInput("foo", "frustum"); EXPECT_EQ(Result(), loadLua(R"( rlogic.cameraBindings.foo.IN.frustum.nearPlane.value = 0.93 )")); @@ -789,7 +785,7 @@ namespace ramses::internal TEST_F(ALogicViewerLua, renderPassBindingByName) { - auto* renderOrder = getInput("foo", "renderOrder"); + auto* renderOrder = getInput("foo", "renderOrder"); EXPECT_EQ(Result(), loadLua(R"( rlogic.renderPassBindings.foo.IN.renderOrder.value = 42 )")); @@ -798,7 +794,7 @@ namespace ramses::internal TEST_F(ALogicViewerLua, renderGroupBindingByName) { - auto* renderOrder = getInput("rg", "renderOrders")->getChild("nestedRG"); + auto* renderOrder = getInput("rg", "renderOrders")->getChild("nestedRG"); //unlink input to avoid generating error for setting value for a linked input unlinkInput(*renderOrder); @@ -810,10 +806,10 @@ namespace ramses::internal TEST_F(ALogicViewerLua, meshNodeBindingByName) { - auto* vertexOffset = getInput("mn", "vertexOffset"); - auto* indexOffset = getInput("mn", "indexOffset"); - auto* indexCount = getInput("mn", "indexCount"); - auto* instanceCount = getInput("mn", "instanceCount"); + auto* vertexOffset = getInput("mn", "vertexOffset"); + auto* indexOffset = getInput("mn", "indexOffset"); + auto* indexCount = getInput("mn", "indexCount"); + auto* instanceCount = getInput("mn", "instanceCount"); //unlink input to avoid generating error for setting value for a linked input unlinkInput(*vertexOffset); @@ -880,26 +876,26 @@ namespace ramses::internal TEST_F(ALogicViewerLua, interfaceById) { auto* node = getNode("foo"); - ASSERT_EQ(1u, node->getId()); - EXPECT_EQ(Result(), loadLua(R"(rlogic.interfaces[1].IN.paramInt32.value = 42)")); + ASSERT_EQ(10u, node->getSceneObjectId().getValue()); + EXPECT_EQ(Result(), loadLua(R"(rlogic.interfaces[10].IN.paramInt32.value = 42)")); EXPECT_EQ(42, GetInput(node, "paramInt32")->get().value()); } TEST_F(ALogicViewerLua, scriptById) { auto* node = getNode("foo"); - ASSERT_EQ(2u, node->getId()); - EXPECT_EQ(Result(), loadLua(R"(rlogic.scripts[2].IN.paramInt64.value = 99)")); + ASSERT_EQ(11u, node->getSceneObjectId().getValue()); + EXPECT_EQ(Result(), loadLua(R"(rlogic.scripts[11].IN.paramInt64.value = 99)")); EXPECT_EQ(99, GetInput(node, "paramInt64")->get().value()); } TEST_F(ALogicViewerLua, nodeBindingById) { - auto* node = getNode("foo"); - ASSERT_EQ(3u, node->getId()); + auto* node = getNode("foo"); + ASSERT_EQ(12u, node->getSceneObjectId().getValue()); auto* translation = GetInput(node, "translation"); EXPECT_EQ(Result(), loadLua(R"( - rlogic.nodeBindings[3].IN.translation.value = {1,2,3} + rlogic.nodeBindings[12].IN.translation.value = {1,2,3} )")); EXPECT_FLOAT_EQ(1.f, translation->get().value()[0]); EXPECT_FLOAT_EQ(2.f, translation->get().value()[1]); @@ -908,58 +904,58 @@ namespace ramses::internal TEST_F(ALogicViewerLua, appearanceBindingById) { - auto* node = getNode("foo"); - ASSERT_EQ(4u, node->getId()); + auto* node = getNode("foo"); + ASSERT_EQ(13u, node->getSceneObjectId().getValue()); auto* floatUniform = GetInput(node, "floatUniform"); //unlink input to avoid generating error for setting value for a linked input unlinkInput(*floatUniform); EXPECT_EQ(Result(), loadLua(R"( - rlogic.appearanceBindings[4].IN.floatUniform.value = 9.1 + rlogic.appearanceBindings[13].IN.floatUniform.value = 9.1 )")); EXPECT_FLOAT_EQ(9.1f, floatUniform->get().value()); } TEST_F(ALogicViewerLua, cameraBindingById) { - auto* node = getNode("foo"); - ASSERT_EQ(5u, node->getId()); + auto* node = getNode("foo"); + ASSERT_EQ(14u, node->getSceneObjectId().getValue()); auto* frustum = GetInput(node, "frustum"); EXPECT_EQ(Result(), loadLua(R"( - rlogic.cameraBindings[5].IN.frustum.nearPlane.value = 0.93 + rlogic.cameraBindings[14].IN.frustum.nearPlane.value = 0.93 )")); EXPECT_FLOAT_EQ(0.93f, frustum->getChild("nearPlane")->get().value()); } TEST_F(ALogicViewerLua, renderPassBindingById) { - auto* rp = getNode("foo"); - ASSERT_EQ(6u, rp->getId()); + auto* rp = getNode("foo"); + ASSERT_EQ(15u, rp->getSceneObjectId().getValue()); auto* renderOrder = GetInput(rp, "renderOrder"); EXPECT_EQ(Result(), loadLua(R"( - rlogic.renderPassBindings[6].IN.renderOrder.value = 42 + rlogic.renderPassBindings[15].IN.renderOrder.value = 42 )")); EXPECT_EQ(42, renderOrder->get().value()); } TEST_F(ALogicViewerLua, renderGroupBindingById) { - auto* rg = getNode("rg"); - ASSERT_EQ(9u, rg->getId()); + auto* rg = getNode("rg"); + ASSERT_EQ(18u, rg->getSceneObjectId().getValue()); auto* renderOrder = GetInput(rg, "renderOrders")->getChild("nestedRG"); //unlink input to avoid generating error for setting value for a linked input unlinkInput(*renderOrder); EXPECT_EQ(Result(), loadLua(R"( - rlogic.renderGroupBindings[9].IN.renderOrders.nestedRG.value = 42 + rlogic.renderGroupBindings[18].IN.renderOrders.nestedRG.value = 42 )")); EXPECT_EQ(42, renderOrder->get().value()); } TEST_F(ALogicViewerLua, meshNodeBindingById) { - auto* mn = getNode("mn"); - ASSERT_EQ(10u, mn->getId()); + auto* mn = getNode("mn"); + ASSERT_EQ(19u, mn->getSceneObjectId().getValue()); auto* vertexOffset = GetInput(mn, "vertexOffset"); auto* indexOffset = GetInput(mn, "indexOffset"); auto* indexCount = GetInput(mn, "indexCount"); @@ -969,10 +965,10 @@ namespace ramses::internal unlinkInput(*vertexOffset); EXPECT_EQ(Result(), loadLua(R"( - rlogic.meshNodeBindings[10].IN.vertexOffset.value = 42 - rlogic.meshNodeBindings[10].IN.indexOffset.value = 43 - rlogic.meshNodeBindings[10].IN.indexCount.value = 44 - rlogic.meshNodeBindings[10].IN.instanceCount.value = 45 + rlogic.meshNodeBindings[19].IN.vertexOffset.value = 42 + rlogic.meshNodeBindings[19].IN.indexOffset.value = 43 + rlogic.meshNodeBindings[19].IN.indexCount.value = 44 + rlogic.meshNodeBindings[19].IN.instanceCount.value = 45 )")); EXPECT_EQ(42, vertexOffset->get().value()); EXPECT_EQ(43, indexOffset->get().value()); @@ -983,10 +979,10 @@ namespace ramses::internal TEST_F(ALogicViewerLua, timerNodeById) { auto* node = getNode("foo"); - ASSERT_EQ(7u, node->getId()); + ASSERT_EQ(16u, node->getSceneObjectId().getValue()); auto* ticker = GetInput(node, "ticker_us"); EXPECT_EQ(Result(), loadLua(R"( - rlogic.timerNodes[7].IN.ticker_us.value = 19083 + rlogic.timerNodes[16].IN.ticker_us.value = 19083 )")); EXPECT_EQ(19083, ticker->get().value()); } @@ -998,8 +994,8 @@ namespace ramses::internal unlinkInput(*getInput("foo", "anchorData2")); EXPECT_EQ(Result(), loadLua(R"( - rlogic.scripts.foo.IN.anchorData1.value = { rlogic.anchorPoints[8].OUT.viewportCoords.value[1], rlogic.anchorPoints[8].OUT.viewportCoords.value[2] } - rlogic.scripts.foo.IN.anchorData2.value = rlogic.anchorPoints[8].OUT.depth.value + rlogic.scripts.foo.IN.anchorData1.value = { rlogic.anchorPoints[17].OUT.viewportCoords.value[1], rlogic.anchorPoints[17].OUT.viewportCoords.value[2] } + rlogic.scripts.foo.IN.anchorData2.value = rlogic.anchorPoints[17].OUT.depth.value )")); auto* outScript = getNode("foo"); @@ -1011,13 +1007,13 @@ namespace ramses::internal TEST_F(ALogicViewerLua, animationNodeById) { auto* node = getNode("foo"); - ASSERT_EQ(13u, node->getId()); + ASSERT_EQ(22u, node->getSceneObjectId().getValue()); auto* progress = GetInput(node, "progress"); //unlink input to avoid generating error for setting value for a linked input unlinkInput(*progress); EXPECT_EQ(Result(), loadLua(R"( - rlogic.animationNodes[13].IN.progress.value = 198 + rlogic.animationNodes[22].IN.progress.value = 198 )")); EXPECT_FLOAT_EQ(198.f, progress->get().value()); } @@ -1025,10 +1021,10 @@ namespace ramses::internal TEST_F(ALogicViewerLua, animationNodeWrongId) { auto* node = getNode("foo"); - ASSERT_EQ(13u, node->getId()); - EXPECT_THAT(loadLua(R"( - rlogic.animationNodes[89032].IN.progress.value = 198 - )").getMessage(), testing::HasSubstr("attempt to index field '?' (a nil value)")); + ASSERT_EQ(22u, node->getSceneObjectId().getValue()); + const auto result = loadLua(R"(rlogic.animationNodes[89032].IN.progress.value = 198)"); + EXPECT_THAT(result.getMessage(), testing::HasSubstr("attempt to index")); + EXPECT_THAT(result.getMessage(), testing::HasSubstr("a nil value")); } TEST_F(ALogicViewerLua, iterateAnimationNodes) @@ -1064,7 +1060,6 @@ namespace ramses::internal // set update interval to 1 to avoid random test failures // (only the longest update is reported for an interval) const size_t updateInterval = 1u; // in frames - EXPECT_TRUE(viewer.loadRamsesLogic(logicFile, m_scene)); EXPECT_FALSE(viewer.isUpdateReportEnabled()); auto& summary = viewer.getUpdateReport(); @@ -1079,7 +1074,7 @@ namespace ramses::internal EXPECT_EQ(Result(), viewer.update()); EXPECT_EQ(2u, summary.getLinkActivations().maxValue); - EXPECT_EQ(11u, summary.getNodesExecuted().size()); - EXPECT_EQ(0u, summary.getNodesSkippedExecution().size()); + EXPECT_EQ(4u, summary.getNodesExecuted().size()); + EXPECT_EQ(7u, summary.getNodesSkippedExecution().size()); } } diff --git a/client/logic/tools/ramses-logic-viewer-test/LogicViewerTest.cpp b/tests/unittests/tools/ramses-logic-viewer/LogicViewerTest.cpp similarity index 56% rename from client/logic/tools/ramses-logic-viewer-test/LogicViewerTest.cpp rename to tests/unittests/tools/ramses-logic-viewer/LogicViewerTest.cpp index d5ba398c5..c0ff29ae5 100644 --- a/client/logic/tools/ramses-logic-viewer-test/LogicViewerTest.cpp +++ b/tests/unittests/tools/ramses-logic-viewer/LogicViewerTest.cpp @@ -7,52 +7,25 @@ // ------------------------------------------------------------------------- #include "LogicViewerTestBase.h" - -const char* const logicFile = "test.rlogic"; +#include "ramses/client/Scene.h" namespace ramses::internal { class ALogicViewer : public ALogicViewerBase { - public: - ALogicViewer() - { - createLogicFile(); - } - - static void createLogicFile() - { - LogicEngine engine{ ramses::EFeatureLevel_Latest }; - engine.createLuaScript(R"( - function interface(IN,OUT) - IN.paramInt32 = Type:Int32() - end - )", - {} , "foo"); - engine.saveToFile(logicFile); - } }; - TEST_F(ALogicViewer, loadRamsesLogicNotAFile) - { - EXPECT_FALSE(viewer.loadRamsesLogic("notAFile", m_scene)); - } - TEST_F(ALogicViewer, loadRamsesLogic) { - EXPECT_TRUE(viewer.loadRamsesLogic(logicFile, m_scene)); - EXPECT_EQ(logicFile, viewer.getLogicFilename()); EXPECT_EQ("", viewer.getLuaFilename()); EXPECT_EQ(Result(), viewer.getLastResult()); } TEST_F(ALogicViewer, loadLuaFileNotAFile) { - EXPECT_TRUE(viewer.loadRamsesLogic(logicFile, m_scene)); auto result = viewer.loadLuaFile("notAFile"); EXPECT_FALSE(result.ok()); EXPECT_EQ("cannot open notAFile: No such file or directory", result.getMessage()); - EXPECT_EQ(logicFile, viewer.getLogicFilename()); EXPECT_EQ("notAFile", viewer.getLuaFilename()); EXPECT_EQ(result, viewer.getLastResult()); } diff --git a/client/logic/tools/ramses-logic-viewer-test/LogicViewerTestBase.cpp b/tests/unittests/tools/ramses-logic-viewer/LogicViewerTestBase.cpp similarity index 100% rename from client/logic/tools/ramses-logic-viewer-test/LogicViewerTestBase.cpp rename to tests/unittests/tools/ramses-logic-viewer/LogicViewerTestBase.cpp diff --git a/client/logic/tools/ramses-logic-viewer-test/LogicViewerTestBase.h b/tests/unittests/tools/ramses-logic-viewer/LogicViewerTestBase.h similarity index 85% rename from client/logic/tools/ramses-logic-viewer-test/LogicViewerTestBase.h rename to tests/unittests/tools/ramses-logic-viewer/LogicViewerTestBase.h index 9612aa3fb..b2afa9066 100644 --- a/client/logic/tools/ramses-logic-viewer-test/LogicViewerTestBase.h +++ b/tests/unittests/tools/ramses-logic-viewer/LogicViewerTestBase.h @@ -13,19 +13,19 @@ #include "RamsesTestUtils.h" #include "WithTempDirectory.h" #include -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-client.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/ramses-client.h" #include @@ -87,7 +87,7 @@ namespace ramses::internal template T* getNode(std::string_view nodeName) { - auto* node = viewer.getEngine().findByName(nodeName); + auto* node = viewer.getLogic().findObject(nodeName); if (node == nullptr) { throw(std::runtime_error(fmt::format("Node not found: '{}'", nodeName))); @@ -134,7 +134,6 @@ namespace ramses::internal protected: WithTempDirectory m_withTempDirectory; - LogicViewer viewer{ ramses::EFeatureLevel_Latest, doScreenshot }; RamsesTestSetup m_ramses; ramses::Scene* m_scene = { m_ramses.createScene() }; ramses::Node* m_node = { m_scene->createNode() }; @@ -144,6 +143,8 @@ namespace ramses::internal ramses::RenderGroup* m_renderGroup = { m_scene->createRenderGroup() }; ramses::RenderGroup* m_nestedRenderGroup = { m_scene->createRenderGroup() }; ramses::MeshNode* m_meshNode = { m_scene->createMeshNode() }; + ramses::LogicEngine* m_logic = { m_scene->createLogicEngine() }; + LogicViewer viewer{ *m_logic, doScreenshot }; private: // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) must be static and non-const (see SetMockScreenshot) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000..3df1b4ba6 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------------- +# Copyright (C) 2018 BMW Car IT GmbH +# ------------------------------------------------------------------------- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# ------------------------------------------------------------------------- + +add_subdirectory(ramses-daemon) + +if(ramses-sdk_ENABLE_LOGIC) + add_subdirectory(ramses-logic-viewer) + if(ramses-sdk_BUILD_FULL_SHARED_LIB) + add_subdirectory(test-asset-producer) + endif() +endif() + +if(TARGET ramses-renderer) + add_subdirectory(ramses-renderer-standalone) + add_subdirectory(ramses-imgui) + add_subdirectory(ramses-scene-viewer) + add_subdirectory(ramses-stream-viewer) +endif() diff --git a/ramses-daemon/CMakeLists.txt b/tools/ramses-daemon/CMakeLists.txt similarity index 100% rename from ramses-daemon/CMakeLists.txt rename to tools/ramses-daemon/CMakeLists.txt diff --git a/ramses-daemon/main.cpp b/tools/ramses-daemon/main.cpp similarity index 73% rename from ramses-daemon/main.cpp rename to tools/ramses-daemon/main.cpp index a433082fe..abc129d68 100644 --- a/ramses-daemon/main.cpp +++ b/tools/ramses-daemon/main.cpp @@ -6,21 +6,22 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "TransportCommon/CommunicationSystemFactory.h" -#include "TransportCommon/IDiscoveryDaemon.h" +#include "internal/Communication/TransportCommon/CommunicationSystemFactory.h" +#include "internal/Communication/TransportCommon/IDiscoveryDaemon.h" #include "ramses-sdk-build-config.h" -#include "Common/ParticipantIdentifier.h" -#include "RamsesFrameworkConfigImpl.h" -#include "Ramsh/RamshStandardSetup.h" -#include "Ramsh/RamshCommandExit.h" -#include "Utils/RamsesLogger.h" -#include "Utils/StatisticCollection.h" +#include "internal/Core/Common/ParticipantIdentifier.h" +#include "impl/RamsesFrameworkConfigImpl.h" +#include "internal/Ramsh/RamshStandardSetup.h" +#include "internal/Ramsh/RamshCommandExit.h" +#include "internal/Core/Utils/RamsesLogger.h" +#include "internal/Core/Utils/StatisticCollection.h" +#include "internal/Core/Utils/LogMacros.h" #include #include "ramses-framework-cli.h" int main(int argc, const char* argv[]) { - using namespace ramses_internal; + using namespace ramses::internal; ramses::RamsesFrameworkConfig config{ramses::EFeatureLevel_Latest}; config.setDLTApplicationDescription("ramses-daemon"); @@ -39,7 +40,7 @@ int main(int argc, const char* argv[]) } CLI11_PARSE(cli, argc, argv); - GetRamsesLogger().initialize(config.m_impl.get().loggerConfig, false, true); // no framework used + GetRamsesLogger().initialize(config.impl().loggerConfig, false, true); // no framework used auto commandExit = std::make_shared(); RamshStandardSetup ramsh(ramses::ERamsesShellType::Console, "Daemon"); @@ -52,7 +53,7 @@ int main(int argc, const char* argv[]) PlatformLock frameworkLock; StatisticCollectionFramework statisticCollection; - std::unique_ptr discoveryDaemon(CommunicationSystemFactory::ConstructDiscoveryDaemon(config.m_impl, frameworkLock, statisticCollection, &ramsh)); + std::unique_ptr discoveryDaemon(CommunicationSystemFactory::ConstructDiscoveryDaemon(config.impl(), frameworkLock, statisticCollection, &ramsh)); discoveryDaemon->start(); LOG_DEBUG(CONTEXT_SMOKETEST, "Ramsh commands registered"); diff --git a/utils/ramses-imgui/CMakeLists.txt b/tools/ramses-imgui/CMakeLists.txt similarity index 94% rename from utils/ramses-imgui/CMakeLists.txt rename to tools/ramses-imgui/CMakeLists.txt index 1d372f559..713533625 100644 --- a/utils/ramses-imgui/CMakeLists.txt +++ b/tools/ramses-imgui/CMakeLists.txt @@ -14,6 +14,5 @@ createModule( SRC_FILES include/* src/*.cpp DEPENDENCIES ramses-client - ramses-renderer-api imgui ) diff --git a/utils/ramses-imgui/include/ImguiClientHelper.h b/tools/ramses-imgui/include/ImguiClientHelper.h similarity index 70% rename from utils/ramses-imgui/include/ImguiClientHelper.h rename to tools/ramses-imgui/include/ImguiClientHelper.h index 65c768a7b..594a82d36 100644 --- a/utils/ramses-imgui/include/ImguiClientHelper.h +++ b/tools/ramses-imgui/include/ImguiClientHelper.h @@ -20,19 +20,19 @@ #pragma GCC diagnostic pop #endif -#include "ramses-client.h" -#include "ramses-utils.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" #include #include #include #include -namespace ramses_internal +namespace ramses::internal { class ImguiClientHelper : public ramses::RendererEventHandlerEmpty, public ramses::RendererSceneControlEventHandlerEmpty { @@ -65,7 +65,7 @@ namespace ramses_internal bool saveScreenshot(const std::string& filename, ramses::displayBufferId_t screenshotBuf, uint32_t x, uint32_t y, uint32_t width, uint32_t height); bool waitForDisplay(ramses::displayId_t displayId); - bool waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); + bool waitForSceneState(ramses::Scene& scene, ramses::RendererSceneState state); bool waitForSceneVersion(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t version); bool waitForOffscreenBufferCreated(const ramses::displayBufferId_t offscreenBufferId); bool waitForOffscreenBufferLinked(const ramses::sceneId_t sceneId); @@ -81,7 +81,7 @@ namespace ramses_internal void displayCreated(ramses::displayId_t displayId, ramses::ERendererEventResult result) override; void displayDestroyed(ramses::displayId_t displayId, ramses::ERendererEventResult result) override; void mouseEvent(ramses::displayId_t displayId, ramses::EMouseEvent eventType, int32_t mousePosX, int32_t mousePosY) override; - void keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, uint32_t keyModifiers, ramses::EKeyCode keyCode) override; + void keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) override; void windowResized(ramses::displayId_t displayId, uint32_t width, uint32_t height) override; void windowClosed(ramses::displayId_t displayId) override; void framebufferPixelsRead(const uint8_t* pixelData, @@ -91,7 +91,7 @@ namespace ramses_internal ramses::ERendererEventResult result) override; private: - bool waitUntil(const std::function& conditionFunction); + bool waitUntil(const std::function& conditionFunction, ramses::Scene* scene = nullptr); struct SceneInfo { @@ -103,30 +103,30 @@ namespace ramses_internal using OffscreenBufferSet = std::unordered_set; using DisplaySet = std::unordered_set; - SceneSet m_scenes; - SceneSet m_scenesAssignedToOffscreenBuffer; - SceneSet m_scenesConsumingOffscreenBuffer; - DisplaySet m_displays; - OffscreenBufferSet m_offscreenBuffers; - ramses::RamsesRenderer* m_renderer = nullptr; - ramses::displayId_t m_displayId; - ramses::Scene* m_imguiscene = nullptr; - ramses::OrthographicCamera* imguicamera = nullptr; - ramses::TextureSampler* sampler = nullptr; - ramses::Effect* effect = nullptr; - ramses::RenderGroup* renderGroup = nullptr; - ramses::UniformInput textureInput; - ramses::AttributeInput inputPosition; - ramses::AttributeInput inputUV; - ramses::AttributeInput inputColor; - std::vector todeleteMeshes; - std::vector todeleteRes; - std::pair m_clickEvent; - std::string m_screenshot; - uint32_t m_screenshotWidth = 0U; - uint32_t m_screenshotHeight = 0U; - bool m_screenshotSaved = false; - bool m_isRunning = true; + SceneSet m_scenes; + SceneSet m_scenesAssignedToOffscreenBuffer; + SceneSet m_scenesConsumingOffscreenBuffer; + DisplaySet m_displays; + OffscreenBufferSet m_offscreenBuffers; + ramses::RamsesRenderer* m_renderer = nullptr; + ramses::displayId_t m_displayId; + ramses::Scene* m_imguiscene = nullptr; + ramses::OrthographicCamera* imguicamera = nullptr; + ramses::TextureSampler* sampler = nullptr; + ramses::Effect* effect = nullptr; + ramses::RenderGroup* renderGroup = nullptr; + std::optional textureInput{std::nullopt}; + std::optional inputPosition{std::nullopt}; + std::optional inputUV{std::nullopt}; + std::optional inputColor{std::nullopt}; + std::vector todeleteMeshes; + std::vector todeleteRes; + std::pair m_clickEvent; + std::string m_screenshot; + uint32_t m_screenshotWidth = 0U; + uint32_t m_screenshotHeight = 0U; + bool m_screenshotSaved = false; + bool m_isRunning = true; }; inline ramses::Scene* ImguiClientHelper::getScene() diff --git a/utils/ramses-imgui/include/ImguiImageCache.h b/tools/ramses-imgui/include/ImguiImageCache.h similarity index 80% rename from utils/ramses-imgui/include/ImguiImageCache.h rename to tools/ramses-imgui/include/ImguiImageCache.h index 181d49537..292b46a67 100644 --- a/utils/ramses-imgui/include/ImguiImageCache.h +++ b/tools/ramses-imgui/include/ImguiImageCache.h @@ -11,18 +11,22 @@ #include "ImguiWidgets.h" #include -namespace ramses_internal +namespace ramses::internal { + struct MipMap; + class ImguiImageCache { public: explicit ImguiImageCache(ramses::Scene* scene); imgui::Image get(const TextureResource* res); + imgui::Image get(const MipMap& mm, EPixelStorageFormat format); private: ramses::Scene* m_scene; std::unordered_map m_images; + std::unordered_map m_imageBuffers; }; } diff --git a/utils/ramses-imgui/include/ImguiWidgets.h b/tools/ramses-imgui/include/ImguiWidgets.h similarity index 84% rename from utils/ramses-imgui/include/ImguiWidgets.h rename to tools/ramses-imgui/include/ImguiWidgets.h index 5a0db2c6e..8a263c6b4 100644 --- a/utils/ramses-imgui/include/ImguiWidgets.h +++ b/tools/ramses-imgui/include/ImguiWidgets.h @@ -8,7 +8,7 @@ #pragma once -#include "Utils/Warnings.h" +#include "internal/Core/Utils/Warnings.h" WARNINGS_PUSH WARNING_DISABLE_LINUX(-Wold-style-cast) @@ -19,10 +19,10 @@ WARNING_DISABLE_LINUX(-Wold-style-cast) WARNINGS_POP #include "absl/types/span.h" -#include "SceneAPI/TextureEnums.h" -#include "Resource/TextureMetaInfo.h" +#include "internal/SceneGraph/SceneAPI/TextureEnums.h" +#include "internal/SceneGraph/Resource/TextureMetaInfo.h" #include -#include "SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" namespace ramses @@ -31,7 +31,7 @@ namespace ramses class TextureSampler; } -namespace ramses_internal +namespace ramses::internal { class TextureResource; class IScene; @@ -60,9 +60,9 @@ namespace ramses_internal void RenderState(IScene& scene, RenderStateHandle hnd); - std::string SaveToPng(const Byte* data, + std::string SaveToPng(const uint8_t* data, size_t size, - ETextureFormat fmt, + EPixelStorageFormat fmt, uint32_t width, uint32_t height, const std::string& filename, diff --git a/utils/ramses-imgui/src/ImguiClientHelper.cpp b/tools/ramses-imgui/src/ImguiClientHelper.cpp similarity index 93% rename from utils/ramses-imgui/src/ImguiClientHelper.cpp rename to tools/ramses-imgui/src/ImguiClientHelper.cpp index b156f5df8..70621dff2 100644 --- a/utils/ramses-imgui/src/ImguiClientHelper.cpp +++ b/tools/ramses-imgui/src/ImguiClientHelper.cpp @@ -8,7 +8,7 @@ #include "ImguiClientHelper.h" -namespace ramses_internal +namespace ramses::internal { namespace { @@ -80,9 +80,9 @@ namespace ramses_internal // emulates an English keyboard for the available ramses key events // (ramses (on Windows) does not consider keymaps, but sends virtual key events - uint32_t GetCharCode(uint32_t keyModifiers, ramses::EKeyCode keyCode) + uint32_t GetCharCode(ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) { - const bool isShift = (keyModifiers & ramses::EKeyModifier_Shift) != 0u; + const bool isShift = keyModifiers.isSet(ramses::EKeyModifier::Shift); uint32_t retval = 0U; if (ramses::EKeyCode_0 <= keyCode && keyCode <= ramses::EKeyCode_9) { @@ -108,8 +108,8 @@ namespace ramses_internal ImguiClientHelper::ImguiClientHelper(ramses::RamsesClient& client, uint32_t width, uint32_t height, ramses::sceneId_t sceneid) { - ramses::SceneConfig local; - m_imguiscene = client.createScene(sceneid, local, "imgui scene"); + ramses::SceneConfig local(sceneid); + m_imguiscene = client.createScene(local, "imgui scene"); imguicamera = m_imguiscene->createOrthographicCamera("imgui camera"); @@ -119,7 +119,7 @@ namespace ramses_internal imguicamera->scale({1.0, -1.0f, 1.0f}); ramses::RenderPass* renderPass = m_imguiscene->createRenderPass("imgui render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*imguicamera); renderGroup = m_imguiscene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -130,11 +130,10 @@ namespace ramses_internal effect = m_imguiscene->createEffect(effectDescImgui); - effect->findUniformInput("textureSampler", textureInput); - - effect->findAttributeInput("a_position", inputPosition); - effect->findAttributeInput("a_texcoord", inputUV); - effect->findAttributeInput("Color", inputColor); + textureInput = effect->findUniformInput("textureSampler"); + inputPosition = effect->findAttributeInput("a_position"); + inputUV = effect->findAttributeInput("a_texcoord"); + inputColor = effect->findAttributeInput("Color"); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); @@ -254,12 +253,12 @@ namespace ramses_internal todeleteRes.push_back(ramsespositions); todeleteRes.push_back(ramsesuv); todeleteRes.push_back(ramsescolor); - auto geobinding = m_imguiscene->createGeometryBinding(*effect); + auto geobinding = m_imguiscene->createGeometry(*effect); todeleteMeshes.push_back(geobinding); - geobinding->setInputBuffer(inputPosition, *ramsespositions); - geobinding->setInputBuffer(inputUV, *ramsesuv); - geobinding->setInputBuffer(inputColor, *ramsescolor); + geobinding->setInputBuffer(*inputPosition, *ramsespositions); + geobinding->setInputBuffer(*inputUV, *ramsesuv); + geobinding->setInputBuffer(*inputColor, *ramsescolor); geobinding->setIndices(*ramsesind); for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { @@ -271,10 +270,10 @@ namespace ramses_internal appearance->setDepthFunction(ramses::EDepthFunc::Disabled); appearance->setDepthWrite(ramses::EDepthWrite::Disabled); appearance->setCullingMode(ramses::ECullMode::Disabled); - appearance->setInputTexture(textureInput, *static_cast(pcmd->TextureId)); + appearance->setInputTexture(*textureInput, *static_cast(pcmd->TextureId)); auto mesh = m_imguiscene->createMeshNode(); todeleteMeshes.push_back(mesh); - mesh->setGeometryBinding(*geobinding); + mesh->setGeometry(*geobinding); mesh->setAppearance(*appearance); const ImVec2 pos = draw_data->DisplayPos; const ImVec2 displaysize = draw_data->DisplaySize; @@ -380,7 +379,7 @@ namespace ramses_internal } } - void ImguiClientHelper::keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, uint32_t keyModifiers, ramses::EKeyCode keyCode) + void ImguiClientHelper::keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) { if (!m_displayId.isValid() || displayId == m_displayId) { @@ -495,9 +494,9 @@ namespace ramses_internal return waitUntil([&] { return m_displays.find(displayId) != m_displays.end(); }); } - bool ImguiClientHelper:: waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + bool ImguiClientHelper:: waitForSceneState(ramses::Scene& scene, ramses::RendererSceneState state) { - return waitUntil([&] { return m_scenes[sceneId].state == state; }); + return waitUntil([&] { return m_scenes[scene.getSceneId()].state == state; }, &scene); } bool ImguiClientHelper::waitForSceneVersion(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t version) @@ -521,11 +520,13 @@ namespace ramses_internal return m_screenshotSaved; } - bool ImguiClientHelper::waitUntil(const std::function& conditionFunction) + bool ImguiClientHelper::waitUntil(const std::function& conditionFunction, ramses::Scene* scene) { const std::chrono::steady_clock::time_point timeoutTS = std::chrono::steady_clock::now() + std::chrono::seconds{5}; while (m_isRunning && !conditionFunction() && std::chrono::steady_clock::now() < timeoutTS) { + if (scene) + scene->flush(); // make sure scene gets flushed if subscribing std::this_thread::sleep_for(std::chrono::milliseconds{5}); // will give the renderer time to process changes m_renderer->dispatchEvents(*this); auto* sceneControl = m_renderer->getSceneControlAPI(); diff --git a/utils/ramses-imgui/src/ImguiImageCache.cpp b/tools/ramses-imgui/src/ImguiImageCache.cpp similarity index 59% rename from utils/ramses-imgui/src/ImguiImageCache.cpp rename to tools/ramses-imgui/src/ImguiImageCache.cpp index 0f0a8c868..9af7772b2 100644 --- a/utils/ramses-imgui/src/ImguiImageCache.cpp +++ b/tools/ramses-imgui/src/ImguiImageCache.cpp @@ -7,17 +7,49 @@ // ------------------------------------------------------------------------- #include "ImguiImageCache.h" -#include "TextureUtils.h" -#include "ramses-client-api/Scene.h" -#include "Resource/TextureResource.h" +#include "impl/TextureUtils.h" +#include "ramses/client/Scene.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "internal/SceneGraph/SceneAPI/TextureBuffer.h" -namespace ramses_internal +namespace ramses::internal { ImguiImageCache::ImguiImageCache(ramses::Scene* scene) : m_scene(scene) { } + imgui::Image ImguiImageCache::get(const MipMap& mm, EPixelStorageFormat format) + { + const auto it = m_imageBuffers.find(mm.data.data()); + imgui::Image image = {nullptr, 0, 0}; + if (it == m_imageBuffers.end()) + { + const ramses::TextureSwizzle textureSwizzle; + std::vector mipLevelData{ramses::MipLevelData(static_cast(mm.data.size()), mm.data.data())}; + auto* texture = m_scene->createTexture2D(TextureUtils::GetTextureFormatFromInternal(format), + mm.width, + mm.height, + static_cast(mipLevelData.size()), + &mipLevelData[0], + false, + textureSwizzle); + image.width = mm.width; + image.height = mm.height; + image.sampler = m_scene->createTextureSampler(ramses::ETextureAddressMode::Clamp, + ramses::ETextureAddressMode::Clamp, + ramses::ETextureSamplingMethod::Linear_MipMapLinear, + ramses::ETextureSamplingMethod::Linear, + *texture); + m_imageBuffers[mm.data.data()] = image; + } + else + { + image = it->second; + } + return image; + } + imgui::Image ImguiImageCache::get(const TextureResource* res) { const auto it = m_images.find(res); @@ -44,11 +76,11 @@ namespace ramses_internal for (const auto& mipSize : res->getMipDataSizes()) { assert(data + mipSize <= blob.data() + blob.size()); - mipLevelData.push_back(ramses::MipLevelData(mipSize, data)); + mipLevelData.emplace_back(mipSize, reinterpret_cast(data)); data += mipSize; } - ramses::Texture2D* texture = m_scene->createTexture2D(ramses::TextureUtils::GetTextureFormatFromInternal(res->getTextureFormat()), + ramses::Texture2D* texture = m_scene->createTexture2D(TextureUtils::GetTextureFormatFromInternal(res->getTextureFormat()), res->getWidth(), res->getHeight(), static_cast(mipLevelData.size()), @@ -73,4 +105,4 @@ namespace ramses_internal return image; } -} // namespace ramses_internal +} // namespace ramses::internal diff --git a/utils/ramses-imgui/src/ImguiWidgets.cpp b/tools/ramses-imgui/src/ImguiWidgets.cpp similarity index 87% rename from utils/ramses-imgui/src/ImguiWidgets.cpp rename to tools/ramses-imgui/src/ImguiWidgets.cpp index c5ad42874..e06a3c35a 100644 --- a/utils/ramses-imgui/src/ImguiWidgets.cpp +++ b/tools/ramses-imgui/src/ImguiWidgets.cpp @@ -8,15 +8,15 @@ #include "ImguiWidgets.h" -#include "ramses-client.h" -#include "ramses-utils.h" -#include "Resource/TextureResource.h" -#include "TextureUtils.h" -#include "AppearanceEnumsImpl.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "impl/TextureUtils.h" +#include "impl/AppearanceEnumsImpl.h" #include "fmt/format.h" -namespace ramses_internal +namespace ramses::internal { // TODO Find a better solution to this. For now, needed to suppress cast warnings WARNINGS_PUSH @@ -26,16 +26,6 @@ namespace ramses_internal constexpr auto green{IM_COL32(15u, 200u, 19u, 255u)}; WARNINGS_POP - using ramses::BlendFactorNames; - using ramses::DepthFuncNames; - using ramses::BlendOperationNames; - using ramses::DepthWriteNames; - using ramses::ScissorTestNames; - using ramses::StencilFuncNames; - using ramses::StencilOperationNames; - using ramses::CullModeNames; - using ramses::DrawModeNames; - namespace imgui { namespace @@ -55,13 +45,12 @@ namespace ramses_internal case ETextureChannelColor::One: return 255; case ETextureChannelColor::Zero: - case ETextureChannelColor::NUMBER_OF_ELEMENTS: return 0; } return 0; } - void ApplySwizzle(std::array& rgba, const ramses_internal::TextureSwizzleArray& swizzle) + void ApplySwizzle(std::array& rgba, const TextureSwizzleArray& swizzle) { const auto r = GetChannelColor(rgba, swizzle[0]); const auto g = GetChannelColor(rgba, swizzle[1]); @@ -70,7 +59,7 @@ namespace ramses_internal rgba = {r, g, b, a}; } - template void ConvertToRgba(std::vector& rgbaData, const Byte* data, const size_t size, const TextureSwizzleArray& swizzle, const T& readPixel) + template void ConvertToRgba(std::vector& rgbaData, const uint8_t* data, const size_t size, const TextureSwizzleArray& swizzle, const T& readPixel) { const auto* end = data + size; const auto* buf = data; @@ -102,12 +91,12 @@ namespace ramses_internal valid = false; value = 0; } - for (size_t n = 0; n < comboboxItems.size(); n++) + for (const auto& item : comboboxItems) { - if (value == comboboxItems[n].contentId) + if (value == item.contentId) { - *inputString = std::to_string(comboboxItems[n].contentId); - currentComboBoxString = comboboxItems[n].preview; + *inputString = std::to_string(item.contentId); + currentComboBoxString = item.preview; break; } } @@ -144,13 +133,13 @@ namespace ramses_internal ImGui::SameLine(0, spacing); if (ImGui::BeginCombo(fmt::format("##custom combo{}", descriptionText).c_str(), currentComboBoxString.c_str())) { - for (size_t n = 0; n < comboboxItems.size(); n++) + for (const auto& item : comboboxItems) { - bool is_selected = (currentComboBoxString == comboboxItems[n].preview); - if (ImGui::Selectable(comboboxItems[n].preview.c_str(), is_selected)) + bool is_selected = (currentComboBoxString == item.preview); + if (ImGui::Selectable(item.preview.c_str(), is_selected)) { - currentComboBoxString = comboboxItems[n].preview; - *inputString = std::to_string(comboboxItems[n].contentId); + currentComboBoxString = item.preview; + *inputString = std::to_string(item.contentId); value = std::stoi(*inputString); } if (is_selected) @@ -183,7 +172,7 @@ namespace ramses_internal valueHasChanged = true; } - ImU32 backgroundColor; + ImU32 backgroundColor = 0; float offsetOnPosition = 0; if (value) { @@ -341,13 +330,13 @@ namespace ramses_internal } } - std::string SaveToPng(const Byte* data, size_t size, ETextureFormat fmt, uint32_t width, uint32_t height, const std::string& filename, const TextureSwizzleArray& swizzle) + std::string SaveToPng(const uint8_t* data, size_t size, EPixelStorageFormat fmt, uint32_t width, uint32_t height, const std::string& filename, const TextureSwizzleArray& swizzle) { std::string errorMsg; std::vector imageData; const auto byteSize = width * height * 4u; imageData.reserve(byteSize); - if (fmt == ETextureFormat::RGBA8) + if (fmt == EPixelStorageFormat::RGBA8) { // just copy the blob imageData.insert(imageData.end(), data, data + size); @@ -357,21 +346,21 @@ namespace ramses_internal // convert to rgba switch (fmt) { - case ETextureFormat::R8: - ConvertToRgba(imageData, data, size, swizzle, [](const Byte* ptr, std::array& rgba) { + case EPixelStorageFormat::R8: + ConvertToRgba(imageData, data, size, swizzle, [](const auto* ptr, std::array& rgba) { rgba[0] = *ptr++; return ptr; }); break; - case ETextureFormat::RG8: - ConvertToRgba(imageData, data, size, swizzle, [](const Byte* ptr, std::array& rgba) { + case EPixelStorageFormat::RG8: + ConvertToRgba(imageData, data, size, swizzle, [](const auto* ptr, std::array& rgba) { rgba[0] = *ptr++; rgba[1] = *ptr++; return ptr; }); break; - case ETextureFormat::RGB8: - ConvertToRgba(imageData, data, size, swizzle, [](const Byte* ptr, std::array& rgba) { + case EPixelStorageFormat::RGB8: + ConvertToRgba(imageData, data, size, swizzle, [](const auto* ptr, std::array& rgba) { rgba[0] = *ptr++; rgba[1] = *ptr++; rgba[2] = *ptr++; @@ -390,7 +379,7 @@ namespace ramses_internal imageData.resize(byteSize); } - if (errorMsg.empty() && !ramses::RamsesUtils::SaveImageBufferToPng(filename, imageData, width, height)) + if (errorMsg.empty() && !RamsesUtils::SaveImageBufferToPng(filename, imageData, width, height)) { errorMsg = fmt::format("Cannot save: {}", filename); } @@ -407,11 +396,12 @@ namespace ramses_internal { resource->decompress(); } - const ramses_internal::TextureResource* textureResource = resource->convertTo(); + const auto* textureResource = resource->convertTo(); const auto& blob = textureResource->getResourceData(); - errorMsg = SaveToPng(blob.data(), blob.size(), resource->getTextureFormat(), resource->getWidth(), resource->getHeight(), filename, resource->getTextureSwizzle()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + errorMsg = SaveToPng(reinterpret_cast(blob.data()), blob.size(), resource->getTextureFormat(), resource->getWidth(), resource->getHeight(), filename, resource->getTextureSwizzle()); } return errorMsg; } } // namespace imgui -} // namespace ramses_internal +} // namespace ramses::internal diff --git a/client/logic/tools/ramses-logic-viewer/Arguments.h b/tools/ramses-logic-viewer/Arguments.h similarity index 85% rename from client/logic/tools/ramses-logic-viewer/Arguments.h rename to tools/ramses-logic-viewer/Arguments.h index a62eff766..8611ca68f 100644 --- a/client/logic/tools/ramses-logic-viewer/Arguments.h +++ b/tools/ramses-logic-viewer/Arguments.h @@ -8,13 +8,13 @@ #pragma once -#include "internals/StdFilesystemWrapper.h" +#include "internal/logic/StdFilesystemWrapper.h" #include #include #include #include -#include "ramses-framework-api/RamsesFrameworkTypes.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include "ramses-sdk-build-config.h" class Arguments @@ -24,10 +24,9 @@ class Arguments { cli.description(R"( Loads and shows a ramses scene from the . - and are auto-resolved if matching files with *.rlogic and *.lua extensions are found in the same path as . (Explicit arguments override autodetection.) + is auto-resolved if matching file with *.lua extension is found in the same path as . (Explicit argument overrides autodetection.) )"); cli.add_option("ramsesfile", m_sceneFile, "Ramses scene file")->required()->check(CLI::ExistingFile); - cli.add_option("logicfile", m_logicFile, "Ramses Logic file")->check(CLI::ExistingFile); cli.add_option("luafile,--lua", m_luaFile, "Lua configuration file")->check(CLI::ExistingFile); auto exec = cli.add_option("--exec", m_luaFunction, "Calls the given lua function and exits."); cli.add_option("--exec-lua", m_exec, "Calls the given lua code and exits.")->excludes(exec); @@ -57,15 +56,6 @@ Loads and shows a ramses scene from the . return m_sceneFile; } - const std::string& logicFile() const - { - if (m_logicFile.empty()) - { - m_logicFile = ReplaceExtension(m_sceneFile, "rlogic"); - } - return m_logicFile; - } - const std::string& luaFile() const { if (m_luaFile.empty()) @@ -109,7 +99,6 @@ Loads and shows a ramses scene from the . } std::string m_sceneFile; - mutable std::string m_logicFile; mutable std::string m_luaFile; std::string m_luaFunction; std::string m_exec; diff --git a/client/logic/tools/ramses-logic-viewer/CMakeLists.txt b/tools/ramses-logic-viewer/CMakeLists.txt similarity index 83% rename from client/logic/tools/ramses-logic-viewer/CMakeLists.txt rename to tools/ramses-logic-viewer/CMakeLists.txt index a43916b18..9cf2eeec7 100644 --- a/client/logic/tools/ramses-logic-viewer/CMakeLists.txt +++ b/tools/ramses-logic-viewer/CMakeLists.txt @@ -28,8 +28,7 @@ set(logic_viewer_src add_library(ramses-logic-viewer-lib INTERFACE) target_sources(ramses-logic-viewer-lib INTERFACE ${logic_viewer_header}) target_sources(ramses-logic-viewer-lib INTERFACE ${logic_viewer_src}) -target_include_directories(ramses-logic-viewer-lib INTERFACE ${PROJECT_SOURCE_DIR}/client/logic/lib) -target_link_libraries(ramses-logic-viewer-lib INTERFACE imgui fmt::fmt sol2::sol2 lua::lua ramses-framework-cli) +target_link_libraries(ramses-logic-viewer-lib INTERFACE imgui fmt::fmt sol2::sol2 lua::lua ramses-framework-cli ramses-client) add_library(ramses-logic-viewer-gui-lib INTERFACE) target_sources(ramses-logic-viewer-gui-lib INTERFACE @@ -41,14 +40,17 @@ target_sources(ramses-logic-viewer-gui-lib INTERFACE ImguiClientHelper.cpp) target_link_libraries(ramses-logic-viewer-gui-lib INTERFACE ramses-logic-viewer-lib) -createModule( - NAME ramses-logic-viewer - TYPE BINARY - ENABLE_INSTALL ${ramses-sdk_ENABLE_INSTALL} - SRC_FILES main.cpp - DEPENDENCIES ramses-logic-viewer-gui-lib - ramses-shared-lib -) + +if(ramses-sdk_BUILD_FULL_SHARED_LIB) + createModule( + NAME ramses-logic-viewer + TYPE BINARY + ENABLE_INSTALL ${ramses-sdk_ENABLE_INSTALL} + SRC_FILES main.cpp + DEPENDENCIES ramses-logic-viewer-gui-lib + ramses-shared-lib + ) +endif() if(ramses-sdk_BUILD_HEADLESS_SHARED_LIB) createModule( diff --git a/client/logic/tools/ramses-logic-viewer/ImguiClientHelper.cpp b/tools/ramses-logic-viewer/ImguiClientHelper.cpp similarity index 93% rename from client/logic/tools/ramses-logic-viewer/ImguiClientHelper.cpp rename to tools/ramses-logic-viewer/ImguiClientHelper.cpp index 869237829..4289e1ff4 100644 --- a/client/logic/tools/ramses-logic-viewer/ImguiClientHelper.cpp +++ b/tools/ramses-logic-viewer/ImguiClientHelper.cpp @@ -80,9 +80,9 @@ namespace ramses // emulates an English keyboard for the available ramses key events // (ramses (on Windows) does not consider keymaps, but sends virtual key events - uint32_t GetCharCode(uint32_t keyModifiers, ramses::EKeyCode keyCode) + uint32_t GetCharCode(KeyModifiers keyModifiers, ramses::EKeyCode keyCode) { - const bool isShift = (keyModifiers & ramses::EKeyModifier_Shift) != 0u; + const bool isShift = keyModifiers.isSet(ramses::EKeyModifier::Shift); uint32_t retval = 0U; if (ramses::EKeyCode_0 <= keyCode && keyCode <= ramses::EKeyCode_9) { @@ -109,8 +109,8 @@ namespace ramses ImguiClientHelper::ImguiClientHelper(ramses::RamsesClient& client, uint32_t width, uint32_t height, ramses::sceneId_t sceneid) { assert(ImGui::GetCurrentContext() != nullptr); // context should be already created - ramses::SceneConfig local; - m_imguiscene = client.createScene(sceneid, local, "imgui scene"); + ramses::SceneConfig local(sceneid); + m_imguiscene = client.createScene(local, "imgui scene"); imguicamera = m_imguiscene->createOrthographicCamera("imgui camera"); @@ -120,7 +120,7 @@ namespace ramses imguicamera->scale({1.0, -1.0f, 1.0f}); ramses::RenderPass* renderPass = m_imguiscene->createRenderPass("imgui render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*imguicamera); renderGroup = m_imguiscene->createRenderGroup(); renderPass->addRenderGroup(*renderGroup); @@ -131,11 +131,10 @@ namespace ramses effect = m_imguiscene->createEffect(effectDescImgui); - effect->findUniformInput("textureSampler", textureInput); - - effect->findAttributeInput("a_position", inputPosition); - effect->findAttributeInput("a_texcoord", inputUV); - effect->findAttributeInput("Color", inputColor); + textureInput = effect->findUniformInput("textureSampler"); + inputPosition = effect->findAttributeInput("a_position"); + inputUV = effect->findAttributeInput("a_texcoord"); + inputColor = effect->findAttributeInput("Color"); ImGuiIO& io = ImGui::GetIO(); io.DisplaySize.x = static_cast(width); @@ -250,12 +249,12 @@ namespace ramses todeleteRes.push_back(ramsespositions); todeleteRes.push_back(ramsesuv); todeleteRes.push_back(ramsescolor); - auto geobinding = m_imguiscene->createGeometryBinding(*effect); + auto geobinding = m_imguiscene->createGeometry(*effect); todeleteMeshes.push_back(geobinding); - geobinding->setInputBuffer(inputPosition, *ramsespositions); - geobinding->setInputBuffer(inputUV, *ramsesuv); - geobinding->setInputBuffer(inputColor, *ramsescolor); + geobinding->setInputBuffer(*inputPosition, *ramsespositions); + geobinding->setInputBuffer(*inputUV, *ramsesuv); + geobinding->setInputBuffer(*inputColor, *ramsescolor); geobinding->setIndices(*ramsesind); for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { @@ -267,10 +266,10 @@ namespace ramses appearance->setDepthFunction(ramses::EDepthFunc::Disabled); appearance->setDepthWrite(ramses::EDepthWrite::Disabled); appearance->setCullingMode(ramses::ECullMode::Disabled); - appearance->setInputTexture(textureInput, *static_cast(pcmd->TextureId)); + appearance->setInputTexture(*textureInput, *static_cast(pcmd->TextureId)); auto mesh = m_imguiscene->createMeshNode(); todeleteMeshes.push_back(mesh); - mesh->setGeometryBinding(*geobinding); + mesh->setGeometry(*geobinding); mesh->setAppearance(*appearance); const ImVec2 pos = draw_data->DisplayPos; const ImVec2 displaysize = draw_data->DisplaySize; @@ -376,7 +375,7 @@ namespace ramses } } - void ImguiClientHelper::keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, uint32_t keyModifiers, ramses::EKeyCode keyCode) + void ImguiClientHelper::keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) { if (!m_displayId.isValid() || displayId == m_displayId) { @@ -494,9 +493,9 @@ namespace ramses return waitUntil([&] { return m_displays.find(displayId) != m_displays.end(); }); } - bool ImguiClientHelper:: waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state) + bool ImguiClientHelper::waitForSceneState(ramses::Scene& scene, ramses::RendererSceneState state) { - return waitUntil([&] { return m_scenes[sceneId].state == state; }); + return waitUntil([&] { return m_scenes[scene.getSceneId()].state == state; }, &scene); } bool ImguiClientHelper::waitForSceneVersion(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t version) @@ -520,13 +519,15 @@ namespace ramses return m_screenshotSaved; } - bool ImguiClientHelper::waitUntil(const std::function& conditionFunction) + bool ImguiClientHelper::waitUntil(const std::function& conditionFunction, ramses::Scene* scene) { if (m_renderer) { const std::chrono::steady_clock::time_point timeoutTS = std::chrono::steady_clock::now() + std::chrono::seconds{5}; while (m_isRunning && !conditionFunction() && std::chrono::steady_clock::now() < timeoutTS) { + if (scene) + scene->flush(); // make sure scene gets flushed if subscribing std::this_thread::sleep_for(std::chrono::milliseconds{5}); // will give the renderer time to process changes m_renderer->dispatchEvents(*this); auto* sceneControl = m_renderer->getSceneControlAPI(); diff --git a/client/logic/tools/ramses-logic-viewer/ImguiClientHelper.h b/tools/ramses-logic-viewer/ImguiClientHelper.h similarity index 69% rename from client/logic/tools/ramses-logic-viewer/ImguiClientHelper.h rename to tools/ramses-logic-viewer/ImguiClientHelper.h index 7944e8b30..a9c7a4423 100644 --- a/client/logic/tools/ramses-logic-viewer/ImguiClientHelper.h +++ b/tools/ramses-logic-viewer/ImguiClientHelper.h @@ -9,12 +9,12 @@ #pragma once #include "ImguiWrapper.h" -#include "ramses-client.h" -#include "ramses-utils.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererSceneControl.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererSceneControl.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" #include #include @@ -54,7 +54,7 @@ namespace ramses bool saveScreenshot(const std::string& filename, ramses::displayBufferId_t screenshotBuf, uint32_t x, uint32_t y, uint32_t width, uint32_t height); bool waitForDisplay(ramses::displayId_t displayId); - bool waitForSceneState(ramses::sceneId_t sceneId, ramses::RendererSceneState state); + bool waitForSceneState(ramses::Scene& scene, ramses::RendererSceneState state); bool waitForSceneVersion(ramses::sceneId_t sceneId, ramses::sceneVersionTag_t version); bool waitForOffscreenBufferCreated(const ramses::displayBufferId_t offscreenBufferId); bool waitForOffscreenBufferLinked(const ramses::sceneId_t sceneId); @@ -70,7 +70,7 @@ namespace ramses void displayCreated(ramses::displayId_t displayId, ramses::ERendererEventResult result) override; void displayDestroyed(ramses::displayId_t displayId, ramses::ERendererEventResult result) override; void mouseEvent(ramses::displayId_t displayId, ramses::EMouseEvent eventType, int32_t mousePosX, int32_t mousePosY) override; - void keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, uint32_t keyModifiers, ramses::EKeyCode keyCode) override; + void keyEvent(ramses::displayId_t displayId, ramses::EKeyEvent eventType, ramses::KeyModifiers keyModifiers, ramses::EKeyCode keyCode) override; void windowResized(ramses::displayId_t displayId, uint32_t width, uint32_t height) override; void windowClosed(ramses::displayId_t displayId) override; void framebufferPixelsRead(const uint8_t* pixelData, @@ -80,7 +80,7 @@ namespace ramses ramses::ERendererEventResult result) override; private: - bool waitUntil(const std::function& conditionFunction); + bool waitUntil(const std::function& conditionFunction, ramses::Scene* scene = nullptr); struct SceneInfo { @@ -92,30 +92,30 @@ namespace ramses using OffscreenBufferSet = std::unordered_set; using DisplaySet = std::unordered_set; - SceneSet m_scenes; - SceneSet m_scenesAssignedToOffscreenBuffer; - SceneSet m_scenesConsumingOffscreenBuffer; - DisplaySet m_displays; - OffscreenBufferSet m_offscreenBuffers; - ramses::RamsesRenderer* m_renderer = nullptr; - ramses::displayId_t m_displayId; - ramses::Scene* m_imguiscene = nullptr; - ramses::OrthographicCamera* imguicamera = nullptr; - ramses::TextureSampler* sampler = nullptr; - ramses::Effect* effect = nullptr; - ramses::RenderGroup* renderGroup = nullptr; - ramses::UniformInput textureInput; - ramses::AttributeInput inputPosition; - ramses::AttributeInput inputUV; - ramses::AttributeInput inputColor; - std::vector todeleteMeshes; - std::vector todeleteRes; - std::pair m_clickEvent; - std::string m_screenshot; - uint32_t m_screenshotWidth = 0U; - uint32_t m_screenshotHeight = 0U; - bool m_screenshotSaved = false; - bool m_isRunning = true; + SceneSet m_scenes; + SceneSet m_scenesAssignedToOffscreenBuffer; + SceneSet m_scenesConsumingOffscreenBuffer; + DisplaySet m_displays; + OffscreenBufferSet m_offscreenBuffers; + ramses::RamsesRenderer* m_renderer = nullptr; + ramses::displayId_t m_displayId; + ramses::Scene* m_imguiscene = nullptr; + ramses::OrthographicCamera* imguicamera = nullptr; + ramses::TextureSampler* sampler = nullptr; + ramses::Effect* effect = nullptr; + ramses::RenderGroup* renderGroup = nullptr; + std::optional textureInput{std::nullopt}; + std::optional inputPosition{std::nullopt}; + std::optional inputUV{std::nullopt}; + std::optional inputColor{std::nullopt}; + std::vector todeleteMeshes; + std::vector todeleteRes; + std::pair m_clickEvent; + std::string m_screenshot; + uint32_t m_screenshotWidth = 0U; + uint32_t m_screenshotHeight = 0U; + bool m_screenshotSaved = false; + bool m_isRunning = true; }; inline ramses::Scene* ImguiClientHelper::getScene() diff --git a/client/logic/tools/ramses-logic-viewer/ImguiWrapper.h b/tools/ramses-logic-viewer/ImguiWrapper.h similarity index 100% rename from client/logic/tools/ramses-logic-viewer/ImguiWrapper.h rename to tools/ramses-logic-viewer/ImguiWrapper.h diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewer.cpp b/tools/ramses-logic-viewer/LogicViewer.cpp similarity index 79% rename from client/logic/tools/ramses-logic-viewer/LogicViewer.cpp rename to tools/ramses-logic-viewer/LogicViewer.cpp index c7c6b58cb..de7a5085a 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewer.cpp +++ b/tools/ramses-logic-viewer/LogicViewer.cpp @@ -9,22 +9,22 @@ #include "LogicViewer.h" #include "LogicViewerLuaTypes.h" #include "LogicViewerLog.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" #include "fmt/format.h" -#include "internals/SolHelper.h" +#include "internal/logic/SolHelper.h" #include #include @@ -83,12 +83,12 @@ namespace ramses NodeListWrapper scripts; NodeListWrapper animations; NodeListWrapper timers; - NodeListWrapper nodeBindings; - NodeListWrapper appearanceBindings; - NodeListWrapper cameraBindings; - NodeListWrapper renderPassBindings; - NodeListWrapper renderGroupBindings; - NodeListWrapper meshNodeBindings; + NodeListWrapper nodeBindings; + NodeListWrapper appearanceBindings; + NodeListWrapper cameraBindings; + NodeListWrapper renderPassBindings; + NodeListWrapper renderGroupBindings; + NodeListWrapper meshNodeBindings; NodeListWrapper anchorPoints; NodeListWrapper skinBindings; }; @@ -120,19 +120,13 @@ namespace ramses const char* const LogicViewer::ltnViewName = "name"; const char* const LogicViewer::ltnViewDescription = "description"; - LogicViewer::LogicViewer(ramses::EFeatureLevel engineFeatureLevel, ScreenshotFunc screenshotFunc) - : m_logicEngine{ engineFeatureLevel } + LogicViewer::LogicViewer(ramses::LogicEngine& logicEngine, ScreenshotFunc screenshotFunc) + : m_logicEngine{ logicEngine } , m_screenshotFunc(std::move(screenshotFunc)) { m_startTime = std::chrono::steady_clock::now(); } - bool LogicViewer::loadRamsesLogic(const std::string& filename, ramses::Scene* scene) - { - m_logicFilename = filename; - return m_logicEngine.loadFromFile(filename, scene); - } - Result LogicViewer::loadLuaFile(const std::string& filename) { m_result = Result(); @@ -143,12 +137,12 @@ namespace ramses registerNodeListType(m_sol, "LuaScripts"); registerNodeListType(m_sol, "AnimationNodes"); registerNodeListType(m_sol, "TimerNodes"); - registerNodeListType(m_sol, "NodeBindings"); - registerNodeListType(m_sol, "AppearanceBindings"); - registerNodeListType(m_sol, "CameraBindings"); - registerNodeListType(m_sol, "RenderPassBindings"); - registerNodeListType(m_sol, "RenderGroupBindings"); - registerNodeListType(m_sol, "MeshNodeBindings"); + registerNodeListType(m_sol, "NodeBindings"); + registerNodeListType(m_sol, "AppearanceBindings"); + registerNodeListType(m_sol, "CameraBindings"); + registerNodeListType(m_sol, "RenderPassBindings"); + registerNodeListType(m_sol, "RenderGroupBindings"); + registerNodeListType(m_sol, "MeshNodeBindings"); registerNodeListType(m_sol, "AnchorPoints"); registerNodeListType(m_sol, "SkinBindings"); m_sol.new_usertype("LogicNode", sol::meta_function::index, &LogicNodeWrapper::get, sol::meta_function::to_string, &LogicNodeWrapper::toString); @@ -159,13 +153,6 @@ namespace ramses &PropertyWrapper::get, sol::meta_function::to_string, &PropertyWrapper::toString); - m_sol.new_usertype("ConstLogicProperty", - ltnPropertyValue, - sol::readonly_property(&ConstPropertyWrapper::getValue), - sol::meta_function::index, - &ConstPropertyWrapper::get, - sol::meta_function::to_string, - &ConstPropertyWrapper::toString); m_sol.new_usertype( "RamsesLogic", sol::no_constructor, @@ -207,9 +194,9 @@ namespace ramses ltnUpdate, [&]() { updateEngine(); }, ltnLink, - [&](const ConstPropertyWrapper& src, const PropertyWrapper& target) { return m_logicEngine.link(src.m_property, target.m_property); }, + [&](PropertyWrapper& src, PropertyWrapper& target) { return m_logicEngine.link(src.m_property, target.m_property); }, ltnUnlink, - [&](const ConstPropertyWrapper& src, const PropertyWrapper& target) { return m_logicEngine.unlink(src.m_property, target.m_property); }); + [&](PropertyWrapper& src, PropertyWrapper& target) { return m_logicEngine.unlink(src.m_property, target.m_property); }); m_sol[ltnModule] = LogicWrapper(m_logicEngine, m_sol); @@ -331,12 +318,12 @@ namespace ramses log.logAllInputs("--Interfaces\n", LogicViewer::ltnInterface); log.logAllInputs("--Scripts\n", LogicViewer::ltnScript); - log.logAllInputs("--Node bindings\n", LogicViewer::ltnNode); - log.logAllInputs("--Appearance bindings\n", LogicViewer::ltnAppearance); - log.logAllInputs("--Camera bindings\n", LogicViewer::ltnCamera); - log.logAllInputs("--RenderPass bindings\n", LogicViewer::ltnRenderPass); - log.logAllInputs("--RenderGroup bindings\n", LogicViewer::ltnRenderGroup); - log.logAllInputs("--MeshNode bindings\n", LogicViewer::ltnMeshNode); + log.logAllInputs("--Node bindings\n", LogicViewer::ltnNode); + log.logAllInputs("--Appearance bindings\n", LogicViewer::ltnAppearance); + log.logAllInputs("--Camera bindings\n", LogicViewer::ltnCamera); + log.logAllInputs("--RenderPass bindings\n", LogicViewer::ltnRenderPass); + log.logAllInputs("--RenderGroup bindings\n", LogicViewer::ltnRenderGroup); + log.logAllInputs("--MeshNode bindings\n", LogicViewer::ltnMeshNode); log.logAllInputs("--Anchor points\n", LogicViewer::ltnAnchorPoint); log.logAllInputs("--Skin bindings\n", LogicViewer::ltnSkinBinding); diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewer.h b/tools/ramses-logic-viewer/LogicViewer.h similarity index 87% rename from client/logic/tools/ramses-logic-viewer/LogicViewer.h rename to tools/ramses-logic-viewer/LogicViewer.h index cf3695683..1b754040c 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewer.h +++ b/tools/ramses-logic-viewer/LogicViewer.h @@ -10,8 +10,8 @@ #include "LogicViewerLuaTypes.h" #include "UpdateReportSummary.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LogicEngine.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LogicEngine.h" #include "Result.h" #include #include @@ -113,11 +113,7 @@ namespace ramses sol::optional m_tbl; }; - LogicViewer(ramses::EFeatureLevel engineFeatureLevel, ScreenshotFunc screenshotFunc); - - [[nodiscard]] bool loadRamsesLogic(const std::string& filename, ramses::Scene* scene); - - [[nodiscard]] const std::string& getLogicFilename() const; + LogicViewer(ramses::LogicEngine& logicEngine, ScreenshotFunc screenshotFunc); [[nodiscard]] Result loadLuaFile(const std::string& filename); @@ -127,7 +123,7 @@ namespace ramses [[nodiscard]] const std::string& getLuaFilename() const; - [[nodiscard]] ramses::LogicEngine& getEngine(); + [[nodiscard]] ramses::LogicEngine& getLogic(); [[nodiscard]] Result update(); @@ -157,13 +153,12 @@ namespace ramses void load(sol::load_result&& loadResult); - ramses::LogicEngine m_logicEngine; - ScreenshotFunc m_screenshotFunc; - std::string m_logicFilename; - std::string m_luaFilename; - sol::state m_sol; - size_t m_view = 1U; - Result m_result; + ramses::LogicEngine& m_logicEngine; + ScreenshotFunc m_screenshotFunc; + std::string m_luaFilename; + sol::state m_sol; + size_t m_view = 1U; + Result m_result; std::chrono::steady_clock::time_point m_startTime; @@ -171,7 +166,7 @@ namespace ramses UpdateReportSummary m_updateReportSummary; }; - inline LogicEngine& LogicViewer::getEngine() + inline LogicEngine& LogicViewer::getLogic() { return m_logicEngine; } @@ -181,11 +176,6 @@ namespace ramses return m_luaFilename; } - inline const std::string& LogicViewer::getLogicFilename() const - { - return m_logicFilename; - } - inline const Result& LogicViewer::getLastResult() const { return m_result; diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerApp.cpp b/tools/ramses-logic-viewer/LogicViewerApp.cpp similarity index 74% rename from client/logic/tools/ramses-logic-viewer/LogicViewerApp.cpp rename to tools/ramses-logic-viewer/LogicViewerApp.cpp index 28e9cad6a..124f23987 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerApp.cpp +++ b/tools/ramses-logic-viewer/LogicViewerApp.cpp @@ -11,7 +11,6 @@ #include "Arguments.h" #include "LogicViewer.h" #include "LogicViewerSettings.h" -#include "ramses-logic/Logger.h" #include "ImguiWrapper.h" namespace ramses @@ -26,24 +25,13 @@ namespace ramses ImGui::DestroyContext(m_imguiContext); } - int LogicViewerApp::GetFeatureLevelFromFiles(const std::string& sceneFilename, const std::string& logicFilename, EFeatureLevel& featureLevel) + int LogicViewerApp::GetFeatureLevelFromFile(const std::string& sceneFilename, EFeatureLevel& featureLevel) { - EFeatureLevel tmpLevel{EFeatureLevel_01}; - if (!RamsesClient::GetFeatureLevelFromFile(sceneFilename, tmpLevel)) + if (!RamsesClient::GetFeatureLevelFromFile(sceneFilename, featureLevel)) { std::cerr << "Could not parse feature level from scene file " << sceneFilename << std::endl; return static_cast(ExitCode::ErrorLoadScene); } - if (!LogicEngine::GetFeatureLevelFromFile(logicFilename, featureLevel)) - { - std::cerr << "Could not parse feature level from logic file " << logicFilename << std::endl; - return static_cast(ExitCode::ErrorLoadLogic); - } - if (tmpLevel != featureLevel) - { - std::cerr << "Feature levels of the scene file and the logic file do not match!" << std::endl; - return static_cast(ExitCode::ErrorLoadScene); - } return 0; } @@ -52,7 +40,6 @@ namespace ramses ramses::RamsesFrameworkConfig frameworkConfig{featureLevel}; frameworkConfig.setPeriodicLogInterval(std::chrono::seconds(0)); frameworkConfig.setLogLevelConsole(args.ramsesLogLevel()); - ramses::Logger::SetLogVerbosityLimit(args.ramsesLogLevel()); m_framework = std::make_unique(frameworkConfig); m_client = m_framework->createClient("ramses-logic-viewer"); if (!m_client) @@ -74,22 +61,22 @@ namespace ramses return 0; } - int LogicViewerApp::createViewer(const Arguments& args, EFeatureLevel featureLevel, LogicViewer::ScreenshotFunc&& fScreenshot) + int LogicViewerApp::createViewer(const Arguments& args, LogicViewer::ScreenshotFunc&& fScreenshot) { - if (!fs::exists(args.logicFile())) - { - std::cerr << "Logic file does not exist: " << args.logicFile() << std::endl; - return static_cast(ExitCode::ErrorLoadLogic); - } + assert(m_scene); - m_viewer = std::make_unique(featureLevel, fScreenshot); + // find logic engine + SceneObjectIterator iter{*m_scene, ERamsesObjectType::LogicEngine}; - if (!m_viewer->loadRamsesLogic(args.logicFile(), m_scene)) + m_logic = object_cast(iter.getNext()); + if (!m_logic) { - std::cerr << "Failed to load logic file: " << args.logicFile() << std::endl; + std::cerr << "Failed to find logic" << std::endl; return static_cast(ExitCode::ErrorLoadLogic); } + m_viewer = std::make_unique(*m_logic, fScreenshot); + if (args.writeConfig()) { ImGui::NewFrame(); diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerApp.h b/tools/ramses-logic-viewer/LogicViewerApp.h similarity index 87% rename from client/logic/tools/ramses-logic-viewer/LogicViewerApp.h rename to tools/ramses-logic-viewer/LogicViewerApp.h index 66b09b122..cbb5ec908 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerApp.h +++ b/tools/ramses-logic-viewer/LogicViewerApp.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-client.h" -#include "ramses-framework-api/RamsesFrameworkTypes.h" +#include "ramses/client/ramses-client.h" +#include "ramses/framework/RamsesFrameworkTypes.h" #include "Result.h" #include "LogicViewer.h" #include @@ -56,9 +56,9 @@ namespace ramses [[nodiscard]] const ramses::LogicViewerSettings* getSettings() const; protected: - [[nodiscard]] static int GetFeatureLevelFromFiles(const std::string& sceneFilename, const std::string& logicFilename, EFeatureLevel& featureLevel); + [[nodiscard]] static int GetFeatureLevelFromFile(const std::string& sceneFilename, EFeatureLevel& featureLevel); [[nodiscard]] int loadScene(const Arguments& args, EFeatureLevel featureLevel); - [[nodiscard]] int createViewer(const Arguments& args, EFeatureLevel featureLevel, LogicViewer::ScreenshotFunc&& fScreenshot); + [[nodiscard]] int createViewer(const Arguments& args, LogicViewer::ScreenshotFunc&& fScreenshot); ImGuiContext* m_imguiContext = nullptr; std::unique_ptr m_framework; @@ -67,6 +67,7 @@ namespace ramses ramses::RamsesClient* m_client = nullptr; ramses::Scene* m_scene = nullptr; + ramses::LogicEngine* m_logic = nullptr; ramses::Result m_loadLuaStatus; int m_exitCode = -1; diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerGui.cpp b/tools/ramses-logic-viewer/LogicViewerGui.cpp similarity index 94% rename from client/logic/tools/ramses-logic-viewer/LogicViewerGui.cpp rename to tools/ramses-logic-viewer/LogicViewerGui.cpp index bd01672a5..02c75ae71 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerGui.cpp +++ b/tools/ramses-logic-viewer/LogicViewerGui.cpp @@ -10,24 +10,24 @@ #include "LogicViewer.h" #include "LogicViewerLog.h" #include "LogicViewerSettings.h" -#include "ramses-client-api/Scene.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" -#include "internals/StdFilesystemWrapper.h" -#include "fmt/format.h" +#include "ramses/client/Scene.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" +#include "internal/logic/StdFilesystemWrapper.h" +#include "internal/PlatformAbstraction/FmtBase.h" #include "glm/gtc/type_ptr.hpp" #ifndef _MSC_VER @@ -139,27 +139,27 @@ namespace ramses { name = "Animation"; } - else if (node->as() != nullptr) + else if (node->as() != nullptr) { name = "NodeBinding"; } - else if (node->as() != nullptr) + else if (node->as() != nullptr) { name = "AppearanceBinding"; } - else if (node->as() != nullptr) + else if (node->as() != nullptr) { name = "CameraBinding"; } - else if (node->as() != nullptr) + else if (node->as() != nullptr) { name = "RenderPassBinding"; } - else if (node->as() != nullptr) + else if (node->as() != nullptr) { name = "RenderGroupBinding"; } - else if (node->as() != nullptr) + else if (node->as() != nullptr) { name = "MeshNodeBinding"; } @@ -197,7 +197,7 @@ namespace ramses LogicViewerGui::LogicViewerGui(ramses::LogicViewer& viewer, LogicViewerSettings& settings, std::string luafile) : m_settings(settings) , m_viewer(viewer) - , m_logicEngine(viewer.getEngine()) + , m_logicEngine(viewer.getLogic()) , m_filename(std::move(luafile)) { m_viewer.enableUpdateReport(m_settings.showUpdateReport, m_updateReportInterval); @@ -372,7 +372,7 @@ namespace ramses void LogicViewerGui::drawWindow() { - if (!ImGui::Begin(fmt::format("Logic Viewer (FeatureLevel 0{})", m_logicEngine.getFeatureLevel()).c_str(), &m_settings.showWindow, ImGuiWindowFlags_MenuBar)) + if (!ImGui::Begin(fmt::format("Logic Viewer (FeatureLevel 0{})", m_logicEngine.getScene().getRamsesClient().getRamsesFramework().getFeatureLevel()).c_str(), &m_settings.showWindow, ImGuiWindowFlags_MenuBar)) { ImGui::End(); return; @@ -529,7 +529,7 @@ namespace ramses bool LogicViewerGui::DrawTreeNode(ramses::LogicObject* obj) { - return TreeNode(obj, fmt::format("[{}]: {}", obj->getId(), obj->getName())); + return TreeNode(obj, fmt::format("[{}]: {}", obj->getSceneObjectId().getValue(), obj->getName())); } void LogicViewerGui::drawScripts() @@ -660,13 +660,13 @@ namespace ramses { if (ImGui::MenuItem("Copy all Node Binding inputs")) { - copyInputs(LogicViewer::ltnNode, m_logicEngine.getCollection()); + copyInputs(LogicViewer::ltnNode, m_logicEngine.getCollection()); } ImGui::EndPopup(); } if (openBindings) { - for (auto* obj : m_logicEngine.getCollection()) + for (auto* obj : m_logicEngine.getCollection()) { const bool open = DrawTreeNode(obj); drawNodeContextMenu(obj, LogicViewer::ltnNode); @@ -688,13 +688,13 @@ namespace ramses { if (ImGui::MenuItem("Copy all Camera Binding inputs")) { - copyInputs(LogicViewer::ltnCamera, m_logicEngine.getCollection()); + copyInputs(LogicViewer::ltnCamera, m_logicEngine.getCollection()); } ImGui::EndPopup(); } if (openBindings) { - for (auto* obj : m_logicEngine.getCollection()) + for (auto* obj : m_logicEngine.getCollection()) { const bool open = DrawTreeNode(obj); drawNodeContextMenu(obj, LogicViewer::ltnCamera); @@ -715,13 +715,13 @@ namespace ramses { if (ImGui::MenuItem("Copy all RenderPass Binding inputs")) { - copyInputs(LogicViewer::ltnRenderPass, m_logicEngine.getCollection()); + copyInputs(LogicViewer::ltnRenderPass, m_logicEngine.getCollection()); } ImGui::EndPopup(); } if (openBindings) { - for (auto* obj : m_logicEngine.getCollection()) + for (auto* obj : m_logicEngine.getCollection()) { const bool open = DrawTreeNode(obj); drawNodeContextMenu(obj, LogicViewer::ltnRenderPass); @@ -742,13 +742,13 @@ namespace ramses { if (ImGui::MenuItem("Copy all RenderGroup Binding inputs")) { - copyInputs(LogicViewer::ltnRenderGroup, m_logicEngine.getCollection()); + copyInputs(LogicViewer::ltnRenderGroup, m_logicEngine.getCollection()); } ImGui::EndPopup(); } if (openBindings) { - for (auto* obj : m_logicEngine.getCollection()) + for (auto* obj : m_logicEngine.getCollection()) { const bool open = DrawTreeNode(obj); drawNodeContextMenu(obj, LogicViewer::ltnRenderGroup); @@ -769,13 +769,13 @@ namespace ramses { if (ImGui::MenuItem("Copy all MeshNode Binding inputs")) { - copyInputs(LogicViewer::ltnMeshNode, m_logicEngine.getCollection()); + copyInputs(LogicViewer::ltnMeshNode, m_logicEngine.getCollection()); } ImGui::EndPopup(); } if (openBindings) { - for (auto* obj : m_logicEngine.getCollection()) + for (auto* obj : m_logicEngine.getCollection()) { const bool open = DrawTreeNode(obj); drawNodeContextMenu(obj, LogicViewer::ltnMeshNode); @@ -896,7 +896,7 @@ namespace ramses { auto* node = timedNode.first; const auto percentage = (longest.count() > 0u) ? (100u * timedNode.second / longest) : 0u; - if (TreeNode(node, fmt::format("{}[{}]: {} [time:{} ms, {}%]", TypeName(node), node->getId(), node->getName(), timedNode.second, percentage))) + if (TreeNode(node, fmt::format("{}[{}]: {} [time:{} ms, {}%]", TypeName(node), node->getSceneObjectId().getValue(), node->getName(), timedNode.second, percentage))) { drawNode(node); ImGui::TreePop(); @@ -909,7 +909,7 @@ namespace ramses { for (auto& node : skipped) { - if (TreeNode(node, fmt::format("{}[{}]: {}", TypeName(node), node->getId(), node->getName()))) + if (TreeNode(node, fmt::format("{}[{}]: {}", TypeName(node), node->getSceneObjectId().getValue(), node->getName()))) { drawNode(node); ImGui::TreePop(); @@ -927,13 +927,13 @@ namespace ramses { if (ImGui::MenuItem("Copy all Appearance Binding inputs")) { - copyInputs(LogicViewer::ltnAppearance, m_logicEngine.getCollection()); + copyInputs(LogicViewer::ltnAppearance, m_logicEngine.getCollection()); } ImGui::EndPopup(); } if (openBindings) { - for (auto* obj : m_logicEngine.getCollection()) + for (auto* obj : m_logicEngine.getCollection()) { const bool open = DrawTreeNode(obj); drawNodeContextMenu(obj, LogicViewer::ltnAppearance); @@ -1339,11 +1339,11 @@ namespace ramses if (!context.empty()) { ImGui::TextUnformatted( - fmt::format("{}: [{}]: {} Type:{}[{}]", context.data(), obj->getId(), obj->getName(), GetLuaPrimitiveTypeName(obj->getDataType()), obj->getNumElements()).c_str()); + fmt::format("{}: [{}]: {} Type:{}[{}]", context.data(), obj->getSceneObjectId().getValue(), obj->getName(), GetLuaPrimitiveTypeName(obj->getDataType()), obj->getNumElements()).c_str()); } else { - ImGui::TextUnformatted(fmt::format("[{}]: {} Type:{}[{}]", obj->getId(), obj->getName(), GetLuaPrimitiveTypeName(obj->getDataType()), obj->getNumElements()).c_str()); + ImGui::TextUnformatted(fmt::format("[{}]: {} Type:{}[{}]", obj->getSceneObjectId().getValue(), obj->getName(), GetLuaPrimitiveTypeName(obj->getDataType()), obj->getNumElements()).c_str()); } } } diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerGui.h b/tools/ramses-logic-viewer/LogicViewerGui.h similarity index 98% rename from client/logic/tools/ramses-logic-viewer/LogicViewerGui.h rename to tools/ramses-logic-viewer/LogicViewerGui.h index 58ba20b36..26857b59e 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerGui.h +++ b/tools/ramses-logic-viewer/LogicViewerGui.h @@ -9,7 +9,7 @@ #pragma once #include "ImguiClientHelper.h" -#include "ramses-logic/Collection.h" +#include "ramses/client/logic/Collection.h" #include struct ImGuiSettingsHandler; diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp b/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp similarity index 95% rename from client/logic/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp rename to tools/ramses-logic-viewer/LogicViewerGuiApp.cpp index 55ad0f9ba..c71ab9968 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp +++ b/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp @@ -14,7 +14,6 @@ #include "LogicViewerGui.h" #include "LogicViewer.h" #include "LogicViewerSettings.h" -#include "ramses-logic/Logger.h" #include "ramses-cli.h" namespace ramses @@ -85,7 +84,7 @@ namespace ramses const bool autoDetectViewportSize = !customHeight && !customWidth; ramses::EFeatureLevel featureLevel = ramses::EFeatureLevel_01; - auto exitCode = GetFeatureLevelFromFiles(args.sceneFile(), args.logicFile(), featureLevel); + auto exitCode = GetFeatureLevelFromFile(args.sceneFile(), featureLevel); if (exitCode != 0) { return exitCode; @@ -147,12 +146,12 @@ namespace ramses if (m_headless) { - exitCode = LogicViewerApp::createViewer(args, featureLevel, LogicViewer::ScreenshotFunc()); + exitCode = LogicViewerApp::createViewer(args, LogicViewer::ScreenshotFunc()); } else { m_sceneSetup->apply(); - exitCode = LogicViewerApp::createViewer(args, featureLevel, takeScreenshot); + exitCode = LogicViewerApp::createViewer(args, takeScreenshot); } if (exitCode != 0) diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerGuiApp.h b/tools/ramses-logic-viewer/LogicViewerGuiApp.h similarity index 100% rename from client/logic/tools/ramses-logic-viewer/LogicViewerGuiApp.h rename to tools/ramses-logic-viewer/LogicViewerGuiApp.h diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerHeadlessApp.cpp b/tools/ramses-logic-viewer/LogicViewerHeadlessApp.cpp similarity index 91% rename from client/logic/tools/ramses-logic-viewer/LogicViewerHeadlessApp.cpp rename to tools/ramses-logic-viewer/LogicViewerHeadlessApp.cpp index 666a87098..708e228b7 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerHeadlessApp.cpp +++ b/tools/ramses-logic-viewer/LogicViewerHeadlessApp.cpp @@ -11,7 +11,6 @@ #include "Arguments.h" #include "LogicViewer.h" #include "LogicViewerSettings.h" -#include "ramses-logic/Logger.h" namespace ramses { @@ -54,7 +53,7 @@ namespace ramses CLI11_PARSE(cli, argc, argv); EFeatureLevel featureLevel{EFeatureLevel_01}; - auto exitCode = GetFeatureLevelFromFiles(args.sceneFile(), args.logicFile(), featureLevel); + auto exitCode = GetFeatureLevelFromFile(args.sceneFile(), featureLevel); if (exitCode != 0) { return exitCode; @@ -66,7 +65,7 @@ namespace ramses return exitCode; } - exitCode = LogicViewerApp::createViewer(args, featureLevel, LogicViewer::ScreenshotFunc()); + exitCode = LogicViewerApp::createViewer(args, LogicViewer::ScreenshotFunc()); if (exitCode != 0) { return exitCode; diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerHeadlessApp.h b/tools/ramses-logic-viewer/LogicViewerHeadlessApp.h similarity index 100% rename from client/logic/tools/ramses-logic-viewer/LogicViewerHeadlessApp.h rename to tools/ramses-logic-viewer/LogicViewerHeadlessApp.h diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerLog.cpp b/tools/ramses-logic-viewer/LogicViewerLog.cpp similarity index 94% rename from client/logic/tools/ramses-logic-viewer/LogicViewerLog.cpp rename to tools/ramses-logic-viewer/LogicViewerLog.cpp index f876ba381..6792ba961 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerLog.cpp +++ b/tools/ramses-logic-viewer/LogicViewerLog.cpp @@ -9,10 +9,10 @@ #include "LogicViewerLog.h" #include "LogicViewer.h" #include "LogicViewerSettings.h" -#include "ramses-client-api/Scene.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/Property.h" +#include "ramses/client/Scene.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/Property.h" #include "fmt/format.h" namespace ramses @@ -34,7 +34,7 @@ namespace ramses std::string prefix; if ((m_settings.luaPreferObjectIds) || obj->getName().empty()) { - prefix = fmt::format("{}[{}]", joinedPath, obj->getId()); + prefix = fmt::format("{}[{}]", joinedPath, obj->getSceneObjectId().getValue()); } else if (m_settings.luaPreferIdentifiers) { diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerLog.h b/tools/ramses-logic-viewer/LogicViewerLog.h similarity index 95% rename from client/logic/tools/ramses-logic-viewer/LogicViewerLog.h rename to tools/ramses-logic-viewer/LogicViewerLog.h index a8eb52ffc..6a30bcab5 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerLog.h +++ b/tools/ramses-logic-viewer/LogicViewerLog.h @@ -8,8 +8,8 @@ #pragma once -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/Collection.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/Collection.h" #include "LogicViewer.h" #include diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerLuaTypes.cpp b/tools/ramses-logic-viewer/LogicViewerLuaTypes.cpp similarity index 81% rename from client/logic/tools/ramses-logic-viewer/LogicViewerLuaTypes.cpp rename to tools/ramses-logic-viewer/LogicViewerLuaTypes.cpp index 2ce4448a6..a6e7ecba3 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerLuaTypes.cpp +++ b/tools/ramses-logic-viewer/LogicViewerLuaTypes.cpp @@ -8,22 +8,22 @@ #include "LogicViewerLuaTypes.h" #include "LogicViewer.h" -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/LogicNode.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/TimerNode.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesRenderPassBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesMeshNodeBinding.h" -#include "ramses-logic/AnchorPoint.h" -#include "ramses-logic/SkinBinding.h" -#include "ramses-logic/Property.h" -#include "internals/SolHelper.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicNode.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/TimerNode.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/RenderPassBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/MeshNodeBinding.h" +#include "ramses/client/logic/AnchorPoint.h" +#include "ramses/client/logic/SkinBinding.h" +#include "ramses/client/logic/Property.h" +#include "internal/logic/SolHelper.h" #include "fmt/format.h" #include "glm/gtx/range.hpp" @@ -157,10 +157,8 @@ namespace ramses return sol::object(L, sol::in_place, sol::lua_nil); } - template sol::object getChildProperty(ramses::Property& prop, sol::stack_object key, sol::this_state L) { - using ChildWrapper = std::conditional_t; auto strKey = key.as>(); Property* child = nullptr; if (strKey) @@ -182,35 +180,12 @@ namespace ramses } if (child) { - return sol::object(L, sol::in_place, ChildWrapper(*child)); + return sol::object(L, sol::in_place, PropertyWrapper(*child)); } return sol::object(L, sol::in_place, sol::lua_nil); } } // namespace - ConstPropertyWrapper::ConstPropertyWrapper(const ramses::Property& property) - : m_property(property) - { - } - - sol::object ConstPropertyWrapper::toString(sol::this_state L) - { - std::string name = "ConstProperty: "; - name += m_property.getName(); - return sol::object(L, sol::in_place, name); - } - - sol::object ConstPropertyWrapper::getValue(sol::this_state L) - { - return getPropertyValue(m_property, L); - } - - sol::object ConstPropertyWrapper::get(sol::stack_object key, sol::this_state L) - { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) avoid code duplication - return getChildProperty(const_cast(m_property), key, L); - } - PropertyWrapper::PropertyWrapper(ramses::Property& property) : m_property(property) { @@ -235,7 +210,7 @@ namespace ramses sol::object PropertyWrapper::get(sol::stack_object key, sol::this_state L) { - return getChildProperty(m_property, key, L); + return getChildProperty(m_property, key, L); } @@ -267,7 +242,7 @@ namespace ramses auto* outputs = m_logicNode.getOutputs(); if ((outputs != nullptr) && (strKey == LogicViewer::ltnOUT)) { - retval = sol::object(L, sol::in_place, ConstPropertyWrapper(*outputs)); + retval = sol::object(L, sol::in_place, PropertyWrapper(*outputs)); } } } @@ -298,7 +273,7 @@ namespace ramses auto intKey = key.as>(); if (intKey) { - auto* obj = m_logicEngine.findLogicObjectById(static_cast(*intKey)); + auto* obj = m_logicEngine.findObject(sceneObjectId_t{ static_cast(*intKey) }); if (obj != nullptr) { node = obj->template as(); @@ -316,7 +291,7 @@ namespace ramses template LogicNode* NodeListWrapper::find(std::string_view key) { - return m_logicEngine.findByName(key); + return m_logicEngine.findObject(key); } template @@ -335,12 +310,12 @@ namespace ramses template struct NodeListWrapper; template struct NodeListWrapper; template struct NodeListWrapper; - template struct NodeListWrapper; - template struct NodeListWrapper; - template struct NodeListWrapper; - template struct NodeListWrapper; - template struct NodeListWrapper; - template struct NodeListWrapper; + template struct NodeListWrapper; + template struct NodeListWrapper; + template struct NodeListWrapper; + template struct NodeListWrapper; + template struct NodeListWrapper; + template struct NodeListWrapper; template struct NodeListWrapper; template struct NodeListWrapper; } // namespace ramses diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerLuaTypes.h b/tools/ramses-logic-viewer/LogicViewerLuaTypes.h similarity index 84% rename from client/logic/tools/ramses-logic-viewer/LogicViewerLuaTypes.h rename to tools/ramses-logic-viewer/LogicViewerLuaTypes.h index adcd8d821..132cf8697 100644 --- a/client/logic/tools/ramses-logic-viewer/LogicViewerLuaTypes.h +++ b/tools/ramses-logic-viewer/LogicViewerLuaTypes.h @@ -8,8 +8,8 @@ #pragma once -#include "internals/SolWrapper.h" -#include "ramses-logic/Collection.h" +#include "internal/logic/SolWrapper.h" +#include "ramses/client/logic/Collection.h" namespace ramses { @@ -17,19 +17,6 @@ namespace ramses class LogicNode; class LogicEngine; - struct ConstPropertyWrapper - { - explicit ConstPropertyWrapper(const ramses::Property& property); - - sol::object getValue(sol::this_state L); - - sol::object toString(sol::this_state L); - - sol::object get(sol::stack_object key, sol::this_state L); - - const Property& m_property; - }; - struct PropertyWrapper { explicit PropertyWrapper(ramses::Property& property); diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerSettings.cpp b/tools/ramses-logic-viewer/LogicViewerSettings.cpp similarity index 100% rename from client/logic/tools/ramses-logic-viewer/LogicViewerSettings.cpp rename to tools/ramses-logic-viewer/LogicViewerSettings.cpp diff --git a/client/logic/tools/ramses-logic-viewer/LogicViewerSettings.h b/tools/ramses-logic-viewer/LogicViewerSettings.h similarity index 100% rename from client/logic/tools/ramses-logic-viewer/LogicViewerSettings.h rename to tools/ramses-logic-viewer/LogicViewerSettings.h diff --git a/client/logic/tools/ramses-logic-viewer/Result.h b/tools/ramses-logic-viewer/Result.h similarity index 100% rename from client/logic/tools/ramses-logic-viewer/Result.h rename to tools/ramses-logic-viewer/Result.h diff --git a/client/logic/tools/ramses-logic-viewer/SceneSetup.h b/tools/ramses-logic-viewer/SceneSetup.h similarity index 89% rename from client/logic/tools/ramses-logic-viewer/SceneSetup.h rename to tools/ramses-logic-viewer/SceneSetup.h index 3be4a7d1c..9b4263d34 100644 --- a/client/logic/tools/ramses-logic-viewer/SceneSetup.h +++ b/tools/ramses-logic-viewer/SceneSetup.h @@ -10,11 +10,11 @@ #include "ImguiClientHelper.h" -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" #include @@ -66,14 +66,14 @@ class OffscreenSetup : public ISceneSetup const auto guiSceneId = m_imguiHelper.getScene()->getSceneId(); const ramses::dataConsumerId_t consumerId(519); + m_imguiHelper.waitForSceneState(*m_imguiHelper.getScene(), ramses::RendererSceneState::Ready); + m_imguiHelper.waitForSceneState(*m_scene, ramses::RendererSceneState::Ready); + m_imguiHelper.getScene()->createTextureConsumer(*m_sampler, consumerId); m_imguiHelper.getScene()->flush(42); m_imguiHelper.waitForSceneVersion(guiSceneId, 42); m_imguiHelper.waitForOffscreenBufferCreated(m_ob); - m_imguiHelper.waitForSceneState(guiSceneId, ramses::RendererSceneState::Ready); - m_imguiHelper.waitForSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Ready); - m_sceneControl->setSceneDisplayBufferAssignment(m_scene->getSceneId(), m_ob); m_sceneControl->linkOffscreenBuffer(m_ob, guiSceneId, consumerId); m_sceneControl->flush(); @@ -82,8 +82,8 @@ class OffscreenSetup : public ISceneSetup m_sceneControl->setSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Rendered); m_sceneControl->flush(); - m_imguiHelper.waitForSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Rendered); - m_imguiHelper.waitForSceneState(guiSceneId, ramses::RendererSceneState::Rendered); + m_imguiHelper.waitForSceneState(*m_scene, ramses::RendererSceneState::Rendered); + m_imguiHelper.waitForSceneState(*m_imguiHelper.getScene(), ramses::RendererSceneState::Rendered); } [[nodiscard]] uint32_t getWidth() const override @@ -133,7 +133,7 @@ class FramebufferSetup : public ISceneSetup void apply() override { - m_imguiHelper.waitForSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Rendered); + m_imguiHelper.waitForSceneState(*m_scene, ramses::RendererSceneState::Rendered); const auto guiSceneId = m_imguiHelper.getScene()->getSceneId(); m_sceneControl->setSceneState(guiSceneId, ramses::RendererSceneState::Rendered); m_sceneControl->flush(); diff --git a/client/logic/tools/ramses-logic-viewer/UpdateReportSummary.h b/tools/ramses-logic-viewer/UpdateReportSummary.h similarity index 98% rename from client/logic/tools/ramses-logic-viewer/UpdateReportSummary.h rename to tools/ramses-logic-viewer/UpdateReportSummary.h index 1a0cc0824..550ac9a4a 100644 --- a/client/logic/tools/ramses-logic-viewer/UpdateReportSummary.h +++ b/tools/ramses-logic-viewer/UpdateReportSummary.h @@ -8,7 +8,7 @@ #pragma once -#include "ramses-logic/LogicEngineReport.h" +#include "ramses/client/logic/LogicEngineReport.h" #include #include #include diff --git a/client/logic/tools/ramses-logic-viewer/main.cpp b/tools/ramses-logic-viewer/main.cpp similarity index 100% rename from client/logic/tools/ramses-logic-viewer/main.cpp rename to tools/ramses-logic-viewer/main.cpp diff --git a/client/logic/tools/ramses-logic-viewer/main_headless.cpp b/tools/ramses-logic-viewer/main_headless.cpp similarity index 100% rename from client/logic/tools/ramses-logic-viewer/main_headless.cpp rename to tools/ramses-logic-viewer/main_headless.cpp diff --git a/renderer/ramses-renderer-main/CMakeLists.txt b/tools/ramses-renderer-standalone/CMakeLists.txt similarity index 92% rename from renderer/ramses-renderer-main/CMakeLists.txt rename to tools/ramses-renderer-standalone/CMakeLists.txt index 82edbd3b1..2a915bec1 100644 --- a/renderer/ramses-renderer-main/CMakeLists.txt +++ b/tools/ramses-renderer-standalone/CMakeLists.txt @@ -7,7 +7,7 @@ # ------------------------------------------------------------------------- createModuleWithRenderer( - NAME ramses-renderer + NAME ramses-renderer-standalone TYPE BINARY ENABLE_INSTALL ON SRC_FILES src/main.cpp diff --git a/renderer/ramses-renderer-main/src/main.cpp b/tools/ramses-renderer-standalone/src/main.cpp similarity index 82% rename from renderer/ramses-renderer-main/src/main.cpp rename to tools/ramses-renderer-standalone/src/main.cpp index 893f12178..51035fb39 100644 --- a/renderer/ramses-renderer-main/src/main.cpp +++ b/tools/ramses-renderer-standalone/src/main.cpp @@ -6,17 +6,17 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-framework-api/RamsesFramework.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "Utils/LogMacros.h" -#include "RendererMate.h" -#include "PlatformAbstraction/PlatformThread.h" -#include "Ramsh/RamshCommandExit.h" -#include "RamsesFrameworkImpl.h" -#include "Ramsh/Ramsh.h" +#include "ramses/framework/RamsesFramework.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "internal/Core/Utils/LogMacros.h" +#include "impl/RendererMate.h" +#include "internal/PlatformAbstraction/PlatformThread.h" +#include "internal/Ramsh/RamshCommandExit.h" +#include "impl/RamsesFrameworkImpl.h" +#include "internal/Ramsh/Ramsh.h" #include #include #include @@ -25,15 +25,15 @@ struct MappingCommand { ramses::displayId_t display; ramses::sceneId_t sceneId; - int32_t sceneRenderOrder; + int32_t sceneRenderOrder = 0; }; namespace CLI { inline std::istringstream& operator>>(std::istringstream& is, MappingCommand& val) { - uint32_t display; - uint64_t sceneId; + uint32_t display = 0; + uint64_t sceneId = 0; char separator = 0; is >> display >> separator >> sceneId >> separator >> val.sceneRenderOrder; val.sceneId = ramses::sceneId_t(sceneId); @@ -105,8 +105,8 @@ int32_t main(int32_t argc, char * argv[]) CLI11_PARSE(cli, argc, argv); ramses::RamsesFramework framework(config); - auto commandExit = std::make_shared(); - framework.m_impl.getRamsh().add(commandExit); + auto commandExit = std::make_shared(); + framework.impl().getRamsh().add(commandExit); ramses::RamsesRenderer& renderer(*framework.createRenderer(rendererConfig)); renderer.setSkippingOfUnmodifiedBuffers(skub); @@ -123,10 +123,10 @@ int32_t main(int32_t argc, char * argv[]) renderer.flush(); renderer.startThread(); - ramses::RendererMate rendererMate(renderer.m_impl, framework.m_impl); + ramses::internal::RendererMate rendererMate(renderer.impl(), framework.impl()); // allow camera free move rendererMate.enableKeysHandling(); - ramses::RendererMateAutoShowHandler dmEventHandler(rendererMate, !disableAutoMapping); + ramses::internal::RendererMateAutoShowHandler dmEventHandler(rendererMate, !disableAutoMapping); // apply mapping commands for (const auto& command : mappingCommands) @@ -139,7 +139,7 @@ int32_t main(int32_t argc, char * argv[]) while (!commandExit->exitRequested() && rendererMate.isRunning()) { rendererMate.dispatchAndFlush(dmEventHandler); - ramses_internal::PlatformThread::Sleep(20u); + ramses::internal::PlatformThread::Sleep(20u); } renderer.stopThread(); diff --git a/utils/ramses-scene-viewer/CMakeLists.txt b/tools/ramses-scene-viewer/CMakeLists.txt similarity index 100% rename from utils/ramses-scene-viewer/CMakeLists.txt rename to tools/ramses-scene-viewer/CMakeLists.txt diff --git a/utils/ramses-scene-viewer/src/ProgressMonitor.h b/tools/ramses-scene-viewer/src/ProgressMonitor.h similarity index 90% rename from utils/ramses-scene-viewer/src/ProgressMonitor.h rename to tools/ramses-scene-viewer/src/ProgressMonitor.h index 54536e0a1..af66104c9 100644 --- a/utils/ramses-scene-viewer/src/ProgressMonitor.h +++ b/tools/ramses-scene-viewer/src/ProgressMonitor.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENE_VIEWER_PROGRESSMONITOR_H -#define RAMSES_SCENE_VIEWER_PROGRESSMONITOR_H +#pragma once #include #include #include #include -namespace ramses_internal +namespace ramses::internal { class ProgressMonitor { @@ -69,14 +68,12 @@ namespace ramses_internal m_futures.clear(); } - std::atomic canceled; - std::atomic current; + std::atomic canceled{}; + std::atomic current{}; private: uint32_t m_total = 0; std::string m_description; FutureList m_futures; }; -} // ramses_internal - -#endif +} // ramses::internal diff --git a/utils/ramses-scene-viewer/src/ResourceList.h b/tools/ramses-scene-viewer/src/ResourceList.h similarity index 79% rename from utils/ramses-scene-viewer/src/ResourceList.h rename to tools/ramses-scene-viewer/src/ResourceList.h index f1482aaa7..7646ec3e7 100644 --- a/utils/ramses-scene-viewer/src/ResourceList.h +++ b/tools/ramses-scene-viewer/src/ResourceList.h @@ -8,21 +8,21 @@ #pragma once -#include "RamsesObjectVector.h" -#include "SceneAPI/ResourceContentHash.h" -#include "ramses-client.h" -#include "RamsesClientImpl.h" -#include "ResourceImpl.h" -#include "SceneDumper.h" +#include "impl/RamsesObjectVector.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "ramses/client/ramses-client.h" +#include "impl/RamsesClientImpl.h" +#include "impl/ResourceImpl.h" +#include "impl/SceneDumper.h" #include #include -namespace ramses_internal +namespace ramses::internal { class ResourceList { public: - ResourceList(ramses::Scene& scene, const ramses::SceneDumper::RamsesObjectImplSet& usedObjects) + ResourceList(ramses::Scene& scene, const ramses::internal::SceneDumper::RamsesObjectImplSet& usedObjects) : m_scene(scene) , m_usedObjects(usedObjects) { @@ -66,7 +66,7 @@ namespace ramses_internal /** * Returns the index of the current order criteria. - * The returned index can be used to access an item in #ramses_internal::ResourceList::orderCriteriaItems + * The returned index can be used to access an item in #ramses::internal::ResourceList::orderCriteriaItems */ [[nodiscard]] int getOrderCriteriaIndex() const { @@ -116,7 +116,7 @@ namespace ramses_internal Compressed = 1, }; - static uint32_t GetCompressedSize(const ramses_internal::ManagedResource& resource) + static uint32_t GetCompressedSize(const ramses::internal::ManagedResource& resource) { const auto compressed = resource->getCompressedDataSize(); return (compressed == 0) ? resource->getDecompressedDataSize() : compressed; @@ -127,11 +127,11 @@ namespace ramses_internal void sort(); ramses::Scene& m_scene; - const ramses::SceneDumper::RamsesObjectImplSet& m_usedObjects; + const ramses::internal::SceneDumper::RamsesObjectImplSet& m_usedObjects; - ramses::RamsesObjectVector m_objects; + SceneObjectVector m_objects; - std::unordered_multimap m_hashLookup; + std::unordered_multimap m_hashLookup; int m_displayLimit = 1000; OrderCriteria m_orderCriteria = OrderCriteria::Uncompressed; @@ -146,20 +146,17 @@ namespace ramses_internal inline void ResourceList::sort() { auto cmp = [&](ramses::RamsesObject* a, ramses::RamsesObject* b) { - ManagedResource resourceA = m_scene.getRamsesClient().m_impl.getResource(static_cast(a)->m_impl.getLowlevelResourceHash()); - ManagedResource resourceB = m_scene.getRamsesClient().m_impl.getResource(static_cast(b)->m_impl.getLowlevelResourceHash()); + ManagedResource resourceA = m_scene.getRamsesClient().impl().getResource(static_cast(a)->impl().getLowlevelResourceHash()); + ManagedResource resourceB = m_scene.getRamsesClient().impl().getResource(static_cast(b)->impl().getLowlevelResourceHash()); if (m_orderCriteria == OrderCriteria::Compressed) { const auto sizeA = resourceA ? GetCompressedSize(resourceA) : 0u; const auto sizeB = resourceB ? GetCompressedSize(resourceB) : 0u; return sizeA > sizeB; } - else - { - const auto sizeA = resourceA ? resourceA->getDecompressedDataSize() : 0u; - const auto sizeB = resourceB ? resourceB->getDecompressedDataSize() : 0u; - return sizeA > sizeB; - } + const auto sizeA = resourceA ? resourceA->getDecompressedDataSize() : 0u; + const auto sizeB = resourceB ? resourceB->getDecompressedDataSize() : 0u; + return sizeA > sizeB; }; std::sort(m_objects.begin(), m_objects.end(), cmp); } @@ -168,7 +165,7 @@ namespace ramses_internal { if (m_objects.empty()) { - const auto& reg = m_scene.m_impl.getObjectRegistry(); + const auto& reg = m_scene.impl().getObjectRegistry(); reg.getObjectsOfType(m_objects, ramses::ERamsesObjectType::Resource); m_displayLimit = std::min(m_displayLimit, static_cast(m_objects.size())); @@ -177,11 +174,11 @@ namespace ramses_internal for (auto it : m_objects) { auto hlResource = static_cast(it); - auto resource = m_scene.getRamsesClient().m_impl.getResource(hlResource->m_impl.getLowlevelResourceHash()); - m_hashLookup.insert({hlResource->m_impl.getLowlevelResourceHash(), hlResource}); + auto resource = m_scene.getRamsesClient().impl().getResource(hlResource->impl().getLowlevelResourceHash()); + m_hashLookup.insert({hlResource->impl().getLowlevelResourceHash(), hlResource}); if (resource) { - if (m_usedObjects.contains(&hlResource->m_impl)) + if (m_usedObjects.contains(&hlResource->impl())) { // don't count duplicates m_compressedSize += GetCompressedSize(resource); @@ -200,8 +197,8 @@ namespace ramses_internal m_displayedSize = 0u; std::for_each(begin(), end(), [&](ramses::RamsesObject* obj) { auto hlResource = static_cast(obj); - auto resource = m_scene.getRamsesClient().m_impl.getResource(hlResource->m_impl.getLowlevelResourceHash()); - if (resource && m_usedObjects.contains(&hlResource->m_impl)) + auto resource = m_scene.getRamsesClient().impl().getResource(hlResource->impl().getLowlevelResourceHash()); + if (resource && m_usedObjects.contains(&hlResource->impl())) { // don't count duplicates m_displayedSize += (m_orderCriteria == OrderCriteria::Compressed) ? GetCompressedSize(resource) : resource->getDecompressedDataSize(); diff --git a/utils/ramses-scene-viewer/src/SceneSetup.h b/tools/ramses-scene-viewer/src/SceneSetup.h similarity index 81% rename from utils/ramses-scene-viewer/src/SceneSetup.h rename to tools/ramses-scene-viewer/src/SceneSetup.h index 7a1e67930..30dd22d72 100644 --- a/utils/ramses-scene-viewer/src/SceneSetup.h +++ b/tools/ramses-scene-viewer/src/SceneSetup.h @@ -6,16 +6,15 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENE_VIEWER_SCENESETUP_H -#define RAMSES_SCENE_VIEWER_SCENESETUP_H +#pragma once #include "ImguiClientHelper.h" -#include "ramses-client.h" +#include "ramses/client/ramses-client.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/RendererSceneControl.h" #include @@ -38,7 +37,7 @@ class ISceneSetup class OffscreenSetup : public ISceneSetup { public: - OffscreenSetup(ramses_internal::ImguiClientHelper& imguiHelper, ramses::RamsesRenderer* renderer, ramses::Scene* scene, ramses::displayId_t display, uint32_t width, uint32_t height) + OffscreenSetup(ramses::internal::ImguiClientHelper& imguiHelper, ramses::RamsesRenderer* renderer, ramses::Scene* scene, ramses::displayId_t display, uint32_t width, uint32_t height) : m_imguiHelper(imguiHelper) , m_sceneControl(renderer->getSceneControlAPI()) , m_scene(scene) @@ -67,14 +66,14 @@ class OffscreenSetup : public ISceneSetup const auto guiSceneId = m_imguiHelper.getScene()->getSceneId(); const ramses::dataConsumerId_t consumerId(519); + m_imguiHelper.waitForSceneState(*m_imguiHelper.getScene(), ramses::RendererSceneState::Ready); + m_imguiHelper.waitForSceneState(*m_scene, ramses::RendererSceneState::Ready); + m_imguiHelper.getScene()->createTextureConsumer(*m_sampler, consumerId); m_imguiHelper.getScene()->flush(42); m_imguiHelper.waitForSceneVersion(guiSceneId, 42); m_imguiHelper.waitForOffscreenBufferCreated(m_ob); - m_imguiHelper.waitForSceneState(guiSceneId, ramses::RendererSceneState::Ready); - m_imguiHelper.waitForSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Ready); - m_sceneControl->setSceneDisplayBufferAssignment(m_scene->getSceneId(), m_ob); m_sceneControl->linkOffscreenBuffer(m_ob, guiSceneId, consumerId); m_sceneControl->flush(); @@ -83,8 +82,8 @@ class OffscreenSetup : public ISceneSetup m_sceneControl->setSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Rendered); m_sceneControl->flush(); - m_imguiHelper.waitForSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Rendered); - m_imguiHelper.waitForSceneState(guiSceneId, ramses::RendererSceneState::Rendered); + m_imguiHelper.waitForSceneState(*m_scene, ramses::RendererSceneState::Rendered); + m_imguiHelper.waitForSceneState(*m_imguiHelper.getScene(), ramses::RendererSceneState::Rendered); } [[nodiscard]] uint32_t getWidth() const override @@ -108,7 +107,7 @@ class OffscreenSetup : public ISceneSetup } private: - ramses_internal::ImguiClientHelper& m_imguiHelper; + ramses::internal::ImguiClientHelper& m_imguiHelper; ramses::RendererSceneControl* m_sceneControl; ramses::Scene* m_scene; uint32_t m_width; @@ -120,7 +119,7 @@ class OffscreenSetup : public ISceneSetup class FramebufferSetup : public ISceneSetup { public: - FramebufferSetup(ramses_internal::ImguiClientHelper& imguiHelper, ramses::RamsesRenderer* renderer, ramses::Scene* scene, ramses::displayId_t display) + FramebufferSetup(ramses::internal::ImguiClientHelper& imguiHelper, ramses::RamsesRenderer* renderer, ramses::Scene* scene, ramses::displayId_t display) : m_imguiHelper(imguiHelper) , m_sceneControl(renderer->getSceneControlAPI()) , m_scene(scene) @@ -142,7 +141,7 @@ class FramebufferSetup : public ISceneSetup { if (m_scene) { - m_imguiHelper.waitForSceneState(m_scene->getSceneId(), ramses::RendererSceneState::Rendered); + m_imguiHelper.waitForSceneState(*m_scene, ramses::RendererSceneState::Rendered); } const auto guiSceneId = m_imguiHelper.getScene()->getSceneId(); m_sceneControl->setSceneState(guiSceneId, ramses::RendererSceneState::Rendered); @@ -170,10 +169,8 @@ class FramebufferSetup : public ISceneSetup } private: - ramses_internal::ImguiClientHelper& m_imguiHelper; + ramses::internal::ImguiClientHelper& m_imguiHelper; ramses::RendererSceneControl* m_sceneControl; ramses::Scene* m_scene; }; -#endif - diff --git a/utils/ramses-scene-viewer/src/SceneViewer.cpp b/tools/ramses-scene-viewer/src/SceneViewer.cpp similarity index 84% rename from utils/ramses-scene-viewer/src/SceneViewer.cpp rename to tools/ramses-scene-viewer/src/SceneViewer.cpp index 05e57ba47..a585ca1dd 100644 --- a/utils/ramses-scene-viewer/src/SceneViewer.cpp +++ b/tools/ramses-scene-viewer/src/SceneViewer.cpp @@ -10,27 +10,27 @@ #include "SceneViewer.h" #include "SceneViewerGui.h" -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" -#include "Utils/LogMacros.h" -#include "Utils/RamsesLogger.h" +#include "internal/Core/Utils/LogMacros.h" +#include "internal/Core/Utils/RamsesLogger.h" #include "ImguiClientHelper.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-framework-api/RamsesFramework.h" -#include "RamsesFrameworkConfigImpl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/framework/RamsesFramework.h" +#include "impl/RamsesFrameworkConfigImpl.h" -#include "PlatformAbstraction/PlatformThread.h" +#include "internal/PlatformAbstraction/PlatformThread.h" #include -#include "Utils/Image.h" -#include "Utils/File.h" +#include "internal/Core/Utils/Image.h" +#include "internal/Core/Utils/File.h" #include "SceneSetup.h" #include "ramses-cli.h" -namespace ramses_internal +namespace ramses::internal { namespace { @@ -91,7 +91,7 @@ namespace ramses_internal int SceneViewer::run() { - GetRamsesLogger().initialize(m_frameworkConfig.m_impl.get().loggerConfig, false, true); + GetRamsesLogger().initialize(m_frameworkConfig.impl().loggerConfig, false, true); const auto scenePathAndFile = m_sceneName; const File sceneFile(scenePathAndFile); m_sceneName = sceneFile.getFileName(); @@ -125,7 +125,9 @@ namespace ramses_internal framework.connect(); LOG_INFO(CONTEXT_CLIENT, "Load scene:" << sceneFile); - auto loadedScene = client->loadSceneFromFile(sceneFile); + // scene viewer relies on resources being kept in memory (e.g. to query size), + // load scene as 'remote' which uses a shadowcopy scene and guarantees to keep resources in memory + auto loadedScene = client->loadSceneFromFile(sceneFile, SceneConfig({}, EScenePublicationMode::LocalAndRemote)); if (loadedScene == nullptr) { LOG_ERROR(CONTEXT_CLIENT, "Loading scene failed!"); @@ -134,9 +136,10 @@ namespace ramses_internal } loadedScene->publish(); loadedScene->flush(); + ramses::ValidationReport validationReport; if (!m_noValidation) { - validateContent(*loadedScene); + validateContent(validationReport, *loadedScene); } const bool customWidth = m_width ? (m_width->count() > 0) : false; @@ -178,9 +181,10 @@ namespace ramses_internal sceneSetup = std::make_unique(imguiHelper, renderer, loadedScene, displayId); break; } + loadedScene->flush(); sceneSetup->apply(); - SceneViewerGui gui(*loadedScene, sceneFile, imguiHelper); + SceneViewerGui gui(*loadedScene, sceneFile, imguiHelper, validationReport); gui.setSceneTexture(sceneSetup->getTextureSampler(), winWidth, winHeight); if (!m_screenshotFile.empty()) @@ -211,29 +215,27 @@ namespace ramses_internal ImGui::EndFrame(); imguiHelper.draw(); } - ramses_internal::PlatformThread::Sleep(20u); + ramses::internal::PlatformThread::Sleep(20u); } } return 0; } - void SceneViewer::validateContent(const ramses::Scene& scene) const + void SceneViewer::validateContent(ramses::ValidationReport& report, const ramses::Scene& scene) const { - ramses::status_t validateStatus = scene.validate(); - if (validateStatus != ramses::StatusOK) + scene.validate(report); + + if (m_guiMode == GuiMode::Off) { - LOG_ERROR(CONTEXT_CLIENT, "Scene validate failed: " << scene.getStatusMessage(validateStatus)); + LOG_INFO(CONTEXT_CLIENT, "Scene validation report: " << report.impl().toString()); } - const auto reportLevel = (m_guiMode == GuiMode::Off) ? ramses::EValidationSeverity::Info : ramses::EValidationSeverity::Warning; - LOG_INFO(CONTEXT_CLIENT, "Scene validation report: " << scene.getValidationReport(reportLevel)); - if (!m_validationOutput.empty()) { // dump scene verification const std::string validationFilePath = m_validationOutput + m_sceneName + "_validationReport.txt"; std::ofstream validationFile(validationFilePath); - validationFile << scene.getValidationReport(ramses::EValidationSeverity::Info) << std::endl; + validationFile << report.impl().toString() << std::endl; // dump unused objects const std::string unrequiredObjectsReportFilePath = m_validationOutput + m_sceneName + "_unrequObjsReport.txt"; std::ofstream unrequObjsOfstream(unrequiredObjectsReportFilePath); diff --git a/utils/ramses-scene-viewer/src/SceneViewer.h b/tools/ramses-scene-viewer/src/SceneViewer.h similarity index 83% rename from utils/ramses-scene-viewer/src/SceneViewer.h rename to tools/ramses-scene-viewer/src/SceneViewer.h index 57ebe9653..00734285d 100644 --- a/utils/ramses-scene-viewer/src/SceneViewer.h +++ b/tools/ramses-scene-viewer/src/SceneViewer.h @@ -6,12 +6,11 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENE_VIEWER_SCENEVIEWER_H -#define RAMSES_SCENE_VIEWER_SCENEVIEWER_H +#pragma once -#include "ramses-framework-api/RamsesFrameworkConfig.h" -#include "ramses-renderer-api/RendererConfig.h" -#include "ramses-renderer-api/DisplayConfig.h" +#include "ramses/framework/RamsesFrameworkConfig.h" +#include "ramses/renderer/RendererConfig.h" +#include "ramses/renderer/DisplayConfig.h" #include #include @@ -22,6 +21,7 @@ namespace ramses class RamsesClient; class Scene; class RamsesRenderer; + class ValidationReport; } namespace CLI @@ -30,7 +30,7 @@ namespace CLI class Option; } -namespace ramses_internal +namespace ramses::internal { class SceneViewer { @@ -51,7 +51,7 @@ namespace ramses_internal }; int loadAndRenderScene(const std::string& sceneFile); - void validateContent(const ramses::Scene& scene) const; + void validateContent(ramses::ValidationReport& report, const ramses::Scene& scene) const; std::string m_sceneName; GuiMode m_guiMode = GuiMode::On; @@ -68,5 +68,3 @@ namespace ramses_internal CLI::Option* m_height = nullptr; }; } - -#endif diff --git a/utils/ramses-scene-viewer/src/SceneViewerGui.cpp b/tools/ramses-scene-viewer/src/SceneViewerGui.cpp similarity index 70% rename from utils/ramses-scene-viewer/src/SceneViewerGui.cpp rename to tools/ramses-scene-viewer/src/SceneViewerGui.cpp index 906fc0639..f7af89ff9 100644 --- a/utils/ramses-scene-viewer/src/SceneViewerGui.cpp +++ b/tools/ramses-scene-viewer/src/SceneViewerGui.cpp @@ -8,54 +8,53 @@ // ------------------------------------------------------------------------- #include "SceneViewerGui.h" -#include "SceneDumper.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-client-api/Scene.h" -#include "ramses-utils.h" - -#include "SceneImpl.h" -#include "NodeImpl.h" -#include "PickableObjectImpl.h" -#include "MeshNodeImpl.h" -#include "CameraNodeImpl.h" -#include "MeshNodeImpl.h" -#include "RenderPassImpl.h" -#include "RenderGroupImpl.h" -#include "RenderTargetImpl.h" -#include "RenderBufferImpl.h" -#include "GeometryBindingImpl.h" -#include "ArrayBufferImpl.h" -#include "EffectImpl.h" -#include "Texture2DImpl.h" -#include "Texture3DImpl.h" -#include "Texture2DBufferImpl.h" -#include "TextureCubeImpl.h" -#include "TextureSamplerImpl.h" -#include "DataObjectImpl.h" -#include "RamsesClientImpl.h" -#include "EffectInputImpl.h" -#include "SceneReferenceImpl.h" -#include "BlitPassImpl.h" -#include "RamsesObjectRegistryIterator.h" -#include "Resource/EffectResource.h" -#include "Resource/TextureResource.h" -#include "RotationTypeUtils.h" -#include "TextureUtils.h" -#include "Scene/ClientScene.h" -#include "Utils/File.h" +#include "impl/SceneDumper.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/client/Scene.h" +#include "ramses/client/ramses-utils.h" + +#include "impl/SceneImpl.h" +#include "impl/NodeImpl.h" +#include "impl/PickableObjectImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/CameraNodeImpl.h" +#include "impl/MeshNodeImpl.h" +#include "impl/RenderPassImpl.h" +#include "impl/RenderGroupImpl.h" +#include "impl/RenderTargetImpl.h" +#include "impl/RenderBufferImpl.h" +#include "impl/GeometryImpl.h" +#include "impl/ArrayBufferImpl.h" +#include "impl/EffectImpl.h" +#include "impl/Texture2DImpl.h" +#include "impl/Texture3DImpl.h" +#include "impl/Texture2DBufferImpl.h" +#include "impl/TextureCubeImpl.h" +#include "impl/TextureSamplerImpl.h" +#include "impl/DataObjectImpl.h" +#include "impl/RamsesClientImpl.h" +#include "impl/EffectInputImpl.h" +#include "impl/SceneReferenceImpl.h" +#include "impl/BlitPassImpl.h" +#include "impl/SceneObjectRegistryIterator.h" +#include "internal/SceneGraph/Resource/EffectResource.h" +#include "internal/SceneGraph/Resource/TextureResource.h" +#include "impl/TextureUtils.h" +#include "internal/SceneGraph/Scene/ClientScene.h" +#include "internal/Core/Utils/File.h" #include "glm/gtc/type_ptr.hpp" - -namespace ramses_internal +#include "impl/TextureEnumsImpl.h" +namespace ramses::internal { namespace { - void getRotation(ramses::NodeImpl* node, glm::vec4& rot, ERotationType& rotationConventionInternal) + void getRotation(NodeImpl* node, glm::vec4& rot, ERotationType& rotationConventionInternal) { rotationConventionInternal = node->getIScene().getRotationType(node->getTransformHandle()); rot = node->getIScene().getRotation(node->getTransformHandle()); } - void setRotation(ramses::NodeImpl* node, const glm::vec4& rot, ramses_internal::ERotationType rotationType) + void setRotation(NodeImpl* node, const glm::vec4& rot, ERotationType rotationType) { if (rotationType == ERotationType::Quaternion) { @@ -63,11 +62,11 @@ namespace ramses_internal } else { - node->setRotation({rot.x, rot.y, rot.z}, ramses::RotationTypeUtils::ConvertRotationTypeFromInternal(rotationType)); + node->setRotation({rot.x, rot.y, rot.z}, rotationType); } } - ramses::EVisibilityMode getEffectiveVisibility(const ramses::NodeImpl& obj) + ramses::EVisibilityMode getEffectiveVisibility(const NodeImpl& obj) { auto visibility = obj.getVisibility(); for (auto parent = obj.getParentImpl(); parent != nullptr; parent = parent->getParentImpl()) @@ -79,16 +78,16 @@ namespace ramses_internal return visibility; } - int32_t getRenderOrder(const ramses::RenderGroupImpl& rg, const ramses::RamsesObjectImpl& child) + int32_t getRenderOrder(const RenderGroupImpl& rg, const SceneObjectImpl& child) { int32_t order = 0; switch (child.getType()) { case ramses::ERamsesObjectType::MeshNode: - rg.getMeshNodeOrder(static_cast(child), order); + rg.getMeshNodeOrder(static_cast(child), order); break; case ramses::ERamsesObjectType::RenderGroup: - rg.getRenderGroupOrder(static_cast(child), order); + rg.getRenderGroupOrder(static_cast(child), order); break; default: assert(false); @@ -101,8 +100,10 @@ namespace ramses_internal { switch (t) { + case ramses::EDataType::Bool: + return "Bool"; case ramses::EDataType::Int32: - return "UInt32"; + return "Int32"; case ramses::EDataType::UInt16: return "UInt16"; case ramses::EDataType::UInt32: @@ -157,7 +158,7 @@ namespace ramses_internal return "n.a."; } - const char* shortName(ramses_internal::EDataType t) { + const char* shortName(ramses::internal::EDataType t) { const char* name = EnumToString(t); assert(std::string(name).find("DATATYPE_") == 0); return name + 9; @@ -165,23 +166,38 @@ namespace ramses_internal const char* shortName(ramses::ERamsesObjectType t) { - const char* name = ramses::RamsesObjectTypeUtils::GetRamsesObjectTypeName(t); - assert(std::string(name).find("ERamsesObjectType_") == 0); - return name + 18; + return RamsesObjectTypeUtils::GetRamsesObjectTypeName(t); + } + + const char* EnumToString(ramses::EIssueType issueType) + { + switch (issueType) + { + case ramses::EIssueType::Warning: + return "Warning"; + case ramses::EIssueType::Error: + return "Error"; + } + return ""; } } // namespace template - bool SceneViewerGui::drawRamsesObject(ramses::RamsesObjectImpl& obj, const C& drawTreeNode) + bool SceneViewerGui::drawRamsesObject(SceneObjectImpl& obj, const C& drawTreeNode) { - const char* report = obj.getValidationReport(ramses::EValidationSeverity::Warning); - const bool hasIssues = report && report[0] != 0; + ramses::ValidationReport report; + obj.getRamsesObject().validate(report); + const bool hasIssues = report.hasIssue(); const bool isUnused = !m_usedObjects.contains(&obj); - if (hasIssues) + if (report.hasError()) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 0, 0).Value); } + else if (hasIssues) + { + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 255, 0).Value); + } else if (isUnused) { ImGui::PushStyleColor(ImGuiCol_Text, ImColor(127, 127, 127).Value); @@ -201,10 +217,8 @@ namespace ramses_internal if (hasIssues) { - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", report); if (isOpen) - ImGui::TextWrapped("%s", report); + DrawIssues(obj, report); ImGui::PopStyleColor(); } else if (isUnused) @@ -218,14 +232,35 @@ namespace ramses_internal return isOpen; } - bool SceneViewerGui::drawRamsesObject(ramses::RamsesObjectImpl& obj) + bool SceneViewerGui::drawRamsesObject(SceneObjectImpl& obj) { return drawRamsesObject(obj, [&]() { - return ImGui::TreeNode(&obj, "%s[%u]: %s", shortName(obj.getType()), obj.getObjectRegistryHandle().asMemoryHandle(), obj.getName().c_str()); + return ImGui::TreeNode(&obj, "%s[%lu]: %s", shortName(obj.getType()), obj.getSceneObjectId().getValue(), obj.getName().c_str()); }); } - void SceneViewerGui::drawUnusedObject(ramses::RamsesObjectImpl& obj) + void SceneViewerGui::DrawIssues(const SceneObjectImpl& obj, const ramses::ValidationReport& report) + { + for (auto& m : report.getIssues()) + { + if (m.object == &obj.getRamsesObject() && m.type <= ramses::EIssueType::Warning) + { + switch (m.type) + { + case ramses::EIssueType::Error: + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 0, 0).Value); + break; + case ramses::EIssueType::Warning: + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(255, 255, 0).Value); + break; + } + ImGui::BulletText("%s", m.message.c_str()); + ImGui::PopStyleColor(); + } + } + } + + void SceneViewerGui::drawUnusedObject(SceneObjectImpl& obj) { if (obj.isOfType(ramses::ERamsesObjectType::Resource)) { @@ -233,11 +268,11 @@ namespace ramses_internal if (ImGui::TreeNode("Duplicates (same hash):")) { m_resourceInfo->reloadIfEmpty(); - auto& hlResource = static_cast(obj); + auto& hlResource = static_cast(obj); auto range = m_resourceInfo->equal_range(hlResource.getLowlevelResourceHash()); for (auto it = range.first; it != range.second; ++it) { - draw(it->second->m_impl); + draw(it->second->impl()); } ImGui::TreePop(); } @@ -256,33 +291,32 @@ namespace ramses_internal } } - SceneViewerGui::SceneViewerGui(ramses::Scene& scene, const std::string& filename, ImguiClientHelper& imguiHelper) + SceneViewerGui::SceneViewerGui(ramses::Scene& scene, const std::string& filename, ImguiClientHelper& imguiHelper, const ramses::ValidationReport& report) : m_scene(scene) + , m_validationReport(report) , m_loadedSceneFile(filename) , m_filename(filename) , m_imageCache(imguiHelper.getScene()) { - ramses_internal::StringOutputStream dummyStream; - ramses::SceneDumper sceneDumper(scene.m_impl); + ramses::internal::StringOutputStream dummyStream; + SceneDumper sceneDumper(scene.impl()); sceneDumper.dumpUnrequiredObjects(dummyStream); m_usedObjects = sceneDumper.getRequiredObjects(); m_resourceInfo = std::make_unique(scene, m_usedObjects); - const char* report = m_scene.getValidationReport(ramses::EValidationSeverity::Warning); - m_hasSceneErrors = ((report != nullptr) && (report[0] != 0)); ImGuiIO& io = ImGui::GetIO(); // NOLINTNEXTLINE(hicpp-signed-bitwise) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; } - const ramses::RenderBuffer* SceneViewerGui::findRenderBuffer(ramses_internal::RenderBufferHandle renderBufferHandle) const + const ramses::RenderBuffer* SceneViewerGui::findRenderBuffer(ramses::internal::RenderBufferHandle renderBufferHandle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isRenderBufferAllocated(renderBufferHandle); + const bool isAllocated = m_scene.impl().getIScene().isRenderBufferAllocated(renderBufferHandle); if (isAllocated) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::RenderBuffer); - while (const ramses::RenderBuffer* renderBuffer = iter.getNext()) + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::RenderBuffer); + while (const auto* renderBuffer = iter.getNext()) { - if (renderBufferHandle == renderBuffer->m_impl.getRenderBufferHandle()) + if (renderBufferHandle == renderBuffer->impl().getRenderBufferHandle()) { return renderBuffer; } @@ -291,15 +325,15 @@ namespace ramses_internal return nullptr; } - const ramses::Texture2DBuffer* SceneViewerGui::findTextureBuffer(ramses_internal::TextureBufferHandle handle) const + const ramses::Texture2DBuffer* SceneViewerGui::findTextureBuffer(ramses::internal::TextureBufferHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isTextureBufferAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isTextureBufferAllocated(handle); if (isAllocated) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::Texture2DBuffer); - while (const ramses::Texture2DBuffer* textureBuffer = iter.getNext()) + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::Texture2DBuffer); + while (const auto* textureBuffer = iter.getNext()) { - if (handle == textureBuffer->m_impl.getTextureBufferHandle()) + if (handle == textureBuffer->impl().getTextureBufferHandle()) { return textureBuffer; } @@ -308,15 +342,15 @@ namespace ramses_internal return nullptr; } - const ramses::TextureSampler* SceneViewerGui::findTextureSampler(ramses_internal::TextureSamplerHandle handle) const + const ramses::TextureSampler* SceneViewerGui::findTextureSampler(ramses::internal::TextureSamplerHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isTextureSamplerAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isTextureSamplerAllocated(handle); if (isAllocated) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::TextureSampler); - while (const ramses::TextureSampler* textureSampler = iter.getNext()) + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::TextureSampler); + while (const auto* textureSampler = iter.getNext()) { - if (handle == textureSampler->m_impl.getTextureSamplerHandle()) + if (handle == textureSampler->impl().getTextureSamplerHandle()) { return textureSampler; } @@ -325,15 +359,15 @@ namespace ramses_internal return nullptr; } - const ramses::ArrayBuffer* SceneViewerGui::findArrayBuffer(ramses_internal::DataBufferHandle handle) const + const ramses::ArrayBuffer* SceneViewerGui::findArrayBuffer(ramses::internal::DataBufferHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isDataBufferAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isDataBufferAllocated(handle); if (isAllocated) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::ArrayBufferObject); - while (const ramses::ArrayBuffer* dataBuffer = iter.getNext()) + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::ArrayBuffer); + while (const auto* dataBuffer = iter.getNext()) { - if (handle == dataBuffer->m_impl.getDataBufferHandle()) + if (handle == dataBuffer->impl().getDataBufferHandle()) { return dataBuffer; } @@ -342,15 +376,15 @@ namespace ramses_internal return nullptr; } - const ramses::Node* SceneViewerGui::findNode(ramses_internal::NodeHandle handle) const + const ramses::Node* SceneViewerGui::findNode(ramses::internal::NodeHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isNodeAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isNodeAllocated(handle); if (isAllocated) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::Node); - while (const ramses::Node* node = iter.getNext()) + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::Node); + while (const auto* node = iter.getNext()) { - if (handle == node->m_impl.getNodeHandle()) + if (handle == node->impl().getNodeHandle()) { return node; } @@ -359,32 +393,32 @@ namespace ramses_internal return nullptr; } - const ramses::DataObject* SceneViewerGui::findDataObject(ramses_internal::DataInstanceHandle handle) const + const ramses::DataObject* SceneViewerGui::findDataObject(ramses::internal::DataInstanceHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isDataInstanceAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isDataInstanceAllocated(handle); if (isAllocated) { - ramses::RamsesObjectVector objects; - m_scene.m_impl.getObjectRegistry().getObjectsOfType(objects, ramses::ERamsesObjectType::DataObject); - for (auto it = objects.begin(); it != objects.end(); ++it) + SceneObjectVector objects; + m_scene.impl().getObjectRegistry().getObjectsOfType(objects, ramses::ERamsesObjectType::DataObject); + for (auto & object : objects) { - if (handle == static_cast(*it)->m_impl.getDataReference()) + if (handle == static_cast(object)->impl().getDataReference()) { - return static_cast(*it); + return static_cast(object); } } } return nullptr; } - const ramses::Texture2D* SceneViewerGui::findTexture2D(ramses_internal::ResourceContentHash hash) const + const ramses::Texture2D* SceneViewerGui::findTexture2D(ramses::internal::ResourceContentHash hash) const { if (hash.isValid()) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::Texture2D); - while (const ramses::Texture2D* texture = iter.getNext()) + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::Texture2D); + while (const auto* texture = iter.getNext()) { - if (texture->m_impl.getLowlevelResourceHash() == hash) + if (texture->impl().getLowlevelResourceHash() == hash) { return texture; } @@ -393,12 +427,12 @@ namespace ramses_internal return nullptr; } - const ramses_internal::DataSlot* SceneViewerGui::findDataSlot(ramses_internal::NodeHandle handle) const + const ramses::internal::DataSlot* SceneViewerGui::findDataSlot(ramses::internal::NodeHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isNodeAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isNodeAllocated(handle); if (isAllocated) { - const auto& slots = m_scene.m_impl.getIScene().getDataSlots(); + const auto& slots = m_scene.impl().getIScene().getDataSlots(); for (auto it : slots) { if (it.second->attachedNode == handle) @@ -410,12 +444,12 @@ namespace ramses_internal return nullptr; } - const ramses_internal::DataSlot* SceneViewerGui::findDataSlot(ramses_internal::DataInstanceHandle handle) const + const ramses::internal::DataSlot* SceneViewerGui::findDataSlot(ramses::internal::DataInstanceHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isDataInstanceAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isDataInstanceAllocated(handle); if (isAllocated) { - const auto& slots = m_scene.m_impl.getIScene().getDataSlots(); + const auto& slots = m_scene.impl().getIScene().getDataSlots(); for (auto it : slots) { if (it.second->attachedDataReference == handle) @@ -427,12 +461,12 @@ namespace ramses_internal return nullptr; } - const ramses_internal::DataSlot* SceneViewerGui::findDataSlot(ramses_internal::TextureSamplerHandle handle) const + const ramses::internal::DataSlot* SceneViewerGui::findDataSlot(ramses::internal::TextureSamplerHandle handle) const { - const bool isAllocated = m_scene.m_impl.getIScene().isTextureSamplerAllocated(handle); + const bool isAllocated = m_scene.impl().getIScene().isTextureSamplerAllocated(handle); if (isAllocated) { - const auto& slots = m_scene.m_impl.getIScene().getDataSlots(); + const auto& slots = m_scene.impl().getIScene().getDataSlots(); for (auto it : slots) { if (it.second->attachedTextureSampler == handle) @@ -444,11 +478,11 @@ namespace ramses_internal return nullptr; } - const ramses_internal::DataSlot* SceneViewerGui::findDataSlot(ramses_internal::ResourceContentHash hash) const + const ramses::internal::DataSlot* SceneViewerGui::findDataSlot(ramses::internal::ResourceContentHash hash) const { if (hash.isValid()) { - const auto& slots = m_scene.m_impl.getIScene().getDataSlots(); + const auto& slots = m_scene.impl().getIScene().getDataSlots(); for (auto it : slots) { if (it.second->attachedTexture == hash) @@ -461,19 +495,19 @@ namespace ramses_internal } template - void SceneViewerGui::drawRefs(const char* headline, const ramses::RamsesObjectImpl& target, Filter filter) + void SceneViewerGui::drawRefs(const char* headline, const SceneObjectImpl& target, Filter filter) { const RefKey key = {&target, headline}; - auto result = m_refs.insert({key, ramses::RamsesObjectVector()}); + auto result = m_refs.insert({ key, SceneObjectVector{} }); - ramses::RamsesObjectVector& filteredList = result.first->second; + SceneObjectVector& filteredList = result.first->second; if (result.second) { // fill the list initially const auto type = ramses::TYPE_ID_OF_RAMSES_OBJECT::ID; - ramses::RamsesObjectVector objects; - m_scene.m_impl.getObjectRegistry().getObjectsOfType(objects, type); - for (ramses::RamsesObject* obj : objects) + SceneObjectVector objects; + m_scene.impl().getObjectRegistry().getObjectsOfType(objects, type); + for (auto* obj : objects) { const T* tObj = static_cast(obj); if (filter(tObj)) @@ -485,86 +519,86 @@ namespace ramses_internal { if (ImGui::TreeNode(headline, "%s (%zu):", headline, filteredList.size())) { - for (ramses::RamsesObject* obj : filteredList) + for (auto* obj : filteredList) { - draw(obj->m_impl); + draw(obj->impl()); } ImGui::TreePop(); } } } - void SceneViewerGui::draw(ramses::RamsesObjectImpl& obj) + void SceneViewerGui::draw(SceneObjectImpl& obj) { if (drawRamsesObject(obj)) { switch (obj.getType()) { case ramses::ERamsesObjectType::Node: - drawNode(static_cast(obj)); + drawNode(static_cast(obj)); break; case ramses::ERamsesObjectType::PickableObject: - drawPickableObject(static_cast (obj)); + drawPickableObject(static_cast (obj)); break; case ramses::ERamsesObjectType::MeshNode: - drawMeshNode(static_cast(obj)); + drawMeshNode(static_cast(obj)); break; case ramses::ERamsesObjectType::PerspectiveCamera: case ramses::ERamsesObjectType::OrthographicCamera: - drawCameraNode(static_cast(obj)); + drawCameraNode(static_cast(obj)); break; case ramses::ERamsesObjectType::Effect: - drawEffect(static_cast(obj)); + drawEffect(static_cast(obj)); break; case ramses::ERamsesObjectType::RenderPass: - drawRenderPass(static_cast(obj)); + drawRenderPass(static_cast(obj)); break; case ramses::ERamsesObjectType::RenderGroup: - drawRenderGroup(static_cast(obj)); + drawRenderGroup(static_cast(obj)); break; case ramses::ERamsesObjectType::Appearance: drawAppearance(static_cast(obj.getRamsesObject())); break; - case ramses::ERamsesObjectType::GeometryBinding: - drawGeometryBinding(static_cast(obj)); + case ramses::ERamsesObjectType::Geometry: + drawGeometry(static_cast(obj)); break; case ramses::ERamsesObjectType::Texture2D: - drawTexture2D(static_cast(obj)); + drawTexture2D(static_cast(obj)); break; case ramses::ERamsesObjectType::Texture3D: - drawTexture3D(static_cast(obj)); + drawTexture3D(static_cast(obj)); break; case ramses::ERamsesObjectType::Texture2DBuffer: - drawTexture2DBuffer(static_cast(obj)); + drawTexture2DBuffer(static_cast(obj)); break; case ramses::ERamsesObjectType::TextureCube: - drawTextureCube(static_cast(obj)); + drawTextureCube(static_cast(obj)); break; case ramses::ERamsesObjectType::TextureSampler: case ramses::ERamsesObjectType::TextureSamplerMS: case ramses::ERamsesObjectType::TextureSamplerExternal: - drawTextureSampler(static_cast(obj)); + drawTextureSampler(static_cast(obj)); break; case ramses::ERamsesObjectType::ArrayResource: - drawArrayResource(static_cast(obj)); + drawArrayResource(static_cast(obj)); break; case ramses::ERamsesObjectType::RenderTarget: - drawRenderTarget(static_cast(obj)); + drawRenderTarget(static_cast(obj)); break; case ramses::ERamsesObjectType::RenderBuffer: - drawRenderBuffer(static_cast(obj)); + drawRenderBuffer(static_cast(obj)); break; case ramses::ERamsesObjectType::SceneReference: - drawSceneReference(static_cast(obj)); + DrawSceneReference(static_cast(obj)); break; case ramses::ERamsesObjectType::DataObject: drawDataObject(static_cast(obj.getRamsesObject())); break; - case ramses::ERamsesObjectType::ArrayBufferObject: - drawArrayBuffer(static_cast(obj)); + case ramses::ERamsesObjectType::ArrayBuffer: + drawArrayBuffer(static_cast(obj)); break; case ramses::ERamsesObjectType::BlitPass: - drawBlitPass(static_cast(obj)); + drawBlitPass(static_cast(obj)); break; case ramses::ERamsesObjectType::Invalid: case ramses::ERamsesObjectType::ClientObject: @@ -574,14 +608,15 @@ namespace ramses_internal case ramses::ERamsesObjectType::Scene: case ramses::ERamsesObjectType::Camera: case ramses::ERamsesObjectType::Resource: - case ramses::ERamsesObjectType::NUMBER_OF_TYPES: + case ramses::ERamsesObjectType::LogicEngine: + case ramses::ERamsesObjectType::LogicObject: ImGui::Text("tbd."); } ImGui::TreePop(); } } - void SceneViewerGui::drawNode(ramses::NodeImpl& obj) + void SceneViewerGui::drawNode(NodeImpl& obj) { int vis = static_cast(obj.getVisibility()); if (ImGui::RadioButton("Visible", &vis, static_cast(ramses::EVisibilityMode::Visible))) @@ -606,7 +641,7 @@ namespace ramses_internal if (ImGui::TreeNode("Transformation")) { glm::vec4 rot; - ramses_internal::ERotationType rotationType; + ERotationType rotationType; getRotation(&obj, rot, rotationType); if (rotationType == ERotationType::Quaternion) { @@ -620,7 +655,7 @@ namespace ramses_internal } int rotationConventionInt = static_cast(rotationType); if (ImGui::Combo("RotationConvention", &rotationConventionInt, ERotationTypeNames.data(), static_cast(ERotationTypeNames.size()), -1)) - setRotation(&obj, rot, static_cast(rotationConventionInt)); + setRotation(&obj, rot, static_cast(rotationConventionInt)); ramses::vec3f xyz; obj.getTranslation(xyz); if (ImGui::DragFloat3("Translation (x,y,z)", glm::value_ptr(xyz), 0.01f, 0.f, 0.f, "%.3f")) @@ -636,11 +671,11 @@ namespace ramses_internal } } - void SceneViewerGui::drawNodeChildrenParent(ramses::NodeImpl& obj) + void SceneViewerGui::drawNodeChildrenParent(NodeImpl& obj) { for (uint32_t i = 0; i < obj.getChildCount(); ++i) { - draw(obj.getChild(i)->m_impl); + draw(obj.getChild(i)->impl()); } if (obj.getParentImpl() != nullptr) { @@ -650,42 +685,44 @@ namespace ramses_internal } } - void SceneViewerGui::drawMeshNode(ramses::MeshNodeImpl& obj) + void SceneViewerGui::drawMeshNode(MeshNodeImpl& obj) { drawNode(obj); - draw(obj.getAppearance()->m_impl); - draw(obj.getGeometryBinding()->m_impl); + if (obj.getAppearance()) + draw(obj.getAppearance()->impl()); + if (obj.getGeometry()) + draw(obj.getGeometry()->impl()); drawNodeChildrenParent(obj); - drawRefs("Used by RenderGroup", obj, [&](const ramses::RenderGroup* ref) { return ref->m_impl.contains(obj); }); + drawRefs("Used by RenderGroup", obj, [&](const ramses::RenderGroup* ref) { return ref->impl().contains(obj); }); } - void SceneViewerGui::drawPickableObject(ramses::PickableObjectImpl& obj) + void SceneViewerGui::drawPickableObject(PickableObjectImpl& obj) { drawNode(obj); - draw(obj.getGeometryBuffer().m_impl); - auto camera = obj.getCamera(); + draw(const_cast(obj.getGeometryBuffer()).impl()); + auto camera = const_cast(obj.getCamera()); if (camera != nullptr) - draw(camera->m_impl); + draw(camera->impl()); drawNodeChildrenParent(obj); } - void SceneViewerGui::drawCameraNode(ramses::CameraNodeImpl& obj) + void SceneViewerGui::drawCameraNode(CameraNodeImpl& obj) { drawNode(obj); - std::array xy; - std::array wh; + std::array xy{}; + std::array wh{}; xy[0] = obj.getViewportX(); xy[1] = obj.getViewportY(); - wh[0] = obj.getViewportWidth(); - wh[1] = obj.getViewportHeight(); + wh[0] = static_cast(obj.getViewportWidth()); + wh[1] = static_cast(obj.getViewportHeight()); if (obj.isViewportOffsetBound()) { - auto dataObject = findDataObject(obj.getViewportOffsetHandle()); + auto dataObject = const_cast(findDataObject(obj.getViewportOffsetHandle())); ImGui::Text("Viewport offset:"); if (dataObject != nullptr) { ImGui::SameLine(); - draw(dataObject->m_impl); + draw(dataObject->impl()); } } else @@ -696,12 +733,12 @@ namespace ramses_internal if (obj.isViewportSizeBound()) { - auto dataObject = findDataObject(obj.getViewportSizeHandle()); + auto dataObject = const_cast(findDataObject(obj.getViewportSizeHandle())); ImGui::Text("Viewport size:"); if (dataObject != nullptr) { ImGui::SameLine(); - draw(dataObject->m_impl); + draw(dataObject->impl()); } } else @@ -713,29 +750,29 @@ namespace ramses_internal if (obj.isFrustumPlanesBound()) { ImGui::Text("Frustrum (l,r,b,t):"); - auto frustrum = findDataObject(obj.getFrustrumPlanesHandle()); + auto frustrum = const_cast(findDataObject(obj.getFrustrumPlanesHandle())); if (frustrum != nullptr) { ImGui::SameLine(); - draw(frustrum->m_impl); + draw(frustrum->impl()); } ImGui::Text("Frustrum (n,f):"); - auto nearFar = findDataObject(obj.getFrustrumNearFarPlanesHandle()); + auto nearFar = const_cast(findDataObject(obj.getFrustrumNearFarPlanesHandle())); if (nearFar != nullptr) { ImGui::SameLine(); - draw(nearFar->m_impl); + draw(nearFar->impl()); } } else { - std::array lrbp; + std::array lrbp{}; lrbp[0] = obj.getLeftPlane(); lrbp[1] = obj.getRightPlane(); lrbp[2] = obj.getBottomPlane(); lrbp[3] = obj.getTopPlane(); - std::array nf; + std::array nf{}; nf[0] = obj.getNearPlane(); nf[1] = obj.getFarPlane(); if (ImGui::DragFloat4("Frustrum (l,r,b,t)", lrbp.data(), 0.001f)) @@ -744,7 +781,7 @@ namespace ramses_internal obj.setFrustum(lrbp[0], lrbp[1], lrbp[2], lrbp[3], nf[0], nf[1]); if (obj.getType() == ramses::ERamsesObjectType::PerspectiveCamera) { - std::array va; + std::array va{}; va[0] = obj.getVerticalFieldOfView(); va[1] = obj.getAspectRatio(); if (ImGui::DragFloat2("VerticalFoV, AspectRatio", va.data(), 0.1f)) @@ -753,14 +790,14 @@ namespace ramses_internal } drawNodeChildrenParent(obj); drawRefs("Used by RenderPass", obj, [&](const ramses::RenderPass* ref) { - return ref->m_impl.getCamera() == &obj.getRamsesObject(); + return ref->impl().getCamera() == &obj.getRamsesObject(); }); } - void SceneViewerGui::drawResource(ramses::ResourceImpl& obj) + void SceneViewerGui::drawResource(ResourceImpl& obj) { const auto hash = obj.getLowlevelResourceHash(); - auto resource = m_scene.getRamsesClient().m_impl.getResource(hash); + auto resource = m_scene.getRamsesClient().impl().getResource(hash); const auto hashString = fmt::format("{}", hash); ImGui::Text("Hash: %s", hashString.c_str()); if (ImGui::BeginPopupContextItem(hashString.c_str())) @@ -801,10 +838,10 @@ namespace ramses_internal } } - void SceneViewerGui::drawEffect(ramses::EffectImpl& obj) + void SceneViewerGui::drawEffect(EffectImpl& obj) { drawResource(obj); - auto resource = m_scene.getRamsesClient().m_impl.getResource(obj.getLowlevelResourceHash()); + auto resource = m_scene.getRamsesClient().impl().getResource(obj.getLowlevelResourceHash()); if (resource) { if (ImGui::Button("Shader Sources...")) @@ -818,7 +855,7 @@ namespace ramses_internal } if (ImGui::BeginPopup("shader_src")) { - const auto effectRes = resource->convertTo(); + const auto effectRes = resource->convertTo(); if (ImGui::BeginPopupContextWindow()) { if (ImGui::MenuItem("Copy vertex shader")) @@ -866,27 +903,27 @@ namespace ramses_internal ImGui::BulletText(format, shortName(a.dataType), a.inputName.c_str(), a.elementCount, EFixedSemanticsNames[static_cast(a.semantics)]); } - drawRefs("Used by Appearance", obj, [&](const ramses::Appearance* ref) { return ref->m_impl.getEffectImpl()== &obj; }); - drawRefs("Used by GeometryBinding", obj, [&](const ramses::GeometryBinding* ref) { return &ref->m_impl.getEffect().m_impl == &obj; }); + drawRefs("Used by Appearance", obj, [&](const ramses::Appearance* ref) { return ref->impl().getEffectImpl()== &obj; }); + drawRefs("Used by Geometry", obj, [&](const ramses::Geometry* ref) { return &ref->impl().getEffect().impl() == &obj; }); } - void SceneViewerGui::drawRenderPass(ramses::RenderPassImpl& obj) + void SceneViewerGui::drawRenderPass(RenderPassImpl& obj) { int32_t renderOrder = obj.getRenderOrder(); if (ImGui::DragInt("RenderOrder", &renderOrder)) obj.setRenderOrder(renderOrder); - uint32_t clearFlags = obj.getClearFlags(); + auto clearFlags = obj.getClearFlags().value(); if (ImGui::TreeNode("Clear")) { - if (ImGui::CheckboxFlags("Color", &clearFlags, ramses::EClearFlags_Color)) - obj.setClearFlags(clearFlags); - if (ImGui::CheckboxFlags("Depth", &clearFlags, ramses::EClearFlags_Depth)) - obj.setClearFlags(clearFlags); - if (ImGui::CheckboxFlags("Stencil", &clearFlags, ramses::EClearFlags_Stencil)) - obj.setClearFlags(clearFlags); - if (clearFlags & ramses::EClearFlags_Color) + if (ImGui::CheckboxFlags("Color", &clearFlags, static_cast(ramses::EClearFlag::Color))) + obj.setClearFlags(ClearFlags(clearFlags)); + if (ImGui::CheckboxFlags("Depth", &clearFlags, static_cast(ramses::EClearFlag::Depth))) + obj.setClearFlags(ClearFlags(clearFlags)); + if (ImGui::CheckboxFlags("Stencil", &clearFlags, static_cast(ramses::EClearFlag::Stencil))) + obj.setClearFlags(EClearFlag(clearFlags)); + if ((clearFlags & static_cast(ramses::EClearFlag::Color)) != 0u) { - std::array rgba; + std::array rgba{}; const auto& color = obj.getClearColor(); rgba[0] = color.r; rgba[1] = color.g; @@ -910,13 +947,18 @@ namespace ramses_internal obj.retriggerRenderOnce(); } - draw(obj.getCamera()->m_impl); + if (obj.getCamera()) + draw(obj.getCamera()->impl()); - auto rt = obj.getRenderTarget(); + auto rt = const_cast(obj.getRenderTarget()); if (rt != nullptr) - draw(rt->m_impl); + { + draw(rt->impl()); + } else + { ImGui::Text("No render target"); + } ImGui::Text("RenderGroups:"); auto& renderGroups = m_renderInfo.renderGroupMap[&obj]; @@ -924,8 +966,8 @@ namespace ramses_internal { renderGroups = obj.getAllRenderGroups(); std::sort(renderGroups.begin(), renderGroups.end(), [&](const auto* a, const auto* b) { - int32_t orderA; - int32_t orderB; + int32_t orderA = 0; + int32_t orderB = 0; obj.getRenderGroupOrder(*a, orderA); obj.getRenderGroupOrder(*b, orderB); return (orderA < orderB); @@ -947,11 +989,11 @@ namespace ramses_internal if (ImGui::IsItemHovered()) ImGui::SetTooltip("Render group order"); ImGui::SameLine(); - draw(*const_cast(it)); + draw(*const_cast(it)); } } - void SceneViewerGui::drawRenderGroup(ramses::RenderGroupImpl& obj) + void SceneViewerGui::drawRenderGroup(RenderGroupImpl& obj) { // sort meshes / render groups by drawing order auto& renderables = m_renderInfo.renderableMap[&obj]; @@ -980,13 +1022,13 @@ namespace ramses_internal { if (it->getType() == ramses::ERamsesObjectType::MeshNode) { - const auto* meshNode = static_cast(it); + const auto* meshNode = static_cast(it); obj.removeIfContained(*meshNode); obj.addMeshNode(*meshNode, order); } else { - const auto* renderGroup = static_cast(it); + const auto* renderGroup = static_cast(it); obj.removeIfContained(*renderGroup); obj.addRenderGroup(*renderGroup, order); } @@ -995,69 +1037,72 @@ namespace ramses_internal if (ImGui::IsItemHovered()) ImGui::SetTooltip("Render order"); ImGui::SameLine(); - draw(*const_cast(it)); + draw(const_cast(*it)); } drawRefs("Used by RenderPass", obj, [&](const ramses::RenderPass* ref) { - const auto& groups = ref->m_impl.getAllRenderGroups(); + const auto& groups = ref->impl().getAllRenderGroups(); return std::find(groups.begin(), groups.end(), &obj) != groups.end(); }); } - void SceneViewerGui::drawRenderTarget(ramses::RenderTargetImpl& obj) + void SceneViewerGui::drawRenderTarget(RenderTargetImpl& obj) { const auto rtHandle = obj.getRenderTargetHandle(); - const uint32_t numBuffers = m_scene.m_impl.getIScene().getRenderTargetRenderBufferCount(rtHandle); + const uint32_t numBuffers = m_scene.impl().getIScene().getRenderTargetRenderBufferCount(rtHandle); for (uint32_t i = 0; i < numBuffers; ++i) { - const auto rbHandle = m_scene.m_impl.getIScene().getRenderTargetRenderBuffer(rtHandle, i); - const ramses::RenderBuffer* rb = findRenderBuffer(rbHandle); + const auto rbHandle = m_scene.impl().getIScene().getRenderTargetRenderBuffer(rtHandle, i); + auto* rb = const_cast(findRenderBuffer(rbHandle)); if (rb != nullptr) - draw(rb->m_impl); + { + draw(rb->impl()); + } else + { ImGui::Text("RenderBuffer not found"); + } } drawRefs("Used by RenderPass", obj, [&](const ramses::RenderPass* ref) { - return (ref->m_impl.getRenderTarget() == &obj.getRamsesObject()); + return (ref->impl().getRenderTarget() == &obj.getRamsesObject()); }); } - void SceneViewerGui::drawRenderBuffer(ramses::RenderBufferImpl& obj) + void SceneViewerGui::drawRenderBuffer(RenderBufferImpl& obj) { - const auto& rb = m_scene.m_impl.getIScene().getRenderBuffer(obj.getRenderBufferHandle()); + const auto& rb = m_scene.impl().getIScene().getRenderBuffer(obj.getRenderBufferHandle()); ImGui::Text("Width:%u Height:%u", rb.width, rb.height); - ImGui::Text("BufferType: %s", EnumToString(rb.type)); ImGui::Text("BufferFormat: %s", EnumToString(rb.format)); ImGui::Text("AccessMode: %s", EnumToString(rb.accessMode)); ImGui::Text("SampleCount: %u", rb.sampleCount); drawRefs("Used by RenderTarget", obj, [&](const ramses::RenderTarget* ref) { - const auto rtHandle = ref->m_impl.getRenderTargetHandle(); - const uint32_t numBuffers = m_scene.m_impl.getIScene().getRenderTargetRenderBufferCount(rtHandle); + const auto rtHandle = ref->impl().getRenderTargetHandle(); + const uint32_t numBuffers = m_scene.impl().getIScene().getRenderTargetRenderBufferCount(rtHandle); for (uint32_t i = 0; i < numBuffers; ++i) { - const auto rbHandle = m_scene.m_impl.getIScene().getRenderTargetRenderBuffer(rtHandle, i); + const auto rbHandle = m_scene.impl().getIScene().getRenderTargetRenderBuffer(rtHandle, i); if (rbHandle == obj.getRenderBufferHandle()) return true; } return false; }); drawRefs("Used by TextureSampler", obj, [&](const ramses::TextureSampler* ref) { - const ramses_internal::TextureSampler& sampler = obj.getIScene().getTextureSampler(ref->m_impl.getTextureSamplerHandle()); + const ramses::internal::TextureSampler& sampler = obj.getIScene().getTextureSampler(ref->impl().getTextureSamplerHandle()); return (sampler.isRenderBuffer()) && (sampler.contentHandle == obj.getRenderBufferHandle().asMemoryHandle()); }); drawRefs("Used by TextureSamplerMS", obj, [&](const ramses::TextureSamplerMS* ref) { - const ramses_internal::TextureSampler& sampler = obj.getIScene().getTextureSampler(ref->m_impl.getTextureSamplerHandle()); + const ramses::internal::TextureSampler& sampler = obj.getIScene().getTextureSampler(ref->impl().getTextureSamplerHandle()); return (sampler.isRenderBuffer()) && (sampler.contentHandle == obj.getRenderBufferHandle().asMemoryHandle()); }); drawRefs("Used by BlitPass", obj, [&](const ramses::BlitPass* ref) { - const auto& bp = m_scene.m_impl.getIScene().getBlitPass(ref->m_impl.getBlitPassHandle()); + const auto& bp = m_scene.impl().getIScene().getBlitPass(ref->impl().getBlitPassHandle()); return (bp.sourceRenderBuffer == obj.getRenderBufferHandle()) || (bp.destinationRenderBuffer == obj.getRenderBufferHandle()); }); } - void SceneViewerGui::drawBlitPass(ramses::BlitPassImpl& obj) + void SceneViewerGui::drawBlitPass(BlitPassImpl& obj) { - const auto& bp = m_scene.m_impl.getIScene().getBlitPass(obj.getBlitPassHandle()); + const auto& bp = m_scene.impl().getIScene().getBlitPass(obj.getBlitPassHandle()); std::array src = {static_cast(bp.sourceRegion.x), static_cast(bp.sourceRegion.y), bp.sourceRegion.width, bp.sourceRegion.height}; std::array dst = {static_cast(bp.destinationRegion.x), static_cast(bp.destinationRegion.y)}; bool isEnabled = bp.isEnabled; @@ -1072,17 +1117,17 @@ namespace ramses_internal obj.setBlittingRegion(src[0], src[1], dst[0], dst[1], src[2], src[3]); ImGui::Text("src:"); ImGui::SameLine(); - draw(obj.getSourceRenderBuffer().m_impl); + draw(const_cast(obj.getSourceRenderBuffer()).impl()); ImGui::Text("dst:"); ImGui::SameLine(); - draw(obj.getDestinationRenderBuffer().m_impl); + draw(const_cast(obj.getDestinationRenderBuffer()).impl()); } void SceneViewerGui::drawAppearance(ramses::Appearance& appearance) { - auto& obj = appearance.m_impl; + auto& obj = appearance.impl(); const ramses::Effect& effect = obj.getEffect(); - ramses_internal::ClientScene& iscene = m_scene.m_impl.getIScene(); + ramses::internal::ClientScene& iscene = m_scene.impl().getIScene(); imgui::RenderState(iscene, obj.getRenderStateHandle()); @@ -1090,36 +1135,39 @@ namespace ramses_internal { for (uint32_t i = 0; i < effect.getUniformInputCount(); ++i) { - ramses::UniformInput uniform; - effect.getUniformInput(i, uniform); - assert(uniform.isValid()); - const ramses::DataObject* boundObj = obj.getBoundDataObject(uniform.m_impl); - if (uniform.getSemantics() != ramses::EEffectUniformSemantic::Invalid) + const std::optional uniform = effect.getUniformInput(i); + assert(uniform.has_value()); + auto* boundObj = const_cast(obj.getBoundDataObject(uniform->impl())); + if (uniform->getSemantics() != ramses::EEffectUniformSemantic::Invalid) { - ImGui::BulletText("%s %s[%lu] (%s)", shortName(uniform.m_impl.get().getInternalDataType()), uniform.getName(), uniform.getElementCount(), EFixedSemanticsNames[static_cast(uniform.m_impl.get().getSemantics())]); + ImGui::BulletText("%s %s[%lu] (%s)", shortName(uniform->impl().getInternalDataType()), uniform->getName(), uniform->getElementCount(), EFixedSemanticsNames[static_cast(uniform->impl().getSemantics())]); } else { ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode(uniform.getName(), "%s %s[%lu]:", shortName(uniform.m_impl.get().getInternalDataType()), uniform.getName(), uniform.getElementCount())) + if (ImGui::TreeNode(uniform->getName(), "%s %s[%lu]:", shortName(uniform->impl().getInternalDataType()), uniform->getName(), uniform->getElementCount())) { if (boundObj != nullptr) - draw(boundObj->m_impl); + { + draw(boundObj->impl()); + } else - drawUniformValue(appearance, uniform); + { + drawUniformValue(appearance, *uniform); + } ImGui::TreePop(); } } } ImGui::TreePop(); } - draw(effect.m_impl); + draw(const_cast(effect).impl()); drawRefs("Used by MeshNode", obj, [&](const ramses::MeshNode* ref) { - return ref->m_impl.getAppearanceImpl() == &obj; + return ref->impl().getAppearanceImpl() == &obj; }); } - void SceneViewerGui::drawUniformValue(ramses::Appearance& appearance, ramses::UniformInput& uniform) + void SceneViewerGui::drawUniformValue(ramses::Appearance& appearance, const ramses::UniformInput& uniform) { std::vector vec4i; std::vector vec3i; @@ -1129,17 +1177,19 @@ namespace ramses_internal std::vector vec3; std::vector vec2; std::vector vec1; - const ramses::TextureSampler* textureSampler; - const ramses::TextureSamplerMS* textureSamplerMS; - const ramses::TextureSamplerExternal* textureSamplerExternal; - ramses::status_t status = ramses::StatusOK; + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + std::unique_ptr uniformBool; + const ramses::TextureSampler* textureSampler = nullptr; + const ramses::TextureSamplerMS* textureSamplerMS = nullptr; + const ramses::TextureSamplerExternal* textureSamplerExternal = nullptr; + bool status = false; - switch (*uniform.getDataType()) + switch (uniform.getDataType()) { case ramses::EDataType::Float: vec1.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec1.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec1.begin(); it != vec1.end(); ++it) { @@ -1152,7 +1202,7 @@ namespace ramses_internal case ramses::EDataType::Vector2F: vec2.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec2.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec2.begin(); it != vec2.end(); ++it) { @@ -1165,7 +1215,7 @@ namespace ramses_internal case ramses::EDataType::Vector3F: vec3.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec3.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec3.begin(); it != vec3.end(); ++it) { @@ -1178,7 +1228,7 @@ namespace ramses_internal case ramses::EDataType::Vector4F: vec4.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec4.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec4.begin(); it != vec4.end(); ++it) { @@ -1188,10 +1238,23 @@ namespace ramses_internal } } break; + case ramses::EDataType::Bool: + uniformBool.reset(new bool[uniform.getElementCount()]); + status = appearance.getInputValue(uniform, uniform.getElementCount(), uniformBool.get()); + if (status) + { + for (size_t i = 0u; i < uniform.getElementCount(); ++i) + { + const std::string label = fmt::format("{}[{}]", uniform.getName(), i); + if (ImGui::Checkbox(label.c_str(), &uniformBool[i])) + appearance.setInputValue(uniform, uniform.getElementCount(), uniformBool.get()); + } + } + break; case ramses::EDataType::Int32: vec1i.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec1i.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec1i.begin(); it != vec1i.end(); ++it) { @@ -1204,7 +1267,7 @@ namespace ramses_internal case ramses::EDataType::Vector2I: vec2i.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec2i.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec2i.begin(); it != vec2i.end(); ++it) { @@ -1217,7 +1280,7 @@ namespace ramses_internal case ramses::EDataType::Vector3I: vec3i.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec3i.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec3i.begin(); it != vec3i.end(); ++it) { @@ -1230,7 +1293,7 @@ namespace ramses_internal case ramses::EDataType::Vector4I: vec4i.resize(uniform.getElementCount()); status = appearance.getInputValue(uniform, uniform.getElementCount(), vec4i.data()); - if (ramses::StatusOK == status) + if (status) { for (auto it = vec4i.begin(); it != vec4i.end(); ++it) { @@ -1244,23 +1307,23 @@ namespace ramses_internal case ramses::EDataType::TextureSampler3D: case ramses::EDataType::TextureSamplerCube: status = appearance.getInputTexture(uniform, textureSampler); - if (ramses::StatusOK == status) + if (status) { - draw(textureSampler->m_impl); + draw(const_cast(textureSampler)->impl()); } break; case ramses::EDataType::TextureSampler2DMS: status = appearance.getInputTextureMS(uniform, textureSamplerMS); - if (ramses::StatusOK == status) + if (status) { - draw(textureSamplerMS->m_impl); + draw(const_cast(textureSamplerMS)->impl()); } break; case ramses::EDataType::TextureSamplerExternal: status = appearance.getInputTextureExternal(uniform, textureSamplerExternal); - if (ramses::StatusOK == status) + if (status) { - draw(textureSamplerExternal->m_impl); + draw(const_cast(textureSamplerExternal)->impl()); } break; case ramses::EDataType::UInt16: @@ -1269,21 +1332,21 @@ namespace ramses_internal case ramses::EDataType::Matrix33F: case ramses::EDataType::Matrix44F: case ramses::EDataType::ByteBlob: - ImGui::Text("tbd. %s", EnumToString(uniform.m_impl.get().getInternalDataType())); + ImGui::Text("tbd. %s", EnumToString(uniform.impl().getInternalDataType())); break; } - if (status != ramses::StatusOK) - ImGui::Text("Error occurred: %s", appearance.getStatusMessage(status)); + if (!status) + ImGui::Text("Error occurred: %s", m_scene.getRamsesClient().getRamsesFramework().getLastError().value_or(Issue{}).message.c_str()); } - void SceneViewerGui::drawGeometryBinding(ramses::GeometryBindingImpl& obj) + void SceneViewerGui::drawGeometry(GeometryImpl& obj) { const ramses::Effect& effect = obj.getEffect(); if (ImGui::TreeNode("Attribute input")) { - auto& iScene = m_scene.m_impl.getIScene(); - const ramses_internal::DataLayout& layout = iScene.getDataLayout(obj.getAttributeDataLayout()); + auto& iScene = m_scene.impl().getIScene(); + const ramses::internal::DataLayout& layout = iScene.getDataLayout(obj.getAttributeDataLayout()); const uint32_t dataLayoutFieldCount = layout.getFieldCount(); for (uint32_t i = 0U; i < dataLayoutFieldCount; ++i) { @@ -1293,54 +1356,56 @@ namespace ramses_internal } else { - ramses::AttributeInput attribute; - effect.getAttributeInput(i - 1, attribute); - ImGui::Text("%s:", attribute.getName()); + ImGui::Text("%s:", effect.getAttributeInput(i - 1)->getName()); } - const ramses_internal::DataFieldHandle fieldIndex(i); - const ramses_internal::ResourceField& dataResource = iScene.getDataResource(obj.getAttributeDataInstance(), fieldIndex); + const ramses::internal::DataFieldHandle fieldIndex(i); + const ramses::internal::ResourceField& dataResource = iScene.getDataResource(obj.getAttributeDataInstance(), fieldIndex); if (dataResource.dataBuffer.isValid()) { - auto buf = findArrayBuffer(dataResource.dataBuffer); + auto buf = const_cast(findArrayBuffer(dataResource.dataBuffer)); assert(buf != nullptr); - draw(buf->m_impl); + draw(buf->impl()); } else if (dataResource.hash.isValid()) { - const ramses::Resource* resource = m_scene.m_impl.scanForResourceWithHash(dataResource.hash); + ramses::Resource* resource = m_scene.impl().scanForResourceWithHash(dataResource.hash); if (resource != nullptr) - draw(resource->m_impl); + { + draw(resource->impl()); + } else + { ImGui::Text("Resource missing"); + } } } ImGui::TreePop(); } - draw(effect.m_impl); + draw(const_cast(effect).impl()); drawRefs("Used by MeshNode", obj, [&](const ramses::MeshNode* ref) { - return ref->m_impl.getGeometryBindingImpl() == &obj; + return ref->impl().getGeometryImpl() == &obj; }); } - void SceneViewerGui::drawTexture2D(ramses::Texture2DImpl& obj) + void SceneViewerGui::drawTexture2D(Texture2DImpl& obj) { drawResource(obj); ImGui::Text("Width:%u Height:%u Format:%s", obj.getWidth(), obj.getHeight(), ramses::toString(obj.getTextureFormat())); const auto& swizzle = obj.getTextureSwizzle(); ImGui::Text("Swizzle: r:%s g:%s b:%s a:%s", - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelRed)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelGreen)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelBlue)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelAlpha))); + ramses::EnumToString(swizzle.channelRed), + ramses::EnumToString(swizzle.channelGreen), + ramses::EnumToString(swizzle.channelBlue), + ramses::EnumToString(swizzle.channelAlpha)); const auto* slot = findDataSlot(obj.getLowlevelResourceHash()); if (slot) ImGui::Text("DataSlot: %u %s", slot->id.getValue(), EnumToString(slot->type)); - auto resource = m_scene.getRamsesClient().m_impl.getResource(obj.getLowlevelResourceHash()); + auto resource = m_scene.getRamsesClient().impl().getResource(obj.getLowlevelResourceHash()); if (resource) { - const ramses_internal::TextureResource* textureResource = resource->convertTo(); + const auto* textureResource = resource->convertTo(); if (textureResource != nullptr) { ImGui::TextUnformatted(fmt::format("GenerateMipChain: {}", textureResource->getGenerateMipChainFlag()).c_str()); @@ -1354,26 +1419,26 @@ namespace ramses_internal } drawRefs("Used by TextureSampler", obj, [&](const ramses::TextureSampler* ref) { - const auto& sampler = obj.getIScene().getTextureSampler(ref->m_impl.getTextureSamplerHandle()); + const auto& sampler = obj.getIScene().getTextureSampler(ref->impl().getTextureSamplerHandle()); return (sampler.contentType == TextureSampler::ContentType::ClientTexture) && (sampler.textureResource == obj.getLowlevelResourceHash()); }); } - void SceneViewerGui::drawTexture3D(ramses::Texture3DImpl& obj) + void SceneViewerGui::drawTexture3D(Texture3DImpl& obj) { drawResource(obj); ImGui::Text("Width:%u Height:%u Format:%s", obj.getWidth(), obj.getHeight(), ramses::toString(obj.getTextureFormat())); ImGui::Text("Depth:%u", obj.getDepth()); drawRefs("Used by TextureSampler", obj, [&](const ramses::TextureSampler* ref) { - const auto& sampler = obj.getIScene().getTextureSampler(ref->m_impl.getTextureSamplerHandle()); + const auto& sampler = obj.getIScene().getTextureSampler(ref->impl().getTextureSamplerHandle()); return (sampler.contentType == TextureSampler::ContentType::ClientTexture) && (sampler.textureResource == obj.getLowlevelResourceHash()); }); } - void SceneViewerGui::drawTexture2DBuffer(ramses::Texture2DBufferImpl& obj) + void SceneViewerGui::drawTexture2DBuffer(Texture2DBufferImpl& obj) { - const auto& tb = m_scene.m_impl.getIScene().getTextureBuffer(obj.getTextureBufferHandle()); + const auto& tb = m_scene.impl().getIScene().getTextureBuffer(obj.getTextureBufferHandle()); ImGui::Text("Format:%s", EnumToString(tb.textureFormat)); for (auto it = tb.mipMaps.begin(); it != tb.mipMaps.end(); ++it) { @@ -1381,31 +1446,37 @@ namespace ramses_internal ImGui::BulletText("width:%u height:%u", it->width, it->height); ImGui::BulletText("area: x:%d y:%d w:%d h:%d", it->usedRegion.x, it->usedRegion.y, it->usedRegion.width, it->usedRegion.height); ImGui::BulletText("size (kB): %" PRIu32, static_cast(it->data.size() / 1024)); + if (ImGui::Button("Save png")) + { + const auto filename = fmt::format("{:04}_Texture2DBuffer.png", obj.getObjectRegistryHandle().asMemoryHandle()); + m_lastErrorMessage = imgui::SaveToPng(reinterpret_cast(it->data.data()), it->data.size(), tb.textureFormat, it->width, it->height, filename); + } + imgui::PreviewImage(m_imageCache.get(*it, tb.textureFormat), ImVec2(128, 128)); } drawRefs("Used by TextureSampler", obj, [&](const ramses::TextureSampler* ref) { - const auto& sampler = obj.getIScene().getTextureSampler(ref->m_impl.getTextureSamplerHandle()); + const auto& sampler = obj.getIScene().getTextureSampler(ref->impl().getTextureSamplerHandle()); return (sampler.contentType == TextureSampler::ContentType::TextureBuffer) && (sampler.contentHandle == obj.getTextureBufferHandle()); }); } - void SceneViewerGui::drawTextureCube(ramses::TextureCubeImpl& obj) + void SceneViewerGui::drawTextureCube(TextureCubeImpl& obj) { drawResource(obj); ImGui::Text("Size:%u Format:%s", obj.getSize(), ramses::toString(obj.getTextureFormat())); const auto& swizzle = obj.getTextureSwizzle(); ImGui::Text("Swizzle: r:%s g:%s b:%s a:%s", - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelRed)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelGreen)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelBlue)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelAlpha))); + ramses::EnumToString(swizzle.channelRed), + ramses::EnumToString(swizzle.channelGreen), + ramses::EnumToString(swizzle.channelBlue), + ramses::EnumToString(swizzle.channelAlpha)); drawRefs("Used by TextureSampler", obj, [&](const ramses::TextureSampler* ref) { - const auto& sampler = obj.getIScene().getTextureSampler(ref->m_impl.getTextureSamplerHandle()); + const auto& sampler = obj.getIScene().getTextureSampler(ref->impl().getTextureSamplerHandle()); return (sampler.contentType == TextureSampler::ContentType::ClientTexture) && (sampler.textureResource == obj.getLowlevelResourceHash()); }); } - void SceneViewerGui::drawTextureSampler(ramses::TextureSamplerImpl& obj) + void SceneViewerGui::drawTextureSampler(TextureSamplerImpl& obj) { const auto* slot = findDataSlot(obj.getTextureSamplerHandle()); if (slot) @@ -1420,32 +1491,44 @@ namespace ramses_internal ImGui::Text("AnisotropyLevel: %u", obj.getAnisotropyLevel()); ramses::Resource* resource = nullptr; - const ramses_internal::TextureSampler& sampler = obj.getIScene().getTextureSampler(obj.getTextureSamplerHandle()); - const ramses::RenderBuffer* rb = nullptr; - const ramses::Texture2DBuffer* textureBuffer = nullptr; + const ramses::internal::TextureSampler& sampler = obj.getIScene().getTextureSampler(obj.getTextureSamplerHandle()); + ramses::RenderBuffer* rb = nullptr; + ramses::Texture2DBuffer* textureBuffer = nullptr; switch (sampler.contentType) { case TextureSampler::ContentType::ClientTexture: resource = obj.getSceneImpl().scanForResourceWithHash(sampler.textureResource); if (resource != nullptr) - draw(resource->m_impl); + { + draw(resource->impl()); + } else + { ImGui::Text("Resource missing"); + } break; case TextureSampler::ContentType::RenderBuffer: case TextureSampler::ContentType::RenderBufferMS: - rb = findRenderBuffer(RenderBufferHandle(sampler.contentHandle)); + rb = const_cast(findRenderBuffer(RenderBufferHandle(sampler.contentHandle))); if (rb != nullptr) - draw(rb->m_impl); + { + draw(rb->impl()); + } else + { ImGui::Text("RenderBuffer missing"); + } break; case TextureSampler::ContentType::TextureBuffer: - textureBuffer = findTextureBuffer(TextureBufferHandle(sampler.contentHandle)); + textureBuffer = const_cast(findTextureBuffer(TextureBufferHandle(sampler.contentHandle))); if (textureBuffer != nullptr) - draw(textureBuffer->m_impl); + { + draw(textureBuffer->impl()); + } else + { ImGui::Text("TextureBuffer missing"); + } break; case TextureSampler::ContentType::ExternalTexture: // no details @@ -1453,38 +1536,44 @@ namespace ramses_internal case TextureSampler::ContentType::OffscreenBuffer: case TextureSampler::ContentType::StreamBuffer: case TextureSampler::ContentType::None: - ImGui::Text("Type: %s (tbd.)", ramses::RamsesObjectTypeUtils::GetRamsesObjectTypeName(obj.getTextureType())); + ImGui::Text("Type: %s (tbd.)", RamsesObjectTypeUtils::GetRamsesObjectTypeName(obj.getTextureType())); break; } drawRefs("Used by Appearance", obj, [&](const ramses::Appearance* ref) { const ramses::Effect& effect = ref->getEffect(); + auto* ref_nonconst = const_cast(ref); for (uint32_t i = 0; i < effect.getUniformInputCount(); ++i) { - ramses::UniformInput uniform; - effect.getUniformInput(i, uniform); - assert(uniform.isValid()); - const ramses::TextureSampler* textureSampler; - const ramses::TextureSamplerMS* textureSamplerMS; - const ramses::TextureSamplerExternal* textureSamplerExternal; - switch (*uniform.getDataType()) + std::optional uniform = effect.getUniformInput(i); + assert(uniform.has_value()); + const ramses::TextureSampler* textureSampler = nullptr; + const ramses::TextureSamplerMS* textureSamplerMS = nullptr; + const ramses::TextureSamplerExternal* textureSamplerExternal = nullptr; + switch (uniform->getDataType()) { case ramses::EDataType::TextureSampler2D: case ramses::EDataType::TextureSampler3D: case ramses::EDataType::TextureSamplerCube: - if (ref->m_impl.getInputTexture(uniform.m_impl, textureSampler) == ramses::StatusOK) + if (ref_nonconst->impl().getInputTexture(uniform->impl(), textureSampler)) + { if (textureSampler == &obj.getRamsesObject()) return true; + } break; case ramses::EDataType::TextureSampler2DMS: - if (ref->m_impl.getInputTextureMS(uniform.m_impl, textureSamplerMS) == ramses::StatusOK) + if (ref_nonconst->impl().getInputTextureMS(uniform->impl(), textureSamplerMS)) + { if (textureSamplerMS == &obj.getRamsesObject()) return true; + } break; case ramses::EDataType::TextureSamplerExternal: - if (ref->m_impl.getInputTextureExternal(uniform.m_impl, textureSamplerExternal) == ramses::StatusOK) + if (ref_nonconst->impl().getInputTextureExternal(uniform->impl(), textureSamplerExternal)) + { if (textureSamplerExternal == &obj.getRamsesObject()) return true; + } break; default: break; @@ -1494,18 +1583,18 @@ namespace ramses_internal }); } - void SceneViewerGui::drawArrayResource(ramses::ArrayResourceImpl& obj) + void SceneViewerGui::drawArrayResource(ArrayResourceImpl& obj) { drawResource(obj); ImGui::Text("%s[%u]", EnumToString(obj.getElementType()), obj.getElementCount()); - drawRefs("Used by GeometryBinding", obj, [&](const ramses::GeometryBinding* ref) { - auto& iScene = m_scene.m_impl.getIScene(); - const auto& layout = iScene.getDataLayout(ref->m_impl.getAttributeDataLayout()); + drawRefs("Used by Geometry", obj, [&](const ramses::Geometry* ref) { + auto& iScene = m_scene.impl().getIScene(); + const auto& layout = iScene.getDataLayout(ref->impl().getAttributeDataLayout()); const uint32_t fieldCount = layout.getFieldCount(); for (uint32_t i = 0U; i < fieldCount; ++i) { - const ramses_internal::DataFieldHandle fieldIndex(i); - const ramses_internal::ResourceField& dataResource = iScene.getDataResource(ref->m_impl.getAttributeDataInstance(), fieldIndex); + const ramses::internal::DataFieldHandle fieldIndex(i); + const ramses::internal::ResourceField& dataResource = iScene.getDataResource(ref->impl().getAttributeDataInstance(), fieldIndex); if (dataResource.hash == obj.getLowlevelResourceHash()) return true; } @@ -1513,17 +1602,17 @@ namespace ramses_internal }); } - void SceneViewerGui::drawArrayBuffer(ramses::ArrayBufferImpl& obj) + void SceneViewerGui::drawArrayBuffer(ArrayBufferImpl& obj) { ImGui::Text("%s[%u]", EnumToString(obj.getDataType()), obj.getElementCount()); - drawRefs("Used by GeometryBinding", obj, [&](const ramses::GeometryBinding* ref) { - auto& iScene = m_scene.m_impl.getIScene(); - const auto& layout = iScene.getDataLayout(ref->m_impl.getAttributeDataLayout()); + drawRefs("Used by Geometry", obj, [&](const ramses::Geometry* ref) { + auto& iScene = m_scene.impl().getIScene(); + const auto& layout = iScene.getDataLayout(ref->impl().getAttributeDataLayout()); const uint32_t fieldCount = layout.getFieldCount(); for (uint32_t i = 0U; i < fieldCount; ++i) { - const ramses_internal::DataFieldHandle fieldIndex(i); - const ramses_internal::ResourceField& dataResource = iScene.getDataResource(ref->m_impl.getAttributeDataInstance(), fieldIndex); + const ramses::internal::DataFieldHandle fieldIndex(i); + const ramses::internal::ResourceField& dataResource = iScene.getDataResource(ref->impl().getAttributeDataInstance(), fieldIndex); if (dataResource.dataBuffer == obj.getDataBufferHandle()) return true; } @@ -1533,15 +1622,15 @@ namespace ramses_internal void SceneViewerGui::drawDataObject(ramses::DataObject& obj) { - float valueF; - int32_t valueI; + float valueF = NAN; + int32_t valueI = 0; ramses::vec2f value2F; ramses::vec3f value3F; ramses::vec4f value4F; ramses::vec2i value2I; ramses::vec3i value3I; ramses::vec4i value4I; - const auto* slot = findDataSlot(obj.m_impl.getDataReference()); + const auto* slot = findDataSlot(obj.impl().getDataReference()); if (slot) ImGui::Text("DataSlot: %u %s", slot->id.getValue(), EnumToString(slot->type)); switch (obj.getDataType()) @@ -1590,71 +1679,85 @@ namespace ramses_internal ImGui::Text("tbd. %s", EnumToString(obj.getDataType())); } - drawRefs("Used by Appearance", obj.m_impl, [&](const ramses::Appearance* ref) { + drawRefs("Used by Appearance", obj.impl(), [&](const ramses::Appearance* ref) { const ramses::Effect& effect = ref->getEffect(); for (uint32_t i = 0; i < effect.getUniformInputCount(); ++i) { - ramses::UniformInput uniform; - effect.getUniformInput(i, uniform); - const ramses::DataObject* boundObj = ref->m_impl.getBoundDataObject(uniform.m_impl); + const ramses::DataObject* boundObj = ref->impl().getBoundDataObject(effect.getUniformInput(i)->impl()); if (boundObj == &obj) return true; } return false; }); - drawRefs("Used by Camera", obj.m_impl, [&](const ramses::Camera* ref) { - auto fp = findDataObject(ref->m_impl.getFrustrumPlanesHandle()); - auto nf = findDataObject(ref->m_impl.getFrustrumNearFarPlanesHandle()); - auto pos = findDataObject(ref->m_impl.getViewportOffsetHandle()); - auto size = findDataObject(ref->m_impl.getViewportSizeHandle()); + drawRefs("Used by Camera", obj.impl(), [&](const ramses::Camera* ref) { + auto fp = findDataObject(ref->impl().getFrustrumPlanesHandle()); + auto nf = findDataObject(ref->impl().getFrustrumNearFarPlanesHandle()); + auto pos = findDataObject(ref->impl().getViewportOffsetHandle()); + auto size = findDataObject(ref->impl().getViewportSizeHandle()); const auto objPtr = &obj; return (objPtr == fp || objPtr == nf || objPtr == pos || objPtr == size); }); } - void SceneViewerGui::drawSceneReference(ramses::SceneReferenceImpl& obj) + void SceneViewerGui::DrawSceneReference(SceneReferenceImpl& obj) { ImGui::TextUnformatted(fmt::format("ReferencedScene: {}", obj.getReferencedSceneId().getValue()).c_str()); - ImGui::TextUnformatted(fmt::format("RequestedState: {}", EnumToString(ramses::SceneReferenceImpl::GetInternalSceneReferenceState(obj.getRequestedState()))).c_str()); - ImGui::TextUnformatted(fmt::format("ReportedState: {}", EnumToString(ramses::SceneReferenceImpl::GetInternalSceneReferenceState(obj.getReportedState()))).c_str()); + ImGui::TextUnformatted(fmt::format("RequestedState: {}", EnumToString(obj.getRequestedState())).c_str()); + ImGui::TextUnformatted(fmt::format("ReportedState: {}", EnumToString(obj.getReportedState())).c_str()); } - void SceneViewerGui::drawDataSlot(const ramses_internal::DataSlot& obj) + void SceneViewerGui::drawDataSlot(const ramses::internal::DataSlot& obj) { if (ImGui::TreeNode(&obj, "Slot: %3u %s", obj.id.getValue(), EnumToString(obj.type))) { if (obj.attachedTexture.isValid()) { - auto tex = findTexture2D(obj.attachedTexture); + auto tex = const_cast(findTexture2D(obj.attachedTexture)); if (tex != nullptr) - draw(tex->m_impl); + { + draw(tex->impl()); + } else + { ImGui::Text("Texture not found: %" PRIx64 ":%" PRIx64, obj.attachedTexture.highPart, obj.attachedTexture.lowPart); + } } else if (obj.attachedNode.isValid()) { - auto node = findNode(obj.attachedNode); + auto node = const_cast(findNode(obj.attachedNode)); if (node != nullptr) - draw(node->m_impl); + { + draw(node->impl()); + } else + { ImGui::Text("Node not found: %u", obj.attachedNode.asMemoryHandle()); + } } else if (obj.attachedDataReference.isValid()) { - auto ref = findDataObject(obj.attachedDataReference); + auto ref = const_cast(findDataObject(obj.attachedDataReference)); if (ref != nullptr) - draw(ref->m_impl); + { + draw(ref->impl()); + } else + { ImGui::Text("DataReference not found: %u", obj.attachedDataReference.asMemoryHandle()); + } } else if (obj.attachedTextureSampler.isValid()) { - auto ref = findTextureSampler(obj.attachedTextureSampler); + auto ref = const_cast(findTextureSampler(obj.attachedTextureSampler)); if (ref != nullptr) - draw(ref->m_impl); + { + draw(ref->impl()); + } else + { ImGui::Text("TextureSampler not found: %u", obj.attachedTextureSampler.asMemoryHandle()); + } } else { @@ -1679,13 +1782,13 @@ namespace ramses_internal if (filterModified || m_sceneObjects.empty()) { - const auto& reg = m_scene.m_impl.getObjectRegistry(); - for (uint32_t i = 0u; i < static_cast(ramses::ERamsesObjectType::NUMBER_OF_TYPES); ++i) + const auto& reg = m_scene.impl().getObjectRegistry(); + for (uint32_t i = 0u; i < static_cast(ramses::RamsesObjectTypeCount); ++i) { const auto type = static_cast(i); - if (ramses::RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ramses::ERamsesObjectType::SceneObject) - && ramses::RamsesObjectTypeUtils::IsConcreteType(type)) + if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, ramses::ERamsesObjectType::SceneObject) + && RamsesObjectTypeUtils::IsConcreteType(type)) { auto& objects = m_sceneObjects[type]; const auto numberOfObjects = reg.getNumberOfObjects(type); @@ -1693,10 +1796,10 @@ namespace ramses_internal objects.clear(); if (numberOfObjects > 0u) { - ramses::RamsesObjectRegistryIterator iter(reg, ramses::ERamsesObjectType(i)); - while (const auto* obj = iter.getNext()) + SceneObjectRegistryIterator iter(reg, ramses::ERamsesObjectType(i)); + while (auto* obj = iter.getNextNonConst()) { - if (passFilter(obj->m_impl)) + if (passFilter(obj->impl())) objects.push_back(obj); } } @@ -1705,18 +1808,18 @@ namespace ramses_internal } } - bool SceneViewerGui::passFilter(const ramses::RamsesObjectImpl& obj) const + bool SceneViewerGui::passFilter(const SceneObjectImpl& obj) const { bool pass = true; - if (ramses::RamsesObjectTypeUtils::IsTypeMatchingBaseType(obj.getType(), ramses::ERamsesObjectType::Resource)) + if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(obj.getType(), ramses::ERamsesObjectType::Resource)) { - auto resource = static_cast(obj); + auto resource = static_cast(obj); const auto hashString = fmt::format("{}", resource.getLowlevelResourceHash()); return m_filter.PassFilter(hashString.c_str()) || m_filter.PassFilter(obj.getName().c_str()); } - else if (ramses::RamsesObjectTypeUtils::IsTypeMatchingBaseType(obj.getType(), ramses::ERamsesObjectType::Node)) + if (RamsesObjectTypeUtils::IsTypeMatchingBaseType(obj.getType(), ramses::ERamsesObjectType::Node)) { - auto node = static_cast(obj); + auto node = static_cast(obj); switch (node.getVisibility()) { case ramses::EVisibilityMode::Off: @@ -1740,23 +1843,23 @@ namespace ramses_internal { drawSceneObjectsFilter(); - for (const auto& it : m_sceneObjects) + for (auto& it : m_sceneObjects) { if (!it.second.empty()) { - const char* typeName = ramses::RamsesObjectTypeUtils::GetRamsesObjectTypeName(it.first); + const char* typeName = RamsesObjectTypeUtils::GetRamsesObjectTypeName(it.first); if (ImGui::TreeNode(typeName, "%s (%zu)", typeName, it.second.size())) { for (auto* obj : it.second) { - draw(obj->m_impl); + draw(obj->impl()); } ImGui::TreePop(); } } } - const auto& slots = m_scene.m_impl.getIScene().getDataSlots(); + const auto& slots = m_scene.impl().getIScene().getDataSlots(); if (slots.getTotalCount() != 0) { if (ImGui::TreeNode("DataSlots", "Data Slots (%u)", slots.getTotalCount())) @@ -1777,10 +1880,10 @@ namespace ramses_internal { ImGui::SetNextItemOpen(true, ImGuiCond_Once); ramses::SceneObjectIterator it(m_scene, ramses::ERamsesObjectType::Node); - while (const ramses::Node* node = static_cast(it.getNext())) + while (auto* node = static_cast(it.getNext())) { if (node->getParent() == nullptr) - draw(node->m_impl); + draw(node->impl()); } } } @@ -1821,7 +1924,7 @@ namespace ramses_internal ImGui::TextUnformatted(fmt::format("Resources sorted by {} (decending):", m_resourceInfo->orderCriteriaItems[m_resourceInfo->getOrderCriteriaIndex()]).c_str()); for (auto it : *m_resourceInfo) { - draw(it->m_impl); + draw(it->impl()); } } } @@ -1841,7 +1944,7 @@ namespace ramses_internal if (m_renderInfo.renderPassVector.empty()) { - const auto& reg = m_scene.m_impl.getObjectRegistry(); + const auto& reg = m_scene.impl().getObjectRegistry(); reg.getObjectsOfType(m_renderInfo.renderPassVector, ramses::ERamsesObjectType::RenderPass); std::sort(m_renderInfo.renderPassVector.begin(), m_renderInfo.renderPassVector.end(), [](const auto* a, const auto* b) { return static_cast(a)->getRenderOrder() < static_cast(b)->getRenderOrder(); @@ -1849,7 +1952,7 @@ namespace ramses_internal } for (auto pObj : m_renderInfo.renderPassVector) { - ramses::RenderPassImpl& renderPass = static_cast(pObj->m_impl); + auto& renderPass = static_cast(pObj->impl()); const ramses::RenderTarget* rt = renderPass.getRenderTarget(); const auto handle = renderPass.getObjectRegistryHandle().asMemoryHandle(); const char* name = renderPass.getName().c_str(); @@ -1868,28 +1971,43 @@ namespace ramses_internal void SceneViewerGui::drawErrors() { - if (m_hasSceneErrors) + if (m_validationReport.hasIssue()) { if (ImGui::CollapsingHeader("Objects with warnings/errors")) { - ImGui::Text("Hover mouse for details"); - if (m_objectsWithErrors.empty()) + if (ImGui::Button("Copy to clipboard")) { - const auto& reg = m_scene.m_impl.getObjectRegistry(); - ramses::RamsesObjectVector allObjects; - reg.getObjectsOfType(allObjects, ramses::ERamsesObjectType::RamsesObject); - for (auto* obj : allObjects) + ImGui::LogToClipboard(); + ImGui::LogText("IssueType, Message, ObjectType, Name, Id\n"); + for (auto& issue : m_validationReport.getIssues()) { - const char* report = obj->getValidationReport(ramses::EValidationSeverity::Warning); - if (report && report[0] != 0) + if (issue.object) + { + auto *sceneObject = issue.object->as(); + const auto id = sceneObject ? sceneObject->getSceneObjectId() : sceneObjectId_t(); + + ImGui::LogText("%s\n", fmt::format(R"("{}", "{}", "{}", "{}", {})", + EnumToString(issue.type), issue.message, shortName(issue.object->getType()), issue.object->getName(), id.getValue()).c_str()); + } + else { - m_objectsWithErrors.push_back(obj); + ImGui::LogText("%s\n", fmt::format(R"("{}", "{}")", EnumToString(issue.type), issue.message).c_str()); } + } + ImGui::LogFinish(); } - for (auto* obj : m_objectsWithErrors) + ImGui::Separator(); + const ramses::RamsesObject* lastVisited = nullptr; + for (auto& msg : m_validationReport.getIssues()) { - draw(obj->m_impl); + if (msg.type <= ramses::EIssueType::Warning) + { + // draw each object only once + if (lastVisited != msg.object && msg.object->isOfType(ERamsesObjectType::SceneObject)) + draw(const_cast(msg.object->as()->impl())); + lastVisited = msg.object; + } } } } @@ -2157,12 +2275,12 @@ namespace ramses_internal { if (ImGui::MenuItem("Copy Texture2D list (CSV)")) { - ramses::RamsesObjectRegistryIterator iter(m_scene.m_impl.getObjectRegistry(), ramses::ERamsesObjectType::Texture2D); + SceneObjectRegistryIterator iter(m_scene.impl().getObjectRegistry(), ramses::ERamsesObjectType::Texture2D); ImGui::LogToClipboard(); ImGui::LogText("Id, Name, Type, Hash, Loaded, Size, CompressedSize, Width, Height, Format, Swizzle, GenerateMipChain\n"); - while (const auto* obj = iter.getNext()) + while (auto* obj = iter.getNextNonConst()) { - logTexture2D(obj->m_impl); + logTexture2D(obj->impl()); } ImGui::LogFinish(); } @@ -2171,22 +2289,22 @@ namespace ramses_internal template void SceneViewerGui::processObjectsAsync(F&& func, ramses::ERamsesObjectType objType, const char* message) { - ramses::RamsesObjectVector objects; - m_scene.m_impl.getObjectRegistry().getObjectsOfType(objects, objType); + SceneObjectVector objects; + m_scene.impl().getObjectRegistry().getObjectsOfType(objects, objType); m_progress.stop(); ProgressMonitor::FutureList futures; const size_t tasks = objects.size() > 16u ? 4u : 1u; - const auto chunkSize = objects.size() / tasks; + const auto chunkSize = static_cast(objects.size() / tasks); for (size_t i = 0; i < tasks; ++i) { - const auto begin = objects.begin() + i * chunkSize; + const auto begin = objects.begin() + static_cast(i) * chunkSize; if (i + 1 == tasks) { - futures.push_back(std::async(std::launch::async, func, ramses::RamsesObjectVector(begin, objects.end()))); + futures.push_back(std::async(std::launch::async, func, SceneObjectVector(begin, objects.end()))); } else { - futures.push_back(std::async(std::launch::async, func, ramses::RamsesObjectVector(begin, begin + chunkSize))); + futures.push_back(std::async(std::launch::async, func, SceneObjectVector(begin, begin + chunkSize))); } } m_progress.start(std::move(futures), static_cast(objects.size()), message); @@ -2196,13 +2314,13 @@ namespace ramses_internal { if (ImGui::MenuItem("Export all 2D textures to png")) { - auto storeAllTextures = [&](ramses::RamsesObjectVector objects) { + auto storeAllTextures = [&](SceneObjectVector objects) { std::vector errorList; for (auto it = objects.begin(); it != objects.end() && !m_progress.canceled; ++it) { const ramses::Texture2D* obj = static_cast(*it); ++m_progress.current; - const auto error = saveTexture2D(obj->m_impl); + const auto error = saveTexture2D(obj->impl()); if (!error.empty()) { errorList.push_back(error); @@ -2219,13 +2337,13 @@ namespace ramses_internal { if (ImGui::MenuItem("Export all shader sources")) { - auto exportShaders = [&](ramses::RamsesObjectVector objects) { + auto exportShaders = [&](SceneObjectVector objects) { std::vector errorList; for (auto it = objects.begin(); it != objects.end() && !m_progress.canceled; ++it) { const ramses::Effect* obj = static_cast(*it); ++m_progress.current; - const auto error = saveShaderSources(obj->m_impl); + const auto error = saveShaderSources(obj->impl()); if (!error.empty()) { errorList.push_back(error); @@ -2240,29 +2358,31 @@ namespace ramses_internal void SceneViewerGui::saveSceneToFile() { - const auto status = m_scene.saveToFile(m_filename.c_str(), m_compressFile); - if (status != ramses::StatusOK) + SaveFileConfig config; + config.setCompressionEnabled(m_compressFile); + config.setValidationEnabled(false); + if (!m_scene.saveToFile(m_filename.c_str(), config)) { - m_lastErrorMessage = m_scene.getStatusMessage(status); + m_lastErrorMessage = m_scene.getRamsesClient().getRamsesFramework().getLastError().value_or(Issue{}).message; } } - std::string SceneViewerGui::saveTexture2D(const ramses::Texture2DImpl& obj) const + std::string SceneViewerGui::saveTexture2D(const Texture2DImpl& obj) const { - auto resource = m_scene.getRamsesClient().m_impl.getResource(obj.getLowlevelResourceHash()); + auto resource = m_scene.getRamsesClient().impl().getResource(obj.getLowlevelResourceHash()); std::string errorMsg; if (resource) { - const ramses_internal::TextureResource* textureResource = resource->convertTo(); + const auto* textureResource = resource->convertTo(); const auto filename = fmt::format("{:04}_{}.png", obj.getObjectRegistryHandle().asMemoryHandle(), obj.getName()); errorMsg = imgui::SaveTextureToPng(textureResource, filename); } return errorMsg; } - std::string SceneViewerGui::saveShaderSources(const ramses::EffectImpl& obj) const + std::string SceneViewerGui::saveShaderSources(const EffectImpl& obj) const { - auto resource = m_scene.getRamsesClient().m_impl.getResource(obj.getLowlevelResourceHash()); + auto resource = m_scene.getRamsesClient().impl().getResource(obj.getLowlevelResourceHash()); std::string errorMsg; if (resource) { @@ -2272,7 +2392,7 @@ namespace ramses_internal { const auto fileName = fmt::format(format, obj.getLowlevelResourceHash()); - ramses_internal::File outputFile(fileName.c_str()); + ramses::internal::File outputFile(fileName.c_str()); if (!outputFile.open(File::Mode::WriteNewBinary)) { @@ -2293,7 +2413,7 @@ namespace ramses_internal return true; }; - const auto effectRes = resource->convertTo(); + const auto effectRes = resource->convertTo(); save("{}.frag", effectRes->getFragmentShader()) && save("{}.vert", effectRes->getVertexShader()) @@ -2302,23 +2422,23 @@ namespace ramses_internal return errorMsg; } - void SceneViewerGui::setVisibility(ramses::NodeImpl& node, ramses::EVisibilityMode visibility) + void SceneViewerGui::setVisibility(NodeImpl& node, ramses::EVisibilityMode visibility) { node.setVisibility(visibility); // can't clear the resource cache immediately, because it might be currently iterated m_nodeVisibilityChanged = true; } - void SceneViewerGui::logRamsesObject(ramses::RamsesObjectImpl& obj) + void SceneViewerGui::LogRamsesObject(SceneObjectImpl& obj) { - ImGui::LogText(R"(%u,"%s","%s",)", obj.getObjectRegistryHandle().asMemoryHandle(), obj.getName().c_str(), shortName(obj.getType())); + ImGui::LogText(R"(%lu,"%s","%s",)", obj.getSceneObjectId().getValue(), obj.getName().c_str(), shortName(obj.getType())); } - void SceneViewerGui::logResource(ramses::ResourceImpl& obj) + void SceneViewerGui::logResource(ResourceImpl& obj) { - logRamsesObject(obj); + LogRamsesObject(obj); const auto hash = obj.getLowlevelResourceHash(); - auto resource = m_scene.getRamsesClient().m_impl.getResource(hash); + auto resource = m_scene.getRamsesClient().impl().getResource(hash); ImGui::LogText("%s", fmt::format("{},", hash).c_str()); if (resource) { @@ -2330,20 +2450,20 @@ namespace ramses_internal } } - void SceneViewerGui::logTexture2D(ramses::Texture2DImpl& obj) + void SceneViewerGui::logTexture2D(Texture2DImpl& obj) { logResource(obj); ImGui::LogText("%u,%u,%s,", obj.getWidth(), obj.getHeight(), ramses::toString(obj.getTextureFormat())); const auto& swizzle = obj.getTextureSwizzle(); ImGui::LogText("r:%s g:%s b:%s a:%s,", - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelRed)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelGreen)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelBlue)), - EnumToString(ramses::TextureUtils::GetTextureChannelColorInternal(swizzle.channelAlpha))); - auto resource = m_scene.getRamsesClient().m_impl.getResource(obj.getLowlevelResourceHash()); + EnumToString(swizzle.channelRed), + EnumToString(swizzle.channelGreen), + EnumToString(swizzle.channelBlue), + EnumToString(swizzle.channelAlpha)); + auto resource = m_scene.getRamsesClient().impl().getResource(obj.getLowlevelResourceHash()); if (resource) { - const ramses_internal::TextureResource* textureResource = resource->convertTo(); + const auto* textureResource = resource->convertTo(); ImGui::LogText("%s,", textureResource->getGenerateMipChainFlag() ? "true" : "false"); } else diff --git a/utils/ramses-scene-viewer/src/SceneViewerGui.h b/tools/ramses-scene-viewer/src/SceneViewerGui.h similarity index 53% rename from utils/ramses-scene-viewer/src/SceneViewerGui.h rename to tools/ramses-scene-viewer/src/SceneViewerGui.h index d892f86c9..e7e180392 100644 --- a/utils/ramses-scene-viewer/src/SceneViewerGui.h +++ b/tools/ramses-scene-viewer/src/SceneViewerGui.h @@ -6,15 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#ifndef RAMSES_SCENE_VIEWER_SCENEVIEWERGUI_H -#define RAMSES_SCENE_VIEWER_SCENEVIEWERGUI_H +#pragma once #include "ImguiClientHelper.h" #include "ImguiImageCache.h" -#include "SceneAPI/Handles.h" -#include "SceneAPI/ResourceContentHash.h" -#include "RamsesObjectVector.h" -#include "SceneDumper.h" +#include "internal/SceneGraph/SceneAPI/Handles.h" +#include "internal/SceneGraph/SceneAPI/ResourceContentHash.h" +#include "impl/RamsesObjectVector.h" +#include "impl/SceneDumper.h" #include "ProgressMonitor.h" #include "ResourceList.h" #include @@ -26,43 +25,45 @@ namespace ramses class Scene; class RenderPass; class Camera; + class RenderTarget; + class Effect; +} + +namespace ramses::internal +{ class RenderGroupImpl; class NodeImpl; class MeshNodeImpl; class CameraNodeImpl; - class RenderTarget; - class RamsesObjectImpl; - class Effect; + class SceneObjectImpl; class PickableObjectImpl; class SceneReferenceImpl; class AnimationSystemImpl; class BlitPassImpl; -} -namespace ramses_internal -{ class TextureResource; struct DataSlot; + struct MipMap; class SceneViewerGui { public: - SceneViewerGui(ramses::Scene& scene, const std::string& filename, ImguiClientHelper& imguiHelper); + SceneViewerGui(ramses::Scene& scene, const std::string& filename, ImguiClientHelper& imguiHelper, const ramses::ValidationReport& report); void draw(); void setSceneTexture(ramses::TextureSampler* sampler, uint32_t width, uint32_t height); private: - [[nodiscard]] const ramses::RenderBuffer* findRenderBuffer(ramses_internal::RenderBufferHandle handle) const; - [[nodiscard]] const ramses::Texture2DBuffer* findTextureBuffer(ramses_internal::TextureBufferHandle handle) const; - [[nodiscard]] const ramses::TextureSampler* findTextureSampler(ramses_internal::TextureSamplerHandle handle) const; - [[nodiscard]] const ramses::ArrayBuffer* findArrayBuffer(ramses_internal::DataBufferHandle handle) const; - [[nodiscard]] const ramses::Node* findNode(ramses_internal::NodeHandle handle) const; - [[nodiscard]] const ramses::DataObject* findDataObject(ramses_internal::DataInstanceHandle) const; - [[nodiscard]] const ramses::Texture2D* findTexture2D(ramses_internal::ResourceContentHash hash) const; - [[nodiscard]] const ramses_internal::DataSlot* findDataSlot(ramses_internal::NodeHandle handle) const; - [[nodiscard]] const ramses_internal::DataSlot* findDataSlot(ramses_internal::DataInstanceHandle handle) const; - [[nodiscard]] const ramses_internal::DataSlot* findDataSlot(ramses_internal::TextureSamplerHandle handle) const; - [[nodiscard]] const ramses_internal::DataSlot* findDataSlot(ramses_internal::ResourceContentHash hash) const; + [[nodiscard]] const ramses::RenderBuffer* findRenderBuffer(ramses::internal::RenderBufferHandle handle) const; + [[nodiscard]] const ramses::Texture2DBuffer* findTextureBuffer(ramses::internal::TextureBufferHandle handle) const; + [[nodiscard]] const ramses::TextureSampler* findTextureSampler(ramses::internal::TextureSamplerHandle handle) const; + [[nodiscard]] const ramses::ArrayBuffer* findArrayBuffer(ramses::internal::DataBufferHandle handle) const; + [[nodiscard]] const ramses::Node* findNode(ramses::internal::NodeHandle handle) const; + [[nodiscard]] const ramses::DataObject* findDataObject(ramses::internal::DataInstanceHandle handle) const; + [[nodiscard]] const ramses::Texture2D* findTexture2D(ramses::internal::ResourceContentHash hash) const; + [[nodiscard]] const ramses::internal::DataSlot* findDataSlot(ramses::internal::NodeHandle handle) const; + [[nodiscard]] const ramses::internal::DataSlot* findDataSlot(ramses::internal::DataInstanceHandle handle) const; + [[nodiscard]] const ramses::internal::DataSlot* findDataSlot(ramses::internal::TextureSamplerHandle handle) const; + [[nodiscard]] const ramses::internal::DataSlot* findDataSlot(ramses::internal::ResourceContentHash hash) const; void zoomIn(); void zoomOut(); @@ -83,34 +84,34 @@ namespace ramses_internal * Draws all objects of type T that own a link to the target object */ template - void drawRefs(const char* headline, const ramses::RamsesObjectImpl& target, Filter f); - - void draw(ramses::RamsesObjectImpl& obj); - void drawNode(ramses::NodeImpl& obj); - void drawPickableObject(ramses::PickableObjectImpl& obj); - void drawNodeChildrenParent(ramses::NodeImpl& obj); - void drawMeshNode(ramses::MeshNodeImpl& obj); - void drawCameraNode(ramses::CameraNodeImpl& obj); - void drawRenderPass(ramses::RenderPassImpl& obj); - void drawResource(ramses::ResourceImpl& obj); - void drawEffect(ramses::EffectImpl& obj); - void drawRenderGroup(ramses::RenderGroupImpl& obj); - void drawRenderTarget(ramses::RenderTargetImpl& obj); - void drawRenderBuffer(ramses::RenderBufferImpl& obj); - void drawBlitPass(ramses::BlitPassImpl& obj); + void drawRefs(const char* headline, const SceneObjectImpl& target, Filter f); + + void draw(SceneObjectImpl& obj); + void drawNode(NodeImpl& obj); + void drawPickableObject(PickableObjectImpl& obj); + void drawNodeChildrenParent(NodeImpl& obj); + void drawMeshNode(MeshNodeImpl& obj); + void drawCameraNode(CameraNodeImpl& obj); + void drawRenderPass(RenderPassImpl& obj); + void drawResource(ResourceImpl& obj); + void drawEffect(EffectImpl& obj); + void drawRenderGroup(RenderGroupImpl& obj); + void drawRenderTarget(RenderTargetImpl& obj); + void drawRenderBuffer(RenderBufferImpl& obj); + void drawBlitPass(BlitPassImpl& obj); void drawAppearance(ramses::Appearance& appearance); - void drawUniformValue(ramses::Appearance& appearance, ramses::UniformInput& uniform); - void drawGeometryBinding(ramses::GeometryBindingImpl& obj); - void drawTexture2D(ramses::Texture2DImpl& obj); - void drawTexture3D(ramses::Texture3DImpl& obj); - void drawTexture2DBuffer(ramses::Texture2DBufferImpl& obj); - void drawTextureCube(ramses::TextureCubeImpl& obj); - void drawTextureSampler(ramses::TextureSamplerImpl& obj); - void drawArrayResource(ramses::ArrayResourceImpl& obj); - void drawArrayBuffer(ramses::ArrayBufferImpl& obj); + void drawUniformValue(ramses::Appearance& appearance, const ramses::UniformInput& uniform); + void drawGeometry(GeometryImpl& obj); + void drawTexture2D(Texture2DImpl& obj); + void drawTexture3D(Texture3DImpl& obj); + void drawTexture2DBuffer(Texture2DBufferImpl& obj); + void drawTextureCube(TextureCubeImpl& obj); + void drawTextureSampler(TextureSamplerImpl& obj); + void drawArrayResource(ArrayResourceImpl& obj); + void drawArrayBuffer(ArrayBufferImpl& obj); void drawDataObject(ramses::DataObject& obj); - void drawSceneReference(ramses::SceneReferenceImpl& obj); - void drawDataSlot(const ramses_internal::DataSlot& obj); + static void DrawSceneReference(SceneReferenceImpl& obj); + void drawDataSlot(const ramses::internal::DataSlot& obj); void drawFile(); void drawSceneObjects(); @@ -120,31 +121,32 @@ namespace ramses_internal void drawRenderHierarchy(); void drawErrors(); - [[nodiscard]] bool passFilter(const ramses::RamsesObjectImpl& obj) const; + [[nodiscard]] bool passFilter(const SceneObjectImpl& obj) const; - bool drawRamsesObject(ramses::RamsesObjectImpl& obj); + bool drawRamsesObject(SceneObjectImpl& obj); + static void DrawIssues(const SceneObjectImpl& obj, const ramses::ValidationReport& report); template - bool drawRamsesObject(ramses::RamsesObjectImpl& obj, const C& drawTreeNode); + bool drawRamsesObject(SceneObjectImpl& obj, const C& drawTreeNode); - void drawUnusedObject(ramses::RamsesObjectImpl& obj); + void drawUnusedObject(SceneObjectImpl& obj); - void logRamsesObject(ramses::RamsesObjectImpl& obj); - void logResource(ramses::ResourceImpl& obj); - void logTexture2D(ramses::Texture2DImpl& obj); + static void LogRamsesObject(SceneObjectImpl& obj); + void logResource(ResourceImpl& obj); + void logTexture2D(Texture2DImpl& obj); void saveSceneToFile(); - [[nodiscard]] std::string saveTexture2D(const ramses::Texture2DImpl& obj) const; - [[nodiscard]] std::string saveShaderSources(const ramses::EffectImpl& obj) const; + [[nodiscard]] std::string saveTexture2D(const Texture2DImpl& obj) const; + [[nodiscard]] std::string saveShaderSources(const EffectImpl& obj) const; - void setVisibility(ramses::NodeImpl& node, ramses::EVisibilityMode visibility); + void setVisibility(NodeImpl& node, ramses::EVisibilityMode visibility); // Stores RenderPasses, RenderGroups, MeshNodes in drawing order struct RenderInfo { - ramses::RamsesObjectVector renderPassVector; - std::unordered_map> renderableMap; - std::unordered_map> renderGroupMap; + SceneObjectVector renderPassVector; + std::unordered_map> renderableMap; + std::unordered_map> renderGroupMap; }; struct NodeFilter @@ -154,12 +156,13 @@ namespace ramses_internal bool showInvisible = true; }; - using SceneObjects = std::map>; + using SceneObjects = std::map>; ImGuiTextFilter m_filter; NodeFilter m_nodeFilter; ramses::Scene& m_scene; - ramses::SceneDumper::RamsesObjectImplSet m_usedObjects; + const ramses::ValidationReport& m_validationReport; + SceneDumper::RamsesObjectImplSet m_usedObjects; const std::string m_loadedSceneFile; std::string m_filename; std::string m_lastErrorMessage; @@ -168,15 +171,14 @@ namespace ramses_internal RenderInfo m_renderInfo; bool m_compressFile = false; bool m_alwaysOverwrite = false; - bool m_hasSceneErrors = false; bool m_nodeVisibilityChanged = false; ImguiImageCache m_imageCache; struct RefKey { - const ramses::RamsesObjectImpl* target; - std::string headline; + const SceneObjectImpl* target; + std::string headline; bool operator==(const RefKey& rhs) const { @@ -194,9 +196,7 @@ namespace ramses_internal } }; - std::unordered_map m_refs; - - ramses::RamsesObjectVector m_objectsWithErrors; + std::unordered_map m_refs; struct Settings { @@ -213,5 +213,3 @@ namespace ramses_internal ImVec2 m_sceneTextureSize; }; } - -#endif diff --git a/utils/ramses-scene-viewer/src/main.cpp b/tools/ramses-scene-viewer/src/main.cpp similarity index 93% rename from utils/ramses-scene-viewer/src/main.cpp rename to tools/ramses-scene-viewer/src/main.cpp index 34c400632..0346729b7 100644 --- a/utils/ramses-scene-viewer/src/main.cpp +++ b/tools/ramses-scene-viewer/src/main.cpp @@ -12,7 +12,7 @@ int main(int argc, char* argv[]) { CLI::App cli; - ramses_internal::SceneViewer sceneViewer; + ramses::internal::SceneViewer sceneViewer; sceneViewer.registerOptions(cli); CLI11_PARSE(cli, argc, argv); return sceneViewer.run(); diff --git a/utils/ramses-stream-viewer/CMakeLists.txt b/tools/ramses-stream-viewer/CMakeLists.txt similarity index 100% rename from utils/ramses-stream-viewer/CMakeLists.txt rename to tools/ramses-stream-viewer/CMakeLists.txt diff --git a/utils/ramses-stream-viewer/src/main.cpp b/tools/ramses-stream-viewer/src/main.cpp similarity index 89% rename from utils/ramses-stream-viewer/src/main.cpp rename to tools/ramses-stream-viewer/src/main.cpp index 366b6d726..6c9439c40 100644 --- a/utils/ramses-stream-viewer/src/main.cpp +++ b/tools/ramses-stream-viewer/src/main.cpp @@ -6,14 +6,14 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" -#include "ramses-renderer-api/RamsesRenderer.h" -#include "ramses-renderer-api/IRendererEventHandler.h" -#include "ramses-renderer-api/DisplayConfig.h" -#include "ramses-renderer-api/IRendererSceneControlEventHandler.h" -#include "ramses-renderer-api/RendererSceneControl.h" +#include "ramses/renderer/RamsesRenderer.h" +#include "ramses/renderer/IRendererEventHandler.h" +#include "ramses/renderer/DisplayConfig.h" +#include "ramses/renderer/IRendererSceneControlEventHandler.h" +#include "ramses/renderer/RendererSceneControl.h" #include #include @@ -68,14 +68,14 @@ class StreamSourceViewer , m_ramsesRenderer(ramsesRenderer) , m_displayId(display) { - ramses::SceneConfig conf; - m_scene = m_ramsesClient.createScene(sceneId, conf); + const ramses::SceneConfig conf{sceneId, publicationMode}; + m_scene = m_ramsesClient.createScene(conf); auto camera = m_scene->createPerspectiveCamera("my camera"); camera->setViewport(0, 0, displayWidth, displayHeight); camera->setFrustum(19.f, static_cast(displayWidth) / static_cast(displayHeight), 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); m_renderPass = m_scene->createRenderPass("my render pass"); - m_renderPass->setClearFlags(ramses::EClearFlags_None); + m_renderPass->setClearFlags(ramses::EClearFlag::None); m_renderPass->setCamera(*camera); m_renderGroup = m_scene->createRenderGroup(); m_renderPass->addRenderGroup(*m_renderGroup); @@ -89,7 +89,7 @@ class StreamSourceViewer const std::array textureData = {1u, 1u, 1u, 1u}; const ramses::MipLevelData mipLevelData(4, textureData.data()); - m_texture = m_scene->createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ramses::ResourceCacheFlag_DoNotCache, ""); + m_texture = m_scene->createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ""); ramses::EffectDescription effectDesc; @@ -97,7 +97,7 @@ class StreamSourceViewer effectDesc.setFragmentShader(fragmentShader); if (flipY) effectDesc.addCompilerDefine("FLIP_Y"); - m_effect = m_scene->createEffect(effectDesc, ramses::ResourceCacheFlag_DoNotCache, "glsl shader"); + m_effect = m_scene->createEffect(effectDesc, "glsl shader"); m_scene->flush(); m_scene->publish(publicationMode); } @@ -117,21 +117,27 @@ class StreamSourceViewer if(sceneId != m_scene->getSceneId()) return; + assert(sceneVersionTag != ramses::InvalidSceneVersionTag); auto it = findStreamEntry(sceneVersionTag); assert(it != m_streamEntries.end()); createAndLinkStreamBuffer(*it); } + void flushScene() + { + m_scene->flush(); + } + private: struct StreamEntry { ramses::waylandIviSurfaceId_t streamSource{0u}; ramses::MeshNode* meshNode = nullptr; ramses::Appearance* appearance = nullptr; - ramses::GeometryBinding* geometryBinding = nullptr; + ramses::Geometry* geometryBinding = nullptr; ramses::TextureSampler* textureSampler = nullptr; ramses::dataConsumerId_t textureConsumerId; - ramses::sceneVersionTag_t flushVersionTag; + ramses::sceneVersionTag_t flushVersionTag = 0; ramses::streamBufferId_t streamBuffer; }; using StreamEntries=std::vector; @@ -177,20 +183,15 @@ class StreamSourceViewer streamEntry.appearance = m_scene->createAppearance(*m_effect); - streamEntry.geometryBinding = m_scene->createGeometryBinding(*m_effect); + streamEntry.geometryBinding = m_scene->createGeometry(*m_effect); streamEntry.geometryBinding->setIndices(*m_indices); - ramses::AttributeInput positionsInput; - m_effect->findAttributeInput("a_position", positionsInput); - streamEntry.geometryBinding->setInputBuffer(positionsInput, *m_vertexPositions); - - ramses::UniformInput textureInput; - m_effect->findUniformInput("textureSampler", textureInput); - streamEntry.appearance->setInputTexture(textureInput, *streamEntry.textureSampler); + streamEntry.geometryBinding->setInputBuffer(*m_effect->findAttributeInput("a_position"), *m_vertexPositions); + streamEntry.appearance->setInputTexture(*m_effect->findUniformInput("textureSampler"), *streamEntry.textureSampler); // create a mesh node to define the triangle with chosen appearance streamEntry.meshNode = m_scene->createMeshNode(); streamEntry.meshNode->setAppearance(*streamEntry.appearance); - streamEntry.meshNode->setGeometryBinding(*streamEntry.geometryBinding); + streamEntry.meshNode->setGeometry(*streamEntry.geometryBinding); m_renderGroup->addMeshNode(*streamEntry.meshNode); @@ -338,10 +339,10 @@ int main(int argc, char* argv[]) renderer->flush(); const ramses::sceneId_t sceneId{1u}; - int32_t x; - int32_t y; - uint32_t width; - uint32_t height; + int32_t x = 0; + int32_t y = 0; + uint32_t width = 0; + uint32_t height = 0; displayConfig.getWindowRectangle(x, y, width, height); StreamSourceViewer sceneCreator(*ramsesClient, *renderer, display, sceneId, flipY, ramses::EScenePublicationMode::LocalOnly, width, height); @@ -354,6 +355,7 @@ int main(int argc, char* argv[]) RendererEventHandler rendererEventHandler; while (!rendererEventHandler.isWindowClosed()) { + sceneCreator.flushScene(); renderer->dispatchEvents(rendererEventHandler); sceneControlAPI->dispatchEvents(*eventHandler); std::this_thread::sleep_for(std::chrono::milliseconds(50)); diff --git a/client/logic/unittests/testAssetProducer/CMakeLists.txt b/tools/test-asset-producer/CMakeLists.txt similarity index 71% rename from client/logic/unittests/testAssetProducer/CMakeLists.txt rename to tools/test-asset-producer/CMakeLists.txt index b2c638e37..72a16fc93 100644 --- a/client/logic/unittests/testAssetProducer/CMakeLists.txt +++ b/tools/test-asset-producer/CMakeLists.txt @@ -7,10 +7,13 @@ # ------------------------------------------------------------------------- createModule( - NAME testAssetProducer + NAME test-asset-producer TYPE BINARY ENABLE_INSTALL OFF SRC_FILES main.cpp DEPENDENCIES ramses-shared-lib ) +add_custom_target(RL_REGEN_TEST_ASSETS + COMMAND test-asset-producer ${PROJECT_SOURCE_DIR}/tests/unittests/client/res) +set_property(TARGET RL_REGEN_TEST_ASSETS PROPERTY FOLDER "CMakePredefinedTargets") diff --git a/client/logic/unittests/testAssetProducer/main.cpp b/tools/test-asset-producer/main.cpp similarity index 80% rename from client/logic/unittests/testAssetProducer/main.cpp rename to tools/test-asset-producer/main.cpp index d80d5ffb3..8d884a6cd 100644 --- a/client/logic/unittests/testAssetProducer/main.cpp +++ b/tools/test-asset-producer/main.cpp @@ -6,23 +6,23 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. // ------------------------------------------------------------------------- -#include "ramses-logic/LogicEngine.h" -#include "ramses-logic/Property.h" -#include "ramses-logic/LuaModule.h" -#include "ramses-logic/LuaScript.h" -#include "ramses-logic/LuaInterface.h" -#include "ramses-logic/RamsesNodeBinding.h" -#include "ramses-logic/RamsesCameraBinding.h" -#include "ramses-logic/RamsesAppearanceBinding.h" -#include "ramses-logic/RamsesRenderGroupBinding.h" -#include "ramses-logic/RamsesRenderGroupBindingElements.h" -#include "ramses-logic/DataArray.h" -#include "ramses-logic/AnimationNode.h" -#include "ramses-logic/AnimationNodeConfig.h" -#include "ramses-logic/EStandardModule.h" - -#include "ramses-client.h" -#include "ramses-utils.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/Property.h" +#include "ramses/client/logic/LuaModule.h" +#include "ramses/client/logic/LuaScript.h" +#include "ramses/client/logic/LuaInterface.h" +#include "ramses/client/logic/NodeBinding.h" +#include "ramses/client/logic/CameraBinding.h" +#include "ramses/client/logic/AppearanceBinding.h" +#include "ramses/client/logic/RenderGroupBinding.h" +#include "ramses/client/logic/RenderGroupBindingElements.h" +#include "ramses/client/logic/DataArray.h" +#include "ramses/client/logic/AnimationNode.h" +#include "ramses/client/logic/AnimationNodeConfig.h" +#include "ramses/client/logic/EStandardModule.h" + +#include "ramses/client/ramses-client.h" +#include "ramses/client/ramses-utils.h" #include @@ -85,25 +85,23 @@ void createTriangle(ramses::Scene& scene) auto effect = scene.createEffect(effectDesc); auto appearance = scene.createAppearance(*effect, "triangle appearance"); - ramses::GeometryBinding* geometry = scene.createGeometryBinding(*effect, "triangle geometry"); + ramses::Geometry* geometry = scene.createGeometry(*effect, "triangle geometry"); const std::array vertexPositionsData{ ramses::vec3f{-1.f, 0.f, -1.f}, ramses::vec3f{1.f, 0.f, -1.f}, ramses::vec3f{0.f, 1.f, -1.f} }; ramses::ArrayResource* vertexPositions = scene.createArrayResource(3u, vertexPositionsData.data()); - ramses::AttributeInput positionsInput; - effect->findAttributeInput("a_position", positionsInput); - geometry->setInputBuffer(positionsInput, *vertexPositions); + geometry->setInputBuffer(*effect->findAttributeInput("a_position"), *vertexPositions); ramses::MeshNode* meshNode = scene.createMeshNode("triangle mesh node"); meshNode->setAppearance(*appearance); meshNode->setIndexCount(3); - meshNode->setGeometryBinding(*geometry); + meshNode->setGeometry(*geometry); auto* camera = scene.createPerspectiveCamera("triangle camera"); camera->setViewport(0, 0, 1280u, 480u); camera->setFrustum(19.f, 1280.f / 480.f, 0.1f, 1500.f); camera->setTranslation({0.0f, 0.0f, 5.0f}); ramses::RenderPass* renderPass = scene.createRenderPass("triangle render pass"); - renderPass->setClearFlags(ramses::EClearFlags_None); + renderPass->setClearFlags(ramses::EClearFlag::None); renderPass->setCamera(*camera); ramses::RenderGroup* renderGroup = scene.createRenderGroup(); renderGroup->addMeshNode(*meshNode); @@ -112,9 +110,9 @@ void createTriangle(ramses::Scene& scene) void createTriangleLogic(ramses::LogicEngine& logic, ramses::Scene& scene) { - auto camera = ramses::RamsesUtils::TryConvert(*scene.findObjectByName("triangle camera")); - auto camNodeBinding = logic.createRamsesNodeBinding(*camera, ramses::ERotationType::Euler_XYZ, "triangleCamNodeBinding"); - auto camBinding = logic.createRamsesCameraBinding(*camera, "triangleCamBinding"); + auto camera = scene.findObject("triangle camera"); + auto camNodeBinding = logic.createNodeBinding(*camera, ramses::ERotationType::Euler_XYZ, "triangleCamNodeBinding"); + auto camBinding = logic.createCameraBinding(*camera, "triangleCamBinding"); auto intf = logic.createLuaInterface(R"( function interface(inout) @@ -158,37 +156,35 @@ int main(int argc, char* argv[]) std::string basePath {"."}; std::string ramsesFilename = std::string("testScene_0") + std::to_string(featureLevel) + ".ramses"; - std::string logicFilename = std::string("testLogic_0") + std::to_string(featureLevel) + ".rlogic"; if (args.size() == 2u) { basePath = args[1]; } - else if (args.size() == 4u) + else if (args.size() == 3u) { basePath = args[1]; ramsesFilename = args[2]; - logicFilename = args[3]; } - if (args.size() == 3 || args.size() > 4u) + if (args.size() > 3u) { std::cerr << "Generator of ramses and ramses logic test content.\n\n" << "Synopsis:\n" - << " testAssetProducer\n" - << " testAssetProducer \n" - << " testAssetProducer \n\n"; + << " test-asset-producer\n" + << " test-asset-producer \n" + << " test-asset-producer \n\n"; return 1; } - ramses::RamsesFrameworkConfig frameworkConfig{ramses::EFeatureLevel_Latest}; + ramses::RamsesFrameworkConfig frameworkConfig{ featureLevel }; ramses::RamsesFramework ramsesFramework{frameworkConfig}; ramses::RamsesClient* ramsesClient = ramsesFramework.createClient(""); - ramses::Scene* scene = ramsesClient->createScene(ramses::sceneId_t(123u), ramses::SceneConfig(), ""); + ramses::Scene* scene = ramsesClient->createScene(ramses::sceneId_t(123u), ""); scene->flush(); - ramses::LogicEngine logicEngine{ featureLevel }; + ramses::LogicEngine& logicEngine{ *scene->createLogicEngine("testAssetLogic") }; ramses::LuaScript* script1 = logicEngine.createLuaScript(R"( function interface(IN,OUT) @@ -302,25 +298,23 @@ int main(int argc, char* argv[]) createTriangle(*scene); createTriangleLogic(logicEngine, *scene); - ramses::RamsesNodeBinding* nodeBinding = logicEngine.createRamsesNodeBinding(*node, ramses::ERotationType::Euler_XYZ, "nodebinding"); - ramses::RamsesCameraBinding* camBindingOrtho = logicEngine.createRamsesCameraBinding(*cameraOrtho, "camerabinding"); - ramses::RamsesAppearanceBinding* appBinding = logicEngine.createRamsesAppearanceBinding(*appearance, "appearancebinding"); - logicEngine.createRamsesCameraBinding(*cameraPersp, "camerabindingPersp"); - logicEngine.createRamsesRenderPassBinding(*renderPass, "renderpassbinding"); + ramses::NodeBinding* nodeBinding = logicEngine.createNodeBinding(*node, ramses::ERotationType::Euler_XYZ, "nodebinding"); + ramses::CameraBinding* camBindingOrtho = logicEngine.createCameraBinding(*cameraOrtho, "camerabinding"); + ramses::AppearanceBinding* appBinding = logicEngine.createAppearanceBinding(*appearance, "appearancebinding"); + logicEngine.createCameraBinding(*cameraPersp, "camerabindingPersp"); + logicEngine.createRenderPassBinding(*renderPass, "renderpassbinding"); logicEngine.createAnchorPoint(*nodeBinding, *camBindingOrtho, "anchorpoint"); - logicEngine.createRamsesCameraBindingWithFrustumPlanes(*cameraPersp, "camerabindingPerspWithFrustumPlanes"); + logicEngine.createCameraBindingWithFrustumPlanes(*cameraPersp, "camerabindingPerspWithFrustumPlanes"); - ramses::RamsesRenderGroupBindingElements elements; + ramses::RenderGroupBindingElements elements; elements.addElement(*meshNode, "mesh"); elements.addElement(*nestedRenderGroup, "nestedRenderGroup"); - logicEngine.createRamsesRenderGroupBinding(*renderGroup, elements, "rendergroupbinding"); + logicEngine.createRenderGroupBinding(*renderGroup, elements, "rendergroupbinding"); - ramses::UniformInput uniform; - appearance->getEffect().findUniformInput("jointMat", uniform); - logicEngine.createSkinBinding({ nodeBinding }, { ramses::matrix44f{ 0.f } }, * appBinding, uniform, "skin"); + logicEngine.createSkinBinding({ nodeBinding }, { ramses::matrix44f{ 0.f } }, * appBinding, *appearance->getEffect().findUniformInput("jointMat"), "skin"); logicEngine.createDataArray(std::vector>{ { 1.f, 2.f, 3.f, 4.f, 5.f }, { 6.f, 7.f, 8.f, 9.f, 10.f } }, "dataarrayOfArrays"); - logicEngine.createRamsesMeshNodeBinding(*meshNode, "meshnodebinding"); + logicEngine.createMeshNodeBinding(*meshNode, "meshnodebinding"); const auto dataArray = logicEngine.createDataArray(std::vector{ 1.f, 2.f }, "dataarray"); ramses::AnimationNodeConfig animConfig; @@ -347,9 +341,10 @@ int main(int argc, char* argv[]) ramses::SaveFileConfig noValidationConfig; noValidationConfig.setValidationEnabled(false); - logicEngine.saveToFile(basePath + "/" + logicFilename, noValidationConfig); + noValidationConfig.setMetadataString("test-asset-producer"); - [[maybe_unused]] auto status = scene->saveToFile(basePath + "/" + ramsesFilename, false); + if (!scene->saveToFile(basePath + "/" + ramsesFilename, noValidationConfig)) + return EXIT_FAILURE; return 0; } diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt deleted file mode 100644 index 0fbd4e382..000000000 --- a/utils/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH -# ------------------------------------------------------------------------- -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. -# ------------------------------------------------------------------------- - -add_subdirectory(ramses-imgui) -add_subdirectory(ramses-scene-viewer) -add_subdirectory(ramses-stream-viewer) From b17da5f12a302c6c5a35f0578c988f4c5085dfcd Mon Sep 17 00:00:00 2001 From: Ramses Tech User <94632088+ramses-tech-user@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:35:30 +0200 Subject: [PATCH 2/2] Oss release 28.0.0-rc2 created 2023-10-20-10-15 see CHANGELOG.md for details Original commit sha: c637fefbd3b7c1a3ab00c4b7d0272263583925f2 Co-authored-by: Askanaz Torosyan <46795157+nVxx@users.noreply.github.com> Co-authored-by: Daniel Haas <25718295+bojackHaasman@users.noreply.github.com> Co-authored-by: Mirko Sova <64351017+smirko-dev@users.noreply.github.com> Co-authored-by: Violin Yanev Co-authored-by: Carsten Rohn <710234+delyas@users.noreply.github.com> Co-authored-by: Tobias Hammer Co-authored-by: Bernhard Kisslinger <65217745+bkisslinger@users.noreply.github.com> Co-authored-by: Martin Veith <3490591+veithm@users.noreply.github.com> Co-authored-by: Jonathan Conrad <833168+jcsneaker@users.noreply.github.com> Co-authored-by: Mohamed Sharaf-El-Deen <769940+mohhsharaf@users.noreply.github.com> Co-authored-by: Markus Keppler <92277233+markuskeppler@users.noreply.github.com> Co-authored-by: Chan Tong Yan <4199832+imyumichan@users.noreply.github.com> Signed-off-by: Ramses Tech User <94632088+ramses-tech-user@users.noreply.github.com> --- CHANGELOG.md | 9 +- external/flatbuffers | 2 +- external/google-benchmark | 2 +- include/ramses/client/Scene.h | 26 +- .../code_style_checker/check_all_styles.py | 2 +- .../check_curly_braces_alone_on_line.py | 2 +- .../code_style_checker/check_enum_style.py | 2 +- .../code_style_checker/check_header_guards.py | 2 +- .../check_last_line_newline.py | 2 +- scripts/code_style_checker/check_license.py | 30 +- .../check_single_definition_on_line.py | 2 +- .../check_single_statement_on_line.py | 2 +- .../check_tabbing_and_spacing.py | 2 +- .../common_modules/common.py | 2 +- src/client/impl/RamsesClientImpl.cpp | 14 +- src/client/impl/RamsesClientImpl.h | 4 +- src/client/impl/Scene.cpp | 21 +- src/client/impl/SceneImpl.cpp | 15 +- src/client/impl/SceneImpl.h | 9 +- src/client/impl/SceneObjectRegistry.cpp | 25 +- src/client/impl/SceneObjectRegistry.h | 39 +- src/client/impl/TextureUtils.cpp | 93 ++- src/client/impl/TextureUtils.h | 21 +- src/client/impl/logic/AnchorPointImpl.h | 3 +- src/client/impl/logic/AnimationNodeImpl.h | 3 +- src/client/impl/logic/AppearanceBindingImpl.h | 3 +- src/client/impl/logic/CameraBindingImpl.h | 3 +- src/client/impl/logic/DataArrayImpl.h | 3 +- src/client/impl/logic/LogicObjectImpl.h | 3 +- src/client/impl/logic/LuaInterfaceImpl.h | 5 +- src/client/impl/logic/LuaModuleImpl.h | 3 +- src/client/impl/logic/LuaScriptImpl.h | 5 +- src/client/impl/logic/MeshNodeBindingImpl.h | 3 +- src/client/impl/logic/NodeBindingImpl.h | 3 +- src/client/impl/logic/PropertyImpl.h | 3 +- src/client/impl/logic/RamsesBindingImpl.h | 3 +- .../impl/logic/RenderGroupBindingImpl.h | 3 +- src/client/impl/logic/RenderPassBindingImpl.h | 3 +- src/client/impl/logic/SkinBindingImpl.h | 3 +- src/client/impl/logic/TimerNodeImpl.h | 3 +- src/client/impl/ramses-utils.cpp | 8 +- src/client/internal/logic/ApiObjects.h | 3 +- .../flatbuffers/generated/AnchorPointGen.h | 66 +- .../flatbuffers/generated/AnimationNodeGen.h | 191 +++--- .../flatbuffers/generated/ApiObjectsGen.h | 258 ++++---- .../generated/AppearanceBindingGen.h | 89 +-- .../flatbuffers/generated/CameraBindingGen.h | 49 +- .../flatbuffers/generated/DataArrayGen.h | 212 +++---- .../logic/flatbuffers/generated/LinkGen.h | 56 +- .../flatbuffers/generated/LogicEngineGen.h | 81 ++- .../flatbuffers/generated/LogicObjectGen.h | 64 +- .../flatbuffers/generated/LuaInterfaceGen.h | 52 +- .../flatbuffers/generated/LuaModuleGen.h | 145 ++--- .../flatbuffers/generated/LuaScriptGen.h | 112 ++-- .../generated/MeshNodeBindingGen.h | 49 +- .../flatbuffers/generated/NodeBindingGen.h | 53 +- .../logic/flatbuffers/generated/PropertyGen.h | 567 ++++++++++-------- .../flatbuffers/generated/RamsesBindingGen.h | 58 +- .../generated/RamsesReferenceGen.h | 46 +- .../generated/RenderGroupBindingGen.h | 124 ++-- .../generated/RenderPassBindingGen.h | 49 +- .../flatbuffers/generated/SkinBindingGen.h | 89 +-- .../flatbuffers/generated/TimerNodeGen.h | 58 +- src/framework/impl/RamsesObject.cpp | 50 +- .../logic-viewer-tests/LogicViewerAppTest.cpp | 9 +- .../DynamicQuad_Resources.cpp | 4 +- .../test-content/CubeTextureScene.cpp | 16 +- .../test-content/MultiTypeLinkScene.cpp | 10 +- .../TestScenes/Texture2DFormatScene.h | 2 +- ...ture2DAnisotropicTextureFilteringScene.cpp | 3 +- .../Texture2DCompressedMipMapScene.cpp | 3 +- .../test-content/Texture2DFormatScene.cpp | 55 +- .../Texture2DGenerateMipMapScene.cpp | 3 +- .../test-content/Texture2DSamplingScene.cpp | 3 +- .../test-content/Texture3DScene.cpp | 3 +- .../test-content/TextureBufferScene.cpp | 4 +- ...reCubeAnisotropicTextureFilteringScene.cpp | 25 +- .../test-content/TextureLinkScene.cpp | 12 +- .../test-content/TextureSamplerScene.cpp | 12 +- tests/unittests/client/AppearanceTest.cpp | 32 +- tests/unittests/client/CreationHelper.cpp | 16 +- tests/unittests/client/ResourceTest.cpp | 203 ++++--- .../client/ScenePersistationTest.cpp | 4 +- tests/unittests/client/SceneTest.cpp | 86 +-- .../client/logic/api/LogicObjectTest.cpp | 15 +- tools/ramses-imgui/src/ImguiClientHelper.cpp | 4 +- tools/ramses-imgui/src/ImguiImageCache.cpp | 6 +- tools/ramses-logic-viewer/Arguments.h | 8 +- .../ramses-logic-viewer/ImguiClientHelper.cpp | 4 +- .../ramses-logic-viewer/LogicViewerGuiApp.cpp | 6 +- tools/ramses-logic-viewer/SceneSetup.h | 6 +- tools/ramses-scene-viewer/src/SceneSetup.h | 8 +- tools/ramses-scene-viewer/src/SceneViewer.h | 2 +- tools/ramses-stream-viewer/src/main.cpp | 4 +- 94 files changed, 1837 insertions(+), 1643 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e0ca7cb8..8a093add9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ - # Ramses Changelog 28.0.0-rc2 @@ -23,7 +22,11 @@ ### Changed +- Modernized mipLevelData from C-style array to vector for Scene::createTexture2D(), Scene::createTexture3D() and Scene::createTextureCube() +- Scene::findObject now also searches for logic objects in all existing logic engines - Switched from fmt 7.0.1 to 10.1.1 +- Switched from google-benchmark 1.5.2 to 1.8.3 +- Switched from google-flatbuffers 1.12.0 to 23.5.9 - LogicEngine is now part of Scene and can only be created using Scene::createLogicEngine - LogicEngine is now a SceneObject of type ERamsesObjectType::LogicEngine and its lifecycle is managed by Scene as for any other SceneObject. LogicEngine is no longer a movable type. - removed scene argument from all loading functions in LogicEngine: loadFromFile, loadFromFileDescriptor, loadFromBuffer @@ -92,6 +95,10 @@ - compression flag is part of SaveFileConfig and is disabled by default - by default `saveToFile()` will fail, if the scene has validation errors (warnings will be ignored) - Changed interfaces to load/create scenes: Added sceneId and verification flag to `ramses::SceneConfig` +- ramses-scene-viewer: + - changed default gui mode to "overlay" (no offscreen buffer) +- ramses-logic-viewer: + - disabled offscreen rendering by default, cli option is changed from `--no-offscreen` to `--offscreen` - Renamed ramses::EClearFlags to ramses::EClearFlag - Renamed standalone renderer executable to `ramses-renderer-standalone` - Renamed `ERamsesObjectType::ArrayBufferObject` to `ERamsesObjectType::ArrayBuffer` to match the class name diff --git a/external/flatbuffers b/external/flatbuffers index 6df40a247..72b56fd08 160000 --- a/external/flatbuffers +++ b/external/flatbuffers @@ -1 +1 @@ -Subproject commit 6df40a2471737b27271bdd9b900ab5f3aec746c7 +Subproject commit 72b56fd0810c9a6cd38ea1c8f40feaa1bb4916b1 diff --git a/external/google-benchmark b/external/google-benchmark index 73d4d5e8d..344117638 160000 --- a/external/google-benchmark +++ b/external/google-benchmark @@ -1 +1 @@ -Subproject commit 73d4d5e8d6d449fc8663765a42aa8aeeee844489 +Subproject commit 344117638c8ff7e239044fd0fa7085839fc03021 diff --git a/include/ramses/client/Scene.h b/include/ramses/client/Scene.h index 739b9331b..fd2d92152 100644 --- a/include/ramses/client/Scene.h +++ b/include/ramses/client/Scene.h @@ -270,8 +270,8 @@ namespace ramses /** * @brief Get an object from the scene by name - * Note that this will not find #ramses::LogicObject instances created from #ramses::LogicEngine, - * use #ramses::LogicEngine::findObject for those. + * This will also search for logic objects in all existing #ramses::LogicEngine instances if used with #ramses::SceneObject or #ramses::LogicObject template type, + * however to search for a concrete logic type (e.g. #ramses::LuaScript) use #ramses::LogicEngine::findObject instead. * Note that giving a concrete object template type might result in faster search because it is limited to only objects of that type. * * @param[in] name The name of the object to get. @@ -286,6 +286,8 @@ namespace ramses /** * @brief Get an object from the scene by id + * This will also search for logic objects in all existing #ramses::LogicEngine instances if used with #ramses::SceneObject or #ramses::LogicObject template type, + * however to search for a concrete logic type (e.g. #ramses::LuaScript) use #ramses::LogicEngine::findObject instead. * * @param[in] id The id of the object to get. * @return Pointer to the object if found and convertible to the demanded type, nullptr otherwise. @@ -540,8 +542,7 @@ namespace ramses * @param[in] format Pixel format of the Texture2D data. * @param[in] width Width of the texture (mipmap level 0). * @param[in] height Height of the texture (mipmap level 0). - * @param[in] mipMapCount Number of mipmap levels contained in mipLevelData array. - * @param[in] mipLevelData Array of #ramses::MipLevelData structs defining mipmap levels + * @param[in] mipLevelData Vector of #ramses::MipLevelData structs defining mipmap levels * to use. Amount and sizes of supplied mipmap levels have to * conform to GL specification. Order is lowest level (biggest * resolution) to highest level (smallest resolution). @@ -555,8 +556,7 @@ namespace ramses ETextureFormat format, uint32_t width, uint32_t height, - size_t mipMapCount, - const MipLevelData mipLevelData[], // NOLINT(modernize-avoid-c-arrays) + const std::vector& mipLevelData, bool generateMipChain = false, const TextureSwizzle& swizzle = {}, std::string_view name = {}); @@ -569,8 +569,7 @@ namespace ramses * @param[in] width Width of the texture (mipmap level 0). * @param[in] height Height of the texture (mipmap level 0). * @param[in] depth Depth of the texture. - * @param[in] mipMapCount Number of mipmap levels contained in mipLevelData array. - * @param[in] mipLevelData Array of #ramses::MipLevelData structs defining mipmap levels + * @param[in] mipLevelData Vector of #ramses::MipLevelData structs defining mipmap levels * to use. Amount and sizes of supplied mipmap levels have to * conform to GL specification. Order is lowest level (biggest * resolution) to highest level (smallest resolution). @@ -583,8 +582,7 @@ namespace ramses uint32_t width, uint32_t height, uint32_t depth, - size_t mipMapCount, - const MipLevelData mipLevelData[], // NOLINT(modernize-avoid-c-arrays) + const std::vector& mipLevelData, bool generateMipChain = false, std::string_view name = {}); @@ -594,8 +592,7 @@ namespace ramses * * @param[in] format Pixel format of the Cube Texture data. * @param[in] size edge length of one quadratic cube face, belonging to the texture. - * @param[in] mipMapCount Number of mipmaps contained in mipLevelData array. - * @param[in] mipLevelData Array of MipLevelData structs defining mipmap levels + * @param[in] mipLevelData Vector of MipLevelData structs defining mipmap levels * to use. Amount and sizes of supplied mipmap levels have to * conform to GL specification. Order ist lowest level (biggest * resolution) to highest level (smallest resolution). @@ -607,8 +604,7 @@ namespace ramses TextureCube* createTextureCube( ETextureFormat format, uint32_t size, - size_t mipMapCount, - const CubeMipLevelData mipLevelData[], // NOLINT(modernize-avoid-c-arrays) + const std::vector& mipLevelData, bool generateMipChain = false, const TextureSwizzle& swizzle = {}, std::string_view name = {}); @@ -937,6 +933,6 @@ namespace ramses void Scene::StaticTypeCheck() { static_assert(std::is_base_of_v, "Type not derived from SceneObject or undefined! Make sure you include the header for the target type class."); - static_assert(!std::is_base_of_v, "To find logic objects use LogicEngine::findObject."); + static_assert(!std::is_base_of_v || std::is_same_v, "To find concrete types derived from LogicObject use LogicEngine::findObject."); } } diff --git a/scripts/code_style_checker/check_all_styles.py b/scripts/code_style_checker/check_all_styles.py index dcc8e3b44..fb51822fe 100755 --- a/scripts/code_style_checker/check_all_styles.py +++ b/scripts/code_style_checker/check_all_styles.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2018 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_curly_braces_alone_on_line.py b/scripts/code_style_checker/check_curly_braces_alone_on_line.py index 67a4db003..a04877817 100644 --- a/scripts/code_style_checker/check_curly_braces_alone_on_line.py +++ b/scripts/code_style_checker/check_curly_braces_alone_on_line.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_enum_style.py b/scripts/code_style_checker/check_enum_style.py index 23d1fa758..2e870552c 100644 --- a/scripts/code_style_checker/check_enum_style.py +++ b/scripts/code_style_checker/check_enum_style.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_header_guards.py b/scripts/code_style_checker/check_header_guards.py index 82051e723..3816c6779 100644 --- a/scripts/code_style_checker/check_header_guards.py +++ b/scripts/code_style_checker/check_header_guards.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_last_line_newline.py b/scripts/code_style_checker/check_last_line_newline.py index 37475dd81..ad688be54 100644 --- a/scripts/code_style_checker/check_last_line_newline.py +++ b/scripts/code_style_checker/check_last_line_newline.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_license.py b/scripts/code_style_checker/check_license.py index fed328b65..5f5299922 100644 --- a/scripts/code_style_checker/check_license.py +++ b/scripts/code_style_checker/check_license.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -20,23 +20,23 @@ ] # generate license regexes only once -G_RE_LICENSE_TEMPLATE_OPEN = re.compile(r"""(?://|::|#| ) ------------------------------------------------------------------------- +G_RE_LICENSE_TEMPLATE_OPEN = re.compile(r"""(?://|::|#|%%| ) ------------------------------------------------------------------------- (?://|::|#| ) Copyright \(C\) 2\d{3}(?:-2\d{3})?(?: BMW AG| BMW Car IT GmbH|, Garmin International, Inc\. and its affiliates\.)$(?:\n(?://|::|#) Copyright \(C\) .*$)* -(?://|::|#| ) ------------------------------------------------------------------------- -(?://|::|#| ) This Source Code Form is subject to the terms of the Mozilla Public -(?://|::|#| ) License, v\. 2\.0\. If a copy of the MPL was not distributed with this -(?://|::|#| ) file, You can obtain one at https://mozilla\.org/MPL/2\.0/\. -(?://|::|#| ) ------------------------------------------------------------------------- +(?://|::|#|%%| ) ------------------------------------------------------------------------- +(?://|::|#|%%| ) This Source Code Form is subject to the terms of the Mozilla Public +(?://|::|#|%%| ) License, v\. 2\.0\. If a copy of the MPL was not distributed with this +(?://|::|#|%%| ) file, You can obtain one at https://mozilla\.org/MPL/2\.0/\. +(?://|::|#|%%| ) ------------------------------------------------------------------------- """, re.MULTILINE) # noqa E501 allow long lines here -G_RE_LICENSE_TEMPLATE_PROP = re.compile(r"""(?://|::|#) ------------------------------------------------------------------------- -(?://|::|#) Copyright \(C\) 2\d{3}(?:-2\d{3})? (BMW AG|BMW Car IT GmbH) -(?://|::|#) All rights reserved\. -(?://|::|#) ------------------------------------------------------------------------- -(?://|::|#) This document contains proprietary information belonging to (\1)(\.) -(?://|::|#) Passing on and copying of this document, use and communication of its -(?://|::|#) contents is not permitted without prior written authorization\. -(?://|::|#) ------------------------------------------------------------------------- +G_RE_LICENSE_TEMPLATE_PROP = re.compile(r"""(?://|::|#|%%) ------------------------------------------------------------------------- +(?://|::|#|%%) Copyright \(C\) 2\d{3}(?:-2\d{3})? (BMW AG|BMW Car IT GmbH) +(?://|::|#|%%) All rights reserved\. +(?://|::|#|%%) ------------------------------------------------------------------------- +(?://|::|#|%%) This document contains proprietary information belonging to (\1)(\.) +(?://|::|#|%%) Passing on and copying of this document, use and communication of its +(?://|::|#|%%) contents is not permitted without prior written authorization\. +(?://|::|#|%%) ------------------------------------------------------------------------- """, re.MULTILINE) diff --git a/scripts/code_style_checker/check_single_definition_on_line.py b/scripts/code_style_checker/check_single_definition_on_line.py index 098023cd1..59edc0e2d 100644 --- a/scripts/code_style_checker/check_single_definition_on_line.py +++ b/scripts/code_style_checker/check_single_definition_on_line.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_single_statement_on_line.py b/scripts/code_style_checker/check_single_statement_on_line.py index 603fc07cc..7c2b8c250 100644 --- a/scripts/code_style_checker/check_single_statement_on_line.py +++ b/scripts/code_style_checker/check_single_statement_on_line.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/check_tabbing_and_spacing.py b/scripts/code_style_checker/check_tabbing_and_spacing.py index c24480245..e21db63a2 100644 --- a/scripts/code_style_checker/check_tabbing_and_spacing.py +++ b/scripts/code_style_checker/check_tabbing_and_spacing.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/scripts/code_style_checker/common_modules/common.py b/scripts/code_style_checker/common_modules/common.py index 2500dc291..8dabbd75b 100644 --- a/scripts/code_style_checker/common_modules/common.py +++ b/scripts/code_style_checker/common_modules/common.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------- -# Copyright (C) 2013 BMW Car IT GmbH +# Copyright (C) 2023 BMW AG # ------------------------------------------------------------------------- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/client/impl/RamsesClientImpl.cpp b/src/client/impl/RamsesClientImpl.cpp index 62fc2db03..a1d36cccb 100644 --- a/src/client/impl/RamsesClientImpl.cpp +++ b/src/client/impl/RamsesClientImpl.cpp @@ -760,16 +760,16 @@ namespace ramses::internal ramses::internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses::internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, - uint32_t mipMapCount, const MipDataStorageType mipLevelData[], bool generateMipChain, // NOLINT(modernize-avoid-c-arrays) + const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) { - if (!TextureUtils::TextureParametersValid(width, height, depth, mipMapCount) || !TextureUtils::MipDataValid(width, height, depth, mipMapCount, mipLevelData, format)) + if (!TextureUtils::TextureParametersValid(width, height, depth, static_cast(mipLevelData.size())) || !TextureUtils::MipDataValid(width, height, depth, mipLevelData, format)) { LOG_ERROR(ramses::internal::CONTEXT_CLIENT, "RamsesClient::createTexture: invalid parameters"); return {}; } - if (generateMipChain && (!FormatSupportsMipChainGeneration(format) || (mipMapCount > 1))) + if (generateMipChain && (!FormatSupportsMipChainGeneration(format) || (mipLevelData.size() > 1))) { LOG_WARN(ramses::internal::CONTEXT_CLIENT, "RamsesClient::createTexture: cannot auto generate mipmaps when custom mipmap data provided or unsupported format used"); generateMipChain = false; @@ -782,22 +782,22 @@ namespace ramses::internal texDesc.m_format = TextureUtils::GetTextureFormatInternal(format); texDesc.m_generateMipChain = generateMipChain; texDesc.m_swizzle = TextureUtils::GetTextureSwizzleInternal(swizzle); - TextureUtils::FillMipDataSizes(texDesc.m_dataSizes, mipMapCount, mipLevelData); + TextureUtils::FillMipDataSizes(texDesc.m_dataSizes, mipLevelData); auto* resource = new ramses::internal::TextureResource(textureType, texDesc, name); - TextureUtils::FillMipData(const_cast(resource->getResourceData().data()), mipMapCount, mipLevelData); + TextureUtils::FillMipData(const_cast(resource->getResourceData().data()), mipLevelData); return manageResource(resource); } template ramses::internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses::internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, - uint32_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, + const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); template ramses::internal::ManagedResource RamsesClientImpl::createManagedTexture(ramses::internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, - uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, + const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); ramses::internal::ManagedResource RamsesClientImpl::createManagedEffect(const EffectDescription& effectDesc, std::string_view name, std::string& errorMessages) diff --git a/src/client/impl/RamsesClientImpl.h b/src/client/impl/RamsesClientImpl.h index 8dc853358..11a8eb7a2 100644 --- a/src/client/impl/RamsesClientImpl.h +++ b/src/client/impl/RamsesClientImpl.h @@ -133,8 +133,8 @@ namespace ramses::internal // NOLINTNEXTLINE(modernize-avoid-c-arrays) ramses::internal::ManagedResource createManagedArrayResource(uint32_t numElements, ramses::EDataType type, const void* arrayData, std::string_view name); - template // NOLINTNEXTLINE(modernize-avoid-c-arrays) - ramses::internal::ManagedResource createManagedTexture(ramses::internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, uint32_t mipMapCount, const MipDataStorageType mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); + template + ramses::internal::ManagedResource createManagedTexture(ramses::internal::EResourceType textureType, uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); ramses::internal::ManagedResource createManagedEffect(const EffectDescription& effectDesc, std::string_view name, std::string& errorMessages); void writeLowLevelResourcesToStream(const ResourceObjects& resources, ramses::internal::IOutputStream& resourceOutputStream, bool compress) const; diff --git a/src/client/impl/Scene.cpp b/src/client/impl/Scene.cpp index 73a81f27a..69f263c46 100644 --- a/src/client/impl/Scene.cpp +++ b/src/client/impl/Scene.cpp @@ -429,27 +429,24 @@ namespace ramses return arr; } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture2D* Scene::createTexture2D(ETextureFormat format, uint32_t width, uint32_t height, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name /* = {} */) + Texture2D* Scene::createTexture2D(ETextureFormat format, uint32_t width, uint32_t height, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name /* = {} */) { - Texture2D* tex = m_impl.createTexture2D(width, height, format, mipMapCount, mipLevelData, generateMipChain, swizzle, name); - LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), width, height, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, swizzle, name); + Texture2D* tex = m_impl.createTexture2D(width, height, format, mipLevelData, generateMipChain, swizzle, name); + LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), width, height, toString(format), mipLevelData.size(), LOG_API_GENERIC_OBJECT_STRING(mipLevelData), generateMipChain, swizzle, name); return tex; } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture3D* Scene::createTexture3D(ETextureFormat format, uint32_t width, uint32_t height, uint32_t depth, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, std::string_view name /* = {} */) + Texture3D* Scene::createTexture3D(ETextureFormat format, uint32_t width, uint32_t height, uint32_t depth, const std::vector& mipLevelData, bool generateMipChain, std::string_view name /* = {} */) { - Texture3D* tex = m_impl.createTexture3D(width, height, depth, format, mipMapCount, mipLevelData, generateMipChain, name); - LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), width, height, depth, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, name); + Texture3D* tex = m_impl.createTexture3D(width, height, depth, format, mipLevelData, generateMipChain, name); + LOG_HL_CLIENT_API8(LOG_API_RESOURCE_PTR_STRING(tex), width, height, depth, toString(format), mipLevelData.size(), LOG_API_GENERIC_OBJECT_STRING(mipLevelData), generateMipChain, name); return tex; } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - TextureCube* Scene::createTextureCube(ETextureFormat format, uint32_t size, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name /* = {} */) + TextureCube* Scene::createTextureCube(ETextureFormat format, uint32_t size, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name /* = {} */) { - TextureCube* tex = m_impl.createTextureCube(size, format, mipMapCount, mipLevelData, generateMipChain, swizzle, name); - LOG_HL_CLIENT_API7(LOG_API_RESOURCE_PTR_STRING(tex), size, toString(format), mipMapCount, LOG_API_GENERIC_PTR_STRING(mipLevelData), generateMipChain, swizzle, name); + TextureCube* tex = m_impl.createTextureCube(size, format, mipLevelData, generateMipChain, swizzle, name); + LOG_HL_CLIENT_API7(LOG_API_RESOURCE_PTR_STRING(tex), size, toString(format), mipLevelData.size(), LOG_API_GENERIC_OBJECT_STRING(mipLevelData), generateMipChain, swizzle, name); return tex; } diff --git a/src/client/impl/SceneImpl.cpp b/src/client/impl/SceneImpl.cpp index 5b05c710a..d8121baba 100644 --- a/src/client/impl/SceneImpl.cpp +++ b/src/client/impl/SceneImpl.cpp @@ -1648,11 +1648,10 @@ namespace ramses::internal return ®isterCreatedResourceObject(std::move(pimpl)); } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture2D* SceneImpl::createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) + Texture2D* SceneImpl::createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) { ramses::internal::ManagedResource res = getClientImpl().createManagedTexture( - ramses::internal::EResourceType::Texture2D, width, height, 1u, format, static_cast(mipMapCount), mipLevelData, generateMipChain, swizzle, name); + ramses::internal::EResourceType::Texture2D, width, height, 1u, format, mipLevelData, generateMipChain, swizzle, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTexture2D: failed to create managed Texture2D resource"); @@ -1676,11 +1675,10 @@ namespace ramses::internal return ®isterCreatedResourceObject(std::move(pimpl)); } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture3D* SceneImpl::createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, std::string_view name) + Texture3D* SceneImpl::createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, std::string_view name) { ramses::internal::ManagedResource res = getClientImpl().createManagedTexture( - ramses::internal::EResourceType::Texture3D, width, height, depth, format, static_cast(mipMapCount), mipLevelData, generateMipChain, {}, name); + ramses::internal::EResourceType::Texture3D, width, height, depth, format, mipLevelData, generateMipChain, {}, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTexture3D: failed to create managed Texture3D resource"); @@ -1702,11 +1700,10 @@ namespace ramses::internal return ®isterCreatedResourceObject(std::move(pimpl)); } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - TextureCube* SceneImpl::createTextureCube(uint32_t size, ETextureFormat format, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) + TextureCube* SceneImpl::createTextureCube(uint32_t size, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name) { ramses::internal::ManagedResource res = getClientImpl().createManagedTexture( - ramses::internal::EResourceType::TextureCube, size, 1u, 1u, format, static_cast(mipMapCount), mipLevelData, generateMipChain, swizzle, name); + ramses::internal::EResourceType::TextureCube, size, 1u, 1u, format, mipLevelData, generateMipChain, swizzle, name); if (!res) { LOG_ERROR(CONTEXT_CLIENT, "Scene::createTextureCube: failed to create managed TextureCube resource"); diff --git a/src/client/impl/SceneImpl.h b/src/client/impl/SceneImpl.h index 7130f78ca..95cc5d7fe 100644 --- a/src/client/impl/SceneImpl.h +++ b/src/client/impl/SceneImpl.h @@ -209,12 +209,9 @@ namespace ramses::internal template // NOLINTNEXTLINE(modernize-avoid-c-arrays) ramses::ArrayResource* createArrayResource(uint32_t numElements, const T* arrayData, std::string_view name); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture2D* createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - Texture3D* createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, size_t mipMapCount, const MipLevelData mipLevelData[], bool generateMipChain, std::string_view name); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - TextureCube* createTextureCube(uint32_t size, ETextureFormat format, size_t mipMapCount, const CubeMipLevelData mipLevelData[], bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); + Texture2D* createTexture2D(uint32_t width, uint32_t height, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); + Texture3D* createTexture3D(uint32_t width, uint32_t height, uint32_t depth, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, std::string_view name); + TextureCube* createTextureCube(uint32_t size, ETextureFormat format, const std::vector& mipLevelData, bool generateMipChain, const TextureSwizzle& swizzle, std::string_view name); Effect* createEffect(const EffectDescription& effectDesc, std::string_view name); std::string getLastEffectErrorMessages() const; diff --git a/src/client/impl/SceneObjectRegistry.cpp b/src/client/impl/SceneObjectRegistry.cpp index fc10e6359..b871b9d82 100644 --- a/src/client/impl/SceneObjectRegistry.cpp +++ b/src/client/impl/SceneObjectRegistry.cpp @@ -10,7 +10,7 @@ #include "ramses/framework/RamsesObject.h" #include "ramses/client/Node.h" #include "impl/RamsesObjectImpl.h" -#include "ObjectIteratorImpl.h" +#include "impl/ObjectIteratorImpl.h" #include "impl/NodeImpl.h" #include "impl/RamsesObjectTypeUtils.h" #include "internal/PlatformAbstraction/PlatformStringUtils.h" @@ -40,8 +40,8 @@ namespace ramses::internal if (object.isOfType(ERamsesObjectType::SceneObject)) { const sceneObjectId_t sceneObjectId = RamsesObjectTypeUtils::ConvertTo(object).getSceneObjectId(); - assert(m_objectsById.contains(sceneObjectId)); - m_objectsById.remove(sceneObjectId); + assert(m_objectsById.count(sceneObjectId) != 0u); + m_objectsById.erase(sceneObjectId); } const SceneObjectRegistryHandle handle = object.impl().getObjectRegistryHandle(); @@ -85,17 +85,26 @@ namespace ramses::internal { auto& sceneObject = RamsesObjectTypeUtils::ConvertTo(object); const sceneObjectId_t sceneObjectId = RamsesObjectTypeUtils::ConvertTo(object).getSceneObjectId(); - assert(!m_objectsById.contains(sceneObjectId)); - m_objectsById.put(sceneObjectId, &sceneObject); + assert(m_objectsById.count(sceneObjectId) == 0u); + m_objectsById[sceneObjectId] = &sceneObject; } } SceneObject* SceneObjectRegistry::findObjectById(sceneObjectId_t id) { - SceneObject* object(nullptr); - m_objectsById.get(id, object); + const auto it = m_objectsById.find(id); + if (it != m_objectsById.cend()) + return it->second; - return object; + auto& logicEngines = m_objects[static_cast(ERamsesObjectType::LogicEngine)]; + for (auto& le : logicEngines) + { + auto obj = (*le.second)->as()->findObject(id); + if (obj) + return obj; + } + + return nullptr; } const SceneObject* SceneObjectRegistry::findObjectById(sceneObjectId_t id) const diff --git a/src/client/impl/SceneObjectRegistry.h b/src/client/impl/SceneObjectRegistry.h index cb717dd3f..1017608c3 100644 --- a/src/client/impl/SceneObjectRegistry.h +++ b/src/client/impl/SceneObjectRegistry.h @@ -10,18 +10,20 @@ #include "ramses/framework/RamsesFrameworkTypes.h" #include "ramses/client/SceneObject.h" +#include "ramses/client/logic/LogicEngine.h" +#include "ramses/client/logic/LogicObject.h" #include "impl/SceneObjectImpl.h" #include "impl/RamsesObjectVector.h" #include "impl/RamsesObjectTypeTraits.h" #include "impl/RamsesObjectTypeUtils.h" -#include "internal/PlatformAbstraction/Collections/HashMap.h" #include "internal/Core/Utils/MemoryPool.h" #include #include #include +#include namespace ramses::internal { @@ -57,8 +59,7 @@ namespace ramses::internal [[nodiscard]] bool containsObject(const SceneObject& object) const; void trackSceneObjectById(SceneObject& object); - using ObjectIdMap = ramses::internal::HashMap; - ObjectIdMap m_objectsById; + std::unordered_map m_objectsById; using SceneObjectsPool = ramses::internal::MemoryPool; std::array m_objects; @@ -87,19 +88,35 @@ namespace ramses::internal template T* SceneObjectRegistry::findObjectByName(std::string_view name) { - constexpr ERamsesObjectType typeToReturn = TYPE_ID_OF_RAMSES_OBJECT::ID; - for (size_t typeIdx = 0u; typeIdx < RamsesObjectTypeCount; ++typeIdx) + if constexpr (!std::is_base_of_v) // if searching for logic object don't bother going thru scene registry { - const auto type = static_cast(typeIdx); - if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, typeToReturn)) + constexpr ERamsesObjectType typeToReturn = TYPE_ID_OF_RAMSES_OBJECT::ID; + for (size_t typeIdx = 0u; typeIdx < RamsesObjectTypeCount; ++typeIdx) { - const auto& objs = m_objects[typeIdx]; - const auto it = std::find_if(objs.begin(), objs.end(), [name](const auto o) { return (*o.second)->getName() == name; }); - if (it != objs.end()) - return (*it->second)->template as(); + const auto type = static_cast(typeIdx); + if (RamsesObjectTypeUtils::IsConcreteType(type) && RamsesObjectTypeUtils::IsTypeMatchingBaseType(type, typeToReturn)) + { + const auto& objs = m_objects[typeIdx]; + const auto it = std::find_if(objs.begin(), objs.end(), [name](const auto o) { return (*o.second)->getName() == name; }); + if (it != objs.end()) + return (*it->second)->template as(); + } } } + // NOLINTNEXTLINE(readability-misleading-indentation) for some reason clang is confused about constexpr branch above + if constexpr (std::is_base_of_v) // additionally search logic engines if type is base of LogicObject + { + auto& logicEngines = m_objects[static_cast(ERamsesObjectType::LogicEngine)]; + for (auto& le : logicEngines) + { + auto obj = (*le.second)->as()->findObject(name); + if (obj) + return obj; + } + } + + // NOLINTNEXTLINE(readability-misleading-indentation) for some reason clang is confused about constexpr branch above return nullptr; } diff --git a/src/client/impl/TextureUtils.cpp b/src/client/impl/TextureUtils.cpp index 688a95ce0..69669f4c8 100644 --- a/src/client/impl/TextureUtils.cpp +++ b/src/client/impl/TextureUtils.cpp @@ -14,100 +14,95 @@ namespace ramses::internal { - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const MipLevelData mipLevelData[]) + void TextureUtils::FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, const std::vector& mipLevelData) { assert(mipDataSizes.empty()); - mipDataSizes.reserve(mipMapCount); - for (uint32_t i = 0u; i < mipMapCount; ++i) + mipDataSizes.reserve(mipLevelData.size()); + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_size; + const uint32_t mipDataSize = data.m_size; mipDataSizes.push_back(mipDataSize); } } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]) + void TextureUtils::FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, const std::vector& mipLevelData) { assert(mipDataSizes.empty()); - mipDataSizes.reserve(mipMapCount); - for (uint32_t i = 0u; i < mipMapCount; ++i) + mipDataSizes.reserve(mipLevelData.size()); + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; + const uint32_t mipDataSize = data.m_faceDataSize; mipDataSizes.push_back(mipDataSize); } } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipData(std::byte* dest, uint32_t mipMapCount, const MipLevelData mipLevelData[]) + void TextureUtils::FillMipData(std::byte* dest, const std::vector& mipLevelData) { - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_size; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_data, mipDataSize); + const uint32_t mipDataSize = data.m_size; + ramses::internal::PlatformMemory::Copy(dest, data.m_data, mipDataSize); dest += mipDataSize; } } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - void TextureUtils::FillMipData(std::byte* dest, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]) + void TextureUtils::FillMipData(std::byte* dest, const std::vector& mipLevelData) { - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPX, mipDataSize); + const uint32_t mipDataSize = data.m_faceDataSize; + ramses::internal::PlatformMemory::Copy(dest, data.m_dataPX, mipDataSize); dest += mipDataSize; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNX, mipDataSize); + const uint32_t mipDataSize = data.m_faceDataSize; + ramses::internal::PlatformMemory::Copy(dest, data.m_dataNX, mipDataSize); dest += mipDataSize; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPY, mipDataSize); + const uint32_t mipDataSize = data.m_faceDataSize; + ramses::internal::PlatformMemory::Copy(dest, data.m_dataPY, mipDataSize); dest += mipDataSize; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNY, mipDataSize); + const uint32_t mipDataSize = data.m_faceDataSize; + ramses::internal::PlatformMemory::Copy(dest, data.m_dataNY, mipDataSize); dest += mipDataSize; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataPZ, mipDataSize); + const uint32_t mipDataSize = data.m_faceDataSize; + ramses::internal::PlatformMemory::Copy(dest, data.m_dataPZ, mipDataSize); dest += mipDataSize; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (const auto& data : mipLevelData) { - const uint32_t mipDataSize = mipLevelData[i].m_faceDataSize; - ramses::internal::PlatformMemory::Copy(dest, mipLevelData[i].m_dataNZ, mipDataSize); + const uint32_t mipDataSize = data.m_faceDataSize; + ramses::internal::PlatformMemory::Copy(dest, data.m_dataNZ, mipDataSize); dest += mipDataSize; } } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - bool TextureUtils::MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const MipLevelData mipLevelData[], ETextureFormat format) + bool TextureUtils::MipDataValid(uint32_t width, uint32_t height, uint32_t depth, const std::vector& mipLevelData, ETextureFormat format) { - if (mipMapCount == 0u || mipLevelData == nullptr) + if (mipLevelData.empty()) { return false; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (size_t i = 0u; i < mipLevelData.size(); ++i) { if (mipLevelData[i].m_data == nullptr || mipLevelData[i].m_size == 0u) { return false; } - const uint32_t mipWidth = ramses::internal::TextureMathUtils::GetMipSize(i, width); - const uint32_t mipHeight = ramses::internal::TextureMathUtils::GetMipSize(i, height); - const uint32_t mipDepth = ramses::internal::TextureMathUtils::GetMipSize(i, depth); + const uint32_t mipWidth = ramses::internal::TextureMathUtils::GetMipSize(static_cast(i), width); + const uint32_t mipHeight = ramses::internal::TextureMathUtils::GetMipSize(static_cast(i), height); + const uint32_t mipDepth = ramses::internal::TextureMathUtils::GetMipSize(static_cast(i), depth); const ramses::internal::EPixelStorageFormat internalFormat = TextureUtils::GetTextureFormatInternal(format); if (!ramses::internal::IsFormatCompressed(internalFormat)) { @@ -132,22 +127,20 @@ namespace ramses::internal return true; } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - bool TextureUtils::MipDataValid(uint32_t width, [[maybe_unused]] uint32_t height, [[maybe_unused]] uint32_t depth, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format) + bool TextureUtils::MipDataValid(uint32_t width, [[maybe_unused]] uint32_t height, [[maybe_unused]] uint32_t depth, const std::vector& mipLevelData, ETextureFormat format) { // wrapper so that this function can be called from template following 2D/3D texture function signature - return MipDataValid(width, mipMapCount, mipLevelData, format); + return MipDataValid(width, mipLevelData, format); } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - bool TextureUtils::MipDataValid(uint32_t size, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format) + bool TextureUtils::MipDataValid(uint32_t size, const std::vector& mipLevelData, ETextureFormat format) { - if (mipMapCount == 0u || mipLevelData == nullptr) + if (mipLevelData.empty()) { return false; } - for (uint32_t i = 0u; i < mipMapCount; ++i) + for (size_t i = 0u; i < mipLevelData.size(); ++i) { if (mipLevelData[i].m_dataPX == nullptr || mipLevelData[i].m_dataNX == nullptr || @@ -160,7 +153,7 @@ namespace ramses::internal return false; } - const uint32_t mipSize = ramses::internal::TextureMathUtils::GetMipSize(i, size); + const uint32_t mipSize = ramses::internal::TextureMathUtils::GetMipSize(static_cast(i), size); const ramses::internal::EPixelStorageFormat internalFormat = TextureUtils::GetTextureFormatInternal(format); if (!ramses::internal::IsFormatCompressed(internalFormat)) { diff --git a/src/client/impl/TextureUtils.h b/src/client/impl/TextureUtils.h index 77ea4ac60..9a74d4839 100644 --- a/src/client/impl/TextureUtils.h +++ b/src/client/impl/TextureUtils.h @@ -484,20 +484,13 @@ namespace ramses::internal return ERenderBufferFormat::RGBA8; } - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const MipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipData(std::byte* dest, uint32_t mipMapCount, const MipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static void FillMipData(std::byte* dest, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[]); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const MipLevelData mipLevelData[], ETextureFormat format); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format); - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - static bool MipDataValid(uint32_t size, uint32_t mipMapCount, const CubeMipLevelData mipLevelData[], ETextureFormat format); + static void FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, const std::vector& mipLevelData); + static void FillMipDataSizes(ramses::internal::MipDataSizeVector& mipDataSizes, const std::vector& mipLevelData); + static void FillMipData(std::byte* dest, const std::vector& mipLevelData); + static void FillMipData(std::byte* dest, const std::vector& mipLevelData); + static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, const std::vector& mipLevelData, ETextureFormat format); + static bool MipDataValid(uint32_t width, uint32_t height, uint32_t depth, const std::vector& mipLevelData, ETextureFormat format); + static bool MipDataValid(uint32_t size, const std::vector& mipLevelData, ETextureFormat format); static bool TextureParametersValid(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipMapCount); }; } diff --git a/src/client/impl/logic/AnchorPointImpl.h b/src/client/impl/logic/AnchorPointImpl.h index 9afec550d..765596290 100644 --- a/src/client/impl/logic/AnchorPointImpl.h +++ b/src/client/impl/logic/AnchorPointImpl.h @@ -18,8 +18,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/AnimationNodeImpl.h b/src/client/impl/logic/AnimationNodeImpl.h index 51be78300..4200dd367 100644 --- a/src/client/impl/logic/AnimationNodeImpl.h +++ b/src/client/impl/logic/AnimationNodeImpl.h @@ -22,7 +22,8 @@ namespace rlogic_serialization namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/AppearanceBindingImpl.h b/src/client/impl/logic/AppearanceBindingImpl.h index dfca3a80d..32465ae55 100644 --- a/src/client/impl/logic/AppearanceBindingImpl.h +++ b/src/client/impl/logic/AppearanceBindingImpl.h @@ -32,8 +32,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/CameraBindingImpl.h b/src/client/impl/logic/CameraBindingImpl.h index 87ee6fd11..78356db93 100644 --- a/src/client/impl/logic/CameraBindingImpl.h +++ b/src/client/impl/logic/CameraBindingImpl.h @@ -28,8 +28,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/DataArrayImpl.h b/src/client/impl/logic/DataArrayImpl.h index 49e1a0f59..7b393743b 100644 --- a/src/client/impl/logic/DataArrayImpl.h +++ b/src/client/impl/logic/DataArrayImpl.h @@ -23,7 +23,8 @@ namespace rlogic_serialization namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/LogicObjectImpl.h b/src/client/impl/logic/LogicObjectImpl.h index a0f89be69..267b10abb 100644 --- a/src/client/impl/logic/LogicObjectImpl.h +++ b/src/client/impl/logic/LogicObjectImpl.h @@ -14,7 +14,8 @@ namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace rlogic_serialization diff --git a/src/client/impl/logic/LuaInterfaceImpl.h b/src/client/impl/logic/LuaInterfaceImpl.h index eb8a8c423..1e1091960 100644 --- a/src/client/impl/logic/LuaInterfaceImpl.h +++ b/src/client/impl/logic/LuaInterfaceImpl.h @@ -24,10 +24,9 @@ namespace flatbuffers { - class FlatBufferBuilder; - - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace rlogic_serialization diff --git a/src/client/impl/logic/LuaModuleImpl.h b/src/client/impl/logic/LuaModuleImpl.h index 596ce76fb..35fa96f64 100644 --- a/src/client/impl/logic/LuaModuleImpl.h +++ b/src/client/impl/logic/LuaModuleImpl.h @@ -22,7 +22,8 @@ namespace rlogic_serialization namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses diff --git a/src/client/impl/logic/LuaScriptImpl.h b/src/client/impl/logic/LuaScriptImpl.h index 3a42c6e67..1502f04a4 100644 --- a/src/client/impl/logic/LuaScriptImpl.h +++ b/src/client/impl/logic/LuaScriptImpl.h @@ -24,10 +24,9 @@ namespace flatbuffers { - class FlatBufferBuilder; - - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace rlogic_serialization diff --git a/src/client/impl/logic/MeshNodeBindingImpl.h b/src/client/impl/logic/MeshNodeBindingImpl.h index 9e3096958..c58e5a52d 100644 --- a/src/client/impl/logic/MeshNodeBindingImpl.h +++ b/src/client/impl/logic/MeshNodeBindingImpl.h @@ -23,8 +23,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/NodeBindingImpl.h b/src/client/impl/logic/NodeBindingImpl.h index 47032ff23..31ed1eb15 100644 --- a/src/client/impl/logic/NodeBindingImpl.h +++ b/src/client/impl/logic/NodeBindingImpl.h @@ -29,8 +29,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/PropertyImpl.h b/src/client/impl/logic/PropertyImpl.h index 7f5184961..6c9222935 100644 --- a/src/client/impl/logic/PropertyImpl.h +++ b/src/client/impl/logic/PropertyImpl.h @@ -35,7 +35,8 @@ namespace rlogic_serialization namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/RamsesBindingImpl.h b/src/client/impl/logic/RamsesBindingImpl.h index a07200f1f..c8f4fa5f1 100644 --- a/src/client/impl/logic/RamsesBindingImpl.h +++ b/src/client/impl/logic/RamsesBindingImpl.h @@ -18,7 +18,8 @@ namespace ramses namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace rlogic_serialization diff --git a/src/client/impl/logic/RenderGroupBindingImpl.h b/src/client/impl/logic/RenderGroupBindingImpl.h index a14a6c92a..5daae03ce 100644 --- a/src/client/impl/logic/RenderGroupBindingImpl.h +++ b/src/client/impl/logic/RenderGroupBindingImpl.h @@ -24,8 +24,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/RenderPassBindingImpl.h b/src/client/impl/logic/RenderPassBindingImpl.h index 49f4e5261..32bcc84cf 100644 --- a/src/client/impl/logic/RenderPassBindingImpl.h +++ b/src/client/impl/logic/RenderPassBindingImpl.h @@ -23,8 +23,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/SkinBindingImpl.h b/src/client/impl/logic/SkinBindingImpl.h index 108584f0f..25a9c66f3 100644 --- a/src/client/impl/logic/SkinBindingImpl.h +++ b/src/client/impl/logic/SkinBindingImpl.h @@ -21,8 +21,9 @@ namespace rlogic_serialization namespace flatbuffers { - class FlatBufferBuilder; template struct Offset; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/logic/TimerNodeImpl.h b/src/client/impl/logic/TimerNodeImpl.h index 3b18b7d6e..b586e1901 100644 --- a/src/client/impl/logic/TimerNodeImpl.h +++ b/src/client/impl/logic/TimerNodeImpl.h @@ -21,7 +21,8 @@ namespace rlogic_serialization namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses::internal diff --git a/src/client/impl/ramses-utils.cpp b/src/client/impl/ramses-utils.cpp index 483399c83..222791248 100644 --- a/src/client/impl/ramses-utils.cpp +++ b/src/client/impl/ramses-utils.cpp @@ -83,8 +83,8 @@ namespace ramses return nullptr; } - MipLevelData mipLevelData{static_cast(data.size()), data.data()}; - return scene.createTexture2D(ETextureFormat::RGBA8, width, height, 1, &mipLevelData, false, swizzle, name); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + return scene.createTexture2D(ETextureFormat::RGBA8, width, height, mipLevelData, false, swizzle, name); } Texture2D* RamsesUtils::CreateTextureResourceFromPngBuffer(const std::vector& pngData, Scene& scene, const TextureSwizzle& swizzle, std::string_view name) @@ -100,8 +100,8 @@ namespace ramses return nullptr; } - MipLevelData mipLevelData{static_cast(data.size()), data.data()}; - return scene.createTexture2D(ETextureFormat::RGBA8, width, height, 1, &mipLevelData, false, swizzle, name); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + return scene.createTexture2D(ETextureFormat::RGBA8, width, height, mipLevelData, false, swizzle, name); } bool RamsesUtils::SaveImageBufferToPng(const std::string& filePath, const std::vector& imageData, uint32_t width, uint32_t height) diff --git a/src/client/internal/logic/ApiObjects.h b/src/client/internal/logic/ApiObjects.h index a2b03c5be..7e5cef1fc 100644 --- a/src/client/internal/logic/ApiObjects.h +++ b/src/client/internal/logic/ApiObjects.h @@ -34,7 +34,8 @@ namespace rlogic_serialization namespace flatbuffers { template struct Offset; - class FlatBufferBuilder; + template class FlatBufferBuilderImpl; + using FlatBufferBuilder = FlatBufferBuilderImpl; } namespace ramses diff --git a/src/client/internal/logic/flatbuffers/generated/AnchorPointGen.h b/src/client/internal/logic/flatbuffers/generated/AnchorPointGen.h index 64931d61c..15152dea9 100644 --- a/src/client/internal/logic/flatbuffers/generated/AnchorPointGen.h +++ b/src/client/internal/logic/flatbuffers/generated/AnchorPointGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" #include "PropertyGen.h" @@ -14,12 +21,12 @@ namespace rlogic_serialization { struct AnchorPoint; struct AnchorPointBuilder; -inline const flatbuffers::TypeTable *AnchorPointTypeTable(); +inline const ::flatbuffers::TypeTable *AnchorPointTypeTable(); -struct AnchorPoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct AnchorPoint FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef AnchorPointBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return AnchorPointTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -44,12 +51,12 @@ struct AnchorPoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::Property *rootOutput() const { return GetPointer(VT_ROOTOUTPUT); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && - VerifyField(verifier, VT_NODEBINDINGID) && - VerifyField(verifier, VT_CAMERABINDINGID) && + VerifyField(verifier, VT_NODEBINDINGID, 8) && + VerifyField(verifier, VT_CAMERABINDINGID, 8) && VerifyOffset(verifier, VT_ROOTINPUT) && verifier.VerifyTable(rootInput()) && VerifyOffset(verifier, VT_ROOTOUTPUT) && @@ -60,9 +67,9 @@ struct AnchorPoint FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct AnchorPointBuilder { typedef AnchorPoint Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(AnchorPoint::VT_BASE, base); } void add_nodeBindingId(uint64_t nodeBindingId) { @@ -71,31 +78,30 @@ struct AnchorPointBuilder { void add_cameraBindingId(uint64_t cameraBindingId) { fbb_.AddElement(AnchorPoint::VT_CAMERABINDINGID, cameraBindingId, 0); } - void add_rootInput(flatbuffers::Offset rootInput) { + void add_rootInput(::flatbuffers::Offset rootInput) { fbb_.AddOffset(AnchorPoint::VT_ROOTINPUT, rootInput); } - void add_rootOutput(flatbuffers::Offset rootOutput) { + void add_rootOutput(::flatbuffers::Offset rootOutput) { fbb_.AddOffset(AnchorPoint::VT_ROOTOUTPUT, rootOutput); } - explicit AnchorPointBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit AnchorPointBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - AnchorPointBuilder &operator=(const AnchorPointBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateAnchorPoint( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, +inline ::flatbuffers::Offset CreateAnchorPoint( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, uint64_t nodeBindingId = 0, uint64_t cameraBindingId = 0, - flatbuffers::Offset rootInput = 0, - flatbuffers::Offset rootOutput = 0) { + ::flatbuffers::Offset rootInput = 0, + ::flatbuffers::Offset rootOutput = 0) { AnchorPointBuilder builder_(_fbb); builder_.add_cameraBindingId(cameraBindingId); builder_.add_nodeBindingId(nodeBindingId); @@ -110,15 +116,15 @@ struct AnchorPoint::Traits { static auto constexpr Create = CreateAnchorPoint; }; -inline const flatbuffers::TypeTable *AnchorPointTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 1 }, - { flatbuffers::ET_SEQUENCE, 0, 1 } +inline const ::flatbuffers::TypeTable *AnchorPointTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::PropertyTypeTable }; @@ -129,8 +135,8 @@ inline const flatbuffers::TypeTable *AnchorPointTypeTable() { "rootInput", "rootOutput" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/AnimationNodeGen.h b/src/client/internal/logic/flatbuffers/generated/AnimationNodeGen.h index 3ac226960..7fa331863 100644 --- a/src/client/internal/logic/flatbuffers/generated/AnimationNodeGen.h +++ b/src/client/internal/logic/flatbuffers/generated/AnimationNodeGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "DataArrayGen.h" #include "LogicObjectGen.h" #include "PropertyGen.h" @@ -18,9 +25,9 @@ struct ChannelBuilder; struct AnimationNode; struct AnimationNodeBuilder; -inline const flatbuffers::TypeTable *ChannelTypeTable(); +inline const ::flatbuffers::TypeTable *ChannelTypeTable(); -inline const flatbuffers::TypeTable *AnimationNodeTypeTable(); +inline const ::flatbuffers::TypeTable *AnimationNodeTypeTable(); enum class EInterpolationType : uint8_t { Step = 0, @@ -56,15 +63,15 @@ inline const char * const *EnumNamesEInterpolationType() { } inline const char *EnumNameEInterpolationType(EInterpolationType e) { - if (flatbuffers::IsOutRange(e, EInterpolationType::Step, EInterpolationType::Cubic_Quaternions)) return ""; + if (::flatbuffers::IsOutRange(e, EInterpolationType::Step, EInterpolationType::Cubic_Quaternions)) return ""; const size_t index = static_cast(e); return EnumNamesEInterpolationType()[index]; } -struct Channel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct Channel FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef ChannelBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return ChannelTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -75,8 +82,8 @@ struct Channel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_TANGENTSIN = 12, VT_TANGENTSOUT = 14 }; - const flatbuffers::String *name() const { - return GetPointer(VT_NAME); + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); } const rlogic_serialization::DataArray *timestamps() const { return GetPointer(VT_TIMESTAMPS); @@ -93,7 +100,7 @@ struct Channel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::DataArray *tangentsOut() const { return GetPointer(VT_TANGENTSOUT); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && verifier.VerifyString(name()) && @@ -101,7 +108,7 @@ struct Channel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { verifier.VerifyTable(timestamps()) && VerifyOffset(verifier, VT_KEYFRAMES) && verifier.VerifyTable(keyframes()) && - VerifyField(verifier, VT_INTERPOLATIONTYPE) && + VerifyField(verifier, VT_INTERPOLATIONTYPE, 1) && VerifyOffset(verifier, VT_TANGENTSIN) && verifier.VerifyTable(tangentsIn()) && VerifyOffset(verifier, VT_TANGENTSOUT) && @@ -112,46 +119,45 @@ struct Channel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct ChannelBuilder { typedef Channel Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_name(flatbuffers::Offset name) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { fbb_.AddOffset(Channel::VT_NAME, name); } - void add_timestamps(flatbuffers::Offset timestamps) { + void add_timestamps(::flatbuffers::Offset timestamps) { fbb_.AddOffset(Channel::VT_TIMESTAMPS, timestamps); } - void add_keyframes(flatbuffers::Offset keyframes) { + void add_keyframes(::flatbuffers::Offset keyframes) { fbb_.AddOffset(Channel::VT_KEYFRAMES, keyframes); } void add_interpolationType(rlogic_serialization::EInterpolationType interpolationType) { fbb_.AddElement(Channel::VT_INTERPOLATIONTYPE, static_cast(interpolationType), 0); } - void add_tangentsIn(flatbuffers::Offset tangentsIn) { + void add_tangentsIn(::flatbuffers::Offset tangentsIn) { fbb_.AddOffset(Channel::VT_TANGENTSIN, tangentsIn); } - void add_tangentsOut(flatbuffers::Offset tangentsOut) { + void add_tangentsOut(::flatbuffers::Offset tangentsOut) { fbb_.AddOffset(Channel::VT_TANGENTSOUT, tangentsOut); } - explicit ChannelBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit ChannelBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - ChannelBuilder &operator=(const ChannelBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateChannel( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset name = 0, - flatbuffers::Offset timestamps = 0, - flatbuffers::Offset keyframes = 0, +inline ::flatbuffers::Offset CreateChannel( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset timestamps = 0, + ::flatbuffers::Offset keyframes = 0, rlogic_serialization::EInterpolationType interpolationType = rlogic_serialization::EInterpolationType::Step, - flatbuffers::Offset tangentsIn = 0, - flatbuffers::Offset tangentsOut = 0) { + ::flatbuffers::Offset tangentsIn = 0, + ::flatbuffers::Offset tangentsOut = 0) { ChannelBuilder builder_(_fbb); builder_.add_tangentsOut(tangentsOut); builder_.add_tangentsIn(tangentsIn); @@ -167,14 +173,14 @@ struct Channel::Traits { static auto constexpr Create = CreateChannel; }; -inline flatbuffers::Offset CreateChannelDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreateChannelDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, - flatbuffers::Offset timestamps = 0, - flatbuffers::Offset keyframes = 0, + ::flatbuffers::Offset timestamps = 0, + ::flatbuffers::Offset keyframes = 0, rlogic_serialization::EInterpolationType interpolationType = rlogic_serialization::EInterpolationType::Step, - flatbuffers::Offset tangentsIn = 0, - flatbuffers::Offset tangentsOut = 0) { + ::flatbuffers::Offset tangentsIn = 0, + ::flatbuffers::Offset tangentsOut = 0) { auto name__ = name ? _fbb.CreateString(name) : 0; return rlogic_serialization::CreateChannel( _fbb, @@ -186,10 +192,10 @@ inline flatbuffers::Offset CreateChannelDirect( tangentsOut); } -struct AnimationNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct AnimationNode FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef AnimationNodeBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return AnimationNodeTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -202,8 +208,8 @@ struct AnimationNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::LogicObject *base() const { return GetPointer(VT_BASE); } - const flatbuffers::Vector> *channels() const { - return GetPointer> *>(VT_CHANNELS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *channels() const { + return GetPointer> *>(VT_CHANNELS); } bool channelsAsProperties() const { return GetField(VT_CHANNELSASPROPERTIES, 0) != 0; @@ -214,14 +220,14 @@ struct AnimationNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::Property *rootOutput() const { return GetPointer(VT_ROOTOUTPUT); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && VerifyOffset(verifier, VT_CHANNELS) && verifier.VerifyVector(channels()) && verifier.VerifyVectorOfTables(channels()) && - VerifyField(verifier, VT_CHANNELSASPROPERTIES) && + VerifyField(verifier, VT_CHANNELSASPROPERTIES, 1) && VerifyOffset(verifier, VT_ROOTINPUT) && verifier.VerifyTable(rootInput()) && VerifyOffset(verifier, VT_ROOTOUTPUT) && @@ -232,42 +238,41 @@ struct AnimationNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct AnimationNodeBuilder { typedef AnimationNode Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(AnimationNode::VT_BASE, base); } - void add_channels(flatbuffers::Offset>> channels) { + void add_channels(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> channels) { fbb_.AddOffset(AnimationNode::VT_CHANNELS, channels); } void add_channelsAsProperties(bool channelsAsProperties) { fbb_.AddElement(AnimationNode::VT_CHANNELSASPROPERTIES, static_cast(channelsAsProperties), 0); } - void add_rootInput(flatbuffers::Offset rootInput) { + void add_rootInput(::flatbuffers::Offset rootInput) { fbb_.AddOffset(AnimationNode::VT_ROOTINPUT, rootInput); } - void add_rootOutput(flatbuffers::Offset rootOutput) { + void add_rootOutput(::flatbuffers::Offset rootOutput) { fbb_.AddOffset(AnimationNode::VT_ROOTOUTPUT, rootOutput); } - explicit AnimationNodeBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit AnimationNodeBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - AnimationNodeBuilder &operator=(const AnimationNodeBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateAnimationNode( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset>> channels = 0, +inline ::flatbuffers::Offset CreateAnimationNode( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> channels = 0, bool channelsAsProperties = false, - flatbuffers::Offset rootInput = 0, - flatbuffers::Offset rootOutput = 0) { + ::flatbuffers::Offset rootInput = 0, + ::flatbuffers::Offset rootOutput = 0) { AnimationNodeBuilder builder_(_fbb); builder_.add_rootOutput(rootOutput); builder_.add_rootInput(rootInput); @@ -282,14 +287,14 @@ struct AnimationNode::Traits { static auto constexpr Create = CreateAnimationNode; }; -inline flatbuffers::Offset CreateAnimationNodeDirect( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - const std::vector> *channels = nullptr, +inline ::flatbuffers::Offset CreateAnimationNodeDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + const std::vector<::flatbuffers::Offset> *channels = nullptr, bool channelsAsProperties = false, - flatbuffers::Offset rootInput = 0, - flatbuffers::Offset rootOutput = 0) { - auto channels__ = channels ? _fbb.CreateVector>(*channels) : 0; + ::flatbuffers::Offset rootInput = 0, + ::flatbuffers::Offset rootOutput = 0) { + auto channels__ = channels ? _fbb.CreateVector<::flatbuffers::Offset>(*channels) : 0; return rlogic_serialization::CreateAnimationNode( _fbb, base, @@ -299,15 +304,15 @@ inline flatbuffers::Offset CreateAnimationNodeDirect( rootOutput); } -inline const flatbuffers::TypeTable *EInterpolationTypeTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 } +inline const ::flatbuffers::TypeTable *EInterpolationTypeTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::EInterpolationTypeTypeTable }; static const char * const names[] = { @@ -317,22 +322,22 @@ inline const flatbuffers::TypeTable *EInterpolationTypeTypeTable() { "Linear_Quaternions", "Cubic_Quaternions" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_ENUM, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_ENUM, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *ChannelTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 1 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 0 } +inline const ::flatbuffers::TypeTable *ChannelTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::DataArrayTypeTable, rlogic_serialization::EInterpolationTypeTypeTable }; @@ -344,21 +349,21 @@ inline const flatbuffers::TypeTable *ChannelTypeTable() { "tangentsIn", "tangentsOut" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 6, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 6, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *AnimationNodeTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 1, 1 }, - { flatbuffers::ET_BOOL, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 2 }, - { flatbuffers::ET_SEQUENCE, 0, 2 } +inline const ::flatbuffers::TypeTable *AnimationNodeTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 1, 1 }, + { ::flatbuffers::ET_BOOL, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::ChannelTypeTable, rlogic_serialization::PropertyTypeTable @@ -370,8 +375,8 @@ inline const flatbuffers::TypeTable *AnimationNodeTypeTable() { "rootInput", "rootOutput" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h b/src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h index 996013b4c..c55d1d66d 100644 --- a/src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h +++ b/src/client/internal/logic/flatbuffers/generated/ApiObjectsGen.h @@ -6,21 +6,24 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "AnchorPointGen.h" #include "AnimationNodeGen.h" #include "AppearanceBindingGen.h" #include "CameraBindingGen.h" #include "DataArrayGen.h" #include "LinkGen.h" -#include "LogicObjectGen.h" #include "LuaInterfaceGen.h" #include "LuaModuleGen.h" #include "LuaScriptGen.h" #include "MeshNodeBindingGen.h" #include "NodeBindingGen.h" -#include "PropertyGen.h" -#include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" #include "RenderGroupBindingGen.h" #include "RenderPassBindingGen.h" #include "SkinBindingGen.h" @@ -31,12 +34,12 @@ namespace rlogic_serialization { struct ApiObjects; struct ApiObjectsBuilder; -inline const flatbuffers::TypeTable *ApiObjectsTypeTable(); +inline const ::flatbuffers::TypeTable *ApiObjectsTypeTable(); -struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct ApiObjects FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef ApiObjectsBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return ApiObjectsTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -56,52 +59,52 @@ struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_SKINBINDINGS = 30, VT_MESHNODEBINDINGS = 32 }; - const flatbuffers::Vector> *luaModules() const { - return GetPointer> *>(VT_LUAMODULES); + const ::flatbuffers::Vector<::flatbuffers::Offset> *luaModules() const { + return GetPointer> *>(VT_LUAMODULES); } - const flatbuffers::Vector> *luaScripts() const { - return GetPointer> *>(VT_LUASCRIPTS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *luaScripts() const { + return GetPointer> *>(VT_LUASCRIPTS); } - const flatbuffers::Vector> *luaInterfaces() const { - return GetPointer> *>(VT_LUAINTERFACES); + const ::flatbuffers::Vector<::flatbuffers::Offset> *luaInterfaces() const { + return GetPointer> *>(VT_LUAINTERFACES); } - const flatbuffers::Vector> *nodeBindings() const { - return GetPointer> *>(VT_NODEBINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *nodeBindings() const { + return GetPointer> *>(VT_NODEBINDINGS); } - const flatbuffers::Vector> *appearanceBindings() const { - return GetPointer> *>(VT_APPEARANCEBINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *appearanceBindings() const { + return GetPointer> *>(VT_APPEARANCEBINDINGS); } - const flatbuffers::Vector> *cameraBindings() const { - return GetPointer> *>(VT_CAMERABINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *cameraBindings() const { + return GetPointer> *>(VT_CAMERABINDINGS); } - const flatbuffers::Vector> *dataArrays() const { - return GetPointer> *>(VT_DATAARRAYS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *dataArrays() const { + return GetPointer> *>(VT_DATAARRAYS); } - const flatbuffers::Vector> *animationNodes() const { - return GetPointer> *>(VT_ANIMATIONNODES); + const ::flatbuffers::Vector<::flatbuffers::Offset> *animationNodes() const { + return GetPointer> *>(VT_ANIMATIONNODES); } - const flatbuffers::Vector> *timerNodes() const { - return GetPointer> *>(VT_TIMERNODES); + const ::flatbuffers::Vector<::flatbuffers::Offset> *timerNodes() const { + return GetPointer> *>(VT_TIMERNODES); } - const flatbuffers::Vector> *links() const { - return GetPointer> *>(VT_LINKS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *links() const { + return GetPointer> *>(VT_LINKS); } - const flatbuffers::Vector> *renderPassBindings() const { - return GetPointer> *>(VT_RENDERPASSBINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *renderPassBindings() const { + return GetPointer> *>(VT_RENDERPASSBINDINGS); } - const flatbuffers::Vector> *anchorPoints() const { - return GetPointer> *>(VT_ANCHORPOINTS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *anchorPoints() const { + return GetPointer> *>(VT_ANCHORPOINTS); } - const flatbuffers::Vector> *renderGroupBindings() const { - return GetPointer> *>(VT_RENDERGROUPBINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *renderGroupBindings() const { + return GetPointer> *>(VT_RENDERGROUPBINDINGS); } - const flatbuffers::Vector> *skinBindings() const { - return GetPointer> *>(VT_SKINBINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *skinBindings() const { + return GetPointer> *>(VT_SKINBINDINGS); } - const flatbuffers::Vector> *meshNodeBindings() const { - return GetPointer> *>(VT_MESHNODEBINDINGS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *meshNodeBindings() const { + return GetPointer> *>(VT_MESHNODEBINDINGS); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_LUAMODULES) && verifier.VerifyVector(luaModules()) && @@ -154,82 +157,81 @@ struct ApiObjects FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct ApiObjectsBuilder { typedef ApiObjects Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_luaModules(flatbuffers::Offset>> luaModules) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_luaModules(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> luaModules) { fbb_.AddOffset(ApiObjects::VT_LUAMODULES, luaModules); } - void add_luaScripts(flatbuffers::Offset>> luaScripts) { + void add_luaScripts(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> luaScripts) { fbb_.AddOffset(ApiObjects::VT_LUASCRIPTS, luaScripts); } - void add_luaInterfaces(flatbuffers::Offset>> luaInterfaces) { + void add_luaInterfaces(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> luaInterfaces) { fbb_.AddOffset(ApiObjects::VT_LUAINTERFACES, luaInterfaces); } - void add_nodeBindings(flatbuffers::Offset>> nodeBindings) { + void add_nodeBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> nodeBindings) { fbb_.AddOffset(ApiObjects::VT_NODEBINDINGS, nodeBindings); } - void add_appearanceBindings(flatbuffers::Offset>> appearanceBindings) { + void add_appearanceBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> appearanceBindings) { fbb_.AddOffset(ApiObjects::VT_APPEARANCEBINDINGS, appearanceBindings); } - void add_cameraBindings(flatbuffers::Offset>> cameraBindings) { + void add_cameraBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> cameraBindings) { fbb_.AddOffset(ApiObjects::VT_CAMERABINDINGS, cameraBindings); } - void add_dataArrays(flatbuffers::Offset>> dataArrays) { + void add_dataArrays(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> dataArrays) { fbb_.AddOffset(ApiObjects::VT_DATAARRAYS, dataArrays); } - void add_animationNodes(flatbuffers::Offset>> animationNodes) { + void add_animationNodes(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> animationNodes) { fbb_.AddOffset(ApiObjects::VT_ANIMATIONNODES, animationNodes); } - void add_timerNodes(flatbuffers::Offset>> timerNodes) { + void add_timerNodes(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> timerNodes) { fbb_.AddOffset(ApiObjects::VT_TIMERNODES, timerNodes); } - void add_links(flatbuffers::Offset>> links) { + void add_links(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> links) { fbb_.AddOffset(ApiObjects::VT_LINKS, links); } - void add_renderPassBindings(flatbuffers::Offset>> renderPassBindings) { + void add_renderPassBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> renderPassBindings) { fbb_.AddOffset(ApiObjects::VT_RENDERPASSBINDINGS, renderPassBindings); } - void add_anchorPoints(flatbuffers::Offset>> anchorPoints) { + void add_anchorPoints(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> anchorPoints) { fbb_.AddOffset(ApiObjects::VT_ANCHORPOINTS, anchorPoints); } - void add_renderGroupBindings(flatbuffers::Offset>> renderGroupBindings) { + void add_renderGroupBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> renderGroupBindings) { fbb_.AddOffset(ApiObjects::VT_RENDERGROUPBINDINGS, renderGroupBindings); } - void add_skinBindings(flatbuffers::Offset>> skinBindings) { + void add_skinBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> skinBindings) { fbb_.AddOffset(ApiObjects::VT_SKINBINDINGS, skinBindings); } - void add_meshNodeBindings(flatbuffers::Offset>> meshNodeBindings) { + void add_meshNodeBindings(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> meshNodeBindings) { fbb_.AddOffset(ApiObjects::VT_MESHNODEBINDINGS, meshNodeBindings); } - explicit ApiObjectsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit ApiObjectsBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - ApiObjectsBuilder &operator=(const ApiObjectsBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateApiObjects( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset>> luaModules = 0, - flatbuffers::Offset>> luaScripts = 0, - flatbuffers::Offset>> luaInterfaces = 0, - flatbuffers::Offset>> nodeBindings = 0, - flatbuffers::Offset>> appearanceBindings = 0, - flatbuffers::Offset>> cameraBindings = 0, - flatbuffers::Offset>> dataArrays = 0, - flatbuffers::Offset>> animationNodes = 0, - flatbuffers::Offset>> timerNodes = 0, - flatbuffers::Offset>> links = 0, - flatbuffers::Offset>> renderPassBindings = 0, - flatbuffers::Offset>> anchorPoints = 0, - flatbuffers::Offset>> renderGroupBindings = 0, - flatbuffers::Offset>> skinBindings = 0, - flatbuffers::Offset>> meshNodeBindings = 0) { +inline ::flatbuffers::Offset CreateApiObjects( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> luaModules = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> luaScripts = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> luaInterfaces = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> nodeBindings = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> appearanceBindings = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> cameraBindings = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> dataArrays = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> animationNodes = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> timerNodes = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> links = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> renderPassBindings = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> anchorPoints = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> renderGroupBindings = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> skinBindings = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> meshNodeBindings = 0) { ApiObjectsBuilder builder_(_fbb); builder_.add_meshNodeBindings(meshNodeBindings); builder_.add_skinBindings(skinBindings); @@ -254,38 +256,38 @@ struct ApiObjects::Traits { static auto constexpr Create = CreateApiObjects; }; -inline flatbuffers::Offset CreateApiObjectsDirect( - flatbuffers::FlatBufferBuilder &_fbb, - const std::vector> *luaModules = nullptr, - const std::vector> *luaScripts = nullptr, - const std::vector> *luaInterfaces = nullptr, - const std::vector> *nodeBindings = nullptr, - const std::vector> *appearanceBindings = nullptr, - const std::vector> *cameraBindings = nullptr, - const std::vector> *dataArrays = nullptr, - const std::vector> *animationNodes = nullptr, - const std::vector> *timerNodes = nullptr, - const std::vector> *links = nullptr, - const std::vector> *renderPassBindings = nullptr, - const std::vector> *anchorPoints = nullptr, - const std::vector> *renderGroupBindings = nullptr, - const std::vector> *skinBindings = nullptr, - const std::vector> *meshNodeBindings = nullptr) { - auto luaModules__ = luaModules ? _fbb.CreateVector>(*luaModules) : 0; - auto luaScripts__ = luaScripts ? _fbb.CreateVector>(*luaScripts) : 0; - auto luaInterfaces__ = luaInterfaces ? _fbb.CreateVector>(*luaInterfaces) : 0; - auto nodeBindings__ = nodeBindings ? _fbb.CreateVector>(*nodeBindings) : 0; - auto appearanceBindings__ = appearanceBindings ? _fbb.CreateVector>(*appearanceBindings) : 0; - auto cameraBindings__ = cameraBindings ? _fbb.CreateVector>(*cameraBindings) : 0; - auto dataArrays__ = dataArrays ? _fbb.CreateVector>(*dataArrays) : 0; - auto animationNodes__ = animationNodes ? _fbb.CreateVector>(*animationNodes) : 0; - auto timerNodes__ = timerNodes ? _fbb.CreateVector>(*timerNodes) : 0; - auto links__ = links ? _fbb.CreateVector>(*links) : 0; - auto renderPassBindings__ = renderPassBindings ? _fbb.CreateVector>(*renderPassBindings) : 0; - auto anchorPoints__ = anchorPoints ? _fbb.CreateVector>(*anchorPoints) : 0; - auto renderGroupBindings__ = renderGroupBindings ? _fbb.CreateVector>(*renderGroupBindings) : 0; - auto skinBindings__ = skinBindings ? _fbb.CreateVector>(*skinBindings) : 0; - auto meshNodeBindings__ = meshNodeBindings ? _fbb.CreateVector>(*meshNodeBindings) : 0; +inline ::flatbuffers::Offset CreateApiObjectsDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + const std::vector<::flatbuffers::Offset> *luaModules = nullptr, + const std::vector<::flatbuffers::Offset> *luaScripts = nullptr, + const std::vector<::flatbuffers::Offset> *luaInterfaces = nullptr, + const std::vector<::flatbuffers::Offset> *nodeBindings = nullptr, + const std::vector<::flatbuffers::Offset> *appearanceBindings = nullptr, + const std::vector<::flatbuffers::Offset> *cameraBindings = nullptr, + const std::vector<::flatbuffers::Offset> *dataArrays = nullptr, + const std::vector<::flatbuffers::Offset> *animationNodes = nullptr, + const std::vector<::flatbuffers::Offset> *timerNodes = nullptr, + const std::vector<::flatbuffers::Offset> *links = nullptr, + const std::vector<::flatbuffers::Offset> *renderPassBindings = nullptr, + const std::vector<::flatbuffers::Offset> *anchorPoints = nullptr, + const std::vector<::flatbuffers::Offset> *renderGroupBindings = nullptr, + const std::vector<::flatbuffers::Offset> *skinBindings = nullptr, + const std::vector<::flatbuffers::Offset> *meshNodeBindings = nullptr) { + auto luaModules__ = luaModules ? _fbb.CreateVector<::flatbuffers::Offset>(*luaModules) : 0; + auto luaScripts__ = luaScripts ? _fbb.CreateVector<::flatbuffers::Offset>(*luaScripts) : 0; + auto luaInterfaces__ = luaInterfaces ? _fbb.CreateVector<::flatbuffers::Offset>(*luaInterfaces) : 0; + auto nodeBindings__ = nodeBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*nodeBindings) : 0; + auto appearanceBindings__ = appearanceBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*appearanceBindings) : 0; + auto cameraBindings__ = cameraBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*cameraBindings) : 0; + auto dataArrays__ = dataArrays ? _fbb.CreateVector<::flatbuffers::Offset>(*dataArrays) : 0; + auto animationNodes__ = animationNodes ? _fbb.CreateVector<::flatbuffers::Offset>(*animationNodes) : 0; + auto timerNodes__ = timerNodes ? _fbb.CreateVector<::flatbuffers::Offset>(*timerNodes) : 0; + auto links__ = links ? _fbb.CreateVector<::flatbuffers::Offset>(*links) : 0; + auto renderPassBindings__ = renderPassBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*renderPassBindings) : 0; + auto anchorPoints__ = anchorPoints ? _fbb.CreateVector<::flatbuffers::Offset>(*anchorPoints) : 0; + auto renderGroupBindings__ = renderGroupBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*renderGroupBindings) : 0; + auto skinBindings__ = skinBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*skinBindings) : 0; + auto meshNodeBindings__ = meshNodeBindings ? _fbb.CreateVector<::flatbuffers::Offset>(*meshNodeBindings) : 0; return rlogic_serialization::CreateApiObjects( _fbb, luaModules__, @@ -305,25 +307,25 @@ inline flatbuffers::Offset CreateApiObjectsDirect( meshNodeBindings__); } -inline const flatbuffers::TypeTable *ApiObjectsTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 1, 0 }, - { flatbuffers::ET_SEQUENCE, 1, 1 }, - { flatbuffers::ET_SEQUENCE, 1, 2 }, - { flatbuffers::ET_SEQUENCE, 1, 3 }, - { flatbuffers::ET_SEQUENCE, 1, 4 }, - { flatbuffers::ET_SEQUENCE, 1, 5 }, - { flatbuffers::ET_SEQUENCE, 1, 6 }, - { flatbuffers::ET_SEQUENCE, 1, 7 }, - { flatbuffers::ET_SEQUENCE, 1, 8 }, - { flatbuffers::ET_SEQUENCE, 1, 9 }, - { flatbuffers::ET_SEQUENCE, 1, 10 }, - { flatbuffers::ET_SEQUENCE, 1, 11 }, - { flatbuffers::ET_SEQUENCE, 1, 12 }, - { flatbuffers::ET_SEQUENCE, 1, 13 }, - { flatbuffers::ET_SEQUENCE, 1, 14 } +inline const ::flatbuffers::TypeTable *ApiObjectsTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 1, 0 }, + { ::flatbuffers::ET_SEQUENCE, 1, 1 }, + { ::flatbuffers::ET_SEQUENCE, 1, 2 }, + { ::flatbuffers::ET_SEQUENCE, 1, 3 }, + { ::flatbuffers::ET_SEQUENCE, 1, 4 }, + { ::flatbuffers::ET_SEQUENCE, 1, 5 }, + { ::flatbuffers::ET_SEQUENCE, 1, 6 }, + { ::flatbuffers::ET_SEQUENCE, 1, 7 }, + { ::flatbuffers::ET_SEQUENCE, 1, 8 }, + { ::flatbuffers::ET_SEQUENCE, 1, 9 }, + { ::flatbuffers::ET_SEQUENCE, 1, 10 }, + { ::flatbuffers::ET_SEQUENCE, 1, 11 }, + { ::flatbuffers::ET_SEQUENCE, 1, 12 }, + { ::flatbuffers::ET_SEQUENCE, 1, 13 }, + { ::flatbuffers::ET_SEQUENCE, 1, 14 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LuaModuleTypeTable, rlogic_serialization::LuaScriptTypeTable, rlogic_serialization::LuaInterfaceTypeTable, @@ -357,8 +359,8 @@ inline const flatbuffers::TypeTable *ApiObjectsTypeTable() { "skinBindings", "meshNodeBindings" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 15, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 15, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h b/src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h index 17ed55924..aae19091f 100644 --- a/src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/AppearanceBindingGen.h @@ -6,10 +6,14 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { @@ -18,9 +22,9 @@ struct ResourceId; struct AppearanceBinding; struct AppearanceBindingBuilder; -inline const flatbuffers::TypeTable *ResourceIdTypeTable(); +inline const ::flatbuffers::TypeTable *ResourceIdTypeTable(); -inline const flatbuffers::TypeTable *AppearanceBindingTypeTable(); +inline const ::flatbuffers::TypeTable *AppearanceBindingTypeTable(); FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ResourceId FLATBUFFERS_FINAL_CLASS { private: @@ -28,29 +32,35 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) ResourceId FLATBUFFERS_FINAL_CLASS { uint64_t resourceIdHigh_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return ResourceIdTypeTable(); } - ResourceId() { - memset(static_cast(this), 0, sizeof(ResourceId)); + ResourceId() + : resourceIdLow_(0), + resourceIdHigh_(0) { } ResourceId(uint64_t _resourceIdLow, uint64_t _resourceIdHigh) - : resourceIdLow_(flatbuffers::EndianScalar(_resourceIdLow)), - resourceIdHigh_(flatbuffers::EndianScalar(_resourceIdHigh)) { + : resourceIdLow_(::flatbuffers::EndianScalar(_resourceIdLow)), + resourceIdHigh_(::flatbuffers::EndianScalar(_resourceIdHigh)) { } uint64_t resourceIdLow() const { - return flatbuffers::EndianScalar(resourceIdLow_); + return ::flatbuffers::EndianScalar(resourceIdLow_); } uint64_t resourceIdHigh() const { - return flatbuffers::EndianScalar(resourceIdHigh_); + return ::flatbuffers::EndianScalar(resourceIdHigh_); } }; FLATBUFFERS_STRUCT_END(ResourceId, 16); -struct AppearanceBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct ResourceId::Traits { + using type = ResourceId; +}; + +struct AppearanceBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef AppearanceBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return AppearanceBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -63,41 +73,40 @@ struct AppearanceBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::ResourceId *parentEffectId() const { return GetStruct(VT_PARENTEFFECTID); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && - VerifyField(verifier, VT_PARENTEFFECTID) && + VerifyField(verifier, VT_PARENTEFFECTID, 8) && verifier.EndTable(); } }; struct AppearanceBindingBuilder { typedef AppearanceBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(AppearanceBinding::VT_BASE, base); } void add_parentEffectId(const rlogic_serialization::ResourceId *parentEffectId) { fbb_.AddStruct(AppearanceBinding::VT_PARENTEFFECTID, parentEffectId); } - explicit AppearanceBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit AppearanceBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - AppearanceBindingBuilder &operator=(const AppearanceBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateAppearanceBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - const rlogic_serialization::ResourceId *parentEffectId = 0) { +inline ::flatbuffers::Offset CreateAppearanceBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + const rlogic_serialization::ResourceId *parentEffectId = nullptr) { AppearanceBindingBuilder builder_(_fbb); builder_.add_parentEffectId(parentEffectId); builder_.add_base(base); @@ -109,28 +118,28 @@ struct AppearanceBinding::Traits { static auto constexpr Create = CreateAppearanceBinding; }; -inline const flatbuffers::TypeTable *ResourceIdTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_ULONG, 0, -1 } +inline const ::flatbuffers::TypeTable *ResourceIdTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 } }; static const int64_t values[] = { 0, 8, 16 }; static const char * const names[] = { "resourceIdLow", "resourceIdHigh" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 2, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 2, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *AppearanceBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 } +inline const ::flatbuffers::TypeTable *AppearanceBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesBindingTypeTable, rlogic_serialization::ResourceIdTypeTable }; @@ -138,8 +147,8 @@ inline const flatbuffers::TypeTable *AppearanceBindingTypeTable() { "base", "parentEffectId" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h b/src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h index e8f5270cd..26ff7844d 100644 --- a/src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/CameraBindingGen.h @@ -6,22 +6,26 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { struct CameraBinding; struct CameraBindingBuilder; -inline const flatbuffers::TypeTable *CameraBindingTypeTable(); +inline const ::flatbuffers::TypeTable *CameraBindingTypeTable(); -struct CameraBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct CameraBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef CameraBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return CameraBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -30,7 +34,7 @@ struct CameraBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::RamsesBinding *base() const { return GetPointer(VT_BASE); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -40,26 +44,25 @@ struct CameraBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct CameraBindingBuilder { typedef CameraBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(CameraBinding::VT_BASE, base); } - explicit CameraBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit CameraBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - CameraBindingBuilder &operator=(const CameraBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateCameraBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0) { +inline ::flatbuffers::Offset CreateCameraBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0) { CameraBindingBuilder builder_(_fbb); builder_.add_base(base); return builder_.Finish(); @@ -70,18 +73,18 @@ struct CameraBinding::Traits { static auto constexpr Create = CreateCameraBinding; }; -inline const flatbuffers::TypeTable *CameraBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 } +inline const ::flatbuffers::TypeTable *CameraBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesBindingTypeTable }; static const char * const names[] = { "base" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/DataArrayGen.h b/src/client/internal/logic/flatbuffers/generated/DataArrayGen.h index 1cc2f040f..01b3479fc 100644 --- a/src/client/internal/logic/flatbuffers/generated/DataArrayGen.h +++ b/src/client/internal/logic/flatbuffers/generated/DataArrayGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" namespace rlogic_serialization { @@ -19,11 +26,11 @@ struct intArrBuilder; struct DataArray; struct DataArrayBuilder; -inline const flatbuffers::TypeTable *floatArrTypeTable(); +inline const ::flatbuffers::TypeTable *floatArrTypeTable(); -inline const flatbuffers::TypeTable *intArrTypeTable(); +inline const ::flatbuffers::TypeTable *intArrTypeTable(); -inline const flatbuffers::TypeTable *DataArrayTypeTable(); +inline const ::flatbuffers::TypeTable *DataArrayTypeTable(); enum class EDataArrayType : uint8_t { Float = 0, @@ -71,7 +78,7 @@ inline const char * const *EnumNamesEDataArrayType() { } inline const char *EnumNameEDataArrayType(EDataArrayType e) { - if (flatbuffers::IsOutRange(e, EDataArrayType::Float, EDataArrayType::FloatArray)) return ""; + if (::flatbuffers::IsOutRange(e, EDataArrayType::Float, EDataArrayType::FloatArray)) return ""; const size_t index = static_cast(e); return EnumNamesEDataArrayType()[index]; } @@ -104,7 +111,7 @@ inline const char * const *EnumNamesArrayUnion() { } inline const char *EnumNameArrayUnion(ArrayUnion e) { - if (flatbuffers::IsOutRange(e, ArrayUnion::NONE, ArrayUnion::intArr)) return ""; + if (::flatbuffers::IsOutRange(e, ArrayUnion::NONE, ArrayUnion::intArr)) return ""; const size_t index = static_cast(e); return EnumNamesArrayUnion()[index]; } @@ -121,22 +128,22 @@ template<> struct ArrayUnionTraits { static const ArrayUnion enum_value = ArrayUnion::intArr; }; -bool VerifyArrayUnion(flatbuffers::Verifier &verifier, const void *obj, ArrayUnion type); -bool VerifyArrayUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types); +bool VerifyArrayUnion(::flatbuffers::Verifier &verifier, const void *obj, ArrayUnion type); +bool VerifyArrayUnionVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); -struct floatArr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct floatArr FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef floatArrBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return floatArrTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_DATA = 4 }; - const flatbuffers::Vector *data() const { - return GetPointer *>(VT_DATA); + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_DATA) && verifier.VerifyVector(data()) && @@ -146,26 +153,25 @@ struct floatArr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct floatArrBuilder { typedef floatArr Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_data(flatbuffers::Offset> data) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { fbb_.AddOffset(floatArr::VT_DATA, data); } - explicit floatArrBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit floatArrBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - floatArrBuilder &operator=(const floatArrBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreatefloatArr( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset> data = 0) { +inline ::flatbuffers::Offset CreatefloatArr( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { floatArrBuilder builder_(_fbb); builder_.add_data(data); return builder_.Finish(); @@ -176,8 +182,8 @@ struct floatArr::Traits { static auto constexpr Create = CreatefloatArr; }; -inline flatbuffers::Offset CreatefloatArrDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreatefloatArrDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const std::vector *data = nullptr) { auto data__ = data ? _fbb.CreateVector(*data) : 0; return rlogic_serialization::CreatefloatArr( @@ -185,19 +191,19 @@ inline flatbuffers::Offset CreatefloatArrDirect( data__); } -struct intArr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct intArr FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef intArrBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return intArrTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_DATA = 4 }; - const flatbuffers::Vector *data() const { - return GetPointer *>(VT_DATA); + const ::flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_DATA) && verifier.VerifyVector(data()) && @@ -207,26 +213,25 @@ struct intArr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct intArrBuilder { typedef intArr Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_data(flatbuffers::Offset> data) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_data(::flatbuffers::Offset<::flatbuffers::Vector> data) { fbb_.AddOffset(intArr::VT_DATA, data); } - explicit intArrBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit intArrBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - intArrBuilder &operator=(const intArrBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateintArr( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset> data = 0) { +inline ::flatbuffers::Offset CreateintArr( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::Vector> data = 0) { intArrBuilder builder_(_fbb); builder_.add_data(data); return builder_.Finish(); @@ -237,8 +242,8 @@ struct intArr::Traits { static auto constexpr Create = CreateintArr; }; -inline flatbuffers::Offset CreateintArrDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreateintArrDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const std::vector *data = nullptr) { auto data__ = data ? _fbb.CreateVector(*data) : 0; return rlogic_serialization::CreateintArr( @@ -246,10 +251,10 @@ inline flatbuffers::Offset CreateintArrDirect( data__); } -struct DataArray FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct DataArray FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef DataArrayBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return DataArrayTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -281,15 +286,15 @@ struct DataArray FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { uint32_t numElements() const { return GetField(VT_NUMELEMENTS, 0); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && - VerifyField(verifier, VT_TYPE) && - VerifyField(verifier, VT_DATA_TYPE) && + VerifyField(verifier, VT_TYPE, 1) && + VerifyField(verifier, VT_DATA_TYPE, 1) && VerifyOffset(verifier, VT_DATA) && VerifyArrayUnion(verifier, data(), data_type()) && - VerifyField(verifier, VT_NUMELEMENTS) && + VerifyField(verifier, VT_NUMELEMENTS, 4) && verifier.EndTable(); } }; @@ -304,9 +309,9 @@ template<> inline const rlogic_serialization::intArr *DataArray::data_as base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(DataArray::VT_BASE, base); } void add_type(rlogic_serialization::EDataArrayType type) { @@ -315,30 +320,29 @@ struct DataArrayBuilder { void add_data_type(rlogic_serialization::ArrayUnion data_type) { fbb_.AddElement(DataArray::VT_DATA_TYPE, static_cast(data_type), 0); } - void add_data(flatbuffers::Offset data) { + void add_data(::flatbuffers::Offset data) { fbb_.AddOffset(DataArray::VT_DATA, data); } void add_numElements(uint32_t numElements) { fbb_.AddElement(DataArray::VT_NUMELEMENTS, numElements, 0); } - explicit DataArrayBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit DataArrayBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - DataArrayBuilder &operator=(const DataArrayBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateDataArray( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, +inline ::flatbuffers::Offset CreateDataArray( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, rlogic_serialization::EDataArrayType type = rlogic_serialization::EDataArrayType::Float, rlogic_serialization::ArrayUnion data_type = rlogic_serialization::ArrayUnion::NONE, - flatbuffers::Offset data = 0, + ::flatbuffers::Offset data = 0, uint32_t numElements = 0) { DataArrayBuilder builder_(_fbb); builder_.add_numElements(numElements); @@ -354,7 +358,7 @@ struct DataArray::Traits { static auto constexpr Create = CreateDataArray; }; -inline bool VerifyArrayUnion(flatbuffers::Verifier &verifier, const void *obj, ArrayUnion type) { +inline bool VerifyArrayUnion(::flatbuffers::Verifier &verifier, const void *obj, ArrayUnion type) { switch (type) { case ArrayUnion::NONE: { return true; @@ -371,10 +375,10 @@ inline bool VerifyArrayUnion(flatbuffers::Verifier &verifier, const void *obj, A } } -inline bool VerifyArrayUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types) { +inline bool VerifyArrayUnionVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { if (!values || !types) return !values && !types; if (values->size() != types->size()) return false; - for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { if (!VerifyArrayUnion( verifier, values->Get(i), types->GetEnum(i))) { return false; @@ -383,19 +387,19 @@ inline bool VerifyArrayUnionVector(flatbuffers::Verifier &verifier, const flatbu return true; } -inline const flatbuffers::TypeTable *EDataArrayTypeTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 } +inline const ::flatbuffers::TypeTable *EDataArrayTypeTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::EDataArrayTypeTypeTable }; static const char * const names[] = { @@ -409,19 +413,19 @@ inline const flatbuffers::TypeTable *EDataArrayTypeTypeTable() { "Vec4i", "FloatArray" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_ENUM, 9, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_ENUM, 9, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *ArrayUnionTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 } +inline const ::flatbuffers::TypeTable *ArrayUnionTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::floatArrTypeTable, rlogic_serialization::intArrTypeTable }; @@ -430,47 +434,47 @@ inline const flatbuffers::TypeTable *ArrayUnionTypeTable() { "floatArr", "intArr" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_UNION, 3, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_UNION, 3, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *floatArrTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_FLOAT, 1, -1 } +inline const ::flatbuffers::TypeTable *floatArrTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_FLOAT, 1, -1 } }; static const char * const names[] = { "data" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *intArrTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_INT, 1, -1 } +inline const ::flatbuffers::TypeTable *intArrTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 1, -1 } }; static const char * const names[] = { "data" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *DataArrayTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 1 }, - { flatbuffers::ET_UTYPE, 0, 2 }, - { flatbuffers::ET_SEQUENCE, 0, 2 }, - { flatbuffers::ET_UINT, 0, -1 } +inline const ::flatbuffers::TypeTable *DataArrayTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 1 }, + { ::flatbuffers::ET_UTYPE, 0, 2 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 }, + { ::flatbuffers::ET_UINT, 0, -1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::EDataArrayTypeTypeTable, rlogic_serialization::ArrayUnionTypeTable @@ -482,8 +486,8 @@ inline const flatbuffers::TypeTable *DataArrayTypeTable() { "data", "numElements" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/LinkGen.h b/src/client/internal/logic/flatbuffers/generated/LinkGen.h index f7abfc902..12e321f87 100644 --- a/src/client/internal/logic/flatbuffers/generated/LinkGen.h +++ b/src/client/internal/logic/flatbuffers/generated/LinkGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "PropertyGen.h" namespace rlogic_serialization { @@ -13,12 +20,12 @@ namespace rlogic_serialization { struct Link; struct LinkBuilder; -inline const flatbuffers::TypeTable *LinkTypeTable(); +inline const ::flatbuffers::TypeTable *LinkTypeTable(); -struct Link FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct Link FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LinkBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LinkTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -35,46 +42,45 @@ struct Link FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool isWeak() const { return GetField(VT_ISWEAK, 0) != 0; } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_SOURCEPROPERTY) && verifier.VerifyTable(sourceProperty()) && VerifyOffset(verifier, VT_TARGETPROPERTY) && verifier.VerifyTable(targetProperty()) && - VerifyField(verifier, VT_ISWEAK) && + VerifyField(verifier, VT_ISWEAK, 1) && verifier.EndTable(); } }; struct LinkBuilder { typedef Link Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_sourceProperty(flatbuffers::Offset sourceProperty) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_sourceProperty(::flatbuffers::Offset sourceProperty) { fbb_.AddOffset(Link::VT_SOURCEPROPERTY, sourceProperty); } - void add_targetProperty(flatbuffers::Offset targetProperty) { + void add_targetProperty(::flatbuffers::Offset targetProperty) { fbb_.AddOffset(Link::VT_TARGETPROPERTY, targetProperty); } void add_isWeak(bool isWeak) { fbb_.AddElement(Link::VT_ISWEAK, static_cast(isWeak), 0); } - explicit LinkBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LinkBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LinkBuilder &operator=(const LinkBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLink( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset sourceProperty = 0, - flatbuffers::Offset targetProperty = 0, +inline ::flatbuffers::Offset CreateLink( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset sourceProperty = 0, + ::flatbuffers::Offset targetProperty = 0, bool isWeak = false) { LinkBuilder builder_(_fbb); builder_.add_targetProperty(targetProperty); @@ -88,13 +94,13 @@ struct Link::Traits { static auto constexpr Create = CreateLink; }; -inline const flatbuffers::TypeTable *LinkTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_BOOL, 0, -1 } +inline const ::flatbuffers::TypeTable *LinkTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_BOOL, 0, -1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::PropertyTypeTable }; static const char * const names[] = { @@ -102,8 +108,8 @@ inline const flatbuffers::TypeTable *LinkTypeTable() { "targetProperty", "isWeak" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h b/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h index 1dbd21f45..2d0fa9eb0 100644 --- a/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h +++ b/src/client/internal/logic/flatbuffers/generated/LogicEngineGen.h @@ -6,38 +6,26 @@ #include "flatbuffers/flatbuffers.h" -#include "AnchorPointGen.h" -#include "AnimationNodeGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "ApiObjectsGen.h" -#include "AppearanceBindingGen.h" -#include "CameraBindingGen.h" -#include "DataArrayGen.h" -#include "LinkGen.h" -#include "LogicObjectGen.h" -#include "LuaInterfaceGen.h" -#include "LuaModuleGen.h" -#include "LuaScriptGen.h" -#include "MeshNodeBindingGen.h" -#include "NodeBindingGen.h" -#include "PropertyGen.h" -#include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" -#include "RenderGroupBindingGen.h" -#include "RenderPassBindingGen.h" -#include "SkinBindingGen.h" -#include "TimerNodeGen.h" namespace rlogic_serialization { struct LogicEngine; struct LogicEngineBuilder; -inline const flatbuffers::TypeTable *LogicEngineTypeTable(); +inline const ::flatbuffers::TypeTable *LogicEngineTypeTable(); -struct LogicEngine FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct LogicEngine FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LogicEngineBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LogicEngineTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -46,7 +34,7 @@ struct LogicEngine FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::ApiObjects *apiObjects() const { return GetPointer(VT_APIOBJECTS); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_APIOBJECTS) && verifier.VerifyTable(apiObjects()) && @@ -56,26 +44,25 @@ struct LogicEngine FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct LogicEngineBuilder { typedef LogicEngine Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_apiObjects(flatbuffers::Offset apiObjects) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_apiObjects(::flatbuffers::Offset apiObjects) { fbb_.AddOffset(LogicEngine::VT_APIOBJECTS, apiObjects); } - explicit LogicEngineBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LogicEngineBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LogicEngineBuilder &operator=(const LogicEngineBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLogicEngine( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset apiObjects = 0) { +inline ::flatbuffers::Offset CreateLogicEngine( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset apiObjects = 0) { LogicEngineBuilder builder_(_fbb); builder_.add_apiObjects(apiObjects); return builder_.Finish(); @@ -86,49 +73,49 @@ struct LogicEngine::Traits { static auto constexpr Create = CreateLogicEngine; }; -inline const flatbuffers::TypeTable *LogicEngineTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 } +inline const ::flatbuffers::TypeTable *LogicEngineTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::ApiObjectsTypeTable }; static const char * const names[] = { "apiObjects" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } inline const rlogic_serialization::LogicEngine *GetLogicEngine(const void *buf) { - return flatbuffers::GetRoot(buf); + return ::flatbuffers::GetRoot(buf); } inline const rlogic_serialization::LogicEngine *GetSizePrefixedLogicEngine(const void *buf) { - return flatbuffers::GetSizePrefixedRoot(buf); + return ::flatbuffers::GetSizePrefixedRoot(buf); } inline bool VerifyLogicEngineBuffer( - flatbuffers::Verifier &verifier) { + ::flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(nullptr); } inline bool VerifySizePrefixedLogicEngineBuffer( - flatbuffers::Verifier &verifier) { + ::flatbuffers::Verifier &verifier) { return verifier.VerifySizePrefixedBuffer(nullptr); } inline void FinishLogicEngineBuffer( - flatbuffers::FlatBufferBuilder &fbb, - flatbuffers::Offset root) { + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { fbb.Finish(root); } inline void FinishSizePrefixedLogicEngineBuffer( - flatbuffers::FlatBufferBuilder &fbb, - flatbuffers::Offset root) { + ::flatbuffers::FlatBufferBuilder &fbb, + ::flatbuffers::Offset root) { fbb.FinishSizePrefixed(root); } diff --git a/src/client/internal/logic/flatbuffers/generated/LogicObjectGen.h b/src/client/internal/logic/flatbuffers/generated/LogicObjectGen.h index 4e5e444de..5c3f32551 100644 --- a/src/client/internal/logic/flatbuffers/generated/LogicObjectGen.h +++ b/src/client/internal/logic/flatbuffers/generated/LogicObjectGen.h @@ -6,17 +6,24 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + namespace rlogic_serialization { struct LogicObject; struct LogicObjectBuilder; -inline const flatbuffers::TypeTable *LogicObjectTypeTable(); +inline const ::flatbuffers::TypeTable *LogicObjectTypeTable(); -struct LogicObject FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct LogicObject FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LogicObjectBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LogicObjectTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -25,8 +32,8 @@ struct LogicObject FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_USERIDHIGH = 8, VT_USERIDLOW = 10 }; - const flatbuffers::String *name() const { - return GetPointer(VT_NAME); + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); } uint64_t id() const { return GetField(VT_ID, 0); @@ -37,22 +44,22 @@ struct LogicObject FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { uint64_t userIdLow() const { return GetField(VT_USERIDLOW, 0); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && verifier.VerifyString(name()) && - VerifyField(verifier, VT_ID) && - VerifyField(verifier, VT_USERIDHIGH) && - VerifyField(verifier, VT_USERIDLOW) && + VerifyField(verifier, VT_ID, 8) && + VerifyField(verifier, VT_USERIDHIGH, 8) && + VerifyField(verifier, VT_USERIDLOW, 8) && verifier.EndTable(); } }; struct LogicObjectBuilder { typedef LogicObject Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_name(flatbuffers::Offset name) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { fbb_.AddOffset(LogicObject::VT_NAME, name); } void add_id(uint64_t id) { @@ -64,21 +71,20 @@ struct LogicObjectBuilder { void add_userIdLow(uint64_t userIdLow) { fbb_.AddElement(LogicObject::VT_USERIDLOW, userIdLow, 0); } - explicit LogicObjectBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LogicObjectBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LogicObjectBuilder &operator=(const LogicObjectBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLogicObject( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset name = 0, +inline ::flatbuffers::Offset CreateLogicObject( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, uint64_t id = 0, uint64_t userIdHigh = 0, uint64_t userIdLow = 0) { @@ -95,8 +101,8 @@ struct LogicObject::Traits { static auto constexpr Create = CreateLogicObject; }; -inline flatbuffers::Offset CreateLogicObjectDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreateLogicObjectDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, uint64_t id = 0, uint64_t userIdHigh = 0, @@ -110,12 +116,12 @@ inline flatbuffers::Offset CreateLogicObjectDirect( userIdLow); } -inline const flatbuffers::TypeTable *LogicObjectTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_ULONG, 0, -1 } +inline const ::flatbuffers::TypeTable *LogicObjectTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 } }; static const char * const names[] = { "name", @@ -123,8 +129,8 @@ inline const flatbuffers::TypeTable *LogicObjectTypeTable() { "userIdHigh", "userIdLow" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 4, type_codes, nullptr, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 4, type_codes, nullptr, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/LuaInterfaceGen.h b/src/client/internal/logic/flatbuffers/generated/LuaInterfaceGen.h index a92e564bb..1ee662abb 100644 --- a/src/client/internal/logic/flatbuffers/generated/LuaInterfaceGen.h +++ b/src/client/internal/logic/flatbuffers/generated/LuaInterfaceGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" #include "PropertyGen.h" @@ -14,12 +21,12 @@ namespace rlogic_serialization { struct LuaInterface; struct LuaInterfaceBuilder; -inline const flatbuffers::TypeTable *LuaInterfaceTypeTable(); +inline const ::flatbuffers::TypeTable *LuaInterfaceTypeTable(); -struct LuaInterface FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct LuaInterface FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LuaInterfaceBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LuaInterfaceTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -32,7 +39,7 @@ struct LuaInterface FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::Property *rootProperty() const { return GetPointer(VT_ROOTPROPERTY); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -44,30 +51,29 @@ struct LuaInterface FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct LuaInterfaceBuilder { typedef LuaInterface Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(LuaInterface::VT_BASE, base); } - void add_rootProperty(flatbuffers::Offset rootProperty) { + void add_rootProperty(::flatbuffers::Offset rootProperty) { fbb_.AddOffset(LuaInterface::VT_ROOTPROPERTY, rootProperty); } - explicit LuaInterfaceBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LuaInterfaceBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LuaInterfaceBuilder &operator=(const LuaInterfaceBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLuaInterface( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset rootProperty = 0) { +inline ::flatbuffers::Offset CreateLuaInterface( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset rootProperty = 0) { LuaInterfaceBuilder builder_(_fbb); builder_.add_rootProperty(rootProperty); builder_.add_base(base); @@ -79,12 +85,12 @@ struct LuaInterface::Traits { static auto constexpr Create = CreateLuaInterface; }; -inline const flatbuffers::TypeTable *LuaInterfaceTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 } +inline const ::flatbuffers::TypeTable *LuaInterfaceTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::PropertyTypeTable }; @@ -92,8 +98,8 @@ inline const flatbuffers::TypeTable *LuaInterfaceTypeTable() { "base", "rootProperty" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/LuaModuleGen.h b/src/client/internal/logic/flatbuffers/generated/LuaModuleGen.h index 3fc6ac8dc..4509a3d18 100644 --- a/src/client/internal/logic/flatbuffers/generated/LuaModuleGen.h +++ b/src/client/internal/logic/flatbuffers/generated/LuaModuleGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" namespace rlogic_serialization { @@ -16,14 +23,14 @@ struct LuaModuleBuilder; struct LuaModuleUsage; struct LuaModuleUsageBuilder; -inline const flatbuffers::TypeTable *LuaModuleTypeTable(); +inline const ::flatbuffers::TypeTable *LuaModuleTypeTable(); -inline const flatbuffers::TypeTable *LuaModuleUsageTypeTable(); +inline const ::flatbuffers::TypeTable *LuaModuleUsageTypeTable(); -struct LuaModule FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct LuaModule FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LuaModuleBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LuaModuleTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -36,19 +43,19 @@ struct LuaModule FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::LogicObject *base() const { return GetPointer(VT_BASE); } - const flatbuffers::String *source() const { - return GetPointer(VT_SOURCE); + const ::flatbuffers::String *source() const { + return GetPointer(VT_SOURCE); } - const flatbuffers::Vector> *dependencies() const { - return GetPointer> *>(VT_DEPENDENCIES); + const ::flatbuffers::Vector<::flatbuffers::Offset> *dependencies() const { + return GetPointer> *>(VT_DEPENDENCIES); } - const flatbuffers::Vector *standardModules() const { - return GetPointer *>(VT_STANDARDMODULES); + const ::flatbuffers::Vector *standardModules() const { + return GetPointer *>(VT_STANDARDMODULES); } - const flatbuffers::Vector *luaByteCode() const { - return GetPointer *>(VT_LUABYTECODE); + const ::flatbuffers::Vector *luaByteCode() const { + return GetPointer *>(VT_LUABYTECODE); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -67,42 +74,41 @@ struct LuaModule FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct LuaModuleBuilder { typedef LuaModule Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(LuaModule::VT_BASE, base); } - void add_source(flatbuffers::Offset source) { + void add_source(::flatbuffers::Offset<::flatbuffers::String> source) { fbb_.AddOffset(LuaModule::VT_SOURCE, source); } - void add_dependencies(flatbuffers::Offset>> dependencies) { + void add_dependencies(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> dependencies) { fbb_.AddOffset(LuaModule::VT_DEPENDENCIES, dependencies); } - void add_standardModules(flatbuffers::Offset> standardModules) { + void add_standardModules(::flatbuffers::Offset<::flatbuffers::Vector> standardModules) { fbb_.AddOffset(LuaModule::VT_STANDARDMODULES, standardModules); } - void add_luaByteCode(flatbuffers::Offset> luaByteCode) { + void add_luaByteCode(::flatbuffers::Offset<::flatbuffers::Vector> luaByteCode) { fbb_.AddOffset(LuaModule::VT_LUABYTECODE, luaByteCode); } - explicit LuaModuleBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LuaModuleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LuaModuleBuilder &operator=(const LuaModuleBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLuaModule( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset source = 0, - flatbuffers::Offset>> dependencies = 0, - flatbuffers::Offset> standardModules = 0, - flatbuffers::Offset> luaByteCode = 0) { +inline ::flatbuffers::Offset CreateLuaModule( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset<::flatbuffers::String> source = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> dependencies = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> standardModules = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> luaByteCode = 0) { LuaModuleBuilder builder_(_fbb); builder_.add_luaByteCode(luaByteCode); builder_.add_standardModules(standardModules); @@ -117,15 +123,15 @@ struct LuaModule::Traits { static auto constexpr Create = CreateLuaModule; }; -inline flatbuffers::Offset CreateLuaModuleDirect( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, +inline ::flatbuffers::Offset CreateLuaModuleDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, const char *source = nullptr, - const std::vector> *dependencies = nullptr, + const std::vector<::flatbuffers::Offset> *dependencies = nullptr, const std::vector *standardModules = nullptr, const std::vector *luaByteCode = nullptr) { auto source__ = source ? _fbb.CreateString(source) : 0; - auto dependencies__ = dependencies ? _fbb.CreateVector>(*dependencies) : 0; + auto dependencies__ = dependencies ? _fbb.CreateVector<::flatbuffers::Offset>(*dependencies) : 0; auto standardModules__ = standardModules ? _fbb.CreateVector(*standardModules) : 0; auto luaByteCode__ = luaByteCode ? _fbb.CreateVector(*luaByteCode) : 0; return rlogic_serialization::CreateLuaModule( @@ -137,56 +143,55 @@ inline flatbuffers::Offset CreateLuaModuleDirect( luaByteCode__); } -struct LuaModuleUsage FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct LuaModuleUsage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LuaModuleUsageBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LuaModuleUsageTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_NAME = 4, VT_MODULEID = 6 }; - const flatbuffers::String *name() const { - return GetPointer(VT_NAME); + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); } uint64_t moduleId() const { return GetField(VT_MODULEID, 0); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && verifier.VerifyString(name()) && - VerifyField(verifier, VT_MODULEID) && + VerifyField(verifier, VT_MODULEID, 8) && verifier.EndTable(); } }; struct LuaModuleUsageBuilder { typedef LuaModuleUsage Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_name(flatbuffers::Offset name) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { fbb_.AddOffset(LuaModuleUsage::VT_NAME, name); } void add_moduleId(uint64_t moduleId) { fbb_.AddElement(LuaModuleUsage::VT_MODULEID, moduleId, 0); } - explicit LuaModuleUsageBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LuaModuleUsageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LuaModuleUsageBuilder &operator=(const LuaModuleUsageBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLuaModuleUsage( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset name = 0, +inline ::flatbuffers::Offset CreateLuaModuleUsage( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, uint64_t moduleId = 0) { LuaModuleUsageBuilder builder_(_fbb); builder_.add_moduleId(moduleId); @@ -199,8 +204,8 @@ struct LuaModuleUsage::Traits { static auto constexpr Create = CreateLuaModuleUsage; }; -inline flatbuffers::Offset CreateLuaModuleUsageDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreateLuaModuleUsageDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, uint64_t moduleId = 0) { auto name__ = name ? _fbb.CreateString(name) : 0; @@ -210,15 +215,15 @@ inline flatbuffers::Offset CreateLuaModuleUsageDirect( moduleId); } -inline const flatbuffers::TypeTable *LuaModuleTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 1, 1 }, - { flatbuffers::ET_UCHAR, 1, -1 }, - { flatbuffers::ET_UCHAR, 1, -1 } +inline const ::flatbuffers::TypeTable *LuaModuleTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 1, 1 }, + { ::flatbuffers::ET_UCHAR, 1, -1 }, + { ::flatbuffers::ET_UCHAR, 1, -1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::LuaModuleUsageTypeTable }; @@ -229,23 +234,23 @@ inline const flatbuffers::TypeTable *LuaModuleTypeTable() { "standardModules", "luaByteCode" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *LuaModuleUsageTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_ULONG, 0, -1 } +inline const ::flatbuffers::TypeTable *LuaModuleUsageTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 } }; static const char * const names[] = { "name", "moduleId" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, nullptr, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, nullptr, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/LuaScriptGen.h b/src/client/internal/logic/flatbuffers/generated/LuaScriptGen.h index ac6cc358f..40536ea33 100644 --- a/src/client/internal/logic/flatbuffers/generated/LuaScriptGen.h +++ b/src/client/internal/logic/flatbuffers/generated/LuaScriptGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" #include "LuaModuleGen.h" #include "PropertyGen.h" @@ -15,12 +22,12 @@ namespace rlogic_serialization { struct LuaScript; struct LuaScriptBuilder; -inline const flatbuffers::TypeTable *LuaScriptTypeTable(); +inline const ::flatbuffers::TypeTable *LuaScriptTypeTable(); -struct LuaScript FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct LuaScript FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef LuaScriptBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return LuaScriptTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -35,14 +42,14 @@ struct LuaScript FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::LogicObject *base() const { return GetPointer(VT_BASE); } - const flatbuffers::String *luaSourceCode() const { - return GetPointer(VT_LUASOURCECODE); + const ::flatbuffers::String *luaSourceCode() const { + return GetPointer(VT_LUASOURCECODE); } - const flatbuffers::Vector> *userModules() const { - return GetPointer> *>(VT_USERMODULES); + const ::flatbuffers::Vector<::flatbuffers::Offset> *userModules() const { + return GetPointer> *>(VT_USERMODULES); } - const flatbuffers::Vector *standardModules() const { - return GetPointer *>(VT_STANDARDMODULES); + const ::flatbuffers::Vector *standardModules() const { + return GetPointer *>(VT_STANDARDMODULES); } const rlogic_serialization::Property *rootInput() const { return GetPointer(VT_ROOTINPUT); @@ -50,10 +57,10 @@ struct LuaScript FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::Property *rootOutput() const { return GetPointer(VT_ROOTOUTPUT); } - const flatbuffers::Vector *luaByteCode() const { - return GetPointer *>(VT_LUABYTECODE); + const ::flatbuffers::Vector *luaByteCode() const { + return GetPointer *>(VT_LUABYTECODE); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -76,50 +83,49 @@ struct LuaScript FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct LuaScriptBuilder { typedef LuaScript Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(LuaScript::VT_BASE, base); } - void add_luaSourceCode(flatbuffers::Offset luaSourceCode) { + void add_luaSourceCode(::flatbuffers::Offset<::flatbuffers::String> luaSourceCode) { fbb_.AddOffset(LuaScript::VT_LUASOURCECODE, luaSourceCode); } - void add_userModules(flatbuffers::Offset>> userModules) { + void add_userModules(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> userModules) { fbb_.AddOffset(LuaScript::VT_USERMODULES, userModules); } - void add_standardModules(flatbuffers::Offset> standardModules) { + void add_standardModules(::flatbuffers::Offset<::flatbuffers::Vector> standardModules) { fbb_.AddOffset(LuaScript::VT_STANDARDMODULES, standardModules); } - void add_rootInput(flatbuffers::Offset rootInput) { + void add_rootInput(::flatbuffers::Offset rootInput) { fbb_.AddOffset(LuaScript::VT_ROOTINPUT, rootInput); } - void add_rootOutput(flatbuffers::Offset rootOutput) { + void add_rootOutput(::flatbuffers::Offset rootOutput) { fbb_.AddOffset(LuaScript::VT_ROOTOUTPUT, rootOutput); } - void add_luaByteCode(flatbuffers::Offset> luaByteCode) { + void add_luaByteCode(::flatbuffers::Offset<::flatbuffers::Vector> luaByteCode) { fbb_.AddOffset(LuaScript::VT_LUABYTECODE, luaByteCode); } - explicit LuaScriptBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit LuaScriptBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - LuaScriptBuilder &operator=(const LuaScriptBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateLuaScript( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset luaSourceCode = 0, - flatbuffers::Offset>> userModules = 0, - flatbuffers::Offset> standardModules = 0, - flatbuffers::Offset rootInput = 0, - flatbuffers::Offset rootOutput = 0, - flatbuffers::Offset> luaByteCode = 0) { +inline ::flatbuffers::Offset CreateLuaScript( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset<::flatbuffers::String> luaSourceCode = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> userModules = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> standardModules = 0, + ::flatbuffers::Offset rootInput = 0, + ::flatbuffers::Offset rootOutput = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> luaByteCode = 0) { LuaScriptBuilder builder_(_fbb); builder_.add_luaByteCode(luaByteCode); builder_.add_rootOutput(rootOutput); @@ -136,17 +142,17 @@ struct LuaScript::Traits { static auto constexpr Create = CreateLuaScript; }; -inline flatbuffers::Offset CreateLuaScriptDirect( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, +inline ::flatbuffers::Offset CreateLuaScriptDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, const char *luaSourceCode = nullptr, - const std::vector> *userModules = nullptr, + const std::vector<::flatbuffers::Offset> *userModules = nullptr, const std::vector *standardModules = nullptr, - flatbuffers::Offset rootInput = 0, - flatbuffers::Offset rootOutput = 0, + ::flatbuffers::Offset rootInput = 0, + ::flatbuffers::Offset rootOutput = 0, const std::vector *luaByteCode = nullptr) { auto luaSourceCode__ = luaSourceCode ? _fbb.CreateString(luaSourceCode) : 0; - auto userModules__ = userModules ? _fbb.CreateVector>(*userModules) : 0; + auto userModules__ = userModules ? _fbb.CreateVector<::flatbuffers::Offset>(*userModules) : 0; auto standardModules__ = standardModules ? _fbb.CreateVector(*standardModules) : 0; auto luaByteCode__ = luaByteCode ? _fbb.CreateVector(*luaByteCode) : 0; return rlogic_serialization::CreateLuaScript( @@ -160,17 +166,17 @@ inline flatbuffers::Offset CreateLuaScriptDirect( luaByteCode__); } -inline const flatbuffers::TypeTable *LuaScriptTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 1, 1 }, - { flatbuffers::ET_UCHAR, 1, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 2 }, - { flatbuffers::ET_SEQUENCE, 0, 2 }, - { flatbuffers::ET_UCHAR, 1, -1 } +inline const ::flatbuffers::TypeTable *LuaScriptTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 1, 1 }, + { ::flatbuffers::ET_UCHAR, 1, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 }, + { ::flatbuffers::ET_UCHAR, 1, -1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::LuaModuleUsageTypeTable, rlogic_serialization::PropertyTypeTable @@ -184,8 +190,8 @@ inline const flatbuffers::TypeTable *LuaScriptTypeTable() { "rootOutput", "luaByteCode" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 7, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 7, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h b/src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h index 499a4bc83..97a333dee 100644 --- a/src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/MeshNodeBindingGen.h @@ -6,22 +6,26 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { struct MeshNodeBinding; struct MeshNodeBindingBuilder; -inline const flatbuffers::TypeTable *MeshNodeBindingTypeTable(); +inline const ::flatbuffers::TypeTable *MeshNodeBindingTypeTable(); -struct MeshNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct MeshNodeBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef MeshNodeBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return MeshNodeBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -30,7 +34,7 @@ struct MeshNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::RamsesBinding *base() const { return GetPointer(VT_BASE); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -40,26 +44,25 @@ struct MeshNodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct MeshNodeBindingBuilder { typedef MeshNodeBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(MeshNodeBinding::VT_BASE, base); } - explicit MeshNodeBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit MeshNodeBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - MeshNodeBindingBuilder &operator=(const MeshNodeBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateMeshNodeBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0) { +inline ::flatbuffers::Offset CreateMeshNodeBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0) { MeshNodeBindingBuilder builder_(_fbb); builder_.add_base(base); return builder_.Finish(); @@ -70,18 +73,18 @@ struct MeshNodeBinding::Traits { static auto constexpr Create = CreateMeshNodeBinding; }; -inline const flatbuffers::TypeTable *MeshNodeBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 } +inline const ::flatbuffers::TypeTable *MeshNodeBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesBindingTypeTable }; static const char * const names[] = { "base" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h b/src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h index 5f5fe512c..16560e096 100644 --- a/src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/NodeBindingGen.h @@ -6,22 +6,26 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { struct NodeBinding; struct NodeBindingBuilder; -inline const flatbuffers::TypeTable *NodeBindingTypeTable(); +inline const ::flatbuffers::TypeTable *NodeBindingTypeTable(); -struct NodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct NodeBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef NodeBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return NodeBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -34,40 +38,39 @@ struct NodeBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { uint8_t rotationType() const { return GetField(VT_ROTATIONTYPE, 0); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && - VerifyField(verifier, VT_ROTATIONTYPE) && + VerifyField(verifier, VT_ROTATIONTYPE, 1) && verifier.EndTable(); } }; struct NodeBindingBuilder { typedef NodeBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(NodeBinding::VT_BASE, base); } void add_rotationType(uint8_t rotationType) { fbb_.AddElement(NodeBinding::VT_ROTATIONTYPE, rotationType, 0); } - explicit NodeBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit NodeBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - NodeBindingBuilder &operator=(const NodeBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateNodeBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, +inline ::flatbuffers::Offset CreateNodeBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, uint8_t rotationType = 0) { NodeBindingBuilder builder_(_fbb); builder_.add_base(base); @@ -80,20 +83,20 @@ struct NodeBinding::Traits { static auto constexpr Create = CreateNodeBinding; }; -inline const flatbuffers::TypeTable *NodeBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, -1 } +inline const ::flatbuffers::TypeTable *NodeBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, -1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesBindingTypeTable }; static const char * const names[] = { "base", "rotationType" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/PropertyGen.h b/src/client/internal/logic/flatbuffers/generated/PropertyGen.h index 1d84926f6..bfb15ba67 100644 --- a/src/client/internal/logic/flatbuffers/generated/PropertyGen.h +++ b/src/client/internal/logic/flatbuffers/generated/PropertyGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + namespace rlogic_serialization { struct float_s; @@ -34,29 +41,29 @@ struct string_sBuilder; struct Property; struct PropertyBuilder; -inline const flatbuffers::TypeTable *float_sTypeTable(); +inline const ::flatbuffers::TypeTable *float_sTypeTable(); -inline const flatbuffers::TypeTable *vec2f_sTypeTable(); +inline const ::flatbuffers::TypeTable *vec2f_sTypeTable(); -inline const flatbuffers::TypeTable *vec3f_sTypeTable(); +inline const ::flatbuffers::TypeTable *vec3f_sTypeTable(); -inline const flatbuffers::TypeTable *vec4f_sTypeTable(); +inline const ::flatbuffers::TypeTable *vec4f_sTypeTable(); -inline const flatbuffers::TypeTable *int32_sTypeTable(); +inline const ::flatbuffers::TypeTable *int32_sTypeTable(); -inline const flatbuffers::TypeTable *int64_sTypeTable(); +inline const ::flatbuffers::TypeTable *int64_sTypeTable(); -inline const flatbuffers::TypeTable *vec2i_sTypeTable(); +inline const ::flatbuffers::TypeTable *vec2i_sTypeTable(); -inline const flatbuffers::TypeTable *vec3i_sTypeTable(); +inline const ::flatbuffers::TypeTable *vec3i_sTypeTable(); -inline const flatbuffers::TypeTable *vec4i_sTypeTable(); +inline const ::flatbuffers::TypeTable *vec4i_sTypeTable(); -inline const flatbuffers::TypeTable *bool_sTypeTable(); +inline const ::flatbuffers::TypeTable *bool_sTypeTable(); -inline const flatbuffers::TypeTable *string_sTypeTable(); +inline const ::flatbuffers::TypeTable *string_sTypeTable(); -inline const flatbuffers::TypeTable *PropertyTypeTable(); +inline const ::flatbuffers::TypeTable *PropertyTypeTable(); enum class EPropertyRootType : uint8_t { Primitive = 0, @@ -86,7 +93,7 @@ inline const char * const *EnumNamesEPropertyRootType() { } inline const char *EnumNameEPropertyRootType(EPropertyRootType e) { - if (flatbuffers::IsOutRange(e, EPropertyRootType::Primitive, EPropertyRootType::Array)) return ""; + if (::flatbuffers::IsOutRange(e, EPropertyRootType::Primitive, EPropertyRootType::Array)) return ""; const size_t index = static_cast(e); return EnumNamesEPropertyRootType()[index]; } @@ -146,7 +153,7 @@ inline const char * const *EnumNamesPropertyValue() { } inline const char *EnumNamePropertyValue(PropertyValue e) { - if (flatbuffers::IsOutRange(e, PropertyValue::NONE, PropertyValue::bool_s)) return ""; + if (::flatbuffers::IsOutRange(e, PropertyValue::NONE, PropertyValue::bool_s)) return ""; const size_t index = static_cast(e); return EnumNamesPropertyValue()[index]; } @@ -199,54 +206,65 @@ template<> struct PropertyValueTraits { static const PropertyValue enum_value = PropertyValue::bool_s; }; -bool VerifyPropertyValue(flatbuffers::Verifier &verifier, const void *obj, PropertyValue type); -bool VerifyPropertyValueVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types); +bool VerifyPropertyValue(::flatbuffers::Verifier &verifier, const void *obj, PropertyValue type); +bool VerifyPropertyValueVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types); FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) float_s FLATBUFFERS_FINAL_CLASS { private: float v_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return float_sTypeTable(); } - float_s() { - memset(static_cast(this), 0, sizeof(float_s)); + float_s() + : v_(0) { } float_s(float _v) - : v_(flatbuffers::EndianScalar(_v)) { + : v_(::flatbuffers::EndianScalar(_v)) { } float v() const { - return flatbuffers::EndianScalar(v_); + return ::flatbuffers::EndianScalar(v_); } }; FLATBUFFERS_STRUCT_END(float_s, 4); +struct float_s::Traits { + using type = float_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec2f_s FLATBUFFERS_FINAL_CLASS { private: float x_; float y_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return vec2f_sTypeTable(); } - vec2f_s() { - memset(static_cast(this), 0, sizeof(vec2f_s)); + vec2f_s() + : x_(0), + y_(0) { } vec2f_s(float _x, float _y) - : x_(flatbuffers::EndianScalar(_x)), - y_(flatbuffers::EndianScalar(_y)) { + : x_(::flatbuffers::EndianScalar(_x)), + y_(::flatbuffers::EndianScalar(_y)) { } float x() const { - return flatbuffers::EndianScalar(x_); + return ::flatbuffers::EndianScalar(x_); } float y() const { - return flatbuffers::EndianScalar(y_); + return ::flatbuffers::EndianScalar(y_); } }; FLATBUFFERS_STRUCT_END(vec2f_s, 8); +struct vec2f_s::Traits { + using type = vec2f_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec3f_s FLATBUFFERS_FINAL_CLASS { private: float x_; @@ -254,29 +272,36 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec3f_s FLATBUFFERS_FINAL_CLASS { float z_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return vec3f_sTypeTable(); } - vec3f_s() { - memset(static_cast(this), 0, sizeof(vec3f_s)); + vec3f_s() + : x_(0), + y_(0), + z_(0) { } vec3f_s(float _x, float _y, float _z) - : x_(flatbuffers::EndianScalar(_x)), - y_(flatbuffers::EndianScalar(_y)), - z_(flatbuffers::EndianScalar(_z)) { + : x_(::flatbuffers::EndianScalar(_x)), + y_(::flatbuffers::EndianScalar(_y)), + z_(::flatbuffers::EndianScalar(_z)) { } float x() const { - return flatbuffers::EndianScalar(x_); + return ::flatbuffers::EndianScalar(x_); } float y() const { - return flatbuffers::EndianScalar(y_); + return ::flatbuffers::EndianScalar(y_); } float z() const { - return flatbuffers::EndianScalar(z_); + return ::flatbuffers::EndianScalar(z_); } }; FLATBUFFERS_STRUCT_END(vec3f_s, 12); +struct vec3f_s::Traits { + using type = vec3f_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec4f_s FLATBUFFERS_FINAL_CLASS { private: float x_; @@ -285,98 +310,122 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec4f_s FLATBUFFERS_FINAL_CLASS { float w_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return vec4f_sTypeTable(); } - vec4f_s() { - memset(static_cast(this), 0, sizeof(vec4f_s)); + vec4f_s() + : x_(0), + y_(0), + z_(0), + w_(0) { } vec4f_s(float _x, float _y, float _z, float _w) - : x_(flatbuffers::EndianScalar(_x)), - y_(flatbuffers::EndianScalar(_y)), - z_(flatbuffers::EndianScalar(_z)), - w_(flatbuffers::EndianScalar(_w)) { + : x_(::flatbuffers::EndianScalar(_x)), + y_(::flatbuffers::EndianScalar(_y)), + z_(::flatbuffers::EndianScalar(_z)), + w_(::flatbuffers::EndianScalar(_w)) { } float x() const { - return flatbuffers::EndianScalar(x_); + return ::flatbuffers::EndianScalar(x_); } float y() const { - return flatbuffers::EndianScalar(y_); + return ::flatbuffers::EndianScalar(y_); } float z() const { - return flatbuffers::EndianScalar(z_); + return ::flatbuffers::EndianScalar(z_); } float w() const { - return flatbuffers::EndianScalar(w_); + return ::flatbuffers::EndianScalar(w_); } }; FLATBUFFERS_STRUCT_END(vec4f_s, 16); +struct vec4f_s::Traits { + using type = vec4f_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) int32_s FLATBUFFERS_FINAL_CLASS { private: int32_t v_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return int32_sTypeTable(); } - int32_s() { - memset(static_cast(this), 0, sizeof(int32_s)); + int32_s() + : v_(0) { } int32_s(int32_t _v) - : v_(flatbuffers::EndianScalar(_v)) { + : v_(::flatbuffers::EndianScalar(_v)) { } int32_t v() const { - return flatbuffers::EndianScalar(v_); + return ::flatbuffers::EndianScalar(v_); } }; FLATBUFFERS_STRUCT_END(int32_s, 4); +struct int32_s::Traits { + using type = int32_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) int64_s FLATBUFFERS_FINAL_CLASS { private: int64_t v_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return int64_sTypeTable(); } - int64_s() { - memset(static_cast(this), 0, sizeof(int64_s)); + int64_s() + : v_(0) { } int64_s(int64_t _v) - : v_(flatbuffers::EndianScalar(_v)) { + : v_(::flatbuffers::EndianScalar(_v)) { } int64_t v() const { - return flatbuffers::EndianScalar(v_); + return ::flatbuffers::EndianScalar(v_); } }; FLATBUFFERS_STRUCT_END(int64_s, 8); +struct int64_s::Traits { + using type = int64_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec2i_s FLATBUFFERS_FINAL_CLASS { private: int32_t x_; int32_t y_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return vec2i_sTypeTable(); } - vec2i_s() { - memset(static_cast(this), 0, sizeof(vec2i_s)); + vec2i_s() + : x_(0), + y_(0) { } vec2i_s(int32_t _x, int32_t _y) - : x_(flatbuffers::EndianScalar(_x)), - y_(flatbuffers::EndianScalar(_y)) { + : x_(::flatbuffers::EndianScalar(_x)), + y_(::flatbuffers::EndianScalar(_y)) { } int32_t x() const { - return flatbuffers::EndianScalar(x_); + return ::flatbuffers::EndianScalar(x_); } int32_t y() const { - return flatbuffers::EndianScalar(y_); + return ::flatbuffers::EndianScalar(y_); } }; FLATBUFFERS_STRUCT_END(vec2i_s, 8); +struct vec2i_s::Traits { + using type = vec2i_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec3i_s FLATBUFFERS_FINAL_CLASS { private: int32_t x_; @@ -384,29 +433,36 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec3i_s FLATBUFFERS_FINAL_CLASS { int32_t z_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return vec3i_sTypeTable(); } - vec3i_s() { - memset(static_cast(this), 0, sizeof(vec3i_s)); + vec3i_s() + : x_(0), + y_(0), + z_(0) { } vec3i_s(int32_t _x, int32_t _y, int32_t _z) - : x_(flatbuffers::EndianScalar(_x)), - y_(flatbuffers::EndianScalar(_y)), - z_(flatbuffers::EndianScalar(_z)) { + : x_(::flatbuffers::EndianScalar(_x)), + y_(::flatbuffers::EndianScalar(_y)), + z_(::flatbuffers::EndianScalar(_z)) { } int32_t x() const { - return flatbuffers::EndianScalar(x_); + return ::flatbuffers::EndianScalar(x_); } int32_t y() const { - return flatbuffers::EndianScalar(y_); + return ::flatbuffers::EndianScalar(y_); } int32_t z() const { - return flatbuffers::EndianScalar(z_); + return ::flatbuffers::EndianScalar(z_); } }; FLATBUFFERS_STRUCT_END(vec3i_s, 12); +struct vec3i_s::Traits { + using type = vec3i_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec4i_s FLATBUFFERS_FINAL_CLASS { private: int32_t x_; @@ -415,66 +471,79 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) vec4i_s FLATBUFFERS_FINAL_CLASS { int32_t w_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return vec4i_sTypeTable(); } - vec4i_s() { - memset(static_cast(this), 0, sizeof(vec4i_s)); + vec4i_s() + : x_(0), + y_(0), + z_(0), + w_(0) { } vec4i_s(int32_t _x, int32_t _y, int32_t _z, int32_t _w) - : x_(flatbuffers::EndianScalar(_x)), - y_(flatbuffers::EndianScalar(_y)), - z_(flatbuffers::EndianScalar(_z)), - w_(flatbuffers::EndianScalar(_w)) { + : x_(::flatbuffers::EndianScalar(_x)), + y_(::flatbuffers::EndianScalar(_y)), + z_(::flatbuffers::EndianScalar(_z)), + w_(::flatbuffers::EndianScalar(_w)) { } int32_t x() const { - return flatbuffers::EndianScalar(x_); + return ::flatbuffers::EndianScalar(x_); } int32_t y() const { - return flatbuffers::EndianScalar(y_); + return ::flatbuffers::EndianScalar(y_); } int32_t z() const { - return flatbuffers::EndianScalar(z_); + return ::flatbuffers::EndianScalar(z_); } int32_t w() const { - return flatbuffers::EndianScalar(w_); + return ::flatbuffers::EndianScalar(w_); } }; FLATBUFFERS_STRUCT_END(vec4i_s, 16); +struct vec4i_s::Traits { + using type = vec4i_s; +}; + FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(1) bool_s FLATBUFFERS_FINAL_CLASS { private: uint8_t v_; public: - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + struct Traits; + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return bool_sTypeTable(); } - bool_s() { - memset(static_cast(this), 0, sizeof(bool_s)); + bool_s() + : v_(0) { } bool_s(bool _v) - : v_(flatbuffers::EndianScalar(static_cast(_v))) { + : v_(::flatbuffers::EndianScalar(static_cast(_v))) { } bool v() const { - return flatbuffers::EndianScalar(v_) != 0; + return ::flatbuffers::EndianScalar(v_) != 0; } }; FLATBUFFERS_STRUCT_END(bool_s, 1); -struct string_s FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct bool_s::Traits { + using type = bool_s; +}; + +struct string_s FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef string_sBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return string_sTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_V = 4 }; - const flatbuffers::String *v() const { - return GetPointer(VT_V); + const ::flatbuffers::String *v() const { + return GetPointer(VT_V); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_V) && verifier.VerifyString(v()) && @@ -484,26 +553,25 @@ struct string_s FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct string_sBuilder { typedef string_s Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_v(flatbuffers::Offset v) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_v(::flatbuffers::Offset<::flatbuffers::String> v) { fbb_.AddOffset(string_s::VT_V, v); } - explicit string_sBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit string_sBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - string_sBuilder &operator=(const string_sBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset Createstring_s( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset v = 0) { +inline ::flatbuffers::Offset Createstring_s( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> v = 0) { string_sBuilder builder_(_fbb); builder_.add_v(v); return builder_.Finish(); @@ -514,8 +582,8 @@ struct string_s::Traits { static auto constexpr Create = Createstring_s; }; -inline flatbuffers::Offset Createstring_sDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset Createstring_sDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const char *v = nullptr) { auto v__ = v ? _fbb.CreateString(v) : 0; return rlogic_serialization::Createstring_s( @@ -523,10 +591,10 @@ inline flatbuffers::Offset Createstring_sDirect( v__); } -struct Property FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct Property FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef PropertyBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return PropertyTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -536,14 +604,14 @@ struct Property FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_VALUE_TYPE = 10, VT_VALUE = 12 }; - const flatbuffers::String *name() const { - return GetPointer(VT_NAME); + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); } rlogic_serialization::EPropertyRootType rootType() const { return static_cast(GetField(VT_ROOTTYPE, 0)); } - const flatbuffers::Vector> *children() const { - return GetPointer> *>(VT_CHILDREN); + const ::flatbuffers::Vector<::flatbuffers::Offset> *children() const { + return GetPointer> *>(VT_CHILDREN); } rlogic_serialization::PropertyValue value_type() const { return static_cast(GetField(VT_VALUE_TYPE, 0)); @@ -585,15 +653,15 @@ struct Property FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::bool_s *value_as_bool_s() const { return value_type() == rlogic_serialization::PropertyValue::bool_s ? static_cast(value()) : nullptr; } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && verifier.VerifyString(name()) && - VerifyField(verifier, VT_ROOTTYPE) && + VerifyField(verifier, VT_ROOTTYPE, 1) && VerifyOffset(verifier, VT_CHILDREN) && verifier.VerifyVector(children()) && verifier.VerifyVectorOfTables(children()) && - VerifyField(verifier, VT_VALUE_TYPE) && + VerifyField(verifier, VT_VALUE_TYPE, 1) && VerifyOffset(verifier, VT_VALUE) && VerifyPropertyValue(verifier, value(), value_type()) && verifier.EndTable(); @@ -646,42 +714,41 @@ template<> inline const rlogic_serialization::bool_s *Property::value_as name) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { fbb_.AddOffset(Property::VT_NAME, name); } void add_rootType(rlogic_serialization::EPropertyRootType rootType) { fbb_.AddElement(Property::VT_ROOTTYPE, static_cast(rootType), 0); } - void add_children(flatbuffers::Offset>> children) { + void add_children(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> children) { fbb_.AddOffset(Property::VT_CHILDREN, children); } void add_value_type(rlogic_serialization::PropertyValue value_type) { fbb_.AddElement(Property::VT_VALUE_TYPE, static_cast(value_type), 0); } - void add_value(flatbuffers::Offset value) { + void add_value(::flatbuffers::Offset value) { fbb_.AddOffset(Property::VT_VALUE, value); } - explicit PropertyBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit PropertyBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - PropertyBuilder &operator=(const PropertyBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateProperty( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset name = 0, +inline ::flatbuffers::Offset CreateProperty( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, rlogic_serialization::EPropertyRootType rootType = rlogic_serialization::EPropertyRootType::Primitive, - flatbuffers::Offset>> children = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> children = 0, rlogic_serialization::PropertyValue value_type = rlogic_serialization::PropertyValue::NONE, - flatbuffers::Offset value = 0) { + ::flatbuffers::Offset value = 0) { PropertyBuilder builder_(_fbb); builder_.add_value(value); builder_.add_children(children); @@ -696,15 +763,15 @@ struct Property::Traits { static auto constexpr Create = CreateProperty; }; -inline flatbuffers::Offset CreatePropertyDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreatePropertyDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, rlogic_serialization::EPropertyRootType rootType = rlogic_serialization::EPropertyRootType::Primitive, - const std::vector> *children = nullptr, + const std::vector<::flatbuffers::Offset> *children = nullptr, rlogic_serialization::PropertyValue value_type = rlogic_serialization::PropertyValue::NONE, - flatbuffers::Offset value = 0) { + ::flatbuffers::Offset value = 0) { auto name__ = name ? _fbb.CreateString(name) : 0; - auto children__ = children ? _fbb.CreateVector>(*children) : 0; + auto children__ = children ? _fbb.CreateVector<::flatbuffers::Offset>(*children) : 0; return rlogic_serialization::CreateProperty( _fbb, name__, @@ -714,53 +781,53 @@ inline flatbuffers::Offset CreatePropertyDirect( value); } -inline bool VerifyPropertyValue(flatbuffers::Verifier &verifier, const void *obj, PropertyValue type) { +inline bool VerifyPropertyValue(::flatbuffers::Verifier &verifier, const void *obj, PropertyValue type) { switch (type) { case PropertyValue::NONE: { return true; } case PropertyValue::float_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::vec2f_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::vec3f_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::vec4f_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::int32_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::int64_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 8); } case PropertyValue::vec2i_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::vec3i_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::vec4i_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 4); } case PropertyValue::string_s: { auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } case PropertyValue::bool_s: { - return verifier.Verify(static_cast(obj), 0); + return verifier.VerifyField(static_cast(obj), 0, 1); } default: return true; } } -inline bool VerifyPropertyValueVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types) { +inline bool VerifyPropertyValueVector(::flatbuffers::Verifier &verifier, const ::flatbuffers::Vector<::flatbuffers::Offset> *values, const ::flatbuffers::Vector *types) { if (!values || !types) return !values && !types; if (values->size() != types->size()) return false; - for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { if (!VerifyPropertyValue( verifier, values->Get(i), types->GetEnum(i))) { return false; @@ -769,13 +836,13 @@ inline bool VerifyPropertyValueVector(flatbuffers::Verifier &verifier, const fla return true; } -inline const flatbuffers::TypeTable *EPropertyRootTypeTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_UCHAR, 0, 0 } +inline const ::flatbuffers::TypeTable *EPropertyRootTypeTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_UCHAR, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::EPropertyRootTypeTypeTable }; static const char * const names[] = { @@ -783,28 +850,28 @@ inline const flatbuffers::TypeTable *EPropertyRootTypeTypeTable() { "Struct", "Array" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_ENUM, 3, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_ENUM, 3, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *PropertyValueTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 }, - { flatbuffers::ET_SEQUENCE, 0, 2 }, - { flatbuffers::ET_SEQUENCE, 0, 3 }, - { flatbuffers::ET_SEQUENCE, 0, 4 }, - { flatbuffers::ET_SEQUENCE, 0, 5 }, - { flatbuffers::ET_SEQUENCE, 0, 6 }, - { flatbuffers::ET_SEQUENCE, 0, 7 }, - { flatbuffers::ET_SEQUENCE, 0, 8 }, - { flatbuffers::ET_SEQUENCE, 0, 9 }, - { flatbuffers::ET_SEQUENCE, 0, 10 } +inline const ::flatbuffers::TypeTable *PropertyValueTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 }, + { ::flatbuffers::ET_SEQUENCE, 0, 3 }, + { ::flatbuffers::ET_SEQUENCE, 0, 4 }, + { ::flatbuffers::ET_SEQUENCE, 0, 5 }, + { ::flatbuffers::ET_SEQUENCE, 0, 6 }, + { ::flatbuffers::ET_SEQUENCE, 0, 7 }, + { ::flatbuffers::ET_SEQUENCE, 0, 8 }, + { ::flatbuffers::ET_SEQUENCE, 0, 9 }, + { ::flatbuffers::ET_SEQUENCE, 0, 10 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::float_sTypeTable, rlogic_serialization::vec2f_sTypeTable, rlogic_serialization::vec3f_sTypeTable, @@ -831,47 +898,47 @@ inline const flatbuffers::TypeTable *PropertyValueTypeTable() { "string_s", "bool_s" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_UNION, 12, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_UNION, 12, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *float_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_FLOAT, 0, -1 } +inline const ::flatbuffers::TypeTable *float_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_FLOAT, 0, -1 } }; static const int64_t values[] = { 0, 4 }; static const char * const names[] = { "v" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 1, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *vec2f_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_FLOAT, 0, -1 }, - { flatbuffers::ET_FLOAT, 0, -1 } +inline const ::flatbuffers::TypeTable *vec2f_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_FLOAT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 0, -1 } }; static const int64_t values[] = { 0, 4, 8 }; static const char * const names[] = { "x", "y" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 2, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 2, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *vec3f_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_FLOAT, 0, -1 }, - { flatbuffers::ET_FLOAT, 0, -1 }, - { flatbuffers::ET_FLOAT, 0, -1 } +inline const ::flatbuffers::TypeTable *vec3f_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_FLOAT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 0, -1 } }; static const int64_t values[] = { 0, 4, 8, 12 }; static const char * const names[] = { @@ -879,18 +946,18 @@ inline const flatbuffers::TypeTable *vec3f_sTypeTable() { "y", "z" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 3, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 3, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *vec4f_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_FLOAT, 0, -1 }, - { flatbuffers::ET_FLOAT, 0, -1 }, - { flatbuffers::ET_FLOAT, 0, -1 }, - { flatbuffers::ET_FLOAT, 0, -1 } +inline const ::flatbuffers::TypeTable *vec4f_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_FLOAT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 0, -1 }, + { ::flatbuffers::ET_FLOAT, 0, -1 } }; static const int64_t values[] = { 0, 4, 8, 12, 16 }; static const char * const names[] = { @@ -899,61 +966,61 @@ inline const flatbuffers::TypeTable *vec4f_sTypeTable() { "z", "w" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 4, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 4, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *int32_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_INT, 0, -1 } +inline const ::flatbuffers::TypeTable *int32_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 } }; static const int64_t values[] = { 0, 4 }; static const char * const names[] = { "v" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 1, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *int64_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_LONG, 0, -1 } +inline const ::flatbuffers::TypeTable *int64_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_LONG, 0, -1 } }; static const int64_t values[] = { 0, 8 }; static const char * const names[] = { "v" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 1, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *vec2i_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_INT, 0, -1 }, - { flatbuffers::ET_INT, 0, -1 } +inline const ::flatbuffers::TypeTable *vec2i_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 } }; static const int64_t values[] = { 0, 4, 8 }; static const char * const names[] = { "x", "y" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 2, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 2, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *vec3i_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_INT, 0, -1 }, - { flatbuffers::ET_INT, 0, -1 }, - { flatbuffers::ET_INT, 0, -1 } +inline const ::flatbuffers::TypeTable *vec3i_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 } }; static const int64_t values[] = { 0, 4, 8, 12 }; static const char * const names[] = { @@ -961,18 +1028,18 @@ inline const flatbuffers::TypeTable *vec3i_sTypeTable() { "y", "z" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 3, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 3, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *vec4i_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_INT, 0, -1 }, - { flatbuffers::ET_INT, 0, -1 }, - { flatbuffers::ET_INT, 0, -1 }, - { flatbuffers::ET_INT, 0, -1 } +inline const ::flatbuffers::TypeTable *vec4i_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 }, + { ::flatbuffers::ET_INT, 0, -1 } }; static const int64_t values[] = { 0, 4, 8, 12, 16 }; static const char * const names[] = { @@ -981,48 +1048,48 @@ inline const flatbuffers::TypeTable *vec4i_sTypeTable() { "z", "w" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 4, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 4, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *bool_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_BOOL, 0, -1 } +inline const ::flatbuffers::TypeTable *bool_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_BOOL, 0, -1 } }; static const int64_t values[] = { 0, 1 }; static const char * const names[] = { "v" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_STRUCT, 1, type_codes, nullptr, values, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_STRUCT, 1, type_codes, nullptr, nullptr, values, names }; return &tt; } -inline const flatbuffers::TypeTable *string_sTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 } +inline const ::flatbuffers::TypeTable *string_sTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_STRING, 0, -1 } }; static const char * const names[] = { "v" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, nullptr, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *PropertyTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_UCHAR, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 1, 1 }, - { flatbuffers::ET_UTYPE, 0, 2 }, - { flatbuffers::ET_SEQUENCE, 0, 2 } +inline const ::flatbuffers::TypeTable *PropertyTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_UCHAR, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 1, 1 }, + { ::flatbuffers::ET_UTYPE, 0, 2 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::EPropertyRootTypeTypeTable, rlogic_serialization::PropertyTypeTable, rlogic_serialization::PropertyValueTypeTable @@ -1034,8 +1101,8 @@ inline const flatbuffers::TypeTable *PropertyTypeTable() { "value_type", "value" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/RamsesBindingGen.h b/src/client/internal/logic/flatbuffers/generated/RamsesBindingGen.h index c14fa82f7..6de983784 100644 --- a/src/client/internal/logic/flatbuffers/generated/RamsesBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/RamsesBindingGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" #include "PropertyGen.h" #include "RamsesReferenceGen.h" @@ -15,12 +22,12 @@ namespace rlogic_serialization { struct RamsesBinding; struct RamsesBindingBuilder; -inline const flatbuffers::TypeTable *RamsesBindingTypeTable(); +inline const ::flatbuffers::TypeTable *RamsesBindingTypeTable(); -struct RamsesBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct RamsesBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef RamsesBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return RamsesBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -37,7 +44,7 @@ struct RamsesBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::Property *rootInput() const { return GetPointer(VT_ROOTINPUT); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -51,34 +58,33 @@ struct RamsesBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct RamsesBindingBuilder { typedef RamsesBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(RamsesBinding::VT_BASE, base); } - void add_boundRamsesObject(flatbuffers::Offset boundRamsesObject) { + void add_boundRamsesObject(::flatbuffers::Offset boundRamsesObject) { fbb_.AddOffset(RamsesBinding::VT_BOUNDRAMSESOBJECT, boundRamsesObject); } - void add_rootInput(flatbuffers::Offset rootInput) { + void add_rootInput(::flatbuffers::Offset rootInput) { fbb_.AddOffset(RamsesBinding::VT_ROOTINPUT, rootInput); } - explicit RamsesBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit RamsesBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesBindingBuilder &operator=(const RamsesBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset boundRamsesObject = 0, - flatbuffers::Offset rootInput = 0) { +inline ::flatbuffers::Offset CreateRamsesBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset boundRamsesObject = 0, + ::flatbuffers::Offset rootInput = 0) { RamsesBindingBuilder builder_(_fbb); builder_.add_rootInput(rootInput); builder_.add_boundRamsesObject(boundRamsesObject); @@ -91,13 +97,13 @@ struct RamsesBinding::Traits { static auto constexpr Create = CreateRamsesBinding; }; -inline const flatbuffers::TypeTable *RamsesBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 }, - { flatbuffers::ET_SEQUENCE, 0, 2 } +inline const ::flatbuffers::TypeTable *RamsesBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 2 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::RamsesReferenceTypeTable, rlogic_serialization::PropertyTypeTable @@ -107,8 +113,8 @@ inline const flatbuffers::TypeTable *RamsesBindingTypeTable() { "boundRamsesObject", "rootInput" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/RamsesReferenceGen.h b/src/client/internal/logic/flatbuffers/generated/RamsesReferenceGen.h index e24710e5b..e9fd60619 100644 --- a/src/client/internal/logic/flatbuffers/generated/RamsesReferenceGen.h +++ b/src/client/internal/logic/flatbuffers/generated/RamsesReferenceGen.h @@ -6,17 +6,24 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + namespace rlogic_serialization { struct RamsesReference; struct RamsesReferenceBuilder; -inline const flatbuffers::TypeTable *RamsesReferenceTypeTable(); +inline const ::flatbuffers::TypeTable *RamsesReferenceTypeTable(); -struct RamsesReference FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct RamsesReference FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef RamsesReferenceBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return RamsesReferenceTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -29,38 +36,37 @@ struct RamsesReference FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { uint32_t objectType() const { return GetField(VT_OBJECTTYPE, 0); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && - VerifyField(verifier, VT_OBJECTID) && - VerifyField(verifier, VT_OBJECTTYPE) && + VerifyField(verifier, VT_OBJECTID, 8) && + VerifyField(verifier, VT_OBJECTTYPE, 4) && verifier.EndTable(); } }; struct RamsesReferenceBuilder { typedef RamsesReference Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; void add_objectId(uint64_t objectId) { fbb_.AddElement(RamsesReference::VT_OBJECTID, objectId, 0); } void add_objectType(uint32_t objectType) { fbb_.AddElement(RamsesReference::VT_OBJECTTYPE, objectType, 0); } - explicit RamsesReferenceBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit RamsesReferenceBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RamsesReferenceBuilder &operator=(const RamsesReferenceBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRamsesReference( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreateRamsesReference( + ::flatbuffers::FlatBufferBuilder &_fbb, uint64_t objectId = 0, uint32_t objectType = 0) { RamsesReferenceBuilder builder_(_fbb); @@ -74,17 +80,17 @@ struct RamsesReference::Traits { static auto constexpr Create = CreateRamsesReference; }; -inline const flatbuffers::TypeTable *RamsesReferenceTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_UINT, 0, -1 } +inline const ::flatbuffers::TypeTable *RamsesReferenceTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_UINT, 0, -1 } }; static const char * const names[] = { "objectId", "objectType" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, nullptr, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, nullptr, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h b/src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h index 2efc9d60e..a7e1a3b41 100644 --- a/src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/RenderGroupBindingGen.h @@ -6,10 +6,14 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { @@ -19,27 +23,27 @@ struct ElementBuilder; struct RenderGroupBinding; struct RenderGroupBindingBuilder; -inline const flatbuffers::TypeTable *ElementTypeTable(); +inline const ::flatbuffers::TypeTable *ElementTypeTable(); -inline const flatbuffers::TypeTable *RenderGroupBindingTypeTable(); +inline const ::flatbuffers::TypeTable *RenderGroupBindingTypeTable(); -struct Element FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct Element FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef ElementBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return ElementTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_NAME = 4, VT_RAMSESOBJECT = 6 }; - const flatbuffers::String *name() const { - return GetPointer(VT_NAME); + const ::flatbuffers::String *name() const { + return GetPointer(VT_NAME); } const rlogic_serialization::RamsesReference *ramsesObject() const { return GetPointer(VT_RAMSESOBJECT); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_NAME) && verifier.VerifyString(name()) && @@ -51,30 +55,29 @@ struct Element FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct ElementBuilder { typedef Element Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_name(flatbuffers::Offset name) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_name(::flatbuffers::Offset<::flatbuffers::String> name) { fbb_.AddOffset(Element::VT_NAME, name); } - void add_ramsesObject(flatbuffers::Offset ramsesObject) { + void add_ramsesObject(::flatbuffers::Offset ramsesObject) { fbb_.AddOffset(Element::VT_RAMSESOBJECT, ramsesObject); } - explicit ElementBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit ElementBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - ElementBuilder &operator=(const ElementBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateElement( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset name = 0, - flatbuffers::Offset ramsesObject = 0) { +inline ::flatbuffers::Offset CreateElement( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset<::flatbuffers::String> name = 0, + ::flatbuffers::Offset ramsesObject = 0) { ElementBuilder builder_(_fbb); builder_.add_ramsesObject(ramsesObject); builder_.add_name(name); @@ -86,10 +89,10 @@ struct Element::Traits { static auto constexpr Create = CreateElement; }; -inline flatbuffers::Offset CreateElementDirect( - flatbuffers::FlatBufferBuilder &_fbb, +inline ::flatbuffers::Offset CreateElementDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, const char *name = nullptr, - flatbuffers::Offset ramsesObject = 0) { + ::flatbuffers::Offset ramsesObject = 0) { auto name__ = name ? _fbb.CreateString(name) : 0; return rlogic_serialization::CreateElement( _fbb, @@ -97,10 +100,10 @@ inline flatbuffers::Offset CreateElementDirect( ramsesObject); } -struct RenderGroupBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct RenderGroupBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef RenderGroupBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return RenderGroupBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -110,10 +113,10 @@ struct RenderGroupBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::RamsesBinding *base() const { return GetPointer(VT_BASE); } - const flatbuffers::Vector> *elements() const { - return GetPointer> *>(VT_ELEMENTS); + const ::flatbuffers::Vector<::flatbuffers::Offset> *elements() const { + return GetPointer> *>(VT_ELEMENTS); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -126,30 +129,29 @@ struct RenderGroupBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct RenderGroupBindingBuilder { typedef RenderGroupBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(RenderGroupBinding::VT_BASE, base); } - void add_elements(flatbuffers::Offset>> elements) { + void add_elements(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> elements) { fbb_.AddOffset(RenderGroupBinding::VT_ELEMENTS, elements); } - explicit RenderGroupBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit RenderGroupBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RenderGroupBindingBuilder &operator=(const RenderGroupBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRenderGroupBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset>> elements = 0) { +inline ::flatbuffers::Offset CreateRenderGroupBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset>> elements = 0) { RenderGroupBindingBuilder builder_(_fbb); builder_.add_elements(elements); builder_.add_base(base); @@ -161,41 +163,41 @@ struct RenderGroupBinding::Traits { static auto constexpr Create = CreateRenderGroupBinding; }; -inline flatbuffers::Offset CreateRenderGroupBindingDirect( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - const std::vector> *elements = nullptr) { - auto elements__ = elements ? _fbb.CreateVector>(*elements) : 0; +inline ::flatbuffers::Offset CreateRenderGroupBindingDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + const std::vector<::flatbuffers::Offset> *elements = nullptr) { + auto elements__ = elements ? _fbb.CreateVector<::flatbuffers::Offset>(*elements) : 0; return rlogic_serialization::CreateRenderGroupBinding( _fbb, base, elements__); } -inline const flatbuffers::TypeTable *ElementTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_STRING, 0, -1 }, - { flatbuffers::ET_SEQUENCE, 0, 0 } +inline const ::flatbuffers::TypeTable *ElementTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_STRING, 0, -1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesReferenceTypeTable }; static const char * const names[] = { "name", "ramsesObject" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } -inline const flatbuffers::TypeTable *RenderGroupBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 1, 1 } +inline const ::flatbuffers::TypeTable *RenderGroupBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 1, 1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesBindingTypeTable, rlogic_serialization::ElementTypeTable }; @@ -203,8 +205,8 @@ inline const flatbuffers::TypeTable *RenderGroupBindingTypeTable() { "base", "elements" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 2, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h b/src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h index 7564e7bd8..184a880f5 100644 --- a/src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/RenderPassBindingGen.h @@ -6,22 +6,26 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { struct RenderPassBinding; struct RenderPassBindingBuilder; -inline const flatbuffers::TypeTable *RenderPassBindingTypeTable(); +inline const ::flatbuffers::TypeTable *RenderPassBindingTypeTable(); -struct RenderPassBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct RenderPassBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef RenderPassBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return RenderPassBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -30,7 +34,7 @@ struct RenderPassBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::RamsesBinding *base() const { return GetPointer(VT_BASE); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -40,26 +44,25 @@ struct RenderPassBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct RenderPassBindingBuilder { typedef RenderPassBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(RenderPassBinding::VT_BASE, base); } - explicit RenderPassBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit RenderPassBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - RenderPassBindingBuilder &operator=(const RenderPassBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateRenderPassBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0) { +inline ::flatbuffers::Offset CreateRenderPassBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0) { RenderPassBindingBuilder builder_(_fbb); builder_.add_base(base); return builder_.Finish(); @@ -70,18 +73,18 @@ struct RenderPassBinding::Traits { static auto constexpr Create = CreateRenderPassBinding; }; -inline const flatbuffers::TypeTable *RenderPassBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 } +inline const ::flatbuffers::TypeTable *RenderPassBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::RamsesBindingTypeTable }; static const char * const names[] = { "base" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 1, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/SkinBindingGen.h b/src/client/internal/logic/flatbuffers/generated/SkinBindingGen.h index be7e0ae55..24d23c737 100644 --- a/src/client/internal/logic/flatbuffers/generated/SkinBindingGen.h +++ b/src/client/internal/logic/flatbuffers/generated/SkinBindingGen.h @@ -6,22 +6,26 @@ #include "flatbuffers/flatbuffers.h" -#include "LogicObjectGen.h" -#include "PropertyGen.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "RamsesBindingGen.h" -#include "RamsesReferenceGen.h" namespace rlogic_serialization { struct SkinBinding; struct SkinBindingBuilder; -inline const flatbuffers::TypeTable *SkinBindingTypeTable(); +inline const ::flatbuffers::TypeTable *SkinBindingTypeTable(); -struct SkinBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct SkinBinding FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef SkinBindingBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return SkinBindingTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -34,19 +38,19 @@ struct SkinBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::LogicObject *base() const { return GetPointer(VT_BASE); } - const flatbuffers::Vector *jointNodeBindingIds() const { - return GetPointer *>(VT_JOINTNODEBINDINGIDS); + const ::flatbuffers::Vector *jointNodeBindingIds() const { + return GetPointer *>(VT_JOINTNODEBINDINGIDS); } - const flatbuffers::Vector *inverseBindingMatricesData() const { - return GetPointer *>(VT_INVERSEBINDINGMATRICESDATA); + const ::flatbuffers::Vector *inverseBindingMatricesData() const { + return GetPointer *>(VT_INVERSEBINDINGMATRICESDATA); } uint64_t appearanceBindingId() const { return GetField(VT_APPEARANCEBINDINGID, 0); } - const flatbuffers::String *jointMatUniformInputName() const { - return GetPointer(VT_JOINTMATUNIFORMINPUTNAME); + const ::flatbuffers::String *jointMatUniformInputName() const { + return GetPointer(VT_JOINTMATUNIFORMINPUTNAME); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -54,7 +58,7 @@ struct SkinBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { verifier.VerifyVector(jointNodeBindingIds()) && VerifyOffset(verifier, VT_INVERSEBINDINGMATRICESDATA) && verifier.VerifyVector(inverseBindingMatricesData()) && - VerifyField(verifier, VT_APPEARANCEBINDINGID) && + VerifyField(verifier, VT_APPEARANCEBINDINGID, 8) && VerifyOffset(verifier, VT_JOINTMATUNIFORMINPUTNAME) && verifier.VerifyString(jointMatUniformInputName()) && verifier.EndTable(); @@ -63,42 +67,41 @@ struct SkinBinding FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct SkinBindingBuilder { typedef SkinBinding Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(SkinBinding::VT_BASE, base); } - void add_jointNodeBindingIds(flatbuffers::Offset> jointNodeBindingIds) { + void add_jointNodeBindingIds(::flatbuffers::Offset<::flatbuffers::Vector> jointNodeBindingIds) { fbb_.AddOffset(SkinBinding::VT_JOINTNODEBINDINGIDS, jointNodeBindingIds); } - void add_inverseBindingMatricesData(flatbuffers::Offset> inverseBindingMatricesData) { + void add_inverseBindingMatricesData(::flatbuffers::Offset<::flatbuffers::Vector> inverseBindingMatricesData) { fbb_.AddOffset(SkinBinding::VT_INVERSEBINDINGMATRICESDATA, inverseBindingMatricesData); } void add_appearanceBindingId(uint64_t appearanceBindingId) { fbb_.AddElement(SkinBinding::VT_APPEARANCEBINDINGID, appearanceBindingId, 0); } - void add_jointMatUniformInputName(flatbuffers::Offset jointMatUniformInputName) { + void add_jointMatUniformInputName(::flatbuffers::Offset<::flatbuffers::String> jointMatUniformInputName) { fbb_.AddOffset(SkinBinding::VT_JOINTMATUNIFORMINPUTNAME, jointMatUniformInputName); } - explicit SkinBindingBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit SkinBindingBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - SkinBindingBuilder &operator=(const SkinBindingBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateSkinBinding( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset> jointNodeBindingIds = 0, - flatbuffers::Offset> inverseBindingMatricesData = 0, +inline ::flatbuffers::Offset CreateSkinBinding( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> jointNodeBindingIds = 0, + ::flatbuffers::Offset<::flatbuffers::Vector> inverseBindingMatricesData = 0, uint64_t appearanceBindingId = 0, - flatbuffers::Offset jointMatUniformInputName = 0) { + ::flatbuffers::Offset<::flatbuffers::String> jointMatUniformInputName = 0) { SkinBindingBuilder builder_(_fbb); builder_.add_appearanceBindingId(appearanceBindingId); builder_.add_jointMatUniformInputName(jointMatUniformInputName); @@ -113,9 +116,9 @@ struct SkinBinding::Traits { static auto constexpr Create = CreateSkinBinding; }; -inline flatbuffers::Offset CreateSkinBindingDirect( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, +inline ::flatbuffers::Offset CreateSkinBindingDirect( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, const std::vector *jointNodeBindingIds = nullptr, const std::vector *inverseBindingMatricesData = nullptr, uint64_t appearanceBindingId = 0, @@ -132,15 +135,15 @@ inline flatbuffers::Offset CreateSkinBindingDirect( jointMatUniformInputName__); } -inline const flatbuffers::TypeTable *SkinBindingTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_ULONG, 1, -1 }, - { flatbuffers::ET_FLOAT, 1, -1 }, - { flatbuffers::ET_ULONG, 0, -1 }, - { flatbuffers::ET_STRING, 0, -1 } +inline const ::flatbuffers::TypeTable *SkinBindingTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_ULONG, 1, -1 }, + { ::flatbuffers::ET_FLOAT, 1, -1 }, + { ::flatbuffers::ET_ULONG, 0, -1 }, + { ::flatbuffers::ET_STRING, 0, -1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable }; static const char * const names[] = { @@ -150,8 +153,8 @@ inline const flatbuffers::TypeTable *SkinBindingTypeTable() { "appearanceBindingId", "jointMatUniformInputName" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 5, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/client/internal/logic/flatbuffers/generated/TimerNodeGen.h b/src/client/internal/logic/flatbuffers/generated/TimerNodeGen.h index 8117d8df6..ff7297115 100644 --- a/src/client/internal/logic/flatbuffers/generated/TimerNodeGen.h +++ b/src/client/internal/logic/flatbuffers/generated/TimerNodeGen.h @@ -6,6 +6,13 @@ #include "flatbuffers/flatbuffers.h" +// Ensure the included flatbuffers.h is the same version as when this file was +// generated, otherwise it may not be compatible. +static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && + FLATBUFFERS_VERSION_MINOR == 5 && + FLATBUFFERS_VERSION_REVISION == 9, + "Non-compatible flatbuffers version included"); + #include "LogicObjectGen.h" #include "PropertyGen.h" @@ -14,12 +21,12 @@ namespace rlogic_serialization { struct TimerNode; struct TimerNodeBuilder; -inline const flatbuffers::TypeTable *TimerNodeTypeTable(); +inline const ::flatbuffers::TypeTable *TimerNodeTypeTable(); -struct TimerNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { +struct TimerNode FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { typedef TimerNodeBuilder Builder; struct Traits; - static const flatbuffers::TypeTable *MiniReflectTypeTable() { + static const ::flatbuffers::TypeTable *MiniReflectTypeTable() { return TimerNodeTypeTable(); } enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -36,7 +43,7 @@ struct TimerNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const rlogic_serialization::Property *rootOutput() const { return GetPointer(VT_ROOTOUTPUT); } - bool Verify(flatbuffers::Verifier &verifier) const { + bool Verify(::flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BASE) && verifier.VerifyTable(base()) && @@ -50,34 +57,33 @@ struct TimerNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { struct TimerNodeBuilder { typedef TimerNode Table; - flatbuffers::FlatBufferBuilder &fbb_; - flatbuffers::uoffset_t start_; - void add_base(flatbuffers::Offset base) { + ::flatbuffers::FlatBufferBuilder &fbb_; + ::flatbuffers::uoffset_t start_; + void add_base(::flatbuffers::Offset base) { fbb_.AddOffset(TimerNode::VT_BASE, base); } - void add_rootInput(flatbuffers::Offset rootInput) { + void add_rootInput(::flatbuffers::Offset rootInput) { fbb_.AddOffset(TimerNode::VT_ROOTINPUT, rootInput); } - void add_rootOutput(flatbuffers::Offset rootOutput) { + void add_rootOutput(::flatbuffers::Offset rootOutput) { fbb_.AddOffset(TimerNode::VT_ROOTOUTPUT, rootOutput); } - explicit TimerNodeBuilder(flatbuffers::FlatBufferBuilder &_fbb) + explicit TimerNodeBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); } - TimerNodeBuilder &operator=(const TimerNodeBuilder &); - flatbuffers::Offset Finish() { + ::flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); - auto o = flatbuffers::Offset(end); + auto o = ::flatbuffers::Offset(end); return o; } }; -inline flatbuffers::Offset CreateTimerNode( - flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset base = 0, - flatbuffers::Offset rootInput = 0, - flatbuffers::Offset rootOutput = 0) { +inline ::flatbuffers::Offset CreateTimerNode( + ::flatbuffers::FlatBufferBuilder &_fbb, + ::flatbuffers::Offset base = 0, + ::flatbuffers::Offset rootInput = 0, + ::flatbuffers::Offset rootOutput = 0) { TimerNodeBuilder builder_(_fbb); builder_.add_rootOutput(rootOutput); builder_.add_rootInput(rootInput); @@ -90,13 +96,13 @@ struct TimerNode::Traits { static auto constexpr Create = CreateTimerNode; }; -inline const flatbuffers::TypeTable *TimerNodeTypeTable() { - static const flatbuffers::TypeCode type_codes[] = { - { flatbuffers::ET_SEQUENCE, 0, 0 }, - { flatbuffers::ET_SEQUENCE, 0, 1 }, - { flatbuffers::ET_SEQUENCE, 0, 1 } +inline const ::flatbuffers::TypeTable *TimerNodeTypeTable() { + static const ::flatbuffers::TypeCode type_codes[] = { + { ::flatbuffers::ET_SEQUENCE, 0, 0 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 }, + { ::flatbuffers::ET_SEQUENCE, 0, 1 } }; - static const flatbuffers::TypeFunction type_refs[] = { + static const ::flatbuffers::TypeFunction type_refs[] = { rlogic_serialization::LogicObjectTypeTable, rlogic_serialization::PropertyTypeTable }; @@ -105,8 +111,8 @@ inline const flatbuffers::TypeTable *TimerNodeTypeTable() { "rootInput", "rootOutput" }; - static const flatbuffers::TypeTable tt = { - flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, names + static const ::flatbuffers::TypeTable tt = { + ::flatbuffers::ST_TABLE, 3, type_codes, type_refs, nullptr, nullptr, names }; return &tt; } diff --git a/src/framework/impl/RamsesObject.cpp b/src/framework/impl/RamsesObject.cpp index c5937d165..89f702036 100644 --- a/src/framework/impl/RamsesObject.cpp +++ b/src/framework/impl/RamsesObject.cpp @@ -128,41 +128,41 @@ namespace ramses return dynamic_cast(this); } - template RAMSES_API const LogicObject* RamsesObject::internalCast() const; - template RAMSES_API const LogicNode* RamsesObject::internalCast() const; - template RAMSES_API const RamsesBinding* RamsesObject::internalCast() const; - template RAMSES_API const LuaModule* RamsesObject::internalCast() const; - template RAMSES_API const LuaScript* RamsesObject::internalCast() const; - template RAMSES_API const LuaInterface* RamsesObject::internalCast() const; + template RAMSES_API const LogicObject* RamsesObject::internalCast() const; + template RAMSES_API const LogicNode* RamsesObject::internalCast() const; + template RAMSES_API const RamsesBinding* RamsesObject::internalCast() const; + template RAMSES_API const LuaModule* RamsesObject::internalCast() const; + template RAMSES_API const LuaScript* RamsesObject::internalCast() const; + template RAMSES_API const LuaInterface* RamsesObject::internalCast() const; template RAMSES_API const NodeBinding* RamsesObject::internalCast() const; - template RAMSES_API const AppearanceBinding* RamsesObject::internalCast() const; + template RAMSES_API const AppearanceBinding* RamsesObject::internalCast() const; template RAMSES_API const CameraBinding* RamsesObject::internalCast() const; template RAMSES_API const RenderPassBinding* RamsesObject::internalCast() const; template RAMSES_API const RenderGroupBinding* RamsesObject::internalCast() const; template RAMSES_API const MeshNodeBinding* RamsesObject::internalCast() const; - template RAMSES_API const SkinBinding* RamsesObject::internalCast() const; - template RAMSES_API const DataArray* RamsesObject::internalCast() const; - template RAMSES_API const AnimationNode* RamsesObject::internalCast() const; - template RAMSES_API const TimerNode* RamsesObject::internalCast() const; - template RAMSES_API const AnchorPoint* RamsesObject::internalCast() const; - - template RAMSES_API LogicObject* RamsesObject::internalCast(); - template RAMSES_API LogicNode* RamsesObject::internalCast(); - template RAMSES_API RamsesBinding* RamsesObject::internalCast(); - template RAMSES_API LuaModule* RamsesObject::internalCast(); - template RAMSES_API LuaScript* RamsesObject::internalCast(); - template RAMSES_API LuaInterface* RamsesObject::internalCast(); + template RAMSES_API const SkinBinding* RamsesObject::internalCast() const; + template RAMSES_API const DataArray* RamsesObject::internalCast() const; + template RAMSES_API const AnimationNode* RamsesObject::internalCast() const; + template RAMSES_API const TimerNode* RamsesObject::internalCast() const; + template RAMSES_API const AnchorPoint* RamsesObject::internalCast() const; + + template RAMSES_API LogicObject* RamsesObject::internalCast(); + template RAMSES_API LogicNode* RamsesObject::internalCast(); + template RAMSES_API RamsesBinding* RamsesObject::internalCast(); + template RAMSES_API LuaModule* RamsesObject::internalCast(); + template RAMSES_API LuaScript* RamsesObject::internalCast(); + template RAMSES_API LuaInterface* RamsesObject::internalCast(); template RAMSES_API NodeBinding* RamsesObject::internalCast(); - template RAMSES_API AppearanceBinding* RamsesObject::internalCast(); + template RAMSES_API AppearanceBinding* RamsesObject::internalCast(); template RAMSES_API CameraBinding* RamsesObject::internalCast(); template RAMSES_API RenderPassBinding* RamsesObject::internalCast(); template RAMSES_API RenderGroupBinding* RamsesObject::internalCast(); template RAMSES_API MeshNodeBinding* RamsesObject::internalCast(); - template RAMSES_API SkinBinding* RamsesObject::internalCast(); - template RAMSES_API DataArray* RamsesObject::internalCast(); - template RAMSES_API AnimationNode* RamsesObject::internalCast(); - template RAMSES_API TimerNode* RamsesObject::internalCast(); - template RAMSES_API AnchorPoint* RamsesObject::internalCast(); + template RAMSES_API SkinBinding* RamsesObject::internalCast(); + template RAMSES_API DataArray* RamsesObject::internalCast(); + template RAMSES_API AnimationNode* RamsesObject::internalCast(); + template RAMSES_API TimerNode* RamsesObject::internalCast(); + template RAMSES_API AnchorPoint* RamsesObject::internalCast(); template RAMSES_API const ClientObject* RamsesObject::internalCast() const; template RAMSES_API const RamsesObject* RamsesObject::internalCast() const; diff --git a/tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp b/tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp index 630f26830..02bfd7d92 100644 --- a/tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp +++ b/tests/integration/logic-viewer-tests/LogicViewerAppTest.cpp @@ -608,7 +608,8 @@ namespace ramses::internal public: ALogicViewerAppUI() { - setup(iniFile); + // use --offscreen to get screenshots without ui + setup(iniFile, {"--offscreen"}); } }; @@ -847,7 +848,7 @@ namespace ramses::internal rlogic.screenshot("test_red.png") end )"); - createApp({ "--exec=test_default", "--no-offscreen", ramsesFile }); + createApp({ "--exec=test_default", ramsesFile }); EXPECT_EQ(0, m_app->run()); EXPECT_TRUE(CompareImage("test_red.png", "ALogicViewerApp_red.png")); } @@ -1237,11 +1238,11 @@ rlogic.meshNodeBindings["myMeshNode"]["IN"]["instanceCount"].value = 1)", rtrim( { if (GetParam()) { - setup(iniFile, { "--clear-color", "0", "0", "0.5", "1" }); + setup(iniFile, { "--offscreen", "--clear-color", "0", "0", "0.5", "1" }); } else { - setup(iniFile, { "--no-offscreen", "--clear-color", "0", "0", "0.5", "1" }); + setup(iniFile, { "--clear-color", "0", "0", "0.5", "1" }); } } }; diff --git a/tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp b/tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp index 6e216eea7..d8b9c6236 100644 --- a/tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp +++ b/tests/integration/resource-stress-tests/DynamicQuad_Resources.cpp @@ -90,8 +90,8 @@ namespace ramses::internal } } - ramses::MipLevelData textureData(textureWidth * textureHeight * 3, rawData.get()); - resources.texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, textureWidth, textureHeight, 1, &textureData, false, {}); + const std::vector textureData{ MipLevelData(textureWidth * textureHeight * 3, rawData.get()) }; + resources.texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, textureWidth, textureHeight, textureData, false, {}); resources.textureSampler = m_scene.createTextureSampler( ramses::ETextureAddressMode::Repeat, diff --git a/tests/integration/test-content/CubeTextureScene.cpp b/tests/integration/test-content/CubeTextureScene.cpp index c5f78b90c..3daacb7ba 100644 --- a/tests/integration/test-content/CubeTextureScene.cpp +++ b/tests/integration/test-content/CubeTextureScene.cpp @@ -133,7 +133,7 @@ namespace ramses::internal ramses::internal::Image imageNZ; imageNZ.loadFromFilePNG("res/ramses-test-client-cube-nz.png"); - ramses::CubeMipLevelData mipLevelData( + const std::vector mipLevelData{ CubeMipLevelData( static_cast(imagePX.getData().size()), // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast(imagePX.getData().data()), @@ -146,9 +146,9 @@ namespace ramses::internal // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast(imagePZ.getData().data()), // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - reinterpret_cast(imageNZ.getData().data())); + reinterpret_cast(imageNZ.getData().data())) }; - return m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, imageNY.getWidth(), 1, &mipLevelData, false); + return m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, imageNY.getWidth(), mipLevelData, false); } case EState_BGRA_Swizzled: { @@ -166,7 +166,7 @@ namespace ramses::internal ramses::internal::Image imageNZ; imageNZ.loadFromFilePNG("res/ramses-test-client-cube-nz.png"); - ramses::CubeMipLevelData mipLevelData( + const std::vector mipLevelData{ CubeMipLevelData( static_cast(imagePX.getData().size()), // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast(imagePX.getData().data()), @@ -179,9 +179,9 @@ namespace ramses::internal // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) reinterpret_cast(imagePZ.getData().data()), // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - reinterpret_cast(imageNZ.getData().data())); + reinterpret_cast(imageNZ.getData().data())) }; - return m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, imageNY.getWidth(), 1, &mipLevelData, false, bgraSwizzle); + return m_scene.createTextureCube(ramses::ETextureFormat::RGBA8, imageNY.getWidth(), mipLevelData, false, bgraSwizzle); } case EState_Float: { @@ -192,9 +192,9 @@ namespace ramses::internal 1.0f, 1.0f, 1.0f}; const auto* texturePtr = reinterpret_cast(texture); - ramses::CubeMipLevelData mipLevelData(sizeof(texture), texturePtr, texturePtr, texturePtr, texturePtr, texturePtr, texturePtr); + const std::vector mipLevelData{ CubeMipLevelData(sizeof(texture), texturePtr, texturePtr, texturePtr, texturePtr, texturePtr, texturePtr) }; - return m_scene.createTextureCube(ramses::ETextureFormat::RGB32F, 2, 1, &mipLevelData, false); + return m_scene.createTextureCube(ramses::ETextureFormat::RGB32F, 2, mipLevelData, false); } default: assert(!"Invalid texture type"); diff --git a/tests/integration/test-content/MultiTypeLinkScene.cpp b/tests/integration/test-content/MultiTypeLinkScene.cpp index 47c315651..ccde8a0eb 100644 --- a/tests/integration/test-content/MultiTypeLinkScene.cpp +++ b/tests/integration/test-content/MultiTypeLinkScene.cpp @@ -76,8 +76,8 @@ namespace ramses::internal scene.createTransformationDataConsumer(*groupNode, TransformationConsumerId); const std::array pxData{ {0xff, 0x0, 0x0, 0xff} }; - const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}); + std::vector mipLevelData{ MipLevelData(4, pxData.data()) }; + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}); const ramses::TextureSampler& sampler = createSampler(texture); SetSampler(appearance2, sampler); @@ -90,12 +90,12 @@ namespace ramses::internal colorData->setValue(ramses::vec4f{ 0.f, 1.f, 0.f, 1.f }); ramses::Node* providerNode = scene.createNode(); - providerNode->setTranslation({1.5f, -2.f, 5.f}); + providerNode->setTranslation({ 1.5f, -2.f, 5.f }); scene.createTransformationDataProvider(*providerNode, TransformationProviderId); const std::array pxData{ { 0x0, 0xff, 0x0, 0xff } }; - const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}); + const std::vector mipLevelData{ MipLevelData(4, pxData.data()) }; + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}); const ramses::TextureSampler& sampler = createSampler(texture); SetSampler(appearance2, sampler); diff --git a/tests/integration/test-content/TestScenes/Texture2DFormatScene.h b/tests/integration/test-content/TestScenes/Texture2DFormatScene.h index 42ba2307d..acc00b470 100644 --- a/tests/integration/test-content/TestScenes/Texture2DFormatScene.h +++ b/tests/integration/test-content/TestScenes/Texture2DFormatScene.h @@ -59,6 +59,6 @@ namespace ramses::internal void createOrthoCamera(); void createQuad(const ramses::TextureSampler& sampler); - static const ramses::MipLevelData& GetTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle); + static const std::vector& GetTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle); }; } diff --git a/tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp b/tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp index 48d37d30c..d9f499b1e 100644 --- a/tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp +++ b/tests/integration/test-content/Texture2DAnisotropicTextureFilteringScene.cpp @@ -55,7 +55,7 @@ namespace ramses::internal 0x00, 0x00, 0xff }; - const ramses::MipLevelData mipLevelData[] = { + const std::vector mipLevelData = { ramses::MipLevelData(sizeof(rgb8_level0), rgb8_level0), ramses::MipLevelData(sizeof(rgb8_level1), rgb8_level1), ramses::MipLevelData(sizeof(rgb8_level2), rgb8_level2) @@ -94,7 +94,6 @@ namespace ramses::internal ramses::Texture2D* texture = m_scene.createTexture2D( ramses::ETextureFormat::RGB8, 4, 4, - 3, mipLevelData, false); diff --git a/tests/integration/test-content/Texture2DCompressedMipMapScene.cpp b/tests/integration/test-content/Texture2DCompressedMipMapScene.cpp index 96a870bbb..32a82ce32 100644 --- a/tests/integration/test-content/Texture2DCompressedMipMapScene.cpp +++ b/tests/integration/test-content/Texture2DCompressedMipMapScene.cpp @@ -36,11 +36,10 @@ namespace ramses::internal const uint8_t dataLevel1[] = {0x7e, 0x80, 0x4, 0x7f, 0x0, 0x7, 0xe0, 0x0}; // 4x4: red - const ramses::MipLevelData mipLevelData[NumMipMaps] = { { 32u, dataLevel0 }, { 8u, dataLevel1 } }; + const std::vector mipLevelData = { MipLevelData{ 32u, dataLevel0 }, MipLevelData{ 8u, dataLevel1 } }; const ramses::Texture2D* texture = m_scene.createTexture2D( ramses::ETextureFormat::ETC2RGB, m_textureWidth, m_textureHeight, - NumMipMaps, mipLevelData, false); diff --git a/tests/integration/test-content/Texture2DFormatScene.cpp b/tests/integration/test-content/Texture2DFormatScene.cpp index 0a07adfb5..84776475f 100644 --- a/tests/integration/test-content/Texture2DFormatScene.cpp +++ b/tests/integration/test-content/Texture2DFormatScene.cpp @@ -31,7 +31,7 @@ const uint16_t rgba4Data[] = 0x00ff, 0xfff7 }; -const ramses::MipLevelData mipLevelData_rgba4(sizeof(rgba4Data), reinterpret_cast(rgba4Data)); +const std::vector mipLevelData_rgba4{ ramses::MipLevelData(sizeof(rgba4Data), reinterpret_cast(rgba4Data)) }; const uint16_t rgba5551Data[] = { @@ -40,7 +40,7 @@ const uint16_t rgba5551Data[] = 0x003f, 0xfffe }; -const ramses::MipLevelData mipLevelData_rgba5551(sizeof(rgba5551Data), reinterpret_cast(rgba5551Data)); +const std::vector mipLevelData_rgba5551{ ramses::MipLevelData(sizeof(rgba5551Data), reinterpret_cast(rgba5551Data)) }; const uint8_t rgba8Data[] = { @@ -49,7 +49,7 @@ const uint8_t rgba8Data[] = 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }; -const ramses::MipLevelData mipLevelData_rgba8(sizeof(rgba8Data), reinterpret_cast(rgba8Data)); +const std::vector mipLevelData_rgba8{ ramses::MipLevelData(sizeof(rgba8Data), reinterpret_cast(rgba8Data)) }; const uint16_t rgba565Data[] = { @@ -58,7 +58,7 @@ const uint16_t rgba565Data[] = 0x001f, 0xffff }; -const ramses::MipLevelData mipLevelData_rgba565(sizeof(rgba565Data), reinterpret_cast(rgba565Data)); +const std::vector mipLevelData_rgba565{ ramses::MipLevelData(sizeof(rgba565Data), reinterpret_cast(rgba565Data)) }; const uint8_t rgb8Data[] = { @@ -67,7 +67,7 @@ const uint8_t rgb8Data[] = 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; -const ramses::MipLevelData mipLevelData_rgb8(sizeof(rgb8Data), reinterpret_cast(rgb8Data)); +const std::vector mipLevelData_rgb8{ ramses::MipLevelData(sizeof(rgb8Data), reinterpret_cast(rgb8Data)) }; const uint8_t rg8Data[] = { @@ -76,7 +76,7 @@ const uint8_t rg8Data[] = 0x7f, 0xbf, // 0.50, 0.75 0x7f, 0x3f }; // 0.50, 0.25 -const ramses::MipLevelData mipLevelData_rg8(sizeof(rg8Data), reinterpret_cast(rg8Data)); +const std::vector mipLevelData_rg8{ ramses::MipLevelData(sizeof(rg8Data), reinterpret_cast(rg8Data)) }; const uint8_t r8Data[] = { @@ -85,7 +85,7 @@ const uint8_t r8Data[] = 0x7f, // 0.50 0x3f }; // 0.25 -const ramses::MipLevelData mipLevelData_r8(sizeof(r8Data), reinterpret_cast(r8Data)); +const std::vector mipLevelData_r8{ ramses::MipLevelData(sizeof(r8Data), reinterpret_cast(r8Data)) }; const uint8_t bgr8Data[] = { @@ -94,7 +94,7 @@ const uint8_t bgr8Data[] = 0xff, 0x00, 0x00, 0xff, 0xff, 0xff }; -const ramses::MipLevelData mipLevelData_bgr8(sizeof(bgr8Data), reinterpret_cast(bgr8Data)); +const std::vector mipLevelData_bgr8{ ramses::MipLevelData(sizeof(bgr8Data), reinterpret_cast(bgr8Data)) }; const uint8_t bgra8Data[] = { @@ -103,20 +103,20 @@ const uint8_t bgra8Data[] = 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x7f }; -const ramses::MipLevelData mipLevelData_bgra8(sizeof(bgra8Data), reinterpret_cast(bgra8Data)); +const std::vector mipLevelData_bgra8{ ramses::MipLevelData(sizeof(bgra8Data), reinterpret_cast(bgra8Data)) }; const uint64_t ETC2RGB[] = { 0xefee1f11fd7fbb7b }; -const ramses::MipLevelData mipLevelData_etc2rgb(sizeof(ETC2RGB), reinterpret_cast(ETC2RGB)); +const std::vector mipLevelData_etc2rgb{ ramses::MipLevelData(sizeof(ETC2RGB), reinterpret_cast(ETC2RGB)) }; const uint64_t ETC2RGBA[] = { 0xefee1f11fd7fbb7b, 0xefee0f00efcaf004 }; -const ramses::MipLevelData mipLevelData_etc2rgba(ramses::MipLevelData(sizeof(ETC2RGBA), reinterpret_cast(ETC2RGBA))); +const std::vector mipLevelData_etc2rgba{ ramses::MipLevelData(sizeof(ETC2RGBA), reinterpret_cast(ETC2RGBA)) }; const uint8_t r16fData[] = // Value (sign, exponent, fraction) { @@ -125,7 +125,7 @@ const uint8_t r16fData[] = // Value (sign, exponent, fraction) 0x0, 0x34, // 0.25 (0 01101 0000000000) 0x0, 0x0 // 1.0 (0 00000 0000000000) }; -const ramses::MipLevelData mipLevelData_r16fData(sizeof(r16fData), reinterpret_cast(r16fData)); +const std::vector mipLevelData_r16fData{ ramses::MipLevelData(sizeof(r16fData), reinterpret_cast(r16fData)) }; const float r32fData[] = { @@ -134,21 +134,21 @@ const float r32fData[] = 0.25f, 0.0f }; -const ramses::MipLevelData mipLevelData_r32fData(sizeof(r32fData), reinterpret_cast(r32fData)); +const std::vector mipLevelData_r32fData{ ramses::MipLevelData(sizeof(r32fData), reinterpret_cast(r32fData)) }; // 4x4 block with various colors encoded as ASTC 4x4 RGBA const uint8_t astcRGBA4x4Data[] = { 0xDE, 0x69, 0x1C, 0x10, 0x05, 0x42, 0x0B, 0x82, 0x20, 0x00, 0x15, 0x00, 0x00, 0x04, 0x00, 0x60 }; -const ramses::MipLevelData mipLevelData_astcRGBA4x4Data(sizeof(astcRGBA4x4Data), reinterpret_cast(astcRGBA4x4Data)); +const std::vector mipLevelData_astcRGBA4x4Data{ ramses::MipLevelData(sizeof(astcRGBA4x4Data), reinterpret_cast(astcRGBA4x4Data)) }; // Similar as above, but now encoded as SRGBA const uint8_t astcSRGB_Alpha4x4Data[] = { 0xDE, 0x09, 0x2C, 0x50, 0x00, 0x02, 0x0A, 0x82, 0x20, 0x00, 0x0B, 0x00, 0xA0, 0xC7, 0x02, 0xF8 }; -const ramses::MipLevelData mipLevelData_astcSRGB_Alpha_4x4Data(sizeof(astcSRGB_Alpha4x4Data), reinterpret_cast(astcSRGB_Alpha4x4Data)); +const std::vector mipLevelData_astcSRGB_Alpha_4x4Data{ ramses::MipLevelData(sizeof(astcSRGB_Alpha4x4Data), reinterpret_cast(astcSRGB_Alpha4x4Data)) }; const uint8_t rg16fData[] = { @@ -157,7 +157,7 @@ const uint8_t rg16fData[] = 0x0, 0x0, 0x0, 0x0, // 0, 0 0x0, 0x3C, 0x0, 0x3C // 1, 1 }; -const ramses::MipLevelData mipLevelData_rg16fData(sizeof(rg16fData), reinterpret_cast(rg16fData)); +const std::vector mipLevelData_rg16fData{ ramses::MipLevelData(sizeof(rg16fData), reinterpret_cast(rg16fData)) }; const float rg32fData[] = { @@ -166,7 +166,7 @@ const float rg32fData[] = 0.0f, 0.0f, 1.0f, 1.0f }; -const ramses::MipLevelData mipLevelData_rg32fData(sizeof(rg32fData), reinterpret_cast(rg32fData)); +const std::vector mipLevelData_rg32fData{ ramses::MipLevelData(sizeof(rg32fData), reinterpret_cast(rg32fData)) }; const uint8_t rgb16fData[] = { @@ -175,7 +175,7 @@ const uint8_t rgb16fData[] = 0x0, 0x0, 0x0, 0x0, 0x0, 0x3C, // 0, 0, 1 0x0, 0x3C, 0x0, 0x3C, 0x0, 0x3C // 1, 1, 1 }; -const ramses::MipLevelData mipLevelData_rgb16fData(sizeof(rgb16fData), reinterpret_cast(rgb16fData)); +const std::vector mipLevelData_rgb16fData{ ramses::MipLevelData(sizeof(rgb16fData), reinterpret_cast(rgb16fData)) }; const float rgb32fData[] = { @@ -184,7 +184,7 @@ const float rgb32fData[] = 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f }; -const ramses::MipLevelData mipLevelData_rgb32fData(sizeof(rgb32fData), reinterpret_cast(rgb32fData)); +const std::vector mipLevelData_rgb32fData{ ramses::MipLevelData(sizeof(rgb32fData), reinterpret_cast(rgb32fData)) }; const uint8_t rgba16fData[] = { @@ -193,7 +193,7 @@ const uint8_t rgba16fData[] = 0x0, 0x0, 0x0, 0x0, 0x0, 0x3C, 0x0, 0x3C, // 0, 0, 1, 1 0x0, 0x3C, 0x0, 0x3C, 0x0, 0x3C, 0x0, 0x3C // 1, 1, 1, 1 }; -const ramses::MipLevelData mipLevelData_rgba16fData(sizeof(rgba16fData), reinterpret_cast(rgba16fData)); +const std::vector mipLevelData_rgba16fData{ ramses::MipLevelData(sizeof(rgba16fData), reinterpret_cast(rgba16fData)) }; const float rgba32fData[] = { @@ -202,7 +202,7 @@ const float rgba32fData[] = 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; -const ramses::MipLevelData mipLevelData_rgba32fData(sizeof(rgba32fData), reinterpret_cast(rgba32fData)); +const std::vector mipLevelData_rgba32fData{ ramses::MipLevelData(sizeof(rgba32fData), reinterpret_cast(rgba32fData)) }; const uint8_t srgb8Data[] = { @@ -211,7 +211,7 @@ const uint8_t srgb8Data[] = 0x00, 0x00, 0x88, 0x88, 0x88, 0x88 }; -const ramses::MipLevelData mipLevelData_srgb8Data(sizeof(srgb8Data), reinterpret_cast(srgb8Data)); +const std::vector mipLevelData_srgb8Data{ ramses::MipLevelData(sizeof(srgb8Data), reinterpret_cast(srgb8Data)) }; const uint8_t srgb8a8Data[] = { @@ -220,9 +220,9 @@ const uint8_t srgb8a8Data[] = 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0xff }; -const ramses::MipLevelData mipLevelData_srgb8a8Data(sizeof(srgb8a8Data), reinterpret_cast(srgb8a8Data)); +const std::vector mipLevelData_srgb8a8Data{ ramses::MipLevelData(sizeof(srgb8a8Data), reinterpret_cast(srgb8a8Data)) }; -const ramses::MipLevelData mipLevelData_null; +const std::vector mipLevelData_null; namespace ramses::internal { @@ -236,14 +236,13 @@ namespace ramses::internal uint32_t height = 0u; ramses::TextureSwizzle swizzle = {}; - const ramses::MipLevelData& mipLevelData = GetTextureFormatAndData(static_cast(state), format, width, height, swizzle); + const std::vector& mipLevelData = GetTextureFormatAndData(static_cast(state), format, width, height, swizzle); ramses::Texture2D* texture = m_scene.createTexture2D( format, width, height, - 1, - &mipLevelData, + mipLevelData, false, swizzle); @@ -305,7 +304,7 @@ namespace ramses::internal mesh->setGeometry(*geometry); } - const ramses::MipLevelData& Texture2DFormatScene::GetTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle) + const std::vector& Texture2DFormatScene::GetTextureFormatAndData(EState state, ramses::ETextureFormat& format, uint32_t& width, uint32_t& height, ramses::TextureSwizzle& swizzle) { width = 2u; height = 2u; diff --git a/tests/integration/test-content/Texture2DGenerateMipMapScene.cpp b/tests/integration/test-content/Texture2DGenerateMipMapScene.cpp index 39f739b25..cfd073563 100644 --- a/tests/integration/test-content/Texture2DGenerateMipMapScene.cpp +++ b/tests/integration/test-content/Texture2DGenerateMipMapScene.cpp @@ -185,14 +185,13 @@ namespace ramses::internal rgba8_level0[idx + 3] = 0xff - transparency; } - const ramses::MipLevelData mipLevelData[] = { + const std::vector mipLevelData = { ramses::MipLevelData(width*height*4u*sizeof(uint8_t), rgba8_level0) }; ramses::Texture2D* texture = m_scene.createTexture2D( ramses::ETextureFormat::RGBA8, width, height, - 1, mipLevelData, true); diff --git a/tests/integration/test-content/Texture2DSamplingScene.cpp b/tests/integration/test-content/Texture2DSamplingScene.cpp index 556cefe57..c1e906a98 100644 --- a/tests/integration/test-content/Texture2DSamplingScene.cpp +++ b/tests/integration/test-content/Texture2DSamplingScene.cpp @@ -52,7 +52,7 @@ namespace ramses::internal 0x00,0xff,0x00 }; - const ramses::MipLevelData mipLevelData[] = { + const std::vector mipLevelData = { ramses::MipLevelData(sizeof(rgb8_level0), rgb8_level0), ramses::MipLevelData(sizeof(rgb8_level1), rgb8_level1), ramses::MipLevelData(sizeof(rgb8_level2), rgb8_level2) @@ -61,7 +61,6 @@ namespace ramses::internal ramses::Texture2D* texture = m_scene.createTexture2D( ramses::ETextureFormat::RGB8, 4, 4, - 3, mipLevelData, false); diff --git a/tests/integration/test-content/Texture3DScene.cpp b/tests/integration/test-content/Texture3DScene.cpp index 86e6b7724..f82ab4966 100644 --- a/tests/integration/test-content/Texture3DScene.cpp +++ b/tests/integration/test-content/Texture3DScene.cpp @@ -63,7 +63,7 @@ namespace ramses::internal 0xff, 0xff, 0x00, }; - const ramses::MipLevelData mipLevelData[] = { + const std::vector mipLevelData = { ramses::MipLevelData(sizeof(rgb8Data_0), rgb8Data_0), ramses::MipLevelData(sizeof(rgb8Data_1), rgb8Data_1), ramses::MipLevelData(sizeof(rgb8Data_2), rgb8Data_2) @@ -72,7 +72,6 @@ namespace ramses::internal m_texture = m_scene.createTexture3D( ramses::ETextureFormat::RGB8, 2, 2, 4, - 3, mipLevelData, false); diff --git a/tests/integration/test-content/TextureBufferScene.cpp b/tests/integration/test-content/TextureBufferScene.cpp index 1be93ec06..3d350f53e 100644 --- a/tests/integration/test-content/TextureBufferScene.cpp +++ b/tests/integration/test-content/TextureBufferScene.cpp @@ -205,8 +205,8 @@ namespace ramses::internal case EState_ClientTextureResource_RGBA8: { const ramses::MipLevelData mip2x2(sizeof(rgba_2x2_green), rgba_2x2_green); - const ramses::MipLevelData mips[] = { mip2x2 }; - m_clientTexture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2u, 2u, 1u, mips); + const std::vector mips = { mip2x2 }; + m_clientTexture = m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 2u, 2u, mips); sampler = m_scene.createTextureSampler(ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Nearest_MipMapNearest, ramses::ETextureSamplingMethod::Nearest, *m_clientTexture); mipToFetch = 0; break; diff --git a/tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp b/tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp index 744e5cc43..4960dc73a 100644 --- a/tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp +++ b/tests/integration/test-content/TextureCubeAnisotropicTextureFilteringScene.cpp @@ -121,7 +121,7 @@ namespace ramses::internal /// Vertically, 1 texel map to one pixel => IntegrationScene::DefaultViewportHeight texels needed. /// Cube map textures are of square size, so take larger value. const uint32_t numberOfMipLevels = GetNextLargerPowerOf2Exponent(std::max(IntegrationScene::DefaultViewportWidth * 2, uint32_t(IntegrationScene::DefaultViewportHeight))) + 1; - auto* mipLevelData = new ramses::CubeMipLevelData[numberOfMipLevels]; + std::vector mipLevelData(numberOfMipLevels); const uint32_t textureResolution = 1u << (numberOfMipLevels - 1); @@ -132,13 +132,15 @@ namespace ramses::internal auto* rgb8_data = new uint8_t[levelSize]; FillMipLevelData(rgb8_data, resolution, i); - mipLevelData[i].m_faceDataSize = levelSize; - mipLevelData[i].m_dataNX = reinterpret_cast(rgb8_data); - mipLevelData[i].m_dataPX = reinterpret_cast(rgb8_data); - mipLevelData[i].m_dataNY = reinterpret_cast(rgb8_data); - mipLevelData[i].m_dataPY = reinterpret_cast(rgb8_data); - mipLevelData[i].m_dataNZ = reinterpret_cast(rgb8_data); - mipLevelData[i].m_dataPZ = reinterpret_cast(rgb8_data); + mipLevelData.emplace_back(CubeMipLevelData( + levelSize, + reinterpret_cast(rgb8_data), + reinterpret_cast(rgb8_data), + reinterpret_cast(rgb8_data), + reinterpret_cast(rgb8_data), + reinterpret_cast(rgb8_data), + reinterpret_cast(rgb8_data) + )); } ramses::Effect* effect(getTestEffect("ramses-test-client-textured-cube")); @@ -174,15 +176,14 @@ namespace ramses::internal ramses::TextureCube* texture = m_scene.createTextureCube( ramses::ETextureFormat::RGB8, textureResolution, - numberOfMipLevels, mipLevelData, false); - for (uint32_t i = 0; i < numberOfMipLevels; i++) + for (auto& data : mipLevelData) { - delete[] mipLevelData[i].m_dataNX; + delete[] data.m_dataNX; } - delete[] mipLevelData; + mipLevelData.clear(); std::optional textureInput = effect->findUniformInput("u_texture"); assert(textureInput.has_value()); diff --git a/tests/integration/test-content/TextureLinkScene.cpp b/tests/integration/test-content/TextureLinkScene.cpp index 3fa6b7770..d69dc5c93 100644 --- a/tests/integration/test-content/TextureLinkScene.cpp +++ b/tests/integration/test-content/TextureLinkScene.cpp @@ -89,8 +89,8 @@ namespace ramses::internal case DATA_PROVIDER: { const std::array pxData{ {0xff, 0x0, 0x0, 0xff} }; - const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "ProviderTexture"); + const std::vector mipLevelData{ MipLevelData(4, pxData.data()) }; + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, "ProviderTexture"); const ramses::TextureSampler& sampler = createSampler(texture); SetSampler(appearance1, sampler); SetSampler(appearance2, sampler); @@ -101,8 +101,8 @@ namespace ramses::internal case DATA_CONSUMER: { const std::array pxData{ { 0x0, 0xff, 0x0, 0xff } }; - const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "ConsumerTexture"); + const std::vector mipLevelData{ MipLevelData(4, pxData.data()) }; + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, "ConsumerTexture"); const ramses::TextureSampler& sampler = createSampler(texture); SetSampler(appearance1, sampler); SetSampler(appearance2, sampler); @@ -143,8 +143,8 @@ namespace ramses::internal case DATA_CONSUMER_AND_PROVIDER: { const std::array pxData{ { 0x0, 0x0, 0xff, 0xff } }; - const ramses::MipLevelData mipLevelData(4, pxData.data()); - const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "ConsumerProviderTexture"); + const std::vector mipLevelData{ MipLevelData(4, pxData.data()) }; + const ramses::Texture2D& texture = *m_scene.createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, "ConsumerProviderTexture"); const ramses::TextureSampler& sampler1 = createSampler(texture); const ramses::TextureSampler& sampler2 = createSampler(texture); SetSampler(appearance1, sampler1); diff --git a/tests/integration/test-content/TextureSamplerScene.cpp b/tests/integration/test-content/TextureSamplerScene.cpp index b2e1a95a7..63e5e927f 100644 --- a/tests/integration/test-content/TextureSamplerScene.cpp +++ b/tests/integration/test-content/TextureSamplerScene.cpp @@ -36,11 +36,11 @@ namespace ramses::internal 0xff,0xff,0xff, 0x00,0x00,0x00, 0xff,0x00,0x00, 0x00,0x00,0xff, }; - const ramses::MipLevelData mipLevelData[] = { { sizeof(rgb8), rgb8 } }; + const std::vector mipLevelData = { MipLevelData{ sizeof(rgb8), rgb8 } }; if (state == EState::EState_ClientTexture) { - const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 2, 2, 1, mipLevelData, false); + const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 2, 2, mipLevelData, false); m_sampler = m_scene.createTextureSampler(ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, ramses::ETextureSamplingMethod::Nearest, ramses::ETextureSamplingMethod::Nearest, *texture); } else if (state == EState::EState_TextureBuffer) @@ -104,8 +104,8 @@ namespace ramses::internal { case EState::EState_SetClientTexture: { - const ramses::MipLevelData mipLevelData[] = { { sizeof(rgb8), rgb8 } }; - const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 3, 3, 1, mipLevelData, false); + const std::vector mipLevelData = { MipLevelData{ sizeof(rgb8), rgb8 } }; + const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 3, 3, mipLevelData, false); m_sampler->setTextureData(*texture); break; } @@ -137,9 +137,9 @@ namespace ramses::internal case EState_SetTextureSampler: { - const ramses::MipLevelData mipLevelData[] = { { sizeof(rgb8), rgb8 } }; + const std::vector mipLevelData = { MipLevelData{ sizeof(rgb8), rgb8 } }; - const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 3, 3, 1, mipLevelData, false); + const ramses::Texture2D* texture = m_scene.createTexture2D(ramses::ETextureFormat::RGB8, 3, 3, mipLevelData, false); m_sampler = m_scene.createTextureSampler(ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, ramses::ETextureSamplingMethod::Nearest, ramses::ETextureSamplingMethod::Nearest, *texture); m_appearance->setInputTexture(*m_effect->findUniformInput("u_texture"), *m_sampler); break; diff --git a/tests/unittests/client/AppearanceTest.cpp b/tests/unittests/client/AppearanceTest.cpp index e6ace6862..da2033cb8 100644 --- a/tests/unittests/client/AppearanceTest.cpp +++ b/tests/unittests/client/AppearanceTest.cpp @@ -64,8 +64,8 @@ namespace ramses::internal EXPECT_TRUE(info.input.has_value()); const uint8_t data[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, data); - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(3u, data) }; + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, mipData, false); EXPECT_TRUE(texture != nullptr); info.texture2D = texture; @@ -94,8 +94,8 @@ namespace ramses::internal EXPECT_TRUE(info.input.has_value()); const uint8_t data[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, data); - Texture3D* texture = sharedTestState->getScene().createTexture3D(ETextureFormat::RGB8, 1u, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(3u, data) }; + Texture3D* texture = sharedTestState->getScene().createTexture3D(ETextureFormat::RGB8, 1u, 1u, 1u, mipData, false); EXPECT_TRUE(texture != nullptr); info.texture3D = texture; @@ -110,8 +110,8 @@ namespace ramses::internal EXPECT_TRUE(info.input.has_value()); const std::byte data[] = { std::byte{1}, std::byte{2}, std::byte{3} }; - const CubeMipLevelData mipData(3u, data, data, data, data, data, data); - TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, 1u, &mipData, false); + std::vector mipData{ CubeMipLevelData(3u, data, data, data, data, data, data) }; + TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, mipData, false); EXPECT_TRUE(texture != nullptr); info.textureCube = texture; @@ -347,9 +347,9 @@ namespace ramses::internal ASSERT_TRUE(optUniform.has_value()); const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); + const std::vector mipData{ MipLevelData(3u, texData) }; - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, mipData, false); ASSERT_TRUE(texture != nullptr); ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(textureSampler != nullptr); @@ -366,10 +366,10 @@ namespace ramses::internal ASSERT_TRUE(optUniform.has_value()); const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); + const std::vector mipData{ MipLevelData(3u, texData) }; ramses::Scene& anotherScene = *sharedTestState->getClient().createScene(sceneId_t(1u)); - Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, mipData, false); ASSERT_TRUE(texture != nullptr); ramses::TextureSampler* textureSampler = anotherScene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(textureSampler != nullptr); @@ -386,8 +386,8 @@ namespace ramses::internal ASSERT_TRUE(optUniform.has_value()); const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(3u, texData) }; + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, mipData, false); ASSERT_TRUE(texture != nullptr); ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(textureSampler != nullptr); @@ -936,9 +936,9 @@ namespace ramses::internal ASSERT_TRUE(optUniform.has_value()); const uint8_t texData[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, texData); + const std::vector mipData{ MipLevelData(3u, texData) }; - Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + Texture2D* texture = sharedTestState->getScene().createTexture2D(ETextureFormat::RGB8, 1u, 1u, mipData, false); ASSERT_TRUE(texture != nullptr); ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(textureSampler != nullptr); @@ -981,9 +981,9 @@ namespace ramses::internal ASSERT_TRUE(optUniform.has_value()); const std::byte texData[] = { std::byte{1}, std::byte{2}, std::byte{3} }; - const CubeMipLevelData mipData(3u, texData, texData, texData, texData, texData, texData); + std::vector mipData{ CubeMipLevelData(3u, texData, texData, texData, texData, texData, texData) }; - TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, 1u, &mipData, false); + TextureCube* texture = sharedTestState->getScene().createTextureCube(ETextureFormat::RGB8, 1u, mipData, false); ASSERT_TRUE(texture != nullptr); ramses::TextureSampler* textureSampler = sharedTestState->getScene().createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Linear, *texture); ASSERT_TRUE(textureSampler != nullptr); diff --git a/tests/unittests/client/CreationHelper.cpp b/tests/unittests/client/CreationHelper.cpp index e481c3aa3..110e10970 100644 --- a/tests/unittests/client/CreationHelper.cpp +++ b/tests/unittests/client/CreationHelper.cpp @@ -125,20 +125,20 @@ namespace ramses::internal template <> Texture2D* CreationHelper::createObjectOfType(std::string_view name) { std::array data = { 0u }; - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - return m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, name); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + return m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, name); } template <> Texture3D* CreationHelper::createObjectOfType(std::string_view name) { std::array data = { 0u }; - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - return m_scene->createTexture3D(ETextureFormat::RGBA8, 1u, 2u, 4u, 1, &mipLevelData, false, name); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + return m_scene->createTexture3D(ETextureFormat::RGBA8, 1u, 2u, 4u, mipLevelData, false, name); } template <> TextureCube* CreationHelper::createObjectOfType(std::string_view name) { std::byte data[4] = { std::byte{0u} }; // NOLINT(modernize-avoid-c-arrays) - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - return m_scene->createTextureCube(ETextureFormat::RGBA8, 1u, 1, &mipLevelData, false, {}, name); + std::vector mipLevelData{ CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + return m_scene->createTextureCube(ETextureFormat::RGBA8, 1u, mipLevelData, false, {}, name); } template <> ArrayResource* CreationHelper::createObjectOfType(std::string_view name) { @@ -165,8 +165,8 @@ namespace ramses::internal template <> ramses::TextureSampler* CreationHelper::createObjectOfType(std::string_view name) { std::array data = { 0u }; - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - Texture2D* texture = m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "texture"); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + Texture2D* texture = m_scene->createTexture2D(ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, "texture"); return m_scene->createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Mirror, ETextureSamplingMethod::Linear, ETextureSamplingMethod::Nearest, *texture, 1u, name); } template <> TextureSamplerMS* CreationHelper::createObjectOfType(std::string_view name) diff --git a/tests/unittests/client/ResourceTest.cpp b/tests/unittests/client/ResourceTest.cpp index 3afaa242a..0e02634ce 100644 --- a/tests/unittests/client/ResourceTest.cpp +++ b/tests/unittests/client/ResourceTest.cpp @@ -60,8 +60,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createTextureAndDestroyManually) { const uint8_t data[4 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(m_scene.destroy(*texture)); } @@ -72,15 +72,15 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(2 * 2 * 4, data); mipLevelData.emplace_back(1 * 1 * 4, data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2, 2, 2, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2, 2, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); } TEST_F(AResourceTestClient, createTextureAndCheckWidthHeight) { const uint8_t data[4 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(10u, texture->getWidth()); EXPECT_EQ(12u, texture->getHeight()); @@ -89,8 +89,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createTextureAndCheckFormat) { const uint8_t data[3 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, {}, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureFormat::RGB8, texture->getTextureFormat()); } @@ -98,9 +98,9 @@ namespace ramses::internal TEST_F(AResourceTestClient, createsTextureWithDefaultSwizzle) { const uint8_t data[3 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; TextureSwizzle swizzle; - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, swizzle, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureChannelColor::Red, swizzle.channelRed); EXPECT_EQ(ETextureChannelColor::Green, swizzle.channelGreen); @@ -112,8 +112,8 @@ namespace ramses::internal { TextureSwizzle swizzle = { ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Red, ETextureChannelColor::Green }; const uint8_t data[3 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, 1, &mipLevelData, false, swizzle, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 10, 12, mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(swizzle.channelRed, texture->getTextureSwizzle().channelRed); EXPECT_EQ(swizzle.channelGreen, texture->getTextureSwizzle().channelGreen); @@ -129,7 +129,7 @@ namespace ramses::internal mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(2 * 2 * 4, data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, 2, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); } @@ -141,7 +141,7 @@ namespace ramses::internal mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(static_cast(sizeof(data)), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 2, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -153,7 +153,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, 1, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -164,7 +164,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(0u, data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, 1, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 4u, 4u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -176,14 +176,14 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(0u, data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } { std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } } @@ -194,29 +194,30 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(2 * 2 * 4, data); mipLevelData.emplace_back(1 * 1 * 2, data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, 2, &mipLevelData[0], false, {}, "name"); + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2u, 2u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureOfZeroSize) { const uint8_t data[4 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 0, 0, 1, &mipLevelData, false, {}, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 0, 0, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureWithNoMipData) { - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 1, 1, 1, nullptr, false, {}, "name"); + std::vector < MipLevelData> mipLevelData; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 1, 1, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createTextureWithoutName) { const uint8_t data[4 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, {}); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(texture->getName().empty()); } @@ -224,8 +225,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createTextureCheckHashIsValid) { const uint8_t data[4 * 10 * 12] = {}; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); @@ -236,15 +237,15 @@ namespace ramses::internal { uint8_t data[4 * 10 * 12] = {}; data[20] = 48; - MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData, false, {}, "name"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); uint8_t data2[4 * 10 * 12] = {}; data[20] = 42; - MipLevelData mipLevelData2(sizeof(data2), data2); - Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, 1, &mipLevelData2, false, {}, "name"); + const std::vector mipLevelData2{ MipLevelData(sizeof(data2), data2) }; + Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::RGBA8, 10, 12, mipLevelData2, false, {}, "name"); ASSERT_TRUE(nullptr != texture2); const ramses::internal::ResourceContentHash hash2 = texture2->impl().getLowlevelResourceHash(); @@ -260,8 +261,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createTextureRGBA_AndCheckTexels) { const auto data = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8); - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2, 1, 1, &mipLevelData, false, {}, {}); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGBA8, 2, 1, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -272,8 +273,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createTextureRGB_AndCheckTexels) { const auto data = make_byte_array(1, 2, 3, 4, 5, 6); - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 2, 1, 1, &mipLevelData, false, {}, {}); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 2, 1, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -286,8 +287,8 @@ namespace ramses::internal { const auto data0 = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); const auto data1 = make_byte_array(13, 14, 15); - const MipLevelData mipLevelData[2] = { { static_cast(data0.size()), data0.data() },{ static_cast(data1.size()), data1.data() } }; - const Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 2, 2, 2, mipLevelData, false, {}, {}); + const std::vector mipLevelData = { MipLevelData{ static_cast(data0.size()), data0.data() }, MipLevelData{ static_cast(data1.size()), data1.data() } }; + const Texture2D* texture = m_scene.createTexture2D(ETextureFormat::RGB8, 2, 2, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); const ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -304,8 +305,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createCubeTextureAndDestroyManually) { const std::byte data[4 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(m_scene.destroy(*texture)); } @@ -316,15 +317,15 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(2 * 2 * 4, data, data, data, data, data, data); mipLevelData.emplace_back(1 * 1 * 4, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2, 2, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); } TEST_F(AResourceTestClient, createCubeTextureAndCheckWidthHeight) { const std::byte data[4 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, "name"); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(10u, texture->getSize()); } @@ -332,8 +333,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, createCubeTextureAndCheckFormat) { const std::byte data[3 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 10, 1, &mipLevelData, false, {}, "name"); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 10, mipLevelData, false, {}, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureFormat::RGB8, texture->getTextureFormat()); } @@ -342,8 +343,8 @@ namespace ramses::internal { TextureSwizzle swizzle; const std::byte data[4 * 10 * 10] = {}; - const CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, swizzle, "name"); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureChannelColor::Red, swizzle.channelRed); EXPECT_EQ(ETextureChannelColor::Green, swizzle.channelGreen); @@ -355,8 +356,8 @@ namespace ramses::internal { TextureSwizzle swizzle = { ETextureChannelColor::Blue, ETextureChannelColor::Alpha, ETextureChannelColor::Red, ETextureChannelColor::Green }; const std::byte data[4 * 10 * 10] = {}; - const CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, swizzle, "name"); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false, swizzle, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(swizzle.channelRed, texture->getTextureSwizzle().channelRed); EXPECT_EQ(swizzle.channelGreen, texture->getTextureSwizzle().channelGreen); @@ -367,30 +368,31 @@ namespace ramses::internal TEST_F(AResourceTestClient, createCubeTextureOfZeroSize) { const std::byte data[4 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 0, 1, &mipLevelData, false, {}, "name"); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 0, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createCubeTextureOfZeroDataSize) { const std::byte data[4 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(0, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, "name"); + std::vector mipLevelData = { CubeMipLevelData(0, data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createCubeTextureWithNoMipData) { - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, nullptr, false, {}, "name"); + std::vector mipLevelData; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, createCubeTextureWithoutName) { const std::byte data[4 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false, {}, {}); + std::vector mipLevelData = { CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(texture->getName().empty()); } @@ -399,8 +401,8 @@ namespace ramses::internal { const auto dataArray = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); const auto* data = dataArray.data(); - CubeMipLevelData mipLevelData(static_cast(dataArray.size()), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2, 1, &mipLevelData, false); + std::vector mipLevelData = { CubeMipLevelData(static_cast(dataArray.size()), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2, mipLevelData, false); ASSERT_TRUE(nullptr != texture); ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -413,8 +415,8 @@ namespace ramses::internal { const auto dataArray = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); const auto* data = dataArray.data(); - CubeMipLevelData mipLevelData(static_cast(dataArray.size()), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 2, 1, &mipLevelData, false); + std::vector mipLevelData = { CubeMipLevelData(static_cast(dataArray.size()), data, data, data, data, data, data) }; + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 2, mipLevelData, false); ASSERT_TRUE(nullptr != texture); ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -438,12 +440,12 @@ namespace ramses::internal const auto data0nz = make_byte_array(14, 24, 34, 44, 54, 64, 74, 84, 94, 104, 114, 124 ); const auto data1nz = make_byte_array(134, 144, 154); - const CubeMipLevelData mipLevelData[2] = + std::vector mipLevelData = { - { static_cast(data0px.size()), data0px.data(), data0nx.data(), data0py.data(), data0ny.data(), data0pz.data(), data0nz.data() }, - { static_cast(data1px.size()), data1px.data(), data1nx.data(), data1py.data(), data1ny.data(), data1pz.data(), data1nz.data() } + CubeMipLevelData{ static_cast(data0px.size()), data0px.data(), data0nx.data(), data0py.data(), data0ny.data(), data0pz.data(), data0nz.data() }, + CubeMipLevelData{ static_cast(data1px.size()), data1px.data(), data1nx.data(), data1py.data(), data1ny.data(), data1pz.data(), data1nz.data() } }; - const TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 2u, 2, mipLevelData, false, {}, {}); + const TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGB8, 2u, mipLevelData, false, {}, {}); ASSERT_TRUE(nullptr != texture); const ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -491,7 +493,7 @@ namespace ramses::internal mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); mipLevelData.emplace_back(2 * 2 * 4, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, 2, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, mipLevelData, false, {}, "name"); EXPECT_NE(nullptr, texture); } @@ -503,7 +505,7 @@ namespace ramses::internal mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 1u, 2, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 1u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -514,7 +516,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, nullptr, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, 1, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -525,7 +527,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(0u, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, 1, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 4u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -536,14 +538,14 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); mipLevelData.emplace_back(0u, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } { std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, data, data); mipLevelData.emplace_back(static_cast(sizeof(data)), data, data, data, data, nullptr, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } } @@ -554,7 +556,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(2 * 2 * 4, data, data, data, data, data, data); mipLevelData.emplace_back(1 * 1 * 2, data, data, data, data, data, data); - TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, 2, &mipLevelData[0], false, {}, "name"); + TextureCube* texture = m_scene.createTextureCube(ETextureFormat::RGBA8, 2u, mipLevelData, false, {}, "name"); EXPECT_EQ(nullptr, texture); } @@ -565,8 +567,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, create3DTextureAndDestroyManually) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, "name"); + const std::vector mipLevelData{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(m_scene.destroy(*texture)); } @@ -577,15 +579,15 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(2 * 2 * 2 * 4, data); mipLevelData.emplace_back(1 * 1 * 1 * 4, data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); } TEST_F(AResourceTestClient, create3DTextureAndCheckWidthHeightDepth) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, "name"); + const std::vector mipLevelData{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(10u, texture->getWidth()); EXPECT_EQ(12u, texture->getHeight()); @@ -595,8 +597,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, create3DTextureAndCheckFormat) { const uint8_t data[3 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 10, 12, 14, 1, &mipLevelData, false, "name"); + const std::vector mipLevelData{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 10, 12, 14, mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); EXPECT_EQ(ETextureFormat::RGB8, texture->getTextureFormat()); } @@ -604,22 +606,23 @@ namespace ramses::internal TEST_F(AResourceTestClient, create3DTextureOfZeroSize) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 0, 0, 0, 1, &mipLevelData, false, "name"); + const std::vector mipLevelData{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 0, 0, 0, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, create3DTextureWithNoMipData) { - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 1, 1, 1, 1, nullptr, false, "name"); + const std::vector mipLevelData; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 1, 1, 1, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } TEST_F(AResourceTestClient, create3DTextureWithoutName) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, {}); + const std::vector mipLevelData{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(texture->getName().empty()); } @@ -627,8 +630,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, create3DTextureCheckHashIsValid) { const uint8_t data[4 * 10 * 12 * 14] = {}; - MipLevelData mipLevelData(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData, false, "name"); + const std::vector mipLevelData{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, mipLevelData, false, "name"); ASSERT_TRUE(nullptr != texture); const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); @@ -639,15 +642,15 @@ namespace ramses::internal { uint8_t data[4 * 10 * 12 * 14] = {}; data[20] = 48; - MipLevelData mipLevelData1(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData1, false, "name"); + const std::vector mipLevelData1{ MipLevelData(static_cast(sizeof(data)), data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, mipLevelData1, false, "name"); ASSERT_TRUE(nullptr != texture); const ramses::internal::ResourceContentHash hash = texture->impl().getLowlevelResourceHash(); uint8_t data2[4 * 10 * 12 * 14] = {}; data[20] = 42; - MipLevelData mipLevelData2(sizeof(data2), data2); - Texture3D* texture2 = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, 1, &mipLevelData2, false, "name"); + const std::vector mipLevelData2{ MipLevelData(sizeof(data2), data2) }; + Texture3D* texture2 = m_scene.createTexture3D(ETextureFormat::RGBA8, 10, 12, 14, mipLevelData2, false, "name"); ASSERT_TRUE(nullptr != texture2); const ramses::internal::ResourceContentHash hash2 = texture2->impl().getLowlevelResourceHash(); @@ -657,8 +660,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, create3DTextureRGBA_AndCheckTexels) { const auto data = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, 1, &mipLevelData, false, {}); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -670,8 +673,8 @@ namespace ramses::internal TEST_F(AResourceTestClient, create3DTextureRGB_AndCheckTexels) { const auto data = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); - MipLevelData mipLevelData(static_cast(data.size()), data.data()); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 2, 1, 2, 1, &mipLevelData, false, {}); + const std::vector mipLevelData{ MipLevelData(static_cast(data.size()), data.data()) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 2, 1, 2, mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -684,8 +687,8 @@ namespace ramses::internal { const auto data0 = make_byte_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120); const auto data1 = make_byte_array(13, 14, 15); - const MipLevelData mipLevelData[2] = { { static_cast(data0.size()), data0.data() },{ static_cast(data1.size()), data1.data() } }; - const Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 2u, 2u, 2u, 2, mipLevelData, false, {}); + const std::vector mipLevelData = { MipLevelData{ static_cast(data0.size()), data0.data() }, MipLevelData{ static_cast(data1.size()), data1.data() } }; + const Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGB8, 2u, 2u, 2u, mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); const ramses::internal::ManagedResource res = getCreatedResource(texture->impl().getLowlevelResourceHash()); @@ -703,7 +706,7 @@ namespace ramses::internal mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(2 * 2 * 2 * 4, data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 4u, 4u, 4u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 4u, 4u, 4u, mipLevelData, false, "name"); EXPECT_NE(nullptr, texture); } @@ -715,7 +718,7 @@ namespace ramses::internal mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(static_cast(sizeof(data)), data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 1u, 1u, 1u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 1u, 1u, 1u, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } @@ -727,7 +730,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 1, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } @@ -738,7 +741,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(0, data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 1, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } @@ -750,14 +753,14 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(0u, data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } { std::vector mipLevelData; mipLevelData.emplace_back(static_cast(sizeof(data)), data); mipLevelData.emplace_back(static_cast(sizeof(data)), dataNullPtr); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } } @@ -768,7 +771,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(2 * 2 * 2 * 4, data); mipLevelData.emplace_back(1 * 1 * 1 * 2, data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::RGBA8, 2u, 2u, 2u, mipLevelData, false, "name"); EXPECT_EQ(nullptr, texture); } @@ -778,7 +781,7 @@ namespace ramses::internal std::vector mipLevelData; mipLevelData.emplace_back(1, data); mipLevelData.emplace_back(1, data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::ASTC_RGBA_4x4, 2u, 2u, 2u, 2, &mipLevelData[0], false, "name"); + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::ASTC_RGBA_4x4, 2u, 2u, 2u, mipLevelData, false, "name"); EXPECT_TRUE(nullptr != texture); } diff --git a/tests/unittests/client/ScenePersistationTest.cpp b/tests/unittests/client/ScenePersistationTest.cpp index b3b4a2316..e411de401 100644 --- a/tests/unittests/client/ScenePersistationTest.cpp +++ b/tests/unittests/client/ScenePersistationTest.cpp @@ -1242,8 +1242,8 @@ namespace ramses::internal const ETextureSamplingMethod minSamplingMethod = ETextureSamplingMethod::Linear_MipMapNearest; const ETextureSamplingMethod magSamplingMethod = ETextureSamplingMethod::Linear; const uint8_t data[4] = { 0u }; - const MipLevelData mipLevelData(sizeof(data), data); - Texture2D* texture = this->m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, "texture"); + const std::vector mipLevelData{ MipLevelData(sizeof(data), data) }; + Texture2D* texture = this->m_scene.createTexture2D(ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, "texture"); auto* sampler = this->m_scene.createTextureSampler(wrapUMode, wrapVMode, minSamplingMethod, magSamplingMethod, *texture, 8u, "sampler"); ASSERT_TRUE(nullptr != sampler); diff --git a/tests/unittests/client/SceneTest.cpp b/tests/unittests/client/SceneTest.cpp index 8f0d5769f..6dbc794cd 100644 --- a/tests/unittests/client/SceneTest.cpp +++ b/tests/unittests/client/SceneTest.cpp @@ -21,9 +21,10 @@ #include "ramses/client/Texture3D.h" #include "ramses/client/OrthographicCamera.h" #include "ramses/client/PerspectiveCamera.h" -#include "ramses/framework/EDataType.h" #include "ramses/client/ArrayBuffer.h" +#include "ramses/client/logic/TimerNode.h" #include "ramses/client/ramses-utils.h" +#include "ramses/framework/EDataType.h" #include "impl/DataObjectImpl.h" #include "impl/RenderGroupImpl.h" @@ -308,8 +309,8 @@ namespace ramses::internal ramses::Scene& anotherScene(*client.createScene(SceneConfig(sceneId_t{ 0xf00 }))); { const uint8_t data[] = { 1, 2, 3 }; - const MipLevelData mipData(3u, data); - Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData({ MipLevelData(3u, data) }); + Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::RGB8, 1u, 1u, mipData, false); ASSERT_TRUE(texture != nullptr); ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -317,8 +318,8 @@ namespace ramses::internal } { const uint8_t data[1 * 2 * 2 * 4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - MipLevelData mipLevelData(sizeof(data), data); - Texture3D* texture = anotherScene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, 1, &mipLevelData, false, {}); + std::vector mipLevelData({ MipLevelData(sizeof(data), data) }); + Texture3D* texture = anotherScene.createTexture3D(ETextureFormat::RGBA8, 2, 1, 2, mipLevelData, false, {}); ASSERT_TRUE(nullptr != texture); ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -326,8 +327,8 @@ namespace ramses::internal } { const std::byte data[4 * 10 * 10] = {}; - CubeMipLevelData mipLevelData(sizeof(data), data, data, data, data, data, data); - TextureCube* texture = anotherScene.createTextureCube(ETextureFormat::RGBA8, 10, 1, &mipLevelData, false); + std::vector mipLevelData{ CubeMipLevelData(sizeof(data), data, data, data, data, data, data) }; + TextureCube* texture = anotherScene.createTextureCube(ETextureFormat::RGBA8, 10, mipLevelData, false); ASSERT_TRUE(nullptr != texture); ramses::TextureSampler* textureSampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -930,8 +931,8 @@ namespace ramses::internal RamsesFramework anotherFramework{config}; ramses::Scene& anotherScene(*client.createScene(SceneConfig(sceneId_t{ 0xf00 }))); uint8_t data = 0u; - MipLevelData mipData(1u, &data); - const Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + const Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); EXPECT_FALSE(m_scene.createTextureProvider(*texture, dataProviderId_t{1u})); @@ -941,8 +942,8 @@ namespace ramses::internal { ramses::Scene& anotherScene = *client.createScene(SceneConfig(sceneId_t(12u))); uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = anotherScene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); const ramses::TextureSampler* sampler = anotherScene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -983,8 +984,8 @@ namespace ramses::internal EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(m_scene.createTextureProvider(*texture, dataProviderId_t{666u})); @@ -1002,13 +1003,13 @@ namespace ramses::internal EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data1 = 0u; - MipLevelData mipData1(1u, &data1); - Texture2D* texture1 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData1, false); + const std::vector mipData1{ MipLevelData(1u, &data1) }; + Texture2D* texture1 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData1, false); ASSERT_TRUE(nullptr != texture1); uint8_t data2 = 1u; - MipLevelData mipData2(1u, &data2); - Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData2, false); + const std::vector mipData2{ MipLevelData(1u, &data2) }; + Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData2, false); ASSERT_TRUE(nullptr != texture2); EXPECT_TRUE(m_scene.createTextureProvider(*texture1, dataProviderId_t{666u})); @@ -1027,8 +1028,8 @@ namespace ramses::internal EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); const ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -1088,8 +1089,8 @@ namespace ramses::internal EXPECT_EQ(0u, m_scene.impl().getIScene().getDataSlotCount()); uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture3D* texture = m_scene.createTexture3D(ETextureFormat::R8, 1u, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture3D* texture = m_scene.createTexture3D(ETextureFormat::R8, 1u, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); const ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -1101,8 +1102,8 @@ namespace ramses::internal TEST_F(AScene, removesDataSlotsOfTextureSamplerOnDestruction) { uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -1122,8 +1123,8 @@ namespace ramses::internal TEST_F(AScene, canNotCreateMoreThanOneConsumerForATextureSampler) { uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -1157,8 +1158,8 @@ namespace ramses::internal TEST_F(AScene, canNotCreateMoreThanOneProviderForATexture) { uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); EXPECT_TRUE(m_scene.createTextureProvider(*texture, dataProviderId_t{666u})); @@ -1168,10 +1169,10 @@ namespace ramses::internal TEST_F(AScene, canNotCreateMoreThanOneTextureConsumerOrProviderWithTheSameId) { uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); - Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + Texture2D* texture2 = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture2); ramses::TextureSampler* sampler = m_scene.createTextureSampler(ETextureAddressMode::Clamp, ETextureAddressMode::Clamp, ETextureSamplingMethod::Nearest, ETextureSamplingMethod::Linear, *texture); @@ -1191,8 +1192,8 @@ namespace ramses::internal TEST_F(AScene, canNotUpdateTextureProviderWhichWasNotCreatedBefore) { uint8_t data = 0u; - MipLevelData mipData(1u, &data); - Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, 1u, &mipData, false); + const std::vector mipData{ MipLevelData(1u, &data) }; + Texture2D* texture = m_scene.createTexture2D(ETextureFormat::R8, 1u, 1u, mipData, false); ASSERT_TRUE(nullptr != texture); EXPECT_FALSE(m_scene.updateTextureProvider(*texture, dataProviderId_t(1u))); @@ -1680,18 +1681,26 @@ namespace ramses::internal TEST_F(AScene, canFindByNameWithSameNameUsedForDifferentTypes) { auto node = m_scene.createNode("test"); - const auto camNode1 = m_scene.createOrthographicCamera("test"); - const auto camNode2 = m_scene.createPerspectiveCamera("test"); - const auto arrBuffer = m_scene.createArrayBuffer(ramses::EDataType::Float, 1u, "test"); + auto camNode1 = m_scene.createOrthographicCamera("test"); + auto camNode2 = m_scene.createPerspectiveCamera("test"); + auto arrBuffer = m_scene.createArrayBuffer(ramses::EDataType::Float, 1u, "test"); + auto logicEngine = m_scene.createLogicEngine("test"); + const auto logicObject = logicEngine->createTimerNode("test"); ASSERT_TRUE(node); ASSERT_TRUE(camNode1); ASSERT_TRUE(camNode2); ASSERT_TRUE(arrBuffer); + ASSERT_TRUE(logicEngine); + ASSERT_TRUE(logicObject); // concrete types EXPECT_EQ(camNode1, m_scene.findObject("test")); EXPECT_EQ(camNode2, m_scene.findObject("test")); EXPECT_EQ(arrBuffer, m_scene.findObject("test")); + EXPECT_EQ(logicEngine, m_scene.findObject("test")); + EXPECT_EQ(logicObject, m_scene.findObject("test")); + + EXPECT_THAT(m_scene.findObject("test"), AnyOf(node, camNode1, camNode2, logicEngine, logicObject)); // this must be one of the 2 cameras EXPECT_THAT(m_scene.findObject("test"), AnyOf(camNode1, camNode2)); @@ -1706,5 +1715,12 @@ namespace ramses::internal EXPECT_TRUE(m_scene.destroy(*node)); // also now this must be one of the 2 cameras EXPECT_THAT(m_scene.findObject("test"), AnyOf(camNode1, camNode2)); + + EXPECT_TRUE(camNode1->setName("other")); + EXPECT_TRUE(camNode2->setName("other")); + EXPECT_TRUE(arrBuffer->setName("other")); + EXPECT_TRUE(logicEngine->setName("other")); + // only one remaining scene object with test name + EXPECT_EQ(logicObject, m_scene.findObject("test")); } } diff --git a/tests/unittests/client/logic/api/LogicObjectTest.cpp b/tests/unittests/client/logic/api/LogicObjectTest.cpp index ba107a7ff..aaed486f3 100644 --- a/tests/unittests/client/logic/api/LogicObjectTest.cpp +++ b/tests/unittests/client/logic/api/LogicObjectTest.cpp @@ -99,7 +99,7 @@ namespace ramses::internal EXPECT_EQ(&this->m_ramses.getFramework(), &obj->impl().getClientImpl().getFramework().getHLRamsesFramework()); } - TYPED_TEST(LogicOwnershipTest, canFindCreatedObjectInLogicEngine) + TYPED_TEST(LogicOwnershipTest, canFindCreatedObjectInLogicEngineAndScene) { { // non-const const auto* obj = this->template createObjectOfType("objectName"); @@ -107,9 +107,13 @@ namespace ramses::internal EXPECT_EQ(obj, this->m_logicEngine->template findObject("objectName")); EXPECT_EQ(obj, this->m_logicEngine->template findObject("objectName")); + EXPECT_EQ(obj, this->m_scene->template findObject("objectName")); + EXPECT_EQ(obj, this->m_scene->template findObject("objectName")); EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_scene->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_scene->template findObject(obj->getSceneObjectId())); const auto coll = this->m_logicEngine->template getCollection(); EXPECT_TRUE(std::find(coll.cbegin(), coll.cend(), obj) != coll.cend()); @@ -121,12 +125,17 @@ namespace ramses::internal const auto* obj = this->template createObjectOfType("objectName2"); ASSERT_TRUE(obj); const auto* logicEngineConst = this->m_logicEngine; + const auto* sceneConst = this->m_scene; EXPECT_EQ(obj, logicEngineConst->template findObject("objectName2")); EXPECT_EQ(obj, logicEngineConst->template findObject("objectName2")); + EXPECT_EQ(obj, sceneConst->template findObject("objectName2")); + EXPECT_EQ(obj, sceneConst->template findObject("objectName2")); EXPECT_EQ(obj, logicEngineConst->template findObject(obj->getSceneObjectId())); EXPECT_EQ(obj, logicEngineConst->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, sceneConst->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, sceneConst->template findObject(obj->getSceneObjectId())); const auto coll = logicEngineConst->template getCollection(); EXPECT_TRUE(std::find(coll.cbegin(), coll.cend(), obj) != coll.cend()); @@ -266,9 +275,13 @@ namespace ramses::internal EXPECT_EQ(obj, this->m_logicEngine->template findObject("newName")); EXPECT_EQ(obj, this->m_logicEngine->template findObject("newName")); + EXPECT_EQ(obj, this->m_scene->template findObject("newName")); + EXPECT_EQ(obj, this->m_scene->template findObject("newName")); EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); EXPECT_EQ(obj, this->m_logicEngine->template findObject(obj->getSceneObjectId())); EXPECT_EQ(obj, this->m_logicEngine->findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_scene->template findObject(obj->getSceneObjectId())); + EXPECT_EQ(obj, this->m_scene->template findObject(obj->getSceneObjectId())); const auto coll = this->m_logicEngine->template getCollection(); EXPECT_TRUE(std::find(coll.cbegin(), coll.cend(), obj) != coll.cend()); diff --git a/tools/ramses-imgui/src/ImguiClientHelper.cpp b/tools/ramses-imgui/src/ImguiClientHelper.cpp index 70621dff2..b98378ffc 100644 --- a/tools/ramses-imgui/src/ImguiClientHelper.cpp +++ b/tools/ramses-imgui/src/ImguiClientHelper.cpp @@ -172,8 +172,8 @@ namespace ramses::internal int textureheight = 0; unsigned char* pixels = nullptr; io.Fonts->GetTexDataAsRGBA32(&pixels, &texturewidth, &textureheight); - ramses::MipLevelData mipLevelData(static_cast(texturewidth * textureheight * 4), pixels); - auto texture = m_imguiscene->createTexture2D(ramses::ETextureFormat::RGBA8, texturewidth, textureheight, 1, &mipLevelData); + const std::vector mipLevelData{ MipLevelData(static_cast(texturewidth * textureheight * 4), pixels) }; + auto texture = m_imguiscene->createTexture2D(ramses::ETextureFormat::RGBA8, texturewidth, textureheight, mipLevelData); sampler = m_imguiscene->createTextureSampler( ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, diff --git a/tools/ramses-imgui/src/ImguiImageCache.cpp b/tools/ramses-imgui/src/ImguiImageCache.cpp index 9af7772b2..d77fdf583 100644 --- a/tools/ramses-imgui/src/ImguiImageCache.cpp +++ b/tools/ramses-imgui/src/ImguiImageCache.cpp @@ -30,8 +30,7 @@ namespace ramses::internal auto* texture = m_scene->createTexture2D(TextureUtils::GetTextureFormatFromInternal(format), mm.width, mm.height, - static_cast(mipLevelData.size()), - &mipLevelData[0], + mipLevelData, false, textureSwizzle); image.width = mm.width; @@ -83,8 +82,7 @@ namespace ramses::internal ramses::Texture2D* texture = m_scene->createTexture2D(TextureUtils::GetTextureFormatFromInternal(res->getTextureFormat()), res->getWidth(), res->getHeight(), - static_cast(mipLevelData.size()), - &mipLevelData[0], + mipLevelData, false, textureSwizzle); diff --git a/tools/ramses-logic-viewer/Arguments.h b/tools/ramses-logic-viewer/Arguments.h index 8611ca68f..dafdcf636 100644 --- a/tools/ramses-logic-viewer/Arguments.h +++ b/tools/ramses-logic-viewer/Arguments.h @@ -38,7 +38,7 @@ Loads and shows a ramses scene from the . ->expected(0, 1) ->type_name("[FILE]") ->excludes(exec); - cli.add_flag("--no-offscreen", m_noOffscreen, "Renders the scene directly to the window's framebuffer. Screenshot size will be the current window size."); + cli.add_flag("--offscreen", m_offscreen, "Renders the scene to an offscreen buffer. Screenshot apply to the offscreen buffer."); cli.set_version_flag("--version", ramses_sdk::RAMSES_SDK_RAMSES_VERSION); std::vector> loglevelMap{{"off", ramses::ELogLevel::Off}, @@ -75,9 +75,9 @@ Loads and shows a ramses scene from the . return m_exec; } - bool noOffscreen() const + bool offscreen() const { - return m_noOffscreen; + return m_offscreen; } bool writeConfig() const @@ -102,7 +102,7 @@ Loads and shows a ramses scene from the . mutable std::string m_luaFile; std::string m_luaFunction; std::string m_exec; - bool m_noOffscreen = false; + bool m_offscreen = false; bool m_writeConfig = false; ramses::ELogLevel m_ramsesLogLevel = ramses::ELogLevel::Error; }; diff --git a/tools/ramses-logic-viewer/ImguiClientHelper.cpp b/tools/ramses-logic-viewer/ImguiClientHelper.cpp index 4289e1ff4..fe59a1a0c 100644 --- a/tools/ramses-logic-viewer/ImguiClientHelper.cpp +++ b/tools/ramses-logic-viewer/ImguiClientHelper.cpp @@ -172,8 +172,8 @@ namespace ramses int textureheight = 0; unsigned char* pixels = nullptr; io.Fonts->GetTexDataAsRGBA32(&pixels, &texturewidth, &textureheight); - ramses::MipLevelData mipLevelData(static_cast(texturewidth * textureheight * 4), pixels); - auto texture = m_imguiscene->createTexture2D(ramses::ETextureFormat::RGBA8, texturewidth, textureheight, 1, &mipLevelData); + const std::vector mipLevelData{ MipLevelData(static_cast(texturewidth * textureheight * 4), pixels) }; + auto texture = m_imguiscene->createTexture2D(ramses::ETextureFormat::RGBA8, texturewidth, textureheight, mipLevelData); sampler = m_imguiscene->createTextureSampler( ramses::ETextureAddressMode::Repeat, ramses::ETextureAddressMode::Repeat, diff --git a/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp b/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp index c71ab9968..f27b61d7b 100644 --- a/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp +++ b/tools/ramses-logic-viewer/LogicViewerGuiApp.cpp @@ -183,13 +183,13 @@ namespace ramses return {}; } - if (args.noOffscreen()) + if (args.offscreen()) { - m_sceneSetup = std::make_unique(*m_imguiHelper, renderer, m_scene, display); + m_sceneSetup = std::make_unique(*m_imguiHelper, renderer, m_scene, display, m_width, m_height); } else { - m_sceneSetup = std::make_unique(*m_imguiHelper, renderer, m_scene, display, m_width, m_height); + m_sceneSetup = std::make_unique(*m_imguiHelper, renderer, m_scene, display); } renderer.setDisplayBufferClearColor(display, m_sceneSetup->getOffscreenBuffer(), {m_defaultClearColor[0], m_defaultClearColor[1], m_defaultClearColor[2], m_defaultClearColor[3]}); diff --git a/tools/ramses-logic-viewer/SceneSetup.h b/tools/ramses-logic-viewer/SceneSetup.h index 9b4263d34..0e9d59231 100644 --- a/tools/ramses-logic-viewer/SceneSetup.h +++ b/tools/ramses-logic-viewer/SceneSetup.h @@ -48,8 +48,8 @@ class OffscreenSetup : public ISceneSetup renderer.flush(); static const std::array imgbuf = {255, 255, 255, 255}; - ramses::MipLevelData mipLevelData(4, imgbuf.data()); - auto* texture = imguiHelper.getScene()->createTexture2D(ramses::ETextureFormat::RGBA8, 1, 1, 1, &mipLevelData); + const std::vector mipLevelData{ ramses::MipLevelData(4, imgbuf.data()) }; + auto* texture = imguiHelper.getScene()->createTexture2D(ramses::ETextureFormat::RGBA8, 1, 1, mipLevelData); m_sampler = imguiHelper.getScene()->createTextureSampler( ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Linear, ramses::ETextureSamplingMethod::Linear, *texture); @@ -127,6 +127,8 @@ class FramebufferSetup : public ISceneSetup const auto guiSceneId = imguiHelper.getScene()->getSceneId(); m_sceneControl->setSceneMapping(scene->getSceneId(), display); m_sceneControl->setSceneMapping(guiSceneId, display); + // inspection gui must be drawn on top + m_sceneControl->setSceneDisplayBufferAssignment(guiSceneId, ramses::displayBufferId_t(), 255); m_sceneControl->setSceneState(scene->getSceneId(), ramses::RendererSceneState::Rendered); m_sceneControl->flush(); } diff --git a/tools/ramses-scene-viewer/src/SceneSetup.h b/tools/ramses-scene-viewer/src/SceneSetup.h index 30dd22d72..920383927 100644 --- a/tools/ramses-scene-viewer/src/SceneSetup.h +++ b/tools/ramses-scene-viewer/src/SceneSetup.h @@ -47,9 +47,9 @@ class OffscreenSetup : public ISceneSetup m_ob = renderer->createOffscreenBuffer(display, width, height); renderer->flush(); - static const std::array imgbuf = {255, 255, 255, 255}; - ramses::MipLevelData mipLevelData(4, imgbuf.data()); - auto* texture = imguiHelper.getScene()->createTexture2D(ramses::ETextureFormat::RGBA8, 1, 1, 1, &mipLevelData); + static const std::array imgbuf = { 255, 255, 255, 255 }; + const std::vector mipLevelData{ ramses::MipLevelData(4, imgbuf.data()) }; + auto* texture = imguiHelper.getScene()->createTexture2D(ramses::ETextureFormat::RGBA8, 1, 1, mipLevelData); m_sampler = imguiHelper.getScene()->createTextureSampler( ramses::ETextureAddressMode::Clamp, ramses::ETextureAddressMode::Clamp, ramses::ETextureSamplingMethod::Linear, ramses::ETextureSamplingMethod::Linear, *texture); @@ -130,6 +130,8 @@ class FramebufferSetup : public ISceneSetup m_sceneControl->setSceneMapping(scene->getSceneId(), display); } m_sceneControl->setSceneMapping(guiSceneId, display); + // inspection gui must be drawn on top + m_sceneControl->setSceneDisplayBufferAssignment(guiSceneId, ramses::displayBufferId_t(), 255); if (scene) { m_sceneControl->setSceneState(scene->getSceneId(), ramses::RendererSceneState::Rendered); diff --git a/tools/ramses-scene-viewer/src/SceneViewer.h b/tools/ramses-scene-viewer/src/SceneViewer.h index 00734285d..7f32e2ed8 100644 --- a/tools/ramses-scene-viewer/src/SceneViewer.h +++ b/tools/ramses-scene-viewer/src/SceneViewer.h @@ -54,7 +54,7 @@ namespace ramses::internal void validateContent(ramses::ValidationReport& report, const ramses::Scene& scene) const; std::string m_sceneName; - GuiMode m_guiMode = GuiMode::On; + GuiMode m_guiMode = GuiMode::Overlay; bool m_noValidation = false; bool m_noSkub = false; std::string m_validationOutput; diff --git a/tools/ramses-stream-viewer/src/main.cpp b/tools/ramses-stream-viewer/src/main.cpp index 6c9439c40..a1930fca2 100644 --- a/tools/ramses-stream-viewer/src/main.cpp +++ b/tools/ramses-stream-viewer/src/main.cpp @@ -88,8 +88,8 @@ class StreamSourceViewer m_indices = m_scene->createArrayResource(6u, indicesArray.data()); const std::array textureData = {1u, 1u, 1u, 1u}; - const ramses::MipLevelData mipLevelData(4, textureData.data()); - m_texture = m_scene->createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, 1, &mipLevelData, false, {}, ""); + const std::vector mipLevelData{ ramses::MipLevelData(4, textureData.data()) }; + m_texture = m_scene->createTexture2D(ramses::ETextureFormat::RGBA8, 1u, 1u, mipLevelData, false, {}, ""); ramses::EffectDescription effectDesc;

~}&pG*yv(<&;Gyae~apE^6 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%0A>qOf?!?{&V_6{JdGMU|iTgi0 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%0DV74WSIl>cJ@_vYJsK6Qr3`XCVzmrs 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%0AjaJ&nquAHXwSY12FdpCiPyNZOR#giL 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ⅅRU<77Lb(+4=o*g*X4-v>Cax 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%0A(gF!n7Z>-$O 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